@@ -48,29 +48,33 @@ Add selection-aware focus methods:
4848``` typescript
4949class FocusManager {
5050 // Existing
51- focusType: PrimitiveAtom <" node" | " waveai" >;
51+ focusType: PrimitiveAtom <" node" | " waveai" >; // Single source of truth
5252 blockFocusAtom: Atom <string | null >;
5353
5454 // NEW: Selection-aware focus checking
5555 waveAIFocusWithin(): boolean ;
5656 nodeFocusWithin(): boolean ;
5757
58- // NEW: Protected transitions (check selections first )
59- requestNodeFocus(): void ; // from Wave AI → node
58+ // NEW: Focus transitions (INTENTIONALLY not defensive )
59+ requestNodeFocus(): void ; // from Wave AI → node (BREAKS selections - that's the point!)
6060 requestWaveAIFocus(): void ; // from node → Wave AI
6161
6262 // NEW: Get current focus type
6363 getFocusType(): FocusStrType ;
6464
6565 // ENHANCED: Smart refocus based on focusType
6666 refocusNode(): void ; // already handles both types
67-
68- // NEW: Focus ring coordination
69- shouldShowWaveAIFocusRing(): boolean ;
70- shouldShowNodeFocusRing(blockId : string ): boolean ;
7167}
7268```
7369
70+ ** Critical Design Decision: ` requestNodeFocus() ` is NOT defensive**
71+
72+ When ` requestNodeFocus() ` is called (e.g., Cmd+n, explicit focus change), it MUST take focus even if there's a selection in Wave AI. This is intentional - the user explicitly requested a focus change. Losing the selection is the correct behavior.
73+
74+ ** Focus Manager as Source of Truth**
75+
76+ The ` focusType ` atom is the single source of truth. The old ` waveAIFocusedAtom ` will be kept in sync during migration but should eventually be removed. All components should read ` focusManager.focusType ` directly (via ` useAtomValue ` ) to determine focus ring state - this ensures synchronized, reactive focus ring updates.
77+
7478## Wave AI Focus Utilities
7579
7680** New File** : [ ` frontend/app/aipanel/waveai-focus-utils.ts ` ] ( frontend/app/aipanel/waveai-focus-utils.ts )
@@ -191,41 +195,33 @@ Smart blur handling:
191195``` typescript
192196// MODIFY: handleFocus - advisory only
193197const handleFocus = useCallback (() => {
194- globalStore .set (atoms .waveAIFocusedAtom , true );
195198 focusManager .requestWaveAIFocus ();
196199}, []);
197200
198- // MODIFY: handleBlur - smart about where focus is going
201+ // MODIFY: handleBlur - simplified with waveAIHasFocusWithin()
199202const handleBlur = useCallback ((e : React .FocusEvent ) => {
200- const relatedTarget = e .relatedTarget ;
201-
202- // Check if focus is moving to another element within Wave AI panel
203- if (relatedTarget instanceof HTMLElement ) {
204- const waveAIPanel = findWaveAIPanel (relatedTarget );
205- if (waveAIPanel ) {
206- // Focus staying within Wave AI, don't revert
207- return ;
208- }
209- }
210-
211- // Check if there's a selection in Wave AI
212- if (waveAIHasSelection ()) {
213- // Selection exists, don't revert focus
203+ // Window blur - preserve state
204+ if (e .relatedTarget === null ) {
214205 return ;
215206 }
216207
217- // Check if this is a window blur (relatedTarget is null)
218- if (relatedTarget === null ) {
219- // Window is losing focus (e.g., Cmd+Tab), don't change focus state
208+ // Still within Wave AI (focus or selection) - don't revert
209+ if (waveAIHasFocusWithin ()) {
220210 return ;
221211 }
222212
223- // Focus is truly leaving Wave AI, revert to node focus
224- globalStore .set (atoms .waveAIFocusedAtom , false );
213+ // Focus truly leaving Wave AI, revert to node focus
225214 focusManager .requestNodeFocus ();
226215}, []);
227216```
228217
218+ ** Note:** ` waveAIHasFocusWithin() ` checks both:
219+
220+ 1 . If ` relatedTarget ` is within Wave AI panel (handles context menus, buttons)
221+ 2 . If there's an active selection in Wave AI (handles text selection clicks)
222+
223+ This combines both checks from the original implementation into a single utility call.
224+
229225## Block Focus Integration
230226
231227** File** : [ ` frontend/app/block/block.tsx ` ] ( frontend/app/block/block.tsx )
@@ -478,20 +474,22 @@ Physical DOM focus granted ✓
478474### 2. Wave AI Integration
479475
480476- Add ` onFocusCapture ` to Wave AI panel
481- - Update ` handleBlur ` with selection protection
477+ - Update ` handleBlur ` with simplified ` waveAIHasFocusWithin() ` check
482478- Update ` handleClick ` with selection awareness
479+ - Components read ` focusManager.focusType ` directly via ` useAtomValue ` for focus ring display
483480
484481### 3. Layout Integration
485482
486- - Update ` isFocused ` atom to check focus manager
487- - Update keyboard navigation to use focus manager
488- - Update global refocus utilities
483+ - Update ` isFocused ` atom to check ` focusManager.focusType `
484+ - Add ` focusManager.requestNodeFocus() ` calls in ` treeReducer ` for focus-claiming operations
485+ - Update keyboard navigation to use ` focusManager.getFocusType() `
489486
490487### 4. Testing
491488
492489- Test all transitions and edge cases
493490- Verify selection protection works
494491- Confirm no focus ring flashing
492+ - Verify focus rings are synchronized through focus manager
495493
496494## Files to Create/Modify
497495
@@ -538,15 +536,16 @@ The changes can be broken into safe, independently testable phases. Each phase c
538536``` typescript
539537// In focusManager.ts - ADD these methods
540538class FocusManager {
541- // NEW methods that ALSO update the old waveAIFocusedAtom
539+ // NEW methods that ALSO update the old waveAIFocusedAtom during migration
542540 requestWaveAIFocus(): void {
543541 globalStore .set (this .focusType , " waveai" );
544- globalStore .set (atoms .waveAIFocusedAtom , true ); // ← Keep old atom in sync!
542+ globalStore .set (atoms .waveAIFocusedAtom , true ); // ← Keep old atom in sync during migration !
545543 }
546544
547545 requestNodeFocus(): void {
546+ // NO defensive checks - when called, we TAKE focus (selections may be lost)
548547 globalStore .set (this .focusType , " node" );
549- globalStore .set (atoms .waveAIFocusedAtom , false ); // ← Keep old atom in sync!
548+ globalStore .set (atoms .waveAIFocusedAtom , false ); // ← Keep old atom in sync during migration !
550549 }
551550
552551 getFocusType(): FocusStrType {
@@ -558,7 +557,6 @@ class FocusManager {
558557 }
559558
560559 nodeFocusWithin(): boolean {
561- // Check if focus is in a block
562560 return focusedBlockId () != null ;
563561 }
564562}
@@ -567,9 +565,10 @@ class FocusManager {
567565** Why this is safe:**
568566
569567- Doesn't change any existing code
570- - Focus manager updates BOTH new ` focusType ` AND old ` waveAIFocusedAtom `
568+ - Focus manager updates BOTH new ` focusType ` AND old ` waveAIFocusedAtom ` during migration
571569- Everything keeps working exactly as before
572570- Can test focus manager methods in isolation
571+ - Components can read ` focusType ` directly via ` useAtomValue ` for reactive updates
573572- No user-visible changes
574573
575574** Testing:**
0 commit comments