@orkestrel/terminal 0.0.12 → 0.0.14

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.
@@ -6,22 +6,20 @@ import { FormInterface } from '@orkestrel/form';
6
6
  import { FormValues } from '@orkestrel/form';
7
7
  import { PromptTheme } from '@orkestrel/terminal';
8
8
  import { PromptThemeOptions } from '@orkestrel/terminal';
9
+ import { StreamTargetInterface } from '@orkestrel/console/server';
9
10
  import { StylerInterface } from '@orkestrel/console';
10
11
  import { TerminalInterface } from '@orkestrel/terminal';
11
12
  import { TextField } from '@orkestrel/form';
12
13
 
13
- /** A carriage return (`\r`, U+000D) — returns the cursor to column 0 so a redraw starts at the line's left edge. */
14
- export declare const CARRIAGE_RETURN: string;
15
-
16
14
  /**
17
- * Erase from the cursor down to the end of the screen (`ESC[J`) — wipes the WHOLE previous (possibly
15
+ * Erases from the cursor down to the end of the screen (`ESC[J`) — wipes the WHOLE previous (possibly
18
16
  * multi-line `select` / `checkbox`) view in one write before the new view is rendered, so a redraw
19
17
  * never leaves orphaned rows below.
20
18
  */
21
19
  export declare const CLEAR_DOWN: string;
22
20
 
23
21
  /**
24
- * The format cue appended to a field's label for each control the walk reads as a line of text —
22
+ * Holds the format cue appended to a field's label for each control the walk reads as a line of text —
25
23
  * the terminal has no date picker, no color well, and no file chooser, so the accepted shape is
26
24
  * stated instead. A control with no entry needs none: `text` and `editor` accept any line,
27
25
  * `password` masks one, and `confirm`, `select`, and `checkbox` are answered by key rather than by
@@ -30,7 +28,7 @@ export declare const CLEAR_DOWN: string;
30
28
  export declare const CONTROL_HINTS: Readonly<Partial<Record<FieldControl, string>>>;
31
29
 
32
30
  /**
33
- * Create the interactive terminal form driver — the local-keyboard arm of the terminal trio, beside
31
+ * Creates the interactive terminal form driver — the local-keyboard arm of the terminal trio, beside
34
32
  * the core headless `createPrompt` broker and the SSE `createPromptClient` bridge. Where the broker
35
33
  * PARKS a live form until somebody elsewhere answers it, a `Terminal` answers one HERE: it walks the
36
34
  * form's fields in schema order, drives each control's pure reducer over raw-mode stdin, binds every
@@ -43,7 +41,7 @@ export declare const CONTROL_HINTS: Readonly<Partial<Record<FieldControl, string
43
41
  * @remarks
44
42
  * - **The form is the unit.** `ask` takes the caller's live form and returns its settled values. The
45
43
  * returned promise is that form's own `answer`, so a caller holding the form can await either one.
46
- * - **Every control renders.** Seven controls are read as a line of text with their format cue,
44
+ * - **Every control renders.** The line-read controls are read as a line of text with their format cue,
47
45
  * `password` masks, `confirm` takes a key, `editor` takes a block, `select` and `checkbox` drive a
48
46
  * list, and an open `select` accepts a value its list does not offer.
49
47
  * - **The form decides.** A blank answer binds as absence, the form evaluates, and a refusal
@@ -74,71 +72,45 @@ export declare const CONTROL_HINTS: Readonly<Partial<Record<FieldControl, string
74
72
  */
75
73
  export declare function createTerminal(options?: TerminalOptions): TerminalInterface;
76
74
 
77
- /** The Control Sequence Introducer (`ESC[`) — the prefix of every cursor / erase sequence. */
78
- export declare const CSI: string;
79
-
80
75
  /**
81
- * The cursor-UP sequence TEMPLATE (`ESC[{count}A`) — {@link import('./helpers.js').moveUp}
76
+ * Holds the cursor-UP sequence TEMPLATE (`ESC[{count}A`) — {@link import('./helpers.js').renderCursorUp}
82
77
  * interpolates the `{count}` placeholder with the number of lines to climb. Kept as a template so
83
78
  * the count stays out of the constant.
84
79
  */
