@orion-studios/payload-studio 0.5.0-beta.65 → 0.5.0-beta.67

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.
@@ -694,6 +694,41 @@ var import_navigation = require("next/navigation");
694
694
 
695
695
  // src/shared/clientImageUploadOptimization.ts
696
696
  var MAX_DIRECT_UPLOAD_BYTES = 4e6;
697
+ var extensionForMimeType = (mimeType) => {
698
+ switch (mimeType) {
699
+ case "image/webp":
700
+ return ".webp";
701
+ case "image/png":
702
+ return ".png";
703
+ default:
704
+ return ".jpg";
705
+ }
706
+ };
707
+ var detectCanvasTransparency = (context, width, height) => {
708
+ try {
709
+ const { data } = context.getImageData(0, 0, width, height);
710
+ for (let index = 3; index < data.length; index += 4) {
711
+ if (data[index] < 255) {
712
+ return true;
713
+ }
714
+ }
715
+ } catch {
716
+ }
717
+ return false;
718
+ };
719
+ var resolveOutputMimeTypes = (sourceMime, hasTransparency) => {
720
+ const candidates = [];
721
+ if (hasTransparency) {
722
+ candidates.push("image/webp", "image/png");
723
+ } else if (sourceMime === "image/webp") {
724
+ candidates.push("image/webp", "image/jpeg");
725
+ } else if (sourceMime === "image/png") {
726
+ candidates.push("image/webp", "image/jpeg", "image/png");
727
+ } else {
728
+ candidates.push("image/jpeg", "image/webp");
729
+ }
730
+ return [...new Set(candidates)];
731
+ };
697
732
  async function optimizeImageForUpload(file) {
698
733
  if (!file.type.startsWith("image/")) {
699
734
  return file;
@@ -714,26 +749,31 @@ async function optimizeImageForUpload(file) {
714
749
  return file;
715
750
  }
716
751
  context.drawImage(image, 0, 0, canvas.width, canvas.height);
717
- const outputMime = file.type === "image/webp" ? "image/webp" : "image/jpeg";
752
+ const sourceMime = file.type.toLowerCase();
753
+ const hasTransparency = detectCanvasTransparency(context, canvas.width, canvas.height);
754
+ const outputMimes = resolveOutputMimeTypes(sourceMime, hasTransparency);
718
755
  const qualityPasses = [0.82, 0.74, 0.66, 0.58, 0.5, 0.42, 0.36, 0.3, 0.26];
719
756
  let bestFile = null;
720
- for (const quality of qualityPasses) {
721
- const blob = await new Promise((resolve) => {
722
- canvas.toBlob((value) => resolve(value), outputMime, quality);
723
- });
724
- if (!blob) {
725
- continue;
726
- }
727
- const optimizedName = file.name.replace(/\.[^/.]+$/, outputMime === "image/webp" ? ".webp" : ".jpg");
728
- const optimized = new File([blob], optimizedName, {
729
- lastModified: Date.now(),
730
- type: outputMime
731
- });
732
- if (!bestFile || optimized.size < bestFile.size) {
733
- bestFile = optimized;
734
- }
735
- if (optimized.size <= MAX_DIRECT_UPLOAD_BYTES) {
736
- break;
757
+ for (const outputMime of outputMimes) {
758
+ const passes = outputMime === "image/png" ? [void 0] : qualityPasses;
759
+ for (const quality of passes) {
760
+ const blob = await new Promise((resolve) => {
761
+ canvas.toBlob((value) => resolve(value), outputMime, quality);
762
+ });
763
+ if (!blob) {
764
+ continue;
765
+ }
766
+ const optimizedName = file.name.replace(/\.[^/.]+$/, extensionForMimeType(outputMime));
767
+ const optimized = new File([blob], optimizedName, {
768
+ lastModified: Date.now(),
769
+ type: outputMime
770
+ });
771
+ if (!bestFile || optimized.size < bestFile.size) {
772
+ bestFile = optimized;
773
+ }
774
+ if (optimized.size <= MAX_DIRECT_UPLOAD_BYTES) {
775
+ break;
776
+ }
737
777
  }
738
778
  }
739
779
  if (!bestFile) {
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  MAX_DIRECT_UPLOAD_BYTES,
4
4
  optimizeImageForUpload
5
- } from "../chunk-I3HYCS77.mjs";
5
+ } from "../chunk-ROTPP5CU.mjs";
6
6
 
7
7
  // src/admin-app/components/AdminShellClient.tsx
8
8
  import { useEffect, useState } from "react";
@@ -0,0 +1,99 @@
1
+ 'use client';
2
+
3
+ // src/shared/clientImageUploadOptimization.ts
4
+ var MAX_DIRECT_UPLOAD_BYTES = 4e6;
5
+ var extensionForMimeType = (mimeType) => {
6
+ switch (mimeType) {
7
+ case "image/webp":
8
+ return ".webp";
9
+ case "image/png":
10
+ return ".png";
11
+ default:
12
+ return ".jpg";
13
+ }
14
+ };
15
+ var detectCanvasTransparency = (context, width, height) => {
16
+ try {
17
+ const { data } = context.getImageData(0, 0, width, height);
18
+ for (let index = 3; index < data.length; index += 4) {
19
+ if (data[index] < 255) {
20
+ return true;
21
+ }
22
+ }
23
+ } catch {
24
+ }
25
+ return false;
26
+ };
27
+ var resolveOutputMimeTypes = (sourceMime, hasTransparency) => {
28
+ const candidates = [];
29
+ if (hasTransparency) {
30
+ candidates.push("image/webp", "image/png");
31
+ } else if (sourceMime === "image/webp") {
32
+ candidates.push("image/webp", "image/jpeg");
33
+ } else if (sourceMime === "image/png") {
34
+ candidates.push("image/webp", "image/jpeg", "image/png");
35
+ } else {
36
+ candidates.push("image/jpeg", "image/webp");
37
+ }
38
+ return [...new Set(candidates)];
39
+ };
40
+ async function optimizeImageForUpload(file) {
41
+ if (!file.type.startsWith("image/")) {
42
+ return file;
43
+ }
44
+ const objectURL = URL.createObjectURL(file);
45
+ try {
46
+ const image = await new Promise((resolve, reject) => {
47
+ const nextImage = new Image();
48
+ nextImage.onload = () => resolve(nextImage);
49
+ nextImage.onerror = () => reject(new Error("Could not read image for upload optimization."));
50
+ nextImage.src = objectURL;
51
+ });
52
+ const canvas = document.createElement("canvas");
53
+ canvas.width = Math.max(1, image.width);
54
+ canvas.height = Math.max(1, image.height);
55
+ const context = canvas.getContext("2d");
56
+ if (!context) {
57
+ return file;
58
+ }
59
+ context.drawImage(image, 0, 0, canvas.width, canvas.height);
60
+ const sourceMime = file.type.toLowerCase();
61
+ const hasTransparency = detectCanvasTransparency(context, canvas.width, canvas.height);
62
+ const outputMimes = resolveOutputMimeTypes(sourceMime, hasTransparency);
63
+ const qualityPasses = [0.82, 0.74, 0.66, 0.58, 0.5, 0.42, 0.36, 0.3, 0.26];
64
+ let bestFile = null;
65
+ for (const outputMime of outputMimes) {
66
+ const passes = outputMime === "image/png" ? [void 0] : qualityPasses;
67
+ for (const quality of passes) {
68
+ const blob = await new Promise((resolve) => {
69
+ canvas.toBlob((value) => resolve(value), outputMime, quality);
70
+ });
71
+ if (!blob) {
72
+ continue;
73
+ }
74
+ const optimizedName = file.name.replace(/\.[^/.]+$/, extensionForMimeType(outputMime));
75
+ const optimized = new File([blob], optimizedName, {
76
+ lastModified: Date.now(),
77
+ type: outputMime
78
+ });
79
+ if (!bestFile || optimized.size < bestFile.size) {
80
+ bestFile = optimized;
81
+ }
82
+ if (optimized.size <= MAX_DIRECT_UPLOAD_BYTES) {
83
+ break;
84
+ }
85
+ }
86
+ }
87
+ if (!bestFile) {
88
+ return file;
89
+ }
90
+ return bestFile.size < file.size ? bestFile : file;
91
+ } finally {
92
+ URL.revokeObjectURL(objectURL);
93
+ }
94
+ }
95
+
96
+ export {
97
+ MAX_DIRECT_UPLOAD_BYTES,
98
+ optimizeImageForUpload
99
+ };
package/dist/index.mjs CHANGED
@@ -4,9 +4,6 @@ import {
4
4
  import {
5
5
  admin_app_exports
6
6
  } from "./chunk-XVH5SCBD.mjs";
7
- import {
8
- blocks_exports
9
- } from "./chunk-4AOYZGIY.mjs";
10
7
  import {
11
8
  nextjs_exports
12
9
  } from "./chunk-SBJHEKTV.mjs";
@@ -16,6 +13,9 @@ import {
16
13
  import {
17
14
  studio_pages_exports
18
15
  } from "./chunk-OHAGPJBM.mjs";
16
+ import {
17
+ blocks_exports
18
+ } from "./chunk-4AOYZGIY.mjs";
19
19
  import "./chunk-SIL2J5MF.mjs";
20
20
  import "./chunk-6BWS3CLP.mjs";
21
21
  export {
@@ -389,9 +389,33 @@ h4 {
389
389
 
390
390
  .quote {
391
391
  font-size: 0.98rem;
392
+ line-height: 1.55;
392
393
  color: var(--orion-studio-ink);
393
394
  }
394
395
 
396
+ .testimonial-quote-mark {
397
+ display: block;
398
+ font-size: 2.2rem;
399
+ line-height: 0.8;
400
+ color: #e7b55a;
401
+ margin-bottom: 0.25rem;
402
+ opacity: 0.8;
403
+ }
404
+
405
+ .testimonial-quote {
406
+ display: -webkit-box;
407
+ -webkit-box-orient: vertical;
408
+ -webkit-line-clamp: 3;
409
+ overflow: hidden;
410
+ }
411
+
412
+ @supports not (-webkit-line-clamp: 1) {
413
+ .testimonial-quote {
414
+ display: block;
415
+ max-height: calc((3 * 1.55em) - 0.5px);
416
+ }
417
+ }
418
+
395
419
  .quote-author {
396
420
  margin-top: 0.75rem;
397
421
  display: flex;
@@ -560,6 +584,16 @@ h4 {
560
584
  grid-template-columns: var(--testimonials-grid-columns, repeat(auto-fit, minmax(260px, 1fr)));
561
585
  }
562
586
 
587
+ .testimonial-quote {
588
+ -webkit-line-clamp: 2;
589
+ }
590
+
591
+ @supports not (-webkit-line-clamp: 1) {
592
+ .testimonial-quote {
593
+ max-height: calc((2 * 1.55em) - 0.5px);
594
+ }
595
+ }
596
+
563
597
  .orion-stats-grid {
564
598
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
565
599
  }
@@ -265,6 +265,41 @@ var layoutToStudioDocument = (layout, title, metadata) => {
265
265
 
266
266
  // src/shared/clientImageUploadOptimization.ts
267
267
  var MAX_DIRECT_UPLOAD_BYTES = 4e6;
268
+ var extensionForMimeType = (mimeType) => {
269
+ switch (mimeType) {
270
+ case "image/webp":
271
+ return ".webp";
272
+ case "image/png":
273
+ return ".png";
274
+ default:
275
+ return ".jpg";
276
+ }
277
+ };
278
+ var detectCanvasTransparency = (context, width, height) => {
279
+ try {
280
+ const { data } = context.getImageData(0, 0, width, height);
281
+ for (let index = 3; index < data.length; index += 4) {
282
+ if (data[index] < 255) {
283
+ return true;
284
+ }
285
+ }
286
+ } catch {
287
+ }
288
+ return false;
289
+ };
290
+ var resolveOutputMimeTypes = (sourceMime, hasTransparency) => {
291
+ const candidates = [];
292
+ if (hasTransparency) {
293
+ candidates.push("image/webp", "image/png");
294
+ } else if (sourceMime === "image/webp") {
295
+ candidates.push("image/webp", "image/jpeg");
296
+ } else if (sourceMime === "image/png") {
297
+ candidates.push("image/webp", "image/jpeg", "image/png");
298
+ } else {
299
+ candidates.push("image/jpeg", "image/webp");
300
+ }
301
+ return [...new Set(candidates)];
302
+ };
268
303
  async function optimizeImageForUpload(file) {
269
304
  if (!file.type.startsWith("image/")) {
270
305
  return file;
@@ -285,26 +320,31 @@ async function optimizeImageForUpload(file) {
285
320
  return file;
286
321
  }
287
322
  context.drawImage(image, 0, 0, canvas.width, canvas.height);
288
- const outputMime = file.type === "image/webp" ? "image/webp" : "image/jpeg";
323
+ const sourceMime = file.type.toLowerCase();
324
+ const hasTransparency = detectCanvasTransparency(context, canvas.width, canvas.height);
325
+ const outputMimes = resolveOutputMimeTypes(sourceMime, hasTransparency);
289
326
  const qualityPasses = [0.82, 0.74, 0.66, 0.58, 0.5, 0.42, 0.36, 0.3, 0.26];
290
327
  let bestFile = null;
291
- for (const quality of qualityPasses) {
292
- const blob = await new Promise((resolve) => {
293
- canvas.toBlob((value) => resolve(value), outputMime, quality);
294
- });
295
- if (!blob) {
296
- continue;
297
- }
298
- const optimizedName = file.name.replace(/\.[^/.]+$/, outputMime === "image/webp" ? ".webp" : ".jpg");
299
- const optimized = new File([blob], optimizedName, {
300
- lastModified: Date.now(),
301
- type: outputMime
302
- });
303
- if (!bestFile || optimized.size < bestFile.size) {
304
- bestFile = optimized;
305
- }
306
- if (optimized.size <= MAX_DIRECT_UPLOAD_BYTES) {
307
- break;
328
+ for (const outputMime of outputMimes) {
329
+ const passes = outputMime === "image/png" ? [void 0] : qualityPasses;
330
+ for (const quality of passes) {
331
+ const blob = await new Promise((resolve) => {
332
+ canvas.toBlob((value) => resolve(value), outputMime, quality);
333
+ });
334
+ if (!blob) {
335
+ continue;
336
+ }
337
+ const optimizedName = file.name.replace(/\.[^/.]+$/, extensionForMimeType(outputMime));
338
+ const optimized = new File([blob], optimizedName, {
339
+ lastModified: Date.now(),
340
+ type: outputMime
341
+ });
342
+ if (!bestFile || optimized.size < bestFile.size) {
343
+ bestFile = optimized;
344
+ }
345
+ if (optimized.size <= MAX_DIRECT_UPLOAD_BYTES) {
346
+ break;
347
+ }
308
348
  }
309
349
  }
310
350
  if (!bestFile) {
@@ -3130,20 +3170,17 @@ function BuilderPageEditor({ initialDoc, pageID }) {
3130
3170
  },
3131
3171
  style: isItemSelected ? { outline: "2px solid rgba(15, 125, 82, 0.55)", outlineOffset: 3 } : void 0,
3132
3172
  children: [
3133
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "quote", children: [
3134
- '"',
3135
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3136
- InlineText,
3137
- {
3138
- as: "span",
3139
- multiline: true,
3140
- onCommit: (value) => updateArrayItemField(index, "items", itemIndex, "quote", value),
3141
- placeholder: "Customer quote",
3142
- value: normalizeText(item?.quote)
3143
- }
3144
- ),
3145
- '"'
3146
- ] }),
3173
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "testimonial-quote-mark", "aria-hidden": "true", children: "\u201C" }),
3174
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "quote testimonial-quote", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3175
+ InlineText,
3176
+ {
3177
+ as: "span",
3178
+ multiline: true,
3179
+ onCommit: (value) => updateArrayItemField(index, "items", itemIndex, "quote", value),
3180
+ placeholder: "Customer quote",
3181
+ value: normalizeText(item?.quote)
3182
+ }
3183
+ ) }),
3147
3184
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "quote-author", children: [
3148
3185
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "quote-avatar", children: normalizeText(item?.name, "C").split(" ").slice(0, 2).map((part) => part[0]).join("").toUpperCase() }),
3149
3186
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
@@ -3203,20 +3240,17 @@ function BuilderPageEditor({ initialDoc, pageID }) {
3203
3240
  },
3204
3241
  style: isItemSelected ? { outline: "2px solid rgba(15, 125, 82, 0.55)", outlineOffset: 3 } : void 0,
3205
3242
  children: [
3206
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "quote", children: [
3207
- '"',
3208
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3209
- InlineText,
3210
- {
3211
- as: "span",
3212
- multiline: true,
3213
- onCommit: (value) => updateArrayItemField(index, "items", itemIndex, "quote", value),
3214
- placeholder: "Customer quote",
3215
- value: normalizeText(item?.quote)
3216
- }
3217
- ),
3218
- '"'
3219
- ] }),
3243
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "testimonial-quote-mark", "aria-hidden": "true", children: "\u201C" }),
3244
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "quote testimonial-quote", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3245
+ InlineText,
3246
+ {
3247
+ as: "span",
3248
+ multiline: true,
3249
+ onCommit: (value) => updateArrayItemField(index, "items", itemIndex, "quote", value),
3250
+ placeholder: "Customer quote",
3251
+ value: normalizeText(item?.quote)
3252
+ }
3253
+ ) }),
3220
3254
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "quote-author", children: [
3221
3255
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "quote-avatar", children: normalizeText(item?.name, "C").split(" ").slice(0, 2).map((part) => part[0]).join("").toUpperCase() }),
3222
3256
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
@@ -3,7 +3,7 @@
3
3
  import {
4
4
  MAX_DIRECT_UPLOAD_BYTES,
5
5
  optimizeImageForUpload
6
- } from "../chunk-I3HYCS77.mjs";
6
+ } from "../chunk-ROTPP5CU.mjs";
7
7
 
