feat: add in-browser text file editor modal#87
Conversation
WalkthroughAdds an in-browser text editor for encrypted text files: new Edit context-menu action, a TextEditorDialog modal that loads/decrypts/edits/re-encrypts and uploads content to IPFS, new folder/service APIs to replace file metadata, modal styling, and end-to-end tests for the editor flow. Changes
Sequence DiagramsequenceDiagram
participant User
participant Dialog as TextEditorDialog
participant Auth as Auth/Keypair
participant IPFS
participant FolderHook as useFolder
participant FolderSvc as FolderService
User->>Dialog: Open editor for text file
Dialog->>Auth: derive keypair / request download
Auth->>IPFS: fetch encrypted blob
IPFS-->>Auth: encrypted blob
Auth-->>Dialog: decrypted plaintext (display)
User->>Dialog: Edit content and Save
Dialog->>Auth: encrypt modified content
Auth-->>Dialog: encrypted blob
Dialog->>IPFS: upload encrypted blob
IPFS-->>Dialog: new CID
Dialog->>FolderHook: updateFile(parentId, {fileId, newCid, newFileKeyEncrypted, newFileIv, newSize})
FolderHook->>FolderSvc: replaceFileInFolder(params)
FolderSvc-->>FolderHook: {newSequenceNumber, oldCid}
FolderHook->>IPFS: unpin(oldCid) (async)
FolderHook-->>Dialog: save complete
Dialog->>User: close
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
No actionable comments were generated in the recent review. 🎉 🧹 Recent nitpick comments
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@apps/web/src/components/file-browser/FileBrowser.tsx`:
- Around line 36-88: The isTextFile logic misses files named like
Dockerfile/Makefile because TEXT_EXTENSIONS only contains dot-prefixed entries;
update the check in isTextFile (or the set) so bare filenames are recognized:
either add dot-less entries ("dockerfile","makefile", "gitignore",
"editorconfig", etc.) to TEXT_EXTENSIONS, or change isTextFile to first test
TEXT_EXTENSIONS.has(lower) (full name) and then fallback to the existing
extension logic (compute lastDot and test
TEXT_EXTENSIONS.has(lower.slice(lastDot))). Reference TEXT_EXTENSIONS and
isTextFile when making the change.
In `@apps/web/src/components/file-browser/TextEditorDialog.tsx`:
- Line 112: The blob creation uses encrypted.ciphertext.buffer which may include
the whole underlying ArrayBuffer and corrupt data; update the Blob construction
in TextEditorDialog (the code around encrypted.ciphertext usage) to pass the
Uint8Array view directly (i.e., pass encrypted.ciphertext) or, if you must use
the underlying buffer, slice it using
encrypted.ciphertext.buffer.slice(encrypted.ciphertext.byteOffset,
encrypted.ciphertext.byteOffset + encrypted.ciphertext.byteLength) so the Blob
contains only the exact ciphertext bytes.
- Around line 173-178: The JSX currently contains a literal "// {lineCount} ..."
which renders as visible text and is misinterpreted as a JS comment; in the
TextEditorDialog component update the span to build that status string as a JSX
expression instead of a bare comment-like token (e.g., replace the raw "//
{lineCount} ..." with a string expression that includes lineCount and
pluralization like {`// ${lineCount} ${lineCount === 1 ? 'line' : 'lines'} |
utf-8`} and keep the isDirty suffix logic intact), ensuring the status line is
explicit and silences the linter.
🧹 Nitpick comments (5)
apps/web/src/styles/text-editor-dialog.css (2)
38-53: Consider responsive behavior for smaller viewports.The textarea has
min-height: 400pxand the modal hasmax-width: 800px. On mobile/small screens, the 400px min-height could push content off-screen or cause awkward scrolling, especially combined with themax-height: calc(90vh - 60px)on the body. Consider adding a media query to reducemin-heighton narrow viewports.
55-57: Hardcoded color in focus glow.
rgba(0, 208, 132, 0.05)is a hardcoded brand color. The rest of the file consistently uses CSS custom properties. Consider deriving this from a variable for theme consistency.apps/web/src/components/file-browser/ContextMenu.tsx (1)
187-193: Edit and Rename share the same icon (✎/✎).Line 190 uses
✎for Edit, and line 197 uses the same✎for Rename. Consider using a distinct icon for Edit to avoid visual confusion between the two adjacent menu items.apps/web/src/hooks/useFolder.ts (1)
554-563: Minor:modifiedAtset both in the service and locally with differentDate.now()calls.
replaceFileInFolder(folder.service.ts line 806) setsmodifiedAt: Date.now()on the IPNS-published metadata, and this local state update (line 562) callsDate.now()again, producing a marginally different timestamp. The IPNS version is authoritative and will be reconciled on sync, so this is benign — but you could capture the timestamp from the service result for perfect consistency, or just note this is intentional.apps/web/src/components/file-browser/TextEditorDialog.tsx (1)
104-109: No existing Web Worker encryption pattern in codebase — unnecessary to reference consistency.The coding guidelines recommend Web Workers for file encryption to avoid blocking the UI thread. However, since there is no established worker-based encryption path elsewhere in the codebase and text files edited in the modal are small, adding a Web Worker here would introduce unnecessary complexity without practical benefit. If a worker-based encryption pattern is established elsewhere for handling large files, revisit this at that time.
There was a problem hiding this comment.
Pull request overview
Adds an in-browser text editor modal to the web file browser, enabling editing of text files with a full client-side crypto round-trip (download/decrypt → edit → encrypt/upload → update IPNS metadata), plus E2E coverage and design updates.
Changes:
- Introduces
TextEditorDialogUI + styling and wires an “Edit” option into the file context menu for text-like filenames. - Adds folder-metadata update support via
replaceFileInFolderand exposes anupdateFileoperation inuseFolder. - Expands Playwright E2E suite with a text editor page object and full workflow tests; updates Pencil design source.
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/e2e/tests/full-workflow.spec.ts | Adds Phase 5.5 E2E tests covering open/edit/save/CID-change/persistence checks. |
| tests/e2e/page-objects/file-browser/context-menu.page.ts | Adds Playwright helpers for selecting the new “Edit” context menu item. |
| tests/e2e/page-objects/dialogs/text-editor-dialog.page.ts | New page object encapsulating editor modal interactions and assertions. |
| tests/e2e/page-objects/dialogs/index.ts | Exports the new dialog page object. |
| designs/cipher-box-design.pen | Adds the “19. Text Editor Modal” design frame to the Pencil source of truth. |
| apps/web/src/styles/text-editor-dialog.css | New CSS for the editor modal variant (width, textarea, status line, loading/error). |
| apps/web/src/services/folder.service.ts | Adds replaceFileInFolder to update file entry fields and republish folder metadata to IPNS. |
| apps/web/src/hooks/useFolder.ts | Adds updateFile operation that updates store state, unpins old CID, refreshes quota. |
| apps/web/src/components/ui/Modal.tsx | Adds className prop to support modal variant styling via the backdrop. |
| apps/web/src/components/file-browser/TextEditorDialog.tsx | New modal implementing download/decrypt/edit/encrypt/upload/update flow. |
| apps/web/src/components/file-browser/FileBrowser.tsx | Adds text-file detection, passes onEdit, and mounts TextEditorDialog. |
| apps/web/src/components/file-browser/ContextMenu.tsx | Adds “Edit” menu item support via optional onEdit callback. |
| .planning/todos/done/2026-01-23-simple-text-file-editor-modal.md | Documents the completed UI todo and intended flow. |
Moved todo from pending to done — beginning implementation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a text editor modal that allows editing text files directly in the browser. The full flow: download → decrypt → edit → encrypt → re-upload → update folder metadata → unpin old CID. - Add replaceFileInFolder service and updateFile hook operation - Create TextEditorDialog component with loading/saving/error states - Add "Edit" context menu option for text file extensions - Add Pencil design screen for the text editor modal - Add E2E page object and 5 test cases (Phase 5.5) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix potential data corruption: avoid using .buffer directly on Uint8Array sub-views by slicing to a clean copy first - Support extensionless text filenames (Dockerfile, Makefile, etc.) via a separate TEXT_FILENAMES set - Wrap JSX comment text in braces to satisfy noCommentText lint rule - Fix JSDoc: handleUpdateFile returns Promise<void>, not old CID - Use #0a0a0a textarea background to match Pencil design Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Test 3.7 (page reload preserves session) involves a full page reload + re-authentication + IPNS sync chain that can exceed the default 30s test timeout in CI. Increase to 90s to match the 60s locator timeout with headroom. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
bb85ba8 to
f166716
Compare
Summary
replaceFileInFolderservice function andupdateFilehook operation following existing patterns (rename, move).txt,.md,.json,.yaml,.js,.ts,.py, etc.)TextEditorDialogPage) and 5 test cases covering the full edit workflowTest plan
.txtor.mdfile, right-click → verify "Edit" appears.png→ verify "Edit" does NOT appearpnpm --filter e2e test(Phase 5.5 tests)🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
UI / Design
Tests