Skip to content

Commit 2c8eeee

Browse files
dmitriyzhukclaude
andcommitted
feat(sdk): rich empty state with avatar, headline, suggestion chips (v0.11.0)
Replaces the lone "Start a conversation" placeholder with an onboarding panel — agent avatar, headline, sub-line, and clickable suggestion chips. Click a chip and it sends as a normal user message (equivalent to typing the same text and pressing Send). The whole block hides the moment the conversation begins, including while the agent is mid-stream — chips don't flash next to a typing indicator. API: four independent options on init() and as `data-*` attributes, all optional, all backwards-compatible (none set → legacy single-line copy stays): - emptyAvatar / data-empty-avatar - emptyTitle / data-empty-title - emptySubtitle / data-empty-subtitle - suggestions / data-suggestions (pipe-split string OR JSON array) Programmatic form accepts `string[]`; the data-* form accepts either a JSON array string or a `|`-separated string, which is easier to write in HTML without escaping. Same parser on both sides. Docs: - script-tag.md — 4 rows in the attribute table, full "Empty state" section with snippet, behavior, and when-not-to-use guidance. - New 06 · Empty state cookbook page with live <BridleEmbed> demo, combine-with-greeting-and-prompt recipe, and tips (chip length, 3–5 chip ceiling, avatar size). - 01 · Basic optional-knobs table picks up the four new attrs. - BridleEmbed accepts all four props so future pages can demo them. - Sidebar adds 06. HTML embed example: basic snippet shows all four data-* with comments; the live floating widget mounts with chips + greeting so a visitor sees the new onboarding flow on page load. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 85b991b commit 2c8eeee

10 files changed

Lines changed: 379 additions & 4 deletions

File tree

docs/docs/.vitepress/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export default defineConfig({
7878
{ text: '03 · Styles', link: '/examples/styles' },
7979
{ text: '04 · Authenticator', link: '/examples/authenticator' },
8080
{ text: '05 · Welcome message', link: '/examples/welcome' },
81+
{ text: '06 · Empty state', link: '/examples/empty-state' },
8182
],
8283
},
8384
],