85
80
  export declare const CSI_UP: string;
86
81
 
87
82
  /**
88
- * Hide the cursor (`ESC[?25l`) — written before the driver starts redrawing a prompt so the cursor
83
+ * Hides the cursor (`ESC[?25l`) — written before the driver starts redrawing a prompt so the cursor
89
84
  * does not flicker across the view during an in-place re-render; paired with {@link CURSOR_SHOW}.
90
85
  */
91
86
  export declare const CURSOR_HIDE: string;
92
87
 
93
- /** Show the cursor (`ESC[?25h`) — restores the cursor after a prompt resolves / cancels (the {@link CURSOR_HIDE} pair). */
88
+ /** Shows the cursor (`ESC[?25h`) — restores the cursor after a prompt resolves / cancels (the {@link CURSOR_HIDE} pair). */
94
89
  export declare const CURSOR_SHOW: string;
95
90
 
96
- /**
97
- * The choices a `select` or `checkbox` field SHOWS but refuses — the complement of
98
- * {@link enabledChoices}, rendered by {@link unavailableLine} above the list so a reader sees why a
99
- * declared choice is missing from it.
100
- *
101
- * @param choices - The field's declared choices
102
- * @returns The refused choices, in declared order
103
- */
104
- export declare function disabledChoices(choices: readonly FieldChoice[]): readonly FieldChoice[];
105
-
106
- /**
107
- * The choices a `select` or `checkbox` field actually OFFERS — the form refuses a disabled choice's
108
- * value at every door, including a fill, so the walk never puts one in front of the cursor. Pair
109
- * with {@link disabledChoices} to tell the reader what was withheld.
110
- *
111
- * @param choices - The field's declared choices
112
- * @returns The choices the walk offers, in declared order
113
- */
114
- export declare function enabledChoices(choices: readonly FieldChoice[]): readonly FieldChoice[];
115
-
116
- /** The Escape byte (ESC, U+001B) — the lead byte of every CSI cursor-control sequence below. */
117
- export declare const ESCAPE: string;
118
-
119
- /** The comma-separated multi-select hint the non-TTY `checkbox` fallback shows (the user types one or more numbers). */
91
+ /** Holds the comma-separated multi-select hint the non-TTY `checkbox` fallback shows (the user types one or more numbers). */
120
92
  export declare const FALLBACK_CHECKBOX_HINT = "Enter numbers separated by commas";
121
93
 
122
- /** The hint the non-TTY `confirm` fallback shows — a piped stream sends a whole line, so the answer is typed rather than pressed. */
94
+ /** Holds the hint the non-TTY `confirm` fallback shows — a piped stream sends a whole line, so the answer is typed rather than pressed. */
123
95
  export declare const FALLBACK_CONFIRM_HINT = "(y/n)";
124
96
 
125
- /** The hint the non-TTY `editor` fallback shows — a piped stream has no ctrl-d, so end of input finishes the block. */
97
+ /** Holds the hint the non-TTY `editor` fallback shows — a piped stream has no ctrl-d, so end of input finishes the block. */
126
98
  export declare const FALLBACK_EDITOR_HINT = "(end of input finishes)";
127
99
 
128
100
  /**
129
- * The numbered-list prompt the non-TTY {@link import('./Terminal.js').Terminal} `select` fallback
101
+ * Holds the numbered-list prompt the non-TTY {@link import('./Terminal.js').Terminal} `select` fallback
130
102
  * appends — a piped (non-terminal) stream cannot navigate with arrow keys, so the choices are
131
103
  * printed numbered and the user types one number on a single readline line.
132
104
  */
