@artooi/ag-ui-web-component 0.6.0 → 0.8.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 (44) hide show
  1. package/CHANGELOG.md +73 -1
  2. package/README.md +180 -9
  3. package/dist/ag-ui-web-component.bundle.js +268 -47
  4. package/dist/ag-ui-web-component.bundle.js.map +4 -4
  5. package/dist/constants.d.ts +5 -0
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/core/ag_ui_chat.d.ts +20 -0
  8. package/dist/core/ag_ui_chat.d.ts.map +1 -1
  9. package/dist/core/agui_client.d.ts +16 -0
  10. package/dist/core/agui_client.d.ts.map +1 -1
  11. package/dist/index.d.ts +3 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +847 -237
  14. package/dist/index.js.map +3 -3
  15. package/dist/tools/page_action_tools.d.ts +31 -0
  16. package/dist/tools/page_action_tools.d.ts.map +1 -0
  17. package/dist/ui/attachment_tray.d.ts +3 -0
  18. package/dist/ui/attachment_tray.d.ts.map +1 -1
  19. package/dist/ui/confirmation_card.d.ts +4 -1
  20. package/dist/ui/confirmation_card.d.ts.map +1 -1
  21. package/dist/ui/relative_time.d.ts +5 -3
  22. package/dist/ui/relative_time.d.ts.map +1 -1
  23. package/dist/ui/styles.d.ts +1 -1
  24. package/dist/ui/styles.d.ts.map +1 -1
  25. package/dist/ui/thread_drawer.d.ts +7 -1
  26. package/dist/ui/thread_drawer.d.ts.map +1 -1
  27. package/dist/ui/tool_call_card.d.ts +19 -8
  28. package/dist/ui/tool_call_card.d.ts.map +1 -1
  29. package/dist/ui/ui_strings.d.ts +126 -0
  30. package/dist/ui/ui_strings.d.ts.map +1 -0
  31. package/package.json +1 -1
  32. package/src/constants.ts +5 -0
  33. package/src/core/ag_ui_chat.ts +267 -46
  34. package/src/core/agui_client.ts +33 -2
  35. package/src/index.ts +7 -0
  36. package/src/tools/page_action_tools.ts +130 -0
  37. package/src/ui/attachment_tray.ts +13 -7
  38. package/src/ui/confirmation_card.ts +15 -5
  39. package/src/ui/relative_time.ts +15 -8
  40. package/src/ui/styles.ts +221 -0
  41. package/src/ui/thread_drawer.ts +53 -25
  42. package/src/ui/tool_call_card.ts +63 -25
  43. package/src/ui/ui_strings.ts +208 -0
  44. package/src/version.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,76 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.8.0] — 2026-06-30
