( isFocused: boolean, enabled: boolean, )
| 17 | * @param enabled - Whether image paste is enabled (onImagePaste is defined) |
| 18 | */ |
| 19 | export function useClipboardImageHint( |
| 20 | isFocused: boolean, |
| 21 | enabled: boolean, |
| 22 | ): void { |
| 23 | const { addNotification } = useNotifications() |
| 24 | const lastFocusedRef = useRef(isFocused) |
| 25 | const lastHintTimeRef = useRef(0) |
| 26 | const checkTimeoutRef = useRef<NodeJS.Timeout | null>(null) |
| 27 | |
| 28 | useEffect(() => { |
| 29 | // Only trigger on focus regain (was unfocused, now focused) |
| 30 | const wasFocused = lastFocusedRef.current |
| 31 | lastFocusedRef.current = isFocused |
| 32 | |
| 33 | if (!enabled || !isFocused || wasFocused) { |
| 34 | return |
| 35 | } |
| 36 | |
| 37 | // Clear any pending check |
| 38 | if (checkTimeoutRef.current) { |
| 39 | clearTimeout(checkTimeoutRef.current) |
| 40 | } |
| 41 | |
| 42 | // Small debounce to batch rapid focus changes |
| 43 | checkTimeoutRef.current = setTimeout( |
| 44 | async (checkTimeoutRef, lastHintTimeRef, addNotification) => { |
| 45 | checkTimeoutRef.current = null |
| 46 | |
| 47 | // Check cooldown to avoid spamming the user |
| 48 | const now = Date.now() |
| 49 | if (now - lastHintTimeRef.current < HINT_COOLDOWN_MS) { |
| 50 | return |
| 51 | } |
| 52 | |
| 53 | // Check if clipboard has an image (async osascript call) |
| 54 | if (await hasImageInClipboard()) { |
| 55 | lastHintTimeRef.current = now |
| 56 | addNotification({ |
| 57 | key: NOTIFICATION_KEY, |
| 58 | text: `Image in clipboard · ${getShortcutDisplay('chat:imagePaste', 'Chat', 'ctrl+v')} to paste`, |
| 59 | priority: 'immediate', |
| 60 | timeoutMs: 8000, |
| 61 | }) |
| 62 | } |
| 63 | }, |
| 64 | FOCUS_CHECK_DEBOUNCE_MS, |
| 65 | checkTimeoutRef, |
| 66 | lastHintTimeRef, |
| 67 | addNotification, |
| 68 | ) |
| 69 | |
| 70 | return () => { |
| 71 | if (checkTimeoutRef.current) { |
| 72 | clearTimeout(checkTimeoutRef.current) |
| 73 | checkTimeoutRef.current = null |
| 74 | } |
| 75 | } |
| 76 | }, [isFocused, enabled, addNotification]) |
no test coverage detected