dsh-ssh-tui 0.7.1 → 0.7.3
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.
- package/README.en.md +92 -8
- package/README.md +64 -8
- package/cordis.patch.yml +15 -0
- package/docs/terminals.md +113 -0
- package/lib/approval-cache.js +12 -9
- package/lib/approval-cache.js.map +1 -1
- package/lib/color-depth.js +10 -4
- package/lib/color-depth.js.map +1 -1
- package/lib/diag.js +76 -17
- package/lib/diag.js.map +1 -1
- package/lib/dialogs.js.map +1 -1
- package/lib/display-sock.js +304 -29
- package/lib/display-sock.js.map +1 -1
- package/lib/doctor.js +71 -37
- package/lib/doctor.js.map +1 -1
- package/lib/dsh-compat.js +206 -88
- package/lib/dsh-compat.js.map +1 -1
- package/lib/footer.js +39 -9
- package/lib/footer.js.map +1 -1
- package/lib/gateway-protocol.js +202 -0
- package/lib/gateway-protocol.js.map +1 -0
- package/lib/i18n/en.js +79 -41
- package/lib/i18n/en.js.map +1 -1
- package/lib/i18n/index.js +13 -7
- package/lib/i18n/index.js.map +1 -1
- package/lib/i18n/zh.js +79 -41
- package/lib/i18n/zh.js.map +1 -1
- package/lib/index.js +132 -31
- package/lib/index.js.map +1 -1
- package/lib/job-label.js +99 -17
- package/lib/job-label.js.map +1 -1
- package/lib/paint.js +5 -2
- package/lib/paint.js.map +1 -1
- package/lib/picker.js +5 -3
- package/lib/picker.js.map +1 -1
- package/lib/plan.js +269 -17
- package/lib/plan.js.map +1 -1
- package/lib/platform.js +373 -0
- package/lib/platform.js.map +1 -0
- package/lib/preset-authoring.js +10 -14
- package/lib/preset-authoring.js.map +1 -1
- package/lib/preset-compat.js +100 -0
- package/lib/preset-compat.js.map +1 -0
- package/lib/preset-picker.js +5 -1
- package/lib/preset-picker.js.map +1 -1
- package/lib/preset-rows.js +83 -13
- package/lib/preset-rows.js.map +1 -1
- package/lib/provider-catalog.js +7 -4
- package/lib/provider-catalog.js.map +1 -1
- package/lib/route-memory.js +11 -4
- package/lib/route-memory.js.map +1 -1
- package/lib/session-index.js +5 -0
- package/lib/session-index.js.map +1 -1
- package/lib/session-list.js +18 -1
- package/lib/session-list.js.map +1 -1
- package/lib/session-lock.js +79 -24
- package/lib/session-lock.js.map +1 -1
- package/lib/session-route.js +334 -0
- package/lib/session-route.js.map +1 -0
- package/lib/settings-routes.js +10 -0
- package/lib/settings-routes.js.map +1 -0
- package/lib/settings-subagent.js +10 -0
- package/lib/settings-subagent.js.map +1 -0
- package/lib/subagent-model.js +84 -6
- package/lib/subagent-model.js.map +1 -1
- package/lib/supergrok-token.js +4 -0
- package/lib/supergrok-token.js.map +1 -1
- package/lib/term-text.js +7 -4
- package/lib/term-text.js.map +1 -1
- package/lib/terminal-caps.js +358 -0
- package/lib/terminal-caps.js.map +1 -0
- package/lib/tool-present.js +46 -14
- package/lib/tool-present.js.map +1 -1
- package/lib/tui.js +1949 -371
- package/lib/tui.js.map +1 -1
- package/lib/types/color-depth.d.ts +3 -3
- package/lib/types/diag.d.ts +18 -1
- package/lib/types/dialogs.d.ts +4 -0
- package/lib/types/display-sock.d.ts +94 -2
- package/lib/types/doctor.d.ts +8 -1
- package/lib/types/dsh-compat.d.ts +141 -44
- package/lib/types/footer.d.ts +21 -5
- package/lib/types/gateway-protocol.d.ts +88 -0
- package/lib/types/i18n/index.d.ts +18 -12
- package/lib/types/index.d.ts +45 -0
- package/lib/types/job-label.d.ts +38 -7
- package/lib/types/picker.d.ts +3 -0
- package/lib/types/plan.d.ts +94 -4
- package/lib/types/platform.d.ts +215 -0
- package/lib/types/preset-authoring.d.ts +8 -14
- package/lib/types/preset-compat.d.ts +58 -0
- package/lib/types/preset-picker.d.ts +1 -1
- package/lib/types/preset-rows.d.ts +51 -9
- package/lib/types/route-memory.d.ts +17 -22
- package/lib/types/session-list.d.ts +5 -0
- package/lib/types/session-lock.d.ts +9 -0
- package/lib/types/session-route.d.ts +191 -0
- package/lib/types/settings-routes.d.ts +16 -0
- package/lib/types/settings-subagent.d.ts +24 -0
- package/lib/types/subagent-model.d.ts +60 -7
- package/lib/types/term-text.d.ts +3 -2
- package/lib/types/terminal-caps.d.ts +105 -0
- package/lib/types/tool-present.d.ts +2 -17
- package/lib/types/transcript-types.d.ts +44 -1
- package/lib/types/tui.d.ts +333 -9
- package/package.json +82 -55
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the terminal in front of us can actually do.
|
|
3
|
+
*
|
|
4
|
+
* `color-depth.ts` answers the palette question; this answers the rest of the
|
|
5
|
+
* ones that change what we may safely emit: mouse reporting, bracketed paste,
|
|
6
|
+
* the alternate screen, clipboard writes (OSC 52) and hyperlinks (OSC 8), plus
|
|
7
|
+
* which family of terminal we are talking to.
|
|
8
|
+
*
|
|
9
|
+
* Why it is a module and not a few `if`s: these decisions are made from
|
|
10
|
+
* environment variables that nobody on the team can test by hand for every
|
|
11
|
+
* desktop — GNOME Terminal, XFCE Terminal, Konsole, xterm, tmux, the Linux
|
|
12
|
+
* virtual console, Windows Terminal, a legacy Windows console. So the input is
|
|
13
|
+
* a parameter, the table is data, and `tests/terminal-caps.test.mjs` pins one
|
|
14
|
+
* fixture per family on any platform.
|
|
15
|
+
*
|
|
16
|
+
* The bias is **not** "assume the best". A capability we claim but the terminal
|
|
17
|
+
* lacks costs the user something real: `?1000h` on a console that cannot report
|
|
18
|
+
* SGR coordinates swallows its mouse; OSC 52 where nothing reads it makes
|
|
19
|
+
* `/copy` look like it worked; the alternate screen on a serial console loses
|
|
20
|
+
* the scrollback. Where a terminal is known to be marginal, the answer is no,
|
|
21
|
+
* and `DSH_TUI_TERM_CAPS` is the escape hatch for the user who knows better.
|
|
22
|
+
*
|
|
23
|
+
* @module dsh-ssh-tui/terminal-caps
|
|
24
|
+
*/
|
|
25
|
+
import { colorDepth } from './color-depth.js';
|
|
26
|
+
/** Capability names a user may force on or off through `DSH_TUI_TERM_CAPS`. */
|
|
27
|
+
const TOGGLEABLE = ['mouse', 'mouseSgr', 'mouseDrag', 'bracketedPaste', 'alternateScreen', 'osc52', 'osc8', 'title'];
|
|
28
|
+
const TRUTHY = new Set(['1', 'true', 'on', 'yes']);
|
|
29
|
+
const FALSY = new Set(['0', 'false', 'off', 'no']);
|
|
30
|
+
/**
|
|
31
|
+
* Parse `DSH_TUI_TERM_CAPS`.
|
|
32
|
+
*
|
|
33
|
+
* `no-mouse`, `mouse`, `mouse=false` and `mouse = false` all mean what they
|
|
34
|
+
* look like: the `=` binds first, so the spaced spelling cannot silently invert
|
|
35
|
+
* into the opposite answer (it did, and `osc52 = false` then *re-enabled* the
|
|
36
|
+
* clipboard promise). A value outside the two vocabularies, an unknown name or a
|
|
37
|
+
* bare `no-` is rejected rather than guessed, and the rejects are returned so
|
|
38
|
+
* `/diag` can show them — this variable is the only feedback channel a user has
|
|
39
|
+
* when an override does not appear to work.
|
|
40
|
+
*/
|
|
41
|
+
export function parseCapsOverrideReport(raw) {
|
|
42
|
+
const overrides = {};
|
|
43
|
+
const ignored = [];
|
|
44
|
+
// Bind `=` before splitting on whitespace: `mouse = false` must not become the
|
|
45
|
+
// bare name `mouse` plus two junk tokens, which read as "on".
|
|
46
|
+
for (const piece of raw.replace(/\s*=\s*/gu, '=').split(/[\s,]+/u)) {
|
|
47
|
+
const token = piece.trim();
|
|
48
|
+
if (token === '')
|
|
49
|
+
continue;
|
|
50
|
+
const [rawName, rawValue] = token.split('=');
|
|
51
|
+
const name = (rawName ?? '').trim();
|
|
52
|
+
const value = rawValue === undefined ? undefined : rawValue.trim().toLowerCase();
|
|
53
|
+
const negated = name.startsWith('no-');
|
|
54
|
+
const key = (negated ? name.slice(3) : name);
|
|
55
|
+
const known = name !== 'no-' && TOGGLEABLE.includes(key);
|
|
56
|
+
if (!known) {
|
|
57
|
+
ignored.push(token);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (negated) {
|
|
61
|
+
// `no-mouse=false` is not a spelling worth guessing at.
|
|
62
|
+
if (value !== undefined) {
|
|
63
|
+
ignored.push(token);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
overrides[key] = false;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (value === undefined || TRUTHY.has(value)) {
|
|
70
|
+
overrides[key] = true;
|
|
71
|
+
// Asking for the mouse asks for the whole set the table gives a mouse
|
|
72
|
+
// terminal: SGR (the only encoding this parser reads, so a bare `mouse`
|
|
73
|
+
// would otherwise be cleared by the cascade and appear to do nothing) and
|
|
74
|
+
// held-button motion, without which a drag never arrives. `no-mouseDrag`
|
|
75
|
+
// on its own still opts out of the drag.
|
|
76
|
+
if (key === 'mouse' || key === 'mouseSgr') {
|
|
77
|
+
overrides.mouse = true;
|
|
78
|
+
overrides.mouseSgr = true;
|
|
79
|
+
if (overrides.mouseDrag === undefined)
|
|
80
|
+
overrides.mouseDrag = true;
|
|
81
|
+
}
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (FALSY.has(value)) {
|
|
85
|
+
overrides[key] = false;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
ignored.push(token);
|
|
89
|
+
}
|
|
90
|
+
return { overrides, ignored };
|
|
91
|
+
}
|
|
92
|
+
/** The overrides alone, for callers that do not report. */
|
|
93
|
+
export function parseCapsOverride(raw) {
|
|
94
|
+
return parseCapsOverrideReport(raw).overrides;
|
|
95
|
+
}
|
|
96
|
+
/** A version-ish number out of `VTE_VERSION` / `KONSOLE_VERSION`, or undefined. */
|
|
97
|
+
function numericVersion(raw) {
|
|
98
|
+
const text = (raw ?? '').trim();
|
|
99
|
+
if (text === '')
|
|
100
|
+
return undefined;
|
|
101
|
+
const match = /^(\d+)/u.exec(text);
|
|
102
|
+
return match === null ? undefined : Number(match[1]);
|
|
103
|
+
}
|
|
104
|
+
/** `true`/`false` from a boolean-ish env value, or undefined when unset. */
|
|
105
|
+
function envFlag(raw) {
|
|
106
|
+
const text = (raw ?? '').trim().toLowerCase();
|
|
107
|
+
if (text === '')
|
|
108
|
+
return undefined;
|
|
109
|
+
if (['1', 'true', 'on', 'yes'].includes(text))
|
|
110
|
+
return true;
|
|
111
|
+
if (['0', 'false', 'off', 'no'].includes(text))
|
|
112
|
+
return false;
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Classify the terminal from the environment.
|
|
117
|
+
*
|
|
118
|
+
* Detection is by the markers terminals actually set: Windows Terminal exports
|
|
119
|
+
* `WT_SESSION`; the VTE family (GNOME Terminal, XFCE Terminal, MATE, Tilix,
|
|
120
|
+
* Terminator, Guake) exports `VTE_VERSION`; Konsole exports `KONSOLE_VERSION`;
|
|
121
|
+
* tmux and screen export `TMUX` / `STY` and set a `tmux*` / `screen*` TERM.
|
|
122
|
+
* Everything else falls back to the TERM name.
|
|
123
|
+
*/
|
|
124
|
+
export function detectTerminalFamily(probe = {}) {
|
|
125
|
+
const env = probe.env ?? process.env;
|
|
126
|
+
const platform = probe.platform ?? process.platform;
|
|
127
|
+
const term = (env.TERM ?? '').trim().toLowerCase();
|
|
128
|
+
if ((env.TMUX ?? '') !== '' || term.startsWith('tmux')) {
|
|
129
|
+
return { family: 'tmux', label: `tmux (TERM=${term || 'unset'})` };
|
|
130
|
+
}
|
|
131
|
+
if ((env.STY ?? '') !== '' || term.startsWith('screen')) {
|
|
132
|
+
return { family: 'screen', label: `screen (TERM=${term || 'unset'})` };
|
|
133
|
+
}
|
|
134
|
+
if (term === 'dumb')
|
|
135
|
+
return { family: 'dumb', label: 'dumb terminal (assuming the xterm baseline)' };
|
|
136
|
+
// `linux` (and the old VT names) are POSIX console names: on Windows they can
|
|
137
|
+
// only come from a wrapper, and the capabilities they describe — no mouse, no
|
|
138
|
+
// paste, no alternate screen — are not what the ConPTY behind that wrapper
|
|
139
|
+
// has. The Windows classification below decides instead.
|
|
140
|
+
if (platform !== 'win32' && (term === 'linux' || term === 'vt100' || term === 'vt220')) {
|
|
141
|
+
return { family: 'linux-console', label: 'Linux virtual console' };
|
|
142
|
+
}
|
|
143
|
+
const vte = numericVersion(env.VTE_VERSION);
|
|
144
|
+
if (vte !== undefined) {
|
|
145
|
+
// VTE is the widget, not the app: name the app when it says so.
|
|
146
|
+
const program = (env.TERM_PROGRAM ?? '').trim();
|
|
147
|
+
const app = program !== '' ? program : 'GNOME/XFCE/Fork (VTE)';
|
|
148
|
+
return { family: 'vte', label: `${app} (VTE ${vte})` };
|
|
149
|
+
}
|
|
150
|
+
const konsole = numericVersion(env.KONSOLE_VERSION);
|
|
151
|
+
if (konsole !== undefined)
|
|
152
|
+
return { family: 'konsole', label: `Konsole (${konsole})` };
|
|
153
|
+
if (platform === 'win32') {
|
|
154
|
+
if ((env.WT_SESSION ?? '').trim() !== '') {
|
|
155
|
+
return { family: 'windows-terminal', label: 'Windows Terminal' };
|
|
156
|
+
}
|
|
157
|
+
// An empty TERM is what PowerShell and cmd.exe report: that is conhost, the
|
|
158
|
+
// classic console host, and it is the one Windows terminal without OSC 52.
|
|
159
|
+
// A *named* TERM means something else is driving the console — Git-Bash and
|
|
160
|
+
// MSYS2 set `xterm-256color`, mintty identifies itself the same way — and
|
|
161
|
+
// those are xterm-family terminals that do more than conhost, so they fall
|
|
162
|
+
// through to the TERM matching below rather than being read as conhost.
|
|
163
|
+
if (term === '')
|
|
164
|
+
return { family: 'windows-console', label: 'Windows console (conhost)' };
|
|
165
|
+
}
|
|
166
|
+
if (term === '') {
|
|
167
|
+
return platform === 'win32'
|
|
168
|
+
? { family: 'windows-console', label: 'Windows console (conhost)' }
|
|
169
|
+
: { family: 'unknown', label: 'unknown (TERM unset)' };
|
|
170
|
+
}
|
|
171
|
+
if (term.includes('xterm') || term.includes('rxvt') || term.includes('alacritty') || term.includes('kitty') || term.includes('wezterm')) {
|
|
172
|
+
return { family: 'xterm', label: `${term}` };
|
|
173
|
+
}
|
|
174
|
+
if (platform === 'win32') {
|
|
175
|
+
// A Windows host whose TERM names nothing we know (a wrapper handing us
|
|
176
|
+
// `linux`, or anything else): conhost is what is actually underneath, and
|
|
177
|
+
// `unknown` would claim bracketed paste and hyperlinks this console lacks.
|
|
178
|
+
return { family: 'windows-console', label: 'Windows console (conhost)' };
|
|
179
|
+
}
|
|
180
|
+
return { family: 'unknown', label: term };
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Every capability for the terminal in front of us.
|
|
184
|
+
*
|
|
185
|
+
* `DSH_TUI_TERM_CAPS` overrides individual answers (`no-mouse`, `osc52=false`);
|
|
186
|
+
* `DSH_TUI_NO_ALT_SCREEN` and `DSH_TUI_OSC8` keep working because they came
|
|
187
|
+
* first and are documented.
|
|
188
|
+
*/
|
|
189
|
+
export function terminalCapabilities(probe = {}) {
|
|
190
|
+
const env = probe.env ?? process.env;
|
|
191
|
+
const platform = probe.platform ?? process.platform;
|
|
192
|
+
const { family, label } = detectTerminalFamily({ env, platform });
|
|
193
|
+
const colors = colorDepth(env, platform);
|
|
194
|
+
const base = {
|
|
195
|
+
// A terminal that paints nothing is not a terminal we may drive.
|
|
196
|
+
mouse: false,
|
|
197
|
+
mouseSgr: false,
|
|
198
|
+
mouseDrag: false,
|
|
199
|
+
bracketedPaste: false,
|
|
200
|
+
alternateScreen: false,
|
|
201
|
+
osc52: false,
|
|
202
|
+
osc8: false,
|
|
203
|
+
title: false,
|
|
204
|
+
};
|
|
205
|
+
switch (family) {
|
|
206
|
+
case 'windows-terminal':
|
|
207
|
+
Object.assign(base, {
|
|
208
|
+
mouse: true, mouseSgr: true, mouseDrag: true,
|
|
209
|
+
bracketedPaste: true, alternateScreen: true,
|
|
210
|
+
// Windows Terminal understands OSC 52 from 1.15 on; it is the only
|
|
211
|
+
// Windows host that does, conhost never has. Claiming it there is what
|
|
212
|
+
// makes `/copy` silently useless on the others.
|
|
213
|
+
osc52: true, osc8: true, title: true,
|
|
214
|
+
});
|
|
215
|
+
break;
|
|
216
|
+
case 'windows-console':
|
|
217
|
+
Object.assign(base, {
|
|
218
|
+
// conhost has had VT mouse input since Windows 10 1703, and mouse
|
|
219
|
+
// reporting is how cards expand, so it stays on — the same trade every
|
|
220
|
+
// other platform makes. What it genuinely lacks is the OSC pair:
|
|
221
|
+
// conhost never writes the clipboard and ignores hyperlinks, which is
|
|
222
|
+
// what `/copy` has to say out loud there.
|
|
223
|
+
mouse: true, mouseSgr: true, mouseDrag: true,
|
|
224
|
+
// Bracketed paste reached conhost only in 2022-11 (Windows 11 22H2), and
|
|
225
|
+
// Windows 10 is still the PowerShell/cmd case this row is about. The
|
|
226
|
+
// paste path survives without it (a multi-line burst is still treated as
|
|
227
|
+
// one paste), so under-claiming here costs nothing and the matrix stays
|
|
228
|
+
// true for the older console.
|
|
229
|
+
bracketedPaste: false,
|
|
230
|
+
alternateScreen: true,
|
|
231
|
+
osc52: false, osc8: false, title: true,
|
|
232
|
+
});
|
|
233
|
+
break;
|
|
234
|
+
case 'vte':
|
|
235
|
+
Object.assign(base, {
|
|
236
|
+
mouse: true, mouseSgr: true, mouseDrag: true,
|
|
237
|
+
bracketedPaste: true, alternateScreen: true,
|
|
238
|
+
// VTE has never implemented OSC 52: the request is still open upstream
|
|
239
|
+
// (GNOME/vte#125) and the widget only *parses* the sequence. Every VTE
|
|
240
|
+
// desktop shares the gap — GNOME Terminal, XFCE Terminal, MATE, Tilix,
|
|
241
|
+
// Terminator, Ptyxis — which made this the single most common terminal
|
|
242
|
+
// to promise a clipboard write it silently drops. `?` would be kinder
|
|
243
|
+
// than `no`, but the user cannot tell the difference when pasting.
|
|
244
|
+
osc52: false,
|
|
245
|
+
// OSC 8 arrived in VTE 0.50, so long-term distributions can lack it.
|
|
246
|
+
osc8: (numericVersion(env.VTE_VERSION) ?? 0) >= 5000,
|
|
247
|
+
title: true,
|
|
248
|
+
});
|
|
249
|
+
break;
|
|
250
|
+
case 'konsole':
|
|
251
|
+
Object.assign(base, {
|
|
252
|
+
mouse: true, mouseSgr: true, mouseDrag: true,
|
|
253
|
+
bracketedPaste: true, alternateScreen: true,
|
|
254
|
+
// Write-only OSC 52 landed in Konsole 24.12 (KDE bug 372116; the patch
|
|
255
|
+
// was merged 2024-07 and 24.08 missed the cut), and KONSOLE_VERSION is
|
|
256
|
+
// YYMMDD (`241200` is 24.12) — so Kubuntu 22.04/24.04 and Debian 12 all
|
|
257
|
+
// ignore the write. 21.12 was Konsole's OSC 8.
|
|
258
|
+
osc52: (numericVersion(env.KONSOLE_VERSION) ?? 0) >= 241200,
|
|
259
|
+
osc8: true, title: true,
|
|
260
|
+
});
|
|
261
|
+
break;
|
|
262
|
+
case 'xterm':
|
|
263
|
+
Object.assign(base, {
|
|
264
|
+
mouse: true, mouseSgr: true, mouseDrag: true,
|
|
265
|
+
bracketedPaste: true, alternateScreen: true,
|
|
266
|
+
// xterm gates clipboard writes behind `allowWindowOps`, which defaults
|
|
267
|
+
// to off in recent builds; assuming it works is how a user ends up with
|
|
268
|
+
// an empty clipboard and no explanation.
|
|
269
|
+
osc52: false, osc8: true, title: true,
|
|
270
|
+
});
|
|
271
|
+
break;
|
|
272
|
+
case 'tmux':
|
|
273
|
+
Object.assign(base, {
|
|
274
|
+
mouse: true, mouseSgr: true, mouseDrag: true,
|
|
275
|
+
bracketedPaste: true, alternateScreen: true,
|
|
276
|
+
// tmux forwards OSC 52 only with `set -g set-clipboard on`.
|
|
277
|
+
osc52: false, osc8: true, title: true,
|
|
278
|
+
});
|
|
279
|
+
break;
|
|
280
|
+
case 'screen':
|
|
281
|
+
Object.assign(base, {
|
|
282
|
+
mouse: true, mouseSgr: true, mouseDrag: true,
|
|
283
|
+
bracketedPaste: true, alternateScreen: true,
|
|
284
|
+
osc52: false, osc8: false, title: true,
|
|
285
|
+
});
|
|
286
|
+
break;
|
|
287
|
+
case 'linux-console':
|
|
288
|
+
Object.assign(base, {
|
|
289
|
+
mouse: false, mouseSgr: false, mouseDrag: false,
|
|
290
|
+
bracketedPaste: false,
|
|
291
|
+
// Keeping the TUI's own screen here costs the user the console
|
|
292
|
+
// scrollback they navigate with Shift+PgUp.
|
|
293
|
+
alternateScreen: false,
|
|
294
|
+
osc52: false, osc8: false, title: false,
|
|
295
|
+
});
|
|
296
|
+
break;
|
|
297
|
+
case 'dumb':
|
|
298
|
+
// A terminal that says `dumb` may still be a capable one behind a wrapper
|
|
299
|
+
// (or a CI pty), and a mode-setting escape it does not understand is
|
|
300
|
+
// ignored, while *not* sending it silently disables the feature. So the
|
|
301
|
+
// baseline stands; only the clipboard and hyperlink promises are dropped,
|
|
302
|
+
// because those are the claims a user cannot verify by looking.
|
|
303
|
+
Object.assign(base, {
|
|
304
|
+
mouse: true, mouseSgr: true, mouseDrag: true,
|
|
305
|
+
bracketedPaste: true, alternateScreen: true,
|
|
306
|
+
osc52: false, osc8: false, title: false,
|
|
307
|
+
});
|
|
308
|
+
break;
|
|
309
|
+
case 'unknown':
|
|
310
|
+
// Something we cannot name: assume the xterm baseline except for the two
|
|
311
|
+
// capabilities that cost the user when wrongly claimed.
|
|
312
|
+
Object.assign(base, {
|
|
313
|
+
mouse: true, mouseSgr: true, mouseDrag: true,
|
|
314
|
+
bracketedPaste: true, alternateScreen: true,
|
|
315
|
+
osc52: false, osc8: true, title: true,
|
|
316
|
+
});
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
if (envFlag(env.DSH_TUI_NO_ALT_SCREEN) === true)
|
|
320
|
+
base.alternateScreen = false;
|
|
321
|
+
const osc8Flag = envFlag(env.DSH_TUI_OSC8);
|
|
322
|
+
if (osc8Flag !== undefined)
|
|
323
|
+
base.osc8 = osc8Flag;
|
|
324
|
+
const override = parseCapsOverrideReport(env.DSH_TUI_TERM_CAPS ?? '');
|
|
325
|
+
Object.assign(base, override.overrides);
|
|
326
|
+
if (base.mouse === false) {
|
|
327
|
+
base.mouseSgr = false;
|
|
328
|
+
base.mouseDrag = false;
|
|
329
|
+
}
|
|
330
|
+
if (base.mouseSgr === false) {
|
|
331
|
+
// The parser understands SGR reports only (`\x1b[<b;x;yM`); enabling the
|
|
332
|
+
// legacy X10 encoding would capture the terminal's mouse and deliver events
|
|
333
|
+
// the TUI drops — dead mouse *and* no native selection. So no SGR means no
|
|
334
|
+
// mouse. (Asking for the mouse turns SGR on with it — see the parser — so
|
|
335
|
+
// this only bites when SGR was refused on purpose.)
|
|
336
|
+
base.mouse = false;
|
|
337
|
+
base.mouseDrag = false;
|
|
338
|
+
}
|
|
339
|
+
return { family, label, colors, ignoredOverrides: override.ignored, ...base };
|
|
340
|
+
}
|
|
341
|
+
/** Mouse-tracking sequences to enable, in one write. Empty when unsupported. */
|
|
342
|
+
export function mouseEnableSequence(caps) {
|
|
343
|
+
if (!caps.mouse)
|
|
344
|
+
return '';
|
|
345
|
+
return `${caps.mouseSgr ? '\x1b[?1006h' : ''}${caps.mouseDrag ? '\x1b[?1002h' : ''}\x1b[?1000h`;
|
|
346
|
+
}
|
|
347
|
+
/** Mouse-tracking sequences to disable. Always safe to emit: `l` on a mode the
|
|
348
|
+
* terminal never enabled is ignored, and leaving a mode on is not. */
|
|
349
|
+
export function mouseDisableSequence() {
|
|
350
|
+
return '\x1b[?1000l\x1b[?1002l\x1b[?1006l';
|
|
351
|
+
}
|
|
352
|
+
/** Bracketed-paste enable/disable, or empty when the terminal lacks it. */
|
|
353
|
+
export function bracketedPasteSequence(caps, on) {
|
|
354
|
+
if (!caps.bracketedPaste)
|
|
355
|
+
return '';
|
|
356
|
+
return on ? '\x1b[?2004h' : '\x1b[?2004l';
|
|
357
|
+
}
|
|
358
|
+
//# sourceMappingURL=terminal-caps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-caps.js","sourceRoot":"","sources":["../src/terminal-caps.ts"],"names":[],"mappings":"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"}
|
package/lib/tool-present.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { SIDE_BY_SIDE_GUTTER, diffHunks, diffLayout, diffLines, sideBySideRows, wordDiffSpans, } from './line-diff.js';
|
|
5
5
|
import { t } from './i18n/index.js';
|
|
6
|
-
import { jobAlias } from './job-label.js';
|
|
6
|
+
import { jobAlias, looksLikeOpaqueId } from './job-label.js';
|
|
7
7
|
import { padToWidth, sliceCodePoints, truncate, wrap } from './term-text.js';
|
|
8
8
|
import { firstString, parseJsonArgs, scalarText } from './json-args.js';
|
|
9
9
|
import { parsePlanTodos, planTitleFromMarkdown, todoProgressBar, todoProgressLabel, todoSummary, askSummary, TODO_STATUS_MARK, todoItemKind, planMarkdownFromArgs, } from './plan.js';
|
|
@@ -63,18 +63,37 @@ export function countDiffLines(hunks) {
|
|
|
63
63
|
}
|
|
64
64
|
return total;
|
|
65
65
|
}
|
|
66
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* Added / removed line counts for a diff (`oldText: null` means a new file).
|
|
68
|
+
*
|
|
69
|
+
* The counts are the *diff's*, not the operands': a three-line snippet with one
|
|
70
|
+
* changed line is `+1 -1`, not `+3 -3`. Counting whole operands made the card
|
|
71
|
+
* header disagree with everything under it — the footer and, since the columns
|
|
72
|
+
* got their colour back, the coloured rows themselves said one line had changed
|
|
73
|
+
* while the header claimed the entire snippet had.
|
|
74
|
+
*
|
|
75
|
+
* The LCS behind `diffLines` is not free, and a paint asks for this stat on
|
|
76
|
+
* every edit card it draws, so the answer is memoized on the hunk array — the
|
|
77
|
+
* same array the row keeps for its whole life.
|
|
78
|
+
*/
|
|
79
|
+
const diffStatCache = new WeakMap();
|
|
67
80
|
export function countDiffAddDel(hunks) {
|
|
68
|
-
const stat = { add: 0, del: 0 };
|
|
69
81
|
if (hunks === undefined)
|
|
70
|
-
return
|
|
82
|
+
return { add: 0, del: 0 };
|
|
83
|
+
const cached = diffStatCache.get(hunks);
|
|
84
|
+
if (cached !== undefined)
|
|
85
|
+
return { ...cached };
|
|
86
|
+
const stat = { add: 0, del: 0 };
|
|
71
87
|
for (const hunk of hunks) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
88
|
+
for (const line of diffLines(hunk.oldText ?? '', hunk.newText)) {
|
|
89
|
+
if (line.kind === 'add')
|
|
90
|
+
stat.add += 1;
|
|
91
|
+
else if (line.kind === 'del')
|
|
92
|
+
stat.del += 1;
|
|
75
93
|
}
|
|
76
94
|
}
|
|
77
|
-
|
|
95
|
+
diffStatCache.set(hunks, stat);
|
|
96
|
+
return { ...stat };
|
|
78
97
|
}
|
|
79
98
|
/**
|
|
80
99
|
* Per-file change counts for the compact view's collapsed line.
|
|
@@ -234,7 +253,7 @@ const TOOL_TITLE_KEYS = [
|
|
|
234
253
|
'web_search', 'web_fetch',
|
|
235
254
|
];
|
|
236
255
|
export function toolTitle(name) {
|
|
237
|
-
if (name === '' || name
|
|
256
|
+
if (name === '' || looksLikeOpaqueId(name))
|
|
238
257
|
return t('card.tool');
|
|
239
258
|
return t(`toolTitle.${name}`, undefined, name === 'tool' ? t('card.tool') : name);
|
|
240
259
|
}
|
|
@@ -569,10 +588,12 @@ export function renderToolDiff(diffs, maxLines, width) {
|
|
|
569
588
|
/**
|
|
570
589
|
* The diff rows with the two columns of a wide terminal.
|
|
571
590
|
*
|
|
572
|
-
* A paired row is
|
|
573
|
-
*
|
|
574
|
-
*
|
|
575
|
-
*
|
|
591
|
+
* A paired row is a removal on the left and an addition on the right at the
|
|
592
|
+
* same time, so it carries one role *per column* (`columns`) instead of one kind
|
|
593
|
+
* for the row — the painter colours each side on its own and leaves the gutter
|
|
594
|
+
* alone. A row with a single side is wholly that change, and a context row,
|
|
595
|
+
* which reads the same on both sides, stays neutral. The `-`/`+` markers inside
|
|
596
|
+
* each column still carry the meaning on a terminal without colour.
|
|
576
597
|
*/
|
|
577
598
|
function paintHunksSideBySide(perHunk, context, width) {
|
|
578
599
|
const column = Math.max(20, Math.floor((width - 2 - SIDE_BY_SIDE_GUTTER.length) / 2));
|
|
@@ -594,10 +615,21 @@ function paintHunksSideBySide(perHunk, context, width) {
|
|
|
594
615
|
...row.leftSpans,
|
|
595
616
|
...row.rightSpans.map(span => ({ start: span.start + column + SIDE_BY_SIDE_GUTTER.length, end: span.end + column + SIDE_BY_SIDE_GUTTER.length })),
|
|
596
617
|
];
|
|
618
|
+
// A side with no line of its own gets no colour: a removal is red where
|
|
619
|
+
// the removal is, an addition green where the addition is, and the
|
|
620
|
+
// blank half of a one-sided change stays neutral instead of turning the
|
|
621
|
+
// whole row into a bar with nothing in it.
|
|
622
|
+
const columns = [
|
|
623
|
+
...(row.context || row.left === '' ? [] : [{ start: 0, end: column, kind: 'diff-del' }]),
|
|
624
|
+
...(row.context || row.right === ''
|
|
625
|
+
? []
|
|
626
|
+
: [{ start: column + SIDE_BY_SIDE_GUTTER.length, end: text.length, kind: 'diff-add' }]),
|
|
627
|
+
];
|
|
597
628
|
rows.push({
|
|
598
|
-
kind:
|
|
629
|
+
kind: 'tool-result',
|
|
599
630
|
text,
|
|
600
631
|
...(spans.length === 0 ? {} : { spans }),
|
|
632
|
+
...(columns.length === 0 ? {} : { columns }),
|
|
601
633
|
});
|
|
602
634
|
}
|
|
603
635
|
}
|