docs/docs/.vitepress/theme/components/BridleEmbed.vue

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ const props = withDefaults(
2020
greeting?: string
2121
greetingDelay?: number
2222
fabIcon?: string
23+
emptyAvatar?: string
24+
emptyTitle?: string
25+
emptySubtitle?: string
26+
suggestions?: string[]
2327
}>(),
2428
{
2529
apiUrl: DEFAULT_API_URL,
@@ -58,6 +62,10 @@ onMounted(async () => {
5862
greeting: props.greeting,
5963
greetingDelay: props.greetingDelay,
6064
fabIcon: props.fabIcon,
65+
emptyAvatar: props.emptyAvatar,
66+
emptyTitle: props.emptyTitle,
67+
emptySubtitle: props.emptySubtitle,
68+
suggestions: props.suggestions,
6169
onError: (err: Error) => {
6270
error.value = err.message || String(err)
6371
},

docs/docs/embed/script-tag.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ When the script loads, it auto-registers a `<bridle-chat>` Custom Element and mo
3232
| `data-prompt` | optional | Free-form context string sent at handshake and forwarded to the agent on every message — page URL, user plan, locale, A/B cohort, etc. See [Page Context](/embed/context) |
3333
| `data-greeting` | optional | Welcome message shown on the first open of an empty chat, after a typing-indicator delay. Markdown supported. See [Welcome message](#welcome-message) |
3434
| `data-greeting-delay` | `3000` | Milliseconds of typing indicator before `data-greeting` appears. Set `0` to skip the delay |
35+
| `data-empty-avatar` | optional | URL of an avatar shown on the empty-state screen. See [Empty state](#empty-state) |
36+
| `data-empty-title` | optional | Headline shown on the empty-state screen, e.g. "What can I help with?" |
37+
| `data-empty-subtitle` | optional | Sub-line shown under `data-empty-title` |
38+
| `data-suggestions` | optional | Suggestion chips rendered on the empty state. Pipe-separated (`Q1|Q2|Q3`) or a JSON array string. Clicking a chip sends it as a user message |
3539

3640
## Choosing where to load the script from
3741

@@ -79,6 +83,47 @@ Mount the chat inside a specific container instead of floating:
7983

8084
In inline mode, the chat fills its container. Set the height/width on the parent via CSS.
8185

86+
## Empty state
87+
88+
The default empty chat shows a single muted "Start a conversation" line. Pass any of `emptyAvatar` / `emptyTitle` / `emptySubtitle` / `suggestions` and you get a richer onboarding panel — agent avatar, headline, sub-line, and pill-shaped suggestion chips. Click a chip and it sends that question as a regular user message; the empty state then disappears.
89+
90+
```html
91+
<script
92+
src="https://bridle.cleanslice.org/sdk/latest.js"
93+
data-agent-id="agent-abc-123"
94+
data-token="<jwt>"
95+
data-empty-avatar="/avatars/support-agent.png"
96+
data-empty-title="How can I help?"
97+
data-empty-subtitle="I can compare plans, schedule a demo, or open a ticket."
98+
data-suggestions="Compare plans|Book a demo|Open a support ticket"
99+
></script>
100+
```
101+
102+
Programmatic:
103+
104+
```ts
105+
init({
106+
apiUrl, agentId, token,
107+
emptyAvatar: '/avatars/support-agent.png',
108+
emptyTitle: 'How can I help?',
109+
emptySubtitle: 'I can compare plans, schedule a demo, or open a ticket.',
110+
suggestions: ['Compare plans', 'Book a demo', 'Open a support ticket'],
111+
})
112+
```
113+
114+
### Behavior
115+
116+
- Hidden as soon as `messages.length > 0` **or** the agent starts replying (`isTyping`). Suggestion chips do not flash next to a streaming reply.
117+
- Clicking a chip is **equivalent to typing the same text and pressing Send**`onMessage` fires for the user turn, the runtime sees it as any other message.
118+
- Chips are disabled while the WebSocket isn't open, so visitors don't fire questions into a closed connection.
119+
- All four fields are independent — pass only the ones you want. Provide nothing and the legacy "Start a conversation" line stays.
120+
121+
### When NOT to use it
122+
123+
- **Long replies as chips** — chip text becomes the entire user message. Keep them short prompts ("Pricing?"), not full sentences the visitor wouldn't have typed.
124+
- **A FAQ list** — chips are for *opening* the conversation, not a static knowledge base. After the first user turn they're gone.
125+
- **Forms / structured input** — chips send free text. For "pick a plan: Basic / Pro / Team" structured selection, the runtime should emit assistant messages with parts of type the renderer understands (not a feature of the SDK today).
126+
82127
## Welcome message
83128

84129
When the visitor opens the chat for the first time and there's no prior transcript, you can pre-seed an assistant bubble — typing dots appear for a moment, then the message fades in like the agent typed it.

docs/docs/examples/basic.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ Everything below works on the script tag with **zero extra integration code**
2121
|-----------|--------------|-----------|
2222
| `data-greeting` | Pre-seeds the first assistant bubble after a typing-indicator delay on an empty chat | [Welcome message](/examples/welcome) |
2323
| `data-greeting-delay` | Milliseconds before the greeting appears (default `3000`) ||
24+
| `data-empty-avatar` / `data-empty-title` / `data-empty-subtitle` | Avatar + headline + sub-line shown on the empty-state screen | [Empty state](/examples/empty-state) |
25+
| `data-suggestions` | Pipe-separated suggestion chips on the empty state — click to send ||
2426
| `data-prompt` | Free-form context forwarded to the agent on every message — page URL, plan, locale, A/B cohort | [Page Context](/embed/context) |
2527
| `data-fab-icon` | URL of an image to replace the FAB's built-in glyph | [Theming › Custom FAB icon](/embed/theming#custom-fab-icon) |
2628
| `data-custom-css` | Inline CSS injected into the shadow root to restyle internal classes | [Theming › Overriding internal classes](/embed/theming#overriding-internal-classes) |

docs/docs/examples/empty-state.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# 06 · Empty state with suggestions
2+
3+
Replace the default "Start a conversation" placeholder with an onboarding panel — agent avatar, headline, sub-line, and clickable suggestion chips. Click a chip and it sends as a normal user message; the whole block disappears the moment the conversation begins.
4+
5+
## Snippet
6+
7+
```html
8+
<script
9+
src="https://bridle.cleanslice.org/sdk/latest.js"
10+
data-agent-id="agent-abc-123"
11+
data-token="<jwt>"
12+
data-empty-avatar="/avatars/support-agent.png"
13+
data-empty-title="How can I help?"
14+
data-empty-subtitle="I can compare plans, schedule a demo, or open a ticket."
15+
data-suggestions="Compare plans|Book a demo|Open a support ticket"
16+
></script>
17+
```
18+
19+
Programmatic form:
20+
21+
```ts
22+
Bridle.init({
23+
apiUrl, agentId, token,
24+
emptyAvatar: '/avatars/support-agent.png',
25+
emptyTitle: 'How can I help?',
26+
emptySubtitle: 'I can compare plans, schedule a demo, or open a ticket.',
27+
suggestions: ['Compare plans', 'Book a demo', 'Open a support ticket'],
28+
})
29+
```
30+
31+
## Live demo
32+
33+
<BridleEmbed
34+
title="Welcome desk"
35+
empty-avatar="https://bridle.cleanslice.org/logo.svg"
36+
empty-title="How can I help?"
37+
empty-subtitle="Ask me anything about Bridle, or pick one of the prompts below."
38+
:suggestions="['How do I embed Bridle?', 'How does theming work?', 'What does data-prompt do?']"
39+
/>
40+
41+
Click a chip — it sends as a user message, the panel switches to the live conversation, and the empty state stays out of the way.
42+
43+
## Behavior
44+
45+
| Condition | Result |
46+
|-----------|--------|
47+
| `messages.length === 0` and not typing | Empty state visible |
48+
| Agent starts replying (`isTyping`) | Empty state hidden (chips don't flash next to a streaming reply) |
49+
| User sends anything — text, image, or a chip | Empty state hidden — conversation begins |
50+
| WebSocket not open | Chips disabled — visitors don't fire into a closed connection |
51+
| All four fields unset | Legacy single-line "Start a conversation" copy |
52+
53+
## Combine with `greeting` and `prompt`
54+
55+
| Field | Job |
56+
|-------|-----|
57+
| `emptyAvatar` / `emptyTitle` / `emptySubtitle` / `suggestions` | What the **visitor** sees in an empty chat. Static content. |
58+
| `greeting` / `greetingDelay` | What the **agent** "says" once typing dots clear. Static, one-shot per session. |
59+
| `prompt` | What the **agent runtime** receives at handshake. Hidden from the visitor. Drives the agent's reasoning. |
60+
61+
Used together, they tell a coherent story to both sides:
62+
63+
```ts
64+
Bridle.init({
65+
apiUrl, agentId, token,
66+
67+
// Visitor sees:
68+
emptyAvatar: '/avatars/support-agent.png',
69+
emptyTitle: 'How can I help?',
70+
emptySubtitle: 'Ask anything about your subscription.',
71+
suggestions: ['Upgrade my plan', 'See past invoices', 'Cancel my account'],
72+
73+
// Then the agent "introduces itself" once they open the panel:
74+
greeting: "Hi! I'm here for your subscription questions. Heads-up: cancellations are processed at the end of the billing cycle.",
75+
greetingDelay: 2000,
76+
77+
// Background context for the runtime (not shown to visitor):
78+
prompt: 'Section: account/billing · Plan: pro · Renews: 2026-07-12',
79+
})
80+
```
81+
82+
## Tips
83+
84+
- **Keep chip text short** — chip text becomes the entire user message. "Pricing?" not "Could you walk me through your pricing model?". Long chips become long user turns in the transcript.
85+
- **3–5 chips max** — more than that and the empty state grows tall enough to push the input below the fold on mobile.
86+
- **Avatar at ≥56px** — the panel renders it as a 56×56 circle. Smaller sources scale up and look fuzzy.
87+
- **Style the chip itself** via [`customCss`](/embed/theming#overriding-internal-classes) targeting `.bridle__suggestion` — see the recipe in [03 · Styles](/examples/styles#styling-image-attachments) (same approach).
88+
89+
## Related
90+
91+
- [Welcome message](/examples/welcome) — what the agent says.
92+
- [Page Context](/embed/context) — what the agent runtime knows.
93+
- [Script Tag → Empty state](/embed/script-tag#empty-state) — full reference.

example/index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ <h1 class="mt-2 text-4xl font-bold tracking-tight">Bridle integration examples</
2626
</p>
2727
<p class="mx-auto mt-3 max-w-xl text-xs text-slate-500">
2828
Latest:
29+
empty-state suggestions (v0.11.0) ·
2930
<code>data-greeting</code> (v0.10.0) ·
3031
image attachments — drag / paste / paperclip (v0.9.0) ·
3132
<code>data-fab-icon</code> (v0.8.2) ·
@@ -51,6 +52,12 @@ <h2 class="mt-1 text-2xl font-semibold">Basic — one <code>&lt;script&gt;</code
5152
data-agent-id="agent-31a6fbd1-…"
5253
data-token="&lt;jwt&gt;"
5354

55+
&lt;!-- v0.11.0: rich empty state with avatar, headline, suggestion chips --&gt;
56+
data-empty-avatar="/avatars/agent.png"
57+
data-empty-title="How can I help?"
58+
data-empty-subtitle="Ask anything, or pick a prompt below."
59+
data-suggestions="Compare plans|Book a demo|Open a support ticket"
60+
5461
&lt;!-- v0.10.0: pre-seed the first bubble on an empty chat --&gt;
5562
data-greeting="Hi! Drop a screenshot or ask me anything."
5663
data-greeting-delay="2500"
@@ -198,6 +205,12 @@ <h2 class="mt-1 text-2xl font-semibold">Authenticator — token as a function</h
198205
sdk.dataset.agentId = basic.agentId;
199206
sdk.dataset.token = basic.token;
200207
sdk.dataset.title = 'Chat';
208+
// v0.11.0 — rich empty state: visitor sees suggestion chips
209+
// before they type. Clicking a chip fires the prompt for them.
210+
sdk.dataset.emptyTitle = 'How can I help?';
211+
sdk.dataset.emptySubtitle = 'Ask anything, or pick a prompt below.';
212+
sdk.dataset.suggestions =
213+
'Compare plans|Book a demo|How does theming work?';
201214
// v0.10.0 — typing dots for 2.5s, then this message lands as the
202215
// first assistant bubble. Cancels if the user types first.
203216
sdk.dataset.greeting =

sdk/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@cleanslice/bridle",
3-
"version": "0.10.0",
3+
"version": "0.11.0",
44
"description": "Embeddable web chat for Bridle — drop-in <script> or programmatic init.",
55
"type": "module",
66
"main": "./dist/bridle.mjs",

0 commit comments

Comments
 (0)