@vincemakes/kiso-tui 0.1.36 → 0.1.38

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.
@@ -15,7 +15,7 @@
15
15
  * (untouched); render.ts supplies the original text (palette, escape,
16
16
  * tint, fold wording).
17
17
  */
18
- import { foldThinking, foldResult, renderToolSummary } from "./render.js";
18
+ import { foldThinking, foldResult, renderToolSummary, type ResumeMeta } from "./render.js";
19
19
  /** The spinner glyphs, cycled by the compositor's on-demand tick. */
20
20
  export declare const SPINNER: string[];
21
21
  /** The frame context the compositor passes down — the pieces of time
@@ -24,6 +24,10 @@ export declare const SPINNER: string[];
24
24
  export interface FrameCtx {
25
25
  readonly spinnerI: number;
26
26
  readonly now: number;
27
+ /** The terminal height (rows) — the banner cell's tier input (W1:
28
+ * the tier table reads H, so a resize RE-TIERS instead of
29
+ * re-folding frozen rows). */
30
+ readonly height: number;
27
31
  }
28
32
  /** ONE screen line a component emits (raw, SGR included). */
29
33
  export type RenderLine = string;
@@ -43,7 +47,19 @@ export declare function visibleWidth(line: string): number;
43
47
  export interface Component {
44
48
  render(width: number, ctx: FrameCtx): string[];
45
49
  }
