feat(composer): update ComposerAction to include searchSpaceId and integrate ChatHeader component

This commit is contained in:
Anish Sarkar 2026-06-13 03:28:54 +05:30
parent 8fe9c21e76
commit 78dbdc454a
3 changed files with 47 additions and 27 deletions

View file

@ -68,6 +68,7 @@ import {
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
import { UserMessage } from "@/components/assistant-ui/user-message";
import { ChatExamplePrompts } from "@/components/new-chat/chat-example-prompts";
import { ChatHeader } from "@/components/new-chat/chat-header";
import { ComposerSuggestionPopoverContent } from "@/components/new-chat/composer-suggestion-popup";
import { PromptPicker, type PromptPickerRef } from "@/components/new-chat/prompt-picker";
import { Avatar, AvatarFallback, AvatarGroup } from "@/components/ui/avatar";
@ -931,7 +932,10 @@ const Composer: FC = () => {
className="min-h-[48px] sm:min-h-[24px] **:data-slate-placeholder:font-normal"
/>
</div>
<ComposerAction isBlockedByOtherUser={isBlockedByOtherUser} />
<ComposerAction
isBlockedByOtherUser={isBlockedByOtherUser}
searchSpaceId={Number(search_space_id)}
/>
<ConnectorIndicator showTrigger={false} />
</div>
<ConnectToolsBanner
@ -950,9 +954,10 @@ const Composer: FC = () => {
interface ComposerActionProps {
isBlockedByOtherUser?: boolean;
searchSpaceId: number;
}
const ComposerAction: FC<ComposerActionProps> = ({ isBlockedByOtherUser = false }) => {
const ComposerAction: FC<ComposerActionProps> = ({ isBlockedByOtherUser = false, searchSpaceId }) => {
const mentionedDocuments = useAtomValue(mentionedDocumentsAtom);
const setConnectorDialogOpen = useSetAtom(connectorDialogOpenAtom);
const [toolsPopoverOpen, setToolsPopoverOpen] = useState(false);
@ -1562,6 +1567,10 @@ const ComposerAction: FC<ComposerActionProps> = ({ isBlockedByOtherUser = false
</div>
)}
<div className="flex items-center gap-2">
<ChatHeader
searchSpaceId={searchSpaceId}
className="h-9 max-w-[44vw] px-2 sm:max-w-[220px] sm:px-3"
/>
<AuiIf condition={({ thread }) => !thread.isRunning}>
<ComposerPrimitive.Send asChild disabled={isSendDisabled}>
<TooltipIconButton
@ -1569,7 +1578,7 @@ const ComposerAction: FC<ComposerActionProps> = ({ isBlockedByOtherUser = false
isBlockedByOtherUser
? "Wait for AI to finish responding"
: !hasModelConfigured
? "Please select a model from the header to start chatting"
? "Please select a model to start chatting"
: isComposerEmpty
? "Enter a message or add a screenshot to send"
: "Send message"

View file

@ -6,7 +6,6 @@ import { currentThreadAtom } from "@/atoms/chat/current-thread.atom";
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
import { activeTabAtom } from "@/atoms/tabs/tabs.atom";
import { ActionLogButton } from "@/components/agent-action-log/action-log-button";
import { ChatHeader } from "@/components/new-chat/chat-header";
import { ChatShareButton } from "@/components/new-chat/chat-share-button";
import type { ThreadRecord } from "@/lib/chat/thread-persistence";
@ -66,12 +65,9 @@ export function Header({ mobileMenuTrigger }: HeaderProps) {
return (
<header className="sticky top-0 z-10 flex h-14 shrink-0 items-center gap-2 bg-main-panel/95 backdrop-blur supports-backdrop-filter:bg-main-panel/60 px-4">
{/* Left side - Mobile menu trigger + Model selector */}
{/* Left side - Mobile menu trigger */}
<div className="flex flex-1 items-center gap-2 min-w-0">
{mobileMenuTrigger}
{isChatPage && !isDocumentTab && searchSpaceId && (
<ChatHeader searchSpaceId={Number(searchSpaceId)} className="md:h-9 md:px-4 md:text-sm" />
)}
</div>
{/* Right side - Actions */}

View file

@ -14,6 +14,7 @@ import { Button } from "@/components/ui/button";
import {
Drawer,
DrawerContent,
DrawerHandle,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
@ -127,23 +128,28 @@ export function ModelSelector({
setOpen(false);
}
function manageModelConnections() {
setOpen(false);
onAddNewLLM();
}
const content = (
<div className="flex max-h-[min(520px,80vh)] flex-col">
<div className="border-b p-3">
<div className="flex max-h-[min(520px,80vh)] flex-col overflow-hidden">
<div className="p-2">
<div className="relative">
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
<Search className="absolute left-0.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
<Input
value={search}
onChange={(event) => setSearch(event.target.value)}
placeholder="Search chat models..."
className="pl-9"
placeholder="Search chat models"
className="h-8 border-0 bg-transparent pl-6 text-sm shadow-none"
/>
</div>
</div>
<div className="overflow-y-auto p-2">
<div className="overflow-y-auto overflow-x-hidden px-1.5 py-1.5">
<button
type="button"
className="flex w-full items-center justify-between rounded-md px-3 py-2 text-left hover:bg-accent"
className="flex w-full items-center justify-between rounded-md px-3 py-2 text-left transition-colors hover:bg-accent hover:text-accent-foreground"
onClick={() => selectModel(0)}
>
<div className="flex items-center gap-3">
@ -175,7 +181,7 @@ export function ModelSelector({
<button
type="button"
key={model.id}
className="flex w-full items-center justify-between rounded-md px-3 py-2 text-left hover:bg-accent"
className="flex w-full items-center justify-between rounded-md px-3 py-2 text-left transition-colors hover:bg-accent hover:text-accent-foreground"
onClick={() => selectModel(model.id)}
>
<div className="min-w-0">
@ -183,7 +189,6 @@ export function ModelSelector({
{getProviderIcon(model.provider, { className: "size-4 shrink-0" })}
<span className="truncate">{modelName(model)}</span>
</div>
<div className="truncate text-xs text-muted-foreground">{model.model_id}</div>
{model.max_input_tokens ? (
<div className="text-xs text-muted-foreground">
{model.max_input_tokens.toLocaleString()} context
@ -204,9 +209,13 @@ export function ModelSelector({
))
)}
</div>
<div className="border-t p-3">
<Button variant="outline" className="w-full justify-start" onClick={() => onAddNewLLM()}>
<Settings2 className="mr-2 h-4 w-4" /> Manage model connections
<div className="p-2">
<Button
variant="ghost"
className="w-full justify-start rounded-md bg-foreground/5 hover:bg-foreground/10 hover:text-foreground"
onClick={manageModelConnections}
>
<Settings2 className="mr-2 h-4 w-4" /> Manage models
</Button>
</div>
</div>
@ -217,17 +226,22 @@ export function ModelSelector({
type="button"
variant="ghost"
size="sm"
className={cn("h-8 gap-2 rounded-full px-3 text-muted-foreground", className)}
className={cn(
"h-8 min-w-0 gap-2 rounded-md px-3 text-muted-foreground transition-colors",
"hover:bg-foreground/10 hover:text-foreground",
"data-[state=open]:bg-foreground/10 data-[state=open]:text-foreground",
className
)}
>
{selected ? (
getProviderIcon(selected.provider, { className: "size-4" })
getProviderIcon(selected.provider, { className: "size-4 shrink-0" })
) : (
<Cpu className="h-4 w-4" />
<Cpu className="h-4 w-4 shrink-0" />
)}
<span className="max-w-[180px] truncate text-sm">
<span className="min-w-0 flex-1 truncate text-sm">
{selected ? modelName(selected) : "Auto"}
</span>
<ChevronDown className="h-3.5 w-3.5" />
<ChevronDown className="h-3.5 w-3.5 shrink-0" />
</Button>
);
@ -235,7 +249,8 @@ export function ModelSelector({
return (
<Drawer open={open} onOpenChange={setOpen}>
<DrawerTrigger asChild>{trigger}</DrawerTrigger>
<DrawerContent>
<DrawerContent className="max-h-[85vh]">
<DrawerHandle />
<DrawerHeader>
<DrawerTitle>Select Chat Model</DrawerTitle>
</DrawerHeader>
@ -248,7 +263,7 @@ export function ModelSelector({
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>{trigger}</PopoverTrigger>
<PopoverContent align="start" className="w-[420px] p-0">
<PopoverContent align="start" className="w-[360px] p-0">
{content}
</PopoverContent>
</Popover>