@tscircuit/runframe 0.0.995 → 0.0.996

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
@@ -4,26 +4,35 @@ import {
4
4
  Button,
5
5
  CadViewer,
6
6
  CircuitJsonPreview,
7
+ Dialog,
8
+ DialogContent,
9
+ DialogDescription,
10
+ DialogFooter,
11
+ DialogHeader,
12
+ DialogTitle,
7
13
  FileMenuLeftHeader,
8
- ImportComponentDialog,
14
+ ImportComponentDialog2,
15
+ ImportComponentDialogForCli,
16
+ Input,
9
17
  PCBViewer,
10
18
  PcbViewerWithContainerHeight,
11
19
  SchematicViewer,
20
+ Tabs,
21
+ TabsContent,
22
+ TabsList,
23
+ TabsTrigger,
12
24
  cn,
13
25
  debug_default,
14
26
  linkify,
15
- mapJLCComponentToSearchResult,
16
- mapTscircuitSnippetToSearchResult,
17
27
  registryKy,
18
- searchJLCComponents,
19
- searchTscircuitComponents,
28
+ toast,
20
29
  useLocalStorageState,
21
30
  useOrderDialog,
22
31
  useOrderDialogCli,
23
32
  useRunFrameStore,
24
33
  useRunnerStore,
25
34
  useStyles
26
- } from "./chunk-Z3QYGCIG.js";
35
+ } from "./chunk-X3W75RUU.js";
27
36
 
28
37
  // lib/components/RunFrame/RunFrame.tsx
29
38
  import { createCircuitWebWorker } from "@tscircuit/eval/worker";
@@ -1392,11 +1401,586 @@ var RunFrameForCli = (props) => {
1392
1401
  );
1393
1402
  };
1394
1403
 
