Fix AIChat fullscreen

- Integrate browser fullscreen in AIChat
- Sync Escape handling with exitFullscreen
- Update UI layout for mobile readability
- Adjust fullscreen toggle to use useBrowserFullscreen
- Minor UI tweaks to memory indicator and controls

X-Lovable-Edit-ID: edt-6e746bae-41a5-43a4-b628-c56d2a857b0e
This commit is contained in:
gpt-engineer-app[bot]
2026-01-15 09:41:02 +00:00
+32 -22
View File
@@ -10,6 +10,7 @@ import { useToast } from '@/hooks/use-toast';
import GlitchText from '@/components/GlitchText'; import GlitchText from '@/components/GlitchText';
import MessageContent from '@/components/MessageContent'; import MessageContent from '@/components/MessageContent';
import { AI_PROVIDERS, AIProvider, getProvider, CUSTOM_API_STORAGE_KEY } from '@/lib/aiProviders'; import { AI_PROVIDERS, AIProvider, getProvider, CUSTOM_API_STORAGE_KEY } from '@/lib/aiProviders';
import { useBrowserFullscreen } from '@/hooks/useGameDimensions';
import { import {
DropdownMenu, DropdownMenu,
DropdownMenuContent, DropdownMenuContent,
@@ -104,21 +105,30 @@ const AIChat = () => {
} }
}, [customApiConfig]); }, [customApiConfig]);
// Exit fullscreen on Escape key const isMobile = typeof window !== 'undefined' && window.innerWidth < 768;
const { enterFullscreen, exitFullscreen } = useBrowserFullscreen();
// Exit fullscreen on Escape key // Exit fullscreen on Escape key
useEffect(() => { useEffect(() => {
const handleEscape = (e: KeyboardEvent) => { const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape' && isFullscreen) { if (e.key === 'Escape' && isFullscreen) {
setIsFullscreen(false); setIsFullscreen(false);
exitFullscreen();
playSound('click'); playSound('click');
} }
}; };
window.addEventListener('keydown', handleEscape); window.addEventListener('keydown', handleEscape);
return () => window.removeEventListener('keydown', handleEscape); return () => window.removeEventListener('keydown', handleEscape);
}, [isFullscreen, playSound]); }, [isFullscreen, playSound, exitFullscreen]);
const toggleFullscreen = () => { const toggleFullscreen = async () => {
setIsFullscreen(prev => !prev); if (!isFullscreen) {
setIsFullscreen(true);
await enterFullscreen();
} else {
setIsFullscreen(false);
await exitFullscreen();
}
playSound('click'); playSound('click');
}; };
@@ -379,21 +389,21 @@ const AIChat = () => {
transition={{ duration: 0.5 }} transition={{ duration: 0.5 }}
className={`flex flex-col ${ className={`flex flex-col ${
isFullscreen isFullscreen
? 'fixed inset-0 z-50 bg-background p-4 md:p-8' ? 'fixed inset-0 z-50 bg-background p-3 md:p-8 overflow-hidden'
: 'h-full' : 'h-full'
}`} }`}
> >
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-2 mb-4"> <div className="flex items-center justify-between gap-2 mb-3 flex-shrink-0">
<GlitchText <GlitchText
text="AI Terminal" text="AI Terminal"
className="font-minecraft text-2xl md:text-3xl text-primary text-glow" className={`font-minecraft ${isMobile ? 'text-xl' : 'text-2xl md:text-3xl'} text-primary text-glow`}
/> />
<div className="flex items-center gap-2 flex-wrap"> <div className="flex items-center gap-1 sm:gap-2">
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={toggleFullscreen} onClick={toggleFullscreen}
className="text-primary hover:bg-primary/20 order-first sm:order-last" className="text-primary hover:bg-primary/20 p-2"
title={isFullscreen ? 'Exit fullscreen (Esc)' : 'Fullscreen'} title={isFullscreen ? 'Exit fullscreen (Esc)' : 'Fullscreen'}
> >
{isFullscreen ? ( {isFullscreen ? (
@@ -404,8 +414,8 @@ const AIChat = () => {
</Button> </Button>
{messages.length > 0 && ( {messages.length > 0 && (
<> <>
<div className="flex items-center gap-2 px-2 py-1 border border-primary/30 rounded text-xs font-pixel"> <div className="hidden sm:flex items-center gap-2 px-2 py-1 border border-primary/30 rounded text-xs font-pixel">
<span className="text-muted-foreground">Memory:</span> <span className="text-muted-foreground">Mem:</span>
<span <span
className={`${ className={`${
(() => { (() => {
@@ -437,24 +447,24 @@ const AIChat = () => {
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={clearChat} onClick={clearChat}
className="text-primary hover:bg-primary/20" className="text-primary hover:bg-primary/20 p-2"
> >
<Trash2 className="w-4 h-4 mr-2" /> <Trash2 className="w-4 h-4" />
Clear <span className="hidden sm:inline ml-2">Clear</span>
</Button> </Button>
</> </>
)} )}
</div> </div>
</div> </div>
<div className="flex items-center gap-2 mb-4"> <div className="flex items-center gap-2 mb-3 flex-shrink-0 overflow-x-auto">
<span className="text-muted-foreground font-pixel text-sm">{'>'} Model:</span> <span className="text-muted-foreground font-pixel text-xs sm:text-sm whitespace-nowrap">{'>'} Model:</span>
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
className="text-primary hover:bg-primary/20 font-pixel text-sm gap-1" className="text-primary hover:bg-primary/20 font-pixel text-xs sm:text-sm gap-1 px-2"
> >
{getProvider(selectedProvider).name} {getProvider(selectedProvider).name}
<ChevronDown className="w-3 h-3" /> <ChevronDown className="w-3 h-3" />
@@ -507,11 +517,11 @@ const AIChat = () => {
</div> </div>
<ScrollArea <ScrollArea
className="flex-1 border border-primary/30 rounded-lg p-4 mb-4 bg-background/50" className="flex-1 min-h-0 border border-primary/30 rounded-lg p-2 sm:p-4 mb-3 bg-background/50"
ref={scrollRef} ref={scrollRef}
> >
{messages.length === 0 ? ( {messages.length === 0 ? (
<div className="h-full flex items-center justify-center text-muted-foreground"> <div className="h-full flex items-center justify-center text-muted-foreground py-8">
<div className="text-center"> <div className="text-center">
<Bot className="w-12 h-12 mx-auto mb-4 opacity-50" /> <Bot className="w-12 h-12 mx-auto mb-4 opacity-50" />
<p className="font-pixel text-sm">No messages yet.</p> <p className="font-pixel text-sm">No messages yet.</p>
@@ -549,7 +559,7 @@ const AIChat = () => {
</div> </div>
)} )}
<div <div
className={`max-w-[80%] p-3 rounded-lg ${ className={`max-w-[85%] sm:max-w-[80%] p-2 sm:p-3 rounded-lg text-sm ${
message.role === 'user' message.role === 'user'
? 'bg-primary/20 border border-primary/50' ? 'bg-primary/20 border border-primary/50'
: 'bg-secondary/50 border border-primary/30' : 'bg-secondary/50 border border-primary/30'
@@ -590,14 +600,14 @@ const AIChat = () => {
)} )}
</ScrollArea> </ScrollArea>
<div className="flex gap-2"> <div className="flex gap-2 flex-shrink-0">
<Textarea <Textarea
value={input} value={input}
onChange={(e) => setInput(e.target.value)} onChange={(e) => setInput(e.target.value)}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
placeholder="Type your message..." placeholder="Type your message..."
disabled={isLoading} disabled={isLoading}
className="resize-none font-pixel text-sm bg-background/50 border-primary/50 text-primary placeholder:text-muted-foreground focus:border-primary" className="resize-none font-pixel text-xs sm:text-sm bg-background/50 border-primary/50 text-primary placeholder:text-muted-foreground focus:border-primary min-h-[60px]"
rows={2} rows={2}
/> />
<Button <Button