@aiden-voice/widget 0.0.0 → 0.1.1
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/LICENSE.md +17 -0
- package/README.md +301 -1
- package/dist/api/client.d.ts +14 -0
- package/dist/api/sse.d.ts +17 -0
- package/dist/components/App.d.ts +4 -0
- package/dist/components/Composer.d.ts +8 -0
- package/dist/components/ErrorBanner.d.ts +3 -0
- package/dist/components/Launcher.d.ts +7 -0
- package/dist/components/MessageFeedback.d.ts +10 -0
- package/dist/components/MessageList.d.ts +10 -0
- package/dist/components/Panel.d.ts +7 -0
- package/dist/config.d.ts +3 -0
- package/dist/controller.d.ts +45 -0
- package/dist/index.d.ts +3 -0
- package/dist/markdown/render.d.ts +2 -0
- package/dist/markdown/reveal.d.ts +6 -0
- package/dist/markdown/tokenizer.d.ts +23 -0
- package/dist/mount.d.ts +2 -0
- package/dist/storage.d.ts +3 -0
- package/dist/theme.d.ts +3 -0
- package/dist/types.d.ts +12 -0
- package/dist/version.d.ts +1 -0
- package/dist/widget.iife.js +6 -0
- package/dist/widget.js +1193 -0
- package/package.json +53 -5
package/LICENSE.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Aiden Widget License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Whoo. All rights reserved.
|
|
4
|
+
|
|
5
|
+
This package ("the Widget") is provided solely for embedding the Aiden service in
|
|
6
|
+
websites and applications operated by, or on behalf of, customers of the Aiden service
|
|
7
|
+
under the applicable Aiden terms of service.
|
|
8
|
+
|
|
9
|
+
Permitted: installing the Widget from npm or loading it from the Aiden CDN, and
|
|
10
|
+
integrating it into such websites and applications.
|
|
11
|
+
|
|
12
|
+
Not permitted without prior written consent from Whoo: redistributing, sublicensing,
|
|
13
|
+
selling or hosting the Widget separately from the Aiden service; modifying, decompiling
|
|
14
|
+
or otherwise deriving works from it; removing this notice.
|
|
15
|
+
|
|
16
|
+
The Widget is provided "as is", without warranty of any kind, express or implied. Whoo
|
|
17
|
+
is not liable for any damages arising from its use, to the extent permitted by law.
|
package/README.md
CHANGED
|
@@ -1,3 +1,303 @@
|
|
|
1
1
|
# @aiden-voice/widget
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An embeddable chat widget for whoo-aiden. Drop in a `<script>` tag (or
|
|
4
|
+
install the npm package) and get a working, ready-to-use chat bubble in
|
|
5
|
+
the corner of your site, backed entirely by whoo-aiden's public widget API
|
|
6
|
+
— no UI code of your own required.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
### Option 1: CDN `<script>` tag
|
|
11
|
+
|
|
12
|
+
```html
|
|
13
|
+
<script src="https://widget.aidenvoice.com/v0/widget.iife.js"></script>
|
|
14
|
+
<script>
|
|
15
|
+
window.whooAidenWidget.init({
|
|
16
|
+
publishableKey: 'pk_live_...',
|
|
17
|
+
})
|
|
18
|
+
</script>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Option 2: npm package (bundler-based integration)
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pnpm add @aiden-voice/widget
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { init } from '@aiden-voice/widget'
|
|
29
|
+
|
|
30
|
+
init({
|
|
31
|
+
publishableKey: 'pk_live_...',
|
|
32
|
+
})
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Both paths expose the identical `init(config)` call — one code path, not
|
|
36
|
+
two — whether you load the widget via the CDN script tag
|
|
37
|
+
(`window.whooAidenWidget.init`) or import it in a bundler-based project
|
|
38
|
+
(`import { init } from '@aiden-voice/widget'`).
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
init({
|
|
44
|
+
publishableKey: 'pk_live_...', // OR sessionToken -- exactly one, never both
|
|
45
|
+
// sessionToken: '<jwt from your own backend's identify-session call>',
|
|
46
|
+
position: 'bottom-right', // optional theming, all have defaults
|
|
47
|
+
color: '#4f46e5',
|
|
48
|
+
greeting: 'Hi! How can we help?',
|
|
49
|
+
logoUrl: 'https://example.com/logo.png',
|
|
50
|
+
})
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
- **`publishableKey` mode**: the widget calls whoo-aiden's
|
|
54
|
+
`POST /widget/session` itself on load, subject to that endpoint's
|
|
55
|
+
Origin-allowlist check. This is the common case — you have no backend
|
|
56
|
+
of your own and are embedding the widget directly on your site.
|
|
57
|
+
- **`sessionToken` mode**: the widget skips session-minting entirely and
|
|
58
|
+
uses the token you supply as-is. Use this if your own backend already
|
|
59
|
+
called `POST /widget/identify-session` server-to-server (using a secret
|
|
60
|
+
key that must never reach the browser) and handed the resulting
|
|
61
|
+
short-lived JWT to the page some other way. The widget itself never
|
|
62
|
+
holds, requests, or sees a secret key under any circumstance.
|
|
63
|
+
|
|
64
|
+
`publishableKey` and `sessionToken` are mutually exclusive — `init()`
|
|
65
|
+
throws synchronously, at call time, if you pass both or neither.
|
|
66
|
+
|
|
67
|
+
## Configuration reference
|
|
68
|
+
|
|
69
|
+
| Option | Description | Default |
|
|
70
|
+
| --- | --- | --- |
|
|
71
|
+
| `publishableKey` | Public key used to mint a session server-side. Mutually exclusive with `sessionToken`. | none (required unless `sessionToken` is set) |
|
|
72
|
+
| `sessionToken` | A pre-minted session JWT from your own backend. Mutually exclusive with `publishableKey`. | none (required unless `publishableKey` is set) |
|
|
73
|
+
| `position` | Launcher/panel corner: `'bottom-right'` or `'bottom-left'`. | `'bottom-right'` |
|
|
74
|
+
| `color` | Primary accent color (any valid CSS color). | `'#4f46e5'` |
|
|
75
|
+
| `greeting` | Welcome text shown in the panel before the first message. | none |
|
|
76
|
+
| `logoUrl` | URL of an image shown on the launcher bubble instead of the default icon. | none (default icon) |
|
|
77
|
+
| `apiBaseUrl` | Override the whoo-aiden API host the widget talks to. | `'https://api.aidenvoice.com'` |
|
|
78
|
+
|
|
79
|
+
## Versions
|
|
80
|
+
|
|
81
|
+
The widget is published to a CDN under `https://widget.aidenvoice.com/<version>/`, plus
|
|
82
|
+
npm as `@aiden-voice/widget`.
|
|
83
|
+
|
|
84
|
+
- Exact versions (e.g. `/v1.3.0/`, including prereleases like `/v1.3.0-rc.1/`) are
|
|
85
|
+
immutable — once published, a given version's contents never change.
|
|
86
|
+
- `/vX.Y/` and `/vX/` are aliases that follow the newest stable release in that major/minor
|
|
87
|
+
or major line — e.g. `/v1.3/` follows the newest `1.3.x`, `/v1/` follows the newest `1.x.x`.
|
|
88
|
+
An alias only exists once a matching stable release has shipped: today's newest stable is
|
|
89
|
+
`0.x.x`, so the current CDN snippet above uses `/v0/`, not `/v1/` — `/v1/` will exist the
|
|
90
|
+
day `1.0.0` ships.
|
|
91
|
+
- `/next/` is an alias for the newest prerelease.
|
|
92
|
+
- The CDN root (`https://widget.aidenvoice.com/`) serves a small index page describing this
|
|
93
|
+
layout, rather than a bare directory listing or a 404.
|
|
94
|
+
- On npm, prereleases are published under the `next` dist-tag
|
|
95
|
+
(`pnpm add @aiden-voice/widget@next`). A release that isn't the newest of its kind (e.g.
|
|
96
|
+
a patch published for an older minor after a newer stable already shipped) is published
|
|
97
|
+
under the `maintenance` dist-tag instead, so an out-of-order release never moves what
|
|
98
|
+
`latest`/`next` — and therefore plain `npm install`/`pnpm add` — resolve to.
|
|
99
|
+
|
|
100
|
+
Because the CDN's global cache header is `public, max-age=31536000, immutable` (see
|
|
101
|
+
`staticwebapp.config.json`), fetching an alias or version URL that doesn't exist yet returns
|
|
102
|
+
a 404 that a browser is free to cache for a year. Only embed an alias URL once you know the
|
|
103
|
+
version it needs exists — don't pre-embed `/v1/` in anticipation of a `1.0.0` release.
|
|
104
|
+
|
|
105
|
+
## Releasing
|
|
106
|
+
|
|
107
|
+
Releases are tag-driven via `.github/workflows/release.yml` (the `Release`
|
|
108
|
+
workflow), running in the `release` GitHub environment. It has two jobs:
|
|
109
|
+
`cdn` (build, archive upload, alias rewrite, Static Web App deploy) and
|
|
110
|
+
`publish` (npm), with `publish` gated on `cdn` succeeding first.
|
|
111
|
+
|
|
112
|
+
1. Bump `version` in **both** `package.json` and `src/version.ts`'s
|
|
113
|
+
`WIDGET_VERSION` (a normal PR — `tests/version.test.ts` asserts the two
|
|
114
|
+
stay in sync), and merge it to `main`.
|
|
115
|
+
2. Tag the merge commit and push the tag:
|
|
116
|
+
|
|
117
|
+
```sh
|
|
118
|
+
git tag v<version> && git push origin v<version>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The tag must match `package.json`'s `version` exactly, for a stable
|
|
122
|
+
release (e.g. `v1.3.0` ↔ `1.3.0`) and for a prerelease alike (e.g.
|
|
123
|
+
`v1.3.0-rc.1` ↔ `1.3.0-rc.1`) — the workflow refuses a mismatch.
|
|
124
|
+
Prereleases use a `-rc.N` suffix.
|
|
125
|
+
|
|
126
|
+
Pushing a `v*` tag runs the workflow. The `cdn` job:
|
|
127
|
+
|
|
128
|
+
- Builds and re-runs the full quality gate (lint, typecheck, unit tests,
|
|
129
|
+
build) against the tagged commit.
|
|
130
|
+
- Logs into Azure via OIDC (`azure/login`) — no stored Azure credentials.
|
|
131
|
+
- Plans the release from the tag and the archive's existing versions
|
|
132
|
+
(`scripts/release-plan.mjs plan`, fed via the pure, unit-tested
|
|
133
|
+
`versions()`/`existingVersions()` helper rather than a shell regex),
|
|
134
|
+
computing the immutable version folder, which alias folders move, and
|
|
135
|
+
the npm dist-tag.
|
|
136
|
+
- Uploads the build to the immutable `/v<version>/` folder in the
|
|
137
|
+
`widget-releases` blob container (`--overwrite false` — a version's
|
|
138
|
+
contents can never change once published), refusing to run if the
|
|
139
|
+
computed folder name doesn't look like a version.
|
|
140
|
+
- Rewrites only the alias folders the plan says should move (`/vX.Y/`,
|
|
141
|
+
`/vX/`, or `/next/` for a prerelease) — version folders are never
|
|
142
|
+
touched by this step.
|
|
143
|
+
- Downloads the whole archive, sanity-checks that the version just
|
|
144
|
+
uploaded is actually present and that the archive grew by at least one
|
|
145
|
+
version, and deploys it as-is to the `widget.aidenvoice.com` Static Web
|
|
146
|
+
App (a SWA deploy replaces the whole site, so the full archive — every
|
|
147
|
+
version and alias — is redeployed each release), with a generated
|
|
148
|
+
`staticwebapp.config.json` adding one cache route per alias folder
|
|
149
|
+
found in the archive.
|
|
150
|
+
|
|
151
|
+
Once `cdn` succeeds, the `publish` job rebuilds from the same tag and
|
|
152
|
+
publishes the package to npm as `@aiden-voice/widget`, tagged `latest` for
|
|
153
|
+
a stable release or `next` for a prerelease.
|
|
154
|
+
|
|
155
|
+
Publishing uses npm **trusted publishing** (OIDC) — no npm token exists
|
|
156
|
+
for this package at all. The package's Trusted Publisher, configured on
|
|
157
|
+
npmjs.com, is org `whoo-ai`, repository `whoo-aiden-widget`, workflow
|
|
158
|
+
`release.yml`, environment `release` (all four fields are case-sensitive
|
|
159
|
+
and must match exactly). Because trusted publishing needs npm ≥ 11.5.1
|
|
160
|
+
and Node 22 bundles npm 10, the `publish` job upgrades npm
|
|
161
|
+
(`npm install -g npm@latest`) immediately before publishing. Provenance
|
|
162
|
+
attestations are **not** generated, because the source repository is
|
|
163
|
+
private (provenance requires either a public repo or npm-side allowlisting
|
|
164
|
+
that isn't set up here).
|
|
165
|
+
|
|
166
|
+
The pnpm version this repo uses is pinned via the top-level `packageManager`
|
|
167
|
+
field, not `devEngines.packageManager` — `npm publish` enforces
|
|
168
|
+
`devEngines` for every command it runs, so a `pnpm`-only `devEngines` entry
|
|
169
|
+
makes `npm publish` refuse to run at all.
|
|
170
|
+
|
|
171
|
+
A stable release lands at `/v1.3.0/`, `/v1.3/`, and `/v1/`; a prerelease
|
|
172
|
+
lands only at its exact version folder and `/next/`. `-rc.N` prereleases
|
|
173
|
+
are never aliased under `/vX.Y/` or `/vX/`, and are published to npm under
|
|
174
|
+
the `next` dist-tag rather than `latest`. Shipping a stable release does
|
|
175
|
+
**not** retire `next` — `next` still points at the newest prerelease, if
|
|
176
|
+
any, even after a newer stable ships. If you'd rather have `next` catch up
|
|
177
|
+
to a stable release (e.g. there's no prerelease queued), move it by hand:
|
|
178
|
+
|
|
179
|
+
```sh
|
|
180
|
+
npm dist-tag add @aiden-voice/widget@<stable-version> next
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
If `publish` fails (a transient npm outage, a misconfigured Trusted
|
|
184
|
+
Publisher) after `cdn` has already succeeded, re-run just that job from
|
|
185
|
+
the GitHub Actions UI
|
|
186
|
+
(Actions → the failed run → **Re-run failed jobs**) — it rebuilds and
|
|
187
|
+
publishes from the same tag without re-touching the archive or
|
|
188
|
+
redeploying the Static Web App. The tag itself is never re-pushed.
|
|
189
|
+
|
|
190
|
+
### Recovering a failed release
|
|
191
|
+
|
|
192
|
+
If the `cdn` job fails partway through — after the version folder was
|
|
193
|
+
already uploaded to the archive — simply re-pushing the same tag will not
|
|
194
|
+
work: `scripts/release-plan.mjs plan` refuses a version that already
|
|
195
|
+
exists in the archive, since versions are immutable by design. To recover,
|
|
196
|
+
delete that version's folder from the archive first, then re-push the tag.
|
|
197
|
+
Run this as an operator, not the workflow's identity — the release
|
|
198
|
+
identity is the only principal with a data-plane role on the container, so
|
|
199
|
+
an operator's own `az storage blob` command needs `--auth-mode key`
|
|
200
|
+
instead (Contributor on the resource group is enough to fetch the account
|
|
201
|
+
key for that):
|
|
202
|
+
|
|
203
|
+
```sh
|
|
204
|
+
az storage blob delete-batch --account-name <acct> --source widget-releases \
|
|
205
|
+
--pattern "vX.Y.Z/*" --auth-mode key
|
|
206
|
+
git push origin :refs/tags/vX.Y.Z # delete the remote tag
|
|
207
|
+
git tag -d vX.Y.Z && git tag vX.Y.Z && git push origin vX.Y.Z
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
If the failed run also rewrote an alias before failing later, any alias
|
|
211
|
+
folders it touched (`vX.Y/*`, `vX/*`, or `next/*`) can be cleaned up the
|
|
212
|
+
same way — the next successful release rewrites them from scratch anyway,
|
|
213
|
+
so deleting them first isn't required, just a way to clear out a
|
|
214
|
+
partial/inconsistent alias sooner:
|
|
215
|
+
|
|
216
|
+
```sh
|
|
217
|
+
az storage blob delete-batch --account-name <acct> --source widget-releases \
|
|
218
|
+
--pattern "vX.Y/*" --auth-mode key
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
To verify a release landed, check the CDN alias (cache headers should show
|
|
222
|
+
a short max-age on an alias path, and immutable on a version path) — use
|
|
223
|
+
whichever alias currently exists (see "Versions" above; `/v0/` today):
|
|
224
|
+
|
|
225
|
+
```sh
|
|
226
|
+
curl -sI https://widget.aidenvoice.com/v0/widget.iife.js
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
and confirm the npm publish:
|
|
230
|
+
|
|
231
|
+
```sh
|
|
232
|
+
npm view @aiden-voice/widget dist-tags
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Accessibility
|
|
236
|
+
|
|
237
|
+
The widget targets WCAG 2.1 AA and ships with dialog focus management,
|
|
238
|
+
keyboard support (Tab-trapping while the panel is open, Escape to close),
|
|
239
|
+
screen-reader announcements for streaming replies and errors, and labeled
|
|
240
|
+
form controls out of the box. A few things stay the embedding site's
|
|
241
|
+
responsibility:
|
|
242
|
+
|
|
243
|
+
- **Theme color contrast:** for any valid CSS color (hex, named colors like
|
|
244
|
+
`'yellow'`, `rgb()`, `hsl()`, etc.), the widget automatically picks black
|
|
245
|
+
or white text against whatever `color` (or server-provided
|
|
246
|
+
`primaryColor`) is in effect on button/bubble surfaces, so text stays
|
|
247
|
+
readable there regardless of how light or dark your color is. One
|
|
248
|
+
exception: the panel's "New conversation" and "Close chat" text buttons
|
|
249
|
+
render your `color` directly as text on the panel's white background,
|
|
250
|
+
which is **not** auto-corrected — pick a color with reasonable contrast
|
|
251
|
+
against white if you care about that spot (the default `#4f46e5` works
|
|
252
|
+
well; very light/pastel colors will not). See
|
|
253
|
+
`docs/accessibility-report.md` for details.
|
|
254
|
+
- **Page language:** the widget renders inside your page's DOM and
|
|
255
|
+
inherits language from your document's `<html lang>` attribute; there is
|
|
256
|
+
no separate widget-level locale override.
|
|
257
|
+
- **Don't hide the mount point:** don't place the widget's container (or
|
|
258
|
+
move it, via your own script) inside an element with `aria-hidden="true"`
|
|
259
|
+
or `inert` — that hides it from assistive technology regardless of the
|
|
260
|
+
widget's own markup.
|
|
261
|
+
- **Escape key:** while the panel is open, the widget closes itself on
|
|
262
|
+
`Escape`. If your page has its own global `Escape` handling, be aware of
|
|
263
|
+
the potential conflict.
|
|
264
|
+
|
|
265
|
+
See `docs/accessibility-checklist.md` in this repository for the manual QA
|
|
266
|
+
checklist run before each release.
|
|
267
|
+
|
|
268
|
+
## Error handling
|
|
269
|
+
|
|
270
|
+
The widget distinguishes two deliberately different error tiers:
|
|
271
|
+
|
|
272
|
+
- **Init/auth failures** (an invalid `publishableKey`, an invalid/expired
|
|
273
|
+
`sessionToken`, an Origin not on the allowlist, or a malformed `init()`
|
|
274
|
+
config) are entirely silent to the visitor — no launcher bubble renders
|
|
275
|
+
at all. The failure is logged via `console.error` with enough detail to
|
|
276
|
+
diagnose it, but nothing partial or broken-looking ever appears on the
|
|
277
|
+
page.
|
|
278
|
+
- **Failures once the widget is already visible and in an active chat**
|
|
279
|
+
(a network error, an SSE connection dropping mid-stream, a
|
|
280
|
+
quota-exceeded response) show a short, non-technical inline message in
|
|
281
|
+
the chat panel itself (e.g. "Something went wrong, please try again"),
|
|
282
|
+
leaving any partial response already rendered in place above it.
|
|
283
|
+
|
|
284
|
+
## License
|
|
285
|
+
|
|
286
|
+
Proprietary. This package is licensed only for embedding the Aiden service under
|
|
287
|
+
the applicable Aiden terms of service — see [`LICENSE.md`](./LICENSE.md).
|
|
288
|
+
|
|
289
|
+
## Local development
|
|
290
|
+
|
|
291
|
+
This repo uses [pnpm](https://pnpm.io) and [Biome](https://biomejs.dev)
|
|
292
|
+
(not npm/yarn or eslint/prettier).
|
|
293
|
+
|
|
294
|
+
| Command | Description |
|
|
295
|
+
| --- | --- |
|
|
296
|
+
| `pnpm build` | Build the ESM (npm) and IIFE (CDN) bundles into `dist/`, once. |
|
|
297
|
+
| `pnpm build:watch` | Same, but rebuilds `dist/` automatically on every source change. Run this in the background whenever you're developing against a sibling `whoo-aiden-dashboard` checkout (its `file:` dependency on this repo resolves to `dist/`, not `src/`, so without this a dashboard rebuild/reload can silently keep serving stale widget code). |
|
|
298
|
+
| `pnpm dev` | Run the Vite dev server. |
|
|
299
|
+
| `pnpm lint` | Lint the repo with Biome. |
|
|
300
|
+
| `pnpm format` | Format the repo with Biome. |
|
|
301
|
+
| `pnpm typecheck` | Type-check with `tsc --noEmit`. |
|
|
302
|
+
| `pnpm test` | Run the Vitest unit/component test suite. |
|
|
303
|
+
| `pnpm e2e` | Run the Playwright end-to-end embed smoke test against the built IIFE bundle. |
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare class WidgetApiError extends Error {
|
|
2
|
+
readonly status?: number;
|
|
3
|
+
constructor(message: string, status?: number);
|
|
4
|
+
}
|
|
5
|
+
export declare function mintSession(apiBaseUrl: string, publishableKey: string): Promise<{
|
|
6
|
+
token: string;
|
|
7
|
+
primaryColor: string | null;
|
|
8
|
+
}>;
|
|
9
|
+
export declare function submitFeedback(apiBaseUrl: string, sessionToken: string, body: {
|
|
10
|
+
messageId: string;
|
|
11
|
+
rating: 'up' | 'down';
|
|
12
|
+
correction?: string;
|
|
13
|
+
category?: 'factual' | 'incomplete' | 'tone' | 'other';
|
|
14
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type ChatEvent = {
|
|
2
|
+
type: 'conversation_id';
|
|
3
|
+
conversationId: string;
|
|
4
|
+
} | {
|
|
5
|
+
type: 'delta';
|
|
6
|
+
delta: string;
|
|
7
|
+
verbatim?: true;
|
|
8
|
+
} | {
|
|
9
|
+
type: 'done';
|
|
10
|
+
conversationId: string;
|
|
11
|
+
messageId: string | null;
|
|
12
|
+
} | {
|
|
13
|
+
type: 'error';
|
|
14
|
+
error: string;
|
|
15
|
+
conversationId?: string;
|
|
16
|
+
};
|
|
17
|
+
export declare function streamChat(apiBaseUrl: string, sessionToken: string, conversationId: string | null, message: string, signal?: AbortSignal): AsyncGenerator<ChatEvent>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { RefObject } from 'preact';
|
|
2
|
+
export declare function Composer({ value, onChange, onSend, disabled, inputRef, }: {
|
|
3
|
+
value: string;
|
|
4
|
+
onChange: (value: string) => void;
|
|
5
|
+
onSend: () => void;
|
|
6
|
+
disabled: boolean;
|
|
7
|
+
inputRef?: RefObject<HTMLInputElement>;
|
|
8
|
+
}): import("preact").JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { RefObject } from 'preact';
|
|
2
|
+
import { FeedbackCategory, MessageFeedbackState } from '../controller';
|
|
3
|
+
export declare function MessageFeedback({ state, containerRef, onThumbsUp, onThumbsDown, onSubmitDetailed, onCancel, }: {
|
|
4
|
+
state: MessageFeedbackState;
|
|
5
|
+
containerRef?: RefObject<HTMLElement>;
|
|
6
|
+
onThumbsUp: () => void;
|
|
7
|
+
onThumbsDown: () => void;
|
|
8
|
+
onSubmitDetailed: (correction: string, category: '' | FeedbackCategory) => void;
|
|
9
|
+
onCancel: () => void;
|
|
10
|
+
}): import("preact").JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FeedbackCategory, Message, MessageFeedbackState } from '../controller';
|
|
2
|
+
export declare function MessageList({ messages, messageFeedback, lastCompletedMessageText, onThumbsUp, onThumbsDown, onSubmitDetailed, onCancelFeedback, }: {
|
|
3
|
+
messages: Message[];
|
|
4
|
+
messageFeedback: Record<string, MessageFeedbackState>;
|
|
5
|
+
lastCompletedMessageText: string | null;
|
|
6
|
+
onThumbsUp: (messageId: string) => void;
|
|
7
|
+
onThumbsDown: (messageId: string) => void;
|
|
8
|
+
onSubmitDetailed: (messageId: string, correction: string, category: '' | FeedbackCategory) => void;
|
|
9
|
+
onCancelFeedback: (messageId: string) => void;
|
|
10
|
+
}): import("preact").JSX.Element;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { WidgetController, WidgetState } from '../controller';
|
|
2
|
+
export declare function getFocusableElements(container: HTMLElement): HTMLElement[];
|
|
3
|
+
export declare function Panel({ state, controller, greeting, }: {
|
|
4
|
+
state: WidgetState;
|
|
5
|
+
controller: WidgetController;
|
|
6
|
+
greeting?: string;
|
|
7
|
+
}): import("preact").JSX.Element;
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { WidgetConfig } from './types';
|
|
2
|
+
export interface Message {
|
|
3
|
+
role: 'user' | 'assistant';
|
|
4
|
+
content: string;
|
|
5
|
+
id: string | null;
|
|
6
|
+
}
|
|
7
|
+
export type FeedbackCategory = 'factual' | 'incomplete' | 'tone' | 'other';
|
|
8
|
+
export interface MessageFeedbackState {
|
|
9
|
+
status: 'idle' | 'expanded' | 'submitting' | 'submitted' | 'error';
|
|
10
|
+
rating?: 'up' | 'down';
|
|
11
|
+
}
|
|
12
|
+
export interface WidgetState {
|
|
13
|
+
isOpen: boolean;
|
|
14
|
+
messages: Message[];
|
|
15
|
+
isStreaming: boolean;
|
|
16
|
+
initError: string | null;
|
|
17
|
+
runtimeError: string | null;
|
|
18
|
+
messageFeedback: Record<string, MessageFeedbackState>;
|
|
19
|
+
primaryColor: string | null;
|
|
20
|
+
lastCompletedMessageText: string | null;
|
|
21
|
+
}
|
|
22
|
+
type Listener = (state: WidgetState) => void;
|
|
23
|
+
export declare class WidgetController {
|
|
24
|
+
readonly config: WidgetConfig;
|
|
25
|
+
private state;
|
|
26
|
+
private readonly listeners;
|
|
27
|
+
private sessionToken;
|
|
28
|
+
private conversationId;
|
|
29
|
+
private readonly storageIdentifier;
|
|
30
|
+
private activeRequestController;
|
|
31
|
+
private readonly prefersReducedMotion;
|
|
32
|
+
constructor(config: WidgetConfig);
|
|
33
|
+
getState(): WidgetState;
|
|
34
|
+
subscribe(listener: Listener): () => void;
|
|
35
|
+
private setState;
|
|
36
|
+
init(): Promise<void>;
|
|
37
|
+
open(): void;
|
|
38
|
+
resetConversation(): void;
|
|
39
|
+
close(): void;
|
|
40
|
+
sendMessage(text: string): Promise<void>;
|
|
41
|
+
expandFeedback(messageId: string): void;
|
|
42
|
+
cancelFeedback(messageId: string): void;
|
|
43
|
+
submitFeedback(messageId: string, rating: 'up' | 'down', correction?: string, category?: FeedbackCategory): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type Token = {
|
|
2
|
+
type: 'text';
|
|
3
|
+
value: string;
|
|
4
|
+
start: number;
|
|
5
|
+
end: number;
|
|
6
|
+
} | {
|
|
7
|
+
type: 'bold';
|
|
8
|
+
value: string;
|
|
9
|
+
start: number;
|
|
10
|
+
end: number;
|
|
11
|
+
} | {
|
|
12
|
+
type: 'italic';
|
|
13
|
+
value: string;
|
|
14
|
+
start: number;
|
|
15
|
+
end: number;
|
|
16
|
+
} | {
|
|
17
|
+
type: 'link';
|
|
18
|
+
text: string;
|
|
19
|
+
url: string;
|
|
20
|
+
start: number;
|
|
21
|
+
end: number;
|
|
22
|
+
};
|
|
23
|
+
export declare function tokenize(text: string): Token[];
|
package/dist/mount.d.ts
ADDED
package/dist/theme.d.ts
ADDED
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface WidgetConfig {
|
|
2
|
+
publishableKey?: string;
|
|
3
|
+
sessionToken?: string;
|
|
4
|
+
position?: 'bottom-right' | 'bottom-left';
|
|
5
|
+
color?: string;
|
|
6
|
+
greeting?: string;
|
|
7
|
+
logoUrl?: string;
|
|
8
|
+
apiBaseUrl?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface WidgetHandle {
|
|
11
|
+
unmount(): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const WIDGET_VERSION = "0.1.1";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
var whooAidenWidget=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(e){if((typeof e.publishableKey==`string`&&e.publishableKey.length>0)==(typeof e.sessionToken==`string`&&e.sessionToken.length>0))throw Error("whoo-aiden-widget: init() requires exactly one of `publishableKey` or `sessionToken`, not both or neither.")}var n,r,i,a,o,s,c,l,u,d,f,p,m,h,g={},_=[],v=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,y=Array.isArray;function b(e,t){for(var n in t)e[n]=t[n];return e}function x(e){e&&e.parentNode&&e.parentNode.removeChild(e)}function S(e,t,r){var i,a,o,s={};for(o in t)o==`key`?i=t[o]:o==`ref`?a=t[o]:s[o]=t[o];if(arguments.length>2&&(s.children=arguments.length>3?n.call(arguments,2):r),typeof e==`function`&&e.defaultProps!=null)for(o in e.defaultProps)s[o]===void 0&&(s[o]=e.defaultProps[o]);return C(e,s,i,a,null)}function C(e,t,n,a,o){var s={type:e,props:t,key:n,ref:a,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:o??++i,__i:-1,__u:0};return o==null&&r.vnode!=null&&r.vnode(s),s}function w(e){return e.children}function T(e,t){this.props=e,this.context=t}function E(e,t){if(t==null)return e.__?E(e.__,e.__i+1):null;for(var n;t<e.__k.length;t++)if((n=e.__k[t])!=null&&n.__e!=null)return n.__e;return typeof e.type==`function`?E(e):null}function D(e){if(e.__P&&e.__d){var t=e.__v,n=t.__e,i=[],a=[],o=b({},t);o.__v=t.__v+1,r.vnode&&r.vnode(o),I(e.__P,o,t,e.__n,e.__P.namespaceURI,32&t.__u?[n]:null,i,n??E(t),!!(32&t.__u),a),o.__v=t.__v,o.__.__k[o.__i]=o,re(i,o,a),t.__e=t.__=null,o.__e!=n&&O(o)}}function O(e){if((e=e.__)!=null&&e.__c!=null)return e.__e=e.__c.base=null,e.__k.some(function(t){if(t!=null&&t.__e!=null)return e.__e=e.__c.base=t.__e}),O(e)}function k(e){(!e.__d&&(e.__d=!0)&&a.push(e)&&!A.__r++||o!=r.debounceRendering)&&((o=r.debounceRendering)||s)(A)}function A(){try{for(var e,t=1;a.length;)a.length>t&&a.sort(c),e=a.shift(),t=a.length,D(e)}finally{a.length=A.__r=0}}function ee(e,t,n,r,i,a,o,s,c,l,u){var d,f,p,m,h,v,y=r&&r.__k||_,b=t.length;for(c=j(n,t,y,c,b),d=0;d<b;d++)(p=n.__k[d])!=null&&(f=p.__i!=-1&&y[p.__i]||g,p.__i=d,v=I(e,p,f,i,a,o,s,c,l,u),m=p.__e,p.ref&&f.ref!=p.ref&&(f.ref&&L(f.ref,null,p),u.push(p.ref,p.__c||m,p)),h==null&&m!=null&&(h=m),4&p.__u?(c=M(p,c,e),f.__e&&(f.__e=null)):typeof p.type==`function`&&v!==void 0?c=v:m&&(c=m.nextSibling),p.__u&=-7);return n.__e=h,c}function j(e,t,n,r,i){var a,o,s,c,l,u=n.length,d=u,f=0;for(e.__k=Array(i),a=0;a<i;a++)(o=t[a])!=null&&typeof o!=`boolean`&&typeof o!=`function`?(typeof o==`string`||typeof o==`number`||typeof o==`bigint`||o.constructor==String?o=e.__k[a]=C(null,o,null,null,null):y(o)?o=e.__k[a]=C(w,{children:o},null,null,null):o.constructor===void 0&&o.__b>0?o=e.__k[a]=C(o.type,o.props,o.key,o.ref?o.ref:null,o.__v):e.__k[a]=o,c=a+f,o.__=e,o.__b=e.__b+1,s=null,(l=o.__i=N(o,n,c,d))!=-1&&(d--,(s=n[l])&&(s.__u|=2)),s==null||s.__v==null?(l==-1&&(i>u?f--:i<u&&f++),typeof o.type!=`function`&&(o.__u|=4)):l!=c&&(l==c-1?f--:l==c+1?f++:(l>c?f--:f++,o.__u|=4))):e.__k[a]=null;if(d)for(a=0;a<u;a++)(s=n[a])!=null&&!(2&s.__u)&&(s.__e==r&&(r=E(s)),oe(s,s));return r}function M(e,t,n){var r,i;if(typeof e.type==`function`){for(r=e.__k,i=0;r&&i<r.length;i++)r[i]&&(r[i].__=e,t=M(r[i],t,n));return t}e.__e!=t&&(t&&e.type&&!t.parentNode&&(t=E(e)),t=n.insertBefore(e.__e,t||null));do t&&=t.nextSibling;while(t!=null&&t.nodeType==8);return t}function N(e,t,n,r){var i,a,o,s=e.key,c=e.type,l=t[n],u=l!=null&&!(2&l.__u);if(l===null&&s==null||u&&s==l.key&&c==l.type)return n;if(r>+!!u){for(i=n-1,a=n+1;i>=0||a<t.length;)if((l=t[o=i>=0?i--:a++])!=null&&!(2&l.__u)&&s==l.key&&c==l.type)return o}return-1}function P(e,t,n){t[0]==`-`?e.setProperty(t,n??``):e[t]=n==null?``:typeof n!=`number`||v.test(t)?n:n+`px`}function F(e,t,n,r,i){var a,o;n:if(t==`style`){if(typeof n==`string`)e.style.cssText=n;else{if(typeof r==`string`&&(e.style.cssText=r=``),r)for(t in r)n&&t in n||P(e.style,t,``);if(n)for(t in n)r&&n[t]==r[t]||P(e.style,t,n[t])}}else if(t[0]==`o`&&t[1]==`n`)a=t!=(t=t.replace(f,`$1`)),o=t.toLowerCase(),t=o in e||t==`onFocusOut`||t==`onFocusIn`?o.slice(2):t.slice(2),e.l||={},e.l[t+a]=n,n?r?n[d]=r[d]:(n[d]=p,e.addEventListener(t,a?h:m,a)):e.removeEventListener(t,a?h:m,a);else{if(i==`http://www.w3.org/2000/svg`)t=t.replace(/xlink(H|:h)/,`h`).replace(/sName$/,`s`);else if(t!=`width`&&t!=`height`&&t!=`href`&&t!=`list`&&t!=`form`&&t!=`tabIndex`&&t!=`download`&&t!=`rowSpan`&&t!=`colSpan`&&t!=`role`&&t!=`popover`&&t in e)try{e[t]=n??``;break n}catch{}typeof n==`function`||(n==null||!1===n&&t[4]!=`-`?e.removeAttribute(t):e.setAttribute(t,t==`popover`&&n==1?``:n))}}function te(e){return function(t){if(this.l){var n=this.l[t.type+e];if(t[u]==null)t[u]=p++;else if(t[u]<n[d])return;return n(r.event?r.event(t):t)}}}function I(e,t,n,i,a,o,s,c,l,u){var d,f,p,m,h,g,v,S,C,D,O,k,A,j,M,N,P=t.type;if(t.constructor!==void 0)return null;128&n.__u&&(l=!!(32&n.__u),o=[c=t.__e=n.__e]),(d=r.__b)&&d(t);n:if(typeof P==`function`){f=s.length;try{if(C=t.props,D=P.prototype&&P.prototype.render,O=(d=P.contextType)&&i[d.__c],k=d?O?O.props.value:d.__:i,n.__c?S=(p=t.__c=n.__c).__=p.__E:(D?t.__c=p=new P(C,k):(t.__c=p=new T(C,k),p.constructor=P,p.render=se),O&&O.sub(p),p.state||(p.state={}),p.__n=i,m=p.__d=!0,p.__h=[],p._sb=[]),D&&p.__s==null&&(p.__s=p.state),D&&P.getDerivedStateFromProps!=null&&(p.__s==p.state&&(p.__s=b({},p.__s)),b(p.__s,P.getDerivedStateFromProps(C,p.__s))),h=p.props,g=p.state,p.__v=t,m)D&&P.getDerivedStateFromProps==null&&p.componentWillMount!=null&&p.componentWillMount(),D&&p.componentDidMount!=null&&p.__h.push(p.componentDidMount);else{if(D&&P.getDerivedStateFromProps==null&&C!==h&&p.componentWillReceiveProps!=null&&p.componentWillReceiveProps(C,k),t.__v==n.__v||!p.__e&&p.shouldComponentUpdate!=null&&!1===p.shouldComponentUpdate(C,p.__s,k)){t.__v!=n.__v&&(p.props=C,p.state=p.__s,p.__d=!1),t.__e=n.__e,t.__k=n.__k,t.__k.some(function(e){e&&(e.__=t)}),_.push.apply(p.__h,p._sb),p._sb=[],p.__h.length&&s.push(p),c=E(n);break n}p.componentWillUpdate!=null&&p.componentWillUpdate(C,p.__s,k),D&&p.componentDidUpdate!=null&&p.__h.push(function(){p.componentDidUpdate(h,g,v)})}if(p.context=k,p.props=C,p.__P=e,p.__e=!1,A=r.__r,j=0,D)p.state=p.__s,p.__d=!1,A&&A(t),d=p.render(p.props,p.state,p.context),_.push.apply(p.__h,p._sb),p._sb=[];else do p.__d=!1,A&&A(t),d=p.render(p.props,p.state,p.context),p.state=p.__s;while(p.__d&&++j<25);p.state=p.__s,p.getChildContext!=null&&(i=b(b({},i),p.getChildContext())),D&&!m&&p.getSnapshotBeforeUpdate!=null&&(v=p.getSnapshotBeforeUpdate(h,g)),M=d!=null&&d.type===w&&d.key==null?ie(d.props.children):d,c=ee(e,y(M)?M:[M],t,n,i,a,o,s,c,l,u),p.base=t.__e,t.__u&=-161,p.__h.length&&s.push(p),S&&(p.__E=p.__=null)}catch(e){if(s.length=f,t.__v=null,l||o!=null){if(e.then){for(t.__u|=l?160:128;c&&c.nodeType==8&&c.nextSibling;)c=c.nextSibling;o!=null&&(o[o.indexOf(c)]=null),t.__e=c}else if(o!=null)for(N=o.length;N--;)x(o[N])}else t.__e=n.__e;t.__k??=n.__k||[],e.then||ne(t),r.__e(e,t,n)}}else o==null&&t.__v==n.__v?(t.__k=n.__k,t.__e=n.__e):c=t.__e=ae(n.__e,t,n,i,a,o,s,l,u);return(d=r.diffed)&&d(t),128&t.__u?void 0:c}function ne(e){e&&(e.__c&&(e.__c.__e=!0),e.__k&&e.__k.some(ne))}function re(e,t,n){for(var i=0;i<n.length;i++)L(n[i],n[++i],n[++i]);r.__c&&r.__c(t,e),e.some(function(t){try{e=t.__h,t.__h=[],e.some(function(e){e.call(t)})}catch(e){r.__e(e,t.__v)}})}function ie(e){return typeof e!=`object`||!e||e.__b>0?e:y(e)?e.map(ie):e.constructor===void 0?b({},e):null}function ae(e,t,i,a,o,s,c,l,u){var d,f,p,m,h,_,v,b=i.props||g,S=t.props,C=t.type;if(C==`svg`?o=`http://www.w3.org/2000/svg`:C==`math`?o=`http://www.w3.org/1998/Math/MathML`:o||=`http://www.w3.org/1999/xhtml`,s!=null){for(d=0;d<s.length;d++)if((h=s[d])&&`setAttribute`in h==!!C&&(C?h.localName==C:h.nodeType==3)){e=h,s[d]=null;break}}if(e==null){if(C==null)return document.createTextNode(S);e=document.createElementNS(o,C,S.is&&S),l&&=(r.__m&&r.__m(t,s),!1),s=null}if(C==null)b===S||l&&e.data==S||(e.data=S);else{if(s=C==`textarea`&&S.defaultValue!=null?null:s&&n.call(e.childNodes),!l&&s!=null)for(b={},d=0;d<e.attributes.length;d++)b[(h=e.attributes[d]).name]=h.value;for(d in b)h=b[d],d==`dangerouslySetInnerHTML`?p=h:d==`children`||d in S||d==`value`&&`defaultValue`in S||d==`checked`&&`defaultChecked`in S||F(e,d,null,h,o);for(d in S)h=S[d],d==`children`?m=h:d==`dangerouslySetInnerHTML`?f=h:d==`value`?_=h:d==`checked`?v=h:l&&typeof h!=`function`||b[d]===h||F(e,d,h,b[d],o);if(f)l||p&&(f.__html==p.__html||f.__html==e.innerHTML)||(e.innerHTML=f.__html),t.__k=[];else if(p&&(e.innerHTML=``),ee(t.type==`template`?e.content:e,y(m)?m:[m],t,i,a,C==`foreignObject`?`http://www.w3.org/1999/xhtml`:o,s,c,s?s[0]:i.__k&&E(i,0),l,u),s!=null)for(d=s.length;d--;)x(s[d]);l&&C!=`textarea`||(d=`value`,C==`progress`&&_==null?e.removeAttribute(`value`):_!=null&&(_!==e[d]||C==`progress`&&!_||C==`option`&&_!=b[d])&&F(e,d,_,b[d],o),d=`checked`,v!=null&&v!=e[d]&&F(e,d,v,b[d],o))}return e}function L(e,t,n){try{if(typeof e==`function`){var i=typeof e.__u==`function`;i&&e.__u(),i&&t==null||(e.__u=e(t))}else e.current=t}catch(e){r.__e(e,n)}}function oe(e,t,n){var i,a;if(r.unmount&&r.unmount(e),(i=e.ref)&&(i.current&&i.current!=e.__e||L(i,null,t)),(i=e.__c)!=null){if(i.componentWillUnmount)try{i.componentWillUnmount()}catch(e){r.__e(e,t)}i.base=i.__P=i.__n=null}if(i=e.__k)for(a=0;a<i.length;a++)i[a]&&oe(i[a],t,n||typeof e.type!=`function`);n||x(e.__e),e.__c=e.__=e.__e=void 0}function se(e,t,n){return this.constructor(e,n)}function ce(e,t,i){var a,o,s,c;t==document&&(t=document.documentElement),r.__&&r.__(e,t),o=(a=typeof i==`function`)?null:i&&i.__k||t.__k,s=[],c=[],I(t,e=(!a&&i||t).__k=S(w,null,[e]),o||g,g,t.namespaceURI,!a&&i?[i]:o?null:t.firstChild?n.call(t.childNodes):null,s,!a&&i?i:o?o.__e:t.firstChild,a,c),re(s,e,c),e.props.children=null}n=_.slice,r={__e:function(e,t,n,r){for(var i,a,o;t=t.__;)if((i=t.__c)&&!i.__)try{if((a=i.constructor)&&a.getDerivedStateFromError!=null&&(i.setState(a.getDerivedStateFromError(e)),o=i.__d),i.componentDidCatch!=null&&(i.componentDidCatch(e,r||{}),o=i.__d),o)return i.__E=i}catch(t){e=t}throw e}},i=0,T.prototype.setState=function(e,t){var n=this.__s!=null&&this.__s!=this.state?this.__s:this.__s=b({},this.state);typeof e==`function`&&(e=e(b({},n),this.props)),e&&b(n,e),e!=null&&this.__v&&(t&&this._sb.push(t),k(this))},T.prototype.forceUpdate=function(e){this.__v&&(this.__e=!0,e&&this.__h.push(e),k(this))},T.prototype.render=w,a=[],s=typeof Promise==`function`?Promise.prototype.then.bind(Promise.resolve()):setTimeout,c=function(e,t){return e.__v.__b-t.__v.__b},A.__r=0,l=Math.random().toString(8),u=`__d`+l,d=`__a`+l,f=/(PointerCapture)$|Capture$/i,p=0,m=te(!1),h=te(!0);var R,z,B,le,V=0,ue=[],H=r,de=H.__b,fe=H.__r,pe=H.diffed,me=H.__c,he=H.unmount,ge=H.__;function U(e,t){H.__h&&H.__h(z,e,V||t),V=0;var n=z.__H||(z.__H={__:[],__h:[]});return e>=n.__.length&&n.__.push({}),n.__[e]}function W(e){return V=1,_e(Ce,e)}function _e(e,t,n){var r=U(R++,2);if(r.t=e,!r.__c&&(r.__=[n?n(t):Ce(void 0,t),function(e){var t=r.__N?r.__N[0]:r.__[0],n=r.t(t,e);t!==n&&(r.__N=[n,r.__[1]],r.__c.setState({}))}],r.__c=z,!z.__f)){var i=function(e,t,n){if(!r.__c.__H)return!0;var i=!1,o=r.__c.props!==e;if(r.__c.__H.__.some(function(e){if(e.__N){i=!0;var t=e.__[0];e.__=e.__N,e.__N=void 0,t!==e.__[0]&&(o=!0)}}),a){var s=a.call(this,e,t,n);return i?s||o:s}return!i||o};z.__f=!0;var a=z.shouldComponentUpdate,o=z.componentWillUpdate;z.componentWillUpdate=function(e,t,n){if(this.__e){var r=a;a=void 0,i(e,t,n),a=r}o&&o.call(this,e,t,n)},z.shouldComponentUpdate=i}return r.__N||r.__}function G(e,t){var n=U(R++,3);!H.__s&&Se(n.__H,t)&&(n.__=e,n.u=t,z.__H.__h.push(n))}function K(e){return V=5,ve(function(){return{current:e}},[])}function ve(e,t){var n=U(R++,7);return Se(n.__H,t)&&(n.__=e(),n.__H=t,n.__h=e),n.__}function ye(){for(var e;e=ue.shift();){var t=e.__H;if(e.__P&&t)try{t.__h.some(q),t.__h.some(J),t.__h=[]}catch(n){t.__h=[],H.__e(n,e.__v)}}}H.__b=function(e){z=null,de&&de(e)},H.__=function(e,t){e&&t.__k&&t.__k.__m&&(e.__m=t.__k.__m),ge&&ge(e,t)},H.__r=function(e){fe&&fe(e),R=0;var t=(z=e.__c).__H;t&&(B===z?(t.__h=[],z.__h=[],t.__.some(function(e){e.__N&&(e.__=e.__N),e.u=e.__N=void 0})):(t.__h.some(q),t.__h.some(J),t.__h=[],R=0)),B=z},H.diffed=function(e){pe&&pe(e);var t=e.__c;t&&t.__H&&(t.__H.__h.length&&(ue.push(t)!==1&&le===H.requestAnimationFrame||((le=H.requestAnimationFrame)||xe)(ye)),t.__H.__.some(function(e){e.u&&=(e.__H=e.u,void 0)})),B=z=null},H.__c=function(e,t){t.some(function(e){try{e.__h.some(q),e.__h=e.__h.filter(function(e){return!e.__||J(e)})}catch(n){t.some(function(e){e.__h&&=[]}),t=[],H.__e(n,e.__v)}}),me&&me(e,t)},H.unmount=function(e){he&&he(e);var t,n=e.__c;n&&n.__H&&(n.__H.__.some(function(e){try{q(e)}catch(e){t=e}}),n.__H=void 0,t&&H.__e(t,n.__v))};var be=typeof requestAnimationFrame==`function`;function xe(e){var t,n=function(){clearTimeout(r),be&&cancelAnimationFrame(t),setTimeout(e)},r=setTimeout(n,35);be&&(t=requestAnimationFrame(n))}function q(e){var t=z,n=e.__c;typeof n==`function`&&(e.__c=void 0,n()),z=t}function J(e){var t=z;e.__c=e.__(),z=t}function Se(e,t){return!e||e.length!==t.length||t.some(function(t,n){return t!==e[n]})}function Ce(e,t){return typeof t==`function`?t(e):t}var we=0;Array.isArray;function Y(e,t,n,i,a,o){t||={};var s,c,l=t;if(`ref`in l)for(c in l={},t)c==`ref`?s=t[c]:l[c]=t[c];var u={type:e,props:l,key:n,ref:s,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:--we,__i:-1,__u:0,__source:a,__self:o};if(typeof e==`function`&&(s=e.defaultProps))for(c in s)l[c]===void 0&&(l[c]=s[c]);return r.vnode&&r.vnode(u),u}function Te({isOpen:e,onClick:t,logoUrl:n,buttonRef:r}){return Y(`button`,{type:`button`,class:`whoo-widget-launcher`,onClick:t,"aria-label":e?`Close chat`:`Open chat`,ref:r,children:e?Y(`span`,{"aria-hidden":`true`,children:`✕`}):n?Y(`img`,{class:`whoo-widget-launcher-logo`,src:n,alt:``}):Y(`span`,{"aria-hidden":`true`,children:`💬`})})}function Ee({value:e,onChange:t,onSend:n,disabled:r,inputRef:i}){return Y(`div`,{class:`whoo-widget-composer`,children:[Y(`label`,{class:`whoo-widget-sr-only`,for:`whoo-widget-composer-input`,children:`Message`}),Y(`input`,{id:`whoo-widget-composer-input`,class:`whoo-widget-composer-input`,value:e,readOnly:r,"aria-disabled":r,ref:i,onInput:e=>t(e.target.value),onKeyDown:e=>{e.key===`Enter`&&n()}}),Y(`button`,{type:`button`,class:`whoo-widget-composer-send`,onClick:n,disabled:r,children:`Send`})]})}function De({message:e}){return Y(`div`,{class:`whoo-widget-error-banner`,role:`alert`,children:e})}var X=/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/y,Z=/\*\*([^*]+)\*\*/y,Q=/(?<![A-Za-z0-9_])_([^_\s](?:[^_]*[^_\s])?)_(?![A-Za-z0-9_])/y,$=/\*([^*]+)\*/y,Oe=/https?:\/\/\S+/y,ke=/[.,;:!?)\]}>]+$/;function Ae(e){let t=[],n=0,r=0,i=n=>{n>r&&t.push({type:`text`,value:e.slice(r,n),start:r,end:n})};for(;n<e.length;){if(e[n]===`!`&&n+1<e.length&&e[n+1]===`[`){let t=/!\[([^\]]*)\]\(([^)]*)\)/y;t.lastIndex=n;let r=t.exec(e);if(r){n+=r[0].length;continue}}if(e[n-1]!==`!`){X.lastIndex=n;let a=X.exec(e);if(a){i(n),t.push({type:`link`,text:a[1],url:a[2],start:n,end:X.lastIndex}),n=X.lastIndex,r=n;continue}}Z.lastIndex=n;let a=Z.exec(e);if(a){i(n),t.push({type:`bold`,value:a[1],start:n,end:Z.lastIndex}),n=Z.lastIndex,r=n;continue}Q.lastIndex=n;let o=Q.exec(e);if(o){i(n),t.push({type:`italic`,value:o[1],start:n,end:Q.lastIndex}),n=Q.lastIndex,r=n;continue}$.lastIndex=n;let s=$.exec(e);if(s){i(n),t.push({type:`italic`,value:s[1],start:n,end:$.lastIndex}),n=$.lastIndex,r=n;continue}Oe.lastIndex=n;let c=Oe.exec(e);if(c){let e=c[0].match(ke),a=e?c[0].slice(0,c[0].length-e[0].length):c[0];if(a.length>0){i(n),t.push({type:`link`,text:a,url:a,start:n,end:n+a.length}),n+=a.length,r=n;continue}}n+=1}return i(e.length),t}function je(e){return Ae(e).map((e,t)=>{switch(e.type){case`bold`:return Y(`strong`,{children:e.value},t);case`italic`:return Y(`em`,{children:e.value},t);case`link`:return Y(`a`,{href:e.url,target:`_blank`,rel:`noopener noreferrer`,children:[e.text,Y(`span`,{class:`whoo-widget-sr-only`,children:` (opens in new tab)`})]},t);default:return e.value}})}var Me=[{value:``,label:`Select a reason (optional)`},{value:`factual`,label:`Factual error`},{value:`incomplete`,label:`Incomplete`},{value:`tone`,label:`Off tone`},{value:`other`,label:`Other`}];function Ne({state:e,containerRef:t,onThumbsUp:n,onThumbsDown:r,onSubmitDetailed:i,onCancel:a}){let[o,s]=W(``),[c,l]=W(``),u=K(e.status);if(G(()=>{let n=u.current;if(u.current=e.status,n===e.status)return;let r=t?.current;if(!r)return;let i=r.getRootNode();r.contains(i.activeElement)||r.focus()},[e.status,t]),e.status===`submitted`)return Y(`div`,{class:`whoo-widget-feedback-submitted`,role:`status`,children:`Thanks for the feedback`});if(e.status===`expanded`)return Y(`div`,{class:`whoo-widget-feedback-form`,children:[Y(`label`,{children:[Y(`span`,{class:`whoo-widget-sr-only`,children:`Additional feedback`}),Y(`textarea`,{class:`whoo-widget-feedback-textarea`,value:o,onInput:e=>s(e.target.value),placeholder:`What was wrong? (optional)`})]}),Y(`select`,{class:`whoo-widget-feedback-select`,"aria-label":`Feedback category`,value:c,onChange:e=>l(e.target.value),children:Me.map(e=>Y(`option`,{value:e.value,children:e.label},e.value))}),Y(`div`,{class:`whoo-widget-feedback-form-buttons`,children:[Y(`button`,{type:`button`,class:`whoo-widget-feedback-submit`,onClick:()=>i(o,c),children:`Submit`}),Y(`button`,{type:`button`,class:`whoo-widget-feedback-cancel`,onClick:a,children:`Cancel`})]})]});let d=e.status===`submitting`;return Y(`div`,{class:`whoo-widget-feedback`,children:[Y(`div`,{class:`whoo-widget-feedback-buttons`,children:[Y(`button`,{type:`button`,"aria-label":`Good response`,disabled:d,onClick:n,children:`👍`}),Y(`button`,{type:`button`,"aria-label":`Bad response`,disabled:d,onClick:r,children:`👎`})]}),e.status===`error`?Y(`div`,{class:`whoo-widget-feedback-error`,role:`status`,children:`Couldn't send feedback`}):null]})}function Pe({message:e,feedbackState:t,onThumbsUp:n,onThumbsDown:r,onSubmitDetailed:i,onCancelFeedback:a}){let o=K(null),s=e.id;return Y(`div`,{ref:o,tabIndex:-1,class:`whoo-widget-message whoo-widget-message--${e.role}`,children:[je(e.content),e.role===`assistant`&&s?Y(Ne,{state:t,containerRef:o,onThumbsUp:()=>n(s),onThumbsDown:()=>r(s),onSubmitDetailed:(e,t)=>i(s,e,t),onCancel:()=>a(s)}):null]})}function Fe({messages:e,messageFeedback:t,lastCompletedMessageText:n,onThumbsUp:r,onThumbsDown:i,onSubmitDetailed:a,onCancelFeedback:o}){let s=K(null);return G(()=>{let e=s.current;e&&(e.scrollTop=e.scrollHeight)},[e]),Y(w,{children:[Y(`div`,{class:`whoo-widget-message-list`,ref:s,children:e.map((e,n)=>Y(Pe,{message:e,feedbackState:e.id?t[e.id]??{status:`idle`}:{status:`idle`},onThumbsUp:r,onThumbsDown:i,onSubmitDetailed:a,onCancelFeedback:o},e.id??n))}),Y(`div`,{class:`whoo-widget-sr-only`,"aria-live":`polite`,"aria-atomic":`true`,children:n??``})]})}var Ie=`button:not(:disabled), [href], input:not(:disabled), select:not(:disabled), textarea:not(:disabled), [tabindex]:not([tabindex="-1"])`;function Le(e){return Array.from(e.querySelectorAll(Ie)).filter(e=>{let t=getComputedStyle(e);return t.display!==`none`&&t.visibility!==`hidden`})}function Re({state:e,controller:t,greeting:n}){let[r,i]=W(``),a=K(null),o=K(null);G(()=>{o.current?.focus()},[]);let s=e=>{if(e.key===`Escape`){t.close();return}if(e.key!==`Tab`)return;let n=a.current;if(!n)return;let r=Le(n);if(r.length===0)return;let i=r[0],o=r[r.length-1],s=n.getRootNode().activeElement;if(!n.contains(s)){e.preventDefault(),i.focus();return}e.shiftKey&&s===i?(e.preventDefault(),o.focus()):!e.shiftKey&&s===o&&(e.preventDefault(),i.focus())},c=()=>{let n=r.trim();!n||e.isStreaming||(i(``),t.sendMessage(n))},l=e.messages.length===0&&n?[{role:`assistant`,content:n,id:null}]:e.messages;return Y(`div`,{class:`whoo-widget-panel`,role:`dialog`,"aria-modal":`true`,"aria-label":`Chat with Aiden`,ref:a,onKeyDown:s,children:[Y(`div`,{class:`whoo-widget-header`,children:[Y(`button`,{type:`button`,class:`whoo-widget-header-close`,onClick:()=>t.close(),children:`Close chat`}),e.messages.length>0&&Y(`button`,{type:`button`,class:`whoo-widget-header-reset`,onClick:()=>t.resetConversation(),children:`New conversation`})]}),Y(Fe,{messages:l,messageFeedback:e.messageFeedback,lastCompletedMessageText:e.lastCompletedMessageText,onThumbsUp:e=>t.submitFeedback(e,`up`),onThumbsDown:e=>t.expandFeedback(e),onSubmitDetailed:(e,n,r)=>t.submitFeedback(e,`down`,n||void 0,r||void 0),onCancelFeedback:e=>t.cancelFeedback(e)}),e.runtimeError&&Y(De,{message:e.runtimeError}),Y(Ee,{value:r,onChange:i,onSend:c,disabled:e.isStreaming,inputRef:o})]})}function ze({controller:e}){let[t,n]=W(e.getState()),r=K(null),i=K(t.isOpen);return G(()=>(n(e.getState()),e.subscribe(n)),[e]),G(()=>{i.current&&!t.isOpen&&r.current?.focus(),i.current=t.isOpen},[t.isOpen]),t.initError?null:Y(w,{children:[Y(Te,{isOpen:t.isOpen,onClick:()=>t.isOpen?e.close():e.open(),logoUrl:e.config.logoUrl,buttonRef:r}),t.isOpen&&Y(Re,{state:t,controller:e,greeting:e.config.greeting})]})}var Be=class extends Error{status;constructor(e,t){super(e),this.name=`WidgetApiError`,this.status=t}};async function Ve(e,t){let n=await fetch(`${e}/api/v1/widget/session`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({publishable_key:t})});if(!n.ok)throw new Be(`Failed to create widget session (${n.status})`,n.status);let r=await n.json();return{token:r.token,primaryColor:r.primary_color??null}}async function He(e,t,n){let r=await fetch(`${e}/api/v1/widget/feedback`,{method:`POST`,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${t}`},body:JSON.stringify({message_id:n.messageId,rating:n.rating,correction:n.correction??null,category:n.category??null})});if(!r.ok)throw new Be(`Feedback submission failed (${r.status})`,r.status)}async function*Ue(e,t,n,r,i){let a=await fetch(`${e}/api/v1/widget/chat`,{method:`POST`,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${t}`},body:JSON.stringify({conversation_id:n,message:r}),signal:i});if(!a.ok||!a.body)throw new Be(`Chat request failed (${a.status})`,a.status);let o=a.body.getReader(),s=new TextDecoder,c=``;for(;;){let{done:e,value:t}=await o.read();if(e)break;c+=s.decode(t,{stream:!0});let n=c.indexOf(`
|
|
2
|
+
|
|
3
|
+
`);for(;n!==-1;){let e=c.slice(0,n);c=c.slice(n+2);let t=e.split(`
|
|
4
|
+
`).find(e=>e.startsWith(`data: `));t&&(yield We(JSON.parse(t.slice(6)))),n=c.indexOf(`
|
|
5
|
+
|
|
6
|
+
`)}}}function We(e){return typeof e.error==`string`?{type:`error`,error:e.error,conversationId:e.conversation_id}:e.done===!0?{type:`done`,conversationId:e.conversation_id,messageId:e.message_id??null}:typeof e.delta==`string`?e.verbatim===!0?{type:`delta`,delta:e.delta,verbatim:!0}:{type:`delta`,delta:e.delta}:{type:`conversation_id`,conversationId:e.conversation_id}}var Ge=40,Ke=120;function qe(e){let t=[];for(let n of Ae(e))if(n.type===`text`){let e=n.value.split(/(\s+)/),r=n.start;for(let n of e)r+=n.length,n.trim().length>0&&t.push(r)}else t.push(n.end);return t}function Je(e){let t=null;return e?.addEventListener(`abort`,()=>{t?.()},{once:!0}),function(e){return new Promise(n=>{let r=setTimeout(()=>{t=null,n()},e);t=()=>{clearTimeout(r),t=null,n()}})}}async function Ye(e,t,n){let r=n?.wordIntervalMs??Ge,i=n?.tokenIntervalMs??Ke,a=n?.signal,o=Ae(e),s=new Set(o.filter(e=>e.type!==`text`).map(e=>e.end)),c=Je(a),l=``;for(let n of qe(e))if(a?.aborted||(l=e.slice(0,n),t(l),await c(s.has(n)?i:r),a?.aborted))return;l!==e&&t(e)}function Xe(e){return`whoo-aiden-widget:conversation:${e}`}function Ze(e){try{return window.sessionStorage.getItem(Xe(e))}catch{return null}}function Qe(e,t){try{window.sessionStorage.setItem(Xe(e),t)}catch{}}function $e(e){try{window.sessionStorage.removeItem(Xe(e))}catch{}}var et=class{config;state;listeners=new Set;sessionToken=null;conversationId=null;storageIdentifier;activeRequestController=null;prefersReducedMotion;constructor(e){this.config=e,this.storageIdentifier=e.publishableKey??`session-token-mode`,this.prefersReducedMotion=typeof window.matchMedia==`function`&&window.matchMedia(`(prefers-reduced-motion: reduce)`).matches,this.state={isOpen:!1,messages:[],isStreaming:!1,initError:null,runtimeError:null,messageFeedback:{},primaryColor:null,lastCompletedMessageText:null}}getState(){return this.state}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}setState(e){this.state={...this.state,...e};for(let e of this.listeners)e(this.state)}async init(){let e=this.config.apiBaseUrl??`https://api.aidenvoice.com`;try{if(this.config.sessionToken)this.sessionToken=this.config.sessionToken;else if(this.config.publishableKey){let{token:t,primaryColor:n}=await Ve(e,this.config.publishableKey);this.sessionToken=t,this.setState({primaryColor:n})}else throw Error(`WidgetConfig must include exactly one of publishableKey or sessionToken`)}catch(e){console.error(`[whoo-aiden-widget] initialization failed:`,e),this.setState({initError:e instanceof Error?e.message:String(e)});return}this.conversationId=Ze(this.storageIdentifier)}open(){this.setState({isOpen:!0})}resetConversation(){this.activeRequestController?.abort(),this.activeRequestController=null,this.conversationId=null,$e(this.storageIdentifier),this.setState({messages:[],messageFeedback:{},runtimeError:null,isStreaming:!1,lastCompletedMessageText:null})}close(){this.activeRequestController?.abort(),this.activeRequestController=null,this.setState({isOpen:!1})}async sendMessage(e){if(!this.sessionToken)return;let t=this.config.apiBaseUrl??`https://api.aidenvoice.com`,n={role:`user`,content:e,id:null},r={role:`assistant`,content:``,id:null};this.setState({messages:[...this.state.messages,n,r],isStreaming:!0,runtimeError:null});let i=e=>{let t=[...this.state.messages];t[t.length-1]={...t[t.length-1],content:e},this.setState({messages:t})};this.activeRequestController=new AbortController;let{signal:a}=this.activeRequestController,o=``;try{for await(let n of Ue(t,this.sessionToken,this.conversationId,e,a))if(n.type===`conversation_id`)this.conversationId=n.conversationId,Qe(this.storageIdentifier,n.conversationId);else if(n.type===`delta`)n.verbatim&&!this.prefersReducedMotion?(await Ye(n.delta,e=>i(e),{signal:a}),o=n.delta):n.verbatim?(o=n.delta,i(o)):(o+=n.delta,i(o));else if(n.type===`done`){if(n.conversationId&&(this.conversationId=n.conversationId,Qe(this.storageIdentifier,n.conversationId)),n.messageId){let e=[...this.state.messages];e[e.length-1]={...e[e.length-1],id:n.messageId},this.setState({messages:e})}this.setState({lastCompletedMessageText:o})}else n.type===`error`&&(console.error(`[whoo-aiden-widget] chat error:`,n.error),this.setState({runtimeError:`Something went wrong, please try again.`}))}catch(e){e?.name===`AbortError`||(console.error(`[whoo-aiden-widget] chat request failed:`,e),this.setState({runtimeError:`Something went wrong, please try again.`}))}finally{this.activeRequestController=null,this.setState({isStreaming:!1})}}expandFeedback(e){this.setState({messageFeedback:{...this.state.messageFeedback,[e]:{status:`expanded`}}})}cancelFeedback(e){this.setState({messageFeedback:{...this.state.messageFeedback,[e]:{status:`idle`}}})}async submitFeedback(e,t,n,r){if(!this.sessionToken)return;let i=this.config.apiBaseUrl??`https://api.aidenvoice.com`;this.setState({messageFeedback:{...this.state.messageFeedback,[e]:{status:`submitting`,rating:t}}});try{await He(i,this.sessionToken,{messageId:e,rating:t,correction:n,category:r}),this.setState({messageFeedback:{...this.state.messageFeedback,[e]:{status:`submitted`,rating:t}}})}catch(n){console.error(`[whoo-aiden-widget] feedback submission failed:`,n),this.setState({messageFeedback:{...this.state.messageFeedback,[e]:{status:`error`,rating:t}}})}}},tt=`:host,.whoo-widget-root{all:initial;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.whoo-widget-launcher{bottom:20px;left:var(--whoo-widget-inset-left,auto);right:var(--whoo-widget-inset-right,20px);background:var(--whoo-widget-color,#4f46e5);width:56px;height:56px;color:var(--whoo-widget-text-on-color,#fff);cursor:pointer;border:none;border-radius:50%;justify-content:center;align-items:center;padding:0;font-size:24px;display:flex;position:fixed;box-shadow:0 2px 8px #0003}.whoo-widget-launcher:focus-visible{box-shadow:0 2px 8px #0003,0 0 0 4px #fff}.whoo-widget-launcher-logo{object-fit:cover;border-radius:50%;width:32px;height:32px}.whoo-widget-panel{bottom:88px;left:var(--whoo-widget-inset-left,auto);right:var(--whoo-widget-inset-right,20px);background:#fff;border-radius:12px;flex-direction:column;width:360px;height:480px;max-height:90vh;display:flex;position:fixed;overflow:hidden;box-shadow:0 4px 16px #0003}.whoo-widget-header{flex-shrink:0;justify-content:space-between;align-items:center;padding:8px 12px 0;display:flex}.whoo-widget-header-close,.whoo-widget-header-reset{color:var(--whoo-widget-color,#4f46e5);cursor:pointer;background:0 0;border:none;padding:4px 6px;font-size:12px}.whoo-widget-message-list{flex:1;min-height:100px;padding:12px;overflow-y:auto}.whoo-widget-message{border-radius:8px;max-width:80%;margin-bottom:8px;padding:8px 12px}.whoo-widget-message--user{background:var(--whoo-widget-color,#4f46e5);color:var(--whoo-widget-text-on-color,#fff);margin-left:auto}.whoo-widget-message--assistant{color:#111;background:#f0f0f0}.whoo-widget-error-banner{color:#991b1b;background:#fee2e2;padding:8px 12px;font-size:13px}.whoo-widget-composer{border-top:1px solid #e5e5e5;gap:8px;padding:12px;display:flex}.whoo-widget-composer-input{border:1px solid #6b7280;border-radius:6px;flex:1;padding:8px}.whoo-widget-composer-send{background:var(--whoo-widget-color,#4f46e5);color:var(--whoo-widget-text-on-color,#fff);cursor:pointer;border:none;border-radius:6px;padding:8px 16px}.whoo-widget-composer-send:disabled,.whoo-widget-composer-input[aria-disabled=true]{opacity:.6;cursor:not-allowed}.whoo-widget-feedback{align-items:center;gap:6px;margin-top:4px;display:flex}.whoo-widget-feedback-buttons{gap:6px;display:flex}.whoo-widget-feedback-buttons button{cursor:pointer;opacity:.6;background:0 0;border:none;padding:2px 4px;font-size:14px}.whoo-widget-feedback-buttons button:disabled{opacity:.3;cursor:not-allowed}.whoo-widget-feedback-form{flex-direction:column;gap:6px;margin-top:6px;display:flex}.whoo-widget-feedback-textarea{box-sizing:border-box;resize:vertical;border:1px solid #6b7280;border-radius:6px;width:100%;padding:6px;font-size:12px}.whoo-widget-feedback-select{border:1px solid #6b7280;border-radius:6px;padding:4px;font-size:12px}.whoo-widget-feedback-form-buttons{gap:6px;display:flex}.whoo-widget-feedback-submit{background:var(--whoo-widget-color,#4f46e5);color:var(--whoo-widget-text-on-color,#fff);cursor:pointer;border:none;border-radius:6px;padding:8px 16px;font-size:13px}.whoo-widget-feedback-cancel{color:#111;cursor:pointer;background:#fff;border:1px solid #6b7280;border-radius:6px;padding:8px 16px;font-size:13px}.whoo-widget-feedback-submitted{color:#6b7280;margin-top:4px;font-size:12px}.whoo-widget-feedback-error{color:#991b1b;font-size:12px}.whoo-widget-sr-only{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}button:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible,a:focus-visible{outline-offset:2px;outline:2px solid #111827;box-shadow:0 0 0 4px #fff}`;function nt(e){let t=document.createElement(`div`);if(t.style.color=e,t.style.color===``)return null;document.body.appendChild(t);let n=getComputedStyle(t).color;document.body.removeChild(t);let r=n.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);return r?[Number(r[1]),Number(r[2]),Number(r[3])]:null}function rt([e,t,n]){let r=e=>{let t=e/255;return t<=.03928?t/12.92:((t+.055)/1.055)**2.4};return .2126*r(e)+.7152*r(t)+.0722*r(n)}function it(e,t){let n=Math.max(e,t),r=Math.min(e,t);return(n+.05)/(r+.05)}function at(e){let t=nt(e);if(!t)return console.warn(`[whoo-aiden-widget] Could not parse color "${e}" for contrast calculation; defaulting to white text.`),`#ffffff`;let n=rt(t);return it(n,rt([0,0,0]))>=it(n,rt([255,255,255]))?`#000000`:`#ffffff`}function ot(e,t){let n=t.color??`#4f46e5`;e.style.setProperty(`--whoo-widget-color`,n),e.style.setProperty(`--whoo-widget-text-on-color`,at(n));let r=t.position===`bottom-left`;e.style.setProperty(`--whoo-widget-inset-left`,r?`20px`:`auto`),e.style.setProperty(`--whoo-widget-inset-right`,r?`auto`:`20px`)}function st(e){let t=document.createElement(`div`);t.id=`whoo-aiden-widget-host`;let n=t.attachShadow({mode:`open`}),r=document.createElement(`style`);r.textContent=tt,n.appendChild(r);let i=document.createElement(`div`);n.appendChild(i),ot(i,e);let a=new et(e);return a.subscribe(e=>{e.primaryColor&&(i.style.setProperty(`--whoo-widget-color`,e.primaryColor),i.style.setProperty(`--whoo-widget-text-on-color`,at(e.primaryColor)))}),ce(Y(ze,{controller:a}),i),a.init(),document.body.appendChild(t),{unmount(){ce(null,i),t.remove()}}}function ct(e){return t(e),st(e)}return e.init=ct,e})({});
|