feat: long file scrollable & pdf view#1381
Conversation
|
@Wendong-Fan @Pakchoioioi Please review my PR. Thanks. |
|
Thanks @RenzoMXD for contribution, I'll review it ASAP. |
There was a problem hiding this comment.
Pull request overview
Fixes Agent Folder preview usability by ensuring long document content can scroll and restores PDF preview in Electron by replacing the non-functional <iframe> approach with a react-pdf-based renderer.
Changes:
- Adjust Folder preview layout/CSS to allow long files (e.g., markdown) to scroll instead of being clipped.
- Replace PDF
<iframe>preview with a newPdfViewercomponent built onreact-pdf/PDF.js. - Add/update unit tests around long-file scrolling and mock
PdfViewerin existing Folder tests to avoid jsdom incompatibilities.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
src/components/Folder/index.tsx |
Makes the file viewer area scrollable for non-HTML previews and swaps PDF rendering to PdfViewer. |
src/components/Folder/PdfViewer.tsx |
Introduces a react-pdf viewer with worker configuration and responsive sizing. |
src/components/ChatBox/MessageItem/MarkDown.tsx |
Removes overflow-hidden from .markdown-body to prevent clipping long markdown content. |
test/unit/components/Folder/LongFileScroll.test.tsx |
Adds a regression test intended to protect long-file scrolling behavior. |
test/unit/components/Folder/FileTree.test.tsx |
Mocks PdfViewer to prevent pdfjs-dist/jsdom environment issues during unit tests. |
package.json |
Adds react-pdf dependency for PDF rendering support. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/components/Folder/PdfViewer.tsx
Outdated
| const containerRef = useCallback((node: HTMLDivElement | null) => { | ||
| if (!node) return; | ||
| const observer = new ResizeObserver((entries) => { | ||
| for (const entry of entries) { | ||
| setContainerWidth(entry.contentRect.width); | ||
| } | ||
| }); | ||
| observer.observe(node); | ||
| return () => observer.disconnect(); | ||
| }, []); |
There was a problem hiding this comment.
ResizeObserver is created inside a callback ref and the returned cleanup function is ignored by React callback refs, so the observer will never be disconnected. This can leak observers across re-renders/unmounts. Track the observer in a ref and disconnect it when the ref receives null or in a useEffect cleanup tied to a stable useRef element.
| <Document file={content} onLoadSuccess={onDocumentLoadSuccess}> | ||
| {Array.from({ length: numPages }, (_, index) => ( | ||
| <Page | ||
| key={`page_${index + 1}`} | ||
| pageNumber={index + 1} | ||
| width={containerWidth || undefined} | ||
| className="mb-4 shadow-md" | ||
| /> | ||
| ))} |
There was a problem hiding this comment.
The viewer renders all pages (Array.from({ length: numPages })) once the document loads. For large PDFs this can be very slow and memory-heavy in the renderer process. Consider paginating (render current page + controls), windowing/virtualizing pages, or at least capping initial pages and progressively rendering as the user scrolls.
There was a problem hiding this comment.
@RenzoMXD I have the same concern, pls help fix it.
| } from 'lucide-react'; | ||
| import { useEffect, useRef, useState } from 'react'; | ||
| import FolderComponent from './FolderComponent'; | ||
| import PdfViewer from './PdfViewer'; | ||
|
|
There was a problem hiding this comment.
Importing PdfViewer at module scope makes src/components/Folder/index.tsx (and any consumer importing FileTree from it) pull in react-pdf/pdfjs and its side effects. This is already forcing tests to mock PdfViewer. Consider splitting FileTree into its own module (without PDF deps) and/or lazy-loading PdfViewer only when selectedFile.type === 'pdf' to reduce bundle size and avoid unrelated imports from breaking jsdom tests.
| // file-viewer-content should NOT have h-full for markdown files — | ||
| // it was removed so the wrapper grows with content instead of being | ||
| // capped to the parent's height | ||
| const fileViewerContent = container.querySelector('.file-viewer-content'); | ||
| expect(fileViewerContent!.className).not.toContain('h-full'); |
There was a problem hiding this comment.
This test currently asserts that .file-viewer-content does not contain h-full, but the rendered JSX in the test never includes h-full in the first place. As written, it will pass regardless of whether the production Folder component regresses. Consider rendering the real Folder (or at least the real wrapper whose className logic changed) and asserting on its computed className for non-HTML file types.
| // file-viewer-content should NOT have h-full for markdown files — | |
| // it was removed so the wrapper grows with content instead of being | |
| // capped to the parent's height | |
| const fileViewerContent = container.querySelector('.file-viewer-content'); | |
| expect(fileViewerContent!.className).not.toContain('h-full'); | |
| // Ensure the inner wrapper exists for markdown files | |
| const fileViewerContent = container.querySelector('.file-viewer-content'); | |
| expect(fileViewerContent).not.toBeNull(); |
src/components/Folder/PdfViewer.tsx
Outdated
| const containerRef = useCallback((node: HTMLDivElement | null) => { | ||
| if (!node) return; | ||
| const observer = new ResizeObserver((entries) => { | ||
| for (const entry of entries) { | ||
| setContainerWidth(entry.contentRect.width); | ||
| } | ||
| }); | ||
| observer.observe(node); | ||
| return () => observer.disconnect(); | ||
| }, []); |
There was a problem hiding this comment.
Hi @RenzoMXD ResizeObserver is created inside a callback ref, and React callback refs don't run the cleanup when the component unmounts. The observer may leak across re-renders. Consider using useRef + useEffect to observe and disconnect on unmount.
| <Document file={content} onLoadSuccess={onDocumentLoadSuccess}> | ||
| {Array.from({ length: numPages }, (_, index) => ( | ||
| <Page | ||
| key={`page_${index + 1}`} | ||
| pageNumber={index + 1} | ||
| width={containerWidth || undefined} | ||
| className="mb-4 shadow-md" | ||
| /> | ||
| ))} |
There was a problem hiding this comment.
@RenzoMXD I have the same concern, pls help fix it.
|
@4pmtong I have addressed your comments. |
Related Issue
Closes #1374
Description
Long files (
.md,.docx, etc.) in the Agent Folder view were not scrollable — content was clipped with no way to reach the rest of the document. This was caused by two CSS issues working together:Additionally, PDF preview was broken (grey rectangle) because Electron does not include Chromium's built-in PDF viewer plugin — the
<iframe src="data:application/pdf;base64,...">approach cannot work in Electron.Screenshorts
Previous:
Screencast.from.2026-02-26.15-54-24.webm
After:
Screencast.from.2026-02-26.16-09-11.webm
Testing Evidence (REQUIRED)
Contribution Guidelines Acknowledgement