@tscircuit/fake-snippets 0.0.108 → 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 +389 -450
- package/bunfig.toml +2 -1
- package/dist/bundle.js +1255 -625
- package/dist/index.d.ts +296 -4
- package/dist/index.js +325 -24
- package/dist/schema.d.ts +282 -1
- package/dist/schema.js +54 -2
- 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 +62 -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 +27 -24
- package/renovate.json +1 -1
- package/scripts/generate-sitemap.ts +1 -1
- package/src/App.tsx +29 -10
- 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 +133 -36
- package/src/components/FileSidebar.tsx +41 -50
- package/src/components/Footer.tsx +8 -10
- 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 +44 -16
- package/src/components/HiddenFilesDropdown.tsx +0 -2
- package/src/components/NotFound.tsx +5 -5
- package/src/components/PackageBreadcrumb.tsx +6 -12
- package/src/components/PackageCard.tsx +0 -1
- 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/ShikiCodeViewer.tsx +20 -11
- package/src/components/ViewPackagePage/components/build-status.tsx +1 -1
- package/src/components/ViewPackagePage/components/important-files-view.tsx +174 -87
- package/src/components/ViewPackagePage/components/main-content-header.tsx +8 -4
- package/src/components/ViewPackagePage/components/main-content-view-selector.tsx +1 -2
- package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +54 -20
- package/src/components/ViewPackagePage/components/package-header.tsx +26 -37
- package/src/components/ViewPackagePage/components/preview-image-squares.tsx +11 -19
- package/src/components/ViewPackagePage/components/repo-page-content.tsx +33 -25
- 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/sidebar.tsx +0 -2
- package/src/components/ViewPackagePage/components/tab-views/files-view.tsx +18 -17
- 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/ViewPackagePage/components/theme-toggle.tsx +0 -2
- package/src/components/ViewPackagePage/hooks/use-toast.tsx +0 -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 +32 -46
- package/src/components/package-port/CodeEditor.tsx +28 -31
- package/src/components/package-port/CodeEditorHeader.tsx +128 -63
- 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 +53 -36
- 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-id.ts +5 -30
- package/src/hooks/use-current-package-info.ts +29 -5
- 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 +183 -35
- package/src/hooks/useOptimizedPackageFilesLoader.ts +136 -0
- package/src/hooks/usePackageFilesLoader.ts +2 -2
- package/src/hooks/useUpdatePackageFilesMutation.ts +15 -1
- package/src/lib/download-fns/download-circuit-png.ts +11 -3
- package/src/lib/download-fns/download-gltf-from-circuit-json.ts +44 -0
- 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 +10 -0
- package/src/main.tsx +2 -1
- package/src/pages/authorize.tsx +0 -2
- 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 -7
- 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 +76 -136
- 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 +21 -26
- package/vite.config.ts +9 -0
- package/fake-snippets-api/routes/api/autocomplete/create_autocomplete.ts +0 -133
- package/src/components/Footer2.tsx +0 -100
- package/src/components/JLCPCBImportDialog.tsx +0 -280
- package/src/components/PackageBuildsPage/LogContent.tsx +0 -72
- package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +0 -115
- package/src/components/PackageBuildsPage/build-preview-content.tsx +0 -27
- 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/components/ShippingInformationForm.tsx +0 -423
- package/src/components/StaticViewSnippetHeader.tsx +0 -70
- package/src/components/ViewPackagePage/components/file-explorer.tsx +0 -67
- package/src/components/ViewPackagePage/components/readme-view.tsx +0 -58
- package/src/components/ViewPackagePage/components/repo-header-button.tsx +0 -36
- package/src/components/ViewPackagePage/components/repo-header.tsx +0 -4
- package/src/components/ViewPackagePage/components/sidebar-contributors-section.tsx +0 -31
- package/src/components/ViewSnippetHeader.tsx +0 -181
- package/src/components/ui/input-otp.tsx +0 -69
- package/src/pages/package-builds.tsx +0 -33
- package/src/pages/settings.tsx +0 -25
|
@@ -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"
|
|
@@ -91,8 +105,9 @@ export const PackageReleasesDashboard = ({
|
|
|
91
105
|
</div>
|
|
92
106
|
<div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4 mt-2 text-sm text-gray-600">
|
|
93
107
|
<div
|
|
94
|
-
className=
|
|
108
|
+
className={`flex items-center gap-1 ${pkg.github_repo_full_name ? "cursor-pointer" : ""}`}
|
|
95
109
|
onClick={() =>
|
|
110
|
+
pkg.github_repo_full_name &&
|
|
96
111
|
window?.open(
|
|
97
112
|
`https://github.com/${pkg.github_repo_full_name}/tree/${latestRelease?.branch_name || "main"}`,
|
|
98
113
|
"_blank",
|
|
@@ -121,22 +136,24 @@ export const PackageReleasesDashboard = ({
|
|
|
121
136
|
</div>
|
|
122
137
|
|
|
123
138
|
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-2">
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
139
|
+
{pkg.github_repo_full_name && (
|
|
140
|
+
<Button
|
|
141
|
+
variant="outline"
|
|
142
|
+
size="sm"
|
|
143
|
+
className="flex items-center gap-2 justify-center min-w-[120px] h-9"
|
|
144
|
+
onClick={() =>
|
|
145
|
+
window.open(
|
|
146
|
+
`https://github.com/${pkg.github_repo_full_name}`,
|
|
147
|
+
"_blank",
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
>
|
|
151
|
+
<GitHubLogoIcon className="w-4 h-4" />
|
|
152
|
+
<span className="hidden sm:inline">Repository</span>
|
|
153
|
+
<span className="sm:hidden">Repository</span>
|
|
154
|
+
</Button>
|
|
155
|
+
)}
|
|
156
|
+
{latestBuild && status !== "error" && (
|
|
140
157
|
<Button
|
|
141
158
|
variant="outline"
|
|
142
159
|
size="sm"
|
|
@@ -164,19 +181,19 @@ export const PackageReleasesDashboard = ({
|
|
|
164
181
|
</Button>
|
|
165
182
|
</DropdownMenuTrigger>
|
|
166
183
|
<DropdownMenuContent align="end">
|
|
167
|
-
|
|
168
|
-
<
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
</
|
|
184
|
+
{pkg.github_repo_full_name && (
|
|
185
|
+
<DropdownMenuItem asChild>
|
|
186
|
+
<a
|
|
187
|
+
href={`https://github.com/${pkg.github_repo_full_name}`}
|
|
188
|
+
target="_blank"
|
|
189
|
+
rel="noopener noreferrer"
|
|
190
|
+
>
|
|
191
|
+
View Source
|
|
192
|
+
</a>
|
|
193
|
+
</DropdownMenuItem>
|
|
194
|
+
)}
|
|
195
|
+
<DropdownMenuItem asChild onClick={handleDownloadZip}>
|
|
196
|
+
<span>Download Zip</span>
|
|
180
197
|
</DropdownMenuItem>
|
|
181
198
|
<DropdownMenuItem
|
|
182
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) {
|
|
@@ -1,41 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useParams } from "wouter"
|
|
3
|
-
import { usePackageById } from "./use-package-by-package-id"
|
|
4
|
-
import { usePackageByName } from "./use-package-by-package-name"
|
|
5
|
-
import { useUrlParams } from "./use-url-params"
|
|
1
|
+
import { useCurrentPackageInfo } from "./use-current-package-info"
|
|
6
2
|
|
|
7
3
|
export const useCurrentPackageId = (): {
|
|
8
4
|
packageId: string | null
|
|
9
5
|
isLoading: boolean
|
|
10
6
|
error: (Error & { status: number }) | null
|
|
11
7
|
} => {
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const wouter = useParams()
|
|
15
|
-
const [packageIdFromUrl, setPackageId] = useState<string | null>(urlPackageId)
|
|
16
|
-
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
if (urlPackageId) {
|
|
19
|
-
setPackageId(urlPackageId)
|
|
20
|
-
}
|
|
21
|
-
}, [urlPackageId])
|
|
22
|
-
|
|
23
|
-
const packageName =
|
|
24
|
-
wouter.author && wouter.packageName
|
|
25
|
-
? `${wouter.author}/${wouter.packageName}`
|
|
26
|
-
: null
|
|
27
|
-
|
|
28
|
-
const {
|
|
29
|
-
data: packageByName,
|
|
30
|
-
isLoading: isLoadingPackageByName,
|
|
31
|
-
error: errorPackageByName,
|
|
32
|
-
} = usePackageByName(packageName)
|
|
33
|
-
|
|
34
|
-
const packageId = packageIdFromUrl ?? packageByName?.package_id ?? null
|
|
8
|
+
const { packageInfo, isLoading, error } = useCurrentPackageInfo()
|
|
9
|
+
const packageId = packageInfo?.package_id ?? null
|
|
35
10
|
|
|
36
11
|
return {
|
|
37
12
|
packageId,
|
|
38
|
-
isLoading
|
|
39
|
-
error
|
|
13
|
+
isLoading,
|
|
14
|
+
error,
|
|
40
15
|
}
|
|
41
16
|
}
|
|
@@ -1,8 +1,32 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useParams } from "wouter"
|
|
2
2
|
import { usePackageById } from "./use-package-by-package-id"
|
|
3
|
+
import { usePackageByName } from "./use-package-by-package-name"
|
|
4
|
+
import { useUrlParams } from "./use-url-params"
|
|
5
|
+
import type { Package } from "fake-snippets-api/lib/db/schema"
|
|
3
6
|
|
|
4
|
-
export const useCurrentPackageInfo = ()
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
export const useCurrentPackageInfo = (): {
|
|
8
|
+
packageInfo: Package | undefined
|
|
9
|
+
isLoading: boolean
|
|
10
|
+
error: (Error & { status: number }) | null
|
|
11
|
+
refetch: () => Promise<unknown>
|
|
12
|
+
} => {
|
|
13
|
+
const urlParams = useUrlParams()
|
|
14
|
+
const packageIdFromQuery = urlParams.package_id ?? null
|
|
15
|
+
|
|
16
|
+
const { author, packageName } = useParams()
|
|
17
|
+
const packageSlug = author && packageName ? `${author}/${packageName}` : null
|
|
18
|
+
|
|
19
|
+
const queryById = usePackageById(packageIdFromQuery)
|
|
20
|
+
const queryByName = usePackageByName(packageSlug)
|
|
21
|
+
|
|
22
|
+
const data = queryById.data ?? queryByName.data
|
|
23
|
+
const isLoading = queryById.isLoading || queryByName.isLoading
|
|
24
|
+
const error =
|
|
25
|
+
(queryById.error as (Error & { status: number }) | null) ??
|
|
26
|
+
(queryByName.error as (Error & { status: number }) | null) ??
|
|
27
|
+
null
|
|
28
|
+
|
|
29
|
+
const refetch = packageIdFromQuery ? queryById.refetch : queryByName.refetch
|
|
30
|
+
|
|
31
|
+
return { packageInfo: data, isLoading, error, refetch }
|
|
8
32
|
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
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 useOrgByGithubHandle = (githubHandle: string | null) => {
|
|
7
|
+
const axios = useAxios()
|
|
8
|
+
const session = useGlobalStore((s) => s.session)
|
|
9
|
+
return useQuery<PublicOrgSchema, Error & { status: number }>(
|
|
10
|
+
["orgs", "by-github-handle", githubHandle],
|
|
11
|
+
async () => {
|
|
12
|
+
if (!githubHandle) {
|
|
13
|
+
throw new Error("GitHub handle is required")
|
|
14
|
+
}
|
|
15
|
+
const { data } = await axios.get("/orgs/get", {
|
|
16
|
+
params: { github_handle: githubHandle },
|
|
17
|
+
})
|
|
18
|
+
return data.org
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
enabled: Boolean(githubHandle && session),
|
|
22
|
+
retry: false,
|
|
23
|
+
refetchOnWindowFocus: false,
|
|
24
|
+
},
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
|
|
5
|
+
export const useOrg = ({ orgName }: { orgName: string | null }) => {
|
|
6
|
+
const axios = useAxios()
|
|
7
|
+
return useQuery<PublicOrgSchema, Error & { status: number }>(
|
|
8
|
+
["orgs", orgName],
|
|
9
|
+
async () => {
|
|
10
|
+
if (!orgName) {
|
|
11
|
+
throw new Error("Organization name is required")
|
|
12
|
+
}
|
|
13
|
+
const { data } = await axios.get("/orgs/get", {
|
|
14
|
+
params: { org_name: orgName },
|
|
15
|
+
})
|
|
16
|
+
return data.org
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
enabled: Boolean(orgName),
|
|
20
|
+
retry: false,
|
|
21
|
+
refetchOnWindowFocus: false,
|
|
22
|
+
},
|
|
23
|
+
)
|
|
24
|
+
}
|