{
  "version": 3,
  "sources": ["../../../src/components/collaborators-overlay/compute-selection.ts"],
  "sourcesContent": ["import { privateApis as coreDataPrivateApis } from '@wordpress/core-data';\nimport type {\n\tCoreDataPrivateApis,\n\tResolvedSelection,\n} from '@wordpress/core-data';\nimport { unlock } from '../../lock-unlock';\nimport {\n\tgetCursorPosition,\n\tgetOrderedBlockRange,\n\tgetSelectionRects,\n} from './cursor-dom-utils';\nimport type { CursorCoords, SelectionRect } from './cursor-dom-utils';\n\nconst { SelectionDirection, SelectionType } = unlock(\n\tcoreDataPrivateApis\n) as Pick< CoreDataPrivateApis, 'SelectionDirection' | 'SelectionType' >;\n\n/** Common parameters passed to cursor/selection computation helpers. */\ninterface OverlayContext {\n\teditorDocument: Document;\n\toverlayRect: DOMRect;\n}\n\n/** Selection rects and the resolved block element for a single-block selection. */\ninterface SingleBlockResult {\n\trects: SelectionRect[];\n\tblockElement: HTMLElement | null;\n}\n\n/** Result of computing visual cursor/selection state for a single user. */\nexport interface SelectionVisual {\n\tcoords?: CursorCoords | null;\n\tselectionRects?: SelectionRect[];\n}\n\n/**\n * Resolve the most specific editor element the selection refers to.\n *\n * When the sender carries an `attributeKey`, narrow to the RichText element\n * matching `data-wp-block-attribute-key` inside the block. This is what makes\n * cursor placement work for blocks with multiple RichText fields (e.g.\n * `core/table` cells: `body.0.cells.0.content`, etc.). Falls back to the\n * block element when `attributeKey` is missing (WholeBlock selections,\n * older senders, or DOM lookup miss).\n *\n * @param editorDocument    - The editor document.\n * @param resolvedSelection - The resolved selection.\n * @return The target element (RichText editable or block), or null.\n */\nexport function resolveTargetElement(\n\teditorDocument: Document,\n\tresolvedSelection: ResolvedSelection\n): HTMLElement | null {\n\tif ( ! resolvedSelection.localClientId ) {\n\t\treturn null;\n\t}\n\n\tconst blockElement = editorDocument.querySelector< HTMLElement >(\n\t\t`[data-block=\"${ resolvedSelection.localClientId }\"]`\n\t);\n\n\tif ( ! blockElement || ! resolvedSelection.attributeKey ) {\n\t\treturn blockElement;\n\t}\n\n\tconst attrKey = CSS.escape( resolvedSelection.attributeKey );\n\tconst byKey = blockElement.querySelector< HTMLElement >(\n\t\t`[data-wp-block-attribute-key=\"${ attrKey }\"]`\n\t);\n\tif ( byKey ) {\n\t\treturn byKey;\n\t}\n\n\t// Fallback chain — each is safer than the raw [data-block] wrapper because\n\t// [data-block] includes toolbar text nodes that corrupt the TreeWalker\n\t// offset walk inside findInnerBlockOffset.\n\t//\n\t// 1. contenteditable — present when the block is in edit mode.\n\tconst editable = blockElement.querySelector< HTMLElement >(\n\t\t'[contenteditable=\"true\"]'\n\t);\n\tif ( editable ) {\n\t\treturn editable;\n\t}\n\n\t// 2. Common block-level text elements — always present in the DOM regardless\n\t//    of whether the block is currently focused by this user.\n\tconst textEl = blockElement.querySelector< HTMLElement >(\n\t\t'p, h1, h2, h3, h4, h5, h6, pre, blockquote, td, th, li, figcaption'\n\t);\n\treturn textEl ?? blockElement;\n}\n\n/**\n * Compute cursor coords and optional selection rects for a single user's selection.\n *\n * @param selection      - The selection state from the awareness layer.\n * @param start          - Start position (block clientId + text index).\n * @param end            - End position (only for range selections).\n * @param overlayContext - Shared editor document / overlay references.\n * @return Cursor coordinates and optional selection rectangles.\n */\nexport function computeSelectionVisual(\n\tselection: any,\n\tstart: ResolvedSelection,\n\tend: ResolvedSelection | undefined,\n\toverlayContext: OverlayContext\n): SelectionVisual {\n\tif (\n\t\tselection.type === SelectionType.None ||\n\t\tselection.type === SelectionType.WholeBlock\n\t) {\n\t\treturn {};\n\t}\n\n\tif ( selection.type === SelectionType.Cursor ) {\n\t\treturn computeCursorOnly( start, overlayContext );\n\t}\n\n\t// SelectionInOneBlock or SelectionInMultipleBlocks.\n\tif ( ! end ) {\n\t\treturn {};\n\t}\n\treturn computeTextSelection( selection, start, end, overlayContext );\n}\n\n/**\n * Compute cursor coordinates for a simple cursor (no highlighted text).\n *\n * @param start          - Cursor position (block clientId + text index).\n * @param overlayContext - Shared editor document / overlay references.\n * @return Cursor coordinates.\n */\nfunction computeCursorOnly(\n\tstart: ResolvedSelection,\n\toverlayContext: OverlayContext\n): SelectionVisual {\n\tif ( ! start.localClientId ) {\n\t\treturn {};\n\t}\n\tconst targetElement = resolveTargetElement(\n\t\toverlayContext.editorDocument,\n\t\tstart\n\t);\n\treturn {\n\t\tcoords: getCursorPosition(\n\t\t\tstart.richTextOffset,\n\t\t\ttargetElement,\n\t\t\toverlayContext.editorDocument,\n\t\t\toverlayContext.overlayRect\n\t\t),\n\t};\n}\n\n/**\n * Compute cursor coordinates and selection highlight rects for a text selection\n * (single-block or multi-block).\n *\n * @param selection      - The selection state.\n * @param start          - Start position (block clientId + text index).\n * @param end            - End position (block clientId + text index).\n * @param overlayContext - Shared editor document / overlay references.\n * @return Cursor coordinates and optional selection rectangles.\n */\nfunction computeTextSelection(\n\tselection: any,\n\tstart: ResolvedSelection,\n\tend: ResolvedSelection,\n\toverlayContext: OverlayContext\n): SelectionVisual {\n\tif ( ! start.localClientId || ! end.localClientId ) {\n\t\treturn {};\n\t}\n\n\tconst isReverse =\n\t\tselection.selectionDirection === SelectionDirection.Backward;\n\tconst activeEnd = isReverse ? start : end;\n\n\t// Single-block: both endpoints must have a text offset.\n\tif ( selection.type === SelectionType.SelectionInOneBlock ) {\n\t\tif ( start.richTextOffset === null || end.richTextOffset === null ) {\n\t\t\treturn {};\n\t\t}\n\t\tconst result = computeSingleBlockRects( start, end, overlayContext );\n\t\tif ( result.rects.length > 0 ) {\n\t\t\treturn {\n\t\t\t\tcoords: getCursorPosition(\n\t\t\t\t\tactiveEnd.richTextOffset,\n\t\t\t\t\tresult.blockElement,\n\t\t\t\t\toverlayContext.editorDocument,\n\t\t\t\t\toverlayContext.overlayRect\n\t\t\t\t),\n\t\t\t\tselectionRects: result.rects,\n\t\t\t};\n\t\t}\n\t\t// Fallback: cursor only, no selection rects.\n\t\treturn {\n\t\t\tcoords: getCursorPosition(\n\t\t\t\tstart.richTextOffset,\n\t\t\t\tresolveTargetElement( overlayContext.editorDocument, start ),\n\t\t\t\toverlayContext.editorDocument,\n\t\t\t\toverlayContext.overlayRect\n\t\t\t),\n\t\t};\n\t}\n\n\t// Multi-block: full bounding-box overlay for text blocks, CSS outline for\n\t// non-text blocks (image, spacer, etc.) via use-block-highlighting.\n\t// No cursor coords — the single avatar placed by use-block-highlighting on\n\t// the topmost block is the only indicator.\n\treturn computeMultiBlockOverlayRects( start, end, overlayContext );\n}\n\n/**\n * Return a full bounding-box SelectionRect for a block element.\n *\n * @param blockEl     - The block element.\n * @param overlayRect - Overlay bounding rect for coordinate transform.\n * @return A single SelectionRect covering the full block.\n */\nfunction blockBoundingRect(\n\tblockEl: HTMLElement,\n\toverlayRect: DOMRect\n): SelectionRect {\n\tconst r = blockEl.getBoundingClientRect();\n\treturn {\n\t\tx: r.left - overlayRect.left,\n\t\ty: r.top - overlayRect.top,\n\t\twidth: r.width,\n\t\theight: r.height,\n\t};\n}\n\n/**\n * Compute overlay rects for a multi-block selection.\n *\n * Expects start/end to already carry container-level clientIds (inner-block\n * promotion is performed by the caller, use-render-cursors, before this\n * function is invoked). richTextOffset is null on promoted endpoints, which\n * triggers the full bounding-box path for those blocks.\n *\n * - Middle blocks always receive a full bounding-box overlay.\n * - First block: full bounding-box when offset is null or 0; partial otherwise.\n * - Last block: partial rects from 0 to offset; full bounding-box when null.\n * - Non-text blocks (image, spacer — no visible innerText) produce no rects;\n *   CSS outline in use-block-highlighting handles those instead.\n *\n * @param start          - Start endpoint (container-level clientId + text offset).\n * @param end            - End endpoint (container-level clientId + text offset).\n * @param overlayContext - Shared editor document / overlay references.\n * @return selectionRects covering each text block in the selection, or {}.\n */\nfunction computeMultiBlockOverlayRects(\n\tstart: ResolvedSelection,\n\tend: ResolvedSelection,\n\toverlayContext: OverlayContext\n): SelectionVisual {\n\tconst { editorDocument, overlayRect } = overlayContext;\n\n\tconst range = getOrderedBlockRange(\n\t\tstart.localClientId!,\n\t\tend.localClientId!,\n\t\teditorDocument\n\t);\n\tif ( ! range ) {\n\t\treturn {};\n\t}\n\n\t// Align ResolvedSelection objects with the DOM-ordered elements returned by\n\t// the helper. start/end are in Yjs selection direction; firstId tells us\n\t// which input ended up first in the document.\n\tconst docFirst = range.firstId === start.localClientId ? start : end;\n\tconst docLast = range.firstId === start.localClientId ? end : start;\n\tconst {\n\t\tfirstEl: docFirstEl,\n\t\tlastEl: docLastEl,\n\t\tmiddleEls,\n\t\tsameContainer,\n\t} = range;\n\n\t// When both endpoints resolve to the same container after promotion\n\t// (e.g. two list-items in the same list), fall back to a full bounding-box.\n\tif ( sameContainer ) {\n\t\treturn docFirstEl.innerText?.trim()\n\t\t\t? {\n\t\t\t\t\tselectionRects: [\n\t\t\t\t\t\tblockBoundingRect( docFirstEl, overlayRect ),\n\t\t\t\t\t],\n\t\t\t  }\n\t\t\t: {};\n\t}\n\n\tconst MAX = Number.MAX_SAFE_INTEGER;\n\tconst rects: SelectionRect[] = [];\n\n\t// First block: full bounding-box when offset is null or 0 (includes\n\t// promoted endpoints whose offset was nulled in use-render-cursors).\n\tif ( docFirstEl.innerText?.trim() ) {\n\t\tconst firstOffset = docFirst.richTextOffset;\n\t\tif ( firstOffset === null || firstOffset === 0 ) {\n\t\t\trects.push( blockBoundingRect( docFirstEl, overlayRect ) );\n\t\t} else {\n\t\t\tconst el = resolveTargetElement( editorDocument, docFirst );\n\t\t\tconst textRects = el\n\t\t\t\t? getSelectionRects(\n\t\t\t\t\t\tel,\n\t\t\t\t\t\tfirstOffset,\n\t\t\t\t\t\tMAX,\n\t\t\t\t\t\teditorDocument,\n\t\t\t\t\t\toverlayRect\n\t\t\t\t  )\n\t\t\t\t: null;\n\t\t\trects.push(\n\t\t\t\t...( textRects ?? [\n\t\t\t\t\tblockBoundingRect( docFirstEl, overlayRect ),\n\t\t\t\t] )\n\t\t\t);\n\t\t}\n\t}\n\n\t// Middle blocks — always the full block width.\n\tfor ( const blockEl of middleEls ) {\n\t\tif ( blockEl.innerText?.trim() ) {\n\t\t\trects.push( blockBoundingRect( blockEl, overlayRect ) );\n\t\t}\n\t}\n\n\t// Last block: partial rects from 0 to offset; full when null.\n\tif ( docLastEl.innerText?.trim() ) {\n\t\tconst lastOffset = docLast.richTextOffset;\n\t\tif ( lastOffset === null ) {\n\t\t\trects.push( blockBoundingRect( docLastEl, overlayRect ) );\n\t\t} else if ( lastOffset > 0 ) {\n\t\t\tconst el = resolveTargetElement( editorDocument, docLast );\n\t\t\tconst textRects = el\n\t\t\t\t? getSelectionRects(\n\t\t\t\t\t\tel,\n\t\t\t\t\t\t0,\n\t\t\t\t\t\tlastOffset,\n\t\t\t\t\t\teditorDocument,\n\t\t\t\t\t\toverlayRect\n\t\t\t\t  )\n\t\t\t\t: null;\n\t\t\trects.push(\n\t\t\t\t...( textRects ?? [\n\t\t\t\t\tblockBoundingRect( docLastEl, overlayRect ),\n\t\t\t\t] )\n\t\t\t);\n\t\t}\n\t\t// lastOffset === 0: cursor at the very start — nothing selected here.\n\t}\n\n\treturn rects.length > 0 ? { selectionRects: rects } : {};\n}\n\n/**\n * Compute selection rects for a selection within a single block.\n *\n * @param start          - Start position (block clientId + text index).\n * @param end            - End position (block clientId + text index).\n * @param overlayContext - Shared editor document / overlay references.\n * @return Array of selection rectangles.\n */\nfunction computeSingleBlockRects(\n\tstart: ResolvedSelection,\n\tend: ResolvedSelection,\n\toverlayContext: OverlayContext\n): SingleBlockResult {\n\tconst blockElement = resolveTargetElement(\n\t\toverlayContext.editorDocument,\n\t\tstart\n\t);\n\tif (\n\t\t! blockElement ||\n\t\tstart.richTextOffset === null ||\n\t\tend.richTextOffset === null\n\t) {\n\t\treturn { rects: [], blockElement: null };\n\t}\n\treturn {\n\t\trects:\n\t\t\tgetSelectionRects(\n\t\t\t\tblockElement,\n\t\t\t\tstart.richTextOffset,\n\t\t\t\tend.richTextOffset,\n\t\t\t\toverlayContext.editorDocument,\n\t\t\t\toverlayContext.overlayRect\n\t\t\t) ?? [],\n\t\tblockElement,\n\t};\n}\n"],
  "mappings": "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",
  "names": ["coreDataPrivateApis"]
}
