@a13xu/lucid 1.9.5 → 1.11.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 (33) hide show
  1. package/README.md +15 -1
  2. package/build/database.d.ts +32 -0
  3. package/build/database.js +38 -0
  4. package/build/index.js +282 -1
  5. package/build/instance.d.ts +9 -0
  6. package/build/instance.js +78 -0
  7. package/build/tools/e2e.d.ts +13 -0
  8. package/build/tools/e2e.js +109 -0
  9. package/build/tools/plan.d.ts +75 -0
  10. package/build/tools/plan.js +148 -0
  11. package/build/tools/webdev/accessibility-audit.d.ts +23 -0
  12. package/build/tools/webdev/accessibility-audit.js +214 -0
  13. package/build/tools/webdev/api-client.d.ts +24 -0
  14. package/build/tools/webdev/api-client.js +167 -0
  15. package/build/tools/webdev/design-tokens.d.ts +18 -0
  16. package/build/tools/webdev/design-tokens.js +375 -0
  17. package/build/tools/webdev/generate-component.d.ts +18 -0
  18. package/build/tools/webdev/generate-component.js +123 -0
  19. package/build/tools/webdev/index.d.ts +10 -0
  20. package/build/tools/webdev/index.js +10 -0
  21. package/build/tools/webdev/perf-hints.d.ts +24 -0
  22. package/build/tools/webdev/perf-hints.js +247 -0
  23. package/build/tools/webdev/responsive-layout.d.ts +18 -0
  24. package/build/tools/webdev/responsive-layout.js +229 -0
  25. package/build/tools/webdev/scaffold-page.d.ts +18 -0
  26. package/build/tools/webdev/scaffold-page.js +137 -0
  27. package/build/tools/webdev/security-scan.d.ts +23 -0
  28. package/build/tools/webdev/security-scan.js +247 -0
  29. package/build/tools/webdev/seo-meta.d.ts +24 -0
  30. package/build/tools/webdev/seo-meta.js +114 -0
  31. package/build/tools/webdev/test-generator.d.ts +18 -0
  32. package/build/tools/webdev/test-generator.js +269 -0
  33. package/package.json +1 -1
