@ogpoyraz/wtx 0.8.6 → 0.8.7

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.
Files changed (2) hide show
  1. package/dist/cli.mjs +691 -242
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -25437,6 +25437,13 @@ function wrapText(text, width) {
25437
25437
  function isTapWithoutDrag(down, up) {
25438
25438
  return Math.abs(up.x - down.x) <= 1 && Math.abs(up.y - down.y) <= 1;
25439
25439
  }
25440
+ function clampSplitRatio(totalWidth, ratio, dividerWidth = DIVIDER_WIDTH) {
25441
+ if (totalWidth <= MIN_PANE_COLS * 2 + dividerWidth)
25442
+ return 0.5;
25443
+ const minRatio = MIN_PANE_COLS / totalWidth;
25444
+ const maxRatio = (totalWidth - MIN_PANE_COLS - dividerWidth) / totalWidth;
25445
+ return Math.max(minRatio, Math.min(maxRatio, ratio));
25446
+ }
25440
25447
  function mergeBlocks(prev, next, scope) {
25441
25448
  if (!scope)
25442
25449
  return sortBlocks(next);
@@ -25492,6 +25499,7 @@ function withCreatePlaceholders(blocks, creating) {
25492
25499
  return { ...block, rows: sortRowsHierarchically([...block.rows, ...placeholders]) };
25493
25500
  });
25494
25501
  }
25502
+ var MIN_PANE_COLS = 20, DIVIDER_WIDTH = 3;
25495
25503
  var init_utils = __esm(() => {
25496
25504
  init_stack();
25497
25505
  });
@@ -25600,14 +25608,19 @@ import { useRef as useRef2, useCallback as useCallback2 } from "react";
25600
25608
  function useTapHandler(onTap) {
25601
25609
  const down = useRef2(null);
25602
25610
  const onMouseDown = useCallback2((e) => {
25611
+ if (e.button !== 0)
25612
+ return;
25603
25613
  down.current = { x: e.x, y: e.y };
25604
25614
  }, []);
25605
25615
  const onMouseUp = useCallback2((e) => {
25606
25616
  const start = down.current;
25607
25617
  down.current = null;
25618
+ if (e.button !== 0)
25619
+ return;
25608
25620
  if (!start || !isTapWithoutDrag(start, e))
25609
25621
  return;
25610
- onTap();
25622
+ e.stopPropagation();
25623
+ onTap(e);
25611
25624
  }, [onTap]);
25612
25625
  return { onMouseDown, onMouseUp };
25613
25626
  }
