File size: 2,458 Bytes
064bfd6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | import { useEffect, useRef } from 'react'
import { useNotifications } from '../context/notifications.js'
import { getShortcutDisplay } from '../keybindings/shortcutFormat.js'
import { hasImageInClipboard } from '../utils/imagePaste.js'
const NOTIFICATION_KEY = 'clipboard-image-hint'
// Small debounce to batch rapid focus changes
const FOCUS_CHECK_DEBOUNCE_MS = 1000
// Don't show the hint more than once per this interval
const HINT_COOLDOWN_MS = 30000
/**
* Hook that shows a notification when the terminal regains focus
* and the clipboard contains an image.
*
* @param isFocused - Whether the terminal is currently focused
* @param enabled - Whether image paste is enabled (onImagePaste is defined)
*/
export function useClipboardImageHint(
isFocused: boolean,
enabled: boolean,
): void {
const { addNotification } = useNotifications()
const lastFocusedRef = useRef(isFocused)
const lastHintTimeRef = useRef(0)
const checkTimeoutRef = useRef<NodeJS.Timeout | null>(null)
useEffect(() => {
// Only trigger on focus regain (was unfocused, now focused)
const wasFocused = lastFocusedRef.current
lastFocusedRef.current = isFocused
if (!enabled || !isFocused || wasFocused) {
return
}
// Clear any pending check
if (checkTimeoutRef.current) {
clearTimeout(checkTimeoutRef.current)
}
// Small debounce to batch rapid focus changes
checkTimeoutRef.current = setTimeout(
async (checkTimeoutRef, lastHintTimeRef, addNotification) => {
checkTimeoutRef.current = null
// Check cooldown to avoid spamming the user
const now = Date.now()
if (now - lastHintTimeRef.current < HINT_COOLDOWN_MS) {
return
}
// Check if clipboard has an image (async osascript call)
if (await hasImageInClipboard()) {
lastHintTimeRef.current = now
addNotification({
key: NOTIFICATION_KEY,
text: `Image in clipboard · ${getShortcutDisplay('chat:imagePaste', 'Chat', 'ctrl+v')} to paste`,
priority: 'immediate',
timeoutMs: 8000,
})
}
},
FOCUS_CHECK_DEBOUNCE_MS,
checkTimeoutRef,
lastHintTimeRef,
addNotification,
)
return () => {
if (checkTimeoutRef.current) {
clearTimeout(checkTimeoutRef.current)
checkTimeoutRef.current = null
}
}
}, [isFocused, enabled, addNotification])
}
|