Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a23316d
feat(ui): complete surface and palette harmonization
pascalandr Sep 1, 2026
4a53baf
fix: stabilize virtual timelines and developer automation
pascalandr Sep 1, 2026
e7b4c9c
fix(ui): place tab scrollbars above strips
pascalandr Sep 2, 2026
05027df
feat(ui): finish compact workspace harmonization
pascalandr Sep 4, 2026
9287816
feat(ui): add worktree to prompt context controls
pascalandr Sep 4, 2026
16c555f
fix(ui): refine session chrome and utility windows
pascalandr Sep 5, 2026
055d944
merge(dev): reconcile PR #667 with current V2 baseline
pascalandr Sep 6, 2026
b11e85c
fix(ui): finalize validated session chrome and transcript controls
pascalandr Sep 7, 2026
f5d28ea
fix(ui): restore soft palette hierarchy and refine session controls
pascalandr Sep 8, 2026
306da15
merge(dev): reconcile chat visibility and native session authority
pascalandr Sep 8, 2026
2567eb2
fix(ui): finalize independent palettes and composer surface alignment
pascalandr Sep 8, 2026
d076074
fix(ui): align session selection and simplify nested indentation
pascalandr Sep 8, 2026
38f5e01
Merge branch 'dev' into feat/ui-harmonization
pascalandr Sep 9, 2026
71c4ce6
merge(dev): preserve palette and server metadata CI coverage
pascalandr Sep 9, 2026
33e16e6
Merge branch 'dev' into feat/ui-harmonization
pascalandr Sep 10, 2026
3688576
docs(ui): add seventeen-palette multi-project demo gallery
pascalandr Sep 10, 2026
92c8833
Merge branch 'dev' into feat/ui-harmonization
pascalandr Sep 10, 2026
c990f07
merge(dev): reconcile pruning dependencies and connected-client coverage
pascalandr Sep 10, 2026
b5adfde
fix(ui): share rounded switch geometry between Yolo and MCP
pascalandr Sep 10, 2026
41d7ba8
fix(ui): harmonize transcript status and content cues
pascalandr Sep 10, 2026
bfc07ec
feat(ui): apply saved palettes and compact shared appearance controls
pascalandr Sep 10, 2026
24b269a
docs(ui): refresh palette gallery and compact visual presentation
pascalandr Sep 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/pr-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ jobs:
packages/ui/src/lib/model-visibility.test.ts
packages/ui/src/lib/runtime-env.test.ts
packages/ui/src/lib/server-meta.test.ts
packages/ui/src/lib/theme-scheme.test.ts
packages/ui/src/lib/trailing-resync.test.ts
packages/ui/src/stores/abort-created-workspace-cleanup.test.ts
packages/ui/src/stores/app-session-reconciliation.test.ts
Expand All @@ -203,12 +204,14 @@ jobs:
packages/ui/src/stores/client-state-partitions.test.ts
packages/ui/src/stores/client-state.test.ts
packages/ui/src/stores/form-settlements.test.ts
packages/ui/src/stores/instance-invalidation.test.ts
packages/ui/src/stores/message-prompt-display.test.ts
packages/ui/src/stores/message-v2/instance-store.test.ts
packages/ui/src/stores/message-v2/message-hydration-authority.test.ts
packages/ui/src/stores/message-v2/message-status.test.ts
packages/ui/src/stores/message-v2/message-window.test.ts
packages/ui/src/stores/message-v2/normalizers.test.ts
packages/ui/src/stores/plugin-activation.test.ts
packages/ui/src/stores/shell-store.test.ts
packages/ui/src/stores/session-generation-recovery.test.ts
packages/ui/src/stores/session-pagination.test.ts
Expand Down
12 changes: 12 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,20 @@
- Prefer smaller, focused style files (≈150 lines or less) over large monoliths. Split by component or feature area if a file grows beyond that size.
- Co-locate reusable UI patterns (buttons, selectors, dropdowns, etc.) under `src/styles/components/` and avoid redefining the same utility classes elsewhere.
- Use the shared `.window-*` primitives from `src/styles/components/window.css` for dialog, popover, and floating-window headers, toolbars, bodies, footers, titles, and actions.
- Keep agent, model, and thinking controls in the composer footer via `PromptContextControls`; adapt that footer with the named `prompt-composer` container rather than viewport-only breakpoints.
- Session rows keep actions inline until their measured title, badges, and controls no longer fit. Keep responsive action styles in `styles/components/session-row-actions.css`; hidden inline controls remain measurable but inert, and an open overflow menu stays mounted until dismissal.
- Session hierarchy geometry lives in `styles/components/session-tree.css`; connector axes follow the parent expander at every depth, including selection mode, RTL and touch layouts.
- Never use rounded corners in UI styling; keep corners square unless the user explicitly requests otherwise for a specific change.
- Explicit round exceptions: Yolo and MCP switches (shared `styles/components/switches.css` geometry), overlay drawer navigation buttons, and floating message scroll buttons. Other chrome remains square.
- Tags and numeric/context/token labels also use rounded geometry via `--chip-radius` (`--pill-radius` is an alias). Register badge variants in `styles/components/badges.css`; use `.badge-shape` for utility-styled labels rather than adding a local radius.
- The message-content popup and Chat settings share `components/transcript-visibility.ts`; tool presentation metadata lives independently of renderers in `components/tool-call/tool-presentation.ts`. Popup styles live in `styles/components/transcript-filters.css`.
- Session timeline placement spans the transcript and composer via the session-owned mount; keep its rail layout in `styles/messaging/session-timeline-rail.css` and preserve compact-layout hiding.
- Document any new styling conventions or directory additions in this file so future changes remain consistent.
- Soft palette families live in `packages/ui/src/lib/soft-color-schemes.ts`, with references in `dev-docs/PALETTE_SOURCES.md`. Keep selection independent of participant identity, and keep transcript/composer surfaces distinct. Run `palette-quality.test.ts` and inspect real rendered captures when changing palette colors or their token mapping.
- Palette settings follow the resolved appearance in Auto mode. Keep the picker/actions beside the two-row swatch grid, stacking via the `palette-settings` container at narrow widths. Swatch styles live in `styles/components/theme-scheme-swatches.css`.
- Appearance mode and the saved light/dark selections are independent (`lib/appearance-preferences.ts`). Message/tool cards use the muted surface, inset output and the composer use the base canvas, and preferences use the same secondary surface as the main panels. Use `--surface-hover-overlay` for a subtle local rollover; preserve selected backgrounds beneath that overlay instead of replacing them with a generic panel color.
- Right-panel base-canvas button rollover overrides live in `styles/panels/control-hover.css`; do not substitute the secondary surface merely to show hover.
- Project and right-panel tabs share `components/tab-scroll.tsx` and `styles/components/tab-scroll.css`. Keep their native scrollbar above upright content without mirrored transforms, negative border overlaps or permanent compositing hints. Validate shared scrollbar styling and adjoining edges at fractional zoom in the browser and isolated Electron renderer fixtures (`tests/browser/tab-chrome.test.ts`).

