diff --git a/frontend/src/lib/components/graph/FlowGraphV2.svelte b/frontend/src/lib/components/graph/FlowGraphV2.svelte index 16788a4054..052df58d63 100644 --- a/frontend/src/lib/components/graph/FlowGraphV2.svelte +++ b/frontend/src/lib/components/graph/FlowGraphV2.svelte @@ -472,8 +472,8 @@ selectionManager.handleKeyDown(event, nodes) } - function handleKeyUp(_event: KeyboardEvent) { - // Keep for potential future use + function handleKeyUp(event: KeyboardEvent) { + selectionManager.handleKeyUp(event) } async function updateStores() { diff --git a/frontend/src/lib/components/graph/SelectionBoundingBox.svelte b/frontend/src/lib/components/graph/SelectionBoundingBox.svelte index ef0e708913..dfacad05e8 100644 --- a/frontend/src/lib/components/graph/SelectionBoundingBox.svelte +++ b/frontend/src/lib/components/graph/SelectionBoundingBox.svelte @@ -59,7 +59,7 @@ {#if bounds() && selectedNodes.length > 1} {@const currentBounds = bounds()!}
-
- {selectedNodes.length} nodes selected -
{/if} diff --git a/frontend/src/lib/components/graph/selectionUtils.svelte.ts b/frontend/src/lib/components/graph/selectionUtils.svelte.ts index ce22d47e29..3b408f1385 100644 --- a/frontend/src/lib/components/graph/selectionUtils.svelte.ts +++ b/frontend/src/lib/components/graph/selectionUtils.svelte.ts @@ -10,6 +10,9 @@ export interface SelectionState { export class SelectionManager { public selectedIds = $state([]) #selectionMode = $state<'normal' | 'rect-select'>('normal') + #modeSource = $state<'button' | 'keyboard' | 'temporary'>('button') + #previousMode = $state<'normal' | 'rect-select'>('normal') + #cmdKeyPressed = $state(false) constructor() {} @@ -26,11 +29,36 @@ export class SelectionManager { } set mode(mode: 'normal' | 'rect-select') { + this.#previousMode = this.#selectionMode this.#selectionMode = mode - if (mode === 'normal') { - // When exiting rect mode, preserve the first item if there are selections - const firstSelected = this.selectedIds[0] - this.selectId(firstSelected ?? 'settings') + // Default to button source when mode is set directly (for backward compatibility) + if (this.#modeSource !== 'temporary') { + this.#modeSource = 'button' + } + // Note: No automatic selection clearing when changing modes - preserve current selection + } + + // Toggle mode temporarily (for cmd key hold behavior) + toggleModeTemporary() { + this.#previousMode = this.#selectionMode + this.#modeSource = 'temporary' + this.#selectionMode = this.#selectionMode === 'normal' ? 'rect-select' : 'normal' + } + + // Toggle mode persistently (for cmd key tap behavior) + toggleModePersistent() { + this.#previousMode = this.#selectionMode + this.#modeSource = 'keyboard' + this.#selectionMode = this.#selectionMode === 'normal' ? 'rect-select' : 'normal' + // Note: Preserve current selection when toggling modes + } + + // Revert temporary mode change + revertTemporaryMode() { + if (this.#modeSource === 'temporary') { + this.#selectionMode = this.#previousMode + this.#modeSource = 'button' // Reset to default button source + // Note: Preserve current selection when reverting temporary mode changes } } @@ -173,9 +201,14 @@ export class SelectionManager { // Handle keyboard shortcuts handleKeyDown(event: KeyboardEvent, nodes?: Node[]) { if (event.key === 'Escape') { - if (this.#selectionMode === 'rect-select') { - // Exit rect mode (this will preserve first item via mode setter) - this.mode = 'normal' + // Escape key clears selection regardless of mode + this.clearSelection() + } else if (event.key === 'Meta' || event.key === 'Cmd') { + // Cmd/Meta key pressed - temporarily toggle mode + if (!this.#cmdKeyPressed) { + this.#cmdKeyPressed = true + event.preventDefault() + this.toggleModeTemporary() } } else if ((event.ctrlKey || event.metaKey) && event.key === 'a') { event.preventDefault() @@ -186,4 +219,13 @@ export class SelectionManager { } } } + + // Handle keyboard releases + handleKeyUp(event: KeyboardEvent) { + if (event.key === 'Meta' || event.key === 'Cmd') { + // Cmd/Meta key released - revert temporary mode change + this.#cmdKeyPressed = false + this.revertTemporaryMode() + } + } }