@iloveagents/foundry-web-ui 0.32.0 → 0.32.2

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.
@@ -11,7 +11,7 @@ import { SentContextBadges } from "./context-badges.js";
11
11
  import { ComposerAddMenu } from "./composer-add-menu.js";
12
12
  import { ChatContext } from "./chat-context.js";
13
13
  import { ComposerSubmitBridge } from "./composer-submit-bridge.js";
14
- import { ChatComposerActionsSlot, ChatComposerBannerSlot, ChatHomeStartersSlot, useChatThreadSurface, useComposerActionsOwnSend, } from "./chat-slots.js";
14
+ import { ChatComposerActionsSlot, ChatComposerBannerSlot, ChatHomeStartersSlot, useChatThreadSurface, useChatSlotsStore, useComposerActionsOwnSend, } from "./chat-slots.js";
15
15
  import { useChatBubbleStore } from "../lib/chat-bubble-store.js";
16
16
  import { ReasoningEffortPicker } from "./reasoning-effort-picker.js";
17
17
  import { ChatMessageParts } from "./chat-message-parts.js";
@@ -129,11 +129,12 @@ export const DEFAULT_COMPOSER_PLACEHOLDER = "Do anything";
129
129
  */
130
130
  export function ChatContent({ centerComposer = false, composerIntro, composerFooter, compactComposer = false, emptyState, starterSuggestions = DEFAULT_STARTER_SUGGESTIONS, placeholder = DEFAULT_COMPOSER_PLACEHOLDER, } = {}) {
131
131
  const ThreadSurface = useChatThreadSurface();
132
+ const hideComposer = useChatSlotsStore((s) => !!s.threadSurface && s.threadSurfaceHidesComposer);
132
133
  const isExpanded = useChatBubbleStore((s) => s.isExpanded);
133
134
  const collapseChat = useChatBubbleStore((s) => s.collapse);
134
135
  const showPagePanel = useChatBubbleStore((s) => s.showPagePanel);
135
136
  const togglePagePanel = useChatBubbleStore((s) => s.togglePagePanel);
136
- return (_jsxs(ThreadPrimitive.Root, { className: cn("flex flex-col flex-1", "bg-background text-foreground", "overflow-hidden"), children: [isExpanded && (_jsxs("div", { className: "shrink-0 flex items-center justify-between px-4 py-2 border-b border-border", children: [_jsxs("span", { className: "text-sm font-medium text-foreground flex items-center gap-2", children: [_jsx(Bot, { className: "size-4 text-primary" }), "Chat"] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(TooltipIconButton, { tooltip: showPagePanel ? "Hide page panel" : "Show page panel", size: "icon", onClick: togglePagePanel, className: "hidden xl:inline-flex", children: showPagePanel ? (_jsx(PanelRightClose, { className: "size-4" })) : (_jsx(PanelRightOpen, { className: "size-4" })) }), _jsx(TooltipIconButton, { tooltip: "Minimize to bubble", size: "icon", onClick: collapseChat, children: _jsx(Minimize2, { className: "size-4" }) })] })] })), _jsxs(DropZone, { centered: centerComposer, children: [_jsxs("div", { className: "relative flex-1 overflow-hidden", style: centerComposer && !ThreadSurface ? { display: "none" } : undefined, children: [ThreadSurface && (
137
+ return (_jsxs(ThreadPrimitive.Root, { className: cn("flex flex-col flex-1", "bg-background text-foreground", "overflow-hidden"), children: [isExpanded && (_jsxs("div", { className: "shrink-0 flex items-center justify-between px-4 py-2 border-b border-border", children: [_jsxs("span", { className: "text-sm font-medium text-foreground flex items-center gap-2", children: [_jsx(Bot, { className: "size-4 text-primary" }), "Chat"] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(TooltipIconButton, { tooltip: showPagePanel ? "Hide page panel" : "Show page panel", size: "icon", onClick: togglePagePanel, className: "hidden xl:inline-flex", children: showPagePanel ? (_jsx(PanelRightClose, { className: "size-4" })) : (_jsx(PanelRightOpen, { className: "size-4" })) }), _jsx(TooltipIconButton, { tooltip: "Minimize to bubble", size: "icon", onClick: collapseChat, children: _jsx(Minimize2, { className: "size-4" }) })] })] })), _jsxs(DropZone, { centered: centerComposer && !ThreadSurface, children: [_jsxs("div", { className: "relative flex-1 overflow-hidden", style: centerComposer && !ThreadSurface ? { display: "none" } : undefined, children: [ThreadSurface && (
137
138
  // `overflow-hidden`, not scroll: a surface owns its own layout,
138
139
  // and a full-bleed one (an avatar cropped to the frame) must be
139
140
  // able to reach the edges rather than sit in a scroll box.
@@ -141,7 +142,7 @@ export function ChatContent({ centerComposer = false, composerIntro, composerFoo
141
142
  UserMessage,
142
143
  EditComposer,
143
144
  AssistantMessage,
144
- } }), _jsx(ThreadPrimitive.If, { running: true, children: _jsx(LoadingIndicator, {}) })] }) })] }), !ThreadSurface && (_jsx(ThreadPrimitive.If, { empty: false, children: _jsx(ScrollToBottomButton, {}) }))] }), _jsxs("div", { className: "shrink-0 bg-background", children: [composerIntro && _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: "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
146
  // Query container for the controls inside, so they size
146
147
  // against the composer rather than the viewport. This one
147
148
  // is wide, so the effort picker keeps its full label.
@@ -11,7 +11,7 @@ import { SentContextBadges } from "./context-badges.js";
11
11
  import { ComposerSubmitBridge } from "./composer-submit-bridge.js";
12
12
  import { ComposerAddMenu } from "./composer-add-menu.js";
13
13
  import { DEFAULT_COMPOSER_PLACEHOLDER } from "./assistant-chat.js";
14
- import { ChatComposerActionsSlot, ChatComposerBannerSlot, ChatHomeStartersSlot, ChatLauncherBadgeSlot, useChatThreadSurface, useComposerActionsOwnSend, } from "./chat-slots.js";
14
+ import { ChatComposerActionsSlot, ChatComposerBannerSlot, ChatHomeStartersSlot, ChatLauncherBadgeSlot, useChatThreadSurface, useChatSlotsStore, useComposerActionsOwnSend, } from "./chat-slots.js";
15
15
  import { ChatMessageParts } from "./chat-message-parts.js";
16
16
  import { ReasoningEffortPicker } from "./reasoning-effort-picker.js";
17
17
  import { LoadingIndicator } from "./loading-indicator.js";
@@ -39,6 +39,7 @@ function BubbleScrollToBottomButton() {
39
39
  }
40
40
  export function ChatBubble() {
41
41
  const ThreadSurface = useChatThreadSurface();
42
+ const hideComposer = useChatSlotsStore((s) => !!s.threadSurface && s.threadSurfaceHidesComposer);
42
43
  const isOpen = useChatBubbleStore((s) => s.isOpen);
43
44
  const isExpanded = useChatBubbleStore((s) => s.isExpanded);
44
45
  const openBubble = useChatBubbleStore((s) => s.open);
@@ -108,7 +109,7 @@ export function ChatBubble() {
108
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
110
  UserMessage: BubbleUserMessage,
110
111
  AssistantMessage: BubbleAssistantMessage,
111
- } }), _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", children: [_jsx(ChatComposerBannerSlot, {}), _jsxs("div", { className: "px-3 py-3", children: [_jsx(ChatContext, {}), _jsx(ComposerPrimitive.Root, { className: cn(
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(
112
113
  // Query container for the controls inside: the bubble is
113
114
  // a narrow panel in a wide window, so they have to size
114
115
  // against THIS box, not the viewport.
@@ -58,6 +58,8 @@ interface ChatSlotsState {
58
58
  composerActionsReplaceSend: boolean;
59
59
  /** Replaces the message viewport entirely while set. */
60
60
  threadSurface: ComponentType | null;
61
+ /** The surface supplies its own controls. Hide (but retain) the text composer. */
62
+ threadSurfaceHidesComposer: boolean;
61
63
  }
62
64
  export declare const useChatSlotsStore: import("zustand").UseBoundStore<import("zustand").StoreApi<ChatSlotsState>>;
63
65
  /**
@@ -54,6 +54,7 @@ export const useChatSlotsStore = create(() => ({
54
54
  composerActions: null,
55
55
  composerActionsReplaceSend: false,
56
56
  threadSurface: null,
57
+ threadSurfaceHidesComposer: false,
57
58
  }));
58
59
  const homeStarterHosts = new Map();
59
60
  let homeStarterHostOrder = 0;
@@ -93,8 +94,14 @@ export function registerChatSlots(slots) {
93
94
  if (slots.composerActionsReplaceSend !== undefined) {
94
95
  next.composerActionsReplaceSend = slots.composerActionsReplaceSend;
95
96
  }
96
- if (slots.threadSurface !== undefined)
97
+ if (slots.threadSurface !== undefined) {
97
98
  next.threadSurface = slots.threadSurface;
99
+ // Composer ownership belongs to this surface, never to the next module.
100
+ next.threadSurfaceHidesComposer = slots.threadSurfaceHidesComposer ?? false;
101
+ }
102
+ if (slots.threadSurfaceHidesComposer !== undefined) {
103
+ next.threadSurfaceHidesComposer = slots.threadSurfaceHidesComposer;
104
+ }
98
105
  useChatSlotsStore.setState(next);
99
106
  }
100
107
  export function ChatComposerBannerSlot() {
@@ -16,5 +16,5 @@ export function ToolCallCard({ icon, title, description, status, action, childre
16
16
  const isError = status.type === "incomplete" && status.reason === "error";
17
17
  const isRunning = status.type === "running";
18
18
  const requiresAction = status.type === "requires-action";
19
- return (_jsx(Collapsible, { open: isOpen, onOpenChange: setIsOpen, className: className, children: _jsxs("div", { className: cn("flex items-start gap-3 p-3 rounded-lg border", "bg-muted/50 text-sm transition-colors", isSuccess && "border-primary/30 bg-primary/5", isError && "border-destructive/30 bg-destructive/5", requiresAction && "border-primary/30 bg-primary/5"), children: [_jsxs("div", { className: "flex-shrink-0 mt-0.5", children: [isRunning && _jsx(Loader2, { className: "size-4 text-primary animate-spin" }), isSuccess && _jsx(CheckCircle2, { className: "size-4 text-primary" }), isError && _jsx(XCircle, { className: "size-4 text-destructive" }), requiresAction && _jsx(AlertCircle, { className: "size-4 text-primary" })] }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "flex items-center gap-2", children: [icon && _jsx("span", { className: "text-muted-foreground", children: icon }), _jsx("span", { className: "font-medium text-foreground", children: title }), _jsxs("div", { className: "ml-auto flex items-center gap-1.5", children: [action && !isRunning && (_jsx("button", { type: "button", onClick: action.onClick, disabled: action.disabled, className: cn("h-6 px-2.5 text-xs font-medium rounded-md", "border border-primary/30 text-primary", "hover:bg-primary/10 transition-colors", "disabled:opacity-50 disabled:pointer-events-none"), children: action.label })), children && (_jsx(CollapsibleTrigger, { asChild: true, children: _jsx("button", { type: "button", className: "p-0.5 hover:bg-muted rounded transition-colors", "aria-label": "Toggle details", children: _jsx(ChevronDown, { className: cn("size-4 text-muted-foreground transition-transform", isOpen && "rotate-180") }) }) }))] })] }), description && (_jsx("div", { className: cn("text-xs mt-0.5", isError ? "text-destructive" : "text-primary"), children: description })), children && (_jsx(CollapsibleContent, { children: _jsx("div", { className: "mt-2", children: children }) }))] })] }) }));
19
+ return (_jsx(Collapsible, { open: isOpen, onOpenChange: setIsOpen, className: className, children: _jsxs("div", { className: cn("flex items-start gap-3 p-3 rounded-lg border", "bg-muted/50 text-sm transition-colors", isSuccess && "border-primary/30 bg-primary/5", isError && "border-destructive/30 bg-destructive/5", requiresAction && "border-primary/30 bg-primary/5"), children: [_jsxs("div", { className: "flex-shrink-0 mt-0.5", children: [isRunning && _jsx(Loader2, { className: "size-4 text-primary animate-spin" }), isSuccess && _jsx(CheckCircle2, { className: "size-4 text-primary" }), isError && _jsx(XCircle, { className: "size-4 text-destructive" }), requiresAction && _jsx(AlertCircle, { className: "size-4 text-primary" })] }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "flex items-center gap-2", children: [icon && _jsx("span", { className: "text-muted-foreground", children: icon }), _jsx("span", { className: "font-medium text-foreground", children: title }), _jsxs("div", { className: "ml-auto flex items-center gap-1.5", children: [action && !isRunning && (_jsx("button", { type: "button", onClick: action.onClick, disabled: action.disabled, className: cn("min-h-6 px-1.5 text-xs font-medium rounded-md text-primary", "hover:bg-primary/10 transition-colors", "disabled:opacity-50 disabled:pointer-events-none"), children: action.label })), children && (_jsx(CollapsibleTrigger, { asChild: true, children: _jsx("button", { type: "button", className: "p-0.5 hover:bg-muted rounded transition-colors", "aria-label": "Toggle details", children: _jsx(ChevronDown, { className: cn("size-4 text-muted-foreground transition-transform", isOpen && "rotate-180") }) }) }))] })] }), description && (_jsx("div", { className: cn("text-xs mt-0.5 wrap-anywhere", isError ? "text-destructive" : "text-primary"), children: description })), children && (_jsx(CollapsibleContent, { children: _jsx("div", { className: "mt-2", children: children }) }))] })] }) }));
20
20
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iloveagents/foundry-web-ui",
3
- "version": "0.32.0",
3
+ "version": "0.32.2",
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.0",
84
- "@iloveagents/foundry-web-primitives": "^0.32.0"
83
+ "@iloveagents/foundry-agent": "^0.32.2",
84
+ "@iloveagents/foundry-web-primitives": "^0.32.2"
85
85
  },
86
86
  "devDependencies": {
87
87
  "@ag-ui/client": "^0.0.52",