@chai-ui/react 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DESIGN.md +6 -1
- package/README.md +79 -17
- package/dist/ChaiProvider.d.ts +3 -42
- package/dist/ChaiProvider.d.ts.map +1 -1
- package/dist/ChaiProvider.js +5 -43
- package/dist/ChaiProvider.js.map +1 -1
- package/dist/Composer.d.ts +10 -60
- package/dist/Composer.d.ts.map +1 -1
- package/dist/Composer.js +26 -24
- package/dist/Composer.js.map +1 -1
- package/dist/EditCard.d.ts +2 -16
- package/dist/EditCard.d.ts.map +1 -1
- package/dist/EditCard.js +7 -2
- package/dist/EditCard.js.map +1 -1
- package/dist/MediaAnalyzer.d.ts +14 -18
- package/dist/MediaAnalyzer.d.ts.map +1 -1
- package/dist/MediaAnalyzer.js +26 -8
- package/dist/MediaAnalyzer.js.map +1 -1
- package/dist/ResultCard.d.ts.map +1 -1
- package/dist/ResultCard.js +10 -6
- package/dist/ResultCard.js.map +1 -1
- package/dist/icons.d.ts +1 -0
- package/dist/icons.d.ts.map +1 -1
- package/dist/icons.js +9 -0
- package/dist/icons.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/media-actions.d.ts +5 -2
- package/dist/media-actions.d.ts.map +1 -1
- package/dist/media-actions.js +12 -3
- package/dist/media-actions.js.map +1 -1
- package/dist/style.css +15 -0
- package/dist/useComposer.d.ts +11 -86
- package/dist/useComposer.d.ts.map +1 -1
- package/dist/useComposer.js +26 -291
- package/dist/useComposer.js.map +1 -1
- package/dist/useMediaAnalyzer.d.ts +8 -28
- package/dist/useMediaAnalyzer.d.ts.map +1 -1
- package/dist/useMediaAnalyzer.js +9 -106
- package/dist/useMediaAnalyzer.js.map +1 -1
- package/package.json +3 -3
- package/src/ChaiProvider.tsx +12 -70
- package/src/Composer.tsx +42 -90
- package/src/EditCard.tsx +18 -20
- package/src/MediaAnalyzer.tsx +51 -30
- package/src/ResultCard.tsx +12 -4
- package/src/icons.tsx +14 -0
- package/src/index.ts +10 -0
- package/src/media-actions.ts +13 -4
- package/src/style.css +15 -0
- package/src/useComposer.ts +33 -411
- package/src/useMediaAnalyzer.ts +15 -146
package/DESIGN.md
CHANGED
|
@@ -50,7 +50,12 @@ use these names: `Results`, `Compare`, `ModelPicker`,
|
|
|
50
50
|
Claude Opus 5 for text and images, Gemini 3.8 Flash for video and
|
|
51
51
|
audio, on OpenRouter. They're suggestions; respect a builder's own
|
|
52
52
|
`reasoningModel`, `reasoningModelByKind` and `reasoningEngine`.
|
|
53
|
-
7. **
|
|
53
|
+
7. **Suggest models, never require them.** With no `models`, Composer and
|
|
54
|
+
MediaAnalyzer use the suggested lists (`suggestedModels`,
|
|
55
|
+
`SUGGESTED_ANALYSIS_MODELS`), each model carrying its verified aspect
|
|
56
|
+
ratios and Fal input format. A builder's own list always wins. Don't
|
|
57
|
+
make a builder write a model list to get a working Model menu.
|
|
58
|
+
8. **Only show what's real.** Show cost only when the engine reports it.
|
|
54
59
|
Don't show model rankings or leaderboard positions you can't keep up to
|
|
55
60
|
date. Don't show a control the selected model can't honor.
|
|
56
61
|
|
package/README.md
CHANGED
|
@@ -80,7 +80,9 @@ capability beyond the default file picker. Every other control is additive:
|
|
|
80
80
|
onAttachMenuSelect={(action) => {
|
|
81
81
|
if (action === "create-image") setUseCase({ kind: "image", label: "Image" });
|
|
82
82
|
}}
|
|
83
|
-
models
|
|
83
|
+
// The Model menu lists Chai's suggested models for the use case, with the
|
|
84
|
+
// first picked, unless you pass your own `models` (see "Suggested models").
|
|
85
|
+
// It shows only with `onModelChange`; without it the first model still runs.
|
|
84
86
|
modelId={modelId}
|
|
85
87
|
onModelChange={setModelId}
|
|
86
88
|
// An "Auto-select model" switch appears at the top of the model menu so
|
|
@@ -89,9 +91,10 @@ capability beyond the default file picker. Every other control is additive:
|
|
|
89
91
|
autoSelectModel={autoSelectModel}
|
|
90
92
|
showAutoSelectToggle
|
|
91
93
|
onAutoSelectModelChange={setAutoSelectModel}
|
|
92
|
-
// The Aspect ratio menu offers what the selected models take
|
|
93
|
-
//
|
|
94
|
-
// them (image-to-video follows
|
|
94
|
+
// The Aspect ratio menu offers what the selected models take. Suggested
|
|
95
|
+
// models carry their `aspectRatios`; give your own models theirs, e.g.
|
|
96
|
+
// ["1:1", "16:9", "9:16"]. A model without them (image-to-video follows
|
|
97
|
+
// the input image) shows no menu.
|
|
95
98
|
aspectRatio={aspectRatio}
|
|
96
99
|
onAspectRatioChange={setAspectRatio}
|
|
97
100
|
// Omit onEnhance entirely to hide the "optimize prompt" button — e.g.
|
|
@@ -113,9 +116,30 @@ for a live example with every opt-in feature.
|
|
|
113
116
|
### No use case picked: a text request
|
|
114
117
|
|
|
115
118
|
A prompt typed with no use case picked is a text request
|
|
116
|
-
(`TEXT_USE_CASE`): it gets a text reply rather than doing nothing.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
+
(`TEXT_USE_CASE`): it gets a text reply rather than doing nothing. That's
|
|
120
|
+
how to use the Composer as a plain chat bar for an LLM:
|
|
121
|
+
|
|
122
|
+
- **The model:** `useComposer` sends it to `textEngine` with `textModel`
|
|
123
|
+
when you set them, otherwise to `ChaiProvider`'s reasoning model
|
|
124
|
+
(default Claude Opus 5 on OpenRouter). Without a `ChaiProvider` or a
|
|
125
|
+
`textEngine`, a mock text engine answers, so wrap your app in
|
|
126
|
+
`<ChaiProvider>` for real replies.
|
|
127
|
+
- **No Model menu:** the suggested lists cover image and video only, so a
|
|
128
|
+
text request shows no Model menu. To offer a choice of text models, pass
|
|
129
|
+
your own `models`.
|
|
130
|
+
- **The reply streams** into the `ResultCard` as it's written, and
|
|
131
|
+
`cancel()` stops it, keeping what has arrived.
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
<ChaiProvider>
|
|
135
|
+
<Composer value={value} onChange={setValue} onSubmit={submit} submitting={busy} onAbort={cancel} />
|
|
136
|
+
{run && <ResultCard results={run.results} prompt={run.request.prompt} onAction={() => {}} />}
|
|
137
|
+
</ChaiProvider>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
For an app that's mostly about images, set the default instead; it's
|
|
141
|
+
never shown as a chip, and it brings the suggested image models and the
|
|
142
|
+
Aspect ratio menu:
|
|
119
143
|
|
|
120
144
|
```tsx
|
|
121
145
|
<Composer defaultUseCase={{ kind: "image", label: "Image" }} … />
|
|
@@ -160,8 +184,8 @@ unlisted reply), the request runs on the first listed model and
|
|
|
160
184
|
## What else this package ships
|
|
161
185
|
|
|
162
186
|
- **`MediaAnalyzer`** — drop in an image, video, or audio clip and get a
|
|
163
|
-
generation-ready prompt back, with kind-aware model menus
|
|
164
|
-
length picker. Wire it with `useMediaAnalyzer()`, which sends the media
|
|
187
|
+
generation-ready prompt back, with kind-aware model menus (suggested
|
|
188
|
+
OpenRouter models unless you pass your own) and a prompt length picker. Wire it with `useMediaAnalyzer()`, which sends the media
|
|
165
189
|
to `ChaiProvider`'s reasoning model and returns the prompt:
|
|
166
190
|
`onSubmit={submit} submitting={analyzing} submitError={error}`.
|
|
167
191
|
- **`ResultCard`** — renders one prompt's generated results (image, video,
|
|
@@ -178,6 +202,44 @@ unlisted reply), the request runs on the first listed model and
|
|
|
178
202
|
The components are built from these; they're also exported directly if
|
|
179
203
|
you're assembling your own layout.
|
|
180
204
|
|
|
205
|
+
## Suggested models
|
|
206
|
+
|
|
207
|
+
You don't have to write model lists. Chai ships suggested lists, checked
|
|
208
|
+
against each provider's own catalog, and the components use them when you
|
|
209
|
+
pass none:
|
|
210
|
+
|
|
211
|
+
| Use case | Export | Provider |
|
|
212
|
+
|---|---|---|
|
|
213
|
+
| Create image | `SUGGESTED_IMAGE_MODELS` | Fal |
|
|
214
|
+
| Create video from a prompt | `SUGGESTED_TEXT_TO_VIDEO_MODELS` | Fal |
|
|
215
|
+
| Create video from an image | `SUGGESTED_IMAGE_TO_VIDEO_MODELS` | Fal |
|
|
216
|
+
| Edit image | `SUGGESTED_EDIT_MODELS` | Fal |
|
|
217
|
+
| Media analysis, per media kind | `SUGGESTED_ANALYSIS_MODELS` | OpenRouter |
|
|
218
|
+
|
|
219
|
+
- **Composer** offers the list for the current use case
|
|
220
|
+
(`suggestedModels(useCase)`), with its first model picked until the person
|
|
221
|
+
picks another. **MediaAnalyzer** offers the analysis lists.
|
|
222
|
+
- **Your own list wins.** Pass `models` (or `modelsByKind`) to replace a
|
|
223
|
+
list, or `[]` for no Model menu. Nothing is required.
|
|
224
|
+
- **Each suggested model knows its inputs**: its aspect ratios, and for Fal
|
|
225
|
+
the field its image goes in (`falInput`), so the Fal engine sends exactly
|
|
226
|
+
what that model expects.
|
|
227
|
+
- **They change only through package releases.** Providers change faster,
|
|
228
|
+
so check them while setting up your engine. It needs no key, runs no
|
|
229
|
+
model and costs nothing:
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
import { checkSuggestedModels } from "@chai-ui/react";
|
|
233
|
+
|
|
234
|
+
const { ok, problems } = await checkSuggestedModels();
|
|
235
|
+
// problems: [{ provider: "fal", modelId, problem: "No longer takes these aspect_ratio values: 21:9." }]
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
It confirms each model still exists and is active, still takes the
|
|
239
|
+
aspect ratios and image field Chai sends (Fal), and still accepts its media
|
|
240
|
+
kind (OpenRouter). If something changed, pick another model or update the
|
|
241
|
+
package once a release catches up.
|
|
242
|
+
|
|
181
243
|
## `useComposer` — wiring `Composer` to a real engine
|
|
182
244
|
|
|
183
245
|
```tsx
|
|
@@ -291,11 +353,12 @@ to `EDIT_IMAGE_USE_CASE`, pass the attached image to `useComposer` as
|
|
|
291
353
|
`editImage`, and drop in the card:
|
|
292
354
|
|
|
293
355
|
```tsx
|
|
294
|
-
import { Composer, EditCard, EDIT_IMAGE_USE_CASE,
|
|
356
|
+
import { Composer, EditCard, EDIT_IMAGE_USE_CASE, SUGGESTED_EDIT_MODELS, useComposer } from "@chai-ui/react";
|
|
295
357
|
|
|
296
358
|
const editImage = useCase?.edit ? (attachments.find((a) => a.kind === "image") ?? null) : null;
|
|
297
359
|
const { run, submit, edit } = useComposer({ engine: createFalEngine({ outputKind: "image" }), editImage });
|
|
298
|
-
|
|
360
|
+
// Unpicked, the first suggested edit model (Flux 3 Image) runs.
|
|
361
|
+
const model = SUGGESTED_EDIT_MODELS.find((m) => m.id === modelId) ?? SUGGESTED_EDIT_MODELS[0];
|
|
299
362
|
|
|
300
363
|
{edit && <EditCard {...edit} maxRegions={model?.maxRegions} />}
|
|
301
364
|
{!edit && run && <ResultCard results={run.results} prompt={run.request.prompt} onAction={…} />}
|
|
@@ -305,7 +368,6 @@ const model = PRECISE_EDIT_MODELS.find((m) => m.id === modelId);
|
|
|
305
368
|
onAttachMenuSelect={(action) => {
|
|
306
369
|
if (action === "edit-media") setUseCase(EDIT_IMAGE_USE_CASE);
|
|
307
370
|
}}
|
|
308
|
-
models={useCase?.edit ? PRECISE_EDIT_MODELS : models}
|
|
309
371
|
regions={edit?.regions}
|
|
310
372
|
onRegionsChange={edit?.onRegionsChange}
|
|
311
373
|
onSubmit={submit}
|
|
@@ -319,11 +381,11 @@ const model = PRECISE_EDIT_MODELS.find((m) => m.id === modelId);
|
|
|
319
381
|
- **Submitting.** An edit needs the image plus a whole-image prompt, a
|
|
320
382
|
region, or both. Regions carry their own instructions, so the prompt can
|
|
321
383
|
stay empty.
|
|
322
|
-
- **Models.**
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
`editPrompt`.
|
|
384
|
+
- **Models.** In edit mode Composer offers `SUGGESTED_EDIT_MODELS`: Flux 3
|
|
385
|
+
Image first, which reads each region as a box, then Nano Banana Pro and
|
|
386
|
+
GPT Image 2. Offer any edit model you like; one without a `regionFormat`
|
|
387
|
+
gets its regions described in words, which is less precise. Change how
|
|
388
|
+
the prompt is built with `useComposer`'s `editPrompt`.
|
|
327
389
|
- **Versions.** Each edit's result is a new version, shown in the card with
|
|
328
390
|
dots to page back. The next edit applies to the version showing.
|
|
329
391
|
- **Images only** for now.
|
package/dist/ChaiProvider.d.ts
CHANGED
|
@@ -1,49 +1,10 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import { type
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
export interface ChaiProviderProps {
|
|
23
|
-
/** Text, image and routing model. Defaults to `DEFAULT_REASONING_MODEL`. */
|
|
24
|
-
reasoningModel?: string;
|
|
25
|
-
/**
|
|
26
|
-
* Per-attachment-kind models, merged over `DEFAULT_REASONING_MODEL_BY_KIND`
|
|
27
|
-
* one kind at a time, e.g. `{ video: "qwen/qwen3.8-omni-flash" }` keeps
|
|
28
|
-
* the default for audio. Map a kind to `reasoningModel`'s value to use
|
|
29
|
-
* one model for everything.
|
|
30
|
-
*/
|
|
31
|
-
reasoningModelByKind?: Partial<Record<MediaKind, string>>;
|
|
32
|
-
/**
|
|
33
|
-
* The engine the reasoning models run on. Defaults to OpenRouter,
|
|
34
|
-
* independent of the generation engine: Fal's own LLM access is an
|
|
35
|
-
* OpenRouter proxy, so defaulting to it would double-proxy.
|
|
36
|
-
*/
|
|
37
|
-
reasoningEngine?: GenerationEngine;
|
|
2
|
+
import { type Reasoning, type ReasoningConfig } from "@chai-ui/core";
|
|
3
|
+
export { DEFAULT_REASONING_MODEL, DEFAULT_REASONING_MODEL_BY_KIND, reasoningModelFor, type Reasoning, } from "@chai-ui/core";
|
|
4
|
+
export interface ChaiProviderProps extends ReasoningConfig {
|
|
38
5
|
children?: ReactNode;
|
|
39
6
|
}
|
|
40
7
|
export declare function ChaiProvider({ reasoningModel, reasoningModelByKind, reasoningEngine, children }: ChaiProviderProps): import("react").JSX.Element;
|
|
41
8
|
/** The nearest `ChaiProvider`'s reasoning engine and models, or `undefined` outside one. */
|
|
42
9
|
export declare function useReasoning(): Reasoning | undefined;
|
|
43
|
-
/**
|
|
44
|
-
* The reasoning model for a call with these attachments: the first
|
|
45
|
-
* attachment whose kind has its own model wins (a clip plus a still goes
|
|
46
|
-
* to the model that can read the clip), else `reasoning.model`.
|
|
47
|
-
*/
|
|
48
|
-
export declare function reasoningModelFor(reasoning: Reasoning, attachments?: DroppedMedia[]): string;
|
|
49
10
|
//# sourceMappingURL=ChaiProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChaiProvider.d.ts","sourceRoot":"","sources":["../src/ChaiProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC3E,OAAO,
|
|
1
|
+
{"version":3,"file":"ChaiProvider.d.ts","sourceRoot":"","sources":["../src/ChaiProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC3E,OAAO,EAAuC,KAAK,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAE1G,OAAO,EACL,uBAAuB,EACvB,+BAA+B,EAC/B,iBAAiB,EACjB,KAAK,SAAS,GACf,MAAM,eAAe,CAAC;AAIvB,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,wBAAgB,YAAY,CAAC,EAAE,cAAc,EAAE,oBAAoB,EAAE,eAAe,EAAE,QAAQ,EAAE,EAAE,iBAAiB,+BAQlH;AAED,4FAA4F;AAC5F,wBAAgB,YAAY,IAAI,SAAS,GAAG,SAAS,CAEpD"}
|
package/dist/ChaiProvider.js
CHANGED
|
@@ -1,54 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { createContext, useContext, useMemo } from "react";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
* The suggested reasoning model for text and images: prompt enhancement,
|
|
6
|
-
* auto-select routing and image analysis. A default, not a requirement —
|
|
7
|
-
* pass any model id `reasoningEngine` can run.
|
|
8
|
-
*/
|
|
9
|
-
export const DEFAULT_REASONING_MODEL = "anthropic/claude-opus-5";
|
|
10
|
-
/**
|
|
11
|
-
* Suggested per-kind overrides for media Claude can't read. Gemini takes
|
|
12
|
-
* video and audio directly. For a cheaper option, swap in
|
|
13
|
-
* `"qwen/qwen3.8-omni-flash"`, which takes both too.
|
|
14
|
-
*/
|
|
15
|
-
export const DEFAULT_REASONING_MODEL_BY_KIND = {
|
|
16
|
-
video: "google/gemini-3.8-flash",
|
|
17
|
-
audio: "google/gemini-3.8-flash",
|
|
18
|
-
};
|
|
3
|
+
import { createReasoning, reasoningEngineFor } from "@chai-ui/core";
|
|
4
|
+
export { DEFAULT_REASONING_MODEL, DEFAULT_REASONING_MODEL_BY_KIND, reasoningModelFor, } from "@chai-ui/core";
|
|
19
5
|
const ReasoningContext = createContext(undefined);
|
|
20
6
|
export function ChaiProvider({ reasoningModel, reasoningModelByKind, reasoningEngine, children }) {
|
|
21
|
-
const engine = useMemo(() => reasoningEngine
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const audio = reasoningModelByKind?.audio;
|
|
25
|
-
const image = reasoningModelByKind?.image;
|
|
26
|
-
const text = reasoningModelByKind?.text;
|
|
27
|
-
const value = useMemo(() => ({
|
|
28
|
-
engine,
|
|
29
|
-
model,
|
|
30
|
-
modelByKind: { ...DEFAULT_REASONING_MODEL_BY_KIND, ...dropUndefined({ video, audio, image, text }) },
|
|
31
|
-
}), [engine, model, video, audio, image, text]);
|
|
7
|
+
const engine = useMemo(() => reasoningEngineFor({ reasoningEngine }), [reasoningEngine]);
|
|
8
|
+
const { video, audio, image, text } = reasoningModelByKind ?? {};
|
|
9
|
+
const value = useMemo(() => createReasoning({ reasoningModel, reasoningModelByKind: { video, audio, image, text } }, engine), [engine, reasoningModel, video, audio, image, text]);
|
|
32
10
|
return _jsx(ReasoningContext.Provider, { value: value, children: children });
|
|
33
11
|
}
|
|
34
|
-
function dropUndefined(obj) {
|
|
35
|
-
return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined));
|
|
36
|
-
}
|
|
37
12
|
/** The nearest `ChaiProvider`'s reasoning engine and models, or `undefined` outside one. */
|
|
38
13
|
export function useReasoning() {
|
|
39
14
|
return useContext(ReasoningContext);
|
|
40
15
|
}
|
|
41
|
-
/**
|
|
42
|
-
* The reasoning model for a call with these attachments: the first
|
|
43
|
-
* attachment whose kind has its own model wins (a clip plus a still goes
|
|
44
|
-
* to the model that can read the clip), else `reasoning.model`.
|
|
45
|
-
*/
|
|
46
|
-
export function reasoningModelFor(reasoning, attachments = []) {
|
|
47
|
-
for (const { kind } of attachments) {
|
|
48
|
-
const model = reasoning.modelByKind[kind];
|
|
49
|
-
if (model)
|
|
50
|
-
return model;
|
|
51
|
-
}
|
|
52
|
-
return reasoning.model;
|
|
53
|
-
}
|
|
54
16
|
//# sourceMappingURL=ChaiProvider.js.map
|
package/dist/ChaiProvider.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChaiProvider.js","sourceRoot":"","sources":["../src/ChaiProvider.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAkB,MAAM,OAAO,CAAC;AAC3E,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"ChaiProvider.js","sourceRoot":"","sources":["../src/ChaiProvider.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAkB,MAAM,OAAO,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAwC,MAAM,eAAe,CAAC;AAE1G,OAAO,EACL,uBAAuB,EACvB,+BAA+B,EAC/B,iBAAiB,GAElB,MAAM,eAAe,CAAC;AAEvB,MAAM,gBAAgB,GAAG,aAAa,CAAwB,SAAS,CAAC,CAAC;AAMzE,MAAM,UAAU,YAAY,CAAC,EAAE,cAAc,EAAE,oBAAoB,EAAE,eAAe,EAAE,QAAQ,EAAqB;IACjH,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,kBAAkB,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;IACzF,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,oBAAoB,IAAI,EAAE,CAAC;IACjE,MAAM,KAAK,GAAG,OAAO,CACnB,GAAG,EAAE,CAAC,eAAe,CAAC,EAAE,cAAc,EAAE,oBAAoB,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,CAAC,EACtG,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CACpD,CAAC;IACF,OAAO,KAAC,gBAAgB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAA6B,CAAC;AACzF,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,YAAY;IAC1B,OAAO,UAAU,CAAC,gBAAgB,CAAC,CAAC;AACtC,CAAC"}
|
package/dist/Composer.d.ts
CHANGED
|
@@ -1,63 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
}
|
|
2
|
+
import type { EditRegion, ModelOption, ParameterOption, MediaKind } from "@chai-ui/core";
|
|
3
|
+
import type { ComposerAttachment, ComposerSubmitPayload, ComposerUseCase } from "@chai-ui/core";
|
|
4
|
+
export { EDIT_IMAGE_USE_CASE, TEXT_USE_CASE, type ComposerAttachment, type ComposerSelection, type ComposerSubmitPayload, type ComposerUseCase, } from "@chai-ui/core";
|
|
6
5
|
export type ComposerAttachMenuAction = "add-media" | "create-image" | "create-video" | "edit-media";
|
|
7
|
-
/**
|
|
8
|
-
* The selected generation use case, shown as a removable chip (reference:
|
|
9
|
-
* the mock's "Video"/"Image" chip) — deliberately independent of
|
|
10
|
-
* `attachments`: a use case can be picked from the attach menu with nothing
|
|
11
|
-
* attached yet (e.g. "Create video"). Composer never derives this from
|
|
12
|
-
* `attachments` itself (that's app-level policy, not a Composer opinion —
|
|
13
|
-
* the design). With none picked, `defaultUseCase` applies.
|
|
14
|
-
*/
|
|
15
|
-
export interface ComposerUseCase {
|
|
16
|
-
kind: MediaKind;
|
|
17
|
-
label: string;
|
|
18
|
-
/**
|
|
19
|
-
* An edit of the attached image rather than a new one: no aspect ratio,
|
|
20
|
-
* region chips when `regions` is set, and a submit needs only an image and
|
|
21
|
-
* either a prompt or a region. Images only for now.
|
|
22
|
-
*/
|
|
23
|
-
edit?: boolean;
|
|
24
|
-
}
|
|
25
|
-
/** A plain text request — what a submit means when no use case is picked, unless the builder sets `defaultUseCase`. */
|
|
26
|
-
export declare const TEXT_USE_CASE: ComposerUseCase;
|
|
27
|
-
/** Editing the attached image, the attach menu's "Edit image". Pair it with `useComposer`'s `editImage` and an `EditCard`. */
|
|
28
|
-
export declare const EDIT_IMAGE_USE_CASE: ComposerUseCase;
|
|
29
|
-
/** One use case to run and the models picked for it. Empty `modelIds` means the engine's default model. */
|
|
30
|
-
export interface ComposerSelection {
|
|
31
|
-
useCase: ComposerUseCase;
|
|
32
|
-
modelIds: string[];
|
|
33
|
-
/** The models offered for this use case: what auto-select routes among. */
|
|
34
|
-
models: ModelOption[];
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Everything Composer itself tracks, assembled into one object at submit
|
|
38
|
-
* time — not the the design `Request` shape (no `mode`/`params`/
|
|
39
|
-
* `presetId`; those belong to the run store this package doesn't have yet,
|
|
40
|
-
* see the package README), just an honest snapshot of this component's own
|
|
41
|
-
* state. Handed to `onSubmit` so a builder doesn't have to re-derive it
|
|
42
|
-
* from five separately-controlled props they already hold — mainly a
|
|
43
|
-
* correctness/ergonomics win for a coding agent wiring this up from the
|
|
44
|
-
* type signature alone.
|
|
45
|
-
*/
|
|
46
|
-
export interface ComposerSubmitPayload {
|
|
47
|
-
value: string;
|
|
48
|
-
attachments: ComposerAttachment[];
|
|
49
|
-
/** The use case to run — the picked one, or `defaultUseCase` when none is picked. The first one with `multiSelectUseCases`. */
|
|
50
|
-
useCase: ComposerUseCase;
|
|
51
|
-
/** The first picked model, or null for the engine's default. */
|
|
52
|
-
modelId: string | null;
|
|
53
|
-
aspectRatio: string | null;
|
|
54
|
-
/** Everything to run: one entry per use case with its picked models. A single entry unless `multiSelectUseCases` is on. */
|
|
55
|
-
selections: ComposerSelection[];
|
|
56
|
-
/** When true, the model is chosen at submit (`useComposer` routes among each selection's `models`), not by the picks. */
|
|
57
|
-
autoSelectModel: boolean;
|
|
58
|
-
/** An edit's marked regions, each with its own instruction. Empty unless the use case is an edit. */
|
|
59
|
-
regions?: EditRegion[];
|
|
60
|
-
}
|
|
61
6
|
export interface ComposerProps {
|
|
62
7
|
/** The prompt text. Controlled — Composer owns no text state of its own. */
|
|
63
8
|
value: string;
|
|
@@ -83,7 +28,7 @@ export interface ComposerProps {
|
|
|
83
28
|
multiSelectUseCases?: boolean;
|
|
84
29
|
useCases?: ComposerUseCase[];
|
|
85
30
|
onUseCasesChange?: (next: ComposerUseCase[]) => void;
|
|
86
|
-
/** Models per use-case kind, for `multiSelectUseCases`. */
|
|
31
|
+
/** Models per use-case kind, for `multiSelectUseCases`. A kind left out gets Chai's suggested models (see `models`). */
|
|
87
32
|
modelsByKind?: Partial<Record<MediaKind, ModelOption[]>>;
|
|
88
33
|
/**
|
|
89
34
|
* Builder opt-in, off by default: the end user can pick several models,
|
|
@@ -94,7 +39,12 @@ export interface ComposerProps {
|
|
|
94
39
|
multiSelectModels?: boolean;
|
|
95
40
|
modelIds?: string[];
|
|
96
41
|
onModelIdsChange?: (ids: string[]) => void;
|
|
97
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* Models for the current use case. Leave it out and Composer offers Chai's
|
|
44
|
+
* suggested Fal models for the use case (`suggestedModels`), with the
|
|
45
|
+
* first one picked until the person picks another. Pass a list to use
|
|
46
|
+
* your own; pass `[]` for no Model menu.
|
|
47
|
+
*/
|
|
98
48
|
models?: ModelOption[];
|
|
99
49
|
modelId?: string | null;
|
|
100
50
|
/** `null` clears the pick: Composer does this when auto-select turns on, until routing picks a model. */
|
package/dist/Composer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Composer.d.ts","sourceRoot":"","sources":["../src/Composer.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Composer.d.ts","sourceRoot":"","sources":["../src/Composer.tsx"],"names":[],"mappings":"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"}
|
package/dist/Composer.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { StopIcon } from "./icons.js";
|
|
2
3
|
import { useEffect, useRef, useState } from "react";
|
|
3
4
|
import { createComponent } from "@lit/react";
|
|
4
5
|
import * as React from "react";
|
|
5
6
|
import { MdFab } from "@material/web/fab/fab.js";
|
|
7
|
+
import { deriveGenerationUseCase, suggestedModels, TEXT_USE_CASE } from "@chai-ui/core";
|
|
6
8
|
import { SearchMenu } from "./primitives/SearchMenu.js";
|
|
7
9
|
import { regionColor } from "./regions.js";
|
|
10
|
+
export { EDIT_IMAGE_USE_CASE, TEXT_USE_CASE, } from "@chai-ui/core";
|
|
8
11
|
/** See primitives/Toggle.tsx for why `createComponent` is used instead of raw JSX on custom-element tags. */
|
|
9
12
|
const MdFabElement = createComponent({
|
|
10
13
|
react: React,
|
|
@@ -37,10 +40,6 @@ function nextAttachmentId() {
|
|
|
37
40
|
? crypto.randomUUID()
|
|
38
41
|
: `attachment-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
39
42
|
}
|
|
40
|
-
/** A plain text request — what a submit means when no use case is picked, unless the builder sets `defaultUseCase`. */
|
|
41
|
-
export const TEXT_USE_CASE = { kind: "text", label: "Text" };
|
|
42
|
-
/** Editing the attached image, the attach menu's "Edit image". Pair it with `useComposer`'s `editImage` and an `EditCard`. */
|
|
43
|
-
export const EDIT_IMAGE_USE_CASE = { kind: "image", label: "Edit image", edit: true };
|
|
44
43
|
/**
|
|
45
44
|
* The `bar`-layout Composer —
|
|
46
45
|
* reference: the "Compose Component" design, whose four states (minimal / model+aspect select / auto-select with
|
|
@@ -77,18 +76,30 @@ export function Composer({ value, onChange, placeholder, promptLabel, attachment
|
|
|
77
76
|
const showStop = submitting && Boolean(onAbort);
|
|
78
77
|
const multiModel = multiSelectModels || multiSelectUseCases;
|
|
79
78
|
const rawPickedIds = multiModel ? modelIds : modelId ? [modelId] : [];
|
|
79
|
+
// The app's list for a use case, or Chai's suggested models when it gave none.
|
|
80
|
+
const rosterFor = (u) => {
|
|
81
|
+
const given = multiSelectUseCases ? modelsByKind?.[u.kind] : models;
|
|
82
|
+
if (given)
|
|
83
|
+
return { options: given, suggested: false };
|
|
84
|
+
return { options: suggestedModels(u.edit ? "image-edit" : deriveGenerationUseCase(u, attachments)), suggested: true };
|
|
85
|
+
};
|
|
80
86
|
// One menu section per use case with multi-use-case select; otherwise one flat roster.
|
|
81
|
-
const modelSections = multiSelectUseCases
|
|
82
|
-
?
|
|
83
|
-
|
|
84
|
-
.filter((s) => s.options.length > 0)
|
|
85
|
-
: [{ label: "", kind: activeUseCases[0].kind, options: models ?? [] }].filter((s) => s.options.length > 0);
|
|
87
|
+
const modelSections = (multiSelectUseCases ? activeUseCases : activeUseCases.slice(0, 1))
|
|
88
|
+
.map((u) => ({ label: multiSelectUseCases ? u.label : "", kind: u.kind, ...rosterFor(u) }))
|
|
89
|
+
.filter((s) => s.options.length > 0);
|
|
86
90
|
const allModels = modelSections.flatMap((s) => s.options);
|
|
87
91
|
// A pick that isn't in the current list (e.g. the edit model after leaving
|
|
88
92
|
// edit mode) is shown as unpicked and never submitted. With no list at
|
|
89
|
-
// all, the app's pick is passed through as given.
|
|
90
|
-
|
|
93
|
+
// all, the app's pick is passed through as given. With nothing picked
|
|
94
|
+
// from a suggested list (and auto-select off), its first model is the pick.
|
|
95
|
+
const validPicks = allModels.length > 0 ? rawPickedIds.filter((id) => allModels.some((m) => m.id === id)) : rawPickedIds;
|
|
96
|
+
const pickedModelIds = validPicks.length > 0 || autoSelectModel
|
|
97
|
+
? validPicks
|
|
98
|
+
: modelSections.filter((s) => s.suggested).map((s) => s.options[0].id);
|
|
91
99
|
const pickedModels = allModels.filter((m) => pickedModelIds.includes(m.id));
|
|
100
|
+
// No menu that can't change anything: without its handler, the Model menu
|
|
101
|
+
// hides (the default pick still runs).
|
|
102
|
+
const canPickModel = multiModel ? Boolean(onModelIdsChange) : Boolean(onModelChange);
|
|
92
103
|
const modelTriggerLabel = pickedModels.length > 1
|
|
93
104
|
? `${pickedModels.length} models`
|
|
94
105
|
: pickedModels[0]?.label ?? (autoSelectModel ? "Auto-select" : "Select models");
|
|
@@ -113,9 +124,9 @@ export function Composer({ value, onChange, placeholder, promptLabel, attachment
|
|
|
113
124
|
function buildSelections() {
|
|
114
125
|
return activeUseCases.map((u) => ({
|
|
115
126
|
useCase: u,
|
|
116
|
-
models:
|
|
127
|
+
models: rosterFor(u).options,
|
|
117
128
|
modelIds: multiSelectUseCases
|
|
118
|
-
? pickedModelIds.filter((id) =>
|
|
129
|
+
? pickedModelIds.filter((id) => rosterFor(u).options.some((m) => m.id === id))
|
|
119
130
|
: pickedModelIds,
|
|
120
131
|
}));
|
|
121
132
|
}
|
|
@@ -254,7 +265,7 @@ export function Composer({ value, onChange, placeholder, promptLabel, attachment
|
|
|
254
265
|
}
|
|
255
266
|
return (_jsxs("div", { className: "chai-composer", children: [attachments.length > 0 && (_jsx("div", { className: "chai-composer__thumbnails", children: attachments.map((a) => (_jsx(ComposerThumbnail, { attachment: a, onRemove: onAttachmentsChange ? () => handleRemoveAttachment(a.id) : undefined }, a.id))) })), editing && regions.length > 0 && (_jsx("ul", { className: "chai-composer__regions", "aria-label": "Edit regions", children: regions.map((r) => (_jsx("li", { children: _jsx(RegionChip, { region: r, onRemove: onRegionsChange ? () => onRegionsChange(regions.filter((x) => x.id !== r.id)) : undefined, disabled: disabled }) }, r.id))) })), _jsxs("div", { className: "chai-composer__field", children: [_jsx("textarea", { ref: textareaRef, className: `chai-composer__input chai-composer__type${animating ? " chai-composer__input--revealing" : ""}`, value: value, onChange: (e) => onChange(e.target.value), placeholder: effectivePlaceholder, "aria-label": promptLabel ?? effectivePlaceholder, rows: 1, disabled: disabled || animating }), animating && (_jsx("div", { className: "chai-composer__reveal chai-composer__type", "aria-hidden": "true", children: _jsx("span", { className: "chai-composer__reveal-text", children: animatingText.slice(0, revealedLength) }) })), onEnhance && (_jsx("button", { type: "button", className: `chai-composer__optimize${enhanceBusy ? " chai-composer__optimize--active" : ""}`, onClick: handleOptimizeClick, disabled: disabled || (isEnhanced ? false : !value.trim() || enhanceBusy), "aria-label": isEnhanced ? "Revert to original prompt" : "Optimize prompt", title: isEnhanced ? "Revert to original prompt" : "Optimize prompt", children: isEnhanced ? _jsx(UndoIcon, {}) : _jsx(OptimizeIcon, {}) }))] }), submitError && (_jsxs("p", { className: "chai-composer__error", role: "alert", children: [_jsx(ErrorIcon, {}), submitError] })), _jsxs("div", { className: "chai-composer__controls", children: [_jsxs("div", { className: "chai-composer__controls-start", children: [menuItems.length > 0 && (_jsx(ComposerAttachMenu, { items: menuItems, onSelect: onAttachMenuSelect, onAddMediaFiles: handleFilesSelected, imagesOnly: editing, disabled: disabled })), pickedUseCases.map((u) => (_jsx(UseCaseChip, { useCase: u, onRemove: multiSelectUseCases
|
|
256
267
|
? onUseCasesChange && (() => onUseCasesChange(useCases.filter((x) => x.kind !== u.kind)))
|
|
257
|
-
: onClearUseCase, disabled: disabled }, u.kind))), modelSections.length > 0 && (_jsx(SearchMenu, { ...(multiSelectUseCases
|
|
268
|
+
: onClearUseCase, disabled: disabled }, u.kind))), modelSections.length > 0 && canPickModel && (_jsx(SearchMenu, { ...(multiSelectUseCases
|
|
258
269
|
? { sections: modelSections.map((s) => ({ label: s.label, options: s.options })) }
|
|
259
270
|
: { options: modelSections[0].options }), value: multiModel ? pickedModelIds : modelId, multiple: multiModel, toggleHeader: showAutoSelectToggle
|
|
260
271
|
? {
|
|
@@ -262,7 +273,7 @@ export function Composer({ value, onChange, placeholder, promptLabel, attachment
|
|
|
262
273
|
value: autoSelectModel,
|
|
263
274
|
onChange: (v) => onAutoSelectModelChange?.(v),
|
|
264
275
|
}
|
|
265
|
-
: undefined, triggerLabel: modelTriggerLabel, menuLabel: multiModel ? "Select models" : "Select model", onSelect: (opt) => toggleModel(opt.id), searchable: false, triggerVariant: "text", disabled: disabled || (autoSelectModel && !showAutoSelectToggle), panelWidth: showAutoSelectToggle ? 320 : undefined })), showAspectRatio && aspectOptions.length > 0 && (_jsx(SearchMenu, { options: aspectOptions.map((o) => ({ id: o.value, label: o.label })), value: offeredAspect, triggerLabel: selectedAspect?.label ?? "Aspect ratio", menuLabel: "Aspect ratio", onSelect: (opt) => onAspectRatioChange?.(opt.id), searchable: false, triggerVariant: "text", disabled: disabled }))] }), _jsx(MdFabElement, { className: "chai-composer__submit", size: "small", variant: "secondary", label: "", "data-ready": showStop || !isSubmitDisabled || undefined, "data-error": Boolean(submitError) || undefined, "data-stop": showStop || undefined, ...{
|
|
276
|
+
: undefined, triggerLabel: modelTriggerLabel, menuLabel: multiModel ? "Select models" : "Select model", onSelect: (opt) => toggleModel(opt.id), searchable: false, triggerVariant: "text", disabled: disabled || (autoSelectModel && !showAutoSelectToggle), panelWidth: showAutoSelectToggle ? 320 : undefined })), showAspectRatio && aspectOptions.length > 0 && onAspectRatioChange && (_jsx(SearchMenu, { options: aspectOptions.map((o) => ({ id: o.value, label: o.label })), value: offeredAspect, triggerLabel: selectedAspect?.label ?? "Aspect ratio", menuLabel: "Aspect ratio", onSelect: (opt) => onAspectRatioChange?.(opt.id), searchable: false, triggerVariant: "text", disabled: disabled }))] }), _jsx(MdFabElement, { className: "chai-composer__submit", size: "small", variant: "secondary", label: "", "data-ready": showStop || !isSubmitDisabled || undefined, "data-error": Boolean(submitError) || undefined, "data-stop": showStop || undefined, ...{
|
|
266
277
|
ariaLabel: showStop ? "Stop" : submitError ? "Retry" : "Submit",
|
|
267
278
|
ariaDisabled: showStop ? "false" : submitting ? "true" : isSubmitDisabled ? "true" : "false",
|
|
268
279
|
}, onClick: () => {
|
|
@@ -418,15 +429,6 @@ function OptimizeIcon() {
|
|
|
418
429
|
function UndoIcon() {
|
|
419
430
|
return (_jsx("svg", { viewBox: "0 -960 960 960", "aria-hidden": "true", children: _jsx("path", { d: "M259-200v-60h310q70 0 120.5-46.5T740-422q0-69-50.5-115.5T569-584H274l114 114-42 42-186-186 186-186 42 42-114 114h294q95 0 163.5 64T800-422q0 94-68.5 158T568-200H259Z", fill: "currentColor" }) }));
|
|
420
431
|
}
|
|
421
|
-
// Real Material Symbols "stop" glyph, the filled variant specifically
|
|
422
|
-
// (`stop-fill`, not the base `stop` — that one traces a hollow square-
|
|
423
|
-
// outline shape even filled with `currentColor`, which read as a thin
|
|
424
|
-
// frame rather than a real stop icon at 24px; design review caught
|
|
425
|
-
// this live). Viewbox `0 -960 960 960` — the submit FAB's in-flight state,
|
|
426
|
-
// its own doc comment above where it's rendered.
|
|
427
|
-
function StopIcon() {
|
|
428
|
-
return (_jsx("svg", { viewBox: "0 -960 960 960", "aria-hidden": "true", children: _jsx("path", { d: "M240-240v-480h480v480H240Z", fill: "currentColor" }) }));
|
|
429
|
-
}
|
|
430
432
|
function AttachFileIcon() {
|
|
431
433
|
return (_jsx("svg", { viewBox: "0 -960 960 960", "aria-hidden": "true", children: _jsx("path", { d: "M728-326q0 103-72.18 174.5-72.17 71.5-175 71.5Q378-80 305.5-151.5T233-326v-380q0-72.5 51.5-123.25T408-880q72 0 123.5 50.75T583-706v360q0 42-30 72t-72.5 30q-42.5 0-72.5-29.67-30-29.68-30-72.33v-370h60v370q0 17 12.5 29.5t30.64 12.5q18.14 0 30-12.5T523-346v-360q0-48-33.5-81t-81.71-33q-48.21 0-81.5 33.06T293-706v380q0 78 54.97 132T481-140q77.92 0 132.46-54Q668-248 668-326v-390h60v390Z", fill: "currentColor" }) }));
|
|
432
434
|
}
|