@assistant-ui/react 0.5.29 → 0.5.31

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
@@ -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 = () => {