codsh-cli 0.1.0 → 0.2.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.
- package/README.md +14 -2
- package/README.zh.md +12 -1
- package/lib/index.js +1154 -251
- package/lib/types/console.d.ts +121 -33
- package/lib/types/keys.d.ts +20 -0
- package/lib/types/prompt.d.ts +15 -0
- package/lib/types/screen.d.ts +204 -0
- package/lib/types/selector.d.ts +0 -1
- package/lib/types/spinner.d.ts +7 -1
- package/lib/types/theme.d.ts +6 -5
- package/lib/types/transcript.d.ts +9 -9
- package/lib/types/wrap.d.ts +27 -0
- package/package.json +4 -2
package/lib/types/console.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The process-facing terminal, in two shapes.
|
|
3
3
|
*
|
|
4
|
-
* On a terminal this surface owns the keyboard: raw mode, its own
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* On a terminal this surface owns the keyboard AND the screen: raw mode, its own
|
|
5
|
+
* key decoding, and an alternate-screen viewport ({@link Screen}) that holds the
|
|
6
|
+
* transcript in a scrollback buffer of its own with the input box pinned below
|
|
7
|
+
* it. That is what an inline completion menu, a multi-line prompt, and a session
|
|
8
|
+
* that reads as its own space require — `readline` reports no lone Escape, draws
|
|
9
|
+
* no menu, and decides for itself what Enter means.
|
|
8
10
|
*
|
|
9
11
|
* Off a terminal it is a line reader over `readline`, because a pipe has no
|
|
10
12
|
* cursor to manage and every line in a script is a separate instruction. Both
|
|
@@ -16,6 +18,7 @@ import type { Key } from './keys.ts';
|
|
|
16
18
|
/** The output stream this surface writes to. */
|
|
17
19
|
export interface OutputStream extends NodeJS.WritableStream {
|
|
18
20
|
readonly columns?: number;
|
|
21
|
+
readonly rows?: number;
|
|
19
22
|
readonly isTTY?: boolean;
|
|
20
23
|
}
|
|
21
24
|
/** The input stream this surface reads from. */
|
|
@@ -41,15 +44,18 @@ export declare class TerminalConsole {
|
|
|
41
44
|
private readonly earlyKeys;
|
|
42
45
|
private escapeTimer;
|
|
43
46
|
private ended;
|
|
44
|
-
/**
|
|
45
|
-
private
|
|
46
|
-
/** Where among those rows the cursor was left. */
|
|
47
|
-
private regionCursor;
|
|
48
|
-
/** Whether the region currently holds input focus, which shows the cursor. */
|
|
49
|
-
private regionFocus;
|
|
47
|
+
/** The viewport this surface owns on a terminal; absent off one. */
|
|
48
|
+
private readonly screen;
|
|
50
49
|
constructor(input: InputStream, output: OutputStream);
|
|
51
50
|
/** Display columns available for one line, never below {@link MIN_COLUMNS}. */
|
|
52
51
|
get columns(): number;
|
|
52
|
+
/**
|
|
53
|
+
* Columns content may be laid out for: one less than the width, because the
|
|
54
|
+
* viewport wraps at that boundary. Markdown layout MUST use this figure — a
|
|
55
|
+
* table laid out one column wider is refolded by the viewport, and its rows
|
|
56
|
+
* shear apart.
|
|
57
|
+
*/
|
|
58
|
+
get contentColumns(): number;
|
|
53
59
|
/** Whether the output stream is a terminal. */
|
|
54
60
|
get isTty(): boolean;
|
|
55
61
|
/**
|
|
@@ -69,10 +75,40 @@ export declare class TerminalConsole {
|
|
|
69
75
|
*/
|
|
70
76
|
onResize(handler: () => void): () => void;
|
|
71
77
|
/**
|
|
72
|
-
*
|
|
78
|
+
* Take the viewport: the transcript and the prompt live on their own screen
|
|
79
|
+
* from here, and the terminal keeps the buffer the person had.
|
|
80
|
+
*/
|
|
81
|
+
enterScreen(): void;
|
|
82
|
+
/**
|
|
83
|
+
* Give the terminal back. Idempotent: every exit path calls it.
|
|
84
|
+
*/
|
|
85
|
+
leaveScreen(): void;
|
|
86
|
+
/** Whether this surface currently holds its own screen. */
|
|
87
|
+
get owningScreen(): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Scroll the transcript inside the viewport.
|
|
90
|
+
* @param delta - rows to move; negative goes back into history.
|
|
91
|
+
*/
|
|
92
|
+
scrollBy(delta: number): void;
|
|
93
|
+
/**
|
|
94
|
+
* Set the notice shown while the transcript is scrolled back.
|
|
95
|
+
* @param text - the styled line, or the empty string for none.
|
|
96
|
+
*/
|
|
97
|
+
setScrollNotice(text: string): void;
|
|
98
|
+
/**
|
|
99
|
+
* Scroll the transcript by a whole viewport.
|
|
100
|
+
* @param direction - -1 for back into history, 1 towards the tail.
|
|
101
|
+
*/
|
|
102
|
+
scrollPage(direction: -1 | 1): void;
|
|
103
|
+
/** Return to the tail of the transcript. */
|
|
104
|
+
scrollToBottom(): void;
|
|
105
|
+
/** Physical rows currently scrolled out of view; zero means at the tail. */
|
|
106
|
+
get scrolledBy(): number;
|
|
107
|
+
/**
|
|
108
|
+
* Clear the transcript this session accumulated.
|
|
73
109
|
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
110
|
+
* On its own screen there is no shell scrollback to preserve, so this empties
|
|
111
|
+
* the buffer the viewport shows rather than wiping a shared terminal.
|
|
76
112
|
*/
|
|
77
113
|
clearScreen(): void;
|
|
78
114
|
/**
|
|
@@ -98,39 +134,82 @@ export declare class TerminalConsole {
|
|
|
98
134
|
/** Mark input finished and release every waiting read. */
|
|
99
135
|
private end;
|
|
100
136
|
/**
|
|
101
|
-
*
|
|
137
|
+
* Keep one finished transcript line.
|
|
102
138
|
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
139
|
+
* On its own screen the line goes into the viewport's scrollback, which is
|
|
140
|
+
* what lets the transcript scroll under a prompt that does not move. Off one
|
|
141
|
+
* it is written straight out, because a pipe's reader wants exactly that.
|
|
105
142
|
* @param line - the line, without its terminator.
|
|
106
143
|
*/
|
|
107
144
|
write(line: string): void;
|
|
108
145
|
/**
|
|
109
|
-
* Replace the
|
|
146
|
+
* Replace the rows pinned below the transcript.
|
|
110
147
|
*
|
|
111
148
|
* This is the whole live area: an input box, a completion menu, a working
|
|
112
|
-
* indicator.
|
|
113
|
-
*
|
|
114
|
-
* terminal the call is ignored — a redirected transcript must not collect
|
|
115
|
-
* frames of a box nobody can see.
|
|
149
|
+
* indicator, the status row. Off a terminal the call is ignored — a
|
|
150
|
+
* redirected transcript must not collect frames of a box nobody can see.
|
|
116
151
|
* @param rows - the rows to display, top to bottom.
|
|
117
152
|
* @param cursor - where to leave the terminal cursor among them.
|
|
118
|
-
* @param focus - whether the
|
|
119
|
-
*
|
|
120
|
-
* a streaming line) reads as content colliding with it.
|
|
153
|
+
* @param focus - whether the rows hold input focus, which is when the cursor
|
|
154
|
+
* shows. Parked anywhere else it reads as content colliding with it.
|
|
121
155
|
*/
|
|
122
156
|
setRegion(rows: readonly string[], cursor: RegionCursor, focus?: boolean): void;
|
|
123
|
-
/** Remove the region, leaving the cursor where the next write will land. */
|
|
124
|
-
clearRegion(): void;
|
|
125
|
-
/** Move to the region's first row and erase everything from there down. */
|
|
126
|
-
private eraseRegion;
|
|
127
157
|
/**
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
158
|
+
* Keep one collapsible block: summary now, full form behind the toggle.
|
|
159
|
+
*
|
|
160
|
+
* Off a terminal only the summary is written — a pipe has no keys to toggle
|
|
161
|
+
* with, and scripts want the digest.
|
|
162
|
+
* @param summary - the collapsed lines.
|
|
163
|
+
* @param full - the expanded lines.
|
|
164
|
+
*/
|
|
165
|
+
appendFold(summary: readonly string[], full: readonly string[]): void;
|
|
166
|
+
/**
|
|
167
|
+
* Swap every collapsible block between summary and full form.
|
|
168
|
+
* @returns false when there is nothing to toggle.
|
|
132
169
|
*/
|
|
133
|
-
|
|
170
|
+
/**
|
|
171
|
+
* Anchor a mouse selection at a terminal position.
|
|
172
|
+
* @param row - terminal row, 1-based.
|
|
173
|
+
* @param column - terminal column, 1-based.
|
|
174
|
+
*/
|
|
175
|
+
mouseDown(row: number, column: number): void;
|
|
176
|
+
/**
|
|
177
|
+
* Extend the mouse selection to a terminal position.
|
|
178
|
+
* @param row - terminal row, 1-based.
|
|
179
|
+
* @param column - terminal column, 1-based.
|
|
180
|
+
*/
|
|
181
|
+
mouseDrag(row: number, column: number): void;
|
|
182
|
+
/**
|
|
183
|
+
* Finish the mouse selection.
|
|
184
|
+
* @returns the selected text, or undefined for a bare click.
|
|
185
|
+
*/
|
|
186
|
+
mouseUp(): string | undefined;
|
|
187
|
+
/**
|
|
188
|
+
* Put text on the clipboard.
|
|
189
|
+
*
|
|
190
|
+
* Two channels, because neither is universal: OSC 52 reaches through SSH and
|
|
191
|
+
* works wherever the terminal permits it, and the platform helper covers the
|
|
192
|
+
* terminals that refuse the escape. `CODSH_CLIPBOARD` narrows it to `osc52`,
|
|
193
|
+
* `system`, or `off` — tests use `osc52` so a run never touches the real
|
|
194
|
+
* clipboard.
|
|
195
|
+
* @param text - the plain text to copy.
|
|
196
|
+
* @returns whether a copy was attempted at all.
|
|
197
|
+
*/
|
|
198
|
+
copyText(text: string): boolean;
|
|
199
|
+
/**
|
|
200
|
+
* Make the last `count` written lines a collapsible block after the fact.
|
|
201
|
+
*
|
|
202
|
+
* Screen-only on purpose: a pipe already carries the full text, and a
|
|
203
|
+
* summary would subtract from it.
|
|
204
|
+
* @param count - how many trailing lines the block owns.
|
|
205
|
+
* @param summary - the collapsed lines, already styled.
|
|
206
|
+
*/
|
|
207
|
+
foldRecent(count: number, summary: readonly string[]): void;
|
|
208
|
+
toggleFolds(): boolean;
|
|
209
|
+
/** Return every block to its summary. */
|
|
210
|
+
collapseFolds(): void;
|
|
211
|
+
/** Take the pinned rows down, leaving the transcript alone. */
|
|
212
|
+
clearRegion(): void;
|
|
134
213
|
/** Ring the terminal bell; a pipe gets nothing to beep with. */
|
|
135
214
|
bell(): void;
|
|
136
215
|
/**
|
|
@@ -149,4 +228,13 @@ export declare class TerminalConsole {
|
|
|
149
228
|
readLine(signal?: AbortSignal): Promise<string | undefined>;
|
|
150
229
|
/** Restore the terminal and stop reading. */
|
|
151
230
|
close(): void;
|
|
231
|
+
/**
|
|
232
|
+
* Write one line to the terminal the person keeps, not to our viewport.
|
|
233
|
+
*
|
|
234
|
+
* The exit summary is what needs this: the session's own screen disappears
|
|
235
|
+
* with it, so the few facts worth keeping — the session id, what it cost —
|
|
236
|
+
* have to land in the buffer that survives.
|
|
237
|
+
* @param line - the line, without its terminator.
|
|
238
|
+
*/
|
|
239
|
+
writeAfterScreen(line: string): void;
|
|
152
240
|
}
|
package/lib/types/keys.d.ts
CHANGED
|
@@ -58,9 +58,29 @@ export type Key = {
|
|
|
58
58
|
kind: 'clear-screen';
|
|
59
59
|
} | {
|
|
60
60
|
kind: 'expand-output';
|
|
61
|
+
} | {
|
|
62
|
+
kind: 'page';
|
|
63
|
+
direction: -1 | 1;
|
|
64
|
+
} | {
|
|
65
|
+
kind: 'scroll';
|
|
66
|
+
lines: number;
|
|
67
|
+
} | {
|
|
68
|
+
kind: 'scroll-end';
|
|
61
69
|
} | {
|
|
62
70
|
kind: 'paste';
|
|
63
71
|
text: string;
|
|
72
|
+
} | {
|
|
73
|
+
kind: 'mouse-down';
|
|
74
|
+
row: number;
|
|
75
|
+
column: number;
|
|
76
|
+
} | {
|
|
77
|
+
kind: 'mouse-drag';
|
|
78
|
+
row: number;
|
|
79
|
+
column: number;
|
|
80
|
+
} | {
|
|
81
|
+
kind: 'mouse-up';
|
|
82
|
+
row: number;
|
|
83
|
+
column: number;
|
|
64
84
|
};
|
|
65
85
|
/** Decodes terminal bytes into keys, holding partial sequences between reads. */
|
|
66
86
|
export declare class KeyDecoder {
|
package/lib/types/prompt.d.ts
CHANGED
|
@@ -44,6 +44,9 @@ export declare class Prompt {
|
|
|
44
44
|
private readonly queued;
|
|
45
45
|
/** The working indicator shown under the box. */
|
|
46
46
|
private hint;
|
|
47
|
+
/** A short-lived notice that borrows the hint row, e.g. the copy toast. */
|
|
48
|
+
private flash;
|
|
49
|
+
private flashTimer;
|
|
47
50
|
/** The always-current session facts shown as the region's last row. */
|
|
48
51
|
private status;
|
|
49
52
|
/** The assistant line still arriving, shown above the box. */
|
|
@@ -52,6 +55,9 @@ export declare class Prompt {
|
|
|
52
55
|
private accent;
|
|
53
56
|
/** Whether a read is outstanding, which decides where a submission goes. */
|
|
54
57
|
private reading;
|
|
58
|
+
/** Wheel rows accumulated this tick, painted once — scrolling per event janks. */
|
|
59
|
+
private pendingScroll;
|
|
60
|
+
private scrollFlushQueued;
|
|
55
61
|
/**
|
|
56
62
|
* Whether the interactive session is running, which is when the box is worth
|
|
57
63
|
* drawing. The box stays up while the agent works — typing ahead must be
|
|
@@ -86,6 +92,15 @@ export declare class Prompt {
|
|
|
86
92
|
* @param text - the text, or undefined to drop the row.
|
|
87
93
|
*/
|
|
88
94
|
setHint(text: string | undefined): void;
|
|
95
|
+
/**
|
|
96
|
+
* Show a notice on the hint row briefly, then give the row back.
|
|
97
|
+
*
|
|
98
|
+
* The hint row belongs to the working indicator, which repaints itself
|
|
99
|
+
* continuously — a notice written through setHint would last one tick. The
|
|
100
|
+
* flash outranks the hint until its moment passes.
|
|
101
|
+
* @param text - the styled notice.
|
|
102
|
+
*/
|
|
103
|
+
setFlash(text: string): void;
|
|
89
104
|
/**
|
|
90
105
|
* Set the status row, the region's always-current last line.
|
|
91
106
|
* @param text - the styled row, or undefined to drop it.
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The session's own screen: an alternate-screen viewport with its own scrollback.
|
|
3
|
+
*
|
|
4
|
+
* This is what makes a session feel like a place rather than a run of output.
|
|
5
|
+
* The terminal's buffer is left exactly as the person had it — their shell
|
|
6
|
+
* history is neither scrolled away nor interleaved — and everything this
|
|
7
|
+
* surface shows lives in a buffer it owns: the transcript scrolls inside the
|
|
8
|
+
* viewport while the input box stays where it is, at the bottom.
|
|
9
|
+
*
|
|
10
|
+
* Owning the viewport means doing three jobs the terminal used to do. Lines are
|
|
11
|
+
* wrapped here ({@link wrapAll}), because a line that overflows would otherwise
|
|
12
|
+
* overwrite the row below. Scrolling is ours, because the terminal's scrollback
|
|
13
|
+
* does not exist on the alternate screen. And every frame is painted as a
|
|
14
|
+
* whole, diffed against the last one — which is what removes the class of bug
|
|
15
|
+
* that relative erase arithmetic keeps producing.
|
|
16
|
+
* @module codsh/src/screen
|
|
17
|
+
*/
|
|
18
|
+
/** Where the cursor belongs within the chrome rows. */
|
|
19
|
+
export interface ChromeCursor {
|
|
20
|
+
row: number;
|
|
21
|
+
column: number;
|
|
22
|
+
}
|
|
23
|
+
/** What the screen writes to and measures itself against. */
|
|
24
|
+
export interface ScreenHost {
|
|
25
|
+
/** Emit raw bytes to the terminal. */
|
|
26
|
+
write(data: string): void;
|
|
27
|
+
/** Display columns currently available. */
|
|
28
|
+
columns(): number;
|
|
29
|
+
/** Screen rows currently available. */
|
|
30
|
+
rows(): number;
|
|
31
|
+
}
|
|
32
|
+
/** An alternate-screen viewport over a scrollback buffer this surface owns. */
|
|
33
|
+
export declare class Screen {
|
|
34
|
+
private readonly host;
|
|
35
|
+
/** Logical transcript lines, unwrapped, oldest first. */
|
|
36
|
+
private logical;
|
|
37
|
+
/** The same lines wrapped to the current width — what the viewport slices. */
|
|
38
|
+
private physical;
|
|
39
|
+
/** The bottom rows: input box, menu, indicator, status. */
|
|
40
|
+
private chrome;
|
|
41
|
+
private chromeCursor;
|
|
42
|
+
/** Whether the chrome holds input focus, which is when the cursor shows. */
|
|
43
|
+
private chromeFocus;
|
|
44
|
+
/** Physical rows hidden below the viewport; zero means following the tail. */
|
|
45
|
+
private offset;
|
|
46
|
+
/** What to show while scrolled back, drawn over the viewport's top row. */
|
|
47
|
+
private notice;
|
|
48
|
+
/** A mouse selection over the transcript, in physical-row coordinates. */
|
|
49
|
+
private selection;
|
|
50
|
+
/** Collapsed blocks in the transcript, in order, with both of their forms. */
|
|
51
|
+
private folds;
|
|
52
|
+
/** Whether the folds currently show their full form. */
|
|
53
|
+
private expanded;
|
|
54
|
+
/** The last painted frame, so a repaint only touches rows that changed. */
|
|
55
|
+
private painted;
|
|
56
|
+
/** Width the current frame was painted at, to detect a resize. */
|
|
57
|
+
private paintedColumns;
|
|
58
|
+
private active;
|
|
59
|
+
constructor(host: ScreenHost);
|
|
60
|
+
/** Whether the alternate screen is currently held. */
|
|
61
|
+
get entered(): boolean;
|
|
62
|
+
/** Physical rows scrolled up out of view; zero means the tail is showing. */
|
|
63
|
+
get scrolledBy(): number;
|
|
64
|
+
/** Take the alternate screen and start reporting the mouse. */
|
|
65
|
+
enter(): void;
|
|
66
|
+
/**
|
|
67
|
+
* Give the terminal back exactly as it was.
|
|
68
|
+
*
|
|
69
|
+
* Idempotent, because every exit path calls it — a normal quit, an
|
|
70
|
+
* interrupt, and a crash handler all have to leave the terminal usable.
|
|
71
|
+
*/
|
|
72
|
+
leave(): void;
|
|
73
|
+
/**
|
|
74
|
+
* Append finished transcript lines.
|
|
75
|
+
*
|
|
76
|
+
* Following the tail is the default; a person who has scrolled up stays
|
|
77
|
+
* where they are, and the new rows accumulate below them.
|
|
78
|
+
* @param lines - the lines to keep, already styled.
|
|
79
|
+
*/
|
|
80
|
+
append(lines: readonly string[]): void;
|
|
81
|
+
/**
|
|
82
|
+
* Append one collapsible block: its summary now, its full form on demand.
|
|
83
|
+
*
|
|
84
|
+
* This is what makes every long block — not merely the latest — expandable:
|
|
85
|
+
* the buffer keeps both forms, and toggling rebuilds the transcript in
|
|
86
|
+
* place, exactly like a details/summary element.
|
|
87
|
+
* @param summary - the collapsed lines, already styled.
|
|
88
|
+
* @param full - the expanded lines, already styled.
|
|
89
|
+
*/
|
|
90
|
+
appendFold(summary: readonly string[], full: readonly string[]): void;
|
|
91
|
+
/**
|
|
92
|
+
* Turn the last `count` appended lines into a collapsible block after the
|
|
93
|
+
* fact.
|
|
94
|
+
*
|
|
95
|
+
* This is how a finished answer becomes foldable without ever having been
|
|
96
|
+
* withheld: it streamed in the open, and only once complete does it grow a
|
|
97
|
+
* summary form. The block starts expanded — the person is reading it — and
|
|
98
|
+
* collapses with the rest when the conversation moves on.
|
|
99
|
+
* @param count - how many trailing lines the block owns.
|
|
100
|
+
* @param summary - the collapsed lines, already styled.
|
|
101
|
+
*/
|
|
102
|
+
foldBack(count: number, summary: readonly string[]): void;
|
|
103
|
+
/** Whether any collapsible block exists. */
|
|
104
|
+
get hasFolds(): boolean;
|
|
105
|
+
/** Whether the folds currently show their full form. */
|
|
106
|
+
get foldsExpanded(): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Swap every fold between its summary and its full form.
|
|
109
|
+
* @returns false when there is nothing to toggle.
|
|
110
|
+
*/
|
|
111
|
+
toggleFolds(): boolean;
|
|
112
|
+
/** Return every fold to its summary, the way moving on reads as dismissal. */
|
|
113
|
+
collapseFolds(): void;
|
|
114
|
+
/** Put every fold into one form, whatever mix of states they are in now. */
|
|
115
|
+
private setFolds;
|
|
116
|
+
/**
|
|
117
|
+
* Replace the bottom rows.
|
|
118
|
+
* @param rows - the chrome, top to bottom.
|
|
119
|
+
* @param cursor - where the cursor belongs among them.
|
|
120
|
+
* @param focus - whether to show the cursor there.
|
|
121
|
+
*/
|
|
122
|
+
setChrome(rows: readonly string[], cursor: ChromeCursor, focus: boolean): void;
|
|
123
|
+
/**
|
|
124
|
+
* Set the line shown while the reader is away from the tail.
|
|
125
|
+
*
|
|
126
|
+
* Drawn OVER the viewport's top row rather than added to the chrome: a notice
|
|
127
|
+
* that changed the chrome's height would move the input box as a side effect
|
|
128
|
+
* of scrolling, and would make a page up and a page down different sizes.
|
|
129
|
+
* @param text - the styled notice, already fitted.
|
|
130
|
+
*/
|
|
131
|
+
setScrollNotice(text: string): void;
|
|
132
|
+
/**
|
|
133
|
+
* Scroll the transcript.
|
|
134
|
+
* @param delta - rows to move; negative scrolls back into history.
|
|
135
|
+
*/
|
|
136
|
+
scrollBy(delta: number): void;
|
|
137
|
+
/**
|
|
138
|
+
* Scroll by a whole viewport, which is what the page keys mean.
|
|
139
|
+
* @param direction - -1 for back into history, 1 towards the tail.
|
|
140
|
+
*/
|
|
141
|
+
scrollPage(direction: -1 | 1): void;
|
|
142
|
+
/** Jump back to the tail, which is also what a new submission does. */
|
|
143
|
+
scrollToBottom(): void;
|
|
144
|
+
/**
|
|
145
|
+
* Drop the transcript, keeping the chrome.
|
|
146
|
+
*
|
|
147
|
+
* Ctrl-L on a shared terminal clears a viewport the person may want back; on
|
|
148
|
+
* our own screen the buffer IS the session's history, so this empties it.
|
|
149
|
+
*/
|
|
150
|
+
clearTranscript(): void;
|
|
151
|
+
/** Re-wrap and repaint after the terminal changed size. */
|
|
152
|
+
resize(): void;
|
|
153
|
+
/**
|
|
154
|
+
* Anchor a selection where the left button went down.
|
|
155
|
+
*
|
|
156
|
+
* The terminal cannot select for us while mouse reporting is on, so the
|
|
157
|
+
* viewport does it: press anchors, motion extends, release copies — the
|
|
158
|
+
* shape opencode and Claude give the same gesture.
|
|
159
|
+
* @param row - terminal row, 1-based.
|
|
160
|
+
* @param column - terminal column, 1-based.
|
|
161
|
+
*/
|
|
162
|
+
mouseDown(row: number, column: number): void;
|
|
163
|
+
/**
|
|
164
|
+
* Extend the selection to where the pointer moved.
|
|
165
|
+
* @param row - terminal row, 1-based.
|
|
166
|
+
* @param column - terminal column, 1-based.
|
|
167
|
+
*/
|
|
168
|
+
mouseDrag(row: number, column: number): void;
|
|
169
|
+
/**
|
|
170
|
+
* Finish the gesture.
|
|
171
|
+
*
|
|
172
|
+
* The highlight stays up — the copy already happened, and the marks show
|
|
173
|
+
* what it took — until the next click or reflow dismisses it.
|
|
174
|
+
* @returns the selected text, or undefined for a bare click.
|
|
175
|
+
*/
|
|
176
|
+
mouseUp(): string | undefined;
|
|
177
|
+
/** The selection's bounds in order, top-left first. */
|
|
178
|
+
private orderedSelection;
|
|
179
|
+
/** The plain text under the selection, visual rows joined by newlines. */
|
|
180
|
+
private selectedText;
|
|
181
|
+
/**
|
|
182
|
+
* Map a terminal position to a physical buffer position.
|
|
183
|
+
* @param row - terminal row, 1-based.
|
|
184
|
+
* @param column - terminal column, 1-based.
|
|
185
|
+
* @param clamp - pull an outside position to the nearest content row, the
|
|
186
|
+
* way dragging past an edge keeps selecting, instead of refusing it.
|
|
187
|
+
* @returns the position, or undefined when it misses the content.
|
|
188
|
+
*/
|
|
189
|
+
private locate;
|
|
190
|
+
/** Rows the transcript viewport occupies. */
|
|
191
|
+
private viewportHeight;
|
|
192
|
+
/** Columns content is laid out for, one short of the width so no row wraps. */
|
|
193
|
+
private contentColumns;
|
|
194
|
+
/** Re-wrap every kept line at the current width. */
|
|
195
|
+
private rewrap;
|
|
196
|
+
/**
|
|
197
|
+
* Compose and paint the frame.
|
|
198
|
+
*
|
|
199
|
+
* The viewport is padded at the top when the transcript is shorter than the
|
|
200
|
+
* screen, which is what puts the chrome at the bottom from the first frame
|
|
201
|
+
* rather than wherever output happened to reach.
|
|
202
|
+
*/
|
|
203
|
+
private render;
|
|
204
|
+
}
|
package/lib/types/selector.d.ts
CHANGED
package/lib/types/spinner.d.ts
CHANGED
|
@@ -48,9 +48,15 @@ export declare class Spinner {
|
|
|
48
48
|
/**
|
|
49
49
|
* Start the indicator, or do nothing when it is already running or the
|
|
50
50
|
* surface has no cursor to rewrite.
|
|
51
|
+
*
|
|
52
|
+
* The clock keeps running across a pause: the elapsed figure is the whole
|
|
53
|
+
* turn's, and an indicator that restarted from zero at every tool call would
|
|
54
|
+
* report each step instead.
|
|
51
55
|
*/
|
|
52
56
|
start(): void;
|
|
53
|
-
/**
|
|
57
|
+
/** Hide the indicator without forgetting when the turn began. */
|
|
58
|
+
pause(): void;
|
|
59
|
+
/** Stop the indicator: the turn is over, and the next one starts at zero. */
|
|
54
60
|
stop(): void;
|
|
55
61
|
/** Paint the current frame. */
|
|
56
62
|
private draw;
|
package/lib/types/theme.d.ts
CHANGED
|
@@ -50,11 +50,12 @@ export declare function createTheme(isTty: boolean, env: Record<string, string |
|
|
|
50
50
|
/**
|
|
51
51
|
* Display columns a string occupies once printed, ignoring styling sequences.
|
|
52
52
|
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
53
|
+
* Measured by `string-width` — the width authority cli-table3, ink, and every
|
|
54
|
+
* maintained terminal renderer sit on — so East Asian Wide, emoji presentation
|
|
55
|
+
* (`⚡` included), combining marks, and ZWJ sequences all match what a
|
|
56
|
+
* terminal's cursor actually does. A hand-kept range table here mis-sized `⚡`
|
|
57
|
+
* and sheared a real table's columns; widths are exactly the kind of data
|
|
58
|
+
* nobody should maintain by hand.
|
|
58
59
|
* @param text - the string to measure, possibly carrying SGR sequences.
|
|
59
60
|
* @returns the number of display columns.
|
|
60
61
|
*/
|
|
@@ -42,8 +42,8 @@ export declare class Transcript {
|
|
|
42
42
|
private readonly options;
|
|
43
43
|
private readonly presenters;
|
|
44
44
|
private readonly calls;
|
|
45
|
-
/** The
|
|
46
|
-
private
|
|
45
|
+
/** The full form of the event just rendered, when its body was collapsed. */
|
|
46
|
+
private fold;
|
|
47
47
|
constructor(options: TranscriptOptions, presenters: ToolPresenters);
|
|
48
48
|
/**
|
|
49
49
|
* Shorten an absolute path inside the workspace to a workspace-relative one.
|
|
@@ -88,14 +88,14 @@ export declare class Transcript {
|
|
|
88
88
|
*/
|
|
89
89
|
private renderResult;
|
|
90
90
|
/**
|
|
91
|
-
* The
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* @returns the
|
|
91
|
+
* The expanded form of the lines {@link render} just returned, when that
|
|
92
|
+
* event's body was collapsed — the whole event re-rendered without its cap,
|
|
93
|
+
* because a fold swaps entire blocks, not just the clipped tail. The full
|
|
94
|
+
* body is kept from the render itself: what a tool truncated before
|
|
95
|
+
* returning is upstream of the log and unrecoverable everywhere.
|
|
96
|
+
* @returns the full lines, or undefined when nothing was collapsed.
|
|
97
97
|
*/
|
|
98
|
-
|
|
98
|
+
takeFold(): string[] | undefined;
|
|
99
99
|
/**
|
|
100
100
|
* Render one completed call's status suffix and body from its declared view.
|
|
101
101
|
* @param view - the result view, absent when no presenter answered.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display-width wrapping for styled text.
|
|
3
|
+
*
|
|
4
|
+
* Inside the alternate screen the terminal no longer wraps for us: a transcript
|
|
5
|
+
* line longer than the viewport must be broken into rows before it is painted,
|
|
6
|
+
* or it would overwrite the row below. The lines being broken are already
|
|
7
|
+
* styled, so a naive split would cut an escape sequence in half and leak
|
|
8
|
+
* gibberish — and a continuation row would lose the colour its first half set.
|
|
9
|
+
* @module codsh/src/wrap
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Break one styled line into rows no wider than `columns` display columns.
|
|
13
|
+
*
|
|
14
|
+
* Styles carry across the break: each continuation row re-opens whatever was
|
|
15
|
+
* active where the cut fell, and every row that opened a style closes it.
|
|
16
|
+
* @param text - the styled line, without a terminator.
|
|
17
|
+
* @param columns - display columns available per row.
|
|
18
|
+
* @returns the rows, at least one (an empty line yields one empty row).
|
|
19
|
+
*/
|
|
20
|
+
export declare function wrapStyled(text: string, columns: number): string[];
|
|
21
|
+
/**
|
|
22
|
+
* Wrap many lines, keeping their order.
|
|
23
|
+
* @param lines - styled lines.
|
|
24
|
+
* @param columns - display columns per row.
|
|
25
|
+
* @returns the physical rows they occupy.
|
|
26
|
+
*/
|
|
27
|
+
export declare function wrapAll(lines: readonly string[], columns: number): string[];
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codsh-cli",
|
|
3
3
|
"description": "A Claude Code-style coding agent for the terminal, composed on the DeepSeek Harness (dsh): interactive TTY surface, plan mode, approvals, custom commands, and session management over the dsh plugin runtime",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -75,7 +75,8 @@
|
|
|
75
75
|
"@deepseek-ai/dsh-workflow-worker-thread": "^0.1.0-rc.7",
|
|
76
76
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
77
77
|
"commander": "^15.0.0",
|
|
78
|
-
"diff": "^9.0.0"
|
|
78
|
+
"diff": "^9.0.0",
|
|
79
|
+
"string-width": "^8.2.2"
|
|
79
80
|
},
|
|
80
81
|
"peerDependencies": {
|
|
81
82
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
@@ -144,6 +145,7 @@
|
|
|
144
145
|
"test": "vitest run",
|
|
145
146
|
"test:e2e": "pnpm run build && vitest run --config vitest.e2e.config.ts",
|
|
146
147
|
"dev": "node scripts/dev.mjs",
|
|
148
|
+
"sync:dsh": "node scripts/sync-dsh.mjs",
|
|
147
149
|
"release": "pnpm run build && changeset publish"
|
|
148
150
|
}
|
|
149
151
|
}
|