create-croissant 0.1.56 → 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 (89) hide show
  1. package/dist/add.js +9 -9
  2. package/dist/index.js +8 -8
  3. package/package.json +1 -1
  4. package/template/README.md +10 -1
  5. package/template/apps/desktop/.vscode/extensions.json +3 -0
  6. package/template/apps/desktop/README.md +7 -0
  7. package/template/apps/desktop/index.html +14 -0
  8. package/template/apps/desktop/package.json +40 -0
  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/components/app-sidebar.tsx +186 -0
  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/routeTree.gen.ts +240 -0
  24. package/template/apps/desktop/src/router.tsx +19 -0
  25. package/template/apps/desktop/src/routes/__root.tsx +52 -0
  26. package/template/apps/desktop/src/routes/_auth/account.tsx +275 -0
  27. package/template/apps/desktop/src/routes/_auth/dashboard.tsx +58 -0
  28. package/template/apps/desktop/src/routes/_auth/examples/client-orpc-auth.tsx +46 -0
  29. package/template/apps/desktop/src/routes/_auth.tsx +23 -0
  30. package/template/apps/desktop/src/routes/_public/examples/client-orpc.tsx +330 -0
  31. package/template/apps/desktop/src/routes/_public/index.tsx +66 -0
  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/routes/_public.tsx +23 -0
  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 +17 -0
  59. package/template/apps/desktop/tsconfig.node.json +10 -0
  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/src/components/login-form.tsx +4 -1
  83. package/template/apps/platform/src/components/signup-form.tsx +15 -12
  84. package/template/apps/platform/src/routes/__root.tsx +7 -1
  85. package/template/apps/platform/src/routes/_public/examples/client-orpc.tsx +12 -3
  86. package/template/apps/platform/src/routes/_public/examples/ssr-orpc.tsx +2 -1
  87. package/template/package.json +7 -5
  88. package/template/packages/orpc/package.json +4 -4
  89. package/template/pnpm-workspace.yaml +3 -3
@@ -0,0 +1,160 @@
1
+ import { cn } from "@workspace/ui/lib/utils";
2
+ import { Button } from "@workspace/ui/components/button";
3
+ import {
4
+ Card,
5
+ CardContent,
6
+ CardDescription,
7
+ CardHeader,
8
+ CardTitle,
9
+ } from "@workspace/ui/components/card";
10
+ import {
11
+ Field,
12
+ FieldDescription,
13
+ FieldError,
14
+ FieldGroup,
15
+ FieldLabel,
16
+ } from "@workspace/ui/components/field";
17
+ import { Input } from "@workspace/ui/components/input";
18
+ import { useState } from "react";
19
+ import { Link, useSearch } from "@tanstack/react-router";
20
+ import { useForm } from "@tanstack/react-form";
21
+ import { z } from "zod";
22
+
23
+ import { authClient } from "@/lib/auth-client";
24
+
25
+ const loginSchema = z.object({
26
+ email: z.string().email("Invalid email address"),
27
+ password: z.string().min(8, "Password must be at least 8 characters"),
28
+ });
29
+
30
+ export function LoginForm({ className, ...props }: React.ComponentProps<"div">) {
31
+ const [loading, setLoading] = useState(false);
32
+ const [error, setError] = useState<string | null>(null);
33
+ const search = useSearch({ from: "/_public/login" }) as { redirect?: string };
34
+
35
+ const form = useForm({
36
+ defaultValues: {
37
+ email: "",
38
+ password: "",
39
+ },
40
+ validators: {
41
+ onChange: loginSchema,
42
+ },
43
+ onSubmit: async ({ value }) => {
44
+ setLoading(true);
45
+ setError(null);
46
+ const { error: signInError } = await authClient.signIn.email({
47
+ email: value.email,
48
+ password: value.password,
49
+ callbackURL: search.redirect || "/dashboard",
50
+ });
51
+ if (signInError) {
52
+ setError(signInError.message || "Failed to sign in");
53
+ }
54
+ setLoading(false);
55
+ },
56
+ });
57
+
58
+ return (
59
+ <div className={cn("flex flex-col gap-6", className)} {...props}>
60
+ <Card>
61
+ <CardHeader>
62
+ <CardTitle>Login to your account</CardTitle>
63
+ <CardDescription>Enter your email below to login to your account</CardDescription>
64
+ </CardHeader>
65
+ <CardContent>
66
+ <form
67
+ onSubmit={(e) => {
68
+ e.preventDefault();
69
+ e.stopPropagation();
70
+ form.handleSubmit();
71
+ }}
72
+ >
73
+ <FieldGroup>
74
+ {error && <div className="rounded bg-red-100 p-2 text-sm text-red-600">{error}</div>}
75
+ <form.Field
76
+ name="email"
77
+ children={(field) => {
78
+ const isInvalid =
79
+ field.state.meta.isTouched && field.state.meta.errors.length > 0;
80
+ return (
81
+ <Field data-invalid={isInvalid}>
82
+ <FieldLabel htmlFor={field.name}>Email</FieldLabel>
83
+ <Input
84
+ id={field.name}
85
+ name={field.name}
86
+ type="email"
87
+ placeholder="m@example.com"
88
+ value={field.state.value}
89
+ onBlur={field.handleBlur}
90
+ onChange={(e) => field.handleChange(e.target.value)}
91
+ required
92
+ />
93
+ {isInvalid && <FieldError errors={field.state.meta.errors} />}
94
+ </Field>
95
+ );
96
+ }}
97
+ />
98
+ <form.Field
99
+ name="password"
100
+ children={(field) => {
101
+ const isInvalid =
102
+ field.state.meta.isTouched && field.state.meta.errors.length > 0;
103
+ return (
104
+ <Field data-invalid={isInvalid}>
105
+ <div className="flex items-center">
106
+ <FieldLabel htmlFor={field.name}>Password</FieldLabel>
107
+ <a
108
+ href="#"
109
+ className="ml-auto inline-block text-sm underline-offset-4 hover:underline"
110
+ >
111
+ Forgot your password?
112
+ </a>
113
+ </div>
114
+ <Input
115
+ id={field.name}
116
+ name={field.name}
117
+ type="password"
118
+ value={field.state.value}
119
+ onBlur={field.handleBlur}
120
+ onChange={(e) => field.handleChange(e.target.value)}
121
+ required
122
+ />
123
+ {isInvalid && <FieldError errors={field.state.meta.errors} />}
124
+ </Field>
125
+ );
126
+ }}
127
+ />
128
+ <Field>
129
+ <form.Subscribe
130
+ selector={(state) => ({
131
+ canSubmit: state.canSubmit,
132
+ isSubmitting: state.isSubmitting,
133
+ })}
134
+ >
135
+ {(state: { canSubmit: boolean; isSubmitting: boolean }) => (
136
+ <Button
137
+ type="submit"
138
+ disabled={!state.canSubmit || state.isSubmitting || loading}
139
+ >
140
+ {state.isSubmitting || loading ? "Logging in..." : "Login"}
141
+ </Button>
142
+ )}
143
+ </form.Subscribe>
144
+ <Button variant="outline" type="button" disabled={loading}>
145
+ Login with Google
146
+ </Button>
147
+ <FieldDescription className="text-center">
148
+ Don&apos;t have an account?{" "}
149
+ <Link to="/signup" className="underline">
150
+ Sign up
151
+ </Link>
152
+ </FieldDescription>
153
+ </Field>
154
+ </FieldGroup>
155
+ </form>
156
+ </CardContent>
157
+ </Card>
158
+ </div>
159
+ );
160
+ }
@@ -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
+ );