diff --git a/apps/x/PLAN-MEDIA.md b/apps/x/PLAN-MEDIA.md new file mode 100644 index 00000000..282037d0 --- /dev/null +++ b/apps/x/PLAN-MEDIA.md @@ -0,0 +1,93 @@ +# 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 +1. In `apps/main/src/main.ts`, before `app.whenReady()`: + - `protocol.registerSchemesAsPrivileged` for `app` with `{ standard: true, secure: true, supportFetchAPI: true, stream: true }` +2. After `app.whenReady()`: + - `protocol.handle('app', ...)` — parses URL, decodes path, resolves against workspace root using the existing `resolveWorkspacePath`, returns `net.fetch(pathToFileURL(absPath))` + - On invalid/outside-workspace path: return 403 + +### Test ✅ +- DevTools: `fetch('app://local/knowledge/').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 ``. + +### Work +1. New file `apps/renderer/src/components/image-file-viewer.tsx` +2. Detects: `.png .jpg .jpeg .webp .gif .svg .avif .bmp .ico` +3. Centered, object-contain layout, dark/light bg +4. HEIC/HEIF (or any other unsupported decode failure) → fall back to "Open in system" via `shell:openPath` +5. Loading state and error state mirroring HtmlFileViewer +6. Wire into `App.tsx` render switch before the `
` fallback
+
+### Test ✅
+- Open `.png`, `.jpg`, `.svg`, `.webp` files → renders correctly
+- Open a `.heic` file (or rename a non-image to `.png` to force decode failure) → shows fallback
+- Switch between image files rapidly → no stale image, no flicker
+
+---
+
+## Phase 3 — Video renderer
+
+### What
+`VideoFileViewer` component using native `