feat(cli): confirmed second Ctrl+O/Ctrl+T collapses blocks stranded above the viewport (#4011) - #4025

Merged
likun666661 merged 3 commits into
apache:mainfrom
me2seeks:fix/cli-4011-confirmed-collapse-redraw
Aug 28, 2026
Merged

feat(cli): confirmed second Ctrl+O/Ctrl+T collapses blocks stranded above the viewport (#4011)#4025
likun666661 merged 3 commits into
apache:mainfrom
me2seeks:fix/cli-4011-confirmed-collapse-redraw

Conversation

@me2seeks

Copy link
Copy Markdown
Contributor

Summary

Fixes#4011 — implements option 2 of #1134 (the explicit redraw command deferred by #1140 as an orthogonal follow-up).

Today a Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live viewport: their heads sit in terminal scrollback, which the #1097 contract forbids rewriting, so they can never be collapsed again (#1134). #1140 made the fully-stranded case explained, but the common partial case — some cards collapse, the rest stay stuck — is silent, so the toggle reads as broken.

This PR turns the #1097 tradeoff into an informed per-press choice:

  • A collapse that strands expanded blocks above the viewport now appends a notice naming them: "N tool cards above the view stayed expanded in scrollback — press Ctrl+O again within 2s to collapse them too (this redraws the screen and clears pre-session scrollback)." Partial collapses stop being silent; expand-direction toggles stay quiet about collapsed blocks above (they are compact and harmless, and arming there would make a quick expand–collapse pair read the second press as "expand everything").
  • A second press of the same key within EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS applies the collapsed default to every entry — without flipping the default the way a plain toggle would — and pays one deliberate scrollback-clearing full redraw through pi-tui's existing public requestRender(true), which re-anchors the viewport at the tail. Transcript content is fully re-rendered into fresh scrollback, so nothing from the session is lost; only pre-session shell scrollback is cleared, and only after the explicit second press.
  • The window constant lives beside the notice copy so the offer text and the runner's confirm window share one authority.

No pi-tui patch or upstream change is needed: requestRender(force) already exposes the full-render path (resetRenderState → width-change branch → ESC[2J ESC[H ESC[3J + full rewrite), and the layout's viewport shadow predicts the same re-anchor (firstChanged < current → tailTop).

Verification

  • packages/cli: npm test 546/546 — 4 new unit tests (mixed-position collapse notice, confirmed collapse does not flip the default and is idempotent, expand direction stays silent, inert-window predicate) and 1 new integration test.
  • The integration test drives the real pi-tui renderer through runMakaPiTui on a 24-row terminal: an 80-line thinking block is expanded past the viewport, the first collapse press renders the offer with noESC[3J in the output stream, and the confirmed second press emits the deliberate clear exactly once while the block returns to its compact Thinking… row. (Tool cards window their expanded rendering — ⋯ N lines hidden ⋯ — so a thinking block is what can actually push its head past the viewport; this matches how fix(cli): flip the expansion default and explain when Ctrl+O/Ctrl+T finds no block in view #1140 verified fix(cli): a block expanded past the viewport cannot be collapsed again #1134.)
  • Root npm run lint, npm run format:check, packages/cli typecheck, and check:asf-headers all clean.
  • Not run: desktop/eval suites (cli-only change; nothing outside packages/cli is touched).

AI use

  • Generative tooling made a substantive contribution

Tool(s) and scope: OpenCode (kimi k3-256k) drafted the implementation and tests under human direction; the approach (second-press confirm, collapse-only arming) was reviewed and decided interactively. Commits carry Generated-by: OpenCode trailers.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above

@github-actionsgithub-actionsBot added the effort/L Under 1000 readable lines label Aug 27, 2026

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed exact head fd9cc61091a826235eb7ee59df031fb4a889cc2c. The off-screen expansion state is derived from the real renderer geometry, and the second-press path correctly applies the collapsed default to all entries instead of toggling it again. I found one P2 usability issue on the normal user path and one P3 consistency gap.

[P2] Start the confirmation window when the proposal can be acted on, or do not reverse the action after it expires

The first collapse records expansionCollapseConfirm.at = Date.now() before the notice is rendered (packages/cli/src/pi-tui-runner.ts:3415-3433). The notice asks the user to read a fairly long explanation and press again within two seconds (packages/cli/src/pi-transcript.ts:643-650). If the user reads it and presses after those two seconds, the input silently falls through to the ordinary toggle path. That path changes the default back to expanded and expands the visible cards—the opposite of the collapse the user just accepted.

This is recoverable, so it is P2 rather than P1, but it is the exact interaction this PR introduces. Please either allow a realistic reading window, start the deadline when the notice is rendered, or make the first press after expiry present the proposal again instead of performing the opposite toggle.

[P3] Document the intentional exception to the inert-geometry guard

applyExpansionDefaultToAll() is the only expansion mutation without the togglesInert() guard used by the other paths (pi-transcript.ts:512-513, 546-552, 566-577, 630-632). A confirmed full redraw may make that safe by design, but a short comment should state that this path intentionally operates even after a wholesale transcript replacement leaves positions unknown.

The key-repeat guard, cross-key disarming, renderer geometry, and full redraw behavior otherwise look sound. Exact-head test and label checks succeeded, and the current-main merge is clean. I did not approve because the P2 remains live.


Posted by an automated review agent operated by @WAWQAQ. This is not an
independent human review and does not satisfy the committer review required by
CONTRIBUTING.md. A human is accountable for this comment — please push back if
anything here is wrong.

简体中文

本条评论由 @WAWQAQ 运行的自动化审查程序发出。它不构成 CONTRIBUTING.md
所要求的独立人类审查,也不能替代人类审查。有人类对本条评论负责,如有错误请直接指出。

…primitives (apache#4011)
A Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live
viewport: their heads sit in terminal scrollback, which the apache#1097
contract forbids rewriting, so they stay expanded with no way back
(apache#1134). apache#1140 made the all-stranded case explained but left the far
more common partial case silent — some cards collapse, some stay stuck,
and the keypress reads as broken.
The toggle now appends a notice whenever a collapse strands blocks,
naming the count and offering the escape hatch: a second press within
EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS collapses them too via one
knowingly-accepted, scrollback-clearing full redraw (apache#1134 option 2,
deferred by apache#1140 as an orthogonal follow-up). The confirm offer exists
for collapses only — collapsed blocks above the viewport are compact
and harmless, and arming on expand would make a quick expand-then-
collapse pair read the second press as "expand everything".
New exports for the runner wiring: hasExpandedEntriesAboveViewport
(arming predicate) and applyExpansionDefaultToAll (applies the current
default to every entry including above-viewport ones, without flipping
the default the way a plain toggle would). The window constant lives
beside the notice copy so the offer text and the runner's confirm
window share one authority.
Generated-by: OpenCode
…e stranded blocks (apache#4011)
Wire the confirm gesture into the key handler: a collapse toggle that
leaves expanded blocks above the viewport arms a 2s window; pressing
the same key again inside it applies the collapsed default to every
entry and forces one scrollback-clearing full redraw through pi-tui's
existing public requestRender(true), which re-anchors the viewport at
the tail. Transcript content is fully re-rendered into fresh scrollback,
so nothing from the session is lost; only pre-session shell scrollback
is cleared, and only after the explicit second press the notice
announced.
The integration test drives the real renderer on a 24-row terminal: an
80-line thinking block is expanded past the viewport, the first collapse
press renders the offer with no ESC[3J in the stream, and the confirmed
second press emits exactly the deliberate clear while collapsing the
block back to its compact row.
Generated-by: OpenCode
@me2seeks
me2seeksforce-pushed the fix/cli-4011-confirmed-collapse-redraw branch from fd9cc61 to 3dd60a7CompareAugust 28, 2026 03:32
@likun666661
likun666661 merged commit 70a66ac into apache:mainAug 28, 2026
1 check passed
saltand pushed a commit to saltand/maka-agent that referenced this pull request Aug 31, 2026
…bove the viewport (apache#4011) (apache#4025)
* feat(cli): name stranded expansion blocks and add confirmed-collapse primitives (apache#4011)
A Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live
viewport: their heads sit in terminal scrollback, which the apache#1097
contract forbids rewriting, so they stay expanded with no way back
(apache#1134). apache#1140 made the all-stranded case explained but left the far
more common partial case silent — some cards collapse, some stay stuck,
and the keypress reads as broken.
The toggle now appends a notice whenever a collapse strands blocks,
naming the count and offering the escape hatch: a second press within
EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS collapses them too via one
knowingly-accepted, scrollback-clearing full redraw (apache#1134 option 2,
deferred by apache#1140 as an orthogonal follow-up). The confirm offer exists
for collapses only — collapsed blocks above the viewport are compact
and harmless, and arming on expand would make a quick expand-then-
collapse pair read the second press as "expand everything".
New exports for the runner wiring: hasExpandedEntriesAboveViewport
(arming predicate) and applyExpansionDefaultToAll (applies the current
default to every entry including above-viewport ones, without flipping
the default the way a plain toggle would). The window constant lives
beside the notice copy so the offer text and the runner's confirm
window share one authority.
Generated-by: OpenCode
* feat(cli): second Ctrl+O/Ctrl+T press pays one full redraw to collapse stranded blocks (apache#4011)
Wire the confirm gesture into the key handler: a collapse toggle that
leaves expanded blocks above the viewport arms a 2s window; pressing
the same key again inside it applies the collapsed default to every
entry and forces one scrollback-clearing full redraw through pi-tui's
existing public requestRender(true), which re-anchors the viewport at
the tail. Transcript content is fully re-rendered into fresh scrollback,
so nothing from the session is lost; only pre-session shell scrollback
is cleared, and only after the explicit second press the notice
announced.
The integration test drives the real renderer on a 24-row terminal: an
80-line thinking block is expanded past the viewport, the first collapse
press renders the offer with no ESC[3J in the stream, and the confirmed
second press emits exactly the deliberate clear while collapsing the
block back to its compact row.
Generated-by: OpenCode
* fix(cli): re-offer expired expansion confirmation
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/LUnder 1000 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

proposal(cli): confirmed full redraw so Ctrl+O/Ctrl+T can collapse blocks stuck above the viewport

3 participants

@me2seeks@jackwener@likun666661
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(cli): confirmed second Ctrl+O/Ctrl+T collapses blocks stranded above the viewport (#4011) - #4025

Merged
likun666661 merged 3 commits into
apache:mainfrom
me2seeks:fix/cli-4011-confirmed-collapse-redraw
Aug 28, 2026
Merged

feat(cli): confirmed second Ctrl+O/Ctrl+T collapses blocks stranded above the viewport (#4011)#4025
likun666661 merged 3 commits into
apache:mainfrom
me2seeks:fix/cli-4011-confirmed-collapse-redraw

Conversation

@me2seeks

Copy link
Copy Markdown
Contributor

Summary

Fixes#4011 — implements option 2 of #1134 (the explicit redraw command deferred by #1140 as an orthogonal follow-up).

Today a Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live viewport: their heads sit in terminal scrollback, which the #1097 contract forbids rewriting, so they can never be collapsed again (#1134). #1140 made the fully-stranded case explained, but the common partial case — some cards collapse, the rest stay stuck — is silent, so the toggle reads as broken.

This PR turns the #1097 tradeoff into an informed per-press choice:

  • A collapse that strands expanded blocks above the viewport now appends a notice naming them: "N tool cards above the view stayed expanded in scrollback — press Ctrl+O again within 2s to collapse them too (this redraws the screen and clears pre-session scrollback)." Partial collapses stop being silent; expand-direction toggles stay quiet about collapsed blocks above (they are compact and harmless, and arming there would make a quick expand–collapse pair read the second press as "expand everything").
  • A second press of the same key within EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS applies the collapsed default to every entry — without flipping the default the way a plain toggle would — and pays one deliberate scrollback-clearing full redraw through pi-tui's existing public requestRender(true), which re-anchors the viewport at the tail. Transcript content is fully re-rendered into fresh scrollback, so nothing from the session is lost; only pre-session shell scrollback is cleared, and only after the explicit second press.
  • The window constant lives beside the notice copy so the offer text and the runner's confirm window share one authority.

No pi-tui patch or upstream change is needed: requestRender(force) already exposes the full-render path (resetRenderState → width-change branch → ESC[2J ESC[H ESC[3J + full rewrite), and the layout's viewport shadow predicts the same re-anchor (firstChanged < current → tailTop).

Verification

  • packages/cli: npm test 546/546 — 4 new unit tests (mixed-position collapse notice, confirmed collapse does not flip the default and is idempotent, expand direction stays silent, inert-window predicate) and 1 new integration test.
  • The integration test drives the real pi-tui renderer through runMakaPiTui on a 24-row terminal: an 80-line thinking block is expanded past the viewport, the first collapse press renders the offer with noESC[3J in the output stream, and the confirmed second press emits the deliberate clear exactly once while the block returns to its compact Thinking… row. (Tool cards window their expanded rendering — ⋯ N lines hidden ⋯ — so a thinking block is what can actually push its head past the viewport; this matches how fix(cli): flip the expansion default and explain when Ctrl+O/Ctrl+T finds no block in view #1140 verified fix(cli): a block expanded past the viewport cannot be collapsed again #1134.)
  • Root npm run lint, npm run format:check, packages/cli typecheck, and check:asf-headers all clean.
  • Not run: desktop/eval suites (cli-only change; nothing outside packages/cli is touched).

AI use

  • Generative tooling made a substantive contribution

Tool(s) and scope: OpenCode (kimi k3-256k) drafted the implementation and tests under human direction; the approach (second-press confirm, collapse-only arming) was reviewed and decided interactively. Commits carry Generated-by: OpenCode trailers.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above

@github-actionsgithub-actionsBot added the effort/L Under 1000 readable lines label Aug 27, 2026

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed exact head fd9cc61091a826235eb7ee59df031fb4a889cc2c. The off-screen expansion state is derived from the real renderer geometry, and the second-press path correctly applies the collapsed default to all entries instead of toggling it again. I found one P2 usability issue on the normal user path and one P3 consistency gap.

[P2] Start the confirmation window when the proposal can be acted on, or do not reverse the action after it expires

The first collapse records expansionCollapseConfirm.at = Date.now() before the notice is rendered (packages/cli/src/pi-tui-runner.ts:3415-3433). The notice asks the user to read a fairly long explanation and press again within two seconds (packages/cli/src/pi-transcript.ts:643-650). If the user reads it and presses after those two seconds, the input silently falls through to the ordinary toggle path. That path changes the default back to expanded and expands the visible cards—the opposite of the collapse the user just accepted.

This is recoverable, so it is P2 rather than P1, but it is the exact interaction this PR introduces. Please either allow a realistic reading window, start the deadline when the notice is rendered, or make the first press after expiry present the proposal again instead of performing the opposite toggle.

[P3] Document the intentional exception to the inert-geometry guard

applyExpansionDefaultToAll() is the only expansion mutation without the togglesInert() guard used by the other paths (pi-transcript.ts:512-513, 546-552, 566-577, 630-632). A confirmed full redraw may make that safe by design, but a short comment should state that this path intentionally operates even after a wholesale transcript replacement leaves positions unknown.

The key-repeat guard, cross-key disarming, renderer geometry, and full redraw behavior otherwise look sound. Exact-head test and label checks succeeded, and the current-main merge is clean. I did not approve because the P2 remains live.


Posted by an automated review agent operated by @WAWQAQ. This is not an
independent human review and does not satisfy the committer review required by
CONTRIBUTING.md. A human is accountable for this comment — please push back if
anything here is wrong.

简体中文

本条评论由 @WAWQAQ 运行的自动化审查程序发出。它不构成 CONTRIBUTING.md
所要求的独立人类审查,也不能替代人类审查。有人类对本条评论负责,如有错误请直接指出。

…primitives (apache#4011)
A Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live
viewport: their heads sit in terminal scrollback, which the apache#1097
contract forbids rewriting, so they stay expanded with no way back
(apache#1134). apache#1140 made the all-stranded case explained but left the far
more common partial case silent — some cards collapse, some stay stuck,
and the keypress reads as broken.
The toggle now appends a notice whenever a collapse strands blocks,
naming the count and offering the escape hatch: a second press within
EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS collapses them too via one
knowingly-accepted, scrollback-clearing full redraw (apache#1134 option 2,
deferred by apache#1140 as an orthogonal follow-up). The confirm offer exists
for collapses only — collapsed blocks above the viewport are compact
and harmless, and arming on expand would make a quick expand-then-
collapse pair read the second press as "expand everything".
New exports for the runner wiring: hasExpandedEntriesAboveViewport
(arming predicate) and applyExpansionDefaultToAll (applies the current
default to every entry including above-viewport ones, without flipping
the default the way a plain toggle would). The window constant lives
beside the notice copy so the offer text and the runner's confirm
window share one authority.
Generated-by: OpenCode
…e stranded blocks (apache#4011)
Wire the confirm gesture into the key handler: a collapse toggle that
leaves expanded blocks above the viewport arms a 2s window; pressing
the same key again inside it applies the collapsed default to every
entry and forces one scrollback-clearing full redraw through pi-tui's
existing public requestRender(true), which re-anchors the viewport at
the tail. Transcript content is fully re-rendered into fresh scrollback,
so nothing from the session is lost; only pre-session shell scrollback
is cleared, and only after the explicit second press the notice
announced.
The integration test drives the real renderer on a 24-row terminal: an
80-line thinking block is expanded past the viewport, the first collapse
press renders the offer with no ESC[3J in the stream, and the confirmed
second press emits exactly the deliberate clear while collapsing the
block back to its compact row.
Generated-by: OpenCode
@me2seeks
me2seeksforce-pushed the fix/cli-4011-confirmed-collapse-redraw branch from fd9cc61 to 3dd60a7CompareAugust 28, 2026 03:32
@likun666661
likun666661 merged commit 70a66ac into apache:mainAug 28, 2026
1 check passed
saltand pushed a commit to saltand/maka-agent that referenced this pull request Aug 31, 2026
…bove the viewport (apache#4011) (apache#4025)
* feat(cli): name stranded expansion blocks and add confirmed-collapse primitives (apache#4011)
A Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live
viewport: their heads sit in terminal scrollback, which the apache#1097
contract forbids rewriting, so they stay expanded with no way back
(apache#1134). apache#1140 made the all-stranded case explained but left the far
more common partial case silent — some cards collapse, some stay stuck,
and the keypress reads as broken.
The toggle now appends a notice whenever a collapse strands blocks,
naming the count and offering the escape hatch: a second press within
EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS collapses them too via one
knowingly-accepted, scrollback-clearing full redraw (apache#1134 option 2,
deferred by apache#1140 as an orthogonal follow-up). The confirm offer exists
for collapses only — collapsed blocks above the viewport are compact
and harmless, and arming on expand would make a quick expand-then-
collapse pair read the second press as "expand everything".
New exports for the runner wiring: hasExpandedEntriesAboveViewport
(arming predicate) and applyExpansionDefaultToAll (applies the current
default to every entry including above-viewport ones, without flipping
the default the way a plain toggle would). The window constant lives
beside the notice copy so the offer text and the runner's confirm
window share one authority.
Generated-by: OpenCode
* feat(cli): second Ctrl+O/Ctrl+T press pays one full redraw to collapse stranded blocks (apache#4011)
Wire the confirm gesture into the key handler: a collapse toggle that
leaves expanded blocks above the viewport arms a 2s window; pressing
the same key again inside it applies the collapsed default to every
entry and forces one scrollback-clearing full redraw through pi-tui's
existing public requestRender(true), which re-anchors the viewport at
the tail. Transcript content is fully re-rendered into fresh scrollback,
so nothing from the session is lost; only pre-session shell scrollback
is cleared, and only after the explicit second press the notice
announced.
The integration test drives the real renderer on a 24-row terminal: an
80-line thinking block is expanded past the viewport, the first collapse
press renders the offer with no ESC[3J in the stream, and the confirmed
second press emits exactly the deliberate clear while collapsing the
block back to its compact row.
Generated-by: OpenCode
* fix(cli): re-offer expired expansion confirmation
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/LUnder 1000 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

proposal(cli): confirmed full redraw so Ctrl+O/Ctrl+T can collapse blocks stuck above the viewport

3 participants

@me2seeks@jackwener@likun666661
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(cli): confirmed second Ctrl+O/Ctrl+T collapses blocks stranded above the viewport (#4011) - #4025

Merged
likun666661 merged 3 commits into
apache:mainfrom
me2seeks:fix/cli-4011-confirmed-collapse-redraw
Aug 28, 2026
Merged

feat(cli): confirmed second Ctrl+O/Ctrl+T collapses blocks stranded above the viewport (#4011)#4025
likun666661 merged 3 commits into
apache:mainfrom
me2seeks:fix/cli-4011-confirmed-collapse-redraw

Conversation

@me2seeks

Copy link
Copy Markdown
Contributor

Summary

Fixes#4011 — implements option 2 of #1134 (the explicit redraw command deferred by #1140 as an orthogonal follow-up).

Today a Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live viewport: their heads sit in terminal scrollback, which the #1097 contract forbids rewriting, so they can never be collapsed again (#1134). #1140 made the fully-stranded case explained, but the common partial case — some cards collapse, the rest stay stuck — is silent, so the toggle reads as broken.

This PR turns the #1097 tradeoff into an informed per-press choice:

  • A collapse that strands expanded blocks above the viewport now appends a notice naming them: "N tool cards above the view stayed expanded in scrollback — press Ctrl+O again within 2s to collapse them too (this redraws the screen and clears pre-session scrollback)." Partial collapses stop being silent; expand-direction toggles stay quiet about collapsed blocks above (they are compact and harmless, and arming there would make a quick expand–collapse pair read the second press as "expand everything").
  • A second press of the same key within EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS applies the collapsed default to every entry — without flipping the default the way a plain toggle would — and pays one deliberate scrollback-clearing full redraw through pi-tui's existing public requestRender(true), which re-anchors the viewport at the tail. Transcript content is fully re-rendered into fresh scrollback, so nothing from the session is lost; only pre-session shell scrollback is cleared, and only after the explicit second press.
  • The window constant lives beside the notice copy so the offer text and the runner's confirm window share one authority.

No pi-tui patch or upstream change is needed: requestRender(force) already exposes the full-render path (resetRenderState → width-change branch → ESC[2J ESC[H ESC[3J + full rewrite), and the layout's viewport shadow predicts the same re-anchor (firstChanged < current → tailTop).

Verification

  • packages/cli: npm test 546/546 — 4 new unit tests (mixed-position collapse notice, confirmed collapse does not flip the default and is idempotent, expand direction stays silent, inert-window predicate) and 1 new integration test.
  • The integration test drives the real pi-tui renderer through runMakaPiTui on a 24-row terminal: an 80-line thinking block is expanded past the viewport, the first collapse press renders the offer with noESC[3J in the output stream, and the confirmed second press emits the deliberate clear exactly once while the block returns to its compact Thinking… row. (Tool cards window their expanded rendering — ⋯ N lines hidden ⋯ — so a thinking block is what can actually push its head past the viewport; this matches how fix(cli): flip the expansion default and explain when Ctrl+O/Ctrl+T finds no block in view #1140 verified fix(cli): a block expanded past the viewport cannot be collapsed again #1134.)
  • Root npm run lint, npm run format:check, packages/cli typecheck, and check:asf-headers all clean.
  • Not run: desktop/eval suites (cli-only change; nothing outside packages/cli is touched).

AI use

  • Generative tooling made a substantive contribution

Tool(s) and scope: OpenCode (kimi k3-256k) drafted the implementation and tests under human direction; the approach (second-press confirm, collapse-only arming) was reviewed and decided interactively. Commits carry Generated-by: OpenCode trailers.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above

@github-actionsgithub-actionsBot added the effort/L Under 1000 readable lines label Aug 27, 2026

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed exact head fd9cc61091a826235eb7ee59df031fb4a889cc2c. The off-screen expansion state is derived from the real renderer geometry, and the second-press path correctly applies the collapsed default to all entries instead of toggling it again. I found one P2 usability issue on the normal user path and one P3 consistency gap.

[P2] Start the confirmation window when the proposal can be acted on, or do not reverse the action after it expires

The first collapse records expansionCollapseConfirm.at = Date.now() before the notice is rendered (packages/cli/src/pi-tui-runner.ts:3415-3433). The notice asks the user to read a fairly long explanation and press again within two seconds (packages/cli/src/pi-transcript.ts:643-650). If the user reads it and presses after those two seconds, the input silently falls through to the ordinary toggle path. That path changes the default back to expanded and expands the visible cards—the opposite of the collapse the user just accepted.

This is recoverable, so it is P2 rather than P1, but it is the exact interaction this PR introduces. Please either allow a realistic reading window, start the deadline when the notice is rendered, or make the first press after expiry present the proposal again instead of performing the opposite toggle.

[P3] Document the intentional exception to the inert-geometry guard

applyExpansionDefaultToAll() is the only expansion mutation without the togglesInert() guard used by the other paths (pi-transcript.ts:512-513, 546-552, 566-577, 630-632). A confirmed full redraw may make that safe by design, but a short comment should state that this path intentionally operates even after a wholesale transcript replacement leaves positions unknown.

The key-repeat guard, cross-key disarming, renderer geometry, and full redraw behavior otherwise look sound. Exact-head test and label checks succeeded, and the current-main merge is clean. I did not approve because the P2 remains live.


Posted by an automated review agent operated by @WAWQAQ. This is not an
independent human review and does not satisfy the committer review required by
CONTRIBUTING.md. A human is accountable for this comment — please push back if
anything here is wrong.

简体中文

本条评论由 @WAWQAQ 运行的自动化审查程序发出。它不构成 CONTRIBUTING.md
所要求的独立人类审查,也不能替代人类审查。有人类对本条评论负责,如有错误请直接指出。

…primitives (apache#4011)
A Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live
viewport: their heads sit in terminal scrollback, which the apache#1097
contract forbids rewriting, so they stay expanded with no way back
(apache#1134). apache#1140 made the all-stranded case explained but left the far
more common partial case silent — some cards collapse, some stay stuck,
and the keypress reads as broken.
The toggle now appends a notice whenever a collapse strands blocks,
naming the count and offering the escape hatch: a second press within
EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS collapses them too via one
knowingly-accepted, scrollback-clearing full redraw (apache#1134 option 2,
deferred by apache#1140 as an orthogonal follow-up). The confirm offer exists
for collapses only — collapsed blocks above the viewport are compact
and harmless, and arming on expand would make a quick expand-then-
collapse pair read the second press as "expand everything".
New exports for the runner wiring: hasExpandedEntriesAboveViewport
(arming predicate) and applyExpansionDefaultToAll (applies the current
default to every entry including above-viewport ones, without flipping
the default the way a plain toggle would). The window constant lives
beside the notice copy so the offer text and the runner's confirm
window share one authority.
Generated-by: OpenCode
…e stranded blocks (apache#4011)
Wire the confirm gesture into the key handler: a collapse toggle that
leaves expanded blocks above the viewport arms a 2s window; pressing
the same key again inside it applies the collapsed default to every
entry and forces one scrollback-clearing full redraw through pi-tui's
existing public requestRender(true), which re-anchors the viewport at
the tail. Transcript content is fully re-rendered into fresh scrollback,
so nothing from the session is lost; only pre-session shell scrollback
is cleared, and only after the explicit second press the notice
announced.
The integration test drives the real renderer on a 24-row terminal: an
80-line thinking block is expanded past the viewport, the first collapse
press renders the offer with no ESC[3J in the stream, and the confirmed
second press emits exactly the deliberate clear while collapsing the
block back to its compact row.
Generated-by: OpenCode
@me2seeks
me2seeksforce-pushed the fix/cli-4011-confirmed-collapse-redraw branch from fd9cc61 to 3dd60a7CompareAugust 28, 2026 03:32
@likun666661
likun666661 merged commit 70a66ac into apache:mainAug 28, 2026
1 check passed
saltand pushed a commit to saltand/maka-agent that referenced this pull request Aug 31, 2026
…bove the viewport (apache#4011) (apache#4025)
* feat(cli): name stranded expansion blocks and add confirmed-collapse primitives (apache#4011)
A Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live
viewport: their heads sit in terminal scrollback, which the apache#1097
contract forbids rewriting, so they stay expanded with no way back
(apache#1134). apache#1140 made the all-stranded case explained but left the far
more common partial case silent — some cards collapse, some stay stuck,
and the keypress reads as broken.
The toggle now appends a notice whenever a collapse strands blocks,
naming the count and offering the escape hatch: a second press within
EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS collapses them too via one
knowingly-accepted, scrollback-clearing full redraw (apache#1134 option 2,
deferred by apache#1140 as an orthogonal follow-up). The confirm offer exists
for collapses only — collapsed blocks above the viewport are compact
and harmless, and arming on expand would make a quick expand-then-
collapse pair read the second press as "expand everything".
New exports for the runner wiring: hasExpandedEntriesAboveViewport
(arming predicate) and applyExpansionDefaultToAll (applies the current
default to every entry including above-viewport ones, without flipping
the default the way a plain toggle would). The window constant lives
beside the notice copy so the offer text and the runner's confirm
window share one authority.
Generated-by: OpenCode
* feat(cli): second Ctrl+O/Ctrl+T press pays one full redraw to collapse stranded blocks (apache#4011)
Wire the confirm gesture into the key handler: a collapse toggle that
leaves expanded blocks above the viewport arms a 2s window; pressing
the same key again inside it applies the collapsed default to every
entry and forces one scrollback-clearing full redraw through pi-tui's
existing public requestRender(true), which re-anchors the viewport at
the tail. Transcript content is fully re-rendered into fresh scrollback,
so nothing from the session is lost; only pre-session shell scrollback
is cleared, and only after the explicit second press the notice
announced.
The integration test drives the real renderer on a 24-row terminal: an
80-line thinking block is expanded past the viewport, the first collapse
press renders the offer with no ESC[3J in the stream, and the confirmed
second press emits exactly the deliberate clear while collapsing the
block back to its compact row.
Generated-by: OpenCode
* fix(cli): re-offer expired expansion confirmation
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/LUnder 1000 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

proposal(cli): confirmed full redraw so Ctrl+O/Ctrl+T can collapse blocks stuck above the viewport

3 participants

@me2seeks@jackwener@likun666661
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(cli): confirmed second Ctrl+O/Ctrl+T collapses blocks stranded above the viewport (#4011) - #4025

Merged
likun666661 merged 3 commits into
apache:mainfrom
me2seeks:fix/cli-4011-confirmed-collapse-redraw
Aug 28, 2026
Merged

feat(cli): confirmed second Ctrl+O/Ctrl+T collapses blocks stranded above the viewport (#4011)#4025
likun666661 merged 3 commits into
apache:mainfrom
me2seeks:fix/cli-4011-confirmed-collapse-redraw

Conversation

@me2seeks

Copy link
Copy Markdown
Contributor

Summary

Fixes#4011 — implements option 2 of #1134 (the explicit redraw command deferred by #1140 as an orthogonal follow-up).

Today a Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live viewport: their heads sit in terminal scrollback, which the #1097 contract forbids rewriting, so they can never be collapsed again (#1134). #1140 made the fully-stranded case explained, but the common partial case — some cards collapse, the rest stay stuck — is silent, so the toggle reads as broken.

This PR turns the #1097 tradeoff into an informed per-press choice:

  • A collapse that strands expanded blocks above the viewport now appends a notice naming them: "N tool cards above the view stayed expanded in scrollback — press Ctrl+O again within 2s to collapse them too (this redraws the screen and clears pre-session scrollback)." Partial collapses stop being silent; expand-direction toggles stay quiet about collapsed blocks above (they are compact and harmless, and arming there would make a quick expand–collapse pair read the second press as "expand everything").
  • A second press of the same key within EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS applies the collapsed default to every entry — without flipping the default the way a plain toggle would — and pays one deliberate scrollback-clearing full redraw through pi-tui's existing public requestRender(true), which re-anchors the viewport at the tail. Transcript content is fully re-rendered into fresh scrollback, so nothing from the session is lost; only pre-session shell scrollback is cleared, and only after the explicit second press.
  • The window constant lives beside the notice copy so the offer text and the runner's confirm window share one authority.

No pi-tui patch or upstream change is needed: requestRender(force) already exposes the full-render path (resetRenderState → width-change branch → ESC[2J ESC[H ESC[3J + full rewrite), and the layout's viewport shadow predicts the same re-anchor (firstChanged < current → tailTop).

Verification

  • packages/cli: npm test 546/546 — 4 new unit tests (mixed-position collapse notice, confirmed collapse does not flip the default and is idempotent, expand direction stays silent, inert-window predicate) and 1 new integration test.
  • The integration test drives the real pi-tui renderer through runMakaPiTui on a 24-row terminal: an 80-line thinking block is expanded past the viewport, the first collapse press renders the offer with noESC[3J in the output stream, and the confirmed second press emits the deliberate clear exactly once while the block returns to its compact Thinking… row. (Tool cards window their expanded rendering — ⋯ N lines hidden ⋯ — so a thinking block is what can actually push its head past the viewport; this matches how fix(cli): flip the expansion default and explain when Ctrl+O/Ctrl+T finds no block in view #1140 verified fix(cli): a block expanded past the viewport cannot be collapsed again #1134.)
  • Root npm run lint, npm run format:check, packages/cli typecheck, and check:asf-headers all clean.
  • Not run: desktop/eval suites (cli-only change; nothing outside packages/cli is touched).

AI use

  • Generative tooling made a substantive contribution

Tool(s) and scope: OpenCode (kimi k3-256k) drafted the implementation and tests under human direction; the approach (second-press confirm, collapse-only arming) was reviewed and decided interactively. Commits carry Generated-by: OpenCode trailers.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above

@github-actionsgithub-actionsBot added the effort/L Under 1000 readable lines label Aug 27, 2026

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed exact head fd9cc61091a826235eb7ee59df031fb4a889cc2c. The off-screen expansion state is derived from the real renderer geometry, and the second-press path correctly applies the collapsed default to all entries instead of toggling it again. I found one P2 usability issue on the normal user path and one P3 consistency gap.

[P2] Start the confirmation window when the proposal can be acted on, or do not reverse the action after it expires

The first collapse records expansionCollapseConfirm.at = Date.now() before the notice is rendered (packages/cli/src/pi-tui-runner.ts:3415-3433). The notice asks the user to read a fairly long explanation and press again within two seconds (packages/cli/src/pi-transcript.ts:643-650). If the user reads it and presses after those two seconds, the input silently falls through to the ordinary toggle path. That path changes the default back to expanded and expands the visible cards—the opposite of the collapse the user just accepted.

This is recoverable, so it is P2 rather than P1, but it is the exact interaction this PR introduces. Please either allow a realistic reading window, start the deadline when the notice is rendered, or make the first press after expiry present the proposal again instead of performing the opposite toggle.

[P3] Document the intentional exception to the inert-geometry guard

applyExpansionDefaultToAll() is the only expansion mutation without the togglesInert() guard used by the other paths (pi-transcript.ts:512-513, 546-552, 566-577, 630-632). A confirmed full redraw may make that safe by design, but a short comment should state that this path intentionally operates even after a wholesale transcript replacement leaves positions unknown.

The key-repeat guard, cross-key disarming, renderer geometry, and full redraw behavior otherwise look sound. Exact-head test and label checks succeeded, and the current-main merge is clean. I did not approve because the P2 remains live.


Posted by an automated review agent operated by @WAWQAQ. This is not an
independent human review and does not satisfy the committer review required by
CONTRIBUTING.md. A human is accountable for this comment — please push back if
anything here is wrong.

简体中文

本条评论由 @WAWQAQ 运行的自动化审查程序发出。它不构成 CONTRIBUTING.md
所要求的独立人类审查,也不能替代人类审查。有人类对本条评论负责,如有错误请直接指出。

…primitives (apache#4011)
A Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live
viewport: their heads sit in terminal scrollback, which the apache#1097
contract forbids rewriting, so they stay expanded with no way back
(apache#1134). apache#1140 made the all-stranded case explained but left the far
more common partial case silent — some cards collapse, some stay stuck,
and the keypress reads as broken.
The toggle now appends a notice whenever a collapse strands blocks,
naming the count and offering the escape hatch: a second press within
EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS collapses them too via one
knowingly-accepted, scrollback-clearing full redraw (apache#1134 option 2,
deferred by apache#1140 as an orthogonal follow-up). The confirm offer exists
for collapses only — collapsed blocks above the viewport are compact
and harmless, and arming on expand would make a quick expand-then-
collapse pair read the second press as "expand everything".
New exports for the runner wiring: hasExpandedEntriesAboveViewport
(arming predicate) and applyExpansionDefaultToAll (applies the current
default to every entry including above-viewport ones, without flipping
the default the way a plain toggle would). The window constant lives
beside the notice copy so the offer text and the runner's confirm
window share one authority.
Generated-by: OpenCode
…e stranded blocks (apache#4011)
Wire the confirm gesture into the key handler: a collapse toggle that
leaves expanded blocks above the viewport arms a 2s window; pressing
the same key again inside it applies the collapsed default to every
entry and forces one scrollback-clearing full redraw through pi-tui's
existing public requestRender(true), which re-anchors the viewport at
the tail. Transcript content is fully re-rendered into fresh scrollback,
so nothing from the session is lost; only pre-session shell scrollback
is cleared, and only after the explicit second press the notice
announced.
The integration test drives the real renderer on a 24-row terminal: an
80-line thinking block is expanded past the viewport, the first collapse
press renders the offer with no ESC[3J in the stream, and the confirmed
second press emits exactly the deliberate clear while collapsing the
block back to its compact row.
Generated-by: OpenCode
@me2seeks
me2seeksforce-pushed the fix/cli-4011-confirmed-collapse-redraw branch from fd9cc61 to 3dd60a7CompareAugust 28, 2026 03:32
@likun666661
likun666661 merged commit 70a66ac into apache:mainAug 28, 2026
1 check passed
saltand pushed a commit to saltand/maka-agent that referenced this pull request Aug 31, 2026
…bove the viewport (apache#4011) (apache#4025)
* feat(cli): name stranded expansion blocks and add confirmed-collapse primitives (apache#4011)
A Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live
viewport: their heads sit in terminal scrollback, which the apache#1097
contract forbids rewriting, so they stay expanded with no way back
(apache#1134). apache#1140 made the all-stranded case explained but left the far
more common partial case silent — some cards collapse, some stay stuck,
and the keypress reads as broken.
The toggle now appends a notice whenever a collapse strands blocks,
naming the count and offering the escape hatch: a second press within
EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS collapses them too via one
knowingly-accepted, scrollback-clearing full redraw (apache#1134 option 2,
deferred by apache#1140 as an orthogonal follow-up). The confirm offer exists
for collapses only — collapsed blocks above the viewport are compact
and harmless, and arming on expand would make a quick expand-then-
collapse pair read the second press as "expand everything".
New exports for the runner wiring: hasExpandedEntriesAboveViewport
(arming predicate) and applyExpansionDefaultToAll (applies the current
default to every entry including above-viewport ones, without flipping
the default the way a plain toggle would). The window constant lives
beside the notice copy so the offer text and the runner's confirm
window share one authority.
Generated-by: OpenCode
* feat(cli): second Ctrl+O/Ctrl+T press pays one full redraw to collapse stranded blocks (apache#4011)
Wire the confirm gesture into the key handler: a collapse toggle that
leaves expanded blocks above the viewport arms a 2s window; pressing
the same key again inside it applies the collapsed default to every
entry and forces one scrollback-clearing full redraw through pi-tui's
existing public requestRender(true), which re-anchors the viewport at
the tail. Transcript content is fully re-rendered into fresh scrollback,
so nothing from the session is lost; only pre-session shell scrollback
is cleared, and only after the explicit second press the notice
announced.
The integration test drives the real renderer on a 24-row terminal: an
80-line thinking block is expanded past the viewport, the first collapse
press renders the offer with no ESC[3J in the stream, and the confirmed
second press emits exactly the deliberate clear while collapsing the
block back to its compact row.
Generated-by: OpenCode
* fix(cli): re-offer expired expansion confirmation
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/LUnder 1000 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

proposal(cli): confirmed full redraw so Ctrl+O/Ctrl+T can collapse blocks stuck above the viewport

3 participants

@me2seeks@jackwener@likun666661
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(cli): confirmed second Ctrl+O/Ctrl+T collapses blocks stranded above the viewport (#4011) - #4025

Merged
likun666661 merged 3 commits into
apache:mainfrom
me2seeks:fix/cli-4011-confirmed-collapse-redraw
Aug 28, 2026
Merged

feat(cli): confirmed second Ctrl+O/Ctrl+T collapses blocks stranded above the viewport (#4011)#4025
likun666661 merged 3 commits into
apache:mainfrom
me2seeks:fix/cli-4011-confirmed-collapse-redraw

Conversation

@me2seeks

Copy link
Copy Markdown
Contributor

Summary

Fixes#4011 — implements option 2 of #1134 (the explicit redraw command deferred by #1140 as an orthogonal follow-up).

Today a Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live viewport: their heads sit in terminal scrollback, which the #1097 contract forbids rewriting, so they can never be collapsed again (#1134). #1140 made the fully-stranded case explained, but the common partial case — some cards collapse, the rest stay stuck — is silent, so the toggle reads as broken.

This PR turns the #1097 tradeoff into an informed per-press choice:

  • A collapse that strands expanded blocks above the viewport now appends a notice naming them: "N tool cards above the view stayed expanded in scrollback — press Ctrl+O again within 2s to collapse them too (this redraws the screen and clears pre-session scrollback)." Partial collapses stop being silent; expand-direction toggles stay quiet about collapsed blocks above (they are compact and harmless, and arming there would make a quick expand–collapse pair read the second press as "expand everything").
  • A second press of the same key within EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS applies the collapsed default to every entry — without flipping the default the way a plain toggle would — and pays one deliberate scrollback-clearing full redraw through pi-tui's existing public requestRender(true), which re-anchors the viewport at the tail. Transcript content is fully re-rendered into fresh scrollback, so nothing from the session is lost; only pre-session shell scrollback is cleared, and only after the explicit second press.
  • The window constant lives beside the notice copy so the offer text and the runner's confirm window share one authority.

No pi-tui patch or upstream change is needed: requestRender(force) already exposes the full-render path (resetRenderState → width-change branch → ESC[2J ESC[H ESC[3J + full rewrite), and the layout's viewport shadow predicts the same re-anchor (firstChanged < current → tailTop).

Verification

  • packages/cli: npm test 546/546 — 4 new unit tests (mixed-position collapse notice, confirmed collapse does not flip the default and is idempotent, expand direction stays silent, inert-window predicate) and 1 new integration test.
  • The integration test drives the real pi-tui renderer through runMakaPiTui on a 24-row terminal: an 80-line thinking block is expanded past the viewport, the first collapse press renders the offer with noESC[3J in the output stream, and the confirmed second press emits the deliberate clear exactly once while the block returns to its compact Thinking… row. (Tool cards window their expanded rendering — ⋯ N lines hidden ⋯ — so a thinking block is what can actually push its head past the viewport; this matches how fix(cli): flip the expansion default and explain when Ctrl+O/Ctrl+T finds no block in view #1140 verified fix(cli): a block expanded past the viewport cannot be collapsed again #1134.)
  • Root npm run lint, npm run format:check, packages/cli typecheck, and check:asf-headers all clean.
  • Not run: desktop/eval suites (cli-only change; nothing outside packages/cli is touched).

AI use

  • Generative tooling made a substantive contribution

Tool(s) and scope: OpenCode (kimi k3-256k) drafted the implementation and tests under human direction; the approach (second-press confirm, collapse-only arming) was reviewed and decided interactively. Commits carry Generated-by: OpenCode trailers.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above

@github-actionsgithub-actionsBot added the effort/L Under 1000 readable lines label Aug 27, 2026

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed exact head fd9cc61091a826235eb7ee59df031fb4a889cc2c. The off-screen expansion state is derived from the real renderer geometry, and the second-press path correctly applies the collapsed default to all entries instead of toggling it again. I found one P2 usability issue on the normal user path and one P3 consistency gap.

[P2] Start the confirmation window when the proposal can be acted on, or do not reverse the action after it expires

The first collapse records expansionCollapseConfirm.at = Date.now() before the notice is rendered (packages/cli/src/pi-tui-runner.ts:3415-3433). The notice asks the user to read a fairly long explanation and press again within two seconds (packages/cli/src/pi-transcript.ts:643-650). If the user reads it and presses after those two seconds, the input silently falls through to the ordinary toggle path. That path changes the default back to expanded and expands the visible cards—the opposite of the collapse the user just accepted.

This is recoverable, so it is P2 rather than P1, but it is the exact interaction this PR introduces. Please either allow a realistic reading window, start the deadline when the notice is rendered, or make the first press after expiry present the proposal again instead of performing the opposite toggle.

[P3] Document the intentional exception to the inert-geometry guard

applyExpansionDefaultToAll() is the only expansion mutation without the togglesInert() guard used by the other paths (pi-transcript.ts:512-513, 546-552, 566-577, 630-632). A confirmed full redraw may make that safe by design, but a short comment should state that this path intentionally operates even after a wholesale transcript replacement leaves positions unknown.

The key-repeat guard, cross-key disarming, renderer geometry, and full redraw behavior otherwise look sound. Exact-head test and label checks succeeded, and the current-main merge is clean. I did not approve because the P2 remains live.


Posted by an automated review agent operated by @WAWQAQ. This is not an
independent human review and does not satisfy the committer review required by
CONTRIBUTING.md. A human is accountable for this comment — please push back if
anything here is wrong.

简体中文

本条评论由 @WAWQAQ 运行的自动化审查程序发出。它不构成 CONTRIBUTING.md
所要求的独立人类审查,也不能替代人类审查。有人类对本条评论负责,如有错误请直接指出。

…primitives (apache#4011)
A Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live
viewport: their heads sit in terminal scrollback, which the apache#1097
contract forbids rewriting, so they stay expanded with no way back
(apache#1134). apache#1140 made the all-stranded case explained but left the far
more common partial case silent — some cards collapse, some stay stuck,
and the keypress reads as broken.
The toggle now appends a notice whenever a collapse strands blocks,
naming the count and offering the escape hatch: a second press within
EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS collapses them too via one
knowingly-accepted, scrollback-clearing full redraw (apache#1134 option 2,
deferred by apache#1140 as an orthogonal follow-up). The confirm offer exists
for collapses only — collapsed blocks above the viewport are compact
and harmless, and arming on expand would make a quick expand-then-
collapse pair read the second press as "expand everything".
New exports for the runner wiring: hasExpandedEntriesAboveViewport
(arming predicate) and applyExpansionDefaultToAll (applies the current
default to every entry including above-viewport ones, without flipping
the default the way a plain toggle would). The window constant lives
beside the notice copy so the offer text and the runner's confirm
window share one authority.
Generated-by: OpenCode
…e stranded blocks (apache#4011)
Wire the confirm gesture into the key handler: a collapse toggle that
leaves expanded blocks above the viewport arms a 2s window; pressing
the same key again inside it applies the collapsed default to every
entry and forces one scrollback-clearing full redraw through pi-tui's
existing public requestRender(true), which re-anchors the viewport at
the tail. Transcript content is fully re-rendered into fresh scrollback,
so nothing from the session is lost; only pre-session shell scrollback
is cleared, and only after the explicit second press the notice
announced.
The integration test drives the real renderer on a 24-row terminal: an
80-line thinking block is expanded past the viewport, the first collapse
press renders the offer with no ESC[3J in the stream, and the confirmed
second press emits exactly the deliberate clear while collapsing the
block back to its compact row.
Generated-by: OpenCode
@me2seeks
me2seeksforce-pushed the fix/cli-4011-confirmed-collapse-redraw branch from fd9cc61 to 3dd60a7CompareAugust 28, 2026 03:32
@likun666661
likun666661 merged commit 70a66ac into apache:mainAug 28, 2026
1 check passed
saltand pushed a commit to saltand/maka-agent that referenced this pull request Aug 31, 2026
…bove the viewport (apache#4011) (apache#4025)
* feat(cli): name stranded expansion blocks and add confirmed-collapse primitives (apache#4011)
A Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live
viewport: their heads sit in terminal scrollback, which the apache#1097
contract forbids rewriting, so they stay expanded with no way back
(apache#1134). apache#1140 made the all-stranded case explained but left the far
more common partial case silent — some cards collapse, some stay stuck,
and the keypress reads as broken.
The toggle now appends a notice whenever a collapse strands blocks,
naming the count and offering the escape hatch: a second press within
EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS collapses them too via one
knowingly-accepted, scrollback-clearing full redraw (apache#1134 option 2,
deferred by apache#1140 as an orthogonal follow-up). The confirm offer exists
for collapses only — collapsed blocks above the viewport are compact
and harmless, and arming on expand would make a quick expand-then-
collapse pair read the second press as "expand everything".
New exports for the runner wiring: hasExpandedEntriesAboveViewport
(arming predicate) and applyExpansionDefaultToAll (applies the current
default to every entry including above-viewport ones, without flipping
the default the way a plain toggle would). The window constant lives
beside the notice copy so the offer text and the runner's confirm
window share one authority.
Generated-by: OpenCode
* feat(cli): second Ctrl+O/Ctrl+T press pays one full redraw to collapse stranded blocks (apache#4011)
Wire the confirm gesture into the key handler: a collapse toggle that
leaves expanded blocks above the viewport arms a 2s window; pressing
the same key again inside it applies the collapsed default to every
entry and forces one scrollback-clearing full redraw through pi-tui's
existing public requestRender(true), which re-anchors the viewport at
the tail. Transcript content is fully re-rendered into fresh scrollback,
so nothing from the session is lost; only pre-session shell scrollback
is cleared, and only after the explicit second press the notice
announced.
The integration test drives the real renderer on a 24-row terminal: an
80-line thinking block is expanded past the viewport, the first collapse
press renders the offer with no ESC[3J in the stream, and the confirmed
second press emits exactly the deliberate clear while collapsing the
block back to its compact row.
Generated-by: OpenCode
* fix(cli): re-offer expired expansion confirmation
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/LUnder 1000 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

proposal(cli): confirmed full redraw so Ctrl+O/Ctrl+T can collapse blocks stuck above the viewport

3 participants

@me2seeks@jackwener@likun666661
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(cli): confirmed second Ctrl+O/Ctrl+T collapses blocks stranded above the viewport (#4011) - #4025

Merged
likun666661 merged 3 commits into
apache:mainfrom
me2seeks:fix/cli-4011-confirmed-collapse-redraw
Aug 28, 2026
Merged

feat(cli): confirmed second Ctrl+O/Ctrl+T collapses blocks stranded above the viewport (#4011)#4025
likun666661 merged 3 commits into
apache:mainfrom
me2seeks:fix/cli-4011-confirmed-collapse-redraw

Conversation

@me2seeks

Copy link
Copy Markdown
Contributor

Summary

Fixes#4011 — implements option 2 of #1134 (the explicit redraw command deferred by #1140 as an orthogonal follow-up).

Today a Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live viewport: their heads sit in terminal scrollback, which the #1097 contract forbids rewriting, so they can never be collapsed again (#1134). #1140 made the fully-stranded case explained, but the common partial case — some cards collapse, the rest stay stuck — is silent, so the toggle reads as broken.

This PR turns the #1097 tradeoff into an informed per-press choice:

  • A collapse that strands expanded blocks above the viewport now appends a notice naming them: "N tool cards above the view stayed expanded in scrollback — press Ctrl+O again within 2s to collapse them too (this redraws the screen and clears pre-session scrollback)." Partial collapses stop being silent; expand-direction toggles stay quiet about collapsed blocks above (they are compact and harmless, and arming there would make a quick expand–collapse pair read the second press as "expand everything").
  • A second press of the same key within EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS applies the collapsed default to every entry — without flipping the default the way a plain toggle would — and pays one deliberate scrollback-clearing full redraw through pi-tui's existing public requestRender(true), which re-anchors the viewport at the tail. Transcript content is fully re-rendered into fresh scrollback, so nothing from the session is lost; only pre-session shell scrollback is cleared, and only after the explicit second press.
  • The window constant lives beside the notice copy so the offer text and the runner's confirm window share one authority.

No pi-tui patch or upstream change is needed: requestRender(force) already exposes the full-render path (resetRenderState → width-change branch → ESC[2J ESC[H ESC[3J + full rewrite), and the layout's viewport shadow predicts the same re-anchor (firstChanged < current → tailTop).

Verification

  • packages/cli: npm test 546/546 — 4 new unit tests (mixed-position collapse notice, confirmed collapse does not flip the default and is idempotent, expand direction stays silent, inert-window predicate) and 1 new integration test.
  • The integration test drives the real pi-tui renderer through runMakaPiTui on a 24-row terminal: an 80-line thinking block is expanded past the viewport, the first collapse press renders the offer with noESC[3J in the output stream, and the confirmed second press emits the deliberate clear exactly once while the block returns to its compact Thinking… row. (Tool cards window their expanded rendering — ⋯ N lines hidden ⋯ — so a thinking block is what can actually push its head past the viewport; this matches how fix(cli): flip the expansion default and explain when Ctrl+O/Ctrl+T finds no block in view #1140 verified fix(cli): a block expanded past the viewport cannot be collapsed again #1134.)
  • Root npm run lint, npm run format:check, packages/cli typecheck, and check:asf-headers all clean.
  • Not run: desktop/eval suites (cli-only change; nothing outside packages/cli is touched).

AI use

  • Generative tooling made a substantive contribution

Tool(s) and scope: OpenCode (kimi k3-256k) drafted the implementation and tests under human direction; the approach (second-press confirm, collapse-only arming) was reviewed and decided interactively. Commits carry Generated-by: OpenCode trailers.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above

@github-actionsgithub-actionsBot added the effort/L Under 1000 readable lines label Aug 27, 2026

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed exact head fd9cc61091a826235eb7ee59df031fb4a889cc2c. The off-screen expansion state is derived from the real renderer geometry, and the second-press path correctly applies the collapsed default to all entries instead of toggling it again. I found one P2 usability issue on the normal user path and one P3 consistency gap.

[P2] Start the confirmation window when the proposal can be acted on, or do not reverse the action after it expires

The first collapse records expansionCollapseConfirm.at = Date.now() before the notice is rendered (packages/cli/src/pi-tui-runner.ts:3415-3433). The notice asks the user to read a fairly long explanation and press again within two seconds (packages/cli/src/pi-transcript.ts:643-650). If the user reads it and presses after those two seconds, the input silently falls through to the ordinary toggle path. That path changes the default back to expanded and expands the visible cards—the opposite of the collapse the user just accepted.

This is recoverable, so it is P2 rather than P1, but it is the exact interaction this PR introduces. Please either allow a realistic reading window, start the deadline when the notice is rendered, or make the first press after expiry present the proposal again instead of performing the opposite toggle.

[P3] Document the intentional exception to the inert-geometry guard

applyExpansionDefaultToAll() is the only expansion mutation without the togglesInert() guard used by the other paths (pi-transcript.ts:512-513, 546-552, 566-577, 630-632). A confirmed full redraw may make that safe by design, but a short comment should state that this path intentionally operates even after a wholesale transcript replacement leaves positions unknown.

The key-repeat guard, cross-key disarming, renderer geometry, and full redraw behavior otherwise look sound. Exact-head test and label checks succeeded, and the current-main merge is clean. I did not approve because the P2 remains live.


Posted by an automated review agent operated by @WAWQAQ. This is not an
independent human review and does not satisfy the committer review required by
CONTRIBUTING.md. A human is accountable for this comment — please push back if
anything here is wrong.

简体中文

本条评论由 @WAWQAQ 运行的自动化审查程序发出。它不构成 CONTRIBUTING.md
所要求的独立人类审查,也不能替代人类审查。有人类对本条评论负责,如有错误请直接指出。

…primitives (apache#4011)
A Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live
viewport: their heads sit in terminal scrollback, which the apache#1097
contract forbids rewriting, so they stay expanded with no way back
(apache#1134). apache#1140 made the all-stranded case explained but left the far
more common partial case silent — some cards collapse, some stay stuck,
and the keypress reads as broken.
The toggle now appends a notice whenever a collapse strands blocks,
naming the count and offering the escape hatch: a second press within
EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS collapses them too via one
knowingly-accepted, scrollback-clearing full redraw (apache#1134 option 2,
deferred by apache#1140 as an orthogonal follow-up). The confirm offer exists
for collapses only — collapsed blocks above the viewport are compact
and harmless, and arming on expand would make a quick expand-then-
collapse pair read the second press as "expand everything".
New exports for the runner wiring: hasExpandedEntriesAboveViewport
(arming predicate) and applyExpansionDefaultToAll (applies the current
default to every entry including above-viewport ones, without flipping
the default the way a plain toggle would). The window constant lives
beside the notice copy so the offer text and the runner's confirm
window share one authority.
Generated-by: OpenCode
…e stranded blocks (apache#4011)
Wire the confirm gesture into the key handler: a collapse toggle that
leaves expanded blocks above the viewport arms a 2s window; pressing
the same key again inside it applies the collapsed default to every
entry and forces one scrollback-clearing full redraw through pi-tui's
existing public requestRender(true), which re-anchors the viewport at
the tail. Transcript content is fully re-rendered into fresh scrollback,
so nothing from the session is lost; only pre-session shell scrollback
is cleared, and only after the explicit second press the notice
announced.
The integration test drives the real renderer on a 24-row terminal: an
80-line thinking block is expanded past the viewport, the first collapse
press renders the offer with no ESC[3J in the stream, and the confirmed
second press emits exactly the deliberate clear while collapsing the
block back to its compact row.
Generated-by: OpenCode
@me2seeks
me2seeksforce-pushed the fix/cli-4011-confirmed-collapse-redraw branch from fd9cc61 to 3dd60a7CompareAugust 28, 2026 03:32
@likun666661
likun666661 merged commit 70a66ac into apache:mainAug 28, 2026
1 check passed
saltand pushed a commit to saltand/maka-agent that referenced this pull request Aug 31, 2026
…bove the viewport (apache#4011) (apache#4025)
* feat(cli): name stranded expansion blocks and add confirmed-collapse primitives (apache#4011)
A Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live
viewport: their heads sit in terminal scrollback, which the apache#1097
contract forbids rewriting, so they stay expanded with no way back
(apache#1134). apache#1140 made the all-stranded case explained but left the far
more common partial case silent — some cards collapse, some stay stuck,
and the keypress reads as broken.
The toggle now appends a notice whenever a collapse strands blocks,
naming the count and offering the escape hatch: a second press within
EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS collapses them too via one
knowingly-accepted, scrollback-clearing full redraw (apache#1134 option 2,
deferred by apache#1140 as an orthogonal follow-up). The confirm offer exists
for collapses only — collapsed blocks above the viewport are compact
and harmless, and arming on expand would make a quick expand-then-
collapse pair read the second press as "expand everything".
New exports for the runner wiring: hasExpandedEntriesAboveViewport
(arming predicate) and applyExpansionDefaultToAll (applies the current
default to every entry including above-viewport ones, without flipping
the default the way a plain toggle would). The window constant lives
beside the notice copy so the offer text and the runner's confirm
window share one authority.
Generated-by: OpenCode
* feat(cli): second Ctrl+O/Ctrl+T press pays one full redraw to collapse stranded blocks (apache#4011)
Wire the confirm gesture into the key handler: a collapse toggle that
leaves expanded blocks above the viewport arms a 2s window; pressing
the same key again inside it applies the collapsed default to every
entry and forces one scrollback-clearing full redraw through pi-tui's
existing public requestRender(true), which re-anchors the viewport at
the tail. Transcript content is fully re-rendered into fresh scrollback,
so nothing from the session is lost; only pre-session shell scrollback
is cleared, and only after the explicit second press the notice
announced.
The integration test drives the real renderer on a 24-row terminal: an
80-line thinking block is expanded past the viewport, the first collapse
press renders the offer with no ESC[3J in the stream, and the confirmed
second press emits exactly the deliberate clear while collapsing the
block back to its compact row.
Generated-by: OpenCode
* fix(cli): re-offer expired expansion confirmation
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/LUnder 1000 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

proposal(cli): confirmed full redraw so Ctrl+O/Ctrl+T can collapse blocks stuck above the viewport

3 participants

@me2seeks@jackwener@likun666661
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(cli): confirmed second Ctrl+O/Ctrl+T collapses blocks stranded above the viewport (#4011) - #4025

Merged
likun666661 merged 3 commits into
apache:mainfrom
me2seeks:fix/cli-4011-confirmed-collapse-redraw
Aug 28, 2026
Merged

feat(cli): confirmed second Ctrl+O/Ctrl+T collapses blocks stranded above the viewport (#4011)#4025
likun666661 merged 3 commits into
apache:mainfrom
me2seeks:fix/cli-4011-confirmed-collapse-redraw

Conversation

@me2seeks

Copy link
Copy Markdown
Contributor

Summary

Fixes#4011 — implements option 2 of #1134 (the explicit redraw command deferred by #1140 as an orthogonal follow-up).

Today a Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live viewport: their heads sit in terminal scrollback, which the #1097 contract forbids rewriting, so they can never be collapsed again (#1134). #1140 made the fully-stranded case explained, but the common partial case — some cards collapse, the rest stay stuck — is silent, so the toggle reads as broken.

This PR turns the #1097 tradeoff into an informed per-press choice:

  • A collapse that strands expanded blocks above the viewport now appends a notice naming them: "N tool cards above the view stayed expanded in scrollback — press Ctrl+O again within 2s to collapse them too (this redraws the screen and clears pre-session scrollback)." Partial collapses stop being silent; expand-direction toggles stay quiet about collapsed blocks above (they are compact and harmless, and arming there would make a quick expand–collapse pair read the second press as "expand everything").
  • A second press of the same key within EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS applies the collapsed default to every entry — without flipping the default the way a plain toggle would — and pays one deliberate scrollback-clearing full redraw through pi-tui's existing public requestRender(true), which re-anchors the viewport at the tail. Transcript content is fully re-rendered into fresh scrollback, so nothing from the session is lost; only pre-session shell scrollback is cleared, and only after the explicit second press.
  • The window constant lives beside the notice copy so the offer text and the runner's confirm window share one authority.

No pi-tui patch or upstream change is needed: requestRender(force) already exposes the full-render path (resetRenderState → width-change branch → ESC[2J ESC[H ESC[3J + full rewrite), and the layout's viewport shadow predicts the same re-anchor (firstChanged < current → tailTop).

Verification

  • packages/cli: npm test 546/546 — 4 new unit tests (mixed-position collapse notice, confirmed collapse does not flip the default and is idempotent, expand direction stays silent, inert-window predicate) and 1 new integration test.
  • The integration test drives the real pi-tui renderer through runMakaPiTui on a 24-row terminal: an 80-line thinking block is expanded past the viewport, the first collapse press renders the offer with noESC[3J in the output stream, and the confirmed second press emits the deliberate clear exactly once while the block returns to its compact Thinking… row. (Tool cards window their expanded rendering — ⋯ N lines hidden ⋯ — so a thinking block is what can actually push its head past the viewport; this matches how fix(cli): flip the expansion default and explain when Ctrl+O/Ctrl+T finds no block in view #1140 verified fix(cli): a block expanded past the viewport cannot be collapsed again #1134.)
  • Root npm run lint, npm run format:check, packages/cli typecheck, and check:asf-headers all clean.
  • Not run: desktop/eval suites (cli-only change; nothing outside packages/cli is touched).

AI use

  • Generative tooling made a substantive contribution

Tool(s) and scope: OpenCode (kimi k3-256k) drafted the implementation and tests under human direction; the approach (second-press confirm, collapse-only arming) was reviewed and decided interactively. Commits carry Generated-by: OpenCode trailers.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above

@github-actionsgithub-actionsBot added the effort/L Under 1000 readable lines label Aug 27, 2026

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed exact head fd9cc61091a826235eb7ee59df031fb4a889cc2c. The off-screen expansion state is derived from the real renderer geometry, and the second-press path correctly applies the collapsed default to all entries instead of toggling it again. I found one P2 usability issue on the normal user path and one P3 consistency gap.

[P2] Start the confirmation window when the proposal can be acted on, or do not reverse the action after it expires

The first collapse records expansionCollapseConfirm.at = Date.now() before the notice is rendered (packages/cli/src/pi-tui-runner.ts:3415-3433). The notice asks the user to read a fairly long explanation and press again within two seconds (packages/cli/src/pi-transcript.ts:643-650). If the user reads it and presses after those two seconds, the input silently falls through to the ordinary toggle path. That path changes the default back to expanded and expands the visible cards—the opposite of the collapse the user just accepted.

This is recoverable, so it is P2 rather than P1, but it is the exact interaction this PR introduces. Please either allow a realistic reading window, start the deadline when the notice is rendered, or make the first press after expiry present the proposal again instead of performing the opposite toggle.

[P3] Document the intentional exception to the inert-geometry guard

applyExpansionDefaultToAll() is the only expansion mutation without the togglesInert() guard used by the other paths (pi-transcript.ts:512-513, 546-552, 566-577, 630-632). A confirmed full redraw may make that safe by design, but a short comment should state that this path intentionally operates even after a wholesale transcript replacement leaves positions unknown.

The key-repeat guard, cross-key disarming, renderer geometry, and full redraw behavior otherwise look sound. Exact-head test and label checks succeeded, and the current-main merge is clean. I did not approve because the P2 remains live.


Posted by an automated review agent operated by @WAWQAQ. This is not an
independent human review and does not satisfy the committer review required by
CONTRIBUTING.md. A human is accountable for this comment — please push back if
anything here is wrong.

简体中文

本条评论由 @WAWQAQ 运行的自动化审查程序发出。它不构成 CONTRIBUTING.md
所要求的独立人类审查,也不能替代人类审查。有人类对本条评论负责,如有错误请直接指出。

…primitives (apache#4011)
A Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live
viewport: their heads sit in terminal scrollback, which the apache#1097
contract forbids rewriting, so they stay expanded with no way back
(apache#1134). apache#1140 made the all-stranded case explained but left the far
more common partial case silent — some cards collapse, some stay stuck,
and the keypress reads as broken.
The toggle now appends a notice whenever a collapse strands blocks,
naming the count and offering the escape hatch: a second press within
EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS collapses them too via one
knowingly-accepted, scrollback-clearing full redraw (apache#1134 option 2,
deferred by apache#1140 as an orthogonal follow-up). The confirm offer exists
for collapses only — collapsed blocks above the viewport are compact
and harmless, and arming on expand would make a quick expand-then-
collapse pair read the second press as "expand everything".
New exports for the runner wiring: hasExpandedEntriesAboveViewport
(arming predicate) and applyExpansionDefaultToAll (applies the current
default to every entry including above-viewport ones, without flipping
the default the way a plain toggle would). The window constant lives
beside the notice copy so the offer text and the runner's confirm
window share one authority.
Generated-by: OpenCode
…e stranded blocks (apache#4011)
Wire the confirm gesture into the key handler: a collapse toggle that
leaves expanded blocks above the viewport arms a 2s window; pressing
the same key again inside it applies the collapsed default to every
entry and forces one scrollback-clearing full redraw through pi-tui's
existing public requestRender(true), which re-anchors the viewport at
the tail. Transcript content is fully re-rendered into fresh scrollback,
so nothing from the session is lost; only pre-session shell scrollback
is cleared, and only after the explicit second press the notice
announced.
The integration test drives the real renderer on a 24-row terminal: an
80-line thinking block is expanded past the viewport, the first collapse
press renders the offer with no ESC[3J in the stream, and the confirmed
second press emits exactly the deliberate clear while collapsing the
block back to its compact row.
Generated-by: OpenCode
@me2seeks
me2seeksforce-pushed the fix/cli-4011-confirmed-collapse-redraw branch from fd9cc61 to 3dd60a7CompareAugust 28, 2026 03:32
@likun666661
likun666661 merged commit 70a66ac into apache:mainAug 28, 2026
1 check passed
saltand pushed a commit to saltand/maka-agent that referenced this pull request Aug 31, 2026
…bove the viewport (apache#4011) (apache#4025)
* feat(cli): name stranded expansion blocks and add confirmed-collapse primitives (apache#4011)
A Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live
viewport: their heads sit in terminal scrollback, which the apache#1097
contract forbids rewriting, so they stay expanded with no way back
(apache#1134). apache#1140 made the all-stranded case explained but left the far
more common partial case silent — some cards collapse, some stay stuck,
and the keypress reads as broken.
The toggle now appends a notice whenever a collapse strands blocks,
naming the count and offering the escape hatch: a second press within
EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS collapses them too via one
knowingly-accepted, scrollback-clearing full redraw (apache#1134 option 2,
deferred by apache#1140 as an orthogonal follow-up). The confirm offer exists
for collapses only — collapsed blocks above the viewport are compact
and harmless, and arming on expand would make a quick expand-then-
collapse pair read the second press as "expand everything".
New exports for the runner wiring: hasExpandedEntriesAboveViewport
(arming predicate) and applyExpansionDefaultToAll (applies the current
default to every entry including above-viewport ones, without flipping
the default the way a plain toggle would). The window constant lives
beside the notice copy so the offer text and the runner's confirm
window share one authority.
Generated-by: OpenCode
* feat(cli): second Ctrl+O/Ctrl+T press pays one full redraw to collapse stranded blocks (apache#4011)
Wire the confirm gesture into the key handler: a collapse toggle that
leaves expanded blocks above the viewport arms a 2s window; pressing
the same key again inside it applies the collapsed default to every
entry and forces one scrollback-clearing full redraw through pi-tui's
existing public requestRender(true), which re-anchors the viewport at
the tail. Transcript content is fully re-rendered into fresh scrollback,
so nothing from the session is lost; only pre-session shell scrollback
is cleared, and only after the explicit second press the notice
announced.
The integration test drives the real renderer on a 24-row terminal: an
80-line thinking block is expanded past the viewport, the first collapse
press renders the offer with no ESC[3J in the stream, and the confirmed
second press emits exactly the deliberate clear while collapsing the
block back to its compact row.
Generated-by: OpenCode
* fix(cli): re-offer expired expansion confirmation
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/LUnder 1000 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

proposal(cli): confirmed full redraw so Ctrl+O/Ctrl+T can collapse blocks stuck above the viewport

3 participants

@me2seeks@jackwener@likun666661
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(cli): confirmed second Ctrl+O/Ctrl+T collapses blocks stranded above the viewport (#4011) - #4025

Merged
likun666661 merged 3 commits into
apache:mainfrom
me2seeks:fix/cli-4011-confirmed-collapse-redraw
Aug 28, 2026
Merged

feat(cli): confirmed second Ctrl+O/Ctrl+T collapses blocks stranded above the viewport (#4011)#4025
likun666661 merged 3 commits into
apache:mainfrom
me2seeks:fix/cli-4011-confirmed-collapse-redraw

Conversation

@me2seeks

Copy link
Copy Markdown
Contributor

Summary

Fixes#4011 — implements option 2 of #1134 (the explicit redraw command deferred by #1140 as an orthogonal follow-up).

Today a Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live viewport: their heads sit in terminal scrollback, which the #1097 contract forbids rewriting, so they can never be collapsed again (#1134). #1140 made the fully-stranded case explained, but the common partial case — some cards collapse, the rest stay stuck — is silent, so the toggle reads as broken.

This PR turns the #1097 tradeoff into an informed per-press choice:

  • A collapse that strands expanded blocks above the viewport now appends a notice naming them: "N tool cards above the view stayed expanded in scrollback — press Ctrl+O again within 2s to collapse them too (this redraws the screen and clears pre-session scrollback)." Partial collapses stop being silent; expand-direction toggles stay quiet about collapsed blocks above (they are compact and harmless, and arming there would make a quick expand–collapse pair read the second press as "expand everything").
  • A second press of the same key within EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS applies the collapsed default to every entry — without flipping the default the way a plain toggle would — and pays one deliberate scrollback-clearing full redraw through pi-tui's existing public requestRender(true), which re-anchors the viewport at the tail. Transcript content is fully re-rendered into fresh scrollback, so nothing from the session is lost; only pre-session shell scrollback is cleared, and only after the explicit second press.
  • The window constant lives beside the notice copy so the offer text and the runner's confirm window share one authority.

No pi-tui patch or upstream change is needed: requestRender(force) already exposes the full-render path (resetRenderState → width-change branch → ESC[2J ESC[H ESC[3J + full rewrite), and the layout's viewport shadow predicts the same re-anchor (firstChanged < current → tailTop).

Verification

  • packages/cli: npm test 546/546 — 4 new unit tests (mixed-position collapse notice, confirmed collapse does not flip the default and is idempotent, expand direction stays silent, inert-window predicate) and 1 new integration test.
  • The integration test drives the real pi-tui renderer through runMakaPiTui on a 24-row terminal: an 80-line thinking block is expanded past the viewport, the first collapse press renders the offer with noESC[3J in the output stream, and the confirmed second press emits the deliberate clear exactly once while the block returns to its compact Thinking… row. (Tool cards window their expanded rendering — ⋯ N lines hidden ⋯ — so a thinking block is what can actually push its head past the viewport; this matches how fix(cli): flip the expansion default and explain when Ctrl+O/Ctrl+T finds no block in view #1140 verified fix(cli): a block expanded past the viewport cannot be collapsed again #1134.)
  • Root npm run lint, npm run format:check, packages/cli typecheck, and check:asf-headers all clean.
  • Not run: desktop/eval suites (cli-only change; nothing outside packages/cli is touched).

AI use

  • Generative tooling made a substantive contribution

Tool(s) and scope: OpenCode (kimi k3-256k) drafted the implementation and tests under human direction; the approach (second-press confirm, collapse-only arming) was reviewed and decided interactively. Commits carry Generated-by: OpenCode trailers.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above

@github-actionsgithub-actionsBot added the effort/L Under 1000 readable lines label Aug 27, 2026

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed exact head fd9cc61091a826235eb7ee59df031fb4a889cc2c. The off-screen expansion state is derived from the real renderer geometry, and the second-press path correctly applies the collapsed default to all entries instead of toggling it again. I found one P2 usability issue on the normal user path and one P3 consistency gap.

[P2] Start the confirmation window when the proposal can be acted on, or do not reverse the action after it expires

The first collapse records expansionCollapseConfirm.at = Date.now() before the notice is rendered (packages/cli/src/pi-tui-runner.ts:3415-3433). The notice asks the user to read a fairly long explanation and press again within two seconds (packages/cli/src/pi-transcript.ts:643-650). If the user reads it and presses after those two seconds, the input silently falls through to the ordinary toggle path. That path changes the default back to expanded and expands the visible cards—the opposite of the collapse the user just accepted.

This is recoverable, so it is P2 rather than P1, but it is the exact interaction this PR introduces. Please either allow a realistic reading window, start the deadline when the notice is rendered, or make the first press after expiry present the proposal again instead of performing the opposite toggle.

[P3] Document the intentional exception to the inert-geometry guard

applyExpansionDefaultToAll() is the only expansion mutation without the togglesInert() guard used by the other paths (pi-transcript.ts:512-513, 546-552, 566-577, 630-632). A confirmed full redraw may make that safe by design, but a short comment should state that this path intentionally operates even after a wholesale transcript replacement leaves positions unknown.

The key-repeat guard, cross-key disarming, renderer geometry, and full redraw behavior otherwise look sound. Exact-head test and label checks succeeded, and the current-main merge is clean. I did not approve because the P2 remains live.


Posted by an automated review agent operated by @WAWQAQ. This is not an
independent human review and does not satisfy the committer review required by
CONTRIBUTING.md. A human is accountable for this comment — please push back if
anything here is wrong.

简体中文

本条评论由 @WAWQAQ 运行的自动化审查程序发出。它不构成 CONTRIBUTING.md
所要求的独立人类审查,也不能替代人类审查。有人类对本条评论负责,如有错误请直接指出。

…primitives (apache#4011)
A Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live
viewport: their heads sit in terminal scrollback, which the apache#1097
contract forbids rewriting, so they stay expanded with no way back
(apache#1134). apache#1140 made the all-stranded case explained but left the far
more common partial case silent — some cards collapse, some stay stuck,
and the keypress reads as broken.
The toggle now appends a notice whenever a collapse strands blocks,
naming the count and offering the escape hatch: a second press within
EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS collapses them too via one
knowingly-accepted, scrollback-clearing full redraw (apache#1134 option 2,
deferred by apache#1140 as an orthogonal follow-up). The confirm offer exists
for collapses only — collapsed blocks above the viewport are compact
and harmless, and arming on expand would make a quick expand-then-
collapse pair read the second press as "expand everything".
New exports for the runner wiring: hasExpandedEntriesAboveViewport
(arming predicate) and applyExpansionDefaultToAll (applies the current
default to every entry including above-viewport ones, without flipping
the default the way a plain toggle would). The window constant lives
beside the notice copy so the offer text and the runner's confirm
window share one authority.
Generated-by: OpenCode
…e stranded blocks (apache#4011)
Wire the confirm gesture into the key handler: a collapse toggle that
leaves expanded blocks above the viewport arms a 2s window; pressing
the same key again inside it applies the collapsed default to every
entry and forces one scrollback-clearing full redraw through pi-tui's
existing public requestRender(true), which re-anchors the viewport at
the tail. Transcript content is fully re-rendered into fresh scrollback,
so nothing from the session is lost; only pre-session shell scrollback
is cleared, and only after the explicit second press the notice
announced.
The integration test drives the real renderer on a 24-row terminal: an
80-line thinking block is expanded past the viewport, the first collapse
press renders the offer with no ESC[3J in the stream, and the confirmed
second press emits exactly the deliberate clear while collapsing the
block back to its compact row.
Generated-by: OpenCode
@me2seeks
me2seeksforce-pushed the fix/cli-4011-confirmed-collapse-redraw branch from fd9cc61 to 3dd60a7CompareAugust 28, 2026 03:32
@likun666661
likun666661 merged commit 70a66ac into apache:mainAug 28, 2026
1 check passed
saltand pushed a commit to saltand/maka-agent that referenced this pull request Aug 31, 2026
…bove the viewport (apache#4011) (apache#4025)
* feat(cli): name stranded expansion blocks and add confirmed-collapse primitives (apache#4011)
A Ctrl+O/Ctrl+T collapse can strand expanded blocks above the live
viewport: their heads sit in terminal scrollback, which the apache#1097
contract forbids rewriting, so they stay expanded with no way back
(apache#1134). apache#1140 made the all-stranded case explained but left the far
more common partial case silent — some cards collapse, some stay stuck,
and the keypress reads as broken.
The toggle now appends a notice whenever a collapse strands blocks,
naming the count and offering the escape hatch: a second press within
EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS collapses them too via one
knowingly-accepted, scrollback-clearing full redraw (apache#1134 option 2,
deferred by apache#1140 as an orthogonal follow-up). The confirm offer exists
for collapses only — collapsed blocks above the viewport are compact
and harmless, and arming on expand would make a quick expand-then-
collapse pair read the second press as "expand everything".
New exports for the runner wiring: hasExpandedEntriesAboveViewport
(arming predicate) and applyExpansionDefaultToAll (applies the current
default to every entry including above-viewport ones, without flipping
the default the way a plain toggle would). The window constant lives
beside the notice copy so the offer text and the runner's confirm
window share one authority.
Generated-by: OpenCode
* feat(cli): second Ctrl+O/Ctrl+T press pays one full redraw to collapse stranded blocks (apache#4011)
Wire the confirm gesture into the key handler: a collapse toggle that
leaves expanded blocks above the viewport arms a 2s window; pressing
the same key again inside it applies the collapsed default to every
entry and forces one scrollback-clearing full redraw through pi-tui's
existing public requestRender(true), which re-anchors the viewport at
the tail. Transcript content is fully re-rendered into fresh scrollback,
so nothing from the session is lost; only pre-session shell scrollback
is cleared, and only after the explicit second press the notice
announced.
The integration test drives the real renderer on a 24-row terminal: an
80-line thinking block is expanded past the viewport, the first collapse
press renders the offer with no ESC[3J in the stream, and the confirmed
second press emits exactly the deliberate clear while collapsing the
block back to its compact row.
Generated-by: OpenCode
* fix(cli): re-offer expired expansion confirmation
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/LUnder 1000 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

proposal(cli): confirmed full redraw so Ctrl+O/Ctrl+T can collapse blocks stuck above the viewport

3 participants

@me2seeks@jackwener@likun666661