Commit Graph

192 Commits

Author SHA1 Message Date
kayjaydee 47c2839ae8 feat(07-03): enrich blog listing with D-16 useSeoMeta + CollectionPage/Breadcrumb JSON-LD
- Add SITE_URL + OG_FALLBACK constants (fallback hardcoded, resolveOgImage helper owned by 07-02)
- Extend useSeoMeta: ogImage (absolute /og-blog-default.jpg), ogUrl, ogLocale, ogLocaleAlternate, twitterCard, twitterImage
- Add useSchemaOrg([defineWebPage CollectionPage, defineBreadcrumb(Accueil -> Blog)])
- inLanguage resolved at setup (type constraint: literal union, not ComputedRef)
- Requirements: SEO-10, SEO-13, SEO-15
2026-04-22 11:17:10 +02:00
kayjaydee fae410243b feat(07-02): add resolveOgImage helper + og-blog-default.jpg fallback asset
- app/utils/resolve-og-image.ts: absolutises frontmatter image or falls back to /og-blog-default.jpg
- public/og-blog-default.jpg: placeholder (copied from og-image.png) — branded 1200x630 design follow-up pending
2026-04-22 11:16:37 +02:00
kayjaydee 9b1717cbd8 docs(07-01): capture plan summary
Foundation SEO Blog shipped — nuxt-schema-org installed, blog schema extended
with updated field, global Person/WebSite schema.org emitted SSR, sitemap.sources
wired to future Nitro endpoint (07-04).
2026-04-22 11:14:46 +02:00
kayjaydee 654842ba44 feat(07-01): wire global schema.org Person + WebSite and sitemap sources
- nuxt.config.ts: register 'nuxt-schema-org' module + sitemap.sources=['/api/__sitemap__/urls']
- app/utils/seo-person.ts: KILLIAN_PERSON_ID + killianPerson (derived from siteConfig, email excluded)
- app/app.vue: useSchemaOrg([definePerson(killianPerson), defineWebSite({name, inLanguage})]) appended (D-12)
- Verified SSR: /fr emits JSON-LD Person @id=#killian + WebSite (curl, pas d'hydratation)
2026-04-22 11:13:51 +02:00
kayjaydee 17420afefe chore(07-01): install nuxt-schema-org + add updated field to blog schema
- pnpm add -D nuxt-schema-org@^6.0.4 (D-01, D-04)
- content.config.ts blogSchema: updated: z.string().optional() (D-13, D-14)
- Caches content/.nuxt vidés (Pitfall 8)
2026-04-22 11:10:39 +02:00
kayjaydee 487e323a94 docs(roadmap): mark Phase 6 plans 03-04 complete (summaries present since 2026-04-22) .planning/ROADMAP.md 2026-04-22 11:09:26 +02:00
kayjaydee 7edc0b8123 docs(07): plan SEO blog — 4 plans (schema-org, useSeoMeta enrich, sitemap Nitro) .planning/phases/07-seo-blog/07-01-PLAN.md .planning/phases/07-seo-blog/07-02-PLAN.md .planning/phases/07-seo-blog/07-03-PLAN.md .planning/phases/07-seo-blog/07-04-PLAN.md .planning/ROADMAP.md 2026-04-22 10:40:12 +02:00
kayjaydee d7a13f0d4a docs(07): map analogs for new SEO files (schema-org + sitemap Nitro) .planning/phases/07-seo-blog/07-PATTERNS.md 2026-04-22 10:34:19 +02:00
kayjaydee 5bd5624121 docs(07): capture phase research — nuxt-schema-org + sitemap Nitro endpoint .planning/phases/07-seo-blog/07-RESEARCH.md 2026-04-22 10:32:18 +02:00
kayjaydee 680bbfbbe6 docs(07): capture phase context — SEO blog (JSON-LD via nuxt-schema-org, og:image hybride, sitemap Nitro endpoint, hreflang alternates) 2026-04-22 10:25:39 +02:00
kayjaydee 41ac2fdc08 docs(06-04): article chrome SUMMARY — BlogToc + BlogPrevNext + enriched [slug].vue 2026-04-22 10:10:41 +02:00
kayjaydee f18b0bff2c feat(06-04): enrich blog article page with breadcrumb, TOC, prev/next
- isFr converti en computed (fix Phase 5 non-reactive isFr)
- { watch: [locale] } sur les 2 useAsyncData (article + surround)
- queryCollectionItemSurroundings avec littéraux 'blog_fr'/'blog_en', fields explicites
- Article query WITHOUT draft filter (direct URL access, D-14)
- Surround query WITH .where('draft','=',false).order('date','DESC')
- Mapping prev=surround[1], next=surround[0] (Pitfall 4 DESC order)
- Header: UBreadcrumb + H1 + meta row (date Intl + reading time) + tags + cover NuxtImg eager
- Layout grid desktop [1fr_16rem] avec max-w-3xl colonne article
- ContentRenderer prose wrapper Phase 5 préservé
- BlogToc aside + BlogPrevNext en bas
- ogType: 'article' (préparation Phase 7)

Requirements: BLOG-03, BLOG-06
2026-04-22 10:09:23 +02:00
kayjaydee 0ff36784e9 feat(06-04): add BlogPrevNext component (grid 2 cols, BlogCard compact variant) 2026-04-22 10:06:52 +02:00
kayjaydee b72b564b69 feat(06-04): add BlogToc component (sticky desktop + drawer mobile + IntersectionObserver highlight) 2026-04-22 10:06:38 +02:00
kayjaydee d8130bba70 docs(06-03): blog listing page SUMMARY 2026-04-22 10:05:57 +02:00
kayjaydee eca09e0c32 feat(06-03): add blog listing page /blog (hero + grid + empty state)
- Query bilingue queryCollection('blog_fr') / queryCollection('blog_en') literal branches (Phase 5 gotcha)
- .where('draft', '=', false).order('date', 'DESC') with { watch: [locale] }
- Hero pattern /projects.vue: slogan // blog + H1 gradient + 3 stats (articles/tags/languages)
- Grid 1/2/3 responsive cols using BlogCard default variant
- Empty state with UIcon book-open + UButton CTA to /contact
- useSeoMeta minimal (full SEO + JSON-LD reserved for Phase 7)

Requirements: BLOG-02, BLOG-06
2026-04-22 10:05:16 +02:00
kayjaydee 5779daf34d docs(06-02): complete components UI + i18n locales plan
- Add 06-02-SUMMARY.md with 3 task commits (d299383, 0e42a05, d0ebf35)
- Update STATE.md : plan counter 11/15 (73%), next = 06-03 listing page
- Update ROADMAP.md Phase 6 progress : 2/4 plans complete
- Record gotcha 06-02 : slug derivation via path.split('/').filter(Boolean).pop()
2026-04-22 09:15:55 +02:00
kayjaydee d0ebf35119 feat(06-02): add BlogCard component with default + compact variants
- variant="default" (listing): cover image conditional (D-03 no fallback),
  aspect-[16/9], first tag UBadge, date i18n via Intl.DateTimeFormat,
  h2 title, line-clamp-2 description, reading time + extra tags pills,
  absolute inset-0 NuxtLink for SEO/a11y (D-02 tags non-clickable)
- variant="compact" (prev/next, D-09/D-10): no image, label row with
  UIcon arrow (left/right per direction), h3 title, date mono,
  text-right on next / text-left on prev
- Props: article, variant?='default'|'compact', direction?='prev'|'next'
- Slug derived from article.path last segment (locale-agnostic)
- readingMinutes: uses article.minutes (Nitro hook) with useReadingTime
  fallback on article.description
- Schema.org BlogPosting markup (headline/description/keywords/url/image/
  datePublished) — ready for Phase 7 JSON-LD Article
- a11y aria-label interpolated via t('a11y.blogPrev'|'blogNext', {title})
2026-04-22 09:13:09 +02:00
kayjaydee 0e42a0591e feat(06-02): add Blog nav link in AppHeader between Hytale and Projects
- Insert { key: 'blog', path: '/blog' } in navLinks computed array
- Position: between hytale and projects (D-15 ordering)
- Template v-for iterations unchanged — new link auto-propagates to
  desktop nav + mobile slideover
- Label resolved via t(`nav.${link.key}`) → uses nav.blog key
  added in Task 2.1
