From 8fe53293076e410e9428112e1ed1d0681b3b0792 Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Tue, 4 Aug 2026 11:16:04 +0200 Subject: [PATCH 1/2] fix(vscode): add project-local navigation hints --- .changeset/project-local-navigation-hints.md | 5 ++++ .../unit/project-local-navigation.test.ts | 25 +++++++++++++++++++ .../webview-ui/agent-manager/ProjectList.tsx | 1 + .../agent-manager/ProjectSidebarBody.tsx | 15 +++++++++++ .../agent-manager/project-local-navigation.ts | 19 ++++++++++++++ 5 files changed, 65 insertions(+) create mode 100644 .changeset/project-local-navigation-hints.md create mode 100644 packages/kilo-vscode/tests/unit/project-local-navigation.test.ts create mode 100644 packages/kilo-vscode/webview-ui/agent-manager/project-local-navigation.ts diff --git a/.changeset/project-local-navigation-hints.md b/.changeset/project-local-navigation-hints.md new file mode 100644 index 00000000000..f6acca80ecd --- /dev/null +++ b/.changeset/project-local-navigation-hints.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Show previous and next navigation hints using each project's own Agent Manager sidebar order. diff --git a/packages/kilo-vscode/tests/unit/project-local-navigation.test.ts b/packages/kilo-vscode/tests/unit/project-local-navigation.test.ts new file mode 100644 index 00000000000..b20bbc5f30a --- /dev/null +++ b/packages/kilo-vscode/tests/unit/project-local-navigation.test.ts @@ -0,0 +1,25 @@ +import { describe, expect, it } from "bun:test" +import { projectAdjacentHint } from "../../webview-ui/agent-manager/project-local-navigation" + +describe("projectAdjacentHint", () => { + it("does not leak a hint to another project with the same raw ID", () => { + expect(projectAdjacentHint("project-a", "project-a", "shared", "local", ["local", "shared"], "prev", "next")).toBe( + "next", + ) + expect(projectAdjacentHint("project-b", "project-a", "shared", "local", ["local", "shared"], "prev", "next")).toBe( + "", + ) + }) + + it("uses the active project's local sidebar order", () => { + expect(projectAdjacentHint("project-a", "project-a", "shared", "local", ["local", "shared"], "prev", "next")).toBe( + "next", + ) + expect(projectAdjacentHint("project-a", "project-a", "local", "shared", ["local", "shared"], "prev", "next")).toBe( + "prev", + ) + expect( + projectAdjacentHint("project-b", "project-b", "shared", "local", ["local", "other", "shared"], "prev", "next"), + ).toBe("") + }) +}) diff --git a/packages/kilo-vscode/webview-ui/agent-manager/ProjectList.tsx b/packages/kilo-vscode/webview-ui/agent-manager/ProjectList.tsx index a07fbab208c..8fe0c65bbdc 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/ProjectList.tsx +++ b/packages/kilo-vscode/webview-ui/agent-manager/ProjectList.tsx @@ -218,6 +218,7 @@ export const ProjectList: Component = (props) => { sessions={props.sessions[project.id]} selectedProject={props.selectedProject} selection={props.selection} + currentSessionID={props.currentSessionID} bindings={props.bindings} t={props.t} onSelectLocal={(projectId) => select({ projectId, kind: "local" })} diff --git a/packages/kilo-vscode/webview-ui/agent-manager/ProjectSidebarBody.tsx b/packages/kilo-vscode/webview-ui/agent-manager/ProjectSidebarBody.tsx index 1b67b39216f..a0fd9a1a814 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/ProjectSidebarBody.tsx +++ b/packages/kilo-vscode/webview-ui/agent-manager/ProjectSidebarBody.tsx @@ -20,6 +20,7 @@ import type { } from "../src/types/messages" import type { LanguageContextValue } from "../src/context/language" import { useVSCode } from "../src/context/vscode" +import { projectAdjacentHint, projectSidebarOrder } from "./project-local-navigation" import SectionHeader from "./SectionHeader" import { WorktreeItem } from "./WorktreeItem" import { UnassignedSessionsSection } from "./UnassignedSessionsSection" @@ -44,6 +45,7 @@ interface Props { sessions?: ProjectSessionInfo[] selectedProject?: string selection?: string + currentSessionID?: () => string | undefined bindings: Record t: LanguageContextValue["t"] onSelectLocal: (projectId: string) => void @@ -100,9 +102,21 @@ export const ProjectSidebarBody: Component = (props) => { const members = (sectionId: string) => sorted().filter((wt) => wt.sectionId === sectionId) const ungrouped = createMemo(() => sorted().filter((wt) => !wt.sectionId)) const top = createMemo(() => buildTopLevelItems(sections(), ungrouped(), sorted(), order())) + const sidebarOrder = createMemo(() => projectSidebarOrder(top(), sorted(), sections(), members, localSessions())) const post = (message: Record) => vscode.postMessage({ ...message, projectId: props.project.id } as never) + const navHint = (id: string) => + projectAdjacentHint( + props.project.id, + props.selectedProject, + id, + props.selection ?? props.currentSessionID?.(), + sidebarOrder(), + props.bindings.previousSession ?? "", + props.bindings.nextSession ?? "", + ) + const scope = (kind: "section" | "worktree", id: string) => `${props.project.id}:${kind}:${id}` const parse = (kind: "section" | "worktree", value: unknown) => { if (typeof value !== "string") return @@ -226,6 +240,7 @@ export const ProjectSidebarBody: Component = (props) => { working={runs()[worktree.id]?.state === "running"} stale={state()?.staleWorktreeIds?.includes(worktree.id) === true} stats={props.stats?.[worktree.id]} + navHint={navHint(worktree.id)} sessions={sessions(worktree.id).length} grouped={isGrouped(worktree)} groupStart={isGroupStart(worktree, idx(), list)} diff --git a/packages/kilo-vscode/webview-ui/agent-manager/project-local-navigation.ts b/packages/kilo-vscode/webview-ui/agent-manager/project-local-navigation.ts new file mode 100644 index 00000000000..ede97a8e129 --- /dev/null +++ b/packages/kilo-vscode/webview-ui/agent-manager/project-local-navigation.ts @@ -0,0 +1,19 @@ +import { adjacentHint } from "./navigate" +import { buildSidebarOrder } from "./section-helpers" + +export function projectSidebarOrder(...args: Parameters): string[] { + return buildSidebarOrder(...args).map((item) => item.id) +} + +export function projectAdjacentHint( + projectId: string, + activeProjectId: string | undefined, + itemId: string, + activeId: string | undefined, + flatIds: string[], + prev: string, + next: string, +): string { + if (projectId !== activeProjectId) return "" + return adjacentHint(itemId, activeId, flatIds, prev, next) +} From 43ffa2bd137c0dbab58ec642e68ede78070421ca Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Tue, 4 Aug 2026 11:39:48 +0200 Subject: [PATCH 2/2] fix(vscode): align hints with collapsed sessions --- .../webview-ui/agent-manager/ProjectSidebarBody.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/kilo-vscode/webview-ui/agent-manager/ProjectSidebarBody.tsx b/packages/kilo-vscode/webview-ui/agent-manager/ProjectSidebarBody.tsx index a0fd9a1a814..0c97381b395 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/ProjectSidebarBody.tsx +++ b/packages/kilo-vscode/webview-ui/agent-manager/ProjectSidebarBody.tsx @@ -102,7 +102,9 @@ export const ProjectSidebarBody: Component = (props) => { const members = (sectionId: string) => sorted().filter((wt) => wt.sectionId === sectionId) const ungrouped = createMemo(() => sorted().filter((wt) => !wt.sectionId)) const top = createMemo(() => buildTopLevelItems(sections(), ungrouped(), sorted(), order())) - const sidebarOrder = createMemo(() => projectSidebarOrder(top(), sorted(), sections(), members, localSessions())) + const sidebarOrder = createMemo(() => + projectSidebarOrder(top(), sorted(), sections(), members, state()?.sessionsCollapsed ? [] : localSessions()), + ) const post = (message: Record) => vscode.postMessage({ ...message, projectId: props.project.id } as never)