@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,83 @@
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 { cn } from "@/lib/utils"
9
+ import { ChevronDown, FileUp, Upload, Zap } from "lucide-react"
10
+ import { PrefetchPageLink } from "./PrefetchPageLink"
11
+
12
+ export default function HeaderDropdown() {
13
+ const blankTemplates = [
14
+ { name: "Blank Circuit Board", type: "board", badgeColor: "bg-blue-500" },
15
+ {
16
+ name: "Blank Circuit Module",
17
+ type: "package",
18
+ badgeColor: "bg-green-500",
19
+ },
20
+ {
21
+ name: "Blank 3D Model",
22
+ type: "model",
23
+ badgeColor: "bg-purple-500 ",
24
+ disabled: true,
25
+ },
26
+ {
27
+ name: "Blank Footprint",
28
+ type: "footprint",
29
+ badgeColor: "bg-pink-500 ",
30
+ disabled: true,
31
+ },
32
+ ]
33
+
34
+ return (
35
+ <DropdownMenu>
36
+ <DropdownMenuTrigger asChild>
37
+ <Button
38
+ size="sm"
39
+ variant="default"
40
+ className="bg-blue-600 hover:bg-blue-700"
41
+ >
42
+ New <ChevronDown className="ml-1 h-4 w-4" />
43
+ </Button>
44
+ </DropdownMenuTrigger>
45
+ <DropdownMenuContent className="w-fit">
46
+ <DropdownMenuItem asChild>
47
+ <PrefetchPageLink
48
+ href="/quickstart"
49
+ className="flex items-center cursor-pointer"
50
+ >
51
+ <Zap className="mr-2 h-3 w-3" />
52
+ Quickstart Templates
53
+ </PrefetchPageLink>
54
+ </DropdownMenuItem>
55
+ {blankTemplates.map((template, index) => (
56
+ <DropdownMenuItem key={index} asChild disabled={template.disabled}>
57
+ <a
58
+ href={`/editor?template=${template.name.toLowerCase().replace(/ /g, "-")}`}
59
+ className={cn(
60
+ "flex items-center cursor-pointer",
61
+ template.disabled && "opacity-50 cursor-not-allowed",
62
+ )}
63
+ >
64
+ <span
65
+ className={`w-2 h-2 rounded-full mr-2 ${template.badgeColor}`}
66
+ />
67
+ {template.name}
68
+ </a>
69
+ </DropdownMenuItem>
70
+ ))}
71
+ <DropdownMenuItem asChild>
72
+ <PrefetchPageLink
73
+ href="/quickstart"
74
+ className="flex items-center cursor-pointer"
75
+ >
76
+ <Upload className="mr-2 h-3 w-3" />
77
+ Import Part
78
+ </PrefetchPageLink>
79
+ </DropdownMenuItem>
80
+ </DropdownMenuContent>
81
+ </DropdownMenu>
82
+ )
83
+ }
@@ -0,0 +1,94 @@
1
+ import React, { useState } from "react"
2
+ import { Button } from "@/components/ui/button"
3
+ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
4
+ import {
5
+ DropdownMenu,
6
+ DropdownMenuContent,
7
+ DropdownMenuItem,
8
+ DropdownMenuTrigger,
9
+ } from "@/components/ui/dropdown-menu"
10
+ import { Link, useLocation, useRouter } from "wouter"
11
+ import { User } from "lucide-react"
12
+ import { useSnippetsBaseApiUrl } from "@/hooks/use-snippets-base-api-url"
13
+ import { useGlobalStore } from "@/hooks/use-global-store"
14
+ import { useAccountBalance } from "@/hooks/use-account-balance"
15
+ import { useIsUsingFakeApi } from "@/hooks/use-is-using-fake-api"
16
+ import { useSignIn } from "@/hooks/use-sign-in"
17
+
18
+ interface HeaderLoginProps {}
19
+
20
+ export const HeaderLogin: React.FC<HeaderLoginProps> = () => {
21
+ const [, setLocation] = useLocation()
22
+ const session = useGlobalStore((s) => s.session)
23
+ const isLoggedIn = Boolean(session)
24
+ const setSession = useGlobalStore((s) => s.setSession)
25
+ const snippetsBaseApiUrl = useSnippetsBaseApiUrl()
26
+ const isUsingFakeApi = useIsUsingFakeApi()
27
+ const signIn = useSignIn()
28
+ const { data: accountBalance } = useAccountBalance()
29
+
30
+ if (!isLoggedIn) {
31
+ const handleLogin = () => {
32
+ if (isUsingFakeApi) {
33
+ setSession({
34
+ account_id: "account-1234",
35
+ github_username: "testuser",
36
+ token: "1234",
37
+ session_id: "session-1234",
38
+ })
39
+ } else {
40
+ signIn()
41
+ }
42
+ }
43
+ return (
44
+ <div className="flex items-center space-x-2 justify-end">
45
+ <Button onClick={() => signIn()} variant="ghost">
46
+ Log in
47
+ </Button>
48
+ <Button onClick={() => signIn()}>Get Started</Button>
49
+ </div>
50
+ )
51
+ }
52
+
53
+ return (
54
+ <div className="flex justify-end items-center">
55
+ <DropdownMenu>
56
+ <DropdownMenuTrigger>
57
+ <Avatar className="w-8 h-8 login-avatar">
58
+ <AvatarImage
59
+ src={`https://github.com/${session?.github_username}.png`}
60
+ alt={`${session?.github_username}'s profile picture`}
61
+ />
62
+ <AvatarFallback aria-label="User avatar fallback">
63
+ <User size={16} aria-hidden="true" />
64
+ </AvatarFallback>
65
+ </Avatar>
66
+ </DropdownMenuTrigger>
67
+ <DropdownMenuContent>
68
+ <DropdownMenuItem className="text-gray-500 text-xs" disabled>
69
+ AI Usage $
70
+ {accountBalance?.monthly_ai_budget_used_usd.toFixed(2) ?? "0.00"} /
71
+ $5.00
72
+ </DropdownMenuItem>
73
+ <DropdownMenuItem
74
+ onClick={() => setLocation(`/${session?.github_username}`)}
75
+ >
76
+ My Profile
77
+ </DropdownMenuItem>
78
+ <DropdownMenuItem onClick={() => setLocation("/dashboard")}>
79
+ Dashboard
80
+ </DropdownMenuItem>
81
+ <DropdownMenuItem onClick={() => setLocation("/my-orders")}>
82
+ My Orders
83
+ </DropdownMenuItem>
84
+ <DropdownMenuItem onClick={() => setLocation("/settings")}>
85
+ Settings
86
+ </DropdownMenuItem>
87
+ <DropdownMenuItem onClick={() => setSession(null)}>
88
+ Sign out
89
+ </DropdownMenuItem>
90
+ </DropdownMenuContent>
91
+ </DropdownMenu>
92
+ </div>
93
+ )
94
+ }
@@ -0,0 +1,155 @@
1
+ import React, { useState } from "react"
2
+ import {
3
+ Dialog,
4
+ DialogContent,
5
+ DialogHeader,
6
+ DialogTitle,
7
+ DialogFooter,
8
+ } from "@/components/ui/dialog"
9
+ import { Button } from "@/components/ui/button"
10
+ import { Input } from "@/components/ui/input"
11
+ import { useAxios } from "@/hooks/use-axios"
12
+ import { useToast } from "@/hooks/use-toast"
13
+ import { useLocation } from "wouter"
14
+ import { useGlobalStore } from "@/hooks/use-global-store"
15
+ import { PrefetchPageLink } from "./PrefetchPageLink"
16
+
17
+ interface JLCPCBImportDialogProps {
18
+ open: boolean
19
+ onOpenChange: (open: boolean) => void
20
+ }
21
+
22
+ export function JLCPCBImportDialog({
23
+ open,
24
+ onOpenChange,
25
+ }: JLCPCBImportDialogProps) {
26
+ const [partNumber, setPartNumber] = useState("")
27
+ const [isLoading, setIsLoading] = useState(false)
28
+ const [error, setError] = useState<string | null>(null)
29
+ const axios = useAxios()
30
+ const { toast } = useToast()
31
+ const [, navigate] = useLocation()
32
+ const isLoggedIn = useGlobalStore((s) => Boolean(s.session))
33
+ const session = useGlobalStore((s) => s.session)
34
+
35
+ const handleImport = async () => {
36
+ if (!partNumber.startsWith("C")) {
37
+ toast({
38
+ title: "Invalid Part Number",
39
+ description: "JLCPCB part numbers should start with 'C'.",
40
+ variant: "destructive",
41
+ })
42
+ return
43
+ }
44
+
45
+ setIsLoading(true)
46
+ setError(null)
47
+ try {
48
+ // Check that module doesn't already exist
49
+ const existingSnippetRes = await axios.get(
50
+ `/snippets/get?owner_name=${session?.github_username}&unscoped_name=${partNumber}`,
51
+ {
52
+ validateStatus: (status) => true,
53
+ },
54
+ )
55
+
56
+ if (existingSnippetRes.status !== 404) {
57
+ toast({
58
+ title: "JLCPCB Part Already Imported",
59
+ description: (
60
+ <div>
61
+ <PrefetchPageLink
62
+ className="text-blue-500 hover:underline"
63
+ href={`/editor?snippet_id=${existingSnippetRes.data.snippet.snippet_id}`}
64
+ >
65
+ View {partNumber}
66
+ </PrefetchPageLink>
67
+ </div>
68
+ ),
69
+ })
70
+ return
71
+ }
72
+
73
+ const response = await axios
74
+ .post("/snippets/generate_from_jlcpcb", {
75
+ jlcpcb_part_number: partNumber,
76
+ })
77
+ .catch((e) => e)
78
+ const { snippet, error } = response.data
79
+ if (error) {
80
+ setError(error.message)
81
+ setIsLoading(false)
82
+ return
83
+ }
84
+ toast({
85
+ title: "Import Successful",
86
+ description: "JLCPCB component has been imported successfully.",
87
+ })
88
+ onOpenChange(false)
89
+ navigate(`/editor?snippet_id=${snippet.snippet_id}`)
90
+ } catch (error) {
91
+ console.error("Error importing JLCPCB component:", error)
92
+ toast({
93
+ title: "Import Failed",
94
+ description: "Failed to import the JLCPCB component. Please try again.",
95
+ variant: "destructive",
96
+ })
97
+ } finally {
98
+ setIsLoading(false)
99
+ }
100
+ }
101
+
102
+ return (
103
+ <Dialog open={open} onOpenChange={onOpenChange}>
104
+ <DialogContent>
105
+ <DialogHeader>
106
+ <DialogTitle>Import from JLCPCB</DialogTitle>
107
+ </DialogHeader>
108
+ <div className="py-4">
109
+ <a
110
+ href="https://yaqwsx.github.io/jlcparts/#/"
111
+ target="_blank"
112
+ rel="noopener noreferrer"
113
+ className="text-blue-500 hover:underline opacity-80"
114
+ >
115
+ JLCPCB Part Search
116
+ </a>
117
+ <Input
118
+ className="mt-3"
119
+ placeholder="Enter JLCPCB part number (e.g., C46749)"
120
+ value={partNumber}
121
+ onChange={(e) => setPartNumber(e.target.value)}
122
+ />
123
+ {error && <p className="bg-red-100 p-2 mt-2 pre-wrap">{error}</p>}
124
+ {error && (
125
+ <div className="flex justify-end mt-2">
126
+ <Button
127
+ variant="default"
128
+ onClick={() => {
129
+ const issueTitle = `[${partNumber}] Failed to import from JLCPCB`
130
+ const issueBody = `I tried to import the part number ${partNumber} from JLCPCB, but it failed. Here's the error I got:\n\`\`\`\n${error}\n\`\`\`\n\nCould be an issue in \`fetchEasyEDAComponent\` or \`convertRawEasyEdaToTs\``
131
+ const issueLabels = "snippets,good first issue"
132
+ const url = `https://github.com/tscircuit/easyeda-converter/issues/new?title=${encodeURIComponent(issueTitle)}&body=${encodeURIComponent(issueBody)}&labels=${encodeURIComponent(issueLabels)}`
133
+
134
+ // Open the issue in a new tab
135
+ window.open(url, "_blank")
136
+ }}
137
+ >
138
+ File Issue on Github (prefilled)
139
+ </Button>
140
+ </div>
141
+ )}
142
+ </div>
143
+ <DialogFooter>
144
+ <Button onClick={handleImport} disabled={isLoading || !isLoggedIn}>
145
+ {!isLoggedIn
146
+ ? "Must be logged in for jlcpcb import"
147
+ : isLoading
148
+ ? "Importing..."
149
+ : "Import"}
150
+ </Button>
151
+ </DialogFooter>
152
+ </DialogContent>
153
+ </Dialog>
154
+ )
155
+ }
@@ -0,0 +1,175 @@
1
+ import { Button } from "@/components/ui/button"
2
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
3
+ import { Link, useLocation } from "wouter"
4
+ import { useGlobalStore } from "../hooks/use-global-store"
5
+ import { CreateNewSnippetWithAiHero } from "./CreateNewSnippetWithAiHero"
6
+ import { TypeBadge } from "./TypeBadge"
7
+ import { useQuery } from "react-query"
8
+ import { useAxios } from "@/hooks/use-axios"
9
+ import { Star } from "lucide-react"
10
+ import { Snippet } from "fake-snippets-api/lib/db/schema"
11
+ import { StarFilledIcon } from "@radix-ui/react-icons"
12
+
13
+ export const LandingHero = () => {
14
+ const { should_onboarding_tips_be_closed, setOnboardingTipsClosed } =
15
+ useGlobalStore((state) => ({
16
+ should_onboarding_tips_be_closed: state.should_onboarding_tips_be_closed,
17
+ setOnboardingTipsClosed: state.setOnboardingTipsClosed,
18
+ }))
19
+ const [, setLocation] = useLocation()
20
+ const axios = useAxios()
21
+
22
+ const { data: trendingSnippets } = useQuery<Snippet[]>(
23
+ "trendingSnippets",
24
+ async () => {
25
+ const response = await axios.get("/snippets/list_trending")
26
+ return response.data.snippets
27
+ },
28
+ )
29
+
30
+ return (
31
+ <div className="p-6">
32
+ <div className="mb-12">
33
+ <h1 className="text-xl font-bold mb-4">
34
+ Design Circuit Boards with Code
35
+ </h1>
36
+ <p className="text-lg text-gray-600 mb-8 max-w-2xl">
37
+ tscircuit is an open-source library
38
+ <a
39
+ href="https://github.com/tscircuit/tscircuit"
40
+ className="inline-flex items-center translate-y-[3px]"
41
+ >
42
+ <img
43
+ src="https://img.shields.io/github/stars/tscircuit/tscircuit?style=social"
44
+ alt="GitHub stars"
45
+ className="mx-1"
46
+ />
47
+ </a>
48
+ for creating and sharing electronic designs using TypeScript and
49
+ React. Design functional circuit boards you can actually order using
50
+ code, completely in your browser!
51
+ </p>
52
+
53
+ {trendingSnippets && trendingSnippets.length > 0 && (
54
+ <div className="mb-8">
55
+ <h2 className="text-xl font-semibold mb-4">Trending Snippets</h2>
56
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
57
+ {trendingSnippets.slice(0, 6).map((snippet) => (
58
+ <Link
59
+ key={snippet.snippet_id}
60
+ href={`/${snippet.owner_name}/${snippet.unscoped_name}`}
61
+ className="text-sm flex justify-between text-blue-600 py-3 px-3 border border-gray-300 shadow rounded-sm m-0 items-center"
62
+ >
63
+ <div className="truncate max-w-[80%]">
64
+ {snippet.owner_name}/{snippet.unscoped_name}
65
+ </div>
66
+ <div className="flex items-center ml-2 flex-shrink-0">
67
+ {snippet.star_count || 0}
68
+ <StarFilledIcon className="w-3 h-3 ml-1" />
69
+ </div>
70
+ </Link>
71
+ ))}
72
+ </div>
73
+ </div>
74
+ )}
75
+
76
+ {!should_onboarding_tips_be_closed && (
77
+ <Card className="mb-6 bg-gray-50 rounded-sm">
78
+ <CardHeader className="p-3 pl-8">
79
+ <CardTitle className="flex justify-between items-center">
80
+ <div className="pt-2">Onboarding Tips</div>
81
+ <Button
82
+ variant="ghost"
83
+ className="text-gray-400 text-2xl"
84
+ onClick={() =>
85
+ setOnboardingTipsClosed(!should_onboarding_tips_be_closed)
86
+ }
87
+ >
88
+ &times;
89
+ </Button>
90
+ </CardTitle>
91
+ </CardHeader>
92
+ <CardContent>
93
+ <ul className="list-disc marker:text-gray-400 pl-5 space-y-2">
94
+ <li>
95
+ tscircuit snippets are React components that turn into
96
+ manufacturable circuit boards, circuit modules, 3D models or
97
+ footprints.
98
+ </li>
99
+ <li>
100
+ You can design full boards, or individual components like
101
+ chips
102
+ </li>
103
+ <li>
104
+ You can export snippets to{" "}
105
+ <a href="#" className="text-blue-600">
106
+ KiCad
107
+ </a>
108
+ ,{" "}
109
+ <a href="#" className="text-blue-600">
110
+ Gerbers
111
+ </a>
112
+ ,{" "}
113
+ <a href="#" className="text-blue-600">
114
+ STEP
115
+ </a>
116
+ , and other formats
117
+ </li>
118
+ <li>Press "import" when coding to import other snippets</li>
119
+ </ul>
120
+ </CardContent>
121
+ </Card>
122
+ )}
123
+
124
+ <div className="grid grid-cols-1 sm:grid-cols-4 gap-4">
125
+ <div>
126
+ <Link href="/editor?template=blank-circuit-board">
127
+ <Button className="w-full justify-between mb-2 px-2">
128
+ Create new <TypeBadge type="board" className="ml-2" />
129
+ </Button>
130
+ </Link>
131
+ <p className="text-sm text-gray-600">
132
+ A printed circuit board containing multiple chips, resistors and
133
+ capacitors to form a functional, manufacturable electronic device.
134
+ </p>
135
+ </div>
136
+ <div>
137
+ <Link href="/editor?template=blank-circuit-module">
138
+ <Button className="w-full justify-between mb-2 px-2">
139
+ Create new <TypeBadge type="package" className="ml-2" />
140
+ </Button>
141
+ </Link>
142
+ <p className="text-sm text-gray-600">
143
+ A chip or circuit module that you'd like to use as part of a
144
+ larger package.
145
+ </p>
146
+ </div>
147
+ <div>
148
+ <Link href="/editor?template=blank-footprint">
149
+ <Button className="w-full justify-between mb-2 px-2">
150
+ Create new <TypeBadge type="footprint" className="ml-2" />
151
+ </Button>
152
+ </Link>
153
+ <p className="text-sm text-gray-600">
154
+ A footprint or landing-pattern for a component. This is usually
155
+ used as part of a chip.
156
+ </p>
157
+ </div>
158
+ <div>
159
+ <Link href="/editor?template=blank-3d-model">
160
+ <Button className="w-full justify-between mb-2 px-2">
161
+ Create new <TypeBadge type="model" className="ml-2" />
162
+ </Button>
163
+ </Link>
164
+ <p className="text-sm text-gray-600">
165
+ A 3d model of a component. This is usually used as part of a chip.
166
+ </p>
167
+ </div>
168
+ </div>
169
+ <div className="mt-6">
170
+ <CreateNewSnippetWithAiHero />
171
+ </div>
172
+ </div>
173
+ </div>
174
+ )
175
+ }
@@ -0,0 +1,39 @@
1
+ import React from "react"
2
+ import { useQuery } from "react-query"
3
+ import { useAxios } from "@/hooks/use-axios"
4
+ import { Snippet } from "fake-snippets-api/lib/db/schema"
5
+ import { Link } from "wouter"
6
+
7
+ export const LatestSnippets: React.FC = () => {
8
+ const axios = useAxios()
9
+ const {
10
+ data: snippets,
11
+ isLoading,
12
+ error,
13
+ } = useQuery<Snippet[]>("latestSnippets", async () => {
14
+ const response = await axios.get("/snippets/list_newest")
15
+ return response.data.snippets
16
+ })
17
+
18
+ if (isLoading) return <div>Loading...</div>
19
+ if (error) return <div>Error loading snippets</div>
20
+
21
+ return (
22
+ <div className="space-y-4">
23
+ {snippets?.map((snippet) => (
24
+ <div key={snippet.snippet_id} className="border p-4 rounded-md">
25
+ <Link
26
+ href={`/${snippet.owner_name}/${snippet.unscoped_name}`}
27
+ className="text-blue-600 hover:underline"
28
+ >
29
+ <h3 className="text-lg font-semibold">{snippet.unscoped_name}</h3>
30
+ </Link>
31
+ <p className="text-sm text-gray-600">by {snippet.owner_name}</p>
32
+ <p className="text-sm text-gray-500">
33
+ Created: {new Date(snippet.created_at).toLocaleString()}
34
+ </p>
35
+ </div>
36
+ ))}
37
+ </div>
38
+ )
39
+ }
@@ -0,0 +1,96 @@
1
+ import { useState, useEffect } from "react"
2
+
3
+ /**
4
+ * OptimizedImage component for responsive images with automatic srcset generation
5
+ *
6
+ * This component automatically generates srcset attributes for responsive images.
7
+ * Place your original high-resolution images in src/assets/originals/.
8
+ * The build process will automatically generate all required sizes.
9
+ *
10
+ * Example usage:
11
+ * <OptimizedImage
12
+ * src="/assets/example.jpg"
13
+ * alt="Example"
14
+ * />
15
+ *
16
+ * The build process will generate:
17
+ * /assets/
18
+ * example-400w.jpg
19
+ * example-600w.jpg
20
+ * example-800w.jpg
21
+ * example-1000w.jpg
22
+ * example-1200w.jpg
23
+ * example-1600w.jpg
24
+ * example-2000w.jpg
25
+ */
26
+ interface OptimizedImageProps
27
+ extends React.ImgHTMLAttributes<HTMLImageElement> {
28
+ src: string
29
+ alt: string
30
+ priority?: boolean
31
+ }
32
+
33
+ const getImageSizes = (src: string) => {
34
+ if (src.endsWith(".svg")) return { srcSet: src, sizes: "100vw" }
35
+
36
+ const widths = [400, 600, 800, 1000, 1200, 1600, 2000]
37
+ const basePath = src.substring(0, src.lastIndexOf("."))
38
+ const extension = src.substring(src.lastIndexOf("."))
39
+ const srcSet = widths
40
+ .map((w) => `${basePath}-${w}w${extension} ${w}w`)
41
+ .join(", ")
42
+
43
+ const sizes =
44
+ "(max-width: 400px) 400px, (max-width: 600px) 600px, (max-width: 800px) 800px, (max-width: 1000px) 1000px, (max-width: 1200px) 1200px, (max-width: 1600px) 1600px, 2000px"
45
+
46
+ return { srcSet, sizes }
47
+ }
48
+
49
+ export function OptimizedImage({
50
+ src,
51
+ alt,
52
+ className,
53
+ priority = false,
54
+ ...props
55
+ }: OptimizedImageProps) {
56
+ const [imageLoading, setImageLoading] = useState(true)
57
+ const { srcSet, sizes } = getImageSizes(src)
58
+
59
+ useEffect(() => {
60
+ if (priority) {
61
+ const link = document.createElement("link")
62
+ link.rel = "preload"
63
+ link.as = "image"
64
+ link.href = src
65
+ link.type = src.endsWith(".svg") ? "image/svg+xml" : "image/webp"
66
+ link.imageSrcset = srcSet
67
+ link.imageSizes = sizes
68
+ document.head.appendChild(link)
69
+
70
+ return () => {
71
+ document.head.removeChild(link)
72
+ }
73
+ }
74
+ }, [src, priority, srcSet, sizes])
75
+
76
+ return (
77
+ <img
78
+ src={src}
79
+ srcSet={srcSet}
80
+ sizes={sizes}
81
+ alt={alt}
82
+ loading={priority ? "eager" : "lazy"}
83
+ decoding={priority ? "sync" : "async"}
84
+ fetchPriority={priority ? "high" : "auto"}
85
+ className={`${className} ${
86
+ imageLoading ? "animate-pulse bg-gray-200" : ""
87
+ } object-contain`}
88
+ onLoad={() => setImageLoading(false)}
89
+ onError={(e) => {
90
+ console.error("Image failed to load:", e)
91
+ setImageLoading(false)
92
+ }}
93
+ {...props}
94
+ />
95
+ )
96
+ }