@btst/stack 1.4.0 → 1.4.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.
Files changed (40) hide show
  1. package/dist/node_modules/.pnpm/@radix-ui_react-accordion@1.2.12_@types_react-dom@19.2.3_@types_react@19.2.6__@types_re_947719a27ff11ec6f09710dd9e85efc5/node_modules/@radix-ui/react-accordion/dist/index.cjs +321 -0
  2. package/dist/node_modules/.pnpm/@radix-ui_react-accordion@1.2.12_@types_react-dom@19.2.3_@types_react@19.2.6__@types_re_947719a27ff11ec6f09710dd9e85efc5/node_modules/@radix-ui/react-accordion/dist/index.mjs +306 -0
  3. package/dist/node_modules/.pnpm/@radix-ui_react-collapsible@1.1.12_@types_react-dom@19.2.3_@types_react@19.2.6__@types__d025a77f62ee83ca6bd8b0ea1f9de738/node_modules/@radix-ui/react-collapsible/dist/index.cjs +168 -0
  4. package/dist/node_modules/.pnpm/@radix-ui_react-collapsible@1.1.12_@types_react-dom@19.2.3_@types_react@19.2.6__@types__d025a77f62ee83ca6bd8b0ea1f9de738/node_modules/@radix-ui/react-collapsible/dist/index.mjs +146 -0
  5. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/chat-interface.cjs +29 -3
  6. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/chat-interface.mjs +29 -3
  7. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/chat-layout.cjs +16 -3
  8. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/chat-layout.mjs +16 -3
  9. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/chat-message.cjs +35 -3
  10. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/chat-message.mjs +35 -3
  11. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/tool-call-display.cjs +123 -0
  12. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/tool-call-display.mjs +121 -0
  13. package/dist/packages/ui/src/components/accordion.cjs +67 -0
  14. package/dist/packages/ui/src/components/accordion.mjs +62 -0
  15. package/dist/plugins/ai-chat/client/components/index.cjs +2 -0
  16. package/dist/plugins/ai-chat/client/components/index.d.cts +1 -1
  17. package/dist/plugins/ai-chat/client/components/index.d.mts +1 -1
  18. package/dist/plugins/ai-chat/client/components/index.d.ts +1 -1
  19. package/dist/plugins/ai-chat/client/components/index.mjs +1 -0
  20. package/dist/plugins/ai-chat/client/index.cjs +2 -0
  21. package/dist/plugins/ai-chat/client/index.d.cts +5 -176
  22. package/dist/plugins/ai-chat/client/index.d.mts +5 -176
  23. package/dist/plugins/ai-chat/client/index.d.ts +5 -176
  24. package/dist/plugins/ai-chat/client/index.mjs +1 -0
  25. package/dist/plugins/blog/client/components/shared/markdown-content-styles.css +6 -0
  26. package/dist/shared/stack.DaOcgmrM.d.cts +323 -0
  27. package/dist/shared/stack.DaOcgmrM.d.mts +323 -0
  28. package/dist/shared/stack.DaOcgmrM.d.ts +323 -0
  29. package/package.json +1 -1
  30. package/src/plugins/ai-chat/client/components/chat-interface.tsx +41 -2
  31. package/src/plugins/ai-chat/client/components/chat-layout.tsx +16 -1
  32. package/src/plugins/ai-chat/client/components/chat-message.tsx +59 -3
  33. package/src/plugins/ai-chat/client/components/index.ts +2 -0
  34. package/src/plugins/ai-chat/client/components/tool-call-display.tsx +197 -0
  35. package/src/plugins/ai-chat/client/index.ts +12 -1
  36. package/src/plugins/ai-chat/client/overrides.ts +71 -0
  37. package/src/plugins/blog/client/components/shared/markdown-content-styles.css +6 -0
  38. package/dist/shared/stack.DorMi9CZ.d.cts +0 -80
  39. package/dist/shared/stack.DorMi9CZ.d.mts +0 -80
  40. package/dist/shared/stack.DorMi9CZ.d.ts +0 -80
