@clivly/chat-widget 0.10.9 → 0.11.0

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/browser.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createRoot } from "react-dom/client";
2
- import { useEffect, useMemo, useRef, useState } from "react";
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
3
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
4
  //#region src/theme.ts
5
5
  const DARK_MEDIA_QUERY = "(prefers-color-scheme: dark)";
@@ -255,10 +255,42 @@ const WIDGET_STYLES = `
255
255
  padding: 0.5rem 1rem;
256
256
  text-align: center;
257
257
  }
258
+ /* Only the name carries the gradient. "Powered by" is ordinary running text
259
+ and inherits the muted colour of the line, so the mark reads as a signature
260
+ rather than the whole sentence being branded. */
258
261
  .clivly-chat-widget__powered a {
259
262
  color: inherit;
260
263
  text-decoration: none;
261
264
  }
265
+ /* The attribution is Clivly's mark, not the host's, so it uses the stops from
266
+ the logo itself rather than --clivly-chat-accent: the accent is driven by
267
+ each customer's own brandColor, and a signature that changed colour per
268
+ customer would stop reading as a signature.
269
+
270
+ The color declaration is the fallback and must stay a real colour:
271
+ background-clip is painted over it, so anything that drops the background —
272
+ forced-colors, an older engine, a copied-out snippet — degrades to solid
273
+ coral rather than to invisible text. */
274
+ .clivly-chat-widget__powered-mark {
275
+ background-image: linear-gradient(90deg, #ff5a4a 0%, #c0231a 100%);
276
+ -webkit-background-clip: text;
277
+ background-clip: text;
278
+ color: #c0231a;
279
+ -webkit-text-fill-color: transparent;
280
+ }
281
+ /* Forced-colors mode strips background-image outright, which would leave the
282
+ text painted in a transparent fill. Hand the colour back to the UA so the
283
+ mark inherits the link colour the UA picked rather than re-pinning coral. */
284
+ @media (forced-colors: active) {
285
+ .clivly-chat-widget__powered a {
286
+ color: LinkText;
287
+ }
288
+ .clivly-chat-widget__powered-mark {
289
+ background-image: none;
290
+ color: currentColor;
291
+ -webkit-text-fill-color: currentColor;
292
+ }
293
+ }
262
294
  .clivly-chat-widget__powered a:hover,
263
295
  .clivly-chat-widget__powered a:focus-visible {
264
296
  text-decoration: underline;
@@ -482,7 +514,27 @@ function MessageList({ greeting, hasSession, messages }) {
482
514
  });
483
515
  return /* @__PURE__ */ jsx(Fragment, { children: messages.map((message) => /* @__PURE__ */ jsx(MessageBubble, { message }, message.id)) });
484
516
  }
485
- function ChatWidget({ className, createTransport, getSession, initiallyOpen = false, launcherLabel = "Chat with us", storage = DEFAULT_STORAGE, style, theme = "auto", widgetId }) {
517
+ /**
518
+ * Whether a seeded visitor is complete enough to skip the form.
519
+ *
520
+ * Both fields have to be present and non-blank: the server requires each, so
521
+ * auto-starting on a half-filled identity would trade a form the visitor can
522
+ * complete for an error they cannot.
523
+ */
524
+ function isCompleteVisitor(visitor) {
525
+ return Boolean(visitor?.name?.trim() && visitor?.email?.trim());
526
+ }
527
+ /**
528
+ * What the composer says about itself. "Connecting…" claims a socket is in
529
+ * flight, so it is as wrong as "Conversation ready" when no transport exists —
530
+ * the visitor is told to wait for something that will never arrive. The third
531
+ * string keeps the status honest in all three states.
532
+ */
533
+ function composerStatusText(hasTransport, isReady) {
534
+ if (!hasTransport) return "Chat is unavailable";
535
+ return isReady ? "Conversation ready" : "Connecting…";
536
+ }
537
+ function ChatWidget({ className, createTransport, defaultVisitor, getSession, initiallyOpen = false, launcherLabel = "Chat with us", storage = DEFAULT_STORAGE, style, theme = "auto", widgetId }) {
486
538
  const [isOpen, setIsOpen] = useState(initiallyOpen);
487
539
  const [error, setError] = useState(null);
488
540
  const [isConnecting, setIsConnecting] = useState(false);
@@ -491,11 +543,15 @@ function ChatWidget({ className, createTransport, getSession, initiallyOpen = fa
491
543
  const [messages, setMessages] = useState([]);
492
544
  const [session, setSession] = useState(null);
493
545
  const [visitor, setVisitor] = useState({
494
- email: "",
495
- name: ""
546
+ email: defaultVisitor?.email ?? "",
547
+ name: defaultVisitor?.name ?? ""
496
548
  });
497
549
  const transportRef = useRef(null);
498
550
  const listRef = useRef(null);
551
+ useEffect(() => {
552
+ if (createTransport) return;
553
+ console.warn("[clivly] <ChatWidget> was given no createTransport, so messages cannot be sent. Pass createTransport={createClivlyTransport()} — see https://www.npmjs.com/package/@clivly/chat-widget#five-minute-quick-start");
554
+ }, []);
499
555
  const themeStyle = useThemeStyle(session, style);
500
556
  const resolvedTheme = useResolvedTheme(theme);
501
557
  useEffect(() => () => {
@@ -538,15 +594,13 @@ function ChatWidget({ className, createTransport, getSession, initiallyOpen = fa
538
594
  if (transportRef.current === connection) transportRef.current = null;
539
595
  };
540
596
  }, [createTransport, session]);
541
- const submitVisitor = async (event) => {
542
- event.preventDefault();
597
+ const startSession = useCallback(async (identity) => {
543
598
  setError(null);
544
599
  setIsConnecting(true);
545
600
  try {
546
- const existingVisitorToken = storage?.getItem(createStorageKey(widgetId)) ?? null;
547
601
  const nextSession = await getSession({
548
- visitor,
549
- visitorToken: existingVisitorToken,
602
+ visitor: identity,
603
+ visitorToken: storage?.getItem(createStorageKey(widgetId)) ?? null,
550
604
  widgetId
551
605
  });
552
606
  storage?.setItem(createStorageKey(widgetId), nextSession.visitorToken);
@@ -558,7 +612,30 @@ function ChatWidget({ className, createTransport, getSession, initiallyOpen = fa
558
612
  } finally {
559
613
  setIsConnecting(false);
560
614
  }
615
+ }, [
616
+ getSession,
617
+ storage,
618
+ widgetId
619
+ ]);
620
+ const submitVisitor = async (event) => {
621
+ event.preventDefault();
622
+ await startSession(visitor);
561
623
  };
624
+ useEffect(() => {
625
+ if (!(isOpen && isCompleteVisitor(defaultVisitor))) return;
626
+ if (session || isConnecting || error) return;
627
+ startSession({
628
+ email: defaultVisitor.email.trim(),
629
+ name: defaultVisitor.name.trim()
630
+ }).catch(() => {});
631
+ }, [
632
+ defaultVisitor,
633
+ error,
634
+ isConnecting,
635
+ isOpen,
636
+ session,
637
+ startSession
638
+ ]);
562
639
  const submitMessage = (event) => {
563
640
  event?.preventDefault();
564
641
  if (!(session && transportRef.current)) {
@@ -590,6 +667,7 @@ function ChatWidget({ className, createTransport, getSession, initiallyOpen = fa
590
667
  setError(resolved.message);
591
668
  }
592
669
  };
670
+ const canSend = Boolean(createTransport) && isReady;
593
671
  const greeting = session?.widget.config.greeting ?? "Tell us a little about yourself and we’ll pick up the conversation from there.";
594
672
  const panelTitle = session?.widget.name ?? "Start a conversation";
595
673
  const sessionSummary = sessionSummaryText(session, greeting);
@@ -657,7 +735,7 @@ function ChatWidget({ className, createTransport, getSession, initiallyOpen = fa
657
735
  onKeyDown: (event) => {
658
736
  if (!isSendKey(event)) return;
659
737
  event.preventDefault();
660
- if (messageDraft.trim() && isReady) submitMessage();
738
+ if (messageDraft.trim() && canSend) submitMessage();
661
739
  },
662
740
  placeholder: "Write your message",
663
741
  value: messageDraft
@@ -666,10 +744,10 @@ function ChatWidget({ className, createTransport, getSession, initiallyOpen = fa
666
744
  className: "clivly-chat-widget__actions",
667
745
  children: [/* @__PURE__ */ jsx("span", {
668
746
  className: "clivly-chat-widget__subtitle",
669
- children: isReady || !createTransport ? "Conversation ready" : "Connecting…"
747
+ children: composerStatusText(Boolean(createTransport), isReady)
670
748
  }), /* @__PURE__ */ jsx("button", {
671
749
  className: "clivly-chat-widget__button",
672
- disabled: !(messageDraft.trim() && isReady),
750
+ disabled: !(messageDraft.trim() && canSend),
673
751
  type: "submit",
674
752
  children: "Send"
675
753
  })]
@@ -731,11 +809,18 @@ function ChatWidget({ className, createTransport, getSession, initiallyOpen = fa
731
809
  }),
732
810
  /* @__PURE__ */ jsx("p", {
733
811
  className: "clivly-chat-widget__powered",
734
- children: /* @__PURE__ */ jsx("a", {
812
+ children: /* @__PURE__ */ jsxs("a", {
735
813
  href: "https://clivly.com",
736
814
  rel: "noopener",
737
815
  target: "_blank",
738
- children: "Powered by Clivly"
816
+ children: [
817
+ "Powered by",
818
+ " ",
819
+ /* @__PURE__ */ jsx("span", {
820
+ className: "clivly-chat-widget__powered-mark",
821
+ children: "Clivly"
822
+ })
823
+ ]
739
824
  })
740
825
  })
741
826
  ]
@@ -826,6 +911,33 @@ function createSessionFetcher({ fetchImpl = fetch, sessionUrl }) {
826
911
  //#endregion
827
912
  //#region src/browser-mount.tsx
828
913
  const MOUNTED_DATA_ATTR = "clivlyMounted";
914
+ /**
915
+ * The identity a script-tag host already knows, read off the tag.
916
+ *
917
+ * The React component takes `defaultVisitor` as a prop; a script tag has only
918
+ * strings, so `data-visitor-name` / `data-visitor-email` are the same seam. A
919
+ * host that server-renders its page can template the signed-in user's details
920
+ * straight into the tag and the visitor never sees the pre-chat form.
921
+ *
922
+ * Returns `undefined` rather than an empty object when neither attribute is
923
+ * present, so the component's own "no identity" path stays untouched. Partial
924
+ * values are passed through deliberately: `ChatWidget` treats one field as a
925
+ * prefill and still renders the form, which is the right outcome for a host
926
+ * that knows an email but not a name.
927
+ *
928
+ * This is self-reported identity — anyone can edit the attribute in devtools.
929
+ * `resolveUser` on the host backend is what makes it trustworthy; this only
930
+ * removes the typing.
931
+ */
932
+ function readScriptVisitor(dataset) {
933
+ const email = dataset.visitorEmail?.trim();
934
+ const name = dataset.visitorName?.trim();
935
+ if (!(email || name)) return;
936
+ return {
937
+ ...email ? { email } : {},
938
+ ...name ? { name } : {}
939
+ };
940
+ }
829
941
  function resolveSocketUrl(socketUrlTemplate, conversationId, token) {
830
942
  return socketUrlTemplate.replaceAll("{conversationId}", encodeURIComponent(conversationId)).replaceAll("{token}", encodeURIComponent(token));
831
943
  }
@@ -870,6 +982,7 @@ function mountChatWidgetScript$1(script = document.currentScript) {
870
982
  shadowRoot.append(container);
871
983
  const { root, setTheme } = createWidgetMount({
872
984
  container,
985
+ defaultVisitor: readScriptVisitor(script.dataset),
873
986
  sessionUrl,
874
987
  socketUrlTemplate: script.dataset.socketUrlTemplate,
875
988
  theme: parseTheme(script.dataset.theme),
package/dist/index.cjs CHANGED
@@ -256,10 +256,42 @@ const WIDGET_STYLES = `
256
256
  padding: 0.5rem 1rem;
