tempest-react-sdk 0.54.0 → 0.56.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 (45) hide show
  1. package/README.md +74 -73
  2. package/dist/components/AudioPlayer/AudioPlayer.cjs +1 -1
  3. package/dist/components/AudioPlayer/AudioPlayer.js +6 -6
  4. package/dist/components/VideoPlayer/VideoPlayer.cjs +2 -0
  5. package/dist/components/VideoPlayer/VideoPlayer.cjs.map +1 -0
  6. package/dist/components/VideoPlayer/VideoPlayer.js +229 -0
  7. package/dist/components/VideoPlayer/VideoPlayer.js.map +1 -0
  8. package/dist/components/VideoPlayer/VideoPlayer.module.cjs +2 -0
  9. package/dist/components/VideoPlayer/VideoPlayer.module.cjs.map +1 -0
  10. package/dist/components/VideoPlayer/VideoPlayer.module.js +19 -0
  11. package/dist/components/VideoPlayer/VideoPlayer.module.js.map +1 -0
  12. package/dist/components/VideoPlayer/playback-rates.cjs +2 -0
  13. package/dist/components/VideoPlayer/playback-rates.cjs.map +1 -0
  14. package/dist/components/VideoPlayer/playback-rates.js +11 -0
  15. package/dist/components/VideoPlayer/playback-rates.js.map +1 -0
  16. package/dist/imaging/exceptions.cjs +1 -1
  17. package/dist/imaging/exceptions.cjs.map +1 -1
  18. package/dist/imaging/exceptions.js +5 -1
  19. package/dist/imaging/exceptions.js.map +1 -1
  20. package/dist/imaging/frame.cjs +2 -0
  21. package/dist/imaging/frame.cjs.map +1 -0
  22. package/dist/imaging/frame.js +82 -0
  23. package/dist/imaging/frame.js.map +1 -0
  24. package/dist/imaging.cjs +1 -1
  25. package/dist/imaging.d.ts +611 -494
  26. package/dist/imaging.js +10 -9
  27. package/dist/styles/VideoPlayer.css +23 -0
  28. package/dist/styles/media.css +22 -0
  29. package/dist/styles.css +1 -1
  30. package/dist/tempest-react-sdk.cjs +1 -1
  31. package/dist/tempest-react-sdk.d.ts +389 -23
  32. package/dist/tempest-react-sdk.js +213 -209
  33. package/dist/webrtc/mesh-quality.cjs +2 -0
  34. package/dist/webrtc/mesh-quality.cjs.map +1 -0
  35. package/dist/webrtc/mesh-quality.js +51 -0
  36. package/dist/webrtc/mesh-quality.js.map +1 -0
  37. package/dist/webrtc/peer-link.cjs +2 -0
  38. package/dist/webrtc/peer-link.cjs.map +1 -0
  39. package/dist/webrtc/peer-link.js +45 -0
  40. package/dist/webrtc/peer-link.js.map +1 -0
  41. package/dist/webrtc/peer-mesh.cjs +2 -0
  42. package/dist/webrtc/peer-mesh.cjs.map +1 -0
  43. package/dist/webrtc/peer-mesh.js +155 -0
  44. package/dist/webrtc/peer-mesh.js.map +1 -0
  45. package/package.json +1 -1
package/dist/imaging.d.ts CHANGED
@@ -12,528 +12,645 @@
12
12
  */
13
13
  export declare function bestSupportedType(preferences?: readonly ImageType[]): Promise<ImageType>;
14
14
 
