Asas
Tipo de letra padrãoAltura


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.

  1. Primeiro

  2. Segundo

  3. 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

HTMLCarregando o editor…
JSONnabi-treeCarregando o editor…
Instalação
npm install nabi-note
Código
import {
  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()))