@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-
|
|
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.
|
|
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.
|
|
84
|
-
"@iloveagents/foundry-web-primitives": "^0.32.
|
|
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",
|