@webless/agent 0.7.0 → 0.7.3
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/{chunk-CYI3OSWG.js → chunk-GS65OJFL.js} +346 -141
- package/dist/chunk-GS65OJFL.js.map +1 -0
- package/dist/embed.cjs +352 -132
- package/dist/embed.cjs.map +1 -1
- package/dist/embed.css +108 -8
- package/dist/embed.css.map +1 -1
- package/dist/embed.d.cts +2 -2
- package/dist/embed.d.ts +2 -2
- package/dist/embed.js +16 -1
- package/dist/embed.js.map +1 -1
- package/dist/{manifest-BNJQfEow.d.cts → manifest-sR_C4rh_.d.cts} +1 -0
- package/dist/{manifest-BNJQfEow.d.ts → manifest-sR_C4rh_.d.ts} +1 -0
- package/dist/react.cjs +337 -132
- package/dist/react.cjs.map +1 -1
- package/dist/react.css +108 -8
- package/dist/react.css.map +1 -1
- package/dist/react.d.cts +4 -3
- package/dist/react.d.ts +4 -3
- package/dist/react.js +1 -1
- package/package.json +2 -1
- package/dist/chunk-CYI3OSWG.js.map +0 -1
package/dist/react.cjs
CHANGED
|
@@ -3801,42 +3801,72 @@ function emptyValues(form) {
|
|
|
3801
3801
|
for (const field of form?.fields ?? []) values[field.id] = "";
|
|
3802
3802
|
return values;
|
|
3803
3803
|
}
|
|
3804
|
+
function createDraft(form) {
|
|
3805
|
+
return {
|
|
3806
|
+
form,
|
|
3807
|
+
values: emptyValues(form),
|
|
3808
|
+
expanded: Boolean(form),
|
|
3809
|
+
submitted: false,
|
|
3810
|
+
dismissalSent: false,
|
|
3811
|
+
attempted: false
|
|
3812
|
+
};
|
|
3813
|
+
}
|
|
3804
3814
|
function Composer({
|
|
3805
3815
|
disabled = false,
|
|
3806
3816
|
placeholder = "Ask anything\u2026",
|
|
3807
3817
|
variant = "default",
|
|
3808
3818
|
form = null,
|
|
3819
|
+
allowFormResume = true,
|
|
3809
3820
|
onSubmit
|
|
3810
3821
|
}) {
|
|
3811
3822
|
const [value, setValue] = (0, import_react8.useState)("");
|
|
3812
|
-
const [
|
|
3813
|
-
() => emptyValues(form)
|
|
3814
|
-
);
|
|
3815
|
-
const [blurred, setBlurred] = (0, import_react8.useState)({});
|
|
3823
|
+
const [draft, setDraft] = (0, import_react8.useState)(() => createDraft(form));
|
|
3816
3824
|
const inputRef = (0, import_react8.useRef)(null);
|
|
3817
3825
|
const firstFieldRef = (0, import_react8.useRef)(null);
|
|
3826
|
+
const formRef = (0, import_react8.useRef)(null);
|
|
3818
3827
|
const formId = (0, import_react8.useId)();
|
|
3819
|
-
|
|
3820
|
-
const
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
setBlurred({});
|
|
3824
|
-
}, [form?.id]);
|
|
3828
|
+
if (form && form.id !== draft.form?.id) setDraft(createDraft(form));
|
|
3829
|
+
const savedForm = allowFormResume && !draft.submitted ? draft.form : null;
|
|
3830
|
+
const activeForm = !disabled && draft.expanded ? savedForm : null;
|
|
3831
|
+
const hasDraft = Object.values(draft.values).some((value2) => value2.trim());
|
|
3825
3832
|
(0, import_react8.useEffect)(() => {
|
|
3826
3833
|
if (activeForm) firstFieldRef.current?.focus();
|
|
3827
|
-
|
|
3834
|
+
else if ((savedForm || draft.submitted) && !disabled)
|
|
3835
|
+
inputRef.current?.focus();
|
|
3836
|
+
}, [activeForm?.id, disabled, savedForm?.id, draft.submitted]);
|
|
3828
3837
|
function submitChat() {
|
|
3829
3838
|
const trimmed = value.trim();
|
|
3830
3839
|
if (!trimmed || disabled) return;
|
|
3831
|
-
|
|
3840
|
+
const shareDismissal = savedForm && !draft.dismissalSent;
|
|
3841
|
+
onSubmit?.(
|
|
3842
|
+
shareDismissal ? `I'd like to keep chatting without sharing the requested details for now. Please don't ask for them again unless I choose to proceed with something that needs them.
|
|
3843
|
+
|
|
3844
|
+
${trimmed}` : trimmed
|
|
3845
|
+
);
|
|
3846
|
+
if (shareDismissal)
|
|
3847
|
+
setDraft((current) => ({ ...current, dismissalSent: true }));
|
|
3832
3848
|
setValue("");
|
|
3833
3849
|
inputRef.current?.focus();
|
|
3834
3850
|
}
|
|
3835
3851
|
function submitForm() {
|
|
3836
|
-
if (!activeForm || disabled
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3852
|
+
if (!activeForm || disabled) return;
|
|
3853
|
+
if (!isComposerFormComplete(activeForm, draft.values)) {
|
|
3854
|
+
setDraft((current) => ({ ...current, attempted: true }));
|
|
3855
|
+
const invalidField = activeForm.fields.find(
|
|
3856
|
+
(field) => !isValidComposerFieldValue(field, draft.values[field.id] ?? "")
|
|
3857
|
+
);
|
|
3858
|
+
const control = invalidField && formRef.current?.elements.namedItem(invalidField.id);
|
|
3859
|
+
if (control instanceof HTMLElement) control.focus();
|
|
3860
|
+
return;
|
|
3861
|
+
}
|
|
3862
|
+
onSubmit?.(formatComposerFormMessage(activeForm, draft.values));
|
|
3863
|
+
setDraft((current) => ({
|
|
3864
|
+
...current,
|
|
3865
|
+
values: emptyValues(activeForm),
|
|
3866
|
+
expanded: false,
|
|
3867
|
+
submitted: true,
|
|
3868
|
+
attempted: false
|
|
3869
|
+
}));
|
|
3840
3870
|
}
|
|
3841
3871
|
function handleSubmit(event) {
|
|
3842
3872
|
event.preventDefault();
|
|
@@ -3860,112 +3890,151 @@ function Composer({
|
|
|
3860
3890
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3861
3891
|
"form",
|
|
3862
3892
|
{
|
|
3893
|
+
ref: formRef,
|
|
3894
|
+
noValidate: true,
|
|
3863
3895
|
className: [
|
|
3864
3896
|
"composer",
|
|
3865
3897
|
variant === "dock" ? "composer--dock" : "",
|
|
3866
3898
|
activeForm ? "composer--form" : ""
|
|
3867
3899
|
].filter(Boolean).join(" "),
|
|
3868
3900
|
onSubmit: handleSubmit,
|
|
3869
|
-
children: activeForm ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3901
|
+
children: activeForm ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
3902
|
+
"div",
|
|
3903
|
+
{
|
|
3904
|
+
className: "composer__sheet",
|
|
3905
|
+
role: "group",
|
|
3906
|
+
"aria-label": "Contact details",
|
|
3907
|
+
children: [
|
|
3908
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "composer__form-heading", children: [
|
|
3909
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: "Share your details" }),
|
|
3910
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Optional" }),
|
|
3911
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: "You can also keep chatting." })
|
|
3912
|
+
] }),
|
|
3913
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "composer__fields", children: activeForm.fields.map((field, index) => {
|
|
3914
|
+
const fieldId = `${formId}-${field.id}`;
|
|
3915
|
+
const invalid = draft.attempted && !isValidComposerFieldValue(field, draft.values[field.id] ?? "");
|
|
3916
|
+
const controlProps = {
|
|
3917
|
+
id: fieldId,
|
|
3918
|
+
name: field.id,
|
|
3919
|
+
disabled,
|
|
3920
|
+
required: field.required,
|
|
3921
|
+
autoComplete: field.autocomplete,
|
|
3922
|
+
placeholder: field.placeholder,
|
|
3923
|
+
spellCheck: false,
|
|
3924
|
+
value: draft.values[field.id] ?? "",
|
|
3925
|
+
"aria-invalid": invalid || void 0,
|
|
3926
|
+
"aria-describedby": invalid ? `${fieldId}-error` : void 0,
|
|
3927
|
+
onChange: (event) => {
|
|
3928
|
+
const next = readComposerControlValue(event);
|
|
3929
|
+
setDraft((current) => ({
|
|
3930
|
+
...current,
|
|
3931
|
+
values: { ...current.values, [field.id]: next }
|
|
3932
|
+
}));
|
|
3933
|
+
},
|
|
3934
|
+
onKeyDown: handleFormKeyDown
|
|
3935
|
+
};
|
|
3936
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
3937
|
+
"div",
|
|
3938
|
+
{
|
|
3939
|
+
className: [
|
|
3940
|
+
"composer__row",
|
|
3941
|
+
field.kind === "textarea" ? "composer__row--grow" : ""
|
|
3942
|
+
].filter(Boolean).join(" "),
|
|
3943
|
+
children: [
|
|
3944
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "composer__label", htmlFor: fieldId, children: [
|
|
3945
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "composer__field-label", children: field.label }),
|
|
3946
|
+
field.kind === "textarea" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3947
|
+
"textarea",
|
|
3948
|
+
{
|
|
3949
|
+
...controlProps,
|
|
3950
|
+
ref: index === 0 ? (node) => {
|
|
3951
|
+
firstFieldRef.current = node;
|
|
3952
|
+
} : void 0,
|
|
3953
|
+
className: "composer__control composer__control--area",
|
|
3954
|
+
rows: 3
|
|
3955
|
+
}
|
|
3956
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3957
|
+
"input",
|
|
3958
|
+
{
|
|
3959
|
+
...controlProps,
|
|
3960
|
+
ref: index === 0 ? (node) => {
|
|
3961
|
+
firstFieldRef.current = node;
|
|
3962
|
+
} : void 0,
|
|
3963
|
+
className: "composer__control",
|
|
3964
|
+
type: field.kind,
|
|
3965
|
+
inputMode: field.kind === "tel" ? "tel" : void 0
|
|
3966
|
+
}
|
|
3967
|
+
)
|
|
3968
|
+
] }),
|
|
3969
|
+
invalid ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "composer__error", id: `${fieldId}-error`, children: field.kind === "email" ? "Enter a valid email to share your details." : `Add your ${field.label.toLowerCase()} to share your details.` }) : null
|
|
3970
|
+
]
|
|
3971
|
+
},
|
|
3972
|
+
field.id
|
|
3973
|
+
);
|
|
3974
|
+
}) }),
|
|
3975
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "composer__toolbar", children: [
|
|
3976
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3977
|
+
"button",
|
|
3978
|
+
{
|
|
3979
|
+
type: "submit",
|
|
3980
|
+
className: "composer__action composer__action--primary",
|
|
3981
|
+
disabled,
|
|
3982
|
+
children: "Share details"
|
|
3983
|
+
}
|
|
3984
|
+
),
|
|
3985
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3986
|
+
"button",
|
|
3987
|
+
{
|
|
3988
|
+
type: "button",
|
|
3989
|
+
className: "composer__action",
|
|
3990
|
+
disabled,
|
|
3991
|
+
onClick: () => setDraft((current) => ({ ...current, expanded: false })),
|
|
3992
|
+
children: "Keep chatting"
|
|
3993
|
+
}
|
|
3994
|
+
)
|
|
3995
|
+
] })
|
|
3996
|
+
]
|
|
3997
|
+
}
|
|
3998
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
3999
|
+
savedForm ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "composer__resume", children: [
|
|
4000
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: hasDraft ? "Your details aren\u2019t shared yet" : "Contact details are optional" }),
|
|
4001
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
4002
|
+
"button",
|
|
3896
4003
|
{
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
);
|
|
3932
|
-
}),
|
|
3933
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "composer__toolbar", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3934
|
-
"button",
|
|
3935
|
-
{
|
|
3936
|
-
type: "submit",
|
|
3937
|
-
className: "composer__send",
|
|
3938
|
-
disabled: disabled || !canSendForm,
|
|
3939
|
-
"aria-label": "Send details",
|
|
3940
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SendIcon, {})
|
|
3941
|
-
}
|
|
3942
|
-
) })
|
|
3943
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "composer__field", children: [
|
|
3944
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3945
|
-
"textarea",
|
|
3946
|
-
{
|
|
3947
|
-
ref: inputRef,
|
|
3948
|
-
className: "composer__input",
|
|
3949
|
-
rows: 1,
|
|
3950
|
-
value,
|
|
3951
|
-
placeholder,
|
|
3952
|
-
disabled,
|
|
3953
|
-
spellCheck: false,
|
|
3954
|
-
"aria-label": "Message",
|
|
3955
|
-
onChange: (event) => setValue(event.target.value),
|
|
3956
|
-
onKeyDown: handleChatKeyDown
|
|
3957
|
-
}
|
|
3958
|
-
),
|
|
3959
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3960
|
-
"button",
|
|
3961
|
-
{
|
|
3962
|
-
type: "submit",
|
|
3963
|
-
className: "composer__send",
|
|
3964
|
-
disabled: disabled || !value.trim(),
|
|
3965
|
-
"aria-label": "Send message",
|
|
3966
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SendIcon, {})
|
|
3967
|
-
}
|
|
3968
|
-
)
|
|
4004
|
+
type: "button",
|
|
4005
|
+
disabled,
|
|
4006
|
+
onClick: () => setDraft((current) => ({ ...current, expanded: true })),
|
|
4007
|
+
children: hasDraft ? "Resume details" : "Add details"
|
|
4008
|
+
}
|
|
4009
|
+
)
|
|
4010
|
+
] }) : null,
|
|
4011
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "composer__field", children: [
|
|
4012
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
4013
|
+
"textarea",
|
|
4014
|
+
{
|
|
4015
|
+
ref: inputRef,
|
|
4016
|
+
className: "composer__input",
|
|
4017
|
+
rows: 1,
|
|
4018
|
+
value,
|
|
4019
|
+
placeholder,
|
|
4020
|
+
disabled,
|
|
4021
|
+
spellCheck: false,
|
|
4022
|
+
"aria-label": "Message",
|
|
4023
|
+
onChange: (event) => setValue(event.target.value),
|
|
4024
|
+
onKeyDown: handleChatKeyDown
|
|
4025
|
+
}
|
|
4026
|
+
),
|
|
4027
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
4028
|
+
"button",
|
|
4029
|
+
{
|
|
4030
|
+
type: "submit",
|
|
4031
|
+
className: "composer__send",
|
|
4032
|
+
disabled: disabled || !value.trim(),
|
|
4033
|
+
"aria-label": "Send message",
|
|
4034
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SendIcon, {})
|
|
4035
|
+
}
|
|
4036
|
+
)
|
|
4037
|
+
] })
|
|
3969
4038
|
] })
|
|
3970
4039
|
}
|
|
3971
4040
|
);
|
|
@@ -4514,6 +4583,126 @@ function toSpeechText(text) {
|
|
|
4514
4583
|
function isSpeechSupported() {
|
|
4515
4584
|
return typeof window !== "undefined" && "speechSynthesis" in window && typeof window.SpeechSynthesisUtterance === "function";
|
|
4516
4585
|
}
|
|
4586
|
+
function stopSpeech() {
|
|
4587
|
+
if (!isSpeechSupported()) return;
|
|
4588
|
+
window.speechSynthesis.cancel();
|
|
4589
|
+
}
|
|
4590
|
+
var speechInterruptListeners = /* @__PURE__ */ new Set();
|
|
4591
|
+
var pageListenersAttached = false;
|
|
4592
|
+
var lastPathname = "";
|
|
4593
|
+
var originalPushState = null;
|
|
4594
|
+
var originalReplaceState = null;
|
|
4595
|
+
var patchedPushState = null;
|
|
4596
|
+
var patchedReplaceState = null;
|
|
4597
|
+
function currentPathname() {
|
|
4598
|
+
return window.location.pathname;
|
|
4599
|
+
}
|
|
4600
|
+
function notifySpeechInterrupts() {
|
|
4601
|
+
if (speechInterruptListeners.size === 0) return;
|
|
4602
|
+
stopSpeech();
|
|
4603
|
+
for (const listener of speechInterruptListeners) {
|
|
4604
|
+
listener();
|
|
4605
|
+
}
|
|
4606
|
+
}
|
|
4607
|
+
function interruptIfPathChanged(nextPath) {
|
|
4608
|
+
if (nextPath === lastPathname) return;
|
|
4609
|
+
lastPathname = nextPath;
|
|
4610
|
+
notifySpeechInterrupts();
|
|
4611
|
+
}
|
|
4612
|
+
function patchHistoryMethod(original) {
|
|
4613
|
+
return function patched(data, unused, url) {
|
|
4614
|
+
const result = original.call(this, data, unused, url);
|
|
4615
|
+
interruptIfPathChanged(currentPathname());
|
|
4616
|
+
return result;
|
|
4617
|
+
};
|
|
4618
|
+
}
|
|
4619
|
+
function historyMethodState(name) {
|
|
4620
|
+
return name === "pushState" ? {
|
|
4621
|
+
original: originalPushState,
|
|
4622
|
+
patched: patchedPushState,
|
|
4623
|
+
set(original, patched) {
|
|
4624
|
+
originalPushState = original;
|
|
4625
|
+
patchedPushState = patched;
|
|
4626
|
+
}
|
|
4627
|
+
} : {
|
|
4628
|
+
original: originalReplaceState,
|
|
4629
|
+
patched: patchedReplaceState,
|
|
4630
|
+
set(original, patched) {
|
|
4631
|
+
originalReplaceState = original;
|
|
4632
|
+
patchedReplaceState = patched;
|
|
4633
|
+
}
|
|
4634
|
+
};
|
|
4635
|
+
}
|
|
4636
|
+
function patchHistoryNamed(name) {
|
|
4637
|
+
const state = historyMethodState(name);
|
|
4638
|
+
const current = history[name];
|
|
4639
|
+
if (state.patched && current === state.patched) return;
|
|
4640
|
+
const patched = patchHistoryMethod(current);
|
|
4641
|
+
state.set(current, patched);
|
|
4642
|
+
history[name] = patched;
|
|
4643
|
+
}
|
|
4644
|
+
function restoreHistoryMethod(name) {
|
|
4645
|
+
const state = historyMethodState(name);
|
|
4646
|
+
if (!state.patched || !state.original || typeof history === "undefined") {
|
|
4647
|
+
return false;
|
|
4648
|
+
}
|
|
4649
|
+
if (history[name] !== state.patched) return false;
|
|
4650
|
+
history[name] = state.original;
|
|
4651
|
+
state.set(null, null);
|
|
4652
|
+
return true;
|
|
4653
|
+
}
|
|
4654
|
+
function handlePageHide() {
|
|
4655
|
+
notifySpeechInterrupts();
|
|
4656
|
+
}
|
|
4657
|
+
function handlePopState() {
|
|
4658
|
+
interruptIfPathChanged(currentPathname());
|
|
4659
|
+
}
|
|
4660
|
+
function handleVisibilityChange() {
|
|
4661
|
+
if (document.visibilityState === "hidden") {
|
|
4662
|
+
notifySpeechInterrupts();
|
|
4663
|
+
}
|
|
4664
|
+
}
|
|
4665
|
+
function attachPageListeners() {
|
|
4666
|
+
if (pageListenersAttached || typeof window === "undefined") return;
|
|
4667
|
+
pageListenersAttached = true;
|
|
4668
|
+
window.addEventListener("pagehide", handlePageHide);
|
|
4669
|
+
window.addEventListener("popstate", handlePopState);
|
|
4670
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
4671
|
+
}
|
|
4672
|
+
function detachPageListeners() {
|
|
4673
|
+
if (!pageListenersAttached || typeof window === "undefined") return;
|
|
4674
|
+
window.removeEventListener("pagehide", handlePageHide);
|
|
4675
|
+
window.removeEventListener("popstate", handlePopState);
|
|
4676
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
4677
|
+
pageListenersAttached = false;
|
|
4678
|
+
}
|
|
4679
|
+
function ensureSpeechInterruptsPatched() {
|
|
4680
|
+
if (typeof window === "undefined") return;
|
|
4681
|
+
lastPathname = currentPathname();
|
|
4682
|
+
patchHistoryNamed("pushState");
|
|
4683
|
+
patchHistoryNamed("replaceState");
|
|
4684
|
+
attachPageListeners();
|
|
4685
|
+
}
|
|
4686
|
+
function teardownSpeechInterrupts() {
|
|
4687
|
+
detachPageListeners();
|
|
4688
|
+
restoreHistoryMethod("pushState");
|
|
4689
|
+
restoreHistoryMethod("replaceState");
|
|
4690
|
+
if (!patchedPushState && !patchedReplaceState) {
|
|
4691
|
+
lastPathname = "";
|
|
4692
|
+
}
|
|
4693
|
+
}
|
|
4694
|
+
function subscribeSpeechInterrupts(onInterrupt) {
|
|
4695
|
+
if (typeof window === "undefined") return () => {
|
|
4696
|
+
};
|
|
4697
|
+
ensureSpeechInterruptsPatched();
|
|
4698
|
+
speechInterruptListeners.add(onInterrupt);
|
|
4699
|
+
return () => {
|
|
4700
|
+
speechInterruptListeners.delete(onInterrupt);
|
|
4701
|
+
if (speechInterruptListeners.size === 0) {
|
|
4702
|
+
teardownSpeechInterrupts();
|
|
4703
|
+
}
|
|
4704
|
+
};
|
|
4705
|
+
}
|
|
4517
4706
|
|
|
4518
4707
|
// src/react/components/MessageActions/MessageActions.tsx
|
|
4519
4708
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
@@ -4769,15 +4958,20 @@ function MessageActions({
|
|
|
4769
4958
|
const [menuOpen, setMenuOpen] = (0, import_react11.useState)(false);
|
|
4770
4959
|
const [menuPosition, setMenuPosition] = (0, import_react11.useState)(null);
|
|
4771
4960
|
const [speaking, setSpeaking] = (0, import_react11.useState)(false);
|
|
4961
|
+
const [speechSupported, setSpeechSupported] = (0, import_react11.useState)(null);
|
|
4772
4962
|
const copyTimerRef = (0, import_react11.useRef)(null);
|
|
4773
4963
|
const menuRef = (0, import_react11.useRef)(null);
|
|
4774
4964
|
const portaledMenuRef = (0, import_react11.useRef)(null);
|
|
4775
4965
|
const moreButtonRef = (0, import_react11.useRef)(null);
|
|
4776
4966
|
const answeredLabel = formatAnsweredAt(answeredAt ?? 0);
|
|
4777
4967
|
const resolvedSpeechText = speechText ?? toSpeechText(copyText);
|
|
4778
|
-
const canReadAloud = readAloud && isSpeechSupported() && resolvedSpeechText;
|
|
4779
|
-
const showMenu = Boolean(answeredLabel) || Boolean(receiptSteps) || canReadAloud;
|
|
4780
4968
|
const canOpenReceipt = Boolean(receiptSteps) && Boolean(onOpenReceipt);
|
|
4969
|
+
const readAloudEligible = Boolean(readAloud && resolvedSpeechText);
|
|
4970
|
+
const canReadAloud = readAloudEligible && speechSupported === true;
|
|
4971
|
+
const showMenu = canOpenReceipt || (speechSupported === null ? readAloudEligible : canReadAloud);
|
|
4972
|
+
(0, import_react11.useLayoutEffect)(() => {
|
|
4973
|
+
setSpeechSupported(isSpeechSupported());
|
|
4974
|
+
}, []);
|
|
4781
4975
|
(0, import_react11.useLayoutEffect)(() => {
|
|
4782
4976
|
if (!menuOpen || !moreButtonRef.current || !portaledMenuRef.current) {
|
|
4783
4977
|
setMenuPosition(null);
|
|
@@ -4795,17 +4989,16 @@ function MessageActions({
|
|
|
4795
4989
|
})
|
|
4796
4990
|
);
|
|
4797
4991
|
}, [menuOpen, menuPortalRoot]);
|
|
4798
|
-
(0, import_react11.useEffect)(
|
|
4799
|
-
() => ()
|
|
4992
|
+
(0, import_react11.useEffect)(() => {
|
|
4993
|
+
const unsubscribe = subscribeSpeechInterrupts(() => setSpeaking(false));
|
|
4994
|
+
return () => {
|
|
4995
|
+
unsubscribe();
|
|
4800
4996
|
if (copyTimerRef.current !== null) {
|
|
4801
4997
|
window.clearTimeout(copyTimerRef.current);
|
|
4802
4998
|
}
|
|
4803
|
-
|
|
4804
|
-
}
|
|
4805
|
-
|
|
4806
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
4807
|
-
[]
|
|
4808
|
-
);
|
|
4999
|
+
stopSpeech();
|
|
5000
|
+
};
|
|
5001
|
+
}, []);
|
|
4809
5002
|
(0, import_react11.useEffect)(() => {
|
|
4810
5003
|
if (!menuOpen) return;
|
|
4811
5004
|
const handlePointerDown = (event) => {
|
|
@@ -4844,14 +5037,14 @@ function MessageActions({
|
|
|
4844
5037
|
if (!canReadAloud) return;
|
|
4845
5038
|
setMenuOpen(false);
|
|
4846
5039
|
if (speaking) {
|
|
4847
|
-
|
|
5040
|
+
stopSpeech();
|
|
4848
5041
|
setSpeaking(false);
|
|
4849
5042
|
return;
|
|
4850
5043
|
}
|
|
4851
5044
|
const utterance = new SpeechSynthesisUtterance(resolvedSpeechText);
|
|
4852
5045
|
utterance.onend = () => setSpeaking(false);
|
|
4853
5046
|
utterance.onerror = () => setSpeaking(false);
|
|
4854
|
-
|
|
5047
|
+
stopSpeech();
|
|
4855
5048
|
window.speechSynthesis.speak(utterance);
|
|
4856
5049
|
setSpeaking(true);
|
|
4857
5050
|
}
|
|
@@ -5749,8 +5942,9 @@ function AgentRail({
|
|
|
5749
5942
|
colorScheme = "auto",
|
|
5750
5943
|
brandLabel = "",
|
|
5751
5944
|
brandLogoUrl,
|
|
5752
|
-
poweredByLabel
|
|
5945
|
+
poweredByLabel,
|
|
5753
5946
|
disclaimerLabel,
|
|
5947
|
+
disclaimerLink,
|
|
5754
5948
|
answerReceipt = false,
|
|
5755
5949
|
readAloud = false,
|
|
5756
5950
|
composerPlaceholder = "Ask anything\u2026",
|
|
@@ -6185,7 +6379,15 @@ function AgentRail({
|
|
|
6185
6379
|
onRetry ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
|
|
6186
6380
|
] }) : null
|
|
6187
6381
|
] }) }),
|
|
6188
|
-
showDisclaimerLabel ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "agent-rail__disclaimer", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { children:
|
|
6382
|
+
showDisclaimerLabel ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "agent-rail__disclaimer", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { children: disclaimerLink ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
6383
|
+
"a",
|
|
6384
|
+
{
|
|
6385
|
+
href: disclaimerLink,
|
|
6386
|
+
rel: "noopener noreferrer",
|
|
6387
|
+
target: "_blank",
|
|
6388
|
+
children: resolvedDisclaimerLabel
|
|
6389
|
+
}
|
|
6390
|
+
) : resolvedDisclaimerLabel }) }) : null,
|
|
6189
6391
|
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
|
|
6190
6392
|
showJumpToLatest ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
6191
6393
|
"button",
|
|
@@ -6203,10 +6405,12 @@ function AgentRail({
|
|
|
6203
6405
|
{
|
|
6204
6406
|
variant: expanded || mobileFullscreen ? "dock" : "default",
|
|
6205
6407
|
disabled: isBusy,
|
|
6206
|
-
form: composerForm,
|
|
6408
|
+
form: composerForm && lastMessage ? { ...composerForm, id: `${lastMessage.id}:${composerForm.id}` } : null,
|
|
6409
|
+
allowFormResume: !state.pendingOffer && !waitingForBooking && !hasPendingConfirmation,
|
|
6207
6410
|
placeholder: composerPlaceholder,
|
|
6208
6411
|
onSubmit: handleSubmit
|
|
6209
|
-
}
|
|
6412
|
+
},
|
|
6413
|
+
state.messages.find((message) => message.role === "visitor")?.id ?? "empty"
|
|
6210
6414
|
),
|
|
6211
6415
|
poweredByLabel ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "agent-rail__footer", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { children: poweredByLabel }) }) }) : null
|
|
6212
6416
|
] })
|
|
@@ -6650,8 +6854,9 @@ function AgentWidget({
|
|
|
6650
6854
|
brandLabel: agentName,
|
|
6651
6855
|
brandLogoUrl: branding?.logoUrl,
|
|
6652
6856
|
composerPlaceholder: branding?.composerPlaceholder ?? "Ask a question\u2026",
|
|
6653
|
-
poweredByLabel: branding?.poweredByLabel
|
|
6857
|
+
poweredByLabel: branding?.poweredByLabel,
|
|
6654
6858
|
disclaimerLabel: branding?.disclaimer,
|
|
6859
|
+
disclaimerLink: branding?.disclaimerLink,
|
|
6655
6860
|
answerReceipt: branding?.answerReceipt,
|
|
6656
6861
|
readAloud: branding?.readAloud,
|
|
6657
6862
|
state,
|