@@ -20,21 +20,51 @@ export class DocumentNavigator {
2020 const validator : NodeProcessor = {
2121 acceptNode ( node : Node ) : number {
2222 if ( node . nodeType !== Node . TEXT_NODE ) return NodeFilter . FILTER_REJECT ;
23-
23+
2424 const container = ( node as Text ) . parentElement ;
2525 if ( ! container ) return NodeFilter . FILTER_REJECT ;
26-
26+
2727 if ( container . closest ( '[aria-hidden="true"]' ) ) return NodeFilter . FILTER_REJECT ;
2828 if ( container . classList . contains ( "sr-only" ) ) return NodeFilter . FILTER_REJECT ;
29-
30- const shouldSkip =
31- container . closest (
32- "script, style, code, noscript, next-route-announcer, .jigts-translation-widget, .jigts-widget-trigger, .jigts-widget-dropdown, .notranslate"
33- ) !== null || ! node . textContent ?. trim ( ) ;
34-
35- return shouldSkip ? NodeFilter . FILTER_REJECT : NodeFilter . FILTER_ACCEPT ;
29+
30+ const skipBySelector = container . closest (
31+ "script, style, code, noscript, next-route-announcer, \
32+ .jigts-translation-widget, .jigts-widget-trigger, \
33+ .jigts-widget-dropdown, .notranslate"
34+ ) ;
35+ if ( skipBySelector ) return NodeFilter . FILTER_REJECT ;
36+
37+ // ✅ If the text is inside a clean wrapper like <span> → allow
38+ const isInSpanWrapper =
39+ container . tagName === "SPAN" &&
40+ Array . from ( container . childNodes ) . every ( ( n ) => n . nodeType === Node . TEXT_NODE ) ;
41+
42+ const interactiveAncestor = container . closest (
43+ "button, input, select, textarea, [role='button'], [role='link']"
44+ ) as HTMLElement | null ;
45+
46+ if ( interactiveAncestor && ! isInSpanWrapper ) {
47+ const isTextSiblingToElement = Array . from ( container . childNodes ) . some (
48+ ( n ) =>
49+ n . nodeType === Node . ELEMENT_NODE &&
50+ node . parentNode === container // sibling to another element in same container
51+ ) ;
52+
53+ const isDirectChildOfInteractive =
54+ interactiveAncestor === container ;
55+
56+ if ( isTextSiblingToElement && isDirectChildOfInteractive ) {
57+ // ⚠️ Text node is a sibling to other element nodes inside the button — unsafe
58+ return NodeFilter . FILTER_REJECT ;
59+ }
60+ }
61+
62+ if ( ! node . textContent ?. trim ( ) ) return NodeFilter . FILTER_REJECT ;
63+
64+ return NodeFilter . FILTER_ACCEPT ;
3665 } ,
3766 } ;
67+
3868
3969 const navigator = document . createTreeWalker ( document . body , NodeFilter . SHOW_TEXT , validator ) ;
4070 const groupedText = new Map < HTMLElement , Text [ ] > ( ) ;
@@ -71,6 +101,25 @@ export class DocumentNavigator {
71101
72102 if ( isDescendantOfNested ) continue ;
73103
104+ const childNodes = Array . from ( element . childNodes ) ;
105+ const hasText = childNodes . some (
106+ ( n ) => n . nodeType === Node . TEXT_NODE && n . textContent ?. trim ( )
107+ ) ;
108+ const hasInteractiveElements = childNodes . some (
109+ ( n ) =>
110+ n . nodeType === Node . ELEMENT_NODE &&
111+ [ "BUTTON" , "INPUT" , "TEXTAREA" , "SELECT" ] . includes (
112+ ( n as HTMLElement ) . tagName
113+ )
114+ ) ;
115+
116+ const isTextMixedWithInteractivity = hasText && hasInteractiveElements ;
117+
118+
119+ if ( isTextMixedWithInteractivity ) {
120+ continue ;
121+ }
122+
74123 for ( const node of textNodes ) {
75124 let text = node . textContent ?. trim ( ) || "" ;
76125 const originalText = element . getAttribute ( "data-original-text" ) ;
@@ -81,9 +130,20 @@ export class DocumentNavigator {
81130
82131 combinedText += ( combinedText ? " " : "" ) + text ;
83132
84- if ( element . children . length > 0 ) {
133+ // if(isTextMixedWithInteractivity) {
134+ // continue;
135+ // }
136+ const hasMixedContent = Array . from ( element . childNodes ) . some (
137+ ( child ) => child . nodeType !== Node . TEXT_NODE
138+ ) ;
139+
140+
141+ if ( hasMixedContent ) {
85142 isNested = true ;
86143 }
144+ // if (element.children.length > 0) {
145+ // isNested = true;
146+ // }
87147 }
88148
89149 if ( combinedText . length > 0 ) {
0 commit comments