257
257
  text-align: center;
258
258
  }
259
+ /* Only the name carries the gradient. "Powered by" is ordinary running text
260
+ and inherits the muted colour of the line, so the mark reads as a signature
261
+ rather than the whole sentence being branded. */
259
262
  .clivly-chat-widget__powered a {
260
263
  color: inherit;
261
264
  text-decoration: none;
262
265
  }
266
+ /* The attribution is Clivly's mark, not the host's, so it uses the stops from
267
+ the logo itself rather than --clivly-chat-accent: the accent is driven by
268
+ each customer's own brandColor, and a signature that changed colour per
269
+ customer would stop reading as a signature.
270
+
271
+ The color declaration is the fallback and must stay a real colour:
272
+ background-clip is painted over it, so anything that drops the background —
273
+ forced-colors, an older engine, a copied-out snippet — degrades to solid
274
+ coral rather than to invisible text. */
275
+ .clivly-chat-widget__powered-mark {
276
+ background-image: linear-gradient(90deg, #ff5a4a 0%, #c0231a 100%);
277
+ -webkit-background-clip: text;
278
+ background-clip: text;
279
+ color: #c0231a;
280
+ -webkit-text-fill-color: transparent;
281
+ }
282
+ /* Forced-colors mode strips background-image outright, which would leave the
283
+ text painted in a transparent fill. Hand the colour back to the UA so the
284
+ mark inherits the link colour the UA picked rather than re-pinning coral. */
285
+ @media (forced-colors: active) {
286
+ .clivly-chat-widget__powered a {
287
+ color: LinkText;
288
+ }
289
+ .clivly-chat-widget__powered-mark {
290
+ background-image: none;
291
+ color: currentColor;
292
+ -webkit-text-fill-color: currentColor;
293
+ }
294
+ }
263
295
  .clivly-chat-widget__powered a:hover,
264
296
  .clivly-chat-widget__powered a:focus-visible {
265
297
  text-decoration: underline;
@@ -483,7 +515,27 @@ function MessageList({ greeting, hasSession, messages }) {
483
515
  });
484
516
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: messages.map((message) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageBubble, { message }, message.id)) });
485
517
  }
