@tscircuit/fake-snippets 0.0.109 → 0.0.110

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 (181) hide show
  1. package/.github/workflows/bun-formatcheck.yml +2 -2
  2. package/.github/workflows/bun-pver-release.yml +3 -3
  3. package/.github/workflows/bun-test.yml +1 -1
  4. package/.github/workflows/bun-typecheck.yml +2 -2
  5. package/.github/workflows/update-snapshots.yml +1 -1
  6. package/README.md +4 -0
  7. package/api/generated-index.js +37 -3
  8. package/biome.json +2 -1
  9. package/bun-tests/fake-snippets-api/fixtures/get-test-server.ts +31 -3
  10. package/bun-tests/fake-snippets-api/fixtures/preload.ts +18 -0
  11. package/bun-tests/fake-snippets-api/routes/orgs/add_member.test.ts +26 -0
  12. package/bun-tests/fake-snippets-api/routes/orgs/create.test.ts +37 -0
  13. package/bun-tests/fake-snippets-api/routes/orgs/get.test.ts +52 -0
  14. package/bun-tests/fake-snippets-api/routes/orgs/list.test.ts +17 -0
  15. package/bun-tests/fake-snippets-api/routes/orgs/list_members.test.ts +23 -0
  16. package/bun-tests/fake-snippets-api/routes/orgs/remove_member.test.ts +81 -0
  17. package/bun-tests/fake-snippets-api/routes/orgs/update.test.ts +99 -0
  18. package/bun-tests/fake-snippets-api/routes/package_builds/get.test.ts +1 -1
  19. package/bun-tests/fake-snippets-api/routes/package_files/create.test.ts +15 -13
  20. package/bun-tests/fake-snippets-api/routes/package_files/create_or_update.test.ts +26 -24
  21. package/bun-tests/fake-snippets-api/routes/package_files/delete.test.ts +9 -9
  22. package/bun-tests/fake-snippets-api/routes/package_files/download.test.ts +4 -4
  23. package/bun-tests/fake-snippets-api/routes/package_files/get.test.ts +38 -28
  24. package/bun-tests/fake-snippets-api/routes/package_files/list.test.ts +23 -15
  25. package/bun-tests/fake-snippets-api/routes/package_releases/create.test.ts +33 -0
  26. package/bun-tests/fake-snippets-api/routes/package_releases/get.test.ts +4 -4
  27. package/bun-tests/fake-snippets-api/routes/package_releases/get_image_generation_fields.test.ts +38 -0
  28. package/bun-tests/fake-snippets-api/routes/packages/create.test.ts +19 -0
  29. package/bun-tests/fake-snippets-api/routes/packages/fork.test.ts +3 -4
  30. package/bun-tests/fake-snippets-api/routes/packages/get.test.ts +30 -0
  31. package/bun-tests/fake-snippets-api/routes/packages/images.test.ts +4 -2
  32. package/bun-tests/fake-snippets-api/routes/packages/list-1.test.ts +34 -0
  33. package/bun.lock +349 -453
  34. package/bunfig.toml +2 -1
  35. package/dist/bundle.js +1253 -624
  36. package/dist/index.d.ts +291 -4
  37. package/dist/index.js +323 -23
  38. package/dist/schema.d.ts +274 -1
  39. package/dist/schema.js +52 -1
  40. package/fake-snippets-api/lib/db/autoload-dev-packages.ts +31 -20
  41. package/fake-snippets-api/lib/db/db-client.ts +214 -3
  42. package/fake-snippets-api/lib/db/schema.ts +61 -0
  43. package/fake-snippets-api/lib/db/seed.ts +100 -0
  44. package/fake-snippets-api/lib/middleware/with-session-auth.ts +1 -1
  45. package/fake-snippets-api/lib/package_file/get-package-file-id-from-file-descriptor.ts +2 -2
  46. package/fake-snippets-api/lib/public-mapping/public-map-org.ts +32 -0
  47. package/fake-snippets-api/lib/public-mapping/public-map-package-build.ts +10 -0
  48. package/fake-snippets-api/lib/public-mapping/public-map-package-release.ts +17 -0
  49. package/fake-snippets-api/routes/api/orgs/add_member.ts +52 -0
  50. package/fake-snippets-api/routes/api/orgs/create.ts +46 -0
  51. package/fake-snippets-api/routes/api/orgs/get.ts +39 -0
  52. package/fake-snippets-api/routes/api/orgs/list.ts +31 -0
  53. package/fake-snippets-api/routes/api/orgs/list_members.ts +67 -0
  54. package/fake-snippets-api/routes/api/orgs/remove_member.ts +46 -0
  55. package/fake-snippets-api/routes/api/orgs/update.ts +93 -0
  56. package/fake-snippets-api/routes/api/package_files/get.ts +3 -6
  57. package/fake-snippets-api/routes/api/package_files/list.ts +7 -4
  58. package/fake-snippets-api/routes/api/packages/create.ts +54 -10
  59. package/fake-snippets-api/routes/api/packages/get.ts +23 -0
  60. package/fake-snippets-api/routes/api/packages/images/[owner_github_username]/[unscoped_name]/[view_format].ts +13 -11
  61. package/fake-snippets-api/routes/api/packages/list.ts +29 -2
  62. package/fake-snippets-api/routes/api/packages/update_ai_description.ts +37 -0
  63. package/package.json +24 -20
  64. package/renovate.json +1 -1
  65. package/scripts/generate-sitemap.ts +1 -1
  66. package/src/App.tsx +29 -8
  67. package/src/ContextProviders.tsx +25 -2
  68. package/src/components/CircuitJsonImportDialog.tsx +1 -1
  69. package/src/components/CmdKMenu.tsx +281 -247
  70. package/src/components/DownloadButtonAndMenu.tsx +3 -4
  71. package/src/components/FileSidebar.tsx +11 -17
  72. package/src/components/Footer.tsx +8 -9
  73. package/src/components/Header.tsx +19 -32
  74. package/src/components/Header2.tsx +16 -32
  75. package/src/components/HeaderDropdown.tsx +13 -8
  76. package/src/components/HeaderLogin.tsx +43 -15
  77. package/src/components/NotFound.tsx +5 -5
  78. package/src/components/PackageBreadcrumb.tsx +6 -12
  79. package/src/components/PackageSearchResults.tsx +1 -1
  80. package/src/components/PrefetchPageLink.tsx +7 -1
  81. package/src/components/ProfileRouter.tsx +32 -0
  82. package/src/components/SearchComponent.tsx +12 -8
  83. package/src/components/UserCard.tsx +80 -0
  84. package/src/components/ViewPackagePage/components/build-status.tsx +1 -1
  85. package/src/components/ViewPackagePage/components/important-files-view.tsx +105 -34
  86. package/src/components/ViewPackagePage/components/main-content-header.tsx +10 -6
  87. package/src/components/ViewPackagePage/components/main-content-view-selector.tsx +1 -1
  88. package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +54 -19
  89. package/src/components/ViewPackagePage/components/package-header.tsx +25 -33
  90. package/src/components/ViewPackagePage/components/preview-image-squares.tsx +11 -18
  91. package/src/components/ViewPackagePage/components/repo-page-content.tsx +12 -5
  92. package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +16 -10
  93. package/src/components/ViewPackagePage/components/sidebar-releases-section.tsx +11 -11
  94. package/src/components/ViewPackagePage/components/tab-views/pcb-view.tsx +1 -2
  95. package/src/components/ViewPackagePage/components/tab-views/schematic-view.tsx +2 -1
  96. package/src/components/dialogs/GitHubRepositorySelector.tsx +56 -49
  97. package/src/components/dialogs/edit-package-details-dialog.tsx +5 -6
  98. package/src/components/dialogs/import-component-dialog.tsx +16 -9
  99. package/src/components/dialogs/import-package-dialog.tsx +3 -2
  100. package/src/components/dialogs/new-package-save-prompt-dialog.tsx +190 -0
  101. package/src/components/organization/OrganizationCard.tsx +204 -0
  102. package/src/components/organization/OrganizationCardSkeleton.tsx +55 -0
  103. package/src/components/organization/OrganizationHeader.tsx +154 -0
  104. package/src/components/organization/OrganizationMembers.tsx +146 -0
  105. package/src/components/package-port/CodeAndPreview.tsx +15 -12
  106. package/src/components/package-port/CodeEditor.tsx +4 -30
  107. package/src/components/package-port/CodeEditorHeader.tsx +123 -61
  108. package/src/components/package-port/EditorNav.tsx +32 -49
  109. package/src/components/preview/ConnectedPackagesList.tsx +8 -8
  110. package/src/components/preview/ConnectedRepoOverview.tsx +102 -2
  111. package/src/components/preview/PackageReleasesDashboard.tsx +23 -11
  112. package/src/components/ui/tree-view.tsx +6 -3
  113. package/src/hooks/use-add-org-member-mutation.ts +51 -0
  114. package/src/hooks/use-create-org-mutation.ts +38 -0
  115. package/src/hooks/use-create-package-mutation.ts +3 -0
  116. package/src/hooks/use-current-package-release.ts +4 -3
  117. package/src/hooks/use-download-zip.ts +2 -2
  118. package/src/hooks/use-global-store.ts +6 -4
  119. package/src/hooks/use-jlcpcb-component-import.tsx +164 -0
  120. package/src/hooks/use-list-org-members.ts +27 -0
  121. package/src/hooks/use-list-user-orgs.ts +25 -0
  122. package/src/hooks/use-org-by-github-handle.ts +26 -0
  123. package/src/hooks/use-org.ts +24 -0
  124. package/src/hooks/use-organization.ts +42 -0
  125. package/src/hooks/use-package-as-snippet.ts +4 -2
  126. package/src/hooks/use-package-builds.ts +6 -2
  127. package/src/hooks/use-package-files.ts +5 -3
  128. package/src/hooks/use-package-release-by-id-or-version.ts +29 -20
  129. package/src/hooks/use-package-release-images.ts +105 -0
  130. package/src/hooks/use-package-release.ts +2 -2
  131. package/src/hooks/use-package-stars.ts +80 -4
  132. package/src/hooks/use-preview-images.ts +6 -3
  133. package/src/hooks/use-remove-org-member-mutation.ts +32 -0
  134. package/src/hooks/use-update-ai-description-mutation.ts +42 -0
  135. package/src/hooks/use-update-org-mutation.ts +41 -0
  136. package/src/hooks/use-warn-user-on-page-change.ts +71 -4
  137. package/src/hooks/useFileManagement.ts +51 -22
  138. package/src/hooks/useOptimizedPackageFilesLoader.ts +11 -24
  139. package/src/hooks/usePackageFilesLoader.ts +2 -2
  140. package/src/hooks/useUpdatePackageFilesMutation.ts +13 -1
  141. package/src/lib/download-fns/download-gltf-from-circuit-json.ts +1 -1
  142. package/src/lib/download-fns/download-kicad-files.ts +12 -11
  143. package/src/lib/normalize-svg-for-tile.ts +50 -0
  144. package/src/lib/posthog.ts +11 -9
  145. package/src/lib/react-query-api-failure-tracking.ts +148 -0
  146. package/src/lib/sentry.ts +14 -0
  147. package/src/lib/templates/blank-circuit-board-template.ts +0 -4
  148. package/src/lib/ts-lib-cache.ts +122 -7
  149. package/src/lib/utils/checkIfManualEditsImported.ts +4 -4
  150. package/src/lib/utils/findTargetFile.ts +45 -10
  151. package/src/lib/utils/isComponentExported.ts +2 -1
  152. package/src/main.tsx +2 -1
  153. package/src/pages/create-organization.tsx +168 -0
  154. package/src/pages/dashboard.tsx +38 -6
  155. package/src/pages/datasheet.tsx +1 -1
  156. package/src/pages/datasheets.tsx +3 -3
  157. package/src/pages/editor.tsx +4 -6
  158. package/src/pages/landing.tsx +6 -6
  159. package/src/pages/latest.tsx +3 -0
  160. package/src/pages/organization-profile.tsx +199 -0
  161. package/src/pages/organization-settings.tsx +566 -0
  162. package/src/pages/package-editor.tsx +21 -21
  163. package/src/pages/preview-release.tsx +75 -145
  164. package/src/pages/quickstart.tsx +159 -123
  165. package/src/pages/release-detail.tsx +119 -31
  166. package/src/pages/search.tsx +192 -57
  167. package/src/pages/settings-redirect.tsx +44 -0
  168. package/src/pages/trending.tsx +29 -20
  169. package/src/pages/user-profile.tsx +58 -7
  170. package/src/pages/view-package.tsx +7 -13
  171. package/vite.config.ts +9 -0
  172. package/fake-snippets-api/routes/api/autocomplete/create_autocomplete.ts +0 -133
  173. package/src/components/JLCPCBImportDialog.tsx +0 -280
  174. package/src/components/PackageBuildsPage/LogContent.tsx +0 -72
  175. package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +0 -113
  176. package/src/components/PackageBuildsPage/build-preview-content.tsx +0 -56
  177. package/src/components/PackageBuildsPage/collapsible-section.tsx +0 -63
  178. package/src/components/PackageBuildsPage/package-build-details-panel.tsx +0 -166
  179. package/src/components/PackageBuildsPage/package-build-header.tsx +0 -79
  180. package/src/components/PageSearchComponent.tsx +0 -148
  181. package/src/pages/package-builds.tsx +0 -33
