Skip to content

fix(chat): replace NativeElement escape hatch with FunctionalUI ComboBox primitive (supersedes #973)#991

Merged
shanselman merged 4 commits into
openclaw:mainfrom
karkarl:fix/970-stable-session-picker
Jul 14, 2026
Merged

fix(chat): replace NativeElement escape hatch with FunctionalUI ComboBox primitive (supersedes #973)#991
shanselman merged 4 commits into
openclaw:mainfrom
karkarl:fix/970-stable-session-picker

Conversation

@karkarl

@karkarl karkarl commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Supersedes #973 (original NativeElement-based fix by @arturict).

Problem

#970: the chat session picker (and, unfixed, the model picker) collapsed whenever the composer re-rendered — e.g. during agent "thinking"/status streams. The root cause is that FunctionalUI's diff loop recreated the ComboBox control on every render, dismissing any open dropdown.

PR #973 worked around this with a raw NativeElement snapshot that reached around the FunctionalUI diff to hand-manage the session picker. That escape hatch fixes one call site but leaves the framework gap open (the model picker had the identical bug) and is easy to reintroduce.

Change

Promote the fix into a first-class rich ComboBox primitive in FunctionalUI so re-render stability is a framework guarantee:

  • ComboItem (pure data) + ItemComboBoxElement with static ItemsEqual.
  • ConfigureItemComboBox rebuilds control.Items only when the item list actually changes, so an open flyout survives composer re-renders. Selection is reconciled by tag; the change handler is detached during programmatic mutation so it never fires the user callback.
  • Both the session picker and the model picker migrated onto the primitive (the model picker had the same unfixed Windows Companion: "Agent thinking" animation closes the chat/session selectbox before a different chat can be chosen #970-class bug).
  • NativeElement and its tests removed entirely.

Net −485 lines.

Tests

  • ItemComboBoxTests — pure coverage of ComboItem defaults, factory capture, and ItemsEqual (equality/order/label/flag/count/reference/empty).
  • ComposerSessionPickerTests — source-contract guard that both pickers use the primitive and the old escape-hatch patterns (border.Child = cb, SessionPickerSnapshot, Native() don't return.

Validation

  • ./build.ps1
  • dotnet test OpenClaw.Shared.Tests ✅ (2739)
  • dotnet test OpenClaw.Tray.Tests ✅ (1672)
  • dotnet test OpenClawTray.FunctionalUI.Tests ✅ (15/15)
  • UITests project compiles ✅

Real behavior proof

Rendered the real FunctionalUI composer offline via the accessibility provider (OPENCLAW_ACCESSIBILITY_TEST_CHAT=1). UI Automation confirmed all three pickers (Session/Model/Reasoning) render, and the Session dropdown expands and populates with grouped seeded sessions. Fully faithful "stays open during a live agent status stream" still needs a paired gateway + LLM.

Co-authored-by: arturict arturict@users.noreply.github.com

arturict and others added 4 commits July 12, 2026 11:22
Keep the session picker ComboBox attached as a declarative native child across FunctionalUI renders, and harden native control lifecycle handling for mount, ownership, and event routing.

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

The openclaw#970 fix originally reached around FunctionalUI with a raw NativeElement
snapshot to keep the session picker open across status re-renders. Promote that
into a first-class rich ComboBox primitive so re-render stability is a framework
guarantee, not a per-call escape hatch.

- Add ComboItem + ItemComboBoxElement with static ItemsEqual; ConfigureItemComboBox
  only rebuilds items when the list actually changes, preserving an open flyout.
- Migrate both the session picker and the model picker (which had the same
  unfixed openclaw#970-class bug) onto the primitive.
- Remove NativeElement and its tests entirely.
- Add pure ItemComboBoxTests and source-contract ComposerSessionPickerTests
  guarding against escape-hatch regressions.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
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.

4 participants