1404
+ // lib/components/ImportComponentDialog/ImportComponentDialog.tsx
1405
+ import "react";
1406
+ import { useState as useState6, useEffect as useEffect6 } from "react";
1407
+ import { Loader2 as Loader22, Search as Search2, ExternalLink } from "lucide-react";
1408
+
1409
+ // lib/components/ImportComponentDialog/jlc-api.ts
1410
+ var searchJLCComponents = async (query, limit = 10) => {
1411
+ try {
1412
+ const encodedQuery = encodeURIComponent(query);
1413
+ const response = await fetch(
1414
+ `https://jlcsearch.tscircuit.com/api/search?limit=${limit}&q=${encodedQuery}`
1415
+ );
1416
+ if (!response.ok) {
1417
+ throw new Error(
1418
+ `JLC API error: ${response.status} ${response.statusText}`
1419
+ );
1420
+ }
1421
+ const data = await response.json();
1422
+ return data.components || [];
1423
+ } catch (error) {
1424
+ console.error("Error searching JLC components:", error);
1425
+ throw error;
1426
+ }
1427
+ };
1428
+ var mapJLCComponentToSearchResult = (jlcComponent) => {
1429
+ return {
1430
+ id: `jlc-${jlcComponent.lcsc}`,
1431
+ name: jlcComponent.mfr,
1432
+ description: jlcComponent.description,
1433
+ source: "jlcpcb",
1434
+ partNumber: `C${jlcComponent.lcsc}`,
1435
+ package: jlcComponent.package,
1436
+ price: jlcComponent.price
1437
+ };
1438
+ };
1439
+
1440
+ // lib/components/ImportComponentDialog/kicad-api.ts
1441
+ import Fuse from "fuse.js";
1442
+ var KICAD_FOOTPRINTS = null;
1443
+ var KICAD_FOOTPRINTS_PROMISE = null;
1444
+ var getKicadFootprints = async () => {
1445
+ if (KICAD_FOOTPRINTS) return KICAD_FOOTPRINTS;
1446
+ if (KICAD_FOOTPRINTS_PROMISE) return KICAD_FOOTPRINTS_PROMISE;
1447
+ KICAD_FOOTPRINTS_PROMISE = fetch(
1448
+ "https://kicad-mod-cache.tscircuit.com/kicad_files.json"
1449
+ ).then((r) => r.json()).then((footprints) => {
1450
+ KICAD_FOOTPRINTS = footprints;
1451
+ KICAD_FOOTPRINTS_PROMISE = null;
1452
+ return footprints;
1453
+ });
1454
+ return KICAD_FOOTPRINTS_PROMISE;
1455
+ };
1456
+ var fuse = null;
1457
+ var searchKicadFootprints = async (query, limit = 20) => {
1458
+ const footprints = await getKicadFootprints();
1459
+ if (!fuse) {
1460
+ fuse = new Fuse(footprints);
1461
+ }
1462
+ return fuse.search(query).slice(0, limit).map((r) => r.item);
1463
+ };
1464
+ var mapKicadFootprintToSearchResult = (footprintPath) => {
1465
+ const cleanedFootprint = footprintPath.replace(".pretty/", ":").replace(".kicad_mod", "");
1466
+ const footprintString = `kicad:${cleanedFootprint}`;
1467
+ return {
1468
+ id: `kicad-${footprintPath}`,
1469
+ name: footprintString,
1470
+ description: cleanedFootprint,
1471
+ source: "kicad"
1472
+ };
1473
+ };
1474
+
1475
+ // lib/components/ImportComponentDialog/tscircuit-registry-api.ts
1476
+ var searchTscircuitComponents = async (query) => {
1477
+ try {
1478
+ const response = await fetch(
1479
+ "https://registry-api.tscircuit.com/packages/search",
1480
+ {
1481
+ method: "POST",
1482
+ headers: {
1483
+ "Content-Type": "application/json"
1484
+ },
1485
+ body: JSON.stringify({ query })
1486
+ }
1487
+ );
1488
+ if (!response.ok) {
1489
+ throw new Error(
1490
+ `tscircuit Registry API error: ${response.status} ${response.statusText}`
1491
+ );
1492
+ }
1493
+ const data = await response.json();
1494
+ return data.packages || [];
1495
+ } catch (error) {
1496
+ console.error("Error searching tscircuit components:", error);
1497
+ throw error;
1498
+ }
1499
+ };
1500
+ var mapTscircuitSnippetToSearchResult = (tscircuitSnippet) => {
1501
+ return {
1502
+ id: `tscircuit-${tscircuitSnippet.package_id}`,
1503
+ name: tscircuitSnippet.unscoped_name,
1504
+ description: tscircuitSnippet.description || `Component by ${tscircuitSnippet.owner_github_username}`,
1505
+ source: "tscircuit.com",
1506
+ partNumber: tscircuitSnippet.name,
1507
+ owner: String(tscircuitSnippet.owner_github_username)
1508
+ };
1509
+ };
1510
+
1511
+ // lib/optional-features/importing/import-component-from-jlcpcb.ts
1512
+ import { fetchEasyEDAComponent, convertRawEasyToTsx } from "easyeda/browser";
1513
+ import ky from "ky";
1514
+ var importComponentFromJlcpcb = async (jlcpcbPartNumber, opts) => {
1515
+ const component = await fetchEasyEDAComponent(jlcpcbPartNumber, {
1516
+ // @ts-ignore
1517
+ fetch: (url, options) => {
1518
+ return fetch(`${API_BASE}/proxy`, {
1519
+ ...options,
1520
+ headers: {
1521
+ ...options?.headers,
1522
+ "X-Target-Url": url.toString(),
1523
+ "X-Sender-Origin": options?.headers?.origin ?? "",
1524
+ "X-Sender-Host": options?.headers?.host ?? "https://easyeda.com",
1525
+ "X-Sender-Referer": options?.headers?.referer ?? "",
1526
+ "X-Sender-User-Agent": options?.headers?.userAgent ?? "",
1527
+ "X-Sender-Cookie": options?.headers?.cookie ?? "",
1528
+ ...opts?.headers
1529
+ }
1530
+ });
1531
+ }
1532
+ });
1533
+ const tsx = await convertRawEasyToTsx(component);
1534
+ const fileName = tsx.match(/export const (\w+) = .*/)?.[1];
1535
+ if (!fileName) {
1536
+ console.error("COULD NOT DETERMINE FILE NAME OF CONVERTED COMPONENT:", tsx);
1537
+ throw new Error(`Could not determine file name of converted component`);
1538
+ }
1539
+ const filePath = `imports/${fileName}.tsx`;
1540
+ await ky.post(`${API_BASE}/files/upsert`, {
1541
+ json: {
1542
+ file_path: filePath,
1543
+ text_content: tsx
1544
+ }
1545
+ });
1546
+ return { filePath };
1547
+ };
1548
+
1549
+ // lib/components/ImportComponentDialog/ImportComponentDialog.tsx
1550
+ import Debug3 from "debug";
1551
+ import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1552
+ var debug4 = Debug3("run-frame:ImportComponentDialog");
1553
+ var ImportComponentDialog = ({
1554
+ isOpen,
1555
+ onClose,
1556
+ proxyRequestHeaders,
1557
+ onImport
1558
+ }) => {
1559
+ const [searchQuery, setSearchQuery] = useState6("");
1560
+ const [searchResults, setSearchResults] = useState6(
1561
+ []
1562
+ );
1563
+ const [hasSearched, setHasSearched] = useState6(false);
1564
+ const [isLoading, setIsLoading] = useState6(false);
1565
+ const [selectedComponent, setSelectedComponent] = useState6(null);
1566
+ const [activeTab, setActiveTab] = useState6("tscircuit.com");
1567
+ const [detailsOpen, setDetailsOpen] = useState6(false);
1568
+ const [detailsComponent, setDetailsComponent] = useState6(null);
1569
+ const [packageDetails, setPackageDetails] = useState6(null);
1570
+ const [packageDetailsLoading, setPackageDetailsLoading] = useState6(false);
1571
+ const [previewActiveTab, setPreviewActiveTab] = useState6(
1572
+ "pcb"
1573
+ );
1574
+ const pushEvent = useRunFrameStore((s) => s.pushEvent);
1575
+ const handleImport = onImport ? (component) => onImport(component) : (component) => {
1576
+ toast.promise(
1577
+ async () => {
1578
+ if (component.source === "tscircuit.com") {
1579
+ await pushEvent({
1580
+ event_type: "INSTALL_PACKAGE",
1581
+ full_package_name: `@tsci/${component.owner}.${component.name}`
1582
+ });
1583
+ throw new Error("Not implemented");
1584
+ } else if (component.source === "jlcpcb") {
1585
+ const { filePath } = await importComponentFromJlcpcb(
1586
+ component.partNumber,
1587
+ { headers: proxyRequestHeaders }
1588
+ );
1589
+ return { filePath };
1590
+ } else if (component.source === "kicad") {
1591
+ await navigator.clipboard.writeText(component.name);
1592
+ return { footprint: component.name };
1593
+ }
1594
+ },
1595
+ {
1596
+ loading: `Importing component: "${component.name}"`,
1597
+ error: (error) => {
1598
+ console.error("IMPORT ERROR", error);
1599
+ return `Error importing component: "${component.name}": ${error.toString()}`;
1600
+ },
1601
+ success: (data) => {
1602
+ if (data?.footprint) {
1603
+ return `Copied "${data.footprint}" to clipboard`;
1604
+ }
1605
+ return data?.filePath ? `Imported to "${data.filePath}"` : "Import Successful";
1606
+ }
1607
+ }
1608
+ );
1609
+ };
1610
+ const fetchPackageDetails = async (owner, name) => {
1611
+ setPackageDetailsLoading(true);
1612
+ try {
1613
+ const response = await fetch(
1614
+ `https://registry-api.tscircuit.com/packages/get?name=${encodeURIComponent(`${owner}/${name}`)}`
1615
+ );
1616
+ if (response.ok) {
1617
+ const data = await response.json();
1618
+ setPackageDetails(data.package || null);
1619
+ }
1620
+ } catch (error) {
1621
+ console.error("Error fetching package details:", error);
1622
+ setPackageDetails(null);
1623
+ } finally {
1624
+ setPackageDetailsLoading(false);
1625
+ }
1626
+ };
1627
+ const handleSearch = async () => {
1628
+ if (!searchQuery.trim()) return;
1629
+ setIsLoading(true);
1630
+ const isJlcPartNumber = /^C\d+/.test(searchQuery);
1631
+ try {
1632
+ if (activeTab === "jlcpcb") {
1633
+ const query = isJlcPartNumber ? searchQuery.substring(1) : searchQuery;
1634
+ const jlcComponents = await searchJLCComponents(query, 10);
1635
+ const mappedResults = jlcComponents.map(mapJLCComponentToSearchResult);
1636
+ setSearchResults(mappedResults);
1637
+ } else if (activeTab === "kicad") {
1638
+ const kicadFootprints = await searchKicadFootprints(searchQuery, 20);
1639
+ const mappedResults = kicadFootprints.map(
1640
+ mapKicadFootprintToSearchResult
1641
+ );
1642
+ setSearchResults(mappedResults);
1643
+ } else {
1644
+ const tscircuitComponents = await searchTscircuitComponents(searchQuery);
1645
+ const mappedResults = tscircuitComponents.map(
1646
+ mapTscircuitSnippetToSearchResult
1647
+ );
1648
+ setSearchResults(mappedResults);
1649
+ }
1650
+ } catch (error) {
1651
+ console.error("Error searching components:", error);
1652
+ setSearchResults([]);
1653
+ } finally {
1654
+ setIsLoading(false);
1655
+ setHasSearched(true);
1656
+ }
1657
+ };
1658
+ const handleKeyDown = (e) => {
1659
+ if (e.key === "Enter") {
1660
+ handleSearch();
1661
+ }
1662
+ };
1663
+ useEffect6(() => {
1664
+ setSearchResults([]);
1665
+ setSelectedComponent(null);
1666
+ }, [activeTab]);
1667
+ const showDetails = (component) => {
1668
+ setDetailsComponent(component);
1669
+ setDetailsOpen(true);
1670
+ setPackageDetails(null);
1671
+ setPreviewActiveTab("pcb");
1672
+ if (component.source === "tscircuit.com" && component.owner) {
1673
+ const packageName = component.name.split("/").pop() || component.name;
1674
+ fetchPackageDetails(component.owner, packageName);
1675
+ }
1676
+ };
1677
+ return /* @__PURE__ */ jsxs7(Dialog, { open: isOpen, onOpenChange: () => onClose(), children: [
1678
+ /* @__PURE__ */ jsxs7(
1679
+ DialogContent,
1680
+ {
1681
+ style: {
1682
+ width: "calc(100vw - 2rem)"
1683
+ },
1684
+ className: "rf-rounded-sm rf-max-h-[90vh] rf-overflow-y-auto rf-flex rf-flex-col",
1685
+ children: [
1686
+ /* @__PURE__ */ jsxs7(DialogHeader, { children: [
1687
+ /* @__PURE__ */ jsx8(DialogTitle, { className: "rf-text-lg sm:rf-text-xl", children: "Import Component" }),
1688
+ /* @__PURE__ */ jsx8(DialogDescription, { className: "rf-text-sm", children: "Search for components from tscircuit.com or JLCPCB parts library." })
1689
+ ] }),
1690
+ /* @__PURE__ */ jsxs7(
1691
+ Tabs,
1692
+ {
1693
+ value: activeTab,
1694
+ onValueChange: (value) => setActiveTab(value),
1695
+ children: [
1696
+ /* @__PURE__ */ jsxs7(TabsList, { className: "rf-grid rf-w-full rf-grid-cols-1 sm:rf-grid-cols-3 rf-h-auto", children: [
1697
+ /* @__PURE__ */ jsx8(
1698
+ TabsTrigger,
1699
+ {
1700
+ value: "tscircuit.com",
1701
+ className: "rf-text-xs sm:rf-text-sm",
1702
+ children: "tscircuit.com"
1703
+ }
1704
+ ),
1705
+ /* @__PURE__ */ jsx8(TabsTrigger, { value: "jlcpcb", className: "rf-text-xs sm:rf-text-sm", children: "JLCPCB Parts" }),
1706
+ /* @__PURE__ */ jsx8(TabsTrigger, { value: "kicad", className: "rf-text-xs sm:rf-text-sm", children: "KiCad" })
1707
+ ] }),
1708
+ /* @__PURE__ */ jsxs7("div", { className: "rf-flex rf-items-center rf-gap-2 rf-mt-4", children: [
1709
+ /* @__PURE__ */ jsxs7("div", { className: "rf-relative rf-flex-grow", children: [
1710
+ /* @__PURE__ */ jsx8(Search2, { className: "rf-absolute rf-left-2 rf-top-2.5 rf-h-4 rf-w-4 rf-text-muted-foreground" }),
1711
+ /* @__PURE__ */ jsx8(
1712
+ Input,
1713
+ {
1714
+ placeholder: activeTab === "tscircuit.com" ? "Search components..." : activeTab === "kicad" ? "Search KiCad footprints..." : "Search JLCPCB parts (e.g. C14663)...",
1715
+ className: "rf-pl-8",
1716
+ spellCheck: false,
1717
+ autoComplete: "off",
1718
+ value: searchQuery,
1719
+ onChange: (e) => setSearchQuery(e.target.value),
1720
+ onKeyDown: handleKeyDown
1721
+ }
1722
+ )
1723
+ ] }),
1724
+ /* @__PURE__ */ jsx8(
1725
+ Button,
1726
+ {
1727
+ onClick: handleSearch,
1728
+ disabled: isLoading || searchQuery.trim().length < 1,
1729
+ className: "sm:rf-px-4 rf-px-3",
1730
+ children: isLoading ? /* @__PURE__ */ jsx8(Loader22, { className: "rf-h-4 rf-w-4 rf-animate-spin" }) : /* @__PURE__ */ jsxs7(Fragment3, { children: [
1731
+ /* @__PURE__ */ jsx8(Search2, { className: "rf-h-4 rf-w-4 sm:rf-hidden" }),
1732
+ /* @__PURE__ */ jsx8("span", { className: "rf-hidden sm:rf-inline", children: "Search" })
1733
+ ] })
1734
+ }
1735
+ )
1736
+ ] }),
1737
+ /* @__PURE__ */ jsx8("div", { className: "rf-mt-4 rf-flex-1 rf-min-h-[200px] !rf-max-h-[40vh] !rf-overflow-y-auto rf-border rf-rounded-md", children: searchResults.length > 0 ? /* @__PURE__ */ jsx8("div", { className: "rf-divide-y", children: searchResults.map((result) => /* @__PURE__ */ jsxs7(
1738
+ "div",
1739
+ {
1740
+ className: `rf-p-3 rf-flex rf-flex-col sm:rf-grid sm:rf-grid-cols-[1fr_auto] rf-items-start sm:rf-items-center rf-cursor-pointer hover:rf-bg-zinc-100 rf-gap-2 ${selectedComponent?.id === result.id ? "rf-bg-zinc-100" : ""}`,
1741
+ onClick: () => setSelectedComponent(result),
1742
+ children: [
1743
+ /* @__PURE__ */ jsxs7("div", { className: "rf-min-w-0 rf-overflow-hidden", children: [
1744
+ /* @__PURE__ */ jsx8("div", { className: "rf-font-medium rf-text-sm rf-truncate", children: result.name }),
1745
+ /* @__PURE__ */ jsxs7("div", { className: "rf-text-xs rf-text-zinc-500 rf-truncate", children: [
1746
+ result.partNumber && /* @__PURE__ */ jsx8("span", { className: "rf-mr-2", children: result.partNumber }),
1747
+ result.description
1748
+ ] })
1749
+ ] }),
1750
+ /* @__PURE__ */ jsx8("div", { className: "rf-flex rf-gap-2 rf-flex-shrink-0 rf-w-full sm:rf-w-auto", children: result.source === "tscircuit.com" && /* @__PURE__ */ jsx8(
1751
+ Button,
1752
+ {
1753
+ variant: "outline",
1754
+ size: "sm",
1755
+ className: "rf-text-xs rf-w-full sm:rf-w-auto",
1756
+ onClick: (e) => {
1757
+ e.stopPropagation();
1758
+ showDetails(result);
1759
+ },
1760
+ children: "See Details"
1761
+ }
1762
+ ) })
1763
+ ]
1764
+ },
1765
+ result.id
1766
+ )) }) : isLoading ? /* @__PURE__ */ jsxs7("div", { className: "rf-p-8 rf-text-center rf-text-zinc-500", children: [
1767
+ /* @__PURE__ */ jsx8(Loader22, { className: "rf-h-8 rf-w-8 rf-animate-spin rf-mx-auto rf-mb-2" }),
1768
+ /* @__PURE__ */ jsx8("p", { children: "Searching..." })
1769
+ ] }) : /* @__PURE__ */ jsx8("div", { className: "rf-p-8 rf-text-center rf-text-zinc-500", children: hasSearched ? "No results found" : "Enter a search term to find components" }) })
1770
+ ]
1771
+ }
1772
+ ),
1773
+ /* @__PURE__ */ jsxs7(DialogFooter, { className: "rf-flex rf-flex-col sm:rf-flex-row rf-gap-2", children: [
1774
+ /* @__PURE__ */ jsx8(
1775
+ Button,
1776
+ {
1777
+ variant: "outline",
1778
+ onClick: onClose,
1779
+ className: "rf-order-2 sm:rf-order-1",
1780
+ children: "Cancel"
1781
+ }
1782
+ ),
1783
+ /* @__PURE__ */ jsx8(
1784
+ Button,
1785
+ {
1786
+ onClick: () => {
1787
+ if (selectedComponent) {
1788
+ handleImport(selectedComponent);
1789
+ onClose();
1790
+ }
1791
+ },
1792
+ disabled: !selectedComponent,
1793
+ children: selectedComponent?.source === "kicad" ? "Copy Footprint" : "Import Component"
1794
+ }
1795
+ )
1796
+ ] })
1797
+ ]
1798
+ }
1799
+ ),
1800
+ /* @__PURE__ */ jsx8(Dialog, { open: detailsOpen, onOpenChange: setDetailsOpen, children: /* @__PURE__ */ jsxs7(
1801
+ DialogContent,
1802
+ {
1803
+ showOverlay: false,
1804
+ style: {
1805
+ width: "calc(100vw - 2rem)"
1806
+ },
1807
+ className: "rf-max-w-5xl !rf-overflow-y-auto rf-max-h-[90vh] rf-overflow-hidden rf-flex rf-flex-col rf-rounded-sm",
1808
+ children: [
1809
+ /* @__PURE__ */ jsx8(DialogHeader, { className: "rf-pb-4 rf-border-b", children: /* @__PURE__ */ jsx8("div", { className: "rf-flex rf-items-start rf-justify-between rf-gap-4", children: /* @__PURE__ */ jsx8("div", { className: "rf-flex-1 rf-min-w-0", children: /* @__PURE__ */ jsx8(DialogTitle, { className: "rf-text-xl rf-font-semibold rf-truncate", children: detailsComponent?.source === "kicad" ? detailsComponent?.name : /* @__PURE__ */ jsx8(
1810
+ "a",
1811
+ {
1812
+ href: `https://tscircuit.com/${detailsComponent?.owner}/${detailsComponent?.name}`,
1813
+ target: "_blank",
1814
+ rel: "noopener noreferrer",
1815
+ className: "rf-text-black hover:rf-underline",
1816
+ children: detailsComponent?.name?.split("/").pop() || detailsComponent?.name
1817
+ }
1818
+ ) }) }) }) }),
1819
+ /* @__PURE__ */ jsx8("div", { className: "rf-flex-1 rf-overflow-y-auto rf-py-4 rf-space-y-6", children: detailsComponent?.source === "tscircuit.com" ? /* @__PURE__ */ jsxs7(Fragment3, { children: [
1820
+ /* @__PURE__ */ jsx8("div", { children: /* @__PURE__ */ jsx8("div", { className: "rf-space-y-3", children: detailsComponent?.owner && /* @__PURE__ */ jsxs7("div", { children: [
1821
+ /* @__PURE__ */ jsx8("label", { className: "rf-text-xs rf-font-medium rf-text-gray-500 rf-uppercase rf-tracking-wide", children: "Created by" }),
1822
+ /* @__PURE__ */ jsx8("div", { className: "rf-mt-1 rf-text-sm rf-font-medium", children: /* @__PURE__ */ jsx8(
1823
+ "a",
1824
+ {
1825
+ href: `https://tscircuit.com/${detailsComponent?.owner}`,
1826
+ target: "_blank",
1827
+ rel: "noopener noreferrer",
1828
+ className: "rf-text-black hover:rf-underline",
1829
+ children: detailsComponent?.owner
1830
+ }
1831
+ ) })
1832
+ ] }) }) }),
1833
+ /* @__PURE__ */ jsxs7("div", { children: [
1834
+ /* @__PURE__ */ jsx8("h3", { className: "rf-text-lg rf-font-semibold rf-mb-4", children: "Preview" }),
1835
+ /* @__PURE__ */ jsxs7(
1836
+ Tabs,
1837
+ {
1838
+ value: previewActiveTab,
1839
+ onValueChange: (value) => setPreviewActiveTab(value),
1840
+ children: [
1841
+ /* @__PURE__ */ jsxs7(TabsList, { className: "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", children: [
1842
+ /* @__PURE__ */ jsx8(
1843
+ TabsTrigger,
1844
+ {
1845
+ value: "pcb",
1846
+ className: "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",
1847
+ children: "PCB"
1848
+ }
1849
+ ),
1850
+ /* @__PURE__ */ jsx8(
1851
+ TabsTrigger,
1852
+ {
1853
+ value: "schematic",
1854
+ className: "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",
1855
+ children: "Schematic"
1856
+ }
1857
+ )
1858
+ ] }),
1859
+ /* @__PURE__ */ jsxs7("div", { className: "rf-mt-4", children: [
1860
+ /* @__PURE__ */ jsx8(
1861
+ TabsContent,
1862
+ {
1863
+ value: "pcb",
1864
+ className: "rf-border rf-rounded-lg rf-overflow-hidden rf-bg-gray-50",
1865
+ children: detailsComponent?.owner && detailsComponent?.name ? /* @__PURE__ */ jsx8("div", { className: "rf-w-full rf-h-fit rf-min-h-[300px] rf-bg-white rf-flex rf-items-center rf-justify-center rf-p-4", children: /* @__PURE__ */ jsx8(
1866
+ "img",
1867
+ {
1868
+ src: `https://registry-api.tscircuit.com/packages/images/${detailsComponent.owner}/${detailsComponent.name}/pcb.png`,
1869
+ alt: `${detailsComponent.name} PCB preview`,
1870
+ className: "rf-max-w-full rf-max-h-full rf-object-contain rf-rounded",
1871
+ onError: (e) => {
1872
+ const target = e.target;
1873
+ target.style.display = "none";
1874
+ const parent = target.parentElement;
1875
+ if (parent) {
1876
+ parent.innerHTML = '<div class="rf-text-center rf-text-gray-500"><div class="rf-text-sm rf-font-medium">PCB preview not available</div><div class="rf-text-xs rf-mt-1">Preview cannot be generated</div></div>';
1877
+ }
1878
+ }
1879
+ }
1880
+ ) }) : /* @__PURE__ */ jsx8("div", { className: "rf-h-[400px] rf-flex rf-items-center rf-justify-center rf-text-gray-500", children: /* @__PURE__ */ jsxs7("div", { className: "rf-text-center", children: [
1881
+ /* @__PURE__ */ jsx8("div", { className: "rf-text-sm rf-font-medium", children: "No PCB preview available" }),
1882
+ /* @__PURE__ */ jsx8("div", { className: "rf-text-xs rf-mt-1", children: "Preview cannot be generated" })
1883
+ ] }) })
1884
+ }
1885
+ ),
1886
+ /* @__PURE__ */ jsx8(
1887
+ TabsContent,
1888
+ {
1889
+ value: "schematic",
1890
+ className: "rf-border rf-rounded-lg rf-overflow-hidden rf-bg-gray-50",
1891
+ children: detailsComponent?.owner && detailsComponent?.name ? /* @__PURE__ */ jsx8("div", { className: "rf-w-full rf-h-fit rf-min-h-[300px] rf-bg-white rf-flex rf-items-center rf-justify-center rf-p-4", children: /* @__PURE__ */ jsx8(
1892
+ "img",
1893
+ {
1894
+ src: `https://registry-api.tscircuit.com/packages/images/${detailsComponent.owner}/${detailsComponent.name}/schematic.png`,
1895
+ alt: `${detailsComponent.name} schematic preview`,
1896
+ className: "rf-max-w-full rf-max-h-full rf-object-contain rf-rounded",
1897
+ onError: (e) => {
1898
+ const target = e.target;
1899
+ target.style.display = "none";
1900
+ const parent = target.parentElement;
1901
+ if (parent) {
1902
+ parent.innerHTML = '<div class="rf-text-center rf-text-gray-500"><div class="rf-text-sm rf-font-medium">Schematic preview not available</div><div class="rf-text-xs rf-mt-1">Preview cannot be generated</div></div>';
1903
+ }
1904
+ }
1905
+ }
1906
+ ) }) : /* @__PURE__ */ jsx8("div", { className: "rf-h-[400px] rf-flex rf-items-center rf-justify-center rf-text-gray-500", children: /* @__PURE__ */ jsxs7("div", { className: "rf-text-center", children: [
1907
+ /* @__PURE__ */ jsx8("div", { className: "rf-text-sm rf-font-medium", children: "No schematic preview available" }),
1908
+ /* @__PURE__ */ jsx8("div", { className: "rf-text-xs rf-mt-1", children: "Preview cannot be generated" })
1909
+ ] }) })
1910
+ }
1911
+ )
1912
+ ] })
1913
+ ]
1914
+ }
1915
+ )
1916
+ ] }),
1917
+ packageDetails?.ai_description && /* @__PURE__ */ jsxs7("div", { children: [
1918
+ /* @__PURE__ */ jsx8("h3", { className: "rf-text-lg rf-font-semibold rf-mb-3", children: "AI Description" }),
1919
+ /* @__PURE__ */ jsx8("div", { className: "rf-bg-gray-50 rf-border rf-border-gray-200 rf-rounded-lg rf-p-4", children: /* @__PURE__ */ jsx8("p", { className: "rf-text-sm rf-text-gray-700 rf-leading-relaxed", children: packageDetails.ai_description }) })
1920
+ ] }),
1921
+ packageDetails?.ai_usage_instructions && /* @__PURE__ */ jsxs7("div", { children: [
1922
+ /* @__PURE__ */ jsx8("h3", { className: "rf-text-lg rf-font-semibold rf-mb-3", children: "Usage Instructions" }),
1923
+ /* @__PURE__ */ jsx8("div", { className: "rf-bg-gray-50 rf-border rf-border-gray-200 rf-rounded-lg rf-p-4", children: /* @__PURE__ */ jsx8("p", { className: "rf-text-sm rf-text-gray-700 rf-leading-relaxed rf-whitespace-pre-wrap", children: packageDetails.ai_usage_instructions }) })
1924
+ ] }),
1925
+ packageDetailsLoading && /* @__PURE__ */ jsxs7("div", { className: "rf-flex rf-justify-center rf-text-center rf-items-center rf-gap-2 rf-text-gray-500", children: [
1926
+ /* @__PURE__ */ jsx8(Loader22, { className: "rf-h-4 rf-w-4 rf-animate-spin" }),
1927
+ /* @__PURE__ */ jsx8("span", { className: "rf-text-sm", children: "Loading package details..." })
1928
+ ] })
1929
+ ] }) : null }),
1930
+ /* @__PURE__ */ jsxs7(DialogFooter, { className: "rf-pt-4 rf-border-t rf-flex rf-flex-col sm:rf-flex-row rf-justify-between rf-items-stretch sm:rf-items-center rf-gap-2", children: [
1931
+ /* @__PURE__ */ jsx8("div", { className: "rf-flex-1 rf-order-3 sm:rf-order-1", children: detailsComponent?.source === "tscircuit.com" && /* @__PURE__ */ jsxs7(
1932
+ Button,
1933
+ {
1934
+ variant: "outline",
1935
+ size: "sm",
1936
+ className: "rf-gap-2 rf-w-full sm:rf-w-auto",
1937
+ onClick: () => {
1938
+ const url = `https://tscircuit.com/${detailsComponent?.owner}/${detailsComponent?.name.split("/").pop()}`;
1939
+ window.open(url, "_blank");
1940
+ },
1941
+ children: [
1942
+ /* @__PURE__ */ jsx8(ExternalLink, { className: "rf-h-4 rf-w-4" }),
1943
+ "View on tscircuit.com"
1944
+ ]
1945
+ }
1946
+ ) }),
1947
+ /* @__PURE__ */ jsxs7("div", { className: "rf-flex rf-flex-col sm:rf-flex-row rf-gap-2 sm:rf-gap-3 rf-order-1 sm:rf-order-2", children: [
1948
+ /* @__PURE__ */ jsx8(
1949
+ Button,
1950
+ {
1951
+ variant: "outline",
1952
+ onClick: () => setDetailsOpen(false),
1953
+ className: "rf-order-2 sm:rf-order-1",
1954
+ children: "Close"
1955
+ }
1956
+ ),
1957
+ /* @__PURE__ */ jsx8(
1958
+ Button,
1959
+ {
1960
+ onClick: () => {
1961
+ setDetailsOpen(false);
1962
+ if (detailsComponent) {
1963
+ handleImport(detailsComponent);
1964
+ onClose();
1965
+ }
1966
+ },
1967
+ className: "rf-bg-blue-600 hover:rf-bg-blue-700 rf-order-1 sm:rf-order-2",
1968
+ children: detailsComponent?.source === "kicad" ? "Copy Footprint" : "Import Component"
1969
+ }
1970
+ )
1971
+ ] })
1972
+ ] })
1973
+ ]
1974
+ }
1975
+ ) })
1976
+ ] });
1977
+ };
1978
+
1395
1979
  // lib/components/RunFrameStaticBuildViewer/RunFrameStaticBuildViewer.tsx
