@tscircuit/runframe 0.0.1180 → 0.0.1182

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
@@ -32,7 +32,7 @@ import {
32
32
  useRunFrameStore,
33
33
  useRunnerStore,
34
34
  useStyles
35
- } from "./chunk-N7PLNL6W.js";
35
+ } from "./chunk-UVCBFE3G.js";
36
36
 
37
37
  // lib/components/RunFrame/RunFrame.tsx
38
38
  import { createCircuitWebWorker } from "@tscircuit/eval/worker";
@@ -748,11 +748,11 @@ var useSyncPageTitle = () => {
748
748
  };
749
749
 
750
750
  // lib/components/RunFrameWithApi/RunFrameWithApi.tsx
751
- import { useCallback as useCallback3, useEffect as useEffect7, useMemo as useMemo2, useState as useState6 } from "react";
751
+ import { useCallback as useCallback4, useEffect as useEffect7, useMemo as useMemo3, useState as useState6 } from "react";
752
752
  import { applyEditEventsToManualEditsFile } from "@tscircuit/core";
753
753
 
754
754
  // lib/components/RunFrameWithApi/EnhancedFileSelectorCombobox/EnhancedFileSelectorCombobox.tsx
755
- import { useEffect as useEffect6, useState as useState5 } from "react";
755
+ import { useEffect as useEffect6, useState as useState5, useRef as useRef3, useMemo as useMemo2, useCallback as useCallback3 } from "react";
756
756
 
757
757
  // lib/components/RunFrameWithApi/EnhancedFileSelectorCombobox/useCurrentFolder.ts
758
758
  import { useState as useState4, useEffect as useEffect5 } from "react";
@@ -947,7 +947,10 @@ import {
947
947
  Code,
948
948
  Folder,
949
949
  ArrowUp,
950
- Star
950
+ Star,
951
+ Eye,
952
+ EyeOff,
953
+ Clock
951
954
  } from "lucide-react";
952
955
 
953
956
  // lib/components/RunFrameWithApi/EnhancedFileSelectorCombobox/parseFilesToTree.ts
@@ -1047,10 +1050,43 @@ var EnhancedFileSelectorCombobox = ({
1047
1050
  const { currentFolder, navigateToFolder, resetManualNavigation } = useCurrentFolder({ currentFile: file, files });
1048
1051
  const [currentFileIndex, setCurrentFileIndex] = useState5(0);
1049
1052
  const [searchValue, setSearchValue] = useState5("");
1053
+ const [recentlyViewedFiles, setRecentlyViewedFiles] = useLocalStorageState("runframe:recentlyViewed", []);
1054
+ const [showRecents, setShowRecents] = useLocalStorageState(
1055
+ "runframe:showRecents",
1056
+ true
1057
+ );
1050
1058
  useEffect6(() => {
1051
- setFile(currentFile);
1052
- }, [currentFile]);
1059
+ const handleKeyDown = (e) => {
1060
+ if ((e.metaKey || e.ctrlKey) && e.key === "k") {
1061
+ e.preventDefault();
1062
+ setOpen((prev) => !prev);
1063
+ }
1064
+ };
1065
+ window.addEventListener("keydown", handleKeyDown);
1066
+ return () => window.removeEventListener("keydown", handleKeyDown);
1067
+ }, []);
1053
1068
  const filteredFiles = files.filter(fileFilter);
1069
+ const debounceTimerRef = useRef3(null);
1070
+ useEffect6(() => {
1071
+ const clearDebounceTimer = () => {
1072
+ if (debounceTimerRef.current) {
1073
+ clearTimeout(debounceTimerRef.current);
1074
+ debounceTimerRef.current = null;
1075
+ }
1076
+ };
1077
+ setFile(currentFile);
1078
+ if (currentFile && filteredFiles.includes(currentFile)) {
1079
+ clearDebounceTimer();
1080
+ debounceTimerRef.current = setTimeout(() => {
1081
+ setRecentlyViewedFiles((prev) => {
1082
+ if (prev[0] === currentFile) return prev;
1083
+ const filtered = prev.filter((f) => f !== currentFile);
1084
+ return [currentFile, ...filtered].slice(0, 10);
1085
+ });
1086
+ }, 500);
1087
+ }
1088
+ return clearDebounceTimer;
1089
+ }, [currentFile, filteredFiles, setRecentlyViewedFiles]);
1054
1090
  const fileTree = parseFilesToTree(filteredFiles);
