@toddzheng024/dscode-bundle 0.3.0 → 0.5.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 (113) hide show
  1. package/THIRD_PARTY_NOTICES.md +15 -0
  2. package/cordis.patch.yml +5 -3
  3. package/package.json +14 -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 +17 -3
  9. package/plugins/email/cli.mjs +32 -0
  10. package/plugins/email/contacts.mjs +36 -0
  11. package/plugins/email/gmail-oauth.mjs +69 -0
  12. package/plugins/email/gmail-store.mjs +2 -0
  13. package/plugins/email/gmail.mjs +194 -0
  14. package/plugins/email/imap.mjs +136 -0
  15. package/plugins/email/inbox.d.mts +25 -0
  16. package/plugins/email/inbox.mjs +76 -0
  17. package/plugins/email/smtp.mjs +69 -0
  18. package/plugins/email/store.mjs +32 -0
  19. package/plugins/email-tools/index.mjs +44 -0
  20. package/plugins/session-metrics/index.mjs +6 -2
  21. package/plugins/session-metrics/rate.mjs +62 -0
  22. package/plugins/session-metrics/view.mjs +15 -9
  23. package/plugins/tui-tools/doctor-cli.mjs +22 -0
  24. package/plugins/tui-tools/doctor.mjs +141 -0
  25. package/plugins/tui-tools/index.mjs +14 -6
  26. package/plugins/ultra/policy.mjs +12 -1
  27. package/plugins/worktree-subagent/worktree.mjs +50 -0
  28. package/presets/dscode/agent.cordis.yml +7 -2
  29. package/vendor/deepseek/index.js +12 -2
  30. package/vendor/subagent/index.js +29 -9
  31. package/vendor/subagent-core/LICENSE +21 -0
  32. package/vendor/subagent-core/index.js +3232 -0
  33. package/vendor/subagent-core/invariant.js +76 -0
  34. package/vendor/subagent-core/typert.host.d.ts +3 -0
  35. package/vendor/subagent-core/typert.host.js +964 -0
  36. package/vendor/subagent-core/typert.remote-client.d.ts +27 -0
  37. package/vendor/subagent-core/typert.remote-client.js +160 -0
  38. package/vendor/subagent-core/types/assistant-output.d.ts +48 -0
  39. package/vendor/subagent-core/types/assistant-output.js +73 -0
  40. package/vendor/subagent-core/types/catalog.d.ts +77 -0
  41. package/vendor/subagent-core/types/catalog.js +103 -0
  42. package/vendor/subagent-core/types/child-agent.d.ts +150 -0
  43. package/vendor/subagent-core/types/child-agent.js +207 -0
  44. package/vendor/subagent-core/types/client.d.ts +8 -0
  45. package/vendor/subagent-core/types/client.js +7 -0
  46. package/vendor/subagent-core/types/continuation-activation.d.ts +251 -0
  47. package/vendor/subagent-core/types/continuation-activation.js +663 -0
  48. package/vendor/subagent-core/types/continuation-messages.d.ts +62 -0
  49. package/vendor/subagent-core/types/continuation-messages.js +102 -0
  50. package/vendor/subagent-core/types/continuation.d.ts +124 -0
  51. package/vendor/subagent-core/types/continuation.js +455 -0
  52. package/vendor/subagent-core/types/control-types.d.ts +146 -0
  53. package/vendor/subagent-core/types/control-types.js +9 -0
  54. package/vendor/subagent-core/types/control.d.ts +71 -0
  55. package/vendor/subagent-core/types/control.js +116 -0
  56. package/vendor/subagent-core/types/depth.d.ts +31 -0
  57. package/vendor/subagent-core/types/depth.js +39 -0
  58. package/vendor/subagent-core/types/descriptor.d.ts +144 -0
  59. package/vendor/subagent-core/types/descriptor.js +193 -0
  60. package/vendor/subagent-core/types/error.d.ts +11 -0
  61. package/vendor/subagent-core/types/error.js +14 -0
  62. package/vendor/subagent-core/types/inbox.d.ts +43 -0
  63. package/vendor/subagent-core/types/inbox.js +61 -0
  64. package/vendor/subagent-core/types/index.d.ts +316 -0
  65. package/vendor/subagent-core/types/index.js +511 -0
  66. package/vendor/subagent-core/types/internal.d.ts +58 -0
  67. package/vendor/subagent-core/types/internal.js +58 -0
  68. package/vendor/subagent-core/types/invariant.d.ts +13 -0
  69. package/vendor/subagent-core/types/invariant.js +91 -0
  70. package/vendor/subagent-core/types/lifecycle.d.ts +113 -0
  71. package/vendor/subagent-core/types/lifecycle.js +178 -0
  72. package/vendor/subagent-core/types/list-children.d.ts +65 -0
  73. package/vendor/subagent-core/types/list-children.js +381 -0
  74. package/vendor/subagent-core/types/out-of-process.d.ts +118 -0
  75. package/vendor/subagent-core/types/out-of-process.js +219 -0
  76. package/vendor/subagent-core/types/projection-types.d.ts +74 -0
  77. package/vendor/subagent-core/types/projection-types.js +7 -0
  78. package/vendor/subagent-core/types/projection.d.ts +95 -0
  79. package/vendor/subagent-core/types/projection.js +151 -0
  80. package/vendor/subagent-core/types/run-settlement.d.ts +17 -0
  81. package/vendor/subagent-core/types/run-settlement.js +70 -0
  82. package/vendor/subagent-core/types/types.d.ts +377 -0
  83. package/vendor/subagent-core/types/types.js +19 -0
  84. package/vendor/subagent-driver/LICENSE +21 -0
  85. package/vendor/subagent-driver/index.js +255 -0
  86. package/vendor/subagent-driver/types/index.d.ts +33 -0
  87. package/vendor/subagent-driver/types/structured.d.ts +42 -0
  88. package/vendor/subagent-fork/LICENSE +21 -0
  89. package/vendor/subagent-fork/index.js +61 -0
  90. package/vendor/subagent-fork/types/index.d.ts +20 -0
  91. package/vendor/subagent-spawn/LICENSE +21 -0
  92. package/vendor/subagent-spawn/index.js +45 -0
  93. package/vendor/subagent-spawn/types/index.d.ts +19 -0
  94. package/vendor/terminal/LICENSE +21 -0
  95. package/vendor/terminal/index.js +1013 -0
  96. package/vendor/terminal/types/config.d.ts +71 -0
  97. package/vendor/terminal/types/index.d.ts +36 -0
  98. package/vendor/terminal/types/sanitize.d.ts +47 -0
  99. package/vendor/terminal/types/session.d.ts +87 -0
  100. package/vendor/tui/dscode-clipboard-image/clipboard-image.swift +56 -0
  101. package/vendor/tui/dscode-clipboard-image/index.mjs +70 -0
  102. package/vendor/tui/dscode-email/cli.mjs +32 -0
  103. package/vendor/tui/dscode-email/contacts.mjs +36 -0
  104. package/vendor/tui/dscode-email/gmail-oauth.mjs +69 -0
  105. package/vendor/tui/dscode-email/gmail-store.mjs +2 -0
  106. package/vendor/tui/dscode-email/gmail.mjs +194 -0
  107. package/vendor/tui/dscode-email/imap.mjs +136 -0
  108. package/vendor/tui/dscode-email/inbox.d.mts +25 -0
  109. package/vendor/tui/dscode-email/inbox.mjs +76 -0
  110. package/vendor/tui/dscode-email/smtp.mjs +69 -0
  111. package/vendor/tui/dscode-email/store.mjs +32 -0
  112. package/vendor/tui/dscode-email.mjs +69 -0
  113. package/vendor/tui/index.mjs +473 -82
