Skip to content

feat: add custom emoji from settings - #346

Merged
wesbillman merged 8 commits into
mainfrom
cid/custom-emoji-add
Sep 28, 2026
Merged

wesbillman merged 8 commits into
mainfrom
cid/custom-emoji-add

Conversation

@kalvinnchau

@kalvinnchau kalvinnchau commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Adds the custom emoji authoring flow to Settings → Custom emoji, matching the block/buzz reference behavior and copy.

Behavior

  • Add emoji uploads an image through the session's existing attachment uploader, suggests a name from the filename (Party Parrot.png → party_parrot), validates it, and publishes the viewer's own kind-30030 set (d=buzz:custom-emoji).
  • Adds are serialized as read → update → publish against the latest own set. Saving an existing name replaces its image and shows the replace hint.
  • New emoji appear immediately in My emoji, the picker, : typeahead, messages, and reactions. They persist across reloads and stay scoped to the community session that authored them.
  • Upload failures, publish rejections, and timeouts map to the reference copy, and Save is re-enabled for retry. Disposing the session cancels in-flight and queued adds.
  • session.emoji gets authoring only when the transport provides an uploader and a writer that accepts kind 30030 (src/features/relay/session.ts). When it can't author, the add form and its "Add one above." empty-state line are hidden, and My emoji is still shown.
  • The dev broker admits kind 30030 for signing and publishing. docs/relay-queries.md documents the session.emoji authoring contract.

Tests

  • Unit/component: src/features/relay/emoji.test.ts covers replace tags, serialized adds, failure and timeout mapping, dispose during running and queued adds, reload, and message tags. src/bundled/emoji/CustomEmojiSettings.test.tsx covers upload → suggested name → save → list, invalid names, upload failure, publish rejection with re-enabled Save, and the fallback when the session can't author. Broker admission is covered in dev/relay-broker-api.test.mjs.
  • Browser journey (added): tests/browser/custom-emoji-authoring.spec.mjs runs the production broker transport and relay session against a local upstream relay and Blossom model. It covers upload rejection and retry, publish rejection and retry, reload persistence, typeahead/picker/message/reaction use of the new emoji, replacement, and isolation between two community sessions. It needs a browser because it checks real upload, broker signing, and rendered-image boundaries that the component tests mock. During development it caught a missing thumbnail route for reaction pills, which failed before the fix and passed after. tests/browser/settings.spec.mjs covers the new Settings entry.
  • Run locally: tsc --noEmit. Vitest across src/features/relay/, src/features/messages/, src/bundled/emoji/, and the broker tests (131 files, 1953 tests). Separately, read-state.test.ts, workflow-broker.test.mjs, and pages.integration.test.mjs. Playwright custom-emoji-authoring.spec.mjs and settings.spec.mjs in Chromium and WebKit (20/20).

Deferred: the native app build, and running against a deployed relay/Blossom.

cid added 5 commits September 28, 2026 08:35
Signed-off-by: cid <d9f92a72922bf45c17379a47d64dae84b6020397c2d5a52b5317d512068cd9d3@buzz.block.builderlab.xyz>
Add a browser journey that uploads, publishes, reloads, uses, replaces and
scopes custom emoji through the production broker and a local upstream relay.
Cover timeout, dispose and publish-rejection paths, hide the add form when the
session cannot author emoji, and document the session.emoji authoring contract.

Signed-off-by: cid <d9f92a72922bf45c17379a47d64dae84b6020397c2d5a52b5317d512068cd9d3@buzz.block.builderlab.xyz>
… in spec

Show the "Add one above." empty-state line only when the add form renders.
In the authoring journey, choose the typeahead suggestion only after the
Unicode search settles; a click on the interim list is not accepted.

Signed-off-by: cid <d9f92a72922bf45c17379a47d64dae84b6020397c2d5a52b5317d512068cd9d3@buzz.block.builderlab.xyz>
Reaction pills now load the small media variant, so the local upstream serves
<hash>.thumb.jpg from the stored blob, and the journey asserts the reaction
image loads before and after replacement.

Signed-off-by: cid <d9f92a72922bf45c17379a47d64dae84b6020397c2d5a52b5317d512068cd9d3@buzz.block.builderlab.xyz>
…sults

Signed-off-by: cid <d9f92a72922bf45c17379a47d64dae84b6020397c2d5a52b5317d512068cd9d3@buzz.block.builderlab.xyz>
@kalvinnchau
kalvinnchau marked this pull request as ready for review September 28, 2026 15:57
@kalvinnchau
kalvinnchau requested review from a team, comp615 and wesbillman as code owners September 28, 2026 15:57
Signed-off-by: cid <d9f92a72922bf45c17379a47d64dae84b6020397c2d5a52b5317d512068cd9d3@buzz.block.builderlab.xyz>

