Skip to content
Merged
Show file tree
Hide file tree
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
13 changes: 11 additions & 2 deletions frontend/src/components/ProjectDetailView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
import { useOpenSecret, type Conversation } from "@opensecret/react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { Sidebar, SidebarToggle } from "@/components/Sidebar";
import { useIsMobile } from "@/utils/utils";
import { useIsMobile, useIsLandscapeMobile } from "@/utils/utils";
import { useLocalState } from "@/state/useLocalState";
import { Button } from "@/components/ui/button";
import { Alert, AlertDescription } from "@/components/ui/alert";
Expand Down Expand Up @@ -146,10 +146,19 @@ export function ProjectDetailView({ projectId }: ProjectDetailViewProps) {
const userId = os.auth.user?.user.id;
const queryClient = useQueryClient();
const isMobile = useIsMobile();
const isLandscapeMobile = useIsLandscapeMobile();
const isCompactLayout = isMobile || isLandscapeMobile;
const { setSelectedProjectId } = useLocalState();
const hasAuthUser = !!os.auth.user;

const [isSidebarOpen, setIsSidebarOpen] = useState(!isMobile);
const [isSidebarOpen, setIsSidebarOpen] = useState(!isCompactLayout);

useEffect(() => {
if (isLandscapeMobile && isSidebarOpen) {
setIsSidebarOpen(false);
}
}, [isLandscapeMobile]); // eslint-disable-line react-hooks/exhaustive-deps
Comment on lines +156 to +160
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 | ⚡ Quick win

Sidebar auto-close should key off compact layout, not landscape-only.

On Line 157, the effect only reacts to isLandscapeMobile. If the viewport transitions from desktop to portrait mobile, isCompactLayout becomes true but the sidebar can remain open.

Proposed fix
-  useEffect(() => {
-    if (isLandscapeMobile && isSidebarOpen) {
-      setIsSidebarOpen(false);
-    }
-  }, [isLandscapeMobile]); // eslint-disable-line react-hooks/exhaustive-deps
+  useEffect(() => {
+    if (isCompactLayout && isSidebarOpen) {
+      setIsSidebarOpen(false);
+    }
+  }, [isCompactLayout, isSidebarOpen]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
if (isLandscapeMobile && isSidebarOpen) {
setIsSidebarOpen(false);
}
}, [isLandscapeMobile]); // eslint-disable-line react-hooks/exhaustive-deps
useEffect(() => {
if (isCompactLayout && isSidebarOpen) {
setIsSidebarOpen(false);
}
}, [isCompactLayout, isSidebarOpen]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/ProjectDetailView.tsx` around lines 156 - 160, The
effect currently only watches isLandscapeMobile; change it to key off
isCompactLayout so the sidebar auto-closes for any compact layout. In the
useEffect (function surrounding
isLandscapeMobile/isSidebarOpen/setIsSidebarOpen) replace the condition to check
isCompactLayout (e.g., if (isCompactLayout && isSidebarOpen)
setIsSidebarOpen(false)) and update the dependency array to [isCompactLayout]
(and include isSidebarOpen if you prefer explicitness) instead of
[isLandscapeMobile]; keep useEffect and setIsSidebarOpen as the referenced
symbols.


const [conversations, setConversations] = useState<Conversation[]>([]);
const [hasMoreConversations, setHasMoreConversations] = useState(false);
const [lastConversationId, setLastConversationId] = useState<string | undefined>();
Expand Down
26 changes: 14 additions & 12 deletions frontend/src/components/Sidebar.tsx
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { ChatHistoryList } from "./ChatHistoryList";
import { AccountMenu } from "./AccountMenu";
import { useRef, useEffect, KeyboardEvent, useCallback, useLayoutEffect, useState } from "react";
import { flushSync } from "react-dom";
import { cn, useClickOutside, useIsMobile } from "@/utils/utils";
import { cn, useClickOutside, useIsMobile, useIsLandscapeMobile } from "@/utils/utils";
import { MapleWordmark } from "@/components/MapleWordmark";
import { Input } from "./ui/input";
import { useLocalState } from "@/state/useLocalState";
Expand Down Expand Up @@ -74,8 +74,8 @@ export function Sidebar({
}, [selectedIds.size]);

async function addChat() {
// If sidebar is open on mobile, close it
if (isOpen && isMobile) {
// If sidebar is open on compact layout, close it
if (isOpen && isCompactLayout) {
onToggle();
}

Expand Down Expand Up @@ -136,14 +136,16 @@ export function Sidebar({
const sidebarRef = useRef<HTMLDivElement>(null);
const historyContainerRef = useRef<HTMLElement>(null);

// Use the centralized hook for mobile detection
// Use the centralized hooks for mobile/compact detection
const isMobile = useIsMobile();
const isLandscapeMobile = useIsLandscapeMobile();
const isCompactLayout = isMobile || isLandscapeMobile;

// Modified click outside handler to ignore clicks in dropdowns and dialogs
// Only applies on mobile - desktop users use the toggle button
const handleClickOutside = useCallback(
(event: MouseEvent | TouchEvent) => {
if (isOpen && isMobile) {
if (isOpen && isCompactLayout) {
// Check if the click was inside a dropdown or dialog
const target = event.target as HTMLElement;
const isInDropdown = target.closest('[role="menu"]');
Expand All @@ -155,7 +157,7 @@ export function Sidebar({
}
}
},
[isOpen, onToggle, isMobile]
[isOpen, onToggle, isCompactLayout]
);

useClickOutside(sidebarRef, handleClickOutside);
Expand All @@ -172,8 +174,8 @@ export function Sidebar({
// This effect closes the sidebar on mobile when navigating,
// but preserves search state between navigations
useEffect(() => {
// Only subscribe if we're on mobile and sidebar is open
if (!isMobile || !isOpen) return;
// Only subscribe if we're on compact layout and sidebar is open
if (!isCompactLayout || !isOpen) return;

const unsubscribe = router.subscribe("onResolved", () => {
// Use a microtask to avoid state updates during render
Expand All @@ -182,7 +184,7 @@ export function Sidebar({
if (!isMountedRef.current) return;

// Double-check conditions after async boundary
if (isOpen && isMobile) {
if (isOpen && isCompactLayout) {
onToggle();
}
});
Expand All @@ -191,14 +193,14 @@ export function Sidebar({
return () => {
unsubscribe();
};
}, [router, isOpen, onToggle, isMobile]);
}, [router, isOpen, onToggle, isCompactLayout]);

return (
<div
ref={sidebarRef}
style={SIDEBAR_LAYOUT_STYLE}
className={cn([
"fixed md:static z-10 h-full overflow-x-hidden overflow-y-hidden",
"fixed md:static landscape-short:fixed z-10 h-full overflow-x-hidden overflow-y-hidden",
isOpen ? `block ${SIDEBAR_WIDTH_CLASS}` : "hidden"
])}
>
Expand Down Expand Up @@ -314,7 +316,7 @@ export function Sidebar({
<ChatHistoryList
currentChatId={chatId}
searchQuery={searchQuery}
isMobile={isMobile}
isMobile={isCompactLayout}
isSelectionMode={isSelectionMode}
onExitSelectionMode={exitSelectionMode}
selectedIds={selectedIds}
Expand Down
84 changes: 58 additions & 26 deletions frontend/src/components/UnifiedChat.tsx
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import { Sidebar, SidebarToggle } from "@/components/Sidebar";
import { MapleWordmark } from "@/components/MapleWordmark";
import { useIsMobile } from "@/utils/utils";
import { useIsMobile, useIsLandscapeMobile } from "@/utils/utils";
import { fileToDataURL } from "@/utils/file";
import { truncateMarkdownPreservingLinks } from "@/utils/markdown";
import { useOpenAI } from "@/ai/useOpenAi";
Expand Down Expand Up @@ -1320,9 +1320,9 @@ const MessageList = memo(
<div
key={group.id}
ref={groupIndex === 0 ? firstMessageRef : undefined}
className="group flex justify-end py-4"
className="group flex justify-end py-4 landscape-short:py-1.5"
>
<div className="max-w-[min(100%,42rem)] rounded-2xl border border-border bg-muted px-4 py-3 backdrop-blur-lg dark:bg-card">
<div className="max-w-[min(100%,42rem)] rounded-2xl border border-border bg-muted px-4 py-3 landscape-short:px-3 landscape-short:py-2 backdrop-blur-lg dark:bg-card">
<div className="prose prose-sm max-w-none text-left dark:prose-invert">
<div className="space-y-3">
{message.content.map((part, partIdx) => {
Expand Down Expand Up @@ -1381,10 +1381,10 @@ const MessageList = memo(
<div
key={group.id}
ref={groupIndex === 0 ? firstMessageRef : undefined}
className="group py-4 px-0 md:p-4"
className="group py-4 px-0 md:p-4 landscape-short:py-1.5 landscape-short:px-2"
>
<div className="mx-auto flex w-full max-w-4xl flex-col gap-2 md:flex-row md:items-start md:gap-3">
<div className="flex h-8 shrink-0 items-center gap-2 px-0 md:h-auto md:flex-col md:items-start md:gap-3">
<div className="flex h-8 shrink-0 items-center gap-2 px-0 md:h-auto md:flex-col md:items-start md:gap-3 landscape-short:h-6">
<MapleChatAvatar />
<div className="text-sm font-semibold leading-none md:hidden">Maple</div>
</div>
Expand Down Expand Up @@ -1418,9 +1418,9 @@ const MessageList = memo(

{/* Loading indicator - only show while waiting for the first assistant item (TTFT) */}
{shouldShowInitialAssistantLoader && (
<div className="group py-4 px-0 md:p-4">
<div className="group py-4 px-0 md:p-4 landscape-short:py-1.5 landscape-short:px-2">
<div className="mx-auto flex w-full max-w-4xl flex-col gap-2 md:flex-row md:items-start md:gap-3">
<div className="flex h-8 shrink-0 items-center gap-2 px-0 md:h-auto md:flex-col md:items-start md:gap-3">
<div className="flex h-8 shrink-0 items-center gap-2 px-0 md:h-auto md:flex-col md:items-start md:gap-3 landscape-short:h-6">
<MapleChatAvatar />
<div className="text-sm font-semibold leading-none md:hidden">Maple</div>
</div>
Expand All @@ -1446,6 +1446,8 @@ MessageList.displayName = "MessageList";

export function UnifiedChat() {
const isMobile = useIsMobile();
const isLandscapeMobile = useIsLandscapeMobile();
const isCompactLayout = isMobile || isLandscapeMobile;
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
const openai = useOpenAI();
const localState = useLocalState();
const { selectedProjectId, setSelectedProjectId } = localState;
Expand All @@ -1466,7 +1468,7 @@ export function UnifiedChat() {
const [input, setInput] = useState("");
const [draftProjectId, setDraftProjectId] = useState<string | null>(() => selectedProjectId);
const [isGenerating, setIsGenerating] = useState(false);
const [isSidebarOpen, setIsSidebarOpen] = useState(!isMobile);
const [isSidebarOpen, setIsSidebarOpen] = useState(!isCompactLayout);
const [error, setError] = useState<string | null>(null);
const [lastSeenItemId, setLastSeenItemId] = useState<string | undefined>();
const [isNewConversationJustCreated, setIsNewConversationJustCreated] = useState(false);
Expand Down Expand Up @@ -1508,6 +1510,13 @@ export function UnifiedChat() {
});
const [isFullscreenAnimating, setIsFullscreenAnimating] = useState(false);

// Close sidebar when rotating to landscape on a short screen
useEffect(() => {
if (isLandscapeMobile && isSidebarOpen) {
setIsSidebarOpen(false);
}
}, [isLandscapeMobile]); // eslint-disable-line react-hooks/exhaustive-deps
Comment on lines +1514 to +1518
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 | ⚡ Quick win

Compact-mode sidebar behavior should include portrait mobile transitions.

On Line 1515, auto-close is tied only to isLandscapeMobile. The compact rules now include portrait mobile too (isCompactLayout), so desktop-to-portrait resize can leave the sidebar open unexpectedly.

Proposed fix
-  useEffect(() => {
-    if (isLandscapeMobile && isSidebarOpen) {
-      setIsSidebarOpen(false);
-    }
-  }, [isLandscapeMobile]); // eslint-disable-line react-hooks/exhaustive-deps
+  useEffect(() => {
+    if (isCompactLayout && isSidebarOpen) {
+      setIsSidebarOpen(false);
+    }
+  }, [isCompactLayout, isSidebarOpen]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/UnifiedChat.tsx` around lines 1514 - 1518, The
useEffect that auto-closes the sidebar only checks isLandscapeMobile; update it
to also consider the compact layout flag so portrait-mobile transitions close
the sidebar too: change the conditional inside the useEffect to if
((isLandscapeMobile || isCompactLayout) && isSidebarOpen)
setIsSidebarOpen(false), and update the dependency array to [isLandscapeMobile,
isCompactLayout, isSidebarOpen] (remove the eslint-disable-line) so the effect
runs when either layout flag or sidebar state changes; modify the effect
surrounding function name useEffect and the referenced symbols
isLandscapeMobile, isCompactLayout, isSidebarOpen, and setIsSidebarOpen
accordingly.


// Save fullscreen preference to localStorage when it changes
useEffect(() => {
localStorage.setItem("chatFullscreen", isFullscreen.toString());
Expand Down Expand Up @@ -1586,11 +1595,11 @@ export function UnifiedChat() {
};
}, []);

// Auto-focus textbox on desktop (not mobile to avoid keyboard popup interrupting reading)
// Auto-focus textbox on desktop (not mobile/landscape-mobile to avoid keyboard popup interrupting reading)
// Focus when: app launches, new chat, conversation loads, or assistant finishes streaming
useEffect(() => {
// Skip on mobile to avoid keyboard popup
if (isMobile) return;
// Skip on compact layouts (mobile + landscape mobile) to avoid keyboard popup
if (isCompactLayout) return;

// Focus when not generating and textbox is not disabled
if (!isGenerating && textareaRef.current && !textareaRef.current.disabled) {
Expand All @@ -1599,7 +1608,7 @@ export function UnifiedChat() {
textareaRef.current?.focus();
}, 100);
}
}, [isMobile, isGenerating, messages.length, chatId]);
}, [isCompactLayout, isGenerating, messages.length, chatId]);

