@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.
- package/.github/workflows/bun-formatcheck.yml +2 -2
- package/.github/workflows/bun-pver-release.yml +3 -3
- package/.github/workflows/bun-test.yml +1 -1
- package/.github/workflows/bun-typecheck.yml +2 -2
- package/.github/workflows/update-snapshots.yml +1 -1
- package/README.md +4 -0
- package/api/generated-index.js +37 -3
- package/biome.json +2 -1
- package/bun-tests/fake-snippets-api/fixtures/get-test-server.ts +31 -3
- package/bun-tests/fake-snippets-api/fixtures/preload.ts +18 -0
- package/bun-tests/fake-snippets-api/routes/orgs/add_member.test.ts +26 -0
- package/bun-tests/fake-snippets-api/routes/orgs/create.test.ts +37 -0
- package/bun-tests/fake-snippets-api/routes/orgs/get.test.ts +52 -0
- package/bun-tests/fake-snippets-api/routes/orgs/list.test.ts +17 -0
- package/bun-tests/fake-snippets-api/routes/orgs/list_members.test.ts +23 -0
- package/bun-tests/fake-snippets-api/routes/orgs/remove_member.test.ts +81 -0
- package/bun-tests/fake-snippets-api/routes/orgs/update.test.ts +99 -0
- package/bun-tests/fake-snippets-api/routes/package_builds/get.test.ts +1 -1
- package/bun-tests/fake-snippets-api/routes/package_files/create.test.ts +15 -13
- package/bun-tests/fake-snippets-api/routes/package_files/create_or_update.test.ts +26 -24
- package/bun-tests/fake-snippets-api/routes/package_files/delete.test.ts +9 -9
- package/bun-tests/fake-snippets-api/routes/package_files/download.test.ts +4 -4
- package/bun-tests/fake-snippets-api/routes/package_files/get.test.ts +38 -28
- package/bun-tests/fake-snippets-api/routes/package_files/list.test.ts +23 -15
- package/bun-tests/fake-snippets-api/routes/package_releases/create.test.ts +33 -0
- package/bun-tests/fake-snippets-api/routes/package_releases/get.test.ts +4 -4
- package/bun-tests/fake-snippets-api/routes/package_releases/get_image_generation_fields.test.ts +38 -0
- package/bun-tests/fake-snippets-api/routes/packages/create.test.ts +19 -0
- package/bun-tests/fake-snippets-api/routes/packages/fork.test.ts +3 -4
- package/bun-tests/fake-snippets-api/routes/packages/get.test.ts +30 -0
- package/bun-tests/fake-snippets-api/routes/packages/images.test.ts +4 -2
- package/bun-tests/fake-snippets-api/routes/packages/list-1.test.ts +34 -0
- package/bun.lock +349 -453
- package/bunfig.toml +2 -1
- package/dist/bundle.js +1253 -624
- package/dist/index.d.ts +291 -4
- package/dist/index.js +323 -23
- package/dist/schema.d.ts +274 -1
- package/dist/schema.js +52 -1
- package/fake-snippets-api/lib/db/autoload-dev-packages.ts +31 -20
- package/fake-snippets-api/lib/db/db-client.ts +214 -3
- package/fake-snippets-api/lib/db/schema.ts +61 -0
- package/fake-snippets-api/lib/db/seed.ts +100 -0
- package/fake-snippets-api/lib/middleware/with-session-auth.ts +1 -1
- package/fake-snippets-api/lib/package_file/get-package-file-id-from-file-descriptor.ts +2 -2
- package/fake-snippets-api/lib/public-mapping/public-map-org.ts +32 -0
- package/fake-snippets-api/lib/public-mapping/public-map-package-build.ts +10 -0
- package/fake-snippets-api/lib/public-mapping/public-map-package-release.ts +17 -0
- package/fake-snippets-api/routes/api/orgs/add_member.ts +52 -0
- package/fake-snippets-api/routes/api/orgs/create.ts +46 -0
- package/fake-snippets-api/routes/api/orgs/get.ts +39 -0
- package/fake-snippets-api/routes/api/orgs/list.ts +31 -0
- package/fake-snippets-api/routes/api/orgs/list_members.ts +67 -0
- package/fake-snippets-api/routes/api/orgs/remove_member.ts +46 -0
- package/fake-snippets-api/routes/api/orgs/update.ts +93 -0
- package/fake-snippets-api/routes/api/package_files/get.ts +3 -6
- package/fake-snippets-api/routes/api/package_files/list.ts +7 -4
- package/fake-snippets-api/routes/api/packages/create.ts +54 -10
- package/fake-snippets-api/routes/api/packages/get.ts +23 -0
- package/fake-snippets-api/routes/api/packages/images/[owner_github_username]/[unscoped_name]/[view_format].ts +13 -11
- package/fake-snippets-api/routes/api/packages/list.ts +29 -2
- package/fake-snippets-api/routes/api/packages/update_ai_description.ts +37 -0
- package/package.json +24 -20
- package/renovate.json +1 -1
- package/scripts/generate-sitemap.ts +1 -1
- package/src/App.tsx +29 -8
- package/src/ContextProviders.tsx +25 -2
- package/src/components/CircuitJsonImportDialog.tsx +1 -1
- package/src/components/CmdKMenu.tsx +281 -247
- package/src/components/DownloadButtonAndMenu.tsx +3 -4
- package/src/components/FileSidebar.tsx +11 -17
- package/src/components/Footer.tsx +8 -9
- package/src/components/Header.tsx +19 -32
- package/src/components/Header2.tsx +16 -32
- package/src/components/HeaderDropdown.tsx +13 -8
- package/src/components/HeaderLogin.tsx +43 -15
- package/src/components/NotFound.tsx +5 -5
- package/src/components/PackageBreadcrumb.tsx +6 -12
- package/src/components/PackageSearchResults.tsx +1 -1
- package/src/components/PrefetchPageLink.tsx +7 -1
- package/src/components/ProfileRouter.tsx +32 -0
- package/src/components/SearchComponent.tsx +12 -8
- package/src/components/UserCard.tsx +80 -0
- package/src/components/ViewPackagePage/components/build-status.tsx +1 -1
- package/src/components/ViewPackagePage/components/important-files-view.tsx +105 -34
- package/src/components/ViewPackagePage/components/main-content-header.tsx +10 -6
- package/src/components/ViewPackagePage/components/main-content-view-selector.tsx +1 -1
- package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +54 -19
- package/src/components/ViewPackagePage/components/package-header.tsx +25 -33
- package/src/components/ViewPackagePage/components/preview-image-squares.tsx +11 -18
- package/src/components/ViewPackagePage/components/repo-page-content.tsx +12 -5
- package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +16 -10
- package/src/components/ViewPackagePage/components/sidebar-releases-section.tsx +11 -11
- package/src/components/ViewPackagePage/components/tab-views/pcb-view.tsx +1 -2
- package/src/components/ViewPackagePage/components/tab-views/schematic-view.tsx +2 -1
- package/src/components/dialogs/GitHubRepositorySelector.tsx +56 -49
- package/src/components/dialogs/edit-package-details-dialog.tsx +5 -6
- package/src/components/dialogs/import-component-dialog.tsx +16 -9
- package/src/components/dialogs/import-package-dialog.tsx +3 -2
- package/src/components/dialogs/new-package-save-prompt-dialog.tsx +190 -0
- package/src/components/organization/OrganizationCard.tsx +204 -0
- package/src/components/organization/OrganizationCardSkeleton.tsx +55 -0
- package/src/components/organization/OrganizationHeader.tsx +154 -0
- package/src/components/organization/OrganizationMembers.tsx +146 -0
- package/src/components/package-port/CodeAndPreview.tsx +15 -12
- package/src/components/package-port/CodeEditor.tsx +4 -30
- package/src/components/package-port/CodeEditorHeader.tsx +123 -61
- package/src/components/package-port/EditorNav.tsx +32 -49
- package/src/components/preview/ConnectedPackagesList.tsx +8 -8
- package/src/components/preview/ConnectedRepoOverview.tsx +102 -2
- package/src/components/preview/PackageReleasesDashboard.tsx +23 -11
- package/src/components/ui/tree-view.tsx +6 -3
- package/src/hooks/use-add-org-member-mutation.ts +51 -0
- package/src/hooks/use-create-org-mutation.ts +38 -0
- package/src/hooks/use-create-package-mutation.ts +3 -0
- package/src/hooks/use-current-package-release.ts +4 -3
- package/src/hooks/use-download-zip.ts +2 -2
- package/src/hooks/use-global-store.ts +6 -4
- package/src/hooks/use-jlcpcb-component-import.tsx +164 -0
- package/src/hooks/use-list-org-members.ts +27 -0
- package/src/hooks/use-list-user-orgs.ts +25 -0
- package/src/hooks/use-org-by-github-handle.ts +26 -0
- package/src/hooks/use-org.ts +24 -0
- package/src/hooks/use-organization.ts +42 -0
- package/src/hooks/use-package-as-snippet.ts +4 -2
- package/src/hooks/use-package-builds.ts +6 -2
- package/src/hooks/use-package-files.ts +5 -3
- package/src/hooks/use-package-release-by-id-or-version.ts +29 -20
- package/src/hooks/use-package-release-images.ts +105 -0
- package/src/hooks/use-package-release.ts +2 -2
- package/src/hooks/use-package-stars.ts +80 -4
- package/src/hooks/use-preview-images.ts +6 -3
- package/src/hooks/use-remove-org-member-mutation.ts +32 -0
- package/src/hooks/use-update-ai-description-mutation.ts +42 -0
- package/src/hooks/use-update-org-mutation.ts +41 -0
- package/src/hooks/use-warn-user-on-page-change.ts +71 -4
- package/src/hooks/useFileManagement.ts +51 -22
- package/src/hooks/useOptimizedPackageFilesLoader.ts +11 -24
- package/src/hooks/usePackageFilesLoader.ts +2 -2
- package/src/hooks/useUpdatePackageFilesMutation.ts +13 -1
- package/src/lib/download-fns/download-gltf-from-circuit-json.ts +1 -1
- package/src/lib/download-fns/download-kicad-files.ts +12 -11
- package/src/lib/normalize-svg-for-tile.ts +50 -0
- package/src/lib/posthog.ts +11 -9
- package/src/lib/react-query-api-failure-tracking.ts +148 -0
- package/src/lib/sentry.ts +14 -0
- package/src/lib/templates/blank-circuit-board-template.ts +0 -4
- package/src/lib/ts-lib-cache.ts +122 -7
- package/src/lib/utils/checkIfManualEditsImported.ts +4 -4
- package/src/lib/utils/findTargetFile.ts +45 -10
- package/src/lib/utils/isComponentExported.ts +2 -1
- package/src/main.tsx +2 -1
- package/src/pages/create-organization.tsx +168 -0
- package/src/pages/dashboard.tsx +38 -6
- package/src/pages/datasheet.tsx +1 -1
- package/src/pages/datasheets.tsx +3 -3
- package/src/pages/editor.tsx +4 -6
- package/src/pages/landing.tsx +6 -6
- package/src/pages/latest.tsx +3 -0
- package/src/pages/organization-profile.tsx +199 -0
- package/src/pages/organization-settings.tsx +566 -0
- package/src/pages/package-editor.tsx +21 -21
- package/src/pages/preview-release.tsx +75 -145
- package/src/pages/quickstart.tsx +159 -123
- package/src/pages/release-detail.tsx +119 -31
- package/src/pages/search.tsx +192 -57
- package/src/pages/settings-redirect.tsx +44 -0
- package/src/pages/trending.tsx +29 -20
- package/src/pages/user-profile.tsx +58 -7
- package/src/pages/view-package.tsx +7 -13
- package/vite.config.ts +9 -0
- package/fake-snippets-api/routes/api/autocomplete/create_autocomplete.ts +0 -133
- package/src/components/JLCPCBImportDialog.tsx +0 -280
- package/src/components/PackageBuildsPage/LogContent.tsx +0 -72
- package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +0 -113
- package/src/components/PackageBuildsPage/build-preview-content.tsx +0 -56
- package/src/components/PackageBuildsPage/collapsible-section.tsx +0 -63
- package/src/components/PackageBuildsPage/package-build-details-panel.tsx +0 -166
- package/src/components/PackageBuildsPage/package-build-header.tsx +0 -79
- package/src/components/PageSearchComponent.tsx +0 -148
- package/src/pages/package-builds.tsx +0 -33
|
@@ -3,7 +3,7 @@ import { Badge } from "@/components/ui/badge"
|
|
|
3
3
|
import { Button } from "@/components/ui/button"
|
|
4
4
|
import { GitBranch, Rocket, Github } from "lucide-react"
|
|
5
5
|
import { cn } from "@/lib/utils"
|
|
6
|
-
import {
|
|
6
|
+
import { Link } from "wouter"
|
|
7
7
|
import { formatTimeAgo } from "@/lib/utils/formatTimeAgo"
|
|
8
8
|
import { getBuildStatus, StatusIcon } from "."
|
|
9
9
|
import { Package } from "fake-snippets-api/lib/db/schema"
|
|
@@ -116,12 +116,12 @@ export const ConnectedPackageCard = ({
|
|
|
116
116
|
>
|
|
117
117
|
<div className="flex items-start justify-between mb-4">
|
|
118
118
|
<div className="flex items-center gap-3">
|
|
119
|
-
<
|
|
120
|
-
href=
|
|
119
|
+
<Link
|
|
120
|
+
href={`/${pkg.owner_github_username}/${pkg.unscoped_name}`}
|
|
121
121
|
className="text-lg font-semibold text-gray-900 hover:text-blue-600 transition-colors"
|
|
122
122
|
>
|
|
123
123
|
{pkg.unscoped_name}
|
|
124
|
-
</
|
|
124
|
+
</Link>
|
|
125
125
|
</div>
|
|
126
126
|
|
|
127
127
|
<div className="flex items-center justify-center gap-2">
|
|
@@ -174,25 +174,25 @@ export const ConnectedPackageCard = ({
|
|
|
174
174
|
</div>
|
|
175
175
|
|
|
176
176
|
<div className="flex gap-2 w-full mt-auto">
|
|
177
|
-
<
|
|
177
|
+
<Link className="w-full" href={`/${pkg.name}/releases`}>
|
|
178
178
|
<Button
|
|
179
179
|
size="sm"
|
|
180
180
|
className="bg-blue-600 w-full hover:bg-blue-700 text-white px-4 py-2"
|
|
181
181
|
>
|
|
182
182
|
View
|
|
183
183
|
</Button>
|
|
184
|
-
</
|
|
184
|
+
</Link>
|
|
185
185
|
{latestBuildInfo?.preview_url &&
|
|
186
186
|
latestBuildInfo?.package_build_id &&
|
|
187
187
|
status === "success" && (
|
|
188
|
-
<
|
|
188
|
+
<Link
|
|
189
189
|
className="w-full"
|
|
190
190
|
href={`/${pkg.name}/releases/${latestBuildInfo.package_release_id}/preview`}
|
|
191
191
|
>
|
|
192
192
|
<Button size="sm" variant="outline" className="px-4 py-2 w-full">
|
|
193
193
|
Preview
|
|
194
194
|
</Button>
|
|
195
|
-
</
|
|
195
|
+
</Link>
|
|
196
196
|
)}
|
|
197
197
|
</div>
|
|
198
198
|
</Card>
|
|
@@ -41,6 +41,7 @@ export const ConnectedRepoOverview = ({
|
|
|
41
41
|
const [openSections, setOpenSections] = useState({
|
|
42
42
|
transpilation: false,
|
|
43
43
|
circuitJson: false,
|
|
44
|
+
imageGeneration: false,
|
|
44
45
|
})
|
|
45
46
|
|
|
46
47
|
// Gracefully handle when there is no build yet
|
|
@@ -120,15 +121,25 @@ export const ConnectedRepoOverview = ({
|
|
|
120
121
|
1000,
|
|
121
122
|
)
|
|
122
123
|
: 0
|
|
124
|
+
const imageGenerationDuration = packageBuild?.image_generation_started_at
|
|
125
|
+
? Math.floor(
|
|
126
|
+
(new Date(
|
|
127
|
+
packageBuild.image_generation_completed_at || new Date(),
|
|
128
|
+
).getTime() -
|
|
129
|
+
new Date(packageBuild.image_generation_started_at).getTime()) /
|
|
130
|
+
1000,
|
|
131
|
+
)
|
|
132
|
+
: 0
|
|
123
133
|
|
|
124
134
|
if (
|
|
125
135
|
!packageBuild?.transpilation_started_at &&
|
|
126
|
-
!packageBuild?.circuit_json_build_started_at
|
|
136
|
+
!packageBuild?.circuit_json_build_started_at &&
|
|
137
|
+
!packageBuild?.image_generation_started_at
|
|
127
138
|
) {
|
|
128
139
|
return null
|
|
129
140
|
}
|
|
130
141
|
|
|
131
|
-
return transpilationDuration + circuitJsonDuration
|
|
142
|
+
return transpilationDuration + circuitJsonDuration + imageGenerationDuration
|
|
132
143
|
})()
|
|
133
144
|
const toggleSection = (section: keyof typeof openSections) => {
|
|
134
145
|
setOpenSections((prev) => ({ ...prev, [section]: !prev[section] }))
|
|
@@ -482,6 +493,95 @@ export const ConnectedRepoOverview = ({
|
|
|
482
493
|
</div>
|
|
483
494
|
</CollapsibleContent>
|
|
484
495
|
</Collapsible>
|
|
496
|
+
|
|
497
|
+
<Collapsible
|
|
498
|
+
open={openSections.imageGeneration}
|
|
499
|
+
onOpenChange={() => toggleSection("imageGeneration")}
|
|
500
|
+
>
|
|
501
|
+
<CollapsibleTrigger asChild>
|
|
502
|
+
<div className="flex items-center justify-between p-4 bg-white border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-50">
|
|
503
|
+
<div className="flex items-center gap-3">
|
|
504
|
+
<ChevronRight
|
|
505
|
+
className={`w-4 h-4 transition-transform ${openSections.imageGeneration ? "rotate-90" : ""}`}
|
|
506
|
+
/>
|
|
507
|
+
{packageBuild.image_generation_error ? (
|
|
508
|
+
<AlertCircle className="w-5 h-5 text-red-500" />
|
|
509
|
+
) : packageBuild.image_generation_completed_at ? (
|
|
510
|
+
<CheckCircle className="w-5 h-5 text-green-500" />
|
|
511
|
+
) : packageBuild.image_generation_in_progress ? (
|
|
512
|
+
<Loader2 className="w-5 h-5 text-blue-500 animate-spin" />
|
|
513
|
+
) : (
|
|
514
|
+
<Clock className="w-5 h-5 text-gray-400" />
|
|
515
|
+
)}
|
|
516
|
+
<span className="font-medium">Image Generation</span>
|
|
517
|
+
</div>
|
|
518
|
+
<div className="flex items-center gap-2">
|
|
519
|
+
{getStepDuration(
|
|
520
|
+
packageBuild.image_generation_started_at,
|
|
521
|
+
packageBuild.image_generation_completed_at,
|
|
522
|
+
) && (
|
|
523
|
+
<span className="text-sm text-gray-600">
|
|
524
|
+
{getStepDuration(
|
|
525
|
+
packageBuild.image_generation_started_at,
|
|
526
|
+
packageBuild.image_generation_completed_at,
|
|
527
|
+
)}
|
|
528
|
+
</span>
|
|
529
|
+
)}
|
|
530
|
+
<Badge
|
|
531
|
+
variant={
|
|
532
|
+
getStepStatus(
|
|
533
|
+
packageBuild.image_generation_error,
|
|
534
|
+
packageBuild.image_generation_completed_at,
|
|
535
|
+
packageBuild.image_generation_in_progress,
|
|
536
|
+
) === "success"
|
|
537
|
+
? "default"
|
|
538
|
+
: getStepStatus(
|
|
539
|
+
packageBuild.image_generation_error,
|
|
540
|
+
packageBuild.image_generation_completed_at,
|
|
541
|
+
packageBuild.image_generation_in_progress,
|
|
542
|
+
) === "error"
|
|
543
|
+
? "destructive"
|
|
544
|
+
: "secondary"
|
|
545
|
+
}
|
|
546
|
+
className="text-xs"
|
|
547
|
+
>
|
|
548
|
+
{packageBuild.image_generation_error
|
|
549
|
+
? "Failed"
|
|
550
|
+
: packageBuild.image_generation_completed_at
|
|
551
|
+
? "Completed"
|
|
552
|
+
: packageBuild.image_generation_in_progress
|
|
553
|
+
? "Running"
|
|
554
|
+
: "Queued"}
|
|
555
|
+
</Badge>
|
|
556
|
+
</div>
|
|
557
|
+
</div>
|
|
558
|
+
</CollapsibleTrigger>
|
|
559
|
+
<CollapsibleContent>
|
|
560
|
+
<div className="bg-white border-x border-b border-gray-200 rounded-b-lg p-4">
|
|
561
|
+
<div className="font-mono text-xs space-y-1">
|
|
562
|
+
{packageBuild.image_generation_error ? (
|
|
563
|
+
<div className="text-red-600 whitespace-pre-wrap">
|
|
564
|
+
{packageBuild.image_generation_error}
|
|
565
|
+
</div>
|
|
566
|
+
) : packageBuild.image_generation_logs &&
|
|
567
|
+
packageBuild.image_generation_logs.length > 0 ? (
|
|
568
|
+
packageBuild.image_generation_logs.map(
|
|
569
|
+
(log: any, i: number) => (
|
|
570
|
+
<div
|
|
571
|
+
key={i}
|
|
572
|
+
className="text-gray-600 whitespace-pre-wrap"
|
|
573
|
+
>
|
|
574
|
+
{log.msg || log.message || JSON.stringify(log)}
|
|
575
|
+
</div>
|
|
576
|
+
),
|
|
577
|
+
)
|
|
578
|
+
) : (
|
|
579
|
+
<div className="text-gray-500">No logs available</div>
|
|
580
|
+
)}
|
|
581
|
+
</div>
|
|
582
|
+
</div>
|
|
583
|
+
</CollapsibleContent>
|
|
584
|
+
</Collapsible>
|
|
485
585
|
</div>
|
|
486
586
|
</div>
|
|
487
587
|
)
|
|
@@ -8,18 +8,20 @@ import {
|
|
|
8
8
|
} from "@/components/ui/dropdown-menu"
|
|
9
9
|
import { MoreHorizontal, Clock, GitBranch, Eye } from "lucide-react"
|
|
10
10
|
import { GitHubLogoIcon } from "@radix-ui/react-icons"
|
|
11
|
-
import { useLocation } from "wouter"
|
|
12
11
|
import { BuildsList } from "./BuildsList"
|
|
13
12
|
import Header from "../Header"
|
|
14
13
|
import { formatTimeAgo } from "@/lib/utils/formatTimeAgo"
|
|
15
14
|
import { getBuildStatus } from "."
|
|
16
|
-
import {
|
|
15
|
+
import { Link } from "wouter"
|
|
17
16
|
import { PackageBreadcrumb } from "../PackageBreadcrumb"
|
|
18
17
|
import {
|
|
19
18
|
Package,
|
|
20
19
|
PackageBuild,
|
|
21
20
|
PackageRelease,
|
|
22
21
|
} from "fake-snippets-api/lib/db/schema"
|
|
22
|
+
import { useDownloadZip } from "@/hooks/use-download-zip"
|
|
23
|
+
import { useToast } from "@/hooks/use-toast"
|
|
24
|
+
import { usePackageFiles } from "@/hooks/use-package-files"
|
|
23
25
|
|
|
24
26
|
export const PackageReleasesDashboard = ({
|
|
25
27
|
latestRelease,
|
|
@@ -30,9 +32,21 @@ export const PackageReleasesDashboard = ({
|
|
|
30
32
|
latestBuild: PackageBuild | null
|
|
31
33
|
pkg: Package
|
|
32
34
|
}) => {
|
|
33
|
-
const [, setLocation] = useLocation()
|
|
34
35
|
const { status, label } = getBuildStatus(latestBuild)
|
|
35
|
-
|
|
36
|
+
const { toastLibrary } = useToast()
|
|
37
|
+
const { downloadZip } = useDownloadZip()
|
|
38
|
+
const { data: packageFiles } = usePackageFiles(
|
|
39
|
+
latestRelease.package_release_id,
|
|
40
|
+
)
|
|
41
|
+
const handleDownloadZip = () => {
|
|
42
|
+
if (pkg && packageFiles) {
|
|
43
|
+
toastLibrary.promise(downloadZip(pkg, packageFiles ?? []), {
|
|
44
|
+
loading: "Downloading ZIP...",
|
|
45
|
+
success: "ZIP downloaded successfully!",
|
|
46
|
+
error: "Failed to download ZIP",
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
}
|
|
36
50
|
return (
|
|
37
51
|
<>
|
|
38
52
|
<Header />
|
|
@@ -59,12 +73,12 @@ export const PackageReleasesDashboard = ({
|
|
|
59
73
|
</div>
|
|
60
74
|
<div className="min-w-0 flex-1">
|
|
61
75
|
<div className="flex flex-col sm:flex-row sm:items-center gap-3">
|
|
62
|
-
<
|
|
76
|
+
<Link
|
|
63
77
|
href={"/" + pkg.name}
|
|
64
78
|
className="text-2xl font-bold text-gray-900 truncate"
|
|
65
79
|
>
|
|
66
80
|
{pkg.name}
|
|
67
|
-
</
|
|
81
|
+
</Link>
|
|
68
82
|
<Badge
|
|
69
83
|
variant={
|
|
70
84
|
status === "success"
|
|
@@ -139,7 +153,7 @@ export const PackageReleasesDashboard = ({
|
|
|
139
153
|
<span className="sm:hidden">Repository</span>
|
|
140
154
|
</Button>
|
|
141
155
|
)}
|
|
142
|
-
{latestBuild && (
|
|
156
|
+
{latestBuild && status !== "error" && (
|
|
143
157
|
<Button
|
|
144
158
|
variant="outline"
|
|
145
159
|
size="sm"
|
|
@@ -178,10 +192,8 @@ export const PackageReleasesDashboard = ({
|
|
|
178
192
|
</a>
|
|
179
193
|
</DropdownMenuItem>
|
|
180
194
|
)}
|
|
181
|
-
<DropdownMenuItem asChild>
|
|
182
|
-
<
|
|
183
|
-
Download Build
|
|
184
|
-
</a>
|
|
195
|
+
<DropdownMenuItem asChild onClick={handleDownloadZip}>
|
|
196
|
+
<span>Download Zip</span>
|
|
185
197
|
</DropdownMenuItem>
|
|
186
198
|
<DropdownMenuItem
|
|
187
199
|
onClick={() => {
|
|
@@ -317,9 +317,12 @@ const TreeNode = ({
|
|
|
317
317
|
default={defaultNodeIcon}
|
|
318
318
|
/>
|
|
319
319
|
<span className="text-sm truncate">{item.name}</span>
|
|
320
|
-
<
|
|
321
|
-
|
|
322
|
-
|
|
320
|
+
<div
|
|
321
|
+
className="flex items-center"
|
|
322
|
+
onClick={(e) => e.stopPropagation()}
|
|
323
|
+
>
|
|
324
|
+
<TreeActions isSelected={true}>{item.actions}</TreeActions>
|
|
325
|
+
</div>
|
|
323
326
|
</AccordionTrigger>
|
|
324
327
|
<AccordionContent className="ml-4 pl-1 border-l">
|
|
325
328
|
<TreeItem
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { useMutation, useQueryClient } from "react-query"
|
|
2
|
+
import { useAxios } from "@/hooks/use-axios"
|
|
3
|
+
import { useGlobalStore } from "@/hooks/use-global-store"
|
|
4
|
+
|
|
5
|
+
export const useAddOrgMemberMutation = ({
|
|
6
|
+
onSuccess,
|
|
7
|
+
onError,
|
|
8
|
+
}: { onSuccess?: () => void; onError?: (error: any) => void } = {}) => {
|
|
9
|
+
const axios = useAxios()
|
|
10
|
+
const session = useGlobalStore((s) => s.session)
|
|
11
|
+
const queryClient = useQueryClient()
|
|
12
|
+
|
|
13
|
+
return useMutation(
|
|
14
|
+
["addOrgMember"],
|
|
15
|
+
async ({
|
|
16
|
+
orgId,
|
|
17
|
+
accountId,
|
|
18
|
+
githubUsername,
|
|
19
|
+
}: {
|
|
20
|
+
orgId: string
|
|
21
|
+
accountId?: string
|
|
22
|
+
githubUsername?: string
|
|
23
|
+
}) => {
|
|
24
|
+
if (!session) throw new Error("No session")
|
|
25
|
+
|
|
26
|
+
const payload: any = {
|
|
27
|
+
org_id: orgId,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (accountId) {
|
|
31
|
+
payload.account_id = accountId
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (githubUsername) {
|
|
35
|
+
payload.github_username = githubUsername
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
await axios.post("/orgs/add_member", payload)
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
onSuccess: () => {
|
|
42
|
+
queryClient.invalidateQueries(["orgs", "members"])
|
|
43
|
+
onSuccess?.()
|
|
44
|
+
},
|
|
45
|
+
onError: (error: any) => {
|
|
46
|
+
console.error("Error adding organization member:", error)
|
|
47
|
+
onError?.(error)
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
)
|
|
51
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useMutation } from "react-query"
|
|
2
|
+
import { useAxios } from "./use-axios"
|
|
3
|
+
import { useGlobalStore } from "./use-global-store"
|
|
4
|
+
import type { PublicOrgSchema } from "fake-snippets-api/lib/db/schema"
|
|
5
|
+
|
|
6
|
+
export const useCreateOrgMutation = ({
|
|
7
|
+
onSuccess,
|
|
8
|
+
}: { onSuccess?: (org: PublicOrgSchema) => void } = {}) => {
|
|
9
|
+
const axios = useAxios()
|
|
10
|
+
const session = useGlobalStore((s) => s.session)
|
|
11
|
+
|
|
12
|
+
return useMutation(
|
|
13
|
+
["createOrg"],
|
|
14
|
+
async ({ name }: { name: string }) => {
|
|
15
|
+
if (!session) throw new Error("No session")
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
data: { org: newOrg },
|
|
19
|
+
} = await axios.post("/orgs/create", {
|
|
20
|
+
name,
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
if (!newOrg) {
|
|
24
|
+
throw new Error("Failed to create organization")
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return newOrg
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
onSuccess: (org: PublicOrgSchema) => {
|
|
31
|
+
onSuccess?.(org)
|
|
32
|
+
},
|
|
33
|
+
onError: (error: any) => {
|
|
34
|
+
console.error("Error creating organization:", error)
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
)
|
|
38
|
+
}
|
|
@@ -16,11 +16,13 @@ export const useCreatePackageMutation = ({
|
|
|
16
16
|
description,
|
|
17
17
|
is_private,
|
|
18
18
|
is_unlisted,
|
|
19
|
+
org_id,
|
|
19
20
|
}: {
|
|
20
21
|
name?: string
|
|
21
22
|
description?: string
|
|
22
23
|
is_private?: boolean
|
|
23
24
|
is_unlisted?: boolean
|
|
25
|
+
org_id?: string
|
|
24
26
|
}) => {
|
|
25
27
|
if (!session) throw new Error("No session")
|
|
26
28
|
|
|
@@ -31,6 +33,7 @@ export const useCreatePackageMutation = ({
|
|
|
31
33
|
description,
|
|
32
34
|
is_private,
|
|
33
35
|
is_unlisted,
|
|
36
|
+
org_id,
|
|
34
37
|
})
|
|
35
38
|
|
|
36
39
|
if (!newPackage) {
|
|
@@ -21,12 +21,13 @@ export const useCurrentPackageRelease = (options?: {
|
|
|
21
21
|
|
|
22
22
|
let query: Parameters<typeof usePackageRelease>[0] | null = null
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
// Prioritize package_name + is_latest for better caching consistency
|
|
25
|
+
if (author && packageName && !version && !releaseId) {
|
|
26
|
+
query = { package_name: `${author}/${packageName}`, is_latest: true }
|
|
27
|
+
} else if (releaseId) {
|
|
25
28
|
query = { package_release_id: releaseId }
|
|
26
29
|
} else if (version && author && packageName) {
|
|
27
30
|
query = { package_name_with_version: `${author}/${packageName}@${version}` }
|
|
28
|
-
} else if (author && packageName) {
|
|
29
|
-
query = { package_name: `${author}/${packageName}`, is_latest: true }
|
|
30
31
|
} else if (packageId) {
|
|
31
32
|
query = { package_id: packageId, is_latest: true }
|
|
32
33
|
}
|
|
@@ -20,8 +20,8 @@ export const useDownloadZip = () => {
|
|
|
20
20
|
|
|
21
21
|
for (const file of visibleFiles) {
|
|
22
22
|
try {
|
|
23
|
-
const response = await axios.
|
|
24
|
-
package_file_id: file.package_file_id,
|
|
23
|
+
const response = await axios.get("/package_files/get", {
|
|
24
|
+
params: { package_file_id: file.package_file_id },
|
|
25
25
|
})
|
|
26
26
|
|
|
27
27
|
const content = response.data.package_file?.content_text || ""
|
|
@@ -28,7 +28,9 @@ export const useGlobalStore = create<Store>()(
|
|
|
28
28
|
),
|
|
29
29
|
)
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
if (typeof window !== "undefined") {
|
|
32
|
+
useGlobalStore.subscribe((state) => {
|
|
33
|
+
;(window as any).globalStore = state
|
|
34
|
+
window.TSCIRCUIT_REGISTRY_TOKEN = state.session?.token ?? null
|
|
35
|
+
})
|
|
36
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import React, { useCallback } from "react"
|
|
2
|
+
import { useToast } from "@/hooks/use-toast"
|
|
3
|
+
import { useGlobalStore } from "@/hooks/use-global-store"
|
|
4
|
+
import { useLocation } from "wouter"
|
|
5
|
+
import { useCreatePackageMutation } from "@/hooks/use-create-package-mutation"
|
|
6
|
+
import { useCreatePackageReleaseMutation } from "@/hooks/use-create-package-release-mutation"
|
|
7
|
+
import { useCreatePackageFilesMutation } from "@/hooks/use-create-package-files-mutation"
|
|
8
|
+
import { useAxios } from "@/hooks/use-axios"
|
|
9
|
+
import { JlcpcbComponentTsxLoadedPayload } from "@tscircuit/runframe/runner"
|
|
10
|
+
|
|
11
|
+
export const useJlcpcbComponentImport = () => {
|
|
12
|
+
const { toastLibrary } = useToast()
|
|
13
|
+
const session = useGlobalStore((s) => s.session)
|
|
14
|
+
const [, navigate] = useLocation()
|
|
15
|
+
const axios = useAxios()
|
|
16
|
+
const createPackageMutation = useCreatePackageMutation()
|
|
17
|
+
const createReleaseMutation = useCreatePackageReleaseMutation()
|
|
18
|
+
const createFilesMutation = useCreatePackageFilesMutation()
|
|
19
|
+
|
|
20
|
+
const runImport = useCallback(
|
|
21
|
+
async ({ result, tsx }: JlcpcbComponentTsxLoadedPayload) => {
|
|
22
|
+
if (!session) {
|
|
23
|
+
throw new Error("You must be logged in to import from JLCPCB")
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const partNumber = result.component.partNumber || "component"
|
|
27
|
+
|
|
28
|
+
const normalizePartNumber = (input: string) =>
|
|
29
|
+
input
|
|
30
|
+
.replace(/^@/, "")
|
|
31
|
+
.trim()
|
|
32
|
+
.replace(/\s+/g, "-")
|
|
33
|
+
.replace(/[^a-zA-Z0-9-_/]/g, "-")
|
|
34
|
+
.replace(/--+/g, "-")
|
|
35
|
+
.replace(/-+$/g, "")
|
|
36
|
+
.replace(/^-+/g, "") || "component"
|
|
37
|
+
|
|
38
|
+
const componentSlug = normalizePartNumber(partNumber)
|
|
39
|
+
const packageName = `${session.github_username}/${componentSlug}`
|
|
40
|
+
|
|
41
|
+
const fetchExistingPackage = async () => {
|
|
42
|
+
try {
|
|
43
|
+
const { data } = await axios.post("/packages/get", {
|
|
44
|
+
name: packageName,
|
|
45
|
+
})
|
|
46
|
+
return data.package
|
|
47
|
+
} catch (error: any) {
|
|
48
|
+
const status = error?.response?.status || error?.status
|
|
49
|
+
if (status === 404) return null
|
|
50
|
+
throw error
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const existingPackage = await fetchExistingPackage()
|
|
55
|
+
if (existingPackage) {
|
|
56
|
+
navigate(`/editor?package_id=${existingPackage.package_id}`)
|
|
57
|
+
return {
|
|
58
|
+
partNumber,
|
|
59
|
+
packageId: existingPackage.package_id,
|
|
60
|
+
existing: true,
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const description =
|
|
65
|
+
result.component.description ||
|
|
66
|
+
`Generated from JLCPCB part number ${partNumber}`
|
|
67
|
+
|
|
68
|
+
let newPackage
|
|
69
|
+
try {
|
|
70
|
+
newPackage = await createPackageMutation.mutateAsync({
|
|
71
|
+
name: packageName,
|
|
72
|
+
description,
|
|
73
|
+
})
|
|
74
|
+
} catch (error) {
|
|
75
|
+
const fallbackPackage = await fetchExistingPackage()
|
|
76
|
+
if (fallbackPackage) {
|
|
77
|
+
navigate(`/editor?package_id=${fallbackPackage.package_id}`)
|
|
78
|
+
return {
|
|
79
|
+
partNumber,
|
|
80
|
+
packageId: fallbackPackage.package_id,
|
|
81
|
+
existing: true,
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
throw error
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const release = await createReleaseMutation.mutateAsync({
|
|
88
|
+
package_id: newPackage.package_id,
|
|
89
|
+
version: "0.1.0",
|
|
90
|
+
is_latest: true,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
await createFilesMutation.mutateAsync({
|
|
94
|
+
file_path: "index.tsx",
|
|
95
|
+
content_text: tsx,
|
|
96
|
+
package_release_id: release.package_release_id,
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
navigate(`/editor?package_id=${newPackage.package_id}`)
|
|
100
|
+
return {
|
|
101
|
+
partNumber,
|
|
102
|
+
packageId: newPackage.package_id,
|
|
103
|
+
existing: false,
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
[
|
|
107
|
+
createFilesMutation,
|
|
108
|
+
createPackageMutation,
|
|
109
|
+
createReleaseMutation,
|
|
110
|
+
axios,
|
|
111
|
+
navigate,
|
|
112
|
+
session,
|
|
113
|
+
],
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
const importComponent = useCallback(
|
|
117
|
+
async (payload: JlcpcbComponentTsxLoadedPayload) => {
|
|
118
|
+
const importPromise = runImport(payload)
|
|
119
|
+
|
|
120
|
+
toastLibrary.promise(importPromise, {
|
|
121
|
+
loading: "Importing component...",
|
|
122
|
+
success: ({ partNumber, existing }) => (
|
|
123
|
+
<p>
|
|
124
|
+
{existing
|
|
125
|
+
? `Component ${partNumber} already exists. Opening package in the editor.`
|
|
126
|
+
: `Component ${partNumber} imported successfully. Opening package in the editor.`}
|
|
127
|
+
</p>
|
|
128
|
+
),
|
|
129
|
+
error: (error) => (
|
|
130
|
+
<p>
|
|
131
|
+
{error instanceof Error
|
|
132
|
+
? error.message
|
|
133
|
+
: "Failed to import component"}
|
|
134
|
+
</p>
|
|
135
|
+
),
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
await importPromise
|
|
139
|
+
},
|
|
140
|
+
[runImport, toastLibrary],
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
importComponent,
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export const openJlcpcbImportIssue = (
|
|
149
|
+
partNumber: string,
|
|
150
|
+
errorMessage: string,
|
|
151
|
+
) => {
|
|
152
|
+
const url = getJlcpcbImportIssueUrl(partNumber, errorMessage)
|
|
153
|
+
window.open(url, "_blank")
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const getJlcpcbImportIssueUrl = (partNumber: string, errorMessage: string) => {
|
|
157
|
+
const issueTitle = `[${partNumber}] Failed to import from JLCPCB`
|
|
158
|
+
const issueBody = `I tried to import the part number ${partNumber} from JLCPCB, but it failed. Here's the error I got:\n\`\`\`\n${errorMessage}\n\`\`\`\n\nCould be an issue in \`fetchEasyEDAComponent\` or \`convertRawEasyEdaToTs\``
|
|
159
|
+
const issueLabels = "snippets,good first issue"
|
|
160
|
+
const url = `https://github.com/tscircuit/easyeda-converter/issues/new?title=${encodeURIComponent(
|
|
161
|
+
issueTitle,
|
|
162
|
+
)}&body=${encodeURIComponent(issueBody)}&labels=${encodeURIComponent(issueLabels)}`
|
|
163
|
+
return url
|
|
164
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useQuery } from "react-query"
|
|
2
|
+
import { useAxios } from "@/hooks/use-axios"
|
|
3
|
+
import type { Account } from "fake-snippets-api/lib/db/schema"
|
|
4
|
+
|
|
5
|
+
export const useListOrgMembers = ({
|
|
6
|
+
orgId,
|
|
7
|
+
orgName,
|
|
8
|
+
}: { orgId?: string; orgName?: string }) => {
|
|
9
|
+
const axios = useAxios()
|
|
10
|
+
return useQuery<Account[], Error & { status: number }>(
|
|
11
|
+
["orgs", "members", orgId || orgName],
|
|
12
|
+
async () => {
|
|
13
|
+
if (!orgId && !orgName) {
|
|
14
|
+
throw new Error("Organization ID or name is required")
|
|
15
|
+
}
|
|
16
|
+
const params = orgId ? { org_id: orgId } : { name: orgName }
|
|
17
|
+
const { data } = await axios.get("/orgs/list_members", { params })
|
|
18
|
+
return data.members
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
enabled: Boolean(orgId || orgName),
|
|
22
|
+
retry: false,
|
|
23
|
+
refetchOnWindowFocus: false,
|
|
24
|
+
keepPreviousData: true,
|
|
25
|
+
},
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useQuery } from "react-query"
|
|
2
|
+
import { useAxios } from "@/hooks/use-axios"
|
|
3
|
+
import type { PublicOrgSchema } from "fake-snippets-api/lib/db/schema"
|
|
4
|
+
import { useGlobalStore } from "./use-global-store"
|
|
5
|
+
|
|
6
|
+
export const useListUserOrgs = (githubHandle?: string) => {
|
|
7
|
+
const axios = useAxios()
|
|
8
|
+
const session = useGlobalStore((s) => s.session)
|
|
9
|
+
const github_handle = githubHandle || session?.github_username
|
|
10
|
+
|
|
11
|
+
return useQuery<PublicOrgSchema[], Error & { status: number }>(
|
|
12
|
+
["orgs", "list", github_handle],
|
|
13
|
+
async () => {
|
|
14
|
+
const { data } = await axios.get("/orgs/list", {
|
|
15
|
+
...(github_handle && { params: { github_handle } }),
|
|
16
|
+
})
|
|
17
|
+
return data.orgs
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
retry: false,
|
|
21
|
+
refetchOnWindowFocus: false,
|
|
22
|
+
enabled: Boolean(github_handle),
|
|
23
|
+
},
|
|
24
|
+
)
|
|
25
|
+
}
|