@toddzheng024/dscode-bundle 0.4.0 → 0.6.0

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 (93) hide show
  1. package/THIRD_PARTY_NOTICES.md +15 -0
  2. package/cordis.patch.yml +3 -3
  3. package/package.json +7 -1
  4. package/plugins/clipboard-image/clipboard-image.swift +56 -0
  5. package/plugins/clipboard-image/index.mjs +70 -0
  6. package/plugins/code-review/git.mjs +95 -0
  7. package/plugins/code-review/index.mjs +88 -0
  8. package/plugins/dscode/index.mjs +57 -5
  9. package/plugins/email-tools/index.mjs +2 -2
  10. package/plugins/exec/index.mjs +107 -0
  11. package/plugins/session-metrics/index.mjs +6 -2
  12. package/plugins/session-metrics/rate.mjs +62 -0
  13. package/plugins/session-metrics/view.mjs +15 -9
  14. package/plugins/tui-tools/doctor-cli.mjs +22 -0
  15. package/plugins/tui-tools/doctor.mjs +141 -0
  16. package/plugins/tui-tools/index.mjs +14 -6
  17. package/plugins/ultra/policy.mjs +13 -2
  18. package/plugins/worktree-subagent/worktree.mjs +50 -0
  19. package/presets/dscode/agent.cordis.yml +14 -3
  20. package/vendor/deepseek/index.js +12 -2
  21. package/vendor/subagent/index.js +39 -12
  22. package/vendor/subagent-core/LICENSE +21 -0
  23. package/vendor/subagent-core/index.js +3232 -0
  24. package/vendor/subagent-core/invariant.js +76 -0
  25. package/vendor/subagent-core/typert.host.d.ts +3 -0
  26. package/vendor/subagent-core/typert.host.js +964 -0
  27. package/vendor/subagent-core/typert.remote-client.d.ts +27 -0
  28. package/vendor/subagent-core/typert.remote-client.js +160 -0
  29. package/vendor/subagent-core/types/assistant-output.d.ts +48 -0
  30. package/vendor/subagent-core/types/assistant-output.js +73 -0
  31. package/vendor/subagent-core/types/catalog.d.ts +77 -0
  32. package/vendor/subagent-core/types/catalog.js +103 -0
  33. package/vendor/subagent-core/types/child-agent.d.ts +150 -0
  34. package/vendor/subagent-core/types/child-agent.js +207 -0
  35. package/vendor/subagent-core/types/client.d.ts +8 -0
  36. package/vendor/subagent-core/types/client.js +7 -0
  37. package/vendor/subagent-core/types/continuation-activation.d.ts +251 -0
  38. package/vendor/subagent-core/types/continuation-activation.js +663 -0
  39. package/vendor/subagent-core/types/continuation-messages.d.ts +62 -0
  40. package/vendor/subagent-core/types/continuation-messages.js +102 -0
  41. package/vendor/subagent-core/types/continuation.d.ts +124 -0
  42. package/vendor/subagent-core/types/continuation.js +455 -0
  43. package/vendor/subagent-core/types/control-types.d.ts +146 -0
  44. package/vendor/subagent-core/types/control-types.js +9 -0
  45. package/vendor/subagent-core/types/control.d.ts +71 -0
  46. package/vendor/subagent-core/types/control.js +116 -0
  47. package/vendor/subagent-core/types/depth.d.ts +31 -0
  48. package/vendor/subagent-core/types/depth.js +39 -0
  49. package/vendor/subagent-core/types/descriptor.d.ts +144 -0
  50. package/vendor/subagent-core/types/descriptor.js +193 -0
  51. package/vendor/subagent-core/types/error.d.ts +11 -0
  52. package/vendor/subagent-core/types/error.js +14 -0
  53. package/vendor/subagent-core/types/inbox.d.ts +43 -0
  54. package/vendor/subagent-core/types/inbox.js +61 -0
  55. package/vendor/subagent-core/types/index.d.ts +316 -0
  56. package/vendor/subagent-core/types/index.js +511 -0
  57. package/vendor/subagent-core/types/internal.d.ts +58 -0
  58. package/vendor/subagent-core/types/internal.js +58 -0
  59. package/vendor/subagent-core/types/invariant.d.ts +13 -0
  60. package/vendor/subagent-core/types/invariant.js +91 -0
  61. package/vendor/subagent-core/types/lifecycle.d.ts +113 -0
  62. package/vendor/subagent-core/types/lifecycle.js +178 -0
  63. package/vendor/subagent-core/types/list-children.d.ts +65 -0
  64. package/vendor/subagent-core/types/list-children.js +381 -0
  65. package/vendor/subagent-core/types/out-of-process.d.ts +118 -0
  66. package/vendor/subagent-core/types/out-of-process.js +219 -0
  67. package/vendor/subagent-core/types/projection-types.d.ts +74 -0
  68. package/vendor/subagent-core/types/projection-types.js +7 -0
  69. package/vendor/subagent-core/types/projection.d.ts +95 -0
  70. package/vendor/subagent-core/types/projection.js +151 -0
  71. package/vendor/subagent-core/types/run-settlement.d.ts +17 -0
  72. package/vendor/subagent-core/types/run-settlement.js +70 -0
  73. package/vendor/subagent-core/types/types.d.ts +377 -0
  74. package/vendor/subagent-core/types/types.js +19 -0
  75. package/vendor/subagent-driver/LICENSE +21 -0
  76. package/vendor/subagent-driver/index.js +255 -0
  77. package/vendor/subagent-driver/types/index.d.ts +33 -0
  78. package/vendor/subagent-driver/types/structured.d.ts +42 -0
  79. package/vendor/subagent-fork/LICENSE +21 -0
  80. package/vendor/subagent-fork/index.js +61 -0
  81. package/vendor/subagent-fork/types/index.d.ts +20 -0
  82. package/vendor/subagent-spawn/LICENSE +21 -0
  83. package/vendor/subagent-spawn/index.js +45 -0
  84. package/vendor/subagent-spawn/types/index.d.ts +19 -0
  85. package/vendor/terminal/LICENSE +21 -0
  86. package/vendor/terminal/index.js +1013 -0
  87. package/vendor/terminal/types/config.d.ts +71 -0
  88. package/vendor/terminal/types/index.d.ts +36 -0
  89. package/vendor/terminal/types/sanitize.d.ts +47 -0
  90. package/vendor/terminal/types/session.d.ts +87 -0
  91. package/vendor/tui/dscode-clipboard-image/clipboard-image.swift +56 -0
  92. package/vendor/tui/dscode-clipboard-image/index.mjs +70 -0
  93. package/vendor/tui/index.mjs +341 -93