15
- /** What {@link compressToTarget} produced. */
16
- export declare interface CompressedImage extends ProcessedImage {
17
- /** The quality it settled on. */
18
- readonly quality: number;
19
- /** How many encodes it took. */
20
- readonly attempts: number;
15
+ /** An encoded frame, and where in the video it came from. */
16
+ export declare interface CapturedFrame extends ProcessedImage {
21
17
  /**
22
- * Whether the result actually fits the budget.
18
+ * The instant actually captured, in milliseconds.
23
19
  *
24
- * `false` means the image could not reach it even at `minQuality`
25
- * reported rather than thrown, because a 2.1 MB result against a 2 MB
26
- * budget is usually still worth uploading, and that call is the
27
- * caller's.
20
+ * Not necessarily the `atMs` asked for: a seek lands on a frame boundary,
21
+ * so a request for 12 500 ms in a 30 fps video captures 12 500 at best and
22
+ * 12 466,67 in practice. Report this one, not the request.
28
23
  */
29
- readonly withinBudget: boolean;
30
- }
31
-
32
- /** Options for {@link compressToTarget}. */
33
- export declare interface CompressOptions extends ResizeOptions {
34
- /** Byte budget the result must fit in. */
35
- readonly maxBytes: number;
36
- /** Lowest quality worth producing. Defaults to `0.4`. */
37
- readonly minQuality?: number;
38
- /** Highest quality to start from. Defaults to `0.92`. */
39
- readonly maxQuality?: number;
40
- /** Search steps. Defaults to `6`, which resolves quality to ~1%. */
41
- readonly steps?: number;
24
+ readonly atMs: number;
25
+ /**
26
+ * Whether a newly presented frame was observed before the pixels were read.
27
+ *
28
+ * `true` only when `requestVideoFrameCallback` reported a frame going to
29
+ * the compositor. Measured in Chromium, 2026-09-04: that callback fires
30
+ * while a video **plays** and does **not** fire for a seek on a paused
31
+ * element so capturing from a playing video (a screen-recording print)
32
+ * can be confirmed, and **capturing at an `atMs` reports `false`**, having
33
+ * settled on `seeked` plus two animation frames instead.
34
+ *
35
+ * So `false` is the normal result for a seek, not a warning. It says the
36
+ * capture is best effort by the standard of what browsers expose, and
37
+ * treating it as a failure would reject the majority of correct captures.
38
+ */
39
+ readonly confirmed: boolean;
42
40
  }
43
41
 
44
42
  /**
45
- * Compress an image until it fits a byte budget.
43
+ * Capture a frame from a video as encoded image bytes.
46
44
  *
47
- * @example
45
+ * Without `atMs` it reads the frame on screen, which is what a screen or camera
46
+ * recording wants — a print of what is being recorded. With `atMs` it seeks,
47
+ * waits for that instant's frame to be presented, captures, and puts the player
48
+ * back.
49
+ *
50
+ * @example Print of a screen recording in progress
48
51
  * ```ts
49
- * const upload = await compressToTarget(file, {
50
- * maxBytes: 2 * 1024 * 1024,
51
- * width: 2000,
52
+ * const shot = await captureFrame(videoRef.current!, {
52
53
  * type: "image/webp",
54
+ * quality: 0.9,
53
55
  * });
54
- *
55
- * if (!upload.withinBudget) {
56
- * console.warn(`still ${upload.bytes} bytes at quality ${upload.quality}`);
57
- * }
56
+ * await shareOrDownloadBlob(shot.blob, "print.webp");
58
57
  * ```
59
58
  *
60
- * Resizing first is what usually does the work: halving the long edge
61
- * removes three quarters of the pixels, which no quality setting matches.
62
- * Pass `width`/`height` when the source is a full-resolution photo.
59
+ * @example A poster from ten seconds in, scaled down
60
+ * ```ts
61
+ * const poster = await captureFrame(video, { atMs: 10_000, width: 640 });
62
+ * setPosterUrl(URL.createObjectURL(poster.blob));
63
+ * console.log(`landed on ${poster.atMs}ms`);
64
+ * ```
63
65
  *
64
- * @param source Anything decodable.
65
- * @param options Byte budget plus the usual resize and format options.
66
- * @returns The best result found, and whether it fits.
67
- * @throws {@link ImageDecodeError} when the source cannot be decoded.
68
- */
69
- export declare function compressToTarget(source: ImageSource, options: CompressOptions): Promise<CompressedImage>;
70
-
71
- /**
72
- * Create a drawing surface, preferring `OffscreenCanvas`.
73
- *
74
- * `OffscreenCanvas` works inside a worker, which is where a PWA wants this
75
- * running: resizing a 12-megapixel photo on the main thread blocks the UI
76
- * for tens of milliseconds per image.
77
- *
78
- * @param width Surface width in pixels.
79
- * @param height Surface height in pixels.
80
- * @returns The surface.
81
- * @throws {@link ImagingUnavailableError} when neither kind exists — a
82
- * server render, or a test environment without a canvas.
83
- */
84
- export declare function createSurface(width: number, height: number): Surface;
85
-
86
- /**
87
- * Produce several sizes from a single decode.
88
- *
89
- * @example
90
- * ```ts
91
- * const [thumb, card] = await createThumbnails(file, [
92
- * { name: "thumb", size: 96 },
93
- * { name: "card", size: 480 },
94
- * ]);
95
- * ```
96
- *
97
- * Sizes are the **longest edge**, and the aspect ratio is kept, so a
98
- * portrait and a landscape photo both fit the same grid cell without a
99
- * separate calculation per orientation.
100
- *
101
- * @param source Anything decodable.
102
- * @param specs The sizes to produce.
103
- * @param options Shared format and quality.
104
- * @returns One result per spec, in the order requested.
105
- * @throws {@link ImageDecodeError} when the source cannot be decoded.
106
- */
107
- export declare function createThumbnails(source: ImageSource, specs: readonly ThumbnailSpec[], options?: EncodeOptions): Promise<Thumbnail[]>;
108
-
109
- /**
110
- * Crop a rectangle out of an image, in source pixels.
111
- *
112
- * The rectangle is clamped to the image, so a crop dragged past the edge
113
- * produces a smaller result instead of transparent padding.
114
- *
115
- * @example
116
- * ```ts
117
- * const badge = await cropImage(file, { x: 120, y: 80, width: 400, height: 400 });
118
- * ```
119
- *
120
- * @param source Anything decodable.
121
- * @param rect The region to keep.
122
- * @param options Format and quality.
123
- * @returns The encoded crop.
124
- * @throws {@link ImageDecodeError} when the source cannot be decoded.
125
- */
126
- export declare function cropImage(source: ImageSource, rect: CropRect, options?: EncodeOptions): Promise<ProcessedImage>;
127
-
128
- /** A rectangle in source pixels. */
129
- export declare interface CropRect {
130
- readonly x: number;
131
- readonly y: number;
132
- readonly width: number;
133
- readonly height: number;
134
- }
135
-
136
- /** A decoded image, ready to draw. */
137
- export declare interface DecodedImage {
138
- /** The pixels. */
139
- readonly bitmap: ImageBitmap;
140
- /** Width in pixels, after orientation was applied. */
141
- readonly width: number;
142
- /** Height in pixels, after orientation was applied. */
143
- readonly height: number;
144
- }
145
-
146
- /**
147
- * Decode any supported source into a bitmap, oriented as the photographer
148
- * held the camera.
149
- *
150
- * @example
151
- * ```ts
152
- * const { bitmap, width, height } = await decodeImage(file);
153
- * ```
154
- *
155
- * @param source A `Blob`/`File`, URL string, `ImageBitmap`, `ImageData`,
156
- * `HTMLImageElement`, or a canvas.
157
- * @returns The decoded pixels and their dimensions.
158
- * @throws {@link ImageDecodeError} when the bytes are not a decodable image,
159
- * or the URL cannot be fetched.
160
- */
161
- export declare function decodeImage(source: ImageSource): Promise<DecodedImage>;
162
-
163
- /** Background used when a format cannot carry transparency. */
164
- export declare const DEFAULT_BACKGROUND = "#ffffff";
165
-
166
- /** Search steps when the caller does not choose. */
167
- export declare const DEFAULT_COMPRESS_STEPS = 6;
168
-
169
- /** Highest quality to start from by default. */
170
- export declare const DEFAULT_MAX_QUALITY = 0.92;
171
-
172
- /** Lowest quality worth producing by default. */
173
- export declare const DEFAULT_MIN_QUALITY = 0.4;
174
-
175
- /** Quality used when the caller does not choose. */
176
- export declare const DEFAULT_QUALITY = 0.85;
177
-
178
- /** Format used when the caller does not choose. */
179
- export declare const DEFAULT_TYPE: ImageType;
180
-
181
- /**
182
- * Draw a bitmap into a surface with high-quality filtering.
183
- *
184
- * @param bitmap The source pixels.
185
- * @param target Destination surface.
186
- * @param box Where to draw inside the destination.
187
- * @param background Fill painted before drawing.
188
- *
189
- * @tempest-limits param-count — source, destination, destination geometry, and an
190
- * optional background: the same four things `CanvasRenderingContext2D.drawImage`
191
- * takes, in the same order. Public surface, and a wrapper over a browser primitive
192
- * reads best when it keeps that primitive's shape.
193
- */
194
- export declare function drawScaled(bitmap: ImageBitmap, target: Surface, box: {
195
- x: number;
196
- y: number;
197
- width: number;
198
- height: number;
199
- }, background?: string): void;
200
-
201
- /**
202
- * Encode a surface into image bytes.
203
- *
204
- * @param surface The canvas to encode.
205
- * @param options Format and quality.
206
- * @returns The blob plus the dimensions and the type actually produced.
207
- * @throws {@link ImageEncodeError} when the canvas produces nothing.
208
- */
209
- export declare function encodeImage(surface: Surface, options?: EncodeOptions): Promise<ProcessedImage>;
210
-
211
- /** Options for {@link encodeImage}. */
212
- export declare interface EncodeOptions {
213
- /** Output format. Defaults to `image/jpeg`. */
214
- readonly type?: ImageType;
215
- /** Quality for lossy formats, `0`-`1`. Defaults to `0.85`. */
216
- readonly quality?: number;
217
- }
218
-
219
- /**
220
- * Mirror an image horizontally, vertically, or both.
221
- *
222
- * @example
223
- * ```ts
224
- * const selfie = await flipImage(capture, { horizontal: true });
225
- * ```
226
- *
227
- * @param source Anything decodable.
228
- * @param axes Which axes to mirror.
229
- * @param options Format and quality.
230
- * @returns The flipped image.
231
- * @throws {@link ImageDecodeError} when the source cannot be decoded.
232
- */
233
- export declare function flipImage(source: ImageSource, axes: {
234
- horizontal?: boolean;
235
- vertical?: boolean;
236
- }, options?: EncodeOptions): Promise<ProcessedImage>;
237
-
238
- /**
239
- * Get a 2-D context configured for image work.
240
- *
241
- * `imageSmoothingQuality = "high"` is the setting that makes a steep
242
- * downscale average its source pixels instead of sampling them sparsely.
243
- *
244
- * @param surface The surface to draw on.
245
- * @param background Optional fill painted before anything else.
246
- * @returns The context.
247
- * @throws {@link ImagingUnavailableError} when the context cannot be created.
248
- */
249
- export declare function getContext(surface: Surface, background?: string): SurfaceContext;
250
-
251
- /** The source could not be decoded into pixels. */
252
- export declare class ImageDecodeError extends ImagingError {
253
- constructor(message: string, options?: ErrorOptions);
254
- }
255
-
256
- /** The canvas could not produce encoded bytes. */
257
- export declare class ImageEncodeError extends ImagingError {
258
- constructor(message: string, options?: ErrorOptions);
259
- }
260
-
261
- /** What a file holds, without decoding all of it. */
262
- export declare interface ImageInfo {
263
- readonly width: number;
264
- readonly height: number;
265
- /** MIME type as reported by the blob. */
266
- readonly type: string;
267
- /** Size in bytes. */
268
- readonly bytes: number;
269
- /** `width / height`. */
270
- readonly aspectRatio: number;
271
- }
272
-
273
- /** Lifecycle of a processing call. */
274
- export declare type ImageProcessingStatus = "idle" | "working" | "done" | "error";
275
-
276
- /**
277
- * Types for browser-side image processing.
278
- */
279
- /** Anything the module can decode. */
280
- export declare type ImageSource = Blob | File | ImageBitmap | ImageData | HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | string;
281
-
282
- /** Encodable image formats. */
283
- export declare type ImageType = "image/jpeg" | "image/png" | "image/webp" | "image/avif";
284
-
285
- /**
286
- * Errors thrown by the imaging module.
287
- *
288
- * `name` is a literal string on every subclass: minifiers rename classes,
289
- * and a derived `new.target.name` ships as `error.name === "t"`.
290
- */
291
- /** Base class for every error this module throws. */
292
- export declare class ImagingError extends Error {
293
- constructor(message: string, options?: ErrorOptions);
294
- }
295
-
296
- /** The environment has no canvas to draw on. */
297
- export declare class ImagingUnavailableError extends ImagingError {
298
- constructor(message: string, options?: ErrorOptions);
299
- }
300
-
301
- /** The result of an operation that produced an encoded image. */
302
- export declare interface ProcessedImage {
303
- /** The encoded bytes. */
304
- readonly blob: Blob;
305
- /** Output width. */
306
- readonly width: number;
307
- /** Output height. */
308
- readonly height: number;
309
- /** Output format actually produced — not necessarily the one asked for. */
310
- readonly type: string;
311
- /** Size in bytes. */
312
- readonly bytes: number;
313
- }
314
-
315
- /**
316
- * Read an image's dimensions, type and size.
317
- *
318
- * Decodes to measure, which is the only reliable way in a browser — there
319
- * is no header parser here, and guessing dimensions from a MIME type is not
320
- * a thing. Cheap enough for a preview, not for a thousand files in a loop.
321
- *
322
- * @example
323
- * ```ts
324
- * const info = await readImageInfo(file);
325
- * if (info.bytes > 5_000_000) {
326
- * // ask before uploading
327
- * }
328
- * ```
329
- *
330
- * @param blob The image file.
331
- * @returns Its dimensions, MIME type, byte size and aspect ratio.
332
- * @throws {@link ImageDecodeError} when the blob is not a decodable image.
66
+ * @param video The element to read. It needs data — the capture waits for
67
+ * `loadeddata` when the element has none yet.
68
+ * @param options Instant, output box, format, and how long to wait.
69
+ * @returns The encoded frame, the instant it actually came from, and whether a
70
+ * presented frame confirmed that instant.
71
+ * @throws {@link FrameSeekError} when the video has no data or no timeline in
72
+ * time, or the seek did not land.
73
+ * @throws {@link ImageDecodeError} when the pixels cannot be read — a
74
+ * cross-origin video without `crossOrigin` is the common one.
75
+ * @throws {@link ImageEncodeError} when the canvas produces no bytes.
333
76
  */
334
- export declare function readImageInfo(blob: Blob): Promise<ImageInfo>;
77
+ export declare function captureFrame(video: HTMLVideoElement, options?: CaptureFrameOptions): Promise<CapturedFrame>;
78
+
79
+ /** Options for {@link captureFrame}. */
80
+ export declare interface CaptureFrameOptions extends ResizeOptions {
81
+ /**
82
+ * Instant to capture, in milliseconds. Left out: the frame on screen now.
83
+ *
84
+ * Clamped to the video's duration. Seeking snaps to a frame boundary, so
85
+ * the frame you get is the one **containing** this instant — the result
86
+ * reports where it actually landed in {@link CapturedFrame.atMs}.
87
+ */
88
+ readonly atMs?: number;
89
+ /**
90
+ * Put `currentTime` and playback back where they were. Default `true`.
91
+ *
92
+ * Only relevant with `atMs`: capturing the current frame moves nothing.
93
+ * Pass `false` when the capture is meant to leave the player parked on the
94
+ * frame it took.
95
+ */
96
+ readonly restore?: boolean;
97
+ /**
98
+ * How long to wait for the seek and for the frame after it. Default `3000`.
99
+ *
100
+ * Reached means {@link FrameSeekError}, never a frame from the wrong
101
+ * instant: a picture of the wrong moment is worse than an error, because
102
+ * nothing downstream can tell.
103
+ */
104
+ readonly timeoutMs?: number;
105
+ /** Abort the capture. Rejects with an `AbortError` `DOMException`. */
106
+ readonly signal?: AbortSignal;
107
+ }
108
+
109
+ /** What {@link compressToTarget} produced. */
110
+ export declare interface CompressedImage extends ProcessedImage {
111
+ /** The quality it settled on. */
112
+ readonly quality: number;
113
+ /** How many encodes it took. */
114
+ readonly attempts: number;
115
+ /**
116
+ * Whether the result actually fits the budget.
117
+ *
118
+ * `false` means the image could not reach it even at `minQuality` —
119
+ * reported rather than thrown, because a 2.1 MB result against a 2 MB
120
+ * budget is usually still worth uploading, and that call is the
121
+ * caller's.
122
+ */
123
+ readonly withinBudget: boolean;
124
+ }
125
+
126
+ /** Options for {@link compressToTarget}. */
127
+ export declare interface CompressOptions extends ResizeOptions {
128
+ /** Byte budget the result must fit in. */
129
+ readonly maxBytes: number;
130
+ /** Lowest quality worth producing. Defaults to `0.4`. */
131
+ readonly minQuality?: number;
132
+ /** Highest quality to start from. Defaults to `0.92`. */
133
+ readonly maxQuality?: number;
134
+ /** Search steps. Defaults to `6`, which resolves quality to ~1%. */
135
+ readonly steps?: number;
136
+ }
335
137
 
336
138
  /**
337
- * How a resize fits the requested box.
338
- *
339
- * - `contain`: the whole image fits inside the box; the result may be
340
- * smaller than the box in one dimension.
341
- * - `cover`: the box is filled; the overflow is cropped, centred.
342
- * - `fill`: the image is stretched to the box, changing its aspect ratio.
343
- * - `pad`: like `contain`, but the result is exactly the box, with the
344
- * remainder painted in `background`.
345
- */
346
- export declare type ResizeFit = "contain" | "cover" | "fill" | "pad";
347
-
348
- /**
349
- * Resize an image, re-encoding it.
139
+ * Compress an image until it fits a byte budget.
350
140
  *
351
141
  * @example
352
142
  * ```ts
353
- * const resized = await resizeImage(file, { width: 1600, type: "image/webp" });
354
- * console.log(resized.width, resized.bytes, resized.type);
143
+ * const upload = await compressToTarget(file, {
144
+ * maxBytes: 2 * 1024 * 1024,
145
+ * width: 2000,
146
+ * type: "image/webp",
147
+ * });
148
+ *
149
+ * if (!upload.withinBudget) {
150
+ * console.warn(`still ${upload.bytes} bytes at quality ${upload.quality}`);
151
+ * }
355
152
  * ```
356
153
  *
154
+ * Resizing first is what usually does the work: halving the long edge
155
+ * removes three quarters of the pixels, which no quality setting matches.
156
+ * Pass `width`/`height` when the source is a full-resolution photo.
157
+ *
357
158
  * @param source Anything decodable.
358
- * @param options Target box, fit, format and quality.
359
- * @returns The encoded result.
159
+ * @param options Byte budget plus the usual resize and format options.
160
+ * @returns The best result found, and whether it fits.
360
161
  * @throws {@link ImageDecodeError} when the source cannot be decoded.
361
- * @throws {@link ImageEncodeError} when the canvas produces no bytes.
162
+ */
163
+ export declare function compressToTarget(source: ImageSource, options: CompressOptions): Promise<CompressedImage>;
164
+
165
+ /**
166
+ * Create a drawing surface, preferring `OffscreenCanvas`.
167
+ *
168
+ * `OffscreenCanvas` works inside a worker, which is where a PWA wants this
169
+ * running: resizing a 12-megapixel photo on the main thread blocks the UI
170
+ * for tens of milliseconds per image.
171
+ *
172
+ * @param width Surface width in pixels.
173
+ * @param height Surface height in pixels.
174
+ * @returns The surface.
175
+ * @throws {@link ImagingUnavailableError} when neither kind exists — a
176
+ * server render, or a test environment without a canvas.
177
+ */
178
+ export declare function createSurface(width: number, height: number): Surface;
179
+
180
+ /**
181
+ * Produce several sizes from a single decode.
182
+ *
183
+ * @example
184
+ * ```ts
185
+ * const [thumb, card] = await createThumbnails(file, [
186
+ * { name: "thumb", size: 96 },
187
+ * { name: "card", size: 480 },
188
+ * ]);
189
+ * ```
190
+ *
191
+ * Sizes are the **longest edge**, and the aspect ratio is kept, so a
192
+ * portrait and a landscape photo both fit the same grid cell without a
193
+ * separate calculation per orientation.
194
+ *
195
+ * @param source Anything decodable.
196
+ * @param specs The sizes to produce.
197
+ * @param options Shared format and quality.
198
+ * @returns One result per spec, in the order requested.
199
+ * @throws {@link ImageDecodeError} when the source cannot be decoded.
200
+ */
201
+ export declare function createThumbnails(source: ImageSource, specs: readonly ThumbnailSpec[], options?: EncodeOptions): Promise<Thumbnail[]>;
202
+
203
+ /**
204
+ * Crop a rectangle out of an image, in source pixels.
205
+ *
206
+ * The rectangle is clamped to the image, so a crop dragged past the edge
207
+ * produces a smaller result instead of transparent padding.
208
+ *
209
+ * @example
210
+ * ```ts
211
+ * const badge = await cropImage(file, { x: 120, y: 80, width: 400, height: 400 });
212
+ * ```
213
+ *
214
+ * @param source Anything decodable.
215
+ * @param rect The region to keep.
216
+ * @param options Format and quality.
217
+ * @returns The encoded crop.
218
+ * @throws {@link ImageDecodeError} when the source cannot be decoded.
219
+ */
220
+ export declare function cropImage(source: ImageSource, rect: CropRect, options?: EncodeOptions): Promise<ProcessedImage>;
221
+
222
+ /** A rectangle in source pixels. */
223
+ export declare interface CropRect {
224
+ readonly x: number;
225
+ readonly y: number;
226
+ readonly width: number;
227
+ readonly height: number;
228
+ }
229
+
230
+ /** A decoded image, ready to draw. */
231
+ export declare interface DecodedImage {
232
+ /** The pixels. */
233
+ readonly bitmap: ImageBitmap;
234
+ /** Width in pixels, after orientation was applied. */
235
+ readonly width: number;
236
+ /** Height in pixels, after orientation was applied. */
237
+ readonly height: number;
238
+ }
239
+
240
+ /**
241
+ * Decode any supported source into a bitmap, oriented as the photographer
242
+ * held the camera.
243
+ *
244
+ * @example
245
+ * ```ts
246
+ * const { bitmap, width, height } = await decodeImage(file);
247
+ * ```
248
+ *
249
+ * @param source A `Blob`/`File`, URL string, `ImageBitmap`, `ImageData`,
250
+ * `HTMLImageElement`, or a canvas.
251
+ * @returns The decoded pixels and their dimensions.
252
+ * @throws {@link ImageDecodeError} when the bytes are not a decodable image,
253
+ * or the URL cannot be fetched.
362
254
  */
363
- export declare function resizeImage(source: ImageSource, options?: ResizeOptions): Promise<ProcessedImage>;
364
-
365
- /** Options for {@link resizeImage}. */
366
- export declare interface ResizeOptions extends EncodeOptions {
367
- /** Target width in pixels. */
368
- readonly width?: number;
369
- /** Target height in pixels. */
370
- readonly height?: number;
371
- /** How the image fits the box. Defaults to `contain`. */
372
- readonly fit?: ResizeFit;
373
- /** Fill colour for `pad`, and behind transparency when encoding JPEG. */
374
- readonly background?: string;
375
- /**
376
- * Never scale an image up.
377
- *
378
- * On by default: enlarging a photo adds no detail and multiplies the
379
- * bytes, which is the opposite of what a resize is usually for.
380
- */
381
- readonly withoutEnlargement?: boolean;
382
- }
255
+ export declare function decodeImage(source: ImageSource): Promise<DecodedImage>;
256
+
257
+ /** Background used when a format cannot carry transparency. */
258
+ export declare const DEFAULT_BACKGROUND = "#ffffff";
259
+
260
+ /** Search steps when the caller does not choose. */
261
+ export declare const DEFAULT_COMPRESS_STEPS = 6;
262
+
263
+ /** How long a seek and the frame after it may take, in milliseconds. */
264
+ export declare const DEFAULT_FRAME_TIMEOUT_MS = 3000;
265
+
266
+ /** Highest quality to start from by default. */
267
+ export declare const DEFAULT_MAX_QUALITY = 0.92;
268
+
269
+ /** Lowest quality worth producing by default. */
270
+ export declare const DEFAULT_MIN_QUALITY = 0.4;
271
+
272
+ /** Quality used when the caller does not choose. */
273
+ export declare const DEFAULT_QUALITY = 0.85;
274
+
275
+ /** Format used when the caller does not choose. */
276
+ export declare const DEFAULT_TYPE: ImageType;
383
277
 
384
278
  /**
385
- * Rotate an image by a multiple of 90 degrees.
279
+ * Draw a bitmap into a surface with high-quality filtering.
386
280
  *
387
- * Restricted to right angles on purpose: an arbitrary angle needs a
388
- * decision about the corners (crop, pad, or grow the canvas) that belongs
389
- * to the caller's design, not to a utility default.
281
+ * @param bitmap The source pixels.
282
+ * @param target Destination surface.
283
+ * @param box Where to draw inside the destination.
284
+ * @param background Fill painted before drawing.
390
285
  *
391
- * @example
392
- * ```ts
393
- * const upright = await rotateImage(file, 90);
394
- * ```
286
+ * @tempest-limits param-count — source, destination, destination geometry, and an
287
+ * optional background: the same four things `CanvasRenderingContext2D.drawImage`
288
+ * takes, in the same order. Public surface, and a wrapper over a browser primitive
289
+ * reads best when it keeps that primitive's shape.
290
+ */
291
+ export declare function drawScaled(bitmap: ImageBitmap, target: Surface, box: {
292
+ x: number;
293
+ y: number;
294
+ width: number;
295
+ height: number;
296
+ }, background?: string): void;
297
+
298
+ /**
299
+ * Encode a surface into image bytes.
395
300
  *
396
- * @param source Anything decodable.
397
- * @param degrees `90`, `180`, `270` — or any multiple, normalised.
301
+ * @param surface The canvas to encode.
398
302
  * @param options Format and quality.
399
- * @returns The rotated image.
400
- * @throws {@link ImageDecodeError} when the source cannot be decoded.
401
- * @throws {@link RangeError} when the angle is not a multiple of 90.
402
- */
403
- export declare function rotateImage(source: ImageSource, degrees: number, options?: EncodeOptions): Promise<ProcessedImage>;
404
-
405
- /**
406
- * Whether this browser can actually encode a format.
407
- *
408
- * Asks for a 1x1 image in that type and checks what came back, because
409
- * that is the only answer that counts: a browser that "supports" WebP for
410
- * display may still not encode it.
411
- *
412
- * @example
413
- * ```ts
414
- * const type = (await supportsImageType("image/webp")) ? "image/webp" : "image/jpeg";
415
- * const resized = await resizeImage(file, { width: 1200, type });
416
- * ```
417
- *
418
- * @param type The format to test.
419
- * @returns Whether encoding produces that type. Cached per type.
420
- */
421
- export declare function supportsImageType(type: ImageType): Promise<boolean>;
422
-
423
- /**
424
- * The drawing surface, and the one thing everyone gets wrong on it.
425
- *
426
- * **JPEG has no alpha.** Encoding a transparent PNG as JPEG paints the
427
- * transparent pixels black. Filling the surface first is the difference
428
- * between a photo on a white background and one with a black hole in it.
429
- *
430
- * What is *not* here is worth recording. The received wisdom for downscaling
431
- * on a canvas is to halve repeatedly, because a single `drawImage` into a
432
- * much smaller box was said to alias. That was implemented here, and then
433
- * measured: on a 512 px checkerboard reduced to 32 px, the stepwise result
434
- * and the single high-quality draw were **pixel-identical** (standard
435
- * deviation 0.0 on both) in Chromium and Firefox — while stepwise cost
436
- * **39.19 ms against 0.13 ms** on a 4000x3000 photo, 300 times more, and
437
- * allocated three intermediate canvases on a device that may not have the
438
- * memory. Modern engines honour `imageSmoothingQuality = "high"`, which is
439
- * what this module sets. The halving was deleted rather than kept "just in
440
- * case": unmeasurable benefit at 300x the cost is not insurance, it is
441
- * ballast.
442
- */
443
- /** A canvas this module can draw on, on the main thread or in a worker. */
444
- export declare type Surface = OffscreenCanvas | HTMLCanvasElement;
445
-
446
- /** A 2-D context from either surface kind. */
447
- export declare type SurfaceContext = OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D;
448
-
449
- /** A produced thumbnail. */
450
- export declare interface Thumbnail extends ProcessedImage {
451
- readonly name: string;
452
- }
453
-
454
- /** One requested size. */
455
- export declare interface ThumbnailSpec {
456
- /** Name to find it by in the result. */
457
- readonly name: string;
458
- /** Longest edge in pixels. */
459
- readonly size: number;
460
- /** Format and quality, overriding the shared options. */
461
- readonly encode?: EncodeOptions;
462
- }
463
-
464
- /**
465
- * The browser cannot encode the requested format.
466
- *
467
- * Worth its own class because the failure is otherwise silent: asking a
468
- * canvas for an unsupported type does not throw — it hands back a PNG,
469
- * and an app that trusted the request ships 4 MB where it expected 300 KB.
470
- */
471
- export declare class UnsupportedImageTypeError extends ImagingError {
472
- constructor(message: string, options?: ErrorOptions);
473
- }
474
-
475
- /**
476
- * Hold an object URL for a blob, and revoke it when it is replaced.
477
- *
478
- * @example
479
- * ```tsx
480
- * function Preview({ file }: { file: File | null }) {
481
- * const { url } = useImagePreview(file);
482
- * return url === null ? null : <img src={url} alt="" />;
483
- * }
484
- * ```
485
- *
486
- * @param source The blob to preview, or `null`.
487
- * @returns The object URL, valid until the source changes or the component
488
- * unmounts.
489
- */
490
- export declare function useImagePreview(source: Blob | null | undefined): UseImagePreviewResult;
491
-
492
- /** What {@link useImagePreview} returns. */
493
- export declare interface UseImagePreviewResult {
494
- /** Object URL for the current source, or `null`. */
495
- readonly url: string | null;
496
- }
497
-
498
- /**
499
- * Run image operations with status tracking, safe against unmount.
500
- *
501
- * @example
502
- * ```tsx
503
- * function Upload() {
504
- * const { compress, isWorking, result } = useImageProcessing();
505
- *
506
- * async function onPick(file: File) {
507
- * const ready = await compress(file, { maxBytes: 1_000_000, width: 1600 });
508
- * await fetch("/api/photos", { method: "POST", body: ready.blob });
509
- * }
510
- *
511
- * return <input type="file" disabled={isWorking} onChange={(e) => onPick(e.target.files![0]!)} />;
512
- * }
513
- * ```
514
- *
515
- * The returned promises still reject on failure, so a caller can `try`
516
- * around them; `status` and `error` exist for rendering, not for swallowing
517
- * the failure.
518
- *
519
- * @returns The operations plus their state.
303
+ * @returns The blob plus the dimensions and the type actually produced.
304
+ * @throws {@link ImageEncodeError} when the canvas produces nothing.
305
+ */
306
+ export declare function encodeImage(surface: Surface, options?: EncodeOptions): Promise<ProcessedImage>;
307
+
308
+ /** Options for {@link encodeImage}. */
309
+ export declare interface EncodeOptions {
310
+ /** Output format. Defaults to `image/jpeg`. */
311
+ readonly type?: ImageType;
312
+ /** Quality for lossy formats, `0`-`1`. Defaults to `0.85`. */
313
+ readonly quality?: number;
314
+ }
315
+
316
+ /**
317
+ * Mirror an image horizontally, vertically, or both.
318
+ *
319
+ * @example
320
+ * ```ts
321
+ * const selfie = await flipImage(capture, { horizontal: true });
322
+ * ```
323
+ *
324
+ * @param source Anything decodable.
325
+ * @param axes Which axes to mirror.
326
+ * @param options Format and quality.
327
+ * @returns The flipped image.
328
+ * @throws {@link ImageDecodeError} when the source cannot be decoded.
520
329
  */
521
- export declare function useImageProcessing(): UseImageProcessingResult;
522
-
523
- /** What {@link useImageProcessing} returns. */
524
- export declare interface UseImageProcessingResult {
525
- /** Resize (and re-encode) an image. */
526
- readonly resize: (source: ImageSource, options?: ResizeOptions) => Promise<ProcessedImage>;
527
- /** Compress an image into a byte budget. */
528
- readonly compress: (source: ImageSource, options: CompressOptions) => Promise<CompressedImage>;
529
- /** The most recent result. */
530
- readonly result: ProcessedImage | null;
531
- /** Where the last call is. */
532
- readonly status: ImageProcessingStatus;
533
- /** Why the last call failed. */
534
- readonly error: Error | null;
535
- /** Whether a call is in flight. */
536
- readonly isWorking: boolean;
537
- }
538
-
539
- export { }
330
+ export declare function flipImage(source: ImageSource, axes: {
331
+ horizontal?: boolean;
332
+ vertical?: boolean;
333
+ }, options?: EncodeOptions): Promise<ProcessedImage>;
334
+
335
+ /**
336
+ * The frame asked for never arrived.
337
+ *
338
+ * Its own class because the alternative is the failure this module refuses to
339
+ * produce: a frame from the wrong instant. `seeked` firing does not mean the
340
+ * frame for the new position is composited and readable, so a capture that
341
+ * gave up waiting has to say so — a thumbnail of the neighbouring frame looks
342
+ * exactly like a correct one, and nothing downstream can tell.
343
+ */
344
+ export declare class FrameSeekError extends ImagingError {
345
+ constructor(message: string, options?: ErrorOptions);
346
+ }
347
+
348
+ /**
349
+ * Get a 2-D context configured for image work.
350
+ *
351
+ * `imageSmoothingQuality = "high"` is the setting that makes a steep
352
+ * downscale average its source pixels instead of sampling them sparsely.
353
+ *
354
+ * @param surface The surface to draw on.
355
+ * @param background Optional fill painted before anything else.
356
+ * @returns The context.
357
+ * @throws {@link ImagingUnavailableError} when the context cannot be created.
358
+ */
359
+ export declare function getContext(surface: Surface, background?: string): SurfaceContext;
360
+
361
+ /** The source could not be decoded into pixels. */
362
+ export declare class ImageDecodeError extends ImagingError {
363
+ constructor(message: string, options?: ErrorOptions);
364
+ }
365
+
366
+ /** The canvas could not produce encoded bytes. */
367
+ export declare class ImageEncodeError extends ImagingError {
368
+ constructor(message: string, options?: ErrorOptions);
369
+ }
370
+
371
+ /** What a file holds, without decoding all of it. */
372
+ export declare interface ImageInfo {
373
+ readonly width: number;
374
+ readonly height: number;
375
+ /** MIME type as reported by the blob. */
376
+ readonly type: string;
377
+ /** Size in bytes. */
378
+ readonly bytes: number;
379
+ /** `width / height`. */
380
+ readonly aspectRatio: number;
381
+ }
382
+
383
+ /** Lifecycle of a processing call. */
384
+ export declare type ImageProcessingStatus = "idle" | "working" | "done" | "error";
385
+
386
+ /**
387
+ * Types for browser-side image processing.
388
+ */
389
+ /**
390
+ * Anything the module can decode.
391
+ *
392
+ * `HTMLVideoElement` reads the frame the element is **currently showing** —
393
+ * `createImageBitmap` accepts it as a `CanvasImageSource`, so nothing here
394
+ * special-cases it. Reading a chosen instant instead of the current one needs
395
+ * the seek to be confirmed first, which is what `captureFrame` is for.
396
+ */
397
+ export declare type ImageSource = Blob | File | ImageBitmap | ImageData | HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | string;
398
+
399
+ /** Encodable image formats. */
400
+ export declare type ImageType = "image/jpeg" | "image/png" | "image/webp" | "image/avif";
401
+
402
+ /**
403
+ * Errors thrown by the imaging module.
404
+ *
405
+ * `name` is a literal string on every subclass: minifiers rename classes,
406
+ * and a derived `new.target.name` ships as `error.name === "t"`.
407
+ */
408
+ /** Base class for every error this module throws. */
409
+ export declare class ImagingError extends Error {
410
+ constructor(message: string, options?: ErrorOptions);
411
+ }
412
+
413
+ /** The environment has no canvas to draw on. */
414
+ export declare class ImagingUnavailableError extends ImagingError {
415
+ constructor(message: string, options?: ErrorOptions);
416
+ }
417
+
418
+ /** The result of an operation that produced an encoded image. */
419
+ export declare interface ProcessedImage {
420
+ /** The encoded bytes. */
421
+ readonly blob: Blob;
422
+ /** Output width. */
423
+ readonly width: number;
424
+ /** Output height. */
425
+ readonly height: number;
426
+ /** Output format actually produced — not necessarily the one asked for. */
427
+ readonly type: string;
428
+ /** Size in bytes. */
429
+ readonly bytes: number;
430
+ }
431
+
432
+ /**
433
+ * Read an image's dimensions, type and size.
434
+ *
435
+ * Decodes to measure, which is the only reliable way in a browser — there
436
+ * is no header parser here, and guessing dimensions from a MIME type is not
437
+ * a thing. Cheap enough for a preview, not for a thousand files in a loop.
438
+ *
439
+ * @example
440
+ * ```ts
441
+ * const info = await readImageInfo(file);
442
+ * if (info.bytes > 5_000_000) {
443
+ * // ask before uploading
444
+ * }
445
+ * ```
446
+ *
447
+ * @param blob The image file.
448
+ * @returns Its dimensions, MIME type, byte size and aspect ratio.
449
+ * @throws {@link ImageDecodeError} when the blob is not a decodable image.
450
+ */
451
+ export declare function readImageInfo(blob: Blob): Promise<ImageInfo>;
452
+
453
+ /**
454
+ * How a resize fits the requested box.
455
+ *
456
+ * - `contain`: the whole image fits inside the box; the result may be
457
+ * smaller than the box in one dimension.
458
+ * - `cover`: the box is filled; the overflow is cropped, centred.
459
+ * - `fill`: the image is stretched to the box, changing its aspect ratio.
460
+ * - `pad`: like `contain`, but the result is exactly the box, with the
461
+ * remainder painted in `background`.
462
+ */
463
+ export declare type ResizeFit = "contain" | "cover" | "fill" | "pad";
464
+
465
+ /**
466
+ * Resize an image, re-encoding it.
467
+ *
468
+ * @example
469
+ * ```ts
470
+ * const resized = await resizeImage(file, { width: 1600, type: "image/webp" });
471
+ * console.log(resized.width, resized.bytes, resized.type);
472
+ * ```
473
+ *
474
+ * @param source Anything decodable.
475
+ * @param options Target box, fit, format and quality.
476
+ * @returns The encoded result.
477
+ * @throws {@link ImageDecodeError} when the source cannot be decoded.
478
+ * @throws {@link ImageEncodeError} when the canvas produces no bytes.
479
+ */
480
+ export declare function resizeImage(source: ImageSource, options?: ResizeOptions): Promise<ProcessedImage>;
481
+
482
+ /** Options for {@link resizeImage}. */
483
+ export declare interface ResizeOptions extends EncodeOptions {
484
+ /** Target width in pixels. */
485
+ readonly width?: number;
486
+ /** Target height in pixels. */
487
+ readonly height?: number;
488
+ /** How the image fits the box. Defaults to `contain`. */
489
+ readonly fit?: ResizeFit;
490
+ /** Fill colour for `pad`, and behind transparency when encoding JPEG. */
491
+ readonly background?: string;
492
+ /**
493
+ * Never scale an image up.
494
+ *
495
+ * On by default: enlarging a photo adds no detail and multiplies the
496
+ * bytes, which is the opposite of what a resize is usually for.
497
+ */
498
+ readonly withoutEnlargement?: boolean;
499
+ }
500
+
501
+ /**
502
+ * Rotate an image by a multiple of 90 degrees.
503
+ *
504
+ * Restricted to right angles on purpose: an arbitrary angle needs a
505
+ * decision about the corners (crop, pad, or grow the canvas) that belongs
506
+ * to the caller's design, not to a utility default.
507
+ *
508
+ * @example
509
+ * ```ts
510
+ * const upright = await rotateImage(file, 90);
511
+ * ```
512
+ *
513
+ * @param source Anything decodable.
514
+ * @param degrees `90`, `180`, `270` — or any multiple, normalised.
515
+ * @param options Format and quality.
516
+ * @returns The rotated image.
517
+ * @throws {@link ImageDecodeError} when the source cannot be decoded.
518
+ * @throws {@link RangeError} when the angle is not a multiple of 90.
519
+ */
520
+ export declare function rotateImage(source: ImageSource, degrees: number, options?: EncodeOptions): Promise<ProcessedImage>;
521
+
522
+ /**
523
+ * Whether this browser can actually encode a format.
524
+ *
525
+ * Asks for a 1x1 image in that type and checks what came back, because
526
+ * that is the only answer that counts: a browser that "supports" WebP for
527
+ * display may still not encode it.
528
+ *
529
+ * @example
530
+ * ```ts
531
+ * const type = (await supportsImageType("image/webp")) ? "image/webp" : "image/jpeg";
532
+ * const resized = await resizeImage(file, { width: 1200, type });
533
+ * ```
534
+ *
535
+ * @param type The format to test.
536
+ * @returns Whether encoding produces that type. Cached per type.
537
+ */
538
+ export declare function supportsImageType(type: ImageType): Promise<boolean>;
539
+
540
+ /**
541
+ * The drawing surface, and the one thing everyone gets wrong on it.
542
+ *
543
+ * **JPEG has no alpha.** Encoding a transparent PNG as JPEG paints the
544
+ * transparent pixels black. Filling the surface first is the difference
545
+ * between a photo on a white background and one with a black hole in it.
546
+ *
547
+ * What is *not* here is worth recording. The received wisdom for downscaling
548
+ * on a canvas is to halve repeatedly, because a single `drawImage` into a
549
+ * much smaller box was said to alias. That was implemented here, and then
550
+ * measured: on a 512 px checkerboard reduced to 32 px, the stepwise result
551
+ * and the single high-quality draw were **pixel-identical** (standard
552
+ * deviation 0.0 on both) in Chromium and Firefox — while stepwise cost
553
+ * **39.19 ms against 0.13 ms** on a 4000x3000 photo, 300 times more, and
554
+ * allocated three intermediate canvases on a device that may not have the
555
+ * memory. Modern engines honour `imageSmoothingQuality = "high"`, which is
556
+ * what this module sets. The halving was deleted rather than kept "just in
557
+ * case": unmeasurable benefit at 300x the cost is not insurance, it is
558
+ * ballast.
559
+ */
560
+ /** A canvas this module can draw on, on the main thread or in a worker. */
561
+ export declare type Surface = OffscreenCanvas | HTMLCanvasElement;
562
+
563
+ /** A 2-D context from either surface kind. */
564
+ export declare type SurfaceContext = OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D;
565
+
566
+ /** A produced thumbnail. */
567
+ export declare interface Thumbnail extends ProcessedImage {
568
+ readonly name: string;
569
+ }
570
+
571
+ /** One requested size. */
572
+ export declare interface ThumbnailSpec {
573
+ /** Name to find it by in the result. */
574
+ readonly name: string;
575
+ /** Longest edge in pixels. */
576
+ readonly size: number;
577
+ /** Format and quality, overriding the shared options. */
578
+ readonly encode?: EncodeOptions;
579
+ }
580
+
581
+ /**
582
+ * The browser cannot encode the requested format.
583
+ *
584
+ * Worth its own class because the failure is otherwise silent: asking a
585
+ * canvas for an unsupported type does not throw — it hands back a PNG,
586
+ * and an app that trusted the request ships 4 MB where it expected 300 KB.
587
+ */
588
+ export declare class UnsupportedImageTypeError extends ImagingError {
589
+ constructor(message: string, options?: ErrorOptions);
590
+ }
591
+
592
+ /**
593
+ * Hold an object URL for a blob, and revoke it when it is replaced.
594
+ *
595
+ * @example
596
+ * ```tsx
597
+ * function Preview({ file }: { file: File | null }) {
598
+ * const { url } = useImagePreview(file);
599
+ * return url === null ? null : <img src={url} alt="" />;
600
+ * }
601
+ * ```
602
+ *
603
+ * @param source The blob to preview, or `null`.
604
+ * @returns The object URL, valid until the source changes or the component
605
+ * unmounts.
606
+ */
607
+ export declare function useImagePreview(source: Blob | null | undefined): UseImagePreviewResult;
608
+
609
+ /** What {@link useImagePreview} returns. */
610
+ export declare interface UseImagePreviewResult {
611
+ /** Object URL for the current source, or `null`. */
612
+ readonly url: string | null;
613
+ }
614
+
615
+ /**
616
+ * Run image operations with status tracking, safe against unmount.
617
+ *
618
+ * @example
619
+ * ```tsx
620
+ * function Upload() {
621
+ * const { compress, isWorking, result } = useImageProcessing();
622
+ *
623
+ * async function onPick(file: File) {
624
+ * const ready = await compress(file, { maxBytes: 1_000_000, width: 1600 });
625
+ * await fetch("/api/photos", { method: "POST", body: ready.blob });
626
+ * }
627
+ *
628
+ * return <input type="file" disabled={isWorking} onChange={(e) => onPick(e.target.files![0]!)} />;
629
+ * }
630
+ * ```
631
+ *
632
+ * The returned promises still reject on failure, so a caller can `try`
633
+ * around them; `status` and `error` exist for rendering, not for swallowing
634
+ * the failure.
635
+ *
636
+ * @returns The operations plus their state.
637
+ */
638
+ export declare function useImageProcessing(): UseImageProcessingResult;
639
+
640
+ /** What {@link useImageProcessing} returns. */
641
+ export declare interface UseImageProcessingResult {
642
+ /** Resize (and re-encode) an image. */
643
+ readonly resize: (source: ImageSource, options?: ResizeOptions) => Promise<ProcessedImage>;
644
+ /** Compress an image into a byte budget. */
645
+ readonly compress: (source: ImageSource, options: CompressOptions) => Promise<CompressedImage>;
646
+ /** The most recent result. */
647
+ readonly result: ProcessedImage | null;
648
+ /** Where the last call is. */
649
+ readonly status: ImageProcessingStatus;
650
+ /** Why the last call failed. */
651
+ readonly error: Error | null;
652
+ /** Whether a call is in flight. */
653
+ readonly isWorking: boolean;
654
+ }
655
+
656
+ export { }