d6bb9ac6ec00fdc4da4c116a7db8bdf0562ff6b6
7 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e930061fe4 | fix(web): chat badge, dockview preview routing, and agent-error icon cleanup (#1395) | ||
|
|
46f20e0dfe |
fix(web): keep dockview group alive on task switch to stop layout collapse (#1308)
* fix(web): keep dockview group alive on task switch to stop layout collapse On a fast-path env switch, the incoming session chat was added only after removeEphemeralPanels closed the outgoing chat. When that chat was the last surviving panel in its group (e.g. the default layout's chat column), removing it first emptied — and so destroyed — the group. The captured group id then no longer existed, and post-#1165 the old `referenceGroup: "sidebar"` fallback is dead, so addPanel ran with an undefined position: dockview dropped the incoming chat into the active (terminal) group and collapsed the grid root to a vertical stack. The corrupted layout was then persisted, so it survived reloads. Add the incoming session panel before removing the outgoing one, anchored at the outgoing panel's current index, so the group stays alive throughout the swap. Removing the panels ahead of the insert point shifts the new panel into the same final slot, so computeSurvivingIndex is no longer needed. * docs(web): note the raw-index equivalence assumption in env switch |
||
|
|
f1bdc56892 |
feat(layout): roomier pane caps + remember user-set widths (#1005)
* refactor(layout): swap pane width caps for viewport-proportional runtime cap Replace the hard 350px sidebar / 450px right caps with max(800, viewport*0.7) so wide displays get a roomy file/terminal panel and the sidebar can grow with the screen. Pinned minimum is raised from 50px to 180px so panels don't collapse into unusable slivers. Caps now live in lib/state/layout-manager/caps.ts; constants module no longer exports per-column max-px constants. * feat(layout): persist preferred pane widths, flush on unload, roomier tablet split - Mirror sidebar / right widths into a sessionStorage pinned-defaults slot on every resize; seed brand-new task envs with those widths so the user's last-set sizes follow them across tasks. - Re-apply the runtime cap to every pinned group on each ResizeObserver tick so a panel pinned to the old cap grows when the user widens the window (and re-clamps when they shrink it). - Flush the debounced layout save on `beforeunload`, so a reload less than 300ms after the last drag doesn't lose the new width. - Drop the tablet right-panel inner split minSize from 30/20 to 15/15 so the files vs. terminal proportions match the loosened desktop feel. * test(e2e): cover pane resize, persistence, and edge cases Four spec files under apps/web/e2e/tests/layout/ exercise the runtime cap and pinned-defaults persistence end-to-end via dockview's `.dv-sash` drag handles and `window.__dockviewApi__`: - pane-resize-right: drag past the old 450px cap, viewport-shrink re-clamp, reload + cross-env propagation of preferred width. - pane-resize-sidebar: same coverage for the sidebar including toggle-off/on width retention. - pane-persistence-tablet: localStorage round-trip for the tablet layout keys (`task-layout-tablet-v1`, `task-layout-right-v2`). - pane-resize-edge-cases: double-click smoke, rapid drags + beforeunload flush, maximize round-trip, sash off-viewport, resize with sidebar hidden. Adds e2e/helpers/dockview-resize.ts for the shared drag + width read-out helpers. * fix(layout): cap sidebar at 30% screen width, preserve legacy initial widths - Split the runtime cap into a sidebar-specific function (max(350, vw*0.3)) and the right-pane function (max(800, vw*0.7)). File-tree / task-list content rarely benefits from more than ~30% of the screen. - Preserve legacy initial widths: getPinnedWidth now clamps the ratio-based default to the old hard limits (350 sidebar, 450 right) so a fresh task env opens at exactly the same width as before. Users can still drag past these via setConstraints — only the *initial* default is capped tightly. * fix(layout): address PR review — narrow viewport, hidden sidebar, e2e flake Bot-review fixups for #1005: - caps: bound runtime caps by `viewport - VIEWPORT_RESERVE_PX` so the 800px right-cap floor can't collapse the center column on narrow screens (Cubic P1). - applier: respect `column.maxWidth` on the sidebar too, so the `compact` preset's 260px cap is honored at runtime (Cubic P2). - dockview-store: skip the pinned-defaults mirror when the column is hidden — auto-sync infers sidebar from grid index 0, which is the center when sidebar is hidden, so an unguarded mirror would persist the center's width as the sidebar's (Greptile/Cubic P1). - trackPinnedWidths: gate the auto-sync on `sidebarVisible` / `rightPanelsVisible` for the same reason. - sizing: use `LAYOUT_RIGHT_RATIO` for non-sidebar columns instead of silently reusing the sidebar ratio (Greptile P2). - e2e: tablet viewport bumped from 1024 → 700 (≥1024 is desktop, not tablet — the old viewport never rendered the tablet layout and every assertion timed out). Tests now also verify rendered panel proportions, not just the localStorage echo (CodeRabbit #2/#3). - e2e helpers: throw on missing `.dv-sash` instead of returning `-1` (CodeRabbit #1); shared `openWideTask` extracted from three spec files (CodeRabbit #5); silent `return` in evaluate blocks replaced with `throw` for fail-fast (CodeRabbit #4). * fix(layout): swap drag-mouse e2e for programmatic resize + claude/cr followups Round 2 of PR review fixups: - e2e: convert pane-resize-right/sidebar/edge-cases specs from `page.mouse` drag-by-delta to a programmatic `splitview.resizeView()` call. The mouse-drag approach was flaky in headless CI (5 chromium failures across shards 2-4); the programmatic path tests the same cap-enforcement and persistence contract without depending on pointer-events targeting through a 1px .dv-sash overlay. - dockview-layout-setup: remove an unreachable guard in trackPinnedWidths — `rightIdx === 0 && sidebarVisible` cannot occur given the `sv.length < 2` early return (Claude). - dockview-layout-setup: cancel any in-flight debounce timer in the setupLayoutPersistence disposer so a fire can't race with teardown and persist stale layout (CodeRabbit). - constants: document why LAYOUT_SIDEBAR_RATIO and LAYOUT_RIGHT_RATIO are both 0.25 today — separate constants stay decoupled so a future tweak to one doesn't silently change the other (Claude). * fix(layout): drop cross-env width propagation — defaults stay unchanged User feedback: the initial sidebar width on a brand-new task should be the same as it was before this PR (350px on typical viewports). The plan's "propagate preferred widths across envs" feature changed that in practice: after the first resize, every new task opened at the resized width instead of the legacy default. - Remove `getPinnedDefaults` / `setPinnedDefaults` helpers and the sessionStorage `kandev.dockview.pinned-defaults` slot. - Drop the write-through in `setPinnedWidth` and the seed in `performBuildDefault`. `freshPinned` is back to `new Map()`, so a brand-new env always renders at the ratio-based default (same as before this PR, since `getPinnedWidth` already clamps the initial ratio to the legacy 350/450 hard caps). - Per-env layout persistence still works via `setEnvLayout` — when a user resizes inside a task and switches back later, the width is restored. Only the cross-env *default* is no longer carried over. - Remove e2e tests + helpers that exercised the propagation contract. - Drop the flaky `rapid resizes persist across reload` edge test — too brittle around the debounce/beforeunload race. - `LAYOUT_INITIAL_RATIO` introduced as the canonical name; `LAYOUT_SIDEBAR_RATIO` / `LAYOUT_RIGHT_RATIO` kept as deprecated aliases for backwards compatibility while sizing.ts dispatches. * fix(layout): bump saved-layout storage keys to v2 Layouts saved by intermediate builds of this PR captured sidebar/right widths beyond the legacy initial caps (when the larger runtime cap was the only thing constraining serialized widths). Restoring those layouts on the current build replays the over-wide widths, defeating the "defaults stay the same" guarantee. Bumping the storage key prefixes (`env-layout` → `env-layout-v2`, `env-maximize` → `env-maximize-v2`, `dockview-layout-v1` → `dockview-layout-v2`) invalidates legacy saves once on first open; subsequent opens persist + restore under v2 normally. Existing users lose stored column widths once and re-customize from defaults — a one-time cost in exchange for the upgrade being clean. * fix(layout): apply target env's widths after fast-path switch Fast-path env switch (`tryFastEnvSwitch`) skips `fromJSON` for speed, which means column widths carry over from the *outgoing* env. If a user resized the sidebar to 480px in task A and then opens task B, B inherits A's 480px sidebar — even with empty sessionStorage on different dev-server ports, because the widths live in dockview's in-memory grid state, not in storage. Explicitly resize sidebar/right after the panel swap: - saved layout exists for target env → use its serialized sizes - no saved layout (brand-new env) → compute defaults via `getPinnedWidth` (legacy initial caps: 350 sidebar / 450 right) * fix(layout): pass measured dims to fromJSON so widths don't grow on rescale Even on a fresh dev instance with no prior storage, brand-new tasks were opening with oversized sidebar/right widths. Root cause: applyLayout fed `api.width` into toSerializedDockview, but `api.width` lags the live container during the dockview mount sequence. fromJSON wrote a layout sized for the stale width; the subsequent `api.layout(safeWidth, ...)` in performBuildDefault's rAF rescaled the grid proportionally — and without the old hard cap (350/450) clamping it, the sidebar grew past its legacy default. Pass the measured container dimensions through applyLayout into toSerializedDockview so fromJSON's grid.width already matches the live container. api.layout in the rAF is then a no-op, no rescale, and sidebar/right open at exactly their legacy defaults. Also applies to applyBuiltInPreset and applyCustomLayout for the same flow. * fix(layout): lock pinned column max width to current size, unlock on drag The real reason brand-new tasks opened with oversized sidebar/right: dockview's splitview rebalances pinned columns proportionally on every `api.layout` call. After we relaxed setConstraints from the legacy 350/450 hard caps to viewport-proportional runtime caps (576/1344), nothing clamped the rebalance — sidebar grew from 350 → ~450, right from 450 → ~530 on the first ResizeObserver tick. New invariant: pinned-column `maximumWidth` is locked to the column's *current* width whenever the user is not actively dragging a sash. The rebalance has zero room to grow. - `applyLayout` / `applyLayoutFixups` set the lock as soon as fromJSON finishes — sidebar/right open at exactly their just-computed widths. - `setupContainerResizeSync` re-applies the lock on every viewport ResizeObserver tick before and after `api.layout`. - `setupSashDragCapToggle` listens for mousedown/mouseup on `.dv-sash`: on mousedown, widen the cap to the runtime viewport-proportional cap so the user can drag past the legacy default; on mouseup, snap the cap back to the new current width. Also addresses Claude's latest review: - caps.test.ts: rename "hits the 350px floor on roomy viewports" → "… when vw*0.3 falls below the minimum" (test name now matches assertion). - dockview-resize.ts: drop the stale "pinned-defaults mirror" comment; guard the sidebar splitview index against the hidden-sidebar footgun. - pane-resize-right.spec.ts: rename the reload test to mention the correct localStorage key (`dockview-layout-v2`). - new `dockview-env-switch-pinned.test.ts` unit-tests applyPinned- ColumnSizes with a spy splitview — verifies sidebar/right are explicitly resized after a fast-path env switch. * fix(layout): constrain all groups, loosen e2e resize cap, drop stale docs Round of PR-review fixes: - applier: `applyConstraintsToAllPanelGroups` replaces the previous first-only helper. The default right column has separate top + bottom groups; constraining only the first left the bottom group unbounded and let the column grow on rebalance via that group (cubic P2 + Claude regression). - sizing: collapse `LAYOUT_SIDEBAR_RATIO` / `LAYOUT_RIGHT_RATIO` dispatch — they alias the same `LAYOUT_INITIAL_RATIO`, so the ternary was a silent no-op (Claude). - sizing: doc — drop "widths seeded from pinned-defaults sessionStorage" reference; that feature was removed in |
||
|
|
586efd1c6b | fix(web): preserve session-tab grouping when restoring contaminated layout (#1028) | ||
|
|
20a660fe3e | fix(web): strip phantom session panels on env-layout restore (#944) | ||
|
|
40c89cd380 | fix(web): preserve dockview state across task and plan-mode switches (#855) | ||
|
|
45ee2ff48f |
feat: key terminals + dockview layout to TaskEnvironment (#755)
* feat(backend): key user shells by TaskEnvironmentID Sessions in the same task share a TaskEnvironment. Re-key the user shell runner so terminals are scoped per env, not per session — switching between sessions of the same task now sees the same shell list. - Add TaskEnvironmentID to AgentExecution / LaunchRequest / ExecutorCreateRequest / executor.LaunchAgentRequest, propagated through ToAgentExecution and the orchestrator launch path. - Add Manager.ResolveScopeKey(sessionID) — returns env when execution carries it, falls back to sessionID for legacy paths. - Rename runner param sessionID -> scopeID in interactive_shells.go; map keys are now scopeID:terminalID. - Shell handlers + WS terminal handler resolve sid -> scopeID via ResolveScopeKey before calling the runner. * feat(web): key dockview layout + portals to TaskEnvironmentID Switching between sessions of the same task no longer rebuilds the dockview layout — terminals and panels persist because layouts are env-scoped, not session-scoped. The bottom terminal also reuses a stable sessionId across same-env switches so it doesn't reconnect to a different shell. - Rename layout persistence: setSessionLayout/getSessionLayout/*SessionMaximizeState -> setEnvLayout/getEnvLayout/*EnvMaximizeState (kandev.dockview.env-layout. prefix). - Rename dockview-session-switch.ts -> dockview-env-switch.ts; performSessionSwitch -> performEnvSwitch with oldEnvId/newEnvId/activeSessionId. - dockview-store.ts: currentLayoutSessionId -> currentLayoutEnvId; switchSessionLayout -> switchEnvLayout(oldEnvId, newEnvId, activeSessionId). Same-env switches no-op. Drop _skipLayoutSwitchForSession + consumeSkipFlag — env-keyed action handles it structurally. - panel-portal-manager: PortalEntry.envId; releaseBySession -> releaseByEnv. ENV_SCOPED_COMPONENTS in dockview-desktop-layout (renamed from SESSION_SCOPED). - useEnvSwitchCleanup replaces useSessionSwitchCleanup; tracks env changes only. - All performLayoutSwitch callers (sidebar, dropdown, header, dialog) resolve sid -> envId via environmentIdBySessionId before switching. - bottom-terminal-panel: drop sessionId prop, use useEnvironmentSessionId(). - cleanupTaskStorage now takes envIds[] alongside sessionIds[] for env-scoped layout/maximize cleanup. * test: add coverage for env-keyed terminals + layout Playwright e2e (terminal-env-keyed.spec.ts) — switching between sessions of the same task keeps terminal scrollback intact and asserts both sessions share one task_environment_id. Update the comment in sessionless-task-switch.spec.ts to reference performEnvSwitch. * fix: address env layout PR feedback * test: fix flaky handshake auth check * fix: address env scope review findings |