Skip to content

Tray menu (right-click): Fluent style tray-menu redesign#383

Merged
shanselman merged 15 commits into
openclaw:masterfrom
kenehong:user/kehong/companion/settings-v2-variant-b
May 14, 2026
Merged

Tray menu (right-click): Fluent style tray-menu redesign#383
shanselman merged 15 commits into
openclaw:masterfrom
kenehong:user/kehong/companion/settings-v2-variant-b

Conversation

@kenehong
Copy link
Copy Markdown
Contributor

@kenehong kenehong commented May 14, 2026

Summary

image VarB variant of the WinUI 3 tray-menu redesign — an informative, Fluent-styled tray context menu (right click) for OpenClaw built fresh on top of `upstream/master` to avoid the perf regressions in the prior round.

Key changes

  • Brand header: app icon (Square44x44Logo) replaces emoji glyph;
    Disconnect/Connect button moved here so the Gateway block stays clean.
  • Gateway: now a chevron-flyout row using the same card format as devices.
    Hover reveals a rich detail flyout (server version, auth mode, protocol,
    uptime, conn id, clients, pending approvals). Click still opens Connection
    settings.
  • Devices: flow directly under Gateway with no divider/section header.
    Device flyout uses Fluent FontIcon glyphs (FluentIconCatalog) and renders
    each capability as a card with bold title + caption commands list. Ellipse +
    theme brush replaces emoji status dot.
  • Permissions: top-level Action with hover flyout. WinUI ToggleSwitch
    rows with Off/On text label that updates live, matching the Windows
    Settings Accessibility page pattern.
  • Sessions / Usage: collapsed L1 rows with rich hover flyouts (mini
    progress bars, totals, providers, by-model).
  • Pairing approval pending: high-priority row placed above Gateway block.
  • Companion Settings moved to footer with Ctrl+Alt+; hotkey (Win+;
    conflicted with Windows emoji panel).
  • Menu cleanup: ExitClose with Cancel (X) icon; removed Quick Send,
    Setup/Reconfigure, More(N).
image image

Validation

  • ./build.ps1 — all projects build cleanly.
  • OpenClaw.Shared.Tests: 1548 passed, 28 skipped.
  • OpenClaw.Tray.Tests: 1188 passed.

Scope

Touches only the tray menu (App.xaml.cs, TrayMenuWindow.xaml.cs,
GlobalHotkeyService.cs, FluentIconCatalog.cs, FluentIconCatalogTests.cs).
No functional behavior changed.

Diff: ~1900 insertions / ~517 deletions across 5 files.

kenehong and others added 13 commits May 14, 2026 08:40
… permissions submenu, a11y)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…progress bar, verbose gateway)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt;
…act+flyout, Usage, Win+; hotkey

- Gateway: revert to AddCustomElement with visible Disconnect/Connect button (action verb)
- Permissions: moved out of per-device card to top-level Actions section above Dashboard
  - Uses WinUI ToggleSwitch via new TrayMenuFlyoutItem.IsToggle/IsOn render path
  - Reordered per PermissionsPage: Node mode -> Browser -> Camera -> Canvas -> Screen -> Location -> TTS -> STT
  - Added Speech-to-text (STT) row wired to NodeSttEnabled
- Sessions: compact card (dot + name + age); model + ProgressBar + token ratio moved to hover flyout
- Usage section added between Sessions and Actions with View detailed usage entry
- Actions: removed Quick Send and Setup/Reconfigure
- Footer: Companion Settings moved here above About; shows Win+; trailing hint
- GlobalHotkeyService: register Win+; (VK_OEM_1 + MOD_WIN) -> ShowHub(companion)
- Removed More(N) overflow row in Sessions

VarA untouched.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…roviders/by-model

- Sessions: single 'Sessions (N active, X tokens)' row with chevron;
  hover flyout lists each session as 2-row card (name+age, model+ProgressBar+ratio).
  ProgressBar bumped to Height=6, MinWidth=80, IsIndeterminate=false so the bar
  is reliably visible.
- Usage: replaces 'View detailed usage' link with 'Usage (\.XX · Y tokens)' row.
  Flyout shows:
    * Totals card (large dollar amount + tokens in/out + requests)
    * Providers section (DisplayName, Plan, per-window ProgressBar)
    * By Model aggregation from sessions (top 3)
    * 'Open detailed usage ->' action link
  Falls back to session aggregation when GatewayUsageInfo is absent.

VarA untouched. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace WinUI ProgressBar (rendering 0-height in dynamic flyout layout)
  with BuildMiniBar — a Grid of two star-sized Borders (accent fill + track)
  that is guaranteed to render at any width.
- Remove leading icons on Sessions and Usage L1 rows for a cleaner look.
- Usage L1 summary now always shows \ · tokens together (e.g.
  '\.00 · 1.1M tokens') so cost is visible even when only one side has data.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- BuildDeviceFlyoutItems: replaced emoji CapabilityIcons with Fluent
  glyphs (FluentIconCatalog Browser/Camera/Canvas/Screen/Location/Voice/
  System) and rendered each capability as a CustomContent card with
  24px icon column + bold title + caption commands list, matching the
  Permissions flyout visual hierarchy.
- Device status row now uses an Ellipse dot (success/neutral brush)
  with theme-aware caption text and a clean 'Last seen Xm ago' line.
- Moved Disconnect/Connect button into the Gateway header row alongside
  the version/Local chip; removed the now-unused gwHost wrapper grid
  for a cleaner gateway block.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Pairing approval pending row moved from Actions section to above
  the Gateway block, directly under the OpenClaw brand header — it's a
  high-priority action so users see it first.
- Disconnect/Connect button moved from Gateway header row up to the
  OpenClaw brand header (right side); Gateway header now just shows
  the dot, label, and version/Local chip.
- Removed the divider and 'Devices' section header between Gateway and
  the device list so the two visually-identical card formats flow
  together. (Test marker comment preserved.)
- Removed 'Open Sessions →' and 'Open detailed usage →' rows from
  Sessions/Usage flyouts since clicking the L1 row already navigates.
- Toggle rows in flyouts now show 'On' / 'Off' text label to the left
  of the ToggleSwitch, matching the Windows Settings Accessibility
  page pattern. Label updates live on Toggled.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…anup

- Replaced lobster emoji brand glyph with the app icon image
  (Square44x44Logo.targetsize-48_altform-unplated.png at 28x28) in the
  tray brand header.
- Gateway block now uses AddFlyoutCustomItem with action='connection'
  so it renders a chevron and exposes a rich hover flyout via
  BuildGatewayFlyoutItems: status card (Ellipse + status text + full
  URL), auth-failure caption, Server details (version/auth/protocol/
  uptime/conn id), Clients list with platform·version·mode, and
  Pending approval counts. Click still opens Connection settings.
- Sessions and Usage flow without a divider between them.
- 'Exit' menu item renamed to 'Close' with Cancel (X) glyph instead of
  PowerButton. FluentIconCatalog.Exit now maps to \\uE711.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Win+; is a Windows-reserved shortcut that opens the system emoji
panel, so our Settings hotkey was effectively unreachable and the
emoji panel popped up instead. Switched the hotkey modifiers to
MOD_CONTROL | MOD_ALT and updated the menu hint string to
'Ctrl+Alt+;'.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Code-review follow-up: the comment and Logger.Info/Warn strings in
GlobalHotkeyService still read 'Win+;' even after the binding was
changed to MOD_CONTROL|MOD_ALT in c6e04b3. Updated all three log
sites and the leading comment to read 'Ctrl+Alt+;' so support logs
match the actual user-facing shortcut.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@shanselman
Copy link
Copy Markdown
Contributor

do we lose devices with this @kenehong ?
image

…STT icons

