shiplightai 0.1.71 → 0.1.73

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.
@@ -7352,6 +7352,22 @@ function ContextMenu({ x, y, items, onDismiss }) {
7352
7352
  }
7353
7353
  );
7354
7354
  }
7355
+ const EXPANDED_DIRS_KEY = "shp-debugger-expanded-dirs";
7356
+ function loadExpandedPaths() {
7357
+ try {
7358
+ const raw = localStorage.getItem(EXPANDED_DIRS_KEY);
7359
+ if (!raw) return /* @__PURE__ */ new Set();
7360
+ return new Set(JSON.parse(raw));
7361
+ } catch {
7362
+ return /* @__PURE__ */ new Set();
7363
+ }
7364
+ }
7365
+ function saveExpandedPaths(paths) {
7366
+ try {
7367
+ localStorage.setItem(EXPANDED_DIRS_KEY, JSON.stringify([...paths]));
7368
+ } catch {
7369
+ }
7370
+ }
7355
7371
  async function fetchDir(dir) {
7356
7372
  try {
7357
7373
  const res = await fetch(`/api/files?dir=${encodeURIComponent(dir)}`);
@@ -7380,7 +7396,7 @@ function updateNode(nodes, targetPath, updater) {
7380
7396
  });
7381
7397
  }
7382
7398
  function FileTreePanel(props) {
7383
- const { initialDir, expandToFile, sessions, focusedPath, onFocus, onOpen } = props;
7399
+ const { initialDir, expandToFile, sessions, focusedPath, onFocus, onOpen, onCollapse } = props;
7384
7400
  const [rootNodes, setRootNodes] = reactExports.useState([]);
7385
7401
  const [rootDir, setRootDir] = reactExports.useState(initialDir);
7386
7402
  const [error, setError] = reactExports.useState(null);
@@ -7392,6 +7408,20 @@ function FileTreePanel(props) {
7392
7408
  }
7393
7409
  return set;
7394
7410
  }, [sessions]);
7411
+ const expandedPathsRef = reactExports.useRef(loadExpandedPaths());
7412
+ const persistExpanded = reactExports.useCallback((path, expanded) => {
7413
+ const set = expandedPathsRef.current;
7414
+ if (expanded) {
7415
+ set.add(path);
7416
+ } else {
7417
+ set.delete(path);
7418
+ const prefix = path + "/";
7419
+ for (const p of set) {
7420
+ if (p.startsWith(prefix)) set.delete(p);
7421
+ }
7422
+ }
7423
+ saveExpandedPaths(set);
7424
+ }, []);
7395
7425
  reactExports.useEffect(() => {
7396
7426
  let cancelled = false;
7397
7427
  async function load() {
@@ -7410,6 +7440,25 @@ function FileTreePanel(props) {
7410
7440
  } else {
7411
7441
  setRootNodes(nodes);
7412
7442
  }
7443
+ const saved = expandedPathsRef.current;
7444
+ if (saved.size > 0 && !cancelled) {
7445
+ const sorted = [...saved].sort(
7446
+ (a, b) => a.split("/").length - b.split("/").length
7447
+ );
7448
+ for (const dirPath of sorted) {
7449
+ if (cancelled) break;
7450
+ const dirResp = await fetchDir(dirPath);
7451
+ if (!dirResp) {
7452
+ saved.delete(dirPath);
7453
+ continue;
7454
+ }
7455
+ const children = entriesToNodes(dirResp.entries);
7456
+ setRootNodes(
7457
+ (prev) => updateNode(prev, dirPath, (n) => ({ ...n, children, expanded: true }))
7458
+ );
7459
+ }
7460
+ saveExpandedPaths(saved);
7461
+ }
7413
7462
  }
7414
7463
  void load();
7415
7464
  return () => {
@@ -7423,22 +7472,25 @@ function FileTreePanel(props) {
7423
7472
  setRootNodes(
7424
7473
  (prev) => updateNode(prev, node.path, (n) => ({ ...n, children, expanded: true }))
7425
7474
  );
7426
- }, []);
7475
+ persistExpanded(node.path, true);
7476
+ }, [persistExpanded]);
7427
7477
  const toggleDir = reactExports.useCallback(
7428
7478
  (node) => {
7429
7479
  if (node.expanded) {
7430
7480
  setRootNodes(
7431
7481
  (prev) => updateNode(prev, node.path, (n) => ({ ...n, expanded: false }))
7432
7482
  );
7483
+ persistExpanded(node.path, false);
7433
7484
  } else if (node.children === null) {
7434
7485
  void expandDir(node);
7435
7486
  } else {
7436
7487
  setRootNodes(
7437
7488
  (prev) => updateNode(prev, node.path, (n) => ({ ...n, expanded: true }))
7438
7489
  );
7490
+ persistExpanded(node.path, true);
7439
7491
  }
7440
7492
  },
7441
- [expandDir]
7493
+ [expandDir, persistExpanded]
7442
7494
  );
