@tscircuit/runframe 0.0.105 → 0.0.107

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.
@@ -0,0 +1,1110 @@
1
+ // lib/components/ui/tabs.tsx
2
+ import * as React from "react";
3
+ import * as TabsPrimitive from "@radix-ui/react-tabs";
4
+
5
+ // lib/utils/index.ts
6
+ import { clsx } from "clsx";
7
+ import { twMerge } from "tailwind-merge";
8
+ function cn(...inputs) {
9
+ return twMerge(clsx(inputs));
10
+ }
11
+ var capitalizeFirstLetters = (str) => {
12
+ return str.replace(/_/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
13
+ };
14
+
15
+ // lib/components/ui/tabs.tsx
16
+ import { jsx } from "react/jsx-runtime";
17
+ var Tabs = TabsPrimitive.Root;
18
+ var TabsList = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
19
+ TabsPrimitive.List,
20
+ {
21
+ ref,
22
+ className: cn(
23
+ "rf-inline-flex rf-h-9 rf-items-center rf-justify-center rf-rounded-lg rf-bg-zinc-100 rf-p-1 rf-text-zinc-500 dark:rf-bg-zinc-800 dark:rf-text-zinc-400",
24
+ className
25
+ ),
26
+ ...props
27
+ }
28
+ ));
29
+ TabsList.displayName = TabsPrimitive.List.displayName;
30
+ var TabsTrigger = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
31
+ TabsPrimitive.Trigger,
32
+ {
33
+ ref,
34
+ className: cn(
35
+ "rf-inline-flex rf-items-center rf-justify-center rf-whitespace-nowrap rf-rounded-md rf-px-3 rf-py-1 rf-text-sm rf-font-medium rf-ring-offset-white rf-transition-all focus-visible:rf-outline-none focus-visible:rf-ring-2 focus-visible:rf-ring-zinc-950 focus-visible:rf-ring-offset-2 disabled:rf-pointer-events-none disabled:rf-opacity-50 data-[state=active]:rf-bg-white data-[state=active]:rf-text-zinc-950 data-[state=active]:rf-shadow dark:rf-ring-offset-zinc-950 dark:focus-visible:rf-ring-zinc-300 dark:data-[state=active]:rf-bg-zinc-950 dark:data-[state=active]:rf-text-zinc-50",
36
+ className
37
+ ),
38
+ ...props
39
+ }
40
+ ));
41
+ TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
42
+ var TabsContent = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
43
+ TabsPrimitive.Content,
44
+ {
45
+ ref,
46
+ className: cn(
47
+ "rf-mt-2 rf-ring-offset-white focus-visible:rf-outline-none focus-visible:rf-ring-2 focus-visible:rf-ring-zinc-950 focus-visible:rf-ring-offset-2 dark:rf-ring-offset-zinc-950 dark:focus-visible:rf-ring-zinc-300",
48
+ className
49
+ ),
50
+ ...props
51
+ }
52
+ ));
53
+ TabsContent.displayName = TabsPrimitive.Content.displayName;
54
+
55
+ // lib/components/CircuitJsonPreview.tsx
56
+ import { CadViewer } from "@tscircuit/3d-viewer";
57
+ import "@tscircuit/pcb-viewer";
58
+ import { useCallback, useEffect as useEffect2, useState as useState4 } from "react";
59
+
60
+ // lib/components/ErrorFallback.tsx
61
+ import "react";
62
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
63
+ var ErrorFallback = ({ error }) => {
64
+ return /* @__PURE__ */ jsx2(
65
+ "div",
66
+ {
67
+ "data-testid": "error-container",
68
+ className: "rf-error-container rf-mt-4 rf-bg-red-50 rf-rounded-md rf-border rf-border-red-200",
69
+ children: /* @__PURE__ */ jsxs("div", { className: "rf-p-4", children: [
70
+ /* @__PURE__ */ jsx2("h2", { className: "rf-text-lg rf-font-semibold rf-text-red-800 rf-mb-3", children: "Error Loading 3D Viewer" }),
71
+ /* @__PURE__ */ jsx2("p", { className: "rf-text-xs rf-font-mono rf-whitespace-pre-wrap rf-text-red-700", children: error.message }),
72
+ /* @__PURE__ */ jsx2(
73
+ "details",
74
+ {
75
+ style: { whiteSpace: "pre-wrap" },
76
+ className: "rf-text-xs rf-font-mono rf-text-red-600 rf-mt-2",
77
+ children: error.stack
78
+ }
79
+ )
80
+ ] })
81
+ }
82
+ );
83
+ };
84
+
85
+ // lib/components/CircuitJsonPreview.tsx
86
+ import { ErrorBoundary } from "react-error-boundary";
87
+
88
+ // lib/components/ErrorTabContent.tsx
89
+ import { GitHubLogoIcon } from "@radix-ui/react-icons";
90
+ import { ClipboardIcon } from "lucide-react";
91
+
92
+ // lib/components/ui/button.tsx
93
+ import * as React3 from "react";
94
+ import { Slot } from "@radix-ui/react-slot";
95
+ import { cva } from "class-variance-authority";
96
+ import { jsx as jsx3 } from "react/jsx-runtime";
97
+ var buttonVariants = cva(
98
+ "rf-inline-flex rf-items-center rf-justify-center rf-gap-2 rf-whitespace-nowrap rf-rounded-md rf-text-sm rf-font-medium rf-transition-colors focus-visible:rf-outline-none focus-visible:rf-ring-1 focus-visible:rf-ring-zinc-950 disabled:rf-pointer-events-none disabled:rf-opacity-50 [&_svg]:rf-pointer-events-none [&_svg]:rf-size-4 [&_svg]:rf-shrink-0 dark:focus-visible:rf-ring-zinc-300",
99
+ {
100
+ variants: {
101
+ variant: {
102
+ default: "rf-bg-zinc-900 rf-text-zinc-50 rf-shadow hover:rf-bg-zinc-900/90 dark:rf-bg-zinc-50 dark:rf-text-zinc-900 dark:hover:rf-bg-zinc-50/90",
103
+ destructive: "rf-bg-red-500 rf-text-zinc-50 rf-shadow-sm hover:rf-bg-red-500/90 dark:rf-bg-red-900 dark:rf-text-zinc-50 dark:hover:rf-bg-red-900/90",
104
+ outline: "rf-border rf-border-zinc-200 rf-bg-white rf-shadow-sm hover:rf-bg-zinc-100 hover:rf-text-zinc-900 dark:rf-border-zinc-800 dark:rf-bg-zinc-950 dark:hover:rf-bg-zinc-800 dark:hover:rf-text-zinc-50",
105
+ secondary: "rf-bg-zinc-100 rf-text-zinc-900 rf-shadow-sm hover:rf-bg-zinc-100/80 dark:rf-bg-zinc-800 dark:rf-text-zinc-50 dark:hover:rf-bg-zinc-800/80",
106
+ ghost: "hover:rf-bg-zinc-100 hover:rf-text-zinc-900 dark:hover:rf-bg-zinc-800 dark:hover:rf-text-zinc-50",
107
+ link: "rf-text-zinc-900 rf-underline-offset-4 hover:rf-underline dark:rf-text-zinc-50"
108
+ },
109
+ size: {
110
+ default: "rf-h-9 rf-px-4 rf-py-2",
111
+ sm: "rf-h-8 rf-rounded-md rf-px-3 rf-text-xs",
112
+ lg: "rf-h-10 rf-rounded-md rf-px-8",
113
+ icon: "rf-h-9 rf-w-9"
114
+ }
115
+ },
116
+ defaultVariants: {
117
+ variant: "default",
118
+ size: "default"
119
+ }
120
+ }
121
+ );
122
+ var Button = React3.forwardRef(
123
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
124
+ const Comp = asChild ? Slot : "button";
125
+ return /* @__PURE__ */ jsx3(
126
+ Comp,
127
+ {
128
+ className: cn(buttonVariants({ variant, size, className })),
129
+ ref,
130
+ ...props
131
+ }
132
+ );
133
+ }
134
+ );
135
+ Button.displayName = "Button";
136
+
137
+ // lib/components/ErrorTabContent.tsx
138
+ import { Fragment, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
139
+ var ErrorTabContent = ({
140
+ code,
141
+ isStreaming,
142
+ errorMessage
143
+ }) => {
144
+ if (!errorMessage) {
145
+ return /* @__PURE__ */ jsx4("div", { className: "rf-mt-4 rf-bg-green-50 rf-rounded-md rf-border rf-border-green-200", children: /* @__PURE__ */ jsxs2("div", { className: "rf-p-4", children: [
146
+ /* @__PURE__ */ jsx4("h3", { className: "rf-text-lg rf-font-semibold rf-text-green-800 rf-mb-3", children: "No Errors \u{1F44C}" }),
147
+ /* @__PURE__ */ jsx4("p", { className: "rf-text-sm rf-text-green-700", children: "Your code is running without any errors." })
148
+ ] }) });
149
+ }
150
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
151
+ /* @__PURE__ */ jsx4("div", { className: "rf-mt-4 rf-bg-red-50 rf-rounded-md rf-border rf-border-red-200 rf-max-h-[500px] rf-overflow-y-auto", children: /* @__PURE__ */ jsxs2("div", { className: "rf-p-4", children: [
152
+ /* @__PURE__ */ jsx4("h3", { className: "rf-text-lg rf-font-semibold rf-text-red-800 rf-mb-3", children: "Error" }),
153
+ /* @__PURE__ */ jsx4("p", { className: "rf-text-xs rf-font-mono rf-whitespace-pre-wrap rf-text-red-600 rf-mt-2", children: errorMessage })
154
+ ] }) }),
155
+ /* @__PURE__ */ jsxs2("div", { className: "rf-flex rf-gap-2 rf-mt-4 rf-justify-end", children: [
156
+ /* @__PURE__ */ jsxs2(
157
+ Button,
158
+ {
159
+ variant: "outline",
160
+ onClick: () => {
161
+ if (!errorMessage) return;
162
+ navigator.clipboard.writeText(errorMessage);
163
+ alert("Error copied to clipboard!");
164
+ },
165
+ children: [
166
+ /* @__PURE__ */ jsx4(ClipboardIcon, { className: "rf-w-4 rf-h-4 rf-mr-2" }),
167
+ "Copy Error"
168
+ ]
169
+ }
170
+ ),
171
+ /* @__PURE__ */ jsxs2(
172
+ Button,
173
+ {
174
+ variant: "outline",
175
+ onClick: () => {
176
+ window.alert(
177
+ "Not supported yet! Please report/upvote an issue on github.com/tscircuit/tscircuit"
178
+ );
179
+ },
180
+ children: [
181
+ /* @__PURE__ */ jsx4(GitHubLogoIcon, { className: "rf-w-4 rf-h-4 rf-mr-2" }),
182
+ "Report Issue"
183
+ ]
184
+ }
185
+ )
186
+ ] })
187
+ ] });
188
+ };
189
+
190
+ // lib/components/CircuitJsonPreview.tsx
191
+ import { SchematicViewer } from "@tscircuit/schematic-viewer";
192
+ import { AssemblyViewer } from "@tscircuit/assembly-viewer";
193
+
194
+ // lib/components/PreviewEmptyState.tsx
195
+ import { PlayIcon } from "lucide-react";
196
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
197
+ var PreviewEmptyState = ({ onRunClicked }) => /* @__PURE__ */ jsxs3("div", { className: "rf-flex rf-items-center rf-gap-3 rf-bg-gray-100 rf-text-center rf-justify-center rf-py-10", children: [
198
+ "No circuit json loaded",
199
+ onRunClicked && /* @__PURE__ */ jsxs3(
200
+ Button,
201
+ {
202
+ className: "rf-bg-blue-600 rf-hover:bg-blue-500",
203
+ onClick: onRunClicked,
204
+ children: [
205
+ "Run Code",
206
+ /* @__PURE__ */ jsx5(PlayIcon, { className: "rf-w-3 rf-h-3 rf-ml-2" })
207
+ ]
208
+ }
209
+ )
210
+ ] });
211
+ var PreviewEmptyState_default = PreviewEmptyState;
212
+
213
+ // lib/components/CircuitJsonTableViewer/CircuitJsonTableViewer.tsx
214
+ import { useReducer, useState } from "react";
215
+
216
+ // lib/components/CircuitJsonTableViewer/ClickableText.tsx
217
+ import { jsx as jsx6 } from "react/jsx-runtime";
218
+ var ClickableText = ({
219
+ text,
220
+ onClick
221
+ }) => {
222
+ return /* @__PURE__ */ jsx6(
223
+ "span",
224
+ {
225
+ className: "rf-cursor-pointer rf-underline rf-text-blue-600 rf-mx-2",
226
+ onClick,
227
+ children: text
228
+ }
229
+ );
230
+ };
231
+
232
+ // lib/components/CircuitJsonTableViewer/HeaderCell.tsx
233
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
234
+ var HeaderCell = (p) => {
235
+ return /* @__PURE__ */ jsxs4("div", { className: "rf-leading-5", children: [
236
+ /* @__PURE__ */ jsx7("div", { className: "rf-py-2 rf-font-bold", children: p.column.name }),
237
+ /* @__PURE__ */ jsx7("div", { children: p.field?.() ?? /* @__PURE__ */ jsx7(
238
+ "input",
239
+ {
240
+ type: "text",
241
+ className: "rf-border rf-rounded rf-p-1 rf-w-full",
242
+ onChange: (e) => {
243
+ p.onTextChange?.(e.target.value);
244
+ }
245
+ }
246
+ ) })
247
+ ] });
248
+ };
249
+
250
+ // lib/components/CircuitJsonTableViewer/Modal.tsx
251
+ import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
252
+ var Modal = ({ open, children, title, onClose }) => {
253
+ if (!open) return null;
254
+ return /* @__PURE__ */ jsx8(
255
+ "div",
256
+ {
257
+ className: "rf-fixed rf-inset-0 rf-bg-black rf-bg-opacity-50 rf-flex rf-justify-center rf-items-center rf-z-50",
258
+ onClick: onClose,
259
+ children: /* @__PURE__ */ jsxs5(
260
+ "div",
261
+ {
262
+ className: "rf-bg-white rf-p-5 rf-rounded-lg rf-relative rf-w-11/12 rf-max-w-2xl",
263
+ onClick: (e) => {
264
+ e.preventDefault();
265
+ e.stopPropagation();
266
+ },
267
+ children: [
268
+ /* @__PURE__ */ jsx8("h2", { className: "rf-mt-0 rf-text-md rf-pb-4", children: title }),
269
+ /* @__PURE__ */ jsx8(
270
+ "button",
271
+ {
272
+ type: "button",
273
+ className: "rf-absolute rf-top-2 rf-right-4 rf-text-2xl rf-font-bold",
274
+ onClick: onClose,
275
+ children: "\xD7"
276
+ }
277
+ ),
278
+ children
279
+ ]
280
+ }
281
+ )
282
+ }
283
+ );
284
+ };
285
+ var Modal_default = Modal;
286
+
287
+ // lib/components/CircuitJsonTableViewer/CircuitJsonTableViewer.tsx
288
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
289
+ var CircuitJsonTableViewer = ({
290
+ elements
291
+ }) => {
292
+ const [modal, setModal] = useState({ open: false });
293
+ const [filters, setFilter] = useReducer(
294
+ (s, a) => ({
295
+ ...s,
296
+ ...a
297
+ }),
298
+ {}
299
+ );
300
+ const element_types = [...new Set(elements.map((e) => e.type))];
301
+ const elements2 = elements.map((e) => {
302
+ const primary_id = e[`${e.type}_id`];
303
+ const other_ids = Object.fromEntries(
304
+ Object.entries(e).filter(([k]) => {
305
+ if (k === `${e.type}_id`) return false;
306
+ if (!k.endsWith("_id")) return false;
307
+ return true;
308
+ })
309
+ );
310
+ const other_props = Object.fromEntries(
311
+ Object.entries(e).filter(([k]) => !k.endsWith("_id"))
312
+ );
313
+ return {
314
+ primary_id,
315
+ other_ids,
316
+ ...other_props,
317
+ _og_elm: e
318
+ };
319
+ });
320
+ const elements3 = elements2.map((e) => {
321
+ let selector_path = "";
322
+ const getSelectorPath = (e2) => {
323
+ const parent_key = Object.keys(e2.other_ids).find(
324
+ (k) => k.startsWith("source_")
325
+ );
326
+ if (!parent_key) return `.${e2.name}`;
327
+ const parent_type = parent_key.slice(0, -3);
328
+ const parent = elements2.find(
329
+ (p) => p.type === parent_type && p.primary_id === e2.other_ids[parent_key]
330
+ );
331
+ if (!parent) return `??? > .${e2.name}`;
332
+ if (!("name" in parent)) return `#${parent.primary_id} > .${e2.name}`;
333
+ return `${getSelectorPath(parent)} > .${e2.name}`;
334
+ };
335
+ if ("name" in e) {
336
+ selector_path = getSelectorPath(e);
337
+ }
338
+ return {
339
+ ...e,
340
+ selector_path
341
+ };
342
+ });
343
+ const columns = [
344
+ {
345
+ key: "primary_id",
346
+ name: "primary_id",
347
+ renderCell: (row) => /* @__PURE__ */ jsxs6("div", { className: "rf-flex rf-items-center", children: [
348
+ /* @__PURE__ */ jsx9(
349
+ ClickableText,
350
+ {
351
+ text: row.primary_id,
352
+ onClick: () => setFilter({
353
+ focused_id: row.primary_id,
354
+ id_search: void 0,
355
+ selector_search: void 0
356
+ })
357
+ }
358
+ ),
359
+ /* @__PURE__ */ jsx9("span", { className: "rf-flex-grow" }),
360
+ /* @__PURE__ */ jsx9(
361
+ ClickableText,
362
+ {
363
+ text: "(JSON)",
364
+ onClick: () => setModal({
365
+ open: true,
366
+ element: row._og_elm,
367
+ title: row.primary_id
368
+ })
369
+ }
370
+ )
371
+ ] }),
372
+ renderHeaderCell: (col) => /* @__PURE__ */ jsx9(
373
+ HeaderCell,
374
+ {
375
+ column: col,
376
+ onTextChange: (v) => setFilter({ id_search: v }),
377
+ field: !filters.focused_id ? void 0 : () => /* @__PURE__ */ jsxs6("div", { children: [
378
+ "Focus:",
379
+ " ",
380
+ /* @__PURE__ */ jsx9("span", { className: "rf-underline", children: filters.focused_id }),
381
+ /* @__PURE__ */ jsx9(
382
+ ClickableText,
383
+ {
384
+ text: "(unfocus)",
385
+ onClick: () => setFilter({ focused_id: void 0 })
386
+ }
387
+ )
388
+ ] })
389
+ }
390
+ )
391
+ },
392
+ {
393
+ key: "type",
394
+ name: "type",
395
+ renderHeaderCell: (col) => /* @__PURE__ */ jsx9(
396
+ HeaderCell,
397
+ {
398
+ column: col,
399
+ field: () => /* @__PURE__ */ jsxs6(
400
+ "select",
401
+ {
402
+ onChange: (e) => setFilter({ component_type_filter: e.target.value }),
403
+ className: "rf-border rf-rounded rf-p-1 rf-w-full",
404
+ children: [
405
+ /* @__PURE__ */ jsx9("option", { value: "any", children: "any" }, "any"),
406
+ /* @__PURE__ */ jsx9("option", { value: "source", children: "source" }, "source"),
407
+ /* @__PURE__ */ jsx9("option", { value: "source/pcb", children: "source/pcb" }, "source/pcb"),
408
+ /* @__PURE__ */ jsx9("option", { value: "source/schematic", children: "source/schematic" }, "source/schematic"),
409
+ element_types.map((type) => /* @__PURE__ */ jsx9("option", { value: type, children: type }, type))
410
+ ]
411
+ }
412
+ )
413
+ }
414
+ )
415
+ },
416
+ {
417
+ key: "name",
418
+ name: "name",
419
+ renderHeaderCell: (col) => /* @__PURE__ */ jsx9(
420
+ HeaderCell,
421
+ {
422
+ column: col,
423
+ onTextChange: (t) => setFilter({ name_search: t })
424
+ }
425
+ )
426
+ },
427
+ {
428
+ key: "selector_path",
429
+ name: "selector_path",
430
+ renderHeaderCell: (col) => /* @__PURE__ */ jsx9(
431
+ HeaderCell,
432
+ {
433
+ column: col,
434
+ onTextChange: (t) => setFilter({ selector_search: t })
435
+ }
436
+ )
437
+ },
438
+ {
439
+ key: "other_ids",
440
+ name: "other_ids",
441
+ renderCell: (row) => /* @__PURE__ */ jsx9("div", { className: "rf-space-x-2", children: Object.entries(row.other_ids).map(([other_id, v]) => /* @__PURE__ */ jsx9(
442
+ ClickableText,
443
+ {
444
+ text: v,
445
+ onClick: () => setFilter({ focused_id: v })
446
+ },
447
+ v
448
+ )) })
449
+ }
450
+ ];
451
+ const elements4 = elements3.filter((e) => {
452
+ if (!filters.name_search) return true;
453
+ return e.name?.toLowerCase()?.includes(filters.name_search.toLowerCase());
454
+ }).filter((e) => {
455
+ if (!filters.component_type_filter) return true;
456
+ if (filters.component_type_filter === "any") return true;
457
+ if (filters.component_type_filter === "source") {
458
+ return e.type.startsWith("source_");
459
+ }
460
+ if (filters.component_type_filter === "source/pcb") {
461
+ return e.type.startsWith("source_") || e.type.startsWith("pcb_");
462
+ }
463
+ if (filters.component_type_filter === "source/schematic") {
464
+ return e.type.startsWith("source_") || e.type.startsWith("schematic_");
465
+ }
466
+ return e.type?.includes(filters.component_type_filter);
467
+ }).filter((e) => {
468
+ if (!filters.selector_search) return true;
469
+ const parts = filters.selector_search.split(" ").filter((p) => p.length > 0);
470
+ return parts.every((part) => e.selector_path?.includes(part));
471
+ }).filter((e) => {
472
+ if (!filters.id_search) return true;
473
+ return e.primary_id?.includes(filters.id_search);
474
+ }).filter((e) => {
475
+ if (!filters.focused_id) return true;
476
+ if (e.primary_id === filters.focused_id) return true;
477
+ if (Object.values(e.other_ids).includes(filters.focused_id)) return true;
478
+ return false;
479
+ });
480
+ return /* @__PURE__ */ jsxs6("div", { className: "rf-font-mono rf-text-xs rf-bg-white", children: [
481
+ /* @__PURE__ */ jsx9("div", { className: "rf-overflow-x-auto", children: /* @__PURE__ */ jsxs6("table", { className: "rf-table-auto rf-w-full rf-text-left", children: [
482
+ /* @__PURE__ */ jsx9("thead", { children: /* @__PURE__ */ jsx9("tr", { children: columns.map((col) => /* @__PURE__ */ jsx9("th", { className: "rf-px-4 rf-py-2 rf-border-b", children: col.renderHeaderCell ? col.renderHeaderCell(col) : col.name }, col.key)) }) }),
483
+ /* @__PURE__ */ jsx9("tbody", { children: elements4.map((row, rowIndex) => /* @__PURE__ */ jsx9("tr", { className: "rf-hover:bg-gray-100", children: columns.map((col) => /* @__PURE__ */ jsx9("td", { className: "rf-px-4 rf-py-2 rf-border-b", children: col.renderCell ? col.renderCell(row) : row[col.key] }, col.key)) }, rowIndex)) })
484
+ ] }) }),
485
+ /* @__PURE__ */ jsx9(
486
+ Modal_default,
487
+ {
488
+ open: modal.open,
489
+ onClose: () => setModal({ open: false }),
490
+ title: modal.open ? modal.title : "",
491
+ children: /* @__PURE__ */ jsx9("div", { className: "rf-bg-gray-800 rf-p-3 rf-text-white rf-rounded", children: /* @__PURE__ */ jsx9("pre", { className: "rf-whitespace-pre-wrap", children: modal.open ? JSON.stringify(modal.element, null, 2) : "" }) })
492
+ }
493
+ )
494
+ ] });
495
+ };
496
+
497
+ // lib/components/BomTable.tsx
498
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
499
+ var linkify = (supplier, partNumber) => {
500
+ if (supplier === "jlcpcb") {
501
+ return /* @__PURE__ */ jsx10(
502
+ "a",
503
+ {
504
+ className: "rf-underline rf-text-blue-500",
505
+ target: "_blank",
506
+ rel: "noreferrer",
507
+ href: `https://jlcpcb.com/partdetail/${partNumber}`,
508
+ children: partNumber
509
+ }
510
+ );
511
+ }
512
+ return partNumber;
513
+ };
514
+ var BomTable = ({ circuitJson }) => {
515
+ const sourceComponents = circuitJson.filter(
516
+ (el) => el.type === "source_component"
517
+ );
518
+ const supplierColumns = /* @__PURE__ */ new Set();
519
+ for (const comp of sourceComponents) {
520
+ if (comp.supplier_part_numbers) {
521
+ for (const supplier of Object.keys(comp.supplier_part_numbers)) {
522
+ supplierColumns.add(supplier);
523
+ }
524
+ }
525
+ }
526
+ return /* @__PURE__ */ jsx10("div", { className: "rf-overflow-x-auto", children: /* @__PURE__ */ jsxs7("table", { className: "rf-w-full rf-text-left rf-text-sm", children: [
527
+ /* @__PURE__ */ jsx10("thead", { children: /* @__PURE__ */ jsxs7("tr", { className: "rf-border-b", children: [
528
+ /* @__PURE__ */ jsx10("th", { className: "rf-p-2", children: "Name" }),
529
+ Array.from(supplierColumns).map((supplier) => /* @__PURE__ */ jsx10("th", { className: "rf-p-2 rf-capitalize", children: supplier }, supplier))
530
+ ] }) }),
531
+ /* @__PURE__ */ jsx10("tbody", { children: sourceComponents.map((comp) => /* @__PURE__ */ jsxs7("tr", { className: "rf-border-b", children: [
532
+ /* @__PURE__ */ jsx10("td", { className: "rf-p-2", children: comp.name }),
533
+ Array.from(supplierColumns).map((supplier) => /* @__PURE__ */ jsx10("td", { className: "rf-p-2", children: linkify(
534
+ supplier,
535
+ comp.supplier_part_numbers?.[supplier]?.[0] || ""
536
+ ) }, supplier))
537
+ ] }, comp.source_component_id)) })
538
+ ] }) });
539
+ };
540
+
541
+ // lib/components/CircuitJsonPreview.tsx
542
+ import {
543
+ CheckIcon,
544
+ EllipsisIcon,
545
+ FullscreenIcon,
546
+ LoaderCircleIcon,
547
+ MinimizeIcon
548
+ } from "lucide-react";
549
+
550
+ // lib/components/ui/dropdown-menu.tsx
551
+ import * as React4 from "react";
552
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
553
+ import { Check, ChevronRight, Circle } from "lucide-react";
554
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
555
+ var DropdownMenu = DropdownMenuPrimitive.Root;
556
+ var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
557
+ var DropdownMenuSubTrigger = React4.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsxs8(
558
+ DropdownMenuPrimitive.SubTrigger,
559
+ {
560
+ ref,
561
+ className: cn(
562
+ "rf-flex rf-cursor-default rf-gap-2 rf-select-none rf-items-center rf-rounded-sm rf-px-2 rf-py-1.5 rf-text-sm rf-outline-none focus:rf-bg-zinc-100 data-[state=open]:rf-bg-zinc-100 [&_svg]:rf-pointer-events-none [&_svg]:rf-size-4 [&_svg]:rf-shrink-0 dark:focus:rf-bg-zinc-800 dark:data-[state=open]:rf-bg-zinc-800",
563
+ inset && "rf-pl-8",
564
+ className
565
+ ),
566
+ ...props,
567
+ children: [
568
+ children,
569
+ /* @__PURE__ */ jsx11(ChevronRight, { className: "rf-ml-auto" })
570
+ ]
571
+ }
572
+ ));
573
+ DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
574
+ var DropdownMenuSubContent = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx11(
575
+ DropdownMenuPrimitive.SubContent,
576
+ {
577
+ ref,
578
+ className: cn(
579
+ "rf-z-50 rf-min-w-[8rem] rf-overflow-hidden rf-rounded-md rf-border rf-border-zinc-200 rf-bg-white rf-p-1 rf-text-zinc-950 rf-shadow-lg data-[state=open]:rf-animate-in data-[state=closed]:rf-animate-out data-[state=closed]:rf-fade-out-0 data-[state=open]:rf-fade-in-0 data-[state=closed]:rf-zoom-out-95 data-[state=open]:rf-zoom-in-95 data-[side=bottom]:rf-slide-in-from-top-2 data-[side=left]:rf-slide-in-from-right-2 data-[side=right]:rf-slide-in-from-left-2 data-[side=top]:rf-slide-in-from-bottom-2 dark:rf-border-zinc-800 dark:rf-bg-zinc-950 dark:rf-text-zinc-50",
580
+ className
581
+ ),
582
+ ...props
583
+ }
584
+ ));
585
+ DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
586
+ var DropdownMenuContent = React4.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx11(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx11(
587
+ DropdownMenuPrimitive.Content,
588
+ {
589
+ ref,
590
+ sideOffset,
591
+ className: cn(
592
+ "rf-z-50 rf-min-w-[8rem] rf-overflow-hidden rf-rounded-md rf-border rf-border-zinc-200 rf-bg-white rf-p-1 rf-text-zinc-950 rf-shadow-md dark:rf-border-zinc-800 dark:rf-bg-zinc-950 dark:rf-text-zinc-50",
593
+ "data-[state=open]:rf-animate-in data-[state=closed]:rf-animate-out data-[state=closed]:rf-fade-out-0 data-[state=open]:rf-fade-in-0 data-[state=closed]:rf-zoom-out-95 data-[state=open]:rf-zoom-in-95 data-[side=bottom]:rf-slide-in-from-top-2 data-[side=left]:rf-slide-in-from-right-2 data-[side=right]:rf-slide-in-from-left-2 data-[side=top]:rf-slide-in-from-bottom-2",
594
+ className
595
+ ),
596
+ ...props
597
+ }
598
+ ) }));
599
+ DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
600
+ var DropdownMenuItem = React4.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx11(
601
+ DropdownMenuPrimitive.Item,
602
+ {
603
+ ref,
604
+ className: cn(
605
+ "rf-relative rf-flex rf-cursor-default rf-select-none rf-items-center rf-gap-2 rf-rounded-sm rf-px-2 rf-py-1.5 rf-text-sm rf-outline-none rf-transition-colors focus:rf-bg-zinc-100 focus:rf-text-zinc-900 data-[disabled]:rf-pointer-events-none data-[disabled]:rf-opacity-50 [&>svg]:rf-size-4 [&>svg]:rf-shrink-0 dark:focus:rf-bg-zinc-800 dark:focus:rf-text-zinc-50",
606
+ inset && "rf-pl-8",
607
+ className
608
+ ),
609
+ ...props
610
+ }
611
+ ));
612
+ DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
613
+ var DropdownMenuCheckboxItem = React4.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ jsxs8(
614
+ DropdownMenuPrimitive.CheckboxItem,
615
+ {
616
+ ref,
617
+ className: cn(
618
+ "rf-relative rf-flex rf-cursor-default rf-select-none rf-items-center rf-rounded-sm rf-py-1.5 rf-pl-8 rf-pr-2 rf-text-sm rf-outline-none rf-transition-colors focus:rf-bg-zinc-100 focus:rf-text-zinc-900 data-[disabled]:rf-pointer-events-none data-[disabled]:rf-opacity-50 dark:focus:rf-bg-zinc-800 dark:focus:rf-text-zinc-50",
619
+ className
620
+ ),
621
+ checked,
622
+ ...props,
623
+ children: [
624
+ /* @__PURE__ */ jsx11("span", { className: "rf-absolute rf-left-2 rf-flex rf-h-3.5 rf-w-3.5 rf-items-center rf-justify-center", children: /* @__PURE__ */ jsx11(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx11(Check, { className: "rf-h-4 rf-w-4" }) }) }),
625
+ children
626
+ ]
627
+ }
628
+ ));
629
+ DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
630
+ var DropdownMenuRadioItem = React4.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs8(
631
+ DropdownMenuPrimitive.RadioItem,
632
+ {
633
+ ref,
634
+ className: cn(
635
+ "rf-relative rf-flex rf-cursor-default rf-select-none rf-items-center rf-rounded-sm rf-py-1.5 rf-pl-8 rf-pr-2 rf-text-sm rf-outline-none rf-transition-colors focus:rf-bg-zinc-100 focus:rf-text-zinc-900 data-[disabled]:rf-pointer-events-none data-[disabled]:rf-opacity-50 dark:focus:rf-bg-zinc-800 dark:focus:rf-text-zinc-50",
636
+ className
637
+ ),
638
+ ...props,
639
+ children: [
640
+ /* @__PURE__ */ jsx11("span", { className: "rf-absolute rf-left-2 rf-flex rf-h-3.5 rf-w-3.5 rf-items-center rf-justify-center", children: /* @__PURE__ */ jsx11(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx11(Circle, { className: "rf-h-2 rf-w-2 rf-fill-current" }) }) }),
641
+ children
642
+ ]
643
+ }
644
+ ));
645
+ DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
646
+ var DropdownMenuLabel = React4.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx11(
647
+ DropdownMenuPrimitive.Label,
648
+ {
649
+ ref,
650
+ className: cn(
651
+ "rf-px-2 rf-py-1.5 rf-text-sm rf-font-semibold",
652
+ inset && "rf-pl-8",
653
+ className
654
+ ),
655
+ ...props
656
+ }
657
+ ));
658
+ DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
659
+ var DropdownMenuSeparator = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx11(
660
+ DropdownMenuPrimitive.Separator,
661
+ {
662
+ ref,
663
+ className: cn(
664
+ "rf--mx-1 rf-my-1 rf-h-px rf-bg-zinc-100 dark:rf-bg-zinc-800",
665
+ className
666
+ ),
667
+ ...props
668
+ }
669
+ ));
670
+ DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
671
+ var DropdownMenuShortcut = ({
672
+ className,
673
+ ...props
674
+ }) => {
675
+ return /* @__PURE__ */ jsx11(
676
+ "span",
677
+ {
678
+ className: cn(
679
+ "rf-ml-auto rf-text-xs rf-tracking-widest rf-opacity-60",
680
+ className
681
+ ),
682
+ ...props
683
+ }
684
+ );
685
+ };
686
+ DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
687
+
688
+ // lib/components/PcbViewerWithContainerHeight.tsx
689
+ import { useRef, useState as useState2, useLayoutEffect } from "react";
690
+ import { PCBViewer } from "@tscircuit/pcb-viewer";
691
+ import { jsx as jsx12 } from "react/jsx-runtime";
692
+ var PcbViewerWithContainerHeight = ({
693
+ containerClassName,
694
+ ...props
695
+ }) => {
696
+ const containerRef = useRef(null);
697
+ const [computedHeight, setComputedHeight] = useState2(620);
698
+ useLayoutEffect(() => {
699
+ const updateHeight = () => {
700
+ if (containerRef.current) {
701
+ const containerHeight = containerRef.current.clientHeight;
702
+ const screenHeight = window.innerHeight;
703
+ setComputedHeight(
704
+ Math.min(Math.max(containerHeight, 620), screenHeight)
705
+ );
706
+ }
707
+ };
708
+ updateHeight();
709
+ const resizeObserver = new ResizeObserver(updateHeight);
710
+ if (containerRef.current) {
711
+ resizeObserver.observe(containerRef.current);
712
+ }
713
+ window.addEventListener("resize", updateHeight);
714
+ return () => {
715
+ resizeObserver.disconnect();
716
+ window.removeEventListener("resize", updateHeight);
717
+ };
718
+ }, []);
719
+ return /* @__PURE__ */ jsx12(
720
+ "div",
721
+ {
722
+ ref: containerRef,
723
+ className: containerClassName || "rf-w-full rf-h-full",
724
+ children: /* @__PURE__ */ jsx12(PCBViewer, { ...props, height: computedHeight })
725
+ }
726
+ );
727
+ };
728
+
729
+ // lib/hooks/use-styles.ts
730
+ import { useEffect } from "react";
731
+
732
+ // lib/hooks/styles.generated.ts
733
+ var styles_generated_default = '*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-feature-settings:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]:where(:not([hidden=until-found])){display:none}:root{--radius:0.5rem}.rf-pointer-events-none{pointer-events:none}.rf-invisible{visibility:hidden}.rf-fixed{position:fixed}.rf-absolute{position:absolute}.rf-relative{position:relative}.rf-inset-0{inset:0}.rf-bottom-full{bottom:100%}.rf-left-1{left:.25rem}.rf-left-1\\/2{left:50%}.rf-left-2{left:.5rem}.rf-right-4{right:1rem}.rf-right-\\[4px\\]{right:4px}.rf-top-2{top:.5rem}.rf-top-\\[6px\\]{top:6px}.rf-z-50{z-index:50}.rf-z-\\[100\\]{z-index:100}.rf--mx-1{margin-left:-.25rem;margin-right:-.25rem}.rf-mx-2{margin-left:.5rem;margin-right:.5rem}.rf-my-1{margin-bottom:.25rem;margin-top:.25rem}.rf-mb-2{margin-bottom:.5rem}.rf-mb-3{margin-bottom:.75rem}.rf-mb-4{margin-bottom:1rem}.rf-ml-1{margin-left:.25rem}.rf-ml-1\\.5{margin-left:.375rem}.rf-ml-2{margin-left:.5rem}.rf-ml-auto{margin-left:auto}.rf-mr-1{margin-right:.25rem}.rf-mr-2{margin-right:.5rem}.rf-mt-0{margin-top:0}.rf-mt-2{margin-top:.5rem}.rf-mt-4{margin-top:1rem}.rf-flex{display:flex}.rf-inline-flex{display:inline-flex}.rf-size-4{height:1rem;width:1rem}.rf-h-1{height:.25rem}.rf-h-10{height:2.5rem}.rf-h-2{height:.5rem}.rf-h-3{height:.75rem}.rf-h-3\\.5{height:.875rem}.rf-h-4{height:1rem}.rf-h-60{height:15rem}.rf-h-8{height:2rem}.rf-h-9{height:2.25rem}.rf-h-\\[620px\\]{height:620px}.rf-h-\\[calc\\(100vh-96px\\)\\]{height:calc(100vh - 96px)}.rf-h-full{height:100%}.rf-h-px{height:1px}.rf-max-h-\\[500px\\]{max-height:500px}.rf-min-h-\\[620px\\]{min-height:620px}.rf-min-h-\\[calc\\(100vh-240px\\)\\]{min-height:calc(100vh - 240px)}.rf-w-1{width:.25rem}.rf-w-11{width:2.75rem}.rf-w-11\\/12{width:91.666667%}.rf-w-2{width:.5rem}.rf-w-3{width:.75rem}.rf-w-3\\.5{width:.875rem}.rf-w-4{width:1rem}.rf-w-9{width:2.25rem}.rf-w-96{width:24rem}.rf-w-full{width:100%}.rf-min-w-\\[8rem\\]{min-width:8rem}.rf-max-w-2xl{max-width:42rem}.rf-shrink-0{flex-shrink:0}.rf-flex-grow{flex-grow:1}.rf-table-auto{table-layout:auto}.rf--translate-x-1{--tw-translate-x:-0.25rem}.rf--translate-x-1,.rf--translate-x-1\\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rf--translate-x-1\\/2{--tw-translate-x:-50%}.rf-transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes rf-spin{to{transform:rotate(1turn)}}.rf-animate-spin{animation:rf-spin 1s linear infinite}.rf-cursor-default{cursor:default}.rf-cursor-pointer{cursor:pointer}.rf-select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.rf-flex-col{flex-direction:column}.rf-items-center{align-items:center}.rf-justify-end{justify-content:flex-end}.rf-justify-center{justify-content:center}.rf-justify-between{justify-content:space-between}.rf-gap-2{gap:.5rem}.rf-gap-3{gap:.75rem}.rf-space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.5rem*var(--tw-space-x-reverse))}.rf-space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.5rem*var(--tw-space-y-reverse));margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)))}.rf-overflow-auto{overflow:auto}.rf-overflow-hidden{overflow:hidden}.rf-overflow-visible{overflow:visible}.rf-overflow-x-auto{overflow-x:auto}.rf-overflow-y-auto{overflow-y:auto}.rf-whitespace-nowrap{white-space:nowrap}.rf-whitespace-pre-wrap{white-space:pre-wrap}.rf-rounded{border-radius:.25rem}.rf-rounded-full{border-radius:9999px}.rf-rounded-lg{border-radius:var(--radius)}.rf-rounded-md{border-radius:calc(var(--radius) - 2px)}.rf-rounded-sm{border-radius:calc(var(--radius) - 4px)}.rf-border{border-width:1px}.rf-border-b{border-bottom-width:1px}.rf-border-green-200{--tw-border-opacity:1;border-color:rgb(187 247 208/var(--tw-border-opacity,1))}.rf-border-neutral-200{--tw-border-opacity:1;border-color:rgb(229 229 229/var(--tw-border-opacity,1))}.rf-border-red-200{--tw-border-opacity:1;border-color:rgb(254 202 202/var(--tw-border-opacity,1))}.rf-border-zinc-200{--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1))}.rf-bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.rf-bg-blue-500{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity,1))}.rf-bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.rf-bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.rf-bg-gray-500{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity,1))}.rf-bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.rf-bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity,1))}.rf-bg-green-50{--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity,1))}.rf-bg-neutral-100{--tw-bg-opacity:1;background-color:rgb(245 245 245/var(--tw-bg-opacity,1))}.rf-bg-neutral-50{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity,1))}.rf-bg-neutral-800{--tw-bg-opacity:1;background-color:rgb(38 38 38/var(--tw-bg-opacity,1))}.rf-bg-neutral-900{--tw-bg-opacity:1;background-color:rgb(23 23 23/var(--tw-bg-opacity,1))}.rf-bg-orange-400{--tw-bg-opacity:1;background-color:rgb(251 146 60/var(--tw-bg-opacity,1))}.rf-bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.rf-bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.rf-bg-red-900{--tw-bg-opacity:1;background-color:rgb(127 29 29/var(--tw-bg-opacity,1))}.rf-bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.rf-bg-zinc-100{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.rf-bg-zinc-50{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity,1))}.rf-bg-zinc-800{--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}.rf-bg-zinc-900{--tw-bg-opacity:1;background-color:rgb(24 24 27/var(--tw-bg-opacity,1))}.rf-bg-opacity-50{--tw-bg-opacity:0.5}.rf-fill-current{fill:currentColor}.rf-p-0{padding:0}.rf-p-0\\.5{padding:.125rem}.rf-p-1{padding:.25rem}.rf-p-2{padding:.5rem}.rf-p-3{padding:.75rem}.rf-p-4{padding:1rem}.rf-p-5{padding:1.25rem}.rf-p-6{padding:1.5rem}.rf-px-1{padding-left:.25rem;padding-right:.25rem}.rf-px-1\\.5{padding-left:.375rem;padding-right:.375rem}.rf-px-2{padding-left:.5rem;padding-right:.5rem}.rf-px-3{padding-left:.75rem;padding-right:.75rem}.rf-px-4{padding-left:1rem;padding-right:1rem}.rf-px-8{padding-left:2rem;padding-right:2rem}.rf-py-1{padding-bottom:.25rem;padding-top:.25rem}.rf-py-1\\.5{padding-bottom:.375rem;padding-top:.375rem}.rf-py-10{padding-bottom:2.5rem;padding-top:2.5rem}.rf-py-2{padding-bottom:.5rem;padding-top:.5rem}.rf-pb-4{padding-bottom:1rem}.rf-pl-8{padding-left:2rem}.rf-pr-2{padding-right:.5rem}.rf-text-left{text-align:left}.rf-text-center{text-align:center}.rf-font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.rf-text-2xl{font-size:1.5rem;line-height:2rem}.rf-text-\\[8px\\]{font-size:8px}.rf-text-lg{font-size:1.125rem;line-height:1.75rem}.rf-text-sm{font-size:.875rem;line-height:1.25rem}.rf-text-xs{font-size:.75rem;line-height:1rem}.rf-font-bold{font-weight:700}.rf-font-medium{font-weight:500}.rf-font-semibold{font-weight:600}.rf-capitalize{text-transform:capitalize}.rf-tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.rf-leading-5{line-height:1.25rem}.rf-tracking-widest{letter-spacing:.1em}.rf-text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity,1))}.rf-text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.rf-text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.rf-text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.rf-text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.rf-text-green-500{--tw-text-opacity:1;color:rgb(34 197 94/var(--tw-text-opacity,1))}.rf-text-green-700{--tw-text-opacity:1;color:rgb(21 128 61/var(--tw-text-opacity,1))}.rf-text-green-800{--tw-text-opacity:1;color:rgb(22 101 52/var(--tw-text-opacity,1))}.rf-text-neutral-50{--tw-text-opacity:1;color:rgb(250 250 250/var(--tw-text-opacity,1))}.rf-text-neutral-500{--tw-text-opacity:1;color:rgb(115 115 115/var(--tw-text-opacity,1))}.rf-text-neutral-900{--tw-text-opacity:1;color:rgb(23 23 23/var(--tw-text-opacity,1))}.rf-text-neutral-950{--tw-text-opacity:1;color:rgb(10 10 10/var(--tw-text-opacity,1))}.rf-text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity,1))}.rf-text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity,1))}.rf-text-red-800{--tw-text-opacity:1;color:rgb(153 27 27/var(--tw-text-opacity,1))}.rf-text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.rf-text-zinc-50{--tw-text-opacity:1;color:rgb(250 250 250/var(--tw-text-opacity,1))}.rf-text-zinc-500{--tw-text-opacity:1;color:rgb(113 113 122/var(--tw-text-opacity,1))}.rf-text-zinc-900{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.rf-text-zinc-950{--tw-text-opacity:1;color:rgb(9 9 11/var(--tw-text-opacity,1))}.rf-underline{text-decoration-line:underline}.rf-underline-offset-4{text-underline-offset:4px}.rf-opacity-0{opacity:0}.rf-opacity-50{opacity:.5}.rf-opacity-60{opacity:.6}.rf-shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.rf-shadow,.rf-shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.rf-shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.rf-shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.rf-shadow-md,.rf-shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.rf-shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.rf-outline-none{outline:2px solid transparent;outline-offset:2px}.rf-ring-offset-white{--tw-ring-offset-color:#fff}.rf-transition-all{transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}.rf-transition-colors{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.rf-transition-opacity{transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}@keyframes enter{0%{opacity:var(--tw-enter-opacity,1);transform:translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0) scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1)) rotate(var(--tw-enter-rotate,0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity,1);transform:translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0) scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1)) rotate(var(--tw-exit-rotate,0))}}.hover\\:rf-bg-red-500\\/90:hover{background-color:rgba(239,68,68,.9)}.hover\\:rf-bg-zinc-100:hover{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.hover\\:rf-bg-zinc-100\\/80:hover{background-color:hsla(240,5%,96%,.8)}.hover\\:rf-bg-zinc-900\\/90:hover{background-color:rgba(24,24,27,.9)}.hover\\:rf-text-zinc-900:hover{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.hover\\:rf-underline:hover{text-decoration-line:underline}.focus\\:rf-bg-zinc-100:focus{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.focus\\:rf-text-zinc-900:focus{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.focus-visible\\:rf-outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\\:rf-ring-1:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus-visible\\:rf-ring-2:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus-visible\\:rf-ring-zinc-950:focus-visible{--tw-ring-opacity:1;--tw-ring-color:rgb(9 9 11/var(--tw-ring-opacity,1))}.focus-visible\\:rf-ring-offset-2:focus-visible{--tw-ring-offset-width:2px}.disabled\\:rf-pointer-events-none:disabled{pointer-events:none}.disabled\\:rf-opacity-50:disabled{opacity:.5}.data-\\[disabled\\]\\:rf-pointer-events-none[data-disabled]{pointer-events:none}.data-\\[state\\=active\\]\\:rf-bg-white[data-state=active]{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.data-\\[state\\=open\\]\\:rf-bg-zinc-100[data-state=open]{--tw-bg-opacity:1;background-color:rgb(244 244 245/var(--tw-bg-opacity,1))}.data-\\[state\\=active\\]\\:rf-text-zinc-950[data-state=active]{--tw-text-opacity:1;color:rgb(9 9 11/var(--tw-text-opacity,1))}.data-\\[disabled\\]\\:rf-opacity-50[data-disabled]{opacity:.5}.data-\\[state\\=active\\]\\:rf-shadow[data-state=active]{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.data-\\[state\\=open\\]\\:rf-animate-in[data-state=open]{animation-duration:.15s;animation-name:enter;--tw-enter-opacity:initial;--tw-enter-scale:initial;--tw-enter-rotate:initial;--tw-enter-translate-x:initial;--tw-enter-translate-y:initial}.data-\\[state\\=closed\\]\\:rf-animate-out[data-state=closed]{animation-duration:.15s;animation-name:exit;--tw-exit-opacity:initial;--tw-exit-scale:initial;--tw-exit-rotate:initial;--tw-exit-translate-x:initial;--tw-exit-translate-y:initial}.data-\\[state\\=closed\\]\\:rf-fade-out-0[data-state=closed]{--tw-exit-opacity:0}.data-\\[state\\=open\\]\\:rf-fade-in-0[data-state=open]{--tw-enter-opacity:0}.data-\\[state\\=closed\\]\\:rf-zoom-out-95[data-state=closed]{--tw-exit-scale:.95}.data-\\[state\\=open\\]\\:rf-zoom-in-95[data-state=open]{--tw-enter-scale:.95}.data-\\[side\\=bottom\\]\\:rf-slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y:-0.5rem}.data-\\[side\\=left\\]\\:rf-slide-in-from-right-2[data-side=left]{--tw-enter-translate-x:0.5rem}.data-\\[side\\=right\\]\\:rf-slide-in-from-left-2[data-side=right]{--tw-enter-translate-x:-0.5rem}.data-\\[side\\=top\\]\\:rf-slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y:0.5rem}.dark\\:rf-border-zinc-800:is(.rf-dark *){--tw-border-opacity:1;border-color:rgb(39 39 42/var(--tw-border-opacity,1))}.dark\\:rf-bg-red-900:is(.rf-dark *){--tw-bg-opacity:1;background-color:rgb(127 29 29/var(--tw-bg-opacity,1))}.dark\\:rf-bg-zinc-50:is(.rf-dark *){--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity,1))}.dark\\:rf-bg-zinc-800:is(.rf-dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}.dark\\:rf-bg-zinc-950:is(.rf-dark *){--tw-bg-opacity:1;background-color:rgb(9 9 11/var(--tw-bg-opacity,1))}.dark\\:rf-text-zinc-400:is(.rf-dark *){--tw-text-opacity:1;color:rgb(161 161 170/var(--tw-text-opacity,1))}.dark\\:rf-text-zinc-50:is(.rf-dark *){--tw-text-opacity:1;color:rgb(250 250 250/var(--tw-text-opacity,1))}.dark\\:rf-text-zinc-900:is(.rf-dark *){--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.dark\\:rf-ring-offset-zinc-950:is(.rf-dark *){--tw-ring-offset-color:#09090b}.dark\\:hover\\:rf-bg-red-900\\/90:hover:is(.rf-dark *){background-color:rgba(127,29,29,.9)}.dark\\:hover\\:rf-bg-zinc-50\\/90:hover:is(.rf-dark *){background-color:hsla(0,0%,98%,.9)}.dark\\:hover\\:rf-bg-zinc-800:hover:is(.rf-dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}.dark\\:hover\\:rf-bg-zinc-800\\/80:hover:is(.rf-dark *){background-color:rgba(39,39,42,.8)}.dark\\:hover\\:rf-text-zinc-50:hover:is(.rf-dark *){--tw-text-opacity:1;color:rgb(250 250 250/var(--tw-text-opacity,1))}.dark\\:focus\\:rf-bg-zinc-800:focus:is(.rf-dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}.dark\\:focus\\:rf-text-zinc-50:focus:is(.rf-dark *){--tw-text-opacity:1;color:rgb(250 250 250/var(--tw-text-opacity,1))}.dark\\:focus-visible\\:rf-ring-zinc-300:focus-visible:is(.rf-dark *){--tw-ring-opacity:1;--tw-ring-color:rgb(212 212 216/var(--tw-ring-opacity,1))}.dark\\:data-\\[state\\=active\\]\\:rf-bg-zinc-950[data-state=active]:is(.rf-dark *){--tw-bg-opacity:1;background-color:rgb(9 9 11/var(--tw-bg-opacity,1))}.dark\\:data-\\[state\\=open\\]\\:rf-bg-zinc-800[data-state=open]:is(.rf-dark *){--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}.dark\\:data-\\[state\\=active\\]\\:rf-text-zinc-50[data-state=active]:is(.rf-dark *){--tw-text-opacity:1;color:rgb(250 250 250/var(--tw-text-opacity,1))}.\\[\\&\\>svg\\]\\:rf-size-4>svg{height:1rem;width:1rem}.\\[\\&\\>svg\\]\\:rf-shrink-0>svg{flex-shrink:0}.\\[\\&_svg\\]\\:rf-pointer-events-none svg{pointer-events:none}.\\[\\&_svg\\]\\:rf-size-4 svg{height:1rem;width:1rem}.\\[\\&_svg\\]\\:rf-shrink-0 svg{flex-shrink:0}';
734
+
735
+ // lib/hooks/use-styles.ts
736
+ var useStyles = () => {
737
+ useEffect(() => {
738
+ const existingStyle = document.querySelector(
739
+ 'style[data-styles="tscircuit-runframe"]'
740
+ );
741
+ if (existingStyle) return;
742
+ const styleElement = document.createElement("style");
743
+ styleElement.setAttribute("data-styles", "tscircuit-runframe");
744
+ styleElement.textContent = styles_generated_default;
745
+ document.head.appendChild(styleElement);
746
+ }, []);
747
+ };
748
+
749
+ // lib/components/RenderLogViewer/RenderLogViewer.tsx
750
+ import { orderedRenderPhases as orderedRenderPhases2 } from "@tscircuit/core";
751
+ import { useState as useState3 } from "react";
752
+
753
+ // lib/components/RenderLogViewer/RenderTimingsBar.tsx
754
+ import { orderedRenderPhases } from "@tscircuit/core";
755
+ import "react";
756
+ import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
757
+ var RenderTimingsBar = ({
758
+ phaseTimings
759
+ }) => {
760
+ if (!phaseTimings) return null;
761
+ const getPhaseColor = (index) => {
762
+ const hue = index * 137.5 % 360;
763
+ return `hsl(${hue}, 70%, 50%)`;
764
+ };
765
+ const totalTime = Object.values(phaseTimings).reduce(
766
+ (sum, time) => sum + time,
767
+ 0
768
+ );
769
+ return /* @__PURE__ */ jsxs9("div", { className: "rf-space-y-2 rf-w-full rf-px-4", children: [
770
+ /* @__PURE__ */ jsx13("div", { className: "rf-relative rf-h-8 rf-flex rf-rounded-sm", children: orderedRenderPhases.map((phase, index) => {
771
+ const time = phaseTimings[phase] || 0;
772
+ const width = time / totalTime * 100;
773
+ return /* @__PURE__ */ jsx13(
774
+ "div",
775
+ {
776
+ className: "rf-group rf-relative rf-overflow-visible",
777
+ style: {
778
+ width: `${width}%`,
779
+ backgroundColor: getPhaseColor(index)
780
+ },
781
+ children: /* @__PURE__ */ jsxs9("div", { className: "rf-opacity-0 rf-group-hover:opacity-100 rf-transition-opacity rf-absolute rf-bottom-full rf-left-1/2 rf-transform rf--translate-x-1/2 rf-mb-2 rf-px-2 rf-py-1 rf-text-xs rf-whitespace-nowrap rf-rounded rf-bg-gray-900 rf-text-white rf-pointer-events-none", children: [
782
+ phase,
783
+ ": ",
784
+ time,
785
+ "ms"
786
+ ] })
787
+ },
788
+ phase
789
+ );
790
+ }) }),
791
+ /* @__PURE__ */ jsxs9("div", { className: "rf-text-xs rf-text-gray-500", children: [
792
+ "Total: ",
793
+ totalTime.toFixed(2),
794
+ "ms"
795
+ ] })
796
+ ] });
797
+ };
798
+ var RenderTimingsBar_default = RenderTimingsBar;
799
+
800
+ // lib/components/RenderLogViewer/RenderLogViewer.tsx
801
+ import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
802
+ var RenderLogViewer = ({
803
+ renderLog
804
+ }) => {
805
+ const [sortOption, setSortOption] = useState3(
806
+ "chronological"
807
+ );
808
+ if (!renderLog)
809
+ return /* @__PURE__ */ jsx14("div", { className: "rf-p-4 rf-bg-gray-100 rf-rounded-md", children: "No render log, make sure this tab is open when you render (TODO add a rerender button here)" });
810
+ const orderedPhaseTimings = Object.entries(renderLog?.phaseTimings ?? {});
811
+ if (sortOption === "chronological") {
812
+ orderedPhaseTimings.sort(
813
+ (a, b) => orderedRenderPhases2.indexOf(a[0]) - orderedRenderPhases2.indexOf(b[0])
814
+ );
815
+ } else {
816
+ orderedPhaseTimings.sort((a, b) => b[1] - a[1]);
817
+ }
818
+ return /* @__PURE__ */ jsxs10("div", { children: [
819
+ /* @__PURE__ */ jsxs10("div", { className: "rf-flex rf-justify-between rf-items-center", children: [
820
+ /* @__PURE__ */ jsx14("div", { children: "Render Logs" }),
821
+ /* @__PURE__ */ jsxs10("div", { className: "rf-mb-4 rf-pr-2 rf-flex rf-text-xs rf-items-center", children: [
822
+ /* @__PURE__ */ jsx14("div", { className: "rf-mr-2", children: "Sort by:" }),
823
+ /* @__PURE__ */ jsxs10(
824
+ "select",
825
+ {
826
+ value: sortOption,
827
+ onChange: (e) => setSortOption(e.target.value),
828
+ className: "rf-px-2 rf-py-1 rf-border rf-rounded rf-text-xs",
829
+ children: [
830
+ /* @__PURE__ */ jsx14("option", { value: "chronological", children: "Phase Order" }),
831
+ /* @__PURE__ */ jsx14("option", { value: "longest", children: "Duration" })
832
+ ]
833
+ }
834
+ )
835
+ ] })
836
+ ] }),
837
+ /* @__PURE__ */ jsx14(RenderTimingsBar_default, { phaseTimings: renderLog.phaseTimings }),
838
+ /* @__PURE__ */ jsxs10("table", { className: "rf-w-full rf-text-xs", children: [
839
+ /* @__PURE__ */ jsx14("thead", { children: /* @__PURE__ */ jsxs10("tr", { children: [
840
+ /* @__PURE__ */ jsx14("th", { className: "rf-text-left rf-p-2", children: "Phase Order" }),
841
+ /* @__PURE__ */ jsx14("th", { className: "rf-text-left rf-p-2", children: "Phase" }),
842
+ /* @__PURE__ */ jsx14("th", { className: "rf-text-left rf-p-2", children: "Duration (ms)" })
843
+ ] }) }),
844
+ /* @__PURE__ */ jsx14("tbody", { children: orderedPhaseTimings.map(([phase, duration]) => /* @__PURE__ */ jsxs10("tr", { children: [
845
+ /* @__PURE__ */ jsx14("td", { className: "rf-p-2", children: orderedRenderPhases2.indexOf(phase) }),
846
+ /* @__PURE__ */ jsx14("td", { className: "rf-p-2", children: phase }),
847
+ /* @__PURE__ */ jsx14("td", { className: "rf-p-2", children: duration })
848
+ ] }, phase)) })
849
+ ] })
850
+ ] });
851
+ };
852
+
853
+ // lib/components/CircuitJsonPreview.tsx
854
+ import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
855
+ var dropdownMenuItems = [
856
+ "assembly",
857
+ "bom",
858
+ "circuit_json",
859
+ "errors",
860
+ "render_log"
861
+ ];
862
+ var CircuitJsonPreview = ({
863
+ code,
864
+ onRunClicked = void 0,
865
+ tsxRunTriggerCount,
866
+ errorMessage,
867
+ circuitJsonKey = "",
868
+ circuitJson,
869
+ showCodeTab = false,
870
+ codeTabContent,
871
+ showJsonTab = true,
872
+ showRenderLogTab = true,
873
+ onActiveTabChange,
874
+ renderLog,
875
+ showImportAndFormatButtons = true,
876
+ className,
877
+ headerClassName,
878
+ leftHeaderContent,
879
+ readOnly,
880
+ isStreaming,
881
+ onToggleFullScreen,
882
+ isFullScreen,
883
+ isRunningCode,
884
+ hasCodeChangedSinceLastRun,
885
+ onEditEvent,
886
+ editEvents,
887
+ defaultActiveTab
888
+ }) => {
889
+ useStyles();
890
+ const [activeTab, setActiveTabState] = useState4(defaultActiveTab ?? "pcb");
891
+ const setActiveTab = useCallback(
892
+ (tab) => {
893
+ setActiveTabState(tab);
894
+ onActiveTabChange?.(tab);
895
+ },
896
+ [onActiveTabChange]
897
+ );
898
+ useEffect2(() => {
899
+ if (errorMessage) {
900
+ setActiveTab("errors");
901
+ }
902
+ }, [errorMessage]);
903
+ useEffect2(() => {
904
+ if (activeTab === "code" && circuitJson && !errorMessage) {
905
+ setActiveTab("pcb");
906
+ }
907
+ }, [circuitJson]);
908
+ return /* @__PURE__ */ jsx15("div", { className: cn("flex flex-col relative", className), children: /* @__PURE__ */ jsx15("div", { className: "rf-md:sticky rf-md:top-2", children: /* @__PURE__ */ jsxs11(
909
+ Tabs,
910
+ {
911
+ value: activeTab,
912
+ onValueChange: setActiveTab,
913
+ className: "rf-flex-grow rf-flex rf-flex-col rf-p-2",
914
+ children: [
915
+ /* @__PURE__ */ jsxs11(
916
+ "div",
917
+ {
918
+ className: cn("rf-flex rf-items-center rf-gap-2", headerClassName),
919
+ children: [
920
+ leftHeaderContent,
921
+ leftHeaderContent && /* @__PURE__ */ jsx15("div", { className: "rf-flex-grow" }),
922
+ !leftHeaderContent && /* @__PURE__ */ jsx15("div", { className: "rf-flex-grow" }),
923
+ renderLog && renderLog.progress !== 1 && /* @__PURE__ */ jsxs11("div", { className: "rf-flex rf-items-center rf-gap-2", children: [
924
+ renderLog.lastRenderEvent && /* @__PURE__ */ jsx15("div", { className: "rf-text-xs rf-text-gray-500", children: renderLog.lastRenderEvent?.type.split("renderable:renderLifecycle:")[1].split(":")[0] }),
925
+ /* @__PURE__ */ jsx15("div", { className: "rf-w-4 rf-h-4 rf-bg-blue-500 rf-opacity-50 rf-rounded-full rf-text-white", children: /* @__PURE__ */ jsx15(LoaderCircleIcon, { className: "rf-w-4 rf-h-4 rf-animate-spin" }) }),
926
+ /* @__PURE__ */ jsxs11("div", { className: "rf-text-xs rf-font-bold rf-text-gray-700 rf-tabular-nums", children: [
927
+ ((renderLog.progress ?? 0) * 100).toFixed(1),
928
+ "%"
929
+ ] })
930
+ ] }),
931
+ /* @__PURE__ */ jsxs11(TabsList, { children: [
932
+ showCodeTab && /* @__PURE__ */ jsx15(TabsTrigger, { value: "code", children: "Code" }),
933
+ /* @__PURE__ */ jsxs11(TabsTrigger, { value: "pcb", className: "rf-whitespace-nowrap", children: [
934
+ circuitJson && /* @__PURE__ */ jsx15(
935
+ "span",
936
+ {
937
+ className: cn(
938
+ "rf-inline-flex rf-items-center rf-justify-center rf-w-2 rf-h-2 rf-mr-1 rf-text-xs rf-font-bold rf-text-white rf-rounded-full",
939
+ !hasCodeChangedSinceLastRun ? "rf-bg-blue-500" : "rf-bg-gray-500"
940
+ )
941
+ }
942
+ ),
943
+ "PCB"
944
+ ] }),
945
+ /* @__PURE__ */ jsxs11(TabsTrigger, { value: "schematic", className: "rf-whitespace-nowrap", children: [
946
+ circuitJson && /* @__PURE__ */ jsx15(
947
+ "span",
948
+ {
949
+ className: cn(
950
+ "rf-inline-flex rf-items-center rf-justify-center rf-w-2 rf-h-2 rf-mr-1 rf-text-xs rf-font-bold rf-text-white rf-rounded-full",
951
+ !hasCodeChangedSinceLastRun ? "rf-bg-blue-500" : "rf-bg-gray-500"
952
+ )
953
+ }
954
+ ),
955
+ "Schematic"
956
+ ] }),
957
+ /* @__PURE__ */ jsxs11(TabsTrigger, { value: "cad", children: [
958
+ circuitJson && /* @__PURE__ */ jsx15(
959
+ "span",
960
+ {
961
+ className: cn(
962
+ "inline-flex items-center justify-center w-2 h-2 mr-1 text-xs font-bold text-white rounded-full",
963
+ !hasCodeChangedSinceLastRun ? "rf-bg-blue-500" : "rf-bg-gray-500"
964
+ )
965
+ }
966
+ ),
967
+ "3D"
968
+ ] }),
969
+ !["pcb", "cad", "schematic"].includes(activeTab) && /* @__PURE__ */ jsx15(TabsTrigger, { value: activeTab, children: capitalizeFirstLetters(activeTab) }),
970
+ /* @__PURE__ */ jsxs11(DropdownMenu, { children: [
971
+ /* @__PURE__ */ jsx15(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs11("div", { className: "rf-whitespace-nowrap rf-p-2 rf-mr-1 rf-cursor-pointer rf-relative", children: [
972
+ /* @__PURE__ */ jsx15(EllipsisIcon, { className: "rf-w-4 rf-h-4" }),
973
+ errorMessage && /* @__PURE__ */ jsx15("span", { className: "rf-inline-flex rf-absolute rf-top-[6px] rf-right-[4px] rf-items-center rf-justify-center rf-w-1 rf-h-1 rf-ml-2 rf-text-[8px] rf-font-bold rf-text-white rf-bg-red-500 rf-rounded-full" })
974
+ ] }) }),
975
+ /* @__PURE__ */ jsx15(DropdownMenuContent, { className: "rf-*:text-xs", children: dropdownMenuItems.map((item) => /* @__PURE__ */ jsxs11(
976
+ DropdownMenuItem,
977
+ {
978
+ onSelect: () => setActiveTab(item),
979
+ children: [
980
+ /* @__PURE__ */ jsx15(
981
+ CheckIcon,
982
+ {
983
+ className: cn(
984
+ "rf-w-3 rf-h-3 rf-mr-2",
985
+ activeTab !== item && "rf-invisible"
986
+ )
987
+ }
988
+ ),
989
+ /* @__PURE__ */ jsx15("div", { className: "rf-pr-2", children: capitalizeFirstLetters(item) }),
990
+ item === "errors" && errorMessage && /* @__PURE__ */ jsx15("span", { className: "rf-inline-flex rf-items-center rf-justify-center rf-w-3 rf-h-3 rf-ml-2 rf-text-[8px] rf-font-bold rf-text-white rf-bg-red-500 rf-rounded-full", children: "1" })
991
+ ]
992
+ },
993
+ item
994
+ )) })
995
+ ] })
996
+ ] }),
997
+ onToggleFullScreen && /* @__PURE__ */ jsx15(Button, { onClick: onToggleFullScreen, variant: "ghost", children: isFullScreen ? /* @__PURE__ */ jsx15(MinimizeIcon, { size: 16 }) : /* @__PURE__ */ jsx15(FullscreenIcon, { size: 16 }) })
998
+ ]
999
+ }
1000
+ ),
1001
+ showCodeTab && /* @__PURE__ */ jsx15(
1002
+ TabsContent,
1003
+ {
1004
+ value: "code",
1005
+ className: "rf-flex-grow rf-overflow-hidden",
1006
+ children: /* @__PURE__ */ jsx15("div", { className: "rf-h-full", children: codeTabContent })
1007
+ }
1008
+ ),
1009
+ /* @__PURE__ */ jsx15(TabsContent, { value: "pcb", children: /* @__PURE__ */ jsx15(
1010
+ "div",
1011
+ {
1012
+ className: cn(
1013
+ "rf-overflow-hidden",
1014
+ isFullScreen ? "rf-h-[calc(100vh-96px)]" : "rf-h-[620px]"
1015
+ ),
1016
+ children: /* @__PURE__ */ jsx15(ErrorBoundary, { fallback: /* @__PURE__ */ jsx15("div", { children: "Error loading PCB viewer" }), children: circuitJson ? /* @__PURE__ */ jsx15(
1017
+ PcbViewerWithContainerHeight,
1018
+ {
1019
+ soup: circuitJson,
1020
+ containerClassName: cn(
1021
+ "rf-h-full rf-w-full",
1022
+ isFullScreen ? "rf-min-h-[calc(100vh-240px)]" : "rf-min-h-[620px]"
1023
+ )
1024
+ },
1025
+ circuitJsonKey
1026
+ ) : /* @__PURE__ */ jsx15(PreviewEmptyState_default, { onRunClicked }) })
1027
+ }
1028
+ ) }),
1029
+ /* @__PURE__ */ jsx15(TabsContent, { value: "assembly", children: /* @__PURE__ */ jsx15(
1030
+ "div",
1031
+ {
1032
+ className: cn(
1033
+ "rf-overflow-auto",
1034
+ isFullScreen ? "rf-h-[calc(100vh-96px)]" : "rf-h-[620px]"
1035
+ ),
1036
+ children: /* @__PURE__ */ jsx15(ErrorBoundary, { fallback: /* @__PURE__ */ jsx15("div", { children: "Error loading Assembly" }), children: circuitJson ? /* @__PURE__ */ jsx15(
1037
+ AssemblyViewer,
1038
+ {
1039
+ circuitJson,
1040
+ containerStyle: {
1041
+ height: "100%"
1042
+ },
1043
+ editingEnabled: true,
1044
+ debugGrid: true
1045
+ }
1046
+ ) : /* @__PURE__ */ jsx15(PreviewEmptyState_default, { onRunClicked }) })
1047
+ }
1048
+ ) }),
1049
+ /* @__PURE__ */ jsx15(TabsContent, { value: "schematic", children: /* @__PURE__ */ jsx15(
1050
+ "div",
1051
+ {
1052
+ className: cn(
1053
+ "rf-overflow-auto",
1054
+ isFullScreen ? "rf-h-[calc(100vh-96px)]" : "rf-h-[620px]"
1055
+ ),
1056
+ children: /* @__PURE__ */ jsx15(ErrorBoundary, { fallback: /* @__PURE__ */ jsx15("div", { children: "Error loading Schematic" }), children: circuitJson ? /* @__PURE__ */ jsx15(
1057
+ SchematicViewer,
1058
+ {
1059
+ circuitJson,
1060
+ containerStyle: {
1061
+ height: "100%"
1062
+ },
1063
+ editingEnabled: true,
1064
+ onEditEvent: (ee) => onEditEvent?.(ee),
1065
+ editEvents,
1066
+ debugGrid: true
1067
+ }
1068
+ ) : /* @__PURE__ */ jsx15(PreviewEmptyState_default, { onRunClicked }) })
1069
+ }
1070
+ ) }),
1071
+ /* @__PURE__ */ jsx15(TabsContent, { value: "cad", children: /* @__PURE__ */ jsx15(
1072
+ "div",
1073
+ {
1074
+ className: cn(
1075
+ "rf-overflow-auto",
1076
+ isFullScreen ? "rf-h-[calc(100vh-96px)]" : "rf-h-[620px]"
1077
+ ),
1078
+ children: /* @__PURE__ */ jsx15(ErrorBoundary, { FallbackComponent: ErrorFallback, children: circuitJson ? /* @__PURE__ */ jsx15(CadViewer, { soup: circuitJson }) : /* @__PURE__ */ jsx15(PreviewEmptyState_default, { onRunClicked }) })
1079
+ }
1080
+ ) }),
1081
+ /* @__PURE__ */ jsx15(TabsContent, { value: "bom", children: /* @__PURE__ */ jsx15(
1082
+ "div",
1083
+ {
1084
+ className: cn(
1085
+ "rf-overflow-auto",
1086
+ isFullScreen ? "rf-h-[calc(100vh-96px)]" : "rf-h-[620px]"
1087
+ ),
1088
+ children: /* @__PURE__ */ jsx15(ErrorBoundary, { fallback: /* @__PURE__ */ jsx15("div", { children: "Error loading BOM" }), children: circuitJson ? /* @__PURE__ */ jsx15(BomTable, { circuitJson }) : /* @__PURE__ */ jsx15(PreviewEmptyState_default, { onRunClicked }) })
1089
+ }
1090
+ ) }),
1091
+ /* @__PURE__ */ jsx15(TabsContent, { value: "circuit_json", children: /* @__PURE__ */ jsx15(
1092
+ "div",
1093
+ {
1094
+ className: cn(
1095
+ "rf-overflow-auto",
1096
+ isFullScreen ? "rf-h-[calc(100vh-96px)]" : "rf-h-[620px]"
1097
+ ),
1098
+ children: /* @__PURE__ */ jsx15(ErrorBoundary, { fallback: /* @__PURE__ */ jsx15("div", { children: "Error loading JSON viewer" }), children: circuitJson ? /* @__PURE__ */ jsx15(CircuitJsonTableViewer, { elements: circuitJson }) : /* @__PURE__ */ jsx15(PreviewEmptyState_default, { onRunClicked }) })
1099
+ }
1100
+ ) }),
1101
+ /* @__PURE__ */ jsx15(TabsContent, { value: "errors", children: circuitJson || errorMessage ? /* @__PURE__ */ jsx15(ErrorTabContent, { code, errorMessage }) : /* @__PURE__ */ jsx15(PreviewEmptyState_default, { onRunClicked }) }),
1102
+ showRenderLogTab && /* @__PURE__ */ jsx15(TabsContent, { value: "render_log", children: /* @__PURE__ */ jsx15(RenderLogViewer, { renderLog }) })
1103
+ ]
1104
+ }
1105
+ ) }) });
1106
+ };
1107
+
1108
+ export {
1109
+ CircuitJsonPreview
1110
+ };