@assistant-ui/react 0.5.29 → 0.5.31

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.mjs CHANGED
@@ -113,36 +113,35 @@ function useThreadContext(options) {
113
113
 
114
114
  // src/context/stores/Composer.ts
115
115
  import { create as create3 } from "zustand";
116
-
117
- // src/context/stores/BaseComposer.ts
118
- var makeBaseComposer = (set) => ({
119
- value: "",
120
- setValue: (value) => {
121
- set({ value });
122
- }
123
- });
124
-
125
- // src/context/stores/Composer.ts
126
- var makeComposerStore = (useThreadMessages, useThreadActions) => {
116
+ var makeComposerStore = (useThreadRuntime) => {
127
117
  const focusListeners = /* @__PURE__ */ new Set();
128
- return create3()((set, get, store) => {
118
+ return create3()((_, get) => {
119
+ const runtime = useThreadRuntime.getState();
129
120
  return {
130
- ...makeBaseComposer(set, get, store),
131
- get canCancel() {
132
- return useThreadActions.getState().capabilities.cancel;
121
+ get value() {
122
+ return get().text;
123
+ },
124
+ setValue(value) {
125
+ get().setText(value);
126
+ },
127
+ text: runtime.composer.text,
128
+ setText: (value) => {
129
+ useThreadRuntime.getState().composer.setText(value);
133
130
  },
131
+ canCancel: runtime.capabilities.cancel,
134
132
  isEditing: true,
135
133
  send: () => {
136
- const { setValue, value } = get();
137
- setValue("");
138
- useThreadActions.getState().append({
139
- parentId: useThreadMessages.getState().at(-1)?.id ?? null,
134
+ const runtime2 = useThreadRuntime.getState();
135
+ const text = runtime2.composer.text;
136
+ runtime2.composer.setText("");
137
+ runtime2.append({
138
+ parentId: runtime2.messages.at(-1)?.id ?? null,
140
139
  role: "user",
141
- content: [{ type: "text", text: value }]
140
+ content: [{ type: "text", text }]
142
141
  });
143
142
  },
144
143
  cancel: () => {
145
- useThreadActions.getState().cancelRun();
144
+ useThreadRuntime.getState().cancelRun();
146
145
  },
147
146
  focus: () => {
148
147
  for (const listener of focusListeners) {
@@ -163,14 +162,10 @@ var makeComposerStore = (useThreadMessages, useThreadActions) => {
163
162
  import { create as create4 } from "zustand";
164
163
  var getThreadStateFromRuntime = (runtime) => {
165
164
  const lastMessage = runtime.messages.at(-1);
166
- if (lastMessage?.role !== "assistant")
167
- return Object.freeze({
168
- isDisabled: runtime.isDisabled,
169
- isRunning: false
170
- });
171
165
  return Object.freeze({
166
+ capabilities: runtime.capabilities,
172
167
  isDisabled: runtime.isDisabled,
173
- isRunning: lastMessage.status.type === "running"
168
+ isRunning: lastMessage?.role !== "assistant" ? false : lastMessage.status.type === "running"
174
169
  });
175
170
  };
176
171
  var makeThreadStore = (runtimeRef) => {
@@ -203,9 +198,6 @@ import { create as create6 } from "zustand";
203
198
  var makeThreadActionStore = (runtimeStore) => {
204
199
  return create6(
205
200
  () => Object.freeze({
206
- get capabilities() {
207
- return runtimeStore.getState().capabilities;
208
- },
209
201
  getBranches: (messageId) => runtimeStore.getState().getBranches(messageId),
210
202
  switchToBranch: (branchId) => runtimeStore.getState().switchToBranch(branchId),
211
203
  startRun: (parentId) => runtimeStore.getState().startRun(parentId),
@@ -258,7 +250,7 @@ var ThreadProvider = ({
258
250
  const useThreadMessages = makeThreadMessagesStore(useThreadRuntime);
259
251
  const useThreadActions = makeThreadActionStore(useThreadRuntime);
260
252
  const useViewport = makeThreadViewportStore();
261
- const useComposer = makeComposerStore(useThreadMessages, useThreadActions);
253
+ const useComposer = makeComposerStore(useThreadRuntime);
262
254
  return {
263
255
  useThread,
264
256
  useThreadRuntime,
@@ -274,15 +266,23 @@ var ThreadProvider = ({
274
266
  const onThreadUpdate = () => {
275
267
  const oldState = context.useThread.getState();
276
268
  const state = getThreadStateFromRuntime(thread);
277
- if (oldState.isDisabled !== state.isDisabled || oldState.isRunning !== state.isRunning) {
269
+ if (oldState.isDisabled !== state.isDisabled || oldState.isRunning !== state.isRunning || // TODO ensure capabilities is memoized
270
+ oldState.capabilities !== state.capabilities) {
278
271
  context.useThread.setState(
279
- getThreadStateFromRuntime(thread),
272
+ state,
280
273
  true
281
274
  );
282
275
  }
283
276
  if (thread.messages !== context.useThreadMessages.getState()) {
284
277
  context.useThreadMessages.setState(thread.messages, true);
285
278
  }
279
+ const composerState = context.useComposer.getState();
280
+ if (thread.composer.text !== composerState.text || state.capabilities.cancel !== composerState.canCancel) {
281
+ context.useComposer.setState({
282
+ text: thread.composer.text,
283
+ canCancel: state.capabilities.cancel
284
+ });
285
+ }
286
286
  };
287
287
  onThreadUpdate();
288
288
  return thread.subscribe(onThreadUpdate);
@@ -552,7 +552,7 @@ var useActionBarCopy = ({
552
552
  const callback = useCallback5(() => {
553
553
  const { message } = useMessage.getState();
554
554
  const { setIsCopied } = useMessageUtils.getState();
555
- const { isEditing, value: composerValue } = useEditComposer.getState();
555
+ const { isEditing, text: composerValue } = useEditComposer.getState();
556
556
  const valueToCopy = isEditing ? composerValue : getThreadMessageText(message);
557
557
  navigator.clipboard.writeText(valueToCopy).then(() => {
558
558
  setIsCopied(true);
@@ -680,7 +680,7 @@ var useComposerSend = () => {
680
680
  const { useComposer } = useComposerContext();
681
681
  const disabled = useCombinedStore(
682
682
  [useThread, useComposer],
683
- (t, c) => t.isRunning || !c.isEditing || c.value.length === 0
683
+ (t, c) => t.isRunning || !c.isEditing || c.text.length === 0
684
684
  );
685
685
  const callback = useCallback11(() => {
686
686
  const composerState = useComposer.getState();
@@ -799,9 +799,9 @@ var useThreadSuggestion = ({
799
799
  const composer = useComposer.getState();
800
800
  if (autoSend && !thread.isRunning) {
801
801
  append(prompt);
802
- composer.setValue("");
802
+ composer.setText("");
803
803
  } else {
804
- composer.setValue(prompt);
804
+ composer.setText(prompt);
805
805
  }
806
806
  }, [useThread, useComposer, autoSend, append, prompt]);
807
807
  if (disabled) return null;
@@ -1562,7 +1562,7 @@ var ComposerPrimitiveInput = forwardRef12(
1562
1562
  const { useComposer, type } = useComposerContext();
1563
1563
  const value = useComposer((c) => {
1564
1564
  if (!c.isEditing) return "";
1565
- return c.value;
1565
+ return c.text;
1566
1566
  });
1567
1567
  const Component = asChild ? Slot : TextareaAutosize;
1568
1568
  const isDisabled = useThread((t) => t.isDisabled) ?? disabledProp ?? false;
@@ -1577,6 +1577,7 @@ var ComposerPrimitiveInput = forwardRef12(
1577
1577
  });
1578
1578
  const handleKeyPress = (e) => {
1579
1579
  if (isDisabled) return;
1580
+ if (e.nativeEvent.isComposing) return;
1580
1581
  if (e.key === "Enter" && e.shiftKey === false) {
1581
1582
  const { isRunning } = useThread.getState();
1582
1583
  if (!isRunning) {
@@ -1612,7 +1613,7 @@ var ComposerPrimitiveInput = forwardRef12(
1612
1613
  onChange: composeEventHandlers5(onChange, (e) => {
1613
1614
  const composerState = useComposer.getState();
1614
1615
  if (!composerState.isEditing) return;
1615
- return composerState.setValue(e.target.value);
1616
+ return composerState.setText(e.target.value);
1616
1617
  }),
1617
1618
  onKeyDown: composeEventHandlers5(onKeyDown, handleKeyPress)
1618
1619
  }
@@ -1627,7 +1628,7 @@ import { Primitive as Primitive7 } from "@radix-ui/react-primitive";
1627
1628
  import { jsx as jsx21 } from "react/jsx-runtime";
1628
1629
  var ComposerPrimitiveSend = forwardRef13(({ disabled, ...rest }, ref) => {
1629
1630
  const { useComposer } = useComposerContext();
1630
- const hasValue = useComposer((c) => c.isEditing && c.value.length > 0);
1631
+ const hasValue = useComposer((c) => c.isEditing && c.text.length > 0);
1631
1632
  return /* @__PURE__ */ jsx21(
1632
1633
  Primitive7.button,
1633
1634
  {
@@ -1841,18 +1842,27 @@ import { create as create12 } from "zustand";
1841
1842
  var makeEditComposerStore = ({
1842
1843
  onEdit,
1843
1844
  onSend
1844
- }) => create12()((set, get, store) => ({
1845
- ...makeBaseComposer(set, get, store),
1845
+ }) => create12()((set, get) => ({
1846
+ get value() {
1847
+ return get().text;
1848
+ },
1849
+ setValue(value) {
1850
+ get().setText(value);
1851
+ },
1852
+ text: "",
1853
+ setText: (text) => {
1854
+ set({ text });
1855
+ },
1846
1856
  canCancel: false,
1847
1857
  isEditing: false,
1848
1858
  edit: () => {
1849
- const value = onEdit();
1850
- set({ isEditing: true, canCancel: true, value });
1859
+ const text = onEdit();
1860
+ set({ isEditing: true, canCancel: true, text });
1851
1861
  },
1852
1862
  send: () => {
1853
- const value = get().value;
1863
+ const text = get().text;
1854
1864
  set({ isEditing: false, canCancel: false });
1855
- onSend(value);
1865
+ onSend(text);
1856
1866
  },
1857
1867
  cancel: () => {
1858
1868
  set({ isEditing: false, canCancel: false });
@@ -2736,6 +2746,13 @@ var LocalThreadRuntime = class {
2736
2746
  get messages() {
2737
2747
  return this.repository.getMessages();
2738
2748
  }
2749
+ composer = {
2750
+ text: "",
2751
+ setText: (value) => {
2752
+ this.composer.text = value;
2753
+ this.notifySubscribers();
2754
+ }
2755
+ };
2739
2756
  getBranches(messageId) {
2740
2757
  return this.repository.getBranches(messageId);
2741
2758
  }
@@ -3027,34 +3044,51 @@ var ExternalStoreThreadRuntime = class {
3027
3044
  _subscriptions = /* @__PURE__ */ new Set();
3028
3045
  repository = new MessageRepository();
3029
3046
  assistantOptimisticId = null;
3047
+ _capabilities = {
3048
+ switchToBranch: false,
3049
+ edit: false,
3050
+ reload: false,
3051
+ cancel: false,
3052
+ copy: false
3053
+ };
3030
3054
  get capabilities() {
3031
- return {
3032
- switchToBranch: this._store.setMessages !== void 0,
3033
- edit: this._store.onEdit !== void 0,
3034
- reload: this._store.onReload !== void 0,
3035
- cancel: this._store.onCancel !== void 0,
3036
- copy: this._store.onCopy !== null
3037
- };
3055
+ return this._capabilities;
3038
3056
  }
3039
3057
  messages = [];
3040
3058
  isDisabled = false;
3041
3059
  converter = new ThreadMessageConverter();
3042
3060
  _store;
3061
+ composer = {
3062
+ text: "",
3063
+ setText: (value) => {
3064
+ this.composer.text = value;
3065
+ this.notifySubscribers();
3066
+ }
3067
+ };
3043
3068
  constructor(store) {
3044
3069
  this.store = store;
3045
3070
  }
3046
3071
  set store(store) {
3072
+ if (this._store === store) return;
3073
+ const isRunning = store.isRunning ?? false;
3074
+ this.isDisabled = store.isDisabled ?? false;
3047
3075
  const oldStore = this._store;
3076
+ this._store = store;
3077
+ this._capabilities = {
3078
+ switchToBranch: this._store.setMessages !== void 0,
3079
+ edit: this._store.onEdit !== void 0,
3080
+ reload: this._store.onReload !== void 0,
3081
+ cancel: this._store.onCancel !== void 0,
3082
+ copy: this._store.onCopy !== null
3083
+ };
3048
3084
  if (oldStore) {
3049
3085
  if (oldStore.convertMessage !== store.convertMessage) {
3050
3086
  this.converter = new ThreadMessageConverter();
3051
- } else if (oldStore.isDisabled === store.isDisabled && oldStore.isRunning === store.isRunning && oldStore.messages === store.messages) {
3087
+ } else if (oldStore.isRunning === store.isRunning && oldStore.messages === store.messages) {
3088
+ this.notifySubscribers();
3052
3089
  return;
3053
3090
  }
3054
3091
  }
3055
- this._store = store;
3056
- const isRunning = store.isRunning ?? false;
3057
- const isDisabled = store.isDisabled ?? false;
3058
3092
  const convertCallback = (cache, m, idx) => {
3059
3093
  if (!store.convertMessage) return m;
3060
3094
  const isLast = idx === store.messages.length - 1;
@@ -3095,7 +3129,9 @@ var ExternalStoreThreadRuntime = class {
3095
3129
  this.assistantOptimisticId ?? messages.at(-1)?.id ?? null
3096
3130
  );
3097
3131
  this.messages = this.repository.getMessages();
3098
- this.isDisabled = isDisabled;
3132
+ this.notifySubscribers();
3133
+ }
3134
+ notifySubscribers() {
3099
3135
  for (const callback of this._subscriptions) callback();
3100
3136
  }
3101
3137
  getBranches(messageId) {
@@ -3220,14 +3256,14 @@ import { CheckIcon, CopyIcon, RefreshCwIcon } from "lucide-react";
3220
3256
  import { Fragment as Fragment4, jsx as jsx31, jsxs as jsxs5 } from "react/jsx-runtime";
3221
3257
  var useAllowCopy = () => {
3222
3258
  const { assistantMessage: { allowCopy = true } = {} } = useThreadConfig();
3223
- const { useThreadActions } = useThreadContext();
3224
- const copySupported = useThreadActions((t) => t.capabilities.copy);
3259
+ const { useThread } = useThreadContext();
3260
+ const copySupported = useThread((t) => t.capabilities.copy);
3225
3261
  return copySupported && allowCopy;
3226
3262
  };
3227
3263
  var useAllowReload = () => {
3228
3264
  const { assistantMessage: { allowReload = true } = {} } = useThreadConfig();
3229
- const { useThreadActions } = useThreadContext();
3230
- const reloadSupported = useThreadActions((t) => t.capabilities.reload);
3265
+ const { useThread } = useThreadContext();
3266
+ const reloadSupported = useThread((t) => t.capabilities.reload);
3231
3267
  return reloadSupported && allowReload;
3232
3268
  };
3233
3269
  var AssistantActionBar = () => {
@@ -3296,8 +3332,8 @@ import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
3296
3332
  import { jsx as jsx32, jsxs as jsxs6 } from "react/jsx-runtime";
3297
3333
  var useAllowBranchPicker = () => {
3298
3334
  const { branchPicker: { allowBranchPicker = true } = {} } = useThreadConfig();
3299
- const { useThreadActions } = useThreadContext();
3300
- const branchPickerSupported = useThreadActions((t) => t.capabilities.edit);
3335
+ const { useThread } = useThreadContext();
3336
+ const branchPickerSupported = useThread((t) => t.capabilities.edit);
3301
3337
  return branchPickerSupported && allowBranchPicker;
3302
3338
  };
3303
3339
  var BranchPicker = () => {
@@ -3457,6 +3493,8 @@ var CircleStopIcon = () => {
3457
3493
  xmlns: "http://www.w3.org/2000/svg",
3458
3494
  viewBox: "0 0 16 16",
3459
3495
  fill: "currentColor",
3496
+ width: "16",
3497
+ height: "16",
3460
3498
  children: /* @__PURE__ */ jsx36("rect", { width: "10", height: "10", x: "3", y: "3", rx: "2" })
3461
3499
  }
3462
3500
  );
@@ -3493,8 +3531,8 @@ var ComposerInput = forwardRef22(
3493
3531
  );
3494
3532
  ComposerInput.displayName = "ComposerInput";
3495
3533
  var useAllowCancel = () => {
3496
- const { useThreadActions } = useThreadContext();
3497
- const cancelSupported = useThreadActions((t) => t.capabilities.cancel);
3534
+ const { useThread } = useThreadContext();
3535
+ const cancelSupported = useThread((t) => t.capabilities.cancel);
3498
3536
  return cancelSupported;
3499
3537
  };
3500
3538
  var ComposerAction = () => {
@@ -3620,8 +3658,8 @@ import { PencilIcon } from "lucide-react";
3620
3658
  import { jsx as jsx39 } from "react/jsx-runtime";
3621
3659
  var useAllowEdit = () => {
3622
3660
  const { userMessage: { allowEdit = true } = {} } = useThreadConfig();
3623
- const { useThreadActions } = useThreadContext();
3624
- const editSupported = useThreadActions((t) => t.capabilities.edit);
3661
+ const { useThread } = useThreadContext();
3662
+ const editSupported = useThread((t) => t.capabilities.edit);
3625
3663
  return editSupported && allowEdit;
3626
3664
  };
3627
3665
  var UserActionBar = () => {