Skip to content

fix(avatar): raft-ui sticker frame, crisp pixel faces, presence light per avatar step - #3207

Merged
stamp-build[bot] merged 6 commits into
agent/sindresorhus/visual-radiusfrom
agent/sindresorhus/avatar-polish
Oct 20, 2026
Merged

stamp-build[bot] merged 6 commits into
agent/sindresorhus/visual-radiusfrom
agent/sindresorhus/avatar-polish

Conversation

@stamp-build

@stamp-build stamp-build Bot commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

View in Stamp

sindresorhus from raft opened this pull request through Stamp


Avatar rendering aligned with the web raft-ui Avatar / AvatarBadge (artin, item 7). Stacked on #3205.

  • Frame: 4dp sticker radius + 1.5dp sticker-ink edge at every size; 1.5dp --sticker-edge outer ring (drawn outside, no layout change); image/pixel face inset inside the edge with 2dp corners.
  • Pixel avatars: one renderer on every platform, cell edges snapped to device pixels (no blur, no seams).
  • No-photo fallback: 1–2 bold initials sized per web step (placeholder icon only when there is no name) — visible change, matches web.
  • Presence light: RaftAvatarWithStatus derives size / ink edge / canvas cut-out / overhang from avatarSize (xl 20, lg 14, md 11, sm 9, xs 7, 3xs 6dp); call sites no longer hardcode statusSize. Same overhang fix in designsystem RaftAvatar (goldens re-recorded).
  • Avatar footprints unchanged (layout stays put); only the trim follows the nearest web step.

Known remaining diffs: presence lights still pulse where callers ask; cut-out is canvas colour, not the actual surface; uploaded photos always on lavender.

Checks: Android shared/designsystem/app compile + avatar unit tests + designsystem desktop screenshot tests pass locally; iOS via CI (full-ci). Visual Testing before/after posted in the PR thread.

🤖 Generated with Claude Code

@stamp-build stamp-build Bot added the full-ci label Oct 20, 2026
@stamp-build
stamp-build Bot force-pushed the agent/sindresorhus/visual-radius branch from df42794 to f6179f0 Compare October 20, 2026 05:39
@stamp-build
stamp-build Bot force-pushed the agent/sindresorhus/avatar-polish branch from 621d158 to 24f8650 Compare October 20, 2026 05:57
stamp-build Bot added a commit that referenced this pull request Oct 20, 2026
… error dialogs (#3227)

The first-character emulator lane failed at random on PRs (#3207, #3211,
#3219) for two infra reasons:
- the image's GMS crashed and ActivityManager killed our app because it
  depends on a provider in the dying process ("Process crashed."), which
  the classifier treated as a real failure;
- a system "isn't responding" dialog stole window focus, timing out
  every focus-dependent test after it.

run-instrumentation-with-retry.sh now reads the attempt's logcat (since the
attempt started; logcat is never cleared) and treats "Process crashed" as
an infra abort only when ActivityManager logged killing a build.raft.app
process for depending on a provider in a dying process. A real app crash
still fails fast. The suite also sets hide_error_dialogs during prep.

Co-authored-by: sindresorhus <raft+sindresorhus@agents.stamp.build>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@stamp-build
stamp-build Bot force-pushed the agent/sindresorhus/visual-radius branch from f6179f0 to 263220a Compare October 20, 2026 08:07
sindresorhus and others added 6 commits October 20, 2026 08:16
RaftAppAvatar now draws raft-ui 0.8.0 Avatar exactly:
- root: 4dp rounded-sticker, 1.5dp sticker-ink edge at every size (was a
  1dp line-strong edge on most call sites), plus the 1.5dp sticker-edge
  ring outside it (transparent in light, the die-cut edge in dark).
- face: inset inside the ink edge with 2dp corners (`rounded-[2px]`), so
  photos and pixel faces are no longer painted under the border.
- pixel avatars: one renderer on every platform; every 8x8 cell edge is
  snapped to whole device pixels (nearest-neighbour, no anti-aliased seams
  or smeared cells). Drops the Android-only fractional-cell path and the
  now unused isRaftNativePixelAvatarViewSupported expect/actuals.
- fallback: a person without a photo shows 1-2 initials (Space Grotesk
  bold, leading-none, -0.01em, sticker-ink) sized per raft-ui step
  (40/22/15/13/11/8; one initial at sm and below); the placeholder icon
  only when there is no name, at the step's icon size.
- RaftAvatarStep maps any avatar Dp onto the nearest raft-ui step.
- Call sites drop their hand-picked 1dp/1.5dp borderWidth; activity "SY"
  and external-author fallbacks and the muted overlay take the sticker
  corners.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rBadge

RaftAvatarWithStatus derives the light from the raft-ui step nearest its
avatarSize instead of every caller hardcoding statusSize:
- size: xl 20 / lg 14 / md 11 / sm 9 / xs+2xs 7 / 3xs 6dp;
- ink edge: 1.5dp (2dp on xl, 1dp on xs/2xs/3xs);
- cut-out: 2dp layer-canvas outline (1dp on xs/2xs/3xs) that follows the
  light's corners (outer radius = 2dp + outline), drawn behind the light
  with no layout of its own; `cutoutColor` names the surface;
- overhang: raft-ui's -8/-5/-4/-3/-2px offsets are measured from the
  avatar's padding edge, so the light passes the outer edge by the offset
  minus the 1.5dp ink border (6.5 / 3.5 / 2.5 / 1.5 / 0.5dp).
The status pixel itself takes raft-ui's `rounded-[2px]` (1px on the 6px
Status xs). `statusSize` stays as an override; all callers (thread, members,
search, create-channel, modal member rows, sidebar) drop theirs, and the
raft-ui library avatar fixture renders through the shared avatar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
raft-ui places AvatarBadge with right/bottom offsets that CSS measures
from the avatar's padding edge, inside its 1.5px ink border; the light
therefore overhangs the outer edge by the offset minus 1.5 (xl 6.5, lg 3.5,
md 2.5, sm 1.5, xs/2xs/3xs 0.5dp), not by the full offset. Goldens
re-recorded (avatar, avatar_list_row_states).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…able

RaftAvatarStep repeated every value of designsystem RaftAvatarSize (edge,
presence light size/border/cut-out/overhang, initials and icon size). The
step now wraps its RaftAvatarSize and reads those values from it (they are
public on RaftAvatarSize now); it only folds raft xs/2xs into one step.
`nearest` states its tie-break explicitly (halfway sizes take the larger
step) instead of an epsilon bias.

raftAvatarInitials / raftFirstGrapheme move to RaftAvatarInitials.kt, so
model and data code can derive initials without initialising the Avatar
file's drawing resources (android.graphics.Path in JVM unit tests).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rutal unchanged

Initials (raftAppAvatarInitials):
- always the raftAvatarInitials rule (first character of the first one or
  two words); the "short word is already initials" uppercase shortcut is
  gone. raft-ui itself takes initials as children, so this is the
  design-system port of avatar.md's 1-2 initials rule.
- what cannot stand as an initial (emoji, punctuation such as @ ? #, lone
  surrogates) is skipped at the start of each word; a label with no letter
  or digit yields "" and the placeholder icon shows. Characters are whole
  graphemes (surrogate pairs, combining marks); CJK keeps its first char.

Labels, fixed at the source:
- every model/parser `avatarLabel = name.take(1).uppercase()` (HomeParser,
  CanonicalMessageV2ThreadProjection, SharedThreadStore, MembersPage,
  ThreadPage, ThreadTimelineSlice, MemberAgentParser, ...) now uses
  toRaftAvatarLabel(): one whole initial, never half a surrogate pair.
- avatars that know the person's name get the full name (DM rows, the user
  header, thread authors, inline replies, members, channel settings,
  composer suggestions/mention actions, forward targets, the DM header,
  the foreground banner), so md+ avatars show two initials.
- no junk: the pinned-member "?" fallback, the member/author id fallbacks
  (ThreadPage member/author profiles, ThreadTimelineSlice, inline replies,
  modal member rows), the mention action "?"/target-type initial, the
  composer fixture's index labels and the "Custom avatar" picker label all
  give way to "" -> the icon.

Fill: agents without a photo sit on RaftColor.Inactive (web bg-inactive)
via raftAvatarFallbackFill(kind) at agent-aware call sites (members, agent
DM rows, thread authors with authorKind agent, followers, modal member
rows, activity senders, forward targets, mention actions); people stay
lavender.

Frame:
- the initials Text is hidden from semantics (clearAndSetSemantics {}).
- RaftAppAvatarFrame is internal; the external-author (Slack bridge)
  fallback and the activity "SY" app avatar draw through it with their own
  fill, so they get the dark-mode sticker-edge ring.
