Skip to content

Commit 6044022

Browse files
committed
more updates
1 parent ed51df0 commit 6044022

1 file changed

Lines changed: 36 additions & 37 deletions

File tree

aiprompts/waveai-focus-updates.md

Lines changed: 36 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -48,29 +48,33 @@ Add selection-aware focus methods:
4848
```typescript
4949
class 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
193197
const 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()
199202
const 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
540538
class 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

Comments
 (0)