@chai-ui/core 0.6.1 → 0.7.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/README.md +3 -2
- package/dist/composer-view.d.ts +100 -0
- package/dist/composer-view.d.ts.map +1 -0
- package/dist/composer-view.js +155 -0
- package/dist/composer-view.js.map +1 -0
- package/dist/edit-card.d.ts +53 -0
- package/dist/edit-card.d.ts.map +1 -0
- package/dist/edit-card.js +119 -0
- package/dist/edit-card.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/media-actions.d.ts +19 -0
- package/dist/media-actions.d.ts.map +1 -0
- package/dist/media-actions.js +57 -0
- package/dist/media-actions.js.map +1 -0
- package/dist/media-analyzer.d.ts +47 -1
- package/dist/media-analyzer.d.ts.map +1 -1
- package/dist/media-analyzer.js +91 -0
- package/dist/media-analyzer.js.map +1 -1
- package/dist/region-edit.d.ts +2 -0
- package/dist/region-edit.d.ts.map +1 -1
- package/dist/region-edit.js +6 -0
- package/dist/region-edit.js.map +1 -1
- package/dist/result-format.d.ts +11 -0
- package/dist/result-format.d.ts.map +1 -0
- package/dist/result-format.js +30 -0
- package/dist/result-format.js.map +1 -0
- package/dist/unique-id.d.ts +3 -0
- package/dist/unique-id.d.ts.map +1 -0
- package/dist/unique-id.js +7 -0
- package/dist/unique-id.js.map +1 -0
- package/package.json +2 -1
- package/src/composer-view.ts +235 -0
- package/src/edit-card.ts +138 -0
- package/src/index.ts +4 -0
- package/src/media-actions.ts +55 -0
- package/src/media-analyzer.ts +112 -1
- package/src/region-edit.ts +8 -0
- package/src/result-format.ts +34 -0
- package/src/unique-id.ts +6 -0
package/README.md
CHANGED
|
@@ -77,8 +77,8 @@ Set `FAL_KEY` and `OPENROUTER_API_KEY` in the server's environment (never
|
|
|
77
77
|
with a `VITE_` or `NEXT_PUBLIC_` prefix). A missing key comes back as a
|
|
78
78
|
clear error, e.g. "FAL_KEY isn't set on the server."
|
|
79
79
|
|
|
80
|
-
**Where it runs.**
|
|
81
|
-
|
|
80
|
+
**Where it runs.** The route lives in your app's framework (React or Vue)
|
|
81
|
+
or on the API server behind it. Mount it at
|
|
82
82
|
`/api/chai`, where the components send their requests. `handle(request)`
|
|
83
83
|
takes a standard web `Request` and returns a `Response`; `GET`, `POST` and
|
|
84
84
|
`PUT` (Fal's cancel call) are the same function. When the browser
|
|
@@ -88,6 +88,7 @@ disconnects, the provider call is aborted too.
|
|
|
88
88
|
|---|---|
|
|
89
89
|
| Next.js (App Router) | `export const { GET, POST, PUT } = createChaiHandler();` in `app/api/chai/[...path]/route.ts` |
|
|
90
90
|
| Remix / React Router | `export const loader = ({ request }) => chai.handle(request); export const action = loader;` in `app/routes/api.chai.$.ts` |
|
|
91
|
+
| Nuxt | `export default defineEventHandler((event) => chai.handle(toWebRequest(event)));` in `server/api/chai/[...path].ts` |
|
|
91
92
|
| Hono | `app.all("/api/chai/*", (c) => chai.handle(c.req.raw))` |
|
|
92
93
|
| Bun, Deno | route `/api/chai/*` to `chai.handle` in `Bun.serve` / `Deno.serve` |
|
|
93
94
|
| Cloudflare Workers | `createChaiHandler({ falKey: env.FAL_KEY, openRouterKey: env.OPENROUTER_API_KEY }).handle(request)` |
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the Composer component shows and submits, worked out from its props:
|
|
3
|
+
* the same in every framework binding. Pure; nothing here renders.
|
|
4
|
+
*/
|
|
5
|
+
import type { ComposerAttachment, ComposerSubmitPayload, ComposerUseCase } from "./composer.js";
|
|
6
|
+
import type { EditRegion } from "./region-edit.js";
|
|
7
|
+
import type { MediaKind, ModelOption, ParameterOption } from "./types.js";
|
|
8
|
+
export type ComposerAttachMenuAction = "add-media" | "create-image" | "create-video" | "edit-media";
|
|
9
|
+
/** The `+` menu, in order. "Add media" is the only item with a divider after it. */
|
|
10
|
+
export declare const COMPOSER_ATTACH_MENU_ITEMS: {
|
|
11
|
+
action: ComposerAttachMenuAction;
|
|
12
|
+
label: string;
|
|
13
|
+
divider?: boolean;
|
|
14
|
+
}[];
|
|
15
|
+
/**
|
|
16
|
+
* Past this length a rewritten prompt appears at once: a long reveal reads
|
|
17
|
+
* as sluggish, and a long prompt is when a person wants the result now.
|
|
18
|
+
*/
|
|
19
|
+
export declare const MAX_ANIMATE_ENHANCE_LENGTH = 260;
|
|
20
|
+
/** How long the Enhance reveal takes: time-driven, between 300ms and 900ms. */
|
|
21
|
+
export declare function enhanceRevealDurationMs(charCount: number): number;
|
|
22
|
+
/** The Composer props `composerView` reads. Callbacks only matter by being there. */
|
|
23
|
+
export interface ComposerViewInput {
|
|
24
|
+
value: string;
|
|
25
|
+
placeholder?: string;
|
|
26
|
+
attachments?: ComposerAttachment[];
|
|
27
|
+
useCase?: ComposerUseCase | null;
|
|
28
|
+
defaultUseCase?: ComposerUseCase;
|
|
29
|
+
multiSelectUseCases?: boolean;
|
|
30
|
+
useCases?: ComposerUseCase[];
|
|
31
|
+
modelsByKind?: Partial<Record<MediaKind, ModelOption[]>>;
|
|
32
|
+
multiSelectModels?: boolean;
|
|
33
|
+
modelIds?: string[];
|
|
34
|
+
models?: ModelOption[];
|
|
35
|
+
modelId?: string | null;
|
|
36
|
+
onModelChange?: unknown;
|
|
37
|
+
onModelIdsChange?: unknown;
|
|
38
|
+
autoSelectModel?: boolean;
|
|
39
|
+
aspectRatios?: ParameterOption<string>[];
|
|
40
|
+
aspectRatio?: string | null;
|
|
41
|
+
submitError?: string | null;
|
|
42
|
+
submitDisabled?: boolean;
|
|
43
|
+
submitting?: boolean;
|
|
44
|
+
onAbort?: unknown;
|
|
45
|
+
regions?: EditRegion[];
|
|
46
|
+
disabled?: boolean;
|
|
47
|
+
}
|
|
48
|
+
export declare function composerView(props: ComposerViewInput): {
|
|
49
|
+
pickedUseCases: ComposerUseCase[];
|
|
50
|
+
/**
|
|
51
|
+
* Which use cases are picked, as one string. When it changes, a Composer
|
|
52
|
+
* whose end user owns the auto-select switch turns auto-select off: it
|
|
53
|
+
* belongs to the Model menu it was turned on in, so a new use case
|
|
54
|
+
* starts with its model picked by hand.
|
|
55
|
+
*/
|
|
56
|
+
useCaseKey: string;
|
|
57
|
+
activeUseCases: ComposerUseCase[];
|
|
58
|
+
editing: boolean;
|
|
59
|
+
isSubmitDisabled: boolean;
|
|
60
|
+
placeholder: string;
|
|
61
|
+
showStop: boolean;
|
|
62
|
+
multiModel: boolean;
|
|
63
|
+
modelSections: {
|
|
64
|
+
options: ModelOption[];
|
|
65
|
+
suggested: boolean;
|
|
66
|
+
label: string;
|
|
67
|
+
kind: MediaKind;
|
|
68
|
+
}[];
|
|
69
|
+
pickedModelIds: string[];
|
|
70
|
+
canPickModel: boolean;
|
|
71
|
+
modelTriggerLabel: string;
|
|
72
|
+
showAspectRatio: boolean;
|
|
73
|
+
aspectOptions: ParameterOption<string>[];
|
|
74
|
+
offeredAspect: string | null;
|
|
75
|
+
selectedAspect: ParameterOption<string> | null;
|
|
76
|
+
/** The model ids after a row click: the one picked, or with multi-select that one toggled. */
|
|
77
|
+
toggledModelIds: (id: string) => string[];
|
|
78
|
+
/** Everything a submit sends. */
|
|
79
|
+
payload: () => ComposerSubmitPayload;
|
|
80
|
+
};
|
|
81
|
+
export type ComposerView = ReturnType<typeof composerView>;
|
|
82
|
+
/** The `+` menu items that can work: "Add media" needs somewhere to put files, the use-case items a handler. */
|
|
83
|
+
export declare function composerAttachMenuItems(actions: ComposerAttachMenuAction[] | undefined, { canAddMedia, canSelect }: {
|
|
84
|
+
canAddMedia: boolean;
|
|
85
|
+
canSelect: boolean;
|
|
86
|
+
}): {
|
|
87
|
+
action: ComposerAttachMenuAction;
|
|
88
|
+
label: string;
|
|
89
|
+
divider?: boolean;
|
|
90
|
+
}[];
|
|
91
|
+
/**
|
|
92
|
+
* The attachments after picking files: each image, video or audio file
|
|
93
|
+
* added in the order picked. Editing works on one image, so its first image
|
|
94
|
+
* replaces what's there (and with none, every file is unsupported).
|
|
95
|
+
*/
|
|
96
|
+
export declare function composerAttachmentsFromFiles(files: Iterable<File>, current: ComposerAttachment[], editing: boolean): Promise<{
|
|
97
|
+
attachments: ComposerAttachment[] | null;
|
|
98
|
+
unsupported: string[];
|
|
99
|
+
}>;
|
|
100
|
+
//# sourceMappingURL=composer-view.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composer-view.d.ts","sourceRoot":"","sources":["../src/composer-view.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { uniqueId } from "./unique-id.js";
|
|
2
|
+
import { TEXT_USE_CASE } from "./composer.js";
|
|
3
|
+
import { readMediaFiles } from "./media-analyzer.js";
|
|
4
|
+
import { deriveGenerationUseCase } from "./run.js";
|
|
5
|
+
import { suggestedModels } from "./suggested-models.js";
|
|
6
|
+
/** The `+` menu, in order. "Add media" is the only item with a divider after it. */
|
|
7
|
+
export const COMPOSER_ATTACH_MENU_ITEMS = [
|
|
8
|
+
{ action: "add-media", label: "Add media", divider: true },
|
|
9
|
+
{ action: "create-image", label: "Create image" },
|
|
10
|
+
{ action: "create-video", label: "Create video" },
|
|
11
|
+
{ action: "edit-media", label: "Edit image" },
|
|
12
|
+
];
|
|
13
|
+
/**
|
|
14
|
+
* Past this length a rewritten prompt appears at once: a long reveal reads
|
|
15
|
+
* as sluggish, and a long prompt is when a person wants the result now.
|
|
16
|
+
*/
|
|
17
|
+
export const MAX_ANIMATE_ENHANCE_LENGTH = 260;
|
|
18
|
+
/** How long the Enhance reveal takes: time-driven, between 300ms and 900ms. */
|
|
19
|
+
export function enhanceRevealDurationMs(charCount) {
|
|
20
|
+
return Math.min(900, Math.max(300, charCount * 6));
|
|
21
|
+
}
|
|
22
|
+
export function composerView(props) {
|
|
23
|
+
const { value, attachments = [], useCase = null, defaultUseCase = TEXT_USE_CASE, multiSelectUseCases = false, useCases = [], modelsByKind, multiSelectModels = false, modelIds = [], models, modelId = null, autoSelectModel = false, aspectRatios, aspectRatio = null, submitError = null, submitting = false, regions = [], disabled = false, } = props;
|
|
24
|
+
// What the person picked, and what actually runs: with nothing picked, the
|
|
25
|
+
// builder's default use case (a text request unless they chose otherwise).
|
|
26
|
+
const pickedUseCases = multiSelectUseCases ? useCases : useCase ? [useCase] : [];
|
|
27
|
+
const activeUseCases = pickedUseCases.length > 0 ? pickedUseCases : [defaultUseCase];
|
|
28
|
+
// An edit needs an image, then a change: a prompt for the whole image, a
|
|
29
|
+
// region with its own instruction, or both.
|
|
30
|
+
const editing = activeUseCases.some((u) => u.edit);
|
|
31
|
+
const hasImage = attachments.some((a) => a.kind === "image");
|
|
32
|
+
const hasContent = editing
|
|
33
|
+
? hasImage && (value.trim().length > 0 || regions.length > 0)
|
|
34
|
+
: value.trim().length > 0 || attachments.length > 0;
|
|
35
|
+
const placeholder = props.placeholder ??
|
|
36
|
+
(editing
|
|
37
|
+
? hasImage
|
|
38
|
+
? "Describe a change across the whole image, or leave this empty"
|
|
39
|
+
: "Attach an image to edit"
|
|
40
|
+
: "Describe media to create");
|
|
41
|
+
// The stop icon only appears with somewhere for its click to go.
|
|
42
|
+
const showStop = submitting && Boolean(props.onAbort);
|
|
43
|
+
const multiModel = multiSelectModels || multiSelectUseCases;
|
|
44
|
+
const rawPickedIds = multiModel ? modelIds : modelId ? [modelId] : [];
|
|
45
|
+
// The app's list for a use case, or Chai's suggested models when it gave none.
|
|
46
|
+
const rosterFor = (u) => {
|
|
47
|
+
const given = multiSelectUseCases ? modelsByKind?.[u.kind] : models;
|
|
48
|
+
if (given)
|
|
49
|
+
return { options: given, suggested: false };
|
|
50
|
+
return { options: suggestedModels(u.edit ? "image-edit" : deriveGenerationUseCase(u, attachments)), suggested: true };
|
|
51
|
+
};
|
|
52
|
+
// One menu section per use case with multi-use-case select; otherwise one flat roster.
|
|
53
|
+
const modelSections = (multiSelectUseCases ? activeUseCases : activeUseCases.slice(0, 1))
|
|
54
|
+
.map((u) => ({ label: multiSelectUseCases ? u.label : "", kind: u.kind, ...rosterFor(u) }))
|
|
55
|
+
.filter((s) => s.options.length > 0);
|
|
56
|
+
const allModels = modelSections.flatMap((s) => s.options);
|
|
57
|
+
// No menu that can't change anything: without its handler, the Model menu hides.
|
|
58
|
+
const canPickModel = Boolean(multiModel ? props.onModelIdsChange : props.onModelChange);
|
|
59
|
+
// A pick that isn't in the current list (e.g. the edit model after leaving
|
|
60
|
+
// edit mode) is shown as unpicked and never submitted. With no list at
|
|
61
|
+
// all, the app's pick is passed through as given. The Composer never picks
|
|
62
|
+
// for the person: with a Model menu, nothing is picked until they pick or
|
|
63
|
+
// turn on auto-select. Only with no menu (nothing the person could pick
|
|
64
|
+
// with) does a suggested list's first model run.
|
|
65
|
+
const validPicks = allModels.length > 0 ? rawPickedIds.filter((id) => allModels.some((m) => m.id === id)) : rawPickedIds;
|
|
66
|
+
const pickedModelIds = validPicks.length > 0 || autoSelectModel || canPickModel
|
|
67
|
+
? validPicks
|
|
68
|
+
: modelSections.filter((s) => s.suggested).map((s) => s.options[0].id);
|
|
69
|
+
const pickedModels = allModels.filter((m) => pickedModelIds.includes(m.id));
|
|
70
|
+
const modelTriggerLabel = pickedModels.length > 1
|
|
71
|
+
? `${pickedModels.length} models`
|
|
72
|
+
: (pickedModels[0]?.label ?? (autoSelectModel ? "Auto-select" : multiModel ? "Select models" : "Select model"));
|
|
73
|
+
// With a Model menu, a submit needs a model: one picked, or auto-select to pick it.
|
|
74
|
+
const needsModel = canPickModel && modelSections.length > 0 && !autoSelectModel && pickedModelIds.length === 0;
|
|
75
|
+
const isSubmitDisabled = submitError ? disabled : (props.submitDisabled ?? (disabled || !hasContent || needsModel));
|
|
76
|
+
// An edit keeps the source image's shape.
|
|
77
|
+
const showAspectRatio = activeUseCases.some((u) => !u.edit && (u.kind === "image" || u.kind === "video"));
|
|
78
|
+
// Only ratios every model that might run takes: the picked ones, or with
|
|
79
|
+
// none picked (or auto-select on) the whole list, so whichever runs can
|
|
80
|
+
// honor it. A model with no `aspectRatios` sets its own shape: no menu.
|
|
81
|
+
const ratioModels = pickedModels.length > 0 && !autoSelectModel ? pickedModels : allModels;
|
|
82
|
+
const sharedRatios = ratioModels.reduce((shared, m) => (shared ?? m.aspectRatios ?? []).filter((r) => m.aspectRatios?.includes(r)), null) ??
|
|
83
|
+
[];
|
|
84
|
+
const aspectOptions = aspectRatios
|
|
85
|
+
? aspectRatios.filter((o) => sharedRatios.includes(o.value))
|
|
86
|
+
: sharedRatios.map((r) => ({ value: r, label: r }));
|
|
87
|
+
// A pick the current models don't take is shown as unpicked and never sent.
|
|
88
|
+
const offeredAspect = showAspectRatio && aspectOptions.some((o) => o.value === aspectRatio) ? aspectRatio : null;
|
|
89
|
+
const selectedAspect = aspectOptions.find((o) => o.value === offeredAspect) ?? null;
|
|
90
|
+
const selections = () => activeUseCases.map((u) => ({
|
|
91
|
+
useCase: u,
|
|
92
|
+
models: rosterFor(u).options,
|
|
93
|
+
modelIds: multiSelectUseCases ? pickedModelIds.filter((id) => rosterFor(u).options.some((m) => m.id === id)) : pickedModelIds,
|
|
94
|
+
}));
|
|
95
|
+
return {
|
|
96
|
+
pickedUseCases,
|
|
97
|
+
/**
|
|
98
|
+
* Which use cases are picked, as one string. When it changes, a Composer
|
|
99
|
+
* whose end user owns the auto-select switch turns auto-select off: it
|
|
100
|
+
* belongs to the Model menu it was turned on in, so a new use case
|
|
101
|
+
* starts with its model picked by hand.
|
|
102
|
+
*/
|
|
103
|
+
useCaseKey: pickedUseCases.map((u) => `${u.kind}${u.edit ? ":edit" : ""}`).join(","),
|
|
104
|
+
activeUseCases,
|
|
105
|
+
editing,
|
|
106
|
+
isSubmitDisabled,
|
|
107
|
+
placeholder,
|
|
108
|
+
showStop,
|
|
109
|
+
multiModel,
|
|
110
|
+
modelSections,
|
|
111
|
+
pickedModelIds,
|
|
112
|
+
canPickModel,
|
|
113
|
+
modelTriggerLabel,
|
|
114
|
+
showAspectRatio,
|
|
115
|
+
aspectOptions,
|
|
116
|
+
offeredAspect,
|
|
117
|
+
selectedAspect,
|
|
118
|
+
/** The model ids after a row click: the one picked, or with multi-select that one toggled. */
|
|
119
|
+
toggledModelIds: (id) => (modelIds.includes(id) ? modelIds.filter((m) => m !== id) : [...modelIds, id]),
|
|
120
|
+
/** Everything a submit sends. */
|
|
121
|
+
payload: () => ({
|
|
122
|
+
value,
|
|
123
|
+
attachments,
|
|
124
|
+
useCase: activeUseCases[0],
|
|
125
|
+
modelId: pickedModelIds[0] ?? null,
|
|
126
|
+
aspectRatio: offeredAspect,
|
|
127
|
+
selections: selections(),
|
|
128
|
+
autoSelectModel,
|
|
129
|
+
regions: editing ? regions : [],
|
|
130
|
+
}),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/** The `+` menu items that can work: "Add media" needs somewhere to put files, the use-case items a handler. */
|
|
134
|
+
export function composerAttachMenuItems(actions, { canAddMedia, canSelect }) {
|
|
135
|
+
return COMPOSER_ATTACH_MENU_ITEMS.filter((item) => (actions ?? COMPOSER_ATTACH_MENU_ITEMS.map((i) => i.action)).includes(item.action) &&
|
|
136
|
+
(item.action === "add-media" ? canAddMedia : canSelect));
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The attachments after picking files: each image, video or audio file
|
|
140
|
+
* added in the order picked. Editing works on one image, so its first image
|
|
141
|
+
* replaces what's there (and with none, every file is unsupported).
|
|
142
|
+
*/
|
|
143
|
+
export async function composerAttachmentsFromFiles(files, current, editing) {
|
|
144
|
+
const list = [...files];
|
|
145
|
+
if (editing) {
|
|
146
|
+
const image = list.find((f) => f.type.startsWith("image/"));
|
|
147
|
+
if (!image)
|
|
148
|
+
return { attachments: null, unsupported: list.map((f) => f.name) };
|
|
149
|
+
const { media, unsupported } = await readMediaFiles([image]);
|
|
150
|
+
return { attachments: media.length > 0 ? media.map((m) => ({ ...m, id: uniqueId() })) : null, unsupported };
|
|
151
|
+
}
|
|
152
|
+
const { media, unsupported } = await readMediaFiles(list);
|
|
153
|
+
return { attachments: [...current, ...media.map((m) => ({ ...m, id: uniqueId() }))], unsupported };
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=composer-view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composer-view.js","sourceRoot":"","sources":["../src/composer-view.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAKxD,oFAAoF;AACpF,MAAM,CAAC,MAAM,0BAA0B,GAA6E;IAClH,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE;IAC1D,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,EAAE;IACjD,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,EAAE;IACjD,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE;CAC9C,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,GAAG,CAAC;AAE9C,+EAA+E;AAC/E,MAAM,UAAU,uBAAuB,CAAC,SAAiB;IACvD,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC;AA6BD,MAAM,UAAU,YAAY,CAAC,KAAwB;IACnD,MAAM,EACJ,KAAK,EACL,WAAW,GAAG,EAAE,EAChB,OAAO,GAAG,IAAI,EACd,cAAc,GAAG,aAAa,EAC9B,mBAAmB,GAAG,KAAK,EAC3B,QAAQ,GAAG,EAAE,EACb,YAAY,EACZ,iBAAiB,GAAG,KAAK,EACzB,QAAQ,GAAG,EAAE,EACb,MAAM,EACN,OAAO,GAAG,IAAI,EACd,eAAe,GAAG,KAAK,EACvB,YAAY,EACZ,WAAW,GAAG,IAAI,EAClB,WAAW,GAAG,IAAI,EAClB,UAAU,GAAG,KAAK,EAClB,OAAO,GAAG,EAAE,EACZ,QAAQ,GAAG,KAAK,GACjB,GAAG,KAAK,CAAC;IAEV,2EAA2E;IAC3E,2EAA2E;IAC3E,MAAM,cAAc,GAAG,mBAAmB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACjF,MAAM,cAAc,GAAG,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;IACrF,yEAAyE;IACzE,4CAA4C;IAC5C,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;IAC7D,MAAM,UAAU,GAAG,OAAO;QACxB,CAAC,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QAC7D,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC;IACtD,MAAM,WAAW,GACf,KAAK,CAAC,WAAW;QACjB,CAAC,OAAO;YACN,CAAC,CAAC,QAAQ;gBACR,CAAC,CAAC,+DAA+D;gBACjE,CAAC,CAAC,yBAAyB;YAC7B,CAAC,CAAC,0BAA0B,CAAC,CAAC;IAClC,iEAAiE;IACjE,MAAM,QAAQ,GAAG,UAAU,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAEtD,MAAM,UAAU,GAAG,iBAAiB,IAAI,mBAAmB,CAAC;IAC5D,MAAM,YAAY,GAAG,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACtE,+EAA+E;IAC/E,MAAM,SAAS,GAAG,CAAC,CAAkB,EAAkD,EAAE;QACvF,MAAM,KAAK,GAAG,mBAAmB,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QACpE,IAAI,KAAK;YAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QACvD,OAAO,EAAE,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IACxH,CAAC,CAAC;IACF,uFAAuF;IACvF,MAAM,aAAa,GAAG,CAAC,mBAAmB,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;SACtF,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;SAC1F,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAC1D,iFAAiF;IACjF,MAAM,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IACxF,2EAA2E;IAC3E,uEAAuE;IACvE,2EAA2E;IAC3E,0EAA0E;IAC1E,wEAAwE;IACxE,iDAAiD;IACjD,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;IACzH,MAAM,cAAc,GAClB,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,eAAe,IAAI,YAAY;QACtD,CAAC,CAAC,UAAU;QACZ,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,EAAE,CAAC,CAAC;IAC5E,MAAM,YAAY,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5E,MAAM,iBAAiB,GACrB,YAAY,CAAC,MAAM,GAAG,CAAC;QACrB,CAAC,CAAC,GAAG,YAAY,CAAC,MAAM,SAAS;QACjC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC;IACpH,oFAAoF;IACpF,MAAM,UAAU,GAAG,YAAY,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,eAAe,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,CAAC;IAC/G,MAAM,gBAAgB,GAAG,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,IAAI,CAAC,QAAQ,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,CAAC,CAAC;IAEpH,0CAA0C;IAC1C,MAAM,eAAe,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC;IAC1G,yEAAyE;IACzE,wEAAwE;IACxE,wEAAwE;IACxE,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3F,MAAM,YAAY,GAChB,WAAW,CAAC,MAAM,CAAkB,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;QACrI,EAAE,CAAC;IACL,MAAM,aAAa,GAAG,YAAY;QAChC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC5D,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACtD,4EAA4E;IAC5E,MAAM,aAAa,GAAG,eAAe,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;IACjH,MAAM,cAAc,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,aAAa,CAAC,IAAI,IAAI,CAAC;IAEpF,MAAM,UAAU,GAAG,GAAwB,EAAE,CAC3C,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACzB,OAAO,EAAE,CAAC;QACV,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO;QAC5B,QAAQ,EAAE,mBAAmB,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc;KAC9H,CAAC,CAAC,CAAC;IAEN,OAAO;QACL,cAAc;QACd;;;;;WAKG;QACH,UAAU,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QACpF,cAAc;QACd,OAAO;QACP,gBAAgB;QAChB,WAAW;QACX,QAAQ;QACR,UAAU;QACV,aAAa;QACb,cAAc;QACd,YAAY;QACZ,iBAAiB;QACjB,eAAe;QACf,aAAa;QACb,aAAa;QACb,cAAc;QACd,8FAA8F;QAC9F,eAAe,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,EAAE,EAAE,CAAC,CAAC;QAC/G,iCAAiC;QACjC,OAAO,EAAE,GAA0B,EAAE,CAAC,CAAC;YACrC,KAAK;YACL,WAAW;YACX,OAAO,EAAE,cAAc,CAAC,CAAC,CAAE;YAC3B,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,IAAI;YAClC,WAAW,EAAE,aAAa;YAC1B,UAAU,EAAE,UAAU,EAAE;YACxB,eAAe;YACf,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;SAChC,CAAC;KACH,CAAC;AACJ,CAAC;AAID,gHAAgH;AAChH,MAAM,UAAU,uBAAuB,CACrC,OAA+C,EAC/C,EAAE,WAAW,EAAE,SAAS,EAAgD;IAExE,OAAO,0BAA0B,CAAC,MAAM,CACtC,CAAC,IAAI,EAAE,EAAE,CACP,CAAC,OAAO,IAAI,0BAA0B,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;QAClF,CAAC,IAAI,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAC1D,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,KAAqB,EACrB,OAA6B,EAC7B,OAAgB;IAEhB,MAAM,IAAI,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;IACxB,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC5D,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/E,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,cAAc,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7D,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC;IAC9G,CAAC;IACD,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;IAC1D,OAAO,EAAE,WAAW,EAAE,CAAC,GAAG,OAAO,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;AACrG,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type EditRegion, type RegionBox } from "./region-edit.js";
|
|
2
|
+
export declare const EDIT_ZOOM_STEPS: number[];
|
|
3
|
+
export type RegionCorner = "nw" | "ne" | "sw" | "se";
|
|
4
|
+
export declare const REGION_CORNERS: RegionCorner[];
|
|
5
|
+
export interface Size {
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
}
|
|
9
|
+
export interface Pan {
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
}
|
|
13
|
+
/** Keeps a box inside the image and at least the minimum size. */
|
|
14
|
+
export declare function fitBox(box: RegionBox): RegionBox;
|
|
15
|
+
/** A box resized by dragging one corner; the opposite corner stays put. */
|
|
16
|
+
export declare function resizeBox(box: RegionBox, corner: RegionCorner, dx: number, dy: number): RegionBox;
|
|
17
|
+
/** A region's box after a key: arrows move it (Shift resizes, Alt takes bigger steps). `null` for any other key. */
|
|
18
|
+
export declare function regionBoxForKey(box: RegionBox, key: string, { shift, alt }?: {
|
|
19
|
+
shift?: boolean | undefined;
|
|
20
|
+
alt?: boolean | undefined;
|
|
21
|
+
}): RegionBox | null;
|
|
22
|
+
/** Pan limits at a zoom level: the image always covers the viewport. */
|
|
23
|
+
export declare function clampPan(pan: Pan, zoom: number, size: Size): Pan;
|
|
24
|
+
/** The pan that zooms around the middle of the view, so what's centered stays centered. */
|
|
25
|
+
export declare function panForZoom(pan: Pan, zoom: number, next: number, size: Size): Pan;
|
|
26
|
+
/** The pan after an arrow key on the zoomed image, or `null` for any other key. */
|
|
27
|
+
export declare function panForKey(pan: Pan, key: string, zoom: number, size: Size): Pan | null;
|
|
28
|
+
/** The lowest number no current region uses, so freed numbers (and colors) come back first. */
|
|
29
|
+
export declare function freeRegionNumber(regions: EditRegion[]): number;
|
|
30
|
+
/** A new region, centered in the part of the image in view, so it's visible when zoomed in. */
|
|
31
|
+
export declare function newRegion(regions: EditRegion[], zoom: number, pan: Pan, size: Size): EditRegion;
|
|
32
|
+
/**
|
|
33
|
+
* Where the instruction field goes: above its region, or below it when
|
|
34
|
+
* there isn't room above and it clears the controls along the bottom edge.
|
|
35
|
+
*/
|
|
36
|
+
export declare function regionPopoverPosition(box: RegionBox, zoom: number, pan: Pan, size: Size, paginated: boolean): {
|
|
37
|
+
left: number;
|
|
38
|
+
top: number;
|
|
39
|
+
above: boolean;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* The card's width: from `scale` (of the image's own width), else `width`,
|
|
43
|
+
* else `maxWidth`, then fitted inside `maxWidth` × `maxHeight` keeping the image's shape.
|
|
44
|
+
*/
|
|
45
|
+
export declare function editCardWidth(natural: Size | null, { width, scale, maxWidth, maxHeight }: {
|
|
46
|
+
width?: number;
|
|
47
|
+
scale?: number;
|
|
48
|
+
maxWidth: number;
|
|
49
|
+
maxHeight: number;
|
|
50
|
+
}): number;
|
|
51
|
+
/** A region's place on the image, as percentages, and its color. */
|
|
52
|
+
export declare function regionBoxStyle(r: EditRegion): Record<string, string>;
|
|
53
|
+
//# sourceMappingURL=edit-card.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit-card.d.ts","sourceRoot":"","sources":["../src/edit-card.ts"],"names":[],"mappings":"AAMA,OAAO,EAAe,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEhF,eAAO,MAAM,eAAe,UAA0B,CAAC;AAQvD,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACrD,eAAO,MAAM,cAAc,EAAE,YAAY,EAA6B,CAAC;AACvE,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AACD,MAAM,WAAW,GAAG;IAClB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAID,kEAAkE;AAClE,wBAAgB,MAAM,CAAC,GAAG,EAAE,SAAS,GAAG,SAAS,CAIhD;AAED,2EAA2E;AAC3E,wBAAgB,SAAS,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,SAAS,CAUjG;AAED,oHAAoH;AACpH,wBAAgB,eAAe,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,KAAa,EAAE,GAAW,EAAE;;;CAAK,GAAG,SAAS,GAAG,IAAI,CAMlH;AAED,wEAAwE;AACxE,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,GAAG,CAKhE;AAED,2FAA2F;AAC3F,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,GAAG,CAKhF;AAED,mFAAmF;AACnF,wBAAgB,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,GAAG,GAAG,IAAI,CAIrF;AAED,+FAA+F;AAC/F,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,CAI9D;AAED,+FAA+F;AAC/F,wBAAgB,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,GAAG,UAAU,CAM/F;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO;;;;EAY3G;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,IAAI,GAAG,IAAI,EACpB,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC7G,MAAM,CAIR;AAED,oEAAoE;AACpE,wBAAgB,cAAc,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQpE"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The EditCard's geometry, the same in every framework binding: region
|
|
3
|
+
* boxes, zoom and pan, where the instruction field goes, and the card's
|
|
4
|
+
* width. Boxes are fractions of the image (0 to 1); pan and sizes are px.
|
|
5
|
+
*/
|
|
6
|
+
import { uniqueId } from "./unique-id.js";
|
|
7
|
+
import { regionColor } from "./region-edit.js";
|
|
8
|
+
export const EDIT_ZOOM_STEPS = [1, 1.25, 1.5, 2, 3, 4];
|
|
9
|
+
const MIN_SIZE = 0.03;
|
|
10
|
+
const KEY_STEP = 0.01;
|
|
11
|
+
const KEY_STEP_LARGE = 0.05;
|
|
12
|
+
/** The instruction field's width, and a typical height (title row and a two-line field), for placing it. */
|
|
13
|
+
const POPOVER_WIDTH = 220;
|
|
14
|
+
const POPOVER_HEIGHT = 96;
|
|
15
|
+
export const REGION_CORNERS = ["nw", "ne", "sw", "se"];
|
|
16
|
+
const clamp = (v, min, max) => Math.min(max, Math.max(min, v));
|
|
17
|
+
/** Keeps a box inside the image and at least the minimum size. */
|
|
18
|
+
export function fitBox(box) {
|
|
19
|
+
const width = clamp(box.width, MIN_SIZE, 1);
|
|
20
|
+
const height = clamp(box.height, MIN_SIZE, 1);
|
|
21
|
+
return { x: clamp(box.x, 0, 1 - width), y: clamp(box.y, 0, 1 - height), width, height };
|
|
22
|
+
}
|
|
23
|
+
/** A box resized by dragging one corner; the opposite corner stays put. */
|
|
24
|
+
export function resizeBox(box, corner, dx, dy) {
|
|
25
|
+
let left = box.x;
|
|
26
|
+
let top = box.y;
|
|
27
|
+
let right = box.x + box.width;
|
|
28
|
+
let bottom = box.y + box.height;
|
|
29
|
+
if (corner === "nw" || corner === "sw")
|
|
30
|
+
left = clamp(left + dx, 0, right - MIN_SIZE);
|
|
31
|
+
else
|
|
32
|
+
right = clamp(right + dx, left + MIN_SIZE, 1);
|
|
33
|
+
if (corner === "nw" || corner === "ne")
|
|
34
|
+
top = clamp(top + dy, 0, bottom - MIN_SIZE);
|
|
35
|
+
else
|
|
36
|
+
bottom = clamp(bottom + dy, top + MIN_SIZE, 1);
|
|
37
|
+
return { x: left, y: top, width: right - left, height: bottom - top };
|
|
38
|
+
}
|
|
39
|
+
/** A region's box after a key: arrows move it (Shift resizes, Alt takes bigger steps). `null` for any other key. */
|
|
40
|
+
export function regionBoxForKey(box, key, { shift = false, alt = false } = {}) {
|
|
41
|
+
const step = alt ? KEY_STEP_LARGE : KEY_STEP;
|
|
42
|
+
const delta = { ArrowLeft: [-step, 0], ArrowRight: [step, 0], ArrowUp: [0, -step], ArrowDown: [0, step] }[key];
|
|
43
|
+
if (!delta)
|
|
44
|
+
return null;
|
|
45
|
+
const [dx, dy] = delta;
|
|
46
|
+
return shift ? resizeBox(box, "se", dx, dy) : fitBox({ ...box, x: box.x + dx, y: box.y + dy });
|
|
47
|
+
}
|
|
48
|
+
/** Pan limits at a zoom level: the image always covers the viewport. */
|
|
49
|
+
export function clampPan(pan, zoom, size) {
|
|
50
|
+
return {
|
|
51
|
+
x: clamp(pan.x, size.width - size.width * zoom, 0),
|
|
52
|
+
y: clamp(pan.y, size.height - size.height * zoom, 0),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** The pan that zooms around the middle of the view, so what's centered stays centered. */
|
|
56
|
+
export function panForZoom(pan, zoom, next, size) {
|
|
57
|
+
const cx = size.width / 2;
|
|
58
|
+
const cy = size.height / 2;
|
|
59
|
+
const ratio = next / zoom;
|
|
60
|
+
return clampPan({ x: cx - (cx - pan.x) * ratio, y: cy - (cy - pan.y) * ratio }, next, size);
|
|
61
|
+
}
|
|
62
|
+
/** The pan after an arrow key on the zoomed image, or `null` for any other key. */
|
|
63
|
+
export function panForKey(pan, key, zoom, size) {
|
|
64
|
+
const step = 40;
|
|
65
|
+
const delta = { ArrowLeft: [step, 0], ArrowRight: [-step, 0], ArrowUp: [0, step], ArrowDown: [0, -step] }[key];
|
|
66
|
+
return delta ? clampPan({ x: pan.x + delta[0], y: pan.y + delta[1] }, zoom, size) : null;
|
|
67
|
+
}
|
|
68
|
+
/** The lowest number no current region uses, so freed numbers (and colors) come back first. */
|
|
69
|
+
export function freeRegionNumber(regions) {
|
|
70
|
+
let n = 1;
|
|
71
|
+
while (regions.some((r) => r.number === n))
|
|
72
|
+
n += 1;
|
|
73
|
+
return n;
|
|
74
|
+
}
|
|
75
|
+
/** A new region, centered in the part of the image in view, so it's visible when zoomed in. */
|
|
76
|
+
export function newRegion(regions, zoom, pan, size) {
|
|
77
|
+
const view = 1 / zoom;
|
|
78
|
+
const centerX = size.width ? -pan.x / (size.width * zoom) + view / 2 : 0.5;
|
|
79
|
+
const centerY = size.height ? -pan.y / (size.height * zoom) + view / 2 : 0.5;
|
|
80
|
+
const box = fitBox({ x: centerX - (0.3 * view) / 2, y: centerY - (0.2 * view) / 2, width: 0.3 * view, height: 0.2 * view });
|
|
81
|
+
return { id: uniqueId(), number: freeRegionNumber(regions), box, prompt: "" };
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Where the instruction field goes: above its region, or below it when
|
|
85
|
+
* there isn't room above and it clears the controls along the bottom edge.
|
|
86
|
+
*/
|
|
87
|
+
export function regionPopoverPosition(box, zoom, pan, size, paginated) {
|
|
88
|
+
const left = box.x * size.width * zoom + pan.x;
|
|
89
|
+
const top = box.y * size.height * zoom + pan.y;
|
|
90
|
+
const bottom = (box.y + box.height) * size.height * zoom + pan.y;
|
|
91
|
+
const reserved = paginated ? 100 : 56;
|
|
92
|
+
const fitsBelow = bottom + 8 + POPOVER_HEIGHT <= size.height - reserved;
|
|
93
|
+
const above = top > POPOVER_HEIGHT + 16 || !fitsBelow;
|
|
94
|
+
return {
|
|
95
|
+
left: clamp(left, 8, Math.max(8, size.width - POPOVER_WIDTH - 8)),
|
|
96
|
+
top: above ? Math.max(top - 8, POPOVER_HEIGHT + 8) : bottom + 8,
|
|
97
|
+
above,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The card's width: from `scale` (of the image's own width), else `width`,
|
|
102
|
+
* else `maxWidth`, then fitted inside `maxWidth` × `maxHeight` keeping the image's shape.
|
|
103
|
+
*/
|
|
104
|
+
export function editCardWidth(natural, { width, scale, maxWidth, maxHeight }) {
|
|
105
|
+
const base = scale != null && natural ? natural.width * scale : (width ?? maxWidth);
|
|
106
|
+
const heightCap = natural && natural.height > 0 ? (maxHeight * natural.width) / natural.height : Infinity;
|
|
107
|
+
return Math.round(Math.min(base, maxWidth, heightCap));
|
|
108
|
+
}
|
|
109
|
+
/** A region's place on the image, as percentages, and its color. */
|
|
110
|
+
export function regionBoxStyle(r) {
|
|
111
|
+
return {
|
|
112
|
+
left: `${r.box.x * 100}%`,
|
|
113
|
+
top: `${r.box.y * 100}%`,
|
|
114
|
+
width: `${r.box.width * 100}%`,
|
|
115
|
+
height: `${r.box.height * 100}%`,
|
|
116
|
+
"--chai-region-color": regionColor(r.number),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=edit-card.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit-card.js","sourceRoot":"","sources":["../src/edit-card.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,4 +11,8 @@ export * from "./check-suggested-models.js";
|
|
|
11
11
|
export * from "./reasoning.js";
|
|
12
12
|
export * from "./composer.js";
|
|
13
13
|
export * from "./media-analyzer.js";
|
|
14
|
+
export * from "./media-actions.js";
|
|
15
|
+
export * from "./result-format.js";
|
|
16
|
+
export * from "./composer-view.js";
|
|
17
|
+
export * from "./edit-card.js";
|
|
14
18
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -11,4 +11,8 @@ export * from "./check-suggested-models.js";
|
|
|
11
11
|
export * from "./reasoning.js";
|
|
12
12
|
export * from "./composer.js";
|
|
13
13
|
export * from "./media-analyzer.js";
|
|
14
|
+
export * from "./media-actions.js";
|
|
15
|
+
export * from "./result-format.js";
|
|
16
|
+
export * from "./composer-view.js";
|
|
17
|
+
export * from "./edit-card.js";
|
|
14
18
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
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 declare function downloadMedia(src: string, filename: string): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* The native share sheet where there is one, otherwise the media's URL
|
|
14
|
+
* copied to the clipboard. Says which, so the button can confirm a copy.
|
|
15
|
+
*/
|
|
16
|
+
export declare function shareMedia(src: string): Promise<"shared" | "copied" | "none">;
|
|
17
|
+
/** A file name for a download: the URL's own extension when it has one, otherwise a guess by kind. */
|
|
18
|
+
export declare function downloadFilename(id: string, kind: string, src: string): string;
|
|
19
|
+
//# sourceMappingURL=media-actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-actions.d.ts","sourceRoot":"","sources":["../src/media-actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAehF;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC,CAgBnF;AAED,sGAAsG;AACtG,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAI9E"}
|
|
@@ -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"}
|