- its RoundedCornerShapes are top-level vals.
- brutal (!raftActive()) keeps its old avatar: 1dp black line drawn over
  the face, square corners, no ring, lavender fill and the 56% person icon
  instead of initials; only raft gets the sticker frame and initials.
- RaftUiAvatar reserves badgeOverhang + badgeCutout for the light instead
  of a flat 4dp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parameterized commonTest cases for RaftAvatarStep.nearest (edges and the
31/43/75dp ties, 44dp), raftAppAvatarInitials (emoji, lone surrogate, CJK,
whitespace, "@x", "?", one and two words, combining accent),
toRaftAvatarLabel, and pixelAvatarGridEdge at 10-16px (whole-pixel edges,
monotonic, every cell 1-2px wide).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@stamp-build
stamp-build Bot force-pushed the agent/sindresorhus/avatar-polish branch from 24f8650 to 75c1447 Compare October 20, 2026 08:19
@stamp-build
stamp-build Bot merged commit 352157b into agent/sindresorhus/visual-radius Oct 20, 2026
14 checks passed
stamp-build Bot added a commit that referenced this pull request Oct 20, 2026
… per avatar step

Re-applies #3207 (352157b), dropped when this branch was rebased onto main.

- RaftAppAvatar draws through RaftAppAvatarFrame: 4dp sticker, 1.5dp
  sticker-ink edge, dark-mode sticker-edge ring, face inset with 2dp corners.
- Pixel faces: one Canvas renderer with whole-pixel cell edges; the
  isRaftNativePixelAvatarViewSupported expect/actuals are gone.
- No photo: 1-2 initials sized per RaftAvatarStep (icon when there is no
  usable name) on fallbackFill (agents on inactive, people lavender).
- RaftAvatarWithStatus sizes the presence light, ink edge, cut-out and
  overhang from the nearest avatar step; replaces the simpler cut-out
  (cutoutWidth / avatarBorderWidth / AvatarSize) and callers drop statusSize.
- Models derive avatarLabel via toRaftAvatarLabel(); initials helpers move
  to designsystem RaftAvatarInitials.kt.

Ported onto main's structure: raft-ui is the only path (no brutal branches);
DirectMessageAvatar and MembersStickerAvatar keep main's layout and pass the
full name and fallback fill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
stamp-build Bot added a commit that referenced this pull request Oct 20, 2026
…hugging Activity filter (#3205)

* fix(ui): round Activity cards and inline replies, cut out avatar presence lights

- Activity rows: container radius (10dp) on the card, its border and the
  selected sticker shadow.
- Inline "N replies" surface: raft-ui MessageReplies rounded-container.
- RaftAvatarWithStatus: canvas-coloured cut-out around the presence light
  (2dp; 1dp with a 1dp ink edge for the 7dp light on xs/2xs avatars), as
  raft-ui 0.8.0 AvatarBadge. Sidebar channel/DM avatar light 8dp -> 7dp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(avatar): raft-ui sticker frame, crisp pixel faces, presence light per avatar step

Re-applies #3207 (352157b), dropped when this branch was rebased onto main.

- RaftAppAvatar draws through RaftAppAvatarFrame: 4dp sticker, 1.5dp
  sticker-ink edge, dark-mode sticker-edge ring, face inset with 2dp corners.
- Pixel faces: one Canvas renderer with whole-pixel cell edges; the
  isRaftNativePixelAvatarViewSupported expect/actuals are gone.
- No photo: 1-2 initials sized per RaftAvatarStep (icon when there is no
  usable name) on fallbackFill (agents on inactive, people lavender).
- RaftAvatarWithStatus sizes the presence light, ink edge, cut-out and
  overhang from the nearest avatar step; replaces the simpler cut-out
  (cutoutWidth / avatarBorderWidth / AvatarSize) and callers drop statusSize.
- Models derive avatarLabel via toRaftAvatarLabel(); initials helpers move
  to designsystem RaftAvatarInitials.kt.

Ported onto main's structure: raft-ui is the only path (no brutal branches);
DirectMessageAvatar and MembersStickerAvatar keep main's layout and pass the
full name and fallback fill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: sindresorhus <raft+sindresorhus@agents.stamp.build>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: stamp-build[bot] <328845999+stamp-build[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants