@tscircuit/fake-snippets 0.0.88 → 0.0.90

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/api/generated-index.js +96 -14
  2. package/bun-tests/fake-snippets-api/routes/proxy.test.ts +42 -0
  3. package/bun.lock +196 -215
  4. package/dist/bundle.js +596 -370
  5. package/fake-snippets-api/routes/api/autocomplete/create_autocomplete.ts +134 -0
  6. package/fake-snippets-api/routes/api/proxy.ts +128 -0
  7. package/package.json +59 -48
  8. package/renovate.json +2 -1
  9. package/src/App.tsx +67 -3
  10. package/src/ContextProviders.tsx +2 -0
  11. package/src/build-watcher.ts +52 -0
  12. package/src/components/CircuitJsonImportDialog.tsx +1 -1
  13. package/src/components/CmdKMenu.tsx +533 -197
  14. package/src/components/DownloadButtonAndMenu.tsx +104 -26
  15. package/src/components/FileSidebar.tsx +11 -1
  16. package/src/components/Header2.tsx +7 -2
  17. package/src/components/PackageBuildsPage/LogContent.tsx +25 -22
  18. package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +6 -6
  19. package/src/components/PackageBuildsPage/build-preview-content.tsx +5 -5
  20. package/src/components/PackageBuildsPage/package-build-details-panel.tsx +15 -13
  21. package/src/components/PackageBuildsPage/package-build-header.tsx +19 -28
  22. package/src/components/PackageCard.tsx +66 -16
  23. package/src/components/SearchComponent.tsx +2 -2
  24. package/src/components/SuspenseRunFrame.tsx +14 -2
  25. package/src/components/ViewPackagePage/components/important-files-view.tsx +90 -17
  26. package/src/components/ViewPackagePage/components/main-content-header.tsx +26 -2
  27. package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +2 -2
  28. package/src/components/ViewPackagePage/components/repo-page-content.tsx +35 -30
  29. package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +2 -2
  30. package/src/components/ViewPackagePage/components/sidebar-releases-section.tsx +20 -12
  31. package/src/components/ViewPackagePage/components/tab-views/files-view.tsx +0 -7
  32. package/src/components/ViewPackagePage/utils/fuzz-search.ts +121 -0
  33. package/src/components/ViewPackagePage/utils/is-hidden-file.ts +4 -0
  34. package/src/components/dialogs/confirm-delete-package-dialog.tsx +1 -1
  35. package/src/components/dialogs/confirm-discard-changes-dialog.tsx +73 -0
  36. package/src/components/dialogs/edit-package-details-dialog.tsx +2 -2
  37. package/src/components/dialogs/view-ts-files-dialog.tsx +478 -42
  38. package/src/components/package-port/CodeAndPreview.tsx +17 -16
  39. package/src/components/package-port/CodeEditor.tsx +138 -17
  40. package/src/components/package-port/CodeEditorHeader.tsx +44 -4
  41. package/src/components/package-port/EditorNav.tsx +42 -29
  42. package/src/components/package-port/GlobalFindReplace.tsx +681 -0
  43. package/src/components/package-port/QuickOpen.tsx +241 -0
  44. package/src/components/ui/dialog.tsx +1 -1
  45. package/src/components/ui/tree-view.tsx +1 -1
  46. package/src/global.d.ts +3 -0
  47. package/src/hooks/use-ai-review.ts +31 -0
  48. package/src/hooks/use-code-completion-ai-api.ts +3 -3
  49. package/src/hooks/use-current-package-release.ts +5 -1
  50. package/src/hooks/use-delete-package.ts +6 -2
  51. package/src/hooks/use-download-zip.ts +50 -0
  52. package/src/hooks/use-hotkey.ts +116 -0
  53. package/src/hooks/use-package-by-package-id.ts +1 -0
  54. package/src/hooks/use-package-by-package-name.ts +1 -0
  55. package/src/hooks/use-package-files.ts +3 -0
  56. package/src/hooks/use-package-release.ts +5 -1
  57. package/src/hooks/use-package.ts +1 -0
  58. package/src/hooks/use-request-ai-review-mutation.ts +14 -6
  59. package/src/hooks/use-snippet.ts +1 -0
  60. package/src/hooks/useFileManagement.ts +28 -10
  61. package/src/hooks/usePackageFilesLoader.ts +3 -1
  62. package/src/index.css +11 -0
  63. package/src/lib/decodeUrlHashToFsMap.ts +17 -0
  64. package/src/lib/download-fns/download-circuit-png.ts +88 -0
  65. package/src/lib/download-fns/download-png-utils.ts +31 -0
  66. package/src/lib/encodeFsMapToUrlHash.ts +13 -0
  67. package/src/lib/populate-query-cache-with-ssr-data.ts +7 -0
  68. package/src/lib/ts-lib-cache.ts +47 -0
  69. package/src/lib/types.ts +2 -0
  70. package/src/lib/utils/findTargetFile.ts +1 -1
  71. package/src/lib/utils/package-utils.ts +10 -0
  72. package/src/main.tsx +7 -0
  73. package/src/pages/dashboard.tsx +18 -5
  74. package/src/pages/view-package.tsx +15 -7
  75. package/src/types/package.ts +4 -0
  76. package/vite.config.ts +100 -1
@@ -13,24 +13,31 @@ import { downloadFabricationFiles } from "@/lib/download-fns/download-fabricatio
13
13
  import { downloadSchematicSvg } from "@/lib/download-fns/download-schematic-svg"
14
14
  import { downloadReadableNetlist } from "@/lib/download-fns/download-readable-netlist"
15
15
  import { downloadAssemblySvg } from "@/lib/download-fns/download-assembly-svg"
16
- import { downloadPcbSvg } from "@/lib/download-fns/download-pcb-svg"
17
16
  import { usePcbDownloadDialog } from "@/components/dialogs/pcb-download-dialog"
18
17
  import { downloadKicadFiles } from "@/lib/download-fns/download-kicad-files"
19
18
  import { AnyCircuitElement } from "circuit-json"
20
19
  import { ChevronDown, Download, Hammer } from "lucide-react"
21
20
  import { downloadGltf } from "@/lib/download-fns/download-gltf"
21
+ import { downloadPngImage } from "@/lib/download-fns/download-png-utils"
22
+ import { ImageFormat } from "@/lib/download-fns/download-circuit-png"
22
23
  import { CubeIcon } from "@radix-ui/react-icons"
23
24
 
24
25
  interface DownloadButtonAndMenuProps {
25
26
  className?: string
26
- snippetUnscopedName: string | undefined
27
+ unscopedName?: string
28
+ author?: string
27
29
  circuitJson?: AnyCircuitElement[] | null
30
+ desiredImageType?: string
31
+ offerMultipleImageFormats?: boolean
28
32
  }
29
33
 
