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", () => {
renderSidebar();

View File

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

View File

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