7771155c8f
* 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>
211 lines
5.6 KiB
CSS
211 lines
5.6 KiB
CSS
/* Dockview workbench chrome */
|
|
|
|
.dockview-theme-kandev {
|
|
--dv-background-color: var(--card);
|
|
--dv-group-view-background-color: var(--background);
|
|
|
|
--dv-separator-border: var(--border);
|
|
--dv-active-sash-color: var(--primary);
|
|
--dv-paneview-header-border-color: var(--border);
|
|
|
|
--dv-tabs-and-actions-container-font-size: 12px;
|
|
--dv-tabs-and-actions-container-height: 40px;
|
|
--dv-tabs-and-actions-container-background-color: var(--background);
|
|
|
|
--dv-activegroup-visiblepanel-tab-background-color: var(--muted);
|
|
--dv-activegroup-visiblepanel-tab-color: var(--foreground);
|
|
|
|
--dv-activegroup-hiddenpanel-tab-background-color: transparent;
|
|
--dv-activegroup-hiddenpanel-tab-color: var(--muted-foreground);
|
|
|
|
--dv-inactivegroup-visiblepanel-tab-background-color: color-mix(
|
|
in srgb,
|
|
var(--muted) 100%,
|
|
transparent
|
|
);
|
|
--dv-inactivegroup-visiblepanel-tab-color: color-mix(
|
|
in srgb,
|
|
var(--muted-foreground) 100%,
|
|
transparent
|
|
);
|
|
--dv-inactivegroup-hiddenpanel-tab-background-color: transparent;
|
|
--dv-inactivegroup-hiddenpanel-tab-color: color-mix(
|
|
in srgb,
|
|
var(--muted-foreground) 100%,
|
|
transparent
|
|
);
|
|
|
|
--dv-tab-close-icon: var(--muted-foreground);
|
|
--dv-tabs-container-scrollbar-color: var(--scrollbar-thumb);
|
|
--dv-drag-over-background-color: color-mix(in oklab, var(--primary) 10%, transparent);
|
|
--dv-drag-over-border-color: var(--primary);
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-tabs-and-actions-container {
|
|
background: var(--background);
|
|
border-bottom: 1px solid var(--border);
|
|
min-height: 40px;
|
|
height: 40px;
|
|
padding: 7px 5px 5px;
|
|
align-items: center;
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-tabs-container.dv-horizontal .dv-tab::before {
|
|
display: none !important;
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-tab {
|
|
padding: 0;
|
|
margin: 0 1px;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
min-width: auto;
|
|
border-radius: 5px;
|
|
border-bottom: none !important;
|
|
outline: none;
|
|
transition:
|
|
background-color 0.15s ease,
|
|
color 0.15s ease;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-tab:hover {
|
|
background: color-mix(in srgb, var(--muted) 50%, transparent);
|
|
color: var(--foreground);
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-active-tab {
|
|
border-bottom: none !important;
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-tab:focus-within,
|
|
.dockview-theme-kandev .dv-tab:focus-visible {
|
|
box-shadow:
|
|
inset 0 0 0 1px var(--ring),
|
|
0 0 0 1px color-mix(in oklab, var(--ring) 20%, transparent);
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-default-tab {
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
padding: 0 7px;
|
|
gap: 5px;
|
|
height: 24px;
|
|
line-height: 1;
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-default-tab .dv-default-tab-action {
|
|
opacity: 0;
|
|
transition:
|
|
opacity 0.15s ease,
|
|
background-color 0.15s ease,
|
|
color 0.15s ease;
|
|
border-radius: 4px;
|
|
padding: 2px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-default-tab .dv-default-tab-action .dv-svg {
|
|
height: 8px;
|
|
width: 8px;
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-default-tab .dv-default-tab-action:hover {
|
|
background: color-mix(in srgb, var(--foreground) 12%, transparent);
|
|
color: var(--foreground);
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-default-tab .dv-default-tab-action:focus-visible {
|
|
outline: 1px solid var(--ring);
|
|
outline-offset: 1px;
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-tab:hover .dv-default-tab .dv-default-tab-action,
|
|
.dockview-theme-kandev .dv-active-tab .dv-default-tab .dv-default-tab-action {
|
|
opacity: 1;
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-content-container {
|
|
background: var(--card);
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-content-container .session-panel-content-wrapper {
|
|
border-radius: 0 !important;
|
|
box-shadow: none !important;
|
|
}
|
|
|
|
/* Per-group minimize: hide the content area while keeping the tab strip visible.
|
|
The splitview size is shrunk programmatically by useMinimizedGroupSync; this
|
|
rule ensures the body is invisible during the transition and at rest. */
|
|
.dockview-theme-kandev .dv-groupview[data-kandev-minimized="true"] .dv-content-container {
|
|
display: none;
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-content-container .session-panel-wrapper {
|
|
border: none !important;
|
|
border-radius: 0 !important;
|
|
padding: 0 !important;
|
|
margin: 0 !important;
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-resize-container > .sash-container .sash {
|
|
background: transparent;
|
|
transition: background-color 0.15s ease;
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-resize-container > .sash-container .sash:hover,
|
|
.dockview-theme-kandev .dv-resize-container > .sash-container .sash.active {
|
|
background: var(--primary);
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-locked-groupview .dv-default-tab .dv-default-tab-action {
|
|
display: none !important;
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-left-actions-container {
|
|
display: flex;
|
|
align-items: center;
|
|
padding-left: 3px;
|
|
gap: 2px;
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-tab:focus-within::after,
|
|
.dockview-theme-kandev .dv-tab:focus::after {
|
|
outline: none !important;
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-right-actions-container {
|
|
display: flex;
|
|
align-items: center;
|
|
padding-right: 3px;
|
|
gap: 2px;
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-left-actions-container button,
|
|
.dockview-theme-kandev .dv-right-actions-container button {
|
|
cursor: pointer;
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-left-actions-container button:focus-visible,
|
|
.dockview-theme-kandev .dv-right-actions-container button:focus-visible {
|
|
outline: 1px solid var(--ring);
|
|
outline-offset: 1px;
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-void-container {
|
|
flex: 1;
|
|
}
|
|
|
|
.dockview-theme-kandev .dv-watermark {
|
|
background: transparent;
|
|
}
|
|
|
|
.dockview-theme-kandev .drop-target-dropzone > .drop-target-selection {
|
|
border: 2px solid var(--primary);
|
|
background: color-mix(in oklab, var(--primary) 8%, transparent);
|
|
border-radius: 4px;
|
|
}
|