@@ -0,0 +1,168 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ const React = require('react');
5
+ const index$4 = require('../../../../../@radix-ui_primitive@1.1.3/node_modules/@radix-ui/primitive/dist/index.cjs');
6
+ const index = require('../../../../../@radix-ui_react-context@1.1.2_@types_react@19.2.6_react@19.2.0/node_modules/@radix-ui/react-context/dist/index.cjs');
7
+ const index$1 = require('../../../../../@radix-ui_react-use-controllable-state@1.2.2_@types_react@19.2.6_react@19.2.0/node_modules/@radix-ui/react-use-controllable-state/dist/index.cjs');
8
+ const index$7 = require('../../../../../@radix-ui_react-use-layout-effect@1.1.1_@types_react@19.2.6_react@19.2.0/node_modules/@radix-ui/react-use-layout-effect/dist/index.cjs');
9
+ const index$6 = require('../../../../../@radix-ui_react-compose-refs@1.1.2_@types_react@19.2.6_react@19.2.0/node_modules/@radix-ui/react-compose-refs/dist/index.cjs');
10
+ const index$3 = require('../../../../../@radix-ui_react-primitive@2.1.3_@types_react-dom@19.2.3_@types_react@19.2.6__@types_rea_a92a69cb1cb39305138539e4fa72f596/node_modules/@radix-ui/react-primitive/dist/index.cjs');
11
+ const index$5 = require('../../../../../@radix-ui_react-presence@1.1.5_@types_react-dom@19.2.3_@types_react@19.2.6__@types_reac_90f8e5c12233caef3399d5fd66452a13/node_modules/@radix-ui/react-presence/dist/index.cjs');
12
+ const index$2 = require('../../../../../@radix-ui_react-id@1.1.1_@types_react@19.2.6_react@19.2.0/node_modules/@radix-ui/react-id/dist/index.cjs');
13
+ const jsxRuntime = require('react/jsx-runtime');
14
+
15
+ function _interopNamespaceCompat(e) {
16
+ if (e && typeof e === 'object' && 'default' in e) return e;
17
+ const n = Object.create(null);
18
+ if (e) {
19
+ for (const k in e) {
20
+ n[k] = e[k];
21
+ }
22
+ }
23
+ n.default = e;
24
+ return n;
25
+ }
26
+
27
+ const React__namespace = /*#__PURE__*/_interopNamespaceCompat(React);
28
+
29
+ var COLLAPSIBLE_NAME = "Collapsible";
30
+ var [createCollapsibleContext, createCollapsibleScope] = index.createContextScope(COLLAPSIBLE_NAME);
31
+ var [CollapsibleProvider, useCollapsibleContext] = createCollapsibleContext(COLLAPSIBLE_NAME);
32
+ var Collapsible = React__namespace.forwardRef(
33
+ (props, forwardedRef) => {
34
+ const {
35
+ __scopeCollapsible,
36
+ open: openProp,
37
+ defaultOpen,
38
+ disabled,
39
+ onOpenChange,
40
+ ...collapsibleProps
41
+ } = props;
42
+ const [open, setOpen] = index$1.useControllableState({
43
+ prop: openProp,
44
+ defaultProp: defaultOpen ?? false,
45
+ onChange: onOpenChange,
46
+ caller: COLLAPSIBLE_NAME
47
+ });
48
+ return /* @__PURE__ */ jsxRuntime.jsx(
49
+ CollapsibleProvider,
50
+ {
51
+ scope: __scopeCollapsible,
52
+ disabled,
53
+ contentId: index$2.useId(),
54
+ open,
55
+ onOpenToggle: React__namespace.useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen]),
56
+ children: /* @__PURE__ */ jsxRuntime.jsx(
57
+ index$3.Primitive.div,
58
+ {
59
+ "data-state": getState(open),
60
+ "data-disabled": disabled ? "" : void 0,
61
+ ...collapsibleProps,
62
+ ref: forwardedRef
63
+ }
64
+ )
65
+ }
66
+ );
67
+ }
68
+ );
69
+ Collapsible.displayName = COLLAPSIBLE_NAME;
70
+ var TRIGGER_NAME = "CollapsibleTrigger";
71
+ var CollapsibleTrigger = React__namespace.forwardRef(
72
+ (props, forwardedRef) => {
73
+ const { __scopeCollapsible, ...triggerProps } = props;
74
+ const context = useCollapsibleContext(TRIGGER_NAME, __scopeCollapsible);
75
+ return /* @__PURE__ */ jsxRuntime.jsx(
76
+ index$3.Primitive.button,
77
+ {
78
+ type: "button",
79
+ "aria-controls": context.contentId,
80
+ "aria-expanded": context.open || false,
81
+ "data-state": getState(context.open),
82
+ "data-disabled": context.disabled ? "" : void 0,
83
+ disabled: context.disabled,
84
+ ...triggerProps,
85
+ ref: forwardedRef,
86
+ onClick: index$4.composeEventHandlers(props.onClick, context.onOpenToggle)
87
+ }
88
+ );
89
+ }
90
+ );
91
+ CollapsibleTrigger.displayName = TRIGGER_NAME;
92
+ var CONTENT_NAME = "CollapsibleContent";
93
+ var CollapsibleContent = React__namespace.forwardRef(
94
+ (props, forwardedRef) => {
95
+ const { forceMount, ...contentProps } = props;
96
+ const context = useCollapsibleContext(CONTENT_NAME, props.__scopeCollapsible);
97
+ return /* @__PURE__ */ jsxRuntime.jsx(index$5.Presence, { present: forceMount || context.open, children: ({ present }) => /* @__PURE__ */ jsxRuntime.jsx(CollapsibleContentImpl, { ...contentProps, ref: forwardedRef, present }) });
98
+ }
99
+ );
100
+ CollapsibleContent.displayName = CONTENT_NAME;
101
+ var CollapsibleContentImpl = React__namespace.forwardRef((props, forwardedRef) => {
102
+ const { __scopeCollapsible, present, children, ...contentProps } = props;
103
+ const context = useCollapsibleContext(CONTENT_NAME, __scopeCollapsible);
104
+ const [isPresent, setIsPresent] = React__namespace.useState(present);
105
+ const ref = React__namespace.useRef(null);
106
+ const composedRefs = index$6.useComposedRefs(forwardedRef, ref);
107
+ const heightRef = React__namespace.useRef(0);
108
+ const height = heightRef.current;
109
+ const widthRef = React__namespace.useRef(0);
110
+ const width = widthRef.current;
111
+ const isOpen = context.open || isPresent;
112
+ const isMountAnimationPreventedRef = React__namespace.useRef(isOpen);
113
+ const originalStylesRef = React__namespace.useRef(void 0);
114
+ React__namespace.useEffect(() => {
115
+ const rAF = requestAnimationFrame(() => isMountAnimationPreventedRef.current = false);
116
+ return () => cancelAnimationFrame(rAF);
117
+ }, []);
118
+ index$7.useLayoutEffect(() => {
119
+ const node = ref.current;
120
+ if (node) {
121
+ originalStylesRef.current = originalStylesRef.current || {
122
+ transitionDuration: node.style.transitionDuration,
123
+ animationName: node.style.animationName
124
+ };
125
+ node.style.transitionDuration = "0s";
126
+ node.style.animationName = "none";
127
+ const rect = node.getBoundingClientRect();
128
+ heightRef.current = rect.height;
129
+ widthRef.current = rect.width;
130
+ if (!isMountAnimationPreventedRef.current) {
131
+ node.style.transitionDuration = originalStylesRef.current.transitionDuration;
132
+ node.style.animationName = originalStylesRef.current.animationName;
133
+ }
134
+ setIsPresent(present);
135
+ }
136
+ }, [context.open, present]);
137
+ return /* @__PURE__ */ jsxRuntime.jsx(
138
+ index$3.Primitive.div,
139
+ {
140
+ "data-state": getState(context.open),
141
+ "data-disabled": context.disabled ? "" : void 0,
142
+ id: context.contentId,
143
+ hidden: !isOpen,
144
+ ...contentProps,
145
+ ref: composedRefs,
146
+ style: {
147
+ [`--radix-collapsible-content-height`]: height ? `${height}px` : void 0,
148
+ [`--radix-collapsible-content-width`]: width ? `${width}px` : void 0,
149
+ ...props.style
150
+ },
151
+ children: isOpen && children
152
+ }
153
+ );
154
+ });
155
+ function getState(open) {
156
+ return open ? "open" : "closed";
157
+ }
158
+ var Root = Collapsible;
159
+ var Trigger = CollapsibleTrigger;
160
+ var Content = CollapsibleContent;
161
+
162
+ exports.Collapsible = Collapsible;
163
+ exports.CollapsibleContent = CollapsibleContent;
164
+ exports.CollapsibleTrigger = CollapsibleTrigger;
165
+ exports.Content = Content;
166
+ exports.Root = Root;
167
+ exports.Trigger = Trigger;
168
+ exports.createCollapsibleScope = createCollapsibleScope;
@@ -0,0 +1,146 @@
1
+ "use client";
2
+ import * as React from 'react';
3
+ import { composeEventHandlers } from '../../../../../@radix-ui_primitive@1.1.3/node_modules/@radix-ui/primitive/dist/index.mjs';
4
+ import { createContextScope } from '../../../../../@radix-ui_react-context@1.1.2_@types_react@19.2.6_react@19.2.0/node_modules/@radix-ui/react-context/dist/index.mjs';
5
+ import { useControllableState } from '../../../../../@radix-ui_react-use-controllable-state@1.2.2_@types_react@19.2.6_react@19.2.0/node_modules/@radix-ui/react-use-controllable-state/dist/index.mjs';
6
+ import { useLayoutEffect as useLayoutEffect2 } from '../../../../../@radix-ui_react-use-layout-effect@1.1.1_@types_react@19.2.6_react@19.2.0/node_modules/@radix-ui/react-use-layout-effect/dist/index.mjs';
7
+ import { useComposedRefs } from '../../../../../@radix-ui_react-compose-refs@1.1.2_@types_react@19.2.6_react@19.2.0/node_modules/@radix-ui/react-compose-refs/dist/index.mjs';
8
+ import { Primitive } from '../../../../../@radix-ui_react-primitive@2.1.3_@types_react-dom@19.2.3_@types_react@19.2.6__@types_rea_a92a69cb1cb39305138539e4fa72f596/node_modules/@radix-ui/react-primitive/dist/index.mjs';
9
+ import { Presence } from '../../../../../@radix-ui_react-presence@1.1.5_@types_react-dom@19.2.3_@types_react@19.2.6__@types_reac_90f8e5c12233caef3399d5fd66452a13/node_modules/@radix-ui/react-presence/dist/index.mjs';
10
+ import { useId } from '../../../../../@radix-ui_react-id@1.1.1_@types_react@19.2.6_react@19.2.0/node_modules/@radix-ui/react-id/dist/index.mjs';
11
+ import { jsx } from 'react/jsx-runtime';
12
+
13
+ var COLLAPSIBLE_NAME = "Collapsible";
14
+ var [createCollapsibleContext, createCollapsibleScope] = createContextScope(COLLAPSIBLE_NAME);
15
+ var [CollapsibleProvider, useCollapsibleContext] = createCollapsibleContext(COLLAPSIBLE_NAME);
16
+ var Collapsible = React.forwardRef(
17
+ (props, forwardedRef) => {
18
+ const {
19
+ __scopeCollapsible,
20
+ open: openProp,
21
+ defaultOpen,
22
+ disabled,
23
+ onOpenChange,
24
+ ...collapsibleProps
25
+ } = props;
26
+ const [open, setOpen] = useControllableState({
27
+ prop: openProp,
28
+ defaultProp: defaultOpen ?? false,
29
+ onChange: onOpenChange,
30
+ caller: COLLAPSIBLE_NAME
31
+ });
32
+ return /* @__PURE__ */ jsx(
33
+ CollapsibleProvider,
34
+ {
35
+ scope: __scopeCollapsible,
36
+ disabled,
37
+ contentId: useId(),
38
+ open,
39
+ onOpenToggle: React.useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen]),
40
+ children: /* @__PURE__ */ jsx(
41
+ Primitive.div,
42
+ {
43
+ "data-state": getState(open),
44
+ "data-disabled": disabled ? "" : void 0,
45
+ ...collapsibleProps,
46
+ ref: forwardedRef
47
+ }
48
+ )
49
+ }
50
+ );
51
+ }
52
+ );
53
+ Collapsible.displayName = COLLAPSIBLE_NAME;
54
+ var TRIGGER_NAME = "CollapsibleTrigger";
55
+ var CollapsibleTrigger = React.forwardRef(
56
+ (props, forwardedRef) => {
57
+ const { __scopeCollapsible, ...triggerProps } = props;
58
+ const context = useCollapsibleContext(TRIGGER_NAME, __scopeCollapsible);
59
+ return /* @__PURE__ */ jsx(
60
+ Primitive.button,
61
+ {
62
+ type: "button",
63
+ "aria-controls": context.contentId,
64
+ "aria-expanded": context.open || false,
65
+ "data-state": getState(context.open),
66
+ "data-disabled": context.disabled ? "" : void 0,
67
+ disabled: context.disabled,
68
+ ...triggerProps,
69
+ ref: forwardedRef,
70
+ onClick: composeEventHandlers(props.onClick, context.onOpenToggle)
71
+ }
72
+ );
73
+ }
74
+ );
75
+ CollapsibleTrigger.displayName = TRIGGER_NAME;
76
+ var CONTENT_NAME = "CollapsibleContent";
77
+ var CollapsibleContent = React.forwardRef(
78
+ (props, forwardedRef) => {
79
+ const { forceMount, ...contentProps } = props;
80
+ const context = useCollapsibleContext(CONTENT_NAME, props.__scopeCollapsible);
81
+ return /* @__PURE__ */ jsx(Presence, { present: forceMount || context.open, children: ({ present }) => /* @__PURE__ */ jsx(CollapsibleContentImpl, { ...contentProps, ref: forwardedRef, present }) });
82
+ }
83
+ );
84
+ CollapsibleContent.displayName = CONTENT_NAME;
85
+ var CollapsibleContentImpl = React.forwardRef((props, forwardedRef) => {
86
+ const { __scopeCollapsible, present, children, ...contentProps } = props;
87
+ const context = useCollapsibleContext(CONTENT_NAME, __scopeCollapsible);
88
+ const [isPresent, setIsPresent] = React.useState(present);
89
+ const ref = React.useRef(null);
90
+ const composedRefs = useComposedRefs(forwardedRef, ref);
91
+ const heightRef = React.useRef(0);
92
+ const height = heightRef.current;
93
+ const widthRef = React.useRef(0);
94
+ const width = widthRef.current;
95
+ const isOpen = context.open || isPresent;
96
+ const isMountAnimationPreventedRef = React.useRef(isOpen);
97
+ const originalStylesRef = React.useRef(void 0);
98
+ React.useEffect(() => {
99
+ const rAF = requestAnimationFrame(() => isMountAnimationPreventedRef.current = false);
100
+ return () => cancelAnimationFrame(rAF);
101
+ }, []);
102
+ useLayoutEffect2(() => {
103
+ const node = ref.current;
104
+ if (node) {
105
+ originalStylesRef.current = originalStylesRef.current || {
106
+ transitionDuration: node.style.transitionDuration,
107
+ animationName: node.style.animationName
108
+ };
109
+ node.style.transitionDuration = "0s";
110
+ node.style.animationName = "none";
111
+ const rect = node.getBoundingClientRect();
112
+ heightRef.current = rect.height;
113
+ widthRef.current = rect.width;
114
+ if (!isMountAnimationPreventedRef.current) {
115
+ node.style.transitionDuration = originalStylesRef.current.transitionDuration;
116
+ node.style.animationName = originalStylesRef.current.animationName;
117
+ }
118
+ setIsPresent(present);
119
+ }
120
+ }, [context.open, present]);
121
+ return /* @__PURE__ */ jsx(
122
+ Primitive.div,
123
+ {
124
+ "data-state": getState(context.open),
125
+ "data-disabled": context.disabled ? "" : void 0,
126
+ id: context.contentId,
127
+ hidden: !isOpen,
128
+ ...contentProps,
129
+ ref: composedRefs,
130
+ style: {
131
+ [`--radix-collapsible-content-height`]: height ? `${height}px` : void 0,
132
+ [`--radix-collapsible-content-width`]: width ? `${width}px` : void 0,
133
+ ...props.style
134
+ },
135
+ children: isOpen && children
136
+ }
137
+ );
138
+ });
139
+ function getState(open) {
140
+ return open ? "open" : "closed";
141
+ }
142
+ var Root = Collapsible;
143
+ var Trigger = CollapsibleTrigger;
144
+ var Content = CollapsibleContent;
145
+
146
+ export { Collapsible, CollapsibleContent, CollapsibleTrigger, Content, Root, Trigger, createCollapsibleScope };
@@ -22,7 +22,8 @@ function ChatInterface({
22
22
  initialMessages,
23
23
  id,
24
24
  variant = "full",
25
- className
25
+ className,
26
+ onMessagesChange
26
27
  }) {
27
28
  const {
28
29
  navigate,
@@ -31,7 +32,8 @@ function ChatInterface({
31
32
  apiBasePath,
32
33
  headers,
33
34
  mode,
34
- showAttribution
35
+ showAttribution,
36
+ chatSuggestions
35
37
  } = context.usePluginOverrides(
36
38
  "ai-chat",
37
39
  { showAttribution: true }
@@ -73,6 +75,12 @@ function ChatInterface({
73
75
  }
74
76
  }, [currentConversationId, isPublicMode]);
75
77
  const editMessagesRef = React.useRef(null);
78
+ const [isMessagesInitialized, setIsMessagesInitialized] = React.useState(
79
+ () => (
80
+ // Start as initialized if there are no initialMessages to load
81
+ !initialMessages || initialMessages.length === 0
82
+ )
83
+ );
76
84
  const transport = React.useMemo(
77
85
  () => new ai.DefaultChatTransport({
78
86
  api: apiPath,
@@ -164,6 +172,7 @@ function ChatInterface({
164
172
  React.useEffect(() => {
165
173
  if (initialMessages && initialMessages.length > 0 && messages.length === 0) {
166
174
  setMessages(initialMessages);
175
+ setIsMessagesInitialized(true);
167
176
  }
168
177
  }, [initialMessages, setMessages, messages.length]);
169
178
  const [input, setInput] = React.useState("");
@@ -179,6 +188,11 @@ function ChatInterface({
179
188
  }
180
189
  }
181
190
  }, [messages]);
191
+ React.useEffect(() => {
192
+ if (isPublicMode && onMessagesChange && isMessagesInitialized) {
193
+ onMessagesChange(messages);
194
+ }
195
+ }, [messages, isPublicMode, onMessagesChange, isMessagesInitialized]);
182
196
  const handleInputChange = (e) => {
183
197
  setInput(e.target.value);
184
198
  };
@@ -259,7 +273,19 @@ function ChatInterface({
259
273
  isWidget ? "max-w-full" : "max-w-3xl mx-auto w-full"
260
274
  ),
261
275
  children: [
262
- messages.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col items-center justify-center h-full min-h-[300px] text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: localization.CHAT_EMPTY_STATE }) }) : messages.map((m, index) => /* @__PURE__ */ jsxRuntime.jsx(
276
+ messages.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col h-full min-h-[300px]", children: [
277
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex items-center justify-center text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: localization.CHAT_EMPTY_STATE }) }),
278
+ chatSuggestions && chatSuggestions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap justify-center gap-2 pb-4 max-w-md mx-auto", children: chatSuggestions.map((suggestion, index) => /* @__PURE__ */ jsxRuntime.jsx(
279
+ "button",
280
+ {
281
+ type: "button",
282
+ onClick: () => setInput(suggestion),
283
+ className: "px-3 py-2 text-sm rounded-lg border border-border bg-background hover:bg-accent hover:text-accent-foreground transition-colors text-foreground",
284
+ children: suggestion
285
+ },
286
+ index
287
+ )) })
288
+ ] }) : messages.map((m, index) => /* @__PURE__ */ jsxRuntime.jsx(
263
289
  chatMessage.ChatMessage,
264
290
  {
265
291
  message: m,
@@ -20,7 +20,8 @@ function ChatInterface({
20
20
  initialMessages,
21
21
  id,
22
22
  variant = "full",
23
- className
23
+ className,
24
+ onMessagesChange
24
25
  }) {
25
26
  const {
26
27
  navigate,
@@ -29,7 +30,8 @@ function ChatInterface({
29
30
  apiBasePath,
30
31
  headers,
31
32
  mode,
32
- showAttribution
33
+ showAttribution,
34
+ chatSuggestions
33
35
  } = usePluginOverrides(
34
36
  "ai-chat",
35
37
  { showAttribution: true }
@@ -71,6 +73,12 @@ function ChatInterface({
71
73
  }
72
74
  }, [currentConversationId, isPublicMode]);
73
75
  const editMessagesRef = useRef(null);
76
+ const [isMessagesInitialized, setIsMessagesInitialized] = useState(
77
+ () => (
78
+ // Start as initialized if there are no initialMessages to load
79
+ !initialMessages || initialMessages.length === 0
80
+ )
81
+ );
74
82
  const transport = useMemo(
75
83
  () => new DefaultChatTransport({
76
84
  api: apiPath,
@@ -162,6 +170,7 @@ function ChatInterface({
162
170
  useEffect(() => {
163
171
  if (initialMessages && initialMessages.length > 0 && messages.length === 0) {
164
172
  setMessages(initialMessages);
173
+ setIsMessagesInitialized(true);
165
174
  }
166
175
  }, [initialMessages, setMessages, messages.length]);
167
176
  const [input, setInput] = useState("");
@@ -177,6 +186,11 @@ function ChatInterface({
177
186
  }
178
187
  }
179
188
  }, [messages]);
189
+ useEffect(() => {
190
+ if (isPublicMode && onMessagesChange && isMessagesInitialized) {
191
+ onMessagesChange(messages);
192
+ }
193
+ }, [messages, isPublicMode, onMessagesChange, isMessagesInitialized]);
180
194
  const handleInputChange = (e) => {
181
195
  setInput(e.target.value);
182
196
  };
@@ -257,7 +271,19 @@ function ChatInterface({
257
271
  isWidget ? "max-w-full" : "max-w-3xl mx-auto w-full"
258
272
  ),
259
273
  children: [
260
- messages.length === 0 ? /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center justify-center h-full min-h-[300px] text-muted-foreground", children: /* @__PURE__ */ jsx("p", { children: localization.CHAT_EMPTY_STATE }) }) : messages.map((m, index) => /* @__PURE__ */ jsx(
274
+ messages.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full min-h-[300px]", children: [
275
+ /* @__PURE__ */ jsx("div", { className: "flex-1 flex items-center justify-center text-muted-foreground", children: /* @__PURE__ */ jsx("p", { children: localization.CHAT_EMPTY_STATE }) }),
276
+ chatSuggestions && chatSuggestions.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap justify-center gap-2 pb-4 max-w-md mx-auto", children: chatSuggestions.map((suggestion, index) => /* @__PURE__ */ jsx(
277
+ "button",
278
+ {
279
+ type: "button",
280
+ onClick: () => setInput(suggestion),
281
+ className: "px-3 py-2 text-sm rounded-lg border border-border bg-background hover:bg-accent hover:text-accent-foreground transition-colors text-foreground",
282
+ children: suggestion
283
+ },
284
+ index
285
+ )) })
286
+ ] }) : messages.map((m, index) => /* @__PURE__ */ jsx(
261
287
  ChatMessage,
262
288
  {
263
289
  message: m,
@@ -17,7 +17,9 @@ function ChatLayout({
17
17
  layout = "full",
18
18
  className,
19
19
  showSidebar = true,
20
- widgetHeight = "600px"
20
+ widgetHeight = "600px",
21
+ initialMessages,
22
+ onMessagesChange
21
23
  }) {
22
24
  const [sidebarOpen, setSidebarOpen] = React.useState(true);
23
25
  const [mobileSidebarOpen, setMobileSidebarOpen] = React.useState(false);
@@ -37,7 +39,16 @@ function ChatLayout({
37
39
  className
38
40
  ),
39
41
  style: { height: widgetHeight },
40
- children: /* @__PURE__ */ jsxRuntime.jsx(chatInterface.ChatInterface, { apiPath, id: conversationId, variant: "widget" })
42
+ children: /* @__PURE__ */ jsxRuntime.jsx(
43
+ chatInterface.ChatInterface,
44
+ {
45
+ apiPath,
46
+ id: conversationId,
47
+ variant: "widget",
48
+ initialMessages,
49
+ onMessagesChange
50
+ }
51
+ )
41
52
  }
42
53
  );
43
54
  }
@@ -109,7 +120,9 @@ function ChatLayout({
109
120
  {
110
121
  apiPath,
111
122
  id: conversationId,
112
- variant: "full"
123
+ variant: "full",
124
+ initialMessages,
125
+ onMessagesChange
113
126
  },
114
127
  `chat-${conversationId ?? "new"}-${chatResetKey}`
115
128
  )
@@ -15,7 +15,9 @@ function ChatLayout({
15
15
  layout = "full",
16
16
  className,
17
17
  showSidebar = true,
18
- widgetHeight = "600px"
18
+ widgetHeight = "600px",
19
+ initialMessages,
20
+ onMessagesChange
19
21
  }) {
20
22
  const [sidebarOpen, setSidebarOpen] = useState(true);
21
23
  const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
@@ -35,7 +37,16 @@ function ChatLayout({
35
37
  className
36
38
  ),
37
39
  style: { height: widgetHeight },
38
- children: /* @__PURE__ */ jsx(ChatInterface, { apiPath, id: conversationId, variant: "widget" })
40
+ children: /* @__PURE__ */ jsx(
41
+ ChatInterface,
42
+ {
43
+ apiPath,
44
+ id: conversationId,
45
+ variant: "widget",
46
+ initialMessages,
47
+ onMessagesChange
48
+ }
49
+ )
39
50
  }
40
51
  );
41
52
  }
@@ -107,7 +118,9 @@ function ChatLayout({
107
118
  {
108
119
  apiPath,
109
120
  id: conversationId,
110
- variant: "full"
121
+ variant: "full",
122
+ initialMessages,
123
+ onMessagesChange
111
124
  },
112
125
  `chat-${conversationId ?? "new"}-${chatResetKey}`
113
126
  )
@@ -7,9 +7,11 @@ const markdownContent = require('../../../../../../ui/src/components/markdown-co
7
7
  const button = require('../../../../../../ui/src/components/button.cjs');
8
8
  const dialog = require('../../../../../../ui/src/components/dialog.cjs');
9
9
  const lucideReact = require('lucide-react');
10
+ const ai = require('ai');
10
11
  const React = require('react');
11
12
  const context = require('@btst/stack/context');
12
13
  const index = require('../localization/index.cjs');
14
+ const toolCallDisplay = require('./tool-call-display.cjs');
13
15
  require('highlight.js/styles/panda-syntax-light.css');
14
16
  const completeMarkdown = require('remend');
15
17
 
@@ -83,7 +85,8 @@ function ChatMessage({
83
85
  const {
84
86
  Link,
85
87
  Image,
86
- localization: customLocalization
88
+ localization: customLocalization,
89
+ toolRenderers
87
90
  } = context.usePluginOverrides(
88
91
  "ai-chat",
89
92
  {}
@@ -155,6 +158,12 @@ function ChatMessage({
155
158
  }
156
159
  return [];
157
160
  }, [message.parts]);
161
+ const toolParts = React.useMemo(() => {
162
+ if (message.parts && Array.isArray(message.parts)) {
163
+ return message.parts.filter((part) => ai.isToolUIPart(part));
164
+ }
165
+ return [];
166
+ }, [message.parts]);
158
167
  const displayContent = React.useMemo(() => {
159
168
  if (!textContent) return "";
160
169
  if (isStreaming && !isUser) {
@@ -276,7 +285,7 @@ function ChatMessage({
276
285
  )
277
286
  ] })
278
287
  ) : (
279
- // Assistant messages: rendered markdown + files + images
288
+ // Assistant messages: rendered markdown + files + images + tool calls
280
289
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "wrap-break-word space-y-2", children: [
281
290
  fileParts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: fileParts.map((part, index) => /* @__PURE__ */ jsxRuntime.jsxs(
282
291
  "a",
@@ -304,6 +313,29 @@ function ChatMessage({
304
313
  },
305
314
  index
306
315
  )) }),
316
+ toolParts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: toolParts.map((part) => {
317
+ const toolName = ai.getToolName(part);
318
+ const toolCallId = part.toolCallId;
319
+ const state = part.state;
320
+ const input = part.input;
321
+ const output = part.output;
322
+ const errorText = part.errorText;
323
+ const isLoading = state === "input-streaming" || state === "input-available";
324
+ const toolCallProps = {
325
+ toolCallId,
326
+ toolName,
327
+ state,
328
+ input,
329
+ output,
330
+ errorText,
331
+ isLoading
332
+ };
333
+ const CustomRenderer = toolRenderers?.[toolName];
334
+ if (CustomRenderer) {
335
+ return /* @__PURE__ */ jsxRuntime.jsx(CustomRenderer, { ...toolCallProps }, toolCallId);
336
+ }
337
+ return /* @__PURE__ */ jsxRuntime.jsx(toolCallDisplay.ToolCallDisplay, { ...toolCallProps }, toolCallId);
338
+ }) }),
307
339
  displayContent ? /* @__PURE__ */ jsxRuntime.jsx(
308
340
  markdownContent.MarkdownContent,
309
341
  {
@@ -312,7 +344,7 @@ function ChatMessage({
312
344
  LinkComponent: Link ?? DefaultLink,
313
345
  ImageComponent
314
346
  }
315
- ) : imageParts.length === 0 && fileParts.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "..." }) : null
347
+ ) : imageParts.length === 0 && fileParts.length === 0 && toolParts.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "..." }) : null
316
348
  ] })
317
349
  )
318
350
  }