@iloveagents/foundry-web-ui 0.27.1 → 0.28.1

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,22 +9,18 @@ import { Button } from "@iloveagents/foundry-web-primitives";
9
9
  export const UserMenu = () => {
10
10
  const { user, signOut } = useStore(authStore, useShallow((s) => ({ user: s.user, signOut: s.signOut })));
11
11
  let preview = false;
12
- if (import.meta.env.DEV) {
13
- try {
14
- const choice = new URLSearchParams(window.location.search).get("ux");
15
- preview =
16
- choice === "workbench" ||
17
- (choice !== "classic" && sessionStorage.getItem("foundry.workbench-preview") === "on");
18
- }
19
- catch {
20
- /* Storage may be unavailable in embedded browsers. */
21
- }
12
+ try {
13
+ const choice = new URLSearchParams(window.location.search).get("ux");
14
+ preview =
15
+ choice === "workbench" ||
16
+ (choice !== "classic" && sessionStorage.getItem("foundry.workbench-preview") === "on");
22
17
  }
23
- if (!user)
24
- return null;
25
- return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "sm", className: "gap-2 rounded-full px-2", children: [user.avatar ? (_jsx("img", { src: user.avatar, alt: user.name, className: "size-6 rounded-full" })) : (_jsx("div", { className: cn("size-6 rounded-full", "bg-primary/10 text-primary", "flex items-center justify-center"), children: _jsx(User, { className: "size-3.5" }) })), _jsx("span", { className: "text-xs text-muted-foreground hidden sm:inline", children: user.name })] }) }), _jsxs(DropdownMenuContent, { align: "end", className: "w-56", children: [_jsx(DropdownMenuLabel, { className: "font-normal", children: _jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("p", { className: "text-sm font-medium", children: user.name }), _jsx("p", { className: "text-xs text-muted-foreground", children: user.email })] }) }), _jsx(DropdownMenuSeparator, {}), import.meta.env.DEV && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuCheckboxItem, { checked: preview, onCheckedChange: (enabled) => {
26
- const url = new URL(window.location.href);
27
- url.searchParams.set("ux", enabled ? "workbench" : "classic");
28
- window.location.assign(url.href);
29
- }, children: "UI preview" }), _jsx(DropdownMenuSeparator, {})] })), _jsxs(DropdownMenuItem, { onClick: signOut, className: "text-destructive cursor-pointer", children: [_jsx(LogOut, { className: "size-4" }), "Sign out"] })] })] }));
18
+ catch {
19
+ /* Storage may be unavailable in embedded browsers. */
20
+ }
21
+ return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "sm", className: "gap-2 rounded-full px-2", "aria-label": user ? "User menu" : "Interface menu", children: [user?.avatar ? (_jsx("img", { src: user.avatar, alt: user.name, className: "size-6 rounded-full" })) : (_jsx("div", { className: cn("size-6 rounded-full", "bg-primary/10 text-primary", "flex items-center justify-center"), children: _jsx(User, { className: "size-3.5" }) })), user && (_jsx("span", { className: "text-xs text-muted-foreground hidden sm:inline", children: user.name }))] }) }), _jsxs(DropdownMenuContent, { align: "end", className: "w-56", children: [user && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuLabel, { className: "font-normal", children: _jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("p", { className: "text-sm font-medium", children: user.name }), _jsx("p", { className: "text-xs text-muted-foreground", children: user.email })] }) }), _jsx(DropdownMenuSeparator, {})] })), _jsx(DropdownMenuCheckboxItem, { checked: preview, onCheckedChange: (enabled) => {
22
+ const url = new URL(window.location.href);
23
+ url.searchParams.set("ux", enabled ? "workbench" : "classic");
24
+ window.location.assign(url.href);
25
+ }, children: "UI preview" }), _jsx(DropdownMenuSeparator, {}), user && (_jsxs(DropdownMenuItem, { onClick: signOut, className: "text-destructive cursor-pointer", children: [_jsx(LogOut, { className: "size-4" }), "Sign out"] }))] })] }));
30
26
  };
@@ -1,6 +1,7 @@
1
1
  import { useChatWidth } from "./use-chat-width.js";
2
- export declare function WorkbenchChatHeader({ collapsed, chatWidth, buttonClass, toggleNavigation, setCollapsed, showHistory, startChat, returnToChat, openHistory, chatOnly, setContentCollapsed, }: {
2
+ export declare function WorkbenchChatHeader({ collapsed, running, chatWidth, buttonClass, toggleNavigation, setCollapsed, showHistory, startChat, returnToChat, openHistory, chatOnly, setContentCollapsed, }: {
3
3
  collapsed: boolean;
4
+ running: boolean;
4
5
  chatWidth: ReturnType<typeof useChatWidth>;
5
6
  buttonClass: string;
6
7
  toggleNavigation: () => void;
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { TooltipIconButton } from "../index.js";
3
- import { SquarePen, PanelLeft, MessageSquare, Menu, History } from "lucide-react";
4
- export function WorkbenchChatHeader({ collapsed, chatWidth, buttonClass, toggleNavigation, setCollapsed, showHistory, startChat, returnToChat, openHistory, chatOnly, setContentCollapsed, }) {
5
- 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: "Expand chat", className: buttonClass, "aria-label": "Expand chat", onClick: () => setCollapsed(false), children: _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(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(History, { size: 18 }) }), _jsx(TooltipIconButton, { tooltip: "Collapse chat", style: chatOnly || chatWidth.compact ? { display: "none" } : undefined, className: buttonClass, "aria-label": "Collapse chat", onClick: () => {
3
+ import { SquarePen, PanelLeft, MessageSquare, Menu, History, LoaderCircle } from "lucide-react";
4
+ export function WorkbenchChatHeader({ collapsed, running, chatWidth, buttonClass, toggleNavigation, setCollapsed, showHistory, startChat, returnToChat, openHistory, chatOnly, setContentCollapsed, }) {
5
+ 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(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(History, { size: 18 }) }), _jsx(TooltipIconButton, { tooltip: "Collapse chat", style: chatOnly || chatWidth.compact ? { display: "none" } : undefined, className: buttonClass, "aria-label": "Collapse chat", onClick: () => {
6
6
  setContentCollapsed(false);
7
7
  setCollapsed(true);
8
8
  }, 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: () => {
@@ -12,6 +12,7 @@ export declare function useWorkbenchState(chatRoute: boolean): {
12
12
  compact: boolean;
13
13
  };
14
14
  collapsed: boolean;
15
+ running: boolean;
15
16
  setCollapsed: import("react").Dispatch<import("react").SetStateAction<boolean>>;
16
17
  compactPane: "content" | "chat";
17
18
  setCompactPane: import("react").Dispatch<import("react").SetStateAction<"content" | "chat">>;
@@ -73,6 +73,7 @@ export function useWorkbenchState(chatRoute) {
73
73
  // the same address again. Collapsing remains local until the next entry.
74
74
  }, [chatOnly, location.key]);
75
75
  const [historyOpen, setHistoryOpen] = useState(false);
76
+ const running = useAuiState((s) => s.thread.isRunning);
76
77
  const emptyChat = useAuiState((s) => s.thread.messages.length === 0 && !s.thread.isRunning && !s.thread.isLoading);
77
78
  const wasEmptyChat = useRef(emptyChat);
78
79
  useEffect(() => {
@@ -122,6 +123,7 @@ export function useWorkbenchState(chatRoute) {
122
123
  pageLabel,
123
124
  chatWidth,
124
125
  collapsed,
126
+ running,
125
127
  setCollapsed,
126
128
  compactPane,
127
129
  setCompactPane,
@@ -8,14 +8,14 @@ import { useWorkbenchState } from "./use-workbench-state.js";
8
8
  import { WorkbenchStyles } from "./workbench-styles.js";
9
9
  import { Button } from "@iloveagents/foundry-web-primitives";
10
10
  import { ChatContent, TooltipIconButton } from "../index.js";
11
- import { FileText, MessageSquare, Menu } from "lucide-react";
11
+ import { FileText, MessageSquare, Menu, LoaderCircle } from "lucide-react";
12
12
  import { ConversationHistory } from "./conversation-history.js";
13
13
  export function WorkbenchPreview({ page, chatOnly: chatRoute = false, }) {
14
- const { panelOpen, chatOnly, pageLabel, chatWidth, collapsed, setCollapsed, compactPane, setCompactPane, contentCollapsed, setContentCollapsed, contentHidden, historyOpen, emptyChat, showHistory, spaciousChat, returnToChat, openHistory, historyShortcut, toggleNavigation, navGroups, workspaceGroups, staticNavigationGroups, closePanel, startChat, } = useWorkbenchState(chatRoute);
14
+ const { panelOpen, chatOnly, pageLabel, chatWidth, collapsed, running, setCollapsed, compactPane, setCompactPane, contentCollapsed, setContentCollapsed, contentHidden, historyOpen, emptyChat, showHistory, spaciousChat, returnToChat, openHistory, historyShortcut, toggleNavigation, navGroups, workspaceGroups, staticNavigationGroups, closePanel, startChat, } = useWorkbenchState(chatRoute);
15
15
  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";
16
16
  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"]
17
17
  .filter((pane) => !chatOnly || pane === "chat")
18
- .map((pane) => (_jsxs(Button, { variant: "ghost", className: "h-11 gap-1.5 px-3 aria-pressed:bg-muted", "aria-pressed": chatOnly || compactPane === pane, "aria-controls": pane === "chat" ? "preview-assistant" : "preview-content", onClick: () => setCompactPane(pane), children: [pane === "chat" ? (_jsx(MessageSquare, { className: "size-4" })) : (_jsx(FileText, { className: "size-4" })), pane === "chat" ? "Chat" : "Content"] }, pane)))] })), _jsxs("div", { ref: chatWidth.containerRef, className: "relative flex min-h-0 min-w-0 flex-1 overflow-x-auto", children: [_jsxs("section", { id: "preview-assistant", "data-panel-motion": true, "data-global-chat": spaciousChat || undefined, "data-chat-collapsed": collapsed ? "true" : undefined, inert: chatWidth.compact && !chatOnly && compactPane !== "chat", "aria-label": "Assistant conversation", className: "relative flex min-h-0 shrink-0 flex-col", style: {
18
+ .map((pane) => (_jsxs(Button, { variant: "ghost", className: "h-11 gap-1.5 px-3 aria-pressed:bg-muted", "aria-pressed": chatOnly || compactPane === pane, "aria-controls": pane === "chat" ? "preview-assistant" : "preview-content", "aria-label": pane === "chat" && running ? "Chat — AI is working" : undefined, onClick: () => setCompactPane(pane), children: [pane === "chat" ? (running ? (_jsx(LoaderCircle, { className: "size-4 animate-spin motion-reduce:animate-none text-primary" })) : (_jsx(MessageSquare, { className: "size-4" }))) : (_jsx(FileText, { className: "size-4" })), pane === "chat" ? "Chat" : "Content"] }, pane)))] })), _jsxs("div", { ref: chatWidth.containerRef, className: "relative flex min-h-0 min-w-0 flex-1 overflow-x-auto", children: [_jsxs("section", { id: "preview-assistant", "data-panel-motion": true, "data-global-chat": spaciousChat || undefined, "data-chat-collapsed": collapsed ? "true" : undefined, inert: chatWidth.compact && !chatOnly && compactPane !== "chat", "aria-label": "Assistant conversation", className: "relative flex min-h-0 shrink-0 flex-col", style: {
19
19
  display: chatWidth.compact && !chatOnly && compactPane !== "chat" ? "none" : "flex",
20
20
  width: chatWidth.compact || chatOnly
21
21
  ? "100%"
@@ -25,7 +25,7 @@ export function WorkbenchPreview({ page, chatOnly: chatRoute = false, }) {
25
25
  ? "calc(100% - 44px)"
26
26
  : chatWidth.width,
27
27
  background: "var(--background)",
28
- }, children: [_jsx(WorkbenchChatHeader, { collapsed: collapsed, 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: workspaceGroups?.[0]?.meta?.exclusiveRootId, onClose: returnToChat, showBack: true })), !chatWidth.compact && !collapsed && !contentCollapsed && (_jsx(ChatResizeHandle, { chatWidth: chatWidth })), _jsx("div", { className: "min-h-0 flex-1 flex-col", style: {
28
+ }, 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: workspaceGroups?.[0]?.meta?.exclusiveRootId, onClose: returnToChat, showBack: true })), !chatWidth.compact && !collapsed && !contentCollapsed && (_jsx(ChatResizeHandle, { chatWidth: chatWidth })), _jsx("div", { className: "min-h-0 flex-1 flex-col", style: {
29
29
  display: collapsed || showHistory ? "none" : "flex",
30
30
  paddingBottom: spaciousChat && emptyChat && !historyOpen ? "clamp(32px, 10vh, 96px)" : undefined,
31
31
  }, children: _jsx(ChatContent, { centerComposer: spaciousChat && emptyChat && !historyOpen, composerIntro: spaciousChat && emptyChat ? (_jsx(WelcomeIntro, { navGroups: navGroups, historyShortcut: historyShortcut })) : undefined, compactComposer: !spaciousChat, hideCurrentPageBadge: true, showPinButton: false, starterSuggestions: [], emptyState: _jsxs("div", { className: "flex min-h-full flex-col justify-center gap-3 px-6 py-8", children: [_jsx("h1", { className: "text-lg font-semibold tracking-tight", 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." }), historyShortcut] }) }) })] }), _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 }) }) }) }) }))] })] })] }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iloveagents/foundry-web-ui",
3
- "version": "0.27.1",
3
+ "version": "0.28.1",
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.27.1",
84
- "@iloveagents/foundry-web-primitives": "^0.27.1"
83
+ "@iloveagents/foundry-agent": "^0.28.1",
84
+ "@iloveagents/foundry-web-primitives": "^0.28.1"
85
85
  },
86
86
  "devDependencies": {
87
87
  "@ag-ui/client": "^0.0.52",