* feat(ui): drop the desktop top bar in favor of sidebar navigation
The Control UI top bar only rendered the OpenClaw › agent › page breadcrumb
on desktop, duplicating the sidebar. Desktop now renders no topbar row; the
row returns solely as the chat split toolbar's backdrop. Narrow viewports
keep the header (drawer toggle, brand, command-palette search) and now show
the brand across the whole drawer range. Routing already uses pushState, so
browser back/forward traverses navigation.
* feat(macos): add back/forward controls to the dashboard window
Native titlebar buttons next to the traffic lights drive WKWebView history,
which carries the Control UI's pushState navigation; enabled state tracks
canGoBack/canGoForward via KVO and trackpad swipe gestures are enabled too.
Back/forward traversals to entries from a replaced gateway endpoint cancel
silently instead of opening a dead URL in the system browser.
* fix(macos): keep the dashboard drag strip off the web content column
With the desktop topbar row removed, the 28px mid-window drag region sat on
top of the content column (chat thread has no top padding) and swallowed
clicks as window drags. Shrink it to a 12px edge strip; the sidebar region
over the reserved native-chrome padding stays the primary drag surface.
* feat(usage): route claude-cli to Anthropic plan usage with plan label and billing
* feat(webchat): redesign context popover with plan-usage bars and API-cost gating
* fix(usage): match plan usage across CLI provider aliases and source plan label from synced auth profile
* docs(plugins): document plan metadata on usage auth token
* chore(usage): document provider-level billing attribution limits, fix overview test types
* fix(webchat): avoid map-spread in quota group collection
* test(webchat): deflake subscription popover reset fixture
* feat(webchat): apply model picker changes immediately, drop Save/Discard row
* test(webchat): keep unavailable-default picker assertion
* fix(webchat): render picker check marks only on the selected row
* fix(webchat): lock reasoning and speed while a model switch is pending
* fix(webchat): guard immediate picker patches against stale rollbacks
* docs(webchat): note send gating invariant in picker commit path
* feat(webchat): rare lobster pet variants modeled on real lobster genetics
Extends the sidebar pet's palette ladder with a rare tier: blue (7%),
calico with mottled spots (3%), deep-sea abyss with glowing eye glints
(2%), translucent ghost/albino (1%), and a bicolor split two-tone with
mismatched claws and antennae (1%). Weights loosely mirror real-world
odds (blue ~1 in 2M, calico ~1 in 30M, split ~1 in 50M, ghost ~1 in
100M). Offline mode still greys out every variant's glints. Drops the
unused --lob-dim inline variable.
* feat(webchat): retro grail variant honoring the classic OpenClaw logo
Adds a ~0.5% super-rare 'retro' lobster pet: classic logo red with a
white sticker-outline halo, one oversized raised claw with a pincer
notch (small left pincher stays), determined angled brows, a smirk, and
tall V antennae. The mega claw rides the .lob-claw--r group so wave and
snip acts swing it.
Replace the native <select> on the Agents page with an accessible custom
listbox dropdown (openclaw-agent-select) that renders each agent's avatar
image, identity emoji, or initial. Local /avatar/<id> routes are fetched
with the bearer credential and rendered as cached blob URLs when gateway
token auth is active, matching the chat-avatar contract.
Refs #102792, #57067
* feat(ui): merge voice level meter into the stop-voice pill and disambiguate stop controls
* docs(web): describe the live voice pill control in Talk mode
* test(ui): capture dark-mode voice control proof in talk e2e
* fix(ui): keep the live voice pill wide at phone widths
* fix(ui): show the stop glyph instead of a fake listening meter when a talk session errors
* test(ui): assemble the mock talk client secret so scanners do not flag it
* feat(webchat): composer lobster mascot perches on the empty prompt
A decorative OpenClaw lobster (smooth icons.lobster style) sits on the
composer edge while the prompt is idle, ducks away on typing/runs, and
plays personality-weighted idle acts (scuttle, wave, snip, hop, spin,
peek, bubbles, nap with z's; poke to startle). Look and personality are
seeded per session + page load: palette, size, antennae, accessory,
perch, facing, temperament. Decorative contract: aria-hidden, no config,
reduced-motion stays fully static, timers pause on hidden tabs and clean
up on disconnect. Hidden on short-landscape viewports where the composer
scrolls.
Closes#102754
* refactor(webchat): move lobster mascot from composer to sidebar status pet
The composer perch read too close to Claude Code's input mascot. The pet
now lives on the sidebar footer ledge above the connection dot and
mirrors gateway state: idle personality acts when quiet, a faster scurry
pool while runs are active, grey-eyed worried pacing while disconnected,
and a startle on every status flip. Composer wiring reverted; element
renamed openclaw-lobster-pet; hidden in the collapsed rail.
Drops the decorative layer from the sessions roster per maintainer feedback:
overview tiles, the drawer hero, detail sections, stat cards, checkpoint
cards, and the goal chip become flat token surfaces (no gradients, no inset
highlight shadows, no translucent color-mix glass); kind avatars and overview
icon chips lose their nested borders and read as flat tints; the skeleton
shimmer gradient becomes a plain opacity pulse. Focus rings, the drag-drop
target outline, and the live status dot ring stay - those are signal, not
decoration.
Adds client-capability-gated tool availability: gateway clients declare
capabilities at connect (new inline-widgets cap), chat.send stamps them into
the run context, and every tool assembly path (embedded runner, queued
followups, Codex app-server harness, plugin-only construction plans) drops
tools whose requiredClientCaps the originating client did not declare. The
Canvas plugin ships the first such tool, show_widget: agents pass SVG or an
HTML fragment plus a title; the plugin hosts it as a bounded, retention-scoped
Canvas document and returns the existing canvas preview handle, which web chat
renders as a sandboxed iframe fitted to the widget's reported content height.
Widget frames never get allow-same-origin (per-preview sandbox ceiling,
including the sidebar path) and the Canvas host serves widget documents with a
CSP sandbox header so direct navigation runs in an opaque origin. Verified
live end-to-end on a Testbox with gpt-5.5 (screenshots on the PR). CLI-backed
model backends do not carry client caps yet and stay fail-closed (#102577).
Closes#101790
Feedback pass on the sessions redesign: status pills become a plain colored
dot + label (green pulse for live, green dot with neutral label for done,
muted idle, red failed) in both the roster and the drawer hero, and the
Runtime column moves out of the table into the row drawer as Runtime (agent
runtime) plus Run duration. Runtime stays searchable. Responsive breakpoints
reflow for the 7-column roster and the actions column shrinks to the chevron
and row menu that landed in #102564.
Closes#102654
* fix(ui): align split-view toolbar segments with the pane edges they label
* fix(ui): track pane gutter and horizontal overflow in the split toolbar
* fix(ui): reset split toolbar scroll offset on mode flips and reserve docked terminal space
* fix(ui): scroll clipped split panes into view when their toolbar segment gains focus
Corner radius is owned by themes; drop the browser-local multiplier that
scaled all --radius-* variables (added in #49436). Stale borderRadius
localStorage keys are ignored and dropped on next settings write.
* fix(ui): keep workboard capture text truncation UTF-16 safe
Replace raw .slice(0, N - 3) with truncateUtf16Safe in
clampSessionCaptureText and clampSessionCaptureTitle to prevent
lone surrogates when emoji cross the truncation boundary.
Completes the UTF-16 hardening started in #101685, which added the
import but only covered buildCardSessionLabel.
* test(ui): cover workboard UTF-16 boundaries
* docs(changelog): note Workboard UTF-16 fix
* docs(changelog): leave release notes to release flow
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(ui): add session context menu to sessions page rows
Extract the sidebar session menu into a shared <openclaw-session-menu>
component and open it from a kebab button or row right-click on the
Sessions page, replacing the per-row unread/fork/pin/archive/workboard
icon buttons. Adds Open chat, Rename, Move to group, and per-row Delete
to the page; guards (main/global/active-run) now flow through
canArchiveSessionRow on both surfaces.
* fix(ui): retarget gateway session after deleting the current session from the sessions page
Deleting the currently selected session via the new row menu (or the
bulk bar) left gateway.snapshot.sessionKey pointing at the deleted key;
fall back to the agent main session, mirroring the archive path and the
sidebar delete path.
* fix(ui): keep streamed talk voice-turn transcripts readable
Talk voice turns collapsed to one character per line in the macOS app
(WKWebView) while streaming: WebKit never re-runs shrink-to-fit intrinsic
sizing for a grid flex-item as its content grows, so the turn froze at
first-delta width. Convert the turn to a flex row (same visual layout).
Assistant transcript deltas also merged through the user-ASR word-boundary
spacing heuristic, mangling verbatim fragments ("Chat G PT"). Assistant
entries now concatenate fragments verbatim, space only sentence-punctuation
joins, and accept both full-transcript and tail-fragment finals.
Fixes#102556
* fix(ui): keep assistant talk fragments strictly verbatim
Drop the sentence-punctuation separator: it invented characters in real
content (Version 1. 2, docs.openclaw. ai) and Google Live tail-finals
could never heal it. The punctuation-spacing heuristic remains user-only.
The Control UI sessions roster now leads with operator telemetry: overview
tiles (sessions / live / unread / tokens), per-kind row avatars with a live-run
dot, compact token values with context-usage meters (ok/warn/danger at
65%/85%), initial-load skeleton rows, and icon-led empty states. Stale token
snapshots render as ~approximations with a neutral meter and never drive
warning tones, matching the chat composer convention; the overview token sum
counts only known snapshots (~ when partial, n/a when none). Label chips
ellipsize instead of overflowing sticky columns; <=500px drops the avatar to
preserve the key column budget.
Closes#102517
The sidebar brand row (logo + wordmark) returned in 8295a12a83 but the
macOS-only titlebar mark from #100648 stayed, so the Mac app showed the
claw icon twice. Delete the sidebar-native-brand element and its
CSS gate; the sidebar brand row is the single brand surface and the
native titlebar strip holds only the traffic lights.