@@ -0,0 +1,375 @@
1
+ import { z } from "zod";
2
+ // ---------------------------------------------------------------------------
3
+ // Schema
4
+ // ---------------------------------------------------------------------------
5
+ export const DesignTokensSchema = z.object({
6
+ brand_name: z.string().describe("Brand/project name"),
7
+ primary_color: z
8
+ .string()
9
+ .describe("Primary brand color as hex (e.g. #3B82F6) or color name (e.g. blue)"),
10
+ mood: z
11
+ .enum(["minimal", "bold", "playful", "corporate"])
12
+ .describe("Design mood"),
13
+ output_format: z
14
+ .enum(["css-variables", "tailwind-config", "json"])
15
+ .describe("Output format for the token set"),
16
+ });
17
+ // ---------------------------------------------------------------------------
18
+ // Color utilities
19
+ // ---------------------------------------------------------------------------
20
+ /** Named colors to hex fallback map */
21
+ const NAMED_COLORS = {
22
+ blue: "#3B82F6",
23
+ indigo: "#6366F1",
24
+ purple: "#8B5CF6",
25
+ pink: "#EC4899",
26
+ red: "#EF4444",
27
+ orange: "#F97316",
28
+ yellow: "#EAB308",
29
+ green: "#22C55E",
30
+ teal: "#14B8A6",
31
+ cyan: "#06B6D4",
32
+ slate: "#64748B",
33
+ gray: "#6B7280",
34
+ zinc: "#71717A",
35
+ neutral: "#737373",
36
+ stone: "#78716C",
37
+ };
38
+ function resolveHex(color) {
39
+ const trimmed = color.trim().toLowerCase();
40
+ if (trimmed.startsWith("#"))
41
+ return trimmed;
42
+ return NAMED_COLORS[trimmed] ?? "#3B82F6";
43
+ }
44
+ function hexToRgb(hex) {
45
+ const clean = hex.replace("#", "");
46
+ const full = clean.length === 3
47
+ ? clean.split("").map((c) => c + c).join("")
48
+ : clean;
49
+ const r = parseInt(full.slice(0, 2), 16);
50
+ const g = parseInt(full.slice(2, 4), 16);
51
+ const b = parseInt(full.slice(4, 6), 16);
52
+ return [r ?? 0, g ?? 0, b ?? 0];
53
+ }
54
+ function rgbToHsl(r, g, b) {
55
+ const rr = r / 255, gg = g / 255, bb = b / 255;
56
+ const max = Math.max(rr, gg, bb);
57
+ const min = Math.min(rr, gg, bb);
58
+ const l = (max + min) / 2;
59
+ if (max === min)
60
+ return [0, 0, Math.round(l * 100)];
61
+ const d = max - min;
62
+ const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
63
+ let h = 0;
64
+ if (max === rr)
65
+ h = ((gg - bb) / d + (gg < bb ? 6 : 0)) / 6;
66
+ else if (max === gg)
67
+ h = ((bb - rr) / d + 2) / 6;
68
+ else
69
+ h = ((rr - gg) / d + 4) / 6;
70
+ return [Math.round(h * 360), Math.round(s * 100), Math.round(l * 100)];
71
+ }
72
+ function hslToHex(h, s, l) {
73
+ const ll = l / 100, ss = s / 100;
74
+ const c = (1 - Math.abs(2 * ll - 1)) * ss;
75
+ const x = c * (1 - Math.abs((h / 60) % 2 - 1));
76
+ const m = ll - c / 2;
77
+ let r = 0, g = 0, b = 0;
78
+ if (h < 60) {
79
+ r = c;
80
+ g = x;
81
+ }
82
+ else if (h < 120) {
83
+ r = x;
84
+ g = c;
85
+ }
86
+ else if (h < 180) {
87
+ g = c;
88
+ b = x;
89
+ }
90
+ else if (h < 240) {
91
+ g = x;
92
+ b = c;
93
+ }
94
+ else if (h < 300) {
95
+ r = x;
96
+ b = c;
97
+ }
98
+ else {
99
+ r = c;
100
+ b = x;
101
+ }
102
+ const toHex = (v) => Math.round((v + m) * 255).toString(16).padStart(2, "0");
103
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
104
+ }
105
+ /** Generate a 10-step color scale (50–950) from a base hex color */
106
+ function generateColorScale(baseHex) {
107
+ const [r, g, b] = hexToRgb(baseHex);
108
+ const [h, s] = rgbToHsl(r, g, b);
109
+ const steps = [
110
+ ["50", s > 10 ? s - 10 : s, 97],
111
+ ["100", s > 10 ? s - 5 : s, 94],
112
+ ["200", s, 86],
113
+ ["300", s, 74],
114
+ ["400", s, 62],
115
+ ["500", s, 50],
116
+ ["600", s, 41],
117
+ ["700", s, 34],
118
+ ["800", s, 27],
119
+ ["900", s, 20],
120
+ ["950", s, 14],
121
+ ];
122
+ const scale = {};
123
+ for (const [name, sat, light] of steps) {
124
+ scale[name] = hslToHex(h, sat, light);
125
+ }
126
+ return scale;
127
+ }
128
+ /** Pick a neutral (desaturated) scale based on primary hue */
129
+ function generateNeutralScale(primaryHex) {
130
+ const [r, g, b] = hexToRgb(primaryHex);
131
+ const [h] = rgbToHsl(r, g, b);
132
+ const neutralSteps = [
133
+ ["50", 8, 98], ["100", 8, 95], ["200", 8, 90], ["300", 8, 82], ["400", 7, 65],
134
+ ["500", 6, 48], ["600", 5, 38], ["700", 5, 28], ["800", 4, 20], ["900", 4, 12], ["950", 3, 8],
135
+ ];
136
+ const scale = {};
137
+ for (const [name, sat, light] of neutralSteps) {
138
+ scale[name] = hslToHex(h, sat, light);
139
+ }
140
+ return scale;
141
+ }
142
+ const MOOD_CONFIG = {
143
+ minimal: {
144
+ fontSans: "'Inter', 'system-ui', sans-serif",
145
+ fontMono: "'JetBrains Mono', 'Fira Code', monospace",
146
+ radiusSm: "2px", radiusMd: "4px", radiusLg: "6px", radiusFull: "9999px",
147
+ shadowSm: "0 1px 2px rgba(0,0,0,0.05)",
148
+ shadowMd: "0 2px 8px rgba(0,0,0,0.08)",
149
+ shadowLg: "0 4px 16px rgba(0,0,0,0.10)",
150
+ spacingBase: 4,
151
+ },
152
+ bold: {
153
+ fontSans: "'Poppins', 'Montserrat', sans-serif",
154
+ fontMono: "'Roboto Mono', monospace",
155
+ radiusSm: "4px", radiusMd: "8px", radiusLg: "12px", radiusFull: "9999px",
156
+ shadowSm: "0 2px 4px rgba(0,0,0,0.15)",
157
+ shadowMd: "0 4px 12px rgba(0,0,0,0.20)",
158
+ shadowLg: "0 8px 24px rgba(0,0,0,0.25)",
159
+ spacingBase: 4,
160
+ },
161
+ playful: {
162
+ fontSans: "'Nunito', 'Quicksand', sans-serif",
163
+ fontMono: "'Space Mono', monospace",
164
+ radiusSm: "8px", radiusMd: "16px", radiusLg: "24px", radiusFull: "9999px",
165
+ shadowSm: "2px 2px 0 rgba(0,0,0,0.10)",
166
+ shadowMd: "4px 4px 0 rgba(0,0,0,0.12)",
167
+ shadowLg: "6px 6px 0 rgba(0,0,0,0.15)",
168
+ spacingBase: 4,
169
+ },
170
+ corporate: {
171
+ fontSans: "'IBM Plex Sans', 'Arial', sans-serif",
172
+ fontMono: "'IBM Plex Mono', monospace",
173
+ radiusSm: "2px", radiusMd: "3px", radiusLg: "4px", radiusFull: "9999px",
174
+ shadowSm: "0 1px 3px rgba(0,0,0,0.12)",
175
+ shadowMd: "0 2px 6px rgba(0,0,0,0.15)",
176
+ shadowLg: "0 4px 12px rgba(0,0,0,0.18)",
177
+ spacingBase: 4,
178
+ },
179
+ };
180
+ // ---------------------------------------------------------------------------
181
+ // Formatters
182
+ // ---------------------------------------------------------------------------
183
+ function formatCssVariables(brand, primary, neutral, mc) {
184
+ const sp = (n) => `${n * mc.spacingBase}px`;
185
+ const primaryVars = Object.entries(primary)
186
+ .map(([k, v]) => ` --color-primary-${k}: ${v};`)
187
+ .join("\n");
188
+ const neutralVars = Object.entries(neutral)
189
+ .map(([k, v]) => ` --color-neutral-${k}: ${v};`)
190
+ .join("\n");
191
+ return `/* ${brand} Design Tokens */
192
+ :root {
193
+ /* Colors — Primary */
194
+ ${primaryVars}
195
+
196
+ /* Colors — Neutral */
197
+ ${neutralVars}
198
+
199
+ /* Semantic colors */
200
+ --color-bg: var(--color-neutral-50);
201
+ --color-bg-subtle: var(--color-neutral-100);
202
+ --color-surface: #ffffff;
203
+ --color-border: var(--color-neutral-200);
204
+ --color-text: var(--color-neutral-900);
205
+ --color-text-muted: var(--color-neutral-500);
206
+ --color-accent: var(--color-primary-500);
207
+ --color-accent-hover: var(--color-primary-600);
208
+
209
+ /* Typography */
210
+ --font-sans: ${mc.fontSans};
211
+ --font-mono: ${mc.fontMono};
212
+ --text-xs: 0.75rem;
213
+ --text-sm: 0.875rem;
214
+ --text-base: 1rem;
215
+ --text-lg: 1.125rem;
216
+ --text-xl: 1.25rem;
217
+ --text-2xl: 1.5rem;
218
+ --text-3xl: 1.875rem;
219
+ --text-4xl: 2.25rem;
220
+ --leading-tight: 1.25;
221
+ --leading-normal: 1.5;
222
+ --leading-loose: 1.75;
223
+
224
+ /* Spacing */
225
+ --space-1: ${sp(1)};
226
+ --space-2: ${sp(2)};
227
+ --space-3: ${sp(3)};
228
+ --space-4: ${sp(4)};
229
+ --space-6: ${sp(6)};
230
+ --space-8: ${sp(8)};
231
+ --space-12: ${sp(12)};
232
+ --space-16: ${sp(16)};
233
+ --space-24: ${sp(24)};
234
+
235
+ /* Border Radius */
236
+ --radius-sm: ${mc.radiusSm};
237
+ --radius-md: ${mc.radiusMd};
238
+ --radius-lg: ${mc.radiusLg};
239
+ --radius-full: ${mc.radiusFull};
240
+
241
+ /* Shadows */
242
+ --shadow-sm: ${mc.shadowSm};
243
+ --shadow-md: ${mc.shadowMd};
244
+ --shadow-lg: ${mc.shadowLg};
245
+
246
+ /* Transitions */
247
+ --duration-fast: 150ms;
248
+ --duration-normal: 250ms;
249
+ --duration-slow: 400ms;
250
+ --ease-default: cubic-bezier(0.4, 0, 0.2, 1);
251
+ }`;
252
+ }
253
+ function formatTailwindConfig(brand, primary, neutral, mc) {
254
+ const primaryEntries = Object.entries(primary)
255
+ .map(([k, v]) => ` ${k}: "${v}",`)
256
+ .join("\n");
257
+ const neutralEntries = Object.entries(neutral)
258
+ .map(([k, v]) => ` ${k}: "${v}",`)
259
+ .join("\n");
260
+ return `// tailwind.config.ts — ${brand} Design Tokens
261
+ import type { Config } from "tailwindcss";
262
+
263
+ const config: Config = {
264
+ content: ["./src/**/*.{ts,tsx,vue,html}"],
265
+ theme: {
266
+ extend: {
267
+ colors: {
268
+ primary: {
269
+ ${primaryEntries}
270
+ },
271
+ neutral: {
272
+ ${neutralEntries}
273
+ },
274
+ },
275
+ fontFamily: {
276
+ sans: [${mc.fontSans.split(",").map((f) => `"${f.trim().replace(/'/g, "")}"`).join(", ")}],
277
+ mono: [${mc.fontMono.split(",").map((f) => `"${f.trim().replace(/'/g, "")}"`).join(", ")}],
278
+ },
279
+ borderRadius: {
280
+ sm: "${mc.radiusSm}",
281
+ md: "${mc.radiusMd}",
282
+ lg: "${mc.radiusLg}",
283
+ full: "${mc.radiusFull}",
284
+ },
285
+ boxShadow: {
286
+ sm: "${mc.shadowSm}",
287
+ md: "${mc.shadowMd}",
288
+ lg: "${mc.shadowLg}",
289
+ },
290
+ spacing: {
291
+ "1": "4px", "2": "8px", "3": "12px", "4": "16px",
292
+ "6": "24px", "8": "32px", "12": "48px", "16": "64px",
293
+ },
294
+ },
295
+ },
296
+ plugins: [],
297
+ };
298
+
299
+ export default config;`;
300
+ }
301
+ function formatJson(brand, primary, neutral, mc) {
302
+ const tokens = {
303
+ brand,
304
+ colors: {
305
+ primary: Object.fromEntries(Object.entries(primary).map(([k, v]) => [`primary.${k}`, v])),
306
+ neutral: Object.fromEntries(Object.entries(neutral).map(([k, v]) => [`neutral.${k}`, v])),
307
+ semantic: {
308
+ "bg": neutral["50"],
309
+ "bg-subtle": neutral["100"],
310
+ "surface": "#ffffff",
311
+ "border": neutral["200"],
312
+ "text": neutral["900"],
313
+ "text-muted": neutral["500"],
314
+ "accent": primary["500"],
315
+ "accent-hover": primary["600"],
316
+ },
317
+ },
318
+ typography: {
319
+ fontSans: mc.fontSans,
320
+ fontMono: mc.fontMono,
321
+ scale: { xs: "0.75rem", sm: "0.875rem", base: "1rem", lg: "1.125rem", xl: "1.25rem", "2xl": "1.5rem", "3xl": "1.875rem", "4xl": "2.25rem" },
322
+ },
323
+ spacing: { "1": "4px", "2": "8px", "3": "12px", "4": "16px", "6": "24px", "8": "32px", "12": "48px", "16": "64px" },
324
+ radii: { sm: mc.radiusSm, md: mc.radiusMd, lg: mc.radiusLg, full: mc.radiusFull },
325
+ shadows: { sm: mc.shadowSm, md: mc.shadowMd, lg: mc.shadowLg },
326
+ };
327
+ return JSON.stringify(tokens, null, 2);
328
+ }
329
+ // ---------------------------------------------------------------------------
330
+ // Handler
331
+ // ---------------------------------------------------------------------------
332
+ // Example call:
333
+ // handleDesignTokens({ brand_name: "Acme", primary_color: "#6366F1", mood: "minimal", output_format: "css-variables" })
334
+ export function handleDesignTokens(args) {
335
+ const { brand_name, primary_color, mood, output_format } = args;
336
+ const primaryHex = resolveHex(primary_color);
337
+ const primaryScale = generateColorScale(primaryHex);
338
+ const neutralScale = generateNeutralScale(primaryHex);
339
+ const mc = MOOD_CONFIG[mood] ?? MOOD_CONFIG["minimal"];
340
+ let code;
341
+ let lang;
342
+ let filename;
343
+ switch (output_format) {
344
+ case "tailwind-config":
345
+ code = formatTailwindConfig(brand_name, primaryScale, neutralScale, mc);
346
+ lang = "typescript";
347
+ filename = "tailwind.config.ts";
348
+ break;
349
+ case "json":
350
+ code = formatJson(brand_name, primaryScale, neutralScale, mc);
351
+ lang = "json";
352
+ filename = "design-tokens.json";
353
+ break;
354
+ default: // css-variables
355
+ code = formatCssVariables(brand_name, primaryScale, neutralScale, mc);
356
+ lang = "css";
357
+ filename = "tokens.css";
358
+ }
359
+ const lines = [
360
+ `✅ Design tokens: ${brand_name}`,
361
+ `📄 Filename: ${filename}`,
362
+ `🎨 Primary: ${primaryHex} | Mood: ${mood} | Format: ${output_format}`,
363
+ ``,
364
+ "```" + lang,
365
+ code,
366
+ "```",
367
+ ``,
368
+ `💡 Reasoning: Generated a complete design token set from primary color ${primaryHex}. ` +
369
+ `Color scales (50–950) derived via HSL lightness interpolation. ` +
370
+ `Neutral scale uses the same hue with reduced saturation. ` +
371
+ `Typography, spacing, radius, and shadow values tuned for "${mood}" mood. ` +
372
+ `Review contrast ratios with a tool like https://webaim.org/resources/contrastchecker/.`,
373
+ ];
374
+ return lines.join("\n");
375
+ }
@@ -0,0 +1,18 @@
1
+ import { z } from "zod";
2
+ export declare const GenerateComponentSchema: z.ZodObject<{
3
+ description: z.ZodString;
4
+ framework: z.ZodEnum<["react", "vue", "nuxt"]>;
5
+ styling: z.ZodEnum<["tailwind", "css-modules", "none"]>;
6
+ typescript: z.ZodBoolean;
7
+ }, "strip", z.ZodTypeAny, {
8
+ typescript: boolean;
9
+ description: string;
10
+ framework: "vue" | "react" | "nuxt";
11
+ styling: "none" | "tailwind" | "css-modules";
12
+ }, {
13
+ typescript: boolean;
14
+ description: string;
15
+ framework: "vue" | "react" | "nuxt";
16
+ styling: "none" | "tailwind" | "css-modules";
17
+ }>;
18
+ export declare function handleGenerateComponent(args: z.infer<typeof GenerateComponentSchema>): string;
@@ -0,0 +1,123 @@
1
+ import { z } from "zod";
2
+ // ---------------------------------------------------------------------------
3
+ // Schema
4
+ // ---------------------------------------------------------------------------
5
+ export const GenerateComponentSchema = z.object({
6
+ description: z.string().describe("Natural language description of the component"),
7
+ framework: z.enum(["react", "vue", "nuxt"]).describe("Target framework"),
8
+ styling: z.enum(["tailwind", "css-modules", "none"]).describe("Styling approach"),
9
+ typescript: z.boolean().describe("Whether to use TypeScript"),
10
+ });
11
+ // ---------------------------------------------------------------------------
12
+ // Helpers
13
+ // ---------------------------------------------------------------------------
14
+ function toPascalCase(str) {
15
+ return str
16
+ .replace(/[^a-zA-Z0-9\s]/g, "")
17
+ .split(/\s+/)
18
+ .filter(Boolean)
19
+ .slice(0, 3)
20
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
21
+ .join("");
22
+ }
23
+ function buildReactComponent(name, description, styling, typescript) {
24
+ const ts = typescript;
25
+ let styleImport = "";
26
+ let classAttr = "";
27
+ if (styling === "tailwind") {
28
+ classAttr = `className="flex flex-col gap-4"`;
29
+ }
30
+ else if (styling === "css-modules") {
31
+ styleImport = `\nimport styles from "./${name}.module.css";`;
32
+ classAttr = `className={styles.container}`;
33
+ }
34
+ const propsBlock = ts
35
+ ? `\ninterface ${name}Props {\n // TODO: define props\n}\n`
36
+ : "";
37
+ const fnSignature = ts
38
+ ? `export const ${name}: React.FC<${name}Props> = (_props) => {`
39
+ : `export const ${name} = (_props) => {`;
40
+ const reactImport = ts ? `import React from "react";` : `import React from "react";`;
41
+ const code = `${reactImport}${styleImport}
42
+ ${propsBlock}
43
+ // ${description}
44
+ ${fnSignature}
45
+ return (
46
+ <div ${classAttr}>
47
+ {/* ${description} */}
48
+ </div>
49
+ );
50
+ };
51
+
52
+ export default ${name};`;
53
+ return { code, lang: ts ? "tsx" : "jsx" };
54
+ }
55
+ function buildVueComponent(name, description, styling, typescript) {
56
+ const scriptLang = typescript ? ` lang="ts"` : "";
57
+ let classAttr = "class=\"container\"";
58
+ let styleTag = "";
59
+ if (styling === "tailwind") {
60
+ classAttr = "class=\"flex flex-col gap-4\"";
61
+ }
62
+ else if (styling === "css-modules") {
63
+ classAttr = `:class="$style.container"`;
64
+ styleTag = `\n<style module>\n.container {\n /* ${name} styles */\n}\n</style>`;
65
+ }
66
+ else {
67
+ styleTag = `\n<style scoped>\n.container {\n /* ${name} styles */\n}\n</style>`;
68
+ }
69
+ const code = `<script setup${scriptLang}>
70
+ // ${description}
71
+ // TODO: define props and emits
72
+ // const props = defineProps<{}>()
73
+ </script>
74
+
75
+ <template>
76
+ <div ${classAttr}>
77
+ <!-- ${description} -->
78
+ </div>
79
+ </template>${styleTag}`;
80
+ return { code, lang: "vue" };
81
+ }
82
+ // ---------------------------------------------------------------------------
83
+ // Handler
84
+ // ---------------------------------------------------------------------------
85
+ // Example call:
86
+ // handleGenerateComponent({ description: "user profile card with avatar", framework: "react", styling: "tailwind", typescript: true })
87
+ export function handleGenerateComponent(args) {
88
+ const { description, framework, styling, typescript } = args;
89
+ const componentName = toPascalCase(description) || "MyComponent";
90
+ let code;
91
+ let lang;
92
+ let filename;
93
+ if (framework === "react") {
94
+ ({ code, lang } = buildReactComponent(componentName, description, styling, typescript));
95
+ filename = `${componentName}.${typescript ? "tsx" : "jsx"}`;
96
+ }
97
+ else {
98
+ // vue or nuxt
99
+ ({ code, lang } = buildVueComponent(componentName, description, styling, typescript));
100
+ filename = `${componentName}.vue`;
101
+ }
102
+ const styleNote = styling === "tailwind"
103
+ ? "Tailwind CSS utility classes pre-applied"
104
+ : styling === "css-modules"
105
+ ? "CSS Modules — a .module.css file is also needed"
106
+ : "No styling framework — add your own styles";
107
+ const lines = [
108
+ `✅ Component: ${componentName}`,
109
+ `📄 Filename: ${filename}`,
110
+ `🔧 ${framework.toUpperCase()} | ${styling} | TypeScript: ${typescript}`,
111
+ ``,
112
+ "```" + lang,
113
+ code,
114
+ "```",
115
+ ``,
116
+ `💡 Reasoning: Scaffold for "${description}". ${styleNote}. ` +
117
+ `Fill in props, state, and template logic. ` +
118
+ (framework === "react"
119
+ ? "Define props in the interface and remove unused ones."
120
+ : "Use defineProps<>() and defineEmits<>() as needed."),
121
+ ];
122
+ return lines.join("\n");
123
+ }
@@ -0,0 +1,10 @@
1
+ export { GenerateComponentSchema, handleGenerateComponent } from "./generate-component.js";
2
+ export { ScaffoldPageSchema, handleScaffoldPage } from "./scaffold-page.js";
3
+ export { SeoMetaSchema, handleSeoMeta } from "./seo-meta.js";
4
+ export { AccessibilityAuditSchema, handleAccessibilityAudit } from "./accessibility-audit.js";
5
+ export { ApiClientSchema, handleApiClient } from "./api-client.js";
6
+ export { TestGeneratorSchema, handleTestGenerator } from "./test-generator.js";
7
+ export { ResponsiveLayoutSchema, handleResponsiveLayout } from "./responsive-layout.js";
8
+ export { SecurityScanSchema, handleSecurityScan } from "./security-scan.js";
9
+ export { DesignTokensSchema, handleDesignTokens } from "./design-tokens.js";
10
+ export { PerfHintsSchema, handlePerfHints } from "./perf-hints.js";
@@ -0,0 +1,10 @@
1
+ export { GenerateComponentSchema, handleGenerateComponent } from "./generate-component.js";
2
+ export { ScaffoldPageSchema, handleScaffoldPage } from "./scaffold-page.js";
3
+ export { SeoMetaSchema, handleSeoMeta } from "./seo-meta.js";
4
+ export { AccessibilityAuditSchema, handleAccessibilityAudit } from "./accessibility-audit.js";
5
+ export { ApiClientSchema, handleApiClient } from "./api-client.js";
6
+ export { TestGeneratorSchema, handleTestGenerator } from "./test-generator.js";
7
+ export { ResponsiveLayoutSchema, handleResponsiveLayout } from "./responsive-layout.js";
8
+ export { SecurityScanSchema, handleSecurityScan } from "./security-scan.js";
9
+ export { DesignTokensSchema, handleDesignTokens } from "./design-tokens.js";
10
+ export { PerfHintsSchema, handlePerfHints } from "./perf-hints.js";
@@ -0,0 +1,24 @@
1
+ import { z } from "zod";
2
+ export declare const PerfHintsSchema: z.ZodObject<{
3
+ code: z.ZodString;
4
+ framework: z.ZodEnum<["react", "vue", "nuxt", "vanilla"]>;
5
+ context: z.ZodEnum<["component", "page", "layout"]>;
6
+ }, "strip", z.ZodTypeAny, {
7
+ code: string;
8
+ context: "component" | "page" | "layout";
9
+ framework: "vue" | "react" | "nuxt" | "vanilla";
10
+ }, {
11
+ code: string;
12
+ context: "component" | "page" | "layout";
13
+ framework: "vue" | "react" | "nuxt" | "vanilla";
14
+ }>;
15
+ export type CwvMetric = "LCP" | "CLS" | "INP" | "FCP" | "TTFB" | "General";
16
+ export type PerfImpact = "high" | "medium" | "low";
17
+ export interface PerfIssue {
18
+ line: number;
19
+ metric: CwvMetric;
20
+ impact: PerfImpact;
21
+ message: string;
22
+ fix: string;
23
+ }
24
+ export declare function handlePerfHints(args: z.infer<typeof PerfHintsSchema>): string;