@tscircuit/fake-snippets 0.0.109 → 0.0.110

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/.github/workflows/bun-formatcheck.yml +2 -2
  2. package/.github/workflows/bun-pver-release.yml +3 -3
  3. package/.github/workflows/bun-test.yml +1 -1
  4. package/.github/workflows/bun-typecheck.yml +2 -2
  5. package/.github/workflows/update-snapshots.yml +1 -1
  6. package/README.md +4 -0
  7. package/api/generated-index.js +37 -3
  8. package/biome.json +2 -1
  9. package/bun-tests/fake-snippets-api/fixtures/get-test-server.ts +31 -3
  10. package/bun-tests/fake-snippets-api/fixtures/preload.ts +18 -0
  11. package/bun-tests/fake-snippets-api/routes/orgs/add_member.test.ts +26 -0
  12. package/bun-tests/fake-snippets-api/routes/orgs/create.test.ts +37 -0
  13. package/bun-tests/fake-snippets-api/routes/orgs/get.test.ts +52 -0
  14. package/bun-tests/fake-snippets-api/routes/orgs/list.test.ts +17 -0
  15. package/bun-tests/fake-snippets-api/routes/orgs/list_members.test.ts +23 -0
  16. package/bun-tests/fake-snippets-api/routes/orgs/remove_member.test.ts +81 -0
  17. package/bun-tests/fake-snippets-api/routes/orgs/update.test.ts +99 -0
  18. package/bun-tests/fake-snippets-api/routes/package_builds/get.test.ts +1 -1
  19. package/bun-tests/fake-snippets-api/routes/package_files/create.test.ts +15 -13
  20. package/bun-tests/fake-snippets-api/routes/package_files/create_or_update.test.ts +26 -24
  21. package/bun-tests/fake-snippets-api/routes/package_files/delete.test.ts +9 -9
  22. package/bun-tests/fake-snippets-api/routes/package_files/download.test.ts +4 -4
  23. package/bun-tests/fake-snippets-api/routes/package_files/get.test.ts +38 -28
  24. package/bun-tests/fake-snippets-api/routes/package_files/list.test.ts +23 -15
  25. package/bun-tests/fake-snippets-api/routes/package_releases/create.test.ts +33 -0
  26. package/bun-tests/fake-snippets-api/routes/package_releases/get.test.ts +4 -4
  27. package/bun-tests/fake-snippets-api/routes/package_releases/get_image_generation_fields.test.ts +38 -0
  28. package/bun-tests/fake-snippets-api/routes/packages/create.test.ts +19 -0
  29. package/bun-tests/fake-snippets-api/routes/packages/fork.test.ts +3 -4
  30. package/bun-tests/fake-snippets-api/routes/packages/get.test.ts +30 -0
  31. package/bun-tests/fake-snippets-api/routes/packages/images.test.ts +4 -2
  32. package/bun-tests/fake-snippets-api/routes/packages/list-1.test.ts +34 -0
  33. package/bun.lock +349 -453
  34. package/bunfig.toml +2 -1
  35. package/dist/bundle.js +1253 -624
  36. package/dist/index.d.ts +291 -4
  37. package/dist/index.js +323 -23
  38. package/dist/schema.d.ts +274 -1
  39. package/dist/schema.js +52 -1
  40. package/fake-snippets-api/lib/db/autoload-dev-packages.ts +31 -20
  41. package/fake-snippets-api/lib/db/db-client.ts +214 -3
  42. package/fake-snippets-api/lib/db/schema.ts +61 -0
  43. package/fake-snippets-api/lib/db/seed.ts +100 -0
  44. package/fake-snippets-api/lib/middleware/with-session-auth.ts +1 -1
  45. package/fake-snippets-api/lib/package_file/get-package-file-id-from-file-descriptor.ts +2 -2
  46. package/fake-snippets-api/lib/public-mapping/public-map-org.ts +32 -0
  47. package/fake-snippets-api/lib/public-mapping/public-map-package-build.ts +10 -0
  48. package/fake-snippets-api/lib/public-mapping/public-map-package-release.ts +17 -0
  49. package/fake-snippets-api/routes/api/orgs/add_member.ts +52 -0
  50. package/fake-snippets-api/routes/api/orgs/create.ts +46 -0
  51. package/fake-snippets-api/routes/api/orgs/get.ts +39 -0
  52. package/fake-snippets-api/routes/api/orgs/list.ts +31 -0
  53. package/fake-snippets-api/routes/api/orgs/list_members.ts +67 -0
  54. package/fake-snippets-api/routes/api/orgs/remove_member.ts +46 -0
  55. package/fake-snippets-api/routes/api/orgs/update.ts +93 -0
  56. package/fake-snippets-api/routes/api/package_files/get.ts +3 -6
  57. package/fake-snippets-api/routes/api/package_files/list.ts +7 -4
  58. package/fake-snippets-api/routes/api/packages/create.ts +54 -10
  59. package/fake-snippets-api/routes/api/packages/get.ts +23 -0
  60. package/fake-snippets-api/routes/api/packages/images/[owner_github_username]/[unscoped_name]/[view_format].ts +13 -11
  61. package/fake-snippets-api/routes/api/packages/list.ts +29 -2
  62. package/fake-snippets-api/routes/api/packages/update_ai_description.ts +37 -0
  63. package/package.json +24 -20
  64. package/renovate.json +1 -1
  65. package/scripts/generate-sitemap.ts +1 -1
  66. package/src/App.tsx +29 -8
  67. package/src/ContextProviders.tsx +25 -2
  68. package/src/components/CircuitJsonImportDialog.tsx +1 -1
  69. package/src/components/CmdKMenu.tsx +281 -247
  70. package/src/components/DownloadButtonAndMenu.tsx +3 -4
  71. package/src/components/FileSidebar.tsx +11 -17
  72. package/src/components/Footer.tsx +8 -9
  73. package/src/components/Header.tsx +19 -32
  74. package/src/components/Header2.tsx +16 -32
  75. package/src/components/HeaderDropdown.tsx +13 -8
  76. package/src/components/HeaderLogin.tsx +43 -15
  77. package/src/components/NotFound.tsx +5 -5
  78. package/src/components/PackageBreadcrumb.tsx +6 -12
  79. package/src/components/PackageSearchResults.tsx +1 -1
  80. package/src/components/PrefetchPageLink.tsx +7 -1
  81. package/src/components/ProfileRouter.tsx +32 -0
  82. package/src/components/SearchComponent.tsx +12 -8
  83. package/src/components/UserCard.tsx +80 -0
  84. package/src/components/ViewPackagePage/components/build-status.tsx +1 -1
  85. package/src/components/ViewPackagePage/components/important-files-view.tsx +105 -34
  86. package/src/components/ViewPackagePage/components/main-content-header.tsx +10 -6
  87. package/src/components/ViewPackagePage/components/main-content-view-selector.tsx +1 -1
  88. package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +54 -19
  89. package/src/components/ViewPackagePage/components/package-header.tsx +25 -33
  90. package/src/components/ViewPackagePage/components/preview-image-squares.tsx +11 -18
  91. package/src/components/ViewPackagePage/components/repo-page-content.tsx +12 -5
  92. package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +16 -10
  93. package/src/components/ViewPackagePage/components/sidebar-releases-section.tsx +11 -11
  94. package/src/components/ViewPackagePage/components/tab-views/pcb-view.tsx +1 -2
  95. package/src/components/ViewPackagePage/components/tab-views/schematic-view.tsx +2 -1
  96. package/src/components/dialogs/GitHubRepositorySelector.tsx +56 -49
  97. package/src/components/dialogs/edit-package-details-dialog.tsx +5 -6
  98. package/src/components/dialogs/import-component-dialog.tsx +16 -9
  99. package/src/components/dialogs/import-package-dialog.tsx +3 -2
  100. package/src/components/dialogs/new-package-save-prompt-dialog.tsx +190 -0
  101. package/src/components/organization/OrganizationCard.tsx +204 -0
  102. package/src/components/organization/OrganizationCardSkeleton.tsx +55 -0
  103. package/src/components/organization/OrganizationHeader.tsx +154 -0
  104. package/src/components/organization/OrganizationMembers.tsx +146 -0
  105. package/src/components/package-port/CodeAndPreview.tsx +15 -12
  106. package/src/components/package-port/CodeEditor.tsx +4 -30
  107. package/src/components/package-port/CodeEditorHeader.tsx +123 -61
  108. package/src/components/package-port/EditorNav.tsx +32 -49
  109. package/src/components/preview/ConnectedPackagesList.tsx +8 -8
  110. package/src/components/preview/ConnectedRepoOverview.tsx +102 -2
  111. package/src/components/preview/PackageReleasesDashboard.tsx +23 -11
  112. package/src/components/ui/tree-view.tsx +6 -3
  113. package/src/hooks/use-add-org-member-mutation.ts +51 -0
  114. package/src/hooks/use-create-org-mutation.ts +38 -0
  115. package/src/hooks/use-create-package-mutation.ts +3 -0
  116. package/src/hooks/use-current-package-release.ts +4 -3
  117. package/src/hooks/use-download-zip.ts +2 -2
  118. package/src/hooks/use-global-store.ts +6 -4
  119. package/src/hooks/use-jlcpcb-component-import.tsx +164 -0
  120. package/src/hooks/use-list-org-members.ts +27 -0
  121. package/src/hooks/use-list-user-orgs.ts +25 -0
  122. package/src/hooks/use-org-by-github-handle.ts +26 -0
  123. package/src/hooks/use-org.ts +24 -0
  124. package/src/hooks/use-organization.ts +42 -0
  125. package/src/hooks/use-package-as-snippet.ts +4 -2
  126. package/src/hooks/use-package-builds.ts +6 -2
  127. package/src/hooks/use-package-files.ts +5 -3
  128. package/src/hooks/use-package-release-by-id-or-version.ts +29 -20
  129. package/src/hooks/use-package-release-images.ts +105 -0
  130. package/src/hooks/use-package-release.ts +2 -2
  131. package/src/hooks/use-package-stars.ts +80 -4
  132. package/src/hooks/use-preview-images.ts +6 -3
  133. package/src/hooks/use-remove-org-member-mutation.ts +32 -0
  134. package/src/hooks/use-update-ai-description-mutation.ts +42 -0
  135. package/src/hooks/use-update-org-mutation.ts +41 -0
  136. package/src/hooks/use-warn-user-on-page-change.ts +71 -4
  137. package/src/hooks/useFileManagement.ts +51 -22
  138. package/src/hooks/useOptimizedPackageFilesLoader.ts +11 -24
  139. package/src/hooks/usePackageFilesLoader.ts +2 -2
  140. package/src/hooks/useUpdatePackageFilesMutation.ts +13 -1
  141. package/src/lib/download-fns/download-gltf-from-circuit-json.ts +1 -1
  142. package/src/lib/download-fns/download-kicad-files.ts +12 -11
  143. package/src/lib/normalize-svg-for-tile.ts +50 -0
  144. package/src/lib/posthog.ts +11 -9
  145. package/src/lib/react-query-api-failure-tracking.ts +148 -0
  146. package/src/lib/sentry.ts +14 -0
  147. package/src/lib/templates/blank-circuit-board-template.ts +0 -4
  148. package/src/lib/ts-lib-cache.ts +122 -7
  149. package/src/lib/utils/checkIfManualEditsImported.ts +4 -4
  150. package/src/lib/utils/findTargetFile.ts +45 -10
  151. package/src/lib/utils/isComponentExported.ts +2 -1
  152. package/src/main.tsx +2 -1
  153. package/src/pages/create-organization.tsx +168 -0
  154. package/src/pages/dashboard.tsx +38 -6
  155. package/src/pages/datasheet.tsx +1 -1
  156. package/src/pages/datasheets.tsx +3 -3
  157. package/src/pages/editor.tsx +4 -6
  158. package/src/pages/landing.tsx +6 -6
  159. package/src/pages/latest.tsx +3 -0
  160. package/src/pages/organization-profile.tsx +199 -0
  161. package/src/pages/organization-settings.tsx +566 -0
  162. package/src/pages/package-editor.tsx +21 -21
  163. package/src/pages/preview-release.tsx +75 -145
  164. package/src/pages/quickstart.tsx +159 -123
  165. package/src/pages/release-detail.tsx +119 -31
  166. package/src/pages/search.tsx +192 -57
  167. package/src/pages/settings-redirect.tsx +44 -0
  168. package/src/pages/trending.tsx +29 -20
  169. package/src/pages/user-profile.tsx +58 -7
  170. package/src/pages/view-package.tsx +7 -13
  171. package/vite.config.ts +9 -0
  172. package/fake-snippets-api/routes/api/autocomplete/create_autocomplete.ts +0 -133
  173. package/src/components/JLCPCBImportDialog.tsx +0 -280
  174. package/src/components/PackageBuildsPage/LogContent.tsx +0 -72
  175. package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +0 -113
  176. package/src/components/PackageBuildsPage/build-preview-content.tsx +0 -56
  177. package/src/components/PackageBuildsPage/collapsible-section.tsx +0 -63
  178. package/src/components/PackageBuildsPage/package-build-details-panel.tsx +0 -166
  179. package/src/components/PackageBuildsPage/package-build-header.tsx +0 -79
  180. package/src/components/PageSearchComponent.tsx +0 -148
  181. package/src/pages/package-builds.tsx +0 -33
