@chai-ui/core 0.11.4 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/composer-view.d.ts.map +1 -1
  2. package/dist/composer-view.js +4 -2
  3. package/dist/composer-view.js.map +1 -1
  4. package/dist/composer.d.ts +22 -1
  5. package/dist/composer.d.ts.map +1 -1
  6. package/dist/composer.js +64 -13
  7. package/dist/composer.js.map +1 -1
  8. package/dist/engines/fal.d.ts.map +1 -1
  9. package/dist/engines/fal.js +15 -6
  10. package/dist/engines/fal.js.map +1 -1
  11. package/dist/evaluation-judge.d.ts +33 -0
  12. package/dist/evaluation-judge.d.ts.map +1 -0
  13. package/dist/evaluation-judge.js +37 -0
  14. package/dist/evaluation-judge.js.map +1 -0
  15. package/dist/image-fit.d.ts +16 -0
  16. package/dist/image-fit.d.ts.map +1 -0
  17. package/dist/image-fit.js +54 -0
  18. package/dist/image-fit.js.map +1 -0
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +2 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/run.d.ts +2 -0
  24. package/dist/run.d.ts.map +1 -1
  25. package/dist/run.js +4 -0
  26. package/dist/run.js.map +1 -1
  27. package/dist/suggested-models.d.ts +9 -2
  28. package/dist/suggested-models.d.ts.map +1 -1
  29. package/dist/suggested-models.js +42 -13
  30. package/dist/suggested-models.js.map +1 -1
  31. package/dist/types.d.ts +7 -0
  32. package/dist/types.d.ts.map +1 -1
  33. package/package.json +1 -1
  34. package/src/composer-view.ts +4 -2
  35. package/src/composer.ts +97 -20
  36. package/src/engines/fal.ts +15 -6
  37. package/src/evaluation-judge.ts +60 -0
  38. package/src/image-fit.ts +61 -0
  39. package/src/index.ts +2 -0
  40. package/src/run.ts +5 -0
  41. package/src/suggested-models.ts +43 -13
  42. package/src/types.ts +7 -0
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Fitting a video's start image to the picked aspect ratio, so the ratio
3
+ * wins even on image-to-video models that take the image's own shape.
4
+ */
5
+ import type { DroppedMedia } from "./types.js";
6
+
7
+ /** How a start image meets the picked ratio: cropped around its center, or padded with black bars. */
8
+ export type StartImageFit = "crop" | "pad";
9
+
10
+ /**
11
+ * The image redrawn at `ratio` (e.g. "16:9"), as a JPEG data URL. Returns
12
+ * `src` unchanged when it already has that shape, the ratio isn't "w:h",
13
+ * or there's no canvas to draw on (a server, or a test DOM).
14
+ */
15
+ export async function fitImageToRatio(src: string, ratio: string, fit: StartImageFit): Promise<string> {
16
+ const [w, h] = ratio.split(":").map(Number);
17
+ if (!w || !h || typeof document === "undefined") return src;
18
+ const canvas = document.createElement("canvas");
19
+ const ctx = canvas.getContext("2d");
20
+ if (!ctx) return src;
21
+ const img = await loadImage(src).catch(() => null);
22
+ if (!img) return src;
23
+ const iw = img.naturalWidth;
24
+ const ih = img.naturalHeight;
25
+ const target = w / h;
26
+ if (!iw || !ih || Math.abs(iw / ih - target) < 0.01) return src;
27
+ // Crop keeps the image's short side and cuts the long one; pad keeps
28
+ // the whole image and grows the short side.
29
+ const wider = iw / ih > target;
30
+ const keepHeight = fit === "crop" ? wider : !wider;
31
+ canvas.width = keepHeight ? Math.round(ih * target) : iw;
32
+ canvas.height = keepHeight ? ih : Math.round(iw / target);
33
+ if (fit === "pad") {
34
+ ctx.fillStyle = "black";
35
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
36
+ }
37
+ ctx.drawImage(img, (canvas.width - iw) / 2, (canvas.height - ih) / 2);
38
+ try {
39
+ return canvas.toDataURL("image/jpeg", 0.92);
40
+ } catch {
41
+ // A cross-origin image without CORS can't be read back: send it as is.
42
+ return src;
43
+ }
44
+ }
45
+
46
+ /** The attachments with each image fitted to `ratio`; other media unchanged. */
47
+ export function fitImagesToRatio(attachments: DroppedMedia[], ratio: string, fit: StartImageFit): Promise<DroppedMedia[]> {
48
+ return Promise.all(
49
+ attachments.map(async (a) => (a.kind === "image" ? { ...a, src: await fitImageToRatio(a.src, ratio, fit) } : a))
50
+ );
51
+ }
52
+
53
+ function loadImage(src: string): Promise<HTMLImageElement> {
54
+ return new Promise((resolve, reject) => {
55
+ const img = new Image();
56
+ img.crossOrigin = "anonymous";
57
+ img.onload = () => resolve(img);
58
+ img.onerror = reject;
59
+ img.src = src;
60
+ });
61
+ }
package/src/index.ts CHANGED
@@ -18,3 +18,5 @@ export * from "./composer-view.js";
18
18
  export * from "./edit-card.js";