@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.

Automated source review by Star Lord, published through Wes's account (wesbillman).

Reviewed head 3965ca2e12c8903e5dd2d9401e3adce234289d74 against base 1d19153276b93ef733fe5e8ead888fc528fc00e3.

Three actionable findings: two P2 issues (the read-only own-set projection and an upload completion overwriting newer name input), plus one P3 form-reset race. Details and smallest repair guidance are inline. The existing reader/uploader/writer ownership and local add serialization are appropriate; these defects can be fixed within the current design.

Scope: Settings registration/form state, own-set read/modify/publish, broker admission, session disposal, and the added test/fixture source. Mantis completed an independent source-only authoring/admission/disposal lane with no additional findings; I integrated the results.

Validation limits: source analysis only, using 32 files verified against the pinned Git blobs; no PR code, tests, builds, browser/native app, or deployed relay/Blossom workflow was executed. The PR's reported test results were not independently verified; CI was not checked in this cycle. This COMMENT review is not an approval or merge authorization.

Comment thread src/features/relay/emoji-directory.ts Outdated
Comment thread src/bundled/emoji/CustomEmojiSettings.tsx Outdated
Comment thread src/bundled/emoji/CustomEmojiSettings.tsx Outdated
@kalvinnchau
kalvinnchau marked this pull request as draft September 28, 2026 16:36
@kalvinnchau
kalvinnchau marked this pull request as ready for review September 28, 2026 16:36
Co-authored-by: Kalvin Chau <kalvin@block.xyz>

Signed-off-by: Kalvin Chau <kalvin@block.xyz>
@kalvinnchau
kalvinnchau marked this pull request as draft September 28, 2026 16:41
@kalvinnchau
kalvinnchau marked this pull request as ready for review September 28, 2026 16:48
Co-authored-by: Kalvin Chau <kalvin@block.xyz>

Signed-off-by: Kalvin Chau <kalvin@block.xyz>
@kalvinnchau
kalvinnchau marked this pull request as draft September 28, 2026 16:53
@kalvinnchau
kalvinnchau marked this pull request as ready for review September 28, 2026 16:58

@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.

Star Lord automated source review

Published via Wes’s account; non-blocking COMMENT review.

The three prior emoji findings are addressed. One new P3 on the additional timeout-only workaround is attached inline.

  • Head: f18b60f16e7011d3abca8b7a34f7f4d9cf6c9c88
  • Base: 1d19153276b93ef733fe5e8ead888fc528fc00e3
  • Follow-up from reviewed head 3965ca2e12c8903e5dd2d9401e3adce234289d74; inspected the five-file delta and affected callers.

Resolved in source:

  • The viewer is now passed independently of authoring capability (session.ts:566–590), and the own-set projection uses that identity (emoji-directory.ts:105–107). The mounted regression seeds an existing own set and checks both missing-uploader and non-30030-writer sessions.
  • Upload completion reads the current name rather than the pre-upload closure (CustomEmojiSettings.tsx:74–75,107–109), preserving the demonstrated mid-upload edit.
  • Clear is disabled while uploading (CustomEmojiSettings.tsx:238), preventing the pending upload from undoing a reset. The deferred-upload regression checks preserved text, disabled Clear, and clearing after completion.

The changed directory signature is consistent with the call sites found across the pinned JS/TS source. No additional product defect was identified in this bounded follow-up; unchanged authoring internals were not reopened. All eight PR commits have DCO trailers.

Validation limits: source-only; 23 extracted files verified against pinned Git blobs, plus a hash-verified JS/TS call-site search; no dirty checkout inputs. Tests were read, not run. No builds, PR-code execution, app/native launches, or deployed relay/Blossom workflows.

A one-time hosted CI snapshot showed the required Linux lanes, all six browser journey shards, security checks, and DCO successful; Windows native validation was skipped. To assess the timeout change, I read the preceding and current JavaScript logs: 392 files/4,580 tests, 333.07s wall and 571.42s summed test time versus 397 files/4,679 tests, 285.29s wall and 491.95s summed test time. The different test populations mean this is not a controlled before/after measurement. No CI polling or reruns were performed. Runtime behavior and human acceptance remain unverified; this is not an approval or merge authorization.

Comment thread dev/vite-config.test.mjs
},
encoding: "utf8",
timeout: 10_000,
timeout: 30_000,

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.

[P3] Remove the timeout-only workaround for the config-load failure

This follow-up only raises the subprocess deadline from 10 to 30 seconds (and the enclosing test from 15 to 35 seconds); it does not change the work or its isolation. The preceding JavaScript job 109031770218 failed here with spawnSync ... ETIMEDOUT at about 10.1 seconds. This now allows the same unexplained slowdown to pass for another 20 seconds, contrary to AGENTS.md:144–146's explicit rule against hiding failures with longer timeouts.

