@alexkroman1/aai-ui 6.11.0 → 7.0.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 (66) hide show
  1. package/README.md +104 -3
  2. package/dist/_run-controls.d.ts +33 -0
  3. package/dist/{chat-view-ByQFf94G.js → chat-view-BKsFfFZJ.js} +57 -11
  4. package/dist/client-config-B4nznRvH.js +134 -0
  5. package/dist/client-config.d.ts +45 -2
  6. package/dist/client-dir.d.ts +3 -1
  7. package/dist/client-dir.js +3 -1
  8. package/dist/components/auto-scroll.d.ts +24 -13
  9. package/dist/components/button.d.ts +8 -4
  10. package/dist/components/button.js +4 -4
  11. package/dist/components/chat-view.d.ts +4 -3
  12. package/dist/components/chat-view.js +1 -1
  13. package/dist/components/console-shell.d.ts +71 -10
  14. package/dist/components/controls.d.ts +15 -4
  15. package/dist/components/controls.js +48 -2
  16. package/dist/components/form-fields.d.ts +142 -0
  17. package/dist/components/form-types.d.ts +6 -0
  18. package/dist/components/form.d.ts +38 -80
  19. package/dist/components/markdown.d.ts +31 -5
  20. package/dist/components/message-list.d.ts +21 -4
  21. package/dist/components/message-list.js +1 -1
  22. package/dist/components/sidebar-layout.d.ts +11 -0
  23. package/dist/components/sidebar-layout.js +2 -0
  24. package/dist/components/start-screen.d.ts +8 -0
  25. package/dist/components/start-screen.js +2 -0
  26. package/dist/components/tool-call-block.js +1 -1
  27. package/dist/components/tool-call-row.d.ts +24 -0
  28. package/dist/components/upload-progress.d.ts +17 -7
  29. package/dist/components/workflow-fields.d.ts +10 -22
  30. package/dist/components/workflow-progress.d.ts +29 -10
  31. package/dist/context.d.ts +36 -0
  32. package/dist/context.js +105 -14
  33. package/dist/default-client/assets/index-S5fkKi6B.css +2 -0
  34. package/dist/default-client/assets/index-fEkrcZgo.js +293 -0
  35. package/dist/default-client/index.html +2 -2
  36. package/dist/define-client.d.ts +67 -62
  37. package/dist/define-client.js +56 -22
  38. package/dist/hooks.d.ts +88 -3
  39. package/dist/index.d.ts +12 -11
  40. package/dist/index.js +501 -167
  41. package/dist/internal.d.ts +40 -0
  42. package/dist/internal.js +6 -0
  43. package/dist/{message-list-CpPV7dGx.js → message-list-DHddO4QC.js} +217 -72
  44. package/dist/{session-core-CAfYmUbg.js → session-core-C2JtLArh.js} +267 -165
  45. package/dist/session-core-audio-setup.d.ts +3 -0
  46. package/dist/session-core-messages.d.ts +3 -0
  47. package/dist/session-core-state.d.ts +146 -0
  48. package/dist/session-core-types.d.ts +77 -32
  49. package/dist/session-core.d.ts +3 -2
  50. package/dist/session-core.js +1 -1
  51. package/dist/{tool-call-block-D6pTEPrT.js → tool-call-block-DoF-cSIZ.js} +27 -19
  52. package/dist/tool-config-context-DzAofqi_.js +19 -0
  53. package/dist/types.d.ts +31 -5
  54. package/dist/types.js +5 -4
  55. package/dist/{controls-CjG91QJ4.js → url-chips-DpM7Oocj.js} +3 -46
  56. package/dist/use-conversation.d.ts +122 -0
  57. package/dist/use-download-url.d.ts +83 -0
  58. package/dist/use-workflow-form.d.ts +59 -3
  59. package/dist/use-workflow-run.d.ts +35 -0
  60. package/dist/use-workflow-stream.d.ts +36 -62
  61. package/dist/workflow-client.d.ts +36 -11
  62. package/dist/workflow-status-labels.d.ts +35 -0
  63. package/package.json +10 -5
  64. package/styles.css +14 -0
  65. package/dist/default-client/assets/index-DTLrhtTF.css +0 -2
  66. package/dist/default-client/assets/index-DXODx_9r.js +0 -293
