Repository navigation
Notes: toolbar avatar indicator only shows one thread's participants when a block has multiple notes #82273
Description
I ran into this while testing the new avatar indicator from PR #75147 - originally noted in this comment when closing PR #78205.
Claude dug through the indicator code and wrote up the details:
Description
When a block carries multiple note threads from multiple users, the avatar indicator in the block toolbar only reflects a single thread. In particular, when a block-level note exists alongside inline (selection) notes, only the block-level note's author shows in the toolbar - other users who left inline notes on the same block are not represented at all.
Step-by-step reproduction instructions
Note: this requires a collaborative setup with two users (eg. two browsers logged in as different users on the same post, with Notes enabled).
- As user A, select a block and add a block-level note. User A's avatar shows on the toolbar indicator.
- As user A, select some text in the same block and add a selection (inline) note.
- As user B, select different text in the same block and add another selection note.
- Select the block and check the avatar indicator in the block toolbar.
Expected: both users' avatar icons show.
Actual: only the block-level note's avatar shows.
![]()
Next, deleting the block-level note:
![]()
Now both icons show:
![]()
The block-level note appears to take precedence when multiple users have left inline notes.
What the code does
Reading trunk, the behavior traces to the indicator being wired to exactly one "primary" thread per block rather than aggregating across the block's threads:
NotesSidebarcollects all threads for the selected block, then narrows to a single thread viapickPrimaryNote()and renders oneNoteAvatarIndicatorwith just that thread:gutenberg/packages/editor/src/components/collab-sidebar/index.js
Lines 143 to 165 in 75768b4
// Surface one thread for the avatar indicator. const currentThreads = blockNoteIds.length > 0 ? notes.filter( ( thread ) => blockNoteIds.includes( thread.id ) ) : []; const currentThread = pickPrimaryNote( currentThreads ); if ( isDistractionFree ) { return <AddNoteMenuItem isDistractionFree />; } return ( <> <NoteHighlightStyles threads={ unresolvedNotes } selectedId={ selectedNoteId } /> { !! currentThread && ( <NoteAvatarIndicator note={ currentThread } onClick={ () => openNoteForBlock( clientId ) } /> ) } pickPrimaryNote()returns the first unresolved (hold) thread, falling back to the first thread:gutenberg/packages/editor/src/components/collab-sidebar/utils.js
Lines 425 to 431 in 75768b4
export function pickPrimaryNote( threads ) { return ( threads.find( ( thread ) => thread.status === 'hold' ) ?? threads[ 0 ] ?? null ); } - Thread ordering within a block puts block-level notes first (
BLOCK_LEVEL_NOTE_START = -1, so they sort ahead of inline notes ordered by marker offset):andgutenberg/packages/editor/src/components/collab-sidebar/hooks.js
Lines 115 to 143 in 75768b4
// Order within a block: block-level notes (no inline anchor) come // first as the "overall comment", then inline notes ascending by // marker start offset. Ties (rare; two markers at the same offset) // fall back to creation order via thread id. Blocks themselves are // already iterated in document order above. const unresolved = []; const resolved = []; for ( const [ clientId, noteIds ] of Object.entries( blocksWithNotes ) ) { const attributes = getBlockAttributes( clientId ); const orderedThreads = noteIds .map( ( noteId ) => { const thread = threadsById.get( noteId ); if ( ! thread ) { return null; } return { thread, start: getInlineMarkerStart( thread, attributes ), }; } ) .filter( Boolean ) .sort( ( a, b ) => { if ( a.start !== b.start ) { return a.start - b.start; } return a.thread.id - b.thread.id; } );
export const BLOCK_LEVEL_NOTE_START = -1; NoteAvatarIndicatorthen builds its participant list only from that single thread - the root note plus its replies ([ note, ...note.reply ]):gutenberg/packages/editor/src/components/collab-sidebar/note-indicator-toolbar.js
Lines 66 to 98 in 75768b4
export function NoteAvatarIndicator( { onClick, note } ) { const threadParticipants = useMemo( () => { if ( ! note ) { return []; } // Track thread participants (original author + repliers), sorted by // date so they appear in chronological order. const participantsMap = new Map(); const allNotes = [ note, ...note.reply ].sort( ( a, b ) => new Date( a.date ) - new Date( b.date ) ); for ( const entry of allNotes ) { if ( ! entry.author_name || participantsMap.has( entry.author ) ) { continue; } participantsMap.set( entry.author, { id: entry.author, name: entry.author_name, avatar: entry.author_avatar_urls?.[ '48' ] || entry.author_avatar_urls?.[ '96' ], } ); } return Array.from( participantsMap.values() ); }, [ note ] ); if ( ! threadParticipants.length ) { return null; } So the "block-level note wins" symptom is really the ordering plus the single-thread pick: an unresolved block-level thread always sorts first, gets picked, and its participants are the only avatars rendered. Other users' separate threads on the block never contribute an avatar.
Worth noting: in the repro above, the two avatars that appear after deleting the block-level note are (per the code) the participants of a single thread - the newly picked inline thread happened to have a reply from the second user. Two inline threads by two users with no cross-replies would likely still show only one avatar, even with no block-level note involved. Untested, but that is what the code reads as.
Possible direction
The indicator could aggregate participants across all of the block's unresolved threads instead of a single picked thread - eg. compute the participant map in
NotesSidebarovercurrentThreadsrather than passing one thread toNoteAvatarIndicator. The existing overflow handling ("+n" past three avatars) already accommodates larger participant lists. There may be a deliberate reason for surfacing only the primary thread, though - input from the folks who worked on PR #75147 would help.
Related
- Avatar indicator introduced in PR #75147
- Found while testing/closing PR #78205
- Notes tracking issue: #73411
AI Use
The investigation and this write-up came out of a session with 🤖 Claude Code; no code changes here, just the report.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fields🏗️ In Progress



Activity
adamsilverstein commentedon Aug 31, 2026
That makes sense to me, the button is designed to handle multiple avatars and when we enabled multiple inline notes per block in 7.1, the intention was to show the avatars of each active participant here - it helps users get some quick context about the notes. I feel like it should be showing the avatars of any users participating in unresolved threads (with the 3+ caveat). This would include block level, inline as well as replies.