@alexkroman1/aai-ui 5.9.0 → 5.10.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/dist/audio.d.ts +10 -0
- package/dist/audio.js +42 -15
- package/dist/client-dir.d.ts +42 -0
- package/dist/client-dir.js +56 -0
- package/dist/default-client/assets/audio-DJF-gC7e.js +1 -0
- package/dist/default-client/assets/{capture-processor-kAhuEwHv.js → capture-processor-D_A9RUeB.js} +1 -1
- package/dist/default-client/assets/index-1JIz81CD.css +2 -0
- package/dist/default-client/assets/index-D_RNiTIh.js +278 -0
- package/dist/default-client/assets/{playback-processor-X5id-na1.js → playback-processor-GPTQTyza.js} +37 -4
- package/dist/default-client/index.html +2 -2
- package/dist/define-client.js +1 -1
- package/dist/index.js +1 -1
- package/dist/{session-core-DqibBzJz.js → session-core-CS37WE1G.js} +36 -18
- package/dist/session-core-messages.d.ts +5 -3
- package/dist/session-core-types.d.ts +13 -0
- package/dist/session-core.js +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +2 -2
- package/dist/worklets/playback-processor.d.ts +1 -1
- package/dist/worklets/playback-processor.js +37 -4
- package/package.json +9 -4
- package/dist/default-client/assets/audio-v_cWeUOV.js +0 -1
- package/dist/default-client/assets/index-CuPmIAbL.css +0 -2
- package/dist/default-client/assets/index-fLjOiK97.js +0 -192
package/dist/audio.d.ts
CHANGED
|
@@ -44,6 +44,16 @@ export type VoiceIOOptions = {
|
|
|
44
44
|
* called for a clean turn, so it can be wired straight to a warning.
|
|
45
45
|
*/
|
|
46
46
|
onPlaybackStats?: ((stats: PlaybackStats) => void) | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* Called every {@link PLAYBACK_PROGRESS_INTERVAL_MS} while the playback
|
|
49
|
+
* buffer holds unplayed agent audio, with its depth in ms. Wire it to the
|
|
50
|
+
* session's `playback_progress` frame: it is the host's only closed-loop
|
|
51
|
+
* view of playback, and without it the host assumes every chunk it forwards
|
|
52
|
+
* starts playing on arrival at exactly 1.0x — so a client whose buffer runs
|
|
53
|
+
* ahead of the wall clock is told the line is silent while the caller is
|
|
54
|
+
* still listening. Unwired, the host degrades to that estimate.
|
|
55
|
+
*/
|
|
56
|
+
onPlaybackProgress?: ((bufferedMs: number) => void) | undefined;
|
|
47
57
|
/**
|
|
48
58
|
* Called once if the microphone delivers nothing but digital silence for
|
|
49
59
|
* the first {@link MIC_SILENCE_PROBE_MS} of capture — a muted or wrong input
|
package/dist/audio.js
CHANGED
|
@@ -77,7 +77,7 @@ function createCaptureNode(ctx, onChunk, onSilent) {
|
|
|
77
77
|
* @throws If microphone access is denied or AudioWorklet registration fails.
|
|
78
78
|
*/
|
|
79
79
|
async function createVoiceIO(opts) {
|
|
80
|
-
const { sttSampleRate, ttsSampleRate, captureWorkletSrc, playbackWorkletSrc, onMicData, onError, onPlaybackStats, onMicSilent } = opts;
|
|
80
|
+
const { sttSampleRate, ttsSampleRate, captureWorkletSrc, playbackWorkletSrc, onMicData, onError, onPlaybackStats, onPlaybackProgress, onMicSilent } = opts;
|
|
81
81
|
const ctx = new AudioContext({
|
|
82
82
|
sampleRate: ttsSampleRate,
|
|
83
83
|
latencyHint: "playback"
|
|
@@ -124,25 +124,42 @@ async function createVoiceIO(opts) {
|
|
|
124
124
|
capture.start();
|
|
125
125
|
let playNode = null;
|
|
126
126
|
let onPlaybackStop = null;
|
|
127
|
+
/**
|
|
128
|
+
* Turn ids for the drain handshake. Every `done()` posts a fresh id and the
|
|
129
|
+
* worklet echoes it on the matching 'stop', so a stop the worklet posted for
|
|
130
|
+
* an EARLIER turn — already in flight when a barge-in flushed that turn —
|
|
131
|
+
* cannot settle the current turn's wait. Dropping only `reason: 'interrupt'`
|
|
132
|
+
* stops was not enough: the stale one is a legitimate drain stop, just for a
|
|
133
|
+
* turn the host has already moved past, and settling on it reports the live
|
|
134
|
+
* reply finished while it is still speaking.
|
|
135
|
+
*/
|
|
136
|
+
let turnSeq = 0;
|
|
137
|
+
let pendingStopTurn = null;
|
|
127
138
|
const lifecycle = new AbortController();
|
|
139
|
+
/** Settle whatever drain wait is pending and forget the turn it belonged to. */
|
|
140
|
+
function settlePendingStop() {
|
|
141
|
+
onPlaybackStop?.();
|
|
142
|
+
onPlaybackStop = null;
|
|
143
|
+
pendingStopTurn = null;
|
|
144
|
+
}
|
|
145
|
+
function onWorkletStop(msg) {
|
|
146
|
+
if (msg.stats && msg.stats.concealedSamples > 0) onPlaybackStats?.(msg.stats);
|
|
147
|
+
if (msg.reason === "interrupt") return;
|
|
148
|
+
if (pendingStopTurn !== null && msg.turn !== pendingStopTurn) return;
|
|
149
|
+
settlePendingStop();
|
|
150
|
+
}
|
|
128
151
|
function ensurePlayNode() {
|
|
129
152
|
if (playNode) return playNode;
|
|
130
153
|
const node = new AudioWorkletNode(ctx, "playback-processor");
|
|
131
154
|
node.connect(ctx.destination);
|
|
132
155
|
node.port.onmessage = (e) => {
|
|
133
|
-
if (e.data.event === "stop")
|
|
134
|
-
|
|
135
|
-
if (stats && stats.concealedSamples > 0) onPlaybackStats?.(stats);
|
|
136
|
-
if (e.data.reason === "interrupt") return;
|
|
137
|
-
onPlaybackStop?.();
|
|
138
|
-
onPlaybackStop = null;
|
|
139
|
-
}
|
|
156
|
+
if (e.data.event === "stop") onWorkletStop(e.data);
|
|
157
|
+
else if (e.data.event === "progress") onPlaybackProgress?.(e.data.bufferedMs);
|
|
140
158
|
};
|
|
141
159
|
node.onprocessorerror = () => {
|
|
142
160
|
const err = /* @__PURE__ */ new Error("Audio playback worklet crashed");
|
|
143
161
|
console.error("[aai-ui]", err.message);
|
|
144
|
-
|
|
145
|
-
onPlaybackStop = null;
|
|
162
|
+
settlePendingStop();
|
|
146
163
|
onError?.(err);
|
|
147
164
|
};
|
|
148
165
|
playNode = node;
|
|
@@ -159,14 +176,24 @@ async function createVoiceIO(opts) {
|
|
|
159
176
|
},
|
|
160
177
|
done() {
|
|
161
178
|
if (!playNode) return Promise.resolve();
|
|
162
|
-
|
|
163
|
-
|
|
179
|
+
const turn = ++turnSeq;
|
|
180
|
+
playNode.port.postMessage({
|
|
181
|
+
event: "done",
|
|
182
|
+
turn
|
|
183
|
+
});
|
|
184
|
+
if (ctx.state !== "running") {
|
|
185
|
+
pendingStopTurn = null;
|
|
186
|
+
return Promise.resolve();
|
|
187
|
+
}
|
|
164
188
|
return new Promise((resolve) => {
|
|
165
189
|
onPlaybackStop?.();
|
|
166
190
|
const settle = () => {
|
|
167
191
|
clearInterval(poll);
|
|
168
192
|
clearTimeout(cap);
|
|
169
|
-
if (onPlaybackStop === settle)
|
|
193
|
+
if (onPlaybackStop === settle) {
|
|
194
|
+
onPlaybackStop = null;
|
|
195
|
+
pendingStopTurn = null;
|
|
196
|
+
}
|
|
170
197
|
resolve();
|
|
171
198
|
};
|
|
172
199
|
const poll = setInterval(() => {
|
|
@@ -174,12 +201,12 @@ async function createVoiceIO(opts) {
|
|
|
174
201
|
}, PLAYBACK_DONE_POLL_MS);
|
|
175
202
|
const cap = setTimeout(settle, PLAYBACK_DONE_MAX_WAIT_MS);
|
|
176
203
|
onPlaybackStop = settle;
|
|
204
|
+
pendingStopTurn = turn;
|
|
177
205
|
});
|
|
178
206
|
},
|
|
179
207
|
flush() {
|
|
180
208
|
if (!playNode) return;
|
|
181
|
-
|
|
182
|
-
onPlaybackStop = null;
|
|
209
|
+
settlePendingStop();
|
|
183
210
|
playNode.port.postMessage({ event: "interrupt" });
|
|
184
211
|
},
|
|
185
212
|
async close() {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filesystem location of the prebuilt default client — **Node only**.
|
|
3
|
+
*
|
|
4
|
+
* Its own subpath rather than the root barrel because it imports `node:module`
|
|
5
|
+
* and `node:path`: the root export is browser code, and pulling Node builtins
|
|
6
|
+
* into it would break every bundler that consumes this package.
|
|
7
|
+
*
|
|
8
|
+
* This exists because locating the directory is otherwise three lines of module
|
|
9
|
+
* archaeology plus knowledge of an internal `dist/` layout, and everybody
|
|
10
|
+
* serving the default UI has to write them — `aai-cli`'s dev server had its own
|
|
11
|
+
* copy, as did every self-hosted example. Three places that would all silently
|
|
12
|
+
* serve nothing if the build output moved.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Absolute path to the prebuilt browser client's static files — pass it to
|
|
16
|
+
* `createServer`/`createAgentServer` as `clientDir`.
|
|
17
|
+
*
|
|
18
|
+
* A function, not a constant: resolution touches the module graph and throws
|
|
19
|
+
* when the package is missing, and a module-level constant would move that
|
|
20
|
+
* failure to import time — where it fires for callers that never wanted the
|
|
21
|
+
* client, and before any of their own error handling is in place.
|
|
22
|
+
*
|
|
23
|
+
* Resolved through this package's own `package.json` rather than relative to
|
|
24
|
+
* this module, so it lands in the same place whether the caller resolved the
|
|
25
|
+
* `@dev/source` TypeScript entry or the compiled one under `dist/`.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* import { agent } from "@alexkroman1/aai";
|
|
30
|
+
* import { createAgentServer } from "@alexkroman1/aai/runtime";
|
|
31
|
+
* import { defaultClientDir } from "@alexkroman1/aai-ui/client-dir";
|
|
32
|
+
*
|
|
33
|
+
* const server = createAgentServer({
|
|
34
|
+
* agent: agent({ name: "Support" }),
|
|
35
|
+
* env: {},
|
|
36
|
+
* clientDir: defaultClientDir(),
|
|
37
|
+
* });
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @public
|
|
41
|
+
*/
|
|
42
|
+
export declare function defaultClientDir(): string;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
//#region client-dir.ts
|
|
4
|
+
/**
|
|
5
|
+
* Filesystem location of the prebuilt default client — **Node only**.
|
|
6
|
+
*
|
|
7
|
+
* Its own subpath rather than the root barrel because it imports `node:module`
|
|
8
|
+
* and `node:path`: the root export is browser code, and pulling Node builtins
|
|
9
|
+
* into it would break every bundler that consumes this package.
|
|
10
|
+
*
|
|
11
|
+
* This exists because locating the directory is otherwise three lines of module
|
|
12
|
+
* archaeology plus knowledge of an internal `dist/` layout, and everybody
|
|
13
|
+
* serving the default UI has to write them — `aai-cli`'s dev server had its own
|
|
14
|
+
* copy, as did every self-hosted example. Three places that would all silently
|
|
15
|
+
* serve nothing if the build output moved.
|
|
16
|
+
*/
|
|
17
|
+
const require = createRequire(import.meta.url);
|
|
18
|
+
/**
|
|
19
|
+
* Absolute path to the prebuilt browser client's static files — pass it to
|
|
20
|
+
* `createServer`/`createAgentServer` as `clientDir`.
|
|
21
|
+
*
|
|
22
|
+
* A function, not a constant: resolution touches the module graph and throws
|
|
23
|
+
* when the package is missing, and a module-level constant would move that
|
|
24
|
+
* failure to import time — where it fires for callers that never wanted the
|
|
25
|
+
* client, and before any of their own error handling is in place.
|
|
26
|
+
*
|
|
27
|
+
* Resolved through this package's own `package.json` rather than relative to
|
|
28
|
+
* this module, so it lands in the same place whether the caller resolved the
|
|
29
|
+
* `@dev/source` TypeScript entry or the compiled one under `dist/`.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* import { agent } from "@alexkroman1/aai";
|
|
34
|
+
* import { createAgentServer } from "@alexkroman1/aai/runtime";
|
|
35
|
+
* import { defaultClientDir } from "@alexkroman1/aai-ui/client-dir";
|
|
36
|
+
*
|
|
37
|
+
* const server = createAgentServer({
|
|
38
|
+
* agent: agent({ name: "Support" }),
|
|
39
|
+
* env: {},
|
|
40
|
+
* clientDir: defaultClientDir(),
|
|
41
|
+
* });
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @public
|
|
45
|
+
*/
|
|
46
|
+
function defaultClientDir() {
|
|
47
|
+
let pkgPath;
|
|
48
|
+
try {
|
|
49
|
+
pkgPath = require.resolve("@alexkroman1/aai-ui/package.json");
|
|
50
|
+
} catch (err) {
|
|
51
|
+
throw new Error("Could not locate the default client UI — is @alexkroman1/aai-ui installed? Try reinstalling dependencies (pnpm install).", { cause: err });
|
|
52
|
+
}
|
|
53
|
+
return path.join(path.dirname(pkgPath), "dist", "default-client");
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { defaultClientDir };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e,o as t,t as n}from"./index-D_RNiTIh.js";function r(e,t,n){if(e!==t)throw Error(`Browser refused the ${n} sample rate: asked for ${t} Hz, got ${e} Hz`)}function i(e){e.then(e=>{for(let t of e.getTracks())t.stop()}).catch(()=>{})}function a(e,t,n){let r=new AudioWorkletNode(e,`capture-processor`,{channelCount:1,channelCountMode:`explicit`}),i=null;return r.port.onmessage=e=>{let r=e.data;r.event===`chunk`&&r.buffer?t(r.buffer):r.event===`silent`?n?.():r.event===`stopped`&&(i?.(),i=null)},{node:r,start(){r.port.postMessage({event:`start`})},stop(){return new Promise(e=>{let t=setTimeout(e,250);i=()=>{clearTimeout(t),e()},r.port.postMessage({event:`stop`})})}}}async function o(o){let{sttSampleRate:s,ttsSampleRate:c,captureWorkletSrc:l,playbackWorkletSrc:u,onMicData:d,onError:f,onPlaybackStats:p,onPlaybackProgress:m,onMicSilent:h}=o,g=new AudioContext({sampleRate:c,latencyHint:`playback`}),_=s===c,v=_?g:new AudioContext({sampleRate:s,latencyHint:`interactive`});async function y(){let e=_?[g]:[g,v];await Promise.all(e.map(e=>e.close().catch(e=>{console.warn(`AudioContext close failed:`,e)})))}let b=navigator.mediaDevices.getUserMedia({audio:{deviceId:{ideal:`default`},...n}}),x;try{[x]=await Promise.all([b,g.resume(),v.resume(),v.audioWorklet.addModule(l),g.audioWorklet.addModule(u)]),r(v.sampleRate,s,`capture`),r(g.sampleRate,c,`playback`)}catch(e){throw i(b),await y(),e}let S=v.createMediaStreamSource(x),C=a(v,d,h);S.connect(C.node),C.node.onprocessorerror=()=>{let e=Error(`Audio capture worklet crashed`);console.error(`[aai-ui]`,e.message),f?.(e)},C.start();let w=null,T=null,E=0,D=null,O=new AbortController;function k(){T?.(),T=null,D=null}function A(e){e.stats&&e.stats.concealedSamples>0&&p?.(e.stats),e.reason!==`interrupt`&&(D!==null&&e.turn!==D||k())}function j(){if(w)return w;let e=new AudioWorkletNode(g,`playback-processor`);return e.connect(g.destination),e.port.onmessage=e=>{e.data.event===`stop`?A(e.data):e.data.event===`progress`&&m?.(e.data.bufferedMs)},e.onprocessorerror=()=>{let e=Error(`Audio playback worklet crashed`);console.error(`[aai-ui]`,e.message),k(),f?.(e)},w=e,e}let M={enqueue(e){O.signal.aborted||e.byteLength!==0&&j().port.postMessage({event:`write`,buffer:new Uint8Array(e)},[e])},done(){if(!w)return Promise.resolve();let n=++E;return w.port.postMessage({event:`done`,turn:n}),g.state===`running`?new Promise(r=>{T?.();let i=()=>{clearInterval(a),clearTimeout(o),T===i&&(T=null,D=null),r()},a=setInterval(()=>{g.state!==`running`&&i()},t),o=setTimeout(i,e);T=i,D=n}):(D=null,Promise.resolve())},flush(){w&&(k(),w.port.postMessage({event:`interrupt`}))},async close(){if(!O.signal.aborted){O.abort(),await C.stop(),S.disconnect(),C.node.disconnect(),w&&w.disconnect();for(let e of x.getTracks())e.stop();await y()}},async[Symbol.asyncDispose](){await M.close()}};return M}export{o as createVoiceIO};
|
package/dist/default-client/assets/{capture-processor-kAhuEwHv.js → capture-processor-D_A9RUeB.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{n as e,r as t}from"./index-
|
|
1
|
+
import{n as e,r as t}from"./index-D_RNiTIh.js";import{t as n}from"./_module-url-BX0RuRU2.js";var r=n(`
|
|
2
2
|
class CaptureProcessor extends AudioWorkletProcessor {
|
|
3
3
|
constructor(options) {
|
|
4
4
|
super();
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
2
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--tracking-wide:.025em;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--font-aai:"Monument Grotesk", "ABC Monument Grotesk", ui-sans-serif, system-ui, -apple-system, sans-serif;--font-aai-serif:"Source Serif 4", "Source Serif Pro", Charter, "Iowan Old Style", Georgia, serif;--font-aai-mono:"JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;--radius-aai:4px}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}html,body{margin:0;padding:0}}@layer components;@layer utilities{.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.m-0{margin:0}.mx-auto{margin-inline:auto}.my-0\.5{margin-block:calc(var(--spacing) * .5)}.my-1\.5{margin-block:calc(var(--spacing) * 1.5)}.my-2\.5{margin-block:calc(var(--spacing) * 2.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.ml-auto{margin-left:auto}.box-border{box-sizing:border-box}.block{display:block}.flex{display:flex}.grid{display:grid}.inline{display:inline}.inline-flex{display:inline-flex}.table{display:table}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-4{height:calc(var(--spacing) * 4)}.h-9{height:calc(var(--spacing) * 9)}.h-11{height:calc(var(--spacing) * 11)}.h-\[7px\]{height:7px}.h-screen{height:100vh}.max-h-64{max-height:calc(var(--spacing) * 64)}.min-h-0{min-height:0}.min-h-5{min-height:calc(var(--spacing) * 5)}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-4{width:calc(var(--spacing) * 4)}.w-\[7px\]{width:7px}.w-fit{width:fit-content}.w-full{width:100%}.max-w-75{max-width:calc(var(--spacing) * 75)}.max-w-105{max-width:calc(var(--spacing) * 105)}.max-w-190{max-width:calc(var(--spacing) * 190)}.max-w-\[60\%\]{max-width:60%}.max-w-\[82\%\]{max-width:82%}.max-w-\[min\(78\%\,64ch\)\]{max-width:min(78%,64ch)}.min-w-0{min-width:0}.flex-1{flex:1}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.border-collapse{border-collapse:collapse}.rotate-90{rotate:90deg}.cursor-pointer{cursor:pointer}.resize{resize:both}.\[scrollbar-width\:none\]{scrollbar-width:none}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.appearance-none{appearance:none}.flex-col{flex-direction:column}.items-center{align-items:center}.items-end{align-items:flex-end}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded-aai{border-radius:var(--radius-aai)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-none{--tw-border-style:none;border-style:none}.bg-transparent{background-color:#0000}.p-2\.5{padding:calc(var(--spacing) * 2.5)}.p-7{padding:calc(var(--spacing) * 7)}.px-1{padding-inline:var(--spacing)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-7{padding-inline:calc(var(--spacing) * 7)}.px-10{padding-inline:calc(var(--spacing) * 10)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-12{padding-block:calc(var(--spacing) * 12)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-5{padding-left:calc(var(--spacing) * 5)}.text-center{text-align:center}.text-left{text-align:left}.font-aai{font-family:var(--font-aai)}.font-aai-mono{font-family:var(--font-aai-mono)}.font-aai-serif{font-family:var(--font-aai-serif)}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12\.5px\]{font-size:12.5px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[14px\]{font-size:14px}.text-\[15px\]{font-size:15px}.text-\[16px\]{font-size:16px}.text-\[17px\]{font-size:17px}.text-\[22px\]{font-size:22px}.text-\[32px\]{font-size:32px}.leading-4{--tw-leading:calc(var(--spacing) * 4);line-height:calc(var(--spacing) * 4)}.leading-\[1\.2\]{--tw-leading:1.2;line-height:1.2}.leading-\[1\.15\]{--tw-leading:1.15;line-height:1.15}.leading-\[22px\]{--tw-leading:22px;line-height:22px}.leading-\[23px\]{--tw-leading:23px;line-height:23px}.leading-\[130\%\]{--tw-leading:130%;line-height:130%}.leading-none{--tw-leading:1;line-height:1}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[-0\.2px\]{--tw-tracking:-.2px;letter-spacing:-.2px}.tracking-\[1\.2px\]{--tw-tracking:1.2px;letter-spacing:1.2px}.tracking-\[1\.4px\]{--tw-tracking:1.4px;letter-spacing:1.4px}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.text-balance{text-wrap:balance}.wrap-break-word{overflow-wrap:break-word}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.uppercase{text-transform:uppercase}.underline{text-decoration-line:underline}.underline-offset-2{text-underline-offset:2px}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.first\:mt-0:first-child{margin-top:0}.last\:mb-0:last-child{margin-bottom:0}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}@media (width>=40rem){.sm\:px-16{padding-inline:calc(var(--spacing) * 16)}.sm\:py-14{padding-block:calc(var(--spacing) * 14)}}}@keyframes aai-pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.45;transform:scale(.82)}}@keyframes aai-bounce{0%,80%,to{opacity:.3;transform:scale(.8)}40%{opacity:1;transform:scale(1)}}@keyframes aai-shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.tool-shimmer{-webkit-text-fill-color:transparent;background:linear-gradient(90deg,currentColor 25%,#0000 50%,currentColor 75%) 0 0/200% 100%;-webkit-background-clip:text;background-clip:text;animation:2s infinite aai-shimmer}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}
|