Chat UI: composer polish, reducer dedup, tool-card layout fixes#484
Merged
Conversation
…width Visual cleanup pass on the chat surface (no functional change): - OpenClawComposer: right-edge padding 8 -> 14 in both ThreeRow and InlinePill branches so the action icons (attach / mic / settings / send) no longer jam against the window edge. - OpenClawComposer: dropdowns row ColumnGap 4 -> 6 for clearer separation between Channel / Model / Reasoning pickers. - OpenClawChatTimeline: cap tool-burst cards (CardOf + TaskList listCard) at MaxWidth=720 with HAlign.Left so a single 'exec' row no longer stretches across the full viewport with the Done pill floating at the far right edge. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Symmetrize user/assistant/tool burst outer margins (16px both sides) - Use bubbleRadius for tool CardOf/listCard and conditional header buttons - Make tool card background Transparent so outline distinguishes it from filled assistant bubble - Drop Plain tool burst footer; assistant follow-up bubble already carries the time Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…thing room - Drop the 36x36 spacer that mid-run assistant bubbles inherited; continuation bubbles now sit at the same left inset as tool burst cards above them, so the agent column reads as a single straight edge. - Add 20px top padding above the first message in the scroll content so the conversation does not crowd the window edge. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ign composer inset - When no assistant avatar shown, drop the leftSlot's right margin so assistant bubbles share the same left edge (16px) as tool burst cards. - Tool burst row header now uses bubblePadding instead of (12,8,12,8) and a 32px MinHeight, so tool rows match chat bubble heights. - Composer outer padding 14->16 to align dropdowns/input flush with chat bubble left edge. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Assistant footer leftInset now respects per-entry avatar visibility (not the global flag), so continuation entries' timestamps align with the bubble's left edge instead of being indented 44px. - Tool burst button hover/press alphas 0x22/0x33 -> 0x10/0x1C for a subtler reveal that doesn't darken the card on every pointer pass. - Tool card background back to a faint LayerOnAcrylicFillColorDefault tint so the card has gentle presence instead of looking like a pure outline. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
User/assistant footer insets now include bubblePadding so timestamps sit flush with the bubble's text content edge instead of the outer bubble border. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When the user avatar is hidden, the rightSlot column still added an 8px left margin even though the column body was empty, leaving an 8px gap on the right of the bubble. Gate the margin on showUserAvatar so the bubble actually reaches the container's right edge when the avatar is off — this makes rightInset (= bubblePadding.Right) place the timestamp flush with the bubble's inner text right edge. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The TaskHeader burst style reserved an avatar slot (36+8px) so the list card lined up with the assistant bubble's text edge, but the Plain and FooterReframe styles started flush at the gutter. When the assistant entry above the burst showed an avatar, the tool cards appeared 44px further left than the bubble. Extracted the avatar-slot wrap into a helper and applied it to all three burst styles so user/assistant/tool share the same left edge regardless of burst style. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Default tool burst rendering switches from Plain (verbose per-row stack) to TaskList:
- While any step is InProgress: auto-expanded, shows 'Working on X...'
- When the burst completes: auto-collapses to a single one-line summary card
('Ran N steps' or last result snippet) with a chevron to expand
- Click chevron to override; per-step rows still individually expandable for
full args/output (3-tier disclosure)
Addresses Scott's feedback: 'I'd like to be able to have tool calls summarized,
or made smaller, or collapsible, so there would be some way to be clear that
work is happening, and if I want to see the verbose logs, I could.'
No data-flow changes — purely the default value of the existing ToolBurstStyle
enum. The dev exploration panel still exposes Plain/TaskHeader/CompactSummary/
FooterReframe/TaskList for tuning.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Tool card aligns under bubble (toolLeftMargin = gutter + avatarSlot + 16) with right edge matched (MaxWidth -= indent). Plain/FooterReframe/CompactSummary/TaskHeader unified. - Footer priority: hide sender/model by default, surface input/output tokens + context % pills. - Preset record defaults updated to match (new presets inherit token/ctx ON, sender/model OFF). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Reorder timeline display within each turn so ToolCall bursts render AFTER the assistant reply (or the thinking indicator if none yet). Gateway still emits tool_start before assistant_delta; only the visual order changes. - Inline 'agent is thinking…' indicator right after the most recent User entry instead of pinning to bottom of timeline, so tool cards visually hang below it. - Tool burst card HAlign Left→Stretch (Plain/FooterReframe/CompactSummary/TaskHeader) so the right edge fills to the bubble's max right boundary instead of shrinking to content. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assistant bubble previously used HAlign=Left with no MaxWidth, so its right edge tracked content width. Tool burst card used HAlign=Stretch with MaxWidth=704, filling further right than the bubble. Give the assistant card MaxWidth=720 and HAlign=Stretch so it always pins to the same max right boundary as the tool card (60 + 720 = 76 + 704 = 780). Tool card now sits indented 16px inside the bubble's left edge with an identical right stroke, reading as a true child of the bubble above. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…p anchored) WinUI's HAlign=Stretch + finite MaxWidth centers the element inside its slot (rather than pinning it left), which detached the bubble from the avatar + timestamp column. Revert to HAlign=Left so the bubble grows from the avatar's edge; MaxWidth=720 still caps the right edge so long messages line up with the tool burst card's right stroke. Short messages keep the previous content-width behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…-on-wide-screen) Same WinUI quirk as the assistant bubble: HAlign=Stretch with finite MaxWidth centers the element inside an oversized slot rather than pinning it left. On wide screens the tool burst card drifted away from the bubble's left edge. Revert all four tool burst styles (Plain, FooterReframe, CompactSummary, TaskHeader) to HAlign=Left. Both bubble and tool card now anchor on the left next to the avatar/timestamp column; right edges line up when both fill their MaxWidth (720 / 720-indent). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…Star Grid Tool card needs to fill its 704-wide slot so the right stroke aligns with the assistant bubble's right edge, but HAlign=Stretch alone centers the card on wide screens (WinUI's Stretch + finite MaxWidth quirk). Wrap the card in an Auto/Star Grid: the Auto column sizes to the card's MaxWidth (704), keeping the card pinned to toolLeftMargin and filling the slot. The Star column absorbs the rest. Applied to Plain, FooterReframe, CompactSummary, TaskHeader. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assistant bubble is content-sized (HAlign=Left, MaxWidth=720), so we can't predict its rendered width at layout time. Result: the tool card's right edge rarely matched the bubble's right edge, especially with short replies. Solution: thread a per-turn Border[1] slot from RenderAssistantEntry into RenderToolBurst. The assistant bubble's Border drops itself into slot[0] on materialize; the tool card subscribes to bubble.SizeChanged and sets its own Width = bubble.ActualWidth - toolIndent. The two cards' left indent and right edges now stay exactly parallel as the bubble grows, regardless of content length. Reset slot at each User entry boundary so tool cards never bind to a prior turn's bubble. Falls back to MaxWidth/AnchorLeft when no bubble exists (tool-only turn). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Scott feedback: when an agent's reply bubble lands and the tool burst beneath it is fully terminal, fold the N step rows into a single collapsed summary (clickable chevron to expand). While tools are still running, keep showing them as per-step rows so each Running/Done pill stays visible. Added ToolBurstStyle.Auto and made it the process-wide default. Auto resolves per burst at render time: - count == 1 -> Plain (one inline row) - count >= 2 && all terminal -> CompactSummary (1-line + chevron) - count >= 2 && any InProgress -> Plain (live status visible) CompactSummary's existing expand machinery (expandedToolChips HashSet) is reused, so the collapse/expand state persists for the session. Exposed Auto in the exploration panel dropdown for testing the other styles. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two follow-ups from Scott's screenshot: 1. The collapsed CompactSummary header used FlexRow(ColumnGap=6)+padding(12,8,12,8)+MinHeight=22 while BuildRow used Grid[Auto,Auto,Auto,Star,Auto]+margin(6,0,0,0)+bubblePadding+MinHeight=32. Rebuilt the summary header with the exact same Grid template, margins, padding, and MinHeight as the step rows so the chevron / lightning / Task label / Done pill axes line up vertically when the burst is expanded. 2. Removed the trailing FooterCaption(timeStr/TaskFooter()) from CompactSummary, TaskHeader, and FooterReframe returns. The assistant bubble above the tool burst already shows its own timestamp/model/tokens footer, so the time under the tool card was redundant noise. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When an assistant message is followed by a tool burst in the same turn, defer the timestamp/model/tokens footer so it renders BELOW the tool card(s) instead of between the bubble and the tools. Order becomes: bubble -> tool card(s) -> timestamp/model/tokens Implementation mirrors the existing bubbleSlot pattern: RenderAssistantEntry accepts an Element[1] footerSlot; when supplied, the built footer is handed back to the caller and the inline footer slot in the VStack collapses to Empty(). The outer loop precomputes turn boundaries, hands out a footerSlot whenever a tool entry follows in the same turn, and splices the captured footer Element into timelineRows just after the last entry of the turn (alongside the thinking indicator splice). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This reverts commit 9f84ba9.
When the assistant has produced a reply AND the tool burst would render
as a single visible row (one chip OR a collapsed multi-step summary),
embed the tool card inside the assistant bubble's content area instead
of rendering it as a sibling card below.
bubble {
text...
[tool card] <- nested
}
In-flight multi-step bursts (Plain expanded) and bursts arriving before
the assistant reply still render externally so live progress stays
visible. Plain / TaskHeader / TaskList / FooterReframe styles never
nest — only Auto and CompactSummary opt in.
Implementation:
- RenderAssistantEntry gains an Element? nestedTool param. When set,
the bubble wraps its markdown text in a VStack(8, text, nestedTool)
so the tool card sits flush below the message with an 8px top gap,
inside the bubble's existing padding/border/radius.
- RenderToolBurst gains a bool nested flag. In nested mode CardOf
drops MaxWidth/HAlign.Left and the bubbleSlot Width binding (the
parent bubble already constrains us); a new Wrap helper bypasses
AnchorLeft and the toolLeftMargin/gutter outer margin so the card
stretches inside the bubble.
- Outer loop precomputes turn boundaries, looks ahead from the last
assistant entry of the turn for a contiguous tool burst, and asks
BurstIsNestable to gate the decision (count==1 OR all-terminal under
Auto/CompactSummary). Consumed orderedIdx positions are tracked in a
HashSet so the external render branch emits Empty() for them.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Markdown text has tight line-height with no trailing descender, so an 8px top gap reads as visibly tighter than the 8px bottom padding below the nested tool card. Set the top gap to bubblePadding.Bottom + 4 so the optical spacing matches the gap from the card to the bubble's bottom edge across all PaddingDensity presets. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The +16 indent on external tool cards exists so the card visually nests inside the assistant bubble's text column. When the agent is still 'thinking…' and no reply bubble has streamed, that indent makes the tool card hang further right than the thinking indicator above it. When bubbleSlot is null (no assistant entry seen in this turn) drop the indent so the tool card aligns flush under the thinking indicator instead of jutting right. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
High priority fixes: - AutomationName: only set bubble-level name when no nested tool card is present so Narrator can traverse into the nested card. - Status colors: replace hardcoded Done-green / Running-orange / pill white with SystemFillColorSuccessBrush / CautionBrush and TextOnAccentFillColorPrimaryBrush so they adapt to dark/HC themes. - Hover/pressed: 3 button surfaces now use SubtleFillColorTertiary (hover) and SubtleFillColorSecondary (pressed) themed brushes. The tool card uses the lighter Tertiary on hover for a more subtle look (Scott feedback) and to stay visible in dark/HC. - SizeChanged handler leak on the bubble-Width binding fixed: subscribe on Loaded, detach on Unloaded with a stable handler reference so re-renders don't accumulate listeners. Medium priority fixes: - toolCardBgBrush: LayerOnAcrylicFillColorDefaultBrush → CardBackgroundFillColorDefaultBrush (semantic match — the bubble surface is opaque, not acrylic). - Tool card MinWidth = 360 so the 5-column header grid doesn't clip when the assistant reply is short and the bubble shrinks below it. - HC border thickness bumped to 2px via AccessibilitySettings probe (graceful 1px fallback if API throws in unpackaged hosts). - BurstIsNestable now returns false when any tool errored — errors stay external so the failure is visually prominent. Low priority fixes: - (int)bubblePadding.Bottom + 4 → (int)Math.Round(...) for fractional density values. - Remove redundant FontSize = 12 sets after Caption() (Caption already sets 12). Status pills bumped 10 → 11 for readability. - Add uniform-CornerRadius assumption comments at two card sites. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The chat UI styling Kenny iterated on was being persisted to a local preset (%APPDATA%\\OpenClawTray\\chat-exploration-presets.json with IsDefault=true) that overrode the in-memory defaults on startup. New installs and other users were therefore landing on the original code defaults (Mica / Comfortable / Both avatars / 14px / 32px send button) instead of the look reviewed by Scott. Bake the preset values into the actual code defaults so a fresh install matches the design without needing the JSON preset file: BackdropMode Mica -> Acrylic PaddingDensity Comfortable -> Cozy AvatarMode Both -> AgentOnly ComposerIconSize 14 -> 16 SendButtonSize 32 -> 40 Updated in three places to keep them consistent: - ChatExplorationState (the in-memory defaults applied at startup) - ChatExplorationPreset record (defaults when a preset omits a field during deserialization) - ChatVariationPresets.Calm (so users who hit Reset or pick the Calm variation land on the same baseline) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Wrap BuildSection in a 2-col Grid with a transparent phantom chevron in col 0 (same glyph + 6px right margin) so the section's lightning glyph and the code block beneath it land at the same x as the header lightning above, regardless of density/font metrics. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Composer (OpenClawComposer.cs): - Paste image preview rendered above text in single user bubble - X-button hover changes circular background opacity instead of fading the X glyph (uses opaque SolidBackgroundFillColor* brushes) - Strip stray top/bottom line on TextBox via theme resource overrides (TextControlBorderThemeThickness / focused / pointerOver) - Tighten composer<->actions-row gap (actionsRow margin -8/-4) Reducer (ChatTimelineReducer.cs): - UpsertAssistant reconcile path now scans backward for the most recent Assistant entry, stopping at User boundary, so streams of shape `text -> tool -> tool output -> final text` no longer duplicate the assistant text into a second bubble. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- AnchorLeft Grid: single Star col so card measure is bounded by chat viewport - headerRow / summaryHeader: outer [Star, Auto] wrapping inner content + Done pill - Summary Caption: TextWrapping=Wrap + MaxLines=1 + CharacterEllipsis for safe trim - CardOf nested branch: MinWidth=360; external Sync clamps bubble width with Math.Max(360, w) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bubble CornerRadius (16) equals bubblePadding.Right (16) in Cozy preset, so the bubble's corner arc reaches the inner content edge and clips the Done pill at the card's right side. Move the nested card in by a full bubbleRadius (and drop MinWidth=360 which forced the card wider than the bubble in narrow viewports) so the pill is comfortably inside the bubble's rounded shape. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
048391c to
9f09e9b
Compare
Add ChatUserBubbleTone enum (default Secondary) and a panel control to switch between the bold accent fill (AccentFillColorDefault) and a softer accent variant (AccentFillColorSecondary). Both tones use TextOnAccentFillColorPrimaryBrush for the text, which Fluent guarantees meets WCAG AA contrast in light, dark, and HighContrast themes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolved conflicts: - OpenClawComposer.cs: kept both new params (OnAttachmentPasted + IsCompact) - OpenClawChatRoot.cs: pass both OnAttachmentPasted and IsCompact through - OpenClawChatDataProvider.cs: accepted upstream cross-client user echo handling (openclaw#469) — supersedes our earlier rollback of that work. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes
Composer
Tool card
AnchorLeftGrid switched to a single Star column so the card's measured width is bounded by the chat viewportheaderRow/summaryHeaderrestructured to outer[Star, Auto]+ inner 4-col grid (Done pill in the outer Auto)TextWrapping=Wrap + MaxLines=1 + CharacterEllipsisfor safe single-line ellipsisbubbleRadius(16px) on the right so the bubble's CornerRadius arc no longer clips the pillUser bubble tone
ChatUserBubbleToneenum (Accent / Secondary), exposed in the Explorations panelAccentFillColorSecondaryBrush(softer accent variant) so the bubble still reads as the brand color but doesn't compete with the accent-colored avatar / send buttonTextOnAccentFillColorPrimaryBrush— Fluent guarantees WCAG AA contrast in light, dark, and HighContrast themesReducer
Out of scope
Validation
./build.ps1✓