fix(agent-manager): expand reviewable diffs by default (#10055)

* fix(agent-manager): expand reviewable diffs by default

* style(agent-manager): format diff panel
This commit is contained in:
Marius
2026-05-08 12:06:24 +02:00
committed by GitHub
parent 09a7be8fcd
commit f9fa1a7efd
5 changed files with 45 additions and 40 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Open reviewable Agent Manager diffs by default while keeping generated or extreme files collapsed.
@@ -1,6 +1,10 @@
import { describe, expect, it } from "bun:test"
import { mergeWorktreeDiffs } from "../../webview-ui/agent-manager/diff-state"
import { initialOpenFiles } from "../../webview-ui/agent-manager/diff-open-policy"
import {
EXTREME_DIFF_CHANGED_LINES,
expandableOpenFiles,
initialOpenFiles,
} from "../../webview-ui/agent-manager/diff-open-policy"
import type { WorktreeFileDiff } from "../../webview-ui/src/types/messages"
function diff(overrides: Partial<WorktreeFileDiff>): WorktreeFileDiff {
@@ -48,15 +52,26 @@ describe("agent manager diff state", () => {
expect(result.stale).toEqual(new Set(["src/app.ts"]))
})
it("does not auto-open generated-like files or large diff sets", () => {
it("opens reviewable diffs initially", () => {
expect(
initialOpenFiles([
diff({ file: "src/app.ts", generatedLike: false, additions: 3 }),
diff({ file: "node_modules/pkg/index.js", generatedLike: true, additions: 3 }),
diff({ file: "src/huge.ts", additions: EXTREME_DIFF_CHANGED_LINES + 1 }),
]),
).toEqual(["src/app.ts"])
const many = Array.from({ length: 26 }, (_, i) => diff({ file: `src/${i}.ts` }))
expect(initialOpenFiles(many)).toEqual([])
expect(initialOpenFiles(many)).toHaveLength(26)
})
it("expands only reviewable files from the bulk action", () => {
expect(
expandableOpenFiles([
diff({ file: "src/app.ts", generatedLike: false, additions: 3 }),
diff({ file: "src/generated.ts", generatedLike: true, additions: 3 }),
diff({ file: "src/huge.ts", additions: EXTREME_DIFF_CHANGED_LINES + 1 }),
]),
).toEqual(["src/app.ts"])
})
})
@@ -21,7 +21,7 @@ import {
type AnnotationLabels,
type AnnotationMeta,
} from "./review-annotations"
import { LONG_DIFF_MARKER_FILE_COUNT, initialOpenFiles, isLargeDiffFile } from "./diff-open-policy"
import { LONG_DIFF_MARKER_FILE_COUNT, expandableOpenFiles, initialOpenFiles, isLargeDiffFile } from "./diff-open-policy"
import { DiffEndMarker } from "./DiffEndMarker"
import { treeOrder } from "./file-tree-utils"
import { isMarkdownFile, MarkdownDiffView } from "./MarkdownDiffView"
@@ -71,8 +71,8 @@ export const DiffPanel: Component<DiffPanelProps> = (props) => {
)
const [editing, setEditing] = createSignal<string | null>(null)
let nextId = 0
// Tracks the session key for which auto-open has already run. When the
// key changes (different worktree) we re-expand. Within the same key,
// Tracks the session key for which initial open state has already run. When the
// key changes (different worktree) we expand reviewable files. Within the same key,
// only pruning happens so the user's manual collapse state is preserved.
let initializedKey: string | undefined
@@ -132,9 +132,9 @@ export const DiffPanel: Component<DiffPanelProps> = (props) => {
focusRoot()
}
// Unified auto-open effect: tracks both sessionKey and diffs in a single effect
// Unified open-state effect: tracks both sessionKey and diffs in a single effect
// to eliminate the race condition between the old separate sessionKey-reset and
// diffs-watch effects. Uses the session key to decide when auto-expand is needed
// diffs-watch effects. Uses the session key to decide when initialization is needed
// vs when we just prune stale entries from the open list.
createEffect(
on(
@@ -321,8 +321,7 @@ export const DiffPanel: Component<DiffPanelProps> = (props) => {
}
const handleExpandAll = () => {
const allOpen = open().length === props.diffs.length
setOpen(allOpen ? [] : props.diffs.map((d) => d.file))
setOpen(open().length > 0 ? [] : expandableOpenFiles(props.diffs))
}
const totals = createMemo(() => ({
@@ -374,22 +373,14 @@ export const DiffPanel: Component<DiffPanelProps> = (props) => {
<div class="am-diff-header-actions">
<Show when={props.diffs.length > 0}>
<Tooltip
value={
open().length === props.diffs.length
? t("ui.sessionReview.collapseAll")
: t("ui.sessionReview.expandAll")
}
value={open().length > 0 ? t("ui.sessionReview.collapseAll") : t("ui.sessionReview.expandAll")}
placement="bottom"
>
<IconButton
icon="chevron-grabber-vertical"
size="small"
variant="ghost"
label={
open().length === props.diffs.length
? t("ui.sessionReview.collapseAll")
: t("ui.sessionReview.expandAll")
}
label={open().length > 0 ? t("ui.sessionReview.collapseAll") : t("ui.sessionReview.expandAll")}
onClick={handleExpandAll}
/>
</Tooltip>
@@ -29,7 +29,7 @@ import {
type AnnotationLabels,
type AnnotationMeta,
} from "./review-annotations"
import { LONG_DIFF_MARKER_FILE_COUNT, initialOpenFiles, isLargeDiffFile } from "./diff-open-policy"
import { LONG_DIFF_MARKER_FILE_COUNT, expandableOpenFiles, initialOpenFiles, isLargeDiffFile } from "./diff-open-policy"
import { DiffEndMarker } from "./DiffEndMarker"
import { isMarkdownFile, MarkdownDiffView } from "./MarkdownDiffView"
@@ -84,8 +84,8 @@ export const FullScreenDiffView: Component<FullScreenDiffViewProps> = (props) =>
const [treeWidth, setTreeWidth] = createSignal(240)
let nextId = 0
let draftMeta: AnnotationMeta | null = null
// Tracks the session key for which auto-open has already run. When the
// key changes (different worktree) we re-expand. Within the same key,
// Tracks the session key for which initial open state has already run. When the
// key changes (different worktree) we expand reviewable files. Within the same key,
// only pruning happens so the user's manual collapse state is preserved.
let initializedKey: string | undefined
let rootRef: HTMLDivElement | undefined
@@ -135,9 +135,9 @@ export const FullScreenDiffView: Component<FullScreenDiffViewProps> = (props) =>
focusRoot()
}
// Unified auto-open effect: tracks both sessionKey and diffs in a single effect
// Unified open-state effect: tracks both sessionKey and diffs in a single effect
// to eliminate the race condition between the old separate sessionKey-reset and
// diffs-watch effects. Uses the session key to decide when auto-expand is needed
// diffs-watch effects. Uses the session key to decide when initialization is needed
// vs when we just prune stale entries from the open list.
createEffect(
on(
@@ -349,8 +349,7 @@ export const FullScreenDiffView: Component<FullScreenDiffViewProps> = (props) =>
}
const handleExpandAll = () => {
const allOpen = open().length === props.diffs.length
setOpen(allOpen ? [] : props.diffs.map((d) => d.file))
setOpen(open().length > 0 ? [] : expandableOpenFiles(props.diffs))
}
const syncActiveFileFromScroll = () => {
@@ -456,7 +455,7 @@ export const FullScreenDiffView: Component<FullScreenDiffViewProps> = (props) =>
<div class="am-review-toolbar-right">
<Button size="small" variant="ghost" onClick={handleExpandAll}>
<Icon name="chevron-grabber-vertical" size="small" />
{open().length === props.diffs.length ? t("ui.sessionReview.collapseAll") : t("ui.sessionReview.expandAll")}
{open().length > 0 ? t("ui.sessionReview.collapseAll") : t("ui.sessionReview.expandAll")}
</Button>
<Show when={comments().length > 0 && props.canComment !== false}>
<TooltipKeybind
@@ -1,21 +1,16 @@
import type { WorktreeFileDiff } from "../src/types/messages"
export const LONG_DIFF_MARKER_FILE_COUNT = 50
const AUTO_OPEN_FILE_COUNT = 25
const AUTO_OPEN_LIMIT = 8
const LARGE_FILE_CHANGED_LINES = 400
export const EXTREME_DIFF_CHANGED_LINES = 2_000
export function isLargeDiffFile(diff: WorktreeFileDiff): boolean {
return diff.additions + diff.deletions > LARGE_FILE_CHANGED_LINES
return diff.additions + diff.deletions > EXTREME_DIFF_CHANGED_LINES
}
export function expandableOpenFiles(diffs: WorktreeFileDiff[]): string[] {
return diffs.filter((diff) => !isLargeDiffFile(diff) && diff.generatedLike !== true).map((diff) => diff.file)
}
export function initialOpenFiles(diffs: WorktreeFileDiff[]): string[] {
if (diffs.length === 0) return []
if (diffs.length > AUTO_OPEN_FILE_COUNT) return []
const files = diffs
.filter((diff) => !isLargeDiffFile(diff) && diff.generatedLike !== true)
.slice(0, AUTO_OPEN_LIMIT)
.map((diff) => diff.file)
return files
return expandableOpenFiles(diffs)
}