@alexkroman1/aai-ui 0.12.2 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/README.md +257 -0
  2. package/dist/_colors-CpZO-88A.js +78 -0
  3. package/dist/_module-url-C13kAJ87.js +15 -0
  4. package/dist/_page-shell.d.ts +11 -0
  5. package/dist/_recover-run.d.ts +79 -0
  6. package/dist/_repeat-until.d.ts +30 -0
  7. package/dist/_run-controls.d.ts +33 -0
  8. package/dist/_submission-state.d.ts +92 -0
  9. package/dist/_tap-to-talk-state.d.ts +88 -0
  10. package/dist/_utils-Mi-SCFA1.js +47 -0
  11. package/dist/_utils.d.ts +26 -0
  12. package/dist/_web-storage.d.ts +43 -0
  13. package/dist/_workflow-api-ref.d.ts +37 -0
  14. package/dist/_workflow-files.d.ts +34 -0
  15. package/dist/_workflow-test-defs.d.ts +38 -0
  16. package/dist/aai-logo-D8pMVn9_.js +85 -0
  17. package/dist/agent-state-labels.d.ts +65 -0
  18. package/dist/agent-state.d.ts +108 -0
  19. package/dist/audio/capture.d.ts +44 -0
  20. package/dist/audio/index.d.ts +18 -0
  21. package/dist/audio/index.js +431 -0
  22. package/dist/audio/preconnect.d.ts +87 -0
  23. package/dist/audio/voice-io.d.ts +112 -0
  24. package/dist/build-default-client.d.ts +1 -0
  25. package/dist/chat-view-BCbI7gD4.js +396 -0
  26. package/dist/client-config-C7fQrioM.js +139 -0
  27. package/dist/client-config.d.ts +119 -0
  28. package/dist/client-dir.d.ts +44 -0
  29. package/dist/client-dir.js +58 -0
  30. package/dist/client-identity.d.ts +131 -0
  31. package/dist/client-tool.d.ts +51 -0
  32. package/dist/components/_colors.d.ts +73 -0
  33. package/dist/components/_form-readiness.d.ts +56 -0
  34. package/dist/components/_form-values.d.ts +19 -0
  35. package/dist/components/aai-logo.d.ts +12 -0
  36. package/dist/components/audio-result.d.ts +94 -0
  37. package/dist/components/auto-scroll.d.ts +74 -0
  38. package/dist/components/bullet-list.d.ts +74 -0
  39. package/dist/components/button.d.ts +58 -0
  40. package/dist/components/button.js +85 -0
  41. package/dist/components/chat-view.d.ts +35 -0
  42. package/dist/components/chat-view.js +3 -0
  43. package/dist/components/console-shell.d.ts +85 -0
  44. package/dist/components/controls.d.ts +33 -0
  45. package/dist/components/controls.js +49 -0
  46. package/dist/components/conversation-view.d.ts +130 -0
  47. package/dist/components/eyebrow.d.ts +12 -0
  48. package/dist/components/facts.d.ts +81 -0
  49. package/dist/components/form-fields.d.ts +142 -0
  50. package/dist/components/form-types.d.ts +73 -0
  51. package/dist/components/form.d.ts +96 -0
  52. package/dist/components/markdown.d.ts +57 -0
  53. package/dist/components/message-list.d.ts +48 -0
  54. package/dist/components/message-list.js +3 -0
  55. package/dist/components/session-controls.d.ts +125 -0
  56. package/dist/components/session-error-banner.d.ts +69 -0
  57. package/dist/components/session-state-dot.d.ts +111 -0
  58. package/dist/components/sidebar-layout.d.ts +42 -0
  59. package/dist/components/sidebar-layout.js +56 -0
  60. package/dist/components/start-screen.d.ts +37 -0
  61. package/dist/components/start-screen.js +71 -0
  62. package/dist/components/tool-call-block.d.ts +28 -0
  63. package/dist/components/tool-call-block.js +3 -0
  64. package/dist/components/tool-call-row.d.ts +75 -0
  65. package/dist/components/tool-config-context.d.ts +22 -0
  66. package/dist/components/upload-progress.d.ts +76 -0
  67. package/dist/components/url-chips.d.ts +29 -0
  68. package/dist/components/workflow-fields.d.ts +46 -0
  69. package/dist/components/workflow-pending-note.d.ts +81 -0
  70. package/dist/components/workflow-progress.d.ts +81 -0
  71. package/dist/components/workflow-run-error.d.ts +48 -0
  72. package/dist/components/workflow-run-panel.d.ts +79 -0
  73. package/dist/context.d.ts +288 -0
  74. package/dist/context.js +382 -0
  75. package/dist/conversation-log.d.ts +88 -0
  76. package/dist/default-client/assets/_module-url-BX0RuRU2.js +1 -0
  77. package/dist/default-client/assets/audio-cGFGID-2.js +1 -0
  78. package/dist/default-client/assets/capture-processor-BUABoo3_.js +90 -0
  79. package/dist/default-client/assets/index-8ihb1Urm.css +2 -0
  80. package/dist/default-client/assets/index-BQURVARl.js +126 -0
  81. package/dist/default-client/assets/playback-processor-ABOa11j0.js +314 -0
  82. package/dist/default-client/favicon.ico +0 -0
  83. package/dist/default-client/index.html +15 -0
  84. package/dist/default-client.d.ts +23 -0
  85. package/dist/define-client.d.ts +162 -26
  86. package/dist/define-client.js +201 -55
  87. package/dist/eyebrow-UfmSz9yy.js +27 -0
  88. package/dist/hooks-DN4gHYsj.js +182 -0
  89. package/dist/hooks.d.ts +172 -0
  90. package/dist/hooks.js +3 -0
  91. package/dist/hooks.test-d.d.ts +14 -0
  92. package/dist/inbox-protocol.d.ts +128 -0
  93. package/dist/inbox.d.ts +132 -0
  94. package/dist/index.d.ts +129 -32
  95. package/dist/index.js +5503 -18
  96. package/dist/internal.d.ts +43 -0
  97. package/dist/internal.js +8 -0
  98. package/dist/linked-client.d.ts +70 -0
  99. package/dist/message-list-DHZcLhRA.js +682 -0
  100. package/dist/notice-player.d.ts +70 -0
  101. package/dist/page.d.ts +114 -0
  102. package/dist/phone.d.ts +50 -0
  103. package/dist/route-fetch.d.ts +79 -0
  104. package/dist/session/audio-effects.d.ts +31 -0
  105. package/dist/session/audio-setup.d.ts +57 -0
  106. package/dist/session/audio-state.d.ts +157 -0
  107. package/dist/session/browser-session.d.ts +30 -0
  108. package/dist/session/close.d.ts +26 -0
  109. package/dist/session/dial.d.ts +65 -0
  110. package/dist/session/handshake.d.ts +60 -0
  111. package/dist/session/index.d.ts +17 -0
  112. package/dist/session/index.js +2 -0
  113. package/dist/session/messages.d.ts +63 -0
  114. package/dist/session/mic.d.ts +64 -0
  115. package/dist/session/preconnect.d.ts +48 -0
  116. package/dist/session/reconnect.d.ts +25 -0
  117. package/dist/session/resume-store.d.ts +57 -0
  118. package/dist/session/state.d.ts +146 -0
  119. package/dist/session/ticket.d.ts +44 -0
  120. package/dist/session/types.d.ts +476 -0
  121. package/dist/session/url.d.ts +17 -0
  122. package/dist/session/user-turn.d.ts +55 -0
  123. package/dist/session-CE0Ipsb3.js +2129 -0
  124. package/dist/stored-value.d.ts +103 -0
  125. package/dist/tool-call-block-uXtMOUWN.js +204 -0
  126. package/dist/tool-config-context-Es4YUzV2.js +19 -0
  127. package/dist/types.d.ts +272 -34
  128. package/dist/types.js +35 -4
  129. package/dist/upload/files.d.ts +83 -0
  130. package/dist/upload/index.d.ts +15 -0
  131. package/dist/upload/pause.d.ts +31 -0
  132. package/dist/upload/recall.d.ts +67 -0
  133. package/dist/upload/report.d.ts +26 -0
  134. package/dist/upload/session.d.ts +99 -0
  135. package/dist/url-chips-oyy_Eiqh.js +239 -0
  136. package/dist/use-client-runs.d.ts +87 -0
  137. package/dist/use-conversation-log.d.ts +86 -0
  138. package/dist/use-conversation.d.ts +131 -0
  139. package/dist/use-copy.d.ts +74 -0
  140. package/dist/use-download-url.d.ts +84 -0
  141. package/dist/use-flash.d.ts +61 -0
  142. package/dist/use-inbox.d.ts +101 -0
  143. package/dist/use-push-to-talk.d.ts +105 -0
  144. package/dist/use-route-mutation.d.ts +113 -0
  145. package/dist/use-route.d.ts +82 -0
  146. package/dist/use-run-key.d.ts +133 -0
  147. package/dist/use-session-controls.d.ts +69 -0
  148. package/dist/use-session-id.d.ts +57 -0
  149. package/dist/use-tap-to-talk.d.ts +138 -0
  150. package/dist/use-user-transcript-uyHhzy4d.js +73 -0
  151. package/dist/use-user-transcript.d.ts +75 -0
  152. package/dist/use-workflow-form.d.ts +285 -0
  153. package/dist/use-workflow-progress.d.ts +137 -0
  154. package/dist/use-workflow-run-DdhWmbx-.js +697 -0
  155. package/dist/use-workflow-run.d.ts +100 -0
  156. package/dist/use-workflow-runs.d.ts +71 -0
  157. package/dist/use-workflow-stream.d.ts +154 -0
  158. package/dist/use-workflows.d.ts +77 -0
  159. package/dist/workflow-client.d.ts +136 -0
  160. package/dist/workflow-def-types.d.ts +31 -0
  161. package/dist/workflow-events.d.ts +39 -0
  162. package/dist/workflow-field-kind.d.ts +95 -0
  163. package/dist/workflow-status-labels.d.ts +35 -0
  164. package/dist/worklets/_module-url.d.ts +10 -0
  165. package/dist/worklets/_playback-bench-harness.d.ts +181 -0
  166. package/dist/worklets/_playback-bench-host.d.ts +63 -0
  167. package/dist/worklets/_playback-bench-page.d.ts +65 -0
  168. package/dist/worklets/_tts-trace-harness.d.ts +142 -0
  169. package/dist/worklets/_worklet-test-utils.d.ts +27 -0
  170. package/dist/worklets/capture-processor.d.ts +4 -2
  171. package/dist/worklets/capture-processor.js +73 -33
  172. package/dist/worklets/playback-processor.d.ts +4 -2
  173. package/dist/worklets/playback-processor.js +258 -42
  174. package/package.json +58 -31
  175. package/styles.css +152 -30
  176. package/dist/_components/app.d.ts +0 -23
  177. package/dist/_components/app.js +0 -41
  178. package/dist/_components/button.d.ts +0 -50
  179. package/dist/_components/button.js +0 -46
  180. package/dist/_components/chat-view.d.ts +0 -29
  181. package/dist/_components/chat-view.js +0 -61
  182. package/dist/_components/controls.d.ts +0 -18
  183. package/dist/_components/controls.js +0 -37
  184. package/dist/_components/error-banner.d.ts +0 -21
  185. package/dist/_components/error-banner.js +0 -27
  186. package/dist/_components/message-bubble.d.ts +0 -23
  187. package/dist/_components/message-bubble.js +0 -35
  188. package/dist/_components/message-list.d.ts +0 -21
  189. package/dist/_components/message-list.js +0 -76
  190. package/dist/_components/sidebar-layout.d.ts +0 -21
  191. package/dist/_components/sidebar-layout.js +0 -36
  192. package/dist/_components/start-screen.d.ts +0 -26
  193. package/dist/_components/start-screen.js +0 -51
  194. package/dist/_components/state-indicator.d.ts +0 -29
  195. package/dist/_components/state-indicator.js +0 -37
  196. package/dist/_components/thinking-indicator.d.ts +0 -16
  197. package/dist/_components/thinking-indicator.js +0 -34
  198. package/dist/_components/tool-call-block.d.ts +0 -27
  199. package/dist/_components/tool-call-block.js +0 -118
  200. package/dist/_components/tool-icons.d.ts +0 -17
  201. package/dist/_components/tool-icons.js +0 -128
  202. package/dist/_components/transcript.d.ts +0 -25
  203. package/dist/_components/transcript.js +0 -35
  204. package/dist/audio.d.ts +0 -45
  205. package/dist/audio.js +0 -121
  206. package/dist/client-context.d.ts +0 -33
  207. package/dist/client-context.js +0 -15
  208. package/dist/client-handler.d.ts +0 -43
  209. package/dist/session-CWB7vmqz.js +0 -429
  210. package/dist/session.d.ts +0 -69
  211. package/dist/session.js +0 -2
  212. package/dist/signals.d.ts +0 -120
  213. package/dist/signals.js +0 -161
  214. package/dist/tsdown.config.d.ts +0 -2
  215. package/dist/types.test-d.d.ts +0 -7
