fix: handle non-image modalities across uploads, previews, and filenames (#255)

SnapOtter spans five modalities now, but several code paths still assumed image input.

- dropzone: default to accept-all when no fileFilter is given (image tools still pass one); neutral "supported file types" error text instead of "image files"
- automate (pipelines): accept any modality in the file pickers and dropzones; render modality-aware previews (video player, audio waveform, document/data card) instead of always using ImageViewer/BeforeAfterSlider
- filename sanitizer: extend the double-extension allowlist beyond image extensions to video/audio/document/data so e.g. "report.csv.php" becomes "report.csv"; add tests
- thumbnail route: return 422 for non-rasterisable files (audio, data, non-PDF docs) instead of attempting a doomed Sharp decode
- pool: unknown tools fall back to the "system" pool, not the image pool
- a11y labels: "Previous/Next image", "Image viewer/area/controls/drop zone" are now modality-neutral, across all 21 locales
- copy: bulk-rename default, find-duplicates ZIP name, SSRF user-agent, fetch-urls fallback name, file-details MIME label, URL-import placeholder, help dialog
This commit is contained in:
SnapOtter
2026-06-16 18:04:48 +08:00
committed by GitHub
parent 622c9f98a5
commit 8eee17aeea
29 changed files with 420 additions and 246 deletions
+17 -1
View File
@@ -69,7 +69,7 @@ describe("sanitizeFilename", () => {
});
it("handles filename with only unknown extensions", () => {
expect(sanitizeFilename("data.csv")).toBe("data.csv");
expect(sanitizeFilename("data.xyz")).toBe("data.xyz");
});
it("truncates very long filenames over 200 bytes", () => {
@@ -139,4 +139,20 @@ describe("sanitizeFilename", () => {
expect(result).toBe(`file.${ext}`);
}
});
it("recognizes safe extensions across all modalities", () => {
const cases: Record<string, string> = {
"clip.mp4.exe": "clip.mp4",
"song.mp3.php": "song.mp3",
"report.csv.php": "report.csv",
"doc.docx.exe": "doc.docx",
"data.json.sh": "data.json",
"book.epub.php": "book.epub",
"sheet.xlsx.exe": "sheet.xlsx",
"subs.srt.php": "subs.srt",
};
for (const [input, expected] of Object.entries(cases)) {
expect(sanitizeFilename(input)).toBe(expected);
}
});
});