@duetso/agent 0.1.81 → 0.1.83
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 +69 -3
- package/dist/src/turn-runner/turn-runner.d.ts.map +1 -1
- package/dist/src/turn-runner/turn-runner.js +123 -20
- package/dist/src/turn-runner/turn-runner.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import { type CliRenderer, type ScrollBoxRenderable, type TextareaRenderable } from "@opentui/core";
|
|
3
|
+
import type { TranscriptWriter } from "./transcript-writer.js";
|
|
4
|
+
export interface StarterSectionOptions {
|
|
5
|
+
/** Working directory used to seed cwd-aware starter prompts. */
|
|
6
|
+
workDir: string;
|
|
7
|
+
/** Current session id; excluded from the recent-sessions list so the
|
|
8
|
+
* "pick up the thread" rows do not advertise the live session. */
|
|
9
|
+
sessionId: string;
|
|
10
|
+
/** Optional resumed-conversation history fed into starter selection so
|
|
11
|
+
* the picker can surface a resume prompt synthesized from the last
|
|
12
|
+
* user turn. */
|
|
13
|
+
history?: AgentMessage[];
|
|
14
|
+
/** Composer renderable. The section watches `plainText.length` to decide
|
|
15
|
+
* whether to repaint the chrome on `syncVisibility`. */
|
|
16
|
+
inputField: TextareaRenderable;
|
|
17
|
+
/** Transcript scrollbox; each rendered starter row is appended to the
|
|
18
|
+
* scrollbox via `transcriptWriter.addLine` and removed on dismiss. */
|
|
19
|
+
transcript: ScrollBoxRenderable;
|
|
20
|
+
/** Transcript writer surfaces `addLine` for the rendered starter rows
|
|
21
|
+
* (so the scrollbox owns them) but the section never writes any
|
|
22
|
+
* bordered blocks. */
|
|
23
|
+
transcriptWriter: TranscriptWriter;
|
|
24
|
+
/** Renderer; only used to tear the TUI down when a "pick up the thread"
|
|
25
|
+
* row signals a resume to the outer dispatcher. */
|
|
26
|
+
renderer: CliRenderer;
|
|
27
|
+
/** Submit a typed prompt as if the user had hit Enter. Wired to
|
|
28
|
+
* `runTui`'s `submit` so non-resume starter rows kick off a real turn. */
|
|
29
|
+
submit: (text: string) => void;
|
|
30
|
+
/** When wired, recent-session rows route through this callback so the
|
|
31
|
+
* outer dispatcher can swap sessions; when unset (tests, playground)
|
|
32
|
+
* the section falls back to re-submitting the prior prompt. */
|
|
33
|
+
onResumeRequest?: (sessionId: string) => void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Boot-screen starter picker. Offers a small set of context-aware prompts
|
|
37
|
+
* so first-time and returning users land on something concrete in <2
|
|
38
|
+
* seconds instead of staring at a blank input.
|
|
39
|
+
*
|
|
40
|
+
* Lifecycle:
|
|
41
|
+
*
|
|
42
|
+
* - `mount(skills)` runs once on boot and lays down the section.
|
|
43
|
+
* - `syncVisibility()` hides/restores the chrome as the composer fills
|
|
44
|
+
* and empties, until the user actually commits a prompt.
|
|
45
|
+
* - The first submit calls `destroyPermanently()` and the section
|
|
46
|
+
* never comes back this session.
|
|
47
|
+
*
|
|
48
|
+
* `runTui` skips construction entirely when `input.isResume` is set —
|
|
49
|
+
* the user explicitly asked to drop back into a known conversation and
|
|
50
|
+
* "what should we work on today?" before replaying history is noise.
|
|
51
|
+
*/
|
|
52
|
+
export declare class StarterSection {
|
|
53
|
+
private readonly opts;
|
|
54
|
+
private readonly entries;
|
|
55
|
+
private readonly refs;
|
|
56
|
+
private readonly rowIndexes;
|
|
57
|
+
private highlightedIndex;
|
|
58
|
+
private visible;
|
|
59
|
+
private permanentlyDismissed;
|
|
60
|
+
private skillCount;
|
|
61
|
+
constructor(opts: StarterSectionOptions);
|
|
62
|
+
/**
|
|
63
|
+
* Build the entry list from cwd-aware starter selection plus the recent
|
|
64
|
+
* sessions on disk, then paint the chrome. Idempotent across the section
|
|
65
|
+
* lifecycle in the sense that calling it again with no entries is a
|
|
66
|
+
* no-op, but typical use is exactly once on boot.
|
|
67
|
+
*/
|
|
68
|
+
mount(skills: ReadonlyArray<{
|
|
69
|
+
name: string;
|
|
70
|
+
}>): void;
|
|
71
|
+
isVisible(): boolean;
|
|
72
|
+
move(delta: number): void;
|
|
73
|
+
jump(targetIndex: number): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Transient hide. Tears down the rendered refs but keeps `entries` and
|
|
76
|
+
* `highlightedIndex` so `mountChrome()` can restore the exact same
|
|
77
|
+
* picker if the user backspaces the composer empty again.
|
|
78
|
+
*/
|
|
79
|
+
dismiss(): void;
|
|
80
|
+
/**
|
|
81
|
+
* Permanent destruction: called when the user commits a prompt. After
|
|
82
|
+
* this the chrome never comes back, even on backspace-to-empty.
|
|
83
|
+
*/
|
|
84
|
+
destroyPermanently(): void;
|
|
85
|
+
isPermanentlyDismissed(): boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Toggle hook called from `inputField.onContentChange`. Hides the chrome
|
|
88
|
+
* as soon as the user starts composing, brings it back if they backspace
|
|
89
|
+
* the composer empty (but only until they actually submit — then
|
|
90
|
+
* `permanentlyDismissed` latches).
|
|
91
|
+
*/
|
|
92
|
+
syncVisibility(): void;
|
|
93
|
+
submitHighlighted(): boolean;
|
|
94
|
+
private mountChrome;
|
|
95
|
+
private addLine;
|
|
96
|
+
private formatRow;
|
|
97
|
+
private paintHighlight;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=starter-section.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"starter-section.d.ts","sourceRoot":"","sources":["../../../src/tui/starter-section.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,mBAAmB,EAExB,KAAK,kBAAkB,EACxB,MAAM,eAAe,CAAC;AAKvB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB;uEACmE;IACnE,SAAS,EAAE,MAAM,CAAC;IAClB;;qBAEiB;IACjB,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB;6DACyD;IACzD,UAAU,EAAE,kBAAkB,CAAC;IAC/B;2EACuE;IACvE,UAAU,EAAE,mBAAmB,CAAC;IAChC;;2BAEuB;IACvB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC;wDACoD;IACpD,QAAQ,EAAE,WAAW,CAAC;IACtB;+EAC2E;IAC3E,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B;;oEAEgE;IAChE,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C;AAMD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,cAAc;IAgBb,OAAO,CAAC,QAAQ,CAAC,IAAI;IAfjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwB;IAG7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgB;IAC3C,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,OAAO,CAAS;IAIxB,OAAO,CAAC,oBAAoB,CAAS;IAGrC,OAAO,CAAC,UAAU,CAAK;gBAEM,IAAI,EAAE,qBAAqB;IAExD;;;;;OAKG;IACH,KAAK,CAAC,MAAM,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI;IAmCpD,SAAS,IAAI,OAAO;IAIpB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAOzB,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAQlC;;;;OAIG;IACH,OAAO,IAAI,IAAI;IAWf;;;OAGG;IACH,kBAAkB,IAAI,IAAI;IAO1B,sBAAsB,IAAI,OAAO;IAIjC;;;;;OAKG;IACH,cAAc,IAAI,IAAI;IAWtB,iBAAiB,IAAI,OAAO;IA6B5B,OAAO,CAAC,WAAW;IAiEnB,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,cAAc;CAQvB"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { listRecentSessions } from "./recent-sessions.js";
|
|
2
|
+
import { orderedSelectableStarters, selectStarters } from "./starters.js";
|
|
3
|
+
import { COLORS } from "./theme.js";
|
|
4
|
+
/**
|
|
5
|
+
* Boot-screen starter picker. Offers a small set of context-aware prompts
|
|
6
|
+
* so first-time and returning users land on something concrete in <2
|
|
7
|
+
* seconds instead of staring at a blank input.
|
|
8
|
+
*
|
|
9
|
+
* Lifecycle:
|
|
10
|
+
*
|
|
11
|
+
* - `mount(skills)` runs once on boot and lays down the section.
|
|
12
|
+
* - `syncVisibility()` hides/restores the chrome as the composer fills
|
|
13
|
+
* and empties, until the user actually commits a prompt.
|
|
14
|
+
* - The first submit calls `destroyPermanently()` and the section
|
|
15
|
+
* never comes back this session.
|
|
16
|
+
*
|
|
17
|
+
* `runTui` skips construction entirely when `input.isResume` is set —
|
|
18
|
+
* the user explicitly asked to drop back into a known conversation and
|
|
19
|
+
* "what should we work on today?" before replaying history is noise.
|
|
20
|
+
*/
|
|
21
|
+
export class StarterSection {
|
|
22
|
+
opts;
|
|
23
|
+
entries = [];
|
|
24
|
+
refs = [];
|
|
25
|
+
// Indexes within `refs` that correspond to numbered rows; used to
|
|
26
|
+
// repaint highlight on arrow / digit navigation.
|
|
27
|
+
rowIndexes = [];
|
|
28
|
+
highlightedIndex = 0;
|
|
29
|
+
visible = false;
|
|
30
|
+
// Once the user commits a prompt, the chrome is gone for the session.
|
|
31
|
+
// Until then `dismiss()` is a reversible hide that `syncVisibility()`
|
|
32
|
+
// can restore when the composer empties again.
|
|
33
|
+
permanentlyDismissed = false;
|
|
34
|
+
// Cached so the chrome can re-render the trailing "✦ N skills · /help"
|
|
35
|
+
// line without re-running selectStarters.
|
|
36
|
+
skillCount = 0;
|
|
37
|
+
constructor(opts) {
|
|
38
|
+
this.opts = opts;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Build the entry list from cwd-aware starter selection plus the recent
|
|
42
|
+
* sessions on disk, then paint the chrome. Idempotent across the section
|
|
43
|
+
* lifecycle in the sense that calling it again with no entries is a
|
|
44
|
+
* no-op, but typical use is exactly once on boot.
|
|
45
|
+
*/
|
|
46
|
+
mount(skills) {
|
|
47
|
+
// Read recent sessions off disk synchronously. The helper swallows fs
|
|
48
|
+
// errors and returns an empty list; a missing/empty ~/.duet/sessions
|
|
49
|
+
// directory is the common first-boot case.
|
|
50
|
+
const recentSessions = listRecentSessions({
|
|
51
|
+
excludeId: this.opts.sessionId,
|
|
52
|
+
limit: 4,
|
|
53
|
+
});
|
|
54
|
+
const result = selectStarters({
|
|
55
|
+
cwd: this.opts.workDir,
|
|
56
|
+
sessionHistory: this.opts.history,
|
|
57
|
+
recentSessions,
|
|
58
|
+
});
|
|
59
|
+
// Selectable rows in render order. Recent sessions lead so returning
|
|
60
|
+
// users hit "pick up the thread" first; new users see the cwd
|
|
61
|
+
// starters under the original "what should we work on today?" header.
|
|
62
|
+
const ordered = orderedSelectableStarters(result);
|
|
63
|
+
this.entries.length = 0;
|
|
64
|
+
for (const row of ordered) {
|
|
65
|
+
if (row.kind === "recent" && row.sessionId !== undefined) {
|
|
66
|
+
this.entries.push({
|
|
67
|
+
kind: "recent",
|
|
68
|
+
label: row.label,
|
|
69
|
+
submit: row.submit,
|
|
70
|
+
sessionId: row.sessionId,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
this.entries.push({ kind: "prompt", label: row.label, submit: row.submit });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
this.skillCount = skills.length;
|
|
78
|
+
this.highlightedIndex = 0;
|
|
79
|
+
this.mountChrome();
|
|
80
|
+
}
|
|
81
|
+
isVisible() {
|
|
82
|
+
return this.visible;
|
|
83
|
+
}
|
|
84
|
+
move(delta) {
|
|
85
|
+
if (!this.visible || this.entries.length === 0)
|
|
86
|
+
return;
|
|
87
|
+
const next = (this.highlightedIndex + delta + this.entries.length) % this.entries.length;
|
|
88
|
+
this.highlightedIndex = next;
|
|
89
|
+
this.paintHighlight();
|
|
90
|
+
}
|
|
91
|
+
jump(targetIndex) {
|
|
92
|
+
if (!this.visible)
|
|
93
|
+
return false;
|
|
94
|
+
if (targetIndex < 0 || targetIndex >= this.entries.length)
|
|
95
|
+
return false;
|
|
96
|
+
this.highlightedIndex = targetIndex;
|
|
97
|
+
this.paintHighlight();
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Transient hide. Tears down the rendered refs but keeps `entries` and
|
|
102
|
+
* `highlightedIndex` so `mountChrome()` can restore the exact same
|
|
103
|
+
* picker if the user backspaces the composer empty again.
|
|
104
|
+
*/
|
|
105
|
+
dismiss() {
|
|
106
|
+
if (!this.visible && this.refs.length === 0)
|
|
107
|
+
return;
|
|
108
|
+
for (const ref of this.refs) {
|
|
109
|
+
this.opts.transcript.remove(ref.id);
|
|
110
|
+
ref.destroy();
|
|
111
|
+
}
|
|
112
|
+
this.refs.length = 0;
|
|
113
|
+
this.rowIndexes.length = 0;
|
|
114
|
+
this.visible = false;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Permanent destruction: called when the user commits a prompt. After
|
|
118
|
+
* this the chrome never comes back, even on backspace-to-empty.
|
|
119
|
+
*/
|
|
120
|
+
destroyPermanently() {
|
|
121
|
+
this.dismiss();
|
|
122
|
+
this.entries.length = 0;
|
|
123
|
+
this.highlightedIndex = 0;
|
|
124
|
+
this.permanentlyDismissed = true;
|
|
125
|
+
}
|
|
126
|
+
isPermanentlyDismissed() {
|
|
127
|
+
return this.permanentlyDismissed;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Toggle hook called from `inputField.onContentChange`. Hides the chrome
|
|
131
|
+
* as soon as the user starts composing, brings it back if they backspace
|
|
132
|
+
* the composer empty (but only until they actually submit — then
|
|
133
|
+
* `permanentlyDismissed` latches).
|
|
134
|
+
*/
|
|
135
|
+
syncVisibility() {
|
|
136
|
+
if (this.permanentlyDismissed)
|
|
137
|
+
return;
|
|
138
|
+
if (this.entries.length === 0)
|
|
139
|
+
return;
|
|
140
|
+
const empty = this.opts.inputField.plainText.length === 0;
|
|
141
|
+
if (!empty && this.visible) {
|
|
142
|
+
this.dismiss();
|
|
143
|
+
}
|
|
144
|
+
else if (empty && !this.visible) {
|
|
145
|
+
this.mountChrome();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
submitHighlighted() {
|
|
149
|
+
if (!this.visible)
|
|
150
|
+
return false;
|
|
151
|
+
const entry = this.entries[this.highlightedIndex];
|
|
152
|
+
if (!entry)
|
|
153
|
+
return false;
|
|
154
|
+
this.destroyPermanently();
|
|
155
|
+
// Recent-session rows: when the host wires `onResumeRequest`, signal
|
|
156
|
+
// the outer dispatcher to swap sessions and tear down this renderer.
|
|
157
|
+
// The dispatcher disposes the placeholder, calls `manager.resume(id)`
|
|
158
|
+
// + `hydrate()` + `start()`, and re-enters `runTui` with the hydrated
|
|
159
|
+
// session + its full message history. End user lands on the same
|
|
160
|
+
// session id, same agent context, same transcript replayed inline.
|
|
161
|
+
//
|
|
162
|
+
// When no callback is wired (tests, playground), fall back to the
|
|
163
|
+
// legacy shortcut: re-submit the prior prompt in the current session.
|
|
164
|
+
// Agent has no context; the user lands on the same task with one
|
|
165
|
+
// keystroke instead of typing it again.
|
|
166
|
+
if (entry.kind === "recent" && this.opts.onResumeRequest) {
|
|
167
|
+
this.opts.onResumeRequest(entry.sessionId);
|
|
168
|
+
this.opts.renderer.destroy();
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
this.opts.submit(entry.submit);
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
// Paint the chrome (section headers, numbered rows, hint footer) from
|
|
175
|
+
// the current `entries` + `highlightedIndex`. Called on first boot and
|
|
176
|
+
// on every backspace-to-empty restoration. Idempotent: bails if the
|
|
177
|
+
// chrome is already mounted or no entries exist.
|
|
178
|
+
mountChrome() {
|
|
179
|
+
if (this.visible || this.entries.length === 0)
|
|
180
|
+
return;
|
|
181
|
+
const recentEntries = this.entries.filter((entry) => entry.kind === "recent");
|
|
182
|
+
const promptEntries = this.entries.filter((entry) => entry.kind === "prompt");
|
|
183
|
+
const hasRecent = recentEntries.length > 0;
|
|
184
|
+
// Every line we render here — spacers included — goes through
|
|
185
|
+
// `addLine` and gets pushed to `refs`. `dismiss()` iterates that list
|
|
186
|
+
// to destroy refs; spacers added via fire-and-forget `appendLine`
|
|
187
|
+
// would leak and accumulate above the next mount on each type →
|
|
188
|
+
// backspace cycle, pushing the chrome lower every time.
|
|
189
|
+
const spacer = () => {
|
|
190
|
+
this.refs.push(this.addLine(" ", COLORS.hint));
|
|
191
|
+
};
|
|
192
|
+
this.rowIndexes.length = 0;
|
|
193
|
+
spacer();
|
|
194
|
+
if (hasRecent) {
|
|
195
|
+
this.refs.push(this.addLine("pick up the thread", COLORS.agent));
|
|
196
|
+
spacer();
|
|
197
|
+
for (let i = 0; i < this.entries.length; i += 1) {
|
|
198
|
+
if (this.entries[i].kind !== "recent")
|
|
199
|
+
continue;
|
|
200
|
+
const ref = this.addLine(this.formatRow(i, false), COLORS.hint);
|
|
201
|
+
this.rowIndexes.push(this.refs.length);
|
|
202
|
+
this.refs.push(ref);
|
|
203
|
+
}
|
|
204
|
+
if (promptEntries.length > 0) {
|
|
205
|
+
spacer();
|
|
206
|
+
this.refs.push(this.addLine("or start something new", COLORS.agent));
|
|
207
|
+
spacer();
|
|
208
|
+
for (let i = 0; i < this.entries.length; i += 1) {
|
|
209
|
+
if (this.entries[i].kind !== "prompt")
|
|
210
|
+
continue;
|
|
211
|
+
const ref = this.addLine(this.formatRow(i, false), COLORS.hint);
|
|
212
|
+
this.rowIndexes.push(this.refs.length);
|
|
213
|
+
this.refs.push(ref);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
this.refs.push(this.addLine("what should we work on today?", COLORS.agent));
|
|
219
|
+
spacer();
|
|
220
|
+
for (let i = 0; i < this.entries.length; i += 1) {
|
|
221
|
+
const ref = this.addLine(this.formatRow(i, false), COLORS.hint);
|
|
222
|
+
this.rowIndexes.push(this.refs.length);
|
|
223
|
+
this.refs.push(ref);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
spacer();
|
|
227
|
+
this.refs.push(this.addLine("type a number to run, ↑/↓ to highlight, or just start typing.", COLORS.hint));
|
|
228
|
+
this.refs.push(this.addLine(`✦ ${this.skillCount} skill${this.skillCount === 1 ? "" : "s"} · /help`, COLORS.hint));
|
|
229
|
+
this.visible = true;
|
|
230
|
+
if (this.highlightedIndex >= this.entries.length)
|
|
231
|
+
this.highlightedIndex = 0;
|
|
232
|
+
this.paintHighlight();
|
|
233
|
+
}
|
|
234
|
+
addLine(content, fg) {
|
|
235
|
+
return this.opts.transcriptWriter.addLine(content, fg);
|
|
236
|
+
}
|
|
237
|
+
formatRow(index, highlighted) {
|
|
238
|
+
const entry = this.entries[index];
|
|
239
|
+
const text = entry?.label ?? "";
|
|
240
|
+
const number = index + 1;
|
|
241
|
+
const arrow = highlighted ? "▶" : "→";
|
|
242
|
+
const numberCell = highlighted ? `[${number}]` : ` ${number} `;
|
|
243
|
+
return ` ${numberCell} ${arrow} ${text}`;
|
|
244
|
+
}
|
|
245
|
+
paintHighlight() {
|
|
246
|
+
for (let i = 0; i < this.rowIndexes.length; i += 1) {
|
|
247
|
+
const ref = this.refs[this.rowIndexes[i]];
|
|
248
|
+
const isHighlighted = i === this.highlightedIndex;
|
|
249
|
+
ref.content = this.formatRow(i, isHighlighted);
|
|
250
|
+
ref.fg = isHighlighted ? COLORS.user : COLORS.hint;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=starter-section.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"starter-section.js","sourceRoot":"","sources":["../../../src/tui/starter-section.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { type CliRenderer, type TextRenderable } from "@opentui/core";
|
|
2
|
+
import type { TurnTerminalEvent } from "../types/protocol.js";
|
|
3
|
+
export interface StatusControllerOptions {
|
|
4
|
+
/** Renderer that owns the chrome TextBuffers. Passed through so the
|
|
5
|
+
* controller can be constructed alongside the other chrome-touching
|
|
6
|
+
* controllers without re-deriving it from `status`/`hint`. */
|
|
7
|
+
renderer: CliRenderer;
|
|
8
|
+
/** Working-status line ("● working… (3s)"). Mutated by `setStatus` and the
|
|
9
|
+
* ticker; cleared when idle. */
|
|
10
|
+
status: TextRenderable;
|
|
11
|
+
/** Bottom hint row. Recomposed by `setHint` from the running/attachment/
|
|
12
|
+
* selection segments. */
|
|
13
|
+
hint: TextRenderable;
|
|
14
|
+
/**
|
|
15
|
+
* Re-paints every in-flight tool block header with a fresh spinner +
|
|
16
|
+
* elapsed counter. Invoked from the ticker so spinners advance in step
|
|
17
|
+
* with the working-status line. Provided externally because the tool
|
|
18
|
+
* block map lives with the step renderer, not the status controller.
|
|
19
|
+
*/
|
|
20
|
+
refreshActiveToolBlocks: () => void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Owns the "is a turn running?" surface of the TUI: the working-status line,
|
|
24
|
+
* the bottom hint row, and the 1 s ticker that advances both while a turn is
|
|
25
|
+
* in flight. Also remembers the most recent terminal event so `runTui` can
|
|
26
|
+
* return it to the caller after the renderer is destroyed.
|
|
27
|
+
*
|
|
28
|
+
* Preserves the v0.1.68 dual-mechanism guard against the OpenTUI race where
|
|
29
|
+
* child TextBuffers are destroyed synchronously *before* the `destroy` event
|
|
30
|
+
* fires: writes short-circuit on `destroyed`, and a `try/catch` around the
|
|
31
|
+
* actual buffer mutation swallows the late-arriving exception and tears down
|
|
32
|
+
* the ticker so the next callback does not throw again.
|
|
33
|
+
*/
|
|
34
|
+
export declare class StatusController {
|
|
35
|
+
private readonly opts;
|
|
36
|
+
private destroyed;
|
|
37
|
+
private running;
|
|
38
|
+
private workingStartedAt;
|
|
39
|
+
private workingTicker;
|
|
40
|
+
private workingMessage;
|
|
41
|
+
private queuedFollowUps;
|
|
42
|
+
private pendingImageCount;
|
|
43
|
+
private lastSelectionText;
|
|
44
|
+
private terminal;
|
|
45
|
+
constructor(opts: StatusControllerOptions);
|
|
46
|
+
isRunning(): boolean;
|
|
47
|
+
lastTerminal(): TurnTerminalEvent | undefined;
|
|
48
|
+
/** Wall-clock start of the current turn, or undefined while idle. Read by
|
|
49
|
+
* the "● turn finished in Ns" and sleep banners which live outside this
|
|
50
|
+
* controller but want to show the same elapsed counter. */
|
|
51
|
+
getWorkingStartedAt(): number | undefined;
|
|
52
|
+
setWorkingMessage(message: string): void;
|
|
53
|
+
setQueuedFollowUps(count: number): void;
|
|
54
|
+
/** Mirror of the most recent drag-selection text. Stored here so the hint
|
|
55
|
+
* row can advertise the copy keystroke only while something is selectable;
|
|
56
|
+
* the canonical selection state lives in `runTui`. */
|
|
57
|
+
setSelectionText(text: string): void;
|
|
58
|
+
/** Mirror of `pendingImages.length`. Stored here so the hint row can show
|
|
59
|
+
* the attachment count without dragging the whole `PendingImage[]` into
|
|
60
|
+
* the controller. */
|
|
61
|
+
setPendingImageCount(count: number): void;
|
|
62
|
+
markRunning(): void;
|
|
63
|
+
/** Settle the working-status surface and optionally record the terminal
|
|
64
|
+
* event that ended the turn. The terminal is exposed via `lastTerminal()`
|
|
65
|
+
* so `runTui` can return it to its caller after renderer teardown. */
|
|
66
|
+
markIdle(terminal?: TurnTerminalEvent): void;
|
|
67
|
+
setStatus(text: string): void;
|
|
68
|
+
/** Recompose the bottom hint row from the controller's current running
|
|
69
|
+
* state plus any active attachment / selection segments. Called from any
|
|
70
|
+
* input that changes a hint segment (attachments, selection) and from
|
|
71
|
+
* the running-state transitions in `markRunning` / `markIdle`. */
|
|
72
|
+
refreshHint(): void;
|
|
73
|
+
refreshWorkingStatus(): void;
|
|
74
|
+
/** Flip the destroyed flag and stop the ticker. Invoked from the renderer
|
|
75
|
+
* destroy handler and from our own try/catch when a buffer write fires the
|
|
76
|
+
* destroyed-buffer guard mid-flight. Subsequent chrome writes no-op. */
|
|
77
|
+
shutdown(): void;
|
|
78
|
+
private startWorkingTicker;
|
|
79
|
+
private stopWorkingTicker;
|
|
80
|
+
private attachmentHint;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Renders an elapsed millisecond span as `${seconds}s` below a minute, and
|
|
84
|
+
* `${minutes}m ${seconds}s` past it. Exported because the tool-call finalizer
|
|
85
|
+
* and the post-turn banners share the same format.
|
|
86
|
+
*/
|
|
87
|
+
export declare function formatElapsed(ms: number): string;
|
|
88
|
+
/**
|
|
89
|
+
* Spinner marker for an in-flight tool call. Sub-second runs drop the
|
|
90
|
+
* elapsed counter so a transcript of fast tools is not littered with "0s"
|
|
91
|
+
* markers.
|
|
92
|
+
*/
|
|
93
|
+
export declare function runningMarker(elapsedMs: number): string;
|
|
94
|
+
//# sourceMappingURL=status-controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-controller.d.ts","sourceRoot":"","sources":["../../../src/tui/status-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAGtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,MAAM,WAAW,uBAAuB;IACtC;;mEAE+D;IAC/D,QAAQ,EAAE,WAAW,CAAC;IACtB;qCACiC;IACjC,MAAM,EAAE,cAAc,CAAC;IACvB;8BAC0B;IAC1B,IAAI,EAAE,cAAc,CAAC;IACrB;;;;;OAKG;IACH,uBAAuB,EAAE,MAAM,IAAI,CAAC;CACrC;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,gBAAgB;IAWf,OAAO,CAAC,QAAQ,CAAC,IAAI;IAVjC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,aAAa,CAA6C;IAClE,OAAO,CAAC,cAAc,CAAc;IACpC,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,QAAQ,CAAgC;gBAEnB,IAAI,EAAE,uBAAuB;IAE1D,SAAS,IAAI,OAAO;IAIpB,YAAY,IAAI,iBAAiB,GAAG,SAAS;IAI7C;;gEAE4D;IAC5D,mBAAmB,IAAI,MAAM,GAAG,SAAS;IAIzC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAKxC,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKvC;;2DAEuD;IACvD,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKpC;;0BAEsB;IACtB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKzC,WAAW,IAAI,IAAI;IASnB;;2EAEuE;IACvE,QAAQ,CAAC,QAAQ,CAAC,EAAE,iBAAiB,GAAG,IAAI;IAU5C,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAoB7B;;;uEAGmE;IACnE,WAAW,IAAI,IAAI;IAkBnB,oBAAoB,IAAI,IAAI;IAY5B;;6EAEyE;IACzE,QAAQ,IAAI,IAAI;IAKhB,OAAO,CAAC,kBAAkB;IAK1B,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,cAAc;CAIvB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAMhD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvD"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { HINT_IDLE, HINT_RUNNING, HINT_SELECTION_COPY } from "./theme.js";
|
|
2
|
+
import { isTextBufferDestroyedError } from "./transcript-writer.js";
|
|
3
|
+
/**
|
|
4
|
+
* Owns the "is a turn running?" surface of the TUI: the working-status line,
|
|
5
|
+
* the bottom hint row, and the 1 s ticker that advances both while a turn is
|
|
6
|
+
* in flight. Also remembers the most recent terminal event so `runTui` can
|
|
7
|
+
* return it to the caller after the renderer is destroyed.
|
|
8
|
+
*
|
|
9
|
+
* Preserves the v0.1.68 dual-mechanism guard against the OpenTUI race where
|
|
10
|
+
* child TextBuffers are destroyed synchronously *before* the `destroy` event
|
|
11
|
+
* fires: writes short-circuit on `destroyed`, and a `try/catch` around the
|
|
12
|
+
* actual buffer mutation swallows the late-arriving exception and tears down
|
|
13
|
+
* the ticker so the next callback does not throw again.
|
|
14
|
+
*/
|
|
15
|
+
export class StatusController {
|
|
16
|
+
opts;
|
|
17
|
+
destroyed = false;
|
|
18
|
+
running = false;
|
|
19
|
+
workingStartedAt;
|
|
20
|
+
workingTicker;
|
|
21
|
+
workingMessage = "working…";
|
|
22
|
+
queuedFollowUps = 0;
|
|
23
|
+
pendingImageCount = 0;
|
|
24
|
+
lastSelectionText = "";
|
|
25
|
+
terminal;
|
|
26
|
+
constructor(opts) {
|
|
27
|
+
this.opts = opts;
|
|
28
|
+
}
|
|
29
|
+
isRunning() {
|
|
30
|
+
return this.running;
|
|
31
|
+
}
|
|
32
|
+
lastTerminal() {
|
|
33
|
+
return this.terminal;
|
|
34
|
+
}
|
|
35
|
+
/** Wall-clock start of the current turn, or undefined while idle. Read by
|
|
36
|
+
* the "● turn finished in Ns" and sleep banners which live outside this
|
|
37
|
+
* controller but want to show the same elapsed counter. */
|
|
38
|
+
getWorkingStartedAt() {
|
|
39
|
+
return this.workingStartedAt;
|
|
40
|
+
}
|
|
41
|
+
setWorkingMessage(message) {
|
|
42
|
+
this.workingMessage = message;
|
|
43
|
+
this.refreshWorkingStatus();
|
|
44
|
+
}
|
|
45
|
+
setQueuedFollowUps(count) {
|
|
46
|
+
this.queuedFollowUps = count;
|
|
47
|
+
this.refreshWorkingStatus();
|
|
48
|
+
}
|
|
49
|
+
/** Mirror of the most recent drag-selection text. Stored here so the hint
|
|
50
|
+
* row can advertise the copy keystroke only while something is selectable;
|
|
51
|
+
* the canonical selection state lives in `runTui`. */
|
|
52
|
+
setSelectionText(text) {
|
|
53
|
+
this.lastSelectionText = text;
|
|
54
|
+
this.refreshHint();
|
|
55
|
+
}
|
|
56
|
+
/** Mirror of `pendingImages.length`. Stored here so the hint row can show
|
|
57
|
+
* the attachment count without dragging the whole `PendingImage[]` into
|
|
58
|
+
* the controller. */
|
|
59
|
+
setPendingImageCount(count) {
|
|
60
|
+
this.pendingImageCount = count;
|
|
61
|
+
this.refreshHint();
|
|
62
|
+
}
|
|
63
|
+
markRunning() {
|
|
64
|
+
this.running = true;
|
|
65
|
+
this.workingMessage = "working…";
|
|
66
|
+
this.workingStartedAt = Date.now();
|
|
67
|
+
this.refreshHint();
|
|
68
|
+
this.refreshWorkingStatus();
|
|
69
|
+
this.startWorkingTicker();
|
|
70
|
+
}
|
|
71
|
+
/** Settle the working-status surface and optionally record the terminal
|
|
72
|
+
* event that ended the turn. The terminal is exposed via `lastTerminal()`
|
|
73
|
+
* so `runTui` can return it to its caller after renderer teardown. */
|
|
74
|
+
markIdle(terminal) {
|
|
75
|
+
if (terminal !== undefined)
|
|
76
|
+
this.terminal = terminal;
|
|
77
|
+
this.running = false;
|
|
78
|
+
this.stopWorkingTicker();
|
|
79
|
+
this.workingStartedAt = undefined;
|
|
80
|
+
this.workingMessage = "working…";
|
|
81
|
+
this.refreshHint();
|
|
82
|
+
this.refreshWorkingStatus();
|
|
83
|
+
}
|
|
84
|
+
setStatus(text) {
|
|
85
|
+
// Renderer teardown destroys the underlying TextBuffer synchronously, but
|
|
86
|
+
// in-flight async work (session events, ticker callbacks, upgrade-status
|
|
87
|
+
// pushes) may still drive chrome updates on the next microtask. The
|
|
88
|
+
// `destroyed` flag catches writes after our destroy handler runs; the
|
|
89
|
+
// try/catch backstops the window between OpenTUI tearing down child
|
|
90
|
+
// TextBuffers and emitting the `destroy` event, which is when the ticker
|
|
91
|
+
// callback typically lands.
|
|
92
|
+
if (this.destroyed)
|
|
93
|
+
return;
|
|
94
|
+
try {
|
|
95
|
+
this.opts.status.content = text;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (isTextBufferDestroyedError(error)) {
|
|
99
|
+
this.shutdown();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Recompose the bottom hint row from the controller's current running
|
|
106
|
+
* state plus any active attachment / selection segments. Called from any
|
|
107
|
+
* input that changes a hint segment (attachments, selection) and from
|
|
108
|
+
* the running-state transitions in `markRunning` / `markIdle`. */
|
|
109
|
+
refreshHint() {
|
|
110
|
+
if (this.destroyed)
|
|
111
|
+
return;
|
|
112
|
+
const base = this.running ? HINT_RUNNING : HINT_IDLE;
|
|
113
|
+
const segments = [];
|
|
114
|
+
if (this.pendingImageCount > 0)
|
|
115
|
+
segments.push(this.attachmentHint());
|
|
116
|
+
segments.push(base);
|
|
117
|
+
if (this.lastSelectionText.trim().length > 0)
|
|
118
|
+
segments.push(HINT_SELECTION_COPY);
|
|
119
|
+
try {
|
|
120
|
+
this.opts.hint.content = segments.join(" · ");
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
if (isTextBufferDestroyedError(error)) {
|
|
124
|
+
this.shutdown();
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
refreshWorkingStatus() {
|
|
131
|
+
if (this.destroyed)
|
|
132
|
+
return;
|
|
133
|
+
this.opts.refreshActiveToolBlocks();
|
|
134
|
+
if (this.workingStartedAt === undefined) {
|
|
135
|
+
this.setStatus(this.queuedFollowUps > 0 ? `queued follow-ups: ${this.queuedFollowUps}` : "");
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const elapsed = formatElapsed(Date.now() - this.workingStartedAt);
|
|
139
|
+
const queued = this.queuedFollowUps > 0 ? ` · queued follow-ups: ${this.queuedFollowUps}` : "";
|
|
140
|
+
this.setStatus(`● ${this.workingMessage} (${elapsed})${queued}`);
|
|
141
|
+
}
|
|
142
|
+
/** Flip the destroyed flag and stop the ticker. Invoked from the renderer
|
|
143
|
+
* destroy handler and from our own try/catch when a buffer write fires the
|
|
144
|
+
* destroyed-buffer guard mid-flight. Subsequent chrome writes no-op. */
|
|
145
|
+
shutdown() {
|
|
146
|
+
this.destroyed = true;
|
|
147
|
+
this.stopWorkingTicker();
|
|
148
|
+
}
|
|
149
|
+
startWorkingTicker() {
|
|
150
|
+
if (this.workingTicker !== undefined)
|
|
151
|
+
return;
|
|
152
|
+
this.workingTicker = setInterval(() => this.refreshWorkingStatus(), 1000);
|
|
153
|
+
}
|
|
154
|
+
stopWorkingTicker() {
|
|
155
|
+
if (this.workingTicker !== undefined) {
|
|
156
|
+
clearInterval(this.workingTicker);
|
|
157
|
+
this.workingTicker = undefined;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
attachmentHint() {
|
|
161
|
+
const n = this.pendingImageCount;
|
|
162
|
+
return n === 1 ? "📎 1 image attached" : `📎 ${n} images attached`;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Renders an elapsed millisecond span as `${seconds}s` below a minute, and
|
|
167
|
+
* `${minutes}m ${seconds}s` past it. Exported because the tool-call finalizer
|
|
168
|
+
* and the post-turn banners share the same format.
|
|
169
|
+
*/
|
|
170
|
+
export function formatElapsed(ms) {
|
|
171
|
+
const totalSeconds = Math.max(0, Math.floor(ms / 1000));
|
|
172
|
+
if (totalSeconds < 60)
|
|
173
|
+
return `${totalSeconds}s`;
|
|
174
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
175
|
+
const seconds = totalSeconds % 60;
|
|
176
|
+
return `${minutes}m ${seconds}s`;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Spinner marker for an in-flight tool call. Sub-second runs drop the
|
|
180
|
+
* elapsed counter so a transcript of fast tools is not littered with "0s"
|
|
181
|
+
* markers.
|
|
182
|
+
*/
|
|
183
|
+
export function runningMarker(elapsedMs) {
|
|
184
|
+
return elapsedMs >= 1000 ? `⏳ ${formatElapsed(elapsedMs)}` : "⏳";
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=status-controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-controller.js","sourceRoot":"","sources":["../../../src/tui/status-controller.ts"],"names":[],"mappings":"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"}
|