@cntyclub/agent-react 0.9.2 → 0.10.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.
@@ -12,11 +12,13 @@ export interface AgentPanelProps {
12
12
  onClose: () => void;
13
13
  /** Remember a write tool and auto-approve its future prompts. */
14
14
  onAlwaysApprove: (toolName: string) => void;
15
+ /** Message id to render a "New messages" divider before (arrived while closed). */
16
+ newMessageAnchorId?: string | null;
15
17
  className?: string;
16
18
  }
17
19
  /**
18
20
  * The chat surface shared by the popup panel and fullscreen Agent Mode.
19
21
  * Header (title, history, new chat, expand/collapse, close) + messages + input.
20
22
  */
21
- export declare function AgentPanel({ agentMode, canToggleAgentMode, chat, className, config, onAlwaysApprove, onClose, onToggleAgentMode, }: AgentPanelProps): React.JSX.Element;
23
+ export declare function AgentPanel({ agentMode, canToggleAgentMode, chat, className, config, newMessageAnchorId, onAlwaysApprove, onClose, onToggleAgentMode, }: AgentPanelProps): React.JSX.Element;
22
24
  //# sourceMappingURL=agent-panel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-panel.d.ts","sourceRoot":"","sources":["../../src/components/agent-panel.tsx"],"names":[],"mappings":"AA8BA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAQ5C,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,cAAc,GAAG,gBAAgB,CAAC;IACxC,yCAAyC;IACzC,SAAS,EAAE,OAAO,CAAC;IACnB,4DAA4D;IAC5D,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAC9B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,iEAAiE;IACjE,eAAe,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,EACzB,SAAS,EACT,kBAAkB,EAClB,IAAI,EACJ,SAAS,EACT,MAAM,EACN,eAAe,EACf,OAAO,EACP,iBAAiB,GAClB,EAAE,eAAe,qBAkTjB"}
