@claudetools/tools 0.9.0 → 0.9.2
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/dist/cli.js +9 -1
- package/dist/codedna/__tests__/examples/mongoose-example.d.ts +6 -0
- package/dist/codedna/__tests__/examples/mongoose-example.js +163 -0
- package/dist/codedna/__tests__/fixtures/typeorm-production-test.d.ts +1 -0
- package/dist/codedna/__tests__/fixtures/typeorm-production-test.js +231 -0
- package/dist/codedna/__tests__/fixtures/typeorm-test.d.ts +1 -0
- package/dist/codedna/__tests__/fixtures/typeorm-test.js +124 -0
- package/dist/codedna/__tests__/laravel-output-review.d.ts +1 -0
- package/dist/codedna/__tests__/laravel-output-review.js +249 -0
- package/dist/codedna/__tests__/mongoose-output-test.d.ts +1 -0
- package/dist/codedna/__tests__/mongoose-output-test.js +178 -0
- package/dist/codedna/examples/radix-example.d.ts +2 -0
- package/dist/codedna/examples/radix-example.js +259 -0
- package/dist/codedna/index.d.ts +5 -3
- package/dist/codedna/index.js +6 -3
- package/dist/codedna/kappa-ast.d.ts +143 -5
- package/dist/codedna/kappa-drizzle-generator.js +8 -5
- package/dist/codedna/kappa-gofiber-generator.d.ts +65 -0
- package/dist/codedna/kappa-gofiber-generator.js +587 -0
- package/dist/codedna/kappa-laravel-generator.d.ts +68 -0
- package/dist/codedna/kappa-laravel-generator.js +741 -0
- package/dist/codedna/kappa-lexer.d.ts +44 -0
- package/dist/codedna/kappa-lexer.js +124 -0
- package/dist/codedna/kappa-mantine-generator.d.ts +65 -0
- package/dist/codedna/kappa-mantine-generator.js +518 -0
- package/dist/codedna/kappa-mongoose-generator.d.ts +44 -0
- package/dist/codedna/kappa-mongoose-generator.js +442 -0
- package/dist/codedna/kappa-parser.d.ts +43 -1
- package/dist/codedna/kappa-parser.js +601 -0
- package/dist/codedna/kappa-radix-generator.d.ts +61 -0
- package/dist/codedna/kappa-radix-generator.js +566 -0
- package/dist/codedna/kappa-typeorm-generator.d.ts +59 -0
- package/dist/codedna/kappa-typeorm-generator.js +723 -0
- package/dist/codedna/kappa-vitest-generator.d.ts +85 -0
- package/dist/codedna/kappa-vitest-generator.js +739 -0
- package/dist/codedna/parser.js +26 -1
- package/dist/codegen/cloud-client.d.ts +160 -0
- package/dist/codegen/cloud-client.js +195 -0
- package/dist/codegen/codegen-tool.d.ts +35 -0
- package/dist/codegen/codegen-tool.js +312 -0
- package/dist/codegen/field-inference.d.ts +24 -0
- package/dist/codegen/field-inference.js +101 -0
- package/dist/codegen/form-parser.d.ts +13 -0
- package/dist/codegen/form-parser.js +186 -0
- package/dist/codegen/index.d.ts +2 -0
- package/dist/codegen/index.js +4 -0
- package/dist/codegen/natural-parser.d.ts +50 -0
- package/dist/codegen/natural-parser.js +769 -0
- package/dist/handlers/codedna-handlers.d.ts +1 -1
- package/dist/handlers/codegen-handlers.d.ts +20 -0
- package/dist/handlers/codegen-handlers.js +60 -0
- package/dist/handlers/kappa-handlers.d.ts +97 -0
- package/dist/handlers/kappa-handlers.js +408 -0
- package/dist/handlers/tool-handlers.js +124 -221
- package/dist/helpers/api-client.js +48 -3
- package/dist/helpers/compact-formatter.d.ts +9 -2
- package/dist/helpers/compact-formatter.js +26 -2
- package/dist/helpers/config.d.ts +7 -2
- package/dist/helpers/config.js +25 -10
- package/dist/helpers/session-validation.d.ts +1 -1
- package/dist/helpers/session-validation.js +2 -4
- package/dist/helpers/tasks.d.ts +21 -0
- package/dist/helpers/tasks.js +52 -0
- package/dist/helpers/workers.d.ts +1 -1
- package/dist/helpers/workers.js +19 -19
- package/dist/setup.d.ts +1 -0
- package/dist/setup.js +228 -3
- package/dist/templates/claude-md.d.ts +1 -1
- package/dist/templates/claude-md.js +37 -152
- package/dist/templates/orchestrator-prompt.d.ts +2 -2
- package/dist/templates/orchestrator-prompt.js +31 -38
- package/dist/templates/self-critique.d.ts +50 -0
- package/dist/templates/self-critique.js +209 -0
- package/dist/templates/worker-prompt.d.ts +3 -3
- package/dist/templates/worker-prompt.js +18 -18
- package/dist/tools.js +77 -413
- package/docs/codedna/generator-testing-summary.md +205 -0
- package/docs/codedna/radix-ui-generator.md +478 -0
- package/docs/kappa-gofiber-generator.md +274 -0
- package/docs/kappa-laravel-fixes.md +172 -0
- package/docs/kappa-mongoose-generator.md +322 -0
- package/docs/kappa-vitest-generator.md +337 -0
- package/package.json +1 -1
- package/dist/context/deduplication.test.d.ts +0 -6
- package/dist/context/deduplication.test.js +0 -84
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Kappa v2.5 Radix UI Component Generator
|
|
3
|
+
// =============================================================================
|
|
4
|
+
//
|
|
5
|
+
// Generates React components using Radix UI primitives from Kappa ComponentBlock AST.
|
|
6
|
+
// Supports compound components, accessibility props, and Radix composition patterns.
|
|
7
|
+
//
|
|
8
|
+
// Radix UI provides unstyled, accessible components that can be composed with
|
|
9
|
+
// Tailwind CSS for complete design control while maintaining accessibility.
|
|
10
|
+
//
|
|
11
|
+
const RADIX_MAPPINGS = {
|
|
12
|
+
Dialog: {
|
|
13
|
+
primitive: 'Dialog',
|
|
14
|
+
packageName: '@radix-ui/react-dialog',
|
|
15
|
+
requiredParts: ['Root', 'Trigger', 'Portal', 'Overlay', 'Content'],
|
|
16
|
+
optionalParts: ['Close', 'Title', 'Description'],
|
|
17
|
+
},
|
|
18
|
+
Modal: {
|
|
19
|
+
primitive: 'Dialog',
|
|
20
|
+
packageName: '@radix-ui/react-dialog',
|
|
21
|
+
requiredParts: ['Root', 'Trigger', 'Portal', 'Overlay', 'Content'],
|
|
22
|
+
optionalParts: ['Close', 'Title', 'Description'],
|
|
23
|
+
},
|
|
24
|
+
Popover: {
|
|
25
|
+
primitive: 'Popover',
|
|
26
|
+
packageName: '@radix-ui/react-popover',
|
|
27
|
+
requiredParts: ['Root', 'Trigger', 'Portal', 'Content'],
|
|
28
|
+
optionalParts: ['Anchor', 'Close', 'Arrow'],
|
|
29
|
+
},
|
|
30
|
+
Select: {
|
|
31
|
+
primitive: 'Select',
|
|
32
|
+
packageName: '@radix-ui/react-select',
|
|
33
|
+
requiredParts: ['Root', 'Trigger', 'Portal', 'Content', 'Viewport', 'Item'],
|
|
34
|
+
optionalParts: ['Value', 'Icon', 'ScrollUpButton', 'ScrollDownButton', 'Group', 'Label', 'Separator'],
|
|
35
|
+
},
|
|
36
|
+
Dropdown: {
|
|
37
|
+
primitive: 'DropdownMenu',
|
|
38
|
+
packageName: '@radix-ui/react-dropdown-menu',
|
|
39
|
+
requiredParts: ['Root', 'Trigger', 'Portal', 'Content', 'Item'],
|
|
40
|
+
optionalParts: ['Group', 'Label', 'Separator', 'CheckboxItem', 'RadioGroup', 'RadioItem', 'Sub', 'SubTrigger', 'SubContent'],
|
|
41
|
+
},
|
|
42
|
+
Tabs: {
|
|
43
|
+
primitive: 'Tabs',
|
|
44
|
+
packageName: '@radix-ui/react-tabs',
|
|
45
|
+
requiredParts: ['Root', 'List', 'Trigger', 'Content'],
|
|
46
|
+
optionalParts: [],
|
|
47
|
+
},
|
|
48
|
+
Accordion: {
|
|
49
|
+
primitive: 'Accordion',
|
|
50
|
+
packageName: '@radix-ui/react-accordion',
|
|
51
|
+
requiredParts: ['Root', 'Item', 'Trigger', 'Content'],
|
|
52
|
+
optionalParts: ['Header'],
|
|
53
|
+
},
|
|
54
|
+
Tooltip: {
|
|
55
|
+
primitive: 'Tooltip',
|
|
56
|
+
packageName: '@radix-ui/react-tooltip',
|
|
57
|
+
requiredParts: ['Provider', 'Root', 'Trigger', 'Portal', 'Content'],
|
|
58
|
+
optionalParts: ['Arrow'],
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
// =============================================================================
|
|
62
|
+
// Generator Class
|
|
63
|
+
// =============================================================================
|
|
64
|
+
export class KappaRadixGenerator {
|
|
65
|
+
provenance;
|
|
66
|
+
typescript;
|
|
67
|
+
basePath;
|
|
68
|
+
a11yDocs;
|
|
69
|
+
usedDependencies = new Set();
|
|
70
|
+
constructor(options = {}) {
|
|
71
|
+
this.provenance = options.provenance ?? true;
|
|
72
|
+
this.typescript = options.typescript ?? true;
|
|
73
|
+
this.basePath = options.basePath ?? 'components/ui';
|
|
74
|
+
this.a11yDocs = options.a11yDocs ?? true;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Generate Radix UI component files from ComponentBlock AST nodes
|
|
78
|
+
*/
|
|
79
|
+
generate(components) {
|
|
80
|
+
this.usedDependencies.clear();
|
|
81
|
+
const generatedComponents = [];
|
|
82
|
+
for (const component of components) {
|
|
83
|
+
const primitive = this.detectPrimitive(component);
|
|
84
|
+
if (primitive) {
|
|
85
|
+
generatedComponents.push(this.generateRadixComponent(component, primitive));
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
// Generate simple component for non-Radix UI components (Button, Card, Badge, etc.)
|
|
89
|
+
generatedComponents.push(this.generateSimpleComponent(component));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
components: generatedComponents,
|
|
94
|
+
dependencies: Array.from(this.usedDependencies).sort(),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// ===========================================================================
|
|
98
|
+
// Primitive Detection
|
|
99
|
+
// ===========================================================================
|
|
100
|
+
detectPrimitive(component) {
|
|
101
|
+
// Check if component name matches a known Radix primitive
|
|
102
|
+
for (const [key, mapping] of Object.entries(RADIX_MAPPINGS)) {
|
|
103
|
+
if (component.name.toLowerCase().includes(key.toLowerCase())) {
|
|
104
|
+
return mapping.primitive;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Check if compound parts suggest a Radix primitive
|
|
108
|
+
if (component.compound && component.compound.length > 0) {
|
|
109
|
+
const partNames = component.compound.map(p => p.name.toLowerCase());
|
|
110
|
+
// Dialog/Modal pattern
|
|
111
|
+
if (partNames.includes('trigger') && partNames.includes('content')) {
|
|
112
|
+
if (partNames.includes('overlay')) {
|
|
113
|
+
return 'Dialog';
|
|
114
|
+
}
|
|
115
|
+
return 'Popover';
|
|
116
|
+
}
|
|
117
|
+
// Tabs pattern
|
|
118
|
+
if (partNames.includes('list') && partNames.includes('trigger') && partNames.includes('content')) {
|
|
119
|
+
return 'Tabs';
|
|
120
|
+
}
|
|
121
|
+
// Accordion pattern
|
|
122
|
+
if (partNames.includes('item') && partNames.includes('trigger')) {
|
|
123
|
+
return 'Accordion';
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
// ===========================================================================
|
|
129
|
+
// Radix Component Generation
|
|
130
|
+
// ===========================================================================
|
|
131
|
+
generateRadixComponent(component, primitive) {
|
|
132
|
+
const mapping = Object.values(RADIX_MAPPINGS).find(m => m.primitive === primitive);
|
|
133
|
+
this.usedDependencies.add(mapping.packageName);
|
|
134
|
+
const lines = [];
|
|
135
|
+
const ext = this.typescript ? 'tsx' : 'jsx';
|
|
136
|
+
// Header
|
|
137
|
+
if (this.provenance) {
|
|
138
|
+
lines.push('// Generated by Kappa v2.5 CodeDNA - Radix UI Generator');
|
|
139
|
+
lines.push(`// Component: ${component.name}`);
|
|
140
|
+
lines.push(`// Primitive: ${primitive}`);
|
|
141
|
+
lines.push('');
|
|
142
|
+
}
|
|
143
|
+
// Accessibility documentation
|
|
144
|
+
if (this.a11yDocs) {
|
|
145
|
+
lines.push('/**');
|
|
146
|
+
lines.push(` * ${component.name} - Built with Radix UI ${primitive}`);
|
|
147
|
+
lines.push(' *');
|
|
148
|
+
lines.push(' * Accessibility features:');
|
|
149
|
+
lines.push(` * - ARIA attributes automatically applied by Radix UI`);
|
|
150
|
+
lines.push(` * - Keyboard navigation support`);
|
|
151
|
+
lines.push(` * - Focus management`);
|
|
152
|
+
if (primitive === 'Dialog' || primitive === 'AlertDialog') {
|
|
153
|
+
lines.push(` * - Focus trap when open`);
|
|
154
|
+
lines.push(` * - Scroll lock when open`);
|
|
155
|
+
}
|
|
156
|
+
lines.push(' */');
|
|
157
|
+
}
|
|
158
|
+
// Imports
|
|
159
|
+
lines.push(...this.generateRadixImports(primitive, mapping, component));
|
|
160
|
+
lines.push('');
|
|
161
|
+
// Generate based on whether it's compound
|
|
162
|
+
if (component.compound && component.compound.length > 0) {
|
|
163
|
+
lines.push(...this.generateCompoundRadixComponent(component, primitive, mapping));
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
lines.push(...this.generateSimpleRadixComponent(component, primitive, mapping));
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
path: `${this.basePath}/${this.toKebabCase(component.name)}.${ext}`,
|
|
170
|
+
content: lines.join('\n'),
|
|
171
|
+
primitive,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
generateRadixImports(primitive, mapping, component) {
|
|
175
|
+
const lines = [];
|
|
176
|
+
// React imports
|
|
177
|
+
lines.push("import * as React from 'react';");
|
|
178
|
+
// Radix imports - import all used parts
|
|
179
|
+
const usedParts = this.getUsedRadixParts(component, mapping);
|
|
180
|
+
const radixImports = usedParts.map(part => `${primitive}${part}`).join(', ');
|
|
181
|
+
lines.push(`import * as ${primitive} from '${mapping.packageName}';`);
|
|
182
|
+
// Utility imports
|
|
183
|
+
lines.push("import { cn } from '@/lib/utils';");
|
|
184
|
+
return lines;
|
|
185
|
+
}
|
|
186
|
+
getUsedRadixParts(component, mapping) {
|
|
187
|
+
const parts = new Set(mapping.requiredParts);
|
|
188
|
+
// Add optional parts if compound components reference them
|
|
189
|
+
if (component.compound) {
|
|
190
|
+
for (const part of component.compound) {
|
|
191
|
+
const radixPart = this.mapCompoundPartToRadix(part.name);
|
|
192
|
+
if (mapping.optionalParts.includes(radixPart)) {
|
|
193
|
+
parts.add(radixPart);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return Array.from(parts);
|
|
198
|
+
}
|
|
199
|
+
mapCompoundPartToRadix(partName) {
|
|
200
|
+
// Map Kappa compound part names to Radix part names
|
|
201
|
+
const mappings = {
|
|
202
|
+
trigger: 'Trigger',
|
|
203
|
+
content: 'Content',
|
|
204
|
+
overlay: 'Overlay',
|
|
205
|
+
title: 'Title',
|
|
206
|
+
description: 'Description',
|
|
207
|
+
close: 'Close',
|
|
208
|
+
header: 'Header',
|
|
209
|
+
body: 'Content',
|
|
210
|
+
footer: 'Footer',
|
|
211
|
+
item: 'Item',
|
|
212
|
+
list: 'List',
|
|
213
|
+
value: 'Value',
|
|
214
|
+
icon: 'Icon',
|
|
215
|
+
};
|
|
216
|
+
return mappings[partName.toLowerCase()] || partName;
|
|
217
|
+
}
|
|
218
|
+
// ===========================================================================
|
|
219
|
+
// Simple Component Generation
|
|
220
|
+
// ===========================================================================
|
|
221
|
+
generateSimpleRadixComponent(component, primitive, mapping) {
|
|
222
|
+
const lines = [];
|
|
223
|
+
// Props interface
|
|
224
|
+
if (this.typescript) {
|
|
225
|
+
lines.push(this.generateRadixPropsInterface(component, primitive));
|
|
226
|
+
lines.push('');
|
|
227
|
+
}
|
|
228
|
+
// Component
|
|
229
|
+
const propsType = this.typescript ? `: ${component.name}Props` : '';
|
|
230
|
+
const destructuredProps = this.getDestructuredProps(component);
|
|
231
|
+
lines.push(`export const ${component.name} = React.forwardRef<`);
|
|
232
|
+
lines.push(` React.ElementRef<typeof ${primitive}.Content>,`);
|
|
233
|
+
lines.push(` ${component.name}Props`);
|
|
234
|
+
lines.push(`>(({ ${destructuredProps} }, ref) => {`);
|
|
235
|
+
lines.push(' return (');
|
|
236
|
+
lines.push(` <${primitive}.Root>`);
|
|
237
|
+
if (component.structure) {
|
|
238
|
+
lines.push(this.generateRadixStructureJSX(component.structure, primitive, 6));
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
lines.push(` <${primitive}.Content ref={ref} className={cn(className)} {...props}>`);
|
|
242
|
+
lines.push(' {children}');
|
|
243
|
+
lines.push(` </${primitive}.Content>`);
|
|
244
|
+
}
|
|
245
|
+
lines.push(` </${primitive}.Root>`);
|
|
246
|
+
lines.push(' );');
|
|
247
|
+
lines.push('});');
|
|
248
|
+
lines.push('');
|
|
249
|
+
lines.push(`${component.name}.displayName = '${component.name}';`);
|
|
250
|
+
return lines;
|
|
251
|
+
}
|
|
252
|
+
generateRadixPropsInterface(component, primitive) {
|
|
253
|
+
const lines = [];
|
|
254
|
+
const propsName = `${component.name}Props`;
|
|
255
|
+
lines.push(`export interface ${propsName} extends React.ComponentPropsWithoutRef<typeof ${primitive}.Content> {`);
|
|
256
|
+
for (const prop of component.props) {
|
|
257
|
+
const optional = prop.optional ? '?' : '';
|
|
258
|
+
lines.push(` /** ${prop.name} prop */`);
|
|
259
|
+
lines.push(` ${prop.name}${optional}: ${this.mapPropType(prop.type)};`);
|
|
260
|
+
}
|
|
261
|
+
lines.push('}');
|
|
262
|
+
return lines.join('\n');
|
|
263
|
+
}
|
|
264
|
+
generateRadixStructureJSX(structure, primitive, indent) {
|
|
265
|
+
const spaces = ' '.repeat(indent);
|
|
266
|
+
const lines = [];
|
|
267
|
+
const radixComponent = this.mapStructureToRadixComponent(structure.type, primitive);
|
|
268
|
+
const props = Object.entries(structure.props || {})
|
|
269
|
+
.map(([k, v]) => `${k}="${v}"`)
|
|
270
|
+
.join(' ');
|
|
271
|
+
if (structure.children && structure.children.length > 0) {
|
|
272
|
+
lines.push(`${spaces}<${radixComponent} ${props}>`);
|
|
273
|
+
for (const child of structure.children) {
|
|
274
|
+
lines.push(this.generateRadixStructureJSX(child, primitive, indent + 2));
|
|
275
|
+
}
|
|
276
|
+
lines.push(`${spaces}</${radixComponent}>`);
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
lines.push(`${spaces}<${radixComponent} ${props}>{children}</${radixComponent}>`);
|
|
280
|
+
}
|
|
281
|
+
return lines.join('\n');
|
|
282
|
+
}
|
|
283
|
+
mapStructureToRadixComponent(type, primitive) {
|
|
284
|
+
// Map generic structure types to Radix components
|
|
285
|
+
const mappings = {
|
|
286
|
+
overlay: `${primitive}.Overlay`,
|
|
287
|
+
content: `${primitive}.Content`,
|
|
288
|
+
title: `${primitive}.Title`,
|
|
289
|
+
description: `${primitive}.Description`,
|
|
290
|
+
close: `${primitive}.Close`,
|
|
291
|
+
trigger: `${primitive}.Trigger`,
|
|
292
|
+
};
|
|
293
|
+
return mappings[type.toLowerCase()] || `${primitive}.Content`;
|
|
294
|
+
}
|
|
295
|
+
// ===========================================================================
|
|
296
|
+
// Compound Component Generation
|
|
297
|
+
// ===========================================================================
|
|
298
|
+
generateCompoundRadixComponent(component, primitive, mapping) {
|
|
299
|
+
const lines = [];
|
|
300
|
+
// Root component interface
|
|
301
|
+
if (this.typescript) {
|
|
302
|
+
lines.push(`export interface ${component.name}Props extends React.ComponentPropsWithoutRef<typeof ${primitive}.Root> {`);
|
|
303
|
+
for (const prop of component.props) {
|
|
304
|
+
const optional = prop.optional ? '?' : '';
|
|
305
|
+
lines.push(` ${prop.name}${optional}: ${this.mapPropType(prop.type)};`);
|
|
306
|
+
}
|
|
307
|
+
lines.push('}');
|
|
308
|
+
lines.push('');
|
|
309
|
+
}
|
|
310
|
+
// Root component
|
|
311
|
+
const propsType = this.typescript ? `: ${component.name}Props` : '';
|
|
312
|
+
const destructuredProps = this.getDestructuredProps(component);
|
|
313
|
+
lines.push(`const ${component.name}Root = React.forwardRef<`);
|
|
314
|
+
lines.push(` React.ElementRef<typeof ${primitive}.Root>,`);
|
|
315
|
+
lines.push(` ${component.name}Props`);
|
|
316
|
+
lines.push(`>(({ ${destructuredProps} }, ref) => (`);
|
|
317
|
+
lines.push(` <${primitive}.Root ref={ref} className={cn(className)} {...props}>`);
|
|
318
|
+
lines.push(' {children}');
|
|
319
|
+
lines.push(` </${primitive}.Root>`);
|
|
320
|
+
lines.push('));');
|
|
321
|
+
lines.push('');
|
|
322
|
+
lines.push(`${component.name}Root.displayName = '${component.name}Root';`);
|
|
323
|
+
lines.push('');
|
|
324
|
+
// Compound parts
|
|
325
|
+
for (const part of component.compound) {
|
|
326
|
+
lines.push(...this.generateRadixCompoundPart(component.name, part, primitive));
|
|
327
|
+
lines.push('');
|
|
328
|
+
}
|
|
329
|
+
// Export compound object
|
|
330
|
+
lines.push(`export const ${component.name} = Object.assign(${component.name}Root, {`);
|
|
331
|
+
for (const part of component.compound) {
|
|
332
|
+
lines.push(` ${part.name}: ${component.name}${part.name},`);
|
|
333
|
+
}
|
|
334
|
+
lines.push('});');
|
|
335
|
+
return lines;
|
|
336
|
+
}
|
|
337
|
+
generateRadixCompoundPart(parentName, part, primitive) {
|
|
338
|
+
const lines = [];
|
|
339
|
+
const partName = `${parentName}${part.name}`;
|
|
340
|
+
const radixPart = this.mapCompoundPartToRadix(part.name);
|
|
341
|
+
const radixComponent = `${primitive}.${radixPart}`;
|
|
342
|
+
// Props interface
|
|
343
|
+
if (this.typescript) {
|
|
344
|
+
lines.push(`export interface ${partName}Props extends React.ComponentPropsWithoutRef<typeof ${radixComponent}> {`);
|
|
345
|
+
if (part.props) {
|
|
346
|
+
for (const prop of part.props) {
|
|
347
|
+
lines.push(` ${prop}?: string;`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
lines.push('}');
|
|
351
|
+
lines.push('');
|
|
352
|
+
}
|
|
353
|
+
const propsType = this.typescript ? `: ${partName}Props` : '';
|
|
354
|
+
const propsStr = part.props
|
|
355
|
+
? `{ className, children, ${part.props.join(', ')}, ...props }`
|
|
356
|
+
: '{ className, children, ...props }';
|
|
357
|
+
lines.push(`const ${partName} = React.forwardRef<`);
|
|
358
|
+
lines.push(` React.ElementRef<typeof ${radixComponent}>,`);
|
|
359
|
+
lines.push(` ${partName}Props`);
|
|
360
|
+
lines.push(`>((`);
|
|
361
|
+
lines.push(` ${propsStr}${propsType},`);
|
|
362
|
+
lines.push(` ref`);
|
|
363
|
+
lines.push(`) => (`);
|
|
364
|
+
lines.push(` <${radixComponent}`);
|
|
365
|
+
lines.push(` ref={ref}`);
|
|
366
|
+
lines.push(` className={cn('${this.toKebabCase(partName)}', className)}`);
|
|
367
|
+
lines.push(` {...props}`);
|
|
368
|
+
lines.push(` >`);
|
|
369
|
+
lines.push(' {children}');
|
|
370
|
+
lines.push(` </${radixComponent}>`);
|
|
371
|
+
lines.push(`));`);
|
|
372
|
+
lines.push('');
|
|
373
|
+
lines.push(`${partName}.displayName = '${partName}';`);
|
|
374
|
+
return lines;
|
|
375
|
+
}
|
|
376
|
+
// ===========================================================================
|
|
377
|
+
// Simple Component Generation (non-Radix)
|
|
378
|
+
// ===========================================================================
|
|
379
|
+
generateSimpleComponent(component) {
|
|
380
|
+
const lines = [];
|
|
381
|
+
const ext = this.typescript ? 'tsx' : 'jsx';
|
|
382
|
+
// Header
|
|
383
|
+
if (this.provenance) {
|
|
384
|
+
lines.push('// Generated by Kappa v2.5 CodeDNA - Simple Component Generator');
|
|
385
|
+
lines.push(`// Component: ${component.name}`);
|
|
386
|
+
lines.push('');
|
|
387
|
+
}
|
|
388
|
+
// Imports
|
|
389
|
+
lines.push("import * as React from 'react';");
|
|
390
|
+
lines.push("import { cn } from '@/lib/utils';");
|
|
391
|
+
lines.push('');
|
|
392
|
+
// Variant styles (if component has variants)
|
|
393
|
+
if (component.variants && component.variants.length > 0) {
|
|
394
|
+
lines.push('// Variant styles');
|
|
395
|
+
lines.push(`const ${component.name.toLowerCase()}Variants = {`);
|
|
396
|
+
for (const variant of component.variants) {
|
|
397
|
+
const className = this.getVariantClassName(component.name, variant.name);
|
|
398
|
+
lines.push(` ${variant.name}: '${className}',`);
|
|
399
|
+
}
|
|
400
|
+
lines.push('} as const;');
|
|
401
|
+
lines.push('');
|
|
402
|
+
lines.push(`type ${component.name}Variant = keyof typeof ${component.name.toLowerCase()}Variants;`);
|
|
403
|
+
lines.push('');
|
|
404
|
+
}
|
|
405
|
+
// Props interface
|
|
406
|
+
if (this.typescript) {
|
|
407
|
+
lines.push(`export interface ${component.name}Props extends React.HTMLAttributes<HTMLElement> {`);
|
|
408
|
+
for (const prop of component.props) {
|
|
409
|
+
const optional = prop.optional ? '?' : '';
|
|
410
|
+
lines.push(` /** ${prop.name} prop */`);
|
|
411
|
+
lines.push(` ${prop.name}${optional}: ${this.mapPropType(prop.type)};`);
|
|
412
|
+
}
|
|
413
|
+
if (component.variants && component.variants.length > 0) {
|
|
414
|
+
const defaultVariant = component.variants[0].name;
|
|
415
|
+
lines.push(` /** Visual variant */`);
|
|
416
|
+
lines.push(` variant?: ${component.name}Variant;`);
|
|
417
|
+
}
|
|
418
|
+
lines.push('}');
|
|
419
|
+
lines.push('');
|
|
420
|
+
}
|
|
421
|
+
// Component
|
|
422
|
+
const destructuredProps = this.getSimpleDestructuredProps(component);
|
|
423
|
+
const elementType = this.getElementType(component.name);
|
|
424
|
+
lines.push(`export const ${component.name} = React.forwardRef<`);
|
|
425
|
+
lines.push(` HTML${elementType}Element,`);
|
|
426
|
+
lines.push(` ${component.name}Props`);
|
|
427
|
+
lines.push(`>(({ ${destructuredProps} }, ref) => {`);
|
|
428
|
+
// Variant handling
|
|
429
|
+
if (component.variants && component.variants.length > 0) {
|
|
430
|
+
const defaultVariant = component.variants[0].name;
|
|
431
|
+
lines.push(` const variantClass = ${component.name.toLowerCase()}Variants[variant ?? '${defaultVariant}'];`);
|
|
432
|
+
lines.push('');
|
|
433
|
+
}
|
|
434
|
+
// Return statement
|
|
435
|
+
lines.push(' return (');
|
|
436
|
+
lines.push(` <${this.getElementTag(component.name)}`);
|
|
437
|
+
lines.push(' ref={ref}');
|
|
438
|
+
if (component.variants && component.variants.length > 0) {
|
|
439
|
+
lines.push(` className={cn('${this.toKebabCase(component.name)}', variantClass, className)}`);
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
lines.push(` className={cn('${this.toKebabCase(component.name)}', className)}`);
|
|
443
|
+
}
|
|
444
|
+
// Add event handlers from props
|
|
445
|
+
for (const prop of component.props) {
|
|
446
|
+
if (prop.name.startsWith('on')) {
|
|
447
|
+
lines.push(` ${prop.name}={${prop.name}}`);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
lines.push(' {...props}');
|
|
451
|
+
lines.push(' >');
|
|
452
|
+
// Add label/children content
|
|
453
|
+
const labelProp = component.props.find(p => p.name === 'label');
|
|
454
|
+
if (labelProp) {
|
|
455
|
+
lines.push(' {label}');
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
lines.push(' {children}');
|
|
459
|
+
}
|
|
460
|
+
lines.push(` </${this.getElementTag(component.name)}>`);
|
|
461
|
+
lines.push(' );');
|
|
462
|
+
lines.push('});');
|
|
463
|
+
lines.push('');
|
|
464
|
+
lines.push(`${component.name}.displayName = '${component.name}';`);
|
|
465
|
+
return {
|
|
466
|
+
path: `${this.basePath}/${this.toKebabCase(component.name)}.${ext}`,
|
|
467
|
+
content: lines.join('\n'),
|
|
468
|
+
primitive: 'Toggle', // Placeholder since this isn't actually a Radix primitive
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
getSimpleDestructuredProps(component) {
|
|
472
|
+
const propNames = component.props.map((p) => p.name);
|
|
473
|
+
if (component.variants && component.variants.length > 0) {
|
|
474
|
+
propNames.push('variant');
|
|
475
|
+
}
|
|
476
|
+
propNames.push('className', 'children', '...props');
|
|
477
|
+
return propNames.join(', ');
|
|
478
|
+
}
|
|
479
|
+
getVariantClassName(componentName, variantName) {
|
|
480
|
+
const base = this.toKebabCase(componentName);
|
|
481
|
+
const styles = {
|
|
482
|
+
button: {
|
|
483
|
+
primary: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
|
484
|
+
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
|
485
|
+
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
|
486
|
+
outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
|
487
|
+
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
|
488
|
+
link: 'text-primary underline-offset-4 hover:underline',
|
|
489
|
+
},
|
|
490
|
+
card: {
|
|
491
|
+
default: 'border bg-card text-card-foreground shadow-sm',
|
|
492
|
+
outlined: 'border-2 bg-background',
|
|
493
|
+
elevated: 'bg-card text-card-foreground shadow-lg',
|
|
494
|
+
},
|
|
495
|
+
badge: {
|
|
496
|
+
default: 'border-transparent bg-primary text-primary-foreground',
|
|
497
|
+
secondary: 'border-transparent bg-secondary text-secondary-foreground',
|
|
498
|
+
outline: 'text-foreground',
|
|
499
|
+
destructive: 'border-transparent bg-destructive text-destructive-foreground',
|
|
500
|
+
},
|
|
501
|
+
};
|
|
502
|
+
const componentStyles = styles[componentName.toLowerCase()];
|
|
503
|
+
if (componentStyles && componentStyles[variantName]) {
|
|
504
|
+
return componentStyles[variantName];
|
|
505
|
+
}
|
|
506
|
+
// Generic variant styles
|
|
507
|
+
return `${base}--${variantName}`;
|
|
508
|
+
}
|
|
509
|
+
getElementType(componentName) {
|
|
510
|
+
const mappings = {
|
|
511
|
+
button: 'Button',
|
|
512
|
+
input: 'Input',
|
|
513
|
+
textarea: 'TextArea',
|
|
514
|
+
select: 'Select',
|
|
515
|
+
card: 'Div',
|
|
516
|
+
badge: 'Div',
|
|
517
|
+
avatar: 'Div',
|
|
518
|
+
alert: 'Div',
|
|
519
|
+
};
|
|
520
|
+
return mappings[componentName.toLowerCase()] || 'Div';
|
|
521
|
+
}
|
|
522
|
+
getElementTag(componentName) {
|
|
523
|
+
const mappings = {
|
|
524
|
+
button: 'button',
|
|
525
|
+
input: 'input',
|
|
526
|
+
textarea: 'textarea',
|
|
527
|
+
select: 'select',
|
|
528
|
+
link: 'a',
|
|
529
|
+
};
|
|
530
|
+
return mappings[componentName.toLowerCase()] || 'div';
|
|
531
|
+
}
|
|
532
|
+
// ===========================================================================
|
|
533
|
+
// Helpers
|
|
534
|
+
// ===========================================================================
|
|
535
|
+
getDestructuredProps(component) {
|
|
536
|
+
const propNames = component.props.map((p) => p.name);
|
|
537
|
+
propNames.push('className', 'children', '...props');
|
|
538
|
+
return propNames.join(', ');
|
|
539
|
+
}
|
|
540
|
+
mapPropType(type) {
|
|
541
|
+
const typeMap = {
|
|
542
|
+
string: 'string',
|
|
543
|
+
number: 'number',
|
|
544
|
+
boolean: 'boolean',
|
|
545
|
+
node: 'React.ReactNode',
|
|
546
|
+
element: 'React.ReactElement',
|
|
547
|
+
func: '() => void',
|
|
548
|
+
array: 'unknown[]',
|
|
549
|
+
object: 'Record<string, unknown>',
|
|
550
|
+
};
|
|
551
|
+
return typeMap[type] || type;
|
|
552
|
+
}
|
|
553
|
+
toKebabCase(str) {
|
|
554
|
+
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
// =============================================================================
|
|
558
|
+
// Convenience Function
|
|
559
|
+
// =============================================================================
|
|
560
|
+
/**
|
|
561
|
+
* Generate Radix UI component files from Kappa ComponentBlock nodes
|
|
562
|
+
*/
|
|
563
|
+
export function generateRadixComponents(components, options = {}) {
|
|
564
|
+
const generator = new KappaRadixGenerator(options);
|
|
565
|
+
return generator.generate(components);
|
|
566
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { EntityBlock } from './kappa-ast.js';
|
|
2
|
+
export type TypeORMDialect = 'postgres' | 'mysql' | 'sqlite';
|
|
3
|
+
export interface TypeORMGeneratorOptions {
|
|
4
|
+
/** Database dialect (default: postgres) */
|
|
5
|
+
dialect?: TypeORMDialect;
|
|
6
|
+
/** Generate repository classes (default: false) */
|
|
7
|
+
repositories?: boolean;
|
|
8
|
+
/** Generate migration files (default: true) */
|
|
9
|
+
migrations?: boolean;
|
|
10
|
+
/** Add provenance comments (default: true) */
|
|
11
|
+
provenance?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface GeneratedTypeORM {
|
|
14
|
+
/** Entity class files (map of entity name to content) */
|
|
15
|
+
entities: Record<string, string>;
|
|
16
|
+
/** Repository files (if enabled) */
|
|
17
|
+
repositories?: Record<string, string>;
|
|
18
|
+
/** Migration file content (if enabled) */
|
|
19
|
+
migration?: string;
|
|
20
|
+
/** Data source configuration */
|
|
21
|
+
dataSource: string;
|
|
22
|
+
}
|
|
23
|
+
export declare class KappaTypeORMGenerator {
|
|
24
|
+
private dialect;
|
|
25
|
+
private provenance;
|
|
26
|
+
private generateRepositories;
|
|
27
|
+
private generateMigrations;
|
|
28
|
+
constructor(options?: TypeORMGeneratorOptions);
|
|
29
|
+
/**
|
|
30
|
+
* Generate TypeORM entities from entity blocks
|
|
31
|
+
*/
|
|
32
|
+
generate(entities: EntityBlock[]): GeneratedTypeORM;
|
|
33
|
+
private generateEntity;
|
|
34
|
+
private generateEntityImports;
|
|
35
|
+
private generateEntityClass;
|
|
36
|
+
private generatePrimaryKey;
|
|
37
|
+
private generateField;
|
|
38
|
+
private generateRelationship;
|
|
39
|
+
private getColumnType;
|
|
40
|
+
private getTSType;
|
|
41
|
+
/**
|
|
42
|
+
* Determine the foreign key column type based on the related entity's primary key
|
|
43
|
+
*/
|
|
44
|
+
private getForeignKeyType;
|
|
45
|
+
private generateDataSource;
|
|
46
|
+
private generateRepositoryFiles;
|
|
47
|
+
private generateRepository;
|
|
48
|
+
private generateMigrationFile;
|
|
49
|
+
private generateCreateTableSQL;
|
|
50
|
+
private generateColumnSQL;
|
|
51
|
+
private generateCreateIndexSQL;
|
|
52
|
+
private toSnakeCase;
|
|
53
|
+
private toCamelCase;
|
|
54
|
+
private toLowerCamelCase;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Generate TypeORM entities from Kappa entity blocks
|
|
58
|
+
*/
|
|
59
|
+
export declare function generateTypeORMEntities(entities: EntityBlock[], options?: TypeORMGeneratorOptions): GeneratedTypeORM;
|