import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore, } from 'react'; import { resolveDocumentAssetUrl } from '../asset_manager'; import { formatTransform } from './math'; import useDocumentDrag from './useDocumentDrag'; import PreviewZoomOverlay from '../detail/PreviewZoomOverlay'; import { WorkspaceEngine, DESK_CANVAS_PADDING, DESK_DEFAULT_CANVAS_HEIGHT, DESK_DEFAULT_CANVAS_WIDTH, clampCardDimensions, computeFallbackCardSize, useWorkspaceSnapshot, } from './workspaceEngine'; import useDeskPointer from './pointer/useDeskPointer'; import useDeskTagInteractions from './tags/useDeskTagInteractions'; import DesktopDocumentCard from './DesktopDocumentCard'; import usePreviewMetadata from './hooks/usePreviewMetadata'; import '../styles/workspace/workspace-layout.css'; import '../styles/workspace/workspace-items.css'; import '../styles/workspace/workspace-cards.css'; type Identifier = string | number; type TagLike = { id?: Identifier | null; label?: string; color?: string | null } | null | undefined; export interface DeskDocument { id?: Identifier | null; title?: string; tags?: TagLike[] | null; [key: string]: unknown; } interface NavigatorSnapshot { url: string | null; alt?: string | null; canGoPrev?: boolean; canGoNext?: boolean; goPrev?: () => void; goNext?: () => void; ordinal?: number | null; width?: number | null; height?: number | null; } interface OverlayOriginTransform { rotation: number; scaleX: number; scaleY: number; baseWidth: number; baseHeight: number; } interface OverlayDisplay extends NavigatorSnapshot { url: string; } interface DocumentSizeInfo { width: number; height: number; source?: 'snapshot' | 'metadata' | 'fallback'; } interface PreviewMetadataEntry { docId: string; width: number; height: number; } interface DragTransformOverride { centerX?: number; centerY?: number; rotation?: number; scale?: number; } interface DragSettings { canvasPadding: number; defaultCanvasWidth: number; defaultCanvasHeight: number; debugDrag?: boolean; } interface LayoutEntry { centerX: number; centerY: number; rotation: number; z: number; width?: number; height?: number; } type WorkspaceSnapshotState = { layout: Map; canvasSize: { width: number; height: number }; visibleDocIds: Set; draggingId: string | null; tagDropTargetId: string | null; pendingTagDocId: string | null; pendingRemovalTag: unknown; initialLoadDone: boolean; }; interface DesktopWorkspaceProps { documents?: DeskDocument[]; searchResults?: DeskDocument[] | null; onDocumentOpen?: (docId: Identifier | null, options?: Record) => void; onInspectDocument?: (...args: unknown[]) => void; onEntryPointer?: (...args: unknown[]) => void; onDocumentStackSelect?: (docIds: Identifier[]) => void; onPromoteSelection?: (...args: unknown[]) => void; onAssignTagToDocument?: (...args: unknown[]) => void; onRemoveTagFromDocument?: (...args: unknown[]) => void; ensureAssetUrl?: (...args: unknown[]) => Promise | unknown; getDocumentAsset?: (...args: unknown[]) => unknown; activeTagIds?: Array; selectedDocumentIds?: Identifier[]; onClearSelection?: () => void; detailPanelOpen?: boolean; onCloseDetailPanel?: () => void; tenantId?: Identifier | null; viewId?: string | null; } interface DesktopWorkspaceViewProps { engine: WorkspaceEngine; items: DeskDocument[]; containerRef: React.RefObject; handleCanvasDragOver: (event: React.DragEvent) => void; handleCanvasDragLeave: (event: React.DragEvent) => void; handleCanvasDrop: (event: React.DragEvent) => void; ensureDocumentSize: (doc: DeskDocument | null | undefined) => DocumentSizeInfo | null; layoutSnapshot: Map; layoutRef: React.MutableRefObject>; dragTransformsRef: React.MutableRefObject>; itemRefs: React.MutableRefObject>; visibleDocIds: Set; draggingId: string | null; tagDropTargetId: string | null; pendingTagDocId: string | null; pendingRemovalTag: unknown; onDocumentOpen?: DesktopWorkspaceProps['onDocumentOpen']; ensureAssetUrl?: DesktopWorkspaceProps['ensureAssetUrl']; getDocumentAsset?: DesktopWorkspaceProps['getDocumentAsset']; handleNavigatorSnapshot: (docId: Identifier | null, snapshot: NavigatorSnapshot | null) => void; activeTagSet: Set; handleTagDragEnterDoc: (...args: unknown[]) => void; handleTagDragOverDoc: (...args: unknown[]) => void; handleTagDragLeaveDoc: (...args: unknown[]) => void; handleTagDropOnDoc: (...args: unknown[]) => void; handleDocTagPointerDown: (...args: unknown[]) => void; handleDocTagDragStart: (...args: unknown[]) => void; handleDocTagDrag: (...args: unknown[]) => void; handleDocTagDragEnd: (...args: unknown[]) => void; overlayDisplay: OverlayDisplay | null; closeOverlay: () => void; overlayOriginRect: DOMRect | null; overlayOriginTransform: OverlayOriginTransform | null; onEntryPointer?: DesktopWorkspaceProps['onEntryPointer']; onDocumentStackSelect?: DesktopWorkspaceProps['onDocumentStackSelect']; onPromoteSelection?: DesktopWorkspaceProps['onPromoteSelection']; selectedDocumentIds: Identifier[]; onClearSelection?: DesktopWorkspaceProps['onClearSelection']; detailPanelOpen: boolean; onCloseDetailPanel?: DesktopWorkspaceProps['onCloseDetailPanel']; documentLookup: Map; resolveBaseMetrics: (doc: DeskDocument | null | undefined, cardWidth: number, cardHeight: number) => { baseWidth: number; baseHeight: number; baseScale: number; }; bringToFront: (docId: Identifier | null | undefined) => void; setDraggingId: (value: string | null) => void; canvasSize: { width: number; height: number }; openOverlayForDoc: (docId: Identifier | null | undefined, originInfo?: OverlayOriginTransform | null) => void; recalcVisibleDocIds: () => void; dragSettings: DragSettings; onInspectDocument?: DesktopWorkspaceProps['onInspectDocument']; markLayoutDirty: () => void; } const DEBUG_DRAG = false; const DEBUG_FOCUS = false; const DesktopWorkspace: React.FC = ({ documents = [], searchResults = null, onDocumentOpen, onInspectDocument = null, onEntryPointer = null, onDocumentStackSelect = null, onPromoteSelection = null, onAssignTagToDocument = null, onRemoveTagFromDocument = null, ensureAssetUrl = null, getDocumentAsset = () => null, activeTagIds = [], selectedDocumentIds = [], onClearSelection = null, detailPanelOpen = false, onCloseDetailPanel = null, tenantId = null, viewId = 'default', }) => { const items = useMemo( () => (searchResults ? searchResults : documents), [documents, searchResults], ); const allowLayoutPersistence = Boolean(tenantId && viewId && viewId.startsWith('folder:')); const containerRef = useRef(null); const itemRefs = useRef>(new Map()); const dragTransformsRef = useRef>(new Map()); const [overlayDocId, setOverlayDocId] = useState(null); const [overlayOriginRect, setOverlayOriginRect] = useState(null); const [overlayOriginTransform, setOverlayOriginTransform] = useState( null, ); const [previewSnapshots, setPreviewSnapshots] = useState>( () => new Map(), ); const [docSizeVersion, setDocSizeVersion] = useState(0); const docSizeMapRef = useRef>(new Map()); const ensureDocumentSize = useCallback((doc: DeskDocument | null | undefined): DocumentSizeInfo | null => { if (!doc?.id) { return null; } return docSizeMapRef.current.get(String(doc.id)) || null; }, []); const previewMetadata = usePreviewMetadata(items, getDocumentAsset, ensureAssetUrl); const documentLookup = useMemo>(() => { const map = new Map(); items.forEach((doc) => { const key = doc?.id != null ? String(doc.id) : null; if (key) { map.set(key, doc); } }); return map; }, [items]); const engineRef = useRef(null); if (!engineRef.current) { engineRef.current = new WorkspaceEngine({ allowLayoutPersistence, tenantId, viewId, }); } const engine = engineRef.current as WorkspaceEngine; useEffect(() => { engine.updateConfig({ allowLayoutPersistence, tenantId, viewId }); }, [engine, allowLayoutPersistence, tenantId, viewId]); useEffect(() => { const nextItems = searchResults ? searchResults : documents; engine.setItems(nextItems || []); }, [engine, documents, searchResults]); useEffect(() => { const map = new Map(); items.forEach((doc) => { const key = doc?.id != null ? String(doc.id) : null; if (key) { map.set(key, doc); } }); engine.setDocumentLookup(map); }, [engine, items]); useEffect(() => { engine.setEnsureDocumentSize(ensureDocumentSize); }, [engine, ensureDocumentSize]); const workspaceSnapshot = useWorkspaceSnapshot(engine, useSyncExternalStore) as WorkspaceSnapshotState; const { layout: layoutSnapshot, canvasSize, visibleDocIds, draggingId, tagDropTargetId, pendingTagDocId, pendingRemovalTag, initialLoadDone, } = workspaceSnapshot; useEffect(() => { const shouldWaitForPersisted = allowLayoutPersistence && !initialLoadDone; if (shouldWaitForPersisted) { return; } engine.ensureLayoutForItems(); }, [engine, docSizeVersion, initialLoadDone, items.length, allowLayoutPersistence]); useEffect(() => { engine.setItemRefs(itemRefs); }, [engine, itemRefs]); const layoutRef = useRef>(layoutSnapshot); layoutRef.current = engine.layout as Map; const bringToFront = useCallback((docId: Identifier | null | undefined) => { engine.bringToFront(docId); }, [engine]); const markLayoutDirty = useCallback(() => { engine.markLayoutDirty(); }, [engine]); const recalcVisibleDocIds = useCallback(() => { engine.recalcVisibleDocIds(); }, [engine]); const setDraggingId = useCallback((value: string | number | null) => { engine.setDraggingId(value); }, [engine]); const applySnapshotDimensions = useCallback((docKey: string, snapshot: NavigatorSnapshot | null) => { const width = Number(snapshot?.width); const height = Number(snapshot?.height); if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) { return; } const normalized = clampCardDimensions(width, height); if (!normalized) { return; } const existing = docSizeMapRef.current.get(docKey); if (existing && existing.width === normalized.width && existing.height === normalized.height) { return; } const next = new Map(docSizeMapRef.current); next.set(docKey, { ...normalized, source: 'snapshot' }); docSizeMapRef.current = next; setDocSizeVersion((value) => value + 1); }, []); const handleNavigatorSnapshot = useCallback( (docId: Identifier | null, snapshot: NavigatorSnapshot | null) => { const docKey = docId != null ? String(docId) : null; if (!docKey) { return; } setPreviewSnapshots((previous) => { const prevSnapshot = previous.get(docKey); if (!snapshot) { if (!previous.has(docKey)) { return previous; } const next = new Map(previous); next.delete(docKey); return next; } const next = new Map(previous); const sameSnapshot = prevSnapshot && prevSnapshot.url === snapshot.url && prevSnapshot.alt === snapshot.alt && prevSnapshot.canGoPrev === snapshot.canGoPrev && prevSnapshot.canGoNext === snapshot.canGoNext && prevSnapshot.goPrev === snapshot.goPrev && prevSnapshot.goNext === snapshot.goNext && prevSnapshot.ordinal === snapshot.ordinal && prevSnapshot.width === snapshot.width && prevSnapshot.height === snapshot.height; if (sameSnapshot) { return previous; } next.set(docKey, snapshot); return next; }); if (snapshot) { applySnapshotDimensions(docKey, snapshot); } }, [applySnapshotDimensions], ); const activeTagSet = useMemo>(() => { if (!Array.isArray(activeTagIds) || activeTagIds.length === 0) { return new Set(); } const set = new Set(); activeTagIds.forEach((id) => { if (id != null) { set.add(String(id)); } }); return set; }, [activeTagIds]); useLayoutEffect(() => { const container = containerRef.current; if (!container) { engine.setCanvasSize({ width: 0, height: 0 }); return undefined; } const commitSize = () => { const rect = container.getBoundingClientRect(); const width = Math.floor(rect.width) || 0; const height = Math.floor(rect.height) || 0; engine.setCanvasSize({ width, height }); }; commitSize(); if (typeof window.ResizeObserver === 'undefined') { window.addEventListener('resize', commitSize); return () => { window.removeEventListener('resize', commitSize); }; } const observer = new window.ResizeObserver(commitSize); observer.observe(container); return () => observer.disconnect(); }, [engine]); const resolvePreviewDimensions = useCallback( (doc: DeskDocument | null | undefined): PreviewMetadataEntry | null => { if (!doc?.id) { return null; } return previewMetadata.get(String(doc.id)) || null; }, [previewMetadata], ); useEffect(() => { if (!ensureAssetUrl) { return; } visibleDocIds.forEach((docId) => { const doc = documentLookup.get(docId); if (!doc) { return; } resolveDocumentAssetUrl(doc, 'preview', { ensureAssetUrl, getAsset: getDocumentAsset, }); }); }, [visibleDocIds, ensureAssetUrl, getDocumentAsset, documentLookup]); const requestCanvasFocus = useCallback(() => { const canvas = containerRef.current; if (!canvas?.focus) { return; } const focusTarget = () => { try { canvas.focus({ preventScroll: true }); } catch (error: unknown) { if (DEBUG_FOCUS) { void error; } } }; const raf = window.requestAnimationFrame; if (raf) { raf(() => focusTarget()); return; } setTimeout(() => { focusTarget(); }, 0); }, []); const tagInteractions = useDeskTagInteractions({ engine, onAssignTagToDocument, onRemoveTagFromDocument, requestCanvasFocus, }); const { handleTagDragEnterDoc, handleTagDragOverDoc, handleTagDragLeaveDoc, handleTagDropOnDoc, handleCanvasDragOver, handleCanvasDragLeave, handleCanvasDrop, handleDocTagPointerDown, handleDocTagDragStart, handleDocTagDrag, handleDocTagDragEnd, } = tagInteractions; useEffect(() => { const current = docSizeMapRef.current; const next = new Map(current); const itemKeys = new Set(items.filter((doc) => doc?.id != null).map((doc) => String(doc.id))); let changed = false; items.forEach((doc) => { if (!doc?.id) { return; } const key = String(doc.id); const existing = next.get(key) || null; const meta = previewMetadata.get(key); if (meta) { const normalized = clampCardDimensions(meta.width, meta.height); if (normalized) { if (existing?.source === 'snapshot') { return; } if (!existing || existing.width !== normalized.width || existing.height !== normalized.height || existing.source !== 'metadata') { next.set(key, { ...normalized, source: 'metadata' }); changed = true; } return; } } if (!existing) { const fallback = computeFallbackCardSize(key); if (fallback) { next.set(key, { ...fallback, source: 'fallback' }); changed = true; } } }); current.forEach((_, key) => { if (!itemKeys.has(key)) { next.delete(key); changed = true; } }); if (changed) { docSizeMapRef.current = next; setDocSizeVersion((value) => value + 1); } }, [items, previewMetadata]); const overlayDisplay = useMemo(() => { if (!overlayDocId) { return null; } const snapshot = previewSnapshots.get(overlayDocId); if (!snapshot || !snapshot.url) { return null; } const doc = documentLookup.get(overlayDocId); const alt = snapshot.alt || (doc?.title as string | undefined); return { url: snapshot.url, alt, canGoPrev: snapshot.canGoPrev, canGoNext: snapshot.canGoNext, goPrev: snapshot.goPrev, goNext: snapshot.goNext, }; }, [overlayDocId, previewSnapshots, documentLookup]); const closeOverlay = useCallback(() => { setOverlayDocId(null); setOverlayOriginRect(null); setOverlayOriginTransform(null); }, []); useEffect(() => { if (overlayDocId && !documentLookup.has(overlayDocId)) { setOverlayDocId(null); setOverlayOriginRect(null); setOverlayOriginTransform(null); } }, [overlayDocId, documentLookup]); const resolveBaseMetrics = useCallback( (doc: DeskDocument | null | undefined, cardWidth: number, cardHeight: number) => { const previewDims = doc ? resolvePreviewDimensions(doc) : null; if (previewDims?.width && previewDims?.height) { const baseWidth = Math.max(previewDims.width, cardWidth); const baseHeight = Math.max(previewDims.height, cardHeight); const scaleX = cardWidth / baseWidth; const scaleY = cardHeight / baseHeight; const baseScale = Math.min(scaleX, scaleY, 1); return { baseWidth, baseHeight, baseScale: Number.isFinite(baseScale) && baseScale > 0 ? baseScale : 1, }; } return { baseWidth: cardWidth, baseHeight: cardHeight, baseScale: 1, }; }, [resolvePreviewDimensions], ); useEffect(() => { if (draggingId && !items.some((doc) => String(doc.id) === draggingId)) { setDraggingId(null); } }, [draggingId, items, setDraggingId]); const openOverlayForDoc = useCallback( (docId: Identifier | null | undefined, originInfo: OverlayOriginTransform | null = null) => { if (!docId) { return; } const docKey = String(docId); const snapshot = previewSnapshots.get(docKey); if (!snapshot || !snapshot.url) { return; } const container = itemRefs.current.get(docKey); const imageNode = container ? container.querySelector('.desk-item__card img') : null; if (!container || !imageNode) { return; } const rect = imageNode.getBoundingClientRect(); let originTransform = null; if (originInfo) { const { rotation = 0, scale = 1, width: originWidth, height: originHeight } = originInfo; originTransform = { rotation, scaleX: scale, scaleY: scale, baseWidth: originWidth, baseHeight: originHeight, }; } if (!originTransform) { const entry = engine.getLayout(docKey); const doc = documentLookup.get(docKey) || null; const sizeInfo = ensureDocumentSize(doc); if (!sizeInfo) { return; } const { width: cardWidth, height: cardHeight } = sizeInfo; const { baseWidth, baseHeight, baseScale } = resolveBaseMetrics(doc, cardWidth, cardHeight); const effectiveWidth = baseWidth * baseScale; const effectiveHeight = baseHeight * baseScale; originTransform = { rotation: entry?.rotation ?? 0, scaleX: baseScale, scaleY: baseScale, baseWidth: Number.isFinite(effectiveWidth) && effectiveWidth > 0 ? effectiveWidth : cardWidth, baseHeight: Number.isFinite(effectiveHeight) && effectiveHeight > 0 ? effectiveHeight : cardHeight, }; } bringToFront(docId); setOverlayOriginRect(rect); setOverlayOriginTransform(originTransform); setOverlayDocId(docKey); }, [ bringToFront, previewSnapshots, itemRefs, setOverlayOriginTransform, ensureDocumentSize, resolveBaseMetrics, documentLookup, engine, ], ); const dragSettings = useMemo( () => ({ canvasPadding: DESK_CANVAS_PADDING, defaultCanvasWidth: DESK_DEFAULT_CANVAS_WIDTH, defaultCanvasHeight: DESK_DEFAULT_CANVAS_HEIGHT, debugDrag: DEBUG_DRAG, }), [], ); const viewProps = useMemo( () => ({ engine, items, containerRef, handleCanvasDragOver, handleCanvasDragLeave, handleCanvasDrop, ensureDocumentSize, layoutSnapshot, layoutRef, dragTransformsRef, itemRefs, visibleDocIds, draggingId, tagDropTargetId, pendingTagDocId, pendingRemovalTag, onDocumentOpen, ensureAssetUrl, getDocumentAsset, handleNavigatorSnapshot, activeTagSet, handleTagDragEnterDoc, handleTagDragOverDoc, handleTagDragLeaveDoc, handleTagDropOnDoc, handleDocTagPointerDown, handleDocTagDragStart, handleDocTagDrag, handleDocTagDragEnd, overlayDisplay, closeOverlay, overlayOriginRect, overlayOriginTransform, onEntryPointer, onDocumentStackSelect, onPromoteSelection, selectedDocumentIds, onClearSelection, detailPanelOpen, onCloseDetailPanel, documentLookup, resolveBaseMetrics, bringToFront, setDraggingId, canvasSize, openOverlayForDoc, recalcVisibleDocIds, dragSettings, onInspectDocument, markLayoutDirty, }), [ activeTagSet, bringToFront, canvasSize, closeOverlay, containerRef, draggingId, dragSettings, engine, ensureAssetUrl, ensureDocumentSize, getDocumentAsset, dragTransformsRef, handleCanvasDragLeave, handleCanvasDragOver, handleCanvasDrop, handleDocTagDrag, handleDocTagDragEnd, handleDocTagDragStart, handleDocTagPointerDown, handleNavigatorSnapshot, handleTagDragEnterDoc, handleTagDragLeaveDoc, handleTagDragOverDoc, handleTagDropOnDoc, itemRefs, items, layoutRef, layoutSnapshot, onClearSelection, onCloseDetailPanel, onDocumentOpen, onDocumentStackSelect, onEntryPointer, onPromoteSelection, openOverlayForDoc, overlayDisplay, overlayOriginRect, overlayOriginTransform, documentLookup, pendingRemovalTag, pendingTagDocId, recalcVisibleDocIds, resolveBaseMetrics, setDraggingId, selectedDocumentIds, detailPanelOpen, onInspectDocument, markLayoutDirty, tagDropTargetId, visibleDocIds, ], ); return ; }; function DesktopWorkspaceView({ engine, items, containerRef, handleCanvasDragOver, handleCanvasDragLeave, handleCanvasDrop, ensureDocumentSize, layoutSnapshot, layoutRef, itemRefs, visibleDocIds, draggingId, tagDropTargetId, pendingTagDocId, pendingRemovalTag, onDocumentOpen, ensureAssetUrl, getDocumentAsset, handleNavigatorSnapshot, activeTagSet, handleTagDragEnterDoc, handleTagDragOverDoc, handleTagDragLeaveDoc, handleTagDropOnDoc, handleDocTagPointerDown, handleDocTagDragStart, handleDocTagDrag, handleDocTagDragEnd, overlayDisplay, closeOverlay, overlayOriginRect, overlayOriginTransform, onEntryPointer, onDocumentStackSelect, onPromoteSelection, selectedDocumentIds, onClearSelection, detailPanelOpen, onCloseDetailPanel, documentLookup, resolveBaseMetrics, bringToFront, setDraggingId, canvasSize, openOverlayForDoc, recalcVisibleDocIds, dragSettings, onInspectDocument, markLayoutDirty, dragTransformsRef, }: DesktopWorkspaceViewProps) { const { handlePointerDown, handlePointerMove, handlePointerUp, handlePointerCancel } = useDocumentDrag({ engine, layoutRef, dragTransformsRef, itemRefs, documentLookup, ensureDocumentSize, resolveBaseMetrics, bringToFront, setDraggingId, canvasSize, openOverlayForDoc, recalcVisibleDocIds, settings: dragSettings, containerRef, onInspectDocument, onDocumentStackSelect, selectedDocumentIds, markLayoutDirty, }) as { handlePointerDown: React.PointerEventHandler; handlePointerMove: React.PointerEventHandler; handlePointerUp: React.PointerEventHandler; handlePointerCancel: React.PointerEventHandler; }; const { getCardPointerHandlers, handleShellKeyDown, focusShell } = useDeskPointer({ containerRef, items, layoutRef, ensureDocumentSize, activeTagSet, handlePointerDown, handlePointerMove, handlePointerUp, handlePointerCancel, onEntryPointer, onDocumentStackSelect, onPromoteSelection, onDocumentOpen, selectedDocumentIds, detailPanelOpen, onCloseDetailPanel, openOverlayForDoc, }) as { getCardPointerHandlers: (doc: DeskDocument) => React.HTMLAttributes; handleShellKeyDown: React.KeyboardEventHandler; focusShell: () => void; }; useEffect(() => { focusShell(); }, [focusShell]); useEffect(() => { if (selectedDocumentIds.length) { focusShell(); } }, [focusShell, selectedDocumentIds.length]); useEffect(() => { if (!detailPanelOpen) { focusShell(); } }, [detailPanelOpen, focusShell]); const allSizesReady = items.every((doc) => Boolean(ensureDocumentSize(doc))); return ( <>
{ if (event.target === event.currentTarget) { onClearSelection?.(); } focusShell(); }} >
{ if (event.target === event.currentTarget) { onClearSelection?.(); } focusShell(); }} > {!allSizesReady ? (

Loading previews…

) : items.length === 0 ? (

No documents to show here yet. Drop files to make this space come alive.

) : ( items.map((doc, index) => { const sizeInfo = ensureDocumentSize(doc); if (!sizeInfo) { return null; } const { width: cardWidth, height: cardHeight } = sizeInfo; const docKey = doc?.id != null ? String(doc.id) : null; const dragOverride = docKey ? dragTransformsRef.current.get(docKey) : null; const layout = docKey ? layoutRef.current.get(docKey) : null; if (!dragOverride && (!layout && (!docKey || !layoutSnapshot.has(docKey)))) { return null; } const centerX = dragOverride?.centerX ?? layout?.centerX; const centerY = dragOverride?.centerY ?? layout?.centerY; if (!Number.isFinite(centerX) || !Number.isFinite(centerY)) { return null; } const resolvedCenterX = centerX as number; const resolvedCenterY = centerY as number; const rotation = dragOverride?.rotation ?? layout?.rotation ?? 0; const scale = dragOverride?.scale ?? 1; const originX = resolvedCenterX - cardWidth / 2; const originY = resolvedCenterY - cardHeight / 2; const transform = formatTransform( Math.round(originX), Math.round(originY), rotation, scale, ); const style = { transform, zIndex: layout?.z ?? 1, width: Math.round(cardWidth), height: Math.round(cardHeight), }; const shouldLoad = docKey ? visibleDocIds.has(docKey) : false; const dragging = docKey ? draggingId === docKey : false; const docTagKeys = Array.isArray(doc?.tags) ? doc.tags .map((tag) => (tag?.id != null ? String(tag.id) : null)) .filter((id): id is string => Boolean(id)) : []; const matchesFilter = activeTagSet.size === 0 || docTagKeys.some((key) => activeTagSet.has(key)); const dropActive = docKey ? tagDropTargetId === docKey : false; const dropPending = docKey ? pendingTagDocId === docKey : false; const docId = doc?.id ?? null; const isSelected = docId != null ? selectedDocumentIds.includes(docId) : false; const docTagTokens = docTagKeys.join(' '); const cardPointerHandlers = getCardPointerHandlers(doc) as React.HTMLAttributes; const registerNode = (node: HTMLDivElement | null) => { if (!docKey) { return; } if (node) { itemRefs.current.set(docKey, node); } else { itemRefs.current.delete(docKey); } }; return ( ); }) )}
); } export default DesktopWorkspace;