@artooi/ag-ui-web-component 0.20.1 → 0.22.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 (49) hide show
  1. package/CHANGELOG.md +192 -1
  2. package/README.md +404 -28
  3. package/dist/ag-ui-web-component.bundle.js +781 -451
  4. package/dist/ag-ui-web-component.bundle.js.map +4 -4
  5. package/dist/constants.d.ts +29 -0
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/core/ag_ui_chat.d.ts +93 -1
  8. package/dist/core/ag_ui_chat.d.ts.map +1 -1
  9. package/dist/core/create_http_agent.d.ts +9 -0
  10. package/dist/core/create_http_agent.d.ts.map +1 -1
  11. package/dist/core/remote_conversation_store.d.ts +8 -1
  12. package/dist/core/remote_conversation_store.d.ts.map +1 -1
  13. package/dist/core/run_index.d.ts +8 -1
  14. package/dist/core/run_index.d.ts.map +1 -1
  15. package/dist/core/transcribe_audio.d.ts +7 -0
  16. package/dist/core/transcribe_audio.d.ts.map +1 -1
  17. package/dist/core/upload_attachment.d.ts +9 -0
  18. package/dist/core/upload_attachment.d.ts.map +1 -1
  19. package/dist/core/utils.d.ts +12 -0
  20. package/dist/core/utils.d.ts.map +1 -0
  21. package/dist/dom/animations.d.ts +51 -11
  22. package/dist/dom/animations.d.ts.map +1 -1
  23. package/dist/dom/dom_driver.d.ts +12 -7
  24. package/dist/dom/dom_driver.d.ts.map +1 -1
  25. package/dist/index.d.ts +3 -3
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +1275 -520
  28. package/dist/index.js.map +3 -3
  29. package/dist/ui/styles.d.ts +1 -1
  30. package/dist/ui/styles.d.ts.map +1 -1
  31. package/dist/ui/ui_strings.d.ts +8 -1
  32. package/dist/ui/ui_strings.d.ts.map +1 -1
  33. package/dist/ui/voice_input.d.ts.map +1 -1
  34. package/package.json +1 -1
  35. package/src/constants.ts +35 -0
  36. package/src/core/ag_ui_chat.ts +452 -55
  37. package/src/core/create_http_agent.ts +14 -3
  38. package/src/core/remote_conversation_store.ts +25 -6
  39. package/src/core/run_index.ts +27 -5
  40. package/src/core/transcribe_audio.ts +20 -5
  41. package/src/core/upload_attachment.ts +13 -0
  42. package/src/core/utils.ts +18 -0
  43. package/src/dom/animations.ts +175 -31
  44. package/src/dom/dom_driver.ts +18 -12
  45. package/src/index.ts +4 -0
  46. package/src/ui/styles.ts +751 -421
  47. package/src/ui/ui_strings.ts +9 -1
  48. package/src/ui/voice_input.ts +7 -1
  49. package/src/version.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,195 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.22.0] — 2026-08-11
