@clivly/chat-widget 0.14.1 → 0.16.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.
@@ -1,5 +1,26 @@
1
1
  import { Root } from "react-dom/client";
2
2
  import { CSSProperties } from "react";
3
+ //#region src/layout.d.ts
4
+ /**
5
+ * Where the widget sits on the host page, and what its launcher looks like.
6
+ *
7
+ * Until 0.16 the widget rendered in normal flow wherever it was mounted — the
8
+ * script tag inserted it directly after itself, so most embeds showed a
9
+ * full-width-label pill at the end of the page rather than a floating button.
10
+ * Floating in a corner is now the default; `"inline"` keeps the old behaviour
11
+ * for a host that positions the widget itself.
12
+ */
13
+ type ChatWidgetPlacement = "bottom-left" | "bottom-right" | "inline";
14
+ /**
15
+ * - `"auto"` (default): the labelled pill on wide screens, a 48px icon-only
16
+ * button below 48rem, where a pill covers the host's own controls.
17
+ * - `"pill"`: always the labelled pill.
18
+ * - `"icon"`: always the icon-only button.
19
+ *
20
+ * The label stays the button's accessible name in every variant.
21
+ */
22
+ type ChatWidgetLauncher = "auto" | "icon" | "pill";
23
+ //#endregion
3
24
  //#region src/theme.d.ts
4
25
  /**
5
26
  * Theme resolution for the widget.
@@ -34,11 +55,30 @@ interface WidgetSummary {
34
55
  }
35
56
  interface WidgetMessage {
36
57
  author: string | null;
58
+ /**
59
+ * Set on messages the support assistant wrote (`greeting`, `topic`,
60
+ * `handoff`, `ai`, `ai_clarify`) and on a visitor's topic click
61
+ * (`topic_choice`). Absent or null on everything a person typed, and on
62
+ * every message from a server that predates the assistant.
63
+ */
64
+ automation?: string | null;
37
65
  body: string;
38
66
  createdAt: string;
39
67
  id: string;
40
68
  senderType: WidgetSenderType;
41
69
  }
