[eric] canvas: Tidy was beheading expanded cards and sliding the first column under the dock

This commit is contained in:
ciregenz
2026-07-31 20:28:40 -07:00
parent 888bbb5cf2
commit fa62c4c536
2 changed files with 17 additions and 8 deletions
@@ -727,10 +727,12 @@ export function useCanvasControls(zoomSensitivity: number = 50, contentBounds?:
floor,
ceiling,
);
// Content wider/taller than the viewport only happens when the zoom floor bit; centering then hides both ends, so anchor the start of it instead.
const targetPanX = contentWidth * targetZoom > vRect.width
? padX - minX * targetZoom
: (vRect.width - contentWidth * targetZoom) / 2 - minX * targetZoom;
// Centering is right until the zoom floor bites and the content outgrows its margins: then the left
// edge slides under the dock (or off screen), so never let it start left of the inset.
const targetPanX = Math.max(
(vRect.width - contentWidth * targetZoom) / 2 - minX * targetZoom,
padX - minX * targetZoom,
);
// A single card normally top-biases (header up top, no dead space below). On creation we want the opposite: the new card dead-centered "in front of you", so `centered` forces true vertical centering.
const topBiased = cardRects.length === 1 && !centered;
const targetPanY = topBiased || contentHeight * targetZoom > vRect.height
@@ -17,6 +17,9 @@ import type { CardType, useDashboardSelection } from '../state/useDashboardSelec
import type { CanvasActions } from '../interaction/useCanvasControls';
import { useSpawnPlacement } from './useSpawnPlacement';
// Title bubble + cost line, the strip an expanded card floats above itself.
const EXPANDED_HEADER_H = 64;
type Selection = ReturnType<typeof useDashboardSelection>;
interface UseDashboardCardActionsArgs {
@@ -114,10 +117,14 @@ export function useDashboardCardActions({
workflowsMonitorCard: tidiedMonitor, settingsCard: tidiedSettings,
} = store.getState().dashboardLayout;
const allRects = [
...Object.values(tidied).map((c) => ({
x: c.x, y: c.y, width: c.width,
height: expandedSet.has(c.session_id) ? Math.max(EXPANDED_CARD_MIN_H, c.height) : c.height,
})),
...Object.values(tidied).map((c) => {
// An expanded card wears its title bubble ABOVE its rect, so the camera has to be told about that strip or Tidy frames the card and beheads it.
const isExpanded = expandedSet.has(c.session_id);
const height = isExpanded ? Math.max(EXPANDED_CARD_MIN_H, c.height) : c.height;
return isExpanded
? { x: c.x, y: c.y - EXPANDED_HEADER_H, width: c.width, height: height + EXPANDED_HEADER_H }
: { x: c.x, y: c.y, width: c.width, height };
}),
...Object.values(tidiedViews).map((c) => ({ x: c.x, y: c.y, width: c.width, height: c.height })),
...Object.values(tidiedBrowsers).map((c) => ({ x: c.x, y: c.y, width: c.width, height: c.height })),
...Object.values(tidiedWorkflows).map((c) => ({ x: c.x, y: c.y, width: c.width, height: c.height })),