From 9cc1dbec5dfe343d823ae428d150a874c5e62027 Mon Sep 17 00:00:00 2001 From: kayjaydee Date: Wed, 22 Apr 2026 18:38:13 +0200 Subject: [PATCH] =?UTF-8?q?docs(08):=20create=20phase=20plan=20=E2=80=94?= =?UTF-8?q?=20content=20&=20cocon=20s=C3=A9mantique=20(3=20plans,=202=20wa?= =?UTF-8?q?ves)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 08-01 (W1): HytaleRecentArticles.vue scaffold + injection hytale.vue + i18n - 08-02 (W2): article tutorial how-to-build-your-first-hytale-plugin FR+EN - 08-03 (W2): article positionnement hytale-plugin-development-2026 FR+EN --- .planning/ROADMAP.md | 9 +- .../08-content-cocon-semantique/08-01-PLAN.md | 290 ++++++++++++++++ .../08-content-cocon-semantique/08-02-PLAN.md | 312 ++++++++++++++++++ .../08-content-cocon-semantique/08-03-PLAN.md | 303 +++++++++++++++++ 4 files changed, 909 insertions(+), 5 deletions(-) create mode 100644 .planning/phases/08-content-cocon-semantique/08-01-PLAN.md create mode 100644 .planning/phases/08-content-cocon-semantique/08-02-PLAN.md create mode 100644 .planning/phases/08-content-cocon-semantique/08-03-PLAN.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 2bd01f7..eb8f13d 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -175,12 +175,11 @@ Plans: 2. Chaque article blog contient au moins un lien interne vers `/hytale` dans le corps du texte 3. La page `/hytale` affiche une section "Articles recents" avec liens vers les 2 articles seed 4. Les articles existent en FR et EN — `curl localhost:3000/en/blog` les liste en anglais -**Plans:** 4 plans +**Plans:** 3 plans Plans: -- [ ] 06-01-PLAN.md — Content schema Zod extension (draft/wordCount/minutes) + Nitro reading-time hook + draft:true sur test articles -- [ ] 06-02-PLAN.md — i18n keys blog.*/nav.blog/a11y.blog* + lien Blog dans AppHeader + BlogCard.vue unifié (default + compact) -- [ ] 06-03-PLAN.md — Page listing app/pages/blog/index.vue (hero + grid + empty state, SSR bilingue) -- [ ] 06-04-PLAN.md — BlogToc.vue + BlogPrevNext.vue + enrichissement app/pages/blog/[slug].vue (breadcrumb + header + TOC + surround) +- [ ] 08-01-PLAN.md — Scaffold HytaleRecentArticles.vue (queryCollection bilingue + filtre tag hytale + limit 2) + injection hytale.vue + i18n hytale.recentArticles.* +- [ ] 08-02-PLAN.md — Article seed tutorial how-to-build-your-first-hytale-plugin (FR+EN, draft:false, bloc Kotlin, liens inline /hytale) +- [ ] 08-03-PLAN.md — Article seed autorité hytale-plugin-development-2026 (FR+EN, draft:false, bloc Kotlin coroutines, liens inline /hytale) **UI hint**: yes --- diff --git a/.planning/phases/08-content-cocon-semantique/08-01-PLAN.md b/.planning/phases/08-content-cocon-semantique/08-01-PLAN.md new file mode 100644 index 0000000..72d959b --- /dev/null +++ b/.planning/phases/08-content-cocon-semantique/08-01-PLAN.md @@ -0,0 +1,290 @@ +--- +phase: 08-content-cocon-semantique +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - app/components/HytaleRecentArticles.vue + - app/pages/hytale.vue + - i18n/locales/fr.json + - i18n/locales/en.json +autonomous: true +requirements: [SEO-14] +tags: [nuxt-content, i18n, hytale, cocon-semantique] + +must_haves: + truths: + - "Visiter /hytale affiche une section 'Articles récents' (uniquement si ≥1 article tagué hytale avec draft:false existe en base content)" + - "La section réutilise BlogCard variant compact en grille 2 colonnes desktop / 1 colonne mobile" + - "Switch FR/EN met à jour la section (useAsyncData key inclut la locale + watch)" + - "Si 0 article hytale publié, la section est entièrement masquée (pas d'empty state)" + - "Un lien 'Voir tous les articles' pointe vers /blog (FR) ou /en/blog (EN) via localePath" + artifacts: + - path: "app/components/HytaleRecentArticles.vue" + provides: "Section composant auto-importé, queryCollection branches littérales + filtre tag hytale + limit 2" + contains: "queryCollection('blog_fr')" + - path: "app/pages/hytale.vue" + provides: "Insertion avant fermeture du root div" + contains: " +Scaffolder l'infrastructure technique du cocon sémantique côté /hytale : composant `HytaleRecentArticles.vue` (queryCollection bilingue, filtre tag=hytale, limit 2, masqué si vide), injection dans `app/pages/hytale.vue`, et clés i18n associées en FR/EN. + +Purpose: Préparer le conteneur qui affichera les 2 articles seed publiés en Wave 2. Le composant doit dégrader gracieusement (v-if=length) tant que les articles ne sont pas encore publiés, ce qui permet de shipper cette Wave 1 sans casser /hytale. + +Output: 1 nouveau composant + 1 page modifiée + 2 fichiers i18n mis à jour. Aucun article créé à ce stade. + + + +@$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/08-content-cocon-semantique/08-CONTEXT.md +@.planning/phases/08-content-cocon-semantique/08-PATTERNS.md +@app/pages/blog/index.vue +@app/components/BlogCard.vue +@app/pages/hytale.vue + + + +```typescript +interface BlogArticle { + path: string + title: string + description?: string + date: string + tags?: string[] + image?: string + minutes?: number +} +interface Props { + article: BlogArticle + variant?: 'default' | 'compact' + direction?: 'prev' | 'next' // default 'next' +} +``` + + +```typescript +const { t, locale } = useI18n() +const localePath = useLocalePath() +const isFr = computed(() => locale.value === 'fr') + +const { data: articles } = await useAsyncData( + `hytale-recent-${locale.value}`, + () => + isFr.value + ? queryCollection('blog_fr') + .where('draft', '=', false) + .order('date', 'DESC') + .all() + : queryCollection('blog_en') + .where('draft', '=', false) + .order('date', 'DESC') + .all(), + { watch: [locale] }, +) +``` + + + + + + + + + Task 1: Créer composant HytaleRecentArticles.vue (query + filtre tag + render) + app/components/HytaleRecentArticles.vue + + - app/pages/blog/index.vue (pattern queryCollection bilingue branches littérales, lignes 1-21) + - app/components/BlogCard.vue (variant compact, props interface lignes 2-21) + - .planning/phases/08-content-cocon-semantique/08-PATTERNS.md §"HytaleRecentArticles.vue" + - .planning/phases/08-content-cocon-semantique/08-CONTEXT.md §D-10, D-11, D-12, D-13 + + +Créer `app/components/HytaleRecentArticles.vue` (~70-90 lignes). + +**Script setup (TypeScript strict) :** +- `const { t, locale } = useI18n()` + `const localePath = useLocalePath()` +- `const isFr = computed(() => locale.value === 'fr')` +- `useAsyncData` avec key littérale interpolée `` `hytale-recent-${locale.value}` ``, ternaire `isFr.value` → `queryCollection('blog_fr')` / `queryCollection('blog_en')` (branches littérales obligatoires — Pitfall Phase 5 D-03, voir STATE.md gotcha). +- Chaîne de la query : `.where('draft', '=', false).order('date', 'DESC').all()` — **SANS `.limit(2)` au SQL ni `.where('tags', 'LIKE', ...)`** (l'opérateur LIKE sur champ JSON array SQLite n'est pas fiable selon D-11). À la place, **filtre JS post-query** : + ```ts + const articles = computed(() => { + const all = data.value ?? [] + return all.filter((a) => Array.isArray(a.tags) && a.tags.includes('hytale')).slice(0, 2) + }) + ``` + (renomme la destructuration `useAsyncData` en `{ data }` et expose `articles` computed — documente en commentaire `// Filtre JS car LIKE SQLite unreliable sur tags[] — D-11`). +- Option `{ watch: [locale] }` sur useAsyncData (re-fetch au switch langue). + +**Template :** +- `
` +- Wrapper intérieur `max-w-7xl mx-auto` pour cohérence /blog. +- Header section : petit `// recent-articles` style mono brand + `

