flavor-code 1.4.3-beta.2 → 1.4.4-beta.2

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 (34) hide show
  1. package/README.md +27 -2
  2. package/README.zh-CN.md +27 -2
  3. package/dist/agent/loop.d.ts +1 -0
  4. package/dist/{app-G35YBHPR.js → app-C5I2TH7I.js} +399 -109
  5. package/dist/{chunk-FAJ6YDZK.js → chunk-36IDJRLQ.js} +1 -1
  6. package/dist/{chunk-HGTCIHWA.js → chunk-5BDKRA6U.js} +30 -1
  7. package/dist/{chunk-T6RE76D2.js → chunk-IKGT4MDT.js} +35 -23
  8. package/dist/{chunk-K7UGGWJV.js → chunk-JQM2L2YM.js} +1 -1
  9. package/dist/{chunk-6X62CGMA.js → chunk-ONRY3C2L.js} +3 -0
  10. package/dist/{chunk-IDJAHEG6.js → chunk-U4XTQ5PU.js} +392 -188
  11. package/dist/{chunk-SJUIIHIA.js → chunk-X6U6BO5D.js} +1 -1
  12. package/dist/{claude-ink-BE56DB6N.js → claude-ink-F554I3GJ.js} +1 -1
  13. package/dist/{cli-3MF6EL2H.js → cli-V3BLZ5UW.js} +7 -7
  14. package/dist/cli-main.js +16 -16
  15. package/dist/config/protected-file.d.ts +2 -0
  16. package/dist/config/schema.d.ts +3 -0
  17. package/dist/context/global-instructions.d.ts +12 -0
  18. package/dist/context/manager.d.ts +2 -0
  19. package/dist/desktop/main.js +660 -419
  20. package/dist/desktop-renderer/assets/{index-DCqIrF1S.js → index-CjC0Ibiq.js} +2 -2
  21. package/dist/desktop-renderer/assets/{interactive-terminal-B6GbpzIn.js → interactive-terminal-IjIT3Xoa.js} +1 -1
  22. package/dist/desktop-renderer/index.html +1 -1
  23. package/dist/{doctor-OHCGK52S.js → doctor-SEL32R7M.js} +3 -3
  24. package/dist/harness/local.d.ts +1 -0
  25. package/dist/{load-QK6CMLDF.js → load-RHWXNUCS.js} +2 -2
  26. package/dist/{manager-5SI4MPES.js → manager-IYBNNKUP.js} +2 -2
  27. package/dist/{production-6DIZ6YLQ.js → production-TD7UWWXW.js} +6 -6
  28. package/dist/production.d.ts +2 -0
  29. package/dist/sdk/index.js +6 -6
  30. package/dist/{store-ZIHN6363.js → store-MZZERW7K.js} +2 -2
  31. package/dist/ui/commands.d.ts +11 -3
  32. package/dist/ui/session.d.ts +2 -1
  33. package/package.json +1 -1
  34. package//346/212/200/346/234/257/346/226/271/346/241/210/346/212/245/345/221/212.md +649 -1
@@ -18,7 +18,7 @@ import {
18
18
  useStdout,
19
19
  use_app_default,
20
20
  use_input_default
21
- } from "./chunk-T6RE76D2.js";
21
+ } from "./chunk-IKGT4MDT.js";
22
22
  import {
23
23
  require_react
24
24
  } from "./chunk-PULWAH6E.js";
@@ -34,12 +34,12 @@ import {
34
34
  createGlobTool,
35
35
  createProductionRuntime,
36
36
  isDestructiveTool
37
- } from "./chunk-IDJAHEG6.js";
37
+ } from "./chunk-U4XTQ5PU.js";
38
38
  import {
39
39
  getGraphemeSegmenter
40
40
  } from "./chunk-TIEOVQW6.js";
41
41
  import "./chunk-OKHV7CTB.js";
42
- import "./chunk-K7UGGWJV.js";
42
+ import "./chunk-JQM2L2YM.js";
43
43
  import {
44
44
  execFileNoThrow
45
45
  } from "./chunk-IU3UBETC.js";