1
+ {"version":3,"file":"agent-panel.d.ts","sourceRoot":"","sources":["../../src/components/agent-panel.tsx"],"names":[],"mappings":"AA+BA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAQ5C,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,cAAc,GAAG,gBAAgB,CAAC;IACxC,yCAAyC;IACzC,SAAS,EAAE,OAAO,CAAC;IACnB,4DAA4D;IAC5D,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAC9B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,iEAAiE;IACjE,eAAe,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,EACzB,SAAS,EACT,kBAAkB,EAClB,IAAI,EACJ,SAAS,EACT,MAAM,EACN,kBAAkB,EAClB,eAAe,EACf,OAAO,EACP,iBAAiB,GAClB,EAAE,eAAe,qBAsUjB"}
@@ -9,15 +9,15 @@ export interface AgentWidgetProps {
9
9
  /**
10
10
  * The embeddable Agent Mode widget.
11
11
  *
12
- * - Closed: a floating round button (bottom-right by default).
13
- * - Open on desktop: a popup panel anchored to the button, with an
14
- * "Agent Mode" button that expands to a fullscreen chat. While in the popup,
15
- * the widget can navigate the host app to pages related to the tools the
16
- * agent uses (via `config.pages` + `config.navigate`).
17
- * - Open on mobile: fullscreen from the start.
12
+ * - Closed: a floating round button. While closed, it surfaces what the agent
13
+ * is doing a live "Thinking… / Calling tools…" ticker, an unread badge when
14
+ * a reply finished, and an "approval needed" chip when a write is waiting.
15
+ * - Open on desktop: a popup panel that can expand to fullscreen ("Agent Mode").
16
+ * - Open on mobile: fullscreen.
18
17
  *
19
- * All colors come from the host app's UI-kit theme tokens, so light/dark mode
20
- * follows the dashboard automatically.
18
+ * Open/close and fullscreen transitions are animated (scale from the launcher
19
+ * corner; size morph between popup and fullscreen). All colors come from the
20
+ * host app's UI-kit theme tokens.
21
21
  */
22
22
  export declare function AgentWidget({ config, onOpenChange, open: controlledOpen }: AgentWidgetProps): React.JSX.Element;
23
23
  //# sourceMappingURL=agent-widget.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-widget.d.ts","sourceRoot":"","sources":["../../src/components/agent-widget.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,WAAW,EAAgB,MAAM,UAAU,CAAC;AAK1D,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,WAAW,CAAC;IACpB,wCAAwC;IACxC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,gBAAgB,qBAkI3F"}
1
+ {"version":3,"file":"agent-widget.d.ts","sourceRoot":"","sources":["../../src/components/agent-widget.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,WAAW,EAAgB,MAAM,UAAU,CAAC;AAK1D,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,WAAW,CAAC;IACpB,wCAAwC;IACxC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC;AAQD;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,gBAAgB,qBAoQ3F"}
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
- import { Button, cn, Tooltip, TooltipTrigger, TooltipContent, ChatMessage, ChatMessageBubble, Markdown, Spinner, Checkbox, Collapsible, CollapsibleTrigger, ChatToolChip, CollapsiblePanel, ChatToolCard, ChatToolCardHeader, ChatToolCardActions, Group, Menu, MenuTrigger, MenuPopup, MenuItem, ChatHeader, ChatHeaderAvatar, ChatHeaderTitle, ChatHeaderActions, Chat, ChatEmptyState, ChatEmptyStateIcon, ChatEmptyStateTitle, ChatEmptyStateDescription, ChatSuggestions, ChatSuggestion, useMediaQuery, TooltipProvider, Fab, DataTablePaged } from '@cntyclub/ui-react';
1
+ import { Button, cn, Tooltip, TooltipTrigger, TooltipContent, ChatMessage, ChatMessageBubble, Markdown, Spinner, Checkbox, Collapsible, CollapsibleTrigger, ChatToolChip, CollapsiblePanel, ChatToolCard, ChatToolCardHeader, ChatToolCardActions, Group, Menu, MenuTrigger, MenuPopup, MenuItem, ChatHeader, ChatHeaderAvatar, ChatHeaderTitle, ChatHeaderActions, Chat, ChatEmptyState, ChatEmptyStateIcon, ChatEmptyStateTitle, ChatEmptyStateDescription, ChatSuggestions, ChatSuggestion, useMediaQuery, TooltipProvider, DataTablePaged } from '@cntyclub/ui-react';
2
2
  import { FileTextIcon, XIcon, PaperclipIcon, ArrowUpIcon, CheckIcon, CopyIcon, Maximize2Icon, ListChecksIcon, ChevronDownIcon, ShieldAlertIcon, CheckCheckIcon, UploadCloudIcon, HistoryIcon, SquarePenIcon, Minimize2Icon, Trash2Icon, SparklesIcon, DownloadIcon, CodeIcon } from 'lucide-react';
3
3
  import * as React8 from 'react';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
+ import { motion, AnimatePresence } from 'motion/react';
5
6
 
6
7
  // src/api-client.ts
7
8
  var AgentApiError = class extends Error {
@@ -1042,6 +1043,7 @@ function AgentPanel({
1042
1043
  chat,
1043
1044
  className,
1044
1045
  config,
1046
+ newMessageAnchorId,
1045
1047
  onAlwaysApprove,
1046
1048
  onClose,
1047
1049
  onToggleAgentMode
@@ -1262,9 +1264,30 @@ function AgentPanel({
1262
1264
  suggestion
1263
1265
  )) })
1264
1266
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1265
- groupMessages(chat.messages).map(
1266
- (item) => item.kind === "tool-activity" ? /* @__PURE__ */ jsx(ChatMessage, { from: "assistant", children: /* @__PURE__ */ jsx(ToolActivityGroup, { describeToolCall: config.describeToolCall, steps: item.steps }) }, item.key) : /* @__PURE__ */ jsx(MessageItem, { message: item.message }, item.key)
1267
- ),
1267
+ (() => {
1268
+ const grouped = groupMessages(chat.messages);
1269
+ const anchorKey = newMessageAnchorId ? grouped.find(
1270
+ (it) => it.kind !== "tool-activity" && it.message.id === newMessageAnchorId
1271
+ )?.key : void 0;
1272
+ return grouped.map((item) => /* @__PURE__ */ jsxs(React8.Fragment, { children: [
1273
+ item.key === anchorKey ? /* @__PURE__ */ jsx(NewMessagesDivider, {}) : null,
1274
+ /* @__PURE__ */ jsx(
1275
+ motion.div,
1276
+ {
1277
+ initial: { opacity: 0, y: 8 },
1278
+ animate: { opacity: 1, y: 0 },
1279
+ transition: { duration: 0.18, ease: "easeOut" },
1280
+ children: item.kind === "tool-activity" ? /* @__PURE__ */ jsx(ChatMessage, { from: "assistant", children: /* @__PURE__ */ jsx(
1281
+ ToolActivityGroup,
1282
+ {
1283
+ describeToolCall: config.describeToolCall,
1284
+ steps: item.steps
1285
+ }
1286
+ ) }) : /* @__PURE__ */ jsx(MessageItem, { message: item.message })
1287
+ }
1288
+ )
1289
+ ] }, item.key));
1290
+ })(),
1268
1291
  chat.pendingApprovals.map((approval) => /* @__PURE__ */ jsx(
1269
1292
  ToolApprovalCard,
1270
1293
  {
@@ -1314,6 +1337,13 @@ function AgentPanel({
1314
1337
  }
1315
1338
  );
1316
1339
  }
1340
+ function NewMessagesDivider() {
1341
+ return /* @__PURE__ */ jsxs("div", { className: "my-1 flex items-center gap-2", "aria-label": "New messages", children: [
1342
+ /* @__PURE__ */ jsx("span", { className: "h-px flex-1 bg-border" }),
1343
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] font-semibold uppercase tracking-wider text-muted-foreground", children: "New messages" }),
1344
+ /* @__PURE__ */ jsx("span", { className: "h-px flex-1 bg-border" })
1345
+ ] });
1346
+ }
1317
1347
  var OPTIMISTIC_ID_PREFIX = "optimistic-";
1318
1348
  var STREAMING_ID_PREFIX = "streaming-";
1319
1349
  var optimisticCounter = 0;
@@ -1515,8 +1545,10 @@ function useAgentChat(config) {
1515
1545
  try {
1516
1546
  const { conversations: list } = await client.listConversations();
1517
1547
  setConversations(list);
1548
+ return list;
1518
1549
  } catch (err) {
1519
1550
  handleFailure(err);
1551
+ return [];
1520
1552
  }
1521
1553
  }, [client, handleFailure]);
1522
1554
  const openConversation = React8.useCallback(
@@ -1616,45 +1648,109 @@ function useAlwaysApprove(clientId) {
1616
1648
  );
1617
1649
  return { clearAlwaysApprove, isAlwaysApproved, setAlwaysApprove };
1618
1650
  }
1651
+ function isUnreadable(message) {
1652
+ if (message.id.startsWith("streaming-")) return false;
1653
+ return message.role === "assistant" || message.role === "tool";
1654
+ }
1619
1655
  function AgentWidget({ config, onOpenChange, open: controlledOpen }) {
1656
+ const behavior = config.behavior ?? {};
1657
+ const position = config.appearance?.position ?? "bottom-right";
1658
+ const isControlled = controlledOpen !== void 0;
1659
+ const storageKey2 = `cc-agent-open:${config.clientId}`;
1620
1660
  const [uncontrolledOpen, setUncontrolledOpen] = React8.useState(false);
1621
1661
  const open = controlledOpen ?? uncontrolledOpen;
1662
+ const persist = React8.useCallback(
1663
+ (next) => {
1664
+ if (isControlled || !behavior.persistOpenState) return;
1665
+ try {
1666
+ window.localStorage.setItem(storageKey2, next ? "1" : "0");
1667
+ } catch {
1668
+ }
1669
+ },
1670
+ [behavior.persistOpenState, isControlled, storageKey2]
1671
+ );
1622
1672
  const setOpen = React8.useCallback(
1623
1673
  (next) => {
1624
1674
  setUncontrolledOpen(next);
1675
+ persist(next);
1625
1676
  onOpenChange?.(next);
1626
1677
  },
1627
- [onOpenChange]
1678
+ [onOpenChange, persist]
1628
1679
  );
1629
1680
  const [agentMode, setAgentMode] = React8.useState(false);
1630
- const isDesktop = useMediaQuery("(min-width: 640px)", {
1631
- defaultSSRValue: true,
1632
- ssr: true
1633
- });
1681
+ const isDesktop = useMediaQuery("(min-width: 640px)", { defaultSSRValue: true, ssr: true });
1634
1682
  const fullscreen = !isDesktop || agentMode;
1635
1683
  const chat = useAgentChat(config);
1636
1684
  const alwaysApprove = useAlwaysApprove(config.clientId);
1637
- const { pendingApprovals, resolveApproval, resolvingApprovalId } = chat;
1685
+ const { messages, pendingApprovals, resolveApproval, resolvingApprovalId, sending } = chat;
1686
+ const bootRef = React8.useRef(false);
1687
+ React8.useEffect(() => {
1688
+ if (isControlled || bootRef.current) return;
1689
+ bootRef.current = true;
1690
+ let cancelled = false;
1691
+ void (async () => {
1692
+ if (behavior.openLastConversation !== false) {
1693
+ const list = await chat.refreshConversations();
1694
+ if (cancelled) return;
1695
+ if (list.length) await chat.openConversation(list[0].id);
1696
+ if (cancelled) return;
1697
+ }
1698
+ let shouldOpen = behavior.initialOpen ?? false;
1699
+ if (behavior.persistOpenState) {
1700
+ try {
1701
+ const stored = window.localStorage.getItem(storageKey2);
1702
+ if (stored !== null) shouldOpen = stored === "1";
1703
+ } catch {
1704
+ }
1705
+ }
1706
+ if (shouldOpen) {
1707
+ setUncontrolledOpen(true);
1708
+ onOpenChange?.(true);
1709
+ }
1710
+ })();
1711
+ return () => {
1712
+ cancelled = true;
1713
+ };
1714
+ }, []);
1715
+ const seenCountRef = React8.useRef(0);
1716
+ const [anchorId, setAnchorId] = React8.useState(null);
1717
+ React8.useEffect(() => {
1718
+ if (open) {
1719
+ if (messages.length > seenCountRef.current) {
1720
+ const firstUnread = messages[seenCountRef.current];
1721
+ setAnchorId(firstUnread && isUnreadable(firstUnread) ? firstUnread.id : null);
1722
+ }
1723
+ seenCountRef.current = messages.length;
1724
+ }
1725
+ }, [open, messages]);
1726
+ const unreadCount = open ? 0 : messages.slice(seenCountRef.current).filter(isUnreadable).length;
1727
+ const workLabel = React8.useMemo(() => {
1728
+ if (!sending) return null;
1729
+ const last = messages[messages.length - 1];
1730
+ if (last?.role === "tool") return "Calling tools\u2026";
1731
+ if (last?.role === "assistant" && (last.tool_calls?.length ?? 0) > 0) return "Calling tools\u2026";
1732
+ return "Thinking\u2026";
1733
+ }, [sending, messages]);
1734
+ const approvalWaiting = pendingApprovals.length > 0;
1638
1735
  const { isAlwaysApproved } = alwaysApprove;
1639
1736
  React8.useEffect(() => {
1640
- if (!open || resolvingApprovalId) return;
1737
+ if (resolvingApprovalId) return;
1641
1738
  const target = pendingApprovals.find((approval) => isAlwaysApproved(approval.tool_name));
1642
1739
  if (target) void resolveApproval(target.id, true);
1643
- }, [open, pendingApprovals, resolvingApprovalId, isAlwaysApproved, resolveApproval]);
1740
+ }, [pendingApprovals, resolvingApprovalId, isAlwaysApproved, resolveApproval]);
1644
1741
  const firedWriteIds = React8.useRef(/* @__PURE__ */ new Set());
1645
1742
  const { onWriteSuccess } = config;
1646
1743
  React8.useEffect(() => {
1647
1744
  if (!onWriteSuccess) return;
1648
- for (const message of chat.messages) {
1745
+ for (const message of messages) {
1649
1746
  if (message.role !== "tool" || !message.is_write || message.tool_status !== "success") continue;
1650
1747
  if (firedWriteIds.current.has(message.id)) continue;
1651
1748
  firedWriteIds.current.add(message.id);
1652
- const args = findToolArguments(chat.messages, message);
1749
+ const args = findToolArguments(messages, message);
1653
1750
  onWriteSuccess({ arguments: args, toolName: message.tool_name ?? "" });
1654
1751
  }
1655
- }, [chat.messages, onWriteSuccess]);
1752
+ }, [messages, onWriteSuccess]);
1656
1753
  const lastNavigatedMessageId = React8.useRef(null);
1657
- const { messages } = chat;
1658
1754
  React8.useEffect(() => {
1659
1755
  if (!open || fullscreen || !config.navigate || !config.pages?.length) return;
1660
1756
  const toolMessages = messages.filter((message) => message.role === "tool");
@@ -1671,65 +1767,133 @@ function AgentWidget({ config, onOpenChange, open: controlledOpen }) {
1671
1767
  setOpen(false);
1672
1768
  setAgentMode(false);
1673
1769
  }, [setOpen]);
1770
+ const transformOrigin = position === "bottom-left" ? "bottom left" : "bottom right";
1771
+ const launcherHidden = open || config.appearance?.hideLauncher;
1674
1772
  return /* @__PURE__ */ jsxs(TooltipProvider, { children: [
1675
- !open && !config.appearance?.hideLauncher ? /* @__PURE__ */ jsx(
1676
- Fab,
1773
+ /* @__PURE__ */ jsx(AnimatePresence, { children: !launcherHidden ? /* @__PURE__ */ jsxs(
1774
+ motion.div,
1677
1775
  {
1678
- "aria-label": "Open assistant",
1679
- onClick: () => setOpen(true),
1680
- position: config.appearance?.position ?? "bottom-right",
1681
- children: /* @__PURE__ */ jsx(SparklesIcon, {})
1682
- }
1683
- ) : null,
1684
- open ? fullscreen ? /* @__PURE__ */ jsx(
1685
- "div",
1776
+ initial: { opacity: 0, scale: 0.6 },
1777
+ animate: { opacity: 1, scale: 1 },
1778
+ exit: { opacity: 0, scale: 0.6 },
1779
+ transition: { type: "spring", stiffness: 300, damping: 24 },
1780
+ style: { transformOrigin },
1781
+ className: cn(
1782
+ "fixed z-50 flex flex-col items-end gap-2",
1783
+ position === "bottom-left" ? "bottom-4 left-4 items-start sm:bottom-6 sm:left-6" : "right-4 bottom-4 sm:right-6 sm:bottom-6"
1784
+ ),
1785
+ children: [
1786
+ /* @__PURE__ */ jsx(AnimatePresence, { mode: "popLayout", children: approvalWaiting ? /* @__PURE__ */ jsx(StatusChip, { tone: "warning", pulse: true, children: "Approval needed" }, "approval") : workLabel ? /* @__PURE__ */ jsx(StatusChip, { tone: "muted", children: workLabel }, workLabel) : null }),
1787
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
1788
+ /* @__PURE__ */ jsx(
1789
+ "button",
1790
+ {
1791
+ type: "button",
1792
+ "aria-label": "Open assistant",
1793
+ onClick: () => setOpen(true),
1794
+ className: "flex size-14 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-lg outline-none transition-transform hover:scale-105 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
1795
+ children: /* @__PURE__ */ jsx(SparklesIcon, { className: "size-6" })
1796
+ }
1797
+ ),
1798
+ /* @__PURE__ */ jsx(AnimatePresence, { children: unreadCount > 0 ? /* @__PURE__ */ jsx(
1799
+ motion.span,
1800
+ {
1801
+ initial: { scale: 0 },
1802
+ animate: { scale: 1 },
1803
+ exit: { scale: 0 },
1804
+ transition: { type: "spring", stiffness: 500, damping: 20 },
1805
+ className: "absolute -right-1 -top-1 flex h-5 min-w-5 items-center justify-center rounded-full bg-destructive px-1 text-xs font-semibold text-destructive-foreground shadow",
1806
+ "aria-label": `${unreadCount} new message${unreadCount === 1 ? "" : "s"}`,
1807
+ children: unreadCount > 9 ? "9+" : unreadCount
1808
+ },
1809
+ "badge"
1810
+ ) : null })
1811
+ ] })
1812
+ ]
1813
+ },
1814
+ "agent-launcher"
1815
+ ) : null }),
1816
+ /* @__PURE__ */ jsx(AnimatePresence, { children: open ? /* @__PURE__ */ jsx(
1817
+ motion.div,
1686
1818
  {
1687
- "aria-modal": "true",
1688
- className: "fixed inset-0 z-50 flex flex-col bg-background",
1689
- role: "dialog",
1690
- children: /* @__PURE__ */ jsx(
1691
- AgentPanel,
1692
- {
1693
- agentMode,
1694
- canToggleAgentMode: isDesktop,
1695
- chat,
1696
- className: "flex-1",
1697
- config,
1698
- onAlwaysApprove: alwaysApprove.setAlwaysApprove,
1699
- onClose: closePanel,
1700
- onToggleAgentMode: () => setAgentMode((value) => !value)
1819
+ layout: true,
1820
+ initial: { opacity: 0, scale: 0.9 },
1821
+ animate: { opacity: 1, scale: 1 },
1822
+ exit: { opacity: 0, scale: 0.9 },
1823
+ transition: { type: "spring", stiffness: 260, damping: 28 },
1824
+ style: {
1825
+ transformOrigin,
1826
+ ...fullscreen ? {} : {
1827
+ height: `min(${config.appearance?.panelHeight ?? 640}px, calc(100dvh - 3rem))`,
1828
+ width: `min(${config.appearance?.panelWidth ?? 400}px, calc(100vw - 2rem))`
1701
1829
  }
1702
- )
1703
- }
1704
- ) : /* @__PURE__ */ jsx(
1705
- "div",
1706
- {
1830
+ },
1707
1831
  className: cn(
1708
- "fixed z-50 flex flex-col overflow-hidden rounded-2xl border border-border shadow-2xl",
1709
- (config.appearance?.position ?? "bottom-right") === "bottom-left" ? "bottom-4 left-4 sm:bottom-6 sm:left-6" : "right-4 bottom-4 sm:right-6 sm:bottom-6"
1832
+ "fixed z-50 flex flex-col overflow-hidden bg-background",
1833
+ fullscreen ? "inset-0" : cn(
1834
+ "rounded-2xl border border-border shadow-2xl",
1835
+ position === "bottom-left" ? "bottom-4 left-4 sm:bottom-6 sm:left-6" : "right-4 bottom-4 sm:right-6 sm:bottom-6"
1836
+ )
1710
1837
  ),
1711
1838
  role: "dialog",
1712
- style: {
1713
- height: `min(${config.appearance?.panelHeight ?? 640}px, calc(100dvh - 3rem))`,
1714
- width: `min(${config.appearance?.panelWidth ?? 400}px, calc(100vw - 2rem))`
1715
- },
1839
+ "aria-modal": fullscreen ? "true" : void 0,
1716
1840
  children: /* @__PURE__ */ jsx(
1717
1841
  AgentPanel,
1718
1842
  {
1719
- agentMode: false,
1720
- canToggleAgentMode: true,
1843
+ agentMode: fullscreen && agentMode,
1844
+ canToggleAgentMode: isDesktop,
1721
1845
  chat,
1722
1846
  className: "flex-1",
1723
1847
  config,
1848
+ newMessageAnchorId: anchorId,
1724
1849
  onAlwaysApprove: alwaysApprove.setAlwaysApprove,
1725
1850
  onClose: closePanel,
1726
- onToggleAgentMode: () => setAgentMode(true)
1851
+ onToggleAgentMode: () => setAgentMode((value) => !value)
1727
1852
  }
1728
1853
  )
1729
- }
1730
- ) : null
1854
+ },
1855
+ "agent-panel"
1856
+ ) : null })
1731
1857
  ] });
