@helloleo/plugins 0.2.12 → 0.2.14

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 (145) hide show
  1. package/dist/index.js +3 -2
  2. package/dist/templates/tanstack-start-2026-07-30/components.json +22 -0
  3. package/dist/templates/tanstack-start-2026-07-30/drizzle/0000_tidy_kang.sql +5 -0
  4. package/dist/templates/tanstack-start-2026-07-30/drizzle/meta/0000_snapshot.json +50 -0
  5. package/dist/templates/tanstack-start-2026-07-30/drizzle/meta/_journal.json +13 -0
  6. package/dist/templates/tanstack-start-2026-07-30/drizzle.config.ts +18 -0
  7. package/dist/templates/tanstack-start-2026-07-30/package.json +103 -0
  8. package/dist/templates/tanstack-start-2026-07-30/src/components/error-component.tsx +47 -0
  9. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/accordion.tsx +51 -0
  10. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/alert-dialog.tsx +115 -0
  11. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/alert.tsx +49 -0
  12. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/aspect-ratio.tsx +5 -0
  13. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/avatar.tsx +47 -0
  14. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/badge.tsx +32 -0
  15. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/breadcrumb.tsx +101 -0
  16. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/button.tsx +49 -0
  17. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/calendar.tsx +177 -0
  18. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/card.tsx +55 -0
  19. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/carousel.tsx +240 -0
  20. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/chart.tsx +371 -0
  21. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/checkbox.tsx +26 -0
  22. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/collapsible.tsx +11 -0
  23. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/command.tsx +143 -0
  24. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/context-menu.tsx +187 -0
  25. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/dialog.tsx +104 -0
  26. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/drawer.tsx +98 -0
  27. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/dropdown-menu.tsx +188 -0
  28. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/form.tsx +171 -0
  29. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/hover-card.tsx +27 -0
  30. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/input-otp.tsx +69 -0
  31. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/input.tsx +22 -0
  32. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/label.tsx +21 -0
  33. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/menubar.tsx +229 -0
  34. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/navigation-menu.tsx +120 -0
  35. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/pagination.tsx +98 -0
  36. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/popover.tsx +31 -0
  37. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/progress.tsx +25 -0
  38. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/radio-group.tsx +36 -0
  39. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/resizable.tsx +37 -0
  40. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/scroll-area.tsx +44 -0
  41. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/select.tsx +152 -0
  42. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/separator.tsx +24 -0
  43. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/sheet.tsx +122 -0
  44. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/sidebar.tsx +744 -0
  45. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/skeleton.tsx +7 -0
  46. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/slider.tsx +23 -0
  47. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/sonner.tsx +23 -0
  48. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/switch.tsx +27 -0
  49. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/table.tsx +94 -0
  50. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/tabs.tsx +53 -0
  51. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/textarea.tsx +21 -0
  52. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/toggle-group.tsx +57 -0
  53. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/toggle.tsx +42 -0
  54. package/dist/templates/tanstack-start-2026-07-30/src/components/ui/tooltip.tsx +32 -0
  55. package/dist/templates/tanstack-start-2026-07-30/src/db/index.ts +9 -0
  56. package/dist/templates/tanstack-start-2026-07-30/src/db/schema.ts +18 -0
  57. package/dist/templates/tanstack-start-2026-07-30/src/global.d.ts +3 -0
  58. package/dist/templates/tanstack-start-2026-07-30/src/hooks/use-mobile.tsx +19 -0
  59. package/dist/templates/tanstack-start-2026-07-30/src/lib/kv.ts +37 -0
  60. package/dist/templates/tanstack-start-2026-07-30/src/lib/proxy.ts +82 -0
  61. package/dist/templates/tanstack-start-2026-07-30/src/lib/storage.ts +44 -0
  62. package/dist/templates/tanstack-start-2026-07-30/src/lib/utils.ts +6 -0
  63. package/dist/templates/tanstack-start-2026-07-30/src/routeTree.gen.ts +86 -0
  64. package/dist/templates/tanstack-start-2026-07-30/src/router.tsx +37 -0
  65. package/dist/templates/tanstack-start-2026-07-30/src/routes/README.md +94 -0
  66. package/dist/templates/tanstack-start-2026-07-30/src/routes/__root.tsx +65 -0
  67. package/dist/templates/tanstack-start-2026-07-30/src/routes/api/health.ts +81 -0
  68. package/dist/templates/tanstack-start-2026-07-30/src/routes/index.tsx +33 -0
  69. package/dist/templates/tanstack-start-2026-07-30/src/server.ts +32 -0
  70. package/dist/templates/tanstack-start-2026-07-30/src/styles.css +214 -0
  71. package/dist/templates/tanstack-start-2026-07-30/tsconfig.json +27 -0
  72. package/dist/templates/tanstack-start-2026-07-30/vite.config.ts +17 -0
  73. package/package.json +1 -1
  74. package/src/templates/tanstack-start-2026-07-30/components.json +22 -0
  75. package/src/templates/tanstack-start-2026-07-30/drizzle/0000_tidy_kang.sql +5 -0
  76. package/src/templates/tanstack-start-2026-07-30/drizzle/meta/0000_snapshot.json +50 -0
  77. package/src/templates/tanstack-start-2026-07-30/drizzle/meta/_journal.json +13 -0
  78. package/src/templates/tanstack-start-2026-07-30/drizzle.config.ts +18 -0
  79. package/src/templates/tanstack-start-2026-07-30/package.json +103 -0
  80. package/src/templates/tanstack-start-2026-07-30/src/components/error-component.tsx +47 -0
  81. package/src/templates/tanstack-start-2026-07-30/src/components/ui/accordion.tsx +51 -0
  82. package/src/templates/tanstack-start-2026-07-30/src/components/ui/alert-dialog.tsx +115 -0
  83. package/src/templates/tanstack-start-2026-07-30/src/components/ui/alert.tsx +49 -0
  84. package/src/templates/tanstack-start-2026-07-30/src/components/ui/aspect-ratio.tsx +5 -0
  85. package/src/templates/tanstack-start-2026-07-30/src/components/ui/avatar.tsx +47 -0
  86. package/src/templates/tanstack-start-2026-07-30/src/components/ui/badge.tsx +32 -0
  87. package/src/templates/tanstack-start-2026-07-30/src/components/ui/breadcrumb.tsx +101 -0
  88. package/src/templates/tanstack-start-2026-07-30/src/components/ui/button.tsx +49 -0
  89. package/src/templates/tanstack-start-2026-07-30/src/components/ui/calendar.tsx +177 -0
  90. package/src/templates/tanstack-start-2026-07-30/src/components/ui/card.tsx +55 -0
  91. package/src/templates/tanstack-start-2026-07-30/src/components/ui/carousel.tsx +240 -0
  92. package/src/templates/tanstack-start-2026-07-30/src/components/ui/chart.tsx +371 -0
  93. package/src/templates/tanstack-start-2026-07-30/src/components/ui/checkbox.tsx +26 -0
  94. package/src/templates/tanstack-start-2026-07-30/src/components/ui/collapsible.tsx +11 -0
  95. package/src/templates/tanstack-start-2026-07-30/src/components/ui/command.tsx +143 -0
  96. package/src/templates/tanstack-start-2026-07-30/src/components/ui/context-menu.tsx +187 -0
  97. package/src/templates/tanstack-start-2026-07-30/src/components/ui/dialog.tsx +104 -0
  98. package/src/templates/tanstack-start-2026-07-30/src/components/ui/drawer.tsx +98 -0
  99. package/src/templates/tanstack-start-2026-07-30/src/components/ui/dropdown-menu.tsx +188 -0
  100. package/src/templates/tanstack-start-2026-07-30/src/components/ui/form.tsx +171 -0
  101. package/src/templates/tanstack-start-2026-07-30/src/components/ui/hover-card.tsx +27 -0
  102. package/src/templates/tanstack-start-2026-07-30/src/components/ui/input-otp.tsx +69 -0
  103. package/src/templates/tanstack-start-2026-07-30/src/components/ui/input.tsx +22 -0
  104. package/src/templates/tanstack-start-2026-07-30/src/components/ui/label.tsx +21 -0
  105. package/src/templates/tanstack-start-2026-07-30/src/components/ui/menubar.tsx +229 -0
  106. package/src/templates/tanstack-start-2026-07-30/src/components/ui/navigation-menu.tsx +120 -0
  107. package/src/templates/tanstack-start-2026-07-30/src/components/ui/pagination.tsx +98 -0
  108. package/src/templates/tanstack-start-2026-07-30/src/components/ui/popover.tsx +31 -0
  109. package/src/templates/tanstack-start-2026-07-30/src/components/ui/progress.tsx +25 -0
  110. package/src/templates/tanstack-start-2026-07-30/src/components/ui/radio-group.tsx +36 -0
  111. package/src/templates/tanstack-start-2026-07-30/src/components/ui/resizable.tsx +37 -0
  112. package/src/templates/tanstack-start-2026-07-30/src/components/ui/scroll-area.tsx +44 -0
  113. package/src/templates/tanstack-start-2026-07-30/src/components/ui/select.tsx +152 -0
  114. package/src/templates/tanstack-start-2026-07-30/src/components/ui/separator.tsx +24 -0
  115. package/src/templates/tanstack-start-2026-07-30/src/components/ui/sheet.tsx +122 -0
  116. package/src/templates/tanstack-start-2026-07-30/src/components/ui/sidebar.tsx +744 -0
  117. package/src/templates/tanstack-start-2026-07-30/src/components/ui/skeleton.tsx +7 -0
  118. package/src/templates/tanstack-start-2026-07-30/src/components/ui/slider.tsx +23 -0
  119. package/src/templates/tanstack-start-2026-07-30/src/components/ui/sonner.tsx +23 -0
  120. package/src/templates/tanstack-start-2026-07-30/src/components/ui/switch.tsx +27 -0
  121. package/src/templates/tanstack-start-2026-07-30/src/components/ui/table.tsx +94 -0
  122. package/src/templates/tanstack-start-2026-07-30/src/components/ui/tabs.tsx +53 -0
  123. package/src/templates/tanstack-start-2026-07-30/src/components/ui/textarea.tsx +21 -0
  124. package/src/templates/tanstack-start-2026-07-30/src/components/ui/toggle-group.tsx +57 -0
  125. package/src/templates/tanstack-start-2026-07-30/src/components/ui/toggle.tsx +42 -0
  126. package/src/templates/tanstack-start-2026-07-30/src/components/ui/tooltip.tsx +32 -0
  127. package/src/templates/tanstack-start-2026-07-30/src/db/index.ts +9 -0
  128. package/src/templates/tanstack-start-2026-07-30/src/db/schema.ts +18 -0
  129. package/src/templates/tanstack-start-2026-07-30/src/global.d.ts +3 -0
  130. package/src/templates/tanstack-start-2026-07-30/src/hooks/use-mobile.tsx +19 -0
  131. package/src/templates/tanstack-start-2026-07-30/src/lib/kv.ts +37 -0
  132. package/src/templates/tanstack-start-2026-07-30/src/lib/proxy.ts +82 -0
  133. package/src/templates/tanstack-start-2026-07-30/src/lib/storage.ts +44 -0
  134. package/src/templates/tanstack-start-2026-07-30/src/lib/utils.ts +6 -0
  135. package/src/templates/tanstack-start-2026-07-30/src/routeTree.gen.ts +86 -0
  136. package/src/templates/tanstack-start-2026-07-30/src/router.tsx +37 -0
  137. package/src/templates/tanstack-start-2026-07-30/src/routes/README.md +94 -0
  138. package/src/templates/tanstack-start-2026-07-30/src/routes/__root.tsx +65 -0
  139. package/src/templates/tanstack-start-2026-07-30/src/routes/api/health.ts +81 -0
  140. package/src/templates/tanstack-start-2026-07-30/src/routes/index.tsx +33 -0
  141. package/src/templates/tanstack-start-2026-07-30/src/server.ts +32 -0
  142. package/src/templates/tanstack-start-2026-07-30/src/styles.css +214 -0
  143. package/src/templates/tanstack-start-2026-07-30/tsconfig.json +27 -0
  144. package/src/templates/tanstack-start-2026-07-30/vite.config.ts +17 -0
  145. package/src/tools/scaffold.ts +3 -2
