From ef48344a2186d3c50ff7b75a10968961311aa9cf Mon Sep 17 00:00:00 2001 From: abccodes Date: Tue, 23 Jun 2026 02:40:09 -0700 Subject: [PATCH] [aidan] fix/selection-tool: never select the workflows app, and exit the tool on Escape without dropping selections --- .../editor/useDomElementSelector.ts | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/components/editor/useDomElementSelector.ts b/frontend/src/app/components/editor/useDomElementSelector.ts index d0ea73ac..cf50b781 100644 --- a/frontend/src/app/components/editor/useDomElementSelector.ts +++ b/frontend/src/app/components/editor/useDomElementSelector.ts @@ -6,9 +6,13 @@ const SELECT_ATTR = 'data-select-type'; const SELECT_ID_ATTR = 'data-select-id'; const SELECT_META_ATTR = 'data-select-meta'; -const DRAG_SELECT_TYPES = ['agent-card', 'view-card', 'browser-card', 'workflow-card', 'workflows-hub-card'] as const; +const DRAG_SELECT_TYPES = ['agent-card', 'view-card', 'browser-card', 'workflow-card'] as const; const DRAG_SELECTOR = DRAG_SELECT_TYPES.map((t) => `[${SELECT_ATTR}="${t}"]`).join(','); +// The Workflows app window is a full app surface, not a card you attach as +// context, so the selection tool never targets it (neither drag nor click). +const NON_SELECTABLE_TYPES = new Set(['workflows-hub-card']); + export interface OverlayState { visible: boolean; top: number; @@ -46,6 +50,8 @@ function findSelectableAncestor(target: Element, excludeId?: string | null): Ele let current: Element | null = target; while (current) { if (current.hasAttribute(SELECT_ATTR)) { + const type = current.getAttribute(SELECT_ATTR); + if (type && NON_SELECTABLE_TYPES.has(type)) return null; if (excludeId && current.getAttribute(SELECT_ID_ATTR) === excludeId) return null; return current; } @@ -408,10 +414,22 @@ export function useDomElementSelector(): DomSelectorState { const prevUserSelect = document.body.style.userSelect; document.body.style.userSelect = 'none'; + // Escape just exits the tool: turn select mode off but leave the already + // attached elements alone. Capture + stopPropagation so it doesn't also + // clear the canvas selection while the tool is the thing in focus. + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key !== 'Escape') return; + e.preventDefault(); + e.stopPropagation(); + ctx.setSelectMode(false); + ctx.setExcludeSelectId(null); + }; + document.addEventListener('mousemove', handleMouseMove, true); document.addEventListener('mousedown', handleMouseDown, true); document.addEventListener('mouseup', handleMouseUp, true); document.addEventListener('click', handleClick, true); + document.addEventListener('keydown', handleKeyDown, true); return () => { document.body.style.userSelect = prevUserSelect; @@ -419,6 +437,7 @@ export function useDomElementSelector(): DomSelectorState { document.removeEventListener('mousedown', handleMouseDown, true); document.removeEventListener('mouseup', handleMouseUp, true); document.removeEventListener('click', handleClick, true); + document.removeEventListener('keydown', handleKeyDown, true); if (rafRef.current) cancelAnimationFrame(rafRef.current); if (dragPreviewRafRef.current) cancelAnimationFrame(dragPreviewRafRef.current); setOverlay(EMPTY_OVERLAY);