@ogpoyraz/wtx 0.8.6 → 0.8.8

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 +790 -267
  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
  });
@@ -25553,10 +25561,11 @@ function useWorktrees(opts) {
25553
25561
  }
25554
25562
  }
25555
25563
  }, [opts]);
25564
+ const clearWarnings = useCallback(() => setWarnings([]), []);
25556
25565
  useEffect(() => {
25557
25566
  refresh();
25558
25567
  }, [refresh]);
25559
- return { blocks, loading, refreshing, error: error52, warnings, lastRefreshed, pendingRepos, refresh };
25568
+ return { blocks, loading, refreshing, error: error52, warnings, lastRefreshed, pendingRepos, refresh, clearWarnings };
25560
25569
  }
25561
25570
  var init_useWorktrees = __esm(() => {
25562
25571
  init_data();
@@ -25600,14 +25609,19 @@ import { useRef as useRef2, useCallback as useCallback2 } from "react";
25600
25609
  function useTapHandler(onTap) {
25601
25610
  const down = useRef2(null);
25602
25611
  const onMouseDown = useCallback2((e) => {
25612
+ if (e.button !== 0)
25613
+ return;
25603
25614
  down.current = { x: e.x, y: e.y };
25604
25615
  }, []);
25605
25616
  const onMouseUp = useCallback2((e) => {
25606
25617
  const start = down.current;
25607
25618
  down.current = null;
25619
+ if (e.button !== 0)
25620
+ return;
25608
25621
  if (!start || !isTapWithoutDrag(start, e))
25609
25622
  return;
25610
- onTap();
25623
+ e.stopPropagation();
25624
+ onTap(e);
25611
25625
  }, [onTap]);
25612
25626
  return { onMouseDown, onMouseUp };
25613
25627
  }
@@ -25679,7 +25693,7 @@ async function openInBrowser(url2) {
25679
25693
 
25680
25694
  // src/tui/components/WorktreeTable.tsx
25681
25695
  import { useEffect as useEffect2, useRef as useRef3 } from "react";
25682
- import { jsx, jsxs, Fragment } from "@opentui/react/jsx-runtime";
25696
+ import { jsx, jsxs } from "@opentui/react/jsx-runtime";
25683
25697
  function statusBadge(row) {
25684
25698
  if (row.isMainCheckout)
25685
25699
  return { text: "[main]", fg: tokens.accent };
@@ -25693,11 +25707,27 @@ function statusBadge(row) {
25693
25707
  return { text: `dirty (${row.dirtyFiles.length})`, fg: tokens.warning };
25694
25708
  return { text: "clean", fg: tokens.dim };
25695
25709
  }
25696
- function WorktreeItem({ row, isSelected, isMultiSelected, indicator, frame, id }) {
25710
+ function WorktreeItem({
25711
+ row,
25712
+ isSelected,
25713
+ isMultiSelected,
25714
+ indicator,
25715
+ frame,
25716
+ id,
25717
+ onRowClick,
25718
+ onToggleSelect
25719
+ }) {
25697
25720
  const prTap = useTapHandler(() => {
25698
25721
  if (row.prUrl)
25699
25722
  openInBrowser(row.prUrl);
25700
25723
  });
25724
+ const rowTap = useTapHandler(() => {
25725
+ onRowClick?.();
25726
+ });
25727
+ const gutterTap = useTapHandler((e) => {
25728
+ e.stopPropagation();
25729
+ onToggleSelect?.();
25730
+ });
25701
25731
  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
25732
  const disabled = indicator !== undefined || row.isPendingCreate === true;
25703
25733
  const primary = isSelected ? tokens.bright : disabled ? tokens.dim : tokens.fg;
@@ -25714,98 +25744,136 @@ function WorktreeItem({ row, isSelected, isMultiSelected, indicator, frame, id }
25714
25744
  ownerSegment,
25715
25745
  baseSegment
25716
25746
  ].filter(Boolean).join(" ").trimEnd();
25747
+ const isClickable = !row.isPendingCreate && !indicator;
25717
25748
  return /* @__PURE__ */ jsxs("box", {
25718
25749
  id,
25719
25750
  flexDirection: "column",
25720
25751
  backgroundColor: isSelected ? tokens.selectionBg : undefined,
25721
25752
  style: { paddingRight: 1 },
25753
+ ...isClickable && onRowClick ? rowTap : {},
25722
25754
  children: [
25723
- /* @__PURE__ */ jsxs("text", {
25755
+ /* @__PURE__ */ jsxs("box", {
25756
+ flexDirection: "row",
25724
25757
  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)
25758
+ /* @__PURE__ */ jsx("text", {
25759
+ children: /* @__PURE__ */ jsx("span", {
25760
+ fg: primary,
25761
+ children: isSelected ? "▸ " : " "
25762
+ })
25740
25763
  }),
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
25764
+ /* @__PURE__ */ jsx("box", {
25765
+ width: 2,
25766
+ ...onToggleSelect ? gutterTap : {},
25767
+ children: /* @__PURE__ */ jsx("text", {
25768
+ fg: tokens.accent,
25769
+ children: isMultiSelected ? "✓ " : " "
25770
+ })
25753
25771
  }),
25754
- row.prNumber !== null && /* @__PURE__ */ jsxs(Fragment, {
25772
+ /* @__PURE__ */ jsxs("text", {
25755
25773
  children: [
25756
25774
  /* @__PURE__ */ jsx("span", {
25757
25775
  fg: tokens.dim,
25758
- children: secondary ? " · " : ""
25776
+ children: hierarchyPrefix
25759
25777
  }),
25760
25778
  /* @__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}`
25779
+ fg: primary,
25780
+ children: truncateBranch(row.branch)
25767
25781
  }),
25768
- row.prChecks && /* @__PURE__ */ jsx("span", {
25782
+ /* @__PURE__ */ jsx("span", {
25783
+ fg: badge.fg,
25784
+ children: ` ${badge.text}`
25785
+ })
25786
+ ]
25787
+ })
25788
+ ]
25789
+ }),
25790
+ /* @__PURE__ */ jsxs("box", {
25791
+ flexDirection: "row",
25792
+ children: [
25793
+ /* @__PURE__ */ jsxs("text", {
25794
+ children: [
25795
+ /* @__PURE__ */ jsx("span", {
25769
25796
  fg: tokens.dim,
25770
- children: ` (${row.prChecks})`
25797
+ children: secondary
25771
25798
  }),
25772
- row.prUrl && /* @__PURE__ */ jsx("span", {
25799
+ row.prNumber !== null && secondary ? /* @__PURE__ */ jsx("span", {
25773
25800
  fg: tokens.dim,
25774
- children: " "
25775
- })
25801
+ children: " · "
25802
+ }) : null
25776
25803
  ]
25777
25804
  }),
25778
- baseChangedSegment && /* @__PURE__ */ jsx("span", {
25779
- fg: tokens.warning,
25780
- children: baseChangedSegment
25805
+ row.prNumber !== null && /* @__PURE__ */ jsx("box", {
25806
+ ...row.prUrl ? prTap : {},
25807
+ children: /* @__PURE__ */ jsxs("text", {
25808
+ children: [
25809
+ /* @__PURE__ */ jsx("span", {
25810
+ fg: tokens.accent,
25811
+ children: `#${row.prNumber}`
25812
+ }),
25813
+ row.prState && /* @__PURE__ */ jsx("span", {
25814
+ fg: tokens.dim,
25815
+ children: ` ${row.prState}`
25816
+ }),
25817
+ row.prChecks && /* @__PURE__ */ jsx("span", {
25818
+ fg: tokens.dim,
25819
+ children: ` (${row.prChecks})`
25820
+ }),
25821
+ row.prUrl && /* @__PURE__ */ jsx("span", {
25822
+ fg: tokens.dim,
25823
+ children: " ↗"
25824
+ })
25825
+ ]
25826
+ })
25781
25827
  }),
25782
- rebaseSegment && /* @__PURE__ */ jsx("span", {
25783
- fg: tokens.error,
25784
- children: rebaseSegment
25828
+ /* @__PURE__ */ jsxs("text", {
25829
+ children: [
25830
+ baseChangedSegment && /* @__PURE__ */ jsx("span", {
25831
+ fg: tokens.warning,
25832
+ children: baseChangedSegment
25833
+ }),
25834
+ rebaseSegment && /* @__PURE__ */ jsx("span", {
25835
+ fg: tokens.error,
25836
+ children: rebaseSegment
25837
+ })
25838
+ ]
25785
25839
  })
25786
25840
  ]
25787
25841
  })
25788
25842
  ]
25789
25843
  });
25790
25844
  }
25791
- function WorktreeTable({ blocks, selectedIndex, selection = new Set, frame, repoVerbs, rowVerbs }) {
25845
+ function WorktreeTable({ blocks, selectedIndex, selection = new Set, frame, repoVerbs, rowVerbs, onRowClick, onToggleSelect }) {
25792
25846
  const scrollRef = useRef3(null);
25793
25847
  useEffect2(() => {
25794
25848
  if (scrollRef.current?.scrollChildIntoView) {
25795
25849
  scrollRef.current.scrollChildIntoView("selected-row");
25796
25850
  }
25797
25851
  }, [selectedIndex]);
25798
- let flatIndex = 0;
25852
+ const flatIndexMap = new Map;
25853
+ let idx = 0;
25854
+ for (const b of blocks) {
25855
+ for (const r of b.rows) {
25856
+ if (!r.isPendingCreate)
25857
+ flatIndexMap.set(r.path, idx++);
25858
+ }
25859
+ }
25860
+ let headerSeen = 0;
25799
25861
  return /* @__PURE__ */ jsx("scrollbox", {
25800
25862
  ref: scrollRef,
25801
25863
  id: "worktree-table",
25802
- flexGrow: 2,
25864
+ flexGrow: 1,
25865
+ width: "100%",
25803
25866
  height: "100%",
25804
25867
  border: true,
25805
25868
  borderColor: tokens.border,
25869
+ focusedBorderColor: tokens.border,
25870
+ focusable: false,
25806
25871
  title: "Worktrees",
25807
25872
  paddingX: 1,
25808
25873
  focused: false,
25874
+ onMouseScroll: (e) => {
25875
+ e.stopPropagation();
25876
+ },
25809
25877
  children: blocks.length === 0 ? /* @__PURE__ */ jsx("text", {
25810
25878
  fg: tokens.dim,
25811
25879
  children: "No repositories configured."
@@ -25816,7 +25884,7 @@ function WorktreeTable({ blocks, selectedIndex, selection = new Set, frame, repo
25816
25884
  style: { marginBottom: 1 },
25817
25885
  children: [
25818
25886
  /* @__PURE__ */ jsx("box", {
25819
- style: { marginTop: flatIndex === 0 ? 0 : 1 },
25887
+ style: { marginTop: headerSeen === 0 ? 0 : 1 },
25820
25888
  children: /* @__PURE__ */ jsxs("text", {
25821
25889
  children: [
25822
25890
  /* @__PURE__ */ jsx("span", {
@@ -25837,18 +25905,23 @@ function WorktreeTable({ blocks, selectedIndex, selection = new Set, frame, repo
25837
25905
  ]
25838
25906
  })
25839
25907
  }),
25908
+ (() => {
25909
+ headerSeen++;
25910
+ return null;
25911
+ })(),
25840
25912
  block.rows.map((row) => {
25841
25913
  const navigable = !row.isPendingCreate;
25842
- const isSelected = navigable && flatIndex === selectedIndex;
25843
- if (navigable)
25844
- flatIndex++;
25914
+ const flatIdx = flatIndexMap.get(row.path);
25915
+ const isSelected = navigable && flatIdx === selectedIndex;
25845
25916
  return /* @__PURE__ */ jsx(WorktreeItem, {
25846
25917
  row,
25847
25918
  isSelected,
25848
25919
  isMultiSelected: selection.has(row.path),
25849
25920
  indicator: rowVerbs.get(row.path),
25850
25921
  frame,
25851
- id: isSelected ? "selected-row" : undefined
25922
+ id: isSelected ? "selected-row" : undefined,
25923
+ onRowClick: navigable && flatIdx !== undefined && onRowClick ? () => onRowClick(flatIdx) : undefined,
25924
+ onToggleSelect: onToggleSelect ? () => onToggleSelect(row.path) : undefined
25852
25925
  }, row.path);
25853
25926
  })
25854
25927
  ]
@@ -25863,7 +25936,7 @@ var init_WorktreeTable = __esm(() => {
25863
25936
  });
25864
25937
 
25865
25938
  // src/tui/components/DetailPane.tsx
25866
- import { jsx as jsx2, jsxs as jsxs2, Fragment as Fragment2 } from "@opentui/react/jsx-runtime";
25939
+ import { jsx as jsx2, jsxs as jsxs2, Fragment } from "@opentui/react/jsx-runtime";
25867
25940
  function DetailPane({ selectedRow }) {
25868
25941
  const prTap = useTapHandler(() => {
25869
25942
  if (selectedRow?.prUrl)
@@ -25873,9 +25946,12 @@ function DetailPane({ selectedRow }) {
25873
25946
  return /* @__PURE__ */ jsx2("box", {
25874
25947
  id: "detail-pane",
25875
25948
  flexGrow: 1,
25949
+ width: "100%",
25876
25950
  height: "100%",
25877
25951
  border: true,
25878
25952
  borderColor: tokens.border,
25953
+ focusedBorderColor: tokens.border,
25954
+ focusable: false,
25879
25955
  title: "Details",
25880
25956
  padding: 1,
25881
25957
  justifyContent: "center",
@@ -25911,9 +25987,12 @@ function DetailPane({ selectedRow }) {
25911
25987
  return /* @__PURE__ */ jsxs2("scrollbox", {
25912
25988
  id: "detail-pane",
25913
25989
  flexGrow: 1,
25990
+ width: "100%",
25914
25991
  height: "100%",
25915
25992
  border: true,
25916
25993
  borderColor: tokens.border,
25994
+ focusedBorderColor: tokens.border,
25995
+ focusable: false,
25917
25996
  title: "Details",
25918
25997
  padding: 1,
25919
25998
  focused: false,
@@ -26083,7 +26162,7 @@ function DetailPane({ selectedRow }) {
26083
26162
  })
26084
26163
  ]
26085
26164
  }),
26086
- prNumber !== null && /* @__PURE__ */ jsxs2(Fragment2, {
26165
+ prNumber !== null && /* @__PURE__ */ jsxs2(Fragment, {
26087
26166
  children: [
26088
26167
  /* @__PURE__ */ jsxs2("text", {
26089
26168
  style: { marginTop: 1 },
@@ -26179,7 +26258,7 @@ function DetailPane({ selectedRow }) {
26179
26258
  fg: tokens.dim,
26180
26259
  children: "Actions:"
26181
26260
  }),
26182
- !isMainCheckout && /* @__PURE__ */ jsxs2(Fragment2, {
26261
+ !isMainCheckout && /* @__PURE__ */ jsxs2(Fragment, {
26183
26262
  children: [
26184
26263
  /* @__PURE__ */ jsxs2("text", {
26185
26264
  fg: tokens.dim,
@@ -26215,8 +26294,44 @@ var init_DetailPane = __esm(() => {
26215
26294
 
26216
26295
  // src/tui/components/Footer.tsx
26217
26296
  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(" · ");
26297
+ function HintItem({ hintKey, label, isFirst, onClick }) {
26298
+ const tap = useTapHandler(() => onClick?.(hintKey));
26299
+ return /* @__PURE__ */ jsx3("box", {
26300
+ flexDirection: "row",
26301
+ ...onClick ? tap : {},
26302
+ children: /* @__PURE__ */ jsxs3("text", {
26303
+ children: [
26304
+ /* @__PURE__ */ jsx3("span", {
26305
+ fg: tokens.dim,
26306
+ children: isFirst ? "" : " · "
26307
+ }),
26308
+ /* @__PURE__ */ jsx3("span", {
26309
+ fg: tokens.dim,
26310
+ children: `${hintKey} ${label}`
26311
+ })
26312
+ ]
26313
+ })
26314
+ });
26315
+ }
26316
+ function ErrorBadge({ count: count2, onClick }) {
26317
+ const tap = useTapHandler(() => onClick?.());
26318
+ return /* @__PURE__ */ jsx3("box", {
26319
+ ...onClick ? tap : {},
26320
+ children: /* @__PURE__ */ jsxs3("text", {
26321
+ children: [
26322
+ /* @__PURE__ */ jsx3("span", {
26323
+ fg: tokens.error,
26324
+ children: `${count2} error${count2 !== 1 ? "s" : ""}`
26325
+ }),
26326
+ /* @__PURE__ */ jsx3("span", {
26327
+ fg: tokens.dim,
26328
+ children: " · e view"
26329
+ })
26330
+ ]
26331
+ })
26332
+ });
26333
+ }
26334
+ function Footer({ loading, lastRefreshed, errorCount, message, busyText, spinnerFrame, filter, onHintClick, onErrorClick }) {
26220
26335
  const frame = spinnerFrame ?? "◌";
26221
26336
  return /* @__PURE__ */ jsxs3("box", {
26222
26337
  id: "footer",
@@ -26232,13 +26347,20 @@ function Footer({ loading, lastRefreshed, errorCount, message, busyText, spinner
26232
26347
  busyText ? /* @__PURE__ */ jsx3("text", {
26233
26348
  fg: tokens.accent,
26234
26349
  children: `${frame} ${busyText}`
26235
- }) : /* @__PURE__ */ jsx3("text", {
26236
- fg: tokens.dim,
26237
- children: hints
26350
+ }) : /* @__PURE__ */ jsx3("box", {
26351
+ flexDirection: "row",
26352
+ gap: 1,
26353
+ children: HINTS.map(([key, label], idx) => /* @__PURE__ */ jsx3(HintItem, {
26354
+ hintKey: key,
26355
+ label,
26356
+ isFirst: idx === 0,
26357
+ onClick: onHintClick
26358
+ }, key))
26238
26359
  }),
26239
26360
  /* @__PURE__ */ jsxs3("box", {
26240
26361
  flexDirection: "row",
26241
26362
  gap: 2,
26363
+ alignItems: "center",
26242
26364
  children: [
26243
26365
  message ? /* @__PURE__ */ jsx3("text", {
26244
26366
  fg: tokens.warning,
@@ -26256,18 +26378,16 @@ function Footer({ loading, lastRefreshed, errorCount, message, busyText, spinner
26256
26378
  ")"
26257
26379
  ]
26258
26380
  }) : 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
- ]
26381
+ errorCount > 0 ? /* @__PURE__ */ jsx3(ErrorBadge, {
26382
+ count: errorCount,
26383
+ onClick: onErrorClick
26270
26384
  }) : null,
26385
+ /* @__PURE__ */ jsx3(HintItem, {
26386
+ hintKey: "?",
26387
+ label: "help",
26388
+ isFirst: errorCount === 0,
26389
+ onClick: onHintClick
26390
+ }),
26271
26391
  /* @__PURE__ */ jsx3("text", {
26272
26392
  fg: tokens.dim,
26273
26393
  children: loading ? `${frame} Refreshing…` : `Updated ${lastRefreshed}`
@@ -26280,27 +26400,83 @@ function Footer({ loading, lastRefreshed, errorCount, message, busyText, spinner
26280
26400
  var HINTS;
26281
26401
  var init_Footer = __esm(() => {
26282
26402
  init_theme();
26403
+ init_use_tap();
26283
26404
  HINTS = [
26284
- ["c", "config"],
26285
26405
  ["n", "create"],
26286
- ["f", "fetch"],
26287
- ["?", "help"],
26288
- ["H", "history"],
26289
- ["o", "ide"],
26290
- ["i", "install"],
26291
- ["p", "pull"],
26292
- ["b", "rebase"],
26293
- ["r", "refresh"],
26294
- ["d", "remove"],
26295
- ["m", "rename"],
26296
- ["s", "sync"]
26406
+ ["d", "remove"]
26297
26407
  ];
26298
26408
  });
26299
26409
 
26300
- // src/tui/components/Overlay.tsx
26410
+ // src/tui/components/Divider.tsx
26411
+ import { useState as useState2, useRef as useRef4, useCallback as useCallback3 } from "react";
26412
+ import { useRenderer } from "@opentui/react";
26301
26413
  import { jsx as jsx4 } from "@opentui/react/jsx-runtime";
26302
- function Overlay({ title, borderColor = tokens.border, width = 64, children }) {
26414
+ function Divider({ splitRatio, totalWidth, onChange, onDraggingChange }) {
26415
+ const renderer = useRenderer();
26416
+ const [hovered, setHovered] = useState2(false);
26417
+ const [dragging, setDragging] = useState2(false);
26418
+ const startXRef = useRef4(null);
26419
+ const startRatioRef = useRef4(splitRatio);
26420
+ const handleMouseDown = useCallback3((e) => {
26421
+ if (e.button !== 0)
26422
+ return;
26423
+ startXRef.current = e.x;
26424
+ startRatioRef.current = splitRatio;
26425
+ setDragging(true);
26426
+ onDraggingChange?.(true);
26427
+ renderer.clearSelection();
26428
+ e.stopPropagation();
26429
+ }, [splitRatio, onDraggingChange, renderer]);
26430
+ const handleMouseUp = useCallback3((e) => {
26431
+ if (e.button !== 0)
26432
+ return;
26433
+ startXRef.current = null;
26434
+ setDragging(false);
26435
+ onDraggingChange?.(false);
26436
+ e.stopPropagation();
26437
+ }, [onDraggingChange]);
26438
+ const handleMouseDrag = useCallback3((e) => {
26439
+ if (startXRef.current === null)
26440
+ return;
26441
+ renderer.clearSelection();
26442
+ const dx = e.x - startXRef.current;
26443
+ const next = clampSplitRatio(totalWidth, startRatioRef.current + dx / totalWidth);
26444
+ onChange(next);
26445
+ e.stopPropagation();
26446
+ }, [totalWidth, onChange, renderer]);
26447
+ const dragHandlers = {
26448
+ onMouseDown: handleMouseDown,
26449
+ onMouseUp: handleMouseUp,
26450
+ onMouseDrag: handleMouseDrag,
26451
+ onMouseMove: handleMouseDrag
26452
+ };
26303
26453
  return /* @__PURE__ */ jsx4("box", {
26454
+ width: 3,
26455
+ height: "100%",
26456
+ backgroundColor: dragging ? tokens.selectionBg : hovered ? tokens.panelBg : undefined,
26457
+ ...dragHandlers,
26458
+ onMouseOver: () => setHovered(true),
26459
+ onMouseOut: () => setHovered(false),
26460
+ alignItems: "center",
26461
+ justifyContent: "center",
26462
+ children: /* @__PURE__ */ jsx4("text", {
26463
+ fg: dragging ? tokens.accent : hovered ? tokens.borderActive : tokens.border,
26464
+ ...dragHandlers,
26465
+ onMouseOver: () => setHovered(true),
26466
+ onMouseOut: () => setHovered(false),
26467
+ children: " │ "
26468
+ })
26469
+ });
26470
+ }
26471
+ var init_Divider = __esm(() => {
26472
+ init_theme();
26473
+ init_utils();
26474
+ });
26475
+
26476
+ // src/tui/components/Overlay.tsx
26477
+ import { jsx as jsx5 } from "@opentui/react/jsx-runtime";
26478
+ function Overlay({ title, borderColor = tokens.border, width = 64, children }) {
26479
+ return /* @__PURE__ */ jsx5("box", {
26304
26480
  id: "overlay-scrim",
26305
26481
  position: "absolute",
26306
26482
  width: "100%",
@@ -26312,7 +26488,7 @@ function Overlay({ title, borderColor = tokens.border, width = 64, children }) {
26312
26488
  flexDirection: "row",
26313
26489
  justifyContent: "center",
26314
26490
  alignItems: "center",
26315
- children: /* @__PURE__ */ jsx4("box", {
26491
+ children: /* @__PURE__ */ jsx5("box", {
26316
26492
  id: "overlay-panel",
26317
26493
  width,
26318
26494
  border: true,
@@ -26332,34 +26508,46 @@ var init_Overlay = __esm(() => {
26332
26508
  });
26333
26509
 
26334
26510
  // src/tui/components/HelpOverlay.tsx
26335
- import { jsx as jsx5, jsxs as jsxs4 } from "@opentui/react/jsx-runtime";
26511
+ import { jsx as jsx6, jsxs as jsxs4 } from "@opentui/react/jsx-runtime";
26336
26512
  function HelpOverlay() {
26337
26513
  return /* @__PURE__ */ jsxs4(Overlay, {
26338
26514
  title: "Help",
26339
26515
  borderColor: tokens.border,
26516
+ width: 84,
26340
26517
  children: [
26341
- /* @__PURE__ */ jsx5("box", {
26518
+ /* @__PURE__ */ jsx6("box", {
26342
26519
  flexDirection: "column",
26343
- style: { maxHeight: 40 },
26344
- children: /* @__PURE__ */ jsx5("scrollbox", {
26520
+ style: { maxHeight: 36 },
26521
+ children: /* @__PURE__ */ jsx6("scrollbox", {
26345
26522
  flexGrow: 1,
26346
- children: HELP_ENTRIES.map(([key, desc]) => /* @__PURE__ */ jsxs4("text", {
26523
+ children: HELP_ENTRIES.map(([key, desc]) => /* @__PURE__ */ jsxs4("box", {
26524
+ flexDirection: "row",
26525
+ gap: 1,
26347
26526
  children: [
26348
- /* @__PURE__ */ jsx5("span", {
26349
- fg: tokens.accent,
26350
- children: key.padEnd(10)
26527
+ /* @__PURE__ */ jsx6("box", {
26528
+ width: 18,
26529
+ flexShrink: 0,
26530
+ children: /* @__PURE__ */ jsx6("text", {
26531
+ fg: tokens.accent,
26532
+ children: key
26533
+ })
26351
26534
  }),
26352
- /* @__PURE__ */ jsx5("span", {
26353
- children: " - "
26535
+ /* @__PURE__ */ jsx6("text", {
26536
+ fg: tokens.dim,
26537
+ children: "-"
26354
26538
  }),
26355
- /* @__PURE__ */ jsx5("span", {
26356
- children: desc
26539
+ /* @__PURE__ */ jsx6("box", {
26540
+ flexGrow: 1,
26541
+ children: /* @__PURE__ */ jsx6("text", {
26542
+ fg: tokens.fg,
26543
+ children: desc
26544
+ })
26357
26545
  })
26358
26546
  ]
26359
26547
  }, key))
26360
26548
  })
26361
26549
  }),
26362
- /* @__PURE__ */ jsx5("text", {
26550
+ /* @__PURE__ */ jsx6("text", {
26363
26551
  style: { marginTop: 1, fg: tokens.dim },
26364
26552
  children: "Press any key to close"
26365
26553
  })
@@ -26398,9 +26586,11 @@ var init_HelpOverlay = __esm(() => {
26398
26586
  });
26399
26587
 
26400
26588
  // 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, {
26589
+ import { jsx as jsx7, jsxs as jsxs5 } from "@opentui/react/jsx-runtime";
26590
+ function ConfirmModal({ title, message, onConfirm, onCancel }) {
26591
+ const yesTap = useTapHandler(() => onConfirm?.());
26592
+ const noTap = useTapHandler(() => onCancel?.());
26593
+ return /* @__PURE__ */ jsx7(Overlay, {
26404
26594
  title,
26405
26595
  borderColor: tokens.warning,
26406
26596
  children: /* @__PURE__ */ jsxs5("box", {
@@ -26408,25 +26598,42 @@ function ConfirmModal({ title, message }) {
26408
26598
  justifyContent: "center",
26409
26599
  alignItems: "center",
26410
26600
  children: [
26411
- /* @__PURE__ */ jsx6("text", {
26601
+ /* @__PURE__ */ jsx7("text", {
26412
26602
  children: message
26413
26603
  }),
26414
- /* @__PURE__ */ jsxs5("text", {
26604
+ /* @__PURE__ */ jsxs5("box", {
26605
+ flexDirection: "row",
26606
+ gap: 2,
26415
26607
  style: { marginTop: 2 },
26416
26608
  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] "
26609
+ /* @__PURE__ */ jsx7("box", {
26610
+ ...onConfirm ? yesTap : {},
26611
+ children: /* @__PURE__ */ jsxs5("text", {
26612
+ children: [
26613
+ /* @__PURE__ */ jsx7("span", {
26614
+ fg: tokens.dim,
26615
+ children: "[y] "
26616
+ }),
26617
+ /* @__PURE__ */ jsx7("span", {
26618
+ fg: tokens.success,
26619
+ children: "Yes"
26620
+ })
26621
+ ]
26622
+ })
26427
26623
  }),
26428
- /* @__PURE__ */ jsx6("span", {
26429
- children: "Cancel"
26624
+ /* @__PURE__ */ jsx7("box", {
26625
+ ...onCancel ? noTap : {},
26626
+ children: /* @__PURE__ */ jsxs5("text", {
26627
+ children: [
26628
+ /* @__PURE__ */ jsx7("span", {
26629
+ fg: tokens.dim,
26630
+ children: "[n/esc] "
26631
+ }),
26632
+ /* @__PURE__ */ jsx7("span", {
26633
+ children: "Cancel"
26634
+ })
26635
+ ]
26636
+ })
26430
26637
  })
26431
26638
  ]
26432
26639
  })
@@ -26437,6 +26644,7 @@ function ConfirmModal({ title, message }) {
26437
26644
  var init_ConfirmModal = __esm(() => {
26438
26645
  init_Overlay();
26439
26646
  init_theme();
26647
+ init_use_tap();
26440
26648
  });
26441
26649
 
26442
26650
  // src/tui/actions.ts
@@ -26497,20 +26705,20 @@ async function runWtxAction(args, onLine) {
26497
26705
  }
26498
26706
 
26499
26707
  // src/tui/components/ActionLogModal.tsx
26500
- import { jsx as jsx7, jsxs as jsxs6 } from "@opentui/react/jsx-runtime";
26708
+ import { jsx as jsx8, jsxs as jsxs6 } from "@opentui/react/jsx-runtime";
26501
26709
  function ActionLogModal({ title, lines, done, exitCode, remaining }) {
26502
26710
  return /* @__PURE__ */ jsxs6(Overlay, {
26503
26711
  title,
26504
26712
  borderColor: tokens.border,
26505
26713
  children: [
26506
- /* @__PURE__ */ jsx7("box", {
26714
+ /* @__PURE__ */ jsx8("box", {
26507
26715
  flexGrow: 1,
26508
26716
  flexDirection: "column",
26509
26717
  style: { minHeight: 10, maxHeight: 20 },
26510
- children: /* @__PURE__ */ jsx7("scrollbox", {
26718
+ children: /* @__PURE__ */ jsx8("scrollbox", {
26511
26719
  flexGrow: 1,
26512
26720
  stickyScroll: true,
26513
- children: lines.map((line, i2) => /* @__PURE__ */ jsx7("text", {
26721
+ children: lines.map((line, i2) => /* @__PURE__ */ jsx8("text", {
26514
26722
  fg: line.type === "err" ? tokens.error : tokens.fg,
26515
26723
  children: line.text
26516
26724
  }, i2))
@@ -26521,11 +26729,11 @@ function ActionLogModal({ title, lines, done, exitCode, remaining }) {
26521
26729
  flexDirection: "row",
26522
26730
  justifyContent: "center",
26523
26731
  children: [
26524
- !done && /* @__PURE__ */ jsx7("text", {
26732
+ !done && /* @__PURE__ */ jsx8("text", {
26525
26733
  fg: tokens.warning,
26526
26734
  children: "Running..."
26527
26735
  }),
26528
- done && exitCode === 0 && /* @__PURE__ */ jsx7("text", {
26736
+ done && exitCode === 0 && /* @__PURE__ */ jsx8("text", {
26529
26737
  fg: tokens.success,
26530
26738
  children: "✓ Done (exit 0)"
26531
26739
  }),
@@ -26538,11 +26746,11 @@ function ActionLogModal({ title, lines, done, exitCode, remaining }) {
26538
26746
  exitCode
26539
26747
  ]
26540
26748
  }),
26541
- remaining !== undefined && remaining > 0 && /* @__PURE__ */ jsx7("span", {
26749
+ remaining !== undefined && remaining > 0 && /* @__PURE__ */ jsx8("span", {
26542
26750
  fg: tokens.warning,
26543
26751
  children: ` · ${remaining} more failure${remaining > 1 ? "s" : ""}`
26544
26752
  }),
26545
- /* @__PURE__ */ jsx7("span", {
26753
+ /* @__PURE__ */ jsx8("span", {
26546
26754
  fg: tokens.dim,
26547
26755
  children: " - press any key to close"
26548
26756
  })
@@ -26629,8 +26837,8 @@ var init_history = __esm(() => {
26629
26837
  });
26630
26838
 
26631
26839
  // 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";
26840
+ import { useEffect as useEffect3, useState as useState3 } from "react";
26841
+ import { jsx as jsx9, jsxs as jsxs7 } from "@opentui/react/jsx-runtime";
26634
26842
  function formatShortTime(iso) {
26635
26843
  const d = new Date(iso);
26636
26844
  if (Number.isNaN(d.getTime()))
@@ -26652,7 +26860,7 @@ function HistoryEntryLine({ entry }) {
26652
26860
  prefix
26653
26861
  ]
26654
26862
  }),
26655
- duration3 && /* @__PURE__ */ jsx8("span", {
26863
+ duration3 && /* @__PURE__ */ jsx9("span", {
26656
26864
  fg: tokens.dim,
26657
26865
  children: duration3
26658
26866
  })
@@ -26669,7 +26877,7 @@ function HistoryEntryLine({ entry }) {
26669
26877
  prefix
26670
26878
  ]
26671
26879
  }),
26672
- duration3 && /* @__PURE__ */ jsx8("span", {
26880
+ duration3 && /* @__PURE__ */ jsx9("span", {
26673
26881
  fg: tokens.dim,
26674
26882
  children: duration3
26675
26883
  })
@@ -26678,11 +26886,11 @@ function HistoryEntryLine({ entry }) {
26678
26886
  }
26679
26887
  return /* @__PURE__ */ jsxs7("text", {
26680
26888
  children: [
26681
- /* @__PURE__ */ jsx8("span", {
26889
+ /* @__PURE__ */ jsx9("span", {
26682
26890
  fg: tokens.error,
26683
26891
  children: "✗ "
26684
26892
  }),
26685
- /* @__PURE__ */ jsx8("span", {
26893
+ /* @__PURE__ */ jsx9("span", {
26686
26894
  children: prefix
26687
26895
  }),
26688
26896
  /* @__PURE__ */ jsxs7("span", {
@@ -26698,7 +26906,7 @@ function HistoryEntryLine({ entry }) {
26698
26906
  });
26699
26907
  }
26700
26908
  function HistoryOverlay() {
26701
- const [entries, setEntries] = useState2([]);
26909
+ const [entries, setEntries] = useState3([]);
26702
26910
  useEffect3(() => {
26703
26911
  setEntries(readRecentHistory(500));
26704
26912
  }, []);
@@ -26707,23 +26915,23 @@ function HistoryOverlay() {
26707
26915
  borderColor: tokens.border,
26708
26916
  width: 110,
26709
26917
  children: [
26710
- /* @__PURE__ */ jsx8("box", {
26918
+ /* @__PURE__ */ jsx9("box", {
26711
26919
  flexGrow: 1,
26712
26920
  flexDirection: "column",
26713
26921
  style: { minHeight: 30, maxHeight: 46 },
26714
- children: entries.length === 0 ? /* @__PURE__ */ jsx8("text", {
26922
+ children: entries.length === 0 ? /* @__PURE__ */ jsx9("text", {
26715
26923
  fg: tokens.dim,
26716
26924
  children: "No actions recorded yet."
26717
- }) : /* @__PURE__ */ jsx8("scrollbox", {
26925
+ }) : /* @__PURE__ */ jsx9("scrollbox", {
26718
26926
  flexGrow: 1,
26719
- children: entries.map((entry, i2) => /* @__PURE__ */ jsx8(HistoryEntryLine, {
26927
+ children: entries.map((entry, i2) => /* @__PURE__ */ jsx9(HistoryEntryLine, {
26720
26928
  entry
26721
26929
  }, i2))
26722
26930
  })
26723
26931
  }),
26724
- /* @__PURE__ */ jsx8("box", {
26932
+ /* @__PURE__ */ jsx9("box", {
26725
26933
  marginTop: 1,
26726
- children: /* @__PURE__ */ jsx8("text", {
26934
+ children: /* @__PURE__ */ jsx9("text", {
26727
26935
  fg: tokens.dim,
26728
26936
  children: "Press any key to close · newest first"
26729
26937
  })
@@ -26738,28 +26946,28 @@ var init_HistoryOverlay = __esm(() => {
26738
26946
  });
26739
26947
 
26740
26948
  // 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";
26949
+ import { useState as useState4 } from "react";
26950
+ import { jsx as jsx10, jsxs as jsxs8 } from "@opentui/react/jsx-runtime";
26743
26951
  function InputModal({ title, placeholder, initialValue, errorMessage: errorMessage2, onSubmit }) {
26744
- const [value, setValue] = useState3(initialValue ?? "");
26745
- return /* @__PURE__ */ jsx9(Overlay, {
26952
+ const [value, setValue] = useState4(initialValue ?? "");
26953
+ return /* @__PURE__ */ jsx10(Overlay, {
26746
26954
  title,
26747
26955
  borderColor: tokens.accent,
26748
26956
  children: /* @__PURE__ */ jsxs8("box", {
26749
26957
  flexDirection: "column",
26750
26958
  children: [
26751
- /* @__PURE__ */ jsx9("input", {
26959
+ /* @__PURE__ */ jsx10("input", {
26752
26960
  focused: true,
26753
26961
  value: initialValue ?? "",
26754
26962
  placeholder,
26755
26963
  onInput: (val) => setValue(val),
26756
26964
  onSubmit: () => onSubmit(value)
26757
26965
  }),
26758
- errorMessage2 ? /* @__PURE__ */ jsx9("text", {
26966
+ errorMessage2 ? /* @__PURE__ */ jsx10("text", {
26759
26967
  fg: tokens.error,
26760
26968
  style: { marginTop: 1 },
26761
26969
  children: errorMessage2
26762
- }) : /* @__PURE__ */ jsx9("text", {
26970
+ }) : /* @__PURE__ */ jsx10("text", {
26763
26971
  fg: tokens.dim,
26764
26972
  style: { marginTop: 1 },
26765
26973
  children: "Press Enter to submit, empty to cancel"
@@ -26774,11 +26982,31 @@ var init_InputModal = __esm(() => {
26774
26982
  });
26775
26983
 
26776
26984
  // src/tui/components/ChoiceModal.tsx
26777
- import { useState as useState4 } from "react";
26985
+ import { useState as useState5 } from "react";
26778
26986
  import { useKeyboard } from "@opentui/react";
26779
- import { jsx as jsx10, jsxs as jsxs9 } from "@opentui/react/jsx-runtime";
26987
+ import { jsx as jsx11, jsxs as jsxs9 } from "@opentui/react/jsx-runtime";
26988
+ function ChoiceOptionRow({ option, index, isSelected, onSelect }) {
26989
+ const tap = useTapHandler(() => onSelect(option.value));
26990
+ return /* @__PURE__ */ jsxs9("box", {
26991
+ flexDirection: "column",
26992
+ backgroundColor: isSelected ? tokens.selectionBg : undefined,
26993
+ ...tap,
26994
+ children: [
26995
+ /* @__PURE__ */ jsx11("text", {
26996
+ children: /* @__PURE__ */ jsx11("span", {
26997
+ fg: isSelected ? tokens.bright : tokens.fg,
26998
+ children: `${index + 1}. ${option.label}`
26999
+ })
27000
+ }),
27001
+ option.desc && /* @__PURE__ */ jsx11("text", {
27002
+ fg: tokens.dim,
27003
+ children: ` ${option.desc}`
27004
+ })
27005
+ ]
27006
+ });
27007
+ }
26780
27008
  function ChoiceModal({ title, options, initialIndex = 0, onSubmit, onCancel }) {
26781
- const [index, setIndex] = useState4(Math.min(Math.max(initialIndex, 0), options.length - 1));
27009
+ const [index, setIndex] = useState5(Math.min(Math.max(initialIndex, 0), options.length - 1));
26782
27010
  useKeyboard((key) => {
26783
27011
  if (key.name === "escape") {
26784
27012
  onCancel();
@@ -26805,27 +27033,17 @@ function ChoiceModal({ title, options, initialIndex = 0, onSubmit, onCancel }) {
26805
27033
  onSubmit(option.value);
26806
27034
  }
26807
27035
  });
26808
- return /* @__PURE__ */ jsx10(Overlay, {
27036
+ return /* @__PURE__ */ jsx11(Overlay, {
26809
27037
  title,
26810
27038
  borderColor: tokens.accent,
26811
27039
  children: /* @__PURE__ */ jsxs9("box", {
26812
27040
  flexDirection: "column",
26813
27041
  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
- ]
27042
+ options.map((option, i2) => /* @__PURE__ */ jsx11(ChoiceOptionRow, {
27043
+ option,
27044
+ index: i2,
27045
+ isSelected: i2 === index,
27046
+ onSelect: onSubmit
26829
27047
  }, option.value)),
26830
27048
  /* @__PURE__ */ jsxs9("text", {
26831
27049
  fg: tokens.dim,
@@ -26833,7 +27051,7 @@ function ChoiceModal({ title, options, initialIndex = 0, onSubmit, onCancel }) {
26833
27051
  children: [
26834
27052
  "↑/↓ to choose · Enter to confirm · 1-",
26835
27053
  options.length,
26836
- " quick pick · Esc to cancel"
27054
+ " quick pick · Esc to cancel · click to select"
26837
27055
  ]
26838
27056
  })
26839
27057
  ]
@@ -26843,18 +27061,19 @@ function ChoiceModal({ title, options, initialIndex = 0, onSubmit, onCancel }) {
26843
27061
  var init_ChoiceModal = __esm(() => {
26844
27062
  init_Overlay();
26845
27063
  init_theme();
27064
+ init_use_tap();
26846
27065
  });
26847
27066
 
26848
27067
  // src/tui/components/ConfigOverlay.tsx
26849
- import { useState as useState5, useEffect as useEffect4 } from "react";
27068
+ import { useState as useState6, useEffect as useEffect4 } from "react";
26850
27069
  import { useKeyboard as useKeyboard2 } from "@opentui/react";
26851
- import { jsx as jsx11, jsxs as jsxs10, Fragment as Fragment3 } from "@opentui/react/jsx-runtime";
27070
+ import { jsx as jsx12, jsxs as jsxs10, Fragment as Fragment2 } from "@opentui/react/jsx-runtime";
26852
27071
  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" });
27072
+ const [config2, setConfig] = useState6(null);
27073
+ const [viewState, setViewState] = useState6({ type: "main" });
27074
+ const [selectedIndex, setSelectedIndex] = useState6(1);
27075
+ const [windowStart, setWindowStart] = useState6(0);
27076
+ const [editState, setEditState] = useState6({ type: "none" });
26858
27077
  useEffect4(() => {
26859
27078
  try {
26860
27079
  setConfig(loadConfig());
@@ -27111,9 +27330,9 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
27111
27330
  hint = "Remove this repository from config";
27112
27331
  }
27113
27332
  }
27114
- return /* @__PURE__ */ jsxs10(Fragment3, {
27333
+ return /* @__PURE__ */ jsxs10(Fragment2, {
27115
27334
  children: [
27116
- /* @__PURE__ */ jsx11(Overlay, {
27335
+ /* @__PURE__ */ jsx12(Overlay, {
27117
27336
  title: "Configuration",
27118
27337
  borderColor: tokens.border,
27119
27338
  children: /* @__PURE__ */ jsxs10("box", {
@@ -27121,7 +27340,7 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
27121
27340
  width: "100%",
27122
27341
  height: 24,
27123
27342
  children: [
27124
- /* @__PURE__ */ jsx11("box", {
27343
+ /* @__PURE__ */ jsx12("box", {
27125
27344
  flexDirection: "column",
27126
27345
  flexGrow: 1,
27127
27346
  overflow: "hidden",
@@ -27129,7 +27348,7 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
27129
27348
  const i2 = windowStart + idx;
27130
27349
  const isSelected = i2 === selectedIndex;
27131
27350
  if (row.type === "header") {
27132
- return /* @__PURE__ */ jsx11("text", {
27351
+ return /* @__PURE__ */ jsx12("text", {
27133
27352
  fg: tokens.dim,
27134
27353
  style: { marginTop: i2 === 0 ? 0 : 1 },
27135
27354
  children: row.label
@@ -27171,7 +27390,7 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
27171
27390
  labelText
27172
27391
  ]
27173
27392
  }),
27174
- valText && /* @__PURE__ */ jsx11("text", {
27393
+ valText && /* @__PURE__ */ jsx12("text", {
27175
27394
  fg: isSelected ? tokens.accent : tokens.dim,
27176
27395
  bg: isSelected ? tokens.selectionBg : undefined,
27177
27396
  flexShrink: 1,
@@ -27181,14 +27400,14 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
27181
27400
  }, i2);
27182
27401
  })
27183
27402
  }),
27184
- /* @__PURE__ */ jsx11("box", {
27403
+ /* @__PURE__ */ jsx12("box", {
27185
27404
  flexDirection: "row",
27186
27405
  border: true,
27187
27406
  borderColor: tokens.border,
27188
27407
  paddingTop: 1,
27189
27408
  marginTop: 1,
27190
27409
  minHeight: 3,
27191
- children: /* @__PURE__ */ jsx11("text", {
27410
+ children: /* @__PURE__ */ jsx12("text", {
27192
27411
  fg: tokens.dim,
27193
27412
  children: hint
27194
27413
  })
@@ -27196,14 +27415,14 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
27196
27415
  ]
27197
27416
  })
27198
27417
  }),
27199
- editState.type === "input" && /* @__PURE__ */ jsx11(InputModal, {
27418
+ editState.type === "input" && /* @__PURE__ */ jsx12(InputModal, {
27200
27419
  title: editState.title,
27201
27420
  initialValue: editState.currentValue,
27202
27421
  placeholder: "Enter value...",
27203
27422
  errorMessage: editState.error,
27204
27423
  onSubmit: handleInputSubmit
27205
27424
  }),
27206
- editState.type === "confirm_remove" && /* @__PURE__ */ jsx11(ConfirmModal, {
27425
+ editState.type === "confirm_remove" && /* @__PURE__ */ jsx12(ConfirmModal, {
27207
27426
  title: "Remove Repo",
27208
27427
  message: `Remove repo ${editState.repo} from config?`
27209
27428
  })
@@ -27220,26 +27439,29 @@ var init_ConfigOverlay = __esm(() => {
27220
27439
  });
27221
27440
 
27222
27441
  // src/tui/components/WarningsOverlay.tsx
27223
- import { jsx as jsx12, jsxs as jsxs11 } from "@opentui/react/jsx-runtime";
27224
- function WarningsOverlay({ warnings }) {
27442
+ import { jsx as jsx13, jsxs as jsxs11 } from "@opentui/react/jsx-runtime";
27443
+ function WarningsOverlay({ warnings, onAcknowledge, onClose }) {
27444
+ const ackTap = useTapHandler(() => onAcknowledge?.());
27445
+ const closeTap = useTapHandler(() => onClose?.());
27225
27446
  return /* @__PURE__ */ jsxs11(Overlay, {
27226
27447
  title: `Warnings (${warnings.length})`,
27227
27448
  borderColor: tokens.warning,
27449
+ width: 80,
27228
27450
  children: [
27229
- /* @__PURE__ */ jsx12("box", {
27451
+ /* @__PURE__ */ jsx13("box", {
27230
27452
  flexDirection: "column",
27231
- style: { maxHeight: 30 },
27232
- children: /* @__PURE__ */ jsx12("scrollbox", {
27453
+ style: { maxHeight: 28 },
27454
+ children: /* @__PURE__ */ jsx13("scrollbox", {
27233
27455
  flexGrow: 1,
27234
27456
  children: warnings.map((warning, i2) => /* @__PURE__ */ jsxs11("box", {
27235
27457
  flexDirection: "column",
27236
27458
  style: { marginBottom: 1 },
27237
27459
  children: [
27238
- /* @__PURE__ */ jsx12("text", {
27460
+ /* @__PURE__ */ jsx13("text", {
27239
27461
  fg: tokens.warning,
27240
27462
  children: `⚠ ${warning.repoName}`
27241
27463
  }),
27242
- wrapText(warning.message, 56).map((line, j) => /* @__PURE__ */ jsx12("text", {
27464
+ wrapText(warning.message, 72).map((line, j) => /* @__PURE__ */ jsx13("text", {
27243
27465
  fg: tokens.fg,
27244
27466
  children: ` ${line}`
27245
27467
  }, j))
@@ -27247,9 +27469,46 @@ function WarningsOverlay({ warnings }) {
27247
27469
  }, i2))
27248
27470
  })
27249
27471
  }),
27250
- /* @__PURE__ */ jsx12("text", {
27472
+ /* @__PURE__ */ jsxs11("box", {
27473
+ flexDirection: "row",
27474
+ gap: 3,
27475
+ style: { marginTop: 2 },
27476
+ justifyContent: "center",
27477
+ children: [
27478
+ /* @__PURE__ */ jsx13("box", {
27479
+ ...ackTap,
27480
+ children: /* @__PURE__ */ jsxs11("text", {
27481
+ children: [
27482
+ /* @__PURE__ */ jsx13("span", {
27483
+ fg: tokens.dim,
27484
+ children: "[a] "
27485
+ }),
27486
+ /* @__PURE__ */ jsx13("span", {
27487
+ fg: tokens.success,
27488
+ children: "Acknowledge"
27489
+ })
27490
+ ]
27491
+ })
27492
+ }),
27493
+ /* @__PURE__ */ jsx13("box", {
27494
+ ...closeTap,
27495
+ children: /* @__PURE__ */ jsxs11("text", {
27496
+ children: [
27497
+ /* @__PURE__ */ jsx13("span", {
27498
+ fg: tokens.dim,
27499
+ children: "[esc] "
27500
+ }),
27501
+ /* @__PURE__ */ jsx13("span", {
27502
+ children: "Close"
27503
+ })
27504
+ ]
27505
+ })
27506
+ })
27507
+ ]
27508
+ }),
27509
+ /* @__PURE__ */ jsx13("text", {
27251
27510
  style: { marginTop: 1, fg: tokens.dim },
27252
- children: "Press any key to close"
27511
+ children: "Press a to acknowledge and clear errors"
27253
27512
  })
27254
27513
  ]
27255
27514
  });
@@ -27258,12 +27517,13 @@ var init_WarningsOverlay = __esm(() => {
27258
27517
  init_Overlay();
27259
27518
  init_theme();
27260
27519
  init_utils();
27520
+ init_use_tap();
27261
27521
  });
27262
27522
 
27263
27523
  // src/tui/hooks/useSpinnerFrame.ts
27264
- import { useEffect as useEffect5, useState as useState6 } from "react";
27524
+ import { useEffect as useEffect5, useState as useState7 } from "react";
27265
27525
  function useSpinnerFrame(active) {
27266
- const [index, setIndex] = useState6(0);
27526
+ const [index, setIndex] = useState7(0);
27267
27527
  useEffect5(() => {
27268
27528
  if (!active)
27269
27529
  return;
@@ -27278,10 +27538,10 @@ var init_useSpinnerFrame = __esm(() => {
27278
27538
  });
27279
27539
 
27280
27540
  // src/tui/components/App.tsx
27281
- import { useState as useState7, useEffect as useEffect6, useMemo, useRef as useRef4, useCallback as useCallback3 } from "react";
27541
+ import { useState as useState8, useEffect as useEffect6, useMemo, useRef as useRef5, useCallback as useCallback4 } from "react";
27282
27542
  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";
27543
+ import { useKeyboard as useKeyboard3, useRenderer as useRenderer2, useSelectionHandler, useTerminalDimensions } from "@opentui/react";
27544
+ import { jsx as jsx14, jsxs as jsxs12 } from "@opentui/react/jsx-runtime";
27285
27545
  function getWorktreePathFor(repoName, branch) {
27286
27546
  try {
27287
27547
  const config2 = loadConfig();
@@ -27292,31 +27552,39 @@ function getWorktreePathFor(repoName, branch) {
27292
27552
  }
27293
27553
  }
27294
27554
  function App({ opts }) {
27295
- const renderer = useRenderer();
27296
- 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) => {
27555
+ const renderer = useRenderer2();
27556
+ const { blocks, loading, refreshing, error: error52, warnings, lastRefreshed, pendingRepos, refresh, clearWarnings } = useWorktrees(opts);
27557
+ const [selectedIndex, setSelectedIndex] = useState8(0);
27558
+ const [modal, setModal] = useState8({ type: "none" });
27559
+ const [actionMessage, setActionMessage] = useState8();
27560
+ const messageTimer = useRef5(null);
27561
+ const [ops, setOps] = useState8([]);
27562
+ const [failedLogs, setFailedLogs] = useState8([]);
27563
+ const nextOpId = useRef5(1);
27564
+ const [createModal, setCreateModal] = useState8(false);
27565
+ const [createError, setCreateError] = useState8();
27566
+ const [createBaseModal, setCreateBaseModal] = useState8(null);
27567
+ const [createBaseError, setCreateBaseError] = useState8();
27568
+ const [createDepsChoice, setCreateDepsChoice] = useState8(null);
27569
+ const [pullPrModal, setPullPrModal] = useState8(false);
27570
+ const [pullPrError, setPullPrError] = useState8();
27571
+ const [pullForceChoice, setPullForceChoice] = useState8(null);
27572
+ const [renameModal, setRenameModal] = useState8(false);
27573
+ const [renameError, setRenameError] = useState8();
27574
+ const [configOpen, setConfigOpen] = useState8(false);
27575
+ const [refreshScopes, setRefreshScopes] = useState8([]);
27576
+ const [filterText, setFilterText] = useState8("");
27577
+ const [isFiltering, setIsFiltering] = useState8(false);
27578
+ const [selection, setSelection] = useState8(new Set);
27579
+ const [splitRatio, setSplitRatio] = useState8(0.6);
27580
+ const [isResizing, setIsResizing] = useState8(false);
27581
+ const { width: termWidth } = useTerminalDimensions();
27582
+ const totalWidth = termWidth || renderer.width || 80;
27583
+ useEffect6(() => {
27584
+ if (termWidth)
27585
+ setSplitRatio((prev) => clampSplitRatio(termWidth, prev));
27586
+ }, [termWidth]);
27587
+ const doRefresh = useCallback4(async (scope) => {
27320
27588
  const targets = scope ?? [
27321
27589
  ...new Set([...blocks.map((b) => b.repoName), ...pendingRepos])
27322
27590
  ];
@@ -27332,13 +27600,17 @@ function App({ opts }) {
27332
27600
  setModal({ type: "error", message: error52 });
27333
27601
  }
27334
27602
  }, [error52]);
27335
- const flash = useCallback3((message, ms = 3000) => {
27603
+ const flash = useCallback4((message, ms = 3000) => {
27336
27604
  if (messageTimer.current)
27337
27605
  clearTimeout(messageTimer.current);
27338
27606
  setActionMessage(message);
27339
27607
  messageTimer.current = setTimeout(() => setActionMessage(undefined), ms);
27340
27608
  }, []);
27341
- const copySelectedText = useCallback3((warnOnEmpty) => {
27609
+ const copySelectedText = useCallback4((warnOnEmpty) => {
27610
+ if (isResizing) {
27611
+ renderer.clearSelection();
27612
+ return;
27613
+ }
27342
27614
  const text = renderer.getSelection()?.getSelectedText() ?? "";
27343
27615
  if (!text) {
27344
27616
  if (warnOnEmpty)
@@ -27346,7 +27618,7 @@ function App({ opts }) {
27346
27618
  return;
27347
27619
  }
27348
27620
  copyTextToClipboard(renderer, text).then((ok) => flash(ok ? `Copied ${text.length} character${text.length !== 1 ? "s" : ""}` : "Copy failed"));
27349
- }, [renderer, flash]);
27621
+ }, [renderer, flash, isResizing]);
27350
27622
  useSelectionHandler(() => copySelectedText(false));
27351
27623
  const busyRepos = useMemo(() => new Set(ops.flatMap((o2) => o2.repoNames)), [ops]);
27352
27624
  const busyRowPaths = useMemo(() => new Set(ops.map((o2) => o2.rowPath).filter((p) => p !== undefined)), [ops]);
@@ -27508,6 +27780,159 @@ function App({ opts }) {
27508
27780
  args.push("--force");
27509
27781
  executeOp(op, args, [repoName]);
27510
27782
  };
27783
+ const handleHintClick = useCallback4((key) => {
27784
+ if (key === "c") {
27785
+ setConfigOpen(true);
27786
+ return;
27787
+ }
27788
+ if (key === "?") {
27789
+ setModal({ type: "help" });
27790
+ return;
27791
+ }
27792
+ if (key === "H") {
27793
+ setModal({ type: "history" });
27794
+ return;
27795
+ }
27796
+ if (key === "r") {
27797
+ doRefresh();
27798
+ return;
27799
+ }
27800
+ if (key === "e" && warnings.length > 0) {
27801
+ setModal({ type: "warnings" });
27802
+ return;
27803
+ }
27804
+ if (key === "n") {
27805
+ if (!selectedRow)
27806
+ return;
27807
+ setCreateModal(true);
27808
+ setCreateError(undefined);
27809
+ return;
27810
+ }
27811
+ if (key === "m") {
27812
+ const target = selectedRow;
27813
+ if (!target || target.isMainCheckout || !target.branch || target.branch === "(detached)") {
27814
+ if (target?.isMainCheckout)
27815
+ flash("Cannot rename main checkout");
27816
+ else if (target && (!target.branch || target.branch === "(detached)"))
27817
+ flash("Cannot rename detached worktree");
27818
+ return;
27819
+ }
27820
+ const conflict = findConflict([target]);
27821
+ if (conflict) {
27822
+ flash(conflict);
27823
+ return;
27824
+ }
27825
+ setRenameModal(true);
27826
+ setRenameError(undefined);
27827
+ return;
27828
+ }
27829
+ if (key === "f") {
27830
+ const targets = getSelectedRows();
27831
+ if (targets.length > 0)
27832
+ startFetch(targets);
27833
+ return;
27834
+ }
27835
+ if (key === "o") {
27836
+ const targets = getSelectedRows();
27837
+ if (targets.length === 0)
27838
+ return;
27839
+ if (targets.length > 1) {
27840
+ flash("Cannot open multiple worktrees");
27841
+ return;
27842
+ }
27843
+ const target = targets[0];
27844
+ const conflict = findConflict(targets);
27845
+ if (conflict) {
27846
+ flash(conflict);
27847
+ return;
27848
+ }
27849
+ const op = {
27850
+ id: nextOpId.current++,
27851
+ kind: "open",
27852
+ repoNames: [target.repoName],
27853
+ rowPath: target.path,
27854
+ label: target.branch,
27855
+ title: `Open ${target.branch}`,
27856
+ status: "queued",
27857
+ lines: []
27858
+ };
27859
+ setOps((prev) => [...prev, op]);
27860
+ executeOp(op, ["open", target.branch, "--repo", target.repoName], null);
27861
+ return;
27862
+ }
27863
+ if (key === "i") {
27864
+ const targets = getSelectedRows();
27865
+ if (targets.length === 0)
27866
+ return;
27867
+ const conflict = findConflict(targets);
27868
+ if (conflict) {
27869
+ flash(conflict);
27870
+ return;
27871
+ }
27872
+ startBatchActions("install", targets, (r) => r.isMainCheckout ? ["deps", "--repo", r.repoName, "--install"] : ["deps", r.branch, "--repo", r.repoName, "--install"]);
27873
+ return;
27874
+ }
27875
+ if (key === "p") {
27876
+ const targets = getSelectedRows();
27877
+ if (targets.length === 0)
27878
+ return;
27879
+ const conflict = findConflict(targets);
27880
+ if (conflict) {
27881
+ flash(conflict);
27882
+ return;
27883
+ }
27884
+ startBatchActions("pull", targets, (r) => ["pull-branch", r.branch, "--repo", r.repoName]);
27885
+ return;
27886
+ }
27887
+ if (key === "b") {
27888
+ const targets = getSelectedRows();
27889
+ if (targets.length === 0)
27890
+ return;
27891
+ if (targets.some((r) => r.isMainCheckout)) {
27892
+ flash("Cannot rebase main checkout");
27893
+ return;
27894
+ }
27895
+ const conflict = findConflict(targets);
27896
+ if (conflict) {
27897
+ flash(conflict);
27898
+ return;
27899
+ }
27900
+ setModal({ type: "confirm_rebase", rows: targets });
27901
+ return;
27902
+ }
27903
+ if (key === "d") {
27904
+ const targets = getSelectedRows();
27905
+ if (targets.length === 0)
27906
+ return;
27907
+ if (targets.some((r) => r.isMainCheckout)) {
27908
+ flash("Cannot remove main checkout");
27909
+ return;
27910
+ }
27911
+ const conflict = findConflict(targets);
27912
+ if (conflict) {
27913
+ flash(conflict);
27914
+ return;
27915
+ }
27916
+ setModal({ type: "confirm_remove", rows: targets });
27917
+ return;
27918
+ }
27919
+ if (key === "s") {
27920
+ const targets = getSelectedRows();
27921
+ if (targets.length === 0)
27922
+ return;
27923
+ if (targets.some((r) => r.isMainCheckout)) {
27924
+ flash("Cannot sync main checkout");
27925
+ return;
27926
+ }
27927
+ const conflict = findConflict(targets);
27928
+ if (conflict) {
27929
+ flash(conflict);
27930
+ return;
27931
+ }
27932
+ setModal({ type: "confirm_sync", rows: targets });
27933
+ return;
27934
+ }
27935
+ }, [selectedRow, selection, warnings, busyRepos, busyRowPaths, doRefresh, flash, blocks]);
27511
27936
  useKeyboard3((key) => {
27512
27937
  if (isFiltering) {
27513
27938
  if (key.name === "escape") {
@@ -27558,7 +27983,20 @@ function App({ opts }) {
27558
27983
  return;
27559
27984
  }
27560
27985
  if (modal.type !== "none") {
27561
- if (modal.type === "error" || modal.type === "help" || modal.type === "history" || modal.type === "warnings") {
27986
+ if (modal.type === "warnings") {
27987
+ if (key.name === "a") {
27988
+ clearWarnings();
27989
+ setModal({ type: "none" });
27990
+ return;
27991
+ }
27992
+ if (key.name === "escape" || key.name === "q" || key.name === "enter" || key.name === "return") {
27993
+ setModal({ type: "none" });
27994
+ return;
27995
+ }
27996
+ setModal({ type: "none" });
27997
+ return;
27998
+ }
27999
+ if (modal.type === "error" || modal.type === "help" || modal.type === "history") {
27562
28000
  setModal({ type: "none" });
27563
28001
  if (modal.type === "error" && error52) {
27564
28002
  renderer.destroy();
@@ -27848,6 +28286,9 @@ function App({ opts }) {
27848
28286
  })();
27849
28287
  }
27850
28288
  });
28289
+ const rawLeft = Math.floor(totalWidth * splitRatio);
28290
+ const leftCols = Math.max(20, Math.min(totalWidth - 20 - DIVIDER_WIDTH, rawLeft));
28291
+ const rightCols = Math.max(20, totalWidth - leftCols - DIVIDER_WIDTH);
27851
28292
  return /* @__PURE__ */ jsxs12("box", {
27852
28293
  flexDirection: "column",
27853
28294
  width: "100%",
@@ -27858,16 +28299,34 @@ function App({ opts }) {
27858
28299
  width: "100%",
27859
28300
  flexGrow: 1,
27860
28301
  children: [
27861
- /* @__PURE__ */ jsx13(WorktreeTable, {
27862
- blocks: displayBlocks,
27863
- selectedIndex,
27864
- selection,
27865
- frame: spinnerFrame,
27866
- repoVerbs,
27867
- rowVerbs
28302
+ /* @__PURE__ */ jsx14("box", {
28303
+ width: leftCols,
28304
+ height: "100%",
28305
+ flexDirection: "column",
28306
+ children: /* @__PURE__ */ jsx14(WorktreeTable, {
28307
+ blocks: displayBlocks,
28308
+ selectedIndex,
28309
+ selection,
28310
+ frame: spinnerFrame,
28311
+ repoVerbs,
28312
+ rowVerbs,
28313
+ onRowClick: (idx) => setSelectedIndex(idx),
28314
+ onToggleSelect: (path37) => setSelection((prev) => toggleSelection(prev, path37))
28315
+ })
28316
+ }),
28317
+ /* @__PURE__ */ jsx14(Divider, {
28318
+ splitRatio,
28319
+ totalWidth,
28320
+ onChange: setSplitRatio,
28321
+ onDraggingChange: setIsResizing
27868
28322
  }),
27869
- /* @__PURE__ */ jsx13(DetailPane, {
27870
- selectedRow
28323
+ /* @__PURE__ */ jsx14("box", {
28324
+ width: rightCols,
28325
+ height: "100%",
28326
+ flexDirection: "column",
28327
+ children: /* @__PURE__ */ jsx14(DetailPane, {
28328
+ selectedRow
28329
+ })
27871
28330
  })
27872
28331
  ]
27873
28332
  }),
@@ -27877,35 +28336,56 @@ function App({ opts }) {
27877
28336
  border: true,
27878
28337
  borderColor: "magenta",
27879
28338
  children: [
27880
- /* @__PURE__ */ jsx13("text", {
28339
+ /* @__PURE__ */ jsx14("text", {
27881
28340
  children: "filter: "
27882
28341
  }),
27883
- /* @__PURE__ */ jsx13("input", {
28342
+ /* @__PURE__ */ jsx14("input", {
27884
28343
  focused: true,
27885
28344
  placeholder: "Type to filter...",
27886
28345
  onInput: (v) => setFilterText(v)
27887
28346
  })
27888
28347
  ]
27889
28348
  }),
27890
- /* @__PURE__ */ jsx13(Footer, {
28349
+ /* @__PURE__ */ jsx14(Footer, {
27891
28350
  loading: loading || refreshing,
27892
28351
  lastRefreshed,
27893
28352
  errorCount: warnings.length,
27894
28353
  message: actionMessage,
27895
28354
  busyText: latestOp ? `${capitalize(VERBS[latestOp.kind])} ${latestOp.label}…${runningOps.length > 1 ? ` (+${runningOps.length - 1})` : ""}` : undefined,
27896
28355
  spinnerFrame,
27897
- filter: filterText ? { term: filterText, matches: flatRows.length, total: totalRows } : undefined
28356
+ filter: filterText ? { term: filterText, matches: flatRows.length, total: totalRows } : undefined,
28357
+ onHintClick: handleHintClick,
28358
+ onErrorClick: () => setModal({ type: "warnings" })
27898
28359
  }),
27899
- modal.type === "help" && /* @__PURE__ */ jsx13(HelpOverlay, {}),
27900
- modal.type === "history" && /* @__PURE__ */ jsx13(HistoryOverlay, {}),
27901
- modal.type === "warnings" && /* @__PURE__ */ jsx13(WarningsOverlay, {
27902
- warnings
28360
+ modal.type === "help" && /* @__PURE__ */ jsx14(HelpOverlay, {}),
28361
+ modal.type === "history" && /* @__PURE__ */ jsx14(HistoryOverlay, {}),
28362
+ modal.type === "warnings" && /* @__PURE__ */ jsx14(WarningsOverlay, {
28363
+ warnings,
28364
+ onAcknowledge: () => {
28365
+ clearWarnings();
28366
+ setModal({ type: "none" });
28367
+ },
28368
+ onClose: () => setModal({ type: "none" })
27903
28369
  }),
27904
- modal.type === "error" && /* @__PURE__ */ jsx13(ConfirmModal, {
28370
+ modal.type === "error" && /* @__PURE__ */ jsx14(ConfirmModal, {
27905
28371
  title: "Error",
27906
- message: modal.message
28372
+ message: modal.message,
28373
+ onConfirm: () => {
28374
+ setModal({ type: "none" });
28375
+ if (error52) {
28376
+ renderer.destroy();
28377
+ process.exit(1);
28378
+ }
28379
+ },
28380
+ onCancel: () => {
28381
+ setModal({ type: "none" });
28382
+ if (error52) {
28383
+ renderer.destroy();
28384
+ process.exit(1);
28385
+ }
28386
+ }
27907
28387
  }),
27908
- modal.type === "confirm_remove" && /* @__PURE__ */ jsx13(ConfirmModal, {
28388
+ modal.type === "confirm_remove" && /* @__PURE__ */ jsx14(ConfirmModal, {
27909
28389
  title: `Remove ${modal.rows.length} Worktree(s)`,
27910
28390
  message: (() => {
27911
28391
  const count2 = modal.rows.length;
@@ -27920,17 +28400,41 @@ function App({ opts }) {
27920
28400
  }
27921
28401
  return lines.join(`
27922
28402
  `);
27923
- })()
28403
+ })(),
28404
+ onConfirm: () => {
28405
+ const rows = modal.rows;
28406
+ setModal({ type: "none" });
28407
+ startBatchActions("remove", rows, (r) => {
28408
+ const args = ["remove", r.branch, "--repo", r.repoName, "--yes"];
28409
+ const needsForce = r.dirtyFiles.length > 0 || !existsSync4(r.path);
28410
+ if (needsForce)
28411
+ args.push("--force");
28412
+ return args;
28413
+ });
28414
+ },
28415
+ onCancel: () => setModal({ type: "none" })
27924
28416
  }),
27925
- modal.type === "confirm_rebase" && /* @__PURE__ */ jsx13(ConfirmModal, {
28417
+ modal.type === "confirm_rebase" && /* @__PURE__ */ jsx14(ConfirmModal, {
27926
28418
  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"}?`
28419
+ message: `Are you sure you want to fetch and rebase ${modal.rows.length === 1 ? modal.rows[0]?.branch : modal.rows.length + " worktrees"}?`,
28420
+ onConfirm: () => {
28421
+ const rows = modal.rows;
28422
+ setModal({ type: "none" });
28423
+ startBatchActions("rebase", rows, (r) => ["rebase", r.branch, "--repo", r.repoName]);
28424
+ },
28425
+ onCancel: () => setModal({ type: "none" })
27928
28426
  }),
27929
- modal.type === "confirm_sync" && /* @__PURE__ */ jsx13(ConfirmModal, {
28427
+ modal.type === "confirm_sync" && /* @__PURE__ */ jsx14(ConfirmModal, {
27930
28428
  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"}?`
28429
+ message: `Are you sure you want to sync ${modal.rows.length === 1 ? modal.rows[0]?.branch : modal.rows.length + " worktrees"}?`,
28430
+ onConfirm: () => {
28431
+ const rows = modal.rows;
28432
+ setModal({ type: "none" });
28433
+ startBatchActions("sync", rows, (r) => ["sync", r.branch, "--repo", r.repoName]);
28434
+ },
28435
+ onCancel: () => setModal({ type: "none" })
27932
28436
  }),
27933
- createModal && /* @__PURE__ */ jsx13(InputModal, {
28437
+ createModal && /* @__PURE__ */ jsx14(InputModal, {
27934
28438
  title: `New worktree branch in ${selectedRow?.repoName ?? ""}`,
27935
28439
  placeholder: "Branch name (empty to cancel)",
27936
28440
  errorMessage: createError,
@@ -27957,7 +28461,7 @@ function App({ opts }) {
27957
28461
  setCreateBaseModal({ branch, repoName });
27958
28462
  }
27959
28463
  }),
27960
- createBaseModal && /* @__PURE__ */ jsx13(InputModal, {
28464
+ createBaseModal && /* @__PURE__ */ jsx14(InputModal, {
27961
28465
  title: `Base ref for ${createBaseModal.branch}`,
27962
28466
  placeholder: "origin/main (empty for default main)",
27963
28467
  errorMessage: createBaseError,
@@ -27973,7 +28477,7 @@ function App({ opts }) {
27973
28477
  setCreateDepsChoice({ branch, repoName, base: base2 || undefined });
27974
28478
  }
27975
28479
  }),
27976
- createDepsChoice && /* @__PURE__ */ jsx13(ChoiceModal, {
28480
+ createDepsChoice && /* @__PURE__ */ jsx14(ChoiceModal, {
27977
28481
  title: `Dependencies for ${createDepsChoice.branch}`,
27978
28482
  options: DEPS_CHOICES,
27979
28483
  onSubmit: (choice) => {
@@ -27983,7 +28487,7 @@ function App({ opts }) {
27983
28487
  },
27984
28488
  onCancel: () => setCreateDepsChoice(null)
27985
28489
  }),
27986
- pullPrModal && /* @__PURE__ */ jsx13(InputModal, {
28490
+ pullPrModal && /* @__PURE__ */ jsx14(InputModal, {
27987
28491
  title: `Pull PR into ${selectedRow?.repoName ?? ""}`,
27988
28492
  placeholder: "https://github.com/owner/repo/pull/123",
27989
28493
  errorMessage: pullPrError,
@@ -28011,7 +28515,7 @@ function App({ opts }) {
28011
28515
  setPullForceChoice({ link, repoName });
28012
28516
  }
28013
28517
  }),
28014
- pullForceChoice && /* @__PURE__ */ jsx13(ChoiceModal, {
28518
+ pullForceChoice && /* @__PURE__ */ jsx14(ChoiceModal, {
28015
28519
  title: `Pull ${pullForceChoice.link.split("/").pop()}?`,
28016
28520
  options: [
28017
28521
  { value: "normal", label: "Pull (skip if exists)", desc: "Fails if branch already exists" },
@@ -28024,7 +28528,7 @@ function App({ opts }) {
28024
28528
  },
28025
28529
  onCancel: () => setPullForceChoice(null)
28026
28530
  }),
28027
- renameModal && selectedRow && /* @__PURE__ */ jsx13(InputModal, {
28531
+ renameModal && selectedRow && /* @__PURE__ */ jsx14(InputModal, {
28028
28532
  title: `Rename branch ${selectedRow.branch}`,
28029
28533
  initialValue: selectedRow.branch,
28030
28534
  placeholder: "New branch name",
@@ -28049,7 +28553,7 @@ function App({ opts }) {
28049
28553
  setModal({ type: "confirm_rename", row: target, to });
28050
28554
  }
28051
28555
  }),
28052
- modal.type === "confirm_rename" && /* @__PURE__ */ jsx13(ConfirmModal, {
28556
+ modal.type === "confirm_rename" && /* @__PURE__ */ jsx14(ConfirmModal, {
28053
28557
  title: "Rename Worktree",
28054
28558
  message: [
28055
28559
  `${modal.row.branch} → ${modal.to}`,
@@ -28060,14 +28564,32 @@ function App({ opts }) {
28060
28564
  "Uncommitted changes and synced files move with it.",
28061
28565
  "Proceed?"
28062
28566
  ].join(`
28063
- `)
28567
+ `),
28568
+ onConfirm: () => {
28569
+ const { row, to } = modal;
28570
+ setModal({ type: "none" });
28571
+ const op = {
28572
+ id: nextOpId.current++,
28573
+ kind: "rename",
28574
+ repoNames: [row.repoName],
28575
+ rowPath: row.path,
28576
+ branch: row.branch,
28577
+ label: `${row.branch} → ${to}`,
28578
+ title: `Rename ${row.branch} → ${to}`,
28579
+ status: "queued",
28580
+ lines: []
28581
+ };
28582
+ setOps((prev) => [...prev, op]);
28583
+ executeOp(op, ["rename", row.branch, to, "--repo", row.repoName], [row.repoName]);
28584
+ },
28585
+ onCancel: () => setModal({ type: "none" })
28064
28586
  }),
28065
- configOpen && /* @__PURE__ */ jsx13(ConfigOverlay, {
28587
+ configOpen && /* @__PURE__ */ jsx14(ConfigOverlay, {
28066
28588
  onClose: () => setConfigOpen(false),
28067
28589
  onSaved: () => void doRefresh(),
28068
28590
  onError: (msg) => setModal({ type: "error", message: msg })
28069
28591
  }),
28070
- failedLogs.length > 0 && /* @__PURE__ */ jsx13(ActionLogModal, {
28592
+ failedLogs.length > 0 && /* @__PURE__ */ jsx14(ActionLogModal, {
28071
28593
  title: failedLogs[0].title,
28072
28594
  lines: failedLogs[0].lines,
28073
28595
  done: true,
@@ -28083,6 +28605,7 @@ var init_App = __esm(() => {
28083
28605
  init_WorktreeTable();
28084
28606
  init_DetailPane();
28085
28607
  init_Footer();
28608
+ init_Divider();
28086
28609
  init_HelpOverlay();
28087
28610
  init_ConfirmModal();
28088
28611
  init_git();
@@ -28122,14 +28645,14 @@ __export(exports_tui, {
28122
28645
  });
28123
28646
  import { createCliRenderer, TextTableRenderable } from "@opentui/core";
28124
28647
  import { createRoot, extend as extend2 } from "@opentui/react";
28125
- import { jsx as jsx14 } from "@opentui/react/jsx-runtime";
28648
+ import { jsx as jsx15 } from "@opentui/react/jsx-runtime";
28126
28649
  async function runTerminal(opts) {
28127
28650
  const renderer = await createCliRenderer({
28128
28651
  exitOnCtrlC: false
28129
28652
  });
28130
28653
  const root = createRoot(renderer);
28131
28654
  try {
28132
- root.render(/* @__PURE__ */ jsx14(App, {
28655
+ root.render(/* @__PURE__ */ jsx15(App, {
28133
28656
  opts
28134
28657
  }));
28135
28658
  } catch (err) {