@duetso/agent 0.1.81 → 0.1.82
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/dist/package.json +1 -1
- package/dist/src/memory/observational.d.ts +7 -0
- package/dist/src/memory/observational.d.ts.map +1 -1
- package/dist/src/memory/observational.js +7 -1
- package/dist/src/memory/observational.js.map +1 -1
- package/dist/src/tui/app.d.ts +25 -35
- package/dist/src/tui/app.d.ts.map +1 -1
- package/dist/src/tui/app.js +195 -2308
- package/dist/src/tui/app.js.map +1 -1
- package/dist/src/tui/autocomplete-controller.d.ts +83 -0
- package/dist/src/tui/autocomplete-controller.d.ts.map +1 -0
- package/dist/src/tui/autocomplete-controller.js +308 -0
- package/dist/src/tui/autocomplete-controller.js.map +1 -0
- package/dist/src/tui/boot-screen.d.ts +64 -0
- package/dist/src/tui/boot-screen.d.ts.map +1 -0
- package/dist/src/tui/boot-screen.js +137 -0
- package/dist/src/tui/boot-screen.js.map +1 -0
- package/dist/src/tui/controllers.d.ts +39 -0
- package/dist/src/tui/controllers.d.ts.map +1 -0
- package/dist/src/tui/controllers.js +53 -0
- package/dist/src/tui/controllers.js.map +1 -0
- package/dist/src/tui/copy-controller.d.ts +75 -0
- package/dist/src/tui/copy-controller.d.ts.map +1 -0
- package/dist/src/tui/copy-controller.js +140 -0
- package/dist/src/tui/copy-controller.js.map +1 -0
- package/dist/src/tui/history-replay.d.ts +33 -0
- package/dist/src/tui/history-replay.d.ts.map +1 -0
- package/dist/src/tui/history-replay.js +47 -0
- package/dist/src/tui/history-replay.js.map +1 -0
- package/dist/src/tui/initial-prompt.d.ts +24 -0
- package/dist/src/tui/initial-prompt.d.ts.map +1 -0
- package/dist/src/tui/initial-prompt.js +29 -0
- package/dist/src/tui/initial-prompt.js.map +1 -0
- package/dist/src/tui/key-handlers.d.ts +49 -0
- package/dist/src/tui/key-handlers.d.ts.map +1 -0
- package/dist/src/tui/key-handlers.js +192 -0
- package/dist/src/tui/key-handlers.js.map +1 -0
- package/dist/src/tui/layout.d.ts +71 -0
- package/dist/src/tui/layout.d.ts.map +1 -0
- package/dist/src/tui/layout.js +245 -0
- package/dist/src/tui/layout.js.map +1 -0
- package/dist/src/tui/paste-controller.d.ts +90 -0
- package/dist/src/tui/paste-controller.d.ts.map +1 -0
- package/dist/src/tui/paste-controller.js +234 -0
- package/dist/src/tui/paste-controller.js.map +1 -0
- package/dist/src/tui/question-picker.d.ts +111 -0
- package/dist/src/tui/question-picker.d.ts.map +1 -0
- package/dist/src/tui/question-picker.js +308 -0
- package/dist/src/tui/question-picker.js.map +1 -0
- package/dist/src/tui/renderer-lifecycle.d.ts +20 -0
- package/dist/src/tui/renderer-lifecycle.d.ts.map +1 -0
- package/dist/src/tui/renderer-lifecycle.js +50 -0
- package/dist/src/tui/renderer-lifecycle.js.map +1 -0
- package/dist/src/tui/session-subscription.d.ts +30 -0
- package/dist/src/tui/session-subscription.d.ts.map +1 -0
- package/dist/src/tui/session-subscription.js +78 -0
- package/dist/src/tui/session-subscription.js.map +1 -0
- package/dist/src/tui/slash-commands.d.ts +23 -0
- package/dist/src/tui/slash-commands.d.ts.map +1 -0
- package/dist/src/tui/slash-commands.js +79 -0
- package/dist/src/tui/slash-commands.js.map +1 -0
- package/dist/src/tui/starter-section.d.ts +99 -0
- package/dist/src/tui/starter-section.d.ts.map +1 -0
- package/dist/src/tui/starter-section.js +254 -0
- package/dist/src/tui/starter-section.js.map +1 -0
- package/dist/src/tui/status-controller.d.ts +94 -0
- package/dist/src/tui/status-controller.d.ts.map +1 -0
- package/dist/src/tui/status-controller.js +186 -0
- package/dist/src/tui/status-controller.js.map +1 -0
- package/dist/src/tui/step-renderer.d.ts +55 -0
- package/dist/src/tui/step-renderer.d.ts.map +1 -0
- package/dist/src/tui/step-renderer.js +246 -0
- package/dist/src/tui/step-renderer.js.map +1 -0
- package/dist/src/tui/transcript-writer.d.ts +63 -0
- package/dist/src/tui/transcript-writer.d.ts.map +1 -0
- package/dist/src/tui/transcript-writer.js +130 -0
- package/dist/src/tui/transcript-writer.js.map +1 -0
- package/dist/src/turn-runner/turn-runner.d.ts +43 -1
- package/dist/src/turn-runner/turn-runner.d.ts.map +1 -1
- package/dist/src/turn-runner/turn-runner.js +86 -4
- package/dist/src/turn-runner/turn-runner.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Session } from "../session/session.js";
|
|
2
|
+
import type { TranscriptEntry } from "./transcript-log.js";
|
|
3
|
+
import type { StatusController } from "./status-controller.js";
|
|
4
|
+
import type { StepRenderer } from "./step-renderer.js";
|
|
5
|
+
export interface BootstrapInitialPromptDeps {
|
|
6
|
+
session: Session;
|
|
7
|
+
initialPrompt?: string;
|
|
8
|
+
statusController: StatusController;
|
|
9
|
+
stepRenderer: StepRenderer;
|
|
10
|
+
appendBlock(label: string | null, body: string, fg: string): void;
|
|
11
|
+
recordTranscriptEntry(kind: TranscriptEntry["kind"], text: string): void;
|
|
12
|
+
reportError(error: unknown): void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Kicks off the first turn (when `--prompt` was supplied) or paints the
|
|
16
|
+
* appropriate idle/sleep banner when no initial prompt was provided.
|
|
17
|
+
*
|
|
18
|
+
* A resumed sleeping session emitted its `sleep` terminal during
|
|
19
|
+
* `session.hydrate()` — before any TUI subscriber attached — so we read
|
|
20
|
+
* the last terminal here and surface it explicitly so the user can see
|
|
21
|
+
* when the next wake will fire.
|
|
22
|
+
*/
|
|
23
|
+
export declare function bootstrapInitialPrompt(deps: BootstrapInitialPromptDeps): void;
|
|
24
|
+
//# sourceMappingURL=initial-prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"initial-prompt.d.ts","sourceRoot":"","sources":["../../../src/tui/initial-prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,YAAY,EAAE,YAAY,CAAC;IAC3B,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClE,qBAAqB,CAAC,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzE,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CACnC;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,0BAA0B,GAAG,IAAI,CAwB7E"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { COLORS } from "./theme.js";
|
|
2
|
+
/**
|
|
3
|
+
* Kicks off the first turn (when `--prompt` was supplied) or paints the
|
|
4
|
+
* appropriate idle/sleep banner when no initial prompt was provided.
|
|
5
|
+
*
|
|
6
|
+
* A resumed sleeping session emitted its `sleep` terminal during
|
|
7
|
+
* `session.hydrate()` — before any TUI subscriber attached — so we read
|
|
8
|
+
* the last terminal here and surface it explicitly so the user can see
|
|
9
|
+
* when the next wake will fire.
|
|
10
|
+
*/
|
|
11
|
+
export function bootstrapInitialPrompt(deps) {
|
|
12
|
+
const { session, initialPrompt, statusController, stepRenderer, appendBlock, recordTranscriptEntry, reportError, } = deps;
|
|
13
|
+
if (initialPrompt) {
|
|
14
|
+
recordTranscriptEntry("user", initialPrompt);
|
|
15
|
+
appendBlock("you:", initialPrompt, COLORS.user);
|
|
16
|
+
void session.prompt({ message: initialPrompt, behavior: "follow_up" }).catch(reportError);
|
|
17
|
+
statusController.markRunning();
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const pending = session.getLastTerminal();
|
|
21
|
+
if (pending?.type === "sleep") {
|
|
22
|
+
stepRenderer.renderSleeping(pending.wakeAt);
|
|
23
|
+
statusController.markIdle(pending);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
statusController.markIdle();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=initial-prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"initial-prompt.js","sourceRoot":"","sources":["../../../src/tui/initial-prompt.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAYpC;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAgC;IACrE,MAAM,EACJ,OAAO,EACP,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,WAAW,GACZ,GAAG,IAAI,CAAC;IACT,IAAI,aAAa,EAAE,CAAC;QAClB,qBAAqB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QAC7C,WAAW,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QAChD,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC1F,gBAAgB,CAAC,WAAW,EAAE,CAAC;QAC/B,OAAO;IACT,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAC1C,IAAI,OAAO,EAAE,IAAI,KAAK,OAAO,EAAE,CAAC;QAC9B,YAAY,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC5C,gBAAgB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;SAAM,CAAC;QACN,gBAAgB,CAAC,QAAQ,EAAE,CAAC;IAC9B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { CliRenderer, ScrollBoxRenderable, TextareaRenderable } from "@opentui/core";
|
|
2
|
+
import type { AutocompleteController } from "./autocomplete-controller.js";
|
|
3
|
+
import type { CopyController } from "./copy-controller.js";
|
|
4
|
+
import type { PasteController } from "./paste-controller.js";
|
|
5
|
+
import type { QuestionPicker } from "./question-picker.js";
|
|
6
|
+
import type { StarterSection } from "./starter-section.js";
|
|
7
|
+
import type { TranscriptWriter } from "./transcript-writer.js";
|
|
8
|
+
/**
|
|
9
|
+
* Mutable flag shared between Esc-closing controllers (autocomplete, question
|
|
10
|
+
* picker) and the global Esc handler. The controllers set `suppress = true`
|
|
11
|
+
* when they consume Escape so the next bubbled Escape does not also cancel
|
|
12
|
+
* the active turn. The flag is reset on the very next Escape press.
|
|
13
|
+
*/
|
|
14
|
+
export interface EscapeSuppressionFlag {
|
|
15
|
+
suppress: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface KeyHandlerDeps {
|
|
18
|
+
renderer: CliRenderer;
|
|
19
|
+
inputField: TextareaRenderable;
|
|
20
|
+
transcript: ScrollBoxRenderable;
|
|
21
|
+
autocomplete: AutocompleteController;
|
|
22
|
+
questionPicker: QuestionPicker;
|
|
23
|
+
pasteController: PasteController;
|
|
24
|
+
copyController: CopyController;
|
|
25
|
+
starters: StarterSection | undefined;
|
|
26
|
+
transcriptWriter: TranscriptWriter;
|
|
27
|
+
escapeState: EscapeSuppressionFlag;
|
|
28
|
+
/** Submit the current composer contents as a regular follow_up turn. */
|
|
29
|
+
onSubmit(value: string): void;
|
|
30
|
+
/** Cancel any in-flight turn (Escape when running). */
|
|
31
|
+
onEscape(): void;
|
|
32
|
+
/** Dispatch the composer text with behavior:"steer" (Ctrl+Enter). */
|
|
33
|
+
onSteer(): void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Wires up the two keyboard event surfaces the TUI listens on:
|
|
37
|
+
*
|
|
38
|
+
* - The renderer's global keypress hook, used for keystrokes that fire
|
|
39
|
+
* regardless of focus (Esc cancel, Cmd/Ctrl+Shift+C copy when focus
|
|
40
|
+
* has drifted off the textarea during drag-select).
|
|
41
|
+
* - The composer `TextareaRenderable`'s `onKeyDown`, which intercepts
|
|
42
|
+
* keys before the textarea's own bindings (Esc would otherwise be
|
|
43
|
+
* consumed by Textarea internals).
|
|
44
|
+
*
|
|
45
|
+
* All input keystroke routing lives here so `runTui` stays a wiring
|
|
46
|
+
* sketch and key handling can be reasoned about in one place.
|
|
47
|
+
*/
|
|
48
|
+
export declare function installKeyHandlers(deps: KeyHandlerDeps): void;
|
|
49
|
+
//# sourceMappingURL=key-handlers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-handlers.d.ts","sourceRoot":"","sources":["../../../src/tui/key-handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAY,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACpG,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,OAAO,CAAC;CACnB;AAMD,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,WAAW,CAAC;IACtB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,UAAU,EAAE,mBAAmB,CAAC;IAChC,YAAY,EAAE,sBAAsB,CAAC;IACrC,cAAc,EAAE,cAAc,CAAC;IAC/B,eAAe,EAAE,eAAe,CAAC;IACjC,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,EAAE,cAAc,GAAG,SAAS,CAAC;IACrC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,WAAW,EAAE,qBAAqB,CAAC;IACnC,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,uDAAuD;IACvD,QAAQ,IAAI,IAAI,CAAC;IACjB,qEAAqE;IACrE,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAkM7D"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wires up the two keyboard event surfaces the TUI listens on:
|
|
3
|
+
*
|
|
4
|
+
* - The renderer's global keypress hook, used for keystrokes that fire
|
|
5
|
+
* regardless of focus (Esc cancel, Cmd/Ctrl+Shift+C copy when focus
|
|
6
|
+
* has drifted off the textarea during drag-select).
|
|
7
|
+
* - The composer `TextareaRenderable`'s `onKeyDown`, which intercepts
|
|
8
|
+
* keys before the textarea's own bindings (Esc would otherwise be
|
|
9
|
+
* consumed by Textarea internals).
|
|
10
|
+
*
|
|
11
|
+
* All input keystroke routing lives here so `runTui` stays a wiring
|
|
12
|
+
* sketch and key handling can be reasoned about in one place.
|
|
13
|
+
*/
|
|
14
|
+
export function installKeyHandlers(deps) {
|
|
15
|
+
const { renderer, inputField, transcript, autocomplete, questionPicker, pasteController, copyController, starters, transcriptWriter, escapeState, onSubmit, onEscape, onSteer, } = deps;
|
|
16
|
+
const keyHandler = renderer._keyHandler;
|
|
17
|
+
keyHandler.onInternal("keypress", (key) => {
|
|
18
|
+
transcriptWriter.logKey("global", key);
|
|
19
|
+
// Copy keystroke. Lives on the global handler (not
|
|
20
|
+
// inputField.onKeyDown) because the mousedown that starts a
|
|
21
|
+
// drag-select moves focus off the textarea — the focused-renderable
|
|
22
|
+
// path stops firing right when the user has something to copy. The
|
|
23
|
+
// global handler always fires regardless of focus.
|
|
24
|
+
if (copyController.handleCopyKeystroke(key))
|
|
25
|
+
return;
|
|
26
|
+
if (key.name !== "escape")
|
|
27
|
+
return;
|
|
28
|
+
if (escapeState.suppress) {
|
|
29
|
+
escapeState.suppress = false;
|
|
30
|
+
key.preventDefault();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (autocomplete.isSkillPickerOpen() || autocomplete.isFilePickerOpen()) {
|
|
34
|
+
key.preventDefault();
|
|
35
|
+
autocomplete.hideAll();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (questionPicker.isOpen()) {
|
|
39
|
+
key.preventDefault();
|
|
40
|
+
questionPicker.hide();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
key.preventDefault();
|
|
44
|
+
onEscape();
|
|
45
|
+
});
|
|
46
|
+
// Keyboard scroll bindings for the transcript. Mirrors the mouse wheel
|
|
47
|
+
// for terminals that swallow mouse events (tmux without mouse mode, ssh
|
|
48
|
+
// sessions where the local terminal owns the wheel, screen readers).
|
|
49
|
+
// Page = one viewport; Shift+arrow = three lines, matching wheel cadence.
|
|
50
|
+
function scrollByLines(delta) {
|
|
51
|
+
transcript.scrollBy({ x: 0, y: delta });
|
|
52
|
+
}
|
|
53
|
+
function scrollByPage(direction) {
|
|
54
|
+
// Subtract 2 to account for the top+bottom border rows; padding lives
|
|
55
|
+
// inside the scroll viewport and does not need to be deducted.
|
|
56
|
+
const viewport = Math.max(1, transcript.height - 2);
|
|
57
|
+
transcript.scrollBy({ x: 0, y: direction * viewport });
|
|
58
|
+
}
|
|
59
|
+
// Attach directly to the focused InputRenderable. The Textarea-based input
|
|
60
|
+
// consumes escape via its own keybindings before any global keypress handler
|
|
61
|
+
// fires, so we intercept at the Renderable's onKeyDown hook which runs first.
|
|
62
|
+
inputField.onKeyDown = (key) => {
|
|
63
|
+
transcriptWriter.logKey("keydown", key);
|
|
64
|
+
if (key.name === "pageup") {
|
|
65
|
+
scrollByPage(-1);
|
|
66
|
+
key.preventDefault();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (key.name === "pagedown") {
|
|
70
|
+
scrollByPage(1);
|
|
71
|
+
key.preventDefault();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (key.shift && key.name === "up" && !key.ctrl && !key.meta && !key.super) {
|
|
75
|
+
scrollByLines(-3);
|
|
76
|
+
key.preventDefault();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (key.shift && key.name === "down" && !key.ctrl && !key.meta && !key.super) {
|
|
80
|
+
scrollByLines(3);
|
|
81
|
+
key.preventDefault();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
// Boot starter navigation. Only intercepts when the starter section is
|
|
85
|
+
// still on screen; once dismissed (by composition or first submit) all
|
|
86
|
+
// of these branches no-op and keys flow normally to the input.
|
|
87
|
+
if (starters?.isVisible() && inputField.plainText.length === 0) {
|
|
88
|
+
if (key.name === "up") {
|
|
89
|
+
starters.move(-1);
|
|
90
|
+
key.preventDefault();
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (key.name === "down") {
|
|
94
|
+
starters.move(1);
|
|
95
|
+
key.preventDefault();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (key.name &&
|
|
99
|
+
key.name.length === 1 &&
|
|
100
|
+
key.name >= "1" &&
|
|
101
|
+
key.name <= "9" &&
|
|
102
|
+
!key.ctrl &&
|
|
103
|
+
!key.meta &&
|
|
104
|
+
!key.super) {
|
|
105
|
+
const target = Number.parseInt(key.name, 10) - 1;
|
|
106
|
+
if (starters.jump(target)) {
|
|
107
|
+
key.preventDefault();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Cmd+V / Ctrl+V keystroke trigger. Many terminals (Warp in particular,
|
|
113
|
+
// and macOS Terminal.app for binary clipboards) do not forward a paste
|
|
114
|
+
// event to TUI programs on Cmd+V — they handle the clipboard at the app
|
|
115
|
+
// level and only deliver the resulting text. We catch the keystroke here
|
|
116
|
+
// and probe the OS clipboard directly so image attach works regardless of
|
|
117
|
+
// whether the terminal cooperates with bracketed paste for binary data.
|
|
118
|
+
//
|
|
119
|
+
// The keystroke handler only fires on terminals that actually deliver
|
|
120
|
+
// Cmd+V as a keypress (kitty-keyboard-aware terminals: kitty, Ghostty,
|
|
121
|
+
// recent iTerm2, WezTerm). For terminals that swallow Cmd+V entirely,
|
|
122
|
+
// the `/paste` slash command below provides a guaranteed fallback.
|
|
123
|
+
if (key.name === "v" && (key.super || key.meta || key.ctrl) && !key.shift) {
|
|
124
|
+
key.preventDefault();
|
|
125
|
+
void pasteController.triggerClipboardProbe("keystroke");
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (autocomplete.handleKey(key))
|
|
129
|
+
return;
|
|
130
|
+
if (questionPicker.handleKey(key))
|
|
131
|
+
return;
|
|
132
|
+
if (key.name === "return" || key.name === "enter") {
|
|
133
|
+
// Three modifier flavors on the Enter key. The modifier is only
|
|
134
|
+
// distinguishable when the terminal speaks the kitty-keyboard
|
|
135
|
+
// protocol (or modifyOtherKeys); in a legacy terminal Ctrl+Enter
|
|
136
|
+
// and Shift+Enter collapse to plain Enter. We accept that
|
|
137
|
+
// tradeoff for the same reason Shift+Enter accepts it: modern
|
|
138
|
+
// terminals are the target.
|
|
139
|
+
//
|
|
140
|
+
// Plain Enter → submit (idle = fresh turn, running = soft queue
|
|
141
|
+
// via follow_up).
|
|
142
|
+
// Shift+Enter → insert a literal newline at the cursor, matching
|
|
143
|
+
// every modern chat composer (Slack, ChatGPT,
|
|
144
|
+
// Discord, Cursor, Claude Code).
|
|
145
|
+
// Ctrl+Enter → steer: dispatch with behavior:"steer" so the
|
|
146
|
+
// runner hands it to agent.steer() at the next
|
|
147
|
+
// inference boundary instead of waiting for the
|
|
148
|
+
// full turn to wrap up.
|
|
149
|
+
key.preventDefault();
|
|
150
|
+
if (key.shift) {
|
|
151
|
+
inputField.insertText("\n");
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (key.ctrl) {
|
|
155
|
+
onSteer();
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const value = inputField.plainText.trim();
|
|
159
|
+
// Pre-latch the starter chrome before clearing the composer so the
|
|
160
|
+
// clear-induced `onContentChange` doesn't briefly re-mount the chrome
|
|
161
|
+
// (it would see empty composer + starters hidden and call
|
|
162
|
+
// mountStarterChrome before submit got a chance to destroy them).
|
|
163
|
+
// Only pre-latch when we know a typed-submit is about to fire —
|
|
164
|
+
// empty-Enter into the picker / starter-row branches still need the
|
|
165
|
+
// chrome state intact so they can dispatch.
|
|
166
|
+
if (value && starters && !starters.isPermanentlyDismissed()) {
|
|
167
|
+
starters.destroyPermanently();
|
|
168
|
+
}
|
|
169
|
+
inputField.clear();
|
|
170
|
+
if (value) {
|
|
171
|
+
onSubmit(value);
|
|
172
|
+
}
|
|
173
|
+
else if (questionPicker.isOpen()) {
|
|
174
|
+
questionPicker.confirmSelection();
|
|
175
|
+
}
|
|
176
|
+
else if (starters?.isVisible()) {
|
|
177
|
+
starters.submitHighlighted();
|
|
178
|
+
}
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (key.name === "escape") {
|
|
182
|
+
// Suppress the next global keypress for this Escape press so the
|
|
183
|
+
// renderer-level handler does not also call onEscape — the textarea
|
|
184
|
+
// hook runs first and consumes the keystroke here.
|
|
185
|
+
key.preventDefault();
|
|
186
|
+
escapeState.suppress = true;
|
|
187
|
+
onEscape();
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=key-handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-handlers.js","sourceRoot":"","sources":["../../../src/tui/key-handlers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { BoxRenderable, type CliRenderer, ScrollBoxRenderable, TextRenderable, TextareaRenderable } from "@opentui/core";
|
|
2
|
+
import { createSidebar } from "./sidebar.js";
|
|
3
|
+
/**
|
|
4
|
+
* Sidebar handle returned by {@link createSidebar}; exposed on
|
|
5
|
+
* {@link LayoutRefs} so the turn controller can push todo / state-machine /
|
|
6
|
+
* context-usage updates without re-importing the sidebar factory.
|
|
7
|
+
*/
|
|
8
|
+
type SidebarHandle = ReturnType<typeof createSidebar>;
|
|
9
|
+
/**
|
|
10
|
+
* Renderable references for the static TUI chrome built by
|
|
11
|
+
* {@link buildLayout}. Every field is mounted into `renderer.root` before
|
|
12
|
+
* this object is returned, so callers only need to wire behavior (event
|
|
13
|
+
* listeners, content updates, visibility flips).
|
|
14
|
+
*/
|
|
15
|
+
export interface LayoutRefs {
|
|
16
|
+
/** Outer row container holding the main column and the sidebar. */
|
|
17
|
+
root: BoxRenderable;
|
|
18
|
+
/** Main column: transcript, status, hint, pickers, input. */
|
|
19
|
+
layout: BoxRenderable;
|
|
20
|
+
/** Right-side panel with todos, follow-up queue, state machine, and context usage. */
|
|
21
|
+
sidebar: SidebarHandle;
|
|
22
|
+
/** Scrollable transcript that pins to the bottom while the user has not manually scrolled. */
|
|
23
|
+
transcript: ScrollBoxRenderable;
|
|
24
|
+
/** Single-line working-status row below the transcript (spinner, elapsed, queue size). */
|
|
25
|
+
status: TextRenderable;
|
|
26
|
+
/** Single-line hint row showing the active keystrokes (Enter / Esc / copy). */
|
|
27
|
+
hint: TextRenderable;
|
|
28
|
+
/** Slash + skill autocomplete panel; toggled via `visible` from the autocomplete controller. */
|
|
29
|
+
skillAutocompletePanel: BoxRenderable;
|
|
30
|
+
/** Header row for the "commands" section of the slash/skill picker. */
|
|
31
|
+
commandHeader: TextRenderable;
|
|
32
|
+
/** Fixed pool of selectable rows for built-in slash commands. */
|
|
33
|
+
commandRows: TextRenderable[];
|
|
34
|
+
/** Header row for the "skills" section of the slash/skill picker. */
|
|
35
|
+
skillHeader: TextRenderable;
|
|
36
|
+
/** Fixed pool of selectable rows for matched skills. */
|
|
37
|
+
skillRows: TextRenderable[];
|
|
38
|
+
/** `@`-file picker panel; mirrors the slash panel's structure. */
|
|
39
|
+
fileAutocompletePanel: BoxRenderable;
|
|
40
|
+
/** Static "files" title row for the file picker. */
|
|
41
|
+
fileAutocompleteTitle: TextRenderable;
|
|
42
|
+
/** Fixed pool of selectable rows for matched files. */
|
|
43
|
+
fileAutocompleteRows: TextRenderable[];
|
|
44
|
+
/** Pending question panel; shown while the runner has questions awaiting answers. */
|
|
45
|
+
questionPanel: BoxRenderable;
|
|
46
|
+
/** Title row for the active question (prefix, prompt, navigation hint). */
|
|
47
|
+
questionTitle: TextRenderable;
|
|
48
|
+
/** Blank spacer between the question title and the option rows. */
|
|
49
|
+
questionSpacer: TextRenderable;
|
|
50
|
+
/** Fixed pool of selectable rows for the active question's options. */
|
|
51
|
+
questionRows: TextRenderable[];
|
|
52
|
+
/** Bordered row containing the prompt sigil and the input textarea. */
|
|
53
|
+
inputBox: BoxRenderable;
|
|
54
|
+
/** Leading "> " sigil rendered before the textarea; excluded from drag-select. */
|
|
55
|
+
prompt: TextRenderable;
|
|
56
|
+
/** Multi-line input textarea; focused on mount, soft-wraps long messages. */
|
|
57
|
+
inputField: TextareaRenderable;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Builds the static TUI chrome (containers, transcript, status / hint rows,
|
|
61
|
+
* autocomplete + question panels, input box) and attaches everything to
|
|
62
|
+
* `renderer.root`. The returned {@link LayoutRefs} are stable for the
|
|
63
|
+
* lifetime of the renderer; callers wire behavior on top without
|
|
64
|
+
* re-creating any of these nodes.
|
|
65
|
+
*
|
|
66
|
+
* Focus is set on `inputField` before returning so the user can type
|
|
67
|
+
* immediately on mount.
|
|
68
|
+
*/
|
|
69
|
+
export declare function buildLayout(renderer: CliRenderer): LayoutRefs;
|
|
70
|
+
export {};
|
|
71
|
+
//# sourceMappingURL=layout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/tui/layout.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,KAAK,WAAW,EAChB,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EACnB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAO7C;;;;GAIG;AACH,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,mEAAmE;IACnE,IAAI,EAAE,aAAa,CAAC;IACpB,6DAA6D;IAC7D,MAAM,EAAE,aAAa,CAAC;IACtB,sFAAsF;IACtF,OAAO,EAAE,aAAa,CAAC;IACvB,8FAA8F;IAC9F,UAAU,EAAE,mBAAmB,CAAC;IAChC,0FAA0F;IAC1F,MAAM,EAAE,cAAc,CAAC;IACvB,+EAA+E;IAC/E,IAAI,EAAE,cAAc,CAAC;IACrB,gGAAgG;IAChG,sBAAsB,EAAE,aAAa,CAAC;IACtC,uEAAuE;IACvE,aAAa,EAAE,cAAc,CAAC;IAC9B,iEAAiE;IACjE,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,qEAAqE;IACrE,WAAW,EAAE,cAAc,CAAC;IAC5B,wDAAwD;IACxD,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,kEAAkE;IAClE,qBAAqB,EAAE,aAAa,CAAC;IACrC,oDAAoD;IACpD,qBAAqB,EAAE,cAAc,CAAC;IACtC,uDAAuD;IACvD,oBAAoB,EAAE,cAAc,EAAE,CAAC;IACvC,qFAAqF;IACrF,aAAa,EAAE,aAAa,CAAC;IAC7B,2EAA2E;IAC3E,aAAa,EAAE,cAAc,CAAC;IAC9B,mEAAmE;IACnE,cAAc,EAAE,cAAc,CAAC;IAC/B,uEAAuE;IACvE,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,uEAAuE;IACvE,QAAQ,EAAE,aAAa,CAAC;IACxB,kFAAkF;IAClF,MAAM,EAAE,cAAc,CAAC;IACvB,6EAA6E;IAC7E,UAAU,EAAE,kBAAkB,CAAC;CAChC;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,WAAW,GAAG,UAAU,CAiP7D"}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { BoxRenderable, ScrollBoxRenderable, TextRenderable, TextareaRenderable, } from "@opentui/core";
|
|
2
|
+
import { AUTOCOMPLETE_LIMITS, BUILT_IN_SLASH_COMMANDS } from "./autocomplete.js";
|
|
3
|
+
import { createSidebar } from "./sidebar.js";
|
|
4
|
+
import { COLORS, HINT_IDLE } from "./theme.js";
|
|
5
|
+
const SKILL_AUTOCOMPLETE_LIMIT = AUTOCOMPLETE_LIMITS.skill;
|
|
6
|
+
const FILE_AUTOCOMPLETE_LIMIT = AUTOCOMPLETE_LIMITS.file;
|
|
7
|
+
const QUESTION_OPTION_LIMIT = AUTOCOMPLETE_LIMITS.questionOption;
|
|
8
|
+
/**
|
|
9
|
+
* Builds the static TUI chrome (containers, transcript, status / hint rows,
|
|
10
|
+
* autocomplete + question panels, input box) and attaches everything to
|
|
11
|
+
* `renderer.root`. The returned {@link LayoutRefs} are stable for the
|
|
12
|
+
* lifetime of the renderer; callers wire behavior on top without
|
|
13
|
+
* re-creating any of these nodes.
|
|
14
|
+
*
|
|
15
|
+
* Focus is set on `inputField` before returning so the user can type
|
|
16
|
+
* immediately on mount.
|
|
17
|
+
*/
|
|
18
|
+
export function buildLayout(renderer) {
|
|
19
|
+
// Outer row wraps the main column and a right-side sidebar that surfaces
|
|
20
|
+
// the runner's current todo list and state-machine progress.
|
|
21
|
+
const root = new BoxRenderable(renderer, {
|
|
22
|
+
flexDirection: "row",
|
|
23
|
+
width: "100%",
|
|
24
|
+
height: "100%",
|
|
25
|
+
});
|
|
26
|
+
const layout = new BoxRenderable(renderer, {
|
|
27
|
+
flexDirection: "column",
|
|
28
|
+
flexGrow: 1,
|
|
29
|
+
flexShrink: 1,
|
|
30
|
+
height: "100%",
|
|
31
|
+
});
|
|
32
|
+
const sidebar = createSidebar(renderer);
|
|
33
|
+
const transcript = new ScrollBoxRenderable(renderer, {
|
|
34
|
+
flexGrow: 1,
|
|
35
|
+
flexShrink: 1,
|
|
36
|
+
scrollY: true,
|
|
37
|
+
// Pin to bottom as new lines arrive, but only while the user has not
|
|
38
|
+
// manually scrolled away. ScrollBoxRenderable flips `_hasManualScroll`
|
|
39
|
+
// the moment the user scrolls up, which pauses pinning until they
|
|
40
|
+
// return to the bottom — without this, new output yanks the viewport
|
|
41
|
+
// down while the user is reading history.
|
|
42
|
+
stickyScroll: true,
|
|
43
|
+
stickyStart: "bottom",
|
|
44
|
+
border: true,
|
|
45
|
+
borderColor: COLORS.border,
|
|
46
|
+
padding: 1,
|
|
47
|
+
});
|
|
48
|
+
// Status and hint chrome are excluded from drag-select so a highlight that
|
|
49
|
+
// sweeps the bottom of the screen does not pull the spinner / hint text
|
|
50
|
+
// into the clipboard alongside the transcript content the user wanted.
|
|
51
|
+
const status = new TextRenderable(renderer, {
|
|
52
|
+
content: "",
|
|
53
|
+
fg: COLORS.status,
|
|
54
|
+
height: 1,
|
|
55
|
+
flexShrink: 0,
|
|
56
|
+
selectable: false,
|
|
57
|
+
});
|
|
58
|
+
const hint = new TextRenderable(renderer, {
|
|
59
|
+
content: HINT_IDLE,
|
|
60
|
+
fg: COLORS.hint,
|
|
61
|
+
height: 1,
|
|
62
|
+
flexShrink: 0,
|
|
63
|
+
selectable: false,
|
|
64
|
+
});
|
|
65
|
+
const skillAutocompletePanel = new BoxRenderable(renderer, {
|
|
66
|
+
flexDirection: "column",
|
|
67
|
+
border: true,
|
|
68
|
+
borderColor: COLORS.border,
|
|
69
|
+
paddingLeft: 1,
|
|
70
|
+
paddingRight: 1,
|
|
71
|
+
flexShrink: 0,
|
|
72
|
+
});
|
|
73
|
+
skillAutocompletePanel.visible = false;
|
|
74
|
+
// Two ordered sections: built-in commands first, skills second. Each
|
|
75
|
+
// section has its own header row plus a fixed pool of item rows. Selection
|
|
76
|
+
// navigates the flat ordered list of visible items across both sections.
|
|
77
|
+
// Row height is assigned per render based on the wrapped description
|
|
78
|
+
// length so a one-line description doesn't leave an empty trailing line
|
|
79
|
+
// beneath the name. The renderer sets `height` whenever it writes
|
|
80
|
+
// `content`.
|
|
81
|
+
// Autocomplete and panel chrome are not part of the transcript content,
|
|
82
|
+
// so exclude them from drag-select to keep the clipboard focused on
|
|
83
|
+
// assistant/user messages.
|
|
84
|
+
const makeItemRow = () => {
|
|
85
|
+
const row = new TextRenderable(renderer, {
|
|
86
|
+
content: "",
|
|
87
|
+
fg: COLORS.hint,
|
|
88
|
+
flexShrink: 0,
|
|
89
|
+
selectable: false,
|
|
90
|
+
});
|
|
91
|
+
row.visible = false;
|
|
92
|
+
return row;
|
|
93
|
+
};
|
|
94
|
+
const makeHeaderRow = (label) => new TextRenderable(renderer, {
|
|
95
|
+
content: label,
|
|
96
|
+
fg: COLORS.status,
|
|
97
|
+
height: 1,
|
|
98
|
+
flexShrink: 0,
|
|
99
|
+
selectable: false,
|
|
100
|
+
});
|
|
101
|
+
const commandHeader = makeHeaderRow("commands");
|
|
102
|
+
const commandRows = Array.from({ length: BUILT_IN_SLASH_COMMANDS.length }, makeItemRow);
|
|
103
|
+
const skillHeader = makeHeaderRow("skills");
|
|
104
|
+
const skillRows = Array.from({ length: SKILL_AUTOCOMPLETE_LIMIT }, makeItemRow);
|
|
105
|
+
skillAutocompletePanel.add(commandHeader);
|
|
106
|
+
for (const row of commandRows)
|
|
107
|
+
skillAutocompletePanel.add(row);
|
|
108
|
+
skillAutocompletePanel.add(skillHeader);
|
|
109
|
+
for (const row of skillRows)
|
|
110
|
+
skillAutocompletePanel.add(row);
|
|
111
|
+
// The @-file picker mirrors the slash picker's structure so the renderer
|
|
112
|
+
// logic and key handling can stay parallel between the two pickers.
|
|
113
|
+
const fileAutocompletePanel = new BoxRenderable(renderer, {
|
|
114
|
+
flexDirection: "column",
|
|
115
|
+
border: true,
|
|
116
|
+
borderColor: COLORS.border,
|
|
117
|
+
paddingLeft: 1,
|
|
118
|
+
paddingRight: 1,
|
|
119
|
+
flexShrink: 0,
|
|
120
|
+
});
|
|
121
|
+
fileAutocompletePanel.visible = false;
|
|
122
|
+
const fileAutocompleteTitle = new TextRenderable(renderer, {
|
|
123
|
+
content: "files",
|
|
124
|
+
fg: COLORS.status,
|
|
125
|
+
height: 1,
|
|
126
|
+
flexShrink: 0,
|
|
127
|
+
selectable: false,
|
|
128
|
+
});
|
|
129
|
+
const fileAutocompleteRows = Array.from({ length: FILE_AUTOCOMPLETE_LIMIT }, () => {
|
|
130
|
+
const row = new TextRenderable(renderer, {
|
|
131
|
+
content: "",
|
|
132
|
+
fg: COLORS.hint,
|
|
133
|
+
height: 1,
|
|
134
|
+
flexShrink: 0,
|
|
135
|
+
selectable: false,
|
|
136
|
+
});
|
|
137
|
+
row.visible = false;
|
|
138
|
+
return row;
|
|
139
|
+
});
|
|
140
|
+
fileAutocompletePanel.add(fileAutocompleteTitle);
|
|
141
|
+
for (const row of fileAutocompleteRows) {
|
|
142
|
+
fileAutocompletePanel.add(row);
|
|
143
|
+
}
|
|
144
|
+
const questionPanel = new BoxRenderable(renderer, {
|
|
145
|
+
flexDirection: "column",
|
|
146
|
+
border: true,
|
|
147
|
+
borderColor: COLORS.border,
|
|
148
|
+
paddingLeft: 1,
|
|
149
|
+
paddingRight: 1,
|
|
150
|
+
flexShrink: 0,
|
|
151
|
+
});
|
|
152
|
+
questionPanel.visible = false;
|
|
153
|
+
const questionTitle = new TextRenderable(renderer, {
|
|
154
|
+
content: "question",
|
|
155
|
+
fg: COLORS.agent,
|
|
156
|
+
wrapMode: "word",
|
|
157
|
+
flexShrink: 0,
|
|
158
|
+
selectable: false,
|
|
159
|
+
});
|
|
160
|
+
const questionSpacer = new TextRenderable(renderer, {
|
|
161
|
+
content: "",
|
|
162
|
+
height: 1,
|
|
163
|
+
flexShrink: 0,
|
|
164
|
+
selectable: false,
|
|
165
|
+
});
|
|
166
|
+
const questionRows = Array.from({ length: QUESTION_OPTION_LIMIT }, () => {
|
|
167
|
+
const row = new TextRenderable(renderer, {
|
|
168
|
+
content: "",
|
|
169
|
+
fg: COLORS.hint,
|
|
170
|
+
wrapMode: "word",
|
|
171
|
+
flexShrink: 0,
|
|
172
|
+
selectable: false,
|
|
173
|
+
});
|
|
174
|
+
row.visible = false;
|
|
175
|
+
return row;
|
|
176
|
+
});
|
|
177
|
+
questionPanel.add(questionTitle);
|
|
178
|
+
questionPanel.add(questionSpacer);
|
|
179
|
+
for (const row of questionRows) {
|
|
180
|
+
questionPanel.add(row);
|
|
181
|
+
}
|
|
182
|
+
const inputBox = new BoxRenderable(renderer, {
|
|
183
|
+
flexDirection: "row",
|
|
184
|
+
border: true,
|
|
185
|
+
borderColor: COLORS.border,
|
|
186
|
+
paddingLeft: 1,
|
|
187
|
+
paddingRight: 1,
|
|
188
|
+
flexShrink: 0,
|
|
189
|
+
});
|
|
190
|
+
// The leading "> " sigil is decoration, not content; excluding it from
|
|
191
|
+
// selection means a drag that starts at the input row does not pull the
|
|
192
|
+
// sigil into the clipboard alongside the highlighted text.
|
|
193
|
+
const prompt = new TextRenderable(renderer, {
|
|
194
|
+
content: "> ",
|
|
195
|
+
fg: COLORS.user,
|
|
196
|
+
width: 2,
|
|
197
|
+
selectable: false,
|
|
198
|
+
});
|
|
199
|
+
// Textarea (rather than Input) so long messages soft-wrap visually. Enter
|
|
200
|
+
// is intercepted in onKeyDown below to submit instead of inserting a newline.
|
|
201
|
+
const inputField = new TextareaRenderable(renderer, {
|
|
202
|
+
placeholder: "Type a message and press Enter…",
|
|
203
|
+
flexGrow: 1,
|
|
204
|
+
minHeight: 1,
|
|
205
|
+
maxHeight: 10,
|
|
206
|
+
wrapMode: "word",
|
|
207
|
+
});
|
|
208
|
+
inputBox.add(prompt);
|
|
209
|
+
inputBox.add(inputField);
|
|
210
|
+
layout.add(transcript);
|
|
211
|
+
layout.add(status);
|
|
212
|
+
layout.add(hint);
|
|
213
|
+
layout.add(skillAutocompletePanel);
|
|
214
|
+
layout.add(fileAutocompletePanel);
|
|
215
|
+
layout.add(questionPanel);
|
|
216
|
+
layout.add(inputBox);
|
|
217
|
+
root.add(layout);
|
|
218
|
+
root.add(sidebar.view);
|
|
219
|
+
renderer.root.add(root);
|
|
220
|
+
inputField.focus();
|
|
221
|
+
return {
|
|
222
|
+
root,
|
|
223
|
+
layout,
|
|
224
|
+
sidebar,
|
|
225
|
+
transcript,
|
|
226
|
+
status,
|
|
227
|
+
hint,
|
|
228
|
+
skillAutocompletePanel,
|
|
229
|
+
commandHeader,
|
|
230
|
+
commandRows,
|
|
231
|
+
skillHeader,
|
|
232
|
+
skillRows,
|
|
233
|
+
fileAutocompletePanel,
|
|
234
|
+
fileAutocompleteTitle,
|
|
235
|
+
fileAutocompleteRows,
|
|
236
|
+
questionPanel,
|
|
237
|
+
questionTitle,
|
|
238
|
+
questionSpacer,
|
|
239
|
+
questionRows,
|
|
240
|
+
inputBox,
|
|
241
|
+
prompt,
|
|
242
|
+
inputField,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../../../src/tui/layout.ts"],"names":[],"mappings":"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"}
|