create-croissant 0.1.55 → 0.1.57

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 (120) hide show
  1. package/dist/add.js +5 -5
  2. package/dist/index.js +1 -1
  3. package/package.json +1 -1
  4. package/template/README.md +10 -3
  5. package/template/apps/desktop/.vscode/extensions.json +1 -1
  6. package/template/apps/desktop/README.md +3 -30
  7. package/template/apps/desktop/index.html +14 -0
  8. package/template/apps/desktop/package.json +21 -32
  9. package/template/apps/desktop/public/tauri.svg +6 -0
  10. package/template/apps/desktop/public/vite.svg +1 -0
  11. package/template/apps/desktop/src/App.css +116 -0
  12. package/template/apps/desktop/src/App.tsx +51 -0
  13. package/template/apps/desktop/src/assets/react.svg +1 -0
  14. package/template/apps/desktop/src/{renderer/src/components → components}/app-sidebar.tsx +7 -3
  15. package/template/apps/desktop/src/components/login-form.tsx +160 -0
  16. package/template/apps/desktop/src/components/search-form.tsx +19 -0
  17. package/template/apps/desktop/src/components/signup-form.tsx +206 -0
  18. package/template/apps/desktop/src/components/version-switcher.tsx +54 -0
  19. package/template/apps/desktop/src/env.d.ts +1 -0
  20. package/template/apps/desktop/src/lib/auth-client.ts +5 -0
  21. package/template/apps/desktop/src/lib/orpc.ts +10 -0
  22. package/template/apps/desktop/src/main.tsx +12 -0
  23. package/template/apps/desktop/src/{renderer/src/routeTree.gen.ts → routeTree.gen.ts} +21 -0
  24. package/template/apps/desktop/src/router.tsx +19 -0
  25. package/template/apps/desktop/src/{renderer/src/routes → routes}/__root.tsx +9 -5
  26. package/template/apps/desktop/src/{renderer/src/routes → routes}/_auth/account.tsx +18 -10
  27. package/template/apps/desktop/src/routes/_auth/dashboard.tsx +58 -0
  28. package/template/apps/desktop/src/{renderer/src/routes → routes}/_auth/examples/client-orpc-auth.tsx +17 -6
  29. package/template/apps/desktop/src/{renderer/src/routes → routes}/_auth.tsx +2 -14
  30. package/template/apps/desktop/src/{renderer/src/routes → routes}/_public/examples/client-orpc.tsx +25 -5
  31. package/template/apps/desktop/src/{renderer/src/routes → routes}/_public/index.tsx +21 -9
  32. package/template/apps/desktop/src/routes/_public/login.tsx +34 -0
  33. package/template/apps/desktop/src/routes/_public/signup.tsx +31 -0
  34. package/template/apps/desktop/src/{renderer/src/routes → routes}/_public.tsx +1 -1
  35. package/template/apps/desktop/src/vite-env.d.ts +1 -0
  36. package/template/apps/desktop/src-tauri/Cargo.toml +25 -0
  37. package/template/apps/desktop/src-tauri/build.rs +3 -0
  38. package/template/apps/desktop/src-tauri/capabilities/default.json +7 -0
  39. package/template/apps/desktop/src-tauri/icons/128x128.png +0 -0
  40. package/template/apps/desktop/src-tauri/icons/128x128@2x.png +0 -0
  41. package/template/apps/desktop/src-tauri/icons/32x32.png +0 -0
  42. package/template/apps/desktop/src-tauri/icons/Square107x107Logo.png +0 -0
  43. package/template/apps/desktop/src-tauri/icons/Square142x142Logo.png +0 -0
  44. package/template/apps/desktop/src-tauri/icons/Square150x150Logo.png +0 -0
  45. package/template/apps/desktop/src-tauri/icons/Square284x284Logo.png +0 -0
  46. package/template/apps/desktop/src-tauri/icons/Square30x30Logo.png +0 -0
  47. package/template/apps/desktop/src-tauri/icons/Square310x310Logo.png +0 -0
  48. package/template/apps/desktop/src-tauri/icons/Square44x44Logo.png +0 -0
  49. package/template/apps/desktop/src-tauri/icons/Square71x71Logo.png +0 -0
  50. package/template/apps/desktop/src-tauri/icons/Square89x89Logo.png +0 -0
  51. package/template/apps/desktop/src-tauri/icons/StoreLogo.png +0 -0
  52. package/template/apps/desktop/src-tauri/icons/icon.icns +0 -0
  53. package/template/apps/desktop/src-tauri/icons/icon.ico +0 -0
  54. package/template/apps/desktop/src-tauri/icons/icon.png +0 -0
  55. package/template/apps/desktop/src-tauri/src/lib.rs +14 -0
  56. package/template/apps/desktop/src-tauri/src/main.rs +6 -0
  57. package/template/apps/desktop/src-tauri/tauri.conf.json +35 -0
  58. package/template/apps/desktop/tsconfig.json +15 -4
  59. package/template/apps/desktop/tsconfig.node.json +5 -3
  60. package/template/apps/desktop/vite.config.ts +40 -0
  61. package/template/apps/mobile/app/(tabs)/_layout.tsx +11 -10
  62. package/template/apps/mobile/app/(tabs)/explore.tsx +29 -27
  63. package/template/apps/mobile/app/(tabs)/index.tsx +25 -24
  64. package/template/apps/mobile/app/_layout.tsx +8 -8
  65. package/template/apps/mobile/app/modal.tsx +6 -6
  66. package/template/apps/mobile/components/external-link.tsx +5 -5
  67. package/template/apps/mobile/components/haptic-tab.tsx +4 -4
  68. package/template/apps/mobile/components/hello-wave.tsx +5 -4
  69. package/template/apps/mobile/components/parallax-scroll-view.tsx +15 -13
  70. package/template/apps/mobile/components/themed-text.tsx +14 -14
  71. package/template/apps/mobile/components/themed-view.tsx +3 -3
  72. package/template/apps/mobile/components/ui/collapsible.tsx +14 -13
  73. package/template/apps/mobile/components/ui/icon-symbol.ios.tsx +4 -4
  74. package/template/apps/mobile/components/ui/icon-symbol.tsx +9 -9
  75. package/template/apps/mobile/constants/theme.ts +19 -19
  76. package/template/apps/mobile/hooks/use-color-scheme.ts +1 -1
  77. package/template/apps/mobile/hooks/use-color-scheme.web.ts +3 -3
  78. package/template/apps/mobile/hooks/use-theme-color.ts +4 -4
  79. package/template/apps/mobile/package.json +7 -7
  80. package/template/apps/mobile/scripts/reset-project.js +2 -2
  81. package/template/apps/mobile/tsconfig.json +3 -13
  82. package/template/apps/platform/package.json +0 -1
  83. package/template/apps/platform/src/components/login-form.tsx +4 -1
  84. package/template/apps/platform/src/components/signup-form.tsx +15 -12
  85. package/template/apps/platform/src/routes/__root.tsx +7 -1
  86. package/template/apps/platform/src/routes/_public/examples/client-orpc.tsx +12 -3
  87. package/template/apps/platform/src/routes/_public/examples/ssr-orpc.tsx +2 -1
  88. package/template/package.json +7 -7
  89. package/template/packages/orpc/package.json +4 -4
  90. package/template/pnpm-workspace.yaml +5 -5
  91. package/template/apps/desktop/.editorconfig +0 -9
  92. package/template/apps/desktop/.eslintcache +0 -1
  93. package/template/apps/desktop/.vscode/launch.json +0 -39
  94. package/template/apps/desktop/.vscode/settings.json +0 -11
  95. package/template/apps/desktop/build/entitlements.mac.plist +0 -12
  96. package/template/apps/desktop/build/icon.icns +0 -0
  97. package/template/apps/desktop/build/icon.ico +0 -0
  98. package/template/apps/desktop/build/icon.png +0 -0
  99. package/template/apps/desktop/dev-app-update.yml +0 -3
  100. package/template/apps/desktop/electron-builder.yml +0 -45
  101. package/template/apps/desktop/electron.vite.config.ts +0 -26
  102. package/template/apps/desktop/resources/icon.png +0 -0
  103. package/template/apps/desktop/src/main/index.ts +0 -88
  104. package/template/apps/desktop/src/preload/index.d.ts +0 -11
  105. package/template/apps/desktop/src/preload/index.ts +0 -29
  106. package/template/apps/desktop/src/renderer/index.html +0 -17
  107. package/template/apps/desktop/src/renderer/src/assets/base.css +0 -67
  108. package/template/apps/desktop/src/renderer/src/assets/electron.svg +0 -10
  109. package/template/apps/desktop/src/renderer/src/assets/main.css +0 -171
  110. package/template/apps/desktop/src/renderer/src/assets/wavy-lines.svg +0 -25
  111. package/template/apps/desktop/src/renderer/src/components/Versions.tsx +0 -15
  112. package/template/apps/desktop/src/renderer/src/components/login-form.tsx +0 -57
  113. package/template/apps/desktop/src/renderer/src/env.d.ts +0 -6
  114. package/template/apps/desktop/src/renderer/src/lib/auth-client.ts +0 -5
  115. package/template/apps/desktop/src/renderer/src/lib/orpc.ts +0 -12
  116. package/template/apps/desktop/src/renderer/src/main.tsx +0 -22
  117. package/template/apps/desktop/src/renderer/src/routes/_auth/dashboard.tsx +0 -46
  118. package/template/apps/desktop/src/renderer/src/routes/_public/login.tsx +0 -16
  119. package/template/apps/desktop/tsconfig.web.json +0 -19
  120. package/template/apps/platform/src/lib/auth-client-electron.ts +0 -13
