Files
portfolio/.planning/STATE.md
T

4.6 KiB
Raw Blame History

gsd_state_version, milestone, milestone_name, status, last_updated, last_activity, progress
gsd_state_version milestone milestone_name status last_updated last_activity progress
1.0 v1.0 milestone Plan 06-02 shipped — i18n FR+EN complet, nav link Blog en place, BlogCard.vue (variant default+compact) auto-importable, typecheck vert 2026-04-22T08:40:12.762Z 2026-04-22
total_phases completed_phases total_plans completed_plans percent
8 4 15 13 87

Project State

Project Reference

  • PROJECT.md: .planning/PROJECT.md
  • REQUIREMENTS.md: .planning/REQUIREMENTS.md
  • ROADMAP.md: .planning/ROADMAP.md

Current Focus

Phase: Phase 6 — Blog Pages Plan: 06-03 (next — Wave 3, listing page /blog) Status: Plan 06-02 shipped — i18n FR+EN complet, nav link Blog en place, BlogCard.vue (variant default+compact) auto-importable, typecheck vert Last activity: 2026-04-22 Resume file: .planning/phases/06-blog-pages/06-03-PLAN.md

Accumulated Context

  • M1 complet — déployé en production sur killiandalcin.fr (phases 14)
  • Stack : Nuxt 4 SSR + Nuxt UI v3 + Tailwind v4 + pnpm + @nuxt/content v3
  • Phase 5 shipped: @nuxt/content installé, collections bilingues blog_fr/blog_en, composants MDC (ProseImg, Alert, ProsePre, Columns, Details, Badge, Video, Clear), Shiki single github-dark, app/pages/blog/[slug].vue rend les articles FR/EN
  • Gotchas Phase 5 (à retenir) :
    • Catch-all [...slug].vue + @nuxtjs/i18n strategy prefix → page component résout à {} (Vue warn: missing template). Fix : single-segment [slug].vue.
    • queryCollection(variable) pas analysable par le Vite extractor de @nuxt/content → utiliser toujours des littéraux queryCollection('blog_fr') / queryCollection('blog_en').
    • i18n.baseUrl requis pour useLocaleHead (SEO tags). Ne pas retirer.
    • Redirection langue-détectée sans langue dans l'URL : detectBrowserLanguage.redirectOn: 'no prefix' + fallbackLocale. Éviter les routeRules /blog/** hardcodés (cassent le slug + bloquent la détection navigateur).
  • Objectif double : ranker sur "Hytale plugin developer" ET capter trafic longue traîne via contenu communauté
  • Articles bilingues : structure FR/EN dans content/ (ex: content/fr/blog/, content/en/blog/)
  • og:image par article : image frontmatter ou fallback branded — jamais l'og-image.png générique M1
  • Plan 06-01 shipped (2026-04-22) : blogSchema étendu (draft.default(false) + wordCount.optional + minutes.optional), Nitro hook content:file:afterParse injecte wordCount+minutes (200 wpm, floor 1 min) sur chaque .md via countWordsInMinimalBody, composable fallback useReadingTime(number|string) auto-importé, articles test-kotlin-syntax.md (FR+EN) marqués draft: true — exclus des listings where('draft', '=', false) mais accessibles par URL directe. Cache node_modules/.cache/content + .nuxt vidés.
  • Gotcha 06-01 : Le hook content:file:afterParse exige que wordCount/minutes soient déclarés dans le schema Zod (.optional() sans default) sinon ils sont strippés avant persistance DB — les propriétés injectées par hook ne sont queryables que si le schema les expose.
  • Gotcha 06-01 (convention) : Dans un plugin Nitro, importer depuis app/utils/ se fait via ~/utils/... (et non ~~/app/utils/...). Nuxt 4 mappe ~/app/ par défaut. Vérifié par typecheck vert sur server/plugins/reading-time.ts.
  • Plan 06-02 shipped (2026-04-22) : i18n nav.blog + 3 clés a11y.blog* (avec interpolation {title}) + bloc blog.* 14 clés (title, subtitle, stats., readingTime, prevArticle/nextArticle, backToBlog, toc.title, emptyState., breadcrumb.*) ajoutés dans fr.json + en.json. AppHeader.vue navLinks : { key: 'blog', path: '/blog' } inséré entre hytale et projects (ligne 11, ordre D-15 respecté). app/components/BlogCard.vue créé (192 lignes, auto-importé Nuxt) : variant default (listing) avec cover conditional + tag UBadge + date Intl.DateTimeFormat + h2 + description line-clamp-2 + reading-time (minutes hook || useReadingTime fallback) + extra tags pills + full-card NuxtLink SEO + Schema.org BlogPosting markup ; variant compact (prev/next, D-09/D-10) : no image + label row avec UIcon arrow directionnelle + h3 + date + NuxtLink aria-label interpolé a11y.blogPrev/a11y.blogNext. Typecheck exit 0.
  • Gotcha 06-02 (slug derivation) : Les articles @nuxt/content ont un path de forme /fr/blog/my-slug. Dans BlogCard.vue, on extrait le slug via article.path.split('/').filter(Boolean).pop() puis on reconstruit localePath('/blog/' + slug) — locale-agnostique. Évite de demander un champ slug explicite dans le frontmatter (cohérent convention @nuxt/content : path dérivé du nom de fichier).