@@ -54,7 +54,7 @@ import {
54
54
  import "./chunk-CVP5OUHD.js";
55
55
  import "./chunk-3563SW4X.js";
56
56
  import "./chunk-RBAZLFKB.js";
57
- import "./chunk-FAJ6YDZK.js";
57
+ import "./chunk-36IDJRLQ.js";
58
58
  import {
59
59
  redactErrorText
60
60
  } from "./chunk-WLZUAVVU.js";
@@ -63,7 +63,7 @@ import {
63
63
  } from "./chunk-RV3EXUXM.js";
64
64
  import "./chunk-IQEEFDEE.js";
65
65
  import "./chunk-GTFP2R5I.js";
66
- import "./chunk-HGTCIHWA.js";
66
+ import "./chunk-5BDKRA6U.js";
67
67
  import {
68
68
  charWidth,
69
69
  createTranscriptState,
@@ -71,11 +71,11 @@ import {
71
71
  thinkingWindow,
72
72
  transcriptReducer,
73
73
  withTimeout
74
- } from "./chunk-SJUIIHIA.js";
74
+ } from "./chunk-X6U6BO5D.js";
75
75
  import {
76
76
  message
77
77
  } from "./chunk-BDX777JR.js";
78
- import "./chunk-6X62CGMA.js";
78
+ import "./chunk-ONRY3C2L.js";
79
79
  import "./chunk-EWD7FWTH.js";
80
80
  import "./chunk-KRWWCVXA.js";
81
81
  import "./chunk-X442CJJH.js";
@@ -1046,7 +1046,10 @@ function TaskStatusLine({
1046
1046
  const thinkingLine = thinkingVisible ? (() => {
1047
1047
  const previous = thinkingScroll.current;
1048
1048
  const state = thinkingWindow(thinking, previous.start, {
1049
- width: Math.max(8, (stdout.columns ?? 80) - 8),
1049
+ // Split task tracks are narrower than the terminal. The rail takes
1050
+ // two cells; measuring against stdout.columns hides the tail behind
1051
+ // the track clip instead of letting the preview advance.
1052
+ width: textWidth === void 0 ? Math.max(8, (stdout.columns ?? 80) - 8) : Math.max(1, textWidth - 2),
1050
1053
  dtMs: time - previous.lastTime
1051
1054
  });
1052
1055
  thinkingScroll.current = { start: state.start, lastTime: time };
@@ -1637,9 +1640,17 @@ function WelcomeCard({
1637
1640
  serviceName,
1638
1641
  workspaceName,
1639
1642
  updateTo,
1640
- columns
1643
+ columns,
1644
+ availableRows = Number.POSITIVE_INFINITY
1641
1645
  }) {
1642
- const wide = Math.max(1, Math.floor(columns)) >= WIDE_WELCOME_COLUMNS;
1646
+ const wide = Math.max(1, Math.floor(columns)) >= WIDE_WELCOME_COLUMNS && availableRows >= 9 + Math.max(serviceName === void 0 ? 0 : 1, updateTo === void 0 ? 0 : 1);
1647
+ const minimal = availableRows < 6 + (updateTo === void 0 ? 0 : 2);
1648
+ if (minimal) {
1649
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { width: "100%", flexDirection: "column", children: [
1650
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { bold: true, color: FLAVOR_ACCENT, wrap: "truncate-end", children: "\u25C6 Flavor Code" }),
1651
+ availableRows >= 2 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { dimColor: true, wrap: "truncate-end", children: updateTo === void 0 ? "/help \xB7 /init" : `\u25B2 Update v${updateTo} \xB7 flavor update` }) : null
1652
+ ] });
1653
+ }
1643
1654
  const updateHint = updateTo === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: "yellowBright", wrap: "wrap", children: [
1644
1655
  "\u25B2 ",
1645
1656
  "Update available: v",
@@ -2051,7 +2062,10 @@ var PromptHistoryStore = class {
2051
2062
  var import_jsx_runtime5 = __toESM(require_jsx_runtime(), 1);
2052
2063
  var HISTORY_CAP = 200;
2053
2064
  var CLIPBOARD_SHORTCUT_FALLBACK_MS = 300;
2054
- var BUILTIN_SLASH_CANDIDATES = MVP_COMMANDS.map((name) => ({ name, description: COMMAND_DESCRIPTIONS[name] }));
2065
+ var BUILTIN_SLASH_CANDIDATES = [
2066
+ ...MVP_COMMANDS.map((name) => ({ name, description: COMMAND_DESCRIPTIONS[name] })),
2067
+ { name: "queue", description: "Review, edit, or cancel queued follow-ups" }
2068
+ ];
2055
2069
  var PROMPT_HORIZONTAL_PADDING = 1;
2056
2070
  function clonePromptDraft(state) {
2057
2071
  return {
@@ -2108,6 +2122,15 @@ function isOutputToggleShortcut(character, key) {
2108
2122
  function outputToggleShortcut(_platform = process.platform) {
2109
2123
  return "Ctrl+O";
2110
2124
  }
2125
+ function fitFooterActions(columns, actions) {
2126
+ let hint = "";
2127
+ for (const action of actions) {
2128
+ const next = hint.length === 0 ? action : `${hint} \xB7 ${action}`;
2129
+ if (stringWidth(next) > columns) break;
2130
+ hint = next;
2131
+ }
2132
+ return hint || actions[0] || "";
2133
+ }
2111
2134
  function removeLastCliImageOnBackspace(input, cursor, images) {
2112
2135
  if (input.length === 0 && cursor === 0 && images.length > 0) {
2113
2136
  return { handled: true, images: images.slice(0, -1) };
@@ -2209,6 +2232,11 @@ var PendingPromptQueue = class {
2209
2232
  const value = this.#values.pop();
2210
2233
  return value === void 0 ? void 0 : cloneQueuedPrompt(value);
2211
2234
  }
2235
+ removeAt(index) {
2236
+ if (!Number.isInteger(index) || index < 0 || index >= this.#values.length) return void 0;
2237
+ const value = this.#values.splice(index, 1)[0];
2238
+ return value === void 0 ? void 0 : cloneQueuedPrompt(value);
2239
+ }
2212
2240
  take() {
2213
2241
  const value = this.#values.shift();
2214
2242
  return value === void 0 ? void 0 : cloneQueuedPrompt(value);
@@ -2277,7 +2305,7 @@ function appRuntimeOptions(props, output, onApprovalChange) {
2277
2305
  }
2278
2306
  function App({ workspace, home, resumeSession, instanceId, palAlias, onSessionEnd }) {
2279
2307
  const { exit } = use_app_default();
2280
- const { stdout } = useStdout();
2308
+ const terminalSize = (0, import_react8.useContext)(TerminalSizeContext);
2281
2309
  const [runtime, setRuntime] = (0, import_react8.useState)();
2282
2310
  const [input, setInput] = (0, import_react8.useState)("");
2283
2311
  const [pastedBlocks, setPastedBlocks] = (0, import_react8.useState)([]);
@@ -2297,16 +2325,22 @@ function App({ workspace, home, resumeSession, instanceId, palAlias, onSessionEn
2297
2325
  const [mentionSelection, setMentionSelection] = (0, import_react8.useState)(0);
2298
2326
  const [dismissedMentionInput, setDismissedMentionInput] = (0, import_react8.useState)();
2299
2327
  const [pendingPrompts, setPendingPrompts] = (0, import_react8.useState)([]);
2328
+ const [queueOpen, setQueueOpen] = (0, import_react8.useState)(false);
2329
+ const [queueSelection, setQueueSelection] = (0, import_react8.useState)(0);
2330
+ const queueSelectionRef = (0, import_react8.useRef)(0);
2331
+ const [scrollPosition, setScrollPosition] = (0, import_react8.useState)({ behind: false, newOutput: false });
2300
2332
  const [revision, setRevision] = (0, import_react8.useState)(0);
2301
2333
  const [ideContext, setIdeContext] = (0, import_react8.useState)();
2302
2334
  const [questionIndex, setQuestionIndex] = (0, import_react8.useState)(0);
2303
2335
  const [questionAnswers, setQuestionAnswers] = (0, import_react8.useState)({});
2304
2336
  const [customQuestionActive, setCustomQuestionActive] = (0, import_react8.useState)(false);
2305
- const [columns, setColumns] = (0, import_react8.useState)(stdout?.columns ?? 80);
2306
- const [rows, setRows] = (0, import_react8.useState)(stdout?.rows ?? 24);
2337
+ const columns = terminalSize?.columns ?? 80;
2338
+ const rows = terminalSize?.rows ?? 24;
2307
2339
  const [updateTo, setUpdateTo] = (0, import_react8.useState)();
2308
2340
  const [transcript, dispatch] = (0, import_react8.useReducer)(transcriptReducer, void 0, createTranscriptState);
2309
2341
  const scrollRef = (0, import_react8.useRef)(null);
2342
+ const focusScrollRef = (0, import_react8.useRef)(null);
2343
+ const turnElementsRef = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
2310
2344
  const mainTaskScrollRef = (0, import_react8.useRef)(null);
2311
2345
  const subagentTaskScrollRef = (0, import_react8.useRef)(null);
2312
2346
  const hoveredTaskTrack = (0, import_react8.useRef)(null);
@@ -2373,6 +2407,9 @@ function App({ workspace, home, resumeSession, instanceId, palAlias, onSessionEn
2373
2407
  t.timer = null;
2374
2408
  }
2375
2409
  if (t.pending.length > 0) {
2410
+ if (scrollRef.current !== null && !scrollRef.current.isSticky()) {
2411
+ setScrollPosition((previous) => previous.newOutput ? previous : { behind: true, newOutput: true });
2412
+ }
2376
2413
  dispatch({ type: "session", event: { type: "text", text: t.pending } });
2377
2414
  t.pending = "";
2378
2415
  }
@@ -2420,6 +2457,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias, onSessionEn
2420
2457
  if (event.type === "clear") {
2421
2458
  flushStreamBuffers();
2422
2459
  dispatch({ type: "clear" });
2460
+ setScrollPosition({ behind: false, newOutput: false });
2423
2461
  return;
2424
2462
  }
2425
2463
  if (event.type === "text") {
@@ -2435,6 +2473,9 @@ function App({ workspace, home, resumeSession, instanceId, palAlias, onSessionEn
2435
2473
  return;
2436
2474
  }
2437
2475
  flushStreamBuffers();
2476
+ if (scrollRef.current !== null && !scrollRef.current.isSticky()) {
2477
+ setScrollPosition((previous) => previous.newOutput ? previous : { behind: true, newOutput: true });
2478
+ }
2438
2479
  dispatch({ type: "session", event });
2439
2480
  };
2440
2481
  void createProductionRuntime(appRuntimeOptions({
@@ -2557,21 +2598,33 @@ function App({ workspace, home, resumeSession, instanceId, palAlias, onSessionEn
2557
2598
  controller.abort();
2558
2599
  };
2559
2600
  }, [workspace]);
2560
- (0, import_react8.useEffect)(() => {
2561
- if (!stdout || typeof stdout.on !== "function") return;
2562
- const onResize = () => {
2563
- setColumns(stdout.columns ?? 80);
2564
- setRows(stdout.rows ?? 24);
2565
- };
2566
- stdout.on("resize", onResize);
2567
- onResize();
2568
- return () => {
2569
- stdout.off("resize", onResize);
2570
- };
2571
- }, [stdout]);
2572
2601
  const approval = runtime?.approvals.pending;
2573
2602
  const questions = runtime?.services.questions.pending;
2574
2603
  const memoryReviews = runtime?.memoryReviews.pending ?? [];
2604
+ const focusedDecision = approval !== void 0 || questions !== void 0 && questions.length > 0;
2605
+ const transcriptVisible = runtime !== void 0 && (transcript.completed.length > 0 || transcript.active !== void 0) && !focusedDecision && !queueOpen;
2606
+ (0, import_react8.useEffect)(() => {
2607
+ if (!transcriptVisible) return;
2608
+ const scroll = scrollRef.current;
2609
+ if (scroll === null) return;
2610
+ const update = () => {
2611
+ const behind = !scroll.isSticky();
2612
+ setScrollPosition((previous) => previous.behind === behind && (behind || !previous.newOutput) ? previous : { behind, newOutput: behind && previous.newOutput });
2613
+ };
2614
+ update();
2615
+ return scroll.subscribe(update);
2616
+ }, [transcriptVisible]);
2617
+ (0, import_react8.useEffect)(() => {
2618
+ if (pendingPrompts.length === 0) setQueueOpen(false);
2619
+ setQueueSelection((previous) => {
2620
+ const next = Math.max(0, Math.min(previous, pendingPrompts.length - 1));
2621
+ queueSelectionRef.current = next;
2622
+ return next;
2623
+ });
2624
+ }, [pendingPrompts.length]);
2625
+ (0, import_react8.useEffect)(() => {
2626
+ if (focusedDecision) focusScrollRef.current?.scrollTo(0);
2627
+ }, [focusedDecision, approval?.id, questionIndex]);
2575
2628
  const memoryAutoDismissSeconds = runtime?.memoryReviews.autoDismissSeconds ?? 0;
2576
2629
  (0, import_react8.useEffect)(() => {
2577
2630
  setQuestionIndex(0);
@@ -2652,12 +2705,66 @@ function App({ workspace, home, resumeSession, instanceId, palAlias, onSessionEn
2652
2705
  }
2653
2706
  };
2654
2707
  use_input_default((character, key, event) => {
2708
+ if (queueOpen && !focusedDecision) {
2709
+ const queue = pendingPromptRef.current;
2710
+ if (key.escape) {
2711
+ setQueueOpen(false);
2712
+ return;
2713
+ }
2714
+ if (key.upArrow || key.downArrow || key.pageUp || key.pageDown || key.wheelUp || key.wheelDown || key.home || key.end) {
2715
+ const delta = key.upArrow ? -1 : key.downArrow ? 1 : key.pageUp ? -5 : key.pageDown ? 5 : key.wheelUp ? -3 : 3;
2716
+ const target = key.home ? 0 : key.end ? queue.size - 1 : queueSelectionRef.current + delta;
2717
+ const next = Math.max(0, Math.min(queue.size - 1, target));
2718
+ queueSelectionRef.current = next;
2719
+ setQueueSelection(next);
2720
+ return;
2721
+ }
2722
+ if (key.ctrl && character.toLowerCase() === "c") {
2723
+ interrupt();
2724
+ return;
2725
+ }
2726
+ if (key.return || !key.ctrl && !key.meta && character.toLowerCase() === "d") {
2727
+ const removed = queue.removeAt(queueSelectionRef.current);
2728
+ if (removed === void 0) return;
2729
+ setPendingPrompts([...queue.values]);
2730
+ if (key.return) {
2731
+ const restoredDraft = restoreQueuedPrompt(removed);
2732
+ promptEditHistory.current.reset();
2733
+ commitPromptDraft(restoredDraft, false);
2734
+ setQueueOpen(false);
2735
+ setClipboardNotice("Queued message moved to draft; sending it again puts it at the end of the queue.");
2736
+ } else if (queue.size === 0) {
2737
+ setQueueOpen(false);
2738
+ setClipboardNotice("Queued message cancelled.");
2739
+ }
2740
+ const next = Math.max(0, Math.min(queueSelectionRef.current, queue.size - 1));
2741
+ queueSelectionRef.current = next;
2742
+ setQueueSelection(next);
2743
+ return;
2744
+ }
2745
+ return;
2746
+ }
2747
+ const focusScroll = focusedDecision || queueOpen ? focusScrollRef.current : null;
2748
+ if (key.home || key.end) {
2749
+ const scroll = focusScroll ?? scrollRef.current;
2750
+ if (scroll !== null) {
2751
+ if (key.end) scroll.scrollToBottom();
2752
+ else scroll.scrollTo(0);
2753
+ }
2754
+ return;
2755
+ }
2756
+ if (!focusedDecision && !queueOpen && slashCompletion === null && mentionCompletion === null && key.ctrl && (key.upArrow || key.downArrow)) {
2757
+ if (jumpToAdjacentTurn(scrollRef.current, turnElementsRef.current, key.upArrow ? "previous" : "next")) {
2758
+ setScrollPosition({ behind: true, newOutput: false });
2759
+ }
2760
+ return;
2761
+ }
2655
2762
  const terminalAction = classifyTerminalInput(
2656
2763
  key,
2657
- transcript.active !== void 0 && slashCompletion === null && mentionCompletion === null
2764
+ (transcript.active !== void 0 || focusedDecision) && slashCompletion === null && mentionCompletion === null
2658
2765
  );
2659
2766
  if (terminalAction?.type === "scroll") {
2660
- const scroll = selectWheelScrollTarget(
2767
+ const scroll = focusScroll ?? selectWheelScrollTarget(
2661
2768
  scrollRef.current,
2662
2769
  mainTaskScrollRef.current,
2663
2770
  subagentTaskScrollRef.current,
@@ -2670,7 +2777,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias, onSessionEn
2670
2777
  return;
2671
2778
  }
2672
2779
  if (terminalAction?.type === "page") {
2673
- const scroll = scrollRef.current;
2780
+ const scroll = focusScroll ?? scrollRef.current;
2674
2781
  if (scroll !== null) jumpScroll(scroll, Math.floor(scroll.getViewportHeight() * terminalAction.fraction));
2675
2782
  return;
2676
2783
  }
@@ -2865,6 +2972,21 @@ function App({ workspace, home, resumeSession, instanceId, palAlias, onSessionEn
2865
2972
  return;
2866
2973
  }
2867
2974
  if (key.return) {
2975
+ if (/^\/queue\s*$/iu.test(input)) {
2976
+ if (imageAttachments.length > 0) {
2977
+ setClipboardNotice("Send or remove image attachments before opening /queue.");
2978
+ return;
2979
+ }
2980
+ setInput("");
2981
+ setPromptCursor(0);
2982
+ if (pendingPromptRef.current.size > 0) {
2983
+ queueSelectionRef.current = 0;
2984
+ setQueueSelection(0);
2985
+ setQueueOpen(true);
2986
+ setClipboardNotice(void 0);
2987
+ } else setClipboardNotice("No queued messages.");
2988
+ return;
2989
+ }
2868
2990
  if (/^\/paste-image\s*$/iu.test(input)) {
2869
2991
  setInput("");
2870
2992
  setPromptCursor(0);
@@ -2894,7 +3016,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias, onSessionEn
2894
3016
  })) return;
2895
3017
  setPendingPrompts([...pendingPromptRef.current.values]);
2896
3018
  }
2897
- scrollRef.current?.scrollToBottom();
3019
+ if (delivery === "prompt" || scrollRef.current?.isSticky()) scrollRef.current?.scrollToBottom();
2898
3020
  const nextHistory = [...historyRef.current, submittedText].slice(-HISTORY_CAP);
2899
3021
  historyRef.current = nextHistory;
2900
3022
  historyCursorRef.current = nextHistory.length;
@@ -3036,6 +3158,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias, onSessionEn
3036
3158
  ...ideContext === void 0 ? {} : { ideContext },
3037
3159
  completedSlashTokenLength: completedTokenLength,
3038
3160
  scrollRef,
3161
+ focusScrollRef,
3039
3162
  mainTaskScrollRef,
3040
3163
  subagentTaskScrollRef,
3041
3164
  onTaskPanelHoverChange: (track) => {
@@ -3050,6 +3173,19 @@ function App({ workspace, home, resumeSession, instanceId, palAlias, onSessionEn
3050
3173
  memoryReviews,
3051
3174
  memoryAutoDismissSeconds,
3052
3175
  pendingPrompts,
3176
+ queueOpen,
3177
+ queueSelection,
3178
+ onQueueSelectionChange: (index) => {
3179
+ queueSelectionRef.current = index;
3180
+ setQueueSelection(index);
3181
+ },
3182
+ scrollBehind: scrollPosition.behind,
3183
+ newOutput: scrollPosition.newOutput,
3184
+ onJumpToLatest: () => scrollRef.current?.scrollToBottom(),
3185
+ onTurnElement: (id, element) => {
3186
+ if (element === null) turnElementsRef.current.delete(id);
3187
+ else turnElementsRef.current.set(id, element);
3188
+ },
3053
3189
  questionIndex,
3054
3190
  questionAnswers,
3055
3191
  customQuestionActive
@@ -3249,11 +3385,19 @@ function TerminalLayout({
3249
3385
  questionIndex = 0,
3250
3386
  questionAnswers = {},
3251
3387
  customQuestionActive = false,
3388
+ queueOpen = false,
3389
+ queueSelection = 0,
3390
+ onQueueSelectionChange,
3391
+ scrollBehind = false,
3392
+ newOutput = false,
3393
+ onJumpToLatest,
3394
+ onTurnElement,
3252
3395
  completion,
3253
3396
  mentionCompletion,
3254
3397
  onMentionSelect,
3255
3398
  completedSlashTokenLength: tokenLength = 0,
3256
3399
  scrollRef,
3400
+ focusScrollRef,
3257
3401
  mainTaskScrollRef,
3258
3402
  subagentTaskScrollRef,
3259
3403
  onTaskPanelHoverChange,
@@ -3282,17 +3426,39 @@ function TerminalLayout({
3282
3426
  () => cliTranscriptWindow(completed, budget.turns, budget.blocks, budget.textChars, budget.blocksPerTurn),
3283
3427
  [completed, budget]
3284
3428
  );
3285
- const questionRows = questions === void 0 ? 0 : 4 + (questions[questionIndex]?.options.length ?? 0) + questions.length * 2;
3286
3429
  const memoryReviewRows = memoryReviews.length === 0 ? 0 : 5;
3287
- const approvalRows = approval === void 0 ? 0 : 3 + (approvalExpanded ? approvalDetailLines(approval).length : 0);
3288
- const fixedBottomRows = approvalRows + questionRows + memoryReviewRows + (pendingPrompts.length === 0 ? 0 : 1) + imageAttachments.length + (clipboardNotice === void 0 ? 0 : 1) + 2;
3430
+ const focusedOpen = approval !== void 0 || questions !== void 0 && questions.length > 0 || queueOpen;
3431
+ const focusedRows = focusedOpen ? Math.min(
3432
+ focusedPanelNaturalRows({ approval, approvalExpanded, questions, questionIndex, queueOpen, pendingPrompts, columns, rows }),
3433
+ Math.min(Math.max(4, rows - 3), Math.max(6, Math.floor(rows / 2)))
3434
+ ) : 0;
3435
+ const fixedBottomRows = focusedRows + memoryReviewRows + (pendingPrompts.length === 0 ? 0 : 1) + imageAttachments.length + (clipboardNotice === void 0 ? 0 : 1) + (activeSession ? 1 : 0) + 2;
3289
3436
  const taskPanelRows = taskPanelViewportRows(rows, fixedBottomRows, activeTaskBlocks.length > 0);
3290
3437
  const availableBottomRows = Math.max(1, rows - taskPanelRows - 1);
3291
3438
  const bottomMaxRows = Math.min(availableBottomRows, Math.max(Math.floor(rows / 2), fixedBottomRows + 1));
3292
3439
  const promptMaxLines = Math.max(1, bottomMaxRows - fixedBottomRows);
3440
+ let footerHint;
3441
+ if (completion !== void 0 || mentionCompletion !== void 0) {
3442
+ footerHint = columns < 48 ? "\u2191\u2193 select \xB7 Tab choose \xB7 Esc close" : mentionCompletion !== void 0 ? "\u2191/\u2193 select \xB7 Tab complete \xB7 click choose \xB7 Esc close" : "\u2191/\u2193 select \xB7 Tab complete \xB7 Esc close";
3443
+ } else if (activeSession) {
3444
+ footerHint = fitFooterActions(columns, [
3445
+ "Enter queue",
3446
+ `Esc ${pendingPrompts.length > 0 ? "edit" : "stop"}`,
3447
+ "Ctrl+C cancel",
3448
+ "/steer now",
3449
+ "/queue manage",
3450
+ `${outputShortcut} ${expandedOutput ? "collapse" : "expand"}`
3451
+ ]);
3452
+ } else if (columns < 48) {
3453
+ footerHint = "Enter send \xB7 Ctrl+C exit";
3454
+ } else if (columns < 96) {
3455
+ footerHint = `Enter send \xB7 Ctrl+C exit \xB7 \u2191\u2193 history \xB7 ${outputShortcut} output`;
3456
+ } else {
3457
+ footerHint = `Enter send \xB7 \u2191\u2193/Ctrl/Cmd+R history \xB7 ${outputShortcut} ${expandedOutput ? "collapse all output" : "expand all output"} \xB7 Ctrl+C exit`;
3458
+ }
3293
3459
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { height: rows, width: "100%", flexDirection: "column", overflow: "hidden", children: [
3294
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(ScrollBox_default, { ...scrollRef === void 0 ? {} : { ref: scrollRef }, flexGrow: 1, flexDirection: "column", stickyScroll: true, children: [
3295
- showWelcome ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(WelcomeCard, { model, ...serviceName === void 0 ? {} : { serviceName }, workspaceName, ...updateTo === void 0 ? {} : { updateTo }, columns }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { dimColor: true, children: [
3460
+ showWelcome && !focusedOpen ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Box_default, { flexGrow: 1, flexShrink: 1, flexDirection: "column", overflow: "hidden", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(WelcomeCard, { model, ...serviceName === void 0 ? {} : { serviceName }, workspaceName, ...updateTo === void 0 ? {} : { updateTo }, columns, availableRows: Math.max(0, rows - taskPanelRows - fixedBottomRows - 1) }) }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(ScrollBox_default, { ...scrollRef === void 0 ? {} : { ref: scrollRef }, flexGrow: 1, flexDirection: "column", stickyScroll: true, children: [
3461
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { dimColor: true, children: [
3296
3462
  "flavor \xB7 ",
3297
3463
  model,
3298
3464
  " \xB7 ",
@@ -3308,15 +3474,20 @@ function TerminalLayout({
3308
3474
  outputShortcut,
3309
3475
  " to view all"
3310
3476
  ] }) : null,
3311
- completedWindow.turns.map((turn, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { flexDirection: "column", children: [
3477
+ completedWindow.turns.map((turn, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { ref: (element) => onTurnElement?.(turn.id, element), flexDirection: "column", children: [
3312
3478
  index > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TurnSeparator, { width: columns }) : null,
3313
3479
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TurnView, { turn, interactive: false, workspaceName, expandedOutput })
3314
3480
  ] }, turn.id)),
3315
- activeWithoutTasks === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { flexDirection: "column", children: [
3481
+ activeWithoutTasks === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { ref: (element) => onTurnElement?.(activeWithoutTasks.id, element), flexDirection: "column", children: [
3316
3482
  completedWindow.turns.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TurnSeparator, { width: columns }) : null,
3317
3483
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TurnView, { turn: activeWithoutTasks, interactive: activeSession, workspaceName, expandedOutput })
3318
3484
  ] })
3319
3485
  ] }),
3486
+ !scrollBehind || showWelcome ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Box_default, { width: "100%", onClick: onJumpToLatest, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "yellow", wrap: "truncate-end", children: [
3487
+ newOutput ? "\u2193 New output" : "\u2191 Reading earlier output",
3488
+ " \xB7 End latest",
3489
+ columns >= 72 ? " \xB7 Ctrl+\u2191/\u2193 turns" : ""
3490
+ ] }) }),
3320
3491
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3321
3492
  TaskProgressPanel,
3322
3493
  {
@@ -3335,49 +3506,29 @@ function TerminalLayout({
3335
3506
  mentionCompletion === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MentionMenu, { completion: mentionCompletion, ...onMentionSelect === void 0 ? {} : { onSelect: onMentionSelect } })
3336
3507
  ] }),
3337
3508
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { flexDirection: "column", flexShrink: 0, maxHeight: bottomMaxRows, width: "100%", overflowY: "hidden", children: [
3338
- approval === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
3339
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "magenta", children: [
3340
- "\u250C\u2500 approval \xB7 ",
3341
- approval.tool
3342
- ] }),
3343
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { wrap: "truncate-end", color: "magentaBright", children: [
3344
- "\u2502 ",
3345
- approval.reason ?? "This action needs permission."
3346
- ] }),
3347
- approvalExpanded ? approvalDetailLines(approval).map((line, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "magentaBright", wrap: "truncate-end", children: [
3348
- "\u2502 ",
3349
- line
3350
- ] }, `${approval.id}:detail:${index}`)) : null,
3351
- isDestructiveTool(approval.tool) || approval.allowAlways === false ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { bold: true, color: "magenta", children: [
3352
- "\u2514\u2500 ",
3353
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "cyan", children: "v" }),
3354
- "=details / ",
3355
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "green", children: "y" }),
3356
- "=once / ",
3357
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "cyan", children: "e" }),
3358
- "=accept edits / ",
3359
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "red", children: "n" }),
3360
- "=deny"
3361
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { bold: true, color: "magenta", children: [
3362
- "\u2514\u2500 ",
3363
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "cyan", children: "v" }),
3364
- "=details / ",
3365
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "green", children: "y" }),
3366
- "=once / ",
3367
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "yellow", children: "a" }),
3368
- "=same-type / ",
3369
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "cyan", children: "e" }),
3370
- "=accept edits / ",
3371
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "red", children: "n" }),
3372
- "=deny"
3373
- ] })
3374
- ] }),
3375
- !questions || questions.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(QuestionCards, { questions, activeIndex: questionIndex, answers: questionAnswers, customActive: customQuestionActive }),
3509
+ !focusedOpen ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3510
+ FocusedCliPanel,
3511
+ {
3512
+ rows,
3513
+ panelRows: focusedRows,
3514
+ columns,
3515
+ ...approval === void 0 ? {} : { approval },
3516
+ approvalExpanded,
3517
+ ...questions === void 0 ? {} : { questions },
3518
+ questionIndex,
3519
+ customQuestionActive,
3520
+ pendingPrompts,
3521
+ queueSelection,
3522
+ ...onQueueSelectionChange === void 0 ? {} : { onQueueSelectionChange },
3523
+ ...focusScrollRef === void 0 ? {} : { focusScrollRef }
3524
+ }
3525
+ ),
3376
3526
  memoryReviews.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MemoryReviewCards, { reviews: memoryReviews, autoDismissSeconds: memoryAutoDismissSeconds }),
3527
+ !activeSession ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: "cyan", wrap: "truncate-end", children: columns < 48 ? "Running \xB7 Enter queues \xB7 /queue list" : "Running \xB7 /queue manage \xB7 Enter queues next \xB7 /steer acts now" }),
3377
3528
  pendingPrompts.length === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "yellow", wrap: "truncate-end", children: [
3378
3529
  "Pending (",
3379
3530
  pendingPrompts.length,
3380
- ") \xB7 ",
3531
+ ") \xB7 /queue manage \xB7 ",
3381
3532
  queuedPromptLabel(pendingPrompts.at(-1)),
3382
3533
  " \xB7 Esc edit latest"
3383
3534
  ] }),
@@ -3404,7 +3555,8 @@ function TerminalLayout({
3404
3555
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3405
3556
  FooterStatus,
3406
3557
  {
3407
- hint: completion !== void 0 ? "\u2191/\u2193 select \xB7 Tab complete \xB7 Esc close" : mentionCompletion !== void 0 ? "\u2191/\u2193 select \xB7 Tab complete \xB7 click choose \xB7 Esc close" : activeSession ? `Esc ${pendingPrompts.length > 0 ? "edit latest" : "stop"} \xB7 Ctrl+C cancel \xB7 Enter queue \xB7 Ctrl/Cmd+R history \xB7 ${outputShortcut} ${expandedOutput ? "collapse all output" : "expand all output"}` : `Enter send \xB7 \u2191\u2193/Ctrl/Cmd+R history \xB7 ${outputShortcut} ${expandedOutput ? "collapse all output" : "expand all output"} \xB7 Ctrl+C exit`,
3558
+ hint: footerHint,
3559
+ columns,
3408
3560
  ...ideContext === void 0 ? {} : { ideContext }
3409
3561
  }
3410
3562
  )
@@ -3414,12 +3566,14 @@ function TerminalLayout({
3414
3566
  }
3415
3567
  function FooterStatus({
3416
3568
  hint,
3569
+ columns,
3417
3570
  ideContext
3418
3571
  }) {
3419
3572
  const ide = ideFooterPresentation(ideContext);
3573
+ const showIde = ide !== void 0 && (columns === void 0 || stringWidth(hint) + stringWidth(ide) + 3 <= columns);
3420
3574
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { flexDirection: "row", width: "100%", justifyContent: "space-between", children: [
3421
3575
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Box_default, { flexGrow: 1, flexShrink: 1, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { dimColor: true, wrap: "truncate-end", children: hint }) }),
3422
- ide === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Box_default, { flexShrink: 0, marginLeft: 1, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "cyan", wrap: "truncate-end", children: [
3576
+ !showIde ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Box_default, { flexShrink: 0, marginLeft: 1, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "cyan", wrap: "truncate-end", children: [
3423
3577
  "\u29C9 ",
3424
3578
  ide
3425
3579
  ] }) })
@@ -3432,42 +3586,168 @@ function ideFooterPresentation(context) {
3432
3586
  const count = Math.max(1, endLine - context.selection.start.line + 1);
3433
3587
  return `${count} ${count === 1 ? "line" : "lines"} selected`;
3434
3588
  }
3435
- function QuestionCards({ questions, activeIndex, answers, customActive }) {
3436
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Box_default, { flexDirection: "column", marginBottom: 1, children: questions.map((q, qi) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { flexDirection: "column", marginBottom: qi < questions.length - 1 ? 1 : 0, children: [
3437
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { bold: true, color: "cyan", children: [
3589
+ function focusedPanelNaturalRows({
3590
+ approval,
3591
+ approvalExpanded,
3592
+ questions,
3593
+ questionIndex,
3594
+ queueOpen,
3595
+ pendingPrompts,
3596
+ columns,
3597
+ rows
3598
+ }) {
3599
+ if (approval !== void 0) {
3600
+ const allowAlways = !isDestructiveTool(approval.tool) && approval.allowAlways !== false;
3601
+ const detail = approvalExpanded ? approvalDetailLines(approval) : [];
3602
+ const contentRows = wrappedRows(approval.reason ?? "This action needs permission.", columns) + detail.reduce((total, line) => total + wrappedRows(line, columns), 0);
3603
+ return 1 + Math.max(contentRows, 1) + 2 + (allowAlways && columns < 36 && rows >= 5 ? 1 : 0);
3604
+ }
3605
+ const question = questions?.[Math.min(questionIndex, questions.length - 1)];
3606
+ if (question !== void 0) {
3607
+ const contentRows = wrappedRows(question.question, columns) + question.options.reduce((total, option) => total + wrappedRows(`${option.label}${option.description ? ` ${option.description}` : ""}`, columns), 0) + 1;
3608
+ return 1 + Math.max(contentRows, 1) + 1;
3609
+ }
3610
+ if (queueOpen) return 1 + Math.max(1, pendingPrompts.length) + 2;
3611
+ return 0;
3612
+ }
3613
+ function FocusedCliPanel({
3614
+ rows,
3615
+ panelRows,
3616
+ columns,
3617
+ approval,
3618
+ approvalExpanded,
3619
+ questions,
3620
+ questionIndex,
3621
+ customQuestionActive,
3622
+ pendingPrompts,
3623
+ queueSelection,
3624
+ onQueueSelectionChange,
3625
+ focusScrollRef
3626
+ }) {
3627
+ if (approval !== void 0) {
3628
+ const allowAlways = !isDestructiveTool(approval.tool) && approval.allowAlways !== false;
3629
+ const narrow = columns < 36;
3630
+ const detailLines = approvalExpanded ? approvalDetailLines(approval) : [];
3631
+ const chromeRows = 3 + (allowAlways && narrow && rows >= 5 ? 1 : 0);
3632
+ const thirdHint = allowAlways && narrow && rows >= 5;
3633
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { width: "100%", height: panelRows, flexDirection: "column", flexShrink: 0, overflow: "hidden", children: [
3634
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { bold: true, color: "magenta", wrap: "truncate-end", children: [
3635
+ "\u250C\u2500 ",
3636
+ "Approval \xB7 ",
3637
+ approval.tool
3638
+ ] }),
3639
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(ScrollBox_default, { ...focusScrollRef === void 0 ? {} : { ref: focusScrollRef }, height: Math.max(1, panelRows - chromeRows), flexDirection: "column", children: [
3640
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "magentaBright", wrap: "wrap", children: [
3641
+ "\u2502 ",
3642
+ approval.reason ?? "This action needs permission."
3643
+ ] }),
3644
+ approvalExpanded ? detailLines.map((line, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "magentaBright", wrap: "wrap", children: [
3645
+ "\u2502 ",
3646
+ line
3647
+ ] }, `${approval.id}:detail:${index}`)) : null
3648
+ ] }),
3649
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { bold: true, color: "magenta", wrap: "truncate-end", children: [
3650
+ "\u2502 ",
3651
+ narrow ? "y once \xB7 n deny" : "y=once \xB7 n=deny \xB7 Esc=deny"
3652
+ ] }),
3653
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "magenta", wrap: "truncate-end", children: [
3654
+ thirdHint ? "\u2502 " : "\u2514\u2500 ",
3655
+ narrow ? "v details \xB7 e edits" : "v=details \xB7 e=accept edits",
3656
+ allowAlways && !narrow ? " \xB7 a=same-type" : ""
3657
+ ] }),
3658
+ thirdHint ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "magenta", wrap: "truncate-end", children: [
3659
+ "\u2514\u2500 ",
3660
+ "a same-type \xB7 Esc deny"
3661
+ ] }) : null
3662
+ ] });
3663
+ }
3664
+ const question = questions?.[Math.min(questionIndex, questions.length - 1)];
3665
+ if (question !== void 0) {
3666
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { width: "100%", height: panelRows, flexDirection: "column", flexShrink: 0, overflow: "hidden", children: [
3667
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { bold: true, color: "cyan", wrap: "truncate-end", children: [
3668
+ "\u250C\u2500 ",
3669
+ "Question ",
3670
+ questionIndex + 1,
3671
+ "/",
3672
+ questions.length,
3673
+ " \xB7 ",
3674
+ question.header
3675
+ ] }),
3676
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(ScrollBox_default, { ...focusScrollRef === void 0 ? {} : { ref: focusScrollRef }, height: Math.max(1, panelRows - 2), flexDirection: "column", children: [
3677
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "cyanBright", wrap: "wrap", children: [
3678
+ "\u2502 ",
3679
+ question.question
3680
+ ] }),
3681
+ question.options.map((option, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "cyan", wrap: "wrap", children: [
3682
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { bold: true, color: "green", children: [
3683
+ "\u2502 ",
3684
+ index + 1,
3685
+ ". ",
3686
+ option.label
3687
+ ] }),
3688
+ option.description ? ` ${option.description}` : ""
3689
+ ] }, index)),
3690
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "cyan", children: [
3691
+ "\u2502 ",
3692
+ question.options.length + 1,
3693
+ ". Custom input"
3694
+ ] })
3695
+ ] }),
3696
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "cyan", wrap: "truncate-end", children: [
3697
+ "\u2514\u2500 ",
3698
+ customQuestionActive ? columns < 40 ? "Enter answer \xB7 Esc choices" : "Enter=answer \xB7 Esc=choices \xB7 PgUp/PgDn=scroll" : columns < 40 ? `1-${question.options.length + 1} choose \xB7 Esc cancel` : `1-${question.options.length + 1}=choose \xB7 Esc=cancel \xB7 PgUp/PgDn=scroll`
3699
+ ] })
3700
+ ] });
3701
+ }
3702
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { width: "100%", height: panelRows, flexDirection: "column", flexShrink: 0, overflow: "hidden", children: [
3703
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { bold: true, color: "yellow", wrap: "truncate-end", children: [
3438
3704
  "\u250C\u2500 ",
3439
- q.header
3705
+ "Queued messages (",
3706
+ pendingPrompts.length,
3707
+ ")"
3440
3708
  ] }),
3441
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { wrap: "truncate-end", color: "cyanBright", children: [
3709
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ScrollBox_default, { ...focusScrollRef === void 0 ? {} : { ref: focusScrollRef }, height: Math.max(1, panelRows - 3), flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3710
+ QueueItems,
3711
+ {
3712
+ items: pendingPrompts,
3713
+ selection: queueSelection,
3714
+ ...focusScrollRef === void 0 ? {} : { scrollRef: focusScrollRef },
3715
+ ...onQueueSelectionChange === void 0 ? {} : { onSelect: onQueueSelectionChange }
3716
+ }
3717
+ ) }),
3718
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { bold: true, color: "yellow", wrap: "truncate-end", children: [
3442
3719
  "\u2502 ",
3443
- q.question
3720
+ columns < 48 ? "Enter edit \xB7 d cancel" : "Enter=edit selected \xB7 d=cancel selected"
3444
3721
  ] }),
