@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.
Files changed (89) hide show
  1. package/bun-tests/fake-snippets-api/fixtures/get-circuit-json.ts +5 -143
  2. package/bun-tests/fake-snippets-api/fixtures/get-test-server.ts +1 -4
  3. package/bun-tests/fake-snippets-api/fixtures/start-server.ts +7 -3
  4. package/bun-tests/fake-snippets-api/routes/order_quotes/create.test.ts +20 -56
  5. package/bun-tests/fake-snippets-api/routes/package_files/create_or_update.test.ts +2 -2
  6. package/bun-tests/fake-snippets-api/routes/package_releases/update.test.ts +1 -1
  7. package/bun-tests/fake-snippets-api/routes/packages/images.test.ts +0 -11
  8. package/bun.lock +26 -75
  9. package/dist/bundle.js +32 -39
  10. package/fake-snippets-api/routes/api/order_quotes/create.ts +30 -37
  11. package/fake-snippets-api/routes/api/order_quotes/get.ts +5 -8
  12. package/package.json +4 -4
  13. package/src/App.tsx +0 -11
  14. package/src/ContextProviders.tsx +2 -0
  15. package/src/components/CmdKMenu.tsx +19 -19
  16. package/src/components/DownloadButtonAndMenu.tsx +1 -4
  17. package/src/components/FAQ.tsx +3 -1
  18. package/src/components/FileSidebar.tsx +50 -1
  19. package/src/components/Footer.tsx +5 -2
  20. package/src/components/Header2.tsx +20 -9
  21. package/src/components/HeaderLogin.tsx +37 -54
  22. package/src/components/ImageWithFallback.tsx +37 -0
  23. package/src/components/JLCPCBImportDialog.tsx +45 -29
  24. package/src/components/PackageCard.tsx +2 -2
  25. package/src/components/{SnippetLink.tsx → PackageLink.tsx} +8 -16
  26. package/src/components/PackageSearchResults.tsx +87 -0
  27. package/src/components/PackagesList.tsx +3 -3
  28. package/src/components/PageSearchComponent.tsx +9 -9
  29. package/src/components/ViewPackagePage/components/ShikiCodeViewer.tsx +5 -28
  30. package/src/components/ViewPackagePage/components/important-files-view.tsx +1 -1
  31. package/src/components/ViewPackagePage/components/main-content-header.tsx +8 -8
  32. package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +24 -14
  33. package/src/components/ViewPackagePage/components/package-header.tsx +7 -2
  34. package/src/components/dialogs/confirm-delete-package-dialog.tsx +8 -0
  35. package/src/components/dialogs/edit-package-details-dialog.tsx +145 -138
  36. package/src/components/package-port/CodeAndPreview.tsx +40 -19
  37. package/src/components/package-port/CodeEditor.tsx +21 -37
  38. package/src/components/package-port/CodeEditorHeader.tsx +1 -1
  39. package/src/components/package-port/EditorNav.tsx +3 -13
  40. package/src/hooks/use-global-store.ts +1 -0
  41. package/src/hooks/use-shiki-highlighter.ts +13 -6
  42. package/src/hooks/useFileManagement.ts +59 -0
  43. package/src/lib/download-fns/download-gltf.ts +3 -10
  44. package/src/lib/handleManualEditsImport.tsx +1 -1
  45. package/src/lib/types.ts +4 -2
  46. package/src/lib/utils/isValidFileName.ts +5 -0
  47. package/src/pages/dashboard.tsx +4 -4
  48. package/src/pages/editor.tsx +20 -14
  49. package/src/pages/latest.tsx +25 -26
  50. package/src/pages/quickstart.tsx +5 -5
  51. package/src/pages/search.tsx +121 -20
  52. package/src/pages/trending.tsx +14 -58
  53. package/src/pages/user-profile.tsx +14 -8
  54. package/bun-tests/fake-snippets-api/routes/snippets/add_star.test.ts +0 -84
  55. package/bun-tests/fake-snippets-api/routes/snippets/create.test.ts +0 -53
  56. package/bun-tests/fake-snippets-api/routes/snippets/delete.test.ts +0 -82
  57. package/bun-tests/fake-snippets-api/routes/snippets/download.test.ts +0 -90
  58. package/bun-tests/fake-snippets-api/routes/snippets/generate_from_jlcpcb.test.ts +0 -16
  59. package/bun-tests/fake-snippets-api/routes/snippets/get.test.ts +0 -163
  60. package/bun-tests/fake-snippets-api/routes/snippets/get_image.test.ts +0 -117
  61. package/bun-tests/fake-snippets-api/routes/snippets/images.test.ts +0 -114
  62. package/bun-tests/fake-snippets-api/routes/snippets/list.test.ts +0 -169
  63. package/bun-tests/fake-snippets-api/routes/snippets/list_newest.test.ts +0 -50
  64. package/bun-tests/fake-snippets-api/routes/snippets/list_trending.test.ts +0 -72
  65. package/bun-tests/fake-snippets-api/routes/snippets/remove_star.test.ts +0 -80
  66. package/bun-tests/fake-snippets-api/routes/snippets/search.test.ts +0 -75
  67. package/bun-tests/fake-snippets-api/routes/snippets/star-count.test.ts +0 -51
  68. package/bun-tests/fake-snippets-api/routes/snippets/update.test.ts +0 -175
  69. package/src/components/AiChatInterface.tsx +0 -229
  70. package/src/components/CodeAndPreview.tsx +0 -289
  71. package/src/components/CodeEditor.tsx +0 -539
  72. package/src/components/CodeEditorHeader.tsx +0 -135
  73. package/src/components/EditorNav.tsx +0 -502
  74. package/src/components/OrderPreviewContent.tsx +0 -61
  75. package/src/components/PreviewContent.tsx +0 -372
  76. package/src/components/SnippetCard.tsx +0 -159
  77. package/src/components/SnippetList.tsx +0 -71
  78. package/src/components/ViewSnippetSidebar.tsx +0 -162
  79. package/src/components/dialogs/create-order-dialog.tsx +0 -146
  80. package/src/hooks/use-compiled-tsx.ts +0 -37
  81. package/src/hooks/use-run-tsx/construct-circuit.tsx +0 -62
  82. package/src/hooks/use-run-tsx/index.tsx +0 -256
  83. package/src/hooks/use-save-snippet.ts +0 -66
  84. package/src/hooks/use-typecheck.ts +0 -54
  85. package/src/lib/utils/getSyntaxError.ts +0 -13
  86. package/src/pages/ai.tsx +0 -92
  87. package/src/pages/preview.tsx +0 -44
  88. package/src/pages/view-order.tsx +0 -111
  89. 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
- }