@iloveagents/foundry-web-ui 0.5.0 → 0.6.0

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.
@@ -9,6 +9,7 @@ import { userAttachmentComponents, composerAttachmentComponents } from "./chat-a
9
9
  import { SentContextBadges, ComposerContextBadges } from "./context-badges.js";
10
10
  import { ContextPins } from "./context-bar.js";
11
11
  import { ComposerSubmitBridge } from "./composer-submit-bridge.js";
12
+ import { ChatComposerBannerSlot } from "./chat-slots.js";
12
13
  import { useChatBubbleStore } from "../lib/chat-bubble-store.js";
13
14
  import { MarkdownText } from "./markdown-text.js";
14
15
  import { ToolFallback } from "./tool-fallback.js";
@@ -90,5 +91,5 @@ export function ChatContent({ starterSuggestions = DEFAULT_STARTER_SUGGESTIONS,
90
91
  UserMessage,
91
92
  EditComposer,
92
93
  AssistantMessage,
93
- } }), _jsx(ThreadPrimitive.If, { running: true, children: _jsx(LoadingIndicator, {}) })] }) })] }), _jsx(ThreadPrimitive.If, { empty: false, children: _jsx(ScrollToBottomButton, {}) })] }), _jsxs("div", { className: "shrink-0 bg-background", children: [_jsx(ThreadPrimitive.If, { running: false, children: _jsx("div", { className: "border-t border-border" }) }), _jsx(ThreadPrimitive.If, { running: true, children: _jsx(LoadingBar, {}) }), _jsx("div", { className: "mx-auto max-w-3xl px-4 py-4", children: _jsxs(ComposerPrimitive.Root, { className: cn("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(ComposerContextBadges, {}), _jsx(ComposerPrimitive.Attachments, { components: composerAttachmentComponents }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("div", { className: "flex items-center -space-x-1 shrink-0", children: [_jsx(ComposerPrimitive.AddAttachment, { asChild: true, children: _jsx(TooltipIconButton, { tooltip: "Attach file", variant: "ghost", className: "shrink-0", children: _jsx(Paperclip, { className: "size-5" }) }) }), _jsx(ContextPins, { showPinButton: showPinButton })] }), _jsx(ComposerPrimitive.Input, { autoFocus: true, placeholder: "Message Assistant...", rows: 1, className: cn("flex-1 bg-transparent", "py-2 text-base", "text-foreground outline-none", "placeholder:text-muted-foreground", "resize-none max-h-32") }), _jsx(ComposerActionButton, {})] })] }) })] })] })] }));
94
+ } }), _jsx(ThreadPrimitive.If, { running: true, children: _jsx(LoadingIndicator, {}) })] }) })] }), _jsx(ThreadPrimitive.If, { empty: false, children: _jsx(ScrollToBottomButton, {}) })] }), _jsxs("div", { className: "shrink-0 bg-background", children: [_jsx(ChatComposerBannerSlot, {}), _jsx(ThreadPrimitive.If, { running: false, children: _jsx("div", { className: "border-t border-border" }) }), _jsx(ThreadPrimitive.If, { running: true, children: _jsx(LoadingBar, {}) }), _jsx("div", { className: "mx-auto max-w-3xl px-4 py-4", children: _jsxs(ComposerPrimitive.Root, { className: cn("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(ComposerContextBadges, {}), _jsx(ComposerPrimitive.Attachments, { components: composerAttachmentComponents }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("div", { className: "flex items-center -space-x-1 shrink-0", children: [_jsx(ComposerPrimitive.AddAttachment, { asChild: true, children: _jsx(TooltipIconButton, { tooltip: "Attach file", variant: "ghost", className: "shrink-0", children: _jsx(Paperclip, { className: "size-5" }) }) }), _jsx(ContextPins, { showPinButton: showPinButton })] }), _jsx(ComposerPrimitive.Input, { autoFocus: true, placeholder: "Message Assistant...", rows: 1, className: cn("flex-1 bg-transparent", "py-2 text-base", "text-foreground outline-none", "placeholder:text-muted-foreground", "resize-none max-h-32") }), _jsx(ComposerActionButton, {})] })] }) })] })] })] }));
94
95
  }
@@ -8,6 +8,7 @@ import { TooltipIconButton } from "./tooltip-icon-button.js";
8
8
  import { ContextPins } from "./context-bar.js";
9
9
  import { ComposerContextBadges, SentContextBadges } from "./context-badges.js";
10
10
  import { ComposerSubmitBridge } from "./composer-submit-bridge.js";
11
+ import { ChatComposerBannerSlot, ChatLauncherBadgeSlot } from "./chat-slots.js";
11
12
  import { MarkdownText } from "./markdown-text.js";
12
13
  import { LoadingIndicator } from "./loading-indicator.js";
13
14
  import { MessageTimestamp } from "./message-timestamp.js";
@@ -96,10 +97,10 @@ export function ChatBubble() {
96
97
  return (_jsxs(_Fragment, { children: [!isOpen && (_jsx("div", { "aria-hidden": "true", className: cn("pointer-events-none fixed bottom-0 left-0 z-40 hidden md:block", "border-r border-sidebar-border", "bg-gradient-to-b from-sidebar/0 via-sidebar/88 to-sidebar/96", "supports-[backdrop-filter]:backdrop-blur"), style: {
97
98
  width: isSidebarOpen ? sidebarWidth : RAIL_WIDTH,
98
99
  height: 64,
99
- } })), !isOpen && (_jsx("button", { type: "button", onClick: openBubble, className: cn("fixed bottom-4 z-50", "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"), style: { left: CHAT_BUBBLE_INSET, bottom: CHAT_BUBBLE_INSET }, "aria-label": "Open chat", children: _jsx(MessageSquare, { className: "size-4" }) })), isOpen && (_jsxs("div", { ref: panelRef, 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
100
+ } })), !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": "Open chat", children: _jsx(MessageSquare, { className: "size-4" }) }), _jsx(ChatLauncherBadgeSlot, {})] })), isOpen && (_jsxs("div", { ref: panelRef, 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
100
101
  ? { left: dragPos.x, top: dragPos.y }
101
102
  : { 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(MessageSquare, { 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: [_jsxs(ThreadPrimitive.Viewport, { className: "absolute inset-0 overflow-y-auto", children: [_jsx(ThreadPrimitive.Empty, { children: _jsx("div", { className: "flex h-full items-center justify-center p-4", children: _jsx("p", { className: "text-sm text-muted-foreground text-center", children: "Ask the agent about this page" }) }) }), _jsx(ThreadPrimitive.If, { empty: false, children: _jsxs("div", { className: "px-4 pt-4", children: [_jsx(ThreadPrimitive.Messages, { components: {
102
103
  UserMessage: BubbleUserMessage,
103
104
  AssistantMessage: BubbleAssistantMessage,
104
- } }), _jsx(ThreadPrimitive.If, { running: true, children: _jsx(LoadingIndicator, {}) })] }) })] }), _jsx(ThreadPrimitive.If, { empty: false, children: _jsx(BubbleScrollToBottomButton, {}) })] }), _jsx("div", { className: "shrink-0 border-t border-border px-3 py-3", children: _jsxs(ComposerPrimitive.Root, { className: cn("flex flex-col gap-1", "rounded-2xl border border-input", "bg-background shadow-sm", "px-3 py-2", "focus-within:border-primary/50", "transition-all duration-200"), children: [_jsx(ComposerSubmitBridge, {}), _jsx(ComposerContextBadges, {}), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("div", { className: "flex items-center -space-x-1 shrink-0", children: [_jsx(ComposerPrimitive.AddAttachment, { asChild: true, children: _jsx(TooltipIconButton, { tooltip: "Attach file", size: "icon", className: "size-7 shrink-0", children: _jsx(Paperclip, { className: "size-3.5" }) }) }), _jsx(ContextPins, { compact: true })] }), _jsx(ComposerPrimitive.Input, { autoFocus: true, placeholder: "Message...", rows: 1, className: cn("flex-1 bg-transparent", "py-1 text-sm", "text-foreground outline-none", "placeholder:text-muted-foreground", "resize-none max-h-20") }), _jsx(ThreadPrimitive.If, { running: true, children: _jsx(ComposerPrimitive.Cancel, { asChild: true, children: _jsx(Button, { variant: "secondary", size: "icon", className: "rounded-full size-8 shrink-0", "aria-label": "Stop", children: _jsx(Square, { className: "size-3" }) }) }) }), _jsx(ThreadPrimitive.If, { running: false, children: _jsx(ComposerPrimitive.Send, { asChild: true, children: _jsx(Button, { size: "icon", className: "rounded-full size-8 shrink-0", "aria-label": "Send", children: _jsx(ArrowUp, { className: "size-4" }) }) }) })] })] }) })] })] }))] }));
105
+ } }), _jsx(ThreadPrimitive.If, { running: true, children: _jsx(LoadingIndicator, {}) })] }) })] }), _jsx(ThreadPrimitive.If, { empty: false, children: _jsx(BubbleScrollToBottomButton, {}) })] }), _jsxs("div", { className: "shrink-0 border-t border-border", children: [_jsx(ChatComposerBannerSlot, {}), _jsx("div", { className: "px-3 py-3", children: _jsxs(ComposerPrimitive.Root, { className: cn("flex flex-col gap-1", "rounded-2xl border border-input", "bg-background shadow-sm", "px-3 py-2", "focus-within:border-primary/50", "transition-all duration-200"), children: [_jsx(ComposerSubmitBridge, {}), _jsx(ComposerContextBadges, {}), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("div", { className: "flex items-center -space-x-1 shrink-0", children: [_jsx(ComposerPrimitive.AddAttachment, { asChild: true, children: _jsx(TooltipIconButton, { tooltip: "Attach file", size: "icon", className: "size-7 shrink-0", children: _jsx(Paperclip, { className: "size-3.5" }) }) }), _jsx(ContextPins, { compact: true })] }), _jsx(ComposerPrimitive.Input, { autoFocus: true, placeholder: "Message...", rows: 1, className: cn("flex-1 bg-transparent", "py-1 text-sm", "text-foreground outline-none", "placeholder:text-muted-foreground", "resize-none max-h-20") }), _jsx(ThreadPrimitive.If, { running: true, children: _jsx(ComposerPrimitive.Cancel, { asChild: true, children: _jsx(Button, { variant: "secondary", size: "icon", className: "rounded-full size-8 shrink-0", "aria-label": "Stop", children: _jsx(Square, { className: "size-3" }) }) }) }), _jsx(ThreadPrimitive.If, { running: false, children: _jsx(ComposerPrimitive.Send, { asChild: true, children: _jsx(Button, { size: "icon", className: "rounded-full size-8 shrink-0", "aria-label": "Send", children: _jsx(ArrowUp, { className: "size-4" }) }) }) })] })] }) })] })] })] }))] }));
105
106
  }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Chat extension slots — module-injectable UI anchored to the chat chrome.
