NABI NOTE
Un editor WYSIWYG de código abierto
NABI NOTE es un editor WYSIWYG de código abierto que separa del núcleo funciones como el formato, la alineación, las tablas y las subidas en módulos independientes llamados «wings». Puedes elegir solo los wings que necesitas para montar el editor y conectar los que desarrolles. Está escrito en JavaScript puro, por lo que también se puede usar con frameworks como React o Vue. En páginas sin entorno de compilación, puedes empezar directamente con la biblioteca CDN. Su formato de documento propio, NABI TREE, puede convertirse en HTML incluso en Node.js sin el DOM del navegador. Los documentos de entrada se normalizan según las reglas registradas; al generar HTML, se escapan textos y atributos y se comprueban las direcciones de los enlaces. Los wings personalizados y los datos recibidos del servidor deben revisarse por separado según la política de seguridad de la aplicación. Las variables CSS y el diseño basado en rem permiten ajustar el tema y el tamaño; también ofrece modo oscuro y tipografías para varios idiomas. La ordenación de tablas, el historial local y el trabajo documental con IA utilizan el mismo modelo de documento.
Tipografía
Las familias sin serifa (predeterminada), con serifa, monoespaciada y cursiva incluyen fuentes alternativas para cada idioma. Si no hay una fuente adecuada para la familia seleccionada, se usa la fuente predeterminada del navegador. La página anfitriona establece la tipografía predeterminada del editor.
Abajo, cada familia mostrada en varios 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
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 se pueden aplicar juntos a un mismo carácter.
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.
Puedes usar direcciones http:// y https://, así como rutas del mismo sitio que empiecen por . o /. No se permiten direcciones como javascript:.
Por ejemplo, escribe https://nabi.saro.me y pulsa espacio o Intro: se convierte solo, como ves aquí.
Apertura de enlaces
El documento solo guarda la dirección del enlace; la página que muestra el documento decide cómo se abre.
Enlace de adjunto
Los archivos que no son imágenes permanecen como enlaces de archivo adjunto, como el siguiente.
Adjunto: su enlace se guarda en el documento.
Alineación
A la izquierda
Centrado
A la derecha
No se puede aplicar alineación a los títulos.
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
Las celdas admiten varios párrafos y bloques, con colores de fondo para distinguir el contenido. Prueba a editar directamente el texto y las imágenes de la tabla siguiente.
Recursos | Notas de trabajo |
|---|---|
Una imagen y su descripción comparten celda. | Párrafos flexiblesPulsa Enter para crear un párrafo o Shift+Enter para saltar de línea dentro del mismo párrafo. |
| Pulsa para ver consejos de ediciónLas celdas también admiten bloques desplegables, citas, código y vídeos. Al combinar celdas, su contenido se conserva en orden. |
Pulsa dentro de una celda para mostrar su color de fondo y las herramientas de filas y columnas. Selecciona varias celdas para cambiar a la vez su color y alineación. Para colocar la tabla completa, elige «Seleccionar tabla» y usa las herramientas de alineación.
Las tablas con colores o varios bloques se conservan como HTML incluso al guardarlas en Markdown.
Colores de fondo de celda
Aquí se muestran todos los fondos de celda disponibles. Pulsa una celda para cambiar su color o elige «Predeterminado» en las propiedades para quitar el fondo.
Amarillo yellow | Verde green | Celeste cyan |
Rosa pink | Morado purple | Naranja orange |
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 contiene solo valores numéricos, por lo que la columna se ordena numéricamente.
Existencias contiene números y texto, por lo que la columna se ordena como cadenas de texto.
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.
Región flotante
De un vistazo
Una pequeña zona auxiliar para texto, imágenes y tablas.
Posición | Derecha |
Ancho máximo | 40% |
Usa una región flotante para añadir explicaciones o pequeños materiales junto al texto principal. Si el documento es ancho, este texto fluye a su lado. Pulsa dentro para editar texto e insertar imágenes o tablas en el mismo editor.
Coloca el cursor en la región para abrir sus propiedades. Alterna entre izquierda y derecha y elige un ancho máximo del 20, 30, 40 o 50% que encaje con el texto. Las tablas e imágenes pegadas también permanecen en la región.
También puedes agrupar los párrafos seleccionados en una región. El botón para deshacer la región junto al ancho deja su contenido en el documento. La vista previa oculta el borde punteado de edición; en documentos estrechos o al imprimir, todo queda en una columna.
YouTube
Subida
Arrastra una imagen o un archivo sobre el editor.
Esta demo no envía archivos a un servidor real. Conecta un gestor de subidas en tu aplicación para guardarlos en un servidor.
Tras abrir el selector de imágenes o iniciar una subida, mover solo el cursor mantiene la inserción en la posición original. Si editas el documento durante la espera, se cancela la inserción y aparece un aviso. El progreso y el resultado de una subida iniciada en una celda permanecen en ella.
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.
Barra de herramientas
Las herramientas de títulos, formato de texto, alineación, listas, inserción y documento se agrupan por función. En el escritorio aparecen desplegadas; en móvil hay herramientas rápidas y una paleta completa. Las propiedades de la tabla o región flotante seleccionada aparecen debajo.
Atajos
Pulsa Shift dos veces rápidamente para elegir herramientas con el teclado. En el escritorio recorres la barra desplegada y en móvil la paleta completa. Usa Tab para cambiar de grupo, las flechas para elegir una herramienta y Enter para ejecutarla.
Al final de un bloque de cita, código o desplegable, pulsa Enter dos veces rápidamente para salir. El intervalo predeterminado es de 180ms.
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 NABI TREE en HTML no necesita DOM.
Un servidor (Node.js) también puede leer el NABI TREE guardado y generar HTML con las mismas reglas.
Amable con el móvil
Interfaz móvil — Distribuye las herramientas de edición según la anchura de la pantalla.
Ajuste del teclado móvil — Ajusta la altura de la vista de edición cuando se abre el teclado.
Tamaños flexibles — Usa unidades rem en el diseño principal para adaptarse al acercar y alejar el zoom.
Varios idiomas — Puedes configurar la configuración regional y la tipografía según el idioma y la dirección del texto.
Personalización
Tus propios módulos wing — si necesitas una función, puedes crearla y conectarla tú mismo.
Tu propio CSS — ajusta los colores principales, las esquinas y los espacios con los tokens --nabi-* para los modos claro y oscuro.
Código abierto — publicamos el código fuente en GitHub y lo desarrollamos en colaboración.
Ver la documentación → 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('es')
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')