Skip to content

feat: add right-click context menu to the project explorer (#455) - #465

Open
musmanghani13 wants to merge 1 commit into
petertzy:mainfrom
musmanghani13:feat/455-explorer-context-menu
Open

musmanghani13 wants to merge 1 commit into
petertzy:mainfrom
musmanghani13:feat/455-explorer-context-menu

Conversation

@musmanghani13

Copy link
Copy Markdown

Closes #455

Summary

Adds a VS Code-style right-click context menu to the project explorer, so common file operations no longer require leaving the app for File Explorer / Finder.

Right-click on Menu
File Open · Reveal in File Explorer / Finder · Cut · Copy · Paste · Copy Path · Copy Relative Path · Rename… · Delete
Folder New File… · New Folder… · Reveal · Cut · Copy · Paste · Copy Path · Copy Relative Path · Rename… · Delete
Empty space (project root) New File… · New Folder… · Reveal · Paste · Copy Path · Copy Relative Path

"Reveal" only appears in the desktop app, and its label follows the platform (Finder / File Explorer / Open Containing Folder).

Why

Until now the explorer could only open files. Creating, renaming, moving or deleting anything meant switching to the system file manager and then refreshing. This brings the most-used explorer actions into the app, with behaviour modelled on VS Code.

What changed

Backend (backend/routers/files.py)

  • New endpoints: POST /api/files/create, /rename, /delete, /copy, /move.
  • Safety: every endpoint resolves paths and refuses anything outside the open project folder (including .. segments and symlinks) with 403. The project folder itself cannot be renamed, deleted, cut or copied. Names that are really paths (../x, a/b) or contain characters Windows forbids are rejected with 400.
  • No silent data loss: create, rename and move never overwrite an existing entry (409). Delete moves the entry to the Recycle Bin / Trash via send2trash, so it can be restored.
  • Copy onto an existing name picks a copy.md, a copy 2.md, … like VS Code; folders are copied with their contents (symlinks are copied as links).
  • New dependency: send2trash>=1.8 (pyproject.toml, uv.lock), added to the PyInstaller hiddenimports in markdown-reader-backend.spec because it picks its platform module at runtime.

Frontend

  • ProjectExplorer.tsx: the context menu, a small dialog for New File / New Folder / Rename / Delete, and the cut/copy clipboard. Expanded folders are now kept in shared state so they stay open when a folder reloads, is renamed or is moved.
  • explorer-paths.mjs (new): dependency-free path and tab helpers (relative paths, joining, remapping tabs after rename/move, closing tabs after delete), kept as .mjs so node --test can import them like tabLifecycle.mjs.
  • useEditor.ts: handlePathRenamed / handlePathDeleted, so open tabs follow renamed and moved files (including unsaved edits and converted documents). Deleting a file closes its tab if it has no unsaved changes; tabs with unsaved changes stay open but are detached, so Save asks for a new location instead of recreating the deleted file.
  • api.ts: Files.create / rename / delete / copy / move.
  • Menu UX: one highlight shared by mouse and arrow keys, Enter / Escape, closes on outside click / blur / resize, stays on screen near window edges, Paste is disabled until something is cut or copied, the cut item is dimmed until pasted. Highlight colours match the existing menu bar.

How I validated it

Automated

  • Backend: 13 new tests in tests/test_files_endpoints.py (create, rename, delete, copy names, move, overwrite refusal, pasting a folder into itself, and paths outside the project). All 18 tests in the file pass; ruff check and ruff format --check pass on the changed files.
  • Frontend: 8 new tests in tests/explorer-paths.test.mjs covering Windows and POSIX paths. npm test (170/170), npm run lint, tsc --noEmit and npm run build all pass.
  • Full backend suite on Windows 11: 613 passed, 13 failed. The same 13 tests fail on main (I ran both and compared the lists); they are in image paths, renderer, export, knowledge and translation and are unrelated to this change. They should pass on Linux CI.

Manual (Windows 11)

  • Desktop app (Tauri dev): context menus on files, folders and empty space, and the menu actions.
  • Browser (npm run dev): rename with an open tab, new file opens automatically, deleting a folder closes its tabs, Cut + Paste moves a file and a later Save writes to the new location, Copy + Paste creates docs copy with its contents, and pasting a folder into itself shows an error.

Not verified

  • macOS and Linux were not tested by hand (I only have Windows). The code handles both path separators and the backend tests are platform-neutral, but it would be great if someone could try Reveal in Finder, Delete → Trash and Copy Path on a Mac / Linux desktop build. Copy Path uses navigator.clipboard, which I could not check in WebKitGTK.
  • No keyboard shortcuts (Ctrl+X/C/V, F2, Delete) yet; the explorer has no notion of a selected row. Happy to follow up in a separate issue, along with drag-and-drop.

Screenshots

File context menu

file_menu

Rename dialog

rename

Delete dialog

delete

Folder context menu

folder_menu

)