30
34
  export function DownloadButtonAndMenu({
31
35
  className,
32
- snippetUnscopedName,
36
+ unscopedName,
37
+ author,
38
+ desiredImageType = "pcb",
33
39
  circuitJson,
40
+ offerMultipleImageFormats = false,
34
41
  }: DownloadButtonAndMenuProps) {
35
42
  const notImplemented = useNotImplementedToast()
36
43
  const { Dialog: PcbDownloadDialog, openDialog: openPcbDownloadDialog } =
@@ -65,13 +72,13 @@ export function DownloadButtonAndMenu({
65
72
  <ChevronDown className="w-4 h-4 ml-1" />
66
73
  </Button>
67
74
  </DropdownMenuTrigger>
68
- <DropdownMenuContent>
75
+ <DropdownMenuContent className="!z-[101]">
69
76
  <DropdownMenuItem
70
77
  className="text-xs"
71
78
  onSelect={() => {
72
79
  downloadCircuitJson(
73
80
  circuitJson,
74
- snippetUnscopedName || "circuit" + ".json",
81
+ unscopedName || "circuit" + ".json",
75
82
  )
76
83
  }}
77
84
  >
@@ -85,7 +92,7 @@ export function DownloadButtonAndMenu({
85
92
  className="text-xs"
86
93
  onClick={async () => {
87
94
  try {
88
- await downloadGltf(snippetUnscopedName || "circuit")
95
+ await downloadGltf(unscopedName || "circuit")
89
96
  } catch (error: any) {
90
97
  toast({
91
98
  title: "Error Downloading 3D Model",
@@ -106,7 +113,7 @@ export function DownloadButtonAndMenu({
106
113
  onClick={async () => {
107
114
  await downloadFabricationFiles({
108
115
  circuitJson,
109
- snippetUnscopedName: snippetUnscopedName || "snippet",
116
+ snippetUnscopedName: unscopedName || "snippet",
110
117
  }).catch((error) => {
111
118
  console.error(error)
112
119
  console.log(error, error.stack)
@@ -137,10 +144,7 @@ export function DownloadButtonAndMenu({
137
144
  <DropdownMenuItem
138
145
  className="text-xs"
139
146
  onSelect={() => {
140
- downloadKicadFiles(
141
- circuitJson,
142
- snippetUnscopedName || "kicad_project",
143
- )
147
+ downloadKicadFiles(circuitJson, unscopedName || "kicad_project")
144
148
  }}
145
149
  >
146
150
  <Download className="mr-1 h-3 w-3" />
@@ -153,10 +157,7 @@ export function DownloadButtonAndMenu({
153
157
  <DropdownMenuItem
154
158
  className="text-xs"
155
159
  onSelect={() => {
156
- downloadSchematicSvg(
157
- circuitJson,
158
- snippetUnscopedName || "circuit",
159
- )
160
+ downloadSchematicSvg(circuitJson, unscopedName || "circuit")
160
161
  }}
161
162
  >
162
163
  <Download className="mr-1 h-3 w-3" />
@@ -168,7 +169,7 @@ export function DownloadButtonAndMenu({
168
169
  <DropdownMenuItem
169
170
  className="text-xs"
170
171
  onSelect={() => {
171
- downloadAssemblySvg(circuitJson, snippetUnscopedName || "circuit")
172
+ downloadAssemblySvg(circuitJson, unscopedName || "circuit")
172
173
  }}
173
174
  >
174
175
  <Download className="mr-1 h-3 w-3" />
@@ -192,7 +193,7 @@ export function DownloadButtonAndMenu({
192
193
  <DropdownMenuItem
193
194
  className="text-xs"
194
195
  onSelect={() => {
195
- downloadDsnFile(circuitJson, snippetUnscopedName || "circuit")
196
+ downloadDsnFile(circuitJson, unscopedName || "circuit")
196
197
  }}
197
198
  >
198
199
  <Download className="mr-1 h-3 w-3" />
@@ -204,10 +205,7 @@ export function DownloadButtonAndMenu({
204
205
  <DropdownMenuItem
205
206
  className="text-xs"
206
207
  onClick={() => {
207
- downloadReadableNetlist(
208
- circuitJson,
209
- snippetUnscopedName || "circuit",
210
- )
208
+ downloadReadableNetlist(circuitJson, unscopedName || "circuit")
211
209
  }}
212
210
  >
213
211
  <Download className="mr-1 h-3 w-3" />
@@ -219,10 +217,7 @@ export function DownloadButtonAndMenu({
219
217
  <DropdownMenuItem
220
218
  className="text-xs"
221
219
  onSelect={() => {
222
- downloadSimpleRouteJson(
223
- circuitJson,
224
- snippetUnscopedName || "circuit",
225
- )
220
+ downloadSimpleRouteJson(circuitJson, unscopedName || "circuit")
226
221
  }}
227
222
  >
228
223
  <Download className="mr-1 h-3 w-3" />
@@ -231,11 +226,94 @@ export function DownloadButtonAndMenu({
231
226
  json
232
227
  </span>
233
228
  </DropdownMenuItem>
229
+
230
+ {!offerMultipleImageFormats && (
231
+ <DropdownMenuItem
232
+ className="text-xs"
233
+ onClick={() => {
234
+ const desiredImageFormat = [
235
+ "pcb",
236
+ "schematic",
237
+ "assembly",
238
+ "3d",
239
+ ].includes(desiredImageType)
240
+ ? desiredImageType
241
+ : "pcb"
242
+ downloadPngImage({
243
+ circuitJson,
244
+ unscopedName,
245
+ author,
246
+ format: desiredImageFormat as ImageFormat,
247
+ })
248
+ }}
249
+ >
250
+ <Download className="mr-1 h-3 w-3" />
251
+ <span className="flex-grow mr-6">Image PNG</span>
252
+ <span className="text-[0.6rem] opacity-80 bg-teal-600 text-white font-mono rounded-md px-1 text-center py-0.5 mr-1">
253
+ png
254
+ </span>
255
+ </DropdownMenuItem>
256
+ )}
257
+ {offerMultipleImageFormats && (
258
+ <>
259
+ <DropdownMenuItem
260
+ className="text-xs"
261
+ onClick={() =>
262
+ downloadPngImage({
263
+ circuitJson,
264
+ unscopedName,
265
+ author,
266
+ format: "schematic",
267
+ })
268
+ }
269
+ >
270
+ <Download className="mr-1 h-3 w-3" />
271
+ <span className="flex-grow mr-6">Schematic PNG</span>
272
+ <span className="text-[0.6rem] opacity-80 bg-teal-600 text-white font-mono rounded-md px-1 text-center py-0.5 mr-1">
273
+ png
274
+ </span>
275
+ </DropdownMenuItem>
276
+ <DropdownMenuItem
277
+ className="text-xs"
278
+ onClick={() =>
279
+ downloadPngImage({
280
+ circuitJson,
281
+ unscopedName,
282
+ author,
283
+ format: "pcb",
284
+ })
285
+ }
286
+ >
287
+ <Download className="mr-1 h-3 w-3" />
288
+ <span className="flex-grow mr-6">PCB PNG</span>
289
+ <span className="text-[0.6rem] opacity-80 bg-teal-600 text-white font-mono rounded-md px-1 text-center py-0.5 mr-1">
290
+ png
291
+ </span>
292
+ </DropdownMenuItem>
293
+ <DropdownMenuItem
294
+ className="text-xs"
295
+ onClick={() =>
296
+ downloadPngImage({
297
+ circuitJson,
298
+ unscopedName,
299
+ author,
300
+ format: "assembly",
301
+ })
302
+ }
303
+ >
304
+ <Download className="mr-1 h-3 w-3" />
305
+ <span className="flex-grow mr-6">Assembly PNG</span>
306
+ <span className="text-[0.6rem] opacity-80 bg-teal-600 text-white font-mono rounded-md px-1 text-center py-0.5 mr-1">
307
+ png
308
+ </span>
309
+ </DropdownMenuItem>
310
+ </>
311
+ )}
234
312
  </DropdownMenuContent>
235
313
  </DropdownMenu>
236
314
  <PcbDownloadDialog
237
315
  circuitJson={circuitJson}
238
- fileName={snippetUnscopedName || "circuit"}
316
+ fileName={unscopedName || "circuit"}
239
317
  />
240
318
  </div>
241
319
  )
@@ -167,6 +167,16 @@ const FileSidebar: React.FC<FileSidebarProps> = ({
167
167
  }
168
168
  }
169
169
 
170
+ const handleCreateFileBlur = () => {
171
+ if (newFileName.trim() === "") {
172
+ setIsCreatingFile(false)
173
+ setNewFileName("")
174
+ setErrorMessage("")
175
+ return
176
+ }
177
+ handleCreateFileInline()
178
+ }
179
+
170
180
  const toggleSidebar = () => {
171
181
  setSidebarOpen(!sidebarOpen)
172
182
  setErrorMessage("")
@@ -204,7 +214,7 @@ const FileSidebar: React.FC<FileSidebarProps> = ({
204
214
  value={newFileName}
205
215
  spellCheck={false}
206
216
  onChange={(e) => setNewFileName(e.target.value)}
207
- onBlur={handleCreateFileInline}
217
+ onBlur={handleCreateFileBlur}
208
218
  onKeyDown={(e) => {
209
219
  if (e.key === "Enter") {
210
220
  handleCreateFileInline()
@@ -8,6 +8,7 @@ import HeaderDropdown from "./HeaderDropdown"
8
8
  import { useState } from "react"
9
9
  import { useGlobalStore } from "@/hooks/use-global-store"
10
10
  import { Analytics } from "./Analytics"
11
+ import CmdKMenu from "./CmdKMenu"
11
12
 
12
13
  const SearchButtonComponent = () => {
13
14
  const [isExpanded, setIsExpanded] = useState(false)
@@ -57,10 +58,13 @@ export const Header2 = () => {
57
58
  <>
58
59
  <header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
59
60
  <div className="container mx-auto flex h-16 items-center justify-between px-2 md:px-6">
60
- <div className="flex items-center gap-2">
61
+ <PrefetchPageLink
62
+ href="/"
63
+ className="flex select-none items-center gap-2"
64
+ >
61
65
  <CircuitBoard className="h-6 w-6" />
62
66
  <span className="text-lg font-bold">tscircuit</span>
63
- </div>
67
+ </PrefetchPageLink>
64
68
  <nav className="flex md:hidden">
65
69
  {isLoggedIn && (
66
70
  <Link
@@ -120,6 +124,7 @@ export const Header2 = () => {
120
124
  </div>
121
125
  </div>
122
126
  </header>
127
+ <CmdKMenu />
123
128
  <Analytics />
124
129
  </>
125
130
  )
@@ -16,35 +16,38 @@ export const LogContent = ({
16
16
  error,
17
17
  }: {
18
18
  logs: Array<{
19
- type: "info" | "success" | "error"
20
- message: string
21
- timestamp: string
19
+ type?: "info" | "success" | "error"
20
+ msg?: string
21
+ timestamp?: string
22
22
  }>
23
23
  error?: ErrorObject | string | null
24
24
  }) => {
25
25
  return (
26
26
  <div className="font-mono text-xs space-y-1 min-w-0">
27
- {logs.map(
28
- (log, i) =>
29
- log.timestamp &&
30
- log.message && (
31
- <div
32
- key={i}
33
- className={`break-words whitespace-pre-wrap ${
34
- log.type === "error"
35
- ? "text-red-600"
36
- : log.type === "success"
37
- ? "text-green-600"
38
- : "text-gray-600"
39
- }`}
40
- >
27
+ {logs.map((log, i) => {
28
+ if (!log.msg) return null
29
+
30
+ return (
31
+ <div
32
+ key={i}
33
+ className={`break-words whitespace-pre-wrap ${
34
+ log.type === "error"
35
+ ? "text-red-600"
36
+ : log.type === "success"
37
+ ? "text-green-600"
38
+ : "text-gray-600"
39
+ }`}
40
+ >
41
+ {log.timestamp && (
41
42
  <span className="text-gray-500 whitespace-nowrap">
42
43
  {new Date(log.timestamp).toLocaleTimeString()}
43
- </span>{" "}
44
- <span className="break-all">{log.message}</span>
45
- </div>
46
- ),
47
- )}
44
+ </span>
45
+ )}
46
+ {log.timestamp && " "}
47
+ <span className="break-all">{log.msg}</span>
48
+ </div>
49
+ )
50
+ })}
48
51
  {error && (
49
52
  <div className="text-red-600 break-words whitespace-pre-wrap">
50
53
  {getErrorText(error)}
@@ -53,12 +53,12 @@ export const PackageBuildDetailsPage = () => {
53
53
  <div className="min-h-screen bg-gray-50 text-gray-900">
54
54
  <PackageBuildHeader />
55
55
 
56
- <div className="px-6 py-6 container mx-auto">
56
+ <div className="px-4 sm:px-6 py-4 sm:py-6 container mx-auto max-w-7xl">
57
57
  {/* Main Content */}
58
- <div className="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-8 items-start">
58
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-4 sm:gap-6 mb-6 sm:mb-8 items-start">
59
59
  {/* Preview Section */}
60
60
  <div className="lg:col-span-2">
61
- <div className="bg-white border border-gray-200 rounded-lg p-4 flex items-center justify-center max-h-[420px]">
61
+ <div className="bg-white border border-gray-200 rounded-lg p-4 flex items-center justify-center min-h-[280px] sm:min-h-[340px] lg:max-h-[420px]">
62
62
  <BuildPreviewContent />
63
63
  </div>
64
64
  </div>
@@ -68,7 +68,7 @@ export const PackageBuildDetailsPage = () => {
68
68
  </div>
69
69
 
70
70
  {/* Collapsible Sections */}
71
- <div className="space-y-4 mb-8">
71
+ <div className="space-y-4 mb-6 sm:mb-8">
72
72
  <CollapsibleSection
73
73
  title="Transpilation Logs"
74
74
  duration={computeDuration(
@@ -82,7 +82,7 @@ export const PackageBuildDetailsPage = () => {
82
82
  <LogContent
83
83
  logs={
84
84
  transpilation_logs ?? [
85
- { message: "No transpilation logs available" },
85
+ { msg: "No transpilation logs available" },
86
86
  ]
87
87
  }
88
88
  error={transpilation_error}
@@ -102,7 +102,7 @@ export const PackageBuildDetailsPage = () => {
102
102
  <LogContent
103
103
  logs={
104
104
  circuit_json_build_logs ?? [
105
- { message: "No Circuit JSON logs available" },
105
+ { msg: "No Circuit JSON logs available" },
106
106
  ]
107
107
  }
108
108
  error={circuit_json_build_error!}
@@ -7,19 +7,19 @@ export function BuildPreviewContent() {
7
7
 
8
8
  if (!packageRelease) {
9
9
  return (
10
- <div className="flex items-center justify-center">
11
- <div className="w-48 h-48 bg-gray-200 rounded animate-pulse-slow"></div>
10
+ <div className="flex items-center justify-center w-full h-full">
11
+ <div className="w-32 h-32 sm:w-48 sm:h-48 bg-gray-200 rounded animate-pulse-slow"></div>
12
12
  </div>
13
13
  )
14
14
  }
15
15
 
16
16
  return (
17
- <div className="flex items-center justify-center">
18
- <div className="rounded overflow-hidden">
17
+ <div className="flex items-center justify-center w-full h-full">
18
+ <div className="rounded overflow-hidden w-full max-w-full">
19
19
  <img
20
20
  src={`https://api.tscircuit.com/packages/images/${packageInfo?.name}/pcb.png`}
21
21
  alt="Package build preview"
22
- className="object-contain rounded max-h-[360px]"
22
+ className="object-contain rounded w-full h-auto max-h-[240px] sm:max-h-[300px] lg:max-h-[360px]"
23
23
  />
24
24
  </div>
25
25
  </div>
@@ -76,16 +76,16 @@ export function PackageBuildDetailsPanel() {
76
76
  : null
77
77
 
78
78
  return (
79
- <div className="space-y-6 bg-white p-4 border border-gray-200 rounded-lg">
79
+ <div className="space-y-4 sm:space-y-6 bg-white p-3 sm:p-4 border border-gray-200 rounded-lg">
80
80
  {/* Created */}
81
81
  <div>
82
82
  <h3 className="text-sm font-medium text-gray-600 mb-2">Created</h3>
83
- <div className="flex items-center gap-2">
84
- <div className="w-6 h-6 bg-orange-500 rounded-full flex items-center justify-center text-xs font-bold">
83
+ <div className="flex items-center gap-2 min-w-0">
84
+ <div className="w-6 h-6 bg-orange-500 rounded-full flex items-center justify-center text-xs font-bold flex-shrink-0">
85
85
  I
86
86
  </div>
87
- <span className="text-sm">{author}</span>
88
- <span className="text-sm text-gray-500">
87
+ <span className="text-sm truncate">{author}</span>
88
+ <span className="text-sm text-gray-500 flex-shrink-0">
89
89
  {timeAgo(created_at, "")}
90
90
  </span>
91
91
  </div>
@@ -96,7 +96,7 @@ export function PackageBuildDetailsPanel() {
96
96
  <h3 className="text-sm font-medium text-gray-600 mb-2">Status</h3>
97
97
  <div className="flex items-center gap-2">
98
98
  <div
99
- className={`w-2 h-2 ${getColorFromDisplayStatus(display_status)} rounded-full`}
99
+ className={`w-2 h-2 ${getColorFromDisplayStatus(display_status)} rounded-full flex-shrink-0`}
100
100
  ></div>
101
101
  <span className="text-sm">{capitalCase(display_status)}</span>
102
102
  {/* <Badge
@@ -110,12 +110,14 @@ export function PackageBuildDetailsPanel() {
110
110
 
111
111
  <div>
112
112
  <h3 className="text-sm font-medium text-gray-600 mb-2">Build Time</h3>
113
- <div className="flex items-center gap-2">
114
- <Clock className="w-4 h-4 text-gray-500" />
113
+ <div className="flex items-center gap-2 min-w-0">
114
+ <Clock className="w-4 h-4 text-gray-500 flex-shrink-0" />
115
115
  {elapsedMs !== null && (
116
- <span className="text-sm">{Math.floor(elapsedMs / 1000)}s</span>
116
+ <span className="text-sm flex-shrink-0">
117
+ {Math.floor(elapsedMs / 1000)}s
118
+ </span>
117
119
  )}
118
- <span className="text-sm text-gray-500">
120
+ <span className="text-sm text-gray-500 truncate">
119
121
  {buildStartedAt
120
122
  ? `Started ${timeAgo(buildStartedAt)}`
121
123
  : "waiting..."}
@@ -126,9 +128,9 @@ export function PackageBuildDetailsPanel() {
126
128
  {/* Version */}
127
129
  <div>
128
130
  <h3 className="text-sm font-medium text-gray-600 mb-2">Version</h3>
129
- <div className="flex items-center gap-2">
130
- <Globe className="w-4 h-4 text-gray-500" />
131
- <span className="text-sm">{packageRelease.version}</span>
131
+ <div className="flex items-center gap-2 min-w-0">
132
+ <Globe className="w-4 h-4 text-gray-500 flex-shrink-0" />
133
+ <span className="text-sm break-all">{packageRelease.version}</span>
132
134
  {/* <Badge variant="default" className="bg-blue-600 text-white text-xs">
133
135
  Current
134
136
  </Badge> */}
@@ -1,31 +1,33 @@
1
1
  import { Button } from "@/components/ui/button"
2
2
  import { useCurrentPackageRelease } from "@/hooks/use-current-package-release"
3
3
  import { useRebuildPackageReleaseMutation } from "@/hooks/use-rebuild-package-release-mutation"
4
- import { Github, RefreshCw, RotateCcw } from "lucide-react"
4
+ import { Github, RefreshCw } from "lucide-react"
5
5
  import { useParams } from "wouter"
6
6
  import { DownloadButtonAndMenu } from "../DownloadButtonAndMenu"
7
7
 
8
8
  export function PackageBuildHeader() {
9
9
  const { author, packageName } = useParams()
10
- const { packageRelease, refetch, isFetching } = useCurrentPackageRelease({
10
+ const { packageRelease } = useCurrentPackageRelease({
11
11
  include_logs: true,
12
12
  })
13
13
  const { mutate: rebuildPackage, isLoading } =
14
14
  useRebuildPackageReleaseMutation()
15
15
 
16
16
  return (
17
- <div className="border-b border-gray-200 bg-white px-6 py-4">
18
- <div className="flex items-center justify-between container mx-auto">
19
- <h1 className="text-2xl font-semibold">
20
- Package Build
21
- <a
22
- className="ml-2 bg-gray-100 px-2 py-1 rounded font-mono text-blue-600"
23
- href={`/${author}/${packageName}`}
24
- >
25
- {author}/{packageName}
26
- </a>
27
- </h1>
28
- <div className="flex items-center gap-3">
17
+ <div className="border-b border-gray-200 bg-white px-4 sm:px-6 py-4">
18
+ <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 sm:gap-4 container mx-auto max-w-7xl">
19
+ <div className="min-w-0 flex-1">
20
+ <h1 className="text-xl sm:text-2xl font-semibold flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2">
21
+ <span className="whitespace-nowrap">Package Build</span>
22
+ <a
23
+ className="bg-gray-100 px-2 py-1 rounded font-mono text-blue-600 text-sm sm:text-base truncate w-fit"
24
+ href={`/${author}/${packageName}`}
25
+ >
26
+ {author}/{packageName}
27
+ </a>
28
+ </h1>
29
+ </div>
30
+ <div className="flex items-center gap-2 sm:gap-3 flex-shrink-0">
29
31
  <Button
30
32
  variant="outline"
31
33
  size="sm"
@@ -38,7 +40,8 @@ export function PackageBuildHeader() {
38
40
  rel="noopener noreferrer"
39
41
  >
40
42
  <Github className="w-3 h-3 sm:w-4 sm:h-4 mr-1 sm:mr-2" />
41
- Report Issue
43
+ <span className="hidden xs:inline">Report Issue</span>
44
+ <span className="xs:hidden">Report</span>
42
45
  </a>
43
46
  </Button>
44
47
  <Button
@@ -56,19 +59,7 @@ export function PackageBuildHeader() {
56
59
  <RefreshCw className="w-3 h-3 sm:w-4 sm:h-4 mr-1 sm:mr-2" />
57
60
  {isLoading ? "Rebuilding..." : "Rebuild"}
58
61
  </Button>
59
- <Button
60
- variant="outline"
61
- size="icon"
62
- aria-label="Reload logs"
63
- className="border-gray-300 bg-white hover:bg-gray-50"
64
- onClick={() => refetch()}
65
- disabled={isFetching}
66
- >
67
- <RotateCcw className="w-3 h-3 sm:w-4 sm:h-4" />
68
- </Button>
69
- <DownloadButtonAndMenu
70
- snippetUnscopedName={`${author}/${packageName}`}
71
- />
62
+ <DownloadButtonAndMenu unscopedName={packageName} author={author} />
72
63
  </div>
73
64
  </div>
74
65
  </div>