feat: ChatGPT/Claude-style persistent sidebar for desktop fullscreen chat (v0.57.0)

Cooking assistant's fullscreen mode gets a real left sidebar (always-
visible conversation list, hover-reveal rename/delete) on desktop
(md+), replacing the small dropdown menu that made sense in the narrow
420px drawer but not in a full-viewport layout. Mobile fullscreen and
the normal drawer keep the dropdown (ConversationMenu) — no room for a
persistent sidebar there.

Extracted the shared fetch/rename/delete/create logic into
use-conversation-list.ts so the dropdown (ConversationMenu) and the new
sidebar (ConversationSidebar) can't silently diverge — both are thin
render layers over the same hook. ConversationMenu gained an optional
className prop so the panel can hide it with `md:hidden` specifically
when the sidebar is already covering that job.

Per-recipe chat is untouched — it's single-threaded by design and never
used either of these components.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Arnaud
2026-07-20 09:04:07 +02:00
parent 2f18462548
commit 1ee3ec70ba
8 changed files with 326 additions and 80 deletions
@@ -12,6 +12,7 @@ import ReactMarkdown from "react-markdown";
import { cn } from "@/lib/utils";
import { ChatHistorySearch } from "./chat-history-search";
import { ConversationMenu, type Conversation } from "./conversation-menu";
import { ConversationSidebar } from "./conversation-sidebar";
import { ShoppingListProposalCard, type ShoppingListProposal } from "./shopping-list-proposal-card";
type RecipeProposal = {
@@ -224,10 +225,20 @@ export function CookingAssistantPanel() {
<SheetContent
side="right"
className={cn(
"flex flex-col p-0",
fullscreen ? "!w-screen !max-w-none !h-screen !inset-0" : "w-full sm:w-[420px]"
"p-0",
fullscreen ? "!w-screen !max-w-none !h-screen !inset-0 flex flex-row" : "flex flex-col w-full sm:w-[420px]"
)}
>
{fullscreen && (
<ConversationSidebar
className="hidden md:flex w-64 shrink-0"
activeId={conversationId}
onSelect={handleSelectConversation}
onCreated={handleConversationCreated}
onDeletedActive={handleActiveConversationDeleted}
/>
)}
<div className="flex flex-col flex-1 min-w-0 h-full">
<SheetHeader className="px-4 py-3 pr-10 border-b shrink-0">
<div className="flex items-center justify-between gap-2">
<SheetTitle className="text-base flex items-center gap-2">
@@ -244,7 +255,11 @@ export function CookingAssistantPanel() {
{fullscreen ? <Minimize2 className="h-4 w-4" /> : <Maximize2 className="h-4 w-4" />}
</button>
<ChatHistorySearch />
{/* On desktop fullscreen, ConversationSidebar (left) replaces this
dropdown — kept for mobile fullscreen and the normal drawer,
where there's no room for a persistent sidebar. */}
<ConversationMenu
className={cn(fullscreen && "md:hidden")}
activeId={conversationId}
onSelect={handleSelectConversation}
onCreated={handleConversationCreated}
@@ -389,6 +404,7 @@ export function CookingAssistantPanel() {
<Send className="h-4 w-4" />
</Button>
</form>
</div>
</SheetContent>
</Sheet>
</>