@vincemakes/kiso-tui-cells 0.38.0 → 0.39.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/dist/components.d.ts +11 -1
- package/dist/components.js +38 -8
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/md.js +36 -26
- package/dist/render.js +16 -4
- package/package.json +1 -1
package/dist/components.d.ts
CHANGED
|
@@ -306,8 +306,18 @@ export declare function diffBody(diff: import("./diff.js").DiffLine[] | null, W:
|
|
|
306
306
|
export declare const CAP_TASK_LIVE = 6;
|
|
307
307
|
/** W20 — the settled block's duration, the `2h 14m` form (the task
|
|
308
308
|
* narrative's long-horizon idiom): minutes+seconds under an hour,
|
|
309
|
-
* hours+minutes past it. */
|
|
309
|
+
* hours+minutes past it. Unchanged. */
|
|
310
310
|
export declare function formatDuration(totalSeconds: number): string;
|
|
311
|
+
/** The LIVE elapsed label — every place a duration is shown while it is
|
|
312
|
+
* still running, and on the card that settles from it, so a card and the
|
|
313
|
+
* status row can never disagree.
|
|
314
|
+
*
|
|
315
|
+
* It replaced the hand-written second counts, which is how the status
|
|
316
|
+
* row came to read "working 637s": ten minutes as a four-figure number,
|
|
317
|
+
* with no branch anywhere that said otherwise. Past an hour it keeps
|
|
318
|
+
* seconds, because a clock the user is watching tick should not stop
|
|
319
|
+
* ticking. */
|
|
320
|
+
export declare function elapsedLabel(totalSeconds: number): string;
|
|
311
321
|
/** The status container's row: the status text (+ the tail) with the
|
|
312
322
|
* right-aligned "/ commands · ↑ history" hint in the idle state —
|
|
313
323
|
* the hint CUT FIRST when the width is short (the #16g rule); when
|
package/dist/components.js
CHANGED
|
@@ -823,7 +823,7 @@ class ToolExecution {
|
|
|
823
823
|
// rewrites it in place: `3s · esc stops` → `exit 0 · 90 lines ·
|
|
824
824
|
// 3.2s`.
|
|
825
825
|
const gestures = c.name === "shell" ? " · esc stops · alt+⏎ redirects" : "";
|
|
826
|
-
const status = pickTier([`${elapsed}
|
|
826
|
+
const status = pickTier([`${elapsedLabel(elapsed)}${gestures}`, elapsedLabel(elapsed)], Math.max(1, W - visibleWidth(noteIndent())));
|
|
827
827
|
const live = toolBlockBody(c, W, ctx);
|
|
828
828
|
if (live.length > 0)
|
|
829
829
|
return slabBlock(gutterCut(gutter, `${verbCol} ${liveTarget(c)}`, W)[0], live, status, W);
|
|
@@ -846,7 +846,7 @@ class ToolExecution {
|
|
|
846
846
|
// way so the COMMAND keeps something to say, and the elapsed —
|
|
847
847
|
// pin 4's core — never does.
|
|
848
848
|
const MIN_TARGET = 10; // the gutter, the verb column, a character of command
|
|
849
|
-
const oneRow = pickTier([`${elapsed}
|
|
849
|
+
const oneRow = pickTier([`${elapsedLabel(elapsed)}${gestures}`, elapsedLabel(elapsed)], Math.max(1, W - MIN_TARGET - 3));
|
|
850
850
|
const room = Math.max(4, W - visibleWidth(oneRow) - 3);
|
|
851
851
|
const only = gutterCut(gutter, `${verbCol} ${liveTarget(c)}`, room)[0];
|
|
852
852
|
return slabBlock(`${only}${p.dim} · ${oneRow}${p.reset}`, [], null, W);
|
|
@@ -854,7 +854,10 @@ class ToolExecution {
|
|
|
854
854
|
// W2: ◦ replaces → for QUEUED — · is the separator inside every
|
|
855
855
|
// metadata group; a queued marker that is also the separator
|
|
856
856
|
// glyph reads as noise
|
|
857
|
-
|
|
857
|
+
// The `◦` carried "not started yet" ALONE, and a marker is not a
|
|
858
|
+
// word: on a screen where running and queued rows sit together, a
|
|
859
|
+
// reader has to already know the glyph. The suffix says it.
|
|
860
|
+
return gutterCut(`${p.dim}◦${p.reset} `, `${verbCol} ${liveTarget(c)}${p.dim} · queued${p.reset}`, W);
|
|
858
861
|
}
|
|
859
862
|
}
|
|
860
863
|
// ---- TUI2-R1 (A): the self-naming expand affordance ----
|
|
@@ -1780,15 +1783,42 @@ class Banner {
|
|
|
1780
1783
|
* done-collapse. Every live row CUTS at W (never folds) — the block's
|
|
1781
1784
|
* height is its row count. */
|
|
1782
1785
|
export const CAP_TASK_LIVE = 6;
|
|
1783
|
-
/**
|
|
1784
|
-
*
|
|
1785
|
-
*
|
|
1786
|
-
|
|
1786
|
+
/** The two duration idioms, from ONE implementation.
|
|
1787
|
+
*
|
|
1788
|
+
* They agree in every branch but the hour: W20's settled task block says
|
|
1789
|
+
* `2h 14m` (a long-horizon narrative does not care about seconds), and a
|
|
1790
|
+
* LIVE elapsed label says `1h 2m 3s` (a running clock does). A near-copy
|
|
1791
|
+
* differing in one branch is the drift that a shared helper exists to
|
|
1792
|
+
* prevent, so the branch is a parameter.
|
|
1793
|
+
*
|
|
1794
|
+
* Negative is clamped: a clock skew is not a negative duration. */
|
|
1795
|
+
function duration(totalSeconds, hoursKeepSeconds) {
|
|
1787
1796
|
const s = Math.max(0, Math.round(totalSeconds));
|
|
1788
1797
|
if (s < 60)
|
|
1789
1798
|
return `${s}s`;
|
|
1790
1799
|
const m = Math.floor(s / 60);
|
|
1791
|
-
|
|
1800
|
+
if (m < 60)
|
|
1801
|
+
return `${m}m ${s % 60}s`;
|
|
1802
|
+
const h = Math.floor(m / 60);
|
|
1803
|
+
return hoursKeepSeconds ? `${h}h ${m % 60}m ${s % 60}s` : `${h}h ${m % 60}m`;
|
|
1804
|
+
}
|
|
1805
|
+
/** W20 — the settled block's duration, the `2h 14m` form (the task
|
|
1806
|
+
* narrative's long-horizon idiom): minutes+seconds under an hour,
|
|
1807
|
+
* hours+minutes past it. Unchanged. */
|
|
1808
|
+
export function formatDuration(totalSeconds) {
|
|
1809
|
+
return duration(totalSeconds, false);
|
|
1810
|
+
}
|
|
1811
|
+
/** The LIVE elapsed label — every place a duration is shown while it is
|
|
1812
|
+
* still running, and on the card that settles from it, so a card and the
|
|
1813
|
+
* status row can never disagree.
|
|
1814
|
+
*
|
|
1815
|
+
* It replaced the hand-written second counts, which is how the status
|
|
1816
|
+
* row came to read "working 637s": ten minutes as a four-figure number,
|
|
1817
|
+
* with no branch anywhere that said otherwise. Past an hour it keeps
|
|
1818
|
+
* seconds, because a clock the user is watching tick should not stop
|
|
1819
|
+
* ticking. */
|
|
1820
|
+
export function elapsedLabel(totalSeconds) {
|
|
1821
|
+
return duration(totalSeconds, true);
|
|
1792
1822
|
}
|
|
1793
1823
|
/**
|
|
1794
1824
|
* W20 — the task checklist as STATE: ONE live block that redraws in
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* package); the cli never imports it directly. Experimental — no
|
|
7
7
|
* API-stability promise yet.
|
|
8
8
|
*/
|
|
9
|
-
export { SPINNER, foldLine, foldWords, visibleWidth, bodySpacing, Container, cellComponent, foldTerms, CAP_TASK_LIVE, formatDuration, statusLine, boxTop, boxBottom, terminalPipe, type FrameCtx, type RenderLine, type Component, type BodyCell, } from "./components.js";
|
|
9
|
+
export { SPINNER, foldLine, foldWords, visibleWidth, bodySpacing, Container, cellComponent, foldTerms, CAP_TASK_LIVE, elapsedLabel, formatDuration, statusLine, boxTop, boxBottom, terminalPipe, type FrameCtx, type RenderLine, type Component, type BodyCell, } from "./components.js";
|
|
10
10
|
export { editFileDiff, truncateDiff, writeFileDiff, type DiffLine, type DiffResult } from "./diff.js";
|
|
11
11
|
export { pendingQueueRows } from "./components.js";
|
|
12
12
|
export { charWidth, displayWidth, leadWidth, widthOf } from "./width.js";
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* package); the cli never imports it directly. Experimental — no
|
|
7
7
|
* API-stability promise yet.
|
|
8
8
|
*/
|
|
9
|
-
export { SPINNER, foldLine, foldWords, visibleWidth, bodySpacing, Container, cellComponent, foldTerms, CAP_TASK_LIVE, formatDuration, statusLine, boxTop, boxBottom, terminalPipe, } from "./components.js";
|
|
9
|
+
export { SPINNER, foldLine, foldWords, visibleWidth, bodySpacing, Container, cellComponent, foldTerms, CAP_TASK_LIVE, elapsedLabel, formatDuration, statusLine, boxTop, boxBottom, terminalPipe, } from "./components.js";
|
|
10
10
|
export { editFileDiff, truncateDiff, writeFileDiff } from "./diff.js";
|
|
11
11
|
// W22 (the v8 input round): the pending-queue chips — the SAME
|
|
12
12
|
// UserMessage chip with the □ gutter, pre-rendered above the input
|
package/dist/md.js
CHANGED
|
@@ -713,35 +713,36 @@ function tableRows(b, W) {
|
|
|
713
713
|
if (cols === null)
|
|
714
714
|
return recordRows(t, W);
|
|
715
715
|
const p = palette();
|
|
716
|
-
//
|
|
717
|
-
//
|
|
718
|
-
//
|
|
719
|
-
//
|
|
720
|
-
//
|
|
721
|
-
//
|
|
716
|
+
// DECLARED REVERSAL (2026-09-17), TABLES ONLY. R2 removed the rails at
|
|
717
|
+
// the nineteen-screen review and MD-1.3 put ONE rule under the header;
|
|
718
|
+
// both are superseded here. Every other R2 hairline rule stands, and
|
|
719
|
+
// the record form keeps NO borders — it is the fallback, not a table.
|
|
720
|
+
//
|
|
721
|
+
// The rails are hairlines in the R2 hairline colour, and the styling
|
|
722
|
+
// goes on AFTER the measure, exactly as it does for a cell: a colour
|
|
723
|
+
// can never move a column.
|
|
724
|
+
const rule = (l, m, r) => `${p.dim}${l}${cols.map((w) => "\u2500".repeat(w + 2)).join(m)}${r}${p.reset}`;
|
|
725
|
+
const V = `${p.dim}\u2502${p.reset}`;
|
|
722
726
|
const row = (cells, bold) => {
|
|
723
727
|
const boxes = cells.map((c, i) => cellBox(c, cols[i], t.align[i], bold));
|
|
724
728
|
const rows = [];
|
|
725
729
|
for (let k = 0; k < Math.max(...boxes.map((x) => x.length)); k += 1) {
|
|
726
730
|
// a short box pays its blanks so the columns to its right do not
|
|
727
731
|
// move: a cell is a BOX, and the row is as tall as its tallest.
|
|
728
|
-
|
|
732
|
+
// Trailing space is NOT stripped any more — it sits inside the
|
|
733
|
+
// closing rail, and stripping it would pull the rail left.
|
|
734
|
+
rows.push(`${V}${boxes.map((x, i) => ` ${x[k] ?? " ".repeat(cols[i])} `).join(V)}${V}`);
|
|
729
735
|
}
|
|
730
736
|
return rows;
|
|
731
737
|
};
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
// was carried by SGR bold ALONE, so in a pipe, under NO_COLOR, or on a
|
|
741
|
-
// terminal with weak bold, seven identical rows arrived with nothing
|
|
742
|
-
// saying which one names the columns.
|
|
743
|
-
const ruleW = cols.reduce((n, w) => n + w, 0) + Math.max(0, cols.length - 1) * 2;
|
|
744
|
-
return [...row(t.header, true), ` ${p.dim}${"\u2500".repeat(ruleW)}${p.reset}`, ...t.rows.flatMap((r) => row(r, false))];
|
|
738
|
+
const body = t.rows.flatMap((r, i) => (i === 0 ? row(r, false) : [rule("\u251c", "\u253c", "\u2524"), ...row(r, false)]));
|
|
739
|
+
return [
|
|
740
|
+
rule("\u250c", "\u252c", "\u2510"),
|
|
741
|
+
...row(t.header, true),
|
|
742
|
+
rule("\u251c", "\u253c", "\u2524"),
|
|
743
|
+
...body,
|
|
744
|
+
rule("\u2514", "\u2534", "\u2518"),
|
|
745
|
+
];
|
|
745
746
|
}
|
|
746
747
|
/** A cell's column count: what a human sees, styling removed. */
|
|
747
748
|
function cellWidth(cell) {
|
|
@@ -753,13 +754,22 @@ function cellWidth(cell) {
|
|
|
753
754
|
* report's sample stopped reading as a table — and it is the one number
|
|
754
755
|
* that decides when the record form is still the better answer. */
|
|
755
756
|
const CELL_FLOOR = 8;
|
|
756
|
-
/** The drawn width of a grid with these columns
|
|
757
|
-
*
|
|
758
|
-
*
|
|
759
|
-
*
|
|
760
|
-
*
|
|
757
|
+
/** The drawn width of a grid with these columns.
|
|
758
|
+
*
|
|
759
|
+
* DECLARED REVERSAL (2026-09-17, tables only): the R2 measure was the
|
|
760
|
+
* two-column inset plus every column and its two-space gutter — the
|
|
761
|
+
* shape of a table with NO RAILS. With rails it is `sum + 3n + 1`: one
|
|
762
|
+
* rail between every pair of columns and one at each edge (n+1), and one
|
|
763
|
+
* space of padding inside every rail (2n).
|
|
764
|
+
*
|
|
765
|
+
* This function's old comment said the record threshold "has always been
|
|
766
|
+
* stated in it", and that is exactly why changing it IS the re-cut: the
|
|
767
|
+
* grid costs n−1 columns more than the railless form — 2 for a
|
|
768
|
+
* three-column table, 6 for a seven-column one — and the record fallback
|
|
769
|
+
* arrives that much earlier. Nothing else moves: the greedy shrink, the
|
|
770
|
+
* CELL_FLOOR of 8 and the fallback criterion are untouched. */
|
|
761
771
|
function gridWidth(cols) {
|
|
762
|
-
return cols.reduce((n, w) => n + w +
|
|
772
|
+
return cols.reduce((n, w) => n + w, 0) + 3 * cols.length + 1;
|
|
763
773
|
}
|
|
764
774
|
/**
|
|
765
775
|
* MD-1.1 — take one column off the WIDEST column until the grid fits.
|
package/dist/render.js
CHANGED
|
@@ -220,8 +220,14 @@ function toolSummaryDetail(name, input, result) {
|
|
|
220
220
|
const exit = exitCodeOf(result);
|
|
221
221
|
return `${command} (exit ${exit})`;
|
|
222
222
|
}
|
|
223
|
-
case "list_dir":
|
|
224
|
-
|
|
223
|
+
case "list_dir": {
|
|
224
|
+
// "(root)" already answered an ABSENT path. A model that sends
|
|
225
|
+
// "." explicitly is making the same request and was getting the
|
|
226
|
+
// dot on the row. `./` likewise. Anything else — including `..`
|
|
227
|
+
// and a dotfile directory like `.github` — is a real path.
|
|
228
|
+
const dir = String(input.path ?? ".");
|
|
229
|
+
return dir === "." || dir === "./" ? "(root)" : dir;
|
|
230
|
+
}
|
|
225
231
|
case "search_text":
|
|
226
232
|
return searchSubject(input);
|
|
227
233
|
default:
|
|
@@ -261,8 +267,14 @@ function toolTargetRaw(name, input) {
|
|
|
261
267
|
return String(input.path ?? "?");
|
|
262
268
|
case "shell":
|
|
263
269
|
return String(input.command ?? "?");
|
|
264
|
-
case "list_dir":
|
|
265
|
-
|
|
270
|
+
case "list_dir": {
|
|
271
|
+
// "(root)" already answered an ABSENT path. A model that sends
|
|
272
|
+
// "." explicitly is making the same request and was getting the
|
|
273
|
+
// dot on the row. `./` likewise. Anything else — including `..`
|
|
274
|
+
// and a dotfile directory like `.github` — is a real path.
|
|
275
|
+
const dir = String(input.path ?? ".");
|
|
276
|
+
return dir === "." || dir === "./" ? "(root)" : dir;
|
|
277
|
+
}
|
|
266
278
|
case "search_text":
|
|
267
279
|
return searchSubject(input);
|
|
268
280
|
default:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-tui-cells",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.0",
|
|
4
4
|
"description": "kiso tui-cells \u2014 the components cell renderer (components, diff, width, the render slice). Zero runtime dependencies: input is data, output is bytes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|