dsh-ssh-tui 0.7.4 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.en.md +37 -0
  2. package/README.md +436 -572
  3. package/docs/display-mode.md +122 -0
  4. package/docs/remote-ops.md +104 -0
  5. package/docs/terminals.md +53 -0
  6. package/lib/attach.js +4 -4
  7. package/lib/attach.js.map +1 -1
  8. package/lib/auth-failure.js +128 -0
  9. package/lib/auth-failure.js.map +1 -0
  10. package/lib/commands.js +3 -0
  11. package/lib/commands.js.map +1 -1
  12. package/lib/dialogs.js +43 -0
  13. package/lib/dialogs.js.map +1 -1
  14. package/lib/display-mode.js +147 -0
  15. package/lib/display-mode.js.map +1 -0
  16. package/lib/display-sock.js +361 -21
  17. package/lib/display-sock.js.map +1 -1
  18. package/lib/footer.js +6 -9
  19. package/lib/footer.js.map +1 -1
  20. package/lib/glyph-measure.js +92 -0
  21. package/lib/glyph-measure.js.map +1 -0
  22. package/lib/i18n/en.js +27 -2
  23. package/lib/i18n/en.js.map +1 -1
  24. package/lib/i18n/zh.js +27 -2
  25. package/lib/i18n/zh.js.map +1 -1
  26. package/lib/index.js +74 -5
  27. package/lib/index.js.map +1 -1
  28. package/lib/paint.js +22 -9
  29. package/lib/paint.js.map +1 -1
  30. package/lib/picker.js +14 -13
  31. package/lib/picker.js.map +1 -1
  32. package/lib/plan.js +11 -11
  33. package/lib/plan.js.map +1 -1
  34. package/lib/platform.js +96 -0
  35. package/lib/platform.js.map +1 -1
  36. package/lib/session-blank.js +81 -0
  37. package/lib/session-blank.js.map +1 -0
  38. package/lib/session-list.js +102 -81
  39. package/lib/session-list.js.map +1 -1
  40. package/lib/startup.js +7 -0
  41. package/lib/startup.js.map +1 -1
  42. package/lib/subagent-model.js +8 -7
  43. package/lib/subagent-model.js.map +1 -1
  44. package/lib/term-text.js +296 -28
  45. package/lib/term-text.js.map +1 -1
  46. package/lib/terminal-input.js +132 -10
  47. package/lib/terminal-input.js.map +1 -1
  48. package/lib/theme.js +318 -0
  49. package/lib/theme.js.map +1 -0
  50. package/lib/tool-present.js +11 -9
  51. package/lib/tool-present.js.map +1 -1
  52. package/lib/tui.js +535 -37
  53. package/lib/tui.js.map +1 -1
  54. package/lib/types/attach.d.ts +6 -2
  55. package/lib/types/auth-failure.d.ts +78 -0
  56. package/lib/types/commands.d.ts +9 -0
  57. package/lib/types/dialogs.d.ts +36 -0
  58. package/lib/types/display-mode.d.ts +99 -0
  59. package/lib/types/display-sock.d.ts +75 -0
  60. package/lib/types/footer.d.ts +1 -1
  61. package/lib/types/glyph-measure.d.ts +41 -0
  62. package/lib/types/index.d.ts +20 -0
  63. package/lib/types/plan.d.ts +5 -2
  64. package/lib/types/platform.d.ts +83 -0
  65. package/lib/types/session-blank.d.ts +51 -0
  66. package/lib/types/session-list.d.ts +34 -0
  67. package/lib/types/startup.d.ts +6 -0
  68. package/lib/types/subagent-model.d.ts +7 -6
  69. package/lib/types/term-text.d.ts +55 -23
  70. package/lib/types/terminal-input.d.ts +37 -0
  71. package/lib/types/theme.d.ts +109 -0
  72. package/lib/types/tool-present.d.ts +2 -2
  73. package/lib/types/tui.d.ts +121 -1
  74. package/package.json +92 -93
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Where the TUI's bytes go when the parent is not a terminal.
3
+ *
4
+ * The plugin paints through a display relay, and a relay normally *is* a
5
+ * terminal: it writes ANSI to stdout, listens for `resize`, and puts stdin in raw
6
+ * mode. A GUI, a browser terminal or a test harness has none of those, but it can
7
+ * provide the same three things over pipes — bytes in, bytes out, and a size it
8
+ * reports as a protocol frame. This module is the switch that says "the parent is
9
+ * such a relay, do not require a TTY".
10
+ *
11
+ * Two entries, one mechanism and one sugar (as chosen when the mode was
12
+ * designed):
13
+ *
14
+ * - `DSH_TUI_DISPLAY=stdio` in the environment — the mechanism. Setting it *is*
15
+ * the parent's statement that it is a relay, so it is also what the no-TTY
16
+ * guard consults.
17
+ * - `--display stdio` on the command line — the sugar, declared in the profile's
18
+ * own grammar so it shows up in `--help` and a typo is an error rather than a
19
+ * silently inert plugin.
20
+ *
21
+ * @module dsh-ssh-tui/display-mode
22
+ */
23
+ /** The environment variable that selects the display mode. */
24
+ export const DISPLAY_MODE_ENV = 'DSH_TUI_DISPLAY';
25
+ /**
26
+ * Parse one mode word.
27
+ * @param value - a value from the environment or a flag.
28
+ * @returns the mode, or undefined for anything else (the caller reports it).
29
+ */
30
+ export function parseDisplayMode(value) {
31
+ const raw = String(value ?? '').trim().toLowerCase();
32
+ return raw === 'stdio' || raw === 'tty' ? raw : undefined;
33
+ }
34
+ /**
35
+ * The `--display <mode>` value in an argument vector.
36
+ *
37
+ * Accepts both spellings a person may type (`--display stdio`,
38
+ * `--display=stdio`) and only ever reads the pair it owns — everything else is
39
+ * left where it is, because the same vector is parsed by the profile's own
40
+ * grammar and by the host.
41
+ * @param argv - an argument vector (defaults to this process's).
42
+ * @returns the value found, if any.
43
+ */
44
+ export function displayModeFromArgv(argv = process.argv) {
45
+ for (let at = 0; at < argv.length; at += 1) {
46
+ const arg = argv[at] ?? '';
47
+ if (arg.startsWith('--display='))
48
+ return arg.slice('--display='.length);
49
+ if (arg === '--display') {
50
+ const next = argv[at + 1];
51
+ // A missing value is not silently "the default": leave it to the grammar,
52
+ // which turns it into a usage error.
53
+ return next === undefined || next.startsWith('-') ? '' : next;
54
+ }
55
+ }
56
+ return undefined;
57
+ }
58
+ /**
59
+ * Resolve the mode request from the environment and the command line.
60
+ *
61
+ * The environment wins when both are present: it is the mechanism, and a parent
62
+ * that sets it is describing the channel it is about to speak on, whereas a flag
63
+ * is a convenience the reader may have left in a wrapper script.
64
+ * @param env - the environment to read.
65
+ * @param argv - the argument vector to read.
66
+ * @returns the requested mode, plus the offending value when one was useless.
67
+ */
68
+ export function requestedDisplayMode(env = process.env, argv = process.argv) {
69
+ const fromEnv = env[DISPLAY_MODE_ENV];
70
+ if (fromEnv !== undefined && String(fromEnv).trim() !== '') {
71
+ const mode = parseDisplayMode(fromEnv);
72
+ return mode === undefined ? { invalid: String(fromEnv).trim() } : { mode };
73
+ }
74
+ const fromArgv = displayModeFromArgv(argv);
75
+ if (fromArgv === undefined || fromArgv === '')
76
+ return {};
77
+ const mode = parseDisplayMode(fromArgv);
78
+ return mode === undefined ? { invalid: fromArgv } : { mode };
79
+ }
80
+ /**
81
+ * A window-size report: `CSI 8 ; rows ; cols t`.
82
+ *
83
+ * This is the sequence a terminal sends when it is asked for its size, and it is
84
+ * also how a pipe parent says "the panel is now this big". A pipe has no
85
+ * `resize` event and no SIGWINCH, so without this the embedder's only way to
86
+ * resize the TUI would be to restart it.
87
+ */
88
+ export const WINDOW_SIZE_REPORT = /\u001b\[8;(\d+);(\d+)t/gu;
89
+ /** Longest run that can still grow into a window-size report. */
90
+ const PARTIAL_REPORT_MAX = 16;
91
+ /** `ESC`, `ESC [`, `ESC [ 8`, `ESC [ 8 ; 24`, … — but not a finished report. */
92
+ const PARTIAL_REPORT = /^\u001b(?:\[(?:8(?:;(?:\d*(?:;\d*)?)?)?)?)?$/u;
93
+ /**
94
+ * Split a parent's input into keystrokes and size reports.
95
+ *
96
+ * A report can arrive split across two reads (`ESC [ 8 ; 2` then `4 ; 80 t`),
97
+ * and forwarding half of it as typing would send escape bytes to the model's
98
+ * prompt. The tail is held until the next read, exactly like the cursor-reply
99
+ * filter holds a half-arrived reply — with the same deadline that filter gets:
100
+ * a lone `ESC` matches the prefix, and the byte after it may never come, so a
101
+ * caller that forwards typing **must** release a {@link pending} run when its
102
+ * window passes (`flush()`). Waiting for the next read instead swallows the
103
+ * user's Escape key, and delivers it glued to whatever key follows as an Alt
104
+ * chord. `TerminalInputGuard` is the same filter with that timer attached.
105
+ * @returns a filter for one input stream.
106
+ */
107
+ export function createParentResizeFilter() {
108
+ let held = '';
109
+ return {
110
+ push(text) {
111
+ const sizes = [];
112
+ const combined = held + text;
113
+ held = '';
114
+ let forward = '';
115
+ let cursor = 0;
116
+ for (const match of combined.matchAll(WINDOW_SIZE_REPORT)) {
117
+ const at = match.index ?? 0;
118
+ forward += combined.slice(cursor, at);
119
+ cursor = at + match[0].length;
120
+ // `CSI 8 ; rows ; cols t`: rows is the first number, columns the second.
121
+ const rows = Number(match[1]);
122
+ const columns = Number(match[2]);
123
+ // A zero dimension is a malformed report, not a request to hide the TUI.
124
+ if (rows > 0 && columns > 0)
125
+ sizes.push({ columns, rows });
126
+ }
127
+ const rest = combined.slice(cursor);
128
+ // Only the run from the last ESC can still grow into a report; everything
129
+ // before it is typing this read already settled.
130
+ const escapeAt = rest.lastIndexOf('\u001b');
131
+ const candidate = escapeAt === -1 ? '' : rest.slice(escapeAt);
132
+ const tail = candidate.length <= PARTIAL_REPORT_MAX && PARTIAL_REPORT.test(candidate) ? candidate : '';
133
+ held = tail;
134
+ return { forward: forward + rest.slice(0, rest.length - tail.length), sizes };
135
+ },
136
+ flush() {
137
+ const tail = held;
138
+ held = '';
139
+ return tail;
140
+ },
141
+ /** Is a run held that could still grow into a report? */
142
+ get pending() {
143
+ return held !== '';
144
+ },
145
+ };
146
+ }
147
+ //# sourceMappingURL=display-mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"display-mode.js","sourceRoot":"","sources":["../src/display-mode.ts"],"names":[],"mappings":"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"}