flavor-code 1.4.0-beta.9 → 1.4.1-beta.1

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.
@@ -19,9 +19,9 @@ import {
19
19
  thinkingWindow,
20
20
  transcriptReducer,
21
21
  withTimeout
22
- } from "./chunk-CWCYXOH6.js";
22
+ } from "./chunk-O2PC44GY.js";
23
23
  import "./chunk-N5MXHJKD.js";
24
- import "./chunk-EXOSMO57.js";
24
+ import "./chunk-7YFHRORG.js";
25
25
  import {
26
26
  Box_default,
27
27
  ClockContext,
@@ -34,18 +34,21 @@ import {
34
34
  instances_default,
35
35
  osc,
36
36
  shiftAnchor,
37
+ stringWidth,
37
38
  stripAnsi,
38
39
  useStdout,
39
40
  use_app_default,
40
41
  use_input_default
41
- } from "./chunk-XMRKRPNC.js";
42
+ } from "./chunk-VMQWXQEC.js";
42
43
  import {
43
- execFileNoThrow
44
+ execFileNoThrow,
45
+ getGraphemeSegmenter
44
46
  } from "./chunk-ZJCAQXA7.js";
45
47
  import "./chunk-S32JRDZK.js";
46
48
 
47
49
  // src/ui/app.tsx
48
50
  import { basename as basename3 } from "path";
51
+ import { homedir } from "os";
49
52
  import { useEffect as useEffect3, useReducer, useRef as useRef4, useState as useState2 } from "react";
50
53
 
51
54
  // src/claude-ink/hooks/use-animation-frame.ts
@@ -969,7 +972,13 @@ function wrapPromptInput(input, codePointCursor, options) {
969
972
  // src/ui/task-progress.tsx
970
973
  import { useRef as useRef3 } from "react";
971
974
  import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
972
- function TaskStatusLine({ block, interactive }) {
975
+ function TaskStatusLine({
976
+ block,
977
+ interactive,
978
+ textWidth,
979
+ maxTextLines,
980
+ showRoleBadge = true
981
+ }) {
973
982
  const running = block.state === "running";
974
983
  const { stdout } = useStdout();
975
984
  const foreground = interactive && (block.activity === "model" || block.task?.role === "main");
@@ -982,6 +991,8 @@ function TaskStatusLine({ block, interactive }) {
982
991
  const elapsed = block.startedAt === void 0 ? startedAt.current === void 0 ? 0 : Math.max(0, time - startedAt.current) : Math.max(0, Date.now() - block.startedAt);
983
992
  const presentation = statusPresentation(block, elapsed, foreground);
984
993
  const { metaColor, metaLabel, statusLabel, statusColor, text } = presentation;
994
+ const badge = showRoleBadge ? presentation.badge : void 0;
995
+ const badgeColor = presentation.badgeColor;
985
996
  let before = text;
986
997
  let after = "";
987
998
  if (statusLabel !== void 0) {
@@ -1007,14 +1018,26 @@ function TaskStatusLine({ block, interactive }) {
1007
1018
  thinkingScroll.current = { start: state.start, lastTime: time };
1008
1019
  return state.text;
1009
1020
  })() : void 0;
1021
+ const textSegments = [
1022
+ { text: `${presentation.glyph} `, color: presentation.color },
1023
+ ...badge ? [{ text: `${badge} `, color: badgeColor }] : [],
1024
+ { text: before, color: presentation.color },
1025
+ ...statusLabel === void 0 ? [] : [
1026
+ { text: " \xB7 ", color: presentation.color },
1027
+ { text: statusLabel, color: statusColor ?? presentation.color }
1028
+ ],
1029
+ ...after ? [{ text: after, color: presentation.color }] : [],
1030
+ ...metaLabel === void 0 ? [] : [{ text: ` ${metaLabel}`, color: metaColor ?? "ansi:blackBright" }]
1031
+ ];
1032
+ const boundedText = textWidth === void 0 || maxTextLines === void 0 ? void 0 : limitTaskTextSegments(textSegments, textWidth, maxTextLines).segments;
1010
1033
  return /* @__PURE__ */ jsxs2(Box_default, { flexDirection: "column", width: "100%", children: [
1011
- /* @__PURE__ */ jsxs2(Box_default, { ref, flexDirection: "row", children: [
1034
+ /* @__PURE__ */ jsx3(Box_default, { ref, flexDirection: "row", width: "100%", children: boundedText === void 0 ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
1012
1035
  /* @__PURE__ */ jsxs2(Text, { ...presentation.color === void 0 ? {} : { color: presentation.color }, children: [
1013
1036
  presentation.glyph,
1014
1037
  " "
1015
1038
  ] }),
1016
- presentation.badge ? /* @__PURE__ */ jsxs2(Text, { color: presentation.badgeColor, children: [
1017
- presentation.badge,
1039
+ badge ? /* @__PURE__ */ jsxs2(Text, { color: badgeColor, children: [
1040
+ badge,
1018
1041
  " "
1019
1042
  ] }) : null,
1020
1043
  /* @__PURE__ */ jsx3(Text, { ...presentation.color === void 0 ? {} : { color: presentation.color }, children: before }),
@@ -1027,10 +1050,69 @@ function TaskStatusLine({ block, interactive }) {
1027
1050
  " ",
1028
1051
  metaLabel
1029
1052
  ] })
1030
- ] }),
1053
+ ] }) : /* @__PURE__ */ jsx3(Text, { children: boundedText.map((segment, index) => /* @__PURE__ */ jsx3(Text, { ...segment.color === void 0 ? {} : { color: segment.color }, children: segment.text }, index)) }) }),
1031
1054
  thinkingLine === void 0 ? null : /* @__PURE__ */ jsx3(ThinkingLineView, { text: thinkingLine })
1032
1055
  ] });
1033
1056
  }
1057
+ function limitTaskTextSegments(segments, width, maxLines = 3) {
1058
+ const lineWidth = Math.max(3, Math.floor(width));
1059
+ const lineLimit = Math.max(1, Math.floor(maxLines));
1060
+ const lines = [[]];
1061
+ const widths = [0];
1062
+ let truncated = false;
1063
+ outer: for (let segmentIndex = 0; segmentIndex < segments.length; segmentIndex += 1) {
1064
+ const segment = segments[segmentIndex];
1065
+ for (const { segment: grapheme } of getGraphemeSegmenter().segment(segment.text)) {
1066
+ if (grapheme === "\n" || grapheme === "\r" || grapheme === "\r\n") {
1067
+ if (lines.length >= lineLimit) {
1068
+ truncated = true;
1069
+ break outer;
1070
+ }
1071
+ lines.push([]);
1072
+ widths.push(0);
1073
+ continue;
1074
+ }
1075
+ const glyphWidth = stringWidth(grapheme);
1076
+ const lineIndex = lines.length - 1;
1077
+ if (glyphWidth > 0 && widths[lineIndex] + glyphWidth > lineWidth && lines[lineIndex].length > 0) {
1078
+ if (lines.length >= lineLimit) {
1079
+ truncated = true;
1080
+ break outer;
1081
+ }
1082
+ lines.push([]);
1083
+ widths.push(0);
1084
+ }
1085
+ const target = lines.length - 1;
1086
+ lines[target].push({ value: grapheme, width: glyphWidth, segmentIndex });
1087
+ widths[target] = widths[target] + glyphWidth;
1088
+ }
1089
+ }
1090
+ if (truncated) {
1091
+ const lastIndex = lines.length - 1;
1092
+ const last = lines[lastIndex];
1093
+ while (last.length > 0 && widths[lastIndex] > lineWidth - 3) {
1094
+ widths[lastIndex] = widths[lastIndex] - last.pop().width;
1095
+ }
1096
+ while (last.length > 0 && /^\s$/u.test(last.at(-1).value)) {
1097
+ widths[lastIndex] = widths[lastIndex] - last.pop().width;
1098
+ }
1099
+ last.push({ value: "...", width: 3, segmentIndex: last.at(-1)?.segmentIndex ?? 0 });
1100
+ }
1101
+ const output = [];
1102
+ const append = (value, segmentIndex) => {
1103
+ const previous = output.at(-1);
1104
+ if (previous?.segmentIndex === segmentIndex) previous.text += value;
1105
+ else output.push({ text: value, color: segments[segmentIndex]?.color, segmentIndex });
1106
+ };
1107
+ lines.forEach((line, lineIndex) => {
1108
+ if (lineIndex > 0) append("\n", line[0]?.segmentIndex ?? output.at(-1)?.segmentIndex ?? 0);
1109
+ for (const token of line) append(token.value, token.segmentIndex);
1110
+ });
1111
+ return {
1112
+ truncated,
1113
+ segments: output.map(({ text, color }) => ({ text, ...color === void 0 ? {} : { color } }))
1114
+ };
1115
+ }
1034
1116
  var THINKING_RAIL = "#416f84";
1035
1117
  var THINKING_TEXT = "#91a0a8";
1036
1118
  function ThinkingLineView({ text }) {
@@ -1039,13 +1121,16 @@ function ThinkingLineView({ text }) {
1039
1121
  /* @__PURE__ */ jsx3(Text, { color: THINKING_TEXT, wrap: "truncate-end", children: text })
1040
1122
  ] });
1041
1123
  }
1042
- var SPLIT_TRACK_MIN_COLUMNS = 72;
1124
+ var SPLIT_TRACK_MIN_COLUMNS = 96;
1125
+ var SPLIT_TRACK_GUTTER_COLUMNS = 3;
1126
+ var SPLIT_TRACK_MAIN_RATIO = 0.44;
1043
1127
  function TaskProgressPanel({
1044
1128
  blocks,
1045
1129
  interactive,
1046
1130
  maxHeight = 8,
1047
1131
  columns = 80,
1048
- scrollRef,
1132
+ mainScrollRef,
1133
+ subagentScrollRef,
1049
1134
  onHoverChange
1050
1135
  }) {
1051
1136
  if (blocks.length === 0 || maxHeight <= 0) return null;
@@ -1054,54 +1139,210 @@ function TaskProgressPanel({
1054
1139
  const hasTasks = taskBlocks.length > 0;
1055
1140
  const hasSubagents = subagentBlocks.length > 0;
1056
1141
  const split = hasTasks && hasSubagents && columns >= SPLIT_TRACK_MIN_COLUMNS;
1057
- return /* @__PURE__ */ jsxs2(
1142
+ const hoverProps = (track) => ({
1143
+ onMouseEnter: () => onHoverChange?.(track)
1144
+ });
1145
+ return /* @__PURE__ */ jsx3(
1058
1146
  Box_default,
1059
1147
  {
1060
1148
  flexDirection: "column",
1061
1149
  flexShrink: 0,
1062
1150
  width: "100%",
1063
1151
  maxHeight,
1064
- onMouseEnter: () => onHoverChange?.(true),
1065
- onMouseLeave: () => onHoverChange?.(false),
1066
- children: [
1067
- split ? /* @__PURE__ */ jsx3(SplitTrackHeaders, {}) : /* @__PURE__ */ jsx3(TrackHeader, { role: hasTasks ? "main" : "subagent" }),
1068
- maxHeight > 1 ? /* @__PURE__ */ jsx3(
1069
- ScrollBox_default,
1070
- {
1071
- ...scrollRef === void 0 ? {} : { ref: scrollRef },
1072
- flexDirection: "column",
1073
- flexShrink: 1,
1074
- width: "100%",
1075
- maxHeight: maxHeight - 1,
1076
- children: split ? pairedTrackRows(taskBlocks, subagentBlocks).map(([task, subagent], index) => /* @__PURE__ */ jsxs2(Box_default, { flexDirection: "row", width: "100%", columnGap: 2, children: [
1077
- /* @__PURE__ */ jsx3(Box_default, { flexBasis: 0, flexGrow: 1, minWidth: 0, flexDirection: "column", children: task === void 0 ? null : /* @__PURE__ */ jsx3(TaskStatusLine, { block: task, interactive }) }),
1078
- /* @__PURE__ */ jsx3(Box_default, { flexBasis: 0, flexGrow: 1, minWidth: 0, flexDirection: "column", children: subagent === void 0 ? null : /* @__PURE__ */ jsx3(TaskStatusLine, { block: subagent, interactive }) })
1079
- ] }, `${task?.id ?? "task"}:${subagent?.id ?? "subagent"}:${index}`)) : hasTasks && hasSubagents ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
1080
- taskBlocks.map((block) => /* @__PURE__ */ jsx3(TaskStatusLine, { block, interactive }, block.id)),
1081
- /* @__PURE__ */ jsx3(TrackHeader, { role: "subagent" }),
1082
- subagentBlocks.map((block) => /* @__PURE__ */ jsx3(TaskStatusLine, { block, interactive }, block.id))
1083
- ] }) : (hasTasks ? taskBlocks : subagentBlocks).map((block) => /* @__PURE__ */ jsx3(TaskStatusLine, { block, interactive }, block.id))
1084
- }
1085
- ) : null
1086
- ]
1152
+ onMouseLeave: () => onHoverChange?.(null),
1153
+ children: split ? /* @__PURE__ */ jsx3(
1154
+ SplitTracks,
1155
+ {
1156
+ taskBlocks,
1157
+ subagentBlocks,
1158
+ interactive,
1159
+ height: maxHeight,
1160
+ columns,
1161
+ mainScrollRef,
1162
+ subagentScrollRef,
1163
+ hoverProps
1164
+ }
1165
+ ) : hasTasks && hasSubagents ? /* @__PURE__ */ jsx3(
1166
+ StackedTracks,
1167
+ {
1168
+ taskBlocks,
1169
+ subagentBlocks,
1170
+ interactive,
1171
+ height: maxHeight,
1172
+ columns,
1173
+ mainScrollRef,
1174
+ subagentScrollRef,
1175
+ hoverProps
1176
+ }
1177
+ ) : /* @__PURE__ */ jsx3(
1178
+ SingleTrack,
1179
+ {
1180
+ role: hasTasks ? "main" : "subagent",
1181
+ blocks: hasTasks ? taskBlocks : subagentBlocks,
1182
+ interactive,
1183
+ height: maxHeight,
1184
+ width: columns,
1185
+ scrollRef: hasTasks ? mainScrollRef : subagentScrollRef,
1186
+ hoverProps
1187
+ }
1188
+ )
1087
1189
  }
1088
1190
  );
1089
1191
  }
