Skip to content

feat: pdf support#2166

Closed
janburzinski wants to merge 11 commits into
mainfrom
emdash/pdf-support-m55s5
Closed

feat: pdf support#2166
janburzinski wants to merge 11 commits into
mainfrom
emdash/pdf-support-m55s5

Conversation

@janburzinski

Copy link
Copy Markdown
Collaborator

No description provided.

@janburzinski janburzinski marked this pull request as ready for review May 21, 2026 18:02
@janburzinski

Copy link
Copy Markdown
Collaborator Author
image

@greptile-apps

greptile-apps Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds first-class PDF support across both local and SSH workspaces: PDFs can now be opened as file tabs and compared side-by-side in the diff view. It also replaces @parcel/watcher with a chokidar (Linux) / native fs.watch (macOS + Windows) hybrid, addressing the previously reported Linux watcher crash.

  • Local PDF rendering: local-fs.ts generates a short-lived token URL via protocol.ts, and the new PdfRenderer uses a <object> element backed by Electron's built-in PDF viewer; SSH workspaces fall back to base64 data URLs.
  • Diff view: diff-file-renderer.tsx adds PdfDiffView with reactive React Query fetching keyed on git revision, transient-retry logic for the modified side, and correct side-panel labelling for added/deleted files.
  • Git blobs: git-service.ts refactors _readImageBlob into a shared _readPreviewBlob helper and wires up getPdfAtRef / getPdfAtIndex, reusing the same size-cap and LFS guard already in place for images.

Confidence Score: 4/5

Safe to merge after adding error handlers to both the chokidar and native fs.watch watchers in local-fs.ts

Both the chokidar watcher (Linux path) and the native fs.watch watcher (macOS/Windows path) are missing 'error' event listeners. Since both FSWatcher types extend Node.js EventEmitter, an unhandled 'error' event — triggered by conditions like inotify limits, permission changes, or the watched directory being deleted — would throw an uncaught exception in the Electron main process. The rest of the PR is well-constructed: the token-based URL scheme addresses the earlier memory-leak concern, the readSftpFully loop fixes the partial-read issue, and the diff view's reactive query keying correctly handles revision changes.

src/main/core/fs/impl/local-fs.ts — both the chokidar branch (line 802) and the fs.watch branch (line 838) need an 'error' event handler added before merge

Important Files Changed

Filename Overview
src/main/core/fs/impl/local-fs.ts Replaces parcel/watcher with a chokidar (Linux) / native fs.watch (macOS+Win) hybrid and adds readPdf; both new watchers are missing an 'error' event handler that could crash the main process
src/main/app/protocol.ts Adds token-based workspace file URL generation with a 30s TTL timer for auto-cleanup; correctly addresses the previously reported unbounded-map leak
src/main/core/fs/impl/ssh-fs.ts Adds readSftpFully loop helper (fixing the partial-read issue) and readPdf returning base64 data URLs for SSH workspaces
src/main/core/git/impl/git-service.ts Refactors _readImageBlob into a shared _readPreviewBlob helper and adds getPdfAtRef/getPdfAtIndex; refactoring is clean and backward-compatible
src/renderer/features/tasks/diff-view/main-panel/diff-file-renderer.tsx Adds PdfDiffView with side-by-side panels, reactive React Query fetching, and transient retry logic; loadPdfFromDisk misclassifies 'too large' errors as the generic git-error reason
src/renderer/features/tasks/editor/stores/file-model-lifecycle-store.ts Extends preview loading to PDFs with a pendingPreviewLoads deduplication guard and a 30s timeout; logic is correct
src/renderer/lib/editor/pdf-renderer.tsx New PDF renderer using the browser's built-in object viewer with loading/error states; straightforward and correct
src/shared/git.ts Adds PdfReadResult type mirroring ImageReadResult; clean addition with no issues

Sequence Diagram