@@ -0,0 +1,83 @@
1
+ /**
2
+ * `useDownloadUrl` — an upload id a run produced, as something `<audio>`,
3
+ * `<img>` or `<a download>` will accept.
4
+ *
5
+ * `api.download(id)` resolves a `Blob`, and it has to: the byte route takes the
6
+ * same bearer every workflow route does, and neither `<audio src>` nor
7
+ * `<a href>` can send one. So every page that plays back what a run WROTE ends
8
+ * up at the same four lines — `download` → `createObjectURL` → state — and the
9
+ * two that are really the point are the two the four lines are wrapped in:
10
+ *
11
+ * - **`URL.revokeObjectURL` on cleanup.** An object URL pins its blob for the
12
+ * life of the DOCUMENT. Miss it and every completed run's audio stays resident
13
+ * until the tab closes, which on a page people run all day is a leak measured
14
+ * in the size of the files.
15
+ * - **A `cancelled` flag.** A second run settling while the first download is
16
+ * still in flight otherwise sets state from the stale one, and the page plays
17
+ * the previous run's audio under the current run's transcript — a wrong answer
18
+ * that looks like a right one.
19
+ *
20
+ * Two templates had written this hook, identically, doc paragraph included, and
21
+ * `aai-ui` exported no download helper at all. Both also faked `pending` by
22
+ * checking `url === undefined && error === undefined`, which reads "idle" and
23
+ * "downloading" as the same thing — so this reports it.
24
+ */
25
+ import type { WorkflowApi } from "./workflow-client.ts";
26
+ /** Options for {@link useDownloadUrl}. */
27
+ export type UseDownloadUrlOptions = {
28
+ /** The client to read the bytes with. Defaults to one for the page's own agent. */
29
+ api?: WorkflowApi;
30
+ };
31
+ /**
32
+ * What {@link useDownloadUrl} reports.
33
+ *
34
+ * @public
35
+ */
36
+ export type UseDownloadUrlResult = {
37
+ /**
38
+ * An object URL for the stored bytes, once they are here. Valid until the id
39
+ * changes or the component unmounts — do not stash it anywhere that outlives
40
+ * the render that read it.
41
+ */
42
+ url?: string;
43
+ /** The read's failure, as the agent's own sentence where it gave one. */
44
+ error?: string;
45
+ /**
46
+ * True while the bytes are on their way.
47
+ *
48
+ * Its own field rather than "neither `url` nor `error`", which cannot tell a
49
+ * download in flight from no id to download — the two states a page most
50
+ * wants to render differently (a spinner, and nothing at all).
51
+ */
52
+ pending: boolean;
53
+ };
54
+ /**
55
+ * Read an upload's bytes and hand back a URL a DOM element can use.
56
+ *
57
+ * @param uploadId - The id a completed run reported, or `undefined` before one
58
+ * exists — which is what a page passes straight through while it waits, and
59
+ * reports as idle rather than pending.
60
+ * @param opts - See {@link UseDownloadUrlOptions}.
61
+ * @returns See {@link UseDownloadUrlResult}.
62
+ *
63
+ * @example
64
+ * ```tsx
65
+ * import { useDownloadUrl, useWorkflowSubmit } from "@alexkroman1/aai-ui";
66
+ *
67
+ * function Playback() {
68
+ * const { run } = useWorkflowSubmit<{ audio: string }>("spokenSummary");
69
+ * const output = run?.status === "completed" ? run.output : undefined;
70
+ * const audio = useDownloadUrl(output?.audio);
71
+ * if (audio.pending) return <p>Fetching audio…</p>;
72
+ * if (audio.error !== undefined) return <p role="alert">{audio.error}</p>;
73
+ * return audio.url === undefined ? null : (
74
+ * <a href={audio.url} download="summary.mp3">
75
+ * Download
76
+ * </a>
77
+ * );
78
+ * }
79
+ * ```
80
+ *
81
+ * @public
82
+ */
83
+ export declare function useDownloadUrl(uploadId: string | undefined, opts?: UseDownloadUrlOptions): UseDownloadUrlResult;
@@ -32,8 +32,7 @@
32
32
  * `wait` here when the page really does want one request, and the run is
33
33
  * followed from the same id either way.
34
34
  */
35
- import { type WorkflowSummary } from "@alexkroman1/aai";
36
- import type { UploadParallel, UploadProgress } from "@alexkroman1/aai/workflow-api";
35
+ import type { UploadParallel, UploadProgress, WorkflowSummary } from "@alexkroman1/aai/workflow-api";
37
36
  import type { WorkflowApi, WorkflowRun } from "./workflow-client.ts";
