mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-17 18:25:42 +02:00
[eric] canvas: revert drag-to-edge snap (card-disappear bug on the real drag)
This commit is contained in:
@@ -7,7 +7,6 @@ import TetherLayer from './TetherLayer';
|
||||
import DashboardCardLayer from './DashboardCardLayer';
|
||||
import DashboardOverlays from './DashboardOverlays';
|
||||
import DashboardEmptyState from './DashboardEmptyState';
|
||||
import SnapZonePreview from './SnapZonePreview';
|
||||
import '../desktop/desktop.css';
|
||||
import DesktopDock from '../desktop/DesktopDock';
|
||||
import MinimizedStack from '../desktop/MinimizedStack';
|
||||
@@ -62,7 +61,6 @@ interface DashboardCanvasProps {
|
||||
focusedCardId: string | null;
|
||||
pendingFocusNoteId: string | null;
|
||||
multiDragDelta: { dx: number; dy: number } | null;
|
||||
snapZone: string | null;
|
||||
shakeDirection: Direction | null;
|
||||
neighborDirections: NeighborDirections;
|
||||
toolbarOpen: boolean;
|
||||
@@ -126,7 +124,6 @@ const DashboardCanvas: React.FC<DashboardCanvasProps> = ({
|
||||
focusedCardId,
|
||||
pendingFocusNoteId,
|
||||
multiDragDelta,
|
||||
snapZone,
|
||||
shakeDirection,
|
||||
neighborDirections,
|
||||
toolbarOpen,
|
||||
@@ -268,8 +265,6 @@ const DashboardCanvas: React.FC<DashboardCanvasProps> = ({
|
||||
/>
|
||||
)}
|
||||
|
||||
<SnapZonePreview zone={snapZone} />
|
||||
|
||||
{!anyFullscreen && (
|
||||
<DesktopDock
|
||||
sessions={sessions}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
|
||||
import { TILE_ZONES } from '@/app/pages/Dashboard/cards/tileZones';
|
||||
|
||||
// A translucent ghost of where a dragged card will land once released against the edge. Screen-space
|
||||
// (fixed), so it lives outside the pan/zoom layer and never counter-transforms. 'fullscreen' fills
|
||||
// the whole viewport; every other zone is a fraction of it.
|
||||
const GAP = 8;
|
||||
|
||||
interface SnapZonePreviewProps {
|
||||
zone: string | null;
|
||||
}
|
||||
|
||||
const SnapZonePreview: React.FC<SnapZonePreviewProps> = ({ zone }) => {
|
||||
const c = useClaudeTokens();
|
||||
if (!zone) return null;
|
||||
|
||||
const vp = document.querySelector('[data-canvas-viewport]');
|
||||
const rect = vp ? vp.getBoundingClientRect() : { left: 0, top: 0, width: window.innerWidth, height: window.innerHeight };
|
||||
const frac = zone === 'fullscreen' ? { x: 0, y: 0, w: 1, h: 1 } : TILE_ZONES[zone];
|
||||
if (!frac) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
left: rect.left + frac.x * rect.width + GAP,
|
||||
top: rect.top + frac.y * rect.height + GAP,
|
||||
width: frac.w * rect.width - GAP * 2,
|
||||
height: frac.h * rect.height - GAP * 2,
|
||||
borderRadius: 14,
|
||||
background: `${c.accent.primary}22`,
|
||||
border: `2px solid ${c.accent.primary}`,
|
||||
boxShadow: `0 0 0 100vmax ${c.accent.primary}0A`,
|
||||
pointerEvents: 'none',
|
||||
zIndex: 999985,
|
||||
transition: 'left 0.12s ease, top 0.12s ease, width 0.12s ease, height 0.12s ease',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SnapZonePreview;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useEffect, useRef, useState, type RefObject } from 'react';
|
||||
import { report } from '@/shared/serviceClient';
|
||||
import { useAppDispatch } from '@/shared/hooks';
|
||||
import { moveCards, setTiledCard } from '@/shared/state/dashboardLayoutSlice';
|
||||
import { moveCards } from '@/shared/state/dashboardLayoutSlice';
|
||||
import type { CardType, useDashboardSelection } from '../state/useDashboardSelection';
|
||||
import type { CanvasActions } from './useCanvasControls';
|
||||
|
||||
@@ -15,25 +15,6 @@ interface UseCardDragArgs {
|
||||
|
||||
const EDGE_ZONE = 60;
|
||||
const EDGE_MAX_SPEED = 8;
|
||||
// Jam a dragged card into the very edge (inside the pan zone) to snap-tile it. Grazing the 60px
|
||||
// edge still pans the infinite canvas; only the inner 24px band arms a snap and pauses the pan, so
|
||||
// the two never fight. Top = fullscreen (macOS drag-to-top), sides = halves, corners = quarters.
|
||||
const SNAP_BAND = 24;
|
||||
|
||||
function snapZoneFor(mx: number, my: number, rect: DOMRect): string | null {
|
||||
const nearL = mx <= rect.left + SNAP_BAND;
|
||||
const nearR = mx >= rect.right - SNAP_BAND;
|
||||
const nearT = my <= rect.top + SNAP_BAND;
|
||||
const nearB = my >= rect.bottom - SNAP_BAND;
|
||||
if (nearT && nearL) return 'tl';
|
||||
if (nearT && nearR) return 'tr';
|
||||
if (nearB && nearL) return 'bl';
|
||||
if (nearB && nearR) return 'br';
|
||||
if (nearT) return 'fullscreen';
|
||||
if (nearL) return 'left';
|
||||
if (nearR) return 'right';
|
||||
return null; // bottom-center is left alone: the composer + dock live there.
|
||||
}
|
||||
|
||||
// Clamped: an infinite canvas lets the cursor sit arbitrarily far outside the viewport, where an unclamped ramp would scale pan speed with distance instead of saturating.
|
||||
function axisIntensity(pos: number, lo: number, hi: number): number {
|
||||
@@ -51,10 +32,6 @@ export function useCardDrag({
|
||||
|
||||
const [multiDragDelta, setMultiDragDelta] = useState<{ dx: number; dy: number } | null>(null);
|
||||
const [liveDragInfo, setLiveDragInfo] = useState<{ cardId: string; dx: number; dy: number } | null>(null);
|
||||
const [snapZone, setSnapZone] = useState<string | null>(null);
|
||||
const snapZoneRef = useRef<string | null>(null);
|
||||
// Snap only a lone card: dragging one of several selected cards to the edge shouldn't tile just it.
|
||||
const snapEligibleRef = useRef(true);
|
||||
const activeDragCardRef = useRef<string | null>(null);
|
||||
const isMultiDragRef = useRef(false);
|
||||
|
||||
@@ -80,8 +57,7 @@ export function useCardDrag({
|
||||
const dx = EDGE_MAX_SPEED * axisIntensity(mx, rect.left, rect.right);
|
||||
const dy = EDGE_MAX_SPEED * axisIntensity(my, rect.top, rect.bottom);
|
||||
|
||||
// Snap armed = the card is about to tile at this edge, so freeze the canvas instead of panning.
|
||||
if ((dx !== 0 || dy !== 0) && !snapZoneRef.current) {
|
||||
if (dx !== 0 || dy !== 0) {
|
||||
// Live-only write (no React commit per frame); clearDrag commits once when the drag ends.
|
||||
canvasActions.panBy(dx, dy);
|
||||
}
|
||||
@@ -91,7 +67,6 @@ export function useCardDrag({
|
||||
|
||||
const handleCardDragStart = useCallback((id: string, type: CardType) => {
|
||||
activeDragCardRef.current = id;
|
||||
snapEligibleRef.current = selection.selectedArray().filter((s) => s.id !== id).length === 0;
|
||||
if (selection.isSelected(id)) {
|
||||
isMultiDragRef.current = true;
|
||||
} else {
|
||||
@@ -105,16 +80,6 @@ export function useCardDrag({
|
||||
if (mouseX !== undefined && mouseY !== undefined) {
|
||||
lastMousePosRef.current = { x: mouseX, y: mouseY };
|
||||
}
|
||||
// Arm/clear the snap target from the live cursor. Single-card drags only: snapping one card of a
|
||||
// multi-selection while the rest move is incoherent.
|
||||
const vp = viewportRef.current;
|
||||
const nextSnap = (vp && snapEligibleRef.current && mouseX !== undefined && mouseY !== undefined)
|
||||
? snapZoneFor(mouseX, mouseY, vp.getBoundingClientRect())
|
||||
: null;
|
||||
if (nextSnap !== snapZoneRef.current) {
|
||||
snapZoneRef.current = nextSnap;
|
||||
setSnapZone(nextSnap);
|
||||
}
|
||||
// Arm the webview shield on the first real MOVE, not on pointerdown: a plain click also arms the drag machinery, and shielding then made the click-to-focus camera fit skip (it saw a "drag in progress"), so focusing a card took two clicks. On a real drag the shield still goes up before the pointer travels, so the webview neutralization + no-nudge + release-over-webview fixes all hold. Idempotent add.
|
||||
document.body.classList.add('dashboard-marquee-active');
|
||||
// Start edge panning only once actual dragging begins; a live frame handle means the loop is already running.
|
||||
@@ -134,8 +99,6 @@ export function useCardDrag({
|
||||
// Reconcile React with whatever edge-pan wrote live during the drag.
|
||||
canvasActions.commit();
|
||||
activeDragCardRef.current = null;
|
||||
snapZoneRef.current = null;
|
||||
setSnapZone(null);
|
||||
document.body.classList.remove('dashboard-marquee-active');
|
||||
isMultiDragRef.current = false;
|
||||
setMultiDragDelta(null);
|
||||
@@ -144,12 +107,7 @@ export function useCardDrag({
|
||||
|
||||
const handleCardDragEnd = useCallback((dx: number, dy: number, didDrag: boolean) => {
|
||||
if (didDrag) report('dashboard', 'card_dragged');
|
||||
const snap = snapZoneRef.current;
|
||||
const activeId = activeDragCardRef.current;
|
||||
if (snap && didDrag && activeId) {
|
||||
// Released against the edge: tile the card there instead of leaving it at the drop position.
|
||||
dispatch(setTiledCard({ cardId: activeId, zone: snap }));
|
||||
} else if (isMultiDragRef.current && didDrag) {
|
||||
if (isMultiDragRef.current && didDrag) {
|
||||
const items = selection.selectedArray()
|
||||
.filter((s) => s.id !== activeDragCardRef.current);
|
||||
if (items.length > 0) {
|
||||
@@ -176,7 +134,6 @@ export function useCardDrag({
|
||||
return {
|
||||
multiDragDelta,
|
||||
liveDragInfo,
|
||||
snapZone,
|
||||
handleCardDragStart,
|
||||
handleCardDragMove,
|
||||
handleCardDragEnd,
|
||||
|
||||
@@ -103,7 +103,6 @@ export function useDashboardController(dashboardId: string, isActive: boolean) {
|
||||
const {
|
||||
multiDragDelta,
|
||||
liveDragInfo,
|
||||
snapZone,
|
||||
handleCardDragStart,
|
||||
handleCardDragMove,
|
||||
handleCardDragEnd,
|
||||
@@ -333,7 +332,7 @@ export function useDashboardController(dashboardId: string, isActive: boolean) {
|
||||
cards, viewCards, browserCards, keepAliveBrowserCards, notes, outputs, glowingAgentCards,
|
||||
workflowCards, workflowsHub,
|
||||
expandedSessionIds, tethers, highlightedCardId, autoFocusSessionId,
|
||||
focusedCardId, pendingFocusNoteId, multiDragDelta, snapZone, shakeDirection,
|
||||
focusedCardId, pendingFocusNoteId, multiDragDelta, shakeDirection,
|
||||
neighborDirections, toolbarOpen, searchPaletteOpen, newAgentBounce,
|
||||
toolbarRef, spawnOriginsRef, revealSpawnedRef, measuredHeightsRef, getCanvasState,
|
||||
toolbarPrefill,
|
||||
|
||||
Reference in New Issue
Block a user