@sagmans/dsh-tui 0.1.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/LICENSE +21 -0
- package/README.md +266 -0
- package/cordis.patch.yml +123 -0
- package/lib/agent/fork.d.ts +24 -0
- package/lib/agent/fork.d.ts.map +1 -0
- package/lib/agent/fork.js +25 -0
- package/lib/agent/fork.js.map +1 -0
- package/lib/agent/history.d.ts +78 -0
- package/lib/agent/history.d.ts.map +1 -0
- package/lib/agent/history.js +179 -0
- package/lib/agent/history.js.map +1 -0
- package/lib/agent/host.d.ts +58 -0
- package/lib/agent/host.d.ts.map +1 -0
- package/lib/agent/host.js +59 -0
- package/lib/agent/host.js.map +1 -0
- package/lib/agent/model.d.ts +61 -0
- package/lib/agent/model.d.ts.map +1 -0
- package/lib/agent/model.js +92 -0
- package/lib/agent/model.js.map +1 -0
- package/lib/agent/present.d.ts +12 -0
- package/lib/agent/present.d.ts.map +1 -0
- package/lib/agent/present.js +52 -0
- package/lib/agent/present.js.map +1 -0
- package/lib/agent/presets.d.ts +75 -0
- package/lib/agent/presets.d.ts.map +1 -0
- package/lib/agent/presets.js +117 -0
- package/lib/agent/presets.js.map +1 -0
- package/lib/agent/projections.d.ts +16 -0
- package/lib/agent/projections.d.ts.map +1 -0
- package/lib/agent/projections.js +31 -0
- package/lib/agent/projections.js.map +1 -0
- package/lib/agent/status.d.ts +46 -0
- package/lib/agent/status.d.ts.map +1 -0
- package/lib/agent/status.js +67 -0
- package/lib/agent/status.js.map +1 -0
- package/lib/cards.d.ts +88 -0
- package/lib/cards.d.ts.map +1 -0
- package/lib/cards.js +218 -0
- package/lib/cards.js.map +1 -0
- package/lib/compat/probe.d.ts +33 -0
- package/lib/compat/probe.d.ts.map +1 -0
- package/lib/compat/probe.js +74 -0
- package/lib/compat/probe.js.map +1 -0
- package/lib/config.d.ts +13 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +49 -0
- package/lib/config.js.map +1 -0
- package/lib/contracts.d.ts +32 -0
- package/lib/contracts.d.ts.map +1 -0
- package/lib/contracts.js +2 -0
- package/lib/contracts.js.map +1 -0
- package/lib/export.d.ts +15 -0
- package/lib/export.d.ts.map +1 -0
- package/lib/export.js +52 -0
- package/lib/export.js.map +1 -0
- package/lib/gates.d.ts +85 -0
- package/lib/gates.d.ts.map +1 -0
- package/lib/gates.js +216 -0
- package/lib/gates.js.map +1 -0
- package/lib/identity.d.ts +33 -0
- package/lib/identity.d.ts.map +1 -0
- package/lib/identity.js +49 -0
- package/lib/identity.js.map +1 -0
- package/lib/index.d.ts +24 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +1196 -0
- package/lib/index.js.map +1 -0
- package/lib/input/completion.d.ts +22 -0
- package/lib/input/completion.d.ts.map +1 -0
- package/lib/input/completion.js +27 -0
- package/lib/input/completion.js.map +1 -0
- package/lib/input/submission.d.ts +62 -0
- package/lib/input/submission.d.ts.map +1 -0
- package/lib/input/submission.js +80 -0
- package/lib/input/submission.js.map +1 -0
- package/lib/jobs.d.ts +64 -0
- package/lib/jobs.d.ts.map +1 -0
- package/lib/jobs.js +123 -0
- package/lib/jobs.js.map +1 -0
- package/lib/startup.d.ts +17 -0
- package/lib/startup.d.ts.map +1 -0
- package/lib/startup.js +67 -0
- package/lib/startup.js.map +1 -0
- package/lib/subagents.d.ts +74 -0
- package/lib/subagents.d.ts.map +1 -0
- package/lib/subagents.js +141 -0
- package/lib/subagents.js.map +1 -0
- package/lib/terminal/bell.d.ts +18 -0
- package/lib/terminal/bell.d.ts.map +1 -0
- package/lib/terminal/bell.js +16 -0
- package/lib/terminal/bell.js.map +1 -0
- package/lib/terminal/clipboard.d.ts +11 -0
- package/lib/terminal/clipboard.d.ts.map +1 -0
- package/lib/terminal/clipboard.js +18 -0
- package/lib/terminal/clipboard.js.map +1 -0
- package/lib/terminal/restore.d.ts +17 -0
- package/lib/terminal/restore.d.ts.map +1 -0
- package/lib/terminal/restore.js +43 -0
- package/lib/terminal/restore.js.map +1 -0
- package/lib/terminal/title.d.ts +22 -0
- package/lib/terminal/title.d.ts.map +1 -0
- package/lib/terminal/title.js +34 -0
- package/lib/terminal/title.js.map +1 -0
- package/lib/text.d.ts +19 -0
- package/lib/text.d.ts.map +1 -0
- package/lib/text.js +61 -0
- package/lib/text.js.map +1 -0
- package/lib/theme.d.ts +47 -0
- package/lib/theme.d.ts.map +1 -0
- package/lib/theme.js +84 -0
- package/lib/theme.js.map +1 -0
- package/lib/transcript.d.ts +91 -0
- package/lib/transcript.d.ts.map +1 -0
- package/lib/transcript.js +315 -0
- package/lib/transcript.js.map +1 -0
- package/lib/ui/dock.d.ts +36 -0
- package/lib/ui/dock.d.ts.map +1 -0
- package/lib/ui/dock.js +110 -0
- package/lib/ui/dock.js.map +1 -0
- package/lib/ui/markdown.d.ts +17 -0
- package/lib/ui/markdown.d.ts.map +1 -0
- package/lib/ui/markdown.js +35 -0
- package/lib/ui/markdown.js.map +1 -0
- package/lib/ui/picker.d.ts +103 -0
- package/lib/ui/picker.d.ts.map +1 -0
- package/lib/ui/picker.js +165 -0
- package/lib/ui/picker.js.map +1 -0
- package/lib/ui/rows.d.ts +30 -0
- package/lib/ui/rows.d.ts.map +1 -0
- package/lib/ui/rows.js +49 -0
- package/lib/ui/rows.js.map +1 -0
- package/lib/ui/status.d.ts +49 -0
- package/lib/ui/status.d.ts.map +1 -0
- package/lib/ui/status.js +90 -0
- package/lib/ui/status.js.map +1 -0
- package/lib/ui/view.d.ts +56 -0
- package/lib/ui/view.d.ts.map +1 -0
- package/lib/ui/view.js +184 -0
- package/lib/ui/view.js.map +1 -0
- package/lib/work.d.ts +39 -0
- package/lib/work.d.ts.map +1 -0
- package/lib/work.js +97 -0
- package/lib/work.js.map +1 -0
- package/package.json +109 -0
package/lib/ui/dock.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { truncateToWidth } from '@earendil-works/pi-tui';
|
|
2
|
+
import { DOCK_JOB_LIMIT, describeJob, isLive } from "../jobs.js";
|
|
3
|
+
import { DOCK_SUBAGENT_LIMIT, describeSubagent } from "../subagents.js";
|
|
4
|
+
import { displayText } from "../text.js";
|
|
5
|
+
/** Todo rows the dock keeps on screen; the rest become a count. */
|
|
6
|
+
export const DOCK_TODO_LIMIT = 4;
|
|
7
|
+
const TODO_GLYPHS = {
|
|
8
|
+
pending: '☐',
|
|
9
|
+
in_progress: '▸',
|
|
10
|
+
completed: '☑',
|
|
11
|
+
};
|
|
12
|
+
/** What the reader most needs to see first: work in flight, then work left. */
|
|
13
|
+
function orderTodos(todos) {
|
|
14
|
+
const rank = { in_progress: 0, pending: 1, completed: 2 };
|
|
15
|
+
return [...todos].sort((left, right) => rank[left.status] - rank[right.status]);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The dock above the editor: what the agent is working on right now.
|
|
19
|
+
*
|
|
20
|
+
* It renders nothing when there is nothing to say, so a plain conversation
|
|
21
|
+
* keeps its rows for the conversation.
|
|
22
|
+
*/
|
|
23
|
+
export class WorkDock {
|
|
24
|
+
state;
|
|
25
|
+
theme;
|
|
26
|
+
jobs;
|
|
27
|
+
subagents;
|
|
28
|
+
now;
|
|
29
|
+
constructor(state, theme,
|
|
30
|
+
/** Live background jobs: process state, not something a resume can replay. */
|
|
31
|
+
jobs = () => [],
|
|
32
|
+
/** Delegations this session started; also live state. */
|
|
33
|
+
subagents = () => [],
|
|
34
|
+
/** Clock for elapsed times, so a frame can be pinned in a test. */
|
|
35
|
+
now = () => Date.now()) {
|
|
36
|
+
this.state = state;
|
|
37
|
+
this.theme = theme;
|
|
38
|
+
this.jobs = jobs;
|
|
39
|
+
this.subagents = subagents;
|
|
40
|
+
this.now = now;
|
|
41
|
+
}
|
|
42
|
+
invalidate() {
|
|
43
|
+
// The fold is the only state; nothing is cached.
|
|
44
|
+
}
|
|
45
|
+
pushTodos(lines, todos, width) {
|
|
46
|
+
const done = todos.filter(todo => todo.status === 'completed').length;
|
|
47
|
+
lines.push(this.theme.bold(truncateToWidth(`☰ todos ${done}/${todos.length} done`, width, '…')));
|
|
48
|
+
const ordered = orderTodos(todos);
|
|
49
|
+
for (const todo of ordered.slice(0, DOCK_TODO_LIMIT)) {
|
|
50
|
+
lines.push(this.theme.dim(truncateToWidth(` ${TODO_GLYPHS[todo.status]} ${displayText(todo.content)}`, width, '…')));
|
|
51
|
+
}
|
|
52
|
+
if (ordered.length > DOCK_TODO_LIMIT) {
|
|
53
|
+
lines.push(this.theme.dim(truncateToWidth(` … ${ordered.length - DOCK_TODO_LIMIT} more`, width, '…')));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
pushSubagents(lines, runs, width) {
|
|
57
|
+
const running = runs.filter(run => run.status === 'running').length;
|
|
58
|
+
lines.push(this.theme.bold(truncateToWidth(`⚇ subagents · ${running} running, ${runs.length - running} done`, width, '…')));
|
|
59
|
+
const now = this.now();
|
|
60
|
+
for (const run of runs.slice(0, DOCK_SUBAGENT_LIMIT)) {
|
|
61
|
+
const glyph = run.status === 'running' ? '▸' : run.status === 'completed' ? '✓' : '✗';
|
|
62
|
+
lines.push(this.theme.dim(truncateToWidth(` ${glyph} ${displayText(describeSubagent(run, now))}`, width, '…')));
|
|
63
|
+
}
|
|
64
|
+
if (runs.length > DOCK_SUBAGENT_LIMIT) {
|
|
65
|
+
lines.push(this.theme.dim(truncateToWidth(` … ${runs.length - DOCK_SUBAGENT_LIMIT} more`, width, '…')));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
pushJobs(lines, jobs, width) {
|
|
69
|
+
const live = jobs.filter(job => isLive(job.status)).length;
|
|
70
|
+
lines.push(this.theme.bold(truncateToWidth(`⛭ jobs · ${live} running, ${jobs.length - live} done`, width, '…')));
|
|
71
|
+
// Work still holding resources first, then the most recent.
|
|
72
|
+
const ordered = [...jobs].sort((left, right) => Number(isLive(right.status)) - Number(isLive(left.status)) || right.startedAt - left.startedAt);
|
|
73
|
+
const now = this.now();
|
|
74
|
+
for (const job of ordered.slice(0, DOCK_JOB_LIMIT)) {
|
|
75
|
+
const glyph = isLive(job.status) ? '▸' : job.status === 'completed' ? '✓' : '✗';
|
|
76
|
+
lines.push(this.theme.dim(truncateToWidth(` ${glyph} ${displayText(describeJob(job, now))}`, width, '…')));
|
|
77
|
+
}
|
|
78
|
+
if (ordered.length > DOCK_JOB_LIMIT) {
|
|
79
|
+
lines.push(this.theme.dim(truncateToWidth(` … ${ordered.length - DOCK_JOB_LIMIT} more`, width, '…')));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
render(width) {
|
|
83
|
+
if (width <= 0)
|
|
84
|
+
return [];
|
|
85
|
+
const state = this.state();
|
|
86
|
+
const lines = [];
|
|
87
|
+
if (state.goal !== undefined) {
|
|
88
|
+
const rounds = state.goal.maxRounds === undefined
|
|
89
|
+
? `round ${state.goal.roundsStarted}`
|
|
90
|
+
: `round ${state.goal.roundsStarted}/${state.goal.maxRounds}`;
|
|
91
|
+
// The row itself decides where a long objective ends, so the reader
|
|
92
|
+
// always sees that something was left out.
|
|
93
|
+
const objective = state.goal.objective.replace(/\s+/gu, ' ');
|
|
94
|
+
lines.push(this.theme.bold(truncateToWidth(`◎ goal ${rounds} · ${displayText(objective)}`, width, '…')));
|
|
95
|
+
}
|
|
96
|
+
if (state.planMode) {
|
|
97
|
+
lines.push(this.theme.bold(truncateToWidth('⏸ plan mode · answer the plan before edits happen', width, '…')));
|
|
98
|
+
}
|
|
99
|
+
const subagents = this.subagents();
|
|
100
|
+
if (subagents.length > 0)
|
|
101
|
+
this.pushSubagents(lines, subagents, width);
|
|
102
|
+
const jobs = this.jobs();
|
|
103
|
+
if (jobs.length > 0)
|
|
104
|
+
this.pushJobs(lines, jobs, width);
|
|
105
|
+
if (state.todos !== undefined)
|
|
106
|
+
this.pushTodos(lines, state.todos, width);
|
|
107
|
+
return lines;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=dock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dock.js","sourceRoot":"","sources":["../../src/ui/dock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type MarkdownTheme } from '@earendil-works/pi-tui';
|
|
2
|
+
/** Parsed messages kept for redraws; everything above this is cold transcript. */
|
|
3
|
+
export declare const MARKDOWN_CACHE_LIMIT = 64;
|
|
4
|
+
/**
|
|
5
|
+
* Render assistant text as markdown without reparsing the whole history.
|
|
6
|
+
*
|
|
7
|
+
* A repaint walks every row, so parsing each message again on every keystroke
|
|
8
|
+
* would make a long session crawl. Parsing is keyed by message text because
|
|
9
|
+
* that is what changes; the component re-renders on its own when the width does.
|
|
10
|
+
*/
|
|
11
|
+
export declare class MarkdownRenderer {
|
|
12
|
+
private readonly theme;
|
|
13
|
+
private readonly parsed;
|
|
14
|
+
constructor(theme: MarkdownTheme);
|
|
15
|
+
render(text: string, width: number): string[];
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/ui/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAErE,kFAAkF;AAClF,eAAO,MAAM,oBAAoB,KAAK,CAAA;AAEtC;;;;;;GAMG;AACH,qBAAa,gBAAgB;IAGf,OAAO,CAAC,QAAQ,CAAC,KAAK;IAFlC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8B;gBAExB,KAAK,EAAE,aAAa;IAEjD,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;CAe9C"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Markdown } from '@earendil-works/pi-tui';
|
|
2
|
+
/** Parsed messages kept for redraws; everything above this is cold transcript. */
|
|
3
|
+
export const MARKDOWN_CACHE_LIMIT = 64;
|
|
4
|
+
/**
|
|
5
|
+
* Render assistant text as markdown without reparsing the whole history.
|
|
6
|
+
*
|
|
7
|
+
* A repaint walks every row, so parsing each message again on every keystroke
|
|
8
|
+
* would make a long session crawl. Parsing is keyed by message text because
|
|
9
|
+
* that is what changes; the component re-renders on its own when the width does.
|
|
10
|
+
*/
|
|
11
|
+
export class MarkdownRenderer {
|
|
12
|
+
theme;
|
|
13
|
+
parsed = new Map();
|
|
14
|
+
constructor(theme) {
|
|
15
|
+
this.theme = theme;
|
|
16
|
+
}
|
|
17
|
+
render(text, width) {
|
|
18
|
+
let message = this.parsed.get(text);
|
|
19
|
+
if (message === undefined) {
|
|
20
|
+
if (this.parsed.size >= MARKDOWN_CACHE_LIMIT) {
|
|
21
|
+
const oldest = this.parsed.keys().next();
|
|
22
|
+
if (oldest.done !== true)
|
|
23
|
+
this.parsed.delete(oldest.value);
|
|
24
|
+
}
|
|
25
|
+
message = new Markdown(text, 0, 0, this.theme);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
// Reinserting keeps a message that is still on screen from ageing out.
|
|
29
|
+
this.parsed.delete(text);
|
|
30
|
+
}
|
|
31
|
+
this.parsed.set(text, message);
|
|
32
|
+
return message.render(Math.max(1, Math.floor(width)));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=markdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/ui/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAsB,MAAM,wBAAwB,CAAA;AAErE,kFAAkF;AAClF,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAA;AAEtC;;;;;;GAMG;AACH,MAAM,OAAO,gBAAgB;IAGE;IAFZ,MAAM,GAAG,IAAI,GAAG,EAAoB,CAAA;IAErD,YAA6B,KAAoB;QAApB,UAAK,GAAL,KAAK,CAAe;IAAG,CAAC;IAErD,MAAM,CAAC,IAAY,EAAE,KAAa;QAChC,IAAI,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACnC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,oBAAoB,EAAE,CAAC;gBAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAA;gBACxC,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI;oBAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YAC5D,CAAC;YACD,OAAO,GAAG,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;QAChD,CAAC;aAAM,CAAC;YACN,uEAAuE;YACvE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAC1B,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAC9B,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IACvD,CAAC;CACF"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { StoredSession } from '../agent/history.ts';
|
|
2
|
+
import { type PresetSummary } from '../agent/presets.ts';
|
|
3
|
+
/** One selectable row of the picker. */
|
|
4
|
+
export interface PickerRow {
|
|
5
|
+
readonly label: string;
|
|
6
|
+
readonly description: string | undefined;
|
|
7
|
+
readonly current: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** How the picker presents itself, independent of how it is drawn. */
|
|
10
|
+
export interface PickerCard {
|
|
11
|
+
readonly title: string;
|
|
12
|
+
/** Why the last pick was refused, for as long as it stays the last thing asked. */
|
|
13
|
+
readonly note: string | undefined;
|
|
14
|
+
readonly rows: readonly PickerRow[];
|
|
15
|
+
readonly filter: string;
|
|
16
|
+
readonly hint: string;
|
|
17
|
+
/** Rows above the window, so the reader knows the list continues. */
|
|
18
|
+
readonly above: number;
|
|
19
|
+
/** Rows below the window. */
|
|
20
|
+
readonly below: number;
|
|
21
|
+
}
|
|
22
|
+
/** What one key press asked the picker to do. */
|
|
23
|
+
export type PickerAction = {
|
|
24
|
+
readonly kind: 'pick';
|
|
25
|
+
readonly id: string;
|
|
26
|
+
} | {
|
|
27
|
+
readonly kind: 'cancel';
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Rows the picker shows at once.
|
|
31
|
+
*
|
|
32
|
+
* The card is drawn at the end of a viewport that follows the transcript, so a
|
|
33
|
+
* list taller than the screen would push its own cursor out of sight. The
|
|
34
|
+
* window stays around the cursor instead, and the counts say what is hidden.
|
|
35
|
+
*/
|
|
36
|
+
export declare const PICKER_WINDOW = 12;
|
|
37
|
+
/** The hint lines a list uses when it is empty and when it holds rows. */
|
|
38
|
+
export interface PickerHints {
|
|
39
|
+
readonly empty: string;
|
|
40
|
+
readonly listed: string;
|
|
41
|
+
}
|
|
42
|
+
/** Human age of a session, so a list of ids becomes a list of moments. */
|
|
43
|
+
export declare function describeAge(createdAt: number, now: number): string;
|
|
44
|
+
/**
|
|
45
|
+
* A filtered list the reader moves through by key.
|
|
46
|
+
*
|
|
47
|
+
* The list is the caller's and is re-read on every key and paint, so rows whose
|
|
48
|
+
* text arrives late — a session title read from its log, a roster re-read after
|
|
49
|
+
* a preset was authored — appear without rebuilding the picker. Windowing,
|
|
50
|
+
* filtering, and the keys are the same whatever the rows mean, which is why
|
|
51
|
+
* they live here once instead of in every list a terminal has to choose from.
|
|
52
|
+
*/
|
|
53
|
+
export declare class ListPicker<Row> {
|
|
54
|
+
private readonly source;
|
|
55
|
+
private readonly heading;
|
|
56
|
+
/** The value a pick settles on. */
|
|
57
|
+
private readonly idOf;
|
|
58
|
+
private readonly describe;
|
|
59
|
+
/** Extra text the filter matches besides the row's own label. */
|
|
60
|
+
private readonly haystackOf;
|
|
61
|
+
private readonly hints;
|
|
62
|
+
private cursor;
|
|
63
|
+
private filter;
|
|
64
|
+
private note;
|
|
65
|
+
constructor(source: () => readonly Row[], heading: () => string,
|
|
66
|
+
/** The value a pick settles on. */
|
|
67
|
+
idOf: (row: Row) => string, describe: (row: Row) => PickerRow,
|
|
68
|
+
/** Extra text the filter matches besides the row's own label. */
|
|
69
|
+
haystackOf: (row: Row) => string, hints: PickerHints);
|
|
70
|
+
/** Rows matching the typed filter, in the order they were listed. */
|
|
71
|
+
visible(): readonly Row[];
|
|
72
|
+
/**
|
|
73
|
+
* Say why the row under the cursor cannot be taken.
|
|
74
|
+
*
|
|
75
|
+
* Cleared by the next key press, because it explains one refusal rather than
|
|
76
|
+
* describing the list: a note that outlived it would read as a property of
|
|
77
|
+
* whatever the reader moved to.
|
|
78
|
+
*/
|
|
79
|
+
setNote(text: string | undefined): void;
|
|
80
|
+
/** Apply one key press; returns an action only when the picker settles. */
|
|
81
|
+
handleKey(data: string): PickerAction | undefined;
|
|
82
|
+
card(): PickerCard;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* One stored session chosen by key press.
|
|
86
|
+
*
|
|
87
|
+
* The picker owns only what it shows: ids and rows come from the caller, and
|
|
88
|
+
* the titles arrive after the list does, because reading every log before the
|
|
89
|
+
* first paint would make opening the picker as slow as the slowest session.
|
|
90
|
+
*/
|
|
91
|
+
export declare class SessionPicker extends ListPicker<StoredSession> {
|
|
92
|
+
constructor(sessions: readonly StoredSession[], titles: () => ReadonlyMap<string, string>, now?: () => number);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* One agent preset chosen by key press.
|
|
96
|
+
*
|
|
97
|
+
* The roster is re-read on every paint, so a preset authored or deleted while
|
|
98
|
+
* the picker is open is present or gone without reopening it.
|
|
99
|
+
*/
|
|
100
|
+
export declare class PresetPicker extends ListPicker<PresetSummary> {
|
|
101
|
+
constructor(presets: () => readonly PresetSummary[], currentId: () => string | undefined);
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=picker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"picker.d.ts","sourceRoot":"","sources":["../../src/ui/picker.ts"],"names":[],"mappings":"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"}
|
package/lib/ui/picker.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { matchesKey } from '@earendil-works/pi-tui';
|
|
2
|
+
import { describePreset } from "../agent/presets.js";
|
|
3
|
+
/**
|
|
4
|
+
* Rows the picker shows at once.
|
|
5
|
+
*
|
|
6
|
+
* The card is drawn at the end of a viewport that follows the transcript, so a
|
|
7
|
+
* list taller than the screen would push its own cursor out of sight. The
|
|
8
|
+
* window stays around the cursor instead, and the counts say what is hidden.
|
|
9
|
+
*/
|
|
10
|
+
export const PICKER_WINDOW = 12;
|
|
11
|
+
const MINUTE_MS = 60_000;
|
|
12
|
+
const HOUR_MS = 60 * MINUTE_MS;
|
|
13
|
+
const DAY_MS = 24 * HOUR_MS;
|
|
14
|
+
/** Human age of a session, so a list of ids becomes a list of moments. */
|
|
15
|
+
export function describeAge(createdAt, now) {
|
|
16
|
+
const elapsed = Math.max(0, now - createdAt);
|
|
17
|
+
if (elapsed < MINUTE_MS)
|
|
18
|
+
return 'just now';
|
|
19
|
+
if (elapsed < HOUR_MS)
|
|
20
|
+
return `${Math.floor(elapsed / MINUTE_MS)}m ago`;
|
|
21
|
+
if (elapsed < DAY_MS)
|
|
22
|
+
return `${Math.floor(elapsed / HOUR_MS)}h ago`;
|
|
23
|
+
return `${Math.floor(elapsed / DAY_MS)}d ago`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A filtered list the reader moves through by key.
|
|
27
|
+
*
|
|
28
|
+
* The list is the caller's and is re-read on every key and paint, so rows whose
|
|
29
|
+
* text arrives late — a session title read from its log, a roster re-read after
|
|
30
|
+
* a preset was authored — appear without rebuilding the picker. Windowing,
|
|
31
|
+
* filtering, and the keys are the same whatever the rows mean, which is why
|
|
32
|
+
* they live here once instead of in every list a terminal has to choose from.
|
|
33
|
+
*/
|
|
34
|
+
export class ListPicker {
|
|
35
|
+
source;
|
|
36
|
+
heading;
|
|
37
|
+
idOf;
|
|
38
|
+
describe;
|
|
39
|
+
haystackOf;
|
|
40
|
+
hints;
|
|
41
|
+
cursor = 0;
|
|
42
|
+
filter = '';
|
|
43
|
+
note;
|
|
44
|
+
constructor(source, heading,
|
|
45
|
+
/** The value a pick settles on. */
|
|
46
|
+
idOf, describe,
|
|
47
|
+
/** Extra text the filter matches besides the row's own label. */
|
|
48
|
+
haystackOf, hints) {
|
|
49
|
+
this.source = source;
|
|
50
|
+
this.heading = heading;
|
|
51
|
+
this.idOf = idOf;
|
|
52
|
+
this.describe = describe;
|
|
53
|
+
this.haystackOf = haystackOf;
|
|
54
|
+
this.hints = hints;
|
|
55
|
+
}
|
|
56
|
+
/** Rows matching the typed filter, in the order they were listed. */
|
|
57
|
+
visible() {
|
|
58
|
+
const needle = this.filter.trim().toLowerCase();
|
|
59
|
+
if (needle === '')
|
|
60
|
+
return this.source();
|
|
61
|
+
return this.source().filter(row => this.haystackOf(row).toLowerCase().includes(needle));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Say why the row under the cursor cannot be taken.
|
|
65
|
+
*
|
|
66
|
+
* Cleared by the next key press, because it explains one refusal rather than
|
|
67
|
+
* describing the list: a note that outlived it would read as a property of
|
|
68
|
+
* whatever the reader moved to.
|
|
69
|
+
*/
|
|
70
|
+
setNote(text) {
|
|
71
|
+
this.note = text;
|
|
72
|
+
}
|
|
73
|
+
/** Apply one key press; returns an action only when the picker settles. */
|
|
74
|
+
handleKey(data) {
|
|
75
|
+
this.note = undefined;
|
|
76
|
+
const rows = this.visible();
|
|
77
|
+
if (matchesKey(data, 'escape'))
|
|
78
|
+
return { kind: 'cancel' };
|
|
79
|
+
if (matchesKey(data, 'enter')) {
|
|
80
|
+
const chosen = rows[Math.min(this.cursor, Math.max(0, rows.length - 1))];
|
|
81
|
+
return chosen === undefined ? undefined : { kind: 'pick', id: this.idOf(chosen) };
|
|
82
|
+
}
|
|
83
|
+
if (matchesKey(data, 'up')) {
|
|
84
|
+
this.cursor = Math.max(0, this.cursor - 1);
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
if (matchesKey(data, 'down')) {
|
|
88
|
+
this.cursor = Math.min(Math.max(0, rows.length - 1), this.cursor + 1);
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
if (matchesKey(data, 'backspace')) {
|
|
92
|
+
this.filter = this.filter.slice(0, -1);
|
|
93
|
+
this.cursor = 0;
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
if (matchesKey(data, 'space')) {
|
|
97
|
+
this.filter += ' ';
|
|
98
|
+
this.cursor = 0;
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
if (data.length === 1 && data >= ' ') {
|
|
102
|
+
this.filter += data;
|
|
103
|
+
this.cursor = 0;
|
|
104
|
+
}
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
card() {
|
|
108
|
+
const rows = this.visible();
|
|
109
|
+
const cursor = Math.min(this.cursor, Math.max(0, rows.length - 1));
|
|
110
|
+
const start = Math.max(0, Math.min(cursor - Math.floor(PICKER_WINDOW / 2), rows.length - PICKER_WINDOW));
|
|
111
|
+
const window = rows.slice(start, start + PICKER_WINDOW);
|
|
112
|
+
return {
|
|
113
|
+
title: this.heading(),
|
|
114
|
+
note: this.note,
|
|
115
|
+
rows: window.map((row, index) => ({
|
|
116
|
+
...this.describe(row),
|
|
117
|
+
current: start + index === cursor,
|
|
118
|
+
})),
|
|
119
|
+
above: start,
|
|
120
|
+
below: Math.max(0, rows.length - start - window.length),
|
|
121
|
+
filter: this.filter,
|
|
122
|
+
hint: rows.length === 0 ? this.hints.empty : this.hints.listed,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* One stored session chosen by key press.
|
|
128
|
+
*
|
|
129
|
+
* The picker owns only what it shows: ids and rows come from the caller, and
|
|
130
|
+
* the titles arrive after the list does, because reading every log before the
|
|
131
|
+
* first paint would make opening the picker as slow as the slowest session.
|
|
132
|
+
*/
|
|
133
|
+
export class SessionPicker extends ListPicker {
|
|
134
|
+
constructor(sessions, titles, now = () => Date.now()) {
|
|
135
|
+
const labelOf = (session) => titles().get(session.id) ?? session.id;
|
|
136
|
+
super(() => sessions, () => `resume a session · ${sessions.length} stored`, session => session.id, session => ({
|
|
137
|
+
label: labelOf(session),
|
|
138
|
+
description: [
|
|
139
|
+
session.cwd ?? 'unknown directory',
|
|
140
|
+
describeAge(session.createdAt, now()),
|
|
141
|
+
...session.eventCount === undefined ? [] : [`${session.eventCount} events`],
|
|
142
|
+
].join(' · '),
|
|
143
|
+
// The card marks the row under the cursor, which the list decides.
|
|
144
|
+
current: false,
|
|
145
|
+
}), session => [labelOf(session), session.id, session.cwd ?? ''].join(' '), {
|
|
146
|
+
empty: 'nothing matches · backspace to widen · esc cancel',
|
|
147
|
+
listed: '↑↓ move · enter open · esc cancel · type to filter',
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* One agent preset chosen by key press.
|
|
153
|
+
*
|
|
154
|
+
* The roster is re-read on every paint, so a preset authored or deleted while
|
|
155
|
+
* the picker is open is present or gone without reopening it.
|
|
156
|
+
*/
|
|
157
|
+
export class PresetPicker extends ListPicker {
|
|
158
|
+
constructor(presets, currentId) {
|
|
159
|
+
super(presets, () => `agent preset · ${presets().length} available`, preset => preset.id, preset => describePreset(preset, currentId()), preset => [preset.id, preset.name ?? '', preset.description ?? ''].join(' '), {
|
|
160
|
+
empty: 'nothing matches · backspace to widen · esc cancel',
|
|
161
|
+
listed: '↑↓ move · enter switch · esc cancel · type to filter',
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=picker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"picker.js","sourceRoot":"","sources":["../../src/ui/picker.ts"],"names":[],"mappings":"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"}
|
package/lib/ui/rows.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendered transcript rows, kept per entry.
|
|
3
|
+
*
|
|
4
|
+
* A repaint walks the whole transcript, and wrapping text is the expensive part
|
|
5
|
+
* of doing that: without a cache, a session with a few thousand rows re-wraps
|
|
6
|
+
* all of them on every keystroke. Entries are stable objects, so their rows can
|
|
7
|
+
* be keyed by identity and only rebuilt when the width or the reader's
|
|
8
|
+
* expansion state changes.
|
|
9
|
+
*/
|
|
10
|
+
/** Entries kept before the oldest are re-rendered on demand. */
|
|
11
|
+
export declare const ROW_CACHE_LIMIT = 4000;
|
|
12
|
+
/** What the cache has done, so a test can assert the work was actually avoided. */
|
|
13
|
+
export interface RowCacheStats {
|
|
14
|
+
readonly hits: number;
|
|
15
|
+
readonly misses: number;
|
|
16
|
+
readonly size: number;
|
|
17
|
+
}
|
|
18
|
+
export declare class RowCache<K extends object = object> {
|
|
19
|
+
private readonly limit;
|
|
20
|
+
private readonly rows;
|
|
21
|
+
private hits;
|
|
22
|
+
private misses;
|
|
23
|
+
constructor(limit?: number);
|
|
24
|
+
/** The rows for a key and tag, or nothing when they have to be rebuilt. */
|
|
25
|
+
lookup(key: K, tag: string): readonly string[] | undefined;
|
|
26
|
+
store(key: K, tag: string, lines: readonly string[]): void;
|
|
27
|
+
clear(): void;
|
|
28
|
+
stats(): RowCacheStats;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=rows.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rows.d.ts","sourceRoot":"","sources":["../../src/ui/rows.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,gEAAgE;AAChE,eAAO,MAAM,eAAe,OAAQ,CAAA;AAOpC,mFAAmF;AACnF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,qBAAa,QAAQ,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IAKjC,OAAO,CAAC,QAAQ,CAAC,KAAK;IAJlC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA2B;IAChD,OAAO,CAAC,IAAI,CAAI;IAChB,OAAO,CAAC,MAAM,CAAI;gBAEW,KAAK,GAAE,MAAwB;IAE5D,2EAA2E;IAC3E,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS;IAa1D,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI;IAS1D,KAAK,IAAI,IAAI;IAIb,KAAK,IAAI,aAAa;CAGvB"}
|
package/lib/ui/rows.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendered transcript rows, kept per entry.
|
|
3
|
+
*
|
|
4
|
+
* A repaint walks the whole transcript, and wrapping text is the expensive part
|
|
5
|
+
* of doing that: without a cache, a session with a few thousand rows re-wraps
|
|
6
|
+
* all of them on every keystroke. Entries are stable objects, so their rows can
|
|
7
|
+
* be keyed by identity and only rebuilt when the width or the reader's
|
|
8
|
+
* expansion state changes.
|
|
9
|
+
*/
|
|
10
|
+
/** Entries kept before the oldest are re-rendered on demand. */
|
|
11
|
+
export const ROW_CACHE_LIMIT = 4_000;
|
|
12
|
+
export class RowCache {
|
|
13
|
+
limit;
|
|
14
|
+
rows = new Map();
|
|
15
|
+
hits = 0;
|
|
16
|
+
misses = 0;
|
|
17
|
+
constructor(limit = ROW_CACHE_LIMIT) {
|
|
18
|
+
this.limit = limit;
|
|
19
|
+
}
|
|
20
|
+
/** The rows for a key and tag, or nothing when they have to be rebuilt. */
|
|
21
|
+
lookup(key, tag) {
|
|
22
|
+
const cached = this.rows.get(key);
|
|
23
|
+
if (cached === undefined || cached.tag !== tag) {
|
|
24
|
+
this.misses += 1;
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
this.hits += 1;
|
|
28
|
+
// Reinserting keeps a row that is still on screen from ageing out.
|
|
29
|
+
this.rows.delete(key);
|
|
30
|
+
this.rows.set(key, cached);
|
|
31
|
+
return cached.lines;
|
|
32
|
+
}
|
|
33
|
+
store(key, tag, lines) {
|
|
34
|
+
this.rows.set(key, { tag, lines });
|
|
35
|
+
while (this.rows.size > this.limit) {
|
|
36
|
+
const oldest = this.rows.keys().next();
|
|
37
|
+
if (oldest.done === true)
|
|
38
|
+
return;
|
|
39
|
+
this.rows.delete(oldest.value);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
clear() {
|
|
43
|
+
this.rows.clear();
|
|
44
|
+
}
|
|
45
|
+
stats() {
|
|
46
|
+
return { hits: this.hits, misses: this.misses, size: this.rows.size };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=rows.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rows.js","sourceRoot":"","sources":["../../src/ui/rows.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,gEAAgE;AAChE,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,CAAA;AAcpC,MAAM,OAAO,QAAQ;IAKU;IAJZ,IAAI,GAAG,IAAI,GAAG,EAAiB,CAAA;IACxC,IAAI,GAAG,CAAC,CAAA;IACR,MAAM,GAAG,CAAC,CAAA;IAElB,YAA6B,QAAgB,eAAe;QAA/B,UAAK,GAAL,KAAK,CAA0B;IAAG,CAAC;IAEhE,2EAA2E;IAC3E,MAAM,CAAC,GAAM,EAAE,GAAW;QACxB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACjC,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;YAC/C,IAAI,CAAC,MAAM,IAAI,CAAC,CAAA;YAChB,OAAO,SAAS,CAAA;QAClB,CAAC;QACD,IAAI,CAAC,IAAI,IAAI,CAAC,CAAA;QACd,mEAAmE;QACnE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QACrB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;QAC1B,OAAO,MAAM,CAAC,KAAK,CAAA;IACrB,CAAC;IAED,KAAK,CAAC,GAAM,EAAE,GAAW,EAAE,KAAwB;QACjD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAA;QAClC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YACnC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAA;YACtC,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI;gBAAE,OAAM;YAChC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAChC,CAAC;IACH,CAAC;IAED,KAAK;QACH,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAA;IACnB,CAAC;IAED,KAAK;QACH,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;IACvE,CAAC;CACF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type Component } from '@earendil-works/pi-tui';
|
|
2
|
+
import type { TuiTheme } from '../theme.ts';
|
|
3
|
+
/** Everything the footer states, gathered by the surface around it. */
|
|
4
|
+
export interface StatusFacts {
|
|
5
|
+
readonly activity: 'idle' | 'working';
|
|
6
|
+
/** How long the running turn has been running, when one is. */
|
|
7
|
+
readonly elapsedMs: number | undefined;
|
|
8
|
+
readonly provider: string | undefined;
|
|
9
|
+
readonly model: string | undefined;
|
|
10
|
+
readonly effort: string | undefined;
|
|
11
|
+
/** The agent preset (mode) the session runs; fixed once it has a turn. */
|
|
12
|
+
readonly agentPreset: string | undefined;
|
|
13
|
+
/** The permission preset, which is a sandbox policy and a different thing. */
|
|
14
|
+
readonly preset: string | undefined;
|
|
15
|
+
readonly contextTokens: number | undefined;
|
|
16
|
+
readonly contextWindow: number | undefined;
|
|
17
|
+
/** Share of prompt tokens served from the provider's cache, 0–1. */
|
|
18
|
+
readonly cacheRate: number | undefined;
|
|
19
|
+
/** Prompt tokens the provider did not have cached, when it reported any. */
|
|
20
|
+
readonly uncachedInputTokens: number | undefined;
|
|
21
|
+
/** Tokens this session has generated, when the provider reported any. */
|
|
22
|
+
readonly outputTokens: number | undefined;
|
|
23
|
+
readonly cwd: string;
|
|
24
|
+
readonly home: string | undefined;
|
|
25
|
+
}
|
|
26
|
+
/** Compact a token count, because the exact number changes nothing a reader decides. */
|
|
27
|
+
export declare function formatTokens(count: number): string;
|
|
28
|
+
/**
|
|
29
|
+
* Shorten an absolute path for the footer: the reader's own home becomes `~`,
|
|
30
|
+
* and only a path from outside it is cut down to its tail.
|
|
31
|
+
*
|
|
32
|
+
* A home path keeps every directory between `~` and the leaf. Dropping those
|
|
33
|
+
* away would name a location that does not exist, which is worse than a long one.
|
|
34
|
+
*/
|
|
35
|
+
export declare function shortPath(path: string, home: string | undefined): string;
|
|
36
|
+
/**
|
|
37
|
+
* One line of state, ordered by what a reader asks first: is it working, what is
|
|
38
|
+
* it, how full is the context, and where am I.
|
|
39
|
+
*/
|
|
40
|
+
export declare function formatStatus(facts: StatusFacts, width: number, theme: TuiTheme): string;
|
|
41
|
+
/** A one-row view of the state around the transcript. */
|
|
42
|
+
export declare class StatusBar implements Component {
|
|
43
|
+
private readonly facts;
|
|
44
|
+
private readonly theme;
|
|
45
|
+
constructor(facts: () => StatusFacts, theme: TuiTheme);
|
|
46
|
+
invalidate(): void;
|
|
47
|
+
render(width: number): string[];
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/ui/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAmB,MAAM,wBAAwB,CAAA;AAExE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAE3C,uEAAuE;AACvE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;IACrC,+DAA+D;IAC/D,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IACtC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,0EAA0E;IAC1E,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;IACxC,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1C,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1C,oEAAoE;IACpE,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IACtC,4EAA4E;IAC5E,QAAQ,CAAC,mBAAmB,EAAE,MAAM,GAAG,SAAS,CAAA;IAChD,yEAAyE;IACzE,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;IACzC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;CAClC;AAUD,wFAAwF;AACxF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlD;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAQxE;AAOD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,MAAM,CAuBvF;AAED,yDAAyD;AACzD,qBAAa,SAAU,YAAW,SAAS;IAEvC,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBADL,KAAK,EAAE,MAAM,WAAW,EACxB,KAAK,EAAE,QAAQ;IAGlC,UAAU,IAAI,IAAI;IAIlB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;CAGhC"}
|