@helloleo/plugins 0.1.20 → 0.2.0-beta.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.
Files changed (133) hide show
  1. package/dist/index.js +12361 -58
  2. package/dist/templates/tanstack-start-2026-06-18/components.json +22 -0
  3. package/dist/templates/tanstack-start-2026-06-18/drizzle.config.ts +15 -0
  4. package/dist/templates/tanstack-start-2026-06-18/package.json +103 -0
  5. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/accordion.tsx +51 -0
  6. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/alert-dialog.tsx +115 -0
  7. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/alert.tsx +49 -0
  8. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/aspect-ratio.tsx +5 -0
  9. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/avatar.tsx +47 -0
  10. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/badge.tsx +32 -0
  11. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/breadcrumb.tsx +101 -0
  12. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/button.tsx +49 -0
  13. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/calendar.tsx +177 -0
  14. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/card.tsx +55 -0
  15. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/carousel.tsx +240 -0
  16. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/chart.tsx +331 -0
  17. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/checkbox.tsx +26 -0
  18. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/collapsible.tsx +11 -0
  19. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/command.tsx +143 -0
  20. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/context-menu.tsx +187 -0
  21. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/dialog.tsx +104 -0
  22. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/drawer.tsx +98 -0
  23. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/dropdown-menu.tsx +188 -0
  24. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/form.tsx +171 -0
  25. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/hover-card.tsx +27 -0
  26. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/input-otp.tsx +69 -0
  27. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/input.tsx +22 -0
  28. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/label.tsx +21 -0
  29. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/menubar.tsx +229 -0
  30. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/navigation-menu.tsx +120 -0
  31. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/pagination.tsx +98 -0
  32. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/popover.tsx +31 -0
  33. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/progress.tsx +25 -0
  34. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/radio-group.tsx +36 -0
  35. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/resizable.tsx +37 -0
  36. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/scroll-area.tsx +44 -0
  37. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/select.tsx +152 -0
  38. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/separator.tsx +24 -0
  39. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/sheet.tsx +122 -0
  40. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/sidebar.tsx +744 -0
  41. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/skeleton.tsx +7 -0
  42. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/slider.tsx +23 -0
  43. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/sonner.tsx +23 -0
  44. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/switch.tsx +27 -0
  45. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/table.tsx +94 -0
  46. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/tabs.tsx +53 -0
  47. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/textarea.tsx +21 -0
  48. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/toggle-group.tsx +57 -0
  49. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/toggle.tsx +42 -0
  50. package/dist/templates/tanstack-start-2026-06-18/src/components/ui/tooltip.tsx +32 -0
  51. package/dist/templates/tanstack-start-2026-06-18/src/db/index.ts +9 -0
  52. package/dist/templates/tanstack-start-2026-06-18/src/db/schema.ts +18 -0
  53. package/dist/templates/tanstack-start-2026-06-18/src/global.d.ts +3 -0
  54. package/dist/templates/tanstack-start-2026-06-18/src/hooks/use-mobile.tsx +19 -0
  55. package/dist/templates/tanstack-start-2026-06-18/src/lib/proxy.ts +83 -0
  56. package/dist/templates/tanstack-start-2026-06-18/src/lib/utils.ts +6 -0
  57. package/dist/templates/tanstack-start-2026-06-18/src/routeTree.gen.ts +59 -0
  58. package/dist/templates/tanstack-start-2026-06-18/src/router.tsx +33 -0
  59. package/dist/templates/tanstack-start-2026-06-18/src/routes/__root.tsx +60 -0
  60. package/dist/templates/tanstack-start-2026-06-18/src/routes/api/health.ts +81 -0
  61. package/dist/templates/tanstack-start-2026-06-18/src/routes/index.tsx +33 -0
  62. package/dist/templates/tanstack-start-2026-06-18/src/server.ts +32 -0
  63. package/dist/templates/tanstack-start-2026-06-18/src/styles.css +214 -0
  64. package/dist/templates/tanstack-start-2026-06-18/tsconfig.json +27 -0
  65. package/dist/templates/tanstack-start-2026-06-18/vite.config.ts +17 -0
  66. package/package.json +1 -1
  67. package/src/template-render.ts +5 -0
  68. package/src/templates/tanstack-start-2026-06-18/components.json +22 -0
  69. package/src/templates/tanstack-start-2026-06-18/drizzle.config.ts +15 -0
  70. package/src/templates/tanstack-start-2026-06-18/package.json +103 -0
  71. package/src/templates/tanstack-start-2026-06-18/src/components/ui/accordion.tsx +51 -0
  72. package/src/templates/tanstack-start-2026-06-18/src/components/ui/alert-dialog.tsx +115 -0
  73. package/src/templates/tanstack-start-2026-06-18/src/components/ui/alert.tsx +49 -0
  74. package/src/templates/tanstack-start-2026-06-18/src/components/ui/aspect-ratio.tsx +5 -0
  75. package/src/templates/tanstack-start-2026-06-18/src/components/ui/avatar.tsx +47 -0
  76. package/src/templates/tanstack-start-2026-06-18/src/components/ui/badge.tsx +32 -0
  77. package/src/templates/tanstack-start-2026-06-18/src/components/ui/breadcrumb.tsx +101 -0
  78. package/src/templates/tanstack-start-2026-06-18/src/components/ui/button.tsx +49 -0
  79. package/src/templates/tanstack-start-2026-06-18/src/components/ui/calendar.tsx +177 -0
  80. package/src/templates/tanstack-start-2026-06-18/src/components/ui/card.tsx +55 -0
  81. package/src/templates/tanstack-start-2026-06-18/src/components/ui/carousel.tsx +240 -0
  82. package/src/templates/tanstack-start-2026-06-18/src/components/ui/chart.tsx +331 -0
  83. package/src/templates/tanstack-start-2026-06-18/src/components/ui/checkbox.tsx +26 -0
  84. package/src/templates/tanstack-start-2026-06-18/src/components/ui/collapsible.tsx +11 -0
  85. package/src/templates/tanstack-start-2026-06-18/src/components/ui/command.tsx +143 -0
  86. package/src/templates/tanstack-start-2026-06-18/src/components/ui/context-menu.tsx +187 -0
  87. package/src/templates/tanstack-start-2026-06-18/src/components/ui/dialog.tsx +104 -0
  88. package/src/templates/tanstack-start-2026-06-18/src/components/ui/drawer.tsx +98 -0
  89. package/src/templates/tanstack-start-2026-06-18/src/components/ui/dropdown-menu.tsx +188 -0
  90. package/src/templates/tanstack-start-2026-06-18/src/components/ui/form.tsx +171 -0
  91. package/src/templates/tanstack-start-2026-06-18/src/components/ui/hover-card.tsx +27 -0
  92. package/src/templates/tanstack-start-2026-06-18/src/components/ui/input-otp.tsx +69 -0
  93. package/src/templates/tanstack-start-2026-06-18/src/components/ui/input.tsx +22 -0
  94. package/src/templates/tanstack-start-2026-06-18/src/components/ui/label.tsx +21 -0
  95. package/src/templates/tanstack-start-2026-06-18/src/components/ui/menubar.tsx +229 -0
  96. package/src/templates/tanstack-start-2026-06-18/src/components/ui/navigation-menu.tsx +120 -0
  97. package/src/templates/tanstack-start-2026-06-18/src/components/ui/pagination.tsx +98 -0
  98. package/src/templates/tanstack-start-2026-06-18/src/components/ui/popover.tsx +31 -0
  99. package/src/templates/tanstack-start-2026-06-18/src/components/ui/progress.tsx +25 -0
  100. package/src/templates/tanstack-start-2026-06-18/src/components/ui/radio-group.tsx +36 -0
  101. package/src/templates/tanstack-start-2026-06-18/src/components/ui/resizable.tsx +37 -0
  102. package/src/templates/tanstack-start-2026-06-18/src/components/ui/scroll-area.tsx +44 -0
  103. package/src/templates/tanstack-start-2026-06-18/src/components/ui/select.tsx +152 -0
  104. package/src/templates/tanstack-start-2026-06-18/src/components/ui/separator.tsx +24 -0
  105. package/src/templates/tanstack-start-2026-06-18/src/components/ui/sheet.tsx +122 -0
  106. package/src/templates/tanstack-start-2026-06-18/src/components/ui/sidebar.tsx +744 -0
  107. package/src/templates/tanstack-start-2026-06-18/src/components/ui/skeleton.tsx +7 -0
  108. package/src/templates/tanstack-start-2026-06-18/src/components/ui/slider.tsx +23 -0
  109. package/src/templates/tanstack-start-2026-06-18/src/components/ui/sonner.tsx +23 -0
  110. package/src/templates/tanstack-start-2026-06-18/src/components/ui/switch.tsx +27 -0
  111. package/src/templates/tanstack-start-2026-06-18/src/components/ui/table.tsx +94 -0
  112. package/src/templates/tanstack-start-2026-06-18/src/components/ui/tabs.tsx +53 -0
  113. package/src/templates/tanstack-start-2026-06-18/src/components/ui/textarea.tsx +21 -0
  114. package/src/templates/tanstack-start-2026-06-18/src/components/ui/toggle-group.tsx +57 -0
  115. package/src/templates/tanstack-start-2026-06-18/src/components/ui/toggle.tsx +42 -0
  116. package/src/templates/tanstack-start-2026-06-18/src/components/ui/tooltip.tsx +32 -0
  117. package/src/templates/tanstack-start-2026-06-18/src/db/index.ts +9 -0
  118. package/src/templates/tanstack-start-2026-06-18/src/db/schema.ts +18 -0
  119. package/src/templates/tanstack-start-2026-06-18/src/global.d.ts +3 -0
  120. package/src/templates/tanstack-start-2026-06-18/src/hooks/use-mobile.tsx +19 -0
  121. package/src/templates/tanstack-start-2026-06-18/src/lib/proxy.ts +83 -0
  122. package/src/templates/tanstack-start-2026-06-18/src/lib/utils.ts +6 -0
  123. package/src/templates/tanstack-start-2026-06-18/src/routeTree.gen.ts +59 -0
  124. package/src/templates/tanstack-start-2026-06-18/src/router.tsx +33 -0
  125. package/src/templates/tanstack-start-2026-06-18/src/routes/__root.tsx +60 -0
  126. package/src/templates/tanstack-start-2026-06-18/src/routes/api/health.ts +81 -0
  127. package/src/templates/tanstack-start-2026-06-18/src/routes/index.tsx +33 -0
  128. package/src/templates/tanstack-start-2026-06-18/src/server.ts +32 -0
  129. package/src/templates/tanstack-start-2026-06-18/src/styles.css +214 -0
  130. package/src/templates/tanstack-start-2026-06-18/tsconfig.json +27 -0
  131. package/src/templates/tanstack-start-2026-06-18/vite.config.ts +17 -0
  132. package/src/tools/scaffold.ts +42 -43
  133. package/src/tools/setup-netsuite.ts +28 -58
