From 7de2613daa53cd6b99dfce6285059bb7d2a831fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Catriel=20M=C3=BCller?= Date: Mon, 1 Jun 2026 12:09:06 -0300 Subject: [PATCH] fix: subpath navigation --- .../src/components/app-sidebar/AppSidebar.tsx | 9 +++--- .../src/context/ConfigProvider.tsx | 4 ++- .../src/routes/config/AgentsRoute.tsx | 10 ++----- .../src/routes/config/ConfigSidebar.tsx | 12 ++++---- .../src/shared/navigation.test.ts | 19 +++++++++++++ .../kilo-console/src/shared/navigation.ts | 28 ++++++++++++++++--- 6 files changed, 58 insertions(+), 24 deletions(-) create mode 100644 packages/kilo-console/src/shared/navigation.test.ts diff --git a/packages/kilo-console/src/components/app-sidebar/AppSidebar.tsx b/packages/kilo-console/src/components/app-sidebar/AppSidebar.tsx index 11182374a4..6f3e07565a 100644 --- a/packages/kilo-console/src/components/app-sidebar/AppSidebar.tsx +++ b/packages/kilo-console/src/components/app-sidebar/AppSidebar.tsx @@ -14,7 +14,7 @@ import { type RecentProjectItem, type ProjectQuery, } from "../../client" -import { type Path } from "../../shared/navigation" +import { strip, type Path } from "../../shared/navigation" import { clean, friendly } from "../../shared/utils" import { projectStatus, @@ -111,6 +111,7 @@ function Glyph(props: { name: "projects" | "settings" | "profile" }) { export function AppSidebar(props: Props) { const loc = useLocation() const params = createMemo(() => new URLSearchParams(loc.search)) + const route = createMemo(() => strip(loc.pathname)) const discoverable = () => shouldDiscover(params()) const fallback = () => base(params()) const [url, setUrl] = createSignal(fallback()) @@ -126,14 +127,14 @@ export function AppSidebar(props: Props) { // project currently rendered by ProjectConsoleRoute — it owns unread tracking for its terminals const activeProject = createMemo(() => { - const match = loc.pathname.match(/^\/projects\/([^/]+)/) + const match = route().match(/^\/projects\/([^/]+)/) return match ? decodeURIComponent(match[1]) : undefined }) const settings = () => `/settings${tail(params())}` const selected = (item: RecentProjectItem) => - loc.pathname.startsWith(`/projects/${encodeURIComponent(item.id)}/`) || - loc.pathname === `/projects/${encodeURIComponent(item.id)}` + route().startsWith(`/projects/${encodeURIComponent(item.id)}/`) || + route() === `/projects/${encodeURIComponent(item.id)}` const nav = () => [ { href: "/projects", label: "Projects", name: "projects", path: "/projects" }, diff --git a/packages/kilo-console/src/context/ConfigProvider.tsx b/packages/kilo-console/src/context/ConfigProvider.tsx index 7005be4e56..15e0438e1e 100644 --- a/packages/kilo-console/src/context/ConfigProvider.tsx +++ b/packages/kilo-console/src/context/ConfigProvider.tsx @@ -18,6 +18,7 @@ import { type TuiPatch, } from "../client" import { ConfigContext, type Task } from "./config" +import { strip } from "../shared/navigation" import { clean, errMsg } from "../shared/utils" import { useLocation, useParams } from "@solidjs/router" @@ -45,7 +46,8 @@ export function ConfigProvider(props: { children?: JSX.Element }) { const discoverable = () => shouldDiscover(search()) const fallback = () => base(search()) const [url, setUrl] = createSignal(fallback()) - const scope = createMemo(() => (loc.pathname.startsWith("/projects/") ? "project" : "global")) + const route = createMemo(() => strip(loc.pathname)) + const scope = createMemo(() => (route().startsWith("/projects/") ? "project" : "global")) const [saving, setSaving] = createSignal() const [failure, setFailure] = createSignal() const needs = createMemo(() => scope() === "project") diff --git a/packages/kilo-console/src/routes/config/AgentsRoute.tsx b/packages/kilo-console/src/routes/config/AgentsRoute.tsx index 5d04777f55..ca6d8d8ba6 100644 --- a/packages/kilo-console/src/routes/config/AgentsRoute.tsx +++ b/packages/kilo-console/src/routes/config/AgentsRoute.tsx @@ -8,6 +8,7 @@ import { CountTag, Tag } from "@kilocode/kilo-web-ui/tag" import { CustomSelect, type SelectOption } from "../../components/CustomSelect" import { SearchField } from "../../components/SearchField" import { useConfig } from "../../context/config" +import { settings } from "../../shared/navigation" import { toolCapabilities, toolName } from "../../shared/utils" import { ConfigPage, SourceBadge } from "./ConfigPage" import { ActionSelect, label as actionLabel, tone as actionTone } from "./PermissionsRoute" @@ -30,13 +31,6 @@ const modes = [ { value: "all", label: "Both" }, ] satisfies SelectOption<"primary" | "subagent" | "all">[] -function base(input: string) { - const index = input.indexOf("/settings") - if (index > 0) return `${input.slice(0, index)}/settings` - if (input.startsWith("/config")) return "/config" - return "/settings" -} - function desc(item: AgentItem) { return item.description ?? "No description available." } @@ -51,7 +45,7 @@ function useAgentLinks() { const nav = useNavigate() const href = (id?: string) => { const suffix = id ? `/${encodeURIComponent(id)}` : "" - return `${base(loc.pathname)}/agents${suffix}${loc.search}` + return `${settings(loc.pathname)}/agents${suffix}${loc.search}` } return { href, nav } } diff --git a/packages/kilo-console/src/routes/config/ConfigSidebar.tsx b/packages/kilo-console/src/routes/config/ConfigSidebar.tsx index 4b011f0347..0a3a9c31ce 100644 --- a/packages/kilo-console/src/routes/config/ConfigSidebar.tsx +++ b/packages/kilo-console/src/routes/config/ConfigSidebar.tsx @@ -3,6 +3,7 @@ import { createMemo, For } from "solid-js" import { Icon } from "@kilocode/kilo-web-ui/icon" import { configNav, type ConfigGroup, type ConfigNode } from "./sections" import { friendly } from "../../shared/utils" +import { settings, strip } from "../../shared/navigation" import { useConfig } from "../../context/config" function repo(input: string) { @@ -14,20 +15,17 @@ export function ConfigSidebar() { const loc = useLocation() const params = useParams() const ctx = useConfig() - const project = createMemo(() => loc.pathname.startsWith("/projects/")) + const route = createMemo(() => strip(loc.pathname)) + const project = createMemo(() => route().startsWith("/projects/")) const scope = createMemo(() => { if (!project()) return "Global" const dir = ctx.query()?.dir if (dir) return friendly(repo(dir)) return friendly(decodeURIComponent(params.project ?? "Project")) }) - const base = createMemo(() => { - const index = loc.pathname.indexOf("/settings") - if (index > 0) return `${loc.pathname.slice(0, index)}/settings` - return "/settings" - }) + const base = createMemo(() => settings(route())) const active = createMemo(() => { - const rest = loc.pathname.slice(base().length) + const rest = route().slice(base().length) if (rest === "/models") return "/models/default" return rest || "/" }) diff --git a/packages/kilo-console/src/shared/navigation.test.ts b/packages/kilo-console/src/shared/navigation.test.ts new file mode 100644 index 0000000000..01f36705c1 --- /dev/null +++ b/packages/kilo-console/src/shared/navigation.test.ts @@ -0,0 +1,19 @@ +import { expect, test } from "bun:test" +import { path, settings, strip } from "./navigation" + +test("strips the deployed console base from route paths", () => { + expect(strip("/console/projects/demo/settings/agents", "/console")).toBe("/projects/demo/settings/agents") + expect(strip("/console", "/console")).toBe("/") + expect(strip("/consoleish/projects", "/console")).toBe("/consoleish/projects") +}) + +test("classifies routes after stripping the console base", () => { + expect(path("/console/projects/demo/settings/agents", "/console")).toBe("/project") + expect(path("/console/settings/agents", "/console")).toBe("/settings") + expect(path("/console/profile", "/console")).toBe("/profile") +}) + +test("builds settings roots without preserving the console base", () => { + expect(settings("/console/projects/demo/settings/agents", "/console")).toBe("/projects/demo/settings") + expect(settings("/console/settings/agents", "/console")).toBe("/settings") +}) diff --git a/packages/kilo-console/src/shared/navigation.ts b/packages/kilo-console/src/shared/navigation.ts index 58c082fa44..3d1476eebe 100644 --- a/packages/kilo-console/src/shared/navigation.ts +++ b/packages/kilo-console/src/shared/navigation.ts @@ -1,8 +1,28 @@ export type Path = "/projects" | "/project" | "/profile" | "/settings" -export function path(input: string): Path { - if (input === "/profile") return "/profile" - if (input.startsWith("/settings") || input.startsWith("/config")) return "/settings" - if (input.startsWith("/projects/")) return "/project" +function base() { + return (import.meta.env?.BASE_URL ?? "/").replace(/\/$/, "") +} + +export function strip(input: string, prefix = base()) { + if (!prefix || prefix === "/") return input + if (input === prefix) return "/" + if (input.startsWith(`${prefix}/`)) return input.slice(prefix.length) + return input +} + +export function settings(input: string, prefix = base()) { + const route = strip(input, prefix) + const index = route.indexOf("/settings") + if (index > 0) return `${route.slice(0, index)}/settings` + if (route.startsWith("/config")) return "/config" + return "/settings" +} + +export function path(input: string, prefix = base()): Path { + const route = strip(input, prefix) + if (route === "/profile") return "/profile" + if (route.startsWith("/settings") || route.startsWith("/config")) return "/settings" + if (route.startsWith("/projects/")) return "/project" return "/projects" }