ocr view
This commit is contained in:
@@ -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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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%;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user