---
phase: 05-nuxt-content-setup-renderer
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - nuxt.config.ts
  - app/assets/css/main.css
  - content.config.ts
  - package.json
autonomous: true
requirements:
  - BLOG-01
  - BLOG-04

must_haves:
  truths:
    - "@nuxt/content est installé et `pnpm dev` démarre sans erreur"
    - "Shiki est configuré avec les langages Kotlin, Java, TypeScript, Shell et les thèmes github-light/github-dark"
    - "Les collections blog_fr et blog_en sont déclarées dans content.config.ts avec le bon prefix i18n"
    - "@tailwindcss/typography est chargé via `@plugin` dans main.css"
  artifacts:
    - path: "content.config.ts"
      provides: "Déclaration des collections bilingues blog_fr + blog_en avec schema Zod"
      exports: ["defineContentConfig"]
    - path: "nuxt.config.ts"
      provides: "Module @nuxt/content + config Shiki dual-theme + sqliteConnector native"
      contains: "@nuxt/content"
    - path: "app/assets/css/main.css"
      provides: "Plugin @tailwindcss/typography chargé"
      contains: "@plugin"
  key_links:
    - from: "nuxt.config.ts content.build.markdown.highlight"
      to: "Shiki dual-theme github-light/github-dark"
      via: "theme.default + theme.dark"
      pattern: "github-light.*github-dark|github-dark.*github-light"
    - from: "content.config.ts collections.blog_fr"
      to: "content/fr/blog/**/*.md"
      via: "source.include"
      pattern: "fr/blog/\\*\\*"
---

<objective>
Installer `@nuxt/content` v3 et `@tailwindcss/typography`, puis configurer le système de rendu markdown — Shiki dual-theme, collections bilingues, connecteur SQLite natif.

Purpose: Cette phase pose les fondations du CMS. Sans elle, les phases 6, 7 et 8 ne peuvent pas fonctionner. La configuration doit être définitive — aucun retour en arrière attendu.

Output:
- `@nuxt/content` installé et déclaré dans `nuxt.config.ts`
- `content.config.ts` avec collections `blog_fr` + `blog_en`
- Shiki configuré pour Kotlin, Java, TypeScript, Shell avec thèmes dark/light
- `@tailwindcss/typography` chargé via `@plugin` dans `main.css`
- `pnpm dev` démarre sans erreur
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/05-nuxt-content-setup-renderer/05-CONTEXT.md
@.planning/phases/05-nuxt-content-setup-renderer/05-RESEARCH.md

<interfaces>
<!-- État actuel de nuxt.config.ts — ne PAS réécrire, uniquement étendre -->
<!-- nuxt.config.ts lignes 7-14 (modules) : -->
```typescript
modules: [
  '@nuxt/ui',
  '@nuxtjs/i18n',
  '@nuxt/eslint',
  '@nuxtjs/sitemap',
  'nuxt-gtag',
  '@nuxt/image'
],
```

<!-- nuxt.config.ts lignes 24-30 (colorMode) : -->
```typescript
colorMode: {
  preference: 'dark',
  fallback: 'dark',
  storage: 'cookie',
  storageKey: 'nuxt-color-mode',
  classSuffix: ''   // ← CRITIQUE: Shiki dual-theme nécessite classSuffix: '' pour html.dark
},
```

<!-- État actuel de app/assets/css/main.css : -->
```css
@import "tailwindcss";
@import "@nuxt/ui";

@theme {
  --color-brand-500: #85cb85;
  /* ... autres tokens brand */
}
```
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Installer @nuxt/content et @tailwindcss/typography</name>
  <files>package.json</files>
  <read_first>
    - package.json (vérifier pnpm.onlyBuiltDependencies existant, ne pas écraser)
  </read_first>
  <action>
    Exécuter les deux commandes d'installation suivantes dans l'ordre :

    ```bash
    pnpm add @nuxt/content
    pnpm add -D @tailwindcss/typography
    ```

    Versions cibles : `@nuxt/content@^3.6.3`, `@tailwindcss/typography@^0.5.x`.

    NE PAS ajouter `better-sqlite3` — le connecteur natif Node 22 sera utilisé via `experimental.sqliteConnector: 'native'` dans nuxt.config.ts (Task 2).

    Si `pnpm add` échoue avec une erreur de script build SQLite, c'est normal sans la config native — continuer vers Task 2 qui la résout.
  </action>
  <verify>
    ```bash
    grep '"@nuxt/content"' package.json
    grep '"@tailwindcss/typography"' package.json
    ```
    Les deux lignes doivent apparaître.
  </verify>
  <acceptance_criteria>
    - `package.json` contient `"@nuxt/content"` dans `dependencies`
    - `package.json` contient `"@tailwindcss/typography"` dans `devDependencies`
    - `node_modules/@nuxt/content` existe
    - `node_modules/@tailwindcss/typography` existe
  </acceptance_criteria>
  <done>Les deux packages sont installés via pnpm sans erreur bloquante.</done>
