# BlockEditor

Éditeur WYSIWYG par blocs, style Notion. Construit en interne (pas de
ProseMirror/Lexical/Tiptap). Un `contentEditable` par bloc, pas un seul
contentEditable global — c'est plus robuste et plus simple à étendre.

## Utilisation

```jsx
import { BlockEditor } from '@zen/core/shared/components';

const [blocks, setBlocks] = useState([]);
<BlockEditor value={blocks} onChange={setBlocks} label="Contenu" />
```

`value` est un **tableau de blocs JSON**. C'est la source de vérité.
`MarkdownEditor` reste disponible en parallèle pour les usages markdown.

## Format des blocs (Phase 1)

Chaque bloc a un `id` (UUID) et un `type`. Selon le type :

| type           | champs        | description                |
|----------------|---------------|----------------------------|
| `paragraph`    | `content`     | texte brut                 |
| `heading_1..6` | `content`     | titre niveau 1 à 6         |
| `bullet_item`  | `content`     | élément de liste à puces   |
| `numbered_item`| `content`     | élément de liste numérotée |
| `quote`        | `content`     | citation                   |
| `code`         | `content`     | bloc de code (monospace)   |
| `divider`      | —             | séparateur horizontal      |

Phase 2 ajoutera : `checklist`, `image`, et le format de `content` passera de
`string` à `InlineNode[]` pour supporter le formatting inline (gras, italique,
couleur, lien). Phase 3 : `table`.

## Props

```jsx
<BlockEditor
  value={blocks}             // Block[]
  onChange={setBlocks}       // (Block[]) => void
  label, error, placeholder, disabled, className
  enabledBlocks={[...]}      // optionnel : restreindre les types disponibles
/>
```

## Interactions clavier

- `/` → ouvre le menu de commandes (filtrable au clavier, ↑ ↓ Entrée pour valider, Échap pour fermer)
- `# ` → titre 1, `## ` → 2, …, `###### ` → 6
- `- ` → liste à puces
- `1. ` → liste numérotée
- `> ` → citation
- ` ``` ` → bloc de code
- `---` → séparateur
- `Backspace` au début d'un bloc typé → repasse en paragraphe ; au début d'un paragraphe, fusionne avec le bloc précédent
- `Entrée` sur un item de liste vide → sort de la liste
- `Ctrl/Cmd + Z` / `Ctrl/Cmd + Shift + Z` → undo / redo

## Drag and drop

Chaque bloc affiche au survol :
- une poignée `+` pour insérer un bloc en dessous (ouvre le slash menu)
- une poignée `⋮⋮` pour glisser-déposer (réordonner)

Le drag-and-drop utilise l'API HTML5 native, pas de dépendance externe.

## Étendre — enregistrer un bloc custom

```js
import { registerBlock, newBlockId } from '@zen/core/shared/components/BlockEditor';

registerBlock({
  type: 'kpi',
  label: 'KPI',
  icon: '📊',
  keywords: ['kpi', 'metric', 'stat'],
  isText: false,
  create: () => ({ id: newBlockId(), type: 'kpi', value: 0 }),
  Component: ({ block, onChange, disabled }) => (
    <input
      type="number"
      value={block.value}
      disabled={disabled}
      onChange={(e) => onChange({ value: Number(e.target.value) })}
    />
  ),
});
```

Le nouveau type apparaît automatiquement dans le slash menu de tout
`<BlockEditor />` rendu après l'enregistrement. Pour les blocs **texte**, on
fournit à la place `isText: true`, `textTag`, `textClassName`, et optionnellement
`renderPrefix({ block, index, numberedIndex })` pour un préfixe (puce, numéro).

## Architecture interne

```
BlockEditor.client.js     orchestrateur : value/onChange, undo, slash menu, drag-drop
Block.client.js           wrapper d'un bloc : handles, contentEditable, paste sanitize
SlashMenu.client.js       menu flottant filtrable
blockRegistry.js          map type → définition, API publique d'extension
blockTypes/               un fichier par type built-in
utils/ids.js              UUID pour les blocs
utils/caret.js            gestion du caret dans un contentEditable
```

## Limitations connues (Phase 1)

- Inline formatting (gras, italique, couleur, lien) **pas encore** : tout est texte brut. Phase 2.
- Pas d'imbrication de listes.
- Paste : seul le texte brut est conservé (sanitize HTML).
- Tables : Phase 3.