38
37
  /** Options for {@link useWorkflows}. */
39
38
  export type UseWorkflowsOptions = {
@@ -69,6 +68,35 @@ export type UseWorkflowsResult = {
69
68
  * empty list — which renders as a form with no fields and reads as "this agent
70
69
  * declares no workflows" about an agent that was merely unreachable.
71
70
  *
71
+ * @example
72
+ * ```tsx
73
+ * import { useWorkflows } from "@alexkroman1/aai-ui";
74
+ *
75
+ * // A page rendering its own chrome from the listing — a picker, say. A form
76
+ * // for ONE workflow wants `<WorkflowFields workflow="name" />` instead,
77
+ * // which does this lookup itself.
78
+ * function WorkflowPicker({ onPick }: { onPick: (name: string) => void }) {
79
+ * const { workflows, loading, error } = useWorkflows();
80
+ * if (loading) return <p>Loading…</p>;
81
+ * if (error !== undefined) return <p role="alert">{error}</p>;
82
+ * return (
83
+ * <ul>
84
+ * {workflows.map((summary) => (
85
+ * <li key={summary.name}>
86
+ * <button type="button" onClick={() => onPick(summary.name)}>
87
+ * {summary.description ?? summary.name}
88
+ * </button>
89
+ * </li>
90
+ * ))}
91
+ * </ul>
92
+ * );
93
+ * }
94
+ * ```
95
+ *
96
+ * @param opts - See {@link UseWorkflowsOptions}.
97
+ * @returns The listing, its loading flag and its failure — see
98
+ * {@link UseWorkflowsResult}.
99
+ *
72
100
  * @public
73
101
  */
74
102
  export declare function useWorkflows(opts?: UseWorkflowsOptions): UseWorkflowsResult;
@@ -99,7 +127,17 @@ export type UploadStatus = UploadProgress & {
99
127
  */
100
128
  paused: boolean;
101
129
  };
102
- /** What {@link useWorkflowSubmit} returns. */
130
+ /**
131
+ * What {@link useWorkflowSubmit} returns.
132
+ *
133
+ * @see {@link WorkflowStreamSubmission} — an ALIAS of this type, returned by
134
+ * `useWorkflowStream`, which is a drop-in sibling. Exactly two fields MEAN
135
+ * something different there, and both differences follow from WHEN the run is
136
+ * created: there, `submit()` resolves when the UPLOAD finishes rather than when
137
+ * the run is accepted, and `run` is non-`undefined` from before the bytes are
138
+ * in, so a page can render `<WorkflowProgress>` beside the upload bar instead
139
+ * of after it. Here the run does not exist until the last byte lands.
140
+ */
103
141
  export type WorkflowSubmission<R = unknown> = {
104
142
  /**
105
143
  * Start a run with this input. Resolves once the run EXISTS — progress
@@ -109,6 +147,24 @@ export type WorkflowSubmission<R = unknown> = {
109
147
  submit: (input: unknown) => Promise<void>;
110
148
  /** Clear the run and any error, putting the form back to its initial state. */
111
149
  reset: () => void;
150
+ /**
151
+ * End the current run's `sleep()` early — "file it now" — resolving how many
152
+ * pending sleeps were interrupted.
153
+ *
154
+ * Bound to the run this submission is following, which is the point: it is
155
+ * the only reason a page holding one of these hooks needed an `api` of its
156
+ * own. `0` is an answer rather than a failure (the run had already moved past
157
+ * its wait, or there is no run yet), so nothing here has to be guarded.
158
+ */
159
+ wake: () => Promise<number>;
160
+ /**
161
+ * Stop the current run, resolving whether this call is what ended it.
162
+ *
163
+ * `false` for a run that had already finished, and for no run at all — the
164
+ * SDK's contract, because two tabs pressing Stop is ordinary. Distinct from
165
+ * `reset()`, which puts the FORM back and leaves the run running.
166
+ */
167
+ cancel: () => Promise<boolean>;
112
168
  /** The run, once started, followed to completion. */
113
169
  run: WorkflowRun<R> | undefined;
114
170
  /**
@@ -43,10 +43,45 @@ export type UseWorkflowRunResult<R = unknown> = {
43
43
  * STOPS on a terminal status, so a finished run costs nothing; passing
44
44
  * `undefined` (nothing started yet) also costs nothing.
45
45
  *
46
+ * @example
47
+ * ```tsx
48
+ * import type { ToolInputSchema, WorkflowDef } from "@alexkroman1/aai";
49
+ * import type { WorkflowOutputOf } from "@alexkroman1/aai/workflow-api";
50
+ * import { useWorkflowRun } from "@alexkroman1/aai-ui";
51
+ *
52
+ * // A real page writes `import type { digest } from "./agent.ts"`. Stood in
53
+ * // for here so the example compiles on its own.
54
+ * declare const digest: WorkflowDef<ToolInputSchema, Promise<{ points: string[] }>>;
55
+ *
56
+ * type Digest = WorkflowOutputOf<typeof digest>;
57
+ *
58
+ * function RunPanel({ runId }: { runId: string | undefined }) {
59
+ * // The type argument is what makes `run.output` a `Digest` below.
60
+ * const { run, error, polling } = useWorkflowRun<Digest>(runId);
61
+ * if (error !== undefined) return <p role="alert">{error}</p>;
62
+ * if (run?.status === "completed") {
63
+ * return (
64
+ * <ul>
65
+ * {run.output.points.map((point) => (
66
+ * <li key={point}>{point}</li>
67
+ * ))}
68
+ * </ul>
69
+ * );
70
+ * }
71
+ * return <p>{polling ? "Working…" : "Nothing running."}</p>;
72
+ * }
73
+ * ```
74
+ *
46
75
  * @typeParam R - The workflow's output type. Supplying it is what makes
47
76
  * `run.status === "completed"` narrow to a typed `run.output` instead of
48
77
  * `unknown`. Derive it with `WorkflowOutputOf<typeof myWorkflow>` — a
49
78
  * type-only import of `agent.ts` is erased, so it costs the bundle nothing.
79
+ * @param runId - The run to watch. `undefined` costs nothing, so a page may
80
+ * pass its state straight through before a run exists.
81
+ * @param opts - `api` when the page holds its own client; `intervalMs` to
82
+ * change the poll interval the stream falls back to.
83
+ * @returns The latest snapshot, the last read's error, and whether the watch
84
+ * is still going — see {@link UseWorkflowRunResult}.
50
85
  *
51
86
  * @public
52
87
  */
@@ -76,68 +76,42 @@
76
76
  * `stream.ts`'s idle bound (five minutes of no new bytes) is what decides that a
77
77
  * pause has become an abandonment.
78
78
  */
79
- import type { UploadParallel } from "@alexkroman1/aai/workflow-api";
80
- import type { UploadStatus } from "./use-workflow-form.ts";
81
- import type { WorkflowApi, WorkflowRun } from "./workflow-client.ts";
82
- /** Options for {@link useWorkflowStream}. */
83
- export type UseWorkflowStreamOptions = {
84
- /** The client to start runs with. Defaults to one for the page's own agent. */
85
- api?: WorkflowApi;
86
- /** Correlation key recorded with the run, for finding it again without the id. */
87
- key?: string;
88
- /** How often the fallback poll re-reads a live run. */
89
- intervalMs?: number;
90
- /**
91
- * Send the file as concurrent parts instead of in one streaming request.
92
- *
93
- * It COMPOSES with what this hook is for rather than competing with it: the run
94
- * still starts before the bytes, and the store still publishes how far the file
95
- * is readable — that number is the CONTIGUOUS prefix, so a run reading ahead of
96
- * the uplink sees the same growing file whether one connection or four are
97
- * filling it. What changes is only how fast it grows.
98
- *
99
- * **On by default.** `false` opts out, `{ partBytes, concurrency }` tunes it.
100
- * See `UploadOptions.parallel`.
101
- */
102
- parallel?: UploadParallel;
103
- };
104
- /** What {@link useWorkflowStream} returns. */
105
- export type WorkflowStreamSubmission<R = unknown> = {
106
- /**
107
- * Start a run and stream this input's file into it.
108
- *
109
- * Resolves when the upload finishes, NOT when the run does — the run's own
110
- * progress arrives through `run`. It resolves rather than rejecting on a failed
111
- * upload; the failure is reported through `error`, the way a form expects.
112
- */
113
- submit: (input: unknown) => Promise<void>;
114
- /** Clear the run and any error, putting the form back to its initial state. */
115
- reset: () => void;
116
- /**
117
- * The run, from the moment it EXISTS — which here is before its bytes are in.
118
- *
119
- * That is the whole difference from `useWorkflowSubmit`, and what lets a page
120
- * render `<WorkflowProgress>` beside the upload bar rather than after it.
121
- */
122
- run: WorkflowRun<R> | undefined;
123
- /** True from `submit()` until the run reaches a terminal status. */
124
- pending: boolean;
125
- /** How far the upload has got, while it is still going. */
126
- upload: UploadStatus | undefined;
127
- /**
128
- * Park the upload where it is, stopping the bytes in flight.
129
- *
130
- * The RUN keeps going — it is watching an upload id, and a paused upload is one
131
- * whose `size` has stopped growing, which is exactly what a slow uplink looks
132
- * like. So a pause costs nothing until the workflow's own idle bound decides the
133
- * uploader is gone (five minutes in `transcription-workflow`).
134
- */
135
- pauseUpload: () => void;
136
- /** Continue a paused upload, sending only the windows the store does not have. */
137
- resumeUpload: () => void;
138
- /** The submit's own failure (a rejected input, or an upload that would not store). */
139
- error: string | undefined;
140
- };
79
+ import type { UseWorkflowSubmitOptions, WorkflowSubmission } from "./use-workflow-form.ts";
80
+ /**
81
+ * Options for {@link useWorkflowStream}.
82
+ *
83
+ * {@link UseWorkflowSubmitOptions} without `wait`, which is the synchronous
84
+ * mode: it holds the `POST` open until the run settles, and here the run is
85
+ * started before its bytes are, so there is nothing left to hold it for.
86
+ *
87
+ * `parallel` COMPOSES with what this hook is for rather than competing with it.
88
+ * The run still starts before the bytes, and the store still publishes how far
89
+ * the file is readable — that number is the CONTIGUOUS prefix, so a run reading
90
+ * ahead of the uplink sees the same growing file whether one connection or four
91
+ * are filling it. What changes is only how fast it grows.
92
+ */
93
+ export type UseWorkflowStreamOptions = Omit<UseWorkflowSubmitOptions, "wait">;
94
+ /**
95
+ * What {@link useWorkflowStream} returns: a {@link WorkflowSubmission}, exactly.
96
+ *
97
+ * The same eight fields `useWorkflowSubmit` returns, of which this hook is a
98
+ * drop-in sibling — same `<Form>`, same `<UploadProgressBar>`, same
99
+ * `<WorkflowProgress>`. An ALIAS rather than a second declaration of the eight:
100
+ * the two have to agree field for field to be drop-in, and two copies of a type
101
+ * that have to agree are two copies that can stop agreeing.
102
+ *
103
+ * Exactly two of the fields mean something different here, and both differences
104
+ * follow from WHEN the run is created — it exists before its bytes do:
105
+ *
106
+ * - `submit()` resolves when the UPLOAD finishes, not when the run is accepted;
107
+ * the run's own progress arrives through `run`. It still resolves rather than
108
+ * rejecting on a failed upload — the failure is reported through `error`, the
109
+ * way a form expects.
110
+ * - `run` is set from the moment the run EXISTS, which here is before the bytes
111
+ * are in. That is what lets a page render `<WorkflowProgress>` beside the
112
+ * upload bar rather than after it.
113
+ */
114
+ export type WorkflowStreamSubmission<R = unknown> = WorkflowSubmission<R>;
141
115
  /**
142
116
  * Start a workflow run and stream a file into it while it works.
143
117
  *
@@ -24,7 +24,7 @@
24
24
  * The loop that keeps asking lives in `use-workflow-run.ts`, and the streaming
25
25
  * fast path under it in `workflow-events.ts`.
26
26
  */
27
- import type { WorkflowRunSnapshot } from "@alexkroman1/aai";
27
+ import type { WorkflowRunSnapshot } from "@alexkroman1/aai/workflow-api";
28
28
  import { type WorkflowApi } from "@alexkroman1/aai/workflow-api";
29
29
  /**
30
30
  * A run's observable state.
@@ -50,7 +50,14 @@ export type WorkflowRun<R = unknown> = WorkflowRunSnapshot<R>;
50
50
  * A workflow's own output type, and the shape `GET /workflows` lists — both
51
51
  * re-exported so a page needs ONE import to type its runs and render its form.
52
52
  */
53
- export type { WorkflowOutputOf, WorkflowSummary } from "@alexkroman1/aai";
53
+ /**
54
+ * The call set {@link createWorkflowApi} returns.
55
+ *
56
+ * Re-exported from the SDK rather than declared here: it IS the SDK's client,
57
+ * and a structural restatement would be a second thing to keep in step with the
58
+ * routes for no gain.
59
+ */
60
+ export type { WorkflowApi, WorkflowOutputOf, WorkflowRunStatus, WorkflowSummary, } from "@alexkroman1/aai/workflow-api";
54
61
  /**
55
62
  * A run status nothing will change again.
56
63
  *
@@ -59,15 +66,7 @@ export type { WorkflowOutputOf, WorkflowSummary } from "@alexkroman1/aai";
59
66
  * forever by a page while the agent considered it finished — the kind of drift a
60
67
  * status predicate beside the status union cannot have.
61
68
  */
62
- export { isTerminal } from "@alexkroman1/aai";
63
- /**
64
- * The call set {@link createWorkflowApi} returns.
65
- *
66
- * Re-exported from the SDK rather than declared here: it IS the SDK's client,
67
- * and a structural restatement would be a second thing to keep in step with the
68
- * routes for no gain.
69
- */
70
- export type { WorkflowApi } from "@alexkroman1/aai/workflow-api";
69
+ export { isTerminal } from "@alexkroman1/aai/workflow-api";
71
70
  export type WorkflowApiOptions = {
72
71
  /**
73
72
  * Base URL of the agent. Defaults to the page's own origin + path, which is
@@ -92,6 +91,32 @@ export type WorkflowApiOptions = {
92
91
  * but a client built in render is still a new `fetch` closure every time and
93
92
  * reads as though it were free.
94
93
  *
94
+ * @example
95
+ * ```tsx
96
+ * import { createWorkflowApi, useWorkflowRun } from "@alexkroman1/aai-ui";
97
+ * import { useState } from "react";
98
+ *
99
+ * // Module scope, not render scope — see above.
100
+ * const api = createWorkflowApi();
101
+ *
102
+ * function StartDigest() {
103
+ * const [runId, setRunId] = useState<string>();
104
+ * const { run } = useWorkflowRun(runId, { api });
105
+ * return (
106
+ * <button
107
+ * type="button"
108
+ * onClick={() => void api.start("digest", { url: "…" }).then(setRunId)}
109
+ * >
110
+ * {run ? run.status : "Start"}
111
+ * </button>
112
+ * );
113
+ * }
114
+ * ```
115
+ *
116
+ * @param opts - See {@link WorkflowApiOptions}. Both fields are optional; the
117
+ * default base URL is the page's own origin and path.
118
+ * @returns The call set — see {@link WorkflowApi}.
119
+ *
95
120
  * @public
96
121
  */
97
122
  export declare function createWorkflowApi(opts?: WorkflowApiOptions): WorkflowApi;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * One line describing where a run has got to, for each status there is.
3
+ *
4
+ * Two template pages carried a byte-identical `Record<WorkflowRun["status"],
5
+ * string>` with the same doc comment, differing only in the `running` label —
6
+ * which is the one a page really does want to write for itself ("Writing…",
7
+ * "Transcribing…") and the only one. The other four are the same sentence about
8
+ * the same five-member union everywhere.
9
+ *
10
+ * The exhaustiveness argument both copies were written for survives, and moves
11
+ * to the SDK boundary: this is a `Record` over {@link WorkflowRunStatus} rather
12
+ * than a `switch`, so a status added to the SDK is a compile error HERE — in one
13
+ * place that every page then inherits — instead of falling through a `default:`
14
+ * in each of them into whichever line was last. A page overriding one member
15
+ * keeps that: spreading a complete record cannot drop a key.
16
+ */
17
+ import type { WorkflowRunStatus } from "@alexkroman1/aai/workflow-api";
18
+ /**
19
+ * The default status line per {@link WorkflowRunStatus}.
20
+ *
21
+ * Override the ones your page has a better word for and keep the rest:
22
+ *
23
+ * ```ts
24
+ * import { WORKFLOW_STATUS_LABELS } from "@alexkroman1/aai-ui";
25
+ *
26
+ * const STATUS_LINE = { ...WORKFLOW_STATUS_LABELS, running: "Writing…" };
27
+ * ```
28
+ *
29
+ * The wording is deliberately about the RUN rather than about the work — a page
30
+ * knows what its workflow does and this does not, so `running` is the neutral
31
+ * "Working…" and every page that cares replaces exactly that key.
32
+ *
33
+ * @public
34
+ */
35
+ export declare const WORKFLOW_STATUS_LABELS: Readonly<Record<WorkflowRunStatus, string>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alexkroman1/aai-ui",
3
- "version": "6.11.0",
3
+ "version": "7.0.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -17,9 +17,13 @@
17
17
  "types": "./dist/client-dir.d.ts",
18
18
  "import": "./dist/client-dir.js"
19
19
  },
20
+ "./internal": {
21
+ "@dev/source": "./internal.ts",
22
+ "types": "./dist/internal.d.ts",
23
+ "import": "./dist/internal.js"
24
+ },
20
25
  "./styles.css": "./styles.css",
21
- "./package.json": "./package.json",
22
- "./default-client/*": "./dist/default-client/*"
26
+ "./package.json": "./package.json"
23
27
  },
24
28
  "dependencies": {
25
29
  "clsx": "^2.1.1",
@@ -28,7 +32,8 @@
28
32
  "remark-gfm": "^4.0.1",
29
33
  "use-stick-to-bottom": "^1.1.6",
30
34
  "use-sync-external-store": "^1.6.0",
31
- "@alexkroman1/aai": "6.11.0"
35
+ "xstate": "^5.32.5",
36
+ "@alexkroman1/aai": "7.0.0"
32
37
  },
33
38
  "peerDependencies": {
34
39
  "react": "^19.0.0",
@@ -71,6 +76,6 @@
71
76
  "typecheck": "tsc --noEmit",
72
77
  "lint": "biome check .",
73
78
  "check:publint": "publint",
74
- "check:attw": "attw --pack --profile esm-only --entrypoints ."
79
+ "check:attw": "attw --pack --profile esm-only --exclude-entrypoints styles.css"
75
80
  }
76
81
  }
package/styles.css CHANGED
@@ -11,8 +11,22 @@
11
11
  * graceful Georgia-class fallbacks).
