From 841851ae851d57472786bfa82d29b4479b79f548 Mon Sep 17 00:00:00 2001 From: kayjaydee Date: Tue, 21 Apr 2026 12:48:02 +0200 Subject: [PATCH] =?UTF-8?q?docs(05):=20create=20phase=205=20plan=20?= =?UTF-8?q?=E2=80=94=20@nuxt/content=20setup=20&=20renderer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2 plans, 2 waves. Plan 01 installe @nuxt/content + typography et configure Shiki dual-theme + collections bilingues. Plan 02 crée ProseImg/Alert MDC et articles de test FR/EN avec checkpoint visuel. --- .planning/ROADMAP.md | 7 +- .../05-01-PLAN.md | 344 +++++++++++++ .../05-02-PLAN.md | 463 ++++++++++++++++++ 3 files changed, 812 insertions(+), 2 deletions(-) create mode 100644 .planning/phases/05-nuxt-content-setup-renderer/05-01-PLAN.md create mode 100644 .planning/phases/05-nuxt-content-setup-renderer/05-02-PLAN.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 8d5c196..905ec43 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -115,7 +115,10 @@ Plans: 2. Un article markdown de test avec un bloc Kotlin est rendu avec coloration syntaxique visible dans le navigateur 3. Une image referencee dans un article s'affiche via `` avec les optimisations (lazy, format webp) 4. Un tableau markdown et un callout/alert sont rendus avec le style correct -**Plans**: TBD +**Plans:** 2 plans +Plans: +- [ ] 05-01-PLAN.md — Installation @nuxt/content, configuration Shiki dual-theme, content.config.ts collections bilingues +- [ ] 05-02-PLAN.md — Composants MDC ProseImg + Alert, articles de test FR/EN, checkpoint visuel **UI hint**: yes ### Phase 6: Blog Pages @@ -161,7 +164,7 @@ Plans: | Phase | Plans Complete | Status | Completed | |-------|----------------|--------|-----------| -| 5. @nuxt/content Setup & Renderer | 0/? | Not started | - | +| 5. @nuxt/content Setup & Renderer | 0/2 | Not started | - | | 6. Blog Pages | 0/? | Not started | - | | 7. SEO Blog | 0/? | Not started | - | | 8. Content & Cocon Semantique | 0/? | Not started | - | diff --git a/.planning/phases/05-nuxt-content-setup-renderer/05-01-PLAN.md b/.planning/phases/05-nuxt-content-setup-renderer/05-01-PLAN.md new file mode 100644 index 0000000..acbc30a --- /dev/null +++ b/.planning/phases/05-nuxt-content-setup-renderer/05-01-PLAN.md @@ -0,0 +1,344 @@ +--- +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/\\*\\*" +--- + + +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 + + + +@$HOME/.claude/get-shit-done/workflows/execute-plan.md +@$HOME/.claude/get-shit-done/templates/summary.md + + + +@.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 + + + + +```typescript +modules: [ + '@nuxt/ui', + '@nuxtjs/i18n', + '@nuxt/eslint', + '@nuxtjs/sitemap', + 'nuxt-gtag', + '@nuxt/image' +], +``` + + +```typescript +colorMode: { + preference: 'dark', + fallback: 'dark', + storage: 'cookie', + storageKey: 'nuxt-color-mode', + classSuffix: '' // ← CRITIQUE: Shiki dual-theme nécessite classSuffix: '' pour html.dark +}, +``` + + +```css +@import "tailwindcss"; +@import "@nuxt/ui"; + +@theme { + --color-brand-500: #85cb85; + /* ... autres tokens brand */ +} +``` + + + + + + + Task 1: Installer @nuxt/content et @tailwindcss/typography + package.json + + - package.json (vérifier pnpm.onlyBuiltDependencies existant, ne pas écraser) + + + 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. + + + ```bash + grep '"@nuxt/content"' package.json + grep '"@tailwindcss/typography"' package.json + ``` + Les deux lignes doivent apparaître. + + + - `package.json` contient `"@nuxt/content"` dans `dependencies` + - `package.json` contient `"@tailwindcss/typography"` dans `devDependencies` + - `node_modules/@nuxt/content` existe + - `node_modules/@tailwindcss/typography` existe + + Les deux packages sont installés via pnpm sans erreur bloquante. + + + + Task 2: Configurer nuxt.config.ts et app/assets/css/main.css + nuxt.config.ts, app/assets/css/main.css + + - nuxt.config.ts (lire INTÉGRALEMENT avant de modifier — ne jamais réécrire, uniquement étendre) + - app/assets/css/main.css (lire INTÉGRALEMENT) + + + **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-*`. + + + ```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. + + + - `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` + + nuxt.config.ts étend le module @nuxt/content avec Shiki dual-theme. main.css charge @tailwindcss/typography via @plugin. + + + + Task 3: Créer content.config.ts avec collections bilingues + content.config.ts + + - 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) + + + 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. + + + ```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 + ``` + + + - `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) + + content.config.ts créé avec collections bilingues. `pnpm dev` démarre sans erreur — l'infrastructure @nuxt/content est opérationnelle. + + + + + +## 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 | + + + +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 +``` + + + +- `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) + + + +Après completion, créer `.planning/phases/05-nuxt-content-setup-renderer/05-01-SUMMARY.md` + diff --git a/.planning/phases/05-nuxt-content-setup-renderer/05-02-PLAN.md b/.planning/phases/05-nuxt-content-setup-renderer/05-02-PLAN.md new file mode 100644 index 0000000..d772d1c --- /dev/null +++ b/.planning/phases/05-nuxt-content-setup-renderer/05-02-PLAN.md @@ -0,0 +1,463 @@ +--- +phase: 05-nuxt-content-setup-renderer +plan: 02 +type: execute +wave: 2 +depends_on: + - "05-01-PLAN.md" +files_modified: + - app/components/content/ProseImg.vue + - app/components/content/Alert.vue + - content/fr/blog/test-kotlin-syntax.md + - content/en/blog/test-kotlin-syntax.md +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 + + - .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` +