8
8
  // src/studio-pages/builder/BuilderPageEditor.tsx
9
9
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
@@ -3053,20 +3053,17 @@ function BuilderPageEditor({ initialDoc, pageID }) {
3053
3053
  },
3054
3054
  style: isItemSelected ? { outline: "2px solid rgba(15, 125, 82, 0.55)", outlineOffset: 3 } : void 0,
3055
3055
  children: [
3056
- /* @__PURE__ */ jsxs("p", { className: "quote", children: [
3057
- '"',
3058
- /* @__PURE__ */ jsx(
3059
- InlineText,
3060
- {
3061
- as: "span",
3062
- multiline: true,
3063
- onCommit: (value) => updateArrayItemField(index, "items", itemIndex, "quote", value),
3064
- placeholder: "Customer quote",
3065
- value: normalizeText(item?.quote)
3066
- }
3067
- ),
3068
- '"'
3069
- ] }),
3056
+ /* @__PURE__ */ jsx("span", { className: "testimonial-quote-mark", "aria-hidden": "true", children: "\u201C" }),
3057
+ /* @__PURE__ */ jsx("p", { className: "quote testimonial-quote", children: /* @__PURE__ */ jsx(
3058
+ InlineText,
3059
+ {
3060
+ as: "span",
3061
+ multiline: true,
3062
+ onCommit: (value) => updateArrayItemField(index, "items", itemIndex, "quote", value),
3063
+ placeholder: "Customer quote",
3064
+ value: normalizeText(item?.quote)
3065
+ }
3066
+ ) }),
3070
3067
  /* @__PURE__ */ jsxs("div", { className: "quote-author", children: [
3071
3068
  /* @__PURE__ */ jsx("div", { className: "quote-avatar", children: normalizeText(item?.name, "C").split(" ").slice(0, 2).map((part) => part[0]).join("").toUpperCase() }),
3072
3069
  /* @__PURE__ */ jsxs("div", { children: [
@@ -3126,20 +3123,17 @@ function BuilderPageEditor({ initialDoc, pageID }) {
3126
3123
  },
3127
3124
  style: isItemSelected ? { outline: "2px solid rgba(15, 125, 82, 0.55)", outlineOffset: 3 } : void 0,
3128
3125
  children: [
3129
- /* @__PURE__ */ jsxs("p", { className: "quote", children: [
3130
- '"',
3131
- /* @__PURE__ */ jsx(
3132
- InlineText,
3133
- {
3134
- as: "span",
3135
- multiline: true,
3136
- onCommit: (value) => updateArrayItemField(index, "items", itemIndex, "quote", value),
3137
- placeholder: "Customer quote",
3138
- value: normalizeText(item?.quote)
3139
- }
3140
- ),
3141
- '"'
3142
- ] }),
3126
+ /* @__PURE__ */ jsx("span", { className: "testimonial-quote-mark", "aria-hidden": "true", children: "\u201C" }),
3127
+ /* @__PURE__ */ jsx("p", { className: "quote testimonial-quote", children: /* @__PURE__ */ jsx(
3128
+ InlineText,
3129
+ {
3130
+ as: "span",
3131
+ multiline: true,
3132
+ onCommit: (value) => updateArrayItemField(index, "items", itemIndex, "quote", value),
3133
+ placeholder: "Customer quote",
3134
+ value: normalizeText(item?.quote)
3135
+ }
3136
+ ) }),
3143
3137
  /* @__PURE__ */ jsxs("div", { className: "quote-author", children: [
3144
3138
  /* @__PURE__ */ jsx("div", { className: "quote-avatar", children: normalizeText(item?.name, "C").split(" ").slice(0, 2).map((part) => part[0]).join("").toUpperCase() }),
3145
3139
  /* @__PURE__ */ jsxs("div", { children: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orion-studios/payload-studio",
3
- "version": "0.5.0-beta.65",
3
+ "version": "0.5.0-beta.67",
4
4
  "description": "Unified Payload CMS toolkit for Orion Studios",
5
5
  "types": "./dist/index.d.ts",
6
6
  "main": "./dist/index.js",
@@ -1,59 +0,0 @@
1
- 'use client';
2
-
3
- // src/shared/clientImageUploadOptimization.ts
4
- var MAX_DIRECT_UPLOAD_BYTES = 4e6;
5
- async function optimizeImageForUpload(file) {
6
- if (!file.type.startsWith("image/")) {
7
- return file;
8
- }
9
- const objectURL = URL.createObjectURL(file);
10
- try {
11
- const image = await new Promise((resolve, reject) => {
12
- const nextImage = new Image();
13
- nextImage.onload = () => resolve(nextImage);
14
- nextImage.onerror = () => reject(new Error("Could not read image for upload optimization."));
15
- nextImage.src = objectURL;
16
- });
17
- const canvas = document.createElement("canvas");
18
- canvas.width = Math.max(1, image.width);
19
- canvas.height = Math.max(1, image.height);
20
- const context = canvas.getContext("2d");
21
- if (!context) {
22
- return file;
23
- }
24
- context.drawImage(image, 0, 0, canvas.width, canvas.height);
25
- const outputMime = file.type === "image/webp" ? "image/webp" : "image/jpeg";
26
- const qualityPasses = [0.82, 0.74, 0.66, 0.58, 0.5, 0.42, 0.36, 0.3, 0.26];
27
- let bestFile = null;
28
- for (const quality of qualityPasses) {
29
- const blob = await new Promise((resolve) => {
30
- canvas.toBlob((value) => resolve(value), outputMime, quality);
31
- });
32
- if (!blob) {
33
- continue;
34
- }
35
- const optimizedName = file.name.replace(/\.[^/.]+$/, outputMime === "image/webp" ? ".webp" : ".jpg");
36
- const optimized = new File([blob], optimizedName, {
37
- lastModified: Date.now(),
38
- type: outputMime
39
- });
40
- if (!bestFile || optimized.size < bestFile.size) {
41
- bestFile = optimized;
42
- }
43
- if (optimized.size <= MAX_DIRECT_UPLOAD_BYTES) {
44
- break;
45
- }
46
- }
47
- if (!bestFile) {
48
- return file;
49
- }
50
- return bestFile.size < file.size ? bestFile : file;
51
- } finally {
52
- URL.revokeObjectURL(objectURL);
53
- }
54
- }
55
-
56
- export {
57
- MAX_DIRECT_UPLOAD_BYTES,
58
- optimizeImageForUpload
59
- };