--- phase: 05-nuxt-content-setup-renderer plan: 02 type: execute wave: 2 depends_on: - '01' files_modified: - app/components/content/ProseImg.vue - app/components/content/Alert.vue - app/components/content/ProseImg.vue - app/components/content/Alert.vue - content/fr/blog/test-kotlin-syntax.md - content/en/blog/test-kotlin-syntax.md - app/pages/test.vue autonomous: false requirements: - BLOG-01 - BLOG-05 must_haves: truths: - "Un article markdown avec un bloc Kotlin est rendu avec coloration syntaxique visible" - "Une image référencée dans l'article s'affiche via NuxtImg avec lazy loading et srcset" - "Un tableau markdown est rendu avec le style prose correct" - "Un callout ::alert{type='info'} affiche un UAlert stylisé Nuxt UI" - "Les quatre types de callout (info, warning, tip, danger) fonctionnent" artifacts: - path: "app/components/content/ProseImg.vue" provides: "Override ProseImg → NuxtImg optimisé" exports: ["default (component)"] - path: "app/components/content/Alert.vue" provides: "Composant MDC callout via UAlert" exports: ["default (component)"] - path: "content/fr/blog/test-kotlin-syntax.md" provides: "Article de test FR couvrant les 4 success criteria" contains: "```kotlin" - path: "content/en/blog/test-kotlin-syntax.md" provides: "Article de test EN — même slug" contains: "```kotlin" key_links: - from: "content/fr/blog/test-kotlin-syntax.md" to: "app/components/content/ProseImg.vue" via: "ContentRenderer détecte les balises img et les route vers ProseImg" pattern: "ProseImg" - from: "content/fr/blog/test-kotlin-syntax.md" to: "app/components/content/Alert.vue" via: "MDC ::alert{type} appelle Alert.vue" pattern: "::alert" --- Créer les composants de rendu markdown (ProseImg + Alert) et les articles de test permettant de valider visuellement les 4 success criteria de la phase. Purpose: Les composants MDC sont le liant entre le markdown brut et le rendu visuel. ProseImg garantit que chaque image passe par NuxtImg (BLOG-05). Alert garantit que les callouts ::alert sont rendus comme des composants Nuxt UI stylisés (BLOG-01). Output: - `app/components/content/ProseImg.vue` — override transparent NuxtImg - `app/components/content/Alert.vue` — callout MDC avec 4 types (info/warning/tip/danger) - `content/fr/blog/test-kotlin-syntax.md` — article de test couvrant les 4 critères - `content/en/blog/test-kotlin-syntax.md` — version EN du même article - Checkpoint visuel validant rendu Kotlin coloré + image NuxtImg + tableau + callout @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/05-nuxt-content-setup-renderer/05-CONTEXT.md @.planning/phases/05-nuxt-content-setup-renderer/05-RESEARCH.md @.planning/phases/05-nuxt-content-setup-renderer/05-UI-SPEC.md @.planning/phases/05-nuxt-content-setup-renderer/05-01-SUMMARY.md ```vue ``` ```typescript const props = withDefaults(defineProps(), { showLevel: true, showImage: true, }) const levelColor = computed(() => { switch (techData.value.level) { case 'Advanced': return 'success' as const // ... } }) ``` ```typescript components: [ { path: '~/components', pathPrefix: false, // composants dans content/ sont auto-importés ET reconnus MDC }, ], ``` ```html
```
Task 1: Créer les composants MDC ProseImg.vue et Alert.vue app/components/content/ProseImg.vue, app/components/content/Alert.vue - app/components/content/ (vérifier si le dossier existe — le créer si nécessaire) - .planning/phases/05-nuxt-content-setup-renderer/05-RESEARCH.md (Pattern 4 ProseImg, Pattern 5 Alert) - .planning/phases/05-nuxt-content-setup-renderer/05-UI-SPEC.md (Component Inventory, tableau iconMap/colorMap) Créer le dossier `app/components/content/` s'il n'existe pas. **1. Créer `app/components/content/ProseImg.vue` :** ```vue ``` NuxtImg est auto-importé par @nuxt/image — pas d'import explicite nécessaire. NE PAS ajouter `loading="lazy"` explicite sur NuxtImg — @nuxt/image gère lazy par défaut. **2. Créer `app/components/content/Alert.vue` :** ```vue ``` CRITIQUE : `` est OBLIGATOIRE — sans cette ligne, le contenu entre `::alert` et `::` n'est pas rendu (Pitfall 4 RESEARCH.md). UAlert et ContentSlot sont auto-importés — pas d'import explicite. ```bash test -f app/components/content/ProseImg.vue && echo "ProseImg OK" test -f app/components/content/Alert.vue && echo "Alert OK" grep "NuxtImg" app/components/content/ProseImg.vue grep "ContentSlot" app/components/content/Alert.vue grep "iconMap" app/components/content/Alert.vue grep "i-heroicons-information-circle" app/components/content/Alert.vue ``` - `app/components/content/ProseImg.vue` existe et contient `` - `Alert.vue` définit les 4 types : `'info' | 'warning' | 'tip' | 'danger'` - `Alert.vue` contient `iconMap` avec les 4 icônes Heroicons - `Alert.vue` contient `colorMap` avec les 4 couleurs Nuxt UI - `Alert.vue` utilise `UAlert` avec `variant="soft"` - `ProseImg.vue` utilise `withDefaults` avec `alt: ''` comme valeur par défaut - Aucun import explicite de NuxtImg, UAlert ou ContentSlot (auto-importés) ProseImg.vue et Alert.vue créés et conformes aux patterns du projet. Task 2: Créer les articles de test markdown FR et EN content/fr/blog/test-kotlin-syntax.md, content/en/blog/test-kotlin-syntax.md, app/pages/test.vue (a supprimer apres checkpoint visuel) - .planning/phases/05-nuxt-content-setup-renderer/05-UI-SPEC.md (Copywriting Contract — copie exacte des textes) - .planning/phases/05-nuxt-content-setup-renderer/05-RESEARCH.md (Code Examples — structure de l'article de test) - content.config.ts (vérifier que le schema Zod attend ces champs frontmatter) Créer les dossiers `content/fr/blog/` et `content/en/blog/` s'ils n'existent pas. **1. Créer `content/fr/blog/test-kotlin-syntax.md` :** ````markdown --- title: "Test Kotlin Syntax Highlighting" description: "Article de test pour valider le renderer @nuxt/content" date: "2026-04-21" tags: ["kotlin", "hytale", "test"] --- ## Bloc de code Kotlin ```kotlin fun main() { println("Hello, Hytale!") } fun createPlugin(name: String): Plugin { return Plugin(name = name, version = "1.0.0") } ``` ## Image optimisée ![Image de test pour NuxtImg dans les articles](/images/og-image.png) ## Tableau | Fonctionnalité | Statut | Notes | |----------------|--------|-------| | Syntax highlighting | ✅ Actif | Kotlin, Java, TypeScript, Shell | | Images optimisées | ✅ Actif | Via NuxtImg (lazy + srcset) | | Tableaux | ✅ Actif | Rendu prose | | Callouts | ✅ Actif | MDC ::alert{type} | ## Callouts ::alert{type="info"} Ceci est un callout d'information. :: ::alert{type="warning"} Ceci est un avertissement. :: ::alert{type="tip"} Conseil pratique de développement Kotlin. :: ::alert{type="danger"} Erreur critique — à ne pas ignorer. :: ```` **2. Créer `content/en/blog/test-kotlin-syntax.md` :** ````markdown --- title: "Test Kotlin Syntax Highlighting" description: "Test article to validate the @nuxt/content renderer" date: "2026-04-21" tags: ["kotlin", "hytale", "test"] --- ## Kotlin Code Block ```kotlin fun main() { println("Hello, Hytale!") } fun createPlugin(name: String): Plugin { return Plugin(name = name, version = "1.0.0") } ``` ## Optimized Image ![Test image for NuxtImg in articles](/images/og-image.png) ## Table | Feature | Status | Notes | |---------|--------|-------| | Syntax highlighting | ✅ Active | Kotlin, Java, TypeScript, Shell | | Optimized images | ✅ Active | Via NuxtImg (lazy + srcset) | | Tables | ✅ Active | Prose rendering | | Callouts | ✅ Active | MDC ::alert{type} | ## Callouts ::alert{type="info"} This is an information callout. :: ::alert{type="warning"} This is a warning. :: ::alert{type="tip"} Practical Kotlin development tip. :: ::alert{type="danger"} Critical error — do not ignore. :: ```` Note sur l'image : utiliser `/images/og-image.png` qui existe déjà dans `public/images/` — cela valide le pipeline ProseImg sans nécessiter une image supplémentaire. ```bash test -f content/fr/blog/test-kotlin-syntax.md && echo "FR OK" test -f content/en/blog/test-kotlin-syntax.md && echo "EN OK" grep '```kotlin' content/fr/blog/test-kotlin-syntax.md grep '::alert{type="info"}' content/fr/blog/test-kotlin-syntax.md grep '::alert{type="warning"}' content/fr/blog/test-kotlin-syntax.md grep '::alert{type="tip"}' content/fr/blog/test-kotlin-syntax.md grep '::alert{type="danger"}' content/fr/blog/test-kotlin-syntax.md grep "| Colonne\|Fonctionnalité\|Feature" content/fr/blog/test-kotlin-syntax.md content/en/blog/test-kotlin-syntax.md ``` - `content/fr/blog/test-kotlin-syntax.md` existe avec frontmatter complet (title, description, date, tags) - `content/en/blog/test-kotlin-syntax.md` existe avec frontmatter EN - Les deux fichiers contiennent un bloc ` ```kotlin ` avec au moins 2 lignes de code - Les deux fichiers contiennent une image markdown `![...](...)` - Les deux fichiers contiennent un tableau markdown avec header `|...|...|` - Les deux fichiers contiennent les 4 callouts : `::alert{type="info"}`, `::alert{type="warning"}`, `::alert{type="tip"}`, `::alert{type="danger"}` - Le slug est identique dans les deux langues : `test-kotlin-syntax` Articles de test créés en FR et EN. L'article couvre les 4 success criteria de la phase. ## Trust Boundaries | Boundary | Description | |----------|-------------| | Markdown → ContentRenderer | HTML généré par @nuxt/content — pas d'input utilisateur dans cette phase | | MDC composants → DOM | Composants Vue rendus côté serveur — auto-échappement Vue actif | ## STRIDE Threat Register | Threat ID | Category | Component | Disposition | Mitigation Plan | |-----------|----------|-----------|-------------|-----------------| | T-05-05 | Tampering | `app/components/content/Alert.vue` prop `type` | accept | Valeur `type` vient du frontmatter markdown (auteur contrôlé) — pas d'input utilisateur; TypeScript union `'info' \| 'warning' \| 'tip' \| 'danger'` limite les valeurs | | T-05-06 | Information Disclosure | `ProseImg.vue` prop `src` | accept | `src` vient du markdown statique — pas d'SSRF possible (NuxtImg résout les chemins au build) | | T-05-07 | Spoofing | `ContentSlot` dans Alert.vue | accept | ContentSlot est un composant officiel @nuxt/content — pas de XSS, le contenu est du texte markdown échappé | Après exécution du plan 02 (checkpoint visuel requis) : 1. Démarrer le serveur de dev : `pnpm dev` 2. Créer une page de test temporaire (ou utiliser la console) pour rendre l'article : - Si une page `/test` existe, y ajouter `` - Sinon, créer `app/pages/test.vue` temporairement avec : ```vue ``` 3. Naviguer vers `http://localhost:3000/test` 4. Vérifier visuellement les 4 critères La page de test temporaire peut être supprimée après validation — elle est hors scope de cette phase. **Vérifications grep :** ```bash test -f app/components/content/ProseImg.vue test -f app/components/content/Alert.vue grep "ContentSlot" app/components/content/Alert.vue test -f content/fr/blog/test-kotlin-syntax.md test -f content/en/blog/test-kotlin-syntax.md ``` - `ProseImg.vue` et `Alert.vue` existent dans `app/components/content/` - Les articles de test FR et EN existent avec les 4 éléments de validation - Checkpoint visuel : bloc Kotlin coloré visible (spans avec couleurs Shiki) - Checkpoint visuel : image rendue via `` (NuxtImg actif) - Checkpoint visuel : tableau affiché avec bordures prose - Checkpoint visuel : callout info affiché comme UAlert bleu avec icône - `pnpm typecheck` passe (0 erreur TypeScript) - ProseImg.vue : override transparent qui route toutes les images markdown vers NuxtImg - Alert.vue : composant MDC pour ::alert{type} avec 4 types (info/warning/tip/danger) via UAlert Nuxt UI - Article de test FR/EN contenant les 4 éléments de validation 1. S'assurer que `pnpm dev` tourne 2. Créer `app/pages/test.vue` temporairement (voir section verification ci-dessus) 3. Visiter http://localhost:3000/test 4. Vérifier visuellement : - [ ] Le bloc Kotlin est coloré (pas du texte brut gris) — en mode dark, fond sombre avec tokens colorés - [ ] L'image s'affiche (pas de 404) et l'élément DOM est `` (inspecter avec DevTools) - [ ] Le tableau markdown est rendu avec des lignes horizontales et en-têtes distingués - [ ] Le callout "info" apparaît comme une alerte bleue avec icône cercle-information - [ ] En passant en mode light (toggle du site), les couleurs Shiki changent (github-light) 5. Supprimer `app/pages/test.vue` après validation Taper "approved" si les 5 points sont validés, ou décrire le problème rencontré Après completion, créer `.planning/phases/05-nuxt-content-setup-renderer/05-02-SUMMARY.md`