Commit Graph

25 Commits

Author SHA1 Message Date
VooDisss
9d9a6a79ec Git diff monaco redesign (#304)
## Summary

Fixes #303.

This PR redesigns the Git Changes Monaco diff gutter so unified and
split view both use a more intentional, space-efficient Monaco
presentation while preserving Monaco's performance on large diffs.

The final behavior includes:

- `Compact` and `Normal` gutter modes for Git Changes
- dynamic gutter sizing based on actual line-number digit counts
- independent original/modified number-column sizing where needed
- split-view fixes for both wasted left inset and line-number/sign
overlap
- persisted gutter-mode selection
- localized user-facing labels for the control

## Visual comparison

### Unified view before

<img width="465" height="353" alt="Unified view before"
src="https://github.com/user-attachments/assets/0c061f25-f20a-4127-a85d-aee1161611c7"
/>

### Unified view after

<img width="634" height="240" alt="Unified view after"
src="https://github.com/user-attachments/assets/f2dfd952-89ed-4fdd-83db-a05f19f023b2"
/>

### Split view before

<img width="596" height="335" alt="Split view before"
src="https://github.com/user-attachments/assets/09bfbe41-9438-4801-b181-49a9d19d5bb8"
/>

### Split view after

<img width="640" height="338" alt="Split view after"
src="https://github.com/user-attachments/assets/fc3618ef-474f-4217-bb21-5ffd53eb4e01"
/>

<!-- If you want to replace these screenshots later, keep the four
sections above and swap the image URLs. -->

## What changed

### Unified view

- added two Git Changes Monaco gutter presentations:
  - `Compact`
  - `Normal`
- kept compact as the tighter single-column-feel unified gutter
- kept normal as the wider Monaco-style unified gutter
- made unified gutter sizing respond to actual line-number digit counts
instead of fixed assumptions
- made normal mode size the visible number columns independently when
one side needs more width than the other

### Split view

- added dynamic split gutter sizing derived from actual before/after
line counts
- made split original and modified number columns size independently
- fixed the modified-pane overlap where larger line numbers could
collide with the `+` lane
- fixed the original-pane wasted left inset caused by Monaco reserving
an empty original-side glyph-margin lane

### Persistence and UI

- persisted the selected gutter mode in preferences so it survives
reloads
- moved the gutter-mode control out of the Git Changes toolbar and into
Appearance settings
- renamed the visible settings options to `Compact` and `Normal`

### i18n

- removed hardcoded user-facing gutter toggle strings
- added localized keys for the gutter control labels and titles used by
the Git Changes surface

## Implementation notes

- Monaco remains the active Git Changes renderer throughout
- gutter sizing logic is centralized in
`packages/ui/src/components/file-viewer/monaco-diff-viewer.tsx`
- CSS is used only for narrow presentation adjustments such as the 4px
left inset and the split original-pane glyph-margin correction
- the persisted gutter-mode preference is the source of truth for the
selected presentation

## Review focus

- unified `Compact` mode should feel tight without clipping or overlap
- unified `Normal` mode should remain wider and readable
- 3-digit and 4-digit line numbers should not collide with the sign lane
- split original pane should no longer show wasted left inset before the
first visible number column
- split modified pane should not leave conspicuous dead space or collide
with the `+` lane as digit counts grow
- selected gutter mode should persist after reload

---------

Co-authored-by: Shantur Rathore <i@shantur.com>
2026-04-17 17:04:10 +01:00
VooDisss
9bf4d351de Refactor Git Changes workflow and diff handling (#311)
# 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>
2026-04-16 23:11:48 +01:00
Shantur Rathore
0739ec857c Reapply "fix(ui): support unified diff patch format in session changes viewer"
This reverts commit af6429162f.
2026-04-08 20:57:23 +01:00
Shantur Rathore
af6429162f Revert "fix(ui): support unified diff patch format in session changes viewer"
This reverts commit 2e9ee2cde6.
2026-04-08 20:57:12 +01:00
Shantur Rathore
2e9ee2cde6 fix(ui): support unified diff patch format in session changes viewer
Session diffs now use a compact patch field instead of storing full
before/after content. Added parsePatchToBeforeAfter utility to extract
before/after from unified diff format, and updated MonacoDiffViewer to
accept patch prop as alternative to before/after strings.
2026-04-08 20:48:13 +01:00
Shantur Rathore
5107ac207e feat(ui): show background process notify state 2026-04-08 16:09:17 +01:00
Pascal André
64ac885157 feat(ui): add session yolo mode controls (#256)
## 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
2026-03-31 14:46:20 +01:00
Jess Chadwick
37b3f85e61 feat: Enable file editing and saving (#252)
## 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>
2026-03-29 22:41:11 +01:00
MusiCode1
1c68f5d288 feat(i18n): Hebrew locale + full RTL support (#243)
# feat(i18n): Hebrew locale + full RTL support

## Summary

This PR adds full Hebrew (he) locale support to the UI, including a
complete translation of all user-facing strings and comprehensive RTL
layout support across all components.

## What was done

### Hebrew translation
- Full translation of all i18n message files for the `he` locale (17
translation files)
- Registered the language in the i18n system and the language picker

### RTL support
- Automatic direction detection (`dir="rtl"`) when Hebrew is selected
- Replaced physical CSS properties (`left`/`right`) with logical
equivalents (`inline-start`/`inline-end`) across the project
- Fixed resize direction, file path alignment, and textarea padding
- Fixed navigation button positioning in textarea for RTL
- Fixed scrollbar direction in RTL
- Fixed code block direction and selector alignment
- Fixed Monaco editor direction in the file viewer
- Auto-detect text direction in reasoning block (`dir="auto"` +
`unicode-bidi: plaintext`)

### Adapted components
- `session-layout` — sidebar and resize handle
- `prompt-input` — text direction and buttons
- `message-base` — message blocks and reasoning
- `message-timeline` — timeline bar
- `right-panel` — right side panel
- `tool-call` — tool call display
- `settings-screen` — settings page
- `selector` — selection component
- `instance-shell` — main shell

## New files

```
packages/ui/src/lib/i18n/messages/he/
  advancedSettings.ts
  app.ts
  commands.ts
  dialogs.ts
  filesystem.ts
  folderSelection.ts
  index.ts
  instance.ts
  loadingScreen.ts
  logs.ts
  markdown.ts
  messaging.ts
  remoteAccess.ts
  session.ts
  settings.ts
  time.ts
  toolCall.ts
```

## Suggested testing
- Switch language to Hebrew and verify all strings are translated
- Verify RTL layout is correct across all screens (session, settings,
file viewer)
- Verify that English text inside a reasoning block is displayed LTR
- Switch back to English and verify everything returns to LTR

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Shantur Rathore <i@shantur.com>
2026-03-24 21:09:52 +00:00
Pascal André
8567d49178 perf(ui): split right panel and secondary viewer chunks (#239)
## Summary
- split the right panel, picker, and tool call secondary viewers into
smaller deferred chunks
- release hidden right-panel file buffers and stop tracking static
tool-call scrollers when they are not needed
- keep this branch focused on the remaining secondary viewer chunking
work now that the Monaco-specific chunking moved into PR 215

## Testing
- npm run build --workspace @codenomad/ui
2026-03-23 08:47:03 +00:00
Pascal André
74f753abf4 perf(ui): lazy-load markdown and defer diff rendering (#215)
## 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
2026-03-22 11:54:05 +00:00
Shantur Rathore
c51e71c7a2 perf(ui): memoize changes lists and reduce stream rendering 2026-02-28 10:31:32 +00:00
Shantur Rathore
5834d2df1b fix(ui): use v2 message info and show model variant 2026-02-25 22:29:27 +00:00
VooDisss
697dea21f8 Add informational tooltips to Status Panel sections 2026-02-20 14:09:54 +02:00
Shantur Rathore
dea5079713 feat(ui): add diff toolbar toggles and word wrap
Replace split/unified and context controls with icon toggles, add a word-wrap toggle (default on), and move the toolbar into the tab header to free vertical space.
2026-02-17 13:47:07 +00:00
Shantur Rathore
36baac06b8 fix(ui): hide kbd hints on non-desktop 2026-02-13 10:02:15 +00:00
Shantur Rathore
3678214e69 fix(ui): hide keyboard hints on non-desktop 2026-02-13 09:54:46 +00:00
Shantur Rathore
338e3d9d38 fix(ui): hide keyboard hints on phone layout 2026-02-13 09:21:24 +00:00
Shantur Rathore
6ed1e09180 fix(ui): align sidebar header icon sizes 2026-02-12 16:07:54 +00:00
Shantur Rathore
54d4cf6604 fix(ui): use PlusSquare icon export 2026-02-12 15:47:50 +00:00
Shantur Rathore
359e89971f feat(ui): add new session icon in sidebar header 2026-02-12 15:37:58 +00:00
Shantur Rathore
d7c4bf1e45 fix(ui): render selected session diff payload
Pass the selected diff object through Solid's Show so MonacoDiffViewer receives before/after content.
2026-02-11 12:31:09 +00:00
Shantur Rathore
fd499d95e6 fix(ui): truncate right panel paths from start
Use RTL ellipsis with bidi isolation so long paths keep the filename visible.
2026-02-11 11:27:24 +00:00
Shantur Rathore
d34e0163e3 fix(ui): keep right panel layout in empty states
Render SplitFilePanel consistently and move empty/loading messages into the viewer area so the right drawer keeps its standard layout even when there are no session diffs, no git changes, or files are still loading.
2026-02-11 10:51:27 +00:00
Shantur Rathore
8ce7a9b4ee refactor(ui): modularize instance shell
Split InstanceShell2 into focused shell modules (drawer chrome/resize, session context/cache, sidebar, right panel tabs/components) to improve maintainability while preserving behavior.
2026-02-11 08:16:44 +00:00