133
105
  export declare const FALLBACK_SELECT_HINT = "Enter a number";
134
106
 
135
107
  /**
136
- * Project any field the walk reads as a LINE OF TEXT into the {@link TextField} the text reducer
137
- * takes — `text` itself, and the six controls a terminal has no widget for: `number`, `date`,
108
+ * Projects any field the walk reads as a LINE OF TEXT into the {@link TextField} the text reducer
109
+ * takes — `text` itself, and the controls a terminal has no widget for: `number`, `date`,
138
110
  * `time`, `datetime`, `color`, and one `file` entry. The label carries that control's format cue
139
111
  * from {@link CONTROL_HINTS}, and a declared `default` becomes the line a bare return submits. The
140
112
  * projection carries no rule, because the AUTHORITATIVE form still evaluates the answer this line
141
- * binds; it exists only so one reducer can render seven controls.
113
+ * binds; it exists only so one reducer covers every one of them.
142
114
  *
143
115
  * @param field - The field being read
144
116
  * @returns The text field the reducer renders for it
@@ -151,14 +123,31 @@ export declare const FALLBACK_SELECT_HINT = "Enter a number";
151
123
  */
152
124
  export declare function fieldToText(field: FormField): TextField;
153
125
 
154
- /** The instruction a `file` field with `multiple` shows before its entries — one path per line, and a blank line ends the list. */
126
+ /** Holds the instruction a `file` field with `multiple` shows before its entries — one path per line, and a blank line ends the list. */
155
127
  export declare const FILE_HINT = "One path per line, blank to finish";
156
128
 
157
- /** The section header the walk writes when it enters a new field group, painted by the `message` role. */
158
- export declare function groupHeader(styler: StylerInterface, theme: PromptTheme, label: string): string;
129
+ /**
130
+ * Returns the choices a `select` or `checkbox` field SHOWS but refuses — the complement of
131
+ * {@link filterEnabled}, rendered by {@link renderUnavailableLine} above the list so a reader sees
132
+ * why a declared choice is missing from it.
133
+ *
134
+ * @param choices - The field's declared choices
135
+ * @returns The refused choices, in declared order
136
+ */
137
+ export declare function filterDisabled(choices: readonly FieldChoice[]): readonly FieldChoice[];
159
138
 
160
139
  /**
161
- * The minimal input-stream shape the driver reads — exactly the slice of a Node `tty.ReadStream` /
140
+ * Returns the choices a `select` or `checkbox` field actually OFFERS — the form refuses a disabled
141
+ * choice's value at every door, including a fill, so the walk never puts one in front of the
142
+ * cursor. Pair with {@link filterDisabled} to tell the reader what was withheld.
143
+ *
144
+ * @param choices - The field's declared choices
145
+ * @returns The choices the walk offers, in declared order
146
+ */
147
+ export declare function filterEnabled(choices: readonly FieldChoice[]): readonly FieldChoice[];
148
+
149
+ /**
150
+ * Represents the minimal input-stream shape the driver reads — exactly the slice of a Node `tty.ReadStream` /
162
151
  * `process.stdin` it touches, and no more. A {@link TerminalOptions} `input` is narrowed to this
163
152
  * through {@link import('./helpers.js').isInputStream}, never an assertion, so a test drives a whole
164
153
  * form with a hand-built fake stream that emits scripted key chunks, never touches the real
@@ -167,7 +156,7 @@ export declare function groupHeader(styler: StylerInterface, theme: PromptTheme,
167
156
  * @remarks
168
157
  * - `on(event, listener)` / `off(event, listener)` — subscribe / unsubscribe a `'data'` chunk
169
158
  * listener (the irreducible event seam; a `Buffer`, string, or `Uint8Array` chunk arrives). The
170
- * two required methods.
159
+ * required methods.
171
160
  * - `setRawMode(mode)` — switch the TTY in and out of raw mode (each keypress delivered
172
161
  * immediately, no line buffering, no echo). Present on a real `tty.ReadStream`; ABSENT on a
173
162
  * piped, non-TTY stream, and its absence (or `isTTY !== true`) selects the
@@ -186,8 +175,8 @@ export declare interface InputStreamInterface {
186
175
  }
187
176
 
188
177
  /**
189
- * Whether `value` is a usable {@link InputStreamInterface} — a record with callable `on` / `off`
190
- * `'data'` subscription methods. A total type guard (AGENTS §14): it NEVER throws and returns `false`
178
+ * Checks whether `value` is a usable {@link InputStreamInterface} — a record with callable `on` / `off`
179
+ * `'data'` subscription methods. A total type guard: it NEVER throws and returns `false`
191
180
  * for anything off-shape, so it narrows the one unavoidable input boundary (the real `process.stdin`,
192
181
  * or a fake TTY a test injects) to the exact slice the driver reads — no `as`.
193
182
  *
@@ -198,23 +187,12 @@ export declare interface InputStreamInterface {
198
187
  * fallback rather than raw mode.
199
188
  *
200
189
  * @param value - Any value crossing the boundary (a process stream, an injected fake, `unknown`)
201
- * @returns `true` when `value` has callable `on` and `off`
190
+ * @returns True if `value` has callable `on` and `off`; false otherwise
202
191
  */
