mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-11 00:02:38 +02:00
refactor: extract getViewerType helper to share extension list
This commit is contained in:
parent
60e5b2cbc7
commit
385ed3377f
3 changed files with 70 additions and 21 deletions
|
|
@ -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. */}
|
||||
<div
|
||||
className="flex-1 min-h-0 overflow-hidden"
|
||||
style={{ display: /\.(html?|pdf)$/i.test(selectedPath) ? 'block' : 'none' }}
|
||||
style={{ display: isCacheableViewerPath(selectedPath) ? 'block' : 'none' }}
|
||||
>
|
||||
<PersistentViewerCache activePath={selectedPath} />
|
||||
</div>
|
||||
{!/\.(html?|pdf)$/i.test(selectedPath) && (
|
||||
{!isCacheableViewerPath(selectedPath) && (
|
||||
selectedPath.endsWith('.md') ? (
|
||||
<div className="flex-1 min-h-0 flex flex-row overflow-hidden">
|
||||
<div className="flex-1 min-h-0 flex flex-col overflow-hidden">
|
||||
|
|
@ -4839,15 +4840,15 @@ function App() {
|
|||
/>
|
||||
)}
|
||||
</div>
|
||||
) : selectedPath && /\.(png|jpe?g|webp|gif|svg|avif|bmp|ico)$/i.test(selectedPath) ? (
|
||||
) : selectedPath && getViewerType(selectedPath) === 'image' ? (
|
||||
<div className="flex-1 min-h-0 overflow-hidden">
|
||||
<ImageFileViewer path={selectedPath} />
|
||||
</div>
|
||||
) : selectedPath && /\.(mp4|mov|webm|m4v)$/i.test(selectedPath) ? (
|
||||
) : selectedPath && getViewerType(selectedPath) === 'video' ? (
|
||||
<div className="flex-1 min-h-0 overflow-hidden">
|
||||
<VideoFileViewer path={selectedPath} />
|
||||
</div>
|
||||
) : selectedPath && /\.(mp3|wav|m4a|ogg|flac|aac)$/i.test(selectedPath) ? (
|
||||
) : selectedPath && getViewerType(selectedPath) === 'audio' ? (
|
||||
<div className="flex-1 min-h-0 overflow-hidden">
|
||||
<AudioFileViewer path={selectedPath} />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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 <HtmlFileViewer path={path} />
|
||||
}
|
||||
if (lower.endsWith('.pdf')) {
|
||||
return <PdfFileViewer path={path} />
|
||||
}
|
||||
const type = getViewerType(path)
|
||||
if (type === 'html') return <HtmlFileViewer path={path} />
|
||||
if (type === 'pdf') return <PdfFileViewer path={path} />
|
||||
return null
|
||||
}
|
||||
|
||||
|
|
@ -31,11 +23,11 @@ interface PersistentViewerCacheProps {
|
|||
*/
|
||||
export function PersistentViewerCache({ activePath }: PersistentViewerCacheProps) {
|
||||
const [mountedPaths, setMountedPaths] = useState<string[]>(() =>
|
||||
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).
|
||||
|
|
|
|||
56
apps/x/apps/renderer/src/lib/file-types.ts
Normal file
56
apps/x/apps/renderer/src/lib/file-types.ts
Normal file
|
|
@ -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<string, ViewerType> = {
|
||||
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'
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue