diff --git a/frontend/src/app/DocumentsRoute.tsx b/frontend/src/app/DocumentsRoute.tsx
index efd6028..f1b0ade 100644
--- a/frontend/src/app/DocumentsRoute.tsx
+++ b/frontend/src/app/DocumentsRoute.tsx
@@ -5,6 +5,7 @@ import {
DocumentsFilterProvider,
} from '../documents/context/DocumentsFilterContext';
import { PreviewProvider } from '../preview/PreviewContext';
+import { DocumentOpenProvider } from '../context/DocumentOpenContext';
import { useWorkspaceSurface } from './useWorkspaceSurface';
import { DocumentsHeaderBreadcrumb } from '../documents/panel/DocumentsPanelHeader';
import { SidebarProvider, useSidebarContext } from '../sidebar/SidebarContext';
@@ -83,7 +84,9 @@ const DocumentsRouteContent: React.FC = () => {
- {content}
+
+ {content}
+
);
diff --git a/frontend/src/context/DocumentOpenContext.tsx b/frontend/src/context/DocumentOpenContext.tsx
new file mode 100644
index 0000000..692ac2d
--- /dev/null
+++ b/frontend/src/context/DocumentOpenContext.tsx
@@ -0,0 +1,57 @@
+import React, { createContext, useContext, useCallback } from 'react';
+import { usePreviewContext } from '../preview/PreviewContext';
+import { useAppShell } from '../appShellContext';
+import type { Document } from '../types/documents';
+import type { Identifier } from '../types/identifiers';
+
+export type DocumentOpenTarget = 'preview' | 'sidepanel' | 'viewer';
+
+export interface DocumentOpenContextValue {
+ openDocument: (doc: Document, target?: DocumentOpenTarget) => void;
+}
+
+const DocumentOpenContext = createContext(null);
+
+export const useDocumentOpen = () => {
+ const context = useContext(DocumentOpenContext);
+ if (!context) {
+ throw new Error('useDocumentOpen must be used within a DocumentOpenProvider');
+ }
+ return context;
+};
+
+export const DocumentOpenProvider: React.FC<{ children: React.ReactNode; onOpenViewer?: (docId: Identifier) => void }> = ({ children, onOpenViewer }) => {
+ const { openPreview } = usePreviewContext();
+ const appShell = useAppShell();
+
+ // We cast appShell.openDetailPanel because AppShellContext is loosely typed
+ const openDetailPanel = appShell.openDetailPanel as ((args: { documentIds: Identifier[] }) => void) | undefined;
+
+ const openDocument = useCallback((doc: Document, target: DocumentOpenTarget = 'preview') => {
+ if (!doc) return;
+
+ switch (target) {
+ case 'preview':
+ openPreview(doc);
+ break;
+ case 'sidepanel':
+ if (openDetailPanel) {
+ openDetailPanel({ documentIds: [doc.id] });
+ } else {
+ console.warn('openDetailPanel is not available in AppShellContext');
+ }
+ break;
+ case 'viewer':
+ if (onOpenViewer) {
+ onOpenViewer(doc.id);
+ }
+ break;
+ }
+ }, [openPreview, openDetailPanel, onOpenViewer]);
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/frontend/src/desktop/DesktopWorkspace.tsx b/frontend/src/desktop/DesktopWorkspace.tsx
index 85b7968..6c6f474 100644
--- a/frontend/src/desktop/DesktopWorkspace.tsx
+++ b/frontend/src/desktop/DesktopWorkspace.tsx
@@ -18,8 +18,8 @@ import { createDocumentEntryKey } from '../app/entryKey';
import { PointerTrackingProvider, usePointerTracking } from './PointerTrackingContext';
import type { Identifier } from '../types/identifiers';
import type { DocumentsListEntry, Document } from '../types/documents';
-import { usePreviewContext } from '../preview/PreviewContext';
import { useAppState } from '../app/appState';
+import { useDocumentOpen } from '../context/DocumentOpenContext';
type TagLike = { id?: Identifier | null; label?: string; color?: string | null } | null;
type OverlaySource = { url: string; alt?: string | null; mimeType?: string | null; };
@@ -48,7 +48,6 @@ export interface DesktopWorkspaceProps {
entries: DocumentsListEntry[];
ensureAssetUrl?: (...args: any[]) => Promise;
getDocumentAsset?: (...args: any[]) => unknown;
- onDocumentActivate?: (doc: DeskDocument, event?: unknown) => void;
onSelectionChange?: (selectedIds: Identifier[]) => void;
onDocumentTagAttach?: (docId: Identifier, tagId: Identifier) => void;
onDocumentTagDetach?: (docId: Identifier, tagId: Identifier) => void;
@@ -83,14 +82,13 @@ const DesktopWorkspaceContent: React.FC = ({
entries,
ensureAssetUrl,
getDocumentAsset,
- onDocumentActivate,
onSelectionChange,
onDocumentTagAttach,
onDocumentTagDetach,
viewId,
defaultCardSize = 200,
}) => {
- const { openPreview } = usePreviewContext();
+ const { openDocument } = useDocumentOpen();
const { tenant } = useAppState();
const tenantId = tenant?.id as Identifier;
const { addPointer, removePointer } = usePointerTracking();
@@ -240,7 +238,7 @@ const DesktopWorkspaceContent: React.FC = ({
const doc = items.find(i => String(i.id) === lastId);
if (doc) {
e.preventDefault();
- openPreview(doc);
+ openDocument(doc, 'preview');
return;
}
}
@@ -352,7 +350,7 @@ const DesktopWorkspaceContent: React.FC = ({
window.addEventListener('keydown', handleWindowKeyDown);
return () => window.removeEventListener('keydown', handleWindowKeyDown);
- }, [selectedDocumentIds, items, openPreview, layoutStore, handleSelectionChange]);
+ }, [selectedDocumentIds, items, openDocument, layoutStore, handleSelectionChange]);
return (
<>
@@ -424,7 +422,10 @@ const DesktopWorkspaceContent: React.FC = ({
ensureAssetUrl={ensureAssetUrl}
getDocumentAsset={getDocumentAsset}
handleNavigatorSnapshot={() => { }}
- onDocumentActivate={(_id, event) => { onDocumentActivate?.(doc, event) }}
+ onDocumentActivate={(_id, event) => {
+ const isPreview = event && ((event as any).altKey || (event as any).button === 1);
+ openDocument(doc, isPreview ? 'preview' : 'sidepanel');
+ }}
layoutCard={layoutCard}
onTagDragEnter={tagInteractions.handleTagDragEnterDoc}
onTagDragOver={tagInteractions.handleTagDragOverDoc}
diff --git a/frontend/src/documents/DocumentsView.tsx b/frontend/src/documents/DocumentsView.tsx
index a95f855..ad219c4 100644
--- a/frontend/src/documents/DocumentsView.tsx
+++ b/frontend/src/documents/DocumentsView.tsx
@@ -3,7 +3,6 @@ import { useDocumentViewLogic, DocumentViewLogic } from './hooks/useDocumentView
import { useDocumentsNavigation } from './hooks/useDocumentsNavigation';
import { useWorkspaceSelectionContext } from '../app/WorkspaceSelectionContext';
import { usePanelManager } from '../app/PanelManagerContext';
-import { usePreviewContext } from '../preview/PreviewContext';
import DocumentsListRow from './components/DocumentsListRow';
import DocumentsGridCard from './components/DocumentsGridCard';
import DocumentsListContainer from './components/DocumentsListContainer';
@@ -24,7 +23,6 @@ const AbstractDocumentsView = void; chil
...props
}: AbstractDocumentsViewProps) => {
const { entries, onDocumentRename, onFolderRename, onFolderSelect, scrollRef, viewId } = props;
- const { openPreview } = usePreviewContext();
const viewLogic = useDocumentViewLogic({
onDocumentRename,
onFolderRename,
@@ -32,7 +30,6 @@ const AbstractDocumentsView = void; chil
const { handleKeyDown, handleFocus } = useDocumentsNavigation({
entries,
onFolderSelect,
- onPreview: openPreview,
viewMode: props.viewMode,
scrollRef: props.scrollRef,
});
@@ -105,7 +102,6 @@ const AbstractDocumentsView = void; chil
entry={entry}
viewLogic={viewLogic}
{...props}
- onPreview={openPreview}
/>
))}
diff --git a/frontend/src/documents/hooks/useDocumentItemLogic.ts b/frontend/src/documents/hooks/useDocumentItemLogic.ts
index 90deca2..5a4ae9a 100644
--- a/frontend/src/documents/hooks/useDocumentItemLogic.ts
+++ b/frontend/src/documents/hooks/useDocumentItemLogic.ts
@@ -1,6 +1,7 @@
import React, { type DragEvent } from 'react';
import { parseTagTransferPayload } from '../tagTransfer';
import { createDocumentEntryKey } from '../../app/entryKey';
+import { useDocumentOpen } from '../../context/DocumentOpenContext';
import type { Document } from '../../types/documents';
import type { DocumentsViewProps } from '../panel/DocumentsPanel';
import type { DocumentViewLogic } from './useDocumentViewLogic';
@@ -17,7 +18,6 @@ export const useDocumentItemLogic = (props: UseDocumentItemLogicProps) => {
doc,
viewLogic,
draggingDocumentIdsSet,
- onDocumentActivate,
onDocumentDragStart,
onDocumentDragEnd,
onDocumentTagDragStart,
@@ -54,6 +54,8 @@ export const useDocumentItemLogic = (props: UseDocumentItemLogicProps) => {
isEditingDoc && trimmedDocumentDraft.length > 0 && trimmedDocumentDraft !== doc.title;
const allowInlineDocumentEdit = onDocumentRename && isSelected && totalSelectionCount === 1;
+ const { openDocument } = useDocumentOpen();
+
const handlers = {
onClick: (event: React.MouseEvent) => {
if (props.onEntryPointer) {
@@ -63,7 +65,10 @@ export const useDocumentItemLogic = (props: UseDocumentItemLogicProps) => {
handleEntrySelection(key, event);
}
},
- onDoubleClick: (event: React.MouseEvent) => onDocumentActivate?.(doc, event),
+ onDoubleClick: (event: React.MouseEvent) => {
+ const isPreview = event && (event.altKey || event.button === 1);
+ openDocument(doc, isPreview ? 'preview' : 'sidepanel');
+ },
onDragStart: (event: DragEvent) => onDocumentDragStart?.(event, doc),
onDragEnd: (event: DragEvent) => onDocumentDragEnd?.(event),
onDragOver: (event: DragEvent) => onDocumentTagDragOver?.(event, doc.id),
diff --git a/frontend/src/documents/hooks/useDocumentsNavigation.ts b/frontend/src/documents/hooks/useDocumentsNavigation.ts
index 684f30c..99812d9 100644
--- a/frontend/src/documents/hooks/useDocumentsNavigation.ts
+++ b/frontend/src/documents/hooks/useDocumentsNavigation.ts
@@ -1,11 +1,11 @@
import React, { useCallback, useMemo } from 'react';
import type { DocumentsListEntry } from '../../types/documents';
import { useWorkspaceSelectionContext } from '../../app/WorkspaceSelectionContext';
+import { useDocumentOpen } from '../../context/DocumentOpenContext';
interface UseDocumentsNavigationProps {
entries: DocumentsListEntry[];
onFolderSelect?: (folderId: string) => void;
- onPreview?: (doc: any) => void;
viewMode?: string;
scrollRef?: React.RefObject;
}
@@ -13,7 +13,6 @@ interface UseDocumentsNavigationProps {
export const useDocumentsNavigation = ({
entries,
onFolderSelect,
- onPreview,
viewMode,
scrollRef,
}: UseDocumentsNavigationProps) => {
@@ -25,6 +24,8 @@ export const useDocumentsNavigation = ({
applySelection,
} = useWorkspaceSelectionContext();
+ const { openDocument } = useDocumentOpen();
+
const navigableRows = useMemo(
() => entries.map((entry) => ({ key: entry.key, type: entry.type, id: entry.id })),
[entries],
@@ -109,7 +110,8 @@ export const useDocumentsNavigation = ({
} else {
const entry = getEntryByKey(activeRow.key);
if (entry && entry.type === 'document') {
- onPreview?.(entry.document);
+ const isPreview = key === ' ' || key === 'Space' || key === 'Spacebar';
+ openDocument(entry.document, isPreview ? 'preview' : 'sidepanel');
}
}
}
@@ -175,7 +177,7 @@ export const useDocumentsNavigation = ({
navigableRows,
onFolderSelect,
selectedEntries,
- onPreview,
+ openDocument,
handleEntrySelection,
setFocusedEntryKey,
viewMode,
diff --git a/frontend/src/documents/hooks/useDocumentsPanelProps.ts b/frontend/src/documents/hooks/useDocumentsPanelProps.ts
index 4bee8c6..a05ee60 100644
--- a/frontend/src/documents/hooks/useDocumentsPanelProps.ts
+++ b/frontend/src/documents/hooks/useDocumentsPanelProps.ts
@@ -52,7 +52,6 @@ export interface UseDocumentsPanelPropsArgs {
clearDocumentSelection?: () => void;
handleDeleteSelection?: () => void;
handleEntryPointerCore?: (...args: unknown[]) => void;
- onDocumentActivate?: (docId: Identifier | null, metadata?: unknown) => void;
tags?: unknown[];
correspondents?: unknown[];
documentLookup?: unknown;
@@ -105,7 +104,6 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
handleDocumentsViewModeChange,
handleDeleteSelection,
handleEntryPointerCore,
- onDocumentActivate,
tags,
correspondents,
documentLookup,
@@ -159,7 +157,6 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
onViewModeChange: handleDocumentsViewModeChange,
onDeleteSelection: handleDeleteSelection,
onEntryPointer: handleEntryPointerCore,
- onDocumentActivate,
tags,
correspondents,
documentLookup,
@@ -206,7 +203,6 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
handleFolderDragEnd,
handleFolderDragStart,
handleFolderRename,
- onDocumentActivate,
moveDocumentsToFolder,
refreshCurrentFolder,
searchLoading,
diff --git a/frontend/src/documents/panel/DocumentsPanel.tsx b/frontend/src/documents/panel/DocumentsPanel.tsx
index d6325dd..2fd501a 100644
--- a/frontend/src/documents/panel/DocumentsPanel.tsx
+++ b/frontend/src/documents/panel/DocumentsPanel.tsx
@@ -15,7 +15,6 @@ import type {
} from '../../types/documents';
import DesktopWorkspace from '../../desktop/DesktopWorkspace';
import { isTagTransferEvent } from '../tagTransfer';
-import { usePreviewContext } from '../../preview/PreviewContext';
import { isPointerModifierEvent, isPrimaryPointerEvent } from '../useEntryPointer';
import {
WorkspaceSelectionProvider,
@@ -65,7 +64,6 @@ export interface DocumentsViewProps {
onFolderDragStart?: (event: DragEvent, folderId: Identifier | 'root') => void;
onFolderDragEnd?: (event: DragEvent) => void;
onFolderRename?: (folderId: Identifier | 'root', nextName: string) => Promise | boolean;
- onDocumentActivate?: (doc: Document, event?: unknown) => void;
onDocumentDragStart?: (event: DragEvent, document: Document) => void;
onDocumentDragEnd?: (event: DragEvent) => void;
onDocumentTagDragStart?: (event: DragEvent, docId: Identifier, tagId: Identifier) => void;
@@ -106,7 +104,6 @@ const DocumentsPanelInner: React.FC = ({
onDocumentDragEnd,
onDocumentRename,
onEntryPointer = null,
- onDocumentActivate = null,
tagLookupById,
activeCorrespondentIds = [],
ensureAssetUrl = null,
@@ -317,8 +314,6 @@ const DocumentsPanelInner: React.FC = ({
const isGridView = viewMode === 'grid';
const isDeskView = viewMode === 'desk';
- const { openPreview } = usePreviewContext();
-
const isTagDragEvent = useCallback((event) => isTagTransferEvent(event), []);
const draggingTagRef = useRef<{ docId: Identifier; tagId: Identifier } | null>(null);
@@ -365,26 +360,6 @@ const DocumentsPanelInner: React.FC = ({
[isTagDragEvent],
);
- const handleDocumentActivate = useCallback(
- (doc, event?: React.MouseEvent | KeyboardEvent | null) => {
- if (!doc) {
- return;
- }
-
- // Handle Preview (Alt+Click or Middle Click)
- if (event && (event.altKey || ((event as React.MouseEvent).button === 1))) {
- openPreview(doc);
- return;
- }
-
- // Handle Activation (Double Click, Enter, or explicit call)
- if (onDocumentActivate) {
- onDocumentActivate(doc);
- }
- },
- [onDocumentActivate, openPreview],
- );
-
const handleFolderClick = useCallback(
(folder, event) => {
if (!folder) {
@@ -445,7 +420,6 @@ const DocumentsPanelInner: React.FC = ({
onFolderDragStart,
onFolderDragEnd,
onFolderRename,
- onDocumentActivate: handleDocumentActivate,
onDocumentDragStart: handleDocumentDragStartLocal,
onDocumentDragEnd: handleDocumentDragEndLocal,
onDocumentTagDragStart: handleDocumentTagDragStart,
diff --git a/frontend/src/hooks/documents/useDocumentsWorkspace.ts b/frontend/src/hooks/documents/useDocumentsWorkspace.ts
index 84b12cc..8c1ce1e 100644
--- a/frontend/src/hooks/documents/useDocumentsWorkspace.ts
+++ b/frontend/src/hooks/documents/useDocumentsWorkspace.ts
@@ -1069,7 +1069,6 @@ const useDocumentsWorkspace = ({
detailPanelProps,
detailPanelOpen,
openDetailPanel,
- inspectDocument,
previewActive,
previewWorkspaceDocument,
resolveFolderPath,
@@ -1178,7 +1177,6 @@ const useDocumentsWorkspace = ({
clearDocumentSelection,
handleDeleteSelection,
handleEntryPointerCore,
- onDocumentActivate: inspectDocument,
tags,
correspondents,
documentLookup,