# Plan Technique - Intégration Novel Editor

## Vue d'ensemble

Intégration d'un éditeur riche type Notion basé sur **Novel.sh** (Tiptap) pour remplacer le textarea actuel en mode desktop, tout en conservant un textarea amélioré pour le mobile.

---

## 1. Architecture Générale

### 1.1 Structure des fichiers

```
keep-notes/
├── app/
│   └── api/
│       └── ai/
│           └── editor/               # Endpoints AI pour l'éditeur
│               ├── improve/route.ts
│               └── shorten/route.ts
├── components/
│   ├── editor/
│   │   ├── novel-editor.tsx          # Composant Novel principal
│   │   ├── mobile-textarea.tsx       # Fallback textarea mobile
│   │   ├── editor-container.tsx      # Switch desktop/mobile
│   │   ├── slash-commands.tsx        # Commandes slash (/heading, /list)
│   │   ├── ai-commands.tsx           # Commandes AI (/ai improve)
│   │   ├── editor-toolbar.tsx        # Toolbar sticky contextuelle
│   │   └── markdown-preview.tsx      # Preview mode carte
│   ├── note-inline-editor.tsx        # MODIFIÉ - Intègre EditorContainer
│   └── note-card.tsx                 # Inchangé - Affiche aperçu texte
├── lib/
│   ├── editor/
│   │   ├── novel-config.ts           # Configuration Tiptap/Novel
│   │   ├── markdown-converter.ts     # MD ↔ JSON conversion
│   │   ├── editor-utils.ts           # Helpers (extract text, etc.)
│   │   └── extensions/               # Extensions custom Tiptap
│   │       ├── checklist-extension.ts
│   │       └── ai-extension.ts
│   └── ai/
│       └── editor-commands.ts        # Intégration AI dans l'éditeur
├── hooks/
│   ├── use-novel-editor.ts           # Hook gestion état Novel
│   ├── use-editor-save.ts            # Hook sauvegarde auto
│   └── use-device-type.ts            # Détection desktop/mobile
└── types/
    └── editor.types.ts               # Types TypeScript pour l'éditeur
```

### 1.2 Flux de données

```
┌─────────────────────────────────────────────────────────────┐
│                        FLUX DE DONNÉES                      │
└─────────────────────────────────────────────────────────────┘

[Mode Liste - Édition]
     │
     ▼
┌─────────────────┐
│ EditorContainer │◄──── Détection mobile/desktop
└────────┬────────┘
         │
    ┌────┴────┐
    │         │
    ▼         ▼
┌───────┐  ┌──────────┐
│ Novel │  │ Textarea │
│Editor │  │ (mobile) │
└───┬───┘  └────┬─────┘
    │           │
    └─────┬─────┘
          ▼
┌─────────────────┐
│ Markdown (JSON) │◄──── Format de stockage
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│   Sauvegarde    │◄──── API /api/notes/[id]
│   Auto (1.5s)   │
└─────────────────┘

[Mode Carte - Affichage]
     │
     ▼
┌─────────────────┐
│  getNotePreview │◄──── Extrait texte brut du Markdown
│   (existing)    │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│   Note Card     │◄──── Affiche 2 lignes max
│   (unchanged)   │
└─────────────────┘
```

---

## 2. Dépendances

### 2.1 Core (obligatoires)

```bash
# Novel et Tiptap
npm install novel
npm install @tiptap/core @tiptap/starter-kit

# Extensions essentielles
npm install @tiptap/extension-task-list
npm install @tiptap/extension-task-item
npm install @tiptap/extension-placeholder
npm install @tiptap/extension-link
npm install @tiptap/extension-underline
npm install @tiptap/extension-highlight
npm install @tiptap/extension-code-block
npm install @tiptap/extension-blockquote
npm install @tiptap/extension-horizontal-rule

# Markdown
npm install @tiptap/extension-markdown
```

### 2.2 UI (shadcn/radix déjà présents)

```bash
# Déjà inclus avec shadcn
# - @radix-ui/react-popover
# - @radix-ui/react-toolbar
# - class-variance-authority
# - clsx / tailwind-merge
```

### 2.3 Types

```bash
npm install -D @types/tiptap
```

---

## 3. Composants détaillés

### 3.1 EditorContainer (`components/editor/editor-container.tsx`)

