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:
SnapOtter
2026-06-15 22:26:24 +08:00
committed by GitHub
parent 25babfae15
commit d8cf979d4b
83 changed files with 16014 additions and 112 deletions
+4
View File
@@ -722,6 +722,7 @@ export const ar: TranslationKeys = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2470,6 +2471,7 @@ export const ar: TranslationKeys = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "جاري إنشاء المعاينة...",
@@ -2488,6 +2490,7 @@ export const ar: TranslationKeys = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "البحث في الأدوات...",
@@ -3445,5 +3448,6 @@ export const ar: TranslationKeys = {
settingsDialog: "Settings",
helpDialog: "Help",
navigationMenu: "Navigation",
homeLink: "SnapOtter home",
},
} as const;
+4
View File
@@ -727,6 +727,7 @@ export const de: TranslationKeys = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2484,6 +2485,7 @@ export const de: TranslationKeys = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "Vorschau wird generiert...",
@@ -2502,6 +2504,7 @@ export const de: TranslationKeys = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "Werkzeuge suchen...",
@@ -3476,5 +3479,6 @@ export const de: TranslationKeys = {
settingsDialog: "Settings",
helpDialog: "Help",
navigationMenu: "Navigation",
homeLink: "SnapOtter home",
},
} as const;
+4
View File
@@ -684,6 +684,7 @@ export const en = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2434,6 +2435,7 @@ export const en = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "Generating preview...",
@@ -2452,6 +2454,7 @@ export const en = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "Search tools...",
@@ -3412,6 +3415,7 @@ export const en = {
settingsDialog: "Settings",
helpDialog: "Help",
navigationMenu: "Navigation",
homeLink: "SnapOtter home",
},
} as const;
+4
View File
@@ -712,6 +712,7 @@ export const es: TranslationKeys = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2466,6 +2467,7 @@ export const es: TranslationKeys = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "Generando vista previa...",
@@ -2484,6 +2486,7 @@ export const es: TranslationKeys = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "Buscar herramientas...",
@@ -3454,5 +3457,6 @@ export const es: TranslationKeys = {
settingsDialog: "Settings",
helpDialog: "Help",
navigationMenu: "Navigation",
homeLink: "SnapOtter home",
},
} as const;
+4
View File
@@ -728,6 +728,7 @@ export const fr: TranslationKeys = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2485,6 +2486,7 @@ export const fr: TranslationKeys = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "Generation de l'apercu...",
@@ -2503,6 +2505,7 @@ export const fr: TranslationKeys = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "Rechercher des outils...",
@@ -3475,5 +3478,6 @@ export const fr: TranslationKeys = {
settingsDialog: "Settings",
helpDialog: "Help",
navigationMenu: "Navigation",
homeLink: "SnapOtter home",
},
} as const;
+4
View File
@@ -719,6 +719,7 @@ export const hi: TranslationKeys = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2467,6 +2468,7 @@ export const hi: TranslationKeys = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "प्रीव्यू जनरेट हो रहा है...",
@@ -2485,6 +2487,7 @@ export const hi: TranslationKeys = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "टूल्स खोजें...",
@@ -3442,5 +3445,6 @@ export const hi: TranslationKeys = {
settingsDialog: "Settings",
helpDialog: "Help",
navigationMenu: "Navigation",
homeLink: "SnapOtter home",
},
} as const;
+4
View File
@@ -727,6 +727,7 @@ export const id: TranslationKeys = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2479,6 +2480,7 @@ export const id: TranslationKeys = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "Membuat pratinjau...",
@@ -2497,6 +2499,7 @@ export const id: TranslationKeys = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "Cari alat...",
@@ -3460,5 +3463,6 @@ export const id: TranslationKeys = {
settingsDialog: "Settings",
helpDialog: "Help",
navigationMenu: "Navigation",
homeLink: "SnapOtter home",
},
} as const;
+4
View File
@@ -726,6 +726,7 @@ export const it: TranslationKeys = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2478,6 +2479,7 @@ export const it: TranslationKeys = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "Generazione anteprima...",
@@ -2496,6 +2498,7 @@ export const it: TranslationKeys = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "Cerca strumenti...",
@@ -3469,5 +3472,6 @@ export const it: TranslationKeys = {
settingsDialog: "Impostazioni",
helpDialog: "Guida",
navigationMenu: "Menu di navigazione",
homeLink: "SnapOtter home",
},
} as const;
+4
View File
@@ -687,6 +687,7 @@ export const ja: TranslationKeys = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2437,6 +2438,7 @@ export const ja: TranslationKeys = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "プレビューを生成中...",
@@ -2455,6 +2457,7 @@ export const ja: TranslationKeys = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "ツールを検索...",
@@ -3417,6 +3420,7 @@ export const ja: TranslationKeys = {
settingsDialog: "Settings",
helpDialog: "Help",
navigationMenu: "Navigation",
homeLink: "SnapOtter home",
},
} as const;
+4
View File
@@ -674,6 +674,7 @@ export const ko: TranslationKeys = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2422,6 +2423,7 @@ export const ko: TranslationKeys = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "미리보기 생성 중...",
@@ -2440,6 +2442,7 @@ export const ko: TranslationKeys = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "도구 검색...",
@@ -3401,6 +3404,7 @@ export const ko: TranslationKeys = {
settingsDialog: "Settings",
helpDialog: "Help",
navigationMenu: "Navigation",
homeLink: "SnapOtter home",
},
} as const;
+4
View File
@@ -727,6 +727,7 @@ export const nl: TranslationKeys = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2481,6 +2482,7 @@ export const nl: TranslationKeys = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "Preview genereren...",
@@ -2499,6 +2501,7 @@ export const nl: TranslationKeys = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "Gereedschap zoeken...",
@@ -3463,5 +3466,6 @@ export const nl: TranslationKeys = {
settingsDialog: "Settings",
helpDialog: "Help",
navigationMenu: "Navigation",
homeLink: "SnapOtter home",
},
} as const;
+4
View File
@@ -728,6 +728,7 @@ export const pl: TranslationKeys = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2482,6 +2483,7 @@ export const pl: TranslationKeys = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "Generowanie podglądu...",
@@ -2500,6 +2502,7 @@ export const pl: TranslationKeys = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "Szukaj narzędzi...",
@@ -3471,5 +3474,6 @@ export const pl: TranslationKeys = {
settingsDialog: "Settings",
helpDialog: "Help",
navigationMenu: "Navigation",
homeLink: "SnapOtter home",
},
} as const;
+4
View File
@@ -725,6 +725,7 @@ export const ptBR: TranslationKeys = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2478,6 +2479,7 @@ export const ptBR: TranslationKeys = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "Gerando visualizacao...",
@@ -2496,6 +2498,7 @@ export const ptBR: TranslationKeys = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "Buscar ferramentas...",
@@ -3464,5 +3467,6 @@ export const ptBR: TranslationKeys = {
settingsDialog: "Settings",
helpDialog: "Help",
navigationMenu: "Navigation",
homeLink: "SnapOtter home",
},
} as const;
+4
View File
@@ -727,6 +727,7 @@ export const ru: TranslationKeys = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2480,6 +2481,7 @@ export const ru: TranslationKeys = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "Генерация предпросмотра...",
@@ -2498,6 +2500,7 @@ export const ru: TranslationKeys = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "Поиск инструментов...",
@@ -3462,5 +3465,6 @@ export const ru: TranslationKeys = {
settingsDialog: "Settings",
helpDialog: "Help",
navigationMenu: "Navigation",
homeLink: "SnapOtter home",
},
} as const;
+4
View File
@@ -725,6 +725,7 @@ export const sv: TranslationKeys = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2477,6 +2478,7 @@ export const sv: TranslationKeys = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "Genererar forhandsvisning...",
@@ -2495,6 +2497,7 @@ export const sv: TranslationKeys = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "Sok verktyg...",
@@ -3457,5 +3460,6 @@ export const sv: TranslationKeys = {
settingsDialog: "Settings",
helpDialog: "Help",
navigationMenu: "Navigation",
homeLink: "SnapOtter home",
},
} as const;
+4
View File
@@ -720,6 +720,7 @@ export const th: TranslationKeys = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2459,6 +2460,7 @@ export const th: TranslationKeys = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "กำลังสร้างตัวอย่าง...",
@@ -2477,6 +2479,7 @@ export const th: TranslationKeys = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "ค้นหาเครื่องมือ...",
@@ -3433,5 +3436,6 @@ export const th: TranslationKeys = {
settingsDialog: "Settings",
helpDialog: "Help",
navigationMenu: "Navigation",
homeLink: "SnapOtter home",
},
} as const;
+4
View File
@@ -728,6 +728,7 @@ export const tr: TranslationKeys = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2482,6 +2483,7 @@ export const tr: TranslationKeys = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "Önizleme oluşturuluyor...",
@@ -2500,6 +2502,7 @@ export const tr: TranslationKeys = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "Araç ara...",
@@ -3468,5 +3471,6 @@ export const tr: TranslationKeys = {
settingsDialog: "Settings",
helpDialog: "Help",
navigationMenu: "Navigation",
homeLink: "SnapOtter home",
},
} as const;
+4
View File
@@ -727,6 +727,7 @@ export const uk: TranslationKeys = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2480,6 +2481,7 @@ export const uk: TranslationKeys = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "Генерація попереднього перегляду...",
@@ -2498,6 +2500,7 @@ export const uk: TranslationKeys = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "Пошук інструментів...",
@@ -3463,5 +3466,6 @@ export const uk: TranslationKeys = {
settingsDialog: "Settings",
helpDialog: "Help",
navigationMenu: "Navigation",
homeLink: "SnapOtter home",
},
} as const;
+4
View File
@@ -728,6 +728,7 @@ export const vi: TranslationKeys = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2479,6 +2480,7 @@ export const vi: TranslationKeys = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "Đang tạo bản xem trước...",
@@ -2497,6 +2499,7 @@ export const vi: TranslationKeys = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "Tìm kiếm công cụ...",
@@ -3457,5 +3460,6 @@ export const vi: TranslationKeys = {
settingsDialog: "Settings",
helpDialog: "Help",
navigationMenu: "Navigation",
homeLink: "SnapOtter home",
},
} as const;
+4
View File
@@ -674,6 +674,7 @@ export const zhCN: TranslationKeys = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2412,6 +2413,7 @@ export const zhCN: TranslationKeys = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "正在生成预览...",
@@ -2430,6 +2432,7 @@ export const zhCN: TranslationKeys = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "搜索工具...",
@@ -3383,5 +3386,6 @@ export const zhCN: TranslationKeys = {
settingsDialog: "Settings",
helpDialog: "Help",
navigationMenu: "Navigation",
homeLink: "SnapOtter home",
},
} as const;
+4
View File
@@ -673,6 +673,7 @@ export const zhTW: TranslationKeys = {
loadFailed: "Failed to load document.",
previousPage: "Previous page",
nextPage: "Next page",
inputNotPreviewable: "Preview not available for this file type",
},
},
toolSettings: {
@@ -2410,6 +2411,7 @@ export const zhTW: TranslationKeys = {
downloadAsFile: "Download as file",
downloadAll: "Download All",
downloadFiles: "Download {count} files",
audioDecodeUnsupported: "This audio format cannot be previewed in the browser",
},
homePage: {
generatingPreview: "正在產生預覽...",
@@ -2428,6 +2430,7 @@ export const zhTW: TranslationKeys = {
documents: "Documents",
data: "Data",
toolCount: "{count} tools",
heading: "SnapOtter Tools",
},
fullscreenGrid: {
searchPlaceholder: "搜尋工具...",
@@ -3382,6 +3385,7 @@ export const zhTW: TranslationKeys = {
settingsDialog: "Settings",
helpDialog: "Help",
navigationMenu: "Navigation",
homeLink: "SnapOtter home",
},
} as const;
-2
View File
@@ -114,8 +114,6 @@ export const DOCUMENT_INPUTS = [
".md",
".html",
".epub",
".mobi",
".azw3",
];
export const FILE_INPUTS = [".csv", ".json", ".xml", ".yaml", ".yml", ".zip"];