@@ -1692,17 +1692,12 @@ export class PresentationEditor extends EventEmitter {
16921692 return this . getRangeRects ( selection . from , selection . to , relativeTo ) ;
16931693 }
16941694
1695- /**
1696- * Convert an arbitrary document range into layout-based bounding rects.
1697- *
1698- * @param from - Start position in the ProseMirror document
1699- * @param to - End position in the ProseMirror document
1700- * @param relativeTo - Optional HTMLElement for coordinate reference. If provided, returns coordinates
1701- * relative to this element's bounding rect. If omitted, returns absolute viewport
1702- * coordinates relative to the selection overlay.
1703- * @returns Array of rects, each containing pageIndex and position data (left, top, right, bottom, width, height)
1704- */
1705- getRangeRects ( from : number , to : number , relativeTo ?: HTMLElement ) : RangeRect [ ] {
1695+ #computeRangeRects(
1696+ from : number ,
1697+ to : number ,
1698+ relativeTo ?: HTMLElement ,
1699+ options : { forceBodySurface ?: boolean } = { } ,
1700+ ) : RangeRect [ ] {
17061701 if ( ! this . #selectionOverlay) return [ ] ;
17071702 if ( ! Number . isFinite ( from ) || ! Number . isFinite ( to ) ) return [ ] ;
17081703
@@ -1720,11 +1715,13 @@ export class PresentationEditor extends EventEmitter {
17201715 let usedDomRects = false ;
17211716 const sessionMode = this . #headerFooterSession?. session ?. mode ?? 'body' ;
17221717 const activeNoteSession = this . #getActiveNoteStorySession( ) ;
1718+ const useHeaderFooterSurface = ! options . forceBodySurface && sessionMode !== 'body' ;
1719+ const useNoteSurface = ! options . forceBodySurface && activeNoteSession != null ;
17231720 const layoutRectSource = ( ) => {
1724- if ( sessionMode !== 'body' ) {
1721+ if ( useHeaderFooterSurface ) {
17251722 return this . #computeHeaderFooterSelectionRects( start , end ) ;
17261723 }
1727- if ( activeNoteSession ) {
1724+ if ( useNoteSurface ) {
17281725 return this . #computeNoteSelectionRects( start , end ) ;
17291726 }
17301727 const domRects = this . #computeSelectionRectsFromDom( start , end ) ;
@@ -1751,7 +1748,7 @@ export class PresentationEditor extends EventEmitter {
17511748 let domCaretStart : { pageIndex : number ; x : number ; y : number } | null = null ;
17521749 let domCaretEnd : { pageIndex : number ; x : number ; y : number } | null = null ;
17531750 const pageDelta : Record < number , { dx : number ; dy : number } > = { } ;
1754- if ( ! usedDomRects && ! activeNoteSession ) {
1751+ if ( ! usedDomRects && ! useNoteSurface ) {
17551752 // Geometry fallback path: apply a small DOM-based delta to reduce drift.
17561753 try {
17571754 domCaretStart = this . #computeDomCaretPageLocal( start ) ;
@@ -1772,8 +1769,8 @@ export class PresentationEditor extends EventEmitter {
17721769 }
17731770
17741771 const pageHeight = this . #getBodyPageHeight( ) ;
1775- const pageGap = sessionMode === 'body' ? ( this . #layoutState. layout ? .pageGap ?? 0 ) : 0 ;
1776- const finalRects = rawRects
1772+ const pageGap = useHeaderFooterSurface || ! this . #layoutState . layout ? 0 : ( this . #layoutState. layout . pageGap ?? 0 ) ;
1773+ return rawRects
17771774 . map ( ( rect : LayoutRect , idx : number , allRects : LayoutRect [ ] ) => {
17781775 let adjustedX = rect . x ;
17791776 let adjustedY = rect . y ;
@@ -1817,8 +1814,20 @@ export class PresentationEditor extends EventEmitter {
18171814 } ;
18181815 } )
18191816 . filter ( ( rect : RangeRect | null ) : rect is RangeRect => Boolean ( rect ) ) ;
1817+ }
18201818
1821- return finalRects ;
1819+ /**
1820+ * Convert an arbitrary document range into layout-based bounding rects.
1821+ *
1822+ * @param from - Start position in the ProseMirror document
1823+ * @param to - End position in the ProseMirror document
1824+ * @param relativeTo - Optional HTMLElement for coordinate reference. If provided, returns coordinates
1825+ * relative to this element's bounding rect. If omitted, returns absolute viewport
1826+ * coordinates relative to the selection overlay.
1827+ * @returns Array of rects, each containing pageIndex and position data (left, top, right, bottom, width, height)
1828+ */
1829+ getRangeRects ( from : number , to : number , relativeTo ?: HTMLElement ) : RangeRect [ ] {
1830+ return this . #computeRangeRects( from , to , relativeTo ) ;
18221831 }
18231832
18241833 /**
@@ -1853,6 +1862,42 @@ export class PresentationEditor extends EventEmitter {
18531862 } ;
18541863 }
18551864
1865+ #getThreadSelectionBounds(
1866+ data : { storyKey ?: unknown ; start ?: unknown ; end ?: unknown ; pos ?: unknown } ,
1867+ relativeTo : HTMLElement | undefined ,
1868+ ) : {
1869+ bounds : { top : number ; left : number ; bottom : number ; right : number ; width : number ; height : number } ;
1870+ rects : RangeRect [ ] ;
1871+ pageIndex : number ;
1872+ } | null {
1873+ const start = Number . isFinite ( data . start ?? data . pos ) ? Number ( data . start ?? data . pos ) : undefined ;
1874+ const end = Number . isFinite ( data . end ) ? Number ( data . end ) : start ;
1875+ if ( ! Number . isFinite ( start ) || ! Number . isFinite ( end ) ) {
1876+ return null ;
1877+ }
1878+
1879+ const storyKey = typeof data . storyKey === 'string' ? data . storyKey : null ;
1880+ const rects =
1881+ storyKey === BODY_STORY_KEY
1882+ ? this . #computeRangeRects( start ! , end ! , relativeTo , { forceBodySurface : true } )
1883+ : this . getRangeRects ( start ! , end ! , relativeTo ) ;
1884+
1885+ if ( ! rects . length ) {
1886+ return null ;
1887+ }
1888+
1889+ const bounds = this . #aggregateLayoutBounds( rects ) ;
1890+ if ( ! bounds ) {
1891+ return null ;
1892+ }
1893+
1894+ return {
1895+ rects,
1896+ bounds,
1897+ pageIndex : rects [ 0 ] ?. pageIndex ?? 0 ,
1898+ } ;
1899+ }
1900+
18561901 /**
18571902 * Remap comment positions to layout coordinates with bounds and rects.
18581903 * Takes a positions object with threadIds as keys and position data as values.
@@ -1925,7 +1970,7 @@ export class PresentationEditor extends EventEmitter {
19251970 return ;
19261971 }
19271972
1928- const layoutRange = this . getSelectionBounds ( start ! , end ! , relativeTo ) ;
1973+ const layoutRange = this . #getThreadSelectionBounds ( data , relativeTo ) ;
19291974 if ( ! layoutRange ) {
19301975 remapped [ threadId ] = data ;
19311976 return ;
0 commit comments