chati-dev 4.2.1 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/README.md +80 -53
  2. package/bin/chati.js +150 -5
  3. package/framework/agents/build/dev.md +509 -76
  4. package/framework/agents/deploy/devops.md +40 -25
  5. package/framework/agents/discover/brief.md +156 -22
  6. package/framework/agents/discover/brownfield-wu.md +24 -14
  7. package/framework/agents/discover/greenfield-wu.md +100 -14
  8. package/framework/agents/plan/architect-data-engineer.md +6 -6
  9. package/framework/agents/plan/architect-system.md +46 -12
  10. package/framework/agents/plan/architect.md +40 -20
  11. package/framework/agents/plan/detail.md +36 -24
  12. package/framework/agents/plan/phases.md +36 -26
  13. package/framework/agents/plan/tasks.md +114 -33
  14. package/framework/agents/plan/ux-brand-architect.md +240 -8
  15. package/framework/agents/plan/ux-component-engineer.md +28 -13
  16. package/framework/agents/plan/ux-researcher.md +7 -6
  17. package/framework/agents/plan/ux.md +55 -22
  18. package/framework/agents/quality/qa-implementation.md +143 -74
  19. package/framework/agents/quality/qa-planning.md +115 -42
  20. package/framework/agents/quality/qa-visual.md +439 -0
  21. package/framework/agents/shared/visualizer.md +128 -0
  22. package/framework/config.yaml +8 -7
  23. package/framework/constitution.md +127 -43
  24. package/framework/context/governance.md +12 -7
  25. package/framework/context/quality.md +6 -5
  26. package/framework/context/root.md +6 -6
  27. package/framework/data/entity-registry.yaml +377 -4
  28. package/framework/data/model-limits.json +19 -0
  29. package/framework/domains/agents/qa-visual.yaml +74 -0
  30. package/framework/domains/constitution.yaml +46 -2
  31. package/framework/domains/workflows/greenfield-fullstack.yaml +2 -2
  32. package/framework/hooks/advance-trigger.js +131 -0
  33. package/framework/hooks/brief-validator.js +83 -0
  34. package/framework/hooks/constitution-guard.js +91 -36
  35. package/framework/hooks/license-guard.js +129 -190
  36. package/framework/hooks/mode-governance.js +68 -16
  37. package/framework/hooks/model-governance.js +19 -8
  38. package/framework/hooks/package.json +3 -0
  39. package/framework/hooks/post-dev.js +207 -0
  40. package/framework/hooks/prism-engine.js +288 -105
  41. package/framework/hooks/read-protection.js +37 -9
  42. package/framework/hooks/reasoning-escalator.js +371 -0
  43. package/framework/hooks/reference-trigger.js +117 -0
  44. package/framework/hooks/session-digest.js +94 -20
  45. package/framework/hooks/settings.json +32 -1
  46. package/framework/hooks/style-guard.js +52 -13
  47. package/framework/hooks/team-quality-gate.js +58 -25
  48. package/framework/hooks/undercover-guard.js +34 -13
  49. package/framework/i18n/en.yaml +3 -3
  50. package/framework/i18n/es.yaml +3 -3
  51. package/framework/i18n/fr.yaml +3 -3
  52. package/framework/i18n/pt.yaml +3 -3
  53. package/framework/intelligence/context-engine.md +4 -5
  54. package/framework/intelligence/decision-engine.md +1 -1
  55. package/framework/intelligence/hooks-performance.md +3 -3
  56. package/framework/migrations/v1.0-to-v1.1.yaml +1 -1
  57. package/framework/migrations/v1.4-to-v2.0.yaml +11 -11
  58. package/framework/migrations/v4.0-to-v4.1.yaml +2 -2
  59. package/framework/migrations/v4.2-to-v4.3.yaml +29 -0
  60. package/framework/orchestrator/chati-router.js +387 -0
  61. package/framework/orchestrator/chati-update.md +40 -40
  62. package/framework/orchestrator/chati.md +318 -115
  63. package/framework/scaffold/motion-premium/README.md +89 -0
  64. package/framework/scaffold/motion-premium/app/globals.css.template +400 -0
  65. package/framework/scaffold/motion-premium/app/layout.tsx.template +110 -0
  66. package/framework/scaffold/motion-premium/components/animation/BackgroundCrossfadeProvider.tsx.template +170 -0
  67. package/framework/scaffold/motion-premium/components/animation/LenisProvider.tsx.template +49 -0
  68. package/framework/scaffold/motion-premium/components/animation/PageTransitionWrapper.tsx.template +83 -0
  69. package/framework/scaffold/motion-premium/components/animation/Preloader.tsx.template +171 -0
  70. package/framework/scaffold/motion-premium/components/ui/Container.tsx.template +69 -0
  71. package/framework/scaffold/motion-premium/components/ui/PageSection.tsx.template +74 -0
  72. package/framework/scaffold/motion-premium/lib/animations/gsap.ts.template +112 -0
  73. package/framework/scaffold/motion-premium/lib/animations/refreshCoordinator.ts.template +75 -0
  74. package/framework/scaffold/motion-premium/lib/animations/tokens.ts.template +119 -0
  75. package/framework/scaffold/motion-premium/lib/animations/useGsapContext.ts.template +92 -0
  76. package/framework/scaffold/motion-premium/lib/animations/useScrollSnapStepper.ts.template +265 -0
  77. package/framework/scaffold/motion-premium/lib/animations/useSmoothScroll.ts.template +67 -0
  78. package/framework/scaffold/motion-premium/lib/brand.ts.template +43 -0
  79. package/framework/scaffold/motion-premium/scaffold.yaml +174 -0
  80. package/framework/scaffold/motion-premium-3d/README.md +80 -0
  81. package/framework/scaffold/motion-premium-3d/app/(3d)/scroll-demo/ScrollDemoCanvas.tsx.template +81 -0
  82. package/framework/scaffold/motion-premium-3d/app/(3d)/scroll-demo/ScrollDemoClient.tsx.template +75 -0
  83. package/framework/scaffold/motion-premium-3d/app/(3d)/scroll-demo/page.tsx.template +26 -0
  84. package/framework/scaffold/motion-premium-3d/components/3d/CameraRig.tsx.template +100 -0
  85. package/framework/scaffold/motion-premium-3d/components/3d/CanvasProvider.tsx.template +85 -0
  86. package/framework/scaffold/motion-premium-3d/components/3d/InvalidateOnScroll.tsx.template +51 -0
  87. package/framework/scaffold/motion-premium-3d/components/3d/MeshCrossfade.tsx.template +79 -0
  88. package/framework/scaffold/motion-premium-3d/components/3d/ScrollCrossfade.tsx.template +88 -0
  89. package/framework/scaffold/motion-premium-3d/components/3d/ScrollScene.tsx.template +121 -0
  90. package/framework/scaffold/motion-premium-3d/components/webgl/SceneFallback.tsx.template +65 -0
  91. package/framework/scaffold/motion-premium-3d/components/webgl/WebGLContext.tsx.template +68 -0
  92. package/framework/scaffold/motion-premium-3d/lib/webgl/detect.ts.template +69 -0
  93. package/framework/scaffold/motion-premium-3d/scaffold.yaml +133 -0
  94. package/framework/schemas/session.schema.json +109 -21
  95. package/framework/scripts/reference-capture.js +430 -0
  96. package/framework/scripts/visual-qa.js +674 -0
  97. package/framework/tasks/orchestrator-handoff.md +1 -1
  98. package/framework/tasks/orchestrator-resume.md +1 -1
  99. package/framework/tasks/orchestrator-route.md +1 -1
  100. package/framework/tasks/orchestrator-status.md +3 -3
  101. package/framework/tasks/qa-planning-gate-define.md +1 -1
  102. package/framework/templates/brandbook-html-tmpl.md +1 -1
  103. package/framework/templates/brandbook-tmpl.yaml +1 -1
  104. package/framework/templates/component-spec-tmpl.yaml +1 -1
  105. package/framework/templates/design-token-tmpl.yaml +1 -1
  106. package/framework/templates/icon-system-tmpl.yaml +1 -1
  107. package/framework/templates/team-planning-tasks.yaml +6 -5
  108. package/framework/workflows/brownfield-discovery.yaml +2 -2
  109. package/framework/workflows/brownfield-fullstack.yaml +15 -11
  110. package/framework/workflows/brownfield-service.yaml +14 -10
  111. package/framework/workflows/brownfield-ui.yaml +15 -11
  112. package/framework/workflows/greenfield-fullstack.yaml +16 -13
  113. package/framework/workflows/quick-flow.yaml +3 -3
  114. package/framework/workflows/standard-flow.yaml +12 -9
  115. package/package.json +10 -5
  116. package/src/autonomy/autonomous-gate.js +1 -0
  117. package/src/autonomy/build-state.js +1 -2
  118. package/src/autonomy/progress-reporter.js +1 -1
  119. package/src/config/agent-customizer.js +11 -3
  120. package/src/config/claude-settings-generator.js +226 -0
  121. package/src/config/context-file-generator.js +41 -21
  122. package/src/config/framework-adapter.js +1 -0
  123. package/src/config/gemini-hooks-generator.js +76 -6
  124. package/src/config/mcp-configs.js +1 -0
  125. package/src/context/layers/l1-global.js +2 -1
  126. package/src/dashboard/data-reader.js +4 -3
  127. package/src/dashboard/layout.js +2 -1
  128. package/src/decision/analyzer.js +6 -30
  129. package/src/decision/engine.js +4 -28
  130. package/src/decision/registry-healer.js +3 -2
  131. package/src/decision/registry-updater.js +23 -14
  132. package/src/extensions/loader.js +2 -8
  133. package/src/gates/g1-planning-complete.js +2 -1
  134. package/src/gates/g2-qa-planning.js +2 -1
  135. package/src/gates/g3-implementation.js +2 -1
  136. package/src/gates/g4-qa-implementation.js +3 -2
  137. package/src/gates/g5-deploy-ready.js +2 -1
  138. package/src/health/engine.js +4 -3
  139. package/src/installer/core.js +557 -81
  140. package/src/installer/preflight.js +131 -0
  141. package/src/installer/provider-overlay.js +3 -3
  142. package/src/installer/scaffold-applier.js +358 -0
  143. package/src/installer/templates.js +52 -31
  144. package/src/installer/validator.js +17 -12
  145. package/src/intelligence/registry-manager.js +22 -29
  146. package/src/intelligence/timeline.js +11 -6
  147. package/src/license/commands.js +1 -1
  148. package/src/license/wait.js +102 -0
  149. package/src/memory/agent-memory.js +81 -0
  150. package/src/memory/dream.js +32 -1
  151. package/src/merger/replace-merger.js +28 -15
  152. package/src/orchestrator/agent-selector.js +2 -1
  153. package/src/orchestrator/cli.js +1908 -80
  154. package/src/orchestrator/doctor.js +270 -0
  155. package/src/orchestrator/handoff-engine.js +4 -3
  156. package/src/orchestrator/index.js +2 -0
  157. package/src/orchestrator/pipeline-manager.js +306 -15
  158. package/src/orchestrator/session-manager.js +331 -6
  159. package/src/tasks/handoff.js +3 -2
  160. package/src/telemetry/config.js +4 -3
  161. package/src/telemetry/schema.js +1 -0
  162. package/src/terminal/collector.js +3 -2
  163. package/src/terminal/index.js +1 -2
  164. package/src/terminal/isolation.js +52 -18
  165. package/src/terminal/prompt-builder.js +42 -25
  166. package/src/terminal/run-parallel.js +1 -1
  167. package/src/terminal/run-team.js +3 -3
  168. package/src/terminal/team-task-list.js +43 -4
  169. package/src/upgrade/backup.js +3 -2
  170. package/src/upgrade/checker.js +3 -2
  171. package/src/upgrade/migrator.js +65 -7
  172. package/src/upgrade/tracked-files-detector.js +86 -0
  173. package/src/upgrade/user-messages.js +94 -0
  174. package/src/utils/config-parser.js +2 -1
  175. package/src/utils/feature-flags.js +2 -1
  176. package/src/utils/flatten-entities.js +69 -0
  177. package/src/utils/framework-dir.js +16 -0
  178. package/src/utils/model-id.js +85 -0
  179. package/src/utils/provider-limits.js +84 -23
  180. package/src/utils/schema-validator.js +1 -1
  181. package/src/wizard/i18n.js +5 -4
  182. package/src/wizard/index.js +14 -0
  183. package/assets/logo - c/303/263pia.png +0 -0
  184. package/assets/logo.svg +0 -42
  185. package/assets/logo2.png +0 -0
  186. package/assets/social-preview.png +0 -0
  187. package/scripts/bundle-framework.js +0 -69
  188. package/scripts/changelog-generator.js +0 -222
  189. package/scripts/codebase-mapper.js +0 -728
  190. package/scripts/commit-message-generator.js +0 -167
  191. package/scripts/coverage-analyzer.js +0 -260
  192. package/scripts/dependency-analyzer.js +0 -280
  193. package/scripts/doctor/checks/agents.js +0 -77
  194. package/scripts/doctor/checks/constitution.js +0 -41
  195. package/scripts/doctor/checks/domain-alignment.js +0 -58
  196. package/scripts/doctor/checks/prism-layers.js +0 -84
  197. package/scripts/doctor/checks/registry.js +0 -55
  198. package/scripts/doctor/checks/schemas.js +0 -61
  199. package/scripts/doctor/fixes/reference-fix.js +0 -100
  200. package/scripts/doctor/fixes/registry-fix.js +0 -56
  201. package/scripts/doctor/index.js +0 -212
  202. package/scripts/framework-analyzer.js +0 -308
  203. package/scripts/generate-constitution-domain.js +0 -253
  204. package/scripts/generate-signing-key.js +0 -33
  205. package/scripts/health-check.js +0 -481
  206. package/scripts/ide-sync.js +0 -326
  207. package/scripts/performance-analyzer.js +0 -325
  208. package/scripts/plan-tracker.js +0 -278
  209. package/scripts/populate-entity-registry.js +0 -481
  210. package/scripts/pr-review.js +0 -317
  211. package/scripts/rollback-manager.js +0 -310
  212. package/scripts/semantic-lint.js +0 -328
  213. package/scripts/sign-manifest.js +0 -53
  214. package/scripts/stuck-detector.js +0 -343
  215. package/scripts/test-quality-assessment.js +0 -257
  216. package/scripts/validate-agents.js +0 -368
  217. package/scripts/validate-package.js +0 -367
  218. package/scripts/validate-tasks.js +0 -465
  219. package/src/autonomy/worktree-manager.js +0 -250
  220. package/src/intelligence/decision-engine.js +0 -256
  221. package/src/intelligence/document-sharder.js +0 -221
  222. package/src/intelligence/elicitation.js +0 -265