3
+ *
4
+ * Modules (via ``bootstrapShell``) sometimes need to render inside the
5
+ * chat surfaces the shell owns — e.g. a background-task progress strip
6
+ * pinned above the composer, or an activity badge on the collapsed chat
7
+ * launcher. Threading those through props would couple the shell to
8
+ * feature modules, so this is a tiny registry instead: a module calls
9
+ * :func:`registerChatSlots` once (import time or ``useInit``) and the
10
+ * shell's chat components render the slots wherever they belong.
11
+ *
12
+ * Render points:
13
+ * - ``composerBanner`` — directly above the composer in BOTH the full
14
+ * chat (``ChatContent``) and the floating bubble panel. It renders
15
+ * inside a fixed (non-scrolling) footer, so it never scrolls away
16
+ * with the thread.
17
+ * - ``launcherBadge`` — inside the collapsed chat-bubble launcher's
18
+ * wrapper (a positioned ``group`` container around the round button),
19
+ * so a badge can position itself absolutely over the button and use
20
+ * ``group-hover:`` styles for a hover flyout.
21
+ */
22
+ import type { ComponentType } from "react";
23
+ interface ChatSlotsState {
24
+ composerBanner: ComponentType | null;
25
+ launcherBadge: ComponentType | null;
26
+ }
27
+ export declare const useChatSlotsStore: import("zustand").UseBoundStore<import("zustand").StoreApi<ChatSlotsState>>;
28
+ /**
29
+ * Register chat slot components. Later calls overwrite only the keys
30
+ * they pass, so independent modules can each claim a different slot.
31
+ * Explicit ``null`` clears a slot; ``undefined`` values are ignored so
32
+ * an optional-spread caller can't corrupt the state shape.
33
+ */
34
+ export declare function registerChatSlots(slots: Partial<ChatSlotsState>): void;
35
+ export declare function ChatComposerBannerSlot(): import("react/jsx-runtime").JSX.Element | null;
36
+ export declare function ChatLauncherBadgeSlot(): import("react/jsx-runtime").JSX.Element | null;
37
+ export {};
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { create } from "zustand";
3
+ export const useChatSlotsStore = create(() => ({
4
+ composerBanner: null,
5
+ launcherBadge: null,
6
+ }));
7
+ /**
8
+ * Register chat slot components. Later calls overwrite only the keys
9
+ * they pass, so independent modules can each claim a different slot.
10
+ * Explicit ``null`` clears a slot; ``undefined`` values are ignored so
11
+ * an optional-spread caller can't corrupt the state shape.
12
+ */
13
+ export function registerChatSlots(slots) {
14
+ const next = {};
15
+ if (slots.composerBanner !== undefined)
16
+ next.composerBanner = slots.composerBanner;
17
+ if (slots.launcherBadge !== undefined)
18
+ next.launcherBadge = slots.launcherBadge;
19
+ useChatSlotsStore.setState(next);
20
+ }
21
+ export function ChatComposerBannerSlot() {
22
+ const Banner = useChatSlotsStore((s) => s.composerBanner);
23
+ return Banner ? _jsx(Banner, {}) : null;
24
+ }
25
+ export function ChatLauncherBadgeSlot() {
26
+ const Badge = useChatSlotsStore((s) => s.launcherBadge);
27
+ return Badge ? _jsx(Badge, {}) : null;
28
+ }
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ export type { AGUIChatConversationFactoryArgs, AGUIHistoryAdapterFactory, } from
8
8
  export { ChatContent, DEFAULT_STARTER_SUGGESTIONS } from "./components/assistant-chat.js";