1090
- function TrackHeader({ role }) {
1091
- return /* @__PURE__ */ jsxs2(Text, { dimColor: true, children: [
1092
- "\u2500\u2500 ",
1093
- role === "main" ? "task plan" : "subagent exploration",
1094
- " \u2500\u2500"
1192
+ function SplitTracks({
1193
+ taskBlocks,
1194
+ subagentBlocks,
1195
+ interactive,
1196
+ height,
1197
+ columns,
1198
+ mainScrollRef,
1199
+ subagentScrollRef,
1200
+ hoverProps
1201
+ }) {
1202
+ const contentHeight = Math.max(0, height - 1);
1203
+ const availableWidth = columns - SPLIT_TRACK_GUTTER_COLUMNS;
1204
+ const leftWidth = Math.max(1, Math.floor(availableWidth * SPLIT_TRACK_MAIN_RATIO));
1205
+ const rightWidth = Math.max(1, availableWidth - leftWidth);
1206
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
1207
+ /* @__PURE__ */ jsxs2(Box_default, { flexDirection: "row", width: "100%", children: [
1208
+ /* @__PURE__ */ jsx3(Box_default, { width: leftWidth, ...hoverProps("main"), children: /* @__PURE__ */ jsx3(TrackHeader, { role: "main", count: taskBlocks.length, width: leftWidth }) }),
1209
+ /* @__PURE__ */ jsx3(Text, { color: "ansi:white", children: "\u2500\u252C\u2500" }),
1210
+ /* @__PURE__ */ jsx3(Box_default, { width: rightWidth, ...hoverProps("subagent"), children: /* @__PURE__ */ jsx3(TrackHeader, { role: "subagent", count: subagentBlocks.length, width: rightWidth }) })
1211
+ ] }),
1212
+ contentHeight <= 0 ? null : /* @__PURE__ */ jsxs2(Box_default, { flexDirection: "row", width: "100%", height: contentHeight, children: [
1213
+ /* @__PURE__ */ jsx3(Box_default, { width: leftWidth, minWidth: 0, flexDirection: "column", ...hoverProps("main"), children: /* @__PURE__ */ jsx3(
1214
+ TrackScrollBox,
1215
+ {
1216
+ ref: mainScrollRef,
1217
+ blocks: taskBlocks,
1218
+ interactive,
1219
+ height: contentHeight,
1220
+ width: leftWidth,
1221
+ maxTextLines: 2
1222
+ }
1223
+ ) }),
1224
+ /* @__PURE__ */ jsx3(TrackDivider, { height: contentHeight }),
1225
+ /* @__PURE__ */ jsx3(Box_default, { width: rightWidth, minWidth: 0, flexDirection: "column", ...hoverProps("subagent"), children: /* @__PURE__ */ jsx3(
1226
+ TrackScrollBox,
1227
+ {
1228
+ ref: subagentScrollRef,
1229
+ blocks: subagentBlocks,
1230
+ interactive,
1231
+ height: contentHeight,
1232
+ width: rightWidth,
1233
+ maxTextLines: 2
1234
+ }
1235
+ ) })
1236
+ ] })
1237
+ ] });
1238
+ }
1239
+ function StackedTracks({
1240
+ taskBlocks,
1241
+ subagentBlocks,
1242
+ interactive,
1243
+ height,
1244
+ columns,
1245
+ mainScrollRef,
1246
+ subagentScrollRef,
1247
+ hoverProps
1248
+ }) {
1249
+ if (height <= 1) {
1250
+ return /* @__PURE__ */ jsx3(Box_default, { ...hoverProps("main"), children: /* @__PURE__ */ jsx3(TrackHeader, { role: "main", count: taskBlocks.length, width: columns }) });
1251
+ }
1252
+ const mainHeight = Math.ceil(height / 2);
1253
+ const subagentHeight = Math.floor(height / 2);
1254
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
1255
+ /* @__PURE__ */ jsx3(
1256
+ SingleTrack,
1257
+ {
1258
+ role: "main",
1259
+ blocks: taskBlocks,
1260
+ interactive,
1261
+ height: mainHeight,
1262
+ width: columns,
1263
+ scrollRef: mainScrollRef,
1264
+ hoverProps
1265
+ }
1266
+ ),
1267
+ /* @__PURE__ */ jsx3(
1268
+ SingleTrack,
1269
+ {
1270
+ role: "subagent",
1271
+ blocks: subagentBlocks,
1272
+ interactive,
1273
+ height: subagentHeight,
1274
+ width: columns,
1275
+ scrollRef: subagentScrollRef,
1276
+ hoverProps
1277
+ }
1278
+ )
1095
1279
  ] });
1096
1280
  }
