@iloveagents/foundry-web-ui 0.32.2 → 0.32.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -115,7 +115,7 @@ const DropZone = ({ children, centered, }) => {
115
115
  : `Couldn't attach ${rejected.length} files (${rejected.join("; ")})`);
116
116
  }
117
117
  }, [aui, showDropError]);
118
- return (_jsxs("div", { className: cn("relative flex flex-col flex-1", centered ? "justify-center overflow-y-auto" : "overflow-hidden"), onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDragOver: handleDragOver, onDrop: handleDrop, children: [children, isDragging && (_jsx("div", { className: cn("absolute inset-0 z-50 flex items-center justify-center pointer-events-none", "bg-background/80 backdrop-blur-sm", "border-2 border-dashed border-primary rounded-xl"), children: _jsx("p", { className: "text-lg text-primary font-medium", children: "Drop files here" }) })), dropError && (_jsx("div", { role: "alert", className: cn("absolute bottom-4 left-1/2 -translate-x-1/2 z-50 max-w-[90%]", "rounded-md border border-destructive/50 bg-background px-3 py-2", "text-sm text-destructive shadow-md pointer-events-none truncate"), children: dropError }))] }));
118
+ return (_jsxs("div", { className: cn("relative flex flex-col flex-1", centered ? "overflow-y-auto" : "overflow-hidden"), onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDragOver: handleDragOver, onDrop: handleDrop, children: [children, isDragging && (_jsx("div", { className: cn("absolute inset-0 z-50 flex items-center justify-center pointer-events-none", "bg-background/80 backdrop-blur-sm", "border-2 border-dashed border-primary rounded-xl"), children: _jsx("p", { className: "text-lg text-primary font-medium", children: "Drop files here" }) })), dropError && (_jsx("div", { role: "alert", className: cn("absolute bottom-4 left-1/2 -translate-x-1/2 z-50 max-w-[90%]", "rounded-md border border-destructive/50 bg-background px-3 py-2", "text-sm text-destructive shadow-md pointer-events-none truncate"), children: dropError }))] }));
119
119
  };
