@tscircuit/fake-snippets 0.0.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.
Files changed (396) hide show
  1. package/.github/CODEOWNERS +1 -0
  2. package/.github/workflows/bun-formatcheck.yml +26 -0
  3. package/.github/workflows/bun-test.yml +28 -0
  4. package/.github/workflows/bun-typecheck.yml +26 -0
  5. package/.github/workflows/bundle-size-analysis.yml +79 -0
  6. package/.github/workflows/playwright-test.yml +37 -0
  7. package/.github/workflows/stale.yml +40 -0
  8. package/.github/workflows/update-snapshots.yml +49 -0
  9. package/CONTRIBUTING.md +59 -0
  10. package/LICENSE +21 -0
  11. package/README.md +113 -0
  12. package/biome.json +60 -0
  13. package/bun-tests/fake-snippets-api/fixtures/get-circuit-json.ts +148 -0
  14. package/bun-tests/fake-snippets-api/fixtures/get-test-server.ts +96 -0
  15. package/bun-tests/fake-snippets-api/fixtures/start-server.ts +44 -0
  16. package/bun-tests/fake-snippets-api/routes/accounts/get_account_balance.test.ts +18 -0
  17. package/bun-tests/fake-snippets-api/routes/health.test.ts +9 -0
  18. package/bun-tests/fake-snippets-api/routes/order_files/get.test.ts +48 -0
  19. package/bun-tests/fake-snippets-api/routes/order_files/upload.test.ts +77 -0
  20. package/bun-tests/fake-snippets-api/routes/orders/create.test.ts +19 -0
  21. package/bun-tests/fake-snippets-api/routes/orders/get.test.ts +38 -0
  22. package/bun-tests/fake-snippets-api/routes/orders/list.test.ts +30 -0
  23. package/bun-tests/fake-snippets-api/routes/orders/update.test.ts +46 -0
  24. package/bun-tests/fake-snippets-api/routes/snippets/add_star.test.ts +114 -0
  25. package/bun-tests/fake-snippets-api/routes/snippets/create.test.ts +28 -0
  26. package/bun-tests/fake-snippets-api/routes/snippets/delete.test.ts +106 -0
  27. package/bun-tests/fake-snippets-api/routes/snippets/download.test.ts +90 -0
  28. package/bun-tests/fake-snippets-api/routes/snippets/generate_from_jlcpcb.test.ts +25 -0
  29. package/bun-tests/fake-snippets-api/routes/snippets/get_image.test.ts +113 -0
  30. package/bun-tests/fake-snippets-api/routes/snippets/images.test.ts +112 -0
  31. package/bun-tests/fake-snippets-api/routes/snippets/list.test.ts +62 -0
  32. package/bun-tests/fake-snippets-api/routes/snippets/list_newest.test.ts +48 -0
  33. package/bun-tests/fake-snippets-api/routes/snippets/list_trending.test.ts +69 -0
  34. package/bun-tests/fake-snippets-api/routes/snippets/remove_star.test.ts +110 -0
  35. package/bun-tests/fake-snippets-api/routes/snippets/search.test.ts +75 -0
  36. package/bun-tests/fake-snippets-api/routes/snippets/star-count.test.ts +44 -0
  37. package/bun-tests/fake-snippets-api/routes/snippets/update.test.ts +116 -0
  38. package/bun-tests/parts-engine.test.ts +18 -0
  39. package/bun.lockb +0 -0
  40. package/bunfig.toml +2 -0
  41. package/components.json +20 -0
  42. package/dist/assets/editor_example_1-1000w.webp +0 -0
  43. package/dist/assets/editor_example_1-1200w.webp +0 -0
  44. package/dist/assets/editor_example_1-1600w.webp +0 -0
  45. package/dist/assets/editor_example_1-2000w.webp +0 -0
  46. package/dist/assets/editor_example_1-400w.webp +0 -0
  47. package/dist/assets/editor_example_1-600w.webp +0 -0
  48. package/dist/assets/editor_example_1-800w.webp +0 -0
  49. package/dist/assets/editor_example_1_more_square-1000w.webp +0 -0
  50. package/dist/assets/editor_example_1_more_square-1200w.webp +0 -0
  51. package/dist/assets/editor_example_1_more_square-1600w.webp +0 -0
  52. package/dist/assets/editor_example_1_more_square-2000w.webp +0 -0
  53. package/dist/assets/editor_example_1_more_square-400w.webp +0 -0
  54. package/dist/assets/editor_example_1_more_square-600w.webp +0 -0
  55. package/dist/assets/editor_example_1_more_square-800w.webp +0 -0
  56. package/dist/assets/editor_example_2-1000w.webp +0 -0
  57. package/dist/assets/editor_example_2-1200w.webp +0 -0
  58. package/dist/assets/editor_example_2-1600w.webp +0 -0
  59. package/dist/assets/editor_example_2-2000w.webp +0 -0
  60. package/dist/assets/editor_example_2-400w.webp +0 -0
  61. package/dist/assets/editor_example_2-600w.webp +0 -0
  62. package/dist/assets/editor_example_2-800w.webp +0 -0
  63. package/dist/assets/example_schematic-1000w.webp +0 -0
  64. package/dist/assets/example_schematic-1200w.webp +0 -0
  65. package/dist/assets/example_schematic-1600w.webp +0 -0
  66. package/dist/assets/example_schematic-2000w.webp +0 -0
  67. package/dist/assets/example_schematic-400w.webp +0 -0
  68. package/dist/assets/example_schematic-600w.webp +0 -0
  69. package/dist/assets/example_schematic-800w.webp +0 -0
  70. package/dist/bundle.js +3270 -0
  71. package/dist/robots.txt +9 -0
  72. package/dist/sitemap.xml +118 -0
  73. package/docs/CIRCUIT_JSON_SOURCE_COMPONENT_OVERVIEW.md +151 -0
  74. package/fake-snippets-api/README.md +6 -0
  75. package/fake-snippets-api/biome.json +47 -0
  76. package/fake-snippets-api/bun.lockb +0 -0
  77. package/fake-snippets-api/lib/db/autoload-dev-snippets.ts +84 -0
  78. package/fake-snippets-api/lib/db/autoload-snippets.json +24 -0
  79. package/fake-snippets-api/lib/db/db-client.ts +343 -0
  80. package/fake-snippets-api/lib/db/schema.ts +112 -0
  81. package/fake-snippets-api/lib/db/seed.ts +1608 -0
  82. package/fake-snippets-api/lib/middleware/with-ctx-error.ts +26 -0
  83. package/fake-snippets-api/lib/middleware/with-db.ts +15 -0
  84. package/fake-snippets-api/lib/middleware/with-error-handling.ts +24 -0
  85. package/fake-snippets-api/lib/middleware/with-optional-session-auth.ts +34 -0
  86. package/fake-snippets-api/lib/middleware/with-request-logging.ts +54 -0
  87. package/fake-snippets-api/lib/middleware/with-session-auth.ts +39 -0
  88. package/fake-snippets-api/lib/middleware/with-winter-spec.ts +24 -0
  89. package/fake-snippets-api/next-env.d.ts +5 -0
  90. package/fake-snippets-api/routes/api/accounts/get.ts +21 -0
  91. package/fake-snippets-api/routes/api/accounts/get_account_balance.ts +22 -0
  92. package/fake-snippets-api/routes/api/accounts/update.ts +32 -0
  93. package/fake-snippets-api/routes/api/ai/[...anyroute].ts +31 -0
  94. package/fake-snippets-api/routes/api/ai.ts +2 -0
  95. package/fake-snippets-api/routes/api/aistream/[...anyroute].ts +65 -0
  96. package/fake-snippets-api/routes/api/health.ts +9 -0
  97. package/fake-snippets-api/routes/api/internal/sessions/create_without_auth.ts +63 -0
  98. package/fake-snippets-api/routes/api/order_files/get.ts +28 -0
  99. package/fake-snippets-api/routes/api/order_files/upload.ts +43 -0
  100. package/fake-snippets-api/routes/api/orders/create.ts +41 -0
  101. package/fake-snippets-api/routes/api/orders/get.ts +28 -0
  102. package/fake-snippets-api/routes/api/orders/list.ts +15 -0
  103. package/fake-snippets-api/routes/api/orders/update.ts +50 -0
  104. package/fake-snippets-api/routes/api/snippets/add_star.ts +42 -0
  105. package/fake-snippets-api/routes/api/snippets/create.ts +55 -0
  106. package/fake-snippets-api/routes/api/snippets/delete.ts +41 -0
  107. package/fake-snippets-api/routes/api/snippets/download.ts +148 -0
  108. package/fake-snippets-api/routes/api/snippets/generate_from_jlcpcb.ts +55 -0
  109. package/fake-snippets-api/routes/api/snippets/get.ts +50 -0
  110. package/fake-snippets-api/routes/api/snippets/get_image.ts +65 -0
  111. package/fake-snippets-api/routes/api/snippets/images/[author]/[snippet_name]/[typeFormat].ts +74 -0
  112. package/fake-snippets-api/routes/api/snippets/list.ts +28 -0
  113. package/fake-snippets-api/routes/api/snippets/list_newest.ts +13 -0
  114. package/fake-snippets-api/routes/api/snippets/list_trending.ts +21 -0
  115. package/fake-snippets-api/routes/api/snippets/remove_star.ts +42 -0
  116. package/fake-snippets-api/routes/api/snippets/search.ts +18 -0
  117. package/fake-snippets-api/routes/api/snippets/update.ts +86 -0
  118. package/favicon.ico +0 -0
  119. package/index.html +23 -0
  120. package/landing.html +23 -0
  121. package/package.json +164 -0
  122. package/playwright-tests/ai-page.spec.ts +19 -0
  123. package/playwright-tests/cmd-click.spec.ts +43 -0
  124. package/playwright-tests/dashboard-page.spec.ts +10 -0
  125. package/playwright-tests/editor-page.spec.ts +15 -0
  126. package/playwright-tests/files-dialog.spec.ts +19 -0
  127. package/playwright-tests/footprint-dialog/footprint-dialog.spec.ts +27 -0
  128. package/playwright-tests/footprint-dialog/footprint-insertion.spec.ts +38 -0
  129. package/playwright-tests/footprint-dialog/footprint-preview.spec.ts +34 -0
  130. package/playwright-tests/footprint-dialog/footprint-selection.spec.ts +29 -0
  131. package/playwright-tests/handle-manual-edits.spec.ts +55 -0
  132. package/playwright-tests/home-page.spec.ts +10 -0
  133. package/playwright-tests/images.spec.ts +17 -0
  134. package/playwright-tests/manual-edits.spec.ts +89 -0
  135. package/playwright-tests/preview-page.spec.ts +14 -0
  136. package/playwright-tests/quickstart-page.spec.ts +10 -0
  137. package/playwright-tests/search-links.spec.ts +21 -0
  138. package/playwright-tests/search.spec.ts +27 -0
  139. package/playwright-tests/snapshots/ai-page.spec.ts-AI-Page-lg.png +0 -0
  140. package/playwright-tests/snapshots/ai-page.spec.ts-AI-Page-md.png +0 -0
  141. package/playwright-tests/snapshots/ai-page.spec.ts-AI-Page-xs.png +0 -0
  142. package/playwright-tests/snapshots/cmd-click.spec.ts-underlined-imports.png +0 -0
  143. package/playwright-tests/snapshots/dashboard-page.spec.ts-Dashboard-page-lg.png +0 -0
  144. package/playwright-tests/snapshots/dashboard-page.spec.ts-Dashboard-page-md.png +0 -0
  145. package/playwright-tests/snapshots/dashboard-page.spec.ts-Dashboard-page-xs.png +0 -0
  146. package/playwright-tests/snapshots/editor-page.spec.ts-editor-with-snippet.png +0 -0
  147. package/playwright-tests/snapshots/error-fallback.spec.ts-error-fallback-lg.png +0 -0
  148. package/playwright-tests/snapshots/error-fallback.spec.ts-error-fallback-md.png +0 -0
  149. package/playwright-tests/snapshots/error-fallback.spec.ts-error-fallback-xs.png +0 -0
  150. package/playwright-tests/snapshots/files-dialog.spec.ts-view-snippet-files.png +0 -0
  151. package/playwright-tests/snapshots/footprint-dialog/footprint-dialog.spec.ts-footprint-preview-lg.png +0 -0
  152. package/playwright-tests/snapshots/footprint-dialog/footprint-dialog.spec.ts-footprint-preview-md.png +0 -0
  153. package/playwright-tests/snapshots/footprint-dialog/footprint-dialog.spec.ts-footprint-preview-xs.png +0 -0
  154. package/playwright-tests/snapshots/footprint-dialog/footprint-insertion.spec.ts-footprint-insertion-lg.png +0 -0
  155. package/playwright-tests/snapshots/footprint-dialog/footprint-insertion.spec.ts-footprint-insertion-md.png +0 -0
  156. package/playwright-tests/snapshots/footprint-dialog/footprint-insertion.spec.ts-footprint-insertion-xs.png +0 -0
  157. package/playwright-tests/snapshots/footprint-dialog/footprint-preview.spec.ts-footprint-preview-lg.png +0 -0
  158. package/playwright-tests/snapshots/footprint-dialog/footprint-preview.spec.ts-footprint-preview-md.png +0 -0
  159. package/playwright-tests/snapshots/footprint-dialog/footprint-preview.spec.ts-footprint-preview-xs.png +0 -0
  160. package/playwright-tests/snapshots/footprint-dialog/footprint-selection.spec.ts-footprint-preview-lg.png +0 -0
  161. package/playwright-tests/snapshots/footprint-dialog/footprint-selection.spec.ts-footprint-preview-md.png +0 -0
  162. package/playwright-tests/snapshots/footprint-dialog/footprint-selection.spec.ts-footprint-preview-xs.png +0 -0
  163. package/playwright-tests/snapshots/handle-manual-edits.spec.ts-handle-manual-edits.png +0 -0
  164. package/playwright-tests/snapshots/home-page.spec.ts-Home-page-lg.png +0 -0
  165. package/playwright-tests/snapshots/home-page.spec.ts-Home-page-md.png +0 -0
  166. package/playwright-tests/snapshots/home-page.spec.ts-Home-page-xs.png +0 -0
  167. package/playwright-tests/snapshots/images.spec.ts-pcb-image.png +0 -0
  168. package/playwright-tests/snapshots/images.spec.ts-schematic-image.png +0 -0
  169. package/playwright-tests/snapshots/manual-edits.spec.ts-editor-manual-edits.png +0 -0
  170. package/playwright-tests/snapshots/manual-edits.spec.ts-manual-edits-view.png +0 -0
  171. package/playwright-tests/snapshots/preview-page.spec.ts-preview-snippet-pcb.png +0 -0
  172. package/playwright-tests/snapshots/preview-page.spec.ts-preview-snippet-schematic.png +0 -0
  173. package/playwright-tests/snapshots/quickstart-page.spec.ts-Quickstart-Pagelg.png +0 -0
  174. package/playwright-tests/snapshots/quickstart-page.spec.ts-Quickstart-Pagemd.png +0 -0
  175. package/playwright-tests/snapshots/quickstart-page.spec.ts-Quickstart-Pagexs.png +0 -0
  176. package/playwright-tests/snapshots/search-links.spec.ts-search-links.png +0 -0
  177. package/playwright-tests/snapshots/search.spec.ts-search-lg.png +0 -0
  178. package/playwright-tests/snapshots/search.spec.ts-search-md.png +0 -0
  179. package/playwright-tests/snapshots/search.spec.ts-search-xs.png +0 -0
  180. package/playwright-tests/snapshots/star.spec.ts-remove-star-button.png +0 -0
  181. package/playwright-tests/snapshots/star.spec.ts-star-button.png +0 -0
  182. package/playwright-tests/snapshots/update-description.spec.ts-update-description.png +0 -0
  183. package/playwright-tests/snapshots/view-snippet.spec.ts-view-snippet-after-lg.png +0 -0
  184. package/playwright-tests/snapshots/view-snippet.spec.ts-view-snippet-after-md.png +0 -0
  185. package/playwright-tests/snapshots/view-snippet.spec.ts-view-snippet-after-xs.png +0 -0
  186. package/playwright-tests/snapshots/view-snippet.spec.ts-view-snippet-before-lg.png +0 -0
  187. package/playwright-tests/snapshots/view-snippet.spec.ts-view-snippet-before-md.png +0 -0
  188. package/playwright-tests/snapshots/view-snippet.spec.ts-view-snippet-before-xs.png +0 -0
  189. package/playwright-tests/snapshots/view-snippet.spec.ts-view-snippet-files.png +0 -0
  190. package/playwright-tests/star.spec.ts +40 -0
  191. package/playwright-tests/update-description.spec.ts +18 -0
  192. package/playwright-tests/view-snippet.spec.ts +35 -0
  193. package/playwright-tests/viewports.ts +5 -0
  194. package/playwright.config.ts +27 -0
  195. package/postcss.config.js +6 -0
  196. package/public/robots.txt +9 -0
  197. package/renovate.json +24 -0
  198. package/scripts/generate-image-sizes.ts +58 -0
  199. package/scripts/generate-sitemap.ts +103 -0
  200. package/scripts/generate_bundle_stats.js +192 -0
  201. package/scripts/snapshot.ts +35 -0
  202. package/src/App.tsx +113 -0
  203. package/src/ContextProviders.tsx +9 -0
  204. package/src/assets/originals/editor_example_1.webp +0 -0
  205. package/src/assets/originals/editor_example_1_more_square.webp +0 -0
  206. package/src/assets/originals/editor_example_2.webp +0 -0
  207. package/src/assets/originals/example_schematic.webp +0 -0
  208. package/src/components/AiChatInterface.tsx +221 -0
  209. package/src/components/AiChatMessage.tsx +86 -0
  210. package/src/components/Analytics.tsx +30 -0
  211. package/src/components/BomTable.tsx +69 -0
  212. package/src/components/ChatInput.tsx +53 -0
  213. package/src/components/CircuitToSvgWithMouseControl.tsx +78 -0
  214. package/src/components/CmdKMenu.tsx +301 -0
  215. package/src/components/CodeAndPreview.tsx +258 -0
  216. package/src/components/CodeEditor.tsx +460 -0
  217. package/src/components/CodeEditorHeader.tsx +160 -0
  218. package/src/components/CreateNewSnippetWithAiHero.tsx +77 -0
  219. package/src/components/DownloadButtonAndMenu.tsx +212 -0
  220. package/src/components/EditorNav.tsx +428 -0
  221. package/src/components/ErrorFallback.tsx +25 -0
  222. package/src/components/ErrorTabContent.tsx +122 -0
  223. package/src/components/FAQ.tsx +113 -0
  224. package/src/components/Footer.tsx +134 -0
  225. package/src/components/Footer2.tsx +100 -0
  226. package/src/components/FootprintDialog.tsx +342 -0
  227. package/src/components/Header.tsx +187 -0
  228. package/src/components/Header2.tsx +135 -0
  229. package/src/components/HeaderDropdown.tsx +83 -0
  230. package/src/components/HeaderLogin.tsx +94 -0
  231. package/src/components/JLCPCBImportDialog.tsx +155 -0
  232. package/src/components/LandingHero.tsx +175 -0
  233. package/src/components/LatestSnippets.tsx +39 -0
  234. package/src/components/OptimizedImage.tsx +96 -0
  235. package/src/components/OrderPreviewContent.tsx +61 -0
  236. package/src/components/ParametersEditor.tsx +140 -0
  237. package/src/components/PcbViewerWithContainerHeight.tsx +47 -0
  238. package/src/components/PrefetchPageLink.tsx +45 -0
  239. package/src/components/PreviewContent.tsx +375 -0
  240. package/src/components/PreviewEmptyState.tsx +16 -0
  241. package/src/components/RunButton.tsx +27 -0
  242. package/src/components/SearchComponent.tsx +163 -0
  243. package/src/components/ShippingInformationForm.tsx +423 -0
  244. package/src/components/SnippetLink.tsx +37 -0
  245. package/src/components/StaticPreviewContent.tsx +89 -0
  246. package/src/components/StaticViewSnippetHeader.tsx +70 -0
  247. package/src/components/StaticViewSnippetSidebar.tsx +100 -0
  248. package/src/components/TableViewer/CircuitJsonTableViewer.tsx +316 -0
  249. package/src/components/TableViewer/ClickableText.tsx +21 -0
  250. package/src/components/TableViewer/HeaderCell.tsx +27 -0
  251. package/src/components/TableViewer/Modal.tsx +39 -0
  252. package/src/components/TrendingSnippetCarousel.tsx +77 -0
  253. package/src/components/TypeBadge.tsx +31 -0
  254. package/src/components/ViewSnippetHeader.tsx +144 -0
  255. package/src/components/ViewSnippetSidebar.tsx +162 -0
  256. package/src/components/dialogs/confirm-delete-snippet-dialog.tsx +80 -0
  257. package/src/components/dialogs/create-order-dialog.tsx +146 -0
  258. package/src/components/dialogs/create-use-dialog.tsx +34 -0
  259. package/src/components/dialogs/edit-description-dialog.tsx +96 -0
  260. package/src/components/dialogs/files-dialog.tsx +70 -0
  261. package/src/components/dialogs/import-snippet-dialog.tsx +84 -0
  262. package/src/components/dialogs/rename-snippet-dialog.tsx +81 -0
  263. package/src/components/dialogs/view-ts-files-dialog.tsx +89 -0
  264. package/src/components/ui/accordion.tsx +55 -0
  265. package/src/components/ui/alert-dialog.tsx +139 -0
  266. package/src/components/ui/alert.tsx +59 -0
  267. package/src/components/ui/aspect-ratio.tsx +5 -0
  268. package/src/components/ui/avatar.tsx +48 -0
  269. package/src/components/ui/badge.tsx +36 -0
  270. package/src/components/ui/breadcrumb.tsx +118 -0
  271. package/src/components/ui/button.tsx +58 -0
  272. package/src/components/ui/calendar.tsx +73 -0
  273. package/src/components/ui/card.tsx +76 -0
  274. package/src/components/ui/carousel.tsx +260 -0
  275. package/src/components/ui/chart.tsx +363 -0
  276. package/src/components/ui/checkbox.tsx +28 -0
  277. package/src/components/ui/collapsible.tsx +9 -0
  278. package/src/components/ui/combobox.tsx +178 -0
  279. package/src/components/ui/command.tsx +151 -0
  280. package/src/components/ui/context-menu.tsx +202 -0
  281. package/src/components/ui/dialog.tsx +120 -0
  282. package/src/components/ui/drawer.tsx +116 -0
  283. package/src/components/ui/dropdown-menu.tsx +203 -0
  284. package/src/components/ui/form.tsx +182 -0
  285. package/src/components/ui/hover-card.tsx +27 -0
  286. package/src/components/ui/input-otp.tsx +69 -0
  287. package/src/components/ui/input.tsx +25 -0
  288. package/src/components/ui/label.tsx +24 -0
  289. package/src/components/ui/menubar.tsx +238 -0
  290. package/src/components/ui/navigation-menu.tsx +129 -0
  291. package/src/components/ui/pagination.tsx +121 -0
  292. package/src/components/ui/popover.tsx +31 -0
  293. package/src/components/ui/progress.tsx +26 -0
  294. package/src/components/ui/radio-group.tsx +42 -0
  295. package/src/components/ui/resizable.tsx +43 -0
  296. package/src/components/ui/scroll-area.tsx +46 -0
  297. package/src/components/ui/searchable-select.tsx +94 -0
  298. package/src/components/ui/select.tsx +162 -0
  299. package/src/components/ui/separator.tsx +29 -0
  300. package/src/components/ui/sheet.tsx +141 -0
  301. package/src/components/ui/skeleton.tsx +18 -0
  302. package/src/components/ui/slider.tsx +26 -0
  303. package/src/components/ui/sonner.tsx +30 -0
  304. package/src/components/ui/switch.tsx +27 -0
  305. package/src/components/ui/table.tsx +120 -0
  306. package/src/components/ui/tabs.tsx +53 -0
  307. package/src/components/ui/textarea.tsx +24 -0
  308. package/src/components/ui/toast.tsx +128 -0
  309. package/src/components/ui/toaster.tsx +33 -0
  310. package/src/components/ui/toggle-group.tsx +59 -0
  311. package/src/components/ui/toggle.tsx +43 -0
  312. package/src/components/ui/tooltip.tsx +28 -0
  313. package/src/entry-server.tsx +12 -0
  314. package/src/hooks/use-account-balance.ts +24 -0
  315. package/src/hooks/use-ai-api.ts +35 -0
  316. package/src/hooks/use-axios.ts +20 -0
  317. package/src/hooks/use-code-completion-ai-api.ts +11 -0
  318. package/src/hooks/use-compiled-tsx.ts +37 -0
  319. package/src/hooks/use-copy-to-clipboard.ts +26 -0
  320. package/src/hooks/use-create-snippet-mutation.ts +62 -0
  321. package/src/hooks/use-current-snippet-id.ts +75 -0
  322. package/src/hooks/use-current-snippet.ts +24 -0
  323. package/src/hooks/use-global-store.ts +33 -0
  324. package/src/hooks/use-is-using-fake-api.ts +3 -0
  325. package/src/hooks/use-run-tsx/construct-circuit.tsx +64 -0
  326. package/src/hooks/use-run-tsx/eval-compiled-js.ts +9 -0
  327. package/src/hooks/use-run-tsx/index.tsx +251 -0
  328. package/src/hooks/use-save-snippet.ts +66 -0
  329. package/src/hooks/use-sign-in.ts +22 -0
  330. package/src/hooks/use-snippet-by-name.ts +25 -0
  331. package/src/hooks/use-snippet.ts +23 -0
  332. package/src/hooks/use-snippets-base-api-url.ts +3 -0
  333. package/src/hooks/use-toast.tsx +208 -0
  334. package/src/hooks/use-typecheck.ts +54 -0
  335. package/src/hooks/use-url-params.ts +31 -0
  336. package/src/hooks/use-warn-user-on-page-change.ts +23 -0
  337. package/src/hooks/useForkSnippetMutation.ts +52 -0
  338. package/src/index.css +21 -0
  339. package/src/lib/__tests__/constants.test.ts +66 -0
  340. package/src/lib/base64ToBytes.ts +5 -0
  341. package/src/lib/bytesToBase64.ts +4 -0
  342. package/src/lib/codemirror/basic-setup.ts +51 -0
  343. package/src/lib/constants.ts +12 -0
  344. package/src/lib/decodeUrlHashToText.ts +15 -0
  345. package/src/lib/defaultCodeForBlankCode.tsx +7 -0
  346. package/src/lib/download-fns/createBlobURL.ts +4 -0
  347. package/src/lib/download-fns/download-assembly-svg.ts +12 -0
  348. package/src/lib/download-fns/download-circuit-json-fn.ts +12 -0
  349. package/src/lib/download-fns/download-dsn-file-fn.ts +12 -0
  350. package/src/lib/download-fns/download-fabrication-files.ts +233 -0
  351. package/src/lib/download-fns/download-gltf.ts +49 -0
  352. package/src/lib/download-fns/download-kicad-files.ts +27 -0
  353. package/src/lib/download-fns/download-readable-netlist.ts +12 -0
  354. package/src/lib/download-fns/download-schematic-svg.ts +12 -0
  355. package/src/lib/download-fns/download-simple-route-json.ts +17 -0
  356. package/src/lib/encodeTextToUrlHash.ts +17 -0
  357. package/src/lib/get-snippet-template.ts +26 -0
  358. package/src/lib/handleManualEditsImport.tsx +65 -0
  359. package/src/lib/jlc-parts-engine.ts +69 -0
  360. package/src/lib/templates/blank-3d-model-template.ts +12 -0
  361. package/src/lib/templates/blank-circuit-board-template.ts +24 -0
  362. package/src/lib/templates/blank-footprint-template.ts +29 -0
  363. package/src/lib/templates/blank-package-template.ts +22 -0
  364. package/src/lib/templates/blinking-led-board-template.ts +63 -0
  365. package/src/lib/templates/manual-edits-template.ts +5 -0
  366. package/src/lib/templates/usb-c-led-flashlight-template.ts +22 -0
  367. package/src/lib/utils/checkIfManualEditsImported.ts +6 -0
  368. package/src/lib/utils/getSyntaxError.ts +13 -0
  369. package/src/lib/utils/index.ts +6 -0
  370. package/src/lib/utils/load-prettier.ts +18 -0
  371. package/src/lib/utils/parseFootprintParams.ts +52 -0
  372. package/src/lib/utils/parseJsonOrNull.ts +8 -0
  373. package/src/lib/utils/pcbManualEditEventHandler.ts +156 -0
  374. package/src/main.tsx +10 -0
  375. package/src/pages/ai.tsx +92 -0
  376. package/src/pages/authorize.tsx +54 -0
  377. package/src/pages/dashboard.tsx +165 -0
  378. package/src/pages/dev-login.tsx +68 -0
  379. package/src/pages/editor.tsx +28 -0
  380. package/src/pages/landing.tsx +236 -0
  381. package/src/pages/my-orders.tsx +54 -0
  382. package/src/pages/newest.tsx +16 -0
  383. package/src/pages/preview.tsx +70 -0
  384. package/src/pages/quickstart.tsx +198 -0
  385. package/src/pages/search.tsx +26 -0
  386. package/src/pages/settings.tsx +25 -0
  387. package/src/pages/user-profile.tsx +97 -0
  388. package/src/pages/view-order.tsx +123 -0
  389. package/src/pages/view-snippet.tsx +149 -0
  390. package/src/prettier.ts +9 -0
  391. package/src/vite-env.d.ts +1 -0
  392. package/tailwind.config.js +47 -0
  393. package/tsconfig.json +30 -0
  394. package/vercel.json +50 -0
  395. package/vite.config.ts +146 -0
  396. package/winterspec.config.ts +6 -0
