import { useEffect, useRef } from 'react' import { useI18n } from '@/i18n' import { setPetActivity } from '@/store/pet' import { setPetScale } from '@/store/pet-gallery' import { setPetOverlayLocale, setPetOverlayOpenAppHandler, setPetOverlayScaleHandler, setPetOverlaySubmitHandler } from '@/store/pet-overlay' import { $sessions } from '@/store/session' import { $attentionSessionIds } from '@/store/session-states' import { isAuxiliaryWindow } from '@/store/windows' import type { GatewayRequester } from '../types' interface PetBridgeParams { requestGateway: GatewayRequester resumeSession: (sessionId: string) => Promise | unknown submitText: (text: string) => Promise | unknown } /** * Wires the popped-out pet overlay back into the app: submit a prompt, resize, * and open the most-recent thread, plus mirroring "a session is awaiting the * user" into the pet's pose. Handlers register ONCE through refs tracking the * latest callbacks — re-registering on identity churn leaves a nulled-handler * window that can drop a submit. Primary window only. */ export function usePetBridge({ requestGateway, resumeSession, submitText }: PetBridgeParams): void { const { locale } = useI18n() useEffect(() => { if (!isAuxiliaryWindow()) { setPetOverlayLocale(locale) } }, [locale]) const submitTextRef = useRef(submitText) submitTextRef.current = submitText const resumeSessionRef = useRef(resumeSession) resumeSessionRef.current = resumeSession const requestGatewayRef = useRef(requestGateway) requestGatewayRef.current = requestGateway useEffect(() => { if (isAuxiliaryWindow()) { return } setPetOverlaySubmitHandler(text => void submitTextRef.current(text)) // Alt+wheel resize from the popped-out pet — persist through this window's // gateway (the overlay has none) so it survives restart. setPetOverlayScaleHandler(scale => setPetScale(requestGatewayRef.current, scale)) // Mail icon: $sessions is most-recent-first; the pet is global, so "most // recent" is the right target. setPetOverlayOpenAppHandler(() => { const recent = $sessions.get()[0] if (recent?.id) { void resumeSessionRef.current(recent.id) } }) return () => { setPetOverlaySubmitHandler(null) setPetOverlayOpenAppHandler(null) setPetOverlayScaleHandler(null) } }, []) // Mirror "a session is blocked on the user" (clarify/approval) into the pet's // awaitingInput flag so it shows the `waiting` pose. useEffect(() => { const sync = () => setPetActivity({ awaitingInput: $attentionSessionIds.get().length > 0 }) sync() return $attentionSessionIds.listen(sync) }, []) }