7443
7495
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
7444
7496
  "div",
@@ -7452,20 +7504,49 @@ function FileTreePanel(props) {
7452
7504
  overflow: "hidden"
7453
7505
  },
7454
7506
  children: [
7455
- /* @__PURE__ */ jsxRuntimeExports.jsx(
7507
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
7456
7508
  "div",
7457
7509
  {
7458
7510
  style: {
7459
- padding: "8px 12px",
7511
+ display: "flex",
7512
+ alignItems: "center",
7513
+ height: 36,
7514
+ padding: "0 4px 0 12px",
7460
7515
  fontSize: 11,
7461
7516
  fontWeight: 600,
7462
7517
  textTransform: "uppercase",
7463
7518
  letterSpacing: 0.4,
7464
7519
  color: colors.textMuted,
7465
7520
  borderBottom: `1px solid ${colors.bgHover}`,
7466
- flexShrink: 0
7521
+ flexShrink: 0,
7522
+ boxSizing: "border-box"
7467
7523
  },
7468
- children: "Tests"
7524
+ children: [
7525
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { flex: 1 }, children: "Tests" }),
7526
+ onCollapse && /* @__PURE__ */ jsxRuntimeExports.jsx(
7527
+ "button",
7528
+ {
7529
+ type: "button",
7530
+ "aria-label": "Collapse sidebar",
7531
+ onClick: onCollapse,
7532
+ style: {
7533
+ width: 24,
7534
+ height: 24,
7535
+ display: "flex",
7536
+ alignItems: "center",
7537
+ justifyContent: "center",
7538
+ border: "none",
7539
+ background: "transparent",
7540
+ color: colors.textMuted,
7541
+ cursor: "pointer",
7542
+ padding: 0,
7543
+ borderRadius: 4,
7544
+ flexShrink: 0
7545
+ },
7546
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M10 3.5L5.5 8 10 12.5V3.5z" }) })
7547
+ }
7548
+ )
7549
+ ]
7469
7550
  }
7470
7551
  ),
