Commit graph

4 commits

Author SHA1 Message Date
Pascal André
248a4b3cd1
feat(status): show the session's /goal on one line above Tokens (#1228)
## Summary

OpenCode's built-in `/goal <objective>` keeps a session working turn
after turn until its reviewer judges the objective met, and pauses the
goal on its own after a stop, a failed run, stalls, its round limit or a
service restart. CodeNomad already listed `/goal` in the composer picker
(it comes from `command.list`) but showed nothing of the goal itself.

While the shown session has a goal, the **Status** tab now shows **one
line above Tokens**:

- a status badge: **Active**, **Paused** or **Complete**;
- the objective on one line with an ellipsis; hovering or focusing it
shows the full objective and the reason for the last change (why it
paused or was met), which is also the row's accessible description;
- the rounds used out of the maximum (`2/50`);
- **Pause goal** / **Resume goal** (one button that keeps focus when the
state flips) and **Clear goal**.

Without a goal, without a session, on a runtime without goal support or
after a failed read, nothing renders (no hint, no gap). The line is not
a Status section: it cannot be hidden or reordered.

## How it works

- **Read path:** the instance proxy refuses plugin RPCs, so `GET
/api/workspaces/:id/sessions/:sessionId/goal`
(`server/routes/session-goal.ts`, contract in
`opencode/goal-contract.ts`) validates the session ID, reads the
session, checks that its Location belongs to the workspace, then calls
the fixed `opencode.goal` `get` in that Location (8 s timeout, abort on
disconnect). It returns only the displayed fields; a missing RPC or
method answers `unsupported`, any other failure or malformed output
`unavailable`.
- **Freshness:** plugin RPC events already reach the UI through the
native event relay (as `rpc.codenomad.missions.changed` does), so the
store follows `rpc.opencode.goal.changed` for tracked sessions, with a
per-session version that drops replies older than a change already
heard. It also reads when the line is shown (Status visible, session
switch), on reconnect (coalesced) and after its own commands. It never
polls; a failed read keeps the last goal shown; deleting the session or
stopping the workspace drops it.
- **Commands:** the buttons reuse the composer's native command path
(`executeCustomCommand(..., "goal", "pause" | "resume" | "clear")`, with
session admission, instruction sync and the profile environment). They
are disabled while a command runs; a refused command (for example
pausing without an active goal) shows OpenCode's message.

## Contract

Upstream `@opencode/schema/goal` (anomalyco/opencode #59437, #59850;
shipped in 2.2.1, identical goal files in 2.2.3 and 2.2.5): RPC
`opencode.goal` with `get({ sessionID }) -> Goal.State | null` and event
`changed { sessionID, goal }` published in the session's Location on
every write. Documented in `dev-docs/OPENCODE_COMPATIBILITY.md`
("Session goal"); refresh policy row in `CACHE_REFRESH_CONVENTIONS.md`;
line convention in `UI_SHELL.md`.

## Help

New `conversations.md#goal` section (start, read, pause/resume/clear,
automatic pauses) and a sentence in `settings.md` about the Status line,
in English and all nine translations.

## Validation

- Root and server typecheck clean.
- Route tests 3/3; store tests 8/8 (with and without
`--conditions=browser`), including rapid session switching through the
shared background read queue and hidden sessions.
- Browser: `session-goal` (no goal, active with Pause, paused with
Resume and reason, Complete, Clear, command arguments, update after
`changed`, keyboard focus kept on the toggle across Pause and Resume,
line above Tokens) and `status-panel` 6/6; `status-tokens` and
`fixture-wiring` also pass.
- Ten-locale help 31/31.
- Native `scripts/test-session-goal-native.mjs` against the exact
OpenCode **2.2.5** CLI (twice): `get` returns null for a new session and
another workspace gets 403; `/goal pause` without a goal fails with
OpenCode's message; a goal set with the fake provider is paused by the
reviewer, then resumed and cleared, with the `changed` events relayed to
the owning workspace. CI runs it on the minimum and latest runtime
lanes; the store test joins the UI unit list.
- Full server suite on the rebased branch: 2,841 passed, 9 skipped; the
`plugin-controls` (same-target serialization) and `panel-extension-api4`
(deadline/abort) timing tests failed and the `index.test.ts` shutdown
test was cancelled under full-suite load. None of them touches this
change, and all three files pass alone (44/44).

