mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-30 17:14:40 +08:00
Merge pull request #12843 from Kilo-Org/fix-multi-project-navigation-and-shortcuts
fix(vscode): fix multi-project navigation shortcuts
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"kilo-code": patch
|
||||
---
|
||||
|
||||
Fix multi-project Agent Manager keyboard navigation and Cmd/Ctrl shortcut selection when worktrees are grouped in sections.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"kilo-code": patch
|
||||
---
|
||||
|
||||
Show previous and next navigation hints using each project's own Agent Manager sidebar order.
|
||||
@@ -401,7 +401,7 @@ describe("buildProjectNavOrder", () => {
|
||||
expect(order.map((e) => e.id)).toEqual([localNavId("A"), worktreeNavId("A", "aw2")])
|
||||
})
|
||||
|
||||
it("renders section members before ungrouped worktrees (matching the project body)", () => {
|
||||
it("renders ungrouped worktrees before section members (matching the project body)", () => {
|
||||
const order = buildProjectNavOrder([
|
||||
project({
|
||||
id: "A",
|
||||
@@ -411,7 +411,76 @@ describe("buildProjectNavOrder", () => {
|
||||
unassigned: [],
|
||||
}),
|
||||
])
|
||||
expect(order.map((e) => e.id)).toEqual([localNavId("A"), worktreeNavId("A", "aw1"), worktreeNavId("A", "aw2")])
|
||||
expect(order.map((e) => e.id)).toEqual([localNavId("A"), worktreeNavId("A", "aw2"), worktreeNavId("A", "aw1")])
|
||||
})
|
||||
|
||||
it("follows persisted top-level section order and worktree order", () => {
|
||||
const order = buildProjectNavOrder([
|
||||
project({
|
||||
id: "A",
|
||||
expanded: true,
|
||||
worktrees: [{ id: "aw1", sectionId: "s1" }, { id: "aw2" }, { id: "aw3", sectionId: "s2" }],
|
||||
worktreeOrder: ["aw2", "s2", "s1", "aw3", "aw1"],
|
||||
sections: [
|
||||
{ id: "s1", collapsed: false },
|
||||
{ id: "s2", collapsed: false },
|
||||
],
|
||||
unassigned: [],
|
||||
}),
|
||||
])
|
||||
|
||||
expect(order.map((e) => e.id)).toEqual([
|
||||
localNavId("A"),
|
||||
worktreeNavId("A", "aw2"),
|
||||
worktreeNavId("A", "aw3"),
|
||||
worktreeNavId("A", "aw1"),
|
||||
])
|
||||
})
|
||||
|
||||
it("keeps multi-version worktrees adjacent", () => {
|
||||
const order = buildProjectNavOrder([
|
||||
project({
|
||||
id: "A",
|
||||
expanded: true,
|
||||
worktrees: [{ id: "aw1", groupId: "g" }, { id: "aw2" }, { id: "aw3", groupId: "g" }],
|
||||
worktreeOrder: ["aw1", "aw2", "aw3"],
|
||||
sections: [],
|
||||
unassigned: [],
|
||||
}),
|
||||
])
|
||||
|
||||
expect(order.map((e) => e.id)).toEqual([
|
||||
localNavId("A"),
|
||||
worktreeNavId("A", "aw1"),
|
||||
worktreeNavId("A", "aw3"),
|
||||
worktreeNavId("A", "aw2"),
|
||||
])
|
||||
})
|
||||
|
||||
it("matches raw ungrouped order when sections are present", () => {
|
||||
const order = buildProjectNavOrder([
|
||||
project({
|
||||
id: "A",
|
||||
expanded: true,
|
||||
worktrees: [
|
||||
{ id: "aw1", groupId: "g" },
|
||||
{ id: "aw2" },
|
||||
{ id: "aw3", groupId: "g" },
|
||||
{ id: "aw4", sectionId: "s1" },
|
||||
],
|
||||
worktreeOrder: ["aw1", "aw2", "aw3", "s1", "aw4"],
|
||||
sections: [{ id: "s1", collapsed: false }],
|
||||
unassigned: [],
|
||||
}),
|
||||
])
|
||||
|
||||
expect(order.map((e) => e.id)).toEqual([
|
||||
localNavId("A"),
|
||||
worktreeNavId("A", "aw1"),
|
||||
worktreeNavId("A", "aw2"),
|
||||
worktreeNavId("A", "aw3"),
|
||||
worktreeNavId("A", "aw4"),
|
||||
])
|
||||
})
|
||||
|
||||
it("excludes unassigned sessions when the sessions section is collapsed", () => {
|
||||
|
||||
@@ -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("")
|
||||
})
|
||||
})
|
||||
@@ -103,7 +103,7 @@ import {
|
||||
focusChatSearch,
|
||||
LOCAL,
|
||||
} from "./navigate"
|
||||
import { createProjectNav } from "./project-nav"
|
||||
import { buildProjectNavEntries, createProjectNav } from "./project-nav"
|
||||
import {
|
||||
addPendingTab as addLocalPendingTab,
|
||||
nextTabAfterClose,
|
||||
@@ -882,6 +882,11 @@ const AgentManagerContent: Component = () => {
|
||||
)
|
||||
/** Map from sidebar item id → 1-based shortcut number (⌘1 for LOCAL, ⌘2 for first worktree, etc.) */
|
||||
const shortcutMap = createMemo(() => buildShortcutMap(sidebarOrder()))
|
||||
const projectShortcutMap = createMemo(() =>
|
||||
buildShortcutMap(
|
||||
buildProjectNavEntries(projectList(), projectStates(), projectLive.sessions()).map((entry) => ({ id: entry.id })),
|
||||
),
|
||||
)
|
||||
|
||||
const moveToSection = (ids: string[], sec: string | null) =>
|
||||
vscode.postMessage({ type: "agentManager.moveToSection", worktreeIds: ids, sectionId: sec })
|
||||
@@ -2347,6 +2352,7 @@ const AgentManagerContent: Component = () => {
|
||||
t={t}
|
||||
onSearchRef={(ref) => (sidebarSearchMenu = ref)}
|
||||
onShortcuts={handleShowKeyboardShortcuts}
|
||||
shortcutMap={projectShortcutMap}
|
||||
/>
|
||||
</Show>
|
||||
<Show when={!multiProject()}>
|
||||
|
||||
@@ -39,6 +39,7 @@ interface Props {
|
||||
t: LanguageContextValue["t"]
|
||||
onSearchRef: (ref: SidebarSearchMenuRef) => void
|
||||
onShortcuts: () => void
|
||||
shortcutMap?: () => Map<string, number>
|
||||
}
|
||||
|
||||
export const ProjectList: Component<Props> = (props) => {
|
||||
@@ -217,6 +218,7 @@ export const ProjectList: Component<Props> = (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" })}
|
||||
@@ -224,6 +226,7 @@ export const ProjectList: Component<Props> = (props) => {
|
||||
onSelectSession={(projectId, sessionId) => select({ projectId, kind: "session", sessionId })}
|
||||
onNewWorktree={newWorktree}
|
||||
onDefaultBranch={defaultBranch}
|
||||
shortcutMap={props.shortcutMap}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -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"
|
||||
@@ -31,6 +32,8 @@ import { ConstrainDragXAxis } from "./constrain-drag-x"
|
||||
import { createProjectStore, type ProjectStore } from "./project/store"
|
||||
import { randomColor } from "./section-colors"
|
||||
|
||||
const isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.userAgent)
|
||||
|
||||
interface Props {
|
||||
project: AgentProjectSnapshot
|
||||
state?: AgentManagerStateMessage
|
||||
@@ -42,6 +45,7 @@ interface Props {
|
||||
sessions?: ProjectSessionInfo[]
|
||||
selectedProject?: string
|
||||
selection?: string
|
||||
currentSessionID?: () => string | undefined
|
||||
bindings: Record<string, string>
|
||||
t: LanguageContextValue["t"]
|
||||
onSelectLocal: (projectId: string) => void
|
||||
@@ -49,6 +53,7 @@ interface Props {
|
||||
onSelectSession: (projectId: string, sessionId: string) => void
|
||||
onNewWorktree: (projectId: string) => void
|
||||
onDefaultBranch: (projectId: string, selected?: string, detected?: string) => void
|
||||
shortcutMap?: () => Map<string, number>
|
||||
}
|
||||
|
||||
/** Permanent real sidebar body for one expanded project. */
|
||||
@@ -97,9 +102,23 @@ export const ProjectSidebarBody: Component<Props> = (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, state()?.sessionsCollapsed ? [] : localSessions()),
|
||||
)
|
||||
const post = (message: Record<string, unknown>) =>
|
||||
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
|
||||
@@ -214,6 +233,7 @@ export const ProjectSidebarBody: Component<Props> = (props) => {
|
||||
<WorktreeItem
|
||||
worktree={worktree}
|
||||
sidebarId={`${props.project.id}:${worktree.id}`}
|
||||
shortcut={props.shortcutMap?.().get(`${props.project.id}:wt:${worktree.id}`)}
|
||||
label={worktree.label || label()}
|
||||
subtitle={worktree.label ? (worktree.label !== worktree.branch ? worktree.branch : undefined) : subtitle()}
|
||||
active={active() && props.selection === worktree.id}
|
||||
@@ -222,6 +242,7 @@ export const ProjectSidebarBody: Component<Props> = (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)}
|
||||
@@ -284,6 +305,14 @@ export const ProjectSidebarBody: Component<Props> = (props) => {
|
||||
<path d="M6 16.5H14" stroke="currentColor" stroke-linecap="square" />
|
||||
<path d="M10 13.5V16.5" stroke="currentColor" />
|
||||
</svg>
|
||||
<Show when={props.shortcutMap?.().get(`${props.project.id}:local`)}>
|
||||
{(shortcut) => (
|
||||
<span class="am-shortcut-badge">
|
||||
{isMac ? "⌘" : "Ctrl+"}
|
||||
{shortcut()}
|
||||
</span>
|
||||
)}
|
||||
</Show>
|
||||
<div class="am-local-text">
|
||||
<span class="am-local-label">{props.t("agentManager.local")}</span>
|
||||
<Show when={props.local?.branch}>
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
* Returns the action to take: select a session by ID, go to local, or do nothing.
|
||||
*/
|
||||
|
||||
import { sortWorktrees } from "./section-helpers"
|
||||
|
||||
/** Sentinel value for the local repo selection. */
|
||||
export const LOCAL = "local" as const
|
||||
|
||||
@@ -137,8 +139,8 @@ export function focusChatSearch(reset: { history(v: boolean): void; review(v: bo
|
||||
* Multi-project navigation.
|
||||
*
|
||||
* In multi-project mode the sidebar shows an accordion of projects; each
|
||||
* expanded project renders its own Local item, worktrees (sections first,
|
||||
* then ungrouped), and an unassigned-sessions list. Keyboard previous/next
|
||||
* expanded project renders its own Local item, ungrouped worktrees, section
|
||||
* members, and an unassigned-sessions list. Keyboard previous/next
|
||||
* and numeric shortcuts must traverse every expanded project in visual
|
||||
* order, not just the active one.
|
||||
*
|
||||
@@ -160,7 +162,9 @@ export interface NavEntry {
|
||||
export interface ProjectNavInput {
|
||||
id: string
|
||||
expanded: boolean
|
||||
worktrees: { id: string; sectionId?: string }[]
|
||||
worktrees: { id: string; sectionId?: string; groupId?: string }[]
|
||||
/** Persisted top-level order containing worktree and section IDs. */
|
||||
worktreeOrder?: string[]
|
||||
sections: { id: string; collapsed: boolean }[]
|
||||
sessionsCollapsed: boolean
|
||||
/** Visible unassigned (root, no worktree) sessions in render order. */
|
||||
@@ -174,10 +178,10 @@ export const sessionNavId = (projectId: string, sessionId: string) => `${project
|
||||
/**
|
||||
* Build one global visual order across expanded projects.
|
||||
*
|
||||
* For each expanded project (in input order): Local, then worktrees in the
|
||||
* order the multi-project body renders them (each non-collapsed section's
|
||||
* members, then ungrouped), then visible unassigned sessions (when the
|
||||
* sessions section is not collapsed). Collapsed projects contribute nothing.
|
||||
* For each expanded project (in input order): Local, then ungrouped worktrees,
|
||||
* then members of each non-collapsed section in top-level order, then visible
|
||||
* unassigned sessions. This matches `buildTopLevelItems` and the project body.
|
||||
* Collapsed projects contribute nothing.
|
||||
*/
|
||||
export function buildProjectNavOrder(projects: ProjectNavInput[]): NavEntry[] {
|
||||
const order: NavEntry[] = []
|
||||
@@ -185,19 +189,28 @@ export function buildProjectNavOrder(projects: ProjectNavInput[]): NavEntry[] {
|
||||
if (!p.expanded) continue
|
||||
const pid = p.id
|
||||
order.push({ id: localNavId(pid), target: { projectId: pid, kind: "local" } })
|
||||
for (const sec of p.sections) {
|
||||
const worktrees = sortWorktrees(p.worktrees, p.worktreeOrder ?? [])
|
||||
const rank = new Map((p.worktreeOrder ?? []).map((id, index) => [id, index] as const))
|
||||
const ungrouped = worktrees.filter((w) => !w.sectionId)
|
||||
if (p.sections.length > 0) {
|
||||
ungrouped.sort(
|
||||
(a, b) => (rank.get(a.id) ?? Number.MAX_SAFE_INTEGER) - (rank.get(b.id) ?? Number.MAX_SAFE_INTEGER),
|
||||
)
|
||||
}
|
||||
const secs = [...p.sections].sort(
|
||||
(a, b) => (rank.get(a.id) ?? Number.MAX_SAFE_INTEGER) - (rank.get(b.id) ?? Number.MAX_SAFE_INTEGER),
|
||||
)
|
||||
for (const w of ungrouped) {
|
||||
order.push({ id: worktreeNavId(pid, w.id), target: { projectId: pid, kind: "worktree", worktreeId: w.id } })
|
||||
}
|
||||
for (const sec of secs) {
|
||||
if (sec.collapsed) continue
|
||||
for (const w of p.worktrees) {
|
||||
for (const w of worktrees) {
|
||||
if (w.sectionId === sec.id) {
|
||||
order.push({ id: worktreeNavId(pid, w.id), target: { projectId: pid, kind: "worktree", worktreeId: w.id } })
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const w of p.worktrees) {
|
||||
if (!w.sectionId) {
|
||||
order.push({ id: worktreeNavId(pid, w.id), target: { projectId: pid, kind: "worktree", worktreeId: w.id } })
|
||||
}
|
||||
}
|
||||
if (!p.sessionsCollapsed) {
|
||||
for (const s of p.unassigned) {
|
||||
order.push({ id: sessionNavId(pid, s.id), target: { projectId: pid, kind: "session", sessionId: s.id } })
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { adjacentHint } from "./navigate"
|
||||
import { buildSidebarOrder } from "./section-helpers"
|
||||
|
||||
export function projectSidebarOrder(...args: Parameters<typeof buildSidebarOrder>): 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)
|
||||
}
|
||||
@@ -19,7 +19,7 @@ import type { AgentManagerStateMessage, AgentProjectSnapshot, ProjectSessionInfo
|
||||
*
|
||||
* Handles previous/next (⌘⌥↑/↓) and numeric-shortcut (⌘1-9) navigation across
|
||||
* the sidebar. In multi-project mode it builds one global visual order across
|
||||
* every expanded project — Local, worktrees (sections first, then ungrouped),
|
||||
* every expanded project — Local, ungrouped worktrees, then section members,
|
||||
* and visible unassigned sessions — using stable project-qualified composite
|
||||
* ids, and activates each target with a single atomic
|
||||
* `agentManager.activateSelection` dispatch. In single-project mode it keeps
|
||||
@@ -48,6 +48,31 @@ export interface ProjectNav {
|
||||
jump: (index: number) => void
|
||||
}
|
||||
|
||||
/** Build the same global order used by keyboard navigation and shortcut badges. */
|
||||
export function buildProjectNavEntries(
|
||||
projects: AgentProjectSnapshot[],
|
||||
states: Record<string, AgentManagerStateMessage>,
|
||||
sessions: Record<string, ProjectSessionInfo[]>,
|
||||
): NavEntry[] {
|
||||
return buildProjectNavOrder(
|
||||
projects.map((p) => {
|
||||
const st = states[p.id]
|
||||
if (!st) {
|
||||
return { id: p.id, expanded: false, worktrees: [], sections: [], sessionsCollapsed: false, unassigned: [] }
|
||||
}
|
||||
return {
|
||||
id: p.id,
|
||||
expanded: p.expanded,
|
||||
worktrees: (st.worktrees ?? []).map((w) => ({ id: w.id, sectionId: w.sectionId, groupId: w.groupId })),
|
||||
worktreeOrder: st.worktreeOrder,
|
||||
sections: (st.sections ?? []).map((s) => ({ id: s.id, collapsed: s.collapsed })),
|
||||
sessionsCollapsed: st.sessionsCollapsed === true,
|
||||
unassigned: (sessions[p.id] ?? []).filter((s) => s.worktreeId === null).map((s) => ({ id: s.id })),
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
/** DOM selector for the sidebar element backing a nav target. */
|
||||
export const navSelector = (target: NavTarget): string => {
|
||||
if (target.kind === "local") return `[data-sidebar-id="${target.projectId}:local"]`
|
||||
@@ -74,24 +99,7 @@ export function createProjectNav(
|
||||
if (!deps.multiProject()) return []
|
||||
const states = deps.states()
|
||||
const live = deps.sessions()
|
||||
return buildProjectNavOrder(
|
||||
deps.projects().map((p) => {
|
||||
const st = states[p.id]
|
||||
// Until a project's state payload arrives its body shows a spinner
|
||||
// (no Local/worktrees/sessions) — exclude it from the nav order.
|
||||
if (!st) {
|
||||
return { id: p.id, expanded: false, worktrees: [], sections: [], sessionsCollapsed: false, unassigned: [] }
|
||||
}
|
||||
return {
|
||||
id: p.id,
|
||||
expanded: p.expanded,
|
||||
worktrees: (st.worktrees ?? []).map((w) => ({ id: w.id, sectionId: w.sectionId })),
|
||||
sections: (st.sections ?? []).map((s) => ({ id: s.id, collapsed: s.collapsed })),
|
||||
sessionsCollapsed: st.sessionsCollapsed === true,
|
||||
unassigned: (live[p.id] ?? []).filter((s) => s.worktreeId === null).map((s) => ({ id: s.id })),
|
||||
}
|
||||
}),
|
||||
)
|
||||
return buildProjectNavEntries(deps.projects(), states, live)
|
||||
})
|
||||
|
||||
const currentId = createMemo((): string | undefined => {
|
||||
|
||||
@@ -10,11 +10,11 @@ export type TopLevelItem = { kind: "section"; section: SectionState } | { kind:
|
||||
export type SidebarItem = { type: "local" | "wt" | "session"; id: string }
|
||||
|
||||
/** Apply persisted order while keeping multi-version worktrees adjacent. */
|
||||
export function sortWorktrees(all: WorktreeState[], order: string[]): WorktreeState[] {
|
||||
export function sortWorktrees<T extends { id: string; groupId?: string }>(all: T[], order: string[]): T[] {
|
||||
const ordered = applyTabOrder(all, order)
|
||||
if (ordered.length === 0) return []
|
||||
|
||||
const groups = new Map<string, WorktreeState[]>()
|
||||
const groups = new Map<string, T[]>()
|
||||
for (const wt of ordered) {
|
||||
if (!wt.groupId) continue
|
||||
const group = groups.get(wt.groupId) ?? []
|
||||
@@ -22,7 +22,7 @@ export function sortWorktrees(all: WorktreeState[], order: string[]): WorktreeSt
|
||||
groups.set(wt.groupId, group)
|
||||
}
|
||||
|
||||
const result: WorktreeState[] = []
|
||||
const result: T[] = []
|
||||
const placed = new Set<string>()
|
||||
for (const wt of ordered) {
|
||||
if (placed.has(wt.id)) continue
|
||||
@@ -145,7 +145,7 @@ export function buildSidebarOrder(
|
||||
}
|
||||
|
||||
/** Build a map from sidebar item id → 1-based shortcut number (1 for LOCAL, 2+ for worktrees). */
|
||||
export function buildShortcutMap(order: SidebarItem[]): Map<string, number> {
|
||||
export function buildShortcutMap(order: { id: string }[]): Map<string, number> {
|
||||
const map = new Map<string, number>()
|
||||
for (let i = 0; i < order.length && i < 9; i++) {
|
||||
map.set(order[i]!.id, i + 1)
|
||||
|
||||
Reference in New Issue
Block a user