@vincemakes/kiso-tui 0.1.34 → 0.1.35

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.
@@ -177,8 +177,12 @@ class ThinkingFold {
177
177
  render(W, _ctx) {
178
178
  const block = this.cell.text;
179
179
  const trimmed = escapeTerminal(block.trim());
180
+ // the SHORT branch is width-aware TOO: the ≤100 short-circuit was
181
+ // W-blind — a short block at a narrow width returned the line
182
+ // UNFOLDED and tripped invariant ① (the crash class still live on
183
+ // npm for short /think blocks after a resize).
180
184
  if (trimmed.length <= 100)
181
- return [`${palette().dim}…${trimmed}${palette().reset}`];
185
+ return [`${palette().dim}…${widthCut(trimmed, Math.max(1, W - 1))}${palette().reset}`];
182
186
  const suffix = ` (${block.length} chars · /think)`;
183
187
  const slice = Math.max(1, W - 1 - suffix.length);
184
188
  return [`${palette().dim}…${widthCut(trimmed, slice)}${suffix}${palette().reset}`];
@@ -674,17 +674,16 @@ export class Body {
674
674
  #drawSteady(out, W, H, liveTop, liveLines, menuRows, ctx) {
675
675
  const editor = this.#inputRow(W, ctx); // derived from the frame — the marker
676
676
  const committed = this.#committedLinesThisFrame;
677
- // the commits' scrolls — from the anchor (H−1) down to H, then LF
678
- if (committed.length > 0) {
679
- out.push("\x1b[1B");
680
- for (let i = 0; i < committed.length; i += 1)
681
- out.push("\n");
682
- }
683
- else {
684
- // no commits — jump from the anchor (H−2) straight to the
685
- // bottom row H (the commit path's LFs left the cursor there)
686
- out.push("\x1b[2B");
687
- }
677
+ // the jump from the anchor (H−2) straight to the bottom row H,
678
+ // then N real LFs scroll the screen exactly N rows — ONE per
679
+ // committed line (the bookkeeping; the stale 1B anchor jumped to
680
+ // H−1 and the N LFs scrolled only N−1 — the committed section sat
681
+ // one row short in the scrollback). The bottom-up repaint below
682
+ // overwrites the scrolled-in rows (the scroll count is screen-
683
+ // neutral — proven by the emulator probe).
684
+ out.push("\x1b[2B");
685
+ for (let i = 0; i < committed.length; i += 1)
686
+ out.push("\n");
688
687
  // the bottom-up repaint, from the last row up — V6-3: the design
689
688
  // §03 chrome: status (H), lower ╌ (H−1), input (H−2), upper ╌ (H−3)
690
689
  out.push(`\x1b[1G\x1b[0K${this.#checked(statusLine(this.#status, this.#tail, this.#question !== null, W), W)}`); // H — the status
@@ -694,17 +693,26 @@ export class Body {
694
693
  for (let i = menuRows.length - 1; i >= 0; i -= 1) {
695
694
  out.push(`\x1b[1A\x1b[1G\x1b[0K${this.#checked(menuRows[i], W)}`);
696
695
  }
697
- for (let i = liveLines.length - 1; i >= 0; i -= 1) {
698
- out.push(`\x1b[1A\x1b[1G\x1b[0K${this.#checked(liveLines[i], W)}`);
696
+ // the LIVE lines at their MODEL rows CUP, never the relative
697
+ // march: the march drew them adjacent to the chrome (rows
698
+ // H−3−n..H−3) while the model placed them at [liveTop..liveTop+n−1]
699
+ // — in the unclamped geometry the gap ELs below erased the march's
700
+ // copy and the streamed text was INVISIBLE until the commit frame.
701
+ // The CUP rows land in the content area (≤ H−4−menu ≤ 21), inside
702
+ // the frozen CUP budget of invariant ②.
703
+ for (let i = 0; i < liveLines.length; i += 1) {
704
+ out.push(`\x1b[${liveTop + i};1H\x1b[0K${this.#checked(liveLines[i], W)}`);
699
705
  }
700
706
  // the FROZEN area — CUP (absolute rows; this is the FREEZE path —
701
- // the old code's frozen writes were CUP too. The live region above
702
- // keeps the relative-only rule; the frozen rows are computed from
703
- // the current geometry, so external writes (the CLI's console.error
704
- // CRLF) cannot misplace them the way a relative march could).
707
+ // the old code's frozen writes were CUP too. The frozen rows are
708
+ // computed from the current geometry, so external writes (the CLI's
709
+ // console.error CRLF) cannot misplace them the way a relative march
710
+ // could).
705
711
  // 1. the GAP rows (between the live content and the chrome) — EL'd
706
- // so the old content there cannot ghost.
707
- for (let r = liveTop + liveLines.length; r <= H - 4; r += 1) {
712
+ // so the old content there cannot ghost; the range stops ABOVE
713
+ // the menu (the menu's rows at [H−3−menu..H−4] are marched and
714
+ // must survive — the unclamped geometry erased them).
715
+ for (let r = liveTop + liveLines.length; r <= H - 4 - menuRows.length; r += 1) {
708
716
  out.push(`\x1b[${r};1H\x1b[0K`);
709
717
  }
710
718
  // 2. the STALE rows above the committed section — the scrolled old
@@ -721,9 +729,21 @@ export class Body {
721
729
  out.push(`\x1b[${Math.max(1, liveTop - committed.length + i)};1H\x1b[0K${this.#checked(committed[i], W)}`);
722
730
  }
723
731
  // the cursor: down to the anchor (H−2, the input row) + left to the marker —
724
- // the down-distance from the LAST written row (the committed bottom,
725
- // the stale bottom, or the gap bottom when nothing else wrote).
726
- const lastRow = committed.length > 0 ? liveTop - 1 : staleFrom < liveTop ? liveTop - 1 : H - 4;
732
+ // the down-distance from the LAST written row, in byte order: the
733
+ // committed band's bottom, then the stale ELs' bottom, then the gap
734
+ // ELs' bottom, then the live lines' bottom, then the menu's top
735
+ // (its last marched row), else the chrome's upper ╌.
736
+ const lastRow = committed.length > 0
737
+ ? Math.max(1, liveTop - 1)
738
+ : staleFrom < liveTop
739
+ ? liveTop - 1
740
+ : liveTop + liveLines.length <= H - 4 - menuRows.length
741
+ ? H - 4 - menuRows.length
742
+ : liveLines.length > 0
743
+ ? liveTop + liveLines.length - 1
744
+ : menuRows.length > 0
745
+ ? H - 3 - menuRows.length
746
+ : H - 3;
727
747
  const down = H - 2 - lastRow; // the anchor: the input row (H−2)
728
748
  if (down > 0)
729
749
  out.push(`\x1b[${down}B`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-tui",
3
- "version": "0.1.34",
3
+ "version": "0.1.35",
4
4
  "description": "kiso tui — the pure terminal layer (cell renderer, dock, raw editor, diff, palette). Zero runtime dependencies: input is data, output is bytes.",
5
5
  "type": "module",
6
6
  "license": "MIT",