@medway-ui/notes-core
v0.1.2
Published
Modelo de documento, schema Tiptap e vocabulário do editor de anotações — compartilhado entre o app mobile e a web.
Readme
@medway-ui/notes-core
Camada de domínio do editor de anotações, compartilhada entre o app mobile e a
web. Não tem UI. Componentes de tela — toolbar, menu /, renderer de
leitura — pertencem a @medway-ui/core (web) e @medway-ui/native (mobile).
O que está aqui é o que corromperia dados se os clientes discordassem: o modelo do documento, a whitelist que o valida, o schema Tiptap e o vocabulário que um host usa para dirigir o editor.
As duas entradas
| Import | Contém | Quem usa |
| --- | --- | --- |
| @medway-ui/notes-core | Modelo do documento, normalização, vocabulário, catálogo do / | Todo mundo — inclusive React Native |
| @medway-ui/notes-core/schema | Extensions do Tiptap, contrato de schema, Editor | Só quem roda um editor |
A separação é uma restrição forte, não estilo: a entrada raiz não pode
importar Tiptap. O bundle React Native a importa e não pode carregar um editor
que não consegue executar. Se você precisar de algo de /schema dentro do app
mobile, o desenho está errado.
No app mobile, mapeie só a entrada raiz (
tsconfig paths/extraNodeModulesdo Metro). Um import acidental de/schemaem código de app falha na hora, em vez de silenciosamente adicionar o Tiptap ao bundle.
Uso
Ler e validar um documento (qualquer plataforma)
import { normalizeDocument, documentsAreEqual } from '@medway-ui/notes-core';
// Tudo que vem da API ou de um editor passa por aqui antes de ser usado.
const doc = normalizeDocument(response.data.content);normalizeDocument nunca lança e nunca devolve null: entrada inutilizável vira
EMPTY_DOCUMENT. Ela é uma whitelist — nó, mark ou atributo desconhecido é
descartado, e é isso que mantém javascript: e payload editado à mão fora do
documento.
Montar um editor (web ou WebView)
import {
Editor,
buildExtensions,
createSlashCommand,
assertSchemaContract,
} from '@medway-ui/notes-core/schema';
const slash = createSlashCommand((state) => setSlashMenu(state));
const editor = new Editor({
element,
extensions: buildExtensions({ extra: [slash.extension] }),
content: doc,
});
const missing = assertSchemaContract(editor.schema);
if (missing.length) throw new Error(`Schema incompleto: ${missing.join(', ')}`);Importe Editor daqui, não do seu próprio @tiptap/core. Duas cópias de
ProseMirror não só incham o bundle: o ProseMirror compara tipos de nó por
identidade de objeto, então duas cópias quebram em runtime.
O menu /
createSlashCommand faz só a detecção — quando o / está ativo, o que foi
digitado, onde está o caret e o range a substituir. O menu você desenha com o seu
design system e responde com slash.run(id), que remove o /query na mesma
transação (um único undo volta ao texto digitado).
Adicionar um nó ao schema
Na ordem, e o compilador te obriga:
src/document/types.ts— o unionEditorNodeTypesrc/document/normalize.ts—CONTENT_KINDe o que o nó aceita como filhosrc/schema/contract.ts—NODE_TYPE_MAPsrc/schema/extensions.ts— a extension de fato- Os renderers de leitura de cada plataforma
Os passos 2 e 3 são Record<EditorNodeType, …>: pular um deles não compila.
O passo 4 é verificado em runtime por assertSchemaContract. O passo 5 é o único
que ninguém checa por você — veja "Renderers", abaixo.
Renderers de leitura
A web não escreve renderer: generateHTML(doc, extensions) do @tiptap/html
gera a leitura a partir das mesmas extensions, correto por construção.
O mobile escreve à mão (NoteContentView), porque React Native não tem DOM — e
por isso é o único que pode divergir do schema. É lá que vale um teste de
contrato com um documento-fixture cobrindo todos os nós e marks.
Desenvolvimento
npm --prefix notes-core run typecheck # inclui o contrato de schema
npm --prefix notes-core run build # emite dist/Da raiz do app: npm run notes-core:typecheck / npm run build:notes-core. Este
último também roda sozinho antes de npm run dev e npm run build na raiz
(predev/prebuild), então um checkout novo não precisa rodar isso à mão para
o site de docs subir.
Como o pacote é resolvido dentro deste repo
O notes-core/ mora na raiz do medway-ui, ao lado de native/ e
editor-bundle/ — não existe diretório packages/. Cada consumidor daqui de
dentro resolve de um jeito, e todos apontam para o mesmo fonte:
| Consumidor | Como resolve |
| --- | --- |
| Site de docs / @medway-ui/core (raiz) | "@medway-ui/notes-core": "file:notes-core" em devDependencies — aponta para notes-core/dist/, por isso o prebuild/predev roda build:notes-core |
| editor-bundle | alias no vite.config.ts → ../notes-core/src/{index,schema/index}.ts |
| @medway-ui/core publicado | peerDependency opcional >=0.1.0 — nada de Tiptap entra no bundle de quem não instala |
Ou seja: dentro do repo o pacote é consumido do fonte; para quem instala
@medway-ui/core de fora, ele é uma dependência de verdade — é isso que a
página de docs do Note Editor manda instalar.
O editor-bundle não tem nenhuma dependência própria — ele é só o host.