## Coding Principles

Expand Down
85 changes: 70 additions & 15 deletions DESKTOP_V2_COMPARISON.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@

This review compares:

- CodeNomad `DEV-v2` at `dea20996` (2026-09-03).
- Official OpenCode Desktop V2 from `anomalyco/opencode` branch `upstream/beta` at `eb1ac54d73` (2026-08-25).
- CodeNomad declares `@opencode-ai/client@beta`; UI and server resolve `beta-18999`, while the runtime CLI version is managed independently.
- CodeNomad `feat/ui-harmonization` after the `733f5bf9` merge of `DEV-v2` (2026-09-04).
- The initial official OpenCode Desktop V2 baseline at `eb1ac54d73` (2026-08-25), which produced `beta-18230`.
- The latest published OpenCode V2 beta source at `c9d240704d6eefc88b63a1eca2cb933b3eb70ed3` (2026-09-04), which produced `beta-19059`.
- The matching `upstream/beta` head, with no later unpublished contract delta at review time.

CodeNomad declares `@opencode-ai/client@beta`; UI and server resolve `beta-19059`. The independently managed runtime used to verify the compatibility fallback still reports `beta-18999`; startup intentionally has no exact client/runtime version gate.

The official reference is `packages/desktop` for the Electron host, `packages/app` for the shared UI, and the V2 client, protocol, schema, server, and core packages for wire behavior. Older `v2`, `opencode-2-0`, and intermediate `desktop-v2-*` branches are historical, not the current Desktop V2 reference.

