Commit Graph

4654 Commits

Author SHA1 Message Date
Peter Steinberger 6b565c047f feat(ui): show unsent-draft pencil on sidebar session rows (#121476)
* feat(ui): show unsent-draft pencil on sidebar session rows

Typed-but-unsent composer text now surfaces as a pencil badge on the
owning session's sidebar row (and Home row) once you switch away.
Draft persistence now notifies stored-outbox subscribers so the
indicator appears and clears live. The active session suppresses the
badge since its composer is already visible.

* chore: refresh merge ref for CI against current main

* chore: refresh merge ref against healed main

* fix(ui): notify draft indicator only on presence transitions

Unconditional notify on every draft persist let outbox-projection
subscribers re-persist a stale pane over a newer draft (chat-state
route-fallback invariant). The sidebar pencil only consumes presence,
so notify on empty/non-empty transitions only.
2026-08-10 04:53:15 -07:00
Peter Steinberger 0a6a1d9419 fix(gateway): unify media privacy in chat history (#121490)
* fix(gateway): unify media privacy in chat history

Centralize image, audio, video, and persisted media-fact privacy at the shared Gateway history projection. Remove duplicate sessions_history redaction, validate managed media claims canonically, and keep safe media-only user turns renderable.

* test(gateway): type history RPC integration

* test(agents): align history fixture with gateway projection
2026-08-10 03:30:44 -07:00
Jesse Merhi e97c3450a2 fix(ui): preserve negotiated paired cancel 2026-08-10 20:15:29 +10:00
Jesse Merhi 66a44643d4 test(ui): follow browser cursor policy 2026-08-10 20:15:29 +10:00
Jesse Merhi 29827ea0ce fix(ui): preserve disabled cancel hover 2026-08-10 20:15:29 +10:00
Jesse Merhi cf1ac91609 test(ui): assert structured receipt count 2026-08-10 20:15:29 +10:00
Jesse Merhi ce852958bd test(ui): cover mounted and receipt wizard submissions 2026-08-10 20:15:29 +10:00
Jesse Merhi 402305f003 fix(ui): align wizard control states 2026-08-10 20:15:29 +10:00
Jesse Merhi 66196b7bf7 Control UI: pair wizard cancel and primary actions 2026-08-10 20:15:29 +10:00
Jesse Merhi 767bbb49d7 Control UI: preserve disabled wizard hover states 2026-08-10 20:15:29 +10:00
Jesse Merhi 84ad551e27 Control UI: size wizard actions for touch 2026-08-10 20:15:29 +10:00
Jesse Merhi 0368d89ebd Control UI: clarify wizard interactions 2026-08-10 20:15:29 +10:00
Peter Steinberger c3775a8f21 test(ui): stabilize checkbox alignment proof
Poll both fixed-width selection rails together and compare their trailing edges so the browser assertion measures settled popover geometry instead of sampling different points in the open animation.
2026-08-10 03:08:24 -07:00
Peter Steinberger c68606b812 fix(ui): align automation session checkmark
Keep Web Awesome checkbox semantics while rendering the selection state in the sidebar filter shared trailing rail. Add mocked-browser coverage for the hidden native mark and one-pixel alignment invariant.

Co-authored-by: Vyctor H. Brzezowski <krzyszchweski@gmail.com>
2026-08-10 03:08:24 -07:00
Jesse Merhi 260628f4fa perf(ui): keep cancel capability within startup budget 2026-08-10 20:01:24 +10:00
jesse-merhi 2300de7162 Gateway: negotiate Custodian wizard cancellation 2026-08-10 20:01:24 +10:00
Jesse Merhi 890a4b0089 Control UI: add direct Custodian wizard cancel 2026-08-10 20:01:24 +10:00
Peter Steinberger 890a5e895b fix: onboarding stalls or loses progress during interrupted setup (#121415)
* fix: harden onboarding recovery flows

Amp-Thread-ID: https://ampcode.com/threads/T-019fe94a-32da-757b-bff1-caefb6302cd1

* refactor: consolidate onboarding state lifecycles

Amp-Thread-ID: https://ampcode.com/threads/T-019fe94a-32da-757b-bff1-caefb6302cd1

* test: model canonical onboarding config writes

Amp-Thread-ID: https://ampcode.com/threads/T-019fe94a-32da-757b-bff1-caefb6302cd1

* fix: compose onboarding locks across gateway surfaces

Amp-Thread-ID: https://ampcode.com/threads/T-019fe94a-32da-757b-bff1-caefb6302cd1

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-08-10 02:45:14 -07:00
Peter Steinberger 3a1f645228 fix(ui): prevent duplicate initial prompts during session startup (#121498)
* fix(ui): unify initial prompt handoff projection

Route create-time prompts through the canonical session projection so live persistence and delayed history adopt one stable bubble. Preserve inline attachment content across reconnects and authoritative identity handoff while removing duplicate matching and post-reducer mutation paths.

* fix(ui): preserve initial prompt projection contracts

* fix(ci): restore exact-head validation
2026-08-10 02:38:33 -07:00
Peter Steinberger 69a414bd0c test(ui): consolidate session pin coverage (#121539) 2026-08-10 02:37:47 -07:00
Peter Steinberger 019bd9a59f fix(ui): keep typing indicator in the composer footer (#121438)
* fix(ui): keep typing indicator in the composer footer

Keep typing activity in the fixed-height Control UI composer footer so collaborators no longer shift the textarea.

Render up to three author avatars before the localized typing label.

* test(ui): target the typing label in E2E

* fix(ui): hide typing avatars from the status announcement

Status text already names typers; role=img avatars made screen readers announce names twice. Addresses ClawSweeper P2.
2026-08-10 02:20:50 -07:00
Peter Steinberger f6298bf84d fix(workers): preserve disappeared-worker failures across restarts (#121122)
* fix(workers): persist placement terminal failures

* fix(workers): refresh placement protocol clients

* refactor(workers): isolate error formatting

* fix: integrate cloud terminal state with current main

* chore(plugin-sdk): refresh API baseline

* refactor(ui): inline one-use cloud terminal-reason banner helper

Keeps the Control UI startup JS bundle inside its 317 KiB gzip budget
(the helper + type-only import tipped it by 16 bytes).

* refactor(ui): trim terminal-reason lookup to type-erased optional access

Recovers the last gzip byte of the Control UI startup budget
(324609 B vs the 324608 B limit).
2026-08-10 00:36:52 -07:00
Peter Steinberger 3f08684989 feat(control-ui): preview text attachments inline with a download action (#121400)
* feat(control-ui): preview text attachments inline with a download action

Document attachments in chat now render a unified attachment card: text-like
files get a bounded, cached inline preview plus an explicit download button;
other documents get the same card chrome with a download action.

* fix(control-ui): split document preview module and e2e file under lint caps

* fix(control-ui): keep availability helpers module-internal after resolver move

* fix(control-ui): cancel document preview streams at the preview budget

Reads at most the preview budget from the response body and cancels the
reader, so unknown-size or endless text attachments cannot buffer fully
just by rendering; adds a streaming regression test.

* fix(control-ui): slice preview chunks to the byte budget before decoding

A blob or misbehaving source can deliver one giant chunk; cap the bytes
handed to TextDecoder at the remaining preview budget so rendering never
allocates or scans the full payload. Adds a single-oversized-chunk
regression asserting the decoded byte bound.

* test(control-ui): track decoded bytes via a TextDecoder subclass

Replaces the prototype spy that tripped typescript(unbound-method) in
oxlint with a stubbed tracking subclass; unstubGlobals restores it.
2026-08-10 00:30:56 -07:00
Peter Steinberger e0262e6aa4 feat(ui): add people session sorting (#121470)
* feat(ui): add people session sorting

Offer People sorting in the Control UI session sidebar when the Gateway reports multiple session-sharing identities.

* fix(ui): restore sidebar translation dependency

* fix(ui): preserve session sort ordering contracts
2026-08-10 00:18:02 -07:00
Peter Steinberger a7d61fc114 fix(ui): remote sessions show pasted titles and final replies (#121467)
* fix(ui): refresh remote session titles and final replies

* test(ui): split sidebar presentation e2e coverage
2026-08-10 00:13:55 -07:00
Peter Steinberger 32c740384a improve(ui): show dashboard switcher only for real board content (#121480)
* improve(ui): remove builtin observer dashboard injection

The Control UI fabricated a synthetic Observer tab + read-only builtin
widget on top of the real board snapshot whenever a session produced an
observer digest, which forced hasBoard=true and surfaced the prominent
chat/split/dashboard face switcher for sessions that never had a
dashboard. The dashboard face only duplicated the chat-rail observer HUD.

Delete the injection path, the builtin widget kind and its readOnly
concept, the pane-local ObserverDigestHistory, the observer widget
renderer/styles, and the orphaned chat.observer.* i18n keys. The session
header face switcher now appears only when the board provider has real
content. Chat-rail observer HUD, sidebar narration, config observer
settings, and protocol surfaces are unchanged.

* test(ui): give plugin widget cell chunk waits a real timeout

The four vi.waitFor calls on the lazily imported workboard-card element
used the 1s default, which a cold transform exceeds on loaded machines;
observed flaking 1-3 of 4 tests depending on host contention.
2026-08-10 00:13:51 -07:00
Peter Steinberger f9730534fb feat(ui): turn the widget pin action into an icon button (#121419)
* feat(ui): turn the widget pin action into an icon button

The chat widget card's pin control was a bare text button ("Pin to
dashboard" / "Pinning…" / "Pinned"). It is now a 26px icon button using
the shared Lucide pin icon, styled like the adjacent widget-actions
trigger, with the accent color marking the pinned state. Labels move to
title/aria-label so tooltips and accessible names are unchanged, and the
two pin handlers collapse into one shared helper. The session-dashboard
e2e now records hover/pinned proof screenshots under
.artifacts/control-ui-e2e/workboard-pin when OPENCLAW_UI_E2E_RECORD=1.

* test(ui): assert pin button state via aria-label

The pin control no longer carries text content; the pinned state is
exposed through aria-label/title, which is what the widget-card unit
test now asserts.

* fix(ui): trim pin action label updates
2026-08-10 00:13:23 -07:00
Peter Steinberger 3621c4b511 feat: browse and start sessions inside agent workspaces (#121417)
* feat(gateway): allow workspace-contained browsing

* fix(ui): preserve canonical workspace selections
2026-08-09 23:57:46 -07:00
Peter Steinberger 98298a9f55 fix(ui): commentary survives refresh after steering (#121449)
* fix(ui): preserve commentary across steered refreshes

Retain keyed active-run commentary and its original timestamps so Control UI reloads keep progress on the correct side of persisted steering messages.

Closes #121448

* refactor(ui): simplify active-run replay coverage

Reuse the run-bound lookup and remove redundant transcript, hydration, and browser-test setup while preserving the steering refresh regression contract.
2026-08-09 23:55:38 -07:00
Peter Steinberger 6b365fa5d3 fix(ui): keep own-message sender name stationary on footer hover (#121406)
The persistent-identity footer for the viewer's own messages is
right-aligned, so the hover-revealed relative timestamp used to push the
always-visible sender name sideways. Flip the visual order with CSS
`order` so the time slots in to the left of the name and the name stays
pinned under the avatar. Peer footers are left-aligned and already
stable; DOM (screen-reader) order is unchanged.
2026-08-09 23:53:49 -07:00
Peter Steinberger a6a464f4c0 feat(terminal): fullscreen /terminal route and dot-matrix claw intro (#121451)
* feat(ui): add fullscreen /terminal route and open button

* feat(terminal): dot-matrix claw intro banner and CLI mascot

* fix(terminal): satisfy lint and wizard-header assertions for the new mascot

* ci: account for fullscreen terminal startup bytes

* test(terminal): split session manager coverage
2026-08-09 23:52:59 -07:00
Peter Steinberger 98d1397a9d fix(ui): keep chat sidebar opening transparent (#121464)
Keep transient sidebar columns transparent while preserving explicit panel and discussion surfaces.\n\nFixes #121462. Related to #113712 and #121435.
2026-08-09 23:42:15 -07:00
Vyctor H. Brzezowski 35e8798828 fix(ui): offer undo and a named recovery path when hiding a sidebar section (#121385)
* fix(ui): offer undo and a named recovery path when hiding a sidebar section

Hiding a session catalog section wrote the preference and returned nothing:
the section vanished, no undo was offered, and the only standing recovery
(Settings > Appearance > Sidebar) was never named anywhere near the action.

Announce the outcome at the producer. hideSessionCatalog now names the section
that disappeared, offers Undo, and carries the Settings path in the toast text
for after it times out. The hidden-catalog set gets one owner,
setStoredSessionCatalogHidden, so the sidebar, the undo, and the Settings
toggle all mutate the same authoritative read instead of each rebuilding the
set from its own copy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(ui): align the automation-sessions checkmark with its popover siblings

Every selection state in the sidebar sessions filter popover reads on the
trailing edge, but "Show automation sessions" used Web Awesome's
type="checkbox" checkmark, which its template renders before the label. The one
row in the menu that is a checkbox rather than a radio put its check on the
opposite side of the row from every neighbour.

Hide the leading part and render the shared session-menu__check in the details
slot, so the row matches GROUP BY / SORT BY / STATUS while Web Awesome keeps
owning the menuitemcheckbox role and aria-checked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(ui): stop toasts from ellipsing away their recovery instruction

app-toast__message was nowrap + ellipsis, so any message longer than one line
lost its tail. That is fine for a bare acknowledgement and wrong for a toast
that tells the operator how to undo or where to re-enable something: the part
that gets cut is exactly the part they need.

Wrap instead, clamped to three lines so a pathological string still cannot grow
the toast without bound.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor(ui): make the hidden-catalog writer module-private

With setStoredSessionCatalogHidden owning every hide/show, the raw
storeHiddenSessionCatalogIds writer no longer has a caller outside its own
module and the deadcode:exports gate flagged it. Drop the export rather than
keep a second public way to write the same preference, and move the two tests
that used it onto the owner they are actually meant to exercise.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(ui): anchor toasts to the bottom-right corner

Toasts were centered over the content column, which on chat routes puts a
passive notification directly on top of the composer. Move the shared
.app-toast container to the trailing bottom corner, the standard desktop
placement for passive notifications.

Anchoring it there alone would trade one overlap for a worse one: at a 20px
inset the toast lands exactly on the composer's mic and send controls. Chat
routes therefore lift it above the composer via --app-toast-inset-bottom on
.shell--chat, which the toast host inherits as a child of .shell. Routes
without a composer keep the tight 20px corner.

Phone widths keep the full-width bottom idiom, and now get it properly: the
centered toast used to collapse to its content width (200px measured at 390px
viewport), so the explicit left/right insets also widen it to 366px.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(ui): drop the chat-route toast lift

The corner toast no longer clears the composer on chat routes. Overlapping
the composer region is the accepted trade for one placement rule on every
route, so the route-specific 140px lift and its custom property are gone and
every surface uses the same 20px safe-area corner inset. Wrapped toast text
balances its lines now that the message can run to two.

* fix(ui): open the sidebar recovery path from its toast

Naming "Settings > Appearance > Sidebar" in the hide toast still left the
operator to find it. The recovery sentence is now a link built from the
settings-search destination that already describes that block, so the toast
opens the re-enable list instead of describing where it lives. Toast messages
accept a template for that.

* revert: leave the automation-checkmark repair to its own PR

#121429 fixes the same Web Awesome checkmark alignment on its own, with
browser regression coverage this branch does not carry. Two PRs owning one
CSS part is worse than losing the drive-by, so this keeps to the hide and
recovery invariant.

This reverts commit 6b1438a5be4bb1c9dd4c2ce9cd7d0dd54e77cb00.

* fix(ui): let the hide outcome reach a phone operator

On a phone the sidebar only exists inside the navigation drawer, and that
drawer is a modal dialog: a toast raised behind it is both occluded and inert,
so hiding a section there stayed exactly as silent as before this branch.
Promoting the toast into the top layer only fixes the paint half — a modal
dialog inerts everything outside it, so Undo and the recovery link still take
no clicks. So the hide hands the drawer back to the shell first, through a
seam the sidebar can use for any outcome that belongs on the main surface.
It is a no-op wherever the drawer is not open.

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 03:41:48 -03:00
Peter Steinberger 9d77e5c3b7 feat(ui): move chat view toggles into a header session menu (#121452)
* feat(ui): move chat view toggles into a header session menu

Control UI: replace the chat composer eye-icon View dropdown with a Claude Desktop-style three-dot session menu at the far right of the chat pane header. The menu adds Open in for editor/worktree sessions, Rename, the three display toggles under View, Fork, Archive/Restore with undo, and Delete with confirmation.

Reuse the sidebar session-organizer actions through a narrowed SessionActionHost seam, consolidate session permission gating in sessionMenuReasons, and remove the obsolete composer menu state, dismissal handling, tests, and CSS.

* test(ui): update header session menu e2e interactions
2026-08-09 23:35:55 -07:00
Peter Steinberger 293de99da8 fix(ui): remove Labs reset buttons (#121427)
* fix(ui): remove Labs reset buttons

* test(ui): update Labs default E2E flow
2026-08-09 22:59:29 -07:00
Peter Steinberger deb63f9db3 fix(ui): make source builds deterministic (#121433) 2026-08-09 22:58:57 -07:00
Peter Steinberger 2ea6f535a0 fix(ui): stop suggested card reload flashes (#121435)
Keep inline MCP App reload frames transparent while preserving explicit dashboard loading surfaces.\n\nFixes #121425. Related to #120820.
2026-08-09 22:45:08 -07:00
Peter Steinberger b81946d6af fix(ui): make Cloud Worker startup responsive (#121200)
* fix(openai): filter platform-only ChatGPT catalog rows

* fix(ui): show Cloud Worker startup progress

* fix(ui): keep cloud startup out of app bootstrap

* fix(ui): reduce cloud startup bootstrap cost

* fix(ui): satisfy cloud startup lint

* fix(ui): keep cloud startup within UI limits

* fix(ui): satisfy cloud startup CI limits

* fix(ui): isolate concurrent cloud startup

* fix(ui): address cloud startup review findings

* fix(ui): persist cloud startup recovery per session

* style(ui): format cloud startup chat props
2026-08-09 22:42:45 -07:00
Peter Steinberger b44f394308 feat(control-ui): minimal hover-reveal session mode pills (#121407)
* feat(control-ui): minimal hover-reveal session mode pills

* fix(control-ui): keep revealed inactive mode pills muted

* test(control-ui): cover draft-enabled mobile footer row at 320px
2026-08-09 22:38:41 -07:00
Peter Steinberger 7d1958bf61 fix(ui): center the onboarding dashboard and fix its card chrome (#121416)
* fix(ui): center onboarding dashboard and quiet its chrome

The custodian onboarding route carried the settings takeover class, so the
shell reserved a hidden 288px sidebar column and the whole page rendered
off-center. Onboarding now opts out of the takeover, drops the duplicate
header identity (the thread already introduces the mascot), and option-card
hover no longer lifts or re-tints cards.

* fix(ui): stop next-step cards overlaying the message meta row

The option-card and wizard-step thread attachments pulled themselves up with
a -12px top margin, covering the preceding message's sender/timestamp line.

* test(ui): align nudge e2e with the minimal onboarding header
2026-08-09 22:31:17 -07:00
Peter Steinberger 4b6a9785c9 fix(ui): dark-mode body text renders brighter than the dark-reading comfort band (#121409)
* fix(ui): soften default dark-mode body text to ~12:1 contrast

Drop --text/--chat-text from #d4d4d8 (12.87:1 on --bg) to #cdcdd1
(12.01:1 on --bg, 11.10:1 on --card) so dark body text sits inside
the 10-12 comfort band instead of above it; brighter body text
halates on dark backgrounds. Light mode, openknot, dash, --text-strong,
and --muted are unchanged.

* fix(ui): dim dark body text a further step to ~11.4:1

#cdcdd1 -> #c8c8cc (11.41:1 on --bg, 10.54:1 on --card), mid-band
instead of top-of-band.

* fix(ui): settle dark body text at ~10:1 on --bg

#c8c8cc -> #bcbcc0 (10.05:1 on --bg, 9.29:1 on --card) per visual
preview pick; low end of the dark comfort band on the page, slightly
under it on cards, still well above AAA.

* docs(ui): sync design-system guides with new dark --text token

ClawSweeper P3: color-tokens.md and accessibility.md still published
#d4d4d8 (~12.9:1); both now name #bcbcc0 (~10.1:1).
2026-08-09 22:30:50 -07:00
Vyctor H. Brzezowski d5457ad62d fix(ui): give the session rail its own header icon (#121420)
The collapsed session rail and the background-tasks toggle both rendered
icons.activity in the chat pane header cluster, so two unrelated actions
were indistinguishable without reading a tooltip. Background tasks keep
the activity metaphor; the rail now uses the companion sparkle, which is
already the product's assist glyph and does not collide with the
split-view panel icons at the end of the same row.

Closes #121418
2026-08-10 02:28:20 -03:00
Peter Steinberger 2873fbbc56 fix(ui): remove assistant avatars from chat transcripts (#121405)
* fix(ui): remove assistant avatars from chat transcripts

* fix(ui): preserve explicit assistant avatars
2026-08-09 22:27:57 -07:00
Peter Steinberger 3c5f89449e fix(ui): restore sidebar and Appearance accessibility (#121360)
* fix(ui): restore sidebar accessibility

Amp-Thread-ID: https://ampcode.com/threads/T-019fe94c-c697-71e9-982f-c164bb98bb6c

* refactor(ui): own sidebar list semantics at hierarchy boundaries

Amp-Thread-ID: https://ampcode.com/threads/T-019fe94c-c697-71e9-982f-c164bb98bb6c

* test(ui): keep sidebar cases within lint limits

Amp-Thread-ID: https://ampcode.com/threads/T-019fe94c-c697-71e9-982f-c164bb98bb6c

* test(ui): assert session tree list ownership

Amp-Thread-ID: https://ampcode.com/threads/T-019fe94c-c697-71e9-982f-c164bb98bb6c

* chore: refresh control UI CI

Amp-Thread-ID: https://ampcode.com/threads/T-019fe94c-c697-71e9-982f-c164bb98bb6c

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-08-09 22:25:26 -07:00
Peter Steinberger d4fd9cc96a fix(ui): restore settings search, media previews, clipboard, and native chat (#116654)
* fix(ui): restore search media and native text

* fix(native): unify visible chat text projection

* fix(ui): search composed array schemas

* fix(ui): keep config search off startup constraints

* fix(ui): honor schema and media path boundaries
2026-08-09 22:07:28 -07:00
Patrick Erichsen d4c3b9e783 refactor(sessions): remove custom icon feature (#121263)
* refactor(sessions): remove custom icon feature

* test(sessions): expect legacy icons to be stripped

* fix(gateway): preserve retired icon request compatibility

* chore(plugin-sdk): refresh API baseline

* fix(plugins): reserve retired session icon slot
2026-08-10 04:51:56 +00:00
Vyctor H. Brzezowski 3f4564f56f fix(ui): microphone picker claims a selection while denying any input exists (#121397)
* fix(ui): give the microphone picker one truthful state

Discovery now reports why it stopped as a code instead of prose, so the
composer popover can pick a single coherent rendering: with no selectable
input it shows one empty state rather than a checked System default row
stacked on two different ways of saying nothing was found. Rows drop
wa-dropdown-item type="checkbox", whose own leading check toggles on
click and disagrees with the trailing check bound to the stored device;
the chat pane gateway picker had the same mix and is fixed with it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat(ui): keep the microphone picker current while it is open

The empty state now promises the list keeps up, so both media surfaces
subscribe to navigator.mediaDevices devicechange for as long as they are
on screen and drop the listener when they close. The composer popover
re-runs permission-requesting discovery, since a microphone that just
appeared has hidden labels until the probe runs; the settings rows
refresh passively so hardware changes never turn into an unasked-for
browser dialog. Also drops the newly unused RealtimeTalkDeviceKind
export that failed the deadcode gate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 01:43:28 -03:00
Peter Steinberger 22e6f5b5e0 improve(ui): polish Markdown tables (#121390)
Co-authored-by: Amp <amp@ampcode.com>
2026-08-09 21:42:04 -07:00
Vyctor H. Brzezowski c0e5856181 fix(ui): reveal a rotated device token in a show-once modal (#121315)
Rotating a device token showed the freshly issued secret through native
window.prompt from the shared Nodes operations module. A WebView without a
dialog bridge returns null for that call without rendering anything, so the
rotation completed on the Gateway and the only copy of the new credential was
discarded. Where the prompt did render it was an editable, unlabelled,
uncopyable text input that Escape threw away.

rotateDeviceToken now returns the token and DevicesPage, the visual owner,
presents it through a dedicated read-only reveal built on the canonical modal
dialog and copy button: selectable monospace value, copy control, show-once
copy, and dismissal gestures refused so a stray Escape or backdrop click cannot
destroy the secret. The shared operations module still presents no UI.

The epoch guard keeps owning the local-storage write and the list refresh, but
no longer swallows the response itself: the previous credential is already dead
on the Gateway, so a reconnect mid-rotate must not be what loses the
replacement.
2026-08-10 01:37:33 -03:00
Vyctor H. Brzezowski 43540640ab fix(ui): give sidebar group headers their own visual level (#121372)
The session-catalog project/group aggregator header rendered at the same
13px/500 as the session rows nested inside it, so a group read as just
another session. Bring the label into the sidebar's existing structural-label
family at one reduced step (smaller, heavier, lightly tracked) and keep the
case as authored, since these labels carry project, custom-group, and person
names. The head's color is deliberately left alone: #115646 raised it for
dark-theme readability, so type carries the whole separation.

Closes #121371
2026-08-10 01:33:05 -03:00