@mozole/cli 1.4.2 → 2.0.0-b
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.
- package/LICENSE +16 -4
- package/README.md +85 -89
- package/dist/cli.d.ts +2 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +204 -157
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt.d.ts +5 -8
- package/dist/commands/adopt.d.ts.map +1 -1
- package/dist/commands/adopt.js +49 -812
- package/dist/commands/adopt.js.map +1 -1
- package/dist/commands/doctor.d.ts +1 -7
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +69 -377
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/new.d.ts +8 -10
- package/dist/commands/new.d.ts.map +1 -1
- package/dist/commands/new.js +32 -149
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/phase.d.ts +11 -0
- package/dist/commands/phase.d.ts.map +1 -0
- package/dist/commands/phase.js +106 -0
- package/dist/commands/phase.js.map +1 -0
- package/dist/commands/prototype.d.ts +6 -0
- package/dist/commands/prototype.d.ts.map +1 -0
- package/dist/commands/prototype.js +28 -0
- package/dist/commands/prototype.js.map +1 -0
- package/dist/commands/repomap.d.ts +6 -0
- package/dist/commands/repomap.d.ts.map +1 -0
- package/dist/commands/repomap.js +71 -0
- package/dist/commands/repomap.js.map +1 -0
- package/dist/commands/test.d.ts +6 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +115 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/ui.d.ts +1 -37
- package/dist/commands/ui.d.ts.map +1 -1
- package/dist/commands/ui.js +75 -317
- package/dist/commands/ui.js.map +1 -1
- package/dist/commands/verify.d.ts +12 -7
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +148 -65
- package/dist/commands/verify.js.map +1 -1
- package/dist/qa/a11y.d.ts +24 -0
- package/dist/qa/a11y.d.ts.map +1 -0
- package/dist/qa/a11y.js +124 -0
- package/dist/qa/a11y.js.map +1 -0
- package/dist/qa/breakpoints.d.ts +16 -0
- package/dist/qa/breakpoints.d.ts.map +1 -0
- package/dist/qa/breakpoints.js +45 -0
- package/dist/qa/breakpoints.js.map +1 -0
- package/dist/qa/contract.d.ts +21 -0
- package/dist/qa/contract.d.ts.map +1 -0
- package/dist/qa/contract.js +116 -0
- package/dist/qa/contract.js.map +1 -0
- package/dist/qa/index.d.ts +6 -0
- package/dist/qa/index.d.ts.map +1 -0
- package/dist/qa/index.js +6 -0
- package/dist/qa/index.js.map +1 -0
- package/dist/qa/probe.d.ts +48 -0
- package/dist/qa/probe.d.ts.map +1 -0
- package/dist/qa/probe.js +206 -0
- package/dist/qa/probe.js.map +1 -0
- package/dist/qa/runner.d.ts +27 -32
- package/dist/qa/runner.d.ts.map +1 -1
- package/dist/qa/runner.js +286 -336
- package/dist/qa/runner.js.map +1 -1
- package/dist/scaffold/agents.d.ts +5 -0
- package/dist/scaffold/agents.d.ts.map +1 -0
- package/dist/scaffold/agents.js +84 -0
- package/dist/scaffold/agents.js.map +1 -0
- package/dist/scaffold/backend.d.ts +6 -8
- package/dist/scaffold/backend.d.ts.map +1 -1
- package/dist/scaffold/backend.js +318 -199
- package/dist/scaffold/backend.js.map +1 -1
- package/dist/scaffold/design.d.ts +4 -3
- package/dist/scaffold/design.d.ts.map +1 -1
- package/dist/scaffold/design.js +38 -47
- package/dist/scaffold/design.js.map +1 -1
- package/dist/scaffold/flagship.d.ts +8 -0
- package/dist/scaffold/flagship.d.ts.map +1 -0
- package/dist/scaffold/flagship.js +337 -0
- package/dist/scaffold/flagship.js.map +1 -0
- package/dist/scaffold/index.d.ts +9 -0
- package/dist/scaffold/index.d.ts.map +1 -0
- package/dist/scaffold/index.js +48 -0
- package/dist/scaffold/index.js.map +1 -0
- package/dist/scaffold/phases.d.ts +10 -0
- package/dist/scaffold/phases.d.ts.map +1 -0
- package/dist/scaffold/phases.js +165 -0
- package/dist/scaffold/phases.js.map +1 -0
- package/dist/scaffold/repomap.d.ts +6 -0
- package/dist/scaffold/repomap.d.ts.map +1 -0
- package/dist/scaffold/repomap.js +89 -0
- package/dist/scaffold/repomap.js.map +1 -0
- package/dist/scaffold/standard.d.ts +13 -0
- package/dist/scaffold/standard.d.ts.map +1 -0
- package/dist/scaffold/standard.js +371 -0
- package/dist/scaffold/standard.js.map +1 -0
- package/dist/scaffold/tokens.d.ts +3 -0
- package/dist/scaffold/tokens.d.ts.map +1 -0
- package/dist/scaffold/tokens.js +126 -0
- package/dist/scaffold/tokens.js.map +1 -0
- package/dist/server/manager.d.ts +23 -0
- package/dist/server/manager.d.ts.map +1 -0
- package/dist/server/manager.js +96 -0
- package/dist/server/manager.js.map +1 -0
- package/dist/utils/ai-trace.d.ts +8 -0
- package/dist/utils/ai-trace.d.ts.map +1 -0
- package/dist/utils/ai-trace.js +69 -0
- package/dist/utils/ai-trace.js.map +1 -0
- package/dist/utils/browser.d.ts +10 -0
- package/dist/utils/browser.d.ts.map +1 -0
- package/dist/utils/browser.js +127 -0
- package/dist/utils/browser.js.map +1 -0
- package/dist/utils/fs.d.ts +11 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js +166 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/git.d.ts +9 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +69 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/process.d.ts +21 -0
- package/dist/utils/process.d.ts.map +1 -0
- package/dist/utils/process.js +120 -0
- package/dist/utils/process.js.map +1 -0
- package/dist/version.d.ts +21 -36
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +21 -44
- package/dist/version.js.map +1 -1
- package/package.json +19 -38
- package/CHANGELOG.md +0 -30
- package/dist/commands/cce.d.ts +0 -7
- package/dist/commands/cce.d.ts.map +0 -1
- package/dist/commands/cce.js +0 -37
- package/dist/commands/cce.js.map +0 -1
- package/dist/commands/logs.d.ts +0 -3
- package/dist/commands/logs.d.ts.map +0 -1
- package/dist/commands/logs.js +0 -39
- package/dist/commands/logs.js.map +0 -1
- package/dist/commands/qa.d.ts +0 -12
- package/dist/commands/qa.d.ts.map +0 -1
- package/dist/commands/qa.js +0 -143
- package/dist/commands/qa.js.map +0 -1
- package/dist/commands/stats.d.ts +0 -37
- package/dist/commands/stats.d.ts.map +0 -1
- package/dist/commands/stats.js +0 -169
- package/dist/commands/stats.js.map +0 -1
- package/dist/config/project.d.ts +0 -51
- package/dist/config/project.d.ts.map +0 -1
- package/dist/config/project.js +0 -81
- package/dist/config/project.js.map +0 -1
- package/dist/context/cce.d.ts +0 -39
- package/dist/context/cce.d.ts.map +0 -1
- package/dist/context/cce.js +0 -455
- package/dist/context/cce.js.map +0 -1
- package/dist/governor/fingerprint.d.ts +0 -7
- package/dist/governor/fingerprint.d.ts.map +0 -1
- package/dist/governor/fingerprint.js +0 -129
- package/dist/governor/fingerprint.js.map +0 -1
- package/dist/governor/hook.d.ts +0 -13
- package/dist/governor/hook.d.ts.map +0 -1
- package/dist/governor/hook.js +0 -389
- package/dist/governor/hook.js.map +0 -1
- package/dist/governor/state.d.ts +0 -45
- package/dist/governor/state.d.ts.map +0 -1
- package/dist/governor/state.js +0 -60
- package/dist/governor/state.js.map +0 -1
- package/dist/integrations/antigravity.d.ts +0 -20
- package/dist/integrations/antigravity.d.ts.map +0 -1
- package/dist/integrations/antigravity.js +0 -153
- package/dist/integrations/antigravity.js.map +0 -1
- package/dist/output.d.ts +0 -5
- package/dist/output.d.ts.map +0 -1
- package/dist/output.js +0 -2
- package/dist/output.js.map +0 -1
- package/dist/platform/executable.d.ts +0 -11
- package/dist/platform/executable.d.ts.map +0 -1
- package/dist/platform/executable.js +0 -69
- package/dist/platform/executable.js.map +0 -1
- package/dist/platform/process.d.ts +0 -25
- package/dist/platform/process.d.ts.map +0 -1
- package/dist/platform/process.js +0 -179
- package/dist/platform/process.js.map +0 -1
- package/dist/progress.d.ts +0 -44
- package/dist/progress.d.ts.map +0 -1
- package/dist/progress.js +0 -123
- package/dist/progress.js.map +0 -1
- package/dist/qa/diff.d.ts +0 -10
- package/dist/qa/diff.d.ts.map +0 -1
- package/dist/qa/diff.js +0 -91
- package/dist/qa/diff.js.map +0 -1
- package/dist/qa/server.d.ts +0 -10
- package/dist/qa/server.d.ts.map +0 -1
- package/dist/qa/server.js +0 -127
- package/dist/qa/server.js.map +0 -1
- package/dist/scaffold/adoption.d.ts +0 -17
- package/dist/scaffold/adoption.d.ts.map +0 -1
- package/dist/scaffold/adoption.js +0 -279
- package/dist/scaffold/adoption.js.map +0 -1
- package/dist/scaffold/legacy.d.ts +0 -4
- package/dist/scaffold/legacy.d.ts.map +0 -1
- package/dist/scaffold/legacy.js +0 -139
- package/dist/scaffold/legacy.js.map +0 -1
- package/dist/scaffold/routes.d.ts +0 -3
- package/dist/scaffold/routes.d.ts.map +0 -1
- package/dist/scaffold/routes.js +0 -37
- package/dist/scaffold/routes.js.map +0 -1
- package/dist/scaffold/standalone.d.ts +0 -7
- package/dist/scaffold/standalone.d.ts.map +0 -1
- package/dist/scaffold/standalone.js +0 -294
- package/dist/scaffold/standalone.js.map +0 -1
- package/dist/scaffold/templates.d.ts +0 -8
- package/dist/scaffold/templates.d.ts.map +0 -1
- package/dist/scaffold/templates.js +0 -312
- package/dist/scaffold/templates.js.map +0 -1
- package/dist/scaffold/write.d.ts +0 -2
- package/dist/scaffold/write.d.ts.map +0 -1
- package/dist/scaffold/write.js +0 -38
- package/dist/scaffold/write.js.map +0 -1
- package/dist/telemetry/store.d.ts +0 -33
- package/dist/telemetry/store.d.ts.map +0 -1
- package/dist/telemetry/store.js +0 -75
- package/dist/telemetry/store.js.map +0 -1
- package/dist/verify/runner.d.ts +0 -15
- package/dist/verify/runner.d.ts.map +0 -1
- package/dist/verify/runner.js +0 -112
- package/dist/verify/runner.js.map +0 -1
- package/docs/agent-audit.md +0 -24
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { atomicWrite } from "../utils/fs.js";
|
|
3
|
+
export const PHASES = [
|
|
4
|
+
{
|
|
5
|
+
id: "00",
|
|
6
|
+
name: "Project Initialization & Contracts",
|
|
7
|
+
description: "Tooling configuration, canonical design tokens, and authoritative agent policies.",
|
|
8
|
+
checklist: [
|
|
9
|
+
"Authoritative AGENTS.md and CLAUDE.md established",
|
|
10
|
+
"Canonical tokens defined in src/styles/tokens.css with Tailwind v4 @theme",
|
|
11
|
+
"Biome linter/formatter configured and passing",
|
|
12
|
+
"Git repository initialized with Conventional Commit rules",
|
|
13
|
+
"Initial verification passing (mozole verify)",
|
|
14
|
+
],
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: "01",
|
|
18
|
+
name: "Core Architecture & Layout",
|
|
19
|
+
description: "Application root shell, navigation container, footer, and responsive viewport frame.",
|
|
20
|
+
checklist: [
|
|
21
|
+
"Root layout established with single <main> landmark",
|
|
22
|
+
"Skip-to-content accessible link implemented",
|
|
23
|
+
"Navigation header with responsive mobile drawer/menu",
|
|
24
|
+
"Footer landmark with copyright and essential links",
|
|
25
|
+
"Zero viewport overflow at 320px minimum mobile width",
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: "02",
|
|
30
|
+
name: "Design Tokens & Primitive Components",
|
|
31
|
+
description: "Bespoke Radix UI primitives, typography hierarchies, and token integration.",
|
|
32
|
+
checklist: [
|
|
33
|
+
"Bespoke primitives implemented in src/components/ui/ (Button, Dialog, Input)",
|
|
34
|
+
"Zero hardcoded color hex/rgb literals (enforced by PostCSS AST audit)",
|
|
35
|
+
"Semantic typography applied via var(--font-display) and var(--font-body)",
|
|
36
|
+
"Interactive targets satisfy minimum 24px/44px touch area",
|
|
37
|
+
"No transition: all used in component styling",
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: "03",
|
|
42
|
+
name: "Primary Routes & Pages",
|
|
43
|
+
description: "Static content routes, hierarchy, meta tags, and document title management.",
|
|
44
|
+
checklist: [
|
|
45
|
+
"Home page and primary sub-pages declared with unique routes",
|
|
46
|
+
"Single <h1> per page verified across all views",
|
|
47
|
+
"Semantic HTML tags used (section, article, nav, aside)",
|
|
48
|
+
"All images include descriptive alt text or explicit decorative alt=''",
|
|
49
|
+
"External links include target='_blank' rel='noopener'",
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "04",
|
|
54
|
+
name: "Interactive Features & State",
|
|
55
|
+
description: "Client interactions, modal dialogs, and form submission flows.",
|
|
56
|
+
checklist: [
|
|
57
|
+
"Interactive modals and menus trap focus correctly",
|
|
58
|
+
"Form controls declare explicit button types (type='button'/'submit')",
|
|
59
|
+
"Mobile inputs satisfy 16px minimum font floor to prevent iOS zoom",
|
|
60
|
+
"Input validation states accessible via aria-invalid and aria-describedby",
|
|
61
|
+
"Keyboard Escape key dismisses active overlays",
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: "05",
|
|
66
|
+
name: "Dynamic Animation & Canvas (Flagship)",
|
|
67
|
+
description: "Smooth scrolling, motion transitions, and persistent WebGL/Canvas layer.",
|
|
68
|
+
checklist: [
|
|
69
|
+
"Lenis smooth scroll initialized and managed cleanly",
|
|
70
|
+
"Persistent WebGL or HTML5 canvas layer running smoothly at 60fps",
|
|
71
|
+
"Prefers-reduced-motion respected (disables heavy motion & smooth scroll)",
|
|
72
|
+
"Zero layout shifts (CLS < 0.05) during dynamic animations",
|
|
73
|
+
"Route transitions maintain scroll position and focus context",
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: "06",
|
|
78
|
+
name: "Backend Integration & Security",
|
|
79
|
+
description: "Zero-dependency PHP 8.1+ endpoints, honeypot spam protection, and rate limiting.",
|
|
80
|
+
checklist: [
|
|
81
|
+
"PHP endpoints responding with valid JSON headers and CORS checks",
|
|
82
|
+
"Honeypot field stops bot spam without user friction",
|
|
83
|
+
"IP rate limiting active and bounded by temporary storage window",
|
|
84
|
+
"Direct file access to config.php denied via .htaccess / security headers",
|
|
85
|
+
"Input sanitization and email header injection protection active",
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: "07",
|
|
90
|
+
name: "Accessibility & WCAG 2.1 AA",
|
|
91
|
+
description: "Screen reader tree, keyboard navigation, and color contrast compliance.",
|
|
92
|
+
checklist: [
|
|
93
|
+
"All interactive elements reachable and operable via Keyboard Tab/Shift-Tab",
|
|
94
|
+
"Color contrast ratios meet or exceed 4.5:1 (normal text) and 3:1 (large text)",
|
|
95
|
+
"Focus indicator visible and distinct on all interactive controls",
|
|
96
|
+
"ARIA roles, states, and properties valid and non-redundant",
|
|
97
|
+
"Automated a11y audit passes with zero critical violations",
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: "08",
|
|
102
|
+
name: "Performance & Core Web Vitals",
|
|
103
|
+
description: "Asset optimization, modern formats (WebP/AVIF), font loading, and LCP tuning.",
|
|
104
|
+
checklist: [
|
|
105
|
+
"LCP element prioritized and fonts preloaded without layout shift",
|
|
106
|
+
"Responsive images use srcset or modern picture elements",
|
|
107
|
+
"Zero blocking third-party scripts or render-delaying styles",
|
|
108
|
+
"Production build bundle size within performance budget (< 150kB initial JS)",
|
|
109
|
+
"Lighthouse performance target >= 95",
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: "09",
|
|
114
|
+
name: "Browser & Breakpoint QA",
|
|
115
|
+
description: "Headless DOM geometry probing across media query boundaries (320px to 1920px).",
|
|
116
|
+
checklist: [
|
|
117
|
+
"Headless DOM probe verifies zero horizontal scroll across all boundaries",
|
|
118
|
+
"Text clipping inspected against overflow:hidden ancestors (zero defects)",
|
|
119
|
+
"Touch targets verified across mobile viewports (320px, 375px, 390px)",
|
|
120
|
+
"Tablet and desktop viewports verified (768px, 1024px, 1440px, 1920px)",
|
|
121
|
+
"Zero raster screenshot dependency during routine QA",
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
id: "10",
|
|
126
|
+
name: "Production Readiness & Launch",
|
|
127
|
+
description: "Final static build, SSG pre-rendering, sitemap, and deployment readiness.",
|
|
128
|
+
checklist: [
|
|
129
|
+
"Full static build succeeds with zero TypeScript or Biome errors",
|
|
130
|
+
"Static pre-rendering generates all required HTML entrypoints",
|
|
131
|
+
"Robots.txt and sitemap.xml generated and verified",
|
|
132
|
+
"Clean git tree with Conventional Commits and zero AI attribution traces",
|
|
133
|
+
"Production deployment verified on hosting target",
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
];
|
|
137
|
+
export function generatePhasesStatusMd(currentPhase = "00") {
|
|
138
|
+
let content = `# Project Development Phases & Status
|
|
139
|
+
|
|
140
|
+
> **Discipline Notice:** Development proceeds sequentially through phases 00 to 10. Run \`mozole phase status\` to inspect progress, and \`mozole phase next\` to advance upon passing all checklist criteria and \`mozole verify\`.
|
|
141
|
+
|
|
142
|
+
**Current Phase:** Phase ${currentPhase}
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
`;
|
|
147
|
+
for (const p of PHASES) {
|
|
148
|
+
const isCompleted = Number(p.id) < Number(currentPhase);
|
|
149
|
+
const isCurrent = p.id === currentPhase;
|
|
150
|
+
const statusBadge = isCompleted ? "[COMPLETED]" : isCurrent ? "[IN PROGRESS]" : "[PENDING]";
|
|
151
|
+
content += `### Phase ${p.id}: ${p.name} ${statusBadge}\n`;
|
|
152
|
+
content += `${p.description}\n\n`;
|
|
153
|
+
for (const item of p.checklist) {
|
|
154
|
+
const mark = isCompleted ? "[x]" : "[ ]";
|
|
155
|
+
content += `- ${mark} ${item}\n`;
|
|
156
|
+
}
|
|
157
|
+
content += "\n---\n\n";
|
|
158
|
+
}
|
|
159
|
+
return content;
|
|
160
|
+
}
|
|
161
|
+
export async function scaffoldPhases(projectRoot) {
|
|
162
|
+
const statusMd = generatePhasesStatusMd("00");
|
|
163
|
+
await atomicWrite(path.join(projectRoot, "docs", "phases", "status.md"), statusMd);
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=phases.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phases.js","sourceRoot":"","sources":["../../src/scaffold/phases.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function generateRepomapArchitecture(projectName: string, isFlagship: boolean): string;
|
|
2
|
+
export declare function generateRepomapComponents(): string;
|
|
3
|
+
export declare function generateRepomapRoutes(): string;
|
|
4
|
+
export declare function generateRepomapTokens(): string;
|
|
5
|
+
export declare function scaffoldRepomap(projectRoot: string, projectName: string, isFlagship?: boolean): Promise<void>;
|
|
6
|
+
//# sourceMappingURL=repomap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repomap.d.ts","sourceRoot":"","sources":["../../src/scaffold/repomap.ts"],"names":[],"mappings":"AAGA,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,MAAM,CA6B5F;AAED,wBAAgB,yBAAyB,IAAI,MAAM,CAelD;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAW9C;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAoB9C;AAED,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,UAAU,UAAQ,GACjB,OAAO,CAAC,IAAI,CAAC,CASf"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { atomicWrite } from "../utils/fs.js";
|
|
3
|
+
export function generateRepomapArchitecture(projectName, isFlagship) {
|
|
4
|
+
return `# Architecture Overview - ${projectName}
|
|
5
|
+
|
|
6
|
+
## Technology Stack
|
|
7
|
+
- **Engine:** Vite (Universal ESM Build Engine)
|
|
8
|
+
- **Frontend Framework:** React 19
|
|
9
|
+
- **Routing Model:** ${isFlagship ? "Wouter (Headless Micro-Router with Dynamic View Transitions)" : "React Router 7 (Framework Mode with Static Pre-rendering / SSG)"}
|
|
10
|
+
- **Styling Architecture:** Tailwind CSS v4 with Canonical Tokens in \`src/styles/tokens.css\`
|
|
11
|
+
- **UI Primitives:** Bespoke application components backed by unstyled Radix UI primitives
|
|
12
|
+
${isFlagship ? "- **Creative Experience:** Lenis (Smooth Scroll) + Motion + Persistent Canvas Layer\n" : ""}
|
|
13
|
+
- **Backend Architecture:** Pure PHP 8.1+ Zero-Dependency API (Contact Mailer, Honeypot, IP Rate Limiter)
|
|
14
|
+
|
|
15
|
+
## Directory Hierarchy
|
|
16
|
+
\`\`\`
|
|
17
|
+
├── api/ # Pure PHP 8.1+ endpoints & security htaccess
|
|
18
|
+
├── docs/
|
|
19
|
+
│ ├── design/ # Design references, exported assets & screens
|
|
20
|
+
│ ├── phases/ # 10-step atomic development status
|
|
21
|
+
│ └── repomap/ # Architecture, components, and routes index
|
|
22
|
+
├── src/
|
|
23
|
+
│ ├── components/
|
|
24
|
+
│ │ ├── layout/ # Header, Footer, PageShell
|
|
25
|
+
│ │ └── ui/ # Bespoke Radix UI primitives (Button, Dialog, etc.)
|
|
26
|
+
│ ├── routes/ # Page components and views
|
|
27
|
+
│ └── styles/
|
|
28
|
+
│ └── tokens.css # Sole canonical design token repository (@theme)
|
|
29
|
+
└── AGENTS.md # Authoritative engineering contract
|
|
30
|
+
\`\`\`
|
|
31
|
+
`;
|
|
32
|
+
}
|
|
33
|
+
export function generateRepomapComponents() {
|
|
34
|
+
return `# Component Registry & Primitives Index
|
|
35
|
+
|
|
36
|
+
All UI primitives are bespoke application code located in \`src/components/ui/\`.
|
|
37
|
+
|
|
38
|
+
| Component | Path | Backing Primitive | Responsibilities |
|
|
39
|
+
| :--- | :--- | :--- | :--- |
|
|
40
|
+
| **Header** | \`src/components/layout/Header.tsx\` | Semantic \`<header>\` | Main navigation, mobile drawer toggle, branding |
|
|
41
|
+
| **Footer** | \`src/components/layout/Footer.tsx\` | Semantic \`<footer>\` | Site links, legal notes, copyright |
|
|
42
|
+
| **Button** | \`src/components/ui/Button.tsx\` | Native \`<button>\` | Accessible action trigger, variant styling, loading state |
|
|
43
|
+
| **Dialog** | \`src/components/ui/Dialog.tsx\` | Radix \`@radix-ui/react-dialog\` | Accessible modal dialog, focus trap, escape key |
|
|
44
|
+
| **Input** | \`src/components/ui/Input.tsx\` | Native \`<input>\` | Form control with 16px mobile font floor and error states |
|
|
45
|
+
|
|
46
|
+
> **Contract Rule:** Primitives must remain unopinionated, unstyled Radix foundations with project design tokens applied. No external template dumping.
|
|
47
|
+
`;
|
|
48
|
+
}
|
|
49
|
+
export function generateRepomapRoutes() {
|
|
50
|
+
return `# Routes & Page Index
|
|
51
|
+
|
|
52
|
+
| Path | Component | Layout | Purpose |
|
|
53
|
+
| :--- | :--- | :--- | :--- |
|
|
54
|
+
| \`/\` | \`src/routes/home.tsx\` | Root Layout | Primary landing page & value proposition |
|
|
55
|
+
| \`/about\` | \`src/routes/about.tsx\` | Root Layout | Studio background and team ethos |
|
|
56
|
+
| \`/contact\` | \`src/routes/contact.tsx\` | Root Layout | Contact form, interactive inquiry submission |
|
|
57
|
+
|
|
58
|
+
> **SEO & A11y Requirement:** Every route must export a unique title and meta description, and render exactly one top-level \`<h1>\` tag.
|
|
59
|
+
`;
|
|
60
|
+
}
|
|
61
|
+
export function generateRepomapTokens() {
|
|
62
|
+
return `# Design Tokens Index
|
|
63
|
+
|
|
64
|
+
Canonical token definitions live in \`src/styles/tokens.css\` using Tailwind v4 \`@theme\`.
|
|
65
|
+
|
|
66
|
+
## Color Tokens
|
|
67
|
+
- \`--color-background\`: Primary viewport background.
|
|
68
|
+
- \`--color-foreground\`: Primary text and icon color.
|
|
69
|
+
- \`--color-surface\`: Card and panel background surface.
|
|
70
|
+
- \`--color-primary\`: Brand primary interactive accent.
|
|
71
|
+
- \`--color-border\`: Component outline and separator.
|
|
72
|
+
|
|
73
|
+
## Typography Roles
|
|
74
|
+
- \`--font-display\`: Headings (\`<h1>\` through \`<h6>\`).
|
|
75
|
+
- \`--font-body\`: Paragraphs, captions, and body copy.
|
|
76
|
+
- \`--font-mono\`: Code snippets, technical telemetry, and metadata.
|
|
77
|
+
|
|
78
|
+
## Fluid Text Scales
|
|
79
|
+
- \`--text-xs\` to \`--text-5xl\`: Fluid viewport-scaled typography via \`clamp()\`.
|
|
80
|
+
`;
|
|
81
|
+
}
|
|
82
|
+
export async function scaffoldRepomap(projectRoot, projectName, isFlagship = false) {
|
|
83
|
+
const repomapDir = path.join(projectRoot, "docs", "repomap");
|
|
84
|
+
await atomicWrite(path.join(repomapDir, "architecture.md"), generateRepomapArchitecture(projectName, isFlagship));
|
|
85
|
+
await atomicWrite(path.join(repomapDir, "components.md"), generateRepomapComponents());
|
|
86
|
+
await atomicWrite(path.join(repomapDir, "routes.md"), generateRepomapRoutes());
|
|
87
|
+
await atomicWrite(path.join(repomapDir, "tokens.md"), generateRepomapTokens());
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=repomap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repomap.js","sourceRoot":"","sources":["../../src/scaffold/repomap.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,MAAM,UAAU,2BAA2B,CAAC,WAAmB,EAAE,UAAmB;IAClF,OAAO,6BAA6B,WAAW;;;;;uBAK1B,UAAU,CAAC,CAAC,CAAC,8DAA8D,CAAC,CAAC,CAAC,iEAAiE;;;EAGpK,UAAU,CAAC,CAAC,CAAC,uFAAuF,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;;;;;CAmB1G,CAAC;AACF,CAAC;AAED,MAAM,UAAU,yBAAyB;IACvC,OAAO;;;;;;;;;;;;;CAaR,CAAC;AACF,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,OAAO;;;;;;;;;CASR,CAAC;AACF,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,OAAO;;;;;;;;;;;;;;;;;;CAkBR,CAAC;AACF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,WAAmB,EACnB,WAAmB,EACnB,UAAU,GAAG,KAAK;IAElB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IAC7D,MAAM,WAAW,CACf,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,iBAAiB,CAAC,EACxC,2BAA2B,CAAC,WAAW,EAAE,UAAU,CAAC,CACrD,CAAC;IACF,MAAM,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,EAAE,yBAAyB,EAAE,CAAC,CAAC;IACvF,MAAM,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,EAAE,qBAAqB,EAAE,CAAC,CAAC;IAC/E,MAAM,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,EAAE,qBAAqB,EAAE,CAAC,CAAC;AACjF,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare function generateStandardPackageJson(projectName: string): string;
|
|
2
|
+
export declare function generateStandardViteConfig(): string;
|
|
3
|
+
export declare function generateStandardRouterConfig(): string;
|
|
4
|
+
export declare function generateStandardTsConfig(): string;
|
|
5
|
+
export declare function generateStandardBiomeJson(): string;
|
|
6
|
+
export declare function generateStandardRoot(): string;
|
|
7
|
+
export declare function generateStandardRoutesHome(): string;
|
|
8
|
+
export declare function generateStandardHeader(): string;
|
|
9
|
+
export declare function generateStandardFooter(): string;
|
|
10
|
+
export declare function generateBespokeButton(): string;
|
|
11
|
+
export declare function generateBespokeInput(): string;
|
|
12
|
+
export declare function scaffoldStandardProject(projectRoot: string, projectName: string): Promise<void>;
|
|
13
|
+
//# sourceMappingURL=standard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standard.d.ts","sourceRoot":"","sources":["../../src/scaffold/standard.ts"],"names":[],"mappings":"AAGA,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAmCvE;AAED,wBAAgB,0BAA0B,IAAI,MAAM,CAYnD;AAED,wBAAgB,4BAA4B,IAAI,MAAM,CAWrD;AAED,wBAAgB,wBAAwB,IAAI,MAAM,CAsBjD;AAED,wBAAgB,yBAAyB,IAAI,MAAM,CAmBlD;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAsC7C;AAED,wBAAgB,0BAA0B,IAAI,MAAM,CAkCnD;AAED,wBAAgB,sBAAsB,IAAI,MAAM,CAkD/C;AAED,wBAAgB,sBAAsB,IAAI,MAAM,CAW/C;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAwD9C;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAiC7C;AAED,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CA+Df"}
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { atomicWrite } from "../utils/fs.js";
|
|
3
|
+
export function generateStandardPackageJson(projectName) {
|
|
4
|
+
const pkg = {
|
|
5
|
+
name: projectName,
|
|
6
|
+
private: true,
|
|
7
|
+
type: "module",
|
|
8
|
+
scripts: {
|
|
9
|
+
dev: "react-router dev",
|
|
10
|
+
build: "react-router build",
|
|
11
|
+
preview: "vite preview",
|
|
12
|
+
typecheck: "react-router typegen && tsc --noEmit",
|
|
13
|
+
verify: "mozole verify",
|
|
14
|
+
},
|
|
15
|
+
dependencies: {
|
|
16
|
+
"@radix-ui/react-dialog": "^1.1.6",
|
|
17
|
+
"@radix-ui/react-slot": "^1.1.2",
|
|
18
|
+
"@react-router/node": "^7.2.0",
|
|
19
|
+
"@react-router/serve": "^7.2.0",
|
|
20
|
+
isbot: "^5.1.23",
|
|
21
|
+
react: "^19.0.0",
|
|
22
|
+
"react-dom": "^19.0.0",
|
|
23
|
+
"react-router": "^7.2.0",
|
|
24
|
+
},
|
|
25
|
+
devDependencies: {
|
|
26
|
+
"@biomejs/biome": "^1.9.4",
|
|
27
|
+
"@react-router/dev": "^7.2.0",
|
|
28
|
+
"@tailwindcss/vite": "^4.0.9",
|
|
29
|
+
"@types/node": "^22.13.0",
|
|
30
|
+
"@types/react": "^19.0.10",
|
|
31
|
+
"@types/react-dom": "^19.0.4",
|
|
32
|
+
tailwindcss: "^4.0.9",
|
|
33
|
+
typescript: "^5.7.3",
|
|
34
|
+
vite: "^6.2.0",
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
return JSON.stringify(pkg, null, 2);
|
|
38
|
+
}
|
|
39
|
+
export function generateStandardViteConfig() {
|
|
40
|
+
return `import { reactRouter } from "@react-router/dev/vite";
|
|
41
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
42
|
+
import { defineConfig } from "vite";
|
|
43
|
+
|
|
44
|
+
export default defineConfig({
|
|
45
|
+
plugins: [tailwindcss(), reactRouter()],
|
|
46
|
+
server: {
|
|
47
|
+
port: 5173,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
`;
|
|
51
|
+
}
|
|
52
|
+
export function generateStandardRouterConfig() {
|
|
53
|
+
return `import type { Config } from "@react-router/dev/config";
|
|
54
|
+
|
|
55
|
+
export default {
|
|
56
|
+
appDirectory: "src",
|
|
57
|
+
ssr: false,
|
|
58
|
+
async prerender() {
|
|
59
|
+
return ["/", "/about", "/contact"];
|
|
60
|
+
},
|
|
61
|
+
} satisfies Config;
|
|
62
|
+
`;
|
|
63
|
+
}
|
|
64
|
+
export function generateStandardTsConfig() {
|
|
65
|
+
return JSON.stringify({
|
|
66
|
+
include: ["src/**/*", ".react-router/types/**/*"],
|
|
67
|
+
compilerOptions: {
|
|
68
|
+
target: "ES2022",
|
|
69
|
+
lib: ["DOM", "DOM.Iterable", "ES2022"],
|
|
70
|
+
module: "ESNext",
|
|
71
|
+
moduleResolution: "Bundler",
|
|
72
|
+
resolveJsonModule: true,
|
|
73
|
+
jsx: "react-jsx",
|
|
74
|
+
strict: true,
|
|
75
|
+
noEmit: true,
|
|
76
|
+
isolatedModules: true,
|
|
77
|
+
esModuleInterop: true,
|
|
78
|
+
skipLibCheck: true,
|
|
79
|
+
rootDirs: [".", "./.react-router/types"],
|
|
80
|
+
},
|
|
81
|
+
}, null, 2);
|
|
82
|
+
}
|
|
83
|
+
export function generateStandardBiomeJson() {
|
|
84
|
+
return JSON.stringify({
|
|
85
|
+
$schema: "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
86
|
+
vcs: { enabled: true, clientKind: "git", useIgnoreFile: true },
|
|
87
|
+
formatter: { enabled: true, indentStyle: "space", indentWidth: 2, lineWidth: 100 },
|
|
88
|
+
linter: {
|
|
89
|
+
enabled: true,
|
|
90
|
+
rules: {
|
|
91
|
+
recommended: true,
|
|
92
|
+
style: { noNonNullAssertion: "warn" },
|
|
93
|
+
a11y: { recommended: true },
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
javascript: { formatter: { quoteStyle: "double", trailingCommas: "all" } },
|
|
97
|
+
}, null, 2);
|
|
98
|
+
}
|
|
99
|
+
export function generateStandardRoot() {
|
|
100
|
+
return `import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router";
|
|
101
|
+
import "./styles/tokens.css";
|
|
102
|
+
import { Footer } from "./components/layout/Footer";
|
|
103
|
+
import { Header } from "./components/layout/Header";
|
|
104
|
+
|
|
105
|
+
export function Layout({ children }: { children: React.ReactNode }) {
|
|
106
|
+
return (
|
|
107
|
+
<html lang="en">
|
|
108
|
+
<head>
|
|
109
|
+
<meta charSet="utf-8" />
|
|
110
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
111
|
+
<Meta />
|
|
112
|
+
<Links />
|
|
113
|
+
</head>
|
|
114
|
+
<body>
|
|
115
|
+
<a
|
|
116
|
+
href="#main-content"
|
|
117
|
+
className="skip-link sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:px-4 focus:py-2 focus:bg-[var(--color-primary)] focus:text-[var(--color-primary-foreground)] focus:rounded-[var(--radius-sm)]"
|
|
118
|
+
>
|
|
119
|
+
Skip to main content
|
|
120
|
+
</a>
|
|
121
|
+
<Header />
|
|
122
|
+
<main id="main-content" tabIndex={-1} className="flex-1 flex flex-col">
|
|
123
|
+
{children}
|
|
124
|
+
</main>
|
|
125
|
+
<Footer />
|
|
126
|
+
<ScrollRestoration />
|
|
127
|
+
<Scripts />
|
|
128
|
+
</body>
|
|
129
|
+
</html>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export default function App() {
|
|
134
|
+
return <Outlet />;
|
|
135
|
+
}
|
|
136
|
+
`;
|
|
137
|
+
}
|
|
138
|
+
export function generateStandardRoutesHome() {
|
|
139
|
+
return `import { Button } from "../components/ui/Button";
|
|
140
|
+
import type { Route } from "./+types/home";
|
|
141
|
+
|
|
142
|
+
export function meta(_args: Route.MetaArgs) {
|
|
143
|
+
return [
|
|
144
|
+
{ title: "Mozole Studio - Digital Engineering" },
|
|
145
|
+
{
|
|
146
|
+
name: "description",
|
|
147
|
+
content: "Web applications and design systems engineered with React Router and Tailwind CSS.",
|
|
148
|
+
},
|
|
149
|
+
];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export default function Home() {
|
|
153
|
+
return (
|
|
154
|
+
<section className="py-20 px-[var(--spacing-container-gutter)] max-w-[var(--spacing-container-max)] mx-auto w-full">
|
|
155
|
+
<div className="flex flex-col gap-6 max-w-2xl">
|
|
156
|
+
<h1 className="text-[var(--text-4xl)] font-bold text-[var(--color-foreground)] leading-tight">
|
|
157
|
+
Digital Engineering & Design Systems
|
|
158
|
+
</h1>
|
|
159
|
+
<p className="text-[var(--text-lg)] text-[var(--color-muted-foreground)] leading-relaxed">
|
|
160
|
+
Production web applications built with bespoke UI primitives, strict design tokens, and
|
|
161
|
+
static pre-rendering.
|
|
162
|
+
</p>
|
|
163
|
+
<div className="flex gap-4 pt-4">
|
|
164
|
+
<Button variant="primary">Explore Projects</Button>
|
|
165
|
+
<Button variant="outline">Learn More</Button>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
</section>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
`;
|
|
172
|
+
}
|
|
173
|
+
export function generateStandardHeader() {
|
|
174
|
+
return `import { NavLink } from "react-router";
|
|
175
|
+
|
|
176
|
+
export function Header() {
|
|
177
|
+
return (
|
|
178
|
+
<header className="border-b border-[var(--color-border)] bg-[var(--color-surface)]/80 backdrop-blur-md sticky top-0 z-40">
|
|
179
|
+
<div className="max-w-[var(--spacing-container-max)] mx-auto px-[var(--spacing-container-gutter)] h-16 flex items-center justify-between">
|
|
180
|
+
<NavLink
|
|
181
|
+
to="/"
|
|
182
|
+
className="text-[var(--text-lg)] font-bold tracking-tight text-[var(--color-foreground)]"
|
|
183
|
+
>
|
|
184
|
+
Mozole Studio
|
|
185
|
+
</NavLink>
|
|
186
|
+
<nav aria-label="Main Navigation" className="flex items-center gap-6 text-[var(--text-sm)]">
|
|
187
|
+
<NavLink
|
|
188
|
+
to="/"
|
|
189
|
+
className={({ isActive }) =>
|
|
190
|
+
isActive
|
|
191
|
+
? "text-[var(--color-primary)] font-medium"
|
|
192
|
+
: "text-[var(--color-muted-foreground)] hover:text-[var(--color-foreground)]"
|
|
193
|
+
}
|
|
194
|
+
>
|
|
195
|
+
Home
|
|
196
|
+
</NavLink>
|
|
197
|
+
<NavLink
|
|
198
|
+
to="/about"
|
|
199
|
+
className={({ isActive }) =>
|
|
200
|
+
isActive
|
|
201
|
+
? "text-[var(--color-primary)] font-medium"
|
|
202
|
+
: "text-[var(--color-muted-foreground)] hover:text-[var(--color-foreground)]"
|
|
203
|
+
}
|
|
204
|
+
>
|
|
205
|
+
About
|
|
206
|
+
</NavLink>
|
|
207
|
+
<NavLink
|
|
208
|
+
to="/contact"
|
|
209
|
+
className={({ isActive }) =>
|
|
210
|
+
isActive
|
|
211
|
+
? "text-[var(--color-primary)] font-medium"
|
|
212
|
+
: "text-[var(--color-muted-foreground)] hover:text-[var(--color-foreground)]"
|
|
213
|
+
}
|
|
214
|
+
>
|
|
215
|
+
Contact
|
|
216
|
+
</NavLink>
|
|
217
|
+
</nav>
|
|
218
|
+
</div>
|
|
219
|
+
</header>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
`;
|
|
223
|
+
}
|
|
224
|
+
export function generateStandardFooter() {
|
|
225
|
+
return `export function Footer() {
|
|
226
|
+
return (
|
|
227
|
+
<footer className="border-t border-[var(--color-border)] bg-[var(--color-surface)] py-8 mt-auto">
|
|
228
|
+
<div className="max-w-[var(--spacing-container-max)] mx-auto px-[var(--spacing-container-gutter)] flex flex-col sm:flex-row items-center justify-between gap-4 text-[var(--text-xs)] text-[var(--color-muted-foreground)]">
|
|
229
|
+
<p>© {new Date().getFullYear()} Mozole Studio. All rights reserved.</p>
|
|
230
|
+
</div>
|
|
231
|
+
</footer>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
`;
|
|
235
|
+
}
|
|
236
|
+
export function generateBespokeButton() {
|
|
237
|
+
return `import { Slot } from "@radix-ui/react-slot";
|
|
238
|
+
import * as React from "react";
|
|
239
|
+
|
|
240
|
+
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
241
|
+
asChild?: boolean;
|
|
242
|
+
variant?: "primary" | "outline" | "ghost";
|
|
243
|
+
size?: "sm" | "md" | "lg";
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
247
|
+
(
|
|
248
|
+
{
|
|
249
|
+
asChild = false,
|
|
250
|
+
className = "",
|
|
251
|
+
variant = "primary",
|
|
252
|
+
size = "md",
|
|
253
|
+
type = "button",
|
|
254
|
+
children,
|
|
255
|
+
...props
|
|
256
|
+
},
|
|
257
|
+
ref,
|
|
258
|
+
) => {
|
|
259
|
+
const Component = asChild ? Slot : "button";
|
|
260
|
+
const base =
|
|
261
|
+
"inline-flex items-center justify-center font-medium transition-[background-color,border-color,color] cursor-pointer focus-visible:outline-2 focus-visible:outline-[var(--color-primary)] focus-visible:outline-offset-2 disabled:opacity-50 disabled:pointer-events-none rounded-[var(--radius-sm)]";
|
|
262
|
+
|
|
263
|
+
const variants = {
|
|
264
|
+
primary:
|
|
265
|
+
"bg-[var(--color-primary)] text-[var(--color-primary-foreground)] hover:bg-[var(--color-accent)]",
|
|
266
|
+
outline:
|
|
267
|
+
"border border-[var(--color-border)] bg-transparent text-[var(--color-foreground)] hover:border-[var(--color-primary)]",
|
|
268
|
+
ghost:
|
|
269
|
+
"bg-transparent text-[var(--color-foreground)] hover:bg-[var(--color-surface-elevated)]",
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const sizes = {
|
|
273
|
+
sm: "h-8 px-3 text-[var(--text-xs)] min-h-8 min-w-8",
|
|
274
|
+
md: "h-10 px-4 text-[var(--text-sm)] min-h-10 min-w-10",
|
|
275
|
+
lg: "h-12 px-6 text-[var(--text-base)] min-h-12 min-w-12",
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
return (
|
|
279
|
+
<Component
|
|
280
|
+
ref={ref}
|
|
281
|
+
type={asChild ? undefined : type}
|
|
282
|
+
className={\`\${base} \${variants[variant]} \${sizes[size]} \${className}\`}
|
|
283
|
+
{...props}
|
|
284
|
+
>
|
|
285
|
+
{children}
|
|
286
|
+
</Component>
|
|
287
|
+
);
|
|
288
|
+
},
|
|
289
|
+
);
|
|
290
|
+
Button.displayName = "Button";
|
|
291
|
+
`;
|
|
292
|
+
}
|
|
293
|
+
export function generateBespokeInput() {
|
|
294
|
+
return `import * as React from "react";
|
|
295
|
+
|
|
296
|
+
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
297
|
+
error?: string;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
301
|
+
({ className = "", error, id, type = "text", ...props }, ref) => {
|
|
302
|
+
return (
|
|
303
|
+
<div className="flex flex-col gap-1.5 w-full">
|
|
304
|
+
<input
|
|
305
|
+
ref={ref}
|
|
306
|
+
id={id}
|
|
307
|
+
type={type}
|
|
308
|
+
aria-invalid={Boolean(error)}
|
|
309
|
+
aria-describedby={error && id ? \`\${id}-error\` : undefined}
|
|
310
|
+
className={\`text-base w-full h-10 px-3 bg-[var(--color-surface)] border \${
|
|
311
|
+
error ? "border-[var(--color-danger)]" : "border-[var(--color-border)]"
|
|
312
|
+
} rounded-[var(--radius-sm)] text-[var(--color-foreground)] placeholder:text-[var(--color-muted-foreground)] focus-visible:outline-2 focus-visible:outline-[var(--color-primary)] transition-[border-color] \${className}\`}
|
|
313
|
+
{...props}
|
|
314
|
+
/>
|
|
315
|
+
{error && id && (
|
|
316
|
+
<span id={\`\${id}-error\`} className="text-[var(--text-xs)] text-[var(--color-danger)]">
|
|
317
|
+
{error}
|
|
318
|
+
</span>
|
|
319
|
+
)}
|
|
320
|
+
</div>
|
|
321
|
+
);
|
|
322
|
+
},
|
|
323
|
+
);
|
|
324
|
+
Input.displayName = "Input";
|
|
325
|
+
`;
|
|
326
|
+
}
|
|
327
|
+
export async function scaffoldStandardProject(projectRoot, projectName) {
|
|
328
|
+
await atomicWrite(path.join(projectRoot, "package.json"), generateStandardPackageJson(projectName));
|
|
329
|
+
await atomicWrite(path.join(projectRoot, "vite.config.ts"), generateStandardViteConfig());
|
|
330
|
+
await atomicWrite(path.join(projectRoot, "react-router.config.ts"), generateStandardRouterConfig());
|
|
331
|
+
await atomicWrite(path.join(projectRoot, "tsconfig.json"), generateStandardTsConfig());
|
|
332
|
+
await atomicWrite(path.join(projectRoot, "biome.json"), generateStandardBiomeJson());
|
|
333
|
+
// Source files
|
|
334
|
+
const srcDir = path.join(projectRoot, "src");
|
|
335
|
+
await atomicWrite(path.join(srcDir, "root.tsx"), generateStandardRoot());
|
|
336
|
+
await atomicWrite(path.join(srcDir, "routes", "home.tsx"), generateStandardRoutesHome());
|
|
337
|
+
await atomicWrite(path.join(srcDir, "routes.ts"), `import { type RouteConfig, index, route } from "@react-router/dev/routes";
|
|
338
|
+
|
|
339
|
+
export default [
|
|
340
|
+
index("routes/home.tsx"),
|
|
341
|
+
route("about", "routes/about.tsx"),
|
|
342
|
+
route("contact", "routes/contact.tsx"),
|
|
343
|
+
] satisfies RouteConfig;
|
|
344
|
+
`);
|
|
345
|
+
for (const [slug, title, content] of [
|
|
346
|
+
["about", "About", "Digital products built with thoughtful design and engineering."],
|
|
347
|
+
["contact", "Contact", "Get in touch to discuss your next project."],
|
|
348
|
+
]) {
|
|
349
|
+
await atomicWrite(path.join(srcDir, "routes", `${slug}.tsx`), `export function meta() {
|
|
350
|
+
return [{ title: "${title} - Mozole Studio" }];
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export default function Page() {
|
|
354
|
+
return (
|
|
355
|
+
<section className="py-20 px-[var(--spacing-container-gutter)] max-w-[var(--spacing-container-max)] mx-auto w-full">
|
|
356
|
+
<h1 className="text-4xl font-bold mb-6">${title}</h1>
|
|
357
|
+
<p className="text-base text-[var(--color-muted-foreground)]">
|
|
358
|
+
${content}
|
|
359
|
+
</p>
|
|
360
|
+
</section>
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
`);
|
|
364
|
+
}
|
|
365
|
+
// Components
|
|
366
|
+
await atomicWrite(path.join(srcDir, "components", "layout", "Header.tsx"), generateStandardHeader());
|
|
367
|
+
await atomicWrite(path.join(srcDir, "components", "layout", "Footer.tsx"), generateStandardFooter());
|
|
368
|
+
await atomicWrite(path.join(srcDir, "components", "ui", "Button.tsx"), generateBespokeButton());
|
|
369
|
+
await atomicWrite(path.join(srcDir, "components", "ui", "Input.tsx"), generateBespokeInput());
|
|
370
|
+
}
|
|
371
|
+
//# sourceMappingURL=standard.js.map
|