@@ -105,28 +105,18 @@ export function setupInput() {
105105 elements . push ( target ) ;
106106 }
107107
108- // 6) Blockly MAIN WORKSPACE (one level above blocks)
108+ // 6) Blockly MAIN WORKSPACE
109109 document
110110 . querySelectorAll ( ".blockly-ws-search input, .blockly-ws-search button" )
111111 . forEach ( pushUnique ) ;
112- const workspaceGroup = Array . from (
113- document . querySelectorAll ( "svg.blocklySvg g.blocklyWorkspace" ) ,
114- )
115- . filter ( ( ws ) => ! ws . closest ( "svg.blocklyFlyout" ) ) // exclude flyout workspaces
116- // If there are multiple, prefer the one that actually contains the block canvas
117- . sort ( ( a , b ) => {
118- const aHasCanvas = ! ! a . querySelector ( "g.blocklyBlockCanvas" ) ;
119- const bHasCanvas = ! ! b . querySelector ( "g.blocklyBlockCanvas" ) ;
120- return Number ( bHasCanvas ) - Number ( aHasCanvas ) ;
121- } ) [ 0 ] ;
122-
123- if ( workspaceGroup && isElementVisible ( workspaceGroup ) ) {
124- if ( workspaceGroup . getAttribute ( "tabindex" ) !== "0" ) {
125- workspaceGroup . setAttribute ( "tabindex" , "0" ) ;
126- }
127- workspaceGroup . setAttribute ( "role" , "group" ) ; // lets AT know it's an interactive region
112+ const blocklySvg = document . querySelector ( "svg.blocklySvg" ) ;
113+ const workspaceGroup = blocklySvg ?. querySelector ( "g.blocklyWorkspace" ) ;
114+
115+ if ( workspaceGroup && isElementVisible ( blocklySvg ) ) {
116+ // Force tabIndex=0 — Blockly's focus manager may have set it to -1
117+ workspaceGroup . setAttribute ( "tabindex" , "0" ) ;
118+ workspaceGroup . setAttribute ( "role" , "group" ) ;
128119 workspaceGroup . setAttribute ( "aria-label" , "Blocks workspace" ) ;
129- workspaceGroup . setAttribute ( "focusable" , "true" ) ; // helpful for SVG focus on some browsers
130120 pushUnique ( workspaceGroup ) ;
131121 }
132122
@@ -166,8 +156,10 @@ export function setupInput() {
166156 currentElement = currentElement . parentElement ;
167157 }
168158
169- // Check if element has actual dimensions
170- const rect = element . getBoundingClientRect ( ) ;
159+ // For SVG child elements, use the owning SVG's bounding rect
160+ // since <g> elements may return zero dimensions even when visible
161+ const rectSource = element . ownerSVGElement ?? element ;
162+ const rect = rectSource . getBoundingClientRect ( ) ;
171163 return rect . width > 0 && rect . height > 0 ;
172164 }
173165
@@ -206,7 +198,15 @@ export function setupInput() {
206198 if ( focusableElements . length === 0 ) return ;
207199
208200 const currentElement = document . activeElement ;
209- const currentIndex = focusableElements . indexOf ( currentElement ) ;
201+ let currentIndex = focusableElements . indexOf ( currentElement ) ;
202+
203+ // If not directly in the list, check if inside a tracked container
204+ // (e.g. Blockly moved focus internally to a block within the flyout/workspace)
205+ if ( currentIndex === - 1 ) {
206+ currentIndex = focusableElements . findIndex (
207+ ( el ) => el !== currentElement && el . contains ?. ( currentElement ) ,
208+ ) ;
209+ }
210210
211211 // Only manage tab navigation for our tracked elements
212212 if ( currentIndex === - 1 || currentElement . closest ( "details[open]" ) )
0 commit comments