sequenceDiagram
    participant R as Renderer
    participant IPC as IPC rpc
    participant FS as LocalFS or SshFS
    participant P as protocol.ts
    participant OBJ as object element

    R->>IPC: rpc.fs.readPdf(projectId, workspaceId, path)
    IPC->>FS: readPdf(path)

    alt Local filesystem
        FS->>FS: stat fullPath size and existence check
        FS->>P: createWorkspaceFileUrl(fullPath)
        P-->>FS: app token URL with 30s TTL
        FS-->>IPC: success true fileUrl mimeType size
    else SSH filesystem
        FS->>FS: sftp.open and fstat
        FS->>FS: readSftpFully loop handles partial reads
        FS-->>IPC: success true dataUrl base64 mimeType size
    end

    IPC-->>R: result
    R->>OBJ: data set to fileUrl or dataUrl
    OBJ-->>R: PDF rendered by Chromium built-in viewer

    Note over R,P: Diff view disk group uses React Query staleTime 25s to trigger background refetch before 30s TTL expires
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
src/main/core/fs/impl/local-fs.ts:802-835
**Missing error handler on chokidar watcher**

`chokidar`'s `FSWatcher` extends Node.js's `EventEmitter`. If it emits an `'error'` event — which it does for conditions like permission-denied, inotify limit exceeded, or the watched path being deleted while watching — and no `'error'` listener is attached, Node.js immediately re-throws the error as an uncaught exception. In Electron's main process, this would crash the process.

The same gap exists in the `fs.watch` path below (line 838): `FSWatcher` is also an `EventEmitter` that emits `'error'` for similar conditions (e.g., `ENOENT` when the directory disappears). The previous `parcel/watcher` used a callback pattern where errors were delivered to the subscriber callback; that contract is not preserved with the new event-based watchers, so adding an `'error'` handler (e.g., `watcher.on('error', (err) => log.error('watcher error', err))`) is required for both branches.

### Issue 2 of 2
src/renderer/features/tasks/diff-view/main-panel/diff-file-renderer.tsx:313-317
**"Too large" errors incorrectly map to the generic `'git-error'` reason**

When `rpc.fs.readPdf` returns `{ success: false, error: 'PDF too large: ...' }`, this branch falls through the `/not found/i` check and returns `{ status: 'unavailable', reason: 'git-error' }`. `pdfUnavailableMessage` then renders `'Preview unavailable'` rather than `'Preview unavailable — PDF is too large'`. Adding a `'too-large'` check here lets the correct message surface to the user.

```suggestion
  if (!pdf?.success) {
    const error = pdf?.error ?? '';
    if (/not found/i.test(error)) return { status: 'missing' };
    if (/too large/i.test(error)) return { status: 'unavailable', reason: 'too-large' };
    return { status: 'unavailable', reason: 'git-error' };
  }
```

Reviews (4): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile

Comment thread src/main/app/protocol.ts
Comment thread src/main/core/fs/impl/local-fs.ts Outdated
Comment thread src/main/core/fs/impl/local-fs.ts
Comment thread src/main/core/fs/impl/ssh-fs.ts Outdated
@janburzinski

Copy link
Copy Markdown
Collaborator Author

@greptileai

@janburzinski

Copy link
Copy Markdown
Collaborator Author

@greptileai

@arnestrickmann

Copy link
Copy Markdown
Contributor

would be cool if you could also annotate the content:) just a thought.

Will review soon!

@janburzinski

Copy link
Copy Markdown
Collaborator Author

like annotating as in like sending that also to an agent?

@arnestrickmann

Copy link
Copy Markdown
Contributor

yes

@janburzinski

Copy link
Copy Markdown
Collaborator Author

should i do this before merge? yea or?

@janburzinski

Copy link
Copy Markdown
Collaborator Author

@greptileai

# Conflicts:
#	src/renderer/features/tasks/diff-view/main-panel/diff-file-renderer.tsx
#	src/renderer/features/tasks/tabs/file-tab-store.ts
@janburzinski

Copy link
Copy Markdown
Collaborator Author

demo:
image

@arnestrickmann

Copy link
Copy Markdown
Contributor

unf. outdated - @janburzinski
closing this for now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants