@cntyclub/agent-react 0.12.0 → 0.14.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.
@@ -14,11 +14,17 @@ export interface AgentPanelProps {
14
14
  onAlwaysApprove: (toolName: string) => void;
15
15
  /** Message id to render a "New messages" divider before (arrived while closed). */
16
16
  newMessageAnchorId?: string | null;
17
+ /**
18
+ * Render the close control as a "collapse to the right" affordance (two right
19
+ * chevrons) instead of an ✕. Used by the docked presentation, where closing
20
+ * collapses the panel to the side rather than dismissing a dialog.
21
+ */
22
+ collapseToSide?: boolean;
17
23
  className?: string;
18
24
  }
19
25
  /**
20
26
  * The chat surface shared by the popup panel and fullscreen Agent Mode.
21
27
  * Header (title, history, new chat, expand/collapse, close) + messages + input.
22
28
  */
23
- export declare function AgentPanel({ agentMode, canToggleAgentMode, chat, className, config, newMessageAnchorId, onAlwaysApprove, onClose, onToggleAgentMode, }: AgentPanelProps): React.JSX.Element;
29
+ export declare function AgentPanel({ agentMode, canToggleAgentMode, chat, className, collapseToSide, config, newMessageAnchorId, onAlwaysApprove, onClose, onToggleAgentMode, }: AgentPanelProps): React.JSX.Element;
24
30
  //# 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":"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,qBAgVjB"}
1
+ {"version":3,"file":"agent-panel.d.ts","sourceRoot":"","sources":["../../src/components/agent-panel.tsx"],"names":[],"mappings":"AAgCA,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;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,EACzB,SAAS,EACT,kBAAkB,EAClB,IAAI,EACJ,SAAS,EACT,cAAc,EACd,MAAM,EACN,kBAAkB,EAClB,eAAe,EACf,OAAO,EACP,iBAAiB,GAClB,EAAE,eAAe,qBAqVjB"}
@@ -5,6 +5,17 @@ export interface AgentWidgetProps {
5
5
  /** Controlled open state (optional). */
6
6
  open?: boolean;
7
7
  onOpenChange?: (open: boolean) => void;
8
+ /**
9
+ * How the panel is presented:
10
+ * - `"floating"` (default): a launcher button + a popup/fullscreen panel,
11
+ * positioned by the widget itself (fixed to a screen corner).
12
+ * - `"docked"`: no launcher; the panel fills its parent element so the host
13
+ * can place it in the layout (e.g. a resizable side pane on desktop, or a
14
+ * fullscreen overlay on smaller screens). Open state is controlled by the
15
+ * host via `open`/`onOpenChange`. Page-follow navigation stays enabled so
16
+ * the agent can drive the main view alongside the chat.
17
+ */
18
+ presentation?: "floating" | "docked";
8
19
  }
9
20
  /**
10
21
  * The embeddable Agent Mode widget.
@@ -19,5 +30,5 @@ export interface AgentWidgetProps {
19
30
  * corner; size morph between popup and fullscreen). All colors come from the
20
31
  * host app's UI-kit theme tokens.
21
32
  */
22
- export declare function AgentWidget({ config, onOpenChange, open: controlledOpen }: AgentWidgetProps): React.JSX.Element;
33
+ export declare function AgentWidget({ config, onOpenChange, open: controlledOpen, presentation, }: AgentWidgetProps): React.JSX.Element;
23
34
  //# 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":"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,qBAqR3F"}
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;IACvC;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,UAAU,GAAG,QAAQ,CAAC;CACtC;AAQD;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,YAAY,EACZ,IAAI,EAAE,cAAc,EACpB,YAAyB,GAC1B,EAAE,gBAAgB,qBAgUlB"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { cn, Button, 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
- import { XIcon, Trash2Icon, SquareIcon, PauseIcon, PlayIcon, CheckIcon, Loader2Icon, FileTextIcon, PaperclipIcon, MicIcon, ArrowUpIcon, CopyIcon, Maximize2Icon, ListChecksIcon, ChevronDownIcon, ShieldAlertIcon, CheckCheckIcon, UploadCloudIcon, HistoryIcon, SquarePenIcon, Minimize2Icon, SparklesIcon, DownloadIcon, CodeIcon } from 'lucide-react';
2
+ import { XIcon, Trash2Icon, SquareIcon, PauseIcon, PlayIcon, CheckIcon, Loader2Icon, FileTextIcon, PaperclipIcon, MicIcon, ArrowUpIcon, CopyIcon, Maximize2Icon, ListChecksIcon, ChevronDownIcon, ShieldAlertIcon, CheckCheckIcon, UploadCloudIcon, HistoryIcon, SquarePenIcon, Minimize2Icon, ChevronsRightIcon, SparklesIcon, DownloadIcon, CodeIcon } from 'lucide-react';
3
3
  import * as React11 from 'react';
