NABI NOTE
Un éditeur WYSIWYG open source
NABI NOTE est un éditeur WYSIWYG open source dont la mise en forme, l’alignement, les tableaux, l’upload et les autres fonctionnalités sont séparés du noyau sous forme de modules indépendants appelés wings. Vous pouvez choisir seulement les wings nécessaires pour assembler un éditeur, et connecter les wings que vous construisez vous-même. Il est écrit en JavaScript pur, donc utilisable depuis des frameworks comme React ou Vue. Sur les pages sans configuration de build, vous pouvez démarrer immédiatement avec la bibliothèque CDN. Son propre format de document, NABI TREE, peut être converti en HTML même dans des environnements Node.js sans DOM de navigateur. Les documents entrants sont réparés par les règles enregistrées ; lors de la création du HTML, le texte et les attributs sont échappés et les adresses de liens sont vérifiées. Les wings personnalisés et les données reçues du serveur doivent toutefois rester examinés séparément selon la politique de sécurité de l’application. Les variables CSS et une mise en page basée sur rem permettent d’ajuster le thème et la taille ; le mode sombre et les réglages de polices multilingues sont aussi fournis. Le tri des tableaux, l’historique local et le travail documentaire assisté par IA continuent tous sur le même modèle de document.
Police
Sans empattement (par défaut), serif, monospace et cursive — chaque famille empile des polices par système d’écriture, si bien que la langue utilisée garde la forme de cette famille ; une écriture sans police manuscrite dans cette famille retombe sur la police du navigateur. L’hôte choisit la police par défaut.
Ci-dessous, chaque famille est affichée dans de nombreuses langues.
Sans empattement · Sin serifa · Sem serifa · Без засечек · بلا زخارف · सैन्स सेरिफ़ · স্যান্স সেরিফ · سانس سیرف · Tanpa serif · بدون سریف · सॅन्स सेरिफ · Không chân · శాన్స్ సెరిఫ్ · Mara seref · Sans serif · Bila serif · சான்ஸ் செரிஃப் · ไร้เชิง · Senza grazie · 산세리프 · ゴシック体 · 无衬线 · Serifenlos
Avec empattement · Con serifa · Com serifa · С засечками · بزخارف · सेरिफ़ · সেরিফ · سیرف · Berserif · سریف · सेरिफ · Có chân · సెరిఫ్ · Mai seref · Serif · Yenye serif · செரிஃப் · มีเชิง · Con grazie · 세리프 · 明朝体 · 衬线
Chasse fixe · Monoespaciada · Monoespaçada · Моноширинный · ثابت العرض · मोनोस्पेस · মনোস্পেস · یکساں چوڑائی · Lebar tetap · تکفاصله · Đơn cách · మోనోస్పేస్ · Tazara ɗaya · Eş aralıklı · Nafasi moja · ஒற்றையகலம் · ความกว้างคงที่ · Monospaziato · Monospace · 고정폭 · 等幅 · 等宽 · Dicktengleich
Cursive · Cursiva · Рукописный · خط اليد · हस्तलिपि · হস্তলিপি · رواں خط · Tulisan tangan · دستنویس · हस्ताक्षर · Chữ viết tay · చేతిరాత · Rubutun hannu · El yazısı · Mwandiko · கையெழுத்து · ลายมือ · Corsivo · 필기체 · 筆記体 · 手写体 · Schreibschrift
Taille du texte
Très petit
Petit
Grand
Très grand
Titre
Sur une ligne vide, tapez # puis une espace pour la transformer aussitôt en titre.
H1
H2
H3
H4
H5
H6
Gras · Italique · Souligné · Barré
Gras italique souligné barré — un exemple.
Ils peuvent aussi être empilés.
Exposant et indice
La surface est de 3,5 m2, et une note se place ainsi.1
L’eau est H2O.
Couleur du texte · Surlignage
La palette est choisie pour rester lisible en mode clair comme en mode sombre.
Couleur du texte Vert · Corail · Violet · Ambre · Bleu
Surlignage Jaune · Vert · Cyan · Rose · Violet · Orange
Lien
Insérez une adresse et elle devient un lien.
Vous pouvez utiliser les URL http:// et https://, ainsi que les chemins du même site commençant par . ou /. Les adresses comme javascript: ne sont pas autorisées.
Par exemple, tapez https://nabi.saro.me puis appuyez sur espace ou Entrée — la conversion se fait d’elle-même, comme ici.
Ouverture des liens
Le document ne stocke que l’adresse du lien ; la page qui affiche le document décide comment les liens s’ouvrent.
Lien de pièce jointe
Uploader autre chose qu’une image laisse un lien de fichier comme celui ci-dessous.
Pièce jointe reste ainsi.
Alignement
Aligné à gauche
Centré
Aligné à droite
Les titres peuvent aussi être alignés.
Listes
Liste à puces
Sur une ligne vide, tapez - et appuyez sur espace — cela devient aussitôt une liste à puces.
Ceci est un élément de liste
Tab / Maj+Tab indente et désindente.
Liste numérotée
Sur une ligne vide, tapez 1. et appuyez sur espace pour obtenir une liste numérotée.
Premier
Deuxième
Troisième
Liste de tâches
Sur une ligne vide, tapez [ ] ou [x] et appuyez sur espace pour obtenir une liste de tâches.
Cet élément est coché.
Celui-ci n’est pas encore coché.
Tableau
Cliquez sur le bouton tableau dans la barre d’outils pour en créer un, puis ajoutez, supprimez et fusionnez lignes et colonnes.
Tri des colonnes
Ouvrez d’abord l’Aperçu, puis cliquez tour à tour sur les cellules d’en-tête Stock et Prix.
Modèle | Stock | Prix | Poids |
|---|---|---|---|
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 | À définir | 12,999 | 2.4 kg |
Prix ne contient que des nombres, donc il est trié numériquement.
Stock est trié comme du texte à cause des lettres dans la dernière cellule.
Séparateur
Tapez --- et appuyez sur Entrée pour le transformer en séparateur.
Image
La largeur va de 30 % à 100 %, et l’image peut être à gauche, au centre ou à droite.
YouTube
Upload
Essayez de glisser une image ou un fichier sur l’éditeur.
L’upload utilisé ici est une maquette ; un réglage le connecte à votre serveur.
Si un upload échoue, l’image ou le fichier est retiré de l’éditeur.
Citation
Sur une ligne vide, tapez > et appuyez sur espace pour obtenir un bloc de citation.
Il peut s’étendre sur plusieurs lignes.
Code
Sur une ligne vide, tapez ``` et appuyez sur espace ou Entrée pour obtenir un bloc de code.
Écrivez aussi le langage, comme ```java, puis appuyez sur espace ou Entrée pour obtenir un bloc de code avec ce langage appliqué.
import { createNabiWith, defaultWings } from 'nabi-note'
const { nabi } = createNabiWith(defaultWings)
const html = nabi.getHtml()Details
Details est composé d’un résumé et d’un corps.
Vous pouvez choisir s’il est enregistré replié ou ouvert.
Raccourcis
Appuyez rapidement deux fois sur Maj pour afficher le raccourci de chaque fonction dans la barre d’outils.
Format automatique
Exemple | Touche d’action | Résultat |
|---|---|---|
# | Espace | Titre |
- | Espace | Liste à puces |
1. | Espace | Liste numérotée |
[ ] · [x] | Espace | Liste de tâches |
> | Espace | Citation |
``` · ```ts | Espace · Entrée | Bloc de code |
--- | Entrée | Séparateur |
https://… | Espace · Entrée | Lien |
Fonctions de sortie
Fonction | Résultat |
|---|---|
getHtml() | HTML |
getJson() | JSON |
Fonctionne sans DOM
Convertir du JSON en HTML ne nécessite aucun DOM.
Un serveur (Node.js) peut lire le NABI TREE stocké et créer du HTML avec les mêmes règles.
Adapté au mobile
Interface mobile — une mise en page responsive porte l’interface mobile.
Inset du clavier mobile — lorsque le clavier s’ouvre, sa hauteur est prise en compte.
Tailles fluides — chaque taille est écrite en rem.
Nombreuses langues — plusieurs langues sont prises en charge.
Personnalisation
Votre propre wing — si vous avez besoin d’une fonction, construisez-la vous-même et enregistrez-la.
Votre propre CSS — couleurs, coins et espacements sont tous définis en --nabi-* ; le sombre, le clair et le reste vous appartiennent.
Open source — le projet est open source sur GitHub.
Lire la documentation → 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: 'fr', name: () => 'note' })
const history = mountLocalHistory({ nabi, storage: browserHistoryStorage(window) })
const diff = mountDiffWing({ nabi, registry, surface: content, locale: 'fr' })
// The locale also sets the direction — Arabic and Urdu run right to left
mountSurface({ nabi, registry, root: content, locale: 'fr', fileSink: upload.take })
const settle = watchSettle(document, { surface: content })
const shared = { nabi, registry, surface: content, settle, locale: 'fr' }
// 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: 'fr', 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()))