Skip to content

Commit 26ac731

Browse files
fix(ui): scope sidebar session active state to chat
1 parent 6799b5b commit 26ac731

5 files changed

Lines changed: 79 additions & 18 deletions

File tree

ui/src/components/app-sidebar.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ type SidebarRecentSession = {
7070
meta: string;
7171
href: string;
7272
active: boolean;
73+
visuallyActive: boolean;
7374
hasActiveRun: boolean;
7475
kind?: string;
7576
pinned: boolean;
@@ -316,12 +317,14 @@ export class AppSidebar extends LitElement {
316317
hello: context?.gateway.snapshot.hello,
317318
compareSessions: this.compareSidebarSessionRows,
318319
});
320+
const highlightCurrentSession = this.activeRouteId === "chat";
319321
const toSidebarSession = (row: SessionsListResult["sessions"][number]) => ({
320322
key: row.key,
321323
label: resolveSessionDisplayName(row.key, row),
322324
meta: row.updatedAt ? formatRelativeTimestamp(row.updatedAt) : "",
323325
href: `${pathForRoute("chat", context?.basePath ?? "")}${searchForSession(row.key)}`,
324326
active: row.key === navigation.activeRowKey,
327+
visuallyActive: highlightCurrentSession && row.key === navigation.currentSessionKey,
325328
hasActiveRun: Boolean(row.hasActiveRun),
326329
kind: row.kind,
327330
pinned: row.pinned === true,
@@ -1206,7 +1209,7 @@ export class AppSidebar extends LitElement {
12061209
const rowClass = [
12071210
"sidebar-recent-session",
12081211
"session-row-host",
1209-
session.active ? "sidebar-recent-session--active" : "",
1212+
session.visuallyActive ? "sidebar-recent-session--active" : "",
12101213
session.pinned ? "session-row-host--pinned" : "",
12111214
session.hasActiveRun ? "session-row-host--running" : "",
12121215
this.draggingSessionKey === session.key ? "sidebar-recent-session--dragging" : "",

ui/src/e2e/session-management.e2e.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,4 +530,49 @@ describeControlUiE2e("Control UI session management mocked Gateway E2E", () => {
530530
await context.close();
531531
}
532532
});
533+
534+
it("only paints a sidebar session active on chat routes", async () => {
535+
const baseTime = Date.parse("2026-07-01T16:00:00.000Z");
536+
const context = await browser.newContext({
537+
locale: "en-US",
538+
serviceWorkers: "block",
539+
viewport: { height: 900, width: 1280 },
540+
});
541+
const page = await context.newPage();
542+
await installMockGateway(page, {
543+
methodResponses: {
544+
"sessions.list": sessionsListResponse([
545+
sessionRow("agent:main:main", "Main", baseTime),
546+
sessionRow("agent:main:release", "Release planning", baseTime - 60_000),
547+
]),
548+
},
549+
sessionKey: "agent:main:main",
550+
});
551+
552+
try {
553+
await page.goto(`${server.baseUrl}overview`);
554+
const activeRows = page.locator(".sidebar-recent-session--active");
555+
const mainRow = page.locator('.sidebar-recent-session[data-session-key="agent:main:main"]');
556+
await mainRow.waitFor({ state: "visible", timeout: 10_000 });
557+
await expect.poll(() => activeRows.count()).toBe(0);
558+
559+
await page.goto(`${server.baseUrl}config`);
560+
await mainRow.waitFor({ state: "visible", timeout: 10_000 });
561+
await expect.poll(() => activeRows.count()).toBe(0);
562+
563+
await page.goto(`${server.baseUrl}chat?session=agent%3Amain%3Amain`);
564+
await expect.poll(() => activeRows.count()).toBe(1);
565+
await expect
566+
.poll(() => activeRows.first().getAttribute("data-session-key"))
567+
.toBe("agent:main:main");
568+
569+
await page.goto(`${server.baseUrl}chat`);
570+
await expect.poll(() => activeRows.count()).toBe(1);
571+
await expect
572+
.poll(() => activeRows.first().getAttribute("data-session-key"))
573+
.toBe("agent:main:main");
574+
} finally {
575+
await context.close();
576+
}
577+
});
533578
});

ui/src/pages/overview/cards.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Control UI view renders overview cards screen content.
22
import { asDateTimestampMs } from "@openclaw/normalization-core/number-coercion";
33
import { html, nothing, type TemplateResult } from "lit";
4-
import { unsafeHTML } from "lit/directives/unsafe-html.js";
54
import type {
65
SessionsUsageResult,
76
SessionsListResult,
@@ -34,14 +33,6 @@ type OverviewCardsProps = {
3433
canNavigate: (routeId: NavigationRouteId) => boolean;
3534
};
3635

37-
const DIGIT_RUN = /\d{3,}/g;
38-
39-
function blurDigits(value: string): TemplateResult {
40-
const escaped = value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
41-
const blurred = escaped.replace(DIGIT_RUN, (m) => `<span class="blur-digits">${m}</span>`);
42-
return html`${unsafeHTML(blurred)}`;
43-
}
44-
4536
type StatCard = {
4637
kind: string;
4738
routeId: NavigationRouteId;
@@ -295,9 +286,7 @@ export function renderOverviewCards(props: OverviewCardsProps) {
295286
${sessions.map(
296287
(s) => html`
297288
<li class="ov-recent__row">
298-
<span class="ov-recent__key"
299-
>${blurDigits(resolveSessionDisplayName(s.key, s))}</span
300-
>
289+
<span class="ov-recent__key">${resolveSessionDisplayName(s.key, s)}</span>
301290
<span class="ov-recent__model">${s.model ?? ""}</span>
302291
<span class="ov-recent__time"
303292
>${s.updatedAt ? formatRelativeTimestamp(s.updatedAt) : ""}</span

ui/src/pages/overview/view.render.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,35 @@ describe("overview view rendering", () => {
140140
expect(recentNames).not.toContain("telegram:123:456");
141141
});
142142

143+
it("does not blur non-sensitive digits in recent session display names", async () => {
144+
const container = document.createElement("div");
145+
const props = createOverviewProps({
146+
sessionsResult: {
147+
ts: 0,
148+
path: "",
149+
count: 1,
150+
defaults: { modelProvider: "openai", model: "gpt-5", contextTokens: null },
151+
sessions: [
152+
{
153+
key: "agent:main:build-review",
154+
kind: "direct",
155+
label: "Build 20260704 review",
156+
model: "gpt-5",
157+
updatedAt: null,
158+
},
159+
],
160+
},
161+
});
162+
163+
render(renderOverview(props), container);
164+
await Promise.resolve();
165+
166+
expect(container.querySelector(".ov-recent__key")?.textContent?.trim()).toBe(
167+
"Build 20260704 review",
168+
);
169+
expect(container.querySelector(".blur-digits")).toBeNull();
170+
});
171+
143172
it("promotes provider quota into a dedicated overview card", async () => {
144173
const container = document.createElement("div");
145174
const props = createOverviewProps({

ui/src/styles/components.css

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5601,11 +5601,6 @@ td.data-table-key-col {
56015601
white-space: nowrap;
56025602
}
56035603

5604-
.blur-digits {
5605-
filter: blur(4px);
5606-
user-select: none;
5607-
}
5608-
56095604
/* Section divider */
56105605
.ov-section-divider {
56115606
border-top: 1px solid var(--border);

0 commit comments

Comments
 (0)