@assistant-ui/react 0.3.0 → 0.3.2

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.
package/dist/index.mjs CHANGED
@@ -148,11 +148,14 @@ var makeBaseComposer = (set) => ({
148
148
  });
149
149
 
150
150
  // src/context/stores/Composer.ts
151
- var makeComposerStore = (useThread, useThreadMessages, useThreadActions) => {
151
+ var makeComposerStore = (useThreadMessages, useThreadActions) => {
152
152
  const focusListeners = /* @__PURE__ */ new Set();
153
153
  return create3()((set, get, store) => {
154
154
  return {
155
155
  ...makeBaseComposer(set, get, store),
156
+ get canCancel() {
157
+ return useThreadActions.getState().capabilities.cancel;
158
+ },
156
159
  isEditing: true,
157
160
  send: () => {
158
161
  const { setValue, value } = get();
@@ -164,10 +167,7 @@ var makeComposerStore = (useThread, useThreadMessages, useThreadActions) => {
164
167
  });
165
168
  },
166
169
  cancel: () => {
167
- const thread = useThread.getState();
168
- if (!thread.isRunning) return false;
169
170
  useThreadActions.getState().cancelRun();
170
- return true;
171
171
  },
172
172
  focus: () => {
173
173
  for (const listener of focusListeners) {
@@ -217,6 +217,9 @@ import { create as create6 } from "zustand";
217
217
  var makeThreadActionStore = (runtimeRef) => {
218
218
  return create6(
219
219
  () => Object.freeze({
220
+ get capabilities() {
221
+ return runtimeRef.current.capabilities;
222
+ },
220
223
  getBranches: (messageId) => runtimeRef.current.getBranches(messageId),
221
224
  switchToBranch: (branchId) => runtimeRef.current.switchToBranch(branchId),
222
225
  startRun: (parentId) => runtimeRef.current.startRun(parentId),
@@ -248,11 +251,7 @@ var ThreadProvider = ({
248
251
  const useThreadMessages = makeThreadMessagesStore(runtimeRef);
249
252
  const useThreadActions = makeThreadActionStore(runtimeRef);
250
253
  const useViewport = makeThreadViewportStore();
251
- const useComposer = makeComposerStore(
252
- useThread,
253
- useThreadMessages,
254
- useThreadActions
255
- );
254
+ const useComposer = makeComposerStore(useThreadMessages, useThreadActions);
256
255
  return {
257
256
  useThread,
258
257
  useThreadMessages,
@@ -625,7 +624,7 @@ var useBranchPickerPrevious = () => {
625
624
  import { useCallback as useCallback9 } from "react";
626
625
  var useComposerCancel = () => {
627
626
  const { useComposer } = useComposerContext();
628
- const disabled = useComposer((c) => !c.isEditing);
627
+ const disabled = useComposer((c) => !c.canCancel);
629
628
  const callback = useCallback9(() => {
630
629
  const { cancel } = useComposer.getState();
631
630
  cancel();
@@ -669,7 +668,7 @@ var useContentPartDisplay = () => {
669
668
  throw new Error(
670
669
  "This component can only be used inside ui content parts."
671
670
  );
672
- return c.part.display;
671
+ return c;
673
672
  });
674
673
  return display;
675
674
  };
@@ -682,7 +681,7 @@ var useContentPartImage = () => {
682
681
  throw new Error(
683
682
  "ContentPartImage can only be used inside image content parts."
684
683
  );
685
- return c.part.image;
684
+ return c;
686
685
  });
687
686
  return image;
688
687
  };
@@ -757,15 +756,18 @@ var useThreadSuggestion = ({
757
756
  autoSend
758
757
  }) => {
759
758
  const { useThread, useComposer } = useThreadContext();
759
+ const append = useAppendMessage();
760
760
  const disabled = useThread((t) => t.isRunning);
761
761
  const callback = useCallback12(() => {
762
762
  const thread = useThread.getState();
763
763
  const composer = useComposer.getState();
764
- composer.setValue(prompt);
765
764
  if (autoSend && !thread.isRunning) {
766
- composer.send();
765
+ append(prompt);
766
+ composer.setValue("");
767
+ } else {
768
+ composer.setValue(prompt);
767
769
  }
768
- }, [useThread, useComposer, prompt, autoSend]);
770
+ }, [useThread, useComposer, autoSend, append, prompt]);
769
771
  if (disabled) return null;
770
772
  return callback;
771
773
  };
@@ -1161,13 +1163,86 @@ var ContentPartProvider = ({
1161
1163
  // src/primitives/contentPart/ContentPartText.tsx
1162
1164
  import { Primitive as Primitive4 } from "@radix-ui/react-primitive";
1163
1165
  import { forwardRef as forwardRef7 } from "react";
1166
+
1167
+ // src/utils/hooks/useSmooth.tsx
1168
+ import { useEffect as useEffect8, useState as useState5 } from "react";
1169
+ var TextStreamAnimator = class {
1170
+ constructor(setText) {
1171
+ this.setText = setText;
1172
+ }
1173
+ animationFrameId = null;
1174
+ lastUpdateTime = Date.now();
1175
+ decayFactor = 0.99;
1176
+ targetText = "";
1177
+ start() {
1178
+ if (this.animationFrameId !== null) return;
1179
+ this.animate();
1180
+ }
1181
+ stop() {
1182
+ if (this.animationFrameId !== null) {
1183
+ cancelAnimationFrame(this.animationFrameId);
1184
+ this.animationFrameId = null;
1185
+ }
1186
+ }
1187
+ animate = () => {
1188
+ const currentTime = Date.now();
1189
+ const deltaTime = currentTime - this.lastUpdateTime;
1190
+ this.lastUpdateTime = currentTime;
1191
+ this.setText((currentText) => {
1192
+ const targetText = this.targetText;
1193
+ if (currentText === targetText) {
1194
+ this.animationFrameId = null;
1195
+ return currentText;
1196
+ }
1197
+ const remainingChars = targetText.length - currentText.length;
1198
+ const charsToAdd = Math.max(
1199
+ 1,
1200
+ Math.floor(
1201
+ remainingChars * (1 - Math.pow(this.decayFactor, deltaTime))
1202
+ )
1203
+ );
1204
+ const newText = targetText.slice(0, currentText.length + charsToAdd);
1205
+ this.animationFrameId = requestAnimationFrame(this.animate);
1206
+ return newText;
1207
+ });
1208
+ };
1209
+ };
1210
+ var useSmooth = (text, smooth = false) => {
1211
+ const [displayedText, setDisplayedText] = useState5(text);
1212
+ const [animatorRef] = useState5(
1213
+ new TextStreamAnimator(setDisplayedText)
1214
+ );
1215
+ useEffect8(() => {
1216
+ if (!smooth) {
1217
+ animatorRef.stop();
1218
+ return;
1219
+ }
1220
+ if (!text.startsWith(animatorRef.targetText)) {
1221
+ setDisplayedText(text);
1222
+ animatorRef.targetText = text;
1223
+ animatorRef.stop();
1224
+ return;
1225
+ }
1226
+ animatorRef.targetText = text;
1227
+ animatorRef.start();
1228
+ }, [animatorRef, smooth, text]);
1229
+ useEffect8(() => {
1230
+ return () => {
1231
+ animatorRef.stop();
1232
+ };
1233
+ }, [animatorRef]);
1234
+ return smooth ? displayedText : text;
1235
+ };
1236
+
1237
+ // src/primitives/contentPart/ContentPartText.tsx
1164
1238
  import { jsx as jsx14 } from "react/jsx-runtime";
1165
- var ContentPartPrimitiveText = forwardRef7((props, forwardedRef) => {
1239
+ var ContentPartPrimitiveText = forwardRef7(({ smooth, ...rest }, forwardedRef) => {
1166
1240
  const {
1167
- part: { text },
1168
- status
1241
+ status,
1242
+ part: { text }
1169
1243
  } = useContentPartText();
1170
- return /* @__PURE__ */ jsx14(Primitive4.span, { "data-status": status, ...props, ref: forwardedRef, children: text });
1244
+ const smoothText = useSmooth(text, smooth);
1245
+ return /* @__PURE__ */ jsx14(Primitive4.span, { "data-status": status, ...rest, ref: forwardedRef, children: smoothText });
1171
1246
  });
1172
1247
  ContentPartPrimitiveText.displayName = "ContentPartPrimitive.Text";
1173
1248
 
@@ -1176,14 +1251,18 @@ import { Primitive as Primitive5 } from "@radix-ui/react-primitive";
1176
1251
  import { forwardRef as forwardRef8 } from "react";
1177
1252
  import { jsx as jsx15 } from "react/jsx-runtime";
1178
1253
  var ContentPartPrimitiveImage = forwardRef8((props, forwardedRef) => {
1179
- const image = useContentPartImage();
1254
+ const {
1255
+ part: { image }
1256
+ } = useContentPartImage();
1180
1257
  return /* @__PURE__ */ jsx15(Primitive5.img, { src: image, ...props, ref: forwardedRef });
1181
1258
  });
1182
1259
  ContentPartPrimitiveImage.displayName = "ContentPartPrimitive.Image";
1183
1260
 
1184
1261
  // src/primitives/contentPart/ContentPartDisplay.tsx
1185
1262
  var ContentPartPrimitiveDisplay = () => {
1186
- const display = useContentPartDisplay();
1263
+ const {
1264
+ part: { display }
1265
+ } = useContentPartDisplay();
1187
1266
  return display ?? null;
1188
1267
  };
1189
1268
  ContentPartPrimitiveDisplay.displayName = "ContentPartPrimitive.Display";
@@ -1203,7 +1282,7 @@ import { jsx as jsx16, jsxs as jsxs2 } from "react/jsx-runtime";
1203
1282
  var defaultComponents = {
1204
1283
  Text: () => /* @__PURE__ */ jsxs2("p", { style: { whiteSpace: "pre-line" }, children: [
1205
1284
  /* @__PURE__ */ jsx16(ContentPartPrimitiveText, {}),
1206
- /* @__PURE__ */ jsx16(ContentPartPrimitiveInProgress, { children: " \u25CF" })
1285
+ /* @__PURE__ */ jsx16(ContentPartPrimitiveInProgress, { children: /* @__PURE__ */ jsx16("span", { style: { fontFamily: "revert" }, children: " \u25CF" }) })
1207
1286
  ] }),
1208
1287
  Image: () => /* @__PURE__ */ jsx16(ContentPartPrimitiveImage, {}),
1209
1288
  UI: () => /* @__PURE__ */ jsx16(ContentPartPrimitiveDisplay, {}),
@@ -1329,7 +1408,7 @@ import { Slot } from "@radix-ui/react-slot";
1329
1408
  import {
1330
1409
  forwardRef as forwardRef11,
1331
1410
  useCallback as useCallback15,
1332
- useEffect as useEffect8,
1411
+ useEffect as useEffect9,
1333
1412
  useRef as useRef4
1334
1413
  } from "react";
1335
1414
  import TextareaAutosize from "react-textarea-autosize";
@@ -1348,7 +1427,8 @@ var ComposerPrimitiveInput = forwardRef11(
1348
1427
  const ref = useComposedRefs2(forwardedRef, textareaRef);
1349
1428
  useEscapeKeydown((e) => {
1350
1429
  const composer = useComposer.getState();
1351
- if (composer.cancel()) {
1430
+ if (composer.canCancel) {
1431
+ composer.cancel();
1352
1432
  e.preventDefault();
1353
1433
  }
1354
1434
  });
@@ -1372,7 +1452,7 @@ var ComposerPrimitiveInput = forwardRef11(
1372
1452
  textareaRef.current.value.length
1373
1453
  );
1374
1454
  }, [autoFocusEnabled]);
1375
- useEffect8(() => focus(), [focus]);
1455
+ useEffect9(() => focus(), [focus]);
1376
1456
  useOnComposerFocus(() => {
1377
1457
  if (type === "new") {
1378
1458
  focus();
@@ -1533,11 +1613,11 @@ var useOnResizeContent = (callback) => {
1533
1613
 
1534
1614
  // src/utils/hooks/useOnScrollToBottom.tsx
1535
1615
  import { useCallbackRef as useCallbackRef3 } from "@radix-ui/react-use-callback-ref";
1536
- import { useEffect as useEffect9 } from "react";
1616
+ import { useEffect as useEffect10 } from "react";
1537
1617
  var useOnScrollToBottom = (callback) => {
1538
1618
  const callbackRef = useCallbackRef3(callback);
1539
1619
  const { useViewport } = useThreadContext();
1540
- useEffect9(() => {
1620
+ useEffect10(() => {
1541
1621
  return useViewport.getState().onScrollToBottom(() => {
1542
1622
  callbackRef();
1543
1623
  });
@@ -1612,7 +1692,7 @@ ThreadPrimitiveViewport.displayName = "ThreadPrimitive.Viewport";
1612
1692
  import { memo as memo3 } from "react";
1613
1693
 
1614
1694
  // src/context/providers/MessageProvider.tsx
1615
- import { useEffect as useEffect10, useState as useState5 } from "react";
1695
+ import { useEffect as useEffect11, useState as useState6 } from "react";
1616
1696
  import { create as create12 } from "zustand";
1617
1697
 
1618
1698
  // src/context/stores/EditComposer.ts
@@ -1622,20 +1702,19 @@ var makeEditComposerStore = ({
1622
1702
  onSend
1623
1703
  }) => create10()((set, get, store) => ({
1624
1704
  ...makeBaseComposer(set, get, store),
1705
+ canCancel: false,
1625
1706
  isEditing: false,
1626
1707
  edit: () => {
1627
1708
  const value = onEdit();
1628
- set({ isEditing: true, value });
1709
+ set({ isEditing: true, canCancel: true, value });
1629
1710
  },
1630
1711
  send: () => {
1631
1712
  const value = get().value;
1632
- set({ isEditing: false });
1713
+ set({ isEditing: false, canCancel: false });
1633
1714
  onSend(value);
1634
1715
  },
1635
1716
  cancel: () => {
1636
- if (!get().isEditing) return false;
1637
- set({ isEditing: false });
1638
- return true;
1717
+ set({ isEditing: false, canCancel: false });
1639
1718
  }
1640
1719
  }));
1641
1720
 
@@ -1675,7 +1754,7 @@ var syncMessage = (messages, getBranches, useMessage, messageIndex) => {
1675
1754
  };
1676
1755
  var useMessageContext2 = (messageIndex) => {
1677
1756
  const { useThreadMessages, useThreadActions } = useThreadContext();
1678
- const [context] = useState5(() => {
1757
+ const [context] = useState6(() => {
1679
1758
  const useMessage = create12(() => ({}));
1680
1759
  const useMessageUtils = makeMessageUtilsStore();
1681
1760
  const useEditComposer = makeEditComposerStore({
@@ -1712,7 +1791,7 @@ var useMessageContext2 = (messageIndex) => {
1712
1791
  );
1713
1792
  return { useMessage, useMessageUtils, useEditComposer };
1714
1793
  });
1715
- useEffect10(() => {
1794
+ useEffect11(() => {
1716
1795
  return useThreadMessages.subscribe((thread) => {
1717
1796
  syncMessage(
1718
1797
  thread,
@@ -1756,9 +1835,9 @@ var ThreadMessageImpl = ({
1756
1835
  };
1757
1836
  var ThreadMessage = memo3(
1758
1837
  ThreadMessageImpl,
1759
- (prev, next) => prev.messageIndex === next.messageIndex && prev.components.UserMessage === next.components.UserMessage && prev.components.EditComposer === next.components.EditComposer && prev.components.AssistantMessage === next.components.AssistantMessage
1838
+ (prev, next) => prev.messageIndex === next.messageIndex && prev.components.Message === next.components.Message && prev.components.UserMessage === next.components.UserMessage && prev.components.EditComposer === next.components.EditComposer && prev.components.AssistantMessage === next.components.AssistantMessage
1760
1839
  );
1761
- var ThreadPrimitiveMessages = ({
1840
+ var ThreadPrimitiveMessagesImpl = ({
1762
1841
  components
1763
1842
  }) => {
1764
1843
  const { useThreadMessages } = useThreadContext();
@@ -1776,7 +1855,11 @@ var ThreadPrimitiveMessages = ({
1776
1855
  );
1777
1856
  });
1778
1857
  };
1779
- ThreadPrimitiveMessages.displayName = "ThreadPrimitive.Messages";
1858
+ ThreadPrimitiveMessagesImpl.displayName = "ThreadPrimitive.Messages";
1859
+ var ThreadPrimitiveMessages = memo3(
1860
+ ThreadPrimitiveMessagesImpl,
1861
+ (prev, next) => prev.components?.Message === next.components?.Message && prev.components?.UserMessage === next.components?.UserMessage && prev.components?.EditComposer === next.components?.EditComposer && prev.components?.AssistantMessage === next.components?.AssistantMessage
1862
+ );
1780
1863
 
1781
1864
  // src/primitives/thread/ThreadScrollToBottom.tsx
1782
1865
  var ThreadPrimitiveScrollToBottom = createActionButton(
@@ -1791,7 +1874,7 @@ var ThreadPrimitiveSuggestion = createActionButton(
1791
1874
  );
1792
1875
 
1793
1876
  // src/runtime/local/useLocalRuntime.tsx
1794
- import { useInsertionEffect as useInsertionEffect3, useState as useState6 } from "react";
1877
+ import { useInsertionEffect as useInsertionEffect3, useState as useState7 } from "react";
1795
1878
 
1796
1879
  // src/runtime/utils/idUtils.tsx
1797
1880
  import { customAlphabet } from "nanoid/non-secure";
@@ -1960,6 +2043,9 @@ var BaseAssistantRuntime = class {
1960
2043
  this._thread = _thread;
1961
2044
  this._unsubscribe = this._thread.subscribe(this.subscriptionHandler);
1962
2045
  }
2046
+ get capabilities() {
2047
+ return this._thread.capabilities;
2048
+ }
1963
2049
  _unsubscribe;
1964
2050
  get thread() {
1965
2051
  return this._thread;
@@ -2032,6 +2118,12 @@ var LocalRuntime = class extends BaseAssistantRuntime {
2032
2118
  );
2033
2119
  }
2034
2120
  };
2121
+ var CAPABILITIES = Object.freeze({
2122
+ edit: true,
2123
+ reload: true,
2124
+ cancel: true,
2125
+ copy: true
2126
+ });
2035
2127
  var LocalThreadRuntime = class {
2036
2128
  constructor(_configProviders, adapter) {
2037
2129
  this._configProviders = _configProviders;
@@ -2040,6 +2132,7 @@ var LocalThreadRuntime = class {
2040
2132
  _subscriptions = /* @__PURE__ */ new Set();
2041
2133
  abortController = null;
2042
2134
  repository = new MessageRepository();
2135
+ capabilities = CAPABILITIES;
2043
2136
  get messages() {
2044
2137
  return this.repository.getMessages();
2045
2138
  }
@@ -2123,7 +2216,7 @@ var LocalThreadRuntime = class {
2123
2216
 
2124
2217
  // src/runtime/local/useLocalRuntime.tsx
2125
2218
  var useLocalRuntime = (adapter) => {
2126
- const [runtime] = useState6(() => new LocalRuntime(adapter));
2219
+ const [runtime] = useState7(() => new LocalRuntime(adapter));
2127
2220
  useInsertionEffect3(() => {
2128
2221
  runtime.adapter = adapter;
2129
2222
  });
@@ -2135,7 +2228,8 @@ var internal_exports = {};
2135
2228
  __export(internal_exports, {
2136
2229
  BaseAssistantRuntime: () => BaseAssistantRuntime,
2137
2230
  MessageRepository: () => MessageRepository,
2138
- ProxyConfigProvider: () => ProxyConfigProvider
2231
+ ProxyConfigProvider: () => ProxyConfigProvider,
2232
+ useSmooth: () => useSmooth
2139
2233
  });
2140
2234
  export {
2141
2235
  actionBar_exports as ActionBarPrimitive,