{{ t('hytale.recentArticles.title') }}

` (réutiliser tailwind styles de /blog hero h1, taille h2 plus sobre : `text-3xl sm:text-4xl font-bold`) + `

{{ t('hytale.recentArticles.subtitle') }}

` si clé présente. +- Grille : `
` avec `` (pas de `direction` → default 'next' accepté, D-13 ne spécifie pas prev/next sémantique, acceptable). +- Footer : `{{ t('hytale.recentArticles.viewAll') }} `. + +**Règles strictes (D-09, D-10, D-11, D-12, D-13) :** +- BlogCard **auto-importé** — pas d'import explicite. +- Pas de fallback empty state (D-12 : masquer section complète). +- Pas d'usage de `queryCollection(variableName)` — littéraux uniquement. + + + pnpm typecheck + + + - Fichier `app/components/HytaleRecentArticles.vue` existe + - `grep -E "queryCollection\\('blog_(fr|en)'\\)" app/components/HytaleRecentArticles.vue` retourne les 2 branches + - `grep "v-if=\"articles.length\"" app/components/HytaleRecentArticles.vue` passe + - `grep "variant=\"compact\"" app/components/HytaleRecentArticles.vue` passe + - `grep "tags.*includes.*'hytale'" app/components/HytaleRecentArticles.vue` passe (filtre JS) + - `pnpm typecheck` exit 0 + + + + + Task 2: Injecter HytaleRecentArticles dans app/pages/hytale.vue + ajouter clés i18n FR/EN + app/pages/hytale.vue, i18n/locales/fr.json, i18n/locales/en.json + + - app/pages/hytale.vue (39 lignes, template section actuelle) + - .planning/phases/08-content-cocon-semantique/08-PATTERNS.md §"app/pages/hytale.vue" + §"i18n/locales" + - i18n/locales/fr.json (bloc hytale.* ~ligne 471, blog.* ~ligne 557 pour le style accentué 2026) + - i18n/locales/en.json (bloc hytale.* miroir) + - .planning/phases/08-content-cocon-semantique/08-CONTEXT.md §D-14 + + +**Étape 1 — `app/pages/hytale.vue` :** + +Template actuel (lignes 30-39) : +```vue + +``` + +Modification exacte : insérer `` **après** le `
` qui ferme le wrapper testimonials, **avant** le `` final du root. Résultat attendu : +```vue + +``` +Aucun changement dans le `