Skip to content

client: a Workflow node avatar takes the first UTF-16 unit, so a name starting with an emoji shows � #2017

Closed

Description

@HugoXK

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.

Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: cdeaf6e53bc74b81866f828c49889cdf
Branch: dev/1-workflow-avatar-initial
Base: b207364
At: 2026-11-01T09:44:28Z

Dedup (2026-11-01, live main b207364f plus every open and closed issue/PR): nothing covers this — #1459 (closed) was about broken flowchart edges and hiding the composer, #464 (closed) was one look per Bot in face-to-face rooms, and neither touches the node avatar's initial. The sibling text-bound work (#2002, #2005, #2006, #2007) is about cutting a string to a length; this is taking a first character, so it is not covered by any of them either.

What happens

core/src/client/workflows.js:302 draws each node of the Workflow graph with a one-character avatar:

h('span', { className: 'bt-wf-av', style: { background: COLORS[node.name] ?? '#4d6bfe' } }, (node.name ?? '?').slice(0, 1)),

slice counts UTF-16 units, so a name that starts with an emoji or any other astral character is cut through the surrogate pair. '🚀 Deployer'.slice(0, 1) is "\ud83d" — not well formed, and every encoder draws that as the replacement character — so the avatar shows � where the name's first character is 🚀. Array.from('🚀 Deployer')[0] is '🚀' and well formed.

The empty-name fallback is fine today (?); only a name whose first character is outside the BMP is affected.

What it should do

  • Take the first code point, not the first UTF-16 unit: Array.from(String(node.name ?? '').trim())[0] ?? '?'.
  • Do not use cut(node.name, 1): that helper keeps the longest prefix that stays inside a budget, so an emoji first character is dropped whole and it returns an empty string — the avatar would fall back to ? for a name that has a perfectly good first character.
  • Keep the case as it is: this issue is about the broken character, not about typography. core/src/client/mark.js:96 (personInitial) already takes the first code point but upper-cases it; adopting it is acceptable only if the PR body says in so many words that this card's initials become upper case (a visible change), otherwise use the plain Array.from form.
  • A name that is empty or whitespace still shows ?.

Reproduce

core/test/workflows-page.test.js already renders the graph, so the case goes there: a node named '🚀 Deployer' renders a .bt-wf-av whose text is '🚀' and is well formed; a node with no name still renders '?'. On the page, a workflow whose first node is named with a leading emoji shows � in its coloured avatar circle today.

Scope

  • core/src/client/workflows.js:302 — the one expression (a local helper is fine if it reads better).
  • core/test/workflows-page.test.js — the two cases above.
  • core/client.js — rebuilt with node core/scripts/build-client.mjs (the same bundle npm run verify checks with --check).
  • patches/ds-bot/files/src/client/workflows.js — the full-file mirror that exists at this ref; regenerate it, do not hand-edit.
  • a changelog.d/ fragment, one paragraph, in the shape the recent fixes use.

Not in scope: the colour picked from the name (COLORS), the fallback for a missing name, and any other place that takes a first character.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions