@pylonsync/create-pylon 0.3.242 → 0.3.243

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.
@@ -173,12 +173,12 @@ if (!flags.template) {
173
173
  process.stdout.write(`\n${lines}\n`);
174
174
  const ans = (
175
175
  await rl.question(
176
- `Template (${TEMPLATES_AVAILABLE.join(", ")}) [todo]: `,
176
+ `Template (${TEMPLATES_AVAILABLE.join(", ")}) [ssr]: `,
177
177
  )
178
178
  )
179
179
  .trim()
180
180
  .toLowerCase();
181
- flags.template = TEMPLATES_AVAILABLE.includes(ans) ? ans : "todo";
181
+ flags.template = TEMPLATES_AVAILABLE.includes(ans) ? ans : "ssr";
182
182
  }
183
183
  // `unified` templates (ssr) are a single app, not a monorepo — they take
184
184
  // no platforms. Skip the platform prompt + validation for them entirely.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pylonsync/create-pylon",
3
- "version": "0.3.242",
3
+ "version": "0.3.243",
4
4
  "description": "Scaffold a new Pylon app — realtime backend + web/mobile/expo frontends in one command. Run via `npm create @pylonsync/pylon@latest`.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import { Button } from "@/components/ui/button";
2
3
 
3
4
  interface PageProps {
4
5
  url: string;
@@ -8,40 +9,45 @@ interface PageProps {
8
9
  // `app/counter/page.tsx` → `/counter`. This page is server-rendered AND
9
10
  // interactive: the HTML arrives with the initial count already in it (try
10
11
  // /counter?start=10), then the per-route chunk hydrates and useState takes
11
- // over. No client/server split to manage — it's one component.
12
+ // over. No client/server split to manage — it's one component. The buttons
13
+ // are shadcn/ui `Button`s, hydrated in place.
12
14
  export default function CounterPage({ searchParams }: PageProps) {
13
15
  const start = Number(searchParams.start ?? "0") || 0;
14
16
  const [count, setCount] = React.useState(start);
15
17
  return (
16
18
  <div className="space-y-6">
17
19
  <h1 className="text-2xl font-semibold tracking-tight">Counter</h1>
18
- <p className="text-zinc-600">
20
+ <p className="text-muted-foreground">
19
21
  Rendered on the server, hydrated in the browser. The buttons work
20
22
  because the page's JS chunk hydrated this exact markup.
21
23
  </p>
22
24
  <div className="flex items-center gap-4">
23
- <button
25
+ <Button
26
+ variant="outline"
27
+ size="icon"
24
28
  onClick={() => setCount((c) => c - 1)}
25
- className="rounded-lg border border-zinc-300 px-4 py-2 text-lg hover:bg-zinc-100"
29
+ aria-label="Decrement"
26
30
  >
27
31
 
28
- </button>
32
+ </Button>
29
33
  <span className="min-w-12 text-center text-2xl font-semibold tabular-nums">
30
34
  {count}
31
35
  </span>
32
- <button
36
+ <Button
37
+ variant="outline"
38
+ size="icon"
33
39
  onClick={() => setCount((c) => c + 1)}
34
- className="rounded-lg border border-zinc-300 px-4 py-2 text-lg hover:bg-zinc-100"
40
+ aria-label="Increment"
35
41
  >
36
42
  +
37
- </button>
43
+ </Button>
38
44
  </div>
39
- <p className="text-xs text-zinc-500">
45
+ <p className="text-xs text-muted-foreground">
40
46
  Initial value comes from <code>?start=</code> — search params flow
41
47
  through SSR. Try{" "}
42
48
  <a
43
49
  href="/counter?start=10"
44
- className="text-blue-600 underline-offset-4 hover:underline"
50
+ className="text-primary underline-offset-4 hover:underline"
45
51
  >
46
52
  /counter?start=10
47
53
  </a>
@@ -1,3 +1,139 @@
1
1
  @import "tailwindcss";
2
+ @import "tw-animate-css";
2
3
 
4
+ /* Tailwind v4 scans these globs for class names. `components/` is here so
5
+ shadcn/ui component classes are seen — add more @source lines if you put
6
+ markup elsewhere. */
3
7
  @source "../app/**/*.{tsx,ts,jsx,js}";
8
+ @source "../components/**/*.{tsx,ts,jsx,js}";
9
+
10
+ @custom-variant dark (&:where(.dark, .dark *));
11
+
12
+ /* shadcn/ui design tokens (new-york / zinc). Edit these to re-theme the
13
+ whole app; `npx shadcn@latest add <component>` drops new components that
14
+ consume the same variables. Toggle dark mode by putting `class="dark"`
15
+ on <html>. */
16
+ :root {
17
+ --radius: 0.625rem;
18
+ --background: oklch(1 0 0);
19
+ --foreground: oklch(0.141 0.005 285.823);
20
+ --card: oklch(1 0 0);
21
+ --card-foreground: oklch(0.141 0.005 285.823);
22
+ --popover: oklch(1 0 0);
23
+ --popover-foreground: oklch(0.141 0.005 285.823);
24
+ --primary: oklch(0.21 0.006 285.885);
25
+ --primary-foreground: oklch(0.985 0 0);
26
+ --secondary: oklch(0.967 0.001 286.375);
27
+ --secondary-foreground: oklch(0.21 0.006 285.885);
28
+ --muted: oklch(0.967 0.001 286.375);
29
+ --muted-foreground: oklch(0.552 0.016 285.938);
30
+ --accent: oklch(0.967 0.001 286.375);
31
+ --accent-foreground: oklch(0.21 0.006 285.885);
32
+ --destructive: oklch(0.577 0.245 27.325);
33
+ --border: oklch(0.92 0.004 286.32);
34
+ --input: oklch(0.92 0.004 286.32);
35
+ --ring: oklch(0.705 0.015 286.067);
36
+ --chart-1: oklch(0.646 0.222 41.116);
37
+ --chart-2: oklch(0.6 0.118 184.704);
38
+ --chart-3: oklch(0.398 0.07 227.392);
39
+ --chart-4: oklch(0.828 0.189 84.429);
40
+ --chart-5: oklch(0.769 0.188 70.08);
41
+ --sidebar: oklch(0.985 0 0);
42
+ --sidebar-foreground: oklch(0.141 0.005 285.823);
43
+ --sidebar-primary: oklch(0.21 0.006 285.885);
44
+ --sidebar-primary-foreground: oklch(0.985 0 0);
45
+ --sidebar-accent: oklch(0.967 0.001 286.375);
46
+ --sidebar-accent-foreground: oklch(0.21 0.006 285.885);
47
+ --sidebar-border: oklch(0.92 0.004 286.32);
48
+ --sidebar-ring: oklch(0.705 0.015 286.067);
49
+ }
50
+
51
+ .dark {
52
+ --background: oklch(0.141 0.005 285.823);
53
+ --foreground: oklch(0.985 0 0);
54
+ --card: oklch(0.21 0.006 285.885);
55
+ --card-foreground: oklch(0.985 0 0);
56
+ --popover: oklch(0.21 0.006 285.885);
57
+ --popover-foreground: oklch(0.985 0 0);
58
+ --primary: oklch(0.92 0.004 286.32);
59
+ --primary-foreground: oklch(0.21 0.006 285.885);
60
+ --secondary: oklch(0.274 0.006 286.033);
61
+ --secondary-foreground: oklch(0.985 0 0);
62
+ --muted: oklch(0.274 0.006 286.033);
63
+ --muted-foreground: oklch(0.705 0.015 286.067);
64
+ --accent: oklch(0.274 0.006 286.033);
65
+ --accent-foreground: oklch(0.985 0 0);
66
+ --destructive: oklch(0.704 0.191 22.216);
67
+ --border: oklch(1 0 0 / 10%);
68
+ --input: oklch(1 0 0 / 15%);
69
+ --ring: oklch(0.552 0.016 285.938);
70
+ --chart-1: oklch(0.488 0.243 264.376);
71
+ --chart-2: oklch(0.696 0.17 162.48);
72
+ --chart-3: oklch(0.769 0.188 70.08);
73
+ --chart-4: oklch(0.627 0.265 303.9);
74
+ --chart-5: oklch(0.645 0.246 16.439);
75
+ --sidebar: oklch(0.21 0.006 285.885);
76
+ --sidebar-foreground: oklch(0.985 0 0);
77
+ --sidebar-primary: oklch(0.488 0.243 264.376);
78
+ --sidebar-primary-foreground: oklch(0.985 0 0);
79
+ --sidebar-accent: oklch(0.274 0.006 286.033);
80
+ --sidebar-accent-foreground: oklch(0.985 0 0);
81
+ --sidebar-border: oklch(1 0 0 / 10%);
82
+ --sidebar-ring: oklch(0.552 0.016 285.938);
83
+ }
84
+
85
+ @theme inline {
86
+ --radius-sm: calc(var(--radius) - 4px);
87
+ --radius-md: calc(var(--radius) - 2px);
88
+ --radius-lg: var(--radius);
89
+ --radius-xl: calc(var(--radius) + 4px);
90
+ --color-background: var(--background);
91
+ --color-foreground: var(--foreground);
92
+ --color-card: var(--card);
93
+ --color-card-foreground: var(--card-foreground);
94
+ --color-popover: var(--popover);
95
+ --color-popover-foreground: var(--popover-foreground);
96
+ --color-primary: var(--primary);
97
+ --color-primary-foreground: var(--primary-foreground);
98
+ --color-secondary: var(--secondary);
99
+ --color-secondary-foreground: var(--secondary-foreground);
100
+ --color-muted: var(--muted);
101
+ --color-muted-foreground: var(--muted-foreground);
102
+ --color-accent: var(--accent);
103
+ --color-accent-foreground: var(--accent-foreground);
104
+ --color-destructive: var(--destructive);
105
+ --color-border: var(--border);
106
+ --color-input: var(--input);
107
+ --color-ring: var(--ring);
108
+ --color-chart-1: var(--chart-1);
109
+ --color-chart-2: var(--chart-2);
110
+ --color-chart-3: var(--chart-3);
111
+ --color-chart-4: var(--chart-4);
112
+ --color-chart-5: var(--chart-5);
113
+ --color-sidebar: var(--sidebar);
114
+ --color-sidebar-foreground: var(--sidebar-foreground);
115
+ --color-sidebar-primary: var(--sidebar-primary);
116
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
117
+ --color-sidebar-accent: var(--sidebar-accent);
118
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
119
+ --color-sidebar-border: var(--sidebar-border);
120
+ --color-sidebar-ring: var(--sidebar-ring);
121
+ }
122
+
123
+ @layer base {
124
+ *,
125
+ ::after,
126
+ ::before,
127
+ ::backdrop,
128
+ ::file-selector-button {
129
+ border-color: var(--color-border, currentColor);
130
+ outline-color: var(--color-ring);
131
+ }
132
+ body {
133
+ background-color: var(--color-background);
134
+ color: var(--color-foreground);
135
+ }
136
+ button {
137
+ cursor: pointer;
138
+ }
139
+ }
@@ -21,8 +21,11 @@ interface LayoutProps {
21
21
  // SSR runtime on every render — server-side, before the HTML is sent.
22
22
  export default function RootLayout({ children, url, auth }: LayoutProps) {
23
23
  const signedIn = Boolean(auth?.user_id);
24
+ // Add `className="dark"` to this <html> to flip every shadcn token to its
25
+ // dark value. The classes below use semantic tokens (bg-background,
26
+ // text-foreground, …) so the whole UI re-themes from app/globals.css.
24
27
  return (
25
- <html lang="en" className="bg-zinc-50">
28
+ <html lang="en">
26
29
  <head>
27
30
  <meta charSet="utf-8" />
28
31
  <meta name="viewport" content="width=device-width, initial-scale=1" />
@@ -31,24 +34,24 @@ export default function RootLayout({ children, url, auth }: LayoutProps) {
31
34
  stylesheet link is injected here automatically — nothing to
32
35
  wire up. */}
33
36
  </head>
34
- <body className="min-h-screen text-zinc-900 antialiased">
35
- <header className="sticky top-0 z-10 border-b border-zinc-200 bg-white/80 backdrop-blur">
37
+ <body className="min-h-screen bg-background text-foreground antialiased">
38
+ <header className="sticky top-0 z-10 border-b bg-background/80 backdrop-blur">
36
39
  <div className="mx-auto flex max-w-3xl items-center justify-between px-4 py-3">
37
40
  <Link
38
41
  href="/"
39
- className="text-sm font-semibold tracking-tight hover:text-zinc-600"
42
+ className="text-sm font-semibold tracking-tight hover:text-muted-foreground"
40
43
  >
41
44
  __APP_NAME__
42
45
  </Link>
43
- <nav className="flex items-center gap-4 text-sm text-zinc-600">
44
- <Link href="/" className="hover:text-zinc-900">
46
+ <nav className="flex items-center gap-4 text-sm text-muted-foreground">
47
+ <Link href="/" className="hover:text-foreground">
45
48
  Home
46
49
  </Link>
47
- <Link href="/counter" className="hover:text-zinc-900">
50
+ <Link href="/counter" className="hover:text-foreground">
48
51
  Counter
49
52
  </Link>
50
53
  <span
51
- className={signedIn ? "text-emerald-600" : "text-zinc-400"}
54
+ className={signedIn ? "text-emerald-600" : "text-muted-foreground/60"}
52
55
  title={url}
53
56
  >
54
57
  {signedIn ? `· ${auth.user_id}` : "· anon"}
@@ -57,7 +60,7 @@ export default function RootLayout({ children, url, auth }: LayoutProps) {
57
60
  </div>
58
61
  </header>
59
62
  <main className="mx-auto max-w-3xl px-4 py-10">{children}</main>
60
- <footer className="border-t border-zinc-200 py-6 text-center text-xs text-zinc-500">
63
+ <footer className="border-t py-6 text-center text-xs text-muted-foreground">
61
64
  Rendered by Pylon · one server, one port
62
65
  </footer>
63
66
  </body>
@@ -1,5 +1,13 @@
1
1
  import React from "react";
2
2
  import { Link } from "@pylonsync/react";
3
+ import { Button } from "@/components/ui/button";
4
+ import {
5
+ Card,
6
+ CardContent,
7
+ CardDescription,
8
+ CardHeader,
9
+ CardTitle,
10
+ } from "@/components/ui/card";
3
11
 
4
12
  interface PageProps {
5
13
  url: string;
@@ -8,47 +16,69 @@ interface PageProps {
8
16
  // `app/page.tsx` → `/`. Pages receive `{ url, auth, searchParams }` from
9
17
  // the SSR runtime. This renders to HTML on the server; the per-route
10
18
  // chunk hydrates it in the browser so interactive pages (see /counter)
11
- // just work.
19
+ // just work. shadcn/ui is pre-wired — `Button`/`Card` resolve through the
20
+ // `@/` alias and add more with `npx shadcn@latest add <component>`.
12
21
  export default function IndexPage({ url }: PageProps) {
13
22
  return (
14
23
  <div className="space-y-8">
15
24
  <section>
16
25
  <h1 className="text-3xl font-semibold tracking-tight">__APP_NAME__</h1>
17
- <p className="mt-2 text-zinc-600">
26
+ <p className="mt-2 text-muted-foreground">
18
27
  A full-stack Pylon app. Server-rendered React, file-based routes,
19
28
  a synced database, and a typed client — served from one binary on
20
- one port. No Next.js, no separate API server.
29
+ one port. No Next.js, no separate API server. Styled with Tailwind
30
+ v4 and shadcn/ui out of the box.
21
31
  </p>
22
32
  </section>
23
33
 
24
- <section className="rounded-2xl border border-zinc-200 bg-white p-6">
25
- <h2 className="text-lg font-semibold">Next steps</h2>
26
- <ul className="mt-3 space-y-2 text-sm text-zinc-700">
27
- <li>
28
- Add a route: drop a file at{" "}
29
- <code className="rounded bg-zinc-100 px-1">app/about/page.tsx</code>{" "}
30
- and visit <code className="rounded bg-zinc-100 px-1">/about</code>.
31
- </li>
32
- <li>
33
- Add data: edit{" "}
34
- <code className="rounded bg-zinc-100 px-1">app.ts</code> every{" "}
35
- <code className="rounded bg-zinc-100 px-1">entity()</code> gets a
36
- REST + realtime API and a typed client automatically.
37
- </li>
38
- <li>
39
- See hydration in action on{" "}
40
- <Link
41
- href="/counter"
42
- className="text-blue-600 underline-offset-4 hover:underline"
43
- >
44
- /counter
45
- </Link>
46
- .
47
- </li>
48
- </ul>
49
- </section>
34
+ <Card>
35
+ <CardHeader>
36
+ <CardTitle>Next steps</CardTitle>
37
+ <CardDescription>
38
+ Everything below is already wired just edit the files.
39
+ </CardDescription>
40
+ </CardHeader>
41
+ <CardContent>
42
+ <ul className="space-y-2 text-sm text-foreground/80">
43
+ <li>
44
+ Add a route: drop a file at{" "}
45
+ <code className="rounded bg-muted px-1">app/about/page.tsx</code>{" "}
46
+ and visit <code className="rounded bg-muted px-1">/about</code>.
47
+ </li>
48
+ <li>
49
+ Add data: edit{" "}
50
+ <code className="rounded bg-muted px-1">app.ts</code> — every{" "}
51
+ <code className="rounded bg-muted px-1">entity()</code> gets a
52
+ REST + realtime API and a typed client automatically.
53
+ </li>
54
+ <li>
55
+ Add a component:{" "}
56
+ <code className="rounded bg-muted px-1">
57
+ npx shadcn@latest add dialog
58
+ </code>{" "}
59
+ drops it into{" "}
60
+ <code className="rounded bg-muted px-1">components/ui</code>.
61
+ </li>
62
+ </ul>
63
+ </CardContent>
64
+ </Card>
65
+
66
+ <div className="flex flex-wrap items-center gap-3">
67
+ <Button asChild>
68
+ <Link href="/counter">See hydration in action →</Link>
69
+ </Button>
70
+ <Button asChild variant="outline">
71
+ <a
72
+ href="https://docs.pylon.dev"
73
+ target="_blank"
74
+ rel="noreferrer noopener"
75
+ >
76
+ Read the docs
77
+ </a>
78
+ </Button>
79
+ </div>
50
80
 
51
- <p className="text-xs text-zinc-500">
81
+ <p className="text-xs text-muted-foreground">
52
82
  You're at <code>{url}</code>. Edit{" "}
53
83
  <code>app/page.tsx</code> and save — the page reloads instantly.
54
84
  </p>
@@ -0,0 +1,56 @@
1
+ import * as React from "react";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+
5
+ import { cn } from "@/lib/utils";
6
+
7
+ const buttonVariants = cva(
8
+ "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-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
13
+ destructive:
14
+ "bg-destructive text-white hover:bg-destructive/90",
15
+ outline:
16
+ "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
17
+ secondary:
18
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
19
+ ghost: "hover:bg-accent hover:text-accent-foreground",
20
+ link: "text-primary underline-offset-4 hover:underline",
21
+ },
22
+ size: {
23
+ default: "h-9 px-4 py-2",
24
+ sm: "h-8 rounded-md px-3 text-xs",
25
+ lg: "h-10 rounded-md px-8",
26
+ icon: "h-9 w-9",
27
+ },
28
+ },
29
+ defaultVariants: {
30
+ variant: "default",
31
+ size: "default",
32
+ },
33
+ },
34
+ );
35
+
36
+ export interface ButtonProps
37
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
38
+ VariantProps<typeof buttonVariants> {
39
+ asChild?: boolean;
40
+ }
41
+
42
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
43
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
44
+ const Comp = asChild ? Slot : "button";
45
+ return (
46
+ <Comp
47
+ className={cn(buttonVariants({ variant, size, className }))}
48
+ ref={ref}
49
+ {...props}
50
+ />
51
+ );
52
+ },
53
+ );
54
+ Button.displayName = "Button";
55
+
56
+ export { Button, buttonVariants };
@@ -0,0 +1,90 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from "@/lib/utils";
4
+
5
+ function Card({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
6
+ return (
7
+ <div
8
+ data-slot="card"
9
+ className={cn(
10
+ "rounded-xl border bg-card text-card-foreground shadow-sm",
11
+ className,
12
+ )}
13
+ {...props}
14
+ />
15
+ );
16
+ }
17
+
18
+ function CardHeader({
19
+ className,
20
+ ...props
21
+ }: React.HTMLAttributes<HTMLDivElement>) {
22
+ return (
23
+ <div
24
+ data-slot="card-header"
25
+ className={cn("flex flex-col space-y-1.5 p-6", className)}
26
+ {...props}
27
+ />
28
+ );
29
+ }
30
+
31
+ function CardTitle({
32
+ className,
33
+ ...props
34
+ }: React.HTMLAttributes<HTMLDivElement>) {
35
+ return (
36
+ <div
37
+ data-slot="card-title"
38
+ className={cn("font-semibold leading-none tracking-tight", className)}
39
+ {...props}
40
+ />
41
+ );
42
+ }
43
+
44
+ function CardDescription({
45
+ className,
46
+ ...props
47
+ }: React.HTMLAttributes<HTMLDivElement>) {
48
+ return (
49
+ <div
50
+ data-slot="card-description"
51
+ className={cn("text-sm text-muted-foreground", className)}
52
+ {...props}
53
+ />
54
+ );
55
+ }
56
+
57
+ function CardContent({
58
+ className,
59
+ ...props
60
+ }: React.HTMLAttributes<HTMLDivElement>) {
61
+ return (
62
+ <div
63
+ data-slot="card-content"
64
+ className={cn("p-6 pt-0", className)}
65
+ {...props}
66
+ />
67
+ );
68
+ }
69
+
70
+ function CardFooter({
71
+ className,
72
+ ...props
73
+ }: React.HTMLAttributes<HTMLDivElement>) {
74
+ return (
75
+ <div
76
+ data-slot="card-footer"
77
+ className={cn("flex items-center p-6 pt-0", className)}
78
+ {...props}
79
+ />
80
+ );
81
+ }
82
+
83
+ export {
84
+ Card,
85
+ CardHeader,
86
+ CardFooter,
87
+ CardTitle,
88
+ CardDescription,
89
+ CardContent,
90
+ };
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "new-york",
4
+ "rsc": false,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "app/globals.css",
9
+ "baseColor": "zinc",
10
+ "cssVariables": true
11
+ },
12
+ "aliases": {
13
+ "components": "@/components",
14
+ "utils": "@/lib/utils",
15
+ "ui": "@/components/ui",
16
+ "lib": "@/lib",
17
+ "hooks": "@/hooks"
18
+ },
19
+ "iconLibrary": "lucide"
20
+ }
@@ -0,0 +1,10 @@
1
+ import { type ClassValue, clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ // `cn` — the shadcn class merger. clsx resolves conditional/array class
5
+ // inputs; tailwind-merge then dedupes conflicting Tailwind utilities so
6
+ // the last one wins (e.g. `cn("px-2", "px-4")` → "px-4"). Every shadcn
7
+ // component routes its className through this.
8
+ export function cn(...inputs: ClassValue[]) {
9
+ return twMerge(clsx(inputs));
10
+ }
@@ -15,7 +15,13 @@
15
15
  "react": "^19.0.0",
16
16
  "react-dom": "^19.0.0",
17
17
  "tailwindcss": "^4.3.0",
18
- "@tailwindcss/cli": "^4.3.0"
18
+ "@tailwindcss/cli": "^4.3.0",
19
+ "tw-animate-css": "^1.2.0",
20
+ "class-variance-authority": "^0.7.1",
21
+ "clsx": "^2.1.1",
22
+ "tailwind-merge": "^2.5.0",
23
+ "lucide-react": "^0.460.0",
24
+ "@radix-ui/react-slot": "^1.1.0"
19
25
  },
20
26
  "devDependencies": {
21
27
  "@pylonsync/cli": "^__PYLON_VERSION__",
@@ -8,7 +8,11 @@
8
8
  "strict": true,
9
9
  "skipLibCheck": true,
10
10
  "lib": ["ES2022", "DOM"],
11
- "types": ["react", "react-dom"]
11
+ "types": ["react", "react-dom"],
12
+ "baseUrl": ".",
13
+ "paths": {
14
+ "@/*": ["./*"]
15
+ }
12
16
  },
13
- "include": ["app.ts", "app/**/*"]
17
+ "include": ["app.ts", "app/**/*", "components/**/*", "lib/**/*"]
14
18
  }