Skip to content

feat(desktop): sender-derived background tints on message rows - #6925

Open
steinermatt wants to merge 1 commit into
block:mainfrom
steinermatt:sender-accent-colors
Open

feat(desktop): sender-derived background tints on message rows#6925
steinermatt wants to merge 1 commit into
block:mainfrom
steinermatt:sender-accent-colors

Conversation

@steinermatt

Copy link
Copy Markdown

Summary

  • Each message row gets a subtle, deterministic background tint (~10% opacity) computed from the sender's pubkey via a djb2-style hash → hue value. Every sender gets a stable, unique colour that persists across sessions.
  • Three files touched, frontend only. No Rust changes, no relay changes, no profile schema changes required.
  • The accentColor field is optional (string | undefined) — zero blast radius on callers that don't set it.

How it works

formatTimelineMessages calls pubkeyToAccentColor(authorPubkey) which hashes the pubkey string to a 0–359 hue, then returns hsla(hue, 65%, 55%, 0.10). The resulting string is stored on TimelineMessage.accentColor.

MessageRow renders an absolutely-positioned <div aria-hidden> with z-index: -10 as the first child of the <article>. Because the article creates its own stacking context (z-index: 10), the overlay lands:

  • above the article's own background (so the tint is always visible), and
  • below all normal-flow content (so text, avatars, reactions render cleanly on top).

This means hover:bg-muted/50, bg-blue-500/10 (reminder state), and the ::before route-target highlight animation all still work correctly — they're separate layers, not overridden.

The React.memo comparator is updated with accentColor so the row only re-renders when the colour actually changes (it won't — pubkeys are stable).

Files changed

File Change
desktop/src/features/messages/types.ts accentColor?: string field added to TimelineMessage
desktop/src/features/messages/lib/formatTimelineMessages.ts pubkeyToAccentColor() helper + field populated in the map
desktop/src/features/messages/ui/MessageRow.tsx Tint overlay div + memo comparator update

Test plan

  • TypeScript typecheck passes (tsc --noEmit, exit 0)
  • Biome lint passes (0 issues on changed files)
  • Desktop unit tests pass (5567/5567, exit 0)
  • Visual: open a channel with multiple senders — each sender has a distinct, subtle hue on their messages
  • Hover state: bg-muted/50 still appears on hover (tint persists underneath)
  • Highlighted message: route-target animation (::before) appears above the tint
  • Reminder state: bg-blue-500/10 composes correctly with the tint

Follow-up (separate PR)

Wire canonical colours from kind:0 profiles: agents that publish an accent_color field in their Nostr profile will have that colour used instead of the hash-derived one. The accentColor field on TimelineMessage is already the right hook.

🤖 Generated with Claude Code

Each message row gets a subtle, deterministic background tint (10% opacity
hsla) computed from the sender's pubkey via a djb2-style hash → hue. Every
sender gets a stable, unique colour across sessions; no profile changes or
relay changes required.

Three-touch, frontend only:
- TimelineMessage.accentColor?: string — new optional field
- formatTimelineMessages: pubkeyToAccentColor() helper populates it
- MessageRow: absolutely-positioned overlay div (z-index: -10, inset-0)
  so the tint lives behind content but above the article background;
  memo comparator updated to include accentColor

Follow-up PR will layer canonical colours from kind:0 profiles for agents
that publish an accent_color field.

Signed-off-by: Matthias Steiner <matthias.steiner@inscope.net>
@steinermatt
steinermatt requested a review from a team as a code owner August 27, 2026 08:33
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.

1 participant