@@ -0,0 +1,212 @@
1
+ import { Button } from "@/components/ui/button"
2
+ import {
3
+ DropdownMenu,
4
+ DropdownMenuContent,
5
+ DropdownMenuItem,
6
+ DropdownMenuTrigger,
7
+ } from "@/components/ui/dropdown-menu"
8
+ import { toast, useNotImplementedToast } from "@/hooks/use-toast"
9
+ import { downloadCircuitJson } from "@/lib/download-fns/download-circuit-json-fn"
10
+ import { downloadSimpleRouteJson } from "@/lib/download-fns/download-simple-route-json"
11
+ import { downloadDsnFile } from "@/lib/download-fns/download-dsn-file-fn"
12
+ import { downloadFabricationFiles } from "@/lib/download-fns/download-fabrication-files"
13
+ import { downloadSchematicSvg } from "@/lib/download-fns/download-schematic-svg"
14
+ import { downloadReadableNetlist } from "@/lib/download-fns/download-readable-netlist"
15
+ import { downloadAssemblySvg } from "@/lib/download-fns/download-assembly-svg"
16
+ import { downloadKicadFiles } from "@/lib/download-fns/download-kicad-files"
17
+ import { AnyCircuitElement } from "circuit-json"
18
+ import { ChevronDown, Download } from "lucide-react"
19
+ import React from "react"
20
+ import { downloadGltf } from "@/lib/download-fns/download-gltf"
21
+
22
+ interface DownloadButtonAndMenuProps {
23
+ className?: string
24
+ snippetUnscopedName: string | undefined
25
+ circuitJson?: AnyCircuitElement[] | null
26
+ }
27
+
28
+ export function DownloadButtonAndMenu({
29
+ className,
30
+ snippetUnscopedName,
31
+ circuitJson,
32
+ }: DownloadButtonAndMenuProps) {
33
+ const notImplemented = useNotImplementedToast()
34
+
35
+ if (!circuitJson) {
36
+ return (
37
+ <Button disabled variant="ghost" size="sm" className="px-2 text-xs">
38
+ <Download className="mr-1 h-3 w-3" />
39
+ Download
40
+ </Button>
41
+ )
42
+ }
43
+
44
+ return (
45
+ <div className={className}>
46
+ <DropdownMenu>
47
+ <DropdownMenuTrigger>
48
+ <Button variant="ghost" size="sm" className="px-2 text-xs">
49
+ <Download className="mr-1 h-3 w-3" />
50
+ Download
51
+ <ChevronDown className="ml-1 h-3 w-3" />
52
+ </Button>
53
+ </DropdownMenuTrigger>
54
+ <DropdownMenuContent>
55
+ <DropdownMenuItem
56
+ className="text-xs"
57
+ onSelect={() => {
58
+ downloadCircuitJson(
59
+ circuitJson,
60
+ snippetUnscopedName || "circuit" + ".json",
61
+ )
62
+ }}
63
+ >
64
+ <Download className="mr-1 h-3 w-3" />
65
+ <span className="flex-grow mr-6">Circuit JSON</span>
66
+ <span className="text-[0.6rem] opacity-80 bg-blue-500 text-white font-mono rounded-md px-1 text-center py-0.5 mr-1">
67
+ json
68
+ </span>
69
+ </DropdownMenuItem>
70
+ <DropdownMenuItem
71
+ className="text-xs"
72
+ onClick={async () => {
73
+ try {
74
+ await downloadGltf(
75
+ circuitJson,
76
+ snippetUnscopedName || "circuit",
77
+ )
78
+ } catch (error: any) {
79
+ toast({
80
+ title: "Error Downloading 3D Model",
81
+ description: error.toString(),
82
+ })
83
+ }
84
+ }}
85
+ >
86
+ <Download className="mr-1 h-3 w-3" />
87
+ <span className="flex-grow mr-6">3D Model</span>
88
+ <span className="text-[0.6rem] bg-green-500 opacity-80 text-white font-mono rounded-md px-1 text-center py-0.5 mr-1">
89
+ gltf
90
+ </span>
91
+ </DropdownMenuItem>
92
+ <DropdownMenuItem
93
+ className="text-xs"
94
+ onClick={async () => {
95
+ await downloadFabricationFiles({
96
+ circuitJson,
97
+ snippetUnscopedName: snippetUnscopedName || "snippet",
98
+ }).catch((error) => {
99
+ console.error(error)
100
+ toast({
101
+ title: "Error Downloading Fabrication Files",
102
+ description: error.toString(),
103
+ })
104
+ })
105
+ }}
106
+ >
107
+ <Download className="mr-1 h-3 w-3" />
108
+ <span className="flex-grow mr-6">Fabrication Files</span>
109
+ <span className="text-[0.6rem] bg-purple-500 opacity-80 text-white font-mono rounded-md px-1 text-center py-0.5 mr-1">
110
+ gerber/pnp/bom/csv
111
+ </span>
112
+ </DropdownMenuItem>
113
+ <DropdownMenuItem
114
+ className="text-xs"
115
+ onClick={() => notImplemented("kicad footprint download")}
116
+ >
117
+ <Download className="mr-1 h-3 w-3" />
118
+ <span className="flex-grow mr-6">KiCad Footprint</span>
119
+ <span className="text-[0.6rem] bg-orange-500 opacity-80 text-white font-mono rounded-md px-1 text-center py-0.5 mr-1">
120
+ kicad_mod
121
+ </span>
122
+ </DropdownMenuItem>
123
+ <DropdownMenuItem
124
+ className="text-xs"
125
+ onSelect={() => {
126
+ downloadKicadFiles(
127
+ circuitJson,
128
+ snippetUnscopedName || "kicad_project",
129
+ )
130
+ }}
131
+ >
132
+ <Download className="mr-1 h-3 w-3" />
133
+ <span className="flex-grow mr-6">KiCad Project</span>
134
+ <span className="text-[0.6rem] bg-orange-500 opacity-80 text-white font-mono rounded-md px-1 text-center py-0.5 mr-1">
135
+ kicad_*.zip
136
+ </span>
137
+ </DropdownMenuItem>
138
+
139
+ <DropdownMenuItem
140
+ className="text-xs"
141
+ onSelect={() => {
142
+ downloadSchematicSvg(
143
+ circuitJson,
144
+ snippetUnscopedName || "circuit",
145
+ )
146
+ }}
147
+ >
148
+ <Download className="mr-1 h-3 w-3" />
149
+ <span className="flex-grow mr-6">Schematic SVG</span>
150
+ <span className="text-[0.6rem] opacity-80 bg-blue-500 text-white font-mono rounded-md px-1 text-center py-0.5 mr-1">
151
+ svg
152
+ </span>
153
+ </DropdownMenuItem>
154
+ <DropdownMenuItem
155
+ className="text-xs"
156
+ onSelect={() => {
157
+ downloadAssemblySvg(circuitJson, snippetUnscopedName || "circuit")
158
+ }}
159
+ >
160
+ <Download className="mr-1 h-3 w-3" />
161
+ <span className="flex-grow mr-6">Assembly SVG</span>
162
+ <span className="text-[0.6rem] opacity-80 bg-blue-500 text-white font-mono rounded-md px-1 text-center py-0.5 mr-1">
163
+ svg
164
+ </span>
165
+ </DropdownMenuItem>
166
+ <DropdownMenuItem
167
+ className="text-xs"
168
+ onSelect={() => {
169
+ downloadDsnFile(circuitJson, snippetUnscopedName || "circuit")
170
+ }}
171
+ >
172
+ <Download className="mr-1 h-3 w-3" />
173
+ <span className="flex-grow mr-6">Specctra DSN</span>
174
+ <span className="text-[0.6rem] opacity-80 bg-blue-500 text-white font-mono rounded-md px-1 text-center py-0.5 mr-1">
175
+ dsn
176
+ </span>
177
+ </DropdownMenuItem>
178
+ <DropdownMenuItem
179
+ className="text-xs"
180
+ onClick={() => {
181
+ downloadReadableNetlist(
182
+ circuitJson,
183
+ snippetUnscopedName || "circuit",
184
+ )
185
+ }}
186
+ >
187
+ <Download className="mr-1 h-3 w-3" />
188
+ <span className="flex-grow mr-6">Readable Netlist</span>
189
+ <span className="text-[0.6rem] opacity-80 bg-blue-500 text-white font-mono rounded-md px-1 text-center py-0.5 mr-1">
190
+ txt
191
+ </span>
192
+ </DropdownMenuItem>
193
+ <DropdownMenuItem
194
+ className="text-xs"
195
+ onSelect={() => {
196
+ downloadSimpleRouteJson(
197
+ circuitJson,
198
+ snippetUnscopedName || "circuit",
199
+ )
200
+ }}
201
+ >
202
+ <Download className="mr-1 h-3 w-3" />
203
+ <span className="flex-grow mr-6">Simple Route JSON</span>
204
+ <span className="text-[0.6rem] opacity-80 bg-blue-500 text-white font-mono rounded-md px-1 text-center py-0.5 mr-1">
205
+ json
206
+ </span>
207
+ </DropdownMenuItem>
208
+ </DropdownMenuContent>
209
+ </DropdownMenu>
210
+ </div>
211
+ )
212
+ }
@@ -0,0 +1,428 @@
1
+ import { Button } from "@/components/ui/button"
2
+ import { GitFork } from "lucide-react"
3
+ import {
4
+ DropdownMenu,
5
+ DropdownMenuContent,
6
+ DropdownMenuItem,
7
+ DropdownMenuSub,
8
+ DropdownMenuSubContent,
9
+ DropdownMenuSubTrigger,
10
+ DropdownMenuTrigger,
11
+ } from "@/components/ui/dropdown-menu"
12
+ import { useGlobalStore } from "@/hooks/use-global-store"
13
+ import { encodeTextToUrlHash } from "@/lib/encodeTextToUrlHash"
14
+ import { cn } from "@/lib/utils"
15
+ import { OpenInNewWindowIcon } from "@radix-ui/react-icons"
16
+ import { AnyCircuitElement } from "circuit-json"
17
+ import { Snippet } from "fake-snippets-api/lib/db/schema"
18
+ import {
19
+ ChevronDown,
20
+ CodeIcon,
21
+ Download,
22
+ Edit2,
23
+ Eye,
24
+ EyeIcon,
25
+ File,
26
+ FilePenLine,
27
+ MoreVertical,
28
+ Package,
29
+ Pencil,
30
+ Save,
31
+ Share,
32
+ Sidebar,
33
+ Sparkles,
34
+ Trash2,
35
+ } from "lucide-react"
36
+ import { useEffect, useState } from "react"
37
+ import { useMutation, useQueryClient } from "react-query"
38
+ import { Link, useLocation } from "wouter"
39
+ import { useAxios } from "../hooks/use-axios"
40
+ import { useToast } from "../hooks/use-toast"
41
+ import { useConfirmDeleteSnippetDialog } from "./dialogs/confirm-delete-snippet-dialog"
42
+ import { useCreateOrderDialog } from "./dialogs/create-order-dialog"
43
+ import { useFilesDialog } from "./dialogs/files-dialog"
44
+ import { useViewTsFilesDialog } from "./dialogs/view-ts-files-dialog"
45
+ import { useRenameSnippetDialog } from "./dialogs/rename-snippet-dialog"
46
+ import { DownloadButtonAndMenu } from "./DownloadButtonAndMenu"
47
+ import { SnippetLink } from "./SnippetLink"
48
+ import { TypeBadge } from "./TypeBadge"
49
+ import { useUpdateDescriptionDialog } from "./dialogs/edit-description-dialog"
50
+ import { useForkSnippetMutation } from "@/hooks/useForkSnippetMutation"
51
+
52
+ export default function EditorNav({
53
+ circuitJson,
54
+ snippet,
55
+ code,
56
+ hasUnsavedChanges,
57
+ onTogglePreview,
58
+ previewOpen,
59
+ onSave,
60
+ snippetType,
61
+ isSaving,
62
+ canSave,
63
+ }: {
64
+ snippet?: Snippet | null
65
+ circuitJson?: AnyCircuitElement[] | null
66
+ code: string
67
+ snippetType?: string
68
+ hasUnsavedChanges: boolean
69
+ previewOpen: boolean
70
+ onTogglePreview: () => void
71
+ isSaving: boolean
72
+ onSave: () => void
73
+ canSave: boolean
74
+ }) {
75
+ const [, navigate] = useLocation()
76
+ const isLoggedIn = useGlobalStore((s) => Boolean(s.session))
77
+ const session = useGlobalStore((s) => s.session)
78
+ const { Dialog: RenameDialog, openDialog: openRenameDialog } =
79
+ useRenameSnippetDialog()
80
+ const {
81
+ Dialog: UpdateDescriptionDialog,
82
+ openDialog: openupdateDescriptionDialog,
83
+ } = useUpdateDescriptionDialog()
84
+ const { Dialog: DeleteDialog, openDialog: openDeleteDialog } =
85
+ useConfirmDeleteSnippetDialog()
86
+ const { Dialog: CreateOrderDialog, openDialog: openCreateOrderDialog } =
87
+ useCreateOrderDialog()
88
+ const { Dialog: FilesDialog, openDialog: openFilesDialog } = useFilesDialog()
89
+ const { Dialog: ViewTsFilesDialog, openDialog: openViewTsFilesDialog } =
90
+ useViewTsFilesDialog()
91
+
92
+ const [isChangingType, setIsChangingType] = useState(false)
93
+ const [currentType, setCurrentType] = useState(
94
+ snippetType ?? snippet?.snippet_type,
95
+ )
96
+ const axios = useAxios()
97
+ const { toast } = useToast()
98
+ const qc = useQueryClient()
99
+
100
+ const { mutate: forkSnippet, isLoading: isForking } = useForkSnippetMutation({
101
+ snippet: snippet!,
102
+ currentCode: code,
103
+ onSuccess: (forkedSnippet) => {
104
+ navigate("/editor?snippet_id=" + forkedSnippet.snippet_id)
105
+ setTimeout(() => {
106
+ window.location.reload() //reload the page
107
+ }, 2000)
108
+ },
109
+ })
110
+
111
+ // Update currentType when snippet or snippetType changes
112
+ useEffect(() => {
113
+ setCurrentType(snippetType ?? snippet?.snippet_type)
114
+ }, [snippetType, snippet?.snippet_type])
115
+
116
+ const handleTypeChange = async (newType: string) => {
117
+ if (!snippet || newType === currentType) return
118
+
119
+ try {
120
+ setIsChangingType(true)
121
+
122
+ const response = await axios.post("/snippets/update", {
123
+ snippet_id: snippet.snippet_id,
124
+ snippet_type: newType,
125
+ })
126
+
127
+ if (response.status === 200) {
128
+ setCurrentType(newType)
129
+ toast({
130
+ title: "Snippet type changed",
131
+ description: `Successfully changed type to "${newType}"`,
132
+ })
133
+
134
+ // Invalidate queries to refetch data
135
+ await Promise.all([
136
+ qc.invalidateQueries({ queryKey: ["snippets"] }),
137
+ qc.invalidateQueries({ queryKey: ["snippets", snippet.snippet_id] }),
138
+ ])
139
+
140
+ // Reload the page to ensure all components reflect the new type
141
+ // window.location.reload()
142
+ } else {
143
+ throw new Error("Failed to update snippet type")
144
+ }
145
+ } catch (error: any) {
146
+ console.error("Error changing snippet type:", error)
147
+ toast({
148
+ title: "Error",
149
+ description:
150
+ error.response?.data?.error?.message ||
151
+ "Failed to change the snippet type. Please try again.",
152
+ variant: "destructive",
153
+ })
154
+ // Reset to previous type on error
155
+ setCurrentType(snippet.snippet_type)
156
+ } finally {
157
+ setIsChangingType(false)
158
+ }
159
+ }
160
+
161
+ const canSaveSnippet =
162
+ !snippet || snippet.owner_name === session?.github_username
163
+
164
+ return (
165
+ <nav className="lg:flex w-screen items-center justify-between px-2 py-3 border-b border-gray-200 bg-white text-sm border-t">
166
+ <div className="lg:flex items-center my-2 ">
167
+ <div className="flex items-center space-x-1">
168
+ {snippet && (
169
+ <>
170
+ <SnippetLink snippet={snippet} />
171
+ <Button
172
+ variant="ghost"
173
+ size="icon"
174
+ className="h-6 w-6 ml-2"
175
+ onClick={() => openRenameDialog()}
176
+ >
177
+ <Pencil className="h-3 w-3 text-gray-700" />
178
+ </Button>
179
+ <Link href={`/${snippet.name}`}>
180
+ <Button variant="ghost" size="icon" className="h-6 w-6">
181
+ <OpenInNewWindowIcon className="h-3 w-3 text-gray-700" />
182
+ </Button>
183
+ </Link>
184
+ </>
185
+ )}
186
+ </div>
187
+ <div className="flex items-center space-x-1">
188
+ {!isLoggedIn && (
189
+ <div className="bg-orange-100 text-orange-700 py-1 px-2 text-xs opacity-70">
190
+ Not logged in, can't save
191
+ </div>
192
+ )}
193
+ <Button
194
+ variant="outline"
195
+ size="sm"
196
+ className={"ml-1 h-6 px-2 text-xs save-button"}
197
+ disabled={!isLoggedIn}
198
+ onClick={canSaveSnippet ? onSave : () => forkSnippet()}
199
+ >
200
+ {canSaveSnippet ? (
201
+ <>
202
+ <Save className="mr-1 h-3 w-3" />
203
+ Save
204
+ </>
205
+ ) : (
206
+ <>
207
+ <GitFork className="mr-1 h-3 w-3" />
208
+ Fork
209
+ </>
210
+ )}
211
+ </Button>
212
+ {isSaving && (
213
+ <div className="animate-fadeIn bg-blue-100 text-blue-800 text-xs font-medium px-2.5 py-0.5 rounded flex items-center">
214
+ <svg
215
+ className="animate-spin h-3 w-3 mr-2 text-blue-600"
216
+ xmlns="http://www.w3.org/2000/svg"
217
+ fill="none"
218
+ viewBox="0 0 24 24"
219
+ >
220
+ <circle
221
+ className="opacity-25"
222
+ cx="12"
223
+ cy="12"
224
+ r="10"
225
+ stroke="currentColor"
226
+ strokeWidth="4"
227
+ ></circle>
228
+ <path
229
+ className="opacity-75"
230
+ fill="currentColor"
231
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
232
+ ></path>
233
+ </svg>
234
+ Saving...
235
+ </div>
236
+ )}
237
+ {hasUnsavedChanges && !isSaving && isLoggedIn && (
238
+ <div className="animate-fadeIn bg-yellow-100 text-yellow-800 text-xs font-medium px-2.5 py-0.5 rounded">
239
+ {snippet ? "unsaved changes" : "unsaved"}
240
+ </div>
241
+ )}
242
+ </div>
243
+ </div>
244
+ <div className="flex items-center justify-end -space-x-1">
245
+ <div className="flex mx-2 items-center space-x-1">
246
+ {snippet && <TypeBadge type={snippetType ?? snippet.snippet_type} />}
247
+ <Button
248
+ variant="ghost"
249
+ size="sm"
250
+ disabled={hasUnsavedChanges || isSaving || !snippet}
251
+ onClick={() => navigate(`/ai?snippet_id=${snippet!.snippet_id}`)}
252
+ >
253
+ <Sparkles className="mr-1 h-3 w-3" />
254
+ Edit with AI
255
+ </Button>
256
+ <DownloadButtonAndMenu
257
+ snippetUnscopedName={snippet?.unscoped_name}
258
+ circuitJson={circuitJson}
259
+ className="hidden md:flex"
260
+ />
261
+ <Button
262
+ variant="ghost"
263
+ size="sm"
264
+ className="hidden md:flex px-2 text-xs"
265
+ onClick={() => {
266
+ const url = encodeTextToUrlHash(code, snippetType)
267
+ navigator.clipboard.writeText(url)
268
+ alert("URL copied to clipboard!")
269
+ }}
270
+ >
271
+ <Share className="mr-1 h-3 w-3" />
272
+ Copy URL
273
+ </Button>
274
+ <Button
275
+ variant="ghost"
276
+ size="sm"
277
+ className="hidden md:flex px-2 text-xs"
278
+ >
279
+ <Eye className="mr-1 h-3 w-3" />
280
+ Public
281
+ </Button>
282
+ <DropdownMenu>
283
+ <DropdownMenuTrigger asChild>
284
+ <Button variant="ghost" size="icon" className="hidden md:flex">
285
+ <MoreVertical className="h-3 w-3" />
286
+ </Button>
287
+ </DropdownMenuTrigger>
288
+ <DropdownMenuContent>
289
+ <DropdownMenuItem
290
+ className="text-xs"
291
+ onClick={() => openCreateOrderDialog()}
292
+ >
293
+ <Package className="mr-2 h-3 w-3" />
294
+ Submit Order
295
+ </DropdownMenuItem>
296
+ <DropdownMenuItem
297
+ className="text-xs"
298
+ onClick={() => openFilesDialog()}
299
+ >
300
+ <File className="mr-2 h-3 w-3" />
301
+ View Files
302
+ </DropdownMenuItem>
303
+ <DropdownMenuItem
304
+ className="text-xs"
305
+ onClick={() => openupdateDescriptionDialog()}
306
+ >
307
+ <FilePenLine className="mr-2 h-3 w-3" />
308
+ Edit Description
309
+ </DropdownMenuItem>
310
+ <DropdownMenuItem
311
+ className="text-xs"
312
+ onClick={() => openViewTsFilesDialog()}
313
+ >
314
+ <File className="mr-2 h-3 w-3" />
315
+ [Debug] View TS Files
316
+ </DropdownMenuItem>
317
+ <DropdownMenuSub>
318
+ <DropdownMenuSubTrigger
319
+ className="text-xs"
320
+ disabled={isChangingType || hasUnsavedChanges}
321
+ >
322
+ <Edit2 className="mr-2 h-3 w-3" />
323
+ {isChangingType ? "Changing..." : "Change Type"}
324
+ </DropdownMenuSubTrigger>
325
+ <DropdownMenuSubContent>
326
+ <DropdownMenuItem
327
+ className="text-xs"
328
+ disabled={currentType === "board" || isChangingType}
329
+ onClick={() => handleTypeChange("board")}
330
+ >
331
+ Board {currentType === "board" && "✓"}
332
+ </DropdownMenuItem>
333
+ <DropdownMenuItem
334
+ className="text-xs"
335
+ disabled={currentType === "package" || isChangingType}
336
+ onClick={() => handleTypeChange("package")}
337
+ >
338
+ Module {currentType === "package" && "✓"}
339
+ </DropdownMenuItem>
340
+ </DropdownMenuSubContent>
341
+ </DropdownMenuSub>
342
+ <DropdownMenuItem
343
+ className="text-xs text-red-600"
344
+ onClick={() => openDeleteDialog()}
345
+ >
346
+ <Trash2 className="mr-2 h-3 w-3" />
347
+ Delete Snippet
348
+ </DropdownMenuItem>
349
+ </DropdownMenuContent>
350
+ </DropdownMenu>
351
+ <Button
352
+ variant="ghost"
353
+ size="icon"
354
+ className={cn(
355
+ "hidden md:flex",
356
+ !previewOpen
357
+ ? "bg-blue-600 text-white hover:bg-blue-700 hover:text-white"
358
+ : "",
359
+ )}
360
+ onClick={() => onTogglePreview()}
361
+ >
362
+ {previewOpen ? (
363
+ <Sidebar className="h-3 w-3" />
364
+ ) : (
365
+ <EyeIcon className="h-3 w-3" />
366
+ )}
367
+ </Button>
368
+ </div>
369
+ <div className="flex items-center ">
370
+ <DropdownMenu>
371
+ <DropdownMenuTrigger>
372
+ <Button className="md:hidden" variant="secondary" size="sm">
373
+ <ChevronDown className="h-4 w-4" />
374
+ </Button>
375
+ </DropdownMenuTrigger>
376
+ <DropdownMenuContent>
377
+ <DropdownMenuItem className="text-xs">
378
+ <Download className="mr-1 h-3 w-3" />
379
+ Download
380
+ </DropdownMenuItem>
381
+ <DropdownMenuItem className="text-xs">
382
+ <Share className="mr-1 h-3 w-3" />
383
+ Copy URL
384
+ </DropdownMenuItem>
385
+ <DropdownMenuItem className="text-xs">
386
+ <Eye className="mr-1 h-3 w-3" />
387
+ Public
388
+ </DropdownMenuItem>
389
+ </DropdownMenuContent>
390
+ </DropdownMenu>
391
+ <Button
392
+ variant="ghost"
393
+ size="sm"
394
+ className="md:hidden"
395
+ onClick={() => onTogglePreview()}
396
+ >
397
+ {previewOpen ? (
398
+ <div className="flex items-center">
399
+ <CodeIcon className="h-3 w-3 mr-1" />
400
+ Show Code
401
+ </div>
402
+ ) : (
403
+ <div className="flex items-center">
404
+ <EyeIcon className="h-3 w-3 mr-1" />
405
+ Show Preview
406
+ </div>
407
+ )}
408
+ </Button>
409
+ </div>
410
+ </div>
411
+ <UpdateDescriptionDialog
412
+ snippetId={snippet?.snippet_id ?? ""}
413
+ currentDescription={snippet?.description ?? ""}
414
+ />
415
+ <RenameDialog
416
+ snippetId={snippet?.snippet_id ?? ""}
417
+ currentName={snippet?.unscoped_name ?? ""}
418
+ />
419
+ <DeleteDialog
420
+ snippetId={snippet?.snippet_id ?? ""}
421
+ snippetName={snippet?.unscoped_name ?? ""}
422
+ />
423
+ <CreateOrderDialog />
424
+ <FilesDialog snippetId={snippet?.snippet_id ?? ""} />
425
+ <ViewTsFilesDialog />
426
+ </nav>
427
+ )
428
+ }
@@ -0,0 +1,25 @@
1
+ import React from "react"
2
+
3
+ export const ErrorFallback = ({ error }: { error: Error }) => {
4
+ return (
5
+ <div
6
+ data-testid="error-container"
7
+ className="error-container mt-4 bg-red-50 rounded-md border border-red-200"
8
+ >
9
+ <div className="p-4">
10
+ <h2 className="text-lg font-semibold text-red-800 mb-3">
11
+ Error Loading 3D Viewer
12
+ </h2>
13
+ <p className="text-xs font-mono whitespace-pre-wrap text-red-700">
14
+ {error.message}
15
+ </p>
16
+ <details
17
+ style={{ whiteSpace: "pre-wrap" }}
18
+ className="text-xs font-mono text-red-600 mt-2"
19
+ >
20
+ {error.stack}
21
+ </details>
22
+ </div>
23
+ </div>
24
+ )
25
+ }