diff --git a/frontend/src/app/pages/Dashboard/cards/CanvasWindowCard.tsx b/frontend/src/app/pages/Dashboard/cards/CanvasWindowCard.tsx index 8c1416de..2e061a27 100644 --- a/frontend/src/app/pages/Dashboard/cards/CanvasWindowCard.tsx +++ b/frontend/src/app/pages/Dashboard/cards/CanvasWindowCard.tsx @@ -6,7 +6,6 @@ import { useCardTiling } from './useCardTiling'; import { useCanvasWindowResize } from './useCanvasWindowResize'; import { useDragEndBackstops } from '../hooks/interaction/useDragEndBackstops'; import { openCardContextMenu, isNativeMenuTarget, type CardMenuRow } from '../desktop/openCardContextMenu'; -import { tileMenuRows } from './tileMenuRows'; import type { CardType } from '@/shared/state/dashboardLayoutSlice'; const DRAG_THRESHOLD = 3; @@ -193,7 +192,6 @@ const CanvasWindowCard: React.FC = ({ const items: CardMenuRow[] = [ { kind: 'header', label: selectName }, { label: tiling.isFullscreen ? 'Exit Full Screen' : 'Full Screen', onClick: () => tiling.applyZone(tiling.isFullscreen ? 'restore' : 'fullscreen') }, - { label: 'Tile to zone', submenu: tileMenuRows(tiling.applyZone, tiling.zone) }, ]; if (onMinimize) items.push({ label: minimized ? 'Restore' : 'Minimize', onClick: onMinimize }); if (onBringToFront) items.push({ label: 'Bring to front', onClick: () => onBringToFront(cardId, cardType) }); diff --git a/frontend/src/app/pages/Dashboard/cards/WindowControls.tsx b/frontend/src/app/pages/Dashboard/cards/WindowControls.tsx index 8029297e..05371d23 100644 --- a/frontend/src/app/pages/Dashboard/cards/WindowControls.tsx +++ b/frontend/src/app/pages/Dashboard/cards/WindowControls.tsx @@ -1,12 +1,10 @@ -import React, { useRef, useState } from 'react'; +import React from 'react'; import Box from '@mui/material/Box'; -import { TILE_ZONES } from '../canvas/tiledGeometry'; -import { TILE_GROUPS, ZONE_LABELS } from './tileZones'; interface WindowControlsProps { onClose: () => void; onMinimize: () => void; - onTile: (zone: string) => void; // a TILE_ZONES key, 'fullscreen', or 'restore' + onTile: (zone: string) => void; // 'fullscreen' or 'restore' tiled?: boolean; /** Onboarding anchor riding the close dot (the settings flow points its cursor here). */ closeDataOnboarding?: string; @@ -14,8 +12,7 @@ interface WindowControlsProps { // macOS-style traffic lights on every card = the "AI OS" window feel. Grey at rest so a canvas // full of cards isn't a wall of color; they colorize when the parent .osw-card is hovered, and the -// close/minus/plus symbols reveal on hovering the group. Hovering the GREEN dot opens the tiling menu (Fill, -// Halves, Quarters, Thirds), exactly like macOS; clicking green = Full Screen (or restore if tiled). +// close/minus/plus symbols reveal on hovering the group. Clicking green = Full Screen (or restore if tiled). const RED = '#ff5f57'; const YELLOW = '#febc2e'; const GREEN = '#28c840'; @@ -50,21 +47,6 @@ export const ARC_CHIP_SX: Record = { }; function WindowControls({ onClose, onMinimize, onTile, tiled, closeDataOnboarding }: WindowControlsProps): React.ReactElement { - const [menuOpen, setMenuOpen] = useState(false); - // Menu DOM (12 tiles + labels, ~30 nodes) mounts on first green-dot hover, not per card at boot. - const [menuHot, setMenuHot] = useState(false); - // Right-edge hosts (the minimized stack) open the menu leftward so it never clips off-window. - const [alignRight, setAlignRight] = useState(false); - const greenRef = useRef(null); - const closeTimer = useRef(null); - const openMenu = (): void => { - if (closeTimer.current) window.clearTimeout(closeTimer.current); - const rect = greenRef.current?.getBoundingClientRect(); - if (rect) setAlignRight(rect.left + 224 > window.innerWidth); - if (!menuHot) { setMenuHot(true); requestAnimationFrame(() => setMenuOpen(true)); return; } - setMenuOpen(true); - }; - const scheduleClose = (): void => { closeTimer.current = window.setTimeout(() => setMenuOpen(false), 550); }; const stop = (e: React.PointerEvent | React.MouseEvent): void => { e.stopPropagation(); }; const btn = (color: string, symbol: string, onClick: () => void, label: string, slot: string): React.ReactElement => ( @@ -76,7 +58,7 @@ function WindowControls({ onClose, onMinimize, onTile, tiled, closeDataOnboardin ); return ( - {btn(RED, '×', onClose, 'Close', 'close')} {btn(YELLOW, '−', onMinimize, 'Minimize', 'minimize')} - + { e.stopPropagation(); onTile(tiled ? 'restore' : 'fullscreen'); }} onPointerDown={stop} sx={dotSx(GREEN)}> {tiled ? '−' : '+'} - {menuHot && - {TILE_GROUPS.map((g) => ( - - {g.label} - - {g.zones.map((zone) => { - const z = TILE_ZONES[zone]; - return ( - { e.stopPropagation(); setMenuOpen(false); onTile(zone); }} - sx={{ position: 'relative', flex: 1, height: 32, border: '1px solid rgba(0,0,0,0.08)', borderRadius: '6px', background: '#F5F4ED', cursor: 'pointer', overflow: 'hidden', transition: 'border-color .12s, background .12s', '&:hover': { borderColor: '#ae5630', background: '#ae56300d' } }}> - - - ); - })} - - - ))} - } ); diff --git a/frontend/src/app/pages/Dashboard/cards/agentCardMenuRows.ts b/frontend/src/app/pages/Dashboard/cards/agentCardMenuRows.ts index 4c2f05d3..ca62a56b 100644 --- a/frontend/src/app/pages/Dashboard/cards/agentCardMenuRows.ts +++ b/frontend/src/app/pages/Dashboard/cards/agentCardMenuRows.ts @@ -8,7 +8,6 @@ import { handleSlashCommand } from '@/app/pages/AgentChat/ChatInput/hooks/slashC import type { AppDispatch } from '@/shared/state/store'; import type { CardMenuRow } from '../desktop/openCardContextMenu'; import { chord } from '../desktop/chord'; -import { tileMenuRows } from './tileMenuRows'; interface AgentMenuArgs { session: AgentSession; @@ -26,7 +25,6 @@ export function agentCardMenuRows({ session, dispatch, expanded, tileZone, expan return [ { label: expanded ? 'Collapse' : 'Open', shortcut: chord('enter'), onClick: () => dispatch(expanded ? collapseSession(session.id) : expandSession(session.id)) }, { label: tileZone === 'fullscreen' ? 'Exit Full Screen' : 'Full Screen', onClick: () => onTile(tileZone === 'fullscreen' ? 'restore' : 'fullscreen') }, - { label: 'Tile to zone', submenu: tileMenuRows(onTile, tileZone) }, { label: 'Bring to front', onClick: () => dispatch(bringToFront({ id: session.id, type: 'agent' })) }, { kind: 'separator' }, { diff --git a/frontend/src/app/pages/Dashboard/cards/browserCardMenuRows.ts b/frontend/src/app/pages/Dashboard/cards/browserCardMenuRows.ts index ca7f3631..03e071a3 100644 --- a/frontend/src/app/pages/Dashboard/cards/browserCardMenuRows.ts +++ b/frontend/src/app/pages/Dashboard/cards/browserCardMenuRows.ts @@ -4,7 +4,6 @@ import { setClipboardCards } from '@/shared/dashboardClipboard'; import type { AppDispatch } from '@/shared/state/store'; import type { CardMenuRow } from '../desktop/openCardContextMenu'; import { chord } from '../desktop/chord'; -import { tileMenuRows } from './tileMenuRows'; interface BrowserNav { reload: () => void; @@ -49,7 +48,6 @@ export function browserCardMenuRows({ { label: 'Copy URL', disabled: !activeUrl, onClick: () => { void navigator.clipboard.writeText(activeUrl); } }, { kind: 'separator' }, { label: tileZone === 'fullscreen' ? 'Exit Full Screen' : 'Full Screen', onClick: () => onTile(tileZone === 'fullscreen' ? 'restore' : 'fullscreen') }, - { label: 'Tile to zone', submenu: tileMenuRows(onTile, tileZone) }, { label: isMinimized ? 'Restore' : 'Minimize', onClick: onMinimize }, { label: 'Bring to front', onClick: () => dispatch(bringToFront({ id: browserId, type: 'browser' })) }, { diff --git a/frontend/src/app/pages/Dashboard/cards/tileMenuRows.ts b/frontend/src/app/pages/Dashboard/cards/tileMenuRows.ts deleted file mode 100644 index d981538e..00000000 --- a/frontend/src/app/pages/Dashboard/cards/tileMenuRows.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { CardMenuRow } from '../desktop/openCardContextMenu'; -import { TILE_GROUPS, ZONE_LABELS } from './tileZones'; - -// The green-dot tiling grid, spelled out as words for the right-click path. Same catalog, so the two -// surfaces always offer the same zones. -export function tileMenuRows(onTile: (zone: string) => void, currentZone?: string): CardMenuRow[] { - const rows: CardMenuRow[] = []; - for (const group of TILE_GROUPS) { - rows.push({ kind: 'header', label: group.label }); - for (const zone of group.zones) { - rows.push({ label: ZONE_LABELS[zone], checked: currentZone === zone, onClick: () => onTile(zone) }); - } - } - return rows; -} diff --git a/frontend/src/app/pages/Dashboard/cards/tileZones.ts b/frontend/src/app/pages/Dashboard/cards/tileZones.ts deleted file mode 100644 index 39ab268d..00000000 --- a/frontend/src/app/pages/Dashboard/cards/tileZones.ts +++ /dev/null @@ -1,14 +0,0 @@ -// The zone catalog the tiling MENUS render. The geometry itself lives in canvas/tiledGeometry. - -export const ZONE_LABELS: Record = { - fill: 'Fill', left: 'Left half', right: 'Right half', top: 'Top half', bottom: 'Bottom half', - tl: 'Top left', tr: 'Top right', bl: 'Bottom left', br: 'Bottom right', -}; - -// The one grid every tiling surface renders: the green-dot hover menu and the right-click "Tile to -// zone" submenu both map this, so they can never drift apart on which zones exist. Thirds were cut -// (Eric 2026-08-09): the geometry still restores an old t3* card fine, the menus just stop offering it. -export const TILE_GROUPS: { label: string; zones: string[] }[] = [ - { label: 'Fill and halves', zones: ['fill', 'left', 'right', 'top', 'bottom'] }, - { label: 'Quarters', zones: ['tl', 'tr', 'bl', 'br'] }, -]; diff --git a/frontend/src/app/pages/Dashboard/cards/viewCardMenuRows.ts b/frontend/src/app/pages/Dashboard/cards/viewCardMenuRows.ts index 3b0d8aa6..afb66150 100644 --- a/frontend/src/app/pages/Dashboard/cards/viewCardMenuRows.ts +++ b/frontend/src/app/pages/Dashboard/cards/viewCardMenuRows.ts @@ -6,7 +6,6 @@ import { API_BASE } from '@/shared/config'; import type { AppDispatch } from '@/shared/state/store'; import type { CardMenuRow } from '../desktop/openCardContextMenu'; import { chord } from '../desktop/chord'; -import { tileMenuRows } from './tileMenuRows'; interface ViewMenuArgs { output: Output; @@ -31,7 +30,6 @@ export function viewCardMenuRows({ { label: 'Open another instance', onClick: () => dispatch(addViewCard({ outputId: output.id, newInstance: true })) }, { kind: 'separator' }, { label: tileZone === 'fullscreen' ? 'Exit Full Screen' : 'Full Screen', onClick: () => onTile(tileZone === 'fullscreen' ? 'restore' : 'fullscreen') }, - { label: 'Tile to zone', submenu: tileMenuRows(onTile, tileZone) }, { label: isMinimized ? 'Restore' : 'Minimize', onClick: onMinimize }, { label: 'Bring to front', onClick: () => dispatch(bringToFront({ id: cardKey, type: 'view' })) }, { kind: 'separator' }, diff --git a/frontend/src/app/pages/Dashboard/desktop/MinimizedStack.tsx b/frontend/src/app/pages/Dashboard/desktop/MinimizedStack.tsx index fa5990a3..31377258 100644 --- a/frontend/src/app/pages/Dashboard/desktop/MinimizedStack.tsx +++ b/frontend/src/app/pages/Dashboard/desktop/MinimizedStack.tsx @@ -12,7 +12,6 @@ import { dropMinimizedShot } from './minimizedShots'; import { buildMinimizedEntries, MinimizedEntry, MinimizedRect } from './minimizedEntries'; import MinimizedTile, { MINIMIZED_TILE_W } from './MinimizedTile'; import { openCardContextMenu } from './openCardContextMenu'; -import { tileMenuRows } from '../cards/tileMenuRows'; import type { BrowserCardPosition, ViewCardPosition } from '@/shared/state/dashboardLayoutSlice'; import type { Output } from '@/shared/state/outputsSlice'; @@ -113,9 +112,6 @@ function MinimizedStack({ browserCards, viewCards, outputs, selectedIds, onResto items: [ { label: 'Restore', onClick: () => restore(entry) }, { label: 'Restore full screen', onClick: () => tile(entry, 'fullscreen') }, - ...(entry.kind === 'browser' || entry.kind === 'view' - ? [{ label: 'Tile to zone', submenu: tileMenuRows((zone) => tile(entry, zone)) }] - : []), { kind: 'separator' }, { label: 'Close', danger: true, onClick: () => close(entry) }, ], diff --git a/frontend/src/app/pages/Dashboard/desktop/desktop.css b/frontend/src/app/pages/Dashboard/desktop/desktop.css index e4924f8c..bc45e5dc 100644 --- a/frontend/src/app/pages/Dashboard/desktop/desktop.css +++ b/frontend/src/app/pages/Dashboard/desktop/desktop.css @@ -5,10 +5,3 @@ body.dashboard-marquee-active * { -webkit-backdrop-filter: none !important; } -/* While a traffic-light tile menu is open, its lights cluster stays visible and interactive even - when the hover that revealed it has moved on (to the menu itself); otherwise the menu fades - mid-approach and the click lands on nothing. */ -.osw-pill-lights:has(.osw-window-lights[data-tilemenu-open="1"]) { - opacity: 1 !important; - pointer-events: auto !important; -}