@assistant-ui/react 0.3.0 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
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,