diff --git a/frontend/src/DesktopWorkspace.jsx b/frontend/src/DesktopWorkspace.jsx index 776bcf6..0ad4b2f 100644 --- a/frontend/src/DesktopWorkspace.jsx +++ b/frontend/src/DesktopWorkspace.jsx @@ -581,6 +581,8 @@ const DesktopWorkspace = ({ activeTagIds = [], selectedDocumentIds = [], onClearSelection = null, + detailPanelOpen = false, + onCloseDetailPanel = null, helpOpen = false, onHelpClose = null, tenantId = null, @@ -1849,6 +1851,8 @@ const recalcVisibleDocIds = useCallback(() => { markLayoutDirty, selectedDocumentIds, onClearSelection, + detailPanelOpen, + onCloseDetailPanel, }), [ activeTagSet, @@ -1897,6 +1901,8 @@ const recalcVisibleDocIds = useCallback(() => { onClearSelection, onDocumentStackSelect, markLayoutDirty, + detailPanelOpen, + onCloseDetailPanel, ], ); @@ -1945,6 +1951,8 @@ const DesktopWorkspaceView = () => { onDocumentStackSelect, selectedDocumentIds, onClearSelection, + detailPanelOpen, + onCloseDetailPanel, documentLookup, } = useDesktopContext(); @@ -2151,11 +2159,8 @@ const DesktopWorkspaceView = () => { } const key = event.key; - if (!(key === ' ' || key === 'Space' || key === 'Spacebar')) { - return; - } - - if (!selectedDocumentIds || selectedDocumentIds.length === 0) { + const spacePressed = key === ' ' || key === 'Space' || key === 'Spacebar'; + if (!spacePressed) { return; } @@ -2173,13 +2178,27 @@ const DesktopWorkspaceView = () => { } } - event.preventDefault(); - onClearSelection(); + const hasSelection = Array.isArray(selectedDocumentIds) && selectedDocumentIds.length > 0; + if (hasSelection) { + event.preventDefault(); + onClearSelection(); + return; + } + + if (detailPanelOpen && typeof onCloseDetailPanel === 'function') { + event.preventDefault(); + onCloseDetailPanel(); + } }; window.addEventListener('keydown', handleKeyDown, true); return () => window.removeEventListener('keydown', handleKeyDown, true); - }, [onClearSelection, selectedDocumentIds]); + }, [ + onClearSelection, + selectedDocumentIds, + detailPanelOpen, + onCloseDetailPanel, + ]); return ( <> @@ -2284,32 +2303,39 @@ const DesktopWorkspaceView = () => { (event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey; let stackDocIds = null; - if ( - metaOrCtrlOnly - && selectedDocumentIds.length === 0 - && typeof onDocumentStackSelect === 'function' - ) { + let appliedStackSelection = false; + + if (metaOrCtrlOnly) { const hits = resolveStackDocIds(event, doc.id); if (Array.isArray(hits) && hits.length > 0) { stackDocIds = hits; - onDocumentStackSelect(hits, event); + const hasStack = hits.length > 1; + if (hasStack && alreadySelected && typeof onDocumentStackSelect === 'function') { + onDocumentStackSelect(hits, event); + appliedStackSelection = true; + } } } - const stackHandled = Array.isArray(stackDocIds) && stackDocIds.length > 0; - - if ( - !stackHandled - && typeof onDocumentPointerSelect === 'function' - && (!alreadySelected - || event.metaKey - || event.ctrlKey + const shouldInvokePointerSelect = + typeof onDocumentPointerSelect === 'function' + && ( + !metaOrCtrlOnly + || !alreadySelected || event.shiftKey - || event.altKey) - ) { + || event.altKey + || !stackDocIds + || stackDocIds.length <= 1 + ); + + if (shouldInvokePointerSelect) { onDocumentPointerSelect(doc.id, event); } - handlePointerDown(event, doc.id, { stackDocIds }); + + handlePointerDown(event, doc.id, { + stackDocIds, + stackSelectionApplied: appliedStackSelection, + }); }} onPointerMove={handlePointerMove} onPointerUp={handlePointerUp} diff --git a/frontend/src/app/AppLayout.jsx b/frontend/src/app/AppLayout.jsx index 01cf362..a571aa2 100644 --- a/frontend/src/app/AppLayout.jsx +++ b/frontend/src/app/AppLayout.jsx @@ -4885,6 +4885,8 @@ const AppLayout = () => { tenantId: currentTenantId, selectedDocumentIds, onClearSelection: clearDocumentSelection, + detailPanelOpen, + onCloseDetailPanel: handleDetailPanelClose, resolveThumbnailUrl: resolveThumbnailUrlForDoc, onAssignTagToDocument: handleDocumentTagAttach, onRemoveTagFromDocument: handleTagRemove, @@ -4911,6 +4913,8 @@ const AppLayout = () => { deskHelpOpen, selectedDocumentIds, clearDocumentSelection, + detailPanelOpen, + handleDetailPanelClose, resolveThumbnailUrlForDoc, handleDocumentTagAttach, handleTagRemove, diff --git a/frontend/src/desktop/useDocumentDrag.js b/frontend/src/desktop/useDocumentDrag.js index a2d6369..2c2b18f 100644 --- a/frontend/src/desktop/useDocumentDrag.js +++ b/frontend/src/desktop/useDocumentDrag.js @@ -32,6 +32,7 @@ const useDocumentDrag = () => { containerRef, onDocumentOpen, onInspectDocument, + onDocumentStackSelect, selectedDocumentIds, markLayoutDirty, } = useDesktopContext(); @@ -286,6 +287,7 @@ const useDocumentDrag = () => { .map((value) => (value != null ? String(value) : null)) .filter(Boolean) : null; + const stackSelectionAppliedInitial = Boolean(options?.stackSelectionApplied); let selectionIds = Array.isArray(selectedDocumentIds) ? selectedDocumentIds.map((id) => String(id)) @@ -416,6 +418,8 @@ const useDocumentDrag = () => { ? performance.now() : Date.now(); + const hasStackSource = Array.isArray(stackDocIdsOption) && stackDocIdsOption.length > 1; + dragStateRef.current = { docId, docKey, @@ -444,9 +448,11 @@ const useDocumentDrag = () => { containerRectTop: containerTop, isGroup: isGroupDrag, groupDocIds, - groupItems, - groupElevated: !isGroupDrag, - }; + groupItems, + groupElevated: !isGroupDrag, + stackDocIds: hasStackSource ? stackDocIdsOption : null, + stackSelectionApplied: stackSelectionAppliedInitial || !hasStackSource, + }; setDraggingId(docId); @@ -524,6 +530,17 @@ const useDocumentDrag = () => { return; } state.moved = true; + if ( + state.isGroup + && !state.stackSelectionApplied + && Array.isArray(state.stackDocIds) + && state.stackDocIds.length > 1 + ) { + if (typeof onDocumentStackSelect === 'function') { + onDocumentStackSelect(state.stackDocIds); + } + state.stackSelectionApplied = true; + } if (!state.groupElevated) { const layout = layoutRef.current; const sortedGroup = state.groupDocIds @@ -811,6 +828,7 @@ const useDocumentDrag = () => { applyTransform, recalcVisibleDocIds, debugDrag, + onDocumentStackSelect, ], ); diff --git a/frontend/src/detail/PreviewZoomOverlay.jsx b/frontend/src/detail/PreviewZoomOverlay.jsx index 4388437..e48c0d6 100644 --- a/frontend/src/detail/PreviewZoomOverlay.jsx +++ b/frontend/src/detail/PreviewZoomOverlay.jsx @@ -173,13 +173,33 @@ const PreviewZoomOverlay = ({ return; } - if (event.key === 'Escape') { + const key = event.key; + + if (key === ' ' || key === 'Space' || key === 'Spacebar') { + const target = event.target; + if (target instanceof HTMLElement) { + const tag = target.tagName ? target.tagName.toLowerCase() : ''; + if ( + target.isContentEditable + || tag === 'input' + || tag === 'textarea' + || tag === 'select' + ) { + return; + } + } event.preventDefault(); onClose(); return; } - if (event.key === 'ArrowLeft') { + if (key === 'Escape') { + event.preventDefault(); + onClose(); + return; + } + + if (key === 'ArrowLeft') { if (activeDisplay?.canGoPrev && activeDisplay?.goPrev) { event.preventDefault(); activeDisplay.goPrev(); @@ -187,7 +207,7 @@ const PreviewZoomOverlay = ({ return; } - if (event.key === 'ArrowRight') { + if (key === 'ArrowRight') { if (activeDisplay?.canGoNext && activeDisplay?.goNext) { event.preventDefault(); activeDisplay.goNext();