@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.
- package/README.md +104 -3
- package/dist/_run-controls.d.ts +33 -0
- package/dist/{chat-view-ByQFf94G.js → chat-view-BKsFfFZJ.js} +57 -11
- package/dist/client-config-B4nznRvH.js +134 -0
- package/dist/client-config.d.ts +45 -2
- package/dist/client-dir.d.ts +3 -1
- package/dist/client-dir.js +3 -1
- package/dist/components/auto-scroll.d.ts +24 -13
- package/dist/components/button.d.ts +8 -4
- package/dist/components/button.js +4 -4
- package/dist/components/chat-view.d.ts +4 -3
- package/dist/components/chat-view.js +1 -1
- package/dist/components/console-shell.d.ts +71 -10
- package/dist/components/controls.d.ts +15 -4
- package/dist/components/controls.js +48 -2
- package/dist/components/form-fields.d.ts +142 -0
- package/dist/components/form-types.d.ts +6 -0
- package/dist/components/form.d.ts +38 -80
- package/dist/components/markdown.d.ts +31 -5
- package/dist/components/message-list.d.ts +21 -4
- package/dist/components/message-list.js +1 -1
- package/dist/components/sidebar-layout.d.ts +11 -0
- package/dist/components/sidebar-layout.js +2 -0
- package/dist/components/start-screen.d.ts +8 -0
- package/dist/components/start-screen.js +2 -0
- package/dist/components/tool-call-block.js +1 -1
- package/dist/components/tool-call-row.d.ts +24 -0
- package/dist/components/upload-progress.d.ts +17 -7
- package/dist/components/workflow-fields.d.ts +10 -22
- package/dist/components/workflow-progress.d.ts +29 -10
- package/dist/context.d.ts +36 -0
- package/dist/context.js +105 -14
- package/dist/default-client/assets/index-S5fkKi6B.css +2 -0
- package/dist/default-client/assets/index-fEkrcZgo.js +293 -0
- package/dist/default-client/index.html +2 -2
- package/dist/define-client.d.ts +67 -62
- package/dist/define-client.js +56 -22
- package/dist/hooks.d.ts +88 -3
- package/dist/index.d.ts +12 -11
- package/dist/index.js +501 -167
- package/dist/internal.d.ts +40 -0
- package/dist/internal.js +6 -0
- package/dist/{message-list-CpPV7dGx.js → message-list-DHddO4QC.js} +217 -72
- package/dist/{session-core-CAfYmUbg.js → session-core-C2JtLArh.js} +267 -165
- package/dist/session-core-audio-setup.d.ts +3 -0
- package/dist/session-core-messages.d.ts +3 -0
- package/dist/session-core-state.d.ts +146 -0
- package/dist/session-core-types.d.ts +77 -32
- package/dist/session-core.d.ts +3 -2
- package/dist/session-core.js +1 -1
- package/dist/{tool-call-block-D6pTEPrT.js → tool-call-block-DoF-cSIZ.js} +27 -19
- package/dist/tool-config-context-DzAofqi_.js +19 -0
- package/dist/types.d.ts +31 -5
- package/dist/types.js +5 -4
- package/dist/{controls-CjG91QJ4.js → url-chips-DpM7Oocj.js} +3 -46
- package/dist/use-conversation.d.ts +122 -0
- package/dist/use-download-url.d.ts +83 -0
- package/dist/use-workflow-form.d.ts +59 -3
- package/dist/use-workflow-run.d.ts +35 -0
- package/dist/use-workflow-stream.d.ts +36 -62
- package/dist/workflow-client.d.ts +36 -11
- package/dist/workflow-status-labels.d.ts +35 -0
- package/package.json +10 -5
- package/styles.css +14 -0
- package/dist/default-client/assets/index-DTLrhtTF.css +0 -2
- 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 {
|
|
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
|
-
/**
|
|
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 {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
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": "
|
|
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
|
-
"
|
|
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}}
|