7
ci / docker (backend, backend/Dockerfile, backend) (push) Successful in 12m50s
ci / docker (frontend, frontend/Dockerfile, frontend) (push) Successful in 12m49s

This commit is contained in:
2025-10-14 02:43:38 +02:00
parent c17a9484ca
commit 9aa43d2753
5 changed files with 183 additions and 298 deletions
+47 -98
View File
@@ -6,6 +6,7 @@ import React, {
useRef,
useState,
} from 'react';
import { resolveDocumentAssetUrl } from './asset_manager';
import './skeuomorphic_ws.css';
const ITEM_WIDTH = 220;
@@ -258,31 +259,6 @@ const useDocumentDrag = ({
};
};
const resolvePreviewDimensions = (doc) => {
if (!doc) return null;
const group = doc?.current_version?.assets || doc?.assets;
let asset = null;
if (group && !Array.isArray(group) && group?.preview) {
asset = group.preview;
} else if (Array.isArray(group)) {
asset = group.find((entry) => entry?.asset_type === 'preview') || null;
}
if (!asset) {
return null;
}
const width = asset?.metadata?.width;
const height = asset?.metadata?.height;
if (Number.isFinite(width) && Number.isFinite(height)) {
return { width, height };
}
return null;
};
const seededRandom = (input) => {
const text = String(input);
let hash = 2166136261;
@@ -336,37 +312,6 @@ const getContrastingTextColor = (hex) => {
return luminance > 0.6 ? '#1b1f24' : '#ffffff';
};
const absolutizeAssetUrl = (input, resolver) => {
if (!input) return null;
if (/^(?:https?:|data:|blob:)/i.test(input)) {
return input;
}
if (typeof resolver === 'function') {
return resolver(input);
}
return input;
};
const resolvePreviewUrl = (doc, resolver) => {
if (!doc) return null;
const group = doc?.current_version?.assets || doc?.assets;
let asset = null;
if (group && !Array.isArray(group) && group?.preview) {
asset = group.preview;
} else if (Array.isArray(group)) {
asset = group.find((entry) => entry?.asset_type === 'preview') || null;
}
if (!asset?.url) {
return null;
}
const now = Date.now();
const expiresAt = typeof asset.expiresAt === 'number' ? asset.expiresAt : null;
if (expiresAt && expiresAt <= now) {
return null;
}
return absolutizeAssetUrl(asset.url, resolver);
};
const SkeuomorphicWorkspace = ({
documents = [],
searchResults = null,
@@ -380,13 +325,13 @@ const SkeuomorphicWorkspace = ({
onCreateTag = null,
onAssignTagToDocument = null,
onRemoveTagFromDocument = null,
resolveAssetPath = null,
ensureAssetUrl = null,
getDocumentAsset = () => null,
}) => {
const items = useMemo(() => (searchResults ? searchResults : documents), [documents, searchResults]);
const showingSearchResults = searchResults !== null;
const containerRef = useRef(null);
const layoutRef = useRef(new Map());
const itemRefs = useRef(new Map());
@@ -406,47 +351,51 @@ const SkeuomorphicWorkspace = ({
const docSizeMapRef = useRef(new Map());
const removalCursorActiveRef = useRef(false);
const resolvePreviewAsset = useCallback(
(doc) => {
if (!doc) return null;
return getDocumentAsset(doc, 'preview');
},
[getDocumentAsset],
);
const resolvePreviewDimensions = useCallback(
(doc) => {
if (!doc) return null;
const asset = resolvePreviewAsset(doc);
const width = asset?.metadata?.width;
const height = asset?.metadata?.height;
if (typeof width === 'number' && typeof height === 'number') {
return { width, height };
}
return null;
},
[resolvePreviewAsset],
);
const resolvePreviewUrl = useCallback(
(doc) => {
if (!doc) return null;
return resolveDocumentAssetUrl(doc, 'preview', {
ensureAssetUrl,
getAsset: getDocumentAsset,
});
},
[ensureAssetUrl, getDocumentAsset],
);
useEffect(() => {
if (!ensureAssetUrl) {
return;
}
const now = Date.now();
const seen = new Set();
const queueAsset = (docId, asset) => {
if (!docId || !asset?.id || seen.has(asset.id)) {
return;
}
const type = asset.asset_type;
if (type && type !== 'thumbnail' && type !== 'preview') {
return;
}
const expiresAt = typeof asset.expiresAt === 'number' ? asset.expiresAt : null;
const hasFreshUrl = asset.url && (!expiresAt || expiresAt > now);
if (hasFreshUrl) {
return;
}
const force = Boolean(asset.url && expiresAt && expiresAt <= now);
seen.add(asset.id);
ensureAssetUrl(docId, asset, { force }).catch(() => {});
};
items.forEach((doc) => {
const docId = doc?.id;
if (!docId) {
return;
}
const previewAsset = getDocumentAsset(doc, 'preview');
if (previewAsset) {
queueAsset(docId, previewAsset);
}
const thumbnailAsset = getDocumentAsset(doc, 'thumbnail');
if (thumbnailAsset) {
queueAsset(docId, thumbnailAsset);
}
resolveDocumentAssetUrl(doc, 'preview', {
ensureAssetUrl,
getAsset: getDocumentAsset,
});
});
}, [items, ensureAssetUrl]);
}, [items, ensureAssetUrl, getDocumentAsset]);
const focusCanvas = useCallback(() => {
const canvas = containerRef.current;
@@ -656,7 +605,7 @@ const SkeuomorphicWorkspace = ({
const size = { width, height };
docSizeMapRef.current.set(key, size);
return size;
}, []);
}, [resolvePreviewDimensions]);
const documentLookup = useMemo(() => {
const map = new Map();
@@ -683,8 +632,8 @@ const SkeuomorphicWorkspace = ({
}, [activeShelfTagId, availableTags]);
const resolveZoomMetrics = useCallback(
(doc, cardWidth, cardHeight) => {
const resolveZoomMetrics = useCallback(
(doc, cardWidth, cardHeight) => {
const canvasWidth = canvasSize.width || DEFAULT_CANVAS_WIDTH;
const canvasHeight = canvasSize.height || DEFAULT_CANVAS_HEIGHT;
const safeCardWidth = cardWidth || CARD_WIDTH_MIN;
@@ -745,7 +694,7 @@ const resolveZoomMetrics = useCallback(
zoomCenterY,
};
},
[canvasSize.width, canvasSize.height],
[canvasSize.width, canvasSize.height, resolvePreviewDimensions],
);
const resolveBaseMetrics = useCallback(
@@ -769,7 +718,7 @@ const resolveZoomMetrics = useCallback(
baseScale: 1,
};
},
[],
[resolvePreviewDimensions],
);
const syncLayoutSnapshot = useCallback(() => {
@@ -1396,7 +1345,7 @@ const resolveZoomMetrics = useCallback(
<div
className="skeuo-canvas"
ref={containerRef}
tabIndex={0}
tabIndex={1}
data-active-tag={activeShelfTagId || undefined}
onDragOver={handleCanvasDragOver}
onDragLeave={handleCanvasDragLeave}
@@ -1455,7 +1404,7 @@ const resolveZoomMetrics = useCallback(
const totalScale = zoomScale > 0 ? zoomScale : 1;
const inverseTagScale = totalScale > 0 ? 1 / totalScale : 1;
const tagsStyle = { '--tag-scale': inverseTagScale };
const previewUrl = resolvePreviewUrl(doc, resolveAssetPath);
const previewUrl = resolvePreviewUrl(doc);
const imageUrl = previewUrl;
const title = doc.title || doc.original_name || 'Document';
const dragging = draggingId === doc.id;