@chat-js/cli 0.4.0 → 0.6.1
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/dist/index.js +1548 -969
- package/package.json +4 -3
- package/templates/chat-app/app/(auth)/device-login/page.tsx +37 -0
- package/templates/chat-app/app/(auth)/login/page.tsx +26 -2
- package/templates/chat-app/app/(auth)/register/page.tsx +0 -12
- package/templates/chat-app/app/(chat)/api/chat/filter-reasoning-parts.ts +1 -1
- package/templates/chat-app/app/(chat)/api/chat/route.ts +13 -5
- package/templates/chat-app/app/(chat)/layout.tsx +4 -1
- package/templates/chat-app/app/api/trpc/[trpc]/route.ts +1 -0
- package/templates/chat-app/app/globals.css +9 -9
- package/templates/chat-app/app/layout.tsx +4 -2
- package/templates/chat-app/biome.jsonc +3 -3
- package/templates/chat-app/chat.config.ts +144 -141
- package/templates/chat-app/components/ai-elements/prompt-input.tsx +1 -1
- package/templates/chat-app/components/anonymous-session-init.tsx +10 -6
- package/templates/chat-app/components/artifact-actions.tsx +81 -18
- package/templates/chat-app/components/artifact-panel.tsx +142 -41
- package/templates/chat-app/components/attachment-list.tsx +1 -1
- package/templates/chat-app/components/{social-auth-providers.tsx → auth-providers.tsx} +49 -4
- package/templates/chat-app/components/chat/chat-welcome.tsx +3 -3
- package/templates/chat-app/components/chat-menu-items.tsx +1 -1
- package/templates/chat-app/components/chat-sync.tsx +3 -8
- package/templates/chat-app/components/console.tsx +9 -9
- package/templates/chat-app/components/context-usage.tsx +2 -2
- package/templates/chat-app/components/create-artifact.tsx +15 -5
- package/templates/chat-app/components/data-stream-handler.tsx +57 -16
- package/templates/chat-app/components/device-login-page.tsx +191 -0
- package/templates/chat-app/components/diffview.tsx +8 -2
- package/templates/chat-app/components/electron-auth-handler.tsx +184 -0
- package/templates/chat-app/components/electron-auth-ui.tsx +121 -0
- package/templates/chat-app/components/favicon-group.tsx +1 -1
- package/templates/chat-app/components/feedback-actions.tsx +1 -1
- package/templates/chat-app/components/greeting.tsx +1 -1
- package/templates/chat-app/components/interactive-chart-impl.tsx +3 -4
- package/templates/chat-app/components/interactive-charts.tsx +1 -1
- package/templates/chat-app/components/login-form.tsx +52 -10
- package/templates/chat-app/components/message-editor.tsx +4 -5
- package/templates/chat-app/components/model-selector.tsx +661 -655
- package/templates/chat-app/components/multimodal-input.tsx +13 -10
- package/templates/chat-app/components/parallel-response-cards.tsx +53 -35
- package/templates/chat-app/components/part/code-execution.tsx +8 -2
- package/templates/chat-app/components/part/document-common.tsx +1 -1
- package/templates/chat-app/components/part/document-preview.tsx +5 -5
- package/templates/chat-app/components/part/retrieve-url.tsx +12 -12
- package/templates/chat-app/components/part/text-message-part.tsx +13 -9
- package/templates/chat-app/components/project-chat-item.tsx +1 -1
- package/templates/chat-app/components/project-menu-items.tsx +1 -1
- package/templates/chat-app/components/research-task.tsx +1 -1
- package/templates/chat-app/components/research-tasks.tsx +1 -1
- package/templates/chat-app/components/retry-button.tsx +1 -1
- package/templates/chat-app/components/sandbox.tsx +1 -1
- package/templates/chat-app/components/sheet-editor.tsx +7 -7
- package/templates/chat-app/components/sidebar-chats-list.tsx +1 -1
- package/templates/chat-app/components/sidebar-toggle.tsx +15 -2
- package/templates/chat-app/components/sidebar-top-row.tsx +27 -12
- package/templates/chat-app/components/sidebar-user-nav.tsx +10 -1
- package/templates/chat-app/components/signup-form.tsx +49 -10
- package/templates/chat-app/components/sources.tsx +4 -4
- package/templates/chat-app/components/text-editor.tsx +5 -2
- package/templates/chat-app/components/toolbar.tsx +3 -3
- package/templates/chat-app/components/ui/sidebar.tsx +0 -1
- package/templates/chat-app/components/upgrade-cta/limit-display.tsx +1 -1
- package/templates/chat-app/components/user-message.tsx +135 -134
- package/templates/chat-app/electron.d.ts +41 -0
- package/templates/chat-app/evals/my-eval.eval.ts +3 -1
- package/templates/chat-app/hooks/use-artifact.tsx +13 -13
- package/templates/chat-app/lib/ai/gateways/provider-types.ts +19 -10
- package/templates/chat-app/lib/ai/stream-errors.test.ts +72 -0
- package/templates/chat-app/lib/ai/stream-errors.ts +94 -0
- package/templates/chat-app/lib/ai/tools/code-execution.javascript.ts +171 -0
- package/templates/chat-app/lib/ai/tools/code-execution.python.ts +336 -0
- package/templates/chat-app/lib/ai/tools/code-execution.shared.test.ts +71 -0
- package/templates/chat-app/lib/ai/tools/code-execution.shared.ts +59 -0
- package/templates/chat-app/lib/ai/tools/code-execution.ts +62 -391
- package/templates/chat-app/lib/ai/tools/code-execution.types.ts +24 -0
- package/templates/chat-app/lib/ai/tools/steps/multi-query-web-search.ts +3 -2
- package/templates/chat-app/lib/anonymous-session-client.ts +0 -3
- package/templates/chat-app/lib/artifacts/code/client.tsx +35 -5
- package/templates/chat-app/lib/artifacts/sheet/client.tsx +11 -3
- package/templates/chat-app/lib/auth-client.ts +23 -1
- package/templates/chat-app/lib/auth.ts +18 -1
- package/templates/chat-app/lib/blob.ts +1 -1
- package/templates/chat-app/lib/clone-messages.ts +1 -1
- package/templates/chat-app/lib/config-schema.ts +13 -1
- package/templates/chat-app/lib/constants.ts +3 -4
- package/templates/chat-app/lib/db/migrations/meta/0044_snapshot.json +42 -129
- package/templates/chat-app/lib/db/migrations/meta/_journal.json +1 -1
- package/templates/chat-app/lib/editor/config.ts +4 -4
- package/templates/chat-app/lib/electron-auth.ts +96 -0
- package/templates/chat-app/lib/env-schema.ts +33 -4
- package/templates/chat-app/lib/message-conversion.ts +1 -1
- package/templates/chat-app/lib/playwright-test-environment.ts +18 -0
- package/templates/chat-app/lib/social-auth.ts +5 -0
- package/templates/chat-app/lib/stores/hooks-threads.ts +2 -1
- package/templates/chat-app/lib/stores/with-threads.test.ts +1 -1
- package/templates/chat-app/lib/stores/with-threads.ts +5 -6
- package/templates/chat-app/lib/stores/with-tracing.ts +1 -1
- package/templates/chat-app/lib/thread-utils.ts +19 -21
- package/templates/chat-app/lib/utils/download-assets.ts +6 -7
- package/templates/chat-app/lib/utils/rate-limit.ts +9 -3
- package/templates/chat-app/package.json +22 -19
- package/templates/chat-app/playwright.config.ts +0 -19
- package/templates/chat-app/providers/chat-input-provider.tsx +1 -1
- package/templates/chat-app/proxy.ts +28 -3
- package/templates/chat-app/scripts/check-env.ts +10 -0
- package/templates/chat-app/trpc/server.tsx +7 -2
- package/templates/chat-app/tsconfig.json +2 -1
- package/templates/chat-app/vercel.json +0 -10
- package/templates/electron/CHANGELOG.md +7 -0
- package/templates/electron/README.md +54 -0
- package/templates/electron/entitlements.mac.plist +10 -0
- package/templates/electron/forge.config.ts +152 -0
- package/templates/electron/icon.png +0 -0
- package/templates/electron/package.json +53 -0
- package/templates/electron/scripts/generate-icons.test.js +37 -0
- package/templates/electron/scripts/generate-icons.ts +29 -0
- package/templates/electron/scripts/run-forge.cjs +28 -0
- package/templates/electron/scripts/write-branding.ts +18 -0
- package/templates/electron/src/config.ts +16 -0
- package/templates/electron/src/lib/auth-client.ts +64 -0
- package/templates/electron/src/main.ts +670 -0
- package/templates/electron/src/preload.d.ts +27 -0
- package/templates/electron/src/preload.ts +25 -0
- package/templates/electron/tsconfig.json +18 -0
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
import { type Dispatch, memo, type SetStateAction, useState } from "react";
|
|
2
2
|
import { toast } from "sonner";
|
|
3
|
+
import type {
|
|
4
|
+
Artifact,
|
|
5
|
+
ArtifactActionContext,
|
|
6
|
+
ArtifactMetadata,
|
|
7
|
+
} from "@/components/create-artifact";
|
|
8
|
+
import {
|
|
9
|
+
codeArtifact,
|
|
10
|
+
getCodeArtifactMetadata,
|
|
11
|
+
} from "@/lib/artifacts/code/client";
|
|
12
|
+
import {
|
|
13
|
+
getSheetArtifactMetadata,
|
|
14
|
+
sheetArtifact,
|
|
15
|
+
} from "@/lib/artifacts/sheet/client";
|
|
16
|
+
import { textArtifact } from "@/lib/artifacts/text/client";
|
|
3
17
|
import { cn } from "@/lib/utils";
|
|
4
|
-
import {
|
|
5
|
-
import type { ArtifactActionContext } from "./create-artifact";
|
|
18
|
+
import type { UIArtifact } from "./artifact-panel";
|
|
6
19
|
import { Button } from "./ui/button";
|
|
7
20
|
import { Toggle } from "./ui/toggle";
|
|
8
21
|
import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
|
|
@@ -13,13 +26,33 @@ interface ArtifactActionsProps {
|
|
|
13
26
|
handleVersionChange: (type: "next" | "prev" | "toggle" | "latest") => void;
|
|
14
27
|
isCurrentVersion: boolean;
|
|
15
28
|
isReadonly: boolean;
|
|
16
|
-
metadata:
|
|
29
|
+
metadata: ArtifactMetadata;
|
|
17
30
|
mode: "edit" | "diff";
|
|
18
|
-
setMetadata: Dispatch<SetStateAction<
|
|
31
|
+
setMetadata: Dispatch<SetStateAction<ArtifactMetadata>>;
|
|
19
32
|
}
|
|
20
33
|
|
|
21
|
-
function
|
|
34
|
+
function createTypedMetadataSetter<M extends ArtifactMetadata>(
|
|
35
|
+
setMetadata: Dispatch<SetStateAction<ArtifactMetadata>>,
|
|
36
|
+
coerce: (metadata: ArtifactMetadata) => M
|
|
37
|
+
): Dispatch<SetStateAction<M>> {
|
|
38
|
+
return (value) => {
|
|
39
|
+
setMetadata((current) => {
|
|
40
|
+
const typedCurrent = coerce(current);
|
|
41
|
+
return typeof value === "function" ? value(typedCurrent) : value;
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface TypedArtifactActionsProps<M extends ArtifactMetadata>
|
|
47
|
+
extends Omit<ArtifactActionsProps, "metadata" | "setMetadata"> {
|
|
48
|
+
artifactDefinition: Artifact<string, M>;
|
|
49
|
+
metadata: M;
|
|
50
|
+
setMetadata: Dispatch<SetStateAction<M>>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function TypedArtifactActions<M extends ArtifactMetadata>({
|
|
22
54
|
artifact,
|
|
55
|
+
artifactDefinition,
|
|
23
56
|
handleVersionChange,
|
|
24
57
|
currentVersionIndex,
|
|
25
58
|
isCurrentVersion,
|
|
@@ -27,18 +60,10 @@ function PureArtifactActions({
|
|
|
27
60
|
metadata,
|
|
28
61
|
setMetadata,
|
|
29
62
|
isReadonly,
|
|
30
|
-
}:
|
|
63
|
+
}: TypedArtifactActionsProps<M>) {
|
|
31
64
|
const [isLoading, setIsLoading] = useState(false);
|
|
32
65
|
|
|
33
|
-
const
|
|
34
|
-
(definition) => definition.kind === artifact.kind
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
if (!artifactDefinition) {
|
|
38
|
-
throw new Error("Artifact definition not found!");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const actionContext: ArtifactActionContext = {
|
|
66
|
+
const actionContext: ArtifactActionContext<M> = {
|
|
42
67
|
content: artifact.content,
|
|
43
68
|
handleVersionChange,
|
|
44
69
|
currentVersionIndex,
|
|
@@ -50,7 +75,7 @@ function PureArtifactActions({
|
|
|
50
75
|
};
|
|
51
76
|
|
|
52
77
|
function isActionDisabled(action: {
|
|
53
|
-
isDisabled?: (context: ArtifactActionContext) => boolean;
|
|
78
|
+
isDisabled?: (context: ArtifactActionContext<M>) => boolean;
|
|
54
79
|
}): boolean {
|
|
55
80
|
if (isLoading || artifact.status === "streaming") {
|
|
56
81
|
return true;
|
|
@@ -106,7 +131,7 @@ function PureArtifactActions({
|
|
|
106
131
|
</div>
|
|
107
132
|
) : (
|
|
108
133
|
<Button
|
|
109
|
-
className={cn("h-fit
|
|
134
|
+
className={cn("h-fit hover:bg-accent", {
|
|
110
135
|
"p-2": !action.label,
|
|
111
136
|
"px-2 py-1.5": action.label,
|
|
112
137
|
})}
|
|
@@ -137,7 +162,45 @@ function PureArtifactActions({
|
|
|
137
162
|
}
|
|
138
163
|
|
|
139
164
|
export const ArtifactActions = memo(
|
|
140
|
-
|
|
165
|
+
function ArtifactActions(props: ArtifactActionsProps) {
|
|
166
|
+
switch (props.artifact.kind) {
|
|
167
|
+
case "code":
|
|
168
|
+
return (
|
|
169
|
+
<TypedArtifactActions
|
|
170
|
+
{...props}
|
|
171
|
+
artifactDefinition={codeArtifact}
|
|
172
|
+
metadata={getCodeArtifactMetadata(props.metadata)}
|
|
173
|
+
setMetadata={createTypedMetadataSetter(
|
|
174
|
+
props.setMetadata,
|
|
175
|
+
getCodeArtifactMetadata
|
|
176
|
+
)}
|
|
177
|
+
/>
|
|
178
|
+
);
|
|
179
|
+
case "sheet":
|
|
180
|
+
return (
|
|
181
|
+
<TypedArtifactActions
|
|
182
|
+
{...props}
|
|
183
|
+
artifactDefinition={sheetArtifact}
|
|
184
|
+
metadata={getSheetArtifactMetadata(props.metadata)}
|
|
185
|
+
setMetadata={createTypedMetadataSetter(
|
|
186
|
+
props.setMetadata,
|
|
187
|
+
getSheetArtifactMetadata
|
|
188
|
+
)}
|
|
189
|
+
/>
|
|
190
|
+
);
|
|
191
|
+
case "text":
|
|
192
|
+
return (
|
|
193
|
+
<TypedArtifactActions
|
|
194
|
+
{...props}
|
|
195
|
+
artifactDefinition={textArtifact}
|
|
196
|
+
metadata={props.metadata}
|
|
197
|
+
setMetadata={props.setMetadata}
|
|
198
|
+
/>
|
|
199
|
+
);
|
|
200
|
+
default:
|
|
201
|
+
throw new Error("Artifact definition not found!");
|
|
202
|
+
}
|
|
203
|
+
},
|
|
141
204
|
(prevProps, nextProps) => {
|
|
142
205
|
if (prevProps.artifact.status !== nextProps.artifact.status) {
|
|
143
206
|
return false;
|
|
@@ -5,14 +5,21 @@ import {
|
|
|
5
5
|
} from "@ai-sdk-tools/store";
|
|
6
6
|
import { useQueryClient } from "@tanstack/react-query";
|
|
7
7
|
import { formatDistance } from "date-fns";
|
|
8
|
+
import type { Dispatch, SetStateAction } from "react";
|
|
8
9
|
import { memo, useCallback, useEffect, useRef, useState } from "react";
|
|
9
10
|
import { useDebounceCallback } from "usehooks-ts";
|
|
10
11
|
import { useDocuments, useSaveDocument } from "@/hooks/chat-sync-hooks";
|
|
11
12
|
import { useArtifact } from "@/hooks/use-artifact";
|
|
12
13
|
import type { ChatMessage } from "@/lib/ai/types";
|
|
13
14
|
import type { ArtifactKind } from "@/lib/artifacts/artifact-kind";
|
|
14
|
-
import {
|
|
15
|
-
|
|
15
|
+
import {
|
|
16
|
+
codeArtifact,
|
|
17
|
+
getCodeArtifactMetadata,
|
|
18
|
+
} from "@/lib/artifacts/code/client";
|
|
19
|
+
import {
|
|
20
|
+
getSheetArtifactMetadata,
|
|
21
|
+
sheetArtifact,
|
|
22
|
+
} from "@/lib/artifacts/sheet/client";
|
|
16
23
|
import { textArtifact } from "@/lib/artifacts/text/client";
|
|
17
24
|
import type { Document } from "@/lib/db/schema";
|
|
18
25
|
import { cn } from "@/lib/utils";
|
|
@@ -27,6 +34,7 @@ import {
|
|
|
27
34
|
ArtifactTitle,
|
|
28
35
|
} from "./ai-elements/artifact";
|
|
29
36
|
import { ArtifactActions as ArtifactPanelActions } from "./artifact-actions";
|
|
37
|
+
import type { ArtifactMetadata } from "./create-artifact";
|
|
30
38
|
//
|
|
31
39
|
import { Toolbar } from "./toolbar";
|
|
32
40
|
import { ScrollArea } from "./ui/scroll-area";
|
|
@@ -45,6 +53,18 @@ export interface UIArtifact {
|
|
|
45
53
|
title: string;
|
|
46
54
|
}
|
|
47
55
|
|
|
56
|
+
function createTypedMetadataSetter<M extends ArtifactMetadata>(
|
|
57
|
+
setMetadata: Dispatch<SetStateAction<ArtifactMetadata>>,
|
|
58
|
+
coerce: (metadata: ArtifactMetadata) => M
|
|
59
|
+
): Dispatch<SetStateAction<M>> {
|
|
60
|
+
return (value) => {
|
|
61
|
+
setMetadata((current) => {
|
|
62
|
+
const typedCurrent = coerce(current);
|
|
63
|
+
return typeof value === "function" ? value(typedCurrent) : value;
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
48
68
|
function PureArtifactPanel({
|
|
49
69
|
isReadonly,
|
|
50
70
|
isAuthenticated,
|
|
@@ -77,15 +97,7 @@ function PureArtifactPanel({
|
|
|
77
97
|
(doc) => doc.messageId === artifact.messageId
|
|
78
98
|
);
|
|
79
99
|
|
|
80
|
-
if (mostRecentDocumentIndex
|
|
81
|
-
const mostRecentDocument = documents[mostRecentDocumentIndex];
|
|
82
|
-
setDocument(mostRecentDocument);
|
|
83
|
-
setCurrentVersionIndex(mostRecentDocumentIndex);
|
|
84
|
-
setArtifact((currentArtifact) => ({
|
|
85
|
-
...currentArtifact,
|
|
86
|
-
content: mostRecentDocument.content ?? "",
|
|
87
|
-
}));
|
|
88
|
-
} else {
|
|
100
|
+
if (mostRecentDocumentIndex === -1) {
|
|
89
101
|
// Fallback to the most recent document
|
|
90
102
|
const latestDocument = documents.at(-1);
|
|
91
103
|
if (latestDocument) {
|
|
@@ -96,6 +108,14 @@ function PureArtifactPanel({
|
|
|
96
108
|
content: latestDocument.content ?? "",
|
|
97
109
|
}));
|
|
98
110
|
}
|
|
111
|
+
} else {
|
|
112
|
+
const mostRecentDocument = documents[mostRecentDocumentIndex];
|
|
113
|
+
setDocument(mostRecentDocument);
|
|
114
|
+
setCurrentVersionIndex(mostRecentDocumentIndex);
|
|
115
|
+
setArtifact((currentArtifact) => ({
|
|
116
|
+
...currentArtifact,
|
|
117
|
+
content: mostRecentDocument.content ?? "",
|
|
118
|
+
}));
|
|
99
119
|
}
|
|
100
120
|
}
|
|
101
121
|
}, [documents, setArtifact, artifact.messageId]);
|
|
@@ -112,17 +132,6 @@ function PureArtifactPanel({
|
|
|
112
132
|
}
|
|
113
133
|
);
|
|
114
134
|
|
|
115
|
-
const artifactDefinition = artifactDefinitions.find(
|
|
116
|
-
(definition) => definition.kind === artifact.kind
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
if (!artifactDefinition) {
|
|
120
|
-
throw new Error("Artifact definition not found!");
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const ArtifactContentComponent = artifactDefinition.content;
|
|
124
|
-
const ArtifactFooterComponent = artifactDefinition.footer;
|
|
125
|
-
|
|
126
135
|
const handleContentChange = useCallback(
|
|
127
136
|
(updatedContent: string) => {
|
|
128
137
|
if (!documents) {
|
|
@@ -225,22 +234,48 @@ function PureArtifactPanel({
|
|
|
225
234
|
: true;
|
|
226
235
|
|
|
227
236
|
useEffect(() => {
|
|
228
|
-
if (
|
|
229
|
-
artifact.
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
237
|
+
if (artifact.documentId !== "init" && artifact.status !== "streaming") {
|
|
238
|
+
switch (artifact.kind) {
|
|
239
|
+
case "code":
|
|
240
|
+
codeArtifact.initialize?.({
|
|
241
|
+
documentId: artifact.documentId,
|
|
242
|
+
setMetadata: createTypedMetadataSetter(
|
|
243
|
+
setMetadata,
|
|
244
|
+
getCodeArtifactMetadata
|
|
245
|
+
),
|
|
246
|
+
trpc,
|
|
247
|
+
queryClient,
|
|
248
|
+
isAuthenticated,
|
|
249
|
+
});
|
|
250
|
+
break;
|
|
251
|
+
case "sheet":
|
|
252
|
+
sheetArtifact.initialize?.({
|
|
253
|
+
documentId: artifact.documentId,
|
|
254
|
+
setMetadata: createTypedMetadataSetter(
|
|
255
|
+
setMetadata,
|
|
256
|
+
getSheetArtifactMetadata
|
|
257
|
+
),
|
|
258
|
+
trpc,
|
|
259
|
+
queryClient,
|
|
260
|
+
isAuthenticated,
|
|
261
|
+
});
|
|
262
|
+
break;
|
|
263
|
+
case "text":
|
|
264
|
+
textArtifact.initialize?.({
|
|
265
|
+
documentId: artifact.documentId,
|
|
266
|
+
setMetadata,
|
|
267
|
+
trpc,
|
|
268
|
+
queryClient,
|
|
269
|
+
isAuthenticated,
|
|
270
|
+
});
|
|
271
|
+
break;
|
|
272
|
+
default:
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
240
275
|
}
|
|
241
276
|
}, [
|
|
242
277
|
artifact.documentId,
|
|
243
|
-
|
|
278
|
+
artifact.kind,
|
|
244
279
|
setMetadata,
|
|
245
280
|
trpc,
|
|
246
281
|
queryClient,
|
|
@@ -272,6 +307,76 @@ function PureArtifactPanel({
|
|
|
272
307
|
title: artifact.title,
|
|
273
308
|
};
|
|
274
309
|
|
|
310
|
+
const renderArtifactContent = () => {
|
|
311
|
+
switch (artifact.kind) {
|
|
312
|
+
case "code":
|
|
313
|
+
return (
|
|
314
|
+
<>
|
|
315
|
+
<codeArtifact.content
|
|
316
|
+
{...sharedArtifactProps}
|
|
317
|
+
metadata={getCodeArtifactMetadata(metadata)}
|
|
318
|
+
setMetadata={createTypedMetadataSetter(
|
|
319
|
+
setMetadata,
|
|
320
|
+
getCodeArtifactMetadata
|
|
321
|
+
)}
|
|
322
|
+
/>
|
|
323
|
+
{codeArtifact.footer ? (
|
|
324
|
+
<codeArtifact.footer
|
|
325
|
+
{...sharedArtifactProps}
|
|
326
|
+
metadata={getCodeArtifactMetadata(metadata)}
|
|
327
|
+
setMetadata={createTypedMetadataSetter(
|
|
328
|
+
setMetadata,
|
|
329
|
+
getCodeArtifactMetadata
|
|
330
|
+
)}
|
|
331
|
+
/>
|
|
332
|
+
) : null}
|
|
333
|
+
</>
|
|
334
|
+
);
|
|
335
|
+
case "sheet":
|
|
336
|
+
return (
|
|
337
|
+
<>
|
|
338
|
+
<sheetArtifact.content
|
|
339
|
+
{...sharedArtifactProps}
|
|
340
|
+
metadata={getSheetArtifactMetadata(metadata)}
|
|
341
|
+
setMetadata={createTypedMetadataSetter(
|
|
342
|
+
setMetadata,
|
|
343
|
+
getSheetArtifactMetadata
|
|
344
|
+
)}
|
|
345
|
+
/>
|
|
346
|
+
{sheetArtifact.footer ? (
|
|
347
|
+
<sheetArtifact.footer
|
|
348
|
+
{...sharedArtifactProps}
|
|
349
|
+
metadata={getSheetArtifactMetadata(metadata)}
|
|
350
|
+
setMetadata={createTypedMetadataSetter(
|
|
351
|
+
setMetadata,
|
|
352
|
+
getSheetArtifactMetadata
|
|
353
|
+
)}
|
|
354
|
+
/>
|
|
355
|
+
) : null}
|
|
356
|
+
</>
|
|
357
|
+
);
|
|
358
|
+
case "text":
|
|
359
|
+
return (
|
|
360
|
+
<>
|
|
361
|
+
<textArtifact.content
|
|
362
|
+
{...sharedArtifactProps}
|
|
363
|
+
metadata={metadata}
|
|
364
|
+
setMetadata={setMetadata}
|
|
365
|
+
/>
|
|
366
|
+
{textArtifact.footer ? (
|
|
367
|
+
<textArtifact.footer
|
|
368
|
+
{...sharedArtifactProps}
|
|
369
|
+
metadata={metadata}
|
|
370
|
+
setMetadata={setMetadata}
|
|
371
|
+
/>
|
|
372
|
+
) : null}
|
|
373
|
+
</>
|
|
374
|
+
);
|
|
375
|
+
default:
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
|
|
275
380
|
return (
|
|
276
381
|
<ArtifactCard
|
|
277
382
|
className={cn(
|
|
@@ -283,7 +388,7 @@ function PureArtifactPanel({
|
|
|
283
388
|
<ArtifactHeader className="items-start bg-background/80 p-2">
|
|
284
389
|
<div className="flex flex-row items-start gap-4">
|
|
285
390
|
<ArtifactClose
|
|
286
|
-
className="h-fit p-2
|
|
391
|
+
className="h-fit p-2 hover:bg-accent"
|
|
287
392
|
data-testid="artifact-close-button"
|
|
288
393
|
onClick={closeArtifact}
|
|
289
394
|
variant="outline"
|
|
@@ -336,7 +441,7 @@ function PureArtifactPanel({
|
|
|
336
441
|
<ArtifactContent className="flex h-full flex-col p-0">
|
|
337
442
|
<ScrollArea className="h-full max-w-full!">
|
|
338
443
|
<div className="flex flex-col items-center bg-background/80">
|
|
339
|
-
|
|
444
|
+
{renderArtifactContent()}
|
|
340
445
|
|
|
341
446
|
{isCurrentVersion && !isReadonly && (
|
|
342
447
|
<Toolbar
|
|
@@ -351,10 +456,6 @@ function PureArtifactPanel({
|
|
|
351
456
|
</div>
|
|
352
457
|
</ScrollArea>
|
|
353
458
|
|
|
354
|
-
{ArtifactFooterComponent ? (
|
|
355
|
-
<ArtifactFooterComponent {...sharedArtifactProps} />
|
|
356
|
-
) : null}
|
|
357
|
-
|
|
358
459
|
{!(isCurrentVersion || isReadonly) && (
|
|
359
460
|
<VersionFooter
|
|
360
461
|
currentVersionIndex={currentVersionIndex}
|
|
@@ -66,7 +66,7 @@ function AttachmentPill({
|
|
|
66
66
|
return (
|
|
67
67
|
<div
|
|
68
68
|
className={cn(
|
|
69
|
-
"group relative flex h-8 cursor-default select-none items-center gap-1.5 rounded-md border border-border px-1.5 font-medium text-sm transition-all hover:bg-accent hover:text-accent-foreground
|
|
69
|
+
"group relative flex h-8 cursor-default select-none items-center gap-1.5 rounded-md border border-border px-1.5 font-medium text-sm transition-all hover:bg-accent hover:text-accent-foreground",
|
|
70
70
|
isUploading && "opacity-60"
|
|
71
71
|
)}
|
|
72
72
|
data-testid="input-attachment-preview"
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { Github } from "lucide-react";
|
|
4
|
+
import { toast } from "sonner";
|
|
5
|
+
import { ElectronBrowserSignIn } from "@/components/electron-auth-ui";
|
|
4
6
|
import { Button } from "@/components/ui/button";
|
|
5
7
|
import authClient from "@/lib/auth-client";
|
|
6
8
|
import { config } from "@/lib/config";
|
|
9
|
+
import type { SocialAuthSignInOptions } from "@/lib/social-auth";
|
|
7
10
|
|
|
8
11
|
function GoogleIcon({ className }: { className?: string }) {
|
|
9
12
|
return (
|
|
@@ -38,13 +41,55 @@ function VercelIcon({ className }: { className?: string }) {
|
|
|
38
41
|
);
|
|
39
42
|
}
|
|
40
43
|
|
|
41
|
-
export function SocialAuthProviders(
|
|
44
|
+
export function SocialAuthProviders({
|
|
45
|
+
callbackURL,
|
|
46
|
+
electronBrowserLabel,
|
|
47
|
+
isElectron = false,
|
|
48
|
+
onRedirectToUrl,
|
|
49
|
+
query = {},
|
|
50
|
+
signInOptions,
|
|
51
|
+
}: {
|
|
52
|
+
callbackURL?: string;
|
|
53
|
+
electronBrowserLabel?: string;
|
|
54
|
+
isElectron?: boolean;
|
|
55
|
+
onRedirectToUrl?: (url: string) => void;
|
|
56
|
+
query?: Record<string, string>;
|
|
57
|
+
signInOptions?: SocialAuthSignInOptions;
|
|
58
|
+
} = {}) {
|
|
59
|
+
// In the Electron app, use the @better-auth/electron bridges exposed by
|
|
60
|
+
// setupRenderer() in the preload script. requestAuth() opens the sign-in
|
|
61
|
+
// URL in the user's default browser with the proper PKCE params.
|
|
62
|
+
if (config.desktopApp.enabled && isElectron) {
|
|
63
|
+
return <ElectronBrowserSignIn buttonLabel={electronBrowserLabel} />;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function signIn(provider: "google" | "github" | "vercel") {
|
|
67
|
+
try {
|
|
68
|
+
const result = await authClient.signIn.social({
|
|
69
|
+
provider,
|
|
70
|
+
callbackURL,
|
|
71
|
+
...signInOptions,
|
|
72
|
+
fetchOptions: {
|
|
73
|
+
query,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const redirectUrl = result.data?.url;
|
|
78
|
+
if (redirectUrl) {
|
|
79
|
+
onRedirectToUrl?.(redirectUrl);
|
|
80
|
+
}
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error(`Failed to start ${provider} sign-in`, error);
|
|
83
|
+
toast.error("Couldn't start sign-in. Please try again.");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
42
87
|
return (
|
|
43
88
|
<div className="space-y-2">
|
|
44
89
|
{config.authentication.google ? (
|
|
45
90
|
<Button
|
|
46
91
|
className="w-full"
|
|
47
|
-
onClick={() =>
|
|
92
|
+
onClick={() => signIn("google")}
|
|
48
93
|
type="button"
|
|
49
94
|
variant="outline"
|
|
50
95
|
>
|
|
@@ -55,7 +100,7 @@ export function SocialAuthProviders() {
|
|
|
55
100
|
{config.authentication.github ? (
|
|
56
101
|
<Button
|
|
57
102
|
className="w-full"
|
|
58
|
-
onClick={() =>
|
|
103
|
+
onClick={() => signIn("github")}
|
|
59
104
|
type="button"
|
|
60
105
|
variant="outline"
|
|
61
106
|
>
|
|
@@ -66,7 +111,7 @@ export function SocialAuthProviders() {
|
|
|
66
111
|
{config.authentication.vercel ? (
|
|
67
112
|
<Button
|
|
68
113
|
className="w-full"
|
|
69
|
-
onClick={() =>
|
|
114
|
+
onClick={() => signIn("vercel")}
|
|
70
115
|
type="button"
|
|
71
116
|
variant="outline"
|
|
72
117
|
>
|
|
@@ -34,12 +34,12 @@ function PureChatWelcome({
|
|
|
34
34
|
return (
|
|
35
35
|
<div
|
|
36
36
|
className={cn(
|
|
37
|
-
"flex min-h-0 flex-1 flex-col
|
|
37
|
+
"flex min-h-0 flex-1 flex-col justify-end md:justify-center",
|
|
38
38
|
className
|
|
39
39
|
)}
|
|
40
40
|
>
|
|
41
|
-
<div className="mx-auto w-full p-2 @[500px]:px-4 md:max-w-3xl">
|
|
42
|
-
<div className="mb-6">
|
|
41
|
+
<div className="mx-auto w-full p-2 @[500px]:px-4 @[500px]:pb-6 pb-4 md:max-w-3xl">
|
|
42
|
+
<div className="mb-4 md:mb-6">
|
|
43
43
|
<WelcomeMessage />
|
|
44
44
|
</div>
|
|
45
45
|
<MultimodalInput
|
|
@@ -36,7 +36,7 @@ export function ChatMenuItems({
|
|
|
36
36
|
{showShare && onShare && <ShareMenuItem onShare={onShare} />}
|
|
37
37
|
|
|
38
38
|
<DropdownMenuItem
|
|
39
|
-
className="cursor-pointer text-destructive focus:bg-destructive/15 focus:text-destructive
|
|
39
|
+
className="cursor-pointer text-destructive focus:bg-destructive/15 focus:text-destructive"
|
|
40
40
|
onSelect={onDelete}
|
|
41
41
|
>
|
|
42
42
|
<Trash2 size={16} />
|
|
@@ -8,6 +8,7 @@ import { useDataStream } from "@/components/data-stream-provider";
|
|
|
8
8
|
import { useSaveMessageMutation } from "@/hooks/chat-sync-hooks";
|
|
9
9
|
import { useCompleteDataPart } from "@/hooks/use-complete-data-part";
|
|
10
10
|
import { ChatSDKError } from "@/lib/ai/errors";
|
|
11
|
+
import { getStreamErrorToastContent } from "@/lib/ai/stream-errors";
|
|
11
12
|
import type { ChatMessage } from "@/lib/ai/types";
|
|
12
13
|
import {
|
|
13
14
|
useAddMessageToTree,
|
|
@@ -103,14 +104,8 @@ export function ChatSync({
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
console.error(error);
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
toast.error(error.message ?? "An error occured, please try again!", {
|
|
109
|
-
description: cause,
|
|
110
|
-
});
|
|
111
|
-
} else {
|
|
112
|
-
toast.error(error.message ?? "An error occured, please try again!");
|
|
113
|
-
}
|
|
107
|
+
const { message, description } = getStreamErrorToastContent(error);
|
|
108
|
+
toast.error(message, description ? { description } : undefined);
|
|
114
109
|
},
|
|
115
110
|
});
|
|
116
111
|
|
|
@@ -54,16 +54,16 @@ export function Console({
|
|
|
54
54
|
|
|
55
55
|
return consoleOutputs.length > 0 ? (
|
|
56
56
|
<div className={cn("flex w-full flex-col overflow-hidden", className)}>
|
|
57
|
-
<div className="flex h-full w-full flex-col overflow-x-hidden overflow-y-scroll border-
|
|
58
|
-
<div className="sticky top-0 z-50 flex h-fit w-full flex-row items-center justify-between border-
|
|
59
|
-
<div className="flex flex-row items-center gap-3 pl-2 text-
|
|
57
|
+
<div className="flex h-full w-full flex-col overflow-x-hidden overflow-y-scroll border-border border-t bg-muted">
|
|
58
|
+
<div className="sticky top-0 z-50 flex h-fit w-full flex-row items-center justify-between border-border border-b bg-muted px-2 py-1">
|
|
59
|
+
<div className="flex flex-row items-center gap-3 pl-2 text-foreground text-sm">
|
|
60
60
|
<div className="text-muted-foreground">
|
|
61
61
|
<Terminal size={16} />
|
|
62
62
|
</div>
|
|
63
63
|
<div>Console</div>
|
|
64
64
|
</div>
|
|
65
65
|
<Button
|
|
66
|
-
className="size-fit p-1 hover:bg-
|
|
66
|
+
className="size-fit p-1 hover:bg-accent"
|
|
67
67
|
onClick={() => setConsoleOutputs([])}
|
|
68
68
|
size="icon"
|
|
69
69
|
variant="ghost"
|
|
@@ -75,7 +75,7 @@ export function Console({
|
|
|
75
75
|
<div>
|
|
76
76
|
{consoleOutputs.map((consoleOutput, index) => (
|
|
77
77
|
<div
|
|
78
|
-
className="flex flex-row border-
|
|
78
|
+
className="flex flex-row border-border border-b bg-muted px-4 py-2 font-mono text-sm"
|
|
79
79
|
key={consoleOutput.id}
|
|
80
80
|
>
|
|
81
81
|
<div
|
|
@@ -102,10 +102,10 @@ export function Console({
|
|
|
102
102
|
</div>
|
|
103
103
|
</div>
|
|
104
104
|
) : (
|
|
105
|
-
<div className="flex w-full flex-col gap-2 overflow-x-scroll text-
|
|
106
|
-
{consoleOutput.contents.map((content
|
|
105
|
+
<div className="flex w-full flex-col gap-2 overflow-x-scroll text-foreground">
|
|
106
|
+
{consoleOutput.contents.map((content) =>
|
|
107
107
|
content.type === "image" ? (
|
|
108
|
-
<picture key={`${consoleOutput.id}-${
|
|
108
|
+
<picture key={`${consoleOutput.id}-${content.value}`}>
|
|
109
109
|
<img
|
|
110
110
|
alt="output"
|
|
111
111
|
className="w-full max-w-(--breakpoint-toast-mobile) rounded-md"
|
|
@@ -117,7 +117,7 @@ export function Console({
|
|
|
117
117
|
) : (
|
|
118
118
|
<div
|
|
119
119
|
className="break-word-wrap w-full whitespace-pre-line"
|
|
120
|
-
key={`${consoleOutput.id}-${
|
|
120
|
+
key={`${consoleOutput.id}-${content.type}-${content.value}`}
|
|
121
121
|
>
|
|
122
122
|
{content.value}
|
|
123
123
|
</div>
|
|
@@ -94,8 +94,8 @@ function ContextUsage({
|
|
|
94
94
|
if (!usage) {
|
|
95
95
|
return 0;
|
|
96
96
|
}
|
|
97
|
-
const input =
|
|
98
|
-
const cached =
|
|
97
|
+
const input = usage.inputTokens ?? 0;
|
|
98
|
+
const cached = usage.cachedInputTokens ?? 0;
|
|
99
99
|
return input + cached;
|
|
100
100
|
}, [usage]);
|
|
101
101
|
|
|
@@ -7,7 +7,11 @@ import type { ChatMessage, CustomUIDataTypes } from "@/lib/ai/types";
|
|
|
7
7
|
import type { useTRPC } from "@/trpc/react";
|
|
8
8
|
import type { UIArtifact } from "./artifact-panel";
|
|
9
9
|
|
|
10
|
-
export
|
|
10
|
+
export type ArtifactMetadata = object | null;
|
|
11
|
+
|
|
12
|
+
export interface ArtifactActionContext<
|
|
13
|
+
M extends ArtifactMetadata = ArtifactMetadata,
|
|
14
|
+
> {
|
|
11
15
|
content: string;
|
|
12
16
|
currentVersionIndex: number;
|
|
13
17
|
handleVersionChange: (type: "next" | "prev" | "toggle" | "latest") => void;
|
|
@@ -18,7 +22,7 @@ export interface ArtifactActionContext<M = any> {
|
|
|
18
22
|
setMetadata: Dispatch<SetStateAction<M>>;
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
interface ArtifactAction<M =
|
|
25
|
+
interface ArtifactAction<M extends ArtifactMetadata = ArtifactMetadata> {
|
|
22
26
|
description: string;
|
|
23
27
|
icon: ReactNode;
|
|
24
28
|
isDisabled?: (context: ArtifactActionContext<M>) => boolean;
|
|
@@ -37,7 +41,7 @@ export interface ArtifactToolbarItem {
|
|
|
37
41
|
onClick: (context: ArtifactToolbarContext) => void;
|
|
38
42
|
}
|
|
39
43
|
|
|
40
|
-
interface ArtifactContent<M =
|
|
44
|
+
interface ArtifactContent<M extends ArtifactMetadata = ArtifactMetadata> {
|
|
41
45
|
content: string;
|
|
42
46
|
currentVersionIndex: number;
|
|
43
47
|
getDocumentContentById: (index: number) => string;
|
|
@@ -53,7 +57,10 @@ interface ArtifactContent<M = any> {
|
|
|
53
57
|
title: string;
|
|
54
58
|
}
|
|
55
59
|
|
|
56
|
-
interface ArtifactConfig<
|
|
60
|
+
interface ArtifactConfig<
|
|
61
|
+
T extends string,
|
|
62
|
+
M extends ArtifactMetadata = ArtifactMetadata,
|
|
63
|
+
> {
|
|
57
64
|
actions: ArtifactAction<M>[];
|
|
58
65
|
content: ComponentType<ArtifactContent<M>>;
|
|
59
66
|
description: string;
|
|
@@ -80,7 +87,10 @@ interface ArtifactConfig<T extends string, M = any> {
|
|
|
80
87
|
toolbar: ArtifactToolbarItem[];
|
|
81
88
|
}
|
|
82
89
|
|
|
83
|
-
export class Artifact<
|
|
90
|
+
export class Artifact<
|
|
91
|
+
T extends string,
|
|
92
|
+
M extends ArtifactMetadata = ArtifactMetadata,
|
|
93
|
+
> {
|
|
84
94
|
readonly kind: T;
|
|
85
95
|
readonly description: string;
|
|
86
96
|
readonly content: ComponentType<ArtifactContent<M>>;
|