@tscircuit/fake-snippets 0.0.72 → 0.0.74
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/bun.lock +33 -17
- package/dist/bundle.js +296 -200
- package/dist/index.d.ts +87 -4
- package/dist/index.js +71 -8
- package/dist/schema.d.ts +120 -0
- package/dist/schema.js +19 -1
- package/fake-snippets-api/lib/db/db-client.ts +58 -7
- package/fake-snippets-api/lib/db/schema.ts +26 -0
- package/fake-snippets-api/lib/package_file/generate-fs-sha.ts +20 -0
- package/fake-snippets-api/lib/public-mapping/public-map-package.ts +1 -0
- package/fake-snippets-api/routes/api/package_files/create.ts +3 -0
- package/fake-snippets-api/routes/api/package_files/create_or_update.ts +6 -0
- package/fake-snippets-api/routes/api/package_files/delete.ts +3 -0
- package/fake-snippets-api/routes/api/package_releases/rebuild.ts +32 -0
- package/fake-snippets-api/routes/api/packages/create.ts +1 -0
- package/package.json +7 -7
- package/src/App.tsx +5 -0
- package/src/components/JLCPCBImportDialog.tsx +1 -1
- package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +56 -0
- package/src/components/PackageBuildsPage/build-preview-content.tsx +11 -0
- package/src/components/PackageBuildsPage/collapsible-section.tsx +70 -0
- package/src/components/PackageBuildsPage/package-build-details-panel.tsx +84 -0
- package/src/components/PackageBuildsPage/package-build-header.tsx +75 -0
- package/src/components/PackageCard.tsx +1 -10
- package/src/components/TrendingPackagesCarousel.tsx +5 -16
- package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +1 -1
- package/src/components/ViewPackagePage/components/preview-image-squares.tsx +2 -6
- package/src/components/package-port/CodeAndPreview.tsx +3 -2
- package/src/components/package-port/CodeEditor.tsx +5 -3
- package/src/components/package-port/EditorNav.tsx +17 -13
- package/src/hooks/use-create-package-mutation.ts +0 -7
- package/src/hooks/use-current-package-id.ts +2 -8
- package/src/hooks/use-current-package-release.ts +28 -0
- package/src/hooks/use-preview-images.ts +3 -15
- package/src/hooks/useFileManagement.ts +26 -21
- package/src/lib/utils/checkIfManualEditsImported.ts +9 -0
- package/src/pages/editor.tsx +2 -10
- package/src/pages/package-builds.tsx +33 -0
- package/src/pages/package-editor.tsx +2 -14
- package/src/hooks/use-get-fsmap-hash-for-package.ts +0 -19
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
type Package,
|
|
16
16
|
type PackageFile,
|
|
17
17
|
type PackageRelease,
|
|
18
|
+
packageReleaseSchema,
|
|
18
19
|
type Session,
|
|
19
20
|
type Snippet,
|
|
20
21
|
databaseSchema,
|
|
@@ -22,6 +23,7 @@ import {
|
|
|
22
23
|
type snippetSchema,
|
|
23
24
|
} from "./schema.ts"
|
|
24
25
|
import { seed as seedFn } from "./seed"
|
|
26
|
+
import { generateFsSha } from "../package_file/generate-fs-sha"
|
|
25
27
|
|
|
26
28
|
export const createDatabase = ({ seed }: { seed?: boolean } = {}) => {
|
|
27
29
|
const db = hoist(createStore(initializer))
|
|
@@ -271,10 +273,11 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
|
|
|
271
273
|
is_public: true,
|
|
272
274
|
is_unlisted: false,
|
|
273
275
|
latest_package_release_id: `package_release_${nextId}`,
|
|
276
|
+
latest_package_release_fs_sha: null,
|
|
274
277
|
}
|
|
275
278
|
|
|
276
279
|
// Create package release
|
|
277
|
-
const newPackageRelease = {
|
|
280
|
+
const newPackageRelease = packageReleaseSchema.parse({
|
|
278
281
|
package_release_id: `package_release_${nextId}`,
|
|
279
282
|
package_id: newPackage.package_id,
|
|
280
283
|
version: "0.0.1",
|
|
@@ -284,7 +287,7 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
|
|
|
284
287
|
updated_at: currentTime,
|
|
285
288
|
has_transpiled: true,
|
|
286
289
|
transpilation_error: null,
|
|
287
|
-
}
|
|
290
|
+
})
|
|
288
291
|
|
|
289
292
|
// Add all the files
|
|
290
293
|
const packageFiles: PackageFile[] = []
|
|
@@ -341,6 +344,27 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
|
|
|
341
344
|
idCounter: fileIdCounter,
|
|
342
345
|
}))
|
|
343
346
|
|
|
347
|
+
// Update fs_sha for the new package release
|
|
348
|
+
const dbState = get()
|
|
349
|
+
const releaseFiles = dbState.packageFiles.filter(
|
|
350
|
+
(pf) => pf.package_release_id === newPackageRelease.package_release_id,
|
|
351
|
+
)
|
|
352
|
+
const fsSha = generateFsSha(releaseFiles)
|
|
353
|
+
|
|
354
|
+
set((state) => ({
|
|
355
|
+
...state,
|
|
356
|
+
packageReleases: state.packageReleases.map((pr) =>
|
|
357
|
+
pr.package_release_id === newPackageRelease.package_release_id
|
|
358
|
+
? { ...pr, fs_sha: fsSha }
|
|
359
|
+
: pr,
|
|
360
|
+
),
|
|
361
|
+
packages: state.packages.map((pkg) =>
|
|
362
|
+
pkg.latest_package_release_id === newPackageRelease.package_release_id
|
|
363
|
+
? { ...pkg, latest_package_release_fs_sha: fsSha }
|
|
364
|
+
: pkg,
|
|
365
|
+
),
|
|
366
|
+
}))
|
|
367
|
+
|
|
344
368
|
// Return in the same format as create endpoint
|
|
345
369
|
return {
|
|
346
370
|
snippet_id: newPackage.package_id,
|
|
@@ -1215,16 +1239,19 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
|
|
|
1215
1239
|
)
|
|
1216
1240
|
},
|
|
1217
1241
|
addPackageRelease: (
|
|
1218
|
-
packageRelease: Omit<
|
|
1242
|
+
packageRelease: Omit<
|
|
1243
|
+
z.input<typeof packageReleaseSchema>,
|
|
1244
|
+
"package_release_id"
|
|
1245
|
+
>,
|
|
1219
1246
|
): PackageRelease => {
|
|
1220
|
-
const
|
|
1247
|
+
const parsed = packageReleaseSchema.parse({
|
|
1221
1248
|
package_release_id: `package_release_${Date.now()}`,
|
|
1222
1249
|
...packageRelease,
|
|
1223
|
-
}
|
|
1250
|
+
})
|
|
1224
1251
|
set((state) => ({
|
|
1225
|
-
packageReleases: [...state.packageReleases,
|
|
1252
|
+
packageReleases: [...state.packageReleases, parsed],
|
|
1226
1253
|
}))
|
|
1227
|
-
return
|
|
1254
|
+
return parsed
|
|
1228
1255
|
},
|
|
1229
1256
|
updatePackageRelease: (packageRelease: PackageRelease): void => {
|
|
1230
1257
|
set((state) => ({
|
|
@@ -1287,4 +1314,28 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
|
|
|
1287
1314
|
(pf) => pf.package_release_id === packageReleaseId,
|
|
1288
1315
|
)
|
|
1289
1316
|
},
|
|
1317
|
+
/**
|
|
1318
|
+
* Update fs_sha for a package release based on its files
|
|
1319
|
+
*/
|
|
1320
|
+
updatePackageReleaseFsSha: (packageReleaseId: string): void => {
|
|
1321
|
+
const state = get()
|
|
1322
|
+
const packageFiles = state.packageFiles.filter(
|
|
1323
|
+
(pf) => pf.package_release_id === packageReleaseId,
|
|
1324
|
+
)
|
|
1325
|
+
const fsSha = generateFsSha(packageFiles)
|
|
1326
|
+
|
|
1327
|
+
set((currentState) => ({
|
|
1328
|
+
...currentState,
|
|
1329
|
+
packageReleases: currentState.packageReleases.map((pr) =>
|
|
1330
|
+
pr.package_release_id === packageReleaseId
|
|
1331
|
+
? { ...pr, fs_sha: fsSha }
|
|
1332
|
+
: pr,
|
|
1333
|
+
),
|
|
1334
|
+
packages: currentState.packages.map((pkg) =>
|
|
1335
|
+
pkg.latest_package_release_id === packageReleaseId
|
|
1336
|
+
? { ...pkg, latest_package_release_fs_sha: fsSha }
|
|
1337
|
+
: pkg,
|
|
1338
|
+
),
|
|
1339
|
+
}))
|
|
1340
|
+
},
|
|
1290
1341
|
}))
|
|
@@ -176,6 +176,31 @@ export const packageReleaseSchema = z.object({
|
|
|
176
176
|
has_transpiled: z.boolean().default(false),
|
|
177
177
|
transpilation_error: z.string().nullable().optional(),
|
|
178
178
|
fs_sha: z.string().nullable().optional(),
|
|
179
|
+
// Build Status and Display
|
|
180
|
+
display_status: z
|
|
181
|
+
.enum(["pending", "building", "successful", "failed"])
|
|
182
|
+
.default("pending"),
|
|
183
|
+
total_build_duration_ms: z.number().nullable().optional(),
|
|
184
|
+
|
|
185
|
+
// Transpilation Process
|
|
186
|
+
transpilation_display_status: z
|
|
187
|
+
.enum(["pending", "building", "successful", "failed"])
|
|
188
|
+
.default("pending"),
|
|
189
|
+
transpilation_in_progress: z.boolean().default(false),
|
|
190
|
+
transpilation_started_at: z.string().datetime().nullable().optional(),
|
|
191
|
+
transpilation_completed_at: z.string().datetime().nullable().optional(),
|
|
192
|
+
transpilation_logs: z.array(z.any()).default([]),
|
|
193
|
+
transpilation_is_stale: z.boolean().default(false),
|
|
194
|
+
|
|
195
|
+
// Circuit JSON Build Process
|
|
196
|
+
circuit_json_build_display_status: z
|
|
197
|
+
.enum(["pending", "building", "successful", "failed"])
|
|
198
|
+
.default("pending"),
|
|
199
|
+
circuit_json_build_in_progress: z.boolean().default(false),
|
|
200
|
+
circuit_json_build_started_at: z.string().datetime().nullable().optional(),
|
|
201
|
+
circuit_json_build_completed_at: z.string().datetime().nullable().optional(),
|
|
202
|
+
circuit_json_build_logs: z.array(z.any()).default([]),
|
|
203
|
+
circuit_json_build_is_stale: z.boolean().default(false),
|
|
179
204
|
})
|
|
180
205
|
export type PackageRelease = z.infer<typeof packageReleaseSchema>
|
|
181
206
|
|
|
@@ -219,6 +244,7 @@ export const packageSchema = z.object({
|
|
|
219
244
|
ai_description: z.string().nullable(),
|
|
220
245
|
latest_license: z.string().nullable().optional(),
|
|
221
246
|
ai_usage_instructions: z.string().nullable(),
|
|
247
|
+
latest_package_release_fs_sha: z.string().nullable().default(null),
|
|
222
248
|
default_view: z
|
|
223
249
|
.enum(["files", "3d", "pcb", "schematic"])
|
|
224
250
|
.default("files")
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import md5 from "md5"
|
|
2
|
+
import type { PackageFile } from "../db/schema"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generate an MD5 hash from package files content
|
|
6
|
+
* Uses the same format as existing tests: creates a map of file_path -> content_text
|
|
7
|
+
* and generates an MD5 hash of the JSON stringified map
|
|
8
|
+
*/
|
|
9
|
+
export function generateFsSha(packageFiles: PackageFile[]): string {
|
|
10
|
+
const fsMap: Record<string, string> = {}
|
|
11
|
+
|
|
12
|
+
packageFiles
|
|
13
|
+
.filter((file) => file.content_text) // Only include files with content
|
|
14
|
+
.forEach((file) => {
|
|
15
|
+
fsMap[file.file_path] = file.content_text || ""
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const hash = md5(JSON.stringify(fsMap))
|
|
19
|
+
return `md5-${hash}`
|
|
20
|
+
}
|
|
@@ -125,6 +125,9 @@ export default withRouteSpec(routeSpec)(async (req, ctx) => {
|
|
|
125
125
|
// Add to the test database
|
|
126
126
|
ctx.db.addPackageFile(newPackageFile)
|
|
127
127
|
|
|
128
|
+
// Update fs_sha for the package release
|
|
129
|
+
ctx.db.updatePackageReleaseFsSha(packageReleaseId)
|
|
130
|
+
|
|
128
131
|
return ctx.json({
|
|
129
132
|
ok: true,
|
|
130
133
|
package_file: newPackageFile,
|
|
@@ -146,6 +146,9 @@ export default withRouteSpec(routeSpec)(async (req, ctx) => {
|
|
|
146
146
|
},
|
|
147
147
|
)
|
|
148
148
|
|
|
149
|
+
// Update fs_sha for the package release
|
|
150
|
+
ctx.db.updatePackageReleaseFsSha(packageReleaseId)
|
|
151
|
+
|
|
149
152
|
return ctx.json({
|
|
150
153
|
ok: true,
|
|
151
154
|
package_file,
|
|
@@ -181,6 +184,9 @@ export default withRouteSpec(routeSpec)(async (req, ctx) => {
|
|
|
181
184
|
// Add to the test database
|
|
182
185
|
ctx.db.addPackageFile(newPackageFile)
|
|
183
186
|
|
|
187
|
+
// Update fs_sha for the package release
|
|
188
|
+
ctx.db.updatePackageReleaseFsSha(packageReleaseId)
|
|
189
|
+
|
|
184
190
|
return ctx.json({
|
|
185
191
|
ok: true,
|
|
186
192
|
package_file: newPackageFile,
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
|
|
2
|
+
import { packageReleaseSchema } from "fake-snippets-api/lib/db/schema"
|
|
3
|
+
import { z } from "zod"
|
|
4
|
+
|
|
5
|
+
export default withRouteSpec({
|
|
6
|
+
methods: ["POST"],
|
|
7
|
+
auth: "session",
|
|
8
|
+
jsonBody: z.object({
|
|
9
|
+
package_release_id: z.string(),
|
|
10
|
+
}),
|
|
11
|
+
jsonResponse: z.object({
|
|
12
|
+
ok: z.boolean(),
|
|
13
|
+
package_release: packageReleaseSchema,
|
|
14
|
+
}),
|
|
15
|
+
})(async (req, ctx) => {
|
|
16
|
+
const { package_release_id } = req.jsonBody
|
|
17
|
+
|
|
18
|
+
const release = ctx.db.getPackageReleaseById(package_release_id)
|
|
19
|
+
|
|
20
|
+
if (!release) {
|
|
21
|
+
return ctx.error(404, {
|
|
22
|
+
error_code: "package_release_not_found",
|
|
23
|
+
message: "Package release not found",
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// In a real API this would trigger a rebuild. Here we simply return the release.
|
|
28
|
+
return ctx.json({
|
|
29
|
+
ok: true,
|
|
30
|
+
package_release: release,
|
|
31
|
+
})
|
|
32
|
+
})
|
|
@@ -42,6 +42,7 @@ export default withRouteSpec({
|
|
|
42
42
|
owner_org_id: ctx.auth.personal_org_id,
|
|
43
43
|
owner_github_username: ctx.auth.github_username,
|
|
44
44
|
latest_package_release_id: null,
|
|
45
|
+
latest_package_release_fs_sha: null,
|
|
45
46
|
latest_version: null,
|
|
46
47
|
license: null,
|
|
47
48
|
website: null,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tscircuit/fake-snippets",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.74",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"lint": "biome format .",
|
|
27
27
|
"build:vite:analyze": "VITE_BUNDLE_ANALYZE=true vite build",
|
|
28
28
|
"build:fake-api:tsup": "tsup-node ./fake-snippets-api/lib/index.ts --format esm --dts",
|
|
29
|
-
"build:fake-api:bundle": "
|
|
29
|
+
"build:fake-api:bundle": "winterspec2 bundle -o dist/bundle.js",
|
|
30
30
|
"build:fake-api": "bun run build:fake-api:tsup && bun run build:fake-api:bundle && bun run build:fake-api:schema",
|
|
31
31
|
"build:fake-api:schema": "tsup-node ./fake-snippets-api/lib/db/schema.ts --format esm --dts",
|
|
32
32
|
"generate-images": "bun run scripts/generate-image-sizes.ts",
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
"recharts": "^2.12.7",
|
|
128
128
|
"remark-gfm": "^4.0.1",
|
|
129
129
|
"rollup-plugin-visualizer": "^5.12.0",
|
|
130
|
-
"schematic-symbols": "^0.0.
|
|
130
|
+
"schematic-symbols": "^0.0.140",
|
|
131
131
|
"sitemap": "^8.0.0",
|
|
132
132
|
"sonner": "^1.5.0",
|
|
133
133
|
"states-us": "^1.1.1",
|
|
@@ -145,7 +145,7 @@
|
|
|
145
145
|
"@biomejs/biome": "^1.9.2",
|
|
146
146
|
"@playwright/test": "^1.48.0",
|
|
147
147
|
"@tailwindcss/typography": "^0.5.16",
|
|
148
|
-
"@tscircuit/core": "^0.0.
|
|
148
|
+
"@tscircuit/core": "^0.0.433",
|
|
149
149
|
"@tscircuit/prompt-benchmarks": "^0.0.28",
|
|
150
150
|
"@tscircuit/runframe": "^0.0.507",
|
|
151
151
|
"@types/babel__standalone": "^7.1.7",
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
"@typescript/vfs": "^1.6.0",
|
|
162
162
|
"@vitejs/plugin-react": "^4.3.1",
|
|
163
163
|
"autoprefixer": "^10.4.20",
|
|
164
|
-
"circuit-to-svg": "^0.0.
|
|
164
|
+
"circuit-to-svg": "^0.0.136",
|
|
165
165
|
"get-port": "^7.1.0",
|
|
166
166
|
"globals": "^15.9.0",
|
|
167
167
|
"he": "^1.2.0",
|
|
@@ -175,11 +175,11 @@
|
|
|
175
175
|
"shiki": "^3.2.1",
|
|
176
176
|
"tailwindcss": "^3.4.13",
|
|
177
177
|
"terser": "^5.27.0",
|
|
178
|
-
"tsup": "^8.
|
|
178
|
+
"tsup": "^8.5.0",
|
|
179
179
|
"typescript": "^5.6.3",
|
|
180
180
|
"vite": "^6.3.4",
|
|
181
181
|
"vite-plugin-image-optimizer": "^1.1.8",
|
|
182
|
-
"winterspec": "^0.0.
|
|
182
|
+
"winterspec": "^0.0.107",
|
|
183
183
|
"zod": "^3.23.8",
|
|
184
184
|
"zustand": "^4.5.5",
|
|
185
185
|
"zustand-hoist": "^2.0.1"
|
package/src/App.tsx
CHANGED
|
@@ -68,6 +68,7 @@ const UserProfilePage = lazyImport(() => import("@/pages/user-profile"))
|
|
|
68
68
|
const DevLoginPage = lazyImport(() => import("@/pages/dev-login"))
|
|
69
69
|
const BetaPage = lazyImport(() => import("@/pages/beta"))
|
|
70
70
|
const ViewPackagePage = lazyImport(() => import("@/pages/view-package"))
|
|
71
|
+
const PackageBuildsPage = lazyImport(() => import("@/pages/package-builds"))
|
|
71
72
|
const TrendingPage = lazyImport(() => import("@/pages/trending"))
|
|
72
73
|
const PackageEditorPage = lazyImport(async () => {
|
|
73
74
|
const [editorModule] = await Promise.all([
|
|
@@ -124,6 +125,10 @@ function App() {
|
|
|
124
125
|
<Route path="/dev-login" component={DevLoginPage} />
|
|
125
126
|
<Route path="/:username" component={UserProfilePage} />
|
|
126
127
|
<Route path="/:author/:packageName" component={ViewPackagePage} />
|
|
128
|
+
<Route
|
|
129
|
+
path="/:author/:packageName/builds"
|
|
130
|
+
component={PackageBuildsPage}
|
|
131
|
+
/>
|
|
127
132
|
<Route component={lazyImport(() => import("@/pages/404"))} />
|
|
128
133
|
</Switch>
|
|
129
134
|
</Suspense>
|
|
@@ -50,7 +50,7 @@ export function JLCPCBImportDialog({
|
|
|
50
50
|
setHasBeenImportedToAccountAlready(false)
|
|
51
51
|
|
|
52
52
|
try {
|
|
53
|
-
const apiUrl = `/
|
|
53
|
+
const apiUrl = `/packages/get?name=${session?.github_username}/${partNumber}`
|
|
54
54
|
|
|
55
55
|
const existingPackageRes = await axios.get(apiUrl, {
|
|
56
56
|
validateStatus: (status) => true,
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useState } from "react"
|
|
4
|
+
import { BuildPreviewContent } from "./build-preview-content"
|
|
5
|
+
import { PackageBuildDetailsPanel } from "./package-build-details-panel"
|
|
6
|
+
import { PackageBuildHeader } from "./package-build-header"
|
|
7
|
+
import { CollapsibleSection } from "./collapsible-section"
|
|
8
|
+
|
|
9
|
+
export const PackageBuildDetailsPage = () => {
|
|
10
|
+
const [openSections, setOpenSections] = useState<Record<string, boolean>>({})
|
|
11
|
+
|
|
12
|
+
const toggleSection = (section: string) => {
|
|
13
|
+
setOpenSections((prev) => ({
|
|
14
|
+
...prev,
|
|
15
|
+
[section]: !prev[section],
|
|
16
|
+
}))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div className="min-h-screen bg-gray-50 text-gray-900">
|
|
21
|
+
<PackageBuildHeader />
|
|
22
|
+
|
|
23
|
+
<div className="px-6 py-6 container mx-auto">
|
|
24
|
+
{/* Main Content */}
|
|
25
|
+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-8 items-start">
|
|
26
|
+
{/* Preview Section */}
|
|
27
|
+
<div className="lg:col-span-2">
|
|
28
|
+
<div className="bg-white border border-gray-200 rounded-lg p-4 flex items-center justify-center max-h-[420px]">
|
|
29
|
+
<BuildPreviewContent />
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
{/* Details Panel */}
|
|
34
|
+
<PackageBuildDetailsPanel />
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
{/* Collapsible Sections */}
|
|
38
|
+
<div className="space-y-4 mb-8">
|
|
39
|
+
<CollapsibleSection
|
|
40
|
+
title="Transpilation Logs"
|
|
41
|
+
duration="1m 15s"
|
|
42
|
+
isOpen={openSections.summary}
|
|
43
|
+
onToggle={() => toggleSection("summary")}
|
|
44
|
+
/>
|
|
45
|
+
|
|
46
|
+
<CollapsibleSection
|
|
47
|
+
title="Circuit JSON Build Logs"
|
|
48
|
+
duration="2m 29s"
|
|
49
|
+
isOpen={openSections.logs}
|
|
50
|
+
onToggle={() => toggleSection("logs")}
|
|
51
|
+
/>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function BuildPreviewContent() {
|
|
2
|
+
return (
|
|
3
|
+
<div className="flex items-center justify-center">
|
|
4
|
+
<img
|
|
5
|
+
src="https://placehold.co/600x400/000/31343C"
|
|
6
|
+
alt="Package build preview"
|
|
7
|
+
className="object-contain rounded p-2 max-h-[400px]"
|
|
8
|
+
/>
|
|
9
|
+
</div>
|
|
10
|
+
)
|
|
11
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type React from "react"
|
|
2
|
+
import { ChevronRight, CheckCircle2 } from "lucide-react"
|
|
3
|
+
import { Badge } from "@/components/ui/badge"
|
|
4
|
+
import {
|
|
5
|
+
Collapsible,
|
|
6
|
+
CollapsibleContent,
|
|
7
|
+
CollapsibleTrigger,
|
|
8
|
+
} from "@/components/ui/collapsible"
|
|
9
|
+
|
|
10
|
+
interface CollapsibleSectionProps {
|
|
11
|
+
title: string
|
|
12
|
+
duration?: string
|
|
13
|
+
isOpen: boolean
|
|
14
|
+
onToggle: () => void
|
|
15
|
+
badges?: Array<{
|
|
16
|
+
text: string
|
|
17
|
+
icon?: React.ReactNode
|
|
18
|
+
variant?: "default" | "secondary"
|
|
19
|
+
className?: string
|
|
20
|
+
}>
|
|
21
|
+
children?: React.ReactNode
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function CollapsibleSection({
|
|
25
|
+
title,
|
|
26
|
+
duration,
|
|
27
|
+
isOpen,
|
|
28
|
+
onToggle,
|
|
29
|
+
badges = [],
|
|
30
|
+
children,
|
|
31
|
+
}: CollapsibleSectionProps) {
|
|
32
|
+
return (
|
|
33
|
+
<Collapsible open={isOpen} onOpenChange={onToggle}>
|
|
34
|
+
<CollapsibleTrigger asChild>
|
|
35
|
+
<div className="flex items-center justify-between p-4 bg-white border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-100">
|
|
36
|
+
<div className="flex items-center gap-2">
|
|
37
|
+
<ChevronRight
|
|
38
|
+
className={`w-4 h-4 transition-transform ${isOpen ? "rotate-90" : ""}`}
|
|
39
|
+
/>
|
|
40
|
+
<span className="font-medium">{title}</span>
|
|
41
|
+
</div>
|
|
42
|
+
<div className="flex items-center gap-2">
|
|
43
|
+
{badges.map((badge, index) => (
|
|
44
|
+
<Badge
|
|
45
|
+
key={index}
|
|
46
|
+
variant={badge.variant || "secondary"}
|
|
47
|
+
className={
|
|
48
|
+
badge.className ||
|
|
49
|
+
"bg-gray-200 text-gray-700 flex items-center gap-1"
|
|
50
|
+
}
|
|
51
|
+
>
|
|
52
|
+
{badge.icon}
|
|
53
|
+
{badge.text}
|
|
54
|
+
</Badge>
|
|
55
|
+
))}
|
|
56
|
+
{duration && (
|
|
57
|
+
<span className="text-sm text-gray-600">{duration}</span>
|
|
58
|
+
)}
|
|
59
|
+
<CheckCircle2 className="w-4 h-4 text-green-500" />
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</CollapsibleTrigger>
|
|
63
|
+
<CollapsibleContent>
|
|
64
|
+
<div className="p-4 bg-white border-x border-b border-gray-200 rounded-b-lg">
|
|
65
|
+
{children || `${title} details would go here...`}
|
|
66
|
+
</div>
|
|
67
|
+
</CollapsibleContent>
|
|
68
|
+
</Collapsible>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Globe, GitBranch, GitCommit, Clock } from "lucide-react"
|
|
2
|
+
import { Badge } from "@/components/ui/badge"
|
|
3
|
+
|
|
4
|
+
export function PackageBuildDetailsPanel() {
|
|
5
|
+
return (
|
|
6
|
+
<div className="space-y-6 bg-white p-4 border border-gray-200 rounded-lg">
|
|
7
|
+
{/* Created */}
|
|
8
|
+
<div>
|
|
9
|
+
<h3 className="text-sm font-medium text-gray-600 mb-2">Created</h3>
|
|
10
|
+
<div className="flex items-center gap-2">
|
|
11
|
+
<div className="w-6 h-6 bg-orange-500 rounded-full flex items-center justify-center text-xs font-bold">
|
|
12
|
+
I
|
|
13
|
+
</div>
|
|
14
|
+
<span className="text-sm">imrishabh18</span>
|
|
15
|
+
<span className="text-sm text-gray-500">48m ago</span>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
{/* Status */}
|
|
20
|
+
<div>
|
|
21
|
+
<h3 className="text-sm font-medium text-gray-600 mb-2">Status</h3>
|
|
22
|
+
<div className="flex items-center gap-2">
|
|
23
|
+
<div className="w-2 h-2 bg-green-500 rounded-full"></div>
|
|
24
|
+
<span className="text-sm">Ready</span>
|
|
25
|
+
<Badge
|
|
26
|
+
variant="secondary"
|
|
27
|
+
className="bg-gray-200 text-gray-700 text-xs"
|
|
28
|
+
>
|
|
29
|
+
Latest
|
|
30
|
+
</Badge>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
{/* Time to Ready */}
|
|
35
|
+
<div>
|
|
36
|
+
<h3 className="text-sm font-medium text-gray-600 mb-2">
|
|
37
|
+
Time to Ready
|
|
38
|
+
</h3>
|
|
39
|
+
<div className="flex items-center gap-2">
|
|
40
|
+
<Clock className="w-4 h-4 text-gray-500" />
|
|
41
|
+
<span className="text-sm">1m 3s</span>
|
|
42
|
+
<span className="text-sm text-gray-500">47m ago</span>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
{/* Version */}
|
|
47
|
+
<div>
|
|
48
|
+
<h3 className="text-sm font-medium text-gray-600 mb-2">Version</h3>
|
|
49
|
+
<div className="flex items-center gap-2">
|
|
50
|
+
<Globe className="w-4 h-4 text-gray-500" />
|
|
51
|
+
<span className="text-sm">v1.0.3</span>
|
|
52
|
+
<Badge variant="default" className="bg-blue-600 text-white text-xs">
|
|
53
|
+
Current
|
|
54
|
+
</Badge>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
{/* Outputs */}
|
|
59
|
+
<div>
|
|
60
|
+
<h3 className="text-sm font-medium text-gray-600 mb-2">Outputs</h3>
|
|
61
|
+
<div>
|
|
62
|
+
<span className="text-sm text-gray-400">None</span>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
{/* Source */}
|
|
67
|
+
<div>
|
|
68
|
+
<h3 className="text-sm font-medium text-gray-600 mb-2">Source</h3>
|
|
69
|
+
<div className="space-y-2">
|
|
70
|
+
<div className="flex items-center gap-2">
|
|
71
|
+
<GitBranch className="w-4 h-4 text-gray-500" />
|
|
72
|
+
<span className="text-sm">main</span>
|
|
73
|
+
</div>
|
|
74
|
+
<div className="flex items-center gap-2">
|
|
75
|
+
<GitCommit className="w-4 h-4 text-gray-500" />
|
|
76
|
+
<span className="text-sm text-gray-500">
|
|
77
|
+
edfdc67 support empty file creation (#356)
|
|
78
|
+
</span>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { ChevronDown, Download, Github, Link } from "lucide-react"
|
|
2
|
+
import { Button } from "@/components/ui/button"
|
|
3
|
+
import {
|
|
4
|
+
DropdownMenu,
|
|
5
|
+
DropdownMenuContent,
|
|
6
|
+
DropdownMenuItem,
|
|
7
|
+
DropdownMenuTrigger,
|
|
8
|
+
} from "@/components/ui/dropdown-menu"
|
|
9
|
+
import { useParams } from "wouter"
|
|
10
|
+
|
|
11
|
+
export function PackageBuildHeader() {
|
|
12
|
+
const { author, packageName } = useParams()
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div className="border-b border-gray-200 bg-white px-6 py-4">
|
|
16
|
+
<div className="flex items-center justify-between container mx-auto">
|
|
17
|
+
<h1 className="text-2xl font-semibold">
|
|
18
|
+
Package Build
|
|
19
|
+
<a
|
|
20
|
+
className="ml-2 bg-gray-100 px-2 py-1 rounded font-mono text-blue-600"
|
|
21
|
+
href={`/${author}/${packageName}`}
|
|
22
|
+
>
|
|
23
|
+
{author}/{packageName}
|
|
24
|
+
</a>
|
|
25
|
+
</h1>
|
|
26
|
+
<div className="flex items-center gap-3">
|
|
27
|
+
<Button
|
|
28
|
+
variant="outline"
|
|
29
|
+
size="sm"
|
|
30
|
+
className="border-gray-300 bg-white hover:bg-gray-50 text-xs sm:text-sm"
|
|
31
|
+
asChild
|
|
32
|
+
>
|
|
33
|
+
<a
|
|
34
|
+
href="https://github.com/tscircuit/tscircuit.com/issues/new"
|
|
35
|
+
target="_blank"
|
|
36
|
+
rel="noopener noreferrer"
|
|
37
|
+
>
|
|
38
|
+
<Github className="w-3 h-3 sm:w-4 sm:h-4 mr-1 sm:mr-2" />
|
|
39
|
+
Report Issue
|
|
40
|
+
</a>
|
|
41
|
+
</Button>
|
|
42
|
+
<DropdownMenu>
|
|
43
|
+
<DropdownMenuTrigger asChild>
|
|
44
|
+
<Button
|
|
45
|
+
size="sm"
|
|
46
|
+
className="bg-gray-900 text-white hover:bg-gray-800"
|
|
47
|
+
>
|
|
48
|
+
<Download className="w-4 h-4 mr-2" />
|
|
49
|
+
Download
|
|
50
|
+
<ChevronDown className="w-4 h-4 ml-2" />
|
|
51
|
+
</Button>
|
|
52
|
+
</DropdownMenuTrigger>
|
|
53
|
+
<DropdownMenuContent
|
|
54
|
+
align="end"
|
|
55
|
+
className="bg-white border-gray-200"
|
|
56
|
+
>
|
|
57
|
+
<DropdownMenuItem className="text-gray-900 hover:bg-gray-100">
|
|
58
|
+
Circuit JSON
|
|
59
|
+
</DropdownMenuItem>
|
|
60
|
+
<DropdownMenuItem className="text-gray-900 hover:bg-gray-100">
|
|
61
|
+
PCB SVG
|
|
62
|
+
</DropdownMenuItem>
|
|
63
|
+
<DropdownMenuItem className="text-gray-900 hover:bg-gray-100">
|
|
64
|
+
Schematic SVG
|
|
65
|
+
</DropdownMenuItem>
|
|
66
|
+
<DropdownMenuItem className="text-gray-900 hover:bg-gray-100">
|
|
67
|
+
3D Model (stl)
|
|
68
|
+
</DropdownMenuItem>
|
|
69
|
+
</DropdownMenuContent>
|
|
70
|
+
</DropdownMenu>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
)
|
|
75
|
+
}
|