create-croissant 0.1.52 → 0.1.54

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-croissant",
3
- "version": "0.1.52",
3
+ "version": "0.1.54",
4
4
  "description": "Scaffold a new project using the Croissant Stack",
5
5
  "repository": {
6
6
  "type": "git",
@@ -26,6 +26,7 @@
26
26
  "@workspace/auth": "workspace:*",
27
27
  "@workspace/orpc": "workspace:*",
28
28
  "@workspace/ui": "workspace:*",
29
+ "better-auth": "1.6.11",
29
30
  "electron-updater": "^6.3.9",
30
31
  "lucide-react": "^1.14.0",
31
32
  "react": "19.2.5",
@@ -44,6 +45,7 @@
44
45
  "@workspace/config-typescript": "workspace:*",
45
46
  "electron": "39.8.9",
46
47
  "electron-builder": "^26.0.12",
47
- "electron-vite": "^5.0.0"
48
+ "electron-vite": "^5.0.0",
49
+ "vite": "^8.0.12"
48
50
  }
49
51
  }
@@ -17,6 +17,20 @@ function createWindow(): void {
17
17
  },
18
18
  });
19
19
 
20
+ // Handle deep links
21
+ if (process.defaultApp) {
22
+ if (process.argv.length >= 2) {
23
+ app.setAsDefaultProtocolClient("desktop", process.execPath, [join(__dirname, "../../")]);
24
+ }
25
+ } else {
26
+ app.setAsDefaultProtocolClient("desktop");
27
+ }
28
+
29
+ app.on("open-url", (event, url) => {
30
+ event.preventDefault();
31
+ mainWindow.webContents.send("auth-callback", url);
32
+ });
33
+
20
34
  mainWindow.on("ready-to-show", () => {
21
35
  mainWindow.show();
22
36
  });
@@ -3,6 +3,9 @@ import type { ElectronAPI } from "@electron-toolkit/preload";
3
3
  declare global {
4
4
  interface Window {
5
5
  electron: ElectronAPI;
6
- api: unknown;
6
+ api: {
7
+ onAuthCallback: (callback: (url: string) => void) => () => void;
8
+ openExternal: (url: string) => Promise<void>;
9
+ };
7
10
  }
8
11
  }
@@ -1,8 +1,15 @@
1
- import { contextBridge } from "electron";
1
+ import { contextBridge, ipcRenderer, shell } from "electron";
2
2
  import { electronAPI } from "@electron-toolkit/preload";
3
3
 
4
4
  // Custom APIs for renderer
5
- const api = {};
5
+ const api = {
6
+ onAuthCallback: (callback: (url: string) => void) => {
7
+ const listener = (_: any, url: string) => callback(url);
8
+ ipcRenderer.on("auth-callback", listener);
9
+ return () => ipcRenderer.removeListener("auth-callback", listener);
10
+ },
11
+ openExternal: (url: string) => shell.openExternal(url),
12
+ };
6
13
 
7
14
  // Use `contextBridge` APIs to expose Electron APIs to
8
15
  // renderer only if context isolation is enabled, otherwise
@@ -15,7 +15,7 @@ import {
15
15
  FieldLabel,
16
16
  } from "@workspace/ui/components/field";
17
17
  import { Input } from "@workspace/ui/components/input";
18
- import { useState } from "react";
18
+ import { useEffect, useState } from "react";
19
19
  import { Link, useNavigate } from "@tanstack/react-router";
20
20
  import { useForm } from "@tanstack/react-form";
21
21
  import { z } from "zod";
@@ -32,6 +32,27 @@ export function LoginForm({ className, ...props }: React.ComponentProps<"div">)
32
32
  const [error, setError] = useState<string | null>(null);
33
33
  const navigate = useNavigate();
34
34
 
