Glossary
Every word this site and the repository lean on, in one place. Some of them collide with names other editors use for something else, so they only ever mean what is written here.
Wing
What other editors call a "plugin". The core is the body; bold, headings, tables, uploads — every format and feature is a wing.
The markup the core knows by itself is paragraphs (p), line breaks (br), and plain text. Anything you did not register gets unwrapped and falls through as plain text. So the answer to "what can this editor produce?" is exactly the list of wings you registered.
The code uses the same word — the type is NabiWing and the option is wings:.
HTML is named after where it is
The same document goes by different names, with a different vocabulary allowed in each. These four are the only names for it.
| Name | What it is | Vocabulary | Who sees it |
|---|---|---|---|
| sourceHtml | HTML arriving from outside | anything | the host hands it in |
| soul | normalized HTML that passed the filter — the document itself | data-nabi-* only, no style | the editor itself |
| flutter | the live DOM inside the editing area | soul + screen-only marks | the user |
| outputHtml | HTML leaving the editor | meaning-bearing looks become style | host, database, clipboard |
sourceHtml ─(filter)─▶ soul ─▶ flutter
flutter ─(filter)─▶ soul ─▶ outputHtmlsourceHtml
HTML coming in from outside — the html option, setHtml(), and paste. It may be our own output, someone else's markup, or an attack, so it is never trusted as one thing.
soul
The normalized string that came through the filter, and the document itself. Only data-nabi-* attributes survive; there is no style. It is never stored anywhere — it is re-derived from flutter whenever it is needed, because the browser or the host can dirty the DOM at any moment.
flutter
The live DOM being edited inside contenteditable. It carries soul plus screen-only marks: the selected-object outline, upload placeholders, code highlighting spans, and the filler <br> that gives an empty block somewhere to put the caret. None of them leave in the saved value.
outputHtml
The HTML that leaves through onChange, getHtml(), and the clipboard. So that it stands on its own without nabi.css, only the looks that carry meaning freeze into inline style.
The test is "does the meaning change without it?" A centered paragraph that loses its centering is a different document; a table without borders is still a table. So alignment, image width, and checked state freeze — heading sizes, table borders, and rule styling do not.
Rendering HTML you stored
The safety of getHtml() is about the string you just made. When you render HTML that went to a database and came back, you still need sanitizing on your side.
What a document is made of
| Word | Meaning |
|---|---|
| mark | formatting laid over characters — <b>, <i>, <a>. It lives anywhere text does: mid-sentence, in a table cell, inside a heading |
| block | a chunk that takes a paragraph's place — paragraphs, headings, lists, tables, images |
| block attribute | a flag that changes what a block is like without changing its tag — alignment, drop cap |
| text block | a block whose insides are inline content, so the caret lives between characters — paragraphs, headings, list items |
| object block | a block that is one whole thing — images, rules, tables, code, fold boxes |
| canonical tag | whether <strong> or <b> came in, the filter funnels them to one tag. Same meaning, shorter tag wins |
| claim | the test that decides which wing owns a piece of markup. If nobody claims it, it gets unwrapped |
Being an object block does not mean the insides are off limits. Tables and code blocks are text on the inside and objects only at the boundary.
Words for the editing screen only
| Word | Meaning |
|---|---|
| caret | the blinking bar marking where the next character goes |
| gap | the spot between two blocks. Not a place for characters but a place at root level, so it appears next to object blocks |
| whole selection | an object picked as one chunk rather than as a range of characters. The caret hides and an outline appears |
| context row | the row that offers what is possible only where the caret currently is — row and column commands in a table, the language field in code, unlink for links |
| pending mark | a mark armed with no selection, so it applies from the next character you type |
| placeholder | the temporary stand-in that exists only while an upload runs. It never bleeds into the value |
Words used when building
| Word | Meaning |
|---|---|
| flower | the only door through which a wing reaches core functionality. It is the surface the body grew for its wings, hence the name |
| input rule | a change that happens from typing alone — a hyphen and a space becomes a list, # and a space becomes a heading |
| filter | the allow-list every value passes on the way in and on the way out. Nothing outside it survives |
| idempotent | running it twice changes nothing. Feeding an onChange value straight back into setHtml() does nothing at all |
Next
- How it works — how values flow in and out
- Build your own wing — build a format that does not exist yet