@@ -1,3 +1,149 @@
1
+ // dscode-welcome-v2
2
+ const WELCOME_ART = ["...........1.1..........","............1...........","............1...........",".........1..1..1........",".......1..22222..1......","...1...1....2....1...1..","..11...2....2....2...11.","....11.2...222...2.11...","......22.22.3.22.22.....",".....22.22.333.22.22....","....1...2.33332.2...1...","........2.33322.2.......","....1...2.33222.2...1...",".....22.22.222.22.22....","......22.22.3.22.22.....","....11.2...222...2.11...","..11...2....2....2...11.","...1...1....2....1...1..",".......1..22222..1......",".........1..1..1........","............1...........","............1...........","...........1.1..........","........................"];
3
+ const WELCOME_ART_SMALL = ["..........1.1.........","...........1..........","........1..1..1.......",".........1.2.1........","...1..1...222...1..1..","...1..1....2....1..1..","..1.1.2...222...2.1.1.",".....22.22.3.22.22....","....12.22.333.22.21...","...1...2.33332.2...1..",".......2.33322.2......","...1...2.33222.2...1..","....12.22.222.22.21...",".....22.22.3.22.22....","..1.1.2...222...2.1.1.","...1..1....2....1..1..","...1..1...222...1..1..",".........1.2.1........","........1..1..1.......","...........1..........","..........1.1.........","......................"];
4
+ function welcomeArtRows(grid, tones) {
5
+ const rows = [];
6
+ for (let y = 0; y < grid.length; y += 2) {
7
+ const top = grid[y] || '';
8
+ const bottom = grid[y + 1] || '';
9
+ const segments = [];
10
+ for (let x = 0; x < Math.max(top.length, bottom.length); x++) {
11
+ const upper = tones[top[x]] || '';
12
+ const lower = tones[bottom[x]] || '';
13
+ const glyph = upper && lower ? upper === lower ? '\u2588' : '\u2580' : upper ? '\u2580' : lower ? '\u2584' : ' ';
14
+ const color = upper || lower;
15
+ const background = upper && lower && upper !== lower ? lower : '';
16
+ const last = segments[segments.length - 1];
17
+ if (last && last.glyph === glyph && last.color === color && last.background === background) last.text += glyph;
18
+ else segments.push({ glyph, text: glyph, color, background });
19
+ }
20
+ rows.push(segments);
21
+ }
22
+ return rows;
23
+ }
24
+ import { readClipboardImage as dscodeReadClipboardImage } from "./dscode-clipboard-image/index.mjs";
25
+ // dscode-clipboard-image-v1
26
+ // dscode-large-paste-v1
27
+ function collapseLargePaste(text, draft, pastes) {
28
+ const safe = text.replaceAll('\r\n', '\n').replaceAll('\r', '\n').replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/gu, '').replaceAll('\t', ' ');
29
+ const count = [...safe].length;
30
+ if (count <= LARGE_PASTE_CHARS) return safe;
31
+ // Keep an executable-looking first line visible to the command router.
32
+ if (draft.trim() === '' && /^\s*[!/]/u.test(safe)) return safe;
33
+ const base = `[Pasted Content ${count} chars]`;
34
+ let marker = base;
35
+ for (let suffix = 2; draft.includes(marker) || pastes.has(marker); suffix++) marker = `${base} #${suffix}`;
36
+ pastes.set(marker, safe);
37
+ return marker;
38
+ }
39
+ function expandLargePastes(value, pastes) {
40
+ const markers = [...pastes.keys()].sort((left, right) => right.length - left.length);
41
+ let expanded = '';
42
+ let offset = 0;
43
+ while (offset < value.length) {
44
+ let found = -1;
45
+ let match;
46
+ for (const marker of markers) {
47
+ const at = value.indexOf(marker, offset);
48
+ if (at >= 0 && (found < 0 || at < found)) {
49
+ found = at;
50
+ match = marker;
51
+ }
52
+ }
53
+ if (match === undefined) break;
54
+ expanded += value.slice(offset, found) + pastes.get(match);
55
+ offset = found + match.length;
56
+ }
57
+ return expanded + value.slice(offset);
58
+ }
59
+ function pasteAtomicEdit(before, edit, pastes) {
60
+ let prefix = 0;
61
+ while (prefix < before.length && prefix < edit.value.length && before[prefix] === edit.value[prefix]) prefix++;
62
+ let suffix = 0;
63
+ while (suffix < before.length - prefix && suffix < edit.value.length - prefix && before[before.length - suffix - 1] === edit.value[edit.value.length - suffix - 1]) suffix++;
64
+ let from = prefix, to = before.length - suffix;
65
+ const inserted = edit.value.slice(prefix, edit.value.length - suffix);
66
+ let touched = false;
67
+ const originalPastes = new Map(pastes);
68
+ for (const marker of pastes.keys()) {
69
+ const start = before.indexOf(marker);
70
+ if (start < 0) continue;
71
+ const end = start + marker.length;
72
+ if (from < end && to > start || from === to && from > start && from < end) {
73
+ from = Math.min(from, start);
74
+ to = Math.max(to, end);
75
+ pastes.delete(marker);
76
+ touched = true;
77
+ }
78
+ }
79
+ if (touched) edit = { ...edit, value: before.slice(0, from) + inserted + before.slice(to), cursor: from + inserted.length,
80
+ killed: edit.killed === undefined ? undefined : expandLargePastes(before.slice(from, to), originalPastes) };
81
+ for (const marker of pastes.keys()) if (!edit.value.includes(marker) && before.includes(marker)) pastes.delete(marker);
82
+ return edit;
83
+ }
84
+ function pasteCursorEdge(value, previous, next, pastes) {
85
+ for (const marker of pastes.keys()) {
86
+ const start = value.indexOf(marker);
87
+ if (start < 0) continue;
88
+ const end = start + marker.length;
89
+ if (next > start && next < end) return next < previous ? start : end;
90
+ }
91
+ return next;
92
+ }
93
+ const LARGE_PASTE_CHARS = 200;
94
+ // dscode-user-background-v1
95
+ function userBackgroundRows(rows, columns, measure) {
96
+ const width = Math.max(1, Math.floor(columns));
97
+ return rows.map(row => {
98
+ const used = row.segments.reduce((sum, segment) => sum + measure(segment.text), 0);
99
+ return {
100
+ ...row,
101
+ background: 'user',
102
+ segments: [...row.segments, { text: ' '.repeat(Math.max(0, width - used)), style: 'plain' }],
103
+ };
104
+ });
105
+ }
106
+ // dscode-turn-divider-v1
107
+ function turnDividedLines(entry, columns, rendered, remaining) {
108
+ if (entry?.turnEnded !== true || (rendered.length > 0 && rendered.length >= remaining)) return rendered;
109
+ // Full terminal width: the row renders with truncate-end, so it can never wrap.
110
+ return [...rendered, { segments: [{ text: '─'.repeat(Math.max(1, columns)), style: 'dim' }] }];
111
+ }
112
+ // dscode-scroll-v1
113
+ function transcriptWindow(lines, rows, offset) {
114
+ if (rows <= 0) return [];
115
+ const end = Math.max(0, lines.length - Math.max(0, offset));
116
+ return lines.slice(Math.max(0, end - rows), end);
117
+ }
118
+ function mouseWheelDirection(unit) {
119
+ const match = /^\x1b\[<(\d+);\d+;\d+([Mm])$/.exec(unit);
120
+ if (!match) return null;
121
+ if (match[2] !== 'M') return 0;
122
+ const button = Number(match[1]) & ~(4 | 8 | 16);
123
+ return button === 64 ? 1 : button === 65 ? -1 : 0;
124
+ }
125
+ const DSCODE_MOUSE_ENABLE = "\x1b[?1000h\x1b[?1006h";
126
+ const DSCODE_MOUSE_DISABLE = "\x1b[?1006l\x1b[?1000l";
127
+ const dscodeWheelListeners = new Set();
128
+ function dscodeHandleMouseUnit(unit) {
129
+ const direction = mouseWheelDirection(unit);
130
+ if (direction === null) return false;
131
+ if (direction !== 0) for (const listener of dscodeWheelListeners) listener(direction);
132
+ return true;
133
+ }
134
+ // dscode-interrupt-v1
135
+ function ctrlCAction(armed, { interrupt, busy, preparingImages, active, hasDraft }) {
136
+ if (armed) return 'quit';
137
+ if (interrupt() || busy) return 'interrupt';
138
+ if (preparingImages) return 'cancel-prepare';
139
+ if (!active) return 'wait';
140
+ return hasDraft ? 'clear-draft' : 'quit';
141
+ }
142
+ // dscode-welcome-scroll-v1
143
+ function welcomeVisibleRows(capacity, maximum, demand, canScroll = true) {
144
+ if (!canScroll || capacity < maximum) return maximum;
145
+ return Math.max(0, Math.min(maximum, capacity - demand));
146
+ }
1
147
  // dscode-email-v4
2
148
  import { createImapConnector as dscodeCreateImapConnector } from "./dscode-email/imap.mjs";
3
149
  import { createGmailConnector as dscodeCreateGmailConnector } from "./dscode-email/gmail.mjs";
@@ -148,7 +294,7 @@ function visibleSettledLines(entries, settled, budget, columns, showReasoning, r
148
294
  const chunks = [];
149
295
  let remaining = budget;
150
296
  for (let index = settled - 1; index >= 0 && remaining > 0; index--) {
151
- const lines = renderEntry(entries[index], Math.max(10, columns - 2), showReasoning);
297
+ const lines = turnDividedLines(entries[index], columns, renderEntry(entries[index], Math.max(10, columns - 2), showReasoning), remaining);
152
298
  const kept = lines.slice(-remaining);
153
299
  if (kept.length) {
154
300
  chunks.unshift(kept);
@@ -159,6 +305,40 @@ function visibleSettledLines(entries, settled, budget, columns, showReasoning, r
159
305
  }
160
306
  // dscode-style-v1
161
307
 
308
+
309
+ // dscode-tps-colors-v1
310
+ function dscodeTpsTone(rate) {
311
+ if (!Number.isFinite(rate) || rate < 0) return null;
312
+ if (rate < 75) return "yellow";
313
+ if (rate < 150) return "green";
314
+ if (rate <= 250) return "blue";
315
+ return "purple";
316
+ }
317
+ function dscodeTpsInkColor(tone) {
318
+ const palette = getPalette();
319
+ if (tone === "yellow") return inkColor(palette.warn);
320
+ if (tone === "green") return inkColor(palette.success);
321
+ if (tone === "blue") return inkColor(palette.brandBright);
322
+ if (tone === "purple") return inkColor(getTheme() === "light" ? [126, 34, 206] : [192, 132, 252]);
323
+ return void 0;
324
+ }
325
+ function dscodeTelemetryParts(value) {
326
+ const result = [];
327
+ for (const [index, part] of value.split(" | ").entries()) {
328
+ if (index > 0) result.push({ text: " | ", tone: null });
329
+ const prefix = part.startsWith("current: ") ? "current: " : part.startsWith("average: ") ? "average: " : "";
330
+ if (!prefix || !part.endsWith(" tps")) { result.push({ text: part, tone: null }); continue; }
331
+ const display = part.slice(prefix.length, -4);
332
+ const rate = Number(display.startsWith("~") ? display.slice(1) : display);
333
+ result.push({ text: prefix, tone: null }, { text: display + " tps", tone: display && dscodeTpsTone(rate) });
334
+ }
335
+ return result;
336
+ }
337
+ function dscodeTelemetryNodes(value, key) {
338
+ return dscodeTelemetryParts(value).map((part, index) => (0, import_react.createElement)(Text,
339
+ { key: key + "p" + index, color: dscodeTpsInkColor(part.tone) }, part.text));
340
+ }
341
+
162
342
  function dscodeActivity(entries, streaming) {
163
343
  const running = entries.filter(entry => entry.kind === "tool" && entry.state === "running");
164
344
  const tool = running.at(-1);
@@ -175,19 +355,25 @@ function dscodeActivity(entries, streaming) {
175
355
  return "正在执行 · " + singleLineText(tool.name) + (running.length > 1 ? " +" + (running.length - 1) : "") +
176
356
  (description ? " · " + truncateColumns(singleLineText(description), 56) : "");
177
357
  }
358
+ // A snowflake with an arc orbiting it clockwise: top-left, top-right, bottom-right, bottom-left.
359
+ const DSCODE_SPIN_FRAMES = [["◜", "❄", " "], [" ", "❄", "◝"], [" ", "❄", "◞"], ["◟", "❄", " "]];
178
360
  function DscodeActivityLine({ entries, streaming, since, animated = true }) {
179
361
  const columns = useStdout().stdout?.columns ?? 80;
180
- const tick = useFrames(animated ? 160 : 1000);
362
+ const tick = useFrames(animated ? 220 : 1000);
181
363
  const elapsed = since > 0 ? Math.max(0, Date.now() - since) : 0;
182
364
  const suffix = columns >= 48 ? " · 本轮 " + runClock(elapsed) + " · Esc 中断" : " · 本轮 " + runClock(elapsed);
183
- const glyph = animated ? ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"][tick % 10] : "●";
184
- const label = truncateColumns(dscodeActivity(entries, streaming), Math.max(1, columns - 6 - visibleColumns(suffix)));
365
+ const [left, flake, right] = animated ? DSCODE_SPIN_FRAMES[tick % DSCODE_SPIN_FRAMES.length] : [" ", "❄", " "];
366
+ const label = truncateColumns(dscodeActivity(entries, streaming), Math.max(1, columns - 8 - visibleColumns(suffix)));
185
367
  return (0, import_react.createElement)(Box, { paddingX: 2 },
186
368
  (0, import_react.createElement)(Text, { wrap: "truncate-end" },
187
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, glyph + " " + label),
369
+ (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandMid) }, left),
370
+ (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, flake),
371
+ (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandMid) }, right),
372
+ (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " " + label),
188
373
  (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, suffix)));
189
374
  }