203
192
  export declare function isInputStream(value: unknown): value is InputStreamInterface;
204
193
 
205
194
  /**
206
- * Whether `value` is a usable {@link OutputStreamInterface} — a record with a callable `write`. A
207
- * total type guard (AGENTS §14): it NEVER throws and returns `false` for anything off-shape, so it
208
- * narrows the output boundary (the real `process.stdout`, or a recording fake a test injects) to the
209
- * one method the driver writes through — no `as`.
210
- *
211
- * @param value - Any value crossing the boundary (a process stream, an injected fake, `unknown`)
212
- * @returns `true` when `value` has a callable `write`
213
- */
214
- export declare function isOutputStream(value: unknown): value is OutputStreamInterface;
215
-
216
- /**
217
- * Whether `value` is a Node {@link NodeJS.ReadableStream} — a total structural guard (AGENTS §14)
195
+ * Checks whether `value` is a Node {@link NodeJS.ReadableStream} — a total structural guard
218
196
  * checking for the callable `read` / `pipe` / `on` that `node:readline`'s `createInterface` requires
219
197
  * as its `input`. The non-TTY fallback narrows the resolved input stream through this before handing
220
198
  * it to readline (never an `as`), so a real piped `process.stdin` (or a `PassThrough` a test injects)
@@ -222,15 +200,12 @@ export declare function isOutputStream(value: unknown): value is OutputStreamInt
222
200
  * isn't a full readable.
223
201
  *
224
202
  * @param value - The resolved input stream (or any value crossing the boundary)
225
- * @returns `true` when `value` has the readable methods readline needs
203
+ * @returns True if `value` has the readable methods readline needs; false otherwise
226
204
  */
227
205
  export declare function isReadable(value: unknown): value is NodeJS.ReadableStream;
228
206
 
229
- /** A line feed (`\n`, U+000A) — the line terminator the driver writes after the final committed prompt view. */
230
- export declare const LINE_FEED: string;
231
-
232
207
  /**
233
- * The number of terminal LINES a rendered prompt `view` occupies — one more than its newline count
208
+ * Counts the terminal LINES a rendered prompt `view` occupies — one more than its newline count
234
209
  * (a view with no newline is a single line; N newlines span N+1 lines). The basis of the in-place
235
210
  * re-render: the driver records the line count of the view it just wrote so the next redraw knows how
236
211
  * far up to move the cursor before overwriting. Total; an empty string is one (empty) line.
@@ -240,24 +215,38 @@ export declare const LINE_FEED: string;
240
215
  */
241
216
  export declare function lineCount(view: string): number;