@@ -0,0 +1,67 @@
1
+ /**
2
+ * lib/animations/useSmoothScroll.ts — scaffold motion-premium v1.0.0
3
+ *
4
+ * Initializes Lenis + GSAP integration exactly once per app lifecycle.
5
+ * Must be mounted in a top-level client component (phase 3: LenisProvider).
6
+ *
7
+ * Non-obvious choices:
8
+ *
9
+ * - `destroyed` flag closure + check inside `.then` — React 19 + strict mode
10
+ * double-mount problem. `initLenis()` is async (dynamic import of 'lenis').
11
+ * If the component mounts, unmounts, and remounts quickly (strict mode does
12
+ * this on first render in dev), the first .then callback may resolve AFTER
13
+ * the first unmount ran. Without the flag, the stale instance gets written
14
+ * to lenisRef.current and the cleanup function for the first mount destroys
15
+ * the NEW (second mount's) instance. Flag + immediate `lenis.destroy()` in
16
+ * the `if (destroyed)` branch guarantees at most one live Lenis, always.
17
+ *
18
+ * - `useRef<Awaited<ReturnType<typeof initLenis>> | null>` — typing the ref
19
+ * as the awaited return of the async function avoids duplicating the Lenis
20
+ * type import here. If `initLenis` is refactored later, the ref type
21
+ * follows automatically.
22
+ *
23
+ * - Returns the ref, not the instance — consumers (LenisProvider) can pass
24
+ * the ref through React context without re-rendering when the internal
25
+ * Lenis instance changes (refs don't trigger renders).
26
+ */
27
+
28
+ 'use client';
29
+
30
+ import { useEffect, useRef } from 'react';
31
+ import { initLenis } from './gsap';
32
+
33
+ /**
34
+ * Hook that initializes Lenis smooth scroll once per app lifecycle.
35
+ * Must be called in a top-level client component (e.g. LenisProvider in phase 3).
36
+ * Handles cleanup on unmount; race-safe under React strict mode.
37
+ *
38
+ * Returns a ref that holds the live Lenis instance, or null before init
39
+ * completes / after unmount.
40
+ */
41
+ export function useSmoothScroll() {
42
+ const lenisRef = useRef<Awaited<ReturnType<typeof initLenis>> | null>(null);
43
+
44
+ useEffect(() => {
45
+ let destroyed = false;
46
+
47
+ initLenis().then((lenis) => {
48
+ // Race guard: if the component already unmounted before Lenis finished
49
+ // loading (dynamic import), destroy the fresh instance immediately.
50
+ if (destroyed) {
51
+ lenis.destroy();
52
+ return;
53
+ }
54
+ lenisRef.current = lenis;
55
+ });
56
+
57
+ return () => {
58
+ destroyed = true;
59
+ if (lenisRef.current) {
60
+ lenisRef.current.destroy();
61
+ lenisRef.current = null;
62
+ }
63
+ };
64
+ }, []);
65
+
66
+ return lenisRef;
67
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Brand tokens — single source of truth for project colors.
3
+ *
4
+ * Scaffold-applied file; user-owned (protected from upgrades via hash diff
5
+ * in .chati/manifest.json). Edit freely.
6
+ *
7
+ * To re-sync these values into app/globals.css (the CSS custom properties
8
+ * consumed by Tailwind v4), run:
9
+ *
10
+ * npx chati-dev scaffold motion-premium --sync-brand
11
+ *
12
+ * The scaffold-applier reads this file, resolves each placeholder in
13
+ * globals.css.template (e.g. {{PRIMARY_HEX}}), and rewrites only the brand
14
+ * --color-* var block. Your spacing/radius/shadow customizations are preserved.
15
+ *
16
+ * See scaffold/motion-premium/scaffold.yaml for the full placeholder map.
17
+ */
18
+
19
+ export const brand = {
20
+ colors: {
21
+ /** Primary brand color (hex). Used for CTAs, focus rings, primary actions. */
22
+ primary: '{{PRIMARY_HEX}}',
23
+ /** Primary as "R, G, B" triple — required for rgba(var(--color-primary-rgb), alpha) constructs in shadows. */
24
+ primaryRgb: '{{PRIMARY_RGB}}',
25
+ /** Primary hover state (usually lighter/more saturated). */
26
+ primaryHover: '{{PRIMARY_HOVER_HEX}}',
27
+ /** Deepest background layer — page body default. */
28
+ bgDeep: '{{BG_DEEP_HEX}}',
29
+ /** Primary background level — main content surface. */
30
+ bgPrimary: '{{BG_PRIMARY_HEX}}',
31
+ /** Elevated background — cards, modals, panels. */
32
+ bgElevated: '{{BG_ELEVATED_HEX}}',
33
+ /** Primary text color — high-contrast copy. */
34
+ textPrimary: '{{TEXT_PRIMARY_HEX}}',
35
+ /** Secondary text — supporting copy, metadata. */
36
+ textSecondary: '{{TEXT_SECONDARY_HEX}}',
37
+ /** Inverse text — sits on primary-colored backgrounds (buttons etc). */
38
+ textInverse: '{{TEXT_INVERSE_HEX}}',
39
+ },
40
+ } as const;
41
+
42
+ export type Brand = typeof brand;
43
+ export type BrandColor = keyof typeof brand.colors;
@@ -0,0 +1,174 @@
1
+ # Scaffold Premium — motion-premium v1.0.0
2
+ #
3
+ # Declarative manifest for the motion-premium scaffold preset. Consumed by
4
+ # the scaffold-applier at apply time (Phase 4) to resolve placeholders and
5
+ # emit files into the target project.
6
+ #
7
+ # Contract: this schema is STABLE within v1.x. Additions only (new optional
8
+ # fields). Breaking changes bump the major version and provide migrations.
9
+
10
+ id: motion-premium
11
+ version: 1.0.0
12
+ description: "Premium animation infrastructure: GSAP + Lenis + cinematic providers. Opinionated for Next.js / React 18+ / Tailwind v4."
13
+
14
+ framework:
15
+ chati_dev: ">=4.3.0"
16
+
17
+ stack:
18
+ build_tools: ["nextjs", "react-vite"]
19
+ tailwind: ">=4.0.0"
20
+ typescript: ">=5.0.0"
21
+ node: ">=18.0.0"
22
+ react: ">=18.0.0"
23
+ # Runtime peer deps imported directly by templates. Bumping policy: every two
24
+ # minor bumps, re-validate templates. GSAP 3.12 is the stable line post-3.11;
25
+ # framer-motion 11 is the React 19 support line; lenis 1.1 introduced the
26
+ # `wrapper`/`content` refactor templates rely on.
27
+ gsap: "^3.12.5"
28
+ "framer-motion": "^11.0.0"
29
+ lenis: "^1.1.0"
30
+
31
+ # Placeholder resolution — keys reference brand source fields.
32
+ # Resolved once at scaffold apply time (Decision 3: install-time, one-shot).
33
+ # To re-sync CSS vars after editing brand.ts, run: scaffold motion-premium --sync-brand
34
+ placeholders:
35
+ PRIMARY_HEX:
36
+ source: brand.colors.primary
37
+ default: "#7C3AED"
38
+ type: hex
39
+ PRIMARY_RGB:
40
+ source: brand.colors.primaryRgb
41
+ default: "124, 58, 237"
42
+ type: rgb_triple
43
+ PRIMARY_HOVER_HEX:
44
+ source: brand.colors.primaryHover
45
+ default: "#6D28D9"
46
+ type: hex
47
+ BG_DEEP_HEX:
48
+ source: brand.colors.bgDeep
49
+ default: "#0A0A0F"
50
+ type: hex
51
+ BG_PRIMARY_HEX:
52
+ source: brand.colors.bgPrimary
53
+ default: "#111118"
54
+ type: hex
55
+ BG_ELEVATED_HEX:
56
+ source: brand.colors.bgElevated
57
+ default: "#1A1A24"
58
+ type: hex
59
+ TEXT_PRIMARY_HEX:
60
+ source: brand.colors.textPrimary
61
+ default: "#FAFAFA"
62
+ type: hex
63
+ TEXT_SECONDARY_HEX:
64
+ source: brand.colors.textSecondary
65
+ default: "#A1A1AA"
66
+ type: hex
67
+ TEXT_INVERSE_HEX:
68
+ source: brand.colors.textInverse
69
+ default: "#0A0A0F"
70
+ type: hex
71
+
72
+ # Brand source resolution priority (Decision 3). First match wins.
73
+ brand_sources:
74
+ - path: lib/brand.ts
75
+ type: typescript
76
+ description: "Existing brand tokens (brownfield or post-F3 scaffold install)"
77
+ - path: artifacts/4-UX/brandbook.md
78
+ type: markdown
79
+ description: "UX-authored brandbook (greenfield, post-UX phase)"
80
+ - path: artifacts/4-UX/brandbook.yaml
81
+ type: yaml
82
+ description: "Alternate brandbook format"
83
+ - type: interactive
84
+ prompt: true
85
+ description: "Fallback: ask user for each placeholder with default hints"
86
+
87
+ # Files shipped by this scaffold. Phase 1: foundation (2). Phase 2: animations (6). Phase 3: providers (5). Phase 4: ui contract (2). Total: 15.
88
+ files:
89
+ - source: app/globals.css.template
90
+ target: app/globals.css
91
+ phase: foundation
92
+ override: partial
93
+ notes: "Brand --color-* vars auto-synced from brand.ts; spacing/radius/shadow user-editable."
94
+ - source: lib/brand.ts.template
95
+ target: lib/brand.ts
96
+ phase: foundation
97
+ override: full
98
+ notes: "User-owned. Protected from upgrades via hash diff in manifest.json."
99
+ - source: lib/animations/gsap.ts.template
100
+ target: lib/animations/gsap.ts
101
+ phase: animations
102
+ override: none
103
+ notes: "Module-level plugin registration + Lenis/GSAP integration. Scaffold-owned — the 13 sauce patterns live here."
104
+ - source: lib/animations/tokens.ts.template
105
+ target: lib/animations/tokens.ts
106
+ phase: animations
107
+ override: partial
108
+ notes: "Adjust values, keep the exported shape (EASINGS/EASING_CURVES/DURATIONS/STAGGERS/SCROLL_TRIGGER_DEFAULTS)."
109
+ - source: lib/animations/useGsapContext.ts.template
110
+ target: lib/animations/useGsapContext.ts
111
+ phase: animations
112
+ override: none
113
+ notes: "Scoped context + reduced-motion safeguard + ctx.revert cleanup. Core pattern — do not modify."
114
+ - source: lib/animations/useSmoothScroll.ts.template
115
+ target: lib/animations/useSmoothScroll.ts
116
+ phase: animations
117
+ override: none
118
+ notes: "Race-guarded async Lenis init. Used by LenisProvider in phase 3."
119
+ - source: lib/animations/useScrollSnapStepper.ts.template
120
+ target: lib/animations/useScrollSnapStepper.ts
121
+ phase: animations
122
+ override: none
123
+ notes: "Wheel accumulator + touch + entry cooldown for panel-snap pins. Imported into components that use pinned sequences."
124
+ - source: lib/animations/refreshCoordinator.ts.template
125
+ target: lib/animations/refreshCoordinator.ts
126
+ phase: animations
127
+ override: none
128
+ notes: "Debounced ScrollTrigger.refresh() singleton. Coalesces mount-burst refreshes."
129
+ - source: components/animation/LenisProvider.tsx.template
130
+ target: components/animation/LenisProvider.tsx
131
+ phase: providers
132
+ override: none
133
+ notes: "Root client component for Lenis init + scrollRestoration manual enforcement."
134
+ - source: components/animation/BackgroundCrossfadeProvider.tsx.template
135
+ target: components/animation/BackgroundCrossfadeProvider.tsx
136
+ phase: providers
137
+ override: partial
138
+ notes: "User customizes color stops via props; ScrollTrigger logic is scaffold-owned."
139
+ - source: components/animation/PageTransitionWrapper.tsx.template
140
+ target: components/animation/PageTransitionWrapper.tsx
141
+ phase: providers
142
+ override: none
143
+ notes: "Framer Motion opacity fade on pathname change + post-exit scroll reset."
144
+ - source: components/animation/Preloader.tsx.template
145
+ target: components/animation/Preloader.tsx
146
+ phase: providers
147
+ override: partial
148
+ notes: "User supplies children (logo slot), videoSelector, durations; readiness gate + RAF are scaffold-owned."
149
+ - source: app/layout.tsx.template
150
+ target: app/layout.tsx
151
+ phase: providers
152
+ override: partial
153
+ notes: "Scaffold ships provider nesting + pre-hydration scroll/motion scripts. User fills metadata, fonts, chrome, analytics."
154
+ - source: components/ui/Container.tsx.template
155
+ target: components/ui/Container.tsx
156
+ phase: ui-contract
157
+ override: none
158
+ notes: "@contract: sole authority for max-width + horizontal padding. Never edit."
159
+ - source: components/ui/PageSection.tsx.template
160
+ target: components/ui/PageSection.tsx
161
+ phase: ui-contract
162
+ override: none
163
+ notes: "Vertical rhythm wrapper. noPadding / noContainer escape hatches for heroes and full-bleed layouts."
164
+
165
+ # Contract — principles this scaffold encodes.
166
+ # Violating any of these during dev breaks the premium guarantees.
167
+ principles:
168
+ - "One Container authority — components/ui/Container.tsx is the sole max-width + horizontal padding owner (Phase 4)"
169
+ - "Fluid type from day one — Utopia clamp scale across all text-* tokens, no retrofit"
170
+ - "xl breakpoint overridden to 75rem — eliminates the 1200–1279px dead zone (MacBook Air M4)"
171
+ - "Mobile-first stack; promote to desktop via matchMedia gates, not CSS-only"
172
+ - "Reduced motion first-class — @media (prefers-reduced-motion) + data-gsap-target safety net"
173
+ - "Single RAF loop — gsap.ticker drives Lenis (Phase 2)"
174
+ - "Scope isolation via gsap.context(fn, containerRef) (Phase 2)"
@@ -0,0 +1,80 @@
1
+ # motion-premium-3d
2
+
3
+ React Three Fiber + Three.js scroll-driven 3D layer for premium sites. Extends `motion-premium` — apply that one FIRST (the 2D base), then this one on top.
4
+
5
+ ## When to apply
6
+
7
+ Apply when the project brief or Animation Inventory cites **explicit 3D intent**:
8
+
9
+ - Named 3D reference sites (Oryzo camera rig, Igloo Inc scenes, many Awwwards winners).
10
+ - Phrase-level signals: "scroll-synced 3D hero", "camera pull", "product rotation", "scroll-driven WebGL", "interactive scene".
11
+ - Animation Inventory rows whose "Reproducible via" column points at `scaffold/motion-premium-3d/*`.
12
+
13
+ The Fase 10 auto-gate fires for this preset when `scaffold_signals.motion_premium_3d.confidence >= 0.8` (higher threshold than 2D motion-premium's 0.7 per ADR-3D-07 — false positives here cost ~600KB of bundle pressed onto a project that did not need it).
14
+
15
+ ## When NOT to apply
16
+
17
+ - 2D-only projects. Apply motion-premium alone.
18
+ - Mobile-first experiences where GPU budget is the primary constraint. The WebGL fallback (ADR-3D-05) handles low-end cases, but you pay the detection cost everywhere.
19
+ - Static marketing pages without scroll choreography.
20
+ - Projects on providers other than Claude Code where R3F's peer-dep matrix is hard to verify at install.
21
+
22
+ ## What it ships
23
+
24
+ 12 templates across 4 phases (webgl / canvas / scroll-scene / demo). Final inventory:
25
+
26
+ ```
27
+ lib/webgl/detect.ts.template
28
+ components/webgl/WebGLContext.tsx.template
29
+ components/webgl/SceneFallback.tsx.template
30
+ components/3d/CanvasProvider.tsx.template
31
+ components/3d/InvalidateOnScroll.tsx.template
32
+ components/3d/ScrollScene.tsx.template
33
+ components/3d/CameraRig.tsx.template
34
+ components/3d/ScrollCrossfade.tsx.template
35
+ components/3d/MeshCrossfade.tsx.template
36
+ app/(3d)/scroll-demo/page.tsx.template
37
+ app/(3d)/scroll-demo/ScrollDemoClient.tsx.template
38
+ app/(3d)/scroll-demo/ScrollDemoCanvas.tsx.template
39
+ ```
40
+
41
+ All 12 entries match `scaffold.yaml` `files[]` exactly. All target paths are DISJOINT from motion-premium. The two presets compose.
42
+
43
+ ## Override classification
44
+
45
+ | File pattern | Override | Notes |
46
+ |---|---|---|
47
+ | `lib/webgl/*` | none | Scaffold-owned — detection logic is invariant |
48
+ | `components/webgl/WebGLContext` | none | Context shape consumed by every 3D template |
49
+ | `components/webgl/SceneFallback` | partial | Extend with children (headline/CTA); keep role/aria contract |
50
+ | `components/3d/CanvasProvider` | none | Single-RAF contract + WebGL gate — do not modify |
51
+ | `components/3d/InvalidateOnScroll` | none | RAF bridge from gsap.ticker to R3F invalidate() |
52
+ | `components/3d/ScrollScene` | none | Pinned section + progress ref — scaffold-owned |
53
+ | `components/3d/CameraRig` | partial | User supplies keyframes[] via props; lerp internals fixed |
54
+ | `components/3d/ScrollCrossfade` | partial | Customise stops[] / at[] for uneven spacing |
55
+ | `components/3d/MeshCrossfade` | partial | Mesh geometry / materials are project-specific |
56
+ | `app/(3d)/scroll-demo/page` | partial | Route-group entry — customise or remove for production |
57
+ | `app/(3d)/scroll-demo/ScrollDemoClient` | partial | Client wrapper + overlay HTML — user extends |
58
+ | `app/(3d)/scroll-demo/ScrollDemoCanvas` | partial | Canvas subtree (CanvasProvider + InvalidateOnScroll + CameraRig + MeshCrossfade + Preload) |
59
+
60
+ ## ADRs governing this preset
61
+
62
+ - **ADR-3D-01** — Separate preset, not extension
63
+ - **ADR-3D-02** — R3F + drei, not vanilla Three.js
64
+ - **ADR-3D-03** — Single RAF via `frameloop="demand"`
65
+ - **ADR-3D-04** — `next/dynamic({ ssr: false })` for all Canvas templates
66
+ - **ADR-3D-05** — WebGL detection + graceful degradation
67
+ - **ADR-3D-06** — Peer-deps pinned by minor (three `^0.160`, R3F `^8.15`)
68
+ - **ADR-3D-07** — New auto-gate signal with confidence threshold 0.8
69
+ - **ADR-3D-08** — `prefers-reduced-motion: reduce` MUST render the fallback
70
+
71
+ Full context in `.planning/motion-premium-3d-v1.md`.
72
+
73
+ ## How to apply
74
+
75
+ ```
76
+ npx chati-dev scaffold motion-premium # 2D base first
77
+ npx chati-dev scaffold motion-premium-3d # 3D layer on top
78
+ ```
79
+
80
+ Or via the Fase 10 auto-gate after qa-planning, when signals align.
@@ -0,0 +1,81 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * ScrollDemoCanvas — dynamically imported via next/dynamic({ ssr: false })
5
+ * from ScrollDemoClient. Owns the Canvas subtree.
6
+ *
7
+ * Bundles:
8
+ * @react-three/fiber — Canvas, useThree, useFrame
9
+ * @react-three/drei — Preload (utility)
10
+ * three — Vector3, geometries, materials
11
+ * Rest of the app does not import these; the dynamic boundary isolates
12
+ * the ~600KB 3D bundle from pages that do not need it.
13
+ */
14
+
15
+ import { Preload } from '@react-three/drei';
16
+ import { CanvasProvider } from '@/components/3d/CanvasProvider';
17
+ import { InvalidateOnScroll } from '@/components/3d/InvalidateOnScroll';
18
+ import { CameraRig } from '@/components/3d/CameraRig';
19
+ import { MeshCrossfade } from '@/components/3d/MeshCrossfade';
20
+ import { SceneFallback } from '@/components/webgl/SceneFallback';
21
+
22
+ /**
23
+ * Keyframes for the demo camera path. Replace with your own keyframes
24
+ * when customising — CameraRig lerps position + lookAt + optional FOV.
25
+ */
26
+ const DEMO_KEYFRAMES = [
27
+ { at: 0, position: [0, 0, 8] as [number, number, number], lookAt: [0, 0, 0] as [number, number, number], fov: 50 },
28
+ { at: 0.5, position: [4, 2, 6] as [number, number, number], lookAt: [0, 0, 0] as [number, number, number], fov: 55 },
29
+ { at: 1, position: [0, 4, 4] as [number, number, number], lookAt: [0, 0, 0] as [number, number, number], fov: 60 },
30
+ ];
31
+
32
+ export default function ScrollDemoCanvas() {
33
+ return (
34
+ <CanvasProvider fallback={<SceneFallback fullBleed accessibleLabel="3D scene unavailable" />}>
35
+ <InvalidateOnScroll />
36
+
37
+ <ambientLight intensity={0.4} />
38
+ <directionalLight position={[5, 5, 5]} intensity={0.8} />
39
+
40
+ <CameraRig keyframes={DEMO_KEYFRAMES} />
41
+
42
+ {/*
43
+ MeshCrossfade: single slot; only the active variant mounts.
44
+ Replace primitives with your own glTF meshes (useGLTF from drei).
45
+ */}
46
+ <MeshCrossfade
47
+ variants={[
48
+ {
49
+ at: 0,
50
+ render: () => (
51
+ <mesh>
52
+ <boxGeometry args={[1.5, 1.5, 1.5]} />
53
+ <meshStandardMaterial color="#7C3AED" />
54
+ </mesh>
55
+ ),
56
+ },
57
+ {
58
+ at: 0.5,
59
+ render: () => (
60
+ <mesh>
61
+ <sphereGeometry args={[1, 32, 32]} />
62
+ <meshStandardMaterial color="#A78BFA" />
63
+ </mesh>
64
+ ),
65
+ },
66
+ {
67
+ at: 1,
68
+ render: () => (
69
+ <mesh>
70
+ <torusGeometry args={[1, 0.4, 16, 64]} />
71
+ <meshStandardMaterial color="#EDE9FE" />
72
+ </mesh>
73
+ ),
74
+ },
75
+ ]}
76
+ />
77
+
78
+ <Preload all />
79
+ </CanvasProvider>
80
+ );
81
+ }
@@ -0,0 +1,75 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * ScrollDemoClient — working example wiring together every P2-P4
5
+ * template. A single pinned scroll scene with:
6
+ * - WebGL gate (P2: WebGLContext + SceneFallback)
7
+ * - Canvas mount with single-RAF (P3: CanvasProvider +
8
+ * InvalidateOnScroll)
9
+ * - Scroll-driven camera (P4: CameraRig)
10
+ * - Mesh crossfade (P4: MeshCrossfade)
11
+ *
12
+ * Real-world scenes replace the box/sphere/torus primitives with
13
+ * glTF meshes loaded via drei's `useGLTF` (not imported here to keep
14
+ * the demo light; see motion-premium-3d README for asset pipeline
15
+ * guidance).
16
+ *
17
+ * Preload hint: drei's <Preload all /> tells R3F to compile all
18
+ * materials in the scene at mount time, eliminating the flash that
19
+ * otherwise happens when a new material appears mid-scroll.
20
+ */
21
+
22
+ import { useRef } from 'react';
23
+ import dynamic from 'next/dynamic';
24
+ import { Preload } from '@react-three/drei';
25
+ import { Container } from '@/components/ui/Container';
26
+ import { WebGLProvider } from '@/components/webgl/WebGLContext';
27
+ import { SceneFallback } from '@/components/webgl/SceneFallback';
28
+ import {
29
+ ScrollScene,
30
+ ScrollSceneContext,
31
+ type ScrollSceneRef,
32
+ } from '@/components/3d/ScrollScene';
33
+
34
+ // ADR-3D-04: R3F / Three.js dependencies must load client-only. Wrapping
35
+ // the scene in next/dynamic with ssr: false ensures Next never attempts
36
+ // to run Canvas code on the server, and the loading state paints the
37
+ // considered fallback instead of a blank canvas during hydration.
38
+ const ScrollDemoCanvas = dynamic(() => import('./ScrollDemoCanvas'), {
39
+ ssr: false,
40
+ loading: () => <SceneFallback fullBleed accessibleLabel="Loading scroll demo" />,
41
+ });
42
+
43
+ export default function ScrollDemoClient() {
44
+ const sceneRef = useRef<ScrollSceneRef>(null);
45
+
46
+ return (
47
+ <WebGLProvider>
48
+ <ScrollScene ref={sceneRef} length={3} scrub={true}>
49
+ <Container className="relative z-10 flex h-full flex-col justify-end pb-24">
50
+ <p className="text-sm uppercase tracking-wider opacity-60">motion-premium-3d demo</p>
51
+ <h1 className="text-display-lg max-w-2xl">
52
+ Scroll to animate the scene.
53
+ </h1>
54
+ </Container>
55
+
56
+ <div className="absolute inset-0">
57
+ <ScrollSceneContext.Provider value={sceneRef.current?.progress ?? null}>
58
+ <ScrollDemoCanvas />
59
+ </ScrollSceneContext.Provider>
60
+ </div>
61
+ </ScrollScene>
62
+ </WebGLProvider>
63
+ );
64
+ }
65
+
66
+ // ---------------------------------------------------------------------------
67
+ // Sub-components defined below are referenced only via next/dynamic above.
68
+ // They are exported from the Client module so the dynamic import is cheap
69
+ // (single chunk). In production demos, prefer a separate file for clearer
70
+ // code-split boundaries.
71
+ // ---------------------------------------------------------------------------
72
+
73
+ export function ScrollDemoCanvasInline() {
74
+ return <Preload all />;
75
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * /scroll-demo — demo route for motion-premium-3d.
3
+ *
4
+ * Server Component by design: metadata exports live here; the actual
5
+ * scroll-scene + Canvas tree live in the sibling Client Component
6
+ * (ScrollDemoClient). This separation keeps the route group
7
+ * "(3d)" URL-transparent while respecting the Next.js 16 rule that
8
+ * `next/dynamic({ ssr: false })` must be invoked from a Client boundary.
9
+ *
10
+ * Users typically remove this route after validating the scaffold on
11
+ * their own content. The `(3d)` route group ( -parens- ) means URLs
12
+ * stay at `/scroll-demo`; dropping the whole `app/(3d)/` folder is a
13
+ * one-step undo.
14
+ */
15
+
16
+ import type { Metadata } from 'next';
17
+ import ScrollDemoClient from './ScrollDemoClient';
18
+
19
+ export const metadata: Metadata = {
20
+ title: 'Scroll Demo — motion-premium-3d',
21
+ description: 'Scaffold demo: pinned scroll scene, camera rig tied to scroll progress, scene crossfade. Remove when validated.',
22
+ };
23
+
24
+ export default function ScrollDemoPage() {
25
+ return <ScrollDemoClient />;
26
+ }
@@ -0,0 +1,100 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * CameraRig — interpolates camera position, target, and optional FOV
5
+ * across keyframes driven by the ScrollSceneContext progress ref.
6
+ *
7
+ * Must mount inside <CanvasProvider> AND inside <ScrollScene>'s
8
+ * context. Reads progress on every frame via useFrame — zero React
9
+ * re-renders, no state library.
10
+ *
11
+ * Keyframe format: an array of { at: 0..1, position, lookAt, fov? }
12
+ * sorted by `at`. Between keyframes, linear interpolation (or custom
13
+ * easing via `ease` on the keyframe). Before the first/after the last,
14
+ * the camera clamps to that keyframe's pose.
15
+ *
16
+ * Usage:
17
+ * <CameraRig
18
+ * keyframes={[
19
+ * { at: 0, position: [0, 0, 8], lookAt: [0, 0, 0], fov: 50 },
20
+ * { at: 0.5, position: [4, 2, 6], lookAt: [0, 0, 0] },
21
+ * { at: 1, position: [0, 4, 3], lookAt: [0, 0, 0], fov: 60 },
22
+ * ]}
23
+ * />
24
+ */
25
+
26
+ import { useContext, useMemo } from 'react';
27
+ import { useThree, useFrame } from '@react-three/fiber';
28
+ import { Vector3, PerspectiveCamera as ThreePerspectiveCamera } from 'three';
29
+ import { ScrollSceneContext } from './ScrollScene';
30
+
31
+ export interface CameraKeyframe {
32
+ at: number; // 0..1
33
+ position: [number, number, number];
34
+ lookAt: [number, number, number];
35
+ fov?: number;
36
+ /** Custom easing on the segment ending at this keyframe (optional). */
37
+ ease?: (t: number) => number;
38
+ }
39
+
40
+ export interface CameraRigProps {
41
+ keyframes: CameraKeyframe[];
42
+ }
43
+
44
+ const DEFAULT_EASE = (t: number) => t; // linear; override per keyframe
45
+
46
+ export function CameraRig({ keyframes }: CameraRigProps) {
47
+ const progressRef = useContext(ScrollSceneContext);
48
+ const camera = useThree((s) => s.camera);
49
+
50
+ // Sorted copy + Vector3 presets — done once.
51
+ const kf = useMemo(() => {
52
+ const sorted = [...keyframes].sort((a, b) => a.at - b.at);
53
+ return sorted.map((k) => ({
54
+ at: k.at,
55
+ position: new Vector3(...k.position),
56
+ lookAt: new Vector3(...k.lookAt),
57
+ fov: k.fov,
58
+ ease: k.ease || DEFAULT_EASE,
59
+ }));
60
+ }, [keyframes]);
61
+
62
+ // Scratch vectors reused every frame (no per-frame allocation).
63
+ const posScratch = useMemo(() => new Vector3(), []);
64
+ const lookAtScratch = useMemo(() => new Vector3(), []);
65
+
66
+ useFrame(() => {
67
+ if (!progressRef || kf.length === 0) return;
68
+ const t = Math.max(0, Math.min(1, progressRef.current.progress));
69
+
70
+ // Find the bracketing keyframe pair.
71
+ let a = kf[0];
72
+ let b = kf[kf.length - 1];
73
+ for (let i = 0; i < kf.length - 1; i++) {
74
+ if (t >= kf[i].at && t <= kf[i + 1].at) {
75
+ a = kf[i];
76
+ b = kf[i + 1];
77
+ break;
78
+ }
79
+ }
80
+
81
+ const range = b.at - a.at;
82
+ const local = range === 0 ? 0 : (t - a.at) / range;
83
+ const eased = b.ease(local);
84
+
85
+ posScratch.lerpVectors(a.position, b.position, eased);
86
+ lookAtScratch.lerpVectors(a.lookAt, b.lookAt, eased);
87
+
88
+ camera.position.copy(posScratch);
89
+ camera.lookAt(lookAtScratch);
90
+
91
+ // FOV interpolation (only meaningful on perspective cameras).
92
+ if (a.fov != null && b.fov != null && (camera as ThreePerspectiveCamera).isPerspectiveCamera) {
93
+ const fov = a.fov + (b.fov - a.fov) * eased;
94
+ (camera as ThreePerspectiveCamera).fov = fov;
95
+ (camera as ThreePerspectiveCamera).updateProjectionMatrix();
96
+ }
97
+ });
98
+
99
+ return null;
100
+ }