feat(frontend): add blank Global Settings page (#2218)

* feat(frontend): add blank Global Settings page

Add a Global Settings page to the desktop renderer, reachable from the
sidebar Settings menu when no project is selected. The project-scoped
Settings entry stays when a project is active; otherwise the menu shows
Global settings, which opens the new /settings route. The page body is
intentionally empty for now and will be filled in incrementally.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(frontend): regenerate route tree + test Global Settings menu

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Harshit Singh Bhandari 2026-06-26 21:07:42 +05:30 committed by GitHub
parent bc84b6d2a3
commit 14030772b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 72 additions and 16 deletions

View File

@ -0,0 +1,12 @@
import { DashboardSubhead } from "./DashboardSubhead";
// App-wide settings, shown from the sidebar when no project is selected. The
// body is intentionally empty for now; it will be filled in incrementally.
export function GlobalSettingsForm() {
return (
<div className="flex h-full min-h-0 flex-col bg-background text-foreground">
<DashboardSubhead title="Global settings" subtitle="Settings that apply across all projects" />
<div className="min-h-0 flex-1 overflow-y-auto p-[18px]" />
</div>
);
}

View File

@ -135,6 +135,16 @@ describe("Sidebar", () => {
); );
}); });
it("opens global settings from the footer menu when no project is selected", async () => {
const user = userEvent.setup();
renderSidebar();
await user.click(screen.getAllByLabelText("Settings")[0]);
await user.click(await screen.findByRole("menuitem", { name: "Global settings" }));
expect(navigateMock).toHaveBeenCalledWith({ to: "/settings" });
});
it("always shows action icons and reserves padding for them", () => { it("always shows action icons and reserves padding for them", () => {
renderSidebar(); renderSidebar();

View File

@ -91,6 +91,7 @@ function useSelection() {
activeSessionId: params.sessionId, activeSessionId: params.sessionId,
goHome: () => void navigate({ to: "/" }), goHome: () => void navigate({ to: "/" }),
goPrs: () => void navigate({ to: "/prs" }), goPrs: () => void navigate({ to: "/prs" }),
goGlobalSettings: () => void navigate({ to: "/settings" }),
goSettings: (projectId: string) => void navigate({ to: "/projects/$projectId/settings", params: { projectId } }), goSettings: (projectId: string) => void navigate({ to: "/projects/$projectId/settings", params: { projectId } }),
goProject: (projectId: string) => void navigate({ to: "/projects/$projectId", params: { projectId } }), goProject: (projectId: string) => void navigate({ to: "/projects/$projectId", params: { projectId } }),
goSession: (projectId: string, sessionId: string) => goSession: (projectId: string, sessionId: string) =>
@ -284,14 +285,17 @@ export function Sidebar({
Search Search
<DropdownMenuShortcut>K</DropdownMenuShortcut> <DropdownMenuShortcut>K</DropdownMenuShortcut>
</DropdownMenuItem> </DropdownMenuItem>
{selection.activeProjectId && ( <DropdownMenuSeparator />
<> {selection.activeProjectId ? (
<DropdownMenuSeparator /> <DropdownMenuItem onSelect={() => selection.goSettings(selection.activeProjectId!)}>
<DropdownMenuItem onSelect={() => selection.goSettings(selection.activeProjectId!)}> <Settings aria-hidden="true" />
<Settings aria-hidden="true" /> Project settings
Project settings </DropdownMenuItem>
</DropdownMenuItem> ) : (
</> <DropdownMenuItem onSelect={selection.goGlobalSettings}>
<Settings aria-hidden="true" />
Global settings
</DropdownMenuItem>
)} )}
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
@ -342,14 +346,17 @@ export function Sidebar({
Search Search
<DropdownMenuShortcut>K</DropdownMenuShortcut> <DropdownMenuShortcut>K</DropdownMenuShortcut>
</DropdownMenuItem> </DropdownMenuItem>
{selection.activeProjectId && ( <DropdownMenuSeparator />
<> {selection.activeProjectId ? (
<DropdownMenuSeparator /> <DropdownMenuItem onSelect={() => selection.goSettings(selection.activeProjectId!)}>
<DropdownMenuItem onSelect={() => selection.goSettings(selection.activeProjectId!)}> <Settings aria-hidden="true" />
<Settings aria-hidden="true" /> Project settings
Project settings </DropdownMenuItem>
</DropdownMenuItem> ) : (
</> <DropdownMenuItem onSelect={selection.goGlobalSettings}>
<Settings aria-hidden="true" />
Global settings
</DropdownMenuItem>
)} )}
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>

View File

@ -11,6 +11,7 @@
import { Route as rootRouteImport } from "./routes/__root"; import { Route as rootRouteImport } from "./routes/__root";
import { Route as ShellRouteImport } from "./routes/_shell"; import { Route as ShellRouteImport } from "./routes/_shell";
import { Route as ShellIndexRouteImport } from "./routes/_shell.index"; import { Route as ShellIndexRouteImport } from "./routes/_shell.index";
import { Route as ShellSettingsRouteImport } from "./routes/_shell.settings";
import { Route as ShellPrsRouteImport } from "./routes/_shell.prs"; import { Route as ShellPrsRouteImport } from "./routes/_shell.prs";
import { Route as ShellSessionsSessionIdRouteImport } from "./routes/_shell.sessions.$sessionId"; import { Route as ShellSessionsSessionIdRouteImport } from "./routes/_shell.sessions.$sessionId";
import { Route as ShellProjectsProjectIdRouteImport } from "./routes/_shell.projects.$projectId"; import { Route as ShellProjectsProjectIdRouteImport } from "./routes/_shell.projects.$projectId";
@ -26,6 +27,11 @@ const ShellIndexRoute = ShellIndexRouteImport.update({
path: "/", path: "/",
getParentRoute: () => ShellRoute, getParentRoute: () => ShellRoute,
} as any); } as any);
const ShellSettingsRoute = ShellSettingsRouteImport.update({
id: "/settings",
path: "/settings",
getParentRoute: () => ShellRoute,
} as any);
const ShellPrsRoute = ShellPrsRouteImport.update({ const ShellPrsRoute = ShellPrsRouteImport.update({
id: "/prs", id: "/prs",
path: "/prs", path: "/prs",
@ -55,6 +61,7 @@ const ShellProjectsProjectIdSessionsSessionIdRoute = ShellProjectsProjectIdSessi
export interface FileRoutesByFullPath { export interface FileRoutesByFullPath {
"/": typeof ShellIndexRoute; "/": typeof ShellIndexRoute;
"/prs": typeof ShellPrsRoute; "/prs": typeof ShellPrsRoute;
"/settings": typeof ShellSettingsRoute;
"/projects/$projectId": typeof ShellProjectsProjectIdRoute; "/projects/$projectId": typeof ShellProjectsProjectIdRoute;
"/sessions/$sessionId": typeof ShellSessionsSessionIdRoute; "/sessions/$sessionId": typeof ShellSessionsSessionIdRoute;
"/projects/$projectId/settings": typeof ShellProjectsProjectIdSettingsRoute; "/projects/$projectId/settings": typeof ShellProjectsProjectIdSettingsRoute;
@ -62,6 +69,7 @@ export interface FileRoutesByFullPath {
} }
export interface FileRoutesByTo { export interface FileRoutesByTo {
"/prs": typeof ShellPrsRoute; "/prs": typeof ShellPrsRoute;
"/settings": typeof ShellSettingsRoute;
"/": typeof ShellIndexRoute; "/": typeof ShellIndexRoute;
"/projects/$projectId": typeof ShellProjectsProjectIdRoute; "/projects/$projectId": typeof ShellProjectsProjectIdRoute;
"/sessions/$sessionId": typeof ShellSessionsSessionIdRoute; "/sessions/$sessionId": typeof ShellSessionsSessionIdRoute;
@ -72,6 +80,7 @@ export interface FileRoutesById {
__root__: typeof rootRouteImport; __root__: typeof rootRouteImport;
"/_shell": typeof ShellRouteWithChildren; "/_shell": typeof ShellRouteWithChildren;
"/_shell/prs": typeof ShellPrsRoute; "/_shell/prs": typeof ShellPrsRoute;
"/_shell/settings": typeof ShellSettingsRoute;
"/_shell/": typeof ShellIndexRoute; "/_shell/": typeof ShellIndexRoute;
"/_shell/projects/$projectId": typeof ShellProjectsProjectIdRoute; "/_shell/projects/$projectId": typeof ShellProjectsProjectIdRoute;
"/_shell/sessions/$sessionId": typeof ShellSessionsSessionIdRoute; "/_shell/sessions/$sessionId": typeof ShellSessionsSessionIdRoute;
@ -83,6 +92,7 @@ export interface FileRouteTypes {
fullPaths: fullPaths:
| "/" | "/"
| "/prs" | "/prs"
| "/settings"
| "/projects/$projectId" | "/projects/$projectId"
| "/sessions/$sessionId" | "/sessions/$sessionId"
| "/projects/$projectId/settings" | "/projects/$projectId/settings"
@ -90,6 +100,7 @@ export interface FileRouteTypes {
fileRoutesByTo: FileRoutesByTo; fileRoutesByTo: FileRoutesByTo;
to: to:
| "/prs" | "/prs"
| "/settings"
| "/" | "/"
| "/projects/$projectId" | "/projects/$projectId"
| "/sessions/$sessionId" | "/sessions/$sessionId"
@ -99,6 +110,7 @@ export interface FileRouteTypes {
| "__root__" | "__root__"
| "/_shell" | "/_shell"
| "/_shell/prs" | "/_shell/prs"
| "/_shell/settings"
| "/_shell/" | "/_shell/"
| "/_shell/projects/$projectId" | "/_shell/projects/$projectId"
| "/_shell/sessions/$sessionId" | "/_shell/sessions/$sessionId"
@ -126,6 +138,13 @@ declare module "@tanstack/react-router" {
preLoaderRoute: typeof ShellIndexRouteImport; preLoaderRoute: typeof ShellIndexRouteImport;
parentRoute: typeof ShellRoute; parentRoute: typeof ShellRoute;
}; };
"/_shell/settings": {
id: "/_shell/settings";
path: "/settings";
fullPath: "/settings";
preLoaderRoute: typeof ShellSettingsRouteImport;
parentRoute: typeof ShellRoute;
};
"/_shell/prs": { "/_shell/prs": {
id: "/_shell/prs"; id: "/_shell/prs";
path: "/prs"; path: "/prs";
@ -166,6 +185,7 @@ declare module "@tanstack/react-router" {
interface ShellRouteChildren { interface ShellRouteChildren {
ShellPrsRoute: typeof ShellPrsRoute; ShellPrsRoute: typeof ShellPrsRoute;
ShellSettingsRoute: typeof ShellSettingsRoute;
ShellIndexRoute: typeof ShellIndexRoute; ShellIndexRoute: typeof ShellIndexRoute;
ShellProjectsProjectIdRoute: typeof ShellProjectsProjectIdRoute; ShellProjectsProjectIdRoute: typeof ShellProjectsProjectIdRoute;
ShellSessionsSessionIdRoute: typeof ShellSessionsSessionIdRoute; ShellSessionsSessionIdRoute: typeof ShellSessionsSessionIdRoute;
@ -175,6 +195,7 @@ interface ShellRouteChildren {
const ShellRouteChildren: ShellRouteChildren = { const ShellRouteChildren: ShellRouteChildren = {
ShellPrsRoute: ShellPrsRoute, ShellPrsRoute: ShellPrsRoute,
ShellSettingsRoute: ShellSettingsRoute,
ShellIndexRoute: ShellIndexRoute, ShellIndexRoute: ShellIndexRoute,
ShellProjectsProjectIdRoute: ShellProjectsProjectIdRoute, ShellProjectsProjectIdRoute: ShellProjectsProjectIdRoute,
ShellSessionsSessionIdRoute: ShellSessionsSessionIdRoute, ShellSessionsSessionIdRoute: ShellSessionsSessionIdRoute,

View File

@ -0,0 +1,6 @@
import { createFileRoute } from "@tanstack/react-router";
import { GlobalSettingsForm } from "../components/GlobalSettingsForm";
export const Route = createFileRoute("/_shell/settings")({
component: GlobalSettingsForm,
});