Right-clicking a file, folder or empty space in the project explorer now
opens a VS Code-style context menu.

- Files: Open, Reveal in File Explorer/Finder, Cut, Copy, Paste,
  Copy Path, Copy Relative Path, Rename, Delete
- Folders: New File, New Folder, plus the same actions
- Empty space acts on the project folder: New File, New Folder, Paste,
  Copy Path (no Rename/Delete/Cut/Copy for the root)

Backend: new /api/files/create, /rename, /delete, /copy and /move
endpoints. All of them refuse paths outside the open project (including
".." and symlinks), never touch the project folder itself, and never
overwrite an existing entry. Delete moves the entry to the Recycle Bin /
Trash via send2trash (new dependency, added to the PyInstaller spec).
Copying onto an existing name picks "a copy.md", "a copy 2.md", ...

Frontend: open tabs follow renamed and moved files; deleted files close
their clean tabs and detach unsaved ones so Save cannot recreate them.
The menu supports arrow keys, Enter and Escape, and keeps on screen.

Tests: 13 backend endpoint tests and 8 frontend path/tab tests.

@petertzy petertzy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for implementing the explorer context menu for #455. The overall feature coverage is strong, including file/folder-specific actions, keyboard navigation, tab path updates, and tests. However, I cannot recommend merging yet.

Blocking: filesystem safety and overwrite guarantees

  1. In backend/routers/files.py, create_entry, _unique_copy_target, and move_entry rely on Path.exists() to determine whether a destination is free. exists() returns false for a dangling symlink, so these checks do not consistently recognize an occupied directory entry. Please use a lexists-style check (os.path.lexists) and add regression tests for dangling symlinks.
  2. rename_entry checks for an existing destination and then calls Path.rename(). On POSIX, rename can replace an entry created between the check and the operation. Similarly, shutil.move() can overwrite a destination under concurrent filesystem changes. The PR promises that rename and move never overwrite existing entries; please either enforce this atomically where supported or explicitly address and test the race.
  3. Please verify and document how symlink entries are handled: _resolve_in_project() resolves the source path, so operations on a symlink can act on its target instead of the link itself, and links pointing outside the project are rejected. This should be intentional and covered by tests, especially for Delete, Rename, and Copy.

Validation needed before approval

  • Bring the latest main into the PR head and resolve any merge conflicts.
  • Re-run backend tests and frontend tests, lint, type checking, and build against the updated branch.
  • Verify desktop Reveal, Trash, and clipboard behavior on macOS/Linux when possible.

I have not independently executed the test suite or completed the upstream merge, so this review is based on source inspection rather than a verified green build. Once the blocking filesystem cases are addressed and CI passes, I would be happy to reassess.

This branch has not been deployed

No deployments
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.

Feature Request: Add Right-Click Context Menu for Files and Folders in the Project Explorer

2 participants