120
120
  /**
121
121
  * "Message Assistant..." named the machine and told the user nothing. The
@@ -142,11 +142,11 @@ export function ChatContent({ centerComposer = false, composerIntro, composerFoo
142
142
  UserMessage,
143
143
  EditComposer,
144
144
  AssistantMessage,
145
- } }), _jsx(ThreadPrimitive.If, { running: true, children: _jsx(LoadingIndicator, {}) })] }) })] }), !ThreadSurface && (_jsx(ThreadPrimitive.If, { empty: false, children: _jsx(ScrollToBottomButton, {}) }))] }), _jsxs("div", { className: "shrink-0 bg-background", hidden: hideComposer, children: [composerIntro && !ThreadSurface && (_jsx("div", { className: "mx-auto max-w-3xl px-4 pt-4", children: composerIntro })), _jsx(ChatComposerBannerSlot, {}), _jsx(ThreadPrimitive.If, { running: false, children: _jsx("div", { "data-chat-composer-divider": true, className: "border-t border-border" }) }), _jsx(ThreadPrimitive.If, { running: true, children: _jsx(LoadingBar, {}) }), _jsxs("div", { className: "mx-auto max-w-3xl px-4 py-4", children: [_jsx(ChatContext, {}), _jsxs(ComposerPrimitive.Root, { style: compactComposer ? { borderRadius: 16 } : undefined, className: cn(
145
+ } }), _jsx(ThreadPrimitive.If, { running: true, children: _jsx(LoadingIndicator, {}) })] }) })] }), !ThreadSurface && (_jsx(ThreadPrimitive.If, { empty: false, children: _jsx(ScrollToBottomButton, {}) }))] }), _jsxs("div", { className: cn("shrink-0 bg-background", centerComposer && !ThreadSurface && "my-auto"), hidden: hideComposer, children: [composerIntro && !ThreadSurface && (_jsx("div", { className: "mx-auto max-w-3xl px-4 pt-4", children: composerIntro })), _jsx(ChatComposerBannerSlot, {}), _jsx(ThreadPrimitive.If, { running: false, children: _jsx("div", { "data-chat-composer-divider": true, className: "border-t border-border" }) }), _jsx(ThreadPrimitive.If, { running: true, children: _jsx(LoadingBar, {}) }), _jsxs("div", { className: "mx-auto max-w-3xl px-4 py-4", children: [_jsx(ChatContext, { centered: centerComposer && !ThreadSurface }), _jsxs(ComposerPrimitive.Root, { style: compactComposer ? { borderRadius: 16 } : undefined, className: cn(
146
146
  // Query container for the controls inside, so they size
147
147
  // against the composer rather than the viewport. This one
148
148
  // is wide, so the effort picker keeps its full label.
149
- "@container", "flex flex-col gap-2", "rounded-3xl border border-input", "bg-background shadow-sm", "px-4 py-2", "focus-within:border-primary/50 focus-within:shadow-md", "transition-all duration-300"), children: [_jsx(ComposerSubmitBridge, {}), _jsx(ComposerPrimitive.Attachments, { components: composerAttachmentComponents }), _jsxs("div", { className: cn("flex items-center gap-2", compactComposer ? "flex-wrap" : "flex-wrap @md:flex-nowrap"), children: [_jsx("div", { className: cn("flex shrink-0 items-center gap-1", compactComposer ? "order-2" : "order-2 @md:order-1"), children: _jsx(ComposerAddMenu, {}) }), _jsx(ComposerInput, { autoFocus: true, placeholder: placeholder, rows: 1, className: cn("bg-transparent", compactComposer
149
+ "@container", "flex flex-col gap-2", "rounded-3xl border border-input", "bg-background shadow-sm", "px-4 py-2", "focus-within:border-primary/50 focus-within:shadow-md", centerComposer && "shadow-md", "transition-all duration-300"), children: [_jsx(ComposerSubmitBridge, {}), _jsx(ComposerPrimitive.Attachments, { components: composerAttachmentComponents }), _jsxs("div", { className: cn("flex items-center gap-2", compactComposer ? "flex-wrap" : "flex-wrap @md:flex-nowrap"), children: [_jsx("div", { className: cn("flex shrink-0 items-center gap-1", compactComposer ? "order-2" : "order-2 @md:order-1"), children: _jsx(ComposerAddMenu, {}) }), _jsx(ComposerInput, { autoFocus: true, placeholder: placeholder, rows: 1, className: cn("bg-transparent", compactComposer
150
150
  ? "order-1 w-full"
151
- : "order-1 w-full @md:order-2 @md:w-auto @md:flex-1", "py-2 text-base sm:text-sm leading-relaxed", "text-foreground outline-none", "placeholder:text-muted-foreground", "resize-none max-h-32") }), _jsx("div", { className: cn("order-3 ml-auto flex shrink-0 items-center gap-2", !compactComposer && "@md:ml-0"), children: _jsx(ComposerActionButton, {}) })] })] }), composerFooter] })] })] })] }));
151
+ : "order-1 w-full @md:order-2 @md:w-auto @md:flex-1", "py-2 text-base sm:text-sm leading-relaxed", "text-foreground outline-none", "placeholder:text-muted-foreground", "resize-none max-h-32") }), _jsx("div", { className: cn("order-3 ml-auto flex shrink-0 items-center gap-2", !compactComposer && "@md:ml-0"), children: _jsx(ComposerActionButton, {}) })] })] }), !ThreadSurface && composerFooter] })] })] })] }));
152
152
  }
@@ -106,7 +106,7 @@ export function ChatBubble() {
106
106
  height: 64,
107
107
  } })), !isOpen && (_jsxs("div", { className: "fixed z-50 group", style: { left: CHAT_BUBBLE_INSET, bottom: CHAT_BUBBLE_INSET }, children: [_jsx("button", { type: "button", onClick: openBubble, className: cn("size-10 rounded-full", "bg-primary text-primary-foreground shadow-lg", "flex items-center justify-center", "hover:bg-primary/90 transition-all duration-200", "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"), "aria-label": "Ask the AI", children: _jsx(Sparkles, { className: "size-4" }) }), _jsx(ChatLauncherBadgeSlot, {})] })), isOpen && (_jsxs("div", { ref: panelRef, "data-chat-surface": "bubble", className: cn("fixed z-50", "rounded-2xl border border-border", "bg-background shadow-2xl", "flex flex-col overflow-hidden", "w-95 h-150"), style: dragPos
108
108
  ? { left: dragPos.x, top: dragPos.y }
109
- : { left: CHAT_BUBBLE_INSET, bottom: CHAT_BUBBLE_INSET }, children: [_jsxs("div", { className: cn("flex items-center justify-between px-4 py-2.5 border-b border-border shrink-0", "cursor-grab active:cursor-grabbing select-none"), style: { touchAction: "none" }, onPointerDown: onHeaderPointerDown, onPointerMove: onHeaderPointerMove, onPointerUp: onHeaderPointerUp, onLostPointerCapture: onHeaderLostPointerCapture, children: [_jsxs("div", { className: "flex flex-col min-w-0", children: [_jsxs("div", { className: "flex items-center gap-1.5 text-sm", children: [_jsx(Sparkles, { className: "size-3.5 text-primary shrink-0" }), _jsx("span", { className: "font-semibold text-foreground truncate", children: pageLabel })] }), _jsx("span", { className: "text-xs text-muted-foreground", children: threadActive ? "Active thread" : "New conversation" })] }), _jsxs("div", { className: "flex items-center gap-0.5", children: [_jsx(TooltipIconButton, { tooltip: "New Thread", size: "icon", className: "size-7", onClick: startNewThread, children: _jsx(SquarePen, { className: "size-3.5" }) }), _jsx(TooltipIconButton, { tooltip: "Expand chat", size: "icon", className: "size-7", onClick: expandChat, children: _jsx(Maximize2, { className: "size-3.5" }) }), _jsx(TooltipIconButton, { tooltip: "Close", size: "icon", className: "size-7", onClick: handleClose, children: _jsx(X, { className: "size-4" }) })] })] }), _jsxs(ThreadPrimitive.Root, { className: "flex flex-col flex-1 overflow-hidden", children: [_jsxs("div", { className: "relative flex-1 overflow-hidden", children: [ThreadSurface && (_jsx("div", { className: "absolute inset-0 z-10 overflow-hidden bg-background", children: _jsx(ThreadSurface, {}) })), _jsxs(ThreadPrimitive.Viewport, { className: cn("absolute inset-0 overflow-y-auto", ThreadSurface && "invisible"), "aria-hidden": ThreadSurface ? true : undefined, children: [_jsx(ThreadPrimitive.Empty, { children: _jsxs("div", { className: "flex min-h-full flex-col items-center justify-center gap-4 p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground text-center", children: "Ask the agent about this page" }), _jsx(ChatHomeStartersSlot, {})] }) }), _jsx(ThreadPrimitive.If, { empty: false, children: _jsxs("div", { className: "px-4 pt-4 pb-3", children: [_jsx(ThreadPrimitive.Messages, { components: {
109
+ : { left: CHAT_BUBBLE_INSET, bottom: CHAT_BUBBLE_INSET }, children: [_jsxs("div", { className: cn("flex items-center justify-between px-4 py-2.5 border-b border-border shrink-0", "cursor-grab active:cursor-grabbing select-none"), style: { touchAction: "none" }, onPointerDown: onHeaderPointerDown, onPointerMove: onHeaderPointerMove, onPointerUp: onHeaderPointerUp, onLostPointerCapture: onHeaderLostPointerCapture, children: [_jsxs("div", { className: "flex flex-col min-w-0", children: [_jsxs("div", { className: "flex items-center gap-1.5 text-sm", children: [_jsx(Sparkles, { className: "size-3.5 text-primary shrink-0" }), _jsx("span", { className: "font-semibold text-foreground truncate", children: pageLabel })] }), _jsx("span", { className: "text-xs text-muted-foreground", children: threadActive ? "Active chat" : "New chat" })] }), _jsxs("div", { className: "flex items-center gap-0.5", children: [_jsx(TooltipIconButton, { tooltip: "New chat", size: "icon", className: "size-7", onClick: startNewThread, children: _jsx(SquarePen, { className: "size-3.5" }) }), _jsx(TooltipIconButton, { tooltip: "Expand chat", size: "icon", className: "size-7", onClick: expandChat, children: _jsx(Maximize2, { className: "size-3.5" }) }), _jsx(TooltipIconButton, { tooltip: "Close", size: "icon", className: "size-7", onClick: handleClose, children: _jsx(X, { className: "size-4" }) })] })] }), _jsxs(ThreadPrimitive.Root, { className: "flex flex-col flex-1 overflow-hidden", children: [_jsxs("div", { className: "relative flex-1 overflow-hidden", children: [ThreadSurface && (_jsx("div", { className: "absolute inset-0 z-10 overflow-hidden bg-background", children: _jsx(ThreadSurface, {}) })), _jsxs(ThreadPrimitive.Viewport, { className: cn("absolute inset-0 overflow-y-auto", ThreadSurface && "invisible"), "aria-hidden": ThreadSurface ? true : undefined, children: [_jsx(ThreadPrimitive.Empty, { children: _jsxs("div", { className: "flex min-h-full flex-col items-center justify-center gap-4 p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground text-center", children: "Ask the agent about this page" }), _jsx(ChatHomeStartersSlot, {})] }) }), _jsx(ThreadPrimitive.If, { empty: false, children: _jsxs("div", { className: "px-4 pt-4 pb-3", children: [_jsx(ThreadPrimitive.Messages, { components: {
110
110
  UserMessage: BubbleUserMessage,
111
111
  AssistantMessage: BubbleAssistantMessage,
112
112
  } }), _jsx(ThreadPrimitive.If, { running: true, children: _jsx(LoadingIndicator, {}) })] }) })] }), _jsx(ThreadPrimitive.If, { empty: false, children: !ThreadSurface && _jsx(BubbleScrollToBottomButton, {}) })] }), _jsxs("div", { className: "shrink-0 border-t border-border", hidden: hideComposer, children: [_jsx(ChatComposerBannerSlot, {}), _jsxs("div", { className: "px-3 py-3", children: [_jsx(ChatContext, {}), _jsx(ComposerPrimitive.Root, { className: cn(
@@ -1,2 +1,4 @@
1
1
  /** The displayed context and the agent request use the same projection. */
2
- export declare function ChatContext(): import("react/jsx-runtime").JSX.Element;
2
+ export declare function ChatContext({ centered }: {
3
+ centered?: boolean;
4
+ }): import("react/jsx-runtime").JSX.Element;
@@ -12,7 +12,7 @@ function readContext() {
12
12
  return summarizeChatContext(useAppStore.getState().getAgentState());
13
13
  }
14
14
  /** The displayed context and the agent request use the same projection. */
15
- export function ChatContext() {
15
+ export function ChatContext({ centered = false }) {
16
16
  const [open, setOpen] = useState(false);
17
17
  const [context, setContext] = useState(readContext);
18
18
  const items = useAppStore((s) => s.contextItems);
@@ -74,7 +74,7 @@ export function ChatContext() {
74
74
  };
75
75
  }, []);
76
76
  const Icon = context.field ? TextCursorInput : context.focused ? Maximize2 : FileText;
