@sales-bot-llm/sdk 0.2.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.
Files changed (54) hide show
  1. package/biome.json +36 -0
  2. package/docs/superpowers/plans/2026-05-08-sales-bot-sdk-plan.md +258 -0
  3. package/docs/superpowers/plans/2026-05-11-w3-sales-tool-polish-plan.md +476 -0
  4. package/docs/superpowers/specs/2026-05-08-sales-bot-sdk-design.md +587 -0
  5. package/example/.env.example +5 -0
  6. package/example/README.md +90 -0
  7. package/example/index.html +12 -0
  8. package/example/package.json +27 -0
  9. package/example/public/vanilla.global.js +345 -0
  10. package/example/src/App.tsx +50 -0
  11. package/example/src/main.tsx +16 -0
  12. package/example/src/routes/HookDemo.tsx +174 -0
  13. package/example/src/routes/VanillaDemo.tsx +67 -0
  14. package/example/src/routes/WidgetDemo.tsx +55 -0
  15. package/example/src/styles.css +18 -0
  16. package/example/tsconfig.json +19 -0
  17. package/example/tsconfig.tsbuildinfo +1 -0
  18. package/example/vite.config.ts +4 -0
  19. package/package.json +106 -0
  20. package/pnpm-workspace.yaml +3 -0
  21. package/src/core/client.ts +245 -0
  22. package/src/core/conversation.ts +34 -0
  23. package/src/core/index.ts +6 -0
  24. package/src/core/sse-parser.ts +87 -0
  25. package/src/core/storage.ts +72 -0
  26. package/src/core/transport.ts +271 -0
  27. package/src/core/types.ts +314 -0
  28. package/src/core/visitor.ts +21 -0
  29. package/src/react/index.ts +2 -0
  30. package/src/react/use-sales-bot.tsx +182 -0
  31. package/src/vanilla/index.ts +38 -0
  32. package/src/vue/index.ts +2 -0
  33. package/src/vue/use-sales-bot.ts +152 -0
  34. package/src/widget/index.ts +3 -0
  35. package/src/widget/markdown.ts +69 -0
  36. package/src/widget/styles.ts +350 -0
  37. package/src/widget/widget.ts +442 -0
  38. package/tests/contract/wire-format.test.ts +158 -0
  39. package/tests/core/client.test.ts +292 -0
  40. package/tests/core/conversation.test.ts +41 -0
  41. package/tests/core/sse-parser.test.ts +142 -0
  42. package/tests/core/storage.test.ts +78 -0
  43. package/tests/core/transport.test.ts +204 -0
  44. package/tests/core/visitor.test.ts +42 -0
  45. package/tests/react/use-sales-bot.test.tsx +188 -0
  46. package/tests/sales-tool-discriminator.test.ts +45 -0
  47. package/tests/setup.ts +3 -0
  48. package/tests/vanilla/vanilla.test.ts +37 -0
  49. package/tests/vue/use-sales-bot.test.ts +163 -0
  50. package/tests/widget/markdown.test.ts +113 -0
  51. package/tests/widget/widget.test.ts +388 -0
  52. package/tsconfig.json +28 -0
  53. package/tsup.config.ts +38 -0
  54. package/vitest.config.ts +26 -0