242
217
 
243
- /** The mark on a locked field's line — the walk renders its value and moves on, because the form refuses an edit there. */
218
+ /** Holds the mark on a locked field's line — the walk renders its value and moves on, because the form refuses an edit there. */
244
219
  export declare const LOCKED_MARK = "(locked)";
245
220
 
246
221
  /**
247
- * The read-only line a LOCKED field renders — its label, the {@link LOCKED_MARK}, and the answer the
248
- * form already holds. The walk writes this instead of a prompt, because the field is still
249
- * validated and still submitted but must not be edited here.
222
+ * Returns the full reposition-and-clear prefix to write BEFORE re-rendering a prompt view in place — given
223
+ * the line count of the PREVIOUS view, it moves the cursor up over those lines, returns it to column
224
+ * 0, and erases everything from there to the end of the screen, so the next view is drawn on a clean
225
+ * region (no orphaned rows from a taller previous view). Pure; the driver writes this immediately
226
+ * followed by the new view.
250
227
  *
251
- * @param styler - The console styler that renders each role
252
- * @param theme - The resolved prompt theme
253
- * @param label - The field's label
254
- * @param value - The held answer, from {@link valueToText}
255
- * @returns The rendered line, with no trailing space when there is nothing to show
228
+ * @remarks
229
+ * For the FIRST render `previousLines` is `1` (the cursor sits on the line the prompt opened on) so
230
+ * the prefix is just a carriage return + clear-down — the prompt draws from the current line. For a
231
+ * subsequent render it climbs `previousLines - 1` lines (the cursor is on the LAST line of the prior
232
+ * view) before clearing. Keeping the math here (not in the driver) makes the re-render unit-testable
233
+ * without a real terminal.
234
+ *
235
+ * @param previousLines - The line count of the view currently on screen (from {@link lineCount})
236
+ * @returns The control-sequence prefix to write before the new view
256
237
  */
257
- export declare function lockedLine(styler: StylerInterface, theme: PromptTheme, label: string, value: string): string;
238
+ export declare function redrawPrefix(previousLines: number): string;
258
239
 
259
240
  /**
260
- * The cursor-UP control sequence that moves the cursor up `count` lines (`ESC[{count}A`) — or the
241
+ * States what a field is told when the walk read an answer the control cannot hold — a word typed into a
242
+ * `number`, an off-list value typed into an open `select` whose choice is refused. The value binds
243
+ * as absence and this message is invalidated onto the field, so the walk re-asks it with the reason
244
+ * on screen.
245
+ */
246
+ export declare const REFUSAL_MESSAGE = "Enter a value this field accepts";
247
+
248
+ /**
249
+ * Returns the cursor-UP control sequence that moves the cursor up `count` lines (`ESC[{count}A`) — or the
261
250
  * empty string when `count` is zero or negative (no movement needed, and `ESC[0A` is a wasted write).
262
251
  * The pure step the in-place re-render uses to climb back over the previous view before clearing it.
263
252
  * Total.
@@ -265,86 +254,84 @@ export declare function lockedLine(styler: StylerInterface, theme: PromptTheme,
265
254
  * @param count - How many lines to move the cursor up
266
255
  * @returns The `ESC[{count}A` sequence, or `''` when `count <= 0`
267
256
  */
268
- export declare function moveUp(count: number): string;
257
+ export declare function renderCursorUp(count: number): string;
269
258
 
270
259
  /**
271
- * The numbered choice list the non-TTY fallback prints — a piped stream cannot navigate with arrow
272
- * keys, so each offered choice is printed with the number the reader types back. One line per
273
- * choice, with no trailing newline.
260
+ * Renders the section header the walk writes when it enters a new field group, painted by the
261
+ * `message` role.
274
262
  *
275
263
  * @param styler - The console styler that renders each role
276
264
  * @param theme - The resolved prompt theme
277
- * @param choices - The choices the walk offers, from {@link enabledChoices}
278
- * @returns The rendered list
265
+ * @param label - The group's declared label, falling back to its own name
266
+ * @returns The rendered section header
279
267
  */
