Skip to content

Commit caaa8c5

Browse files
committed
Merge branch 'main' into feat/soft-todo-leaky-bucket
2 parents c6f6bee + 3cb1abb commit caaa8c5

2 files changed

Lines changed: 8 additions & 16 deletions

File tree

docs/specs/layout.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -297,14 +297,14 @@ When a pane is added, its dockview group element gets a directional `.pane-spawn
297297

298298
The direction is carried via `FreshlySpawnedContext` — a `Map<paneId, SpawnDirection>` written by the spawn call site and consumed once by `TerminalPanel`'s `useLayoutEffect` on first mount.
299299

300-
### Kill (ghost fade + FLIP reclaim)
300+
### Kill (in-place fade + FLIP reclaim)
301301

302-
`orchestrateKill(api, killedId)` in `Pond.tsx` runs on kill confirmation:
302+
`orchestrateKill(api, killedId)` in `Pond.tsx` runs on kill confirmation. It fades the real pane element in place (its content dissolves against the same-colored background), then removes the panel and FLIP-reveals the survivors:
303303

304-
1. Snapshot `getBoundingClientRect` for every other panel's group element.
305-
2. `destroyTerminal` + `api.removePanel`; dockview snaps the layout.
306-
3. Measure post-rects. Any panel whose rect grew is a "grower."
307-
4. Mount a solid `var(--color-surface)` ghost overlay at the killed rect (`position: fixed`, `z-index: 55`) with the `.pane-fading-out` class; it removes itself on `animationend` with a 1s timeout safety net. A uniform fade works for every case (edge/middle/last-pane kill) because the directional cue is already carried by the grower's FLIP reveal.
304+
1. Add `.pane-fading-out` (or `.pane-fading-and-shrinking-to-br` for a last-pane kill) to the killed pane's group element. Block pointer events during the fade.
305+
2. On `animationend`, snapshot `getBoundingClientRect` for every surviving panel's group element.
306+
3. `destroyTerminal` + `api.removePanel`; dockview snaps the layout.
307+
4. Measure post-rects. Any panel whose rect grew is a "grower."
308308
5. For each grower, apply an inline `clip-path: inset(...)` with the newly-claimed territory clipped off, force a reflow, then transition to `inset(0)`. This reveals the grower into the vacated space without affecting `getBoundingClientRect`. Clears on `transitionend`.
309309

310310
Case handling is purely rect-based (measure before and after removal), so 2-pane splits, linear 3+ rows/columns, and nested splits all fall through the same code path with no per-case branching.
@@ -326,7 +326,7 @@ The deferred spawn also only calls `selectPanel` if selection is null. The kill
326326
7. **Asymmetric back-navigation**: breadcrumb tracks last direction + origin for opposite-direction return.
327327
8. **Center drop merges panels**: intercepted at group-level `model.onWillDrop` and converted to a swap.
328328
9. **Group drag has null panelId**: falls back to `api.getGroup(groupId).activePanel.id`.
329-
10. **Auto-spawn on empty**: `onDidRemovePanel` creates a new session whenever the last visible pane is removed, whether or not doors exist — there is always a pane visible. The new pane receives the just-removed pane's id as `paneToCopy` for future cwd/terminal-kind inheritance. The `addPanel` call is delayed 440ms (see "Auto-spawn delay" under Animations) so the outgoing kill/detach animation finishes first.
329+
10. **Auto-spawn on empty**: `onDidRemovePanel` creates a new session whenever the last visible pane is removed, whether or not doors exist — there is always a pane visible. The `addPanel` call is delayed 440ms (see "Auto-spawn delay" under Animations) so the outgoing kill/detach animation finishes first.
330330
11. **Door focus survives auto-spawn**: `api.addPanel` auto-activates the new panel, firing `onDidActivePanelChange`. When the current selection is a door (e.g., just-detached last pane), that listener must not flip `selectedId` to the new pane — otherwise `selectedType === 'door'` + `selectedId === newPaneId` desyncs and the door loses its highlight while the SelectionOverlay is stuck on the stale door rect. The listener early-returns when `selectedType === 'door'`.
331331

332332
## Files

lib/src/components/Pond.tsx

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,10 +1142,6 @@ export function Pond({
11421142
return `pane-${(++paneCounterRef.current).toString(36)}-${Math.random().toString(36).substring(2, 7)}`;
11431143
}, []);
11441144

1145-
// newPaneId -> sourcePaneId for panes born from a split or the empty-state auto-spawn.
1146-
// Nothing reads this yet; it exists so future work can copy cwd / terminal kind from the source.
1147-
const paneToCopyByIdRef = useRef(new Map<string, string>());
1148-
11491145
// Ids of panes that were just spawned, keyed by id with the direction the spawn
11501146
// should reveal from. TerminalPanel consumes its id on first mount to play the
11511147
// matching directional entrance animation.
@@ -1484,9 +1480,7 @@ export function Pond({
14841480
});
14851481

14861482
// Always keep one pane visible: when the last visible pane is removed (killed
1487-
// or detached), spawn a fresh one — regardless of whether doors exist. Carry
1488-
// the just-removed pane's id forward as paneToCopy so future work can copy
1489-
// cwd / terminal kind from it.
1483+
// or detached), spawn a fresh one — regardless of whether doors exist.
14901484
//
14911485
// Delay the spawn by the kill/detach animation duration so the two animations
14921486
// don't overlap — the outgoing pane crushes/fades first, then the new pane
@@ -1501,7 +1495,6 @@ export function Pond({
15011495
const spawn = () => {
15021496
if (e.api.totalPanels > 0) return;
15031497
const id = generatePaneId();
1504-
paneToCopyByIdRef.current.set(id, removed.id);
15051498
freshlySpawnedRef.current.set(id, 'top-left');
15061499
e.api.addPanel({ id, component: 'terminal', tabComponent: 'terminal', title: '<unnamed>' });
15071500
// Only steal focus if nothing is selected (i.e., the kill path, which
@@ -1950,7 +1943,6 @@ export function Pond({
19501943
if (!api) return;
19511944
const newId = generatePaneId();
19521945
const ref = id && api.getPanel(id) ? id : null;
1953-
if (ref) paneToCopyByIdRef.current.set(newId, ref);
19541946
// Horizontal split places the new pane to the right → reveal from its left edge.
19551947
// Vertical split places it below → reveal from its top edge.
19561948
freshlySpawnedRef.current.set(newId, direction === 'right' ? 'left' : 'top');

0 commit comments

Comments
 (0)