/** * @license * SPDX-License-Identifier: Apache-2.0 */ /** * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import cn from 'classnames'; // FIX: Added missing React imports. import React, { memo, useEffect, useRef, useState, FormEvent, Ref } from 'react'; import { useLogStore, useUI, useSettings } from '@/lib/state'; import { useLiveAPIContext } from '../contexts/LiveAPIContext'; // Hook to detect screen size for responsive component rendering const useMediaQuery = (query: string) => { const [matches, setMatches] = useState(false); useEffect(() => { const media = window.matchMedia(query); if (media.matches !== matches) { setMatches(media.matches); } const listener = () => { setMatches(media.matches); }; media.addEventListener('change', listener); return () => media.removeEventListener('change', listener); }, [matches, query]); return matches; }; export type ControlTrayProps = { trayRef?: Ref; }; function ControlTray({trayRef}: ControlTrayProps) { const [textPrompt, setTextPrompt] = useState(''); const connectButtonRef = useRef(null); const { toggleSidebar } = useUI(); const { activateEasterEggMode } = useSettings(); const settingsClickTimestamps = useRef([]); const isMobile = useMediaQuery('(max-width: 768px), (orientation: landscape) and (max-height: 768px)'); const [isTextEntryVisible, setIsTextEntryVisible] = useState(false); const isLandscape = useMediaQuery('(orientation: landscape) and (max-height: 768px)'); const { client, connected, connect, disconnect } = useLiveAPIContext(); useEffect(() => { if (!connected && connectButtonRef.current) { connectButtonRef.current.focus(); } }, [connected]); const handleTextSubmit = async (e: FormEvent) => { e.preventDefault(); if (!textPrompt.trim()) return; useLogStore.getState().addTurn({ role: 'user', text: textPrompt, isFinal: true, }); const currentPrompt = textPrompt; setTextPrompt(''); // Clear input immediately if (!connected) { console.warn("Cannot send text message: not connected to live stream."); useLogStore.getState().addTurn({ role: 'system', text: `Cannot send message. Please connect to the stream first.`, isFinal: true, }); return; } client.sendRealtimeText(currentPrompt); }; const handleSettingsClick = () => { toggleSidebar(); const now = Date.now(); settingsClickTimestamps.current.push(now); // Filter out clicks older than 3 seconds settingsClickTimestamps.current = settingsClickTimestamps.current.filter( timestamp => now - timestamp < 3000 ); if (settingsClickTimestamps.current.length >= 6) { activateEasterEggMode(); useLogStore.getState().addTurn({ role: 'system', text: "You've unlocked Scavenger Hunt mode!.", isFinal: true, }); // Reset after triggering settingsClickTimestamps.current = []; } }; const connectButtonTitle = connected ? 'Stop session' : 'Start session'; return (
); } export default memo(ControlTray);