3445
- qi < activeIndex ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "green", children: [
3446
- "\u2502 \u2713 ",
3447
- answers[qi]
3448
- ] }) : qi > activeIndex ? null : q.options.map((opt, oi) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "cyan", children: [
3449
- "\u2502 ",
3450
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { bold: true, color: "green", children: oi + 1 }),
3451
- ". ",
3452
- opt.label,
3453
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { dimColor: true, children: [
3454
- " ",
3455
- opt.description
3456
- ] })
3457
- ] }, oi)),
3458
- qi === activeIndex ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
3459
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "cyan", children: [
3460
- "\u2502 ",
3461
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { bold: true, color: "green", children: q.options.length + 1 }),
3462
- ". Custom input",
3463
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { dimColor: true, children: " Type your own answer" })
3464
- ] }),
3465
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { dimColor: true, color: "cyan", children: [
3466
- "\u2514\u2500 ",
3467
- customActive ? "Type below and press Enter; Esc returns to choices" : `Press 1-${q.options.length + 1} to choose; Esc dismisses`
3722
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "yellow", wrap: "truncate-end", children: [
3723
+ "\u2514\u2500 ",
3724
+ columns < 48 ? "\u2191\u2193 select \xB7 Esc close" : "\u2191/\u2193=select \xB7 PgUp/PgDn=jump \xB7 Esc=close"
3725
+ ] })
3726
+ ] });
3727
+ }
3728
+ function wrappedRows(text, columns) {
3729
+ return Math.max(1, Math.ceil(stringWidth(text) / Math.max(20, columns)));
3730
+ }
3731
+ function QueueItems({ items, selection, scrollRef, onSelect }) {
3732
+ const selectedElement = (0, import_react8.useRef)(null);
3733
+ (0, import_react8.useEffect)(() => {
3734
+ if (selectedElement.current !== null) scrollRef?.current?.scrollToElement(selectedElement.current);
3735
+ }, [selection, items.length, scrollRef]);
3736
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Box_default, { flexDirection: "column", width: "100%", children: items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3737
+ Box_default,
3738
+ {
3739
+ ref: index === selection ? selectedElement : void 0,
3740
+ onClick: () => onSelect?.(index),
3741
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: index === selection ? "yellowBright" : void 0, bold: index === selection, wrap: "wrap", children: [
3742
+ index === selection ? "\u276F" : " ",
3743
+ " ",
3744
+ index + 1,
3745
+ ". ",
3746
+ queuedPromptLabel(item)
3468
3747
  ] })
3469
- ] }) : null
3470
- ] }, qi)) });
3748
+ },
3749
+ `${index}:${item.text}`
3750
+ )) });
3471
3751
  }
