* feat(task): add per-group minimize state to dockview store
Adds `minimizedGroupIds: Set<string>` plus `toggleGroupMinimized` and
`clearMinimizedGroups` actions. The set is cleared on maximize,
exit-maximize, preset apply, custom layout apply, env switch,
default-layout rebuild, and release-to-default so minimize state never
leaks across task environments and never overlaps with maximize.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(task): add minimize button to dockview group header
Adds an IconMinus toggle button alongside Maximize/Split/Close in each
non-chat dockview group header. Clicking it adds the group ID to the
store's `minimizedGroupIds`; a new `useMinimizedGroupSync` hook walks
`api.groups`, stamps a `data-kandev-minimized` attribute on each group
element, and calls `group.api.setSize` to shrink the group to the
splitview minimum (the freed space redistributes to siblings). A CSS
rule hides the content container so only the tab strip remains visible.
The hook also auto-restores a minimized group when a new panel is added
to it (e.g. via the `+` menu) and evicts removed groups from the set.
Clicking Minimize while maximized first exits maximize so the user
doesn't end up with a header-only view.
In wide mode the button is inline; in narrow mode it folds into the
existing overflow dropdown as its first item. Chat group never sees the
button (same exclusion as Close).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test(task): tighten minimize-icon test, drop unused TID
The previous "renders Restore tooltip and icon swap" case had two weak
assertions: `getAttribute(...) !== null || btn !== null` is
tautologically true, and `btn.innerHTML.toContain("svg")` matches both
icon variants. Replaced with two focused tests that assert the Tabler
class differs between the minimized and non-minimized state. Also
dropped the unused `minimizeMenuItem` entry from the TID map.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(task): minimize-from-maximize used stale group ID
`handleMinimize` previously called `toggleGroupMinimized(group.id)`
after `exitMaximizedLayout()`. During maximize, `group.id` is the
ephemeral overlay group's ID — once the original layout is restored
that ID is gone from `api.groups`, and the minimize was silently lost.
Read `maximizedGroupId` (the pre-maximize group ID stored at maximize
time) from the store and target that instead.
Reported on PR #1039 by claude-review, CodeRabbit, and Greptile.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* perf(task): skip redundant data-attribute writes in minimize sync
The sync hook re-runs on every `onDidAddGroup` / `onDidAddPanel`, which
previously rewrote `data-kandev-minimized` on every group regardless of
whether the value actually changed. Guard with a getAttribute-before-set
check.
Reported on PR #1039 by claude-review.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test(task): cover useMinimizedGroupSync hook
Adds 11 unit tests for the hook: mount-time attribute stamping, null-api
no-op, minimize/restore round-trip with prior-size replay, auto-restore
on panel add into a minimized group, no auto-restore for unrelated
groups, group-removal eviction from the store and prior-size map, fresh
size on re-added group with the same ID, sync of newly added groups,
the perf guard on redundant attribute writes, and clean unmount.
Reported on PR #1039 by claude-review as a blocker (CLAUDE.md mandates
tests for new hooks).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(task): defer minimize toggle to next RAF when exiting maximize
`exitMaximizedLayout` schedules `api.layout(w, h)` in a requestAnimation
Frame after the synchronous fromJSON rebuild. Calling `toggleGroup
Minimized` immediately afterwards races with that layout pass, and the
resulting `setSize({ width: 0, height: 0 })` could be overridden — the
content was still hidden by CSS but the group kept its full splitview
footprint. Defer the toggle one frame so the maximize-exit settles
first.
Reported on PR #1039 by claude-review, Greptile, and cubic.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(task): guard deferred minimize against env-switch + redundant setSize
Two small polish fixes from PR #1039 review:
- The RAF callback in `handleMinimize` now verifies the target group
still exists in `api.groups` before toggling. Within the frame, an
env-switch (or another maximize) could clear `minimizedGroupIds`;
blindly re-adding a stale ID would leave it in the set, and if the
next env happens to reuse the same well-known constant ID (e.g.
"right-top"), the wrong group would render as minimized.
(cubic P2 finding.)
- `applyGroupMinimized` now early-returns when re-syncing an already-
minimized group (`priorSizes.has(groupId)`), instead of re-emitting
setSize(0,0) and its size-change events. New test covers this.
(claude-review suggestion.)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* feat(web): track active file path for diff panels
Extend resolveFilePath so activeFilePath updates when a file-diff panel
(preview:file-diff or diff:file:<path>) becomes the active dockview
panel. Also re-subscribe onDidParametersChange for the preview file-diff
slot so swapping the preview to a different file refreshes the path.
* feat(web): highlight active-tab file row in Changes panel
When a file's diff tab is the active dockview panel, mark the matching
row in unstaged, staged, and PR Changes lists with data-active="true"
and reuse the existing bg-accent/60 highlight (same as multi-select).
PRFileRow also gains data-changes-file so it shares the existing row
selector with FileRow.
* test(web): cover active-tab highlight unit + E2E
Unit test in FileRow guards the data-active attribute and bg-accent/60
class when isActive (with and without isSelected). New E2E spec drives
the full flow: open file-a diff, switch to file-b, close the preview,
and verify highlight follows when a file moves from unstaged to staged.
* fix(web): use distinct e2e task titles for active-tab highlight spec
clickTab("Changes") fails strict-mode when the task title also contains
the word "Changes" — Playwright matches both the session tab and the
dockview Changes tab. Renames to "Active Highlight (Staged) Test".
* test(web): close pinned diff:file panels in closeFileDiffPreview
The helper previously closed only the preview slot, leaving pinned
diff:file:<path> panels (created when a preview is promoted) active.
Sweeps every diff:file: panel so the row settles to data-active=false
regardless of how the diff was opened.
* test(web): unit-cover resolveFilePath panel-id branches
Exercises every branch of the dockview-store resolveFilePath closure:
file:, diff:file:, preview:file-editor, and preview:file-diff (the
last two via params.path), plus the null-out paths. Catches regressions
in the panel-id mapping without paying for an E2E round-trip.
* fix(web): strip "changes" from active-tab spec profile names
The agent profile name becomes the dockview session tab title, so a
profile named "changes-active-highlight" makes clickTab("Changes")
match two .dv-default-tab elements (session tab + Changes tab) and
trip strict-mode. Rename to "active-highlight" / "active-highlight-
staged" to keep the locator unambiguous.
* docs(web): note multi-repo path collision in PRFilesRepoGroup
Mirrors the FileListBody comment so future authors see the same caveat
in both file-list components: activeFilePath carries the path but not
the repo, so identical paths across repos light up both rows.
* fix(web): snapshot panels before closing in closeFileDiffPreview
dockview's api.panels is a live reference; calling panel.api.close()
removes the panel in place, so iterating the live array skipped every
other diff:file:* panel when two or more were open. Filter into a
local array before closing.