* fix(ui): refresh new session after reconnect
* fix(ui): preserve selected agent across reconnect
* fix(ui): harden new session reconnect state
* fix(ui): block unsafe reconnect retries
* test(ui): cover reconnect branch refresh
* fix(ui): block retry after transport loss
* style(ui): preserve new session LOC ratchet
* fix(ui): clear stale branch refs on reconnect
* feat(ui): redesign Channels page with guided channel setup wizard
Adds wizard.start flow=channels gateway RPC (additive protocol change) that
drives the shared channel-setup wizard (openclaw channels add) over the
session step protocol. Control UI Channels page becomes a card hub with
plugin-art covers, guided per-channel setup modal (WhatsApp QR pairing via
web.login.*, BotFather/Slack/Discord helper links), and a detail overlay
hosting the full schema config form.
* test(ui): cover channel wizard controller; refresh channels view test props
* chore(mock): use non-token-shaped wizard placeholder
* test(gateway): scanner-safe auth local in channels wizard test
* fix(ui): cancel gateway wizard session on channels page disconnect
* fix(ui): adopt browse-all channel pick and guard dirty config before setup
* fix(ui): cancel gateway session created by a stale wizard.start
* fix(ui): adopt multiselect channel picks; drop redundant String()
* fix(ui): track all wizard-adopted channels so WhatsApp QR runs regardless of pick order
* feat(gateway): report configured channels on terminal channel-wizard result
Replaces the Control UI's channel-adoption heuristic with the authoritative
outcome: the channels flow reports its actual selection after config commit,
the wizard session surfaces it as an additive channels field on the terminal
wizard.next result, and the UI keys WhatsApp QR linking off that.
* refactor(ui): align channels hub with the unified settings design language
Hub becomes settings-language rows (44px art tiles, status dots, uppercase
section headings, hairline groups) instead of a card gallery; the detail
overlay hosts the migrated per-channel settings sections with Run setup in
the header. Wizard dialog and tile/cover styles move to spacing tokens.
* chore(i18n): translate channels hub/setup strings; refresh raw-copy baseline
* refactor: satisfy LOC ratchet and dead-export gates
Split mock-dev channel/plugin fixtures into their own modules, move wizard
runner types/defaults to server-methods/wizard.ts, extract the channels page
wizard host and nostr profile HTTP ops, and unexport internal-only types
(incl. the unused config-form SECTION_META barrel re-export).
* chore(i18n): retranslate channels strings on rebased locale bundles
* fix(test): drop redundant String() in channels wizard e2e
* fix: address channel-wizard review findings
- lock wizard cancellation before durable installs/config writes
- report configured channel accounts on the terminal wizard result and
start WhatsApp QR pairing for that account (web.login accountId)
- forward request options through the browser gateway client so wizard
RPC timeouts apply
- render skipped setup as a no-change completion
- keep detail/advanced config reachable for unconfigured channels
- surface the dirty-config warning inside the detail overlay
- adopt the picked channel for wizard title/links in browse-all flows
* fix(ui): local wizard RPC timeout; translate no-change completion strings
* feat(ui): redesign Settings About with Clawd hero and community links
* fix(ui): route About hero product name through i18n
* chore(i18n): translate About page keys, resync raw-copy baseline
* fix(ui): keep About brand icons in a local module (LOC ratchet)
* chore(i18n): resync About locale bundles after rebase
Replaces the Settings -> Worktrees column grid (which collapsed to unlabeled
stacked cells at common window widths) with list-item rows: name + status
chip, repo/branch/owner meta line, right-aligned last-active + actions.
Rows sort by most recent activity. Deletes the now-unused generic .table
CSS family and stale column-header i18n keys.
The settings shell capped page headers at 1120px while narrow settings
pages use a 760px column, leaving the title floating left of the
content. The header now adopts the column width of the page below it
(760px default, 1120px for wide pages); fill-height surfaces without a
settings-page column are unchanged.
* 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
* refactor(ui): align plugin filter chips with the hub tab strip
Drop the segmented inventory meter above the installed filters and
restyle the filter chips to match the tab strip above them: same
radius, hover, active fill, and count badges. Removes the unused
pluginsPage.pulseLabel key and prunes generated locales.
* ci: trigger PR checks
* 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>
Config surface worktrees.cleanup.{maxCount,maxTotalSizeGb} (0/unset disables),
enforced by hourly gc, worktrees.gc RPC, and openclaw worktrees gc: least
recently active session/Workboard worktrees are snapshotted and removed until
count and total size fit. Manual worktrees, live locks, run leases, and active
session owners are never limit-evicted. Settings -> Worktrees gains a Cleanup
section with debounced, quota-aware stepper controls. Closes#106213.
* 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(usage): surface plan windows and account email in the chat context popover
The context-window popover now shows which account a session runs on:
provider usage snapshots carry an accountEmail resolved from the auth
profile when stored, the ChatGPT access-token JWT claims (openai/codex),
or the Claude CLI config file (~/.claude.json oauthAccount) for
keychain-synced logins. models.authStatus embeds it, quota groups keep
distinct accounts separate, and the popover renders the email under the
plan header for local CLI sessions and OpenClaw-configured subscriptions
alike.
* test(openai): assemble the fake usage JWT from parts
* test(usage): keep fixture tokens and identity plumbing scanner-safe
* fix(usage): verify the Claude CLI login before labeling usage with its email
Review findings: the CLI-config email fallback now requires the usage token
to match the cached CLI credential (and honors CLAUDE_CONFIG_DIR), so an
ambient login for another account never labels a snapshot; token-type
credentials propagate their stored email alongside oauth ones.
* style(anthropic): scanner-safe local for the CLI login read
* fix(usage): capture the Claude CLI account email on the credential
Review findings: reading ambient CLI config at usage-fetch time could not
verify keychain-only logins and could go stale across account switches.
The credential reader now captures oauthAccount.emailAddress next to the
credential it belongs to, the synced claude-cli profile carries it, and
the anthropic fetcher uses only the credential-borne identity.
* style(usage): scanner-safe credential fixtures and helper naming
* fix(auth): backfill the CLI account email onto existing synced profiles
Profiles synced before identity capture stay usable and skip CLI reads,
so upgraded installs would never gain the email until token rotation.
While the stored token material matches the CLI login, merge the
non-secret email onto the stored profile.
* style(test): scanner-safe email assertion
* test(auth): pin the no-reread invariant to identity-complete profiles
* 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.