2026-04-22 09:11:27 +02:00
kayjaydee d29938335d feat(06-02): add blog i18n keys (nav.blog, a11y.blog*, blog.*)
- Add nav.blog in FR (Blog) + EN (Blog)
- Add a11y.blogTocToggle, blogPrev, blogNext with {title} interpolation
- Add blog.* block: title, subtitle, stats (articles/tags/languages),
  readingTime ({minutes} interpolation), prevArticle, nextArticle,
  backToBlog, toc.title, emptyState (title/description/cta),
  breadcrumb (home/blog)
- FR uses accents (Bientôt, précédent, Sommaire) per projects convention
2026-04-22 09:11:07 +02:00
kayjaydee 31dce7df0c docs(06-01): complete content schema + reading-time foundation plan
- Add 06-01-SUMMARY.md (5 tasks shipped, 0 deviations).
- Update STATE.md: Phase 6 Plan 06-01 shipped (1/4), gotchas recorded
  (hook schema strip, Nitro ~/ alias), next plan = 06-02.
- Update ROADMAP.md M1.1 progress: Phase 5 Complete, Phase 6 at 1/4.
2026-04-22 09:08:42 +02:00
kayjaydee f1d89ea532 chore(06-01): mark test-kotlin-syntax articles as draft (FR + EN)
- Add `draft: true` to frontmatter of both test-kotlin-syntax.md files
  so they are excluded from all `queryCollection(...).where('draft', '=', false)`
  listings (D-14).
- Articles remain accessible via direct URL (no draft filter on `.path(x).first()`),
  keeping them available for internal rendering tests.
- Listings will be empty until real Hytale seed articles land in Phase 8 —
  the empty state will render per D-16 ("Hytale articles coming soon" CTA).
- Body content untouched (only frontmatter +1 line each).
2026-04-22 09:05:47 +02:00
kayjaydee dd9ce6e8b4 feat(06-01): add useReadingTime composable fallback (200 wpm)
- Pure synchronous helper returning minutes (>= 1) from either a pre-computed
  word count (number) or raw text (string, tokenized on whitespace).
- Client-side safety net when `article.minutes` isn't yet populated
  (e.g., dev hot-reload before the Nitro hook re-parsed). Source of truth
  remains the Nitro `content:file:afterParse` hook (D-19).
- Same 200 wpm formula as server-side hook — ensures listing ↔ article parity.
- Auto-imported by Nuxt thanks to `use*` naming convention.
2026-04-22 09:04:53 +02:00
kayjaydee 5397390be2 feat(06-01): add Nitro hook content:file:afterParse for reading-time injection
- Register `content:file:afterParse` hook to inject `wordCount` + `minutes`
  on every parsed markdown content object (D-19: 200 wpm, floor 1 min).
- Import pure util `countWordsInMinimalBody` from app/utils/countWords.
- Guard against non-`.md` files (defensive — hook fires on all sources).
- Values persist in @nuxt/content SQLite DB and are queryable via
  queryCollection thanks to matching Zod fields (content.config.ts).
2026-04-22 09:02:23 +02:00
kayjaydee 63d0173b2d feat(06-01): add countWordsInMinimalBody util for reading-time computation
- Pure AST traversal of @nuxt/content v3 minimal body shape
- Skips code and pre tags (code snippets are not readable prose)
- Zero dependency, zero import, reused by Nitro hook
2026-04-22 08:57:05 +02:00
kayjaydee 6b4935ebba feat(06-01): extend blogSchema with draft/wordCount/minutes fields
- Add draft: z.boolean().optional().default(false) to allow .where('draft','=',false)
- Add wordCount + minutes as optional (injected by Nitro hook at parse time)
- Collections blog_fr/blog_en unchanged (schema is referenced by variable)
2026-04-22 08:56:46 +02:00
kayjaydee 4d1fb94531 docs(state): phase 6 planned (4 plans, 3 waves) .planning/STATE.md 2026-04-22 01:12:01 +02:00
kayjaydee edf7593f4f docs(06): create phase plan (4 plans, 3 waves)
Phase 6 Blog Pages decomposed into:
- 06-01 (Wave 1): content schema + reading-time Nitro hook + draft flags
- 06-02 (Wave 2): i18n keys + AppHeader link + BlogCard unified
- 06-03 (Wave 3): listing page /blog SSR bilingue
- 06-04 (Wave 3): [slug] enrichment + BlogToc + BlogPrevNext

Plans 06-03 and 06-04 have zero file overlap and run in parallel.

