From 13e43d4c9fe1781b1e20e076c7d968f91b8f162f Mon Sep 17 00:00:00 2001 From: theAfish Date: Thu, 22 Oct 2026 11:35:17 +0800 Subject: [PATCH] feat: enhance sticky behavior for agent activity and step feed summaries --- .../src/features/graphs/StepExecutionFeed.js | 1 + .../src/features/session/VirtualTranscript.js | 4 +- web/vite-frontend/src/styles/chat.css | 48 ++++++++++++++++++- 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/web/vite-frontend/src/features/graphs/StepExecutionFeed.js b/web/vite-frontend/src/features/graphs/StepExecutionFeed.js index 3135b7f..2c8ecb8 100644 --- a/web/vite-frontend/src/features/graphs/StepExecutionFeed.js +++ b/web/vite-frontend/src/features/graphs/StepExecutionFeed.js @@ -591,6 +591,7 @@ export class StepExecutionFeed { const task = document.createElement("span"); task.className = "step-feed-task"; task.textContent = titleInfo.action; + task.title = titleInfo.action; title.appendChild(task); if (titleInfo.identifier) { const identity = document.createElement("span"); diff --git a/web/vite-frontend/src/features/session/VirtualTranscript.js b/web/vite-frontend/src/features/session/VirtualTranscript.js index 531d8fc..d646503 100644 --- a/web/vite-frontend/src/features/session/VirtualTranscript.js +++ b/web/vite-frontend/src/features/session/VirtualTranscript.js @@ -248,7 +248,9 @@ export class VirtualTranscript { const { element } = entry; element.dataset.index = String(virtualItem.index); element.dataset.transcriptRowId = row.id; - element.style.transform = `translateY(${virtualItem.start}px)`; + // Transforms also shift sticky descendants after their scroll constraint + // is calculated, pushing disclosure headings below the chat viewport. + element.style.top = `${virtualItem.start}px`; if (row.type === "gap") { element.style.height = `${virtualItem.size}px`; if (entry.revision !== row.revision) { diff --git a/web/vite-frontend/src/styles/chat.css b/web/vite-frontend/src/styles/chat.css index 48a0473..21e2f39 100644 --- a/web/vite-frontend/src/styles/chat.css +++ b/web/vite-frontend/src/styles/chat.css @@ -961,7 +961,6 @@ width: 100%; display: flow-root; contain: layout style; - will-change: transform; } .virtual-transcript-row.is-gap { @@ -1099,7 +1098,9 @@ --step-feed-header-bg: var(--step-feed-idle-bg); background: var(--agent-bg-1); border-color: var(--step-feed-idle-border); - overflow: hidden; + /* Clip rounded corners without creating a scroll container that traps + the expanded card's sticky summary. */ + overflow: clip; padding: 0; } @@ -1257,6 +1258,49 @@ align-items: flex-start; } +/* Keep collapse controls within reach while scrolling a long disclosure. + Sticky positioning naturally releases at the end of each details element. */ +.chat-area .agent-activity[open] > summary, +.chat-area .step-feed-details[open] > .step-feed-summary { + position: sticky; + /* Cancel the chat area's 16px top padding so scrolling text cannot peek + through a strip above the pinned heading. */ + top: -16px; + z-index: 2; + background-color: var(--agent-bg-1); + box-shadow: 0 1px 0 var(--border), 0 4px 10px rgb(0 0 0 / 8%); + border-radius: 7px 7px 0 0; + cursor: pointer; +} + +.chat-area .step-feed-details[open] > .step-feed-summary { + align-items: center; + gap: 10px; + padding: 10px 14px; + /* A quiet status tint over an opaque surface keeps body text from showing + through while preserving the card's success/running/error color. */ + background-image: linear-gradient( + color-mix(in srgb, var(--step-feed-header-bg) 55%, transparent), + color-mix(in srgb, var(--step-feed-header-bg) 55%, transparent) + ); +} + +.chat-area .step-feed-details[open] > .step-feed-summary .step-feed-meta { + padding: 3px 7px; + border: 1px solid var(--border); + border-radius: 6px; + background: var(--agent-bg-1); +} + +.chat-area .step-feed-details[open] > .step-feed-summary .step-feed-meta:empty { + display: none; +} + +.chat-area :is(.agent-activity, .step-feed-details)[open] > summary:focus-visible { + outline: 2px solid var(--accent); + outline-offset: -2px; +} + /* Keep the disclosure and status controls anchored to the header's visual centre when the title wraps after opening the card. */ .step-feed-details[open] > .step-feed-summary::before,