190
375
 
376
+
191
377
  // dscode-footer-v1
192
378
  import { footerFor as dscodeFooterFor } from "../../plugins/session-metrics/view.mjs";
193
379
  // dscode-ime-v1
@@ -240,7 +426,8 @@ function dscodeChatLines(entry, columns) {
240
426
  if (!entry.text && !entry.interrupted) return [];
241
427
  entry = { ...entry, reasoning: "" };
242
428
  }
243
- return transcriptEntryLines(entry, columns, false, false, false);
429
+ const rows = transcriptEntryLines(entry, columns, false, false, false);
430
+ return entry.kind === "user" && !entry.notice ? userBackgroundRows(rows, columns, visibleColumns) : rows;
244
431
  }
245
432
  import { n as __require, r as __toESM, t as __commonJSMin } from "./rolldown-runtime-CMFfr-1z.mjs";
246
433
  import { a as inkColor, c as chalk, i as getTheme, n as dim, o as parseThemeName, r as getPalette, s as setTheme } from "./theme-DCT8Y2xf.mjs";
@@ -25874,9 +26061,18 @@ function replayProjectEvent(acc, event) {
25874
26061
  acc.busy = false;
25875
26062
  acc.busySince = 0;
25876
26063
  for (const entry of appended) appendReplayEntry(acc, entry);
25877
- return true;
25878
- }
25879
- case "llm/retry": {
26064
+ for (let index = acc.entries.length - 1; index >= 0; index--) {
26065
+ const entry = acc.entries[index];
26066
+ if (entry === void 0 || entry.kind === "pending") continue;
26067
+ if (entry.turnEnded !== true) {
26068
+ acc.entries[index] = { ...entry, turnEnded: true };
26069
+ acc.ops += 1;
26070
+ }
26071
+ break;
26072
+ }
26073
+ return true;
26074
+ }
26075
+ case "llm/retry": {
25880
26076
  const data = event.data;
25881
26077
  acc.streaming = "";
25882
26078
  acc.streamingReasoning = "";
@@ -28353,7 +28549,7 @@ function cacheHitPercent(usage) {
28353
28549
  return usage.inputTokens === 0 ? null : Math.round(usage.cacheReadTokens / usage.inputTokens * 1e3) / 10;
28354
28550
  }
28355
28551
  /** Separator between trailing state spans. */
28356
- const STATUS_ITEM_SEPARATOR = " · ";
28552
+ const STATUS_ITEM_SEPARATOR = " | ";
28357
28553
  /** The Codex-style mode cycle hint appended to the permission badge. */
28358
28554
  const STATUS_CYCLE_HINT = " (shift+tab to cycle)";
28359
28555
  /** Occupancy at which the usage readout flips from brand blue to amber. */
@@ -28568,7 +28764,7 @@ function safe(text) {
28568
28764
  /** Dim junction separator span inside a cluster. */
28569
28765
  function sep() {
28570
28766
  return {
28571
- text: " · ",
28767
+ text: " | ",
28572
28768
  tone: "label"
28573
28769
  };
28574
28770
  }
@@ -28599,7 +28795,7 @@ function buildCandidates(facts, stats, busy, enabled, contextWidth) {
28599
28795
  if (model !== "" && enabled.has("model")) {
28600
28796
  const effort = safe(facts.effort ?? stats.reasoningEffort);
28601
28797
  push({
28602
- text: effort === "" ? model : `${model} · ${effort}`,
28798
+ text: effort === "" ? model : `${model} | ${effort}`,
28603
28799
  tone: "model"
28604
28800
  });
28605
28801
  }
@@ -28796,7 +28992,7 @@ function layoutStatusBar(facts, stats, columns, options = {}) {
28796
28992
  const budget = Math.max(1, Math.floor(columns) - WIDTH_SAFETY);
28797
28993
  const maxContextWidth = Math.max(CONTEXT_MIN_WIDTH, Math.min(budget, Math.floor(options.contextWidth ?? 24)));
28798
28994
  const { left, right, badge, row2 } = buildCandidates(facts, stats, busy, enabled, maxContextWidth);
28799
- const groupSeparator = visibleColumns(" | ");
28995
+ const groupSeparator = visibleColumns(" | ");
28800
28996
  const itemSeparator = visibleColumns(STATUS_ITEM_SEPARATOR);
28801
28997
  const position = new Map(items.map((id, index) => [id, index]));
28802
28998
  const byPosition = (a, b) => (position.get(a.id) ?? Number.MAX_SAFE_INTEGER) - (position.get(b.id) ?? Number.MAX_SAFE_INTEGER);
@@ -28897,7 +29093,7 @@ function layoutStatusBar(facts, stats, columns, options = {}) {
28897
29093
  };
28898
29094
  }
28899
29095
  const row2Kept = [...orderedRow2];
28900
- const row2Budget = Math.max(0, budget - 2 - (facts.telemetry ? visibleColumns(facts.telemetry) + 3 : 0));
29096
+ const row2Budget = Math.max(0, budget - 1 - (facts.telemetry ? visibleColumns(facts.telemetry) + 3 : 0));
28901
29097
  const row2Width = () => joinWidth(row2Kept.map((entry) => spansWidth(entry.group.spans)), groupSeparator);
28902
29098
  while (row2Width() > row2Budget && row2Kept.length > 0) {
28903
29099
  let dropIndex = 0;
@@ -32003,8 +32199,10 @@ function lineStyleProps(style) {
32003
32199
  function StyledRows({ lines }) {
32004
32200
  return (0, import_react.createElement)(Box, { flexDirection: "column" }, ...lines.map((line, index) => (0, import_react.createElement)(Text, {
32005
32201
  key: index,
32006
- wrap: "truncate-end"
32007
- }, line.segments.length === 0 ? " " : line.segments.map((segment, at) => (0, import_react.createElement)(Text, {
32202
+ wrap: "truncate-end",
32203
+ backgroundColor: line.background === "user" ? inkColor(getPalette().composerBand) : void 0,
32204
+ color: line.background === "user" ? inkColor(getPalette().text) : void 0
32205
+ }, line.segments.length === 0 ? " " : line.segments.map((segment, at) => (0, import_react.createElement)(Text, {
32008
32206
  key: at,
32009
32207
  ...lineStyleProps(segment.style)
32010
32208
  }, segment.text)))));
@@ -32073,29 +32271,25 @@ function Header({ cwd = "", model = "", effort = "" }) {
32073
32271
  const modelName = singleLineText(model).split("/").at(-1) || "unknown";
32074
32272
  const effortName = singleLineText(effort) || "default";
32075
32273
  const project = welcomePath(cwd, detailsWidth);
32274
+ const palette = getPalette();
32275
+ const art = (stdout?.rows ?? 30) >= 26 ? WELCOME_ART : WELCOME_ART_SMALL;
32276
+ const luminance = ([red, green, blue]) => red * 299 + green * 587 + blue * 114;
32277
+ const tones = [palette.brandDeep, palette.brand, palette.brandBright].sort((left, right) => luminance(left) - luminance(right));
32076
32278
  if (!full) return (0, import_react.createElement)(Box, { flexDirection: "column", paddingX: 2, marginBottom: 1 },
32077
32279
  (0, import_react.createElement)(Text, { wrap: "truncate-end" },
32078
32280
  (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright), bold: true }, "❄ DSCODE"),
32079
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, " v0.4.0")),
32281
+ (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, " v0.6.0")),
32080
32282
  (0, import_react.createElement)(Text, { wrap: "truncate-end" }, truncateColumns(modelName + " · " + effortName, width)),
32081
32283
  (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim), wrap: "truncate-end" }, welcomePath(cwd, width)));
32082
- return (0, import_react.createElement)(Box, { flexDirection: "column", width, borderStyle: "round", borderColor: inkColor(getPalette().brand), paddingX: 1, marginBottom: 1 },
32284
+ return (0, import_react.createElement)(Box, { flexDirection: "column", width, borderStyle: "round", borderColor: inkColor(getPalette().brand), paddingX: 1 },
32083
32285
  (0, import_react.createElement)(Box, { flexDirection: "row" },
32084
32286
  (0, import_react.createElement)(Box, { flexDirection: "column", width: 28 },
32085
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " ▄"),
32086
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " █▄▄█ █▄█▄█ █▄▄█"),
32087
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " ▄███▄ ▀█▀ ▄███▄"),
32088
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " ▀██▄ █ ▄██▀"),
32089
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " ▄███▄"),
32090
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " ▀███▀"),
32091
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " ▄██▀ █ ▀██▄"),
32092
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " ▀███▀ ▄█▄ ▀███▀"),
32093
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " █▀▀█ █▀█▀█ █▀▀█"),
32094
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright) }, " ▀")),
32287
+ ...welcomeArtRows(art, { "1": inkColor(tones[0]), "2": inkColor(tones[1]), "3": inkColor(tones[2]) }).map((segments, row) => (0, import_react.createElement)(Text, { key: row }, " ",
32288
+ ...segments.map((segment, index) => (0, import_react.createElement)(Text, { key: index, color: segment.color || void 0, backgroundColor: segment.background || void 0 }, segment.text))))),
32095
32289
  (0, import_react.createElement)(Box, { flexDirection: "column", width: detailsWidth, marginTop: 2 },
32096
32290
  (0, import_react.createElement)(Text, { color: inkColor(getPalette().text), bold: true }, "DSCODE"),
32097
32291
  (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandDeep) }, "────────────"),