@@ -0,0 +1,19 @@
1
+ import { Label } from "@workspace/ui/components/label";
2
+ import { SidebarGroup, SidebarGroupContent, SidebarInput } from "@workspace/ui/components/sidebar";
3
+ import { SearchIcon } from "lucide-react";
4
+
5
+ export function SearchForm({ ...props }: React.ComponentProps<"form">) {
6
+ return (
7
+ <form {...props}>
8
+ <SidebarGroup className="py-0">
9
+ <SidebarGroupContent className="relative">
10
+ <Label htmlFor="search" className="sr-only">
11
+ Search
12
+ </Label>
13
+ <SidebarInput id="search" placeholder="Search the docs..." className="pl-8" />
14
+ <SearchIcon className="pointer-events-none absolute top-1/2 left-2 size-4 -translate-y-1/2 opacity-50 select-none" />
15
+ </SidebarGroupContent>
16
+ </SidebarGroup>
17
+ </form>
18
+ );
19
+ }
@@ -0,0 +1,206 @@
1
+ import { Button } from "@workspace/ui/components/button";
2
+ import {
3
+ Card,
4
+ CardContent,
5
+ CardDescription,
6
+ CardHeader,
7
+ CardTitle,
8
+ } from "@workspace/ui/components/card";
9
+ import {
10
+ Field,
11
+ FieldDescription,
12
+ FieldError,
13
+ FieldGroup,
14
+ FieldLabel,
15
+ } from "@workspace/ui/components/field";
16
+ import { Input } from "@workspace/ui/components/input";
17
+ import { useState } from "react";
18
+ import { Link } from "@tanstack/react-router";
19
+ import { useForm } from "@tanstack/react-form";
20
+ import { z } from "zod";
21
+ import { authClient } from "@/lib/auth-client";
22
+
23
+ const signupSchema = z
24
+ .object({
25
+ name: z.string().min(1, "Name is required"),
26
+ email: z.string().email("Invalid email address"),
27
+ password: z.string().min(8, "Password must be at least 8 characters"),
28
+ confirmPassword: z.string().min(1, "Confirm password is required"),
29
+ })
30
+ .refine((data) => data.password === data.confirmPassword, {
31
+ message: "Passwords do not match",
32
+ path: ["confirmPassword"],
33
+ });
34
+
35
+ export function SignupForm({ ...props }: React.ComponentProps<typeof Card>) {
36
+ const [loading, setLoading] = useState(false);
37
+ const [error, setError] = useState<string | null>(null);
38
+
39
+ const form = useForm({
40
+ defaultValues: {
41
+ name: "",
42
+ email: "",
43
+ password: "",
44
+ confirmPassword: "",
45
+ },
46
+ validators: {
47
+ onChange: signupSchema,
48
+ },
49
+ onSubmit: async ({ value }) => {
50
+ setLoading(true);
51
+ setError(null);
52
+ const { error: signUpError } = await authClient.signUp.email({
53
+ email: value.email,
54
+ password: value.password,
55
+ name: value.name,
56
+ callbackURL: "/dashboard",
57
+ });
58
+ if (signUpError) {
59
+ setError(signUpError.message || "Failed to sign up");
60
+ }
61
+ setLoading(false);
62
+ },
63
+ });
64
+
65
+ return (
66
+ <Card {...props}>
67
+ <CardHeader>
68
+ <CardTitle>Create an account</CardTitle>
69
+ <CardDescription>Enter your information below to create your account</CardDescription>
70
+ </CardHeader>
71
+ <CardContent>
72
+ <form
73
+ onSubmit={(e) => {
74
+ e.preventDefault();
75
+ e.stopPropagation();
76
+ form.handleSubmit();
77
+ }}
78
+ >
79
+ <FieldGroup>
80
+ {error && <div className="rounded bg-red-100 p-2 text-sm text-red-600">{error}</div>}
81
+ <form.Field
82
+ name="name"
83
+ children={(field) => {
84
+ const isInvalid = field.state.meta.isTouched && field.state.meta.errors.length > 0;
85
+ return (
86
+ <Field data-invalid={isInvalid}>
87
+ <FieldLabel htmlFor={field.name}>Full Name</FieldLabel>
88
+ <Input
89
+ id={field.name}
90
+ name={field.name}
91
+ type="text"
92
+ placeholder="John Doe"
93
+ value={field.state.value}
94
+ onBlur={field.handleBlur}
95
+ onChange={(e) => field.handleChange(e.target.value)}
96
+ required
97
+ />
98
+ {isInvalid && <FieldError errors={field.state.meta.errors} />}
99
+ </Field>
100
+ );
101
+ }}
102
+ />
103
+ <form.Field
104
+ name="email"
105
+ children={(field) => {
106
+ const isInvalid = field.state.meta.isTouched && field.state.meta.errors.length > 0;
107
+ return (
108
+ <Field data-invalid={isInvalid}>
109
+ <FieldLabel htmlFor={field.name}>Email</FieldLabel>
110
+ <Input
111
+ id={field.name}
112
+ name={field.name}
113
+ type="email"
114
+ placeholder="m@example.com"
115
+ value={field.state.value}
116
+ onBlur={field.handleBlur}
117
+ onChange={(e) => field.handleChange(e.target.value)}
118
+ required
119
+ />
120
+ <FieldDescription>
121
+ We&apos;ll use this to contact you. We will not share your email with anyone
122
+ else.
123
+ </FieldDescription>
124
+ {isInvalid && <FieldError errors={field.state.meta.errors} />}
125
+ </Field>
126
+ );
127
+ }}
128
+ />
129
+ <form.Field
130
+ name="password"
131
+ children={(field) => {
132
+ const isInvalid = field.state.meta.isTouched && field.state.meta.errors.length > 0;
133
+ return (
134
+ <Field data-invalid={isInvalid}>
135
+ <FieldLabel htmlFor={field.name}>Password</FieldLabel>
136
+ <Input
137
+ id={field.name}
138
+ name={field.name}
139
+ type="password"
140
+ value={field.state.value}
141
+ onBlur={field.handleBlur}
142
+ onChange={(e) => field.handleChange(e.target.value)}
143
+ required
144
+ />
145
+ <FieldDescription>Must be at least 8 characters long.</FieldDescription>
146
+ {isInvalid && <FieldError errors={field.state.meta.errors} />}
147
+ </Field>
148
+ );
149
+ }}
150
+ />
151
+ <form.Field
152
+ name="confirmPassword"
153
+ children={(field) => {
154
+ const isInvalid = field.state.meta.isTouched && field.state.meta.errors.length > 0;
155
+ return (
156
+ <Field data-invalid={isInvalid}>
157
+ <FieldLabel htmlFor={field.name}>Confirm Password</FieldLabel>
158
+ <Input
159
+ id={field.name}
160
+ name={field.name}
161
+ type="password"
162
+ value={field.state.value}
163
+ onBlur={field.handleBlur}
164
+ onChange={(e) => field.handleChange(e.target.value)}
165
+ required
166
+ />
167
+ <FieldDescription>Please confirm your password.</FieldDescription>
168
+ {isInvalid && <FieldError errors={field.state.meta.errors} />}
169
+ </Field>
170
+ );
171
+ }}
172
+ />
173
+ <FieldGroup>
174
+ <Field>
175
+ <form.Subscribe
176
+ selector={(state) => ({
177
+ canSubmit: state.canSubmit,
178
+ isSubmitting: state.isSubmitting,
179
+ })}
180
+ >
181
+ {(state: { canSubmit: boolean; isSubmitting: boolean }) => (
182
+ <Button
183
+ type="submit"
184
+ disabled={!state.canSubmit || state.isSubmitting || loading}
185
+ >
186
+ {state.isSubmitting || loading ? "Creating..." : "Create Account"}
187
+ </Button>
188
+ )}
189
+ </form.Subscribe>
190
+ <Button variant="outline" type="button" disabled={loading}>
191
+ Sign up with Google
192
+ </Button>
193
+ <FieldDescription className="px-6 text-center">
194
+ Already have an account?{" "}
195
+ <Link to="/login" className="underline">
196
+ Sign in
197
+ </Link>
198
+ </FieldDescription>
199
+ </Field>
200
+ </FieldGroup>
201
+ </FieldGroup>
202
+ </form>
203
+ </CardContent>
204
+ </Card>
205
+ );
206
+ }
@@ -0,0 +1,54 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+
5
+ import {
6
+ DropdownMenu,
7
+ DropdownMenuContent,
8
+ DropdownMenuItem,
9
+ DropdownMenuTrigger,
10
+ } from "@workspace/ui/components/dropdown-menu";
11
+ import { SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "@workspace/ui/components/sidebar";
12
+ import { CheckIcon, ChevronsUpDownIcon, GalleryVerticalEndIcon } from "lucide-react";
13
+
14
+ export function VersionSwitcher({
15
+ versions,
16
+ defaultVersion,
17
+ }: {
18
+ versions: Array<string>;
19
+ defaultVersion: string;
20
+ }) {
21
+ const [selectedVersion, setSelectedVersion] = React.useState(defaultVersion);
22
+ return (
23
+ <SidebarMenu>
24
+ <SidebarMenuItem>
25
+ <DropdownMenu>
26
+ <DropdownMenuTrigger
27
+ render={
28
+ <SidebarMenuButton
29
+ size="lg"
30
+ className="data-open:bg-sidebar-accent data-open:text-sidebar-accent-foreground"
31
+ />
32
+ }
33
+ >
34
+ <div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
35
+ <GalleryVerticalEndIcon className="size-4" />
36
+ </div>
37
+ <div className="flex flex-col gap-0.5 leading-none">
38
+ <span className="font-medium">Documentation</span>
39
+ <span className="">v{selectedVersion}</span>
40
+ </div>
41
+ <ChevronsUpDownIcon className="ml-auto" />
42
+ </DropdownMenuTrigger>
43
+ <DropdownMenuContent align="start">
44
+ {versions.map((version) => (
45
+ <DropdownMenuItem key={version} onSelect={() => setSelectedVersion(version)}>
46
+ v{version} {version === selectedVersion && <CheckIcon className="ml-auto" />}
47
+ </DropdownMenuItem>
48
+ ))}
49
+ </DropdownMenuContent>
50
+ </DropdownMenu>
51
+ </SidebarMenuItem>
52
+ </SidebarMenu>
53
+ );
54
+ }
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,5 @@
1
+ import { createAuthClient } from "better-auth/react";
2
+
3
+ export const authClient = createAuthClient({
4
+ baseURL: import.meta.env.VITE_API_URL || "https://platform.localhost",
5
+ });
@@ -0,0 +1,10 @@
1
+ import { createORPCClient } from "@orpc/client";
2
+ import { RPCLink } from "@orpc/client/fetch";
3
+ import type { AppRouter } from "@workspace/orpc/router";
4
+ import type { RouterClient } from "@orpc/server";
5
+
6
+ const link = new RPCLink({
7
+ url: `${import.meta.env.VITE_API_URL || "https://platform.localhost"}/api/rpc`,
8
+ });
9
+
10
+ export const orpc = createORPCClient(link) as RouterClient<AppRouter>;
@@ -0,0 +1,12 @@
1
+ import { StrictMode } from "react";
2
+ import { createRoot } from "react-dom/client";
3
+ import { RouterProvider } from "@tanstack/react-router";
4
+ import { getRouter } from "./router";
5
+
6
+ const router = getRouter();
7
+
8
+ createRoot(document.getElementById("root")!).render(
9
+ <StrictMode>
10
+ <RouterProvider router={router} />
11
+ </StrictMode>,
12
+ );
@@ -12,6 +12,7 @@ import { Route as rootRouteImport } from './routes/__root'
12
12
  import { Route as PublicRouteImport } from './routes/_public'
13
13
  import { Route as AuthRouteImport } from './routes/_auth'
14
14
  import { Route as PublicIndexRouteImport } from './routes/_public/index'
15
+ import { Route as PublicSignupRouteImport } from './routes/_public/signup'
15
16
  import { Route as PublicLoginRouteImport } from './routes/_public/login'
16
17
  import { Route as AuthDashboardRouteImport } from './routes/_auth/dashboard'
17
18
  import { Route as AuthAccountRouteImport } from './routes/_auth/account'
@@ -31,6 +32,11 @@ const PublicIndexRoute = PublicIndexRouteImport.update({
31
32
  path: '/',
32
33
  getParentRoute: () => PublicRoute,
33
34
  } as any)
35
+ const PublicSignupRoute = PublicSignupRouteImport.update({
36
+ id: '/signup',
37
+ path: '/signup',
38
+ getParentRoute: () => PublicRoute,
39
+ } as any)
34
40
  const PublicLoginRoute = PublicLoginRouteImport.update({
35
41
  id: '/login',
36
42
  path: '/login',
@@ -64,6 +70,7 @@ export interface FileRoutesByFullPath {
64
70
  '/account': typeof AuthAccountRoute
65
71
  '/dashboard': typeof AuthDashboardRoute
66
72
  '/login': typeof PublicLoginRoute
73
+ '/signup': typeof PublicSignupRoute
67
74
  '/examples/client-orpc-auth': typeof AuthExamplesClientOrpcAuthRoute
68
75
  '/examples/client-orpc': typeof PublicExamplesClientOrpcRoute
69
76
  }
@@ -72,6 +79,7 @@ export interface FileRoutesByTo {
72
79
  '/account': typeof AuthAccountRoute
73
80
  '/dashboard': typeof AuthDashboardRoute
74
81
  '/login': typeof PublicLoginRoute
82
+ '/signup': typeof PublicSignupRoute
75
83
  '/examples/client-orpc-auth': typeof AuthExamplesClientOrpcAuthRoute
76
84
  '/examples/client-orpc': typeof PublicExamplesClientOrpcRoute
77
85
  }
@@ -82,6 +90,7 @@ export interface FileRoutesById {
82
90
  '/_auth/account': typeof AuthAccountRoute
83
91
  '/_auth/dashboard': typeof AuthDashboardRoute
84
92
  '/_public/login': typeof PublicLoginRoute
93
+ '/_public/signup': typeof PublicSignupRoute
85
94
  '/_public/': typeof PublicIndexRoute
86
95
  '/_auth/examples/client-orpc-auth': typeof AuthExamplesClientOrpcAuthRoute
87
96
  '/_public/examples/client-orpc': typeof PublicExamplesClientOrpcRoute
@@ -93,6 +102,7 @@ export interface FileRouteTypes {
93
102
  | '/account'
94
103
  | '/dashboard'
95
104
  | '/login'
105
+ | '/signup'
96
106
  | '/examples/client-orpc-auth'
97
107
  | '/examples/client-orpc'
98
108
  fileRoutesByTo: FileRoutesByTo
@@ -101,6 +111,7 @@ export interface FileRouteTypes {
101
111
  | '/account'
102
112
  | '/dashboard'
103
113
  | '/login'
114
+ | '/signup'
104
115
  | '/examples/client-orpc-auth'
105
116
  | '/examples/client-orpc'
106
117
  id:
@@ -110,6 +121,7 @@ export interface FileRouteTypes {
110
121
  | '/_auth/account'
111
122
  | '/_auth/dashboard'
112
123
  | '/_public/login'
124
+ | '/_public/signup'
113
125
  | '/_public/'
114
126
  | '/_auth/examples/client-orpc-auth'
115
127
  | '/_public/examples/client-orpc'
@@ -143,6 +155,13 @@ declare module '@tanstack/react-router' {
143
155
  preLoaderRoute: typeof PublicIndexRouteImport
144
156
  parentRoute: typeof PublicRoute
145
157
  }
158
+ '/_public/signup': {
159
+ id: '/_public/signup'
160
+ path: '/signup'
161
+ fullPath: '/signup'
162
+ preLoaderRoute: typeof PublicSignupRouteImport
163
+ parentRoute: typeof PublicRoute
164
+ }
146
165
  '/_public/login': {
147
166
  id: '/_public/login'
148
167
  path: '/login'
@@ -197,12 +216,14 @@ const AuthRouteWithChildren = AuthRoute._addFileChildren(AuthRouteChildren)
197
216
 
198
217
  interface PublicRouteChildren {
199
218
  PublicLoginRoute: typeof PublicLoginRoute
219
+ PublicSignupRoute: typeof PublicSignupRoute
200
220
  PublicIndexRoute: typeof PublicIndexRoute
201
221
  PublicExamplesClientOrpcRoute: typeof PublicExamplesClientOrpcRoute
202
222
  }
203
223
 
204
224
  const PublicRouteChildren: PublicRouteChildren = {
205
225
  PublicLoginRoute: PublicLoginRoute,
226
+ PublicSignupRoute: PublicSignupRoute,
206
227
  PublicIndexRoute: PublicIndexRoute,
207
228
  PublicExamplesClientOrpcRoute: PublicExamplesClientOrpcRoute,
208
229
  }
@@ -0,0 +1,19 @@
1
+ import { createRouter as createTanStackRouter } from "@tanstack/react-router";
2
+ import { routeTree } from "./routeTree.gen";
3
+
4
+ export function getRouter() {
5
+ const router = createTanStackRouter({
6
+ routeTree,
7
+ scrollRestoration: true,
8
+ defaultPreload: "intent",
9
+ defaultPreloadStaleTime: 0,
10
+ });
11
+
12
+ return router;
13
+ }
14
+
15
+ declare module "@tanstack/react-router" {
16
+ interface Register {
17
+ router: ReturnType<typeof getRouter>;
18
+ }
19
+ }
@@ -3,10 +3,15 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
3
3
  import { Toaster } from "@workspace/ui/components/sonner";
4
4
  import { ThemeProvider } from "@workspace/ui/components/theme-provider";
5
5
  import { ORPCProvider } from "@workspace/orpc/react";
6
- import { orpc } from "@renderer/lib/orpc";
7
- import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
8
- import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyTitle } from "@workspace/ui/components/empty";
9
- import { Button, buttonVariants } from "@workspace/ui/components/button";
6
+ import { orpc } from "@/lib/orpc";
7
+ import {
8
+ Empty,
9
+ EmptyContent,
10
+ EmptyDescription,
11
+ EmptyHeader,
12
+ EmptyTitle,
13
+ } from "@workspace/ui/components/empty";
14
+ import { buttonVariants } from "@workspace/ui/components/button";
10
15
 
11
16
  import "@workspace/ui/globals.css";
12
17
 
@@ -40,7 +45,6 @@ function RootLayout() {
40
45
  <ORPCProvider client={orpc}>
41
46
  <Outlet />
42
47
  <Toaster />
43
- <TanStackRouterDevtools />
44
48
  </ORPCProvider>
45
49
  </QueryClientProvider>
46
50
  </ThemeProvider>
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import { createFileRoute } from "@tanstack/react-router";
2
+ import { createFileRoute, redirect } from "@tanstack/react-router";
3
3
  import { useForm } from "@tanstack/react-form";
4
4
  import { z } from "zod";
5
5
  import { toast } from "sonner";
@@ -19,7 +19,7 @@ import { Field, FieldError, FieldLabel } from "@workspace/ui/components/field";
19
19
  import { Avatar, AvatarFallback, AvatarImage } from "@workspace/ui/components/avatar";
20
20
  import { Separator } from "@workspace/ui/components/separator";
21
21
 
22
- import { authClient } from "@renderer/lib/auth-client";
22
+ import { authClient } from "@/lib/auth-client";
23
23
 
24
24
  const profileSchema = z.object({
25
25
  name: z.string().min(1, "Name is required"),
@@ -37,18 +37,29 @@ const passwordSchema = z
37
37
  });
38
38
 
39
39
  export const Route = createFileRoute("/_auth/account")({
40
+ beforeLoad: async () => {
41
+ const { data: session } = await authClient.getSession();
42
+ if (!session) {
43
+ throw redirect({
44
+ to: "/login",
45
+ search: {
46
+ redirect: "/account",
47
+ },
48
+ });
49
+ }
50
+ return { session };
51
+ },
40
52
  component: AccountPage,
41
53
  });
42
54
 
43
55
  function AccountPage() {
44
- const { data: sessionData, isPending } = authClient.useSession();
56
+ const { session } = Route.useRouteContext();
45
57
  const [loading, setLoading] = React.useState(false);
46
-
47
- const user = sessionData?.user;
58
+ const user = session.user;
48
59
 
49
60
  const profileForm = useForm({
50
61
  defaultValues: {
51
- name: user?.name || "",
62
+ name: user.name || "",
52
63
  },
53
64
  validators: {
54
65
  onChange: profileSchema,
@@ -94,9 +105,6 @@ function AccountPage() {
94
105
  },
95
106
  });
96
107
 
97
- if (isPending) return <div>Loading...</div>;
98
- if (!user) return null;
99
-
100
108
  return (
101
109
  <div className="container max-w-4xl py-10">
102
110
  <div className="flex flex-col gap-8">
@@ -251,7 +259,7 @@ function AccountPage() {
251
259
  if (error) {
252
260
  toast.error(error.message || "Failed to delete account");
253
261
  } else {
254
- window.location.reload();
262
+ window.location.href = "/";
255
263
  }
256
264
  }
257
265
  }}
@@ -0,0 +1,58 @@
1
+ import { createFileRoute, redirect } from "@tanstack/react-router";
2
+ import { authClient } from "@/lib/auth-client";
3
+ import { orpc } from "@/lib/orpc";
4
+
5
+ export const Route = createFileRoute("/_auth/dashboard")({
6
+ beforeLoad: async () => {
7
+ const { data: session } = await authClient.getSession();
8
+ if (!session) {
9
+ throw redirect({
10
+ to: "/",
11
+ });
12
+ }
13
+ return { session };
14
+ },
15
+ loader: async () => {
16
+ try {
17
+ const data = await orpc.getSecretData();
18
+ return { secretData: data.secret };
19
+ } catch (err: any) {
20
+ return { secretData: "Error: " + (err.message || "Unknown error") };
21
+ }
22
+ },
23
+ component: Dashboard,
24
+ });
25
+
26
+ function Dashboard() {
27
+ const { session } = Route.useRouteContext();
28
+ const { secretData } = Route.useLoaderData();
29
+
30
+ return (
31
+ <div className="flex min-h-svh p-6">
32
+ <div className="flex max-w-md min-w-0 flex-col gap-4 text-sm leading-loose">
33
+ <div>
34
+ <h1 className="text-2xl font-bold">Dashboard</h1>
35
+ <p>Welcome, {session.user.name}!</p>
36
+ <p>This is a protected page. Only authenticated users can see this.</p>
37
+
38
+ <div className="mt-6 rounded-lg border bg-gray-50 p-4">
39
+ <h2 className="font-semibold mb-2">Secure oRPC Data:</h2>
40
+ <p className="font-mono text-xs">{secretData}</p>
41
+ </div>
42
+
43
+ <div className="mt-4 flex gap-2">
44
+ <button
45
+ onClick={async () => {
46
+ await authClient.signOut();
47
+ window.location.href = "/";
48
+ }}
49
+ className="rounded bg-red-500 px-4 py-2 text-white hover:bg-red-600"
50
+ >
51
+ Sign Out
52
+ </button>
53
+ </div>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ );
58
+ }
@@ -1,14 +1,25 @@
1
- import { createFileRoute } from "@tanstack/react-router";
1
+ import { createFileRoute, redirect } from "@tanstack/react-router";
2
+ import { authClient } from "@/lib/auth-client";
2
3
  import { useSecretData } from "@workspace/orpc/react";
3
- import { authClient } from "@renderer/lib/auth-client";
4
4
 
5
5
  export const Route = createFileRoute("/_auth/examples/client-orpc-auth")({
6
+ beforeLoad: async () => {
7
+ const { data: session } = await authClient.getSession();
8
+ if (!session) {
9
+ throw redirect({
10
+ to: "/login",
11
+ search: {
12
+ redirect: "/examples/client-orpc-auth",
13
+ },
14
+ });
15
+ }
16
+ return { session };
17
+ },
6
18
  component: ClientORPCAuth,
7
19
  });
8
20
 
9
21
  function ClientORPCAuth() {
10
- const { data: sessionData } = authClient.useSession();
11
- const session = sessionData;
22
+ const { session } = Route.useRouteContext();
12
23
 
13
24
  const { data, isLoading } = useSecretData();
14
25
 
@@ -19,7 +30,7 @@ function ClientORPCAuth() {
19
30
 
20
31
  <div className="rounded-lg border p-4">
21
32
  <h2 className="font-semibold">User Session:</h2>
22
- <pre className="text-xs bg-muted p-2 rounded dark:bg-zinc-900 overflow-auto">{JSON.stringify(session, null, 2)}</pre>
33
+ <pre className="text-xs bg-muted p-2 rounded">{JSON.stringify(session, null, 2)}</pre>
23
34
  </div>
24
35
 
25
36
  <div className="rounded-lg border p-4">
@@ -27,7 +38,7 @@ function ClientORPCAuth() {
27
38
  {isLoading ? (
28
39
  <p>Loading...</p>
29
40
  ) : (
30
- <pre className="text-xs bg-muted p-2 rounded dark:bg-zinc-900 overflow-auto">{JSON.stringify(data, null, 2)}</pre>
41
+ <pre className="text-xs bg-muted p-2 rounded">{JSON.stringify(data, null, 2)}</pre>
31
42
  )}
32
43
  </div>
33
44
  </div>
@@ -1,20 +1,8 @@
1
- import { Outlet, createFileRoute, redirect } from "@tanstack/react-router";
1
+ import { Outlet, createFileRoute } from "@tanstack/react-router";
2
2
  import { SidebarProvider, SidebarTrigger } from "@workspace/ui/components/sidebar";
3
- import { AuthSidebar } from "@renderer/components/app-sidebar";
4
- import { authClient } from "@renderer/lib/auth-client";
3
+ import { AuthSidebar } from "@/components/app-sidebar";
5
4
 
6
5
  export const Route = createFileRoute("/_auth")({
7
- beforeLoad: async ({ location }) => {
8
- const session = await authClient.getSession();
9
- if (!session.data) {
10
- throw redirect({
11
- to: "/login",
12
- search: {
13
- redirect: location.href,
14
- },
15
- });
16
- }
17
- },
18
6
  component: AuthLayout,
19
7
  });
20
8