486
- function ChatWidget({ className, createTransport, getSession, initiallyOpen = false, launcherLabel = "Chat with us", storage = DEFAULT_STORAGE, style, theme = "auto", widgetId }) {
518
+ /**
519
+ * Whether a seeded visitor is complete enough to skip the form.
520
+ *
521
+ * Both fields have to be present and non-blank: the server requires each, so
522
+ * auto-starting on a half-filled identity would trade a form the visitor can
523
+ * complete for an error they cannot.
524
+ */
525
+ function isCompleteVisitor(visitor) {
526
+ return Boolean(visitor?.name?.trim() && visitor?.email?.trim());
527
+ }
528
+ /**
529
+ * What the composer says about itself. "Connecting…" claims a socket is in
530
+ * flight, so it is as wrong as "Conversation ready" when no transport exists —
531
+ * the visitor is told to wait for something that will never arrive. The third
532
+ * string keeps the status honest in all three states.
533
+ */
534
+ function composerStatusText(hasTransport, isReady) {
535
+ if (!hasTransport) return "Chat is unavailable";
536
+ return isReady ? "Conversation ready" : "Connecting…";
537
+ }
538
+ function ChatWidget({ className, createTransport, defaultVisitor, getSession, initiallyOpen = false, launcherLabel = "Chat with us", storage = DEFAULT_STORAGE, style, theme = "auto", widgetId }) {
487
539
  const [isOpen, setIsOpen] = (0, react.useState)(initiallyOpen);
488
540
  const [error, setError] = (0, react.useState)(null);
489
541
  const [isConnecting, setIsConnecting] = (0, react.useState)(false);
@@ -492,11 +544,15 @@ function ChatWidget({ className, createTransport, getSession, initiallyOpen = fa
492
544
  const [messages, setMessages] = (0, react.useState)([]);
493
545
  const [session, setSession] = (0, react.useState)(null);
494
546
  const [visitor, setVisitor] = (0, react.useState)({
495
- email: "",
496
- name: ""
547
+ email: defaultVisitor?.email ?? "",
548
+ name: defaultVisitor?.name ?? ""
497
549
  });
498
550
  const transportRef = (0, react.useRef)(null);
499
551
  const listRef = (0, react.useRef)(null);
552
+ (0, react.useEffect)(() => {
553
+ if (createTransport) return;
554
+ console.warn("[clivly] <ChatWidget> was given no createTransport, so messages cannot be sent. Pass createTransport={createClivlyTransport()} — see https://www.npmjs.com/package/@clivly/chat-widget#five-minute-quick-start");
555
+ }, []);
500
556
  const themeStyle = useThemeStyle(session, style);
501
557
  const resolvedTheme = useResolvedTheme(theme);
502
558
  (0, react.useEffect)(() => () => {
@@ -539,15 +595,13 @@ function ChatWidget({ className, createTransport, getSession, initiallyOpen = fa
539
595
  if (transportRef.current === connection) transportRef.current = null;
540
596
  };
541
597
  }, [createTransport, session]);
542
- const submitVisitor = async (event) => {
543
- event.preventDefault();
598
+ const startSession = (0, react.useCallback)(async (identity) => {
544
599
  setError(null);
545
600
  setIsConnecting(true);
546
601
  try {
547
- const existingVisitorToken = storage?.getItem(createStorageKey(widgetId)) ?? null;
548
602
  const nextSession = await getSession({
549
- visitor,
550
- visitorToken: existingVisitorToken,
603
+ visitor: identity,
604
+ visitorToken: storage?.getItem(createStorageKey(widgetId)) ?? null,
551
605
  widgetId
552
606
  });
553
607
  storage?.setItem(createStorageKey(widgetId), nextSession.visitorToken);
@@ -559,7 +613,30 @@ function ChatWidget({ className, createTransport, getSession, initiallyOpen = fa
559
613
  } finally {
560
614
  setIsConnecting(false);
561
615
  }
616
+ }, [
617
+ getSession,
618
+ storage,
619
+ widgetId
620
+ ]);
621
+ const submitVisitor = async (event) => {
622
+ event.preventDefault();
623
+ await startSession(visitor);
562
624
  };
625
+ (0, react.useEffect)(() => {
626
+ if (!(isOpen && isCompleteVisitor(defaultVisitor))) return;
627
+ if (session || isConnecting || error) return;
628
+ startSession({
629
+ email: defaultVisitor.email.trim(),
630
+ name: defaultVisitor.name.trim()
631
+ }).catch(() => {});
632
+ }, [
633
+ defaultVisitor,
634
+ error,
635
+ isConnecting,
636
+ isOpen,
637
+ session,
638
+ startSession
639
+ ]);
563
640
  const submitMessage = (event) => {
564
641
  event?.preventDefault();
565
642
  if (!(session && transportRef.current)) {
@@ -591,6 +668,7 @@ function ChatWidget({ className, createTransport, getSession, initiallyOpen = fa
591
668
  setError(resolved.message);
592
669
  }
593
670
  };
671
+ const canSend = Boolean(createTransport) && isReady;
594
672
  const greeting = session?.widget.config.greeting ?? "Tell us a little about yourself and we’ll pick up the conversation from there.";
595
673
  const panelTitle = session?.widget.name ?? "Start a conversation";
596
674
  const sessionSummary = sessionSummaryText(session, greeting);
@@ -658,7 +736,7 @@ function ChatWidget({ className, createTransport, getSession, initiallyOpen = fa
658
736
  onKeyDown: (event) => {
659
737
  if (!isSendKey(event)) return;
660
738
  event.preventDefault();
661
- if (messageDraft.trim() && isReady) submitMessage();
739
+ if (messageDraft.trim() && canSend) submitMessage();
662
740
  },
663
741
  placeholder: "Write your message",
664
742
  value: messageDraft
@@ -667,10 +745,10 @@ function ChatWidget({ className, createTransport, getSession, initiallyOpen = fa
667
745
  className: "clivly-chat-widget__actions",
668
746
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
669
747
  className: "clivly-chat-widget__subtitle",
670
- children: isReady || !createTransport ? "Conversation ready" : "Connecting…"
748
+ children: composerStatusText(Boolean(createTransport), isReady)
671
749
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
672
750
  className: "clivly-chat-widget__button",
673
- disabled: !(messageDraft.trim() && isReady),
751
+ disabled: !(messageDraft.trim() && canSend),
674
752
  type: "submit",
675
753
  children: "Send"
676
754
  })]
@@ -732,11 +810,18 @@ function ChatWidget({ className, createTransport, getSession, initiallyOpen = fa
732
810
  }),
733
811
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
734
812
  className: "clivly-chat-widget__powered",
735
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
813
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
736
814
  href: "https://clivly.com",
737
815
  rel: "noopener",
738
816
  target: "_blank",
739
- children: "Powered by Clivly"
817
+ children: [
818
+ "Powered by",
819
+ " ",
820
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
821
+ className: "clivly-chat-widget__powered-mark",
822
+ children: "Clivly"
823
+ })
824
+ ]
740
825
  })
741
826
  })
742
827
  ]
@@ -899,6 +984,33 @@ function createSessionFetcher({ fetchImpl = fetch, sessionUrl }) {
899
984
  //#endregion
900
985
  //#region src/browser-mount.tsx
901
986
  const MOUNTED_DATA_ATTR = "clivlyMounted";
987
+ /**
988
+ * The identity a script-tag host already knows, read off the tag.
989
+ *
990
+ * The React component takes `defaultVisitor` as a prop; a script tag has only
991
+ * strings, so `data-visitor-name` / `data-visitor-email` are the same seam. A
992
+ * host that server-renders its page can template the signed-in user's details
993
+ * straight into the tag and the visitor never sees the pre-chat form.
994
+ *
995
+ * Returns `undefined` rather than an empty object when neither attribute is
996
+ * present, so the component's own "no identity" path stays untouched. Partial
997
+ * values are passed through deliberately: `ChatWidget` treats one field as a
998
+ * prefill and still renders the form, which is the right outcome for a host
999
+ * that knows an email but not a name.
1000
+ *
1001
+ * This is self-reported identity — anyone can edit the attribute in devtools.
1002
+ * `resolveUser` on the host backend is what makes it trustworthy; this only
1003
+ * removes the typing.
1004
+ */
1005
+ function readScriptVisitor(dataset) {
1006
+ const email = dataset.visitorEmail?.trim();
1007
+ const name = dataset.visitorName?.trim();
1008
+ if (!(email || name)) return;
1009
+ return {
1010
+ ...email ? { email } : {},
1011
+ ...name ? { name } : {}
1012
+ };
1013
+ }
902
1014
  function resolveSocketUrl(socketUrlTemplate, conversationId, token) {
903
1015
  return socketUrlTemplate.replaceAll("{conversationId}", encodeURIComponent(conversationId)).replaceAll("{token}", encodeURIComponent(token));
904
1016
  }
@@ -943,6 +1055,7 @@ function mountChatWidgetScript$1(script = document.currentScript) {
943
1055
  shadowRoot.append(container);
944
1056
  const { root, setTheme } = createWidgetMount({
945
1057
  container,
1058
+ defaultVisitor: readScriptVisitor(script.dataset),
946
1059
  sessionUrl,
947
1060
  socketUrlTemplate: script.dataset.socketUrlTemplate,
948
1061
  theme: parseTheme(script.dataset.theme),
package/dist/index.d.cts CHANGED
@@ -24,7 +24,7 @@ interface ResolveThemeOptions {
24
24
  * document, and guessing dark would flash the wrong palette on hydration for
25
25
  * the majority of pages.
26
26
  */
27
- declare function resolveTheme(theme: ChatWidgetTheme, { root, view }?: ResolveThemeOptions): ResolvedTheme;
27
+ export declare function resolveTheme(theme: ChatWidgetTheme, { root, view }?: ResolveThemeOptions): ResolvedTheme;
28
28
  //#endregion
29
29
  //#region src/types.d.ts
30
30
  type WidgetSenderType = "visitor" | "operator" | "system";
@@ -125,18 +125,36 @@ interface CreateWebSocketTransportOptions {
125
125
  getProtocols?: (session: WidgetSessionPayload) => string | string[] | undefined;
126
126
  resolveUrl: (session: WidgetSessionPayload) => string;
127
127
  }
128
- declare function createWebSocketTransport({ createSocket, getProtocols, resolveUrl }: CreateWebSocketTransportOptions): CreateChatTransport;
128
+ export declare function createWebSocketTransport({ createSocket, getProtocols, resolveUrl }: CreateWebSocketTransportOptions): CreateChatTransport;
129
129
  interface CreateLoopbackTransportOptions {
130
130
  operatorName?: string;
131
131
  operatorReply?: (body: string) => string | null;
132
132
  replyDelayMs?: number;
133
133
  }
134
- declare function createLoopbackTransport({ operatorName, operatorReply, replyDelayMs }?: CreateLoopbackTransportOptions): CreateChatTransport;
134
+ export declare function createLoopbackTransport({ operatorName, operatorReply, replyDelayMs }?: CreateLoopbackTransportOptions): CreateChatTransport;
135
135
  //#endregion
136
136
  //#region src/chat-widget.d.ts
137
137
  interface ChatWidgetProps {
138
138
  className?: string;
139
139
  createTransport?: CreateChatTransport;
140
+ /**
141
+ * Identity the host already knows, used to skip the pre-chat form.
142
+ *
143
+ * When both `name` and `email` are non-empty the widget opens straight into
144
+ * the conversation and bootstraps the session itself, instead of asking a
145
+ * signed-in user to retype what the host app already displays in its own
146
+ * header. A partial value (one field, or blanks) is treated as a prefill:
147
+ * the form still renders, with the known fields filled in.
148
+ *
149
+ * This seeds the *form*, and a form value is self-reported no matter who
150
+ * typed it. It is not, on its own, proof of identity — anyone can pass any
151
+ * address here. Verification is a separate, server-side mechanism: pass
152
+ * `resolveUser` to `createChatSessionHandler` on the host backend and it
153
+ * resolves the caller's real session behind the API key, which is what marks
154
+ * the resulting contact verified. Use both together for a signed-in user:
155
+ * this prop removes the typing, `resolveUser` is what makes it trustworthy.
156
+ */
157
+ defaultVisitor?: Partial<ChatWidgetVisitor>;
140
158
  getSession: GetSession;
141
159
  initiallyOpen?: boolean;
142
160
  launcherLabel?: string;
@@ -155,12 +173,12 @@ interface ChatWidgetProps {
155
173
  theme?: ChatWidgetTheme;
156
174
  widgetId: string;
157
175
  }
158
- declare function ChatWidget({ className, createTransport, getSession, initiallyOpen, launcherLabel, storage, style, theme, widgetId }: ChatWidgetProps): ReactElement;
159
- declare function ChatLauncher(): null;
160
- declare function ChatPanel(): null;
161
- declare function MessageComposer(): null;
162
- declare function PreChatForm(): null;
163
- declare function ConversationView(): null;
176
+ export declare function ChatWidget({ className, createTransport, defaultVisitor, getSession, initiallyOpen, launcherLabel, storage, style, theme, widgetId }: ChatWidgetProps): ReactElement;
177
+ export declare function ChatLauncher(): null;
178
+ export declare function ChatPanel(): null;
179
+ export declare function MessageComposer(): null;
180
+ export declare function PreChatForm(): null;
181
+ export declare function ConversationView(): null;
164
182
  //#endregion
165
183
  //#region src/browser-mount.d.ts
166
184
  interface MountChatWidgetOptions$1 extends Omit<ChatWidgetProps, "getSession" | "widgetId"> {
@@ -206,16 +224,16 @@ interface CreateSessionFetcherOptions {
206
224
  fetchImpl?: typeof fetch;
207
225
  sessionUrl: string;
208
226
  }
209
- declare function createSessionFetcher({ fetchImpl, sessionUrl }: CreateSessionFetcherOptions): GetSession;
227
+ export declare function createSessionFetcher({ fetchImpl, sessionUrl }: CreateSessionFetcherOptions): GetSession;
210
228
  //#endregion
211
229
  //#region src/index.d.ts
212
- type ChatWidgetMount = ChatWidgetMount$1;
213
- type MountChatWidgetOptions = MountChatWidgetOptions$1;
214
- type ScriptMountResult = ScriptMountResult$1;
215
- declare const autoMountChatWidgetScript: typeof autoMountChatWidgetScript$1;
216
- type CreateClivlyTransportOptions = CreateClivlyTransportOptions$1;
217
- declare const createClivlyTransport: typeof createClivlyTransport$1;
218
- declare const mountChatWidget: typeof mountChatWidget$1;
219
- declare const mountChatWidgetScript: typeof mountChatWidgetScript$1;
230
+ export type ChatWidgetMount = ChatWidgetMount$1;
231
+ export type MountChatWidgetOptions = MountChatWidgetOptions$1;
232
+ export type ScriptMountResult = ScriptMountResult$1;
233
+ export declare const autoMountChatWidgetScript: typeof autoMountChatWidgetScript$1;
234
+ export type CreateClivlyTransportOptions = CreateClivlyTransportOptions$1;
235
+ export declare const createClivlyTransport: typeof createClivlyTransport$1;
236
+ export declare const mountChatWidget: typeof mountChatWidget$1;
237
+ export declare const mountChatWidgetScript: typeof mountChatWidgetScript$1;
220
238
  //#endregion
221
- export { ChatLauncher, ChatPanel, type ChatTransportConnection, type ChatTransportHandlers, ChatWidget, ChatWidgetMount, type ChatWidgetProps, type ChatWidgetTheme, type ChatWidgetVisitor, ConversationView, type CreateChatTransport, type CreateChatTransportOptions, CreateClivlyTransportOptions, type CreateLoopbackTransportOptions, type CreateWebSocketTransportOptions, type GetSession, type GetSessionOptions, MessageComposer, MountChatWidgetOptions, PreChatForm, type ResolvedTheme, ScriptMountResult, type WidgetClientCreateMessageEvent, type WidgetClientEvent, type WidgetConfig, type WidgetContact, type WidgetConversation, type WidgetConversationSnapshotEvent, type WidgetErrorEvent, type WidgetMessage, type WidgetMessageAckEvent, type WidgetMessageCreatedEvent, type WidgetSenderType, type WidgetServerEvent, type WidgetSessionPayload, type WidgetSessionReadyEvent, type WidgetSummary, autoMountChatWidgetScript, createClivlyTransport, createLoopbackTransport, createSessionFetcher, createWebSocketTransport, mountChatWidget, mountChatWidgetScript, resolveTheme };
239
+ export type { ChatTransportConnection, ChatTransportHandlers, ChatWidgetProps, ChatWidgetTheme, ChatWidgetVisitor, CreateChatTransport, CreateChatTransportOptions, CreateLoopbackTransportOptions, CreateWebSocketTransportOptions, GetSession, GetSessionOptions, ResolvedTheme, WidgetClientCreateMessageEvent, WidgetClientEvent, WidgetConfig, WidgetContact, WidgetConversation, WidgetConversationSnapshotEvent, WidgetErrorEvent, WidgetMessage, WidgetMessageAckEvent, WidgetMessageCreatedEvent, WidgetSenderType, WidgetServerEvent, WidgetSessionPayload, WidgetSessionReadyEvent, WidgetSummary };
package/dist/index.d.mts CHANGED
@@ -24,7 +24,7 @@ interface ResolveThemeOptions {
24
24
  * document, and guessing dark would flash the wrong palette on hydration for
25
25
  * the majority of pages.
26
26
  */
27
- declare function resolveTheme(theme: ChatWidgetTheme, { root, view }?: ResolveThemeOptions): ResolvedTheme;
27
+ export declare function resolveTheme(theme: ChatWidgetTheme, { root, view }?: ResolveThemeOptions): ResolvedTheme;
28
28
  //#endregion
29
29
  //#region src/types.d.ts
30
30
  type WidgetSenderType = "visitor" | "operator" | "system";
@@ -125,18 +125,36 @@ interface CreateWebSocketTransportOptions {
125
125
  getProtocols?: (session: WidgetSessionPayload) => string | string[] | undefined;
126
126
  resolveUrl: (session: WidgetSessionPayload) => string;
127
127
  }
128
- declare function createWebSocketTransport({ createSocket, getProtocols, resolveUrl }: CreateWebSocketTransportOptions): CreateChatTransport;
128
+ export declare function createWebSocketTransport({ createSocket, getProtocols, resolveUrl }: CreateWebSocketTransportOptions): CreateChatTransport;
129
129
  interface CreateLoopbackTransportOptions {
130
130
  operatorName?: string;
131
131
  operatorReply?: (body: string) => string | null;
132
132
  replyDelayMs?: number;
133
133
  }
134
- declare function createLoopbackTransport({ operatorName, operatorReply, replyDelayMs }?: CreateLoopbackTransportOptions): CreateChatTransport;
134
+ export declare function createLoopbackTransport({ operatorName, operatorReply, replyDelayMs }?: CreateLoopbackTransportOptions): CreateChatTransport;
135
135
  //#endregion
136
136
  //#region src/chat-widget.d.ts
137
137
  interface ChatWidgetProps {
138
138
  className?: string;
139
139
  createTransport?: CreateChatTransport;
140
+ /**
141
+ * Identity the host already knows, used to skip the pre-chat form.
142
+ *
143
+ * When both `name` and `email` are non-empty the widget opens straight into
144
+ * the conversation and bootstraps the session itself, instead of asking a
145
+ * signed-in user to retype what the host app already displays in its own
146
+ * header. A partial value (one field, or blanks) is treated as a prefill:
147
+ * the form still renders, with the known fields filled in.
148
+ *
149
+ * This seeds the *form*, and a form value is self-reported no matter who
150
+ * typed it. It is not, on its own, proof of identity — anyone can pass any
151
+ * address here. Verification is a separate, server-side mechanism: pass
152
+ * `resolveUser` to `createChatSessionHandler` on the host backend and it
153
+ * resolves the caller's real session behind the API key, which is what marks
154
+ * the resulting contact verified. Use both together for a signed-in user:
155
+ * this prop removes the typing, `resolveUser` is what makes it trustworthy.
156
+ */
157
+ defaultVisitor?: Partial<ChatWidgetVisitor>;
140
158
  getSession: GetSession;
141
159
  initiallyOpen?: boolean;
142
160
  launcherLabel?: string;
@@ -155,12 +173,12 @@ interface ChatWidgetProps {
155
173
  theme?: ChatWidgetTheme;
156
174
  widgetId: string;
157
175
  }
158
- declare function ChatWidget({ className, createTransport, getSession, initiallyOpen, launcherLabel, storage, style, theme, widgetId }: ChatWidgetProps): ReactElement;
159
- declare function ChatLauncher(): null;
160
- declare function ChatPanel(): null;
161
- declare function MessageComposer(): null;
162
- declare function PreChatForm(): null;
163
- declare function ConversationView(): null;
176
+ export declare function ChatWidget({ className, createTransport, defaultVisitor, getSession, initiallyOpen, launcherLabel, storage, style, theme, widgetId }: ChatWidgetProps): ReactElement;
177
+ export declare function ChatLauncher(): null;
178
+ export declare function ChatPanel(): null;
179
+ export declare function MessageComposer(): null;
180
+ export declare function PreChatForm(): null;
181
+ export declare function ConversationView(): null;
164
182
  //#endregion
165
183
  //#region src/browser-mount.d.ts
166
184
  interface MountChatWidgetOptions$1 extends Omit<ChatWidgetProps, "getSession" | "widgetId"> {
@@ -206,16 +224,16 @@ interface CreateSessionFetcherOptions {
206
224
  fetchImpl?: typeof fetch;
207
225
  sessionUrl: string;
208
226
  }
209
- declare function createSessionFetcher({ fetchImpl, sessionUrl }: CreateSessionFetcherOptions): GetSession;
227
+ export declare function createSessionFetcher({ fetchImpl, sessionUrl }: CreateSessionFetcherOptions): GetSession;
210
228
  //#endregion
211
229
  //#region src/index.d.ts
212
- type ChatWidgetMount = ChatWidgetMount$1;
213
- type MountChatWidgetOptions = MountChatWidgetOptions$1;
214
- type ScriptMountResult = ScriptMountResult$1;
215
- declare const autoMountChatWidgetScript: typeof autoMountChatWidgetScript$1;
216
- type CreateClivlyTransportOptions = CreateClivlyTransportOptions$1;
217
- declare const createClivlyTransport: typeof createClivlyTransport$1;
218
- declare const mountChatWidget: typeof mountChatWidget$1;
219
- declare const mountChatWidgetScript: typeof mountChatWidgetScript$1;
230
+ export type ChatWidgetMount = ChatWidgetMount$1;
231
+ export type MountChatWidgetOptions = MountChatWidgetOptions$1;
232
+ export type ScriptMountResult = ScriptMountResult$1;
233
+ export declare const autoMountChatWidgetScript: typeof autoMountChatWidgetScript$1;
234
+ export type CreateClivlyTransportOptions = CreateClivlyTransportOptions$1;
235
+ export declare const createClivlyTransport: typeof createClivlyTransport$1;
236
+ export declare const mountChatWidget: typeof mountChatWidget$1;
237
+ export declare const mountChatWidgetScript: typeof mountChatWidgetScript$1;
220
238
  //#endregion
221
- export { ChatLauncher, ChatPanel, type ChatTransportConnection, type ChatTransportHandlers, ChatWidget, ChatWidgetMount, type ChatWidgetProps, type ChatWidgetTheme, type ChatWidgetVisitor, ConversationView, type CreateChatTransport, type CreateChatTransportOptions, CreateClivlyTransportOptions, type CreateLoopbackTransportOptions, type CreateWebSocketTransportOptions, type GetSession, type GetSessionOptions, MessageComposer, MountChatWidgetOptions, PreChatForm, type ResolvedTheme, ScriptMountResult, type WidgetClientCreateMessageEvent, type WidgetClientEvent, type WidgetConfig, type WidgetContact, type WidgetConversation, type WidgetConversationSnapshotEvent, type WidgetErrorEvent, type WidgetMessage, type WidgetMessageAckEvent, type WidgetMessageCreatedEvent, type WidgetSenderType, type WidgetServerEvent, type WidgetSessionPayload, type WidgetSessionReadyEvent, type WidgetSummary, autoMountChatWidgetScript, createClivlyTransport, createLoopbackTransport, createSessionFetcher, createWebSocketTransport, mountChatWidget, mountChatWidgetScript, resolveTheme };
239
+ export type { ChatTransportConnection, ChatTransportHandlers, ChatWidgetProps, ChatWidgetTheme, ChatWidgetVisitor, CreateChatTransport, CreateChatTransportOptions, CreateLoopbackTransportOptions, CreateWebSocketTransportOptions, GetSession, GetSessionOptions, ResolvedTheme, WidgetClientCreateMessageEvent, WidgetClientEvent, WidgetConfig, WidgetContact, WidgetConversation, WidgetConversationSnapshotEvent, WidgetErrorEvent, WidgetMessage, WidgetMessageAckEvent, WidgetMessageCreatedEvent, WidgetSenderType, WidgetServerEvent, WidgetSessionPayload, WidgetSessionReadyEvent, WidgetSummary };