create-mantiq 0.2.2 → 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 +23 -0
- package/src/ui/shadcn.ts +46 -448
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -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')
|
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
|
|
|
@@ -174,444 +201,17 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
174
201
|
.animate-fade-up { animation: fadeUp 0.4s ease-out; }
|
|
175
202
|
`,
|
|
176
203
|
|
|
177
|
-
//
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
import { cva, type VariantProps } from 'class-variance-authority'
|
|
181
|
-
import { cn } from '../../lib/utils.ts'
|
|
182
|
-
|
|
183
|
-
const buttonVariants = cva(
|
|
184
|
-
'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',
|
|
185
|
-
{
|
|
186
|
-
variants: {
|
|
187
|
-
variant: {
|
|
188
|
-
default:
|
|
189
|
-
'bg-primary text-primary-foreground shadow hover:bg-primary/90',
|
|
190
|
-
destructive:
|
|
191
|
-
'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
|
|
192
|
-
outline:
|
|
193
|
-
'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
|
|
194
|
-
secondary:
|
|
195
|
-
'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
|
|
196
|
-
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
|
197
|
-
link: 'text-primary underline-offset-4 hover:underline',
|
|
198
|
-
},
|
|
199
|
-
size: {
|
|
200
|
-
default: 'h-9 px-4 py-2',
|
|
201
|
-
sm: 'h-8 rounded-md px-3 text-xs',
|
|
202
|
-
lg: 'h-10 rounded-md px-8',
|
|
203
|
-
icon: 'h-9 w-9',
|
|
204
|
-
},
|
|
205
|
-
},
|
|
206
|
-
defaultVariants: {
|
|
207
|
-
variant: 'default',
|
|
208
|
-
size: 'default',
|
|
209
|
-
},
|
|
210
|
-
}
|
|
211
|
-
)
|
|
212
|
-
|
|
213
|
-
export interface ButtonProps
|
|
214
|
-
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
215
|
-
VariantProps<typeof buttonVariants> {
|
|
216
|
-
asChild?: boolean
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
220
|
-
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
221
|
-
const Comp = asChild ? Slot : 'button'
|
|
222
|
-
return (
|
|
223
|
-
<Comp
|
|
224
|
-
className={cn(buttonVariants({ variant, size, className }))}
|
|
225
|
-
ref={ref}
|
|
226
|
-
{...props}
|
|
227
|
-
/>
|
|
228
|
-
)
|
|
229
|
-
}
|
|
230
|
-
)
|
|
231
|
-
Button.displayName = 'Button'
|
|
232
|
-
|
|
233
|
-
export { Button, buttonVariants }
|
|
234
|
-
`,
|
|
235
|
-
|
|
236
|
-
// ── Input component ─────────────────────────────────────────────────────
|
|
237
|
-
'src/components/ui/input.tsx': `import * as React from 'react'
|
|
238
|
-
import { cn } from '../../lib/utils.ts'
|
|
239
|
-
|
|
240
|
-
export interface InputProps
|
|
241
|
-
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
|
242
|
-
|
|
243
|
-
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
244
|
-
({ className, type, ...props }, ref) => {
|
|
245
|
-
return (
|
|
246
|
-
<input
|
|
247
|
-
type={type}
|
|
248
|
-
className={cn(
|
|
249
|
-
'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',
|
|
250
|
-
className
|
|
251
|
-
)}
|
|
252
|
-
ref={ref}
|
|
253
|
-
{...props}
|
|
254
|
-
/>
|
|
255
|
-
)
|
|
256
|
-
}
|
|
257
|
-
)
|
|
258
|
-
Input.displayName = 'Input'
|
|
259
|
-
|
|
260
|
-
export { Input }
|
|
261
|
-
`,
|
|
262
|
-
|
|
263
|
-
// ── Label component ─────────────────────────────────────────────────────
|
|
264
|
-
'src/components/ui/label.tsx': `import * as React from 'react'
|
|
265
|
-
import * as LabelPrimitive from '@radix-ui/react-label'
|
|
266
|
-
import { cva, type VariantProps } from 'class-variance-authority'
|
|
267
|
-
import { cn } from '../../lib/utils.ts'
|
|
268
|
-
|
|
269
|
-
const labelVariants = cva(
|
|
270
|
-
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'
|
|
271
|
-
)
|
|
272
|
-
|
|
273
|
-
const Label = React.forwardRef<
|
|
274
|
-
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
275
|
-
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
|
276
|
-
VariantProps<typeof labelVariants>
|
|
277
|
-
>(({ className, ...props }, ref) => (
|
|
278
|
-
<LabelPrimitive.Root
|
|
279
|
-
ref={ref}
|
|
280
|
-
className={cn(labelVariants(), className)}
|
|
281
|
-
{...props}
|
|
282
|
-
/>
|
|
283
|
-
))
|
|
284
|
-
Label.displayName = LabelPrimitive.Root.displayName
|
|
285
|
-
|
|
286
|
-
export { Label }
|
|
287
|
-
`,
|
|
288
|
-
|
|
289
|
-
// ── Card component ──────────────────────────────────────────────────────
|
|
290
|
-
'src/components/ui/card.tsx': `import * as React from 'react'
|
|
291
|
-
import { cn } from '../../lib/utils.ts'
|
|
292
|
-
|
|
293
|
-
const Card = React.forwardRef<
|
|
294
|
-
HTMLDivElement,
|
|
295
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
296
|
-
>(({ className, ...props }, ref) => (
|
|
297
|
-
<div
|
|
298
|
-
ref={ref}
|
|
299
|
-
className={cn(
|
|
300
|
-
'rounded-xl border border-border bg-card text-card-foreground shadow',
|
|
301
|
-
className
|
|
302
|
-
)}
|
|
303
|
-
{...props}
|
|
304
|
-
/>
|
|
305
|
-
))
|
|
306
|
-
Card.displayName = 'Card'
|
|
307
|
-
|
|
308
|
-
const CardHeader = React.forwardRef<
|
|
309
|
-
HTMLDivElement,
|
|
310
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
311
|
-
>(({ className, ...props }, ref) => (
|
|
312
|
-
<div
|
|
313
|
-
ref={ref}
|
|
314
|
-
className={cn('flex flex-col space-y-1.5 p-6', className)}
|
|
315
|
-
{...props}
|
|
316
|
-
/>
|
|
317
|
-
))
|
|
318
|
-
CardHeader.displayName = 'CardHeader'
|
|
319
|
-
|
|
320
|
-
const CardTitle = React.forwardRef<
|
|
321
|
-
HTMLDivElement,
|
|
322
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
323
|
-
>(({ className, ...props }, ref) => (
|
|
324
|
-
<div
|
|
325
|
-
ref={ref}
|
|
326
|
-
className={cn('font-semibold leading-none tracking-tight', className)}
|
|
327
|
-
{...props}
|
|
328
|
-
/>
|
|
329
|
-
))
|
|
330
|
-
CardTitle.displayName = 'CardTitle'
|
|
331
|
-
|
|
332
|
-
const CardDescription = React.forwardRef<
|
|
333
|
-
HTMLDivElement,
|
|
334
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
335
|
-
>(({ className, ...props }, ref) => (
|
|
336
|
-
<div
|
|
337
|
-
ref={ref}
|
|
338
|
-
className={cn('text-sm text-muted-foreground', className)}
|
|
339
|
-
{...props}
|
|
340
|
-
/>
|
|
341
|
-
))
|
|
342
|
-
CardDescription.displayName = 'CardDescription'
|
|
343
|
-
|
|
344
|
-
const CardContent = React.forwardRef<
|
|
345
|
-
HTMLDivElement,
|
|
346
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
347
|
-
>(({ className, ...props }, ref) => (
|
|
348
|
-
<div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
|
|
349
|
-
))
|
|
350
|
-
CardContent.displayName = 'CardContent'
|
|
351
|
-
|
|
352
|
-
const CardFooter = React.forwardRef<
|
|
353
|
-
HTMLDivElement,
|
|
354
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
355
|
-
>(({ className, ...props }, ref) => (
|
|
356
|
-
<div
|
|
357
|
-
ref={ref}
|
|
358
|
-
className={cn('flex items-center p-6 pt-0', className)}
|
|
359
|
-
{...props}
|
|
360
|
-
/>
|
|
361
|
-
))
|
|
362
|
-
CardFooter.displayName = 'CardFooter'
|
|
363
|
-
|
|
364
|
-
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
|
365
|
-
`,
|
|
366
|
-
|
|
367
|
-
// ── Badge component ─────────────────────────────────────────────────────
|
|
368
|
-
'src/components/ui/badge.tsx': `import * as React from 'react'
|
|
369
|
-
import { cva, type VariantProps } from 'class-variance-authority'
|
|
370
|
-
import { cn } from '../../lib/utils.ts'
|
|
371
|
-
|
|
372
|
-
const badgeVariants = cva(
|
|
373
|
-
'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',
|
|
374
|
-
{
|
|
375
|
-
variants: {
|
|
376
|
-
variant: {
|
|
377
|
-
default:
|
|
378
|
-
'border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80',
|
|
379
|
-
secondary:
|
|
380
|
-
'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
|
381
|
-
destructive:
|
|
382
|
-
'border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80',
|
|
383
|
-
outline: 'text-foreground',
|
|
384
|
-
},
|
|
385
|
-
},
|
|
386
|
-
defaultVariants: {
|
|
387
|
-
variant: 'default',
|
|
388
|
-
},
|
|
389
|
-
}
|
|
390
|
-
)
|
|
391
|
-
|
|
392
|
-
export interface BadgeProps
|
|
393
|
-
extends React.HTMLAttributes<HTMLDivElement>,
|
|
394
|
-
VariantProps<typeof badgeVariants> {}
|
|
395
|
-
|
|
396
|
-
function Badge({ className, variant, ...props }: BadgeProps) {
|
|
397
|
-
return (
|
|
398
|
-
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
|
399
|
-
)
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
export { Badge, badgeVariants }
|
|
403
|
-
`,
|
|
404
|
-
|
|
405
|
-
// ── Table component ─────────────────────────────────────────────────────
|
|
406
|
-
'src/components/ui/table.tsx': `import * as React from 'react'
|
|
407
|
-
import { cn } from '../../lib/utils.ts'
|
|
408
|
-
|
|
409
|
-
const Table = React.forwardRef<
|
|
410
|
-
HTMLTableElement,
|
|
411
|
-
React.HTMLAttributes<HTMLTableElement>
|
|
412
|
-
>(({ className, ...props }, ref) => (
|
|
413
|
-
<div className="relative w-full overflow-auto">
|
|
414
|
-
<table
|
|
415
|
-
ref={ref}
|
|
416
|
-
className={cn('w-full caption-bottom text-sm', className)}
|
|
417
|
-
{...props}
|
|
418
|
-
/>
|
|
419
|
-
</div>
|
|
420
|
-
))
|
|
421
|
-
Table.displayName = 'Table'
|
|
422
|
-
|
|
423
|
-
const TableHeader = React.forwardRef<
|
|
424
|
-
HTMLTableSectionElement,
|
|
425
|
-
React.HTMLAttributes<HTMLTableSectionElement>
|
|
426
|
-
>(({ className, ...props }, ref) => (
|
|
427
|
-
<thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} />
|
|
428
|
-
))
|
|
429
|
-
TableHeader.displayName = 'TableHeader'
|
|
430
|
-
|
|
431
|
-
const TableBody = React.forwardRef<
|
|
432
|
-
HTMLTableSectionElement,
|
|
433
|
-
React.HTMLAttributes<HTMLTableSectionElement>
|
|
434
|
-
>(({ className, ...props }, ref) => (
|
|
435
|
-
<tbody
|
|
436
|
-
ref={ref}
|
|
437
|
-
className={cn('[&_tr:last-child]:border-0', className)}
|
|
438
|
-
{...props}
|
|
439
|
-
/>
|
|
440
|
-
))
|
|
441
|
-
TableBody.displayName = 'TableBody'
|
|
442
|
-
|
|
443
|
-
const TableFooter = React.forwardRef<
|
|
444
|
-
HTMLTableSectionElement,
|
|
445
|
-
React.HTMLAttributes<HTMLTableSectionElement>
|
|
446
|
-
>(({ className, ...props }, ref) => (
|
|
447
|
-
<tfoot
|
|
448
|
-
ref={ref}
|
|
449
|
-
className={cn(
|
|
450
|
-
'border-t bg-muted/50 font-medium [&>tr]:last:border-b-0',
|
|
451
|
-
className
|
|
452
|
-
)}
|
|
453
|
-
{...props}
|
|
454
|
-
/>
|
|
455
|
-
))
|
|
456
|
-
TableFooter.displayName = 'TableFooter'
|
|
457
|
-
|
|
458
|
-
const TableRow = React.forwardRef<
|
|
459
|
-
HTMLTableRowElement,
|
|
460
|
-
React.HTMLAttributes<HTMLTableRowElement>
|
|
461
|
-
>(({ className, ...props }, ref) => (
|
|
462
|
-
<tr
|
|
463
|
-
ref={ref}
|
|
464
|
-
className={cn(
|
|
465
|
-
'border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted',
|
|
466
|
-
className
|
|
467
|
-
)}
|
|
468
|
-
{...props}
|
|
469
|
-
/>
|
|
470
|
-
))
|
|
471
|
-
TableRow.displayName = 'TableRow'
|
|
472
|
-
|
|
473
|
-
const TableHead = React.forwardRef<
|
|
474
|
-
HTMLTableCellElement,
|
|
475
|
-
React.ThHTMLAttributes<HTMLTableCellElement>
|
|
476
|
-
>(({ className, ...props }, ref) => (
|
|
477
|
-
<th
|
|
478
|
-
ref={ref}
|
|
479
|
-
className={cn(
|
|
480
|
-
'h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
|
|
481
|
-
className
|
|
482
|
-
)}
|
|
483
|
-
{...props}
|
|
484
|
-
/>
|
|
485
|
-
))
|
|
486
|
-
TableHead.displayName = 'TableHead'
|
|
487
|
-
|
|
488
|
-
const TableCell = React.forwardRef<
|
|
489
|
-
HTMLTableCellElement,
|
|
490
|
-
React.TdHTMLAttributes<HTMLTableCellElement>
|
|
491
|
-
>(({ className, ...props }, ref) => (
|
|
492
|
-
<td
|
|
493
|
-
ref={ref}
|
|
494
|
-
className={cn(
|
|
495
|
-
'p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
|
|
496
|
-
className
|
|
497
|
-
)}
|
|
498
|
-
{...props}
|
|
499
|
-
/>
|
|
500
|
-
))
|
|
501
|
-
TableCell.displayName = 'TableCell'
|
|
502
|
-
|
|
503
|
-
const TableCaption = React.forwardRef<
|
|
504
|
-
HTMLTableCaptionElement,
|
|
505
|
-
React.HTMLAttributes<HTMLTableCaptionElement>
|
|
506
|
-
>(({ className, ...props }, ref) => (
|
|
507
|
-
<caption
|
|
508
|
-
ref={ref}
|
|
509
|
-
className={cn('mt-4 text-sm text-muted-foreground', className)}
|
|
510
|
-
{...props}
|
|
511
|
-
/>
|
|
512
|
-
))
|
|
513
|
-
TableCaption.displayName = 'TableCaption'
|
|
514
|
-
|
|
515
|
-
export {
|
|
516
|
-
Table,
|
|
517
|
-
TableHeader,
|
|
518
|
-
TableBody,
|
|
519
|
-
TableFooter,
|
|
520
|
-
TableHead,
|
|
521
|
-
TableRow,
|
|
522
|
-
TableCell,
|
|
523
|
-
TableCaption,
|
|
524
|
-
}
|
|
525
|
-
`,
|
|
526
|
-
|
|
527
|
-
// ── Avatar component ────────────────────────────────────────────────────
|
|
528
|
-
'src/components/ui/avatar.tsx': `import * as React from 'react'
|
|
529
|
-
import { cn } from '../../lib/utils.ts'
|
|
530
|
-
|
|
531
|
-
const Avatar = React.forwardRef<
|
|
532
|
-
HTMLDivElement,
|
|
533
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
534
|
-
>(({ className, ...props }, ref) => (
|
|
535
|
-
<div
|
|
536
|
-
ref={ref}
|
|
537
|
-
className={cn(
|
|
538
|
-
'relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full',
|
|
539
|
-
className
|
|
540
|
-
)}
|
|
541
|
-
{...props}
|
|
542
|
-
/>
|
|
543
|
-
))
|
|
544
|
-
Avatar.displayName = 'Avatar'
|
|
545
|
-
|
|
546
|
-
const AvatarImage = React.forwardRef<
|
|
547
|
-
HTMLImageElement,
|
|
548
|
-
React.ImgHTMLAttributes<HTMLImageElement>
|
|
549
|
-
>(({ className, ...props }, ref) => (
|
|
550
|
-
<img
|
|
551
|
-
ref={ref}
|
|
552
|
-
className={cn('aspect-square h-full w-full', className)}
|
|
553
|
-
{...props}
|
|
554
|
-
/>
|
|
555
|
-
))
|
|
556
|
-
AvatarImage.displayName = 'AvatarImage'
|
|
557
|
-
|
|
558
|
-
const AvatarFallback = React.forwardRef<
|
|
559
|
-
HTMLDivElement,
|
|
560
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
561
|
-
>(({ className, ...props }, ref) => (
|
|
562
|
-
<div
|
|
563
|
-
ref={ref}
|
|
564
|
-
className={cn(
|
|
565
|
-
'flex h-full w-full items-center justify-center rounded-full bg-muted',
|
|
566
|
-
className
|
|
567
|
-
)}
|
|
568
|
-
{...props}
|
|
569
|
-
/>
|
|
570
|
-
))
|
|
571
|
-
AvatarFallback.displayName = 'AvatarFallback'
|
|
572
|
-
|
|
573
|
-
export { Avatar, AvatarImage, AvatarFallback }
|
|
574
|
-
`,
|
|
575
|
-
|
|
576
|
-
// ── Separator component ─────────────────────────────────────────────────
|
|
577
|
-
'src/components/ui/separator.tsx': `import * as React from 'react'
|
|
578
|
-
import { cn } from '../../lib/utils.ts'
|
|
579
|
-
|
|
580
|
-
interface SeparatorProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
581
|
-
orientation?: 'horizontal' | 'vertical'
|
|
582
|
-
decorative?: boolean
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
const Separator = React.forwardRef<HTMLDivElement, SeparatorProps>(
|
|
586
|
-
(
|
|
587
|
-
{ className, orientation = 'horizontal', decorative = true, ...props },
|
|
588
|
-
ref
|
|
589
|
-
) => (
|
|
590
|
-
<div
|
|
591
|
-
ref={ref}
|
|
592
|
-
role={decorative ? 'none' : 'separator'}
|
|
593
|
-
aria-orientation={decorative ? undefined : orientation}
|
|
594
|
-
className={cn(
|
|
595
|
-
'shrink-0 bg-border',
|
|
596
|
-
orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
|
|
597
|
-
className
|
|
598
|
-
)}
|
|
599
|
-
{...props}
|
|
600
|
-
/>
|
|
601
|
-
)
|
|
602
|
-
)
|
|
603
|
-
Separator.displayName = 'Separator'
|
|
604
|
-
|
|
605
|
-
export { Separator }
|
|
606
|
-
`,
|
|
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.
|
|
607
207
|
|
|
608
208
|
// ── Login page (shadcn) ─────────────────────────────────────────────────
|
|
609
209
|
'src/pages/Login.tsx': `import { useState } from 'react'
|
|
610
210
|
import { post } from '../lib/api.ts'
|
|
611
|
-
import { Button } from '
|
|
612
|
-
import { Input } from '
|
|
613
|
-
import { Label } from '
|
|
614
|
-
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'
|
|
615
215
|
|
|
616
216
|
interface LoginProps {
|
|
617
217
|
appName?: string
|
|
@@ -698,10 +298,10 @@ export default function Login({ appName = '${ctx.name}', navigate }: LoginProps)
|
|
|
698
298
|
// ── Register page (shadcn) ──────────────────────────────────────────────
|
|
699
299
|
'src/pages/Register.tsx': `import { useState } from 'react'
|
|
700
300
|
import { post } from '../lib/api.ts'
|
|
701
|
-
import { Button } from '
|
|
702
|
-
import { Input } from '
|
|
703
|
-
import { Label } from '
|
|
704
|
-
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'
|
|
705
305
|
|
|
706
306
|
interface RegisterProps {
|
|
707
307
|
appName?: string
|
|
@@ -799,11 +399,11 @@ export default function Register({ appName = '${ctx.name}', navigate }: Register
|
|
|
799
399
|
// ── Dashboard page (shadcn) ─────────────────────────────────────────────
|
|
800
400
|
'src/pages/Dashboard.tsx': `import { useState, useEffect, useCallback } from 'react'
|
|
801
401
|
import { api, post } from '../lib/api.ts'
|
|
802
|
-
import { Button } from '
|
|
803
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '
|
|
804
|
-
import { Badge } from '
|
|
805
|
-
import { Avatar, AvatarFallback } from '
|
|
806
|
-
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'
|
|
807
407
|
import {
|
|
808
408
|
Table,
|
|
809
409
|
TableBody,
|
|
@@ -811,7 +411,7 @@ import {
|
|
|
811
411
|
TableHead,
|
|
812
412
|
TableHeader,
|
|
813
413
|
TableRow,
|
|
814
|
-
} from '
|
|
414
|
+
} from '@/components/ui/table'
|
|
815
415
|
|
|
816
416
|
interface User { id: number; name: string; email: string; role: string }
|
|
817
417
|
|
|
@@ -1022,8 +622,6 @@ export default function Dashboard({ appName = '${ctx.name}', currentUser, users:
|
|
|
1022
622
|
'tailwind-merge': '^2.6.0',
|
|
1023
623
|
'class-variance-authority': '^0.7.0',
|
|
1024
624
|
'lucide-react': '^0.460.0',
|
|
1025
|
-
'@radix-ui/react-slot': '^1.1.0',
|
|
1026
|
-
'@radix-ui/react-label': '^2.1.0',
|
|
1027
625
|
},
|
|
1028
626
|
}
|
|
1029
627
|
}
|