create-mantiq 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.ts +33 -13
- package/src/ui/shadcn.ts +57 -448
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -78,7 +78,7 @@ if (!isCI && !kit) {
|
|
|
78
78
|
|
|
79
79
|
// Framework selection
|
|
80
80
|
const framework = await term.select('Which framework would you like to use?', [
|
|
81
|
-
{ value: 'react', label: 'React'
|
|
81
|
+
{ value: 'react', label: 'React' },
|
|
82
82
|
{ value: 'vue', label: 'Vue' },
|
|
83
83
|
{ value: 'svelte', label: 'Svelte' },
|
|
84
84
|
{ value: 'none', label: 'None', hint: 'API only' },
|
|
@@ -133,6 +133,29 @@ const install = Bun.spawn(['bun', 'install'], {
|
|
|
133
133
|
await install.exited
|
|
134
134
|
spin.stop('Dependencies installed')
|
|
135
135
|
|
|
136
|
+
// ── Install shadcn components (if selected) ─────────────────────────────────
|
|
137
|
+
if (ui === 'shadcn' && kit === 'react') {
|
|
138
|
+
const shadcnSpin = term.spinner('Installing shadcn/ui components')
|
|
139
|
+
|
|
140
|
+
const run = async (args: string[]) => {
|
|
141
|
+
const p = Bun.spawn(['bunx', '--bun', ...args], {
|
|
142
|
+
cwd: projectDir,
|
|
143
|
+
stdout: 'pipe',
|
|
144
|
+
stderr: 'pipe',
|
|
145
|
+
env: { ...process.env, CI: 'true' },
|
|
146
|
+
})
|
|
147
|
+
await p.exited
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Init shadcn
|
|
151
|
+
await run(['shadcn@latest', 'init', '--defaults', '--force'])
|
|
152
|
+
|
|
153
|
+
// Install core components + sidebar
|
|
154
|
+
await run(['shadcn@latest', 'add', 'button', 'input', 'label', 'card', 'badge', 'table', 'avatar', 'separator', 'dropdown-menu', 'sheet', 'tooltip', 'sidebar', '--overwrite', '-y'])
|
|
155
|
+
|
|
156
|
+
shadcnSpin.stop('shadcn/ui components installed')
|
|
157
|
+
}
|
|
158
|
+
|
|
136
159
|
// ── Build frontend (if kit) ─────────────────────────────────────────────────
|
|
137
160
|
if (kit) {
|
|
138
161
|
const buildSpin = term.spinner('Building frontend assets')
|
|
@@ -168,15 +191,12 @@ if (!noGit) {
|
|
|
168
191
|
}
|
|
169
192
|
|
|
170
193
|
// ── Done ─────────────────────────────────────────────────────────────────────
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
]
|
|
181
|
-
|
|
182
|
-
term.box(summaryLines)
|
|
194
|
+
console.log(`\n ${emerald('✓')} ${bold(projectName)} created\n`)
|
|
195
|
+
console.log(` ${dim('Framework')} ${kit ? bold(kit.charAt(0).toUpperCase() + kit.slice(1)) : dim('None (API only)')}`)
|
|
196
|
+
if (kit === 'react') console.log(` ${dim('UI Kit')} ${ui === 'shadcn' ? bold('shadcn/ui') : dim('Plain Tailwind')}`)
|
|
197
|
+
console.log(`\n ${dim('Next steps:')}\n`)
|
|
198
|
+
console.log(` cd ${projectName}`)
|
|
199
|
+
console.log(` bun mantiq migrate`)
|
|
200
|
+
console.log(` bun run dev\n`)
|
|
201
|
+
|
|
202
|
+
process.exit(0)
|
package/src/ui/shadcn.ts
CHANGED
|
@@ -6,6 +6,30 @@ export function getShadcnTemplates(ctx: TemplateContext): {
|
|
|
6
6
|
} {
|
|
7
7
|
return {
|
|
8
8
|
files: {
|
|
9
|
+
// ── tsconfig override with @/ path alias ──────────────────────────────
|
|
10
|
+
'tsconfig.json': JSON.stringify({
|
|
11
|
+
compilerOptions: {
|
|
12
|
+
target: 'ESNext',
|
|
13
|
+
module: 'ESNext',
|
|
14
|
+
moduleResolution: 'bundler',
|
|
15
|
+
lib: ['ESNext', 'DOM', 'DOM.Iterable'],
|
|
16
|
+
types: ['bun-types'],
|
|
17
|
+
jsx: 'react-jsx',
|
|
18
|
+
strict: true,
|
|
19
|
+
noImplicitAny: true,
|
|
20
|
+
strictNullChecks: true,
|
|
21
|
+
noUncheckedIndexedAccess: true,
|
|
22
|
+
noImplicitOverride: true,
|
|
23
|
+
allowImportingTsExtensions: true,
|
|
24
|
+
noEmit: true,
|
|
25
|
+
skipLibCheck: true,
|
|
26
|
+
baseUrl: '.',
|
|
27
|
+
paths: { '@/*': ['./src/*'] },
|
|
28
|
+
},
|
|
29
|
+
include: ['./**/*'],
|
|
30
|
+
exclude: ['node_modules'],
|
|
31
|
+
}, null, 2) + '\n',
|
|
32
|
+
|
|
9
33
|
// ── cn helper ───────────────────────────────────────────────────────────
|
|
10
34
|
'src/lib/utils.ts': `import { type ClassValue, clsx } from 'clsx'
|
|
11
35
|
import { twMerge } from 'tailwind-merge'
|
|
@@ -28,8 +52,11 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
28
52
|
cssVariables: true,
|
|
29
53
|
},
|
|
30
54
|
aliases: {
|
|
31
|
-
components: '
|
|
32
|
-
utils: '
|
|
55
|
+
components: '@/components',
|
|
56
|
+
utils: '@/lib/utils',
|
|
57
|
+
ui: '@/components/ui',
|
|
58
|
+
lib: '@/lib',
|
|
59
|
+
hooks: '@/hooks',
|
|
33
60
|
},
|
|
34
61
|
}, null, 2) + '\n',
|
|
35
62
|
|
|
@@ -37,6 +64,17 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
37
64
|
'src/style.css': `@import "tailwindcss";
|
|
38
65
|
@custom-variant dark (&:where(.dark, .dark *));
|
|
39
66
|
|
|
67
|
+
/*
|
|
68
|
+
* shadcn/ui theme — default: emerald
|
|
69
|
+
*
|
|
70
|
+
* To swap themes, visit https://ui.shadcn.com/themes
|
|
71
|
+
* Pick a theme, copy the CSS variables, and replace
|
|
72
|
+
* the :root and .dark blocks below.
|
|
73
|
+
*
|
|
74
|
+
* Or install a community theme:
|
|
75
|
+
* bunx --bun shadcn@latest add @ss-themes/caffeine
|
|
76
|
+
*/
|
|
77
|
+
|
|
40
78
|
@theme inline {
|
|
41
79
|
--color-background: var(--background);
|
|
42
80
|
--color-foreground: var(--foreground);
|
|
@@ -163,444 +201,17 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
163
201
|
.animate-fade-up { animation: fadeUp 0.4s ease-out; }
|
|
164
202
|
`,
|
|
165
203
|
|
|
166
|
-
//
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
import { cva, type VariantProps } from 'class-variance-authority'
|
|
170
|
-
import { cn } from '../../lib/utils.ts'
|
|
171
|
-
|
|
172
|
-
const buttonVariants = cva(
|
|
173
|
-
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
|
174
|
-
{
|
|
175
|
-
variants: {
|
|
176
|
-
variant: {
|
|
177
|
-
default:
|
|
178
|
-
'bg-primary text-primary-foreground shadow hover:bg-primary/90',
|
|
179
|
-
destructive:
|
|
180
|
-
'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
|
|
181
|
-
outline:
|
|
182
|
-
'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
|
|
183
|
-
secondary:
|
|
184
|
-
'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
|
|
185
|
-
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
|
186
|
-
link: 'text-primary underline-offset-4 hover:underline',
|
|
187
|
-
},
|
|
188
|
-
size: {
|
|
189
|
-
default: 'h-9 px-4 py-2',
|
|
190
|
-
sm: 'h-8 rounded-md px-3 text-xs',
|
|
191
|
-
lg: 'h-10 rounded-md px-8',
|
|
192
|
-
icon: 'h-9 w-9',
|
|
193
|
-
},
|
|
194
|
-
},
|
|
195
|
-
defaultVariants: {
|
|
196
|
-
variant: 'default',
|
|
197
|
-
size: 'default',
|
|
198
|
-
},
|
|
199
|
-
}
|
|
200
|
-
)
|
|
201
|
-
|
|
202
|
-
export interface ButtonProps
|
|
203
|
-
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
204
|
-
VariantProps<typeof buttonVariants> {
|
|
205
|
-
asChild?: boolean
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
209
|
-
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
210
|
-
const Comp = asChild ? Slot : 'button'
|
|
211
|
-
return (
|
|
212
|
-
<Comp
|
|
213
|
-
className={cn(buttonVariants({ variant, size, className }))}
|
|
214
|
-
ref={ref}
|
|
215
|
-
{...props}
|
|
216
|
-
/>
|
|
217
|
-
)
|
|
218
|
-
}
|
|
219
|
-
)
|
|
220
|
-
Button.displayName = 'Button'
|
|
221
|
-
|
|
222
|
-
export { Button, buttonVariants }
|
|
223
|
-
`,
|
|
224
|
-
|
|
225
|
-
// ── Input component ─────────────────────────────────────────────────────
|
|
226
|
-
'src/components/ui/input.tsx': `import * as React from 'react'
|
|
227
|
-
import { cn } from '../../lib/utils.ts'
|
|
228
|
-
|
|
229
|
-
export interface InputProps
|
|
230
|
-
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
|
231
|
-
|
|
232
|
-
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
233
|
-
({ className, type, ...props }, ref) => {
|
|
234
|
-
return (
|
|
235
|
-
<input
|
|
236
|
-
type={type}
|
|
237
|
-
className={cn(
|
|
238
|
-
'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
|
|
239
|
-
className
|
|
240
|
-
)}
|
|
241
|
-
ref={ref}
|
|
242
|
-
{...props}
|
|
243
|
-
/>
|
|
244
|
-
)
|
|
245
|
-
}
|
|
246
|
-
)
|
|
247
|
-
Input.displayName = 'Input'
|
|
248
|
-
|
|
249
|
-
export { Input }
|
|
250
|
-
`,
|
|
251
|
-
|
|
252
|
-
// ── Label component ─────────────────────────────────────────────────────
|
|
253
|
-
'src/components/ui/label.tsx': `import * as React from 'react'
|
|
254
|
-
import * as LabelPrimitive from '@radix-ui/react-label'
|
|
255
|
-
import { cva, type VariantProps } from 'class-variance-authority'
|
|
256
|
-
import { cn } from '../../lib/utils.ts'
|
|
257
|
-
|
|
258
|
-
const labelVariants = cva(
|
|
259
|
-
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'
|
|
260
|
-
)
|
|
261
|
-
|
|
262
|
-
const Label = React.forwardRef<
|
|
263
|
-
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
264
|
-
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
|
265
|
-
VariantProps<typeof labelVariants>
|
|
266
|
-
>(({ className, ...props }, ref) => (
|
|
267
|
-
<LabelPrimitive.Root
|
|
268
|
-
ref={ref}
|
|
269
|
-
className={cn(labelVariants(), className)}
|
|
270
|
-
{...props}
|
|
271
|
-
/>
|
|
272
|
-
))
|
|
273
|
-
Label.displayName = LabelPrimitive.Root.displayName
|
|
274
|
-
|
|
275
|
-
export { Label }
|
|
276
|
-
`,
|
|
277
|
-
|
|
278
|
-
// ── Card component ──────────────────────────────────────────────────────
|
|
279
|
-
'src/components/ui/card.tsx': `import * as React from 'react'
|
|
280
|
-
import { cn } from '../../lib/utils.ts'
|
|
281
|
-
|
|
282
|
-
const Card = React.forwardRef<
|
|
283
|
-
HTMLDivElement,
|
|
284
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
285
|
-
>(({ className, ...props }, ref) => (
|
|
286
|
-
<div
|
|
287
|
-
ref={ref}
|
|
288
|
-
className={cn(
|
|
289
|
-
'rounded-xl border border-border bg-card text-card-foreground shadow',
|
|
290
|
-
className
|
|
291
|
-
)}
|
|
292
|
-
{...props}
|
|
293
|
-
/>
|
|
294
|
-
))
|
|
295
|
-
Card.displayName = 'Card'
|
|
296
|
-
|
|
297
|
-
const CardHeader = React.forwardRef<
|
|
298
|
-
HTMLDivElement,
|
|
299
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
300
|
-
>(({ className, ...props }, ref) => (
|
|
301
|
-
<div
|
|
302
|
-
ref={ref}
|
|
303
|
-
className={cn('flex flex-col space-y-1.5 p-6', className)}
|
|
304
|
-
{...props}
|
|
305
|
-
/>
|
|
306
|
-
))
|
|
307
|
-
CardHeader.displayName = 'CardHeader'
|
|
308
|
-
|
|
309
|
-
const CardTitle = React.forwardRef<
|
|
310
|
-
HTMLDivElement,
|
|
311
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
312
|
-
>(({ className, ...props }, ref) => (
|
|
313
|
-
<div
|
|
314
|
-
ref={ref}
|
|
315
|
-
className={cn('font-semibold leading-none tracking-tight', className)}
|
|
316
|
-
{...props}
|
|
317
|
-
/>
|
|
318
|
-
))
|
|
319
|
-
CardTitle.displayName = 'CardTitle'
|
|
320
|
-
|
|
321
|
-
const CardDescription = React.forwardRef<
|
|
322
|
-
HTMLDivElement,
|
|
323
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
324
|
-
>(({ className, ...props }, ref) => (
|
|
325
|
-
<div
|
|
326
|
-
ref={ref}
|
|
327
|
-
className={cn('text-sm text-muted-foreground', className)}
|
|
328
|
-
{...props}
|
|
329
|
-
/>
|
|
330
|
-
))
|
|
331
|
-
CardDescription.displayName = 'CardDescription'
|
|
332
|
-
|
|
333
|
-
const CardContent = React.forwardRef<
|
|
334
|
-
HTMLDivElement,
|
|
335
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
336
|
-
>(({ className, ...props }, ref) => (
|
|
337
|
-
<div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
|
|
338
|
-
))
|
|
339
|
-
CardContent.displayName = 'CardContent'
|
|
340
|
-
|
|
341
|
-
const CardFooter = React.forwardRef<
|
|
342
|
-
HTMLDivElement,
|
|
343
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
344
|
-
>(({ className, ...props }, ref) => (
|
|
345
|
-
<div
|
|
346
|
-
ref={ref}
|
|
347
|
-
className={cn('flex items-center p-6 pt-0', className)}
|
|
348
|
-
{...props}
|
|
349
|
-
/>
|
|
350
|
-
))
|
|
351
|
-
CardFooter.displayName = 'CardFooter'
|
|
352
|
-
|
|
353
|
-
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
|
354
|
-
`,
|
|
355
|
-
|
|
356
|
-
// ── Badge component ─────────────────────────────────────────────────────
|
|
357
|
-
'src/components/ui/badge.tsx': `import * as React from 'react'
|
|
358
|
-
import { cva, type VariantProps } from 'class-variance-authority'
|
|
359
|
-
import { cn } from '../../lib/utils.ts'
|
|
360
|
-
|
|
361
|
-
const badgeVariants = cva(
|
|
362
|
-
'inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
|
363
|
-
{
|
|
364
|
-
variants: {
|
|
365
|
-
variant: {
|
|
366
|
-
default:
|
|
367
|
-
'border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80',
|
|
368
|
-
secondary:
|
|
369
|
-
'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
|
370
|
-
destructive:
|
|
371
|
-
'border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80',
|
|
372
|
-
outline: 'text-foreground',
|
|
373
|
-
},
|
|
374
|
-
},
|
|
375
|
-
defaultVariants: {
|
|
376
|
-
variant: 'default',
|
|
377
|
-
},
|
|
378
|
-
}
|
|
379
|
-
)
|
|
380
|
-
|
|
381
|
-
export interface BadgeProps
|
|
382
|
-
extends React.HTMLAttributes<HTMLDivElement>,
|
|
383
|
-
VariantProps<typeof badgeVariants> {}
|
|
384
|
-
|
|
385
|
-
function Badge({ className, variant, ...props }: BadgeProps) {
|
|
386
|
-
return (
|
|
387
|
-
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
|
388
|
-
)
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
export { Badge, badgeVariants }
|
|
392
|
-
`,
|
|
393
|
-
|
|
394
|
-
// ── Table component ─────────────────────────────────────────────────────
|
|
395
|
-
'src/components/ui/table.tsx': `import * as React from 'react'
|
|
396
|
-
import { cn } from '../../lib/utils.ts'
|
|
397
|
-
|
|
398
|
-
const Table = React.forwardRef<
|
|
399
|
-
HTMLTableElement,
|
|
400
|
-
React.HTMLAttributes<HTMLTableElement>
|
|
401
|
-
>(({ className, ...props }, ref) => (
|
|
402
|
-
<div className="relative w-full overflow-auto">
|
|
403
|
-
<table
|
|
404
|
-
ref={ref}
|
|
405
|
-
className={cn('w-full caption-bottom text-sm', className)}
|
|
406
|
-
{...props}
|
|
407
|
-
/>
|
|
408
|
-
</div>
|
|
409
|
-
))
|
|
410
|
-
Table.displayName = 'Table'
|
|
411
|
-
|
|
412
|
-
const TableHeader = React.forwardRef<
|
|
413
|
-
HTMLTableSectionElement,
|
|
414
|
-
React.HTMLAttributes<HTMLTableSectionElement>
|
|
415
|
-
>(({ className, ...props }, ref) => (
|
|
416
|
-
<thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} />
|
|
417
|
-
))
|
|
418
|
-
TableHeader.displayName = 'TableHeader'
|
|
419
|
-
|
|
420
|
-
const TableBody = React.forwardRef<
|
|
421
|
-
HTMLTableSectionElement,
|
|
422
|
-
React.HTMLAttributes<HTMLTableSectionElement>
|
|
423
|
-
>(({ className, ...props }, ref) => (
|
|
424
|
-
<tbody
|
|
425
|
-
ref={ref}
|
|
426
|
-
className={cn('[&_tr:last-child]:border-0', className)}
|
|
427
|
-
{...props}
|
|
428
|
-
/>
|
|
429
|
-
))
|
|
430
|
-
TableBody.displayName = 'TableBody'
|
|
431
|
-
|
|
432
|
-
const TableFooter = React.forwardRef<
|
|
433
|
-
HTMLTableSectionElement,
|
|
434
|
-
React.HTMLAttributes<HTMLTableSectionElement>
|
|
435
|
-
>(({ className, ...props }, ref) => (
|
|
436
|
-
<tfoot
|
|
437
|
-
ref={ref}
|
|
438
|
-
className={cn(
|
|
439
|
-
'border-t bg-muted/50 font-medium [&>tr]:last:border-b-0',
|
|
440
|
-
className
|
|
441
|
-
)}
|
|
442
|
-
{...props}
|
|
443
|
-
/>
|
|
444
|
-
))
|
|
445
|
-
TableFooter.displayName = 'TableFooter'
|
|
446
|
-
|
|
447
|
-
const TableRow = React.forwardRef<
|
|
448
|
-
HTMLTableRowElement,
|
|
449
|
-
React.HTMLAttributes<HTMLTableRowElement>
|
|
450
|
-
>(({ className, ...props }, ref) => (
|
|
451
|
-
<tr
|
|
452
|
-
ref={ref}
|
|
453
|
-
className={cn(
|
|
454
|
-
'border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted',
|
|
455
|
-
className
|
|
456
|
-
)}
|
|
457
|
-
{...props}
|
|
458
|
-
/>
|
|
459
|
-
))
|
|
460
|
-
TableRow.displayName = 'TableRow'
|
|
461
|
-
|
|
462
|
-
const TableHead = React.forwardRef<
|
|
463
|
-
HTMLTableCellElement,
|
|
464
|
-
React.ThHTMLAttributes<HTMLTableCellElement>
|
|
465
|
-
>(({ className, ...props }, ref) => (
|
|
466
|
-
<th
|
|
467
|
-
ref={ref}
|
|
468
|
-
className={cn(
|
|
469
|
-
'h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
|
|
470
|
-
className
|
|
471
|
-
)}
|
|
472
|
-
{...props}
|
|
473
|
-
/>
|
|
474
|
-
))
|
|
475
|
-
TableHead.displayName = 'TableHead'
|
|
476
|
-
|
|
477
|
-
const TableCell = React.forwardRef<
|
|
478
|
-
HTMLTableCellElement,
|
|
479
|
-
React.TdHTMLAttributes<HTMLTableCellElement>
|
|
480
|
-
>(({ className, ...props }, ref) => (
|
|
481
|
-
<td
|
|
482
|
-
ref={ref}
|
|
483
|
-
className={cn(
|
|
484
|
-
'p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
|
|
485
|
-
className
|
|
486
|
-
)}
|
|
487
|
-
{...props}
|
|
488
|
-
/>
|
|
489
|
-
))
|
|
490
|
-
TableCell.displayName = 'TableCell'
|
|
491
|
-
|
|
492
|
-
const TableCaption = React.forwardRef<
|
|
493
|
-
HTMLTableCaptionElement,
|
|
494
|
-
React.HTMLAttributes<HTMLTableCaptionElement>
|
|
495
|
-
>(({ className, ...props }, ref) => (
|
|
496
|
-
<caption
|
|
497
|
-
ref={ref}
|
|
498
|
-
className={cn('mt-4 text-sm text-muted-foreground', className)}
|
|
499
|
-
{...props}
|
|
500
|
-
/>
|
|
501
|
-
))
|
|
502
|
-
TableCaption.displayName = 'TableCaption'
|
|
503
|
-
|
|
504
|
-
export {
|
|
505
|
-
Table,
|
|
506
|
-
TableHeader,
|
|
507
|
-
TableBody,
|
|
508
|
-
TableFooter,
|
|
509
|
-
TableHead,
|
|
510
|
-
TableRow,
|
|
511
|
-
TableCell,
|
|
512
|
-
TableCaption,
|
|
513
|
-
}
|
|
514
|
-
`,
|
|
515
|
-
|
|
516
|
-
// ── Avatar component ────────────────────────────────────────────────────
|
|
517
|
-
'src/components/ui/avatar.tsx': `import * as React from 'react'
|
|
518
|
-
import { cn } from '../../lib/utils.ts'
|
|
519
|
-
|
|
520
|
-
const Avatar = React.forwardRef<
|
|
521
|
-
HTMLDivElement,
|
|
522
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
523
|
-
>(({ className, ...props }, ref) => (
|
|
524
|
-
<div
|
|
525
|
-
ref={ref}
|
|
526
|
-
className={cn(
|
|
527
|
-
'relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full',
|
|
528
|
-
className
|
|
529
|
-
)}
|
|
530
|
-
{...props}
|
|
531
|
-
/>
|
|
532
|
-
))
|
|
533
|
-
Avatar.displayName = 'Avatar'
|
|
534
|
-
|
|
535
|
-
const AvatarImage = React.forwardRef<
|
|
536
|
-
HTMLImageElement,
|
|
537
|
-
React.ImgHTMLAttributes<HTMLImageElement>
|
|
538
|
-
>(({ className, ...props }, ref) => (
|
|
539
|
-
<img
|
|
540
|
-
ref={ref}
|
|
541
|
-
className={cn('aspect-square h-full w-full', className)}
|
|
542
|
-
{...props}
|
|
543
|
-
/>
|
|
544
|
-
))
|
|
545
|
-
AvatarImage.displayName = 'AvatarImage'
|
|
546
|
-
|
|
547
|
-
const AvatarFallback = React.forwardRef<
|
|
548
|
-
HTMLDivElement,
|
|
549
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
550
|
-
>(({ className, ...props }, ref) => (
|
|
551
|
-
<div
|
|
552
|
-
ref={ref}
|
|
553
|
-
className={cn(
|
|
554
|
-
'flex h-full w-full items-center justify-center rounded-full bg-muted',
|
|
555
|
-
className
|
|
556
|
-
)}
|
|
557
|
-
{...props}
|
|
558
|
-
/>
|
|
559
|
-
))
|
|
560
|
-
AvatarFallback.displayName = 'AvatarFallback'
|
|
561
|
-
|
|
562
|
-
export { Avatar, AvatarImage, AvatarFallback }
|
|
563
|
-
`,
|
|
564
|
-
|
|
565
|
-
// ── Separator component ─────────────────────────────────────────────────
|
|
566
|
-
'src/components/ui/separator.tsx': `import * as React from 'react'
|
|
567
|
-
import { cn } from '../../lib/utils.ts'
|
|
568
|
-
|
|
569
|
-
interface SeparatorProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
570
|
-
orientation?: 'horizontal' | 'vertical'
|
|
571
|
-
decorative?: boolean
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
const Separator = React.forwardRef<HTMLDivElement, SeparatorProps>(
|
|
575
|
-
(
|
|
576
|
-
{ className, orientation = 'horizontal', decorative = true, ...props },
|
|
577
|
-
ref
|
|
578
|
-
) => (
|
|
579
|
-
<div
|
|
580
|
-
ref={ref}
|
|
581
|
-
role={decorative ? 'none' : 'separator'}
|
|
582
|
-
aria-orientation={decorative ? undefined : orientation}
|
|
583
|
-
className={cn(
|
|
584
|
-
'shrink-0 bg-border',
|
|
585
|
-
orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
|
|
586
|
-
className
|
|
587
|
-
)}
|
|
588
|
-
{...props}
|
|
589
|
-
/>
|
|
590
|
-
)
|
|
591
|
-
)
|
|
592
|
-
Separator.displayName = 'Separator'
|
|
593
|
-
|
|
594
|
-
export { Separator }
|
|
595
|
-
`,
|
|
204
|
+
// Components (button, input, label, card, badge, table, avatar,
|
|
205
|
+
// separator, sidebar, etc.) are installed by the shadcn CLI during
|
|
206
|
+
// scaffold — no hand-rolled templates needed.
|
|
596
207
|
|
|
597
208
|
// ── Login page (shadcn) ─────────────────────────────────────────────────
|
|
598
209
|
'src/pages/Login.tsx': `import { useState } from 'react'
|
|
599
210
|
import { post } from '../lib/api.ts'
|
|
600
|
-
import { Button } from '
|
|
601
|
-
import { Input } from '
|
|
602
|
-
import { Label } from '
|
|
603
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '
|
|
211
|
+
import { Button } from '@/components/ui/button'
|
|
212
|
+
import { Input } from '@/components/ui/input'
|
|
213
|
+
import { Label } from '@/components/ui/label'
|
|
214
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
604
215
|
|
|
605
216
|
interface LoginProps {
|
|
606
217
|
appName?: string
|
|
@@ -687,10 +298,10 @@ export default function Login({ appName = '${ctx.name}', navigate }: LoginProps)
|
|
|
687
298
|
// ── Register page (shadcn) ──────────────────────────────────────────────
|
|
688
299
|
'src/pages/Register.tsx': `import { useState } from 'react'
|
|
689
300
|
import { post } from '../lib/api.ts'
|
|
690
|
-
import { Button } from '
|
|
691
|
-
import { Input } from '
|
|
692
|
-
import { Label } from '
|
|
693
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '
|
|
301
|
+
import { Button } from '@/components/ui/button'
|
|
302
|
+
import { Input } from '@/components/ui/input'
|
|
303
|
+
import { Label } from '@/components/ui/label'
|
|
304
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
694
305
|
|
|
695
306
|
interface RegisterProps {
|
|
696
307
|
appName?: string
|
|
@@ -788,11 +399,11 @@ export default function Register({ appName = '${ctx.name}', navigate }: Register
|
|
|
788
399
|
// ── Dashboard page (shadcn) ─────────────────────────────────────────────
|
|
789
400
|
'src/pages/Dashboard.tsx': `import { useState, useEffect, useCallback } from 'react'
|
|
790
401
|
import { api, post } from '../lib/api.ts'
|
|
791
|
-
import { Button } from '
|
|
792
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '
|
|
793
|
-
import { Badge } from '
|
|
794
|
-
import { Avatar, AvatarFallback } from '
|
|
795
|
-
import { Separator } from '
|
|
402
|
+
import { Button } from '@/components/ui/button'
|
|
403
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
404
|
+
import { Badge } from '@/components/ui/badge'
|
|
405
|
+
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
|
|
406
|
+
import { Separator } from '@/components/ui/separator'
|
|
796
407
|
import {
|
|
797
408
|
Table,
|
|
798
409
|
TableBody,
|
|
@@ -800,7 +411,7 @@ import {
|
|
|
800
411
|
TableHead,
|
|
801
412
|
TableHeader,
|
|
802
413
|
TableRow,
|
|
803
|
-
} from '
|
|
414
|
+
} from '@/components/ui/table'
|
|
804
415
|
|
|
805
416
|
interface User { id: number; name: string; email: string; role: string }
|
|
806
417
|
|
|
@@ -1011,8 +622,6 @@ export default function Dashboard({ appName = '${ctx.name}', currentUser, users:
|
|
|
1011
622
|
'tailwind-merge': '^2.6.0',
|
|
1012
623
|
'class-variance-authority': '^0.7.0',
|
|
1013
624
|
'lucide-react': '^0.460.0',
|
|
1014
|
-
'@radix-ui/react-slot': '^1.1.0',
|
|
1015
|
-
'@radix-ui/react-label': '^2.1.0',
|
|
1016
625
|
},
|
|
1017
626
|
}
|
|
1018
627
|
}
|