@appilots/web-sdk 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +216 -0
- package/dist/AppilotsWebRuntime-DZF26UyU.d.mts +1389 -0
- package/dist/AppilotsWebRuntime-DZF26UyU.d.ts +1389 -0
- package/dist/chunk-T5TUFZ6T.js +10442 -0
- package/dist/chunk-VHRE4RUO.mjs +10382 -0
- package/dist/index.d.mts +1010 -0
- package/dist/index.d.ts +1010 -0
- package/dist/index.js +234 -0
- package/dist/index.mjs +1 -0
- package/dist/react/index.d.mts +84 -0
- package/dist/react/index.d.ts +84 -0
- package/dist/react/index.js +748 -0
- package/dist/react/index.mjs +738 -0
- package/package.json +76 -0
package/README.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# @appilots/web-sdk
|
|
2
|
+
|
|
3
|
+
The Appilots client for web applications.
|
|
4
|
+
|
|
5
|
+
It observes the page through the **DOM and its accessibility tree** —
|
|
6
|
+
never through a framework's internals — so the same runtime drives a
|
|
7
|
+
React, Vue, Svelte, or hand-written app without knowing which it is.
|
|
8
|
+
|
|
9
|
+
The agent loop itself is not here. Turns, continuation, recovery
|
|
10
|
+
counters and the confirm gate live in `@appilots/client-core` and are
|
|
11
|
+
shared with the React Native SDK. This package supplies the two things
|
|
12
|
+
that cannot be shared: **eyes** (`captureSnapshot`) and **hands**
|
|
13
|
+
(`executeAction`), plus a navigation adapter that maps a URL onto the
|
|
14
|
+
agent contract's `navigationState`.
|
|
15
|
+
|
|
16
|
+
## Why DOM and ARIA, not React internals
|
|
17
|
+
|
|
18
|
+
The React Native SDK reads React's Fiber tree. That is the right call
|
|
19
|
+
there — RN has no DOM and Fiber is the only complete view of what is
|
|
20
|
+
mounted. On the web it would be the wrong call:
|
|
21
|
+
|
|
22
|
+
- **It would only ever work for React.** The point of a web client is
|
|
23
|
+
to work for any app. The DOM is the one tree every framework agrees on.
|
|
24
|
+
- **Fiber is a private API.** It has no compatibility guarantee, differs
|
|
25
|
+
between development and production builds, and has changed shape
|
|
26
|
+
across React versions.
|
|
27
|
+
- **The accessibility tree already answers our questions.** "What is
|
|
28
|
+
this control, what is it called, is it disabled, is it selected, is it
|
|
29
|
+
behind a modal" are exactly what ARIA encodes. An app that is
|
|
30
|
+
accessible is automatically legible to the agent, and an app that is
|
|
31
|
+
not gets a concrete reason to improve.
|
|
32
|
+
|
|
33
|
+
The output shape is identical to the RN walker's (`ScreenSnapshot`), and
|
|
34
|
+
the interaction-graph derivation is literally the same shared code, so
|
|
35
|
+
equivalent content yields identical `el:` ids on both platforms.
|
|
36
|
+
|
|
37
|
+
## Quick start (React)
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import { AppilotsWebProvider, AppilotsChatFab } from '@appilots/web-sdk/react';
|
|
41
|
+
import { useNavigate } from 'react-router-dom';
|
|
42
|
+
|
|
43
|
+
function Root() {
|
|
44
|
+
const navigate = useNavigate();
|
|
45
|
+
return (
|
|
46
|
+
<AppilotsWebProvider
|
|
47
|
+
projectId="proj_..."
|
|
48
|
+
apiKey="ak_..."
|
|
49
|
+
apiBaseUrl="https://your-api/api/v1"
|
|
50
|
+
routes={[
|
|
51
|
+
{ name: 'VehicleList', path: '/vehicles' },
|
|
52
|
+
{ name: 'VehicleDetails', path: '/vehicles/:id' },
|
|
53
|
+
]}
|
|
54
|
+
screens={[
|
|
55
|
+
{
|
|
56
|
+
name: 'VehicleList',
|
|
57
|
+
actions: [{ id: 'delete-vehicle', label: 'Excluir veículo', requiresConfirmation: true }],
|
|
58
|
+
},
|
|
59
|
+
]}
|
|
60
|
+
navigate={(path, options) => navigate(path, { replace: options?.replace })}
|
|
61
|
+
>
|
|
62
|
+
<App />
|
|
63
|
+
<AppilotsChatFab />
|
|
64
|
+
</AppilotsWebProvider>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`AppilotsChatFab` adds a floating button and chat panel to your site. It starts
|
|
70
|
+
closed, opens with focus in the composer, and closes with the button, the
|
|
71
|
+
header close control or Escape. Closing preserves the conversation, draft,
|
|
72
|
+
pending approvals and human support; it does not stop an active response.
|
|
73
|
+
Use “Stop response” or “New conversation” for that.
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
<AppilotsChatFab
|
|
77
|
+
locale="pt-BR"
|
|
78
|
+
title="Assistente"
|
|
79
|
+
position="bottom-right"
|
|
80
|
+
accentColor="#285b45"
|
|
81
|
+
suggestedPrompts={['O que posso fazer por aqui?']}
|
|
82
|
+
/>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Mount it inside `AppilotsWebProvider`. The React binding requires `react` and
|
|
86
|
+
`react-dom` 18 or newer. The widget renders in a portal under `document.body`
|
|
87
|
+
after hydration, so host containers with `overflow` or transforms cannot clip
|
|
88
|
+
it. Its entire UI is excluded from the agent's observations. No CSS import or
|
|
89
|
+
application-owned open/close state is needed.
|
|
90
|
+
|
|
91
|
+
`position` also accepts `bottom-left`; `defaultOpen` defaults to `false`.
|
|
92
|
+
`zIndex` defaults to `1000`: keep your app's blocking dialogs above the widget,
|
|
93
|
+
or lower this value to fit the site's stacking order. The panel adapts to the
|
|
94
|
+
viewport and safe area. `style` customizes the chat panel, as with `AppilotsChat`.
|
|
95
|
+
|
|
96
|
+
For a chat embedded in your own layout, keep using `AppilotsChat`:
|
|
97
|
+
|
|
98
|
+
`AppilotsChat` includes message bubbles, timestamps, Markdown (the same subset
|
|
99
|
+
as mobile), multiline input, Enter to send / Shift+Enter for a new line,
|
|
100
|
+
stop response, new conversation, and inline action confirmations. It follows
|
|
101
|
+
new messages while you are at the bottom and lets you return to the latest
|
|
102
|
+
messages when reading older replies.
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
<AppilotsChat
|
|
106
|
+
locale="pt-BR"
|
|
107
|
+
title="Assistente"
|
|
108
|
+
placeholder="Escreva uma mensagem…"
|
|
109
|
+
suggestedPrompts={['O que posso fazer por aqui?']}
|
|
110
|
+
style={{ height: 600 }}
|
|
111
|
+
/>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
`locale` supports `en` (default) and `pt-BR`. Suggestions prefill the composer;
|
|
115
|
+
they never send automatically. Markdown supports headings, lists, bold, italic,
|
|
116
|
+
inline/fenced code and HTTP(S) links. HTML is rendered as text.
|
|
117
|
+
|
|
118
|
+
“Talk to a person” uses the existing escalation API: waiting/connected status,
|
|
119
|
+
operator replies, and user messages all stay in the same transcript. The API
|
|
120
|
+
and an operator in the dashboard must be available for a person to answer.
|
|
121
|
+
Starting a new conversation abandons open human support. Stop and handoff
|
|
122
|
+
cancel pending actions and ignore late model replies; an action already
|
|
123
|
+
executing is allowed to settle.
|
|
124
|
+
|
|
125
|
+
Styles are scoped and included with the component; no CSS import is required.
|
|
126
|
+
For a custom surface, use `useAppilotsChat()` / `useAppilotsActions()`.
|
|
127
|
+
|
|
128
|
+
Not using React? `AppilotsWebRuntime` is a plain class:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
const runtime = new AppilotsWebRuntime({ projectId, apiKey, routes, screens, navigate });
|
|
132
|
+
await runtime.chat.sendMessage('Cadastre um Corolla 2023');
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Markup conventions
|
|
136
|
+
|
|
137
|
+
None of these are required — the walker falls back to `data-testid`,
|
|
138
|
+
`id`, `name`, `aria-label` and the accessible name. They are how you get
|
|
139
|
+
_precision_ when defaults are ambiguous.
|
|
140
|
+
|
|
141
|
+
| Attribute | Purpose |
|
|
142
|
+
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
143
|
+
| `data-appilots-id` | The agent-facing id, checked before everything else. Use it when `data-testid` is already spoken for. |
|
|
144
|
+
| `data-appilots-action` | Points a control at a **declared screen action**. Essential for list rows: each row's delete button needs a unique id to be targetable, but all of them are the same declared action and must inherit its `requiresConfirmation`. |
|
|
145
|
+
| `data-appilots-skip` | Excludes a subtree from observation entirely. |
|
|
146
|
+
| `data-appilots-sensitive="true"` | Masks a field's value even if it isn't `type="password"`. |
|
|
147
|
+
| `data-appilots-list-id` | Marks a list container and names it. |
|
|
148
|
+
| `data-appilots-list-item` | Marks a row. |
|
|
149
|
+
| `data-appilots-item-key` | A row's stable domain id. |
|
|
150
|
+
| `data-appilots-item-index` | A row's 0-based index in the backing data (for virtualized lists). |
|
|
151
|
+
| `data-appilots-item-count` | Total data-set size when the DOM only holds a page of it. |
|
|
152
|
+
|
|
153
|
+
The SDK's own chat UI carries `data-appilots-chat`, which the walker
|
|
154
|
+
treats as a skip marker — without it the agent would observe its own
|
|
155
|
+
transcript and try to press its own buttons.
|
|
156
|
+
|
|
157
|
+
## What the agent sees
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
const snapshot = captureSnapshot();
|
|
161
|
+
// { route, texts, inputs, buttons, toggles, sliders, lists,
|
|
162
|
+
// choiceGroups, elements, loading, modalOpen }
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Semantics worth knowing:
|
|
166
|
+
|
|
167
|
+
- **Classification is an else-if chain**, so an element is exactly one
|
|
168
|
+
of text/input/slider/toggle/button. Sliders are checked before
|
|
169
|
+
pressables so a range track never becomes a button.
|
|
170
|
+
- **List containers are not descended in the main pass.** A row's
|
|
171
|
+
content lives on that row, not flattened into the top-level arrays —
|
|
172
|
+
that association is the whole reason the shape exists.
|
|
173
|
+
- **Password and marked-sensitive values never leave the page.** They
|
|
174
|
+
are replaced with `<hidden>` before the snapshot is built.
|
|
175
|
+
- **A modal marks its contents `inModal`.** While one is open the
|
|
176
|
+
executor refuses to press anything behind it, because such a press
|
|
177
|
+
would silently do nothing.
|
|
178
|
+
|
|
179
|
+
## Executing actions
|
|
180
|
+
|
|
181
|
+
`executeAction` handles all six action types with the same
|
|
182
|
+
`ActionDiagnose` categories as the RN executor, so server-side recovery
|
|
183
|
+
behaves identically.
|
|
184
|
+
|
|
185
|
+
The subtle one is `form_fill` against a **controlled React input**.
|
|
186
|
+
Assigning `element.value` does not work: React installs its own value
|
|
187
|
+
setter on the element instance and tracks the last value it wrote, so a
|
|
188
|
+
direct assignment is invisible to it and the app reverts the field on
|
|
189
|
+
the next render. The executor calls the _prototype's_ native setter
|
|
190
|
+
(which updates React's tracker) and then dispatches a bubbling `input`
|
|
191
|
+
event, which is what React's `onChange` actually listens for. The same
|
|
192
|
+
sequence satisfies Vue's `v-model` and plain listeners.
|
|
193
|
+
|
|
194
|
+
## Navigation
|
|
195
|
+
|
|
196
|
+
`WebNavigationAdapter` maps the URL onto the contract: the path becomes
|
|
197
|
+
`activePath`, the matched route becomes `currentRouteName`, and route +
|
|
198
|
+
params become the signature the settle loop uses to detect a navigate to
|
|
199
|
+
the same route with different params.
|
|
200
|
+
|
|
201
|
+
Pass your router's `navigate` so agent-driven navigation stays
|
|
202
|
+
client-side. Without one it falls back to `history.pushState` plus a
|
|
203
|
+
synthetic `popstate`, which most routers observe.
|
|
204
|
+
|
|
205
|
+
Route matching prefers the **most specific** pattern, so `/vehicles/new`
|
|
206
|
+
resolves to `VehicleCreate` rather than `VehicleDetails` with
|
|
207
|
+
`id: 'new'`.
|
|
208
|
+
|
|
209
|
+
## Testing
|
|
210
|
+
|
|
211
|
+
`pnpm --filter @appilots/web-sdk test` — unit tests for the walker,
|
|
212
|
+
locators, executor and navigation, plus integration tests that run the
|
|
213
|
+
whole loop against a mocked backend in a real DOM.
|
|
214
|
+
|
|
215
|
+
`pnpm qa:web:agent` — Playwright missions against `apps/example-web-app`
|
|
216
|
+
in a real browser.
|