@aiwayds/dsh-tui-pi 0.3.0 → 0.4.2

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.
@@ -1,57 +1,80 @@
1
1
  /**
2
- * Live widgets: the Todos boxed panel pinned ABOVE the chat input, and the
3
- * running-subagent activity merged into the last-request area BELOW the
4
- * editor. Two separate plain containers keep the two live surfaces apart:
2
+ * Live widgets — every fixed live surface around the chat window:
5
3
  *
6
- * - `todosDoc` — the dock slot above the input (`ui.widgets`): a single
7
- * bordered Todos panel (top border + header row + tree body rows + bottom
8
- * border, same chrome as the thinking/tool panels). Auto height — it
9
- * renders zero rows while empty and grows to its content while the model
10
- * has todos.
11
- * - `activityDoc` — the lastRequest container below the editor
12
- * (`ui.lastRequest`): the ` ● <last request>` line (persisting across
13
- * agent churn) followed by one compact line PER RUNNING agent. NO box
14
- * chrome, NO `● Agents` header, NO provider — just
15
- * `├─ ⠋ <name> · ↻N≤M · 21k/1m · 13.6s · <latest output>`, the `├─ ` /
16
- * `└─ ` prefix in the same column as the todo rows and the request ` ● `
17
- * (the last running agent closes the list with `└─ `). Auto height — it
18
- * collapses to zero rows when both the last-request line is cleared and no
19
- * agent runs.
4
+ * - `todosDoc` (the widgets slot ABOVE the chat input) hosts THREE pinned
5
+ * panels: the bordered Todos table, the ThinkPanel and the ToolPanel
6
+ * (src/activity.ts). Think/tool activity NEVER creates transcript blocks
7
+ * — one panel of each kind exists for the whole run, every event
8
+ * refreshes it in place, and a panel with no content renders zero rows.
9
+ * - `activityDoc` (the lastRequest container BELOW the editor): the
10
+ * ` ● <last request>` line (persisting across agent churn) followed by one
11
+ * compact line PER RUNNING agent — `├─ ⠋ <name> · ↻N≤M · 21k/1m · 13.6s ·
12
+ * <content tail>`. The tail is the child's latest CONTENT line (assistant
13
+ * text/reasoning, live-refreshed — never a tool name) and takes whatever
14
+ * the row has left, truncated at the right edge: one row, no wrap.
20
15
  *
21
- * Show-when-content, clear-when-done: the Todos panel appears only while it
22
- * has content and disappears when it empties (an all-completed snapshot or
23
- * `/new`); an agent line renders only while its child RUNS (a settled child
24
- * drops off immediately). `clear()` (/new) drops the Todos panel and the
25
- * agent lines but preserves the last-request echo.
16
+ * Event flow: index.ts routes every parent-session event through
17
+ * `applyEvent` (think/tool phase machine) and todo/write snapshots through
18
+ * `renderTodos`; the bridge's onLive fold feeds `renderAgents`. Phase rules
19
+ * for the panels: a reasoning delta shows the think panel; a tool call
20
+ * refreshes the tool panel (pending); a matching result settles it (status
21
+ * icon, frozen time, result tail); a text delta / assembled message / user
22
+ * message / turn end hides the finished phases. `clear()` (/new, resume)
23
+ * hides both panels.
26
24
  *
27
- * Live refresh: `tickLive` (the AGENT_TICK_MS timer in index.ts) advances the
28
- * spinner and re-reads the elapsed clock; it is a no-op while nothing runs.
29
- * `setTheme` recolors in place on a theme hot-switch (no replay buffer — the
30
- * widget is live state, not transcript history).
25
+ * Live refresh: `tickLive` (the AGENT_TICK_MS timer in index.ts) advances
26
+ * the spinner and repaints while any agent runs OR either panel is visible
27
+ * (the elapsed columns re-read the clock each frame); no-op otherwise.
28
+ * `setTheme` recolors in place on a theme hot-switch (the panels re-render
29
+ * each frame through their theme getters — no replay buffer, they are live
30
+ * state, not transcript history).
31
31
  */
32
- import { Container } from '@earendil-works/pi-tui';
32
+ import { Container, type Component } from '@earendil-works/pi-tui';
33
+ import type { SessionEvent } from '@deepseek-ai/dsh-session';
34
+ import type { PanelHeight } from './activity.ts';
33
35
  import type { AgentView } from './dsh-events.ts';
34
36
  import { type TuiTheme } from './theme/index.ts';
35
- /** Refresh interval of the live running-agent lines (spinner + elapsed). */
37
+ /** Refresh interval of the live surfaces (spinner + elapsed columns). */
36
38
  export declare const AGENT_TICK_MS = 100;
37
39
  /** Braille spinner cycle; the frame index advances once per tick. */
38
40
  export declare const AGENT_SPINNER_FRAMES: readonly ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