3472
3752
  function MemoryReviewCards({ reviews, autoDismissSeconds }) {
3473
3753
  const review = reviews[0];
@@ -3565,6 +3845,15 @@ function jumpScroll(scroll, delta) {
3565
3845
  scroll.scrollTo(Math.max(0, target));
3566
3846
  }
3567
3847
  }
3848
+ function jumpToAdjacentTurn(scroll, elements, direction) {
3849
+ if (scroll === null) return false;
3850
+ const top = scroll.getScrollTop() + scroll.getPendingDelta();
3851
+ const positions = [...elements.values()].map((element) => ({ element, top: element.yogaNode?.getComputedTop() })).filter((item) => item.top !== void 0).sort((left, right) => left.top - right.top);
3852
+ const target = direction === "previous" ? positions.filter((item) => item.top < top - 1).at(-1) : positions.find((item) => item.top > top + 1);
3853
+ if (target === void 0) return false;
3854
+ scroll.scrollToElement(target.element);
3855
+ return true;
3856
+ }
3568
3857
  function selectWheelScrollTarget(transcript, mainTasks, subagentTasks, hoveredTrack) {
3569
3858
  if (hoveredTrack === "main" && mainTasks !== null) return mainTasks;
3570
3859
  if (hoveredTrack === "subagent" && subagentTasks !== null) return subagentTasks;
@@ -4496,6 +4785,7 @@ export {
4496
4785
  isOutputToggleShortcut,
4497
4786
  isPlatformShortcut,
4498
4787
  jumpScroll,
4788
+ jumpToAdjacentTurn,
4499
4789
  navigateHistory,
4500
4790
  navigatePromptHistory,
4501
4791
  outputToggleShortcut,