77
- return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", "data-chat-context": true, "aria-label": `Message context: ${context.label}${detail ? `, ${detail}` : ""}${additionalCount ? `, ${additionalCount} more` : ""}`, className: "mb-2 flex min-h-9 w-full min-w-0 items-center gap-2 rounded-lg px-2 py-1.5 text-left text-xs text-muted-foreground hover:bg-muted hover:text-foreground focus-visible:outline-2 focus-visible:outline-primary [@media(pointer:coarse)]:min-h-11", children: [_jsx(Icon, { className: "size-3.5 shrink-0" }), _jsxs("span", { className: "min-w-0 flex-1 truncate", "aria-live": "polite", children: ["With ", _jsx("span", { className: "font-medium text-foreground", children: context.label }), detail && (_jsxs(_Fragment, { children: [" ", _jsx("span", { "aria-hidden": true, children: "\u203A" }), " ", _jsx("span", { className: "font-medium text-foreground", children: detail })] }))] }), additionalCount > 0 && (_jsxs("span", { className: "shrink-0 tabular-nums", "aria-hidden": true, children: ["+", additionalCount] })), _jsx(ChevronDown, { className: "size-3.5 shrink-0" })] }) }), _jsxs(PopoverContent, { align: "start", side: "top", collisionPadding: 12, "aria-label": "Context and actions", className: "w-[22rem] max-w-[calc(100vw-1.5rem)] max-h-[min(36rem,var(--radix-popover-content-available-height))] overflow-y-auto overscroll-contain space-y-3 p-3 text-sm", children: [_jsxs("div", { className: "px-2", children: [_jsx("h2", { className: "text-xs font-medium text-muted-foreground", children: "Current context" }), _jsxs("p", { className: "mt-1 break-words font-medium", children: [context.label, detail && ` › ${detail}`] })] }), _jsx("p", { className: "px-2 text-xs leading-relaxed text-muted-foreground", children: detail
77
+ return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", "data-chat-context": true, "aria-label": `Message context: ${context.label}${detail ? `, ${detail}` : ""}${additionalCount ? `, ${additionalCount} more` : ""}`, className: `mb-2 flex min-h-9 min-w-0 items-center gap-2 rounded-lg px-2 py-1.5 text-left text-xs text-muted-foreground hover:bg-muted hover:text-foreground focus-visible:outline-2 focus-visible:outline-primary [@media(pointer:coarse)]:min-h-11 ${centered ? "mx-auto w-fit max-w-full" : "w-full"}`, children: [_jsx(Icon, { className: "size-3.5 shrink-0" }), _jsxs("span", { className: "min-w-0 flex-1 truncate", "aria-live": "polite", children: ["With ", _jsx("span", { className: "font-medium text-foreground", children: context.label }), detail && (_jsxs(_Fragment, { children: [" ", _jsx("span", { "aria-hidden": true, children: "\u203A" }), " ", _jsx("span", { className: "font-medium text-foreground", children: detail })] }))] }), additionalCount > 0 && (_jsxs("span", { className: "shrink-0 tabular-nums", "aria-hidden": true, children: ["+", additionalCount] })), _jsx(ChevronDown, { className: "size-3.5 shrink-0" })] }) }), _jsxs(PopoverContent, { align: centered ? "center" : "start", side: "top", collisionPadding: 12, "aria-label": "Context and actions", className: "w-[22rem] max-w-[calc(100vw-1.5rem)] max-h-[min(36rem,var(--radix-popover-content-available-height))] overflow-y-auto overscroll-contain space-y-3 p-3 text-sm", children: [_jsxs("div", { className: "px-2", children: [_jsx("h2", { className: "text-xs font-medium text-muted-foreground", children: "Current context" }), _jsxs("p", { className: "mt-1 break-words font-medium", children: [context.label, detail && ` › ${detail}`] })] }), _jsx("p", { className: "px-2 text-xs leading-relaxed text-muted-foreground", children: detail
78
78
  ? "This selection stays available when you move into chat."
79
79
  : "The current page follows your navigation. Select text or a field to work more specifically." }), context.selectedText && (_jsx("blockquote", { className: "max-h-32 overflow-y-auto border-l-2 border-border pl-3 text-xs", children: context.selectedText })), !context.focused && context.label !== "Your spaces" && (_jsxs("button", { type: "button", "aria-pressed": isPinned, onClick: toggle, className: "flex min-h-9 w-full items-center gap-2 rounded-lg px-2 text-left text-sm hover:bg-muted", children: [_jsx(PinStateIcon, { pinned: isPinned, className: "size-4 shrink-0 text-muted-foreground" }), isPinned ? "Unpin this page" : "Keep this page in context"] })), _jsx(ChatContextItems, { items: items, onNavigate: () => setOpen(false) }), _jsx(ChatContextDetailsSlot, { onDismiss: () => setOpen(false) }), context.references.length > 0 && (_jsxs("div", { className: "border-t border-border px-2 pt-3 text-xs", children: [_jsx("h3", { className: "font-medium", children: "Also open" }), _jsx("ul", { className: "mt-1 space-y-1 text-muted-foreground", children: context.references.map((label, index) => (_jsx("li", { className: "truncate", children: label }, `${label}-${index}`))) })] }))] })] }));
80
80
  }
@@ -2,5 +2,5 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
2
2
  import { ChatHomeStartersSlot } from "./chat-slots.js";
3
3
  /** One compact conversation home for workbench, focus and embedded chat panes. */
4
4
  export function ChatEmptyState({ pageLabel, children, showHomeStarters = true, homeStarterPriority = 0, }) {
5
- return (_jsxs("div", { "data-chat-empty-state": true, className: "flex min-h-full flex-col justify-start gap-3 px-4 pb-8 pt-3", children: [_jsx("h1", { className: "text-base leading-6 font-semibold", children: "What would you like to get done?" }), _jsx("p", { className: "max-w-sm text-sm leading-relaxed text-muted-foreground", children: pageLabel && !pageLabel.startsWith("/") ? (_jsxs(_Fragment, { children: ["Work with ", _jsx("span", { className: "font-medium text-foreground", children: pageLabel }), " or across your spaces: find information, create or edit content, and run actions."] })) : ("Find information, create or edit content, and run actions across your spaces.") }), _jsx("p", { className: "max-w-sm text-xs leading-relaxed text-muted-foreground", children: "Your conversation continues as you browse. Select text or a field to work more specifically." }), showHomeStarters && _jsx(ChatHomeStartersSlot, { priority: homeStarterPriority }), children] }));
5
+ return (_jsxs("div", { "data-chat-empty-state": true, className: "flex min-h-full flex-col justify-start gap-3 px-4 pb-8 pt-3", children: [_jsx("h1", { className: "text-base leading-6 font-semibold", children: "What would you like to get done?" }), _jsx("p", { className: "max-w-sm text-sm leading-relaxed text-muted-foreground", children: pageLabel && !pageLabel.startsWith("/") ? (_jsxs(_Fragment, { children: ["Work with ", _jsx("span", { className: "font-medium text-foreground", children: pageLabel }), " or across your spaces: find information, create or edit content, and run actions."] })) : ("Find information, create or edit content, and run actions across your spaces.") }), _jsx("p", { className: "max-w-sm text-xs leading-relaxed text-muted-foreground", children: "Your chat continues as you browse. Select text or a field to work more specifically." }), showHomeStarters && _jsx(ChatHomeStartersSlot, { priority: homeStarterPriority }), children] }));
6
6
  }