</task>

<task type="auto">
  <name>Task 2: Configurer nuxt.config.ts et app/assets/css/main.css</name>
  <files>nuxt.config.ts, app/assets/css/main.css</files>
  <read_first>
    - nuxt.config.ts (lire INTÉGRALEMENT avant de modifier — ne jamais réécrire, uniquement étendre)
    - app/assets/css/main.css (lire INTÉGRALEMENT)
  </read_first>
  <action>
    **1. nuxt.config.ts — deux modifications :**

    a) Ajouter `'@nuxt/content'` à la fin du tableau `modules` (après `'@nuxt/image'`) :
    ```typescript
    modules: [
      '@nuxt/ui',
      '@nuxtjs/i18n',
      '@nuxt/eslint',
      '@nuxtjs/sitemap',
      'nuxt-gtag',
      '@nuxt/image',
      '@nuxt/content'    // ← ligne ajoutée
    ],
    ```

    b) Ajouter le bloc `content` après le bloc `gtag` existant (avant la fermeture `}`) :
    ```typescript
    content: {
      build: {
        markdown: {
          highlight: {
            theme: {
              default: 'github-light',
              dark: 'github-dark'
            },
            langs: ['kotlin', 'java', 'typescript', 'shell', 'bash', 'json', 'vue', 'html', 'css']
          }
        }
      },
      experimental: {
        sqliteConnector: 'native'
      }
    }
    ```

    NE PAS utiliser `nativeSqlite: true` (déprécié). Utiliser exclusivement `sqliteConnector: 'native'`.
    NE PAS modifier `colorMode.classSuffix` — doit rester `''` pour que Shiki dual-theme fonctionne via `html.dark`.

    **2. app/assets/css/main.css — une ligne ajoutée :**

    Ajouter `@plugin "@tailwindcss/typography";` après `@import "@nuxt/ui";` :
    ```css
    @import "tailwindcss";
    @import "@nuxt/ui";
    @plugin "@tailwindcss/typography";
    ```

    NE PAS utiliser `plugins: [require('@tailwindcss/typography')]` dans tailwind.config.js — cette syntaxe est ignorée en Tailwind v4. La syntaxe `@plugin` dans le CSS est la seule valide.
    NE PAS toucher le bloc `@theme` existant avec les tokens `--color-brand-*`.
  </action>
  <verify>
    ```bash
    grep "'@nuxt/content'" nuxt.config.ts
    grep "github-dark" nuxt.config.ts
    grep "sqliteConnector" nuxt.config.ts
    grep "kotlin" nuxt.config.ts
    grep '@plugin "@tailwindcss/typography"' app/assets/css/main.css
    ```
    Les cinq lignes doivent retourner un résultat.
  </verify>
  <acceptance_criteria>
    - `nuxt.config.ts` contient `'@nuxt/content'` dans le tableau `modules`
    - `nuxt.config.ts` contient le bloc `content.build.markdown.highlight.theme` avec `default: 'github-light'` et `dark: 'github-dark'`
    - `nuxt.config.ts` contient `sqliteConnector: 'native'` (PAS `nativeSqlite`)
    - `nuxt.config.ts` liste au minimum ces langages Shiki : `'kotlin'`, `'java'`, `'typescript'`, `'shell'`
    - `nuxt.config.ts` ne contient PAS `nativeSqlite`
    - `app/assets/css/main.css` contient `@plugin "@tailwindcss/typography";` sur sa propre ligne
    - `app/assets/css/main.css` contient toujours le bloc `@theme` avec `--color-brand-500`
  </acceptance_criteria>
  <done>nuxt.config.ts étend le module @nuxt/content avec Shiki dual-theme. main.css charge @tailwindcss/typography via @plugin.</done>
</task>

