mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-28 19:11:03 +08:00
feat(agent-manager): default base branch selection and BranchSelect component (#6701)
- Add persistent default base branch with picker dialog - Extract BranchSelect reusable component from duplicated branch list UI - Add base-branch.ts pure logic module with normalizeBaseBranch/chooseBaseBranch - Add isCheckedOut flag to branch list items - Update WorktreeStateManager to persist defaultBaseBranch - Add i18n keys for all 16 locales - Add unit tests for base-branch logic - Update architecture tests
This commit is contained in:
@@ -9,6 +9,7 @@ import { KiloProvider } from "../KiloProvider"
|
||||
import { buildWebviewHtml } from "../utils"
|
||||
import { WorktreeManager, type CreateWorktreeResult } from "./WorktreeManager"
|
||||
import { WorktreeStateManager } from "./WorktreeStateManager"
|
||||
import { chooseBaseBranch, normalizeBaseBranch } from "./base-branch"
|
||||
import { GitStatsPoller, type WorktreePresenceResult } from "./GitStatsPoller"
|
||||
import { GitOps, type ApplyConflict } from "./GitOps"
|
||||
import { versionedName } from "./branch-name"
|
||||
@@ -281,6 +282,12 @@ export class AgentManagerProvider implements vscode.Disposable {
|
||||
return null
|
||||
}
|
||||
|
||||
if (type === "agentManager.setDefaultBaseBranch") {
|
||||
const branch = normalizeBaseBranch(msg.branch as string | undefined)
|
||||
this.state?.setDefaultBaseBranch(branch)
|
||||
this.pushState()
|
||||
return null
|
||||
}
|
||||
if (type === "agentManager.requestExternalWorktrees") {
|
||||
void this.onRequestExternalWorktrees()
|
||||
return null
|
||||
@@ -386,6 +393,31 @@ export class AgentManagerProvider implements vscode.Disposable {
|
||||
// Shared helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Resolve the effective base branch using the configured default, explicit override, and existence check. */
|
||||
private async resolveBaseBranch(
|
||||
manager: WorktreeManager,
|
||||
state: WorktreeStateManager,
|
||||
explicit?: string,
|
||||
): Promise<string | undefined> {
|
||||
const configured = state.getDefaultBaseBranch()
|
||||
if (!configured && !explicit) return undefined
|
||||
|
||||
const configuredExists = configured ? await manager.branchExists(configured) : false
|
||||
const result = chooseBaseBranch({ explicit, configured, configuredExists })
|
||||
|
||||
if (result.stale) {
|
||||
this.clearStaleDefaultBaseBranch(state, result.stale)
|
||||
}
|
||||
return result.branch
|
||||
}
|
||||
|
||||
/** Reset a stale default base branch and notify the webview. */
|
||||
private clearStaleDefaultBaseBranch(state: WorktreeStateManager, stale: string): void {
|
||||
this.log(`Default base branch "${stale}" no longer exists, clearing`)
|
||||
state.setDefaultBaseBranch(undefined)
|
||||
this.pushState()
|
||||
}
|
||||
|
||||
/** Create a git worktree on disk and register it in state. Returns null on failure. */
|
||||
private async createWorktreeOnDisk(opts?: {
|
||||
groupId?: string
|
||||
@@ -411,11 +443,16 @@ export class AgentManagerProvider implements vscode.Disposable {
|
||||
|
||||
this.postToWebview({ type: "agentManager.worktreeSetup", status: "creating", message: "Creating git worktree..." })
|
||||
|
||||
// Resolve effective base branch using configured default
|
||||
const effectiveBase = opts?.existingBranch
|
||||
? undefined
|
||||
: await this.resolveBaseBranch(manager, state, opts?.baseBranch)
|
||||
|
||||
let result: CreateWorktreeResult
|
||||
try {
|
||||
result = await manager.createWorktree({
|
||||
prompt: opts?.name || "kilo",
|
||||
baseBranch: opts?.baseBranch,
|
||||
baseBranch: effectiveBase ?? opts?.baseBranch,
|
||||
branchName: opts?.branchName,
|
||||
existingBranch: opts?.existingBranch,
|
||||
})
|
||||
@@ -940,10 +977,23 @@ export class AgentManagerProvider implements vscode.Disposable {
|
||||
try {
|
||||
const result = await manager.listBranches()
|
||||
const checkedOut = await manager.checkedOutBranches()
|
||||
const filtered = result.branches.filter((b) => !checkedOut.has(b.name))
|
||||
|
||||
// Include isCheckedOut flag on each branch — let the webview decide how to filter
|
||||
const branches = result.branches.map((b) => ({
|
||||
...b,
|
||||
isCheckedOut: checkedOut.has(b.name),
|
||||
}))
|
||||
|
||||
// Validate configured default branch still exists
|
||||
const state = this.getStateManager()
|
||||
const configured = state?.getDefaultBaseBranch()
|
||||
if (configured && !branches.some((b) => b.name === configured)) {
|
||||
this.clearStaleDefaultBaseBranch(state!, configured)
|
||||
}
|
||||
|
||||
this.postToWebview({
|
||||
type: "agentManager.branches",
|
||||
branches: filtered,
|
||||
branches,
|
||||
defaultBranch: result.defaultBranch,
|
||||
})
|
||||
} catch (error) {
|
||||
@@ -1336,7 +1386,8 @@ export class AgentManagerProvider implements vscode.Disposable {
|
||||
if (!manager) return
|
||||
try {
|
||||
const branch = await manager.currentBranch()
|
||||
this.postToWebview({ type: "agentManager.repoInfo", branch })
|
||||
const defaultBranch = await manager.defaultBranch()
|
||||
this.postToWebview({ type: "agentManager.repoInfo", branch, defaultBranch })
|
||||
} catch (error) {
|
||||
this.log(`Failed to get current branch: ${error}`)
|
||||
}
|
||||
@@ -1409,6 +1460,7 @@ export class AgentManagerProvider implements vscode.Disposable {
|
||||
sessionsCollapsed: state.getSessionsCollapsed(),
|
||||
reviewDiffStyle: state.getReviewDiffStyle(),
|
||||
isGitRepo: true,
|
||||
defaultBaseBranch: state.getDefaultBaseBranch(),
|
||||
})
|
||||
|
||||
this.statsPoller.setEnabled(worktrees.length > 0 || this.panel !== undefined)
|
||||
|
||||
@@ -37,6 +37,7 @@ interface StateFile {
|
||||
tabOrder?: Record<string, string[]>
|
||||
sessionsCollapsed?: boolean
|
||||
reviewDiffStyle?: "unified" | "split"
|
||||
defaultBaseBranch?: string
|
||||
}
|
||||
|
||||
const STATE_FILE = "agent-manager.json"
|
||||
@@ -55,6 +56,7 @@ export class WorktreeStateManager {
|
||||
private tabOrder: Record<string, string[]> = {}
|
||||
private collapsed = false
|
||||
private reviewDiffStyle: "unified" | "split" = "unified"
|
||||
private defaultBase: string | undefined
|
||||
private readonly log: (msg: string) => void
|
||||
private saving: Promise<void> | undefined
|
||||
private pendingSave = false
|
||||
@@ -245,6 +247,19 @@ export class WorktreeStateManager {
|
||||
void this.save()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Default base branch
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getDefaultBaseBranch(): string | undefined {
|
||||
return this.defaultBase
|
||||
}
|
||||
|
||||
setDefaultBaseBranch(value: string | undefined): void {
|
||||
this.defaultBase = value
|
||||
void this.save()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Persistence
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -271,6 +286,7 @@ export class WorktreeStateManager {
|
||||
if (data.reviewDiffStyle === "split") {
|
||||
this.reviewDiffStyle = "split"
|
||||
}
|
||||
this.defaultBase = data.defaultBaseBranch
|
||||
this.log(`Loaded state: ${this.worktrees.size} worktrees, ${this.sessions.size} sessions`)
|
||||
} catch (error) {
|
||||
const code = (error as NodeJS.ErrnoException).code
|
||||
@@ -343,6 +359,9 @@ export class WorktreeStateManager {
|
||||
if (this.reviewDiffStyle === "split") {
|
||||
data.reviewDiffStyle = "split"
|
||||
}
|
||||
if (this.defaultBase) {
|
||||
data.defaultBaseBranch = this.defaultBase
|
||||
}
|
||||
|
||||
try {
|
||||
const dir = path.dirname(this.file)
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Pure logic for resolving the effective base branch.
|
||||
*
|
||||
* Priority chain:
|
||||
* 1. Explicit (per-worktree override, from dialog)
|
||||
* 2. Configured default (persisted in agent-manager.json), if it still exists
|
||||
* 3. Auto-detect (undefined → WorktreeManager.defaultBranch() at call site)
|
||||
*/
|
||||
|
||||
/** Trim whitespace, return undefined for empty/blank. */
|
||||
export function normalizeBaseBranch(value: string | undefined): string | undefined {
|
||||
if (!value) return undefined
|
||||
const trimmed = value.trim()
|
||||
return trimmed.length > 0 ? trimmed : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick the effective base branch given an explicit override, a configured
|
||||
* default, and whether that configured branch still exists in the repo.
|
||||
*
|
||||
* Returns `{ branch }` with the winner, and optionally `{ stale }` when the
|
||||
* configured branch no longer exists (so the caller can clear it).
|
||||
*/
|
||||
export function chooseBaseBranch(opts: { explicit?: string; configured?: string; configuredExists?: boolean }): {
|
||||
branch?: string
|
||||
stale?: string
|
||||
} {
|
||||
if (opts.explicit) return { branch: opts.explicit }
|
||||
if (opts.configured) {
|
||||
if (opts.configuredExists) return { branch: opts.configured }
|
||||
return { stale: opts.configured }
|
||||
}
|
||||
return {}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ export interface BranchListItem {
|
||||
isRemote: boolean
|
||||
isDefault: boolean
|
||||
lastCommitDate?: string
|
||||
isCheckedOut?: boolean
|
||||
}
|
||||
|
||||
export interface PRUrlParts {
|
||||
|
||||
@@ -25,6 +25,7 @@ const TSX_FILES = [
|
||||
path.join(ROOT, "webview-ui/agent-manager/review-annotations.ts"),
|
||||
path.join(ROOT, "webview-ui/agent-manager/MultiModelSelector.tsx"),
|
||||
path.join(ROOT, "webview-ui/agent-manager/ApplyDialog.tsx"),
|
||||
path.join(ROOT, "webview-ui/agent-manager/BranchSelect.tsx"),
|
||||
path.join(ROOT, "webview-ui/agent-manager/WorktreeItem.tsx"),
|
||||
]
|
||||
const TSX_FILE = TSX_FILES[0]
|
||||
@@ -171,6 +172,7 @@ describe("Agent Manager Provider — onMessage routing", () => {
|
||||
"agentManager.requestRepoInfo",
|
||||
"agentManager.requestState",
|
||||
"agentManager.setTabOrder",
|
||||
"agentManager.setDefaultBaseBranch",
|
||||
]
|
||||
for (const msg of expected) {
|
||||
expect(text, `onMessage should handle "${msg}"`).toContain(msg)
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { describe, it, expect } from "bun:test"
|
||||
import { normalizeBaseBranch, chooseBaseBranch } from "../../src/agent-manager/base-branch"
|
||||
|
||||
describe("normalizeBaseBranch", () => {
|
||||
it("returns undefined for undefined input", () => {
|
||||
expect(normalizeBaseBranch(undefined)).toBeUndefined()
|
||||
})
|
||||
|
||||
it("returns undefined for empty string", () => {
|
||||
expect(normalizeBaseBranch("")).toBeUndefined()
|
||||
})
|
||||
|
||||
it("returns undefined for whitespace-only string", () => {
|
||||
expect(normalizeBaseBranch(" ")).toBeUndefined()
|
||||
expect(normalizeBaseBranch("\t\n")).toBeUndefined()
|
||||
})
|
||||
|
||||
it("trims whitespace from valid input", () => {
|
||||
expect(normalizeBaseBranch(" main ")).toBe("main")
|
||||
expect(normalizeBaseBranch(" develop\n")).toBe("develop")
|
||||
})
|
||||
|
||||
it("returns the value as-is when already trimmed", () => {
|
||||
expect(normalizeBaseBranch("main")).toBe("main")
|
||||
expect(normalizeBaseBranch("feature/branch")).toBe("feature/branch")
|
||||
})
|
||||
})
|
||||
|
||||
describe("chooseBaseBranch", () => {
|
||||
it("prefers explicit over configured", () => {
|
||||
const result = chooseBaseBranch({ explicit: "release", configured: "develop", configuredExists: true })
|
||||
expect(result).toEqual({ branch: "release" })
|
||||
})
|
||||
|
||||
it("uses configured when it exists and no explicit is given", () => {
|
||||
const result = chooseBaseBranch({ configured: "develop", configuredExists: true })
|
||||
expect(result).toEqual({ branch: "develop" })
|
||||
})
|
||||
|
||||
it("marks configured as stale when it no longer exists", () => {
|
||||
const result = chooseBaseBranch({ configured: "old-branch", configuredExists: false })
|
||||
expect(result).toEqual({ stale: "old-branch" })
|
||||
})
|
||||
|
||||
it("falls back to auto-detect (undefined) when nothing is configured", () => {
|
||||
const result = chooseBaseBranch({})
|
||||
expect(result).toEqual({})
|
||||
})
|
||||
|
||||
it("falls back to auto-detect when configured is undefined", () => {
|
||||
const result = chooseBaseBranch({ configured: undefined, configuredExists: false })
|
||||
expect(result).toEqual({})
|
||||
})
|
||||
|
||||
it("explicit takes priority even when configured is stale", () => {
|
||||
const result = chooseBaseBranch({ explicit: "main", configured: "deleted", configuredExists: false })
|
||||
expect(result).toEqual({ branch: "main" })
|
||||
})
|
||||
})
|
||||
@@ -56,7 +56,6 @@ import { IconButton } from "@kilocode/kilo-ui/icon-button"
|
||||
import { Spinner } from "@kilocode/kilo-ui/spinner"
|
||||
import { Tooltip, TooltipKeybind } from "@kilocode/kilo-ui/tooltip"
|
||||
import { Popover } from "@kilocode/kilo-ui/popover"
|
||||
import { HoverCard } from "@kilocode/kilo-ui/hover-card"
|
||||
import { VSCodeProvider, useVSCode } from "../src/context/vscode"
|
||||
import { ServerProvider } from "../src/context/server"
|
||||
import { ProviderProvider } from "../src/context/provider"
|
||||
@@ -85,6 +84,7 @@ import { FullScreenDiffView } from "./FullScreenDiffView"
|
||||
import { ApplyDialog } from "./ApplyDialog"
|
||||
import { groupApplyConflicts } from "./apply-conflicts"
|
||||
import type { ReviewComment } from "./review-comments"
|
||||
import { BranchSelect } from "./BranchSelect"
|
||||
import { WorktreeItem } from "./WorktreeItem"
|
||||
import "./agent-manager.css"
|
||||
|
||||
@@ -313,6 +313,11 @@ const AgentManagerContent: Component = () => {
|
||||
const [worktreesLoaded, setWorktreesLoaded] = createSignal(false)
|
||||
const [sessionsLoaded, setSessionsLoaded] = createSignal(false)
|
||||
const [isGitRepo, setIsGitRepo] = createSignal(true)
|
||||
const [repoDetectedBranch, setRepoDetectedBranch] = createSignal<string | undefined>()
|
||||
const [defaultBaseBranch, setDefaultBaseBranch] = createSignal<string | undefined>()
|
||||
|
||||
const repoDefaultBranch = () => defaultBaseBranch() ?? repoDetectedBranch() ?? "main"
|
||||
const hasConfiguredBranch = () => !!defaultBaseBranch()
|
||||
|
||||
const DEFAULT_SIDEBAR_WIDTH = 260
|
||||
const MIN_SIDEBAR_WIDTH = 200
|
||||
@@ -1055,6 +1060,7 @@ const AgentManagerContent: Component = () => {
|
||||
if (msg.type === "agentManager.repoInfo") {
|
||||
const info = msg as AgentManagerRepoInfoMessage
|
||||
setRepoBranch(info.branch)
|
||||
if (info.defaultBranch) setRepoDetectedBranch(info.defaultBranch)
|
||||
}
|
||||
|
||||
if (msg.type === "agentManager.worktreeSetup") {
|
||||
@@ -1114,6 +1120,7 @@ const AgentManagerContent: Component = () => {
|
||||
if (state.reviewDiffStyle === "split" || state.reviewDiffStyle === "unified") {
|
||||
setReviewDiffStyle(state.reviewDiffStyle)
|
||||
}
|
||||
if ("defaultBaseBranch" in state) setDefaultBaseBranch(state.defaultBaseBranch || undefined)
|
||||
const current = session.currentSessionID()
|
||||
if (current) {
|
||||
const ms = state.sessions.find((s) => s.id === current)
|
||||
@@ -1412,6 +1419,101 @@ const AgentManagerContent: Component = () => {
|
||||
vscode.postMessage({ type: "agentManager.configureSetupScript" })
|
||||
}
|
||||
|
||||
const handleChangeDefaultBaseBranch = () => {
|
||||
const [search, setSearch] = createSignal("")
|
||||
const [branches, setBranches] = createSignal<BranchInfo[]>([])
|
||||
const [loading, setLoading] = createSignal(true)
|
||||
const [highlighted, setHighlighted] = createSignal(-1)
|
||||
|
||||
const unsub = vscode.onMessage((msg) => {
|
||||
if (msg.type === "agentManager.branches") {
|
||||
const ev = msg as AgentManagerBranchesMessage
|
||||
setBranches(ev.branches)
|
||||
if (ev.defaultBranch) setRepoDetectedBranch(ev.defaultBranch)
|
||||
setLoading(false)
|
||||
}
|
||||
})
|
||||
|
||||
vscode.postMessage({ type: "agentManager.requestBranches" })
|
||||
|
||||
const filtered = createMemo(() => {
|
||||
const s = search().toLowerCase()
|
||||
if (!s) return branches()
|
||||
return branches().filter((b) => b.name.toLowerCase().includes(s))
|
||||
})
|
||||
|
||||
const selectBranch = (name: string | undefined) => {
|
||||
vscode.postMessage({ type: "agentManager.setDefaultBaseBranch", branch: name })
|
||||
setDefaultBaseBranch(name)
|
||||
unsub()
|
||||
dialog.close()
|
||||
}
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
const items = filtered()
|
||||
// offset by 1 for auto-detect option (-1 = auto-detect)
|
||||
const total = items.length + 1
|
||||
if (e.key === "ArrowDown") {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
setHighlighted((prev) => Math.min(prev + 1, total - 2))
|
||||
} else if (e.key === "ArrowUp") {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
setHighlighted((prev) => Math.max(prev - 1, -1))
|
||||
} else if (e.key === "Enter") {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
const idx = highlighted()
|
||||
if (idx === -1) {
|
||||
selectBranch(undefined)
|
||||
} else {
|
||||
const branch = items[idx]
|
||||
if (branch) selectBranch(branch.name)
|
||||
}
|
||||
} else if (e.key === "Escape") {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
unsub()
|
||||
dialog.close()
|
||||
}
|
||||
}
|
||||
|
||||
dialog.show(() => (
|
||||
<Dialog title={t("agentManager.worktree.defaultBaseBranch")} fit>
|
||||
<div class="am-default-base-branch">
|
||||
<BranchSelect
|
||||
branches={filtered()}
|
||||
loading={loading()}
|
||||
search={search()}
|
||||
onSearch={(v) => {
|
||||
setSearch(v)
|
||||
setHighlighted(-1)
|
||||
}}
|
||||
onSelect={(b) => selectBranch(b.name)}
|
||||
onSearchKeyDown={handleKeyDown}
|
||||
selected={defaultBaseBranch()}
|
||||
highlighted={highlighted()}
|
||||
onHighlight={setHighlighted}
|
||||
searchPlaceholder={t("agentManager.dialog.searchBranches")}
|
||||
emptyLabel={t("agentManager.import.noMatchingBranches")}
|
||||
loadingLabel={t("agentManager.import.loadingBranches")}
|
||||
defaultLabel={t("agentManager.dialog.branchBadge.default")}
|
||||
remoteLabel={t("agentManager.dialog.branchBadge.remote")}
|
||||
defaultName={defaultBaseBranch()}
|
||||
autoOption={{
|
||||
label: t("agentManager.worktree.defaultBaseBranchAuto"),
|
||||
hint: repoDetectedBranch(),
|
||||
active: !hasConfiguredBranch(),
|
||||
highlighted: highlighted() === -1,
|
||||
onSelect: () => selectBranch(undefined),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Dialog>
|
||||
))
|
||||
}
|
||||
|
||||
const handleShowKeyboardShortcuts = () => {
|
||||
const categories = buildShortcutCategories(kb(), t)
|
||||
dialog.show(() => (
|
||||
@@ -1449,7 +1551,7 @@ const AgentManagerContent: Component = () => {
|
||||
|
||||
// Advanced worktree dialog — opens a full dialog with prompt, versions, model, mode
|
||||
const showAdvancedWorktreeDialog = () => {
|
||||
dialog.show(() => <NewWorktreeDialog onClose={() => dialog.close()} />)
|
||||
dialog.show(() => <NewWorktreeDialog onClose={() => dialog.close()} defaultBaseBranch={repoDefaultBranch()} />)
|
||||
}
|
||||
|
||||
const confirmDeleteWorktree = (worktreeId: string) => {
|
||||
@@ -1849,6 +1951,12 @@ const AgentManagerContent: Component = () => {
|
||||
<DropdownMenu.Item onSelect={handleConfigureSetupScript}>
|
||||
<DropdownMenu.ItemLabel>{t("agentManager.worktree.setupScript")}</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item onSelect={handleChangeDefaultBaseBranch}>
|
||||
<DropdownMenu.ItemLabel>
|
||||
{t("agentManager.worktree.defaultBaseBranch")}: {repoDefaultBranch()}
|
||||
</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
</DropdownMenu>
|
||||
@@ -2435,7 +2543,7 @@ function sanitizeBranchName(name: string): string {
|
||||
.join("/")
|
||||
}
|
||||
|
||||
const NewWorktreeDialog: Component<{ onClose: () => void }> = (props) => {
|
||||
const NewWorktreeDialog: Component<{ onClose: () => void; defaultBaseBranch?: string }> = (props) => {
|
||||
const { t } = useLanguage()
|
||||
const vscode = useVSCode()
|
||||
const session = useSession()
|
||||
@@ -2445,7 +2553,7 @@ const NewWorktreeDialog: Component<{ onClose: () => void }> = (props) => {
|
||||
// --- Shared branch data (used by both New tab's base branch selector and Import tab) ---
|
||||
const [branches, setBranches] = createSignal<BranchInfo[]>([])
|
||||
const [branchesLoading, setBranchesLoading] = createSignal(false)
|
||||
const [defaultBranch, setDefaultBranch] = createSignal("main")
|
||||
const [defaultBranch, setDefaultBranch] = createSignal(props.defaultBaseBranch ?? "main")
|
||||
const [branchSearch, setBranchSearch] = createSignal("")
|
||||
|
||||
// --- New tab state ---
|
||||
@@ -2552,7 +2660,7 @@ const NewWorktreeDialog: Component<{ onClose: () => void }> = (props) => {
|
||||
if (msg.type === "agentManager.branches") {
|
||||
const ev = msg as AgentManagerBranchesMessage
|
||||
setBranches(ev.branches)
|
||||
setDefaultBranch(ev.defaultBranch)
|
||||
if (!props.defaultBaseBranch) setDefaultBranch(ev.defaultBranch)
|
||||
setBranchesLoading(false)
|
||||
}
|
||||
if (msg.type === "agentManager.importResult") {
|
||||
@@ -2730,98 +2838,69 @@ const NewWorktreeDialog: Component<{ onClose: () => void }> = (props) => {
|
||||
</button>
|
||||
}
|
||||
>
|
||||
<div class="am-dropdown-search">
|
||||
<Icon name="magnifying-glass" size="small" />
|
||||
<input
|
||||
class="am-dropdown-search-input"
|
||||
type="text"
|
||||
placeholder={t("agentManager.dialog.searchBranches")}
|
||||
value={branchSearch()}
|
||||
autofocus
|
||||
onInput={(e) => {
|
||||
setBranchSearch(e.currentTarget.value)
|
||||
setHighlightedIndex(0)
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
const items = filteredBranches()
|
||||
if (e.key === "ArrowDown") {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
const next = Math.min(highlightedIndex() + 1, items.length - 1)
|
||||
setHighlightedIndex(next)
|
||||
requestAnimationFrame(() => {
|
||||
document
|
||||
.querySelector(`.am-branch-item[data-index="${next}"]`)
|
||||
?.scrollIntoView({ block: "nearest" })
|
||||
})
|
||||
} else if (e.key === "ArrowUp") {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
const prev = Math.max(highlightedIndex() - 1, 0)
|
||||
setHighlightedIndex(prev)
|
||||
requestAnimationFrame(() => {
|
||||
document
|
||||
.querySelector(`.am-branch-item[data-index="${prev}"]`)
|
||||
?.scrollIntoView({ block: "nearest" })
|
||||
})
|
||||
} else if (e.key === "Enter") {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
const selected = items[highlightedIndex()]
|
||||
if (selected) {
|
||||
setBaseBranch(selected.name)
|
||||
setBaseBranchOpen(false)
|
||||
setBranchSearch("")
|
||||
setHighlightedIndex(0)
|
||||
}
|
||||
} else if (e.key === "Escape") {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
<BranchSelect
|
||||
branches={filteredBranches()}
|
||||
loading={branchesLoading()}
|
||||
search={branchSearch()}
|
||||
onSearch={(v) => {
|
||||
setBranchSearch(v)
|
||||
setHighlightedIndex(0)
|
||||
}}
|
||||
onSelect={(b) => {
|
||||
setBaseBranch(b.name)
|
||||
setBaseBranchOpen(false)
|
||||
setBranchSearch("")
|
||||
setHighlightedIndex(0)
|
||||
}}
|
||||
onSearchKeyDown={(e) => {
|
||||
const items = filteredBranches()
|
||||
if (e.key === "ArrowDown") {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
const next = Math.min(highlightedIndex() + 1, items.length - 1)
|
||||
setHighlightedIndex(next)
|
||||
requestAnimationFrame(() => {
|
||||
document
|
||||
.querySelector(`.am-branch-item[data-index="${next}"]`)
|
||||
?.scrollIntoView({ block: "nearest" })
|
||||
})
|
||||
} else if (e.key === "ArrowUp") {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
const prev = Math.max(highlightedIndex() - 1, 0)
|
||||
setHighlightedIndex(prev)
|
||||
requestAnimationFrame(() => {
|
||||
document
|
||||
.querySelector(`.am-branch-item[data-index="${prev}"]`)
|
||||
?.scrollIntoView({ block: "nearest" })
|
||||
})
|
||||
} else if (e.key === "Enter") {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
const selected = items[highlightedIndex()]
|
||||
if (selected) {
|
||||
setBaseBranch(selected.name)
|
||||
setBaseBranchOpen(false)
|
||||
setBranchSearch("")
|
||||
setHighlightedIndex(0)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="am-dropdown-list">
|
||||
<For each={filteredBranches()}>
|
||||
{(branch, index) => (
|
||||
<button
|
||||
class="am-branch-item"
|
||||
classList={{
|
||||
"am-branch-item-active": effectiveBaseBranch() === branch.name,
|
||||
"am-branch-item-highlighted": highlightedIndex() === index(),
|
||||
}}
|
||||
data-index={index()}
|
||||
onClick={() => {
|
||||
setBaseBranch(branch.name)
|
||||
setBaseBranchOpen(false)
|
||||
setBranchSearch("")
|
||||
setHighlightedIndex(0)
|
||||
}}
|
||||
onMouseEnter={() => setHighlightedIndex(index())}
|
||||
type="button"
|
||||
>
|
||||
<span class="am-branch-item-left">
|
||||
<Icon name="branch" size="small" />
|
||||
<span class="am-branch-item-name">{branch.name}</span>
|
||||
<Show when={branch.isDefault}>
|
||||
<span class="am-branch-badge">{t("agentManager.dialog.branchBadge.default")}</span>
|
||||
</Show>
|
||||
<Show when={!branch.isLocal && branch.isRemote}>
|
||||
<span class="am-branch-badge am-branch-badge-remote">
|
||||
{t("agentManager.dialog.branchBadge.remote")}
|
||||
</span>
|
||||
</Show>
|
||||
</span>
|
||||
<Show when={branch.lastCommitDate}>
|
||||
<span class="am-branch-item-time">{formatRelativeDate(branch.lastCommitDate!)}</span>
|
||||
</Show>
|
||||
</button>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
} else if (e.key === "Escape") {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
setBaseBranchOpen(false)
|
||||
setBranchSearch("")
|
||||
setHighlightedIndex(0)
|
||||
}
|
||||
}}
|
||||
selected={effectiveBaseBranch()}
|
||||
highlighted={highlightedIndex()}
|
||||
onHighlight={setHighlightedIndex}
|
||||
searchPlaceholder={t("agentManager.dialog.searchBranches")}
|
||||
emptyLabel={t("agentManager.import.noMatchingBranches")}
|
||||
defaultLabel={t("agentManager.dialog.branchBadge.default")}
|
||||
remoteLabel={t("agentManager.dialog.branchBadge.remote")}
|
||||
defaultName={defaultBranch()}
|
||||
/>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
@@ -3000,49 +3079,18 @@ const NewWorktreeDialog: Component<{ onClose: () => void }> = (props) => {
|
||||
</button>
|
||||
}
|
||||
>
|
||||
<div class="am-dropdown-search">
|
||||
<Icon name="magnifying-glass" size="small" />
|
||||
<input
|
||||
class="am-dropdown-search-input"
|
||||
type="text"
|
||||
placeholder={t("agentManager.dialog.searchBranches")}
|
||||
value={branchSearch()}
|
||||
onInput={(e) => setBranchSearch(e.currentTarget.value)}
|
||||
autofocus
|
||||
/>
|
||||
</div>
|
||||
<div class="am-dropdown-list">
|
||||
<Show
|
||||
when={filteredBranches().length > 0}
|
||||
fallback={
|
||||
<div class="am-dropdown-empty">
|
||||
{branchesLoading()
|
||||
? t("agentManager.import.loadingBranches")
|
||||
: t("agentManager.import.noMatchingBranches")}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<For each={filteredBranches()}>
|
||||
{(branch) => (
|
||||
<div class="am-branch-item" onClick={() => handleBranchSelect(branch.name)}>
|
||||
<span class="am-branch-item-left">
|
||||
<Icon name="branch" size="small" />
|
||||
<span class="am-branch-item-name">{branch.name}</span>
|
||||
<Show when={branch.isDefault}>
|
||||
<span class="am-branch-badge">{t("agentManager.dialog.branchBadge.default")}</span>
|
||||
</Show>
|
||||
<Show when={!branch.isLocal && branch.isRemote}>
|
||||
<span class="am-branch-badge">{t("agentManager.dialog.branchBadge.remote")}</span>
|
||||
</Show>
|
||||
</span>
|
||||
<Show when={branch.lastCommitDate}>
|
||||
<span class="am-branch-item-time">{formatRelativeDate(branch.lastCommitDate!)}</span>
|
||||
</Show>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</Show>
|
||||
</div>
|
||||
<BranchSelect
|
||||
branches={filteredBranches().filter((b) => !b.isCheckedOut)}
|
||||
loading={branchesLoading()}
|
||||
search={branchSearch()}
|
||||
onSearch={setBranchSearch}
|
||||
onSelect={(b) => handleBranchSelect(b.name)}
|
||||
searchPlaceholder={t("agentManager.dialog.searchBranches")}
|
||||
loadingLabel={t("agentManager.import.loadingBranches")}
|
||||
emptyLabel={t("agentManager.import.noMatchingBranches")}
|
||||
defaultLabel={t("agentManager.dialog.branchBadge.default")}
|
||||
remoteLabel={t("agentManager.dialog.branchBadge.remote")}
|
||||
/>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
// Reusable branch selector: search input + scrollable list with keyboard navigation
|
||||
|
||||
import { Component, For, Show } from "solid-js"
|
||||
import type { BranchInfo } from "../src/types/messages"
|
||||
import { Icon } from "@kilocode/kilo-ui/icon"
|
||||
import { Spinner } from "@kilocode/kilo-ui/spinner"
|
||||
import { formatRelativeDate } from "../src/utils/date"
|
||||
|
||||
export interface AutoOption {
|
||||
label: string
|
||||
hint?: string
|
||||
active: boolean
|
||||
highlighted?: boolean
|
||||
onSelect: () => void
|
||||
}
|
||||
|
||||
export interface BranchSelectProps {
|
||||
branches: BranchInfo[]
|
||||
loading?: boolean
|
||||
search: string
|
||||
onSearch: (value: string) => void
|
||||
onSelect: (branch: BranchInfo) => void
|
||||
onSearchKeyDown?: (event: KeyboardEvent) => void
|
||||
selected?: string
|
||||
highlighted?: number
|
||||
onHighlight?: (index: number) => void
|
||||
defaultName?: string
|
||||
searchPlaceholder: string
|
||||
loadingLabel?: string
|
||||
emptyLabel: string
|
||||
defaultLabel: string
|
||||
remoteLabel: string
|
||||
autoOption?: AutoOption
|
||||
}
|
||||
|
||||
export const BranchSelect: Component<BranchSelectProps> = (props) => {
|
||||
const isDefault = (branch: BranchInfo) => {
|
||||
if (props.defaultName) return branch.name === props.defaultName
|
||||
return branch.isDefault
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class="am-dropdown-search">
|
||||
<Icon name="magnifying-glass" size="small" />
|
||||
<input
|
||||
class="am-dropdown-search-input"
|
||||
type="text"
|
||||
placeholder={props.searchPlaceholder}
|
||||
value={props.search}
|
||||
autofocus
|
||||
onInput={(e) => props.onSearch(e.currentTarget.value)}
|
||||
onKeyDown={(e) => props.onSearchKeyDown?.(e)}
|
||||
/>
|
||||
</div>
|
||||
<div class="am-dropdown-list">
|
||||
<Show when={props.autoOption}>
|
||||
{(opt) => (
|
||||
<button
|
||||
class="am-branch-item"
|
||||
classList={{
|
||||
"am-branch-item-active": opt().active,
|
||||
"am-branch-item-highlighted": opt().highlighted,
|
||||
}}
|
||||
data-index="auto"
|
||||
onClick={() => opt().onSelect()}
|
||||
type="button"
|
||||
>
|
||||
<span class="am-branch-item-left">
|
||||
<Icon name="branch" size="small" />
|
||||
<span class="am-branch-item-name">{opt().label}</span>
|
||||
</span>
|
||||
<Show when={opt().hint}>
|
||||
<span class="am-branch-hint">{opt().hint}</span>
|
||||
</Show>
|
||||
</button>
|
||||
)}
|
||||
</Show>
|
||||
<Show
|
||||
when={props.branches.length > 0}
|
||||
fallback={
|
||||
<Show when={!props.autoOption}>
|
||||
<div class="am-dropdown-empty">
|
||||
{props.loading ? (props.loadingLabel ?? props.emptyLabel) : props.emptyLabel}
|
||||
</div>
|
||||
</Show>
|
||||
}
|
||||
>
|
||||
<For each={props.branches}>
|
||||
{(branch, index) => (
|
||||
<button
|
||||
class="am-branch-item"
|
||||
classList={{
|
||||
"am-branch-item-active": props.selected === branch.name,
|
||||
"am-branch-item-highlighted": props.highlighted === index(),
|
||||
}}
|
||||
data-index={index()}
|
||||
onClick={() => props.onSelect(branch)}
|
||||
onMouseEnter={() => props.onHighlight?.(index())}
|
||||
type="button"
|
||||
>
|
||||
<span class="am-branch-item-left">
|
||||
<Icon name="branch" size="small" />
|
||||
<span class="am-branch-item-name">{branch.name}</span>
|
||||
<Show when={isDefault(branch)}>
|
||||
<span class="am-branch-badge">{props.defaultLabel}</span>
|
||||
</Show>
|
||||
<Show when={!branch.isLocal && branch.isRemote}>
|
||||
<span class="am-branch-badge am-branch-badge-remote">{props.remoteLabel}</span>
|
||||
</Show>
|
||||
</span>
|
||||
<Show when={branch.lastCommitDate}>
|
||||
<span class="am-branch-item-time">{formatRelativeDate(branch.lastCommitDate!)}</span>
|
||||
</Show>
|
||||
</button>
|
||||
)}
|
||||
</For>
|
||||
</Show>
|
||||
<Show when={props.loading && props.branches.length === 0 && !props.autoOption}>
|
||||
<div class="am-dropdown-empty">
|
||||
<Spinner class="am-setup-spinner" />
|
||||
<span>{props.loadingLabel}</span>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -3132,3 +3132,19 @@ button.am-section-toggle:hover .am-section-label {
|
||||
.am-file-tree-summary-dels {
|
||||
color: var(--syntax-diff-delete, #da3319);
|
||||
}
|
||||
|
||||
/* ============ Default base branch picker dialog ============ */
|
||||
|
||||
.am-default-base-branch {
|
||||
min-width: 360px;
|
||||
}
|
||||
|
||||
.am-default-base-branch .am-dropdown-list {
|
||||
max-height: 320px;
|
||||
}
|
||||
|
||||
.am-branch-hint {
|
||||
margin-left: auto;
|
||||
color: var(--text-weaker);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ export const dict = {
|
||||
"agentManager.worktree.doubleClickRename": "انقر مرتين لإعادة التسمية",
|
||||
"agentManager.worktree.versions": "{{count}} نسخ",
|
||||
"agentManager.worktree.advancedOptions": "خيارات Worktree متقدمة",
|
||||
"agentManager.worktree.defaultBaseBranch": "الفرع الأساسي الافتراضي",
|
||||
"agentManager.worktree.defaultBaseBranchAuto": "اكتشاف تلقائي",
|
||||
"agentManager.hoverCard.branch": "الفرع",
|
||||
"agentManager.hoverCard.base": "الأساس",
|
||||
"agentManager.hoverCard.sessions": "الجلسات",
|
||||
|
||||
@@ -14,6 +14,8 @@ export const dict = {
|
||||
"agentManager.worktree.doubleClickRename": "Clique duplo para renomear",
|
||||
"agentManager.worktree.versions": "{{count}} versões",
|
||||
"agentManager.worktree.advancedOptions": "Opções avançadas de Worktree",
|
||||
"agentManager.worktree.defaultBaseBranch": "Branch base padrão",
|
||||
"agentManager.worktree.defaultBaseBranchAuto": "Detecção automática",
|
||||
"agentManager.hoverCard.branch": "BRANCH",
|
||||
"agentManager.hoverCard.base": "Base",
|
||||
"agentManager.hoverCard.sessions": "Sessões",
|
||||
|
||||
@@ -14,6 +14,8 @@ export const dict = {
|
||||
"agentManager.worktree.doubleClickRename": "Dvostruki klik za preimenovanje",
|
||||
"agentManager.worktree.versions": "{{count}} verzija",
|
||||
"agentManager.worktree.advancedOptions": "Napredne opcije Worktree-a",
|
||||
"agentManager.worktree.defaultBaseBranch": "Zadana bazna grana",
|
||||
"agentManager.worktree.defaultBaseBranchAuto": "Automatsko otkrivanje",
|
||||
"agentManager.hoverCard.branch": "BRANCH",
|
||||
"agentManager.hoverCard.base": "Baza",
|
||||
"agentManager.hoverCard.sessions": "Sesije",
|
||||
|
||||
@@ -14,6 +14,8 @@ export const dict = {
|
||||
"agentManager.worktree.doubleClickRename": "Dobbeltklik for at omdøbe",
|
||||
"agentManager.worktree.versions": "{{count}} versioner",
|
||||
"agentManager.worktree.advancedOptions": "Avancerede Worktree-indstillinger",
|
||||
"agentManager.worktree.defaultBaseBranch": "Standard basisgren",
|
||||
"agentManager.worktree.defaultBaseBranchAuto": "Automatisk registrering",
|
||||
"agentManager.hoverCard.branch": "BRANCH",
|
||||
"agentManager.hoverCard.base": "Base",
|
||||
"agentManager.hoverCard.sessions": "Sessioner",
|
||||
|
||||
@@ -14,6 +14,8 @@ export const dict = {
|
||||
"agentManager.worktree.doubleClickRename": "Doppelklick zum Umbenennen",
|
||||
"agentManager.worktree.versions": "{{count}} Versionen",
|
||||
"agentManager.worktree.advancedOptions": "Erweiterte Worktree-Optionen",
|
||||
"agentManager.worktree.defaultBaseBranch": "Standard-Basiszweig",
|
||||
"agentManager.worktree.defaultBaseBranchAuto": "Automatisch erkennen",
|
||||
"agentManager.hoverCard.branch": "BRANCH",
|
||||
"agentManager.hoverCard.base": "Basis",
|
||||
"agentManager.hoverCard.sessions": "Sitzungen",
|
||||
|
||||
@@ -15,6 +15,8 @@ export const dict = {
|
||||
"agentManager.worktree.doubleClickRename": "Double-click to rename",
|
||||
"agentManager.worktree.versions": "{{count}} versions",
|
||||
"agentManager.worktree.advancedOptions": "Advanced worktree options",
|
||||
"agentManager.worktree.defaultBaseBranch": "Default Base Branch",
|
||||
"agentManager.worktree.defaultBaseBranchAuto": "Auto-detect",
|
||||
|
||||
"agentManager.hoverCard.branch": "BRANCH",
|
||||
"agentManager.hoverCard.base": "Base",
|
||||
@@ -37,7 +39,6 @@ export const dict = {
|
||||
|
||||
"agentManager.setup.failed": "Workspace setup failed",
|
||||
"agentManager.setup.settingUp": "Setting up workspace",
|
||||
|
||||
"agentManager.shortcuts.title": "Keyboard Shortcuts",
|
||||
"agentManager.shortcuts.category.sidebar": "Sidebar",
|
||||
"agentManager.shortcuts.category.tabs": "Tabs",
|
||||
|
||||
@@ -14,6 +14,8 @@ export const dict = {
|
||||
"agentManager.worktree.doubleClickRename": "Doble clic para renombrar",
|
||||
"agentManager.worktree.versions": "{{count}} versiones",
|
||||
"agentManager.worktree.advancedOptions": "Opciones avanzadas de Worktree",
|
||||
"agentManager.worktree.defaultBaseBranch": "Rama base predeterminada",
|
||||
"agentManager.worktree.defaultBaseBranchAuto": "Detección automática",
|
||||
"agentManager.hoverCard.branch": "BRANCH",
|
||||
"agentManager.hoverCard.base": "Base",
|
||||
"agentManager.hoverCard.sessions": "Sesiones",
|
||||
|
||||
@@ -14,6 +14,8 @@ export const dict = {
|
||||
"agentManager.worktree.doubleClickRename": "Double-cliquez pour renommer",
|
||||
"agentManager.worktree.versions": "{{count}} versions",
|
||||
"agentManager.worktree.advancedOptions": "Options avancées du Worktree",
|
||||
"agentManager.worktree.defaultBaseBranch": "Branche de base par défaut",
|
||||
"agentManager.worktree.defaultBaseBranchAuto": "Détection automatique",
|
||||
"agentManager.hoverCard.branch": "BRANCHE",
|
||||
"agentManager.hoverCard.base": "Base",
|
||||
"agentManager.hoverCard.sessions": "Sessions",
|
||||
|
||||
@@ -14,6 +14,8 @@ export const dict = {
|
||||
"agentManager.worktree.doubleClickRename": "ダブルクリックで名前を変更",
|
||||
"agentManager.worktree.versions": "{{count}}バージョン",
|
||||
"agentManager.worktree.advancedOptions": "Worktreeの詳細オプション",
|
||||
"agentManager.worktree.defaultBaseBranch": "デフォルトベースブランチ",
|
||||
"agentManager.worktree.defaultBaseBranchAuto": "自動検出",
|
||||
"agentManager.hoverCard.branch": "ブランチ",
|
||||
"agentManager.hoverCard.base": "ベース",
|
||||
"agentManager.hoverCard.sessions": "セッション",
|
||||
|
||||
@@ -14,6 +14,8 @@ export const dict = {
|
||||
"agentManager.worktree.doubleClickRename": "더블 클릭하여 이름 변경",
|
||||
"agentManager.worktree.versions": "{{count}}개 버전",
|
||||
"agentManager.worktree.advancedOptions": "Worktree 고급 옵션",
|
||||
"agentManager.worktree.defaultBaseBranch": "기본 베이스 브랜치",
|
||||
"agentManager.worktree.defaultBaseBranchAuto": "자동 감지",
|
||||
"agentManager.hoverCard.branch": "브랜치",
|
||||
"agentManager.hoverCard.base": "베이스",
|
||||
"agentManager.hoverCard.sessions": "세션",
|
||||
|
||||
@@ -14,6 +14,8 @@ export const dict = {
|
||||
"agentManager.worktree.doubleClickRename": "Dobbeltklikk for å gi nytt navn",
|
||||
"agentManager.worktree.versions": "{{count}} versjoner",
|
||||
"agentManager.worktree.advancedOptions": "Avanserte Worktree-alternativer",
|
||||
"agentManager.worktree.defaultBaseBranch": "Standard basisgren",
|
||||
"agentManager.worktree.defaultBaseBranchAuto": "Automatisk gjenkjenning",
|
||||
"agentManager.hoverCard.branch": "BRANCH",
|
||||
"agentManager.hoverCard.base": "Base",
|
||||
"agentManager.hoverCard.sessions": "Økter",
|
||||
|
||||
@@ -14,6 +14,8 @@ export const dict = {
|
||||
"agentManager.worktree.doubleClickRename": "Kliknij dwukrotnie, aby zmienić nazwę",
|
||||
"agentManager.worktree.versions": "{{count}} wersji",
|
||||
"agentManager.worktree.advancedOptions": "Zaawansowane opcje Worktree",
|
||||
"agentManager.worktree.defaultBaseBranch": "Domyślna gałąź bazowa",
|
||||
"agentManager.worktree.defaultBaseBranchAuto": "Automatyczne wykrywanie",
|
||||
"agentManager.hoverCard.branch": "BRANCH",
|
||||
"agentManager.hoverCard.base": "Baza",
|
||||
"agentManager.hoverCard.sessions": "Sesje",
|
||||
|
||||
@@ -14,6 +14,8 @@ export const dict = {
|
||||
"agentManager.worktree.doubleClickRename": "Двойной клик для переименования",
|
||||
"agentManager.worktree.versions": "{{count}} версий",
|
||||
"agentManager.worktree.advancedOptions": "Расширенные настройки Worktree",
|
||||
"agentManager.worktree.defaultBaseBranch": "Базовая ветка по умолчанию",
|
||||
"agentManager.worktree.defaultBaseBranchAuto": "Автоопределение",
|
||||
"agentManager.hoverCard.branch": "ВЕТКА",
|
||||
"agentManager.hoverCard.base": "Основа",
|
||||
"agentManager.hoverCard.sessions": "Сессии",
|
||||
|
||||
@@ -14,6 +14,8 @@ export const dict = {
|
||||
"agentManager.worktree.doubleClickRename": "ดับเบิลคลิกเพื่อเปลี่ยนชื่อ",
|
||||
"agentManager.worktree.versions": "{{count}} เวอร์ชัน",
|
||||
"agentManager.worktree.advancedOptions": "ตัวเลือก Worktree ขั้นสูง",
|
||||
"agentManager.worktree.defaultBaseBranch": "สาขาฐานเริ่มต้น",
|
||||
"agentManager.worktree.defaultBaseBranchAuto": "ตรวจจับอัตโนมัติ",
|
||||
"agentManager.hoverCard.branch": "BRANCH",
|
||||
"agentManager.hoverCard.base": "ฐาน",
|
||||
"agentManager.hoverCard.sessions": "เซสชัน",
|
||||
|
||||
@@ -14,6 +14,8 @@ export const dict = {
|
||||
"agentManager.worktree.doubleClickRename": "双击重命名",
|
||||
"agentManager.worktree.versions": "{{count}} 个版本",
|
||||
"agentManager.worktree.advancedOptions": "Worktree 高级选项",
|
||||
"agentManager.worktree.defaultBaseBranch": "默认基础分支",
|
||||
"agentManager.worktree.defaultBaseBranchAuto": "自动检测",
|
||||
"agentManager.hoverCard.branch": "分支",
|
||||
"agentManager.hoverCard.base": "基础",
|
||||
"agentManager.hoverCard.sessions": "会话",
|
||||
|
||||
@@ -14,6 +14,8 @@ export const dict = {
|
||||
"agentManager.worktree.doubleClickRename": "按兩下以重新命名",
|
||||
"agentManager.worktree.versions": "{{count}} 個版本",
|
||||
"agentManager.worktree.advancedOptions": "Worktree 進階選項",
|
||||
"agentManager.worktree.defaultBaseBranch": "預設基礎分支",
|
||||
"agentManager.worktree.defaultBaseBranchAuto": "自動偵測",
|
||||
"agentManager.hoverCard.branch": "分支",
|
||||
"agentManager.hoverCard.base": "基底",
|
||||
"agentManager.hoverCard.sessions": "工作階段",
|
||||
|
||||
@@ -626,6 +626,7 @@ export interface AgentManagerSessionMetaMessage {
|
||||
export interface AgentManagerRepoInfoMessage {
|
||||
type: "agentManager.repoInfo"
|
||||
branch: string
|
||||
defaultBranch?: string
|
||||
}
|
||||
|
||||
// Agent Manager worktree setup progress
|
||||
@@ -674,6 +675,7 @@ export interface AgentManagerStateMessage {
|
||||
sessionsCollapsed?: boolean
|
||||
reviewDiffStyle?: "unified" | "split"
|
||||
isGitRepo?: boolean
|
||||
defaultBaseBranch?: string
|
||||
}
|
||||
|
||||
// Resolved keybindings for agent manager actions
|
||||
@@ -703,6 +705,7 @@ export interface BranchInfo {
|
||||
isRemote: boolean
|
||||
isDefault: boolean
|
||||
lastCommitDate?: string
|
||||
isCheckedOut?: boolean
|
||||
}
|
||||
|
||||
export interface AgentManagerBranchesMessage {
|
||||
@@ -1450,6 +1453,12 @@ export interface EnhancePromptRequest {
|
||||
requestId: string
|
||||
}
|
||||
|
||||
// Set default base branch (webview → extension)
|
||||
export interface SetDefaultBaseBranchRequest {
|
||||
type: "agentManager.setDefaultBaseBranch"
|
||||
branch?: string
|
||||
}
|
||||
|
||||
export type WebviewMessage =
|
||||
| SendMessageRequest
|
||||
| AbortRequest
|
||||
@@ -1532,6 +1541,7 @@ export type WebviewMessage =
|
||||
// legacy-migration end
|
||||
| ApplyWorktreeDiffMessage
|
||||
| EnhancePromptRequest
|
||||
| SetDefaultBaseBranchRequest
|
||||
|
||||
// ============================================
|
||||
// VS Code API type
|
||||
|
||||
Reference in New Issue
Block a user