Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 110 additions & 34 deletions frontend/src/components/UnifiedChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ import {
Search,
Loader2,
Globe,
Brain
Brain,
Maximize2,
Minimize2
} from "lucide-react";
import RecordRTC from "recordrtc";
import { useQueryClient } from "@tanstack/react-query";
Expand Down Expand Up @@ -663,6 +665,25 @@ export function UnifiedChat() {
// Web search toggle state
const [isWebSearchEnabled, setIsWebSearchEnabled] = useState(false);

// Fullscreen mode for power users - persisted in localStorage
const [isFullscreen, setIsFullscreen] = useState(() => {
return localStorage.getItem("chatFullscreen") === "true";
});
const [isFullscreenAnimating, setIsFullscreenAnimating] = useState(false);

// Save fullscreen preference to localStorage when it changes
useEffect(() => {
localStorage.setItem("chatFullscreen", isFullscreen.toString());
}, [isFullscreen]);

// Toggle fullscreen with animation
const toggleFullscreen = useCallback(() => {
setIsFullscreenAnimating(true);
setIsFullscreen((prev) => !prev);
// Reset animation state after transition completes
setTimeout(() => setIsFullscreenAnimating(false), 300);
}, []);

// Easter egg state (for future features)
const [logoTapCount, setLogoTapCount] = useState(0);
const tapTimeoutRef = useRef<number | null>(null);
Expand Down Expand Up @@ -2355,35 +2376,52 @@ export function UnifiedChat() {
{/* Input Area - centered when no messages, fixed at bottom when chatting */}
{messages.length === 0 && !chatId ? (
// Centered input for new chat
<div className="absolute inset-0 flex flex-col justify-center px-4">
<div className="w-full max-w-4xl mx-auto">
{/* Logo section - raised higher */}
<div className="flex flex-col items-center -mt-20 mb-16">
{/* Logo with Maple text - combined image */}
<div
className="flex items-center justify-center mb-3"
onClick={handleLogoTap}
style={{ cursor: "default" }}
>
<img
src="/maple-leaf-and-maple-white.png"
alt="Maple"
className="h-12 hidden dark:block"
/>
<img
src="/maple-leaf-and-maple-black.png"
alt="Maple"
className="h-12 block dark:hidden"
/>
</div>
<div
className={`absolute inset-0 flex flex-col px-4 ${
isFullscreenAnimating ? "transition-all duration-300" : ""
} ${isFullscreen ? "justify-start pt-8" : "justify-center"}`}
>
<div
className={`w-full mx-auto ${
isFullscreenAnimating ? "transition-all duration-300" : ""
} ${isFullscreen ? "max-w-6xl h-full flex flex-col" : "max-w-4xl"}`}
>
{/* Logo section - hidden in fullscreen */}
{!isFullscreen && (
<div className="flex flex-col items-center -mt-20 mb-16">
{/* Logo with Maple text - combined image */}
<div
className="flex items-center justify-center mb-3"
onClick={handleLogoTap}
style={{ cursor: "default" }}
>
<img
src="/maple-leaf-and-maple-white.png"
alt="Maple"
className="h-12 hidden dark:block"
/>
<img
src="/maple-leaf-and-maple-black.png"
alt="Maple"
className="h-12 block dark:hidden"
/>
</div>

{/* Subtitle right under the logo */}
<p className="text-xl font-light text-muted-foreground">Private AI Chat</p>
</div>
{/* Subtitle right under the logo */}
<p className="text-xl font-light text-muted-foreground">Private AI Chat</p>
</div>
)}

{/* Main prompt section with more emphasis */}
<div className="flex flex-col items-center gap-6">
<h1 className="text-3xl font-medium text-foreground">How can I help you today?</h1>
<div
className={`flex flex-col items-center gap-6 ${isFullscreen ? "flex-1 justify-center" : ""}`}
>
{/* "How can I help you today?" - hidden in fullscreen */}
{!isFullscreen && (
<h1 className="text-3xl font-medium text-foreground">
How can I help you today?
</h1>
)}
Comment on lines +2379 to +2424
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Textarea never actually reaches fullscreen height due to 200px JS cap

The fullscreen UI/UX wiring looks good (logo + prompt hidden, container widened to max-w-6xl, input box h-[70vh] max-h-[800px], toggle button, branding, etc.). However, the existing auto-resize effect still enforces a hard 200px max height on the textarea:

useEffect(() => {
  if (textareaRef.current) {
    textareaRef.current.style.height = "auto";
    const scrollHeight = textareaRef.current.scrollHeight;
    textareaRef.current.style.height = `${Math.min(scrollHeight, 200)}px`;
  }
}, [input]);

So even in fullscreen, the textarea never grows beyond 200px, which defeats the goal of a large 70vh editor.

A minimal fix is to make the auto-resize aware of isFullscreen and allow a larger cap there, while preserving current behavior elsewhere:

-  useEffect(() => {
-    if (textareaRef.current) {
-      textareaRef.current.style.height = "auto";
-      const scrollHeight = textareaRef.current.scrollHeight;
-      textareaRef.current.style.height = `${Math.min(scrollHeight, 200)}px`;
-    }
-  }, [input]);
+  useEffect(() => {
+    if (!textareaRef.current) return;
+
+    textareaRef.current.style.height = "auto";
+    const scrollHeight = textareaRef.current.scrollHeight;
+    const maxHeight = isFullscreen ? 800 : 200;
+    textareaRef.current.style.height = `${Math.min(scrollHeight, maxHeight)}px`;
+  }, [input, isFullscreen]);

This keeps the compact behavior for the non-fullscreen inputs while allowing the fullscreen editor to grow toward the intended 70vh/800px height as the prompt gets longer.

Optional UX note: fullscreen is currently only available in the “new chat” view (messages.length === 0 && !chatId). If you later want fullscreen while in an active conversation as well, you could reuse this same toggle and state in the bottom input layout to keep the experience consistent.

Also applies to: 2470-2487, 2496-2499, 2688-2709

🤖 Prompt for AI Agents
In frontend/src/components/UnifiedChat.tsx around lines 2370-2415 (and also
apply to the other noted spots: 2470-2487, 2496-2499, 2688-2709), the textarea
auto-resize effect currently hard-caps the height to 200px; update the effect to
be fullscreen-aware so when isFullscreen is true it uses a much larger cap
(e.g., based on 70vh or the 800px max used in CSS) and when false retains the
existing compact 200px cap — compute the scrollHeight, pick the cap based on
isFullscreen, and set textareaRef.current.style.height to the min(scrollHeight,
cap); apply the same change to the other repeated resize handlers so the
fullscreen editor can grow to the intended size.


{/* Input form */}
<form onSubmit={handleSendMessage} className="w-full relative">
Expand Down Expand Up @@ -2438,16 +2476,36 @@ export function UnifiedChat() {
)}

{/* Main input container with purple focus border */}
<div className="relative rounded-xl border-2 border-border focus-within:border-purple-500 transition-colors bg-background overflow-hidden">
<div
className={`relative rounded-xl border-2 border-border focus-within:border-purple-500 bg-background overflow-hidden ${
isFullscreenAnimating ? "transition-all duration-300" : "transition-colors"
} ${isFullscreen ? "flex flex-col h-[70vh] max-h-[800px]" : ""}`}
>
{/* Fullscreen toggle button - top right corner */}
<button
type="button"
onClick={toggleFullscreen}
className="absolute right-2 top-2 z-10 p-1.5 rounded-md text-muted-foreground/60 hover:text-foreground hover:bg-muted/50 transition-colors"
aria-label={isFullscreen ? "Exit fullscreen" : "Enter fullscreen"}
>
{isFullscreen ? (
<Minimize2 className="h-4 w-4" />
) : (
<Maximize2 className="h-4 w-4" />
)}
</button>

<Textarea
ref={textareaRef}
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Message Maple..."
disabled={isGenerating || isRecording}
className="w-full resize-none min-h-[120px] max-h-[200px] px-5 pt-4 pb-2 border-0 bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 placeholder:text-muted-foreground/60 text-base"
rows={4}
className={`w-full resize-none px-5 pt-4 pb-2 pr-10 border-0 bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 placeholder:text-muted-foreground/60 text-base ${
Comment thread
AnthonyRonning marked this conversation as resolved.
isFullscreen ? "flex-1 min-h-0" : "min-h-[120px] max-h-[200px]"
}`}
rows={isFullscreen ? undefined : 4}
id="message"
/>

Expand Down Expand Up @@ -2636,10 +2694,28 @@ export function UnifiedChat() {
</div>
</form>

{/* Footer text */}
<p className="text-sm text-center text-muted-foreground/60">
Encrypted at every step
</p>
{/* Footer text - hidden in fullscreen */}
{!isFullscreen && (
<p className="text-sm text-center text-muted-foreground/60">
Encrypted at every step
</p>
)}

{/* Tiny branding in bottom right when fullscreen */}
{isFullscreen && (
<div className="fixed bottom-4 right-4">
Comment thread
AnthonyRonning marked this conversation as resolved.
<img
src="/maple-leaf-and-maple-white.png"
alt="Maple"
className="h-6 hidden dark:block"
/>
<img
src="/maple-leaf-and-maple-black.png"
alt="Maple"
className="h-6 block dark:hidden"
/>
</div>
)}
</div>
</div>
</div>
Expand Down
Loading