NABI NOTE
Um editor WYSIWYG de código aberto
NABI NOTE é um editor WYSIWYG de código aberto que separa do núcleo funções como formatação, alinhamento, tabelas e envio de arquivos em módulos independentes chamados wings. Você pode escolher apenas os wings necessários para montar o editor e conectar aqueles que criar. Ele é escrito em JavaScript puro, podendo ser usado em frameworks como React ou Vue. Em páginas sem ambiente de compilação, você pode começar diretamente com a biblioteca CDN. Seu formato de documento próprio, NABI TREE, pode ser convertido em HTML até mesmo em Node.js sem o DOM do navegador. Os documentos recebidos são normalizados conforme as regras registradas; ao gerar HTML, textos e atributos são escapados e os endereços dos links são verificados. Wings personalizados e dados recebidos do servidor ainda devem ser revisados separadamente conforme a política de segurança da aplicação. Variáveis CSS e um layout baseado em rem permitem ajustar tema e tamanho, com modo escuro e tipografias para vários idiomas. Ordenação de tabelas, histórico local e trabalho com documentos auxiliado por IA usam o mesmo modelo de documento.
Fonte
As famílias sem serifa (padrão), com serifa, monoespaçada e cursiva incluem fontes alternativas por idioma. Se não houver uma fonte adequada para a família escolhida, será usada a fonte padrão do navegador. A página que incorpora o editor define sua fonte padrão.
Abaixo, cada família mostrada em vários idiomas.
산세리프 · ゴシック体 · 无衬线 · Serifenlos · Sans empattement · Sin serifa · Sem serifa · Без засечек · بلا زخارف · सैन्स सेरिफ़ · স্যান্স সেরিফ · سانس سیرف · Tanpa serif · بدون سریف · सॅन्स सेरिफ · Không chân · శాన్స్ సెరిఫ్ · Mara seref · Sans serif · Bila serif · சான்ஸ் செரிஃப் · ไร้เชิง · Senza grazie
세리프 · 明朝体 · 衬线 · Serif · Avec empattement · Con serifa · Com serifa · С засечками · بزخارف · सेरिफ़ · সেরিফ · سیرف · Berserif · سریف · सेरिफ · Có chân · సెరిఫ్ · Mai seref · Yenye serif · செரிஃப் · มีเชิง · Con grazie
고정폭 · 等幅 · 等宽 · Dicktengleich · Chasse fixe · Monoespaciada · Monoespaçada · Моноширинный · ثابت العرض · मोनोस्पेस · মনোস্পেস · یکساں چوڑائی · Lebar tetap · تکفاصله · Đơn cách · మోనోస్పేస్ · Tazara ɗaya · Eş aralıklı · Nafasi moja · ஒற்றையகலம் · ความกว้างคงที่ · Monospaziato · Monospace
필기체 · 筆記体 · 手写体 · Schreibschrift · Cursive · Cursiva · Рукописный · خط اليد · हस्तलिपि · হস্তলিপি · رواں خط · Tulisan tangan · دستنویس · हस्ताक्षर · Chữ viết tay · చేతిరాత · Rubutun hannu · El yazısı · Mwandiko · கையெழுத்து · ลายมือ · Corsivo
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 podem ser aplicados juntos ao mesmo caractere.
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.
Você pode usar URLs http:// e https://, além de caminhos do mesmo site que começam com . ou /. Endereços como javascript: não são permitidos.
Por exemplo, escreva https://nabi.saro.me e aperte espaço ou Enter: converte sozinho, como se vê aqui.
Abertura de links
O documento armazena apenas o endereço do link; a página que exibe o documento decide como os links são abertos.
Link de anexo
Arquivos que não são imagens permanecem como links de anexo, como o exemplo abaixo.
Anexo: o link é salvo no documento.
Alinhamento
À esquerda
Centralizado
À direita
Não é possível aplicar alinhamento aos títulos.
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
As células podem conter vários parágrafos e blocos, com cores de fundo para distinguir o conteúdo. Experimente editar o texto e as imagens da tabela abaixo.
Recursos | Notas de trabalho |
|---|---|
Uma imagem e sua descrição na mesma célula. | Parágrafos flexíveisPressione Enter para criar um parágrafo ou Shift+Enter para quebrar a linha no mesmo parágrafo. |
| Clique para ver dicas de ediçãoAs células também aceitam blocos recolhíveis, citações, código e vídeos. Ao mesclar células, o conteúdo de cada uma é mantido na ordem. |
Clique em uma célula para mostrar sua cor de fundo e as ferramentas de linhas e colunas. Selecione várias células para alterar a cor e o alinhamento em conjunto. Para posicionar a tabela inteira, escolha “Selecionar tabela” e use as ferramentas de alinhamento.
Tabelas com cores ou vários blocos são preservadas como HTML mesmo ao salvar em Markdown.
Cores de fundo das células
Aqui estão todas as cores de fundo disponíveis. Clique em uma célula para mudar sua cor ou escolha “Padrão” nas propriedades para remover o fundo.
Amarelo yellow | Verde green | Ciano cyan |
Rosa pink | Roxo purple | Laranja orange |
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 contém apenas valores numéricos, portanto a coluna é ordenada numericamente.
Estoque contém números e texto, portanto a coluna é ordenada como sequências de caracteres.
Separador
Escreva --- e aperte Enter: vira um separador.
Imagem
A largura vai de 30% a 100% e ela fica à esquerda, no centro ou à direita.
Área flutuante
Visão geral
Uma pequena região de apoio para texto, imagens e tabelas.
Posição | Direita |
Largura máxima | 40% |
Use uma área flutuante para colocar explicações ou pequenos materiais ao lado do texto principal. Em um documento largo, este texto flui ao lado da região. Clique dentro dela para editar texto e inserir imagens ou tabelas no mesmo editor.
Coloque o cursor na região para abrir suas propriedades. Alterne entre esquerda e direita e escolha uma largura máxima de 20, 30, 40 ou 50% adequada ao texto. Tabelas e imagens coladas também ficam dentro da região.
Você também pode agrupar os parágrafos selecionados em uma região. O botão de desfazer a região ao lado da largura deixa o conteúdo no documento. A prévia oculta o contorno pontilhado de edição; em documentos estreitos ou na impressão, tudo segue em uma coluna.
YouTube
Envio
Arraste uma imagem ou um arquivo para cima do editor.
Esta demonstração não envia arquivos a um servidor real. Conecte um carregador à sua aplicação para armazená-los em um servidor.
Depois de abrir o seletor de imagens ou iniciar um envio, mover apenas o cursor mantém a inserção na posição original. Editar o documento durante a espera cancela a inserção e mostra um aviso. O progresso e o resultado de um envio iniciado em uma célula também ficam nela.
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.
Barra de ferramentas
As ferramentas de títulos, formatação, alinhamento, listas, inserção e documento estão agrupadas. No computador, ficam expandidas; no celular, há ferramentas rápidas e a paleta completa. As propriedades da tabela ou área flutuante selecionada aparecem abaixo.
Atalhos
Pressione Shift duas vezes rapidamente para escolher ferramentas pelo teclado. No computador, navegue pela barra expandida; no celular, pela paleta completa. Use Tab para trocar de grupo, as setas para escolher uma ferramenta e Enter para executá-la.
No fim de um bloco de citação, código ou recolhível, pressione Enter duas vezes rapidamente para sair. O intervalo padrão é de 180ms.
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 NABI TREE em HTML não precisa de DOM.
Um servidor (Node.js) também pode ler o NABI TREE salvo e gerar HTML com as mesmas regras.
Amigo do celular
Interface móvel — Organiza as ferramentas de edição conforme a largura da tela.
Ajuste do teclado móvel — Ajusta a altura da área de edição quando o teclado é aberto.
Tamanhos flexíveis — Usa unidades rem no layout principal para se adaptar à ampliação e à redução.
Vários idiomas — Você pode definir a localidade e a tipografia conforme o idioma e a direção do texto.
Personalização
Seus próprios módulos wing — se precisar de uma função, você pode criá-la e conectá-la.
Seu próprio CSS — ajuste as cores principais, os cantos e os espaçamentos com os tokens --nabi-* para os modos claro e escuro.
Código aberto — publicamos o código-fonte no GitHub e o desenvolvemos em colaboração.
Ver a documentação → nabi.saro.me
npm install nabi-noteimport {
createLocale, createNabiWith, mountSurface, mountToolbar,
mountContextToolbar, mountHints, watchSettle, makeTranslator,
defaultWings, mountUpload, mountUploadView, browserFileStore,
mountFile, mountViewTools, browserHistoryStorage, mountLocalHistory,
openHistoryPanel,
} from 'nabi-note'
import { mountDiffWing } from 'nabi-note/diff'
import { mountOutline } from 'nabi-note/viewer'
// 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 locale = createLocale('pt')
const { nabi, registry } = createNabiWith(selected, { ask, locale })
// 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, locale })
const upload = mountUpload({
nabi, root: content, locale,
// 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), locale, name: () => 'note' })
const history = mountLocalHistory({ nabi, storage: browserHistoryStorage(window) })
const diff = mountDiffWing({ nabi, registry, surface: content, locale })
// The locale also sets the direction — Arabic and Urdu run right to left
mountSurface({ nabi, registry, root: content, locale, fileSink: upload.take })
const trigger = document.createElement('button')
trigger.type = 'button'
trigger.textContent = makeTranslator(locale).t('outline')
trigger.style.cssText = 'position:fixed;right:1rem;bottom:1rem;z-index:1000'
root.append(trigger)
const outline = mountOutline(content, {
trigger, locale,
scrollContainer: () => root.classList.contains('is-fullscreen') ? root : window,
onChange: ({ items, activeIndex, available, open }) => {
trigger.hidden = !available
trigger.setAttribute("aria-expanded", String(open))
},
})
trigger.onclick = () => outline.toggle()
// Call outline.unmount() when removing the editor
const settle = watchSettle(document, { surface: content })
const shared = { nabi, registry, surface: content, settle, locale }
// 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, 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()))
// Call from your language selector. Content and editing history stay intact.
// locale.setLocale('ko')