@@ -0,0 +1,382 @@
1
+ import { createContext, createElement, useContext, useEffect, useMemo, useSyncExternalStore } from "react";
2
+ import { useSyncExternalStoreWithSelector } from "use-sync-external-store/with-selector";
3
+ //#region src/context.ts
4
+ const DEFAULT_THEME = {
5
+ bg: "#FBF8F2",
6
+ primary: "#3F2BC1",
7
+ text: "#1B1A18",
8
+ surface: "#FFFFFF",
9
+ border: "#DCD7CC"
10
+ };
11
+ const SessionCtx = createContext(null);
12
+ /**
13
+ * Provides the {@link BrowserSession} the session hooks read. `mountClient()`
14
+ * installs it automatically; a custom tree only needs it when bypassing
15
+ * `mountClient()` and mounting React itself.
16
+ *
17
+ * @internal
18
+ */
19
+ function SessionProvider({ value, children }) {
20
+ return createElement(SessionCtx.Provider, { value }, children);
21
+ }
22
+ /**
23
+ * Return the raw {@link BrowserSession} from context without subscribing to
24
+ * snapshot changes. Useful for accessing stable methods (`start`, `toggle`,
25
+ * `reset`, …) from components that select narrow state via
26
+ * {@link useSessionSelector}.
27
+ *
28
+ * Not part of the package's public export surface — internal to aai-ui
29
+ * components.
30
+ */
31
+ function useSessionCore() {
32
+ const core = useContext(SessionCtx);
33
+ if (!core) throw new Error("Session hooks must be used within <SessionProvider>");
34
+ return core;
35
+ }
36
+ /** The session from context, or `null` outside the provider (`useRoute` on a `mountPage()` page). */
37
+ function useOptionalSessionCore() {
38
+ return useContext(SessionCtx);
39
+ }
40
+ /**
41
+ * The session's control methods — `start`, `cancel`, `sendText`,
42
+ * `setMicMuted`, `resetState`, `reset`, `restart`, `resume`, `disconnect`,
43
+ * `toggle`, `end` — with **no snapshot subscription**.
44
+ * Push-to-talk is not among them: that is `usePushToTalk`.
45
+ *
46
+ * This is the narrow half of {@link useSession}, and it is the half a custom
47
+ * chrome could not reach. `<Controls>` and `<StartScreen>` in this package pair
48
+ * a one-field `useSessionSelector` with this package's own `useSessionCore`
49
+ * (`context.ts`, unpublished); a `client.tsx`
50
+ * could not, because that hook is not published — so a footer needing `start`
51
+ * and `toggle` held a WHOLE-SNAPSHOT `useSession()`, and `session/browser-session.ts`
52
+ * rebuilds the snapshot object on every change. Measured consequence: four
53
+ * components across three templates re-rendered on every STT partial and every
54
+ * streaming delta, in files whose every other component is narrowly subscribed
55
+ * on purpose. One of them (`text-adventure-agent`'s `TitleScreen`) reads nothing
56
+ * from the snapshot at all and subscribes to all of it for `session.start`.
57
+ *
58
+ * **Why publishing this does not reopen what `/internal` closed.**
59
+ * `useSessionCore` hands back the STORE — `subscribe`, `getSnapshot`,
60
+ * `connect`, `Symbol.dispose` — which is the framework's own plumbing, the same
61
+ * category as the providers and `buildAgentUrl` that live on
62
+ * `@alexkroman1/aai-ui/internal`. A client that holds it can subscribe out of
63
+ * band of React, dial a socket the mount did not, and dispose the session under
64
+ * the tree that is rendering it. What comes back from here is the SAME
65
+ * methods `useSession()` already publishes on its result, built into a fresh
66
+ * object rather than passed through, so the store is not reachable from it.
67
+ * There is no new capability here — only the existing one without the
68
+ * subscription tax.
69
+ *
70
+ * Identity-stable per core, so it is safe in a dependency array and in a
71
+ * `memo()` child's props: the methods are closures created once by
72
+ * `createBrowserSession`, and the object wrapping them is memoized on the core.
73
+ *
74
+ * Throws outside the provider `mountClient()` installs, like every session hook.
75
+ *
76
+ * @example A footer that acts on the session without re-rendering with it
77
+ * ```tsx
78
+ * import { useSessionActions, useSessionSelector } from "@alexkroman1/aai-ui";
79
+ *
80
+ * function Footer() {
81
+ * // Two narrow subscriptions and no snapshot read: this row re-renders when
82
+ * // `running` flips, and not on every transcript delta.
83
+ * const running = useSessionSelector((s) => s.running);
84
+ * const { toggle, end } = useSessionActions();
85
+ * return (
86
+ * <>
87
+ * <button onClick={toggle}>{running ? "Pause" : "Resume"}</button>
88
+ * <button onClick={end}>Hang up</button>
89
+ * </>
90
+ * );
91
+ * }
92
+ * ```
93
+ *
94
+ * @returns The control methods — see {@link SessionActions}.
95
+ *
96
+ * @public
97
+ */
98
+ function useSessionActions() {
99
+ const core = useSessionCore();
100
+ return useMemo(() => ({
101
+ start: core.start,
102
+ cancel: core.cancel,
103
+ sendText: core.sendText,
104
+ setMicMuted: core.setMicMuted,
105
+ resetState: core.resetState,
106
+ reset: core.reset,
107
+ restart: core.restart,
108
+ resume: core.resume,
109
+ disconnect: core.disconnect,
110
+ toggle: core.toggle,
111
+ end: core.end
112
+ }), [core]);
113
+ }
114
+ /**
115
+ * Return the live {@link Session}: the current snapshot fields plus the
116
+ * control methods (`start`, `toggle`, `reset`, `resetState`, `disconnect`,
117
+ * `cancel`, `end`).
118
+ *
119
+ * Throws if used outside the provider `mountClient()` installs (the error names
120
+ * `<SessionProvider>` — you only mount that yourself when bypassing
121
+ * `mountClient()`). Re-renders the component on *every* snapshot change; for a
122
+ * component that reads one field, prefer {@link useSessionSelector} for a
123
+ * targeted subscription.
124
+ *
125
+ * @example
126
+ * ```tsx
127
+ * import { useSession } from "@alexkroman1/aai-ui";
128
+ *
129
+ * function Controls() {
130
+ * const session = useSession();
131
+ * if (!session.started) return <button onClick={session.start}>Start</button>;
132
+ * return <button onClick={session.toggle}>{session.running ? "Pause" : "Resume"}</button>;
133
+ * }
134
+ * ```
135
+ *
136
+ * @public
137
+ */
138
+ function useSession() {
139
+ const core = useSessionCore();
140
+ const snapshot = useSyncExternalStore(core.subscribe, core.getSnapshot);
141
+ const actions = useSessionActions();
142
+ return useMemo(() => ({
143
+ ...snapshot,
144
+ ...actions
145
+ }), [snapshot, actions]);
146
+ }
147
+ /**
148
+ * Subscribe to a narrow slice of the session snapshot.
149
+ *
150
+ * Unlike {@link useSession} — which re-renders the component on *every*
151
+ * snapshot change — this only triggers a re-render when the selected value
152
+ * changes (per `isEqual`, default `Object.is`). Use it for components that
153
+ * read a single field, e.g. `useSessionSelector((s) => s.running)`.
154
+ *
155
+ * The selector must be pure. It may run on every snapshot change, so keep it
156
+ * cheap; when it returns a derived object, pass a custom `isEqual` to avoid
157
+ * re-renders on referentially-new-but-equal results.
158
+ *
159
+ * @example
160
+ * ```tsx
161
+ * import { useSessionSelector } from "@alexkroman1/aai-ui";
162
+ *
163
+ * // Re-renders when `running` flips, and on nothing else — not on every
164
+ * // transcript delta the way `useSession()` would.
165
+ * function MicDot() {
166
+ * const running = useSessionSelector((snapshot) => snapshot.running);
167
+ * return <span>{running ? "●" : "○"}</span>;
168
+ * }
169
+ * ```
170
+ *
171
+ * @param selector - Reads the slice out of the snapshot. Must be pure.
172
+ * @param isEqual - Compares two selected values. Defaults to `Object.is`.
173
+ * @returns The selected slice.
174
+ *
175
+ * @public
176
+ */
177
+ function useSessionSelector(selector, isEqual = Object.is) {
178
+ const core = useSessionCore();
179
+ return useSyncExternalStoreWithSelector(core.subscribe, core.getSnapshot, core.getSnapshot, selector, isEqual);
180
+ }
181
+ /**
182
+ * The two selectors below are MODULE-SCOPE functions, and that is load-bearing
183
+ * rather than tidy.
184
+ *
185
+ * `useSyncExternalStoreWithSelector` caches its selection keyed on the selector
186
+ * it was handed: a fresh arrow per render invalidates that memo every render,
187
+ * so the selector re-runs and the `isEqual` short-circuit protects only the
188
+ * re-RENDER, never the work. Hoisting them means the two fields more than one
189
+ * chrome ever reads are selected by one stable function for the life of the
190
+ * program — which is also the thing a caller writing the arrow inline cannot
191
+ * do for themselves, and the reason these two are hooks at all rather than a
192
+ * documented one-liner.
193
+ */
194
+ const selectState = (s) => s.state;
195
+ const selectError = (s) => s.error;
196
+ /**
197
+ * The agent's live {@link AgentState} — `disconnected`, `connecting`, `ready`,
198
+ * `listening`, `thinking`, `speaking`, `error` — on its own narrow
199
+ * subscription.
200
+ *
201
+ * `useSessionSelector((s) => s.state)` spelled once. It is one of exactly two
202
+ * snapshot fields that more than one custom chrome ever selects (the other is
203
+ * {@link useSessionError}), and it had been written inline at eight sites —
204
+ * including inside this package and, worse, in `ConsoleShell`'s own `@example`,
205
+ * which taught the inline form to everyone who read it.
206
+ *
207
+ * **Named `useSessionStatus`, not `useSessionState`.** `useAgentState` is the
208
+ * SLOT hook — the agent's own synced application state, whatever a
209
+ * `sessionSlot()` projects — and `AgentState` here is the phase of the CALL.
210
+ * Two different concepts one letter apart, so the shorter-sounding name is the
211
+ * one deliberately not taken.
212
+ *
213
+ * Pair it with {@link AGENT_STATE_LABELS} for a rendered word; the raw member
214
+ * is a wire value, not a label.
215
+ *
216
+ * @example
217
+ * ```tsx
218
+ * import { AGENT_STATE_LABELS, useSessionStatus } from "@alexkroman1/aai-ui";
219
+ *
220
+ * function StatusDot() {
221
+ * const status = useSessionStatus();
222
+ * return <span data-state={status}>{AGENT_STATE_LABELS[status]}</span>;
223
+ * }
224
+ * ```
225
+ *
226
+ * @returns The current agent state.
227
+ *
228
+ * @public
229
+ */
230
+ function useSessionStatus() {
231
+ return useSessionSelector(selectState);
232
+ }
233
+ /**
234
+ * The session's current {@link SessionError}, or `null` when there is none, on
235
+ * its own narrow subscription.
236
+ *
237
+ * The other half of {@link useSessionStatus} — the second of the two fields a
238
+ * custom chrome reads over and over, and the one whose absence is invisible:
239
+ * per the `fatalError` latch in `session/browser-session.ts` the error is the ONLY
240
+ * remaining signal that a session died, since the state beside it goes back to
241
+ * reading like a live one.
242
+ *
243
+ * A chrome rendering it owes `role="alert"` — which is what
244
+ * {@link SessionErrorBanner} is for, and why reaching for that beats reaching
245
+ * for this.
246
+ *
247
+ * @returns The current error, or `null`.
248
+ *
249
+ * @public
250
+ */
251
+ function useSessionError() {
252
+ return useSessionSelector(selectError);
253
+ }
254
+ const ThemeCtx = createContext(DEFAULT_THEME);
255
+ /**
256
+ * Provides the theme the components read via `useTheme`. `mountClient()` installs
257
+ * it automatically (from `ClientConfig.theme`); a custom tree only needs it
258
+ * when bypassing `mountClient()` and mounting React itself.
259
+ *
260
+ * @internal
261
+ */
262
+ function ThemeProvider({ value, children }) {
263
+ const merged = useMemo(() => value ? {
264
+ ...DEFAULT_THEME,
265
+ ...value
266
+ } : DEFAULT_THEME, [value]);
267
+ useThemeStyles(merged);
268
+ return createElement(ThemeCtx.Provider, { value: merged }, children);
269
+ }
270
+ /**
271
+ * The custom property each {@link ClientTheme} field is published as.
272
+ *
273
+ * Named after the field, so `theme.surface` and `--aai-surface` are obviously
274
+ * the same thing, and prefixed for the reason `--aai-btn-bg` and
275
+ * `--aai-sidebar-w` already are: these land on `:root`, where an app's own
276
+ * variables live too.
277
+ *
278
+ * @internal
279
+ */
280
+ const THEME_VARS = {
281
+ bg: "--aai-bg",
282
+ surface: "--aai-surface",
283
+ text: "--aai-text",
284
+ border: "--aai-border",
285
+ primary: "--aai-primary"
286
+ };
287
+ /**
288
+ * Publish the theme to CSS, and paint the page with it.
289
+ *
290
+ * Two jobs on one element deliberately — both are about the parts of the page
291
+ * React does not own.
292
+ *
293
+ * **The variables.** {@link useTheme} hands a component a JavaScript object, so
294
+ * everything it styles carries an inline `style={{ }}`; measured across the
295
+ * template tree the ratio of `theme.` reads to inline style objects is
296
+ * essentially one to one. A Tailwind class cannot see a JavaScript object — so
297
+ * the five values are written to `:root` as custom properties, `styles.css`
298
+ * maps them into the `@theme` block's `--color-*` namespace, and
299
+ * `className="bg-aai-surface text-aai-text border-aai-border"` works. This is
300
+ * ADDITIVE: `useTheme()` stays, because the derived tints in
301
+ * `components/_colors.ts` `color-mix` on the resolved values and a page is
302
+ * entitled to read them for a `satisfies`-pinned palette.
303
+ *
304
+ * **The background.** Components paint `theme.bg` on their own containers, but
305
+ * the page behind them keeps whatever the static `<style>` in index.html set.
306
+ * Any viewport wider (or taller) than the app column then shows that color as a
307
+ * border around the UI — which is how a cream theme ended up letterboxed in
308
+ * black. It stays an explicit `background` on `html` AND `body` rather than
309
+ * riding on the new variable: a variable only paints where some rule consumes
310
+ * it, and the two elements this is about are the two nothing in this package
311
+ * renders.
312
+ */
313
+ function useThemeStyles(theme) {
314
+ const { bg, surface, text, border, primary } = theme;
315
+ useEffect(() => {
316
+ if (typeof document === "undefined") return;
317
+ const { body, documentElement: html } = document;
318
+ const values = {
319
+ bg,
320
+ surface,
321
+ text,
322
+ border,
323
+ primary
324
+ };
325
+ const previousVars = Object.entries(THEME_VARS).map(([field, prop]) => [
326
+ prop,
327
+ html.style.getPropertyValue(prop),
328
+ values[field]
329
+ ]);
330
+ for (const [prop, , next] of previousVars) html.style.setProperty(prop, next);
331
+ const previousBg = {
332
+ body: body.style.background,
333
+ html: html.style.background
334
+ };
335
+ body.style.background = bg;
336
+ html.style.background = bg;
337
+ return () => {
338
+ for (const [prop, before] of previousVars) if (before === "") html.style.removeProperty(prop);
339
+ else html.style.setProperty(prop, before);
340
+ body.style.background = previousBg.body;
341
+ html.style.background = previousBg.html;
342
+ };
343
+ }, [
344
+ bg,
345
+ surface,
346
+ text,
347
+ border,
348
+ primary
349
+ ]);
350
+ }
351
+ /**
352
+ * Read the resolved theme (every {@link ClientTheme} field filled with its
353
+ * default) from the nearest theme context. Returns the default theme when no
354
+ * provider is present, so components can call it unconditionally.
355
+ *
356
+ * This is how a custom component stays on the agent's palette: a
357
+ * `mountClient({ theme })` override reaches it here, where a hardcoded colour or a
358
+ * Tailwind class cannot see it.
359
+ *
360
+ * @example
361
+ * ```tsx
362
+ * import { useTheme } from "@alexkroman1/aai-ui";
363
+ *
364
+ * function Total({ amount }: { amount: string }) {
365
+ * const theme = useTheme();
366
+ * return (
367
+ * <strong style={{ color: theme.primary, background: theme.surface }}>
368
+ * {amount}
369
+ * </strong>
370
+ * );
371
+ * }
372
+ * ```
373
+ *
374
+ * @returns Every {@link ClientTheme} field, filled in.
375
+ *
376
+ * @public
377
+ */
378
+ function useTheme() {
379
+ return useContext(ThemeCtx);
380
+ }
381
+ //#endregion
382
+ export { SessionProvider, ThemeProvider, useOptionalSessionCore, useSession, useSessionActions, useSessionCore, useSessionError, useSessionSelector, useSessionStatus, useTheme };
@@ -0,0 +1,88 @@
1
+ /**
2
+ * A transcript that outlives the session: the pure half of
3
+ * `useConversationLog`, with no React and no storage.
4
+ *
5
+ * A page beside a device holds many SHORT, resumable sessions (tap, a few
6
+ * turns, a hang-up, and the next tap resumes), and the live conversation the
7
+ * session holds is only ever the current one. This log keeps the rest, in the
8
+ * order things happened, interleaved with notes and what the device said on
9
+ * its own.
10
+ *
11
+ * ## A resume replays, so the live list is never appended as it stands
12
+ *
13
+ * A resumed session's turns come back (`history.restored`) — appending the live
14
+ * list would log every earlier turn again. Each server session is instead a
15
+ * CHAIN of entries whose items, end to end, are its conversation: the chain is
16
+ * rewritten from the live list and only what is new goes on the end — in a NEW
17
+ * entry when something else (a note, a notice) was logged since, so the log
18
+ * stays in order. A session the server had already retired comes back under
19
+ * the same id but EMPTY and greeting; it continues the chain only if every turn
20
+ * the two share matches, and starts a new `run` otherwise, so it cannot
21
+ * overwrite what the old one said.
22
+ *
23
+ * @module
24
+ */
25
+ import type { InboxEvent } from "./inbox-protocol.ts";
26
+ import type { ConversationItem } from "./use-conversation.ts";
27
+ /**
28
+ * One entry of a {@link useConversationLog} transcript.
29
+ *
30
+ * - `session` — a stretch of one server session's conversation. `run` tells
31
+ * apart two conversations the server held under one id (a retired session
32
+ * that came back empty); `clientId` is whose it was — this browser's own or
33
+ * a linked device's, since only the page's CURRENT client can resume one.
34
+ * Tool calls are stored without their `result`, which is the agent's to keep
35
+ * and can be large.
36
+ * - `note` — a line the page logged itself (`addNote`): "New session".
37
+ * - `spoken` — what the agent said on its own, outside a session (a reminder
38
+ * read aloud): rendered as an assistant turn.
39
+ *
40
+ * @public
41
+ */
42
+ export type ConversationLogEntry = {
43
+ readonly kind: "session";
44
+ readonly sessionId: string;
45
+ readonly run: number;
46
+ /** When the entry was started, epoch ms. */
47
+ readonly at: number;
48
+ readonly items: readonly ConversationItem[];
49
+ readonly clientId?: string;
50
+ } | {
51
+ readonly kind: "note";
52
+ readonly at: number;
53
+ readonly text: string;
54
+ } | {
55
+ readonly kind: "spoken";
56
+ readonly at: number;
57
+ readonly text: string;
58
+ };
59
+ /** The item as the log keeps it: a tool call's `result` dropped. */
60
+ export declare function logItem(item: ConversationItem): ConversationItem;
61
+ /** Add an entry at the end. */
62
+ export declare function appendEntry(log: readonly ConversationLogEntry[], entry: ConversationLogEntry, max: number): ConversationLogEntry[];
63
+ /**
64
+ * Write the live conversation of `sessionId` into the log — see the module
65
+ * doc. Returns `log` itself when nothing changed, so a caller can skip a render.
66
+ */
67
+ export declare function recordSession(log: readonly ConversationLogEntry[], sessionId: string, live: readonly ConversationItem[], now: number, max: number, clientId?: string): readonly ConversationLogEntry[];
68
+ /**
69
+ * The log item for one frame of a client's live conversation — what
70
+ * `useInbox({ onEvent })` delivers for another session of the same client (a
71
+ * device's, when the page is linked to one) — or `undefined` for a frame that
72
+ * is not shown: a partial transcript, a recovery phrase ("sorry, say that
73
+ * again", the agent's filler rather than a reply), a tool's completion, a
74
+ * `session_ended`.
75
+ *
76
+ * `useConversationLog().mirror` is built on this; call it yourself to render
77
+ * another session some other way.
78
+ *
79
+ * @param event - The inbox frame.
80
+ * @param id - The id to give the item: its index in that session's list, say.
81
+ * Messages take it as `id`, tool calls as `seq`.
82
+ * @returns A committed user or assistant turn, a tool call (pending), or `undefined`.
83
+ *
84
+ * @public
85
+ */
86
+ export declare function inboxEventToItem(event: InboxEvent, id?: number): ConversationItem | undefined;
87
+ /** A stored log, with anything malformed dropped — never a throw. */
88
+ export declare function parseLog(raw: string | undefined, max: number): ConversationLogEntry[];
@@ -0,0 +1 @@
1
+ function e(e){return URL.createObjectURL(new Blob([e],{type:`application/javascript`}))}export{e as t};
@@ -0,0 +1 @@
1
+ import{a as e,i as t,t as n}from"./index-BQURVARl.js";var r={echoCancellation:!0,noiseSuppression:!1,autoGainControl:!1,voiceIsolation:!1};function i(e,t,n){if(e!==t)throw Error(`Browser refused the ${n} sample rate: asked for ${t} Hz, got ${e} Hz`)}function a(e){let t=Error(`Audio ${e} worklet crashed`);return console.error(`[aai-ui]`,t.message),t}function o(e){e.then(e=>{for(let t of e.getTracks())t.stop()}).catch(()=>{})}function s(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(){let{promise:e,resolve:t}=Promise.withResolvers(),n=setTimeout(t,250);return i=()=>{clearTimeout(n),t()},r.port.postMessage({event:`stop`}),e}}}async function c(e){let{sampleRate:t,captureWorkletSrc:n,maxSeconds:a}=e,c=new AudioContext({sampleRate:t,latencyHint:`interactive`}),l=navigator.mediaDevices.getUserMedia({audio:{deviceId:{ideal:`default`},...r}}),u;try{[u]=await Promise.all([l,c.resume(),c.audioWorklet.addModule(n)]),i(c.sampleRate,t,`capture`)}catch(e){throw o(l),await c.close().catch(()=>{}),e}let d=Math.round(t*a)*2,f=[],p=0,m=!1,h=null,g=c.createMediaStreamSource(u),_=s(c,e=>{if(h){h.onChunk(e);return}for(f.push(e),p+=e.byteLength;p>d&&f.length>1;)p-=f.shift()?.byteLength??0},()=>{h?h.onSilent?.():m=!0});g.connect(_.node),_.start();let v=()=>{let e=f;return f=[],p=0,e},y=!1,b=async()=>{y||(y=!0,g.disconnect(),_.node.disconnect(),await c.close().catch(e=>{console.warn(`AudioContext close failed:`,e)}))};return{sampleRate:t,stream:u,ctx:c,handOff(e){h=e;let t=v();return t.length>0&&e.onBuffered(t),m&&e.onSilent?.(),{mic:g,capture:_}},async detach(){return await _.stop(),await b(),{chunks:v(),sampleRate:t}},async close(){y||await _.stop();for(let e of u.getTracks())e.stop();await b()}}}function l(e){let t=new Int16Array(e.length);for(let n=0;n<e.length;n++){let r=Math.max(-1,Math.min(1,e[n]??0));t[n]=r<0?r*32768:r*32767}return t.buffer}async function u(e,t,r){let i=e.reduce((e,t)=>e+t.byteLength/2,0);if(i===0)return[];if(t===r)return e;try{let a=Math.round(i*r/t),o=new OfflineAudioContext(1,a,r),s=o.createBuffer(1,i,t),c=s.getChannelData(0),u=0;for(let t of e)for(let e of new Int16Array(t))c[u++]=e/32768;let d=o.createBufferSource();d.buffer=s,d.connect(o.destination),d.start();let f=(await o.startRendering()).getChannelData(0),p=Math.max(1,Math.round(r*n)),m=[];for(let e=0;e<f.length;e+=p)m.push(l(f.subarray(e,e+p)));return m}catch(e){return console.warn(`[aai-ui] pre-connect audio dropped: resample failed`,e),[]}}async function d(e,t,n){let r=e.createMediaStreamSource(t),i=s(e,n.onMicData,n.onMicSilent);if(r.connect(i.node),n.buffered){let{chunks:t,sampleRate:r}=n.buffered,i=await u(t,r,e.sampleRate);i.length>0&&n.onBuffered(i)}return i.start(),{mic:r,capture:i}}async function f(n){let{sttSampleRate:s,ttsSampleRate:c,captureWorkletSrc:l,playbackWorkletSrc:u,onMicData:f,onError:p,onPlaybackStats:m,onPlaybackProgress:h,onMicSilent:g,preConnect:_}=n,v=n.onPreConnectAudio??(e=>{for(let t of e)f(t)}),y=_?.sampleRate===s?_:null,b=_&&!y?await _.detach():null,x=new AudioContext({sampleRate:c,latencyHint:`playback`}),S=!y&&s===c,C=y?.ctx??(S?x:new AudioContext({sampleRate:s,latencyHint:`interactive`}));async function w(){let e=S?[x]:[x,C];await Promise.all(e.map(e=>e.close().catch(e=>{console.warn(`AudioContext close failed:`,e)})))}let T=_?Promise.resolve(_.stream):navigator.mediaDevices.getUserMedia({audio:{deviceId:{ideal:`default`},...r}}),E;try{[E]=await Promise.all([T,x.resume(),C.resume(),y?void 0:C.audioWorklet.addModule(l),x.audioWorklet.addModule(u)]),i(C.sampleRate,s,`capture`),i(x.sampleRate,c,`playback`)}catch(e){throw o(T),await w(),e}let{mic:D,capture:O}=y?y.handOff({onBuffered:v,onChunk:f,onSilent:g}):await d(C,E,{onMicData:f,onMicSilent:g,buffered:b,onBuffered:v});O.node.onprocessorerror=()=>p?.(a(`capture`));let k=null,A=0,j=null,M=new AbortController;function N(){let e=j;j=null,e?.settle()}function P(e){e.stats&&e.stats.concealedSamples>0&&m?.(e.stats),e.reason!==`interrupt`&&(j===null||e.turn===j.turn)&&N()}function F(){if(k)return k;let e=new AudioWorkletNode(x,`playback-processor`);return e.connect(x.destination),e.port.onmessage=e=>{e.data.event===`stop`?P(e.data):e.data.event===`progress`&&h?.(e.data.bufferedMs)},e.onprocessorerror=()=>{let e=a(`playback`);N(),p?.(e)},k=e,e}let I={enqueue(e){M.signal.aborted||e.byteLength!==0&&F().port.postMessage({event:`write`,buffer:new Uint8Array(e)},[e])},done(){if(!k)return Promise.resolve();let n=++A;if(k.port.postMessage({event:`done`,turn:n}),x.state!==`running`)return N(),Promise.resolve();let{promise:r,resolve:i}=Promise.withResolvers();N();let a=()=>{clearInterval(o),clearTimeout(s),j?.settle===a&&(j=null),i()},o=setInterval(()=>{x.state!==`running`&&a()},e),s=setTimeout(a,t);return j={turn:n,settle:a},r},flush(){k&&(N(),k.port.postMessage({event:`interrupt`}))},async close(){if(!M.signal.aborted){M.abort(),await O.stop(),D.disconnect(),O.node.disconnect(),k&&k.disconnect();for(let e of E.getTracks())e.stop();await w()}},async[Symbol.asyncDispose](){await I.close()}};return I}export{f as createVoiceIO,c as openPreConnectCapture};
@@ -0,0 +1,90 @@
1
+ import{n as e,t}from"./index-BQURVARl.js";import{t as n}from"./_module-url-BX0RuRU2.js";var r=`
2
+ class CaptureProcessor extends AudioWorkletProcessor {
3
+ constructor(options) {
4
+ super();
5
+ this.recording = false;
6
+ const opts = options.processorOptions || {};
7
+ // The context runs at the STT rate, so this is both the input and the
8
+ // output rate — there is nothing to convert.
9
+ this.rate = opts.sampleRate || sampleRate;
10
+ // Int16 accumulation buffer: flushed to the main thread as one transferred
11
+ // ArrayBuffer once ~bufferSeconds of samples are batched. Sized 2x the
12
+ // flush target so a whole render quantum always fits before flushing.
13
+ this.targetSamples = Math.max(1, Math.round(this.rate * (opts.bufferSeconds || ${t})));
14
+ this.pending = new Int16Array(this.targetSamples * 2);
15
+ this.pendingLen = 0;
16
+ // Dead-mic probe: samples left to inspect before concluding the device
17
+ // delivers nothing but digital silence. Only consumed while recording, so
18
+ // the cost disappears after the window (or after the first real sample).
19
+ this.probeSamplesLeft = Math.round(
20
+ (this.rate * (opts.silenceProbeMs ?? ${e})) / 1000,
21
+ );
22
+ this.port.onmessage = (e) => {
23
+ if (e.data.event === 'start') this.recording = true;
24
+ else if (e.data.event === 'stop') {
25
+ // Final flush so the tail of speech isn't dropped on close, then ack
26
+ // so the host knows the tail chunk (if any) has been posted and it is
27
+ // safe to tear the context down.
28
+ this.flush();
29
+ this.recording = false;
30
+ this.port.postMessage({ event: 'stopped' });
31
+ }
32
+ };
33
+ }
34
+
35
+ // Convert Float32 -> Int16 and append to the pending batch. Writes through
36
+ // an Int16Array directly (assignment truncates like DataView.setInt16).
37
+ accumulate(samples) {
38
+ let buf = this.pending;
39
+ if (this.pendingLen + samples.length > buf.length) {
40
+ // Defensive: only reachable if a render quantum outproduces the 1x
41
+ // headroom above the flush target (never with 128-sample quanta).
42
+ const grown = new Int16Array((this.pendingLen + samples.length) * 2);
43
+ grown.set(buf.subarray(0, this.pendingLen));
44
+ this.pending = grown;
45
+ buf = grown;
46
+ }
47
+ for (let i = 0; i < samples.length; i++) {
48
+ const s = Math.max(-1, Math.min(1, samples[i]));
49
+ buf[this.pendingLen++] = s < 0 ? s * 0x8000 : s * 0x7fff;
50
+ }
51
+ }
52
+
53
+ // Post the batched samples as one transferred ArrayBuffer and reset.
54
+ flush() {
55
+ if (this.pendingLen === 0) return;
56
+ const buffer = this.pending.buffer.slice(0, this.pendingLen * 2);
57
+ this.pendingLen = 0;
58
+ this.port.postMessage({ event: 'chunk', buffer }, [buffer]);
59
+ }
60
+
61
+ // Watch the first window of input for any nonzero sample. One is enough to
62
+ // prove the device is live — a real mic in a quiet room still carries a
63
+ // noise floor, so all-zeros means muted, wrong input, or no input at all.
64
+ probeForSilence(channel) {
65
+ for (let i = 0; i < channel.length; i++) {
66
+ if (channel[i] !== 0) {
67
+ this.probeSamplesLeft = 0;
68
+ return;
69
+ }
70
+ }
71
+ this.probeSamplesLeft -= channel.length;
72
+ if (this.probeSamplesLeft <= 0) {
73
+ this.port.postMessage({ event: 'silent' });
74
+ }
75
+ }
76
+
77
+ process(inputs) {
78
+ const input = inputs[0];
79
+ if (!input || !input[0] || !this.recording) return true;
80
+
81
+ if (this.probeSamplesLeft > 0) this.probeForSilence(input[0]);
82
+
83
+ this.accumulate(input[0]);
84
+ if (this.pendingLen >= this.targetSamples) this.flush();
85
+ return true;
86
+ }
87
+ }
88
+
89
+ registerProcessor('capture-processor', CaptureProcessor);
90
+ `,i=n(r);export{i as default};
@@ -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-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction: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-outline-style:solid;--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;--color-red-600:oklch(57.7% .245 27.325);--spacing:.25rem;--container-2xl:42rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--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-out:cubic-bezier(0, 0, .2, 1);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--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);--color-aai-surface:var(--aai-surface,#fff);--color-aai-text:var(--aai-text,#1b1a18);--color-aai-border:var(--aai-border,#dcd7cc);--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}.order-last{order:9999}.col-span-2{grid-column:span 2/span 2}.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)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.ml-auto{margin-left:auto}.box-border{box-sizing:border-box}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-1{height:var(--spacing)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-2\.5{height:calc(var(--spacing) * 2.5)}.h-4{height:calc(var(--spacing) * 4)}.h-6{height:calc(var(--spacing) * 6)}.h-9{height:calc(var(--spacing) * 9)}.h-11{height:calc(var(--spacing) * 11)}.h-\[7px\]{height:7px}.h-full{height:100%}.h-screen{height:100vh}.max-h-64{max-height:calc(var(--spacing) * 64)}.max-h-\[40vh\]{max-height:40vh}.min-h-0{min-height:0}.min-h-5{min-height:calc(var(--spacing) * 5)}.min-h-screen{min-height:100vh}.w-1{width:var(--spacing)}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-2{width:calc(var(--spacing) * 2)}.w-2\.5{width:calc(var(--spacing) * 2.5)}.w-4{width:calc(var(--spacing) * 4)}.w-\[7px\]{width:7px}.w-fit{width:fit-content}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.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-\[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}.basis-full{flex-basis:100%}.border-collapse{border-collapse:collapse}.rotate-90{rotate:90deg}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-pulse{animation:var(--animate-pulse)}.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}.grid-cols-\[1fr_320px\]{grid-template-columns:1fr 320px}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.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)}.gap-6{gap:calc(var(--spacing) * 6)}.self-end{align-self:flex-end}.self-start{align-self:flex-start}.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:2147483647px}.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{border-left-style:var(--tw-border-style);border-left-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}.border-\(--aai-btn-bd\){border-color:var(--aai-btn-bd)}.border-aai-border{border-color:var(--color-aai-border)}.bg-\(--aai-btn-bg\){background-color:var(--aai-btn-bg)}.bg-aai-surface{background-color:var(--color-aai-surface)}.bg-transparent{background-color:#0000}.p-2\.5{padding:calc(var(--spacing) * 2.5)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-7{padding:calc(var(--spacing) * 7)}.p-8{padding:calc(var(--spacing) * 8)}.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-4{padding-inline:calc(var(--spacing) * 4)}.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-1\.5{padding-block:calc(var(--spacing) * 1.5)}.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-1\.5{padding-left:calc(var(--spacing) * 1.5)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-4{padding-left:calc(var(--spacing) * 4)}.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)}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.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-\[0\.625rem\]{font-size:.625rem}.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}.text-\(--aai-btn-fg\){color:var(--aai-btn-fg)}.text-aai-text{color:var(--color-aai-text)}.text-red-600{color:var(--color-red-600)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.underline{text-decoration-line:underline}.underline-offset-2{text-underline-offset:2px}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-\[0_0_6px_currentColor\]{--tw-shadow:0 0 6px var(--tw-shadow-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)}.ring,.ring-1{--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)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + 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)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.blur{--tw-blur:blur(8px);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,)}.invert{--tw-invert:invert(100%);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,)}.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-\[width\]{transition-property:width;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}.duration-200{--tw-duration:.2s;transition-duration:.2s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.file\:mr-3::file-selector-button{margin-right:calc(var(--spacing) * 3)}.file\:cursor-pointer::file-selector-button{cursor:pointer}.file\:rounded-aai::file-selector-button{border-radius:var(--radius-aai)}.file\:border-0::file-selector-button{border-style:var(--tw-border-style);border-width:0}.file\:px-3::file-selector-button{padding-inline:calc(var(--spacing) * 3)}.file\:py-1\.5::file-selector-button{padding-block:calc(var(--spacing) * 1.5)}.file\:font-aai::file-selector-button{font-family:var(--font-aai)}.file\:text-sm::file-selector-button{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.file\:font-medium::file-selector-button{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.file\:\[color\:var\(--aai-file-button-fg\)\]::file-selector-button{color:var(--aai-file-button-fg)}.file\:\[background\:var\(--aai-file-button-bg\)\]::file-selector-button{background:var(--aai-file-button-bg)}.first\:mt-0:first-child{margin-top:0}.last\:mb-0:last-child{margin-bottom:0}.focus-visible\:\[outline\:2px_solid\]:focus-visible{outline:2px solid}.focus-visible\:\[outline-offset\:2px\]:focus-visible{outline-offset:2px}@media (hover:hover){.enabled\:hover\:border-\(--aai-btn-bd-hover\):enabled:hover{border-color:var(--aai-btn-bd-hover)}.enabled\:hover\:bg-\(--aai-btn-bg-hover\):enabled:hover{background-color:var(--aai-btn-bg-hover)}.enabled\:hover\:text-\(--aai-btn-fg-hover\):enabled:hover{color:var(--aai-btn-fg-hover)}}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}@media (width>=40rem){.sm\:ml-auto{margin-left:auto}.sm\:max-w-\[60\%\]{max-width:60%}.sm\:basis-auto{flex-basis:auto}.sm\:px-16{padding-inline:calc(var(--spacing) * 16)}.sm\:py-14{padding-block:calc(var(--spacing) * 14)}}@media (width>=48rem){.md\:order-none{order:0}.md\:h-screen{height:100vh}.md\:max-h-none{max-height:none}.md\:w-\(--aai-sidebar-w\){width:var(--aai-sidebar-w)}.md\:flex-row{flex-direction:row}.md\:border-t-0{border-top-style:var(--tw-border-style);border-top-width:0}.md\:border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.md\:border-b-0{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.md\:border-l{border-left-style:var(--tw-border-style);border-left-width:1px}}}@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}@media (prefers-reduced-motion:reduce){*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}.aai-scroll{scrollbar-width:thin;scrollbar-color:var(--aai-scrollbar-thumb,var(--aai-border,#dcd7cc)) var(--aai-scrollbar-track,transparent)}.aai-scroll::-webkit-scrollbar{width:6px}.aai-scroll::-webkit-scrollbar-track{background:var(--aai-scrollbar-track,transparent)}.aai-scroll::-webkit-scrollbar-thumb{background:var(--aai-scrollbar-thumb,var(--aai-border,#dcd7cc));border-radius:3px}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@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-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{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-outline-style{syntax:"*";inherits:false;initial-value:solid}@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}@keyframes pulse{50%{opacity:.5}}