@@ -0,0 +1,60 @@
1
+ import {
2
+ HeadContent,
3
+ Outlet,
4
+ Scripts,
5
+ createRootRouteWithContext,
6
+ } from '@tanstack/react-router'
7
+ import type { QueryClient } from '@tanstack/react-query'
8
+ import type { ReactNode } from 'react'
9
+
10
+ // Side-effect order matters: Tailwind needs to be linked into the SSR'd
11
+ // document. Importing the stylesheet as a URL and listing it in `head.links`
12
+ // is the Start-native way (a bare `import './styles.css'` only works client-side).
13
+ import appCss from '../styles.css?url'
14
+
15
+ export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()({
16
+ head: () => ({
17
+ meta: [
18
+ { charSet: 'utf-8' },
19
+ { name: 'viewport', content: 'width=device-width, initial-scale=1.0' },
20
+ { title: '{{LEO_NAME}}' },
21
+ { name: 'description', content: '{{LEO_DESCRIPTION}}' },
22
+ ],
23
+ links: [
24
+ { rel: 'stylesheet', href: appCss },
25
+ { rel: 'preconnect', href: 'https://fonts.googleapis.com' },
26
+ {
27
+ rel: 'preconnect',
28
+ href: 'https://fonts.gstatic.com',
29
+ crossOrigin: 'anonymous',
30
+ },
31
+ {
32
+ rel: 'stylesheet',
33
+ href: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Space+Grotesk:wght@400;500;600;700&display=swap',
34
+ },
35
+ ],
36
+ }),
37
+ component: RootComponent,
38
+ })
39
+
40
+ function RootComponent() {
41
+ return (
42
+ <RootDocument>
43
+ <Outlet />
44
+ </RootDocument>
45
+ )
46
+ }
47
+
48
+ function RootDocument({ children }: Readonly<{ children: ReactNode }>) {
49
+ return (
50
+ <html lang="en">
51
+ <head>
52
+ <HeadContent />
53
+ </head>
54
+ <body>
55
+ {children}
56
+ <Scripts />
57
+ </body>
58
+ </html>
59
+ )
60
+ }
@@ -0,0 +1,81 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { db } from '#/db'
3
+ import { entries } from '#/db/schema'
4
+
5
+ /**
6
+ * Server route example: GET /api/health. Use this file as the base for
7
+ * building JSON APIs (CRUD endpoints, webhooks) with TanStack Start
8
+ * server routes:
9
+ * https://tanstack.com/start/latest/docs/framework/react/guide/server-routes
10
+ *
11
+ * How server routes work:
12
+ * - Files under src/routes/ can export server handlers. The URL matches
13
+ * the file path: src/routes/api/health.ts serves /api/health.
14
+ * - Add one handler per HTTP method under server.handlers (GET, POST,
15
+ * PUT, PATCH, DELETE). A handler receives ({ request, params }) and
16
+ * returns a Response, usually Response.json().
17
+ * - Dynamic segments use $: src/routes/api/todos/$id.ts serves
18
+ * /api/todos/:id and the handler reads params.id.
19
+ * - Read the request body with await request.json() and validate it
20
+ * before use. Return proper status codes (201 created, 404 not found).
21
+ * - Query the database with `import { db } from '#/db'` INSIDE the
22
+ * handler, never at module scope (the binding only exists per request).
23
+ */
24
+ export const Route = createFileRoute('/api/health')({
25
+ server: {
26
+ handlers: {
27
+ GET: async () => {
28
+ const startedAt = Date.now()
29
+
30
+ const server = {
31
+ status: 'ok' as const,
32
+ uptimeSeconds: Math.round(
33
+ typeof process !== 'undefined' &&
34
+ typeof process.uptime === 'function'
35
+ ? process.uptime()
36
+ : 0,
37
+ ),
38
+ timestamp: new Date().toISOString(),
39
+ runtime: 'cloudflare-workers',
40
+ }
41
+
42
+ // Database health: lightweight query against a known table
43
+ let database: {
44
+ status: 'ok' | 'error'
45
+ latencyMs: number
46
+ error: string | null
47
+ }
48
+ const dbStart = Date.now()
49
+ try {
50
+ await db.select({ id: entries.id }).from(entries).limit(1)
51
+ database = {
52
+ status: 'ok',
53
+ latencyMs: Date.now() - dbStart,
54
+ error: null,
55
+ }
56
+ } catch (err) {
57
+ database = {
58
+ status: 'error',
59
+ latencyMs: Date.now() - dbStart,
60
+ error: err instanceof Error ? err.message : String(err),
61
+ }
62
+ }
63
+
64
+ const overall =
65
+ server.status === 'ok' && database.status === 'ok'
66
+ ? 'ok'
67
+ : 'degraded'
68
+
69
+ return Response.json(
70
+ {
71
+ status: overall,
72
+ server,
73
+ database,
74
+ checkedInMs: Date.now() - startedAt,
75
+ },
76
+ { status: overall === 'ok' ? 200 : 503 },
77
+ )
78
+ },
79
+ },
80
+ },
81
+ })
@@ -0,0 +1,33 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { createServerFn } from '@tanstack/react-start'
3
+
4
+ // Server functions run only on the server (dev: node + cf-shim; prod: the
5
+ // worker). This is the seam where you read your bindings — import { db } from
6
+ // '#/db' and query Drizzle, or import { kv, storage } from '@helloleo/runtime'.
7
+ // Keep binding access INSIDE the handler, never at module scope.
8
+ const getMessage = createServerFn({ method: 'GET' }).handler(() => {
9
+ return { message: 'Hello from the server' }
10
+ })
11
+
12
+ export const Route = createFileRoute('/')({
13
+ component: Home,
14
+ loader: () => getMessage(),
15
+ })
16
+
17
+ /**
18
+ * Do not `export default function Home()`. This breaks the default
19
+ * codesplitting by Tanstack Router.
20
+ */
21
+ function Home() {
22
+ const { message } = Route.useLoaderData()
23
+
24
+ return (
25
+ <div className="p-8">
26
+ <h1 className="text-4xl font-bold">Welcome to HelloLeo</h1>
27
+ <p className="mt-4 text-lg">{message}</p>
28
+ <p className="mt-2 text-sm text-muted-foreground">
29
+ Edit <code>src/routes/index.tsx</code> to get started.
30
+ </p>
31
+ </div>
32
+ )
33
+ }
@@ -0,0 +1,32 @@
1
+ // Cloudflare Worker entry (the Worker `main`, configured by @helloleo/vite-config).
2
+ // Wraps the TanStack Start server entry so an SSR failure renders a clean 500
3
+ // instead of a raw stack.
4
+ type ServerEntry = {
5
+ fetch: (request: Request, env: unknown, ctx: unknown) => Promise<Response> | Response
6
+ }
7
+
8
+ let serverEntryPromise: Promise<ServerEntry> | undefined
9
+
10
+ async function getServerEntry(): Promise<ServerEntry> {
11
+ if (!serverEntryPromise) {
12
+ serverEntryPromise = import('@tanstack/react-start/server-entry').then(
13
+ (m) => ((m as { default?: ServerEntry }).default ?? m) as ServerEntry,
14
+ )
15
+ }
16
+ return serverEntryPromise
17
+ }
18
+
19
+ export default {
20
+ async fetch(request: Request, env: unknown, ctx: unknown) {
21
+ try {
22
+ const handler = await getServerEntry()
23
+ return await handler.fetch(request, env, ctx)
24
+ } catch (error) {
25
+ console.error(error)
26
+ return new Response('<h1>Something went wrong</h1>', {
27
+ status: 500,
28
+ headers: { 'content-type': 'text/html; charset=utf-8' },
29
+ })
30
+ }
31
+ },
32
+ }
@@ -0,0 +1,214 @@
1
+ /*
2
+ * HelloLeo Design System -- READ BEFORE STYLING.
3
+ *
4
+ * Design tokens are the single source of truth and power the no-code Design
5
+ * panel (the user edits colors / fonts / radius visually). Conventions:
6
+ * - Raw tokens live in :root (light) and .dark (dark). The @media
7
+ * (prefers-color-scheme: dark) block mirrors .dark so apps follow the OS
8
+ * by default; adding the `dark` class still forces dark explicitly.
9
+ * - @theme inline maps each raw token to a Tailwind utility
10
+ * (bg-background, text-foreground, rounded-lg, ...). Edit the raw token,
11
+ * not the component class. Never hardcode #hex in components.
12
+ * - When you add a color token, add it to :root, .dark, AND the media block,
13
+ * or one theme breaks.
14
+ *
15
+ * UI primitives live in src/components/ui (shadcn/ui) and consume these
16
+ * tokens. One responsibility per file.
17
+ */
18
+
19
+ @import "tailwindcss" source(none);
20
+ @source "../src";
21
+ @import "tw-animate-css";
22
+
23
+ @custom-variant dark (&:is(.dark *));
24
+
25
+ @theme inline {
26
+ --radius-sm: calc(var(--radius) - 4px);
27
+ --radius-md: calc(var(--radius) - 2px);
28
+ --radius-lg: var(--radius);
29
+ --radius-xl: calc(var(--radius) + 4px);
30
+ --color-background: var(--background);
31
+ --color-foreground: var(--foreground);
32
+ --color-card: var(--card);
33
+ --color-card-foreground: var(--card-foreground);
34
+ --color-popover: var(--popover);
35
+ --color-popover-foreground: var(--popover-foreground);
36
+ --color-primary: var(--primary);
37
+ --color-primary-foreground: var(--primary-foreground);
38
+ --color-secondary: var(--secondary);
39
+ --color-secondary-foreground: var(--secondary-foreground);
40
+ --color-muted: var(--muted);
41
+ --color-muted-foreground: var(--muted-foreground);
42
+ --color-accent: var(--accent);
43
+ --color-accent-foreground: var(--accent-foreground);
44
+ --color-destructive: var(--destructive);
45
+ --color-destructive-foreground: var(--destructive-foreground);
46
+ --color-border: var(--border);
47
+ --color-input: var(--input);
48
+ --color-ring: var(--ring);
49
+ --color-chart-1: var(--chart-1);
50
+ --color-chart-2: var(--chart-2);
51
+ --color-chart-3: var(--chart-3);
52
+ --color-chart-4: var(--chart-4);
53
+ --color-chart-5: var(--chart-5);
54
+ --color-sidebar: var(--sidebar);
55
+ --color-sidebar-foreground: var(--sidebar-foreground);
56
+ --color-sidebar-primary: var(--sidebar-primary);
57
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
58
+ --color-sidebar-accent: var(--sidebar-accent);
59
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
60
+ --color-sidebar-border: var(--sidebar-border);
61
+ --color-sidebar-ring: var(--sidebar-ring);
62
+ --font-sans: "Inter", system-ui, sans-serif;
63
+ --font-display: "Space Grotesk", "Inter", sans-serif;
64
+ }
65
+
66
+ :root {
67
+ --radius: 0.625rem;
68
+ --background: oklch(1 0 0);
69
+ --foreground: oklch(0.145 0 0);
70
+ --card: oklch(1 0 0);
71
+ --card-foreground: oklch(0.145 0 0);
72
+ --popover: oklch(1 0 0);
73
+ --popover-foreground: oklch(0.145 0 0);
74
+ --primary: oklch(0.205 0 0);
75
+ --primary-foreground: oklch(0.985 0 0);
76
+ --secondary: oklch(0.97 0 0);
77
+ --secondary-foreground: oklch(0.205 0 0);
78
+ --muted: oklch(0.97 0 0);
79
+ --muted-foreground: oklch(0.556 0 0);
80
+ --accent: oklch(0.97 0 0);
81
+ --accent-foreground: oklch(0.205 0 0);
82
+ --destructive: oklch(0.577 0.245 27.325);
83
+ --destructive-foreground: oklch(0.985 0 0);
84
+ --border: oklch(0.922 0 0);
85
+ --input: oklch(0.922 0 0);
86
+ --ring: oklch(0.708 0 0);
87
+ --chart-1: oklch(0.646 0.222 41.116);
88
+ --chart-2: oklch(0.6 0.118 184.704);
89
+ --chart-3: oklch(0.398 0.07 227.392);
90
+ --chart-4: oklch(0.828 0.189 84.429);
91
+ --chart-5: oklch(0.769 0.188 70.08);
92
+ --sidebar: oklch(0.985 0 0);
93
+ --sidebar-foreground: oklch(0.145 0 0);
94
+ --sidebar-primary: oklch(0.205 0 0);
95
+ --sidebar-primary-foreground: oklch(0.985 0 0);
96
+ --sidebar-accent: oklch(0.97 0 0);
97
+ --sidebar-accent-foreground: oklch(0.205 0 0);
98
+ --sidebar-border: oklch(0.922 0 0);
99
+ --sidebar-ring: oklch(0.708 0 0);
100
+ }
101
+
102
+ /* Dark palette -- kept identical in .dark (explicit) and the media block
103
+ * (system default). Edit BOTH when changing a token. */
104
+ .dark {
105
+ --background: oklch(0.145 0 0);
106
+ --foreground: oklch(0.985 0 0);
107
+ --card: oklch(0.205 0 0);
108
+ --card-foreground: oklch(0.985 0 0);
109
+ --popover: oklch(0.205 0 0);
110
+ --popover-foreground: oklch(0.985 0 0);
111
+ --primary: oklch(0.922 0 0);
112
+ --primary-foreground: oklch(0.205 0 0);
113
+ --secondary: oklch(0.269 0 0);
114
+ --secondary-foreground: oklch(0.985 0 0);
115
+ --muted: oklch(0.269 0 0);
116
+ --muted-foreground: oklch(0.708 0 0);
117
+ --accent: oklch(0.269 0 0);
118
+ --accent-foreground: oklch(0.985 0 0);
119
+ --destructive: oklch(0.704 0.191 22.216);
120
+ --destructive-foreground: oklch(0.985 0 0);
121
+ --border: oklch(1 0 0 / 10%);
122
+ --input: oklch(1 0 0 / 15%);
123
+ --ring: oklch(0.556 0 0);
124
+ --chart-1: oklch(0.488 0.243 264.376);
125
+ --chart-2: oklch(0.696 0.17 162.48);
126
+ --chart-3: oklch(0.769 0.188 70.08);
127
+ --chart-4: oklch(0.627 0.265 303.9);
128
+ --chart-5: oklch(0.645 0.246 16.439);
129
+ --sidebar: oklch(0.205 0 0);
130
+ --sidebar-foreground: oklch(0.985 0 0);
131
+ --sidebar-primary: oklch(0.488 0.243 264.376);
132
+ --sidebar-primary-foreground: oklch(0.985 0 0);
133
+ --sidebar-accent: oklch(0.269 0 0);
134
+ --sidebar-accent-foreground: oklch(0.985 0 0);
135
+ --sidebar-border: oklch(1 0 0 / 10%);
136
+ --sidebar-ring: oklch(0.556 0 0);
137
+ }
138
+
139
+ @media (prefers-color-scheme: dark) {
140
+ :root {
141
+ --background: oklch(0.145 0 0);
142
+ --foreground: oklch(0.985 0 0);
143
+ --card: oklch(0.205 0 0);
144
+ --card-foreground: oklch(0.985 0 0);
145
+ --popover: oklch(0.205 0 0);
146
+ --popover-foreground: oklch(0.985 0 0);
147
+ --primary: oklch(0.922 0 0);
148
+ --primary-foreground: oklch(0.205 0 0);
149
+ --secondary: oklch(0.269 0 0);
150
+ --secondary-foreground: oklch(0.985 0 0);
151
+ --muted: oklch(0.269 0 0);
152
+ --muted-foreground: oklch(0.708 0 0);
153
+ --accent: oklch(0.269 0 0);
154
+ --accent-foreground: oklch(0.985 0 0);
155
+ --destructive: oklch(0.704 0.191 22.216);
156
+ --destructive-foreground: oklch(0.985 0 0);
157
+ --border: oklch(1 0 0 / 10%);
158
+ --input: oklch(1 0 0 / 15%);
159
+ --ring: oklch(0.556 0 0);
160
+ --chart-1: oklch(0.488 0.243 264.376);
161
+ --chart-2: oklch(0.696 0.17 162.48);
162
+ --chart-3: oklch(0.769 0.188 70.08);
163
+ --chart-4: oklch(0.627 0.265 303.9);
164
+ --chart-5: oklch(0.645 0.246 16.439);
165
+ --sidebar: oklch(0.205 0 0);
166
+ --sidebar-foreground: oklch(0.985 0 0);
167
+ --sidebar-primary: oklch(0.488 0.243 264.376);
168
+ --sidebar-primary-foreground: oklch(0.985 0 0);
169
+ --sidebar-accent: oklch(0.269 0 0);
170
+ --sidebar-accent-foreground: oklch(0.985 0 0);
171
+ --sidebar-border: oklch(1 0 0 / 10%);
172
+ --sidebar-ring: oklch(0.556 0 0);
173
+ }
174
+ }
175
+
176
+ @layer base {
177
+ * {
178
+ border-color: var(--color-border);
179
+ }
180
+ html,
181
+ body {
182
+ font-family: var(--font-sans);
183
+ }
184
+ body {
185
+ background-color: var(--color-background);
186
+ color: var(--color-foreground);
187
+ min-height: 100vh;
188
+ }
189
+ h1, h2, h3, h4 {
190
+ font-family: var(--font-display);
191
+ letter-spacing: -0.02em;
192
+ }
193
+ }
194
+
195
+ @layer utilities {
196
+ .prose-note {
197
+ color: var(--color-foreground);
198
+ }
199
+ .prose-note h1 { font-size: 1.875rem; font-weight: 700; margin: 1.25rem 0 0.75rem; font-family: var(--font-display); }
200
+ .prose-note h2 { font-size: 1.5rem; font-weight: 600; margin: 1rem 0 0.5rem; font-family: var(--font-display); }
201
+ .prose-note h3 { font-size: 1.25rem; font-weight: 600; margin: 0.875rem 0 0.5rem; }
202
+ .prose-note p { margin: 0.5rem 0; line-height: 1.7; }
203
+ .prose-note ul { list-style: disc; padding-left: 1.5rem; margin: 0.5rem 0; }
204
+ .prose-note ol { list-style: decimal; padding-left: 1.5rem; margin: 0.5rem 0; }
205
+ .prose-note li { margin: 0.25rem 0; }
206
+ .prose-note a { color: var(--color-primary); text-decoration: underline; }
207
+ .prose-note code { background: var(--color-muted); padding: 0.125rem 0.375rem; border-radius: 0.25rem; font-size: 0.875em; }
208
+ .prose-note pre { background: var(--color-muted); padding: 1rem; border-radius: 0.5rem; overflow-x: auto; margin: 0.75rem 0; }
209
+ .prose-note pre code { background: transparent; padding: 0; }
210
+ .prose-note blockquote { border-left: 3px solid var(--color-primary); padding-left: 1rem; margin: 0.75rem 0; color: var(--color-muted-foreground); }
211
+ .prose-note hr { border-color: var(--color-border); margin: 1.5rem 0; }
212
+ .prose-note table { border-collapse: collapse; margin: 0.75rem 0; }
213
+ .prose-note th, .prose-note td { border: 1px solid var(--color-border); padding: 0.375rem 0.75rem; }
214
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "include": ["**/*.ts", "**/*.tsx"],
3
+ "compilerOptions": {
4
+ "target": "ES2022",
5
+ "jsx": "react-jsx",
6
+ "module": "ESNext",
7
+ "paths": {
8
+ "#/*": ["./src/*"],
9
+ "@/*": ["./src/*"]
10
+ },
11
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
12
+ "types": ["@cloudflare/workers-types", "vite/client"],
13
+
14
+ /* Bundler mode */
15
+ "moduleResolution": "bundler",
16
+ "allowImportingTsExtensions": true,
17
+ "noEmit": true,
18
+
19
+ /* Linting */
20
+ "skipLibCheck": true,
21
+ "strict": true,
22
+ "noUnusedLocals": true,
23
+ "noUnusedParameters": true,
24
+ "noFallthroughCasesInSwitch": true,
25
+ "noUncheckedSideEffectImports": true
26
+ }
27
+ }
@@ -0,0 +1,17 @@
1
+ import { defineConfig } from '@helloleo/vite-config'
2
+
3
+ // @helloleo/vite-config owns the whole build/dev/sandbox pipeline: in dev it
4
+ // aliases `cloudflare:workers` -> @helloleo/runtime/cf-shim (libsql .dev.db for
5
+ // D1+KV, local disk for R2) and runs plain Vite SSR; at build it adds
6
+ // @cloudflare/vite-plugin to emit a workerd Worker bound to wrangler.jsonc. It
7
+ // also binds host:true/port:5173 itself (>=0.1.2).
8
+ //
9
+ // The one thing the wrapper doesn't set: HMR over the HelloLeo preview proxy,
10
+ // which terminates HTTPS on :443 — so point the HMR client at wss://…:443.
11
+ export default defineConfig({
12
+ vite: {
13
+ server: {
14
+ hmr: { clientPort: 443, protocol: 'wss' },
15
+ },
16
+ },
17
+ })
@@ -2,17 +2,18 @@ import { mkdir } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import type { Plugin } from "@opencode-ai/plugin";
4
4
  import { tool } from "@opencode-ai/plugin";
