@webless/agent 0.7.2 → 0.7.4

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.
@@ -4273,7 +4273,7 @@ import {
4273
4273
  useRef as useRef4,
4274
4274
  useState as useState4
4275
4275
  } from "react";
4276
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
4276
+ import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
4277
4277
  function SendIcon() {
4278
4278
  return /* @__PURE__ */ jsx4("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx4(
4279
4279
  "path",
@@ -4291,42 +4291,72 @@ function emptyValues(form) {
4291
4291
  for (const field of form?.fields ?? []) values[field.id] = "";
4292
4292
  return values;
4293
4293
  }
4294
+ function createDraft(form) {
4295
+ return {
4296
+ form,
4297
+ values: emptyValues(form),
4298
+ expanded: Boolean(form),
4299
+ submitted: false,
4300
+ dismissalSent: false,
4301
+ attempted: false
4302
+ };
4303
+ }
4294
4304
  function Composer({
4295
4305
  disabled = false,
4296
4306
  placeholder = "Ask anything\u2026",
4297
4307
  variant = "default",
4298
4308
  form = null,
4309
+ allowFormResume = true,
4299
4310
  onSubmit
4300
4311
  }) {
4301
4312
  const [value, setValue] = useState4("");
4302
- const [values, setValues] = useState4(
4303
- () => emptyValues(form)
4304
- );
4305
- const [blurred, setBlurred] = useState4({});
4313
+ const [draft, setDraft] = useState4(() => createDraft(form));
4306
4314
  const inputRef = useRef4(null);
4307
4315
  const firstFieldRef = useRef4(null);
4316
+ const formRef = useRef4(null);
4308
4317
  const formId = useId();
4309
- const activeForm = form;
4310
- const canSendForm = activeForm ? isComposerFormComplete(activeForm, values) : Boolean(value.trim());
4311
- useEffect4(() => {
4312
- setValues(emptyValues(form));
4313
- setBlurred({});
4314
- }, [form?.id]);
4318
+ if (form && form.id !== draft.form?.id) setDraft(createDraft(form));
4319
+ const savedForm = allowFormResume && !draft.submitted ? draft.form : null;
4320
+ const activeForm = !disabled && draft.expanded ? savedForm : null;
4321
+ const hasDraft = Object.values(draft.values).some((value2) => value2.trim());
4315
4322
  useEffect4(() => {
4316
4323
  if (activeForm) firstFieldRef.current?.focus();
4317
- }, [activeForm?.id]);
4324
+ else if ((savedForm || draft.submitted) && !disabled)
4325
+ inputRef.current?.focus();
4326
+ }, [activeForm?.id, disabled, savedForm?.id, draft.submitted]);
4318
4327
  function submitChat() {
4319
4328
  const trimmed = value.trim();
4320
4329
  if (!trimmed || disabled) return;
4321
- onSubmit?.(trimmed);
4330
+ const shareDismissal = savedForm && !draft.dismissalSent;
4331
+ onSubmit?.(
4332
+ 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.
4333
+
4334
+ ${trimmed}` : trimmed
4335
+ );
4336
+ if (shareDismissal)
4337
+ setDraft((current) => ({ ...current, dismissalSent: true }));
4322
4338
  setValue("");
4323
4339
  inputRef.current?.focus();
4324
4340
  }
4325
4341
  function submitForm() {
4326
- if (!activeForm || disabled || !canSendForm) return;
4327
- onSubmit?.(formatComposerFormMessage(activeForm, values));
4328
- setValues(emptyValues(activeForm));
4329
- setBlurred({});
4342
+ if (!activeForm || disabled) return;
4343
+ if (!isComposerFormComplete(activeForm, draft.values)) {
4344
+ setDraft((current) => ({ ...current, attempted: true }));
4345
+ const invalidField = activeForm.fields.find(
4346
+ (field) => !isValidComposerFieldValue(field, draft.values[field.id] ?? "")
4347
+ );
4348
+ const control = invalidField && formRef.current?.elements.namedItem(invalidField.id);
4349
+ if (control instanceof HTMLElement) control.focus();
4350
+ return;
4351
+ }
4352
+ onSubmit?.(formatComposerFormMessage(activeForm, draft.values));
4353
+ setDraft((current) => ({
4354
+ ...current,
4355
+ values: emptyValues(activeForm),
4356
+ expanded: false,
4357
+ submitted: true,
4358
+ attempted: false
4359
+ }));
4330
4360
  }
4331
4361
  function handleSubmit(event) {
4332
4362
  event.preventDefault();
@@ -4350,112 +4380,151 @@ function Composer({
4350
4380
  return /* @__PURE__ */ jsx4(
4351
4381
  "form",
4352
4382
  {
4383
+ ref: formRef,
4384
+ noValidate: true,
4353
4385
  className: [
4354
4386
  "composer",
4355
4387
  variant === "dock" ? "composer--dock" : "",
4356
4388
  activeForm ? "composer--form" : ""
4357
4389
  ].filter(Boolean).join(" "),
4358
4390
  onSubmit: handleSubmit,
4359
- children: activeForm ? /* @__PURE__ */ jsxs4("div", { className: "composer__sheet", role: "group", "aria-label": "Required details", children: [
4360
- activeForm.fields.map((field, index) => {
4361
- const fieldId = `${formId}-${field.id}`;
4362
- const invalid = Boolean(blurred[field.id]) && !isValidComposerFieldValue(field, values[field.id] ?? "");
4363
- const controlProps = {
4364
- id: fieldId,
4365
- name: field.id,
4366
- disabled,
4367
- required: field.required,
4368
- autoComplete: field.autocomplete,
4369
- placeholder: field.placeholder,
4370
- spellCheck: false,
4371
- value: values[field.id] ?? "",
4372
- "aria-invalid": invalid || void 0,
4373
- "aria-describedby": invalid ? `${fieldId}-error` : void 0,
4374
- onBlur: () => setBlurred((current) => ({ ...current, [field.id]: true })),
4375
- onChange: (event) => {
4376
- const next = readComposerControlValue(event);
4377
- setValues((current) => ({
4378
- ...current,
4379
- [field.id]: next
4380
- }));
4381
- },
4382
- onKeyDown: handleFormKeyDown
4383
- };
4384
- return /* @__PURE__ */ jsxs4(
4385
- "div",
4391
+ children: activeForm ? /* @__PURE__ */ jsxs4(
4392
+ "div",
4393
+ {
4394
+ className: "composer__sheet",
4395
+ role: "group",
4396
+ "aria-label": "Contact details",
4397
+ children: [
4398
+ /* @__PURE__ */ jsxs4("div", { className: "composer__form-heading", children: [
4399
+ /* @__PURE__ */ jsx4("strong", { children: "Share your details" }),
4400
+ /* @__PURE__ */ jsx4("span", { children: "Optional" }),
4401
+ /* @__PURE__ */ jsx4("p", { children: "You can also keep chatting." })
4402
+ ] }),
4403
+ /* @__PURE__ */ jsx4("div", { className: "composer__fields", children: activeForm.fields.map((field, index) => {
4404
+ const fieldId = `${formId}-${field.id}`;
4405
+ const invalid = draft.attempted && !isValidComposerFieldValue(field, draft.values[field.id] ?? "");
4406
+ const controlProps = {
4407
+ id: fieldId,
4408
+ name: field.id,
4409
+ disabled,
4410
+ required: field.required,
4411
+ autoComplete: field.autocomplete,
4412
+ placeholder: field.placeholder,
4413
+ spellCheck: false,
4414
+ value: draft.values[field.id] ?? "",
4415
+ "aria-invalid": invalid || void 0,
4416
+ "aria-describedby": invalid ? `${fieldId}-error` : void 0,
4417
+ onChange: (event) => {
4418
+ const next = readComposerControlValue(event);
4419
+ setDraft((current) => ({
4420
+ ...current,
4421
+ values: { ...current.values, [field.id]: next }
4422
+ }));
4423
+ },
4424
+ onKeyDown: handleFormKeyDown
4425
+ };
4426
+ return /* @__PURE__ */ jsxs4(
4427
+ "div",
4428
+ {
4429
+ className: [
4430
+ "composer__row",
4431
+ field.kind === "textarea" ? "composer__row--grow" : ""
4432
+ ].filter(Boolean).join(" "),
4433
+ children: [
4434
+ /* @__PURE__ */ jsxs4("label", { className: "composer__label", htmlFor: fieldId, children: [
4435
+ /* @__PURE__ */ jsx4("span", { className: "composer__field-label", children: field.label }),
4436
+ field.kind === "textarea" ? /* @__PURE__ */ jsx4(
4437
+ "textarea",
4438
+ {
4439
+ ...controlProps,
4440
+ ref: index === 0 ? (node) => {
4441
+ firstFieldRef.current = node;
4442
+ } : void 0,
4443
+ className: "composer__control composer__control--area",
4444
+ rows: 3
4445
+ }
4446
+ ) : /* @__PURE__ */ jsx4(
4447
+ "input",
4448
+ {
4449
+ ...controlProps,
4450
+ ref: index === 0 ? (node) => {
4451
+ firstFieldRef.current = node;
4452
+ } : void 0,
4453
+ className: "composer__control",
4454
+ type: field.kind,
4455
+ inputMode: field.kind === "tel" ? "tel" : void 0
4456
+ }
4457
+ )
4458
+ ] }),
4459
+ invalid ? /* @__PURE__ */ jsx4("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
4460
+ ]
4461
+ },
4462
+ field.id
4463
+ );
4464
+ }) }),
4465
+ /* @__PURE__ */ jsxs4("div", { className: "composer__toolbar", children: [
4466
+ /* @__PURE__ */ jsx4(
4467
+ "button",
4468
+ {
4469
+ type: "submit",
4470
+ className: "composer__action composer__action--primary",
4471
+ disabled,
4472
+ children: "Share details"
4473
+ }
4474
+ ),
4475
+ /* @__PURE__ */ jsx4(
4476
+ "button",
4477
+ {
4478
+ type: "button",
4479
+ className: "composer__action",
4480
+ disabled,
4481
+ onClick: () => setDraft((current) => ({ ...current, expanded: false })),
4482
+ children: "Keep chatting"
4483
+ }
4484
+ )
4485
+ ] })
4486
+ ]
4487
+ }
4488
+ ) : /* @__PURE__ */ jsxs4(Fragment, { children: [
4489
+ savedForm ? /* @__PURE__ */ jsxs4("div", { className: "composer__resume", children: [
4490
+ /* @__PURE__ */ jsx4("span", { children: hasDraft ? "Your details aren\u2019t shared yet" : "Contact details are optional" }),
4491
+ /* @__PURE__ */ jsx4(
4492
+ "button",
4386
4493
  {
4387
- className: [
4388
- "composer__row",
4389
- field.kind === "textarea" ? "composer__row--grow" : ""
4390
- ].filter(Boolean).join(" "),
4391
- children: [
4392
- /* @__PURE__ */ jsxs4("label", { className: "composer__label", htmlFor: fieldId, children: [
4393
- /* @__PURE__ */ jsx4("span", { className: "composer__sr-only", children: field.label }),
4394
- field.kind === "textarea" ? /* @__PURE__ */ jsx4(
4395
- "textarea",
4396
- {
4397
- ...controlProps,
4398
- ref: index === 0 ? (node) => {
4399
- firstFieldRef.current = node;
4400
- } : void 0,
4401
- className: "composer__control composer__control--area",
4402
- rows: 3
4403
- }
4404
- ) : /* @__PURE__ */ jsx4(
4405
- "input",
4406
- {
4407
- ...controlProps,
4408
- ref: index === 0 ? (node) => {
4409
- firstFieldRef.current = node;
4410
- } : void 0,
4411
- className: "composer__control",
4412
- type: field.kind,
4413
- inputMode: field.kind === "tel" ? "tel" : void 0
4414
- }
4415
- )
4416
- ] }),
4417
- invalid ? /* @__PURE__ */ jsx4("p", { className: "composer__error", id: `${fieldId}-error`, children: field.kind === "email" ? "Enter a valid email to continue." : `Add your ${field.label.toLowerCase()} to continue.` }) : null
4418
- ]
4419
- },
4420
- field.id
4421
- );
4422
- }),
4423
- /* @__PURE__ */ jsx4("div", { className: "composer__toolbar", children: /* @__PURE__ */ jsx4(
4424
- "button",
4425
- {
4426
- type: "submit",
4427
- className: "composer__send",
4428
- disabled: disabled || !canSendForm,
4429
- "aria-label": "Send details",
4430
- children: /* @__PURE__ */ jsx4(SendIcon, {})
4431
- }
4432
- ) })
4433
- ] }) : /* @__PURE__ */ jsxs4("div", { className: "composer__field", children: [
4434
- /* @__PURE__ */ jsx4(
4435
- "textarea",
4436
- {
4437
- ref: inputRef,
4438
- className: "composer__input",
4439
- rows: 1,
4440
- value,
4441
- placeholder,
4442
- disabled,
4443
- spellCheck: false,
4444
- "aria-label": "Message",
4445
- onChange: (event) => setValue(event.target.value),
4446
- onKeyDown: handleChatKeyDown
4447
- }
4448
- ),
4449
- /* @__PURE__ */ jsx4(
4450
- "button",
4451
- {
4452
- type: "submit",
4453
- className: "composer__send",
4454
- disabled: disabled || !value.trim(),
4455
- "aria-label": "Send message",
4456
- children: /* @__PURE__ */ jsx4(SendIcon, {})
4457
- }
4458
- )
4494
+ type: "button",
4495
+ disabled,
4496
+ onClick: () => setDraft((current) => ({ ...current, expanded: true })),
4497
+ children: hasDraft ? "Resume details" : "Add details"
4498
+ }
4499
+ )
4500
+ ] }) : null,
4501
+ /* @__PURE__ */ jsxs4("div", { className: "composer__field", children: [
4502
+ /* @__PURE__ */ jsx4(
4503
+ "textarea",
4504
+ {
4505
+ ref: inputRef,
4506
+ className: "composer__input",
4507
+ rows: 1,
4508
+ value,
4509
+ placeholder,
4510
+ disabled,
4511
+ spellCheck: false,
4512
+ "aria-label": "Message",
4513
+ onChange: (event) => setValue(event.target.value),
4514
+ onKeyDown: handleChatKeyDown
4515
+ }
4516
+ ),
4517
+ /* @__PURE__ */ jsx4(
4518
+ "button",
4519
+ {
4520
+ type: "submit",
4521
+ className: "composer__send",
4522
+ disabled: disabled || !value.trim(),
4523
+ "aria-label": "Send message",
4524
+ children: /* @__PURE__ */ jsx4(SendIcon, {})
4525
+ }
4526
+ )
4527
+ ] })
4459
4528
  ] })
4460
4529
  }
4461
4530
  );
@@ -5680,7 +5749,7 @@ function isRenderableVisitorToolResult(result) {
5680
5749
  }
5681
5750
 
5682
5751
  // src/react/components/AgentRail/AgentRail.tsx
5683
- import { Fragment, jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
5752
+ import { Fragment as Fragment2, jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
5684
5753
  function MinimizeIcon() {
5685
5754
  return /* @__PURE__ */ jsx16("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx16(
5686
5755
  "path",
@@ -6147,7 +6216,7 @@ function AgentRail({
6147
6216
  onFeedback: onFeedback ? (rating) => onFeedback(rating, message) : void 0
6148
6217
  }
6149
6218
  ) : null,
6150
- index === lastVisitorIndex ? /* @__PURE__ */ jsxs15(Fragment, { children: [
6219
+ index === lastVisitorIndex ? /* @__PURE__ */ jsxs15(Fragment2, { children: [
6151
6220
  showActivity ? /* @__PURE__ */ jsx16(
6152
6221
  AgentActivityBubble,
6153
6222
  {
@@ -6221,10 +6290,12 @@ function AgentRail({
6221
6290
  {
6222
6291
  variant: expanded || mobileFullscreen ? "dock" : "default",
6223
6292
  disabled: isBusy,
6224
- form: composerForm,
6293
+ form: composerForm && lastMessage ? { ...composerForm, id: `${lastMessage.id}:${composerForm.id}` } : null,
6294
+ allowFormResume: !state.pendingOffer && !waitingForBooking && !hasPendingConfirmation,
6225
6295
  placeholder: composerPlaceholder,
6226
6296
  onSubmit: handleSubmit
6227
- }
6297
+ },
6298
+ state.messages.find((message) => message.role === "visitor")?.id ?? "empty"
6228
6299
  ),
6229
6300
  poweredByLabel ? /* @__PURE__ */ jsx16("div", { className: "agent-rail__footer", children: /* @__PURE__ */ jsx16("p", { children: /* @__PURE__ */ jsx16("span", { children: poweredByLabel }) }) }) : null
6230
6301
  ] })
@@ -6246,7 +6317,7 @@ function AgentRail({
6246
6317
  }
6247
6318
 
6248
6319
  // src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
6249
- import { Fragment as Fragment2, jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
6320
+ import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
6250
6321
  function SparklesIcon() {
6251
6322
  return /* @__PURE__ */ jsxs16(
6252
6323
  "svg",
@@ -6379,7 +6450,7 @@ function AssistEdgeTab({
6379
6450
  tabIndex: visible ? 0 : -1,
6380
6451
  onClick: onOpen,
6381
6452
  children: [
6382
- mobile ? /* @__PURE__ */ jsxs16(Fragment2, { children: [
6453
+ mobile ? /* @__PURE__ */ jsxs16(Fragment3, { children: [
6383
6454
  /* @__PURE__ */ jsxs16(
6384
6455
  "span",
6385
6456
  {
@@ -6402,7 +6473,7 @@ function AssistEdgeTab({
6402
6473
  }
6403
6474
  ),
6404
6475
  /* @__PURE__ */ jsx17("span", { className: "assist-edge-tab__label", children: visibleLabel })
6405
- ] }) : variant === "outline" ? /* @__PURE__ */ jsxs16(Fragment2, { children: [
6476
+ ] }) : variant === "outline" ? /* @__PURE__ */ jsxs16(Fragment3, { children: [
6406
6477
  /* @__PURE__ */ jsxs16("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
6407
6478
  /* @__PURE__ */ jsx17(TabMarkIcon, { customIconUrl }),
6408
6479
  showLogo ? /* @__PURE__ */ jsx17(
@@ -6420,12 +6491,12 @@ function AssistEdgeTab({
6420
6491
  /* @__PURE__ */ jsx17("span", { className: "assist-edge-tab__label", children: visibleLabel }),
6421
6492
  /* @__PURE__ */ jsx17(ChevronDownIcon2, {})
6422
6493
  ] }) : null,
6423
- variant === "ask" ? /* @__PURE__ */ jsxs16(Fragment2, { children: [
6494
+ variant === "ask" ? /* @__PURE__ */ jsxs16(Fragment3, { children: [
6424
6495
  /* @__PURE__ */ jsx17(ChevronLeftIcon, {}),
6425
6496
  /* @__PURE__ */ jsx17("span", { className: "assist-edge-tab__label", children: visibleLabel }),
6426
6497
  /* @__PURE__ */ jsx17(DragDots, {})
6427
6498
  ] }) : null,
6428
- variant === "fill" ? /* @__PURE__ */ jsxs16(Fragment2, { children: [
6499
+ variant === "fill" ? /* @__PURE__ */ jsxs16(Fragment3, { children: [
6429
6500
  /* @__PURE__ */ jsxs16("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
6430
6501
  /* @__PURE__ */ jsx17(TabMarkIcon, { customIconUrl }),
6431
6502
  showLogo ? /* @__PURE__ */ jsx17(
@@ -6850,4 +6921,4 @@ export {
6850
6921
  sendAgentAnswerFeedback,
6851
6922
  AgentWidget
6852
6923
  };
6853
- //# sourceMappingURL=chunk-HZUPH5Z7.js.map
6924
+ //# sourceMappingURL=chunk-GS65OJFL.js.map