mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-31 12:19:48 +02:00
[eric] dashboard: spawn lands in the closest open gap to viewport center; spawn-framing zoom capped 1.15 -> 0.75
This commit is contained in:
@@ -21,6 +21,7 @@ import {
|
||||
EXPANDED_CARD_MIN_H,
|
||||
GRID_GAP,
|
||||
type CardPosition,
|
||||
SPAWN_FOCUS_MAX_ZOOM,
|
||||
} from '@/shared/state/dashboardLayoutSlice';
|
||||
import { generateDashboardName } from '@/shared/state/dashboardsSlice';
|
||||
import type { ContextPath } from '@/app/components/editor/DirectoryBrowser';
|
||||
@@ -198,7 +199,7 @@ export function useAgentSpawn({
|
||||
if (bc) rects.push({ x: bc.x, y: bc.y, width: bc.width, height: bc.height });
|
||||
}
|
||||
}
|
||||
canvasActions.fitToCards(rects, 1.15, true, undefined, true);
|
||||
canvasActions.fitToCards(rects, SPAWN_FOCUS_MAX_ZOOM, true, undefined, true);
|
||||
handleHighlightCard(draftId);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
DEFAULT_NOTE_W,
|
||||
DEFAULT_NOTE_H,
|
||||
EXPANDED_CARD_MIN_H,
|
||||
SPAWN_FOCUS_MAX_ZOOM,
|
||||
} from '@/shared/state/dashboardLayoutSlice';
|
||||
import type { CardType, useDashboardSelection } from '../state/useDashboardSelection';
|
||||
import type { CanvasActions } from '../interaction/useCanvasControls';
|
||||
@@ -65,7 +66,7 @@ export function useDashboardCardActions({
|
||||
}
|
||||
const card = viewCards[focusKey];
|
||||
if (card) {
|
||||
canvasActions.fitToCards([{ x: card.x, y: card.y, width: card.width, height: card.height }], 1.15, true, undefined, true);
|
||||
canvasActions.fitToCards([{ x: card.x, y: card.y, width: card.width, height: card.height }], SPAWN_FOCUS_MAX_ZOOM, true, undefined, true);
|
||||
handleHighlightCard(focusKey);
|
||||
}
|
||||
}, 200);
|
||||
@@ -88,7 +89,7 @@ export function useDashboardCardActions({
|
||||
const newId = Object.keys(allNotes).find((id) => !prevIds.has(id));
|
||||
if (newId) {
|
||||
const note = allNotes[newId];
|
||||
canvasActions.fitToCards([{ x: note.x, y: note.y, width: note.width, height: note.height }], 1.15, true, undefined, true);
|
||||
canvasActions.fitToCards([{ x: note.x, y: note.y, width: note.width, height: note.height }], SPAWN_FOCUS_MAX_ZOOM, true, undefined, true);
|
||||
handleHighlightCard(newId);
|
||||
}
|
||||
}, 200);
|
||||
@@ -109,7 +110,7 @@ export function useDashboardCardActions({
|
||||
setTimeout(() => {
|
||||
const card = store.getState().dashboardLayout.cards[sessionId];
|
||||
if (card) {
|
||||
canvasActions.fitToCards([{ x: card.x, y: card.y, width: card.width, height: card.height }], 1.15, true);
|
||||
canvasActions.fitToCards([{ x: card.x, y: card.y, width: card.width, height: card.height }], SPAWN_FOCUS_MAX_ZOOM, true);
|
||||
handleHighlightCard(sessionId);
|
||||
}
|
||||
}, 200);
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
clearPendingFocusWorkflowId,
|
||||
clearPendingFocusWorkflowsHub,
|
||||
type ViewCardPosition,
|
||||
SPAWN_FOCUS_MAX_ZOOM,
|
||||
} from '@/shared/state/dashboardLayoutSlice';
|
||||
import { fetchOutputs, type Output } from '@/shared/state/outputsSlice';
|
||||
import { generateDashboardName } from '@/shared/state/dashboardsSlice';
|
||||
@@ -215,7 +216,7 @@ export function useDashboardLifecycle({
|
||||
setTimeout(() => {
|
||||
const card = store.getState().dashboardLayout.cards[agentId];
|
||||
if (card) {
|
||||
canvasActions.fitToCards([{ x: card.x, y: card.y, width: card.width, height: card.height }], 1.15, true);
|
||||
canvasActions.fitToCards([{ x: card.x, y: card.y, width: card.width, height: card.height }], SPAWN_FOCUS_MAX_ZOOM, true);
|
||||
handleHighlightCard(agentId);
|
||||
}
|
||||
}, 350);
|
||||
@@ -253,7 +254,7 @@ export function useDashboardLifecycle({
|
||||
setTimeout(() => {
|
||||
const card = store.getState().dashboardLayout.viewCards[cardKey];
|
||||
if (card) {
|
||||
canvasActions.fitToCards([{ x: card.x, y: card.y, width: card.width, height: card.height }], 1.15, true);
|
||||
canvasActions.fitToCards([{ x: card.x, y: card.y, width: card.width, height: card.height }], SPAWN_FOCUS_MAX_ZOOM, true);
|
||||
handleHighlightCard(cardKey);
|
||||
}
|
||||
}, 200);
|
||||
@@ -270,7 +271,7 @@ export function useDashboardLifecycle({
|
||||
if (card) {
|
||||
canvasActions.fitToCards(
|
||||
[{ x: card.x, y: card.y, width: card.width, height: card.height }],
|
||||
1.15,
|
||||
SPAWN_FOCUS_MAX_ZOOM,
|
||||
true,
|
||||
);
|
||||
handleHighlightCard(workflowId);
|
||||
@@ -352,7 +353,7 @@ export function useDashboardLifecycle({
|
||||
const rects = [{ x: vc.x, y: vc.y, width: vc.width, height: vc.height }];
|
||||
const ac = store.getState().dashboardLayout.cards[sid];
|
||||
if (ac) rects.push({ x: ac.x, y: ac.y, width: ac.width, height: ac.height });
|
||||
canvasActions.fitToCards(rects, 1.15, true);
|
||||
canvasActions.fitToCards(rects, SPAWN_FOCUS_MAX_ZOOM, true);
|
||||
handleHighlightCard(outputId);
|
||||
}, 200);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ export const DEFAULT_WORKFLOWS_HUB_W = DEFAULT_BROWSER_CARD_W;
|
||||
export const DEFAULT_WORKFLOWS_HUB_H = DEFAULT_BROWSER_CARD_H;
|
||||
export const EXPANDED_CARD_MIN_H = 620;
|
||||
export const GRID_GAP = 24;
|
||||
// Zoom ceiling when the camera frames a freshly spawned card. 1.15 slammed the user to 115% on every spawn; 0.75 keeps the new card readable without the lurch-into-your-face zoom.
|
||||
export const SPAWN_FOCUS_MAX_ZOOM = 0.75;
|
||||
// Gap between the Workflows window and the cards it spawns (run monitor, that monitor's browser). Keeps the hub -> monitor -> browser row evenly spaced.
|
||||
export const WORKFLOW_CARD_GAP = 140;
|
||||
const GRID_ORIGIN = { x: 40, y: 100 };
|
||||
@@ -505,8 +507,14 @@ export function computeSpawnPosition(
|
||||
return placeBesideCard(state, anchor.beside, newW, newH, expandedSessionIds);
|
||||
}
|
||||
if (anchor.viewportCenter) {
|
||||
// Land dead-center, "in front of you", even if a card is already there. Overlap is intentional (new card sits on top via its higher zOrder); dodging to free space is exactly the "spawned off to the side" behavior we're removing.
|
||||
return { x: anchor.viewportCenter.x - newW / 2, y: anchor.viewportCenter.y - newH / 2 };
|
||||
// Closest open gap to the viewport center: dead-center-with-overlap stacked spawns invisibly on top of each other (two center spawns in a row = the second fully covers the first). The spiral stays center-biased so it still reads as "in front of you".
|
||||
return findOpenSpotNear(
|
||||
anchor.viewportCenter.x - newW / 2,
|
||||
anchor.viewportCenter.y - newH / 2,
|
||||
collectOccupiedRects(state, expandedSessionIds),
|
||||
newW,
|
||||
newH,
|
||||
);
|
||||
}
|
||||
return findOpenGridCell(collectOccupiedRects(state, expandedSessionIds), newW, newH);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user