1097
- function SplitTrackHeaders() {
1098
- return /* @__PURE__ */ jsxs2(Box_default, { flexDirection: "row", width: "100%", columnGap: 2, children: [
1099
- /* @__PURE__ */ jsx3(Box_default, { flexBasis: 0, flexGrow: 1, minWidth: 0, children: /* @__PURE__ */ jsx3(TrackHeader, { role: "main" }) }),
1100
- /* @__PURE__ */ jsx3(Box_default, { flexBasis: 0, flexGrow: 1, minWidth: 0, children: /* @__PURE__ */ jsx3(TrackHeader, { role: "subagent" }) })
1281
+ function SingleTrack({
1282
+ role,
1283
+ blocks,
1284
+ interactive,
1285
+ height,
1286
+ width,
1287
+ scrollRef,
1288
+ hoverProps
1289
+ }) {
1290
+ const contentHeight = Math.max(0, height - 1);
1291
+ return /* @__PURE__ */ jsxs2(Box_default, { flexDirection: "column", width: "100%", height, ...hoverProps(role), children: [
1292
+ /* @__PURE__ */ jsx3(TrackHeader, { role, count: blocks.length, width }),
1293
+ contentHeight <= 0 ? null : /* @__PURE__ */ jsx3(
1294
+ TrackScrollBox,
1295
+ {
1296
+ ref: scrollRef,
1297
+ blocks,
1298
+ interactive,
1299
+ height: contentHeight,
1300
+ width,
1301
+ maxTextLines: 3
1302
+ }
1303
+ )
1101
1304
  ] });
1102
1305
  }
1103
- function pairedTrackRows(tasks, subagents) {
1104
- return Array.from({ length: Math.max(tasks.length, subagents.length) }, (_, index) => [tasks[index], subagents[index]]);
1306
+ function TrackScrollBox({
1307
+ ref,
1308
+ blocks,
1309
+ interactive,
1310
+ height,
1311
+ width,
1312
+ maxTextLines
1313
+ }) {
1314
+ return /* @__PURE__ */ jsx3(
1315
+ ScrollBox_default,
1316
+ {
1317
+ ...ref === void 0 ? {} : { ref },
1318
+ flexDirection: "column",
1319
+ flexShrink: 1,
1320
+ width: "100%",
1321
+ height,
1322
+ maxHeight: height,
1323
+ paddingX: 1,
1324
+ children: blocks.map((block) => /* @__PURE__ */ jsx3(
1325
+ TaskStatusLine,
1326
+ {
1327
+ block,
1328
+ interactive,
1329
+ textWidth: Math.max(3, width - 2),
1330
+ maxTextLines,
1331
+ showRoleBadge: false
1332
+ },
1333
+ block.id
1334
+ ))
1335
+ }
1336
+ );
1337
+ }
1338
+ function TrackHeader({ role, count, width }) {
1339
+ const label = role === "main" ? `task plan (${count})` : `subagents (${count})`;
1340
+ const prefix = `\u2500\u2500 ${label} `;
1341
+ const line = prefix.length >= width ? prefix.slice(0, Math.max(0, width)) : `${prefix}${"\u2500".repeat(width - prefix.length)}`;
1342
+ return /* @__PURE__ */ jsx3(Text, { color: "ansi:white", wrap: "truncate-end", children: line });
1343
+ }
1344
+ function TrackDivider({ height }) {
1345
+ return /* @__PURE__ */ jsx3(Box_default, { flexDirection: "column", width: SPLIT_TRACK_GUTTER_COLUMNS, flexShrink: 0, children: Array.from({ length: height }, (_, index) => /* @__PURE__ */ jsx3(Text, { color: "ansi:white", children: " \u2502 " }, index)) });
1105
1346
  }
1106
1347
 
1107
1348
  // src/ui/file-diff-style.ts
@@ -1117,6 +1358,12 @@ function fileDiffLineStyle(kind) {
1117
1358
  }
1118
1359
 
1119
1360
  // src/ui/slash-completion.ts
1361
+ var CANDIDATE_KIND_PRIORITY = {
1362
+ skill: 0,
1363
+ plugin: 0,
1364
+ command: 1,
1365
+ tool: 2
1366
+ };
1120
1367
  function slashCandidatePresentation(selected) {
1121
1368
  return {
1122
1369
  marker: selected ? "\u203A " : " ",
@@ -1177,6 +1424,8 @@ function deriveSlashCompletion(input, cursor, candidates, selectedIndex, visible
1177
1424
  const leftPrefix = left.name.toLowerCase().startsWith(normalized);
1178
1425
  const rightPrefix = right.name.toLowerCase().startsWith(normalized);
1179
1426
  if (leftPrefix !== rightPrefix) return leftPrefix ? -1 : 1;
1427
+ const kindPriority = CANDIDATE_KIND_PRIORITY[left.kind] - CANDIDATE_KIND_PRIORITY[right.kind];
1428
+ if (kindPriority !== 0) return kindPriority;
1180
1429
  return left.name.localeCompare(right.name);
1181
1430
  });
1182
1431
  if (items.length === 0) return null;
@@ -1651,26 +1900,118 @@ function timestamp(value) {
1651
1900
  return `${year}${month}${day}-${hour}${minute}${second}`;
1652
1901
  }
1653
1902
 
1903
+ // src/ui/prompt-history.ts
1904
+ import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
1905
+ import { mkdir as mkdir2, readFile, rename as rename2, writeFile } from "fs/promises";
1906
+ import { join as join2, resolve as resolve2 } from "path";
1907
+ var HISTORY_VERSION = 1;
1908
+ var PromptHistoryStore = class {
1909
+ #directory;
1910
+ #path;
1911
+ #workspace;
1912
+ #maxEntries;
1913
+ #writeChain = Promise.resolve();
1914
+ constructor(options) {
1915
+ this.#workspace = resolve2(options.workspace);
1916
+ this.#maxEntries = Math.max(1, Math.floor(options.maxEntries ?? 200));
1917
+ this.#directory = join2(resolve2(options.home), ".flavor-code", "history");
1918
+ const key = createHash2("sha256").update(this.#workspace).digest("hex").slice(0, 24);
1919
+ this.#path = join2(this.#directory, `${key}.json`);
1920
+ }
1921
+ async load() {
1922
+ try {
1923
+ const parsed = JSON.parse(await readFile(this.#path, "utf8"));
1924
+ if (parsed.version !== HISTORY_VERSION || parsed.workspace !== this.#workspace || !Array.isArray(parsed.entries)) return [];
1925
+ return parsed.entries.filter((entry) => typeof entry === "string" && entry.trim().length > 0).slice(-this.#maxEntries);
1926
+ } catch {
1927
+ return [];
1928
+ }
1929
+ }
1930
+ append(entry) {
1931
+ const normalized = entry.trim();
1932
+ if (normalized.length === 0) return this.#writeChain;
1933
+ this.#writeChain = this.#writeChain.catch(() => void 0).then(async () => {
1934
+ const entries = [...await this.load(), normalized].slice(-this.#maxEntries);
1935
+ await mkdir2(this.#directory, { recursive: true, mode: 448 });
1936
+ const temporary = join2(this.#directory, `.${randomUUID2()}.tmp`);
1937
+ const document = { version: HISTORY_VERSION, workspace: this.#workspace, entries };
1938
+ await writeFile(temporary, `${JSON.stringify(document, null, 2)}
1939
+ `, { encoding: "utf8", mode: 384 });
1940
+ await rename2(temporary, this.#path);
1941
+ });
1942
+ return this.#writeChain;
1943
+ }
1944
+ };
1945
+
1654
1946
  // src/ui/app.tsx
1655
1947
  import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1656
1948
  var HISTORY_CAP = 200;
1657
1949
  var BUILTIN_SLASH_CANDIDATES = MVP_COMMANDS.map((name) => ({ name, description: COMMAND_DESCRIPTIONS[name] }));
1658
1950
  var PROMPT_HORIZONTAL_PADDING = 1;
1951
+ function clonePromptDraft(state) {
1952
+ return {
1953
+ ...state,
1954
+ pastedBlocks: [...state.pastedBlocks],
1955
+ imageAttachments: [...state.imageAttachments]
1956
+ };
1957
+ }
1958
+ var PromptEditHistory = class {
1959
+ constructor(limit = 100) {
1960
+ this.limit = limit;
1961
+ }
1962
+ limit;
1963
+ #undo = [];
1964
+ #redo = [];
1965
+ record(previous, next) {
1966
+ if (previous.text === next.text && previous.pastedBlocks === next.pastedBlocks && previous.imageAttachments === next.imageAttachments) return;
1967
+ this.#undo.push(clonePromptDraft(previous));
1968
+ if (this.#undo.length > this.limit) this.#undo.shift();
1969
+ this.#redo.length = 0;
1970
+ }
1971
+ undo(current) {
1972
+ const previous = this.#undo.pop();
1973
+ if (previous === void 0) return void 0;
1974
+ this.#redo.push(clonePromptDraft(current));
1975
+ return clonePromptDraft(previous);
1976
+ }
1977
+ redo(current) {
1978
+ const next = this.#redo.pop();
1979
+ if (next === void 0) return void 0;
1980
+ this.#undo.push(clonePromptDraft(current));
1981
+ return clonePromptDraft(next);
1982
+ }
1983
+ reset() {
1984
+ this.#undo.length = 0;
1985
+ this.#redo.length = 0;
1986
+ }
1987
+ };
1988
+ function promptHistoryAction(character, key, platform = process.platform) {
1989
+ const modifier = platform === "darwin" ? key.super : key.ctrl;
1990
+ if (!modifier || character.toLowerCase() !== "z") return null;
1991
+ return key.shift ? "redo" : "undo";
1992
+ }
1993
+ function isCopyShortcut(character, key, platform = process.platform) {
1994
+ return character.toLowerCase() === "c" && (platform === "darwin" ? key.super : key.ctrl);
1995
+ }
1996
+ function isPlatformShortcut(character, key, letter, platform = process.platform) {
1997
+ if (character.toLowerCase() !== letter.toLowerCase()) return false;
1998
+ return platform === "darwin" ? key.super || key.ctrl : key.ctrl;
1999
+ }
2000
+ function outputToggleShortcut(platform = process.platform) {
2001
+ return platform === "darwin" ? "Cmd+O" : "Ctrl+O";
2002
+ }
1659
2003
  function removeLastCliImageOnBackspace(input, cursor, images) {
1660
2004
  if (input.length === 0 && cursor === 0 && images.length > 0) {
1661
2005
  return { handled: true, images: images.slice(0, -1) };
1662
2006
  }
1663
2007
  return { handled: false, images: [...images] };
1664
2008
  }
1665
- function prepareCliSubmission(input, images, activeSession) {
2009
+ function prepareCliSubmission(input, images) {
1666
2010
  const entered = input.trim();
1667
2011
  if (entered.length === 0 && images.length === 0) return { kind: "empty" };
1668
2012
  if (images.length > 0 && entered.startsWith("/")) {
1669
2013
  return { kind: "error", message: "Image attachments cannot be used with slash commands." };
1670
2014
  }
1671
- if (images.length > 0 && activeSession) {
1672
- return { kind: "error", message: "Images can only be attached to a new prompt." };
1673
- }
1674
2015
  const text = entered || "Analyze the attached image(s).";
1675
2016
  if (images.length === 0) return { kind: "ready", text, displayText: text };
1676
2017
  const content = [{ type: "text", text }, ...images];
@@ -1681,6 +2022,10 @@ function prepareCliSubmission(input, images, activeSession) {
1681
2022
  content
1682
2023
  };
1683
2024
  }
2025
+ function runningEscapeAction(activeSession, pendingCount) {
2026
+ if (!activeSession) return null;
2027
+ return pendingCount > 0 ? "restore-pending" : "interrupt";
2028
+ }
1684
2029
  function promptDelivery(activeSession, key) {
1685
2030
  if (!activeSession) return "prompt";
1686
2031
  return "followUp";
@@ -1694,52 +2039,100 @@ function resolvePromptDelivery(activeSession, key, input) {
1694
2039
  }
1695
2040
  return { delivery: promptDelivery(activeSession, key), prompt: input };
1696
2041
  }
1697
- var SinglePendingPrompt = class {
1698
- #value;
2042
+ function cloneQueuedPrompt(prompt) {
2043
+ return {
2044
+ text: prompt.text,
2045
+ displayText: prompt.displayText,
2046
+ ...prompt.content === void 0 ? {} : { content: [...prompt.content] }
2047
+ };
2048
+ }
2049
+ function normalizeQueuedPrompt(prompt) {
2050
+ if (typeof prompt === "string") {
2051
+ const text2 = prompt.trim();
2052
+ return text2.length === 0 ? void 0 : { text: text2, displayText: text2 };
2053
+ }
2054
+ const text = prompt.text.trim();
2055
+ if (text.length === 0) return void 0;
2056
+ const displayText = prompt.displayText.trim() || text;
2057
+ return cloneQueuedPrompt({
2058
+ text,
2059
+ displayText,
2060
+ ...prompt.content === void 0 ? {} : { content: prompt.content }
2061
+ });
2062
+ }
2063
+ function queuedPromptLabel(prompt) {
2064
+ const imageCount = prompt.content?.filter((block) => block.type === "image").length ?? 0;
2065
+ const text = prompt.text.replace(/\s+/gu, " ").trim();
2066
+ if (imageCount === 0) return text;
2067
+ return `${text} \xB7 ${imageCount} ${imageCount === 1 ? "image" : "images"}`;
2068
+ }
2069
+ function restoreQueuedPrompt(prompt) {
2070
+ const imageAttachments = prompt.content?.filter(
2071
+ (block) => block.type === "image"
2072
+ ) ?? [];
2073
+ return {
2074
+ text: prompt.text,
2075
+ cursor: [...prompt.text].length,
2076
+ pastedBlocks: [],
2077
+ imageAttachments
2078
+ };
2079
+ }
2080
+ var PendingPromptQueue = class {
2081
+ #values = [];
1699
2082
  get value() {
1700
- return this.#value;
2083
+ const value = this.#values.at(-1);
2084
+ return value === void 0 ? void 0 : cloneQueuedPrompt(value);
2085
+ }
2086
+ get values() {
2087
+ return this.#values.map(cloneQueuedPrompt);
2088
+ }
2089
+ get size() {
2090
+ return this.#values.length;
1701
2091
  }
1702
2092
  queue(prompt) {
1703
- const value = prompt.trim();
1704
- if (value.length === 0 || this.#value !== void 0) return false;
1705
- this.#value = value;
2093
+ const value = normalizeQueuedPrompt(prompt);
2094
+ if (value === void 0) return false;
2095
+ this.#values.push(value);
1706
2096
  return true;
1707
2097
  }
1708
2098
  cancel() {
1709
- const value = this.#value;
1710
- this.#value = void 0;
1711
- return value;
2099
+ const value = this.#values.pop();
2100
+ return value === void 0 ? void 0 : cloneQueuedPrompt(value);
1712
2101
  }
1713
2102
  take() {
1714
- return this.cancel();
2103
+ const value = this.#values.shift();
2104
+ return value === void 0 ? void 0 : cloneQueuedPrompt(value);
1715
2105
  }
1716
2106
  };
1717
2107
  async function runTerminalSubmissionChain(options) {
1718
- let prompt = options.initialPrompt;
1719
- let initial = true;
2108
+ let prompt = normalizeQueuedPrompt({
2109
+ text: options.initialPrompt,
2110
+ displayText: options.initialDisplayPrompt ?? options.initialPrompt,
2111
+ ...options.initialContent === void 0 ? {} : { content: options.initialContent }
2112
+ });
1720
2113
  while (prompt !== void 0) {
1721
- options.onStart(initial ? options.initialDisplayPrompt ?? prompt : prompt);
1722
- if (initial && options.initialSubmit !== void 0) {
2114
+ options.onStart(prompt.displayText);
2115
+ if (prompt.content === void 0) await submitSafely(options.session, prompt.text, options.report);
2116
+ else {
1723
2117
  try {
1724
- await options.initialSubmit(prompt);
2118
+ await options.session.submit({ text: prompt.text, content: prompt.content });
1725
2119
  } catch (error) {
1726
2120
  safeReport(options.report, safeUiError(error));
1727
2121
  }
1728
- } else {
1729
- await submitSafely(options.session, prompt, options.report);
1730
2122
  }
1731
2123
  options.onFinish();
1732
2124
  if (options.shouldContinue?.() === false) return;
1733
2125
  prompt = options.pending.take();
1734
- if (prompt !== void 0) options.onPendingConsumed();
1735
- initial = false;
2126
+ if (prompt !== void 0) options.onPendingConsumed(options.pending.values);
1736
2127
  }
1737
2128
  }
1738
- function classifyTerminalInput(key) {
2129
+ function classifyTerminalInput(key, activeSession = false) {
1739
2130
  if (key.wheelUp) return { type: "scroll", rows: -3 };
1740
2131
  if (key.wheelDown) return { type: "scroll", rows: 3 };
1741
2132
  if (key.pageUp) return { type: "page", fraction: -0.5 };
1742
2133
  if (key.pageDown) return { type: "page", fraction: 0.5 };
2134
+ if (activeSession && key.upArrow) return { type: "scroll", rows: -3 };
2135
+ if (activeSession && key.downArrow) return { type: "scroll", rows: 3 };
1743
2136
  if (key.upArrow) return { type: "history", direction: "up" };
1744
2137
  if (key.downArrow) return { type: "history", direction: "down" };
1745
2138
  return null;
@@ -1755,6 +2148,9 @@ function completionKeyAction(key, menuOpen) {
1755
2148
  function slashKeyAction(key, completion) {
1756
2149
  return completionKeyAction(key, completion !== null);
1757
2150
  }
2151
+ function canShowSlashCompletion(_activeSession, input, dismissedInput, blockedByModal) {
2152
+ return dismissedInput !== input && !blockedByModal;
2153
+ }
1758
2154
  function appRuntimeOptions(props, output, onApprovalChange) {
1759
2155
  return {
1760
2156
  workspace: props.workspace,
@@ -1779,6 +2175,8 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
1779
2175
  const [clipboardNotice, setClipboardNotice] = useState2();
1780
2176
  const [history, setHistory] = useState2([]);
1781
2177
  const [historyCursor, setHistoryCursor] = useState2(0);
2178
+ const [approvalExpanded, setApprovalExpanded] = useState2(false);
2179
+ const [expandedOutput, setExpandedOutput] = useState2(false);
1782
2180
  const [promptCursor, setPromptCursor] = useState2(0);
1783
2181
  const [slashCandidates, setSlashCandidates] = useState2(
1784
2182
  () => buildSlashCandidates(BUILTIN_SLASH_CANDIDATES, [], [])
@@ -1788,7 +2186,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
1788
2186
  const [mentionCandidates, setMentionCandidates] = useState2([]);
1789
2187
  const [mentionSelection, setMentionSelection] = useState2(0);
1790
2188
  const [dismissedMentionInput, setDismissedMentionInput] = useState2();
1791
- const [pendingPrompt, setPendingPrompt] = useState2();
2189
+ const [pendingPrompts, setPendingPrompts] = useState2([]);
1792
2190
  const [revision, setRevision] = useState2(0);
1793
2191
  const [ideContext, setIdeContext] = useState2();
1794
2192
  const [questionIndex, setQuestionIndex] = useState2(0);
@@ -1799,11 +2197,12 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
1799
2197
  const [updateTo, setUpdateTo] = useState2();
1800
2198
  const [transcript, dispatch] = useReducer(transcriptReducer, void 0, createTranscriptState);
1801
2199
  const scrollRef = useRef4(null);
1802
- const taskScrollRef = useRef4(null);
1803
- const taskPanelHovered = useRef4(false);
2200
+ const mainTaskScrollRef = useRef4(null);
2201
+ const subagentTaskScrollRef = useRef4(null);
2202
+ const hoveredTaskTrack = useRef4(null);
1804
2203
  const runtimeRef = useRef4(void 0);
1805
2204
  const pendingPromptRef = useRef4(void 0);
1806
- pendingPromptRef.current ??= new SinglePendingPrompt();
2205
+ pendingPromptRef.current ??= new PendingPromptQueue();
1807
2206
  const closing = useRef4(false);
1808
2207
  const clipboardBusy = useRef4(false);
1809
2208
  const textBuf = useRef4({ pending: "", timer: null });
@@ -1814,8 +2213,15 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
1814
2213
  pastedBlocks,
1815
2214
  imageAttachments
1816
2215
  });
2216
+ const promptEditHistory = useRef4(new PromptEditHistory());
2217
+ const historyRef = useRef4([]);
2218
+ const historyCursorRef = useRef4(0);
2219
+ const historyDraftRef = useRef4(void 0);
2220
+ const reverseSearchRef = useRef4(void 0);
2221
+ const promptHistoryStoreRef = useRef4(void 0);
2222
+ promptHistoryStoreRef.current ??= new PromptHistoryStore({ home: home ?? homedir(), workspace, maxEntries: HISTORY_CAP });
1817
2223
  promptDraftRef.current = { text: input, cursor: promptCursor, pastedBlocks, imageAttachments };
1818
- const commitPromptDraft = (next) => {
2224
+ const commitPromptDraft = (next, recordHistory = true) => {
1819
2225
  const current = promptDraftRef.current;
1820
2226
  const resolved = {
1821
2227
  text: next.text,
@@ -1823,6 +2229,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
1823
2229
  pastedBlocks: next.pastedBlocks ?? current.pastedBlocks,
1824
2230
  imageAttachments: next.imageAttachments ?? current.imageAttachments
1825
2231
  };
2232
+ if (recordHistory) promptEditHistory.current.record(current, resolved);
1826
2233
  promptDraftRef.current = resolved;
1827
2234
  setInput(resolved.text);
1828
2235
  setPromptCursor(resolved.cursor);
@@ -1830,6 +2237,19 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
1830
2237
  if (next.imageAttachments !== void 0) setImageAttachments(next.imageAttachments);
1831
2238
  };
1832
2239
  useTerminalTitle("Flavor Code");
2240
+ useEffect3(() => {
2241
+ let disposed = false;
2242
+ void promptHistoryStoreRef.current.load().then((entries) => {
2243
+ if (disposed) return;
2244
+ historyRef.current = entries;
2245
+ historyCursorRef.current = entries.length;
2246
+ setHistory(entries);
2247
+ setHistoryCursor(entries.length);
2248
+ });
2249
+ return () => {
2250
+ disposed = true;
2251
+ };
2252
+ }, [workspace, home]);
1833
2253
  const flushText = () => {
1834
2254
  flushThinking();
1835
2255
  const t = textBuf.current;
@@ -2043,8 +2463,14 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2043
2463
  setQuestionAnswers({});
2044
2464
  setCustomQuestionActive(false);
2045
2465
  }, [questions]);
2466
+ useEffect3(() => setApprovalExpanded(false), [approval?.id]);
2046
2467
  const derivedSlashCompletion = deriveSlashCompletion(input, promptCursor, slashCandidates, slashSelection);
2047
- const slashCompletion = dismissedSlashInput === input || transcript.active !== void 0 || approval !== void 0 || questions !== void 0 ? null : derivedSlashCompletion;
2468
+ const slashCompletion = canShowSlashCompletion(
2469
+ transcript.active !== void 0,
2470
+ input,
2471
+ dismissedSlashInput,
2472
+ approval !== void 0 || questions !== void 0
2473
+ ) ? derivedSlashCompletion : null;
2048
2474
  const derivedMentionCompletion = slashCompletion === null ? deriveMentionCompletion(input, promptCursor, mentionCandidates, mentionSelection) : null;
2049
2475
  const mentionCompletion = dismissedMentionInput === input || transcript.active !== void 0 || approval !== void 0 || questions !== void 0 ? null : derivedMentionCompletion;
2050
2476
  const completedTokenLength = completedSlashTokenLength(input, slashCandidates, slashCompletion !== null);
@@ -2062,11 +2488,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2062
2488
  setClipboardNotice("Flavor is still starting; try pasting the image again.");
2063
2489
  return;
2064
2490
  }
2065
- if (transcript.active !== void 0) {
2066
- setClipboardNotice("Images can only be attached to a new prompt.");
2067
- return;
2068
- }
2069
- if (imageAttachments.length >= DEFAULT_MAX_IMAGES) {
2491
+ if (promptDraftRef.current.imageAttachments.length >= DEFAULT_MAX_IMAGES) {
2070
2492
  setClipboardNotice(`A prompt can contain at most ${DEFAULT_MAX_IMAGES} images.`);
2071
2493
  return;
2072
2494
  }
@@ -2080,14 +2502,15 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2080
2502
  return;
2081
2503
  }
2082
2504
  const incomingBytes = Buffer.byteLength(attachment.dataBase64, "base64");
2083
- const currentBytes = imageAttachments.reduce((sum, image) => sum + image.bytes, 0);
2505
+ const currentDraft = promptDraftRef.current;
2506
+ const currentBytes = currentDraft.imageAttachments.reduce((sum, image) => sum + image.bytes, 0);
2084
2507
  if (currentBytes + incomingBytes > DEFAULT_MAX_TOTAL_IMAGE_BYTES) {
2085
2508
  throw new Error(`Image attachments exceed the maximum total size of ${DEFAULT_MAX_TOTAL_IMAGE_BYTES} bytes`);
2086
2509
  }
2087
2510
  const [stored] = await new SessionAssetStore({ workspace }).store(active.sessionId, [attachment]);
2088
2511
  if (stored === void 0) throw new Error("Clipboard image could not be stored");
2089
- setImageAttachments((current) => [...current, stored]);
2090
- setClipboardNotice(`Added [Image #${imageAttachments.length + 1}] ${stored.name ?? basename3(stored.source.path)}`);
2512
+ commitPromptDraft({ ...currentDraft, imageAttachments: [...currentDraft.imageAttachments, stored] });
2513
+ setClipboardNotice(`Added [Image #${currentDraft.imageAttachments.length + 1}] ${stored.name ?? basename3(stored.source.path)}`);
2091
2514
  } catch (error) {
2092
2515
  setClipboardNotice(safeUiError(error));
2093
2516
  } finally {
@@ -2095,9 +2518,17 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2095
2518
  }
2096
2519
  };
2097
2520
  use_input_default((character, key, event) => {
2098
- const terminalAction = classifyTerminalInput(key);
2521
+ const terminalAction = classifyTerminalInput(
2522
+ key,
2523
+ transcript.active !== void 0 && slashCompletion === null && mentionCompletion === null
2524
+ );
2099
2525
  if (terminalAction?.type === "scroll") {
2100
- const scroll = selectWheelScrollTarget(scrollRef.current, taskScrollRef.current, taskPanelHovered.current);
2526
+ const scroll = selectWheelScrollTarget(
2527
+ scrollRef.current,
2528
+ mainTaskScrollRef.current,
2529
+ subagentTaskScrollRef.current,
2530
+ hoveredTaskTrack.current
2531
+ );
2101
2532
  if (scroll !== null) {
2102
2533
  if (terminalAction.rows < 0) scrollUp(scroll, -terminalAction.rows);
2103
2534
  else scrollDown(scroll, terminalAction.rows);
@@ -2110,12 +2541,17 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2110
2541
  return;
2111
2542
  }
2112
2543
  const active = runtimeRef.current;
2113
- if (key.ctrl && character === "c") {
2114
- if (selection.hasSelection()) {
2115
- selection.copySelection();
2116
- } else {
2117
- interrupt();
2118
- }
2544
+ if (isCopyShortcut(character, key)) {
2545
+ if (selection.hasSelection()) selection.copySelection();
2546
+ else if (process.platform !== "darwin") interrupt();
2547
+ return;
2548
+ }
2549
+ if (key.ctrl && character.toLowerCase() === "c") {
2550
+ interrupt();
2551
+ return;
2552
+ }
2553
+ if (isPlatformShortcut(character, key, "o")) {
2554
+ setExpandedOutput((value) => !value);
2119
2555
  return;
2120
2556
  }
2121
2557
  if (shouldReadClipboardImage(character, key, event.keypress.isPasted)) {
@@ -2123,8 +2559,15 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2123
2559
  return;
2124
2560
  }
2125
2561
  if (active?.approvals.pending !== void 0) {
2562
+ if (character.toLowerCase() === "v") setApprovalExpanded((value) => !value);
2126
2563
  if (character.toLowerCase() === "y") active.approvals.resolve("once");
2127
2564
  if (character.toLowerCase() === "n" || key.escape) active.approvals.resolve("deny");
2565
+ if (character.toLowerCase() === "e") {
2566
+ void Promise.resolve(active.services.setPermissionMode("acceptEdits")).then(() => {
2567
+ setRevision((value) => value + 1);
2568
+ setClipboardNotice("Permission mode changed to acceptEdits; review the current request, then allow or deny it.");
2569
+ }).catch((error) => dispatch({ type: "submit-error", message: safeUiError(error) }));
2570
+ }
2128
2571
  if (character.toLowerCase() === "a") {
2129
2572
  if (isDestructiveTool(active.approvals.pending.tool)) {
2130
2573
  active.approvals.resolve("once");
@@ -2188,23 +2631,35 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2188
2631
  setPromptCursor(0);
2189
2632
  return;
2190
2633
  }
2191
- if (!key.ctrl && !key.meta && character) setCustomQuestionActive(true);
2634
+ if (!key.ctrl && !key.meta && !key.super && character) setCustomQuestionActive(true);
2192
2635
  else return;
2193
2636
  }
2194
2637
  }
2195
- if (key.escape && transcript.active !== void 0) {
2196
- const restored = pendingPromptRef.current.cancel();
2197
- if (restored !== void 0) {
2198
- setPendingPrompt(void 0);
2199
- setInput(restored);
2200
- setPastedBlocks([]);
2201
- setPromptCursor([...restored].length);
2202
- setSlashSelection(0);
2203
- setDismissedSlashInput(void 0);
2204
- setMentionSelection(0);
2205
- setDismissedMentionInput(void 0);
2206
- return;
2207
- }
2638
+ const historyAction = promptHistoryAction(character, key);
2639
+ if (historyAction !== null) {
2640
+ const draft = promptDraftRef.current;
2641
+ const restored = historyAction === "undo" ? promptEditHistory.current.undo(draft) : promptEditHistory.current.redo(draft);
2642
+ if (restored !== void 0) commitPromptDraft(restored, false);
2643
+ setSlashSelection(0);
2644
+ setDismissedSlashInput(void 0);
2645
+ setMentionSelection(0);
2646
+ setDismissedMentionInput(void 0);
2647
+ return;
2648
+ }
2649
+ if (isPlatformShortcut(character, key, "r")) {
2650
+ const draft = promptDraftRef.current;
2651
+ if (historyCursorRef.current === historyRef.current.length) historyDraftRef.current = clonePromptDraft(draft);
2652
+ const previous = reverseSearchRef.current;
2653
+ const query = previous?.query ?? draft.text;
2654
+ const match = reverseSearchHistory(historyRef.current, query, previous?.cursor ?? historyRef.current.length);
2655
+ if (match !== void 0) {
2656
+ reverseSearchRef.current = { query, cursor: match.cursor };
2657
+ historyCursorRef.current = match.cursor;
2658
+ setHistoryCursor(match.cursor);
2659
+ commitPromptDraft({ text: match.input, cursor: match.promptCursor, pastedBlocks: [], imageAttachments: [] }, false);
2660
+ setClipboardNotice(`reverse-i-search: ${query || "(latest)"} \xB7 Ctrl/Cmd+R for older match`);
2661
+ } else setClipboardNotice(`No older history match for ${query || "(latest)"}.`);
2662
+ return;
2208
2663
  }
2209
2664
  const menuAction = slashKeyAction(key, slashCompletion);
2210
2665
  if (menuAction?.type === "select" && slashCompletion !== null) {
@@ -2215,8 +2670,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2215
2670
  const selected = slashCompletion.items[slashCompletion.selectedIndex];
2216
2671
  if (selected !== void 0) {
2217
2672
  const next = completeSlashSelection(input, promptCursor, selected.name);
2218
- setInput(next.text);
2219
- setPromptCursor(next.cursor);
2673
+ commitPromptDraft(next);
2220
2674
  setDismissedSlashInput(next.text);
2221
2675
  }
2222
2676
  return;
@@ -2239,9 +2693,28 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2239
2693
  setDismissedMentionInput(input);
2240
2694
  return;
2241
2695
  }
2696
+ if (key.escape && transcript.active !== void 0) {
2697
+ const escapeAction = runningEscapeAction(true, pendingPromptRef.current.size);
2698
+ if (escapeAction === "restore-pending") {
2699
+ const restored = pendingPromptRef.current.cancel();
2700
+ if (restored === void 0) return;
2701
+ setPendingPrompts([...pendingPromptRef.current.values]);
2702
+ const restoredDraft = restoreQueuedPrompt(restored);
2703
+ promptEditHistory.current.reset();
2704
+ commitPromptDraft(restoredDraft, false);
2705
+ setClipboardNotice(restoredDraft.imageAttachments.length === 0 ? void 0 : `Restored ${restoredDraft.imageAttachments.length} pending ${restoredDraft.imageAttachments.length === 1 ? "image" : "images"}.`);
2706
+ setSlashSelection(0);
2707
+ setDismissedSlashInput(void 0);
2708
+ setMentionSelection(0);
2709
+ setDismissedMentionInput(void 0);
2710
+ return;
2711
+ }
2712
+ interrupt();
2713
+ return;
2714
+ }
2242
2715
  if (key.return) {
2243
2716
  const images = [...imageAttachments];
2244
- const prepared = prepareCliSubmission(input, images, transcript.active !== void 0);
2717
+ const prepared = prepareCliSubmission(input, images);
2245
2718
  if (prepared.kind === "empty" || active === void 0) return;
2246
2719
  if (prepared.kind === "error") {
2247
2720
  setClipboardNotice(prepared.message);
@@ -2251,16 +2724,27 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2251
2724
  const { delivery, prompt } = resolvePromptDelivery(transcript.active !== void 0, key, submittedText);
2252
2725
  if (!prompt) return;
2253
2726
  if (delivery === "followUp" && transcript.active !== void 0) {
2254
- if (!pendingPromptRef.current.queue(prompt)) return;
2255
- setPendingPrompt(prompt);
2727
+ if (!pendingPromptRef.current.queue({
2728
+ text: prompt,
2729
+ displayText: prepared.content === void 0 ? prompt : prepared.displayText,
2730
+ ...prepared.content === void 0 ? {} : { content: prepared.content }
2731
+ })) return;
2732
+ setPendingPrompts([...pendingPromptRef.current.values]);
2256
2733
  }
2257
2734
  scrollRef.current?.scrollToBottom();
2258
- setHistory((current) => [...current, submittedText].slice(-HISTORY_CAP));
2259
- setHistoryCursor(history.length + 1);
2735
+ const nextHistory = [...historyRef.current, submittedText].slice(-HISTORY_CAP);
2736
+ historyRef.current = nextHistory;
2737
+ historyCursorRef.current = nextHistory.length;
2738
+ historyDraftRef.current = void 0;
2739
+ reverseSearchRef.current = void 0;
2740
+ setHistory(nextHistory);
2741
+ setHistoryCursor(nextHistory.length);
2742
+ void promptHistoryStoreRef.current.append(submittedText).catch(() => void 0);
2260
2743
  setInput("");
2261
2744
  setPastedBlocks([]);
2262
2745
  setImageAttachments([]);
2263
2746
  promptDraftRef.current = { text: "", cursor: 0, pastedBlocks: [], imageAttachments: [] };
2747
+ promptEditHistory.current.reset();
2264
2748
  setClipboardNotice(void 0);
2265
2749
  setPromptCursor(0);
2266
2750
  setSlashSelection(0);
@@ -2273,15 +2757,12 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2273
2757
  initialPrompt: prompt,
2274
2758
  ...prepared.content === void 0 ? {} : {
2275
2759
  initialDisplayPrompt: prepared.displayText,
2276
- initialSubmit: (text) => active.session.submit({
2277
- text,
2278
- content: prepared.content
2279
- })
2760
+ initialContent: prepared.content
2280
2761
  },
2281
2762
  pending: pendingPromptRef.current,
2282
2763
  onStart: (next) => dispatch({ type: "submit", prompt: next }),
2283
2764
  onFinish: () => dispatch({ type: "finish" }),
2284
- onPendingConsumed: () => setPendingPrompt(void 0),
2765
+ onPendingConsumed: (remaining) => setPendingPrompts([...remaining]),
2285
2766
  report: (error) => dispatch({ type: "submit-error", message: error }),
2286
2767
  shouldContinue: () => !closing.current
2287
2768
  });
@@ -2295,11 +2776,11 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2295
2776
  return;
2296
2777
  }
2297
2778
  if (key.backspace) {
2779
+ reverseSearchRef.current = void 0;
2298
2780
  const draft = promptDraftRef.current;
2299
2781
  const imageEdit = removeLastCliImageOnBackspace(draft.text, draft.cursor, draft.imageAttachments);
2300
2782
  if (imageEdit.handled) {
2301
- setImageAttachments(imageEdit.images);
2302
- promptDraftRef.current = { ...draft, imageAttachments: imageEdit.images };
2783
+ commitPromptDraft({ ...draft, imageAttachments: imageEdit.images });
2303
2784
  setClipboardNotice(imageEdit.images.length === 0 ? "Removed image attachment." : `Removed image attachment; ${imageEdit.images.length} remaining.`);
2304
2785
  return;
2305
2786
  }
@@ -2314,6 +2795,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2314
2795
  setMentionSelection(0);
2315
2796
  setDismissedMentionInput(void 0);
2316
2797
  } else if (key.delete) {
2798
+ reverseSearchRef.current = void 0;
2317
2799
  const draft = promptDraftRef.current;
2318
2800
  commitPromptDraft({ ...editPrompt({ text: draft.text, cursor: draft.cursor }, { type: "delete" }), pastedBlocks: draft.pastedBlocks });
2319
2801
  setSlashSelection(0);
@@ -2321,28 +2803,31 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2321
2803
  setMentionSelection(0);
2322
2804
  setDismissedMentionInput(void 0);
2323
2805
  } else if (key.leftArrow) {
2806
+ reverseSearchRef.current = void 0;
2324
2807
  const draft = promptDraftRef.current;
2325
2808
  commitPromptDraft({ ...editPrompt({ text: draft.text, cursor: draft.cursor }, { type: "left" }), pastedBlocks: draft.pastedBlocks });
2326
2809
  } else if (key.rightArrow) {
2810
+ reverseSearchRef.current = void 0;
2327
2811
  const draft = promptDraftRef.current;
2328
2812
  commitPromptDraft({ ...editPrompt({ text: draft.text, cursor: draft.cursor }, { type: "right" }), pastedBlocks: draft.pastedBlocks });
2329
- } else if (terminalAction?.type === "history" && terminalAction.direction === "up" && history.length) {
2330
- const next = navigateHistory({ history, cursor: historyCursor }, "up");
2331
- setHistoryCursor(next.cursor);
2332
- commitPromptDraft({ text: next.input, cursor: next.promptCursor, pastedBlocks: [] });
2333
- setSlashSelection(0);
2334
- setDismissedSlashInput(void 0);
2335
- setMentionSelection(0);
2336
- setDismissedMentionInput(void 0);
2337
- } else if (terminalAction?.type === "history" && terminalAction.direction === "down" && history.length) {
2338
- const next = navigateHistory({ history, cursor: historyCursor }, "down");
2813
+ } else if (terminalAction?.type === "history" && historyRef.current.length) {
2814
+ const next = navigatePromptHistory({
2815
+ history: historyRef.current,
2816
+ cursor: historyCursorRef.current,
2817
+ current: promptDraftRef.current,
2818
+ ...historyDraftRef.current === void 0 ? {} : { stashed: historyDraftRef.current }
2819
+ }, terminalAction.direction);
2820
+ historyCursorRef.current = next.cursor;
2821
+ historyDraftRef.current = next.stashed;
2822
+ reverseSearchRef.current = void 0;
2339
2823
  setHistoryCursor(next.cursor);
2340
- commitPromptDraft({ text: next.input, cursor: next.promptCursor, pastedBlocks: [] });
2824
+ commitPromptDraft(next.draft, false);
2341
2825
  setSlashSelection(0);
2342
2826
  setDismissedSlashInput(void 0);
2343
2827
  setMentionSelection(0);
2344
2828
  setDismissedMentionInput(void 0);
2345
- } else if (!key.ctrl && !key.meta && character) {
2829
+ } else if (!key.ctrl && !key.meta && !key.super && character) {
2830
+ reverseSearchRef.current = void 0;
2346
2831
  const draft = promptDraftRef.current;
2347
2832
  const nextPastedBlocks = /[\r\n]/u.test(character) ? [...draft.pastedBlocks, { id: draft.pastedBlocks.length + 1, text: character }] : draft.pastedBlocks;
2348
2833
  commitPromptDraft({
@@ -2388,17 +2873,20 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2388
2873
  ...ideContext === void 0 ? {} : { ideContext },
2389
2874
  completedSlashTokenLength: completedTokenLength,
2390
2875
  scrollRef,
2391
- taskScrollRef,
2392
- onTaskPanelHoverChange: (hovered) => {
2393
- taskPanelHovered.current = hovered;
2876
+ mainTaskScrollRef,
2877
+ subagentTaskScrollRef,
2878
+ onTaskPanelHoverChange: (track) => {
2879
+ hoveredTaskTrack.current = track;
2394
2880
  },
2395
2881
  ...slashCompletion === null ? {} : { completion: slashCompletion },
2396
2882
  ...mentionCompletion === null ? {} : { mentionCompletion, onMentionSelect: selectMention },
2397
2883
  ...approval === void 0 ? {} : { approval },
2884
+ approvalExpanded,
2885
+ expandedOutput,
2398
2886
  ...questions === void 0 ? {} : { questions },
2399
2887
  memoryReviews,
2400
2888
  memoryAutoDismissSeconds,
2401
- ...pendingPrompt === void 0 ? {} : { pendingPrompt },
2889
+ pendingPrompts,
2402
2890
  questionIndex,
2403
2891
  questionAnswers,
2404
2892
  customQuestionActive
@@ -2433,6 +2921,46 @@ var CLI_VISIBLE_TURN_LIMIT = 40;
2433
2921
  var CLI_VISIBLE_BLOCK_LIMIT = 320;
2434
2922
  var CLI_VISIBLE_BLOCKS_PER_TURN = 80;
2435
2923
  var CLI_VISIBLE_TEXT_CHARS = 32e3;
2924
+ function approvalDetailLines(approval) {
2925
+ const lines = [];
2926
+ if (approval.agent !== "main") lines.push(`Agent: ${approval.agent}`);
2927
+ if (approval.command !== void 0) {
2928
+ const command = [approval.command, ...(approval.args ?? []).map(formatApprovalArgument)].join(" ");
2929
+ lines.push(`Command: ${command}`);
2930
+ } else if (approval.args !== void 0 && approval.args.length > 0) {
2931
+ lines.push(`Arguments: ${approval.args.map(formatApprovalArgument).join(" ")}`);
2932
+ }
2933
+ if (approval.cwd !== void 0) lines.push(`Working directory: ${approval.cwd}`);
2934
+ for (const [index, path] of (approval.paths ?? []).entries()) {
2935
+ lines.push(`${approval.paths.length === 1 ? "Path" : `Path ${index + 1}`}: ${path}`);
2936
+ }
2937
+ const inputLines = approvalInputLines(approval.input);
2938
+ if (inputLines.length > 0) lines.push("Tool input:", ...inputLines.map((line) => ` ${line}`));
2939
+ if (lines.length === 0) lines.push("No additional parameters were supplied.");
2940
+ return lines;
2941
+ }
2942
+ function formatApprovalArgument(value) {
2943
+ return /\s|["']/u.test(value) ? JSON.stringify(value) : value;
2944
+ }
2945
+ function approvalInputLines(input) {
2946
+ if (input === void 0) return [];
2947
+ if (typeof input !== "object" || input === null || Array.isArray(input)) return [formatApprovalValue(input)];
2948
+ const lines = [];
2949
+ for (const [key, value] of Object.entries(input)) {
2950
+ if (typeof value === "string" && /[\r\n]/u.test(value)) {
2951
+ lines.push(`${key}: |`, ...value.replace(/\r\n/gu, "\n").split("\n").map((line) => ` ${line}`));
2952
+ } else lines.push(`${key}: ${formatApprovalValue(value)}`);
2953
+ }
2954
+ return lines;
2955
+ }
2956
+ function formatApprovalValue(value) {
2957
+ if (typeof value === "string") return value;
2958
+ try {
2959
+ return JSON.stringify(value);
2960
+ } catch {
2961
+ return String(value);
2962
+ }
2963
+ }
2436
2964
  function boundedCliDisplayText(text, limit = CLI_VISIBLE_TEXT_CHARS) {
2437
2965
  if (text.length <= limit) return text;
2438
2966
  const half = Math.floor((limit - 80) / 2);
@@ -2511,8 +3039,10 @@ function TerminalLayout({
2511
3039
  columns,
2512
3040
  rows = 24,
2513
3041
  activeSession,
2514
- pendingPrompt,
3042
+ pendingPrompts = [],
2515
3043
  approval,
3044
+ approvalExpanded = false,
3045
+ expandedOutput = false,
2516
3046
  questions,
2517
3047
  memoryReviews = [],
2518
3048
  memoryAutoDismissSeconds = 0,
@@ -2524,12 +3054,14 @@ function TerminalLayout({
2524
3054
  onMentionSelect,
2525
3055
  completedSlashTokenLength: tokenLength = 0,
2526
3056
  scrollRef,
2527
- taskScrollRef,
3057
+ mainTaskScrollRef,
3058
+ subagentTaskScrollRef,
2528
3059
  onTaskPanelHoverChange,
2529
3060
  onPromptCursorChange,
2530
3061
  ideContext
2531
3062
  }) {
2532
3063
  const dividerWidth = Math.max(1, columns - 1);
3064
+ const outputShortcut = outputToggleShortcut();
2533
3065
  const showWelcome = completed.length === 0 && active === void 0;
2534
3066
  const activeCompletion = completion ?? mentionCompletion;
2535
3067
  const menuRows = activeCompletion === void 0 ? 0 : Math.min(6, activeCompletion.items.length - activeCompletion.windowStart);
@@ -2542,11 +3074,12 @@ function TerminalLayout({
2542
3074
  (block) => !(block.kind === "status" && block.task !== void 0)
2543
3075
  )
2544
3076
  };
2545
- const activeWithoutTasks = rawActiveWithoutTasks === void 0 ? void 0 : boundedCliTurn(rawActiveWithoutTasks).turn;
2546
- const completedWindow = cliTranscriptWindow(completed);
3077
+ const activeWithoutTasks = rawActiveWithoutTasks === void 0 ? void 0 : expandedOutput ? rawActiveWithoutTasks : boundedCliTurn(rawActiveWithoutTasks).turn;
3078
+ const completedWindow = expandedOutput ? { turns: completed, hiddenTurns: 0, hiddenBlocks: 0 } : cliTranscriptWindow(completed);
2547
3079
  const questionRows = questions === void 0 ? 0 : 4 + (questions[questionIndex]?.options.length ?? 0) + questions.length * 2;
2548
3080
  const memoryReviewRows = memoryReviews.length === 0 ? 0 : 5;
2549
- const fixedBottomRows = (approval === void 0 ? 0 : 3) + questionRows + memoryReviewRows + menuRows + (pendingPrompt === void 0 ? 0 : 1) + imageAttachments.length + (clipboardNotice === void 0 ? 0 : 1) + 2;
3081
+ const approvalRows = approval === void 0 ? 0 : 3 + (approvalExpanded ? approvalDetailLines(approval).length : 0);
3082
+ const fixedBottomRows = approvalRows + questionRows + memoryReviewRows + menuRows + (pendingPrompts.length === 0 ? 0 : 1) + imageAttachments.length + (clipboardNotice === void 0 ? 0 : 1) + 2;
2550
3083
  const taskPanelRows = taskPanelViewportRows(rows, fixedBottomRows, activeTaskBlocks.length > 0);
2551
3084
  const availableBottomRows = Math.max(1, rows - taskPanelRows - 1);
2552
3085
  const bottomMaxRows = Math.min(availableBottomRows, Math.max(Math.floor(rows / 2), fixedBottomRows + 1));
@@ -2565,15 +3098,17 @@ function TerminalLayout({
2565
3098
  completedWindow.hiddenTurns,
2566
3099
  " earlier turns and ",
2567
3100
  completedWindow.hiddenBlocks,
2568
- " output items are outside the live render window"
3101
+ " output items hidden \xB7 ",
3102
+ outputShortcut,
3103
+ " expand all"
2569
3104
  ] }) : null,
2570
3105
  completedWindow.turns.map((turn, index) => /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", children: [
2571
3106
  index > 0 ? /* @__PURE__ */ jsx5(TurnSeparator, { width: columns }) : null,
2572
- /* @__PURE__ */ jsx5(TurnView, { turn, interactive: false, workspaceName })
3107
+ /* @__PURE__ */ jsx5(TurnView, { turn, interactive: false, workspaceName, expandedOutput })
2573
3108
  ] }, turn.id)),
2574
3109
  activeWithoutTasks === void 0 ? null : /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", children: [
2575
3110
  completedWindow.turns.length > 0 ? /* @__PURE__ */ jsx5(TurnSeparator, { width: columns }) : null,
2576
- /* @__PURE__ */ jsx5(TurnView, { turn: activeWithoutTasks, interactive: activeSession, workspaceName })
3111
+ /* @__PURE__ */ jsx5(TurnView, { turn: activeWithoutTasks, interactive: activeSession, workspaceName, expandedOutput })
2577
3112
  ] })
2578
3113
  ] }),
2579
3114
  /* @__PURE__ */ jsx5(
@@ -2583,7 +3118,8 @@ function TerminalLayout({
2583
3118
  interactive: activeSession,
2584
3119
  maxHeight: taskPanelRows,
2585
3120
  columns,
2586
- ...taskScrollRef === void 0 ? {} : { scrollRef: taskScrollRef },
3121
+ ...mainTaskScrollRef === void 0 ? {} : { mainScrollRef: mainTaskScrollRef },
3122
+ ...subagentTaskScrollRef === void 0 ? {} : { subagentScrollRef: subagentTaskScrollRef },
2587
3123
  ...onTaskPanelHoverChange === void 0 ? {} : { onHoverChange: onTaskPanelHoverChange }
2588
3124
  }
2589
3125
  ),
@@ -2597,18 +3133,30 @@ function TerminalLayout({
2597
3133
  "\u2502 ",
2598
3134
  approval.reason ?? "This action needs permission."
2599
3135
  ] }),
2600
- isDestructiveTool(approval.tool) ? /* @__PURE__ */ jsxs4(Text, { bold: true, color: "magenta", children: [
2601
- "\u2514\u2500 Allow? ",
3136
+ approvalExpanded ? approvalDetailLines(approval).map((line, index) => /* @__PURE__ */ jsxs4(Text, { color: "magentaBright", wrap: "truncate-end", children: [
3137
+ "\u2502 ",
3138
+ line
3139
+ ] }, `${approval.id}:detail:${index}`)) : null,
3140
+ isDestructiveTool(approval.tool) || approval.allowAlways === false ? /* @__PURE__ */ jsxs4(Text, { bold: true, color: "magenta", children: [
3141
+ "\u2514\u2500 ",
3142
+ /* @__PURE__ */ jsx5(Text, { color: "cyan", children: "v" }),
3143
+ "=details / ",
2602
3144
  /* @__PURE__ */ jsx5(Text, { color: "green", children: "y" }),
2603
3145
  "=once / ",
3146
+ /* @__PURE__ */ jsx5(Text, { color: "cyan", children: "e" }),
3147
+ "=accept edits / ",
2604
3148
  /* @__PURE__ */ jsx5(Text, { color: "red", children: "n" }),
2605
3149
  "=deny"
2606
3150
  ] }) : /* @__PURE__ */ jsxs4(Text, { bold: true, color: "magenta", children: [
2607
- "\u2514\u2500 Allow? ",
3151
+ "\u2514\u2500 ",
3152
+ /* @__PURE__ */ jsx5(Text, { color: "cyan", children: "v" }),
3153
+ "=details / ",
2608
3154
  /* @__PURE__ */ jsx5(Text, { color: "green", children: "y" }),
2609
3155
  "=once / ",
2610
3156
  /* @__PURE__ */ jsx5(Text, { color: "yellow", children: "a" }),
2611
3157
  "=same-type / ",
3158
+ /* @__PURE__ */ jsx5(Text, { color: "cyan", children: "e" }),
3159
+ "=accept edits / ",
2612
3160
  /* @__PURE__ */ jsx5(Text, { color: "red", children: "n" }),
2613
3161
  "=deny"
2614
3162
  ] })
@@ -2617,10 +3165,12 @@ function TerminalLayout({
2617
3165
  memoryReviews.length === 0 ? null : /* @__PURE__ */ jsx5(MemoryReviewCards, { reviews: memoryReviews, autoDismissSeconds: memoryAutoDismissSeconds }),
2618
3166
  completion === void 0 ? null : /* @__PURE__ */ jsx5(SlashMenu, { completion }),
2619
3167
  mentionCompletion === void 0 ? null : /* @__PURE__ */ jsx5(MentionMenu, { completion: mentionCompletion, ...onMentionSelect === void 0 ? {} : { onSelect: onMentionSelect } }),
2620
- pendingPrompt === void 0 ? null : /* @__PURE__ */ jsxs4(Text, { color: "yellow", wrap: "truncate-end", children: [
2621
- "Pending \xB7 ",
2622
- pendingPrompt,
2623
- " \xB7 Esc edit"
3168
+ pendingPrompts.length === 0 ? null : /* @__PURE__ */ jsxs4(Text, { color: "yellow", wrap: "truncate-end", children: [
3169
+ "Pending (",
3170
+ pendingPrompts.length,
3171
+ ") \xB7 ",
3172
+ queuedPromptLabel(pendingPrompts.at(-1)),
3173
+ " \xB7 Esc edit latest"
2624
3174
  ] }),
2625
3175
  /* @__PURE__ */ jsx5(Text, { dimColor: true, children: "\u2500".repeat(dividerWidth) }),
2626
3176
  imageAttachments.map((image, index) => /* @__PURE__ */ jsxs4(Text, { color: "cyan", wrap: "truncate-end", children: [
@@ -2645,7 +3195,7 @@ function TerminalLayout({
2645
3195
  /* @__PURE__ */ jsx5(
2646
3196
  FooterStatus,
2647
3197
  {
2648
- hint: activeSession ? "Ctrl+C cancel \xB7 Enter queue \xB7 Esc edit pending \xB7 /steer sends now" : 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" : "Enter send \xB7 Ctrl/Cmd+V image \xB7 \u2191\u2193 history \xB7 Ctrl+C exit",
3198
+ hint: activeSession ? `Esc ${pendingPrompts.length > 0 ? "edit latest" : "stop"} \xB7 Ctrl+C cancel \xB7 Enter queue \xB7 Ctrl/Cmd+R history \xB7 ${outputShortcut} ${expandedOutput ? "collapse" : "expand"} output` : 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" : `Enter send \xB7 \u2191\u2193/Ctrl/Cmd+R history \xB7 ${outputShortcut} ${expandedOutput ? "collapse" : "expand"} output \xB7 Ctrl+C exit`,
2649
3199
  ...ideContext === void 0 ? {} : { ideContext }
2650
3200
  }
2651
3201
  )
@@ -2749,6 +3299,7 @@ function SlashMenu({ completion }) {
2749
3299
  return /* @__PURE__ */ jsxs4(Text, { ...presentation.rowStyle, wrap: "truncate-end", children: [
2750
3300
  presentation.marker,
2751
3301
  /* @__PURE__ */ jsx5(HighlightedName, { name: candidate.name, query: completion.query, matchStyle: presentation.matchStyle }),
3302
+ candidate.kind === "skill" || candidate.kind === "plugin" ? /* @__PURE__ */ jsx5(Text, { color: candidate.kind === "skill" ? "magentaBright" : "cyanBright", children: ` ${candidate.kind}` }) : null,
2752
3303
  candidate.description === void 0 ? null : /* @__PURE__ */ jsx5(Text, { dimColor: true, children: ` ${candidate.description}` })
2753
3304
  ] }, `${candidate.kind}:${candidate.name}`);
2754
3305
  }) });
@@ -2804,8 +3355,10 @@ function jumpScroll(scroll, delta) {
2804
3355
  scroll.scrollTo(Math.max(0, target));
2805
3356
  }
2806
3357
  }
2807
- function selectWheelScrollTarget(transcript, tasks, taskPanelHovered) {
2808
- return taskPanelHovered && tasks !== null ? tasks : transcript;
3358
+ function selectWheelScrollTarget(transcript, mainTasks, subagentTasks, hoveredTrack) {
3359
+ if (hoveredTrack === "main" && mainTasks !== null) return mainTasks;
3360
+ if (hoveredTrack === "subagent" && subagentTasks !== null) return subagentTasks;
3361
+ return transcript;
2809
3362
  }
2810
3363
  function taskPanelViewportRows(rows, reservedBottomRows, hasTasks) {
2811
3364
  if (!hasTasks) return 0;
@@ -2829,12 +3382,13 @@ function TurnSeparator({ width }) {
2829
3382
  function TurnView({
2830
3383
  turn,
2831
3384
  interactive,
2832
- workspaceName
3385
+ workspaceName,
3386
+ expandedOutput
2833
3387
  }) {
2834
3388
  if (turn.kind === "compaction") {
2835
3389
  return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", marginBottom: 1, borderStyle: "round", borderColor: "ansi:yellowBright", paddingX: 1, children: [
2836
3390
  /* @__PURE__ */ jsx5(Text, { color: "ansi:yellowBright", bold: true, children: turn.prompt }),
2837
- turn.blocks.map((block, index) => block.kind === "status" ? /* @__PURE__ */ jsx5(StatusBlockView, { block, interactive, workspaceName }, block.id) : /* @__PURE__ */ jsx5(Box_default, { children: /* @__PURE__ */ jsx5(AssistantText, { text: block.text }) }, `${turn.id}-text-${index}`))
3391
+ turn.blocks.map((block, index) => block.kind === "status" ? /* @__PURE__ */ jsx5(StatusBlockView, { block, interactive, workspaceName, expandedOutput }, block.id) : /* @__PURE__ */ jsx5(Box_default, { children: /* @__PURE__ */ jsx5(AssistantText, { text: block.text }) }, `${turn.id}-text-${index}`))
2838
3392
  ] });
2839
3393
  }
2840
3394
  if (turn.source?.kind === "pal") {
@@ -2851,7 +3405,7 @@ function TurnView({
2851
3405
  ] }),
2852
3406
  /* @__PURE__ */ jsx5(Text, { color: "ansi:whiteBright", children: turn.prompt })
2853
3407
  ] }),
2854
- /* @__PURE__ */ jsx5(Box_default, { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: turn.blocks.map((block, index) => block.kind === "status" ? /* @__PURE__ */ jsx5(StatusBlockView, { block, interactive, workspaceName }, block.id) : /* @__PURE__ */ jsx5(Box_default, { marginBottom: 1, children: /* @__PURE__ */ jsx5(AssistantText, { text: block.text }) }, `${turn.id}-text-${index}`)) })
3408
+ /* @__PURE__ */ jsx5(Box_default, { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: turn.blocks.map((block, index) => block.kind === "status" ? /* @__PURE__ */ jsx5(StatusBlockView, { block, interactive, workspaceName, expandedOutput }, block.id) : /* @__PURE__ */ jsx5(Box_default, { marginBottom: 1, children: /* @__PURE__ */ jsx5(AssistantText, { text: block.text }) }, `${turn.id}-text-${index}`)) })
2855
3409
  ] });
2856
3410
  }
2857
3411
  return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", marginBottom: 1, children: [
@@ -2862,7 +3416,7 @@ function TurnView({
2862
3416
  turn.prompt
2863
3417
  ] })
2864
3418
  ] }),
2865
- /* @__PURE__ */ jsx5(Box_default, { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: turn.blocks.map((block, index) => block.kind === "status" ? /* @__PURE__ */ jsx5(StatusBlockView, { block, interactive, workspaceName }, block.id) : /* @__PURE__ */ jsx5(Box_default, { marginBottom: 1, children: /* @__PURE__ */ jsx5(AssistantText, { text: block.text }) }, `${turn.id}-text-${index}`)) })
3419
+ /* @__PURE__ */ jsx5(Box_default, { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: turn.blocks.map((block, index) => block.kind === "status" ? /* @__PURE__ */ jsx5(StatusBlockView, { block, interactive, workspaceName, expandedOutput }, block.id) : /* @__PURE__ */ jsx5(Box_default, { marginBottom: 1, children: /* @__PURE__ */ jsx5(AssistantText, { text: block.text }) }, `${turn.id}-text-${index}`)) })
2866
3420
  ] });
2867
3421
  }
2868
3422
  var DIFF_CONTENT = fileDiffLineStyle("context").contentColor;
@@ -3041,11 +3595,12 @@ function PromptLine({
3041
3595
  function StatusBlockView({
3042
3596
  block,
3043
3597
  interactive,
3044
- workspaceName
3598
+ workspaceName,
3599
+ expandedOutput
3045
3600
  }) {
3046
3601
  const visibleBlock = cliToolTitle(block);
3047
3602
  const outcome = cliToolOutcome(block);
3048
- const primary = visibleBlock.activity === "model" || visibleBlock.task !== void 0 ? /* @__PURE__ */ jsx5(TaskStatusLine, { block: visibleBlock, interactive }) : visibleBlock.state === "completed" && visibleBlock.presentation !== void 0 ? /* @__PURE__ */ jsx5(ToolPresentationView, { presentation: visibleBlock.presentation, workspaceName }) : /* @__PURE__ */ jsx5(StatusLine, { block: visibleBlock, interactive });
3603
+ const primary = visibleBlock.activity === "model" || visibleBlock.task !== void 0 ? /* @__PURE__ */ jsx5(TaskStatusLine, { block: visibleBlock, interactive }) : visibleBlock.state === "completed" && visibleBlock.presentation !== void 0 ? /* @__PURE__ */ jsx5(ToolPresentationView, { presentation: visibleBlock.presentation, workspaceName, expandedOutput }) : /* @__PURE__ */ jsx5(StatusLine, { block: visibleBlock, interactive });
3049
3604
  return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", children: [
3050
3605
  primary,
3051
3606
  outcome === void 0 ? null : /* @__PURE__ */ jsx5(Box_default, { paddingLeft: 2, children: /* @__PURE__ */ jsxs4(Text, { dimColor: true, children: [
@@ -3057,13 +3612,14 @@ function StatusBlockView({
3057
3612
  }
3058
3613
  function ToolPresentationView({
3059
3614
  presentation,
3060
- workspaceName
3615
+ workspaceName,
3616
+ expandedOutput
3061
3617
  }) {
3062
- if (presentation.kind === "changeset") return /* @__PURE__ */ jsx5(ChangeSetPresentationView, { presentation, workspaceName });
3618
+ if (presentation.kind === "changeset") return /* @__PURE__ */ jsx5(ChangeSetPresentationView, { presentation, workspaceName, expanded: expandedOutput });
3063
3619
  if (presentation.kind === "file-change") return /* @__PURE__ */ jsx5(FileDiffView, { presentation });
3064
- if (presentation.kind === "terminal") return /* @__PURE__ */ jsx5(CommandPresentationView, { presentation });
3065
- if (presentation.kind === "web") return /* @__PURE__ */ jsx5(WebPresentationView, { presentation });
3066
- if (presentation.kind === "job") return /* @__PURE__ */ jsx5(JobPresentationView, { presentation });
3620
+ if (presentation.kind === "terminal") return /* @__PURE__ */ jsx5(CommandPresentationView, { presentation, expanded: expandedOutput });
3621
+ if (presentation.kind === "web") return /* @__PURE__ */ jsx5(WebPresentationView, { presentation, expanded: expandedOutput });
3622
+ if (presentation.kind === "job") return /* @__PURE__ */ jsx5(JobPresentationView, { presentation, expanded: expandedOutput });
3067
3623
  return /* @__PURE__ */ jsx5(Box_default, { paddingLeft: 2, children: /* @__PURE__ */ jsxs4(Text, { children: [
3068
3624
  presentation.title,
3069
3625
  presentation.summary ? ` \xB7 ${presentation.summary}` : ""
@@ -3073,9 +3629,10 @@ var CLI_CHANGESET_FILE_LIMIT = 8;
3073
3629
  var CHANGESET_TONE = "#b99bf8";
3074
3630
  function ChangeSetPresentationView({
3075
3631
  presentation,
3076
- workspaceName
3632
+ workspaceName,
3633
+ expanded
3077
3634
  }) {
3078
- const visibleFiles = presentation.files.slice(0, CLI_CHANGESET_FILE_LIMIT);
3635
+ const visibleFiles = expanded ? presentation.files : presentation.files.slice(0, CLI_CHANGESET_FILE_LIMIT);
3079
3636
  const added = presentation.files.reduce((total, file) => total + file.added, 0);
3080
3637
  const removed = presentation.files.reduce((total, file) => total + file.removed, 0);
3081
3638
  const fileLabel = presentation.files.length === 1 ? "FILE" : "FILES";
@@ -3142,18 +3699,22 @@ function workspaceRelativePath(value, workspaceName) {
3142
3699
  if (normalized.toLocaleLowerCase().startsWith(rootPrefix.toLocaleLowerCase())) return normalized.slice(rootPrefix.length);
3143
3700
  return normalized;
3144
3701
  }
3145
- var CLI_COMMAND_OUTPUT_LINE_LIMIT = 16;
3702
+ var CLI_COMMAND_OUTPUT_LINE_LIMIT = 8;
3146
3703
  function CommandPresentationView({
3147
- presentation
3704
+ presentation,
3705
+ expanded
3148
3706
  }) {
3149
3707
  const state = presentation.state ?? commandState(presentation.exitCode);
3150
3708
  const tone = jobStateColor(state);
3151
3709
  const label = presentation.variant === "terminal" ? "TERMINAL" : "COMMAND";
3152
3710
  const hasStdout = jobOutputLines(presentation.stdout ?? "").length > 0;
3153
3711
  const hasStderr = jobOutputLines(presentation.stderr ?? "").length > 0;
3154
- const perStreamLimit = hasStdout && hasStderr ? Math.floor(CLI_COMMAND_OUTPUT_LINE_LIMIT / 2) : CLI_COMMAND_OUTPUT_LINE_LIMIT;
3712
+ const collapsedPerStreamLimit = hasStdout && hasStderr ? Math.floor(CLI_COMMAND_OUTPUT_LINE_LIMIT / 2) : CLI_COMMAND_OUTPUT_LINE_LIMIT;
3713
+ const perStreamLimit = expanded ? Number.POSITIVE_INFINITY : collapsedPerStreamLimit;
3155
3714
  const stdout = boundedCommandLines(presentation.stdout ?? "", perStreamLimit);
3156
3715
  const stderr = boundedCommandLines(presentation.stderr ?? "", perStreamLimit);
3716
+ const collapsible = stdout.total > collapsedPerStreamLimit || stderr.total > collapsedPerStreamLimit;
3717
+ const shortcut = outputToggleShortcut();
3157
3718
  return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [
3158
3719
  /* @__PURE__ */ jsxs4(Box_default, { children: [
3159
3720
  /* @__PURE__ */ jsx5(Text, { color: tone, children: "\u250C\u2500 " }),
@@ -3175,7 +3736,15 @@ function CommandPresentationView({
3175
3736
  ] }),
3176
3737
  /* @__PURE__ */ jsxs4(Box_default, { children: [
3177
3738
  /* @__PURE__ */ jsx5(Text, { color: tone, children: "\u2514\u2500 " }),
3178
- /* @__PURE__ */ jsx5(Text, { dimColor: true, children: commandFooter(presentation, state) })
3739
+ /* @__PURE__ */ jsx5(Text, { dimColor: true, children: commandFooter(presentation, state) }),
3740
+ collapsible ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
3741
+ /* @__PURE__ */ jsx5(Text, { dimColor: true, children: " \xB7 " }),
3742
+ /* @__PURE__ */ jsx5(Text, { color: "ansi:cyanBright", children: shortcut }),
3743
+ /* @__PURE__ */ jsxs4(Text, { dimColor: true, children: [
3744
+ " ",
3745
+ expanded ? "collapse" : "expand"
3746
+ ] })
3747
+ ] }) : null
3179
3748
  ] })
3180
3749
  ] });
3181
3750
  }
@@ -3189,7 +3758,8 @@ function CommandOutputSection({
3189
3758
  /* @__PURE__ */ jsxs4(Box_default, { children: [
3190
3759
  /* @__PURE__ */ jsx5(Text, { color: tone, children: "\u251C\u2500 " }),
3191
3760
  /* @__PURE__ */ jsx5(Text, { color: error ? "#e06c50" : tone, bold: true, children: label }),
3192
- /* @__PURE__ */ jsx5(Text, { dimColor: true, children: ` \xB7 ${value.total} ${value.total === 1 ? "LINE" : "LINES"}` })
3761
+ /* @__PURE__ */ jsx5(Text, { dimColor: true, children: ` \xB7 ${value.total} ${value.total === 1 ? "LINE" : "LINES"}` }),
3762
+ value.hidden === 0 ? null : /* @__PURE__ */ jsx5(Text, { dimColor: true, children: ` \xB7 ${value.head.length + value.tail.length} SHOWN` })
3193
3763
  ] }),
3194
3764
  value.head.map((line, index) => /* @__PURE__ */ jsx5(CommandOutputLine, { line, tone, error }, `head:${index}`)),
3195
3765
  value.hidden === 0 ? null : /* @__PURE__ */ jsxs4(Box_default, { children: [
@@ -3214,8 +3784,8 @@ function CommandOutputLine({ line, tone, error }) {
3214
3784
  function boundedCommandLines(output, limit) {
3215
3785
  const lines = jobOutputLines(output);
3216
3786
  if (lines.length <= limit) return { total: lines.length, head: lines, tail: [], hidden: 0 };
3217
- const headCount = Math.ceil(limit / 2);
3218
- const tailCount = Math.floor(limit / 2);
3787
+ const headCount = Math.ceil(limit * 0.625);
3788
+ const tailCount = Math.max(0, limit - headCount);
3219
3789
  return {
3220
3790
  total: lines.length,
3221
3791
  head: lines.slice(0, headCount),
@@ -3234,7 +3804,8 @@ function commandFooter(presentation, state) {
3234
3804
  }
3235
3805
  var CLI_WEB_RESULT_LIMIT = 5;
3236
3806
  function WebPresentationView({
3237
- presentation
3807
+ presentation,
3808
+ expanded
3238
3809
  }) {
3239
3810
  const items = presentation.items ?? [];
3240
3811
  if (presentation.items === void 0) return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [
@@ -3252,7 +3823,7 @@ function WebPresentationView({
3252
3823
  /* @__PURE__ */ jsx5(Text, { dimColor: true, children: "Page content added to context" })
3253
3824
  ] })
3254
3825
  ] });
3255
- const visibleItems = items.slice(0, CLI_WEB_RESULT_LIMIT);
3826
+ const visibleItems = expanded ? items : items.slice(0, CLI_WEB_RESULT_LIMIT);
3256
3827
  const query = presentation.title.replace(/^Search:\s*/iu, "").trim();
3257
3828
  return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [
3258
3829
  /* @__PURE__ */ jsxs4(Box_default, { children: [
@@ -3298,13 +3869,14 @@ function compactWebSource(value) {
3298
3869
  var CLI_JOB_LOG_LINE_LIMIT = 12;
3299
3870
  var CLI_JOB_LIST_LIMIT = 8;
3300
3871
  function JobPresentationView({
3301
- presentation
3872
+ presentation,
3873
+ expanded
3302
3874
  }) {
3303
- if (presentation.action === "list") return /* @__PURE__ */ jsx5(JobListPresentationView, { presentation });
3875
+ if (presentation.action === "list") return /* @__PURE__ */ jsx5(JobListPresentationView, { presentation, expanded });
3304
3876
  const state = presentation.state ?? "running";
3305
3877
  const tone = jobStateColor(state);
3306
3878
  const lines = jobOutputLines(presentation.output ?? "");
3307
- const visibleLines = lines.slice(-CLI_JOB_LOG_LINE_LIMIT);
3879
+ const visibleLines = expanded ? lines : lines.slice(-CLI_JOB_LOG_LINE_LIMIT);
3308
3880
  const hiddenLines = lines.length - visibleLines.length;
3309
3881
  const showLog = presentation.action === "read" || presentation.action === "kill" || lines.length > 0;
3310
3882
  return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [
@@ -3353,10 +3925,11 @@ function JobPresentationView({
3353
3925
  ] });
3354
3926
  }
3355
3927
  function JobListPresentationView({
3356
- presentation
3928
+ presentation,
3929
+ expanded
3357
3930
  }) {
3358
3931
  const jobs = presentation.jobs ?? [];
3359
- const visibleJobs = jobs.slice(0, CLI_JOB_LIST_LIMIT);
3932
+ const visibleJobs = expanded ? jobs : jobs.slice(0, CLI_JOB_LIST_LIMIT);
3360
3933
  return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [
3361
3934
  /* @__PURE__ */ jsxs4(Box_default, { children: [
3362
3935
  /* @__PURE__ */ jsx5(Text, { color: "#d7a657", children: "\u250C\u2500 " }),
@@ -3624,6 +4197,22 @@ function navigateHistory(state, direction) {
3624
4197
  const input = state.history[cursor] ?? "";
3625
4198
  return { cursor, input, promptCursor: [...input].length };
3626
4199
  }
4200
+ function navigatePromptHistory(state, direction) {
4201
+ const stashed = direction === "up" && state.cursor === state.history.length ? clonePromptDraft(state.current) : state.stashed;
4202
+ const next = navigateHistory(state, direction);
4203
+ const draft = next.cursor === state.history.length && stashed !== void 0 ? clonePromptDraft(stashed) : { text: next.input, cursor: next.promptCursor, pastedBlocks: [], imageAttachments: [] };
4204
+ return { cursor: next.cursor, draft, ...stashed === void 0 ? {} : { stashed } };
4205
+ }
4206
+ function reverseSearchHistory(history, query, before = history.length) {
4207
+ const needle = query.toLocaleLowerCase();
4208
+ for (let cursor = Math.min(history.length, before) - 1; cursor >= 0; cursor -= 1) {
4209
+ const input = history[cursor];
4210
+ if (needle.length === 0 || input.toLocaleLowerCase().includes(needle)) {
4211
+ return { cursor, input, promptCursor: [...input].length };
4212
+ }
4213
+ }
4214
+ return void 0;
4215
+ }
3627
4216
  async function submitSafely(session, prompt, report) {
3628
4217
  try {
3629
4218
  await session.submit(prompt);
@@ -3682,12 +4271,15 @@ export {
3682
4271
  FooterStatus,
3683
4272
  HISTORY_CAP,
3684
4273
  MentionMenu,
4274
+ PendingPromptQueue,
4275
+ PromptEditHistory,
3685
4276
  PromptLine,
3686
4277
  SHUTDOWN_TIMEOUT_MS,
3687
- SinglePendingPrompt,
3688
4278
  TerminalLayout,
3689
4279
  appRuntimeOptions,
4280
+ approvalDetailLines,
3690
4281
  boundedCliTurn,
4282
+ canShowSlashCompletion,
3691
4283
  classifyTerminalInput,
3692
4284
  cliTranscriptWindow,
3693
4285
  closeAndDisposeRuntime,
@@ -3695,14 +4287,23 @@ export {
3695
4287
  editPrompt,
3696
4288
  editPromptWithPastedBlocks,
3697
4289
  ideFooterPresentation,
4290
+ isCopyShortcut,
4291
+ isPlatformShortcut,
3698
4292
  navigateHistory,
4293
+ navigatePromptHistory,
4294
+ outputToggleShortcut,
3699
4295
  pastedDraftPresentation,
3700
4296
  prepareCliSubmission,
3701
4297
  promptCursorFromClick,
3702
4298
  promptDelivery,
4299
+ promptHistoryAction,
4300
+ queuedPromptLabel,
3703
4301
  removeLastCliImageOnBackspace,
3704
4302
  resolvePromptDelivery,
4303
+ restoreQueuedPrompt,
4304
+ reverseSearchHistory,
3705
4305
  runTerminalSubmissionChain,
4306
+ runningEscapeAction,
3706
4307
  selectWheelScrollTarget,
3707
4308
  shutdownRuntime,
3708
4309
  slashKeyAction,