Skip to content

Files tab v2 and Cursor chat reliability#449

Merged
arul28 merged 5 commits into
mainfrom
ade/files-tab-fix-034f07d3
May 31, 2026
Merged

Files tab v2 and Cursor chat reliability#449
arul28 merged 5 commits into
mainfrom
ade/files-tab-fix-034f07d3

Conversation

@arul28
Copy link
Copy Markdown
Owner

@arul28 arul28 commented May 30, 2026

Summary

  • Add the flagged Files v2 workbench and expand file service/preload contracts for paginated trees, Git decorations, range reads, blame, and viewer support.
  • Harden Cursor chat/model runtime behavior while preserving direct cursor-agent launches and busy-turn model switch safety.
  • Capture Files/Cursor invariants in docs so this fragile path stays protected.

Validation

  • npm --prefix apps/desktop run typecheck
  • npm --prefix apps/desktop run lint (warnings only)
  • npm --prefix apps/desktop run build
  • npm run test:desktop:sharded
  • npm --prefix apps/ade-cli run typecheck
  • npm --prefix apps/ade-cli run test
  • npm --prefix apps/ade-cli run build
  • node scripts/validate-docs.mjs

ADE   Open in ADE  ·  ade/files-tab-fix-034f07d3 branch  ·  PR #449

Greptile Summary

This PR adds the Files v2 workbench (Monaco-backed editor groups, PDF/CSV/image/markdown viewers, paginated tree loading, git blame, range reads) and hardens Cursor chat/model runtime behaviour (deferred model-switch teardown during live turns, live SDK-policy sync, direct cursor-agent launch replacing the create-chat / --resume shell dance).

  • Files v2: Introduces a full editor surface with split groups, a Monaco CodeViewer, virtualized LargeTextViewer/CsvViewer, a PdfViewer using pdfjs-dist + streamFileBytes, and new back-end APIs (readFileRange, listTreeChildren, refreshGitDecorations, blame).
  • Cursor reliability: Model switches during a live turn are now deferred via pendingModelSwitchReset and processed on idle; syncLiveCursorSdkPolicy propagates mode/permission changes to a live SDK instance; CLI launches are simplified to direct cursor-agent invocation.

Confidence Score: 4/5

Safe to merge with one fix recommended: the CsvViewer streaming loop should be capped before merging to avoid OOM crashes when opening large CSVs.

The Cursor runtime hardening and file-service API additions are solid. The only active defect introduced is in the new CsvViewer: it accumulates all streamed chunks via O(n²) string concatenation with no byte cap, while the analogous LargeTextViewer was deliberately given a 25 MB ceiling and an array-based accumulator. A large CSV will exhaust renderer memory and crash the tab before the fix is in place.

apps/desktop/src/renderer/components/files/v2/viewers/CsvViewer.tsx — the streaming accumulation loop needs the same MAX_STREAM_BYTES cap and parts-array pattern used by LargeTextViewer.

Important Files Changed

Filename Overview
apps/desktop/src/renderer/components/files/v2/viewers/CsvViewer.tsx New file. Streams CSV content via readFileRange using O(n²) string concatenation and has no MAX_STREAM_BYTES cap, which could OOM the renderer for large files.
apps/desktop/src/main/services/files/fileService.ts Adds readFileRange, listTreeChildren, refreshGitDecorations, blame APIs. PDF binary detection fixed (BASE64_RANGE_EXTENSIONS). completeUtf8ByteLength boundary logic looks correct.
apps/desktop/src/main/services/chat/agentChatService.ts Defers Cursor model-switch teardown during busy turns via pendingModelSwitchReset; adds syncLiveCursorSdkPolicy; wraps preview update in try/catch. Logic looks sound.
apps/desktop/src/renderer/components/files/v2/streamBytes.ts New streaming utility. Now correctly branches on page.encoding before calling atob — fixes the previously flagged PDF streaming bug.
apps/desktop/src/renderer/components/files/v2/viewers/LargeTextViewer.tsx New file. Uses parts array + join() to avoid O(n²) concat. Has MAX_STREAM_BYTES=25MB cap with user-visible warning. Virtualizer-backed rendering.
apps/desktop/src/renderer/components/files/v2/viewers/PdfViewer.tsx New PDF viewer using pdfjs-dist + streamFileBytes. Has 25MB cap. Cancel-safe lifecycle. PDF binary path fixed on the server side.
apps/desktop/src/shared/cliLaunch.ts Simplifies cursor-agent launch to direct invocation (removes create-chat/--resume shell dance); removes --trust from full-auto flags. Intentional hardening.
apps/desktop/src/main/services/chat/cursorModelsDiscovery.ts Large refactor. Adds SDK model discovery via @cursor/sdk + official API, variant folding, per-key caching with TTL. Logic is thorough and well-tested.
apps/desktop/src/shared/modelRegistry.ts Adds resolveCursorCliModelVariant, cursorCliVariants support, sortCursorCliDescriptorsForPicker, Droid model display helpers. No obvious logic issues.

