create-mantiq 0.2.2 → 0.3.1
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 +71 -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,55 @@ export function getShadcnTemplates(ctx: TemplateContext): {
|
|
|
6
6
|
} {
|
|
7
7
|
return {
|
|
8
8
|
files: {
|
|
9
|
+
// ── vite config override with @/ resolve alias ────────────────────────
|
|
10
|
+
'vite.config.ts': `import { defineConfig } from 'vite'
|
|
11
|
+
import react from '@vitejs/plugin-react'
|
|
12
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
13
|
+
import path from 'path'
|
|
14
|
+
|
|
15
|
+
export default defineConfig({
|
|
16
|
+
plugins: [react(), tailwindcss()],
|
|
17
|
+
publicDir: false,
|
|
18
|
+
resolve: {
|
|
19
|
+
alias: {
|
|
20
|
+
'@': path.resolve(__dirname, './src'),
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
build: {
|
|
24
|
+
outDir: 'public/build',
|
|
25
|
+
manifest: true,
|
|
26
|
+
emptyOutDir: true,
|
|
27
|
+
rollupOptions: {
|
|
28
|
+
input: ['src/main.tsx', 'src/style.css'],
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
`,
|
|
33
|
+
|
|
34
|
+
// ── tsconfig override with @/ path alias ──────────────────────────────
|
|
35
|
+
'tsconfig.json': JSON.stringify({
|
|
36
|
+
compilerOptions: {
|
|
37
|
+
target: 'ESNext',
|
|
38
|
+
module: 'ESNext',
|
|
39
|
+
moduleResolution: 'bundler',
|
|
40
|
+
lib: ['ESNext', 'DOM', 'DOM.Iterable'],
|
|
41
|
+
types: ['bun-types'],
|
|
42
|
+
jsx: 'react-jsx',
|
|
43
|
+
strict: true,
|
|
44
|
+
noImplicitAny: true,
|
|
45
|
+
strictNullChecks: true,
|
|
46
|
+
noUncheckedIndexedAccess: true,
|
|
47
|
+
noImplicitOverride: true,
|
|
48
|
+
allowImportingTsExtensions: true,
|
|
49
|
+
noEmit: true,
|
|
50
|
+
skipLibCheck: true,
|
|
51
|
+
baseUrl: '.',
|
|
52
|
+
paths: { '@/*': ['./src/*'] },
|
|
53
|
+
},
|
|
54
|
+
include: ['./**/*'],
|
|
55
|
+
exclude: ['node_modules'],
|
|
56
|
+
}, null, 2) + '\n',
|
|
57
|
+
|
|
9
58
|
// ── cn helper ───────────────────────────────────────────────────────────
|
|
10
59
|
'src/lib/utils.ts': `import { type ClassValue, clsx } from 'clsx'
|
|
11
60
|
import { twMerge } from 'tailwind-merge'
|
|
@@ -28,8 +77,11 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
28
77
|
cssVariables: true,
|
|
29
78
|
},
|
|
30
79
|
aliases: {
|
|
31
|
-
components: '
|
|
32
|
-
utils: '
|
|
80
|
+
components: '@/components',
|
|
81
|
+
utils: '@/lib/utils',
|
|
82
|
+
ui: '@/components/ui',
|
|
83
|
+
lib: '@/lib',
|
|
84
|
+
hooks: '@/hooks',
|
|
33
85
|
},
|
|
34
86
|
}, null, 2) + '\n',
|
|
35
87
|
|
|
@@ -174,444 +226,17 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
174
226
|
.animate-fade-up { animation: fadeUp 0.4s ease-out; }
|
|
175
227
|
`,
|
|
176
228
|
|
|
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
|
-
`,
|
|
229
|
+
// Components (button, input, label, card, badge, table, avatar,
|
|
230
|
+
// separator, sidebar, etc.) are installed by the shadcn CLI during
|
|
231
|
+
// scaffold — no hand-rolled templates needed.
|
|
607
232
|
|
|
608
233
|
// ── Login page (shadcn) ─────────────────────────────────────────────────
|
|
609
234
|
'src/pages/Login.tsx': `import { useState } from 'react'
|
|
610
235
|
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 '
|
|
236
|
+
import { Button } from '@/components/ui/button'
|
|
237
|
+
import { Input } from '@/components/ui/input'
|
|
238
|
+
import { Label } from '@/components/ui/label'
|
|
239
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
615
240
|
|
|
616
241
|
interface LoginProps {
|
|
617
242
|
appName?: string
|
|
@@ -698,10 +323,10 @@ export default function Login({ appName = '${ctx.name}', navigate }: LoginProps)
|
|
|
698
323
|
// ── Register page (shadcn) ──────────────────────────────────────────────
|
|
699
324
|
'src/pages/Register.tsx': `import { useState } from 'react'
|
|
700
325
|
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 '
|
|
326
|
+
import { Button } from '@/components/ui/button'
|
|
327
|
+
import { Input } from '@/components/ui/input'
|
|
328
|
+
import { Label } from '@/components/ui/label'
|
|
329
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
705
330
|
|
|
706
331
|
interface RegisterProps {
|
|
707
332
|
appName?: string
|
|
@@ -799,11 +424,11 @@ export default function Register({ appName = '${ctx.name}', navigate }: Register
|
|
|
799
424
|
// ── Dashboard page (shadcn) ─────────────────────────────────────────────
|
|
800
425
|
'src/pages/Dashboard.tsx': `import { useState, useEffect, useCallback } from 'react'
|
|
801
426
|
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 '
|
|
427
|
+
import { Button } from '@/components/ui/button'
|
|
428
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
429
|
+
import { Badge } from '@/components/ui/badge'
|
|
430
|
+
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
|
|
431
|
+
import { Separator } from '@/components/ui/separator'
|
|
807
432
|
import {
|
|
808
433
|
Table,
|
|
809
434
|
TableBody,
|
|
@@ -811,7 +436,7 @@ import {
|
|
|
811
436
|
TableHead,
|
|
812
437
|
TableHeader,
|
|
813
438
|
TableRow,
|
|
814
|
-
} from '
|
|
439
|
+
} from '@/components/ui/table'
|
|
815
440
|
|
|
816
441
|
interface User { id: number; name: string; email: string; role: string }
|
|
817
442
|
|
|
@@ -1022,8 +647,6 @@ export default function Dashboard({ appName = '${ctx.name}', currentUser, users:
|
|
|
1022
647
|
'tailwind-merge': '^2.6.0',
|
|
1023
648
|
'class-variance-authority': '^0.7.0',
|
|
1024
649
|
'lucide-react': '^0.460.0',
|
|
1025
|
-
'@radix-ui/react-slot': '^1.1.0',
|
|
1026
|
-
'@radix-ui/react-label': '^2.1.0',
|
|
1027
650
|
},
|
|
1028
651
|
}
|
|
1029
652
|
}
|