32098
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, "v0.4.0"),
32292
+ (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, "v0.6.0"),
32099
32293
  (0, import_react.createElement)(Text, { wrap: "truncate-end" }, truncateColumns("model " + modelName, detailsWidth)),
32100
32294
  (0, import_react.createElement)(Text, { wrap: "truncate-end" }, truncateColumns("effort " + effortName, detailsWidth)),
32101
32295
  (0, import_react.createElement)(Text, null, " "),
@@ -32306,7 +32500,7 @@ function deepseekWaveHues(tier) {
32306
32500
  function StatusLine({ facts, stats, busy, columns, items }) {
32307
32501
  const [, refreshMetrics] = (0, import_react.useState)(0);
32308
32502
  (0, import_react.useEffect)(() => { const timer = setInterval(() => refreshMetrics(n => n + 1), 1000); return () => clearInterval(timer); }, []);
32309
- facts = { ...facts, telemetry: columns >= 64 ? dscodeFooterFor(facts.fullSessionId, stats, Math.max(1, Math.floor(columns / 2) - 4)) : "" };
32503
+ facts = { ...facts, telemetry: columns >= 48 ? dscodeFooterFor(facts.fullSessionId, stats, Math.max(1, Math.min(columns - 8, Math.max(40, Math.floor(columns * 0.8) - 4)))) : "" };
32310
32504
  const layout = (0, import_react.useMemo)(() => layoutStatusBar(facts, stats, Math.max(8, columns - 2), {
32311
32505
  busy,
32312
32506
  items,
@@ -32337,7 +32531,7 @@ function StatusLine({ facts, stats, busy, columns, items }) {
32337
32531
  if (groupIndex > 0) leftParts.push((0, import_react.createElement)(Text, {
32338
32532
  key: key + "gs" + groupIndex,
32339
32533
  color: inkColor(getPalette().dim)
32340
- }, " | "));
32534
+ }, " | "));
32341
32535
  group.spans.forEach((span, spanIndex) => {
32342
32536
  leftParts.push((0, import_react.createElement)(Text, {
32343
32537
  key: key + "g" + groupIndex + "s" + spanIndex,
@@ -32347,6 +32541,7 @@ function StatusLine({ facts, stats, busy, columns, items }) {
32347
32541
  });
32348
32542
  });
32349
32543
  const rightParts = [];
32544
+ if (key === "s2" && row.left.length > 0 && row.right.length > 0) rightParts.push((0, import_react.createElement)(Text, { key: key + "divider", color: inkColor(getPalette().dim) }, "| "));
32350
32545
  row.right.forEach((span, index) => {
32351
32546
  if (index > 0) rightParts.push((0, import_react.createElement)(Text, {
32352
32547
  key: key + "rs" + index,
@@ -32356,7 +32551,7 @@ function StatusLine({ facts, stats, busy, columns, items }) {
32356
32551
  key: key + "r" + index,
32357
32552
  wrap: "truncate-end",
32358
32553
  ...statusToneProps(span.tone)
32359
- }, span.text));
32554
+ }, key === "s2" && index === 0 ? dscodeTelemetryNodes(span.text, key + "r" + index) : span.text));
32360
32555
  });
32361
32556
  if (row.hint) rightParts.push((0, import_react.createElement)(Text, {
32362
32557
  key: key + "hint",
@@ -33820,7 +34015,7 @@ function HelpPanel({ descriptors, skills, commandError, skillError, onClose }) {
33820
34015
  key: "key-cancel",
33821
34016
  dimColor: true,
33822
34017
  wrap: "truncate-end"
33823
- }, " esc interrupt the running turn · ctrl+c cancel / clear / quit · ctrl+d exit"),
34018
+ }, " ctrl+c stop running turn · ctrl+c again exit · ctrl+d exit"),
33824
34019
  (0, import_react.createElement)(Text, {
33825
34020
  key: "key-queue",
33826
34021
  dimColor: true,
@@ -34340,7 +34535,7 @@ function CompletionMenu({ active, mention, index, rows, error }) {
34340
34535
  * While a modal (approval / question / model panel) owns the keys, the
34341
34536
  * box passes every key through untouched.
34342
34537
  */
34343
- function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, skills, dispatch, steer, interrupt, quit, openEmail, emailFill, emailConsumed, openLogin, openModel, openEffort, openHelp, openMode, openPermission, openResume, openPlugin, openJobs, openStatusline, openTheme, openHistory, openAgents, openSubagent, openTodos, openDelete, openDiff, reviewChanges, deleteConfirm, confirmDelete, cancelDelete, createSession, forkSession, cancelSessionSwitch, notify, applyEditorKeys, hasNotice, dismissNotice, toggleReasoning, openVerbose, clearView, refresh, loadMentions, inspectImages, prepareImages, inspectFiles, prepareFiles, cyclePermission, exportTranscript, renameTitle, copyLastResponse, recallSpace, recordLocal, recordHistory, queued, cancelQueued, historyFill, historyConsumed, animations, applyAnimations, waveTier, waveStyle, maxRows, onEditorRows, onMenuRows, sessionKey }) {
34538
+ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, skills, dispatch, steer, interrupt, quit, openEmail, emailFill, emailConsumed, openLogin, openModel, openEffort, openHelp, openMode, openPermission, openResume, openPlugin, openJobs, openStatusline, openTheme, openHistory, openAgents, openSubagent, openTodos, openDelete, openDiff, reviewChanges, deleteConfirm, confirmDelete, cancelDelete, createSession, forkSession, cancelSessionSwitch, notify, applyEditorKeys, hasNotice, dismissNotice, toggleReasoning, openVerbose, clearView, refresh, loadMentions, inspectImages, prepareImages, inspectFiles, prepareFiles, readClipboardImage, cyclePermission, exportTranscript, renameTitle, copyLastResponse, recallSpace, recordLocal, recordHistory, queued, cancelQueued, historyFill, historyConsumed, animations, applyAnimations, waveTier, waveStyle, maxRows, onEditorRows, onMenuRows, onTranscriptScroll, sessionKey }) {
34344
34539
  const columns = useStdout().stdout?.columns ?? 80;
34345
34540
  const inputTerminalRows = useStdout().stdout?.rows ?? 30;
34346
34541
  const dscodeImeStdout = useStdout().stdout;
@@ -34357,6 +34552,8 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34357
34552
  const [cursor, setCursor] = (0, import_react.useState)(0);
34358
34553
  const valueRef = (0, import_react.useRef)(value);
34359
34554
  const cursorRef = (0, import_react.useRef)(cursor);
34555
+ const pendingPastesRef = (0, import_react.useRef)(new Map());
34556
+ const ctrlCArmedRef = (0, import_react.useRef)(false);
34360
34557
  valueRef.current = value;
34361
34558
  cursorRef.current = cursor;
34362
34559
  const [draftImages, setDraftImages] = (0, import_react.useState)([]);
@@ -34400,6 +34597,7 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34400
34597
  (0, import_react.useEffect)(() => {
34401
34598
  if (historyFill === void 0) return;
34402
34599
  const safe = sanitizeDraftText(historyFill.text);
34600
+ pendingPastesRef.current.clear();
34403
34601
  draftImagesRef.current = [];
34404
34602
  setDraftImages([]);
34405
34603
  draftFilesRef.current = [];
@@ -34436,6 +34634,9 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34436
34634
  return next.length === current.length ? current : next;
34437
34635
  });
34438
34636
  }, [value]);
34637
+ (0, import_react.useEffect)(() => {
34638
+ for (const marker of pendingPastesRef.current.keys()) if (!value.includes(marker)) pendingPastesRef.current.delete(marker);
34639
+ }, [value]);
34439
34640
  (0, import_react.useEffect)(() => {
34440
34641
  if (stdin === void 0) return;
34441
34642
  const originalRead = stdin.read.bind(stdin);
@@ -34481,11 +34682,12 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34481
34682
  const uniqueImageMarker = (name, source, reserved = [], kind = "image") => {
34482
34683
  const safeName = singleLineText(sanitizeDraftText(name));
34483
34684
  const label = kind === "file" ? "file" : "image";
34484
- let marker = source === "mention" ? `@${safeName}` : `[${label}: ${safeName}]`;
34685
+ // dscode-image-marker-v1
34686
+ let marker = source === "mention" ? `@${safeName}` : kind === "image" ? "[Image 1]" : `[${label}: ${safeName}]`;
34485
34687
  let suffix = 2;
34486
34688
  const taken = (candidate) => valueRef.current.includes(candidate) || draftImagesRef.current.some((image) => image.marker === candidate) || draftFilesRef.current.some((file) => file.marker === candidate) || reserved.includes(candidate);
34487
34689
  while (taken(marker)) {
34488
- marker = source === "mention" ? `@${safeName} (${suffix})` : `[${label}: ${safeName} ${suffix}]`;
34690
+ marker = source === "mention" ? `@${safeName} (${suffix})` : kind === "image" ? `[Image ${suffix}]` : `[${label}: ${safeName} ${suffix}]`;
34489
34691
  suffix += 1;
34490
34692
  }
34491
34693
  return marker;
@@ -34508,9 +34710,7 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34508
34710
  * durable image blocks, files ride the 0.1.5 file blocks, and both register
34509
34711
  * visible draft markers anchored at the drop point.
34510
34712
  */
34511
- const insertDroppedAttachments = (imagePaths, filePaths) => {
34512
- const originalValue = valueRef.current;
34513
- const originalCursor = cursorRef.current;
34713
+ const insertDroppedAttachments = (imagePaths, filePaths, originalValue = valueRef.current, originalCursor = cursorRef.current) => {
34514
34714
  const total = imagePaths.length + filePaths.length;
34515
34715
  if (total === 0) return;
34516
34716
  notify(`checking ${total} attachment${total === 1 ? "" : "s"}…`);
@@ -34695,6 +34895,7 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34695
34895
  };
34696
34896
  /** Apply one editor edit: draft, cursor, kill buffer, menu reset. */
34697
34897
  const applyEdit = (edit) => {
34898
+ edit = pasteAtomicEdit(valueRef.current, edit, pendingPastesRef.current);
34698
34899
  if (edit.killed !== void 0 && edit.killed !== "") killRef.current = edit.killed;
34699
34900
  valueRef.current = edit.value;
34700
34901
  cursorRef.current = edit.cursor;
@@ -34709,6 +34910,7 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34709
34910
  const moveCursorTo = (next) => {
34710
34911
  resetCursorBlink();
34711
34912
  if (next === cursorRef.current) return;
34913
+ next = pasteCursorEdge(valueRef.current, cursorRef.current, next, pendingPastesRef.current);
34712
34914
  cursorRef.current = next;
34713
34915
  setCursor(next);
34714
34916
  preferredColumnRef.current = null;
@@ -34719,23 +34921,24 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34719
34921
  let nextCursor = cursorRef.current;
34720
34922
  for (const token of tokens) {
34721
34923
  if (token.kind === "text") {
34722
- const edit = insertText(nextValue, nextCursor, token.text);
34924
+ const edit = pasteAtomicEdit(nextValue, insertText(nextValue, nextCursor, collapseLargePaste(token.text, nextValue, pendingPastesRef.current)), pendingPastesRef.current);
34723
34925
  nextValue = edit.value;
34724
34926
  nextCursor = edit.cursor;
34725
34927
  continue;
34726
34928
  }
34727
34929
  if (token.kind === "home") {
34728
- nextCursor = moveToLineStart(nextValue, nextCursor, false);
34930
+ nextCursor = pasteCursorEdge(nextValue, nextCursor, moveToLineStart(nextValue, nextCursor, false), pendingPastesRef.current);
34729
34931
  continue;
34730
34932
  }
34731
34933
  if (token.kind === "end") {
34732
- nextCursor = moveToLineEnd(nextValue, nextCursor, false);
34934
+ nextCursor = pasteCursorEdge(nextValue, nextCursor, moveToLineEnd(nextValue, nextCursor, false), pendingPastesRef.current);
34733
34935
  continue;
34734
34936
  }
34735
34937
  const edit = token.kind === "delete-backward" ? deleteBackward(nextValue, nextCursor) : token.kind === "delete-word-backward" ? deleteWordBackward(nextValue, nextCursor) : token.kind === "delete-forward" ? deleteForward(nextValue, nextCursor) : deleteWordForward(nextValue, nextCursor);
34736
- if (edit.killed !== void 0 && edit.killed !== "") killRef.current = edit.killed;
34737
- nextValue = edit.value;
34738
- nextCursor = edit.cursor;
34938
+ const atomic = pasteAtomicEdit(nextValue, edit, pendingPastesRef.current);
34939
+ if (atomic.killed !== void 0 && atomic.killed !== "") killRef.current = atomic.killed;
34940
+ nextValue = atomic.value;
34941
+ nextCursor = atomic.cursor;
34739
34942
  }
34740
34943
  valueRef.current = nextValue;
34741
34944
  cursorRef.current = nextCursor;
@@ -34762,8 +34965,9 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34762
34965
  const preferred = preferredColumnRef.current ?? caretSite(model, currentCursor).column;
34763
34966
  const next = moveCursorVertically(model, currentCursor, preferred, direction);
34764
34967
  if (next !== currentCursor) {
34765
- cursorRef.current = next;
34766
- setCursor(next);
34968
+ const edge = pasteCursorEdge(currentValue, currentCursor, next, pendingPastesRef.current);
34969
+ cursorRef.current = edge;
34970
+ setCursor(edge);
34767
34971
  resetCursorBlink();
34768
34972
  preferredColumnRef.current = preferred;
34769
34973
  return;
@@ -34784,10 +34988,29 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34784
34988
  resetCursorBlink();
34785
34989
  };
34786
34990
  useStableInput((input, key) => {
34787
- if (!active) return;
34788
- const liveValue = valueRef.current;
34789
- const liveCursor = cursorRef.current;
34790
- if (preparingImages) {
34991
+ if (!active && !(key.ctrl && input === "c")) { ctrlCArmedRef.current = false; return; }
34992
+ const liveValue = valueRef.current;
34993
+ const liveCursor = cursorRef.current;
34994
+ if (key.ctrl && input === "c") {
34995
+ const repeated = ctrlCArmedRef.current;
34996
+ const action = ctrlCAction(ctrlCArmedRef.current, { interrupt, busy, preparingImages, active: active && deleteConfirm === void 0, hasDraft: liveValue !== "" });
34997
+ ctrlCArmedRef.current = action !== "quit";
34998
+ if (action === "quit") quit(repeated);
34999
+ else if (action === "cancel-prepare") cancelImageSubmission();
35000
+ else if (action === "wait" && deleteConfirm !== void 0) cancelDelete();
35001
+ else if (action === "clear-draft") {
35002
+ pendingPastesRef.current.clear();
35003
+ valueRef.current = ""; cursorRef.current = 0;
35004
+ setValue(""); setCursor(0); resetCursorBlink();
35005
+ draftImagesRef.current = []; setDraftImages([]);
35006
+ draftFilesRef.current = []; setDraftFiles([]);
35007
+ setCompletionIndex(0); setDismissedMenuValue(void 0);
35008
+ }
35009
+ return;
35010
+ }
35011
+ ctrlCArmedRef.current = false;
35012
+ if (key.pageUp || key.pageDown) { onTranscriptScroll?.(key.pageUp ? 1 : -1, true); return; }
35013
+ if (preparingImages) {
34791
35014
  if (key.escape || key.ctrl && input === "c") cancelImageSubmission();
34792
35015
  return;
34793
35016
  }
@@ -34810,27 +35033,16 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34810
35033
  toggleReasoning();
34811
35034
  return;
34812
35035
  }
35036
+ if ((key.ctrl || key.meta) && input === "v") {
35037
+ const atValue = liveValue, atCursor = liveCursor;
35038
+ notify("reading clipboard image…");
35039
+ readClipboardImage().then(path => insertDroppedAttachments([path], [], atValue, atCursor), reason => notify(reason instanceof Error ? reason.message : String(reason), "warning"));
35040
+ return;
35041
+ }
34813
35042
  if (key.ctrl && input === "o") {
34814
35043
  openVerbose();
34815
35044
  return;
34816
35045
  }
34817
- if (key.ctrl && input === "c") {
34818
- if (busy) interrupt();
34819
- else if (liveValue !== "") {
34820
- valueRef.current = "";
34821
- cursorRef.current = 0;
34822
- setValue("");
34823
- setCursor(0);
34824
- resetCursorBlink();
34825
- draftImagesRef.current = [];
34826
- setDraftImages([]);
34827
- draftFilesRef.current = [];
34828
- setDraftFiles([]);
34829
- setCompletionIndex(0);
34830
- setDismissedMenuValue(void 0);
34831
- } else quit();
34832
- return;
34833
- }
34834
35046
  if (key.ctrl && input === "d") {
34835
35047
  if (liveValue !== "") {
34836
35048
  applyEdit(deleteForward(liveValue, liveCursor));
@@ -34873,8 +35085,9 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34873
35085
  return;
34874
35086
  }
34875
35087
  }
34876
- const trimmed = liveValue.trim();
34877
- const text = submissionPayload(liveValue);
35088
+ const expandedValue = expandLargePastes(liveValue, pendingPastesRef.current);
35089
+ const trimmed = expandedValue.trim();
35090
+ const text = submissionPayload(expandedValue);
34878
35091
  if (/^\/email(?:\s|$)/.test(trimmed)) {
34879
35092
  if (trimmed !== "/email") { notify("Use /email to open the inbox.", "warning"); return; }
34880
35093
  valueRef.current = ""; cursorRef.current = 0;
@@ -34906,6 +35119,7 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34906
35119
  if (controller.signal.aborted || prepareEpochRef.current !== epoch) return;
34907
35120
  prepareAbortRef.current = void 0;
34908
35121
  setPreparingImages(false);
35122
+ pendingPastesRef.current.clear();
34909
35123
  valueRef.current = "";
34910
35124
  cursorRef.current = 0;
34911
35125
  setValue("");
@@ -34933,6 +35147,7 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34933
35147
  });
34934
35148
  return;
34935
35149
  }
35150
+ pendingPastesRef.current.clear();
34936
35151
  valueRef.current = "";
34937
35152
  cursorRef.current = 0;
34938
35153
  setValue("");
@@ -34979,7 +35194,8 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34979
35194
  return;
34980
35195
  }
34981
35196
  if (text === "/review" || text.startsWith("/review ")) {
34982
- reviewChanges(text.slice(7));
35197
+ // dscode-review-command-v1
35198
+ dispatch(text);
34983
35199
  return;
34984
35200
  }
34985
35201
  if (text === "/model" || text.startsWith("/model ")) {
@@ -35176,12 +35392,12 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
35176
35392
  clearTimeout(timer);
35177
35393
  pasteBracketCancelRef.current = void 0;
35178
35394
  };
35179
- text = text.replaceAll(PASTE_START_MARKER, "");
35395
+ text = text.replaceAll(`\x1b${PASTE_START_MARKER}`, "").replaceAll(PASTE_START_MARKER, "");
35180
35396
  }
35181
35397
  if (text.includes("[201~")) {
35182
35398
  pasteBracketRef.current = false;
35183
35399
  pasteBracketCancelRef.current?.();
35184
- text = text.replaceAll(PASTE_END_MARKER, "");
35400
+ text = text.replaceAll(`\x1b${PASTE_END_MARKER}`, "").replaceAll(PASTE_END_MARKER, "");
35185
35401
  }
35186
35402
  if (text === "") return;
35187
35403
  if (text.length > 1) {
@@ -35191,9 +35407,9 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
35191
35407
  return;
35192
35408
  }
35193
35409
  }
35194
- applyEdit(insertText(valueRef.current, cursorRef.current, text));
35410
+ applyEdit(insertText(valueRef.current, cursorRef.current, collapseLargePaste(text, valueRef.current, pendingPastesRef.current)));
35195
35411
  }
35196
- }, active);
35412
+ }, true);
35197
35413
  const waveKey = waveTier !== null && waveStyle !== null ? `${waveTier}:${waveStyle}` : null;