@@ -20,5 +20,5 @@ export const ChatHeader = () => {
20
20
  const handleNewConversation = useNewConversation(isExpanded ? { navigateToChat: false, preserveNavigation: true } : undefined);
21
21
  const toggleMobile = useSidebarStore((s) => s.toggleMobile);
22
22
  const isSidebarOpen = useSidebarStore((s) => s.isOpen);
23
- return (_jsx("div", { className: cn("shrink-0", "bg-background border-b border-border"), children: _jsxs("div", { className: "px-4 py-3 flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(TooltipIconButton, { tooltip: "Open sidebar", onClick: toggleMobile, className: "md:hidden", children: _jsx(Menu, { className: "size-5" }) }), _jsx(Link, { to: "/", className: cn("flex items-center gap-2.5 hover:opacity-80 transition-opacity", isSidebarOpen && "md:hidden"), children: _jsx(AppBrand, { labelClassName: "text-sm font-semibold text-foreground" }) })] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(RunningChatsMenu, {}), _jsx(ThemeToggle, {}), _jsx(TooltipIconButton, { tooltip: "New Thread", size: "icon", onClick: handleNewConversation, children: _jsx(SquarePen, { className: "size-4 text-primary" }) }), _jsx(UserMenu, {})] })] }) }));
23
+ return (_jsx("div", { className: cn("shrink-0", "bg-background border-b border-border"), children: _jsxs("div", { className: "px-4 py-3 flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(TooltipIconButton, { tooltip: "Open sidebar", onClick: toggleMobile, className: "md:hidden", children: _jsx(Menu, { className: "size-5" }) }), _jsx(Link, { to: "/", className: cn("flex items-center gap-2.5 hover:opacity-80 transition-opacity", isSidebarOpen && "md:hidden"), children: _jsx(AppBrand, { labelClassName: "text-sm font-semibold text-foreground" }) })] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(RunningChatsMenu, {}), _jsx(ThemeToggle, {}), _jsx(TooltipIconButton, { tooltip: "New chat", size: "icon", onClick: handleNewConversation, children: _jsx(SquarePen, { className: "size-4 text-primary" }) }), _jsx(UserMenu, {})] })] }) }));
24
24
  };
@@ -1008,7 +1008,7 @@ function CreateActionsMenu({ group }) {
1008
1008
  action.handler();
1009
1009
  }, children: action.label }, action.id))) })] }));
1010
1010
  }
