@artooi/ag-ui-web-component 0.33.1 → 0.35.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.
- package/CHANGELOG.md +563 -1
- package/README.md +279 -16
- package/dist/ag-ui-web-component.bundle.js +676 -101
- package/dist/ag-ui-web-component.bundle.js.map +4 -4
- package/dist/constants.d.ts +80 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/core/ag_ui_chat.d.ts +37 -2
- package/dist/core/ag_ui_chat.d.ts.map +1 -1
- package/dist/dom/animations.d.ts +14 -0
- package/dist/dom/animations.d.ts.map +1 -1
- package/dist/dom/highlight_overlay.d.ts +47 -0
- package/dist/dom/highlight_overlay.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2288 -388
- package/dist/index.js.map +4 -4
- package/dist/tools/chat_surface_tools.d.ts +96 -0
- package/dist/tools/chat_surface_tools.d.ts.map +1 -0
- package/dist/tools/page_action_tools.d.ts +2 -0
- package/dist/tools/page_action_tools.d.ts.map +1 -1
- package/dist/ui/chart_block.d.ts +18 -0
- package/dist/ui/chart_block.d.ts.map +1 -1
- package/dist/ui/clamp_launcher.d.ts +10 -5
- package/dist/ui/clamp_launcher.d.ts.map +1 -1
- package/dist/ui/clamp_panel.d.ts +13 -0
- package/dist/ui/clamp_panel.d.ts.map +1 -0
- package/dist/ui/launcher_drag.d.ts +2 -2
- package/dist/ui/launcher_drag.d.ts.map +1 -1
- package/dist/ui/launcher_placement.d.ts +14 -1
- package/dist/ui/launcher_placement.d.ts.map +1 -1
- package/dist/ui/panel_drag.d.ts +40 -0
- package/dist/ui/panel_drag.d.ts.map +1 -0
- package/dist/ui/place_widget.d.ts +31 -0
- package/dist/ui/place_widget.d.ts.map +1 -0
- package/dist/ui/run_notice.d.ts +14 -3
- package/dist/ui/run_notice.d.ts.map +1 -1
- package/dist/ui/styles.d.ts +1 -1
- package/dist/ui/styles.d.ts.map +1 -1
- package/dist/ui/thread_drawer.d.ts +21 -0
- package/dist/ui/thread_drawer.d.ts.map +1 -1
- package/dist/ui/ui_strings.d.ts +16 -0
- package/dist/ui/ui_strings.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +87 -0
- package/src/core/ag_ui_chat.ts +1146 -38
- package/src/dom/animations.ts +30 -0
- package/src/dom/highlight_overlay.ts +256 -0
- package/src/index.ts +12 -0
- package/src/tools/chat_surface_tools.ts +207 -0
- package/src/tools/page_action_tools.ts +2 -0
- package/src/ui/chart_block.ts +222 -57
- package/src/ui/clamp_launcher.ts +25 -7
- package/src/ui/clamp_panel.ts +30 -0
- package/src/ui/launcher_drag.ts +11 -2
- package/src/ui/launcher_placement.ts +50 -60
- package/src/ui/panel_drag.ts +138 -0
- package/src/ui/place_widget.ts +64 -0
- package/src/ui/run_notice.ts +32 -3
- package/src/ui/styles.ts +622 -47
- package/src/ui/thread_drawer.ts +138 -8
- package/src/ui/ui_strings.ts +24 -0
- package/src/version.ts +1 -1
package/README.md
CHANGED
|
@@ -183,17 +183,23 @@ another origin, add `credentials="include"` too; see
|
|
|
183
183
|
| `data-icon-url` | — | Header (and launcher) icon image URL. A slotted `slot="icon"` wins; see [Header & launcher icon](#header-and-launcher-icon). |
|
|
184
184
|
| `data-launcher-icon-url` | — | Icon image URL for the collapsed launcher only, when it should differ from the header's. Falls back to `data-icon-url`; a slotted `slot="launcher"` wins over both. |
|
|
185
185
|
| `data-unread-badge` | — | **On by default.** `="false"` hides the launcher's unread badge; the count and the `ag-ui-unread` event keep running. See [Collapsing to the launcher](#collapsing-to-the-launcher). |
|
|
186
|
-
| `data-launcher-drag` | — | **On by default.** `="false"` leaves the
|
|
186
|
+
| `data-launcher-drag` | — | **On by default.** `="false"` leaves the widget wherever your CSS puts it. Otherwise the collapsed launcher can be dragged anywhere on screen (the panel opens into the clearest space) and the open panel can be dragged by its header. See [Moving the launcher](#moving-the-launcher) and [Moving the panel](#moving-the-panel). |
|
|
187
187
|
| `data-quote-selection` | — | **On by default.** `="false"` stops the transcript offering to quote a selection. `quote()` keeps working either way. See [Quoting a selection](#quoting-a-selection). |
|
|
188
188
|
| `data-message-actions` | — | **All on by default.** A comma list of the actions a finished answer keeps: `copy` / `retry` / `feedback` (e.g. `"copy,retry"`). `="false"` removes the row entirely. See [Message actions](#message-actions-copy-retry-feedback). |
|
|
189
189
|
| `data-max-tool-rounds` | — | Upper bound on frontend tool-call → re-run rounds within one send (default 10; a value below 1 is ignored). Raise it for a page-driving agent whose turn takes many small steps. See [The run loop](#the-run-loop-and-the-ag-ui-client). |
|
|
190
|
-
| `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). |
|
|
190
|
+
| `data-page-actions` | — | Opt-in built-in page-action tools: a comma list of `scroll` / `drag` / `chat` (e.g. `"scroll,drag"`). See [Page-action tools](#page-action-tools). |
|
|
191
191
|
| `data-side` | — | CSS-only, for `placement="sidebar"`: which edge it docks to — `right` (default) / `left`. |
|
|
192
192
|
| `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). |
|
|
193
|
-
| `collapsed` | `collapsed` | Reflected boolean; collapses the widget to its [launcher](#collapsing-to-the-launcher) (a rail under `placement="sidebar"`, the header bar under `embedded`
|
|
193
|
+
| `collapsed` | `collapsed` | Reflected boolean; collapses the widget to its [launcher](#collapsing-to-the-launcher) (a rail under `placement="sidebar"`, the header bar under `embedded`). Persisted per tab. `placement="page"` has no collapsed state and ignores it. |
|
|
194
|
+
| `data-dragging` | — | **Written by the element, not by you.** Stamped on whichever handle a gesture is currently using, so the styles can react and so the element knows not to re-place the widget under a drag in progress. Cleared on `pointerup` and on `pointercancel`. |
|
|
195
|
+
| `data-expand-corner` | — | **Written by the element, not by you.** It stamps the corner a dragged or agent-moved panel opens from, so the collapse animation starts where the panel actually is. Listed because the element reads its own stamp back; setting it yourself is overwritten on the next move. |
|
|
196
|
+
| `data-small-viewport` | — | CSS-only: `off` keeps the desktop layout at every width, opting out of the [small-viewport override](#small-viewports). Everything that override sets is a token you can re-state; its trigger is a media query, which is the one thing you cannot. |
|
|
197
|
+
| `data-paste-attach` | — | When to turn a long text paste into an attachment instead of composer text: absent for the 5000-character default, `off` to never, or a positive number of characters. Only acts where `data-attachments-url` (or a custom `uploadHandler`) gives it somewhere to go. |
|
|
198
|
+
| `data-starters` | — | JSON array of prompts offered on an empty transcript, e.g. `'["Summarise this page"]'`. Fallback content for `slot="empty"`, so slotting your own replaces them. Shares the four-prompt and 120-character limits with the suggestion chips a run pushes. Read once at connect. |
|
|
199
|
+
| `data-start-open` | — | Mount the panel open on a first visit. The corner placements otherwise rest at their launcher, the way every corner chat does; a stored choice wins over both. The placements that place themselves are unaffected. |
|
|
194
200
|
| `theme` | — | CSS-only: `light` (default) / `dark` / `auto` / `code`. |
|
|
195
201
|
| `density` | — | CSS-only: `comfortable` (default) / `compact`. |
|
|
196
|
-
| `placement` | — | CSS-only: `floating` (default) / `
|
|
202
|
+
| `placement` | — | CSS-only: `floating` (default) / `sidebar` / `page` / `embedded`. |
|
|
197
203
|
|
|
198
204
|
Each header control also takes its own icon slot — `icon-history`, `icon-checkpoints`,
|
|
199
205
|
`icon-new`, `icon-collapse` — with the built-in glyph as the fallback, so a host can project a
|
|
@@ -268,7 +274,8 @@ the `copyCode` / `copied` / `copyFailed` strings.
|
|
|
268
274
|
|
|
269
275
|
**Methods**: `registerTool`, `registerPageState`, `registerActivityRenderer`, `setSkills`,
|
|
270
276
|
`sendMessage`, `attachFile`, `appendMessage`, `retryLastTurn`, `quote`, `offerQuoteInPage`,
|
|
271
|
-
`enableCharts`, `newChat`, `setCollapsed`, `toggleCollapsed`, `
|
|
277
|
+
`enableCharts`, `newChat`, `setCollapsed`, `toggleCollapsed`, `describeSurface`, `moveTo`,
|
|
278
|
+
`toggleTheme`, `openThreads`, `closeThreads`,
|
|
272
279
|
`openCheckpoints`, `closeCheckpoints`, `toggleCheckpoints`, `reload`, and the deprecated
|
|
273
280
|
`registerStateHook` (renamed to `registerPageState`).
|
|
274
281
|
|
|
@@ -812,6 +819,44 @@ operations a tool handler typically wants:
|
|
|
812
819
|
flash defaults to `flashMs: 0` here: the field is about to be typed into, which is its own
|
|
813
820
|
highlight. Pass `flashMs` (and optionally `color`) to ring it first.
|
|
814
821
|
- `clickElement(el, options)` / `pressButton(el, options)` — scroll to, highlight/press, and click.
|
|
822
|
+
- `showHighlightOverlay(el, options)` — ring an element from an **overlay drawn outside it**, and
|
|
823
|
+
optionally dim everything else (`scrim`) or flow a gradient round it (`gradient`). Returns a
|
|
824
|
+
function that removes it. `flash` and `focusWithFlash` take `scrim` and `gradient` too and route
|
|
825
|
+
through this when either is asked for.
|
|
826
|
+
|
|
827
|
+
**Why a second mechanism.** The plain ring is an `outline` on the element, which is deliberate:
|
|
828
|
+
a `box-shadow` paints outside the border box, so an `overflow: hidden` ancestor sharing the
|
|
829
|
+
target's box clips the whole ring away while the helper still reports success. But an outline
|
|
830
|
+
takes a *colour* — there is no `outline-image` — so a gradient cannot be one, and anything else
|
|
831
|
+
that can be is a property of the target and lands back inside whatever is clipping it. Dimming
|
|
832
|
+
everything else needs a surface larger than the target, which is the same problem from the other
|
|
833
|
+
side. So they are one overlay rather than two features.
|
|
834
|
+
|
|
835
|
+
**It is inert.** The overlay never takes a pointer event, at the cut-out or anywhere else — a dim
|
|
836
|
+
that swallows clicks is a modal the user did not open, and `highlightThenClick` has to reach the
|
|
837
|
+
control it just finished pointing at. It follows the target on scroll and resize, and under
|
|
838
|
+
reduced motion the gradient is drawn but does not travel.
|
|
839
|
+
|
|
840
|
+
**Themed from the element you point at, not from the widget.** The overlay is appended to the
|
|
841
|
+
document body so it can escape the clipping it exists to avoid, which means a `var()` in its own
|
|
842
|
+
style would resolve against the body — so every token is read from the *target's* computed style
|
|
843
|
+
instead, the same place the flat ring reads `--ag-ui-accent`. Set them wherever they inherit to
|
|
844
|
+
the elements the agent touches, usually `:root`:
|
|
845
|
+
|
|
846
|
+
```css
|
|
847
|
+
:root {
|
|
848
|
+
--ag-ui-highlight-scrim: rgba(15, 15, 25, 0.45);
|
|
849
|
+
--ag-ui-highlight-gradient: linear-gradient(115deg, transparent 20%, #4f46e5 50%, transparent 80%);
|
|
850
|
+
--ag-ui-highlight-ring-width: 3; /* unitless; px */
|
|
851
|
+
--ag-ui-highlight-flow-ms: 2400; /* one pass of the gradient */
|
|
852
|
+
--ag-ui-highlight-z-index: 2147483001;
|
|
853
|
+
}
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
`ringWidth` and `flowMs` options override the tokens per call; `color`, `padding` and `radius`
|
|
857
|
+
have no token because they are per-target rather than per-theme. The overlay's own styles are
|
|
858
|
+
inline and it lives in the light DOM, so neither a stylesheet rule nor `::part` can reach it —
|
|
859
|
+
these tokens and options are the whole surface, which is why they cover every value it draws.
|
|
815
860
|
- `selectControl(el, value)` / `toggleCheckbox(el, checked)` — animate a `<select>` / checkbox.
|
|
816
861
|
- `setControlValue(el, value)` — set a `<select>` or checkbox without animation, dispatching
|
|
817
862
|
`input`/`change`.
|
|
@@ -861,6 +906,22 @@ want — so you control the agent's interaction surface:
|
|
|
861
906
|
- **`drag_and_drop`** — drag the `from` element onto the `to` element (selectors / page-map ids),
|
|
862
907
|
firing the standard HTML5 drag sequence (`dragstart` → `dragenter`/`dragover`/`drop` → `dragend`)
|
|
863
908
|
so the page's own drop handler reacts. Useful for reordering sortable lists.
|
|
909
|
+
- **`chat`** — four tools that let the agent move the panel it is speaking from:
|
|
910
|
+
`read_chat_surface`, `move_chat`, `minimise_chat`, `restore_chat`.
|
|
911
|
+
|
|
912
|
+
This is the one nobody else can offer. Every other assistant's chat is a surface of its own, so
|
|
913
|
+
there is nothing for it to be in the way *of*; ours is mounted in the page the user is working
|
|
914
|
+
in, which makes "let me move this aside so you can see the table" something the agent can act on
|
|
915
|
+
rather than apologise for.
|
|
916
|
+
|
|
917
|
+
**They report what happened, not what was asked.** A panel that fills the screen has nowhere to
|
|
918
|
+
move to, and a placement that places itself owns its position — so `move_chat` answers
|
|
919
|
+
`moved: false` with the reason and what would work instead, rather than reporting success on a
|
|
920
|
+
panel that did not budge. `read_chat_surface` is there so the agent can ask before it acts
|
|
921
|
+
instead of learning through a failure; it is read-only.
|
|
922
|
+
|
|
923
|
+
None of them is stamped `x-destructive`. Moving a window destroys nothing, and a confirmation
|
|
924
|
+
card in front of it would be worse than the move.
|
|
864
925
|
|
|
865
926
|
**Your drag surface must listen to drag events, and many "modern" ones do not.** `drag_and_drop`
|
|
866
927
|
dispatches the native HTML5 sequence with one shared `DataTransfer`. A surface built on a
|
|
@@ -907,6 +968,13 @@ matching JS API:
|
|
|
907
968
|
server-backed store it stays on the server. Deleting one is the drawer row's own action. A chat
|
|
908
969
|
nothing was ever sent in is the exception — it was never listed, so it is dropped rather than
|
|
909
970
|
left behind.
|
|
971
|
+
- `describeSurface()` — where the panel is and what can be done to it: placement, collapsed,
|
|
972
|
+
whether it can be moved, whether it fills the screen, its box and the viewport. `movable` folds
|
|
973
|
+
the two reasons a move can fail into the one answer a caller needs.
|
|
974
|
+
- `moveTo(corner)` — send the panel to `top-left` / `top-right` / `bottom-left` / `bottom-right`,
|
|
975
|
+
returning whether it went. It takes the axes the same way a user drag does, so the launcher
|
|
976
|
+
travels with it and switching placement hands them back. Returns `false` rather than pretending
|
|
977
|
+
when the placement owns its position or the panel fills the screen.
|
|
910
978
|
- `setCollapsed(collapsed)` / `toggleCollapsed()` — collapse or expand the widget. The state is
|
|
911
979
|
reflected as the boolean `collapsed` attribute/property and persisted per-tab in
|
|
912
980
|
`sessionStorage`, so it survives a reload.
|
|
@@ -921,6 +989,25 @@ chat.toggleCollapsed();
|
|
|
921
989
|
chat.addEventListener("ag-ui-toggle", (e) => console.log(e.detail.collapsed));
|
|
922
990
|
```
|
|
923
991
|
|
|
992
|
+
### The composer's own keys
|
|
993
|
+
|
|
994
|
+
**Enter during a run queues.** A second run cannot start while one is in flight —
|
|
995
|
+
it would orphan the first — so that key used to do nothing at all, silently. What
|
|
996
|
+
is waiting shows above the composer as chips, each of which takes its message
|
|
997
|
+
back when pressed, and the next one is sent when the run settles. Stopping the
|
|
998
|
+
run discards them: sending into a conversation someone has just stopped is the
|
|
999
|
+
opposite of what stopping meant. It is not thrown away, though — a queued
|
|
1000
|
+
message has already left the composer, so it goes to the front of the recall
|
|
1001
|
+
history below rather than nowhere.
|
|
1002
|
+
|
|
1003
|
+
The composer also walks back through what you have already sent, on **Up** and
|
|
1004
|
+
**Down** — the shape every shell and every coding agent uses. Only from an empty
|
|
1005
|
+
composer and only with the skills palette closed: an arrow inside text is how you
|
|
1006
|
+
move the caret, and taking it unconditionally would break editing to add a
|
|
1007
|
+
shortcut. Arrowing forward past the newest turn empties the box again, so the way
|
|
1008
|
+
out is the key that got you in. The history is this conversation's: starting a
|
|
1009
|
+
new chat, switching threads or changing `user-key` clears it with the transcript.
|
|
1010
|
+
|
|
924
1011
|
### Collapsing to the launcher
|
|
925
1012
|
|
|
926
1013
|
A collapsed widget shrinks to a round **floating launcher** in the corner it already occupies: the
|
|
@@ -997,6 +1084,34 @@ against a placement. Switching to a placement that places itself hands both back
|
|
|
997
1084
|
> property and your CSS decides, exactly as before. The geometry is built so that feeding it the
|
|
998
1085
|
> resting position reproduces the default `auto 24px 24px auto` unchanged.
|
|
999
1086
|
|
|
1087
|
+
#### Moving the panel
|
|
1088
|
+
|
|
1089
|
+
An open panel moves by its **header**, the way a window moves by its title bar.
|
|
1090
|
+
|
|
1091
|
+
**The launcher travels the same distance.** Drag the panel 100px left and the bubble it collapses
|
|
1092
|
+
into is 100px left of where it was — it is one widget being moved, not two things being placed.
|
|
1093
|
+
The distance is the panel's own, so a panel held against the viewport's margin stops and the
|
|
1094
|
+
bubble stops with it, and the bubble is then held on screen in its own right.
|
|
1095
|
+
|
|
1096
|
+
**A position you state is kept, not re-derived.** That is the difference between the two drags: a
|
|
1097
|
+
launcher drag says where the bubble goes and lets the panel open into whatever room the viewport
|
|
1098
|
+
has, which is re-decided on every expand and every window resize; a header drag states the
|
|
1099
|
+
panel's own position, so it survives collapsing, reopening and reloading. Dragging the bubble
|
|
1100
|
+
again hands the decision back. Both are stored per tab, and `data-launcher-drag="false"` turns
|
|
1101
|
+
off both.
|
|
1102
|
+
|
|
1103
|
+
Which corner the panel is *pinned* by is re-picked when the drag ends, from where the bubble has
|
|
1104
|
+
ended up, so the next expand still opens into clear space. Re-picking it moves nothing: the
|
|
1105
|
+
corner only says which edges the two insets are written from, and both are written from positions
|
|
1106
|
+
that are already decided.
|
|
1107
|
+
|
|
1108
|
+
The controls in the header keep their own presses — a drag started on a button, a link or a field
|
|
1109
|
+
never begins, including one you slot in.
|
|
1110
|
+
|
|
1111
|
+
There is no keyboard shortcut on the header, deliberately. A header is not a control, and making
|
|
1112
|
+
it focusable would put a tab stop with no role ahead of the controls a keyboard user came for —
|
|
1113
|
+
while arrow keys on the collapsed launcher already move the widget, panel included.
|
|
1114
|
+
|
|
1000
1115
|
```js
|
|
1001
1116
|
chat.unread; // 2
|
|
1002
1117
|
|
|
@@ -1074,8 +1189,24 @@ label — a shorter one misaligns every value after the gap, and a chart that is
|
|
|
1074
1189
|
subtly wrong still reads as authoritative, so the whole spec is dropped instead.
|
|
1075
1190
|
A pie's slices are its labels, so it draws the first series only.
|
|
1076
1191
|
|
|
1077
|
-
Theme the series with `--ag-ui-chart-1` … `--ag-ui-chart-6`,
|
|
1078
|
-
|
|
1192
|
+
Theme the series with `--ag-ui-chart-1` … `--ag-ui-chart-6`, size it with
|
|
1193
|
+
`--ag-ui-chart-max-width`, and style the block through the `chart-block`,
|
|
1194
|
+
`chart-title` and `chart-legend` parts.
|
|
1195
|
+
|
|
1196
|
+
**A chart stops at its own width, and is sized in pixels rather than scaled to
|
|
1197
|
+
them.** Two separate things, and a widened panel needs both.
|
|
1198
|
+
|
|
1199
|
+
It is drawn for the width the block actually has -- one SVG unit per CSS pixel
|
|
1200
|
+
-- and redrawn when that width changes, so a 10px axis label is 10px in a 380px
|
|
1201
|
+
panel and in a 1200px one. Where the labels no longer fit, the axis draws every
|
|
1202
|
+
second or third rather than a smear of overlapping words. Below 220px it goes
|
|
1203
|
+
back to scaling, since at that size nothing fits either way.
|
|
1204
|
+
|
|
1205
|
+
And it takes the width it needs rather than the width it is offered, capping at
|
|
1206
|
+
`--ag-ui-chart-max-width` (**480px**): widening the panel should no more resize
|
|
1207
|
+
a chart than it resizes a message. Raise the token for a bigger chart and the
|
|
1208
|
+
labels stay 10px -- the cap is about how much room the drawing gets, never
|
|
1209
|
+
about how big its type is. Height follows width inside a 160-320px band.
|
|
1079
1210
|
|
|
1080
1211
|
### Drawing something other than a chart
|
|
1081
1212
|
|
|
@@ -1320,9 +1451,9 @@ server's text.
|
|
|
1320
1451
|
The panel carries a grip on **every edge and every corner**, so it can be
|
|
1321
1452
|
dragged from whichever side you are already near.
|
|
1322
1453
|
|
|
1323
|
-
- `placement="
|
|
1454
|
+
- `placement="page"` gets **no grips** — a full-bleed layout
|
|
1324
1455
|
is `100vw`/`100vh` by definition, so there is nothing to drag.
|
|
1325
|
-
- `placement="sidebar"`
|
|
1456
|
+
- `placement="sidebar"` keeps only the two vertical edges;
|
|
1326
1457
|
the placement owns the height, so a horizontal edge or a corner would
|
|
1327
1458
|
advertise a drag that does nothing.
|
|
1328
1459
|
- Everything else gets all eight.
|
|
@@ -2269,6 +2400,12 @@ re-export point. Internal modules import from leaf paths.
|
|
|
2269
2400
|
| `isNavigates(parameters)` | function | Read the `x-navigates` flag. |
|
|
2270
2401
|
| `createPageActionTools(enabled, resolveTarget)` | function | Build the opt-in `scroll_to` / `drag_and_drop` tools. |
|
|
2271
2402
|
| `PAGE_ACTIONS` | const | The page-action opt-in tokens (`scroll` / `drag`). |
|
|
2403
|
+
| `createChatSurfaceTools(surface)` | function | Build the opt-in `read_chat_surface` / `move_chat` / `minimise_chat` / `restore_chat` tools, which let the agent move the panel it is speaking from. |
|
|
2404
|
+
| `ChatSurface` | type | The narrow port those tools drive — `describeSurface` / `moveTo` / `setCollapsed`. The element satisfies it. |
|
|
2405
|
+
| `ChatSurfaceReport` | type | What `describeSurface()` answers: placement, collapsed, collapsible, movable, draggable, fullBleed, and the panel's box against the viewport it sits in. |
|
|
2406
|
+
| `ChatCorner` | type | `"top-left"` / `"top-right"` / `"bottom-left"` / `"bottom-right"` — the argument to `moveTo`. |
|
|
2407
|
+
| `CHAT_CORNERS` | const | Those four, as a list. |
|
|
2408
|
+
| `isChatCorner(value)` | function | Whether a string names one of them. |
|
|
2272
2409
|
| `ResolvePageTarget` | type | `(target) => HTMLElement \| null` — the page-target resolver. |
|
|
2273
2410
|
| `X_DESTRUCTIVE_KEY` / `X_NAVIGATES_KEY` | const | The JSON-Schema extension keys. |
|
|
2274
2411
|
| `parseToolCatalog(data)` | function | Parse a fetched `data-tools-url` catalog into a `Record<string, ToolCatalogEntry>` — whole entries, not bare summaries, so a caller can reach `description` too. Malformed input yields an empty map rather than throwing. |
|
|
@@ -2369,6 +2506,8 @@ re-export point. Internal modules import from leaf paths.
|
|
|
2369
2506
|
| `asQuote(text)` | function | Shape text as a markdown blockquote with a blank line after it. |
|
|
2370
2507
|
| `MAX_QUOTE_CHARS` | const | The cap a quotation is truncated to (500). |
|
|
2371
2508
|
| `typeInto` / `highlightThenClick` / `pressThenClick` / `selectOption` / `toggleControl` / `scrollIntoCenterView` / `flash` / `focusWithFlash` / `prefersReducedMotion` | function | Animation primitives. |
|
|
2509
|
+
| `showHighlightOverlay` | function | Ring a host-page element from an overlay drawn outside it, optionally dimming everything else or flowing a gradient round it. Returns a function that removes it. |
|
|
2510
|
+
| `HighlightOverlayOptions` | type | Options for `showHighlightOverlay`. |
|
|
2372
2511
|
| `fillField` / `clickElement` / `pressButton` / `selectControl` / `setControlValue` / `toggleCheckbox` | function | DOM-driver primitives. |
|
|
2373
2512
|
| `setNativeValue` / `setNativeChecked` | function | Set a control via its native prototype setter (React-controlled inputs). |
|
|
2374
2513
|
| `TypeOptions` / `HighlightClickOptions` / `PressOptions` / `SelectOptions` / `ToggleOptions` / `FlashOptions` / `ScrollOptions` / `FillFieldOptions` / `TextLikeElement` | type | Primitive option shapes. |
|
|
@@ -2434,6 +2573,8 @@ ag-ui-chat {
|
|
|
2434
2573
|
--ag-ui-bg: #ffffff;
|
|
2435
2574
|
--ag-ui-fg: #1a1a2e;
|
|
2436
2575
|
--ag-ui-radius: 12px;
|
|
2576
|
+
--ag-ui-header-btn-size: 30px; /* the header's controls; 44px on touch */
|
|
2577
|
+
--ag-ui-header-gap: 4px;
|
|
2437
2578
|
|
|
2438
2579
|
/* What is drawn on top of the accent and danger fills. Change these with
|
|
2439
2580
|
the fills: a pale accent leaves white-on-pale everywhere they are used. */
|
|
@@ -2448,6 +2589,117 @@ ag-ui-chat {
|
|
|
2448
2589
|
}
|
|
2449
2590
|
```
|
|
2450
2591
|
|
|
2592
|
+
### Small viewports
|
|
2593
|
+
|
|
2594
|
+
At **600px wide and below** every placement but `embedded` becomes one full-bleed
|
|
2595
|
+
shape: edge to edge, no radius, no shadow, no resize grips. A phone is not an
|
|
2596
|
+
eighth placement — it is an override that collapses the others onto one of them.
|
|
2597
|
+
The host picked a placement for the desktop it was designing, and a 380x560
|
|
2598
|
+
panel with a 24px margin is not a smaller version of that decision, it is most
|
|
2599
|
+
of the screen with a frame drawn round it.
|
|
2600
|
+
|
|
2601
|
+
`embedded` is left alone deliberately: it sits in a box you sized and placed, and
|
|
2602
|
+
only you know whether that column should become the whole screen.
|
|
2603
|
+
|
|
2604
|
+
The corner placements still rest at their launcher, so a full-bleed panel is
|
|
2605
|
+
something the user opens rather than something they are given.
|
|
2606
|
+
|
|
2607
|
+
### The conversation list on a full page
|
|
2608
|
+
|
|
2609
|
+
On `placement="page"`, once the panel is at least **900px** wide, the chat-history
|
|
2610
|
+
list **docks beside the transcript** instead of covering it — no backdrop, no
|
|
2611
|
+
focus trap, and `role="region"` rather than a modal dialog. Covering the
|
|
2612
|
+
conversation to show the list of conversations hides the thing you are trying to
|
|
2613
|
+
get back to, and a dedicated route is the one surface with width to spare.
|
|
2614
|
+
|
|
2615
|
+
Narrower than that, or under any other placement, it stays the slide-over it was:
|
|
2616
|
+
a few hundred pixels of panel with a list docked into it leaves a column of
|
|
2617
|
+
transcript narrower than the messages in it. Width alone is not the test — an app
|
|
2618
|
+
shell can hand `embedded` a page-sized box, and that box is still a column of
|
|
2619
|
+
somebody's layout.
|
|
2620
|
+
|
|
2621
|
+
`--ag-ui-threads-rail-width` sets the docked width (default 280px). While it is
|
|
2622
|
+
docked the host carries `data-threads-docked`, so your own CSS can react.
|
|
2623
|
+
|
|
2624
|
+
The list also grows a filter once there are eight or more conversations in it
|
|
2625
|
+
— above that a search box is worth having, below it it is a control asking to be
|
|
2626
|
+
used on a list you can already read in one glance. It matches the title **and**
|
|
2627
|
+
the preview, because the title is often the model's one-line summary and the
|
|
2628
|
+
phrase you remember is as likely to be inside the conversation as on it, and it
|
|
2629
|
+
filters what the drawer already holds rather than going back to the server for a
|
|
2630
|
+
list that is already in memory.
|
|
2631
|
+
|
|
2632
|
+
**To keep your desktop layout at every width**, set `data-small-viewport="off"`.
|
|
2633
|
+
That exists because the *trigger* is the one part of this you cannot reach: every
|
|
2634
|
+
value the override sets is a `--ag-ui-*` token you can re-state, but a media
|
|
2635
|
+
query cannot read a custom property, so the breakpoint itself is a literal.
|
|
2636
|
+
|
|
2637
|
+
The breakpoint is a width rather than a pointer test, and that is on purpose: a
|
|
2638
|
+
touch laptop is coarse-pointered and wide, a narrow desktop window is
|
|
2639
|
+
fine-pointered and small. Width decides the layout; the pointer decides which
|
|
2640
|
+
controls make sense.
|
|
2641
|
+
|
|
2642
|
+
### Reserving the space your own chrome occupies
|
|
2643
|
+
|
|
2644
|
+
A fixed placement covers the viewport it is given, and it does not know about
|
|
2645
|
+
your sticky header. Tell it which edges are already spent and every placement
|
|
2646
|
+
does its own arithmetic:
|
|
2647
|
+
|
|
2648
|
+
```css
|
|
2649
|
+
ag-ui-chat {
|
|
2650
|
+
--ag-ui-viewport-inset-top: 64px; /* your nav bar */
|
|
2651
|
+
}
|
|
2652
|
+
```
|
|
2653
|
+
|
|
2654
|
+
`page` and `full` inset by all four edges; `sidebar` and `side` by three, leaving
|
|
2655
|
+
the docked edge free; `floating` adds them to its own margins.
|
|
2656
|
+
**The heights follow on their own** — that is the point of these rather than
|
|
2657
|
+
restating `--ag-ui-inset` per placement, which leaves you to keep
|
|
2658
|
+
`--ag-ui-height` and `--ag-ui-max-height` in step by hand and overflows the panel
|
|
2659
|
+
off the bottom of the screen the one time you forget.
|
|
2660
|
+
|
|
2661
|
+
Four longhands rather than one shorthand because a custom property is a token
|
|
2662
|
+
stream and CSS cannot index one; the height arithmetic needs the vertical pair on
|
|
2663
|
+
its own. Any CSS length works — `px`, `rem`, `env(safe-area-inset-*)`, or a
|
|
2664
|
+
`calc()` combining them; the widget reads the resolved value rather than the text
|
|
2665
|
+
you wrote, so the number it clamps against is the one the stylesheet uses:
|
|
2666
|
+
|
|
2667
|
+
```css
|
|
2668
|
+
ag-ui-chat {
|
|
2669
|
+
--ag-ui-viewport-inset-top: env(safe-area-inset-top);
|
|
2670
|
+
--ag-ui-viewport-inset-bottom: env(safe-area-inset-bottom);
|
|
2671
|
+
}
|
|
2672
|
+
```
|
|
2673
|
+
|
|
2674
|
+
If your chrome changes height — a bar that wraps at narrow widths — measure it
|
|
2675
|
+
and publish the value, since no CSS length tracks it:
|
|
2676
|
+
|
|
2677
|
+
```js
|
|
2678
|
+
new ResizeObserver(() => {
|
|
2679
|
+
document.documentElement.style.setProperty("--bar-h", `${bar.offsetHeight}px`);
|
|
2680
|
+
}).observe(bar);
|
|
2681
|
+
```
|
|
2682
|
+
|
|
2683
|
+
```css
|
|
2684
|
+
ag-ui-chat { --ag-ui-viewport-inset-top: var(--bar-h, 0px); }
|
|
2685
|
+
```
|
|
2686
|
+
|
|
2687
|
+
`--ag-ui-edge-gutter` (default `24px`) is the gap a resting `floating` panel
|
|
2688
|
+
keeps between itself and that box. The size cap subtracts the same one, so a
|
|
2689
|
+
panel grown to its limit reaches the far edge of the usable box and no further —
|
|
2690
|
+
set it to `0` for a panel flush against the corner.
|
|
2691
|
+
|
|
2692
|
+
`--ag-ui-keyboard-inset` overrides the lift an on-screen keyboard earns. The
|
|
2693
|
+
widget measures the hidden band and publishes it as
|
|
2694
|
+
`--ag-ui-visual-viewport-inset-bottom`; state this one instead to outrank that
|
|
2695
|
+
measurement, or set it to `0px` to opt out of the lift entirely.
|
|
2696
|
+
|
|
2697
|
+
`--ag-ui-viewport-height` and `--ag-ui-viewport-width` state the usable box
|
|
2698
|
+
outright, for the case where no viewport-percentage length describes it. An
|
|
2699
|
+
on-screen keyboard is the one that matters: it changes neither `vh` nor `dvh` nor
|
|
2700
|
+
`svh` on any current mobile browser, so a full-bleed panel has to be told the
|
|
2701
|
+
visual viewport's height rather than deriving it.
|
|
2702
|
+
|
|
2451
2703
|
Marks are variables too, so one vocabulary covers a re-theme rather than
|
|
2452
2704
|
leaving half the transcript in the built-in set: `--ag-ui-tool-icon-done` /
|
|
2453
2705
|
`-error` / `-declined` for tool status, and `--ag-ui-disclosure-collapsed` /
|
|
@@ -2504,9 +2756,17 @@ have to hand-tune the variables:
|
|
|
2504
2756
|
|
|
2505
2757
|
- `theme` — `light` (default) / `dark` / `auto` (follow the OS) / `code`.
|
|
2506
2758
|
- `density` — `comfortable` (default) / `compact`.
|
|
2507
|
-
- `placement` — `floating` (default) / `
|
|
2508
|
-
|
|
2509
|
-
|
|
2759
|
+
- `placement` — `floating` (default) / `sidebar` / `page` / `embedded`. `embedded` drops the
|
|
2760
|
+
fixed positioning and z-index so the widget sits in normal document flow; `page` is a
|
|
2761
|
+
full-screen [centred reading column](#page-placement) for a route of its own.
|
|
2762
|
+
|
|
2763
|
+
Four, because those are the four shapes that differ structurally: a corner panel, a docked
|
|
2764
|
+
rail, a surface that owns the screen, and a thing in your layout. Three older values --
|
|
2765
|
+
`bottom-left`, `side` and `full` -- still parse and still work, and are no longer documented:
|
|
2766
|
+
each is a variant of one of the four rather than a shape of its own. `full` is `page` with
|
|
2767
|
+
`--ag-ui-content-max-width: none`, `bottom-left` is `floating` with a different
|
|
2768
|
+
`--ag-ui-inset`, and `side` is `sidebar` that collapses to the floating launcher instead of
|
|
2769
|
+
an edge rail. Nothing warns and nothing breaks; there is simply less to choose between.
|
|
2510
2770
|
|
|
2511
2771
|
**`embedded` fills the box your page gives it, so give it one.** It is the placement app-shell
|
|
2512
2772
|
layouts reach for, and a grid or flex item defaults to `min-height: auto` — which lets a growing
|
|
@@ -2518,7 +2778,7 @@ fix belongs to the containing element, not to the widget:
|
|
|
2518
2778
|
```
|
|
2519
2779
|
|
|
2520
2780
|
```html
|
|
2521
|
-
<ag-ui-chat endpoint="/agent/" theme="dark" density="compact" placement="
|
|
2781
|
+
<ag-ui-chat endpoint="/agent/" theme="dark" density="compact" placement="sidebar"></ag-ui-chat>
|
|
2522
2782
|
```
|
|
2523
2783
|
|
|
2524
2784
|
See [`src/ui/styles.ts`](src/ui/styles.ts) for the full variable + preset list. The
|
|
@@ -2526,6 +2786,8 @@ See [`src/ui/styles.ts`](src/ui/styles.ts) for the full variable + preset list.
|
|
|
2526
2786
|
text-animation, tool-display, and the answer well live from a single page, and demos the
|
|
2527
2787
|
streamed thoughts region, the mic, and the header theme toggle.
|
|
2528
2788
|
|
|
2789
|
+
It binds every interface and prints the addresses this machine can be reached on, so you can open the playground on a phone — which is the only way to see the small-viewport layout with a real on-screen keyboard rather than a resized desktop window. `HOST=127.0.0.1` keeps it to this machine.
|
|
2790
|
+
|
|
2529
2791
|
### Parts and slots
|
|
2530
2792
|
|
|
2531
2793
|
For styling beyond the `--ag-ui-*` variables, every structural element exposes a `part` so you can
|
|
@@ -2548,12 +2810,13 @@ component sets, so a new one cannot ship undocumented.
|
|
|
2548
2810
|
| --- | --- |
|
|
2549
2811
|
| Shell | `panel`, `header`, `title`, `icon`, `header-controls`, `messages`, `empty`, `pending`, `stopped`, `jump-latest`, and one per resize grip: `resize-handle` plus `resize-handle-top`, `resize-handle-bottom`, `resize-handle-left`, `resize-handle-right`, `resize-handle-top-left`, `resize-handle-top-right`, `resize-handle-bottom-left`, `resize-handle-bottom-right` |
|
|
2550
2812
|
| Header buttons | `header-button` on each, plus `history-button`, `checkpoints-button`, `new-button`, `collapse-button`, `theme-toggle` |
|
|
2551
|
-
| Collapsed widget | `launcher`, `launcher-icon`, `launcher-badge` |
|
|
2813
|
+
| Collapsed widget | `launcher`, `launcher-icon`, `launcher-badge`, `rail-label` |
|
|
2552
2814
|
| Answers | `answer` (the per-turn group), `message` (plus `message-user`, `message-assistant`), `code-copy` |
|
|
2553
2815
|
| Reasoning | `thoughts`, `thoughts-toggle`, `thoughts-body`, `thoughts-label` |
|
|
2554
2816
|
| Follow-up suggestions | `suggestions`, `suggestion-chip` |
|
|
2555
2817
|
| Message actions | `message-actions`, `message-action` (plus `message-action-retry`, `message-action-copy`, `message-action-up`, `message-action-down`), and the icon holder inside each: `message-action-icon` (plus `message-action-icon-retry`, `message-action-icon-copy`, `message-action-icon-up`, `message-action-icon-down`) |
|
|
2556
|
-
|
|
|
2818
|
+
| Queued messages | `queued`, `queued-chip` |
|
|
2819
|
+
| Run notices | `run-notice` (plus `run-notice-interrupted`, `run-notice-attachment-pending`, `run-notice-compaction`, `run-notice-skill`, `run-notice-history-replaced`, `run-notice-chart-undrawable`, `run-notice-surface`), `run-notice-icon`, `run-notice-text`, `run-notice-undo` |
|
|
2557
2820
|
| Tool cards | `tool-card`, `tool-card-head`, `tool-card-icon`, `tool-card-name`, `tool-card-status`, `tool-card-decision`, `tool-card-toggle`, `tool-card-body`, `tool-card-section` (plus `tool-card-args-section`, `tool-card-result-section`), `tool-card-section-label` (plus `tool-card-args-label`, `tool-card-result-label`), `tool-card-args`, `tool-card-result`, `tool-card-approval`, `tool-card-subagent` |
|
|
2558
2821
|
| Delegated sub-agents | `subagent`, `subagent-row`, `subagent-icon`, `subagent-status`, `subagent-steps`, `subagent-step`, `subagent-step-icon`, `subagent-step-name` |
|
|
2559
2822
|
| Client-side confirmation | `confirm`, `confirm-body`, `confirm-args`, `confirm-actions`, `confirm-button` (plus `confirm-confirm`, `confirm-cancel`, `confirm-always`) |
|
|
@@ -2562,7 +2825,7 @@ component sets, so a new one cannot ship undocumented.
|
|
|
2562
2825
|
| Composer | `composer`, `composer-surface`, `composer-tools`, `input`, `send`, `attach-button`, `voice-button` |
|
|
2563
2826
|
| Attachments | `attachment-tray`, `attachment-chips` (the read-only chips on sent bubbles), and the shared chip parts `attachment-chip`, `attachment-chip-icon`, `attachment-chip-name`, `attachment-chip-size`, `attachment-chip-bar`, `attachment-chip-bar-fill`, `attachment-chip-retry`, `attachment-chip-remove` |
|
|
2564
2827
|
| Skills | `skill-chips`, `skill-chip`, `skill-palette`, `skill-item`, `skill-item-title`, `skill-item-desc`, `skill-item-token`, `skill-hint` (the missing-placeholder hint) |
|
|
2565
|
-
| Thread drawer | `drawer`, `drawer-backdrop`, `drawer-panel`, `drawer-header`, `drawer-title`, `drawer-new`, `drawer-list`, `drawer-empty`, `drawer-row`, `drawer-row-select`, `drawer-row-title`, `drawer-row-time`, `drawer-row-preview`, `drawer-row-actions`, `drawer-row-rename`, `drawer-row-delete`, `drawer-rename-input`, `drawer-confirm`, `drawer-confirm-label`, `drawer-confirm-yes`, `drawer-confirm-no` |
|
|
2828
|
+
| Thread drawer | `drawer`, `drawer-backdrop`, `drawer-panel`, `drawer-header`, `drawer-title`, `drawer-new`, `drawer-close`, `drawer-filter`, `drawer-list`, `drawer-empty`, `drawer-row`, `drawer-row-select`, `drawer-row-title`, `drawer-row-time`, `drawer-row-preview`, `drawer-row-actions`, `drawer-row-rename`, `drawer-row-delete`, `drawer-rename-input`, `drawer-confirm`, `drawer-confirm-label`, `drawer-confirm-yes`, `drawer-confirm-no` |
|
|
2566
2829
|
| Charts | `chart-block`, `chart-title`, `chart-legend` |
|
|
2567
2830
|
| Checkpoints panel | `checkpoints`, `checkpoints-header`, `checkpoints-title`, `checkpoints-list`, `checkpoints-empty`, `checkpoint-row`, `checkpoint-label`, `checkpoint-time`, `checkpoint-id`, `checkpoint-branch`, `checkpoint-action` (plus `checkpoint-resume`, `checkpoint-fork`) |
|
|
2568
2831
|
|