@@ -1,3 +1,256 @@
1
+ import { readClipboardImage as dscodeReadClipboardImage } from "./dscode-clipboard-image/index.mjs";
2
+ // dscode-clipboard-image-v1
3
+ // dscode-large-paste-v1
4
+ function collapseLargePaste(text, draft, pastes) {
5
+ const safe = text.replaceAll('\r\n', '\n').replaceAll('\r', '\n').replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/gu, '').replaceAll('\t', ' ');
6
+ const count = [...safe].length;
7
+ if (count <= LARGE_PASTE_CHARS) return safe;
8
+ // Keep an executable-looking first line visible to the command router.
9
+ if (draft.trim() === '' && /^\s*[!/]/u.test(safe)) return safe;
10
+ const base = `[Pasted Content ${count} chars]`;
11
+ let marker = base;
12
+ for (let suffix = 2; draft.includes(marker) || pastes.has(marker); suffix++) marker = `${base} #${suffix}`;
13
+ pastes.set(marker, safe);
14
+ return marker;
15
+ }
16
+ function expandLargePastes(value, pastes) {
17
+ const markers = [...pastes.keys()].sort((left, right) => right.length - left.length);
18
+ let expanded = '';
19
+ let offset = 0;
20
+ while (offset < value.length) {
21
+ let found = -1;
22
+ let match;
23
+ for (const marker of markers) {
24
+ const at = value.indexOf(marker, offset);
25
+ if (at >= 0 && (found < 0 || at < found)) {
26
+ found = at;
27
+ match = marker;
28
+ }
29
+ }
30
+ if (match === undefined) break;
31
+ expanded += value.slice(offset, found) + pastes.get(match);
32
+ offset = found + match.length;
33
+ }
34
+ return expanded + value.slice(offset);
35
+ }
36
+ function pasteAtomicEdit(before, edit, pastes) {
37
+ let prefix = 0;
38
+ while (prefix < before.length && prefix < edit.value.length && before[prefix] === edit.value[prefix]) prefix++;
39
+ let suffix = 0;
40
+ while (suffix < before.length - prefix && suffix < edit.value.length - prefix && before[before.length - suffix - 1] === edit.value[edit.value.length - suffix - 1]) suffix++;
41
+ let from = prefix, to = before.length - suffix;
42
+ const inserted = edit.value.slice(prefix, edit.value.length - suffix);
43
+ let touched = false;
44
+ const originalPastes = new Map(pastes);
45
+ for (const marker of pastes.keys()) {
46
+ const start = before.indexOf(marker);
47
+ if (start < 0) continue;
48
+ const end = start + marker.length;
49
+ if (from < end && to > start || from === to && from > start && from < end) {
50
+ from = Math.min(from, start);
51
+ to = Math.max(to, end);
52
+ pastes.delete(marker);
53
+ touched = true;
54
+ }
55
+ }
56
+ if (touched) edit = { ...edit, value: before.slice(0, from) + inserted + before.slice(to), cursor: from + inserted.length,
57
+ killed: edit.killed === undefined ? undefined : expandLargePastes(before.slice(from, to), originalPastes) };
58
+ for (const marker of pastes.keys()) if (!edit.value.includes(marker) && before.includes(marker)) pastes.delete(marker);
59
+ return edit;
60
+ }
61
+ function pasteCursorEdge(value, previous, next, pastes) {
62
+ for (const marker of pastes.keys()) {
63
+ const start = value.indexOf(marker);
64
+ if (start < 0) continue;
65
+ const end = start + marker.length;
66
+ if (next > start && next < end) return next < previous ? start : end;
67
+ }
68
+ return next;
69
+ }
70
+ const LARGE_PASTE_CHARS = 200;
71
+ // dscode-user-background-v1
72
+ function userBackgroundRows(rows, columns, measure) {
73
+ const width = Math.max(1, Math.floor(columns));
74
+ return rows.map(row => {
75
+ const used = row.segments.reduce((sum, segment) => sum + measure(segment.text), 0);
76
+ return {
77
+ ...row,
78
+ background: 'user',
79
+ segments: [...row.segments, { text: ' '.repeat(Math.max(0, width - used)), style: 'plain' }],
80
+ };
81
+ });
82
+ }
83
+ // dscode-turn-divider-v1
84
+ function turnDividedLines(entry, columns, rendered, remaining) {
85
+ if (entry?.turnEnded !== true || (rendered.length > 0 && rendered.length >= remaining)) return rendered;
86
+ const width = Math.max(1, Math.min(72, columns - 6));
87
+ return [...rendered, { segments: [{ text: ` ${'─'.repeat(width)}`, style: 'dim' }] }];
88
+ }
89
+ // dscode-scroll-v1
90
+ function transcriptWindow(lines, rows, offset) {
91
+ if (rows <= 0) return [];
92
+ const end = Math.max(0, lines.length - Math.max(0, offset));
93
+ return lines.slice(Math.max(0, end - rows), end);
94
+ }
95
+ function mouseWheelDirection(unit) {
96
+ const match = /^\x1b\[<(\d+);\d+;\d+([Mm])$/.exec(unit);
97
+ if (!match) return null;
98
+ if (match[2] !== 'M') return 0;
99
+ const button = Number(match[1]) & ~(4 | 8 | 16);
100
+ return button === 64 ? 1 : button === 65 ? -1 : 0;
101
+ }
102
+ const DSCODE_MOUSE_ENABLE = "\x1b[?1000h\x1b[?1006h";
103
+ const DSCODE_MOUSE_DISABLE = "\x1b[?1006l\x1b[?1000l";
104
+ const dscodeWheelListeners = new Set();
105
+ function dscodeHandleMouseUnit(unit) {
106
+ const direction = mouseWheelDirection(unit);
107
+ if (direction === null) return false;
108
+ if (direction !== 0) for (const listener of dscodeWheelListeners) listener(direction);
109
+ return true;
110
+ }
111
+ // dscode-interrupt-v1
112
+ function ctrlCAction(armed, { interrupt, busy, preparingImages, active, hasDraft }) {
113
+ if (armed) return 'quit';
114
+ if (interrupt() || busy) return 'interrupt';
115
+ if (preparingImages) return 'cancel-prepare';
116
+ if (!active) return 'wait';
117
+ return hasDraft ? 'clear-draft' : 'quit';
118
+ }
119
+ // dscode-welcome-scroll-v1
120
+ function welcomeVisibleRows(capacity, maximum, demand, canScroll = true) {
121
+ if (!canScroll || capacity < maximum) return maximum;
122
+ return Math.max(0, Math.min(maximum, capacity - demand));
123
+ }
124
+ // dscode-email-v4
125
+ import { createImapConnector as dscodeCreateImapConnector } from "./dscode-email/imap.mjs";
126
+ import { createGmailConnector as dscodeCreateGmailConnector } from "./dscode-email/gmail.mjs";
127
+ import { createEmailInbox as dscodeCreateEmailInbox, emailKey as dscodeEmailKey, emailPrompt as dscodeEmailPrompt, emailText as dscodeEmailText } from "./dscode-email/inbox.mjs";
128
+ function DscodeImapSetup({ connector, back, done }) {
129
+ const [values, setValues] = (0, import_react.useState)(() => {
130
+ const saved = connector.status();
131
+ return [saved.account || '', saved.host || 'imap.gmail.com', String(saved.port || 993), saved.mailbox || 'INBOX', ''];
132
+ });
133
+ const [step, setStep] = (0, import_react.useState)(0);
134
+ const [error, setError] = (0, import_react.useState)('');
135
+ const [busy, setBusy] = (0, import_react.useState)(false);
136
+ const operation = (0, import_react.useRef)(null);
137
+ (0, import_react.useEffect)(() => () => operation.current?.abort(), []);
138
+ const names = ['Email address', 'IMAP host', 'TLS port', 'Mailbox folder', 'Application password'];
139
+ useStableInput((input, key) => {
140
+ if (key.escape || key.ctrl && input === 'c') { operation.current?.abort(); setValues([]); back(); return; }
141
+ if (operation.current) return;
142
+ if (key.return) {
143
+ if (!values[step]?.trim()) { setError('This field is required.'); return; }
144
+ if (step < 4) { setStep(step + 1); setError(''); return; }
145
+ const [account, host, port, mailbox, password] = values;
146
+ const controller = new AbortController(); operation.current = controller;
147
+ setValues(current => current.map((value, index) => index === 4 ? '' : value)); setBusy(true); setError('');
148
+ Promise.resolve().then(() => connector.connect({ account, host, port, mailbox, password }, { signal: controller.signal })).then(result => {
149
+ if (controller.signal.aborted) return;
150
+ if (result.busy) { setError('Another session is syncing. Retry shortly.'); return; }
151
+ done();
152
+ }, reason => { if (!controller.signal.aborted) setError(reason.message); }).finally(() => {
153
+ operation.current = null; if (!controller.signal.aborted) setBusy(false);
154
+ });
155
+ return;
156
+ }
157
+ if (key.tab) { setStep(current => (current + (key.shift ? 4 : 1)) % 5); setError(''); return; }
158
+ if (key.ctrl && input === 'u') { setValues(current => current.map((value, index) => index === step ? '' : value)); return; }
159
+ if (key.backspace || key.delete) { setValues(current => current.map((value, index) => index === step ? [...value].slice(0, -1).join('') : value)); return; }
160
+ if (key.ctrl || key.meta || !input) return;
161
+ const pasted = stripPasteMarkers(input).replace(/[\x00-\x1f\x7f]/g, '');
162
+ setValues(current => current.map((value, index) => index === step ? (value + pasted).slice(0, 1024) : value));
163
+ });
164
+ const text = (value, props = {}) => (0, import_react.createElement)(Text, { wrap: 'truncate-end', ...props }, value);
165
+ return (0, import_react.createElement)(Box, { flexDirection: 'column' },
166
+ text('Connect IMAP · ' + (step + 1) + '/5', { bold: true }),
167
+ text(busy ? 'Connecting securely…' : names[step] + ' › ' + (step === 4 ? values[step] ? '••••••••' : '' : values[step])),
168
+ text(error || (step === 4 ? 'Gmail: use an app password from 2-Step Verification.' : 'Enter keeps defaults · Ctrl+U clears'), { dimColor: !error, color: error ? 'red' : undefined }),
169
+ text('Enter next/connect · Tab edit · Esc cancel', { dimColor: true }));
170
+ }
171
+ function DscodeEmailPanel({ columns, rows, pick, close, gmail, imap }) {
172
+ const [snapshot, setSnapshot] = (0, import_react.useState)({ emails: [], rejected: 0 });
173
+ const [error, setError] = (0, import_react.useState)('');
174
+ const [selected, setSelected] = (0, import_react.useState)(null);
175
+ const [offset, setOffset] = (0, import_react.useState)(0);
176
+ const [preview, setPreview] = (0, import_react.useState)(false);
177
+ const [gmailStatus, setGmailStatus] = (0, import_react.useState)(() => gmail.status());
178
+ const [imapStatus, setImapStatus] = (0, import_react.useState)(() => imap.status());
179
+ const [setup, setSetup] = (0, import_react.useState)(false);
180
+ const [connecting, setConnecting] = (0, import_react.useState)(false);
181
+ const operation = (0, import_react.useRef)(null);
182
+ (0, import_react.useEffect)(() => () => operation.current?.abort(), []);
183
+ const inbox = (0, import_react.useMemo)(() => dscodeCreateEmailInbox(), []);
184
+ const refresh = () => {
185
+ try { setSnapshot(inbox.list()); setGmailStatus(gmail.status()); setImapStatus(imap.status()); }
186
+ catch { setError('Could not read inbox. Press r to retry.'); }
187
+ };
188
+ (0, import_react.useEffect)(() => {
189
+ refresh();
190
+ const timer = setInterval(refresh, 2000);
191
+ return () => clearInterval(timer);
192
+ }, [inbox]);
193
+ const emails = snapshot.emails;
194
+ const index = Math.max(0, emails.findIndex(mail => dscodeEmailKey(mail) === selected));
195
+ const mail = emails[index];
196
+ (0, import_react.useEffect)(() => {
197
+ if (mail) setSelected(dscodeEmailKey(mail));
198
+ }, [mail && dscodeEmailKey(mail)]);
199
+ const height = Math.max(3, rows);
200
+ const contentRows = Math.max(1, height - 4);
201
+ const wide = columns >= 64;
202
+ const listWidth = wide ? Math.max(26, Math.floor(columns * 0.4)) : columns;
203
+ const previewWidth = wide ? Math.max(1, columns - listWidth - 1) : columns;
204
+ const clean = value => dscodeEmailText(value).replace(/\n/g, ' ');
205
+ const bodyLines = mail ? wrapText(dscodeEmailText(mail.body), Math.max(1, previewWidth - 2), 'wrap').split('\n') : [];
206
+ useStableInput((input, key) => {
207
+ if (setup) return;
208
+ if (key.escape || key.ctrl && input === 'c') { close(); return; }
209
+ if (input === 'i' && !operation.current) { setSetup(true); setError(''); return; }
210
+ if (input === 'g' || input === 'r') {
211
+ if (operation.current) return;
212
+ const controller = new AbortController(); operation.current = controller;
213
+ setError(''); setConnecting(input === 'g');
214
+ const action = input === 'g' ? gmail.connect({ signal: controller.signal }) : (imapStatus.connected ? imap : gmail).sync({ force: true, signal: controller.signal });
215
+ Promise.resolve(action).then(result => {
216
+ if (!controller.signal.aborted) { if (result.busy) setError('Gmail is busy in another session. Retry shortly.'); refresh(); }
217
+ }, reason => { if (!controller.signal.aborted) setError(reason.message); }).finally(() => {
218
+ operation.current = null;
219
+ if (!controller.signal.aborted) setConnecting(false);
220
+ });
221
+ return;
222
+ }
223
+ if (key.tab) { setPreview(current => !current); return; }
224
+ if (!mail) return;
225
+ if (key.upArrow || key.downArrow) {
226
+ const next = Math.max(0, Math.min(emails.length - 1, index + (key.upArrow ? -1 : 1)));
227
+ setSelected(dscodeEmailKey(emails[next])); setOffset(0); return;
228
+ }
229
+ if (key.pageDown || key.pageUp) {
230
+ setOffset(current => Math.max(0, Math.min(Math.max(0, bodyLines.length - contentRows + 2), current + (key.pageUp ? -1 : 1) * Math.max(1, contentRows - 2)))); return;
231
+ }
232
+ if (key.return) pick(mail);
233
+ });
234
+ if (setup) return (0, import_react.createElement)(Box, { height, overflow: 'hidden', flexDirection: 'column' },
235
+ (0, import_react.createElement)(DscodeImapSetup, { connector: imap, back: () => setSetup(false), done: () => { setSetup(false); refresh(); } }));
236
+ const text = (value, extra = {}) => (0, import_react.createElement)(Text, { wrap: 'truncate-end', ...extra }, value);
237
+ const start = Math.max(0, index - contentRows + 1);
238
+ const list = (0, import_react.createElement)(Box, { width: listWidth, flexDirection: 'column', overflow: 'hidden' },
239
+ text('Email · newest updates first', { bold: true }),
240
+ ...emails.slice(start, start + contentRows).map((entry, i) => text(
241
+ (start + i === index ? '› ' : ' ') + entry.updatedAt.slice(5, 16).replace('T', ' ') + ' ' + clean(entry.subject),
242
+ { key: dscodeEmailKey(entry), color: start + i === index ? 'cyan' : undefined })),
243
+ !mail ? text(error || 'No emails received.') : null);
244
+ return (0, import_react.createElement)(Box, { flexDirection: 'column', height, overflow: 'hidden' },
245
+ text(imapStatus.connected ? imapStatus.error || 'IMAP · ' + clean(imapStatus.account) + ' · ' + clean(imapStatus.mailbox) : connecting ? 'Gmail · Complete Google login in your browser · Esc cancels' : gmailStatus.error || (gmailStatus.connected ? 'Gmail · ' + clean(gmailStatus.account) + (gmailStatus.lastSyncAt ? ' · synced ' + new Date(gmailStatus.lastSyncAt).toLocaleTimeString() : ' · waiting for new mail') : 'Email not connected · i IMAP · g Google OAuth'), { dimColor: true }),
246
+ (0, import_react.createElement)(Box, { flexDirection: 'row', height: Math.max(1, height - 2) },
247
+ wide || preview ? (0, import_react.createElement)(Box, { width: previewWidth, marginRight: wide ? 1 : 0, flexDirection: 'column', overflow: 'hidden' },
248
+ text(mail ? clean(mail.subject) : 'Email preview', { bold: true }),
249
+ text(mail ? 'From: ' + clean(mail.from) : 'Waiting for a connector', { dimColor: true }),
250
+ ...bodyLines.slice(offset, offset + Math.max(1, contentRows - 1)).map((line, i) => text(line, { key: i }))) : null,
251
+ wide || !preview ? list : null),
252
+ text(error || (snapshot.rejected ? snapshot.rejected + ' invalid records skipped · ' : '') + (wide ? '↑↓ select · Enter steer · Esc · PgUp/Dn · i IMAP · g OAuth · r sync' : '↑↓ Enter · Tab · i IMAP · r sync'), { dimColor: true }));
253
+ }
1
254
  // dscode-effort-bar-v6
2
255
  // dscode-welcome-v1
3
256
  function welcomePath(path, width) {
@@ -18,7 +271,7 @@ function visibleSettledLines(entries, settled, budget, columns, showReasoning, r
18
271
  const chunks = [];
19
272
  let remaining = budget;
20
273
  for (let index = settled - 1; index >= 0 && remaining > 0; index--) {
21
- const lines = renderEntry(entries[index], Math.max(10, columns - 2), showReasoning);
274
+ const lines = turnDividedLines(entries[index], columns, renderEntry(entries[index], Math.max(10, columns - 2), showReasoning), remaining);
22
275
  const kept = lines.slice(-remaining);
23
276
  if (kept.length) {
24
277
  chunks.unshift(kept);
@@ -29,6 +282,40 @@ function visibleSettledLines(entries, settled, budget, columns, showReasoning, r
29
282
  }
30
283
  // dscode-style-v1
31
284
 
285
+
286
+ // dscode-tps-colors-v1
287
+ function dscodeTpsTone(rate) {
288
+ if (!Number.isFinite(rate) || rate < 0) return null;
289
+ if (rate < 75) return "yellow";
290
+ if (rate < 150) return "green";
291
+ if (rate <= 250) return "blue";
292
+ return "purple";
293
+ }
294
+ function dscodeTpsInkColor(tone) {
295
+ const palette = getPalette();
296
+ if (tone === "yellow") return inkColor(palette.warn);
297
+ if (tone === "green") return inkColor(palette.success);
298
+ if (tone === "blue") return inkColor(palette.brandBright);
299
+ if (tone === "purple") return inkColor(getTheme() === "light" ? [126, 34, 206] : [192, 132, 252]);
300
+ return void 0;
301
+ }
302
+ function dscodeTelemetryParts(value) {
303
+ const result = [];
304
+ for (const [index, part] of value.split(" | ").entries()) {
305
+ if (index > 0) result.push({ text: " | ", tone: null });
306
+ const prefix = part.startsWith("current: ") ? "current: " : part.startsWith("average: ") ? "average: " : "";
307
+ if (!prefix || !part.endsWith(" tps")) { result.push({ text: part, tone: null }); continue; }
308
+ const display = part.slice(prefix.length, -4);
309
+ const rate = Number(display.startsWith("~") ? display.slice(1) : display);
310
+ result.push({ text: prefix, tone: null }, { text: display + " tps", tone: display && dscodeTpsTone(rate) });
311
+ }
312
+ return result;
313
+ }
314
+ function dscodeTelemetryNodes(value, key) {
315
+ return dscodeTelemetryParts(value).map((part, index) => (0, import_react.createElement)(Text,
316
+ { key: key + "p" + index, color: dscodeTpsInkColor(part.tone) }, part.text));
317
+ }
318
+
32
319
  function dscodeActivity(entries, streaming) {
33
320
  const running = entries.filter(entry => entry.kind === "tool" && entry.state === "running");
34
321
  const tool = running.at(-1);
@@ -110,7 +397,8 @@ function dscodeChatLines(entry, columns) {
110
397
  if (!entry.text && !entry.interrupted) return [];
111
398
  entry = { ...entry, reasoning: "" };
112
399
  }
113
- return transcriptEntryLines(entry, columns, false, false, false);
400
+ const rows = transcriptEntryLines(entry, columns, false, false, false);
401
+ return entry.kind === "user" && !entry.notice ? userBackgroundRows(rows, columns, visibleColumns) : rows;
114
402
  }
115
403
  import { n as __require, r as __toESM, t as __commonJSMin } from "./rolldown-runtime-CMFfr-1z.mjs";
116
404
  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";
@@ -25744,9 +26032,18 @@ function replayProjectEvent(acc, event) {
25744
26032
  acc.busy = false;
25745
26033
  acc.busySince = 0;
25746
26034
  for (const entry of appended) appendReplayEntry(acc, entry);
25747
- return true;
25748
- }
25749
- case "llm/retry": {
26035
+ for (let index = acc.entries.length - 1; index >= 0; index--) {
26036
+ const entry = acc.entries[index];
26037
+ if (entry === void 0 || entry.kind === "pending") continue;
26038
+ if (entry.turnEnded !== true) {
26039
+ acc.entries[index] = { ...entry, turnEnded: true };
26040
+ acc.ops += 1;
26041
+ }
26042
+ break;
26043
+ }
26044
+ return true;
26045
+ }
26046
+ case "llm/retry": {
25750
26047
  const data = event.data;
25751
26048
  acc.streaming = "";
25752
26049
  acc.streamingReasoning = "";
@@ -28223,7 +28520,7 @@ function cacheHitPercent(usage) {
28223
28520
  return usage.inputTokens === 0 ? null : Math.round(usage.cacheReadTokens / usage.inputTokens * 1e3) / 10;
28224
28521
  }
28225
28522
  /** Separator between trailing state spans. */
28226
- const STATUS_ITEM_SEPARATOR = " · ";
28523
+ const STATUS_ITEM_SEPARATOR = " | ";
28227
28524
  /** The Codex-style mode cycle hint appended to the permission badge. */
28228
28525
  const STATUS_CYCLE_HINT = " (shift+tab to cycle)";
28229
28526
  /** Occupancy at which the usage readout flips from brand blue to amber. */
@@ -28438,7 +28735,7 @@ function safe(text) {
28438
28735
  /** Dim junction separator span inside a cluster. */
28439
28736
  function sep() {
28440
28737
  return {
28441
- text: " · ",
28738
+ text: " | ",
28442
28739
  tone: "label"
28443
28740
  };
28444
28741
  }
@@ -28469,7 +28766,7 @@ function buildCandidates(facts, stats, busy, enabled, contextWidth) {
28469
28766
  if (model !== "" && enabled.has("model")) {
28470
28767
  const effort = safe(facts.effort ?? stats.reasoningEffort);
28471
28768
  push({
28472
- text: effort === "" ? model : `${model} · ${effort}`,
28769
+ text: effort === "" ? model : `${model} | ${effort}`,
28473
28770
  tone: "model"
28474
28771
  });
28475
28772
  }
@@ -28666,7 +28963,7 @@ function layoutStatusBar(facts, stats, columns, options = {}) {
28666
28963
  const budget = Math.max(1, Math.floor(columns) - WIDTH_SAFETY);
28667
28964
  const maxContextWidth = Math.max(CONTEXT_MIN_WIDTH, Math.min(budget, Math.floor(options.contextWidth ?? 24)));
28668
28965
  const { left, right, badge, row2 } = buildCandidates(facts, stats, busy, enabled, maxContextWidth);
28669
- const groupSeparator = visibleColumns(" | ");
28966
+ const groupSeparator = visibleColumns(" | ");
28670
28967
  const itemSeparator = visibleColumns(STATUS_ITEM_SEPARATOR);
28671
28968
  const position = new Map(items.map((id, index) => [id, index]));
28672
28969
  const byPosition = (a, b) => (position.get(a.id) ?? Number.MAX_SAFE_INTEGER) - (position.get(b.id) ?? Number.MAX_SAFE_INTEGER);
@@ -28767,7 +29064,7 @@ function layoutStatusBar(facts, stats, columns, options = {}) {
28767
29064
  };
28768
29065
  }
28769
29066
  const row2Kept = [...orderedRow2];
28770
- const row2Budget = Math.max(0, budget - 2 - (facts.telemetry ? visibleColumns(facts.telemetry) + 3 : 0));
29067
+ const row2Budget = Math.max(0, budget - 1 - (facts.telemetry ? visibleColumns(facts.telemetry) + 3 : 0));
28771
29068
  const row2Width = () => joinWidth(row2Kept.map((entry) => spansWidth(entry.group.spans)), groupSeparator);
28772
29069
  while (row2Width() > row2Budget && row2Kept.length > 0) {
28773
29070
  let dropIndex = 0;
@@ -31522,6 +31819,7 @@ function readSettledRowCap() {
31522
31819
  const SYNCHRONIZED_UPDATE_END = "\x1B[?2026l";
31523
31820
  /** One source of truth for TUI-owned slash commands in completion and `/help`. */
31524
31821
  const LOCAL_COMMANDS = [
31822
+ { label: "/email", description: "browse email and steer into this session" },
31525
31823
  { label: "/login", description: "save a DeepSeek API key locally" },
31526
31824
  // dscode: startup command discovery
31527
31825
  {"label":"/status","description":"session, model, permissions and usage"},
@@ -31872,8 +32170,10 @@ function lineStyleProps(style) {
31872
32170
  function StyledRows({ lines }) {
31873
32171
  return (0, import_react.createElement)(Box, { flexDirection: "column" }, ...lines.map((line, index) => (0, import_react.createElement)(Text, {
31874
32172
  key: index,
31875
- wrap: "truncate-end"
31876
- }, line.segments.length === 0 ? " " : line.segments.map((segment, at) => (0, import_react.createElement)(Text, {
32173
+ wrap: "truncate-end",
32174
+ backgroundColor: line.background === "user" ? inkColor(getPalette().composerBand) : void 0,
32175
+ color: line.background === "user" ? inkColor(getPalette().text) : void 0
32176
+ }, line.segments.length === 0 ? " " : line.segments.map((segment, at) => (0, import_react.createElement)(Text, {
31877
32177
  key: at,
31878
32178
  ...lineStyleProps(segment.style)
31879
32179
  }, segment.text)))));
@@ -31945,7 +32245,7 @@ function Header({ cwd = "", model = "", effort = "" }) {
31945
32245
  if (!full) return (0, import_react.createElement)(Box, { flexDirection: "column", paddingX: 2, marginBottom: 1 },
31946
32246
  (0, import_react.createElement)(Text, { wrap: "truncate-end" },
31947
32247
  (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandBright), bold: true }, "❄ DSCODE"),
31948
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, " v0.2.0")),
32248
+ (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, " v0.5.0")),
31949
32249
  (0, import_react.createElement)(Text, { wrap: "truncate-end" }, truncateColumns(modelName + " · " + effortName, width)),
31950
32250
  (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim), wrap: "truncate-end" }, welcomePath(cwd, width)));
31951
32251
  return (0, import_react.createElement)(Box, { flexDirection: "column", width, borderStyle: "round", borderColor: inkColor(getPalette().brand), paddingX: 1, marginBottom: 1 },
@@ -31964,7 +32264,7 @@ function Header({ cwd = "", model = "", effort = "" }) {
31964
32264
  (0, import_react.createElement)(Box, { flexDirection: "column", width: detailsWidth, marginTop: 2 },
31965
32265
  (0, import_react.createElement)(Text, { color: inkColor(getPalette().text), bold: true }, "DSCODE"),
31966
32266
  (0, import_react.createElement)(Text, { color: inkColor(getPalette().brandDeep) }, "────────────"),
31967
- (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, "v0.2.0"),
32267
+ (0, import_react.createElement)(Text, { color: inkColor(getPalette().dim) }, "v0.5.0"),
31968
32268
  (0, import_react.createElement)(Text, { wrap: "truncate-end" }, truncateColumns("model " + modelName, detailsWidth)),
31969
32269
  (0, import_react.createElement)(Text, { wrap: "truncate-end" }, truncateColumns("effort " + effortName, detailsWidth)),
31970
32270
  (0, import_react.createElement)(Text, null, " "),
@@ -32175,7 +32475,7 @@ function deepseekWaveHues(tier) {
32175
32475
  function StatusLine({ facts, stats, busy, columns, items }) {
32176
32476
  const [, refreshMetrics] = (0, import_react.useState)(0);
32177
32477
  (0, import_react.useEffect)(() => { const timer = setInterval(() => refreshMetrics(n => n + 1), 1000); return () => clearInterval(timer); }, []);
32178
- facts = { ...facts, telemetry: columns >= 64 ? dscodeFooterFor(facts.fullSessionId, stats, Math.max(1, Math.floor(columns / 2) - 4)) : "" };
32478
+ 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)))) : "" };
32179
32479
  const layout = (0, import_react.useMemo)(() => layoutStatusBar(facts, stats, Math.max(8, columns - 2), {
32180
32480
  busy,
32181
32481
  items,
@@ -32206,7 +32506,7 @@ function StatusLine({ facts, stats, busy, columns, items }) {
32206
32506
  if (groupIndex > 0) leftParts.push((0, import_react.createElement)(Text, {
32207
32507
  key: key + "gs" + groupIndex,
32208
32508
  color: inkColor(getPalette().dim)
32209
- }, " | "));
32509
+ }, " | "));
32210
32510
  group.spans.forEach((span, spanIndex) => {
32211
32511
  leftParts.push((0, import_react.createElement)(Text, {
32212
32512
  key: key + "g" + groupIndex + "s" + spanIndex,
@@ -32216,6 +32516,7 @@ function StatusLine({ facts, stats, busy, columns, items }) {
32216
32516
  });
32217
32517
  });
32218
32518
  const rightParts = [];
32519
+ 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) }, "| "));
32219
32520
  row.right.forEach((span, index) => {
32220
32521
  if (index > 0) rightParts.push((0, import_react.createElement)(Text, {
32221
32522
  key: key + "rs" + index,
@@ -32225,7 +32526,7 @@ function StatusLine({ facts, stats, busy, columns, items }) {
32225
32526
  key: key + "r" + index,
32226
32527
  wrap: "truncate-end",
32227
32528
  ...statusToneProps(span.tone)
32228
- }, span.text));
32529
+ }, key === "s2" && index === 0 ? dscodeTelemetryNodes(span.text, key + "r" + index) : span.text));
32229
32530
  });
32230
32531
  if (row.hint) rightParts.push((0, import_react.createElement)(Text, {
32231
32532
  key: key + "hint",
@@ -33689,7 +33990,7 @@ function HelpPanel({ descriptors, skills, commandError, skillError, onClose }) {
33689
33990
  key: "key-cancel",
33690
33991
  dimColor: true,
33691
33992
  wrap: "truncate-end"
33692
- }, " esc interrupt the running turn · ctrl+c cancel / clear / quit · ctrl+d exit"),
33993
+ }, " ctrl+c stop running turn · ctrl+c again exit · ctrl+d exit"),
33693
33994
  (0, import_react.createElement)(Text, {
33694
33995
  key: "key-queue",
33695
33996
  dimColor: true,
@@ -34209,7 +34510,7 @@ function CompletionMenu({ active, mention, index, rows, error }) {
34209
34510
  * While a modal (approval / question / model panel) owns the keys, the
34210
34511
  * box passes every key through untouched.
34211
34512
  */
34212
- function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, skills, dispatch, steer, interrupt, quit, 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 }) {
34513
+ 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 }) {
34213
34514
  const columns = useStdout().stdout?.columns ?? 80;
34214
34515
  const inputTerminalRows = useStdout().stdout?.rows ?? 30;
34215
34516
  const dscodeImeStdout = useStdout().stdout;
@@ -34226,6 +34527,8 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34226
34527
  const [cursor, setCursor] = (0, import_react.useState)(0);
34227
34528
  const valueRef = (0, import_react.useRef)(value);
34228
34529
  const cursorRef = (0, import_react.useRef)(cursor);
34530
+ const pendingPastesRef = (0, import_react.useRef)(new Map());
34531
+ const ctrlCArmedRef = (0, import_react.useRef)(false);
34229
34532
  valueRef.current = value;
34230
34533
  cursorRef.current = cursor;
34231
34534
  const [draftImages, setDraftImages] = (0, import_react.useState)([]);
@@ -34242,6 +34545,16 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34242
34545
  prepareEpochRef.current += 1;
34243
34546
  prepareAbortRef.current?.abort();
34244
34547
  }, []);
34548
+
34549
+ (0, import_react.useEffect)(() => {
34550
+ if (!emailFill) return;
34551
+ if (emailFill.sessionKey === sessionKey) {
34552
+ const next = valueRef.current + (valueRef.current ? "\n\n" : "") + sanitizeDraftText(emailFill.text);
34553
+ valueRef.current = next; cursorRef.current = next.length;
34554
+ setValue(next); setCursor(next.length); setDismissedMenuValue(void 0);
34555
+ }
34556
+ emailConsumed();
34557
+ }, [emailFill, sessionKey, emailConsumed]);
34245
34558
  const killRef = (0, import_react.useRef)("");
34246
34559
  const preferredColumnRef = (0, import_react.useRef)(null);
34247
34560
  const editorScrollRef = (0, import_react.useRef)(0);
@@ -34259,6 +34572,7 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34259
34572
  (0, import_react.useEffect)(() => {
34260
34573
  if (historyFill === void 0) return;
34261
34574
  const safe = sanitizeDraftText(historyFill.text);
34575
+ pendingPastesRef.current.clear();
34262
34576
  draftImagesRef.current = [];
34263
34577
  setDraftImages([]);
34264
34578
  draftFilesRef.current = [];
@@ -34295,6 +34609,9 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34295
34609
  return next.length === current.length ? current : next;
34296
34610
  });
34297
34611
  }, [value]);
34612
+ (0, import_react.useEffect)(() => {
34613
+ for (const marker of pendingPastesRef.current.keys()) if (!value.includes(marker)) pendingPastesRef.current.delete(marker);
34614
+ }, [value]);
34298
34615
  (0, import_react.useEffect)(() => {
34299
34616
  if (stdin === void 0) return;
34300
34617
  const originalRead = stdin.read.bind(stdin);
@@ -34340,11 +34657,12 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34340
34657
  const uniqueImageMarker = (name, source, reserved = [], kind = "image") => {
34341
34658
  const safeName = singleLineText(sanitizeDraftText(name));
34342
34659
  const label = kind === "file" ? "file" : "image";
34343
- let marker = source === "mention" ? `@${safeName}` : `[${label}: ${safeName}]`;
34660
+ // dscode-image-marker-v1
34661
+ let marker = source === "mention" ? `@${safeName}` : kind === "image" ? "[Image 1]" : `[${label}: ${safeName}]`;
34344
34662
  let suffix = 2;
34345
34663
  const taken = (candidate) => valueRef.current.includes(candidate) || draftImagesRef.current.some((image) => image.marker === candidate) || draftFilesRef.current.some((file) => file.marker === candidate) || reserved.includes(candidate);
34346
34664
  while (taken(marker)) {
34347
- marker = source === "mention" ? `@${safeName} (${suffix})` : `[${label}: ${safeName} ${suffix}]`;
34665
+ marker = source === "mention" ? `@${safeName} (${suffix})` : kind === "image" ? `[Image ${suffix}]` : `[${label}: ${safeName} ${suffix}]`;
34348
34666
  suffix += 1;
34349
34667
  }
34350
34668
  return marker;
@@ -34367,9 +34685,7 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34367
34685
  * durable image blocks, files ride the 0.1.5 file blocks, and both register
34368
34686
  * visible draft markers anchored at the drop point.
34369
34687
  */
34370
- const insertDroppedAttachments = (imagePaths, filePaths) => {
34371
- const originalValue = valueRef.current;
34372
- const originalCursor = cursorRef.current;
34688
+ const insertDroppedAttachments = (imagePaths, filePaths, originalValue = valueRef.current, originalCursor = cursorRef.current) => {
34373
34689
  const total = imagePaths.length + filePaths.length;
34374
34690
  if (total === 0) return;
34375
34691
  notify(`checking ${total} attachment${total === 1 ? "" : "s"}…`);
@@ -34554,6 +34870,7 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34554
34870
  };
34555
34871
  /** Apply one editor edit: draft, cursor, kill buffer, menu reset. */
34556
34872
  const applyEdit = (edit) => {
34873
+ edit = pasteAtomicEdit(valueRef.current, edit, pendingPastesRef.current);
34557
34874
  if (edit.killed !== void 0 && edit.killed !== "") killRef.current = edit.killed;
34558
34875
  valueRef.current = edit.value;
34559
34876
  cursorRef.current = edit.cursor;
@@ -34568,6 +34885,7 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34568
34885
  const moveCursorTo = (next) => {
34569
34886
  resetCursorBlink();
34570
34887
  if (next === cursorRef.current) return;
34888
+ next = pasteCursorEdge(valueRef.current, cursorRef.current, next, pendingPastesRef.current);
34571
34889
  cursorRef.current = next;
34572
34890
  setCursor(next);
34573
34891
  preferredColumnRef.current = null;
@@ -34578,23 +34896,24 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34578
34896
  let nextCursor = cursorRef.current;
34579
34897
  for (const token of tokens) {
34580
34898
  if (token.kind === "text") {
34581
- const edit = insertText(nextValue, nextCursor, token.text);
34899
+ const edit = pasteAtomicEdit(nextValue, insertText(nextValue, nextCursor, collapseLargePaste(token.text, nextValue, pendingPastesRef.current)), pendingPastesRef.current);
34582
34900
  nextValue = edit.value;
34583
34901
  nextCursor = edit.cursor;
34584
34902
  continue;
34585
34903
  }
34586
34904
  if (token.kind === "home") {
34587
- nextCursor = moveToLineStart(nextValue, nextCursor, false);
34905
+ nextCursor = pasteCursorEdge(nextValue, nextCursor, moveToLineStart(nextValue, nextCursor, false), pendingPastesRef.current);
34588
34906
  continue;
34589
34907
  }
34590
34908
  if (token.kind === "end") {
34591
- nextCursor = moveToLineEnd(nextValue, nextCursor, false);
34909
+ nextCursor = pasteCursorEdge(nextValue, nextCursor, moveToLineEnd(nextValue, nextCursor, false), pendingPastesRef.current);
34592
34910
  continue;
34593
34911
  }
34594
34912
  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);
34595
- if (edit.killed !== void 0 && edit.killed !== "") killRef.current = edit.killed;
34596
- nextValue = edit.value;
34597
- nextCursor = edit.cursor;
34913
+ const atomic = pasteAtomicEdit(nextValue, edit, pendingPastesRef.current);
34914
+ if (atomic.killed !== void 0 && atomic.killed !== "") killRef.current = atomic.killed;
34915
+ nextValue = atomic.value;
34916
+ nextCursor = atomic.cursor;
34598
34917
  }
34599
34918
  valueRef.current = nextValue;
34600
34919
  cursorRef.current = nextCursor;
@@ -34621,8 +34940,9 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34621
34940
  const preferred = preferredColumnRef.current ?? caretSite(model, currentCursor).column;
34622
34941
  const next = moveCursorVertically(model, currentCursor, preferred, direction);
34623
34942
  if (next !== currentCursor) {
34624
- cursorRef.current = next;
34625
- setCursor(next);
34943
+ const edge = pasteCursorEdge(currentValue, currentCursor, next, pendingPastesRef.current);
34944
+ cursorRef.current = edge;
34945
+ setCursor(edge);
34626
34946
  resetCursorBlink();
34627
34947
  preferredColumnRef.current = preferred;
34628
34948
  return;
@@ -34643,10 +34963,29 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34643
34963
  resetCursorBlink();
34644
34964
  };
34645
34965
  useStableInput((input, key) => {
34646
- if (!active) return;
34647
- const liveValue = valueRef.current;
34648
- const liveCursor = cursorRef.current;
34649
- if (preparingImages) {
34966
+ if (!active && !(key.ctrl && input === "c")) { ctrlCArmedRef.current = false; return; }
34967
+ const liveValue = valueRef.current;
34968
+ const liveCursor = cursorRef.current;
34969
+ if (key.ctrl && input === "c") {
34970
+ const repeated = ctrlCArmedRef.current;
34971
+ const action = ctrlCAction(ctrlCArmedRef.current, { interrupt, busy, preparingImages, active: active && deleteConfirm === void 0, hasDraft: liveValue !== "" });
34972
+ ctrlCArmedRef.current = action !== "quit";
34973
+ if (action === "quit") quit(repeated);
34974
+ else if (action === "cancel-prepare") cancelImageSubmission();
34975
+ else if (action === "wait" && deleteConfirm !== void 0) cancelDelete();
34976
+ else if (action === "clear-draft") {
34977
+ pendingPastesRef.current.clear();
34978
+ valueRef.current = ""; cursorRef.current = 0;
34979
+ setValue(""); setCursor(0); resetCursorBlink();
34980
+ draftImagesRef.current = []; setDraftImages([]);
34981
+ draftFilesRef.current = []; setDraftFiles([]);
34982
+ setCompletionIndex(0); setDismissedMenuValue(void 0);
34983
+ }
34984
+ return;
34985
+ }
34986
+ ctrlCArmedRef.current = false;
34987
+ if (key.pageUp || key.pageDown) { onTranscriptScroll?.(key.pageUp ? 1 : -1, true); return; }
34988
+ if (preparingImages) {
34650
34989
  if (key.escape || key.ctrl && input === "c") cancelImageSubmission();
34651
34990
  return;
34652
34991
  }
@@ -34669,27 +35008,16 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34669
35008
  toggleReasoning();
34670
35009
  return;
34671
35010
  }
35011
+ if ((key.ctrl || key.meta) && input === "v") {
35012
+ const atValue = liveValue, atCursor = liveCursor;
35013
+ notify("reading clipboard image…");
35014
+ readClipboardImage().then(path => insertDroppedAttachments([path], [], atValue, atCursor), reason => notify(reason instanceof Error ? reason.message : String(reason), "warning"));
35015
+ return;
35016
+ }
34672
35017
  if (key.ctrl && input === "o") {
34673
35018
  openVerbose();
34674
35019
  return;
34675
35020
  }
34676
- if (key.ctrl && input === "c") {
34677
- if (busy) interrupt();
34678
- else if (liveValue !== "") {
34679
- valueRef.current = "";
34680
- cursorRef.current = 0;
34681
- setValue("");
34682
- setCursor(0);
34683
- resetCursorBlink();
34684
- draftImagesRef.current = [];
34685
- setDraftImages([]);
34686
- draftFilesRef.current = [];
34687
- setDraftFiles([]);
34688
- setCompletionIndex(0);
34689
- setDismissedMenuValue(void 0);
34690
- } else quit();
34691
- return;
34692
- }
34693
35021
  if (key.ctrl && input === "d") {
34694
35022
  if (liveValue !== "") {
34695
35023
  applyEdit(deleteForward(liveValue, liveCursor));
@@ -34732,8 +35060,16 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34732
35060
  return;
34733
35061
  }
34734
35062
  }
34735
- const trimmed = liveValue.trim();
34736
- const text = submissionPayload(liveValue);
35063
+ const expandedValue = expandLargePastes(liveValue, pendingPastesRef.current);
35064
+ const trimmed = expandedValue.trim();
35065
+ const text = submissionPayload(expandedValue);
35066
+ if (/^\/email(?:\s|$)/.test(trimmed)) {
35067
+ if (trimmed !== "/email") { notify("Use /email to open the inbox.", "warning"); return; }
35068
+ valueRef.current = ""; cursorRef.current = 0;
35069
+ setValue(""); setCursor(0); setCompletionIndex(0); setDismissedMenuValue(void 0);
35070
+ recall.current = beginRecall(recallSpace, "");
35071
+ openEmail(); return;
35072
+ }
34737
35073
  if (/^\/login(?:\s|$)/.test(trimmed)) {
34738
35074
  valueRef.current = ""; cursorRef.current = 0;
34739
35075
  setValue(""); setCursor(0); setCompletionIndex(0); setDismissedMenuValue(void 0);
@@ -34758,6 +35094,7 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34758
35094
  if (controller.signal.aborted || prepareEpochRef.current !== epoch) return;
34759
35095
  prepareAbortRef.current = void 0;
34760
35096
  setPreparingImages(false);
35097
+ pendingPastesRef.current.clear();
34761
35098
  valueRef.current = "";
34762
35099
  cursorRef.current = 0;
34763
35100
  setValue("");
@@ -34785,6 +35122,7 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34785
35122
  });
34786
35123
  return;
34787
35124
  }
35125
+ pendingPastesRef.current.clear();
34788
35126
  valueRef.current = "";
34789
35127
  cursorRef.current = 0;
34790
35128
  setValue("");
@@ -34831,7 +35169,8 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
34831
35169
  return;
34832
35170
  }
34833
35171
  if (text === "/review" || text.startsWith("/review ")) {
34834
- reviewChanges(text.slice(7));
35172
+ // dscode-review-command-v1
35173
+ dispatch(text);
34835
35174
  return;
34836
35175
  }
34837
35176
  if (text === "/model" || text.startsWith("/model ")) {
@@ -35028,12 +35367,12 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
35028
35367
  clearTimeout(timer);
35029
35368
  pasteBracketCancelRef.current = void 0;
35030
35369
  };
35031
- text = text.replaceAll(PASTE_START_MARKER, "");
35370
+ text = text.replaceAll(`\x1b${PASTE_START_MARKER}`, "").replaceAll(PASTE_START_MARKER, "");
35032
35371
  }
35033
35372
  if (text.includes("[201~")) {
35034
35373
  pasteBracketRef.current = false;
35035
35374
  pasteBracketCancelRef.current?.();
35036
- text = text.replaceAll(PASTE_END_MARKER, "");
35375
+ text = text.replaceAll(`\x1b${PASTE_END_MARKER}`, "").replaceAll(PASTE_END_MARKER, "");
35037
35376
  }
35038
35377
  if (text === "") return;
35039
35378
  if (text.length > 1) {
@@ -35043,9 +35382,9 @@ function Input({ effortSurface, ultraPulse, active, frozen, busy, descriptors, s
35043
35382
  return;
35044
35383
  }
35045
35384
  }
35046
- applyEdit(insertText(valueRef.current, cursorRef.current, text));
35385
+ applyEdit(insertText(valueRef.current, cursorRef.current, collapseLargePaste(text, valueRef.current, pendingPastesRef.current)));
35047
35386
  }
35048
- }, active);
35387
+ }, true);
35049
35388
  const waveKey = waveTier !== null && waveStyle !== null ? `${waveTier}:${waveStyle}` : null;
35050
35389
  const [wavePlayedKey, setWavePlayedKey] = (0, import_react.useState)(null);
35051
35390
  (0, import_react.useEffect)(() => {
@@ -35317,6 +35656,18 @@ function computeSettledRows(previous, entries, settled, showReasoning, resumed,
35317
35656
  }
35318
35657
  /** The whole terminal app; state arrives via the store, output via Ink. */
35319
35658
  function App(props) {
35659
+ const gmail = (0, import_react.useMemo)(() => dscodeCreateGmailConnector(), []);
35660
+ const imap = (0, import_react.useMemo)(() => dscodeCreateImapConnector(), []);
35661
+ (0, import_react.useEffect)(() => {
35662
+ const controller = new AbortController();
35663
+ const sync = () => (imap.status().connected ? imap : gmail).sync({ signal: controller.signal }).catch(() => {});
35664
+ sync(); const timer = setInterval(sync, 30000);
35665
+ return () => { clearInterval(timer); controller.abort(); };
35666
+ }, [gmail, imap]);
35667
+ const [emailOpen, setEmailOpen] = (0, import_react.useState)(false);
35668
+ const [emailFill, setEmailFill] = (0, import_react.useState)(void 0);
35669
+ const emailConsumed = (0, import_react.useCallback)(() => setEmailFill(void 0), []);
35670
+ (0, import_react.useEffect)(() => { setEmailOpen(false); setEmailFill(void 0); }, [props.sessionKey]);
35320
35671
  const view = (0, import_react.useSyncExternalStore)(props.store.subscribe, props.store.getView);
35321
35672
  useStableInput(() => {}, true);
35322
35673
  const readDescriptors = (0, import_react.useCallback)(() => props.commands.descriptors, [props.commands]);
@@ -35554,9 +35905,10 @@ function App(props) {
35554
35905
  const agentRows = (0, import_react.useSyncExternalStore)(props.subagents.subscribe, props.subagents.getSnapshot);
35555
35906
  const approvalPending = approvalSnapshot.pending !== void 0;
35556
35907
  const questionPending = questionSnapshot.pending !== void 0;
35557
- const inputActive = deleteConfirmId !== void 0 ? !approvalPending && !questionPending : !modelOpen && !helpOpen && !modeOpen && !permissionOpen && !resumeOpen && !pluginOpen && !jobsOpen && !statuslineOpen && !themeOpen && !historyOpen && !agentsOpen && !subagentOpen && !todosOpen && !verboseOpen && diffView === void 0 && !approvalPending && !questionPending;
35908
+ const inputActive = deleteConfirmId !== void 0 ? !approvalPending && !questionPending : !emailOpen && !modelOpen && !helpOpen && !modeOpen && !permissionOpen && !resumeOpen && !pluginOpen && !jobsOpen && !statuslineOpen && !themeOpen && !historyOpen && !agentsOpen && !subagentOpen && !todosOpen && !verboseOpen && diffView === void 0 && !approvalPending && !questionPending;
35558
35909
  (0, import_react.useEffect)(() => {
35559
35910
  if (!approvalPending && !questionPending) return;
35911
+ setEmailOpen(false);
35560
35912
  setModelOpen(false);
35561
35913
  setProviderOpen(false);
35562
35914
  setProviderAction(void 0);
@@ -35619,21 +35971,48 @@ function App(props) {
35619
35971
  setMenuRows((current) => current === rows ? current : rows);
35620
35972
  }, []);
35621
35973
  const composerEditorCap = composerMaxRows(terminalRows);
35622
- const transcriptVisible = !modelOpen && !helpOpen && !modeOpen && !permissionOpen && !resumeOpen && !pluginOpen && !jobsOpen && !statuslineOpen && !themeOpen && !historyOpen && !agentsOpen && !subagentOpen && !todosOpen && !verboseOpen && diffView === void 0 && !approvalPending && !questionPending;
35974
+ const transcriptVisible = !emailOpen && !modelOpen && !helpOpen && !modeOpen && !permissionOpen && !resumeOpen && !pluginOpen && !jobsOpen && !statuslineOpen && !themeOpen && !historyOpen && !agentsOpen && !subagentOpen && !todosOpen && !verboseOpen && diffView === void 0 && !approvalPending && !questionPending;
35623
35975
  const welcomeFull = terminalRows >= 24 && terminalColumns >= 64;
35624
- const welcomeChromeRows = welcomeFull ? 22 : terminalRows >= 10 ? 13 : 10;
35625
- const settledBudget = transcriptVisible ? Math.max(0, terminalRows - welcomeChromeRows - composerGutterRows - (composerRows - 1) - menuRows) : 0;
35626
- const settledViewportRows = busy || view.streaming !== "" ? Math.floor(settledBudget / 3) : settledBudget;
35627
- const renderedSettled = (0, import_react.useMemo)(() => visibleSettledLines(view.entries, settled, settledViewportRows, terminalColumns, showReasoning, settledEntryLines), [settled, view.entries[settled - 1], settledViewportRows, terminalColumns, showReasoning]);
35976
+ const welcomeMaxRows = welcomeFull ? 13 : terminalRows >= 10 ? 4 : 1;
35977
+ // The nine fixed rows belong to the composer, footer and their gutters.
35978
+ const transcriptCapacity = transcriptVisible ? Math.max(0, terminalRows - 9 - composerGutterRows - (composerRows - 1) - menuRows) : 0;
35979
+ const streamingActive = view.streaming !== "";
35980
+ const deepDivingVisible = busy;
35981
+ const allLiveLines = (0, import_react.useMemo)(() => view.entries.slice(settled).flatMap((entry) => dscodeChatLines(entry, Math.max(1, terminalColumns - 2))), [
35982
+ view.entries,
35983
+ settled,
35984
+ terminalColumns,
35985
+ showReasoning
35986
+ ]);
35987
+ const allSettledLines = (0, import_react.useMemo)(() => visibleSettledLines(view.entries, settled, Infinity, terminalColumns, showReasoning, settledEntryLines), [settled, view.entries[settled - 1], terminalColumns, showReasoning]);
35988
+ const settledTail = allSettledLines.slice(-transcriptCapacity);
35989
+ const streamingDemand = streamingActive ? textLines(view.streaming.slice(-Math.max(1, terminalColumns * transcriptCapacity)), Math.max(10, terminalColumns - 2)).length : 0;
35990
+ const demand = settledTail.length + allLiveLines.length + streamingDemand + (busy ? 1 : 0) + (agentRows.length > 0 ? 1 : 0);
35991
+ const welcomeRows = welcomeVisibleRows(transcriptCapacity, welcomeMaxRows, demand, transcriptVisible);
35992
+ const settledBudget = transcriptVisible ? Math.max(0, transcriptCapacity - welcomeRows) : 0;
35993
+ const settledViewportRows = busy || streamingActive ? Math.floor(settledBudget / 3) : settledBudget;
35994
+ const [scrollOffset, setScrollOffset] = (0, import_react.useState)(0);
35995
+ const previousHistory = (0, import_react.useRef)({ sessionKey: props.sessionKey, count: allSettledLines.length });
35996
+ const sameHistory = previousHistory.current.sessionKey === props.sessionKey;
35997
+ const growth = sameHistory ? Math.max(0, allSettledLines.length - previousHistory.current.count) : 0;
35998
+ const maxScrollOffset = Math.max(0, allSettledLines.length - settledViewportRows);
35999
+ const effectiveScrollOffset = sameHistory ? Math.min(maxScrollOffset, scrollOffset === 0 ? 0 : scrollOffset + growth) : 0;
36000
+ (0, import_react.useEffect)(() => {
36001
+ previousHistory.current = { sessionKey: props.sessionKey, count: allSettledLines.length };
36002
+ if (scrollOffset !== effectiveScrollOffset) setScrollOffset(effectiveScrollOffset);
36003
+ }, [props.sessionKey, allSettledLines.length, effectiveScrollOffset]);
36004
+ const scrollTranscript = (direction, page = false) => {
36005
+ if (!transcriptVisible || settledViewportRows <= 0) return;
36006
+ const step = page ? Math.max(1, settledViewportRows - 1) : 3;
36007
+ setScrollOffset(current => Math.max(0, Math.min(maxScrollOffset, (current === scrollOffset ? effectiveScrollOffset : current) + direction * step)));
36008
+ };
36009
+ (0, import_react.useEffect)(() => {
36010
+ const listener = direction => scrollTranscript(direction);
36011
+ dscodeWheelListeners.add(listener);
36012
+ return () => dscodeWheelListeners.delete(listener);
36013
+ });
36014
+ const renderedSettled = transcriptWindow(allSettledLines, settledViewportRows, effectiveScrollOffset);
35628
36015
  const dynamicRows = Math.max(0, settledBudget - settledViewportRows);
35629
- const streamingActive = view.streaming !== "";
35630
- const deepDivingVisible = busy;
35631
- const allLiveLines = (0, import_react.useMemo)(() => view.entries.slice(settled).flatMap((entry) => dscodeChatLines(entry, Math.max(1, terminalColumns - 2))), [
35632
- view.entries,
35633
- settled,
35634
- terminalColumns,
35635
- showReasoning
35636
- ]);
35637
36016
  const liveBudget = dynamicRows === 0 ? 0 : busy || streamingActive ? Math.max(1, Math.floor(dynamicRows / 3)) : Math.max(0, dynamicRows - (deepDivingVisible ? 1 : 0));
35638
36017
  const visibleLiveLines = liveBudget === 0 ? [] : allLiveLines.slice(-liveBudget);
35639
36018
  const streamRows = Math.max(1, dynamicRows - visibleLiveLines.length);
@@ -35652,7 +36031,7 @@ function App(props) {
35652
36031
  const auditedReasoningRows = liveAudit.allocation.reasoning;
35653
36032
  const auditedAnswerRows = liveAudit.allocation.answer;
35654
36033
 
35655
- const modalVisible = modelOpen || helpOpen || modeOpen || permissionOpen || resumeOpen || pluginOpen || jobsOpen || statuslineOpen || themeOpen || historyOpen || agentsOpen || subagentOpen || todosOpen || verboseOpen && !approvalPending && !questionPending || diffView !== void 0 || approvalPending || questionPending;
36034
+ const modalVisible = emailOpen || modelOpen || helpOpen || modeOpen || permissionOpen || resumeOpen || pluginOpen || jobsOpen || statuslineOpen || themeOpen || historyOpen || agentsOpen || subagentOpen || todosOpen || verboseOpen && !approvalPending && !questionPending || diffView !== void 0 || approvalPending || questionPending;
35656
36035
  const closeInspector = (0, import_react.useCallback)(() => {
35657
36036
  setVerboseOpen(false);
35658
36037
  }, []);
@@ -35901,7 +36280,13 @@ function App(props) {
35901
36280
  });
35902
36281
  }
35903
36282
  return (0, import_react.createElement)(Box, { flexDirection: "column", height: Math.max(1, terminalRows - 1) },
35904
- 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"),
36283
+ welcomeRows > 0 ? (0, import_react.createElement)(Box, { height: welcomeRows, overflowY: "hidden", flexDirection: "column", justifyContent: "flex-end", flexShrink: 0 },
36284
+ (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,
36285
+ emailOpen && !approvalPending && !questionPending ? (0, import_react.createElement)(DscodeEmailPanel, {
36286
+ gmail, imap, columns: terminalColumns, rows: Math.max(3, terminalRows - 8 - composerGutterRows - composerRows),
36287
+ close: () => setEmailOpen(false),
36288
+ pick: mail => { props.steer(dscodeEmailPrompt(mail), [], props.sessionKey); setEmailOpen(false); }
36289
+ }) : void 0,
35905
36290
  transcriptVisible && settledViewportRows > 0 ? (0, import_react.createElement)(StyledRows, { lines: renderedSettled }) : void 0,
35906
36291
  transcriptVisible ? (0, import_react.createElement)(Box, { flexDirection: "column" }, auditedLiveLines.length === 0 ? void 0 : (0, import_react.createElement)(StyledRows, { lines: auditedLiveLines }), view.streamingReasoning !== "" && auditedReasoningRows > 0 ? showReasoning ? (0, import_react.createElement)(StreamTail, {
35907
36292
  text: view.streamingReasoning,
@@ -36067,7 +36452,9 @@ function App(props) {
36067
36452
  steer: props.steer,
36068
36453
  interrupt: props.interrupt,
36069
36454
  quit: props.quit,
36070
- openLogin: () => {
36455
+ openEmail: () => setEmailOpen(true),
36456
+ emailFill, emailConsumed,
36457
+ openLogin: () => {
36071
36458
  setProviderOpen(false); setEffortFor(void 0);
36072
36459
  setProviderAction({ kind: "dscode-key" }); setModelOpen(true);
36073
36460
  },
@@ -36187,7 +36574,8 @@ function App(props) {
36187
36574
  waveStyle,
36188
36575
  maxRows: composerEditorCap,
36189
36576
  onEditorRows: handleEditorRows,
36190
- onMenuRows: handleMenuRows
36577
+ onMenuRows: handleMenuRows,
36578
+ onTranscriptScroll: scrollTranscript
36191
36579
  }), (0, import_react.createElement)(StatusLine, {
36192
36580
  facts: {
36193
36581
  fullSessionId: props.sessionKey,
@@ -36420,7 +36808,7 @@ function createSplitStdin(source) {
36420
36808
  stalePasteTimer.unref?.();
36421
36809
  };
36422
36810
  const onChunk = (chunk) => {
36423
- for (const unit of splitter.push(String(chunk))) stream.write(unit);
36811
+ for (const unit of splitter.push(String(chunk))) if (!dscodeHandleMouseUnit(unit)) stream.write(unit);
36424
36812
  if (splitter.openPaste()) armStalePasteTimer();
36425
36813
  else disarmStalePasteTimer();
36426
36814
  };
@@ -36465,7 +36853,7 @@ const internals = {
36465
36853
  if (process.stdin.isTTY === true) process.stdin.setRawMode?.(true);
36466
36854
  try {
36467
36855
  if (process.stdout.isTTY === true) process.stdout.write("\x1B[r\x1B[0m\x1B[H\x1B[2J\x1B[3J\x1B[H");
36468
- process.stdout.write((keyboardEnhanced ? KEYBOARD_ENHANCE_ENABLE : "") + BRACKETED_PASTE_ENABLE + (focusReporting ? TERMINAL_FOCUS_REPORT_ENABLE : ""));
36856
+ process.stdout.write((keyboardEnhanced ? KEYBOARD_ENHANCE_ENABLE : "") + BRACKETED_PASTE_ENABLE + (focusReporting ? TERMINAL_FOCUS_REPORT_ENABLE : "") + (process.stdout.isTTY === true ? DSCODE_MOUSE_ENABLE : ""));
36469
36857
  const tuiStdin = createSplitStdin(process.stdin);
36470
36858
  const instance = render(element, {
36471
36859
  exitOnCtrlC: false,
@@ -36478,7 +36866,7 @@ const internals = {
36478
36866
  },
36479
36867
  unmount() {
36480
36868
  try {
36481
- process.stdout.write((keyboardEnhanced ? KEYBOARD_ENHANCE_DISABLE : "") + BRACKETED_PASTE_DISABLE + (focusReporting ? TERMINAL_FOCUS_REPORT_DISABLE : ""));
36869
+ process.stdout.write((keyboardEnhanced ? KEYBOARD_ENHANCE_DISABLE : "") + BRACKETED_PASTE_DISABLE + (focusReporting ? TERMINAL_FOCUS_REPORT_DISABLE : "") + (process.stdout.isTTY === true ? DSCODE_MOUSE_DISABLE : ""));
36482
36870
  } finally {
36483
36871
  try {
36484
36872
  instance.unmount();
@@ -36490,6 +36878,7 @@ const internals = {
36490
36878
  }
36491
36879
  };
36492
36880
  } catch (error) {
36881
+ if (process.stdout.isTTY === true) process.stdout.write(DSCODE_MOUSE_DISABLE);
36493
36882
  if (process.stdin.isTTY === true) process.stdin.setRawMode?.(false);
36494
36883
  throw error;
36495
36884
  }
@@ -38556,7 +38945,7 @@ async function run(ctx, startup, io) {
38556
38945
  };
38557
38946
  const mountRef = {};
38558
38947
  let quitting = false;
38559
- const quit = () => {
38948
+ const quit = (fast = false) => {
38560
38949
  if (quitting) return;
38561
38950
  quitting = true;
38562
38951
  switchQueue.cancel();
@@ -38576,6 +38965,7 @@ async function run(ctx, startup, io) {
38576
38965
  } catch (error) {
38577
38966
  report("unmount", error);
38578
38967
  }
38968
+ if (fast) setTimeout(() => process.exit(0), 250);
38579
38969
  runQuitSequence([
38580
38970
  ...currentSession === void 0 || currentActive === void 0 ? [] : [{
38581
38971
  name: "flush",
@@ -39283,6 +39673,7 @@ async function run(ctx, startup, io) {
39283
39673
  openAuthorizationUrl,
39284
39674
  copyTextValue: copyText,
39285
39675
  loadMentions: (query, signal) => mentions.candidates(query, signal),
39676
+ readClipboardImage: dscodeReadClipboardImage,
39286
39677
  inspectImages: (paths) => inspectImagePaths(paths, ctx.get("attachments"), session?.header.cwd ?? cwd),
39287
39678
  prepareImages: (paths, signal) => saveImagePaths(paths, ctx.get("attachments"), signal),
39288
39679
  inspectFiles: (paths) => inspectFilePaths(paths, ctx.get("attachments"), session?.header.cwd ?? cwd),