<task type="auto">
  <name>Task 3: Créer content.config.ts avec collections bilingues</name>
  <files>content.config.ts</files>
  <read_first>
    - nuxt.config.ts (vérifier i18n.strategy et i18n.defaultLocale pour confirmer le prefix des collections)
    - .planning/phases/05-nuxt-content-setup-renderer/05-RESEARCH.md (Pattern 2 — content.config.ts)
  </read_first>
  <action>
    Créer `content.config.ts` à la RACINE du projet (même niveau que `nuxt.config.ts`).

    Contenu exact :
    ```typescript
    import { defineContentConfig, defineCollection, z } from '@nuxt/content'

    const blogSchema = z.object({
      title: z.string(),
      description: z.string(),
      date: z.string(),
      tags: z.array(z.string()).optional(),
      image: z.string().optional(),
    })

    export default defineContentConfig({
      collections: {
        blog_fr: defineCollection({
          type: 'page',
          source: { include: 'fr/blog/**/*.md', prefix: '/blog' },
          schema: blogSchema,
        }),
        blog_en: defineCollection({
          type: 'page',
          source: { include: 'en/blog/**/*.md', prefix: '/en/blog' },
          schema: blogSchema,
        }),
      },
    })
    ```

    Justification des prefixes :
    - `blog_fr` → prefix `/blog` (FR est la locale par défaut avec `prefix_except_default`, donc pas de `/fr/` dans l'URL)
    - `blog_en` → prefix `/en/blog` (EN reçoit le préfixe de langue)

    Ce schema minimal sera étendu en Phase 7 (author, og:image, etc.) — ne pas anticiper.
  </action>
  <verify>
    ```bash
    test -f content.config.ts && echo "EXISTS"
    grep "blog_fr" content.config.ts
    grep "blog_en" content.config.ts
    grep "prefix: '/blog'" content.config.ts
    grep "prefix: '/en/blog'" content.config.ts
    ```
  </verify>
  <acceptance_criteria>
    - `content.config.ts` existe à la racine du projet
    - Contient l'export `defineContentConfig`
    - Contient la collection `blog_fr` avec `source.include: 'fr/blog/**/*.md'` et `source.prefix: '/blog'`
    - Contient la collection `blog_en` avec `source.include: 'en/blog/**/*.md'` et `source.prefix: '/en/blog'`
    - Le schema Zod contient les champs `title`, `description`, `date` (requis) et `tags`, `image` (optionnels)
    - `pnpm dev` démarre sans erreur après ces trois tasks (vérification smoke finale)
  </acceptance_criteria>
  <done>content.config.ts créé avec collections bilingues. `pnpm dev` démarre sans erreur — l'infrastructure @nuxt/content est opérationnelle.</done>
</task>

</tasks>

<threat_model>
## Trust Boundaries

| Boundary | Description |
|----------|-------------|
| Système de fichiers → Parser @nuxt/content | Fichiers markdown lus au build — source contrôlée (auteur uniquement, pas d'input utilisateur) |
| Node.js 22 → SQLite natif | Connecteur natif utilisé au lieu de better-sqlite3 — pas d'exposition réseau |

## STRIDE Threat Register

| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-05-01 | Tampering | `content.config.ts` source.include glob | accept | Seuls les fichiers `*.md` dans `content/` sont indexés — aucun input utilisateur dans cette phase, glob contrôlé par l'auteur |
| T-05-02 | Information Disclosure | Shiki HTML output | accept | Shiki génère du HTML échappé — pas de XSS possible via blocs de code |
| T-05-03 | Denial of Service | SQLite natif Node 22 au build | accept | Build-time uniquement, pas d'exposition runtime — risque nul en production |
| T-05-04 | Elevation of Privilege | `experimental.sqliteConnector: 'native'` | accept | Connecteur natif Node.js — pas de binary externe, surface d'attaque réduite vs better-sqlite3 |
</threat_model>

<verification>
Après exécution du plan 01, vérifier :

```bash
# 1. Packages installés
grep '"@nuxt/content"' package.json && grep '"@tailwindcss/typography"' package.json

# 2. nuxt.config.ts étendu correctement
grep "'@nuxt/content'" nuxt.config.ts
grep "github-dark" nuxt.config.ts
grep "sqliteConnector.*native" nuxt.config.ts
# NE DOIT PAS contenir l'option dépréciée :
grep "nativeSqlite" nuxt.config.ts  # doit retourner RIEN

# 3. CSS typography
grep '@plugin "@tailwindcss/typography"' app/assets/css/main.css

# 4. content.config.ts collections
grep "blog_fr\|blog_en" content.config.ts

# 5. Smoke test
pnpm dev  # doit démarrer sans erreur
```
</verification>

<success_criteria>
- `pnpm dev` démarre sans erreur après installation et configuration
- `nuxt.config.ts` contient `'@nuxt/content'` dans modules et le bloc `content` avec Shiki dual-theme + langages
- `content.config.ts` existe avec les deux collections bilingues et le bon prefix i18n
- `app/assets/css/main.css` charge `@tailwindcss/typography` via `@plugin`
- `pnpm typecheck` passe (0 erreur TypeScript)
</success_criteria>

<output>
Après completion, créer `.planning/phases/05-nuxt-content-setup-renderer/05-01-SUMMARY.md`
</output>