4
4
  import { AnimatePresence, motion } from 'motion/react';
5
5
  import { createPortal } from 'react-dom';
@@ -1494,6 +1494,7 @@ function AgentPanel({
1494
1494
  canToggleAgentMode,
1495
1495
  chat,
1496
1496
  className,
1497
+ collapseToSide,
1497
1498
  config,
1498
1499
  newMessageAnchorId,
1499
1500
  onAlwaysApprove,
@@ -1662,7 +1663,16 @@ function AgentPanel({
1662
1663
  ),
1663
1664
  /* @__PURE__ */ jsx(TooltipContent, { children: agentMode ? "Exit Agent Mode" : "Agent Mode" })
1664
1665
  ] }) : null,
1665
- /* @__PURE__ */ jsx(Button, { "aria-label": "Close", onClick: onClose, size: "icon-sm", variant: "ghost", children: /* @__PURE__ */ jsx(XIcon, {}) })
1666
+ /* @__PURE__ */ jsx(
1667
+ Button,
1668
+ {
1669
+ "aria-label": collapseToSide ? "Collapse agent" : "Close",
1670
+ onClick: onClose,
1671
+ size: "icon-sm",
1672
+ variant: "ghost",
1673
+ children: collapseToSide ? /* @__PURE__ */ jsx(ChevronsRightIcon, {}) : /* @__PURE__ */ jsx(XIcon, {})
1674
+ }
1675
+ )
1666
1676
  ] })
1667
1677
  ] }),
