Skip to content

Commit 60cf7ea

Browse files
authored
feat(ui): make the sidebar session-first and minimal (#100386)
* feat(ui): bolder sidebar minimalism — pinned/chats session groups, compact icon footer, no brand header * test(ui): align fixtures with single default pinned route; document pinned-cap invariant * fix(ui): preserve sidebar session groups * fix(ui): polish minimal sidebar behavior * fix(ui): avoid duplicate pinned chat fallback * docs(changelog): note sidebar layout refresh * chore(i18n): preserve sidebar locale metadata * fix(ui): reuse localized sessions label * test(ui): follow localized sessions label * chore(ui): trim sidebar landing churn * docs(ui): update minimal sidebar guidance
1 parent b170c08 commit 60cf7ea

18 files changed

Lines changed: 332 additions & 452 deletions

docs/web/control-ui.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,9 @@ Appearance also has a browser-local Text size setting, stored with the rest of C
113113

114114
## Sidebar navigation
115115

116-
The sidebar keeps sessions first, followed by a small pinned destination set. **Overview**, **Workboard**, and **Agents** are pinned by default; expand **More** to reach every other destination. Select **Customize sidebar** under More, or right-click the navigation area, to pin or unpin destinations and restore the defaults. The pinned set and More expansion state are stored in the current browser profile and survive reloads.
116+
The sidebar keeps sessions first, split into **Pinned** and **Sessions** groups. Every pinned session stays visible, while unpinned sessions keep an independent nine-item recent budget. **Overview** is the only destination pinned by default; expand **More** to reach every other destination. Select **Customize sidebar** under More, or right-click the navigation area, to pin or unpin destinations and restore the defaults. The pinned set and More expansion state are stored in the current browser profile and survive reloads.
117117

118-
**Settings** stays available in the sidebar footer next to **Docs**. On desktop, use the topbar button next to the terminal control to collapse or expand the sidebar. At drawer breakpoints, the hamburger button replaces that control.
118+
The compact footer keeps connection status, **Settings**, **Docs**, and mobile pairing together. On desktop, use the topbar button next to the terminal control to collapse or expand the sidebar. At drawer breakpoints, the hamburger button replaces that control.
119119

120120
## What it can do (today)
121121

ui/src/app-navigation.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ export const SIDEBAR_NAV_ROUTES = [
2929

3030
export type SidebarNavRoute = (typeof SIDEBAR_NAV_ROUTES)[number];
3131

32+
// Sessions are the sidebar's core content; Overview is the only page pinned by
33+
// default. Users pin more via the customize menu.
3234
export const DEFAULT_SIDEBAR_PINNED_ROUTES = [
3335
"overview",
34-
"workboard",
35-
"agents",
3636
] as const satisfies readonly SidebarNavRoute[];
3737

3838
/**

ui/src/app/app-host.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,6 @@ class OpenClawShell extends LitElement {
306306
@state() private navCollapsed = false;
307307
@state() private sidebarPinnedRoutes: readonly SidebarNavRoute[] = [];
308308
@state() private sidebarMoreExpanded = false;
309-
@state() private recentSessionsCollapsed = false;
310309
@state() private navDrawerOpen = false;
311310
@state() private gatewayConnected = false;
312311
@state() private terminalAvailable = false;
@@ -599,7 +598,6 @@ class OpenClawShell extends LitElement {
599598
this.navCollapsed = snapshot.navCollapsed;
600599
this.sidebarPinnedRoutes = snapshot.sidebarPinnedRoutes;
601600
this.sidebarMoreExpanded = snapshot.sidebarMoreExpanded;
602-
this.recentSessionsCollapsed = snapshot.recentSessionsCollapsed;
603601
};
604602

605603
override render() {
@@ -673,18 +671,13 @@ class OpenClawShell extends LitElement {
673671
hasOperatorAdminAccess(context.gateway.snapshot.hello?.auth ?? null)}
674672
.sidebarPinnedRoutes=${this.sidebarPinnedRoutes}
675673
.sidebarMoreExpanded=${this.sidebarMoreExpanded}
676-
.recentSessionsCollapsed=${this.recentSessionsCollapsed}
677674
.themeMode=${context.theme.mode}
678675
.onToggleMore=${() =>
679676
context.navigation.update({
680677
sidebarMoreExpanded: !context.navigation.snapshot.sidebarMoreExpanded,
681678
})}
682679
.onUpdatePinnedRoutes=${(routes: SidebarNavRoute[]) =>
683680
context.navigation.update({ sidebarPinnedRoutes: routes })}
684-
.onToggleRecentSessions=${() =>
685-
context.navigation.update({
686-
recentSessionsCollapsed: !context.navigation.snapshot.recentSessionsCollapsed,
687-
})}
688681
.onPairMobile=${() => void context.overlays.openDevicePairSetup()}
689682
.onNavigate=${(routeId: string, options?: ApplicationNavigationOptions) =>
690683
this.navigate(routeId, options)}

ui/src/app/bootstrap.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,6 @@ function createApplicationNavigationPreferences(
171171
navCollapsed: settings.navCollapsed,
172172
sidebarPinnedRoutes: settings.sidebarPinnedRoutes,
173173
sidebarMoreExpanded: settings.sidebarMoreExpanded,
174-
recentSessionsCollapsed: settings.recentSessionsCollapsed ?? false,
175174
};
176175
const listeners = new Set<(next: ApplicationNavigationPreferencesSnapshot) => void>();
177176

@@ -183,7 +182,6 @@ function createApplicationNavigationPreferences(
183182
const nextSnapshot = { ...snapshot, ...patch };
184183
if (
185184
nextSnapshot.navCollapsed === snapshot.navCollapsed &&
186-
nextSnapshot.recentSessionsCollapsed === snapshot.recentSessionsCollapsed &&
187185
nextSnapshot.sidebarPinnedRoutes === snapshot.sidebarPinnedRoutes &&
188186
nextSnapshot.sidebarMoreExpanded === snapshot.sidebarMoreExpanded
189187
) {
@@ -193,7 +191,6 @@ function createApplicationNavigationPreferences(
193191
navCollapsed: nextSnapshot.navCollapsed,
194192
sidebarPinnedRoutes: [...nextSnapshot.sidebarPinnedRoutes],
195193
sidebarMoreExpanded: nextSnapshot.sidebarMoreExpanded,
196-
recentSessionsCollapsed: nextSnapshot.recentSessionsCollapsed,
197194
});
198195
snapshot = nextSnapshot;
199196
for (const listener of listeners) {

ui/src/app/context.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ export type ApplicationNavigationPreferencesSnapshot = {
3434
navCollapsed: boolean;
3535
sidebarPinnedRoutes: readonly SidebarNavRoute[];
3636
sidebarMoreExpanded: boolean;
37-
recentSessionsCollapsed: boolean;
3837
};
3938

4039
export type ApplicationNavigationPreferences = {

ui/src/app/settings.node.test.ts

Lines changed: 14 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,8 @@ describe("loadSettings default gateway URL derivation", () => {
146146
splitRatio: 0.6,
147147
navCollapsed: false,
148148
navWidth: 220,
149-
sidebarPinnedRoutes: ["overview", "workboard", "agents"],
149+
sidebarPinnedRoutes: ["overview"],
150150
sidebarMoreExpanded: false,
151-
recentSessionsCollapsed: false,
152151
borderRadius: 50,
153152
textScale: 100,
154153
sessionsByGateway: {
@@ -182,7 +181,7 @@ describe("loadSettings default gateway URL derivation", () => {
182181
splitRatio: 0.6,
183182
navCollapsed: false,
184183
navWidth: 220,
185-
sidebarPinnedRoutes: ["overview", "workboard", "agents"],
184+
sidebarPinnedRoutes: ["overview"],
186185
sidebarMoreExpanded: false,
187186
borderRadius: 50,
188187
textScale: 100,
@@ -215,7 +214,7 @@ describe("loadSettings default gateway URL derivation", () => {
215214
splitRatio: 0.6,
216215
navCollapsed: false,
217216
navWidth: 220,
218-
sidebarPinnedRoutes: ["overview", "workboard", "agents"],
217+
sidebarPinnedRoutes: ["overview"],
219218
sidebarMoreExpanded: false,
220219
borderRadius: 50,
221220
});
@@ -233,7 +232,7 @@ describe("loadSettings default gateway URL derivation", () => {
233232
splitRatio: 0.6,
234233
navCollapsed: false,
235234
navWidth: 220,
236-
sidebarPinnedRoutes: ["overview", "workboard", "agents"],
235+
sidebarPinnedRoutes: ["overview"],
237236
sidebarMoreExpanded: false,
238237
borderRadius: 50,
239238
});
@@ -263,7 +262,7 @@ describe("loadSettings default gateway URL derivation", () => {
263262
splitRatio: 0.6,
264263
navCollapsed: false,
265264
navWidth: 220,
266-
sidebarPinnedRoutes: ["overview", "workboard", "agents"],
265+
sidebarPinnedRoutes: ["overview"],
267266
sidebarMoreExpanded: false,
268267
borderRadius: 50,
269268
});
@@ -283,9 +282,8 @@ describe("loadSettings default gateway URL derivation", () => {
283282
splitRatio: 0.6,
284283
navCollapsed: false,
285284
navWidth: 220,
286-
sidebarPinnedRoutes: ["overview", "workboard", "agents"],
285+
sidebarPinnedRoutes: ["overview"],
287286
sidebarMoreExpanded: false,
288-
recentSessionsCollapsed: false,
289287
borderRadius: 50,
290288
textScale: 100,
291289
sessionsByGateway: {
@@ -298,65 +296,6 @@ describe("loadSettings default gateway URL derivation", () => {
298296
expect(sessionStorage.length).toBe(1);
299297
});
300298

301-
it("persists recent sessions collapse state across save and load", () => {
302-
setTestLocation({
303-
protocol: "https:",
304-
host: "gateway.example:8443",
305-
pathname: "/",
306-
});
307-
308-
const gwUrl = expectedGatewayUrl("");
309-
saveSettings({
310-
gatewayUrl: gwUrl,
311-
token: "",
312-
sessionKey: "main",
313-
lastActiveSessionKey: "main",
314-
theme: "claw",
315-
themeMode: "system",
316-
chatShowThinking: true,
317-
chatShowToolCalls: true,
318-
chatAutoScroll: "near-bottom",
319-
splitRatio: 0.6,
320-
navCollapsed: false,
321-
navWidth: 220,
322-
sidebarPinnedRoutes: ["overview", "workboard", "agents"],
323-
sidebarMoreExpanded: false,
324-
recentSessionsCollapsed: true,
325-
borderRadius: 50,
326-
textScale: 100,
327-
});
328-
329-
expect(loadSettings().recentSessionsCollapsed).toBe(true);
330-
331-
saveSettings({
332-
gatewayUrl: gwUrl,
333-
token: "",
334-
sessionKey: "main",
335-
lastActiveSessionKey: "main",
336-
theme: "claw",
337-
themeMode: "system",
338-
chatShowThinking: true,
339-
chatShowToolCalls: true,
340-
chatAutoScroll: "near-bottom",
341-
splitRatio: 0.6,
342-
navCollapsed: false,
343-
navWidth: 220,
344-
sidebarPinnedRoutes: ["overview", "workboard", "agents"],
345-
sidebarMoreExpanded: false,
346-
recentSessionsCollapsed: false,
347-
borderRadius: 50,
348-
textScale: 100,
349-
});
350-
351-
const scopedKey = `openclaw.control.settings.v1:${gwUrl}`;
352-
const persisted = JSON.parse(localStorage.getItem(scopedKey) ?? "{}") as Record<
353-
string,
354-
unknown
355-
>;
356-
expect(persisted.recentSessionsCollapsed).toBe(false);
357-
expect(loadSettings().recentSessionsCollapsed).toBe(false);
358-
});
359-
360299
it("persists sidebar customization across save and load, normalizing bad values", () => {
361300
setTestLocation({
362301
protocol: "https:",
@@ -380,7 +319,6 @@ describe("loadSettings default gateway URL derivation", () => {
380319
navWidth: 220,
381320
sidebarPinnedRoutes: ["sessions", "cron"],
382321
sidebarMoreExpanded: true,
383-
recentSessionsCollapsed: false,
384322
borderRadius: 50,
385323
textScale: 100,
386324
});
@@ -398,7 +336,7 @@ describe("loadSettings default gateway URL derivation", () => {
398336
persisted.sidebarMoreExpanded = "yes";
399337
localStorage.setItem(scopedKey, JSON.stringify(persisted));
400338

401-
expect(loadSettings().sidebarPinnedRoutes).toEqual(["overview", "workboard", "agents"]);
339+
expect(loadSettings().sidebarPinnedRoutes).toEqual(["overview"]);
402340
expect(loadSettings().sidebarMoreExpanded).toBe(false);
403341
});
404342

@@ -468,7 +406,7 @@ describe("loadSettings default gateway URL derivation", () => {
468406
splitRatio: 0.6,
469407
navCollapsed: false,
470408
navWidth: 220,
471-
sidebarPinnedRoutes: ["overview", "workboard", "agents"],
409+
sidebarPinnedRoutes: ["overview"],
472410
sidebarMoreExpanded: false,
473411
borderRadius: 50,
474412
});
@@ -484,7 +422,7 @@ describe("loadSettings default gateway URL derivation", () => {
484422
splitRatio: 0.6,
485423
navCollapsed: false,
486424
navWidth: 220,
487-
sidebarPinnedRoutes: ["overview", "workboard", "agents"],
425+
sidebarPinnedRoutes: ["overview"],
488426
sidebarMoreExpanded: false,
489427
borderRadius: 50,
490428
});
@@ -513,7 +451,7 @@ describe("loadSettings default gateway URL derivation", () => {
513451
splitRatio: 0.6,
514452
navCollapsed: false,
515453
navWidth: 320,
516-
sidebarPinnedRoutes: ["overview", "workboard", "agents"],
454+
sidebarPinnedRoutes: ["overview"],
517455
sidebarMoreExpanded: false,
518456
borderRadius: 50,
519457
});
@@ -549,7 +487,7 @@ describe("loadSettings default gateway URL derivation", () => {
549487
splitRatio: 0.6,
550488
navCollapsed: false,
551489
navWidth: 220,
552-
sidebarPinnedRoutes: ["overview", "workboard", "agents"],
490+
sidebarPinnedRoutes: ["overview"],
553491
sidebarMoreExpanded: false,
554492
borderRadius: 50,
555493
customTheme,
@@ -580,7 +518,7 @@ describe("loadSettings default gateway URL derivation", () => {
580518
splitRatio: 0.6,
581519
navCollapsed: false,
582520
navWidth: 220,
583-
sidebarPinnedRoutes: ["overview", "workboard", "agents"],
521+
sidebarPinnedRoutes: ["overview"],
584522
sidebarMoreExpanded: false,
585523
borderRadius: 50,
586524
customTheme: {
@@ -625,7 +563,7 @@ describe("loadSettings default gateway URL derivation", () => {
625563
splitRatio: 0.6,
626564
navCollapsed: false,
627565
navWidth: 220,
628-
sidebarPinnedRoutes: ["overview", "workboard", "agents"],
566+
sidebarPinnedRoutes: ["overview"],
629567
sidebarMoreExpanded: false,
630568
borderRadius: 50,
631569
});
@@ -669,7 +607,7 @@ describe("loadSettings default gateway URL derivation", () => {
669607
splitRatio: 0.6,
670608
navCollapsed: false,
671609
navWidth: 220,
672-
sidebarPinnedRoutes: ["overview", "workboard", "agents"],
610+
sidebarPinnedRoutes: ["overview"],
673611
sidebarMoreExpanded: false,
674612
borderRadius: 50,
675613
});

ui/src/app/settings.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ export type UiSettings = {
104104
navWidth: number; // Sidebar width when expanded (240–400px)
105105
sidebarPinnedRoutes: SidebarNavRoute[]; // Nav routes shown above the "More" section
106106
sidebarMoreExpanded: boolean; // Whether the sidebar "More" section is expanded
107-
recentSessionsCollapsed?: boolean; // Collapse recent sessions list in sidebar
108107
borderRadius: number; // Corner roundness (0–100, default 50)
109108
textScale?: TextScaleStop; // Browser-local text scale percentage
110109
customTheme?: ImportedCustomTheme;
@@ -467,7 +466,6 @@ export function loadSettings(): UiSettings {
467466
navWidth: 220,
468467
sidebarPinnedRoutes: [...DEFAULT_SIDEBAR_PINNED_ROUTES],
469468
sidebarMoreExpanded: false,
470-
recentSessionsCollapsed: false,
471469
borderRadius: 50,
472470
textScale: 100,
473471
};
@@ -530,10 +528,6 @@ export function loadSettings(): UiSettings {
530528
typeof parsed.sidebarMoreExpanded === "boolean"
531529
? parsed.sidebarMoreExpanded
532530
: defaults.sidebarMoreExpanded,
533-
recentSessionsCollapsed:
534-
typeof parsed.recentSessionsCollapsed === "boolean"
535-
? parsed.recentSessionsCollapsed
536-
: defaults.recentSessionsCollapsed,
537531
borderRadius:
538532
typeof parsed.borderRadius === "number" &&
539533
parsed.borderRadius >= 0 &&
@@ -637,7 +631,6 @@ function persistSettings(next: UiSettings) {
637631
navWidth: next.navWidth,
638632
sidebarPinnedRoutes: next.sidebarPinnedRoutes,
639633
sidebarMoreExpanded: next.sidebarMoreExpanded,
640-
recentSessionsCollapsed: next.recentSessionsCollapsed ?? false,
641634
borderRadius: next.borderRadius,
642635
textScale: normalizeTextScale(next.textScale),
643636
...(next.customTheme ? { customTheme: next.customTheme } : {}),

0 commit comments

Comments
 (0)