Compare commits

..

197 Commits

Author SHA1 Message Date
kayjaydee 1282d0e350 feat(pricing): recalibrate to real Hytale market (149€/349€/790€)
Pricing refonte après rapport 3 (RESEARCH/Hytale/3 Pricing Calibration).

**Décision clé du rapport** : aucun serveur Hytale n'a payé €500+ pour
un plugin single en 2026 (public data). Top server Runeteria = 29 CCU
peak, €200-800/mois gross revenue ceiling. 70-80% du top 30 serveurs
= volunteer/owner-coded, zéro budget externe.

**Nouvelle grille adaptée à la réalité du marché Hytale** :
- Plugin Essentiel     149€   (1 feature, ≤8h, livraison 3-5j)
- Système Sur-Mesure   349€   (GUI in-game, ≤20h, 1-2 sem)  [featured]
- Module Flagship      790€   (quote-based, top-30 tier only)
- Retainer Mensuel     450€/mois (~12h/mo)
- Site Gaming          500€

**Ajout** : pricingNote avec mention 45€/h (spot fixes) et flagship CTA.

Ancienne grille (400€/1500€/5000€) filtrait 95% du marché — non viable.
Nouvelle grille capture ~85% de la demande Hytale observée tout en
laissant room upsell pour les 5-8 flagship servers réels.

RESEARCH/Hytale/3 ajouté (rapport complet avec top 30 audit, BBB
scatter analysis, WTP evidence par segment, 3 scénarios projetés).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 13:51:41 +02:00
kayjaydee 343df2dea2 feat(rebrand): pricing market-calibrated + remove /fiverr entirely
PRICING (/hytale) calibrated from market research (RESEARCH/Hytale/):
- Plugin Express: from 400€ (1-2j, petit système scopé)
- Projet Sur-Mesure: from 1 500€ (2-6 sem, featured popular)
- Système MMO Complet: from 5 000€ (4-8 sem, stack complet)
- Retainer Mensuel: from 800€/mois (4-8h/sem)
- Site Gaming/Serveur: from 1 000€ (Nuxt SSR + vote-rewards)

Source: TJM défensif senior Java FR 7ans = €450-650/jour (Freelance.com,
Arc.dev, Portage360). Segments cibles : small network (2-5 staff) +
mid-tier RPG/MMO (5-15 staff). Hobbyistes redirigés vers plugins
productisés BBB $15-25 (phase 10 future).

REMOVED /fiverr page entirely:
- Deleted app/pages/fiverr.vue (page removed from build + SEO)
- Removed from AppHeader navLinks + AppFooter quickLinks + socialLinks
- Removed nav.fiverr + a11y.fiverr + seo.fiverr + full fiverr.* block
  from fr.json + en.json (~75 clés chacun)
- Removed fiverr + FiverrConfig + FiverrService from site.ts + shared types
- Removed /fiverr internal links priority (redirected to /hytale)
- JSON-LD index.vue sameAs cleaned (LinkedIn + Gitea only, no Fiverr)
- reviewsLink testimonials redirected to /contact
Rationale: analyse de marché explicite "Fiverr destroys senior
positioning faster than it generates revenue"

RESEARCH/Hytale/ added (3 deep reports on pricing + prospection).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 13:32:23 +02:00
kayjaydee 4d5ed19b9c docs: audit plugins vs dominants gratuits (baseline top 10 CurseForge)
PLUGINS.md:
- FireballStaff → ChainLightning Sceptre (évite conflit Wan's Wonder
  Weapons 342K free). Chaînage BFS 5 targets max radius 8, damage
  dégressif 8/6/4/3/2, particles ELECTRIC_SPARK+END_ROD. Toujours 1j.
- Note "Audit concurrence" ajoutée listant les 6 dominants évités

IDEAS-BACKLOG.md:
- #12 TreasureHunt Map → EchoLocation Sonar (conflit BetterMap 502K).
  Crystal sonar burst qui reveal + aggro mobs radius 15. Unique.
- #30 StatBoost Food → Storm Seal Talisman (conflit Wan's + RPG
  Leveling). Weather interaction rare : charge pendant orages, décharge
  AoE foudre au clic.
- Section Notes mise à jour avec audit complet des dominants

Principe conservé : wow + dev rapide 1-2j, mais différenciation
sémantique vs Wan's Wonder Weapons / BetterMap / Spellbook / RPG
Leveling. Catégories sous-servies (magie, quêtes, anti-triche) toujours
prioritaires selon analyse marché.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 23:19:51 +02:00
kayjaydee 1a31056633 docs: rebalance plugins + backlog after Hytale market analysis (avril 2026)
PLUGINS.md (Phase 10 actifs):
- GravityFlip conservé (teaser gif-ready, 1j)
- FireballStaff (magie, catégorie 1-seul-payant, 1j)
- ShadowClone Decoy (combat unique, gameplay-saving, 1j)
- GrapplingHook (mouvement classique wow, 1.5j)
- EarthquakeSlam (combat AoE visuel max, 1j)
Total: ~6.5j (vs 10j avant). Tous "wow + dev rapide" mais rééquilibrés
vers catégories moins saturées quand possible.

IDEAS-BACKLOG.md (pipeline long terme):
- 30 concepts réécrits, priorité aux catégories vides:
  * Magie (8 ideas) - catégorie 1-seul-payant
  * Quêtes & NPCs (4) - quasi vide
  * Anti-triche light (3) - seulement 2 gratuits
  * Discord intégrations (3) - seulement 6 payants
  * Mini-bibliothèques (3) - catégorie 6 payants
  * Combat counter-play (5) - différenciant dans 120 RPG
  * MMO mechanics (4) - serveurs flagship demande
- Bundles pipeline suggérés (Spell Pack, MMO Essentials, Admin Suite, Combat)
- Frameworks long terme (myth_lib, Quest Framework, MythGuard, MythArena)
  renvoyés en Phase 12+ (planning séparé)

Source: rapport analyse marché Hytale interne (BuiltByBit 235 créateurs,
295 plugins payants, 1221 serveurs, catégories sous-servies identifiées).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 23:16:01 +02:00
kayjaydee fc524486d5 docs(11): mark Phase 11 shipped — rebranding + cocon complete
- REBRAND-01/02/03 commited in f72170b (JSON-LD + 14 i18n keys FR/EN)
- COCON-01 already shipped with M1.1 carry-over (HytaleRecentArticles
  live sur /hytale.vue:38, bilingue FR/EN avec filter tag hytale)

M1.2 progress: 4/6 plans (67%). Seule Phase 10 (5 demo plugins Hytale)
reste — user code les plugins en side, Plan 10-03 (HytaleDemoGrid) à
attaquer quand ≥1 plugin shippé sur GitHub.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 22:57:15 +02:00
kayjaydee f72170b4f3 feat(rebrand): align jobTitle + meta + i18n on Hytale Plugin Developer (REBRAND-01/02/03)
- app/pages/index.vue: JSON-LD utilise siteConfig.jobTitle (Hytale Plugin Developer)
  + siteConfig.contact.email au lieu de hardcode "Developpeur Full Stack"
- nuxt.config.ts: site.name "Developpeur Full Stack" → "Hytale Plugin Developer"
- app/data/site.ts: description "Professional Full Stack Developer..." →
  "Hytale Plugin Developer & Web Developer..."
- i18n/fr.json + en.json: refonte positionnement sur 14 clés
  - a11y.logoLabel, seo.home/about/contact, about.title/intro/approach/cta,
    home.cta2, contact.title/subtitle/faq.projectTypes, projects.subtitle
  - Le titre principal "Hytale Plugin Developer & [Freelance] Web Dev"
  - Les 2 occurrences "full stack" restantes sont contextuelles (skills)

Laisse les projets web/bot existants (virtual-tour, flowboard, xinko...) visibles,
mentionne Hytale plugins car Phase 10 (5 démos) est planifiée.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 22:55:37 +02:00
kayjaydee 30b9efc5d2 docs(10): plan Phase 10 — 5 demo plugins Hytale
- 10-CONTEXT: specs des 5 plugins (GravityFlip, MagneticHand, TimeRewind,
  BlackHoleGrenade, Paintball) avec core mechanics, wow factor, complexité
- 10-01-PLAN: brainstorm done (concepts locked)
- 10-02-PLAN: 5 waves de code (1 plugin par wave, complexité ascendante)
  Java + Gradle Kotlin DSL + JDK 17 + MIT license, repos kayjaydee/hytale-*
- 10-03-PLAN: composant HytaleDemoGrid.vue + intégration /hytale +
  i18n hytale.demos.* + data source app/data/hytaleDemos.ts
- IDEAS-BACKLOG.md (root): 30 concepts supplémentaires pour v1.3+
  classés par mouvement/combat/monde/social/économie