**Responsabilité :** Point d'entrée unique, détecte le device et route vers le bon éditeur.

```typescript
interface EditorContainerProps {
  content: string;              // Markdown
  onChange: (markdown: string) => void;
  placeholder?: string;
  enableAI?: boolean;           // Active les commandes AI
  readOnly?: boolean;
}

function EditorContainer(props: EditorContainerProps) {
  const isMobile = useMediaQuery('(max-width: 768px)');
  
  if (isMobile) {
    return <MobileTextarea {...props} />;
  }
  
  return <NovelEditor {...props} />;
}
```

### 3.2 NovelEditor (`components/editor/novel-editor.tsx`)

**Responsabilité :** Éditeur riche avec toutes les fonctionnalités.

**Props :**
```typescript
interface NovelEditorProps {
  value: string;                    // Markdown initial
  onChange: (markdown: string) => void;
  placeholder?: string;
  enableAI?: boolean;
  extensions?: Extension[];         // Extensions Tiptap additionnelles
}
```

**Configuration :**
```typescript
const defaultExtensions = [
  StarterKit.configure({
    heading: { levels: [1, 2, 3] },
    bulletList: {},
    orderedList: {},
    codeBlock: {},
    blockquote: {},
    horizontalRule: {},
  }),
  TaskList,
  TaskItem.configure({
    nested: true,
    HTMLAttributes: { class: 'flex items-start gap-2' }
  }),
  Link.configure({
    openOnClick: false,
    HTMLAttributes: { class: 'text-primary underline' }
  }),
  Underline,
  Highlight.configure({ multicolor: true }),
  Placeholder.configure({
    placeholder: 'Écris / pour voir les commandes...'
  }),
  Markdown.configure({
    html: false,
    transformCopiedText: true,
  }),
];
```

### 3.3 MobileTextarea (`components/editor/mobile-textarea.tsx`)

**Responsabilité :** Textarea amélioré pour mobile avec toolbar minimal.

