fix(desktop): read composer image preview locally in remote mode - #167
Conversation
|
| Filename | Overview |
|---|---|
| apps/desktop/src/lib/desktop-fs.ts | Adds readComposerImagePreview() with local-first read and silent error fallback. Logic is sound; one subtle edge: a falsy (empty-string) return from the local bridge also triggers the fallback, which in remote mode issues an unnecessary backend request. |
| apps/desktop/src/app/chat/hooks/use-composer-actions.ts | Single call-site swap from readDesktopFileDataUrl to readComposerImagePreview; existing try/catch error-handling is preserved and still shows the Image preview failed toast on failure. |
| apps/desktop/src/lib/desktop-fs.test.ts | Adds a focused describe block with 3 new tests; all key paths (remote-local, local-local, ENOENT fallback) are covered. The no-bridge case and empty-string-return case are not explicitly tested but are not regression risks. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[attachImagePath called\nwith filePath] --> B[readComposerImagePreview]
B --> C{window.hermesDesktop\n.readFileDataUrl available?}
C -- No --> F[readDesktopFileDataUrl]
C -- Yes --> D[localReader path]
D -- Returns non-empty string --> E[Return local data URL\n✓ Preview shown]
D -- Returns empty string --> F
D -- Throws e.g. ENOENT --> F
F --> G{Remote mode?}
G -- No --> H[bridge.readFileDataUrl path\nLocal Electron IPC]
G -- Yes --> I[GET /api/fs/read-data-url\nBackend REST]
H --> J[Return data URL]
I --> J
H -- Throws --> K[Error propagates to caller]
I -- 404 Not Found --> K
K --> L[notifyError\nImage preview failed toast]
J --> M[addComposerAttachment\nwith previewUrl]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A[attachImagePath called\nwith filePath] --> B[readComposerImagePreview]
B --> C{window.hermesDesktop\n.readFileDataUrl available?}
C -- No --> F[readDesktopFileDataUrl]
C -- Yes --> D[localReader path]
D -- Returns non-empty string --> E[Return local data URL\n✓ Preview shown]
D -- Returns empty string --> F
D -- Throws e.g. ENOENT --> F
F --> G{Remote mode?}
G -- No --> H[bridge.readFileDataUrl path\nLocal Electron IPC]
G -- Yes --> I[GET /api/fs/read-data-url\nBackend REST]
H --> J[Return data URL]
I --> J
H -- Throws --> K[Error propagates to caller]
I -- 404 Not Found --> K
K --> L[notifyError\nImage preview failed toast]
J --> M[addComposerAttachment\nwith previewUrl]
Reviews (2): Last reviewed commit: "fix(desktop): read composer image previe..." | Re-trigger Greptile
A pasted/clipboard/dropped/picked composer image is written to the CLIENT's local Electron userData dir (saveImageBuffer / saveClipboardImage) even when the app runs against a REMOTE gateway. The preview readback, however, went through readDesktopFileDataUrl(), which in remote mode routes to the backend's GET /api/fs/read-data-url. The backend is a different machine and has no such file, so it returns 404 "File not found" — surfaced to the user as the "Image preview failed" toast on every paste. The attachment itself was unaffected (submit uploads the bytes via image.attach_bytes, which already reads the local path through the Electron bridge) — only the thumbnail preview was broken. Fix: add readComposerImagePreview(), which reads the preview from the LOCAL Electron bridge first and only falls back to the remote-aware read when the local bridge is unavailable or can't read the path (e.g. a path dragged from the remote file tree). This mirrors what the submit-time upload already does and keeps the local-mode behavior identical. Tests: 3 new cases in desktop-fs.test.ts assert remote-mode preview reads the local bridge and never touches the backend FS REST, plus the remote-file-tree fallback path.
6ff24ce to
aa3df2c
Compare
Problem
When the desktop app runs against a remote gateway (thin client on one Mac driving a
hermes dashboardon another), pasting an image into the composer pops "Image preview failed" / "file not found" every time.Root cause
A pasted/clipboard/dropped/picked composer image is written to the client's local Electron
userData/composer-images/dir (saveImageBuffer/saveClipboardImage) — even in remote mode, because that's the only place the clipboard bytes exist. But the preview readback (attachImagePath) went throughreadDesktopFileDataUrl(), which in remote mode routes to the backend'sGET /api/fs/read-data-url. The backend is a different machine and has no such file →404 File not found→ the "Image preview failed" toast.Verified live: the pasted PNG exists on the client Mac's disk; the exact path does not exist on the backend Mac, and
_fs_regular_fileraises404 "File not found"for it.The attachment itself was unaffected — submit uploads the bytes via
image.attach_bytes, which already reads the local path through the Electron bridge (readImageForRemoteAttach→window.hermesDesktop.readFileDataUrl). Only the thumbnail preview was broken.Fix
Add
readComposerImagePreview()which reads the preview from the local Electron bridge first, falling back to the remote-aware read only when the local bridge is unavailable or can't read the path (e.g. a path dragged from the remote file tree). This mirrors what the submit-time upload already does, and keeps local-mode behavior identical.Tests
3 new cases in
desktop-fs.test.ts:apps/desktoptypecheck, eslint, and prettier all clean; the two touched test files are green (22 tests).