39
41
  /**
40
- * Compact human-readable size: 1_500_000 → `1.5m`, 20_965 → `20k`, 999 → `999`.
42
+ * Compact human-readable size: 1_500_000 -> `1.5m`, 20_965 -> `20k`, 999 -> `999`.
41
43
  * Millions keep one decimal (a trailing `.0` dropped), thousands floor to `k`.
42
44
  */
43
45
  export declare function fmtCompact(n: number): string;
46
+ /**
47
+ * The live Todos panel - a self-drawing table on the panel framework
48
+ * (padCell/columnWidths from panels.ts), rendered at the CURRENT width on
49
+ * every frame. pi-tui's Container calls `render(width)` per frame with no
50
+ * caching, so a terminal resize re-lays the table out (clip + column widths)
51
+ * automatically - no stale baked rows, no word-wrap. Renders zero rows while
52
+ * the list is empty or every todo is completed (clear-when-done).
53
+ */
54
+ export declare class TodosPanel implements Component {
55
+ private todos;
56
+ private readonly getTheme;
57
+ constructor(getTheme: () => TuiTheme);
58
+ invalidate(): void;
59
+ /** Replace the todos snapshot (`todo/write` events, replay, /new). */
60
+ setTodos(todos: readonly {
61
+ content: string;
62
+ status: string;
63
+ }[]): void;
64
+ render(width: number): string[];
65
+ }
44
66
  export declare class LiveWidgets {
45
67
  private readonly todosDoc;
46
68
  private readonly activityDoc;
47
69
  private theme;
48
70
  private readonly requestRender;
49
- /** Latest todo list from `todo/write`, rendered while non-empty. */
50
- private liveTodos;
71
+ /** The self-drawing Todos table, mounted once in `todosDoc`. */
72
+ private readonly todosPanel;
73
+ /** The fixed think/tool status panels, mounted once in `todosDoc`. */
74
+ private readonly thinkPanel;
75
+ private readonly toolPanel;
51
76
  /** Latest subagent views from the bridge's onLive fold. */
52
77
  private liveAgents;
53
- /** The Todos panel Text in `todosDoc`, replaced on rebuild. */
54
- private todosText;
55
78
  /** The merged running-agent Text in `activityDoc`, replaced on rebuild. */
56
79
  private agentsText;
57
80
  /** The ` ● <last request>` Text in `activityDoc` (persists across churn). */
@@ -61,12 +84,14 @@ export declare class LiveWidgets {
61
84
  /** Spinner frame counter, advanced by tickLive while any agent runs. */
62
85
  private spinnerFrame;
63
86
  /**
64
- * @param todosDoc The dock slot ABOVE the chat input — the Todos boxed
65
- * panel lives here.
66
- * @param activityDoc The lastRequest container BELOW the editor — the ` ● `
87
+ * @param todosDoc The dock slot ABOVE the chat input - the Todos table and
88
+ * the think/tool status panels live here.
89
+ * @param activityDoc The lastRequest container BELOW the editor - the ` ● `
67
90
  * last-request line plus the compact running-agent lines live here.
91
+ * @param panelHeight Configured think/tool panel height ('1' one row, or a
92
+ * boxed budget - see activity.ts).
68
93
  */
69
- constructor(todosDoc: Container, activityDoc: Container, theme: TuiTheme, requestRender: () => void);
94
+ constructor(todosDoc: Container, activityDoc: Container, theme: TuiTheme, requestRender: () => void, panelHeight?: PanelHeight);
70
95
  /**
71
96
  * Render the ` ● <text>` last-request line in `activityDoc`, UNDER which the
72
97
  * running-agent lines appear. `undefined` (or blank) removes the line. Same
@@ -80,59 +105,65 @@ export declare class LiveWidgets {
80
105
  content: string;
81
106
  status: string;
82
107
  }[]): void;
108
+ /**
109
+ * One parent-session session event, driving the think/tool phase machine:
110
+ * a reasoning delta opens/feeds the think panel; a tool call refreshes the
111
+ * tool panel (pending, replacing any tracked tool) — except delegation
112
+ * spawn tools (`use_agent`/`subagent`/`workflow`/`ralph`), whose children
113
+ * already render in the running-agent lines below the editor and never
114
+ * open a tool block; a matching result settles the tracked tool; a text
115
+ * delta, an assembled assistant message, a user message or a turn end
116
+ * hides the finished phases. Called for every event AND for replayed
117
+ * history (a resumed session replays its tool calls; its final turn/end
118
+ * leaves the panels hidden).
119
+ */
120
+ applyEvent(event: SessionEvent): void;
83
121
  /** Replace the subagent views (the bridge's onLive fold). */
84
122
  renderAgents(agents: readonly AgentView[]): void;
85
123
  /**
86
- * Live refresh ~10x/sec while any subagent runs: advance the spinner and
87
- * repaint (the elapsed column re-reads Date.now()). No-op when nothing runs
88
- * — the activity area then holds its final (empty) state.
124
+ * Live refresh ~10x/sec while any subagent runs or either status panel is
125
+ * visible: advance the spinner and repaint (the elapsed columns re-read
126
+ * Date.now() each frame). No-op otherwise — the activity area then holds
127
+ * its final (empty) state.
89
128
  */
90
129
  tickLive(): void;
91
- /** Recolor the widget (Todos panel, ● line and agent lines) under a theme
92
- * hot-switch (no-op on the same bundle). */
130
+ /**
131
+ * Switch the configured think/tool panel height (the settings watch sink).
132
+ * The panels re-render at the new budget on the next frame — no rebuild
133
+ * wiring, they are self-drawing.
134
+ */
135
+ setPanelHeight(panelHeight: PanelHeight): void;
136
+ /** Recolor the widget (Todos panel, think/tool panels, ● line and agent
137
+ * lines) under a theme hot-switch (no-op on the same bundle). The panels
138
+ * read the theme through their getters, so the swap needs only a repaint. */
93
139
  setTheme(theme: TuiTheme): void;
94
140
  /**
95
141
  * Drop everything except the last-request echo (`/new`): clears the Todos
96
- * panel and the running-agent lines, keeps the ` ● ` line. The bridge also
97
- * fires `renderAgents([])`.
142
+ * panel, hides the think/tool panels and drops the running-agent lines,
143
+ * keeps the ` ● ` line. The bridge also fires `renderAgents([])`.
98
144
  */
99
145
  clear(): void;
100
146
  /**
101
- * Rebuild the two live surfaces:
102
- * - `todosDoc`: one bordered Todos panel (or nothing when all completed).
103
- * - `activityDoc`: the ` ● ` line (managed separately, persists) followed
104
- * by ONE Text holding the compact running-agent lines joined by '\n'
105
- * (or nothing when no agent runs — the slot collapses to the ● line).
106
- * Clear-when-done: the Todos panel hides once every todo is completed (the
107
- * model writes the whole-list snapshot and rarely clears it — an
108
- * all-completed list is the end-of-work signal); the agent lines hide once
109
- * no child is running.
147
+ * Rebuild the running-agent surface of `activityDoc`: the ` ● ` line
148
+ * (managed separately, persists) followed by ONE Text holding the compact
149
+ * running-agent lines joined by '\n' (or nothing when no agent runs - the
150
+ * slot collapses to the ● line). The pinned panels are NOT rebuilt here -
151
+ * they are self-drawing components that re-render each frame.
152
+ * Clear-when-done: the agent lines hide once no child is running.
110
153
  */
111
154
  private rebuild;
112
- /** One bordered panel: top border + header row + body rows + bottom border. */
113
- private boxedPanel;
114
- /** `● Todos (done/total)`, styled for the panel header row. */
115
- private todosHeader;
116
- /**
117
- * Tree-style todo body lines: `├─`/`└─` connectors with `☐`/`◐`/`☑` status
118
- * icons. Content is model-controlled: clipped BEFORE styling to the boxed
119
- * row's inner budget minus the tree chrome (connector 3 cols + icon+space
120
- * 2 cols) — see clipPanelLine's contract.
121
- */
122
- private todoLines;
123
155
  /**
124
156
  * One compact running-agent line for the last-request area, todo-style:
125
157
  * `├─ `/`└─ ` connector (same column as the todo rows and the request
126
- * ` ● `) + spinner + the agent NAME (`view.label`, matching the boxed
127
- * panel's main line) + the exact meta that main line showed (`↻retries≤max`,
128
- * compact `tokens[/contextWindow]`, elapsed) + the child's latest output
129
- * line when one exists (` · <tail>`, so the user sees it is alive). NO box
130
- * chrome, NO provider — the name is the prominent element; the last running
131
- * agent closes the list with `└─ `. The label is the only unbounded field;
132
- * it is clipped against a budget measured from the terminal width so the
133
- * whole plain line (prefix included) fits, then the assembled plain line is
134
- * clipped to the terminal width BEFORE any ANSI is applied (clipToWidth
135
- * counts SGR fragments as visible columns — style last).
158
+ * ` ● `) + spinner + the agent NAME (`view.label`) + the exact meta the
159
+ * boxed board showed (`↻retries≤max`, compact `tokens[/contextWindow]`,
160
+ * elapsed) + the child's latest CONTENT line — live-refreshed assistant
161
+ * text/reasoning, NEVER a tool name — as the ` · <tail>` suffix. NO box
162
+ * chrome, NO provider. Layout against the terminal width: the name caps at
163
+ * 40% of the space the meta leaves; the tail takes EVERYTHING else and is
164
+ * truncated at the right edge (single row, never wrapped). The assembled
165
+ * plain line is clipped to the terminal width BEFORE any ANSI is applied
166
+ * (clipToWidth counts SGR fragments as visible columns — style last).
136
167
  */
137
168
  private compactAgentLine;
138
169
  }