1011
- function SidebarContent({ collapsed, groups, onNewConversation, hideFooterActions, hideNewConversation, collapsedGroupsOnly, newConversationAfterGroup, newConversationLabel = "New Thread", }) {
1011
+ function SidebarContent({ collapsed, groups, onNewConversation, hideFooterActions, hideNewConversation, collapsedGroupsOnly, newConversationAfterGroup, newConversationLabel = "New chat", }) {
1012
1012
  const toggle = useSidebarStore((s) => s.toggle);
1013
1013
  const defaultNewConversation = useNewConversation();
1014
1014
  const handleNewConversation = onNewConversation ?? defaultNewConversation;
@@ -39,7 +39,7 @@ export class AGUIAdapterSDK {
39
39
  }
40
40
  async *run({ messages, abortSignal }) {
41
41
  if (useChatRuns.getState().deletedThreads[this.threadId]) {
42
- throw new Error("This conversation was deleted. Start a new chat to continue.");
42
+ throw new Error("This chat was deleted. Start a new chat to continue.");
43
43
  }
44
44
  const aguiMessages = convertMessagesToAGUI(messages);
45
45
  // App-level lifecycle: mark thread active, snapshot context, consume
@@ -78,7 +78,7 @@ export class AGUIAdapterSDK {
78
78
  .filter((p) => p.type === "text")
79
79
  .map((p) => p.text)
80
80
  .join(" ")
81
- .slice(0, 120) || "Conversation",
81
+ .slice(0, 120) || "Chat",
82
82
  pageLabel: useAppStore.getState().navContext.label ?? undefined,
83
83
  contextMeta: { ...useAppStore.getState().navContext.meta },
84
84
  stop: () => stopController.abort(),
@@ -3,7 +3,7 @@ import { TooltipIconButton } from "../index.js";
3
3
  import { SquarePen, PanelLeft, MessageSquare, Menu, MessagesSquare, LoaderCircle, } from "lucide-react";
4
4
  import { RunningChatsMenu } from "./running-chats-menu.js";
5
5
  export function WorkbenchChatHeader({ collapsed, running, chatWidth, buttonClass, toggleNavigation, setCollapsed, showHistory, startChat, returnToChat, openHistory, chatOnly, setContentCollapsed, }) {
6
- return (_jsxs(_Fragment, { children: [_jsxs("div", { "data-chat-header": true, className: `relative flex shrink-0 items-center ${collapsed ? "justify-center" : "gap-1 px-2"}`, children: [!collapsed && !chatWidth.compact && (_jsx(TooltipIconButton, { tooltip: "Open navigation", className: `${buttonClass} preview-mobile-navigation`, "aria-label": "Open navigation", onClick: toggleNavigation, children: _jsx(Menu, { size: 18 }) })), collapsed ? (_jsx(TooltipIconButton, { tooltip: running ? "AI is working — expand chat" : "Expand chat", className: buttonClass, "aria-label": running ? "AI is working — expand chat" : "Expand chat", onClick: () => setCollapsed(false), children: running ? (_jsx(LoaderCircle, { size: 18, className: "animate-spin motion-reduce:animate-none text-primary" })) : (_jsx(MessageSquare, { size: 18 })) })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "min-w-0 flex-1 px-2 py-2", children: _jsx("div", { className: "text-sm font-semibold", children: showHistory ? "Conversations" : "Chat" }) }), _jsx(RunningChatsMenu, { onOpen: returnToChat }), _jsx(TooltipIconButton, { tooltip: "New chat", className: buttonClass, "aria-label": "New chat", onClick: startChat, children: _jsx(SquarePen, { size: 18 }) }), _jsx(TooltipIconButton, { tooltip: "Chat history", className: buttonClass, "aria-label": "Chat history", "aria-expanded": showHistory, onClick: () => (showHistory ? returnToChat() : openHistory()), children: _jsx(MessagesSquare, { size: 18 }) }), _jsx(TooltipIconButton, { tooltip: "Collapse chat", style: chatOnly || chatWidth.compact ? { display: "none" } : undefined, className: buttonClass, "aria-label": "Collapse chat", onClick: () => {
6
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { "data-chat-header": true, className: `relative flex shrink-0 items-center ${collapsed ? "justify-center" : "gap-1 px-2"}`, children: [!collapsed && !chatWidth.compact && (_jsx(TooltipIconButton, { tooltip: "Open navigation", className: `${buttonClass} preview-mobile-navigation`, "aria-label": "Open navigation", onClick: toggleNavigation, children: _jsx(Menu, { size: 18 }) })), collapsed ? (_jsx(TooltipIconButton, { tooltip: running ? "AI is working — expand chat" : "Expand chat", className: buttonClass, "aria-label": running ? "AI is working — expand chat" : "Expand chat", onClick: () => setCollapsed(false), children: running ? (_jsx(LoaderCircle, { size: 18, className: "animate-spin motion-reduce:animate-none text-primary" })) : (_jsx(MessageSquare, { size: 18 })) })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "min-w-0 flex-1 px-2 py-2", children: _jsx("div", { className: "text-sm font-semibold", children: showHistory ? "Chats" : "Chat" }) }), _jsx(RunningChatsMenu, { onOpen: returnToChat }), _jsx(TooltipIconButton, { tooltip: "New chat", className: buttonClass, "aria-label": "New chat", onClick: startChat, children: _jsx(SquarePen, { size: 18 }) }), _jsx(TooltipIconButton, { tooltip: "Chat history", className: buttonClass, "aria-label": "Chat history", "aria-expanded": showHistory, onClick: () => (showHistory ? returnToChat() : openHistory()), children: _jsx(MessagesSquare, { size: 18 }) }), _jsx(TooltipIconButton, { tooltip: "Collapse chat", style: chatOnly || chatWidth.compact ? { display: "none" } : undefined, className: buttonClass, "aria-label": "Collapse chat", onClick: () => {
7
7
  setContentCollapsed(false);
8
8
  setCollapsed(true);
9
9
  }, children: _jsx(PanelLeft, { size: 18 }) })] }))] }), collapsed && (_jsxs("div", { "data-chat-rail": true, className: "flex flex-col items-center gap-1", children: [_jsx(TooltipIconButton, { tooltip: "Open navigation", className: `${buttonClass} preview-mobile-navigation`, "aria-label": "Open navigation", onClick: toggleNavigation, children: _jsx(Menu, { size: 18 }) }), _jsx(TooltipIconButton, { tooltip: "Chat history", className: buttonClass, "aria-label": "Chat history", onClick: () => {
@@ -1,12 +1,14 @@
1
1
  import type { NavGroup } from "../lib/nav-config.js";
2
2
  /** History is a view of existing navigation targets, never a second chat runtime. */
3
- export declare function ConversationHistory({ groups, workspaceId, onClose, showBack, welcome, compact, onViewAll, entityId, rootContext, }: {
3
+ export declare function ConversationHistory({ groups, workspaceId, onClose, showBack, welcome, compact, home, onViewAll, entityId, rootContext, }: {
4
4
  groups: NavGroup[];
5
5
  workspaceId: unknown;
6
6
  onClose: () => void;
7
7
  showBack?: boolean;
8
8
  welcome?: boolean;
9
9
  compact?: boolean;
10
+ /** The global start page shows the three newest chats below the composer. */
11
+ home?: boolean;
10
12
  onViewAll?: () => void;
11
13
  /** Current content entity. Compact homes show only an exact match unless this is the workspace root. */
12
14
  entityId?: unknown;
@@ -6,7 +6,7 @@ import { Link } from "react-router";
6
6
  import { ArrowLeft, MessageSquare, MoreHorizontal, Pin, Loader2, ChevronDown, Check, MessagesSquare, PauseCircle, Copy, } from "lucide-react";
7
7
  import { useChatRuns, isChatRunActive } from "../lib/chat-runs-store.js";
8
8
  /** History is a view of existing navigation targets, never a second chat runtime. */
9
- export function ConversationHistory({ groups, workspaceId, onClose, showBack = true, welcome = false, compact = false, onViewAll, entityId, rootContext = false, }) {
9
+ export function ConversationHistory({ groups, workspaceId, onClose, showBack = true, welcome = false, compact = false, home = false, onViewAll, entityId, rootContext = false, }) {
10
10
  const [loadingTo, setLoadingTo] = useState(null);
11
11
  const [copiedId, setCopiedId] = useState(null);
12
12
  const [loadError, setLoadError] = useState(null);
@@ -18,6 +18,8 @@ export function ConversationHistory({ groups, workspaceId, onClose, showBack = t
18
18
  groups
19
19
  .filter((g) => g.meta?.type === "recent-chats" || g.meta?.type === "pinned-chats")
20
20
  .flatMap((g) => g.items);
21
+ const savedTargets = new Set(allItems.map((item) => item.to));
22
+ const hasServerTimestamps = allItems.some((item) => typeof item.meta?.updatedAt === "string");
21
23
  const runs = useChatRuns((s) => s.runs);
22
24
  const runFor = (item) => Object.values(runs).find((run) => (run.conversationUrl ?? `/chat/${encodeURIComponent(run.threadId)}`) === item.to);
23
25
  const workspaceItems = all || !workspaceId
@@ -57,8 +59,26 @@ export function ConversationHistory({ groups, workspaceId, onClose, showBack = t
57
59
  const run = runFor(item);
58
60
  return run && isChatRunActive(run) ? 1 : 0;
59
61
  };
62
+ if (home) {
63
+ // The server list can lag a just-finished run. Compare its session
64
+ // timestamp with server timestamps so newer chats from other tabs
65
+ // take priority as soon as navigation refreshes.
66
+ const activityAt = (item) => {
67
+ const updatedAt = item.meta?.updatedAt;
68
+ const serverAt = typeof updatedAt === "string" ? Date.parse(updatedAt) || 0 : 0;
69
+ const run = runFor(item);
70
+ const sessionAt = run && (hasServerTimestamps || !savedTargets.has(item.to))
71
+ ? (run.finishedAt ?? run.startedAt)
72
+ : 0;
73
+ return Math.max(serverAt, sessionAt);
74
+ };
75
+ return active(b) - active(a) || activityAt(b) - activityAt(a);
76
+ }
60
77
  return (active(b) - active(a) || Number(Boolean(b.meta?.pinned)) - Number(Boolean(a.meta?.pinned)));
61
78
  });
79
+ const homeRecentItems = home ? items.filter((item) => !item.meta?.pinned).slice(0, 3) : [];
80
+ const homePinnedItems = home ? items.filter((item) => item.meta?.pinned).slice(0, 3) : [];
81
+ const homeHasItems = homeRecentItems.length > 0 || homePinnedItems.length > 0;
62
82
  const visibleItems = compact ? items.slice(0, 3) : items;
63
83
  const activeItems = visibleItems.filter((item) => {
64
84
  const run = runFor(item);
@@ -66,22 +86,43 @@ export function ConversationHistory({ groups, workspaceId, onClose, showBack = t
66
86
  });
67
87
  const idleItems = visibleItems.filter((item) => !activeItems.includes(item));
68
88
  const pinnedItems = idleItems.filter((item) => item.meta?.pinned);
69
- const sections = compact
70
- ? [{ label: "", items: visibleItems }]
71
- : [
72
- { label: "Running", items: activeItems },
73
- { label: "Pinned", items: pinnedItems },
74
- {
75
- label: activeItems.length || pinnedItems.length ? "Recent chats" : "",
76
- items: idleItems.filter((item) => !item.meta?.pinned),
77
- },
78
- ];
79
- return (_jsxs("section", { "data-conversation-history": true, "aria-label": compact ? "Recent conversations" : "Chat history", className: compact ? "w-full" : "mx-auto flex min-h-0 w-full max-w-3xl flex-1 flex-col px-4 pb-4", children: [welcome && (_jsxs("div", { className: "pb-6 pt-8", children: [_jsx("h1", { className: "text-xl font-semibold", children: "How can I help?" }), _jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "Start something new or continue a recent chat." })] })), showBack && (_jsxs("button", { onClick: onClose, className: "mb-4 flex items-center gap-2 py-2 text-xs text-muted-foreground hover:text-foreground", children: [_jsx(ArrowLeft, { size: 14 }), " Back to chat"] })), (!compact || items.length > 0) && (_jsx("div", { className: "mb-2 mt-2 flex items-center justify-between", children: _jsx("h2", { className: compact ? "text-xs font-medium text-muted-foreground" : "text-base font-semibold", children: compact ? "Recent conversations" : "Conversation history" }) })), Boolean(workspaceId) && !compact && (_jsx("div", { className: "mb-3", children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", "aria-label": "History filter", className: "w-full justify-between font-normal shadow-none", children: [_jsx("span", { className: "truncate", children: all ? "All conversations" : "Last used in this workspace" }), _jsx(ChevronDown, { className: "size-4 shrink-0 text-muted-foreground", "aria-hidden": "true" })] }) }), _jsx(DropdownMenuContent, { align: "start", className: "w-[var(--radix-dropdown-menu-trigger-width)]", children: [
89
+ const sections = home
90
+ ? [
91
+ { label: "Recent", items: homeRecentItems },
92
+ { label: "Pinned", items: homePinnedItems },
93
+ ]
94
+ : compact
95
+ ? [{ label: "", items: visibleItems }]
96
+ : [
97
+ { label: "Running", items: activeItems },
98
+ { label: "Pinned", items: pinnedItems },
99
+ {
100
+ label: activeItems.length || pinnedItems.length ? "Recent chats" : "",
101
+ items: idleItems.filter((item) => !item.meta?.pinned),
102
+ },
103
+ ];
104
+ return (_jsxs("section", { "data-conversation-history": true, "aria-label": home ? "Home chats" : compact ? "Recent chats" : "Chat history", className: home
105
+ ? "w-full"
106
+ : compact
107
+ ? "w-full"
108
+ : "mx-auto flex min-h-0 w-full max-w-3xl flex-1 flex-col px-4 pb-4", children: [welcome && (_jsxs("div", { className: "pb-6 pt-8", children: [_jsx("h1", { className: "text-xl font-semibold", children: "How can I help?" }), _jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "Start something new or continue a recent chat." })] })), showBack && (_jsxs("button", { onClick: onClose, className: "mb-4 flex items-center gap-2 py-2 text-xs text-muted-foreground hover:text-foreground", children: [_jsx(ArrowLeft, { size: 14 }), " Back to chat"] })), (!compact || items.length > 0) && (_jsxs("div", { className: home
109
+ ? "mb-2 mt-5 flex items-center justify-between px-1"
110
+ : "mb-2 mt-2 flex items-center justify-between", children: [_jsx("h2", { className: compact ? "text-xs font-medium text-muted-foreground" : "text-base font-semibold", children: home ? "Your chats" : compact ? "Recent chats" : "Chat history" }), home && onViewAll && (_jsx("button", { type: "button", onClick: onViewAll, className: "rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground focus-visible:outline-2 focus-visible:outline-primary [@media(pointer:coarse)]:min-h-11", children: "View all" }))] })), Boolean(workspaceId) && !compact && (_jsx("div", { className: "mb-3", children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", "aria-label": "History filter", className: "w-full justify-between font-normal shadow-none", children: [_jsx("span", { className: "truncate", children: all ? "All chats" : "Last used in this workspace" }), _jsx(ChevronDown, { className: "size-4 shrink-0 text-muted-foreground", "aria-hidden": "true" })] }) }), _jsx(DropdownMenuContent, { align: "start", className: "w-[var(--radix-dropdown-menu-trigger-width)]", children: [
80
111
  { value: false, label: "Last used in this workspace" },
81
- { value: true, label: "All conversations" },
82
- ].map((option) => (_jsxs(DropdownMenuItem, { role: "menuitemradio", "aria-checked": all === option.value, onSelect: () => setAll(option.value), children: [_jsx("span", { className: "flex-1", children: option.label }), all === option.value && _jsx(Check, { className: "size-4", "aria-hidden": "true" })] }, option.label))) })] }) })), !compact && (_jsx(Input, { "aria-label": "Search chats", placeholder: "Search chats", value: search, onChange: (event) => setSearch(event.target.value), className: "mb-3" })), _jsx("span", { className: "sr-only", role: "status", children: copiedId ? "Chat ID copied" : "" }), loadError && (_jsx("p", { role: "alert", className: "mb-2 text-xs text-destructive", children: loadError })), _jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto", "aria-busy": Boolean(loadingTo), children: [sections
83
- .filter((section) => section.items.length)
84
- .map((section) => (_jsxs("section", { "aria-label": section.label || "Recent chats", className: "mb-4 last:mb-0", children: [section.label && (_jsx("h3", { "data-conversation-section-label": true, className: "px-2 py-2 text-xs font-medium text-muted-foreground", children: section.label })), section.items.map((item) => item.disabled ? (_jsx("p", { className: "py-3 text-sm text-muted-foreground", children: item.label }, item.to)) : (_jsxs("div", { className: "group flex items-center rounded-lg hover:bg-muted", children: [_jsxs(Link, { to: item.to, onClick: (event) => {
112
+ { value: true, label: "All chats" },
113
+ ].map((option) => (_jsxs(DropdownMenuItem, { role: "menuitemradio", "aria-checked": all === option.value, onSelect: () => setAll(option.value), children: [_jsx("span", { className: "flex-1", children: option.label }), all === option.value && _jsx(Check, { className: "size-4", "aria-hidden": "true" })] }, option.label))) })] }) })), !compact && (_jsx(Input, { "aria-label": "Search chats", placeholder: "Search chats", value: search, onChange: (event) => setSearch(event.target.value), className: "mb-3" })), _jsx("span", { className: "sr-only", role: "status", children: copiedId ? "Chat ID copied" : "" }), loadError && (_jsx("p", { role: "alert", className: "mb-2 text-xs text-destructive", children: loadError })), _jsxs("div", { className: home
114
+ ? "grid grid-cols-[repeat(auto-fit,minmax(min(100%,18rem),1fr))] gap-4"
115
+ : "min-h-0 flex-1 overflow-y-auto", "aria-busy": Boolean(loadingTo), children: [sections
116
+ .filter((section) => (home ? homeHasItems : section.items.length))
117
+ .map((section) => (_jsxs("section", { "aria-label": section.label || "Recent chats", className: home
118
+ ? `min-w-0 rounded-xl border border-border/70 bg-muted/10 p-3 ${section.items.length ? "" : "hidden min-[640px]:block"}`
119
+ : "mb-4 last:mb-0", children: [section.label && (_jsx("h3", { "data-conversation-section-label": true, className: home
120
+ ? "px-1 pb-2 text-xs font-medium text-muted-foreground"
121
+ : "px-2 py-2 text-xs font-medium text-muted-foreground", children: section.label })), home && !section.items.length && (_jsx("p", { className: "px-1 py-3 text-xs text-muted-foreground", children: section.label === "Pinned"
122
+ ? "Pin chats to keep them close."
123
+ : "Your latest chats will appear here." })), section.items.map((item) => item.disabled ? (_jsx("p", { className: "py-3 text-sm text-muted-foreground", children: item.label }, item.to)) : (_jsxs("div", { className: home
124
+ ? "group flex items-center rounded-lg transition-colors hover:bg-muted/60"
125
+ : "group flex items-center rounded-lg hover:bg-muted", children: [_jsxs(Link, { to: item.to, onClick: (event) => {
85
126
  if (loadingTo) {
86
127
  event.preventDefault();
87
128
  return;
@@ -111,7 +152,9 @@ export function ConversationHistory({ groups, workspaceId, onClose, showBack = t
111
152
  }
112
153
  setLoadingTo(null);
113
154
  onClose();
114
- }, "data-conversation-row": true, className: "flex min-w-0 flex-1 items-center gap-2 rounded-lg px-2 py-1.5 text-sm hover:bg-muted focus-visible:outline-2 focus-visible:outline-primary", children: [loadingTo === item.to ? (_jsx(Loader2, { className: "size-4 shrink-0 animate-spin text-muted-foreground", "aria-label": "Opening chat" })) : runFor(item)?.status === "running" ? (_jsx(Loader2, { className: "size-4 shrink-0 animate-spin text-primary motion-reduce:animate-none", "aria-hidden": true })) : runFor(item)?.status === "waiting" ? (_jsx(PauseCircle, { className: "size-4 shrink-0 text-primary", "aria-hidden": true })) : item.meta?.pinned ? (_jsx(Pin, { className: "size-4 shrink-0 text-muted-foreground", "aria-label": "Pinned chat" })) : (_jsx(MessageSquare, { className: "size-4 shrink-0 text-muted-foreground" })), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "block truncate", children: item.label }), " ", runFor(item) && isChatRunActive(runFor(item)) ? (_jsx("span", { className: "block text-xs text-primary", children: runFor(item)?.status === "waiting" ? "Waiting for you" : "Running" })) : ((!compact || rootContext) && (_jsx("span", { className: "block truncate text-xs text-muted-foreground", children: !all &&
155
+ }, "data-conversation-row": true, className: home
156
+ ? "flex min-w-0 flex-1 items-center gap-3 rounded-lg px-2 py-2 text-left text-sm focus-visible:outline-2 focus-visible:outline-primary"
157
+ : "flex min-w-0 flex-1 items-center gap-2 rounded-lg px-2 py-1.5 text-sm hover:bg-muted focus-visible:outline-2 focus-visible:outline-primary", children: [loadingTo === item.to ? (_jsx(Loader2, { className: "size-4 shrink-0 animate-spin text-muted-foreground", "aria-label": "Opening chat" })) : runFor(item)?.status === "running" ? (_jsx(Loader2, { className: "size-4 shrink-0 animate-spin text-primary motion-reduce:animate-none", "aria-hidden": true })) : runFor(item)?.status === "waiting" ? (_jsx(PauseCircle, { className: "size-4 shrink-0 text-primary", "aria-hidden": true })) : item.meta?.pinned ? (_jsx(Pin, { className: "size-4 shrink-0 text-muted-foreground", "aria-label": "Pinned chat" })) : (_jsx(MessageSquare, { className: "size-4 shrink-0 text-muted-foreground" })), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "block truncate", children: item.label }), " ", runFor(item) && isChatRunActive(runFor(item)) ? (_jsx("span", { className: "block text-xs text-primary", children: runFor(item)?.status === "waiting" ? "Waiting for you" : "Running" })) : ((!compact || rootContext) && (_jsx("span", { className: "block truncate text-xs text-muted-foreground", children: !all &&
115
158
  typeof item.meta?.previewEntityName === "string" &&
116
159
  item.meta.previewEntityName
117
160
  ? `Last at ${item.meta.previewEntityName}`
@@ -138,5 +181,5 @@ export function ConversationHistory({ groups, workspaceId, onClose, showBack = t
138
181
  .catch(() => setLoadError(`Could not copy. Chat ID: ${decodeURIComponent(id)}`));
139
182
  }, children: [_jsx(Copy, { className: "size-4" }), "Copy chat ID"] }), Boolean(item.actions?.length) && _jsx(DropdownMenuSeparator, {}), item.actions?.map((action) => (_jsxs(Fragment, { children: [action.separator && _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onSelect: () => {
140
183
  void action.handler();
141
- }, className: action.destructive ? "text-destructive" : undefined, children: [_jsx(action.icon, { className: "mr-2 size-4" }), action.label] })] }, action.label)))] })] })] }, item.to)))] }, section.label))), items.length === 0 && !compact && (_jsx("p", { className: "py-3 text-sm text-muted-foreground", children: search ? "No matching chats" : "No chats yet" }))] }), compact && onViewAll && (_jsxs(Button, { variant: "ghost", size: "sm", onClick: onViewAll, className: "mt-1 h-9 w-fit gap-2 px-2 text-sm font-normal text-muted-foreground [@media(pointer:coarse)]:min-h-11", children: [_jsx(MessagesSquare, { className: "size-4", "aria-hidden": true }), "Conversation history"] }))] }));
184
+ }, className: action.destructive ? "text-destructive" : undefined, children: [_jsx(action.icon, { className: "mr-2 size-4" }), action.label] })] }, action.label)))] })] })] }, item.to)))] }, section.label))), items.length === 0 && !compact && (_jsx("p", { className: "py-3 text-sm text-muted-foreground", children: search ? "No matching chats" : "No chats yet" }))] }), compact && onViewAll && !home && (_jsxs(Button, { variant: "ghost", size: "sm", onClick: onViewAll, className: "mt-1 h-9 w-fit gap-2 px-2 text-sm font-normal text-muted-foreground [@media(pointer:coarse)]:min-h-11", children: [_jsx(MessagesSquare, { className: "size-4", "aria-hidden": true }), "Chat history"] }))] }));
142
185
  }
