{
  "version": 3,
  "sources": ["../../../src/components/collab-sidebar/utils.js"],
  "sourcesContent": ["import { _x } from '@wordpress/i18n';\nimport { create, RichTextData } from '@wordpress/rich-text';\nimport { getRectangleFromRange } from '@wordpress/dom';\n\n/**\n * Sanitizes a note string by trimming leading and trailing whitespace.\n *\n * @param {string} str - The note string to sanitize.\n * @return {string} - The sanitized note string.\n */\nexport function sanitizeNoteContent( str ) {\n\treturn str.trim();\n}\n\nconst THREAD_ALIGN_OFFSET = -16;\nconst THREAD_GAP = 16;\nconst OVERLAP_MARGIN = 20;\n\n/**\n * Avatar border colors chosen to be visually distinct from each other and from\n * the editor's semantic UI colors (Delta E > 10 between all pairs).\n */\nconst AVATAR_BORDER_COLORS = [\n\t'#6F42C1', // Purple\n\t'#D94145', // Red\n\t'#FBBF24', // Orange\n\t'#FF35EE', // Magenta\n\t'#879F11', // Olive\n\t'#0F766E', // Teal\n\t'#00CFFF', // Cyan\n];\n\n/**\n * Gets the border color for an avatar based on the user ID.\n *\n * Always returns a 6-digit `#RRGGBB` hex string; callers (e.g. the highlight\n * styles) rely on this format to append alpha suffixes.\n *\n * @param {number} userId - The user ID.\n * @return {string} - The border color as a `#RRGGBB` hex string.\n */\nexport function getAvatarBorderColor( userId ) {\n\treturn AVATAR_BORDER_COLORS[ userId % AVATAR_BORDER_COLORS.length ];\n}\n\n/**\n * Generates a note excerpt from text based on word count type and length.\n *\n * @param {string} text          - The note text to generate excerpt from.\n * @param {number} excerptLength - The maximum length for the note excerpt.\n * @return {string} - The generated note excerpt.\n */\nexport function getNoteExcerpt( text, excerptLength = 10 ) {\n\tif ( ! text ) {\n\t\treturn '';\n\t}\n\n\t/*\n\t * translators: If your word count is based on single characters (e.g. East Asian characters),\n\t * enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'.\n\t * Do not translate into your own language.\n\t */\n\tconst wordCountType = _x( 'words', 'Word count type. Do not translate!' );\n\n\tconst rawText = text.trim();\n\tlet trimmedExcerpt = '';\n\n\tif ( wordCountType === 'words' ) {\n\t\ttrimmedExcerpt = rawText.split( ' ', excerptLength ).join( ' ' );\n\t} else if ( wordCountType === 'characters_excluding_spaces' ) {\n\t\t/*\n\t\t * 1. Split the text at the character limit,\n\t\t * then join the substrings back into one string.\n\t\t * 2. Count the number of spaces in the text\n\t\t * by comparing the lengths of the string with and without spaces.\n\t\t * 3. Add the number to the length of the visible excerpt,\n\t\t * so that the spaces are excluded from the word count.\n\t\t */\n\t\tconst textWithSpaces = rawText.split( '', excerptLength ).join( '' );\n\n\t\tconst numberOfSpaces =\n\t\t\ttextWithSpaces.length - textWithSpaces.replaceAll( ' ', '' ).length;\n\n\t\ttrimmedExcerpt = rawText\n\t\t\t.split( '', excerptLength + numberOfSpaces )\n\t\t\t.join( '' );\n\t} else if ( wordCountType === 'characters_including_spaces' ) {\n\t\ttrimmedExcerpt = rawText.split( '', excerptLength ).join( '' );\n\t}\n\n\tconst isTrimmed = trimmedExcerpt !== rawText;\n\treturn isTrimmed ? trimmedExcerpt + '…' : trimmedExcerpt;\n}\n\n/**\n * Normalizes noteId metadata to always return an array of unique numeric ids,\n * preserving insertion order. Handles both scalar (legacy, possibly\n * string-typed) and array (new) values.\n *\n * @param {Object} metadata Block metadata object\n * @return {number[]} Array of note IDs (may be empty)\n */\nexport function getNoteIdsFromMetadata( metadata ) {\n\tconst noteId = metadata?.noteId;\n\tconst raw = Array.isArray( noteId ) ? noteId : [ noteId ];\n\tconst ids = new Set();\n\tfor ( const value of raw ) {\n\t\tconst id = Number( value );\n\t\tif ( Number.isFinite( id ) && id > 0 ) {\n\t\t\tids.add( id );\n\t\t}\n\t}\n\treturn [ ...ids ];\n}\n\n/**\n * Adds a note ID to the metadata.\n * Converts scalar to array if needed, otherwise appends.\n *\n * @param {Object} metadata Existing block metadata\n * @param {number} noteId   Note ID to add\n * @return {Object} Updated metadata object\n */\nexport function addNoteIdToMetadata( metadata, noteId ) {\n\tconst ids = new Set( getNoteIdsFromMetadata( metadata ) );\n\tconst id = Number( noteId );\n\tif ( ids.has( id ) ) {\n\t\treturn metadata;\n\t}\n\tids.add( id );\n\treturn { ...metadata, noteId: [ ...ids ] };\n}\n\nconst NOTE_FORMAT_TYPE = 'core/note';\n\n/**\n * Search a rich-text value for a `core/note` marker matching `noteId` and\n * return its character range. Used to derive an inline note's anchor from\n * the in-content marker (resilient to edits) rather than stale offset meta.\n *\n * @param {*}             value  Block attribute value (RichTextData, string, or other).\n * @param {number|string} noteId Note id to search for.\n * @return {?{start: number, end: number}} Range or null when no marker is found.\n */\nexport function findNoteRange( value, noteId ) {\n\tif ( noteId === undefined || noteId === null ) {\n\t\treturn null;\n\t}\n\tlet html = null;\n\tif ( value instanceof RichTextData ) {\n\t\thtml = value.toHTMLString();\n\t} else if ( typeof value === 'string' ) {\n\t\thtml = value;\n\t}\n\tif ( ! html || html.indexOf( 'wp-note' ) === -1 ) {\n\t\treturn null;\n\t}\n\tconst target = String( noteId );\n\tconst record = create( { html } );\n\tconst formats = record.formats;\n\tlet start = -1;\n\tfor ( let i = 0; i < formats.length; i++ ) {\n\t\tconst stack = formats[ i ];\n\t\tconst hit = stack?.find(\n\t\t\t( f ) =>\n\t\t\t\tf.type === NOTE_FORMAT_TYPE &&\n\t\t\t\tf.attributes &&\n\t\t\t\tf.attributes[ 'data-id' ] === target\n\t\t);\n\t\tif ( hit ) {\n\t\t\tif ( start === -1 ) {\n\t\t\t\tstart = i;\n\t\t\t}\n\t\t} else if ( start !== -1 ) {\n\t\t\treturn { start, end: i };\n\t\t}\n\t}\n\tif ( start !== -1 ) {\n\t\treturn { start, end: formats.length };\n\t}\n\treturn null;\n}\n\n/**\n * Locate a note's in-content `core/note` marker across all of a block's\n * attributes. The marker (carrying `data-id`) is the single source of truth for\n * an inline note's anchor: a note is inline iff a marker with its id exists in\n * the block, and the attribute that holds it is discovered here rather than\n * stored separately. Returns the matching attribute key and the marker range.\n *\n * @param {?Object}       attributes Block attributes, or null/undefined when unloaded.\n * @param {number|string} noteId     Note id to search for.\n * @return {?{attributeKey: string, start: number, end: number}} Anchor or null when no marker is found.\n */\nexport function findNoteInBlock( attributes, noteId ) {\n\tif ( ! attributes ) {\n\t\treturn null;\n\t}\n\tfor ( const attributeKey of Object.keys( attributes ) ) {\n\t\tconst range = findNoteRange( attributes[ attributeKey ], noteId );\n\t\tif ( range ) {\n\t\t\treturn { attributeKey, start: range.start, end: range.end };\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Build the CSS selector matching a note's in-content `core/note` marker in\n * the editor canvas. The format serializes as `<mark class=\"wp-note\">` with\n * the note id in `data-id`.\n *\n * @param {number|string} noteId Note id the marker carries.\n * @return {string} Selector for the note's marker element(s).\n */\nexport function getNoteMarkerSelector( noteId ) {\n\t/*\n\t * `noteId` is a server comment ID (always a positive integer), but the\n\t * value composes a selector from stored data, so escape it defensively.\n\t *\n\t * Deliberately not `CSS.escape`: that escapes for *identifier* context,\n\t * where a leading digit is illegal, so it renders the id 7 as `\\37 `.\n\t * That is valid, and matches, but it makes every rule\n\t * `buildHighlightCss` generates unreadable. Inside a quoted attribute\n\t * value the only characters that need escaping are the quote, the\n\t * backslash, and raw line breaks (a parse error in a CSS string).\n\t */\n\tconst escapedId = String( noteId ).replace( /[\"\\\\\\n\\r\\f]/g, ( char ) =>\n\t\tchar === '\"' || char === '\\\\'\n\t\t\t? `\\\\${ char }`\n\t\t\t: `\\\\${ char.codePointAt( 0 ).toString( 16 ) } `\n\t);\n\treturn `mark.wp-note[data-id=\"${ escapedId }\"]`;\n}\n\n/**\n * Measure the bounding rect of the current text selection within a block\n * element, or return null when there is no usable selection (collapsed, or\n * not fully inside the block). A pending new note has no in-content marker\n * yet, so the selection it will attach to is the only anchor available for\n * positioning its floating form.\n *\n * @param {HTMLElement} blockEl Block DOM element to resolve the selection in.\n * @return {?DOMRect} Selection rect, or null.\n */\nexport function getSelectionRect( blockEl ) {\n\tconst selection = blockEl.ownerDocument.defaultView?.getSelection();\n\tif ( ! selection || selection.rangeCount === 0 || selection.isCollapsed ) {\n\t\treturn null;\n\t}\n\tconst range = selection.getRangeAt( 0 );\n\t// `isCollapsed` can be false with a collapsed first range, and\n\t// `getRectangleFromRange` measures those by inserting a temporary node.\n\tif ( range.collapsed ) {\n\t\treturn null;\n\t}\n\tif ( ! blockEl.contains( range.commonAncestorContainer ) ) {\n\t\treturn null;\n\t}\n\t// `getRectangleFromRange` over `Range.getBoundingClientRect()`: it drops\n\t// the hairline rects a selection picks up at a line's edge, so a\n\t// selection starting at the end of one line aligns to the line that\n\t// actually holds the text rather than to the line above it.\n\tconst rect = getRectangleFromRange( range );\n\t// A range with no rendered client rects still yields an all-zero rect\n\t// rather than null, which would pin the thread to the top of the canvas.\n\t// Treat it as \"no usable selection\" so callers fall back to the block.\n\tif ( ! rect || ( rect.width === 0 && rect.height === 0 ) ) {\n\t\treturn null;\n\t}\n\treturn rect;\n}\n\n// Sentinel that sorts a block-level (whole-block) note before any inline note\n// within the same block. Negative so any real character offset (>= 0) ranks\n// after it. Number.NEGATIVE_INFINITY would work too; -1 is enough and keeps\n// the diff arithmetic in safe integers.\nexport const BLOCK_LEVEL_NOTE_START = -1;\n\n/**\n * Resolve an inline note's character offset in its block so threads can be\n * sorted by reading order. A note is inline iff an in-content `core/note`\n * marker carries its id; block-level notes (no marker) sort first within their\n * block via a sentinel.\n *\n * @param {Object}  thread     Materialized thread record (with `.id`).\n * @param {?Object} attributes Block attributes for the thread's block.\n * @return {number} Marker start offset, or `BLOCK_LEVEL_NOTE_START` when there is no inline anchor.\n */\nexport function getInlineMarkerStart( thread, attributes ) {\n\tconst found = findNoteInBlock( attributes, thread?.id );\n\treturn found ? found.start : BLOCK_LEVEL_NOTE_START;\n}\n\n/**\n * Apply a `core/note` marker across `[start, end)` without removing notes\n * already present in that range.\n *\n * Rich-text's `applyFormat` strips any existing format of the same type before\n * applying, so two `core/note` markers can't coexist - a note drawn over an\n * existing one would wipe it in the overlap. This keeps every overlapping note\n * and orders the markers outermost-first by span, so a note fully contained in\n * another nests inside it (`<mark><mark>…</mark></mark>`). Crossing (partial)\n * overlaps can't nest in HTML and serialize as split runs, but each note keeps\n * its full range. The returned record is not normalised; callers should\n * round-trip it (e.g. through `RichTextData`) before storing.\n *\n * @param {Object} record A rich-text record (`{ text, formats, … }`).\n * @param {Object} format The `core/note` format to add (`{ type, attributes }`).\n * @param {number} start  Range start (inclusive).\n * @param {number} end    Range end (exclusive).\n * @return {Object} A new record with the note applied.\n */\nexport function applyNoteFormat( record, format, start, end ) {\n\tconst formats = record.formats.slice();\n\tfor ( let i = start; i < end; i++ ) {\n\t\tconst stack = formats[ i ] ? formats[ i ].slice() : [];\n\t\tstack.push( format );\n\t\tformats[ i ] = stack;\n\t}\n\n\t// Measure each note's full span so containment can order the markers.\n\tconst spans = new Map();\n\tfor ( let i = 0; i < formats.length; i++ ) {\n\t\tconst stack = formats[ i ];\n\t\tif ( ! stack ) {\n\t\t\tcontinue;\n\t\t}\n\t\tfor ( const fmt of stack ) {\n\t\t\tif ( fmt.type !== NOTE_FORMAT_TYPE ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst id = fmt.attributes?.[ 'data-id' ];\n\t\t\tconst span = spans.get( id );\n\t\t\tif ( span ) {\n\t\t\t\tspan.end = i;\n\t\t\t} else {\n\t\t\t\tspans.set( id, { start: i, end: i } );\n\t\t\t}\n\t\t}\n\t}\n\tconst sizeOf = ( id ) => {\n\t\tconst span = spans.get( id );\n\t\treturn span ? span.end - span.start : 0;\n\t};\n\n\t// Order markers outermost-first (widest span) so `toTree` nests them rather\n\t// than splitting an outer note around an inner one. Notes sort ahead of\n\t// other formats so a note wraps the formatted text it spans.\n\tfor ( let i = 0; i < formats.length; i++ ) {\n\t\tconst stack = formats[ i ];\n\t\tif ( ! stack || stack.length < 2 ) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst notes = stack.filter( ( fmt ) => fmt.type === NOTE_FORMAT_TYPE );\n\t\tif ( notes.length === 0 ) {\n\t\t\tcontinue;\n\t\t}\n\t\tif ( notes.length > 1 ) {\n\t\t\tnotes.sort(\n\t\t\t\t( a, b ) =>\n\t\t\t\t\tsizeOf( b.attributes?.[ 'data-id' ] ) -\n\t\t\t\t\tsizeOf( a.attributes?.[ 'data-id' ] )\n\t\t\t);\n\t\t}\n\t\tconst others = stack.filter( ( fmt ) => fmt.type !== NOTE_FORMAT_TYPE );\n\t\tformats[ i ] = [ ...notes, ...others ];\n\t}\n\n\treturn { ...record, formats };\n}\n\n/**\n * Remove a single note's `core/note` marker from a rich-text value, leaving any\n * other notes nested or overlapping with it intact. Used when a note is deleted\n * or resolved so its highlight does not linger in the content.\n *\n * Rich-text's `removeFormat` strips every `core/note` marker in a range, so it\n * would wipe co-located notes; this filters by `data-id` to drop only the target\n * marker.\n *\n * @param {*}             value  Block attribute value (RichTextData or other).\n * @param {number|string} noteId Note id whose marker should be removed.\n * @return {?RichTextData} A new value with the marker removed, or null when the\n *                         attribute isn't rich text or carries no such marker.\n */\nexport function removeNoteFormat( value, noteId ) {\n\tif ( ! ( value instanceof RichTextData ) ) {\n\t\treturn null;\n\t}\n\tconst target = String( noteId );\n\tconst record = create( { html: value.toHTMLString() } );\n\tlet changed = false;\n\tconst formats = record.formats.map( ( stack ) => {\n\t\tif ( ! stack ) {\n\t\t\treturn stack;\n\t\t}\n\t\tconst filtered = stack.filter(\n\t\t\t( format ) =>\n\t\t\t\t! (\n\t\t\t\t\tformat.type === NOTE_FORMAT_TYPE &&\n\t\t\t\t\tformat.attributes?.[ 'data-id' ] === target\n\t\t\t\t)\n\t\t);\n\t\tif ( filtered.length === stack.length ) {\n\t\t\treturn stack;\n\t\t}\n\t\tchanged = true;\n\t\treturn filtered.length ? filtered : undefined;\n\t} );\n\t// Round-trip through HTML so the stored value matches a fresh reload.\n\treturn changed\n\t\t? RichTextData.fromHTMLString(\n\t\t\t\tnew RichTextData( { ...record, formats } ).toHTMLString()\n\t\t  )\n\t\t: null;\n}\n\n/**\n * Picks the most relevant thread from a list: first unresolved, else first.\n *\n * @param {Array} threads Ordered list of thread objects.\n * @return {Object|null} Selected thread or null when the list is empty.\n */\nexport function pickPrimaryNote( threads ) {\n\treturn (\n\t\tthreads.find( ( thread ) => thread.status === 'hold' ) ??\n\t\tthreads[ 0 ] ??\n\t\tnull\n\t);\n}\n\n/**\n * Removes a note ID from the metadata.\n *\n * @param {Object} metadata Existing block metadata\n * @param {number} noteId   Note ID to remove\n * @return {Object} Updated metadata object\n */\nexport function removeNoteIdFromMetadata( metadata, noteId ) {\n\tconst ids = new Set( getNoteIdsFromMetadata( metadata ) );\n\tids.delete( Number( noteId ) );\n\treturn {\n\t\t...metadata,\n\t\tnoteId: ids.size > 0 ? [ ...ids ] : undefined,\n\t};\n}\n\n/**\n * Calculate final top positions for all floating note threads in the\n * editor's content coordinate space. Adjusts positions to prevent overlapping\n * by pushing threads above the selected one upward and threads below it downward.\n *\n * @param {Object}                  params\n * @param {Array}                   params.threads        Ordered list of thread objects.\n * @param {string|number|undefined} params.selectedNoteId ID of the currently selected thread.\n * @param {Object<string,DOMRect>}  params.blockRects     Pre-read anchor rects keyed by thread ID.\n * @param {Object<string,number>}   params.heights        Rendered heights keyed by thread ID.\n * @param {number}                  params.scrollTop      Current scroll offset of the editor content.\n * @return {{ positions: Object<string,number> }} Computed top positions.\n */\nexport function calculateNotePositions( {\n\tthreads,\n\tselectedNoteId,\n\tblockRects,\n\theights,\n\tscrollTop = 0,\n} ) {\n\tconst offsets = {};\n\n\t// The overlap sweep walks outward from the anchor assuming each thread's\n\t// top is greater than the previous one's. Thread order is document order,\n\t// which tracks visual order for notes anchored to their markers, but a\n\t// pending \"new\" note anchors to the live selection and can therefore sit\n\t// above notes that precede it in the list. Sort by measured top so the\n\t// sweep's assumption holds and cards never displace past their markers.\n\t// Threads without a rect keep their relative order; they are skipped\n\t// below and never receive a position.\n\tconst orderedThreads = [ ...threads ].sort(\n\t\t( a, b ) =>\n\t\t\t( blockRects[ a.id ]?.top ?? Number.MAX_VALUE ) -\n\t\t\t( blockRects[ b.id ]?.top ?? Number.MAX_VALUE )\n\t);\n\n\tconst anchorIndex = Math.max(\n\t\t0,\n\t\torderedThreads.findIndex( ( thread ) => thread.id === selectedNoteId )\n\t);\n\n\tconst anchorThread = orderedThreads[ anchorIndex ];\n\n\tif ( ! anchorThread || ! blockRects[ anchorThread.id ] ) {\n\t\treturn { positions: {} };\n\t}\n\n\tconst anchorRect = blockRects[ anchorThread.id ];\n\tconst anchorTop = anchorRect.top || 0;\n\tconst anchorHeight = heights[ anchorThread.id ] || 0;\n\n\toffsets[ anchorThread.id ] = THREAD_ALIGN_OFFSET;\n\n\t// Process threads after the anchor, offsetting overlapping threads downward.\n\tlet prevAdjustedTop = anchorTop + THREAD_ALIGN_OFFSET;\n\tlet prevHeight = anchorHeight;\n\n\tfor ( let i = anchorIndex + 1; i < orderedThreads.length; i++ ) {\n\t\tconst thread = orderedThreads[ i ];\n\t\tconst threadRect = blockRects[ thread.id ];\n\t\tif ( ! threadRect ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst threadTop = threadRect.top || 0;\n\t\tconst threadHeight = heights[ thread.id ] || 0;\n\n\t\tlet offset = THREAD_ALIGN_OFFSET;\n\n\t\tconst prevBottom = prevAdjustedTop + prevHeight;\n\t\tif ( threadTop < prevBottom + THREAD_GAP ) {\n\t\t\toffset = prevBottom - threadTop + OVERLAP_MARGIN;\n\t\t}\n\n\t\toffsets[ thread.id ] = offset;\n\n\t\tprevAdjustedTop = threadTop + offset;\n\t\tprevHeight = threadHeight;\n\t}\n\n\t// Process threads before the anchor, offsetting overlapping threads upward.\n\tlet belowAdjustedTop = anchorTop + THREAD_ALIGN_OFFSET;\n\n\tfor ( let i = anchorIndex - 1; i >= 0; i-- ) {\n\t\tconst thread = orderedThreads[ i ];\n\t\tconst threadRect = blockRects[ thread.id ];\n\t\tif ( ! threadRect ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst threadTop = threadRect.top || 0;\n\t\tconst threadHeight = heights[ thread.id ] || 0;\n\n\t\tlet offset = THREAD_ALIGN_OFFSET;\n\n\t\tconst threadBottom = threadTop + threadHeight;\n\n\t\tif ( threadBottom > belowAdjustedTop ) {\n\t\t\toffset =\n\t\t\t\tbelowAdjustedTop - threadTop - threadHeight - OVERLAP_MARGIN;\n\t\t}\n\n\t\toffsets[ thread.id ] = offset;\n\n\t\tbelowAdjustedTop = threadTop + offset;\n\t}\n\n\t// blockRect.top + scrollTop is the block's absolute y within the editor's\n\t// scroll content; CSS translates each thread by -scrollTop at render time.\n\tconst positions = {};\n\tfor ( const thread of orderedThreads ) {\n\t\tconst blockRect = blockRects[ thread.id ];\n\t\tif ( blockRect && offsets[ thread.id ] !== undefined ) {\n\t\t\tpositions[ thread.id ] =\n\t\t\t\tblockRect.top + scrollTop + offsets[ thread.id ];\n\t\t}\n\t}\n\n\treturn { positions };\n}\n\n/**\n * Resolve the DOM element for a note thread once it's mounted,\n * or `null` if not found within 3 seconds.\n *\n * @param {string}       noteId             Note thread ID.\n * @param {?HTMLElement} container          Container to search within.\n * @param {string}       additionalSelector Optional descendant selector.\n * @return {Promise<HTMLElement|null>} Resolved element, or `null` on timeout.\n */\nfunction findNoteThread( noteId, container, additionalSelector ) {\n\tif ( ! container ) {\n\t\treturn Promise.resolve( null );\n\t}\n\n\t// A thread without a noteId is a new note thread.\n\tconst threadSelector =\n\t\tnoteId && noteId !== 'new'\n\t\t\t? `[role=treeitem][id=\"note-thread-${ noteId }\"]`\n\t\t\t: '[role=treeitem]:not([id])';\n\tconst selector = additionalSelector\n\t\t? `${ threadSelector } ${ additionalSelector }`\n\t\t: threadSelector;\n\n\treturn new Promise( ( resolve ) => {\n\t\tif ( container.querySelector( selector ) ) {\n\t\t\treturn resolve( container.querySelector( selector ) );\n\t\t}\n\n\t\tlet timer = null;\n\t\t// Wait for the element to be added to the DOM.\n\t\tconst observer = new window.MutationObserver( () => {\n\t\t\tif ( container.querySelector( selector ) ) {\n\t\t\t\tclearTimeout( timer );\n\t\t\t\tobserver.disconnect();\n\t\t\t\tresolve( container.querySelector( selector ) );\n\t\t\t}\n\t\t} );\n\n\t\tobserver.observe( container, { childList: true, subtree: true } );\n\n\t\t// Stop trying after 3 seconds.\n\t\ttimer = setTimeout( () => {\n\t\t\tobserver.disconnect();\n\t\t\tresolve( null );\n\t\t}, 3000 );\n\t} );\n}\n\n/**\n * Focus a note thread (or a descendant) and scroll it into view.\n *\n * @param {string}       noteId             Note thread ID.\n * @param {?HTMLElement} container          Container to search within.\n * @param {string}       additionalSelector Optional descendant selector.\n */\nexport function focusNoteThread( noteId, container, additionalSelector ) {\n\treturn findNoteThread( noteId, container, additionalSelector ).then(\n\t\t( element ) => {\n\t\t\tif ( ! element ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telement.focus();\n\t\t\telement.scrollIntoView( { block: 'nearest' } );\n\t\t}\n\t);\n}\n\n/**\n * Scroll a note thread into view without changing focus.\n *\n * @param {string}       noteId    Note thread ID.\n * @param {?HTMLElement} container Container to search within.\n */\nexport function scrollNoteThreadIntoView( noteId, container ) {\n\treturn findNoteThread( noteId, container ).then( ( element ) => {\n\t\telement?.scrollIntoView( { block: 'nearest' } );\n\t} );\n}\n"],
  "mappings": "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",
  "names": []
}