12
12
  * - font-aai-mono: JetBrains Mono stand-in for code/tool rows.
13
13
  * - radius-aai: the refresh's squared-off 4px control radius.
14
+ * - color-aai-*: the five `ClientTheme` fields, so a Tailwind class can see
15
+ * them. `ThemeProvider` writes `--aai-bg` … `--aai-primary` onto `:root`
16
+ * from `client({ theme })`; these map them into Tailwind's `--color-*`
17
+ * namespace, which is what makes `bg-aai-surface`, `text-aai-text`,
18
+ * `border-aai-border` and `text-aai-primary` real utilities. The fallbacks
19
+ * ARE the default theme, so the classes work on a page that mounts no
20
+ * provider at all (a static page, a Storybook frame) instead of collapsing
21
+ * to transparent — and they must be kept in step with `DEFAULT_THEME` in
22
+ * `context.ts`, which `theme-css-vars.test.ts` asserts.
14
23
  */
15
24
  @theme {
25
+ --color-aai-bg: var(--aai-bg, #fbf8f2);
26
+ --color-aai-surface: var(--aai-surface, #ffffff);
27
+ --color-aai-text: var(--aai-text, #1b1a18);
28
+ --color-aai-border: var(--aai-border, #dcd7cc);
29
+ --color-aai-primary: var(--aai-primary, #3f2bc1);
16
30
  --font-aai:
17
31
  "Monument Grotesk", "ABC Monument Grotesk", ui-sans-serif, system-ui, -apple-system, sans-serif;
18
32
  --font-aai-serif:
@@ -1,2 +0,0 @@
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;--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-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);--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}.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)}.box-border{box-sizing:border-box}.block{display:block}.contents{display:contents}.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-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\.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-\[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}.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)}.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)}.bg-\(--aai-btn-bg\){background-color:var(--aai-btn-bg)}.bg-transparent{background-color:#0000}.p-2\.5{padding:calc(var(--spacing) * 2.5)}.p-4{padding:calc(var(--spacing) * 4)}.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-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-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)}.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-60{opacity:.6}.opacity-70{opacity:.7}.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)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.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}@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}}