Please drop this unrelated timeout-only change from the emoji fix, or address the config-loading/fixture bottleneck at its owner and provide comparable timing evidence. The latest successful job ran this test in 8.65 seconds—already below the old deadline—so that green result does not establish that the larger budget fixes the failure mechanism. All functional assertions remain, but the cause of the intermittent timeout is still unverified.

@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.

Carl, an automated reviewer, commenting via Wes’s GitHub account.

No blocking findings at this head. The three earlier findings are fixed: read-only own-set display, preserving names typed during upload, and preventing Clear from being undone by upload completion. Each has current component regression coverage.

Reviewed f18b60f16e7011d3abca8b7a34f7f4d9cf6c9c88 against 1d19153276b93ef733fe5e8ead888fc528fc00e3. Integrated independent UI/lifecycle, concurrency/persistence, and broker/protocol/privacy review lanes.

The scope is coherent: Settings adds upload, naming and replacement to the existing emoji directory. Session ownership, uploader, verified reader and writer are reused; there is no parallel service or persistence layer. Same-session serialization preserves preceding additions; this does not provide cross-device compare-and-swap, nor does the existing Desktop reference. Full-diff and PR-text inspection found no internal-information leak or accidentally committed review media.

Validation: independently ran the complete custom-emoji authoring browser file in Chromium and WebKit at the pinned, unchanged head: 2/2 passed. It exercises upload/publish failure and retry, reload, picker/typeahead, message/reaction rendering, replacement retaining historical images, and community separation through the real development broker with a simulated local upstream. Hosted CI at this head is green, including 397 Vitest files / 4,679 tests; broad suites were not duplicated locally. This is not native-app or deployed relay/Blossom acceptance, and no human test confirmation was established here.

Two optional low-priority improvements are inline. The unrelated Vite-test timeout increase corresponds to a recorded ETIMEDOUT on the preceding head, but a larger timeout alone is not proof that the underlying flake is fixed; keep that claim separate from emoji correctness.

Remaining gate: required human review/testing and GitHub approval. This COMMENT is not approval or merge authorization.

type="submit"
variant="primary"
loading={saving}
disabled={!canSubmit}

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.

Optional, low priority: preserve the design-system loading button's keyboard focus behavior. canSubmit becomes false while saving, so passing disabled={!canSubmit} together with loading={saving} disables Button's default focusableWhenDisabled behavior (Button.tsx:44–46). Follow the existing ProfileSettings pattern, disabled={!canSubmit && !saving}, so Save remains focusable but non-activatable while pending. A deferred-save keyboard check would cover it. This is a source-level accessibility observation, not a browser-reproduced blocker.

e.content !== "" ||
!Number.isSafeInteger(e.created_at) ||
(e.created_at ?? -1) < 0 ||
(e.created_at ?? 0) > now + 300 ||

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.

Optional, low priority: document or align this future-time ceiling with relay admission. publishAdd chooses max(now, current.created_at + 1), so an existing own set timestamped ten minutes ahead by another client makes an ordinary add fail this five-minute broker check until the clock catches up. The relay's general ingest bound is ±900 seconds (block/buzz, 447951a1, crates/buzz-relay/src/handlers/ingest.rs:2316–2322). Consider an explicitly shared compatible bound, or specific clock-skew feedback, rather than silently changing replacement ordering. This is bounded interoperability polish, not a request for cross-device locking.

@wesbillman
wesbillman merged commit e32b7a6 into main Sep 28, 2026
14 checks passed
@wesbillman
wesbillman deleted the cid/custom-emoji-add branch September 28, 2026 17:38
johnmatthewtennant pushed a commit that referenced this pull request Sep 29, 2026
* origin/main:
  Keep custom emoji animated in reactions (#354)
  Polish community dialogs, agent cards, and conversation controls (#342)
  fix(channels): paginate membership discovery beyond 500 channels (#326)
  Remove local project context from docs (#350)
  feat(github): render PR descriptions with inline media (#335)
  feat(dev): measure channel opens, warming cost and live setup (#315)
  fix(agents): start new agents on Create and make their status clear (#332)
  fix(macos): close the window without quitting Buzz (#349)
  fix: allow parallel desktop dev worktrees (#336)
  feat(relay): complete packaged community access and recovery (#338)
  fix(workflows): clarify controls and align compact workflow UI (#337)
  feat: add custom emoji from settings (#346)
  feat(channels): show typing status on sidebar dm rows (#305)
  fix(workflows): page batched definition reads (#325)

Signed-off-by: Sol <49aa1f65411fd096d2e2ec144f1e7aa36fdc76d1b907cfdf7be000c66f9d3b8e@buzz.block.builderlab.xyz>

# Conflicts:
#	src/bundled/agents/AgentsPage.tsx
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