From a0004a5766e785f480dddd957522d8e4c35625b3 Mon Sep 17 00:00:00 2001 From: Luis Arias Date: Thu, 1 Oct 2026 13:28:48 +0000 Subject: [PATCH] feat(desktop): preview PDF sources in-app MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 👾 Generated with [Letta Code](https://letta.com) Model: lc-codex/gpt-6-luna Co-Authored-By: Letta Code --- docs/architecture/documents.md | 2 +- docs/architecture/overview.md | 2 +- docs/proposals/source-preview.md | 2 +- scripts/check_translations.mjs | 9 +- .../backend/modules/documents/models.py | 7 + .../backend/modules/documents/schemas.py | 1 + .../integration/documents/test_routes.py | 21 ++ .../features/dashboard/chrome-prefs.test.ts | 29 ++ .../src/features/dashboard/chrome-prefs.ts | 38 +++ .../dashboard/dashboard-page.test.tsx | 81 ++++- .../src/features/dashboard/dashboard-page.tsx | 56 +++- .../src/features/file-viewers/pdf-viewer.tsx | 289 ++++++++++++++++++ .../features/file-viewers/registry.test.ts | 11 + .../src/features/file-viewers/registry.tsx | 21 ++ .../source-preview/source-preview-panel.tsx | 97 ++++++ .../frontend/src/features/sources/api.ts | 10 +- .../features/sources/source-upload.test.tsx | 43 +++ .../src/features/sources/sources-panel.tsx | 23 +- .../src/features/studio/studio-panel.test.tsx | 1 + surfsense_local/frontend/translations/de.json | 13 + surfsense_local/frontend/translations/en.json | 13 + surfsense_local/frontend/translations/es.json | 13 + surfsense_local/frontend/translations/fr.json | 13 + surfsense_local/frontend/translations/hi.json | 13 + surfsense_local/frontend/translations/ja.json | 13 + surfsense_local/frontend/translations/ko.json | 13 + .../frontend/translations/pt-BR.json | 13 + surfsense_local/frontend/translations/ru.json | 13 + .../frontend/translations/zh-CN.json | 13 + 29 files changed, 858 insertions(+), 15 deletions(-) create mode 100644 surfsense_local/frontend/src/features/dashboard/chrome-prefs.test.ts create mode 100644 surfsense_local/frontend/src/features/file-viewers/pdf-viewer.tsx create mode 100644 surfsense_local/frontend/src/features/file-viewers/registry.test.ts create mode 100644 surfsense_local/frontend/src/features/file-viewers/registry.tsx create mode 100644 surfsense_local/frontend/src/features/source-preview/source-preview-panel.tsx diff --git a/docs/architecture/documents.md b/docs/architecture/documents.md index a8e3adfa9..eefa7eba0 100644 --- a/docs/architecture/documents.md +++ b/docs/architecture/documents.md @@ -78,7 +78,7 @@ No filename is stored in a row: the document's directory is the record. The orig `GET .../original` serves the stored bytes with the document's title as the filename and the stored MIME type, always as an attachment: an HTML or SVG file rendered inline would run its script against the app's origin. It answers `404` when there is no file behind the document, as for a note. -The desktop app opens originals natively instead. Through the preload bridge, Electron applies the same rule to the directory under the data directory for the given ids, since the main process never calls the backend, and hands it to the operating system, or reveals it in the file manager ([`document-files.ts`](../../surfsense_local/electron/src/main/document-files.ts)). +The desktop app previews an original in its left rail when its MIME type has a source viewer, starting with PDF. Other file types still open natively: through the preload bridge, Electron applies the same rule to the directory under the data directory for the given ids, since the main process never calls the backend, and hands it to the operating system, or reveals it in the file manager ([`document-files.ts`](../../surfsense_local/electron/src/main/document-files.ts)). The `DocumentRead` row carries `mime_type` from stored metadata only for `FILE`; notes always report `null`, even if their user-provided metadata contains a `mime_type`. The preview fetches the existing `/original` route, which remains an attachment so directly navigating to HTML or SVG cannot execute it on the app's origin. ## Ingest pipeline diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md index 0792c67ba..cfd63b9ec 100644 --- a/docs/architecture/overview.md +++ b/docs/architecture/overview.md @@ -89,7 +89,7 @@ The rules that keep the processes out of each other's way: - A Vite and React SPA with Tailwind and shadcn/ui components in `components/ui/`, built on Base UI in shadcn's `base-nova` style; assistant-ui drives the conversation, and still depends on Radix. Packaged, Electron loads `frontend/dist/index.html` from disk, which is why Vite builds with relative asset paths. In dev it loads the Vite server on port 5173. - The API's address comes from the preload: Electron passes it as a command-line argument and [`preload/index.ts`](../../surfsense_local/electron/src/preload/index.ts) exposes it as `window.surfsense.apiUrl`. In a bare browser there is no preload, requests stay root-relative, and the Vite dev server forwards `/health`, `/llm`, `/workspaces`, `/chat` and `/artifacts` to `127.0.0.1:8000`. -- The preload bridge is the renderer's only other channel: opening or revealing an original file, opening an external link, the platform name, updates, theme, the title bar, this session's log and unexpected sidecar-exit notices. The renderer never sees Node or the sidecars directly. +- The preload bridge is the renderer's only other channel: opening or revealing an original file natively, opening an external link, the platform name, updates, theme, the title bar, this session's log and unexpected sidecar-exit notices. The renderer never sees Node or the sidecars directly. A source with a registered viewer (PDF first) is fetched from the API and previewed in the left rail; other file types keep the native open action ([`documents.md`](documents.md)). - API calls go through `request()` in [`lib/api.ts`](../../surfsense_local/frontend/src/lib/api.ts), except the Studio viewers, which fetch an artifact's file bytes directly; an ``, `