@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,5 @@
1
+ export default {
2
+ pcb_placements: [],
3
+ edit_events: [],
4
+ manual_trace_hints: [],
5
+ }
@@ -0,0 +1,22 @@
1
+ export const usbCLedFlashlightTemplate = {
2
+ type: "board",
3
+ code: `
4
+ import { useRedLed } from "@tsci/seveibar.red-led"
5
+ import { usePushButton } from "@tsci/seveibar.push-button"
6
+ import { useUsbC } from "@tsci/seveibar.smd-usb-c"
7
+
8
+ export default () => {
9
+ const USBC = useUsbC("USBC")
10
+ const Button = usePushButton("SW1")
11
+ const Led = useRedLed("LED")
12
+ return (
13
+ <board width="12mm" height="30mm" schAutoLayoutEnabled autorouter="auto-cloud">
14
+ <USBC GND1="net.GND" GND2="net.GND" pcbY={-10} VBUS1="net.VBUS" VBUS2="net.VBUS" />
15
+ <Led neg="net.GND" pcbY={12} />
16
+ <Button pcbY={0} pin2=".R1 > .pos" pin3="net.VBUS" />
17
+ <resistor name="R1" footprint="0603" resistance="1k" pcbY={7} />
18
+ <trace from=".R1 > .neg" to={Led.pos} />
19
+ </board>
20
+ )
21
+ }`.trim(),
22
+ }
@@ -0,0 +1,6 @@
1
+ export const checkIfManualEditsImported = (files: Record<string, string>) => {
2
+ const importRegex =
3
+ /import\s+(?:\*\s+as\s+)?([a-zA-Z_$][\w$]*)\s+from\s+["']\.\/manual-edits\.json["'];?/
4
+ const hasManualEditsImported = importRegex.test(files["index.tsx"])
5
+ return files["manual-edits.json"].length > 0 && !hasManualEditsImported
6
+ }
@@ -0,0 +1,13 @@
1
+ import * as Babel from "@babel/standalone"
2
+
3
+ export function getSyntaxError(code: string): string | null {
4
+ try {
5
+ Babel.transform(code, {
6
+ filename: "index.tsx",
7
+ presets: ["react", "typescript"],
8
+ })
9
+ return null
10
+ } catch (error: unknown) {
11
+ return (error as Error).message
12
+ }
13
+ }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
@@ -0,0 +1,18 @@
1
+ export async function loadPrettier() {
2
+ if (window.prettier) return
3
+
4
+ await Promise.all([
5
+ loadScript("https://unpkg.com/prettier@2.8.8/standalone.js"),
6
+ loadScript("https://unpkg.com/prettier@2.8.8/parser-typescript.js"),
7
+ ])
8
+ }
9
+
10
+ function loadScript(src: string): Promise<void> {
11
+ return new Promise((resolve, reject) => {
12
+ const script = document.createElement("script")
13
+ script.src = src
14
+ script.onload = () => resolve()
15
+ script.onerror = reject
16
+ document.head.appendChild(script)
17
+ })
18
+ }
@@ -0,0 +1,52 @@
1
+ export interface FootprintParams {
2
+ [key: string]: string | number | boolean | string[]
3
+ }
4
+
5
+ /**
6
+ * Parses and normalizes footprint parameters
7
+ * @param params Raw parameters from footprint
8
+ * @returns Normalized parameters with proper formatting
9
+ */
10
+ export function parseFootprintParams(params: FootprintParams): FootprintParams {
11
+ if (params.grid) {
12
+ const grid = params.grid
13
+ if (typeof grid === "object" && grid !== null) {
14
+ const { x, y } = grid as any
15
+ params.grid = `${x}x${y}`
16
+ } else if (typeof grid === "string") {
17
+ const gridMatch = grid.match(/^(\d+)(?:x(\d+)?)?$/)
18
+ if (gridMatch) {
19
+ const [, x, y = x] = gridMatch
20
+ params.grid = `${x}x${y}`
21
+ }
22
+ } else if (typeof grid === "number") {
23
+ params.grid = `${grid}x${grid}`
24
+ }
25
+ delete params.grid3x3
26
+ }
27
+
28
+ if ("missing" in params && typeof params.missing === "string") {
29
+ const value = params.missing
30
+ if (value === "") {
31
+ params.missing = []
32
+ } else if (!Array.isArray(value)) {
33
+ if (value.startsWith("missing(") && value.endsWith(")")) {
34
+ const pinsStr = value.slice(8, -1)
35
+ params.missing = pinsStr ? pinsStr.split(",").map((p) => p.trim()) : []
36
+ } else {
37
+ params.missing = value
38
+ .split(",")
39
+ .map((p) => p.trim())
40
+ .filter(Boolean)
41
+ }
42
+ }
43
+ }
44
+
45
+ Object.entries(params).forEach(([key, value]) => {
46
+ if (typeof value === "string" && !isNaN(Number(value)) && key !== "grid") {
47
+ params[key] = Number(Number(value).toFixed(2))
48
+ }
49
+ })
50
+
51
+ return params
52
+ }
@@ -0,0 +1,8 @@
1
+ export const parseJsonOrNull = (string: string | null): any => {
2
+ if (!string) return null
3
+ try {
4
+ return JSON.parse(string)
5
+ } catch {
6
+ return null
7
+ }
8
+ }
@@ -0,0 +1,156 @@
1
+ import type { ManualTraceHint } from "@tscircuit/layout"
2
+ import { getManualTraceHintFromEvent } from "@tscircuit/layout"
3
+ import type { EditEvent } from "@tscircuit/manual-edit-events"
4
+ import type {
5
+ AnyCircuitElement,
6
+ PcbComponent,
7
+ SourceComponentBase,
8
+ } from "circuit-json"
9
+
10
+ export interface PCBPlacement {
11
+ selector: string
12
+ center: { x: number; y: number }
13
+ relative_to: "group_center"
14
+ _edit_event_id?: string
15
+ }
16
+
17
+ export interface ManualEditState {
18
+ pcb_placements: PCBPlacement[]
19
+ edit_events: EditEvent[]
20
+ manual_trace_hints: ManualTraceHint[]
21
+ }
22
+
23
+ export const createInitialManualEditState = (): ManualEditState => ({
24
+ pcb_placements: [],
25
+ edit_events: [],
26
+ manual_trace_hints: [],
27
+ })
28
+
29
+ export const applyPcbEditEvents = ({
30
+ editEvents,
31
+ circuitJson,
32
+ manualEditsFileContent,
33
+ }: {
34
+ editEvents: EditEvent[]
35
+ circuitJson: AnyCircuitElement[]
36
+ manualEditsFileContent?: string
37
+ }): ManualEditState => {
38
+ try {
39
+ // Ensure we have a valid state to work with
40
+ const validatedManualEdits = ensureValidState(manualEditsFileContent)
41
+
42
+ // Create a new state object with properly initialized arrays
43
+ const newManualEditState: ManualEditState = {
44
+ pcb_placements: [...validatedManualEdits.pcb_placements],
45
+ edit_events: [...validatedManualEdits.edit_events],
46
+ manual_trace_hints: [...validatedManualEdits.manual_trace_hints],
47
+ }
48
+
49
+ // Create a set of handled event IDs
50
+ const handledEventIds = new Set<string>()
51
+
52
+ // Add existing event IDs to the set
53
+ newManualEditState.pcb_placements.forEach((placement) => {
54
+ if (placement._edit_event_id) {
55
+ handledEventIds.add(placement._edit_event_id)
56
+ }
57
+ })
58
+
59
+ newManualEditState.edit_events.forEach((event) => {
60
+ if (event.edit_event_id) {
61
+ handledEventIds.add(event.edit_event_id)
62
+ }
63
+ })
64
+
65
+ // Process new edit events
66
+ for (const editEvent of editEvents) {
67
+ if (
68
+ (editEvent.in_progress && !editEvent.edit_event_id) ||
69
+ handledEventIds.has(editEvent.edit_event_id)
70
+ )
71
+ continue
72
+
73
+ if (
74
+ editEvent.pcb_edit_event_type === "edit_component_location" &&
75
+ editEvent.pcb_component_id
76
+ ) {
77
+ // Find the component in the circuitJson
78
+ const pcbComponent = circuitJson.find(
79
+ (item: AnyCircuitElement) =>
80
+ item.type === "pcb_component" &&
81
+ item.pcb_component_id === editEvent.pcb_component_id,
82
+ ) as PcbComponent
83
+
84
+ if (!pcbComponent?.pcb_component_id) continue
85
+
86
+ const nameofComponent = circuitJson.find(
87
+ (item: AnyCircuitElement) =>
88
+ item.type === "source_component" &&
89
+ item.source_component_id === pcbComponent.source_component_id,
90
+ ) as SourceComponentBase
91
+
92
+ // Update or add placement
93
+ const existingPlacementIndex =
94
+ newManualEditState.pcb_placements.findIndex(
95
+ (p) => p.selector === nameofComponent.name,
96
+ )
97
+
98
+ const newPlacement: PCBPlacement = {
99
+ selector: nameofComponent.name,
100
+ center: editEvent.new_center,
101
+ relative_to: "group_center",
102
+ _edit_event_id: editEvent.edit_event_id,
103
+ }
104
+
105
+ if (existingPlacementIndex !== -1) {
106
+ // Update existing placement
107
+ newManualEditState.pcb_placements[existingPlacementIndex] =
108
+ newPlacement
109
+ } else {
110
+ // Add new placement
111
+ newManualEditState.pcb_placements.push(newPlacement)
112
+ }
113
+ } else if (editEvent.pcb_edit_event_type === "edit_trace_hint") {
114
+ const newTraceHint = getManualTraceHintFromEvent(circuitJson, editEvent)
115
+ if (newTraceHint) {
116
+ newManualEditState.manual_trace_hints = [
117
+ ...newManualEditState.manual_trace_hints.filter(
118
+ (th) => th.pcb_port_selector !== newTraceHint.pcb_port_selector,
119
+ ),
120
+ newTraceHint,
121
+ ]
122
+ }
123
+ } else {
124
+ // Add any other type of event to edit_events array
125
+ newManualEditState.edit_events.push(editEvent)
126
+ }
127
+
128
+ handledEventIds.add(editEvent.edit_event_id)
129
+ }
130
+
131
+ return newManualEditState
132
+ } catch (error) {
133
+ console.error("Error handling edit events:", error)
134
+ // Return a fresh state if there's an error
135
+ return createInitialManualEditState()
136
+ }
137
+ }
138
+
139
+ // Helper function to ensure we have a valid state
140
+ const ensureValidState = (manualEditsFileContent?: string): ManualEditState => {
141
+ if (!manualEditsFileContent) return createInitialManualEditState()
142
+
143
+ const manualEditState = JSON.parse(manualEditsFileContent)
144
+
145
+ return {
146
+ pcb_placements: Array.isArray(manualEditState.pcb_placements)
147
+ ? manualEditState.pcb_placements
148
+ : [],
149
+ edit_events: Array.isArray(manualEditState.edit_events)
150
+ ? manualEditState.edit_events
151
+ : [],
152
+ manual_trace_hints: Array.isArray(manualEditState.manual_trace_hints)
153
+ ? manualEditState.manual_trace_hints
154
+ : [],
155
+ }
156
+ }
package/src/main.tsx ADDED
@@ -0,0 +1,10 @@
1
+ import { StrictMode } from "react"
2
+ import { createRoot } from "react-dom/client"
3
+ import App from "./App.tsx"
4
+ import "./index.css"
5
+
6
+ createRoot(document.getElementById("root")!).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>,
10
+ )
@@ -0,0 +1,92 @@
1
+ import AIChatInterface from "@/components/AiChatInterface"
2
+ import Header from "@/components/Header"
3
+ import { PreviewContent } from "@/components/PreviewContent"
4
+ import { useRunTsx } from "@/hooks/use-run-tsx"
5
+ import { useSaveSnippet } from "@/hooks/use-save-snippet"
6
+ import { useSnippet } from "@/hooks/use-snippet"
7
+ import { useToast } from "@/hooks/use-toast"
8
+ import { useEffect, useState } from "react"
9
+ import { useLocation } from "wouter"
10
+
11
+ export const AiPage = () => {
12
+ const [code, setCode] = useState("")
13
+ const [manualEditsFileContent, setManualEditsFileContent] = useState("")
14
+ const [dts, setDts] = useState("")
15
+ const [isStreaming, setIsStreaming] = useState(false)
16
+ const { saveSnippet, isLoading: isSaving } = useSaveSnippet()
17
+ const snippetIdFromUrl = new URLSearchParams(window.location.search).get(
18
+ "snippet_id",
19
+ )
20
+ const [snippetId, setSnippetId] = useState<string | null>(snippetIdFromUrl)
21
+ const { data: snippet } = useSnippet(snippetId)
22
+ const { toast } = useToast()
23
+ const [, navigate] = useLocation()
24
+ const {
25
+ message: errorMessage,
26
+ circuitJson,
27
+ triggerRunTsx,
28
+ isRunningCode,
29
+ tsxRunTriggerCount,
30
+ circuitJsonKey,
31
+ } = useRunTsx({
32
+ code,
33
+ type: "board",
34
+ isStreaming,
35
+ circuitDisplayName: snippet?.name,
36
+ })
37
+
38
+ useEffect(() => {
39
+ if (!code && snippet && snippetIdFromUrl) {
40
+ setCode(snippet.code)
41
+ }
42
+ }, [code, snippet])
43
+
44
+ const hasUnsavedChanges = snippet?.code !== code
45
+
46
+ return (
47
+ <div>
48
+ <Header />
49
+ <div className="flex bg-gray-100">
50
+ <div className="w-1/2">
51
+ <AIChatInterface
52
+ code={code}
53
+ hasUnsavedChanges={hasUnsavedChanges}
54
+ snippetId={snippet?.snippet_id}
55
+ onCodeChange={setCode}
56
+ errorMessage={errorMessage}
57
+ onStartStreaming={() => {
58
+ setIsStreaming(true)
59
+ }}
60
+ onStopStreaming={() => {
61
+ setIsStreaming(false)
62
+ }}
63
+ />
64
+ </div>
65
+ <div className="w-1/2">
66
+ <div className="p-4 h-full">
67
+ <PreviewContent
68
+ className="bg-white h-full p-4 rounded-lg shadow"
69
+ code={code}
70
+ isStreaming={isStreaming}
71
+ isRunningCode={isRunningCode}
72
+ onCodeChange={setCode}
73
+ onDtsChange={setDts}
74
+ showCodeTab
75
+ triggerRunTsx={triggerRunTsx}
76
+ errorMessage={errorMessage}
77
+ circuitJson={circuitJson}
78
+ circuitJsonKey={circuitJsonKey}
79
+ manualEditsFileContent={manualEditsFileContent}
80
+ onManualEditsFileContentChange={(
81
+ newManualEditsFileContent: string,
82
+ ) => {
83
+ setManualEditsFileContent(newManualEditsFileContent)
84
+ }}
85
+ tsxRunTriggerCount={tsxRunTriggerCount}
86
+ />
87
+ </div>
88
+ </div>
89
+ </div>
90
+ </div>
91
+ )
92
+ }
@@ -0,0 +1,54 @@
1
+ "use client"
2
+
3
+ import Header from "@/components/Header"
4
+ import { useGlobalStore } from "@/hooks/use-global-store"
5
+ import { useEffect, useState } from "react"
6
+ import * as jose from "jose"
7
+ import Footer from "@/components/Footer"
8
+ import { useLocation } from "wouter"
9
+
10
+ const AuthenticatePageInnerContent = () => {
11
+ const [location, setLocation] = useLocation()
12
+ const setSession = useGlobalStore((s) => s.setSession)
13
+ const [message, setMessage] = useState("logging you in...")
14
+ const searchParams = new URLSearchParams(window.location.search.split("?")[1])
15
+ const session_token = searchParams.get("session_token")
16
+ useEffect(() => {
17
+ async function login() {
18
+ if (!session_token) {
19
+ setMessage("couldn't log in - no token")
20
+ return
21
+ }
22
+
23
+ if (session_token) {
24
+ const decodedToken = jose.decodeJwt(session_token)
25
+ setSession({
26
+ ...(decodedToken as any),
27
+ token: session_token,
28
+ })
29
+ setLocation("/")
30
+ return
31
+ }
32
+ }
33
+ login().catch((e) => {
34
+ setMessage(`error logging you in\n\n${e.toString()}`)
35
+ })
36
+ }, [session_token])
37
+
38
+ return (
39
+ <div className="bg-white p-8 min-h-screen">
40
+ <div>Authentication Redirect</div>
41
+ <pre>{message}</pre>
42
+ </div>
43
+ )
44
+ }
45
+
46
+ export const AuthorizePage = () => (
47
+ <div>
48
+ <Header />
49
+ <AuthenticatePageInnerContent />
50
+ <Footer />
51
+ </div>
52
+ )
53
+
54
+ export default AuthorizePage
@@ -0,0 +1,165 @@
1
+ import React from "react"
2
+ import { useQuery } from "react-query"
3
+ import { useAxios } from "@/hooks/use-axios"
4
+ import Header from "@/components/Header"
5
+ import Footer from "@/components/Footer"
6
+ import { Snippet } from "fake-snippets-api/lib/db/schema"
7
+ import { Link } from "wouter"
8
+ import { CreateNewSnippetWithAiHero } from "@/components/CreateNewSnippetWithAiHero"
9
+ import { Edit2, Star } from "lucide-react"
10
+ import { Button } from "@/components/ui/button"
11
+ import { useGlobalStore } from "@/hooks/use-global-store"
12
+ import { PrefetchPageLink } from "@/components/PrefetchPageLink"
13
+
14
+ export const DashboardPage = () => {
15
+ const axios = useAxios()
16
+
17
+ const currentUser = useGlobalStore((s) => s.session?.github_username)
18
+
19
+ const {
20
+ data: mySnippets,
21
+ isLoading,
22
+ error,
23
+ } = useQuery<Snippet[]>("userSnippets", async () => {
24
+ const response = await axios.get(`/snippets/list?owner_name=${currentUser}`)
25
+ return response.data.snippets.sort(
26
+ (a: any, b: any) =>
27
+ new Date(b.updated_at || b.created_at).getTime() -
28
+ new Date(a.updated_at || a.created_at).getTime(),
29
+ )
30
+ })
31
+
32
+ const { data: trendingSnippets } = useQuery<Snippet[]>(
33
+ "trendingSnippets",
34
+ async () => {
35
+ const response = await axios.get("/snippets/list_trending")
36
+ return response.data.snippets
37
+ },
38
+ )
39
+
40
+ const { data: newestSnippets } = useQuery<Snippet[]>(
41
+ "newestSnippets",
42
+ async () => {
43
+ const response = await axios.get("/snippets/list_newest")
44
+ return response.data.snippets
45
+ },
46
+ )
47
+
48
+ return (
49
+ <div>
50
+ <Header />
51
+ <div className="container mx-auto px-4 py-8">
52
+ <h1 className="text-3xl font-bold mb-6">Dashboard</h1>
53
+ <div className="flex md:flex-row flex-col">
54
+ <div className="md:w-3/4 p-0 md:pr-6">
55
+ <div className="mt-6 mb-4">
56
+ <div className="flex items-center">
57
+ <h2 className="text-sm text-gray-600 whitespace-nowrap">
58
+ Edit Recent
59
+ </h2>
60
+ <div className="flex gap-2 items-center overflow-x-scroll md:overflow-hidden ">
61
+ {mySnippets &&
62
+ mySnippets.slice(0, 3).map((snippet) => (
63
+ <div key={snippet.snippet_id}>
64
+ <PrefetchPageLink
65
+ href={`/editor?snippet_id=${snippet.snippet_id}`}
66
+ className="text-blue-600 hover:underline"
67
+ >
68
+ <Button
69
+ variant="ghost"
70
+ size="sm"
71
+ className="font-medium"
72
+ >
73
+ {snippet.unscoped_name}
74
+ <Edit2 className="w-3 h-3 ml-2" />
75
+ </Button>
76
+ </PrefetchPageLink>
77
+ </div>
78
+ ))}
79
+ </div>
80
+ </div>
81
+ </div>
82
+ <CreateNewSnippetWithAiHero />
83
+ <h2 className="text-sm font-bold mb-2 text-gray-700 border-b border-gray-200">
84
+ Your Recent Snippets
85
+ </h2>
86
+ {isLoading && <div>Loading...</div>}
87
+ {mySnippets && (
88
+ <ul className="space-y-1">
89
+ {mySnippets.slice(0, 10).map((snippet) => (
90
+ <li
91
+ key={snippet.snippet_id}
92
+ className="flex items-center justify-between"
93
+ >
94
+ <Link
95
+ href={`/${snippet.owner_name}/${snippet.unscoped_name}`}
96
+ className="text-blue-600 hover:underline text-sm"
97
+ >
98
+ {snippet.unscoped_name}
99
+ </Link>
100
+ <span className="text-xs text-gray-500">
101
+ {new Date(snippet.created_at).toLocaleDateString()}
102
+ </span>
103
+ </li>
104
+ ))}
105
+ </ul>
106
+ )}
107
+ {mySnippets && mySnippets.length > 10 && (
108
+ <Link
109
+ href={`/${currentUser}`}
110
+ className="text-sm text-blue-600 hover:underline mt-2 inline-block"
111
+ >
112
+ View all snippets
113
+ </Link>
114
+ )}
115
+ </div>
116
+ <div className="md:w-1/4">
117
+ <h2 className="text-sm font-bold mb-2 text-gray-700 border-b border-gray-200">
118
+ Trending Snippets
119
+ </h2>
120
+ {trendingSnippets && (
121
+ <ul className="space-y-1">
122
+ {trendingSnippets.map((snippet) => (
123
+ <li key={snippet.snippet_id}>
124
+ <div className="flex items-center">
125
+ <Link
126
+ href={`/${snippet.owner_name}/${snippet.unscoped_name}`}
127
+ className="text-blue-600 hover:underline text-sm"
128
+ >
129
+ {snippet.owner_name}/{snippet.unscoped_name}
130
+ </Link>
131
+ {snippet.star_count > 0 && (
132
+ <span className="ml-2 text-gray-500 text-xs flex items-center">
133
+ <Star className="w-3 h-3 mr-1" />
134
+ {snippet.star_count}
135
+ </span>
136
+ )}
137
+ </div>
138
+ </li>
139
+ ))}
140
+ </ul>
141
+ )}
142
+ <h2 className="text-sm font-bold mt-8 mb-2 text-gray-700 border-b border-gray-200">
143
+ Newest Snippets
144
+ </h2>
145
+ {newestSnippets && (
146
+ <ul className="space-y-1">
147
+ {newestSnippets.map((snippet) => (
148
+ <li key={snippet.snippet_id}>
149
+ <Link
150
+ href={`/${snippet.name}`}
151
+ className="text-blue-600 hover:underline text-sm"
152
+ >
153
+ {snippet.name}
154
+ </Link>
155
+ </li>
156
+ ))}
157
+ </ul>
158
+ )}
159
+ </div>
160
+ </div>
161
+ </div>
162
+ <Footer />
163
+ </div>
164
+ )
165
+ }
@@ -0,0 +1,68 @@
1
+ import { useGlobalStore } from "@/hooks/use-global-store"
2
+ import { useSnippetsBaseApiUrl } from "@/hooks/use-snippets-base-api-url"
3
+ import { useState } from "react"
4
+ import { useLocation } from "wouter"
5
+
6
+ export const DevLoginPage = () => {
7
+ const snippetsBaseApiUrl = useSnippetsBaseApiUrl()
8
+ const [username, setUsername] = useState("")
9
+ const setSession = useGlobalStore((s) => s.setSession)
10
+ const [, setLocation] = useLocation()
11
+
12
+ const handleDevLogin = async () => {
13
+ try {
14
+ // Call passwordless login endpoint
15
+ const response = await fetch(
16
+ `${snippetsBaseApiUrl}/sessions/create_passwordless`,
17
+ {
18
+ method: "POST",
19
+ headers: {
20
+ "Content-Type": "application/json",
21
+ },
22
+ body: JSON.stringify({
23
+ github_username: username,
24
+ }),
25
+ },
26
+ )
27
+
28
+ const data = await response.json()
29
+ const token = data.session.token
30
+
31
+ if (!token) {
32
+ throw new Error("No token received from server")
33
+ }
34
+
35
+ // Set session in global store
36
+ setSession({
37
+ token,
38
+ account_id: data.session.account_id,
39
+ session_id: data.session.session_id,
40
+ github_username: username,
41
+ })
42
+
43
+ // Redirect to home page
44
+ setLocation("/")
45
+ } catch (error) {
46
+ console.error("Dev login failed:", error)
47
+ }
48
+ }
49
+
50
+ return (
51
+ <div className="flex flex-col items-center justify-center min-h-screen bg-gray-100">
52
+ <h1 className="text-2xl font-bold mb-4">Development Login</h1>
53
+ <input
54
+ type="text"
55
+ value={username}
56
+ onChange={(e) => setUsername(e.target.value)}
57
+ placeholder="Enter GitHub username"
58
+ className="mb-4 p-2 border border-gray-300 rounded"
59
+ />
60
+ <button
61
+ onClick={handleDevLogin}
62
+ className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
63
+ >
64
+ Login
65
+ </button>
66
+ </div>
67
+ )
68
+ }