11
+
12
+ ### Added
13
+
14
+ - **Per-turn answer group + opt-in well (WELL-1).** Each assistant turn now
15
+ renders inside one `.answer` group (part `answer`) that holds its streamed
16
+ text, tool cards, and pending indicator — so a turn that calls tools reads as
17
+ a single answer instead of loose siblings. The group spans the whole
18
+ multi-round frontend-tool loop (several AG-UI runs), opening on the turn's
19
+ first run and closing at settle; user bubbles stay outside it, and history
20
+ replay reconstructs one group per assistant turn. Add the boolean
21
+ `data-answer-well` attribute to box that group in a bordered, padded "well"
22
+ (themeable via `--ag-ui-well-bg` / `--ag-ui-well-border`); without it the
23
+ layout is the flat stack as before. Pure CSS, turn-scoped, no JS API.
24
+ - **Full-screen page placement (PAGE-1).** New `placement="page"`: a full-bleed
25
+ background with the conversation in a centred reading column (default ~820px,
26
+ set via `--ag-ui-content-max-width`). The assistant turn spans the column
27
+ while the user message stays a right-aligned pill — the layout for a dedicated
28
+ chat page (distinct from `full`'s edge-to-edge, left-aligned messages).
29
+ - **Inline tool-display mode + themeable status icons (CARD-1).** New
30
+ `data-tool-display="inline"`: the lightest card — a one-line status row (icon
31
+ + summary, no box chrome) with the result behind its own toggle. Every
32
+ tool-call card now leads with a CSS-drawn **status icon** (part
33
+ `tool-card-icon`): a spinning ring while running, then a check / cross / slash
34
+ on success / error / decline, replacing the hardcoded `🔧` glyph. Re-theme via
35
+ `--ag-ui-tool-icon-done` / `--ag-ui-tool-icon-error` / `--ag-ui-tool-icon-declined`
36
+ and `--ag-ui-tool-spin-duration`; the spin respects `prefers-reduced-motion`.
37
+
38
+ ## [0.7.0] — 2026-06-26
39
+
40
+ ### Added
41
+
42
+ - **Localization (i18n).** Every user-facing string — labels, placeholders,
43
+ `aria-label`s, and `title` tooltips — now reads from a flat `UiStrings` table.
44
+ Override any subset via the `strings` property or the `data-strings` JSON
45
+ attribute (the property wins key-by-key); the rest fall back to the English
46
+ defaults. A few keys are `{token}` templates (`minutesAgo`, `confirmRun`,
47
+ `tooLarge`, …). New exports: `UiStrings`, `DEFAULT_UI_STRINGS`,
48
+ `mergeUiStrings`.
49
+ - **`::part()` styling and replaceable slots.** Every structural element exposes
50
+ a stable `part` (`panel`, `header`, `title`, `messages`, `tool-card`,
51
+ `composer`, `input`, `send`, `launcher`, the drawer parts, …) so hosts restyle
52
+ from outside the Shadow DOM without piercing it. Coarse slots — `icon`,
53
+ `header-actions`, `empty`, `footer`, `launcher` — replace whole regions with
54
+ host markup.
55
+ - **Header / launcher icon.** An `icon` slot (any markup) before the title, or
56
+ the `data-icon-url` attribute convenience (an `<img>`); the slot wins. Sized
57
+ via `--ag-ui-icon-size`.
58
+ - **Sidebar placement.** `placement="sidebar"` is a full-height docked panel
59
+ that slides open/closed and collapses to a slim icon **rail** (instead of the
60
+ floating launcher). Docks right by default; `data-side="left"` docks left.
61
+ Overlays by default (`--ag-ui-position: static` for host-managed push); the
62
+ slide honours `prefers-reduced-motion`; the rail carries `aria-expanded`.
63
+ - **Built-in page-action tools.** Opt in via `data-page-actions` (a comma list of
64
+ `scroll` / `drag`): `scroll_to` (a target into view — `top` / `bottom` / a
65
+ selector or page-map id) and `drag_and_drop` (fires the native HTML5 drag
66
+ sequence so the page's own drop handler reacts). Targets resolve through the
67
+ overridable `resolvePageTarget` property. Not stamped destructive — gate
68
+ auto-persist-on-drop pages with `confirmPredicate`. New exports:
69
+ `createPageActionTools`, `PAGE_ACTIONS`, `ResolvePageTarget`.
70
+
71
+ ### Fixed
72
+
73
+ - **Stuck "pending" UI when the stream drops mid-run.** A run whose stream closes
74
+ without a terminal `RUN_FINISHED` / `RUN_ERROR` event used to resolve as if it
75
+ had succeeded, leaving the thinking indicator — and any in-flight tool card —
76
+ stuck forever. Such a close is now surfaced as a connection-loss error (the
77
+ localizable `connectionLost` string), and `onSettled` sweeps any tool card
78
+ still pending to the no-result fallback. New export: `ConnectionLostError`.
79
+
10
80
  ## [0.6.0] — 2026-06-25
11
81
 
12
82
  ### Added
@@ -297,7 +367,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
297
367
  ### Notes
298
368
  - First release — exercising the automated npm OIDC publish pipeline end-to-end.
299
369
 
300
- [Unreleased]: https://github.com/Artui/ag-ui-web-component/compare/v0.6.0...HEAD
370
+ [Unreleased]: https://github.com/Artui/ag-ui-web-component/compare/v0.8.0...HEAD
371
+ [0.8.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.7.0...v0.8.0
372
+ [0.7.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.6.0...v0.7.0
301
373
  [0.6.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.5.0...v0.6.0
302
374
  [0.5.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.4.0...v0.5.0
303
375
  [0.4.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.3.1...v0.4.0
package/README.md CHANGED
@@ -44,6 +44,7 @@ No framework, no Django, no admin specifics live here. Downstream consumers (e.g
44
44
  - [Registering tools](#registering-tools)
45
45
  - [Inline confirmation (`x-destructive` / `x-confirm` / `confirmPredicate`)](#inline-confirmation-x-destructive--x-confirm--confirmpredicate)
46
46
  - [DOM-driver and animation primitives](#dom-driver-and-animation-primitives)
47
+ - [Page-action tools](#page-action-tools)
47
48
  - [New chat and collapse](#new-chat-and-collapse)
48
49
  - [Tool-call display modes](#tool-call-display-modes)
49
50
  - [Markdown rendering](#markdown-rendering)
@@ -52,6 +53,7 @@ No framework, no Django, no admin specifics live here. Downstream consumers (e.g
52
53
  - [Host seams: the SPA story](#host-seams-the-spa-story)
53
54
  - [Public API surface](#public-api-surface)
54
55
  - [Theming, density, and placement](#theming-density-and-placement)
56
+ - [Internationalization (i18n)](#internationalization-i18n)
55
57
  - [Building the bundle](#building-the-bundle)
56
58
  - [Compatibility](#compatibility)
57
59
 
@@ -145,7 +147,7 @@ That's the whole integration: an `endpoint` attribute pointing at your AG-UI ser
145
147
  | --- | --- | --- |
146
148
  | `endpoint` | `endpoint` | The AG-UI endpoint URL. Required to send. Reflecting getter + setter. |
147
149
  | `title-text` | — | Header label; defaults to `"Assistant"`. The only **observed** attribute (live-updates the header). |
148
- | `data-tool-display` | `toolDisplay` | Tool-call card detail: `minimal` / `compact` / `full` (default `full`). |
150
+ | `data-tool-display` | `toolDisplay` | Tool-call card detail: `inline` / `minimal` / `compact` / `full` (default `full`). |
149
151
  | `data-text-animation` | — | Incoming-text reveal: `none` (default) / `fade` / `word`. |
150
152
  | `data-prompt-chips` | — | `"true"` to surface skills as chips. |
151
153
  | `data-slash-commands` | — | `"true"` to enable the `/`-command palette. |
@@ -156,15 +158,21 @@ That's the whole integration: an `endpoint` attribute pointing at your AG-UI ser
156
158
  | `data-attachments-url` | — | URL of the file-upload endpoint (django-ag-ui's `AttachmentsView`); reveals the composer's 📎 picker + drag-and-drop. |
157
159
  | `data-attachment-accept` | — | `<input accept>` list for client-side type filtering (e.g. `image/*,.pdf`). The server stays authoritative. |
158
160
  | `data-attachment-max-bytes` | — | Client-side upload size cap in bytes (default 10 MiB; `0` disables). The server stays authoritative. |
159
- | `collapsed` | `collapsed` | Reflected boolean; collapses the widget. Persisted per-tab in `sessionStorage`. |
161
+ | `data-strings` | `strings` | Partial JSON override of the UI string table (localization). The property wins key-by-key over the attribute; see [Internationalization](#internationalization-i18n). |
162
+ | `data-icon-url` | — | Header (and sidebar-rail) icon image URL. A slotted `slot="icon"` wins; see [Header & launcher icon](#header-and-launcher-icon). |
163
+ | `data-page-actions` | — | Opt-in built-in page-action tools: a comma list of `scroll` / `drag` (e.g. `"scroll,drag"`). See [Page-action tools](#page-action-tools). |
164
+ | `data-side` | — | CSS-only, for `placement="sidebar"`: which edge it docks to — `right` (default) / `left`. |
165
+ | `data-answer-well` | — | CSS-only boolean: box each assistant turn (its text, tool cards, and thinking) in one bordered "well". Off by default. See [The answer well](#the-answer-well). |
166
+ | `collapsed` | `collapsed` | Reflected boolean; collapses the widget (to a rail under `placement="sidebar"`). Persisted per-tab in `sessionStorage`. |
160
167
  | `theme` | — | CSS-only: `light` (default) / `dark` / `auto` / `code`. |
161
168
  | `density` | — | CSS-only: `comfortable` (default) / `compact`. |
162
- | `placement` | — | CSS-only: `floating` (default) / `bottom-left` / `side` / `full` / `embedded`. |
169
+ | `placement` | — | CSS-only: `floating` (default) / `bottom-left` / `side` / `sidebar` / `full` / `page` / `embedded`. |
163
170
 
164
171
  **Properties** (JS only, not attributes): `headers`, `allowImages`, `autoConfirm`,
165
172
  `confirmPredicate`, `agentFactory`, `getTools`, `getContext`, `routeMap`, `navigate`,
166
173
  `getPageMap`, `autoInjectPageMap`, `conversationStore`, `uploadHandler`, `navigationResult`,
167
- `skillContext`, `toolSummaries`, plus the mirrors `endpoint` / `toolDisplay` / `collapsed`.
174
+ `skillContext`, `toolSummaries`, `strings`, `resolvePageTarget`, plus the mirrors
175
+ `endpoint` / `toolDisplay` / `collapsed`.
168
176
 
169
177
  `allowImages` (default `false`) re-enables `<img>` in rendered assistant markdown.
170
178
  It is off by default because a model-controlled image URL is fetched by the browser
@@ -329,6 +337,36 @@ register the change.
329
337
  Each takes an element the caller has already located; host packages wrap them with
330
338
  environment-aware lookups (e.g. "find `#id_<name>`, then `fillField`").
331
339
 
340
+ ### Page-action tools
341
+
342
+ Two built-in client tools let the agent perform common page interactions without every host
343
+ re-implementing them. They are **opt-in** via `data-page-actions` — a comma list of the tokens you
344
+ want — so you control the agent's interaction surface:
345
+
346
+ ```html
347
+ <ag-ui-chat endpoint="/agent/" data-page-actions="scroll,drag"></ag-ui-chat>
348
+ ```
349
+
350
+ - **`scroll_to`** — scroll a target into view. `target` is `"top"`, `"bottom"`, or a CSS selector
351
+ / page-map element id. Read-only (no confirmation).
352
+ - **`drag_and_drop`** — drag the `from` element onto the `to` element (selectors / page-map ids),
353
+ firing the standard HTML5 drag sequence (`dragstart` → `dragenter`/`dragover`/`drop` → `dragend`)
354
+ so the page's own drop handler reacts. Useful for reordering sortable lists.
355
+
356
+ Targets resolve through the overridable `resolvePageTarget` property — `(target) => HTMLElement |
357
+ null`, defaulting to `document.querySelector`. A host with a page map overrides it to map its own
358
+ element ids (the same way the DOM-driver primitives are wrapped with environment-aware lookups):
359
+
360
+ ```js
361
+ chat.resolvePageTarget = (id) => myPageMap.elementFor(id);
362
+ ```
363
+
364
+ **Destructiveness.** Page actions are *not* stamped `x-destructive` — a drag rearranges transient
365
+ state, and the durable change happens at the page's explicit commit (a Save), which stays in the
366
+ user's hands. If your page persists *on drop* (a kanban board firing a PATCH from the drop
367
+ handler), gate `drag_and_drop` with [`confirmPredicate`](#inline-confirmation-x-destructive--x-confirm--confirmpredicate)
368
+ — or don't enable it. A target that resolves to nothing returns a clean, model-readable tool error.
369
+
332
370
  ---
333
371
 
334
372
  ## New chat and collapse
@@ -357,8 +395,11 @@ chat.addEventListener("ag-ui-toggle", (e) => console.log(e.detail.collapsed));
357
395
  ## Tool-call display modes
358
396
 
359
397
  How much a tool-call card shows is set via the `data-tool-display` attribute (or `toolDisplay`
360
- property), one of `minimal` / `compact` / `full` (default `full`):
398
+ property), one of `inline` / `minimal` / `compact` / `full` (default `full`):
361
399
 
400
+ - `inline` — the lightest mode: a single status row (icon + summary, no card chrome) with the
401
+ result behind its own toggle. Reads as one line of the answer — pairs with [the answer
402
+ well](#the-answer-well).
362
403
  - `minimal` — tool name + status pill only.
363
404
  - `compact` — name + status, with args *and* result behind a single collapsed "Details" toggle.
364
405
  - `full` — args inline, result behind its own toggle (the original behaviour).
@@ -366,6 +407,12 @@ property), one of `minimal` / `compact` / `full` (default `full`):
366
407
  If a tool's schema carries an `x-summary` string (use `X_SUMMARY_KEY`), the card shows it on the
367
408
  label instead of the raw tool name.
368
409
 
410
+ Every card leads with a **status icon** drawn entirely in CSS — a spinning ring while the call
411
+ runs, then a check / cross / slash on success / error / decline. Re-theme it via custom
412
+ properties (or the `tool-card-icon` part): `--ag-ui-tool-icon-done`, `--ag-ui-tool-icon-error`,
413
+ `--ag-ui-tool-icon-declined` (quoted-string glyphs) and `--ag-ui-tool-spin-duration` (spinner
414
+ speed; the spin respects `prefers-reduced-motion`).
415
+
369
416
  ```html
370
417
  <ag-ui-chat endpoint="/agent/" data-tool-display="compact"></ag-ui-chat>
371
418
  ```
@@ -604,6 +651,7 @@ re-export point. Internal modules import from leaf paths.
604
651
  | `AgUiClient` | class | Orchestration layer over an AG-UI `AbstractAgent`. |
605
652
  | `AgUiClientConfig` / `AgUiClientHandlers` / `AgUiRunInputs` | type | Client config, lifecycle handlers, per-run input providers. |
606
653
  | `AgUiToolCall` / `ToolExecution` / `ExecuteTool` | type | Tool-call shape, execution result, executor signature. |
654
+ | `ConnectionLostError` | class | Raised (→ `onError`) when a run's stream closes with no terminal AG-UI event. |
607
655
  | `createHttpAgent(options)` | function | Default agent factory (wraps `HttpAgent`). |
608
656
  | `AgentFactory` / `HttpAgentOptions` | type | Factory signature and its options. |
609
657
 
@@ -615,6 +663,9 @@ re-export point. Internal modules import from leaf paths.
615
663
  | `ClientTool` | type | A frontend tool declaration. |
616
664
  | `isDestructive(parameters)` | function | Read the `x-destructive` flag. |
617
665
  | `isNavigates(parameters)` | function | Read the `x-navigates` flag. |
666
+ | `createPageActionTools(enabled, resolveTarget)` | function | Build the opt-in `scroll_to` / `drag_and_drop` tools. |
667
+ | `PAGE_ACTIONS` | const | The page-action opt-in tokens (`scroll` / `drag`). |
668
+ | `ResolvePageTarget` | type | `(target) => HTMLElement | null` — the page-target resolver. |
618
669
  | `X_DESTRUCTIVE_KEY` / `X_NAVIGATES_KEY` | const | The JSON-Schema extension keys. |
619
670
 
620
671
  ### Host seams
@@ -658,7 +709,10 @@ re-export point. Internal modules import from leaf paths.
658
709
  | `ToolCallStatus` / `SettledStatus` / `ToolDisplayMode` | type | Card lifecycle states + display mode. |
659
710
  | `requestConfirmation(host, request, options?)` | function | Append the inline confirmation card to the transcript. |
660
711
  | `ConfirmationRequest` | type | What the card displays. |
661
- | `ConfirmationOptions` | type | `{ signal? }` — aborting resolves the card as declined (the Stop control's hook). |
712
+ | `ConfirmationOptions` | type | `{ signal?, strings? }` — abort resolves the card as declined; `strings` localizes it. |
713
+ | `UiStrings` | type | The flat table of every user-facing string. |
714
+ | `DEFAULT_UI_STRINGS` | const | The English defaults (the override floor). |
715
+ | `mergeUiStrings(overrides)` | function | Merge a partial override over the defaults. |
662
716
  | `renderMarkdown(text)` | function | Render sanitized markdown/HTML (marked + DOMPurify). |
663
717
  | `typeInto` / `highlightThenClick` / `pressThenClick` / `selectOption` / `toggleControl` / `scrollIntoCenterView` / `focusWithFlash` / `prefersReducedMotion` | function | Animation primitives. |
664
718
  | `fillField` / `clickElement` / `pressButton` / `selectControl` / `setControlValue` / `toggleCheckbox` | function | DOM-driver primitives. |
@@ -708,8 +762,9 @@ have to hand-tune the variables:
708
762
 
709
763
  - `theme` — `light` (default) / `dark` / `auto` (follow the OS) / `code`.
710
764
  - `density` — `comfortable` (default) / `compact`.
711
- - `placement` — `floating` (default) / `bottom-left` / `side` / `full` / `embedded`. `embedded`
712
- drops the fixed positioning and z-index so the widget sits in normal document flow.
765
+ - `placement` — `floating` (default) / `bottom-left` / `side` / `sidebar` / `full` / `page` /
766
+ `embedded`. `embedded` drops the fixed positioning and z-index so the widget sits in normal
767
+ document flow; `page` is a full-screen [centred reading column](#page-placement).
713
768
 
714
769
  ```html
715
770
  <ag-ui-chat endpoint="/agent/" theme="dark" density="compact" placement="side"></ag-ui-chat>
@@ -717,7 +772,123 @@ have to hand-tune the variables:
717
772
 
718
773
  See [`src/ui/styles.ts`](src/ui/styles.ts) for the full variable + preset list. The
719
774
  [`demo/`](demo/) live playground (`node demo/mock-server.mjs`) flips theme, density, placement,
720
- text-animation, and tool-display live from a single page.
775
+ text-animation, tool-display, and the answer well live from a single page.
776
+
777
+ ### Parts and slots
778
+
779
+ For styling beyond the `--ag-ui-*` variables, every structural element exposes a `part` so you can
780
+ reach it from outside the Shadow DOM with `::part()` — no shadow piercing. The part names are
781
+ **public API** (additions are non-breaking; renames are breaking):
782
+
783
+ ```css
784
+ ag-ui-chat::part(panel) { border-radius: 0; }
785
+ ag-ui-chat::part(header) { background: #111; }
786
+ ag-ui-chat::part(send) { text-transform: uppercase; }
787
+ ag-ui-chat::part(tool-card) { font-family: var(--my-mono); }
788
+ ```
789
+
790
+ Available parts: `panel`, `header`, `title`, `icon`, `header-controls`, `header-button`
791
+ (plus `history-button` / `new-button` / `collapse-button`), `messages`, `answer` (the per-turn
792
+ group), `message` (plus `message-user` / `message-assistant`), `empty`, `pending`, `tool-card`
793
+ (plus `tool-card-head` / `-icon` / `-name` / `-status` / `-args` / `-toggle` / `-result`),
794
+ `confirm` (plus `confirm-body` /
795
+ `-args` / `-actions` / `-button` / `-cancel` / `-confirm`), `composer`, `input`, `send`,
796
+ `attach-button`, `attachment-tray`, `launcher`, `launcher-icon`, and the drawer parts
797
+ (`drawer`, `drawer-backdrop`, `drawer-panel`, `drawer-header`, `drawer-title`, `drawer-new`,
798
+ `drawer-list`, `drawer-empty`, `drawer-row`, `drawer-row-select`).
799
+
800
+ Coarse **slots** let you replace whole regions with your own markup (project light-DOM children
801
+ with a matching `slot=`):
802
+
803
+ | Slot | Where |
804
+ | --- | --- |
805
+ | `icon` | A header brand icon, before the title. |
806
+ | `header-actions` | Extra controls between the title and the built-in buttons. |
807
+ | `empty` | The empty-state shown before any message. |
808
+ | `footer` | Below the composer. |
809
+ | `launcher` | The collapsed sidebar rail's content. |
810
+
811
+ ```html
812
+ <ag-ui-chat endpoint="/agent/">
813
+ <img slot="icon" src="/logo.svg" alt="" />
814
+ <button slot="header-actions" onclick="openHelp()">?</button>
815
+ </ag-ui-chat>
816
+ ```
817
+
818
+ ### Header and launcher icon
819
+
820
+ Give the header a brand icon with either the `icon` slot (any markup) or the `data-icon-url`
821
+ convenience attribute (an `<img>`); the slot wins when both are set, and with neither the header
822
+ stays icon-less. The same icon seam feeds the collapsed sidebar rail. Size it via
823
+ `--ag-ui-icon-size` (default `22px`).
824
+
825
+ ```html
826
+ <ag-ui-chat endpoint="/agent/" data-icon-url="/logo.png"></ag-ui-chat>
827
+ ```
828
+
829
+ ### Sidebar placement
830
+
831
+ `placement="sidebar"` is a full-height **docked** panel that slides open/closed and collapses to a
832
+ slim **icon rail** (rather than the floating launcher). It docks right by default; `data-side="left"`
833
+ docks it left. Collapse state reuses the `collapsed` attribute (persisted per-tab), and the rail
834
+ carries `aria-expanded`. The slide honours `prefers-reduced-motion`.
835
+
836
+ ```html
837
+ <ag-ui-chat endpoint="/agent/" placement="sidebar" data-side="left"></ag-ui-chat>
838
+ ```
839
+
840
+ It overlays the page by default (no host-layout coupling). To make the host content reflow around
841
+ it instead, set `--ag-ui-position: static` and place the element in your own grid/flex layout.
842
+
843
+ ### Page placement
844
+
845
+ `placement="page"` turns the widget into a full-screen chat **page**: a full-bleed background with
846
+ the conversation in a centred reading column (default ~820px, set via `--ag-ui-content-max-width`).
847
+ The assistant turn spans the column width while the user message stays a right-aligned pill. Unlike
848
+ `full` (edge-to-edge, left-aligned), it's the layout you want for a dedicated `/chat` route. Pairs
849
+ naturally with the [answer well](#the-answer-well).
850
+
851
+ ```html
852
+ <ag-ui-chat endpoint="/agent/" placement="page" data-answer-well></ag-ui-chat>
853
+ ```
854
+
855
+ ### The answer well
856
+
857
+ Each assistant turn renders inside one `.answer` group (part `answer`) that holds its streamed
858
+ text, tool cards, and pending indicator — so a turn that calls tools reads as a single answer
859
+ rather than a string of loose siblings. Add the boolean `data-answer-well` attribute to box that
860
+ group in a bordered, padded "well"; without it the layout is the flat stack as before. The well is
861
+ pure CSS and turn-scoped — no JS API — and themeable via `--ag-ui-well-bg` / `--ag-ui-well-border`
862
+ (and `::part(answer)`).
863
+
864
+ ```html
865
+ <ag-ui-chat endpoint="/agent/" data-answer-well></ag-ui-chat>
866
+ ```
867
+
868
+ ---
869
+
870
+ ## Internationalization (i18n)
871
+
872
+ Every user-facing string — labels, placeholders, `aria-label`s, and `title` tooltips — is read
873
+ from a flat `UiStrings` table, so a non-English host can translate the widget without forking it.
874
+ Override any subset; the rest fall back to the English defaults. Two equivalent seams:
875
+
876
+ ```js
877
+ // As a property (merged over the defaults):
878
+ chat.strings = { send: "Senden", inputPlaceholder: "Frag mich…", stop: "Stopp" };
879
+ ```
880
+
881
+ ```html
882
+ <!-- Or inline, as JSON (the property wins key-by-key when both are set): -->
883
+ <ag-ui-chat endpoint="/agent/" data-strings='{"send": "Senden", "inputPlaceholder": "Frag mich…"}'></ag-ui-chat>
884
+ ```
885
+
886
+ Set `strings` / `data-strings` **before** the element connects (they resolve on mount). A few keys
887
+ are templates carrying `{token}` placeholders the widget fills in — e.g. `minutesAgo`
888
+ (`"{n}m ago"`), `confirmRun` (`"Run “{tool}”?"`), `tooLarge` (`"Too large (max {size})"`). Keep the
889
+ token verbatim when translating. The full key list and English defaults live in
890
+ [`src/ui/ui_strings.ts`](src/ui/ui_strings.ts) (exported as `DEFAULT_UI_STRINGS`); `mergeUiStrings`
891
+ is exported too if you want to compute a complete table yourself.
721
892
 
722
893
  ---
723
894