* fix(terminal): harden node PTY relay and terminal session lifecycle
Pin node relay invokes to the authorized node connection (ROUTE_CHANGED
guard), stop node-side input writes to settled/dead PTYs, finalize
sessions on resize failure like write failure, keep surrogate pairs
intact across scrollback and relay prelude truncation, bound the relay
pre-registration output buffer, and buffer Control UI keystrokes typed
while terminal.open/attach is in flight so slow node-relay opens no
longer drop input. Correct the terminal.data seq doc to its real
contract and pin seq monotonicity, seq-gap bounds, input coercion, and
runner event routing with new tests.
* refactor(terminal): extract startup input buffer and fix CI findings
Move Control UI startup input buffering into terminal-startup-input.ts
(LOC budget), drop a stale session-manager comment, remove a map
spread, and fix test typings for event frames and typed mock access.
Catalog session rows (sidebar context menu + click), the built-in viewer
header, and a new "Open Codex/Claude sessions in" preference can launch the
native CLI (codex resume / claude --resume) in the operator terminal on the
machine that owns the session.
- Gateway-local sessions spawn through the existing terminal launch policy
(sandbox/enabled gates preserved) with the resume command in the session cwd.
- Paired-node sessions run through a new seq-ordered node PTY relay: a
duplex node-host command streams PTY output via node.invoke.progress and
receives keystrokes/resize via a new node.invoke.input event, behind the
unchanged terminal.* client protocol (TerminalSessionManager gains a backend
abstraction; node relay reuses the streaming-invoke controller).
- Owner boundary: each plugin owns its resume command and builds argv from a
validated thread id; the gateway routes node opens through the node command
allowlist and plugin invoke policy (no advertisement-only trust), and nodes
re-verify session eligibility before spawning.
- UI setting catalogOpenTarget + canOpenTerminal capability gate every entry
point; capability requires the owning host to actually have the CLI.
Node PATH is normalized before command-availability probes, Windows .cmd/.bat
shims spawn via ComSpec, and catalog terminal opens reattach persisted tabs
before opening the new tab.
* refactor(ui): add Web Awesome foundation
* refactor(ui): migrate shared menus and chat controls
* refactor(ui): migrate page controls to Web Awesome
* refactor(ui): migrate workboard controls to Web Awesome
* fix(ui): harden Web Awesome interactions
* test(ui): type Web Awesome dropdown state
* fix(ui): preserve menu focus and selection
* fix(ui): restore agent picker focus
* fix(ui): track keyboard menu dismissal
* fix(ui): ignore stale session menu closes
* fix(ui): complete Web Awesome keyboard migration
* fix(ui): repair Web Awesome lockfile graph
* docs(ui): keep release note in PR body
* fix(ui): satisfy Web Awesome migration gates
* perf(ui): keep Web Awesome theme in startup stylesheet
* fix(ui): replace reopened Web Awesome session menus
* fix(ui): keep replacement session menus open
* feat(ui): add settings design-language foundation (tokens, settings.css, helpers)
* refactor(ui): move settings workspace shell styles into settings.css
* refactor(ui): migrate all settings surfaces to the unified settings design language
Every settings surface (General quick+advanced schema form, Appearance,
Communications, Automation, MCP, Infrastructure, AI Agents, Channels,
Connection, About, Profile, Model Providers, Plugins, Skills, Skill
Workshop chrome, Devices, Worktrees, Debug, Logs, Activity, Automations,
Sessions, Usage chrome) now renders through the settings-ui helpers and
settings.css: sections as plain uppercase headings outside one flat
group surface, hairline-divided rows, a single toggle/segmented/status
vocabulary, status dots instead of pills, and no cards-in-cards or enter
animations. Deletes the qs-*/cfg-*/config-section-card/account-card/
plugins-row/cron-editor-card/session-badge/agent-pill families and
orphaned shared rules.
* style(ui-docs): format design-system docs
* fix(ui): make activity statusKind lookup exhaustive for consistent-return lint
* fix(ui): address settings design-language review findings
Label-wrapped toggle rows with required accessible names, named selects
and cron form controls, busy-disabled segmented controls with restored
theme-transition anchors, collapsible skill groups, semantic plugin
headings, gateway connected status, paired-devices empty state, logs
subtitle, cron prompt help, and keyboard-accessible session goal
objectives.
* fix(ui): harden settings primitives per review round two
Wrap row action clusters, fully-inset nav focus ring, restore number
steppers in the schema form, cap node capability lines, keep the named
Activity region landmark, and associate cron help text via the row
label.
* fix(ui): skip empty section headings and fix the toggle doc example
* docs(ui): import renderSettingsToggleRow in the settings example
* fix(ui): rename number stepper delta to avoid shadowing
* fix(ui): preserve sidebar collapsed state when group rename/delete fails
* test(ui): cover group rename/delete rejection error publish
* fix(ui): harden session group mutations
* fix(ui): guard group mutations across reconnects
* fix(ui): keep session group result internal
* style(ui): format sidebar tests
* fix(ui): preserve confirmed group mutations
* chore: keep release notes out of contributor PR
* style(ui): refresh session test formatting
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(ui): fold agent-menu help links into a Help flyout
Docs, Get help, Discord community, and View changelog move from
top-level agent-menu rows into a Help flyout submenu (hover on mouse,
click elsewhere; right/left/overlay placement by available space).
* chore(ui): translate agentChip.help across locales
* feat(ui): dismissible sidebar attention chips
* fix(ui): use toSorted for attention signatures
* refactor(ui): move attention dismissal store to its own module
* fix(ui): make attention dismissals safe across tabs
* style(ui): drop redundant String() in storage mock
* fix(ui): prune dismissals only after fresh data and key overdue snoozes by incident
* feat(ui): move Devices page into settings and redesign device inventory
- Devices now lives at /settings/devices (System group); /nodes stays as alias
- per-device form-factor icons (phone/browser/terminal/machine) with status dot
- equal-height header actions; stale cleanup and pairing only
- drop manual Refresh button; page already auto-refreshes via presence events + 30s poll
- dynamic connected/pending summary replaces static card subtitle
* style(ui): oxfmt pass + loading empty state for devices inventory
* chore(ui): sync locale bundles for devices settings copy; docs point at Settings → Devices
* refactor(ui): split device tile + pending rows out of view-inventory; drop unused icons
Keeps the TypeScript LOC ratchet green: view-inventory.ts returns below its
baseline and icons.ts sheds unused folderOpen/micOff/volumeOff glyphs.
* fix(ui): ratchet LOC baseline, drop unused DeviceIconSource export, settle devices settings copy
- baseline: icons.ts 693, view-inventory.ts back under the 500 ceiling
- subtitles.nodes now describes the settings page; nav tests updated
- locale bundles resynced (fallbacks=0)
* refactor(ui): rename token row param for reviewer-tool clarity
* fix(ui): collapse device rows in narrow containers and classify the TUI as a terminal client
Review findings: the nodes-entry grid override beat the shared 560px
list-item collapse, overflowing phone-width rows; openclaw-tui connects
with mode ui so only its client id marks it as a terminal.
* chore(ui): translate pending locale keys after rebase (fallbacks=0)
* feat(ui): restyle composer send button as filled accent circle
The send button was an outline ghost (transparent bg, 1px accent border,
outlined paper plane) while every other primary action uses the solid
.btn.primary fill. Send now matches: solid accent circle, white arrow-up
glyph, accent-hover + glow on hover, muted fill when disabled. Stop and
voice variants drop their borders and escalate via fill strength instead.
* fix(ui): keep send button focus ring visible under hover
Hover rules now set box-shadow and out-specify :focus-visible, so a
box-shadow focus ring vanished for keyboard users hovering the control.
Use an offset outline instead; hover shadows cannot override it.
* chore(ui): compact arrow icons to satisfy TS LOC ratchet
icons.ts is over the 500-line ratchet cap, so the new arrowUp entry
must not grow the file; merge each arrow's two paths into one
(identical rendering) for a net -4 lines.
The composer gear popover mixed misplaced, mislabeled, and dead controls.
Replace it with a three-row View menu (Reasoning, Tool calls, Keep
commentary); drop the manual-refresh machinery and the auto-scroll modes
(near-bottom stays the only behavior); move the cron-session filter to the
sidebar sort menu as a working persisted toggle; move the send shortcut and
microphone picker to Settings -> Appearance -> Chat; delete the ephemeral
voice/model/sensitivity overrides in favor of the server talk config.
Settings keep an in-memory fallback when localStorage is blocked, and talk
sessions re-read persisted settings at launch.
* fix(ui): make catalog sessions selectable in place and continuable from desktop-app sources
- sidebar: no phantom chat row for catalog keys, active highlight on catalog rows,
adopted sessions render as their live row inside the catalog group (deduped from
the regular list), host subtitle only for paired-node rows
- chat: view-only banner only after catalog metadata proves canContinue=false
- chat->sidebar continued event binds the adopted row before the next catalog poll
- anthropic: local claude-desktop sessions share the ~/.claude/projects store, so
list them as continuable and allow catalog continue to fork-resume them
* refactor(ui): extract catalog row rendering and lookup into shared modules
Keeps app-sidebar/chat-pane/anthropic session-catalog under the LOC ratchet by
moving cohesive blocks: sidebar catalog groups + adopted-row binding into
app-sidebar-session-catalogs.ts, the catalog metadata lookup into
catalog-key.ts, and the Claude node-host commands into
session-catalog-node-commands.ts; ratchet baselines down for all three.
* fix(ui): explain the disabled composer when catalog metadata cannot resolve a session
* style(ui): keep chat-pane within the LOC ratchet
* fix(anthropic): keep catalog node-command helpers module-local and repin SDK surface
The node-command module now owns its capability constant, projects-store
probe, and params parsing, so the catalog engine exports nothing new.
Repin the SDK surface budgets (+1 export/+1 callable landed on main
without a pin bump); unexport the UI helper types knip flagged.
The previous commit accidentally truncated app-sidebar.ts to an empty
file while fixing the missing IconName import from #105850. This
restores the full component with the corrected icons import.
* feat(ui): docs, help, Discord, and changelog links in the agent chip menu
* chore(i18n): normalize locale bundle metadata after rebase
* refactor(ui): move the agent menu links into the nav-menus module for the LOC ratchet
* Revert "refactor(ui): move the agent menu links into the nav-menus module for the LOC ratchet"
This reverts commit 0e4d72f4c6.
Streaming assistant replies in the Control UI now render markdown live: the in-flight tail is repaired with remend (zero-dep, Apache-2.0, extracted from Vercel's Streamdown) before the existing markdown-it + DOMPurify pipeline, and open code fences stream as live-highlighted code blocks. Deletes the plain-text streaming tail path (toStreamingPlainTextHtml) and extracts an uncached render core so tail deltas stop churning the markdown LRU cache. Closes#106053
The sidebar's expandable "MORE" section (uppercase header, chevron, persisted
sidebarMoreExpanded pref, dedicated "Edit pinned items" row) collapses into a
single More nav row that opens a transient popup menu with the unpinned
routes, dynamic plugin tabs, and the pin editor entry. Menu rows are real
anchors, so Cmd/middle-click keeps native link behavior; the More row carries
the active highlight when the current route lives inside the menu. The
sessions list gets the reclaimed vertical space, and one persisted setting is
gone (stale keys are ignored on load). The nav rows and menus move to
app-sidebar-nav-menus.ts to keep app-sidebar.ts under the TS LOC ratchet.
Docs: docs/web/control-ui.md sidebar-navigation section updated.
Fixes#105905
* feat(ui): make the sidebar agent chip body the menu trigger
The footer chip's big name/avatar area now opens the agent menu directly
and the chevron toggle button is removed. New-session plus stays as the
only trailing action; the unread dot moves onto the chip body.
* chore(ui): prune removed agentChip.openConversation key from locale bundles
* fix(ui): keep the agent name in the chip menu trigger accessible name