From 7a74443b0794fb1a92bf0bfacf8fbfec3b1223af Mon Sep 17 00:00:00 2001 From: ciregenz Date: Tue, 28 Jul 2026 21:17:31 -0700 Subject: [PATCH] [eric] tiles: dragging an edge of a tiled browser/app exits the tile and resizes from its exact spot (macOS-style) --- .../app/pages/Dashboard/cards/BrowserCard.tsx | 20 ++++++++++++++++--- .../Dashboard/cards/DashboardViewCard.tsx | 20 ++++++++++++++++--- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/pages/Dashboard/cards/BrowserCard.tsx b/frontend/src/app/pages/Dashboard/cards/BrowserCard.tsx index 4f6f1c64..db6029c4 100644 --- a/frontend/src/app/pages/Dashboard/cards/BrowserCard.tsx +++ b/frontend/src/app/pages/Dashboard/cards/BrowserCard.tsx @@ -1,4 +1,5 @@ import React, { useState, useRef, useCallback, useEffect } from 'react'; +import { store } from '@/shared/state/store'; import { createPortal } from 'react-dom'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; @@ -44,7 +45,7 @@ import { type BrowserTab, } from '@/shared/state/dashboardLayoutSlice'; import WindowControls from './WindowControls'; -import { useTiledStyle } from './tileZones'; +import { useTiledStyle, computeTiledStyle } from './tileZones'; import { saveMinimizedShot } from '../desktop/minimizedShots'; import { removeBrowserCardCleanly } from '@/shared/browserTeardown'; import { createSelector } from '@reduxjs/toolkit'; @@ -890,14 +891,27 @@ const BrowserCard: React.FC = ({ if (e.button !== 0) return; e.preventDefault(); e.stopPropagation(); + // Grabbing an edge of a TILED card exits the tile and resizes from exactly where it sat, + // macOS-style; without this the handles resized the stale free-position geometry. + let origX = cardX, origY = cardY, origW = cardWidth, origH = cardHeight; + const zone = store.getState().dashboardLayout.tiledCards[browserId]; + if (zone) { + const cam = getCanvasState(); + const ts = computeTiledStyle(zone, cam.panX, cam.panY, cam.zoom); + if (ts) { + origX = ts.left; origY = ts.top; origW = ts.width / cam.zoom; origH = ts.height / cam.zoom; + setLocalResize({ x: origX, y: origY, w: origW, h: origH }); + dispatch(clearTiledCard(browserId)); + } + } resizeRef.current = { dir, startX: e.clientX, startY: e.clientY, - origX: cardX, origY: cardY, origW: cardWidth, origH: cardHeight, + origX, origY, origW, origH, }; setIsResizing(true); (e.target as HTMLElement).setPointerCapture(e.pointerId); }, - [cardX, cardY, cardWidth, cardHeight], + [cardX, cardY, cardWidth, cardHeight, getCanvasState, dispatch], ); const computeResize = useCallback( diff --git a/frontend/src/app/pages/Dashboard/cards/DashboardViewCard.tsx b/frontend/src/app/pages/Dashboard/cards/DashboardViewCard.tsx index fe96abae..73c24a3b 100644 --- a/frontend/src/app/pages/Dashboard/cards/DashboardViewCard.tsx +++ b/frontend/src/app/pages/Dashboard/cards/DashboardViewCard.tsx @@ -1,4 +1,5 @@ import React, { useState, useRef, useCallback, useEffect } from 'react'; +import { store } from '@/shared/state/store'; import { createPortal } from 'react-dom'; import Box from '@mui/material/Box'; import Fade from '@mui/material/Fade'; @@ -21,7 +22,7 @@ import { expandSession } from '@/shared/state/agentsSlice'; import WindowControls from './WindowControls'; import { openCardContextMenu } from '../desktop/CardContextMenu'; import { useDragEndBackstops } from '../hooks/interaction/useDragEndBackstops'; -import { useTiledStyle } from './tileZones'; +import { useTiledStyle, computeTiledStyle } from './tileZones'; import { useAppDispatch, useAppSelector } from '@/shared/hooks'; import { API_BASE, getAuthToken } from '@/shared/config'; import { useClaudeTokens } from '@/shared/styles/ThemeContext'; @@ -478,14 +479,27 @@ const DashboardViewCard: React.FC = ({ if (e.button !== 0) return; e.preventDefault(); e.stopPropagation(); + // Grabbing an edge of a TILED card exits the tile and resizes from exactly where it sat, + // macOS-style; without this the handles resized the stale free-position geometry. + let origX = cardX, origY = cardY, origW = cardWidth, origH = cardHeight; + const zone = store.getState().dashboardLayout.tiledCards[cardKey]; + if (zone) { + const cam = getCanvasState(); + const ts = computeTiledStyle(zone, cam.panX, cam.panY, cam.zoom); + if (ts) { + origX = ts.left; origY = ts.top; origW = ts.width / cam.zoom; origH = ts.height / cam.zoom; + setLocalResize({ x: origX, y: origY, w: origW, h: origH }); + dispatch(clearTiledCard(cardKey)); + } + } resizeRef.current = { dir, startX: e.clientX, startY: e.clientY, - origX: cardX, origY: cardY, origW: cardWidth, origH: cardHeight, + origX, origY, origW, origH, }; setIsResizing(true); (e.target as HTMLElement).setPointerCapture(e.pointerId); }, - [cardX, cardY, cardWidth, cardHeight], + [cardX, cardY, cardWidth, cardHeight, getCanvasState, dispatch], ); const computeResize = useCallback(