Problem
When opening a new terminal (Cmd+T), the spatial canvas places it with a diagonal cascade from the viewport center — each successive tile offsets by only 48px while tiles are 800×540. The result: every new terminal visually overlaps the previous ones, stacking on top rather than tiling alongside.
The cascade is implemented in packages/client/src/canvas/tilePlacement.ts:findFreeTilePosition. It exists so that opening two terminals without panning produces a staircase, not a stack — but the step size is far too small relative to the tile dimensions to actually avoid overlap.
Proposal
Replace the diagonal cascade heuristic with right-edge placement:
- Find the rightmost existing tile (by
x + w).
- Place the new tile at
snapToGrid(rightmost.x + rightmost.w + GAP), same y as the rightmost tile.
- Fall back to viewport-center placement only when no tiles exist yet (first terminal).
This keeps the "nothing else moves on create" invariant (no auto-arrange) — the new tile simply appears to the right of the existing rightmost tile instead of overlapping it.
Minimal incision
tilePlacement.ts:30-43 — change the body of findFreeTilePosition:
- Widen
existing from ReadonlyArray<{ x, y }> to ReadonlyArray<{ x, y, w, h }> (collision detection now uses actual tile extents).
- Replace the diagonal cascade loop with a single right-edge lookup.
TerminalCanvas.tsx:188 — pass full layouts (with w/h) to findFreeTilePosition instead of just the position.
No server changes, no schema changes, no new preferences.
Related
Problem
When opening a new terminal (Cmd+T), the spatial canvas places it with a diagonal cascade from the viewport center — each successive tile offsets by only 48px while tiles are 800×540. The result: every new terminal visually overlaps the previous ones, stacking on top rather than tiling alongside.
The cascade is implemented in
packages/client/src/canvas/tilePlacement.ts:findFreeTilePosition. It exists so that opening two terminals without panning produces a staircase, not a stack — but the step size is far too small relative to the tile dimensions to actually avoid overlap.Proposal
Replace the diagonal cascade heuristic with right-edge placement:
x + w).snapToGrid(rightmost.x + rightmost.w + GAP), sameyas the rightmost tile.This keeps the "nothing else moves on create" invariant (no auto-arrange) — the new tile simply appears to the right of the existing rightmost tile instead of overlapping it.
Minimal incision
tilePlacement.ts:30-43— change the body offindFreeTilePosition:existingfromReadonlyArray<{ x, y }>toReadonlyArray<{ x, y, w, h }>(collision detection now uses actual tile extents).TerminalCanvas.tsx:188— pass full layouts (withw/h) tofindFreeTilePositioninstead of just the position.No server changes, no schema changes, no new preferences.
Related