35
+ useEffect(() => {
36
+ const unsubscribe = window.api.onAuthCallback(async (url) => {
37
+ const token = new URL(url).searchParams.get("token");
38
+ if (token) {
39
+ setLoading(true);
40
+ // Better Auth typically handles this via the setSession/proxy
41
+ // In deep link flow, we might need to manually set the session
42
+ // or let the client handle the token.
43
+ // For now, we redirect to dashboard and let authClient.getSession() handle it
44
+ navigate({ to: "/dashboard" });
45
+ setLoading(false);
46
+ }
47
+ });
48
+ return () => unsubscribe();
49
+ }, [navigate]);
50
+
51
+ const handleBrowserLogin = () => {
52
+ const platformUrl = `${import.meta.env.VITE_API_URL.replace("/api/auth", "")}/login?redirect=desktop://auth-callback`;
53
+ window.api.openExternal(platformUrl);
54
+ };
55
+
35
56
  const form = useForm({
36
57
  defaultValues: {
37
58
  email: "",
@@ -139,6 +160,17 @@ export function LoginForm({ className, ...props }: React.ComponentProps<"div">)
139
160
  </Button>
140
161
  )}
141
162
  </form.Subscribe>
163
+ <div className="relative">
164
+ <div className="absolute inset-0 flex items-center">
165
+ <span className="w-full border-t" />
166
+ </div>
167
+ <div className="relative flex justify-center text-xs uppercase">
168
+ <span className="bg-background px-2 text-muted-foreground">Or continue with</span>
169
+ </div>
170
+ </div>
171
+ <Button variant="outline" type="button" onClick={handleBrowserLogin} disabled={loading}>
172
+ Login with Browser
173
+ </Button>
142
174
  <Button variant="outline" type="button" disabled={loading}>
143
175
  Login with Google
144
176
  </Button>
@@ -1,3 +1,5 @@
1
1
  import { createAuthClient } from "better-auth/react";
2
2
 
3
- export const authClient = createAuthClient();
3
+ export const authClient: ReturnType<typeof createAuthClient> = createAuthClient({
4
+ baseURL: import.meta.env.VITE_API_URL,
5
+ });
@@ -1,10 +1,12 @@
1
- import { Outlet, createRootRoute } from "@tanstack/react-router";
1
+ import { Link, Outlet, createRootRoute } from "@tanstack/react-router";
2
2
  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
6
  import { orpc } from "@renderer/lib/orpc";
7
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";
8
10
 
9
11
  import "@workspace/ui/globals.css";
10
12
 
@@ -12,6 +14,23 @@ const queryClient = new QueryClient();
12
14
 
13
15
  export const Route = createRootRoute({
14
16
  component: RootLayout,
17
+ notFoundComponent: () => {
18
+ return (
19
+ <Empty className="h-screen border-none">
20
+ <EmptyHeader>
21
+ <EmptyTitle>404 - Page Not Found</EmptyTitle>
22
+ <EmptyDescription>
23
+ The page you are looking for does not exist or has been moved.
24
+ </EmptyDescription>
25
+ </EmptyHeader>
26
+ <EmptyContent>
27
+ <Link to="/" className={buttonVariants({ variant: "outline" })}>
28
+ Go Home
29
+ </Link>
30
+ </EmptyContent>
31
+ </Empty>
32
+ );
33
+ },
15
34
  });
16
35
 
17
36
  function RootLayout() {
@@ -9,6 +9,7 @@
9
9
  ],
10
10
  "compilerOptions": {
11
11
  "composite": true,
12
+ "types": ["vite/client"],
12
13
  "paths": {
13
14
  "@renderer/*": ["./src/renderer/src/*"],
14
15
  "@main/*": ["./src/main/src/*"],
@@ -12,6 +12,7 @@
12
12
  "db:studio": "drizzle-kit studio"
13
13
  },
14
14
  "dependencies": {
15
+ "@better-auth/electron": "1.6.11",
15
16
  "@noble/ciphers": "^2.2.0",
16
17
  "@tailwindcss/vite": "^4.2.4",
17
18
  "@tanstack/react-router": "^1.168.24",
@@ -20,11 +21,12 @@
20
21
  "@workspace/auth": "workspace:*",
21
22
  "@workspace/orpc": "workspace:*",
22
23
  "@workspace/ui": "workspace:*",
24
+ "better-auth": "1.6.11",
23
25
  "lucide-react": "^1.11.0",
24
26
  "nitro": "latest",
25
- "sonner": "^2.0.7",
26
27
  "react": "19.2.5",
27
28
  "react-dom": "19.2.5",
29
+ "sonner": "^2.0.7",
28
30
  "tailwindcss": "^4.2.4",
29
31
  "vite-tsconfig-paths": "^6.1.1"
30
32
  },
@@ -16,7 +16,7 @@ import {
16
16
  } from "@workspace/ui/components/field";
17
17
  import { Input } from "@workspace/ui/components/input";
18
18
  import { useState } from "react";
19
- import { Link } from "@tanstack/react-router";
19
+ import { Link, useSearch } from "@tanstack/react-router";
20
20
  import { useForm } from "@tanstack/react-form";
21
21
  import { z } from "zod";
22
22
 
@@ -30,6 +30,7 @@ const loginSchema = z.object({
30
30
  export function LoginForm({ className, ...props }: React.ComponentProps<"div">) {
31
31
  const [loading, setLoading] = useState(false);
32
32
  const [error, setError] = useState<string | null>(null);
33
+ const search = useSearch({ from: "/_public/login" }) as { redirect?: string };
33
34
 
34
35
  const form = useForm({
35
36
  defaultValues: {
@@ -45,7 +46,7 @@ export function LoginForm({ className, ...props }: React.ComponentProps<"div">)
45
46
  const { error: signInError } = await authClient.signIn.email({
46
47
  email: value.email,
47
48
  password: value.password,
48
- callbackURL: "/dashboard",
49
+ callbackURL: search.redirect || "/dashboard",
49
50
  });
50
51
  if (signInError) {
51
52
  setError(signInError.message || "Failed to sign in");
@@ -0,0 +1,13 @@
1
+ import { createAuthClient } from "better-auth/client";
2
+ import { electronProxyClient } from "@better-auth/electron/proxy";
3
+
4
+ export const authClient = createAuthClient({
5
+ baseURL: import.meta.env.VITE_API_URL,
6
+ plugins: [
7
+ electronProxyClient({
8
+ protocol: {
9
+ scheme: "com.example.app"
10
+ },
11
+ }),
12
+ ],
13
+ });
@@ -1,3 +1,5 @@
1
1
  import { createAuthClient } from "better-auth/react";
2
2
 
3
- export const authClient = createAuthClient();
3
+ export const authClient = createAuthClient({
4
+ baseURL: typeof window !== "undefined" ? window.location.origin : "http://localhost:3000",
5
+ });
@@ -1,9 +1,11 @@
1
- import { HeadContent, Scripts, createRootRoute } from "@tanstack/react-router";
1
+ import { HeadContent, Link, Scripts, createRootRoute } from "@tanstack/react-router";
2
2
  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
6
  import { orpc } from "@/lib/orpc";
7
+ import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyTitle } from "@workspace/ui/components/empty";
8
+ import { Button, buttonVariants } from "@workspace/ui/components/button";
7
9
 
8
10
  import appCss from "@workspace/ui/globals.css?url";
9
11
 
@@ -31,6 +33,23 @@ export const Route = createRootRoute({
31
33
  ],
32
34
  }),
33
35
  shellComponent: RootDocument,
36
+ notFoundComponent: () => {
37
+ return (
38
+ <Empty className="h-screen border-none">
39
+ <EmptyHeader>
40
+ <EmptyTitle>404 - Page Not Found</EmptyTitle>
41
+ <EmptyDescription>
42
+ The page you are looking for does not exist or has been moved.
43
+ </EmptyDescription>
44
+ </EmptyHeader>
45
+ <EmptyContent>
46
+ <Link to="/" className={buttonVariants({ variant: "outline" })}>
47
+ Go Home
48
+ </Link>
49
+ </EmptyContent>
50
+ </Empty>
51
+ );
52
+ },
34
53
  });
35
54
 
36
55
  function RootDocument({ children }: { children: React.ReactNode }) {
@@ -25,11 +25,9 @@
25
25
  "@orpc/server": "latest",
26
26
  "@orpc/tanstack-query": "latest",
27
27
  "@tanstack/react-form": "latest",
28
- "@tanstack/react-query": "latest",
29
- "better-auth": "^1.6.11"
28
+ "@tanstack/react-query": "latest"
30
29
  },
31
30
  "devDependencies": {
32
- "@better-auth/core": "^1.6.11",
33
31
  "husky": "latest",
34
32
  "oxfmt": "latest",
35
33
  "oxlint": "latest",
@@ -12,7 +12,8 @@
12
12
  "generate": "better-auth generate --output ../db/src/lib/auth-schema.ts"
13
13
  },
14
14
  "dependencies": {
15
- "@workspace/db": "workspace:*"
15
+ "@workspace/db": "workspace:*",
16
+ "better-auth": "1.6.11"
16
17
  },
17
18
  "devDependencies": {
18
19
  "@better-auth/cli": "^1.4.22",