Configuration

Tout ce qui suit se règle au montage. Trois principes gouvernent cette surface : ce que vous n'importez pas n'est pas dans votre bundle, l'ordre d'un tableau est la précédence, etrien n'est privilégié — le jeu par défaut est un tableau littéral que vous pouvez copier et modifier.

Instanciation

import { Editor, createDoc } from '@nbe/core';
import { EditorView, defaultFeatures, minimalFeatures } from '@nbe/dom';
import { callout } from '@nbe/blocks-callout/dom';
import '@nbe/dom/style.css';

const editor = new Editor({ doc: createDoc() });

const view = new EditorView(container, editor, {
  // quels blocs existent
  blocks: [callout],
  // quels comportements sont attachés
  features: defaultFeatures.filter((f) => f.name !== 'slash-menu'),
  // affichage
  readOnly: false,
  spellcheck: true,
  maxWidth: '720px',
  padding: { top: '32px', x: '24px' },
  // apparence
  theme: { '--nbe-accent-rgb': '220 38 38', '--nbe-radius': '2px' },
  // langue
  labels: { bold: 'Bold', italic: 'Italic' },
});

view.destroy();

Le document est non contrôlé : reconstruire la vue à chaque rendu détruirait le caret et l'historique. destroy() détache toutes les fonctionnalités et retire l'élément.

Deux recettes

// un lecteur : rend le document, n'attache rien, aucun caret
new EditorView(el, editor, { readOnly: true });

// un champ de commentaire : édition sans chrome
new EditorView(el, editor, { features: minimalFeatures });

Quatre fonctionnalités hors du jeu par défaut

import { defaultFeatures, exportFeature, findFeature, wordCountFeature } from '@nbe/dom';
import { mermaidFeature } from '@nbe/blocks-mermaid/dom';

new EditorView(el, editor, {
  features: [...defaultFeatures, findFeature, exportFeature, wordCountFeature, mermaidFeature],
});

findFeature et exportFeature prennent ⌘F et ⌘P : dans un navigateur ces touches sont celles du navigateur, et les reprendre pour offrir moins bien est une des choses qu'on reproche aux concurrents de ce projet. Elles existent pour les hôtes où il n'y a rien à reprendre — un volet Obsidian, une coquille bureau. wordCountFeature écrit sous le document, via view.slot('bottom'). mermaidFeature dessine les blocs de code en mermaid et importe la bibliothèque au premier diagramme.

EditorViewOptions

NomTypeDéfautDescription
blocksDomBlockPlugin[]

Block plugins, replacing the closed dispatches. Defaults to the built-in set; not importing a plugin keeps it out of the bundle, which is the point of activation-by-import.

columnsboolean```ts false ```

**Experimental.** Whether dropping a block beside another creates a column.

Off by default, and the default is the interesting part. Columns are ordinary nested blocks (§2.3), so this flag governs only the *gesture* — a document that already has columns still renders and still reorders, and columns stay reachable from the slash menu either way. What the default buys is a drag with one meaning. Two answers to the same gesture is what made dragging feel unreliable: aiming for "move below" and getting a two-column layout is not a near miss, it is a different document, and the side bands have to be tuned rather than merely correct. Until that is settled, a drag reorders — vertically, always — and side-by-side layout is something you ask for explicitly.

new EditorView(el, editor, { columns: true })
commentAuthorCommentAuthor

Who is commenting.

Absent, comments are anonymous — which is a real mode, not a degraded one: a shared machine, a kiosk, a review link with no login. The editor never invents an identity to fill the gap, it passes `null` and lets the host decide what "anonymous" looks like. The editor does not persist this. It hands it to `onComment` and the host stores `id` and `name` on the thread; `avatar` is display only.

commentCountfunction

What the margin marker counts, when threads are not the honest answer.

The document knows how many *threads* a block carries — each is a `comment` mark with a `threadId` — and that needed no host API, which is why the marker had none. It is the wrong number to show: three comments made one after another join one thread, so the margin said "1" beside a panel with three messages in it. The messages are the host's, so the host counts them. Call refreshCommentMarkers after a change the document cannot see — a reply adds no mark, so nothing else tells the margin.

commentStoreCommentStore

The store the host keeps its threads in.

Hand this over and the margin is correct for free: the badge counts *messages* rather than threads, and it refreshes when a reply lands — which no edit announces, because a reply adds no mark to the document. It exists because the two options above are easy to get right and easy to forget, and forgetting is silent: three of the four hosts in this repository wired `onComment`, shipped, and showed "1" beside a panel with two messages in it. A store has `get` and `onChange` on it already; asking for the store asks for something the host has, rather than for two callbacks it has to think about. EditorViewOptions.commentCount still wins when both are given: a host that counts differently — unresolved only, say — keeps saying so.

databaseDatabaseHost

Collections/views/row-pages live in the host workspace (phase 3, §2.5).

emojistypeOperator

The emoji the icon picker offers — a callout's, a host's own.

