@leadbot/components 2.21.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/README.md ADDED
@@ -0,0 +1,332 @@
1
+ # @leadbot/components
2
+
3
+ LeadBot Components building blocks: mount an AI agent chat, lead forms,
4
+ callback/email requests or a WhatsApp handoff into your own DOM, styled by
5
+ your site. Rendered into the light DOM (no shadow DOM, no iframe); Preact is
6
+ bundled internally and invisible to consumers.
7
+
8
+ ## Usage
9
+
10
+ ```ts
11
+ import {
12
+ init,
13
+ mountAgentChat,
14
+ mountForm,
15
+ mountCallbackRequest,
16
+ mountEmailRequest,
17
+ mountWhatsApp,
18
+ mountButtons,
19
+ mountFlow,
20
+ openWidget,
21
+ } from "@leadbot/components";
22
+ import "@leadbot/components/styles.css";
23
+
24
+ init({ domainKey: "your-domain-key", apiHost: "https://collector.example.com" });
25
+
26
+ const chat = mountAgentChat(document.querySelector("#chat")!, {
27
+ leadbotId: 18,
28
+ id: "chat-step-uuid",
29
+ initialMessage: "Hi! How can I help?",
30
+ conversationStarters: ["What are your opening hours?"],
31
+ });
32
+
33
+ const form = mountCallbackRequest(document.querySelector("#callback")!, {
34
+ leadbotId: 19,
35
+ id: "callback-step-uuid",
36
+ eventLabel: "Homepage callback",
37
+ marksAsQualified: true,
38
+ });
39
+
40
+ // Every mount returns { update(options), destroy() }.
41
+ form.update({ submitLabel: "Call me!" });
42
+ chat.destroy();
43
+
44
+ const buttons = mountButtons(document.querySelector("#buttons")!, {
45
+ leadbotId: 18,
46
+ id: "buttons-step-uuid",
47
+ onSelect: (button) => console.log("clicked", button.label),
48
+ });
49
+
50
+ const flow = mountFlow(document.querySelector("#flow")!);
51
+ ```
52
+
53
+ Every mount requires a `leadbotId` — the id of the component leadbot the
54
+ component belongs to (each embed can target a different one) — and an `id`,
55
+ the uuid of the published step it renders. Mounts also
56
+ accept `domainKey` / `apiHost` to override the `init()` defaults, a
57
+ `className` for the container, and a `labels` object to translate every UI
58
+ string (defaults are English).
59
+
60
+ `preview` / `previewToken` exist for the dashboard preview of unpublished draft
61
+ flows: `preview` keeps submissions out of the inbox, `previewToken` authorizes
62
+ the draft. The token is ignored unless `preview` is set, and it must never be
63
+ committed into a production build — anyone reading the shipped JS could then
64
+ read your unpublished flows.
65
+
66
+ ## Published steps
67
+
68
+ Every component is backed by a published step. Its `id` is the step uuid, and
69
+ it configures the component from the dashboard — form fields, labels, WhatsApp
70
+ number, agent:
71
+
72
+ ```ts
73
+ mountForm(document.querySelector("#demo")!, { leadbotId: 18, id: "step-uuid" });
74
+ ```
75
+
76
+ The flow is fetched once per leadbot per page and shared by every component;
77
+ that request is also what counts the pageview. Options you pass explicitly
78
+ still win over the published step, but the `id` itself is not optional: it is
79
+ also what the collector validates submissions and chat turns against.
80
+
81
+ Because the published flow is the single source of truth, **unpublishing a bot
82
+ removes its components from every page** — they render nothing until it is
83
+ published again.
84
+
85
+ ## Theming
86
+
87
+ Everything is restylable, at whichever level suits you:
88
+
89
+ 1. **Tokens** — override a custom property on `.leadbot-components` or **any
90
+ ancestor** (including `:root`). Defaults are declared on `:root` inside
91
+ `@layer leadbot`, so an inherited value always wins.
92
+ 2. **Your own rules** — every default in `@leadbot/components/styles.css` lives in
93
+ `@layer leadbot`, so any *unlayered* rule of yours beats it regardless of
94
+ specificity. No `!important` needed:
95
+ `.leadbot-components .leadbot-chatbot-message-bot { background: #111; }`
96
+ 3. **Start from scratch** — don't import `styles.css` at all. The markup is
97
+ plain light DOM with the stable class names listed under
98
+ [Class names](#class-names); style it however you like.
99
+
100
+ ```css
101
+ /* 1. tokens */
102
+ .my-section {
103
+ --leadbot-primary: #059669;
104
+ --leadbot-font-family: "Söhne", system-ui, sans-serif;
105
+ --leadbot-radius-card: 4px;
106
+ }
107
+ ```
108
+
109
+ ### Dark mode
110
+
111
+ Set `data-leadbot-theme="dark"` on `<html>`, `<body>` or any ancestor section —
112
+ every mount inside it switches to the built-in dark token set (surfaces, text,
113
+ borders, feedback colors and the WhatsApp backdrop; layout and the brand greens
114
+ are unchanged). `data-leadbot-theme="auto"` follows the OS
115
+ `prefers-color-scheme` instead. It's all tokens inside `@layer leadbot`, so your
116
+ own overrides keep winning either way.
117
+
118
+ ```html
119
+ <html data-leadbot-theme="dark"> <!-- or "auto" -->
120
+ ```
121
+
122
+ ### Tokens
123
+
124
+ | Property | Default | Purpose |
125
+ |---|---|---|
126
+ | `--leadbot-primary` | `#2563eb` | Buttons, accents, user chat bubbles, focus rings |
127
+ | `--leadbot-available-height` | `600px` | Max height of chat / form panels |
128
+ | `--leadbot-form-max-width` | `360px` | Form card width cap; set `none` for full width |
129
+ | `--leadbot-form-max-height` | *available height* | Form card height cap; set `none` to never scroll |
130
+ | `--leadbot-font-family` | *page default* | Type stack; undeclared so your page's font shows through |
131
+ | `--leadbot-font-size` | `14px` | Body text |
132
+ | `--leadbot-font-size-sm` / `-xs` / `-lg` | `13px` / `12px` / `18px` | Secondary text, captions, form titles |
133
+ | `--leadbot-color-text` | `#1f2937` | Body text |
134
+ | `--leadbot-color-text-strong` | `#374151` | Menu items, table headers |
135
+ | `--leadbot-color-text-body` | `#4b5563` | Compact labels, quoted text |
136
+ | `--leadbot-color-text-muted` | `#6b7280` | Timestamps, citations |
137
+ | `--leadbot-color-text-subtle` | `#9ca3af` | Placeholders, typing dots, branding |
138
+ | `--leadbot-color-on-primary` | `#ffffff` | Text/icons on primary and brand fills |
139
+ | `--leadbot-color-surface` | `#ffffff` | Panels, inputs, menus |
140
+ | `--leadbot-color-surface-alt` | `#f9fafb` | Table headers, menu hover |
141
+ | `--leadbot-color-surface-sunken` | `#f3f4f6` | Bot chat bubbles |
142
+ | `--leadbot-color-surface-tray` / `-tray-alt` | `#f8f9fa` / `#f0f0f0` | Composer trays |
143
+ | `--leadbot-color-border` | `#e5e7eb` | Dividers, default borders |
144
+ | `--leadbot-color-border-input` / `-field` / `-checkbox` | `#ddd` / `#e0e3e8` / `#d1d5db` | Field borders |
145
+ | `--leadbot-color-disabled` | `#ccc` | Disabled send button |
146
+ | `--leadbot-color-logo` | `#111827` | "Powered by" wordmark |
147
+ | `--leadbot-color-error` | `#ef4444` | Field validation |
148
+ | `--leadbot-color-error-bg` / `-fg` / `-border` | `#fee` / `#c33` / `#fcc` | Form error banner |
149
+ | `--leadbot-color-success` | `#059669` | Success message |
150
+ | `--leadbot-color-success-text` | `#065f46` | "Opened in WhatsApp" confirmation |
151
+ | `--leadbot-color-online` | `#34d399` | Agent online dot |
152
+ | `--leadbot-focus-ring` / `-soft` | primary @ 15% / 12% | Focus rings on text controls |
153
+ | `--leadbot-radius-card` | `12px` | Panel border radius |
154
+ | `--leadbot-radius-sm` / `-md` / `-pill` | `6px` / `8px` / `20px` | Inputs / buttons / pills |
155
+ | `--leadbot-spacing-card` | `16px` | Panel padding |
156
+ | `--leadbot-gradient-card` | white gradient | Form card background |
157
+ | `--leadbot-border-light` | `1px solid rgba(0,0,0,.12)` | Card border |
158
+ | `--leadbot-shadow-sm` / `-md` / `-lg` | — | Elevation |
159
+ | `--leadbot-duration-fast` / `-normal` / `-slow` | `150ms` / `300ms` / `500ms` | Transitions |
160
+ | `--leadbot-ease-appear` / `-interactive` / `-loop` / `-bounce` | — | Easing curves |
161
+ | `--leadbot-whatsapp-green` / `-hover` / `-on-green` | `#42d66b` / `#35c95d` / `#111b21` | WhatsApp panel chrome |
162
+ | `--leadbot-whatsapp-wallpaper` / `-end` | `#e5ddd5` / `#d9d0c4` | WhatsApp chat backdrop |
163
+ | `--leadbot-whatsapp-bubble` / `-text` | `#ffffff` / `#303030` | WhatsApp message bubble |
164
+ | `--leadbot-whatsapp-tick` | `#4fc3f7` | Read receipts |
165
+ | `--leadbot-whatsapp-brand` / `-hover` / `-dark` | `#25d366` / `#1fb959` / `#128c7e` | Official WhatsApp brand fills |
166
+
167
+ Alternatively pass `baseColor` (e.g. `baseColor: "#ff0076"` in a mount, the
168
+ React/Vue provider, or `init()`) — it sets `--leadbot-primary` on the mount
169
+ container for you.
170
+
171
+ The spacing scale, circular radii, fixed element sizes (avatars, icons, send
172
+ buttons) and one-off shadows are deliberately **not** tokenized — override them
173
+ with a rule against the class instead (level 2 above).
174
+
175
+ ### Class names
176
+
177
+ Stable and semantic; `tests/components_styles.test.ts` fails if a component
178
+ renders a class the stylesheet doesn't style.
179
+
180
+ - **Forms** — `leadbot-form` · `leadbot-callback-form` ·
181
+ `leadbot-email-request-form` · `leadbot-form-shell` · `leadbot-form-scroll` ·
182
+ `leadbot-form-title` · `leadbot-form-fields` · `leadbot-form-actions` ·
183
+ `leadbot-form-error` · `leadbot-form-complete` · `leadbot-success-message` ·
184
+ `leadbot-submit-btn` (+ `leadbot-loading`) · `leadbot-field` ·
185
+ `leadbot-field-checkbox` · `leadbot-field-error` ·
186
+ `leadbot-field-error-text` · `leadbot-field-list` ·
187
+ `leadbot-field-list-label` · `leadbot-field-options` ·
188
+ `leadbot-field-option` · `leadbot-required-asterisk`
189
+ - **Agent chat** — `leadbot-chatbot-interface` and its `-header`,
190
+ `-header-info`, `-title`, `-status`, `-online-dot`, `-menu-btn`, `-menu`,
191
+ `-menu-item`, `-menu-item-icon`, `-messages`, `-message`, `-message-bot`,
192
+ `-message-user`, `-message-typing`, `-message-content`, `-thinking`,
193
+ `-thinking-text`, `-typing-dots`, `-citations`, `-citation-item`,
194
+ `-citation-number`, `-citation-url`, `-starters`, `-starter-btn`, `-input`,
195
+ `-input-field`, `-message-input`, `-send-btn` (+ `--active`) suffixes
196
+ - **WhatsApp** — `leadbot-whatsapp-interface` and its `-header`,
197
+ `-contact-info`, `-contact-details`, `-name`, `-status`, `-chat`, `-message`,
198
+ `-message-content`, `-input-container`, `-input-bar`, `-message-field`,
199
+ `-send-btn` suffixes, plus `leadbot-whatsapp-send-disabled`
200
+ - **Buttons** — `leadbot-buttons`, `leadbot-buttons-btn`
201
+
202
+ ### DOM anatomy
203
+
204
+ The class names alone don't tell you which element carries what, so here is the
205
+ tree each component renders. **This nesting is public API** — it changes only on
206
+ a major version, and `display: contents` on a wrapper marked *(structural)* is a
207
+ supported way to flatten it.
208
+
209
+ ```
210
+ div.leadbot-components mount container (yours; also .leadbot-bare)
211
+ └─ div.leadbot-form surface: background, border, shadow, radius,
212
+ │ size caps (also .leadbot-callback-form /
213
+ │ .leadbot-email-request-form)
214
+ └─ form.leadbot-form-shell (structural) flex column
215
+ ├─ div.leadbot-form-scroll (structural) card padding + scroll region
216
+ │ ├─ div.leadbot-form-title
217
+ │ ├─ div.leadbot-form-error
218
+ │ └─ fieldset.leadbot-form-fields (structural)
219
+ │ └─ div.leadbot-field[data-field][data-field-type] × N
220
+ │ ├─ label
221
+ │ ├─ input | select | textarea (+ .leadbot-field-error)
222
+ │ └─ div.leadbot-field-error-text
223
+ └─ div.leadbot-form-actions surface: own background + top border
224
+ └─ button.leadbot-submit-btn
225
+ ```
226
+
227
+ After a successful submit the whole tree is replaced by:
228
+
229
+ ```
230
+ div.leadbot-form.leadbot-form-complete
231
+ └─ div.leadbot-success-message
232
+ ```
233
+
234
+ Every field carries `data-field="<name>"` and `data-field-type="<canonical
235
+ type>"` — target those instead of `:nth-child`, which silently re-pairs when
236
+ fields are reordered in the dashboard.
237
+
238
+ ### Chrome, size and layout
239
+
240
+ Three opinions in the default styling are widget-isms, and each has an off
241
+ switch:
242
+
243
+ - **`chrome: false`** on any mount removes the card surface, the size caps, the
244
+ scroll region and the actions bar, leaving only functional styling (field
245
+ stacking, focus rings, error and success states). Use it whenever the form is
246
+ part of your page rather than a floating panel.
247
+ - **`--leadbot-form-max-width`** (`360px`) and **`--leadbot-form-max-height`**
248
+ (`--leadbot-available-height`) — set either to `none` to keep the card but
249
+ drop the cap.
250
+ - **`layout: "inline"`** on `mountForm` puts the submit button on the same row
251
+ as the last field; earlier fields span the full width. Pairing is positional.
252
+
253
+ ### Recipes
254
+
255
+ ```css
256
+ /* Full-width form, no card — pairs with chrome: false */
257
+ .leadbot-components .leadbot-field input { border-radius: 999px; padding: 14px 18px; }
258
+ .leadbot-components .leadbot-submit-btn { border-radius: 999px; width: auto; padding: 14px 28px; }
259
+ ```
260
+
261
+ ```js
262
+ /* Inline "url + email + button" capture row, 2/3 – 1/3 */
263
+ mountForm(el, { leadbotId: 18, id: stepUuid, chrome: false, layout: "inline", hideLabels: true });
264
+ ```
265
+
266
+ ```css
267
+ /* ...and the 2/3 – 1/3 split, by field name rather than by position */
268
+ .leadbot-components .leadbot-form-shell { grid-template-columns: 2fr 1fr; }
269
+ .leadbot-components [data-field="email"] { grid-column: 1; }
270
+ ```
271
+
272
+ ```html
273
+ <!-- Dark section — full built-in dark theme for the mounts inside it -->
274
+ <section data-leadbot-theme="dark">...</section>
275
+ ```
276
+
277
+ Components ships no focus-visible, reduced-motion or responsive rules (those are
278
+ widget-only partials) — if you replace `styles.css` wholesale, those states are
279
+ yours to provide.
280
+
281
+ ## Mounts
282
+
283
+ - `mountAgentChat(el, { leadbotId, id, initialMessage?, conversationStarters?, placeholder?, labels?, eventLabel?, chatSessionId?, onMessage?, onError?, className? })`
284
+ - `mountForm(el, { leadbotId, id, fields?, stepType?, submitLabel?, successMessage?, eventLabel?, marksAsQualified?, onSubmit?, onSuccess?, onError?, labels?, className?, chrome?, layout?, hideLabels? })` — `chrome: false` drops the widget card, `layout: "inline"` puts the button beside the last field, `hideLabels: true` keeps labels for screen readers only and uses them as placeholders
285
+ - `mountCallbackRequest(el, options)` — `mountForm` preset (name + phone, `stepType: "callback_request"`); the published step or an explicit `fields` array overrides the preset
286
+ - `mountEmailRequest(el, options)` — preset (name + email + message, `stepType: "email_request"`)
287
+ - `mountWhatsApp(el, { leadbotId, id, phoneNumber?, prefilledMessage?, ... })` — records the interaction, then opens WhatsApp
288
+ - `mountButtons(el, { leadbotId, id, buttons?, eventLabel?, onSelect?, className? })` — renders a published step's options as buttons; an option with a `next_step_uuid` opens the page widget at that step and sends its label as the first message; an explicit `buttons` array overrides the published options. A button may set `children` (any markup — icons etc.) to render instead of its `label`; the label still identifies the button for tracking and `onSelect`
289
+ - `mountFlow(el, { leadbotId?, typingIndicator?, ... })` — renders a full leadbot flow (a standard bot marked "served via SDK") inline in your page, with no widget chrome
290
+ - `openWidget(params?: { stepUuid?, message? })` / `closeWidget()` / `toggleWidget()` — control an embedded LeadBot widget on the same page; passing both `stepUuid` and `message` navigates to that step and sends the message
291
+ - `clearFlowCache()` — drops the page-lifetime boot-fetch cache, so the next mount (or an SPA route change) refetches the published flow
292
+
293
+ Full documentation: [docs/SDK.md](../../docs/SDK.md) in the repo (published docs mirror it).
294
+
295
+ ## Embedded flows
296
+
297
+ A standard leadbot flow — built in the dashboard and marked **Settings → Serve via SDK** — renders inline anywhere on your page via `mountFlow()`. It's the full conversation flow (all steps, routing, state) with no widget chrome: no floating panel, toggle button, or fixed positioning. Your host element decides the size and placement.
298
+
299
+ ```ts
300
+ mountFlow(document.querySelector("#leadbot-flow")!, {
301
+ leadbotId: 18, // falls back to init() default
302
+ typingIndicator: true, // show typing dots and navigation delay (default)
303
+ });
304
+ ```
305
+
306
+ The bot ID can be passed explicitly or defaults to the `leadbotId` in `init()`. The boot fetch selects the bot by ID, counts the pageview, and respects the bot's **visible URLs** setting — if the page falls outside that scope, nothing renders.
307
+
308
+ Import `@leadbot/components/styles.css` for the default widget-like look (scoped to `.leadbot-flow`), or style the `.leadbot-*` markup yourself. The publication's dashboard base color applies automatically; pass `baseColor` to override it.
309
+
310
+ Conversation state restores across page navigations if the bot has **keep open on navigation** enabled (stored per leadbot in `sessionStorage`). On SPA route changes, a new pageview is billed and the conversation restarts unless the restore setting keeps it open.
311
+
312
+ Multiple `mountFlow()` calls with different `leadbotId`s can coexist on one page, each maintaining separate conversation state. A call returns `{ update(options), destroy() }` — `update()` re-renders in place and preserves conversation history, while `destroy()` cleans up. The classic LeadBot widget (via `window.LeadBot` or `script.js`) remains unaffected.
313
+
314
+ ## Upgrading to 0.5
315
+
316
+ 0.5 makes the published step the source of truth, which is a breaking change
317
+ for every component:
318
+
319
+ - **`id` is now required** on every mount and component. It is the uuid of the
320
+ published step, from the dashboard. Without it a component renders nothing.
321
+ - **`agentId` is gone** from `mountAgentChat` — pass the chat step's `id`
322
+ instead and the collector resolves the agent from it.
323
+ - **`phoneNumber` is now optional** on `mountWhatsApp`; omit it to use the
324
+ published step's number.
325
+ - **`init({ leadbotId })` is new.** Without it there is no boot fetch, so no
326
+ pageview is counted and no GA events fire on a first-ever visit. Pass it to
327
+ get the same tracking as the classic widget.
328
+
329
+ ```diff
330
+ - mountAgentChat(el, { leadbotId: 18, agentId: "agent-uuid" });
331
+ + mountAgentChat(el, { leadbotId: 18, id: "chat-step-uuid" });
332
+ ```
@@ -0,0 +1,45 @@
1
+ import { type ChatCitation } from "@leadbot/core";
2
+ import { type BaseMountOptions, type MountHandle } from "./mount";
3
+ import { type StepOptions } from "./useStep";
4
+ export interface AgentChatMessage {
5
+ role: "user" | "assistant";
6
+ content: string;
7
+ citations?: ChatCitation[];
8
+ }
9
+ export interface AgentChatLabels {
10
+ title: string;
11
+ status: string;
12
+ reset: string;
13
+ send: string;
14
+ menu: string;
15
+ thinking: string;
16
+ searching: string;
17
+ chatbot_error_default: string;
18
+ chatbot_error_network: string;
19
+ chatbot_error_invalid_request: string;
20
+ chatbot_error_step_not_found: string;
21
+ chatbot_error_agent_not_found: string;
22
+ chatbot_error_agent_not_configured: string;
23
+ chatbot_error_agent_unavailable: string;
24
+ chatbot_error_agent_no_response: string;
25
+ chatbot_error_agent_response_failed: string;
26
+ }
27
+ export declare const DEFAULT_CHAT_LABELS: AgentChatLabels;
28
+ export interface AgentChatOptions extends BaseMountOptions, StepOptions {
29
+ /** Id of the component leadbot this chat belongs to (dashboard → Settings → Components). */
30
+ leadbotId: number;
31
+ initialMessage?: string;
32
+ conversationStarters?: string[];
33
+ placeholder?: string;
34
+ labels?: Partial<AgentChatLabels>;
35
+ /** Snapshot label for the chat conversation in the inbox. */
36
+ eventLabel?: string;
37
+ /** Groups turns into one conversation. Defaults to a UUID per mount. */
38
+ chatSessionId?: string;
39
+ /** Called for every completed user and assistant message. */
40
+ onMessage?: (message: AgentChatMessage) => void;
41
+ onError?: (error: unknown) => void;
42
+ }
43
+ export declare function AgentChat(props: AgentChatOptions): import("preact").JSX.Element | null;
44
+ export declare function mountAgentChat(el: HTMLElement, options: AgentChatOptions): MountHandle<AgentChatOptions>;
45
+ //# sourceMappingURL=agentChat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentChat.d.ts","sourceRoot":"","sources":["../src/agentChat.tsx"],"names":[],"mappings":"AACA,OAAO,EAEN,KAAK,YAAY,EASjB,MAAM,eAAe,CAAC;AACvB,OAAO,EACN,KAAK,gBAAgB,EAGrB,KAAK,WAAW,EAGhB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,KAAK,WAAW,EAAW,MAAM,WAAW,CAAC;AAEtD,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,6BAA6B,EAAE,MAAM,CAAC;IACtC,4BAA4B,EAAE,MAAM,CAAC;IACrC,6BAA6B,EAAE,MAAM,CAAC;IACtC,kCAAkC,EAAE,MAAM,CAAC;IAC3C,+BAA+B,EAAE,MAAM,CAAC;IACxC,+BAA+B,EAAE,MAAM,CAAC;IACxC,mCAAmC,EAAE,MAAM,CAAC;CAC5C;AAED,eAAO,MAAM,mBAAmB,EAAE,eAyBjC,CAAC;AAEF,MAAM,WAAW,gBAAiB,SAAQ,gBAAgB,EAAE,WAAW;IACtE,4FAA4F;IAC5F,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAClC,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACnC;AAwCD,wBAAgB,SAAS,CAAC,KAAK,EAAE,gBAAgB,uCA4ahD;AAED,wBAAgB,cAAc,CAC7B,EAAE,EAAE,WAAW,EACf,OAAO,EAAE,gBAAgB,GACvB,WAAW,CAAC,gBAAgB,CAAC,CAE/B"}
@@ -0,0 +1,42 @@
1
+ import type { ComponentChildren } from "preact";
2
+ import { type BaseMountOptions, type MountHandle } from "./mount";
3
+ import { type StepOptions } from "./useStep";
4
+ export type ComponentsButtonAction = {
5
+ type: "open_widget";
6
+ stepUuid?: string;
7
+ message?: string;
8
+ } | {
9
+ type: "url";
10
+ href: string;
11
+ target?: "_blank";
12
+ };
13
+ export interface ComponentsButton {
14
+ /** Identifies the button (key, onSelect payload, click handlers). */
15
+ label: string;
16
+ /** Rendered inside the <button> instead of the label — any markup (icons
17
+ * etc.). The label still identifies the button. */
18
+ children?: ComponentChildren;
19
+ action?: ComponentsButtonAction;
20
+ }
21
+ export interface ButtonsOptions extends BaseMountOptions, Omit<StepOptions, "id"> {
22
+ /** Uuid of the published step behind these buttons. Optional: a fully
23
+ * explicit `buttons` list needs no published step, but clicks are only
24
+ * tracked against the collector when an id is set. */
25
+ id?: string;
26
+ /** Id of the component leadbot this component belongs to (dashboard →
27
+ * Settings → Components). Optional so a provider/init() config can supply it. */
28
+ leadbotId?: number;
29
+ /** Omit to render the published step behind `id`: a button step becomes a
30
+ * single button (with its dashboard-configured widget link), any other
31
+ * step renders its options. */
32
+ buttons?: ComponentsButton[];
33
+ /** Snapshot label for the click event sent to the collector/GA. */
34
+ eventLabel?: string;
35
+ onSelect?: (button: ComponentsButton) => void;
36
+ /** Reports each button's element (null on unmount) so a host framework
37
+ * (e.g. @leadbot/react) can portal its own rich children into it. */
38
+ onButtonEl?: (label: string, el: HTMLButtonElement | null) => void;
39
+ }
40
+ export declare function ComponentsButtons(props: ButtonsOptions): import("preact").JSX.Element | null;
41
+ export declare function mountButtons(el: HTMLElement, options: ButtonsOptions): MountHandle<ButtonsOptions>;
42
+ //# sourceMappingURL=buttons.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buttons.d.ts","sourceRoot":"","sources":["../src/buttons.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAQhD,OAAO,EACN,KAAK,gBAAgB,EAErB,KAAK,WAAW,EAGhB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAsB,KAAK,WAAW,EAAW,MAAM,WAAW,CAAC;AAE1E,MAAM,MAAM,sBAAsB,GAC/B;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEpD,MAAM,WAAW,gBAAgB;IAChC,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd;uDACmD;IACnD,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,MAAM,CAAC,EAAE,sBAAsB,CAAC;CAChC;AAED,MAAM,WAAW,cAChB,SAAQ,gBAAgB,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC;IACjD;;0DAEsD;IACtD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ;qFACiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;mCAE+B;IAC/B,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC7B,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC9C;yEACqE;IACrE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,iBAAiB,GAAG,IAAI,KAAK,IAAI,CAAC;CACnE;AA0DD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,uCA2EtD;AAED,wBAAgB,YAAY,CAC3B,EAAE,EAAE,WAAW,EACf,OAAO,EAAE,cAAc,GACrB,WAAW,CAAC,cAAc,CAAC,CAE7B"}
package/dist/flow.d.ts ADDED
@@ -0,0 +1,41 @@
1
+ import { type BaseMountOptions, type MountHandle } from "./mount";
2
+ export interface FlowOptions extends BaseMountOptions {
3
+ /**
4
+ * Id of the served-via-SDK leadbot whose flow to render
5
+ * (dashboard → Settings → Serve via SDK). Falls back to the `init()`
6
+ * default.
7
+ */
8
+ leadbotId?: number;
9
+ /**
10
+ * Show the typing dots and the short delay between interactive steps,
11
+ * like the widget. Default true.
12
+ */
13
+ typingIndicator?: boolean;
14
+ /** Layout of the embedded conversation. See {@link FlowLayout}. */
15
+ layout?: FlowLayout;
16
+ }
17
+ /**
18
+ * Layout of an embedded flow, as CSS custom properties on the mount container
19
+ * — so a host (the dashboard's layout toolbar included) never has to target the
20
+ * SDK's internal `.leadbot-*` class names, which are free to be renamed.
21
+ */
22
+ export interface FlowLayout {
23
+ /** Width cap on cards, option buttons and forms. Default `"320px"`. */
24
+ maxWidth?: string;
25
+ /**
26
+ * Horizontal alignment of the conversation. Default `"flex-start"`;
27
+ * `"stretch"` makes cards fill the container up to `maxWidth`.
28
+ */
29
+ align?: "flex-start" | "center" | "flex-end" | "stretch";
30
+ }
31
+ /** Marks the mount root; the flow rules in styles.css are scoped to it. */
32
+ export declare const FLOW_CLASS = "leadbot-flow";
33
+ /**
34
+ * Render a full leadbot flow (a standard bot marked "served via SDK") inline
35
+ * in `el`. The boot fetch selects the bot by id, bills the pageview and
36
+ * honours the bot's visible-URLs scoping — off-path, nothing renders. Import
37
+ * `@leadbot/components/styles.css` for the default look, or style the
38
+ * `.leadbot-*` markup yourself.
39
+ */
40
+ export declare function mountFlow(el: HTMLElement, options?: FlowOptions): MountHandle<FlowOptions>;
41
+ //# sourceMappingURL=flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flow.d.ts","sourceRoot":"","sources":["../src/flow.ts"],"names":[],"mappings":"AAIA,OAAO,EACN,KAAK,gBAAgB,EAGrB,KAAK,WAAW,EAEhB,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,WAAY,SAAQ,gBAAgB;IACpD;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,mEAAmE;IACnE,MAAM,CAAC,EAAE,UAAU,CAAC;CACpB;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IAC1B,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,KAAK,CAAC,EAAE,YAAY,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;CACzD;AAYD,2EAA2E;AAC3E,eAAO,MAAM,UAAU,iBAAiB,CAAC;AAmFzC;;;;;;GAMG;AACH,wBAAgB,SAAS,CACxB,EAAE,EAAE,WAAW,EACf,OAAO,GAAE,WAAgB,GACvB,WAAW,CAAC,WAAW,CAAC,CAsB1B"}
package/dist/form.d.ts ADDED
@@ -0,0 +1,63 @@
1
+ import { type FormField } from "@leadbot/core";
2
+ import { type BaseMountOptions, type MountHandle } from "./mount";
3
+ import { type StepOptions } from "./useStep";
4
+ export interface ComponentsFormField {
5
+ /** Key used for the user_data entry and server field errors. */
6
+ name: string;
7
+ /** Set for fields coming from a published form; user_data is keyed by it. */
8
+ uuid?: string;
9
+ label: string;
10
+ /** Accepts the widget's loose field types; normalized via resolveFieldType. */
11
+ type: FormField["type"];
12
+ required?: boolean;
13
+ options?: FormField["options"];
14
+ placeholder?: FormField["placeholder"];
15
+ }
16
+ export interface FormLabels {
17
+ submitError: string;
18
+ required: string;
19
+ invalidEmail: string;
20
+ invalidPhone: string;
21
+ invalidNumber: string;
22
+ selectPlaceholder: string;
23
+ }
24
+ export declare const DEFAULT_FORM_LABELS: FormLabels;
25
+ export type ComponentsStepType = "form_submit" | "callback_request" | "email_request" | "whatsapp";
26
+ export interface FormOptions extends BaseMountOptions, StepOptions {
27
+ /** Id of the component leadbot this form belongs to (dashboard → Settings → Components). */
28
+ leadbotId: number;
29
+ /** Omit to render the fields of the published form behind `id`. */
30
+ fields?: ComponentsFormField[];
31
+ /**
32
+ * @internal Set by the preset mounters below; not part of the public API.
33
+ * Used only when neither the props nor the published step supply a value.
34
+ */
35
+ defaults?: Pick<FormOptions, "fields" | "submitLabel" | "successMessage">;
36
+ /** Interaction type sent to the collector. Defaults to "form_submit". */
37
+ stepType?: ComponentsStepType;
38
+ submitLabel?: string;
39
+ successMessage?: string;
40
+ /**
41
+ * "inline" puts the submit button on the same row as the last field (the
42
+ * usual lead-capture layout); every earlier field spans the full width.
43
+ * Pairing is positional — reorder the fields and the button follows.
44
+ */
45
+ layout?: "stacked" | "inline";
46
+ /** Render labels for screen readers only; the label becomes the placeholder. */
47
+ hideLabels?: boolean;
48
+ /** Snapshot label (max 100 chars) shown in the inbox for this submission. */
49
+ eventLabel?: string;
50
+ marksAsQualified?: boolean;
51
+ /** Return false (or a promise of false) to cancel the submission. */
52
+ onSubmit?: (data: Record<string, unknown>) => boolean | void | Promise<boolean | void>;
53
+ onSuccess?: (data: Record<string, unknown>) => void;
54
+ onError?: (error: unknown) => void;
55
+ labels?: Partial<FormLabels>;
56
+ }
57
+ export declare function ComponentsForm(props: FormOptions): import("preact").JSX.Element | null;
58
+ export declare function mountForm(el: HTMLElement, options: FormOptions): MountHandle<FormOptions>;
59
+ /** Presets: same options as mountForm but with default fields per step type. */
60
+ export type FormPresetOptions = Omit<FormOptions, "stepType" | "defaults">;
61
+ export declare function mountCallbackRequest(el: HTMLElement, options: FormPresetOptions): MountHandle<FormOptions>;
62
+ export declare function mountEmailRequest(el: HTMLElement, options: FormPresetOptions): MountHandle<FormOptions>;
63
+ //# sourceMappingURL=form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../src/form.tsx"],"names":[],"mappings":"AACA,OAAO,EAIN,KAAK,SAAS,EAOd,MAAM,eAAe,CAAC;AACvB,OAAO,EACN,KAAK,gBAAgB,EAGrB,KAAK,WAAW,EAGhB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,KAAK,WAAW,EAAW,MAAM,WAAW,CAAC;AAEtD,MAAM,WAAW,mBAAmB;IACnC,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC/B,WAAW,CAAC,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,UAAU;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,mBAAmB,EAAE,UAOjC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAC3B,aAAa,GACb,kBAAkB,GAClB,eAAe,GACf,UAAU,CAAC;AAWd,MAAM,WAAW,WAAY,SAAQ,gBAAgB,EAAE,WAAW;IACjE,4FAA4F;IAC5F,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,MAAM,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,GAAG,aAAa,GAAG,gBAAgB,CAAC,CAAC;IAC1E,yEAAyE;IACzE,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC9B,gFAAgF;IAChF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,CACV,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACzB,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAC9C,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACpD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CAC7B;AA+OD,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,uCAyMhD;AAED,wBAAgB,SAAS,CACxB,EAAE,EAAE,WAAW,EACf,OAAO,EAAE,WAAW,GAClB,WAAW,CAAC,WAAW,CAAC,CAE1B;AAED,gFAAgF;AAChF,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,UAAU,CAAC,CAAC;AAa3E,wBAAgB,oBAAoB,CACnC,EAAE,EAAE,WAAW,EACf,OAAO,EAAE,iBAAiB,GACxB,WAAW,CAAC,WAAW,CAAC,CAU1B;AAED,wBAAgB,iBAAiB,CAChC,EAAE,EAAE,WAAW,EACf,OAAO,EAAE,iBAAiB,GACxB,WAAW,CAAC,WAAW,CAAC,CAS1B"}
@@ -0,0 +1,17 @@
1
+ export { buildAnalytics, init, loadFlow, ROOT_CLASS } from "./mount";
2
+ export type { BaseMountOptions, LeadbotComponentsConfig, MountHandle, } from "./mount";
3
+ export { useStep } from "./useStep";
4
+ export type { PublishedStep, StepOptions } from "./useStep";
5
+ export { AgentChat, DEFAULT_CHAT_LABELS, mountAgentChat } from "./agentChat";
6
+ export type { AgentChatLabels, AgentChatMessage, AgentChatOptions, } from "./agentChat";
7
+ export { DEFAULT_FORM_LABELS, ComponentsForm, mountCallbackRequest, mountEmailRequest, mountForm, } from "./form";
8
+ export type { FormLabels, FormOptions, FormPresetOptions, ComponentsFormField, ComponentsStepType, } from "./form";
9
+ export { DEFAULT_WHATSAPP_LABELS, ComponentsWhatsApp, mountWhatsApp, } from "./whatsapp";
10
+ export type { WhatsAppLabels, WhatsAppOptions } from "./whatsapp";
11
+ export { FLOW_CLASS, mountFlow } from "./flow";
12
+ export type { FlowLayout, FlowOptions } from "./flow";
13
+ export { ComponentsButtons, mountButtons } from "./buttons";
14
+ export type { ButtonsOptions, ComponentsButton, ComponentsButtonAction, } from "./buttons";
15
+ export { clearFlowCache, closeWidget, getActiveChatSession, getTracker, openWidget, sendChatFeedback, toggleWidget, trackClick, trackPageviewUrlChange, } from "@leadbot/core";
16
+ export type { GoogleAnalyticsTracker, OpenWidgetParams } from "@leadbot/core";
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrE,YAAY,EACX,gBAAgB,EAChB,uBAAuB,EACvB,WAAW,GACX,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE5D,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7E,YAAY,EACX,eAAe,EACf,gBAAgB,EAChB,gBAAgB,GAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,iBAAiB,EACjB,SAAS,GACT,MAAM,QAAQ,CAAC;AAChB,YAAY,EACX,UAAU,EACV,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,GAClB,MAAM,QAAQ,CAAC;AAEhB,OAAO,EACN,uBAAuB,EACvB,kBAAkB,EAClB,aAAa,GACb,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC/C,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAEtD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC5D,YAAY,EACX,cAAc,EACd,gBAAgB,EAChB,sBAAsB,GACtB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACN,cAAc,EACd,WAAW,EACX,oBAAoB,EACpB,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACV,sBAAsB,GACtB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC"}