@webless/agent 0.7.0 → 0.7.2

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.
@@ -3372,6 +3372,126 @@ function toSpeechText(text) {
3372
3372
  function isSpeechSupported() {
3373
3373
  return typeof window !== "undefined" && "speechSynthesis" in window && typeof window.SpeechSynthesisUtterance === "function";
3374
3374
  }
3375
+ function stopSpeech() {
3376
+ if (!isSpeechSupported()) return;
3377
+ window.speechSynthesis.cancel();
3378
+ }
3379
+ var speechInterruptListeners = /* @__PURE__ */ new Set();
3380
+ var pageListenersAttached = false;
3381
+ var lastPathname = "";
3382
+ var originalPushState = null;
3383
+ var originalReplaceState = null;
3384
+ var patchedPushState = null;
3385
+ var patchedReplaceState = null;
3386
+ function currentPathname() {
3387
+ return window.location.pathname;
3388
+ }
3389
+ function notifySpeechInterrupts() {
3390
+ if (speechInterruptListeners.size === 0) return;
3391
+ stopSpeech();
3392
+ for (const listener of speechInterruptListeners) {
3393
+ listener();
3394
+ }
3395
+ }
3396
+ function interruptIfPathChanged(nextPath) {
3397
+ if (nextPath === lastPathname) return;
3398
+ lastPathname = nextPath;
3399
+ notifySpeechInterrupts();
3400
+ }
3401
+ function patchHistoryMethod(original) {
3402
+ return function patched(data, unused, url) {
3403
+ const result = original.call(this, data, unused, url);
3404
+ interruptIfPathChanged(currentPathname());
3405
+ return result;
3406
+ };
3407
+ }
3408
+ function historyMethodState(name) {
3409
+ return name === "pushState" ? {
3410
+ original: originalPushState,
3411
+ patched: patchedPushState,
3412
+ set(original, patched) {
3413
+ originalPushState = original;
3414
+ patchedPushState = patched;
3415
+ }
3416
+ } : {
3417
+ original: originalReplaceState,
3418
+ patched: patchedReplaceState,
3419
+ set(original, patched) {
3420
+ originalReplaceState = original;
3421
+ patchedReplaceState = patched;
3422
+ }
3423
+ };
3424
+ }
3425
+ function patchHistoryNamed(name) {
3426
+ const state = historyMethodState(name);
3427
+ const current = history[name];
3428
+ if (state.patched && current === state.patched) return;
3429
+ const patched = patchHistoryMethod(current);
3430
+ state.set(current, patched);
3431
+ history[name] = patched;
3432
+ }
3433
+ function restoreHistoryMethod(name) {
3434
+ const state = historyMethodState(name);
3435
+ if (!state.patched || !state.original || typeof history === "undefined") {
3436
+ return false;
3437
+ }
3438
+ if (history[name] !== state.patched) return false;
3439
+ history[name] = state.original;
3440
+ state.set(null, null);
3441
+ return true;
3442
+ }
3443
+ function handlePageHide() {
3444
+ notifySpeechInterrupts();
3445
+ }
3446
+ function handlePopState() {
3447
+ interruptIfPathChanged(currentPathname());
3448
+ }
3449
+ function handleVisibilityChange() {
3450
+ if (document.visibilityState === "hidden") {
3451
+ notifySpeechInterrupts();
3452
+ }
3453
+ }
3454
+ function attachPageListeners() {
3455
+ if (pageListenersAttached || typeof window === "undefined") return;
3456
+ pageListenersAttached = true;
3457
+ window.addEventListener("pagehide", handlePageHide);
3458
+ window.addEventListener("popstate", handlePopState);
3459
+ document.addEventListener("visibilitychange", handleVisibilityChange);
3460
+ }
3461
+ function detachPageListeners() {
3462
+ if (!pageListenersAttached || typeof window === "undefined") return;
3463
+ window.removeEventListener("pagehide", handlePageHide);
3464
+ window.removeEventListener("popstate", handlePopState);
3465
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
3466
+ pageListenersAttached = false;
3467
+ }
3468
+ function ensureSpeechInterruptsPatched() {
3469
+ if (typeof window === "undefined") return;
3470
+ lastPathname = currentPathname();
3471
+ patchHistoryNamed("pushState");
3472
+ patchHistoryNamed("replaceState");
3473
+ attachPageListeners();
3474
+ }
3475
+ function teardownSpeechInterrupts() {
3476
+ detachPageListeners();
3477
+ restoreHistoryMethod("pushState");
3478
+ restoreHistoryMethod("replaceState");
3479
+ if (!patchedPushState && !patchedReplaceState) {
3480
+ lastPathname = "";
3481
+ }
3482
+ }
3483
+ function subscribeSpeechInterrupts(onInterrupt) {
3484
+ if (typeof window === "undefined") return () => {
3485
+ };
3486
+ ensureSpeechInterruptsPatched();
3487
+ speechInterruptListeners.add(onInterrupt);
3488
+ return () => {
3489
+ speechInterruptListeners.delete(onInterrupt);
3490
+ if (speechInterruptListeners.size === 0) {
3491
+ teardownSpeechInterrupts();
3492
+ }
3493
+ };
3494
+ }
3375
3495
 
3376
3496
  // src/react/components/AgentRail/AgentRailOverlayContext.tsx
3377
3497
  import { createContext, useContext } from "react";
@@ -3637,15 +3757,20 @@ function MessageActions({
3637
3757
  const [menuOpen, setMenuOpen] = useState2(false);
3638
3758
  const [menuPosition, setMenuPosition] = useState2(null);
3639
3759
  const [speaking, setSpeaking] = useState2(false);
3760
+ const [speechSupported, setSpeechSupported] = useState2(null);
3640
3761
  const copyTimerRef = useRef2(null);
3641
3762
  const menuRef = useRef2(null);
3642
3763
  const portaledMenuRef = useRef2(null);
3643
3764
  const moreButtonRef = useRef2(null);
3644
3765
  const answeredLabel = formatAnsweredAt(answeredAt ?? 0);
3645
3766
  const resolvedSpeechText = speechText ?? toSpeechText(copyText);
3646
- const canReadAloud = readAloud && isSpeechSupported() && resolvedSpeechText;
3647
- const showMenu = Boolean(answeredLabel) || Boolean(receiptSteps) || canReadAloud;
3648
3767
  const canOpenReceipt = Boolean(receiptSteps) && Boolean(onOpenReceipt);
3768
+ const readAloudEligible = Boolean(readAloud && resolvedSpeechText);
3769
+ const canReadAloud = readAloudEligible && speechSupported === true;
3770
+ const showMenu = canOpenReceipt || (speechSupported === null ? readAloudEligible : canReadAloud);
3771
+ useLayoutEffect(() => {
3772
+ setSpeechSupported(isSpeechSupported());
3773
+ }, []);
3649
3774
  useLayoutEffect(() => {
3650
3775
  if (!menuOpen || !moreButtonRef.current || !portaledMenuRef.current) {
3651
3776
  setMenuPosition(null);
@@ -3663,17 +3788,16 @@ function MessageActions({
3663
3788
  })
3664
3789
  );
3665
3790
  }, [menuOpen, menuPortalRoot]);
3666
- useEffect2(
3667
- () => () => {
3791
+ useEffect2(() => {
3792
+ const unsubscribe = subscribeSpeechInterrupts(() => setSpeaking(false));
3793
+ return () => {
3794
+ unsubscribe();
3668
3795
  if (copyTimerRef.current !== null) {
3669
3796
  window.clearTimeout(copyTimerRef.current);
3670
3797
  }
3671
- if (speaking) window.speechSynthesis.cancel();
3672
- },
3673
- // Cancel speech only when this answer's actions unmount.
3674
- // eslint-disable-next-line react-hooks/exhaustive-deps
3675
- []
3676
- );
3798
+ stopSpeech();
3799
+ };
3800
+ }, []);
3677
3801
  useEffect2(() => {
3678
3802
  if (!menuOpen) return;
3679
3803
  const handlePointerDown = (event) => {
@@ -3712,14 +3836,14 @@ function MessageActions({
3712
3836
  if (!canReadAloud) return;
3713
3837
  setMenuOpen(false);
3714
3838
  if (speaking) {
3715
- window.speechSynthesis.cancel();
3839
+ stopSpeech();
3716
3840
  setSpeaking(false);
3717
3841
  return;
3718
3842
  }
3719
3843
  const utterance = new SpeechSynthesisUtterance(resolvedSpeechText);
3720
3844
  utterance.onend = () => setSpeaking(false);
3721
3845
  utterance.onerror = () => setSpeaking(false);
3722
- window.speechSynthesis.cancel();
3846
+ stopSpeech();
3723
3847
  window.speechSynthesis.speak(utterance);
3724
3848
  setSpeaking(true);
3725
3849
  }
@@ -5634,8 +5758,9 @@ function AgentRail({
5634
5758
  colorScheme = "auto",
5635
5759
  brandLabel = "",
5636
5760
  brandLogoUrl,
5637
- poweredByLabel = "Powered by Webless",
5761
+ poweredByLabel,
5638
5762
  disclaimerLabel,
5763
+ disclaimerLink,
5639
5764
  answerReceipt = false,
5640
5765
  readAloud = false,
5641
5766
  composerPlaceholder = "Ask anything\u2026",
@@ -6070,7 +6195,15 @@ function AgentRail({
6070
6195
  onRetry ? /* @__PURE__ */ jsx16("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
6071
6196
  ] }) : null
6072
6197
  ] }) }),
6073
- showDisclaimerLabel ? /* @__PURE__ */ jsx16("div", { className: "agent-rail__disclaimer", children: /* @__PURE__ */ jsx16("p", { children: resolvedDisclaimerLabel }) }) : null,
6198
+ showDisclaimerLabel ? /* @__PURE__ */ jsx16("div", { className: "agent-rail__disclaimer", children: /* @__PURE__ */ jsx16("p", { children: disclaimerLink ? /* @__PURE__ */ jsx16(
6199
+ "a",
6200
+ {
6201
+ href: disclaimerLink,
6202
+ rel: "noopener noreferrer",
6203
+ target: "_blank",
6204
+ children: resolvedDisclaimerLabel
6205
+ }
6206
+ ) : resolvedDisclaimerLabel }) }) : null,
6074
6207
  /* @__PURE__ */ jsxs15("div", { className: "agent-rail__composer-wrap", children: [
6075
6208
  showJumpToLatest ? /* @__PURE__ */ jsx16(
6076
6209
  "button",
@@ -6634,8 +6767,9 @@ function AgentWidget({
6634
6767
  brandLabel: agentName,
6635
6768
  brandLogoUrl: branding?.logoUrl,
6636
6769
  composerPlaceholder: branding?.composerPlaceholder ?? "Ask a question\u2026",
6637
- poweredByLabel: branding?.poweredByLabel ?? "Powered by Webless",
6770
+ poweredByLabel: branding?.poweredByLabel,
6638
6771
  disclaimerLabel: branding?.disclaimer,
6772
+ disclaimerLink: branding?.disclaimerLink,
6639
6773
  answerReceipt: branding?.answerReceipt,
6640
6774
  readAloud: branding?.readAloud,
6641
6775
  state,
@@ -6716,4 +6850,4 @@ export {
6716
6850
  sendAgentAnswerFeedback,
6717
6851
  AgentWidget
6718
6852
  };
6719
- //# sourceMappingURL=chunk-CYI3OSWG.js.map
6853
+ //# sourceMappingURL=chunk-HZUPH5Z7.js.map