Expand All @@ -16,7 +19,7 @@ This is an incremental review. It does not repeat issues already closed by CodeN

CodeNomad implements the important V2 architecture rather than emulating the V1 desktop model. It uses the native client contract, locations, shared service, sessions, messages, Forms, permissions, providers, Shells, worktrees, and event stream. Its multi-window and cross-host restore implementation is broader than the official Electron-only desktop implementation.

The comparison and subsequent beta-contract audit found concrete CodeNomad defects in pagination, navigation, model projection, inbox delivery, follow behavior, background control, location selectors, and proxy route coverage. Those defects are fixed in the commits accompanying this document. The remaining differences are scoped workflow defects, release hardening, or optional Desktop features. None requires restoring V1 code or replacing native V2 cursors.
The comparison and subsequent beta-contract audit found concrete CodeNomad defects in pagination, navigation, model projection, inbox delivery, follow behavior, background control, location selectors, plugin readiness, event handling, durable metadata, and proxy route coverage. The accompanying changes close the published-contract defects. The remaining differences are release hardening and optional Desktop workflows. None requires restoring V1 code or replacing native V2 cursors.

## Closed Findings

Expand Down Expand Up @@ -46,11 +49,43 @@ The comparison and subsequent beta-contract audit found concrete CodeNomad defec

### Failed plugin inventory

**Previous behavior:** Metadata projection called `startsWith` on every plugin ID. Current OpenCode V2 can report a failed plugin without an ID, which would break metadata refresh after a client upgrade.
**Previous behavior:** Metadata projection first assumed every plugin had an ID, then accepted every string ID as active. The current contract permits failed plugins both with and without an ID, so a named failure could be displayed with the same healthy indicator as an active plugin.

**Official behavior:** `PluginInfo` has an optional `id` and a required `state` discriminator whose status is `active` or `failed`.

**Resolution:** CodeNomad projects only non-builtin plugins with a string ID and `state.status === "active"`. Failed records remain available in the native inventory but are no longer represented as healthy in the legacy name-only status list.

### Asynchronous plugin activation

**Previous behavior:** Initial agent, provider, model, command, and plugin reads could run while a Location's configured plugins were still installing or activating. CodeNomad could therefore retain a transiently incomplete catalog until another event forced a refresh.

**Official behavior:** `beta-18999` adds `POST /api/plugin/await-activation`; the official ACP client waits on it before caching a Location catalog. OpenCode also replaces `plugin.added` with the settled `plugin.updated` event.

**Resolution:** The proxy exposes only the non-mutating activation wait, catalog and plugin-status reads wait for it, concurrent waits for one client and Location coalesce, and an unsupported lagging runtime falls back to authoritative reads. `plugin.updated` now refreshes agents, providers, commands, and metadata; the obsolete `plugin.added` branch is removed. `plugin.check`, `plugin.update`, and generic plugin RPC remain blocked.

### Active-location MCP and plugin status

**Previous behavior:** Metadata requests and their loaded-state check used the instance root even when the selected session belonged to a worktree with different `.opencode` configuration.

**Official behavior:** Location-scoped status follows the selected session, and request inputs encode native `workspaceID` values as the wire-level `location[workspace]` selector.

**Resolution:** MCP, plugin activation, and plugin inventory reads now use the active `SessionInfo.location`; metadata readiness is keyed by the location returned from MCP; session switches trigger a new status load; and MCP toggles continue to use that same returned location. Replaced clients and superseded locations cannot commit stale metadata.

### Filesystem list ownership

