@tscircuit/fake-snippets 0.0.66 → 0.0.68
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/bun-tests/fake-snippets-api/fixtures/get-circuit-json.ts +5 -143
- package/bun-tests/fake-snippets-api/fixtures/get-test-server.ts +1 -4
- package/bun-tests/fake-snippets-api/fixtures/start-server.ts +7 -3
- package/bun-tests/fake-snippets-api/routes/order_quotes/create.test.ts +20 -56
- package/bun-tests/fake-snippets-api/routes/package_files/create_or_update.test.ts +2 -2
- package/bun-tests/fake-snippets-api/routes/package_releases/update.test.ts +1 -1
- package/bun-tests/fake-snippets-api/routes/packages/images.test.ts +0 -11
- package/bun.lock +26 -75
- package/dist/bundle.js +32 -39
- package/fake-snippets-api/routes/api/order_quotes/create.ts +30 -37
- package/fake-snippets-api/routes/api/order_quotes/get.ts +5 -8
- package/package.json +4 -4
- package/src/App.tsx +0 -11
- package/src/ContextProviders.tsx +2 -0
- package/src/components/CmdKMenu.tsx +19 -19
- package/src/components/DownloadButtonAndMenu.tsx +1 -4
- package/src/components/FAQ.tsx +3 -1
- package/src/components/FileSidebar.tsx +50 -1
- package/src/components/Footer.tsx +5 -2
- package/src/components/Header2.tsx +20 -9
- package/src/components/HeaderLogin.tsx +37 -54
- package/src/components/ImageWithFallback.tsx +37 -0
- package/src/components/JLCPCBImportDialog.tsx +45 -29
- package/src/components/PackageCard.tsx +2 -2
- package/src/components/{SnippetLink.tsx → PackageLink.tsx} +8 -16
- package/src/components/PackageSearchResults.tsx +87 -0
- package/src/components/PackagesList.tsx +3 -3
- package/src/components/PageSearchComponent.tsx +9 -9
- package/src/components/ViewPackagePage/components/ShikiCodeViewer.tsx +5 -28
- package/src/components/ViewPackagePage/components/important-files-view.tsx +1 -1
- package/src/components/ViewPackagePage/components/main-content-header.tsx +8 -8
- package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +24 -14
- package/src/components/ViewPackagePage/components/package-header.tsx +7 -2
- package/src/components/dialogs/confirm-delete-package-dialog.tsx +8 -0
- package/src/components/dialogs/edit-package-details-dialog.tsx +145 -138
- package/src/components/package-port/CodeAndPreview.tsx +40 -19
- package/src/components/package-port/CodeEditor.tsx +21 -37
- package/src/components/package-port/CodeEditorHeader.tsx +1 -1
- package/src/components/package-port/EditorNav.tsx +3 -13
- package/src/hooks/use-global-store.ts +1 -0
- package/src/hooks/use-shiki-highlighter.ts +13 -6
- package/src/hooks/useFileManagement.ts +59 -0
- package/src/lib/download-fns/download-gltf.ts +3 -10
- package/src/lib/handleManualEditsImport.tsx +1 -1
- package/src/lib/types.ts +4 -2
- package/src/lib/utils/isValidFileName.ts +5 -0
- package/src/pages/dashboard.tsx +4 -4
- package/src/pages/editor.tsx +20 -14
- package/src/pages/latest.tsx +25 -26
- package/src/pages/quickstart.tsx +5 -5
- package/src/pages/search.tsx +121 -20
- package/src/pages/trending.tsx +14 -58
- package/src/pages/user-profile.tsx +14 -8
- package/bun-tests/fake-snippets-api/routes/snippets/add_star.test.ts +0 -84
- package/bun-tests/fake-snippets-api/routes/snippets/create.test.ts +0 -53
- package/bun-tests/fake-snippets-api/routes/snippets/delete.test.ts +0 -82
- package/bun-tests/fake-snippets-api/routes/snippets/download.test.ts +0 -90
- package/bun-tests/fake-snippets-api/routes/snippets/generate_from_jlcpcb.test.ts +0 -16
- package/bun-tests/fake-snippets-api/routes/snippets/get.test.ts +0 -163
- package/bun-tests/fake-snippets-api/routes/snippets/get_image.test.ts +0 -117
- package/bun-tests/fake-snippets-api/routes/snippets/images.test.ts +0 -114
- package/bun-tests/fake-snippets-api/routes/snippets/list.test.ts +0 -169
- package/bun-tests/fake-snippets-api/routes/snippets/list_newest.test.ts +0 -50
- package/bun-tests/fake-snippets-api/routes/snippets/list_trending.test.ts +0 -72
- package/bun-tests/fake-snippets-api/routes/snippets/remove_star.test.ts +0 -80
- package/bun-tests/fake-snippets-api/routes/snippets/search.test.ts +0 -75
- package/bun-tests/fake-snippets-api/routes/snippets/star-count.test.ts +0 -51
- package/bun-tests/fake-snippets-api/routes/snippets/update.test.ts +0 -175
- package/src/components/AiChatInterface.tsx +0 -229
- package/src/components/CodeAndPreview.tsx +0 -289
- package/src/components/CodeEditor.tsx +0 -539
- package/src/components/CodeEditorHeader.tsx +0 -135
- package/src/components/EditorNav.tsx +0 -502
- package/src/components/OrderPreviewContent.tsx +0 -61
- package/src/components/PreviewContent.tsx +0 -372
- package/src/components/SnippetCard.tsx +0 -159
- package/src/components/SnippetList.tsx +0 -71
- package/src/components/ViewSnippetSidebar.tsx +0 -162
- package/src/components/dialogs/create-order-dialog.tsx +0 -146
- package/src/hooks/use-compiled-tsx.ts +0 -37
- package/src/hooks/use-run-tsx/construct-circuit.tsx +0 -62
- package/src/hooks/use-run-tsx/index.tsx +0 -256
- package/src/hooks/use-save-snippet.ts +0 -66
- package/src/hooks/use-typecheck.ts +0 -54
- package/src/lib/utils/getSyntaxError.ts +0 -13
- package/src/pages/ai.tsx +0 -92
- package/src/pages/preview.tsx +0 -44
- package/src/pages/view-order.tsx +0 -111
- package/src/pages/view-snippet.tsx +0 -166
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { getTestServer } from "bun-tests/fake-snippets-api/fixtures/get-test-server"
|
|
2
|
-
import { expect, test } from "bun:test"
|
|
3
|
-
|
|
4
|
-
test("star count is updated correctly", async () => {
|
|
5
|
-
const { axios, db } = await getTestServer()
|
|
6
|
-
|
|
7
|
-
// Create a snippet using the API
|
|
8
|
-
const snippet = {
|
|
9
|
-
unscoped_name: "TestSnippet",
|
|
10
|
-
owner_name: "testuser",
|
|
11
|
-
code: "Test Content",
|
|
12
|
-
created_at: "2023-01-01T00:00:00Z",
|
|
13
|
-
updated_at: "2023-01-01T00:00:00Z",
|
|
14
|
-
name: "testuser/TestSnippet",
|
|
15
|
-
snippet_type: "package",
|
|
16
|
-
description: "Test Description",
|
|
17
|
-
}
|
|
18
|
-
const createResponse = await axios.post("/api/snippets/create", snippet)
|
|
19
|
-
expect(createResponse.status).toBe(200)
|
|
20
|
-
const createdSnippet = createResponse.data.snippet
|
|
21
|
-
|
|
22
|
-
db.addStar("user1", createdSnippet.snippet_id)
|
|
23
|
-
db.addStar("user2", createdSnippet.snippet_id)
|
|
24
|
-
db.addStar("user3", createdSnippet.snippet_id)
|
|
25
|
-
|
|
26
|
-
// Test star count in list endpoint
|
|
27
|
-
const listResponse = await axios.get("/api/snippets/list")
|
|
28
|
-
expect(listResponse.status).toBe(200)
|
|
29
|
-
expect(listResponse.data.snippets[0].star_count).toBe(3)
|
|
30
|
-
|
|
31
|
-
// Test star count in get endpoint
|
|
32
|
-
const getResponse = await axios.get("/api/snippets/get", {
|
|
33
|
-
params: { snippet_id: createdSnippet.snippet_id },
|
|
34
|
-
})
|
|
35
|
-
expect(getResponse.status).toBe(200)
|
|
36
|
-
expect(getResponse.data.snippet.star_count).toBe(3)
|
|
37
|
-
|
|
38
|
-
await axios.post("/api/snippets/add_star", {
|
|
39
|
-
snippet_id: createdSnippet.snippet_id,
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
// Remove a star using the API
|
|
43
|
-
await axios.post("/api/snippets/remove_star", {
|
|
44
|
-
snippet_id: createdSnippet.snippet_id,
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
// Verify updated star count
|
|
48
|
-
const updatedListResponse = await axios.get("/api/snippets/list")
|
|
49
|
-
expect(updatedListResponse.status).toBe(200)
|
|
50
|
-
expect(updatedListResponse.data.snippets[0].star_count).toBe(3)
|
|
51
|
-
})
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import { getTestServer } from "bun-tests/fake-snippets-api/fixtures/get-test-server"
|
|
2
|
-
import { test, expect } from "bun:test"
|
|
3
|
-
|
|
4
|
-
test("update snippet", async () => {
|
|
5
|
-
const { axios, db } = await getTestServer()
|
|
6
|
-
|
|
7
|
-
// Add a test snippet
|
|
8
|
-
const snippet = {
|
|
9
|
-
unscoped_name: "TestSnippet",
|
|
10
|
-
owner_name: "testuser",
|
|
11
|
-
code: "Original Content",
|
|
12
|
-
created_at: "2023-01-01T00:00:00Z",
|
|
13
|
-
updated_at: "2023-01-01T00:00:00Z",
|
|
14
|
-
name: "testuser/TestSnippet",
|
|
15
|
-
snippet_type: "package",
|
|
16
|
-
description: "Original Description",
|
|
17
|
-
compiled_js: null,
|
|
18
|
-
}
|
|
19
|
-
db.addSnippet(snippet as any)
|
|
20
|
-
|
|
21
|
-
const addedPackage = db.packages[0]
|
|
22
|
-
|
|
23
|
-
// Update the snippet
|
|
24
|
-
const updatedCode = "Updated Content"
|
|
25
|
-
const updatedCompiledJs = "console.log('Updated Content')"
|
|
26
|
-
const response = await axios.post("/api/snippets/update", {
|
|
27
|
-
snippet_id: addedPackage.package_id,
|
|
28
|
-
code: updatedCode,
|
|
29
|
-
compiled_js: updatedCompiledJs,
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
expect(response.status).toBe(200)
|
|
33
|
-
expect(response.data.snippet.code).toBe(updatedCode)
|
|
34
|
-
expect(response.data.snippet.compiled_js).toBe(updatedCompiledJs)
|
|
35
|
-
expect(response.data.snippet.updated_at).not.toBe(addedPackage.created_at)
|
|
36
|
-
|
|
37
|
-
// Verify the snippet was updated in the database
|
|
38
|
-
const updatedPackageFiles = db.packageFiles.filter(
|
|
39
|
-
(p) => p.package_release_id === addedPackage.latest_package_release_id,
|
|
40
|
-
)
|
|
41
|
-
expect(updatedPackageFiles.length).toBe(3)
|
|
42
|
-
expect(updatedPackageFiles[0].content_text).toBe(updatedCode)
|
|
43
|
-
expect(updatedPackageFiles[1].content_text).toBe("") // dts
|
|
44
|
-
expect(updatedPackageFiles[2].content_text).toBe(updatedCompiledJs)
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
test("update non-existent snippet", async () => {
|
|
48
|
-
const { axios } = await getTestServer()
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
await axios.post("/api/snippets/update", {
|
|
52
|
-
snippet_id: "non-existent-id",
|
|
53
|
-
code: "Updated Content",
|
|
54
|
-
compiled_js: "console.log('Updated Content')",
|
|
55
|
-
})
|
|
56
|
-
// If the request doesn't throw an error, fail the test
|
|
57
|
-
expect(true).toBe(false)
|
|
58
|
-
} catch (error: any) {
|
|
59
|
-
expect(error.status).toBe(404)
|
|
60
|
-
expect(error.data.error.message).toBe("Snippet not found")
|
|
61
|
-
}
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
test("update snippet with null compiled_js", async () => {
|
|
65
|
-
const { axios, db } = await getTestServer()
|
|
66
|
-
|
|
67
|
-
// Add a test snippet with compiled_js
|
|
68
|
-
const snippet = {
|
|
69
|
-
unscoped_name: "TestSnippet",
|
|
70
|
-
owner_name: "testuser",
|
|
71
|
-
code: "Original Content",
|
|
72
|
-
created_at: "2023-01-01T00:00:00Z",
|
|
73
|
-
updated_at: "2023-01-01T00:00:00Z",
|
|
74
|
-
name: "testuser/TestSnippet",
|
|
75
|
-
snippet_type: "package",
|
|
76
|
-
description: "Original Description",
|
|
77
|
-
compiled_js: "console.log('Original Content')",
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
db.addSnippet(snippet as any)
|
|
81
|
-
|
|
82
|
-
const addedPackage = db.packages[0]
|
|
83
|
-
|
|
84
|
-
// Update the snippet with null compiled_js
|
|
85
|
-
const response = await axios.post("/api/snippets/update", {
|
|
86
|
-
snippet_id: addedPackage.package_id,
|
|
87
|
-
compiled_js: "",
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
expect(response.status).toBe(200)
|
|
91
|
-
expect(response.data.snippet.compiled_js).toBeEmpty()
|
|
92
|
-
|
|
93
|
-
// Verify the snippet was updated in the database
|
|
94
|
-
const updatedPackageFiles = db.packageFiles.filter(
|
|
95
|
-
(p) => p.package_release_id === addedPackage.latest_package_release_id,
|
|
96
|
-
)
|
|
97
|
-
expect(updatedPackageFiles.length).toBe(3)
|
|
98
|
-
expect(updatedPackageFiles[0].content_text).toBe(snippet.code)
|
|
99
|
-
expect(updatedPackageFiles[1].content_text).toBe("")
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
test("update snippet after create snippet", async () => {
|
|
103
|
-
const { axios, db } = await getTestServer()
|
|
104
|
-
|
|
105
|
-
const snippet = {
|
|
106
|
-
unscoped_name: "TestSnippet",
|
|
107
|
-
code: "Test Content",
|
|
108
|
-
snippet_type: "package",
|
|
109
|
-
description: "Test Description",
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
await axios.post("/api/snippets/create", snippet)
|
|
113
|
-
|
|
114
|
-
const createdSnippet = db.packages[0]
|
|
115
|
-
|
|
116
|
-
const updatedCode = "Updated Content"
|
|
117
|
-
const response = await axios.post("/api/snippets/update", {
|
|
118
|
-
snippet_id: createdSnippet.package_id,
|
|
119
|
-
code: updatedCode,
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
expect(response.status).toBe(200)
|
|
123
|
-
expect(response.data.snippet.code).toBe(updatedCode)
|
|
124
|
-
// Don't check updated_at timestamp as it might not change in tests
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
test("update snippet visibility", async () => {
|
|
128
|
-
const { axios, db } = await getTestServer()
|
|
129
|
-
|
|
130
|
-
const snippet = {
|
|
131
|
-
unscoped_name: "TestSnippet",
|
|
132
|
-
code: "Test Content",
|
|
133
|
-
snippet_type: "package",
|
|
134
|
-
description: "Test Description",
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
await axios.post("/api/snippets/create", snippet)
|
|
138
|
-
|
|
139
|
-
const createdSnippet = db.packages[0]
|
|
140
|
-
|
|
141
|
-
const response = await axios.post("/api/snippets/update", {
|
|
142
|
-
snippet_id: createdSnippet.package_id,
|
|
143
|
-
is_private: true,
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
expect(response.status).toBe(200)
|
|
147
|
-
expect(response.data.snippet.is_private).toBe(true)
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
test("update snippet visibility with unauthenticated user", async () => {
|
|
151
|
-
const { axios, db } = await getTestServer()
|
|
152
|
-
|
|
153
|
-
const snippet = {
|
|
154
|
-
unscoped_name: "TestSnippet",
|
|
155
|
-
code: "Test Content",
|
|
156
|
-
snippet_type: "package",
|
|
157
|
-
description: "Test Description",
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
db.addSnippet(snippet as any)
|
|
161
|
-
|
|
162
|
-
const createdSnippet = db.packages[0]
|
|
163
|
-
|
|
164
|
-
try {
|
|
165
|
-
await axios.post("/api/snippets/update", {
|
|
166
|
-
snippet_id: createdSnippet.package_id,
|
|
167
|
-
is_private: true,
|
|
168
|
-
})
|
|
169
|
-
} catch (error: any) {
|
|
170
|
-
expect(error.status).toBe(403)
|
|
171
|
-
expect(error.data.error.message).toBe(
|
|
172
|
-
"You don't have permission to update this snippet",
|
|
173
|
-
)
|
|
174
|
-
}
|
|
175
|
-
})
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
import { useState, useRef, useEffect } from "react"
|
|
2
|
-
import { Button } from "@/components/ui/button"
|
|
3
|
-
import ChatInput from "./ChatInput"
|
|
4
|
-
import { useAiApi } from "@/hooks/use-ai-api"
|
|
5
|
-
import { createCircuitBoard1Template } from "@tscircuit/prompt-benchmarks"
|
|
6
|
-
import { TextDelta } from "@anthropic-ai/sdk/resources/messages.mjs"
|
|
7
|
-
import { MagicWandIcon } from "@radix-ui/react-icons"
|
|
8
|
-
import { AiChatMessage } from "./AiChatMessage"
|
|
9
|
-
import { useLocation } from "wouter"
|
|
10
|
-
import { useSnippet } from "@/hooks/use-snippet"
|
|
11
|
-
import { Edit2 } from "lucide-react"
|
|
12
|
-
import { SnippetLink } from "./SnippetLink"
|
|
13
|
-
import { useGlobalStore } from "@/hooks/use-global-store"
|
|
14
|
-
import { useSignIn } from "@/hooks/use-sign-in"
|
|
15
|
-
import { extractCodefence } from "extract-codefence"
|
|
16
|
-
import { PrefetchPageLink } from "./PrefetchPageLink"
|
|
17
|
-
|
|
18
|
-
export default function AIChatInterface({
|
|
19
|
-
code,
|
|
20
|
-
hasUnsavedChanges,
|
|
21
|
-
snippetId,
|
|
22
|
-
onCodeChange,
|
|
23
|
-
onStartStreaming,
|
|
24
|
-
onStopStreaming,
|
|
25
|
-
errorMessage,
|
|
26
|
-
disabled,
|
|
27
|
-
}: {
|
|
28
|
-
code: string
|
|
29
|
-
disabled?: boolean
|
|
30
|
-
hasUnsavedChanges: boolean
|
|
31
|
-
snippetId?: string | null
|
|
32
|
-
onCodeChange: (code: string) => void
|
|
33
|
-
onStartStreaming: () => void
|
|
34
|
-
onStopStreaming: () => void
|
|
35
|
-
errorMessage: string | null
|
|
36
|
-
}) {
|
|
37
|
-
const [messages, setMessages] = useState<AiChatMessage[]>([])
|
|
38
|
-
const [isStreaming, setIsStreaming] = useState(false)
|
|
39
|
-
const anthropic = useAiApi()
|
|
40
|
-
const messagesEndRef = useRef<HTMLDivElement>(null)
|
|
41
|
-
const { data: snippet } = useSnippet(snippetId!)
|
|
42
|
-
const [currentCodeBlock, setCurrentCodeBlock] = useState<string | null>(null)
|
|
43
|
-
const [location, navigate] = useLocation()
|
|
44
|
-
const isStreamingRef = useRef(false)
|
|
45
|
-
const isLoggedIn = useGlobalStore((s) => Boolean(s.session))
|
|
46
|
-
const signIn = useSignIn()
|
|
47
|
-
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" })
|
|
50
|
-
}, [messages])
|
|
51
|
-
|
|
52
|
-
const addMessage = async (message: string) => {
|
|
53
|
-
const newMessages = messages.concat([
|
|
54
|
-
{
|
|
55
|
-
sender: "user",
|
|
56
|
-
content: message,
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
sender: "bot",
|
|
60
|
-
content: "",
|
|
61
|
-
codeVersion: messages.filter((m) => m.sender === "bot").length,
|
|
62
|
-
},
|
|
63
|
-
])
|
|
64
|
-
setMessages(newMessages)
|
|
65
|
-
setIsStreaming(true)
|
|
66
|
-
onStartStreaming()
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
const stream = await anthropic.messages.stream({
|
|
70
|
-
model: "claude-3-sonnet-20240229",
|
|
71
|
-
system: createCircuitBoard1Template({
|
|
72
|
-
currentCode: code,
|
|
73
|
-
}),
|
|
74
|
-
messages: [
|
|
75
|
-
// TODO: include previous messages
|
|
76
|
-
{
|
|
77
|
-
role: "user",
|
|
78
|
-
content: message,
|
|
79
|
-
},
|
|
80
|
-
],
|
|
81
|
-
max_tokens: 1000,
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
let accumulatedContent = ""
|
|
85
|
-
let isInCodeBlock = false
|
|
86
|
-
|
|
87
|
-
for await (const chunk of stream) {
|
|
88
|
-
if (chunk.type === "content_block_delta") {
|
|
89
|
-
const chunkText = (chunk.delta as TextDelta).text
|
|
90
|
-
accumulatedContent += chunkText
|
|
91
|
-
|
|
92
|
-
if (chunkText.includes("```")) {
|
|
93
|
-
isInCodeBlock = !isInCodeBlock
|
|
94
|
-
if (isInCodeBlock) {
|
|
95
|
-
setCurrentCodeBlock("")
|
|
96
|
-
} else {
|
|
97
|
-
const codeContent = extractCodefence(accumulatedContent)
|
|
98
|
-
if (codeContent) {
|
|
99
|
-
onCodeChange(codeContent)
|
|
100
|
-
}
|
|
101
|
-
setCurrentCodeBlock(null)
|
|
102
|
-
}
|
|
103
|
-
} else if (isInCodeBlock) {
|
|
104
|
-
setCurrentCodeBlock((prev) => {
|
|
105
|
-
const updatedCode = (prev || "") + chunkText
|
|
106
|
-
onCodeChange(updatedCode)
|
|
107
|
-
return updatedCode
|
|
108
|
-
})
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
setMessages((prevMessages) => {
|
|
112
|
-
const updatedMessages = [...prevMessages]
|
|
113
|
-
updatedMessages[updatedMessages.length - 1].content =
|
|
114
|
-
accumulatedContent
|
|
115
|
-
return updatedMessages
|
|
116
|
-
})
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
} catch (error) {
|
|
120
|
-
console.error("Error streaming response:", error)
|
|
121
|
-
setMessages((prevMessages) => {
|
|
122
|
-
const updatedMessages = [...prevMessages]
|
|
123
|
-
updatedMessages[updatedMessages.length - 1].content =
|
|
124
|
-
"An error occurred while generating the response."
|
|
125
|
-
return updatedMessages
|
|
126
|
-
})
|
|
127
|
-
} finally {
|
|
128
|
-
setIsStreaming(false)
|
|
129
|
-
onStopStreaming()
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
useEffect(() => {
|
|
134
|
-
const searchParams = new URLSearchParams(
|
|
135
|
-
window.location.search.split("?")[1],
|
|
136
|
-
)
|
|
137
|
-
const initialPrompt = searchParams.get("initial_prompt")
|
|
138
|
-
|
|
139
|
-
if (initialPrompt && messages.length === 0 && !isStreamingRef.current) {
|
|
140
|
-
isStreamingRef.current = true
|
|
141
|
-
addMessage(initialPrompt)
|
|
142
|
-
}
|
|
143
|
-
}, [])
|
|
144
|
-
|
|
145
|
-
return (
|
|
146
|
-
<div className="flex flex-col h-[calc(100vh-60px)] max-w-2xl mx-auto p-4 bg-gray-100">
|
|
147
|
-
<div className="flex-1 overflow-y-auto space-y-4 mb-4">
|
|
148
|
-
{snippet && (
|
|
149
|
-
<div className="flex pl-4 p-2 rounded items-center bg-white border border-gray-200 text-sm mb-4 shadow-sm">
|
|
150
|
-
<SnippetLink snippet={snippet} />
|
|
151
|
-
<div className="flex-grow" />
|
|
152
|
-
<PrefetchPageLink href={`/editor?snippet_id=${snippet.snippet_id}`}>
|
|
153
|
-
<Button
|
|
154
|
-
size="sm"
|
|
155
|
-
className="text-xs"
|
|
156
|
-
variant="ghost"
|
|
157
|
-
disabled={hasUnsavedChanges}
|
|
158
|
-
>
|
|
159
|
-
Open in Editor
|
|
160
|
-
<Edit2 className="w-3 h-3 ml-2 opacity-60" />
|
|
161
|
-
</Button>
|
|
162
|
-
</PrefetchPageLink>
|
|
163
|
-
</div>
|
|
164
|
-
)}
|
|
165
|
-
{messages.length === 0 && isLoggedIn && (
|
|
166
|
-
<div className="text-gray-500 text-xl text-center pt-[30vh] flex flex-col items-center">
|
|
167
|
-
<div>Submit a prompt to {snippet ? "edit!" : "get started!"}</div>
|
|
168
|
-
<div className="mt-2">
|
|
169
|
-
This is our legacy AI chat interface. For a better experience,
|
|
170
|
-
please use{" "}
|
|
171
|
-
<PrefetchPageLink href="https://chat.tscircuit.com">
|
|
172
|
-
chat.tscircuit.com
|
|
173
|
-
</PrefetchPageLink>
|
|
174
|
-
.
|
|
175
|
-
</div>
|
|
176
|
-
<div className="text-6xl mt-4">↓</div>
|
|
177
|
-
</div>
|
|
178
|
-
)}
|
|
179
|
-
{!isLoggedIn && (
|
|
180
|
-
<div className="text-gray-500 text-xl text-center pt-[30vh] flex flex-col items-center">
|
|
181
|
-
<div>
|
|
182
|
-
Sign in use the AI chat or{" "}
|
|
183
|
-
<PrefetchPageLink
|
|
184
|
-
className="text-blue-500 underline"
|
|
185
|
-
href="/quickstart"
|
|
186
|
-
>
|
|
187
|
-
use the regular editor
|
|
188
|
-
</PrefetchPageLink>
|
|
189
|
-
</div>
|
|
190
|
-
<div className="mt-4 flex gap-2">
|
|
191
|
-
<Button onClick={() => signIn()}>Sign In</Button>
|
|
192
|
-
<Button onClick={() => signIn()} variant="outline">
|
|
193
|
-
Sign Up
|
|
194
|
-
</Button>
|
|
195
|
-
</div>
|
|
196
|
-
</div>
|
|
197
|
-
)}
|
|
198
|
-
{messages.map((message, index) => (
|
|
199
|
-
<AiChatMessage key={index} message={message} />
|
|
200
|
-
))}
|
|
201
|
-
<div ref={messagesEndRef} />
|
|
202
|
-
</div>
|
|
203
|
-
{code && errorMessage && !isStreaming && (
|
|
204
|
-
<div className="flex justify-end mr-6">
|
|
205
|
-
<Button
|
|
206
|
-
onClick={() => {
|
|
207
|
-
addMessage(`Fix this error: ${errorMessage}`)
|
|
208
|
-
}}
|
|
209
|
-
disabled={!isLoggedIn}
|
|
210
|
-
className="mb-2 bg-green-50 hover:bg-green-100"
|
|
211
|
-
variant="outline"
|
|
212
|
-
>
|
|
213
|
-
<MagicWandIcon className="w-4 h-4 mr-2" />
|
|
214
|
-
<span className="font-bold">Fix Error with AI</span>
|
|
215
|
-
<span className="italic font-normal ml-2">
|
|
216
|
-
"{errorMessage.slice(0, 26)}..."
|
|
217
|
-
</span>
|
|
218
|
-
</Button>
|
|
219
|
-
</div>
|
|
220
|
-
)}
|
|
221
|
-
<ChatInput
|
|
222
|
-
onSubmit={async (message: string) => {
|
|
223
|
-
addMessage(message)
|
|
224
|
-
}}
|
|
225
|
-
disabled={isStreaming || !isLoggedIn}
|
|
226
|
-
/>
|
|
227
|
-
</div>
|
|
228
|
-
)
|
|
229
|
-
}
|