280
- export declare function numberedList(styler: StylerInterface, theme: PromptTheme, choices: readonly FieldChoice[]): string;
268
+ export declare function renderGroupHeader(styler: StylerInterface, theme: PromptTheme, label: string): string;
281
269
 
282
270
  /**
283
- * The minimal output-stream shape the driver writes — exactly the slice of a Node `tty.WriteStream`
284
- * / `process.stdout` it touches. A {@link TerminalOptions} `output` is narrowed to this through
285
- * {@link import('./helpers.js').isOutputStream}, never an assertion, so a test records every byte
286
- * the walk renders and asserts the rendered content with the ANSI stripped.
271
+ * Renders the read-only line a LOCKED field shows — its label, the {@link LOCKED_MARK}, and the
272
+ * answer the form already holds. The walk writes this instead of a prompt, because the field is still
273
+ * validated and still submitted but must not be edited here.
287
274
  *
288
- * @remarks
289
- * - `write(text)` — the one required method: push a chunk (a rendered field view, a group header, a
290
- * cursor or clear sequence) to the stream. A real stream returns a backpressure boolean; the
291
- * driver ignores the return, because a prompt is human-paced, so a fake may return `void`.
292
- * - `isTTY` — present and `true` on a real terminal. The driver does not branch its rendering on
293
- * it; the styler already decided color.
275
+ * @param styler - The console styler that renders each role
276
+ * @param theme - The resolved prompt theme
277
+ * @param label - The field's label
278
+ * @param value - The held answer, from {@link valueToText}
279
+ * @returns The rendered line, with no trailing space when there is nothing to show
294
280
  */
295
- export declare interface OutputStreamInterface {
296
- write(text: string): boolean | void;
297
- readonly isTTY?: boolean;
298
- }
281
+ export declare function renderLockedLine(styler: StylerInterface, theme: PromptTheme, label: string, value: string): string;
299
282
 
300
283
  /**
301
- * Whether an input stream can be driven in RAW mode — it both reports `isTTY === true` AND exposes a
302
- * callable `setRawMode`. The {@link import('./Terminal.js').Terminal} probes this to choose its path:
303
- * `true` ⇒ the interactive raw-mode prompts (arrow-key navigation, live re-render); `false` ⇒ the
304
- * `node:readline` line-input fallback (a piped / non-terminal stream cannot enter raw mode). Total —
305
- * never throws.
284
+ * Renders the numbered choice list the non-TTY fallback prints — a piped stream cannot navigate
285
+ * with arrow keys, so each offered choice is printed with the number the reader types back. One
286
+ * line per choice, with no trailing newline.
306
287
  *
307
- * @param input - The resolved {@link InputStreamInterface}
308
- * @returns `true` when the stream is a TTY with `setRawMode`
288
+ * @param styler - The console styler that renders each role
289
+ * @param theme - The resolved prompt theme
290
+ * @param choices - The choices the walk offers, from {@link filterEnabled}
291
+ * @returns The rendered list
309
292
  */
310
- export declare function rawCapable(input: InputStreamInterface): boolean;
293
+ export declare function renderNumberedList(styler: StylerInterface, theme: PromptTheme, choices: readonly FieldChoice[]): string;
311
294
 