@@ -0,0 +1,350 @@
1
+ /**
2
+ * CSS injected into the shadow root.
3
+ *
4
+ * Every visual property is driven by a CSS custom property on `:host`, so
5
+ * integrators can override anything through the typed `theme` option (which
6
+ * sets these vars at runtime on the shadow host's inline style) or through
7
+ * the raw-CSS escape hatch `customCss`.
8
+ *
9
+ * Class names (sb-launcher, sb-panel, …) are intentionally exposed for
10
+ * `customCss` selectors, but treat them as v1 surface — they may change.
11
+ */
12
+ export const WIDGET_CSS = `
13
+ :host {
14
+ /* ─── Colors ─────────────────────────────────────────────────────────── */
15
+ --sb-primary: #2563eb;
16
+ --sb-primary-hover: #1d4ed8;
17
+ --sb-primary-text: #ffffff;
18
+ --sb-text: #111827;
19
+ --sb-text-light: #6b7280;
20
+ --sb-bg: #ffffff;
21
+ --sb-bg-user: var(--sb-primary);
22
+ --sb-bg-assistant: #f3f4f6;
23
+ --sb-bg-error: #fef2f2;
24
+ --sb-text-error: #dc2626;
25
+ --sb-border: #e5e7eb;
26
+ --sb-focus-ring: rgba(37,99,235,0.4);
27
+
28
+ /* ─── Layout & sizing ────────────────────────────────────────────────── */
29
+ --sb-radius: 12px;
30
+ --sb-message-radius: 16px;
31
+ --sb-input-radius: 20px;
32
+ --sb-z: 9999;
33
+ --sb-launcher-size: 56px;
34
+ --sb-send-size: 38px;
35
+ --sb-panel-width: 380px;
36
+ --sb-panel-height: 560px;
37
+ --sb-panel-max-height: 80vh;
38
+ --sb-bottom-offset: 24px;
39
+ --sb-side-offset: 24px;
40
+ --sb-panel-gap: 12px;
41
+
42
+ /* ─── Typography ─────────────────────────────────────────────────────── */
43
+ --sb-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
44
+ --sb-font-size: 14px;
45
+ --sb-font-size-header: 15px;
46
+ --sb-font-size-small: 13px;
47
+
48
+ /* ─── Effects ────────────────────────────────────────────────────────── */
49
+ --sb-shadow: 0 4px 24px rgba(0,0,0,0.15);
50
+ --sb-transition: 0.15s ease;
51
+
52
+ all: initial;
53
+ display: block;
54
+ position: fixed;
55
+ bottom: var(--sb-bottom-offset);
56
+ right: var(--sb-side-offset);
57
+ z-index: var(--sb-z);
58
+ font-family: var(--sb-font-family);
59
+ }
60
+
61
+ :host(.sb-position--bottom-left) {
62
+ right: auto;
63
+ left: var(--sb-side-offset);
64
+ }
65
+
66
+ *, *::before, *::after {
67
+ box-sizing: border-box;
68
+ }
69
+
70
+ /* Launcher button */
71
+ .sb-launcher {
72
+ width: var(--sb-launcher-size);
73
+ height: var(--sb-launcher-size);
74
+ border-radius: 50%;
75
+ background: var(--sb-primary);
76
+ border: none;
77
+ cursor: pointer;
78
+ display: flex;
79
+ align-items: center;
80
+ justify-content: center;
81
+ box-shadow: var(--sb-shadow);
82
+ transition: background var(--sb-transition), transform var(--sb-transition);
83
+ color: var(--sb-primary-text);
84
+ font-size: 24px;
85
+ outline: none;
86
+ }
87
+
88
+ .sb-launcher:hover {
89
+ background: var(--sb-primary-hover);
90
+ transform: scale(1.05);
91
+ }
92
+
93
+ .sb-launcher:focus-visible {
94
+ box-shadow: 0 0 0 3px var(--sb-focus-ring);
95
+ }
96
+
97
+ /* Panel */
98
+ .sb-panel {
99
+ display: none;
100
+ position: absolute;
101
+ bottom: calc(var(--sb-launcher-size) + var(--sb-panel-gap));
102
+ right: 0;
103
+ width: var(--sb-panel-width);
104
+ height: var(--sb-panel-height);
105
+ max-height: var(--sb-panel-max-height);
106
+ background: var(--sb-bg);
107
+ border-radius: var(--sb-radius);
108
+ box-shadow: var(--sb-shadow);
109
+ border: 1px solid var(--sb-border);
110
+ flex-direction: column;
111
+ overflow: hidden;
112
+ }
113
+
114
+ :host(.sb-position--bottom-left) .sb-panel {
115
+ right: auto;
116
+ left: 0;
117
+ }
118
+
119
+ .sb-panel--open {
120
+ display: flex !important;
121
+ }
122
+
123
+ /* Header */
124
+ .sb-header {
125
+ padding: 16px 20px;
126
+ background: var(--sb-primary);
127
+ color: var(--sb-primary-text);
128
+ display: flex;
129
+ align-items: center;
130
+ justify-content: space-between;
131
+ flex-shrink: 0;
132
+ }
133
+
134
+ .sb-header-title {
135
+ font-weight: 600;
136
+ font-size: var(--sb-font-size-header);
137
+ margin: 0;
138
+ }
139
+
140
+ .sb-header-actions {
141
+ display: flex;
142
+ align-items: center;
143
+ gap: 4px;
144
+ }
145
+
146
+ .sb-close-btn,
147
+ .sb-new-chat-btn {
148
+ background: none;
149
+ border: none;
150
+ color: var(--sb-primary-text);
151
+ cursor: pointer;
152
+ padding: 6px;
153
+ line-height: 1;
154
+ opacity: 0.8;
155
+ transition: opacity var(--sb-transition), background var(--sb-transition);
156
+ border-radius: 6px;
157
+ display: inline-flex;
158
+ align-items: center;
159
+ justify-content: center;
160
+ }
161
+
162
+ .sb-close-btn { font-size: 20px; padding: 0 6px; }
163
+
164
+ .sb-close-btn:hover,
165
+ .sb-new-chat-btn:hover {
166
+ opacity: 1;
167
+ background: rgba(255, 255, 255, 0.15);
168
+ }
169
+
170
+ /* Messages */
171
+ .sb-messages {
172
+ flex: 1;
173
+ overflow-y: auto;
174
+ padding: 16px;
175
+ display: flex;
176
+ flex-direction: column;
177
+ gap: 10px;
178
+ }
179
+
180
+ .sb-message {
181
+ max-width: 80%;
182
+ padding: 10px 14px;
183
+ border-radius: var(--sb-message-radius);
184
+ font-size: var(--sb-font-size);
185
+ line-height: 1.5;
186
+ word-wrap: break-word;
187
+ }
188
+
189
+ .sb-message--user {
190
+ align-self: flex-end;
191
+ background: var(--sb-bg-user);
192
+ color: var(--sb-primary-text);
193
+ border-bottom-right-radius: 4px;
194
+ }
195
+
196
+ .sb-message--assistant {
197
+ align-self: flex-start;
198
+ background: var(--sb-bg-assistant);
199
+ color: var(--sb-text);
200
+ border-bottom-left-radius: 4px;
201
+ }
202
+
203
+ .sb-message--error {
204
+ align-self: center;
205
+ background: var(--sb-bg-error);
206
+ color: var(--sb-text-error);
207
+ border: 1px solid var(--sb-text-error);
208
+ font-size: var(--sb-font-size-small);
209
+ }
210
+
211
+ /* Typing indicator */
212
+ .sb-thinking {
213
+ display: flex;
214
+ gap: 4px;
215
+ align-items: center;
216
+ padding: 12px 14px;
217
+ }
218
+
219
+ .sb-thinking span {
220
+ width: 8px;
221
+ height: 8px;
222
+ border-radius: 50%;
223
+ background: var(--sb-text-light);
224
+ animation: sb-bounce 1.2s infinite;
225
+ }
226
+
227
+ .sb-thinking span:nth-child(2) { animation-delay: 0.2s; }
228
+ .sb-thinking span:nth-child(3) { animation-delay: 0.4s; }
229
+
230
+ @keyframes sb-bounce {
231
+ 0%, 80%, 100% { transform: scale(0.8); opacity: 0.5; }
232
+ 40% { transform: scale(1); opacity: 1; }
233
+ }
234
+
235
+ /* Input row */
236
+ .sb-input-row {
237
+ padding: 12px 16px;
238
+ border-top: 1px solid var(--sb-border);
239
+ display: flex;
240
+ gap: 8px;
241
+ align-items: flex-end;
242
+ flex-shrink: 0;
243
+ }
244
+
245
+ .sb-input {
246
+ flex: 1;
247
+ padding: 10px 14px;
248
+ border: 1px solid var(--sb-border);
249
+ border-radius: var(--sb-input-radius);
250
+ font-size: var(--sb-font-size);
251
+ font-family: inherit;
252
+ outline: none;
253
+ resize: none;
254
+ line-height: 1.4;
255
+ max-height: 100px;
256
+ background: var(--sb-bg);
257
+ color: var(--sb-text);
258
+ transition: border-color var(--sb-transition);
259
+ }
260
+
261
+ .sb-input:focus {
262
+ border-color: var(--sb-primary);
263
+ }
264
+
265
+ .sb-input::placeholder {
266
+ color: var(--sb-text-light);
267
+ }
268
+
269
+ .sb-send {
270
+ width: var(--sb-send-size);
271
+ height: var(--sb-send-size);
272
+ border-radius: 50%;
273
+ background: var(--sb-primary);
274
+ border: none;
275
+ cursor: pointer;
276
+ display: flex;
277
+ align-items: center;
278
+ justify-content: center;
279
+ color: var(--sb-primary-text);
280
+ font-size: 16px;
281
+ flex-shrink: 0;
282
+ transition: background var(--sb-transition);
283
+ }
284
+
285
+ .sb-send:hover { background: var(--sb-primary-hover); }
286
+ .sb-send:disabled { opacity: 0.5; cursor: not-allowed; }
287
+
288
+ /* Markdown content */
289
+ .sb-message strong { font-weight: 700; }
290
+ .sb-message em { font-style: italic; }
291
+ .sb-message code {
292
+ font-family: 'SFMono-Regular', Consolas, monospace;
293
+ background: rgba(0,0,0,0.08);
294
+ padding: 1px 4px;
295
+ border-radius: 3px;
296
+ font-size: 0.9em;
297
+ }
298
+ .sb-message--user code {
299
+ background: rgba(255,255,255,0.2);
300
+ }
301
+ .sb-message a {
302
+ color: var(--sb-primary);
303
+ text-decoration: underline;
304
+ }
305
+ .sb-message--user a {
306
+ color: rgba(255,255,255,0.9);
307
+ }
308
+ .sb-message p {
309
+ margin: 0;
310
+ }
311
+ .sb-message p + p {
312
+ margin-top: 0.6em;
313
+ }
314
+ .sb-message ul,
315
+ .sb-message ol {
316
+ margin: 0.4em 0 0.4em 0;
317
+ padding-left: 1.4em;
318
+ }
319
+ .sb-message li + li {
320
+ margin-top: 2px;
321
+ }
322
+ .sb-message--user ul,
323
+ .sb-message--user ol {
324
+ padding-left: 1.2em;
325
+ }
326
+
327
+ /* ─── Sales-workflow status pills ──────────────────────────────────────── */
328
+ .sb-sales-pill {
329
+ display: inline-flex;
330
+ align-self: flex-start;
331
+ align-items: center;
332
+ margin: 4px 0;
333
+ padding: 4px 10px;
334
+ border-radius: 999px;
335
+ background: var(--sb-pill-bg, rgba(0, 0, 0, 0.05));
336
+ color: var(--sb-pill-fg, rgba(0, 0, 0, 0.7));
337
+ font-size: 12px;
338
+ line-height: 1.3;
339
+ animation: sb-sales-pill-pulse 1.8s ease-in-out infinite;
340
+ }
341
+ .sb-sales-pill-error {
342
+ background: var(--sb-pill-error-bg, rgba(255, 69, 58, 0.12));
343
+ color: var(--sb-pill-error-fg, rgba(155, 28, 28, 0.95));
344
+ animation: none;
345
+ }
346
+ @keyframes sb-sales-pill-pulse {
347
+ 0%, 100% { opacity: 1; }
348
+ 50% { opacity: 0.55; }
349
+ }
350
+ `