5
+ import { z } from "zod/v4";
5
6
  import { renderTemplate } from "../template-render.ts";
6
7
 
7
8
  /**
8
9
  * Identifier for the scaffold template a project was generated from. It is
9
10
  * also the directory name under src/templates/. Written to .leo/project.json
10
11
  * so the webapp / leo-core can tell which template (and therefore which
11
- * conventions: TanStack Router file routing, Tailwind v4, shadcn/ui) a
12
+ * conventions: TanStack Start file routing + SSR, Tailwind v4, shadcn/ui) a
12
13
  * workspace was bootstrapped with. Bump this (and add a new templates/<id>/
13
14
  * dir) when the scaffold's shape changes.
14
15
  */
15
- export const SCAFFOLD_VERSION = "tanstack-router-2026-06-15";
16
+ export const SCAFFOLD_VERSION = "tanstack-start-2026-06-18";
16
17
 
17
18
  /**
18
19
  * The project workspace directory is named after the project UUID -- the
@@ -25,25 +26,31 @@ export function projectUuidFromDir(dir: string): string {
25
26
  return path.basename(dir);
26
27
  }
27
28
 
28
- export interface BaseScaffoldOptions {
29
- name?: string;
30
- }
29
+ const scaffoldOptionsSchema = z.object({
30
+ name: z
31
+ .string()
32
+ .describe("Short product name. Used as the page title and package name."),
33
+ description: z
34
+ .string()
35
+ .describe("One sentence describing the app. Used as the meta description."),
36
+ });
31
37
 
32
38
  /**
33
- * Write the base scaffold by rendering the TanStack Router template into dir,
39
+ * Write the base scaffold by rendering the TanStack Start template into dir,
34
40
  * then adding .leo/project.json (template metadata the tool owns, not part of
35
- * the template tree). Shared between `scaffold` (frontend-only) and
36
- * `setup_netsuite` (base + OAuth files). Returns the relative paths written.
41
+ * the template tree). Returns the relative paths written.
37
42
  */