312
295
  /**
313
- * The full reposition-and-clear prefix to write BEFORE re-rendering a prompt view in place — given
314
- * the line count of the PREVIOUS view, it moves the cursor up over those lines, returns it to column
315
- * 0, and erases everything from there to the end of the screen, so the next view is drawn on a clean
316
- * region (no orphaned rows from a taller previous view). Pure; the driver writes this immediately
317
- * followed by the new view.
296
+ * Renders the line listing an OPEN select's offered values above its text prompt — a suggestion
297
+ * list, because an open select admits an answer the list does not offer.
318
298
  *
319
- * @remarks
320
- * For the FIRST render `previousLines` is `1` (the cursor sits on the line the prompt opened on) so
321
- * the prefix is just a carriage return + clear-down — the prompt draws from the current line. For a
322
- * subsequent render it climbs `previousLines - 1` lines (the cursor is on the LAST line of the prior
323
- * view) before clearing. Keeping the math here (not in the driver) makes the re-render unit-testable
324
- * without a real terminal.
325
- *
326
- * @param previousLines - The line count of the view currently on screen (from {@link lineCount})
327
- * @returns The control-sequence prefix to write before the new view
299
+ * @param styler - The console styler that renders each role
300
+ * @param theme - The resolved prompt theme
301
+ * @param choices - The choices the open select offers, from {@link filterEnabled}
302
+ * @returns The rendered suggestion line
328
303
  */
329
- export declare function redrawPrefix(previousLines: number): string;
304
+ export declare function renderSuggestionLine(styler: StylerInterface, theme: PromptTheme, choices: readonly FieldChoice[]): string;
330
305
 
331
306
  /**
332
- * What a field is told when the walk read an answer the control cannot hold — a word typed into a
333
- * `number`, an off-list value typed into an open `select` whose choice is refused. The value binds
334
- * as absence and this message is invalidated onto the field, so the walk re-asks it with the reason
335
- * on screen.
307
+ * Renders the line naming the choices a field shows but refuses, written above the list the walk
308
+ * drives.
309
+ *
310
+ * @param styler - The console styler that renders each role
311
+ * @param theme - The resolved prompt theme
312
+ * @param choices - The refused choices, from {@link filterDisabled}
313
+ * @returns The rendered unavailable line
336
314
  */
337
- export declare const REFUSAL_MESSAGE = "Enter a value this field accepts";
315
+ export declare function renderUnavailableLine(styler: StylerInterface, theme: PromptTheme, choices: readonly FieldChoice[]): string;
338
316
 
339
- /** The lead on the line listing an open `select`'s offered values, which a typed answer may ignore. */
317
+ /** Holds the lead on the line listing an open `select`'s offered values, which a typed answer may ignore. */
340
318
  export declare const SUGGESTION_LEAD = "Suggestions";
341
319
 
342
- /** The line listing an OPEN select's offered values above its text prompt — a suggestion list, because an open select admits an answer the list does not offer. */
343
- export declare function suggestionLine(styler: StylerInterface, theme: PromptTheme, choices: readonly FieldChoice[]): string;
320
+ /**
321
+ * Checks whether an input stream can be driven in RAW mode — it both reports `isTTY === true` AND
322
+ * exposes a callable `setRawMode`. The {@link import('./Terminal.js').Terminal} probes this to choose its path:
323
+ * `true` ⇒ the interactive raw-mode prompts (arrow-key navigation, live re-render); `false` ⇒ the
324
+ * `node:readline` line-input fallback (a piped / non-terminal stream cannot enter raw mode). Total —
325
+ * never throws.
326
+ *
327
+ * @param input - The resolved {@link InputStreamInterface}
328
+ * @returns True if the stream is a TTY with `setRawMode`; false otherwise
329
+ */
330
+ export declare function supportsRawMode(input: InputStreamInterface): boolean;
344
331
 