The editor bundles a curated couple of dozen, which is what an editor should weigh. Every emoji there is — 1914 of them, named in French and in English by CLDR — is `EMOJI_CATALOG`, two hundred kilobytes that only a host asking for them carries: ```ts import { EMOJI_CATALOG } from '@nbe/dom' new EditorView(el, editor, { emojis: EMOJI_CATALOG }) ```

featuresEditorFeature[]

Behaviour attached to the mounted view, in registration order.

Defaults to defaultFeatures. Pass minimalFeatures for a comment box, `[]` for a viewer, or a filtered copy to drop one thing. What you leave out is not in your bundle.

features: defaultFeatures.filter((f) => f.name !== 'slash-menu')
gutterobject

What the two hover gutters contain.

Both sides are lists you can add to, reorder, or empty — see GutterItem. The defaults are `['add', 'handle']` on the left and `['comment']` on the right.

import { defaultLeftGutter } from '@nbe/dom'
new EditorView(el, editor, {
  gutter: {
    left: defaultLeftGutter,
    right: [
      'comment',
      { name: 'approve', icon: 'check', title: 'Valider', onClick: (id) => approve(id) },
    ],
  },
})
labelsPartial<EditorLabels>

Every string the editor puts on screen. Merged over the French defaults, so a partial dictionary is fine.

linkIconsboolean```ts false ```

Draw each named external link's favicon before its text.

What makes a link read as a *reference* rather than as a URL somebody left in a sentence — the mention shape, for something outside the vault. Only links whose text is not the URL get one: a bare `https://…` already says where it goes. Off by default, and that is not timidity: it is one request to a third party for every link a reader scrolls past, from the link's own origin, and an editor whose claim is that your notes are yours does not do that behind anyone's back. A host that wants it says so.

maxWidthstring

Text column width; '100%' disables centering. Default 708px (Notion).

onCommentfunction

Open a comment, from any of the three affordances that ask for one.

A comment used to be **on a block** and nothing else, which was the right first shape and too narrow: the thing people want to argue about is usually a sentence. There are three ways in now, and CommentContext is how they differ — the gutter button passes nothing and means the whole block, the format toolbar passes the selected `range`, and clicking an existing yellow highlight passes its `threadId`. The threads themselves are the host's: `@nbe/core` has the model and `@nbe/collab` puts it in the CRDT, but *where a discussion is displayed* is a layout decision the editor cannot make — a sidebar, a popover, a panel in another pane. So the editor contributes the affordance and nothing else, and without this host neither the button nor the toolbar entry is rendered at all, rather than rendered dead.

onCreatePagefunction

Create a page in the host workspace (slash menu "Page" item).

onOpenPagefunction

Follow a page link. The editor never routes; the host decides.

onSearchPagesfunction

Candidates for the `@` mention picker.

Without it the `@` trigger stays inert, because an autocomplete with nothing to complete is worse than no autocomplete.

onStoreAssetfunction

Store a pasted/dropped binary and return the opaque src to persist (convention: `asset:<content-hash>`). Without it, file paste/drop is ignored.

paddingobject

Page geometry owned by the editor, not by the host app. The editor fills its container and centers the text column inside `maxWidth`; everything outside the column but inside the padding is still editor surface, so rubber-band selection and click-to-place work there (Word-like margins). Set any value to 0 / '0px' for a flush-to-the-edge look.

readOnlyboolean```ts false ```

Render the document without making it editable.

Drops `contenteditable` and the tab stop, so no caret ever appears — which is the difference between a viewer and an editor that silently ignores keystrokes. Combine with `features: []` to attach nothing at all.

recognizersGestureRecognizer[]

Pointer gestures, in precedence order — the arbitration story as data. Defaults to text selection, block click-routing and the rubber band. Replace it to add a gesture, reorder it to change who wins a contested press, or pass `[]` for an editor that handles none.

resolveAssetUrlfunction

Resolve a persisted src (asset:… or URL) to something an <img> can load.

resolvePageTitlefunction

The current title of a mentioned page.

Called at render time so a rename propagates to every mention. Return `null` for a page that no longer exists — the mention then shows its stored text, marked as unresolved, instead of disappearing.

spellcheckboolean```ts false ```

Ask the browser to spell-check the editable surface.

themeRecord<string, string>

CSS custom properties set on the editor root, overriding the token layer.

The stylesheet resolves every colour from about a dozen base channels plus the named block palette, so a theme is a handful of values rather than a rule override. Keys may be written with or without the `--` prefix.

theme: { '--nbe-accent-rgb': '220 38 38', '--nbe-radius': '2px' }
topologyEditableTopology

Where the editable boundary sits. Defaults to one host per block (D1); `singleHostTopology` puts it on the root instead. Every interaction module is written against this, so switching is a config change.

EditorLabels

Les valeurs par défaut sont en anglais ; cinq packs complets sont livrés et un dictionnaire partiel suffit, il est fusionné par-dessus les défauts. Voir Internationalisation.

Rien de documenté ici pour l'instant.