Alas
Tipografía baseAltura


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.

  1. Primero

  2. Segundo

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

HTMLCargando el editor…
JSONnabi-treeCargando el editor…
Instalación
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: '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()))