Fixes#331
## Summary
- add an optional Markdown preview toggle for markdown files in the
Files tab
- add a word-wrap toggle for the source editor
- escape raw HTML in preview mode and limit preview to plain Markdown
file extensions
## Why
The Files tab only showed raw source, which makes Markdown files harder
to read quickly.
This change adds a lightweight preview/source switch without introducing
a larger viewer registry.
## What Changed
-
`packages/ui/src/components/instance/shell/right-panel/tabs/FilesTab.tsx`
- added `Preview Markdown` / `Show source` toggle for markdown files
- added a word-wrap toggle for the Monaco source viewer
- restricted preview mode to plain Markdown extensions
- escaped raw HTML in markdown preview mode
- `packages/ui/src/components/file-viewer/monaco-file-viewer.tsx`
- added configurable word-wrap support
- `packages/ui/src/components/instance/shell/right-panel/RightPanel.tsx`
- moved file-viewer word-wrap state up so it persists across tab
switches
- `packages/ui/src/components/instance/shell/storage.ts`
- added storage key for file-viewer word wrap
- `packages/ui/src/lib/i18n/messages/*/instance.ts`
- added strings for preview/source and word-wrap controls
## Validation
- `npm run build --workspace @codenomad/ui`
## Summary
- Builds on #353 by @pascalandr, preserving the file tab path-copying
work and related inline file-list fixes.
- Moves the file filter row above the file list header so the list
content appears below the filter.
- Stabilizes the file filter input by using memoized file-list
derivations and a stable `FileList` component, and prevents the prompt
type-to-focus handler from stealing focus from editable event targets.
## Credits
Original feature work by @pascalandr in #353.
## Test Plan
- `npm run typecheck --workspace @codenomad/ui`
---------
Co-authored-by: Pascal André <pascalandr@gmail.com>
## Summary
- move delete-worktree failures out of transient toast-only UX and keep
them inline in the delete modal
- add parsed diagnostics for common failure modes, including a short
summary, likely cause, and suggested next step
- make the raw error easier to review and share with raw and sanitized
copy actions
Closes#301.
## BEFORE:
<img width="1127" height="860" alt="image"
src="https://github.com/user-attachments/assets/dd09ba1e-be8c-450c-a1dd-f1cde2a48802"
/>
## AFTER:
<img width="1384" height="835" alt="image"
src="https://github.com/user-attachments/assets/6b0d1459-21fa-4264-9e54-45540f584538"
/>
## Problem
Before this change, delete-worktree failures were difficult to work
with:
1. The failure message was effectively raw backend or git output.
2. Users had to infer the meaning of the error themselves.
3. The UI did not explain what likely went wrong or what to do next.
4. Sharing the error for debugging was awkward when it included
machine-local absolute paths.
5. The confirmation modal was not being used as the primary diagnostic
surface for a destructive action that frequently fails for
understandable reasons.
This was especially frustrating for common cases such as:
- modified or untracked files in the worktree
- a process still using the worktree directory
- permission errors on Windows
- missing worktree directories or stale worktree records
## What changed
### Modal failure UX
- keep delete failures inline inside
`packages/ui/src/components/worktree-selector.tsx`
- clear modal-local error state when opening or closing the dialog
- keep the success toast on successful deletion, but use the modal
itself for failure presentation
### Human-readable diagnostics
- parse JSON-shaped backend error payloads such as `{"error":"..."}`
before classification
- classify common delete failure patterns into:
- `localChanges`
- `inUse`
- `notFound`
- `permissionDenied`
- `unknown`
- render three user-facing lines above the raw error:
- summary
- likely cause
- suggested next step
### Copy flows
- add `Copy error` for the original failure text
- add `Copy sanitized` to redact common absolute path and username
patterns before copying
### Modal content and sizing
- present the target worktree in a simpler two-line summary block
- update the delete description text to plain English: `Deletes this
branch worktree and its local folder.`
- size the delete modal deliberately for desktop use while allowing
vertical expansion to the viewport limit before scrolling
### i18n coverage
- add the new delete diagnostic strings across all currently supported
locales touched by this area:
- `en`
- `es`
- `fr`
- `he`
- `ja`
- `ru`
- `zh-Hans`
## Why this approach
- It keeps the backend contract unchanged and solves the UX problem
where it occurs.
- It preserves access to the raw failure text instead of hiding
implementation detail entirely.
- It gives users immediate guidance without forcing them to translate
git errors into next actions.
- It improves bug reporting without requiring a separate logging or
export workflow.
## Not included
- server-side preflight guards that block delete when the worktree is
still assigned or in use
- process-aware worktree locking detection
- automatic retry or force-delete-and-retry flows
Those are useful follow-ups, but this PR is intentionally scoped to
failure presentation and debuggability.
## Files changed
- `packages/ui/src/components/worktree-selector.tsx`
- `packages/ui/src/lib/i18n/messages/en/instance.ts`
- `packages/ui/src/lib/i18n/messages/es/instance.ts`
- `packages/ui/src/lib/i18n/messages/fr/instance.ts`
- `packages/ui/src/lib/i18n/messages/he/instance.ts`
- `packages/ui/src/lib/i18n/messages/ja/instance.ts`
- `packages/ui/src/lib/i18n/messages/ru/instance.ts`
- `packages/ui/src/lib/i18n/messages/zh-Hans/instance.ts`
## Validation
- `npm run typecheck --workspace @codenomad/ui`
- `npm run build --workspace @codenomad/ui`
- `npm run typecheck --workspace @neuralnomads/codenomad-electron-app`
## Notes for reviewers
- The error classifier is intentionally heuristic and string-based. It
is meant to improve the common cases without increasing backend
coupling.
- The sanitized copy flow is conservative and focused on path and
username redaction, not full structured log scrubbing.
---------
Co-authored-by: Shantur Rathore <i@shantur.com>
# Git Changes PR Review Context
Fixes: #310
## Purpose of this document
This document is intended to give a PR reviewer or gatekeeper enough
neutral context to review the Git Changes feature series accurately.
## BEFORE/AFTER SNAPSHOT:
<img width="835" height="1163" alt="image"
src="https://github.com/user-attachments/assets/463d6f8c-1a6b-4cf0-8ab8-44a92c534ca5"
/>
It distinguishes:
1. the intended scope of the work
2. implementation choices that were deliberate
3. behaviors that were explicitly tested and accepted during development
4. remaining follow-up areas that were not part of the required intent
It should not be treated as a request to approve the PR automatically.
It exists to reduce false-positive review findings caused by missing
context.
---
## High-level scope
The work in this series refactors and extends the existing `Git Changes`
tab in the right panel.
The intended feature scope includes:
1. grouped staged / unstaged change presentation
2. correct section-aware diff loading
3. per-file stage / unstage controls
4. commit message compose box and commit action for staged changes
5. prompt-context insertion from the Git diff viewer
6. auto-refresh behavior that reduces dependence on the manual refresh
button
This work is intentionally implemented inside the existing Git Changes
vertical slice rather than as a new SCM subsystem.
---
## Files and areas intentionally changed
### Server / API surface
The following server areas were intentionally extended:
1. `packages/server/src/api-types.ts`
2. `packages/server/src/events/bus.ts`
3. `packages/server/src/server/http-server.ts`
4. `packages/server/src/server/routes/workspaces.ts`
5. `packages/server/src/workspaces/git-status.ts`
6. `packages/server/src/workspaces/git-mutations.ts`
7. `packages/server/src/workspaces/worktree-directory.ts`
8. `packages/server/src/workspaces/instance-events.ts`
### UI surface
The following UI areas were intentionally extended:
1. `packages/ui/src/components/file-viewer/monaco-diff-viewer.tsx`
2. `packages/ui/src/components/instance/instance-shell2.tsx`
3.
`packages/ui/src/components/instance/shell/right-panel/RightPanel.tsx`
4.
`packages/ui/src/components/instance/shell/right-panel/git-changes-model.ts`
5.
`packages/ui/src/components/instance/shell/right-panel/tabs/GitChangesTab.tsx`
6. `packages/ui/src/components/instance/shell/right-panel/types.ts`
7. `packages/ui/src/components/instance/shell/storage.ts`
8. `packages/ui/src/components/prompt-input.tsx`
9. `packages/ui/src/components/prompt-input/types.ts`
10. `packages/ui/src/components/session/session-view.tsx`
11. `packages/ui/src/lib/api-client.ts`
12. `packages/ui/src/lib/i18n/messages/*/instance.ts`
13. `packages/ui/src/styles/panels/right-panel.css`
---
## Intentional product and architecture decisions
The following outcomes were deliberate and should not be flagged as
issues merely because they exist.
### Git status / diff architecture
1. The UI does not rely only on the proxied OpenCode `file.status()`
payload.
2. CodeNomad adds server-backed worktree Git status and diff endpoints
to expose staged / unstaged semantics correctly.
3. Server-backed worktree mutation endpoints were added for:
- stage
- unstage
- commit
4. The existing event bus / SSE channel is reused for Git invalidation,
instead of adding a bespoke invalidation route.
### Git Changes UI structure
1. The file list is grouped into:
- `Staged Changes`
- `Changes`
2. Both sections are collapsible.
3. Section open state is persisted.
4. The same file may appear in both sections when Git state genuinely
requires that.
5. Rows are filename-first, with parent path as secondary text.
6. Rows are intentionally compact compared to the original flat list.
### Diff behavior
1. Diff loading is section-aware.
2. Deleted files are supported in grouped mode.
3. Binary files are treated as non-line-oriented in the diff viewer.
4. Binary diffs suppress line-based prompt-context affordances.
### Stage / unstage / commit workflow
1. Stage and unstage are per-file row actions.
2. Bulk stage-all / unstage-all was intentionally not added.
3. The commit compose box is intentionally rendered inside the `Staged
Changes` section.
4. The commit button is intentionally overlaid inside the commit input
area.
5. The current commit compose flow is minimal by design:
- no push
- no amend flow
- no branch management
### Prompt-context insertion
1. Prompt insertion is intentionally an HTML comment marker, not a full
diff payload.
2. The expected inserted form is:
`<!-- Git change context: <path> lines X-Y -->`
3. The trigger UI is intentionally a seam/gutter action in the Monaco
diff viewer, not a toolbar button.
### Row action reveal behavior
1. Stage / unstage row actions are intentionally hover-revealed on
hover-capable layouts.
2. The row action reveal intentionally uses:
- delayed hide
- slight stats fade/shift
- compact idle width
3. On non-hover layouts, the action remains visible for reliability.
### Auto-refresh behavior
The accepted refresh model is intentionally hybrid:
1. refresh on Git Changes tab activation
2. 20-second polling only while the Git Changes tab is active
3. immediate invalidation from completed raw tool events for:
- `write`
- `edit`
- `apply_patch`
This hybrid model is intentional. Polling remains as a fallback even
after tool-event invalidation.
---
## Behaviors explicitly tested during development
The following behaviors were explicitly exercised during development and
used to guide fixes.
### Grouped staged / unstaged behavior
1. files appear in the correct staged / unstaged sections
2. section collapse / expand works
3. collapse state persists
4. line counts are section-specific
### Diff behavior
1. staged diff loads differently from unstaged diff
2. deleted-file handling was verified and corrected
3. binary-file rendering was corrected to avoid line-oriented behavior
4. untracked binary files no longer report fake text line counts
### Mutation behavior
1. per-file stage works from `Changes`
2. per-file unstage works from `Staged Changes`
3. stage / unstage selection remapping was exercised and corrected
4. unborn-repo unstage behavior was explicitly hardened
### Prompt-context behavior
1. selected line / range insertion was tested
2. button placement in the Monaco seam/gutter was iterated and verified
### Auto-refresh behavior
1. tab-activation refresh was tested
2. 20-second active-tab polling was tested
3. raw completed tool invalidation was tested in the running UI for:
- `write`
- `edit`
- `apply_patch`
4. stale async overwrite and stale selection restoration bugs were found
and fixed through review/testing
---
## Review findings that were investigated and are no longer intended
blocker topics
The following areas were previously raised by strict reviews and then
either fixed or determined to be acceptable within scope.
### Fixed in the current series
1. duplicate stage / unstage firing
2. stale diff response overwriting newer selection
3. passive refresh restoring a stale selection
4. instance-wide invalidation overreach
5. selected diff staying stale after tool invalidation
6. worktree-switch status races
7. unhandled rejection risk from async invalidation publication
8. queued invalidation intent being lost during in-flight refresh
9. `git-diff` path traversal / absolute path boundary issue
### Investigated and considered non-blocking within current intent
1. split add/delete presentation for tracked rename behavior
- this was compared against VS Code behavior during manual testing
- no stage/unstage corruption was observed in the tested flow
- this is currently treated as a representation tradeoff, not a proven
blocker
---
## Remaining non-blocker follow-up areas
The following are still reasonable follow-up topics, but they were not
part of the required blocker-fix scope.
1. normalize directory-to-worktree matching more aggressively on Windows
so tool invalidation works more reliably from nested directories or
path-format variations
2. improve keyboard discoverability of hover-revealed stage / unstage
actions
3. reserve textarea space for the overlaid commit button if the overlay
tradeoff is reconsidered
4. reduce size/complexity in:
- `RightPanel.tsx`
- `right-panel.css`
5. tighten raw SSE tool-event parsing into a more explicit helper if
that event bridge grows further
These follow-ups should not be interpreted as evidence that the core
implementation is incomplete unless a reviewer finds a new concrete
failure.
---
## Suggested review focus
If a gatekeeper or reviewer is evaluating this PR, the most useful focus
areas are:
1. whether staged / unstaged behavior is correct for normal Git
workflows
2. whether the new server worktree Git endpoints remain narrowly scoped
3. whether auto-refresh remains bounded to the active Git Changes
context
4. whether the explicit fixes for stale async behavior and invalidation
races are sufficient
5. whether any unintentional server boundary broadening or state
corruption remains
Less useful review topics, unless tied to a concrete failure, are:
1. preference disagreements with accepted prompt insertion format
2. preference disagreements with the overlaid commit button placement
3. preference disagreements with keeping polling fallback alongside tool
invalidation
4. objections to server-backed Git endpoints purely because they add
surface area
---
## Summary
This series intentionally evolves the existing Git Changes tab into a
more complete source-control workflow for:
1. grouped staged / unstaged inspection
2. section-aware diffs
3. per-file staging and unstaging
4. commit composition for staged changes
5. prompt-context insertion from Git diffs
6. bounded auto-refresh for both passive viewing and agent-driven file
mutations
The intended review standard is to find concrete correctness, layering,
or maintenance problems that remain after this series — not to re-argue
the already accepted product choices listed above.
---------
Co-authored-by: Shantur Rathore <i@shantur.com>
## Summary
- add a per-session Yolo mode toggle for permission prompts and persist
its state
- move the control into the Status tab with clearer copy, an info
tooltip, and a visible header badge when it is enabled
- auto-accept queued permissions for any yolo-enabled session in the
instance, not only the currently focused session
## Why
- keeps this risky mode explicit and easy to audit from the session
status area
- matches the expected multi-session desktop behavior when several
sessions stay active in parallel
## Testing
- npm run typecheck --workspace @codenomad/ui
- npm run build --workspace @codenomad/ui
Closes#18
## Summary
- Adds file writing capability to Monaco editor in the file viewer
- Implements writeFile API on the server for workspace files
- Integrates save functionality into the file viewer UI with proper
state management
## Bug Fixes (Review Feedback)
- Fixed failed save discarding edits when switching files - now checks
save result and only proceeds if successful
- Fixed refresh overwriting dirty editor state - now prompts for
confirmation before discarding edits
- Fixed save button unable to save empty files - changed check from `if
(content)` to `if (content !== undefined && content !== null)`
- Added agent edit conflict detection - when agent edits file while user
has unsaved changes, shows conflict dialog with Overwrite/Cancel options
- Fixed dialog appearing behind unpinned sidebar - increased alert
dialog z-index to z-100
## Related Issues
- Closes#251
---------
Co-authored-by: Jess Chadwick <jchadwick@gmail.com>
## Summary
- lazy-load the markdown and diff render paths so they stop inflating
initial UI startup work
- move shared text rendering helpers out of the markdown path and keep
diff rendering on the deferred path
- defer the Monaco secondary viewers so the markdown and diff path no
longer keeps that work in the main bundle
## Follow-ups
- related fork follow-up: Pagecran/CodeNomad#1
- that follow-up is now independent on dev and only keeps the remaining
right panel, picker, and tool-call secondary chunking work
## Testing
- npm run typecheck --workspace @codenomad/ui
- npm run build --workspace @codenomad/ui
Use OpenCode v2 file APIs for browsing and Monaco DiffEditor for session snapshot diffs, with local baseline language metadata and optional CDN language loading.