@assistant-ui/react 0.5.41 → 0.5.42

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
@@ -1072,54 +1072,6 @@ var useEdgeRuntime = ({
1072
1072
  // src/runtimes/local/shouldContinue.tsx
1073
1073
  var shouldContinue = (result) => result.status?.type === "requires-action" && result.status.reason === "tool-calls" && result.content.every((c) => c.type !== "tool-call" || !!c.result);
1074
1074
 
1075
- // src/utils/getThreadMessageText.tsx
1076
- var getThreadMessageText = (message) => {
1077
- const textParts = message.content.filter(
1078
- (part) => part.type === "text"
1079
- );
1080
- return textParts.map((part) => part.text).join("\n\n");
1081
- };
1082
-
1083
- // src/runtimes/speech/WebSpeechSynthesisAdapter.ts
1084
- var WebSpeechSynthesisAdapter = class {
1085
- speak(message) {
1086
- const text = getThreadMessageText(message);
1087
- const utterance = new SpeechSynthesisUtterance(text);
1088
- let ended = false;
1089
- const endHandlers = /* @__PURE__ */ new Set();
1090
- const handleEnd = () => {
1091
- if (ended) return;
1092
- ended = true;
1093
- endHandlers.forEach((handler) => handler());
1094
- };
1095
- utterance.addEventListener("end", handleEnd);
1096
- utterance.addEventListener("error", handleEnd);
1097
- window.speechSynthesis.speak(utterance);
1098
- return {
1099
- stop: () => {
1100
- window.speechSynthesis.cancel();
1101
- handleEnd();
1102
- },
1103
- onEnd: (callback) => {
1104
- if (ended) {
1105
- let cancelled = false;
1106
- queueMicrotask(() => {
1107
- if (!cancelled) callback();
1108
- });
1109
- return () => {
1110
- cancelled = true;
1111
- };
1112
- } else {
1113
- endHandlers.add(callback);
1114
- return () => {
1115
- endHandlers.delete(callback);
1116
- };
1117
- }
1118
- }
1119
- };
1120
- }
1121
- };
1122
-
1123
1075
  // src/runtimes/local/LocalThreadRuntime.tsx
1124
1076
  var LocalThreadRuntime = class {
1125
1077
  constructor(configProvider, adapter, { initialMessages, ...options }) {
@@ -1158,6 +1110,9 @@ var LocalThreadRuntime = class {
1158
1110
  }
1159
1111
  };
1160
1112
  _options;
1113
+ get options() {
1114
+ return this._options;
1115
+ }
1161
1116
  set options({ initialMessages, ...options }) {
1162
1117
  this._options = options;
1163
1118
  const canSpeak = options.adapters?.speech !== void 0;
@@ -1239,7 +1194,7 @@ var LocalThreadRuntime = class {
1239
1194
  this.repository.addOrUpdateMessage(parentId, message);
1240
1195
  this.notifySubscribers();
1241
1196
  };
1242
- const maxToolRoundtrips = this._options.maxToolRoundtrips ?? 1;
1197
+ const maxToolRoundtrips = this.options.maxToolRoundtrips ?? 1;
1243
1198
  const toolRoundtrips = message.metadata?.roundtrips?.length ?? 0;
1244
1199
  if (toolRoundtrips > maxToolRoundtrips) {
1245
1200
  updateMessage({
@@ -1334,10 +1289,24 @@ var LocalThreadRuntime = class {
1334
1289
  this.performRoundtrip(parentId, message);
1335
1290
  }
1336
1291
  }
1292
+ // TODO lift utterance state to thread runtime
1293
+ _utterance;
1337
1294
  speak(messageId) {
1295
+ const adapter = this.options.adapters?.speech;
1296
+ if (!adapter) throw new Error("Speech adapter not configured");
1338
1297
  const { message } = this.repository.getMessage(messageId);
1339
- const adapter = new WebSpeechSynthesisAdapter();
1340
- return adapter.speak(message);
1298
+ if (this._utterance) {
1299
+ this._utterance.cancel();
1300
+ this._utterance = void 0;
1301
+ }
1302
+ const utterance = adapter.speak(message);
1303
+ utterance.onEnd(() => {
1304
+ if (this._utterance === utterance) {
1305
+ this._utterance = void 0;
1306
+ }
1307
+ });
1308
+ this._utterance = utterance;
1309
+ return this._utterance;
1341
1310
  }
1342
1311
  export() {
1343
1312
  return this.repository.export();
@@ -1497,6 +1466,14 @@ var fromThreadMessageLike = (like, fallbackId, fallbackStatus) => {
1497
1466
  }
1498
1467
  };
1499
1468
 
1469
+ // src/utils/getThreadMessageText.tsx
1470
+ var getThreadMessageText = (message) => {
1471
+ const textParts = message.content.filter(
1472
+ (part) => part.type === "text"
1473
+ );
1474
+ return textParts.map((part) => part.text).join("\n\n");
1475
+ };
1476
+
1500
1477
  // src/runtimes/external-store/ExternalStoreThreadRuntime.tsx
1501
1478
  var hasUpcomingMessage = (isRunning, messages) => {
1502
1479
  return isRunning && messages[messages.length - 1]?.role !== "assistant";
@@ -1743,6 +1720,47 @@ var useDangerousInBrowserRuntime = ({
1743
1720
  return useLocalRuntime(adapter, { initialMessages });
1744
1721
  };
1745
1722
 
1723
+ // src/runtimes/speech/WebSpeechSynthesisAdapter.ts
1724
+ var WebSpeechSynthesisAdapter = class {
1725
+ speak(message) {
1726
+ const text = getThreadMessageText(message);
1727
+ const utterance = new SpeechSynthesisUtterance(text);
1728
+ const endHandlers = /* @__PURE__ */ new Set();
1729
+ const handleEnd = (reason, error) => {
1730
+ if (res.status.type === "ended") return;
1731
+ res.status = { type: "ended", reason, error };
1732
+ endHandlers.forEach((handler) => handler());
1733
+ };
1734
+ utterance.addEventListener("end", () => handleEnd("finished"));
1735
+ utterance.addEventListener("error", (e) => handleEnd("error", e.error));
1736
+ window.speechSynthesis.speak(utterance);
1737
+ const res = {
1738
+ status: { type: "running" },
1739
+ cancel: () => {
1740
+ window.speechSynthesis.cancel();
1741
+ handleEnd("cancelled");
1742
+ },
1743
+ onEnd: (callback) => {
1744
+ if (res.status.type === "ended") {
1745
+ let cancelled = false;
1746
+ queueMicrotask(() => {
1747
+ if (!cancelled) callback();
1748
+ });
1749
+ return () => {
1750
+ cancelled = true;
1751
+ };
1752
+ } else {
1753
+ endHandlers.add(callback);
1754
+ return () => {
1755
+ endHandlers.delete(callback);
1756
+ };
1757
+ }
1758
+ }
1759
+ };
1760
+ return res;
1761
+ }
1762
+ };
1763
+
1746
1764
  // src/context/providers/ThreadProvider.tsx
1747
1765
  import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
1748
1766
  var ThreadProvider = ({
@@ -3335,7 +3353,7 @@ var makeMessageUtilsStore = () => create14((set) => {
3335
3353
  },
3336
3354
  isSpeaking: false,
3337
3355
  stopSpeaking: () => {
3338
- utterance?.stop();
3356
+ utterance?.cancel();
3339
3357
  },
3340
3358
  addUtterance: (utt) => {
3341
3359
  utterance = utt;
@@ -3537,28 +3555,29 @@ import {
3537
3555
  StopCircleIcon
3538
3556
  } from "lucide-react";
3539
3557
  import { Fragment as Fragment4, jsx as jsx32, jsxs as jsxs5 } from "react/jsx-runtime";
3540
- var useAllowCopy = () => {
3558
+ var useAllowCopy = (ensureCapability = false) => {
3541
3559
  const { assistantMessage: { allowCopy = true } = {} } = useThreadConfig();
3542
3560
  const { useThread } = useThreadContext();
3543
3561
  const copySupported = useThread((t) => t.capabilities.unstable_copy);
3544
- return copySupported && allowCopy;
3562
+ return allowCopy && (!ensureCapability || copySupported);
3545
3563
  };
3546
- var useAllowSpeak = () => {
3564
+ var useAllowSpeak = (ensureCapability = false) => {
3547
3565
  const { assistantMessage: { allowSpeak = true } = {} } = useThreadConfig();
3548
3566
  const { useThread } = useThreadContext();
3549
3567
  const speakSupported = useThread((t) => t.capabilities.speak);
3550
- return speakSupported && allowSpeak;
3568
+ return allowSpeak && (!ensureCapability || speakSupported);
3551
3569
  };
3552
- var useAllowReload = () => {
3570
+ var useAllowReload = (ensureCapability = false) => {
3553
3571
  const { assistantMessage: { allowReload = true } = {} } = useThreadConfig();
3554
3572
  const { useThread } = useThreadContext();
3555
3573
  const reloadSupported = useThread((t) => t.capabilities.reload);
3556
- return reloadSupported && allowReload;
3574
+ return allowReload && (!ensureCapability || reloadSupported);
3557
3575
  };
3558
3576
  var AssistantActionBar = () => {
3559
- const allowCopy = useAllowCopy();
3560
- const allowReload = useAllowReload();
3561
- if (!allowCopy && !allowReload) return null;
3577
+ const allowCopy = useAllowCopy(true);
3578
+ const allowReload = useAllowReload(true);
3579
+ const allowSpeak = useAllowSpeak(true);
3580
+ if (!allowCopy && !allowReload && !allowSpeak) return null;
3562
3581
  return /* @__PURE__ */ jsxs5(
3563
3582
  AssistantActionBarRoot,
3564
3583
  {
@@ -3566,9 +3585,9 @@ var AssistantActionBar = () => {
3566
3585
  autohide: "not-last",
3567
3586
  autohideFloat: "single-branch",
3568
3587
  children: [
3569
- /* @__PURE__ */ jsx32(AssistantActionBarSpeechControl, {}),
3570
- /* @__PURE__ */ jsx32(AssistantActionBarCopy, {}),
3571
- /* @__PURE__ */ jsx32(AssistantActionBarReload, {})
3588
+ allowSpeak && /* @__PURE__ */ jsx32(AssistantActionBarSpeechControl, {}),
3589
+ allowCopy && /* @__PURE__ */ jsx32(AssistantActionBarCopy, {}),
3590
+ allowReload && /* @__PURE__ */ jsx32(AssistantActionBarReload, {})
3572
3591
  ]
3573
3592
  }
3574
3593
  );
@@ -3584,8 +3603,6 @@ var AssistantActionBarCopy = forwardRef20((props, ref) => {
3584
3603
  assistantMessage: { copy: { tooltip = "Copy" } = {} } = {}
3585
3604
  } = {}
3586
3605
  } = useThreadConfig();
3587
- const allowCopy = useAllowCopy();
3588
- if (!allowCopy) return null;
3589
3606
  return /* @__PURE__ */ jsx32(actionBar_exports.Copy, { asChild: true, children: /* @__PURE__ */ jsx32(TooltipIconButton, { tooltip, ...props, ref, children: props.children ?? /* @__PURE__ */ jsxs5(Fragment4, { children: [
3590
3607
  /* @__PURE__ */ jsx32(message_exports.If, { copied: true, children: /* @__PURE__ */ jsx32(CheckIcon, {}) }),
3591
3608
  /* @__PURE__ */ jsx32(message_exports.If, { copied: false, children: /* @__PURE__ */ jsx32(CopyIcon, {}) })
@@ -3979,14 +3996,14 @@ import { forwardRef as forwardRef26 } from "react";
3979
3996
  import { forwardRef as forwardRef25 } from "react";
3980
3997
  import { PencilIcon } from "lucide-react";
3981
3998
  import { jsx as jsx40 } from "react/jsx-runtime";
3982
- var useAllowEdit = () => {
3999
+ var useAllowEdit = (ensureCapability = false) => {
3983
4000
  const { userMessage: { allowEdit = true } = {} } = useThreadConfig();
3984
4001
  const { useThread } = useThreadContext();
3985
4002
  const editSupported = useThread((t) => t.capabilities.edit);
3986
- return editSupported && allowEdit;
4003
+ return allowEdit && (!ensureCapability || editSupported);
3987
4004
  };
3988
4005
  var UserActionBar = () => {
3989
- const allowEdit = useAllowEdit();
4006
+ const allowEdit = useAllowEdit(true);
3990
4007
  if (!allowEdit) return null;
3991
4008
  return /* @__PURE__ */ jsx40(UserActionBarRoot, { hideWhenRunning: true, autohide: "not-last", children: /* @__PURE__ */ jsx40(UserActionBarEdit, {}) });
3992
4009
  };