Skip to content

Commit 7e32d84

Browse files
committed
fix(ui): pin sidebar Settings entry to the fixed footer instead of the scrollable nav
1 parent f141408 commit 7e32d84

4 files changed

Lines changed: 7 additions & 30 deletions

File tree

ui/src/app-navigation-groups.test.ts

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,16 @@ import {
44
SETTINGS_NAVIGATION_ROUTES,
55
SIDEBAR_SECTIONS,
66
isSettingsNavigationRoute,
7-
isRouteInSidebarSection,
87
} from "./app-navigation.ts";
98
import { routeIdFromPath } from "./app-routes.ts";
109

1110
describe("SIDEBAR_SECTIONS", () => {
12-
it("collapses detailed settings slices into one sidebar entry", () => {
13-
const settings = SIDEBAR_SECTIONS.find((group) => group.label === "settings");
14-
expect(settings?.routes).toEqual(["config"]);
11+
it("keeps settings out of the scrollable sections; the footer pins it", () => {
12+
expect(SIDEBAR_SECTIONS.flatMap((group) => group.routes)).not.toContain("config");
1513
expect(SETTINGS_NAVIGATION_ROUTES.every((routeId) => isSettingsNavigationRoute(routeId))).toBe(
1614
true,
1715
);
16+
expect(isSettingsNavigationRoute("chat")).toBe(false);
1817
});
1918

2019
it("keeps channel management out of the primary control sidebar", () => {
@@ -31,18 +30,6 @@ describe("SIDEBAR_SECTIONS", () => {
3130
expect(SETTINGS_NAVIGATION_ROUTES).toContain("channels");
3231
});
3332

34-
it("keeps the settings group active for nested settings routes", () => {
35-
const settings = SIDEBAR_SECTIONS.find((group) => group.label === "settings");
36-
if (!settings) {
37-
throw new Error("Expected settings group");
38-
}
39-
40-
expect(isRouteInSidebarSection(settings, "appearance")).toBe(true);
41-
expect(isRouteInSidebarSection(settings, "channels")).toBe(true);
42-
expect(isRouteInSidebarSection(settings, "debug")).toBe(true);
43-
expect(isRouteInSidebarSection(settings, "chat")).toBe(false);
44-
});
45-
4633
it("routes every published settings slice", () => {
4734
expect(routeIdFromPath("/communications")).toBe("communications");
4835
expect(routeIdFromPath("/appearance")).toBe("appearance");

ui/src/app-navigation.test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ describe("plugin tabs route", () => {
268268

269269
describe("SIDEBAR_SECTIONS", () => {
270270
it("contains all expected groups", () => {
271-
expect(SIDEBAR_SECTIONS.map((g) => g.label)).toEqual(["chat", "control", "agent", "settings"]);
271+
expect(SIDEBAR_SECTIONS.map((g) => g.label)).toEqual(["chat", "control", "agent"]);
272272
});
273273

274274
it("all routes are unique", () => {
@@ -278,8 +278,7 @@ describe("SIDEBAR_SECTIONS", () => {
278278
});
279279

280280
it("keeps detailed settings slices routed but out of the root sidebar", () => {
281-
const settings = SIDEBAR_SECTIONS.find((group) => group.label === "settings");
282-
expect(settings?.routes).toEqual(["config"]);
281+
expect(SIDEBAR_SECTIONS.flatMap((g) => g.routes)).not.toContain("config");
283282
expect(SETTINGS_NAVIGATION_ROUTES).toEqual([
284283
"config",
285284
"channels",

ui/src/app-navigation.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ type NavigationItem = {
1414
[TRouteId in NavigationRouteId]: IconName;
1515
};
1616

17+
// Settings ("config") is not a scrollable section; the sidebar footer pins it.
1718
export const SIDEBAR_SECTIONS = [
1819
{ label: "chat", routes: ["chat"] },
1920
{
2021
label: "control",
2122
routes: ["overview", "activity", "workboard", "instances", "sessions", "usage", "cron"],
2223
},
2324
{ label: "agent", routes: ["agents", "skills", "skill-workshop", "nodes", "dreams"] },
24-
{ label: "settings", routes: ["config"] },
2525
] as const satisfies readonly SidebarSection[];
2626

2727
export const SETTINGS_NAVIGATION_ROUTES = [
@@ -68,16 +68,6 @@ export function isSettingsNavigationRoute(routeId: NavigationRouteId): boolean {
6868
return (SETTINGS_NAVIGATION_ROUTES as readonly NavigationRouteId[]).includes(routeId);
6969
}
7070

71-
export function isRouteInSidebarSection(
72-
section: SidebarSection,
73-
routeId: NavigationRouteId,
74-
): boolean {
75-
if (section.label === "settings") {
76-
return isSettingsNavigationRoute(routeId);
77-
}
78-
return section.routes.includes(routeId);
79-
}
80-
8171
export function navigationIconForRoute(routeId: NavigationRouteId): IconName {
8272
return NAVIGATION_ICONS[routeId] ?? "folder";
8373
}

ui/src/components/app-sidebar.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,7 @@ export class AppSidebar extends LitElement {
759759
</div>
760760
<div class="sidebar-shell__footer">
761761
<div class="sidebar-utility-group">
762+
${this.renderRoute("config")}
762763
${this.collapsed
763764
? html`
764765
<openclaw-tooltip

0 commit comments

Comments
 (0)