Sequence Diagram

sequenceDiagram
    participant R as Renderer
    participant P as Preload (IPC)
    participant FS as FileService (Main)
    participant Git as Git Process

    Note over R,FS: Large file / PDF open
    R->>P: "files.readFile({path})"
    P->>FS: readFile
    FS-->>P: "{content: firstChunk, isPartial:true, nextOffset:N}"
    P-->>R: FileContent (partial)

    loop Streaming chunks (readFileRange)
        R->>P: "files.readFileRange({offset:N, length:512KB})"
        P->>FS: readFileRange
        FS-->>P: "{encoding:base64|utf-8, content, nextOffset}"
        P-->>R: FilesReadFileRangeResult
    end

    Note over R,FS: Paginated directory load
    R->>P: "files.listTreeChildren({parentPath, offset})"
    P->>FS: listTreeChildren
    FS->>FS: collectPagedVisibleChildEntries (2s cache)
    FS-->>P: "{children[], nextOffset}"
    P-->>R: FilesListTreeChildrenResult

    Note over R,FS: Git decorations (separate from tree load)
    R->>P: "files.refreshGitDecorations({workspaceId})"
    P->>FS: refreshGitDecorations
    FS->>Git: git status
    Git-->>FS: changed files
    FS-->>P: "FilesGitStatusEvent {files[], directories[]}"
    P-->>R: GitStatusEvent
Loading

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
apps/desktop/src/renderer/components/files/v2/viewers/CsvViewer.tsx:31-40
`LargeTextViewer` was fixed to collect chunks into an array and join once to avoid O(n²) string re-allocation. `CsvViewer` uses the old `text += page.content` pattern, so every append copies the entire accumulated string. For a 50 MB CSV streamed in 512 KB chunks (~100 iterations), the last append alone copies 50 MB and the total allocation is ~2.5 GB. Additionally, there is no `MAX_STREAM_BYTES` cap — the only guard is `guard > 10_000`, which allows ~5 GB before stopping. `LargeTextViewer` uses a 25 MB cap and tells the user when content is truncated; the same pattern should be applied here.

```suggestion
      const MAX_CSV_STREAM_BYTES = 25 * 1024 * 1024;
      const parts: string[] = [content.content];
      let next: number | null = content.isPartial ? content.nextOffset ?? null : null;
      let guard = 0;
      while (next != null) {
        const remainingBytes = MAX_CSV_STREAM_BYTES - next;
        if (remainingBytes <= 0) {
          setTruncated(true);
          break;
        }
        const page = await window.ade.files.readFileRange({ workspaceId, path: tab.path, offset: next, length: Math.min(512 * 1024, remainingBytes) });
        if (cancelled) return;
        parts.push(page.content);
        next = page.nextOffset;
        if (++guard > 10_000) break;
      }
      const text = parts.join("");
```

Reviews (2): Last reviewed commit: "fix: address files streaming review" | Re-trigger Greptile

@vercel
Copy link
Copy Markdown

vercel Bot commented May 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
ade Ignored Ignored Preview May 30, 2026 11:49pm

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 30, 2026

@copilot review but do not make fixes

@capy-ai
Copy link
Copy Markdown

capy-ai Bot commented May 30, 2026

Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 30, 2026

Warning

Review limit reached

@arul28, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 24 minutes and 2 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6d602403-a939-4b74-ab8e-6155cbdb2786

📥 Commits

Reviewing files that changed from the base of the PR and between d316c02 and e105124.

⛔ Files ignored due to path filters (6)
  • apps/desktop/package-lock.json is excluded by !**/package-lock.json, !**/package-lock.json
  • docs/ARCHITECTURE.md is excluded by !docs/**
  • docs/features/chat/README.md is excluded by !docs/**
  • docs/features/files-and-editor/README.md is excluded by !docs/**
  • docs/features/files-and-editor/editor-surfaces.md is excluded by !docs/**
  • docs/features/files-and-editor/file-watcher-and-trust.md is excluded by !docs/**
📒 Files selected for processing (79)
  • apps/ade-cli/src/adeRpcServer.test.ts
  • apps/ade-cli/src/services/sync/syncHostService.ts
  • apps/ade-cli/tsup.config.ts
  • apps/desktop/package.json
  • apps/desktop/src/main/services/adeActions/registry.ts
  • apps/desktop/src/main/services/ai/tools/ctoOperatorTools.ts
  • apps/desktop/src/main/services/chat/agentChatService.test.ts
  • apps/desktop/src/main/services/chat/agentChatService.ts
  • apps/desktop/src/main/services/chat/cursorModelsDiscovery.test.ts
  • apps/desktop/src/main/services/chat/cursorModelsDiscovery.ts
  • apps/desktop/src/main/services/chat/cursorSdkPool.ts
  • apps/desktop/src/main/services/chat/cursorSdkProtocol.ts
  • apps/desktop/src/main/services/chat/cursorSdkWorker.ts
  • apps/desktop/src/main/services/files/fileService.test.ts
  • apps/desktop/src/main/services/files/fileService.ts
  • apps/desktop/src/main/services/ipc/registerIpc.ts
  • apps/desktop/src/main/services/localRuntime/localRuntimeConnectionPool.ts
  • apps/desktop/src/main/services/pty/ptyService.test.ts
  • apps/desktop/src/main/services/pty/ptyService.ts
  • apps/desktop/src/main/services/sync/syncRemoteCommandService.test.ts
  • apps/desktop/src/main/utils/terminalSessionSignals.test.ts
  • apps/desktop/src/main/utils/terminalSessionSignals.ts
  • apps/desktop/src/preload/global.d.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/browserMock.ts
  • apps/desktop/src/renderer/components/app/App.tsx
  • apps/desktop/src/renderer/components/app/App.workKeepAlive.test.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatPane.test.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatPane.tsx
  • apps/desktop/src/renderer/components/files/FilesExplorer.tsx
  • apps/desktop/src/renderer/components/files/FilesPage.test.tsx
  • apps/desktop/src/renderer/components/files/FilesPage.tsx
  • apps/desktop/src/renderer/components/files/FilesTab.tsx
  • apps/desktop/src/renderer/components/files/filePresentation.tsx
  • apps/desktop/src/renderer/components/files/filesWorkbenchFlag.ts
  • apps/desktop/src/renderer/components/files/monacoModelRegistry.test.ts
  • apps/desktop/src/renderer/components/files/monacoModelRegistry.ts
  • apps/desktop/src/renderer/components/files/treeHelpers.ts
  • apps/desktop/src/renderer/components/files/v2/ContextMenu.tsx
  • apps/desktop/src/renderer/components/files/v2/EditorGroup.tsx
  • apps/desktop/src/renderer/components/files/v2/EditorGroups.tsx
  • apps/desktop/src/renderer/components/files/v2/FilesWorkbench.tsx
  • apps/desktop/src/renderer/components/files/v2/StatusBar.tsx
  • apps/desktop/src/renderer/components/files/v2/ViewerHost.tsx
  • apps/desktop/src/renderer/components/files/v2/WarmEmptyState.tsx
  • apps/desktop/src/renderer/components/files/v2/WorkspacePicker.tsx
  • apps/desktop/src/renderer/components/files/v2/editorGroupsStore.test.ts
  • apps/desktop/src/renderer/components/files/v2/editorGroupsStore.ts
  • apps/desktop/src/renderer/components/files/v2/monacoLoader.ts
  • apps/desktop/src/renderer/components/files/v2/overlays.tsx
  • apps/desktop/src/renderer/components/files/v2/pendingReveals.ts
  • apps/desktop/src/renderer/components/files/v2/recentFiles.ts
  • apps/desktop/src/renderer/components/files/v2/streamBytes.test.ts
  • apps/desktop/src/renderer/components/files/v2/streamBytes.ts
  • apps/desktop/src/renderer/components/files/v2/useFileContent.ts
  • apps/desktop/src/renderer/components/files/v2/viewerRegistry.test.ts
  • apps/desktop/src/renderer/components/files/v2/viewerRegistry.ts
  • apps/desktop/src/renderer/components/files/v2/viewers/BinaryViewer.tsx
  • apps/desktop/src/renderer/components/files/v2/viewers/CodeViewer.tsx
  • apps/desktop/src/renderer/components/files/v2/viewers/CsvViewer.tsx
  • apps/desktop/src/renderer/components/files/v2/viewers/DiffViewer.tsx
  • apps/desktop/src/renderer/components/files/v2/viewers/ImageViewer.tsx
  • apps/desktop/src/renderer/components/files/v2/viewers/LargeTextViewer.tsx
  • apps/desktop/src/renderer/components/files/v2/viewers/MarkdownViewer.tsx
  • apps/desktop/src/renderer/components/files/v2/viewers/PdfViewer.tsx
  • apps/desktop/src/renderer/components/files/v2/viewers/pdfLoader.ts
  • apps/desktop/src/renderer/components/files/v2/viewers/types.ts
  • apps/desktop/src/renderer/components/shared/ModelPicker/ModelListRow.tsx
  • apps/desktop/src/renderer/components/shared/ModelPicker/ModelPicker.test.tsx
  • apps/desktop/src/renderer/components/shared/ModelPicker/ModelPickerContent.tsx
  • apps/desktop/src/renderer/components/shared/ModelPicker/modelCatalog.ts
  • apps/desktop/src/renderer/components/terminals/WorkSidebar.tsx
  • apps/desktop/src/renderer/components/terminals/cliLaunch.test.ts
  • apps/desktop/src/shared/cliLaunch.ts
  • apps/desktop/src/shared/ipc.ts
  • apps/desktop/src/shared/modelRegistry.test.ts
  • apps/desktop/src/shared/modelRegistry.ts
  • apps/desktop/src/shared/types/chat.ts
  • apps/desktop/src/shared/types/files.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ade/files-tab-fix-034f07d3

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review

Scope: 84 file(s), +6665 / −451
Verdict: Minor issues

This PR adds a v2 Files workbench (editor groups, specialized viewers, streaming reads, paginated tree loading) and hardens Cursor chat/CLI model handling (variant folding, deferred runtime reset on model switch, direct cursor-agent launch). The backend path-safety and range-read caps look sound; the main gaps are incomplete tree pagination UX and a few renderer memory/perf edges on very large assets.


🐛 Functionality

[Medium] Tree expansion stops at 10k entries with no way to load the rest

File: apps/desktop/src/renderer/components/files/v2/FilesWorkbench.tsx:L217-L223
Issue: When expanding a directory, the loader pages through listTreeChildren until it hits MAX_AUTO_LOADED_CHILDREN (10,000), then stores loadMoreOffset on the node and sets childrenTruncated. Nothing in the renderer reads those fields — FilesExplorer has no "Load more" row or handler — so entries beyond the cap are unreachable.
Repro: Open a workspace with a directory that has >10,000 visible children (e.g. a large node_modules with ignored files off), expand it in v2 (or v1 Files, which shares the same explorer).
Fix: In FilesExplorer, render a trailing row when node.childrenTruncated / node.loadMoreOffset != null, and on click call listTreeChildren with offset: loadMoreOffset, append results, and update loadMoreOffset until null.


⚡ Performance

[Medium] listTreeChildren re-reads the full directory on every page

File: apps/desktop/src/main/services/files/fileService.ts:L636-L652 and L790-L799
Issue: Each paginated request calls collectVisibleChildEntries, which readdirs the entire parent, filters/sorts all entries, then slices [offset, offset+limit). Expanding a 20k-entry folder issues ~5 full scans; a 100k-entry folder is worse.
Impact: Multi-second main-process stalls when expanding very large directories; scales with directory size × page count, not page size alone.
Fix: Cache the filtered/sorted visible entry list per (workspaceId, parentPath, includeIgnored) with invalidation on watcher events, or stream from a single readdir and yield pages without rescanning.

[Medium] PDF viewer loads the entire file into renderer memory

File: apps/desktop/src/renderer/components/files/v2/streamBytes.ts:L8-L37 and apps/desktop/src/renderer/components/files/v2/viewers/PdfViewer.tsx:L27-L32
Issue: PdfViewer calls streamFileBytes, which concatenates every readFileRange chunk into one Uint8Array with no byte cap. Large PDFs bypass the inline readFile binary limit because the viewer streams via range reads.
Impact: Opening a multi-hundred-MB PDF can allocate the full file in the renderer and freeze or crash the window.
Fix: Enforce a max PDF size (similar to LargeTextViewer's MAX_STREAM_BYTES), or use pdf.js range/stream transport without holding the whole file in memory.


🎨 UI/UX

[Low] Failed directory expansion shows an empty folder with no error

File: apps/desktop/src/renderer/components/files/v2/FilesWorkbench.tsx:L224-L225
Issue: loadDirectory catches all errors and ignores them (/* ignore — directory may have been removed */), so permission failures, missing workspace, or IPC timeouts look like an empty directory.
Fix: Surface the error via setError (or a per-node error flag) when the catch is not a benign race (e.g. workspace still matches and path still exists).


Notes

Positive patterns worth keeping: ensureSafePath on all new file APIs; UTF-8-safe readFileRange trimming; Monaco model registry reuse; Cursor model-switch deferral via pendingModelSwitchReset; sanitized markdown preview via existing PlanMarkdown.

Could not run the full desktop test suite in this environment; findings are from static review of the diff and head commit cf95162.

Open in Web View Automation 

Sent by Cursor Automation: BUGBOT in Versic

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 30, 2026

@copilot review but do not make fixes

Comment thread apps/desktop/src/main/services/files/fileService.ts Outdated
Comment thread apps/desktop/src/renderer/components/files/v2/streamBytes.ts Outdated
Comment thread apps/desktop/src/renderer/components/files/v2/viewers/LargeTextViewer.tsx Outdated
@arul28 arul28 merged commit 986203e into main May 31, 2026
46 of 50 checks passed
@arul28 arul28 deleted the ade/files-tab-fix-034f07d3 branch May 31, 2026 08:37
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.

1 participant