Skip to content

feat(desktop): emoji avatar picker for agents + reliable picker scroll#1576

Merged
wesbillman merged 10 commits into
mainfrom
kenny/agent-avatar-emoji-picker
Jul 7, 2026
Merged

feat(desktop): emoji avatar picker for agents + reliable picker scroll#1576
wesbillman merged 10 commits into
mainfrom
kenny/agent-avatar-emoji-picker

Conversation

@klopez4212

Copy link
Copy Markdown
Contributor

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:

avatar preview

Emoji tab — scrollable picker + compact 12-column color swatches:

emoji tab

After selecting an emoji — auto-assigned color, custom-color swatch enabled, "Remove avatar":

emoji selected

Image tab — drop-or-browse zone + URL input, one big popover-wide drop target:

image tab

Changes

  • AgentCreationPreview.tsx — Image/Emoji segment control with sliding-pill motion; unified empty/filled states into one Popover + stable PopoverAnchor (no reposition on select); emoji-tab-gated style injection + conditional Picker mount; centered glyph (zeroed offset) and MaskedAvatarBadgeFrame pencil badge.
  • ProfileAvatarEditor.utils.ts — Shared useEmojiMartStyles now installs a manual wheel handler on emoji-mart's .scroll region. WKWebView retargets wheel events outside the shadow root, so the default scroll never fired; the handler normalizes delta mode, clamps scrollTop, and reconnects via MutationObserver. Profile benefits too.
  • agents.spec.ts — E2E regression test that wheels the picker and asserts scrollTop increases.
  • check-px-text.mjs — Allowlist the decorative text-[4rem] emoji glyph (matches existing AvatarStep / ProfileSettingsCard entries).

Testing

  • tsc --noEmit ✅ · biome ✅ · check:px-text ✅ · check:file-sizes
  • E2E agents.spec.ts — 10/10 pass (incl. new emoji-scroll test)
  • Frontend-only change; Rust lint/tests not applicable.

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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 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".

Comment thread desktop/src/features/agents/ui/AgentCreationPreview.tsx
Comment thread desktop/src/features/agents/ui/AgentCreationPreview.tsx

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 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".

Comment thread desktop/src/features/agents/ui/managedAgentAvatar.ts
Comment thread desktop/src/features/agents/ui/AgentCreationPreview.tsx Outdated
npub1n6taw59js7ztz8pt9vma9uqx6g3gcvrwg37nmx9m8lq88yla0tjqfy2ncn and others added 3 commits July 7, 2026 12:05
- 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>
@klopez4212 klopez4212 force-pushed the kenny/agent-avatar-emoji-picker branch from b9417c4 to c03f908 Compare July 7, 2026 11:06

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 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".

Comment thread desktop/src/features/agents/ui/AgentCreationPreview.tsx
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>
@klopez4212 klopez4212 requested a review from wesbillman July 7, 2026 11:29

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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:

  1. 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.
  2. 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.

@wesbillman wesbillman merged commit ed84a87 into main Jul 7, 2026
25 checks passed
@wesbillman wesbillman deleted the kenny/agent-avatar-emoji-picker branch July 7, 2026 14:16
tellaho added a commit that referenced this pull request Jul 7, 2026
…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>
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