Covers BLOG-02, BLOG-03, BLOG-06. Honors all 21 D-XX user decisions
from 06-CONTEXT.md. Phase 5 gotchas (literal queryCollection, single
[slug].vue, no routeRules /blog/**) respected in every query branch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 01:09:25 +02:00
kayjaydee 7bbcd67b29 docs(06): research phase blog pages - API @nuxt/content v3, TOC IO, surround, hook reading time
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 00:51:49 +02:00
kayjaydee 6b76208c24 docs(state): record phase 6 UI-SPEC approved session .planning/STATE.md 2026-04-22 00:42:01 +02:00
kayjaydee 5ec19a5f13 docs(06): UI-SPEC approved (6/6 dimensions pass) .planning/phases/06-blog-pages/06-UI-SPEC.md 2026-04-22 00:41:46 +02:00
kayjaydee f96f25aee9 docs(06): UI design contract
Phase 6 Blog Pages — contrat visuel/interaction pour listing /blog et article /blog/[slug]. Tokens hérités Phase 5 (prose, brand-*, colorMode), inventaire composants (BlogCard, BlogToc, BlogPrevNext), i18n keys blog.* + nav.blog, pattern hero + cards inspiré /projects.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 00:40:21 +02:00
kayjaydee 456f6bfb6f docs(state): record phase 6 context session .planning/STATE.md 2026-04-22 00:34:58 +02:00
kayjaydee bd33e64e1a docs(06): capture phase 6 blog pages context .planning/phases/06-blog-pages/06-CONTEXT.md .planning/phases/06-blog-pages/06-DISCUSSION-LOG.md 2026-04-22 00:34:43 +02:00
kayjaydee 3e8e6f33d2 feat(ROADMAP): mark Phase 5 as completed and update project state; prepare for Phase 6 planning 2026-04-22 00:23:25 +02:00
kayjaydee 127db8b77a feat(blog): add dynamic blog post rendering with i18n support and error handling in [slug].vue 2026-04-22 00:20:52 +02:00
kayjaydee e66c7984a4 test(05): complete UAT - 5 passed, 2 issues .planning/phases/05-nuxt-content-setup-renderer/05-UAT.md 2026-04-21 23:25:18 +02:00
kayjaydee 839c584b0a refactor(config): update nuxt.config.ts to enhance module configuration, remove deprecated files, and improve contact form validation with zod schema 2026-04-21 23:15:04 +02:00
kayjaydee 20a5b5d85f feat(config): add route rules for blog redirection to French version with 301 status code 2026-04-21 19:36:43 +02:00
kayjaydee 7cd1531e06 fix(05): update test.vue path to /fr/blog prefix, add compatibilityDate 2026-04-21 16:55:57 +02:00
kayjaydee fd18ea99e1 content(en): update test article to match FR showcase — identical content, translated 2026-04-21 16:51:58 +02:00
kayjaydee 277b407361 feat(05): i18n strategy prefix — /fr/blog and /en/blog explicit routes, update collection prefixes 2026-04-21 16:49:32 +02:00
kayjaydee 06f47cbe11 fix(05): blog EN path uses /en/blog prefix to match blog_en collection 2026-04-21 16:47:12 +02:00
kayjaydee f2e29e6c2f feat(05): add blog/[...slug].vue — render @nuxt/content articles via queryCollection 2026-04-21 16:45:34 +02:00
kayjaydee 2ea6af0fff fix(05): install @iconify-json/lucide, pre-bundle zod in vite optimizeDeps 2026-04-21 16:41:23 +02:00
kayjaydee b63afc4152 docs(05-02): SUMMARY.md — MDC components, test articles, checkpoint approved 2026-04-21 16:36:38 +02:00
kayjaydee c5be72bdd9 fix(05-02): single dark theme for code blocks — github-dark always, remove dual-theme CSS 2026-04-21 16:35:06 +02:00
kayjaydee b0af1d3913 fix(05-02): ProseImg use span.block instead of figure — fix SSR hydration mismatch (block-in-p invalid HTML) 2026-04-21 15:58:41 +02:00
kayjaydee 006df6ad30 fix(05-02): Clear.vue MDC component, replace raw div clear:both (hydration mismatch) 2026-04-21 15:51:06 +02:00
kayjaydee 3e20e9ece9 fix(05-02): ProseImg inheritAttrs false — classes MDC custom overrident le layout auto 2026-04-21 15:37:51 +02:00