@@ -1,133 +0,0 @@
1
- import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
2
- import { z } from "zod"
3
- import OpenAI from "openai"
4
-
5
- // Lazy-loaded client instance
6
- let openai: OpenAI | null = null
7
- let cachedReadme: string | null = null
8
-
9
- function getOpenAIClient() {
10
- const apiKey = process.env.VITE_OPENROUTER_API_KEY
11
- if (!apiKey) {
12
- throw new Error("Missing Api Key in env")
13
- }
14
-
15
- if (!openai) {
16
- openai = new OpenAI({
17
- apiKey,
18
- baseURL: "https://openrouter.ai/api/v1",
19
- defaultHeaders: {
20
- "HTTP-Referer": "https://tscircuit.com",
21
- "X-Title": "TSCircuit Editor",
22
- },
23
- })
24
- }
25
-
26
- return openai
27
- }
28
-
29
- // Cache README
30
- async function getCachedReadme(): Promise<string> {
31
- if (cachedReadme !== null) return cachedReadme
32
- const res = await fetch(
33
- "https://raw.githubusercontent.com/tscircuit/props/main/README.md",
34
- )
35
- if (!res.ok) {
36
- throw new Error(`Failed to fetch README: ${res.status}`)
37
- }
38
- cachedReadme = await res.text()
39
- return cachedReadme
40
- }
41
-
42
- async function completion(
43
- openai: OpenAI,
44
- readmeContent: string,
45
- prefix: string,
46
- suffix: string,
47
- model = "openai/gpt-4.1-mini",
48
- language?: string,
49
- ) {
50
- const systemMessage = `You are an expert ${language ? language + " " : ""}programmer working in a TSX (TypeScript + React JSX) environment.
51
-
52
- Below is the README.md for the available components. You MUST use this to determine which components and props are valid.
53
- Only use components explicitly documented under Available Components in the README. Never invent or guess new components. If the user partially types a component that does not exist in the README, do NOT try to complete it.
54
-
55
- ===== README.md START =====
56
- ${readmeContent}
57
- ===== README.md END =====
58
-
59
- Special instruction for the <chip> component:
60
- - Do NOT add chip as a prop (e.g., <chip chip="..."> is invalid).
61
- - Always use this format:
62
- <chip name="U<number>" footprint="<valid footprint>" pinLabels={{}} pcbX={0} pcbY={0} schX={0} schY={0} />
63
- - Determine the next sequential name automatically: e.g. U1, U2, U3.
64
- - Only use valid footprints and pinLabels from the README.
65
- - Some components like <netlabel> do not have a 'name' prop — do not add it for those.
66
-
67
- STRICT rules:
68
- - If partial like "<capa", only append remaining "citor". Never repeat letters.
69
- - If input is "<capacitor", add only props, never repeat tag.
70
- - Always produce exactly one JSX component, starting with "<" if needed.
71
- - If partial doesn’t match any valid component, output nothing.
72
- - Never output two JSX elements. Always end with exactly one "/>".
73
- - Never add duplicate closing "/>".
74
- - Never output the component name as a prop.
75
- - Never add whitespace before your completion.
76
- - If the input is exactly "<", then start with the component name directly (like "resistor ... />") without adding another "<".
77
- - So that the final result is "<resistor ... />", not "<<resistor ... />".
78
- - Never produce a double "<".
79
-
80
- Examples:
81
- - Input: "<FILL_ME>"
82
- Output: <resistor name="R1" footprint="0603" pcbX={5} pcbY={7} schX={1} schY={2} resistance={1000} />
83
- - Input: "<ca<FILL_ME>"
84
- Output: pacitor name="C1" footprint="0805" pcbX={10} pcbY={15} schX={3} schY={4} />
85
- - Input: "<chip<FILL_ME>"
86
- Output: name="U1" footprint="SOIC-8" pinLabels={{}} pcbX={0} pcbY={0} schX={0} schY={0} />
87
- - Input: "<netl<FILL_ME>"
88
- Output: abel name="N1" />
89
- - NEVER output: <capacitor capacitor ... /> or <netnet ... />
90
- - Input: "<"
91
- Output: resistor name="R1" footprint="0603" pcbX={5} pcbY={7} schX={1} schY={2} resistance={1000} />
92
- - Input: "<ca"
93
- Output: pacitor name="C1" footprint="0805" pcbX={10} pcbY={15} schX={3} schY={4} />
94
- - Input: "<capacitor"
95
- Output: capacitance="1000pF" footprint="0805" name="C1" pcbX={10} pcbY={15} schX={3} schY={4} />`
96
-
97
- const chatCompletion = await openai.chat.completions.create({
98
- messages: [
99
- { role: "system", content: systemMessage },
100
- { role: "user", content: `${prefix}<FILL_ME>${suffix}` },
101
- ],
102
- model,
103
- })
104
-
105
- return chatCompletion.choices[0].message?.content ?? ""
106
- }
107
- export default withRouteSpec({
108
- methods: ["POST"],
109
- auth: "session", // ✅ Require user to be signed in
110
- jsonBody: z.object({
111
- prefix: z.string(),
112
- suffix: z.string(),
113
- model: z.string().optional(),
114
- language: z.string().optional(),
115
- }),
116
- jsonResponse: z.object({
117
- prediction: z.string(),
118
- }),
119
- })(async (req, ctx) => {
120
- const openai = getOpenAIClient()
121
- const { prefix, suffix, model, language } = req.jsonBody
122
-
123
- const readmeContent = await getCachedReadme()
124
- const predictionResult = await completion(
125
- openai,
126
- readmeContent,
127
- prefix,
128
- suffix,
129
- model,
130
- language,
131
- )
132
- return ctx.json({ prediction: predictionResult })
133
- })
@@ -1,280 +0,0 @@
1
- import React, { useState } from "react"
2
- import {
3
- Dialog,
4
- DialogContent,
5
- DialogHeader,
6
- DialogTitle,
7
- DialogFooter,
8
- DialogDescription,
9
- } from "@/components/ui/dialog"
10
- import { Button } from "@/components/ui/button"
11
- import { Input } from "@/components/ui/input"
12
- import { useAxios } from "@/hooks/use-axios"
13
- import { useToast } from "@/hooks/use-toast"
14
- import { useLocation } from "wouter"
15
- import { useGlobalStore } from "@/hooks/use-global-store"
16
- import { PrefetchPageLink } from "./PrefetchPageLink"
17
-
18
- interface JLCPCBImportDialogProps {
19
- open: boolean
20
- onOpenChange: (open: boolean) => void
21
- }
22
-
23
- interface ImportState {
24
- isLoading: boolean
25
- error: string | null
26
- existingComponent: {
27
- partNumber: string
28
- username: string
29
- } | null
30
- }
31
-
32
- interface JLCPCBResponse {
33
- ok: boolean
34
- package: {
35
- package_id: string
36
- }
37
- }
38
-
39
- interface APIError {
40
- status: number
41
- data?: {
42
- message?: string
43
- existing_part_number?: string
44
- part_number?: string
45
- error?: {
46
- message?: string
47
- }
48
- }
49
- }
50
-
51
- const extractErrorMessage = (error: APIError): string => {
52
- return (
53
- error?.data?.message ||
54
- error?.data?.error?.message ||
55
- "An unexpected error occurred"
56
- )
57
- }
58
-
59
- const extractExistingPartNumber = (
60
- error: APIError,
61
- fallback: string,
62
- ): string => {
63
- return error?.data?.message || error?.data?.error?.message || fallback
64
- }
65
-
66
- const useJLCPCBImport = () => {
67
- const [state, setState] = useState<ImportState>({
68
- isLoading: false,
69
- error: null,
70
- existingComponent: null,
71
- })
72
-
73
- const axios = useAxios()
74
- const { toast } = useToast()
75
- const [, navigate] = useLocation()
76
- const session = useGlobalStore((s) => s.session)
77
-
78
- const resetState = () => {
79
- setState({
80
- isLoading: false,
81
- error: null,
82
- existingComponent: null,
83
- })
84
- }
85
-
86
- const importComponent = async (partNumber: string) => {
87
- if (!partNumber.startsWith("C") || partNumber.length < 2) {
88
- toast({
89
- title: "Invalid Part Number",
90
- description:
91
- "JLCPCB part numbers should start with 'C' and be at least 2 characters long.",
92
- variant: "destructive",
93
- })
94
- return { success: false }
95
- }
96
-
97
- setState((prev) => ({
98
- ...prev,
99
- isLoading: true,
100
- error: null,
101
- existingComponent: null,
102
- }))
103
-
104
- try {
105
- const response = await axios.post<JLCPCBResponse>(
106
- "/packages/generate_from_jlcpcb",
107
- {
108
- jlcpcb_part_number: partNumber,
109
- },
110
- )
111
-
112
- if (!response.data.ok) {
113
- setState((prev) => ({
114
- ...prev,
115
- isLoading: false,
116
- error: "Failed to generate package from JLCPCB part",
117
- }))
118
- return { success: false }
119
- }
120
-
121
- const { package: generatedPackage } = response.data
122
-
123
- toast({
124
- title: "Import Successful",
125
- description: "JLCPCB component has been imported successfully.",
126
- })
127
-
128
- navigate(`/editor?package_id=${generatedPackage.package_id}`)
129
- return { success: true }
130
- } catch (error: any) {
131
- const apiError = error as APIError
132
-
133
- if (apiError.status === 404) {
134
- setState((prev) => ({
135
- ...prev,
136
- isLoading: false,
137
- error: `Component with JLCPCB part number ${partNumber} not found`,
138
- }))
139
- } else if (apiError.status === 409) {
140
- const existingPartNumber = extractExistingPartNumber(
141
- apiError,
142
- partNumber,
143
- )
144
- setState((prev) => ({
145
- ...prev,
146
- isLoading: false,
147
- existingComponent: {
148
- partNumber: existingPartNumber,
149
- username: session?.github_username || "",
150
- },
151
- }))
152
- } else {
153
- const errorMessage = extractErrorMessage(apiError)
154
- setState((prev) => ({ ...prev, isLoading: false, error: errorMessage }))
155
-
156
- toast({
157
- title: "Import Failed",
158
- description: errorMessage,
159
- variant: "destructive",
160
- })
161
- }
162
-
163
- return { success: false }
164
- }
165
- }
166
-
167
- return {
168
- ...state,
169
- importComponent,
170
- resetState,
171
- }
172
- }
173
-
174
- export function JLCPCBImportDialog({
175
- open,
176
- onOpenChange,
177
- }: JLCPCBImportDialogProps) {
178
- const [partNumber, setPartNumber] = useState("")
179
- const isLoggedIn = useGlobalStore((s) => Boolean(s.session))
180
-
181
- const { isLoading, error, existingComponent, importComponent, resetState } =
182
- useJLCPCBImport()
183
-
184
- const handleImport = async () => {
185
- const result = await importComponent(partNumber)
186
- if (result.success) {
187
- onOpenChange(false)
188
- }
189
- }
190
-
191
- const handleInputChange = (value: string) => {
192
- setPartNumber(value)
193
- resetState()
194
- }
195
-
196
- const createGitHubIssue = () => {
197
- const issueTitle = `[${partNumber}] Failed to import from JLCPCB`
198
- const issueBody = `I tried to import the part number ${partNumber} from JLCPCB, but it failed. Here's the error I got:\n\`\`\`\n${error}\n\`\`\`\n\nCould be an issue in \`fetchEasyEDAComponent\` or \`convertRawEasyEdaToTs\``
199
- const issueLabels = "snippets,good first issue"
200
- const url = `https://github.com/tscircuit/easyeda-converter/issues/new?title=${encodeURIComponent(
201
- issueTitle,
202
- )}&body=${encodeURIComponent(issueBody)}&labels=${encodeURIComponent(issueLabels)}`
203
- window.open(url, "_blank")
204
- }
205
-
206
- return (
207
- <Dialog open={open} onOpenChange={onOpenChange}>
208
- <DialogContent>
209
- <DialogHeader>
210
- <DialogTitle>Import from JLCPCB</DialogTitle>
211
- <DialogDescription>
212
- Enter the JLCPCB part number to import the component.
213
- </DialogDescription>
214
- </DialogHeader>
215
-
216
- <div className="py-4 text-center">
217
- <a
218
- href="https://yaqwsx.github.io/jlcparts/#/"
219
- target="_blank"
220
- rel="noopener noreferrer"
221
- className="text-blue-500 hover:underline opacity-80"
222
- >
223
- JLCPCB Part Search
224
- </a>
225
-
226
- <Input
227
- className="mt-3"
228
- placeholder="Enter JLCPCB part number (e.g., C46749)"
229
- value={partNumber}
230
- disabled={isLoading}
231
- onChange={(e) => handleInputChange(e.target.value)}
232
- onKeyDown={(e) => {
233
- if (
234
- e.key === "Enter" &&
235
- !isLoading &&
236
- isLoggedIn &&
237
- partNumber.trim()
238
- ) {
239
- handleImport()
240
- }
241
- }}
242
- />
243
-
244
- {error && !existingComponent && (
245
- <>
246
- <p className="bg-red-100 p-2 mt-2 pre-wrap">{error}</p>
247
- <div className="flex justify-end mt-2">
248
- <Button variant="default" onClick={createGitHubIssue}>
249
- File Issue on GitHub (prefilled)
250
- </Button>
251
- </div>
252
- </>
253
- )}
254
-
255
- {existingComponent && (
256
- <p className="p-2 mt-2 pre-wrap text-md text-green-600">
257
- This part number has already been imported to your profile.{" "}
258
- <PrefetchPageLink
259
- className="text-blue-500 hover:underline"
260
- href={`/${existingComponent.username}/${existingComponent.partNumber}`}
261
- >
262
- View it here
263
- </PrefetchPageLink>
264
- </p>
265
- )}
266
- </div>
267
-
268
- <DialogFooter>
269
- <Button onClick={handleImport} disabled={isLoading || !isLoggedIn}>
270
- {!isLoggedIn
271
- ? "You must be logged in to import from JLCPCB"
272
- : isLoading
273
- ? "Importing..."
274
- : "Import"}
275
- </Button>
276
- </DialogFooter>
277
- </DialogContent>
278
- </Dialog>
279
- )
280
- }
@@ -1,72 +0,0 @@
1
- type ErrorObject =
2
- | {
3
- message: string
4
- }
5
- | string
6
-
7
- const getErrorText = (error: ErrorObject | string) => {
8
- if (typeof error === "string") {
9
- return error
10
- }
11
- return error.message
12
- }
13
-
14
- export const LogContent = ({
15
- logs,
16
- error,
17
- }: {
18
- logs: Array<{
19
- type?: "info" | "success" | "error"
20
- msg?: string
21
- message?: string
22
- timestamp?: string | number
23
- [key: string]: unknown
24
- }>
25
- error?: ErrorObject | string | null
26
- }) => {
27
- return (
28
- <div className="font-mono text-xs space-y-1 min-w-0">
29
- {logs.map((log, i) => {
30
- const { type, msg, message, timestamp, ...rest } = log
31
- const text = msg ?? message
32
- if (!text) return null
33
-
34
- return (
35
- <div
36
- key={i}
37
- className={`break-words whitespace-pre-wrap ${
38
- type === "error"
39
- ? "text-red-600"
40
- : type === "success"
41
- ? "text-green-600"
42
- : "text-gray-600"
43
- }`}
44
- >
45
- {timestamp !== undefined && (
46
- <span className="text-gray-500 whitespace-nowrap">
47
- {new Date(Number(timestamp)).toLocaleTimeString()}
48
- </span>
49
- )}
50
- {timestamp !== undefined && " "}
51
- <span className="break-all">{text}</span>
52
- {Object.keys(rest).filter((k) => k !== "package_release_id")
53
- .length > 0 && (
54
- <span className="text-gray-500">
55
- {" "}
56
- {Object.entries(rest)
57
- .filter(([key]) => key !== "package_release_id")
58
- .map(([key, value]) => `${key}: ${String(value)}`)
59
- .join(" ")}
60
- </span>
61
- )}
62
- </div>
63
- )
64
- })}
65
- {error && (
66
- <div className="text-red-600 break-words whitespace-pre-wrap">
67
- {getErrorText(error)}
68
- </div>
69
- )}
70
- </div>
71
- )
72
- }
@@ -1,113 +0,0 @@
1
- import { useCurrentPackageRelease } from "@/hooks/use-current-package-release"
2
- import { PackageRelease } from "fake-snippets-api/lib/db/schema"
3
- import { useState } from "react"
4
- import { LogContent } from "./LogContent"
5
- import { BuildPreviewContent } from "./build-preview-content"
6
- import { CollapsibleSection } from "./collapsible-section"
7
- import { PackageBuildDetailsPanel } from "./package-build-details-panel"
8
- import { PackageBuildHeader } from "./package-build-header"
9
-
10
- function computeDuration(
11
- startedAt: string | null | undefined,
12
- completedAt: string | null | undefined,
13
- ) {
14
- if (!startedAt || !completedAt) return ""
15
- return `${Math.floor((new Date(completedAt).getTime() - new Date(startedAt).getTime()) / 1000)}s`
16
- }
17
-
18
- export const PackageBuildDetailsPage = () => {
19
- const { packageRelease } = useCurrentPackageRelease({
20
- include_logs: true,
21
- refetchInterval: 2000,
22
- })
23
- const [openSections, setOpenSections] = useState<Record<string, boolean>>({})
24
-
25
- const {
26
- circuit_json_build_logs,
27
- circuit_json_build_completed_at,
28
- circuit_json_build_in_progress,
29
- circuit_json_build_is_stale,
30
- circuit_json_build_started_at,
31
- circuit_json_build_error,
32
- circuit_json_build_error_last_updated_at,
33
- transpilation_completed_at,
34
- transpilation_in_progress,
35
- transpilation_is_stale,
36
- transpilation_logs,
37
- transpilation_started_at,
38
- circuit_json_build_display_status,
39
- transpilation_display_status,
40
- transpilation_error,
41
- } = packageRelease ?? ({} as Partial<PackageRelease>)
42
-
43
- const toggleSection = (section: string) => {
44
- setOpenSections((prev) => ({
45
- ...prev,
46
- [section]: !prev[section],
47
- }))
48
- }
49
-
50
- return (
51
- <div className="min-h-screen bg-gray-50 text-gray-900">
52
- <PackageBuildHeader />
53
-
54
- <div className="px-4 sm:px-6 py-4 sm:py-6 container mx-auto max-w-7xl">
55
- {/* Main Content */}
56
- <div className="grid grid-cols-1 lg:grid-cols-3 gap-4 sm:gap-6 mb-6 sm:mb-8 items-start">
57
- {/* Preview Section */}
58
- <div className="lg:col-span-2">
59
- <div className="bg-white border border-gray-200 rounded-lg p-4 flex items-center justify-center min-h-[280px] sm:min-h-[340px] lg:max-h-[420px]">
60
- <BuildPreviewContent />
61
- </div>
62
- </div>
63
-
64
- {/* Details Panel */}
65
- <PackageBuildDetailsPanel />
66
- </div>
67
-
68
- {/* Collapsible Sections */}
69
- <div className="space-y-4 mb-6 sm:mb-8">
70
- <CollapsibleSection
71
- title="Transpilation Logs"
72
- duration={computeDuration(
73
- transpilation_started_at,
74
- transpilation_completed_at,
75
- )}
76
- displayStatus={transpilation_display_status}
77
- isOpen={openSections.summary}
78
- onToggle={() => toggleSection("summary")}
79
- >
80
- <LogContent
81
- logs={
82
- transpilation_logs ?? [
83
- { msg: "No transpilation logs available" },
84
- ]
85
- }
86
- error={transpilation_error}
87
- />
88
- </CollapsibleSection>
89
-
90
- <CollapsibleSection
91
- title="Circuit JSON Build Logs"
92
- duration={computeDuration(
93
- circuit_json_build_started_at,
94
- circuit_json_build_completed_at,
95
- )}
96
- displayStatus={circuit_json_build_display_status}
97
- isOpen={openSections.logs}
98
- onToggle={() => toggleSection("logs")}
99
- >
100
- <LogContent
101
- logs={
102
- circuit_json_build_logs ?? [
103
- { msg: "No Circuit JSON logs available" },
104
- ]
105
- }
106
- error={circuit_json_build_error!}
107
- />
108
- </CollapsibleSection>
109
- </div>
110
- </div>
111
- </div>
112
- )
113
- }
@@ -1,56 +0,0 @@
1
- import { useCurrentPackageInfo } from "@/hooks/use-current-package-info"
2
- import { useCurrentPackageRelease } from "@/hooks/use-current-package-release"
3
- import { useState } from "react"
4
- import { CircuitBoard } from "lucide-react"
5
-
6
- export function BuildPreviewContent() {
7
- const { packageRelease } = useCurrentPackageRelease({ refetchInterval: 2000 })
8
- const { packageInfo } = useCurrentPackageInfo()
9
- const [imageError, setImageError] = useState(false)
10
- const [imageLoading, setImageLoading] = useState(true)
11
-
12
- if (!packageRelease) {
13
- return (
14
- <div className="flex items-center justify-center w-full h-full">
15
- <div className="w-32 h-32 sm:w-48 sm:h-48 bg-gray-200 rounded animate-pulse-slow"></div>
16
- </div>
17
- )
18
- }
19
-
20
- return (
21
- <div className="flex items-center justify-center w-full h-full">
22
- <div className="rounded overflow-hidden w-full max-w-full">
23
- {imageError ? (
24
- <div className="flex flex-col items-center justify-center bg-gray-50 border border-gray-300 rounded-lg p-8 sm:p-12 lg:p-16 min-h-[240px] sm:min-h-[300px] lg:min-h-[360px]">
25
- <CircuitBoard className="w-12 h-12 sm:w-16 sm:h-16 lg:w-20 lg:h-20 text-gray-400 mb-4" />
26
- <h3 className="text-lg sm:text-xl font-medium text-gray-600 mb-2">
27
- Preview Not Available
28
- </h3>
29
- <p className="text-sm sm:text-base text-gray-500 text-center max-w-sm">
30
- The build preview image could not be loaded. This may be because
31
- the build is still processing or the image is not available.
32
- </p>
33
- </div>
34
- ) : (
35
- <>
36
- {imageLoading && (
37
- <div className="flex items-center justify-center bg-gray-100 rounded-lg min-h-[240px] sm:min-h-[300px] lg:min-h-[360px]">
38
- <div className="w-16 h-16 sm:w-20 sm:h-20 bg-gray-200 rounded animate-pulse"></div>
39
- </div>
40
- )}
41
- <img
42
- src={`https://api.tscircuit.com/packages/images/${packageInfo?.name}/pcb.png`}
43
- alt="Package build preview"
44
- className={`object-contain rounded w-full h-auto max-h-[240px] sm:max-h-[300px] lg:max-h-[360px] ${imageLoading ? "hidden" : "block"}`}
45
- onLoad={() => setImageLoading(false)}
46
- onError={() => {
47
- setImageError(true)
48
- setImageLoading(false)
49
- }}
50
- />
51
- </>
52
- )}
53
- </div>
54
- </div>
55
- )
56
- }