38
43
  export async function writeBaseScaffold(
39
- opts: BaseScaffoldOptions,
44
+ { name, description }: z.infer<typeof scaffoldOptionsSchema>,
40
45
  dir: string,
41
46
  ): Promise<string[]> {
42
- const name = opts.name ?? "leo-project";
43
-
44
47
  const created = await renderTemplate(
45
48
  SCAFFOLD_VERSION,
46
- { LEO_NAME: name, LEO_PROJECT_UUID: projectUuidFromDir(dir) },
49
+ {
50
+ LEO_NAME: name,
51
+ LEO_DESCRIPTION: description,
52
+ LEO_PROJECT_UUID: projectUuidFromDir(dir),
53
+ },
47
54
  dir,
48
55
  );
49
56
 
@@ -59,44 +66,36 @@ export async function writeBaseScaffold(
59
66
 
60
67
  export const scaffold = tool({
61
68
  description:
62
- "Scaffold a new React 19 + Vite + Tailwind CSS v4 + TanStack Router project " +
63
- "with shadcn/ui baked in. Renders the full template: TanStack file-based " +
64
- "routing (src/routes/), the complete shadcn/ui component set under " +
65
- "src/components/ui, src/lib/utils.ts (cn helper), src/lib/proxy.ts " +
66
- "(HelloLeo integration proxy), src/styles.css (shadcn design tokens, " +
67
- "light+dark), plus package.json, vite.config.ts, tsconfig.json, " +
68
- "tsr.config.json, components.json, .gitignore and the generated " +
69
- "src/routeTree.gen.ts. Call this FIRST for any new project before adding " +
70
- "routes and pages. For NetSuite projects (NetSuite MCP in the live list), " +
71
- "call `setup_netsuite` INSTEAD -- it bundles base scaffold + OAuth wiring " +
72
- "in one call. Do not call both.",
69
+ "Scaffold a new TanStack Start project with React 19, SSR, file-based " +
70
+ "routing, Tailwind v4, shadcn/ui, react-query, and a database, KV and " +
71
+ "bucket via @helloleo/runtime. Call this first for any new project, " +
72
+ "before writing routes or components.",
73
73
  args: {
74
- name: tool.schema
75
- .string()
76
- .optional()
77
- .describe("Project name for package.json (default: leo-project)"),
74
+ name: scaffoldOptionsSchema.shape.name,
75
+ description: scaffoldOptionsSchema.shape.description,
78
76
  },
79
77
  async execute(args, context) {
80
78
  const created = await writeBaseScaffold(args, context.directory);
79
+ const uiComponents = created.filter((f) =>
80
+ f.startsWith("src/components/ui/"),
81
+ );
82
+ const rest = created.filter((f) => !f.startsWith("src/components/ui/"));
81
83
  return [
82
84
  `Scaffolded ${created.length} files:`,
83
- ...created.map((f) => ` - ${f}`),
85
+ ...rest.map((f) => ` - ${f}`),
86
+ ` - src/components/ui/ (${uiComponents.length} shadcn/ui components)`,
87
+ "",
88
+ "Notes:",
89
+ "- This is a TanStack Start project. The server entry is src/server.ts. There is no index.html or client entrypoint.",
90
+ "- Add routes as files in src/routes/. Do not default export route components.",
91
+ "- For JSON/CRUD APIs, add server routes under src/routes/api/. Read src/routes/api/health.ts first, add routes as needed.",
92
+ "- shadcn/ui components are already in src/components/ui.",
93
+ "- Design system: edit the tokens in src/styles.css (light and dark blocks).",
94
+ "- The database schema lives in src/db/schema.ts. Migrations run automatically.",
95
+ "- Always fetch data with react-query (useQuery and useMutation).",
84
96
  "",
85
- "Next: add routes as files under src/routes/ (TanStack file-based",
86
- " routing). Edit src/routes/index.tsx for the home page.",
87
- "UI: shadcn/ui components are already in src/components/ui -- import and",
88
- " use them; do not attempt to re-add via the shadcn CLI.",
89
- "Design system: edit tokens in src/styles.css (light + dark blocks).",
90
- "Data fetching: React Query is wired (QueryClient on the router context +",
91
- " QueryClientProvider). ALWAYS fetch with @tanstack/react-query --",
92
- " useQuery / useMutation in components, or a route loader calling",
93
- " context.queryClient.ensureQueryData(). Do NOT hand-roll fetches with",
94
- " useEffect + useState. Wrap the fetch call (usually callIntegration())",
95
- " in the queryFn.",
96
- "Integration calls: use callIntegration() from src/lib/proxy.ts -- as the",
97
- " queryFn body, never on its own in useEffect. Its header comment",
98
- " documents slugs, response unwrapping, pagination, and frontend-only vs",
99
- " HelloLeo Cloud routing.",
97
+ "Next: read src/routes/index.tsx and continue with the user's request.",
98
+ "For NetSuite projects, call setup_netsuite.",
100
99
  ].join("\n");
101
100
  },
102
101
  });