From 385ed3377ff6f415cc29a01fbc7b2e195c8a0a81 Mon Sep 17 00:00:00 2001 From: Gagancreates Date: Fri, 8 May 2026 16:45:47 +0530 Subject: [PATCH] refactor: extract getViewerType helper to share extension list --- apps/x/apps/renderer/src/App.tsx | 15 ++--- .../components/persistent-viewer-cache.tsx | 20 ++----- apps/x/apps/renderer/src/lib/file-types.ts | 56 +++++++++++++++++++ 3 files changed, 70 insertions(+), 21 deletions(-) create mode 100644 apps/x/apps/renderer/src/lib/file-types.ts diff --git a/apps/x/apps/renderer/src/App.tsx b/apps/x/apps/renderer/src/App.tsx index 39cf44e2..4fd75591 100644 --- a/apps/x/apps/renderer/src/App.tsx +++ b/apps/x/apps/renderer/src/App.tsx @@ -17,6 +17,7 @@ import { ImageFileViewer } from '@/components/image-file-viewer'; import { VideoFileViewer } from '@/components/video-file-viewer'; import { AudioFileViewer } from '@/components/audio-file-viewer'; import { PersistentViewerCache } from '@/components/persistent-viewer-cache'; +import { getViewerType, isMediaPath, isCacheableViewerPath } from '@/lib/file-types'; import { useDebounce } from './hooks/use-debounce'; import { SidebarContentPanel } from '@/components/sidebar-content'; import { SuggestedTopicsView } from '@/components/suggested-topics-view'; @@ -1428,10 +1429,10 @@ function App() { } const requestId = (fileLoadRequestIdRef.current += 1) const pathToLoad = selectedPath - // Media viewers (HTML, image, video, PDF) self-load via app:// protocol. + // Media viewers (HTML, image, video, audio, PDF) self-load via app:// protocol. // Skip the generic UTF-8 loader so we don't trash fileContent with binary // bytes or double-fetch large files. - if (/\.(html?|png|jpe?g|webp|gif|svg|avif|bmp|ico|mp4|mov|webm|m4v|pdf|mp3|wav|m4a|ogg|flac|aac)$/i.test(pathToLoad)) { + if (isMediaPath(pathToLoad)) { setFileContent('') return } @@ -4725,11 +4726,11 @@ function App() { {/* Always-mounted persistent cache for HTML/PDF — hidden when active file is something else, so iframes preserve scroll/page/zoom across switches. */}
- {!/\.(html?|pdf)$/i.test(selectedPath) && ( + {!isCacheableViewerPath(selectedPath) && ( selectedPath.endsWith('.md') ? (
@@ -4839,15 +4840,15 @@ function App() { /> )}
- ) : selectedPath && /\.(png|jpe?g|webp|gif|svg|avif|bmp|ico)$/i.test(selectedPath) ? ( + ) : selectedPath && getViewerType(selectedPath) === 'image' ? (
- ) : selectedPath && /\.(mp4|mov|webm|m4v)$/i.test(selectedPath) ? ( + ) : selectedPath && getViewerType(selectedPath) === 'video' ? (
- ) : selectedPath && /\.(mp3|wav|m4a|ogg|flac|aac)$/i.test(selectedPath) ? ( + ) : selectedPath && getViewerType(selectedPath) === 'audio' ? (
diff --git a/apps/x/apps/renderer/src/components/persistent-viewer-cache.tsx b/apps/x/apps/renderer/src/components/persistent-viewer-cache.tsx index d655b929..ca71eca2 100644 --- a/apps/x/apps/renderer/src/components/persistent-viewer-cache.tsx +++ b/apps/x/apps/renderer/src/components/persistent-viewer-cache.tsx @@ -1,22 +1,14 @@ import { useEffect, useState } from 'react' import { HtmlFileViewer } from './html-file-viewer' import { PdfFileViewer } from './pdf-file-viewer' +import { getViewerType, isCacheableViewerPath } from '@/lib/file-types' const CACHE_LIMIT = 3 -function isCacheable(path: string): boolean { - const lower = path.toLowerCase() - return lower.endsWith('.html') || lower.endsWith('.htm') || lower.endsWith('.pdf') -} - function renderViewer(path: string): JSX.Element | null { - const lower = path.toLowerCase() - if (lower.endsWith('.html') || lower.endsWith('.htm')) { - return - } - if (lower.endsWith('.pdf')) { - return - } + const type = getViewerType(path) + if (type === 'html') return + if (type === 'pdf') return return null } @@ -31,11 +23,11 @@ interface PersistentViewerCacheProps { */ export function PersistentViewerCache({ activePath }: PersistentViewerCacheProps) { const [mountedPaths, setMountedPaths] = useState(() => - isCacheable(activePath) ? [activePath] : [] + isCacheableViewerPath(activePath) ? [activePath] : [] ) useEffect(() => { - if (!isCacheable(activePath)) return + if (!isCacheableViewerPath(activePath)) return setMountedPaths((prev) => { // Never reorder existing entries — moving a keyed iframe in the DOM // detaches it, which causes the browser to re-navigate (state lost). diff --git a/apps/x/apps/renderer/src/lib/file-types.ts b/apps/x/apps/renderer/src/lib/file-types.ts new file mode 100644 index 00000000..d4477f7a --- /dev/null +++ b/apps/x/apps/renderer/src/lib/file-types.ts @@ -0,0 +1,56 @@ +/** + * Single source of truth for which file types the knowledge viewer renders. + * + * Both the App.tsx loader-skip check and the render-switch consume this so + * adding a new extension is a one-place edit. The persistent-viewer-cache + * also uses it to decide what to keep mounted. + */ + +export type ViewerType = 'html' | 'image' | 'video' | 'audio' | 'pdf' + +const VIEWER_BY_EXT: Record = { + html: 'html', + htm: 'html', + png: 'image', + jpg: 'image', + jpeg: 'image', + webp: 'image', + gif: 'image', + svg: 'image', + avif: 'image', + bmp: 'image', + ico: 'image', + mp4: 'video', + mov: 'video', + webm: 'video', + m4v: 'video', + mp3: 'audio', + wav: 'audio', + m4a: 'audio', + ogg: 'audio', + flac: 'audio', + aac: 'audio', + pdf: 'pdf', +} + +function extensionOf(path: string): string { + const lower = path.toLowerCase() + const dot = lower.lastIndexOf('.') + return dot >= 0 ? lower.slice(dot + 1) : '' +} + +/** Returns the viewer type for a path, or null if no media viewer handles it. */ +export function getViewerType(path: string): ViewerType | null { + return VIEWER_BY_EXT[extensionOf(path)] ?? null +} + +/** True if the path is rendered by one of the dedicated media viewers. */ +export function isMediaPath(path: string): boolean { + return getViewerType(path) !== null +} + +/** True if the viewer for this path participates in the persistent mount cache. */ +export function isCacheableViewerPath(path: string): boolean { + const t = getViewerType(path) + return t === 'html' || t === 'pdf' +}