9
9
  export type { ChatContentProps } from "./components/assistant-chat.js";
10
10
  export { ChatBubble } from "./components/chat-bubble.js";
11
+ export { registerChatSlots, useChatSlotsStore, ChatComposerBannerSlot, ChatLauncherBadgeSlot, } from "./components/chat-slots.js";
11
12
  export { ChatHeader } from "./components/chat-header.js";
12
13
  export { ComposerAttachment, UserMessageAttachment, userAttachmentComponents, composerAttachmentComponents, } from "./components/chat-attachments.js";
13
14
  export { ShowDocumentToolUI } from "./components/show-document-tool-ui.js";
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ export { ClientToolExecutor } from "./components/client-tool-executor.js";
7
7
  export { AGUIRuntimeProvider, useAGUIAdapter } from "./components/ag-ui-runtime-provider.js";
8
8
  export { ChatContent, DEFAULT_STARTER_SUGGESTIONS } from "./components/assistant-chat.js";
9
9
  export { ChatBubble } from "./components/chat-bubble.js";
10
+ export { registerChatSlots, useChatSlotsStore, ChatComposerBannerSlot, ChatLauncherBadgeSlot, } from "./components/chat-slots.js";
10
11
  export { ChatHeader } from "./components/chat-header.js";
11
12
  export { ComposerAttachment, UserMessageAttachment, userAttachmentComponents, composerAttachmentComponents, } from "./components/chat-attachments.js";
12
13
  export { ShowDocumentToolUI } from "./components/show-document-tool-ui.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iloveagents/foundry-web-ui",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -53,8 +53,8 @@
53
53
  "tailwind-merge": "^3.5.0",
54
54
  "react-markdown": "^10.0.0",
55
55
  "remark-gfm": "^4.0.0",
56
- "@iloveagents/foundry-agent": "^0.5.0",
57
- "@iloveagents/foundry-web-primitives": "^0.5.0"
56
+ "@iloveagents/foundry-agent": "^0.6.0",
57
+ "@iloveagents/foundry-web-primitives": "^0.6.0"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@ag-ui/client": "^0.0.52",