Skip to content

feat(vscode): persist diff viewer unified/split style - #14322

Merged
marius-kilocode merged 4 commits into
Kilo-Org:mainfrom
BambinoSK:feat/persist-diff-style
Sep 21, 2026
Merged

marius-kilocode merged 4 commits into
Kilo-Org:mainfrom
BambinoSK:feat/persist-diff-style

Conversation

@BambinoSK

@BambinoSK BambinoSK commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

feat(vscode): persist diff viewer unified/split style

Issue

Fixes #14321

Context

The unified/split choice in Kilo's diff viewers is not persisted anywhere (#14321):

  • The lightweight diff tab (e.g. expanding an edit approval from the permission dock) holds the style in a local signal — the choice is lost when the tab closes or a new diff opens.
  • The full diff viewer does post a diffViewer.setDiffStyle message when toggled (the client side was added in fix: default edit-tool diff to side-by-side (split) view #9576), but the host handler was an empty stub, so nothing was ever stored. It also force-resets to unified whenever the source or PR identity changes.
  • The only place the choice survives today is the Agent Manager review, persisted per-worktree — the main diff viewers don't share it.

The sibling toggle in the same toolbar (Markdown render) was persisted as kilo-code.new.diff.renderMarkdown in #9846; this applies the same pattern to the style toggle.

Implementation

Adds a kilo-code.new.diff.style setting (unified | split):

  • Toggling the RadioGroup in the lightweight diff tab (diffVirtual.setDiffStyle) or the full diff viewer (diffViewer.setDiffStyle, previously a no-op stub) writes the choice to global settings.
  • Both viewers seed their style from the setting on open, and the full viewer no longer force-resets to unified on source/PR change.

Design note: the setting intentionally has no default, so "never toggled" is distinguishable from "unified". The read uses the effective configuration via get(), so a workspace override applies, matching the other Kilo settings. The per-surface defaults from #9576 (split for edit-tool diffs, unified for the permission dock) keep working until the user makes an explicit choice.

Screenshots / Video

N/A — no visual layout change; the toggle already existed. (The only visible difference is kilo-code.new.diff.style appearing in Settings after the first toggle.)

How to Test

Manual/local verification

  • From packages/kilo-vscode/: bun run typecheck, bun run lint, bun run knip — all pass.
  • bun test tests/unit/diff-style-persistence.test.ts — 4 new tests pass; bun test tests/unit/databridge-shape.test.ts tests/unit/diff-viewer-provider.test.ts tests/unit/extension-arch.test.ts — pass (all run by me as the PR author, on Windows 11).
  • Manually in a dev host (bun run extension:isolated): opened an edit-approval diff, toggled to split, verified kilo-code.new.diff.style = split in Settings; subsequent diff viewers open split; toggling back to unified persists; clearing the setting restores the pre-PR per-surface defaults.

Reviewer test steps

  1. bun run extension:isolated from packages/kilo-vscode/
  2. Ask the agent to edit a file; when the approval diff opens, toggle Unified → Split
  3. Check VS Code Settings: kilo-code.new.diff.style is now split
  4. Trigger another edit approval — the diff opens split without touching the toggle
  5. Toggle back to unified; the next diff opens unified

Blocked checks and substitute verification

  • bun run compile / bun run package fails at the ffmpeg-helper npm pack step on my Windows machine — reproduced on clean main, unrelated to this change. Substitute: the esbuild bundle (bun run bundle) builds and contains the new handlers; package-level typecheck/lint/tests all pass.
  • The 4 kilo-ui-contract runtime-import test failures seen locally are pre-existing on clean main (Windows-specific) and reproduce identically without this change.

Checklist

  • Issue linked above, or exception explained
  • Tests/verification described
  • Screenshots/video included for visual changes, or marked N/A
  • Changeset considered for user-facing changes
  • I personally reviewed the diff and can explain the changes, including any AI-assisted work.

Remember the user's unified/split choice across diff viewers. Toggling the
style in the full diff viewer or the lightweight diff tab writes it to
kilo-code.new.diff.style; viewers open with that choice once set, and
source/PR switches no longer force-reset to unified. No setting is written
until the first toggle, so per-surface defaults (split for edit-tool diffs,
unified for the permission dock) keep working.
if (prev === undefined || id === prev) return
setComments([])
setDiffStyle("unified")
setDiffStyle(savedDiffStyle() ?? "unified")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[WARNING]: savedDiffStyle is never updated when the user toggles the style, so a source/PR switch can undo a just-made choice.

The toggle handler (onDiffStyleChange, around line 434) only calls setDiffStyle(style) and posts diffViewer.setDiffStyle — it never calls setSavedDiffStyle(style). The signal is populated only by the host's diffViewer.initialDiffStyle message on webviewReady, which is sent once per panel. So on a fresh panel savedDiffStyle() is still undefined right after the user toggles to split, and switching the source (or PR identity) reaches this line and resets to savedDiffStyle() ?? "unified" — the exact force-reset this PR set out to remove. Conversely, if a style was persisted from a previous session, toggling the opposite way then switching source reverts to the stale persisted value rather than the user's latest choice. Consider also calling setSavedDiffStyle(style) in the toggle handler (the host could echo the value back for consistency).


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in a21753b — the toggle handler now also updates savedDiffStyle.


test("getUserDiffStyle reads the global layer, not workspace-local overrides", () => {
installConfig({ "diff.style": "split" })
expect(getUserDiffStyle()).toBe("split")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SUGGESTION]: This test does not exercise what its name claims. The name says "not workspace-local overrides", but the mock's inspect always returns { globalValue: store[key] } and never exposes a workspaceValue, so no workspace-layer value exists to be ignored. The assertion would pass even if getUserDiffStyle preferred a workspace value, making this effectively a duplicate of the second test. To actually cover the behavior, have the mock return a workspaceValue that differs from globalValue and assert the global one wins.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in a21753b — the test now mocks a differing workspaceValue and asserts the global layer wins

@kilo-code-bot

kilo-code-bot Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (2 files)
  • packages/kilo-vscode/src/review-settings.ts - getUserDiffStyle now reads the effective configuration (get) so a workspace override applies; consistent with the sibling getDiffMarkdownRender reader
  • packages/kilo-vscode/tests/unit/diff-style-persistence.test.ts - mock now exposes a differing workspace layer and the test asserts the workspace override wins, so it exercises the new behavior instead of duplicating the round-trip case

Prior findings re-verified at 2ec95fe970: the savedDiffStyle toggle-tracking fix (DiffViewerApp.tsx:436) and the workspace-layer test mock are both present and correct.

Previous Review Summaries (3 snapshots, latest commit cb42e41)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit cb42e41)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (1 file)
  • packages/kilo-vscode/tests/unit/diff-style-persistence.test.ts - previous suggestion resolved: installConfig now accepts a workspace-layer map and the mock exposes a differing workspaceValue, so the test genuinely asserts the global layer wins.

Previous review (commit a21753b)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1

Fix these issues in Kilo Cloud

Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/kilo-vscode/tests/unit/diff-style-persistence.test.ts 51 The added workspace-layer mock is overwritten by installConfig(...) on the next line, so workspaceValue is still never observed and the test still does not assert that workspace-local overrides are ignored.
Files Reviewed (2 files)
  • packages/kilo-vscode/tests/unit/diff-style-persistence.test.ts - 1 issue
  • packages/kilo-vscode/webview-ui/diff-viewer/DiffViewerApp.tsx - fixed (toggle now updates savedDiffStyle)

Previous review (commit 4fb3e2e)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1

Fix these issues in Kilo Cloud

Issue Details (click to expand)

WARNING

File Line Issue
packages/kilo-vscode/webview-ui/diff-viewer/DiffViewerApp.tsx 324 savedDiffStyle is never updated on toggle, so a source/PR switch resets to a stale or default style instead of the user's latest choice.

SUGGESTION

File Line Issue
packages/kilo-vscode/tests/unit/diff-style-persistence.test.ts 50 Test named "not workspace-local overrides" never exposes a workspace-layer value, so it does not assert the behavior it claims.
Files Reviewed (10 files)
  • .changeset/persist-diff-style.md
  • packages/kilo-vscode/package.json
  • packages/kilo-vscode/src/DiffVirtualProvider.ts
  • packages/kilo-vscode/src/diff/DiffViewerProvider.ts
  • packages/kilo-vscode/src/review-settings.ts
  • packages/kilo-vscode/tests/unit/diff-style-persistence.test.ts - 1 issue
  • packages/kilo-vscode/webview-ui/diff-viewer/DiffViewerApp.tsx - 1 issue
  • packages/kilo-vscode/webview-ui/diff-virtual/DiffVirtualApp.tsx
  • packages/kilo-vscode/webview-ui/src/types/messages/extension-messages.ts
  • packages/kilo-vscode/webview-ui/src/types/messages/webview-messages.ts

Reviewed by deepseek-v4.1-flash · Input: 0 · Output: 0 · Cached: 0

Review guidance: REVIEW.md from base branch main

Track the toggle in savedDiffStyle so a source or PR switch uses the
user's latest choice instead of a stale persisted value. Also make the
global-layer test exercise a differing workspace value.
test("getUserDiffStyle reads the global layer, not workspace-local overrides", () => {
// Mock a workspace-level value differing from the global one; only the
// global layer may win, matching the inspect().globalValue read.
;(vscode.workspace as unknown as { getConfiguration: typeof vscode.workspace.getConfiguration }).getConfiguration =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: The workspace-layer mock added here is overwritten before it can ever run.

The installConfig(...) call on the next line reassigns vscode.workspace.getConfiguration to its own mock, whose inspect only ever returns { globalValue }. So the custom mock installed at lines 51–59 is dead code and workspaceValue is still never observed by getUserDiffStyle. The test passes, but for the wrong reason: it remains a duplicate of the second test and still does not assert that workspace-local overrides are ignored, so the earlier concern is not actually resolved. Move the installConfig(...) call above this mock (or fold a differing workspaceValue into installConfig's own mock) so the workspace value is actually visible.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in cb42e41 — installConfig now exposes a workspace layer via a second map, so the differing workspaceValue is actually observed by the test

BambinoSK and others added 2 commits September 19, 2026 22:50
installConfig now takes a separate workspace-layer map so the global-
layer test's differing workspaceValue is actually observed instead of
being overwritten by the helper's own mock.

@marius-kilocode marius-kilocode left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix, this works well. I verified the persistence in an isolated dev host and adjusted the read to use the effective configuration via get(), so a workspace override applies like the other Kilo settings. Looks good to merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

VS Code diff viewers do not remember the unified/split choice

2 participants