19
19
  export * from "./voice-input.js";
20
20
  export * from "./menu-placement.js";
21
+ export * from "./image-fit.js";
22
+ export * from "./evaluation-judge.js";
package/src/run.ts CHANGED
@@ -112,6 +112,11 @@ export function resolveResult(
112
112
  }));
113
113
  }
114
114
 
115
+ /** The result with `evaluations` added after any it already has (e.g. the judge's after the backend's). */
116
+ export function addEvaluations(run: Run, resultId: string, evaluations: Evaluation[]): Run {
117
+ return updateResult(run, resultId, (r) => ({ ...r, evaluations: [...(r.evaluations ?? []), ...evaluations] }));
118
+ }
119
+
115
120
  export function failResult(run: Run, resultId: string, error: Result["error"]): Run {
116
121
  return updateResult(run, resultId, (r) => ({
117
122
  ...r,
@@ -19,31 +19,48 @@ import { DEFAULT_MAX_REGIONS } from "./region-edit.js";
19
19
 
20
20
  const NANO_BANANA_RATIOS = ["21:9", "16:9", "3:2", "4:3", "5:4", "1:1", "4:5", "3:4", "2:3", "9:16"];
21
21
  const FLUX_3_RATIOS = ["21:9", "2:1", "16:9", "3:2", "7:5", "4:3", "5:4", "1:1", "4:5", "3:4", "5:7", "2:3", "9:16", "1:2"];
22
- /** The ratios a named `image_size` covers (FLUX.1, GPT Image 2). */
22
+ const NANO_BANANA_2_RATIOS = [...NANO_BANANA_RATIOS, "4:1", "1:4", "8:1", "1:8"];
23
+ /** The ratios a named `image_size` covers (Flux 3 Klein, GPT Image 2). */
23
24
  const IMAGE_SIZE_RATIOS = ["1:1", "4:3", "16:9", "3:4", "9:16"];
24
25
 
25
- /** Create image, from a prompt. */
26
+ /**
27
+ * Create image. Each model's `withImages` endpoint takes attached images
28
+ * (as references) and still the picked aspect ratio.
29
+ */
26
30
  export const SUGGESTED_IMAGE_MODELS: ModelOption[] = [
27
- { id: "fal-ai/nano-banana-pro", label: "Nano Banana Pro", provider: "Google", speed: "standard", aspectRatios: NANO_BANANA_RATIOS, falInput: { aspectRatio: "aspect_ratio" } },
31
+ { id: "fal-ai/nano-banana-pro", label: "Nano Banana Pro", provider: "Google", speed: "standard", aspectRatios: NANO_BANANA_RATIOS, falInput: { aspectRatio: "aspect_ratio" }, withImages: "fal-ai/nano-banana-pro/edit" },
28
32
  {
29
33
  id: "fal-ai/nano-banana-2",
30
34
  label: "Nano Banana 2",
31
35
  provider: "Google",
32
36
  speed: "fast",
33
- aspectRatios: [...NANO_BANANA_RATIOS, "4:1", "1:4", "8:1", "1:8"],
37
+ aspectRatios: NANO_BANANA_2_RATIOS,
34
38
  falInput: { aspectRatio: "aspect_ratio" },
39
+ withImages: "fal-ai/nano-banana-2/edit",
40
+ },
41
+ { id: "blackforestlabs/flux-3/text-to-image", label: "Flux 3 Image", provider: "Black Forest Labs", speed: "standard", aspectRatios: FLUX_3_RATIOS, falInput: { aspectRatio: "aspect_ratio" }, withImages: "blackforestlabs/flux-3/edit-image" },
42
+ { id: "openai/gpt-image-2", label: "GPT Image 2", provider: "OpenAI", speed: "slow", aspectRatios: IMAGE_SIZE_RATIOS, falInput: { aspectRatio: "image_size" }, withImages: "openai/gpt-image-2/edit" },
43
+ {
44
+ id: "blackforestlabs/flux-3/klein/text-to-image",
45
+ label: "Flux 3 Klein",
46
+ provider: "Black Forest Labs",
47
+ speed: "fast",
48
+ aspectRatios: IMAGE_SIZE_RATIOS,
49
+ falInput: { aspectRatio: "image_size" },
50
+ withImages: "blackforestlabs/flux-3/klein/edit-image",
35
51
  },
36
- { id: "blackforestlabs/flux-3/text-to-image", label: "Flux 3 Image", provider: "Black Forest Labs", speed: "standard", aspectRatios: FLUX_3_RATIOS, falInput: { aspectRatio: "aspect_ratio" } },
37
- { id: "openai/gpt-image-2", label: "GPT Image 2", provider: "OpenAI", speed: "slow", aspectRatios: IMAGE_SIZE_RATIOS, falInput: { aspectRatio: "image_size" } },
38
- { id: "fal-ai/flux/schnell", label: "FLUX.1 schnell", provider: "Black Forest Labs", speed: "fast", aspectRatios: IMAGE_SIZE_RATIOS, falInput: { aspectRatio: "image_size" } },
39
52
  ];
40
53
 
41
- /** Create video, from a prompt alone. */
54
+ /**
55
+ * Create video. With an image attached, each runs as its `withImages`
56
+ * image-to-video endpoint, the image as the first frame; the image is
57
+ * fitted to the picked ratio first (`startImageFit`).
58
+ */
42
59
  export const SUGGESTED_TEXT_TO_VIDEO_MODELS: ModelOption[] = [
43
- { id: "minimax/h3-max/text-to-video", label: "MiniMax H3 Max", provider: "MiniMax", speed: "slow", aspectRatios: ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"], falInput: { aspectRatio: "aspect_ratio" } },
44
- { id: "bytedance/seedance-2.5/text-to-video", label: "Seedance 2.5", provider: "ByteDance", speed: "slow", aspectRatios: ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"], falInput: { aspectRatio: "aspect_ratio" } },
45
- { id: "fal-ai/kling-video/v3/pro/text-to-video", label: "Kling v3 Pro", provider: "Kling", speed: "slow", aspectRatios: ["16:9", "9:16", "1:1"], falInput: { aspectRatio: "aspect_ratio" } },
46
- { id: "fal-ai/veo3.1/fast", label: "Veo 3.1 Fast", provider: "Google", speed: "standard", aspectRatios: ["16:9", "9:16"], falInput: { aspectRatio: "aspect_ratio" } },
60
+ { id: "minimax/h3-max/text-to-video", label: "MiniMax H3 Max", provider: "MiniMax", speed: "slow", aspectRatios: ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"], falInput: { aspectRatio: "aspect_ratio" }, withImages: "minimax/h3-max/image-to-video" },
61
+ { id: "bytedance/seedance-2.5/text-to-video", label: "Seedance 2.5", provider: "ByteDance", speed: "slow", aspectRatios: ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"], falInput: { aspectRatio: "aspect_ratio" }, withImages: "bytedance/seedance-2.5/image-to-video" },
62
+ { id: "fal-ai/kling-video/v3/pro/text-to-video", label: "Kling v3 Pro", provider: "Kling", speed: "slow", aspectRatios: ["16:9", "9:16", "1:1"], falInput: { aspectRatio: "aspect_ratio" }, withImages: "fal-ai/kling-video/v3/pro/image-to-video" },
63
+ { id: "fal-ai/veo3.1/fast", label: "Veo 3.1 Fast", provider: "Google", speed: "standard", aspectRatios: ["16:9", "9:16"], falInput: { aspectRatio: "aspect_ratio" }, withImages: "fal-ai/veo3.1/fast/image-to-video" },
47
64
  ];
48
65
 
49
66
  /**
@@ -122,7 +139,20 @@ export function suggestedModels(useCase: GenerationUseCase | undefined): ModelOp
122
139
  }
123
140
  }
124
141
 
142
+ /** `withImages` endpoints in no menu of their own. */
143
+ const IMAGE_INPUT_ONLY: ModelOption[] = [
144
+ { id: "fal-ai/nano-banana-2/edit", label: "Nano Banana 2", provider: "Google", speed: "fast", aspectRatios: NANO_BANANA_2_RATIOS, falInput: { aspectRatio: "aspect_ratio", image: "image_urls" } },
145
+ {
146
+ id: "blackforestlabs/flux-3/klein/edit-image",
147
+ label: "Flux 3 Klein",
148
+ provider: "Black Forest Labs",
149
+ speed: "fast",
150
+ aspectRatios: IMAGE_SIZE_RATIOS,
151
+ falInput: { aspectRatio: "image_size", image: "image_urls" },
152
+ },
153
+ ];
154
+
125
155
  /** Every suggested Fal model, by id: how the Fal engine knows each one's inputs. */
126
156
  export const SUGGESTED_FAL_MODELS: ReadonlyMap<string, ModelOption> = new Map(
127
- [...SUGGESTED_IMAGE_MODELS, ...SUGGESTED_TEXT_TO_VIDEO_MODELS, ...SUGGESTED_IMAGE_TO_VIDEO_MODELS, ...SUGGESTED_EDIT_MODELS].map((m) => [m.id, m])
157
+ [...SUGGESTED_IMAGE_MODELS, ...SUGGESTED_TEXT_TO_VIDEO_MODELS, ...SUGGESTED_IMAGE_TO_VIDEO_MODELS, ...SUGGESTED_EDIT_MODELS, ...IMAGE_INPUT_ONLY].map((m) => [m.id, m])
128
158
  );
package/src/types.ts CHANGED
@@ -112,6 +112,13 @@ export interface ModelOption {
112
112
  * without it the Fal engine guesses from the id.
113
113
  */
114
114
  falInput?: FalInputFormat;
115
+ /**
116
+ * The Fal model to call instead when images are attached, for a model
117
+ * whose own endpoint takes a prompt only: e.g. Nano Banana Pro's edit
118
+ * endpoint, or a video model's image-to-video one. The Fal engine reads
119
+ * that model's inputs from the suggested lists or `FalEngineOptions.models`.
120
+ */
121
+ withImages?: string;
115
122
  /**
116
123
  * The output aspect ratios the model takes, e.g. `["1:1", "16:9", "9:16"]`.
117
124
  * Composer's Aspect ratio menu offers only ratios the selected models