mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-10 15:52:38 +02:00
3.6 KiB
3.6 KiB
Media File Rendering — Implementation Plan
Goal
Render PDFs, images, and videos in the knowledge view, alongside the existing HTML viewer.
Foundation
All three file types are served through a single custom app:// protocol registered in the Electron main process. This avoids file:// (elevated privileges in Electron) and supports byte-range streaming (needed for video seeking).
Phase 1 — Register app:// custom protocol
What
Main-process protocol handler that serves any workspace-relative file with path traversal guard.
Work
- In
apps/main/src/main.ts, beforeapp.whenReady():protocol.registerSchemesAsPrivilegedforappwith{ standard: true, secure: true, supportFetchAPI: true, stream: true }
- After
app.whenReady():protocol.handle('app', ...)— parses URL, decodes path, resolves against workspace root using the existingresolveWorkspacePath, returnsnet.fetch(pathToFileURL(absPath))- On invalid/outside-workspace path: return 403
Test ✅
- DevTools:
fetch('app://local/knowledge/<known-file>').then(r => r.status)→ 200 - DevTools:
fetch('app://local/../../etc/passwd')→ 403 - Verify response includes correct mime type for known extensions
Phase 2 — Image renderer
What
ImageFileViewer component renders supported images via <img src="app://local/<path>">.
Work
- New file
apps/renderer/src/components/image-file-viewer.tsx - Detects:
.png .jpg .jpeg .webp .gif .svg .avif .bmp .ico - Centered, object-contain layout, dark/light bg
- HEIC/HEIF (or any other unsupported decode failure) → fall back to "Open in system" via
shell:openPath - Loading state and error state mirroring HtmlFileViewer
- Wire into
App.tsxrender switch before the<pre>fallback
Test ✅
- Open
.png,.jpg,.svg,.webpfiles → renders correctly - Open a
.heicfile (or rename a non-image to.pngto force decode failure) → shows fallback - Switch between image files rapidly → no stale image, no flicker
Phase 3 — Video renderer
What
VideoFileViewer component using native <video> tag.
Work
- New file
apps/renderer/src/components/video-file-viewer.tsx - Detects:
.mp4 .mov .webm .m4v <video controls src="app://local/<path>" />— full width, max height- Loading and error states
- Wire into
App.tsxrender switch
Test ✅
- Open a 50MB+ MP4 → starts playing, scrubber works, can seek to middle without re-downloading
- Verify byte-range requests in DevTools Network tab (Range header on requests)
- Switch away mid-playback → video stops cleanly, no leaked audio
Phase 4 — PDF renderer
What
PdfFileViewer using Chromium's built-in PDFium via <iframe src="app://local/<path>">.
Work
- New file
apps/renderer/src/components/pdf-file-viewer.tsx - Detects:
.pdf <iframe src="app://local/<path>" className="w-full h-full" />— Chromium auto-detects PDF mime type and uses PDFium plugin- Confirm
webPreferences.plugins: trueis set on the BrowserWindow (required for PDFium to activate) - Wire into
App.tsxrender switch
Test ✅
- Open multi-page PDF → renders with native zoom/scroll/print toolbar
- Open password-protected PDF → Chromium prompts for password (built-in)
- Switch between PDFs → no stale content
Out of scope for this branch
- DOCX rendering (requires
docx-previewlibrary — separate PR) - Split-pane resizable layout
- Audio (the existing
AudioFileCardis reused if needed) - Annotation/highlighting on PDFs (would require
pdfjs-dist) - Iframe persistence cache for HTML viewer (separate optimization)