From eb5059574e662ec998602b7cf92b7073383d598e Mon Sep 17 00:00:00 2001 From: HuanLinOTO Date: Mon, 10 Aug 2026 16:54:13 +0800 Subject: [PATCH] fix: route images before binary detection --- src/client/EditorView.tsx | 12 +++++++----- src/client/image-types.ts | 8 ++++++++ tests/unit.spec.ts | 12 ++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 src/client/image-types.ts diff --git a/src/client/EditorView.tsx b/src/client/EditorView.tsx index 355f34d..02b380c 100644 --- a/src/client/EditorView.tsx +++ b/src/client/EditorView.tsx @@ -23,9 +23,9 @@ import { DocxView, XlsxView } from './office-view.tsx' import { isPdfExt } from './pdf-types.ts' import { PdfView } from './PdfView.tsx' import { PptxView } from './PptxView.tsx' +import { isImageExt } from './image-types.ts' import css from './sidebar.module.css' -const IMAGE_EXT = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.bmp', '.ico', '.avif'] const MD_EXT = ['.md', '.markdown'] type EditorLoad = @@ -67,6 +67,12 @@ export function EditorView(props: { scope: SessionScope; path: string; title: st // own renderers below. Detect by extension and skip fsRead entirely — // the renderer fetches bytes through the media route itself. const ext = extOfPath(path) + // Images are binary by nature; short-circuit before fsRead's NUL probe or + // every PNG/JPEG would be classified as an unsupported binary file. + if (isImageExt(ext)) { + setLoad({ status: 'ready', kind: 'image', content: '', truncated: false }) + return + } if (isPdfExt(ext)) { setLoad({ status: 'ready', kind: 'pdf', content: '', truncated: false }) return @@ -86,10 +92,6 @@ export function EditorView(props: { scope: SessionScope; path: string; title: st setLoad({ status: 'binary' }) return } - if (IMAGE_EXT.includes(ext)) { - setLoad({ status: 'ready', kind: 'image', content: '', truncated: false }) - return - } setLoad({ status: 'ready', kind: MD_EXT.includes(ext) ? 'md' : 'text', diff --git a/src/client/image-types.ts b/src/client/image-types.ts new file mode 100644 index 0000000..c9c72a2 --- /dev/null +++ b/src/client/image-types.ts @@ -0,0 +1,8 @@ +/** Image extension dispatch, separate from React for unit testing. */ +export const IMAGE_EXTENSIONS = [ + '.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.bmp', '.ico', '.avif', +] as readonly string[] + +export function isImageExt(ext: string): boolean { + return IMAGE_EXTENSIONS.includes(ext) +} diff --git a/tests/unit.spec.ts b/tests/unit.spec.ts index d1a0366..e35f67c 100644 --- a/tests/unit.spec.ts +++ b/tests/unit.spec.ts @@ -11,6 +11,7 @@ import { SIDEBAR_PREFS_DEFAULTS } from '../src/prefs-shared.ts' import { extOf, languageKeyForExt } from '../src/client/lang.ts' import { officeKindForExt } from '../src/client/office-types.ts' import { isPdfExt } from '../src/client/pdf-types.ts' +import { isImageExt } from '../src/client/image-types.ts' import { relativeTo } from '../src/client/paths.ts' import { producedForClosing, resolveSidebarPath, selectProducedFiles } from '../src/client/produced-files.ts' import { defaultShell, ensureSpawnHelper } from '../src/pty-manager.ts' @@ -486,6 +487,17 @@ describe('pdf preview kind', () => { }) }) +describe('image preview kind', () => { + it('routes supported image extensions before binary probing', () => { + expect(isImageExt('.png')).toBe(true) + expect(isImageExt('.jpg')).toBe(true) + expect(isImageExt('.svg')).toBe(true) + expect(isImageExt('.avif')).toBe(true) + expect(isImageExt('.pdf')).toBe(false) + expect(isImageExt('')).toBe(false) + }) +}) + describe('side card preferences', () => { /** A fake settings wire face whose settingsGet resolves to one raw value. */ const wire = (value: unknown): SidebarSettingsClient => ({