Effort estimé Phase 10: ~10 jours code + 1-2 jours frontend intégration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 22:49:59 +02:00
kayjaydee 042a05c3a8 docs(09): mark Phase 9 shipped — M1.1 live sur killiandalcin.fr
DEPLOY-02 + DEPLOY-03 validés. Build hang résolu via hook close dans
nuxt.config.ts (nuxt/nuxt#33987). Next: Phase 10 (démos plugins).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 22:40:49 +02:00
kayjaydee 93f781f789 fix(nuxt.config): add hook to prevent Docker build hangs due to esbuild zombie process 2026-04-22 22:36:50 +02:00
kayjaydee 23cd320871 docs(m1.2): bootstrap milestone — ship to prod + credibility gap
- PROJECT.md: v1.2 active, why-now, scope resserré (deploy + demos + rebrand)
- REQUIREMENTS.md: v1.2 section (DEPLOY-02/03, DEMO-01/02/03, REBRAND-01/02/03, COCON-01)
- ROADMAP.md: phases 9 (Deploy), 10 (Demo Plugins), 11 (Rebranding + Cocon) — 6 plans
- STATE.md: reset progress 0/6, focus Phase 9, context gotchas M1.1 conservé
- MILESTONES.md: M1.1 partielle (Phase 8 carried), M1.2 active

Prochaine étape: /gsd-plan-phase 9

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 22:24:00 +02:00
kayjaydee c32e44d0e8 chore: archive v1.1 milestone — SEO Hytale Autorité & Contenu shipped
M1.1 complete (phases 5-8, 13 plans):
- @nuxt/content v3 + Shiki syntax highlighting
- Blog listing + article pages SSR bilingue (TOC + prev/next)
- JSON-LD Article/Breadcrumb/CollectionPage + sitemap hreflang x-default
- 2 articles seed Hytale FR+EN (API Java réelle)
- Cocon sémantique /blog ↔ /hytale
2026-04-22 22:08:00 +02:00
kayjaydee 7768f6fd9d docs(08): capture API correction — Kotlin→Java rewrite based on hytalemodding.dev 2026-04-22 22:03:44 +02:00
kayjaydee bc1c4517f2 refactor(08-03): rewrite EN article with real Hytale Java API 2026-04-22 22:02:59 +02:00
kayjaydee a61596a6fb refactor(08-03): rewrite FR article with real Hytale Java API 2026-04-22 22:01:56 +02:00
kayjaydee be613f8eeb refactor(08-02): rewrite EN article with real Hytale Java API 2026-04-22 22:00:42 +02:00
kayjaydee 301ab48216 refactor(08-02): rewrite FR article with real Hytale Java API 2026-04-22 22:00:37 +02:00
kayjaydee c5bcafce2f docs(08): mark Phase 8 complete — M1.1 milestone shipped .planning/ROADMAP.md .planning/phases/08-content-cocon-semantique/08-VERIFICATION.md 2026-04-22 21:55:24 +02:00
kayjaydee 43a129fbfc docs(08-03): article seed 2 positionnement Hytale 2026 SUMMARY
- 2 articles markdown FR+EN (même slug) 1148/1009 mots, draft: false
- Tags ['hytale', 'industry', 'analysis'], date 2026-04-21 (ordering test)
- Coroutines Kotlin lifecycle-aware (SupervisorJob + cancel onDisable)
- Cocon sémantique bidirectionnel fermé avec l'article 08-02

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 21:53:25 +02:00
kayjaydee ba73a4d246 docs(08-02): capture summary — first Hytale plugin tutorial (FR+EN seed article) 2026-04-22 21:52:53 +02:00
kayjaydee 70407035e8 feat(08-03): add EN blog article 'Hytale plugin development in 2026'
- Positioning/authority article 1009 words, draft: false, same slug as FR
- Tags ['hytale', 'industry', 'analysis'], date 2026-04-21
- 6 sections mirroring FR structure (idiomatic EN, not literal translation)
- Same Kotlin coroutines code block (code not translated)
- 2 inline links to /en/hytale (intro + outlook section)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 21:52:21 +02:00
kayjaydee 9dde719469 feat(08-03): add FR blog article 'Développement de plugins Hytale en 2026'
- Article positionnement/autorité 1148 mots, draft: false
- Tags ['hytale', 'industry', 'analysis'], date 2026-04-21
- 6 sections (intro, stack, patterns, écosystème, outlook, conclusion)
- 1 bloc Kotlin coroutines moderne (SupervisorJob + Dispatchers.IO + cancel lifecycle)
- 2 liens inline vers /hytale (intro + section outlook)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 21:52:16 +02:00
kayjaydee 2d6b23acb5 feat(08-02): add EN article how-to-build-your-first-hytale-plugin
- Equivalent EN version (970 mots), same slug
- 3 Kotlin code blocks (build.gradle.kts, event listener, command)
- 2 inline links to /en/hytale (intro + build section)
- Frontmatter Zod-valid : draft false, tags [hytale, tutorial, kotlin], date 2026-04-22
2026-04-22 21:52:00 +02:00
kayjaydee 9f77ea9d87 feat(08-02): add FR article how-to-build-your-first-hytale-plugin
- Tutorial debutant plugin Hytale en Kotlin (1049 mots)
- 3 blocs code Kotlin realistes (build.gradle.kts, event listener, command)
- 2 liens inline vers /hytale (intro + build section)
- Frontmatter Zod-valide : draft false, tags [hytale, tutorial, kotlin], date 2026-04-22
2026-04-22 21:50:57 +02:00
kayjaydee 431cf7d572 docs(08-01): scaffold HytaleRecentArticles SUMMARY — component bilingue + i18n FR/EN + injection /hytale 2026-04-22 21:48:48 +02:00
kayjaydee bf2ec8671c feat(08-01): inject HytaleRecentArticles in /hytale + add i18n keys FR/EN
- app/pages/hytale.vue: <HytaleRecentArticles /> inserted after TestimonialsSection wrapper, before closing root div
- i18n/locales/fr.json: hytale.recentArticles {title, subtitle, viewAll} accentue style (aligned with blog.* 2026)
- i18n/locales/en.json: mirror keys
- No script changes (auto-import Nuxt)
2026-04-22 21:47:57 +02:00
kayjaydee ddfc685421 feat(08-01): add HytaleRecentArticles component (queryCollection bilingual + JS tag filter)
- Bilingual literal branches queryCollection('blog_fr'|'blog_en') (Phase 5 Pitfall D-03)
- JS post-filter tags.includes('hytale') + slice(0,2) (D-11 — SQLite LIKE unreliable on JSON array)
- v-if=articles.length hides section when no hytale-tagged articles (D-12)
- BlogCard variant=compact in grid 2 col desktop / 1 col mobile
- NuxtLink localePath('/blog') viewAll CTA
- useAsyncData key hytale-recent-${locale.value} + watch:[locale]
2026-04-22 21:47:23 +02:00
kayjaydee abb7964214 docs(08): create phase plan — content & cocon sémantique (3 plans, 2 waves)
- 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

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 18:38:13 +02:00
kayjaydee 0d92729654 docs(08): map analogs for seed articles + HytaleRecentArticles component .planning/phases/08-content-cocon-semantique/08-PATTERNS.md 2026-04-22 18:32:43 +02:00
kayjaydee 985dcdbd80 docs(08): capture phase context — 2 articles seed Hytale + HytaleRecentArticles section (cocon sémantique blog↔hytale) .planning/phases/08-content-cocon-semantique/08-CONTEXT.md 2026-04-22 12:19:00 +02:00
kayjaydee 0b1152c8a1 docs(07): mark Phase 7 complete in ROADMAP (4/4 plans) .planning/ROADMAP.md .planning/phases/07-seo-blog/07-VERIFICATION.md 2026-04-22 11:25:57 +02:00
kayjaydee 4bc0886a42 docs(07-04): complete sitemap dynamic feed plan
- Add 07-04-SUMMARY.md (endpoint Nitro bilingue + hreflang x-default + draft filter)
- Update STATE.md (14/15 plans, 93%)
- Check SEO-12 in REQUIREMENTS.md and ROADMAP.md
- Document gotcha queryCollection import from '@nuxt/content/server' (vue-tsc auto-import not resolved in server/)
2026-04-22 11:22:57 +02:00
kayjaydee 97ea1a8df2 docs(07-02): complete blog article SEO plan summary + state update
Plan 07-02 shipped: useSeoMeta D-15 + useSchemaOrg Article/Breadcrumb on /blog/[slug],
resolveOgImage helper + og-blog-default.jpg fallback. Curl SSR validated,
typecheck green. Requirements satisfied: SEO-10, SEO-11, SEO-13, SEO-15.
2026-04-22 11:21:46 +02:00
kayjaydee 466bed0944 feat(07-04): add dynamic sitemap URL feed for bilingual blog articles
- Nitro route server/api/__sitemap__/urls.ts via defineSitemapEventHandler
- Queries blog_fr + blog_en with literal strings and event first-arg (Pitfalls 1 & 2)
- Filters draft=false (D-10, T-07-06 mitigation)
- lastmod = updated ?? date (D-09)
- Emits hreflang alternates fr/en/x-default for bilingual pairs, none for single-language (D-11)
- Feeds @nuxtjs/sitemap via sitemap.sources declared in 07-01
2026-04-22 11:20:09 +02:00
kayjaydee e17faae5d7 feat(07-02): enrich blog article page with full SEO meta + Article/Breadcrumb JSON-LD
- D-15: useSeoMeta extended with ogImage (absolute via resolveOgImage),
  ogUrl (canonical), ogLocale + ogLocaleAlternate (emitted only when bilingual
  pair exists), twitterCard + twitterImage, article:published_time,
  article:modified_time (fallback to date when updated absent — D-13),
  articleAuthor
- SEO-11/SEO-15: useSchemaOrg([defineArticle, defineBreadcrumb])
  — Article author/publisher reference global Person via @id=#killian
  (from app/utils/seo-person.ts KILLIAN_PERSON_ID), image mirrors ogImage,
  mainEntityOfPage = canonical; BreadcrumbList emits Accueil → Blog → title
- Pitfall 7: altExists query via queryCollection('blog_en'|'blog_fr') with
  literal collection names (Vite extractor constraint)
- inLanguageTag computed cast to satisfy overly narrow defineArticle typings
  without changing runtime emission
- Validated SSR: curl /fr/blog/test-kotlin-syntax returns og:image absolute,
  article:published_time, Article JSON-LD (author @id=#killian), BreadcrumbList 3 items
2026-04-22 11:19:58 +02:00
kayjaydee 15e1a37e59 docs(07-03): blog listing SEO enrichment SUMMARY — D-16 + CollectionPage/Breadcrumb JSON-LD 2026-04-22 11:17:55 +02:00
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
kayjaydee 221b1a076c fix(05-02): restore Shiki token colors — add .shiki to ProsePre pre, broaden CSS selector to pre span 2026-04-21 15:34:02 +02:00
kayjaydee f179d64253 feat(05-02): ProsePre override — dark bg fixe #0d1117, badge langage, Shiki tokens transparents 2026-04-21 15:31:40 +02:00
kayjaydee 60e05f7a56 feat(05-02): add Columns/Details/Video/Badge MDC components + full showcase article 2026-04-21 15:31:00 +02:00
kayjaydee b63869f042 feat(05-02): ProseImg flexible — align left/right/center/full + caption + width 2026-04-21 15:28:39 +02:00
kayjaydee 37b6ef9112 fix(05-02): widen test page to max-w-6xl 2026-04-21 15:26:05 +02:00
kayjaydee ee7509cff0 fix(05-02): widen test page to max-w-3xl 2026-04-21 15:25:41 +02:00
kayjaydee 9849c18da4 fix(05-02): rebuild Alert sans UAlert, ProseImg img natif, test.vue layout propre 2026-04-21 15:24:22 +02:00
kayjaydee e1c91e583f fix(05-02): alert alignment via #title slot, dark-only code theme, simplify ProseImg 2026-04-21 15:20:14 +02:00
kayjaydee b5c3250a4e fix(05-02): ContentSlot→slot, image path, Shiki dual-theme CSS 2026-04-21 15:16:04 +02:00
kayjaydee 0fa19a7701 feat(05-02): add test articles FR/EN and temporary test page
- content/fr/blog/test-kotlin-syntax.md: FR test article covering all 4 validation criteria
- content/en/blog/test-kotlin-syntax.md: EN version with same slug
- app/pages/test.vue: temporary page at /test for visual checkpoint verification
- Both articles contain: kotlin code block, NuxtImg image, markdown table, 4 callout types
2026-04-21 14:36:49 +02:00
kayjaydee c9a14a9086 feat(05-02): create MDC components ProseImg.vue and Alert.vue
- ProseImg.vue: transparent NuxtImg override for markdown images (BLOG-05)
- Alert.vue: MDC callout component with 4 types (info/warning/tip/danger) via UAlert
- ContentSlot required for MDC slot content rendering (Pitfall 4)
2026-04-21 14:36:22 +02:00
kayjaydee 557861aa95 docs(05-01): complete @nuxt/content setup plan — SUMMARY created 2026-04-21 14:35:24 +02:00
kayjaydee f49fab2532 chore(05-01): add .data to .gitignore (nuxt/content SQLite runtime artifact) 2026-04-21 14:34:58 +02:00
kayjaydee 83197899c8 feat(05-01): create content.config.ts with bilingual blog collections
- Define blog_fr collection: fr/blog/**/*.md → prefix /blog (FR default locale)
- Define blog_en collection: en/blog/**/*.md → prefix /en/blog (EN prefixed)
- Add Zod schema: title, description, date (required) + tags, image (optional)
2026-04-21 14:34:42 +02:00
kayjaydee 3381b2efb3 feat(05-01): configure @nuxt/content with Shiki dual-theme and typography plugin
- Add '@nuxt/content' to modules array in nuxt.config.ts
- Add content block: Shiki dual-theme github-light/github-dark
- Add Shiki langs: kotlin, java, typescript, shell, bash, json, vue, html, css
- Add experimental.sqliteConnector: 'native' (Node 22 native SQLite)
- Add @plugin "@tailwindcss/typography" in main.css
2026-04-21 14:33:54 +02:00
kayjaydee c64709da10 chore(05-01): install @nuxt/content@3.13.0 and @tailwindcss/typography@0.5.19
- Add @nuxt/content to dependencies
- Add @tailwindcss/typography to devDependencies
2026-04-21 14:33:29 +02:00
kayjaydee 1df6a21c5e docs(05): add detailed pattern map for @nuxt/content setup and renderer
- Introduced a new document outlining the configuration and component patterns for integrating @nuxt/content.
- Included mappings for `nuxt.config.ts`, `content.config.ts`, and new components `ProseImg.vue` and `Alert.vue`.
- Added example markdown content for testing syntax highlighting and layout.
- Documented critical patterns and anti-patterns to follow during implementation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 12:52:59 +02:00
kayjaydee cb477006f4 fix(05): resolve checker issues — open questions resolved, depends_on corrected, test.vue added
- 05-RESEARCH.md: rename section to 'Open Questions (RESOLVED)' with explicit answers
  (frontmatter schema: tags array, image relative path, author implicit from site.ts;
   i18n prefix: /blog for blog_fr, /en/blog for blog_en)
- 05-02-PLAN.md: fix depends_on from '05-01-PLAN.md' to '01'
- 05-02-PLAN.md: add app/pages/test.vue in Task 2 files (with note to delete after checkpoint)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 12:52:51 +02:00
kayjaydee 808835d5eb docs(05): create phase 5 plan — @nuxt/content setup & renderer
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.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 12:48:02 +02:00
kayjaydee afd81e7e84 docs(05): UI design contract for nuxt-content renderer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 12:30:44 +02:00
kayjaydee 2d8f0ca7c3 docs(05): research phase nuxt-content setup renderer 2026-04-21 11:31:32 +02:00
kayjaydee 2b06dfe463 docs(05): capture phase context 2026-04-21 11:22:39 +02:00
kayjaydee 2658b0c607 docs: create milestone v1.1 roadmap (4 phases, 13 requirements) 2026-04-21 11:11:53 +02:00
kayjaydee a4ee7fe007 docs: start milestone v1.1 — SEO Hytale, Autorité & Contenu 2026-04-21 11:09:29 +02:00
kayjaydee a0788f1edd docs: sync GSD tracking — milestone M1 complete, all 4 phases shipped to prod 2026-04-21 11:00:16 +02:00
kayjaydee 888650ce3d docs: sync GSD tracking — phases 1 & 2 complete (retroactive audit 2026-04-21) 2026-04-21 10:59:28 +02:00
kayjaydee 848387d69c feat(contact): nouvelle template email terminal-style 2026-04-17 09:28:54 +02:00
kayjaydee 438b238946 feat: nouvelle template email 2026-04-17 09:25:45 +02:00
kayjaydee c7e74bd699 chore: sync pnpm-lock.yaml 2026-04-17 09:08:05 +02:00
kayjaydee 39f2a81e8f feat(hytale): implement Hytale plugin development page and related components
- Added a new `/hytale` page with sections for hero, services, and pricing.
- Updated existing components to support Hytale-specific content and i18n.
- Modified site configuration and state to reflect the new focus on Hytale plugin development.
- Enhanced testimonials section to feature relevant client feedback.
- Adjusted navigation to include a link to the new Hytale page.
2026-04-11 04:19:27 +02:00
kayjaydee 215fba6342 docs(02): create phase 2 content plans 2026-04-11 03:58:21 +02:00
kayjaydee 710692f0ae docs(02): UI design contract 2026-04-11 03:48:14 +02:00
kayjaydee 8478c7b00a docs(02): capture phase context 2026-04-11 03:42:43 +02:00
kayjaydee b85f58115f docs(01): complete phase 1 cleanup & fixes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 19:20:32 +02:00
kayjaydee 8b69a12342 chore: update Dockerfile for pnpm, modify package.json dependencies, and implement rate limiting
- Switched from npm to pnpm for dependency management in Dockerfile, improving build efficiency.
- Updated Vue and Vue Router versions in package.json for better compatibility.
- Changed placeholder URLs in site.ts to actual Fiverr links and adjusted review count.
- Removed obsolete sitemap.xml file to streamline the project.
- Added a new rate limiting plugin to manage API request limits for the contact endpoint.
2026-04-10 19:19:36 +02:00
kayjaydee 9a66eec033 docs(01): plan phase 1 cleanup & fixes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 18:56:12 +02:00
kayjaydee 8ce1b62240 docs(01): create phase 1 cleanup & fixes plans 2026-04-10 18:55:18 +02:00
kayjaydee e8bb0d0465 docs: create requirements, roadmap, and state
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 18:51:11 +02:00
kayjaydee fdd7f39972 docs: complete project research
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 18:08:28 +02:00
kayjaydee e2d352bd0a docs: initialize project 2026-04-10 17:52:39 +02:00
kayjaydee 76abd8b6bc chore: add project config 2026-04-10 17:51:57 +02:00
kayjaydee ce7cd19fef docs: map existing codebase
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 17:50:10 +02:00
kayjaydee 7f776298a9 chore: remove obsolete planning files for Nuxt 4 migration
- Deleted several planning documents including config.json, PROJECT.md, REQUIREMENTS.md, ROADMAP.md, STATE.md, and various phase plans.
- These files were no longer relevant to the current project structure and development practices, streamlining the codebase.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 17:37:59 +02:00
kayjaydee 3f0af5ca5a chore: remove outdated planning documents from codebase
- Deleted several planning documents including ARCHITECTURE.md, CONCERNS.md, CONVENTIONS.md, INTEGRATIONS.md, STACK.md, STRUCTURE.md, and TESTING.md.
- These files were no longer relevant to the current project structure and development practices.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 20:02:41 +02:00
kayjaydee c8dac9ac88 fix: update portfolio branding to "Killian' DAL-CIN" across all documentation and components
- Corrected the name in various files including CLAUDE.md, README.md, and configuration files to reflect the updated branding.
- Ensured consistency in the use of the new name throughout the project, enhancing brand identity.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 19:54:46 +02:00
kayjaydee 9779e4e133 feat: redesign entire portfolio with bold modern dark theme
Complete visual overhaul of all pages and components with generous spacing,
bold typography, hover effects, gradient accents, and section differentiation.
Hero features animated terminal mockup and gradient text. Cards use hover
transforms with brand-colored shadows. CTAs use gradient backgrounds.
All i18n keys, data structures, SEO meta, and composable logic preserved.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 19:08:55 +02:00
kayjaydee 9739becbb7 fix: rewrite AppHeader — replace UDrawer with USlideover, clean design
UDrawer (vaul-vue bottom-sheet) rendered content in DOM even when closed,
causing mobile nav to show on desktop. Replaced with USlideover (proper
sidebar panel). Also: backdrop-blur header, UButton for actions, Lucide
icons, brand color active states.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 18:55:58 +02:00
kayjaydee 08b7e37acc fix: correct i18n key paths for projects, featured, testimonials
- useProjects: projects.${id}.* → projectData.${id}.* (matches locale structure)
- FeaturedProjectsSection: home.projects.* → home.featuredProjects.*
- TestimonialsSection: home.testimonials.* → testimonials.*

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 18:53:49 +02:00
kayjaydee a8f2874413 fix: use array syntax for components config with pathPrefix
Nuxt requires array syntax when configuring pathPrefix per directory.
Object syntax { pathPrefix: false } doesn't register component dirs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 18:48:19 +02:00
kayjaydee e88a33987a fix: add pathPrefix: false to components config for auto-import
Nuxt prefixes components in subdirectories (layout/AppHeader → LayoutAppHeader).
Setting pathPrefix: false allows using <AppHeader>, <HeroSection>, etc. directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 18:47:05 +02:00
kayjaydee 25e910d030 docs(03-04): complete Dockerfile SSR + legacy cleanup plan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 18:41:40 +02:00
kayjaydee 081ed0365b chore(03-04): remove legacy SPA files and verify GA4 config
- Delete entire src/ directory (160+ legacy Vue SPA files)
- Delete old/, nginx.conf, index.html, eslint.config.ts, env.d.ts
- GA4 nuxt-gtag already correctly configured (production-only, runtimeConfig)
- No formation.vue exists, /formation returns 404 naturally

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 18:40:53 +02:00
kayjaydee 39749c61c1 feat(03-04): Dockerfile SSR multi-stage + docker-compose Traefik port 3000
- Rewrite Dockerfile: node:22-alpine build + runtime, copy .output/, node server
- Add .dockerignore excluding node_modules, .nuxt, .output, src, .git, .planning
- Update docker-compose loadbalancer port from 80 to 3000
- Add SMTP and GA4 environment variables to docker-compose

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 18:40:23 +02:00
kayjaydee 54cf031cd7 docs(03-03): complete About/Contact/Fiverr/Error pages plan 2026-04-08 18:39:22 +02:00
kayjaydee 5a7a816638 docs(03-02): complete main pages plan
- SUMMARY.md for landing + projects + detail pages
- STATE.md updated to plan 2/3 phase 3
- ROADMAP.md progress updated
- Requirements PAGE-01, PAGE-02, PAGE-03 marked complete
2026-04-08 18:39:09 +02:00
kayjaydee 55f9c8eaf6 feat(03-03): create error.vue (404 page) with i18n keys
- error.vue in app/ with statusCode display, i18n message, clearError redirect
- Added error.notFound, error.generic, error.backHome keys to fr.json and en.json
2026-04-08 18:38:35 +02:00
kayjaydee 91ac322c57 feat(03-03): build Fiverr page with hero, service cards, FAQ accordion, and CTA
- Hero with stats (available services count, rating) and profile CTA
- Service cards grid with NuxtImg, price/status badges, order buttons
- FAQSection with UAccordion using homeFAQs data
- Final CTA section linking to Fiverr profile
2026-04-08 18:38:01 +02:00
kayjaydee af12fa5e4f feat(03-02): project detail page with dynamic route and gallery
- Dynamic route /project/[id] with findById composable
- 404 via createError if project not found
- Hero grid: image + info + CTA buttons (demo, source, custom)
- About section with features list (checkmarks)
- Technologies section with TechBadge
- Gallery thumbnails with zoom overlay, opens ProjectGallery modal
- Sidebar: project info card + related projects
- Responsive 2-col layout (main + sidebar)
2026-04-08 18:37:58 +02:00
kayjaydee ffa6ba8bfe feat(03-03): build About page with tech stack badges and Contact page with form
- About: hero bio, 5 tech categories with TechBadge (UCard grid), approach cards, CTA
- Contact: hero stats, ContactForm component, contact info from siteConfig, social links, FAQ cards
2026-04-08 18:37:34 +02:00
kayjaydee 8e9c6c7848 feat(03-02): projects page with search and category filters
- Text search filtering by title, description, technologies
- Category filter buttons (UButton solid/soft variants)
- ProjectCard grid responsive 1/2/3 columns
- Empty state with reset button
- Stats: total projects, featured, categories
2026-04-08 18:37:17 +02:00
kayjaydee a4b53caaa2 feat(03-02): landing page with 6 sections
- HeroSection, FeaturedProjectsSection, ServicesSection
- TestimonialsSection, FAQSection with homeFAQs, CTASection
- Preserved useSeoMeta and JSON-LD from Phase 2 stub
2026-04-08 18:36:49 +02:00
kayjaydee eff8ca4210 docs(03-01): complete shared components plan
- SUMMARY.md with 3 tasks, 17 files, 239s duration
- STATE.md advanced to phase 3 plan 1
- ROADMAP.md updated with plan progress
- COMP-01 to COMP-04 marked complete
2026-04-08 18:35:37 +02:00
kayjaydee 84e4202536 feat(03-01): create ContactForm with Zod validation and nodemailer SMTP server route
- ContactForm.vue: UForm + Zod schema (name/email/message) + useToast feedback
- server/api/contact.post.ts: nodemailer SMTP with server-side validation + HTML escaping
- SMTP credentials in private runtimeConfig (T-03-03)
- HTML escaping prevents XSS in email body (T-03-02)
2026-04-08 18:34:38 +02:00
kayjaydee 7f715e4b01 feat(03-01): create 9 shared components for landing sections and project display
- HeroSection: title + subtitle + 3 CTA UButtons
- FeaturedProjectsSection: 3 featured projects via useProjects()
- ServicesSection: 4 service cards with UCard + UIcon
- TestimonialsSection: UCard per testimonial with ratings and stats
- FAQSection: UAccordion with i18n-resolved items
- CTASection: final CTA with 2 UButtons
- ProjectCard: NuxtLink + NuxtImg + UBadge + schema.org microdata
- TechBadge: Technology lookup with NuxtImg + UBadge level
- ProjectGallery: UModal fullscreen + UCarousel + thumbnails + keyboard nav
2026-04-08 18:34:03 +02:00
kayjaydee 21450afb20 feat(03-01): install deps, migrate site config, add SMTP runtimeConfig, wrap UApp
- Install nodemailer, zod, @types/nodemailer
- Create app/data/site.ts with migrated siteConfig from src/config/site.ts
- Add SiteConfig, ContactInfo, SocialLink, FiverrService, FiverrConfig to shared/types
- Add smtpHost/smtpUser/smtpPass/smtpTo to private runtimeConfig
- Wrap app.vue with UApp for useToast() support
2026-04-08 18:32:24 +02:00
kayjaydee b10ff2bc0b docs(03): fix plan blockers — remove formation completely, cleanup legacy
- Remove PAGE-07 from requirements (formation deleted per D-19)
- No redirect, /formation returns 404 naturally
- Plan 04 now includes full legacy src/ cleanup
- Update success criteria: 7 routes, SMTP instead of EmailJS

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 18:29:39 +02:00
kayjaydee 3e38ea02b1 docs(03): create phase 3 plans — pages, components, Docker SSR
4 plans across 3 waves: shared components + deps (wave 1),
pages landing/projects/detail + about/contact/fiverr/404 (wave 2),
Dockerfile SSR + GA4 + docker-compose (wave 3).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 18:25:28 +02:00
kayjaydee 039cabd8f4 docs(03): research phase Pages & Ship domain
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 18:19:06 +02:00
kayjaydee 36768e2441 docs(03): update context — SMTP direct OVH, remove formation from scope
- Contact form uses server-side nodemailer via Nuxt API route (not EmailJS)
- Formation page removed from Phase 3 scope (was SaaS pricing, not portfolio)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 17:26:02 +02:00
kayjaydee ab9831cce9 feat: remove formation/pricing page and all related content
Formation was a SaaS pricing page unrelated to the portfolio.
Removed: page, nav link, locale keys (nav.formation, seo.formation,
pricing.*) in both FR and EN, legacy source files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 17:24:54 +02:00
kayjaydee 0f8627b397 feat(docker): add docker-compose configuration for portfolio service
- Introduced a new docker-compose.yml file to define the portfolio service.
- Configured Traefik routing with TLS settings and redirect middleware for non-www to www.
- Set up environment variables and network configuration for the service.
2026-04-08 16:48:47 +02:00
kayjaydee a93a362d21 docs(03): create phase 3 context from discussion
Decisions: 6-section landing, UModal+UCarousel gallery with thumbnails,
3-field contact form with EmailJS+Zod, SSR Docker with runtimeConfig.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:48:21 +02:00
kayjaydee eb3e979d59 docs(state): mark phase 2 verification pass and complete
All 3 TypeScript errors resolved, build passes, server renders.
Phase 2 SSR Shell marked complete.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:41:19 +02:00
kayjaydee 3687f6dcf5 fix: add tailwindcss as devDependency for Nuxt UI v3
@nuxt/ui provides the Vite plugin but tailwindcss package itself
must be installed for @import "tailwindcss" to resolve in CSS.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:39:39 +02:00
kayjaydee 0565fe4b6a fix: remove legacy tailwind.config.js conflicting with Nuxt UI v3
Nuxt UI v3 manages Tailwind v4 internally. The old tailwind.config.js
pointed to src/ and used Tailwind v3 format, causing SSR conflicts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:38:07 +02:00
kayjaydee 6ae48691bd fix: remove vite.config.ts and postcss.config.js conflicting with Nuxt
These are legacy configs from the Vue SPA. Nuxt manages Vite and
PostCSS internally — external configs cause IPC connection errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:34:12 +02:00
kayjaydee 00b4f3c79c fix(i18n): move locale files to i18n/locales/ for @nuxtjs/i18n resolution
@nuxtjs/i18n resolves langDir relative to its own i18n/ directory,
not the project root. Moved fr.json and en.json accordingly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:33:07 +02:00
kayjaydee 09cfc0aaf3 fix(02): resolve 3 typecheck errors and i18n langDir path
- useSetLocale → destructured setLocale from useI18n()
- addSeoAttributes → seo option for useLocaleHead()
- process.env → import.meta.env for Nuxt compatibility
- langDir: 'locales/' → 'app/locales/' (Nuxt 4 resolves from project root)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:31:58 +02:00
kayjaydee 5597c6a8dd docs(02-02): complete layout shell plan (header + footer + default layout) 2026-04-08 16:27:02 +02:00
kayjaydee cfe0180c1f feat(02-02): create AppFooter, default layout, update app.vue with useLocaleHead
- AppFooter with copyright + Gitea/LinkedIn/Fiverr social icons (rel=noopener noreferrer)
- Default layout wraps header + slot + footer with min-h-screen flex
- app.vue uses NuxtLayout + useLocaleHead for global hreflang/canonical
- Fixed a11y.github -> a11y.gitea in both locale files
2026-04-08 16:26:14 +02:00
kayjaydee 93e5d4bc29 docs(02-03): complete per-route SEO metadata plan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:26:04 +02:00
kayjaydee 23fa399d6b feat(02-02): create AppHeader with nav, lang/theme toggles, mobile drawer
- Sticky header with z-[1020], desktop nav with locale-aware NuxtLinks
- FR/EN text toggle using useSetLocale, dark/light icon toggle using useColorMode
- Mobile UDrawer with stacked nav links and toggles
- WCAG: min-w-11 min-h-11 touch targets, focus-visible:ring-2, aria-current on active link
2026-04-08 16:25:16 +02:00
kayjaydee 0a58201f74 feat(02-03): add per-route SEO metadata and JSON-LD to all page stubs
- useSeoMeta() with localized title/description/og tags on all 6 pages
- Homepage JSON-LD with Person + ProfessionalService schema
- og:image absolute URL on every page
- Stub templates with max-w-7xl wrapper and h1

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:25:13 +02:00
kayjaydee 67c511f247 docs(02-01): complete design system + i18n config plan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:23:48 +02:00
kayjaydee 898ef5c3cd feat(02-01): migrate i18n translations for Phase 2 scope
- nav, footer, a11y, seo keys from UI-SPEC copywriting contract
- All existing keys migrated from src/locales/fr.ts and en.ts
- Includes home, projects, about, contact, fiverr, faq, pricing, projectData, testimonials, common
- Emojis stripped from translation values for clean rendering

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:23:03 +02:00
kayjaydee d27b9a3d3c feat(02-01): design system, color-mode, sitemap config
- Brand color #85cb85 as CSS @theme with full shade palette
- app.config.ts maps Nuxt UI primary to brand
- colorMode with cookie storage, dark default, no FOUC
- i18n baseUrl and site.url for absolute SEO URLs
- Static og:image placeholder in public/

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:17:04 +02:00
kayjaydee 33c382f0b7 docs(state): phase 2 planned 2026-04-08 16:13:48 +02:00
kayjaydee 66392740be docs(02): update CONTEXT.md D-05 Gitea + D-12 static og:image 2026-04-08 16:12:23 +02:00
kayjaydee 05e54db4ff docs(02): create phase 2 SSR shell plans 2026-04-08 16:10:05 +02:00
kayjaydee 8cb65c92cd docs(02): research phase SSR shell domain 2026-04-08 15:57:15 +02:00
kayjaydee 08caf52183 docs(state): record phase 2 UI-SPEC session 2026-04-08 15:38:39 +02:00
kayjaydee e9ecfacc92 docs(phase-02): UI design contract for SSR Shell 2026-04-08 15:37:38 +02:00
kayjaydee 0875ec2136 docs(state): record phase 2 context session 2026-04-08 15:32:51 +02:00
kayjaydee 8015a0ea38 docs(02): capture phase context 2026-04-08 15:32:30 +02:00
kayjaydee f1ed93e5d4 docs(phase-01): evolve PROJECT.md after phase completion 2026-04-08 15:19:34 +02:00
kayjaydee 26c2279bdf docs(phase-01): complete phase execution 2026-04-08 15:18:49 +02:00
kayjaydee f64a6754c1 docs(01): add code review fix report 2026-04-08 15:18:04 +02:00
kayjaydee 43356352b3 fix(01): WR-04 add dynamic lang attr on html element via useHead 2026-04-08 15:17:36 +02:00
kayjaydee 89ce718c6c fix(01): WR-03 move Bootstrap and Tailwind CSS from database to front category 2026-04-08 15:17:25 +02:00
kayjaydee 7d81d47b3c fix(01): WR-02 use te() to detect missing i18n keys in useProjects 2026-04-08 15:17:12 +02:00
kayjaydee c6744ab107 fix(01): WR-01 complete i18n config with strategy, langDir and locale files 2026-04-08 15:17:00 +02:00
kayjaydee 184e1257fe fix(01): CR-01 move gtag ID to runtime config env var 2026-04-08 15:16:50 +02:00
kayjaydee 650b860cbb test(01): persist verification and human UAT items 2026-04-08 15:15:24 +02:00
kayjaydee 441ee5245e docs(01): add code review report 2026-04-08 15:13:01 +02:00
kayjaydee 978a564621 fix: restore CLAUDE.md deleted by worktree agent 2026-04-08 15:00:48 +02:00
kayjaydee 432e0d0c21 docs(01-02): complete static data migration plan summary 2026-04-08 15:00:27 +02:00
kayjaydee 55019f68b8 feat(01-02): create useProjects() composable with i18n support
- useProjects() returns projects, featuredProjects, filterByCategory, search, findById
- Added title/description/longDescription fields to Project interface
- Uses Nuxt auto-imports (computed, useI18n, Ref)
- i18n keys follow projects.${id}.title pattern
2026-04-08 14:59:29 +02:00
kayjaydee 2b97bc767e feat(01-02): migrate static data files and images to Nuxt structure
- 4 data files created in app/data/ with proper type imports from shared/types
- 74 WebP images copied to public/images/ (including flowboard gallery)
- All image paths migrated from @/assets/images/ to /images/
- FAQ uses i18n keys instead of direct text
2026-04-08 14:56:53 +02:00
kayjaydee 6b1642479e docs(01-01): complete Nuxt 4 initialization plan summary 2026-04-08 14:53:43 +02:00
kayjaydee c4923a0da9 feat(01-01): add TypeScript interfaces and configure ESLint for Nuxt
- shared/types/index.ts with tightened Project, Technology, TechStack, Testimonial, FAQ interfaces
- technologies, category, date now required on Project (was optional)
- FAQ uses i18n keys (questionKey, answerKey, featuresKey)
- Replace old eslint.config.ts with Nuxt-compatible eslint.config.mjs
2026-04-08 14:53:06 +02:00
kayjaydee 9fbbce07e0 feat(01-01): initialize Nuxt 4 project with all modules
- nuxt.config.ts with compatibilityVersion 4, SSR, 6 modules
- app/app.vue and app/pages/index.vue minimal setup
- pnpm as package manager with all dependencies installed
- TypeScript strict mode enabled
- .gitignore updated for Nuxt (.nuxt, .output, .env)
- tsconfig.json extends .nuxt/tsconfig.json
2026-04-08 14:51:52 +02:00
kayjaydee b075fb81c4 docs(01): address checker revision issues
- Mark RESEARCH.md Open Questions as RESOLVED with decisions
- Fix Plan 01-02 Task 1 verify to be independent of Task 2 (file existence + grep check instead of typecheck)
- Strengthen negative criterion: all app/data/ files must NOT contain @/assets/images/

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 23:45:00 +02:00
19 changed files with 47 additions and 819 deletions
-153
View File
@@ -1,153 +0,0 @@
<script setup lang="ts">
import { hytaleDemos } from '~/data/hytaleDemos'
const { t } = useI18n()
const localePath = useLocalePath()
</script>
<template>
<section
id="demos"
class="py-16 md:py-24 px-4 sm:px-6 lg:px-8"
>
<div class="max-w-7xl mx-auto">
<div class="text-center mb-12">
<span class="font-mono text-sm text-brand-500 dark:text-brand-400 tracking-wider">
{{ t('hytale.demos.label') }}
</span>
<h2
class="text-3xl sm:text-4xl lg:text-5xl font-bold mt-3 bg-gradient-to-r from-gray-900 via-gray-800 to-gray-600 dark:from-white dark:via-gray-200 dark:to-gray-500 bg-clip-text text-transparent"
>
{{ t('hytale.demos.title') }}
</h2>
<p class="text-base sm:text-lg text-gray-500 dark:text-gray-400 mt-4 max-w-2xl mx-auto leading-relaxed">
{{ t('hytale.demos.subtitle') }}
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<UCard
v-for="demo in hytaleDemos"
:key="demo.id"
class="hover:border-brand-500/40 hover:shadow-xl hover:shadow-brand-500/10 hover:-translate-y-1 transition-all duration-200 overflow-hidden"
:class="{ 'ring-2 ring-brand-500': demo.featured }"
>
<template #header>
<div class="aspect-video w-full bg-gray-100 dark:bg-gray-900 -m-4 mb-2 overflow-hidden">
<NuxtImg
:src="demo.image"
:alt="t(`hytale.demos.${demo.id}.title`)"
width="600"
height="338"
loading="lazy"
class="w-full h-full object-cover"
/>
</div>
</template>
<div class="flex flex-col gap-3 p-2">
<div class="flex items-center justify-between gap-2">
<h3 class="text-lg font-bold text-gray-900 dark:text-white">
{{ t(`hytale.demos.${demo.id}.title`) }}
</h3>
<UBadge
v-if="demo.featured"
color="primary"
variant="subtle"
size="sm"
>
{{ t('hytale.demos.featured') }}
</UBadge>
</div>
<p class="text-sm text-gray-600 dark:text-gray-300 leading-relaxed line-clamp-3">
{{ t(`hytale.demos.${demo.id}.tagline`) }}
</p>
<div class="flex flex-wrap gap-1.5">
<UBadge
v-for="t in demo.tech"
:key="t"
color="neutral"
variant="soft"
size="xs"
>
{{ t }}
</UBadge>
</div>
<div class="flex flex-wrap gap-2 mt-2">
<UButton
:to="localePath(`/project/${demo.id}`)"
color="primary"
variant="soft"
size="sm"
trailing-icon="i-lucide-arrow-right"
>
{{ t('projects.buttons.viewProject') }}
</UButton>
<UButton
v-if="demo.website"
:to="demo.website"
target="_blank"
rel="noopener noreferrer"
color="primary"
variant="solid"
size="sm"
trailing-icon="i-lucide-external-link"
>
{{ t('hytale.demos.viewSite') }}
</UButton>
<UButton
v-if="demo.modtale"
:to="demo.modtale"
target="_blank"
rel="noopener noreferrer"
color="neutral"
variant="outline"
size="sm"
>
Modtale
</UButton>
<UButton
v-if="demo.curseforge"
:to="demo.curseforge"
target="_blank"
rel="noopener noreferrer"
color="neutral"
variant="outline"
size="sm"
>
CurseForge
</UButton>
<UButton
v-if="demo.github"
:to="demo.github"
target="_blank"
rel="noopener noreferrer"
color="neutral"
variant="outline"
size="sm"
icon="i-simple-icons-github"
/>
<UButton
v-if="demo.gitea"
:to="demo.gitea"
target="_blank"
rel="noopener noreferrer"
color="neutral"
variant="outline"
size="sm"
icon="i-simple-icons-gitea"
/>
</div>
</div>
</UCard>
</div>
<p class="text-center text-sm text-gray-500 dark:text-gray-400 mt-10 italic">
{{ t('hytale.demos.footnote') }}
</p>
</div>
</section>
</template>
+2 -3
View File
@@ -7,7 +7,6 @@ interface Props {
const props = defineProps<Props>()
const { t } = useI18n()
const localePath = useLocalePath()
const translatedCategory = computed(() => {
if (!props.project.category) return ''
@@ -23,7 +22,7 @@ const translatedCategory = computed(() => {
itemtype="https://schema.org/CreativeWork"
>
<!-- Image -->
<NuxtLink :to="localePath(`/project/${project.id}`)" class="block relative overflow-hidden">
<NuxtLink :to="`/project/${project.id}`" class="block relative overflow-hidden">
<NuxtImg
:src="project.image"
:alt="`${project.title} - ${project.description.slice(0, 60)}...`"
@@ -82,7 +81,7 @@ const translatedCategory = computed(() => {
<!-- Hidden SEO link -->
<NuxtLink
:to="localePath(`/project/${project.id}`)"
:to="`/project/${project.id}`"
class="absolute inset-0 z-10"
:aria-label="`${t('projects.buttons.viewProject')} - ${project.title}`"
itemprop="url"
+6 -6
View File
@@ -28,7 +28,7 @@ src="/images/logo.webp" alt="Killian' DAL-CIN" width="36" height="36" loading="l
<span class="text-lg font-bold text-gray-900 dark:text-white">Killian' DAL-CIN</span>
</NuxtLink>
<p class="text-sm text-gray-500 dark:text-gray-400 leading-relaxed max-w-xs">
{{ t('footer.tagline') }}
Full Stack Developer &amp; Hytale Plugin Developer. Building modern web experiences and game plugins.
</p>
</div>
@@ -49,13 +49,13 @@ v-for="link in quickLinks" :key="link.key" :to="localePath(link.path)"
<!-- Services links -->
<div>
<h3 class="font-mono text-xs text-gray-400 dark:text-gray-500 uppercase tracking-widest mb-5">
{{ t('footer.services') }}
Services
</h3>
<nav class="flex flex-col gap-3">
<span class="text-sm text-gray-600 dark:text-gray-400">{{ t('footer.servicesList.hytalePlugins') }}</span>
<span class="text-sm text-gray-600 dark:text-gray-400">{{ t('footer.servicesList.webDev') }}</span>
<span class="text-sm text-gray-600 dark:text-gray-400">{{ t('footer.servicesList.retainer') }}</span>
<span class="text-sm text-gray-600 dark:text-gray-400">{{ t('footer.servicesList.consulting') }}</span>
<span class="text-sm text-gray-600 dark:text-gray-400">Web Development</span>
<span class="text-sm text-gray-600 dark:text-gray-400">Hytale Plugins</span>
<span class="text-sm text-gray-600 dark:text-gray-400">Consulting</span>
<span class="text-sm text-gray-600 dark:text-gray-400">Maintenance</span>
</nav>
</div>
@@ -1,7 +1,6 @@
<script setup lang="ts">
const { t } = useI18n()
const { featuredProjects } = useProjects()
const localePath = useLocalePath()
</script>
<template>
@@ -14,7 +13,7 @@ const localePath = useLocalePath()
<h2 class="text-3xl sm:text-4xl lg:text-5xl font-bold mt-3 bg-gradient-to-r from-gray-900 via-gray-800 to-gray-600 dark:from-white dark:via-gray-200 dark:to-gray-500 bg-clip-text text-transparent">{{ t('home.featuredProjects.title') }}</h2>
<p class="text-lg text-gray-500 dark:text-gray-400 mt-4 max-w-2xl leading-relaxed">{{ t('home.featuredProjects.subtitle') }}</p>
</div>
<UButton :to="localePath('/projects')" variant="ghost" trailing-icon="i-lucide-arrow-right" class="shrink-0 self-start md:self-auto group">
<UButton to="/projects" variant="ghost" trailing-icon="i-lucide-arrow-right" class="shrink-0 self-start md:self-auto group">
{{ t('home.cta.viewProjects') }}
</UButton>
</div>
+5 -5
View File
@@ -2,16 +2,16 @@
const { t } = useI18n()
const services = computed(() => [
{
icon: 'i-lucide-package',
title: t('home.services.hytalePlugins.title'),
description: t('home.services.hytalePlugins.description'),
},
{
icon: 'i-lucide-monitor',
title: t('home.services.webDev.title'),
description: t('home.services.webDev.description'),
},
{
icon: 'i-lucide-smartphone',
title: t('home.services.mobileApps.title'),
description: t('home.services.mobileApps.description'),
},
{
icon: 'i-lucide-zap',
title: t('home.services.optimization.title'),
-58
View File
@@ -1,58 +0,0 @@
// Live Hytale plugins shipped publicly — featured on /hytale#demos
// Each entry has corresponding i18n keys at hytale.demos.{id}.*
export interface HytaleDemo {
id: string
image: string
github?: string
gitea?: string
curseforge?: string
modtale?: string
website?: string
tech: string[]
status: 'live' | 'pending' | 'soon'
featured?: boolean
}
export const hytaleDemos: HytaleDemo[] = [
{
id: 'votepipe',
image: '/images/projects/votepipe.svg',
website: 'https://votepipe.com',
modtale: 'https://modtale.net/mod/votepipe',
curseforge: 'https://www.curseforge.com/hytale/mods/votepipe',
tech: ['Java 25', 'SaaS', 'Webhooks', 'Votifier'],
status: 'live',
featured: true,
},
{
id: 'gravity-flip',
image: '/images/projects/gravityflip.png',
modtale: 'https://modtale.net/mod/gravity-flip',
curseforge: 'https://curseforge.com/hytale/mods/gravity-flip',
github: 'https://github.com/Mr-KayJayDee/hytale-gravity-flip',
gitea: 'https://gitea.kamisama.ovh/kayjaydee/hytale-gravity-flip',
tech: ['Java 25', 'Gradle Shadow', 'Hytale API'],
status: 'live',
},
{
id: 'async',
image: '/images/projects/async.png',
modtale: 'https://modtale.net/mod/async',
curseforge: 'https://www.curseforge.com/hytale/mods/async',
github: 'https://github.com/Mr-KayJayDee/async',
gitea: 'https://gitea.kamisama.ovh/kayjaydee/async',
tech: ['Kotlin 2.2', 'Coroutines', 'JDK 25', 'Hytale API'],
status: 'live',
featured: true,
},
{
id: 'chain-lightning',
image: '/images/projects/chain-lightning.png',
modtale: 'https://modtale.net/mod/chain-lightning-sceptre',
curseforge: 'https://www.curseforge.com/hytale/mods/chain-lightning-sceptre',
github: 'https://github.com/Mr-KayJayDee/hytale-chain-lightning',
gitea: 'https://gitea.kamisama.ovh/kayjaydee/hytale-chain-lightning',
tech: ['Java 25', 'JUnit 5', 'Hytale API'],
status: 'live',
},
]
+2 -121
View File
@@ -3,127 +3,6 @@ import type { Project } from '~~/shared/types'
// Base project data without translations
// Titles and descriptions are resolved via i18n keys: projects.${id}.title, projects.${id}.description
export const projects: Omit<Project, 'title' | 'description' | 'longDescription'>[] = [
{
id: 'votepipe',
image: '/images/projects/votepipe.svg',
technologies: ['Java 25', 'Hytale Plugin API', 'TypeScript', 'SaaS', 'HTTPS Webhooks', 'Votifier RSA/HMAC'],
category: 'Hytale Plugin',
date: '2026',
featured: true,
buttons: [
{
title: 'Website',
link: 'https://votepipe.com',
},
{
title: 'Modtale',
link: 'https://modtale.net/mod/votepipe',
},
{
title: 'CurseForge',
link: 'https://www.curseforge.com/hytale/mods/votepipe',
},
{
title: 'Documentation',
link: 'https://votepipe.com/docs',
},
],
},
{
id: 'gravity-flip',
image: '/images/projects/gravityflip.png',
technologies: ['Java 25', 'Hytale Plugin API', 'Gradle Shadow', 'JUnit 5'],
category: 'Hytale Plugin',
date: '2026',
featured: true,
buttons: [
{
title: 'Modtale',
link: 'https://modtale.net/mod/gravity-flip',
},
{
title: 'CurseForge',
link: 'https://curseforge.com/hytale/mods/gravity-flip',
},
{
title: 'GitHub',
link: 'https://github.com/Mr-KayJayDee/hytale-gravity-flip',
},
{
title: 'Gitea',
link: 'https://gitea.kamisama.ovh/kayjaydee/hytale-gravity-flip',
},
],
},
{
id: 'async',
image: '/images/projects/async.png',
technologies: ['Kotlin 2.2', 'Coroutines', 'JDK 25', 'Hytale Plugin API', 'Gradle Shadow', 'JUnit 5'],
category: 'Hytale Library',
date: '2026',
featured: true,
buttons: [
{
title: 'Modtale',
link: 'https://modtale.net/mod/async',
},
{
title: 'CurseForge',
link: 'https://www.curseforge.com/hytale/mods/async',
},
{
title: 'GitHub',
link: 'https://github.com/Mr-KayJayDee/async',
},
{
title: 'Gitea',
link: 'https://gitea.kamisama.ovh/kayjaydee/async',
},
],
},
{
id: 'chain-lightning',
image: '/images/projects/chain-lightning.png',
technologies: ['Java 25', 'Hytale Plugin API', 'Gradle Shadow', 'JUnit 5'],
category: 'Hytale Plugin',
date: '2026',
featured: true,
buttons: [
{
title: 'Modtale',
link: 'https://modtale.net/mod/chain-lightning-sceptre',
},
{
title: 'CurseForge',
link: 'https://www.curseforge.com/hytale/mods/chain-lightning-sceptre',
},
{
title: 'GitHub',
link: 'https://github.com/Mr-KayJayDee/hytale-chain-lightning',
},
{
title: 'Gitea',
link: 'https://gitea.kamisama.ovh/kayjaydee/hytale-chain-lightning',
},
],
},
{
id: 'playhours',
image: '/images/projects/playhours.png',
technologies: ['Java 17', 'Forge 1.20.1', 'LuckPerms', 'TOML Config'],
category: 'Minecraft Mod',
date: '2025',
buttons: [
{
title: 'CurseForge',
link: 'https://www.curseforge.com/minecraft/mc-mods/playhours',
},
{
title: 'Repository',
link: 'https://gitea.kamisama.ovh/kayjaydee/PlayHours',
},
],
},
{
id: 'virtual-tour',
image: '/images/virtualtour.webp',
@@ -143,6 +22,7 @@ export const projects: Omit<Project, 'title' | 'description' | 'longDescription'
technologies: ['Node.js', 'Discord.js', 'MongoDB', 'Express'],
category: 'Bot Development',
date: '2023',
featured: true,
buttons: [
{
title: 'Invite',
@@ -156,6 +36,7 @@ export const projects: Omit<Project, 'title' | 'description' | 'longDescription'
technologies: ['JavaScript', 'Node.js', 'Canvas', 'npm'],
category: 'Open Source',
date: '2022',
featured: true,
buttons: [
{
title: 'Repository',
+2 -3
View File
@@ -2,7 +2,6 @@
import { techStack } from '~/data/techstack'
const { t } = useI18n()
const localePath = useLocalePath()
useSeoMeta({
title: () => t('seo.about.title'),
@@ -181,9 +180,9 @@ const approachCards = computed(() => [
:title="t('about.cta.title')"
:subtitle="t('about.cta.description')"
:primary-text="t('about.cta.button')"
:primary-to="localePath('/contact')"
primary-to="/contact"
:secondary-text="t('home.cta.viewProjects')"
:secondary-to="localePath('/projects')"
secondary-to="/projects"
/>
</div>
</template>
-3
View File
@@ -31,9 +31,6 @@ useHead({
<div>
<HytaleHeroSection />
<HytaleServicesSection />
<div class="relative bg-gray-50/50 dark:bg-gray-900/20">
<HytaleDemoGrid />
</div>
<HytalePricingSection />
<div class="relative bg-gray-50/50 dark:bg-gray-900/20">
<TestimonialsSection />
+2 -3
View File
@@ -1,7 +1,6 @@
<script setup lang="ts">
const route = useRoute()
const { t } = useI18n()
const localePath = useLocalePath()
const { findById, projects } = useProjects()
const project = findById(route.params.id as string)
@@ -53,7 +52,7 @@ useSeoMeta({
variant="solid"
color="neutral"
icon="i-lucide-arrow-left"
:to="localePath('/projects')"
to="/projects"
size="sm"
class="shadow-lg backdrop-blur-sm"
>
@@ -216,7 +215,7 @@ useSeoMeta({
<NuxtLink
v-for="related in relatedProjects"
:key="related.id"
:to="localePath(`/project/${related.id}`)"
:to="`/project/${related.id}`"
class="flex gap-3 p-3 rounded-xl border border-transparent hover:border-brand-500/20 hover:bg-brand-500/5 transition-all duration-200 group"
>
<NuxtImg
@@ -1,138 +0,0 @@
---
title: "GravityFlip: from client brief to a production-grade Hytale plugin"
description: "Lessons learned shipping GravityFlip — how a vague request (\"I want to flip gravity\") became an architected, configurable Hytale plugin that builders can use without touching code."
date: "2026-04-25"
tags: ["hytale", "case-study", "gravity-flip", "consulting"]
draft: false
---
> **TL;DR** — A client pinged me to "invert gravity in part of my map". Five questions later we were building a **multi-region plugin** with an in-game wand, JSON persistence, and three visualization modes. It's now live in production: [GravityFlip on Modtale](https://modtale.net/mod/gravity-flip).
## The original brief
The Discord message ran two sentences long:
> *"Hi, I'd like a plugin that flips gravity on a zone of my server. It's for an event this weekend, I can pay."*
This is exactly the most dangerous brief shape. Read fast, it sounds clear (*"flip gravity"* + *"zone"* — that's enough to start coding, right?). Read carefully, it says **nothing** specific:
- *"a zone"* — how many? one, configured via YAML? several, managed in-game? server-wide?
- *"flip gravity"* — for whom? players only? falling items? mobs? projectiles?
- *"for an event this weekend"* — one-off, or a reusable tool for the server's builders later?
- *"I can pay"* — what scope, what budget? Essential plugin at €149 or full system at €349?
I've made it a rule to **never write code until I've exhausted ambiguity**. It feels counter-intuitive when the client is in a hurry, but 30 minutes of questions saves 10 hours of rewrites.
## The five questions that changed everything
### 1. "How many zones, and who defines them?"
Answer: *"At first I thought just one, but actually I'd like my builders to create more without asking me each time."*
Immediate decision: **multi-region system** with persistence. Hardcoded YAML is out (too much friction). We pivot to an **in-game wand** plus `/gravityflip define <name>` commands. Classic Bukkit/Spigot community pattern, ported to Hytale.
### 2. "Who flips? Players only, or everything?"
Answer: *"Players for sure, but flipping dropped items would be sweet too. Mobs I'm not sure."*
Decision: **three booleans per region**`AffectPlayers`, `AffectItems`, `AffectNpcs`. Default: all on, but the builder can disable mobs on a "jump arena" zone if floating mobs ruin the gameplay. The marginal cost of those three toggles in the JSON codec was zero — optionality offered for free.
### 3. "What happens when a player enters the zone at full free-fall speed?"
Answer (after a pause): *"Uhh... I hadn't thought about it. They shouldn't take fall damage, right?"*
Decision: **configurable `GracePeriodMs` (default 2500ms)**. During the transition, we smooth the gravity flip instead of an instant binary swap that produces brutal acceleration and absurd fall damage. Bonus: a `FallDamage` toggle (default false) for zones where falling should still cost something gameplay-wise.
That's the kind of detail **a written brief never surfaces**. A real conversation does.
### 4. "Do you want to see the zones when you're inside one?"
Answer: *"Yeah in build mode I have to, otherwise I lose track. But in player mode nothing should show."*
Decision: **three visualization modes**.
- `Outline` — configurable wireframe color (`VisualColor`, default `#00FFFF`) — build mode
- `Particles` — edge-emitting particles (`Torch_Fire` default), more subtle but visible
- `None` — invisible, production mode
Per-region toggle via the `/gravityflip toggle` command. Build and live mode coexist on the same map without re-deploying the plugin.
### 5. "Is this one-shot or are you reusing it?"
Answer: *"One-shot for the event, but if it's well done I'll keep it."*
Pivotal decision: we treat this project as **a production plugin**, not a script. Concretely:
- JSON persistence in `Server/mods/Mythlane_GravityFlip/regions.json` (not memory-only)
- 10 Hz tick loop with concurrent snapshots (lock-free reads)
- Unit tests on pure logic (codec, AABB geometry)
- Auto-seeded demo region on first run for instant onboarding
- Polished EN README, distributable
**Cost vs "quick & dirty"** : about 30 % more time. **Benefit** : the client moved from "I need it by Saturday" to "I still use it, my builders love it". The plugin is now publishable, monetizable, and so it became a shared asset.
## The architecture that emerged
```
Player / NPC / Item
▼ (each tick, 10 Hz)
RegionTickLoop ◄─── snapshots from regions.json
GravityApplier + FallDamageGuard
per-region effect
```
The wand follows its own cycle:
```
Player click → WandSelectionStore
/gravityflip define <name>
Region registry → regions.json (auto-save)
```
Stack: **Java 25**, official Hytale Plugin API (`com.hypixel.hytale.plugin`), Gradle Shadow to relocate Gson (avoiding stdlib conflicts), JUnit 5 for tests. Source ~2,500 lines, 30 % of which is tests.
### The piece of code that took the most thinking
The **GracePeriodMs**. Not the code itself but the idea behind it: instead of a binary flip, we interpolate the vertical velocity component over a sliding window. Naively:
```java
// naive version — produces absurd fall damage
if (region.contains(entity)) {
entity.velocity.y = -entity.velocity.y; // instant flip
}
```
With a grace period, smooth transition:
```java
// production version — gradual entry
final long timeInRegion = now - entry.enteredAt();
final double gracePct = Math.min(1.0, timeInRegion / region.gracePeriodMs());
final double targetVy = -region.verticalForce(); // antigrav
entity.velocity.y = lerp(entity.velocity.y, targetVy, gracePct);
```
Three extra lines, but that's what makes the difference between a plugin that's "fun for 30 seconds" and a plugin that players actually use without rage-quitting.
## What this project taught me (or reminded me)
**First**: senior dev value isn't in code-typing speed. It's in the **series of questions** that turn a vague brief into an actionable spec. Without question 3 (free-fall), the client would have shipped the event with absurd fall damage, and the plugin would've been dropped after the weekend.
**Second**: a Hytale plugin that sells isn't a script. It's a **production-grade Java codebase** with persistence, tests, docs, and sub-5-minute onboarding. The client paid €349 (the "Custom System" tier), not €50 Fiverr — and the premium is justified by the quality the client will exploit for months.
**Third**: every plugin I write ends up published. GravityFlip is freely available on [Modtale](https://modtale.net/mod/gravity-flip) and soon on CurseForge. This doesn't dilute the value of paid commissions — it **boosts** my credibility to future prospects looking for a developer who can ship clean code, not just glue StackOverflow snippets.
## Got a Hytale project in mind?
The pattern is always the same: we talk for 30 minutes, I ask the 5-10 questions that kill ambiguity, I send a firm quote, I deliver. Pricing is public on [/hytale](/hytale) — €149 for an essential plugin, €349 for a custom system, €790+ for custom MMO infrastructure.
No Fiverr, no race-to-the-bottom. Just a senior dev shipping code you'll still use six months later.
[Request a quote](/contact) · [See GravityFlip in action](https://modtale.net/mod/gravity-flip) · [See my other plugins](/projects)
@@ -1,138 +0,0 @@
---
title: "GravityFlip : du brief client au plugin Hytale en production"
description: "Retour d'expérience sur le développement de GravityFlip — comment une demande floue (\"je veux inverser la gravité\") devient un plugin Hytale architecturé, configurable, et utilisable par des builders sans toucher au code."
date: "2026-04-25"
tags: ["hytale", "case-study", "gravity-flip", "consulting"]
draft: false
---
> **TL;DR** — Un client m'a contacté pour "inverser la gravité dans une partie de la map". Cinq questions plus tard, on était sur un plugin de **régions paramétrables** avec wand in-game, persistence JSON, et trois modes de visualisation. Le résultat tourne aujourd'hui en production : [GravityFlip sur Modtale](https://modtale.net/mod/gravity-flip).
## Le brief initial
Le message Discord d'origine tient en deux phrases :
> *"Salut, j'aimerais un plugin qui inverse la gravité sur une zone de mon serveur. C'est pour un event ce week-end, je peux payer."*
C'est exactement la forme de brief la plus dangereuse. Lue rapidement, elle donne l'illusion d'être claire (*"inverser la gravité"* + *"zone"* = ça suffit pour coder, non ?). Lue posément, elle ne dit **rien** de précis :
- *"une zone"* — combien ? une seule, configurée par YAML ? plusieurs, gérées en jeu ? globale au serveur ?
- *"inverser la gravité"* — pour qui ? les joueurs uniquement ? les items qui tombent ? les mobs ? les projectiles ?
- *"pour un event ce week-end"* — usage one-shot ou outil réutilisable par les builders du serveur après ?
- *"je peux payer"* — quel scope, quel budget ? Plugin essentiel à 149€ ou système complet à 349€ ?
J'ai pris l'habitude de **ne jamais coder avant d'avoir épuisé les ambiguïtés**. Ça paraît contre-intuitif quand le client est pressé, mais 30 minutes de questions économisent 10 heures de réécriture.
## Les cinq questions qui ont tout changé
### 1. "Combien de zones, et qui les définit ?"
Réponse : *"Au début je pensais une seule, mais en fait j'aimerais que mes builders puissent en créer plusieurs sans me demander à chaque fois."*
Décision immédiate : **système multi-régions** avec persistence. On exclut le YAML hardcodé (trop friction) et on s'oriente vers un **wand in-game** + commandes `/gravityflip define <name>`. Pattern classique de la communauté Bukkit/Spigot, repris pour Hytale.
### 2. "Pour qui la gravité s'inverse ? Joueurs uniquement, ou tout ?"
Réponse : *"Les joueurs ouais, mais aussi les items dropés ce serait stylé. Les mobs je sais pas."*
Décision : **trois booleans configurables par région**`AffectPlayers`, `AffectItems`, `AffectNpcs`. Default = tout activé, mais le builder peut désactiver les mobs sur une zone "salle de saut" si les mobs flottants gâchent le gameplay. Le coût marginal de ces 3 toggles dans le codec JSON était nul, l'optionalité a été offerte.
### 3. "Que se passe-t-il quand un joueur entre dans la zone à pleine vitesse en chute libre ?"
Réponse (après une pause) : *"Heu... j'avais pas pensé. Il devrait pas se prendre les dégâts de chute non ?"*
Décision : **`GracePeriodMs` configurable (default 2500ms)**. Pendant la transition, on lisse l'inversion de gravité au lieu d'un flip instantané qui produit une accélération brutale et des dégâts de chute aberrants. En bonus : un toggle `FallDamage` (default false) pour les zones où on veut quand même que tomber ait un coût gameplay.
C'est le genre de détail qu'**un brief écrit ne fait jamais émerger**. Une vraie discussion, oui.
### 4. "Tu veux voir les zones quand tu es dedans ?"
Réponse : *"Oui en build c'est obligé sinon je sais plus où elles sont. Mais en live joueur faut rien voir."*
Décision : **trois modes de visualisation**.
- `Outline` — wireframe couleur configurable (`VisualColor`, default `#00FFFF`), pour le mode build
- `Particles` — bordures émettrices de particules (`Torch_Fire` par défaut), plus discret mais visible
- `None` — invisible, mode production
Toggle par région via la commande `/gravityflip toggle`. Build mode et live mode sur la même map sans rebuild du plugin.
### 5. "C'est un one-shot ou tu réutilises ?"
Réponse : *"One-shot pour l'event, mais si c'est bien fait je le garde."*
Décision déterminante : on traite ce projet comme **un plugin de production**, pas comme un script. Concrètement :
- Persistance JSON dans `Server/mods/Mythlane_GravityFlip/regions.json` (pas mémoire seule)
- Tick loop 10x/sec avec snapshots concurrents (lecture lock-free)
- Tests unitaires sur la logique pure (codec, géométrie AABB)
- Demo region auto-seed au premier démarrage pour onboarding instantané
- Documentation README EN pro, distribuable
**Surcoût vs version "quick & dirty"** : environ 30 % de temps en plus. **Bénéfice** : le client est passé de "j'ai besoin pour samedi" à "je l'utilise toujours, mes builders l'adorent". Le plugin est désormais publiable, monétisable, et donc devenu un actif partagé.
## L'architecture qui en est sortie
```
Player / NPC / Item
▼ (chaque tick, 10 Hz)
RegionTickLoop ◄─── snapshots de regions.json
GravityApplier + FallDamageGuard
effet par région
```
Le wand suit son propre cycle :
```
Player click → WandSelectionStore
/gravityflip define <name>
Region registry → regions.json (auto-save)
```
Stack technique : **Java 25**, Hytale Plugin API officielle (`com.hypixel.hytale.plugin`), Gradle Shadow pour relocalisation Gson (évite les conflits stdlib), JUnit 5 pour les tests. Code source ~2 500 lignes, dont 30 % de tests.
### Le bout de code qui m'a coûté le plus de réflexion
Le **GracePeriodMs**. Pas du code, mais l'idée derrière : au lieu d'un flip binaire, on interpole la composante verticale de la vélocité sur une fenêtre glissante. Naïvement on écrit :
```java
// version naïve — produit des dégâts de chute aberrants
if (region.contains(entity)) {
entity.velocity.y = -entity.velocity.y; // flip instantané
}
```
Avec grace period, on lisse la transition :
```java
// version production — entrée progressive
final long timeInRegion = now - entry.enteredAt();
final double gracePct = Math.min(1.0, timeInRegion / region.gracePeriodMs());
final double targetVy = -region.verticalForce(); // antigrav
entity.velocity.y = lerp(entity.velocity.y, targetVy, gracePct);
```
Trois lignes de plus, mais c'est ce qui fait la différence entre un plugin "marrant 30 secondes" et un plugin que les joueurs utilisent sans rage-quit.
## Ce que ce projet m'a appris (ou rappelé)
**Premièrement** : la valeur du dev senior n'est pas dans la rapidité de code. C'est dans la **série de questions** qui transforme un brief flou en spec actionnable. Si je n'avais pas posé la question 3 (chute libre), le client aurait livré l'event avec des dégâts de chute aberrants, et le plugin aurait été abandonné après le week-end.
**Deuxièmement** : un plugin Hytale qui se vend, ce n'est pas un script. C'est une **codebase Java production-grade** avec persistence, tests, doc, et un onboarding sub-5 minutes. Le client a payé 349€ (tier "Système Sur-Mesure"), pas 50€ Fiverr — et le surcoût est justifié par la qualité que le client va exploiter pendant des mois.
**Troisièmement** : chaque plugin que je code finit publié. GravityFlip est dispo gratuitement sur [Modtale](https://modtale.net/mod/gravity-flip) et bientôt sur CurseForge. Ça ne dilue pas la valeur de la commande client — ça **augmente** ma crédibilité auprès des futurs prospects qui cherchent un dev capable de livrer du code propre, et pas juste de coller des snippets StackOverflow.
## Tu as un projet Hytale en tête ?
Le pattern est toujours le même : on parle 30 minutes, je pose les 5-10 questions qui tuent les ambiguïtés, je te donne un devis ferme, et je livre. Les tarifs sont publics sur [/hytale](/hytale) — entre 149€ pour un plugin essentiel, 349€ pour un système complet, et 790€+ pour une infrastructure MMO custom.
Pas de Fiverr, pas de race-to-the-bottom. Juste un dev senior qui livre du code que tu utilises encore six mois plus tard.
[Demander un devis](/contact) · [Voir GravityFlip en action](https://modtale.net/mod/gravity-flip) · [Voir mes autres plugins](/projects)
+13 -90
View File
@@ -9,15 +9,14 @@
},
"footer": {
"copyright": "© 2026 Killian' DAL-CIN",
"tagline": "Hytale Plugin Developer & Freelance Web Dev. Custom Java plugins, gaming server websites, production-grade Vue/Nuxt apps.",
"navigation": "Quick Links",
"services": "Services",
"legalNotices": "Legal Notices",
"privacyPolicy": "Privacy Policy",
"servicesList": {
"hytalePlugins": "Hytale Plugins (Java)",
"webDev": "Web Sites & Apps",
"retainer": "Monthly Retainer",
"webDev": "Web Development",
"mobileApps": "Mobile Apps",
"apiBackend": "API Development",
"consulting": "Tech Consulting"
}
},
@@ -67,20 +66,20 @@
"contact": "Contact me"
},
"featuredProjects": {
"title": "Hytale plugins & web apps running in production",
"subtitle": "Portfolio of real projects, in production, used by real players and clients. Java 25 Hytale plugins, Vue/Nuxt apps, SaaS, Discord bots — not proofs-of-concept, actual shipping.",
"title": "Web Applications That Deliver Results",
"subtitle": "Portfolio of real projects that transformed ideas into success. Lightning-fast Vue.js apps, scalable React platforms, robust Node.js APIs.",
"viewAll": "Explore All Projects"
},
"services": {
"title": "Premium Hytale & Web Services",
"subtitle": "Custom Hytale plugins, high-performance web apps, gaming SaaS. Stack: Java 25 + Vue/Nuxt + Node.js. Transparent pricing (€149-€790), quote within 24h.",
"hytalePlugins": {
"title": "Custom Hytale Plugins (Java)",
"description": "From essential plugin to full MMO system. Wand-based regions, votes & rewards, economy, quests, mini-games. Stack: Java 25 + Hytale Plugin API + Gradle Shadow."
},
"title": "Premium Web Development Services",
"subtitle": "Turnkey solutions that boost your growth. Cutting-edge technologies + proven methodology = guaranteed success for your digital project.",
"webDev": {
"title": "Vue.js / Nuxt / React Web Apps",
"description": "Gaming server websites, SaaS platforms, e-commerce. SEO-optimized Nuxt 4 SSR, admin dashboards, Tebex/Discord integrations. Lighthouse 95+, <2s load times."
"title": "Custom Vue.js/React Web Applications",
"description": "Lightning-fast web apps that convert visitors into customers. Modern SPAs, offline-first PWAs, high-conversion e-commerce. SEO-friendly from day one."
},
"mobileApps": {
"title": "Cost-Effective Cross-Platform Mobile Apps",
"description": "One codebase = iOS + Android + Web. React Native for performant native apps. 60% cost savings vs native development."
},
"optimization": {
"title": "Performance & Technical SEO Optimization",
@@ -113,9 +112,6 @@
"subtitle": "Browse my work: custom Hytale plugins, Vue.js applications, React websites, Node.js APIs, Discord bots, and enterprise software.",
"categories": {
"all": "All Projects",
"hytaleplugin": "Hytale Plugin",
"hytalelibrary": "Hytale Library",
"minecraftmod": "Minecraft Mod",
"webdevelopment": "Web Development",
"botdevelopment": "Bot Development",
"opensource": "Open Source",
@@ -253,55 +249,6 @@
}
},
"projectData": {
"votepipe": {
"title": "VotePipe — Hytale Vote Rewards SaaS",
"description": "Unified SaaS platform that combines Webhook (V1 RSA, V2 HMAC) and Votifier to handle votes from all 7 major Hytale server lists in a single plugin. Visual reward builder, automatic delivery, no port forwarding needed.",
"longDescription": "The only Hytale plugin that runs Webhook and Votifier through one unified pipeline. Free / Pro / Network tiers with web dashboard (app.votepipe.com), visual reward builder, streaks, milestones, lucky tiers. Stack: Java 25 plugin + TypeScript backend + SaaS dashboard. Outbound-only secure cloud architecture.",
"buttons": {
"website": "Official Site",
"modtale": "Modtale",
"curseforge": "CurseForge",
"documentation": "Documentation"
}
},
"gravity-flip": {
"title": "GravityFlip — Hytale Anti-Gravity Regions",
"description": "Hytale plugin that creates custom anti-gravity zones with an in-game wand. Walk on ceilings, floating items, drifting mobs — all configurable without touching files.",
"longDescription": "Wand-based region builder for Hytale servers. Corners are set with left/right click, JSON persistence is automatic, 10x/sec tick loop, configurable vertical force and grace period. Visual modes: outline / particles / hidden. Built on Hytale Plugin API + Java 25 + Gradle Shadow.",
"buttons": {
"modtale": "Modtale",
"curseforge": "CurseForge"
}
},
"async": {
"title": "Async — Coroutines for Hytale's per-world ECS",
"description": "Kotlin coroutine library that replaces the noisy CompletableFuture + world.execute pattern with one suspending call. Player/world/plugin scopes, three dispatchers, suspending ECS DSL.",
"longDescription": "Async solves Hytale's per-world thread model: each world runs on its own thread, touching components from elsewhere throws, and blocking I/O on the world thread freezes players. The library ships dispatchers (World, HytaleIO, Scheduled), scope registries (PlayerScopes, WorldScopes, PluginScopes) with automatic cancellation on disconnect, and a suspending read/modify DSL. Built in Kotlin 2.2, target JVM 24, modular split (core / ecs / binding / dist) so business logic stays testable without a Hytale server.",
"buttons": {
"modtale": "Modtale",
"curseforge": "CurseForge",
"github": "GitHub",
"gitea": "Gitea"
}
},
"chain-lightning": {
"title": "ChainLightning Sceptre — Hytale Magic Wand",
"description": "Hytale plugin that fires chain lightning on right-click — bolt jumps to up to 5 nearby enemies within 8 blocks, with damage falloff per hop and a 4-second cooldown.",
"longDescription": "Magical sceptre for Hytale servers. Pure-Java chain resolver decoupled from Hytale via small interfaces (RayCaster, EntitySource, ChainEntity), JUnit 5 tested without a running server. Built on Hytale Plugin API + Java 25 + Gradle Shadow.",
"buttons": {
"modtale": "Modtale",
"curseforge": "CurseForge"
}
},
"playhours": {
"title": "PlayHours — Forge Server Hours Enforcement",
"description": "Forge 1.20.1 mod that enforces per-day open windows, blocks logins outside hours, warns at 15/10/5/1 min, auto-kicks at close, handles holidays, whitelist/blacklist, force modes, LuckPerms integration.",
"longDescription": "Minecraft server mod for time-gated access: per-day schedules, midnight-spanning, date exceptions, dynamic MOTD, multi-language (EN/FR), LuckPerms or vanilla ops permissions. Perfect for educational servers, family servers, or maintenance windows.",
"buttons": {
"curseforge": "CurseForge",
"repository": "Repository"
}
},
"virtual-tour": {
"title": "Virtual Tour - Interactive 360° Experience",
"description": "My high school teacher and me had an idea to create a Virtual tour with 360° videos to allow everyone to visit the school from the web.",
@@ -524,30 +471,6 @@
"hourly": "Outside packages: €45/h excl. VAT, 1h minimum (spot fixes, audits).",
"flagshipCta": "Flagship: custom quote after a 30-minute scoping call."
},
"demos": {
"label": "// live-demos",
"title": "Live plugins, production-grade code",
"subtitle": "No marketing promises. These are the Hytale plugins I publicly maintain — usable today on any Hytale server.",
"featured": "Featured",
"viewSite": "View site",
"footnote": "Every plugin is built in-house, production-tested, and ships with full documentation.",
"votepipe": {
"title": "VotePipe — Vote Rewards SaaS",
"tagline": "Hytale plugin + SaaS dashboard that unifies Webhook and Votifier across the 7 major vote lists. No port forwarding, automatic delivery, visual reward builder."
},
"gravity-flip": {
"title": "GravityFlip Region",
"tagline": "Drop a wand, set 2 corners, gravity flips inside the zone. Ceiling-walking and floating items live in 5 minutes of setup."
},
"chain-lightning": {
"title": "ChainLightning Sceptre",
"tagline": "Right-click a mob and the bolt jumps to up to 5 nearby enemies within 8 blocks. Damage falls off per hop, JUnit-tested chain resolver."
},
"async": {
"title": "Async — Kotlin coroutines for Hytale ECS",
"tagline": "One suspending call replaces CompletableFuture + world.execute boilerplate. Player/world/plugin scopes, three dispatchers, automatic cancellation on disconnect."
}
},
"recentArticles": {
"title": "Recent articles",
"subtitle": "Latest writing on Hytale plugin development",
+14 -91
View File
@@ -9,16 +9,15 @@
},
"footer": {
"copyright": "© 2026 Killian' DAL-CIN",
"tagline": "Hytale Plugin Developer & Dev Web Freelance. Plugins Java sur-mesure, sites pour serveurs gaming, applications Vue/Nuxt en production.",
"navigation": "Liens Rapides",
"services": "Services",
"legalNotices": "Mentions Légales",
"privacyPolicy": "Politique de Confidentialité",
"servicesList": {
"hytalePlugins": "Plugins Hytale (Java)",
"webDev": "Sites & Apps Web",
"retainer": "Retainer Mensuel",
"consulting": "Conseil Tech"
"webDev": "Développement Web",
"mobileApps": "Applications Mobiles",
"apiBackend": "Développement API",
"consulting": "Consulting Tech"
}
},
"a11y": {
@@ -67,20 +66,20 @@
"contact": "Me contacter"
},
"featuredProjects": {
"title": "Plugins Hytale & Apps Web qui tournent en prod",
"subtitle": "Portfolio de projets réels, en production, utilisés par de vrais joueurs et clients. Plugins Hytale Java 25, applications Vue.js/Nuxt, SaaS, bots Discord — pas du proof-of-concept, du shipping.",
"title": "Applications Web Qui Cartonnent",
"subtitle": "Portfolio de projets réels qui ont transformé des idées en succès. Applications Vue.js ultra-rapides, plateformes React scalables, API Node.js robustes.",
"viewAll": "Explorer Tous les Projets"
},
"services": {
"title": "Services Premium Hytale & Web",
"subtitle": "Plugins Hytale custom, applications web haute performance, SaaS gaming. Stack Java 25 + Vue/Nuxt + Node.js. Tarifs transparents (149€-790€), devis sous 24h.",
"hytalePlugins": {
"title": "Plugins Hytale Custom (Java)",
"description": "Du plugin essentiel au système MMO complet. Wand-based regions, votes & rewards, économie, quêtes, mini-jeux. Stack Java 25 + Hytale Plugin API + Gradle Shadow."
},
"title": "Services Premium de Développement Web",
"subtitle": "Solutions clés en main qui boostent votre croissance. Technologies de pointe + méthodologie éprouvée = succès garanti pour votre projet digital.",
"webDev": {
"title": "Applications Web Vue.js / Nuxt / React",
"description": "Sites pour serveurs gaming, SaaS, e-commerce. SSR Nuxt 4 SEO-optimisé, dashboards admin, intégrations Tebex/Discord. Lighthouse 95+, chargement <2s."
"title": "Applications Web Vue.js/React Sur-Mesure",
"description": "Création d'applications web lightning-fast qui convertissent. SPA modernes, PWA offline-first, e-commerce haute conversion. SEO-friendly dès la conception."
},
"mobileApps": {
"title": "Apps Mobiles Cross-Platform Rentables",
"description": "Une seule codebase = iOS + Android + Web. React Native pour des apps natives performantes. 60% d'économie vs développement natif."
},
"optimization": {
"title": "Optimisation Performance & SEO Technique",
@@ -113,9 +112,6 @@
"subtitle": "Parcourez mes projets : plugins Hytale, applications Vue.js, sites React, API Node.js, bots Discord et solutions d'entreprise.",
"categories": {
"all": "Tous les Projets",
"hytaleplugin": "Plugin Hytale",
"hytalelibrary": "Librairie Hytale",
"minecraftmod": "Mod Minecraft",
"webdevelopment": "Développement Web",
"botdevelopment": "Développement de Bot",
"opensource": "Open Source",
@@ -253,55 +249,6 @@
}
},
"projectData": {
"votepipe": {
"title": "VotePipe — SaaS Vote Rewards Hytale",
"description": "Plateforme SaaS unifiée qui combine Webhook (V1 RSA, V2 HMAC) et Votifier pour récupérer les votes des 7 grandes listes Hytale en un seul plugin. Visual reward builder, livraison automatique, aucun port à ouvrir.",
"longDescription": "Le seul plugin Hytale qui fait passer Webhook et Votifier dans un pipeline unifié. Tarifs Free / Pro / Network avec dashboard web (app.votepipe.com), reward builder visuel, streaks, milestones, lucky tiers. Stack : Java 25 plugin + backend TypeScript + dashboard SaaS. Architecture cloud sécurisée — outbound-only.",
"buttons": {
"website": "Site Officiel",
"modtale": "Modtale",
"curseforge": "CurseForge",
"documentation": "Documentation"
}
},
"gravity-flip": {
"title": "GravityFlip — Régions Anti-Gravité Hytale",
"description": "Plugin Hytale qui crée des zones d'anti-gravité custom avec un wand in-game. Marche sur le plafond, items qui flottent, mobs flottants — tout configurable sans toucher aux fichiers.",
"longDescription": "Wand-based region builder pour serveurs Hytale. Les corners se définissent en clic gauche/droit, persistance JSON automatique, tick loop 10x/sec, force verticale et grace period configurables. Mode visuel outline / particles / hidden. Construit sur Hytale Plugin API + Java 25 + Gradle Shadow.",
"buttons": {
"modtale": "Modtale",
"curseforge": "CurseForge"
}
},
"async": {
"title": "Async — Coroutines pour l'ECS per-world de Hytale",
"description": "Bibliothèque Kotlin qui remplace le pattern CompletableFuture + world.execute par un seul appel suspending. Scopes player/world/plugin, trois dispatchers, DSL ECS suspending.",
"longDescription": "Async résout le modèle thread per-world de Hytale : chaque monde tourne sur son thread, toucher un composant ailleurs throw, et un I/O bloquant sur le thread world freeze tous les joueurs. La lib expose des dispatchers (World, HytaleIO, Scheduled), des registres de scopes (PlayerScopes, WorldScopes, PluginScopes) avec annulation automatique au disconnect, et un DSL read/modify suspending. Construit en Kotlin 2.2, cible JVM 24, split modulaire (core / ecs / binding / dist) pour garder la logique testable sans serveur Hytale.",
"buttons": {
"modtale": "Modtale",
"curseforge": "CurseForge",
"github": "GitHub",
"gitea": "Gitea"
}
},
"chain-lightning": {
"title": "ChainLightning Sceptre — Wand Magique Hytale",
"description": "Plugin Hytale qui lance un éclair en chaîne au clic droit — le bolt rebondit sur jusqu'à 5 ennemis proches dans un rayon de 8 blocs, avec damage falloff par hop et un cooldown de 4 secondes.",
"longDescription": "Sceptre magique pour serveurs Hytale. Chain resolver pur Java découplé de Hytale via petites interfaces (RayCaster, EntitySource, ChainEntity), testé en JUnit 5 sans serveur. Construit sur Hytale Plugin API + Java 25 + Gradle Shadow.",
"buttons": {
"modtale": "Modtale",
"curseforge": "CurseForge"
}
},
"playhours": {
"title": "PlayHours — Forge Server Hours Enforcement",
"description": "Mod Forge 1.20.1 qui force des horaires d'ouverture par jour, blocage de connexion hors heures, warns 15/10/5/1 min, auto-kick à la fermeture, gestion des jours fériés, whitelist/blacklist, force modes, intégration LuckPerms.",
"longDescription": "Mod serveur Minecraft pour gérer l'accès aux horaires : schedules per-day, midnight-spanning, exceptions de dates, MOTD dynamique, multi-langues (EN/FR), permissions LuckPerms ou ops vanilla. Parfait pour serveurs scolaires, familiaux, ou avec maintenance windows.",
"buttons": {
"curseforge": "CurseForge",
"repository": "Dépôt"
}
},
"virtual-tour": {
"title": "Visite Virtuelle - Expérience 360° Interactive",
"description": "Mon professeur de lycée et moi avons eu l'idée de créer une visite virtuelle avec des vidéos 360° pour permettre à tous de visiter l'école depuis le web.",
@@ -524,30 +471,6 @@
"hourly": "Hors packages : 45€/h HT, minimum 1h (spot fixes, audits).",
"flagshipCta": "Flagship : sur devis après cadrage de 30 minutes."
},
"demos": {
"label": "// live-demos",
"title": "Plugins live, code en production",
"subtitle": "Pas de promesses marketing. Voici les plugins Hytale que je maintiens publiquement, utilisables aujourd'hui sur n'importe quel serveur Hytale.",
"featured": "À la une",
"viewSite": "Voir le site",
"footnote": "Chaque plugin est build maison, testé en production, et livré avec sa documentation.",
"votepipe": {
"title": "VotePipe — SaaS Vote Rewards",
"tagline": "Plugin Hytale + dashboard SaaS qui unifie Webhook et Votifier sur les 7 grandes listes de votes. Sans port à ouvrir, livraison automatique, reward builder visuel."
},
"gravity-flip": {
"title": "GravityFlip Region",
"tagline": "Pose un wand, définis 2 corners, gravité inversée dans la zone. Marche-au-plafond et items qui flottent en 5 minutes de setup."
},
"chain-lightning": {
"title": "ChainLightning Sceptre",
"tagline": "Clic droit sur un mob et l'éclair rebondit sur jusqu'à 5 ennemis dans un rayon de 8 blocs. Damage falloff par hop, chain resolver JUnit-testé."
},
"async": {
"title": "Async — Coroutines Kotlin pour l'ECS Hytale",
"tagline": "Un seul appel suspending remplace le boilerplate CompletableFuture + world.execute. Scopes player/world/plugin, trois dispatchers, annulation auto au disconnect."
}
},
"recentArticles": {
"title": "Articles récents",
"subtitle": "Les dernières publications sur le développement de plugins Hytale",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 915 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 322 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

-5
View File
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
<rect width="500" height="500" style="fill: rgb(12, 17, 29);"/>
<text style="fill: rgb(136, 172, 217); font-family: &quot;Microsoft Sans Serif&quot;; font-size: 411.6px; font-weight: 700; white-space: pre;" x="111.729" y="396.341">V</text>
</svg>

Before

Width:  |  Height:  |  Size: 352 B