Blocs et schéma
À l'exécution, le document est une carte plateMap<BlockId, Block> plus des références d'enfants ; au repos, c'est un arbre JSON imbriqué. La carte plate rend les recherches et les modifications en O(1) ; l'arbre rend le fichier lisible par un humain.
interface Block {
id: BlockId; // UUIDv7 — triable par date de création
type: string; // 'paragraph', 'heading', 'table_cell', le vôtre…
version: number; // versionné par type, pour les migrations
props: Record<string, unknown>;
text?: Run[]; // texte enrichi, seulement pour les blocs "inline"
children: BlockId[];
parentId: BlockId | null;
}Le registre de schéma
Un type de bloc est déclaré par une spécification sérialisable en JSON. C'est délibéré : le rendu statique et un futur portage Swift consomment la même déclaration sans exécuter le moindre comportement JavaScript.
s.register({
type: 'callout',
version: 1,
inline: true, // porte du texte enrichi
defaultProps: { icon: '💡' },
placeholder: 'Écris quelque chose…',
});Trois catégories
- text — porte du texte, donc un caret : paragraphe, titre, élément de liste.
- void — pas de texte à éditer : image, séparateur, lien de page. On l'attrape, on ne l'édite pas.
- layout — de la structure : colonnes, lignes de tableau, la racine. Jamais une cible de glisser-déposer en soi.
Cette catégorie n'est pas cosmétique : elle décide si un clic place un caret ou sélectionne le bloc, et si un pointeur qui descend démarre une sélection de texte ou un déplacement.