**Official behavior:** `packages/schema/src/plugin.ts` defines active and failed plugin variants, with an optional ID for failures.
**Previous behavior:** The proxy authorized the native Location for `fs.list` but did not separately authorize its `path` query. The official contract permits an absolute path or traversal to parents and siblings, so a workspace-scoped caller could ask the shared daemon to list an unrelated directory.

**Resolution:** CodeNomad now accepts only string IDs when projecting its current plugin-name list. An ID-less failed record can no longer prevent project, MCP, and plugin metadata from loading.
**Official behavior:** `fs.list` keeps the requested Location while resolving its optional path independently; returned entry paths remain relative to that Location.

**Resolution:** CodeNomad resolves relative targets against the authorized Location, rejects duplicate path selectors and targets outside owned worktrees, and translates accepted paths into the shared service namespace for WSL. Parent and sibling browsing remains possible only inside owned worktrees.

### Durable session and message additions

**Previous behavior:** The local session adapter reconstructed `SessionInfo` without its durable JSON `metadata`, `session.created` also dropped that metadata, and the local message-info time shape omitted the new `streamed` boundary. A recovered `session.step.streamed` event alone did not mark an idle local session as working.

**Official behavior:** Published V2 sessions carry optional `SessionMetadata`; assistant messages carry `time.streamed`; `session.step.streamed` and `session.message.content.updated` are native durable events reduced by `@opencode-ai/client/solid`.

**Resolution:** REST and event session projections retain metadata, message projection retains streamed time, a streamed step restores working status after an event gap, and focused tests verify authoritative assistant-content replacement through the generated Solid reducer.

### Deprecated models

Expand Down Expand Up @@ -95,15 +130,30 @@ The review reconfirmed these areas and found no current incompatibility:

Service stop removal is intentional: CodeNomad does not own the shared daemon. Upstream session sharing is disabled, so its absence is not a parity gap. Upstream's temporary SSE heartbeat change was reverted and requires no CodeNomad change.

## Remaining Correctness Work
## Latest Published Beta Audit

### Active-location MCP and plugin status
The official `anomalyco/opencode-beta` repository published 22 beta tags from `beta-18230` through `beta-19059`. Their GitHub release bodies are empty, so there are no prose release notes to review. This audit instead matched every successful publish workflow to its source commit, read the intervening official commits, compared npm artifacts and generated declarations, and checked the official V2 documentation index and relevant API/client pages.

**Priority:** Medium. **Client upgrade required:** No.
The final refresh is exact:

- `beta-18866` was built from `519cd8c7712fc2ca6d2ca1d356d7f52cbd6d5808`.
- `beta-18999` was built from `887f319769c55718e3e64f64b32c9aafb13c5d66`.
- `beta-19059` was built from `c9d240704d6eefc88b63a1eca2cb933b3eb70ed3`.
- The ranges contain 114 commits from `beta-18866` to `beta-18999`, then 52 commits to `beta-19059`.
- The Promise client remains at 136 routes instead of 135: the only added route is `POST /api/plugin/await-activation`.
- The only added exported types are `PluginAwaitActivationInput` and `PluginAwaitActivationOutput`; `PluginAdded` is removed.
- `V2Event` removes `plugin.added`; `ConfigEntry.autoupdate?: boolean | "notify"` first becomes `update?: "disable" | "notify" | "auto"`, then `beta-19059` removes the `"auto"` value.
- `beta-19059` adds optional compaction `model` and `providerState`, the command-config `subagent` flag, and guarded Solid event refreshes with `onError`; it adds no route or exported type name.

CodeNomad consumes the activation boundary and current event/state shapes, preserves the new compaction metadata, logs bounded Solid refresh failures, authorizes the independently resolved filesystem-list target, and avoids falsely marking a parent busy when a command may spawn a background subagent. It has no OpenCode update-setting caller to migrate. Runtime-side fixes—plugin activation stability, session-entry readiness, configuration and symlink watching, abandoned-compaction settlement, provider identity/state preservation, Location retry, command-subagent backgrounding, and Windows interruption—are acquired from the independently updated OpenCode runtime rather than duplicated in CodeNomad. Official App/Desktop/TUI-only navigation, styling, terminal-pane, timeline-detail, and plugin-dialog changes were reviewed as product references, not treated as wire requirements.