11
+
12
+ Two complaints about how the widget *feels*, and both turned out to be structural
13
+ rather than cosmetic.
14
+
15
+ ### Changed
16
+
17
+ - **The composer is one surface, not four boxes.** The input row was a flat flex
18
+ row — attach, mic, textarea, send — with every sibling stretched to the
19
+ textarea's two-row height. That gave a paperclip the same visual weight as the
20
+ field it sits next to and made Send a full-height filled slab. The border,
21
+ background and focus ring now belong to a wrapping `composer-surface`: the
22
+ field is borderless and **grows with what is typed** (from one row up to
23
+ `--ag-ui-composer-max-height`, then scrolls), and a `composer-tools` row
24
+ underneath carries the paperclip and mic as quiet icon buttons with a circular
25
+ Send closing the right-hand end.
26
+
27
+ Send is now icon-only. Its accessible name still comes from the `send` /
28
+ `stop` strings (`aria-label` + `title`), and the run state swaps its glyph
29
+ rather than its text, so nothing moves when a run starts. A host that sized
30
+ `::part(send)` by its padding should switch to `--ag-ui-send-size`.
31
+
32
+ - **Collapsing goes to a round floating launcher.** `collapsed` used to leave the
33
+ full-width header bar sitting on the page, which is most of a chat widget's
34
+ footprint for none of its use. The panel now scales down into a launcher in the
35
+ corner it already occupies and the launcher grows back out of that point;
36
+ `transform` and `opacity` are all that animate, so the morph is
37
+ compositor-only and cannot reflow the host page. `placement="sidebar"` keeps
38
+ its edge rail (and now genuinely *slides* out through the edge it docks
39
+ against — the transition was declared but never wired to a transform), and
40
+ `embedded` / `page` keep the header bar, being host-laid-out and full-screen
41
+ respectively.
42
+
43
+ This changes what an existing floating embed looks like when collapsed. The
44
+ collapsed host keeps its box with `pointer-events: none`, so the page beneath
45
+ stays interactive and the launcher takes the clicks.
46
+
47
+ - **The chat-history drawer and the checkpoints panel slide.** Both were toggled
48
+ with `hidden` alone, which snaps. They now keep their box and hide with
49
+ `visibility`, which is what lets a surface animate *in and out* — an element
50
+ that was never rendered has no before-change style to animate from, and one
51
+ whose `display` flips to `none` cannot animate at all.
52
+
53
+ - **The chrome's glyphs are inline SVG** (send, stop, paperclip, mic, launcher)
54
+ rather than emoji, each in a slot with the mark as its fallback:
55
+ `icon-send`, `icon-stop`, `icon-attach`, `icon-voice`, `launcher`.
56
+
57
+ ### Added
58
+
59
+ - **An unread badge on the launcher.** A collapsed widget is the one state where
60
+ an answer can arrive with nothing on screen to say so, so the launcher now
61
+ counts the answers that finished while it was closed (capped at `9+`) and
62
+ expanding marks them read. It is the only affordance here that is **on by
63
+ default**; `data-unread-badge="false"` turns the badge off, and the count keeps
64
+ running so a host chrome can render its own from the new `ag-ui-unread` event
65
+ (`UNREAD_EVENT` / `UnreadDetail`, plus a `chat.unread` getter). The count is
66
+ also the launcher's accessible name — a coloured dot says nothing to a screen
67
+ reader — via the new `expandUnread` string. Tokens:
68
+ `--ag-ui-badge-{bg,fg,size,font-size}`; part `launcher-badge`.
69
+
70
+ - **`data-launcher-icon-url`** — an icon for the collapsed launcher when it should
71
+ differ from the header's. Falls back to `data-icon-url`, so one attribute still
72
+ feeds both.
73
+
74
+ - **Motion tokens** — `--ag-ui-motion`, `--ag-ui-ease`, `--ag-ui-ease-pop`. One
75
+ duration and two curves drive every collapse, expand and slide-over. Under
76
+ `prefers-reduced-motion` the duration collapses to a frame; `--ag-ui-motion: 0s`
77
+ switches the animation off outright.
78
+
79
+ - **Launcher and composer tokens** — `--ag-ui-launcher-{size,bg,fg,radius,icon-size,inset}`,
80
+ `--ag-ui-composer-{radius,max-height}`, `--ag-ui-send-size`, `--ag-ui-tool-btn-size`,
81
+ `--ag-ui-glyph-{size,stroke}`; parts `composer-surface` and `composer-tools`.
82
+
83
+ - **Motion tests that run in a real browser.** happy-dom runs no transitions, so
84
+ every assertion about this would pass on a stylesheet where nothing animates.
85
+ The Chromium project now asserts on `getAnimations()` — that the browser
86
+ actually *started* the transitions a collapse, an expand and a drawer open are
87
+ supposed to start.
88
+
89
+ ## [0.21.0] — 2026-08-11
90
+
91
+ Ten findings from a real embed — a cross-origin, cookie-authenticated React host.
92
+ Six of them are one defect: **the component assumed it owned the page.** It had
93
+ only ever been embedded in its own playground and in `django-admin-agent`, two
94
+ hosts that both arrange the page the way it expects.
95
+
96
+ ### Added
97
+
98
+ - **`getHeaders`** — a function consulted immediately before **every** request, so
99
+ a rotating credential (a short-lived JWT, a re-issued CSRF token) reaches the
100
+ request that needs it. `headers` was a plain field the element never wrote to,
101
+ read at ten sites; configuring auth through `agentFactory` therefore
102
+ authenticated the run and nothing else, and thread history, attachments and the
103
+ catalogs went out anonymous and 401'd — which reads as a backend fault. The two
104
+ compose, merged per key with `getHeaders` winning, so adding a rotating header
105
+ cannot silently drop a static one.
106
+
107
+ - **`credentials`** (attribute and property) — `omit` / `same-origin` / `include`,
108
+ applied to every request. There was previously no occurrence of `credentials`
109
+ anywhere in the source, so every request used the browser default and a host
110
+ serving its SPA and API from different subdomains sent no cookies, with **no way
111
+ to express the fix** short of replacing the transport. An unknown value is
112
+ rejected where it was written (the property throws; the attribute logs and stays
113
+ inert) rather than becoming a 401 later.
114
+
115
+ - **`flash(el, options)`** — ring an element without moving focus.
116
+ `focusWithFlash` calls `el.focus()`, which for a helper named "flash" is
117
+ surprising: it takes focus off the composer, can fire blur validation on
118
+ whatever the user was mid-edit in, and can close a menu. Both now accept an
119
+ explicit `focus` option, and `focusWithFlash` focuses with `preventScroll: true`
120
+ so it no longer fights the scroll it just started.
121
+
122
+ - **`openThreads()` / `openCheckpoints()` / `reload()`.** The header's controls all
123
+ live inside `::part(header)`, so a host rendering its own title bar and hiding it
124
+ lost thread switching entirely. The built-in buttons call exactly these methods,
125
+ so the two routes cannot drift. `reload()` re-runs the startup fetches once
126
+ credentials that arrive late have landed.
127
+
128
+ - **`ScrollOptions`**, and `scrollIntoCenterView` now returns a promise that
129
+ resolves when the scroll has settled (`scrollend` where available, a short probe
130
+ when nothing moved, a 600 ms cap otherwise). Every DOM-driver primitive awaits it.
131
+
132
+ ### Changed
133
+
134
+ - **CSS custom properties now work from an ancestor**, which is what the README
135
+ always described and the one thing that could not work: every `--ag-ui-*` default
136
+ was declared on `:host`, which sets the property *on the element*, and an
137
+ element's own value beats anything inherited. A consumer ran the full token map
138
+ on a wrapper for an entire build and concluded they had the names wrong. The
139
+ defaults now sit behind private aliases, so `:root`, a wrapper, the element and
140
+ an inline style all work and resolve in the usual order. **One vocabulary — the
141
+ public `--ag-ui-*` names are unchanged**, and a built-in `theme` / `density` /
142
+ `placement` preset still loses to an explicit page rule.
143
+
144
+ - **The flash is an `outline`, not a `box-shadow`**, and holds for **1200 ms** with
145
+ a fade rather than 200 ms. A shadow paints outside the border box, so any
146
+ `overflow: hidden` ancestor sharing the element's box — a card, a table cell —
147
+ clipped it entirely while the tool reported success. And 200 ms is below the
148
+ threshold at which someone who does not know where to look notices anything.
149
+ ⭐ Neither our tests nor the consumer's could have found the second one:
150
+ headless Chromium hides the scroll race, and no automated check has an opinion
151
+ about whether a human sees a 200 ms ring.
152
+
153
+ - **The flash colour comes from the target's `--ag-ui-accent`** rather than a
154
+ hardcoded indigo, so a themed page is flashed in its own colour.
155
+
156
+ - **`prefersReducedMotion()` is now honoured where the README already claimed it
157
+ was.** The flash ignored it entirely while the docs said the preference was
158
+ "honoured throughout"; a consumer investigated reduced motion as the cause of an
159
+ invisible highlight on the strength of that sentence. Under reduced motion the
160
+ ring drops its fade but keeps its full hold — reduced motion asks for no
161
+ animation, not for no feedback. `typeInto` and `highlightThenClick` keep their
162
+ explicit-duration contract, and the docs now say so instead of overclaiming.
163
+
164
+ - **The tool and skill catalog fetches are deferred by one microtask**, so a React
165
+ `ref` assigned in the same commit as insertion is honoured. ⚠ The thread-history
166
+ request is deliberately **not** deferred: a deferred replay can land after a
167
+ `sendMessage()` and duplicate the transcript. Configure before you insert, or
168
+ call `reload()`; the new React recipe in the README shows both.
169
+
170
+ ### Fixed
171
+
172
+ - **The resize grip now sits on the corner that actually moves.** The element
173
+ measures which edges its host's layout holds still and stamps them as
174
+ `data-resize-anchor="<y>-<x>"`, but the rules meant to read it were written as
175
+ `[data-resize-anchor~="left"]` — and `~=` matches whitespace-separated words
176
+ while the stamped value is a single hyphenated token, so they could never
177
+ match. ⚠ **The cursor rules used `=` and did match**, so the pointer followed
178
+ the measurement while the grip stayed where `placement` had guessed: for any
179
+ host that aligns the panel the other way, the cursor promised a diagonal the
180
+ grip was not on, and the grip sat on the corner that stays put.
181
+
182
+ Each anchor rule now sets **both** sides of its axis. One that flipped a
183
+ single way could not undo a placement guess that had flipped the other, which
184
+ put the grip back on the anchored corner for an embedded panel its host
185
+ right-aligns.
186
+
187
+ - **`placement="page"` gutters its composer to the same reading column as its
188
+ messages.** The rule was unscoped, which tied it on specificity with the base
189
+ `.input-row` rule that sets the `padding` shorthand later in the stylesheet;
190
+ source order decided and the shorthand won. ⇒ *No placement ever got the
191
+ gutter* — least of all the one it was written for, where the messages sat in a
192
+ centred column and the composer spanned the full width.
193
+
194
+ A duplicated copy of the whole resize block also sat earlier in the file,
195
+ welded to the preceding rule by a comment left between a selector and its
196
+ subject; it parsed as `:host([placement="page"]) .resize-handle`, scoping the
197
+ grip's base positioning to the one placement that hides it.
198
+
10
199
  ## [0.20.1] — 2026-08-11
11
200
 
12
201
  ### Fixed
@@ -1061,7 +1250,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1061
1250
  ### Notes
1062
1251
  - First release — exercising the automated npm OIDC publish pipeline end-to-end.
1063
1252
 
1064
- [Unreleased]: https://github.com/Artui/ag-ui-web-component/compare/v0.20.1...HEAD
1253
+ [Unreleased]: https://github.com/Artui/ag-ui-web-component/compare/v0.22.0...HEAD
1254
+ [0.22.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.21.0...v0.22.0
1255
+ [0.21.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.20.1...v0.21.0
1065
1256
  [0.20.1]: https://github.com/Artui/ag-ui-web-component/compare/v0.20.0...v0.20.1
1066
1257
  [0.20.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.19.0...v0.20.0
1067
1258
  [0.19.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.18.0...v0.19.0