7471
7552
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -7600,6 +7681,7 @@ function TreeRow({
7600
7681
  /* @__PURE__ */ jsxRuntimeExports.jsx(
7601
7682
  "span",
7602
7683
  {
7684
+ title: node.name,
7603
7685
  style: {
7604
7686
  flex: 1,
7605
7687
  whiteSpace: "nowrap",
@@ -7859,6 +7941,37 @@ function DebuggerShellApp() {
7859
7941
  const [focusedPath, setFocusedPath] = reactExports.useState(bootstrap.initialFile);
7860
7942
  const sessionsState = useSessions();
7861
7943
  const autoOpenedRef = reactExports.useRef(false);
7944
+ const [asideWidth, setAsideWidth] = reactExports.useState(280);
7945
+ const [isDragging, setIsDragging] = reactExports.useState(false);
7946
+ const [collapsed, setCollapsed] = reactExports.useState(false);
7947
+ const dragCleanupRef = reactExports.useRef(null);
7948
+ reactExports.useEffect(() => {
7949
+ return () => {
7950
+ var _a;
7951
+ (_a = dragCleanupRef.current) == null ? void 0 : _a.call(dragCleanupRef);
7952
+ };
7953
+ }, []);
7954
+ const onResizeStart = reactExports.useCallback((e) => {
7955
+ var _a;
7956
+ e.preventDefault();
7957
+ const startX = e.clientX;
7958
+ const startWidth = asideWidth;
7959
+ setIsDragging(true);
7960
+ const onMouseMove = (ev) => {
7961
+ const newWidth = Math.max(160, Math.min(600, startWidth + ev.clientX - startX));
7962
+ setAsideWidth(newWidth);
7963
+ };
7964
+ const cleanup = () => {
7965
+ setIsDragging(false);
7966
+ document.removeEventListener("mousemove", onMouseMove);
7967
+ document.removeEventListener("mouseup", cleanup);
7968
+ dragCleanupRef.current = null;
7969
+ };
7970
+ (_a = dragCleanupRef.current) == null ? void 0 : _a.call(dragCleanupRef);
7971
+ dragCleanupRef.current = cleanup;
7972
+ document.addEventListener("mousemove", onMouseMove);
7973
+ document.addEventListener("mouseup", cleanup);
7974
+ }, [asideWidth]);
7862
7975
  reactExports.useEffect(() => {
7863
7976
  let cancelled = false;
7864
7977
  (async () => {
@@ -7891,35 +8004,106 @@ function DebuggerShellApp() {
7891
8004
  height: "100%",
7892
8005
  width: "100%",
7893
8006
  color: colors.textBody,
7894
- fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif"
8007
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif",
8008
+ cursor: isDragging ? "col-resize" : void 0,
8009
+ userSelect: isDragging ? "none" : void 0
7895
8010
  },
7896
8011
  children: [
8012
+ isDragging && /* @__PURE__ */ jsxRuntimeExports.jsx(
8013
+ "div",
8014
+ {
8015
+ style: {
8016
+ position: "fixed",
8017
+ inset: 0,
8018
+ zIndex: 9999,
8019
+ cursor: "col-resize"
8020
+ }
8021
+ }
8022
+ ),
7897
8023
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
7898
8024
  "aside",
7899
8025
  {
7900
8026
  "data-testid": "file-tree-panel",
7901
8027
  style: {
7902
- width: 280,
8028
+ width: collapsed ? 36 : asideWidth,
7903
8029
  flexShrink: 0,
7904
8030
  height: "100%",
7905
- background: colors.bgPanel
8031
+ background: colors.bgPanel,
8032
+ position: "relative",
8033
+ overflow: "hidden",
8034
+ transition: isDragging ? "none" : "width 200ms ease"
7906
8035
  },
7907
8036
  children: [
7908
- resolvedInitialDir && /* @__PURE__ */ jsxRuntimeExports.jsx(
7909
- FileTreePanel,
8037
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
8038
+ "div",
7910
8039
  {
7911
- initialDir: resolvedInitialDir,
7912
- expandToFile: bootstrap.initialFile,
7913
- sessions: sessionsState.sessions,
7914
- focusedPath,
7915
- onFocus: setFocusedPath,
7916
- onOpen: (path) => {
7917
- setFocusedPath(path);
7918
- sessionsState.openSession(path);
7919
- }
8040
+ style: {
8041
+ position: "absolute",
8042
+ inset: 0,
8043
+ opacity: collapsed ? 1 : 0,
8044
+ pointerEvents: collapsed ? "auto" : "none",
8045
+ transition: "opacity 200ms ease"
8046
+ },
8047
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
8048
+ "button",
8049
+ {
8050
+ type: "button",
8051
+ "aria-label": "Expand sidebar",
8052
+ onClick: () => setCollapsed(false),
8053
+ style: toggleBtnStyle,
8054
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M6 3.5L10.5 8 6 12.5V3.5z" }) })
8055
+ }
8056
+ )
8057
+ }
8058
+ ),
8059
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
8060
+ "div",
8061
+ {
8062
+ style: {
8063
+ width: asideWidth,
8064
+ height: "100%",
8065
+ opacity: collapsed ? 0 : 1,
8066
+ pointerEvents: collapsed ? "none" : "auto",
8067
+ transition: "opacity 200ms ease"
8068
+ },
8069
+ children: [
8070
+ resolvedInitialDir && /* @__PURE__ */ jsxRuntimeExports.jsx(
8071
+ FileTreePanel,
8072
+ {
8073
+ initialDir: resolvedInitialDir,
8074
+ expandToFile: bootstrap.initialFile,
8075
+ sessions: sessionsState.sessions,
8076
+ focusedPath,
8077
+ onFocus: setFocusedPath,
8078
+ onOpen: (path) => {
8079
+ setFocusedPath(path);
8080
+ sessionsState.openSession(path);
8081
+ },
8082
+ onCollapse: () => setCollapsed(true)
8083
+ }
8084
+ ),
8085
+ !resolvedInitialDir && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { padding: 12, color: colors.textDim, fontSize: 13 }, children: "Loading file tree…" })
8086
+ ]
7920
8087
  }
7921
8088
  ),
7922
- !resolvedInitialDir && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { padding: 12, color: colors.textDim, fontSize: 13 }, children: "Loading file tree…" })
8089
+ !collapsed && /* @__PURE__ */ jsxRuntimeExports.jsx(
8090
+ "div",
8091
+ {
8092
+ "data-testid": "resize-handle",
8093
+ role: "separator",
8094
+ "aria-orientation": "vertical",
8095
+ onMouseDown: onResizeStart,
8096
+ style: {
8097
+ position: "absolute",
8098
+ top: 0,
8099
+ right: 0,
8100
+ width: 4,
8101
+ height: "100%",
8102
+ cursor: "col-resize",
8103
+ zIndex: 10
8104
+ }
8105
+ }
8106
+ )
7923
8107
  ]
7924
8108
  }
7925
8109
  ),
@@ -7967,6 +8151,20 @@ function DebuggerShellApp() {
7967
8151
  )
7968
8152
  ] });
7969
8153
  }