`packages/ui/src/lib/hooks/use-instance-metadata.ts` currently queries MCP and plugin state with the instance root. The active session may belong to a worktree or nested location with different `.opencode` configuration. Official Desktop derives the status location from the selected session in `packages/app/src/pages/session.tsx` and `status-popover-body.tsx`.
### Beta 19059 delta

The CodeNomad metadata request and cache authority should be keyed by the active `SessionInfo.location`, and MCP toggles should use that same location. This needs a focused state change rather than a root fallback patch because switching tabs must not display or mutate another location's MCP state.
The previously failed publish was rerun successfully on 2026-09-04. The OpenAPI remains at 119 paths, 140 operations, and 229 schemas. The generated Promise surface remains at 136 methods. The full declaration delta from `beta-18999` is the optional compaction model/provider state, command-config `subagent` plus deprecated `subtask`, and removal of the `"auto"` update mode; the Solid helper separately adds connection/disposal guards and `onError` for event-triggered reads.

All 52 intervening commits were classified. Core and client correctness fixes flow through the upgraded client or independently updated runtime. The now-published official Desktop session-import action remains an optional CodeNomad product workflow because the ownership-validated native import route already exists without requiring UI parity. At review time `upstream/beta` points to the same source commit, so there is no unpublished head to represent as shipped behavior.

## Remaining Correctness Work

### Signed desktop releases

Expand All @@ -121,10 +171,15 @@ Generated types, proxy routes, events, plugin inventory, Forms, sessions, and re

## Optional Feature Gaps

These are official Desktop capabilities, not migration blockers:
These are official Desktop or published native V2 capabilities, not migration blockers:

- **Interactive PTY UI:** CodeNomad proxies native PTY lifecycle routes but has no embedded terminal, connect-ticket WebSocket, resize, reconnect, or restore UI. The existing external terminal action and background Shell panel are not equivalent.
- **Session export:** Official Desktop paginates and exports a complete session. CodeNomad has no export command or allowlisted export route.
- **Interactive and persistent PTY UI:** CodeNomad proxies standard PTY lifecycle routes but has no embedded terminal, connect-ticket WebSocket, resize, reconnect, or restore UI. Published `experimental.persistentPty.*` session-terminal routes remain blocked because CodeNomad has no corresponding ownership lifecycle. The external terminal action and background Shell panel are not equivalent.
- **Session transfer UI:** The ownership-validated `session.import` route is allowlisted, but CodeNomad has no import action. Official Desktop now ships an import action and paginates before exporting a complete session; CodeNomad has no export command or allowlisted export route.
- **Native session analytics:** `session.stats` can provide server-side activity, model, token, and tool aggregates. CodeNomad has per-session usage presentation but no native statistics dashboard or allowlisted stats route.
- **Plugin package management:** `plugin.check` and `plugin.update` are not exposed. A future inventory UI can add the read-like check, while executable package updates require an explicit trusted confirmation and mutation policy.
- **Plugin diagnostics and capabilities:** The current status panel projects active non-builtin plugin names. It does not yet expose failed-plugin errors, sources, update state, or `PluginFeatures`; those require a richer typed inventory UI rather than treating failures as healthy names.
- **Typed plugin RPC:** Generic RPC remains blocked until CodeNomad intentionally installs a reviewed plugin contract and can authorize each method; exposing the wildcard endpoint would bypass the proxy's narrow capability model.
- **Review-base and non-Git diffs:** Published `vcs.base` plus committed/base diff inputs can support branch review and arbitrary VCS backends. CodeNomad currently keeps its validated Git status, diff, stage, unstage, and commit boundary.
- **References and MCP resources:** Official prompt suggestions can attach configured references and MCP resources. CodeNomad's picker currently offers agents, files, and commands only.

Implement these when product scope requires them. They should use the existing native V2 APIs; no compatibility abstraction or V1 fallback is needed.
Expand Down
Loading
Loading