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:
@@ -119,6 +119,7 @@ export function HomePage() {
|
||||
return (
|
||||
<AppLayout>
|
||||
<div>
|
||||
<h1 className="sr-only">{t.homePage.heading}</h1>
|
||||
<div className="mx-auto max-w-6xl px-4 py-8 sm:px-6 lg:px-8">
|
||||
<HomeSearchBar
|
||||
value={search}
|
||||
|
||||
@@ -366,21 +366,27 @@ export function ToolPage() {
|
||||
setMobileSettingsOpen(false);
|
||||
}, [toolId]);
|
||||
|
||||
const toolAccept = registryEntry?.accept ?? tool?.acceptedInputs?.join(",");
|
||||
const toolAccept = registryEntry?.accept ?? (tool?.acceptedInputs?.join(",") || undefined);
|
||||
const acceptsAnyFile = !registryEntry?.accept && tool?.acceptedInputs?.length === 0;
|
||||
const toolAcceptExts = useMemo(
|
||||
() => toolAccept?.split(",").map((e) => e.trim().replace(/^\./, "").toLowerCase()),
|
||||
() =>
|
||||
toolAccept
|
||||
?.split(",")
|
||||
.map((e) => e.trim().replace(/^\./, "").toLowerCase())
|
||||
.filter(Boolean),
|
||||
[toolAccept],
|
||||
);
|
||||
const toolFileFilter = useMemo(() => {
|
||||
if (acceptsAnyFile) return () => true;
|
||||
if (!toolAcceptExts || toolAcceptExts.length === 0) return undefined;
|
||||
return (file: File) => {
|
||||
const ext = file.name.split(".").pop()?.toLowerCase() ?? "";
|
||||
return toolAcceptExts.includes(ext);
|
||||
};
|
||||
}, [toolAcceptExts]);
|
||||
}, [toolAcceptExts, acceptsAnyFile]);
|
||||
const toolAcceptDescription = useMemo(
|
||||
() =>
|
||||
toolAcceptExts
|
||||
toolAcceptExts && toolAcceptExts.length > 0
|
||||
? `${toolAcceptExts.map((e) => e.toUpperCase()).join(", ")} files only`
|
||||
: undefined,
|
||||
[toolAcceptExts],
|
||||
@@ -415,16 +421,18 @@ export function ToolPage() {
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.multiple = true;
|
||||
input.accept =
|
||||
toolAccept ??
|
||||
"image/*,.avif,.heic,.heif,.hif,.jxl,.dng,.cr2,.cr3,.nef,.nrw,.arw,.orf,.rw2,.raf,.pef,.3fr,.iiq,.srw,.x3f,.rwl,.gpr,.fff,.mrw,.mef,.kdc,.dcr,.erf,.ptx,.tga,.psd,.exr,.hdr,.svgz,.jp2,.j2k,.qoi,.eps,.dds,.cur,.apng,.dpx,.cin,.fits,.ppm,.pgm,.pbm,.pfm";
|
||||
if (!acceptsAnyFile) {
|
||||
input.accept =
|
||||
toolAccept ??
|
||||
"image/*,.avif,.heic,.heif,.hif,.jxl,.dng,.cr2,.cr3,.nef,.nrw,.arw,.orf,.rw2,.raf,.pef,.3fr,.iiq,.srw,.x3f,.rwl,.gpr,.fff,.mrw,.mef,.kdc,.dcr,.erf,.ptx,.tga,.psd,.exr,.hdr,.svgz,.jp2,.j2k,.qoi,.eps,.dds,.cur,.apng,.dpx,.cin,.fits,.ppm,.pgm,.pbm,.pfm";
|
||||
}
|
||||
input.onchange = (e) => {
|
||||
const selected = Array.from((e.target as HTMLInputElement).files || []);
|
||||
const newFiles = toolFileFilter ? selected.filter(toolFileFilter) : selected;
|
||||
if (newFiles.length > 0) addFiles(newFiles);
|
||||
};
|
||||
input.click();
|
||||
}, [addFiles, toolAccept, toolFileFilter]);
|
||||
}, [addFiles, toolAccept, toolFileFilter, acceptsAnyFile]);
|
||||
|
||||
// Page-level drag handlers (active when a file is already loaded)
|
||||
const handleDragEnter = useCallback((e: React.DragEvent) => {
|
||||
|
||||
Reference in New Issue
Block a user