@@ -11,7 +11,7 @@ export function RunningChatsMenu({ onOpen } = {}) {
11
11
  const active = Object.values(runs).filter(isChatRunActive);
12
12
  if (!active.length)
13
13
  return null;
14
- return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs("button", { type: "button", "aria-label": `${active.length} running conversations`, className: "inline-flex items-center gap-1.5 px-2 text-xs text-primary", children: [_jsx(LoaderCircle, { className: "size-3.5 animate-spin motion-reduce:animate-none" }), _jsx("span", { children: active.length })] }) }), _jsxs(DropdownMenuContent, { align: "end", className: "max-w-80", children: [_jsx(DropdownMenuLabel, { children: "Running in this browser" }), active.map((run) => (_jsxs(DropdownMenuItem, { onSelect: () => {
14
+ return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs("button", { type: "button", "aria-label": `${active.length} running chats`, className: "inline-flex items-center gap-1.5 px-2 text-xs text-primary", children: [_jsx(LoaderCircle, { className: "size-3.5 animate-spin motion-reduce:animate-none" }), _jsx("span", { children: active.length })] }) }), _jsxs(DropdownMenuContent, { align: "end", className: "max-w-80", children: [_jsx(DropdownMenuLabel, { children: "Running in this browser" }), active.map((run) => (_jsxs(DropdownMenuItem, { onSelect: () => {
15
15
  if (!selectRetainedThread(run.threadId)) {
16
16
  navigate(run.conversationUrl ?? `/chat/${encodeURIComponent(run.threadId)}`);
17
17
  }
@@ -105,7 +105,7 @@ export function useWorkbenchState(chatRoute) {
105
105
  const openHistory = () => {
106
106
  setHistoryOpen(true);
107
107
  };
108
- const historyShortcut = (_jsxs(Button, { variant: "ghost", size: "sm", onClick: openHistory, className: "h-9 w-fit gap-2 px-2 text-sm font-normal text-muted-foreground [@media(pointer:coarse)]:min-h-11", children: [_jsx(MessagesSquare, { className: "size-4", "aria-hidden": true }), "Conversation history"] }));
108
+ const historyShortcut = (_jsxs(Button, { variant: "ghost", size: "sm", onClick: openHistory, className: "h-9 w-fit gap-2 px-2 text-sm font-normal text-muted-foreground [@media(pointer:coarse)]:min-h-11", children: [_jsx(MessagesSquare, { className: "size-4", "aria-hidden": true }), "Chat history"] }));
109
109
  const toggleNavigation = useSidebarStore((s) => s.toggleMobile);
110
110
  useEffect(() => {
111
111
  if (panelOpen) {
@@ -4,7 +4,7 @@ import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuIte
4
4
  import { ChatHomeStartersSlot } from "../components/chat-slots.js";
5
5
  import { Layers } from "lucide-react";
6
6
  export function WelcomeIntro({ navGroups, historyShortcut, }) {
7
- return (_jsxs("div", { className: "mb-4 text-center", children: [_jsx("h1", { className: "text-2xl font-semibold tracking-tight", children: "What would you like to get done?" }), _jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "Find information, create or edit content, and run actions across your spaces." }), _jsx("div", { className: "mx-auto mt-4 max-w-sm text-left", children: _jsx(ChatHomeStartersSlot, { priority: 10 }) }), _jsxs("div", { className: "mt-4 flex flex-wrap items-center justify-center gap-2", children: [historyShortcut, navGroups.some((group) => group.meta?.type === "spaces-workspaces" && group.createActions?.length) && (_jsx("div", { children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "sm", className: "h-9 gap-2 px-2 text-sm font-normal text-muted-foreground [@media(pointer:coarse)]:min-h-11", children: [_jsx(Layers, { className: "size-4", "aria-hidden": "true" }), "Create a space"] }) }), _jsx(DropdownMenuContent, { align: "center", children: navGroups
7
+ return (_jsxs("div", { className: "mb-4 text-center", children: [_jsx("h1", { className: "text-2xl font-semibold tracking-tight", children: "What would you like to get done?" }), _jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "Find information, create or edit content, and run actions across your spaces." }), _jsx("div", { className: "mx-auto mt-4 max-w-sm text-left", children: _jsx(ChatHomeStartersSlot, { priority: 10 }) }), _jsxs("div", { className: "mt-4 flex flex-wrap items-center justify-center gap-2", children: [historyShortcut, navGroups.some((group) => group.meta?.type === "spaces-workspaces" && group.createActions?.length) && (_jsx("div", { children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "sm", className: "h-9 gap-2 bg-primary/10 px-3 text-sm font-medium hover:bg-primary/15 [@media(pointer:coarse)]:min-h-11", children: [_jsx(Layers, { className: "size-4", "aria-hidden": "true" }), "Create a space"] }) }), _jsx(DropdownMenuContent, { align: "center", children: navGroups
8
8
  .filter((group) => group.meta?.type === "spaces-workspaces")
9
9
  .flatMap((group) => group.createActions ?? [])
10
10
  .map((action) => (_jsx(DropdownMenuItem, { onSelect: () => action.handler(), children: action.label }, action.id))) })] }) }))] })] }));
@@ -127,6 +127,7 @@ export function WorkbenchStyles() {
127
127
  top: 0;
128
128
  bottom: 0;
129
129
  width: 1px;
130
+ z-index: 20;
130
131
  pointer-events: none;
131
132
  background: color-mix(in srgb, var(--border) 55%, transparent);
132
133
  }
@@ -23,6 +23,7 @@ export function WorkbenchPreview({ page, chatOnly: chatRoute = false, }) {
23
23
  normalizeWorkbenchPath(rootItem.to) === normalizeWorkbenchPath(currentPage));
24
24
  const conversationHome = (_jsx(ConversationHistory, { groups: navGroups, workspaceId: workspaceId, entityId: entityId, rootContext: rootContext, onClose: returnToChat, onViewAll: openHistory, showBack: false, compact: true }));
25
25
  const emptyChatFooter = workspaceId ? conversationHome : historyShortcut;
26
+ const homeRecents = !workspaceId ? (_jsx(ConversationHistory, { groups: navGroups, workspaceId: undefined, onClose: returnToChat, onViewAll: openHistory, showBack: false, compact: true, home: true })) : undefined;
26
27
  const buttonClass = "inline-flex items-center justify-center rounded-lg p-2 text-muted-foreground hover:bg-muted hover:text-foreground focus-visible:outline-2 focus-visible:outline-primary";
27
28
  return (_jsxs("div", { className: "workbench-preview flex min-h-0 flex-1 overflow-hidden", "data-workspace-navigation": workspaceGroups ? "true" : undefined, children: [_jsx(WorkbenchStyles, {}), _jsx(WorkbenchNavigation, { groups: staticNavigationGroups, workspace: !!workspaceGroups, onNewConversation: startChat }), _jsxs("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden", "data-workbench-surface": true, role: "region", "aria-label": "Workspace working area", children: [chatWidth.compact && (_jsxs("div", { className: "flex shrink-0 items-center gap-1 border-b border-border px-2 py-1", role: "group", "aria-label": "Workspace panes", children: [_jsx(Button, { variant: "ghost", size: "icon", className: "preview-mobile-navigation size-11", "aria-label": "Open navigation", onClick: toggleNavigation, children: _jsx(Menu, { className: "size-4" }) }), ["content", "chat"]
28
29
  .filter((pane) => !chatOnly || pane === "chat")
@@ -39,5 +40,5 @@ export function WorkbenchPreview({ page, chatOnly: chatRoute = false, }) {
39
40
  }, children: [_jsx(WorkbenchChatHeader, { collapsed: collapsed, running: running, chatWidth: chatWidth, buttonClass: buttonClass, toggleNavigation: toggleNavigation, setCollapsed: setCollapsed, showHistory: showHistory, startChat: startChat, returnToChat: returnToChat, openHistory: openHistory, chatOnly: chatOnly, setContentCollapsed: setContentCollapsed }), !collapsed && showHistory && (_jsx(ConversationHistory, { groups: navGroups, workspaceId: workspaceId, onClose: returnToChat, showBack: true })), !chatWidth.compact && !collapsed && !contentCollapsed && (_jsx(ChatResizeHandle, { chatWidth: chatWidth })), _jsx("div", { className: "min-h-0 flex-1 flex-col", style: {
40
41
  display: collapsed || showHistory ? "none" : "flex",
41
42
  paddingBottom: spaciousChat && emptyChat && !historyOpen ? "clamp(32px, 10vh, 96px)" : undefined,
42
- }, children: _jsx(ChatContent, { centerComposer: spaciousChat && emptyChat && !historyOpen, composerIntro: spaciousChat && emptyChat ? (_jsx(WelcomeIntro, { navGroups: navGroups, historyShortcut: emptyChatFooter })) : undefined, compactComposer: !spaciousChat, starterSuggestions: [], emptyState: _jsx(ChatEmptyState, { pageLabel: pageLabel, showHomeStarters: !spaciousChat, children: emptyChatFooter }) }) })] }), _jsx(WorkbenchContentPane, { contentHidden: contentHidden, panelOpen: panelOpen, chatWidth: chatWidth, contentCollapsed: contentCollapsed, collapsed: collapsed, buttonClass: buttonClass, setCollapsed: setCollapsed, setContentCollapsed: setContentCollapsed, closePanel: closePanel, page: page }), contentCollapsed && !chatOnly && !chatWidth.compact && (_jsx("aside", { "aria-label": "Collapsed content", className: "flex w-11 shrink-0 flex-col items-center", style: { width: 44 }, children: _jsx("div", { className: "flex h-full w-full flex-col items-center", "data-chat-rail": true, children: _jsx("div", { className: "flex items-center justify-center", style: { height: 60 }, children: _jsx(TooltipIconButton, { tooltip: "Expand content", className: buttonClass, "aria-label": "Expand content", onClick: () => setContentCollapsed(false), children: _jsx(FileText, { size: 18 }) }) }) }) }))] })] })] }));
43
+ }, children: _jsx(ChatContent, { centerComposer: spaciousChat && emptyChat && !historyOpen, composerIntro: spaciousChat && emptyChat ? (_jsx(WelcomeIntro, { navGroups: navGroups, historyShortcut: workspaceId ? emptyChatFooter : null })) : undefined, composerFooter: spaciousChat && emptyChat && !historyOpen && homeRecents ? (_jsx("div", { className: "w-full", children: homeRecents })) : undefined, compactComposer: !spaciousChat, starterSuggestions: [], emptyState: _jsx(ChatEmptyState, { pageLabel: pageLabel, showHomeStarters: !spaciousChat, children: emptyChatFooter }) }) })] }), _jsx(WorkbenchContentPane, { contentHidden: contentHidden, panelOpen: panelOpen, chatWidth: chatWidth, contentCollapsed: contentCollapsed, collapsed: collapsed, buttonClass: buttonClass, setCollapsed: setCollapsed, setContentCollapsed: setContentCollapsed, closePanel: closePanel, page: page }), contentCollapsed && !chatOnly && !chatWidth.compact && (_jsx("aside", { "aria-label": "Collapsed content", className: "flex w-11 shrink-0 flex-col items-center", style: { width: 44 }, children: _jsx("div", { className: "flex h-full w-full flex-col items-center", "data-chat-rail": true, children: _jsx("div", { className: "flex items-center justify-center", style: { height: 60 }, children: _jsx(TooltipIconButton, { tooltip: "Expand content", className: buttonClass, "aria-label": "Expand content", onClick: () => setContentCollapsed(false), children: _jsx(FileText, { size: 18 }) }) }) }) }))] })] })] }));
43
44
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iloveagents/foundry-web-ui",
3
- "version": "0.32.2",
3
+ "version": "0.32.4",
4
4
  "license": "MIT",
5
5
  "description": "React agent UI core for Foundry UI — chat, composer, AG-UI adapter for assistant-ui, tool cards, panels, sidebar, theme runtime, and UI stores.",
6
6
  "keywords": [
@@ -80,8 +80,8 @@
80
80
  "recharts": "^3.10.1",
81
81
  "remark-gfm": "^4.0.0",
82
82
  "tailwind-merge": "^3.5.0",
83
- "@iloveagents/foundry-agent": "^0.32.2",
84
- "@iloveagents/foundry-web-primitives": "^0.32.2"
83
+ "@iloveagents/foundry-agent": "^0.32.4",
84
+ "@iloveagents/foundry-web-primitives": "^0.32.4"
85
85
  },
86
86
  "devDependencies": {
87
87
  "@ag-ui/client": "^0.0.52",