8154
+ const toggleBtnStyle = {
8155
+ width: 36,
8156
+ height: 36,
8157
+ display: "flex",
8158
+ alignItems: "center",
8159
+ justifyContent: "center",
8160
+ border: "none",
8161
+ background: "transparent",
8162
+ color: colors.textMuted,
8163
+ cursor: "pointer",
8164
+ padding: 0,
8165
+ borderBottom: `1px solid ${colors.bgHover}`,
8166
+ boxSizing: "border-box"
8167
+ };
7970
8168
  const globalCss = `
7971
8169
  html, body, #root { height: 100%; margin: 0; padding: 0; }
7972
8170
  * { box-sizing: border-box; }
@@ -7977,4 +8175,4 @@ const container = document.getElementById("root");
7977
8175
  if (container) {
7978
8176
  clientExports.createRoot(container).render(/* @__PURE__ */ jsxRuntimeExports.jsx(DebuggerShellApp, {}));
7979
8177
  }
7980
- //# sourceMappingURL=index-BQYw0-8-.js.map
8178
+ //# sourceMappingURL=index-CsPWUDqs.js.map
@@ -13,7 +13,7 @@
13
13
  html, body { margin: 0; height: 100%; background: #1a1b1e; color: #c1c2c5; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; }
14
14
  #root { height: 100%; }
15
15
  </style>
16
- <script type="module" crossorigin src="/assets/index-BQYw0-8-.js"></script>
16
+ <script type="module" crossorigin src="/assets/index-CsPWUDqs.js"></script>
17
17
  </head>
18
18
  <body>
19
19
  <div id="root"></div>