feat(FN-2649): add themed scrollbar styles in task detail modal

- Add token-based scrollbar styling for .detail-body across Firefox and WebKit pseudo-elements
- Scope agent log scrollbar rules to .detail-section--agent-log .agent-log-viewer-scroll to avoid selector target drift
- Add TaskDetailModal CSS regression tests that assert scrollbar declarations for modal body and agent log containers
- Introduce shared test helpers for robust selector/declaration matching in stylesheet assertions
This commit is contained in:
Fusion
2026-04-27 03:47:09 -07:00
committed by gsxdsm
parent f333c180f9
commit 0383613957
2 changed files with 68 additions and 1 deletions

View File

@@ -45,9 +45,28 @@
padding: 20px;
overflow-x: hidden;
overflow-y: auto;
scrollbar-color: var(--border) transparent;
scrollbar-width: thin;
flex: 1;
}
.detail-body::-webkit-scrollbar {
width: 6px;
}
.detail-body::-webkit-scrollbar-track {
background: transparent;
}
.detail-body::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: var(--radius-sm);
}
.detail-body::-webkit-scrollbar-thumb:hover {
background: var(--text-muted);
}
/* When the Agent Log tab is active, switch to flex layout so the log section
can stretch to fill the remaining space above the action bar.
The detail-body itself should NOT scroll; the agent-log-viewer scrolls instead. */
@@ -241,11 +260,13 @@
overflow: hidden;
}
.agent-log-viewer-scroll {
.detail-section--agent-log .agent-log-viewer-scroll {
flex: 1;
min-height: 0;
overflow-y: auto;
overflow-x: hidden;
scrollbar-color: var(--border) transparent;
scrollbar-width: thin;
font-family: var(--font-mono);
font-size: 13px;
line-height: 1.5;
@@ -256,6 +277,23 @@
overflow-wrap: break-word;
}
.detail-section--agent-log .agent-log-viewer-scroll::-webkit-scrollbar {
width: 6px;
}
.detail-section--agent-log .agent-log-viewer-scroll::-webkit-scrollbar-track {
background: transparent;
}
.detail-section--agent-log .agent-log-viewer-scroll::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: var(--radius-sm);
}
.detail-section--agent-log .agent-log-viewer-scroll::-webkit-scrollbar-thumb:hover {
background: var(--text-muted);
}
.agent-log-model-header {
display: flex;
flex-wrap: wrap;

View File

@@ -104,6 +104,15 @@ function getCssRuleBlock(css: string, selector: string): string {
return ruleMatch?.[1] ?? "";
}
function escapeRegExp(value: string): string {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function expectBaseRule(css: string, selector: string, declaration: string): void {
const pattern = new RegExp(`${escapeRegExp(selector)}\\s*\\{[^}]*${escapeRegExp(declaration)}`);
expect(pattern.test(css)).toBe(true);
}
function readDashboardStylesSource(): string {
return loadAllAppCss();
}
@@ -119,6 +128,26 @@ describe("TaskDetailModal", () => {
localStorage.removeItem("fn.authToken");
});
it("styles detail-body scrollbar rules", () => {
const css = loadAllAppCss();
expectBaseRule(css, ".detail-body", "scrollbar-color: var(--border) transparent;");
expectBaseRule(css, ".detail-body", "scrollbar-width: thin;");
expectBaseRule(css, ".detail-body::-webkit-scrollbar", "width: 6px;");
expectBaseRule(css, ".detail-body::-webkit-scrollbar-track", "background: transparent;");
expectBaseRule(css, ".detail-body::-webkit-scrollbar-thumb", "background: var(--border);");
expectBaseRule(css, ".detail-body::-webkit-scrollbar-thumb:hover", "background: var(--text-muted);");
});
it("styles agent log viewer scroll container scrollbar rules", () => {
const css = loadAllAppCss();
expectBaseRule(css, ".detail-section--agent-log .agent-log-viewer-scroll", "scrollbar-color: var(--border) transparent;");
expectBaseRule(css, ".detail-section--agent-log .agent-log-viewer-scroll", "scrollbar-width: thin;");
expectBaseRule(css, ".detail-section--agent-log .agent-log-viewer-scroll::-webkit-scrollbar", "width: 6px;");
expectBaseRule(css, ".detail-section--agent-log .agent-log-viewer-scroll::-webkit-scrollbar-thumb", "background: var(--border);");
});
it("renders markdown-body without detail-prompt class when prompt exists", () => {
const { container } = render(
<TaskDetailModal