@talk2view/sdk 0.18.1 → 0.19.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 (82) hide show
  1. package/README.md +63 -101
  2. package/dist/assistant-ui/index.d.ts.map +1 -1
  3. package/dist/assistant-ui/index.js +4 -2
  4. package/dist/assistant-ui/index.js.map +1 -1
  5. package/dist/chat/auth-gate.d.ts +3 -1
  6. package/dist/chat/auth-gate.d.ts.map +1 -1
  7. package/dist/chat/auth-gate.js +29 -5
  8. package/dist/chat/auth-gate.js.map +1 -1
  9. package/dist/chat/composer-extras.js +2 -2
  10. package/dist/chat/markdown.d.ts +14 -4
  11. package/dist/chat/markdown.d.ts.map +1 -1
  12. package/dist/chat/markdown.js +28 -1
  13. package/dist/chat/markdown.js.map +1 -1
  14. package/dist/chat/vendor/LICENSE +21 -0
  15. package/dist/chat/vendor/MANIFEST.json +84 -0
  16. package/dist/chat/vendor/attachment.aui.d.ts.map +1 -1
  17. package/dist/chat/vendor/attachment.aui.js +8 -0
  18. package/dist/chat/vendor/attachment.aui.js.map +1 -1
  19. package/dist/chat/vendor/file.d.ts.map +1 -1
  20. package/dist/chat/vendor/file.js +8 -0
  21. package/dist/chat/vendor/file.js.map +1 -1
  22. package/dist/chat/vendor/follow-up-suggestions.aui.d.ts.map +1 -1
  23. package/dist/chat/vendor/follow-up-suggestions.aui.js +8 -0
  24. package/dist/chat/vendor/follow-up-suggestions.aui.js.map +1 -1
  25. package/dist/chat/vendor/image.d.ts.map +1 -1
  26. package/dist/chat/vendor/image.js +8 -0
  27. package/dist/chat/vendor/image.js.map +1 -1
  28. package/dist/chat/vendor/markdown-text.d.ts.map +1 -1
  29. package/dist/chat/vendor/markdown-text.js +8 -0
  30. package/dist/chat/vendor/markdown-text.js.map +1 -1
  31. package/dist/chat/vendor/reasoning.aui.d.ts.map +1 -1
  32. package/dist/chat/vendor/reasoning.aui.js +8 -0
  33. package/dist/chat/vendor/reasoning.aui.js.map +1 -1
  34. package/dist/chat/vendor/reasoning.d.ts.map +1 -1
  35. package/dist/chat/vendor/reasoning.js +8 -0
  36. package/dist/chat/vendor/reasoning.js.map +1 -1
  37. package/dist/chat/vendor/thread-list.aui.d.ts.map +1 -1
  38. package/dist/chat/vendor/thread-list.aui.js +8 -0
  39. package/dist/chat/vendor/thread-list.aui.js.map +1 -1
  40. package/dist/chat/vendor/thread.aui.d.ts.map +1 -1
  41. package/dist/chat/vendor/thread.aui.js +8 -0
  42. package/dist/chat/vendor/thread.aui.js.map +1 -1
  43. package/dist/chat/vendor/tool-fallback.aui.d.ts.map +1 -1
  44. package/dist/chat/vendor/tool-fallback.aui.js +8 -0
  45. package/dist/chat/vendor/tool-fallback.aui.js.map +1 -1
  46. package/dist/chat/vendor/tool-group.aui.d.ts.map +1 -1
  47. package/dist/chat/vendor/tool-group.aui.js +8 -0
  48. package/dist/chat/vendor/tool-group.aui.js.map +1 -1
  49. package/dist/chat/vendor/tooltip-icon-button.d.ts.map +1 -1
  50. package/dist/chat/vendor/tooltip-icon-button.js +8 -0
  51. package/dist/chat/vendor/tooltip-icon-button.js.map +1 -1
  52. package/dist/chat/views/account.d.ts.map +1 -1
  53. package/dist/chat/views/account.js +6 -0
  54. package/dist/chat/views/account.js.map +1 -1
  55. package/dist/chat-css.js +1 -1
  56. package/dist/chat.css +1 -1
  57. package/dist/chat.isolated.css +7 -0
  58. package/dist/chat.raw.css +7 -0
  59. package/dist/client.d.ts.map +1 -1
  60. package/dist/client.js +49 -0
  61. package/dist/client.js.map +1 -1
  62. package/dist/index.d.ts +3 -0
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +11 -0
  65. package/dist/index.js.map +1 -1
  66. package/dist/streaming.d.ts.map +1 -1
  67. package/dist/streaming.js +5 -1
  68. package/dist/streaming.js.map +1 -1
  69. package/dist/tools.d.ts.map +1 -1
  70. package/dist/tools.js +13 -2
  71. package/dist/tools.js.map +1 -1
  72. package/dist/ui/components/MarkdownRenderer.d.ts +1 -1
  73. package/dist/ui/components/MarkdownRenderer.js +1 -1
  74. package/dist/ui/index.d.ts +10 -0
  75. package/dist/ui/index.d.ts.map +1 -1
  76. package/dist/ui/index.js +10 -0
  77. package/dist/ui/index.js.map +1 -1
  78. package/dist/ui/safeMarkdown.d.ts.map +1 -1
  79. package/dist/ui/safeMarkdown.js +4 -2
  80. package/dist/ui/safeMarkdown.js.map +1 -1
  81. package/docs/chat.md +532 -0
  82. package/package.json +20 -9