35198
35414
  const [wavePlayedKey, setWavePlayedKey] = (0, import_react.useState)(null);
35199
35415
  (0, import_react.useEffect)(() => {
@@ -35782,19 +35998,46 @@ function App(props) {
35782
35998
  const composerEditorCap = composerMaxRows(terminalRows);
35783
35999
  const transcriptVisible = !emailOpen && !modelOpen && !helpOpen && !modeOpen && !permissionOpen && !resumeOpen && !pluginOpen && !jobsOpen && !statuslineOpen && !themeOpen && !historyOpen && !agentsOpen && !subagentOpen && !todosOpen && !verboseOpen && diffView === void 0 && !approvalPending && !questionPending;
35784
36000
  const welcomeFull = terminalRows >= 24 && terminalColumns >= 64;
35785
- const welcomeChromeRows = welcomeFull ? 22 : terminalRows >= 10 ? 13 : 10;
35786
- const settledBudget = transcriptVisible ? Math.max(0, terminalRows - welcomeChromeRows - composerGutterRows - (composerRows - 1) - menuRows) : 0;
35787
- const settledViewportRows = busy || view.streaming !== "" ? Math.floor(settledBudget / 3) : settledBudget;
35788
- const renderedSettled = (0, import_react.useMemo)(() => visibleSettledLines(view.entries, settled, settledViewportRows, terminalColumns, showReasoning, settledEntryLines), [settled, view.entries[settled - 1], settledViewportRows, terminalColumns, showReasoning]);
36001
+ const welcomeMaxRows = welcomeFull ? terminalRows >= 26 ? 14 : 13 : terminalRows >= 10 ? 4 : 1;
36002
+ // The nine fixed rows belong to the composer, footer and their gutters.
36003
+ const transcriptCapacity = transcriptVisible ? Math.max(0, terminalRows - 9 - composerGutterRows - (composerRows - 1) - menuRows) : 0;
36004
+ const streamingActive = view.streaming !== "";
36005
+ const deepDivingVisible = busy;
36006
+ const allLiveLines = (0, import_react.useMemo)(() => view.entries.slice(settled).flatMap((entry) => dscodeChatLines(entry, Math.max(1, terminalColumns - 2))), [
36007
+ view.entries,
36008
+ settled,
36009
+ terminalColumns,
36010
+ showReasoning
36011
+ ]);
36012
+ const allSettledLines = (0, import_react.useMemo)(() => visibleSettledLines(view.entries, settled, Infinity, terminalColumns, showReasoning, settledEntryLines), [settled, view.entries[settled - 1], terminalColumns, showReasoning]);
36013
+ const settledTail = allSettledLines.slice(-transcriptCapacity);
36014
+ const streamingDemand = streamingActive ? textLines(view.streaming.slice(-Math.max(1, terminalColumns * transcriptCapacity)), Math.max(10, terminalColumns - 2)).length : 0;
36015
+ const demand = settledTail.length + allLiveLines.length + streamingDemand + (busy ? 1 : 0) + (agentRows.length > 0 ? 1 : 0);
36016
+ const welcomeRows = welcomeVisibleRows(transcriptCapacity, welcomeMaxRows, demand, transcriptVisible);
36017
+ const settledBudget = transcriptVisible ? Math.max(0, transcriptCapacity - welcomeRows) : 0;
36018
+ const settledViewportRows = busy || streamingActive ? Math.floor(settledBudget / 3) : settledBudget;
36019
+ const [scrollOffset, setScrollOffset] = (0, import_react.useState)(0);
36020
+ const previousHistory = (0, import_react.useRef)({ sessionKey: props.sessionKey, count: allSettledLines.length });
36021
+ const sameHistory = previousHistory.current.sessionKey === props.sessionKey;
36022
+ const growth = sameHistory ? Math.max(0, allSettledLines.length - previousHistory.current.count) : 0;
36023
+ const maxScrollOffset = Math.max(0, allSettledLines.length - settledViewportRows);
36024
+ const effectiveScrollOffset = sameHistory ? Math.min(maxScrollOffset, scrollOffset === 0 ? 0 : scrollOffset + growth) : 0;
36025
+ (0, import_react.useEffect)(() => {
36026
+ previousHistory.current = { sessionKey: props.sessionKey, count: allSettledLines.length };
36027
+ if (scrollOffset !== effectiveScrollOffset) setScrollOffset(effectiveScrollOffset);
36028
+ }, [props.sessionKey, allSettledLines.length, effectiveScrollOffset]);
36029
+ const scrollTranscript = (direction, page = false) => {
36030
+ if (!transcriptVisible || settledViewportRows <= 0) return;
36031
+ const step = page ? Math.max(1, settledViewportRows - 1) : 3;
36032
+ setScrollOffset(current => Math.max(0, Math.min(maxScrollOffset, (current === scrollOffset ? effectiveScrollOffset : current) + direction * step)));
36033
+ };
36034
+ (0, import_react.useEffect)(() => {
36035
+ const listener = direction => scrollTranscript(direction);
36036
+ dscodeWheelListeners.add(listener);
36037
+ return () => dscodeWheelListeners.delete(listener);
36038
+ });
36039
+ const renderedSettled = transcriptWindow(allSettledLines, settledViewportRows, effectiveScrollOffset);
35789
36040
  const dynamicRows = Math.max(0, settledBudget - settledViewportRows);
35790
- const streamingActive = view.streaming !== "";
35791
- const deepDivingVisible = busy;
35792
- const allLiveLines = (0, import_react.useMemo)(() => view.entries.slice(settled).flatMap((entry) => dscodeChatLines(entry, Math.max(1, terminalColumns - 2))), [
35793
- view.entries,
35794
- settled,
35795
- terminalColumns,
35796
- showReasoning
35797
- ]);
35798
36041
  const liveBudget = dynamicRows === 0 ? 0 : busy || streamingActive ? Math.max(1, Math.floor(dynamicRows / 3)) : Math.max(0, dynamicRows - (deepDivingVisible ? 1 : 0));
35799
36042
  const visibleLiveLines = liveBudget === 0 ? [] : allLiveLines.slice(-liveBudget);
35800
36043
  const streamRows = Math.max(1, dynamicRows - visibleLiveLines.length);
@@ -36062,7 +36305,8 @@ function App(props) {
36062
36305
  });
36063
36306
  }
36064
36307
  return (0, import_react.createElement)(Box, { flexDirection: "column", height: Math.max(1, terminalRows - 1) },
36065
- !emailOpen && terminalRows >= 10 ? (0, import_react.createElement)(Header, { cwd: props.workspaceRoot ?? props.cwd, model: modelLabel, effort: effortLabel }) : (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright), bold: true }, "DSCODE"),
36308
+ welcomeRows > 0 ? (0, import_react.createElement)(Box, { height: welcomeRows, overflowY: "hidden", flexDirection: "column", justifyContent: "flex-end", flexShrink: 0 },
36309
+ (0, import_react.createElement)(Box, { flexShrink: 0 }, !emailOpen && terminalRows >= 10 ? (0, import_react.createElement)(Header, { cwd: props.workspaceRoot ?? props.cwd, model: modelLabel, effort: effortLabel }) : (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright), bold: true }, "DSCODE"))) : void 0,
36066
36310
  emailOpen && !approvalPending && !questionPending ? (0, import_react.createElement)(DscodeEmailPanel, {
36067
36311
  gmail, imap, columns: terminalColumns, rows: Math.max(3, terminalRows - 8 - composerGutterRows - composerRows),
36068
36312
  close: () => setEmailOpen(false),
@@ -36355,7 +36599,8 @@ function App(props) {
36355
36599
  waveStyle,
36356
36600
  maxRows: composerEditorCap,
36357
36601
  onEditorRows: handleEditorRows,
36358
- onMenuRows: handleMenuRows
36602
+ onMenuRows: handleMenuRows,
36603
+ onTranscriptScroll: scrollTranscript
36359
36604
  }), (0, import_react.createElement)(StatusLine, {
36360
36605
  facts: {
36361
36606
  fullSessionId: props.sessionKey,
@@ -36588,7 +36833,7 @@ function createSplitStdin(source) {
36588
36833
  stalePasteTimer.unref?.();
36589
36834
  };
36590
36835
  const onChunk = (chunk) => {
36591
- for (const unit of splitter.push(String(chunk))) stream.write(unit);
36836
+ for (const unit of splitter.push(String(chunk))) if (!dscodeHandleMouseUnit(unit)) stream.write(unit);
36592
36837
  if (splitter.openPaste()) armStalePasteTimer();
36593
36838
  else disarmStalePasteTimer();
36594
36839
  };
@@ -36633,7 +36878,7 @@ const internals = {
36633
36878
  if (process.stdin.isTTY === true) process.stdin.setRawMode?.(true);
36634
36879
  try {
36635
36880
  if (process.stdout.isTTY === true) process.stdout.write("\x1B[r\x1B[0m\x1B[H\x1B[2J\x1B[3J\x1B[H");
36636
- process.stdout.write((keyboardEnhanced ? KEYBOARD_ENHANCE_ENABLE : "") + BRACKETED_PASTE_ENABLE + (focusReporting ? TERMINAL_FOCUS_REPORT_ENABLE : ""));
36881
+ process.stdout.write((keyboardEnhanced ? KEYBOARD_ENHANCE_ENABLE : "") + BRACKETED_PASTE_ENABLE + (focusReporting ? TERMINAL_FOCUS_REPORT_ENABLE : "") + (process.stdout.isTTY === true ? DSCODE_MOUSE_ENABLE : ""));
36637
36882
  const tuiStdin = createSplitStdin(process.stdin);
36638
36883
  const instance = render(element, {
36639
36884
  exitOnCtrlC: false,
@@ -36646,7 +36891,7 @@ const internals = {
36646
36891
  },
36647
36892
  unmount() {
36648
36893
  try {
36649
- process.stdout.write((keyboardEnhanced ? KEYBOARD_ENHANCE_DISABLE : "") + BRACKETED_PASTE_DISABLE + (focusReporting ? TERMINAL_FOCUS_REPORT_DISABLE : ""));
36894
+ process.stdout.write((keyboardEnhanced ? KEYBOARD_ENHANCE_DISABLE : "") + BRACKETED_PASTE_DISABLE + (focusReporting ? TERMINAL_FOCUS_REPORT_DISABLE : "") + (process.stdout.isTTY === true ? DSCODE_MOUSE_DISABLE : ""));
36650
36895
  } finally {
36651
36896
  try {
36652
36897
  instance.unmount();
@@ -36658,6 +36903,7 @@ const internals = {
36658
36903
  }
36659
36904
  };
36660
36905
  } catch (error) {
36906
+ if (process.stdout.isTTY === true) process.stdout.write(DSCODE_MOUSE_DISABLE);
36661
36907
  if (process.stdin.isTTY === true) process.stdin.setRawMode?.(false);
36662
36908
  throw error;
36663
36909
  }
@@ -38724,7 +38970,7 @@ async function run(ctx, startup, io) {
38724
38970
  };
38725
38971
  const mountRef = {};
38726
38972
  let quitting = false;
38727
- const quit = () => {
38973
+ const quit = (fast = false) => {
38728
38974
  if (quitting) return;
38729
38975
  quitting = true;
38730
38976
  switchQueue.cancel();
@@ -38744,6 +38990,7 @@ async function run(ctx, startup, io) {
38744
38990
  } catch (error) {
38745
38991
  report("unmount", error);
38746
38992
  }
38993
+ if (fast) setTimeout(() => process.exit(0), 250);
38747
38994
  runQuitSequence([
38748
38995
  ...currentSession === void 0 || currentActive === void 0 ? [] : [{
38749
38996
  name: "flush",
@@ -39451,6 +39698,7 @@ async function run(ctx, startup, io) {
39451
39698
  openAuthorizationUrl,
39452
39699
  copyTextValue: copyText,
39453
39700
  loadMentions: (query, signal) => mentions.candidates(query, signal),
39701
+ readClipboardImage: dscodeReadClipboardImage,
39454
39702
  inspectImages: (paths) => inspectImagePaths(paths, ctx.get("attachments"), session?.header.cwd ?? cwd),
39455
39703
  prepareImages: (paths, signal) => saveImagePaths(paths, ctx.get("attachments"), signal),
39456
39704
  inspectFiles: (paths) => inspectFilePaths(paths, ctx.get("attachments"), session?.header.cwd ?? cwd),