mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: resolve 18 QA-discovered bugs across tools, previews, and the AI pipeline (#242)
Exhaustive QA sweep of all 157 tools. Fixes: CSP blob media, csv-excel ExcelJS interop, ocr-pdf segfault, chart-maker upload, non-PDF doc preview, RAW decode, merge-tool multi-file path, html-to-image chromium, ogv/wma/amr/ac3 preview fallbacks, meme/gif/stabilize codecs, nav+home a11y. Plus orphan-format and test-debt cleanup, the AI bundle build script, and a reusable Playwright QA harness under tests/qa/.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { FileText } from "lucide-react";
|
||||
import * as pdfjs from "pdfjs-dist";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "@/contexts/i18n-context";
|
||||
@@ -16,8 +17,14 @@ export function DocumentView() {
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageCount, setPageCount] = useState(0);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const hasProcessedUrl = !!entry?.processedUrl;
|
||||
const src = entry?.processedUrl ?? entry?.blobUrl;
|
||||
|
||||
// F6: detect non-PDF input files (word, excel, html, markdown, etc.)
|
||||
const isPdfInput = entry?.file?.name?.toLowerCase().endsWith(".pdf") ?? false;
|
||||
const showInputFallback = !isPdfInput && !hasProcessedUrl;
|
||||
|
||||
/* A+B: reset pagination and clear stale errors when the document changes.
|
||||
src is intentionally a trigger-only dep (not read inside the callback). */
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: src is the trigger
|
||||
@@ -29,8 +36,10 @@ export function DocumentView() {
|
||||
|
||||
/* C+D: cancel in-flight renders and destroy the doc proxy on cleanup. */
|
||||
useEffect(() => {
|
||||
if (!canvasRef.current) return;
|
||||
const file = entry?.file;
|
||||
if (!canvasRef.current || showInputFallback) return;
|
||||
// F22: when processedUrl is available, use URL-based loading instead of
|
||||
// entry.file (which is the original non-PDF input and would fail pdf.js)
|
||||
const file = hasProcessedUrl ? undefined : entry?.file;
|
||||
if (!file && !src) return;
|
||||
let cancelled = false;
|
||||
let doc: pdfjs.PDFDocumentProxy | undefined;
|
||||
@@ -63,10 +72,26 @@ export function DocumentView() {
|
||||
renderTask?.cancel();
|
||||
doc?.loadingTask.destroy();
|
||||
};
|
||||
}, [src, page, entry?.file]);
|
||||
}, [src, page, entry?.file, showInputFallback, hasProcessedUrl]);
|
||||
|
||||
if (!entry) return null;
|
||||
|
||||
// F6: graceful fallback for non-PDF input files
|
||||
if (showInputFallback) {
|
||||
const ext = entry.file?.name?.split(".").pop()?.toUpperCase() ?? "";
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col items-center justify-center gap-2 p-4">
|
||||
<div className="mx-auto w-16 h-16 rounded-2xl bg-muted flex items-center justify-center mb-2">
|
||||
<FileText className="h-8 w-8 text-muted-foreground" />
|
||||
</div>
|
||||
{ext && <p className="text-xs text-muted-foreground">{ext}</p>}
|
||||
<p className="text-sm text-muted-foreground text-center">
|
||||
{t.tools.documentView.inputNotPreviewable}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col items-center gap-2 overflow-auto p-4">
|
||||
{error && <p className="p-4 text-sm text-destructive">{t.tools.documentView.loadFailed}</p>}
|
||||
|
||||
Reference in New Issue
Block a user