-
Notifications
You must be signed in to change notification settings - Fork 3.1k
feat(vscode): restore multi-project section and drag-and-drop support #12803
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "kilo-code": patch | ||
| --- | ||
|
|
||
| Restore Agent Manager sections and worktree drag-and-drop when multiple projects are shown, with ordering and section moves scoped to the owning project. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| import { describe, expect, it } from "bun:test" | ||
| import { createProjectStore } from "../../webview-ui/agent-manager/project/store" | ||
|
|
||
| const state = (projectId: string, order: string[]) => ({ | ||
| type: "agentManager.state" as const, | ||
| projectId, | ||
| worktrees: order.map((id) => ({ | ||
| id, | ||
| branch: `${projectId}-${id}`, | ||
| path: `/repo/${projectId}/${id}`, | ||
| parentBranch: "main", | ||
| createdAt: "2026-01-01", | ||
| })), | ||
| sessions: [], | ||
| sections: [], | ||
| worktreeOrder: order, | ||
| }) | ||
|
|
||
| describe("project stores", () => { | ||
| it("keeps worktree order isolated between projects", () => { | ||
| const first = createProjectStore("a") | ||
| const second = createProjectStore("b") | ||
| first.applyState(state("a", ["same", "other"])) | ||
| second.applyState(state("b", ["same", "other"])) | ||
|
|
||
| first.setWorktreeOrder(["other", "same"]) | ||
|
|
||
| expect(first.worktreeOrder()).toEqual(["other", "same"]) | ||
| expect(second.worktreeOrder()).toEqual(["same", "other"]) | ||
| }) | ||
|
|
||
| it("preserves live run statuses when state omits them", () => { | ||
| const store = createProjectStore("a") | ||
| store.applyState(state("a", ["same", "other"])) | ||
| store.setRunStatuses({ | ||
| same: { worktreeId: "same", state: "running" }, | ||
| }) | ||
|
|
||
| store.applyState(state("a", ["other", "same"])) | ||
|
|
||
| expect(store.runStatuses()).toEqual({ | ||
| same: { worktreeId: "same", state: "running" }, | ||
| }) | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ import { | |
| isGrouped, | ||
| isGroupStart, | ||
| isGroupEnd, | ||
| sortWorktrees, | ||
| } from "../../webview-ui/agent-manager/section-helpers" | ||
| import type { WorktreeState, SectionState } from "../../webview-ui/src/types/messages" | ||
|
|
||
|
|
@@ -112,6 +113,23 @@ describe("isGrouped", () => { | |
| }) | ||
| }) | ||
|
|
||
| describe("sortWorktrees", () => { | ||
| it("applies persisted order", () => { | ||
| const all = [wt("a"), wt("b"), wt("c")] | ||
| expect(sortWorktrees(all, ["c", "a", "b"]).map((item) => item.id)).toEqual(["c", "a", "b"]) | ||
| }) | ||
|
|
||
| it("keeps multi-version siblings adjacent at the first group position", () => { | ||
| const all = [wt("a", { groupId: "g" }), wt("b"), wt("c", { groupId: "g" })] | ||
| expect(sortWorktrees(all, ["b", "c", "a"]).map((item) => item.id)).toEqual(["b", "c", "a"]) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SUGGESTION: This case does not actually exercise the grouping branch With Reply with |
||
| }) | ||
|
|
||
| it("appends worktrees missing from persisted order", () => { | ||
| const all = [wt("a"), wt("b"), wt("c")] | ||
| expect(sortWorktrees(all, ["b"]).map((item) => item.id)).toEqual(["b", "a", "c"]) | ||
| }) | ||
| }) | ||
|
|
||
| describe("isGroupStart", () => { | ||
| const list = [wt("a", { groupId: "g1" }), wt("b", { groupId: "g1" }), wt("c", { groupId: "g2" }), wt("d")] | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SUGGESTION: The added
pushState()on every drag end may not be needed, and it is not freeThe multi-project body now applies the new order to its own store optimistically (
ProjectSidebarBody.tsx:156-162), so unlikesetSessionsCollapsed(which documents why it must round-trip) nothing here depends on the echoed state. Two side effects worth weighing:pushState()callspushProjectSessions(), which re-lists sessions for the project root and every worktree directory whenever the 2s freshness window has lapsed — i.e. a backend round trip per worktree on each drop.tabOrder, andstore.applyState()overwrites the store's tab order with it. The persisted order has terminal/review tab ids stripped (AgentManagerApp.tsx:506-508), so a worktree drag end can snap those tabs back to the tail of the tab bar. That already happens forrenameWorktree/section ops, but this adds a new, much more frequent trigger.If the push is only there so a background project's normalized order lands in the webview, the store already holds an equivalent order; consider dropping it (or gating on whether the normalization actually changed anything —
WorktreeStateManager.setWorktreeOrderdiscards thechangedresult thatmoveSectionalready uses).Reply with
@kilocode-bot fix itto have Kilo Code address this issue.