@@ -1,12 +1,13 @@
1
- import { JLCPCBImportDialog } from "@/components/JLCPCBImportDialog"
2
1
  import { useAxios } from "@/hooks/use-axios"
3
2
  import { useGlobalStore } from "@/hooks/use-global-store"
4
3
  import { useHotkeyCombo } from "@/hooks/use-hotkey"
5
4
  import { useNotImplementedToast } from "@/hooks/use-toast"
6
5
  import { fuzzyMatch } from "@/components/ViewPackagePage/utils/fuzz-search"
6
+ import { useImportComponentDialog } from "@/components/dialogs/import-component-dialog"
7
+ import { useJlcpcbComponentImport } from "@/hooks/use-jlcpcb-component-import"
7
8
  import { Command } from "cmdk"
8
9
  import { Package, Account } from "fake-snippets-api/lib/db/schema"
9
- import React, { useCallback, useEffect, useMemo, useRef } from "react"
10
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"
10
11
  import { useQuery } from "react-query"
11
12
  import {
12
13
  Search,
@@ -19,7 +20,14 @@ import {
19
20
  Star,
20
21
  User,
21
22
  } from "lucide-react"
22
- import { DialogTitle, DialogDescription } from "@/components/ui/dialog"
23
+ import {
24
+ Dialog,
25
+ DialogContent,
26
+ DialogTitle,
27
+ DialogDescription,
28
+ } from "@/components/ui/dialog"
29
+ import { CircuitJsonImportDialog } from "./CircuitJsonImportDialog"
30
+ import { JlcpcbComponentTsxLoadedPayload } from "@tscircuit/runframe/runner"
23
31
 
24
32
  type SnippetType = "board" | "package" | "model" | "footprint"
25
33
 
@@ -48,13 +56,33 @@ interface ScoredAccount extends Account {
48
56
  }
49
57
 
50
58
  const CmdKMenu = () => {
51
- const [open, setOpen] = React.useState(false)
52
- const [searchQuery, setSearchQuery] = React.useState("")
53
- const [isJLCPCBDialogOpen, setIsJLCPCBDialogOpen] = React.useState(false)
54
- const [selectedIndex, setSelectedIndex] = React.useState(0)
59
+ const [open, setOpen] = useState(false)
60
+ const [searchQuery, setSearchQuery] = useState("")
61
+ const [isCircuitJsonImportDialogOpen, setIsCircuitJsonImportDialogOpen] =
62
+ useState(false)
63
+ const [selectedIndex, setSelectedIndex] = useState(0)
55
64
  const toastNotImplemented = useNotImplementedToast()
56
65
  const axios = useAxios()
57
- const currentUser = useGlobalStore((s) => s.session?.github_username)
66
+ const { Dialog: ImportComponentDialog, openDialog: openImportDialog } =
67
+ useImportComponentDialog()
68
+ const { importComponent: importJlcpcbComponent } = useJlcpcbComponentImport()
69
+ const session = useGlobalStore((s) => s.session)
70
+ const currentUser = session?.github_username
71
+ const jlcpcbProxyRequestHeaders = useMemo(
72
+ () =>
73
+ session?.token
74
+ ? {
75
+ Authorization: `Bearer ${session.token}`,
76
+ }
77
+ : undefined,
78
+ [session?.token],
79
+ )
80
+ const handleJlcpcbComponentSelected = useCallback(
81
+ async (payload: JlcpcbComponentTsxLoadedPayload) => {
82
+ await importJlcpcbComponent(payload)
83
+ },
84
+ [importJlcpcbComponent],
85
+ )
58
86
  const selectedItemRef = useRef<HTMLDivElement>(null)
59
87
 
60
88
  const blankTemplates = useMemo(
@@ -92,19 +120,10 @@ const CmdKMenu = () => {
92
120
  const importOptions = useMemo(
93
121
  (): ImportOption[] => [
94
122
  {
95
- name: "KiCad Footprint",
96
- type: "footprint",
97
- icon: <Download className="w-4 h-4 text-gray-500" />,
98
- },
99
- {
100
- name: "KiCad Project",
101
- type: "board",
102
- icon: <Download className="w-4 h-4 text-gray-500" />,
103
- },
104
- {
105
- name: "KiCad Module",
123
+ name: "Circuit JSON",
106
124
  type: "package",
107
- icon: <Download className="w-4 h-4 text-gray-500" />,
125
+ special: true,
126
+ icon: <Download className="w-4 h-4 text-red-500" />,
108
127
  },
109
128
  {
110
129
  name: "JLCPCB Component",
@@ -369,8 +388,16 @@ const CmdKMenu = () => {
369
388
  break
370
389
  case "import":
371
390
  if (item.special) {
372
- setOpen(false)
373
- setIsJLCPCBDialogOpen(true)
391
+ switch (item.name) {
392
+ case "Circuit JSON":
393
+ setOpen(false)
394
+ setIsCircuitJsonImportDialogOpen(true)
395
+ break
396
+ case "JLCPCB Component":
397
+ setOpen(false)
398
+ openImportDialog()
399
+ break
400
+ }
374
401
  } else {
375
402
  setOpen(false)
376
403
  toastNotImplemented(`${item.name} Import`)
@@ -401,8 +428,8 @@ const CmdKMenu = () => {
401
428
  const isSelected = index === selectedIndex
402
429
 
403
430
  const baseClasses = `
404
- group flex items-center justify-between px-3 py-2 rounded-md cursor-pointer
405
- transition-all duration-150 border border-transparent text-sm
431
+ group flex items-center justify-between px-2 sm:px-3 py-2 rounded-md cursor-pointer
432
+ transition-all duration-150 border border-transparent text-sm w-full
406
433
  ${isSelected ? "bg-blue-50 border-blue-200" : "hover:bg-gray-50"}
407
434
  ${disabled ? "opacity-50 cursor-not-allowed" : ""}
408
435
  `
@@ -417,32 +444,32 @@ const CmdKMenu = () => {
417
444
  className={baseClasses}
418
445
  onClick={() => !disabled && handleItemSelect(item)}
419
446
  >
420
- <div className="flex items-center gap-2 min-w-0">
447
+ <div className="flex items-center gap-2 min-w-0 flex-1 overflow-hidden">
421
448
  <Package2 className="w-4 h-4 text-blue-500 flex-shrink-0" />
422
- <div className="flex flex-col min-w-0">
423
- <span className="font-medium text-gray-900 truncate">
449
+ <div className="flex flex-col min-w-0 flex-1 overflow-hidden">
450
+ <span className="font-medium text-gray-900 truncate max-w-full">
424
451
  {type === "package"
425
452
  ? renderHighlighted(data, data.name)
426
453
  : data.name}
427
454
  </span>
428
455
  {data.description && (
429
- <span className="text-xs text-gray-500 truncate">
456
+ <span className="text-xs text-gray-500 truncate max-w-full">
430
457
  {data.description}
431
458
  </span>
432
459
  )}
433
460
  {type === "recent" && (
434
- <span className="text-xs text-gray-400">
461
+ <span className="text-xs text-gray-400 hidden sm:block truncate">
435
462
  {new Date(data.updated_at).toLocaleDateString()}
436
463
  </span>
437
464
  )}
438
465
  </div>
439
466
  </div>
440
- <div className="flex items-center gap-2 flex-shrink-0">
467
+ <div className="flex items-center gap-1 sm:gap-2 flex-shrink-0">
441
468
  <div className="flex items-center gap-1 text-gray-500">
442
469
  <Star className="w-3 h-3 fill-yellow-400 text-yellow-400" />
443
470
  <span className="text-xs">{data.star_count ?? 0}</span>
444
471
  </div>
445
- <span className="text-xs text-gray-500 bg-gray-100 px-1.5 py-0.5 rounded">
472
+ <span className="text-xs text-gray-500 bg-gray-100 px-1.5 py-0.5 rounded hidden sm:inline">
446
473
  package
447
474
  </span>
448
475
  {isSelected && <ArrowRight className="w-3 h-3 text-gray-400" />}
@@ -458,7 +485,7 @@ const CmdKMenu = () => {
458
485
  className={baseClasses}
459
486
  onClick={() => !disabled && handleItemSelect(item)}
460
487
  >
461
- <div className="flex items-center gap-2 min-w-0">
488
+ <div className="flex items-center gap-2 min-w-0 flex-1">
462
489
  <img
463
490
  src={`https://github.com/${data.github_username}.png`}
464
491
  alt={`${data.github_username} avatar`}
@@ -470,14 +497,14 @@ const CmdKMenu = () => {
470
497
  }}
471
498
  />
472
499
  <User className="w-6 h-6 text-gray-400 flex-shrink-0 hidden" />
473
- <div className="flex flex-col min-w-0">
500
+ <div className="flex flex-col min-w-0 flex-1">
474
501
  <span className="font-medium text-gray-900 truncate">
475
502
  {renderHighlighted(data, data.github_username)}
476
503
  </span>
477
504
  </div>
478
505
  </div>
479
506
  <div className="flex items-center gap-1 flex-shrink-0">
480
- <span className="text-xs text-gray-500 bg-gray-100 px-1.5 py-0.5 rounded">
507
+ <span className="text-xs text-gray-500 bg-gray-100 px-1.5 py-0.5 rounded hidden sm:inline">
481
508
  user
482
509
  </span>
483
510
  {isSelected && <ArrowRight className="w-3 h-3 text-gray-400" />}
@@ -494,14 +521,14 @@ const CmdKMenu = () => {
494
521
  className={baseClasses}
495
522
  onClick={() => !disabled && handleItemSelect(item)}
496
523
  >
497
- <div className="flex items-center gap-2">
524
+ <div className="flex items-center gap-2 min-w-0 flex-1">
498
525
  {data.icon}
499
- <span className="font-medium text-gray-900">
526
+ <span className="font-medium text-gray-900 truncate">
500
527
  {renderHighlighted(data, data.name)}
501
528
  </span>
502
529
  </div>
503
- <div className="flex items-center gap-1">
504
- <span className="text-xs text-gray-500 bg-gray-100 px-1.5 py-0.5 rounded">
530
+ <div className="flex items-center gap-1 flex-shrink-0">
531
+ <span className="text-xs text-gray-500 bg-gray-100 px-1.5 py-0.5 rounded hidden sm:inline">
505
532
  {data.type}
506
533
  </span>
507
534
  {isSelected && <ArrowRight className="w-3 h-3 text-gray-400" />}
@@ -517,14 +544,14 @@ const CmdKMenu = () => {
517
544
  className={baseClasses}
518
545
  onClick={() => handleItemSelect(item)}
519
546
  >
520
- <div className="flex items-center gap-2">
547
+ <div className="flex items-center gap-2 min-w-0 flex-1">
521
548
  {data.icon}
522
- <span className="font-medium text-gray-900">
549
+ <span className="font-medium text-gray-900 truncate">
523
550
  Import {renderHighlighted(data, data.name)}
524
551
  </span>
525
552
  </div>
526
- <div className="flex items-center gap-1">
527
- <span className="text-xs text-gray-500 bg-gray-100 px-1.5 py-0.5 rounded">
553
+ <div className="flex items-center gap-1 flex-shrink-0">
554
+ <span className="text-xs text-gray-500 bg-gray-100 px-1.5 py-0.5 rounded hidden sm:inline">
528
555
  {data.type}
529
556
  </span>
530
557
  {isSelected && <ArrowRight className="w-3 h-3 text-gray-400" />}
@@ -541,229 +568,236 @@ const CmdKMenu = () => {
541
568
 
542
569
  if (!open)
543
570
  return (
544
- <JLCPCBImportDialog
545
- open={isJLCPCBDialogOpen}
546
- onOpenChange={setIsJLCPCBDialogOpen}
547
- />
571
+ <>
572
+ <ImportComponentDialog
573
+ onJlcpcbComponentTsxLoaded={handleJlcpcbComponentSelected}
574
+ jlcpcbProxyRequestHeaders={jlcpcbProxyRequestHeaders}
575
+ />
576
+ <CircuitJsonImportDialog
577
+ open={isCircuitJsonImportDialogOpen}
578
+ onOpenChange={setIsCircuitJsonImportDialogOpen}
579
+ />
580
+ </>
548
581
  )
549
582
 
550
583
  return (
551
584
  <>
552
- <div
553
- className="fixed inset-0 bg-black/20 backdrop-blur-sm z-40"
554
- onClick={() => setOpen(false)}
555
- />
585
+ <Dialog open={open} onOpenChange={setOpen}>
586
+ <DialogContent className="max-w-xl lg:max-w-2xl w-[95vw] bg-white rounded-lg shadow-xl border border-gray-200 p-0 top-[10vh] translate-y-0">
587
+ <DialogTitle className="sr-only">Command Menu</DialogTitle>
588
+ <DialogDescription className="sr-only">
589
+ Use this menu to search packages and commands.
590
+ </DialogDescription>
591
+ <Command
592
+ className="w-full overflow-hidden"
593
+ loop={false}
594
+ onKeyDown={handleKeyDown}
595
+ >
596
+ <div className="flex items-center border-b border-gray-200 px-3 sm:px-4 py-3">
597
+ <Search className="w-4 h-4 mr-2 text-gray-400 flex-shrink-0" />
598
+ <Command.Input
599
+ placeholder="Search packages and commands..."
600
+ value={searchQuery}
601
+ onValueChange={setSearchQuery}
602
+ className="w-full bg-transparent border-none outline-none text-sm text-gray-900 placeholder-gray-500"
603
+ />
604
+ </div>
556
605
 
557
- <Command.Dialog
558
- open={open}
559
- onOpenChange={setOpen}
560
- label="Command Menu"
561
- className="fixed top-16 left-1/2 -translate-x-1/2 max-w-2xl w-[90vw] bg-white rounded-lg shadow-xl border border-gray-200 z-50"
562
- loop={false}
563
- onKeyDown={handleKeyDown}
564
- aria-describedby="dialog-description"
565
- >
566
- <DialogTitle className="sr-only">Command Menu</DialogTitle>
567
- <DialogDescription id="dialog-description" className="sr-only">
568
- Use this menu to search packages and commands.
569
- </DialogDescription>
570
- <div className="flex items-center border-b border-gray-200 px-4 py-3">
571
- <Search className="w-4 h-4 mr-2 text-gray-400 flex-shrink-0" />
572
- <Command.Input
573
- placeholder="Search packages and commands..."
574
- value={searchQuery}
575
- onValueChange={setSearchQuery}
576
- className="w-full bg-transparent border-none outline-none text-gray-900 placeholder-gray-500"
577
- />
578
- </div>
579
-
580
- <Command.List className="max-h-80 overflow-y-auto p-2 space-y-4">
581
- {isSearching || isSearchingAccounts ? (
582
- <Command.Loading className="p-6 text-center text-gray-500">
583
- <div className="flex items-center justify-center gap-2">
584
- <div className="w-3 h-3 border-2 border-gray-300 border-t-gray-600 rounded-full animate-spin"></div>
585
- Loading...
586
- </div>
587
- </Command.Loading>
588
- ) : (
589
- <>
590
- {searchQuery && searchResults.length > 0 && (
591
- <div>
592
- <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2 px-2">
593
- Packages
594
- </h3>
595
- <div className="space-y-1">
596
- {searchResults.slice(0, 8).map((pkg, localIndex) => {
597
- const globalIndex = localIndex
598
- return renderItem(
599
- { type: "package", item: pkg },
600
- globalIndex,
601
- )
602
- })}
606
+ <Command.List className="max-h-[60vh] sm:max-h-[50vh] overflow-y-auto p-2 sm:p-3 space-y-3 no-scrollbar">
607
+ {isSearching || isSearchingAccounts ? (
608
+ <Command.Loading className="p-6 text-center text-gray-500">
609
+ <div className="flex items-center justify-center gap-2">
610
+ <div className="w-3 h-3 border-2 border-gray-300 border-t-gray-600 rounded-full animate-spin"></div>
611
+ Loading...
603
612
  </div>
604
- </div>
605
- )}
613
+ </Command.Loading>
614
+ ) : (
615
+ <>
616
+ {searchQuery && searchResults.length > 0 && (
617
+ <div>
618
+ <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2 px-2">
619
+ Packages
620
+ </h3>
621
+ <div className="space-y-1">
622
+ {searchResults.slice(0, 8).map((pkg, localIndex) => {
623
+ const globalIndex = localIndex
624
+ return renderItem(
625
+ { type: "package", item: pkg },
626
+ globalIndex,
627
+ )
628
+ })}
629
+ </div>
630
+ </div>
631
+ )}
606
632
 
607
- {searchQuery && accountSearchResults.length > 0 && (
608
- <div>
609
- <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2 px-2">
610
- Users
611
- </h3>
612
- <div className="space-y-1">
613
- {accountSearchResults
614
- .slice(0, 5)
615
- .map((account, localIndex) => {
616
- const globalIndex = searchResults.length + localIndex
617
- return renderItem(
618
- { type: "account", item: account },
619
- globalIndex,
620
- )
621
- })}
622
- </div>
623
- </div>
624
- )}
633
+ {searchQuery && accountSearchResults.length > 0 && (
634
+ <div>
635
+ <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2 px-2">
636
+ Users
637
+ </h3>
638
+ <div className="space-y-1">
639
+ {accountSearchResults
640
+ .slice(0, 5)
641
+ .map((account, localIndex) => {
642
+ const globalIndex =
643
+ searchResults.length + localIndex
644
+ return renderItem(
645
+ { type: "account", item: account },
646
+ globalIndex,
647
+ )
648
+ })}
649
+ </div>
650
+ </div>
651
+ )}
625
652
 
626
- {!searchQuery && recentPackages.length > 0 && (
627
- <div>
628
- <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2 px-2 flex items-center gap-1">
629
- <Clock className="w-3 h-3" />
630
- Recent
631
- </h3>
632
- <div className="space-y-1">
633
- {recentPackages.slice(0, 6).map((pkg, localIndex) => {
634
- const globalIndex = localIndex
635
- return renderItem(
636
- { type: "recent", item: pkg },
637
- globalIndex,
638
- )
639
- })}
640
- </div>
641
- </div>
642
- )}
653
+ {!searchQuery && recentPackages.length > 0 && (
654
+ <div>
655
+ <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2 px-2 flex items-center gap-1">
656
+ <Clock className="w-3 h-3" />
657
+ Recent
658
+ </h3>
659
+ <div className="space-y-1">
660
+ {recentPackages.slice(0, 6).map((pkg, localIndex) => {
661
+ const globalIndex = localIndex
662
+ return renderItem(
663
+ { type: "recent", item: pkg },
664
+ globalIndex,
665
+ )
666
+ })}
667
+ </div>
668
+ </div>
669
+ )}
643
670
 
644
- {filteredStaticOptions.blankTemplates.length > 0 && (
645
- <div>
646
- <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2 px-2">
647
- Create
648
- </h3>
649
- <div className="space-y-1">
650
- {filteredStaticOptions.blankTemplates.map(
651
- (template, localIndex) => {
652
- const globalIndex =
653
- (searchQuery
654
- ? searchResults.length + accountSearchResults.length
655
- : recentPackages.length) + localIndex
656
- return renderItem(
657
- {
658
- type: "blank",
659
- item: template,
660
- disabled: template.disabled,
671
+ {filteredStaticOptions.blankTemplates.length > 0 && (
672
+ <div>
673
+ <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2 px-2">
674
+ Create
675
+ </h3>
676
+ <div className="space-y-1">
677
+ {filteredStaticOptions.blankTemplates.map(
678
+ (template, localIndex) => {
679
+ const globalIndex =
680
+ (searchQuery
681
+ ? searchResults.length +
682
+ accountSearchResults.length
683
+ : recentPackages.length) + localIndex
684
+ return renderItem(
685
+ {
686
+ type: "blank",
687
+ item: template,
688
+ disabled: template.disabled,
689
+ },
690
+ globalIndex,
691
+ )
661
692
  },
662
- globalIndex,
663
- )
664
- },
665
- )}
666
- </div>
667
- </div>
668
- )}
693
+ )}
694
+ </div>
695
+ </div>
696
+ )}
669
697
 
670
- {filteredStaticOptions.templates.length > 0 && (
671
- <div>
672
- <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2 px-2">
673
- Templates
674
- </h3>
675
- <div className="space-y-1">
676
- {filteredStaticOptions.templates.map(
677
- (template, localIndex) => {
678
- const globalIndex =
679
- (searchQuery
680
- ? searchResults.length + accountSearchResults.length
681
- : recentPackages.length) +
682
- filteredStaticOptions.blankTemplates.length +
683
- localIndex
684
- return renderItem(
685
- { type: "template", item: template },
686
- globalIndex,
687
- )
688
- },
689
- )}
690
- </div>
691
- </div>
692
- )}
698
+ {filteredStaticOptions.templates.length > 0 && (
699
+ <div>
700
+ <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2 px-2">
701
+ Templates
702
+ </h3>
703
+ <div className="space-y-1">
704
+ {filteredStaticOptions.templates.map(
705
+ (template, localIndex) => {
706
+ const globalIndex =
707
+ (searchQuery
708
+ ? searchResults.length +
709
+ accountSearchResults.length
710
+ : recentPackages.length) +
711
+ filteredStaticOptions.blankTemplates.length +
712
+ localIndex
713
+ return renderItem(
714
+ { type: "template", item: template },
715
+ globalIndex,
716
+ )
717
+ },
718
+ )}
719
+ </div>
720
+ </div>
721
+ )}
693
722
 
694
- {filteredStaticOptions.importOptions.length > 0 && (
695
- <div>
696
- <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2 px-2">
697
- Import
698
- </h3>
699
- <div className="space-y-1">
700
- {filteredStaticOptions.importOptions.map(
701
- (option, localIndex) => {
702
- const globalIndex =
703
- (searchQuery
704
- ? searchResults.length + accountSearchResults.length
705
- : recentPackages.length) +
706
- filteredStaticOptions.blankTemplates.length +
707
- filteredStaticOptions.templates.length +
708
- localIndex
709
- return renderItem(
710
- { type: "import", item: option },
711
- globalIndex,
712
- )
713
- },
723
+ {filteredStaticOptions.importOptions.length > 0 && (
724
+ <div>
725
+ <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2 px-2">
726
+ Import
727
+ </h3>
728
+ <div className="space-y-1">
729
+ {filteredStaticOptions.importOptions.map(
730
+ (option, localIndex) => {
731
+ const globalIndex =
732
+ (searchQuery
733
+ ? searchResults.length +
734
+ accountSearchResults.length
735
+ : recentPackages.length) +
736
+ filteredStaticOptions.blankTemplates.length +
737
+ filteredStaticOptions.templates.length +
738
+ localIndex
739
+ return renderItem(
740
+ { type: "import", item: option },
741
+ globalIndex,
742
+ )
743
+ },
744
+ )}
745
+ </div>
746
+ </div>
747
+ )}
748
+
749
+ {searchQuery &&
750
+ !searchResults.length &&
751
+ !accountSearchResults.length &&
752
+ !filteredStaticOptions.blankTemplates.length &&
753
+ !filteredStaticOptions.templates.length &&
754
+ !filteredStaticOptions.importOptions.length &&
755
+ !isSearching &&
756
+ !isSearchingAccounts && (
757
+ <Command.Empty className="py-8 text-center">
758
+ <div className="text-gray-400 mb-1">
759
+ No results found
760
+ </div>
761
+ <div className="text-gray-500 text-xs">
762
+ Try different search terms
763
+ </div>
764
+ </Command.Empty>
714
765
  )}
766
+ </>
767
+ )}
768
+ </Command.List>
769
+
770
+ <div className="border-t border-gray-200 px-2 py-2 bg-gray-50/50 rounded-b-lg">
771
+ <div className="flex justify-between items-center text-[11px] sm:text-xs text-gray-500">
772
+ <div className="flex items-center gap-1.5 sm:gap-3">
773
+ <div className="flex items-center gap-1">
774
+ <kbd className="px-1 py-0.5 font-mono bg-white border border-gray-200 shadow-sm rounded text-[10px] sm:text-xs">
775
+ ↑↓
776
+ </kbd>
777
+ <span className="hidden sm:inline">navigate</span>
778
+ </div>
779
+ <div className="flex items-center gap-1">
780
+ <kbd className="px-1 py-0.5 font-mono bg-white border border-gray-200 shadow-sm rounded text-[10px] sm:text-xs">
781
+
782
+ </kbd>
783
+ <span className="hidden sm:inline">select</span>
715
784
  </div>
716
785
  </div>
717
- )}
718
-
719
- {searchQuery &&
720
- !searchResults.length &&
721
- !accountSearchResults.length &&
722
- !filteredStaticOptions.blankTemplates.length &&
723
- !filteredStaticOptions.templates.length &&
724
- !filteredStaticOptions.importOptions.length &&
725
- !isSearching &&
726
- !isSearchingAccounts && (
727
- <Command.Empty className="py-8 text-center">
728
- <div className="text-gray-400 mb-1">No results found</div>
729
- <div className="text-gray-500 text-xs">
730
- Try different search terms
731
- </div>
732
- </Command.Empty>
733
- )}
734
- </>
735
- )}
736
- </Command.List>
737
-
738
- <div className="border-t border-gray-200 px-4 py-2 bg-gray-50/50 rounded-b-lg">
739
- <div className="flex justify-between items-center text-xs text-gray-500">
740
- <div className="flex items-center gap-4">
741
- <div className="flex items-center gap-1">
742
- <kbd className="px-1.5 py-0.5 font-mono bg-white border border-gray-300 rounded text-xs">
743
- ↑↓
744
- </kbd>
745
- <span>navigate</span>
746
- </div>
747
- <div className="flex items-center gap-1">
748
- <kbd className="px-1.5 py-0.5 font-mono bg-white border border-gray-300 rounded text-xs">
749
-
750
- </kbd>
751
- <span>select</span>
786
+ <div className="flex items-center gap-1">
787
+ <kbd className="px-1 py-0.5 font-mono bg-white border border-gray-200 shadow-sm rounded text-[10px] sm:text-xs">
788
+ ⌘K
789
+ </kbd>
790
+ <span className="hidden sm:inline">close</span>
791
+ </div>
752
792
  </div>
753
793
  </div>
754
- <div className="flex items-center gap-1">
755
- <kbd className="px-1.5 py-0.5 font-mono bg-white border border-gray-300 rounded text-xs">
756
- ⌘K
757
- </kbd>
758
- <span>close</span>
759
- </div>
760
- </div>
761
- </div>
762
- </Command.Dialog>
794
+ </Command>
795
+ </DialogContent>
796
+ </Dialog>
763
797
 
764
- <JLCPCBImportDialog
765
- open={isJLCPCBDialogOpen}
766
- onOpenChange={setIsJLCPCBDialogOpen}
798
+ <ImportComponentDialog
799
+ onJlcpcbComponentTsxLoaded={handleJlcpcbComponentSelected}
800
+ jlcpcbProxyRequestHeaders={jlcpcbProxyRequestHeaders}
767
801
  />
768
802
  </>
769
803
  )