Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions media/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -2323,18 +2323,19 @@ html.pi-viewport-recovering #pi-web-app {
expected while reading; scrolling past the end chains to the conversation). */
.thinking-block:not(.thinking-collapsed) .thinking-content {
display: block;
max-height: min(60vh, 520px);
max-height: min(60vh, 260px);
overflow-y: auto;
}

/* Live (spinner present): bottom-aligned latest lines, capped and clipped
without a nested scrollbar so the wheel always scrolls the conversation.
Kept last so it wins over the reader rule while the model is thinking. */
/* Live (spinner present): bottom-aligned latest lines, capped with the same
max height as the finished reader and clipped without a nested scrollbar so
the wheel always scrolls the conversation. Kept last so it wins over the
reader rule while the model is thinking. */
.thinking-block:has(.thinking-spinner) .thinking-content {
display: flex;
flex-direction: column;
justify-content: flex-end;
max-height: 320px;
max-height: min(60vh, 260px);
overflow: hidden;
}

Expand Down
34 changes: 34 additions & 0 deletions src/test/thinking-height.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as assert from "node:assert";
import { readFileSync } from "node:fs";

const styles = readFileSync(
new URL("../../media/style.css", import.meta.url),
"utf8",
);

suite("Thinking block height", () => {
test("halves the finished reader height to 260px", () => {
const rule = styles.match(/\.thinking-block:not\(\.thinking-collapsed\) \.thinking-content \{[\s\S]*?\n\}/)?.[0];
assert.ok(rule, "finished thinking reader rule not found");
assert.match(rule, /max-height: min\(60vh, 260px\)/);
});

test("uses the same max height for the live streaming tail", () => {
const reader = styles.match(/\.thinking-block:not\(\.thinking-collapsed\) \.thinking-content \{[\s\S]*?\n\}/)?.[0];
const live = styles.match(/\.thinking-block:has\(\.thinking-spinner\) \.thinking-content \{[\s\S]*?\n\}/)?.[0];
assert.ok(live, "live thinking rule not found");
assert.match(live, /max-height: min\(60vh, 260px\)/);
assert.strictEqual(
live?.match(/max-height: [^;]+;/)?.[0],
reader?.match(/max-height: [^;]+;/)?.[0],
"both thinking modes must cap at the same height",
);
});

test("keeps per-mode overflow behaviour intact", () => {
const reader = styles.match(/\.thinking-block:not\(\.thinking-collapsed\) \.thinking-content \{[\s\S]*?\n\}/)?.[0];
const live = styles.match(/\.thinking-block:has\(\.thinking-spinner\) \.thinking-content \{[\s\S]*?\n\}/)?.[0];
assert.match(reader ?? "", /overflow-y: auto/);
assert.match(live ?? "", /overflow: hidden/);
});
});
Loading