create-better-t-stack 3.9.0 → 3.11.0-pr749.7e7198c
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/README.md +2 -1
- package/bin/create-better-t-stack +94 -0
- package/package.json +51 -55
- package/scripts/postinstall.mjs +129 -0
- package/templates/auth/better-auth/convex/backend/convex/auth.config.ts.hbs +5 -7
- package/templates/auth/better-auth/convex/backend/convex/auth.ts.hbs +17 -17
- package/templates/auth/better-auth/convex/backend/convex/http.ts.hbs +4 -4
- package/templates/auth/better-auth/convex/web/react/next/src/app/api/auth/[...all]/route.ts.hbs +2 -2
- package/templates/auth/better-auth/convex/web/react/next/src/components/user-menu.tsx.hbs +10 -10
- package/templates/auth/better-auth/convex/web/react/next/src/lib/auth-server.ts.hbs +13 -5
- package/templates/auth/better-auth/convex/web/react/tanstack-router/src/components/user-menu.tsx.hbs +14 -12
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/user-menu.tsx.hbs +13 -16
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/lib/auth-server.ts.hbs +11 -5
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/routes/api/auth/$.ts.hbs +4 -4
- package/templates/auth/better-auth/fullstack/tanstack-start/src/routes/api/auth/$.ts.hbs +1 -1
- package/templates/auth/better-auth/web/react/next/src/components/user-menu.tsx.hbs +17 -15
- package/templates/auth/better-auth/web/react/react-router/src/components/user-menu.tsx.hbs +16 -15
- package/templates/auth/better-auth/web/react/tanstack-router/src/components/{user-menu.tsx → user-menu.tsx.hbs} +16 -15
- package/templates/auth/better-auth/web/react/tanstack-start/src/components/{user-menu.tsx → user-menu.tsx.hbs} +16 -15
- package/templates/backend/convex/packages/backend/convex/README.md +4 -4
- package/templates/backend/convex/packages/backend/convex/convex.config.ts.hbs +17 -0
- package/templates/backend/convex/packages/backend/convex/tsconfig.json.hbs +1 -1
- package/templates/examples/ai/convex/packages/backend/convex/agent.ts.hbs +9 -0
- package/templates/examples/ai/convex/packages/backend/convex/chat.ts.hbs +67 -0
- package/templates/examples/ai/native/bare/app/(drawer)/ai.tsx.hbs +301 -3
- package/templates/examples/ai/native/unistyles/app/(drawer)/ai.tsx.hbs +296 -10
- package/templates/examples/ai/native/uniwind/app/(drawer)/ai.tsx.hbs +180 -1
- package/templates/examples/ai/web/react/next/src/app/ai/page.tsx.hbs +172 -9
- package/templates/examples/ai/web/react/react-router/src/routes/ai.tsx.hbs +156 -6
- package/templates/examples/ai/web/react/tanstack-router/src/routes/ai.tsx.hbs +156 -4
- package/templates/examples/ai/web/react/tanstack-start/src/routes/ai.tsx.hbs +159 -6
- package/templates/frontend/react/next/package.json.hbs +8 -7
- package/templates/frontend/react/next/src/app/layout.tsx.hbs +28 -1
- package/templates/frontend/react/next/src/components/mode-toggle.tsx.hbs +4 -6
- package/templates/frontend/react/next/src/components/providers.tsx.hbs +14 -4
- package/templates/frontend/react/react-router/package.json.hbs +2 -1
- package/templates/frontend/react/{tanstack-router/src/components/mode-toggle.tsx → react-router/src/components/mode-toggle.tsx.hbs} +4 -6
- package/templates/frontend/react/tanstack-router/package.json.hbs +2 -1
- package/templates/frontend/react/{react-router/src/components/mode-toggle.tsx → tanstack-router/src/components/mode-toggle.tsx.hbs} +4 -6
- package/templates/frontend/react/tanstack-start/package.json.hbs +2 -1
- package/templates/frontend/react/tanstack-start/src/router.tsx.hbs +6 -0
- package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +13 -14
- package/templates/frontend/react/tanstack-start/vite.config.ts.hbs +5 -0
- package/templates/frontend/react/web-base/components.json +5 -2
- package/templates/frontend/react/web-base/src/components/ui/button.tsx.hbs +57 -0
- package/templates/frontend/react/web-base/src/components/ui/card.tsx.hbs +103 -0
- package/templates/frontend/react/web-base/src/components/ui/checkbox.tsx.hbs +26 -0
- package/templates/frontend/react/web-base/src/components/ui/dropdown-menu.tsx.hbs +262 -0
- package/templates/frontend/react/web-base/src/components/ui/input.tsx.hbs +20 -0
- package/templates/frontend/react/web-base/src/components/ui/label.tsx.hbs +20 -0
- package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx.hbs +13 -0
- package/templates/frontend/react/web-base/src/components/ui/sonner.tsx.hbs +44 -0
- package/templates/frontend/react/web-base/src/index.css.hbs +58 -64
- package/dist/cli.d.mts +0 -1
- package/dist/cli.mjs +0 -8
- package/dist/index.d.mts +0 -347
- package/dist/index.mjs +0 -4
- package/dist/src-DLvUK0Qf.mjs +0 -7069
- package/templates/auth/better-auth/convex/backend/convex/convex.config.ts.hbs +0 -7
- package/templates/examples/ai/web/react/base/src/components/response.tsx.hbs +0 -22
- package/templates/frontend/react/web-base/src/components/ui/button.tsx +0 -56
- package/templates/frontend/react/web-base/src/components/ui/card.tsx +0 -75
- package/templates/frontend/react/web-base/src/components/ui/checkbox.tsx +0 -27
- package/templates/frontend/react/web-base/src/components/ui/dropdown-menu.tsx +0 -228
- package/templates/frontend/react/web-base/src/components/ui/input.tsx +0 -21
- package/templates/frontend/react/web-base/src/components/ui/label.tsx +0 -19
- package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx +0 -13
- package/templates/frontend/react/web-base/src/components/ui/sonner.tsx +0 -25
- /package/templates/auth/better-auth/web/react/tanstack-router/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/react/tanstack-router/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/react/tanstack-router/src/routes/{login.tsx → login.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/react/tanstack-start/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/react/tanstack-start/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/react/tanstack-start/src/routes/{login.tsx → login.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/solid/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/solid/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/solid/src/routes/{login.tsx → login.tsx.hbs} +0 -0
- /package/templates/frontend/react/react-router/src/components/{theme-provider.tsx → theme-provider.tsx.hbs} +0 -0
- /package/templates/frontend/react/tanstack-router/src/components/{theme-provider.tsx → theme-provider.tsx.hbs} +0 -0
- /package/templates/frontend/react/web-base/src/lib/{utils.ts → utils.ts.hbs} +0 -0
package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/user-menu.tsx.hbs
CHANGED
|
@@ -1,49 +1,46 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DropdownMenu,
|
|
3
3
|
DropdownMenuContent,
|
|
4
|
+
DropdownMenuGroup,
|
|
4
5
|
DropdownMenuItem,
|
|
5
6
|
DropdownMenuLabel,
|
|
6
7
|
DropdownMenuSeparator,
|
|
7
8
|
DropdownMenuTrigger,
|
|
8
9
|
} from "@/components/ui/dropdown-menu";
|
|
9
10
|
import { authClient } from "@/lib/auth-client";
|
|
10
|
-
import { useNavigate } from "@tanstack/react-router";
|
|
11
|
-
import { Button } from "./ui/button";
|
|
12
11
|
import { useQuery } from "convex/react";
|
|
13
12
|
import { api } from "@{{projectName}}/backend/convex/_generated/api";
|
|
14
13
|
|
|
14
|
+
import { Button } from "./ui/button";
|
|
15
|
+
|
|
15
16
|
export default function UserMenu() {
|
|
16
|
-
const navigate = useNavigate();
|
|
17
17
|
const user = useQuery(api.auth.getCurrentUser)
|
|
18
18
|
|
|
19
19
|
return (
|
|
20
20
|
<DropdownMenu>
|
|
21
|
-
<DropdownMenuTrigger
|
|
22
|
-
|
|
21
|
+
<DropdownMenuTrigger render={<Button variant="outline" />}>
|
|
22
|
+
{user?.name}
|
|
23
23
|
</DropdownMenuTrigger>
|
|
24
24
|
<DropdownMenuContent className="bg-card">
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
<
|
|
25
|
+
<DropdownMenuGroup>
|
|
26
|
+
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
27
|
+
<DropdownMenuSeparator />
|
|
28
|
+
<DropdownMenuItem>{user?.email}</DropdownMenuItem>
|
|
29
|
+
<DropdownMenuItem
|
|
30
30
|
variant="destructive"
|
|
31
|
-
className="w-full"
|
|
32
31
|
onClick={() => {
|
|
33
32
|
authClient.signOut({
|
|
34
33
|
fetchOptions: {
|
|
35
34
|
onSuccess: () => {
|
|
36
|
-
|
|
37
|
-
to: "/dashboard",
|
|
38
|
-
});
|
|
35
|
+
location.reload();
|
|
39
36
|
},
|
|
40
37
|
},
|
|
41
38
|
});
|
|
42
39
|
}}
|
|
43
40
|
>
|
|
44
41
|
Sign Out
|
|
45
|
-
</
|
|
46
|
-
</
|
|
42
|
+
</DropdownMenuItem>
|
|
43
|
+
</DropdownMenuGroup>
|
|
47
44
|
</DropdownMenuContent>
|
|
48
45
|
</DropdownMenu>
|
|
49
46
|
);
|
package/templates/auth/better-auth/convex/web/react/tanstack-start/src/lib/auth-server.ts.hbs
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { setupFetchClient } from "@convex-dev/better-auth/react-start";
|
|
3
|
-
import { getCookie } from "@tanstack/react-start/server";
|
|
1
|
+
import { convexBetterAuthReactStart } from "@convex-dev/better-auth/react-start";
|
|
4
2
|
|
|
5
|
-
export const {
|
|
6
|
-
|
|
3
|
+
export const {
|
|
4
|
+
handler,
|
|
5
|
+
getToken,
|
|
6
|
+
fetchAuthQuery,
|
|
7
|
+
fetchAuthMutation,
|
|
8
|
+
fetchAuthAction,
|
|
9
|
+
} = convexBetterAuthReactStart({
|
|
10
|
+
convexUrl: process.env.VITE_CONVEX_URL!,
|
|
11
|
+
convexSiteUrl: process.env.VITE_CONVEX_SITE_URL!,
|
|
12
|
+
});
|
package/templates/auth/better-auth/convex/web/react/tanstack-start/src/routes/api/auth/$.ts.hbs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { reactStartHandler } from "@convex-dev/better-auth/react-start";
|
|
2
1
|
import { createFileRoute } from "@tanstack/react-router";
|
|
2
|
+
import { handler } from "@/lib/auth-server";
|
|
3
3
|
|
|
4
4
|
export const Route = createFileRoute("/api/auth/$")({
|
|
5
5
|
server: {
|
|
6
6
|
handlers: {
|
|
7
|
-
GET: ({ request }) =>
|
|
8
|
-
POST: ({ request }) =>
|
|
7
|
+
GET: ({ request }) => handler(request),
|
|
8
|
+
POST: ({ request }) => handler(request),
|
|
9
9
|
},
|
|
10
10
|
},
|
|
11
|
-
});
|
|
11
|
+
});
|
|
@@ -1,16 +1,19 @@
|
|
|
1
|
+
import Link from "next/link";
|
|
2
|
+
import { useRouter } from "next/navigation";
|
|
3
|
+
|
|
1
4
|
import {
|
|
2
5
|
DropdownMenu,
|
|
3
6
|
DropdownMenuContent,
|
|
7
|
+
DropdownMenuGroup,
|
|
4
8
|
DropdownMenuItem,
|
|
5
9
|
DropdownMenuLabel,
|
|
6
10
|
DropdownMenuSeparator,
|
|
7
11
|
DropdownMenuTrigger,
|
|
8
12
|
} from "@/components/ui/dropdown-menu";
|
|
9
13
|
import { authClient } from "@/lib/auth-client";
|
|
14
|
+
|
|
10
15
|
import { Button } from "./ui/button";
|
|
11
16
|
import { Skeleton } from "./ui/skeleton";
|
|
12
|
-
import { useRouter } from "next/navigation";
|
|
13
|
-
import Link from "next/link";
|
|
14
17
|
|
|
15
18
|
export default function UserMenu() {
|
|
16
19
|
const router = useRouter();
|
|
@@ -22,25 +25,24 @@ export default function UserMenu() {
|
|
|
22
25
|
|
|
23
26
|
if (!session) {
|
|
24
27
|
return (
|
|
25
|
-
<
|
|
26
|
-
<
|
|
27
|
-
</
|
|
28
|
+
<Link href="/login">
|
|
29
|
+
<Button variant="outline">Sign In</Button>
|
|
30
|
+
</Link>
|
|
28
31
|
);
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
return (
|
|
32
35
|
<DropdownMenu>
|
|
33
|
-
<DropdownMenuTrigger
|
|
34
|
-
|
|
36
|
+
<DropdownMenuTrigger render={<Button variant="outline" />}>
|
|
37
|
+
{session.user.name}
|
|
35
38
|
</DropdownMenuTrigger>
|
|
36
39
|
<DropdownMenuContent className="bg-card">
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
<
|
|
40
|
+
<DropdownMenuGroup>
|
|
41
|
+
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
42
|
+
<DropdownMenuSeparator />
|
|
43
|
+
<DropdownMenuItem>{session.user.email}</DropdownMenuItem>
|
|
44
|
+
<DropdownMenuItem
|
|
42
45
|
variant="destructive"
|
|
43
|
-
className="w-full"
|
|
44
46
|
onClick={() => {
|
|
45
47
|
authClient.signOut({
|
|
46
48
|
fetchOptions: {
|
|
@@ -52,8 +54,8 @@ export default function UserMenu() {
|
|
|
52
54
|
}}
|
|
53
55
|
>
|
|
54
56
|
Sign Out
|
|
55
|
-
</
|
|
56
|
-
</
|
|
57
|
+
</DropdownMenuItem>
|
|
58
|
+
</DropdownMenuGroup>
|
|
57
59
|
</DropdownMenuContent>
|
|
58
60
|
</DropdownMenu>
|
|
59
61
|
);
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
+
import { Link, useNavigate } from "react-router";
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
DropdownMenu,
|
|
3
5
|
DropdownMenuContent,
|
|
6
|
+
DropdownMenuGroup,
|
|
4
7
|
DropdownMenuItem,
|
|
5
8
|
DropdownMenuLabel,
|
|
6
9
|
DropdownMenuSeparator,
|
|
7
10
|
DropdownMenuTrigger,
|
|
8
11
|
} from "@/components/ui/dropdown-menu";
|
|
9
12
|
import { authClient } from "@/lib/auth-client";
|
|
10
|
-
|
|
13
|
+
|
|
11
14
|
import { Button } from "./ui/button";
|
|
12
15
|
import { Skeleton } from "./ui/skeleton";
|
|
13
|
-
import { Link } from "react-router";
|
|
14
16
|
|
|
15
17
|
export default function UserMenu() {
|
|
16
18
|
const navigate = useNavigate();
|
|
@@ -22,25 +24,24 @@ export default function UserMenu() {
|
|
|
22
24
|
|
|
23
25
|
if (!session) {
|
|
24
26
|
return (
|
|
25
|
-
<
|
|
26
|
-
<
|
|
27
|
-
</
|
|
27
|
+
<Link to="/login">
|
|
28
|
+
<Button variant="outline">Sign In</Button>
|
|
29
|
+
</Link>
|
|
28
30
|
);
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
return (
|
|
32
34
|
<DropdownMenu>
|
|
33
|
-
<DropdownMenuTrigger
|
|
34
|
-
|
|
35
|
+
<DropdownMenuTrigger render={<Button variant="outline" />}>
|
|
36
|
+
{session.user.name}
|
|
35
37
|
</DropdownMenuTrigger>
|
|
36
38
|
<DropdownMenuContent className="bg-card">
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
<
|
|
39
|
+
<DropdownMenuGroup>
|
|
40
|
+
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
41
|
+
<DropdownMenuSeparator />
|
|
42
|
+
<DropdownMenuItem>{session.user.email}</DropdownMenuItem>
|
|
43
|
+
<DropdownMenuItem
|
|
42
44
|
variant="destructive"
|
|
43
|
-
className="w-full"
|
|
44
45
|
onClick={() => {
|
|
45
46
|
authClient.signOut({
|
|
46
47
|
fetchOptions: {
|
|
@@ -52,8 +53,8 @@ export default function UserMenu() {
|
|
|
52
53
|
}}
|
|
53
54
|
>
|
|
54
55
|
Sign Out
|
|
55
|
-
</
|
|
56
|
-
</
|
|
56
|
+
</DropdownMenuItem>
|
|
57
|
+
</DropdownMenuGroup>
|
|
57
58
|
</DropdownMenuContent>
|
|
58
59
|
</DropdownMenu>
|
|
59
60
|
);
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
+
import { Link, useNavigate } from "@tanstack/react-router";
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
DropdownMenu,
|
|
3
5
|
DropdownMenuContent,
|
|
6
|
+
DropdownMenuGroup,
|
|
4
7
|
DropdownMenuItem,
|
|
5
8
|
DropdownMenuLabel,
|
|
6
9
|
DropdownMenuSeparator,
|
|
7
10
|
DropdownMenuTrigger,
|
|
8
11
|
} from "@/components/ui/dropdown-menu";
|
|
9
12
|
import { authClient } from "@/lib/auth-client";
|
|
10
|
-
|
|
13
|
+
|
|
11
14
|
import { Button } from "./ui/button";
|
|
12
15
|
import { Skeleton } from "./ui/skeleton";
|
|
13
|
-
import { Link } from "@tanstack/react-router";
|
|
14
16
|
|
|
15
17
|
export default function UserMenu() {
|
|
16
18
|
const navigate = useNavigate();
|
|
@@ -22,25 +24,24 @@ export default function UserMenu() {
|
|
|
22
24
|
|
|
23
25
|
if (!session) {
|
|
24
26
|
return (
|
|
25
|
-
<
|
|
26
|
-
<
|
|
27
|
-
</
|
|
27
|
+
<Link to="/login">
|
|
28
|
+
<Button variant="outline">Sign In</Button>
|
|
29
|
+
</Link>
|
|
28
30
|
);
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
return (
|
|
32
34
|
<DropdownMenu>
|
|
33
|
-
<DropdownMenuTrigger
|
|
34
|
-
|
|
35
|
+
<DropdownMenuTrigger render={<Button variant="outline" />}>
|
|
36
|
+
{session.user.name}
|
|
35
37
|
</DropdownMenuTrigger>
|
|
36
38
|
<DropdownMenuContent className="bg-card">
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
<
|
|
39
|
+
<DropdownMenuGroup>
|
|
40
|
+
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
41
|
+
<DropdownMenuSeparator />
|
|
42
|
+
<DropdownMenuItem>{session.user.email}</DropdownMenuItem>
|
|
43
|
+
<DropdownMenuItem
|
|
42
44
|
variant="destructive"
|
|
43
|
-
className="w-full"
|
|
44
45
|
onClick={() => {
|
|
45
46
|
authClient.signOut({
|
|
46
47
|
fetchOptions: {
|
|
@@ -54,8 +55,8 @@ export default function UserMenu() {
|
|
|
54
55
|
}}
|
|
55
56
|
>
|
|
56
57
|
Sign Out
|
|
57
|
-
</
|
|
58
|
-
</
|
|
58
|
+
</DropdownMenuItem>
|
|
59
|
+
</DropdownMenuGroup>
|
|
59
60
|
</DropdownMenuContent>
|
|
60
61
|
</DropdownMenu>
|
|
61
62
|
);
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
+
import { Link, useNavigate } from "@tanstack/react-router";
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
DropdownMenu,
|
|
3
5
|
DropdownMenuContent,
|
|
6
|
+
DropdownMenuGroup,
|
|
4
7
|
DropdownMenuItem,
|
|
5
8
|
DropdownMenuLabel,
|
|
6
9
|
DropdownMenuSeparator,
|
|
7
10
|
DropdownMenuTrigger,
|
|
8
11
|
} from "@/components/ui/dropdown-menu";
|
|
9
12
|
import { authClient } from "@/lib/auth-client";
|
|
10
|
-
|
|
13
|
+
|
|
11
14
|
import { Button } from "./ui/button";
|
|
12
15
|
import { Skeleton } from "./ui/skeleton";
|
|
13
|
-
import { Link } from "@tanstack/react-router";
|
|
14
16
|
|
|
15
17
|
export default function UserMenu() {
|
|
16
18
|
const navigate = useNavigate();
|
|
@@ -22,25 +24,24 @@ export default function UserMenu() {
|
|
|
22
24
|
|
|
23
25
|
if (!session) {
|
|
24
26
|
return (
|
|
25
|
-
<
|
|
26
|
-
<
|
|
27
|
-
</
|
|
27
|
+
<Link to="/login">
|
|
28
|
+
<Button variant="outline">Sign In</Button>
|
|
29
|
+
</Link>
|
|
28
30
|
);
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
return (
|
|
32
34
|
<DropdownMenu>
|
|
33
|
-
<DropdownMenuTrigger
|
|
34
|
-
|
|
35
|
+
<DropdownMenuTrigger render={<Button variant="outline" />}>
|
|
36
|
+
{session.user.name}
|
|
35
37
|
</DropdownMenuTrigger>
|
|
36
38
|
<DropdownMenuContent className="bg-card">
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
<
|
|
39
|
+
<DropdownMenuGroup>
|
|
40
|
+
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
41
|
+
<DropdownMenuSeparator />
|
|
42
|
+
<DropdownMenuItem>{session.user.email}</DropdownMenuItem>
|
|
43
|
+
<DropdownMenuItem
|
|
42
44
|
variant="destructive"
|
|
43
|
-
className="w-full"
|
|
44
45
|
onClick={() => {
|
|
45
46
|
authClient.signOut({
|
|
46
47
|
fetchOptions: {
|
|
@@ -54,8 +55,8 @@ export default function UserMenu() {
|
|
|
54
55
|
}}
|
|
55
56
|
>
|
|
56
57
|
Sign Out
|
|
57
|
-
</
|
|
58
|
-
</
|
|
58
|
+
</DropdownMenuItem>
|
|
59
|
+
</DropdownMenuGroup>
|
|
59
60
|
</DropdownMenuContent>
|
|
60
61
|
</DropdownMenu>
|
|
61
62
|
);
|
|
@@ -6,7 +6,7 @@ See https://docs.convex.dev/functions for more.
|
|
|
6
6
|
A query function that takes two arguments looks like:
|
|
7
7
|
|
|
8
8
|
```ts
|
|
9
|
-
//
|
|
9
|
+
// convex/myFunctions.ts
|
|
10
10
|
import { query } from "./_generated/server";
|
|
11
11
|
import { v } from "convex/values";
|
|
12
12
|
|
|
@@ -36,7 +36,7 @@ export const myQueryFunction = query({
|
|
|
36
36
|
Using this query function in a React component looks like:
|
|
37
37
|
|
|
38
38
|
```ts
|
|
39
|
-
const data = useQuery(api.
|
|
39
|
+
const data = useQuery(api.myFunctions.myQueryFunction, {
|
|
40
40
|
first: 10,
|
|
41
41
|
second: "hello",
|
|
42
42
|
});
|
|
@@ -45,7 +45,7 @@ const data = useQuery(api.functions.myQueryFunction, {
|
|
|
45
45
|
A mutation function looks like:
|
|
46
46
|
|
|
47
47
|
```ts
|
|
48
|
-
//
|
|
48
|
+
// convex/myFunctions.ts
|
|
49
49
|
import { mutation } from "./_generated/server";
|
|
50
50
|
import { v } from "convex/values";
|
|
51
51
|
|
|
@@ -73,7 +73,7 @@ export const myMutationFunction = mutation({
|
|
|
73
73
|
Using this mutation function in a React component looks like:
|
|
74
74
|
|
|
75
75
|
```ts
|
|
76
|
-
const mutation = useMutation(api.
|
|
76
|
+
const mutation = useMutation(api.myFunctions.myMutationFunction);
|
|
77
77
|
function handleButtonPress() {
|
|
78
78
|
// fire and forget, the most common way to use mutations
|
|
79
79
|
mutation({ first: "Hello!", second: "me" });
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineApp } from "convex/server";
|
|
2
|
+
{{#if (eq auth "better-auth")}}
|
|
3
|
+
import betterAuth from "@convex-dev/better-auth/convex.config";
|
|
4
|
+
{{/if}}
|
|
5
|
+
{{#if (includes examples "ai")}}
|
|
6
|
+
import agent from "@convex-dev/agent/convex.config";
|
|
7
|
+
{{/if}}
|
|
8
|
+
|
|
9
|
+
const app = defineApp();
|
|
10
|
+
{{#if (eq auth "better-auth")}}
|
|
11
|
+
app.use(betterAuth);
|
|
12
|
+
{{/if}}
|
|
13
|
+
{{#if (includes examples "ai")}}
|
|
14
|
+
app.use(agent);
|
|
15
|
+
{{/if}}
|
|
16
|
+
|
|
17
|
+
export default app;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
/* This TypeScript project config describes the environment that
|
|
3
3
|
* Convex functions run in and is used to typecheck them.
|
|
4
|
-
* You can modify it, but some settings required to use Convex.
|
|
4
|
+
* You can modify it, but some settings are required to use Convex.
|
|
5
5
|
*/
|
|
6
6
|
"compilerOptions": {
|
|
7
7
|
/* These settings are not required by Convex and can be modified. */
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Agent } from "@convex-dev/agent";
|
|
2
|
+
import { google } from "@ai-sdk/google";
|
|
3
|
+
import { components } from "./_generated/api";
|
|
4
|
+
|
|
5
|
+
export const chatAgent = new Agent(components.agent, {
|
|
6
|
+
name: "Chat Agent",
|
|
7
|
+
languageModel: google("gemini-2.5-flash"),
|
|
8
|
+
instructions: "You are a helpful AI assistant. Be concise and friendly in your responses.",
|
|
9
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createThread,
|
|
3
|
+
listUIMessages,
|
|
4
|
+
saveMessage,
|
|
5
|
+
syncStreams,
|
|
6
|
+
vStreamArgs,
|
|
7
|
+
} from "@convex-dev/agent";
|
|
8
|
+
import { paginationOptsValidator } from "convex/server";
|
|
9
|
+
import { v } from "convex/values";
|
|
10
|
+
|
|
11
|
+
import { components, internal } from "./_generated/api";
|
|
12
|
+
import { internalAction, mutation, query } from "./_generated/server";
|
|
13
|
+
import { chatAgent } from "./agent";
|
|
14
|
+
|
|
15
|
+
export const createNewThread = mutation({
|
|
16
|
+
args: {},
|
|
17
|
+
handler: async (ctx) => {
|
|
18
|
+
const threadId = await createThread(ctx, components.agent, {});
|
|
19
|
+
return threadId;
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export const listMessages = query({
|
|
24
|
+
args: {
|
|
25
|
+
threadId: v.string(),
|
|
26
|
+
paginationOpts: paginationOptsValidator,
|
|
27
|
+
streamArgs: vStreamArgs,
|
|
28
|
+
},
|
|
29
|
+
handler: async (ctx, args) => {
|
|
30
|
+
const paginated = await listUIMessages(ctx, components.agent, args);
|
|
31
|
+
const streams = await syncStreams(ctx, components.agent, args);
|
|
32
|
+
return { ...paginated, streams };
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export const sendMessage = mutation({
|
|
37
|
+
args: {
|
|
38
|
+
threadId: v.string(),
|
|
39
|
+
prompt: v.string(),
|
|
40
|
+
},
|
|
41
|
+
handler: async (ctx, { threadId, prompt }) => {
|
|
42
|
+
const { messageId } = await saveMessage(ctx, components.agent, {
|
|
43
|
+
threadId,
|
|
44
|
+
prompt,
|
|
45
|
+
});
|
|
46
|
+
await ctx.scheduler.runAfter(0, internal.chat.generateResponseAsync, {
|
|
47
|
+
threadId,
|
|
48
|
+
promptMessageId: messageId,
|
|
49
|
+
});
|
|
50
|
+
return messageId;
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export const generateResponseAsync = internalAction({
|
|
55
|
+
args: {
|
|
56
|
+
threadId: v.string(),
|
|
57
|
+
promptMessageId: v.string(),
|
|
58
|
+
},
|
|
59
|
+
handler: async (ctx, { threadId, promptMessageId }) => {
|
|
60
|
+
await chatAgent.streamText(
|
|
61
|
+
ctx,
|
|
62
|
+
{ threadId },
|
|
63
|
+
{ promptMessageId },
|
|
64
|
+
{ saveStreamDeltas: true },
|
|
65
|
+
);
|
|
66
|
+
},
|
|
67
|
+
});
|