@chai-ui/core 0.6.0 → 0.7.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 (52) hide show
  1. package/README.md +10 -5
  2. package/dist/composer-view.d.ts +93 -0
  3. package/dist/composer-view.d.ts.map +1 -0
  4. package/dist/composer-view.js +142 -0
  5. package/dist/composer-view.js.map +1 -0
  6. package/dist/composer.d.ts +188 -0
  7. package/dist/composer.d.ts.map +1 -0
  8. package/dist/composer.js +326 -0
  9. package/dist/composer.js.map +1 -0
  10. package/dist/edit-card.d.ts +53 -0
  11. package/dist/edit-card.d.ts.map +1 -0
  12. package/dist/edit-card.js +119 -0
  13. package/dist/edit-card.js.map +1 -0
  14. package/dist/index.d.ts +7 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +7 -0
  17. package/dist/index.js.map +1 -1
  18. package/dist/media-actions.d.ts +19 -0
  19. package/dist/media-actions.d.ts.map +1 -0
  20. package/dist/media-actions.js +57 -0
  21. package/dist/media-actions.js.map +1 -0
  22. package/dist/media-analyzer.d.ts +101 -0
  23. package/dist/media-analyzer.d.ts.map +1 -0
  24. package/dist/media-analyzer.js +215 -0
  25. package/dist/media-analyzer.js.map +1 -0
  26. package/dist/reasoning.d.ts +59 -0
  27. package/dist/reasoning.d.ts.map +1 -0
  28. package/dist/reasoning.js +55 -0
  29. package/dist/reasoning.js.map +1 -0
  30. package/dist/region-edit.d.ts +2 -0
  31. package/dist/region-edit.d.ts.map +1 -1
  32. package/dist/region-edit.js +6 -0
  33. package/dist/region-edit.js.map +1 -1
  34. package/dist/result-format.d.ts +11 -0
  35. package/dist/result-format.d.ts.map +1 -0
  36. package/dist/result-format.js +30 -0
  37. package/dist/result-format.js.map +1 -0
  38. package/dist/unique-id.d.ts +3 -0
  39. package/dist/unique-id.d.ts.map +1 -0
  40. package/dist/unique-id.js +7 -0
  41. package/dist/unique-id.js.map +1 -0
  42. package/package.json +2 -1
  43. package/src/composer-view.ts +222 -0
  44. package/src/composer.ts +525 -0
  45. package/src/edit-card.ts +138 -0
  46. package/src/index.ts +7 -0
  47. package/src/media-actions.ts +55 -0
  48. package/src/media-analyzer.ts +293 -0
  49. package/src/reasoning.ts +90 -0
  50. package/src/region-edit.ts +8 -0
  51. package/src/result-format.ts +34 -0
  52. package/src/unique-id.ts +6 -0
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Download and share for a piece of generated media, shared by the cards in
3
+ * every framework binding.
4
+ *
5
+ * A plain `<a download href={remoteUrl}>` only forces a download for a
6
+ * same-origin or `blob:`/`data:` URL; for a cross-origin CDN URL (what a
7
+ * generated result usually is) browsers ignore `download` and open it
8
+ * instead. Fetching it into a blob first works wherever the source allows
9
+ * cross-origin fetches. When it doesn't, opening the media is the fallback.
10
+ */
11
+ export async function downloadMedia(src, filename) {
12
+ try {
13
+ const response = await fetch(src);
14
+ const blob = await response.blob();
15
+ const blobUrl = URL.createObjectURL(blob);
16
+ const a = document.createElement("a");
17
+ a.href = blobUrl;
18
+ a.download = filename;
19
+ document.body.appendChild(a);
20
+ a.click();
21
+ a.remove();
22
+ URL.revokeObjectURL(blobUrl);
23
+ }
24
+ catch {
25
+ window.open(src, "_blank", "noopener");
26
+ }
27
+ }
28
+ /**
29
+ * The native share sheet where there is one, otherwise the media's URL
30
+ * copied to the clipboard. Says which, so the button can confirm a copy.
31
+ */
32
+ export async function shareMedia(src) {
33
+ if (navigator.share) {
34
+ try {
35
+ await navigator.share({ url: src });
36
+ return "shared";
37
+ }
38
+ catch {
39
+ // Dismissed, or not shareable: same as closing a native share sheet.
40
+ return "none";
41
+ }
42
+ }
43
+ try {
44
+ await navigator.clipboard.writeText(src);
45
+ return "copied";
46
+ }
47
+ catch {
48
+ return "none";
49
+ }
50
+ }
51
+ /** A file name for a download: the URL's own extension when it has one, otherwise a guess by kind. */
52
+ export function downloadFilename(id, kind, src) {
53
+ const match = /\.([a-zA-Z0-9]{2,5})(?:[?#]|$)/.exec(src);
54
+ const ext = match?.[1] ?? (kind === "video" ? "mp4" : kind === "audio" ? "mp3" : kind === "text" ? "txt" : "png");
55
+ return `${kind}-${id}.${ext}`;
56
+ }
57
+ //# sourceMappingURL=media-actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-actions.js","sourceRoot":"","sources":["../src/media-actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAW,EAAE,QAAgB;IAC/D,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;QAClC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,CAAC,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QACtC,CAAC,CAAC,IAAI,GAAG,OAAO,CAAC;QACjB,CAAC,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACtB,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;QAC7B,CAAC,CAAC,KAAK,EAAE,CAAC;QACV,CAAC,CAAC,MAAM,EAAE,CAAC;QACX,GAAG,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW;IAC1C,IAAI,SAAS,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;YACpC,OAAO,QAAQ,CAAC;QAClB,CAAC;QAAC,MAAM,CAAC;YACP,qEAAqE;YACrE,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IACD,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACzC,OAAO,QAAQ,CAAC;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC;IAChB,CAAC;AACH,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,gBAAgB,CAAC,EAAU,EAAE,IAAY,EAAE,GAAW;IACpE,MAAM,KAAK,GAAG,gCAAgC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzD,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAClH,OAAO,GAAG,IAAI,IAAI,EAAE,IAAI,GAAG,EAAE,CAAC;AAChC,CAAC"}
@@ -0,0 +1,101 @@
1
+ import { type GenerationEngine } from "./engine.js";
2
+ import { type MediaAnalysisPromptLength } from "./media-analysis-prompts.js";
3
+ import { type ModelRoutingInput, type ModelRoutingResult } from "./model-routing.js";
4
+ import { type Reasoning } from "./reasoning.js";
5
+ import type { DroppedMedia, MediaKind, ModelOption } from "./types.js";
6
+ export interface MediaAnalyzerAttachment extends DroppedMedia {
7
+ id: string;
8
+ }
9
+ export interface MediaAnalyzerSubmitPayload {
10
+ attachments: MediaAnalyzerAttachment[];
11
+ modelId: string | null;
12
+ /** When true, the analyzer routes among `models` at submit instead of using `modelId`. */
13
+ autoSelectModel: boolean;
14
+ /** The models offered for the attached media's kind. */
15
+ models: ModelOption[];
16
+ /** `null` until a person actually picks one — see `MediaAnalyzer`'s `promptLength` prop for why this doesn't default to "concise". */
17
+ promptLength: MediaAnalysisPromptLength | null;
18
+ }
19
+ export interface MediaAnalyzerOptions {
20
+ /**
21
+ * Overrides the nearest `ChaiProvider`'s reasoning engine. With neither,
22
+ * a mock text engine answers, so a first render works with no key.
23
+ */
24
+ engine?: GenerationEngine;
25
+ /** The model when the person hasn't picked one. Defaults to the provider's reasoning model for the media's kind. */
26
+ model?: string;
27
+ onResult?: (prompt: string) => void;
28
+ onError?: (message: string) => void;
29
+ /** Auto-select: fires with the routed model so the trigger shows it. Wire it to `MediaAnalyzer`'s `onModelChange` state. */
30
+ onModelChange?: (modelId: string) => void;
31
+ /** Replaces the routing instruction (`buildModelRoutingPrompt`). */
32
+ routingPrompt?: (input: ModelRoutingInput) => string;
33
+ /** Fires when auto-select fell back to the first listed model (no reasoning model, a failed call, or an unlisted reply). Analysis still runs. */
34
+ onRoutingFallback?: (result: Extract<ModelRoutingResult, {
35
+ status: "fallback";
36
+ }>) => void;
37
+ }
38
+ export interface MediaAnalyzerState {
39
+ analyzing: boolean;
40
+ error: string | null;
41
+ prompt: string | null;
42
+ }
43
+ /**
44
+ * `getOptions` is read on every call, so options can change between
45
+ * renders; `getProvided` is the nearest provider's reasoning, if any.
46
+ */
47
+ export declare function createMediaAnalyzerStore(getOptions: () => MediaAnalyzerOptions, getProvided?: () => Reasoning | undefined): {
48
+ getState: () => MediaAnalyzerState;
49
+ subscribe(listener: () => void): () => undefined;
50
+ submit: (payload: MediaAnalyzerSubmitPayload) => void;
51
+ cancel: () => void;
52
+ reset: () => void;
53
+ };
54
+ export type MediaAnalyzerStore = ReturnType<typeof createMediaAnalyzerStore>;
55
+ export declare const MEDIA_KIND_LABEL: Record<MediaKind, string>;
56
+ /** Clarifies what "Terse"/"Concise"/"Detailed" mean in the "Prompt length" menu's rows. UI copy, not sent to any model. */
57
+ export declare const PROMPT_LENGTH_HINT: Record<MediaAnalysisPromptLength, string>;
58
+ /**
59
+ * How many attachments of each kind an analysis can hold at once. Image
60
+ * defaults to 5 (a sequence/series reads as one analysis); video and audio
61
+ * default to 1. Only one *kind* is ever active per analysis — these caps only
62
+ * bound how many of that one kind can pile up before the drop zone stops
63
+ * accepting more. `text` is output, never dropped: present only so this
64
+ * stays a total map.
65
+ */
66
+ export declare const DEFAULT_MAX_ATTACHMENTS_BY_KIND: Record<MediaKind, number>;
67
+ /**
68
+ * Applies one newly-read file to the current attachment list. Only one media
69
+ * *kind* exists per analysis: a different kind replaces the whole list,
70
+ * never merges. Video and audio are single-slot (a same-kind drop replaces
71
+ * it); image fills up to `max`, then silently stops accepting more (the drop
72
+ * zone's "maximum added" look explains why).
73
+ */
74
+ export declare function nextMediaAnalyzerAttachments(current: MediaAnalyzerAttachment[], incoming: DroppedMedia, maxByKind?: Record<MediaKind, number>): MediaAnalyzerAttachment[];
75
+ /** True once a same-kind drop would be rejected: only ever for image, since video and audio replace. */
76
+ export declare function isMediaAnalyzerAtCap(current: MediaAnalyzerAttachment[], maxByKind?: Record<MediaKind, number>): boolean;
77
+ /**
78
+ * Reads image, video and audio files as data URLs, all of them before
79
+ * returning, so a caller applies them in drop order in one go (applying
80
+ * each as its reader finishes would stack them on a stale list).
81
+ * `unsupported` names the files of another type, or that couldn't be read.
82
+ */
83
+ export declare function readMediaFiles(files: Iterable<File>): Promise<{
84
+ media: DroppedMedia[];
85
+ unsupported: string[];
86
+ }>;
87
+ /** "Replaced 2 images with clip.wav.", or `null` when nothing was replaced. */
88
+ export declare function replacedNote(before: MediaAnalyzerAttachment[], after: MediaAnalyzerAttachment[]): string | null;
89
+ /**
90
+ * The "Select model" menu's sections: once a kind is attached, only its
91
+ * models (falling back to `models`); before that, all three kinds, each
92
+ * under its own header.
93
+ */
94
+ export declare function mediaAnalyzerModelSections(kind: MediaKind | undefined, modelsByKind: Partial<Record<MediaKind, ModelOption[]>>, models: ModelOption[]): {
95
+ label: string;
96
+ options: {
97
+ id: string;
98
+ label: string;
99
+ }[];
100
+ }[];
101
+ //# sourceMappingURL=media-analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-analyzer.d.ts","sourceRoot":"","sources":["../src/media-analyzer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,215 @@
1
+ /**
2
+ * The Media Analyzer's behavior with no framework: sends the attached media
3
+ * and the instruction for its kind and prompt length
4
+ * (`MEDIA_ANALYSIS_PROMPTS`) to the reasoning engine, and keeps the
5
+ * generated prompt, which fills in as the text streams. A small store
6
+ * (`getState` + `subscribe`) that `useMediaAnalyzer` (React) and the Vue
7
+ * composable wrap.
8
+ */
9
+ import { uniqueId } from "./unique-id.js";
10
+ import { warnNoReasoningOnce } from "./composer.js";
11
+ import { createMockEngine, isAbortError, normalizeGenerationError } from "./engine.js";
12
+ import { DEFAULT_MEDIA_ANALYSIS_PROMPT_LENGTH, MEDIA_ANALYSIS_PROMPTS, } from "./media-analysis-prompts.js";
13
+ import { routeModel } from "./model-routing.js";
14
+ import { reasoningModelFor } from "./reasoning.js";
15
+ const mockTextEngine = createMockEngine({ outputKind: "text" });
16
+ /**
17
+ * `getOptions` is read on every call, so options can change between
18
+ * renders; `getProvided` is the nearest provider's reasoning, if any.
19
+ */
20
+ export function createMediaAnalyzerStore(getOptions, getProvided = () => undefined) {
21
+ let state = { analyzing: false, error: null, prompt: null };
22
+ const listeners = new Set();
23
+ const set = (next) => {
24
+ state = { ...state, ...next };
25
+ for (const l of listeners)
26
+ l();
27
+ };
28
+ // Each submit gets an id; a late reply from an earlier submit is dropped,
29
+ // and its call aborted.
30
+ let callId = 0;
31
+ let controller = null;
32
+ function submit(payload) {
33
+ const opts = getOptions();
34
+ const reasoning = getProvided();
35
+ const id = ++callId;
36
+ controller?.abort();
37
+ const { signal } = (controller = new AbortController());
38
+ set({ error: null, prompt: null });
39
+ const kind = payload.attachments[0]?.kind;
40
+ if (kind !== "image" && kind !== "video" && kind !== "audio") {
41
+ const message = "Attach an image, video, or audio file to analyze.";
42
+ set({ error: message });
43
+ opts.onError?.(message);
44
+ return;
45
+ }
46
+ const engine = opts.engine ?? reasoning?.engine ?? mockTextEngine;
47
+ const defaultModel = opts.model ?? (reasoning ? reasoningModelFor(reasoning, payload.attachments) : undefined);
48
+ const instruction = MEDIA_ANALYSIS_PROMPTS[kind][payload.promptLength ?? DEFAULT_MEDIA_ANALYSIS_PROMPT_LENGTH];
49
+ const current = () => id === callId;
50
+ const autoSelect = payload.autoSelectModel && payload.models.length > 0;
51
+ const analyze = (modelId) => engine
52
+ .generate({
53
+ modelId,
54
+ prompt: instruction,
55
+ attachments: payload.attachments,
56
+ onText: (text) => current() && set({ prompt: text }),
57
+ signal,
58
+ })
59
+ .then(({ src }) => {
60
+ if (!current())
61
+ return;
62
+ set({ prompt: src });
63
+ getOptions().onResult?.(src);
64
+ }, (err) => {
65
+ if (!current() || isAbortError(err))
66
+ return;
67
+ const { message } = normalizeGenerationError(err);
68
+ set({ error: message });
69
+ getOptions().onError?.(message);
70
+ })
71
+ .finally(() => current() && set({ analyzing: false }));
72
+ set({ analyzing: true });
73
+ if (!autoSelect) {
74
+ void analyze(payload.modelId ?? defaultModel);
75
+ return;
76
+ }
77
+ // Auto-select: the reasoning model looks at the media and picks the
78
+ // analysis model first. A routing problem falls back, never blocks.
79
+ void routeModel({
80
+ prompt: "",
81
+ outputKind: "text",
82
+ attachmentKinds: payload.attachments.map((a) => a.kind),
83
+ attachments: payload.attachments,
84
+ models: payload.models,
85
+ engine: opts.engine ?? reasoning?.engine,
86
+ model: defaultModel,
87
+ routingPrompt: opts.routingPrompt,
88
+ signal,
89
+ }).then((routed) => {
90
+ if (!current() || signal.aborted)
91
+ return;
92
+ const latest = getOptions();
93
+ if (routed.status === "fallback") {
94
+ if (routed.reason === "no-reasoning")
95
+ warnNoReasoningOnce("analysis runs on the first listed model");
96
+ latest.onRoutingFallback?.(routed);
97
+ }
98
+ latest.onModelChange?.(routed.modelId);
99
+ return analyze(routed.modelId);
100
+ });
101
+ }
102
+ /** Stops the analysis in flight (cancelling at the provider where it can), keeping any text that already streamed in. */
103
+ function cancel() {
104
+ callId += 1;
105
+ controller?.abort();
106
+ controller = null;
107
+ set({ analyzing: false });
108
+ }
109
+ /** Stops anything in flight and clears the prompt and error, e.g. when the person changes their media. */
110
+ function reset() {
111
+ cancel();
112
+ set({ error: null, prompt: null });
113
+ }
114
+ return {
115
+ getState: () => state,
116
+ subscribe(listener) {
117
+ listeners.add(listener);
118
+ return () => void listeners.delete(listener);
119
+ },
120
+ submit,
121
+ cancel,
122
+ reset,
123
+ };
124
+ }
125
+ // --- Attachments and menus: what the MediaAnalyzer component shows, the same in every framework binding.
126
+ const ACCEPTED_KINDS = ["image", "video", "audio"];
127
+ export const MEDIA_KIND_LABEL = { image: "Image", video: "Video", audio: "Audio", text: "Text" };
128
+ /** Clarifies what "Terse"/"Concise"/"Detailed" mean in the "Prompt length" menu's rows. UI copy, not sent to any model. */
129
+ export const PROMPT_LENGTH_HINT = {
130
+ terse: "short",
131
+ concise: "moderate",
132
+ detailed: "long",
133
+ };
134
+ /**
135
+ * How many attachments of each kind an analysis can hold at once. Image
136
+ * defaults to 5 (a sequence/series reads as one analysis); video and audio
137
+ * default to 1. Only one *kind* is ever active per analysis — these caps only
138
+ * bound how many of that one kind can pile up before the drop zone stops
139
+ * accepting more. `text` is output, never dropped: present only so this
140
+ * stays a total map.
141
+ */
142
+ export const DEFAULT_MAX_ATTACHMENTS_BY_KIND = { image: 5, video: 1, audio: 1, text: 0 };
143
+ /**
144
+ * Applies one newly-read file to the current attachment list. Only one media
145
+ * *kind* exists per analysis: a different kind replaces the whole list,
146
+ * never merges. Video and audio are single-slot (a same-kind drop replaces
147
+ * it); image fills up to `max`, then silently stops accepting more (the drop
148
+ * zone's "maximum added" look explains why).
149
+ */
150
+ export function nextMediaAnalyzerAttachments(current, incoming, maxByKind = DEFAULT_MAX_ATTACHMENTS_BY_KIND) {
151
+ const existingKind = current[0]?.kind;
152
+ const withId = { ...incoming, id: uniqueId() };
153
+ if (!existingKind || existingKind !== incoming.kind)
154
+ return [withId];
155
+ if (incoming.kind === "image") {
156
+ if (current.length >= (maxByKind.image ?? DEFAULT_MAX_ATTACHMENTS_BY_KIND.image))
157
+ return current;
158
+ return [...current, withId];
159
+ }
160
+ return [withId];
161
+ }
162
+ /** True once a same-kind drop would be rejected: only ever for image, since video and audio replace. */
163
+ export function isMediaAnalyzerAtCap(current, maxByKind = DEFAULT_MAX_ATTACHMENTS_BY_KIND) {
164
+ if (current[0]?.kind !== "image")
165
+ return false;
166
+ return current.length >= (maxByKind.image ?? DEFAULT_MAX_ATTACHMENTS_BY_KIND.image);
167
+ }
168
+ /**
169
+ * Reads image, video and audio files as data URLs, all of them before
170
+ * returning, so a caller applies them in drop order in one go (applying
171
+ * each as its reader finishes would stack them on a stale list).
172
+ * `unsupported` names the files of another type, or that couldn't be read.
173
+ */
174
+ export async function readMediaFiles(files) {
175
+ const unsupported = [];
176
+ const read = await Promise.all([...files].map((file) => {
177
+ const kind = ACCEPTED_KINDS.find((k) => k === file.type.split("/")[0]);
178
+ if (!kind) {
179
+ unsupported.push(file.name);
180
+ return null;
181
+ }
182
+ return new Promise((resolve) => {
183
+ const reader = new FileReader();
184
+ reader.onload = () => resolve({ src: String(reader.result), kind, name: file.name, size: file.size });
185
+ reader.onerror = () => {
186
+ unsupported.push(file.name);
187
+ resolve(null);
188
+ };
189
+ reader.readAsDataURL(file);
190
+ });
191
+ }));
192
+ return { media: read.filter((m) => m !== null), unsupported };
193
+ }
194
+ /** "Replaced 2 images with clip.wav.", or `null` when nothing was replaced. */
195
+ export function replacedNote(before, after) {
196
+ const removed = before.filter((a) => !after.some((n) => n.id === a.id));
197
+ const added = after.filter((n) => !before.some((a) => a.id === n.id));
198
+ if (removed.length === 0 || added.length === 0)
199
+ return null;
200
+ const name = (a) => a.name ?? MEDIA_KIND_LABEL[a.kind].toLowerCase();
201
+ const describe = (list) => list.length === 1 ? name(list[0]) : `${list.length} ${MEDIA_KIND_LABEL[list[0].kind].toLowerCase()}s`;
202
+ return `Replaced ${describe(removed)} with ${describe(added)}.`;
203
+ }
204
+ /**
205
+ * The "Select model" menu's sections: once a kind is attached, only its
206
+ * models (falling back to `models`); before that, all three kinds, each
207
+ * under its own header.
208
+ */
209
+ export function mediaAnalyzerModelSections(kind, modelsByKind, models) {
210
+ const options = (list = []) => list.map((m) => ({ id: m.id, label: m.label }));
211
+ return kind
212
+ ? [{ label: MEDIA_KIND_LABEL[kind], options: options(modelsByKind[kind] ?? models) }]
213
+ : ["image", "video", "audio"].map((k) => ({ label: MEDIA_KIND_LABEL[k], options: options(modelsByKind[k]) }));
214
+ }
215
+ //# sourceMappingURL=media-analyzer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-analyzer.js","sourceRoot":"","sources":["../src/media-analyzer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,wBAAwB,EAAyB,MAAM,aAAa,CAAC;AAC9G,OAAO,EACL,oCAAoC,EACpC,sBAAsB,GAEvB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,UAAU,EAAmD,MAAM,oBAAoB,CAAC;AACjG,OAAO,EAAE,iBAAiB,EAAkB,MAAM,gBAAgB,CAAC;AA0CnE,MAAM,cAAc,GAAG,gBAAgB,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC;AAEhE;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CACtC,UAAsC,EACtC,cAA2C,GAAG,EAAE,CAAC,SAAS;IAE1D,IAAI,KAAK,GAAuB,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAChF,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAC;IACxC,MAAM,GAAG,GAAG,CAAC,IAAiC,EAAE,EAAE;QAChD,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,IAAI,EAAE,CAAC;QAC9B,KAAK,MAAM,CAAC,IAAI,SAAS;YAAE,CAAC,EAAE,CAAC;IACjC,CAAC,CAAC;IAEF,0EAA0E;IAC1E,wBAAwB;IACxB,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,UAAU,GAA2B,IAAI,CAAC;IAE9C,SAAS,MAAM,CAAC,OAAmC;QACjD,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;QAC1B,MAAM,SAAS,GAAG,WAAW,EAAE,CAAC;QAChC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,KAAK,EAAE,CAAC;QACpB,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC,CAAC;QACxD,GAAG,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAEnC,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;QAC1C,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;YAC7D,MAAM,OAAO,GAAG,mDAAmD,CAAC;YACpE,GAAG,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;YACxB,IAAI,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC;YACxB,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,SAAS,EAAE,MAAM,IAAI,cAAc,CAAC;QAClE,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,SAAS,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAC/G,MAAM,WAAW,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,YAAY,IAAI,oCAAoC,CAAC,CAAC;QAC/G,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,EAAE,KAAK,MAAM,CAAC;QACpC,MAAM,UAAU,GAAG,OAAO,CAAC,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;QAExE,MAAM,OAAO,GAAG,CAAC,OAA2B,EAAE,EAAE,CAC9C,MAAM;aACH,QAAQ,CAAC;YACR,OAAO;YACP,MAAM,EAAE,WAAW;YACnB,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,EAAE,IAAI,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;YACpD,MAAM;SACP,CAAC;aACD,IAAI,CACH,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;YACV,IAAI,CAAC,OAAO,EAAE;gBAAE,OAAO;YACvB,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YACrB,UAAU,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC;QAC/B,CAAC,EACD,CAAC,GAAG,EAAE,EAAE;YACN,IAAI,CAAC,OAAO,EAAE,IAAI,YAAY,CAAC,GAAG,CAAC;gBAAE,OAAO;YAC5C,MAAM,EAAE,OAAO,EAAE,GAAG,wBAAwB,CAAC,GAAG,CAAC,CAAC;YAClD,GAAG,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;YACxB,UAAU,EAAE,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC;QAClC,CAAC,CACF;aACA,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,IAAI,GAAG,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QAE3D,GAAG,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzB,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,KAAK,OAAO,CAAC,OAAO,CAAC,OAAO,IAAI,YAAY,CAAC,CAAC;YAC9C,OAAO;QACT,CAAC;QACD,oEAAoE;QACpE,oEAAoE;QACpE,KAAK,UAAU,CAAC;YACd,MAAM,EAAE,EAAE;YACV,UAAU,EAAE,MAAM;YAClB,eAAe,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YACvD,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,SAAS,EAAE,MAAM;YACxC,KAAK,EAAE,YAAY;YACnB,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,MAAM;SACP,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;YACjB,IAAI,CAAC,OAAO,EAAE,IAAI,MAAM,CAAC,OAAO;gBAAE,OAAO;YACzC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;YAC5B,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;gBACjC,IAAI,MAAM,CAAC,MAAM,KAAK,cAAc;oBAAE,mBAAmB,CAAC,yCAAyC,CAAC,CAAC;gBACrG,MAAM,CAAC,iBAAiB,EAAE,CAAC,MAAM,CAAC,CAAC;YACrC,CAAC;YACD,MAAM,CAAC,aAAa,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACvC,OAAO,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,yHAAyH;IACzH,SAAS,MAAM;QACb,MAAM,IAAI,CAAC,CAAC;QACZ,UAAU,EAAE,KAAK,EAAE,CAAC;QACpB,UAAU,GAAG,IAAI,CAAC;QAClB,GAAG,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;IAC5B,CAAC;IAED,0GAA0G;IAC1G,SAAS,KAAK;QACZ,MAAM,EAAE,CAAC;QACT,GAAG,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACrC,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK;QACrB,SAAS,CAAC,QAAoB;YAC5B,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACxB,OAAO,GAAG,EAAE,CAAC,KAAK,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC/C,CAAC;QACD,MAAM;QACN,MAAM;QACN,KAAK;KACN,CAAC;AACJ,CAAC;AAID,0GAA0G;AAE1G,MAAM,cAAc,GAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAEhE,MAAM,CAAC,MAAM,gBAAgB,GAA8B,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAE5H,2HAA2H;AAC3H,MAAM,CAAC,MAAM,kBAAkB,GAA8C;IAC3E,KAAK,EAAE,OAAO;IACd,OAAO,EAAE,UAAU;IACnB,QAAQ,EAAE,MAAM;CACjB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAA8B,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AAEpH;;;;;;GAMG;AACH,MAAM,UAAU,4BAA4B,CAC1C,OAAkC,EAClC,QAAsB,EACtB,YAAuC,+BAA+B;IAEtE,MAAM,YAAY,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;IACtC,MAAM,MAAM,GAA4B,EAAE,GAAG,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC;IACxE,IAAI,CAAC,YAAY,IAAI,YAAY,KAAK,QAAQ,CAAC,IAAI;QAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACrE,IAAI,QAAQ,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC9B,IAAI,OAAO,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,+BAA+B,CAAC,KAAK,CAAC;YAAE,OAAO,OAAO,CAAC;QACjG,OAAO,CAAC,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,CAAC;AAClB,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,oBAAoB,CAClC,OAAkC,EAClC,YAAuC,+BAA+B;IAEtE,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IAC/C,OAAO,OAAO,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,+BAA+B,CAAC,KAAK,CAAC,CAAC;AACtF,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAqB;IACxD,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,CAC5B,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACtB,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACvE,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC5B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,IAAI,OAAO,CAAsB,CAAC,OAAO,EAAE,EAAE;YAClD,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAChC,MAAM,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YACtG,MAAM,CAAC,OAAO,GAAG,GAAG,EAAE;gBACpB,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC5B,OAAO,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC,CAAC;YACF,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CACH,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAqB,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,EAAE,WAAW,EAAE,CAAC;AACnF,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,YAAY,CAAC,MAAiC,EAAE,KAAgC;IAC9F,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACxE,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACtE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5D,MAAM,IAAI,GAAG,CAAC,CAA0B,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9F,MAAM,QAAQ,GAAG,CAAC,IAA+B,EAAE,EAAE,CACnD,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC;IAC1G,OAAO,YAAY,QAAQ,CAAC,OAAO,CAAC,SAAS,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC;AAClE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CACxC,IAA2B,EAC3B,YAAuD,EACvD,MAAqB;IAErB,MAAM,OAAO,GAAG,CAAC,OAAsB,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC9F,OAAO,IAAI;QACT,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,gBAAgB,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,EAAE,CAAC;QACrF,CAAC,CAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC7H,CAAC"}
@@ -0,0 +1,59 @@
1
+ import type { GenerationEngine } from "./engine.js";
2
+ import type { DroppedMedia, MediaKind } from "./types.js";
3
+ /**
4
+ * The suggested reasoning model for text and images: prompt enhancement,
5
+ * auto-select routing and image analysis. A default, not a requirement —
6
+ * pass any model id `reasoningEngine` can run.
7
+ */
8
+ export declare const DEFAULT_REASONING_MODEL = "anthropic/claude-opus-5";
9
+ /**
10
+ * Suggested per-kind overrides for media Claude can't read. Gemini takes
11
+ * video and audio directly. For a cheaper option, swap in
12
+ * `"qwen/qwen3.8-omni-flash"`, which takes both too.
13
+ */
14
+ export declare const DEFAULT_REASONING_MODEL_BY_KIND: Partial<Record<MediaKind, string>>;
15
+ /** The reasoning engine and models every CHAI component shares. */
16
+ export interface Reasoning {
17
+ engine: GenerationEngine;
18
+ /** The model for text and any attachment kind without its own entry in `modelByKind`. */
19
+ model: string;
20
+ modelByKind: Partial<Record<MediaKind, string>>;
21
+ }
22
+ /** What a `ChaiProvider` takes, in any framework. */
23
+ export interface ReasoningConfig {
24
+ /** Text, image and routing model. Defaults to `DEFAULT_REASONING_MODEL`. */
25
+ reasoningModel?: string;
26
+ /**
27
+ * Per-attachment-kind models, merged over `DEFAULT_REASONING_MODEL_BY_KIND`
28
+ * one kind at a time, e.g. `{ video: "qwen/qwen3.8-omni-flash" }` keeps
29
+ * the default for audio. Map a kind to `reasoningModel`'s value to use
30
+ * one model for everything.
31
+ */
32
+ reasoningModelByKind?: Partial<Record<MediaKind, string>>;
33
+ /**
34
+ * The engine the reasoning models run on. Defaults to OpenRouter,
35
+ * independent of the generation engine: Fal's own LLM access is an
36
+ * OpenRouter proxy, so defaulting to it would double-proxy.
37
+ */
38
+ reasoningEngine?: GenerationEngine;
39
+ }
40
+ /** A provider's `Reasoning`, from its config and the engine it holds (`config.reasoningEngine`, else one OpenRouter engine it created once). */
41
+ export declare function createReasoning(config: ReasoningConfig, engine: GenerationEngine): Reasoning;
42
+ /** The reasoning engine a provider uses: the one passed, else OpenRouter's. */
43
+ export declare function reasoningEngineFor(config: ReasoningConfig): GenerationEngine;
44
+ /**
45
+ * A hook's or composable's own pair wins, but only as a pair: half of one
46
+ * never mixes with half of the provider's (e.g. a hook engine with the
47
+ * provider's model). Half a pair means no reasoning.
48
+ */
49
+ export declare function pickReasoning(own: {
50
+ reasoningEngine?: GenerationEngine;
51
+ reasoningModel?: string;
52
+ }, provided: Reasoning | undefined): Reasoning | undefined;
53
+ /**
54
+ * The reasoning model for a call with these attachments: the first
55
+ * attachment whose kind has its own model wins (a clip plus a still goes
56
+ * to the model that can read the clip), else `reasoning.model`.
57
+ */
58
+ export declare function reasoningModelFor(reasoning: Reasoning, attachments?: DroppedMedia[]): string;
59
+ //# sourceMappingURL=reasoning.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reasoning.d.ts","sourceRoot":"","sources":["../src/reasoning.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE1D;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,4BAA4B,CAAC;AAEjE;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAG9E,CAAC;AAEF,mEAAmE;AACnE,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,gBAAgB,CAAC;IACzB,yFAAyF;IACzF,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;CACjD;AAED,qDAAqD;AACrD,MAAM,WAAW,eAAe;IAC9B,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;IAC1D;;;;OAIG;IACH,eAAe,CAAC,EAAE,gBAAgB,CAAC;CACpC;AAED,gJAAgJ;AAChJ,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,gBAAgB,GAAG,SAAS,CAQ5F;AAED,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,eAAe,GAAG,gBAAgB,CAE5E;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE;IAAE,eAAe,CAAC,EAAE,gBAAgB,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,EACpE,QAAQ,EAAE,SAAS,GAAG,SAAS,GAC9B,SAAS,GAAG,SAAS,CAIvB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,SAAS,EAAE,WAAW,GAAE,YAAY,EAAO,GAAG,MAAM,CAMhG"}
@@ -0,0 +1,55 @@
1
+ import { createOpenRouterEngine } from "./engines/openrouter.js";
2
+ /**
3
+ * The suggested reasoning model for text and images: prompt enhancement,
4
+ * auto-select routing and image analysis. A default, not a requirement —
5
+ * pass any model id `reasoningEngine` can run.
6
+ */
7
+ export const DEFAULT_REASONING_MODEL = "anthropic/claude-opus-5";
8
+ /**
9
+ * Suggested per-kind overrides for media Claude can't read. Gemini takes
10
+ * video and audio directly. For a cheaper option, swap in
11
+ * `"qwen/qwen3.8-omni-flash"`, which takes both too.
12
+ */
13
+ export const DEFAULT_REASONING_MODEL_BY_KIND = {
14
+ video: "google/gemini-3.8-flash",
15
+ audio: "google/gemini-3.8-flash",
16
+ };
17
+ /** A provider's `Reasoning`, from its config and the engine it holds (`config.reasoningEngine`, else one OpenRouter engine it created once). */
18
+ export function createReasoning(config, engine) {
19
+ const byKind = config.reasoningModelByKind ?? {};
20
+ const overrides = Object.fromEntries(Object.entries(byKind).filter(([, v]) => v !== undefined));
21
+ return {
22
+ engine,
23
+ model: config.reasoningModel ?? DEFAULT_REASONING_MODEL,
24
+ modelByKind: { ...DEFAULT_REASONING_MODEL_BY_KIND, ...overrides },
25
+ };
26
+ }
27
+ /** The reasoning engine a provider uses: the one passed, else OpenRouter's. */
28
+ export function reasoningEngineFor(config) {
29
+ return config.reasoningEngine ?? createOpenRouterEngine();
30
+ }
31
+ /**
32
+ * A hook's or composable's own pair wins, but only as a pair: half of one
33
+ * never mixes with half of the provider's (e.g. a hook engine with the
34
+ * provider's model). Half a pair means no reasoning.
35
+ */
36
+ export function pickReasoning(own, provided) {
37
+ const { reasoningEngine, reasoningModel } = own;
38
+ if (!reasoningEngine && !reasoningModel)
39
+ return provided;
40
+ return reasoningEngine && reasoningModel ? { engine: reasoningEngine, model: reasoningModel, modelByKind: {} } : undefined;
41
+ }
42
+ /**
43
+ * The reasoning model for a call with these attachments: the first
44
+ * attachment whose kind has its own model wins (a clip plus a still goes
45
+ * to the model that can read the clip), else `reasoning.model`.
46
+ */
47
+ export function reasoningModelFor(reasoning, attachments = []) {
48
+ for (const { kind } of attachments) {
49
+ const model = reasoning.modelByKind[kind];
50
+ if (model)
51
+ return model;
52
+ }
53
+ return reasoning.model;
54
+ }
55
+ //# sourceMappingURL=reasoning.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reasoning.js","sourceRoot":"","sources":["../src/reasoning.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAIjE;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,yBAAyB,CAAC;AAEjE;;;;GAIG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAuC;IACjF,KAAK,EAAE,yBAAyB;IAChC,KAAK,EAAE,yBAAyB;CACjC,CAAC;AA6BF,gJAAgJ;AAChJ,MAAM,UAAU,eAAe,CAAC,MAAuB,EAAE,MAAwB;IAC/E,MAAM,MAAM,GAAG,MAAM,CAAC,oBAAoB,IAAI,EAAE,CAAC;IACjD,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC;IAChG,OAAO;QACL,MAAM;QACN,KAAK,EAAE,MAAM,CAAC,cAAc,IAAI,uBAAuB;QACvD,WAAW,EAAE,EAAE,GAAG,+BAA+B,EAAE,GAAG,SAAS,EAAE;KAClE,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,kBAAkB,CAAC,MAAuB;IACxD,OAAO,MAAM,CAAC,eAAe,IAAI,sBAAsB,EAAE,CAAC;AAC5D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAC3B,GAAoE,EACpE,QAA+B;IAE/B,MAAM,EAAE,eAAe,EAAE,cAAc,EAAE,GAAG,GAAG,CAAC;IAChD,IAAI,CAAC,eAAe,IAAI,CAAC,cAAc;QAAE,OAAO,QAAQ,CAAC;IACzD,OAAO,eAAe,IAAI,cAAc,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7H,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,SAAoB,EAAE,cAA8B,EAAE;IACtF,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,WAAW,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;IAC1B,CAAC;IACD,OAAO,SAAS,CAAC,KAAK,CAAC;AACzB,CAAC"}
@@ -47,4 +47,6 @@ export declare function buildRegionEditPrompt({ prompt, regions, format, }: {
47
47
  regions: EditRegion[];
48
48
  format?: RegionFormat;
49
49
  }): string;
50
+ /** The color token for a region number. Numbers past the last color start over at the first. */
51
+ export declare function regionColor(number: number): string;
50
52
  //# sourceMappingURL=region-edit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"region-edit.d.ts","sourceRoot":"","sources":["../src/region-edit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE5D,iHAAiH;AACjH,MAAM,WAAW,SAAS;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,gEAAgE;AAChE,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,kHAAkH;IAClH,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,SAAS,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,kFAAkF;AAClF,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,EAAE,WAAW,EAS5C,CAAC;AAmBF;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,MAAM,EACN,OAAO,EACP,MAAe,GAChB,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB,GAAG,MAAM,CA8BT"}
1
+ {"version":3,"file":"region-edit.d.ts","sourceRoot":"","sources":["../src/region-edit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE5D,iHAAiH;AACjH,MAAM,WAAW,SAAS;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,gEAAgE;AAChE,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,kHAAkH;IAClH,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,SAAS,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,kFAAkF;AAClF,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,EAAE,WAAW,EAS5C,CAAC;AAmBF;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,MAAM,EACN,OAAO,EACP,MAAe,GAChB,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB,GAAG,MAAM,CA8BT;AAKD,gGAAgG;AAChG,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElD"}
@@ -75,4 +75,10 @@ export function buildRegionEditPrompt({ prompt, regions, format = "text", }) {
75
75
  .filter(Boolean)
76
76
  .join(" ");
77
77
  }
78
+ /** How many region colors the tokens define (`--chai-color-semantic-region-1` to `-6`). */
79
+ const REGION_COLOR_COUNT = 6;
80
+ /** The color token for a region number. Numbers past the last color start over at the first. */
81
+ export function regionColor(number) {
82
+ return `var(--chai-color-semantic-region-${((Math.max(1, number) - 1) % REGION_COLOR_COUNT) + 1})`;
83
+ }
78
84
  //# sourceMappingURL=region-edit.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"region-edit.js","sourceRoot":"","sources":["../src/region-edit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAqBH,kFAAkF;AAClF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAkB;IAChD;QACE,EAAE,EAAE,mCAAmC;QACvC,KAAK,EAAE,cAAc;QACrB,QAAQ,EAAE,mBAAmB;QAC7B,KAAK,EAAE,UAAU;QACjB,YAAY,EAAE,cAAc;QAC5B,UAAU,EAAE,mBAAmB;KAChC;CACF,CAAC;AAEF,+FAA+F;AAC/F,MAAM,YAAY,GAAG,aAAa,CAAC;AAEnC,SAAS,QAAQ,CAAC,MAAkB;IAClC,OAAO,UAAU,MAAM,CAAC,MAAM,EAAE,CAAC;AACnC,CAAC;AAED,oGAAoG;AACpG,SAAS,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAa;IACnD,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC5E,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,OAAO,CAAC,CAAS;IACxB,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC;AAC7D,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,qBAAqB,CAAC,EACpC,MAAM,EACN,OAAO,EACP,MAAM,GAAG,MAAM,GAKhB;IACC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IAEzC,IAAI,MAAM,KAAK,cAAc,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtF,MAAM,MAAM,GAAG;YACb,OAAO,YAAY,sCAAsC,OAAO,GAAG;YACnE,OAAO,IAAI,2BAA2B,OAAO,GAAG;YAChD,uDAAuD;SACxD;aACE,MAAM,CAAC,OAAO,CAAC;aACf,IAAI,CAAC,GAAG,CAAC,CAAC;QACb,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YAC7B,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAC7B,OAAO,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACtH,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,MAAM,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;IAC7C,CAAC;IAED,MAAM,OAAO,GAAG,OAAO;SACpB,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,CACJ,UAAU,CAAC,CAAC,MAAM,UAAU,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,cAAc;QAC/F,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,CACzF;SACA,IAAI,CAAC,GAAG,CAAC,CAAC;IACb,OAAO,CAAC,uCAAuC,OAAO,EAAE,EAAE,OAAO,IAAI,2BAA2B,OAAO,GAAG,EAAE,iCAAiC,CAAC;SAC3I,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"region-edit.js","sourceRoot":"","sources":["../src/region-edit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /** How `ResultCard` shows a result's details, the same in every framework binding. */
2
+ import { type Result } from "./run.js";
3
+ import type { ModelOption } from "./types.js";
4
+ export declare function formatDuration(ms: number | undefined): string | undefined;
5
+ export declare function formatCost(usd: number | undefined): string | undefined;
6
+ export declare function formatTokens(total: number | undefined): string | undefined;
7
+ /** A real, exactly-reduced ratio (e.g. 1024×768 → "4:3", 1920×1080 → "16:9") from the media's own measured dimensions — not a guess at the nearest "named" ratio, so an unusual crop just shows its own honest (if less familiar) reduced fraction instead of a misleading rounded one. */
8
+ export declare function formatAspectRatio(width: number, height: number): string | undefined;
9
+ /** The model's label from `models`, falling back to its raw id; "Choosing model…" while auto-select is still picking. */
10
+ export declare function resultModelLabel(result: Result, models?: ModelOption[]): string;
11
+ //# sourceMappingURL=result-format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result-format.d.ts","sourceRoot":"","sources":["../src/result-format.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,OAAO,EAAoB,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAEzE;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAEtE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAE1E;AAMD,2RAA2R;AAC3R,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMnF;AAED,yHAAyH;AACzH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,EAAE,GAAG,MAAM,CAG/E"}
@@ -0,0 +1,30 @@
1
+ /** How `ResultCard` shows a result's details, the same in every framework binding. */
2
+ import { ROUTING_MODEL_ID } from "./run.js";
3
+ export function formatDuration(ms) {
4
+ return ms == null ? undefined : `${(ms / 1000).toFixed(2)}s`;
5
+ }
6
+ export function formatCost(usd) {
7
+ return usd == null ? undefined : `$${usd.toFixed(2)}`;
8
+ }
9
+ export function formatTokens(total) {
10
+ return total == null ? undefined : `${total.toLocaleString()} tokens`;
11
+ }
12
+ function gcd(a, b) {
13
+ return b === 0 ? a : gcd(b, a % b);
14
+ }
15
+ /** A real, exactly-reduced ratio (e.g. 1024×768 → "4:3", 1920×1080 → "16:9") from the media's own measured dimensions — not a guess at the nearest "named" ratio, so an unusual crop just shows its own honest (if less familiar) reduced fraction instead of a misleading rounded one. */
16
+ export function formatAspectRatio(width, height) {
17
+ if (!width || !height)
18
+ return undefined;
19
+ const w = Math.round(width);
20
+ const h = Math.round(height);
21
+ const divisor = gcd(w, h);
22
+ return `${w / divisor}:${h / divisor}`;
23
+ }
24
+ /** The model's label from `models`, falling back to its raw id; "Choosing model…" while auto-select is still picking. */
25
+ export function resultModelLabel(result, models) {
26
+ if (result.modelId === ROUTING_MODEL_ID)
27
+ return "Choosing model…";
28
+ return models?.find((m) => m.id === result.modelId)?.label ?? result.modelId;
29
+ }
30
+ //# sourceMappingURL=result-format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result-format.js","sourceRoot":"","sources":["../src/result-format.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,OAAO,EAAE,gBAAgB,EAAe,MAAM,UAAU,CAAC;AAGzD,MAAM,UAAU,cAAc,CAAC,EAAsB;IACnD,OAAO,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAuB;IAChD,OAAO,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAyB;IACpD,OAAO,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,cAAc,EAAE,SAAS,CAAC;AACxE,CAAC;AAED,SAAS,GAAG,CAAC,CAAS,EAAE,CAAS;IAC/B,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;AACrC,CAAC;AAED,2RAA2R;AAC3R,MAAM,UAAU,iBAAiB,CAAC,KAAa,EAAE,MAAc;IAC7D,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IACxC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC5B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,MAAM,OAAO,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1B,OAAO,GAAG,CAAC,GAAG,OAAO,IAAI,CAAC,GAAG,OAAO,EAAE,CAAC;AACzC,CAAC;AAED,yHAAyH;AACzH,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,MAAsB;IACrE,IAAI,MAAM,CAAC,OAAO,KAAK,gBAAgB;QAAE,OAAO,iBAAiB,CAAC;IAClE,OAAO,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,OAAO,CAAC,EAAE,KAAK,IAAI,MAAM,CAAC,OAAO,CAAC;AAC/E,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** A unique id for an attachment or region. Internal: not exported from the package. */
2
+ export declare function uniqueId(): string;
3
+ //# sourceMappingURL=unique-id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unique-id.d.ts","sourceRoot":"","sources":["../src/unique-id.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,wBAAgB,QAAQ,IAAI,MAAM,CAIjC"}