package/docs/chat.md ADDED
@@ -0,0 +1,532 @@
1
+ # The packaged chat — `@talk2view/sdk/chat`
2
+
3
+ The branded Talk2View chat, ready to render. Two components and one stylesheet.
4
+ Your app needs no Tailwind, no PostCSS, no shadcn and no copied files.
5
+
6
+ ```tsx
7
+ import { Talk2ViewChat } from '@talk2view/sdk/chat';
8
+ import '@talk2view/sdk/chat.css';
9
+
10
+ <Talk2ViewChat partnerKey="pk_live_…" tools={tools} />
11
+ ```
12
+
13
+ A runnable version of everything below, in a Vite app with no Tailwind, is in
14
+ [`examples/react-chat`](../examples/react-chat).
15
+
16
+ ---
17
+
18
+ ## Contents
19
+
20
+ - [The two components](#the-two-components)
21
+ - [Props](#props)
22
+ - [What you have to know](#what-you-have-to-know)
23
+ - [Theming](#theming)
24
+ - [Dark mode](#dark-mode)
25
+ - [The launcher in someone else's page](#the-launcher-in-someone-elses-page)
26
+ - [Content Security Policy, and hosts with no CSS loader](#content-security-policy-and-hosts-with-no-css-loader)
27
+ - [Next.js App Router](#nextjs-app-router)
28
+ - [Size](#size)
29
+ - [Errors you may see](#errors-you-may-see)
30
+ - [For contributors: the stylesheet and the vendored files](#for-contributors-the-stylesheet-and-the-vendored-files)
31
+
32
+ ---
33
+
34
+ ## The two components
35
+
36
+ ### `<Talk2ViewChat>`
37
+
38
+ Fills whatever box you put it in — a side panel, a task pane, a page section.
39
+ The parent needs a height: the chat is `height: 100%`, so a parent with `auto`
40
+ height collapses it to its content.
41
+
42
+ ```tsx
43
+ <div style={{ height: '100%' }}>
44
+ <Talk2ViewChat
45
+ partnerKey="pk_live_…"
46
+ tools={tools}
47
+ welcome={{ heading: 'How can I help?', suggestions: ['What can you do?'] }}
48
+ />
49
+ </div>
50
+ ```
51
+
52
+ A header of one-tap views sits over a thread that never unmounts, so switching
53
+ to Settings and back keeps the conversation, the scroll position and a
54
+ half-typed message.
55
+
56
+ The views are Account, Skills, Settings and Chats.
57
+
58
+ **Skills** is the end-user's own knowledge: markdown they write once — a
59
+ protocol, a house style, the way their team names things — that the agent can
60
+ load when it helps. They are kept on that device and sent with that person's
61
+ chats, where they merge with the partner's and the built-in skills and win over
62
+ both. A skill can be switched off without deleting it, and a `.md` file can be
63
+ imported, taking its `name` and `description` from frontmatter when it has any.
64
+ Turn the view off with `features={{ skills: false }}`; the underlying
65
+ `client.skills` API stays available either way.
66
+
67
+ ### `<Talk2ViewChatLauncher>`
68
+
69
+ The same chat in a floating panel: a mark in the corner of the page, a popover
70
+ on a desktop, a full-screen sheet on a phone. It takes every prop
71
+ `<Talk2ViewChat>` takes, plus the launcher's own.
72
+
73
+ ```tsx
74
+ <Talk2ViewChatLauncher
75
+ partnerKey="pk_live_…"
76
+ tools={tools}
77
+ label="Ask Talk2View"
78
+ colourway="smoke-teal"
79
+ keepClearOf="#cookie-banner"
80
+ />
81
+ ```
82
+
83
+ ### `useTalk2ViewChatClient()`
84
+
85
+ The client the chat is using, for your own UI — a sign-out item in your account
86
+ menu, a button that starts a conversation from somewhere else in your app. It
87
+ must be called inside the chat.
88
+
89
+ ```tsx
90
+ function SignOut() {
91
+ const t2v = useTalk2ViewChatClient();
92
+ return <button onClick={() => t2v.auth.logout()}>Sign out</button>;
93
+ }
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Props
99
+
100
+ Both components also accept everything `T2VConfig` accepts (`baseUrl`, `model`,
101
+ `debug`, `requestTimeout`, `anonymousAutoStart`) except `partnerKey`, which they
102
+ take directly.
103
+
104
+ | Prop | Default | What it does |
105
+ |---|---|---|
106
+ | `partnerKey` | — | Your key. Give this **or** `client`. |
107
+ | `client` | — | An existing `Talk2View` instance, shared with the rest of your app. The chat will not destroy a client you passed in. |
108
+ | `tools` | none | Your client tools. They run in your app. |
109
+ | `systemPrompt` | none | Your own instructions, sent with every message. Changing it does not restart the conversation. |
110
+ | `welcome` | `{}` | `heading` and `suggestions` on the empty chat. Clicking a suggestion sends it. |
111
+ | `allowAnonymous` | `true` | Logged-out visitors chat as guests. `false` shows sign-in first — **and see the note below**. |
112
+ | `features` | all on | `{ dictation, attachments, settings, account, threadList, skills }`. |
113
+ | `fontFamily` | inherit | Your own font stack. Default: whatever the page uses. |
114
+ | `resetPasswordUrl` | none | Where "Forgot it?" goes. Without it the link is not shown. |
115
+ | `footer` | none | A short line under the composer — your terms, a disclaimer. |
116
+ | `describeToolActivity` | none | `(name, args) => string \| null`. What a running tool is doing, in your words. |
117
+ | `isToolDestructive` | none | `(name, args) => boolean`. Warn before this call. |
118
+ | `destructiveWarning` | built-in | `(name, activity) => ReactNode`. Your own warning text. |
119
+ | `className` | none | On the chat's own element — **on the launcher, on the anchor**. |
120
+
121
+ Launcher only:
122
+
123
+ | Prop | Default | What it does |
124
+ |---|---|---|
125
+ | `label` | `'Ask Talk2View'` | The words beside the mark, until the visitor opens the chat once. `''` hides them; the mark and the tooltip stay. |
126
+ | `colourway` | `'smoke-teal'` | `'smoke-teal'`, `'teal-smoke'` or `'teal-white'`. |
127
+ | `visitorColourway` | `false` | Put a colour picker in Settings. |
128
+ | `keepClearOf` | none | A CSS selector for something pinned to the bottom of the page. |
129
+ | `sheetBelow` | `640` | Screen width in px at and below which the panel is a full-screen sheet. |
130
+
131
+ ---
132
+
133
+ ## What you have to know
134
+
135
+ These are the things that surprise people. None of them is a bug; all of them
136
+ have cost someone an afternoon.
137
+
138
+ ### `allowAnonymous: false` also stops guest sessions
139
+
140
+ It is not only a screen. The gate is computed from whether anyone is signed in,
141
+ and the client starts a guest session on demand — listing the models for
142
+ Settings would be enough — which would sign the visitor in and open the gate
143
+ from behind. So `allowAnonymous: false` also turns off `anonymousAutoStart`.
144
+
145
+ If you want a sign-in gate **and** guest sessions (there is no sensible reason
146
+ to), pass both explicitly:
147
+
148
+ ```tsx
149
+ <Talk2ViewChat partnerKey="…" allowAnonymous={false} anonymousAutoStart />
150
+ ```
151
+
152
+ ### A page with no font gets a serif chat
153
+
154
+ `--t2v-font` is `inherit`. That is the design: the chat looks like part of your
155
+ app, not like something bolted on. But a page that sets no font anywhere
156
+ inherits the browser's default, which is a serif — and so does the chat. Set a
157
+ font on `body`, or pass one:
158
+
159
+ ```tsx
160
+ <Talk2ViewChat partnerKey="…" fontFamily='"Space Grotesk", system-ui, sans-serif' />
161
+ ```
162
+
163
+ Nothing is fetched for it. The chat never loads a font, a stylesheet or a script
164
+ from a third-party host, so whatever you name has to be available to the page.
165
+
166
+ ### `isToolDestructive` is your claim about your own tool
167
+
168
+ Nothing checks it. The chat shows a warning above the approval buttons when you
169
+ say a call is destructive, and shows none when you do not — a tool that deletes
170
+ the end-user's work gets no warning unless you say so. Approval itself is a
171
+ different thing and is enforced: a tool declared `permission: true` is never run
172
+ without an answer.
173
+
174
+ ```tsx
175
+ <Talk2ViewChat
176
+ partnerKey="…"
177
+ tools={tools}
178
+ describeToolActivity={(name, args) =>
179
+ name === 'insert_text' ? `Inserting ${String(args?.text ?? '').length} characters` : null
180
+ }
181
+ isToolDestructive={(name) => name === 'insert_text' || name === 'delete_section'}
182
+ />
183
+ ```
184
+
185
+ ### Your theme override loses if your CSS loads before ours
186
+
187
+ The chat's own tokens are declared at `.t2v-chat`, and the override the theming
188
+ section below shows you is `.t2v-chat` too. Identical specificity, so the
189
+ winner is whichever stylesheet the browser read **last**. Import ours first and
190
+ yours wins, which is what the usual shape does:
191
+
192
+ ```ts
193
+ import '@talk2view/sdk/chat.css'; // ours
194
+ import './app.css'; // yours, and it wins
195
+ ```
196
+
197
+ Load them the other way — a global stylesheet imported at the entry and the
198
+ chat's CSS pulled in later by a lazily imported component, or a bundler that
199
+ hoists `node_modules` CSS after your own — and every override silently does
200
+ nothing. No error, no warning, just the Talk2View palette.
201
+
202
+ When you cannot control the order, name the class twice. It is the same
203
+ element, so it still matches exactly the chat, but at double the specificity,
204
+ which no source order can beat:
205
+
206
+ ```css
207
+ .t2v-chat.t2v-chat {
208
+ --primary: #4A73F0;
209
+ }
210
+ ```
211
+
212
+ Never reach for `!important` on a token instead. The chat's own stylesheet is
213
+ forbidden from using it on a custom property, and an `!important` of yours would
214
+ be inherited by every surface that reads the token downstream.
215
+
216
+ ### Links in the agent's answers always open in a new tab
217
+
218
+ Every link in a markdown answer is rendered with `target="_blank"` and
219
+ `rel="noopener noreferrer"`, and there is no prop to change that. The chat is
220
+ usually a panel inside your application, and the links are not yours or ours —
221
+ the model writes them, often out of a web-search result or a file the end-user
222
+ attached. A same-tab navigation would take your app down with it, unsaved state
223
+ and all.
224
+
225
+ `javascript:` URLs and raw HTML in an answer are already neutralised, by
226
+ react-markdown's default URL transform and by there being no raw-HTML plugin.
227
+
228
+ ### Turning attachments off turns off two things
229
+
230
+ `features.attachments: false` removes both the attach button and the drop zone.
231
+ There is no half-off state: either the chat takes files or it does not.
232
+
233
+ ---
234
+
235
+ ## Theming
236
+
237
+ Everything the chat draws is inside `<div class="t2v-chat">`, and every colour,
238
+ radius and font it uses is a custom property declared on that element. Redeclare
239
+ one and the chat follows; nothing else on your page changes.
240
+
241
+ ```css
242
+ .t2v-chat {
243
+ --radius: 0.75rem; /* square by default; this rounds everything */
244
+ --primary: #4A73F0; /* the send button, the accent */
245
+ --primary-foreground: #fff;
246
+ --composer-radius: 1rem; /* the composer on its own */
247
+ }
248
+ ```
249
+
250
+ Import `@talk2view/sdk/chat.css` **before** your own CSS, or write the selector
251
+ twice (`.t2v-chat.t2v-chat`) — see
252
+ [above](#your-theme-override-loses-if-your-css-loads-before-ours) for why.
253
+
254
+ | Token | What it paints |
255
+ |---|---|
256
+ | `--background` / `--foreground` | The chat's ground and its text |
257
+ | `--card` / `--card-foreground` | Raised blocks |
258
+ | `--popover` / `--popover-foreground` | The launcher's panel, menus, tooltips |
259
+ | `--primary` / `--primary-foreground` | Send, submit, the agreed action |
260
+ | `--secondary` / `--secondary-foreground` | Quiet buttons |
261
+ | `--muted` / `--muted-foreground` | Soft surfaces (the user's bubble) and quiet text |
262
+ | `--accent` / `--accent-foreground` | Hover surfaces |
263
+ | `--destructive` | Errors, Deny |
264
+ | `--border` / `--input` / `--ring` | Edges and the focus ring |
265
+ | `--radius` | Every corner. `--radius-sm/md/lg/xl` are computed from it |
266
+ | `--composer-radius` / `--composer-bg` / `--composer-padding` | The composer on its own |
267
+ | `--t2v-font` | The font. `inherit` by default — prefer the `fontFamily` prop |
268
+
269
+ The brand hexes are there too, if you want to reach past the semantic names:
270
+ `--t2v-teal`, `--t2v-smoke`, `--t2v-ivory`, `--t2v-mist`, `--t2v-stone`,
271
+ `--t2v-slate`, `--t2v-ash`, `--t2v-highlight`, `--t2v-harbor`, `--t2v-amber`.
272
+
273
+ **Two things do not follow your tokens**, on purpose:
274
+
275
+ - **The Apple sign-in button.** Apple's Human Interface Guidelines fix its
276
+ colours, and an app that repaints it can be rejected.
277
+ - **The launcher's mark and tile.** Three fixed colourways, chosen with the
278
+ `colourway` prop, each pinned in every state so a host's hover rules cannot
279
+ half-repaint them. `visitorColourway` hands the choice to the end-user.
280
+
281
+ Everything else, the voice button included, reads the tokens above.
282
+
283
+ ---
284
+
285
+ ## Dark mode
286
+
287
+ Put `dark` on the chat's container — the same class shadcn uses, scoped so it
288
+ never reaches your page:
289
+
290
+ ```tsx
291
+ <Talk2ViewChat partnerKey="…" className={theme === 'dark' ? 'dark' : undefined} />
292
+ ```
293
+
294
+ The class travels with the chat: tooltips, dialogs and the launcher's panel
295
+ render in a host element appended to `<body>`, outside the element `className`
296
+ lands on, and the chat carries the theme out to them for you.
297
+
298
+ `dark` on your `<html>` or `<body>` does **not** darken the chat, deliberately —
299
+ your page's theme and the chat's are separate choices. Wire them together in
300
+ your own code if you want them to move as one, as the example does.
301
+
302
+ ---
303
+
304
+ ## The launcher in someone else's page
305
+
306
+ ### Mount it outside anything with a `transform`
307
+
308
+ The launcher's corner is `position: fixed`. An ancestor with `transform`,
309
+ `filter`, `perspective`, `backdrop-filter` or `contain` becomes the containing
310
+ block for fixed descendants, and the launcher then pins to the corner of *that
311
+ element* instead of the window. There is no CSS fix from our side. Render it as
312
+ a child of `<body>`, or of a container with none of those properties:
313
+
314
+ ```tsx
315
+ createPortal(<Talk2ViewChatLauncher partnerKey="…" />, document.body)
316
+ ```
317
+
318
+ A host page whose own CSS shouts `position: static !important` at your
319
+ containers will do the same thing, for the same reason.
320
+
321
+ ### `className` lands on the anchor
322
+
323
+ The anchor is the fixed corner in your page, so `className` is how you move the
324
+ launcher to the other side:
325
+
326
+ ```css
327
+ .my-launcher { inset-inline: 1rem auto !important; } /* bottom-left */
328
+ ```
329
+
330
+ ### `keepClearOf` rides above a banner
331
+
332
+ Give it a CSS selector for anything pinned to the bottom of the page — a cookie
333
+ notice, a playback bar — and the launcher rises above it while it is there and
334
+ drops back when it goes. The selector is re-resolved as the page changes, so a
335
+ banner that appears after the chat is picked up, and a mistyped selector means
336
+ "keep clear of nothing" rather than an error.
337
+
338
+ ### The phone sheet
339
+
340
+ At and below `sheetBelow` (640 px by default) the panel is a full-screen sheet
341
+ with its own Close button, and the page behind it does not scroll. The launcher
342
+ stays tappable over it.
343
+
344
+ ### What the visitor gets to keep
345
+
346
+ Two things are remembered **per device, in `localStorage`, not per account**:
347
+ the colourway (when `visitorColourway` is on) and the size they dragged the
348
+ panel to. A double click on the panel's top-left corner gives the default size
349
+ back. Whether they have opened the chat before is remembered too — that is what
350
+ retires the label beside the mark.
351
+
352
+ ---
353
+
354
+ ## Content Security Policy, and hosts with no CSS loader
355
+
356
+ The chat fetches nothing at runtime: no fonts, no CDN, no stylesheet, no script.
357
+ A `connect-src` that allows your Talk2View engine is all it needs.
358
+
359
+ `import '@talk2view/sdk/chat.css'` needs a build that can import CSS. Some hosts
360
+ cannot — an Office add-in's webpack with no CSS rule, a strict `style-src` that
361
+ blocks a `<style>` tag. For those:
362
+
363
+ ```ts
364
+ import { injectTalk2ViewChatStyles } from '@talk2view/sdk/chat';
365
+
366
+ injectTalk2ViewChatStyles(); // idempotent; call it as often as you like
367
+ ```
368
+
369
+ It installs the same stylesheet through `adoptedStyleSheets`, which `style-src`
370
+ does not govern, and falls back to one `<style>` tag where constructable
371
+ stylesheets are missing. Use one route or the other, not both.
372
+
373
+ ---
374
+
375
+ ## Next.js App Router
376
+
377
+ The chat is a client component. Import it from a file that has `'use client'`
378
+ at the top, or mark the boundary yourself:
379
+
380
+ ```tsx
381
+ // app/chat/panel.tsx
382
+ 'use client';
383
+ import { Talk2ViewChat } from '@talk2view/sdk/chat';
384
+ export default function Panel() {
385
+ return <Talk2ViewChat partnerKey={process.env.NEXT_PUBLIC_T2V_KEY!} />;
386
+ }
387
+ ```
388
+
389
+ The stylesheet is a side-effect import, so put it in `app/layout.tsx` (or
390
+ anywhere in the client graph) rather than inside a server component.
391
+
392
+ Both components survive `renderToString` — every store the chat subscribes to
393
+ supplies a server snapshot, and the portal host is not created without a
394
+ `document` — so neither has to be loaded with `ssr: false`. Doing so is
395
+ harmless, though, if you would rather keep the weight out of the server bundle
396
+ entirely.
397
+
398
+ ---
399
+
400
+ ## Size
401
+
402
+ Stated plainly, because it is large.
403
+
404
+ | | gzip |
405
+ |---|---|
406
+ | `@talk2view/sdk` (the core client) | 9.4 KB |
407
+ | `@talk2view/sdk/ui` (the previous chat panel) | 39.9 KB |
408
+ | **`@talk2view/sdk/chat`** | **361.8 KB** |
409
+ | A whole page containing the chat and React | ~437 KB |
410
+
411
+ The first three exclude React and React DOM, which your app already has. The
412
+ weight is `@assistant-ui/react`, `@base-ui/react` and the markdown stack, and
413
+ there is no code-splitting seam inside them to defer.
414
+
415
+ **If you never import `/chat`, you pay none of it.** The entry points are
416
+ separate and `sideEffects` lists only CSS, so a consumer of `@talk2view/sdk` or
417
+ `@talk2view/sdk/ui` reaches no assistant-ui code at all. That is pinned by
418
+ `tests/chat/tree-shake.test.ts`, which bundles the published `dist/` on every
419
+ test run and fails if any of these numbers grows past a fence set just above
420
+ today's reading.
421
+
422
+ ---
423
+
424
+ ## Errors you may see
425
+
426
+ ### "Two copies of @assistant-ui/react are loaded"
427
+
428
+ Thrown in development, on mount. Your app installed its own
429
+ `@assistant-ui/react` at a version npm could not collapse with ours, so there
430
+ are two physical copies on the page. Each has its own React context: the chat
431
+ would mount, render, and then quietly do nothing — the runtime fills one
432
+ context and the components read the other.
433
+
434
+ ```bash
435
+ npm ls @assistant-ui/react # shows every copy and who asked for it
436
+ ```
437
+
438
+ Fix it by aligning your version with the one `@talk2view/sdk` depends on, so npm
439
+ dedupes them, or by dropping your own copy if the packaged chat is the only
440
+ thing using it. The check does not run in production builds — it is there to
441
+ tell you now rather than leave you with a chat that does nothing.
442
+
443
+ ### "`<Talk2ViewChat>` needs either a partnerKey or a client"
444
+
445
+ Exactly what it says. Pass one or the other.
446
+
447
+ ### "This component must be rendered inside `<Talk2ViewChat>`"
448
+
449
+ `useTalk2ViewChatClient()` was called outside the chat. Create your own
450
+ `Talk2View` client and pass it in with the `client` prop if you need one in both
451
+ places.
452
+
453
+ ---
454
+
455
+ ## For contributors: the stylesheet and the vendored files
456
+
457
+ Skip this unless you are changing the chat itself.
458
+
459
+ ### Two authoring traps in `chat.src.css`
460
+
461
+ **1. The root rule is written `:root`, never `.t2v-chat`.**
462
+ `scripts/chat/isolate.mjs` rewrites `:root` to `.t2v-chat` and prefixes
463
+ everything else as a descendant of it. So a rule you write as `.t2v-chat { … }`
464
+ ships as `.t2v-chat .t2v-chat { … }` and matches nothing — silently. This cost
465
+ the full-pane chat its height once: it collapsed to its content and nobody found
466
+ out until a browser rendered it.
467
+
468
+ ```css
469
+ :root { height: 100%; } /* the chat's own element */
470
+ .aui-thread-root { … } /* anything inside it */
471
+ :root.aui-modal-anchor { … } /* another chat root: the launcher's corner */
472
+ ```
473
+
474
+ **2. Armoured utilities beat inline styles and unlayered rules.**
475
+ `isolate.mjs --armour` marks every declaration in `@layer utilities` and
476
+ `@layer components` `!important`. An important author declaration beats an
477
+ inline `style` attribute, so `style={{ width }}` on an element that also carries
478
+ `w-[…]` does nothing. It also beats any hand-written rule of yours that is not
479
+ itself `!important` **and** more specific.
480
+
481
+ This has bitten three separate pieces of work in three disguises, and the build
482
+ never says a word: the rule simply has no effect. When you write CSS that has to
483
+ win against a utility, give it `!important` and higher specificity, and check it
484
+ in a browser:
485
+
486
+ ```css
487
+ .aui-modal-content[data-sized] { width: var(--t2v-panel-width) !important; }
488
+ ```
489
+
490
+ Never put `!important` on a custom property — that would break retheming, and
491
+ the audit script fails the build if you do.
492
+
493
+ The other side of the same coin: armour does **not** cover hand-written rules in
494
+ the unlayered origin, which is where the launcher's anchor, phone sheet and
495
+ colourways live. A host page's `!important` can beat those.
496
+ `tests/e2e/chat-hosts/` says exactly which ones hold and which do not.
497
+
498
+ ### Changing a vendored file
499
+
500
+ Files under `src/chat/vendor/` come from assistant-ui's shadcn registry and are
501
+ never hand-edited. The registry is unversioned and mutable, so every file's
502
+ sha256 is recorded in `MANIFEST.json`.
503
+
504
+ ```bash
505
+ npm run chat:sync # refetch, rewrite imports, run the codemod, apply patches
506
+ npm run chat:check # verify the tree is exactly what sync produced
507
+ npm run build:css # rebuild dist/chat.css (scope, namespace, audit, minify)
508
+ ```
509
+
510
+ Every difference from upstream must be one of three things: a path rewrite in
511
+ `sync-registry.mjs`, the idempotent `asChild → render` codemod, or a patch file
512
+ under `scripts/chat/patches/`. To change a patch, reverse-apply the old one,
513
+ edit the pristine file, and regenerate with `diff -u` — do not hand-edit the
514
+ hunks. Then run the **online** sync to prove the patch still applies to what the
515
+ registry serves today.
516
+
517
+ Prefer a path rewrite to a patch when the change is one: a rewrite has no hunk
518
+ to reconcile when upstream edits the lines around it. Two of them are in the
519
+ script today — the app aliases (`@/components/ui/` and friends) and the
520
+ `MarkdownText` redirect, which points the thread and the reasoning block at
521
+ `src/chat/markdown.tsx` so the `a` override lives in first-party code. A rewrite
522
+ that finds nothing to do throws, because a silent no-op is how a behaviour
523
+ quietly reverts.
524
+
525
+ Anything else belongs in a wrapper, in `src/chat/ui/`, or in the stylesheet.
526
+
527
+ ### Running the browser suites
528
+
529
+ ```bash
530
+ npm run test:e2e:chat-hosts # five hostile host pages (also runs in CI)
531
+ npm run test:e2e:chat-example # the react-chat example
532
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@talk2view/sdk",
3
- "version": "0.18.1",
3
+ "version": "0.19.0",
4
4
  "description": "Talk2View SDK — Add AI-powered natural language control to any application",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -55,10 +55,11 @@
55
55
  }
56
56
  },
57
57
  "files": [
58
- "dist"
58
+ "dist",
59
+ "docs"
59
60
  ],
60
61
  "scripts": {
61
- "build": "npm run clean && npm run build:css && tsc",
62
+ "build": "npm run clean && npm run build:css && tsc && npm run build:licenses",
62
63
  "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
63
64
  "dev": "tsc --watch",
64
65
  "test": "vitest run",
@@ -74,7 +75,8 @@
74
75
  "chat:sync": "node scripts/chat/sync-registry.mjs",
75
76
  "chat:check": "node scripts/chat/check.mjs",
76
77
  "test:e2e:chat-hosts": "npm run build && playwright test --config playwright.chat-hosts.config.ts",
77
- "test:e2e:chat-example": "npm run build && playwright test --config playwright.chat-example.config.ts"
78
+ "test:e2e:chat-example": "npm run build && playwright test --config playwright.chat-example.config.ts",
79
+ "build:licenses": "node -e \"const fs=require('node:fs');fs.mkdirSync('dist/chat/vendor',{recursive:true});fs.copyFileSync('src/chat/vendor/LICENSE','dist/chat/vendor/LICENSE');fs.copyFileSync('src/chat/vendor/MANIFEST.json','dist/chat/vendor/MANIFEST.json')\""
78
80
  },
79
81
  "dependencies": {
80
82
  "@assistant-ui/react": "^0.15.20",
@@ -101,16 +103,16 @@
101
103
  "@types/dompurify": "^3.0.5",
102
104
  "@types/react": "^19.0.0",
103
105
  "@types/react-dom": "^19.0.0",
104
- "@vitest/coverage-v8": "^3.0.0",
106
+ "@vitest/coverage-v8": "^3.2.7",
105
107
  "browserslist": "^4.29.0",
106
- "esbuild": "^0.27.3",
108
+ "esbuild": "^0.28.2",
107
109
  "jsdom": "^25.0.0",
108
110
  "lightningcss": "^1.33.0",
109
111
  "postcss": "^8.5.28",
110
112
  "tailwindcss": "^4.3.3",
111
113
  "tw-animate-css": "^1.4.0",
112
114
  "typescript": "^5.7.0",
113
- "vitest": "^3.0.0"
115
+ "vitest": "^3.2.7"
114
116
  },
115
117
  "peerDependencies": {
116
118
  "react": "^18.0.0 || ^19.0.0",
@@ -139,9 +141,18 @@
139
141
  "license": "MIT",
140
142
  "repository": {
141
143
  "type": "git",
142
- "url": "https://github.com/A2B-Technology-Corporation/Talk2View-Platform"
144
+ "url": "git+https://github.com/Talk2View/talk2view-typescript.git"
143
145
  },
144
146
  "sideEffects": [
145
147
  "*.css"
146
- ]
148
+ ],
149
+ "bugs": {
150
+ "url": "https://talk2view.com/contact"
151
+ },
152
+ "engines": {
153
+ "node": ">=20.19"
154
+ },
155
+ "publishConfig": {
156
+ "access": "public"
157
+ }
147
158
  }