NABI NOTE
Un editor WYSIWYG de código abierto
NABI NOTE es un editor WYSIWYG de código abierto en el que cada función principal —formato, alineación, tablas, subidas y lo demás— vive aparte del núcleo como un módulo independiente llamado «wing», de modo que quien programa puede ampliarlo sin límite. Está escrito en Vanilla JS puro con la meta de CERO dependencia de frameworks, por lo que se integra fácilmente en React, Vue o cualquier otro entorno, y trae una biblioteca CDN para proyectos sin sistema de compilación. Lleva su propio formato JSON, NABI TREE, de modo que la conversión entre HTML y texto puede prepararse donde no hay DOM (Node.js, SSR); y como rearma los documentos con un vocabulario permitido en vez de remendarlos, garantiza scripts XSS bloqueados de raíz sin ninguna biblioteca de saneamiento aparte. En el diseño adopta el sistema de variables CSS, con lo que el color de marca se cambia fácil, y una maquetación en rem, de modo que ampliar o reducir mantiene fluida la interfaz móvil; incluye colores adaptados a los modos claro y oscuro, marcadores y tipografías multilingües. A eso se suman la ordenación de columnas que reconoce el tipo, un historial local y soporte para vibe coding.
Tipografía
Palo seco (por defecto), serif, monoespaciada y cursiva: cada familia apila tipografías por sistema de escritura, así que cualquier idioma conserva el rostro de esa familia; una escritura que no tenga mano cursiva en esa familia cae a la fuente del navegador. La tipografía por defecto la decide el anfitrión.
Abajo, cada familia mostrada en varios idiomas.
Sin serifa · Sem serifa · Без засечек · بلا زخارف · सैन्स सेरिफ़ · স্যান্স সেরিফ · سانس سیرف · Tanpa serif · بدون سریف · सॅन्स सेरिफ · Không chân · శాన్స్ సెరిఫ్ · Mara seref · Sans serif · Bila serif · சான்ஸ் செரிஃப் · ไร้เชิง · Senza grazie · 산세리프 · ゴシック体 · 无衬线 · Serifenlos · Sans empattement
Con serifa · Com serifa · С засечками · بزخارف · सेरिफ़ · সেরিফ · سیرف · Berserif · سریف · सेरिफ · Có chân · సెరిఫ్ · Mai seref · Serif · Yenye serif · செரிஃப் · มีเชิง · Con grazie · 세리프 · 明朝体 · 衬线 · Avec empattement
Monoespaciada · Monoespaçada · Моноширинный · ثابت العرض · मोनोस्पेस · মনোস্পেস · یکساں چوڑائی · Lebar tetap · تکفاصله · Đơn cách · మోనోస్పేస్ · Tazara ɗaya · Eş aralıklı · Nafasi moja · ஒற்றையகலம் · ความกว้างคงที่ · Monospaziato · Monospace · 고정폭 · 等幅 · 等宽 · Dicktengleich · Chasse fixe
Cursiva · Рукописный · خط اليد · हस्तलिपि · হস্তলিপি · رواں خط · Tulisan tangan · دستنویس · हस्ताक्षर · Chữ viết tay · చేతిరాత · Rubutun hannu · El yazısı · Mwandiko · கையெழுத்து · ลายมือ · Corsivo · Cursive · 필기체 · 筆記体 · 手写体 · Schreibschrift
Tamaño de letra
Muy pequeño
Pequeño
Grande
Muy grande
Título
En una línea vacía escribe # y pulsa espacio: al momento se vuelve un título.
H1
H2
H3
H4
H5
H6
Negrita · Cursiva · Subrayado · Tachado
Negrita cursiva subrayado tachado: un ejemplo.
También se pueden superponer.
Superíndice y subíndice
La superficie es de 3,5 m2, y una nota se pone así1.
El agua es H2O.
Color de texto · Marcador
La paleta está elegida para leerse bien tanto en claro como en oscuro.
Color de texto Verde · Coral · Violeta · Ámbar · Azul
Marcador Amarillo · Verde · Cian · Rosa · Púrpura · Naranja
Enlace
Pon una dirección y se convierte en un enlace.
Solo se admiten http:// y https://; algo como javascript: no pasa.
Por ejemplo, escribe https://nabi.saro.me y pulsa espacio o Intro: se convierte solo, como ves aquí.
Apertura de enlaces
Por defecto, un enlace del mismo origen se abre en esta ventana y cualquier otro sitio en una nueva; la regla se fija al declarar el editor.
Enlace de adjunto
Si subes algo que no sea una imagen, queda un enlace con forma de archivo como el de abajo.
Adjunto así es como queda.
Alineación
A la izquierda
Centrado
A la derecha
Los títulos también se alinean.
Listas
Lista con viñetas
En una línea vacía escribe - y pulsa espacio: al momento se vuelve una lista con viñetas.
Esto es una viñeta
Tab / Mayús+Tab sangran y quitan sangría.
Lista numerada
En una línea vacía escribe 1. y pulsa espacio: sale una lista numerada.
Primero
Segundo
Tercero
Lista de tareas
En una línea vacía escribe [ ] o [x] y pulsa espacio: sale una lista de tareas.
Esta tarea está hecha.
Esta todavía no.
Tabla
Créala desde la tabla de la barra de herramientas; se añaden, se quitan y se combinan filas y columnas.
Ordenar columnas
Pulsa Vista previa y luego haz clic, uno tras otro, en los encabezados Existencias y Precio.
Modelo | Existencias | Precio | 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 | Por definir | 12,999 | 2.4 kg |
Precio son solo números, así que se ordena como número.
Existencias se ordena como texto porque la última celda lleva letras.
Separador
Escribe --- y pulsa Intro: se vuelve un separador.
Imagen
El ancho va del 30 % al 100 % y se coloca a la izquierda, al centro o a la derecha.
YouTube
Subida
Arrastra una imagen o un archivo sobre el editor.
La subida de esta demostración es de mentira; con un ajuste se conecta a tu servidor.
Si una subida falla, esa imagen o archivo se retira del editor.
Cita
En una línea vacía escribe > y pulsa espacio: sale un bloque de cita.
Puede ocupar varias líneas.
Código
En una línea vacía escribe ``` y pulsa espacio o Intro: sale un bloque de código.
Escribe también el lenguaje, como ```java, y pulsa espacio o Intro: el bloque toma ese lenguaje.
import { createNabiWith, defaultWings } from 'nabi-note'
const { nabi } = createNabiWith(defaultWings)
const html = nabi.getHtml()Bloque plegable
Un bloque plegable se compone de título y contenido.
Tú decides si se guarda plegado o desplegado.
Atajos
Pulsa dos veces seguidas Mayús y la barra de herramientas muestra el atajo de cada función.
Formato automático
Ejemplo | Tecla | Resultado |
|---|---|---|
# | Espacio | Título |
- | Espacio | Lista con viñetas |
1. | Espacio | Lista numerada |
[ ] · [x] | Espacio | Lista de tareas |
> | Espacio | Cita |
``` · ```ts | Espacio · Intro | Bloque de código |
--- | Intro | Separador |
https://… | Espacio · Intro | Enlace |
Funciones de salida
Función | Resultado |
|---|---|
getHtml() | HTML |
getJson() | JSON |
Funciona sin DOM
Convertir JSON en HTML no necesita DOM.
Un servidor (Node.js) lee tal cual el NABI TREE guardado y arma el HTML mientras frena el XSS.
Amable con el móvil
Interfaz móvil — una maquetación adaptable sostiene la interfaz móvil.
Corrección del teclado — cuando el teclado se abre, se corrige su altura.
Tamaños fluidos — todos los tamaños están escritos en rem.
Multilingüe — admite varios idiomas.
Personalización
Tus propios módulos wing — si falta una función, constrúyela y regístrala.
Tu propio CSS — colores, esquinas y márgenes están definidos con --nabi-*, lo claro y lo oscuro son cosa tuya.
Código abierto — abierto en GitHub.
Ver la documentación → 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: 'es', name: () => 'note' })
const history = mountLocalHistory({ nabi, storage: browserHistoryStorage(window) })
const diff = mountDiffWing({ nabi, registry, surface: content, locale: 'es' })
// The locale also sets the direction — Arabic and Urdu run right to left
mountSurface({ nabi, registry, root: content, locale: 'es', fileSink: upload.take })
const settle = watchSettle(document, { surface: content })
const shared = { nabi, registry, surface: content, settle, locale: 'es' }
// 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: 'es', 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()))