@sagmans/dsh-tui 0.2.0 → 0.4.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 +282 -25
- package/cordis.patch.yml +14 -0
- package/lib/agent/model.d.ts +26 -0
- package/lib/agent/model.d.ts.map +1 -1
- package/lib/agent/model.js +41 -0
- package/lib/agent/model.js.map +1 -1
- package/lib/agent/projections.d.ts +22 -6
- package/lib/agent/projections.d.ts.map +1 -1
- package/lib/agent/projections.js +21 -10
- package/lib/agent/projections.js.map +1 -1
- package/lib/agent/prompt-history.d.ts +89 -0
- package/lib/agent/prompt-history.d.ts.map +1 -0
- package/lib/agent/prompt-history.js +380 -0
- package/lib/agent/prompt-history.js.map +1 -0
- package/lib/agent/status.d.ts +16 -0
- package/lib/agent/status.d.ts.map +1 -1
- package/lib/agent/status.js +3 -0
- package/lib/agent/status.js.map +1 -1
- package/lib/cards.d.ts +1 -1
- package/lib/cards.d.ts.map +1 -1
- package/lib/cards.js +4 -3
- package/lib/cards.js.map +1 -1
- package/lib/gates.d.ts +145 -2
- package/lib/gates.d.ts.map +1 -1
- package/lib/gates.js +335 -40
- package/lib/gates.js.map +1 -1
- package/lib/herdr/client.d.ts +57 -0
- package/lib/herdr/client.d.ts.map +1 -0
- package/lib/herdr/client.js +218 -0
- package/lib/herdr/client.js.map +1 -0
- package/lib/herdr/constants.d.ts +93 -0
- package/lib/herdr/constants.d.ts.map +1 -0
- package/lib/herdr/constants.js +79 -0
- package/lib/herdr/constants.js.map +1 -0
- package/lib/herdr/reporter.d.ts +60 -0
- package/lib/herdr/reporter.d.ts.map +1 -0
- package/lib/herdr/reporter.js +217 -0
- package/lib/herdr/reporter.js.map +1 -0
- package/lib/herdr/state.d.ts +57 -0
- package/lib/herdr/state.d.ts.map +1 -0
- package/lib/herdr/state.js +62 -0
- package/lib/herdr/state.js.map +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +631 -118
- package/lib/index.js.map +1 -1
- package/lib/input/actions.d.ts +136 -0
- package/lib/input/actions.d.ts.map +1 -0
- package/lib/input/actions.js +743 -0
- package/lib/input/actions.js.map +1 -0
- package/lib/input/ghost.d.ts +46 -0
- package/lib/input/ghost.d.ts.map +1 -0
- package/lib/input/ghost.js +58 -0
- package/lib/input/ghost.js.map +1 -0
- package/lib/input/keymap-settings.d.ts +21 -0
- package/lib/input/keymap-settings.d.ts.map +1 -0
- package/lib/input/keymap-settings.js +28 -0
- package/lib/input/keymap-settings.js.map +1 -0
- package/lib/input/keymap.d.ts +91 -0
- package/lib/input/keymap.d.ts.map +1 -0
- package/lib/input/keymap.js +172 -0
- package/lib/input/keymap.js.map +1 -0
- package/lib/input/match.d.ts +10 -0
- package/lib/input/match.d.ts.map +1 -0
- package/lib/input/match.js +83 -0
- package/lib/input/match.js.map +1 -0
- package/lib/input/submission.d.ts +29 -1
- package/lib/input/submission.d.ts.map +1 -1
- package/lib/input/submission.js +33 -2
- package/lib/input/submission.js.map +1 -1
- package/lib/input.d.ts +17 -0
- package/lib/input.d.ts.map +1 -0
- package/lib/input.js +27 -0
- package/lib/input.js.map +1 -0
- package/lib/keys-command.d.ts +16 -0
- package/lib/keys-command.d.ts.map +1 -0
- package/lib/keys-command.js +60 -0
- package/lib/keys-command.js.map +1 -0
- package/lib/queue.d.ts +6 -0
- package/lib/queue.d.ts.map +1 -0
- package/lib/queue.js +68 -0
- package/lib/queue.js.map +1 -0
- package/lib/settings-notice.d.ts +19 -0
- package/lib/settings-notice.d.ts.map +1 -0
- package/lib/settings-notice.js +30 -0
- package/lib/settings-notice.js.map +1 -0
- package/lib/stash/lock.d.ts +72 -0
- package/lib/stash/lock.d.ts.map +1 -0
- package/lib/stash/lock.js +374 -0
- package/lib/stash/lock.js.map +1 -0
- package/lib/stash/paths.d.ts +22 -0
- package/lib/stash/paths.d.ts.map +1 -0
- package/lib/stash/paths.js +83 -0
- package/lib/stash/paths.js.map +1 -0
- package/lib/stash/private-fs.d.ts +61 -0
- package/lib/stash/private-fs.d.ts.map +1 -0
- package/lib/stash/private-fs.js +379 -0
- package/lib/stash/private-fs.js.map +1 -0
- package/lib/stash/schema.d.ts +57 -0
- package/lib/stash/schema.d.ts.map +1 -0
- package/lib/stash/schema.js +127 -0
- package/lib/stash/schema.js.map +1 -0
- package/lib/stash/store.d.ts +92 -0
- package/lib/stash/store.d.ts.map +1 -0
- package/lib/stash/store.js +262 -0
- package/lib/stash/store.js.map +1 -0
- package/lib/stash.d.ts +91 -0
- package/lib/stash.d.ts.map +1 -0
- package/lib/stash.js +296 -0
- package/lib/stash.js.map +1 -0
- package/lib/theme-settings.d.ts +72 -8
- package/lib/theme-settings.d.ts.map +1 -1
- package/lib/theme-settings.js +125 -5
- package/lib/theme-settings.js.map +1 -1
- package/lib/theme-tokens.d.ts +4 -4
- package/lib/theme-tokens.d.ts.map +1 -1
- package/lib/theme-tokens.js +39 -5
- package/lib/theme-tokens.js.map +1 -1
- package/lib/todo-guard.d.ts +116 -0
- package/lib/todo-guard.d.ts.map +1 -0
- package/lib/todo-guard.js +259 -0
- package/lib/todo-guard.js.map +1 -0
- package/lib/ui/editor.d.ts +58 -1
- package/lib/ui/editor.d.ts.map +1 -1
- package/lib/ui/editor.js +174 -19
- package/lib/ui/editor.js.map +1 -1
- package/lib/ui/frame.d.ts +54 -0
- package/lib/ui/frame.d.ts.map +1 -0
- package/lib/ui/frame.js +79 -0
- package/lib/ui/frame.js.map +1 -0
- package/lib/ui/gate-input.d.ts +20 -0
- package/lib/ui/gate-input.d.ts.map +1 -0
- package/lib/ui/gate-input.js +96 -0
- package/lib/ui/gate-input.js.map +1 -0
- package/lib/ui/history-picker.d.ts +15 -0
- package/lib/ui/history-picker.d.ts.map +1 -0
- package/lib/ui/history-picker.js +26 -0
- package/lib/ui/history-picker.js.map +1 -0
- package/lib/ui/markdown.d.ts +11 -2
- package/lib/ui/markdown.d.ts.map +1 -1
- package/lib/ui/markdown.js +25 -6
- package/lib/ui/markdown.js.map +1 -1
- package/lib/ui/mermaid.d.ts +44 -0
- package/lib/ui/mermaid.d.ts.map +1 -0
- package/lib/ui/mermaid.js +178 -0
- package/lib/ui/mermaid.js.map +1 -0
- package/lib/ui/picker.d.ts +40 -8
- package/lib/ui/picker.d.ts.map +1 -1
- package/lib/ui/picker.js +79 -24
- package/lib/ui/picker.js.map +1 -1
- package/lib/ui/prompt.d.ts +32 -0
- package/lib/ui/prompt.d.ts.map +1 -0
- package/lib/ui/prompt.js +49 -0
- package/lib/ui/prompt.js.map +1 -0
- package/lib/ui/queue.d.ts +24 -0
- package/lib/ui/queue.d.ts.map +1 -0
- package/lib/ui/queue.js +57 -0
- package/lib/ui/queue.js.map +1 -0
- package/lib/ui/stash-picker.d.ts +43 -0
- package/lib/ui/stash-picker.d.ts.map +1 -0
- package/lib/ui/stash-picker.js +78 -0
- package/lib/ui/stash-picker.js.map +1 -0
- package/lib/ui/status.d.ts +6 -0
- package/lib/ui/status.d.ts.map +1 -1
- package/lib/ui/status.js +15 -0
- package/lib/ui/status.js.map +1 -1
- package/lib/ui/view.d.ts +22 -2
- package/lib/ui/view.d.ts.map +1 -1
- package/lib/ui/view.js +84 -24
- package/lib/ui/view.js.map +1 -1
- package/lib/work.d.ts +51 -1
- package/lib/work.d.ts.map +1 -1
- package/lib/work.js +53 -1
- package/lib/work.js.map +1 -1
- package/package.json +7 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"todo-guard.js","sourceRoot":"","sources":["../src/todo-guard.ts"],"names":[],"mappings":"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"}
|
package/lib/ui/editor.d.ts
CHANGED
|
@@ -1,4 +1,29 @@
|
|
|
1
1
|
import { Editor, type EditorTheme, type TUI, type TuiMouseEvent, type TuiMouseEventResult } from '@earendil-works/pi-tui';
|
|
2
|
+
import { type Keymap } from '../input/actions.ts';
|
|
3
|
+
import { type EditorCursor } from '../input/ghost.ts';
|
|
4
|
+
/** Which run of a ghost is being drawn: the cursor cell, or the text after it. */
|
|
5
|
+
export type GhostCell = 'cursor' | 'rest';
|
|
6
|
+
/** What the bar knows when it asks for a suggestion. */
|
|
7
|
+
export interface GhostRequest {
|
|
8
|
+
readonly text: string;
|
|
9
|
+
readonly lines: readonly string[];
|
|
10
|
+
readonly cursor: EditorCursor;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The recorded-prompt suggestion the bar offers, and how to draw it.
|
|
14
|
+
*
|
|
15
|
+
* Kept as a collaborator rather than built in: the editor owns where the text
|
|
16
|
+
* goes, while the surface owns where the entries come from and which shade they
|
|
17
|
+
* are drawn in, so neither has to know the other's world.
|
|
18
|
+
*/
|
|
19
|
+
export interface GhostBrush {
|
|
20
|
+
/** Whether the affordance may draw at all; colour and settings decide this. */
|
|
21
|
+
enabled(): boolean;
|
|
22
|
+
/** The suffix to offer after the typed text, or undefined for none. */
|
|
23
|
+
suggestion(input: GhostRequest): string | undefined;
|
|
24
|
+
/** Paint one run of the suffix; '' leaves the run undrawn. */
|
|
25
|
+
paint(text: string, cell: GhostCell): string;
|
|
26
|
+
}
|
|
2
27
|
/**
|
|
3
28
|
* The input bar drawn as a box, with its completion menu above it.
|
|
4
29
|
*
|
|
@@ -8,17 +33,49 @@ import { Editor, type EditorTheme, type TUI, type TuiMouseEvent, type TuiMouseEv
|
|
|
8
33
|
* while writing: a list that grows upward never covers the line being typed.
|
|
9
34
|
*/
|
|
10
35
|
export declare class BoxedEditor extends Editor {
|
|
36
|
+
private readonly keymap;
|
|
37
|
+
private readonly ghost?;
|
|
11
38
|
/** Rows the menu took in the last render; a click there belongs to the list. */
|
|
12
39
|
private menuRows;
|
|
13
40
|
/** Rows between the rules in the last render; how the base editor counts its own. */
|
|
14
41
|
private textRows;
|
|
15
42
|
/** Whether the last render drew a frame, which is what a click is mapped through. */
|
|
16
43
|
private boxed;
|
|
17
|
-
constructor(tui: TUI, theme: EditorTheme);
|
|
44
|
+
constructor(tui: TUI, theme: EditorTheme, keymap?: () => Keymap, ghost?: GhostBrush | undefined);
|
|
45
|
+
/**
|
|
46
|
+
* Read the two presses the base class cannot place on its own.
|
|
47
|
+
*
|
|
48
|
+
* Enter breaks the line while the reader keeps that key for the line, so the
|
|
49
|
+
* press the base would submit with becomes a newline — except while it is
|
|
50
|
+
* picking a completion, the one press that chooses something instead of
|
|
51
|
+
* sending it, and except on a bar a question or a picker has borrowed, whose
|
|
52
|
+
* keys belong to whoever borrowed it.
|
|
53
|
+
*/
|
|
54
|
+
handleInput(data: string): void;
|
|
18
55
|
/** Tag the closing rule so {@link render} can find where the box ends. */
|
|
19
56
|
protected renderBottomBorder(width: number, hiddenLineCount: number): string;
|
|
20
57
|
/** A rule redrawn as a box edge, so the corners carry the border's own colour. */
|
|
21
58
|
private edge;
|
|
59
|
+
/**
|
|
60
|
+
* Rewrite one row of the text before it is framed, so a subclass can hide
|
|
61
|
+
* what it collects without hiding the box the reader types inside.
|
|
62
|
+
*/
|
|
63
|
+
protected decorateText(row: string): string;
|
|
64
|
+
/** The suggestion to draw this paint, or undefined when the brush offers none. */
|
|
65
|
+
private currentGhostSuffix;
|
|
66
|
+
/**
|
|
67
|
+
* Replace the end-of-text cursor cell with the offered suffix.
|
|
68
|
+
*
|
|
69
|
+
* The base draws the cursor as one reverse-video cell and pads the rest of the
|
|
70
|
+
* row; the suffix takes that cell plus the padding after it, so the row keeps
|
|
71
|
+
* exactly its width and the cursor stays where the reader is typing.
|
|
72
|
+
*/
|
|
73
|
+
private ghostRow;
|
|
74
|
+
/**
|
|
75
|
+
* Take a ghost-accepting key before the base sees it, so the key returns to
|
|
76
|
+
* its own meaning the moment nothing is offered.
|
|
77
|
+
*/
|
|
78
|
+
private acceptGhost;
|
|
22
79
|
render(width: number): string[];
|
|
23
80
|
/**
|
|
24
81
|
* Map a click back through the frame and the lifted menu.
|
package/lib/ui/editor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../src/ui/editor.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../src/ui/editor.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EAMN,KAAK,WAAW,EAChB,KAAK,GAAG,EACR,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACzB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAA4B,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAC3E,OAAO,EAAsE,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAmDzH,kFAAkF;AAClF,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAA;AAEzC,wDAAwD;AACxD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;CAC9B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,+EAA+E;IAC/E,OAAO,IAAI,OAAO,CAAA;IAClB,uEAAuE;IACvE,UAAU,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAAA;IACnD,8DAA8D;IAC9D,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,MAAM,CAAA;CAC7C;AAED;;;;;;;GAOG;AACH,qBAAa,WAAY,SAAQ,MAAM;IAWnC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;IAXzB,gFAAgF;IAChF,OAAO,CAAC,QAAQ,CAAI;IACpB,qFAAqF;IACrF,OAAO,CAAC,QAAQ,CAAI;IACpB,qFAAqF;IACrF,OAAO,CAAC,KAAK,CAAQ;gBAGnB,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,WAAW,EACD,MAAM,GAAE,MAAM,MAAsB,EACpC,KAAK,CAAC,EAAE,UAAU,YAAA;IAKrC;;;;;;;;OAQG;IACM,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAkCxC,0EAA0E;cACvD,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,MAAM;IAIrF,kFAAkF;IAClF,OAAO,CAAC,IAAI;IAIZ;;;OAGG;IACH,SAAS,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAI3C,kFAAkF;IAClF,OAAO,CAAC,kBAAkB;IAiB1B;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ;IA0BhB;;;OAGG;IACH,OAAO,CAAC,WAAW;IAgBV,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IA4BxC;;;;;;;;OAQG;IACM,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,mBAAmB,GAAG,SAAS;CAQ5E"}
|
package/lib/ui/editor.js
CHANGED
|
@@ -1,16 +1,9 @@
|
|
|
1
|
-
import { Editor, stripTerminalSequences, } from '@earendil-works/pi-tui';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const SIDE = '│';
|
|
8
|
-
/** The frame costs one column each side, so the text wraps that much narrower. */
|
|
9
|
-
const FRAME_COLUMNS = 2;
|
|
10
|
-
/** One column of air inside the frame, so a full line never touches the border. */
|
|
11
|
-
const PADDING_X = 1;
|
|
12
|
-
/** A box needs both edges, both paddings, and one column left to type in. */
|
|
13
|
-
const MIN_BOX_WIDTH = FRAME_COLUMNS + PADDING_X * 2 + 1;
|
|
1
|
+
import { Editor, getKeybindings, isKittyProtocolActive, matchesKey, stripTerminalSequences, visibleWidth, } from '@earendil-works/pi-tui';
|
|
2
|
+
import { ENTER_KEY, defaultKeymap } from "../input/actions.js";
|
|
3
|
+
import { ghostDisplayLine, ghostGraphemes, isCursorAtTextEnd, nextGhostWord } from "../input/ghost.js";
|
|
4
|
+
import { promptKeys } from "../input/keymap.js";
|
|
5
|
+
import { displayText } from "../text.js";
|
|
6
|
+
import { FRAME_COLUMNS, FRAME_GLYPHS, MIN_BOX_WIDTH, PADDING_X } from "./frame.js";
|
|
14
7
|
/**
|
|
15
8
|
* Marks the rule that closes the input while one render is in flight.
|
|
16
9
|
*
|
|
@@ -22,6 +15,36 @@ const MIN_BOX_WIDTH = FRAME_COLUMNS + PADDING_X * 2 + 1;
|
|
|
22
15
|
* pass removes it.
|
|
23
16
|
*/
|
|
24
17
|
const CLOSING_TAG = '\u0000';
|
|
18
|
+
/**
|
|
19
|
+
* The line feed a plain Enter is handed to the base class as.
|
|
20
|
+
*
|
|
21
|
+
* The base owns what a newline does to paste markers, history, and undo, and it
|
|
22
|
+
* reads this byte as one in every terminal mode, including the modes where it
|
|
23
|
+
* no longer calls Enter itself a newline.
|
|
24
|
+
*/
|
|
25
|
+
const NEWLINE_BYTE = '\n';
|
|
26
|
+
/** The one sequence a terminal that reports no modifiers sends for alt+enter. */
|
|
27
|
+
const LEGACY_ALT_ENTER = '\u001b\r';
|
|
28
|
+
/**
|
|
29
|
+
* The chords as the keyboard protocol spells them.
|
|
30
|
+
*
|
|
31
|
+
* The base class reads a legacy alt+enter and a bare line feed as line breaks
|
|
32
|
+
* before it looks at any binding, so each press is handed over as the chord the
|
|
33
|
+
* map actually holds for it. The submit path then runs whole, with the same
|
|
34
|
+
* guards a real chord meets, instead of a second sender beside it.
|
|
35
|
+
*/
|
|
36
|
+
const KITTY_ALT_ENTER = '\u001b[13;3u';
|
|
37
|
+
const KITTY_CTRL_J = '\u001b[106;5u';
|
|
38
|
+
/**
|
|
39
|
+
* The cell the base editor draws for a cursor parked past the last character.
|
|
40
|
+
*
|
|
41
|
+
* Ghost text takes that cell over: the base offers no seam for "style the text
|
|
42
|
+
* after the cursor", so one paint pass finds the cell it drew and replaces the
|
|
43
|
+
* padding after it. The cell looks the same when the cursor sits over a typed
|
|
44
|
+
* space, which is why a caller also confirms the cursor is past the last
|
|
45
|
+
* character before treating the sequence as padding.
|
|
46
|
+
*/
|
|
47
|
+
const CURSOR_AT_END = '\u001b[7m \u001b[0m';
|
|
25
48
|
/**
|
|
26
49
|
* The input bar drawn as a box, with its completion menu above it.
|
|
27
50
|
*
|
|
@@ -31,14 +54,61 @@ const CLOSING_TAG = '\u0000';
|
|
|
31
54
|
* while writing: a list that grows upward never covers the line being typed.
|
|
32
55
|
*/
|
|
33
56
|
export class BoxedEditor extends Editor {
|
|
57
|
+
keymap;
|
|
58
|
+
ghost;
|
|
34
59
|
/** Rows the menu took in the last render; a click there belongs to the list. */
|
|
35
60
|
menuRows = 0;
|
|
36
61
|
/** Rows between the rules in the last render; how the base editor counts its own. */
|
|
37
62
|
textRows = 0;
|
|
38
63
|
/** Whether the last render drew a frame, which is what a click is mapped through. */
|
|
39
64
|
boxed = false;
|
|
40
|
-
constructor(tui, theme) {
|
|
65
|
+
constructor(tui, theme, keymap = defaultKeymap, ghost) {
|
|
41
66
|
super(tui, theme, { paddingX: PADDING_X });
|
|
67
|
+
this.keymap = keymap;
|
|
68
|
+
this.ghost = ghost;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Read the two presses the base class cannot place on its own.
|
|
72
|
+
*
|
|
73
|
+
* Enter breaks the line while the reader keeps that key for the line, so the
|
|
74
|
+
* press the base would submit with becomes a newline — except while it is
|
|
75
|
+
* picking a completion, the one press that chooses something instead of
|
|
76
|
+
* sending it, and except on a bar a question or a picker has borrowed, whose
|
|
77
|
+
* keys belong to whoever borrowed it.
|
|
78
|
+
*/
|
|
79
|
+
handleInput(data) {
|
|
80
|
+
if (this.disableSubmit) {
|
|
81
|
+
super.handleInput(data);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (this.acceptGhost(data))
|
|
85
|
+
return;
|
|
86
|
+
const keys = promptKeys(this.keymap());
|
|
87
|
+
// A bare line feed is a line break in the base class whatever the map says,
|
|
88
|
+
// so a reader who moved the line break off ctrl+j and sends with it would
|
|
89
|
+
// keep getting lines. It is read before the Return guard because a terminal
|
|
90
|
+
// without the protocol reports that byte as Return as well — and only
|
|
91
|
+
// without it, because a terminal that speaks the protocol sends the chord
|
|
92
|
+
// itself and reports a line feed for a key the reader meant as a line.
|
|
93
|
+
if (data === NEWLINE_BYTE && !isKittyProtocolActive() && keys.submit.includes('ctrl+j')) {
|
|
94
|
+
super.handleInput(KITTY_CTRL_J);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (keys.enterBreaksLine && matchesKey(data, ENTER_KEY) && !this.isShowingAutocomplete()) {
|
|
98
|
+
super.handleInput(NEWLINE_BYTE);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// Only while the terminal cannot tell alt+enter apart from a mapping, and
|
|
102
|
+
// only for the reader who bound that key: with the protocol active the same
|
|
103
|
+
// sequence is the reader's shift+enter, and a bar that answered it as a send
|
|
104
|
+
// because some other chord contains the same bytes would be submitting a key
|
|
105
|
+
// the reader never bound. A reader who keeps the sequence for a line is
|
|
106
|
+
// answered by the library's own rule.
|
|
107
|
+
if (data === LEGACY_ALT_ENTER && !isKittyProtocolActive() && keys.submit.includes('alt+enter')) {
|
|
108
|
+
super.handleInput(KITTY_ALT_ENTER);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
super.handleInput(data);
|
|
42
112
|
}
|
|
43
113
|
/** Tag the closing rule so {@link render} can find where the box ends. */
|
|
44
114
|
renderBottomBorder(width, hiddenLineCount) {
|
|
@@ -48,14 +118,99 @@ export class BoxedEditor extends Editor {
|
|
|
48
118
|
edge(open, close, row) {
|
|
49
119
|
return this.borderColor(`${open}${stripTerminalSequences(row)}${close}`);
|
|
50
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Rewrite one row of the text before it is framed, so a subclass can hide
|
|
123
|
+
* what it collects without hiding the box the reader types inside.
|
|
124
|
+
*/
|
|
125
|
+
decorateText(row) {
|
|
126
|
+
return row;
|
|
127
|
+
}
|
|
128
|
+
/** The suggestion to draw this paint, or undefined when the brush offers none. */
|
|
129
|
+
currentGhostSuffix() {
|
|
130
|
+
const brush = this.ghost;
|
|
131
|
+
if (brush === undefined || !brush.enabled())
|
|
132
|
+
return undefined;
|
|
133
|
+
const lines = this.getLines();
|
|
134
|
+
const cursor = this.getCursor();
|
|
135
|
+
// A cursor parked over a typed space draws the same cell as one at the very
|
|
136
|
+
// end, so the position has to be confirmed before a brush is asked at all.
|
|
137
|
+
if (!isCursorAtTextEnd({ lines, cursor }))
|
|
138
|
+
return undefined;
|
|
139
|
+
// The brush is a collaborator this class does not own, and its answer is
|
|
140
|
+
// both drawn and inserted: a control sequence would reach the terminal or
|
|
141
|
+
// the bar itself. The store refuses them too, so this only has to hold for
|
|
142
|
+
// any other brush. The expanded text is what was actually written; a large
|
|
143
|
+
// paste is stored as a marker and would otherwise match nothing.
|
|
144
|
+
const suffix = brush.suggestion({ text: this.getExpandedText(), lines, cursor });
|
|
145
|
+
return suffix === undefined ? undefined : displayText(suffix);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Replace the end-of-text cursor cell with the offered suffix.
|
|
149
|
+
*
|
|
150
|
+
* The base draws the cursor as one reverse-video cell and pads the rest of the
|
|
151
|
+
* row; the suffix takes that cell plus the padding after it, so the row keeps
|
|
152
|
+
* exactly its width and the cursor stays where the reader is typing.
|
|
153
|
+
*/
|
|
154
|
+
ghostRow(row, suffix) {
|
|
155
|
+
const brush = this.ghost;
|
|
156
|
+
if (brush === undefined || suffix === undefined)
|
|
157
|
+
return row;
|
|
158
|
+
const cursorAt = row.indexOf(CURSOR_AT_END);
|
|
159
|
+
if (cursorAt < 0)
|
|
160
|
+
return row;
|
|
161
|
+
const before = row.slice(0, cursorAt);
|
|
162
|
+
const pad = visibleWidth(row.slice(cursorAt + CURSOR_AT_END.length));
|
|
163
|
+
// The freed cursor cell is one more column the suffix may fill.
|
|
164
|
+
const room = pad + 1;
|
|
165
|
+
// A width-aware cut can split a wide grapheme or return the escape that
|
|
166
|
+
// closed a style, so the fit is measured one grapheme at a time; when even
|
|
167
|
+
// the first one is too wide, the row keeps the cursor the base drew.
|
|
168
|
+
let drawn = '';
|
|
169
|
+
let used = 0;
|
|
170
|
+
for (const grapheme of ghostGraphemes(ghostDisplayLine(suffix))) {
|
|
171
|
+
const width = visibleWidth(grapheme);
|
|
172
|
+
if (used + width > room)
|
|
173
|
+
break;
|
|
174
|
+
drawn += grapheme;
|
|
175
|
+
used += width;
|
|
176
|
+
}
|
|
177
|
+
if (drawn === '')
|
|
178
|
+
return row;
|
|
179
|
+
const [first = '', ...rest] = ghostGraphemes(drawn);
|
|
180
|
+
const spaces = ' '.repeat(Math.max(0, room - used));
|
|
181
|
+
return before + brush.paint(first, 'cursor') + brush.paint(rest.join(''), 'rest') + spaces;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Take a ghost-accepting key before the base sees it, so the key returns to
|
|
185
|
+
* its own meaning the moment nothing is offered.
|
|
186
|
+
*/
|
|
187
|
+
acceptGhost(data) {
|
|
188
|
+
const suffix = this.currentGhostSuffix();
|
|
189
|
+
if (suffix === undefined)
|
|
190
|
+
return false;
|
|
191
|
+
if (getKeybindings().matches(data, 'tui.editor.cursorWordRight')) {
|
|
192
|
+
const word = nextGhostWord(suffix);
|
|
193
|
+
if (word === undefined)
|
|
194
|
+
return false;
|
|
195
|
+
this.insertTextAtCursor(word);
|
|
196
|
+
this.tui.requestRender();
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
if (!matchesKey(data, 'ctrl+e'))
|
|
200
|
+
return false;
|
|
201
|
+
this.insertTextAtCursor(suffix);
|
|
202
|
+
this.tui.requestRender();
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
51
205
|
render(width) {
|
|
52
|
-
const side = this.borderColor(
|
|
206
|
+
const side = this.borderColor(FRAME_GLYPHS.side);
|
|
53
207
|
// A frame narrower than its own furniture would eat the text it exists to
|
|
54
208
|
// hold, and a hidden border token asks for no frame at all: both cases keep
|
|
55
209
|
// the plain rules rather than reserve columns for what nobody can see.
|
|
210
|
+
const ghost = this.disableSubmit ? undefined : this.currentGhostSuffix();
|
|
56
211
|
if (width < MIN_BOX_WIDTH || side === '') {
|
|
57
212
|
this.boxed = false;
|
|
58
|
-
return super.render(width);
|
|
213
|
+
return super.render(width).map(row => this.decorateText(this.ghostRow(row, ghost)));
|
|
59
214
|
}
|
|
60
215
|
const rows = super.render(width - FRAME_COLUMNS);
|
|
61
216
|
const closing = rows.findIndex(row => row.includes(CLOSING_TAG));
|
|
@@ -70,10 +225,10 @@ export class BoxedEditor extends Editor {
|
|
|
70
225
|
this.boxed = true;
|
|
71
226
|
// The menu keeps the page's own width; only its rows moved above the box.
|
|
72
227
|
const lines = menu.map(row => row + ' '.repeat(FRAME_COLUMNS));
|
|
73
|
-
lines.push(this.edge(
|
|
228
|
+
lines.push(this.edge(FRAME_GLYPHS.topLeft, FRAME_GLYPHS.topRight, rows[0] ?? ''));
|
|
74
229
|
for (const row of text)
|
|
75
|
-
lines.push(`${side}${row}${side}`);
|
|
76
|
-
lines.push(this.edge(
|
|
230
|
+
lines.push(`${side}${this.decorateText(this.ghostRow(row, ghost))}${side}`);
|
|
231
|
+
lines.push(this.edge(FRAME_GLYPHS.bottomLeft, FRAME_GLYPHS.bottomRight, rows[closing].replace(CLOSING_TAG, '')));
|
|
77
232
|
return lines;
|
|
78
233
|
}
|
|
79
234
|
/**
|
package/lib/ui/editor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.js","sourceRoot":"","sources":["../../src/ui/editor.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,sBAAsB,
|
|
1
|
+
{"version":3,"file":"editor.js","sourceRoot":"","sources":["../../src/ui/editor.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,cAAc,EACd,qBAAqB,EACrB,UAAU,EACV,sBAAsB,EACtB,YAAY,GAKb,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,SAAS,EAAE,aAAa,EAAe,MAAM,qBAAqB,CAAA;AAC3E,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,iBAAiB,EAAE,aAAa,EAAqB,MAAM,mBAAmB,CAAA;AACzH,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAElF;;;;;;;;;GASG;AACH,MAAM,WAAW,GAAG,QAAQ,CAAA;AAE5B;;;;;;GAMG;AACH,MAAM,YAAY,GAAG,IAAI,CAAA;AAEzB,iFAAiF;AACjF,MAAM,gBAAgB,GAAG,UAAU,CAAA;AAEnC;;;;;;;GAOG;AACH,MAAM,eAAe,GAAG,cAAc,CAAA;AACtC,MAAM,YAAY,GAAG,eAAe,CAAA;AAEpC;;;;;;;;GAQG;AACH,MAAM,aAAa,GAAG,qBAAqB,CAAA;AA4B3C;;;;;;;GAOG;AACH,MAAM,OAAO,WAAY,SAAQ,MAAM;IAWlB;IACA;IAXnB,gFAAgF;IACxE,QAAQ,GAAG,CAAC,CAAA;IACpB,qFAAqF;IAC7E,QAAQ,GAAG,CAAC,CAAA;IACpB,qFAAqF;IAC7E,KAAK,GAAG,KAAK,CAAA;IAErB,YACE,GAAQ,EACR,KAAkB,EACD,SAAuB,aAAa,EACpC,KAAkB;QAEnC,KAAK,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAA;QAHzB,WAAM,GAAN,MAAM,CAA8B;QACpC,UAAK,GAAL,KAAK,CAAa;IAGrC,CAAC;IAED;;;;;;;;OAQG;IACM,WAAW,CAAC,IAAY;QAC/B,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;YACvB,OAAM;QACR,CAAC;QACD,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;YAAE,OAAM;QAClC,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA;QACtC,4EAA4E;QAC5E,0EAA0E;QAC1E,4EAA4E;QAC5E,sEAAsE;QACtE,0EAA0E;QAC1E,uEAAuE;QACvE,IAAI,IAAI,KAAK,YAAY,IAAI,CAAC,qBAAqB,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YACxF,KAAK,CAAC,WAAW,CAAC,YAAY,CAAC,CAAA;YAC/B,OAAM;QACR,CAAC;QACD,IAAI,IAAI,CAAC,eAAe,IAAI,UAAU,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,CAAC;YACzF,KAAK,CAAC,WAAW,CAAC,YAAY,CAAC,CAAA;YAC/B,OAAM;QACR,CAAC;QACD,0EAA0E;QAC1E,4EAA4E;QAC5E,6EAA6E;QAC7E,6EAA6E;QAC7E,wEAAwE;QACxE,sCAAsC;QACtC,IAAI,IAAI,KAAK,gBAAgB,IAAI,CAAC,qBAAqB,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YAC/F,KAAK,CAAC,WAAW,CAAC,eAAe,CAAC,CAAA;YAClC,OAAM;QACR,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;IAED,0EAA0E;IACvD,kBAAkB,CAAC,KAAa,EAAE,eAAuB;QAC1E,OAAO,GAAG,KAAK,CAAC,kBAAkB,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,WAAW,EAAE,CAAA;IAC5E,CAAC;IAED,kFAAkF;IAC1E,IAAI,CAAC,IAAY,EAAE,KAAa,EAAE,GAAW;QACnD,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,IAAI,GAAG,sBAAsB,CAAC,GAAG,CAAC,GAAG,KAAK,EAAE,CAAC,CAAA;IAC1E,CAAC;IAED;;;OAGG;IACO,YAAY,CAAC,GAAW;QAChC,OAAO,GAAG,CAAA;IACZ,CAAC;IAED,kFAAkF;IAC1E,kBAAkB;QACxB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;QACxB,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE;YAAE,OAAO,SAAS,CAAA;QAC7D,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAA;QAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAA;QAC/B,4EAA4E;QAC5E,2EAA2E;QAC3E,IAAI,CAAC,iBAAiB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;YAAE,OAAO,SAAS,CAAA;QAC3D,yEAAyE;QACzE,0EAA0E;QAC1E,2EAA2E;QAC3E,2EAA2E;QAC3E,iEAAiE;QACjE,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,eAAe,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;QAChF,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;IAC/D,CAAC;IAED;;;;;;OAMG;IACK,QAAQ,CAAC,GAAW,EAAE,MAA0B;QACtD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;QACxB,IAAI,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,GAAG,CAAA;QAC3D,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAA;QAC3C,IAAI,QAAQ,GAAG,CAAC;YAAE,OAAO,GAAG,CAAA;QAC5B,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;QACrC,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;QACpE,gEAAgE;QAChE,MAAM,IAAI,GAAG,GAAG,GAAG,CAAC,CAAA;QACpB,wEAAwE;QACxE,2EAA2E;QAC3E,qEAAqE;QACrE,IAAI,KAAK,GAAG,EAAE,CAAA;QACd,IAAI,IAAI,GAAG,CAAC,CAAA;QACZ,KAAK,MAAM,QAAQ,IAAI,cAAc,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;YAChE,MAAM,KAAK,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAA;YACpC,IAAI,IAAI,GAAG,KAAK,GAAG,IAAI;gBAAE,MAAK;YAC9B,KAAK,IAAI,QAAQ,CAAA;YACjB,IAAI,IAAI,KAAK,CAAA;QACf,CAAC;QACD,IAAI,KAAK,KAAK,EAAE;YAAE,OAAO,GAAG,CAAA;QAC5B,MAAM,CAAC,KAAK,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,CAAA;QACnD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC,CAAC,CAAA;QACnD,OAAO,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IAC5F,CAAC;IAED;;;OAGG;IACK,WAAW,CAAC,IAAY;QAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAA;QACxC,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,KAAK,CAAA;QACtC,IAAI,cAAc,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,4BAA4B,CAAC,EAAE,CAAC;YACjE,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,CAAA;YAClC,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAO,KAAK,CAAA;YACpC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAA;YAC7B,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAA;YACxB,OAAO,IAAI,CAAA;QACb,CAAC;QACD,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC;YAAE,OAAO,KAAK,CAAA;QAC7C,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAA;QAC/B,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAA;QACxB,OAAO,IAAI,CAAA;IACb,CAAC;IAEQ,MAAM,CAAC,KAAa;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;QAChD,0EAA0E;QAC1E,4EAA4E;QAC5E,uEAAuE;QACvE,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAA;QACxE,IAAI,KAAK,GAAG,aAAa,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;YACzC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;YAClB,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;QACrF,CAAC;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,GAAG,aAAa,CAAC,CAAA;QAChD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAA;QAChE,0EAA0E;QAC1E,wEAAwE;QACxE,IAAI,OAAO,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAA;QACrE,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAA;QACpC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAA;QACnC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAA;QAC3B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAA;QAC3B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QACjB,0EAA0E;QAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAA;QAC9D,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;QACjF,KAAK,MAAM,GAAG,IAAI,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,CAAA;QACnG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,YAAY,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAE,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAA;QACjH,OAAO,KAAK,CAAA;IACd,CAAC;IAED;;;;;;;;OAQG;IACM,WAAW,CAAC,KAAoB;QACvC,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAO,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;QAChD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,GAAG,aAAa,CAAA;QACzC,IAAI,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC5B,OAAO,KAAK,CAAC,WAAW,CAAC,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC,CAAA;QAC/E,CAAC;QACD,OAAO,KAAK,CAAC,WAAW,CAAC,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;IAC3F,CAAC;CACF"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The frame every prompt is closed into.
|
|
3
|
+
*
|
|
4
|
+
* The editor, the prompts queued above it, and the prompts already submitted
|
|
5
|
+
* draw the same glyphs at the same width, so one prompt reads as the same object
|
|
6
|
+
* through its whole life; keeping the shape here is what stops the three from
|
|
7
|
+
* drifting.
|
|
8
|
+
*/
|
|
9
|
+
export declare const FRAME_GLYPHS: {
|
|
10
|
+
readonly topLeft: "╭";
|
|
11
|
+
readonly topRight: "╮";
|
|
12
|
+
readonly bottomLeft: "╰";
|
|
13
|
+
readonly bottomRight: "╯";
|
|
14
|
+
readonly side: "│";
|
|
15
|
+
};
|
|
16
|
+
/** The frame costs one column each side, so the text wraps that much narrower. */
|
|
17
|
+
export declare const FRAME_COLUMNS = 2;
|
|
18
|
+
/** One column of air inside the frame, so a full line never touches the border. */
|
|
19
|
+
export declare const PADDING_X = 1;
|
|
20
|
+
/** A box needs both edges, both paddings, and one column left to type in. */
|
|
21
|
+
export declare const MIN_BOX_WIDTH: number;
|
|
22
|
+
/** The columns the text of a bar owns, given the width of everything inside its frame. */
|
|
23
|
+
export declare function textWidth(innerWidth: number): number;
|
|
24
|
+
/** One text row of a bar: the air, the text, and the rest of the inside width. */
|
|
25
|
+
export declare function textRow(text: string, innerWidth: number): string;
|
|
26
|
+
/**
|
|
27
|
+
* The rule that closes a bar, naming what the bar could not fit.
|
|
28
|
+
*
|
|
29
|
+
* A hidden count is a fact about the text, not extra text, so it rides the rule
|
|
30
|
+
* the way the editor's own scroll indicator does: the same label in the same
|
|
31
|
+
* place, at the same width, so both bars report a fold identically.
|
|
32
|
+
*/
|
|
33
|
+
export declare function frameRule(innerWidth: number, hiddenRows: number): string;
|
|
34
|
+
/** How one block of text paints itself inside the bar that carries it. */
|
|
35
|
+
export interface FrameFaces {
|
|
36
|
+
/** Paints one row of the block's own text. */
|
|
37
|
+
readonly text: (line: string) => string;
|
|
38
|
+
/** Paints the frame's rules and its two sides. */
|
|
39
|
+
readonly border: (rule: string) => string;
|
|
40
|
+
/** Whether the frame may be drawn at all. */
|
|
41
|
+
readonly framed: boolean;
|
|
42
|
+
}
|
|
43
|
+
/** Whether a bar has the width, and the visible border, to close a frame. */
|
|
44
|
+
export declare function canFrame(width: number, borderVisible: boolean): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* One block of text as a bar draws it: wrapped to the frame's own width, and
|
|
47
|
+
* closed into it when the frame can be drawn.
|
|
48
|
+
*
|
|
49
|
+
* A block with a limit is a preview, and the rows it drops are counted on the
|
|
50
|
+
* closing rule: a reader who cannot see the whole text still has to see that it
|
|
51
|
+
* continues. A block without a limit is the prompt itself, which is never cut.
|
|
52
|
+
*/
|
|
53
|
+
export declare function frameText(text: string, width: number, faces: FrameFaces, limit?: number): string[];
|
|
54
|
+
//# sourceMappingURL=frame.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame.d.ts","sourceRoot":"","sources":["../../src/ui/frame.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY;;;;;;CAAyF,CAAA;AAElH,kFAAkF;AAClF,eAAO,MAAM,aAAa,IAAI,CAAA;AAC9B,mFAAmF;AACnF,eAAO,MAAM,SAAS,IAAI,CAAA;AAC1B,6EAA6E;AAC7E,eAAO,MAAM,aAAa,QAAoC,CAAA;AAE9D,0FAA0F;AAC1F,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,kFAAkF;AAClF,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAGhE;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAgBxE;AAED,0EAA0E;AAC1E,MAAM,WAAW,UAAU;IACzB,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IACvC,kDAAkD;IAClD,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IACzC,6CAA6C;IAC7C,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CACzB;AAED,6EAA6E;AAC7E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,GAAG,OAAO,CAEvE;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,SAA2B,GAAG,MAAM,EAAE,CAYpH"}
|
package/lib/ui/frame.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { visibleWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';
|
|
2
|
+
import { displayText } from "../text.js";
|
|
3
|
+
/**
|
|
4
|
+
* The frame every prompt is closed into.
|
|
5
|
+
*
|
|
6
|
+
* The editor, the prompts queued above it, and the prompts already submitted
|
|
7
|
+
* draw the same glyphs at the same width, so one prompt reads as the same object
|
|
8
|
+
* through its whole life; keeping the shape here is what stops the three from
|
|
9
|
+
* drifting.
|
|
10
|
+
*/
|
|
11
|
+
export const FRAME_GLYPHS = { topLeft: '╭', topRight: '╮', bottomLeft: '╰', bottomRight: '╯', side: '│' };
|
|
12
|
+
/** The frame costs one column each side, so the text wraps that much narrower. */
|
|
13
|
+
export const FRAME_COLUMNS = 2;
|
|
14
|
+
/** One column of air inside the frame, so a full line never touches the border. */
|
|
15
|
+
export const PADDING_X = 1;
|
|
16
|
+
/** A box needs both edges, both paddings, and one column left to type in. */
|
|
17
|
+
export const MIN_BOX_WIDTH = FRAME_COLUMNS + PADDING_X * 2 + 1;
|
|
18
|
+
/** The columns the text of a bar owns, given the width of everything inside its frame. */
|
|
19
|
+
export function textWidth(innerWidth) {
|
|
20
|
+
return Math.max(1, innerWidth - PADDING_X * 2);
|
|
21
|
+
}
|
|
22
|
+
/** One text row of a bar: the air, the text, and the rest of the inside width. */
|
|
23
|
+
export function textRow(text, innerWidth) {
|
|
24
|
+
const room = Math.max(0, textWidth(innerWidth) - visibleWidth(text));
|
|
25
|
+
return `${' '.repeat(PADDING_X)}${text}${' '.repeat(room)}${' '.repeat(PADDING_X)}`;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The rule that closes a bar, naming what the bar could not fit.
|
|
29
|
+
*
|
|
30
|
+
* A hidden count is a fact about the text, not extra text, so it rides the rule
|
|
31
|
+
* the way the editor's own scroll indicator does: the same label in the same
|
|
32
|
+
* place, at the same width, so both bars report a fold identically.
|
|
33
|
+
*/
|
|
34
|
+
export function frameRule(innerWidth, hiddenRows) {
|
|
35
|
+
const width = Math.max(0, innerWidth);
|
|
36
|
+
if (hiddenRows <= 0)
|
|
37
|
+
return '─'.repeat(width);
|
|
38
|
+
const label = ` ↓ ${hiddenRows} more `;
|
|
39
|
+
const labelWidth = visibleWidth(label);
|
|
40
|
+
if (labelWidth + 2 <= width) {
|
|
41
|
+
const left = Math.floor((width - labelWidth) / 2);
|
|
42
|
+
return `${'─'.repeat(left)}${label}${'─'.repeat(width - left - labelWidth)}`;
|
|
43
|
+
}
|
|
44
|
+
// Too narrow for a centred label: name it as far as it fits instead of
|
|
45
|
+
// dropping the fact. Both the label and the fallback are ASCII, so slicing by
|
|
46
|
+
// character is slicing by column here.
|
|
47
|
+
const indicator = `─── ↓ ${hiddenRows} more `;
|
|
48
|
+
if (visibleWidth(indicator) <= width)
|
|
49
|
+
return indicator + '─'.repeat(width - visibleWidth(indicator));
|
|
50
|
+
const ellipsis = '...'.slice(0, width);
|
|
51
|
+
return indicator.slice(0, Math.max(0, width - ellipsis.length)) + ellipsis;
|
|
52
|
+
}
|
|
53
|
+
/** Whether a bar has the width, and the visible border, to close a frame. */
|
|
54
|
+
export function canFrame(width, borderVisible) {
|
|
55
|
+
return borderVisible && width >= MIN_BOX_WIDTH;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* One block of text as a bar draws it: wrapped to the frame's own width, and
|
|
59
|
+
* closed into it when the frame can be drawn.
|
|
60
|
+
*
|
|
61
|
+
* A block with a limit is a preview, and the rows it drops are counted on the
|
|
62
|
+
* closing rule: a reader who cannot see the whole text still has to see that it
|
|
63
|
+
* continues. A block without a limit is the prompt itself, which is never cut.
|
|
64
|
+
*/
|
|
65
|
+
export function frameText(text, width, faces, limit = Number.POSITIVE_INFINITY) {
|
|
66
|
+
const inside = faces.framed ? width - FRAME_COLUMNS : width;
|
|
67
|
+
const wrapped = wrapTextWithAnsi(displayText(text), textWidth(inside));
|
|
68
|
+
const body = wrapped.slice(0, Math.max(0, limit));
|
|
69
|
+
const rows = body.map(line => textRow(faces.text(line), inside));
|
|
70
|
+
if (!faces.framed)
|
|
71
|
+
return rows;
|
|
72
|
+
const side = faces.border(FRAME_GLYPHS.side);
|
|
73
|
+
return [
|
|
74
|
+
faces.border(`${FRAME_GLYPHS.topLeft}${frameRule(inside, 0)}${FRAME_GLYPHS.topRight}`),
|
|
75
|
+
...rows.map(row => `${side}${row}${side}`),
|
|
76
|
+
faces.border(`${FRAME_GLYPHS.bottomLeft}${frameRule(inside, wrapped.length - body.length)}${FRAME_GLYPHS.bottomRight}`),
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=frame.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame.js","sourceRoot":"","sources":["../../src/ui/frame.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BoxedEditor } from './editor.ts';
|
|
2
|
+
/** How a question's answer is shown while it is written. */
|
|
3
|
+
export type GateInputMode = 'answer' | 'secret';
|
|
4
|
+
/**
|
|
5
|
+
* The input bar a gate collects an answer with.
|
|
6
|
+
*
|
|
7
|
+
* It is the prompt bar's own component, so an answer is written with the
|
|
8
|
+
* movement, deletion, undo, and paste that bar already has. Its mode decides
|
|
9
|
+
* only how the answer is shown: a question that declares a credential is
|
|
10
|
+
* answered in the same bar with the middle of the value hidden, never in a
|
|
11
|
+
* second widget that would have to grow its own editing.
|
|
12
|
+
*/
|
|
13
|
+
export declare class GateInputBar extends BoxedEditor {
|
|
14
|
+
private mode;
|
|
15
|
+
/** Switch what the bar shows without changing a character of what it holds. */
|
|
16
|
+
setMode(mode: GateInputMode): void;
|
|
17
|
+
/** Hide a credential's middle while its owner still recognizes both ends. */
|
|
18
|
+
protected decorateText(row: string): string;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=gate-input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate-input.d.ts","sourceRoot":"","sources":["../../src/ui/gate-input.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAEzC,4DAA4D;AAC5D,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,CAAA;AA2E/C;;;;;;;;GAQG;AACH,qBAAa,YAAa,SAAQ,WAAW;IAC3C,OAAO,CAAC,IAAI,CAA0B;IAEtC,+EAA+E;IAC/E,OAAO,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAMlC,6EAA6E;cAC1D,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;CAGrD"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { visibleWidth } from '@earendil-works/pi-tui';
|
|
2
|
+
import { BoxedEditor } from "./editor.js";
|
|
3
|
+
/** One character of the mask, repeated to the width of what it hides. */
|
|
4
|
+
const MASKED = '*';
|
|
5
|
+
/** How much of a credential stays readable at each end, so its owner can recognize it. */
|
|
6
|
+
const MASK_HEAD = 4;
|
|
7
|
+
const MASK_TAIL = 4;
|
|
8
|
+
/**
|
|
9
|
+
* A terminal sequence, so masking can tell a row's styling from its text: the
|
|
10
|
+
* cursor is drawn as an escape around a character, and hiding the character
|
|
11
|
+
* must not hide the mark that says where the cursor is.
|
|
12
|
+
*
|
|
13
|
+
* The string forms carry payloads a terminal reads rather than shows, and a
|
|
14
|
+
* cursor marker is one of them: masking a marker's own text would replace the
|
|
15
|
+
* position the terminal was told to place the cursor at with asterisks.
|
|
16
|
+
*/
|
|
17
|
+
const SEQUENCE = /\u001b(?:\[[0-9;?]*[ -/]*[@-~]|[\][P_^X][^\u0007\u001b]*(?:\u0007|\u001b\\)|[@-Z\\-_])/gu;
|
|
18
|
+
const GRAPHEMES = new Intl.Segmenter(undefined, { granularity: 'grapheme' });
|
|
19
|
+
/**
|
|
20
|
+
* One visible grapheme, and where it sits among the row's visible characters.
|
|
21
|
+
*
|
|
22
|
+
* Whitespace is not among them: the editor lays the answer out with spaces that
|
|
23
|
+
* belong to its own drawing, and hiding them would move the text under the
|
|
24
|
+
* cursor rather than hide it.
|
|
25
|
+
*/
|
|
26
|
+
function visibleAt(text) {
|
|
27
|
+
const found = [];
|
|
28
|
+
let position = 0;
|
|
29
|
+
for (const { segment } of GRAPHEMES.segment(text)) {
|
|
30
|
+
if (segment.trim() === '') {
|
|
31
|
+
found.push({ segment, position: -1 });
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
found.push({ segment, position });
|
|
35
|
+
position += 1;
|
|
36
|
+
}
|
|
37
|
+
return found;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Hide the middle of a credential and keep its ends readable.
|
|
41
|
+
*
|
|
42
|
+
* The reader is checking which key they pasted, and a row of asterisks answers
|
|
43
|
+
* nothing; a bystander is not meant to read it either, so only the first and
|
|
44
|
+
* last few characters survive. Rows are masked one at a time because that is
|
|
45
|
+
* how the editor hands them over, and a credential is written on one line.
|
|
46
|
+
*/
|
|
47
|
+
function maskCredentialRow(row) {
|
|
48
|
+
const segments = [];
|
|
49
|
+
let last = 0;
|
|
50
|
+
for (const match of row.matchAll(SEQUENCE)) {
|
|
51
|
+
segments.push({ escape: false, text: row.slice(last, match.index) });
|
|
52
|
+
segments.push({ escape: true, text: match[0] });
|
|
53
|
+
last = match.index + match[0].length;
|
|
54
|
+
}
|
|
55
|
+
segments.push({ escape: false, text: row.slice(last) });
|
|
56
|
+
const visible = segments.filter(segment => !segment.escape).flatMap(segment => visibleAt(segment.text));
|
|
57
|
+
const total = visible.reduce((count, entry) => count + (entry.position >= 0 ? 1 : 0), 0);
|
|
58
|
+
if (total <= MASK_HEAD + MASK_TAIL)
|
|
59
|
+
return row;
|
|
60
|
+
let seen = 0;
|
|
61
|
+
return segments.map((segment) => {
|
|
62
|
+
if (segment.escape)
|
|
63
|
+
return segment.text;
|
|
64
|
+
return visibleAt(segment.text).map((entry) => {
|
|
65
|
+
if (entry.position < 0)
|
|
66
|
+
return entry.segment;
|
|
67
|
+
const readable = seen < MASK_HEAD || seen >= total - MASK_TAIL;
|
|
68
|
+
seen += 1;
|
|
69
|
+
return readable ? entry.segment : MASKED.repeat(Math.max(1, visibleWidth(entry.segment)));
|
|
70
|
+
}).join('');
|
|
71
|
+
}).join('');
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The input bar a gate collects an answer with.
|
|
75
|
+
*
|
|
76
|
+
* It is the prompt bar's own component, so an answer is written with the
|
|
77
|
+
* movement, deletion, undo, and paste that bar already has. Its mode decides
|
|
78
|
+
* only how the answer is shown: a question that declares a credential is
|
|
79
|
+
* answered in the same bar with the middle of the value hidden, never in a
|
|
80
|
+
* second widget that would have to grow its own editing.
|
|
81
|
+
*/
|
|
82
|
+
export class GateInputBar extends BoxedEditor {
|
|
83
|
+
mode = 'answer';
|
|
84
|
+
/** Switch what the bar shows without changing a character of what it holds. */
|
|
85
|
+
setMode(mode) {
|
|
86
|
+
if (this.mode === mode)
|
|
87
|
+
return;
|
|
88
|
+
this.mode = mode;
|
|
89
|
+
this.invalidate();
|
|
90
|
+
}
|
|
91
|
+
/** Hide a credential's middle while its owner still recognizes both ends. */
|
|
92
|
+
decorateText(row) {
|
|
93
|
+
return this.mode === 'secret' ? maskCredentialRow(row) : row;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=gate-input.js.map
|