This commit is contained in:
2025-10-29 18:03:21 +01:00
parent ca30b20873
commit b6bcb72165
6 changed files with 639 additions and 364 deletions
+82 -86
View File
@@ -34,6 +34,84 @@ const DEBUG_DRAG = false;
const DEBUG_FOCUS = true;
const DEBUG_DROP = true;
const signedDistance = (ax, ay, bx, by, px, py) => (bx - ax) * (py - ay) - (by - ay) * (px - ax);
const clipPolygonWithEdge = (subject, edgeStart, edgeEnd) => {
if (!subject.length) {
return [];
}
const result = [];
let prev = subject[subject.length - 1];
let prevInside = signedDistance(edgeStart.x, edgeStart.y, edgeEnd.x, edgeEnd.y, prev.x, prev.y) >= 0;
subject.forEach((curr) => {
const currInside = signedDistance(edgeStart.x, edgeStart.y, edgeEnd.x, edgeEnd.y, curr.x, curr.y) >= 0;
if (currInside !== prevInside) {
const dx = curr.x - prev.x;
const dy = curr.y - prev.y;
const denom = (edgeEnd.x - edgeStart.x) * dy - (edgeEnd.y - edgeStart.y) * dx;
if (Math.abs(denom) > 1e-9) {
const t = ((edgeStart.x - prev.x) * dy - (edgeStart.y - prev.y) * dx) / denom;
result.push({
x: edgeStart.x + t * (edgeEnd.x - edgeStart.x),
y: edgeStart.y + t * (edgeEnd.y - edgeStart.y),
});
}
}
if (currInside) {
result.push(curr);
}
prev = curr;
prevInside = currInside;
});
return result;
};
const clipPolygon = (subject, clipShape) => {
if (!subject.length) {
return [];
}
let output = subject;
let prev = clipShape[clipShape.length - 1];
for (let index = 0; index < clipShape.length; index += 1) {
const curr = clipShape[index];
output = clipPolygonWithEdge(output, prev, curr);
if (!output.length) {
return [];
}
prev = curr;
}
return output;
};
const isPointInsideConvex = (point, polygon) => {
if (!polygon.length) {
return false;
}
let prev = polygon[polygon.length - 1];
for (let index = 0; index < polygon.length; index += 1) {
const curr = polygon[index];
if (signedDistance(prev.x, prev.y, curr.x, curr.y, point.x, point.y) < -1e-6) {
return false;
}
prev = curr;
}
return true;
};
const polygonCentroid = (polygon) => {
let x = 0;
let y = 0;
polygon.forEach((point) => {
x += point.x;
y += point.y;
});
const count = polygon.length || 1;
return {
x: x / count,
y: y / count,
};
};
const readTransferData = (dataTransfer, mimeTypes) => {
if (!dataTransfer) {
return null;
@@ -866,84 +944,6 @@ const DesktopWorkspace = ({
},
[resolvePreviewDimensions],
);
const signedDistance = (ax, ay, bx, by, px, py) => (bx - ax) * (py - ay) - (by - ay) * (px - ax);
const clipPolygonWithEdge = (subject, edgeStart, edgeEnd) => {
if (!subject.length) {
return [];
}
const result = [];
let prev = subject[subject.length - 1];
let prevInside = signedDistance(edgeStart.x, edgeStart.y, edgeEnd.x, edgeEnd.y, prev.x, prev.y) >= 0;
subject.forEach((curr) => {
const currInside = signedDistance(edgeStart.x, edgeStart.y, edgeEnd.x, edgeEnd.y, curr.x, curr.y) >= 0;
if (currInside !== prevInside) {
const dx = curr.x - prev.x;
const dy = curr.y - prev.y;
const denom = (edgeEnd.x - edgeStart.x) * dy - (edgeEnd.y - edgeStart.y) * dx;
if (Math.abs(denom) > 1e-9) {
const t = ((edgeStart.x - prev.x) * dy - (edgeStart.y - prev.y) * dx) / denom;
result.push({
x: edgeStart.x + t * (edgeEnd.x - edgeStart.x),
y: edgeStart.y + t * (edgeEnd.y - edgeStart.y),
});
}
}
if (currInside) {
result.push(curr);
}
prev = curr;
prevInside = currInside;
});
return result;
};
const clipPolygon = (subject, clipShape) => {
if (!subject.length) {
return [];
}
let output = subject;
let prev = clipShape[clipShape.length - 1];
for (let index = 0; index < clipShape.length; index += 1) {
const curr = clipShape[index];
output = clipPolygonWithEdge(output, prev, curr);
if (!output.length) {
return [];
}
prev = curr;
}
return output;
};
const isPointInsideConvex = (point, polygon) => {
if (!polygon.length) {
return false;
}
let prev = polygon[polygon.length - 1];
for (let index = 0; index < polygon.length; index += 1) {
const curr = polygon[index];
if (signedDistance(prev.x, prev.y, curr.x, curr.y, point.x, point.y) < -1e-6) {
return false;
}
prev = curr;
}
return true;
};
const polygonCentroid = (polygon) => {
let x = 0;
let y = 0;
polygon.forEach((point) => {
x += point.x;
y += point.y;
});
const count = polygon.length || 1;
return {
x: x / count,
y: y / count,
};
};
const recalcVisibleDocIds = useCallback(() => {
const layoutMap = layoutRef.current;
const canvasWidth = canvasSize.width || DEFAULT_CANVAS_WIDTH;
@@ -1069,7 +1069,6 @@ const recalcVisibleDocIds = useCallback(() => {
canvasSize.height,
ensureDocumentSize,
documentLookup,
docSizeVersion,
]);
const syncLayoutSnapshot = useCallback(() => {
@@ -1080,7 +1079,8 @@ const syncLayoutSnapshot = useCallback(() => {
const container = containerRef.current;
if (!container) return () => {};
if (process.env.NODE_ENV !== 'production') {
const nodeEnv = typeof globalThis !== 'undefined' ? globalThis.process?.env?.NODE_ENV : undefined;
if (nodeEnv !== 'production') {
console.log('[skeuo] canvas element', container);
}
@@ -1207,7 +1207,7 @@ const syncLayoutSnapshot = useCallback(() => {
useEffect(() => {
recalcVisibleDocIds();
}, [recalcVisibleDocIds, items.length, canvasSize.width, canvasSize.height]);
}, [recalcVisibleDocIds, items.length, canvasSize.width, canvasSize.height, docSizeVersion]);
useEffect(() => {
if (draggingId && !items.some((doc) => doc.id === draggingId)) {
@@ -1849,16 +1849,12 @@ const DesktopWorkspaceView = () => {
closeOverlay,
overlayOriginRect,
overlayOriginTransform,
docSizeVersion,
} = useDesktopContext();
const { handlePointerDown, handlePointerMove, handlePointerUp, handlePointerCancel } =
useDocumentDrag();
const allSizesReady = useMemo(
() => items.every((doc) => ensureDocumentSize(doc)),
[items, ensureDocumentSize, docSizeVersion],
);
const allSizesReady = items.every((doc) => ensureDocumentSize(doc));
return (
<>
+1
View File
@@ -196,6 +196,7 @@ const useDocumentDrag = () => {
recalcVisibleDocIds();
},
[
bringToFront,
canvasPadding,
canvasSize.height,
canvasSize.width,
+2
View File
@@ -828,11 +828,13 @@ const DetailPanel = ({
warmNavigator(stackPreviewNavigator);
}, [
ensureAssetUrl,
singlePreviewNavigator,
singlePreviewNavigator.documentId,
singlePreviewNavigator.asset,
singlePreviewNavigator.ordinal,
singlePreviewNavigator.canGoPrev,
singlePreviewNavigator.canGoNext,
stackPreviewNavigator,
stackPreviewNavigator.documentId,
stackPreviewNavigator.asset,
stackPreviewNavigator.ordinal,
+1 -1
View File
@@ -172,7 +172,7 @@ const DocumentThumbnailImage = ({
} else {
delete node.dataset.thumbnailAspect;
}
}, [aspectRatio]);
}, [aspectRatio, visibilityRef]);
return (
<div className="document-thumbnail-wrapper" ref={visibilityRef}>
+550 -274
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import React from 'react';
import { formatFileSize } from '../utils/format';
import { DownloadIcon, TextScanIcon, AnalyzeIcon, CloseIcon } from '../ui/icons';
import { openOcrTextInNewTab } from '../utils/ocr';
@@ -24,7 +24,7 @@ const PreviewWorkspace = ({
const tags = Array.isArray(document.tags) ? document.tags : [];
const correspondents = Array.isArray(document.correspondents) ? document.correspondents : [];
const metadataSummary = useMemo(() => {
const metadataSummary = (() => {
const rows = [];
if (mime) rows.push(['Type', mime]);
if (sizeLabel) rows.push(['Size', sizeLabel]);
@@ -45,7 +45,7 @@ const PreviewWorkspace = ({
]);
}
return rows;
}, [mime, sizeLabel, issuedAt, createdAt, updatedAt, folderName, tags, correspondents]);
})();
return (
<section className="preview-workspace">