**Features :**
- Toolbar sticky bottom (B, I, List, Checklist)
- Markdown shortcuts (## pour H2, - pour liste)
- Auto-grow height
- Swipe gestures (optionnel)

```typescript
interface MobileTextareaProps {
  value: string;
  onChange: (value: string) => void;
  placeholder?: string;
}
```

### 3.4 SlashCommands (`components/editor/slash-commands.tsx`)

**Responsabilité :** Menu commandes déclenché par `/`.

**Commandes implémentées :**

| Commande | Action | Icône |
|----------|--------|-------|
| `/h1` | Titre niveau 1 | Heading1 |
| `/h2` | Titre niveau 2 | Heading2 |
| `/h3` | Titre niveau 3 | Heading3 |
| `/list` | Liste à puces | List |
| `/num` | Liste numérotée | ListOrdered |
| `/check` | Checklist | CheckSquare |
| `/quote` | Citation | Quote |
| `/code` | Bloc de code | Code |
| `/line` | Ligne horizontale | Minus |
| `/ai` | Commandes AI | Sparkles |

### 3.5 AICommands (`components/editor/ai-commands.tsx`)

**Responsabilité :** Intégration AI dans l'éditeur.

**Commandes AI :**

| Commande | Description | Raccourci |
|----------|-------------|-----------|
| `/ai improve` | Améliore la rédaction | Ctrl+Shift+I |
| `/ai shorten` | Raccourcit le texte | Ctrl+Shift+S |
| `/ai longer` | Développe le texte | Ctrl+Shift+L |
| `/ai fix` | Corrige orthographe | - |
| `/ai title` | Génère titre depuis contenu | - |

### 3.6 EditorToolbar (`components/editor/editor-toolbar.tsx`)

**Responsabilité :** Toolbar contextuelle (bubble menu) et sticky.

**Modes :**

1. **Bubble Menu** (texte sélectionné) :
   - Bold, Italic, Strike, Underline
   - Highlight
   - Link
   - AI Improve (si sélection > 10 mots)

2. **Floating Menu** (ligne vide) :
   - + pour ajouter bloc
   - Raccourcis rapides

---

## 4. Configuration Novel

### 4.1 novel-config.ts

```typescript
// lib/editor/novel-config.ts

import { Extension } from '@tiptap/core';
import StarterKit from '@tiptap/starter-kit';
import TaskList from '@tiptap/extension-task-list';
import TaskItem from '@tiptap/extension-task-item';
// ... autres imports

export const novelConfig = {
  // Extensions de base
  extensions: [
    StarterKit.configure({
      heading: { levels: [1, 2, 3] },
    }),
    TaskList,
    TaskItem.configure({ nested: true }),
    Link.configure({ openOnClick: false }),
    Placeholder.configure({
      placeholder: ({ node }) => {
        if (node.type.name === 'heading') return 'Titre...';
        return 'Écris / pour commencer...';
      }
    }),
    Markdown.configure({
      transformCopiedText: true,
      transformPastedText: true,
    }),
  ],

  // Options d'éditeur
  editorProps: {
    attributes: {
      class: 'prose prose-sm dark:prose-invert max-w-none focus:outline-none',
    },
    handleDOMEvents: {
      keydown: (view, event) => {
        // Custom keyboard shortcuts
        if (event.key === 'Tab' && event.shiftKey) {
          // Outdent
          return true;
        }
        return false;
      }
    }
  },

  // Thème
  theme: {
    color: 'inherit',
    backgroundColor: 'transparent',
  }
};

// Commandes slash
export const slashCommands = [
  {
    title: 'Titre 1',
    description: 'Grand titre',
    searchTerms: ['h1', 'title', 'titre'],
    icon: Heading1,
    command: ({ editor, range }) => {
      editor.chain().focus().deleteRange(range).toggleHeading({ level: 1 }).run();
    }
  },
  // ... autres commandes
];
```

---

## 5. Conversion Markdown

### 5.1 markdown-converter.ts

**Responsabilité :** Convertir entre Markdown (stockage) et JSON Novel (édition).

```typescript
// lib/editor/markdown-converter.ts

import { generateJSON } from '@tiptap/html';
import { generateHTML } from '@tiptap/core';
import { novelExtensions } from './novel-config';

/**
 * Convertit Markdown en JSON Novel
 * Utilisé au chargement d'une note
 */
export function markdownToNovelJSON(markdown: string): JSONContent {
  // Tiptap Markdown extension parse le MD
  const html = markdownToHtml(markdown); // Utilise marked ou similar
  return generateJSON(html, novelExtensions);
}

/**
 * Convertit JSON Novel en Markdown
 * Utilisé pour la sauvegarde
 */
export function novelJSONToMarkdown(json: JSONContent): string {
  const html = generateHTML(json, novelExtensions);
  return htmlToMarkdown(html); // Utilise turndown ou similar
}

/**
 * Extrait le texte brut pour les cartes
 * Utilisé dans note-card.tsx
 */
export function extractTextFromNovelJSON(json: JSONContent): string {
  // Récursion sur les nodes pour extraire le texte
  let text = '';
  
  function traverse(node: any) {
    if (node.text) {
      text += node.text + ' ';
    }
    if (node.content) {
      node.content.forEach(traverse);
    }
  }
  
  traverse(json);
  return text.trim().slice(0, 200); // Limite pour les cartes
}
```

---

## 6. Hooks

### 6.1 useNovelEditor (`hooks/use-novel-editor.ts`)

```typescript
import { useEditor } from '@tiptap/react';
import { markdownToNovelJSON, novelJSONToMarkdown } from '@/lib/editor/markdown-converter';

export function useNovelEditor(initialMarkdown: string) {
  const [markdown, setMarkdown] = useState(initialMarkdown);
  
  const editor = useEditor({
    extensions: novelExtensions,
    content: markdownToNovelJSON(initialMarkdown),
    onUpdate: ({ editor }) => {
      const json = editor.getJSON();
      const newMarkdown = novelJSONToMarkdown(json);
      setMarkdown(newMarkdown);
    },
  });

  const updateContent = useCallback((newMarkdown: string) => {
    if (editor && newMarkdown !== markdown) {
      editor.commands.setContent(markdownToNovelJSON(newMarkdown));
    }
  }, [editor, markdown]);

  return {
    editor,
    markdown,
    updateContent,
  };
}
```

### 6.2 useEditorSave (`hooks/use-editor-save.ts`)

```typescript
import { useDebounce } from './use-debounce';

export function useEditorSave(noteId: string) {
  const [content, setContent] = useState('');
  const [isDirty, setIsDirty] = useState(false);
  const debouncedContent = useDebounce(content, 1500);

  useEffect(() => {
    if (debouncedContent && isDirty) {
      saveInline(noteId, { content: debouncedContent });
      setIsDirty(false);
    }
  }, [debouncedContent, noteId, isDirty]);

  const updateContent = (newContent: string) => {
    setContent(newContent);
    setIsDirty(true);
  };

  return {
    content,
    updateContent,
    isDirty,
    isSaving: isDirty,
  };
}
```

### 6.3 useDeviceType (`hooks/use-device-type.ts`)

```typescript
import { useMediaQuery } from './use-media-query';

export function useDeviceType() {
  const isMobile = useMediaQuery('(max-width: 768px)');
  const isTablet = useMediaQuery('(min-width: 769px) and (max-width: 1024px)');
  const isDesktop = useMediaQuery('(min-width: 1025px)');
  
  return {
    isMobile,
    isTablet,
    isDesktop,
    isTouch: isMobile || isTablet,
  };
}
```

---

## 7. Intégration avec NoteInlineEditor

### 7.1 Modification de note-inline-editor.tsx

```typescript
// components/note-inline-editor.tsx

import { EditorContainer } from './editor/editor-container';

export function NoteInlineEditor({ note, onChange, onDelete, onArchive }: NoteInlineEditorProps) {
  const { t } = useLanguage();
  const { deviceType } = useDeviceType();
  
  // ... autres states (title, isMarkdown, etc.)

  return (
    <div className="flex h-full flex-col overflow-hidden">
      {/* Toolbar existante (image, link, markdown toggle, AI) */}
      <div className="flex shrink-0 items-center justify-between border-b...">
        {/* ... existing toolbar buttons ... */}
      </div>

      {/* Zone d'édition - NOUVEAU */}
      <div className="flex flex-1 flex-col overflow-y-auto px-8 py-5">
        {/* Titre */}
        <input
          type="text"
          className="..."
          value={title}
          onChange={(e) => { 
            changeTitle(e.target.value); 
            scheduleSave(); 
          }}
        />

        {/* Contenu - REMPLACÉ PAR EditorContainer */}
        <div className="mt-4 flex flex-1 flex-col">
          {note.type === 'text' ? (
            <EditorContainer
              content={content}
              onChange={(newContent) => {
                changeContent(newContent);
                scheduleSave();
              }}
              placeholder={t('notes.takeNote')}
              enableAI={true}
            />
          ) : (
            /* Checklist existante - Gardée telle quelle ou migrée vers Tiptap */
            <ChecklistEditor ... />
          )}
        </div>
      </div>
    </div>
  );
}
```

---

## 8. Mode Carte (NoteCard)

### 8.1 Impact sur note-card.tsx

**Aucune modification majeure requise.**

Le système actuel utilise `getNotePreview()` qui extrait le texte du Markdown. Comme on continue de stocker du Markdown, la compatibilité est assurée.

```typescript
// Pas de changement nécessaire dans note-card.tsx
// getNotePreview() continue de fonctionner avec le Markdown

function getNotePreview(note: Note, maxLength = 150): string {
  const content = note.content || '';
  // Supprime la syntaxe Markdown pour l'affichage
  const plainText = content
    .replace(/#+ /g, '')           // Titres
    .replace(/\*\*/g, '')          // Gras
    .replace(/\*/g, '')            // Italique
    .replace(/- \[([ x])\] /g, '') // Checklists
    .replace(/- /g, '')            // Listes
    .replace(/\n/g, ' ');          // Sauts de ligne
  
  return plainText.slice(0, maxLength) + (plainText.length > maxLength ? '...' : '');
}
```

---

## 9. API AI pour l'Éditeur

### 9.1 POST /api/ai/editor/improve

```typescript
// app/api/ai/editor/improve/route.ts

import { NextRequest, NextResponse } from 'next/server';
import { auth } from '@/auth';
import { getAIProvider } from '@/lib/ai/factory';

export async function POST(req: NextRequest) {
  const session = await auth();
  if (!session?.user?.id) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }

  const { text, language } = await req.json();
  
  if (!text || text.length < 10) {
    return NextResponse.json({ error: 'Text too short' }, { status: 400 });
  }

  const provider = getAIProvider(await getSystemConfig());
  
  const prompt = `Améliore ce texte en ${language || 'français'} pour le rendre plus clair et fluide. 
Garde le même sens mais améliore la formulation.

Texte: ${text}

Réponds uniquement avec le texte amélioré, sans explications.`;

  const improved = await provider.generateText(prompt);
  
  return NextResponse.json({ text: improved.trim() });
}
```

### 9.2 POST /api/ai/editor/shorten

Similaire avec prompt pour raccourcir.

---

## 10. Responsive Strategy

### 10.1 Breakpoints

| Breakpoint | Device | Composant | Raison |
|------------|--------|-----------|--------|
| `< 640px` | Mobile | Textarea | Performance, touch friendly |
| `640px - 768px` | Large Mobile | Textarea | Touche bientôt desktop |
| `768px - 1024px` | Tablet | Novel (light) | Plus d'espace, mais UX tactile |
| `> 1024px` | Desktop | Novel (full) | Toutes les fonctionnalités |

### 10.2 Détection côté serveur (optionnel)

```typescript
// Pour éviter le flash au chargement
import { headers } from 'next/headers';
import { userAgent } from 'next/server';

export function detectMobile() {
  const { device } = userAgent({ headers: headers() });
  return device?.type === 'mobile';
}
```

---

## 11. Checklists

### 11.1 Approche recommandée : Hybride

**Stockage :** Markdown natif `- [ ]` et `- [x]`

**Pourquoi ?**
- ✅ Standard Markdown (portable)
- ✅ Lisible en mode carte
- ✅ Éditable partout
- ✅ Compatible export/import

**Implémentation :**
```markdown
# Ma note

- [ ] Tâche à faire
- [x] Tâche complétée
- [ ] Sous-tâche
  - [ ] Item 1
  - [x] Item 2
```

### 11.2 Extensions Tiptap pour checklists

```typescript
import TaskList from '@tiptap/extension-task-list';
import TaskItem from '@tiptap/extension-task-item';

TaskItem.configure({
  nested: true,
  HTMLAttributes: {
    class: 'flex items-start gap-2 my-1',
  },
});
```

---

## 12. Tests

### 12.1 Tests unitaires

```typescript
// __tests__/markdown-converter.test.ts

describe('Markdown Converter', () => {
  test('converts checklist markdown to JSON', () => {
    const md = '- [ ] Task 1\n- [x] Task 2';
    const json = markdownToNovelJSON(md);
    
    expect(json.content[0].type).toBe('taskList');
    expect(json.content[0].content).toHaveLength(2);
  });

  test('converts headings correctly', () => {
    const md = '# Title\n## Subtitle';
    const json = markdownToNovelJSON(md);
    
    expect(json.content[0].type).toBe('heading');
    expect(json.content[0].attrs.level).toBe(1);
  });

  test('extracts plain text for cards', () => {
    const json = {
      type: 'doc',
      content: [
        { type: 'heading', content: [{ text: 'Title' }] },
        { type: 'paragraph', content: [{ text: 'Content' }] },
      ]
    };
    
    const text = extractTextFromNovelJSON(json);
    expect(text).toBe('Title Content');
  });
});
```

### 12.2 Tests e2e

```typescript
// tests/editor.spec.ts

test('user can create a checklist in novel editor', async ({ page }) => {
  await page.goto('/');
  await page.click('[data-testid="new-note"]');
  await page.click('[data-testid="list-view-toggle"]');
  
  // Type slash command
  await page.click('[data-testid="note-editor"]');
  await page.keyboard.type('/check');
  await page.click('text=Checklist');
  
  // Type checklist item
  await page.keyboard.type('My task');
  
  // Verify checkbox appears
  await expect(page.locator('input[type="checkbox"]')).toBeVisible();
});
```

---

## 13. Timeline Détaillée

### Jour 1 : Setup et Configuration (4-5h)

**Tâches :**
- [ ] Installer toutes les dépendances Novel/Tiptap
- [ ] Créer `lib/editor/novel-config.ts`
- [ ] Créer `components/editor/novel-editor.tsx` (basique)
- [ ] Vérifier que Novel s'affiche correctement

**Livrable :** Novel affiche un texte simple, sauvegarde en Markdown.

### Jour 2 : Fonctionnalités Essentielles (6-7h)

**Tâches :**
- [ ] Créer `slash-commands.tsx` avec toutes les commandes
- [ ] Configurer extensions checklists
- [ ] Créer `editor-toolbar.tsx` (bubble menu)
- [ ] Intégrer Markdown converter
- [ ] Tester import/export Markdown

**Livrable :** Éditeur fonctionnel avec slash commands et checklists.

### Jour 3 : Intégration Système (6-7h)

**Tâches :**
- [ ] Modifier `note-inline-editor.tsx` pour utiliser EditorContainer
- [ ] Créer `hooks/use-novel-editor.ts`
- [ ] Créer `hooks/use-editor-save.ts`
- [ ] Connecter sauvegarde auto existante
- [ ] Vérifier compatibilité mode liste/carte

**Livrable :** Novel fonctionne dans l'app, sauvegarde auto OK.

### Jour 4 : Mobile et Responsive (5-6h)

**Tâches :**
- [ ] Créer `mobile-textarea.tsx` avec toolbar
- [ ] Créer `editor-container.tsx` avec switch desktop/mobile
- [ ] Implémenter `use-device-type.ts`
- [ ] Tester sur différentes tailles d'écran
- [ ] Optimiser performance mobile

**Livrable :** Textarea sur mobile, Novel sur desktop.

### Jour 5 : AI et Polish (5-6h)

**Tâches :**
- [ ] Créer `/api/ai/editor/improve` et `/shorten`
- [ ] Créer `ai-commands.tsx`
- [ ] Intégrer commandes AI dans Novel
- [ ] Tests et correction de bugs
- [ ] Documentation

**Livrable :** Commandes AI fonctionnelles, tests passent.

### Jour 6 : Tests et Déploiement (4-5h)

**Tâches :**
- [ ] Tests cross-navigateurs
- [ ] Test avec 50+ notes
- [ ] Test utilisateurs (interne)
- [ ] Corrections finales
- [ ] Merge et déploiement

**Total : 6 jours (30-36h de développement)**

---

## 14. Risques et Mitigations

| Risque | Probabilité | Impact | Mitigation |
|--------|-------------|--------|------------|
| Bundle size trop grand | Moyenne | Moyen | Tree-shaking, lazy load Novel |
| Perf sur vieux devices | Moyenne | Haut | Fallback textarea automatique |
| Migration données | Faible | Haut | Tests exhaustifs MD ↔ JSON |
| UX change trop radicale | Faible | Moyen | Feature flag, rollback possible |
| Conflits avec checklists existantes | Moyenne | Haut | Garder format MD compatible |

---

## 15. Checklist de Validation

Avant de merger :

- [ ] Novel fonctionne sur Chrome, Firefox, Safari
- [ ] Mobile textarea fonctionne sur iOS/Android
- [ ] Sauvegarde auto fonctionne (pas de régression)
- [ ] Mode liste ↔ Mode carte sans perte de données
- [ ] Checklists s'affichent correctement dans les deux modes
- [ ] Export Markdown identique à l'entrée
- [ ] Tests unitaires passent
- [ ] Tests e2e passent
- [ ] Bundle size < +150KB gzipped
- [ ] Performance OK avec 50 notes
- [ ] Accessibilité (keyboard navigation, ARIA)

---

## 16. Ressources

### Documentation
- [Novel.sh](https://novel.sh/)
- [Tiptap Docs](https://tiptap.dev/)
- [ProseMirror (base de Tiptap)](https://prosemirror.net/)

### Exemples
- [Novel sur GitHub](https://github.com/steven-tey/novel)
- [Tiptap examples](https://tiptap.dev/examples)

### Articles
- [Building a Notion-like editor](https://tiptap.dev/blog/building-a-notion-like-editor)

---

**Document créé le :** 2024  
**Version :** 1.0  
**Auteur :** Assistant Claude  
**Statut :** Prêt pour développement

---

## Questions/Réponses

**Q : Puis-je encore utiliser Markdown brut ?**  
R : Oui, le stockage reste en Markdown. Vous pouvez éditer le MD directement si besoin.

**Q : Et si Novel ne me plaît pas ?**  
R : Retour au textarea est instantané (même format MD). Pas de vendor lock-in.

**Q : Les checklists existantes seront-elles conservées ?**  
R : Oui, format MD identique. Migration transparente.

**Q : Performance sur mobile ?**  
R : Textarea dédié mobile, donc meilleure perf qu'actuellement sur mobile.