70
+ type WidgetSupportState = "automated" | "handoff" | "human";
71
+ interface WidgetSupportTopic {
72
+ action: "answer" | "ask" | "link" | "human";
73
+ id: string;
74
+ label: string;
75
+ }
76
+ /** Guided support for one conversation. Answers stay on the server. */
77
+ interface WidgetSupport {
78
+ mode: "human" | "guided" | "ai";
79
+ state: WidgetSupportState | null;
80
+ topics: WidgetSupportTopic[];
81
+ }
42
82
  interface WidgetSessionPayload {
43
83
  contact: WidgetContact | null;
44
84
  conversation: WidgetConversation;
@@ -46,6 +86,8 @@ interface WidgetSessionPayload {
46
86
  messages: WidgetMessage[];
47
87
  /** Absolute socket URL returned by Clivly's bootstrap. */
48
88
  socketUrl?: string;
89
+ /** Absent or null when the widget has no guided support configured. */
90
+ support?: WidgetSupport | null;
49
91
  token: string;
50
92
  visitorToken: string;
51
93
  widget: WidgetSummary;
@@ -65,6 +107,12 @@ interface WidgetClientCreateMessageEvent {
65
107
  id: string;
66
108
  type: "message.create";
67
109
  }
110
+ /** The visitor picked a guided topic (see `ChatTransportConnection.selectTopic`). */
111
+ interface WidgetClientTopicEvent {
112
+ id: string;
113
+ topicId: string;
114
+ type: "topic.select";
115
+ }
68
116
  interface WidgetSessionReadyEvent {
69
117
  conversationId: string;
70
118
  type: "session.ready";
@@ -83,11 +131,20 @@ interface WidgetMessageAckEvent {
83
131
  message: WidgetMessage;
84
132
  type: "message.ack";
85
133
  }
134
+ interface WidgetSupportStateEvent {
135
+ state: WidgetSupportState;
136
+ type: "support.state";
137
+ }
138
+ /** The assistant is preparing an answer (`thinking`) or has finished (`idle`). */
139
+ interface WidgetAssistantStatusEvent {
140
+ status: "thinking" | "idle";
141
+ type: "assistant.status";
142
+ }
86
143
  interface WidgetErrorEvent {
87
144
  message: string;
88
145
  type: "error";
89
146
  }
90
- type WidgetServerEvent = WidgetConversationSnapshotEvent | WidgetErrorEvent | WidgetMessageAckEvent | WidgetMessageCreatedEvent | WidgetSessionReadyEvent;
147
+ type WidgetServerEvent = WidgetAssistantStatusEvent | WidgetConversationSnapshotEvent | WidgetErrorEvent | WidgetMessageAckEvent | WidgetMessageCreatedEvent | WidgetSessionReadyEvent | WidgetSupportStateEvent;
91
148
  //#endregion
92
149
  //#region src/transport.d.ts
93
150
  interface ChatTransportHandlers {
@@ -104,6 +161,11 @@ interface ChatTransportConnection {
104
161
  * without it still works, its visitors are just emailed replies they saw.
105
162
  */
106
163
  markRead?: () => void;
164
+ /**
165
+ * Send a guided-topic click. Optional: without it the widget sends the
166
+ * topic's label as an ordinary message, so a custom transport still works.
167
+ */
168
+ selectTopic?: (event: WidgetClientTopicEvent) => void;
107
169
  sendMessage: (event: WidgetClientCreateMessageEvent) => void;
108
170
  }
109
171
  interface CreateChatTransportOptions {
@@ -136,7 +198,17 @@ interface ChatWidgetProps {
136
198
  defaultVisitor?: Partial<ChatWidgetVisitor>;
137
199
  getSession: GetSession;
138
200
  initiallyOpen?: boolean;
201
+ /** Launcher shape; see {@link ChatWidgetLauncher}. Defaults to `"auto"`. */
202
+ launcher?: ChatWidgetLauncher;
203
+ /** The launcher's visible text, and always its accessible name. */
139
204
  launcherLabel?: string;
205
+ /**
206
+ * Where the widget sits; see {@link ChatWidgetPlacement}. Defaults to
207
+ * `"bottom-right"`, floating clear of the device's safe-area insets. Stacks
208
+ * at `--clivly-chat-z-index` (default 2147483000) — lower it through
209
+ * `style` to sit beneath the host's own dialogs.
210
+ */
211
+ placement?: ChatWidgetPlacement;
140
212
  storage?: Pick<Storage, "getItem" | "setItem">;
141
213
  /**
142
214
  * Inline styles on the widget root. The intended use is overriding palette
@@ -1,5 +1,26 @@
1
1
  import { Root } from "react-dom/client";
2
2
  import { CSSProperties } from "react";
3
+ //#region src/layout.d.ts
4
+ /**
5
+ * Where the widget sits on the host page, and what its launcher looks like.
6
+ *
7
+ * Until 0.16 the widget rendered in normal flow wherever it was mounted — the
8
+ * script tag inserted it directly after itself, so most embeds showed a
9
+ * full-width-label pill at the end of the page rather than a floating button.
10
+ * Floating in a corner is now the default; `"inline"` keeps the old behaviour
11
+ * for a host that positions the widget itself.
12
+ */
13
+ type ChatWidgetPlacement = "bottom-left" | "bottom-right" | "inline";
14
+ /**
15
+ * - `"auto"` (default): the labelled pill on wide screens, a 48px icon-only
16
+ * button below 48rem, where a pill covers the host's own controls.
17
+ * - `"pill"`: always the labelled pill.
18
+ * - `"icon"`: always the icon-only button.
19
+ *
20
+ * The label stays the button's accessible name in every variant.
21
+ */
22
+ type ChatWidgetLauncher = "auto" | "icon" | "pill";
23
+ //#endregion
3
24
  //#region src/theme.d.ts
4
25
  /**
5
26
  * Theme resolution for the widget.
@@ -34,11 +55,30 @@ interface WidgetSummary {
34
55
  }
35
56
  interface WidgetMessage {
36
57
  author: string | null;
58
+ /**
59
+ * Set on messages the support assistant wrote (`greeting`, `topic`,
60
+ * `handoff`, `ai`, `ai_clarify`) and on a visitor's topic click
61
+ * (`topic_choice`). Absent or null on everything a person typed, and on
62
+ * every message from a server that predates the assistant.
63
+ */
64
+ automation?: string | null;
37
65
  body: string;
38
66
  createdAt: string;
39
67
  id: string;
40
68
  senderType: WidgetSenderType;
41
69
  }
70
+ type WidgetSupportState = "automated" | "handoff" | "human";
71
+ interface WidgetSupportTopic {
72
+ action: "answer" | "ask" | "link" | "human";
73
+ id: string;
74
+ label: string;
75
+ }
76
+ /** Guided support for one conversation. Answers stay on the server. */
77
+ interface WidgetSupport {
78
+ mode: "human" | "guided" | "ai";
79
+ state: WidgetSupportState | null;
80
+ topics: WidgetSupportTopic[];
81
+ }
42
82
  interface WidgetSessionPayload {
43
83
  contact: WidgetContact | null;
44
84
  conversation: WidgetConversation;
@@ -46,6 +86,8 @@ interface WidgetSessionPayload {
46
86
  messages: WidgetMessage[];
47
87
  /** Absolute socket URL returned by Clivly's bootstrap. */
48
88
  socketUrl?: string;
89
+ /** Absent or null when the widget has no guided support configured. */
90
+ support?: WidgetSupport | null;
49
91
  token: string;
50
92
  visitorToken: string;
51
93
  widget: WidgetSummary;
@@ -65,6 +107,12 @@ interface WidgetClientCreateMessageEvent {
65
107
  id: string;
66
108
  type: "message.create";
67
109
  }
110
+ /** The visitor picked a guided topic (see `ChatTransportConnection.selectTopic`). */
111
+ interface WidgetClientTopicEvent {
112
+ id: string;
113
+ topicId: string;
114
+ type: "topic.select";
115
+ }
68
116
  interface WidgetSessionReadyEvent {
69
117
  conversationId: string;
70
118
  type: "session.ready";
@@ -83,11 +131,20 @@ interface WidgetMessageAckEvent {
83
131
  message: WidgetMessage;
84
132
  type: "message.ack";
85
133
  }
134
+ interface WidgetSupportStateEvent {
135
+ state: WidgetSupportState;
136
+ type: "support.state";
137
+ }
138
+ /** The assistant is preparing an answer (`thinking`) or has finished (`idle`). */
139
+ interface WidgetAssistantStatusEvent {
140
+ status: "thinking" | "idle";
141
+ type: "assistant.status";
142
+ }
86
143
  interface WidgetErrorEvent {
87
144
  message: string;
88
145
  type: "error";
89
146
  }
90
- type WidgetServerEvent = WidgetConversationSnapshotEvent | WidgetErrorEvent | WidgetMessageAckEvent | WidgetMessageCreatedEvent | WidgetSessionReadyEvent;
147
+ type WidgetServerEvent = WidgetAssistantStatusEvent | WidgetConversationSnapshotEvent | WidgetErrorEvent | WidgetMessageAckEvent | WidgetMessageCreatedEvent | WidgetSessionReadyEvent | WidgetSupportStateEvent;
91
148
  //#endregion
92
149
  //#region src/transport.d.ts
93
150
  interface ChatTransportHandlers {
@@ -104,6 +161,11 @@ interface ChatTransportConnection {
104
161
  * without it still works, its visitors are just emailed replies they saw.
105
162
  */
106
163
  markRead?: () => void;
164
+ /**
165
+ * Send a guided-topic click. Optional: without it the widget sends the
166
+ * topic's label as an ordinary message, so a custom transport still works.
167
+ */
168
+ selectTopic?: (event: WidgetClientTopicEvent) => void;
107
169
  sendMessage: (event: WidgetClientCreateMessageEvent) => void;
108
170
  }
109
171
  interface CreateChatTransportOptions {
@@ -136,7 +198,17 @@ interface ChatWidgetProps {
136
198
  defaultVisitor?: Partial<ChatWidgetVisitor>;
137
199
  getSession: GetSession;
138
200
  initiallyOpen?: boolean;
201
+ /** Launcher shape; see {@link ChatWidgetLauncher}. Defaults to `"auto"`. */
202
+ launcher?: ChatWidgetLauncher;
203
+ /** The launcher's visible text, and always its accessible name. */
139
204
  launcherLabel?: string;
205
+ /**
206
+ * Where the widget sits; see {@link ChatWidgetPlacement}. Defaults to
207
+ * `"bottom-right"`, floating clear of the device's safe-area insets. Stacks
208
+ * at `--clivly-chat-z-index` (default 2147483000) — lower it through
209
+ * `style` to sit beneath the host's own dialogs.
210
+ */
211
+ placement?: ChatWidgetPlacement;
140
212
  storage?: Pick<Storage, "getItem" | "setItem">;
141
213
  /**
142
214
  * Inline styles on the widget root. The intended use is overriding palette