Large files touched with 1–4 line changes:
`packages/server/src/server/http-server.ts` (~2,490 lines),
`packages/ui/src/lib/api-client.ts` (~860 lines).
2026-10-31 09:46:04 +01:00
Pascal André
5bc483d08d
feat(ui): toggle several rows at once by dragging across toggles (#1221)
Setting many rows of the same kind (Message content eyes, session
selection checkboxes, model visibility) took one click per row. Image
editors let users drag across a column of layer eyes instead; CodeNomad
now does the same.

Pressing a toggle and dragging out of it flips it, and every toggle of
the same column or list the pointer then crosses takes that new state;
toggles already in that state stay as they are. Releasing the button
clicks nothing, including the first toggle when the drag returns to it.
It works for mouse and pen; touch drags keep scrolling, and a touch
landing during a mouse drag leaves the sweep going.

Implementation: `lib/toggle-sweep.ts` installs one capture listener on
first use. Toggles opt in with `data-toggle-sweep={toggleSweepGroup(...)}`,
one group per column or list, so the Message content eyes and chevrons
sweep separately and exclusive selectors (tabs, presets) never sweep.
Each toggle changes through its own click, so its handlers, disabled
state and accessibility are unchanged; state is read from `checked`
(labels stand for their checkbox) or `aria-pressed`/`aria-checked`. The
drag is sampled every 4 px so fast moves skip no row, text selection
and native drag are blocked during a sweep, the trailing click is
swallowed, and a sweep whose release went elsewhere ends at the next
move without the button. `afterSweep` defers costly work until the
sweep ends.

Groups: Message content visibility and expansion columns, session
selection checkboxes, right-panel customization (built-in tabs and
Status sections; extension activation is a trust decision and stays
out), notification routing, model visibility, and the multi-select
options of agent forms and provider sign-in forms.

Message content dropped clicks made while a save was pending and showed
a row only once the server confirmed it, re-rendering the transcript
for each. Rows now show their requested state at once; a sweep saves
once, on release, so the transcript re-renders once. Saves run in
order and each batch of rows is sealed as it joins the queue, so a
later click never jumps ahead of a preset change nor rides along with a
sweep still held.

Notification routing shows each box at once, writes the whole table
once per sweep, runs its writes one after another so an older table
never lands last, and asks for System permission once per gesture.
Model visibility already showed pending changes and wrote them in
order.

Validation: browser regressions in transcript-presets (a single jump
across four eyes with slow saves, shown during the drag with nothing
saved, then one write on release; a hidden row that stays hidden; a
release back on the origin; the chevron column alone; a held sweep not
persisted by queued saves; a row clicked after a preset applying after
it), session-search (both directions, a release over a row title
opening no session, a lost release leaving no sweep, a touch mid-drag)
and notification-routing (one write per sweep, serialized writes with
the last winning, one permission request). Each fails with its part of
the fix removed. Two gatekeeper rounds ended with no P0-P2 finding; the
user validated the behaviour in a separate Electron dev instance.
dev-docs/UI_CONVENTIONS.md and dev-docs/TRANSCRIPT.md record the
convention, and the help describes the gesture in all ten languages.
2026-10-31 00:49:50 +01:00
Pascal André
1dabb5110f
docs(help): bring the user guide back in line with the current app (#1079)
docs(help): bring the user guide back in line with the current app

The help was written on 2026-10-14 and about forty user-visible changes merged since without touching it, so several pages described controls that no longer exist: Missions still listed four playbooks and a Save step for preferences that now save on each change, the depth editor kept its old names, notifications still had their former switches, and voice input pointed to a microphone button that is not in the composer.

Every English page was re-checked claim by claim against the current labels (English UI messages) and behaviour, then the nine translations were updated to match, using each locale's own UI labels. Notable corrections: Missions (Compare two models, autosaving defaults with Apply to / Global / Reset, Subsession nesting depth, prepared hand-offs, retired-task interrupts, the named causes of "Mission Control is unavailable", standalone server support); conversations and history (reasoning/tool durations, token lines adding up the cleanup selection, presets including Nothing, timeline markers following Message content, in-place reveal of long text, Open in session, focus after send, Queue message / Steer now, Retrying); settings (notification routing table and defaults, Stats, Mode, info tips, Server setting badge, localized native menus); shortcuts (72 actions, Clear shortcut, Ctrl/Cmd+K); voice (More actions entries, Save status); files (live Workspace refresh, Show 200 more); OpenCode, install, server and updates (2.1.1 recommended, OpenCode 2 beside an older installation, version card first, private loopback listener for Missions, preview releases offered once all builds are published); providers, integrations, browser, SideCars, desktop and remote pages. Translations also repaired older drift, mainly the project-tab closing passages.

The Chat settings table header (Default state, its title, item column, the thinking row and its accessible label) was still English in eight locales; it is now translated so the help can cite what users see. AGENTS.md now asks every PR changing user-visible behavior to update the affected help pages in the same PR, since CI validates structure and links but not whether the text still matches the UI; it also corrects the needs-input notification default. USER_HELP.md no longer claims native menus stay English.

Validation: npm run test:help (all ten locales, 25 pages, heading ids unchanged), a full help build, and the i18n and transcript-visibility unit tests.
2026-10-21 14:38:29 +02:00
Pascal André
c0d53f13d4
docs(help): multilingual help site with in-app Help menu and palette entry (#859)
## Summary

A complete public CodeNomad help site in the app's ten languages,
published on GitHub Pages (https://neuralnomadsai.github.io/CodeNomad/)
and opened from the app.

- **Content:** 25 pages in 8 sections (Start, Workflows, OpenCode setup,
Remote access, Customize, Extensions, Desktop, Help), written for
OpenCode power users and covering the current version only. It includes
everything user-relevant from `README.md`, `packages/server/README.md`
(server, network, auth, TLS, Remote Control) and the user-facing
dev-docs, with flags, defaults, labels and safety boundaries checked
against source.
- **Languages:** en, fr, de, es, he (RTL), ja, ne, ru, tr, zh-Hans.
Interface labels reuse each locale's own app strings; native desktop
menu names, code, commands, flags, paths and anchors stay unchanged.
- **Site:** `scripts/build-help.mjs` renders
`docs/help/<locale>/<slug>.md` with translated chrome
(`docs/help/i18n/`), stable `{#id}` anchors identical across locales,
same-article language links with hreflang, a root language chooser, and
a first-party offline per-locale search (Ctrl/⌘+K, substring matching
for CJK, Devanagari and Hebrew, hidden without JS). No analytics or
remote resources.
- **Validation:** `npm run test:help` fails on any missing page or
string, heading drift, broken link/anchor/asset or invalid search
target. `.github/workflows/help-pages.yml` validates PRs and deploys
only from `dev`.
- **App entry points:** one `open-help` command for the palette and a
new **CodeNomad Help** item at the top of the Electron and Tauri Help
menus. It opens the help in the invoking window's UI language through
the existing external opener, falls back to the root URL for windows
without the handler, and reports failures without replay.
- Documents the strict boolean environment-variable rule from #959 and
the updater entry points from #960.

## Review

Delivered as a CodeNomad mission with independent review loops, each to
zero findings:
- English content audit (5 passes, 14 fixes) and technical gatekeeper (2
passes) before translation.
- After translation: QA, UX review, and a final multilingual gatekeeper
(35 translation/label findings fixed, then verified), plus a visual fix
for the search focus ring and a verified sync with #960.

## Validation

- `npm run test:help`: 9/9 (25 pages × 10 locales, strict, including
negative tests); `npm run build:help`: 250 pages.
- `node scripts/test-help-browser.mjs`: 44/44; Playwright: 500 page
visits at 390 and 1280 px, all locales, no overflow, no external
requests.
- UI help-command and desktop-updates browser tests 19/19; help URL unit
tests; Electron menu tests; UI and Electron typechecks; Tauri `cargo
test` 272/272.
2026-10-14 22:44:56 +02:00