diff --git a/frontend/src/renderer/components/GlobalSettingsForm.tsx b/frontend/src/renderer/components/GlobalSettingsForm.tsx
new file mode 100644
index 000000000..89e280e93
--- /dev/null
+++ b/frontend/src/renderer/components/GlobalSettingsForm.tsx
@@ -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 (
+
+ );
+}
diff --git a/frontend/src/renderer/components/Sidebar.test.tsx b/frontend/src/renderer/components/Sidebar.test.tsx
index 9b805e4e8..c900aab4e 100644
--- a/frontend/src/renderer/components/Sidebar.test.tsx
+++ b/frontend/src/renderer/components/Sidebar.test.tsx
@@ -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();
diff --git a/frontend/src/renderer/components/Sidebar.tsx b/frontend/src/renderer/components/Sidebar.tsx
index 83e9a5c41..e321619ed 100644
--- a/frontend/src/renderer/components/Sidebar.tsx
+++ b/frontend/src/renderer/components/Sidebar.tsx
@@ -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
⌘K
- {selection.activeProjectId && (
- <>
-
- selection.goSettings(selection.activeProjectId!)}>
-
- Project settings
-
- >
+
+ {selection.activeProjectId ? (
+ selection.goSettings(selection.activeProjectId!)}>
+
+ Project settings
+
+ ) : (
+
+
+ Global settings
+
)}
@@ -342,14 +346,17 @@ export function Sidebar({
Search
⌘K
- {selection.activeProjectId && (
- <>
-
- selection.goSettings(selection.activeProjectId!)}>
-
- Project settings
-
- >
+
+ {selection.activeProjectId ? (
+ selection.goSettings(selection.activeProjectId!)}>
+
+ Project settings
+
+ ) : (
+
+
+ Global settings
+
)}
diff --git a/frontend/src/renderer/routeTree.gen.ts b/frontend/src/renderer/routeTree.gen.ts
index ffe4cdc41..26cb7f148 100644
--- a/frontend/src/renderer/routeTree.gen.ts
+++ b/frontend/src/renderer/routeTree.gen.ts
@@ -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,
diff --git a/frontend/src/renderer/routes/_shell.settings.tsx b/frontend/src/renderer/routes/_shell.settings.tsx
new file mode 100644
index 000000000..4d07584ec
--- /dev/null
+++ b/frontend/src/renderer/routes/_shell.settings.tsx
@@ -0,0 +1,6 @@
+import { createFileRoute } from "@tanstack/react-router";
+import { GlobalSettingsForm } from "../components/GlobalSettingsForm";
+
+export const Route = createFileRoute("/_shell/settings")({
+ component: GlobalSettingsForm,
+});