@@ -25679,7 +25692,7 @@ async function openInBrowser(url2) {
25679
25692
 
25680
25693
  // src/tui/components/WorktreeTable.tsx
25681
25694
  import { useEffect as useEffect2, useRef as useRef3 } from "react";
25682
- import { jsx, jsxs, Fragment } from "@opentui/react/jsx-runtime";
25695
+ import { jsx, jsxs } from "@opentui/react/jsx-runtime";
25683
25696
  function statusBadge(row) {
25684
25697
  if (row.isMainCheckout)
25685
25698
  return { text: "[main]", fg: tokens.accent };
@@ -25693,11 +25706,27 @@ function statusBadge(row) {
25693
25706
  return { text: `dirty (${row.dirtyFiles.length})`, fg: tokens.warning };
25694
25707
  return { text: "clean", fg: tokens.dim };
25695
25708
  }
25696
- function WorktreeItem({ row, isSelected, isMultiSelected, indicator, frame, id }) {
25709
+ function WorktreeItem({
25710
+ row,
25711
+ isSelected,
25712
+ isMultiSelected,
25713
+ indicator,
25714
+ frame,
25715
+ id,
25716
+ onRowClick,
25717
+ onToggleSelect
25718
+ }) {
25697
25719
  const prTap = useTapHandler(() => {
25698
25720
  if (row.prUrl)
25699
25721
  openInBrowser(row.prUrl);
25700
25722
  });
25723
+ const rowTap = useTapHandler(() => {
25724
+ onRowClick?.();
25725
+ });
25726
+ const gutterTap = useTapHandler((e) => {
25727
+ e.stopPropagation();
25728
+ onToggleSelect?.();
25729
+ });
25701
25730
  const badge = indicator ? indicator.running ? { text: `${frame} ${indicator.verb}…`, fg: tokens.accent } : { text: `◌ ${indicator.verb}`, fg: tokens.dim } : row.isPendingCreate ? { text: `${frame} creating…`, fg: tokens.accent } : statusBadge(row);
25702
25731
  const disabled = indicator !== undefined || row.isPendingCreate === true;
25703
25732
  const primary = isSelected ? tokens.bright : disabled ? tokens.dim : tokens.fg;
@@ -25714,98 +25743,134 @@ function WorktreeItem({ row, isSelected, isMultiSelected, indicator, frame, id }
25714
25743
  ownerSegment,
25715
25744
  baseSegment
25716
25745
  ].filter(Boolean).join(" ").trimEnd();
25746
+ const isClickable = !row.isPendingCreate && !indicator;
25717
25747
  return /* @__PURE__ */ jsxs("box", {
25718
25748
  id,
25719
25749
  flexDirection: "column",
25720
25750
  backgroundColor: isSelected ? tokens.selectionBg : undefined,
25721
25751
  style: { paddingRight: 1 },
25752
+ ...isClickable && onRowClick ? rowTap : {},
25722
25753
  children: [
25723
- /* @__PURE__ */ jsxs("text", {
25754
+ /* @__PURE__ */ jsxs("box", {
25755
+ flexDirection: "row",
25724
25756
  children: [
25725
- /* @__PURE__ */ jsx("span", {
25726
- fg: primary,
25727
- children: isSelected ? "▸ " : " "
25728
- }),
25729
- /* @__PURE__ */ jsx("span", {
25730
- fg: tokens.accent,
25731
- children: isMultiSelected ? "✓ " : " "
25732
- }),
25733
- /* @__PURE__ */ jsx("span", {
25734
- fg: tokens.dim,
25735
- children: hierarchyPrefix
25736
- }),
25737
- /* @__PURE__ */ jsx("span", {
25738
- fg: primary,
25739
- children: truncateBranch(row.branch)
25757
+ /* @__PURE__ */ jsx("text", {
25758
+ children: /* @__PURE__ */ jsx("span", {
25759
+ fg: primary,
25760
+ children: isSelected ? "▸ " : " "
25761
+ })
25740
25762
  }),
25741
- /* @__PURE__ */ jsx("span", {
25742
- fg: badge.fg,
25743
- children: ` ${badge.text}`
25744
- })
25745
- ]
25746
- }),
25747
- /* @__PURE__ */ jsxs("text", {
25748
- ...row.prUrl ? prTap : {},
25749
- children: [
25750
- /* @__PURE__ */ jsx("span", {
25751
- fg: tokens.dim,
25752
- children: secondary
25763
+ /* @__PURE__ */ jsx("box", {
25764
+ width: 2,
25765
+ ...onToggleSelect ? gutterTap : {},
25766
+ children: /* @__PURE__ */ jsx("text", {
25767
+ fg: tokens.accent,
25768
+ children: isMultiSelected ? "✓ " : " "
25769
+ })
25753
25770
  }),
25754
- row.prNumber !== null && /* @__PURE__ */ jsxs(Fragment, {
25771
+ /* @__PURE__ */ jsxs("text", {
25755
25772
  children: [
25756
25773
  /* @__PURE__ */ jsx("span", {
25757
25774
  fg: tokens.dim,
25758
- children: secondary ? " · " : ""
25775
+ children: hierarchyPrefix
25759
25776
  }),
25760
25777
  /* @__PURE__ */ jsx("span", {
25761
- fg: tokens.accent,
25762
- children: `#${row.prNumber}`
25763
- }),
25764
- row.prState && /* @__PURE__ */ jsx("span", {
25765
- fg: tokens.dim,
25766
- children: ` ${row.prState}`
25778
+ fg: primary,
25779
+ children: truncateBranch(row.branch)
25767
25780
  }),
25768
- row.prChecks && /* @__PURE__ */ jsx("span", {
25781
+ /* @__PURE__ */ jsx("span", {
25782
+ fg: badge.fg,
25783
+ children: ` ${badge.text}`
25784
+ })
25785
+ ]
25786
+ })
25787
+ ]
25788
+ }),
25789
+ /* @__PURE__ */ jsxs("box", {
25790
+ flexDirection: "row",
25791
+ children: [
25792
+ /* @__PURE__ */ jsxs("text", {
25793
+ children: [
25794
+ /* @__PURE__ */ jsx("span", {
25769
25795
  fg: tokens.dim,
25770
- children: ` (${row.prChecks})`
25796
+ children: secondary
25771
25797
  }),
25772
- row.prUrl && /* @__PURE__ */ jsx("span", {
25798
+ row.prNumber !== null && secondary ? /* @__PURE__ */ jsx("span", {
25773
25799
  fg: tokens.dim,
25774
- children: " "
25775
- })
25800
+ children: " · "
25801
+ }) : null
25776
25802
  ]
25777
25803
  }),
25778
- baseChangedSegment && /* @__PURE__ */ jsx("span", {
25779
- fg: tokens.warning,
25780
- children: baseChangedSegment
25804
+ row.prNumber !== null && /* @__PURE__ */ jsx("box", {
25805
+ ...row.prUrl ? prTap : {},
25806
+ children: /* @__PURE__ */ jsxs("text", {
25807
+ children: [
25808
+ /* @__PURE__ */ jsx("span", {
25809
+ fg: tokens.accent,
25810
+ children: `#${row.prNumber}`
25811
+ }),
25812
+ row.prState && /* @__PURE__ */ jsx("span", {
25813
+ fg: tokens.dim,
25814
+ children: ` ${row.prState}`
25815
+ }),
25816
+ row.prChecks && /* @__PURE__ */ jsx("span", {
25817
+ fg: tokens.dim,
25818
+ children: ` (${row.prChecks})`
25819
+ }),
25820
+ row.prUrl && /* @__PURE__ */ jsx("span", {
25821
+ fg: tokens.dim,
25822
+ children: " ↗"
25823
+ })
25824
+ ]
25825
+ })
25781
25826
  }),
25782
- rebaseSegment && /* @__PURE__ */ jsx("span", {
25783
- fg: tokens.error,
25784
- children: rebaseSegment
25827
+ /* @__PURE__ */ jsxs("text", {
25828
+ children: [
25829
+ baseChangedSegment && /* @__PURE__ */ jsx("span", {
25830
+ fg: tokens.warning,
25831
+ children: baseChangedSegment
25832
+ }),
25833
+ rebaseSegment && /* @__PURE__ */ jsx("span", {
25834
+ fg: tokens.error,
25835
+ children: rebaseSegment
25836
+ })
25837
+ ]
25785
25838
  })
25786
25839
  ]
25787
25840
  })
25788
25841
  ]
25789
25842
  });
25790
25843
  }
25791
- function WorktreeTable({ blocks, selectedIndex, selection = new Set, frame, repoVerbs, rowVerbs }) {
25844
+ function WorktreeTable({ blocks, selectedIndex, selection = new Set, frame, repoVerbs, rowVerbs, onRowClick, onToggleSelect }) {
25792
25845
  const scrollRef = useRef3(null);
25793
25846
  useEffect2(() => {
25794
25847
  if (scrollRef.current?.scrollChildIntoView) {
25795
25848
  scrollRef.current.scrollChildIntoView("selected-row");
25796
25849
  }
25797
25850
  }, [selectedIndex]);
25798
- let flatIndex = 0;
25851
+ const flatIndexMap = new Map;
25852
+ let idx = 0;
25853
+ for (const b of blocks) {
25854
+ for (const r of b.rows) {
25855
+ if (!r.isPendingCreate)
25856
+ flatIndexMap.set(r.path, idx++);
25857
+ }
25858
+ }
25859
+ let headerSeen = 0;
25799
25860
  return /* @__PURE__ */ jsx("scrollbox", {
25800
25861
  ref: scrollRef,
25801
25862
  id: "worktree-table",
25802
- flexGrow: 2,
25863
+ flexGrow: 1,
25864
+ width: "100%",
25803
25865
  height: "100%",
25804
25866
  border: true,
25805
25867
  borderColor: tokens.border,
25806
25868
  title: "Worktrees",
25807
25869
  paddingX: 1,
25808
25870
  focused: false,
25871
+ onMouseScroll: (e) => {
25872
+ e.stopPropagation();
25873
+ },
25809
25874
  children: blocks.length === 0 ? /* @__PURE__ */ jsx("text", {
25810
25875
  fg: tokens.dim,
25811
25876
  children: "No repositories configured."
@@ -25816,7 +25881,7 @@ function WorktreeTable({ blocks, selectedIndex, selection = new Set, frame, repo
25816
25881
  style: { marginBottom: 1 },
25817
25882
  children: [
25818
25883
  /* @__PURE__ */ jsx("box", {
25819
- style: { marginTop: flatIndex === 0 ? 0 : 1 },
25884
+ style: { marginTop: headerSeen === 0 ? 0 : 1 },
25820
25885
  children: /* @__PURE__ */ jsxs("text", {
25821
25886
  children: [
25822
25887
  /* @__PURE__ */ jsx("span", {
@@ -25837,18 +25902,23 @@ function WorktreeTable({ blocks, selectedIndex, selection = new Set, frame, repo
25837
25902
  ]
25838
25903
  })
25839
25904
  }),
25905
+ (() => {
25906
+ headerSeen++;
25907
+ return null;
25908
+ })(),
25840
25909
  block.rows.map((row) => {
25841
25910
  const navigable = !row.isPendingCreate;
25842
- const isSelected = navigable && flatIndex === selectedIndex;
25843
- if (navigable)
25844
- flatIndex++;
25911
+ const flatIdx = flatIndexMap.get(row.path);
25912
+ const isSelected = navigable && flatIdx === selectedIndex;
25845
25913
  return /* @__PURE__ */ jsx(WorktreeItem, {
25846
25914
  row,
25847
25915
  isSelected,
25848
25916
  isMultiSelected: selection.has(row.path),
25849
25917
  indicator: rowVerbs.get(row.path),
25850
25918
  frame,
25851
- id: isSelected ? "selected-row" : undefined
25919
+ id: isSelected ? "selected-row" : undefined,
25920
+ onRowClick: navigable && flatIdx !== undefined && onRowClick ? () => onRowClick(flatIdx) : undefined,
25921
+ onToggleSelect: onToggleSelect ? () => onToggleSelect(row.path) : undefined
25852
25922
  }, row.path);
25853
25923
  })
25854
25924
  ]
@@ -25863,7 +25933,7 @@ var init_WorktreeTable = __esm(() => {
25863
25933
  });
25864
25934
 
25865
25935
  // src/tui/components/DetailPane.tsx
25866
- import { jsx as jsx2, jsxs as jsxs2, Fragment as Fragment2 } from "@opentui/react/jsx-runtime";
25936
+ import { jsx as jsx2, jsxs as jsxs2, Fragment } from "@opentui/react/jsx-runtime";
25867
25937
  function DetailPane({ selectedRow }) {
25868
25938
  const prTap = useTapHandler(() => {
25869
25939
  if (selectedRow?.prUrl)
@@ -25873,6 +25943,7 @@ function DetailPane({ selectedRow }) {
25873
25943
  return /* @__PURE__ */ jsx2("box", {
25874
25944
  id: "detail-pane",
25875
25945
  flexGrow: 1,
25946
+ width: "100%",
25876
25947
  height: "100%",
25877
25948
  border: true,
25878
25949
  borderColor: tokens.border,
@@ -25911,6 +25982,7 @@ function DetailPane({ selectedRow }) {
25911
25982
  return /* @__PURE__ */ jsxs2("scrollbox", {
25912
25983
  id: "detail-pane",
25913
25984
  flexGrow: 1,
25985
+ width: "100%",
25914
25986
  height: "100%",
25915
25987
  border: true,
25916
25988
  borderColor: tokens.border,
@@ -26083,7 +26155,7 @@ function DetailPane({ selectedRow }) {
26083
26155
  })
26084
26156
  ]
26085
26157
  }),
26086
- prNumber !== null && /* @__PURE__ */ jsxs2(Fragment2, {
26158
+ prNumber !== null && /* @__PURE__ */ jsxs2(Fragment, {
26087
26159
  children: [
26088
26160
  /* @__PURE__ */ jsxs2("text", {
26089
26161
  style: { marginTop: 1 },
@@ -26179,7 +26251,7 @@ function DetailPane({ selectedRow }) {
26179
26251
  fg: tokens.dim,
26180
26252
  children: "Actions:"
26181
26253
  }),
26182
- !isMainCheckout && /* @__PURE__ */ jsxs2(Fragment2, {
26254
+ !isMainCheckout && /* @__PURE__ */ jsxs2(Fragment, {
26183
26255
  children: [
26184
26256
  /* @__PURE__ */ jsxs2("text", {
26185
26257
  fg: tokens.dim,
@@ -26215,8 +26287,44 @@ var init_DetailPane = __esm(() => {
26215
26287
 
26216
26288
  // src/tui/components/Footer.tsx
26217
26289
  import { jsx as jsx3, jsxs as jsxs3 } from "@opentui/react/jsx-runtime";
26218
- function Footer({ loading, lastRefreshed, errorCount, message, busyText, spinnerFrame, filter }) {
26219
- const hints = HINTS.map(([key, action]) => `${key} ${action}`).join(" · ");
26290
+ function HintItem({ hintKey, label, isFirst, onClick }) {
26291
+ const tap = useTapHandler(() => onClick?.(hintKey));
26292
+ return /* @__PURE__ */ jsx3("box", {
26293
+ flexDirection: "row",
26294
+ ...onClick ? tap : {},
26295
+ children: /* @__PURE__ */ jsxs3("text", {
26296
+ children: [
26297
+ /* @__PURE__ */ jsx3("span", {
26298
+ fg: tokens.dim,
26299
+ children: isFirst ? "" : " · "
26300
+ }),
26301
+ /* @__PURE__ */ jsx3("span", {
26302
+ fg: tokens.dim,
26303
+ children: `${hintKey} ${label}`
26304
+ })
26305
+ ]
26306
+ })
26307
+ });
26308
+ }
26309
+ function ErrorBadge({ count: count2, onClick }) {
26310
+ const tap = useTapHandler(() => onClick?.());
26311
+ return /* @__PURE__ */ jsx3("box", {
26312
+ ...onClick ? tap : {},
26313
+ children: /* @__PURE__ */ jsxs3("text", {
26314
+ children: [
26315
+ /* @__PURE__ */ jsx3("span", {
26316
+ fg: tokens.error,
26317
+ children: `${count2} error${count2 !== 1 ? "s" : ""}`
26318
+ }),
26319
+ /* @__PURE__ */ jsx3("span", {
26320
+ fg: tokens.dim,
26321
+ children: " · e view"
26322
+ })
26323
+ ]
26324
+ })
26325
+ });
26326
+ }
26327
+ function Footer({ loading, lastRefreshed, errorCount, message, busyText, spinnerFrame, filter, onHintClick, onErrorClick }) {
26220
26328
  const frame = spinnerFrame ?? "◌";
26221
26329
  return /* @__PURE__ */ jsxs3("box", {
26222
26330
  id: "footer",
@@ -26232,9 +26340,15 @@ function Footer({ loading, lastRefreshed, errorCount, message, busyText, spinner
26232
26340
  busyText ? /* @__PURE__ */ jsx3("text", {
26233
26341
  fg: tokens.accent,
26234
26342
  children: `${frame} ${busyText}`
26235
- }) : /* @__PURE__ */ jsx3("text", {
26236
- fg: tokens.dim,
26237
- children: hints
26343
+ }) : /* @__PURE__ */ jsx3("box", {
26344
+ flexDirection: "row",
26345
+ gap: 1,
26346
+ children: HINTS.map(([key, label], idx) => /* @__PURE__ */ jsx3(HintItem, {
26347
+ hintKey: key,
26348
+ label,
26349
+ isFirst: idx === 0,
26350
+ onClick: onHintClick
26351
+ }, key))
26238
26352
  }),
26239
26353
  /* @__PURE__ */ jsxs3("box", {
26240
26354
  flexDirection: "row",
@@ -26256,17 +26370,9 @@ function Footer({ loading, lastRefreshed, errorCount, message, busyText, spinner
26256
26370
  ")"
26257
26371
  ]
26258
26372
  }) : null,
26259
- errorCount > 0 ? /* @__PURE__ */ jsxs3("text", {
26260
- children: [
26261
- /* @__PURE__ */ jsx3("span", {
26262
- fg: tokens.error,
26263
- children: `${errorCount} error${errorCount !== 1 ? "s" : ""}`
26264
- }),
26265
- /* @__PURE__ */ jsx3("span", {
26266
- fg: tokens.dim,
26267
- children: " · e view"
26268
- })
26269
- ]
26373
+ errorCount > 0 ? /* @__PURE__ */ jsx3(ErrorBadge, {
26374
+ count: errorCount,
26375
+ onClick: onErrorClick
26270
26376
  }) : null,
26271
26377
  /* @__PURE__ */ jsx3("text", {
26272
26378
  fg: tokens.dim,
@@ -26280,6 +26386,7 @@ function Footer({ loading, lastRefreshed, errorCount, message, busyText, spinner
26280
26386
  var HINTS;
26281
26387
  var init_Footer = __esm(() => {
26282
26388
  init_theme();
26389
+ init_use_tap();
26283
26390
  HINTS = [
26284
26391
  ["c", "config"],
26285
26392
  ["n", "create"],
@@ -26297,10 +26404,76 @@ var init_Footer = __esm(() => {
26297
26404
  ];
26298
26405
  });
26299
26406
 
26300
- // src/tui/components/Overlay.tsx
26407
+ // src/tui/components/Divider.tsx
26408
+ import { useState as useState2, useRef as useRef4, useCallback as useCallback3 } from "react";
26409
+ import { useRenderer } from "@opentui/react";
26301
26410
  import { jsx as jsx4 } from "@opentui/react/jsx-runtime";
26302
- function Overlay({ title, borderColor = tokens.border, width = 64, children }) {
26411
+ function Divider({ splitRatio, totalWidth, onChange, onDraggingChange }) {
26412
+ const renderer = useRenderer();
26413
+ const [hovered, setHovered] = useState2(false);
26414
+ const [dragging, setDragging] = useState2(false);
26415
+ const startXRef = useRef4(null);
26416
+ const startRatioRef = useRef4(splitRatio);
26417
+ const handleMouseDown = useCallback3((e) => {
26418
+ if (e.button !== 0)
26419
+ return;
26420
+ startXRef.current = e.x;
26421
+ startRatioRef.current = splitRatio;
26422
+ setDragging(true);
26423
+ onDraggingChange?.(true);
26424
+ renderer.clearSelection();
26425
+ e.stopPropagation();
26426
+ }, [splitRatio, onDraggingChange, renderer]);
26427
+ const handleMouseUp = useCallback3((e) => {
26428
+ if (e.button !== 0)
26429
+ return;
26430
+ startXRef.current = null;
26431
+ setDragging(false);
26432
+ onDraggingChange?.(false);
26433
+ e.stopPropagation();
26434
+ }, [onDraggingChange]);
26435
+ const handleMouseDrag = useCallback3((e) => {
26436
+ if (startXRef.current === null)
26437
+ return;
26438
+ renderer.clearSelection();
26439
+ const dx = e.x - startXRef.current;
26440
+ const next = clampSplitRatio(totalWidth, startRatioRef.current + dx / totalWidth);
26441
+ onChange(next);
26442
+ e.stopPropagation();
26443
+ }, [totalWidth, onChange, renderer]);
26444
+ const dragHandlers = {
26445
+ onMouseDown: handleMouseDown,
26446
+ onMouseUp: handleMouseUp,
26447
+ onMouseDrag: handleMouseDrag,
26448
+ onMouseMove: handleMouseDrag
26449
+ };
26303
26450
  return /* @__PURE__ */ jsx4("box", {
26451
+ width: 3,
26452
+ height: "100%",
26453
+ backgroundColor: dragging ? tokens.selectionBg : hovered ? tokens.panelBg : undefined,
26454
+ ...dragHandlers,
26455
+ onMouseOver: () => setHovered(true),
26456
+ onMouseOut: () => setHovered(false),
26457
+ alignItems: "center",
26458
+ justifyContent: "center",
26459
+ children: /* @__PURE__ */ jsx4("text", {
26460
+ fg: dragging ? tokens.accent : hovered ? tokens.borderActive : tokens.border,
26461
+ ...dragHandlers,
26462
+ onMouseOver: () => setHovered(true),
26463
+ onMouseOut: () => setHovered(false),
26464
+ children: " │ "
26465
+ })
26466
+ });
26467
+ }
26468
+ var init_Divider = __esm(() => {
26469
+ init_theme();
26470
+ init_utils();
26471
+ });
26472
+
26473
+ // src/tui/components/Overlay.tsx
26474
+ import { jsx as jsx5 } from "@opentui/react/jsx-runtime";
26475
+ function Overlay({ title, borderColor = tokens.border, width = 64, children }) {
26476
+ return /* @__PURE__ */ jsx5("box", {
26304
26477
  id: "overlay-scrim",
26305
26478
  position: "absolute",
26306
26479
  width: "100%",
@@ -26312,7 +26485,7 @@ function Overlay({ title, borderColor = tokens.border, width = 64, children }) {
26312
26485
  flexDirection: "row",
26313
26486
  justifyContent: "center",
26314
26487
  alignItems: "center",
26315
- children: /* @__PURE__ */ jsx4("box", {
26488
+ children: /* @__PURE__ */ jsx5("box", {
26316
26489
  id: "overlay-panel",
26317
26490
  width,
26318
26491
  border: true,
@@ -26332,34 +26505,34 @@ var init_Overlay = __esm(() => {
26332
26505
  });
26333
26506
 
26334
26507
  // src/tui/components/HelpOverlay.tsx
26335
- import { jsx as jsx5, jsxs as jsxs4 } from "@opentui/react/jsx-runtime";
26508
+ import { jsx as jsx6, jsxs as jsxs4 } from "@opentui/react/jsx-runtime";
26336
26509
  function HelpOverlay() {
26337
26510
  return /* @__PURE__ */ jsxs4(Overlay, {
26338
26511
  title: "Help",
26339
26512
  borderColor: tokens.border,
26340
26513
  children: [
26341
- /* @__PURE__ */ jsx5("box", {
26514
+ /* @__PURE__ */ jsx6("box", {
26342
26515
  flexDirection: "column",
26343
26516
  style: { maxHeight: 40 },
26344
- children: /* @__PURE__ */ jsx5("scrollbox", {
26517
+ children: /* @__PURE__ */ jsx6("scrollbox", {
26345
26518
  flexGrow: 1,
26346
26519
  children: HELP_ENTRIES.map(([key, desc]) => /* @__PURE__ */ jsxs4("text", {
26347
26520
  children: [
26348
- /* @__PURE__ */ jsx5("span", {
26521
+ /* @__PURE__ */ jsx6("span", {
26349
26522
  fg: tokens.accent,
26350
26523
  children: key.padEnd(10)
26351
26524
  }),
26352
- /* @__PURE__ */ jsx5("span", {
26525
+ /* @__PURE__ */ jsx6("span", {
26353
26526
  children: " - "
26354
26527
  }),
26355
- /* @__PURE__ */ jsx5("span", {
26528
+ /* @__PURE__ */ jsx6("span", {
26356
26529
  children: desc
26357
26530
  })
26358
26531
  ]
26359
26532
  }, key))
26360
26533
  })
26361
26534
  }),
26362
- /* @__PURE__ */ jsx5("text", {
26535
+ /* @__PURE__ */ jsx6("text", {
26363
26536
  style: { marginTop: 1, fg: tokens.dim },
26364
26537
  children: "Press any key to close"
26365
26538
  })
@@ -26398,9 +26571,11 @@ var init_HelpOverlay = __esm(() => {
26398
26571
  });
26399
26572
 
26400
26573
  // src/tui/components/ConfirmModal.tsx
26401
- import { jsx as jsx6, jsxs as jsxs5 } from "@opentui/react/jsx-runtime";
26402
- function ConfirmModal({ title, message }) {
26403
- return /* @__PURE__ */ jsx6(Overlay, {
26574
+ import { jsx as jsx7, jsxs as jsxs5 } from "@opentui/react/jsx-runtime";
26575
+ function ConfirmModal({ title, message, onConfirm, onCancel }) {
26576
+ const yesTap = useTapHandler(() => onConfirm?.());
26577
+ const noTap = useTapHandler(() => onCancel?.());
26578
+ return /* @__PURE__ */ jsx7(Overlay, {
26404
26579
  title,
26405
26580
  borderColor: tokens.warning,
26406
26581
  children: /* @__PURE__ */ jsxs5("box", {
@@ -26408,25 +26583,42 @@ function ConfirmModal({ title, message }) {
26408
26583
  justifyContent: "center",
26409
26584
  alignItems: "center",
26410
26585
  children: [
26411
- /* @__PURE__ */ jsx6("text", {
26586
+ /* @__PURE__ */ jsx7("text", {
26412
26587
  children: message
26413
26588
  }),
26414
- /* @__PURE__ */ jsxs5("text", {
26589
+ /* @__PURE__ */ jsxs5("box", {
26590
+ flexDirection: "row",
26591
+ gap: 2,
26415
26592
  style: { marginTop: 2 },
26416
26593
  children: [
26417
- /* @__PURE__ */ jsx6("span", {
26418
- fg: tokens.dim,
26419
- children: "[y] "
26420
- }),
26421
- /* @__PURE__ */ jsx6("span", {
26422
- children: "Yes "
26423
- }),
26424
- /* @__PURE__ */ jsx6("span", {
26425
- fg: tokens.dim,
26426
- children: "[n/esc] "
26594
+ /* @__PURE__ */ jsx7("box", {
26595
+ ...onConfirm ? yesTap : {},
26596
+ children: /* @__PURE__ */ jsxs5("text", {
26597
+ children: [
26598
+ /* @__PURE__ */ jsx7("span", {
26599
+ fg: tokens.dim,
26600
+ children: "[y] "
26601
+ }),
26602
+ /* @__PURE__ */ jsx7("span", {
26603
+ fg: tokens.success,
26604
+ children: "Yes"
26605
+ })
26606
+ ]
26607
+ })
26427
26608
  }),
26428
- /* @__PURE__ */ jsx6("span", {
26429
- children: "Cancel"
26609
+ /* @__PURE__ */ jsx7("box", {
26610
+ ...onCancel ? noTap : {},
26611
+ children: /* @__PURE__ */ jsxs5("text", {
26612
+ children: [
26613
+ /* @__PURE__ */ jsx7("span", {
26614
+ fg: tokens.dim,
26615
+ children: "[n/esc] "
26616
+ }),
26617
+ /* @__PURE__ */ jsx7("span", {
26618
+ children: "Cancel"
26619
+ })
26620
+ ]
26621
+ })
26430
26622
  })
26431
26623
  ]
26432
26624
  })
@@ -26437,6 +26629,7 @@ function ConfirmModal({ title, message }) {
26437
26629
  var init_ConfirmModal = __esm(() => {
26438
26630
  init_Overlay();
26439
26631
  init_theme();
26632
+ init_use_tap();
26440
26633
  });
26441
26634
 
26442
26635
  // src/tui/actions.ts
@@ -26497,20 +26690,20 @@ async function runWtxAction(args, onLine) {
26497
26690
  }
26498
26691
 
26499
26692
  // src/tui/components/ActionLogModal.tsx
26500
- import { jsx as jsx7, jsxs as jsxs6 } from "@opentui/react/jsx-runtime";
26693
+ import { jsx as jsx8, jsxs as jsxs6 } from "@opentui/react/jsx-runtime";
26501
26694
  function ActionLogModal({ title, lines, done, exitCode, remaining }) {
26502
26695
  return /* @__PURE__ */ jsxs6(Overlay, {
26503
26696
  title,
26504
26697
  borderColor: tokens.border,
26505
26698
  children: [
26506
- /* @__PURE__ */ jsx7("box", {
26699
+ /* @__PURE__ */ jsx8("box", {
26507
26700
  flexGrow: 1,
26508
26701
  flexDirection: "column",
26509
26702
  style: { minHeight: 10, maxHeight: 20 },
26510
- children: /* @__PURE__ */ jsx7("scrollbox", {
26703
+ children: /* @__PURE__ */ jsx8("scrollbox", {
26511
26704
  flexGrow: 1,
26512
26705
  stickyScroll: true,
26513
- children: lines.map((line, i2) => /* @__PURE__ */ jsx7("text", {
26706
+ children: lines.map((line, i2) => /* @__PURE__ */ jsx8("text", {
26514
26707
  fg: line.type === "err" ? tokens.error : tokens.fg,
26515
26708
  children: line.text
26516
26709
  }, i2))
@@ -26521,11 +26714,11 @@ function ActionLogModal({ title, lines, done, exitCode, remaining }) {
26521
26714
  flexDirection: "row",
26522
26715
  justifyContent: "center",
26523
26716
  children: [
26524
- !done && /* @__PURE__ */ jsx7("text", {
26717
+ !done && /* @__PURE__ */ jsx8("text", {
26525
26718
  fg: tokens.warning,
26526
26719
  children: "Running..."
26527
26720
  }),
26528
- done && exitCode === 0 && /* @__PURE__ */ jsx7("text", {
26721
+ done && exitCode === 0 && /* @__PURE__ */ jsx8("text", {
26529
26722
  fg: tokens.success,
26530
26723
  children: "✓ Done (exit 0)"
26531
26724
  }),
@@ -26538,11 +26731,11 @@ function ActionLogModal({ title, lines, done, exitCode, remaining }) {
26538
26731
  exitCode
26539
26732
  ]
26540
26733
  }),
26541
- remaining !== undefined && remaining > 0 && /* @__PURE__ */ jsx7("span", {
26734
+ remaining !== undefined && remaining > 0 && /* @__PURE__ */ jsx8("span", {
26542
26735
  fg: tokens.warning,
26543
26736
  children: ` · ${remaining} more failure${remaining > 1 ? "s" : ""}`
26544
26737
  }),
26545
- /* @__PURE__ */ jsx7("span", {
26738
+ /* @__PURE__ */ jsx8("span", {
26546
26739
  fg: tokens.dim,
26547
26740
  children: " - press any key to close"
26548
26741
  })
@@ -26629,8 +26822,8 @@ var init_history = __esm(() => {
26629
26822
  });
26630
26823
 
26631
26824
  // src/tui/components/HistoryOverlay.tsx
26632
- import { useEffect as useEffect3, useState as useState2 } from "react";
26633
- import { jsx as jsx8, jsxs as jsxs7 } from "@opentui/react/jsx-runtime";
26825
+ import { useEffect as useEffect3, useState as useState3 } from "react";
26826
+ import { jsx as jsx9, jsxs as jsxs7 } from "@opentui/react/jsx-runtime";
26634
26827
  function formatShortTime(iso) {
26635
26828
  const d = new Date(iso);
26636
26829
  if (Number.isNaN(d.getTime()))
@@ -26652,7 +26845,7 @@ function HistoryEntryLine({ entry }) {
26652
26845
  prefix
26653
26846
  ]
26654
26847
  }),
26655
- duration3 && /* @__PURE__ */ jsx8("span", {
26848
+ duration3 && /* @__PURE__ */ jsx9("span", {
26656
26849
  fg: tokens.dim,
26657
26850
  children: duration3
26658
26851
  })
@@ -26669,7 +26862,7 @@ function HistoryEntryLine({ entry }) {
26669
26862
  prefix
26670
26863
  ]
26671
26864
  }),
26672
- duration3 && /* @__PURE__ */ jsx8("span", {
26865
+ duration3 && /* @__PURE__ */ jsx9("span", {
26673
26866
  fg: tokens.dim,
26674
26867
  children: duration3
26675
26868
  })
@@ -26678,11 +26871,11 @@ function HistoryEntryLine({ entry }) {
26678
26871
  }
26679
26872
  return /* @__PURE__ */ jsxs7("text", {
26680
26873
  children: [
26681
- /* @__PURE__ */ jsx8("span", {
26874
+ /* @__PURE__ */ jsx9("span", {
26682
26875
  fg: tokens.error,
26683
26876
  children: "✗ "
26684
26877
  }),
26685
- /* @__PURE__ */ jsx8("span", {
26878
+ /* @__PURE__ */ jsx9("span", {
26686
26879
  children: prefix
26687
26880
  }),
26688
26881
  /* @__PURE__ */ jsxs7("span", {
@@ -26698,7 +26891,7 @@ function HistoryEntryLine({ entry }) {
26698
26891
  });
26699
26892
  }
26700
26893
  function HistoryOverlay() {
26701
- const [entries, setEntries] = useState2([]);
26894
+ const [entries, setEntries] = useState3([]);
26702
26895
  useEffect3(() => {
26703
26896
  setEntries(readRecentHistory(500));
26704
26897
  }, []);
@@ -26707,23 +26900,23 @@ function HistoryOverlay() {
26707
26900
  borderColor: tokens.border,
26708
26901
  width: 110,
26709
26902
  children: [
26710
- /* @__PURE__ */ jsx8("box", {
26903
+ /* @__PURE__ */ jsx9("box", {
26711
26904
  flexGrow: 1,
26712
26905
  flexDirection: "column",
26713
26906
  style: { minHeight: 30, maxHeight: 46 },
26714
- children: entries.length === 0 ? /* @__PURE__ */ jsx8("text", {
26907
+ children: entries.length === 0 ? /* @__PURE__ */ jsx9("text", {
26715
26908
  fg: tokens.dim,
26716
26909
  children: "No actions recorded yet."
26717
- }) : /* @__PURE__ */ jsx8("scrollbox", {
26910
+ }) : /* @__PURE__ */ jsx9("scrollbox", {
26718
26911
  flexGrow: 1,
26719
- children: entries.map((entry, i2) => /* @__PURE__ */ jsx8(HistoryEntryLine, {
26912
+ children: entries.map((entry, i2) => /* @__PURE__ */ jsx9(HistoryEntryLine, {
26720
26913
  entry
26721
26914
  }, i2))
26722
26915
  })
26723
26916
  }),
26724
- /* @__PURE__ */ jsx8("box", {
26917
+ /* @__PURE__ */ jsx9("box", {
26725
26918
  marginTop: 1,
26726
- children: /* @__PURE__ */ jsx8("text", {
26919
+ children: /* @__PURE__ */ jsx9("text", {
26727
26920
  fg: tokens.dim,
26728
26921
  children: "Press any key to close · newest first"
26729
26922
  })
@@ -26738,28 +26931,28 @@ var init_HistoryOverlay = __esm(() => {
26738
26931
  });
26739
26932
 
26740
26933
  // src/tui/components/InputModal.tsx
26741
- import { useState as useState3 } from "react";
26742
- import { jsx as jsx9, jsxs as jsxs8 } from "@opentui/react/jsx-runtime";
26934
+ import { useState as useState4 } from "react";
26935
+ import { jsx as jsx10, jsxs as jsxs8 } from "@opentui/react/jsx-runtime";
26743
26936
  function InputModal({ title, placeholder, initialValue, errorMessage: errorMessage2, onSubmit }) {
26744
- const [value, setValue] = useState3(initialValue ?? "");
26745
- return /* @__PURE__ */ jsx9(Overlay, {
26937
+ const [value, setValue] = useState4(initialValue ?? "");
26938
+ return /* @__PURE__ */ jsx10(Overlay, {
26746
26939
  title,
26747
26940
  borderColor: tokens.accent,
26748
26941
  children: /* @__PURE__ */ jsxs8("box", {
26749
26942
  flexDirection: "column",
26750
26943
  children: [
26751
- /* @__PURE__ */ jsx9("input", {
26944
+ /* @__PURE__ */ jsx10("input", {
26752
26945
  focused: true,
26753
26946
  value: initialValue ?? "",
26754
26947
  placeholder,
26755
26948
  onInput: (val) => setValue(val),
26756
26949
  onSubmit: () => onSubmit(value)
26757
26950
  }),
26758
- errorMessage2 ? /* @__PURE__ */ jsx9("text", {
26951
+ errorMessage2 ? /* @__PURE__ */ jsx10("text", {
26759
26952
  fg: tokens.error,
26760
26953
  style: { marginTop: 1 },
26761
26954
  children: errorMessage2
26762
- }) : /* @__PURE__ */ jsx9("text", {
26955
+ }) : /* @__PURE__ */ jsx10("text", {
26763
26956
  fg: tokens.dim,
26764
26957
  style: { marginTop: 1 },
26765
26958
  children: "Press Enter to submit, empty to cancel"
@@ -26774,11 +26967,31 @@ var init_InputModal = __esm(() => {
26774
26967
  });
26775
26968
 
26776
26969
  // src/tui/components/ChoiceModal.tsx
26777
- import { useState as useState4 } from "react";
26970
+ import { useState as useState5 } from "react";
26778
26971
  import { useKeyboard } from "@opentui/react";
26779
- import { jsx as jsx10, jsxs as jsxs9 } from "@opentui/react/jsx-runtime";
26972
+ import { jsx as jsx11, jsxs as jsxs9 } from "@opentui/react/jsx-runtime";
26973
+ function ChoiceOptionRow({ option, index, isSelected, onSelect }) {
26974
+ const tap = useTapHandler(() => onSelect(option.value));
26975
+ return /* @__PURE__ */ jsxs9("box", {
26976
+ flexDirection: "column",
26977
+ backgroundColor: isSelected ? tokens.selectionBg : undefined,
26978
+ ...tap,
26979
+ children: [
26980
+ /* @__PURE__ */ jsx11("text", {
26981
+ children: /* @__PURE__ */ jsx11("span", {
26982
+ fg: isSelected ? tokens.bright : tokens.fg,
26983
+ children: `${index + 1}. ${option.label}`
26984
+ })
26985
+ }),
26986
+ option.desc && /* @__PURE__ */ jsx11("text", {
26987
+ fg: tokens.dim,
26988
+ children: ` ${option.desc}`
26989
+ })
26990
+ ]
26991
+ });
26992
+ }
26780
26993
  function ChoiceModal({ title, options, initialIndex = 0, onSubmit, onCancel }) {
26781
- const [index, setIndex] = useState4(Math.min(Math.max(initialIndex, 0), options.length - 1));
26994
+ const [index, setIndex] = useState5(Math.min(Math.max(initialIndex, 0), options.length - 1));
26782
26995
  useKeyboard((key) => {
26783
26996
  if (key.name === "escape") {
26784
26997
  onCancel();
@@ -26805,27 +27018,17 @@ function ChoiceModal({ title, options, initialIndex = 0, onSubmit, onCancel }) {
26805
27018
  onSubmit(option.value);
26806
27019
  }
26807
27020
  });
26808
- return /* @__PURE__ */ jsx10(Overlay, {
27021
+ return /* @__PURE__ */ jsx11(Overlay, {
26809
27022
  title,
26810
27023
  borderColor: tokens.accent,
26811
27024
  children: /* @__PURE__ */ jsxs9("box", {
26812
27025
  flexDirection: "column",
26813
27026
  children: [
26814
- options.map((option, i2) => /* @__PURE__ */ jsxs9("box", {
26815
- flexDirection: "column",
26816
- backgroundColor: i2 === index ? tokens.selectionBg : undefined,
26817
- children: [
26818
- /* @__PURE__ */ jsx10("text", {
26819
- children: /* @__PURE__ */ jsx10("span", {
26820
- fg: i2 === index ? tokens.bright : tokens.fg,
26821
- children: `${i2 + 1}. ${option.label}`
26822
- })
26823
- }),
26824
- option.desc && /* @__PURE__ */ jsx10("text", {
26825
- fg: tokens.dim,
26826
- children: ` ${option.desc}`
26827
- })
26828
- ]
27027
+ options.map((option, i2) => /* @__PURE__ */ jsx11(ChoiceOptionRow, {
27028
+ option,
27029
+ index: i2,
27030
+ isSelected: i2 === index,
27031
+ onSelect: onSubmit
26829
27032
  }, option.value)),
26830
27033
  /* @__PURE__ */ jsxs9("text", {
26831
27034
  fg: tokens.dim,
@@ -26833,7 +27036,7 @@ function ChoiceModal({ title, options, initialIndex = 0, onSubmit, onCancel }) {
26833
27036
  children: [
26834
27037
  "↑/↓ to choose · Enter to confirm · 1-",
26835
27038
  options.length,
26836
- " quick pick · Esc to cancel"
27039
+ " quick pick · Esc to cancel · click to select"
26837
27040
  ]
26838
27041
  })
26839
27042
  ]
@@ -26843,18 +27046,19 @@ function ChoiceModal({ title, options, initialIndex = 0, onSubmit, onCancel }) {
26843
27046
  var init_ChoiceModal = __esm(() => {
26844
27047
  init_Overlay();
26845
27048
  init_theme();
27049
+ init_use_tap();
26846
27050
  });
26847
27051
 
26848
27052
  // src/tui/components/ConfigOverlay.tsx
26849
- import { useState as useState5, useEffect as useEffect4 } from "react";
27053
+ import { useState as useState6, useEffect as useEffect4 } from "react";
26850
27054
  import { useKeyboard as useKeyboard2 } from "@opentui/react";
26851
- import { jsx as jsx11, jsxs as jsxs10, Fragment as Fragment3 } from "@opentui/react/jsx-runtime";
27055
+ import { jsx as jsx12, jsxs as jsxs10, Fragment as Fragment2 } from "@opentui/react/jsx-runtime";
26852
27056
  function ConfigOverlay({ onClose, onSaved, onError }) {
26853
- const [config2, setConfig] = useState5(null);
26854
- const [viewState, setViewState] = useState5({ type: "main" });
26855
- const [selectedIndex, setSelectedIndex] = useState5(1);
26856
- const [windowStart, setWindowStart] = useState5(0);
26857
- const [editState, setEditState] = useState5({ type: "none" });
27057
+ const [config2, setConfig] = useState6(null);
27058
+ const [viewState, setViewState] = useState6({ type: "main" });
27059
+ const [selectedIndex, setSelectedIndex] = useState6(1);
27060
+ const [windowStart, setWindowStart] = useState6(0);
27061
+ const [editState, setEditState] = useState6({ type: "none" });
26858
27062
  useEffect4(() => {
26859
27063
  try {
26860
27064
  setConfig(loadConfig());
@@ -27111,9 +27315,9 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
27111
27315
  hint = "Remove this repository from config";
27112
27316
  }
27113
27317
  }
27114
- return /* @__PURE__ */ jsxs10(Fragment3, {
27318
+ return /* @__PURE__ */ jsxs10(Fragment2, {
27115
27319
  children: [
27116
- /* @__PURE__ */ jsx11(Overlay, {
27320
+ /* @__PURE__ */ jsx12(Overlay, {
27117
27321
  title: "Configuration",
27118
27322
  borderColor: tokens.border,
27119
27323
  children: /* @__PURE__ */ jsxs10("box", {
@@ -27121,7 +27325,7 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
27121
27325
  width: "100%",
27122
27326
  height: 24,
27123
27327
  children: [
27124
- /* @__PURE__ */ jsx11("box", {
27328
+ /* @__PURE__ */ jsx12("box", {
27125
27329
  flexDirection: "column",
27126
27330
  flexGrow: 1,
27127
27331
  overflow: "hidden",
@@ -27129,7 +27333,7 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
27129
27333
  const i2 = windowStart + idx;
27130
27334
  const isSelected = i2 === selectedIndex;
27131
27335
  if (row.type === "header") {
27132
- return /* @__PURE__ */ jsx11("text", {
27336
+ return /* @__PURE__ */ jsx12("text", {
27133
27337
  fg: tokens.dim,
27134
27338
  style: { marginTop: i2 === 0 ? 0 : 1 },
27135
27339
  children: row.label
@@ -27171,7 +27375,7 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
27171
27375
  labelText
27172
27376
  ]
27173
27377
  }),
27174
- valText && /* @__PURE__ */ jsx11("text", {
27378
+ valText && /* @__PURE__ */ jsx12("text", {
27175
27379
  fg: isSelected ? tokens.accent : tokens.dim,
27176
27380
  bg: isSelected ? tokens.selectionBg : undefined,
27177
27381
  flexShrink: 1,
@@ -27181,14 +27385,14 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
27181
27385
  }, i2);
27182
27386
  })
27183
27387
  }),
27184
- /* @__PURE__ */ jsx11("box", {
27388
+ /* @__PURE__ */ jsx12("box", {
27185
27389
  flexDirection: "row",
27186
27390
  border: true,
27187
27391
  borderColor: tokens.border,
27188
27392
  paddingTop: 1,
27189
27393
  marginTop: 1,
27190
27394
  minHeight: 3,
27191
- children: /* @__PURE__ */ jsx11("text", {
27395
+ children: /* @__PURE__ */ jsx12("text", {
27192
27396
  fg: tokens.dim,
27193
27397
  children: hint
27194
27398
  })
@@ -27196,14 +27400,14 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
27196
27400
  ]
27197
27401
  })
27198
27402
  }),
27199
- editState.type === "input" && /* @__PURE__ */ jsx11(InputModal, {
27403
+ editState.type === "input" && /* @__PURE__ */ jsx12(InputModal, {
27200
27404
  title: editState.title,
27201
27405
  initialValue: editState.currentValue,
27202
27406
  placeholder: "Enter value...",
27203
27407
  errorMessage: editState.error,
27204
27408
  onSubmit: handleInputSubmit
27205
27409
  }),
27206
- editState.type === "confirm_remove" && /* @__PURE__ */ jsx11(ConfirmModal, {
27410
+ editState.type === "confirm_remove" && /* @__PURE__ */ jsx12(ConfirmModal, {
27207
27411
  title: "Remove Repo",
27208
27412
  message: `Remove repo ${editState.repo} from config?`
27209
27413
  })
@@ -27220,26 +27424,26 @@ var init_ConfigOverlay = __esm(() => {
27220
27424
  });
27221
27425
 
27222
27426
  // src/tui/components/WarningsOverlay.tsx
27223
- import { jsx as jsx12, jsxs as jsxs11 } from "@opentui/react/jsx-runtime";
27427
+ import { jsx as jsx13, jsxs as jsxs11 } from "@opentui/react/jsx-runtime";
27224
27428
  function WarningsOverlay({ warnings }) {
27225
27429
  return /* @__PURE__ */ jsxs11(Overlay, {
27226
27430
  title: `Warnings (${warnings.length})`,
27227
27431
  borderColor: tokens.warning,
27228
27432
  children: [
27229
- /* @__PURE__ */ jsx12("box", {
27433
+ /* @__PURE__ */ jsx13("box", {
27230
27434
  flexDirection: "column",
27231
27435
  style: { maxHeight: 30 },
27232
- children: /* @__PURE__ */ jsx12("scrollbox", {
27436
+ children: /* @__PURE__ */ jsx13("scrollbox", {
27233
27437
  flexGrow: 1,
27234
27438
  children: warnings.map((warning, i2) => /* @__PURE__ */ jsxs11("box", {
27235
27439
  flexDirection: "column",
27236
27440
  style: { marginBottom: 1 },
27237
27441
  children: [
27238
- /* @__PURE__ */ jsx12("text", {
27442
+ /* @__PURE__ */ jsx13("text", {
27239
27443
  fg: tokens.warning,
27240
27444
  children: `⚠ ${warning.repoName}`
27241
27445
  }),
27242
- wrapText(warning.message, 56).map((line, j) => /* @__PURE__ */ jsx12("text", {
27446
+ wrapText(warning.message, 56).map((line, j) => /* @__PURE__ */ jsx13("text", {
27243
27447
  fg: tokens.fg,
27244
27448
  children: ` ${line}`
27245
27449
  }, j))
@@ -27247,7 +27451,7 @@ function WarningsOverlay({ warnings }) {
27247
27451
  }, i2))
27248
27452
  })
27249
27453
  }),
27250
- /* @__PURE__ */ jsx12("text", {
27454
+ /* @__PURE__ */ jsx13("text", {
27251
27455
  style: { marginTop: 1, fg: tokens.dim },
27252
27456
  children: "Press any key to close"
27253
27457
  })
@@ -27261,9 +27465,9 @@ var init_WarningsOverlay = __esm(() => {
27261
27465
  });
27262
27466
 
27263
27467
  // src/tui/hooks/useSpinnerFrame.ts
27264
- import { useEffect as useEffect5, useState as useState6 } from "react";
27468
+ import { useEffect as useEffect5, useState as useState7 } from "react";
27265
27469
  function useSpinnerFrame(active) {
27266
- const [index, setIndex] = useState6(0);
27470
+ const [index, setIndex] = useState7(0);
27267
27471
  useEffect5(() => {
27268
27472
  if (!active)
27269
27473
  return;
@@ -27278,10 +27482,10 @@ var init_useSpinnerFrame = __esm(() => {
27278
27482
  });
27279
27483
 
27280
27484
  // src/tui/components/App.tsx
27281
- import { useState as useState7, useEffect as useEffect6, useMemo, useRef as useRef4, useCallback as useCallback3 } from "react";
27485
+ import { useState as useState8, useEffect as useEffect6, useMemo, useRef as useRef5, useCallback as useCallback4 } from "react";
27282
27486
  import { existsSync as existsSync4 } from "node:fs";
27283
- import { useKeyboard as useKeyboard3, useRenderer, useSelectionHandler } from "@opentui/react";
27284
- import { jsx as jsx13, jsxs as jsxs12 } from "@opentui/react/jsx-runtime";
27487
+ import { useKeyboard as useKeyboard3, useRenderer as useRenderer2, useSelectionHandler, useTerminalDimensions } from "@opentui/react";
27488
+ import { jsx as jsx14, jsxs as jsxs12 } from "@opentui/react/jsx-runtime";
27285
27489
  function getWorktreePathFor(repoName, branch) {
27286
27490
  try {
27287
27491
  const config2 = loadConfig();
@@ -27292,31 +27496,39 @@ function getWorktreePathFor(repoName, branch) {
27292
27496
  }
27293
27497
  }
27294
27498
  function App({ opts }) {
27295
- const renderer = useRenderer();
27499
+ const renderer = useRenderer2();
27296
27500
  const { blocks, loading, refreshing, error: error52, warnings, lastRefreshed, pendingRepos, refresh } = useWorktrees(opts);
27297
- const [selectedIndex, setSelectedIndex] = useState7(0);
27298
- const [modal, setModal] = useState7({ type: "none" });
27299
- const [actionMessage, setActionMessage] = useState7();
27300
- const messageTimer = useRef4(null);
27301
- const [ops, setOps] = useState7([]);
27302
- const [failedLogs, setFailedLogs] = useState7([]);
27303
- const nextOpId = useRef4(1);
27304
- const [createModal, setCreateModal] = useState7(false);
27305
- const [createError, setCreateError] = useState7();
27306
- const [createBaseModal, setCreateBaseModal] = useState7(null);
27307
- const [createBaseError, setCreateBaseError] = useState7();
27308
- const [createDepsChoice, setCreateDepsChoice] = useState7(null);
27309
- const [pullPrModal, setPullPrModal] = useState7(false);
27310
- const [pullPrError, setPullPrError] = useState7();
27311
- const [pullForceChoice, setPullForceChoice] = useState7(null);
27312
- const [renameModal, setRenameModal] = useState7(false);
27313
- const [renameError, setRenameError] = useState7();
27314
- const [configOpen, setConfigOpen] = useState7(false);
27315
- const [refreshScopes, setRefreshScopes] = useState7([]);
27316
- const [filterText, setFilterText] = useState7("");
27317
- const [isFiltering, setIsFiltering] = useState7(false);
27318
- const [selection, setSelection] = useState7(new Set);
27319
- const doRefresh = useCallback3(async (scope) => {
27501
+ const [selectedIndex, setSelectedIndex] = useState8(0);
27502
+ const [modal, setModal] = useState8({ type: "none" });
27503
+ const [actionMessage, setActionMessage] = useState8();
27504
+ const messageTimer = useRef5(null);
27505
+ const [ops, setOps] = useState8([]);
27506
+ const [failedLogs, setFailedLogs] = useState8([]);
27507
+ const nextOpId = useRef5(1);
27508
+ const [createModal, setCreateModal] = useState8(false);
27509
+ const [createError, setCreateError] = useState8();
27510
+ const [createBaseModal, setCreateBaseModal] = useState8(null);
27511
+ const [createBaseError, setCreateBaseError] = useState8();
27512
+ const [createDepsChoice, setCreateDepsChoice] = useState8(null);
27513
+ const [pullPrModal, setPullPrModal] = useState8(false);
27514
+ const [pullPrError, setPullPrError] = useState8();
27515
+ const [pullForceChoice, setPullForceChoice] = useState8(null);
27516
+ const [renameModal, setRenameModal] = useState8(false);
27517
+ const [renameError, setRenameError] = useState8();
27518
+ const [configOpen, setConfigOpen] = useState8(false);
27519
+ const [refreshScopes, setRefreshScopes] = useState8([]);
27520
+ const [filterText, setFilterText] = useState8("");
27521
+ const [isFiltering, setIsFiltering] = useState8(false);
27522
+ const [selection, setSelection] = useState8(new Set);
27523
+ const [splitRatio, setSplitRatio] = useState8(0.6);
27524
+ const [isResizing, setIsResizing] = useState8(false);
27525
+ const { width: termWidth } = useTerminalDimensions();
27526
+ const totalWidth = termWidth || renderer.width || 80;
27527
+ useEffect6(() => {
27528
+ if (termWidth)
27529
+ setSplitRatio((prev) => clampSplitRatio(termWidth, prev));
27530
+ }, [termWidth]);
27531
+ const doRefresh = useCallback4(async (scope) => {
27320
27532
  const targets = scope ?? [
27321
27533
  ...new Set([...blocks.map((b) => b.repoName), ...pendingRepos])
27322
27534
  ];
@@ -27332,13 +27544,17 @@ function App({ opts }) {
27332
27544
  setModal({ type: "error", message: error52 });
27333
27545
  }
27334
27546
  }, [error52]);
27335
- const flash = useCallback3((message, ms = 3000) => {
27547
+ const flash = useCallback4((message, ms = 3000) => {
27336
27548
  if (messageTimer.current)
27337
27549
  clearTimeout(messageTimer.current);
27338
27550
  setActionMessage(message);
27339
27551
  messageTimer.current = setTimeout(() => setActionMessage(undefined), ms);
27340
27552
  }, []);
27341
- const copySelectedText = useCallback3((warnOnEmpty) => {
27553
+ const copySelectedText = useCallback4((warnOnEmpty) => {
27554
+ if (isResizing) {
27555
+ renderer.clearSelection();
27556
+ return;
27557
+ }
27342
27558
  const text = renderer.getSelection()?.getSelectedText() ?? "";
27343
27559
  if (!text) {
27344
27560
  if (warnOnEmpty)
@@ -27346,7 +27562,7 @@ function App({ opts }) {
27346
27562
  return;
27347
27563
  }
27348
27564
  copyTextToClipboard(renderer, text).then((ok) => flash(ok ? `Copied ${text.length} character${text.length !== 1 ? "s" : ""}` : "Copy failed"));
27349
- }, [renderer, flash]);
27565
+ }, [renderer, flash, isResizing]);
27350
27566
  useSelectionHandler(() => copySelectedText(false));
27351
27567
  const busyRepos = useMemo(() => new Set(ops.flatMap((o2) => o2.repoNames)), [ops]);
27352
27568
  const busyRowPaths = useMemo(() => new Set(ops.map((o2) => o2.rowPath).filter((p) => p !== undefined)), [ops]);
@@ -27508,6 +27724,159 @@ function App({ opts }) {
27508
27724
  args.push("--force");
27509
27725
  executeOp(op, args, [repoName]);
27510
27726
  };
27727
+ const handleHintClick = useCallback4((key) => {
27728
+ if (key === "c") {
27729
+ setConfigOpen(true);
27730
+ return;
27731
+ }
27732
+ if (key === "?") {
27733
+ setModal({ type: "help" });
27734
+ return;
27735
+ }
27736
+ if (key === "H") {
27737
+ setModal({ type: "history" });
27738
+ return;
27739
+ }
27740
+ if (key === "r") {
27741
+ doRefresh();
27742
+ return;
27743
+ }
27744
+ if (key === "e" && warnings.length > 0) {
27745
+ setModal({ type: "warnings" });
27746
+ return;
27747
+ }
27748
+ if (key === "n") {
27749
+ if (!selectedRow)
27750
+ return;
27751
+ setCreateModal(true);
27752
+ setCreateError(undefined);
27753
+ return;
27754
+ }
27755
+ if (key === "m") {
27756
+ const target = selectedRow;
27757
+ if (!target || target.isMainCheckout || !target.branch || target.branch === "(detached)") {
27758
+ if (target?.isMainCheckout)
27759
+ flash("Cannot rename main checkout");
27760
+ else if (target && (!target.branch || target.branch === "(detached)"))
27761
+ flash("Cannot rename detached worktree");
27762
+ return;
27763
+ }
27764
+ const conflict = findConflict([target]);
27765
+ if (conflict) {
27766
+ flash(conflict);
27767
+ return;
27768
+ }
27769
+ setRenameModal(true);
27770
+ setRenameError(undefined);
27771
+ return;
27772
+ }
27773
+ if (key === "f") {
27774
+ const targets = getSelectedRows();
27775
+ if (targets.length > 0)
27776
+ startFetch(targets);
27777
+ return;
27778
+ }
27779
+ if (key === "o") {
27780
+ const targets = getSelectedRows();
27781
+ if (targets.length === 0)
27782
+ return;
27783
+ if (targets.length > 1) {
27784
+ flash("Cannot open multiple worktrees");
27785
+ return;
27786
+ }
27787
+ const target = targets[0];
27788
+ const conflict = findConflict(targets);
27789
+ if (conflict) {
27790
+ flash(conflict);
27791
+ return;
27792
+ }
27793
+ const op = {
27794
+ id: nextOpId.current++,
27795
+ kind: "open",
27796
+ repoNames: [target.repoName],
27797
+ rowPath: target.path,
27798
+ label: target.branch,
27799
+ title: `Open ${target.branch}`,
27800
+ status: "queued",
27801
+ lines: []
27802
+ };
27803
+ setOps((prev) => [...prev, op]);
27804
+ executeOp(op, ["open", target.branch, "--repo", target.repoName], null);
27805
+ return;
27806
+ }
27807
+ if (key === "i") {
27808
+ const targets = getSelectedRows();
27809
+ if (targets.length === 0)
27810
+ return;
27811
+ const conflict = findConflict(targets);
27812
+ if (conflict) {
27813
+ flash(conflict);
27814
+ return;
27815
+ }
27816
+ startBatchActions("install", targets, (r) => r.isMainCheckout ? ["deps", "--repo", r.repoName, "--install"] : ["deps", r.branch, "--repo", r.repoName, "--install"]);
27817
+ return;
27818
+ }
27819
+ if (key === "p") {
27820
+ const targets = getSelectedRows();
27821
+ if (targets.length === 0)
27822
+ return;
27823
+ const conflict = findConflict(targets);
27824
+ if (conflict) {
27825
+ flash(conflict);
27826
+ return;
27827
+ }
27828
+ startBatchActions("pull", targets, (r) => ["pull-branch", r.branch, "--repo", r.repoName]);
27829
+ return;
27830
+ }
27831
+ if (key === "b") {
27832
+ const targets = getSelectedRows();
27833
+ if (targets.length === 0)
27834
+ return;
27835
+ if (targets.some((r) => r.isMainCheckout)) {
27836
+ flash("Cannot rebase main checkout");
27837
+ return;
27838
+ }
27839
+ const conflict = findConflict(targets);
27840
+ if (conflict) {
27841
+ flash(conflict);
27842
+ return;
27843
+ }
27844
+ setModal({ type: "confirm_rebase", rows: targets });
27845
+ return;
27846
+ }
27847
+ if (key === "d") {
27848
+ const targets = getSelectedRows();
27849
+ if (targets.length === 0)
27850
+ return;
27851
+ if (targets.some((r) => r.isMainCheckout)) {
27852
+ flash("Cannot remove main checkout");
27853
+ return;
27854
+ }
27855
+ const conflict = findConflict(targets);
27856
+ if (conflict) {
27857
+ flash(conflict);
27858
+ return;
27859
+ }
27860
+ setModal({ type: "confirm_remove", rows: targets });
27861
+ return;
27862
+ }
27863
+ if (key === "s") {
27864
+ const targets = getSelectedRows();
27865
+ if (targets.length === 0)
27866
+ return;
27867
+ if (targets.some((r) => r.isMainCheckout)) {
27868
+ flash("Cannot sync main checkout");
27869
+ return;
27870
+ }
27871
+ const conflict = findConflict(targets);
27872
+ if (conflict) {
27873
+ flash(conflict);
27874
+ return;
27875
+ }
27876
+ setModal({ type: "confirm_sync", rows: targets });
27877
+ return;
27878
+ }
27879
+ }, [selectedRow, selection, warnings, busyRepos, busyRowPaths, doRefresh, flash, blocks]);
27511
27880
  useKeyboard3((key) => {
27512
27881
  if (isFiltering) {
27513
27882
  if (key.name === "escape") {
@@ -27848,6 +28217,9 @@ function App({ opts }) {
27848
28217
  })();
27849
28218
  }
27850
28219
  });
28220
+ const rawLeft = Math.floor(totalWidth * splitRatio);
28221
+ const leftCols = Math.max(20, Math.min(totalWidth - 20 - DIVIDER_WIDTH, rawLeft));
28222
+ const rightCols = Math.max(20, totalWidth - leftCols - DIVIDER_WIDTH);
27851
28223
  return /* @__PURE__ */ jsxs12("box", {
27852
28224
  flexDirection: "column",
27853
28225
  width: "100%",
@@ -27858,16 +28230,34 @@ function App({ opts }) {
27858
28230
  width: "100%",
27859
28231
  flexGrow: 1,
27860
28232
  children: [
27861
- /* @__PURE__ */ jsx13(WorktreeTable, {
27862
- blocks: displayBlocks,
27863
- selectedIndex,
27864
- selection,
27865
- frame: spinnerFrame,
27866
- repoVerbs,
27867
- rowVerbs
28233
+ /* @__PURE__ */ jsx14("box", {
28234
+ width: leftCols,
28235
+ height: "100%",
28236
+ flexDirection: "column",
28237
+ children: /* @__PURE__ */ jsx14(WorktreeTable, {
28238
+ blocks: displayBlocks,
28239
+ selectedIndex,
28240
+ selection,
28241
+ frame: spinnerFrame,
28242
+ repoVerbs,
28243
+ rowVerbs,
28244
+ onRowClick: (idx) => setSelectedIndex(idx),
28245
+ onToggleSelect: (path37) => setSelection((prev) => toggleSelection(prev, path37))
28246
+ })
27868
28247
  }),
27869
- /* @__PURE__ */ jsx13(DetailPane, {
27870
- selectedRow
28248
+ /* @__PURE__ */ jsx14(Divider, {
28249
+ splitRatio,
28250
+ totalWidth,
28251
+ onChange: setSplitRatio,
28252
+ onDraggingChange: setIsResizing
28253
+ }),
28254
+ /* @__PURE__ */ jsx14("box", {
28255
+ width: rightCols,
28256
+ height: "100%",
28257
+ flexDirection: "column",
28258
+ children: /* @__PURE__ */ jsx14(DetailPane, {
28259
+ selectedRow
28260
+ })
27871
28261
  })
27872
28262
  ]
27873
28263
  }),
@@ -27877,35 +28267,51 @@ function App({ opts }) {
27877
28267
  border: true,
27878
28268
  borderColor: "magenta",
27879
28269
  children: [
27880
- /* @__PURE__ */ jsx13("text", {
28270
+ /* @__PURE__ */ jsx14("text", {
27881
28271
  children: "filter: "
27882
28272
  }),
27883
- /* @__PURE__ */ jsx13("input", {
28273
+ /* @__PURE__ */ jsx14("input", {
27884
28274
  focused: true,
27885
28275
  placeholder: "Type to filter...",
27886
28276
  onInput: (v) => setFilterText(v)
27887
28277
  })
27888
28278
  ]
27889
28279
  }),
27890
- /* @__PURE__ */ jsx13(Footer, {
28280
+ /* @__PURE__ */ jsx14(Footer, {
27891
28281
  loading: loading || refreshing,
27892
28282
  lastRefreshed,
27893
28283
  errorCount: warnings.length,
27894
28284
  message: actionMessage,
27895
28285
  busyText: latestOp ? `${capitalize(VERBS[latestOp.kind])} ${latestOp.label}…${runningOps.length > 1 ? ` (+${runningOps.length - 1})` : ""}` : undefined,
27896
28286
  spinnerFrame,
27897
- filter: filterText ? { term: filterText, matches: flatRows.length, total: totalRows } : undefined
28287
+ filter: filterText ? { term: filterText, matches: flatRows.length, total: totalRows } : undefined,
28288
+ onHintClick: handleHintClick,
28289
+ onErrorClick: () => setModal({ type: "warnings" })
27898
28290
  }),
27899
- modal.type === "help" && /* @__PURE__ */ jsx13(HelpOverlay, {}),
27900
- modal.type === "history" && /* @__PURE__ */ jsx13(HistoryOverlay, {}),
27901
- modal.type === "warnings" && /* @__PURE__ */ jsx13(WarningsOverlay, {
28291
+ modal.type === "help" && /* @__PURE__ */ jsx14(HelpOverlay, {}),
28292
+ modal.type === "history" && /* @__PURE__ */ jsx14(HistoryOverlay, {}),
28293
+ modal.type === "warnings" && /* @__PURE__ */ jsx14(WarningsOverlay, {
27902
28294
  warnings
27903
28295
  }),
27904
- modal.type === "error" && /* @__PURE__ */ jsx13(ConfirmModal, {
28296
+ modal.type === "error" && /* @__PURE__ */ jsx14(ConfirmModal, {
27905
28297
  title: "Error",
27906
- message: modal.message
28298
+ message: modal.message,
28299
+ onConfirm: () => {
28300
+ setModal({ type: "none" });
28301
+ if (error52) {
28302
+ renderer.destroy();
28303
+ process.exit(1);
28304
+ }
28305
+ },
28306
+ onCancel: () => {
28307
+ setModal({ type: "none" });
28308
+ if (error52) {
28309
+ renderer.destroy();
28310
+ process.exit(1);
28311
+ }
28312
+ }
27907
28313
  }),
27908
- modal.type === "confirm_remove" && /* @__PURE__ */ jsx13(ConfirmModal, {
28314
+ modal.type === "confirm_remove" && /* @__PURE__ */ jsx14(ConfirmModal, {
27909
28315
  title: `Remove ${modal.rows.length} Worktree(s)`,
27910
28316
  message: (() => {
27911
28317
  const count2 = modal.rows.length;
@@ -27920,17 +28326,41 @@ function App({ opts }) {
27920
28326
  }
27921
28327
  return lines.join(`
27922
28328
  `);
27923
- })()
28329
+ })(),
28330
+ onConfirm: () => {
28331
+ const rows = modal.rows;
28332
+ setModal({ type: "none" });
28333
+ startBatchActions("remove", rows, (r) => {
28334
+ const args = ["remove", r.branch, "--repo", r.repoName, "--yes"];
28335
+ const needsForce = r.dirtyFiles.length > 0 || !existsSync4(r.path);
28336
+ if (needsForce)
28337
+ args.push("--force");
28338
+ return args;
28339
+ });
28340
+ },
28341
+ onCancel: () => setModal({ type: "none" })
27924
28342
  }),
27925
- modal.type === "confirm_rebase" && /* @__PURE__ */ jsx13(ConfirmModal, {
28343
+ modal.type === "confirm_rebase" && /* @__PURE__ */ jsx14(ConfirmModal, {
27926
28344
  title: `Rebase ${modal.rows.length} Worktree(s)`,
27927
- message: `Are you sure you want to fetch and rebase ${modal.rows.length === 1 ? modal.rows[0]?.branch : modal.rows.length + " worktrees"}?`
28345
+ message: `Are you sure you want to fetch and rebase ${modal.rows.length === 1 ? modal.rows[0]?.branch : modal.rows.length + " worktrees"}?`,
28346
+ onConfirm: () => {
28347
+ const rows = modal.rows;
28348
+ setModal({ type: "none" });
28349
+ startBatchActions("rebase", rows, (r) => ["rebase", r.branch, "--repo", r.repoName]);
28350
+ },
28351
+ onCancel: () => setModal({ type: "none" })
27928
28352
  }),
27929
- modal.type === "confirm_sync" && /* @__PURE__ */ jsx13(ConfirmModal, {
28353
+ modal.type === "confirm_sync" && /* @__PURE__ */ jsx14(ConfirmModal, {
27930
28354
  title: `Sync ${modal.rows.length} Worktree(s)`,
27931
- message: `Are you sure you want to sync ${modal.rows.length === 1 ? modal.rows[0]?.branch : modal.rows.length + " worktrees"}?`
28355
+ message: `Are you sure you want to sync ${modal.rows.length === 1 ? modal.rows[0]?.branch : modal.rows.length + " worktrees"}?`,
28356
+ onConfirm: () => {
28357
+ const rows = modal.rows;
28358
+ setModal({ type: "none" });
28359
+ startBatchActions("sync", rows, (r) => ["sync", r.branch, "--repo", r.repoName]);
28360
+ },
28361
+ onCancel: () => setModal({ type: "none" })
27932
28362
  }),
27933
- createModal && /* @__PURE__ */ jsx13(InputModal, {
28363
+ createModal && /* @__PURE__ */ jsx14(InputModal, {
27934
28364
  title: `New worktree branch in ${selectedRow?.repoName ?? ""}`,
27935
28365
  placeholder: "Branch name (empty to cancel)",
27936
28366
  errorMessage: createError,
@@ -27957,7 +28387,7 @@ function App({ opts }) {
27957
28387
  setCreateBaseModal({ branch, repoName });
27958
28388
  }
27959
28389
  }),
27960
- createBaseModal && /* @__PURE__ */ jsx13(InputModal, {
28390
+ createBaseModal && /* @__PURE__ */ jsx14(InputModal, {
27961
28391
  title: `Base ref for ${createBaseModal.branch}`,
27962
28392
  placeholder: "origin/main (empty for default main)",
27963
28393
  errorMessage: createBaseError,
@@ -27973,7 +28403,7 @@ function App({ opts }) {
27973
28403
  setCreateDepsChoice({ branch, repoName, base: base2 || undefined });
27974
28404
  }
27975
28405
  }),
27976
- createDepsChoice && /* @__PURE__ */ jsx13(ChoiceModal, {
28406
+ createDepsChoice && /* @__PURE__ */ jsx14(ChoiceModal, {
27977
28407
  title: `Dependencies for ${createDepsChoice.branch}`,
27978
28408
  options: DEPS_CHOICES,
27979
28409
  onSubmit: (choice) => {
@@ -27983,7 +28413,7 @@ function App({ opts }) {
27983
28413
  },
27984
28414
  onCancel: () => setCreateDepsChoice(null)
27985
28415
  }),
27986
- pullPrModal && /* @__PURE__ */ jsx13(InputModal, {
28416
+ pullPrModal && /* @__PURE__ */ jsx14(InputModal, {
27987
28417
  title: `Pull PR into ${selectedRow?.repoName ?? ""}`,
27988
28418
  placeholder: "https://github.com/owner/repo/pull/123",
27989
28419
  errorMessage: pullPrError,
@@ -28011,7 +28441,7 @@ function App({ opts }) {
28011
28441
  setPullForceChoice({ link, repoName });
28012
28442
  }
28013
28443
  }),
28014
- pullForceChoice && /* @__PURE__ */ jsx13(ChoiceModal, {
28444
+ pullForceChoice && /* @__PURE__ */ jsx14(ChoiceModal, {
28015
28445
  title: `Pull ${pullForceChoice.link.split("/").pop()}?`,
28016
28446
  options: [
28017
28447
  { value: "normal", label: "Pull (skip if exists)", desc: "Fails if branch already exists" },
@@ -28024,7 +28454,7 @@ function App({ opts }) {
28024
28454
  },
28025
28455
  onCancel: () => setPullForceChoice(null)
28026
28456
  }),
28027
- renameModal && selectedRow && /* @__PURE__ */ jsx13(InputModal, {
28457
+ renameModal && selectedRow && /* @__PURE__ */ jsx14(InputModal, {
28028
28458
  title: `Rename branch ${selectedRow.branch}`,
28029
28459
  initialValue: selectedRow.branch,
28030
28460
  placeholder: "New branch name",
@@ -28049,7 +28479,7 @@ function App({ opts }) {
28049
28479
  setModal({ type: "confirm_rename", row: target, to });
28050
28480
  }
28051
28481
  }),
28052
- modal.type === "confirm_rename" && /* @__PURE__ */ jsx13(ConfirmModal, {
28482
+ modal.type === "confirm_rename" && /* @__PURE__ */ jsx14(ConfirmModal, {
28053
28483
  title: "Rename Worktree",
28054
28484
  message: [
28055
28485
  `${modal.row.branch} → ${modal.to}`,
@@ -28060,14 +28490,32 @@ function App({ opts }) {
28060
28490
  "Uncommitted changes and synced files move with it.",
28061
28491
  "Proceed?"
28062
28492
  ].join(`
28063
- `)
28493
+ `),
28494
+ onConfirm: () => {
28495
+ const { row, to } = modal;
28496
+ setModal({ type: "none" });
28497
+ const op = {
28498
+ id: nextOpId.current++,
28499
+ kind: "rename",
28500
+ repoNames: [row.repoName],
28501
+ rowPath: row.path,
28502
+ branch: row.branch,
28503
+ label: `${row.branch} → ${to}`,
28504
+ title: `Rename ${row.branch} → ${to}`,
28505
+ status: "queued",
28506
+ lines: []
28507
+ };
28508
+ setOps((prev) => [...prev, op]);
28509
+ executeOp(op, ["rename", row.branch, to, "--repo", row.repoName], [row.repoName]);
28510
+ },
28511
+ onCancel: () => setModal({ type: "none" })
28064
28512
  }),
28065
- configOpen && /* @__PURE__ */ jsx13(ConfigOverlay, {
28513
+ configOpen && /* @__PURE__ */ jsx14(ConfigOverlay, {
28066
28514
  onClose: () => setConfigOpen(false),
28067
28515
  onSaved: () => void doRefresh(),
28068
28516
  onError: (msg) => setModal({ type: "error", message: msg })
28069
28517
  }),
28070
- failedLogs.length > 0 && /* @__PURE__ */ jsx13(ActionLogModal, {
28518
+ failedLogs.length > 0 && /* @__PURE__ */ jsx14(ActionLogModal, {
28071
28519
  title: failedLogs[0].title,
28072
28520
  lines: failedLogs[0].lines,
28073
28521
  done: true,
@@ -28083,6 +28531,7 @@ var init_App = __esm(() => {
28083
28531
  init_WorktreeTable();
28084
28532
  init_DetailPane();
28085
28533
  init_Footer();
28534
+ init_Divider();
28086
28535
  init_HelpOverlay();
28087
28536
  init_ConfirmModal();
28088
28537
  init_git();
@@ -28122,14 +28571,14 @@ __export(exports_tui, {
28122
28571
  });
28123
28572
  import { createCliRenderer, TextTableRenderable } from "@opentui/core";
28124
28573
  import { createRoot, extend as extend2 } from "@opentui/react";
28125
- import { jsx as jsx14 } from "@opentui/react/jsx-runtime";
28574
+ import { jsx as jsx15 } from "@opentui/react/jsx-runtime";
28126
28575
  async function runTerminal(opts) {
28127
28576
  const renderer = await createCliRenderer({
28128
28577
  exitOnCtrlC: false
28129
28578
  });
28130
28579
  const root = createRoot(renderer);
28131
28580
  try {
28132
- root.render(/* @__PURE__ */ jsx14(App, {
28581
+ root.render(/* @__PURE__ */ jsx15(App, {
28133
28582
  opts
28134
28583
  }));
28135
28584
  } catch (err) {