mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-24 11:25:50 -06:00
fix(ui): keep long Markdown previews scrollable (#113465)
* [AI] fix(ui): add min-height:0 to flex scroll containers for Firefox Firefox enforces the CSS Flexbox automatic minimum size more strictly than Chromium, preventing .sidebar-content and .md-preview-dialog__body from shrinking below their content height. This blocked overflow:auto from creating a scrollable area when Markdown content exceeded the available panel height. Add min-height:0 to both flex children so Firefox allows them to shrink and become the inner scroll container. Related to #107571 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] fix(ui): bound Markdown sidebar previews * [AI] fix(ui): bound file sidebar editors * test(ui): cover scroll repair in sidebar column layout --------- Co-authored-by: 杨爱文 <yang.aiwen@xydigit.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Thiago Pontes AI <thiagopontesai@Hefestos-Mac-Mini.local>
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import "../../../styles.css";
|
||||
import "../../../styles/chat.css";
|
||||
import type { SidebarContent } from "./chat-sidebar.ts";
|
||||
import "./chat-sidebar.ts";
|
||||
|
||||
const browserMode = "__vitest_browser__" in globalThis;
|
||||
|
||||
type DetailPanel = HTMLElement & {
|
||||
content: SidebarContent;
|
||||
updateComplete: Promise<unknown>;
|
||||
};
|
||||
|
||||
describe.runIf(browserMode)("chat sidebar layout", () => {
|
||||
it("keeps long markdown scrollable inside a bounded sidebar", async () => {
|
||||
const container = document.createElement("div");
|
||||
container.className = "sidebar-column__panel";
|
||||
container.style.cssText = "display:flex;width:480px;height:320px;";
|
||||
|
||||
const panel = document.createElement("openclaw-chat-detail-panel") as DetailPanel;
|
||||
panel.className = "chat-sidebar";
|
||||
panel.content = {
|
||||
kind: "markdown",
|
||||
content: Array.from(
|
||||
{ length: 40 },
|
||||
(_, index) => `## Section ${index + 1}\n\nLong preview content for scrolling.`,
|
||||
).join("\n\n"),
|
||||
};
|
||||
container.append(panel);
|
||||
document.body.append(container);
|
||||
|
||||
try {
|
||||
await panel.updateComplete;
|
||||
const content = panel.querySelector<HTMLElement>(".sidebar-content");
|
||||
expect(content).not.toBeNull();
|
||||
expect(content!.clientHeight).toBeLessThan(content!.scrollHeight);
|
||||
|
||||
content!.scrollTop = content!.scrollHeight;
|
||||
await new Promise(requestAnimationFrame);
|
||||
expect(content!.scrollTop).toBeGreaterThan(0);
|
||||
} finally {
|
||||
container.remove();
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps long files scrollable inside CodeMirror", async () => {
|
||||
const container = document.createElement("div");
|
||||
container.className = "sidebar-column__panel";
|
||||
container.style.cssText = "display:flex;width:480px;height:320px;";
|
||||
|
||||
const panel = document.createElement("openclaw-chat-detail-panel") as DetailPanel;
|
||||
panel.className = "chat-sidebar";
|
||||
panel.content = {
|
||||
kind: "file",
|
||||
path: "src/long-example.ts",
|
||||
name: "long-example.ts",
|
||||
language: "typescript",
|
||||
content: Array.from(
|
||||
{ length: 200 },
|
||||
(_, index) => `export const value${index + 1} = ${index + 1};`,
|
||||
).join("\n"),
|
||||
};
|
||||
container.append(panel);
|
||||
document.body.append(container);
|
||||
|
||||
try {
|
||||
await panel.updateComplete;
|
||||
await expect
|
||||
.poll(() => panel.querySelector<HTMLElement>(".cm-scroller"), { timeout: 5_000 })
|
||||
.not.toBeNull();
|
||||
const scroller = panel.querySelector<HTMLElement>(".cm-scroller");
|
||||
expect(scroller).not.toBeNull();
|
||||
expect(scroller!.clientHeight).toBeLessThan(scroller!.scrollHeight);
|
||||
|
||||
scroller!.scrollTop = scroller!.scrollHeight;
|
||||
await new Promise(requestAnimationFrame);
|
||||
expect(scroller!.scrollTop).toBeGreaterThan(0);
|
||||
} finally {
|
||||
container.remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1280,8 +1280,12 @@ class ChatDetailPanel extends OpenClawLightDomElement {
|
||||
const currentMatchIndex = matches.length
|
||||
? Math.min(this.fileSearchMatchIndex, matches.length - 1)
|
||||
: 0;
|
||||
// Markdown previews and file editors need a bounded host wrapper so their
|
||||
// inner content can shrink and scroll. Content-sized kinds keep auto height.
|
||||
const fillHost =
|
||||
this.visibleContent?.kind === "file" || this.visibleContent?.kind === "markdown";
|
||||
return html`
|
||||
<div @click=${this.handlePanelClick}>
|
||||
<div class=${fillHost ? "sidebar-panel-host--fill" : ""} @click=${this.handlePanelClick}>
|
||||
${renderMarkdownSidebar({
|
||||
content: this.visibleContent,
|
||||
error: this.error,
|
||||
|
||||
@@ -1347,10 +1347,20 @@ openclaw-chat-sidebar-region,
|
||||
|
||||
.sidebar-content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
/* Full-height panel kinds need a bounded wrapper so their inner content can
|
||||
shrink and scroll instead of expanding past the rail. */
|
||||
.sidebar-panel-host--fill {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
openclaw-session-discussion {
|
||||
display: flex;
|
||||
flex: 1 1 0;
|
||||
|
||||
@@ -4040,6 +4040,7 @@ td.data-table-key-col {
|
||||
|
||||
.md-preview-dialog__body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
overscroll-behavior: contain;
|
||||
padding: clamp(18px, 3vw, 28px);
|
||||
|
||||
Reference in New Issue
Block a user