46
- /** The containervertical concatenation of its children. */
50
+ /** The W11 spacing formula "a row gets one blank line above it when
51
+ * the row is itself a block, or when the previous sibling was taller
52
+ * than one row". One-row siblings pack tight; anything multi-row
53
+ * breathes on both sides. The FIRST cell never gets the blank (it sits
54
+ * at the body's top — the banner would otherwise start one row down).
55
+ * `prev` is the previous sibling's OWN rows (raw — a cell's own blank
56
+ * must never count toward its height). The blank is a JOIN artifact:
57
+ * the cell's own render stays blank-free, so per-cell accounting
58
+ * (heights, the fold cache) never sees a fake row. */
59
+ export declare function bodySpacing(prev: readonly string[] | null, rows: readonly string[]): string[];
60
+ /** The container — vertical concatenation with the W11 formula. No
61
+ * component decides its own spacing: every blank in the body is the
62
+ * container's. */
47
63
  export declare class Container implements Component {
48
64
  private readonly children;
49
65
  constructor(children: Component[]);
@@ -53,14 +69,21 @@ export type BodyCell = {
53
69
  kind: "user";
54
70
  text: string;
55
71
  done: true;
72
+ turn: number;
56
73
  } | {
57
74
  kind: "thinking";
58
75
  text: string;
59
76
  done: boolean;
77
+ turn: number;
60
78
  } | {
61
79
  kind: "tool";
62
80
  name: string;
63
81
  input: string;
82
+ /** W15: the FULL input JSON (pretty-printed) — the display
83
+ * summary above is sliced at 60 chars; the expanded block's
84
+ * "--- input ---" section mirrors /last and needs it all. */
85
+ inputFull: string;
86
+ childRoles: string[];
64
87
  state: "pending" | "approval" | "running" | "done";
65
88
  isError: boolean;
66
89
  resultText: string;
@@ -70,6 +93,32 @@ export type BodyCell = {
70
93
  startedAt: number | null;
71
94
  doneAt: number | null;
72
95
  done: boolean;
96
+ /** W15: the live-region expand toggle — while the cell is live
97
+ * the FULL body renders in place (the compositor owns those
98
+ * rows and redraws them); a committed cell can never toggle
99
+ * (history is never rewritten — ADR-0046). */
100
+ expanded: boolean;
101
+ /** W14: the turn boundary — the index of the turn record that
102
+ * created this cell (the fold-hold's owner; −1 when no turn
103
+ * exists yet — the pre-turn cells never hold). */
104
+ turn: number;
105
+ /** W13: the rolled-up group summary — set at COMMIT time when
106
+ * the head of an N > 2 same-tool run renders the group (the
107
+ * work order's claimed shape: "✓ read 5 files (2.4k lines,
108
+ * 1.1s)" + the target children). The members carry null — the
109
+ * compositor's rolled-heads bookkeeping renders them []. */
110
+ rolled: null | {
111
+ count: number;
112
+ lines: number;
113
+ elapsed: string;
114
+ targets: string[];
115
+ };
116
+ /** W19: a DENIED call's reason (the CLI extracted it from the
117
+ * result's "[Permission denied] " prefix, keyed on the "denied"
118
+ * tag). Non-null renders the pinned row — the full call name,
119
+ * the target, the reason in the W4 parentheses idiom, NO timing
120
+ * metadata (the call never ran). */
121
+ reason: string | null;
73
122
  } | {
74
123
  kind: "text";
75
124
  text: string;
@@ -78,6 +127,12 @@ export type BodyCell = {
78
127
  kind: "notice";
79
128
  text: string;
80
129
  done: true;
130
+ } | {
131
+ kind: "banner";
132
+ version: string;
133
+ extensionsText: string;
134
+ resume: ResumeMeta[];
135
+ done: true;
81
136
  } | {
82
137
  kind: "raw";
83
138
  lines: string[];
@@ -89,26 +144,77 @@ export type BodyCell = {
89
144
  done: true;
90
145
  } | {
91
146
  kind: "checklist";
147
+ /** the model-authored header tail (parseChecklist's count line —
148
+ * chat.ts). The compositor's fixed "todo" prefix rides BEFORE it
149
+ * (W20 naming ruling: never model-controlled). */
92
150
  header: string;
93
151
  items: {
94
152
  text: string;
95
153
  status: "pending" | "active" | "done";
96
154
  }[];
97
- done: true;
155
+ /** W20: false while LIVE — the current turn's ONE in-place block
156
+ * (the commit loop only takes done cells, so it stays in the
157
+ * live region); true once SETTLED — endTurn committed it as the
158
+ * turn's one recap block. */
159
+ done: boolean;
160
+ /** W20: the LIVE block's ctrl+r toggle (W15) — the capped form
161
+ * flips to the full list in place. The settled render ignores
162
+ * it (already full). */
163
+ expanded: boolean;
164
+ /** W20: the wall clock of the block's FIRST call — the settled
165
+ * header's duration is clocked from here, compositor-side (the
166
+ * CLI stays unchanged). */
167
+ startedAt: number;
168
+ /** W20: the run's duration at the settle — the `2h 14m` form. */
169
+ durationSeconds: number;
170
+ turn: number;
98
171
  };
99
172
  declare const TOOL_SUMMARY_MAX = 60;
100
173
  /** The component for one cell — the mapping table lives here so the
101
174
  * compositor stays a pure writer. */
102
175
  export declare function cellComponent(cell: BodyCell): Component;
176
+ /** W13 — the rollup opt-in table: which tools collapse, and the count
177
+ * NOUN (read_file calls → "5 files", list_dir → "5 dirs", search_text
178
+ * → "5 matches"). Only these tools opt in — a shell burst is never
179
+ * rolled up (its rows carry meaning). The folded-turn line (W14) reuses
180
+ * the plurals for its other-tool terms ("2 dirs", "1 match"). */
181
+ export declare const ROLLUP_NOUN: Readonly<Record<string, string>>;
182
+ /** W14 — the folded-turn line: a whole QUIET turn (no text), once it is
183
+ * scrollback, becomes ONE line — the work order's claimed shape
184
+ * (`▞ thought 19s · 5 reads · no edits`), the counts accumulated at
185
+ * toolStart: read_file → "reads", edit_file → "edits", the other tools
186
+ * as first-call-order terms (the ROLLUP_NOUN plurals when the tool opts
187
+ * in, the verb + "s" otherwise). */
188
+ export declare function turnFold(t: {
189
+ thoughtSeconds: number;
190
+ reads: number;
191
+ edits: number;
192
+ others: [string, number][];
193
+ }): string[];
194
+ /** W20 — the todo block's fixed-window height: the whole live block
195
+ * (header + rows) in POST-FOLD screen rows at EVERY width: the header,
196
+ * the active row, up to 2 pending, the overflow-pending fold, the
197
+ * done-collapse. Every live row CUTS at W (never folds) — the block's
198
+ * height is its row count. */
199
+ export declare const CAP_TODO_LIVE = 6;
200
+ /** W20 — the settled block's duration, the `2h 14m` form (the todo
201
+ * narrative's long-horizon idiom): minutes+seconds under an hour,
202
+ * hours+minutes past it. */
203
+ export declare function formatDuration(totalSeconds: number): string;
103
204
  /** The status container's row: the status text (+ the tail) with the
104
205
  * right-aligned "/ commands · ↑ history" hint in the idle state —
105
206
  * the hint CUT FIRST when the width is short (the #16g rule); when
106
207
  * the STATUS ITSELF cannot fit, it cuts with a "…" — the last resort,
107
208
  * enforced by invariant ① (the old code let the status soft-wrap). */
108
- export declare function statusLine(status: string, tail: string, question: boolean, W: number): string;
109
- /** The footer — the ONE dotted row (the old two-row chrome is gone;
110
- * the wall cannot return by construction). */
111
- export declare function footerLine(W: number): string;
209
+ export declare function statusLine(status: string, tail: string, question: boolean, W: number, hint?: string): string;
210
+ /** W6 — the box: the chrome's top rail. The two dotted rows become
211
+ * a rounded box (the box already says "input lives here"); the rails
212
+ * stay dim, the width is still the full W (the box is a rail with
213
+ * corners — the menu/gap rows above and the status below are
214
+ * untouched). */
215
+ export declare function boxTop(W: number): string;
216
+ /** W6 — the box: the chrome's bottom rail. */
217
+ export declare function boxBottom(W: number): string;
112
218
  /** The terminal label + rhythm gap (the pipe path's v2c bytes — the
113
219
  * exact render the passthrough needs). */
114
220
  export declare function terminalPipe(label: string, statusLineText: string): string;