- Permissions menu row now routes to ShowHub(\"permissions\") on click (was opening submenu only); hover still reveals the toggle flyout.

- Strip verb prefixes from permission labels: 'Allow camera' -> 'Camera', 'Enable Windows node' -> 'Windows node', etc. Toggle state (Off/On) already conveys the verb.

- TTS uses Volume glyph (E767, speaker+waves), STT uses Dictate glyph (F12E). New FluentIconCatalog.Speech constant.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@kenehong
Copy link
Copy Markdown
Contributor Author

kenehong commented May 14, 2026

No, they’re re-located with the gateway section @shanselman
image

Device permissions are listed within the same menu as the other options.
image

Copy link
Copy Markdown
Contributor

@shanselman shanselman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update here. This now clean-merges onto current master (including #384/#385), and CI is green, but I still think this needs changes before merge because it regresses a few tray-menu entry points.

Blocking issues I found in the current head (508c7bb):

  1. The Gateway card advertises "Activate to open connection settings" and is wired with action: "connection", but OnTrayMenuItemClicked still has no case "connection". Clicking the gateway row therefore no-ops instead of opening Connection settings. Suggested fix: add case "connection": ShowHub("connection"); break; and ideally add a regression assertion that every tray-menu action emitted by BuildTrayMenuPopup has a handler or is explicitly flyout-only.

  2. The visible Setup/Reconfigure tray entry is still gone. We recently fixed regressions around keeping/dismissing existing setup, and the tray menu needs to preserve an obvious path back to setup/reconfigure. Suggested fix: restore a visible Setup/Reconfigure row using the existing onboarding/config guard label logic and route it to the existing "setup" action.

  3. QuickSend is still removed from the visible tray menu even though the handler/action path remains. If removal is intentional, we should call it out in the PR and remove/deprecate the dead path separately; otherwise restore the QuickSend row.

Non-blocking cleanup: the brand header duplicates the disconnect logic inline instead of reusing the "disconnect" action path, which makes future state-reset changes easier to miss. I would fix the blocking action/entry regressions first, then consider extracting the duplicate disconnect logic.

… regression test

Blocking review feedback:

- Add explicit `case \"connection\":` and `case \"permissions\":` in OnTrayMenuItemClicked. The default fallthrough still routes correctly, but explicit cases make the wiring obvious and prevent silent regressions if the default behavior changes.

- Restore visible QuickSend row (\"quicksend\" action) using Menu_QuickSend localized label and FluentIconCatalog.QuickSend.

- Restore visible Setup/Reconfigure row. Label flips between Menu_SetupGuide and Menu_Reconfigure via OnboardingExistingConfigGuard.HasExistingConfiguration(); dispatches the existing \"setup\" action.

- Refactor brand-header Disconnect/Connect button to call OnTrayMenuItemClicked(this, \"disconnect\"|\"reconnect\") instead of duplicating the disconnect cleanup logic inline. Future state-reset changes now live in one place.

Regression guard:

- New test `BuildTrayMenuPopup_TopLevelActions_AllHaveExplicitHandlers` asserts that every static top-level action (connection, disconnect, reconnect, permissions, setup, quicksend, companion, about, exit) has an explicit `case` in OnTrayMenuItemClicked.

Validation: build green; Tray 1189/1189; Shared 1548/1548.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@kenehong
Copy link
Copy Markdown
Contributor Author

Thanks for the review — addressed all blocking + non-blocking points in
4e18aee:

Blocking

  • Added explicit case "connection": and case "permissions": in
    OnTrayMenuItemClicked. The default fallthrough was technically routing
    correctly via ShowHub(action), but explicit cases make the wiring
    discoverable and prevent silent regressions.
  • Restored visible Setup Guide / Reconfigure row using
    OnboardingExistingConfigGuard.HasExistingConfiguration() to flip the label,
    dispatching the existing "setup" action.
  • Restored visible QuickSend row (Menu_QuickSend localized label →
    "quicksend" action).

Non-blocking (also done)

  • Refactored the brand-header Disconnect/Connect button to call
    OnTrayMenuItemClicked(this, "disconnect" | "reconnect") instead of
    duplicating the cleanup inline. Future state-reset changes now live in one
    place.

Regression guard

  • New test BuildTrayMenuPopup_TopLevelActions_AllHaveExplicitHandlers
    asserts that every static top-level action (connection, disconnect,
    reconnect, permissions, setup, quicksend, companion, about, exit)
    has an explicit case in the click handler. Caught the missing
    "permissions" case the moment I wrote it.

Validation: build green; Tray 1189/1189; Shared 1548/1548.

Copy link
Copy Markdown
Contributor

@shanselman shanselman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rechecked the updated head on current master. The prior blockers are addressed: the Gateway card now has an explicit \connection\ handler, QuickSend is restored, Setup/Reconfigure is restored with existing-config label logic, and regression coverage was added for the top-level tray actions. Local validation passed: build, Shared tests 1572/1600 with 28 skipped, Tray tests 1210/1210.

@shanselman shanselman merged commit 95cfcfb into openclaw:master May 14, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants