fix(tui): dropped stale hidden-lines footer on expanded output

Expanded `!` bash and `eval` execution output kept rendering the
`… N more lines (ctrl+o to expand)` footer after Ctrl+O revealed every
line, because `hiddenLineCount` was computed from the collapsed preview
window regardless of the `#expanded` (or sixel-passthrough) state.

Zero the hidden count whenever the full output is shown so
`buildStatusFooter()` stops advertising hidden lines and ctrl+o.

Fixes #5842
This commit is contained in:
roboomp
2026-07-17 13:11:52 +00:00
parent 0f9fceeea4
commit ea78e346d0
4 changed files with 58 additions and 4 deletions
+4
View File
@@ -2,6 +2,10 @@
## [Unreleased]
### Fixed
- Fixed expanded `!` bash and `eval` output keeping a stale `… N more lines (ctrl+o to expand)` footer after Ctrl+O revealed every line ([#5842](https://github.com/can1357/oh-my-pi/issues/5842)).
## [17.0.2] - 2026-07-17
### Added
@@ -146,14 +146,18 @@ export class BashExecutionComponent extends Container {
#updateDisplay(): void {
const availableLines = this.#outputLines;
// Apply preview truncation based on expanded state
// Full output is shown when expanded or when sixel passthrough renders
// the raw payload; the collapsed preview shows only the tail window.
const previewLogicalLines = availableLines.slice(-PREVIEW_LINES);
const hiddenLineCount = availableLines.length - previewLogicalLines.length;
const sixelLineMask =
TERMINAL.imageProtocol === ImageProtocol.Sixel && isSixelPassthroughEnabled()
? getSixelLineMask(availableLines)
: undefined;
const hasSixelOutput = sixelLineMask?.some(Boolean) ?? false;
const showingAllLines = this.#expanded || hasSixelOutput;
// Only the collapsed preview hides lines; when the full output is shown
// the footer must not keep advertising hidden lines / ctrl+o.
const hiddenLineCount = showingAllLines ? 0 : availableLines.length - previewLogicalLines.length;
// Rebuild content container
this.#contentContainer.clear();
@@ -163,7 +167,7 @@ export class BashExecutionComponent extends Container {
// Output
if (availableLines.length > 0) {
if (this.#expanded || hasSixelOutput) {
if (showingAllLines) {
const displayText = availableLines
.map((line, index) => (sixelLineMask?.[index] ? line : theme.fg("muted", line)))
.join("\n");
@@ -114,7 +114,9 @@ export class EvalExecutionComponent extends Container {
#updateDisplay(): void {
const availableLines = this.#outputLines;
const previewLogicalLines = availableLines.slice(-PREVIEW_LINES);
const hiddenLineCount = availableLines.length - previewLogicalLines.length;
// Only the collapsed preview hides lines; when expanded the footer must
// not keep advertising hidden lines / ctrl+o.
const hiddenLineCount = this.#expanded ? 0 : availableLines.length - previewLogicalLines.length;
this.#contentContainer.clear();
@@ -141,3 +141,47 @@ describe("BashExecutionComponent streaming throttle", () => {
expect(output).not.toContain("streaming_line");
});
});
describe("BashExecutionComponent expand footer", () => {
const ui = { requestRender: () => {}, requestComponentRender: () => {} } as unknown as TUI;
beforeEach(async () => {
const theme = await getThemeByName("dark");
expect(theme).toBeDefined();
setThemeInstance(theme!);
});
// PREVIEW_LINES is 20: 27 lines leaves 7 hidden in the collapsed preview.
const makeComponent = () => {
const component = new BashExecutionComponent("ls", ui, false);
const lines = Array.from({ length: 27 }, (_, i) => `entry${i}`);
component.setComplete(0, false, { output: lines.join("\n") });
return component;
};
it("advertises hidden lines while collapsed", () => {
const rendered = makeComponent().render(120).join("\n");
expect(rendered).toContain("more lines");
expect(rendered).toContain("ctrl+o to expand");
});
it("drops the hidden-lines footer once expanded", () => {
const component = makeComponent();
component.setExpanded(true);
const rendered = component.render(120).join("\n");
expect(rendered).not.toContain("more lines");
expect(rendered).not.toContain("ctrl+o to expand");
// Every line is now present, including the previously hidden prefix.
expect(rendered).toContain("entry0");
expect(rendered).toContain("entry26");
});
it("restores the footer when collapsed again", () => {
const component = makeComponent();
component.setExpanded(true);
component.setExpanded(false);
const rendered = component.render(120).join("\n");
expect(rendered).toContain("more lines");
expect(rendered).toContain("ctrl+o to expand");
});
});