NABI NOTE
Um editor WYSIWYG de código aberto
NABI NOTE é um editor WYSIWYG de código aberto em que cada função principal — formatação, alinhamento, tabelas, envios e o resto — vive fora do núcleo como um módulo independente chamado «wing», de modo que quem programa pode estendê-lo sem limite. É escrito em Vanilla JS puro com a meta de ZERO dependências de frameworks, por isso integra-se facilmente com React, Vue ou qualquer outro ambiente, e traz uma biblioteca CDN para projetos sem sistema de build. Carrega o seu próprio formato JSON, NABI TREE, de modo que a conversão entre HTML e texto pode ser preparada onde não há DOM (Node.js, SSR); e como remonta os documentos com um vocabulário permitido em vez de remendá-los, garante scripts XSS bloqueados na raiz sem nenhuma biblioteca de saneamento à parte. No design adota o sistema de variáveis CSS, o que torna fácil trocar a cor da marca, e um layout em rem, de modo que ampliar ou reduzir mantém a interface móvel fluida; as cores ajustadas ao claro e ao escuro, os marcadores e as fontes multilíngues já estão lá. A isso somam-se a ordenação de colunas que reconhece o tipo, um histórico local e suporte a vibe coding.
Fonte
Sem serifa (padrão), com serifa, monoespaçada e cursiva: cada família empilha fontes por sistema de escrita, então qualquer idioma guarda o rosto daquela família; uma escrita sem mão cursiva naquela família cai para a fonte do navegador. O ambiente anfitrião define a fonte padrão.
Abaixo, cada família mostrada em vários idiomas.
Sem serifa · Без засечек · بلا زخارف · सैन्स सेरिफ़ · স্যান্স সেরিফ · سانس سیرف · Tanpa serif · بدون سریف · सॅन्स सेरिफ · Không chân · శాన్స్ సెరిఫ్ · Mara seref · Sans serif · Bila serif · சான்ஸ் செரிஃப் · ไร้เชิง · Senza grazie · 산세리프 · ゴシック体 · 无衬线 · Serifenlos · Sans empattement · Sin serifa
Com serifa · С засечками · بزخارف · सेरिफ़ · সেরিফ · سیرف · Berserif · سریف · सेरिफ · Có chân · సెరిఫ్ · Mai seref · Serif · Yenye serif · செரிஃப் · มีเชิง · Con grazie · 세리프 · 明朝体 · 衬线 · Avec empattement · Con serifa
Monoespaçada · Моноширинный · ثابت العرض · मोनोस्पेस · মনোস্পেস · یکساں چوڑائی · Lebar tetap · تکفاصله · Đơn cách · మోనోస్పేస్ · Tazara ɗaya · Eş aralıklı · Nafasi moja · ஒற்றையகலம் · ความกว้างคงที่ · Monospaziato · Monospace · 고정폭 · 等幅 · 等宽 · Dicktengleich · Chasse fixe · Monoespaciada
Cursiva · Рукописный · خط اليد · हस्तलिपि · হস্তলিপি · رواں خط · Tulisan tangan · دستنویس · हस्ताक्षर · Chữ viết tay · చేతిరాత · Rubutun hannu · El yazısı · Mwandiko · கையெழுத்து · ลายมือ · Corsivo · Cursive · 필기체 · 筆記体 · 手写体 · Schreibschrift
Tamanho da letra
Muito pequeno
Pequeno
Grande
Muito grande
Título
Numa linha vazia escreva # e aperte espaço: na hora vira um título.
H1
H2
H3
H4
H5
H6
Negrito · Itálico · Sublinhado · Riscado
Negrito itálico sublinhado riscado — um exemplo.
Também dá para sobrepor.
Sobrescrito e subscrito
A área é de 3,5 m2, e uma nota fica assim1.
A água é H2O.
Cor do texto · Marcador
A paleta foi escolhida para ler bem tanto no claro quanto no escuro.
Cor do texto Verde · Coral · Violeta · Âmbar · Azul
Marcador Amarelo · Verde · Ciano · Rosa · Roxo · Laranja
Link
Ponha um endereço e vira um link.
Só http:// e https:// são aceitos; algo como javascript: não passa.
Por exemplo, escreva https://nabi.saro.me e aperte espaço ou Enter: converte sozinho, como se vê aqui.
Abertura de links
Por padrão, um link da mesma origem abre nesta janela e qualquer outro site numa nova; a regra se define ao declarar o editor.
Link de anexo
Se enviar algo que não seja imagem, fica um link em forma de arquivo como o de baixo.
Anexo é assim que fica.
Alinhamento
À esquerda
Centralizado
À direita
Títulos também se alinham.
Listas
Lista com marcadores
Numa linha vazia escreva - e aperte espaço: na hora vira uma lista com marcadores.
Isto é um item
Tab / Shift+Tab recuam e voltam.
Lista numerada
Numa linha vazia escreva 1. e aperte espaço: sai uma lista numerada.
Primeiro
Segundo
Terceiro
Lista de tarefas
Numa linha vazia escreva [ ] ou [x] e aperte espaço: sai uma lista de tarefas.
Esta tarefa está feita.
Esta ainda não.
Tabela
Crie pela tabela da barra de ferramentas; linhas e colunas podem ser somadas, tiradas e juntadas.
Ordenar colunas
Aperte Pré-visualizar e depois clique, um depois do outro, nos cabeçalhos Estoque e Preço.
Modelo | Estoque | Preço | Peso |
|---|---|---|---|
NB-7 | 1,200 | 349 | 1.2 kg |
NB-9 | 20,000 | 99 | 0.9 kg |
NB-12 | 3,500 | 1,299 | 1.4 kg |
NB-80 | 900 | 8,900 | 2.1 kg |
NB-100 | A definir | 12,999 | 2.4 kg |
Preço é só número, então ordena como número.
Estoque ordena como texto porque a última célula tem letras.
Separador
Escreva --- e aperte Enter: vira um separador.
Imagem
A largura vai de 30% a 100% e ela fica à esquerda, no centro ou à direita.
YouTube
Envio
Arraste uma imagem ou um arquivo para cima do editor.
O envio desta demonstração é de mentirinha; com um ajuste ele liga no seu servidor.
Se um envio falhar, essa imagem ou arquivo é tirado do editor.
Citação
Numa linha vazia escreva > e aperte espaço: sai um bloco de citação.
Pode ocupar várias linhas.
Código
Numa linha vazia escreva ``` e aperte espaço ou Enter: sai um bloco de código.
Escreva também a linguagem, como ```java, e aperte espaço ou Enter: o bloco toma aquela linguagem.
import { createNabiWith, defaultWings } from 'nabi-note'
const { nabi } = createNabiWith(defaultWings)
const html = nabi.getHtml()Bloco recolhível
Um bloco recolhível é feito de título e conteúdo.
Você decide se ele é salvo fechado ou aberto.
Atalhos
Aperte Shift duas vezes rápido e a barra de ferramentas mostra o atalho de cada função.
Formato automático
Exemplo | Tecla | Resultado |
|---|---|---|
# | Espaço | Título |
- | Espaço | Lista com marcadores |
1. | Espaço | Lista numerada |
[ ] · [x] | Espaço | Lista de tarefas |
> | Espaço | Citação |
``` · ```ts | Espaço · Enter | Bloco de código |
--- | Enter | Separador |
https://… | Espaço · Enter | Link |
Funções de saída
Função | Resultado |
|---|---|
getHtml() | HTML |
getJson() | JSON |
Funciona sem DOM
Converter JSON em HTML não precisa de DOM.
Um servidor (Node.js) lê a NABI TREE salvo do jeito que está e monta o HTML enquanto barra o XSS.
Amigo do celular
Interface móvel — um layout responsivo sustenta a interface móvel.
Correção do teclado — quando o teclado abre, a altura dele é corrigida.
Tamanhos fluidos — todos os tamanhos são escritos em rem.
Multilíngue — oferece suporte a vários idiomas.
Personalização
Seus próprios módulos wing — se faltar uma função, construa e registre a sua.
Seu próprio CSS — cores, cantos e espaços são definidos com --nabi-*, o claro e o escuro são por sua conta.
Código aberto — aberto no GitHub.
Ver a documentação → nabi.saro.me
npm install nabi-noteimport {
createNabiWith, mountSurface, mountToolbar, mountContextToolbar,
mountHints, watchSettle, defaultWings, mountUpload, mountUploadView,
browserFileStore, mountFile, parseNodes, mountViewTools,
browserHistoryStorage, mountLocalHistory, openHistoryPanel,
} from 'nabi-note'
import { mountDiffWing } from 'nabi-note/diff'
// The box that asks — without it the core answers "no" to everything (plug in your own)
const ask = { message: (t: string) => alert(t), confirm: (t: string) => confirm(t) }
const selected = [
...defaultWings,
]
const { nabi, registry } = createNabiWith(selected, { ask })
// Bring your own highlighter (Prism, highlight.js, Shiki, …) — the wing stays as it is
// and only the attach is swapped (`makeCodeAttach` ships from the same door)
// { ...codeWing, attach: makeCodeAttach({ highlight: (source, lang) => [{ text: source }] }) }
const root = document.querySelector('.nabi')!
const content = document.querySelector('.nabi-content')!
const view = mountUploadView({ nabi, surface: content })
const upload = mountUpload({
nabi, root: content,
// your upload goes here — report progress with task.onProgress(0–100)
uploader: async (task) => ({ uri: 'https://cdn.example/uploaded' }),
extensions: ['png', 'jpg', 'pdf'], maxFileSize: 10 * 1024 * 1024,
onStart: (tasks) => view.start(tasks),
onProgress: (id, percent) => view.progress(id, percent),
onSettle: () => view.settle(),
onDone: () => view.done(),
})
const file = mountFile({ nabi, registry, store: browserFileStore(document), parse: parseNodes, locale: 'pt', name: () => 'note' })
const history = mountLocalHistory({ nabi, storage: browserHistoryStorage(window) })
const diff = mountDiffWing({ nabi, registry, surface: content, locale: 'pt' })
// The locale also sets the direction — Arabic and Urdu run right to left
mountSurface({ nabi, registry, root: content, locale: 'pt', fileSink: upload.take })
const settle = watchSettle(document, { surface: content })
const shared = { nabi, registry, surface: content, settle, locale: 'pt' }
// A tool that needs a panel comes back to the host — without this the button is dead
const toolbar = mountToolbar({
...shared, root: document.querySelector('#toolbar')!,
file,
onFiles: upload.take,
onHost: (w) => {
if (w === 'diff') { diff.open(); return }
if (w !== 'localHistory') return
openHistoryPanel({
history, surface: content, locale: 'pt', sessionId: history.sessionId,
render: (record) => createNabiWith(selected, { doc: JSON.parse(record.body) }).nabi.getHtml(),
})
},
})
const context = mountContextToolbar({ ...shared, root: document.querySelector('#context')! })
mountHints({ toolbar, context, root, surface: content })
// The preview and fullscreen buttons — they stand their own box at the end of the row
mountViewTools({ ...shared, root, container: document.querySelector('#toolbar')! })
// on every change — hook up your own code here
// nabi.onChange(() => user_callback(nabi.getHtml()))