1055
1091
  const { files: currentFiles, folders: currentFolders } = getCurrentFolderContents(fileTree, currentFolder || "");
1056
1092
  const getSearchResults = () => {
@@ -1085,12 +1121,23 @@ var EnhancedFileSelectorCombobox = ({
1085
1121
  navigateToFolder(folderPath);
1086
1122
  setCurrentFileIndex(0);
1087
1123
  };
1088
- const navigateUp = () => {
1124
+ const navigateUp = useCallback3(() => {
1089
1125
  if (!currentFolder) return;
1090
1126
  const lastSlashIndex = currentFolder.lastIndexOf("/");
1091
1127
  const parentPath = lastSlashIndex === -1 ? null : currentFolder.substring(0, lastSlashIndex);
1092
1128
  handleNavigateToFolder(parentPath);
1093
- };
1129
+ }, [currentFolder, handleNavigateToFolder]);
1130
+ useEffect6(() => {
1131
+ if (!open) return;
1132
+ const handleKeyDown = (e) => {
1133
+ if ((e.metaKey || e.ctrlKey) && e.key === "ArrowUp") {
1134
+ e.preventDefault();
1135
+ navigateUp();
1136
+ }
1137
+ };
1138
+ window.addEventListener("keydown", handleKeyDown);
1139
+ return () => window.removeEventListener("keydown", handleKeyDown);
1140
+ }, [open, navigateUp]);
1094
1141
  const selectFile = (filePath, index, updateFolder = false) => {
1095
1142
  setFile(filePath);
1096
1143
  setCurrentFileIndex(index);
@@ -1112,6 +1159,10 @@ var EnhancedFileSelectorCombobox = ({
1112
1159
  }
1113
1160
  }
1114
1161
  }, [currentFiles, currentFile]);
1162
+ const recentFiles = useMemo2(() => {
1163
+ if (!showRecents) return [];
1164
+ return recentlyViewedFiles.filter((file2) => filteredFiles.includes(file2)).slice(0, 3);
1165
+ }, [showRecents, recentlyViewedFiles, filteredFiles]);
1115
1166
  const displayPath = currentFolder ?? "/";
1116
1167
  const shortDisplayPath = displayPath.length > 25 ? "..." + displayPath.slice(-22) : displayPath;
1117
1168
  const getDropdownWidth = () => {
@@ -1160,8 +1211,8 @@ var EnhancedFileSelectorCombobox = ({
1160
1211
  onValueChange: setSearchValue
1161
1212
  }
1162
1213
  ),
1163
- /* @__PURE__ */ jsx5("div", { className: "rf-px-3 rf-py-2 rf-border-t rf-border-b rf-border-gray-200 rf-bg-slate-50", children: /* @__PURE__ */ jsxs4("div", { className: "rf-flex rf-items-center rf-justify-between rf-text-xs rf-text-slate-600 rf-min-w-0 rf-overflow-hidden", children: [
1164
- /* @__PURE__ */ jsxs4("div", { className: "rf-flex rf-items-center rf-min-w-0 rf-flex-1", children: [
1214
+ /* @__PURE__ */ jsxs4("div", { className: "rf-px-3 rf-py-2 rf-border-t rf-border-b rf-border-gray-200 rf-bg-slate-50 rf-flex rf-items-center rf-justify-between rf-gap-2", children: [
1215
+ /* @__PURE__ */ jsx5("div", { className: "rf-flex rf-items-center rf-text-xs rf-text-slate-600 rf-min-w-0 rf-flex-1", children: /* @__PURE__ */ jsxs4("div", { className: "rf-flex rf-items-center rf-min-w-0", children: [
1165
1216
  /* @__PURE__ */ jsx5(
1166
1217
  "button",
1167
1218
  {
@@ -1199,22 +1250,65 @@ var EnhancedFileSelectorCombobox = ({
1199
1250
  pathToSegment
1200
1251
  );
1201
1252
  })
1202
- ] }),
1203
- currentFolder && /* @__PURE__ */ jsxs4(
1204
- "button",
1205
- {
1206
- onClick: navigateUp,
1207
- className: "rf-ml-2 rf-flex rf-items-center rf-gap-1 rf-text-blue-600 hover:rf-text-blue-800 rf-bg-transparent rf-border rf-border-blue-300 hover:rf-border-blue-500 rf-rounded rf-px-2 rf-py-1 rf-flex-shrink-0",
1208
- title: "Go up one level",
1209
- children: [
1210
- /* @__PURE__ */ jsx5(ArrowUp, { className: "rf-h-3 rf-w-3" }),
1211
- /* @__PURE__ */ jsx5("span", { children: "Up" })
1212
- ]
1213
- }
1214
- )
1215
- ] }) }),
1253
+ ] }) }),
1254
+ /* @__PURE__ */ jsxs4("div", { className: "rf-flex rf-items-center rf-gap-2 rf-flex-shrink-0", children: [
1255
+ /* @__PURE__ */ jsx5(
1256
+ "button",
1257
+ {
1258
+ onClick: () => setShowRecents(!showRecents),
1259
+ className: "rf-flex rf-items-center rf-gap-1 rf-text-slate-600 hover:rf-text-slate-800 rf-bg-transparent rf-border-none rf-p-0",
1260
+ title: showRecents ? "Hide recent files" : "Show recent files",
1261
+ children: showRecents ? /* @__PURE__ */ jsx5(Eye, { className: "rf-h-3.5 rf-w-3.5" }) : /* @__PURE__ */ jsx5(EyeOff, { className: "rf-h-3.5 rf-w-3.5" })
1262
+ }
1263
+ ),
1264
+ currentFolder && /* @__PURE__ */ jsxs4(
1265
+ "button",
1266
+ {
1267
+ onClick: navigateUp,
1268
+ className: "rf-flex rf-items-center rf-gap-1 rf-text-blue-600 hover:rf-text-blue-800 rf-bg-transparent rf-border rf-border-blue-300 hover:rf-border-blue-500 rf-rounded rf-px-2 rf-py-1",
1269
+ title: "Go up one level",
1270
+ children: [
1271
+ /* @__PURE__ */ jsx5(ArrowUp, { className: "rf-h-3 rf-w-3" }),
1272
+ /* @__PURE__ */ jsx5("span", { children: "Up" })
1273
+ ]
1274
+ }
1275
+ )
1276
+ ] })
1277
+ ] }),
1216
1278
  /* @__PURE__ */ jsx5(CommandList, { className: "rf-max-h-[70vh] rf-overflow-y-auto", children: !isSearching ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
1217
1279
  /* @__PURE__ */ jsx5(CommandEmpty, { children: emptyMessage }),
1280
+ recentFiles.length > 0 && /* @__PURE__ */ jsx5(
1281
+ CommandGroup,
1282
+ {
1283
+ heading: "Recent",
1284
+ className: "rf-border-b rf-border-gray-200 rf-pb-1 rf-bg-blue-50/30",
1285
+ children: recentFiles.map((path, index) => /* @__PURE__ */ jsxs4(
1286
+ CommandItem,
1287
+ {
1288
+ value: path,
1289
+ onSelect: () => selectFile(path, index, true),
1290
+ className: cn(
1291
+ path === currentFile && "rf-font-medium"
1292
+ ),
1293
+ children: [
1294
+ /* @__PURE__ */ jsx5(Clock, { className: "rf-mr-2 rf-h-4 rf-w-4 rf-text-blue-500" }),
1295
+ getDisplayName(path.split("/").pop() || ""),
1296
+ /* @__PURE__ */ jsx5("span", { className: "rf-text-xs rf-text-muted-foreground rf-ml-2 rf-truncate rf-max-w-[40%]", children: getDirectoryPath2(path) }),
1297
+ /* @__PURE__ */ jsx5(
1298
+ Check,
1299
+ {
1300
+ className: cn(
1301
+ "rf-ml-auto rf-h-4 rf-w-4",
1302
+ path === currentFile ? "rf-opacity-100" : "rf-opacity-0"
1303
+ )
1304
+ }
1305
+ )
1306
+ ]
1307
+ },
1308
+ path
1309
+ ))
1310
+ }
1311
+ ),
1218
1312
  pinnedFiles.length > 0 && /* @__PURE__ */ jsx5(
1219
1313
  CommandGroup,
1220
1314
  {
@@ -1226,10 +1320,24 @@ var EnhancedFileSelectorCombobox = ({
1226
1320
  value: path,
1227
1321
  onSelect: () => selectFile(path, index, true),
1228
1322
  className: cn(
1229
- path === currentFile && "rf-font-medium"
1323
+ path === currentFile && "rf-font-medium",
1324
+ "rf-group"
1230
1325
  ),
1231
1326
  children: [
1232
- /* @__PURE__ */ jsx5(Star, { className: "rf-mr-2 rf-h-4 rf-w-4 rf-text-amber-500 rf-fill-amber-500" }),
1327
+ onToggleFavorite && /* @__PURE__ */ jsx5(
1328
+ "button",
1329
+ {
1330
+ type: "button",
1331
+ onClick: (e) => {
1332
+ e.stopPropagation();
1333
+ onToggleFavorite(path);
1334
+ },
1335
+ className: "rf-mr-2 rf-p-0 rf-bg-transparent rf-border-none",
1336
+ "aria-label": "Remove from favorites",
1337
+ title: "Remove from favorites",
1338
+ children: /* @__PURE__ */ jsx5(Star, { className: "rf-h-4 rf-w-4 rf-text-amber-500 rf-fill-amber-500" })
1339
+ }
1340
+ ),
1233
1341
  getDisplayName(path.split("/").pop() || ""),
1234
1342
  /* @__PURE__ */ jsx5("span", { className: "rf-text-xs rf-text-muted-foreground rf-ml-2 rf-truncate rf-max-w-[40%]", children: getDirectoryPath2(path) }),
1235
1343
  /* @__PURE__ */ jsx5(
@@ -1490,12 +1598,12 @@ var RunFrameWithApi = (props) => {
1490
1598
  }));
1491
1599
  const hasReceivedInitialFiles = useHasReceivedInitialFilesLoaded();
1492
1600
  const fsMap = useRunFrameStore((s) => s.fsMap);
1493
- const allFiles = useMemo2(() => Array.from(fsMap.keys()), [fsMap]);
1494
- const projectConfigContent = useMemo2(() => {
1601
+ const allFiles = useMemo3(() => Array.from(fsMap.keys()), [fsMap]);
1602
+ const projectConfigContent = useMemo3(() => {
1495
1603
  const rawConfig = fsMap.get("tscircuit.config.json");
1496
1604
  return typeof rawConfig === "string" ? rawConfig : void 0;
1497
1605
  }, [fsMap]);
1498
- const boardFiles = useMemo2(
1606
+ const boardFiles = useMemo3(
1499
1607
  () => getBoardFilesFromConfig(allFiles, projectConfigContent),
1500
1608
  [allFiles, projectConfigContent]
1501
1609
  );
@@ -1511,7 +1619,7 @@ var RunFrameWithApi = (props) => {
1511
1619
  []
1512
1620
  );
1513
1621
  const isLoadingFiles = !hasReceivedInitialFiles;
1514
- const handleToggleFavorite = useCallback3(
1622
+ const handleToggleFavorite = useCallback4(
1515
1623
  (filePath) => {
1516
1624
  setFavorites(
1517
1625
  (prev) => prev.includes(filePath) ? prev.filter((f) => f !== filePath) : [...prev, filePath]
@@ -1549,7 +1657,7 @@ var RunFrameWithApi = (props) => {
1549
1657
  setComponentPath(firstMatch);
1550
1658
  }
1551
1659
  }, [boardFiles, props.initialMainComponentPath, componentPath]);
1552
- const updateFileHash = useCallback3((filePath) => {
1660
+ const updateFileHash = useCallback4((filePath) => {
1553
1661
  if (typeof window === "undefined") return;
1554
1662
  const params = new URLSearchParams(window.location.hash.slice(1));
1555
1663
  if (params.get("file") === filePath) return;
@@ -1585,7 +1693,7 @@ var RunFrameWithApi = (props) => {
1585
1693
  startPolling();
1586
1694
  return () => stopPolling();
1587
1695
  }, [startPolling, stopPolling]);
1588
- const componentProp = useMemo2(
1696
+ const componentProp = useMemo3(
1589
1697
  () => String(componentPath).length > 0 ? {
1590
1698
  mainComponentPath: componentPath
1591
1699
  } : {},
@@ -1665,7 +1773,7 @@ var RunFrameWithApi = (props) => {
1665
1773
  };
1666
1774
 
1667
1775
  // lib/components/RunFrameForCli/RunFrameForCli.tsx
1668
- import { useCallback as useCallback4, useState as useState7 } from "react";
1776
+ import { useCallback as useCallback5, useState as useState7 } from "react";
1669
1777
  import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1670
1778
  var RunFrameForCli = (props) => {
1671
1779
  const [shouldLoadLatestEval, setLoadLatestEval] = useLocalStorageState(
@@ -1677,7 +1785,7 @@ var RunFrameForCli = (props) => {
1677
1785
  const params = new URLSearchParams(window.location.hash.slice(1));
1678
1786
  return params.get("main_component") ?? void 0;
1679
1787
  });
1680
- const updateMainComponentHash = useCallback4((mainComponentPath) => {
1788
+ const updateMainComponentHash = useCallback5((mainComponentPath) => {
1681
1789
  if (typeof window === "undefined") return;
1682
1790
  const params = new URLSearchParams(window.location.hash.slice(1));
1683
1791
  if (params.get("main_component") === mainComponentPath) return;
@@ -2293,7 +2401,7 @@ var ImportComponentDialog = ({
2293
2401
  };
2294
2402
 
2295
2403
  // lib/components/RunFrameStaticBuildViewer/RunFrameStaticBuildViewer.tsx
2296
- import { useCallback as useCallback5, useEffect as useEffect9, useState as useState9 } from "react";
2404
+ import { useCallback as useCallback6, useEffect as useEffect9, useState as useState9 } from "react";
2297
2405
 
2298
2406
  // lib/components/RunFrameStaticBuildViewer/CircuitJsonFileSelectorCombobox.tsx
2299
2407
  import { jsx as jsx9 } from "react/jsx-runtime";
@@ -2333,7 +2441,7 @@ var RunFrameStaticBuildViewer = (props) => {
2333
2441
  const [failedFiles, setFailedFiles] = useState9(/* @__PURE__ */ new Set());
2334
2442
  const fileReferences = props.files;
2335
2443
  const availableFiles = fileReferences.map((f) => f.filePath);
2336
- const defaultFetchFile = useCallback5(
2444
+ const defaultFetchFile = useCallback6(
2337
2445
  async (fileRef) => {
2338
2446
  if (!fileRef.fileStaticAssetUrl) {
2339
2447
  throw new Error(
@@ -2350,7 +2458,7 @@ var RunFrameStaticBuildViewer = (props) => {
2350
2458
  },
2351
2459
  []
2352
2460
  );
2353
- const loadCircuitJsonFile = useCallback5(
2461
+ const loadCircuitJsonFile = useCallback6(
2354
2462
  async (filePath) => {
2355
2463
  if (fileCache[filePath]) {
2356
2464
  setCircuitJson(fileCache[filePath]);
@@ -2417,10 +2525,10 @@ var RunFrameStaticBuildViewer = (props) => {
2417
2525
  loadCircuitJsonFile(selectedPath);
2418
2526
  }
2419
2527
  }, [currentCircuitJsonPath]);
2420
- const handleFileChange = useCallback5((newPath) => {
2528
+ const handleFileChange = useCallback6((newPath) => {
2421
2529
  setCurrentCircuitJsonPath(newPath);
2422
2530
  }, []);
2423
- const retryFailedFile = useCallback5(
2531
+ const retryFailedFile = useCallback6(
2424
2532
  (filePath) => {
2425
2533
  setFailedFiles((prev) => {
2426
2534
  const newSet = new Set(prev);