This commit is contained in:
2025-10-16 18:03:31 +02:00
parent a8a3d95c40
commit c2b0351598
2 changed files with 36 additions and 36 deletions
+27 -36
View File
@@ -296,10 +296,9 @@ const DetailPanel = ({
const [titleSaving, setTitleSaving] = useState(false); const [titleSaving, setTitleSaving] = useState(false);
const [titleError, setTitleError] = useState(null); const [titleError, setTitleError] = useState(null);
const [ocrOpen, setOcrOpen] = useState(false); const [ocrOpen, setOcrOpen] = useState(false);
const [ocrUrl, setOcrUrl] = useState(null);
const [ocrLoading, setOcrLoading] = useState(false); const [ocrLoading, setOcrLoading] = useState(false);
const [ocrError, setOcrError] = useState(null); const [ocrError, setOcrError] = useState(null);
const [ocrText, setOcrText] = useState('');
const [ocrLoadedDocId, setOcrLoadedDocId] = useState(null);
useEffect(() => { useEffect(() => {
if (!singleDoc) { if (!singleDoc) {
@@ -310,8 +309,7 @@ const DetailPanel = ({
setOcrOpen(false); setOcrOpen(false);
setOcrLoading(false); setOcrLoading(false);
setOcrError(null); setOcrError(null);
setOcrText(''); setOcrUrl(null);
setOcrLoadedDocId(null);
return; return;
} }
@@ -327,8 +325,7 @@ const DetailPanel = ({
setOcrOpen(false); setOcrOpen(false);
setOcrLoading(false); setOcrLoading(false);
setOcrError(null); setOcrError(null);
setOcrText(''); setOcrUrl(null);
setOcrLoadedDocId(null);
}, [singleDoc?.id]); }, [singleDoc?.id]);
const startTitleEdit = useCallback(() => { const startTitleEdit = useCallback(() => {
@@ -384,29 +381,25 @@ const DetailPanel = ({
[singleDoc, getDocumentAsset], [singleDoc, getDocumentAsset],
); );
const loadOcrText = useCallback(async () => { const loadOcrUrl = useCallback(async () => {
if (!singleDoc) { if (!singleDoc) {
return; return;
} }
if (ocrLoadedDocId === singleDoc.id && ocrText) {
return;
}
const asset = getDocumentAsset(singleDoc, 'ocr-text'); const asset = getDocumentAsset(singleDoc, 'ocr-text');
if (!asset) { if (!asset) {
setOcrError('No OCR text available for this document.'); setOcrError('No OCR text available for this document.');
setOcrText(''); setOcrUrl(null);
setOcrLoadedDocId(singleDoc.id);
return; return;
} }
setOcrLoading(true); setOcrLoading(true);
setOcrError(null); setOcrError(null);
try { try {
let entry = asset; let url = resolveDocumentAssetUrl(singleDoc, 'ocr-text', {
if (ensureAssetUrl) { ensureAssetUrl,
entry = (await ensureAssetUrl(singleDoc.id, asset, { force: false })) || asset; getAsset: getDocumentAsset,
} });
let url = entry?.url || null; if (!url && typeof ensureAssetUrl === 'function') {
if (!url) { await ensureAssetUrl(singleDoc.id, asset, { force: false });
url = resolveDocumentAssetUrl(singleDoc, 'ocr-text', { url = resolveDocumentAssetUrl(singleDoc, 'ocr-text', {
ensureAssetUrl, ensureAssetUrl,
getAsset: getDocumentAsset, getAsset: getDocumentAsset,
@@ -415,30 +408,24 @@ const DetailPanel = ({
if (!url) { if (!url) {
throw new Error('OCR text URL is unavailable.'); throw new Error('OCR text URL is unavailable.');
} }
const response = await fetch(url); setOcrUrl(url);
if (!response.ok) {
throw new Error(`Failed to fetch OCR text (status ${response.status}).`);
}
const text = await response.text();
setOcrText(text);
setOcrLoadedDocId(singleDoc.id);
} catch (error) { } catch (error) {
setOcrError(error.message || 'Failed to load OCR text.'); setOcrError(error.message || 'Failed to load OCR text.');
setOcrText(''); setOcrUrl(null);
} finally { } finally {
setOcrLoading(false); setOcrLoading(false);
} }
}, [singleDoc, ensureAssetUrl, getDocumentAsset, ocrLoadedDocId, ocrText]); }, [singleDoc, ensureAssetUrl, getDocumentAsset]);
const openOcr = useCallback(() => { const openOcrModal = useCallback(() => {
if (!singleDoc) { if (!singleDoc) {
return; return;
} }
setOcrOpen(true); setOcrOpen(true);
loadOcrText(); loadOcrUrl();
}, [singleDoc, loadOcrText]); }, [singleDoc, loadOcrUrl]);
const closeOcr = useCallback(() => { const closeOcrModal = useCallback(() => {
setOcrOpen(false); setOcrOpen(false);
}, []); }, []);
@@ -773,7 +760,7 @@ const DetailPanel = ({
</div> </div>
{hasOcrAsset ? ( {hasOcrAsset ? (
<div className="detail-ocr-trigger"> <div className="detail-ocr-trigger">
<button type="button" className="secondary" onClick={openOcr}> <button type="button" className="secondary" onClick={openOcrModal}>
View OCR text View OCR text
</button> </button>
</div> </div>
@@ -819,7 +806,7 @@ const DetailPanel = ({
)} )}
{hasOcrAsset && ocrOpen {hasOcrAsset && ocrOpen
? createPortal( ? createPortal(
<div className="modal-backdrop" role="presentation" onClick={closeOcr}> <div className="modal-backdrop" role="presentation" onClick={closeOcrModal}>
<div <div
className="modal modal--ocr" className="modal modal--ocr"
role="dialog" role="dialog"
@@ -832,7 +819,7 @@ const DetailPanel = ({
<button <button
type="button" type="button"
className="icon-button ghost" className="icon-button ghost"
onClick={closeOcr} onClick={closeOcrModal}
aria-label="Close OCR text" aria-label="Close OCR text"
> >
× ×
@@ -843,8 +830,12 @@ const DetailPanel = ({
<div className="meta">Loading OCR text</div> <div className="meta">Loading OCR text</div>
) : ocrError ? ( ) : ocrError ? (
<div className="status-inline error">{ocrError}</div> <div className="status-inline error">{ocrError}</div>
) : ocrText ? ( ) : ocrUrl ? (
<pre>{ocrText}</pre> <iframe
title="OCR text"
src={ocrUrl}
className="detail-ocr__frame"
/>
) : ( ) : (
<div className="meta">OCR text empty.</div> <div className="meta">OCR text empty.</div>
)} )}
+9
View File
@@ -1539,6 +1539,15 @@ button.icon-button.ghost:hover:not([disabled]) {
line-height: 1.4; line-height: 1.4;
} }
.detail-ocr__frame {
width: 100%;
height: 60vh;
border: none;
background: var(--surface-subtle);
border-radius: 4px;
box-shadow: inset 0 1px 2px rgba(15, 23, 42, 0.05);
}
.modal__body { .modal__body {
width: 100%; width: 100%;
} }