@@ -379,11 +379,75 @@ export class TagNodeHandler {
379379 }
380380
381381 let hoverTimeout : number | null = null ;
382+ let currentMouseY = 0 ;
383+ let currentMouseX = 0 ;
384+
385+ // Track mouse position to determine which part of multi-line tag is hovered
386+ const handleMouseMove = ( e : MouseEvent ) => {
387+ currentMouseY = e . clientY ;
388+ currentMouseX = e . clientX ;
389+
390+ // Update tooltip position if it's already visible
391+ if ( this . currentTooltip ) {
392+ updateTooltipPosition ( ) ;
393+ }
394+ } ;
395+
396+ const getClosestRect = ( ) : DOMRect => {
397+ const range = document . createRange ( ) ;
398+ range . selectNodeContents ( tagElement ) ;
399+ const clientRects = range . getClientRects ( ) ;
400+
401+ if ( clientRects . length > 0 ) {
402+ // If tag spans multiple lines, find the rect closest to mouse position
403+ if ( clientRects . length > 1 ) {
404+ let closestRect : DOMRect | null = null ;
405+ let minDistance = Infinity ;
406+
407+ for ( let i = 0 ; i < clientRects . length ; i ++ ) {
408+ const r = clientRects . item ( i ) ;
409+ if ( ! r ) continue ;
410+
411+ // Calculate distance from mouse position to center of this rect
412+ const rectCenterY = r . top + r . height / 2 ;
413+ const rectCenterX = r . left + r . width / 2 ;
414+ const distanceY = Math . abs ( currentMouseY - rectCenterY ) ;
415+ const distanceX = Math . abs ( currentMouseX - rectCenterX ) ;
416+ // Weight Y distance more heavily since we care more about vertical proximity
417+ const distance = distanceY * 2 + distanceX ;
418+
419+ if ( distance < minDistance ) {
420+ minDistance = distance ;
421+ closestRect = r ;
422+ }
423+ }
424+
425+ return (
426+ closestRect ||
427+ clientRects . item ( clientRects . length - 1 ) ||
428+ tagElement . getBoundingClientRect ( )
429+ ) ;
430+ } else {
431+ // Single line tag - use the only rect
432+ return clientRects . item ( 0 ) || tagElement . getBoundingClientRect ( ) ;
433+ }
434+ }
435+
436+ return tagElement . getBoundingClientRect ( ) ;
437+ } ;
438+
439+ const updateTooltipPosition = ( ) => {
440+ if ( ! this . currentTooltip ) return ;
441+
442+ const rect = getClosestRect ( ) ;
443+ this . currentTooltip . style . top = `${ rect . top - TOOLTIP_OFFSET } px` ;
444+ this . currentTooltip . style . left = `${ rect . left + rect . width / 2 } px` ;
445+ } ;
382446
383447 const showTooltip = ( ) => {
384448 if ( this . currentTooltip ) return ;
385449
386- const rect = tagElement . getBoundingClientRect ( ) ;
450+ const rect = getClosestRect ( ) ;
387451
388452 this . currentTooltip = document . createElement ( "div" ) ;
389453 this . currentTooltip . className = "discourse-tag-popover" ;
@@ -443,6 +507,8 @@ export class TagNodeHandler {
443507 hoverTimeout = window . setTimeout ( showTooltip , HOVER_DELAY ) ;
444508 } ) ;
445509
510+ tagElement . addEventListener ( "mousemove" , handleMouseMove ) ;
511+
446512 tagElement . addEventListener ( "mouseleave" , ( e ) => {
447513 if ( hoverTimeout ) {
448514 clearTimeout ( hoverTimeout ) ;
@@ -456,6 +522,7 @@ export class TagNodeHandler {
456522 } ) ;
457523
458524 const cleanup = ( ) => {
525+ tagElement . removeEventListener ( "mousemove" , handleMouseMove ) ;
459526 if ( hoverTimeout ) {
460527 clearTimeout ( hoverTimeout ) ;
461528 }
0 commit comments