1396
- import { useCallback as useCallback4, useEffect as useEffect6, useState as useState6 } from "react";
1980
+ import { useCallback as useCallback4, useEffect as useEffect7, useState as useState7 } from "react";
1397
1981
 
1398
1982
  // lib/components/RunFrameStaticBuildViewer/CircuitJsonFileSelectorCombobox.tsx
1399
- import { jsx as jsx8 } from "react/jsx-runtime";
1983
+ import { jsx as jsx9 } from "react/jsx-runtime";
1400
1984
  var circuitJsonConfig = {
1401
1985
  fileFilter: () => true,
1402
1986
  placeholder: "Select circuit",
@@ -1408,7 +1992,7 @@ var CircuitJsonFileSelectorCombobox = ({
1408
1992
  onFileChange,
1409
1993
  currentFile
1410
1994
  }) => {
1411
- return /* @__PURE__ */ jsx8(
1995
+ return /* @__PURE__ */ jsx9(
1412
1996
  EnhancedFileSelectorCombobox,
1413
1997
  {
1414
1998
  files,
@@ -1421,19 +2005,19 @@ var CircuitJsonFileSelectorCombobox = ({
1421
2005
 
1422
2006
  // lib/components/RunFrameStaticBuildViewer/RunFrameStaticBuildViewer.tsx
1423
2007
  import { ErrorBoundary } from "react-error-boundary";
1424
- import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
2008
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
1425
2009
  var RunFrameStaticBuildViewer = (props) => {
1426
2010
  useStyles();
1427
- const [currentCircuitJsonPath, setCurrentCircuitJsonPath] = useState6(
2011
+ const [currentCircuitJsonPath, setCurrentCircuitJsonPath] = useState7(
1428
2012
  () => {
1429
2013
  return props.initialCircuitPath ?? "";
1430
2014
  }
1431
2015
  );
1432
- const [circuitJson, setCircuitJson] = useState6(null);
1433
- const [isLoadingCurrentFile, setIsLoadingCurrentFile] = useState6(false);
1434
- const [fileCache, setFileCache] = useState6({});
1435
- const [loadingFiles, setLoadingFiles] = useState6(/* @__PURE__ */ new Set());
1436
- const [failedFiles, setFailedFiles] = useState6(/* @__PURE__ */ new Set());
2016
+ const [circuitJson, setCircuitJson] = useState7(null);
2017
+ const [isLoadingCurrentFile, setIsLoadingCurrentFile] = useState7(false);
2018
+ const [fileCache, setFileCache] = useState7({});
2019
+ const [loadingFiles, setLoadingFiles] = useState7(/* @__PURE__ */ new Set());
2020
+ const [failedFiles, setFailedFiles] = useState7(/* @__PURE__ */ new Set());
1437
2021
  const fileReferences = props.files;
1438
2022
  const availableFiles = fileReferences.map((f) => f.filePath);
1439
2023
  const defaultFetchFile = useCallback4(
@@ -1509,7 +2093,7 @@ var RunFrameStaticBuildViewer = (props) => {
1509
2093
  defaultFetchFile
1510
2094
  ]
1511
2095
  );
1512
- useEffect6(() => {
2096
+ useEffect7(() => {
1513
2097
  if (availableFiles.length === 0) return;
1514
2098
  let selectedPath = currentCircuitJsonPath;
1515
2099
  if (!selectedPath || !availableFiles.includes(selectedPath)) {
@@ -1535,22 +2119,22 @@ var RunFrameStaticBuildViewer = (props) => {
1535
2119
  [loadCircuitJsonFile]
1536
2120
  );
1537
2121
  if (availableFiles.length === 0) {
1538
- return /* @__PURE__ */ jsx9("div", { className: "rf-flex rf-flex-col rf-w-full rf-h-full rf-items-center rf-justify-center", children: /* @__PURE__ */ jsxs7("div", { className: "rf-text-center rf-p-8", children: [
1539
- /* @__PURE__ */ jsx9("h3", { className: "rf-text-lg rf-font-semibold rf-text-gray-800 rf-mb-2", children: "No Circuit JSON Files Found" }),
1540
- /* @__PURE__ */ jsx9("p", { className: "rf-text-sm rf-text-gray-600", children: "Please provide circuit JSON files to view." })
2122
+ return /* @__PURE__ */ jsx10("div", { className: "rf-flex rf-flex-col rf-w-full rf-h-full rf-items-center rf-justify-center", children: /* @__PURE__ */ jsxs8("div", { className: "rf-text-center rf-p-8", children: [
2123
+ /* @__PURE__ */ jsx10("h3", { className: "rf-text-lg rf-font-semibold rf-text-gray-800 rf-mb-2", children: "No Circuit JSON Files Found" }),
2124
+ /* @__PURE__ */ jsx10("p", { className: "rf-text-sm rf-text-gray-600", children: "Please provide circuit JSON files to view." })
1541
2125
  ] }) });
1542
2126
  }
1543
2127
  const currentFileFailed = failedFiles.has(currentCircuitJsonPath);
1544
- return /* @__PURE__ */ jsx9(
2128
+ return /* @__PURE__ */ jsx10(
1545
2129
  ErrorBoundary,
1546
2130
  {
1547
- fallbackRender: ({ error }) => /* @__PURE__ */ jsx9("div", { className: "rf-mt-4 rf-mx-4 rf-bg-red-50 rf-rounded-md rf-border rf-border-red-200", children: /* @__PURE__ */ jsxs7("div", { className: "rf-p-4", children: [
1548
- /* @__PURE__ */ jsx9("h3", { className: "rf-text-lg rf-font-semibold rf-text-red-800 rf-mb-3", children: "Error loading Circuit JSON Preview" }),
1549
- /* @__PURE__ */ jsx9("p", { className: "rf-text-xs rf-font-mono rf-whitespace-pre-wrap rf-text-red-600 rf-mt-2", children: error.message })
2131
+ fallbackRender: ({ error }) => /* @__PURE__ */ jsx10("div", { className: "rf-mt-4 rf-mx-4 rf-bg-red-50 rf-rounded-md rf-border rf-border-red-200", children: /* @__PURE__ */ jsxs8("div", { className: "rf-p-4", children: [
2132
+ /* @__PURE__ */ jsx10("h3", { className: "rf-text-lg rf-font-semibold rf-text-red-800 rf-mb-3", children: "Error loading Circuit JSON Preview" }),
2133
+ /* @__PURE__ */ jsx10("p", { className: "rf-text-xs rf-font-mono rf-whitespace-pre-wrap rf-text-red-600 rf-mt-2", children: error.message })
1550
2134
  ] }) }),
1551
- children: currentFileFailed ? /* @__PURE__ */ jsxs7("div", { className: "rf-w-full rf-h-full rf-flex rf-flex-col", children: [
1552
- /* @__PURE__ */ jsxs7("div", { className: "rf-flex rf-items-center rf-justify-between rf-w-full rf-p-4 rf-border-b rf-border-gray-200", children: [
1553
- (props.showFileMenu ?? true) && /* @__PURE__ */ jsx9(
2135
+ children: currentFileFailed ? /* @__PURE__ */ jsxs8("div", { className: "rf-w-full rf-h-full rf-flex rf-flex-col", children: [
2136
+ /* @__PURE__ */ jsxs8("div", { className: "rf-flex rf-items-center rf-justify-between rf-w-full rf-p-4 rf-border-b rf-border-gray-200", children: [
2137
+ (props.showFileMenu ?? true) && /* @__PURE__ */ jsx10(
1554
2138
  FileMenuLeftHeader,
1555
2139
  {
1556
2140
  isWebEmbedded: true,
@@ -1558,7 +2142,7 @@ var RunFrameStaticBuildViewer = (props) => {
1558
2142
  projectName: props.projectName
1559
2143
  }
1560
2144
  ),
1561
- availableFiles.length > 1 && /* @__PURE__ */ jsx9("div", { className: "rf-absolute rf-left-1/2 rf-transform rf--translate-x-1/2 rf-flex rf-items-center rf-gap-2", children: /* @__PURE__ */ jsx9(
2145
+ availableFiles.length > 1 && /* @__PURE__ */ jsx10("div", { className: "rf-absolute rf-left-1/2 rf-transform rf--translate-x-1/2 rf-flex rf-items-center rf-gap-2", children: /* @__PURE__ */ jsx10(
1562
2146
  CircuitJsonFileSelectorCombobox,
1563
2147
  {
1564
2148
  currentFile: currentCircuitJsonPath,
@@ -1567,14 +2151,14 @@ var RunFrameStaticBuildViewer = (props) => {
1567
2151
  }
1568
2152
  ) })
1569
2153
  ] }),
1570
- /* @__PURE__ */ jsx9("div", { className: "rf-flex-1 rf-flex rf-items-center rf-justify-center", children: /* @__PURE__ */ jsxs7("div", { className: "rf-text-center rf-p-8", children: [
1571
- /* @__PURE__ */ jsx9("h3", { className: "rf-text-lg rf-font-semibold rf-text-red-600 rf-mb-2", children: "Failed to Load Circuit File" }),
1572
- /* @__PURE__ */ jsxs7("p", { className: "rf-text-sm rf-text-gray-600 rf-mb-4", children: [
2154
+ /* @__PURE__ */ jsx10("div", { className: "rf-flex-1 rf-flex rf-items-center rf-justify-center", children: /* @__PURE__ */ jsxs8("div", { className: "rf-text-center rf-p-8", children: [
2155
+ /* @__PURE__ */ jsx10("h3", { className: "rf-text-lg rf-font-semibold rf-text-red-600 rf-mb-2", children: "Failed to Load Circuit File" }),
2156
+ /* @__PURE__ */ jsxs8("p", { className: "rf-text-sm rf-text-gray-600 rf-mb-4", children: [
1573
2157
  "Could not load: ",
1574
2158
  currentCircuitJsonPath
1575
2159
  ] }),
1576
- /* @__PURE__ */ jsxs7("div", { className: "rf-flex rf-flex-col rf-items-center rf-gap-2", children: [
1577
- /* @__PURE__ */ jsx9(
2160
+ /* @__PURE__ */ jsxs8("div", { className: "rf-flex rf-flex-col rf-items-center rf-gap-2", children: [
2161
+ /* @__PURE__ */ jsx10(
1578
2162
  "button",
1579
2163
  {
1580
2164
  onClick: () => retryFailedFile(currentCircuitJsonPath),
@@ -1582,10 +2166,10 @@ var RunFrameStaticBuildViewer = (props) => {
1582
2166
  children: "Retry"
1583
2167
  }
1584
2168
  ),
1585
- availableFiles.length > 1 && /* @__PURE__ */ jsx9("p", { className: "rf-text-xs rf-text-gray-500", children: "Or select a different file from the dropdown above" })
2169
+ availableFiles.length > 1 && /* @__PURE__ */ jsx10("p", { className: "rf-text-xs rf-text-gray-500", children: "Or select a different file from the dropdown above" })
1586
2170
  ] })
1587
2171
  ] }) })
1588
- ] }) : /* @__PURE__ */ jsx9(
2172
+ ] }) : /* @__PURE__ */ jsx10(
1589
2173
  CircuitJsonPreview,
1590
2174
  {
1591
2175
  circuitJson,
@@ -1594,8 +2178,8 @@ var RunFrameStaticBuildViewer = (props) => {
1594
2178
  showFileMenu: false,
1595
2179
  isWebEmbedded: false,
1596
2180
  projectName: props.projectName,
1597
- leftHeaderContent: /* @__PURE__ */ jsxs7("div", { className: "rf-flex rf-items-center rf-justify-between rf-w-full", children: [
1598
- (props.showFileMenu ?? true) && /* @__PURE__ */ jsx9(
2181
+ leftHeaderContent: /* @__PURE__ */ jsxs8("div", { className: "rf-flex rf-items-center rf-justify-between rf-w-full", children: [
2182
+ (props.showFileMenu ?? true) && /* @__PURE__ */ jsx10(
1599
2183
  FileMenuLeftHeader,
1600
2184
  {
1601
2185
  isWebEmbedded: true,
@@ -1603,11 +2187,11 @@ var RunFrameStaticBuildViewer = (props) => {
1603
2187
  projectName: props.projectName
1604
2188
  }
1605
2189
  ),
1606
- availableFiles.length > 1 && /* @__PURE__ */ jsx9(
2190
+ availableFiles.length > 1 && /* @__PURE__ */ jsx10(
1607
2191
  "div",
1608
2192
  {
1609
2193
  className: `rf-absolute rf-left-1/2 rf-transform rf--translate-x-1/2 rf-flex rf-items-center rf-gap-2 ${isLoadingCurrentFile ? "rf-opacity-50" : ""}`,
1610
- children: /* @__PURE__ */ jsx9(
2194
+ children: /* @__PURE__ */ jsx10(
1611
2195
  CircuitJsonFileSelectorCombobox,
1612
2196
  {
1613
2197
  currentFile: currentCircuitJsonPath,
@@ -1629,6 +2213,8 @@ export {
1629
2213
  CircuitJsonFileSelectorCombobox,
1630
2214
  CircuitJsonPreview,
1631
2215
  ImportComponentDialog,
2216
+ ImportComponentDialog2,
2217
+ ImportComponentDialogForCli,
1632
2218
  PCBViewer as PcbViewer,
1633
2219
  PcbViewerWithContainerHeight,
1634
2220
  RunFrame,