345
332
  /**
346
- * The interactive terminal form DRIVER — the {@link TerminalInterface} implementation for a human at
347
- * this machine's keyboard, and the only impure part of the terminal stack. {@link ask} walks one
333
+ * Implements {@link TerminalInterface} for a human at this machine's keyboard — the interactive form
334
+ * DRIVER, and the only impure part of the terminal stack. {@link ask} walks one
348
335
  * form's fields in schema order, feeds raw-mode stdin bytes through `parseKey` into the matching
349
336
  * pure reducer, renders each returned view in place, and binds every answer through the form's own
350
337
  * `fill`. It owns no form logic: the schema, the rules, the values, and the settlement all belong to
@@ -353,7 +340,7 @@ export declare function suggestionLine(styler: StylerInterface, theme: PromptThe
353
340
  * @remarks
354
341
  * See {@link TerminalInterface} for the driving contract. The walk itself:
355
342
  *
356
- * - **Twelve controls, seven reducers.** `text`, `number`, `date`, `time`, `datetime`, `color`, and
343
+ * - **Controls and reducers.** `text`, `number`, `date`, `time`, `datetime`, `color`, and
357
344
  * each `file` entry are read as one line of text through {@link fieldToText}, which appends that
358
345
  * control's format cue to the label. `password`, `confirm`, `editor`, `select`, and `checkbox`
359
346
  * each drive their own reducer. An open `select` is a suggestion list plus a typed line, because
@@ -382,7 +369,7 @@ export declare class Terminal implements TerminalInterface {
382
369
  }
383
370
 
384
371
  /**
385
- * Options for {@link import('./factories.js').createTerminal} — every member optional, so a bare
372
+ * Configures {@link import('./factories.js').createTerminal} — every member optional, so a bare
386
373
  * `createTerminal()` walks a form over the real `process.stdin` / `process.stdout` with the default
387
374
  * theme.
388
375
  *
@@ -392,8 +379,10 @@ export declare class Terminal implements TerminalInterface {
392
379
  * {@link import('./helpers.js').isInputStream}, so a test injects a fake TTY that emits scripted
393
380
  * `'data'` chunks. A stream that is not a TTY falls back to `node:readline` line input.
394
381
  * - `output` — the stream each view is rendered to; defaults to `process.stdout`. Any
395
- * {@link OutputStreamInterface}-shaped stream is accepted, resolved through
396
- * {@link import('./helpers.js').isOutputStream}, so a test records the rendered output.
382
+ * {@link import('@orkestrel/console/server').StreamTargetInterface}-shaped stream is accepted,
383
+ * resolved through {@link import('@orkestrel/console/server').isStreamTarget}, so a test records
384
+ * the rendered output. That is the console module's own boundary shape, so one target drives a
385
+ * console sink and this driver alike.
397
386
  * - `theme` — the glyphs and role styles every rendered line is painted with, merged over
398
387
  * {@link import('@src/core').DEFAULT_PROMPT_THEME} leaf by leaf by
399
388
  * {@link import('@src/core').createPromptTheme}. Supplying one icon or one role leaves every
@@ -401,18 +390,15 @@ export declare class Terminal implements TerminalInterface {
401
390
  */
402
391
  export declare interface TerminalOptions {
403
392
  readonly input?: InputStreamInterface;
404
- readonly output?: OutputStreamInterface;
393
+ readonly output?: StreamTargetInterface;
405
394
  readonly theme?: PromptThemeOptions;
406
395
  }
407
396
 
408
- /** The lead on the line listing the choices a `select` or `checkbox` shows but refuses, so a reader sees why one is missing from the list below. */
397
+ /** Holds the lead on the line listing the choices a `select` or `checkbox` shows but refuses, so a reader sees why one is missing from the list below. */
409
398
  export declare const UNAVAILABLE_LEAD = "Unavailable";
410
399
 
411
- /** The line naming the choices a field shows but refuses, written above the list the walk drives. */
412
- export declare function unavailableLine(styler: StylerInterface, theme: PromptTheme, choices: readonly FieldChoice[]): string;
413
-
414
400
  /**
415
- * Project one held answer into the text a read-only line shows — a scalar as itself, a boolean as
401
+ * Projects one held answer into the text a read-only line shows — a scalar as itself, a boolean as
416
402
  * `yes` / `no` (the word the confirm reducer commits), and a list joined by commas. Absence renders
417
403
  * as nothing, because a locked field nobody has answered has nothing to show.
418
404
  *