fix(web): 修复中等宽度下聊天栏信息重叠 (#151)

Co-authored-by: Yaowei Zheng <hiyouga@buaa.edu.cn>
This commit is contained in:
Yiru Dong
2026-08-07 17:19:03 +08:00
committed by GitHub
parent a6e54488b8
commit 9745773cb9
4 changed files with 34 additions and 6 deletions
@@ -0,0 +1,3 @@
# Chat toolbar actions stay clear of live statistics
At medium desktop widths, the pinned sidebar leaves the chat toolbar substantially narrower than the viewport breakpoint suggests. The Agents panel and Workspace actions now remain icon-only until the large-screen breakpoint, preserving space between the running indicator and the live Token, cost, and elapsed-time statistics; their labels return on wider screens, while titles and accessible names keep the compact buttons identifiable. A browser regression test reproduces the approximately 2:1 viewport from issue #150 and verifies that the running status and Token total never intersect.
+2
View File
@@ -1,5 +1,7 @@
# Unreleased
- [2026-08-03] Web App: medium-width chat toolbars keep the running indicator and live statistics separate by collapsing the Agents panel and Workspace actions to accessible icon-only buttons until the large-screen breakpoint. ([details](2026-08-03-chat-toolbar-layout.md))
- [2026-08-06] Desktop app: penguin brand icons on every platform, task-completion system notifications (renderer-only, desktop sessions), explicit single-user mode (user/member management rejected with `desktop_single_user` and hidden), and a bundled `penguin` CLI on PATH (automatic on deb; menu-driven install elsewhere, no system Node needed). ([details](2026-08-06-desktop-app.md))
- [2026-08-06] Admin "use system HTTP proxy" switch: server-wide proxy control (default on, live toggle, loopback exemption), off-state proxy-env stripping for agent subprocesses, OS-proxy injection on desktop. ([details](2026-08-06-system-proxy-switch.md))
+21
View File
@@ -67,6 +67,27 @@ test("chat + tool approval + stats/cost/copy + traces + files", async ({ page })
const headerElapsed = page.locator('div.hidden span[title="用时"]');
const elapsedBefore = await headerElapsed.textContent();
await expect(headerElapsed).not.toHaveText(elapsedBefore);
// Issue #150: at an approximately 2:1 browser size the pinned sidebar leaves the chat
// toolbar much narrower than the viewport breakpoint suggests. The running label used to
// stay expanded and the shrinkable stats row then painted its Token chip over that label.
// Keep the status dot and the live stats, but their rendered boxes must remain disjoint.
await page.setViewportSize({ width: 877, height: 438 });
await page.waitForTimeout(200);
const runningStatus = page.locator('span[title="运行中"]').filter({ hasText: "运行中" }).first();
const tokenTotal = page.locator('span[title="Token 累计(Token)"]');
const [runningBox, tokenBox] = await Promise.all([
runningStatus.boundingBox(),
tokenTotal.boundingBox(),
]);
expect(runningBox, "running status is rendered @877").not.toBeNull();
expect(tokenBox, "Token total is rendered @877").not.toBeNull();
const overlap =
Math.min(runningBox.x + runningBox.width, tokenBox.x + tokenBox.width) -
Math.max(runningBox.x, tokenBox.x);
expect(overlap, "running status and Token total do not overlap @877").toBeLessThanOrEqual(0);
await page.setViewportSize({ width: 1280, height: 720 });
await page.waitForTimeout(200);
// The user takes control of the running work group (toggle = userToggled), keeps it open, and
// opens the exec_command card to watch the arguments. Both must survive the end of the turn.
//
+8 -6
View File
@@ -1103,9 +1103,10 @@ export function ChatPage() {
<circle cx="19" cy="18.5" r="2.5" />
<path d="M7.4 11 16.7 6.6M7.4 13l9.3 4.4" />
</svg>
{/* Below sm the button is icon-only (title/aria keep the name), same rule as the
workspace button next to it: the label ate the title's room on phones. */}
<span className="hidden sm:inline">{S.chat.openAgents}</span>
{/* At md widths the pinned sidebar leaves less room than the viewport breakpoint
suggests. Keep both panel actions icon-only until lg so the running status and
live stats retain their own layout space. */}
<span className="hidden lg:inline">{S.chat.openAgents}</span>
{/* A pending approval inside a subagent: amber dot (the chip in the stream carries the accessible announcement). */}
{anySubagentPending && (
<span aria-hidden className="h-1.5 w-1.5 rounded-full bg-amber-500" />
@@ -1136,9 +1137,10 @@ export function ChatPage() {
>
<path d={STAT_ICONS.folder} />
</svg>
{/* Below sm the button is icon-only (title/aria keep the name): the label plus the
running indicator squeezed the session title to nothing on phones. */}
<span className="hidden sm:inline">{S.chat.openWorkspace}</span>
{/* Below lg the button is icon-only (title/aria keep the name): between md and lg
the pinned sidebar makes the chat toolbar substantially narrower than the
viewport, so the action labels would squeeze the status into the Token stats. */}
<span className="hidden lg:inline">{S.chat.openWorkspace}</span>
</button>
{/* Conversation index fallback: exactly when the gutter tick rail can't show