1668
1678
  view === "history" ? /* @__PURE__ */ jsx("div", { className: "flex min-h-0 flex-1 flex-col gap-1 overflow-y-auto p-2", children: chat.conversations.length === 0 ? /* @__PURE__ */ jsx("p", { className: "p-4 text-center text-muted-foreground text-sm", children: "No conversations yet." }) : chat.conversations.map((conversation) => /* @__PURE__ */ jsxs(
@@ -2120,10 +2130,16 @@ function isUnreadable(message) {
2120
2130
  if (message.id.startsWith("streaming-")) return false;
2121
2131
  return message.role === "assistant" || message.role === "tool";
2122
2132
  }
2123
- function AgentWidget({ config, onOpenChange, open: controlledOpen }) {
2133
+ function AgentWidget({
2134
+ config,
2135
+ onOpenChange,
2136
+ open: controlledOpen,
2137
+ presentation = "floating"
2138
+ }) {
2124
2139
  const behavior = config.behavior ?? {};
2125
2140
  const position = config.appearance?.position ?? "bottom-right";
2126
2141
  const isControlled = controlledOpen !== void 0;
2142
+ const isDocked = presentation === "docked";
2127
2143
  const storageKey2 = `cc-agent-open:${config.clientId}`;
2128
2144
  const [uncontrolledOpen, setUncontrolledOpen] = React11.useState(false);
2129
2145
  const open = controlledOpen ?? uncontrolledOpen;
@@ -2154,7 +2170,8 @@ function AgentWidget({ config, onOpenChange, open: controlledOpen }) {
2154
2170
  const { messages, pendingApprovals, resolveApproval, resolvingApprovalId, sending } = chat;
2155
2171
  const bootRef = React11.useRef(false);
2156
2172
  React11.useEffect(() => {
2157
- if (isControlled || bootRef.current) return;
2173
+ if (bootRef.current) return;
2174
+ if (isControlled && !isDocked) return;
2158
2175
  bootRef.current = true;
2159
2176
  let cancelled = false;
2160
2177
  void (async () => {
@@ -2164,17 +2181,19 @@ function AgentWidget({ config, onOpenChange, open: controlledOpen }) {
2164
2181
  if (list.length) await chat.openConversation(list[0].id);
2165
2182
  if (cancelled) return;
2166
2183
  }
2167
- let shouldOpen = behavior.initialOpen ?? false;
2168
- if (behavior.persistOpenState) {
2169
- try {
2170
- const stored = window.localStorage.getItem(storageKey2);
2171
- if (stored !== null) shouldOpen = stored === "1";
2172
- } catch {
2184
+ if (!isControlled) {
2185
+ let shouldOpen = behavior.initialOpen ?? false;
2186
+ if (behavior.persistOpenState) {
2187
+ try {
2188
+ const stored = window.localStorage.getItem(storageKey2);
2189
+ if (stored !== null) shouldOpen = stored === "1";
2190
+ } catch {
2191
+ }
2192
+ }
2193
+ if (shouldOpen) {
2194
+ setUncontrolledOpen(true);
2195
+ onOpenChange?.(true);
2173
2196
  }
2174
- }
2175
- if (shouldOpen) {
2176
- setUncontrolledOpen(true);
2177
- onOpenChange?.(true);
2178
2197
  }
2179
2198
  setBootstrapDone(true);
2180
2199
  })();
@@ -2237,14 +2256,16 @@ function AgentWidget({ config, onOpenChange, open: controlledOpen }) {
2237
2256
  if (!mapping) return;
2238
2257
  const args = findToolArguments(messages, latest);
2239
2258
  const path = mapping.buildRoute ? mapping.buildRoute(args) : mapping.route;
2240
- if (path) config.navigate(path);
2259
+ if (!path) return;
2260
+ const scrollTo = typeof mapping.scrollTo === "function" ? mapping.scrollTo(args) : mapping.scrollTo;
2261
+ config.navigate(path, { scrollTo: scrollTo ?? null });
2241
2262
  }, [config, fullscreen, messages, open]);
2242
2263
  const closePanel = React11.useCallback(() => {
2243
2264
  setOpen(false);
2244
2265
  setAgentMode(false);
2245
2266
  }, [setOpen]);
2246
2267
  const transformOrigin = position === "bottom-left" ? "bottom left" : "bottom right";
2247
- const launcherHidden = open || config.appearance?.hideLauncher;
2268
+ const launcherHidden = open || config.appearance?.hideLauncher || isDocked;
2248
2269
  return /* @__PURE__ */ jsxs(TooltipProvider, { children: [
2249
2270
  /* @__PURE__ */ jsx(AnimatePresence, { children: !launcherHidden ? /* @__PURE__ */ jsxs(
2250
2271
  motion.div,
@@ -2289,7 +2310,33 @@ function AgentWidget({ config, onOpenChange, open: controlledOpen }) {
2289
2310
  },
2290
2311
  "agent-launcher"
2291
2312
  ) : null }),
2292
- /* @__PURE__ */ jsx(AnimatePresence, { children: open ? /* @__PURE__ */ jsx(
2313
+ isDocked ? /* @__PURE__ */ jsx(AnimatePresence, { children: open ? /* @__PURE__ */ jsx(
2314
+ motion.div,
2315
+ {
2316
+ initial: { opacity: 0, x: 28 },
2317
+ animate: { opacity: 1, x: 0 },
2318
+ exit: { opacity: 0, x: 28 },
2319
+ transition: { type: "spring", stiffness: 260, damping: 30 },
2320
+ className: "flex h-full w-full flex-col overflow-hidden bg-background",
2321
+ children: /* @__PURE__ */ jsx(
2322
+ AgentPanel,
2323
+ {
2324
+ agentMode: false,
2325
+ canToggleAgentMode: false,
2326
+ chat,
2327
+ className: "flex-1",
2328
+ collapseToSide: true,
2329
+ config,
2330
+ newMessageAnchorId: anchorId,
2331
+ onAlwaysApprove: alwaysApprove.setAlwaysApprove,
2332
+ onClose: closePanel,
2333
+ onToggleAgentMode: () => {
2334
+ }
2335
+ }
2336
+ )
2337
+ },
2338
+ "agent-docked"
2339
+ ) : null }) : /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(AnimatePresence, { children: open ? /* @__PURE__ */ jsx(
2293
2340
  motion.div,
2294
2341
  {
2295
2342
  layout: true,
@@ -2329,7 +2376,7 @@ function AgentWidget({ config, onOpenChange, open: controlledOpen }) {
2329
2376
  )
2330
2377
  },
2331
2378
  "agent-panel"
2332
- ) : null })
2379
+ ) : null }) })
2333
2380
  ] });
2334
2381
  }
2335
2382
  function StatusChip({