feat(desktop): emoji avatar picker for agents + reliable picker scroll#1576
Conversation
Replace the simple upload/URL popover in AgentCreationPreview with a tabbed popover containing an 'Image' tab (existing upload + URL flow) and an 'Emoji' tab (emoji-mart picker + color swatches + custom color). Selecting an emoji produces an emojiAvatarDataUrl that flows through the existing avatarUrl state — ProfileAvatar already renders these as SVG data URLs, so no changes needed downstream. Reuses the same emoji-mart config, color palette, custom color panel, and emoji burst animation from ProfileAvatarEditor (user profile).
Image tab: - Replace menu-style buttons with a mini drop/browse zone (click to open file picker, drag to upload) matching the profile editor style - URL input below with Link2 icon, same rounded-lg bg-muted treatment - Upload error shown inline Emoji tab: - Color swatches always visible by default (no progressive disclosure) - Emoji picker height reduced slightly (240px) to keep popover compact - Custom color picker still available via the rainbow swatch
- Widen popover to 400px so emoji category nav isn't cut off and scrolling works properly in the emoji grid - Color swatches: 12 columns with smaller dots (h-6 w-6) for a more compact, less vertically spaced layout - Single drop zone: dragging a file highlights the entire popover with a 'Drop image here' overlay instead of two competing drop zones - Suppress outer avatar drag highlight when the popover is open - Auto-switch to Image tab when dragging a file into the popover - Add squish animation to the avatar preview when emoji/color changes, matching the profile editor behavior (buzz-avatar-squish CSS class) - Render emoji avatars as colored circle + glyph (not via ProfileAvatar img) so the background-color transition and squish animation work
- Remove overflow-hidden from emoji picker container so the internal emoji-mart scroll works (was clipping the scrollable area) - Add 'relative' to the emoji tab wrapper so AvatarCustomColorPanel (absolute inset-0) positions correctly and custom color works - Drop overlay: simplified to just a dashed border highlight on the fieldset (no redundant icon/text since the popover already shows them) - URL placeholder: more muted (text-muted-foreground/50, icon /60) - Pencil badge: added to the empty-state avatar circle so both empty and filled states show the edit affordance
- Replace the SVG clipPath + manual badge positioning with the shared MaskedAvatarBadgeFrame component, giving the pencil badge the same smooth organic mask curve as the profile editor - Fix emoji centering: zero out the x offset and reduce y offset to 4px (was 1px/7px from the profile's larger 192px avatar) - Both empty and filled states use MaskedAvatarBadgeFrame for consistent badge masking
- Always render the emoji picker (hidden when image tab active) so the shadow CSS injection has time to set up before the user sees it. Previously the picker remounted on every tab switch, racing the style injection. - Enable useEmojiMartStyles when the popover is open (not just when emoji tab is active) so styles are ready before tab switch. - Set --buzz-avatar-emoji-offset-y to 0px so the emoji stays centered on change instead of nudging down then back up. The squish animation now only scales (no translate), which feels squishy without movement.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 52a3acf09a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b9417c4439
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- Scroll: wheel events retarget to the emoji-mart host outside its shadow root, so the browser did not apply default scroll to .scroll. Add a wheel handler (delta-mode normalization, clamped scrollTop) installed on the shadow-root scroll region, reconnected via MutationObserver. - Gate emoji-mart style injection on the Emoji tab being active so the shadow CSS lands right after the Picker mounts (matches profile editor). - Unify the empty/filled avatar states into one Popover with a stable PopoverAnchor so it no longer jumps when an emoji is selected. - Sliding-pill segment control motion matching the profile editor. - Centered emoji glyph (zeroed offset) and MaskedAvatarBadgeFrame pencil. - Add e2e coverage for emoji-picker scroll inside the popover. Signed-off-by: npub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 <8a675edd33677aa0389f6650d467b2041fb0df4ca820eacb009babb95e3715d4@sprout-oss.stage.blox.sqprod.co>
…oji state Addresses two Codex review comments on the agent emoji-avatar picker: - P1: resolveManagedAgentAvatarUrl ran atob() on every data:image/ URL, but emoji avatars are percent-encoded SVG data URLs (no ;base64), so atob threw and the emoji was silently dropped to the default avatar on create. Pass non-base64 (inline SVG) data URLs through unchanged — the same self-contained form profile persists — and only upload base64 payloads. Adds a regression test. - P2: opening the popover on a non-emoji avatar left the previous selectedEmoji/selectedColor in state, so tapping a color swatch before choosing a new emoji re-applied the stale emoji over the image avatar. Reset emoji selection in the non-emoji branch, matching the profile editor. Bump the text-[4rem] glyph allowlist line in check-px-text.mjs to track the shifted line. Signed-off-by: npub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 <8a675edd33677aa0389f6650d467b2041fb0df4ca820eacb009babb95e3715d4@sprout-oss.stage.blox.sqprod.co>
…URL draft on blur Address two follow-up review comments (P2): - managedAgentAvatar/channelAgents: createChannelManagedAgent duplicated the base64-only atob handling, so deploying a persona/team with an emoji SVG avatar into a channel silently dropped the emoji. Route it through the shared resolveManagedAgentAvatarUrl resolver so emoji SVG data URLs pass through and base64 PNGs still upload — identical to the agent-create path. - AgentCreationPreview: the URL input committed the draft on every blur (which also closes the popover), so clicking the Emoji tab, Remove, or the drop zone after typing/pasting a URL applied the draft before the intended click. Commit only on Apply/Enter now. Signed-off-by: npub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 <8a675edd33677aa0389f6650d467b2041fb0df4ca820eacb009babb95e3715d4@sprout-oss.stage.blox.sqprod.co>
b9417c4 to
c03f908
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c03f908e98
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The agent avatar picker keeps the color swatches visible even before an emoji is chosen, so a user can pick a background color first. The first emoji click took the `selectedEmoji === null` branch and always applied a random color, discarding the user's explicit swatch choice (the selected ring moved but the saved avatar got a different background). Track whether the user has explicitly chosen a color (`hasChosenColor`): set on swatch tap and custom-color commit, and adopted from the saved color when reopening on an existing emoji avatar; reset when the popover opens on a non-emoji/empty avatar. The first emoji now honors a chosen color and only falls back to random when no color was picked. Addresses the third-pass P2 review comment. Signed-off-by: npub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 <8a675edd33677aa0389f6650d467b2041fb0df4ca820eacb009babb95e3715d4@sprout-oss.stage.blox.sqprod.co>
wesbillman
left a comment
There was a problem hiding this comment.
Reviewed on behalf of Wes — full review notes in the Buzz thread.
Verified: all ProfileAvatarEditor.utils.ts imports exist on main; channelAgents.ts → shared resolveManagedAgentAvatarUrl refactor is behavior-preserving with the new isBase64DataUri guard unit-tested; wheel-scroll handler normalizes delta modes, clamps, and reconnects via MutationObserver with proper cleanup; merge-base is 28 commits behind main but none of main's changes touch this PR's files; CI fully green.
Non-blocking notes:
- The old inline upload path logged
console.warn("Avatar upload failed…"); the shared helper's catch is silent — a one-line warn would keep that observability. - The new E2E scroll test (Chromium) guards against the manual wheel handler breaking scroll rather than reproducing the WKWebView bug it fixes — still useful, just noting what it proves.
…ivity * origin/main: fix(desktop): hydrate reactions for Inbox context messages (#1596) fix: cleanup old screenshots that my agents committed (#1598) chore(release): release Buzz Desktop version 0.3.46 (#1585) fix(desktop): preserve agent model/provider when persona snapshot fields are blank (#1583) feat(acp,desktop): identify and reap stale agent harness processes (#1582) feat(desktop): active-draft badge, send-from-drafts confirm dialog, thread-deleted state (#1581) fix(desktop): treat baked build env vars as satisfying required agent config (#1580) feat(desktop): add "Copy image" to image right-click context menu (#1579) fix(nest): use buzz-dev symlink name for dev builds (#1587) fix(composer): address image-editor follow-up nits on #1491 (#1565) fix(desktop): render black static boot screen (#1570) feat(agents): group activity tool bursts (#1571) feat(desktop): aggregated overview rail, commit detail page, and full breadcrumbs (#1573) fix(desktop): fetch profiles for reaction actors and thread-reply authors (#1550) refactor(desktop): unify EditAgentDialog styling with PersonaDialog (#1540) feat(desktop): add 10-minute message grouping window (#1578) feat(desktop): unify sidebar section actions into a per-section ⋮ menu (#1577) feat(desktop): emoji avatar picker for agents + reliable picker scroll (#1576) Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Summary
Adds an emoji avatar picker to the agent creation/edit avatar popover, alongside the existing image-upload flow, and makes the emoji grid reliably scrollable inside the popover.
Selecting an emoji renders a centered glyph on a colored circle (with a matched pencil-badge mask), and the color can be tuned from the swatch row or a custom color panel.
Screenshots
Filled emoji avatar — centered glyph on a colored circle with the masked pencil badge:
Emoji tab — scrollable picker + compact 12-column color swatches:
After selecting an emoji — auto-assigned color, custom-color swatch enabled, "Remove avatar":
Image tab — drop-or-browse zone + URL input, one big popover-wide drop target:
Changes
Popover+ stablePopoverAnchor(no reposition on select); emoji-tab-gated style injection + conditional Picker mount; centered glyph (zeroed offset) andMaskedAvatarBadgeFramepencil badge.useEmojiMartStylesnow installs a manualwheelhandler on emoji-mart's.scrollregion. WKWebView retargets wheel events outside the shadow root, so the default scroll never fired; the handler normalizes delta mode, clampsscrollTop, and reconnects viaMutationObserver. Profile benefits too.scrollTopincreases.text-[4rem]emoji glyph (matches existingAvatarStep/ProfileSettingsCardentries).Testing
tsc --noEmit✅ · biome ✅ ·check:px-text✅ ·check:file-sizes✅agents.spec.ts— 10/10 pass (incl. new emoji-scroll test)