Repository navigation
feat(files): read-only Files card, reader, and rich viewer - #125
Conversation
Adds the Files canvas card: a read-only browser and reader rooted either at a live terminal session working directory or at a folder picked in a native dialog. Main owns access through a new FileAccessService that canonicalizes root and target with realpath, rejects absolute paths, `..`, escaping symlinks and non-regular files, and enforces byte, entry, depth and result caps; the renderer only receives an opaque root id plus relative paths over the allow-listed `files:*` channels. Cards persist and restore their root, bounds, open file and expanded folders, and show an unavailable state when a root is gone. Text and code are rendered richly: Markdown files render as GitHub-Flavored Markdown (headings, lists, task lists, tables, blockquotes, rules, inline and fenced code) and recognized code files are syntax-highlighted by extension. Rendering is sanitized (no raw HTML, event handlers or dangerous schemes; no innerHTML), http(s) links open in the built-in browser, only embedded `data:` images render, and files above the ~250 KB threshold fall back to plain text with a notice. The heavy Markdown/highlight renderers and the card are lazy-loaded to keep them out of the startup bundle. Excluded: the unrelated custom-agents WIP is intentionally not part of this branch. Also raises the renderer static-graph budget in tests/renderer-bundle-splitting.test.mjs (still well under the old ~1.6 MB single chunk) to account for the new card's startup orchestration.
`FileAccessService` registered roots with `realpathSync` but canonicalized targets with the async `realpath`, which on Windows can return a differently cased or 8.3-shortened path. The prefix containment check then treated the root itself (and every child) as outside the root, so listing/reading failed. Resolve targets with the same `realpathSync` used at registration and compare normalized (case-insensitive, `\\?\`-stripped) paths on Windows. Windows CI: tests/file-access-service.test.mjs and files-ipc-security.test.mjs were the 13 failing tests; they now pass on Linux and should on Windows.
|
Reviewed head P2 — Bind every asynchronous completion to the current card/root generation
Example: start a slow listing/search in folder A, switch the same card to folder B, let B finish first, then let A finish. A's tree or search results can overwrite B's runtime state. Clicking an old result subsequently resolves that relative path against B, giving an error or opening a different same-named file. A late listing can also recreate runtime state after a card was closed. Please invalidate outstanding operations when the root changes or the card closes, and check card existence plus the current root/generation before applying any asynchronous success or error. Apply the guard consistently to initial listings, child listings, quick-open searches, and restore operations; the file-read sequence alone does not cover these paths. Regression coverage needed with deferred responses:
P2 — Bound traversal work, not just the number of matching resultsIn Directory listing has a related issue: Please add a bounded traversal-work budget with an explicit incomplete/truncated result, and avoid accumulating obsolete searches when the user types quickly. Debouncing is useful, but service-side bounds are still needed for a single large no-match search. Ensure the listing cap also bounds metadata work. If a search is truncated, retain that information through the renderer rather than silently presenting an incomplete result as exhaustive. Regression coverage needed: a wide tree with no matches should stop at the work budget; a burst of queries should not leave all obsolete traversals running; large directory listings should perform bounded metadata work. Preserve the existing containment, symlink, and read-size checks. Integration note#123 adds individual material cards, while this PR adds a folder browser/reader; both can be useful. Their current heads have merge conflicts in 10 files, including the canvas, minimap, These findings are from source review and the existing CI results; I have not run new local tests or a live UI reproduction. |
|
@howdeploy Thank you for the detailed review. We are working through the two P2 findings and the requested regression coverage. The merge conflicts with |
This integrates the six contributions accepted during the maintainer's DEV review, together with the maintainer's local UI and minimap fixes. Rejected orchestration features, including #133, are excluded entirely. ## Included contributions and credit - #125 by @tab11pm: read-only file cards, directory browsing, and richer Markdown/code previews. - #130 by @SaneSanders: image/material remarks and handoff to an existing agent session. - #131 by @BIackFIame: macOS clipboard/edit context menus for text fields. - #138 by @pepechka-kun: usage history, measured quota changes, local-log evidence, and explicit attribution uncertainty. - #139 by @CLOSETTTY: interactive mascots and the Codex mascot creation workflow. - #140 by @teo-nex: the macOS node-pty file-descriptor leak fix, patch delivery, and resource diagnostics. Thank you to all six contributors. Each original PR head and its author commits are retained in Git history through separate merge commits. This integration must use a merge commit rather than squash or rebase. ## Maintainer patches - Fit the minimap overview to the camera viewport and all canvas objects together, with padding; retain stable drag coordinates during a gesture. - Keep the material handoff icon fixed in the header, remove the count beneath it, and place equally sized Cancel/Send actions at the bottom right of the handoff dialog. - Split usage history into five left-side sections: conditional estimate, quotas, agents, local-log tokens, and source state. Use pages within sections to fit the window without a long scrolling report. - Resolve shared App/IPC/preload integrations while retaining the file, handoff, and usage-history flows. - Retain the approved DEV dependency set and Electron 43.7.7; reuse the matching lockfile with patch-package 8.0.1. ## Scope #132, #133, #134, #135, and #137 are not included. #136 is also excluded pending the previously requested UI/UX redesign. No commits from that cumulative orchestration series are ancestors of this branch. Material handoff in #130 does not automatically apply a saved JSON agent report to the material state; this remains the reviewed behavior. ## Validation - `VITE_BUILD_CHANNEL=DEV npm run build` passed, including the script's TypeScript checks and native-helper build. - `git diff --check` passed. - Verified that all six accepted PR heads are ancestors of the integration and all six excluded PR heads are not. - Verified that the three approved local UI/minimap implementations match the selected DEV copy. - Added minimap geometry coverage for distant camera positions and adapted usage-history fixtures for section navigation. - The maintainer manually reviewed the accepted features in the earlier combined DEV build. The final integration without #133 has not received another manual UI pass. Automated tests and macOS/Windows runtime checks were not run locally for this integration.
Summary
Adds a read-only Files canvas card with a browser and reader, plus rich rendering for Markdown and code.
rootIdand a label; every listing/read/search pairs it with a relative path.FileAccessService(main) canonicalizes root and target withrealpath, rejects absolute paths,.., escaping symlinks and non-regular files, re-checks containment around the open, and caps bytes, entries, search depth and results. New allow-listedfiles:*IPC channels are guarded by the trusted-main-renderer check and exposed through the typed preloadfilescapability.innerHTML, dangerous schemes neutralized),http(s)links open in the built-in browser, only embeddeddata:images render, and files above the ~250 KB threshold fall back to plain text with a notice.Notes
tests/renderer-bundle-splitting.test.mjsstatic-graph budget is raised from 1.50 MB to 1.55 MB (still well under the old ~1.6 MB single chunk) because the card adds startup orchestration toApp.tsx.openspec/is not included: upstream does not track it.Verification
npm test— 1809 pass, 0 fail (29 skipped)npm run typecheck— both projects passnpm run build— succeeds