@assistant-ui/react 0.5.41 → 0.5.42

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