diff --git a/apps/x/apps/renderer/src/App.tsx b/apps/x/apps/renderer/src/App.tsx index 915e3516..39cf44e2 100644 --- a/apps/x/apps/renderer/src/App.tsx +++ b/apps/x/apps/renderer/src/App.tsx @@ -13,11 +13,10 @@ import { ChatInputWithMentions, type StagedAttachment } from './components/chat- import { ChatMessageAttachments } from '@/components/chat-message-attachments' import { GraphView, type GraphEdge, type GraphNode } from '@/components/graph-view'; import { BasesView, type BaseConfig, DEFAULT_BASE_CONFIG } from '@/components/bases-view'; -import { HtmlFileViewer } from '@/components/html-file-viewer'; import { ImageFileViewer } from '@/components/image-file-viewer'; import { VideoFileViewer } from '@/components/video-file-viewer'; -import { PdfFileViewer } from '@/components/pdf-file-viewer'; import { AudioFileViewer } from '@/components/audio-file-viewer'; +import { PersistentViewerCache } from '@/components/persistent-viewer-cache'; import { useDebounce } from './hooks/use-debounce'; import { SidebarContentPanel } from '@/components/sidebar-content'; import { SuggestedTopicsView } from '@/components/suggested-topics-view'; @@ -4722,6 +4721,15 @@ function App() { /> ) : selectedPath ? ( + <> + {/* 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) && ( selectedPath.endsWith('.md') ? (
@@ -4831,10 +4839,6 @@ function App() { /> )}
- ) : selectedPath?.toLowerCase().endsWith('.html') || selectedPath?.toLowerCase().endsWith('.htm') ? ( -
- -
) : selectedPath && /\.(png|jpe?g|webp|gif|svg|avif|bmp|ico)$/i.test(selectedPath) ? (
@@ -4843,10 +4847,6 @@ function App() {
- ) : selectedPath?.toLowerCase().endsWith('.pdf') ? ( -
- -
) : selectedPath && /\.(mp3|wav|m4a|ogg|flac|aac)$/i.test(selectedPath) ? (
@@ -4858,6 +4858,8 @@ function App() {
) + )} + ) : selectedTask ? (
+ } + if (lower.endsWith('.pdf')) { + return + } + return null +} + +interface PersistentViewerCacheProps { + activePath: string +} + +/** + * Keeps recently-opened HTML and PDF viewers mounted in the DOM, + * toggling visibility instead of unmounting. This preserves iframe + * state (PDF page/zoom, HTML scroll/JS state) across file switches. + */ +export function PersistentViewerCache({ activePath }: PersistentViewerCacheProps) { + const [mountedPaths, setMountedPaths] = useState(() => + isCacheable(activePath) ? [activePath] : [] + ) + + useEffect(() => { + if (!isCacheable(activePath)) return + setMountedPaths((prev) => { + if (prev.includes(activePath)) { + // Move to most-recent position + return [...prev.filter((p) => p !== activePath), activePath] + } + const next = [...prev, activePath] + return next.length > CACHE_LIMIT ? next.slice(-CACHE_LIMIT) : next + }) + }, [activePath]) + + return ( +
+ {mountedPaths.map((p) => ( +
+ {renderViewer(p)} +
+ ))} +
+ ) +}