1732
1858
  }
1859
+ function StatusChip({
1860
+ children,
1861
+ tone,
1862
+ pulse
1863
+ }) {
1864
+ return /* @__PURE__ */ jsxs(
1865
+ motion.div,
1866
+ {
1867
+ layout: true,
1868
+ initial: { opacity: 0, y: 6, scale: 0.9 },
1869
+ animate: { opacity: 1, y: 0, scale: 1 },
1870
+ exit: { opacity: 0, y: 6, scale: 0.9 },
1871
+ transition: { duration: 0.18 },
1872
+ className: cn(
1873
+ "flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs font-medium shadow-sm",
1874
+ tone === "warning" ? "border-warning bg-warning-surface text-warning-foreground" : "border-border bg-card text-secondary-foreground"
1875
+ ),
1876
+ children: [
1877
+ pulse ? /* @__PURE__ */ jsx("span", { className: "size-1.5 rounded-full bg-warning-foreground" }) : /* @__PURE__ */ jsxs("span", { className: "flex gap-0.5", children: [
1878
+ /* @__PURE__ */ jsx(Dot, { delay: 0 }),
1879
+ /* @__PURE__ */ jsx(Dot, { delay: 0.15 }),
1880
+ /* @__PURE__ */ jsx(Dot, { delay: 0.3 })
1881
+ ] }),
1882
+ children
1883
+ ]
1884
+ }
1885
+ );
1886
+ }
1887
+ function Dot({ delay }) {
1888
+ return /* @__PURE__ */ jsx(
1889
+ motion.span,
1890
+ {
1891
+ className: "size-1.5 rounded-full bg-current opacity-60",
1892
+ animate: { opacity: [0.2, 1, 0.2] },
1893
+ transition: { duration: 1, repeat: Infinity, delay }
1894
+ }
1895
+ );
1896
+ }
1733
1897
  function findToolArguments(messages, toolMessage) {
1734
1898
  if (!toolMessage.tool_call_id) return {};
1735
1899
  for (let index = messages.length - 1; index >= 0; index -= 1) {