// Improved scroll detection - track if user is near bottom
const handleScroll = useCallback(() => {
Expand Down Expand Up @@ -3231,7 +3240,7 @@ export function UnifiedChat() {
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
// On desktop: Enter submits, Shift+Enter for new line
// On mobile: Enter for new line, no keyboard shortcut to submit (use button)
if (e.key === "Enter" && !e.shiftKey && !isMobile) {
if (e.key === "Enter" && !e.shiftKey && !isCompactLayout) {
e.preventDefault();
handleSendMessage();
}
Expand Down Expand Up @@ -3280,8 +3289,8 @@ export function UnifiedChat() {
</div>
)}

{/* Sidebar toggle + wordmark — fixed except on mobile while chatting (two-row header below) */}
{!isSidebarOpen && !(isMobile && messages.length > 0) && (
{/* Sidebar toggle + wordmark — fixed except on compact layouts while chatting (two-row header below) */}
{!isSidebarOpen && !(isCompactLayout && messages.length > 0) && (
<div className="fixed left-4 top-[9.5px] z-20 flex items-center gap-1.5">
<SidebarToggle onToggle={toggleSidebar} />
<MapleWordmark
Expand All @@ -3293,7 +3302,30 @@ export function UnifiedChat() {

{/* Only show header when there are messages (conversation exists) */}
{messages.length > 0 &&
(isMobile && !isSidebarOpen ? (
(isLandscapeMobile && !isSidebarOpen ? (
<div className="z-10 flex shrink-0 items-center gap-2 bg-background px-1 py-1 pr-4">
<SidebarToggle onToggle={toggleSidebar} />
<div className="min-w-0 overflow-hidden">
<MapleWordmark className="h-4 w-auto max-w-full" aria-hidden />
</div>
<h1
className={`min-w-0 flex-1 truncate px-1 text-center text-base font-medium text-foreground transition-colors duration-300 ${
titleJustUpdated ? "title-update-animation" : ""
}`}
>
{conversation?.metadata?.title || "Chat"}
</h1>
<Button
variant="outline"
size="icon"
className="h-9 w-9 shrink-0 border-0"
onClick={handleNewChatFromHeader}
aria-label="New chat"
>
<SquarePen className="h-4 w-4" />
</Button>
</div>
) : isMobile && !isSidebarOpen ? (
<div className="z-10 flex shrink-0 flex-col gap-2 bg-background pb-2 pl-1 pr-4 pt-2">
<div className="flex items-center justify-between gap-3">
<div className="flex min-w-0 flex-1 items-center gap-1.5">
Expand Down Expand Up @@ -3352,7 +3384,7 @@ export function UnifiedChat() {
>
{/* Only show messages when there are messages */}
{messages.length > 0 && (
<div className="mx-auto w-full max-w-4xl p-4 md:p-6">
<div className="mx-auto w-full max-w-4xl p-4 md:p-6 landscape-short:p-2">
{/* Message list with modern ChatGPT/Claude style */}
<div className="space-y-1">
<MessageList
Expand Down Expand Up @@ -3384,13 +3416,13 @@ export function UnifiedChat() {
isFullscreenAnimating ? "transition-all duration-300" : ""
} ${isFullscreen ? "flex h-full max-w-6xl flex-col" : "max-w-4xl"}`}
>
{!isFullscreen && <div className="mb-16" />}
{!isFullscreen && <div className="mb-16 landscape-short:mb-4" />}

<div
className={`flex flex-col items-center gap-6 ${isFullscreen ? "flex-1 justify-center" : ""}`}
className={`flex flex-col items-center gap-6 landscape-short:gap-3 ${isFullscreen ? "flex-1 justify-center" : ""}`}
>
{!isFullscreen && (
<h1 className="mb-6 w-full overflow-visible pb-1 text-center font-displayWide text-4xl font-normal leading-tight brand-gradient-text sm:leading-relaxed">
<h1 className="mb-6 landscape-short:mb-2 w-full overflow-visible pb-1 text-center font-displayWide text-4xl landscape-short:text-2xl font-normal leading-tight brand-gradient-text sm:leading-relaxed">
Research anything...
</h1>
)}
Expand Down Expand Up @@ -3632,11 +3664,11 @@ export function UnifiedChat() {
) : (
// Fixed at bottom when there are messages
<div className="bg-background pb-[env(safe-area-inset-bottom)]">
<div className="mx-auto max-w-4xl px-4">
<div className="mx-auto max-w-4xl px-4 landscape-short:px-3">
<form onSubmit={handleSendMessage} className="relative">
<div className="space-y-2">
<div className="space-y-2 landscape-short:space-y-1">
{(draftImages.length > 0 || documentName) && (
<div className="space-y-2">
<div className="space-y-2 landscape-short:space-y-1">
{draftImages.length > 0 && (
<div className="flex flex-wrap gap-2">
{draftImages.map((file, i) => (
Expand Down Expand Up @@ -3688,12 +3720,12 @@ export function UnifiedChat() {
onKeyDown={handleKeyDown}
placeholder="Message Maple..."
disabled={isGenerating || isRecording}
className="w-full min-h-[52px] max-h-[200px] resize-none border-0 bg-transparent py-3.5 pl-4 pr-2 leading-6 focus-visible:ring-0 focus-visible:ring-offset-0 placeholder:text-muted-foreground/60"
className="w-full min-h-[52px] landscape-short:min-h-[40px] max-h-[200px] landscape-short:max-h-[100px] resize-none border-0 bg-transparent py-3.5 landscape-short:py-2 pl-4 pr-2 leading-6 focus-visible:ring-0 focus-visible:ring-offset-0 placeholder:text-muted-foreground/60"
rows={1}
id="message"
/>

<div className="grid grid-cols-[minmax(0,1fr)_auto] items-end gap-x-2 gap-y-2 px-2 pb-2 pt-1">
<div className="grid grid-cols-[minmax(0,1fr)_auto] items-end gap-x-2 gap-y-2 px-2 pb-2 landscape-short:pb-1.5 pt-1">
<div className="flex min-w-0 flex-wrap items-center gap-1.5 sm:gap-2">
<ModelSelector
hasImages={
Expand Down Expand Up @@ -3825,7 +3857,7 @@ export function UnifiedChat() {
</div>
</div>
</form>
<p className="mb-2 mt-1 text-center text-[10px] text-muted-foreground/50">
<p className="mb-2 mt-1 landscape-short:mb-1 text-center text-[10px] text-muted-foreground/50">
AI can make mistakes. Check important info.
</p>
</div>
Expand Down
16 changes: 13 additions & 3 deletions frontend/src/routes/_auth.chat.$chatId.tsx
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Markdown } from "@/components/markdown";
import { Sidebar, SidebarToggle } from "@/components/Sidebar";
import { Button } from "@/components/ui/button";
import { useNavigate } from "@tanstack/react-router";
import { useIsMobile } from "@/utils/utils";
import { useIsMobile, useIsLandscapeMobile } from "@/utils/utils";
import { useQuery } from "@tanstack/react-query";
import { SIDEBAR_GRID_COLUMNS_CLASS, SIDEBAR_LAYOUT_STYLE } from "@/constants/layout";

Expand Down Expand Up @@ -164,8 +164,18 @@ function ChatComponent() {
const { getChatById } = useLocalState();
const navigate = useNavigate();
const isMobile = useIsMobile();
const isLandscapeMobile = useIsLandscapeMobile();
const isCompactLayout = isMobile || isLandscapeMobile;
const [showScrollButton, setShowScrollButton] = useState(false);
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.

// Close sidebar when rotating to landscape on a short screen
useEffect(() => {
if (isLandscapeMobile && isSidebarOpen) {
setIsSidebarOpen(false);
}
}, [isLandscapeMobile]); // eslint-disable-line react-hooks/exhaustive-deps
Comment on lines +173 to +177
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 | ⚡ Quick win

Use isCompactLayout for sidebar auto-close on viewport transitions.

On Line 174, the close logic only watches isLandscapeMobile. That misses desktop-to-portrait-mobile transitions where compact behavior should also close/overlay the sidebar.

Proposed fix
-  useEffect(() => {
-    if (isLandscapeMobile && isSidebarOpen) {
-      setIsSidebarOpen(false);
-    }
-  }, [isLandscapeMobile]); // eslint-disable-line react-hooks/exhaustive-deps
+  useEffect(() => {
+    if (isCompactLayout && isSidebarOpen) {
+      setIsSidebarOpen(false);
+    }
+  }, [isCompactLayout, isSidebarOpen]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
if (isLandscapeMobile && isSidebarOpen) {
setIsSidebarOpen(false);
}
}, [isLandscapeMobile]); // eslint-disable-line react-hooks/exhaustive-deps
useEffect(() => {
if (isCompactLayout && isSidebarOpen) {
setIsSidebarOpen(false);
}
}, [isCompactLayout, isSidebarOpen]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/routes/_auth.chat`.$chatId.tsx around lines 173 - 177, The
sidebar auto-close effect currently only watches isLandscapeMobile; update the
useEffect that contains isLandscapeMobile to also consider isCompactLayout and
change the condition to close the sidebar when the layout becomes compact (e.g.,
if (isCompactLayout && isSidebarOpen) setIsSidebarOpen(false)). Modify the
dependency array to include isCompactLayout (and keep isLandscapeMobile if still
needed) so transitions like desktop→portrait-mobile trigger the overlay-close
behavior; reference the useEffect, isLandscapeMobile, isCompactLayout,
isSidebarOpen, and setIsSidebarOpen identifiers when making the change.


const chatContainerRef = useRef<HTMLDivElement>(null);

// Fetch chat from KV store
Expand Down Expand Up @@ -256,7 +266,7 @@ function ChatComponent() {
<Sidebar chatId={chatId} isOpen={isSidebarOpen} onToggle={toggleSidebar} />
<main className="flex h-dvh flex-col bg-card/90 backdrop-blur-lg bg-center overflow-hidden">
{!isSidebarOpen && (
<div className="fixed top-4 left-4 z-20 md:hidden">
<div className="fixed top-4 left-4 z-20 md:hidden landscape-short:block">
<SidebarToggle onToggle={toggleSidebar} />
</div>
)}
Expand All @@ -265,7 +275,7 @@ function ChatComponent() {
className="flex-1 min-h-0 overflow-y-auto overflow-x-hidden flex flex-col relative"
>
<div className="mt-4 md:mt-8 w-full h-10 flex items-center justify-center relative">
{isMobile && (
{isCompactLayout && (
<Button
variant="outline"
size="icon"
Expand Down
Loading
Loading