diff --git a/app/globals.css b/app/globals.css index bf2dc3da4..b1641090e 100644 --- a/app/globals.css +++ b/app/globals.css @@ -312,20 +312,25 @@ button.extension-widget-trigger:focus-visible { .extension-status-line { display: flex; flex: 1 1 0; - align-items: center; + align-items: flex-start; min-width: 0; - height: 35px; - padding: 0 12px; + min-height: 35px; + max-height: min(144px, 18dvh); + padding: 8px 12px; + overflow-y: auto; + overscroll-behavior: contain; + scrollbar-gutter: stable; } .extension-status-text { + flex: 1; min-width: 0; - overflow: hidden; color: var(--text-muted); font-family: var(--font-mono); font-size: 11px; - text-overflow: ellipsis; - white-space: nowrap; + line-height: 1.45; + overflow-wrap: anywhere; + white-space: pre-wrap; } ::-webkit-scrollbar-thumb:hover { background: var(--text-dim); diff --git a/components/ExtensionStatusBar.test.mjs b/components/ExtensionStatusBar.test.mjs index 9747c119f..1be91ae34 100644 --- a/components/ExtensionStatusBar.test.mjs +++ b/components/ExtensionStatusBar.test.mjs @@ -1,4 +1,5 @@ import assert from "node:assert/strict"; +import { readFile } from "node:fs/promises"; import test from "node:test"; import React from "react"; import { renderToStaticMarkup } from "react-dom/server"; @@ -39,13 +40,25 @@ test("sorts status text by hidden key like the Pi CLI footer", () => { ); }); -test("sanitizes status text for a single-line display", () => { +test("preserves status line breaks while normalizing horizontal whitespace", () => { assert.equal( sanitizeExtensionStatusText(" first\tsecond \r\n third "), - "first second third", + "first second\nthird", ); }); +test("allows multiline status text to wrap and scroll within the footer", async () => { + const css = await readFile(new URL("../app/globals.css", import.meta.url), "utf8"); + const statusLineRule = css.match(/\.extension-status-line\s*\{([^}]*)\}/)?.[1] ?? ""; + const statusTextRule = css.match(/\.extension-status-text\s*\{([^}]*)\}/)?.[1] ?? ""; + + assert.match(statusLineRule, /max-height:/); + assert.match(statusLineRule, /overflow-y:\s*auto/); + assert.match(statusTextRule, /overflow-wrap:\s*anywhere/); + assert.match(statusTextRule, /white-space:\s*pre-wrap/); + assert.doesNotMatch(statusTextRule, /text-overflow:\s*ellipsis/); +}); + test("renders a single status line without identifier keys", () => { const html = renderStatusBar({ statuses: [ diff --git a/components/ExtensionStatusBar.tsx b/components/ExtensionStatusBar.tsx index fad86391e..61add835b 100644 --- a/components/ExtensionStatusBar.tsx +++ b/components/ExtensionStatusBar.tsx @@ -6,8 +6,10 @@ import { ExtensionWidgets } from "./ExtensionWidgets"; export function sanitizeExtensionStatusText(text: string): string { return text - .replace(/[\r\n\t]/g, " ") - .replace(/ +/g, " ") + .replace(/\r\n?/g, "\n") + .split("\n") + .map((line) => line.replace(/\t/g, " ").replace(/ +/g, " ").trim()) + .join("\n") .trim(); }