@tscircuit/runframe 0.0.285 → 0.0.287

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/runner.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  TabsTrigger,
15
15
  buttonVariants,
16
16
  cn
17
- } from "./chunk-FTA6I3F6.js";
17
+ } from "./chunk-QBYYLV3Y.js";
18
18
 
19
19
  // lib/components/RunFrame/RunFrame.tsx
20
20
  import { createCircuitWebWorker } from "@tscircuit/eval/worker";
@@ -481,7 +481,7 @@ var RunFrame = (props) => {
481
481
  props.onCircuitJsonChange?.(circuitJson2);
482
482
  setCircuitJson(circuitJson2);
483
483
  props.onRenderFinished?.({ circuitJson: circuitJson2 });
484
- setAutoroutingGraphics(null);
484
+ setAutoroutingGraphics({});
485
485
  if (activeTab === "render_log") {
486
486
  renderLog2.phaseTimings = getPhaseTimingsFromRenderEvents(
487
487
  renderLog2.renderEvents ?? []
@@ -763,7 +763,7 @@ function useLocalStorageState(key, initialValue) {
763
763
  }
764
764
 
765
765
  // lib/components/RunFrameForCli/LeftHeader.tsx
766
- import { useEffect as useEffect7, useMemo as useMemo3, useState as useState6 } from "react";
766
+ import { useEffect as useEffect8, useMemo as useMemo3, useState as useState8 } from "react";
767
767
 
768
768
  // lib/components/RunFrameForCli/SelectSnippetDialog.tsx
769
769
  import { useState as useState4 } from "react";
@@ -868,7 +868,7 @@ var useEventHandler = (callback) => {
868
868
  };
869
869
 
870
870
  // lib/components/RunFrameForCli/LeftHeader.tsx
871
- import { CheckCircle } from "lucide-react";
871
+ import "lucide-react";
872
872
 
873
873
  // lib/components/ui/alert-dialog.tsx
874
874
  import * as React from "react";
@@ -1083,7 +1083,7 @@ var mapTscircuitSnippetToSearchResult = (tscircuitSnippet) => {
1083
1083
  id: `tscircuit-${tscircuitSnippet.snippet_id}`,
1084
1084
  name: tscircuitSnippet.unscoped_name,
1085
1085
  description: tscircuitSnippet.description || `Component by ${tscircuitSnippet.owner_name}`,
1086
- source: "tscircuit",
1086
+ source: "tscircuit.com",
1087
1087
  partNumber: tscircuitSnippet.name,
1088
1088
  previewUrl: tscircuitSnippet.preview_url,
1089
1089
  // Additional tscircuit-specific properties
@@ -1108,7 +1108,7 @@ var ImportComponentDialog = ({
1108
1108
  const [isLoading, setIsLoading] = useState5(false);
1109
1109
  const [selectedComponent, setSelectedComponent] = useState5(null);
1110
1110
  const [activeTab, setActiveTab] = useState5(
1111
- "tscircuit"
1111
+ "tscircuit.com"
1112
1112
  );
1113
1113
  const [detailsOpen, setDetailsOpen] = useState5(false);
1114
1114
  const [detailsComponent, setDetailsComponent] = useState5(null);
@@ -1169,7 +1169,7 @@ var ImportComponentDialog = ({
1169
1169
  onValueChange: (value) => setActiveTab(value),
1170
1170
  children: [
1171
1171
  /* @__PURE__ */ jsxs5(TabsList, { className: "rf-grid rf-w-full rf-grid-cols-2", children: [
1172
- /* @__PURE__ */ jsx7(TabsTrigger, { value: "tscircuit", children: "tscircuit.com" }),
1172
+ /* @__PURE__ */ jsx7(TabsTrigger, { value: "tscircuit.com", children: "tscircuit.com" }),
1173
1173
  /* @__PURE__ */ jsx7(TabsTrigger, { value: "jlcpcb", children: "JLCPCB Parts" })
1174
1174
  ] }),
1175
1175
  /* @__PURE__ */ jsxs5("div", { className: "rf-flex rf-items-center rf-gap-2 rf-mt-4", children: [
@@ -1178,7 +1178,7 @@ var ImportComponentDialog = ({
1178
1178
  /* @__PURE__ */ jsx7(
1179
1179
  Input,
1180
1180
  {
1181
- placeholder: activeTab === "tscircuit" ? "Search components..." : "Search JLCPCB parts (e.g. C14663)...",
1181
+ placeholder: activeTab === "tscircuit.com" ? "Search components..." : "Search JLCPCB parts (e.g. C14663)...",
1182
1182
  className: "rf-pl-8",
1183
1183
  value: searchQuery,
1184
1184
  onChange: (e) => setSearchQuery(e.target.value),
@@ -1201,7 +1201,7 @@ var ImportComponentDialog = ({
1201
1201
  result.description
1202
1202
  ] })
1203
1203
  ] }),
1204
- /* @__PURE__ */ jsx7("div", { className: "rf-flex rf-gap-2", children: result.source === "tscircuit" && /* @__PURE__ */ jsx7(
1204
+ /* @__PURE__ */ jsx7("div", { className: "rf-flex rf-gap-2", children: result.source === "tscircuit.com" && /* @__PURE__ */ jsx7(
1205
1205
  Button,
1206
1206
  {
1207
1207
  variant: "outline",
@@ -1265,7 +1265,7 @@ var ImportComponentDialog = ({
1265
1265
  ] })
1266
1266
  ] })
1267
1267
  ] }),
1268
- detailsComponent?.source === "tscircuit" && /* @__PURE__ */ jsxs5("div", { className: "rf-my-4 rf-border rf-rounded-md rf-p-4 !rf-max-w-[60vw] !rf-overflow-x-hidden", children: [
1268
+ detailsComponent?.source === "tscircuit.com" && /* @__PURE__ */ jsxs5("div", { className: "rf-my-4 rf-border rf-rounded-md rf-p-4 !rf-max-w-[60vw] !rf-overflow-x-hidden", children: [
1269
1269
  /* @__PURE__ */ jsx7("div", { className: "rf-text-sm rf-font-medium rf-mb-2", children: "tscircuit Details" }),
1270
1270
  /* @__PURE__ */ jsx7("div", { className: "rf-grid rf-grid-cols-2 rf-gap-2", children: detailsComponent.owner && /* @__PURE__ */ jsxs5("div", { className: "rf-flex rf-flex-col", children: [
1271
1271
  /* @__PURE__ */ jsx7("span", { className: "rf-text-xs rf-text-zinc-500", children: "Owner" }),
@@ -1281,7 +1281,7 @@ var ImportComponentDialog = ({
1281
1281
  ] }),
1282
1282
  /* @__PURE__ */ jsxs5("div", { className: "rf-my-4 rf-border rf-rounded-md rf-p-4", children: [
1283
1283
  /* @__PURE__ */ jsx7("div", { className: "rf-text-sm rf-font-medium rf-mb-2", children: "Preview" }),
1284
- detailsComponent?.source === "tscircuit" && detailsComponent.code ? /* @__PURE__ */ jsx7("div", { className: "rf-flex rf-justify-center", children: /* @__PURE__ */ jsx7("div", { className: "rf-border rf-rounded rf-p-4 rf-bg-zinc-50", children: /* @__PURE__ */ jsx7(
1284
+ detailsComponent?.source === "tscircuit.com" && detailsComponent.code ? /* @__PURE__ */ jsx7("div", { className: "rf-flex rf-justify-center", children: /* @__PURE__ */ jsx7("div", { className: "rf-border rf-rounded rf-p-4 rf-bg-zinc-50", children: /* @__PURE__ */ jsx7(
1285
1285
  "img",
1286
1286
  {
1287
1287
  src: createPngUrl(detailsComponent.code, "pcb"),
@@ -1328,38 +1328,237 @@ var ImportComponentDialog = ({
1328
1328
  ] });
1329
1329
  };
1330
1330
 
1331
- // lib/components/RunFrameForCli/LeftHeader.tsx
1332
- import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1331
+ // lib/optional-features/exporting/formats/export-fabrication-files.ts
1332
+ import JSZip from "jszip";
1333
+ import {
1334
+ stringifyGerberCommandLayers,
1335
+ convertSoupToGerberCommands,
1336
+ convertSoupToExcellonDrillCommands,
1337
+ stringifyExcellonDrill
1338
+ } from "circuit-json-to-gerber";
1339
+ import {
1340
+ convertCircuitJsonToBomRows,
1341
+ convertBomRowsToCsv
1342
+ } from "circuit-json-to-bom-csv";
1343
+ import { convertCircuitJsonToPickAndPlaceCsv } from "circuit-json-to-pnp-csv";
1344
+
1345
+ // lib/optional-features/exporting/open-for-download.ts
1346
+ var openForDownload = (content, opts) => {
1347
+ const { fileName, mimeType = "text/plain" } = opts;
1348
+ const blob = content instanceof Blob ? content : new Blob([content], { type: mimeType });
1349
+ const url = URL.createObjectURL(blob);
1350
+ const a = document.createElement("a");
1351
+ a.href = url;
1352
+ a.download = fileName;
1353
+ document.body.appendChild(a);
1354
+ a.click();
1355
+ setTimeout(() => {
1356
+ document.body.removeChild(a);
1357
+ URL.revokeObjectURL(url);
1358
+ }, 0);
1359
+ };
1360
+
1361
+ // lib/optional-features/exporting/formats/export-fabrication-files.ts
1362
+ var exportFabricationFiles = async ({
1363
+ circuitJson,
1364
+ projectName
1365
+ }) => {
1366
+ const zip = new JSZip();
1367
+ const gerberLayerCmds = convertSoupToGerberCommands(circuitJson, {
1368
+ flip_y_axis: false
1369
+ });
1370
+ const gerberFileContents = stringifyGerberCommandLayers(gerberLayerCmds);
1371
+ for (const [fileName, fileContents] of Object.entries(gerberFileContents)) {
1372
+ zip.file(`gerber/${fileName}.gbr`, fileContents);
1373
+ }
1374
+ const drillCmds = convertSoupToExcellonDrillCommands({
1375
+ circuitJson,
1376
+ is_plated: true,
1377
+ flip_y_axis: false
1378
+ });
1379
+ const drillFileContents = stringifyExcellonDrill(drillCmds);
1380
+ zip.file("gerber/drill.drl", drillFileContents);
1381
+ const bomRows = await convertCircuitJsonToBomRows({ circuitJson });
1382
+ const bomCsv = await convertBomRowsToCsv(bomRows);
1383
+ zip.file("bom.csv", bomCsv);
1384
+ const pnpCsv = await convertCircuitJsonToPickAndPlaceCsv(circuitJson);
1385
+ zip.file("pick_and_place.csv", pnpCsv);
1386
+ const zipBlob = await zip.generateAsync({ type: "blob" });
1387
+ openForDownload(zipBlob, {
1388
+ fileName: `${projectName}_fabrication_files.zip`
1389
+ });
1390
+ };
1391
+
1392
+ // lib/optional-features/exporting/export-and-download.ts
1333
1393
  var availableExports = [
1334
- { extension: "json", name: "JSON" },
1335
- { extension: "svg", name: "SVG" },
1336
- { extension: "dsn", name: "Specctra DSN" },
1337
- { extension: "glb", name: "GLB (Binary GLTF)" },
1338
- { extension: "csv", name: "CSV (Comma-Separated Values)" },
1339
- { extension: "text", name: "Plain Text" },
1340
- { extension: "kicad_mod", name: "KiCad Module" },
1341
- { extension: "kicad_project", name: "KiCad Project" },
1342
- { extension: "gbr", name: "Gerbers" }
1394
+ { extension: "json", name: "Circuit JSON" },
1395
+ { extension: "zip", name: "Fabrication Files" }
1396
+ // { extension: "svg", name: "SVG" },
1397
+ // { extension: "dsn", name: "Specctra DSN" },
1398
+ // { extension: "glb", name: "GLB (Binary GLTF)" },
1399
+ // { extension: "kicad_mod", name: "KiCad Module" },
1400
+ // { extension: "kicad_project", name: "KiCad Project" },
1401
+ // { extension: "gbr", name: "Gerbers" },
1343
1402
  ];
1403
+ var exportAndDownload = async ({
1404
+ exportName,
1405
+ circuitJson,
1406
+ projectName
1407
+ }) => {
1408
+ if (exportName === "Fabrication Files") {
1409
+ exportFabricationFiles({ circuitJson, projectName });
1410
+ return;
1411
+ }
1412
+ if (exportName === "Circuit JSON") {
1413
+ openForDownload(JSON.stringify(circuitJson, null, 2), {
1414
+ fileName: `${projectName}.circuit.json`,
1415
+ mimeType: "application/json"
1416
+ });
1417
+ return;
1418
+ }
1419
+ throw new Error(`Unsupported export type: "${exportName}"`);
1420
+ };
1421
+
1422
+ // lib/utils/toast.ts
1423
+ import { toast } from "react-hot-toast";
1424
+
1425
+ // lib/components/RunFrameForCli/LeftHeader.tsx
1426
+ import { Toaster } from "react-hot-toast";
1427
+
1428
+ // lib/optional-features/importing/import-component-from-jlcpcb.ts
1429
+ import { fetchEasyEDAComponent, convertRawEasyToTsx } from "easyeda/browser";
1430
+ import ky from "ky";
1431
+ var importComponentFromJlcpcb = async (jlcpcbPartNumber) => {
1432
+ const component = await fetchEasyEDAComponent(jlcpcbPartNumber, {
1433
+ fetch: (url, options) => {
1434
+ return fetch(`${API_BASE}/proxy`, {
1435
+ ...options,
1436
+ headers: {
1437
+ ...options?.headers,
1438
+ "X-Target-Url": url.toString(),
1439
+ "X-Sender-Origin": options?.headers?.origin ?? "",
1440
+ "X-Sender-Host": options?.headers?.host ?? "https://easyeda.com",
1441
+ "X-Sender-Referer": options?.headers?.referer ?? "",
1442
+ "X-Sender-User-Agent": options?.headers?.userAgent ?? "",
1443
+ "X-Sender-Cookie": options?.headers?.cookie ?? ""
1444
+ }
1445
+ });
1446
+ }
1447
+ });
1448
+ const tsx = await convertRawEasyToTsx(component);
1449
+ const fileName = tsx.match(/export const (\w+) = .*/)?.[1];
1450
+ if (!fileName) {
1451
+ console.error("COULD NOT DETERMINE FILE NAME OF CONVERTED COMPONENT:", tsx);
1452
+ throw new Error(`Could not determine file name of converted component`);
1453
+ }
1454
+ const filePath = `imports/${fileName}.tsx`;
1455
+ await ky.post(`${API_BASE}/files/upsert`, {
1456
+ json: {
1457
+ file_path: filePath,
1458
+ text_content: tsx
1459
+ }
1460
+ });
1461
+ return { filePath };
1462
+ };
1463
+
1464
+ // lib/components/OrderDialog/useOrderDialog.tsx
1465
+ import { useState as useState7 } from "react";
1466
+
1467
+ // lib/components/OrderDialog/OrderDialog.tsx
1468
+ import { useState as useState6 } from "react";
1469
+ import ky2 from "ky";
1470
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1471
+ var OrderDialog = ({
1472
+ isOpen,
1473
+ onClose
1474
+ }) => {
1475
+ const [order, setOrder] = useState6(null);
1476
+ const [stage, setStage] = useState6(
1477
+ "setup"
1478
+ );
1479
+ const circuitJson = useRunFrameStore((state) => state.circuitJson);
1480
+ return /* @__PURE__ */ jsx8(AlertDialog, { open: isOpen, onOpenChange: onClose, children: /* @__PURE__ */ jsxs6(AlertDialogContent, { children: [
1481
+ /* @__PURE__ */ jsxs6(AlertDialogHeader, { children: [
1482
+ /* @__PURE__ */ jsx8(AlertDialogTitle, { children: "Order PCB" }),
1483
+ /* @__PURE__ */ jsx8(AlertDialogDescription, { children: stage })
1484
+ ] }),
1485
+ stage === "order-progress" && /* @__PURE__ */ jsx8("div", { className: "rf-space-y-4 rf-py-4 ", children: /* @__PURE__ */ jsx8(
1486
+ "pre",
1487
+ {
1488
+ style: {
1489
+ maxHeight: "500px",
1490
+ overflowY: "scroll",
1491
+ fontSize: "8px"
1492
+ },
1493
+ children: JSON.stringify(order, null, 2)
1494
+ }
1495
+ ) }),
1496
+ /* @__PURE__ */ jsxs6(AlertDialogFooter, { children: [
1497
+ /* @__PURE__ */ jsx8(AlertDialogCancel, { onClick: onClose, children: "Cancel" }),
1498
+ stage === "setup" && /* @__PURE__ */ jsx8(
1499
+ Button,
1500
+ {
1501
+ onClick: async () => {
1502
+ if (!circuitJson) {
1503
+ return;
1504
+ }
1505
+ const { order: order2 } = await ky2.post("registry/orders/create", {
1506
+ json: {
1507
+ circuit_json: circuitJson
1508
+ },
1509
+ headers: {
1510
+ Authorization: `Bearer account-1234`
1511
+ }
1512
+ }).json();
1513
+ setOrder(order2);
1514
+ setStage("order-progress");
1515
+ },
1516
+ children: "Submit Order"
1517
+ }
1518
+ ),
1519
+ stage === "order-progress" && /* @__PURE__ */ jsx8(Button, { disabled: true, children: "Continue to Shipping" })
1520
+ ] })
1521
+ ] }) });
1522
+ };
1523
+
1524
+ // lib/components/OrderDialog/useOrderDialog.tsx
1525
+ import { jsx as jsx9 } from "react/jsx-runtime";
1526
+ var useOrderDialog = () => {
1527
+ const [isOpen, setIsOpen] = useState7(false);
1528
+ const [isRegistryConnected, setIsRegistryConnected] = useState7(false);
1529
+ return {
1530
+ isOpen,
1531
+ isRegistryConnected,
1532
+ open: () => setIsOpen(true),
1533
+ close: () => setIsOpen(false),
1534
+ OrderDialog: () => {
1535
+ return /* @__PURE__ */ jsx9(OrderDialog, { isOpen, onClose: () => setIsOpen(false) });
1536
+ }
1537
+ };
1538
+ };
1539
+
1540
+ // lib/components/RunFrameForCli/LeftHeader.tsx
1541
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1344
1542
  var RunframeCliLeftHeader = (props) => {
1345
1543
  const lastRunEvalVersion = useRunnerStore((s) => s.lastRunEvalVersion);
1346
- const [snippetName, setSnippetName] = useState6(null);
1347
- const [hasUnsavedChanges, setHasUnsavedChanges] = useState6(false);
1348
- const [hasNeverBeenSaved, setHasNeverBeenSaved] = useState6(true);
1349
- const [isSaving, setIsSaving] = useState6(false);
1350
- const [requestToSaveSentAt, setRequestToSaveSentAt] = useState6(
1544
+ const [snippetName, setSnippetName] = useState8(null);
1545
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState8(false);
1546
+ const [hasNeverBeenSaved, setHasNeverBeenSaved] = useState8(true);
1547
+ const [isSaving, setIsSaving] = useState8(false);
1548
+ const [requestToSaveSentAt, setRequestToSaveSentAt] = useState8(
1351
1549
  null
1352
1550
  );
1353
- const [availableSnippets, setAvailableSnippets] = useState6(
1551
+ const [availableSnippets, setAvailableSnippets] = useState8(
1354
1552
  null
1355
1553
  );
1356
- const [isSelectSnippetDialogOpen, setIsSelectSnippetDialogOpen] = useState6(false);
1357
- const [notificationMessage, setNotificationMessage] = useState6(
1554
+ const [isSelectSnippetDialogOpen, setIsSelectSnippetDialogOpen] = useState8(false);
1555
+ const [notificationMessage, setNotificationMessage] = useState8(
1358
1556
  null
1359
1557
  );
1360
- const [errorMessage, setErrorMessage] = useState6(null);
1361
- const [isError, setIsError] = useState6(false);
1362
- const [isExporting, setisExporting] = useState6(false);
1558
+ const [errorMessage, setErrorMessage] = useState8(null);
1559
+ const [isError, setIsError] = useState8(false);
1560
+ const [isExporting, setisExporting] = useState8(false);
1561
+ const orderDialog = useOrderDialog();
1363
1562
  const pushEvent = useRunFrameStore((state) => state.pushEvent);
1364
1563
  const recentEvents = useRunFrameStore((state) => state.recentEvents);
1365
1564
  const firstRenderTime = useMemo3(() => Date.now(), []);
@@ -1387,7 +1586,7 @@ var RunframeCliLeftHeader = (props) => {
1387
1586
  setisExporting(false);
1388
1587
  }
1389
1588
  });
1390
- useEffect7(() => {
1589
+ useEffect8(() => {
1391
1590
  if (!isSaving || requestToSaveSentAt === null) return;
1392
1591
  const eventsSinceRequestToSave = recentEvents.filter(
1393
1592
  (event) => new Date(event.created_at).valueOf() > requestToSaveSentAt
@@ -1428,12 +1627,13 @@ var RunframeCliLeftHeader = (props) => {
1428
1627
  snippet_name: snippetName
1429
1628
  });
1430
1629
  };
1431
- const [isImportDialogOpen, setIsImportDialogOpen] = useState6(false);
1432
- return /* @__PURE__ */ jsxs6(Fragment2, { children: [
1433
- /* @__PURE__ */ jsxs6(DropdownMenu, { children: [
1434
- /* @__PURE__ */ jsx8(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx8("div", { className: "rf-whitespace-nowrap rf-text-xs font-medium rf-p-2 rf-mx-1 rf-cursor-pointer rf-relative", children: "File" }) }),
1435
- /* @__PURE__ */ jsxs6(DropdownMenuContent, { children: [
1436
- /* @__PURE__ */ jsx8(
1630
+ const circuitJson = useRunFrameStore((state) => state.circuitJson);
1631
+ const [isImportDialogOpen, setIsImportDialogOpen] = useState8(false);
1632
+ return /* @__PURE__ */ jsxs7(Fragment2, { children: [
1633
+ /* @__PURE__ */ jsxs7(DropdownMenu, { children: [
1634
+ /* @__PURE__ */ jsx10(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx10("div", { className: "rf-whitespace-nowrap rf-text-xs font-medium rf-p-2 rf-mx-1 rf-cursor-pointer rf-relative", children: "File" }) }),
1635
+ /* @__PURE__ */ jsxs7(DropdownMenuContent, { children: [
1636
+ /* @__PURE__ */ jsx10(
1437
1637
  DropdownMenuItem,
1438
1638
  {
1439
1639
  className: "rf-text-xs",
@@ -1442,7 +1642,17 @@ var RunframeCliLeftHeader = (props) => {
1442
1642
  children: isSaving ? "Saving..." : "Push"
1443
1643
  }
1444
1644
  ),
1445
- /* @__PURE__ */ jsx8(
1645
+ parseInt(window.location.port) > 5e3 && /* @__PURE__ */ jsx10(
1646
+ DropdownMenuItem,
1647
+ {
1648
+ className: "rf-text-xs",
1649
+ onSelect: () => {
1650
+ orderDialog.open();
1651
+ },
1652
+ children: "Order"
1653
+ }
1654
+ ),
1655
+ /* @__PURE__ */ jsx10(
1446
1656
  DropdownMenuItem,
1447
1657
  {
1448
1658
  className: "rf-text-xs",
@@ -1451,8 +1661,8 @@ var RunframeCliLeftHeader = (props) => {
1451
1661
  children: "Import"
1452
1662
  }
1453
1663
  ),
1454
- /* @__PURE__ */ jsxs6(DropdownMenuSub, { children: [
1455
- /* @__PURE__ */ jsx8(
1664
+ /* @__PURE__ */ jsxs7(DropdownMenuSub, { children: [
1665
+ /* @__PURE__ */ jsx10(
1456
1666
  DropdownMenuSubTrigger,
1457
1667
  {
1458
1668
  className: "rf-text-xs",
@@ -1460,32 +1670,31 @@ var RunframeCliLeftHeader = (props) => {
1460
1670
  children: isExporting ? "Exporting..." : "Export"
1461
1671
  }
1462
1672
  ),
1463
- /* @__PURE__ */ jsx8(DropdownMenuPortal, { children: /* @__PURE__ */ jsx8(DropdownMenuSubContent, { children: availableExports.map((exp, i) => /* @__PURE__ */ jsx8(
1673
+ /* @__PURE__ */ jsx10(DropdownMenuPortal, { children: /* @__PURE__ */ jsx10(DropdownMenuSubContent, { children: availableExports.map((exp, i) => /* @__PURE__ */ jsx10(
1464
1674
  DropdownMenuItem,
1465
1675
  {
1466
1676
  onSelect: () => {
1467
- if (!isExporting) {
1468
- pushEvent({
1469
- event_type: "REQUEST_EXPORT",
1470
- exportType: exp.extension
1471
- });
1472
- setNotificationMessage(
1473
- `Export requested for ${exp.name}`
1474
- );
1475
- setIsError(false);
1677
+ if (!circuitJson) {
1678
+ toast.error("No Circuit JSON to export");
1679
+ return;
1476
1680
  }
1681
+ exportAndDownload({
1682
+ exportName: exp.name,
1683
+ circuitJson,
1684
+ projectName: snippetName ?? "Untitled"
1685
+ });
1477
1686
  },
1478
1687
  disabled: isExporting,
1479
- children: /* @__PURE__ */ jsx8("span", { className: "rf-text-xs", children: exp.name })
1688
+ children: /* @__PURE__ */ jsx10("span", { className: "rf-text-xs", children: exp.name })
1480
1689
  },
1481
1690
  i
1482
1691
  )) }) })
1483
1692
  ] }),
1484
- /* @__PURE__ */ jsxs6(DropdownMenuSub, { children: [
1485
- /* @__PURE__ */ jsx8(DropdownMenuSubTrigger, { className: "rf-text-xs", children: "Advanced" }),
1486
- /* @__PURE__ */ jsx8(DropdownMenuPortal, { children: /* @__PURE__ */ jsxs6(DropdownMenuSubContent, { children: [
1487
- /* @__PURE__ */ jsx8(DropdownMenuItem, { className: "rf-flex rf-items-center rf-gap-2", children: /* @__PURE__ */ jsxs6("div", { className: "rf-flex rf-items-center rf-gap-2", children: [
1488
- /* @__PURE__ */ jsx8(
1693
+ /* @__PURE__ */ jsxs7(DropdownMenuSub, { children: [
1694
+ /* @__PURE__ */ jsx10(DropdownMenuSubTrigger, { className: "rf-text-xs", children: "Advanced" }),
1695
+ /* @__PURE__ */ jsx10(DropdownMenuPortal, { children: /* @__PURE__ */ jsxs7(DropdownMenuSubContent, { children: [
1696
+ /* @__PURE__ */ jsx10(DropdownMenuItem, { className: "rf-flex rf-items-center rf-gap-2", children: /* @__PURE__ */ jsxs7("div", { className: "rf-flex rf-items-center rf-gap-2", children: [
1697
+ /* @__PURE__ */ jsx10(
1489
1698
  Checkbox,
1490
1699
  {
1491
1700
  id: "load-latest-eval",
@@ -1495,7 +1704,7 @@ var RunframeCliLeftHeader = (props) => {
1495
1704
  }
1496
1705
  }
1497
1706
  ),
1498
- /* @__PURE__ */ jsx8(
1707
+ /* @__PURE__ */ jsx10(
1499
1708
  "label",
1500
1709
  {
1501
1710
  htmlFor: "load-latest-eval",
@@ -1504,53 +1713,31 @@ var RunframeCliLeftHeader = (props) => {
1504
1713
  }
1505
1714
  )
1506
1715
  ] }) }),
1507
- lastRunEvalVersion && /* @__PURE__ */ jsx8(DropdownMenuItem, { className: "rf-flex rf-items-center rf-gap-2", children: /* @__PURE__ */ jsx8("div", { className: "rf-flex rf-items-center rf-gap-2", children: /* @__PURE__ */ jsxs6("span", { className: "rf-text-xs", children: [
1716
+ lastRunEvalVersion && /* @__PURE__ */ jsx10(DropdownMenuItem, { className: "rf-flex rf-items-center rf-gap-2", children: /* @__PURE__ */ jsx10("div", { className: "rf-flex rf-items-center rf-gap-2", children: /* @__PURE__ */ jsxs7("span", { className: "rf-text-xs", children: [
1508
1717
  "@tscircuit/eval@",
1509
1718
  lastRunEvalVersion
1510
1719
  ] }) }) }),
1511
- /* @__PURE__ */ jsx8(
1720
+ /* @__PURE__ */ jsx10(
1512
1721
  DropdownMenuItem,
1513
1722
  {
1514
1723
  className: "rf-flex rf-items-center rf-gap-2",
1515
1724
  onClick: () => {
1516
1725
  window.open("/api/admin", "_blank");
1517
1726
  },
1518
- children: /* @__PURE__ */ jsx8("div", { className: "rf-flex rf-items-center rf-gap-2", children: /* @__PURE__ */ jsx8("span", { className: "rf-text-xs", children: "CLI Admin Panel" }) })
1727
+ children: /* @__PURE__ */ jsx10("div", { className: "rf-flex rf-items-center rf-gap-2", children: /* @__PURE__ */ jsx10("span", { className: "rf-text-xs", children: "CLI Admin Panel" }) })
1519
1728
  }
1520
1729
  )
1521
1730
  ] }) })
1522
1731
  ] })
1523
1732
  ] }),
1524
- /* @__PURE__ */ jsx8("div", { className: "!rf-h-full rf-w-fit rf-grid rf-place-items-center rf-my-auto", children: /* @__PURE__ */ jsxs6("div", { className: "rf-flex rf-gap-4", children: [
1525
- hasUnsavedChanges || hasNeverBeenSaved && /* @__PURE__ */ jsx8(
1526
- "button",
1527
- {
1528
- type: "button",
1529
- disabled: isSaving,
1530
- onClick: triggerSaveSnippet,
1531
- className: "transition ease-in-out hover:scale-105 pointer-cursor rf-text-xs rf-h-fit disabled:rf-bg-blue-600/60 rf-bg-blue-600/70 rf-text-white rf-p-0.5 rf-px-1.5 rf-rounded",
1532
- children: isSaving ? "Syncing..." : "Not Synced"
1533
- }
1534
- ),
1535
- notificationMessage && /* @__PURE__ */ jsx8(
1536
- "div",
1537
- {
1538
- className: `rf-text-xs rf-font-medium rf-mt-1 rf-flex rf-max-w-xl rf-text-blue-500 rf-break-words rf-text-center rf-h-full`,
1539
- children: /* @__PURE__ */ jsxs6("div", { className: "rf-flex rf-items-center", children: [
1540
- /* @__PURE__ */ jsx8(CheckCircle, { className: "rf-w-4 rf-h-4 rf-inline rf-mr-1" }),
1541
- notificationMessage
1542
- ] })
1543
- }
1544
- )
1545
- ] }) }),
1546
- /* @__PURE__ */ jsx8(AlertDialog, { open: isError, onOpenChange: setIsError, children: /* @__PURE__ */ jsxs6(AlertDialogContent, { children: [
1547
- /* @__PURE__ */ jsxs6(AlertDialogHeader, { children: [
1548
- /* @__PURE__ */ jsx8(AlertDialogTitle, { children: "Error Saving Snippet" }),
1549
- /* @__PURE__ */ jsx8(AlertDialogDescription, { children: errorMessage })
1733
+ /* @__PURE__ */ jsx10(AlertDialog, { open: isError, onOpenChange: setIsError, children: /* @__PURE__ */ jsxs7(AlertDialogContent, { children: [
1734
+ /* @__PURE__ */ jsxs7(AlertDialogHeader, { children: [
1735
+ /* @__PURE__ */ jsx10(AlertDialogTitle, { children: "Error Saving Snippet" }),
1736
+ /* @__PURE__ */ jsx10(AlertDialogDescription, { children: errorMessage })
1550
1737
  ] }),
1551
- /* @__PURE__ */ jsx8(AlertDialogFooter, { children: /* @__PURE__ */ jsx8(AlertDialogCancel, { onClick: () => setIsError(false), children: "Dismiss" }) })
1738
+ /* @__PURE__ */ jsx10(AlertDialogFooter, { children: /* @__PURE__ */ jsx10(AlertDialogCancel, { onClick: () => setIsError(false), children: "Dismiss" }) })
1552
1739
  ] }) }),
1553
- /* @__PURE__ */ jsx8(
1740
+ /* @__PURE__ */ jsx10(
1554
1741
  SelectSnippetDialog,
1555
1742
  {
1556
1743
  snippetNames: availableSnippets ?? [],
@@ -1569,39 +1756,55 @@ var RunframeCliLeftHeader = (props) => {
1569
1756
  }
1570
1757
  )
1571
1758
  ] }),
1572
- /* @__PURE__ */ jsx8(
1759
+ /* @__PURE__ */ jsx10(
1573
1760
  ImportComponentDialog,
1574
1761
  {
1575
1762
  isOpen: isImportDialogOpen,
1576
1763
  onClose: () => setIsImportDialogOpen(false),
1577
1764
  onImport: async (component) => {
1578
- console.log("Component imported:", component);
1579
- setIsImportDialogOpen(false);
1580
- await pushEvent({
1581
- event_type: "IMPORT_COMPONENT",
1582
- component
1583
- });
1765
+ toast.promise(
1766
+ async () => {
1767
+ if (component.source === "tscircuit.com") {
1768
+ await pushEvent({
1769
+ event_type: "INSTALL_PACKAGE",
1770
+ full_package_name: `@tsci/${component.owner}.${component.name}`
1771
+ });
1772
+ throw new Error("Not implemented");
1773
+ } else if (component.source === "jlcpcb") {
1774
+ const { filePath } = await importComponentFromJlcpcb(
1775
+ component.partNumber
1776
+ );
1777
+ return { filePath };
1778
+ }
1779
+ },
1780
+ {
1781
+ loading: `Importing component: "${component.name}"`,
1782
+ success: (data) => data?.filePath ? `Imported to "${data.filePath}"` : "Import Successful"
1783
+ }
1784
+ );
1584
1785
  }
1585
1786
  }
1586
- )
1787
+ ),
1788
+ /* @__PURE__ */ jsx10(Toaster, { position: "top-center", reverseOrder: false }),
1789
+ /* @__PURE__ */ jsx10(orderDialog.OrderDialog, {})
1587
1790
  ] });
1588
1791
  };
1589
1792
 
1590
1793
  // lib/components/RunFrameForCli/RunFrameForCli.tsx
1591
- import { jsx as jsx9 } from "react/jsx-runtime";
1794
+ import { jsx as jsx11 } from "react/jsx-runtime";
1592
1795
  var RunFrameForCli = (props) => {
1593
1796
  const [shouldLoadLatestEval, setLoadLatestEval] = useLocalStorageState(
1594
1797
  "load-latest-eval",
1595
1798
  true
1596
1799
  );
1597
- return /* @__PURE__ */ jsx9(
1800
+ return /* @__PURE__ */ jsx11(
1598
1801
  RunFrameWithApi,
1599
1802
  {
1600
1803
  debug: props.debug,
1601
1804
  forceLatestEvalVersion: shouldLoadLatestEval,
1602
1805
  defaultToFullScreen: true,
1603
1806
  showToggleFullScreen: false,
1604
- leftHeaderContent: /* @__PURE__ */ jsx9("div", { className: "rf-flex", children: /* @__PURE__ */ jsx9(
1807
+ leftHeaderContent: /* @__PURE__ */ jsx11("div", { className: "rf-flex", children: /* @__PURE__ */ jsx11(
1605
1808
  RunframeCliLeftHeader,
1606
1809
  {
1607
1810
  shouldLoadLatestEval,