@@ -0,0 +1,86 @@
1
+ /* eslint-disable */
2
+
3
+ // @ts-nocheck
4
+
5
+ // noinspection JSUnusedGlobalSymbols
6
+
7
+ // This file was automatically generated by TanStack Router.
8
+ // You should NOT make any changes in this file as it will be overwritten.
9
+ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
10
+
11
+ import { Route as rootRouteImport } from './routes/__root'
12
+ import { Route as IndexRouteImport } from './routes/index'
13
+ import { Route as ApiHealthRouteImport } from './routes/api/health'
14
+
15
+ const IndexRoute = IndexRouteImport.update({
16
+ id: '/',
17
+ path: '/',
18
+ getParentRoute: () => rootRouteImport,
19
+ } as any)
20
+ const ApiHealthRoute = ApiHealthRouteImport.update({
21
+ id: '/api/health',
22
+ path: '/api/health',
23
+ getParentRoute: () => rootRouteImport,
24
+ } as any)
25
+
26
+ export interface FileRoutesByFullPath {
27
+ '/': typeof IndexRoute
28
+ '/api/health': typeof ApiHealthRoute
29
+ }
30
+ export interface FileRoutesByTo {
31
+ '/': typeof IndexRoute
32
+ '/api/health': typeof ApiHealthRoute
33
+ }
34
+ export interface FileRoutesById {
35
+ __root__: typeof rootRouteImport
36
+ '/': typeof IndexRoute
37
+ '/api/health': typeof ApiHealthRoute
38
+ }
39
+ export interface FileRouteTypes {
40
+ fileRoutesByFullPath: FileRoutesByFullPath
41
+ fullPaths: '/' | '/api/health'
42
+ fileRoutesByTo: FileRoutesByTo
43
+ to: '/' | '/api/health'
44
+ id: '__root__' | '/' | '/api/health'
45
+ fileRoutesById: FileRoutesById
46
+ }
47
+ export interface RootRouteChildren {
48
+ IndexRoute: typeof IndexRoute
49
+ ApiHealthRoute: typeof ApiHealthRoute
50
+ }
51
+
52
+ declare module '@tanstack/react-router' {
53
+ interface FileRoutesByPath {
54
+ '/': {
55
+ id: '/'
56
+ path: '/'
57
+ fullPath: '/'
58
+ preLoaderRoute: typeof IndexRouteImport
59
+ parentRoute: typeof rootRouteImport
60
+ }
61
+ '/api/health': {
62
+ id: '/api/health'
63
+ path: '/api/health'
64
+ fullPath: '/api/health'
65
+ preLoaderRoute: typeof ApiHealthRouteImport
66
+ parentRoute: typeof rootRouteImport
67
+ }
68
+ }
69
+ }
70
+
71
+ const rootRouteChildren: RootRouteChildren = {
72
+ IndexRoute: IndexRoute,
73
+ ApiHealthRoute: ApiHealthRoute,
74
+ }
75
+ export const routeTree = rootRouteImport
76
+ ._addFileChildren(rootRouteChildren)
77
+ ._addFileTypes<FileRouteTypes>()
78
+
79
+ import type { getRouter } from './router.tsx'
80
+ import type { createStart } from '@tanstack/react-start'
81
+ declare module '@tanstack/react-start' {
82
+ interface Register {
83
+ ssr: true
84
+ router: Awaited<ReturnType<typeof getRouter>>
85
+ }
86
+ }
@@ -0,0 +1,37 @@
1
+ import { QueryClient } from '@tanstack/react-query'
2
+ import { createRouter as createTanStackRouter } from '@tanstack/react-router'
3
+ import { routerWithQueryClient } from '@tanstack/react-router-with-query'
4
+ import { routeTree } from './routeTree.gen'
5
+ import { DefaultErrorComponent } from '@/components/error-component'
6
+
7
+ // TanStack Start calls getRouter() on both server and client. routerWithQueryClient
8
+ // wires the QueryClient into the router context and handles SSR dehydration /
9
+ // rehydration, so loaders can call context.queryClient.ensureQueryData() and the
10
+ // client picks the cache up without refetching.
11
+ export function getRouter() {
12
+ const queryClient = new QueryClient()
13
+
14
+ const router = routerWithQueryClient(
15
+ createTanStackRouter({
16
+ routeTree,
17
+ context: { queryClient },
18
+ scrollRestoration: true,
19
+ // Catches uncaught render/loader errors on any route. Routes can still
20
+ // set their own `errorComponent` to override this.
21
+ defaultErrorComponent: DefaultErrorComponent,
22
+ defaultPreload: 'intent',
23
+ // With React Query we never want loader data to be stale -- the loader runs
24
+ // on every preload/visit and React Query owns caching from there.
25
+ defaultPreloadStaleTime: 0,
26
+ }),
27
+ queryClient,
28
+ )
29
+
30
+ return router
31
+ }
32
+
33
+ declare module '@tanstack/react-router' {
34
+ interface Register {
35
+ router: ReturnType<typeof getRouter>
36
+ }
37
+ }
@@ -0,0 +1,94 @@
1
+ # Routes
2
+
3
+ TanStack Start uses file-based routing. Every `.ts` and `.tsx` file in this directory is a route. Do not create `src/pages/`, `app/layout.tsx`, or similar, those are Next.js / Remix conventions. The only root layout is `src/routes/__root.tsx`.
4
+
5
+ ## Conventions
6
+
7
+ | File | URL |
8
+ | --- | --- |
9
+ | `index.tsx` | `/` |
10
+ | `about.tsx` | `/about` |
11
+ | `users/index.tsx` | `/users` |
12
+ | `users/$id.tsx` | `/users/:id` (dynamic — bare `$`, no curly braces) |
13
+ | `posts.$postId.tsx` | `/posts/:postId` (dots nest without folders) |
14
+ | `admin/route.tsx` | layout for every `/admin/*` page; must render `<Outlet />` |
15
+ | `admin_.login.tsx` | `/admin/login`, but outside the admin layout (trailing `_` escapes it) |
16
+ | `_auth.tsx` | pathless layout; wraps `_auth/*` files without adding a URL segment |
17
+ | `api/health.ts` | server route for defining API endpoints |
18
+ | `__root.tsx` | app shell, wraps every page; preserve `<Outlet />` |
19
+
20
+ ## Rules
21
+
22
+ - A layout route without `<Outlet />` renders its children nowhere.
23
+ - Do not `export default` route components, it breaks code splitting. Use a named function passed as `component:`.
24
+ - `routeTree.gen.ts` is auto-generated. Don't edit it by hand.
25
+
26
+ ## API routes vs server functions
27
+
28
+ TanStack Start offers two ways to run server-side code:
29
+
30
+ - [Server routes](https://tanstack.com/start/latest/docs/framework/react/guide/server-routes) — files under `src/routes/api/` that export HTTP handlers.
31
+ - [Server functions](https://tanstack.com/start/latest/docs/framework/react/guide/server-functions) — `createServerFn()` calls colocated with components.
32
+
33
+ Prefer API routes. Create them under `src/routes/api/` following the same convention as `src/routes/api/health.ts` (one handler per HTTP method under `server.handlers`, returning `Response.json()`). This is more scalable and gives the user visually readable endpoints (`/api/todos`, `/api/todos/:id`) they can recognize in the network monitor. Server functions are served from generated URLs that read as random gibberish, which makes requests hard to identify and debug.
34
+
35
+ ## Authenticated routes
36
+
37
+ Put protected pages inside a pathless `_authed` layout and keep login/signup
38
+ at the top level, outside it. Never special-case the login pathname inside
39
+ the layout guard.
40
+
41
+ ```
42
+ routes/
43
+ __root.tsx loads the session into router context (context.user)
44
+ _authed.tsx pathless guard — no URL segment of its own
45
+ _authed/
46
+ dashboard.tsx /dashboard (protected)
47
+ posts.tsx /posts (protected)
48
+ index.tsx / (public)
49
+ login.tsx /login (public)
50
+ logout.tsx /logout
51
+ ```
52
+
53
+ `_authed.tsx` does the guarding, all children inherit it:
54
+
55
+ ```tsx _authed.tsx
56
+ export const Route = createFileRoute('/_authed')({
57
+ beforeLoad: ({ context }) => {
58
+ if (!context.user) {
59
+ throw redirect({ to: '/login' })
60
+ }
61
+ },
62
+ })
63
+ ```
64
+
65
+ The session is loaded once in `__root.tsx`'s `beforeLoad` and returned into
66
+ the router context so every route can read `context.user`. `getUser()` is
67
+ whatever your auth uses (e.g. verify a JWT from localStorage) and resolves to
68
+ the user or null:
69
+
70
+ ```tsx __root.tsx
71
+ // (keep the existing head() and component, add this)
72
+ export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()({
73
+ beforeLoad: async () => {
74
+ const user = await getUser()
75
+ return { user }
76
+ },
77
+ // ...head, component unchanged
78
+ })
79
+ ```
80
+
81
+ Components read it with `Route.useRouteContext()` (e.g. to swap a Login link
82
+ for a Logout button in the shell):
83
+
84
+ ```tsx
85
+ function Route() {
86
+ const { user } = Route.useRouteContext()
87
+
88
+ return (
89
+ <nav>
90
+ {user ? <Link to="/logout">Logout</Link> : <Link to="/login">Login</Link>}
91
+ </nav>
92
+ )
93
+ }
94
+ ```
@@ -0,0 +1,65 @@
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
+ import { Toaster } from '@/components/ui/sonner'
10
+
11
+ // Side-effect order matters: Tailwind needs to be linked into the SSR'd
12
+ // document. Importing the stylesheet as a URL and listing it in `head.links`
13
+ // is the Start-native way (a bare `import './styles.css'` only works client-side).
14
+ import appCss from '../styles.css?url'
15
+
16
+ export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()({
17
+ head: () => ({
18
+ meta: [
19
+ { charSet: 'utf-8' },
20
+ { name: 'viewport', content: 'width=device-width, initial-scale=1.0' },
21
+ { title: '{{LEO_NAME}}' },
22
+ { name: 'description', content: '{{LEO_DESCRIPTION}}' },
23
+ { property: 'og:title', content: '{{LEO_NAME}}' },
24
+ { property: 'og:description', content: '{{LEO_DESCRIPTION}}' },
25
+ { property: 'og:type', content: 'website' },
26
+ ],
27
+ links: [
28
+ { rel: 'stylesheet', href: appCss },
29
+ { rel: 'preconnect', href: 'https://fonts.googleapis.com' },
30
+ {
31
+ rel: 'preconnect',
32
+ href: 'https://fonts.gstatic.com',
33
+ crossOrigin: 'anonymous',
34
+ },
35
+ {
36
+ rel: 'stylesheet',
37
+ href: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Space+Grotesk:wght@400;500;600;700&display=swap',
38
+ },
39
+ ],
40
+ }),
41
+ component: RootComponent,
42
+ })
43
+
44
+ function RootComponent() {
45
+ return (
46
+ <RootDocument>
47
+ <Outlet />
48
+ </RootDocument>
49
+ )
50
+ }
51
+
52
+ function RootDocument({ children }: Readonly<{ children: ReactNode }>) {
53
+ return (
54
+ <html lang="en">
55
+ <head>
56
+ <HeadContent />
57
+ </head>
58
+ <body>
59
+ {children}
60
+ <Toaster richColors position="top-right" />
61
+ <Scripts />
62
+ </body>
63
+ </html>
64
+ )
65
+ }
@@ -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
+ }