diff --git a/frontend/src/app/AppLayout.jsx b/frontend/src/app/AppLayout.jsx
index cda9ee9..c2560e6 100644
--- a/frontend/src/app/AppLayout.jsx
+++ b/frontend/src/app/AppLayout.jsx
@@ -5030,7 +5030,7 @@ const AppLayout = () => {
onExit: handleDeskExit,
onRefresh: refreshCurrentFolder,
onDocumentOpen: handleDeskDocumentOpen,
- onInspectDocument: null,
+ onInspectDocument: inspectDocument,
onEntryPointer: handleEntryPointerCore,
onDocumentStackSelect: handleDeskDocumentStackSelect,
onPromoteSelection: promoteSelectionOrder,
@@ -5088,6 +5088,7 @@ const AppLayout = () => {
detailPanelOpen,
handleDetailPanelClose,
handleDeskDocumentOpen,
+ inspectDocument,
resolveThumbnailUrlForDoc,
handleDocumentTagDrop,
handleTagRemove,
diff --git a/frontend/src/desktop/DesktopWorkspace.jsx b/frontend/src/desktop/DesktopWorkspace.jsx
index 86b952a..ad65cd2 100644
--- a/frontend/src/desktop/DesktopWorkspace.jsx
+++ b/frontend/src/desktop/DesktopWorkspace.jsx
@@ -703,7 +703,6 @@ const DesktopWorkspace = ({
onDocumentOpen,
onDocumentStackSelect,
onEntryPointer,
- onInspectDocument,
onPromoteSelection,
openOverlayForDoc,
overlayDisplay,
@@ -717,6 +716,7 @@ const DesktopWorkspace = ({
setDraggingId,
selectedDocumentIds,
detailPanelOpen,
+ onInspectDocument,
markLayoutDirty,
tagDropTargetId,
visibleDocIds,
@@ -804,7 +804,7 @@ const DesktopWorkspaceView = ({
markLayoutDirty,
});
- const { getCardPointerHandlers, handleShellKeyDown } = useDeskPointer({
+ const { getCardPointerHandlers, handleShellKeyDown, focusShell } = useDeskPointer({
containerRef,
items,
layoutRef,
@@ -819,11 +819,27 @@ const DesktopWorkspaceView = ({
onPromoteSelection,
onDocumentOpen,
selectedDocumentIds,
- onClearSelection,
detailPanelOpen,
onCloseDetailPanel,
+ openOverlayForDoc,
});
+ useEffect(() => {
+ focusShell();
+ }, [focusShell]);
+
+ useEffect(() => {
+ if (selectedDocumentIds.length) {
+ focusShell();
+ }
+ }, [focusShell, selectedDocumentIds.length]);
+
+ useEffect(() => {
+ if (!detailPanelOpen) {
+ focusShell();
+ }
+ }, [detailPanelOpen, focusShell]);
+
const allSizesReady = items.every((doc) => ensureDocumentSize(doc));
@@ -834,6 +850,7 @@ const DesktopWorkspaceView = ({
if (event.target === event.currentTarget && typeof onClearSelection === 'function') {
onClearSelection();
}
+ focusShell();
}}
>
{!allSizesReady ? (
diff --git a/frontend/src/desktop/pointer/pointerUtils.js b/frontend/src/desktop/pointer/pointerUtils.js
index e6603c1..160d78a 100644
--- a/frontend/src/desktop/pointer/pointerUtils.js
+++ b/frontend/src/desktop/pointer/pointerUtils.js
@@ -40,7 +40,7 @@ export const createPointerIntent = ({
clickAction = CLICK_ACTIONS.addStack;
dragAction = DRAG_ACTIONS.dragSelectStack;
} else if (alreadySelected) {
- clickAction = CLICK_ACTIONS.openDetail;
+ clickAction = CLICK_ACTIONS.selectSingle;
dragAction = selectionCount > 1 ? DRAG_ACTIONS.dragSelection : DRAG_ACTIONS.dragSelectSingle;
} else {
clickAction = CLICK_ACTIONS.selectSingle;
diff --git a/frontend/src/desktop/pointer/useDeskPointer.js b/frontend/src/desktop/pointer/useDeskPointer.js
index 0206a1c..855f955 100644
--- a/frontend/src/desktop/pointer/useDeskPointer.js
+++ b/frontend/src/desktop/pointer/useDeskPointer.js
@@ -37,9 +37,9 @@ export const useDeskPointer = ({
onPromoteSelection,
onDocumentOpen,
selectedDocumentIds,
- onClearSelection,
detailPanelOpen,
onCloseDetailPanel,
+ openOverlayForDoc = null,
}) => {
const pointerIntentRef = useRef(null);
const pointerStartRef = useRef({ x: 0, y: 0 });
@@ -401,9 +401,16 @@ export const useDeskPointer = ({
}
}
- if (Array.isArray(selectedDocumentIds) && selectedDocumentIds.length > 0) {
+ if (
+ typeof openOverlayForDoc === 'function'
+ && Array.isArray(selectedDocumentIds)
+ && selectedDocumentIds.length > 0
+ ) {
event.preventDefault();
- onClearSelection?.();
+ const targetId = selectedDocumentIds[selectedDocumentIds.length - 1];
+ if (targetId) {
+ openOverlayForDoc(targetId);
+ }
return;
}
@@ -412,12 +419,18 @@ export const useDeskPointer = ({
safeInvoke(onCloseDetailPanel);
}
},
- [detailPanelOpen, onClearSelection, onCloseDetailPanel, selectedDocumentIds],
+ [detailPanelOpen, onCloseDetailPanel, openOverlayForDoc, selectedDocumentIds],
);
return {
getCardPointerHandlers,
handleShellKeyDown,
+ focusShell: () => {
+ const shell = containerRef.current;
+ if (shell && typeof shell.focus === 'function') {
+ shell.focus({ preventScroll: true });
+ }
+ },
};
};
diff --git a/frontend/src/desktop/useDocumentDrag.js b/frontend/src/desktop/useDocumentDrag.js
index 0a73d49..354e20c 100644
--- a/frontend/src/desktop/useDocumentDrag.js
+++ b/frontend/src/desktop/useDocumentDrag.js
@@ -54,25 +54,18 @@ const useDocumentDrag = (options = {}) => {
const tapHandler = usePointerTap({
delay: 220,
- onSingle: ({ data, event }) => {
- if (!data || !data.docId) {
- return;
- }
- if (event && (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)) {
- return;
- }
- if (typeof onInspectDocument === 'function') {
- onInspectDocument(data.docId);
- }
- },
+ onSingle: () => {},
onDouble: ({ data, event }) => {
if (!data || !data.docId) {
return;
}
- if (event && (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)) {
+ if (event?.altKey) {
+ openOverlayForDoc(data.docId, data.originInfo);
return;
}
- openOverlayForDoc(data.docId, data.originInfo);
+ if (typeof onInspectDocument === 'function') {
+ onInspectDocument(data.docId, event);
+ }
},
});
const dragStateRef = useRef(null);
diff --git a/frontend/src/documents/DocumentsPanel.jsx b/frontend/src/documents/DocumentsPanel.jsx
index 9e1d759..740b423 100644
--- a/frontend/src/documents/DocumentsPanel.jsx
+++ b/frontend/src/documents/DocumentsPanel.jsx
@@ -204,6 +204,9 @@ const DocumentsPanel = ({
const handleDocumentActivate = useCallback(
(doc, event) => {
+ if (!doc) {
+ return;
+ }
if (event) {
if (typeof event.preventDefault === 'function') {
event.preventDefault();
@@ -212,9 +215,13 @@ const DocumentsPanel = ({
event.stopPropagation();
}
}
- handleDocumentPreviewZoom(doc);
+ if (event?.altKey) {
+ handleDocumentPreviewZoom(doc);
+ return;
+ }
+ onInspectDocument?.(doc.id, event);
},
- [handleDocumentPreviewZoom],
+ [handleDocumentPreviewZoom, onInspectDocument],
);
const selectedRowKeySet = useMemo(() => new Set(selectedEntries || []), [selectedEntries]);
@@ -318,8 +325,7 @@ const DocumentsPanel = ({
} else {
const entry = getEntryByKey(activeRow.key);
if (entry?.document) {
- handleDocumentActivate(entry.document, event);
- onInspectDocument?.(entry.document.id, event);
+ handleDocumentPreviewZoom(entry.document);
}
}
}
@@ -355,14 +361,13 @@ const DocumentsPanel = ({
[
focusedRowKey,
getEntryByKey,
- handleDocumentActivate,
navigableRowKeys,
navigableRows,
onEntrySelection,
onFocusedRowChange,
onFolderSelect,
- onInspectDocument,
selectedEntries,
+ handleDocumentPreviewZoom,
],
);
@@ -500,16 +505,8 @@ const DocumentsPanel = ({
event,
);
}
-
- if (
- typeof onInspectDocument === 'function'
- && !isPointerModifierEvent(event)
- && isPrimaryPointerEvent(event)
- ) {
- onInspectDocument(doc.id, event);
- }
},
- [onEntryPointer, onInspectDocument],
+ [onEntryPointer],
);
const handleFolderClick = useCallback(
@@ -980,6 +977,7 @@ export const createDocumentsSurface = ({
onMoveDocumentsToFolder,
searchIncludeDescendants,
onToggleSearchIncludeDescendants,
+ onInspectDocument,
} = tableProps;
const title = Array.isArray(searchResults) ? 'Search results' : currentFolderName;
@@ -1044,7 +1042,13 @@ export const createDocumentsSurface = ({
breadcrumbs,
selectionLabel: null,
floatingActions,
- content: ,
+ content: (
+
+ ),
detail,
});
diff --git a/frontend/src/ui/icons.js b/frontend/src/ui/icons.js
index 1e85254..6b4cb25 100644
--- a/frontend/src/ui/icons.js
+++ b/frontend/src/ui/icons.js
@@ -38,6 +38,7 @@ import {
IconLoader,
IconSortAscendingLetters,
IconSortDescendingLetters,
+ IconFileInfo,
} from '@tabler/icons-react';
import FolderSvg from '../assets/folder.svg';
@@ -176,6 +177,15 @@ export const InfoIcon = ({ className, size = '1em', stroke = 1.6, ...rest }) =>
/>
);
+export const FileInfoIcon = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
+
+);
+
export const DetailPanelCollapseIcon = ({ className, size = '1em', stroke = 1.6, ...rest }) => (