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

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.
@@ -691,6 +691,61 @@ function MediaListItem({
691
691
  // src/admin-app/components/MediaUploadForm.tsx
692
692
  var import_react5 = require("react");
693
693
  var import_navigation = require("next/navigation");
694
+
695
+ // src/shared/clientImageUploadOptimization.ts
696
+ var MAX_DIRECT_UPLOAD_BYTES = 4e6;
697
+ async function optimizeImageForUpload(file) {
698
+ if (!file.type.startsWith("image/")) {
699
+ return file;
700
+ }
701
+ const objectURL = URL.createObjectURL(file);
702
+ try {
703
+ const image = await new Promise((resolve, reject) => {
704
+ const nextImage = new Image();
705
+ nextImage.onload = () => resolve(nextImage);
706
+ nextImage.onerror = () => reject(new Error("Could not read image for upload optimization."));
707
+ nextImage.src = objectURL;
708
+ });
709
+ const canvas = document.createElement("canvas");
710
+ canvas.width = Math.max(1, image.width);
711
+ canvas.height = Math.max(1, image.height);
712
+ const context = canvas.getContext("2d");
713
+ if (!context) {
714
+ return file;
715
+ }
716
+ context.drawImage(image, 0, 0, canvas.width, canvas.height);
717
+ const outputMime = file.type === "image/webp" ? "image/webp" : "image/jpeg";
718
+ const qualityPasses = [0.82, 0.74, 0.66, 0.58, 0.5, 0.42, 0.36, 0.3, 0.26];
719
+ 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;
737
+ }
738
+ }
739
+ if (!bestFile) {
740
+ return file;
741
+ }
742
+ return bestFile.size < file.size ? bestFile : file;
743
+ } finally {
744
+ URL.revokeObjectURL(objectURL);
745
+ }
746
+ }
747
+
748
+ // src/admin-app/components/MediaUploadForm.tsx
694
749
  var import_jsx_runtime6 = require("react/jsx-runtime");
695
750
  var MEDIA_LIBRARY_SYNC_EVENT = "orion-media-library-updated";
696
751
  var notifyMediaLibraryUpdated = () => {
@@ -773,12 +828,16 @@ function MediaUploadForm() {
773
828
  setSubmitting(true);
774
829
  setError(null);
775
830
  try {
831
+ const optimizedFile = await optimizeImageForUpload(file);
832
+ if (optimizedFile.size > MAX_DIRECT_UPLOAD_BYTES) {
833
+ throw new Error("Image is too large. Use an image under 4MB or lower-resolution export.");
834
+ }
776
835
  const formData = new FormData();
777
836
  const fallbackAlt = file.name.replace(/\.[^/.]+$/, "").trim();
778
837
  const resolvedAlt = alt.trim() || fallbackAlt || "Uploaded image";
779
838
  formData.set("_payload", JSON.stringify({ alt: resolvedAlt }));
780
839
  formData.set("alt", resolvedAlt);
781
- formData.set("file", file);
840
+ formData.set("file", optimizedFile);
782
841
  const response = await fetch("/api/media", {
783
842
  method: "POST",
784
843
  credentials: "include",
@@ -1,4 +1,8 @@
1
1
  'use client';
2
+ import {
3
+ MAX_DIRECT_UPLOAD_BYTES,
4
+ optimizeImageForUpload
5
+ } from "../chunk-I3HYCS77.mjs";
2
6
 
3
7
  // src/admin-app/components/AdminShellClient.tsx
4
8
  import { useEffect, useState } from "react";
@@ -742,12 +746,16 @@ function MediaUploadForm() {
742
746
  setSubmitting(true);
743
747
  setError(null);
744
748
  try {
749
+ const optimizedFile = await optimizeImageForUpload(file);
750
+ if (optimizedFile.size > MAX_DIRECT_UPLOAD_BYTES) {
751
+ throw new Error("Image is too large. Use an image under 4MB or lower-resolution export.");
752
+ }
745
753
  const formData = new FormData();
746
754
  const fallbackAlt = file.name.replace(/\.[^/.]+$/, "").trim();
747
755
  const resolvedAlt = alt.trim() || fallbackAlt || "Uploaded image";
748
756
  formData.set("_payload", JSON.stringify({ alt: resolvedAlt }));
749
757
  formData.set("alt", resolvedAlt);
750
- formData.set("file", file);
758
+ formData.set("file", optimizedFile);
751
759
  const response = await fetch("/api/media", {
752
760
  method: "POST",
753
761
  credentials: "include",
@@ -0,0 +1,59 @@
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
+ };
@@ -263,6 +263,59 @@ var layoutToStudioDocument = (layout, title, metadata) => {
263
263
  };
264
264
  };
265
265
 
266
+ // src/shared/clientImageUploadOptimization.ts
267
+ var MAX_DIRECT_UPLOAD_BYTES = 4e6;
268
+ async function optimizeImageForUpload(file) {
269
+ if (!file.type.startsWith("image/")) {
270
+ return file;
271
+ }
272
+ const objectURL = URL.createObjectURL(file);
273
+ try {
274
+ const image = await new Promise((resolve, reject) => {
275
+ const nextImage = new Image();
276
+ nextImage.onload = () => resolve(nextImage);
277
+ nextImage.onerror = () => reject(new Error("Could not read image for upload optimization."));
278
+ nextImage.src = objectURL;
279
+ });
280
+ const canvas = document.createElement("canvas");
281
+ canvas.width = Math.max(1, image.width);
282
+ canvas.height = Math.max(1, image.height);
283
+ const context = canvas.getContext("2d");
284
+ if (!context) {
285
+ return file;
286
+ }
287
+ context.drawImage(image, 0, 0, canvas.width, canvas.height);
288
+ const outputMime = file.type === "image/webp" ? "image/webp" : "image/jpeg";
289
+ const qualityPasses = [0.82, 0.74, 0.66, 0.58, 0.5, 0.42, 0.36, 0.3, 0.26];
290
+ 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;
308
+ }
309
+ }
310
+ if (!bestFile) {
311
+ return file;
312
+ }
313
+ return bestFile.size < file.size ? bestFile : file;
314
+ } finally {
315
+ URL.revokeObjectURL(objectURL);
316
+ }
317
+ }
318
+
266
319
  // src/studio-pages/builder/presets.ts
267
320
  var sectionPresets = [
268
321
  {
@@ -1030,7 +1083,6 @@ async function parsePayloadErrorMessage(response, fallbackMessage) {
1030
1083
  }
1031
1084
  return fallbackMessage;
1032
1085
  }
1033
- var MAX_DIRECT_UPLOAD_BYTES = 4e6;
1034
1086
  var MEDIA_LIBRARY_SYNC_EVENT = "orion-media-library-updated";
1035
1087
  var notifyMediaLibraryUpdated = () => {
1036
1088
  if (typeof window === "undefined") {
@@ -1049,59 +1101,6 @@ var notifyMediaLibraryUpdated = () => {
1049
1101
  } catch {
1050
1102
  }
1051
1103
  };
1052
- async function optimizeImageForUpload(file) {
1053
- if (!file.type.startsWith("image/")) {
1054
- return file;
1055
- }
1056
- if (file.size <= MAX_DIRECT_UPLOAD_BYTES) {
1057
- return file;
1058
- }
1059
- const objectURL = URL.createObjectURL(file);
1060
- try {
1061
- const image = await new Promise((resolve, reject) => {
1062
- const nextImage = new Image();
1063
- nextImage.onload = () => resolve(nextImage);
1064
- nextImage.onerror = () => reject(new Error("Could not read image for upload optimization."));
1065
- nextImage.src = objectURL;
1066
- });
1067
- const canvas = document.createElement("canvas");
1068
- canvas.width = Math.max(1, image.width);
1069
- canvas.height = Math.max(1, image.height);
1070
- const context = canvas.getContext("2d");
1071
- if (!context) {
1072
- return file;
1073
- }
1074
- context.drawImage(image, 0, 0, canvas.width, canvas.height);
1075
- const outputMime = file.type === "image/webp" ? "image/webp" : "image/jpeg";
1076
- const qualityPasses = [0.82, 0.74, 0.66, 0.58, 0.5, 0.42, 0.36];
1077
- let bestFile = null;
1078
- for (const quality of qualityPasses) {
1079
- const blob = await new Promise((resolve) => {
1080
- canvas.toBlob((value) => resolve(value), outputMime, quality);
1081
- });
1082
- if (!blob) {
1083
- continue;
1084
- }
1085
- const optimizedName = file.name.replace(/\.[^/.]+$/, outputMime === "image/webp" ? ".webp" : ".jpg");
1086
- const optimized = new File([blob], optimizedName, {
1087
- lastModified: Date.now(),
1088
- type: outputMime
1089
- });
1090
- if (!bestFile || optimized.size < bestFile.size) {
1091
- bestFile = optimized;
1092
- }
1093
- if (optimized.size <= MAX_DIRECT_UPLOAD_BYTES) {
1094
- break;
1095
- }
1096
- }
1097
- if (!bestFile) {
1098
- return file;
1099
- }
1100
- return bestFile.size < file.size ? bestFile : file;
1101
- } finally {
1102
- URL.revokeObjectURL(objectURL);
1103
- }
1104
- }
1105
1104
  function InlineText({
1106
1105
  as = "p",
1107
1106
  className,
@@ -1,5 +1,9 @@
1
1
  'use client';
2
2
  "use client";
3
+ import {
4
+ MAX_DIRECT_UPLOAD_BYTES,
5
+ optimizeImageForUpload
6
+ } from "../chunk-I3HYCS77.mjs";
3
7
 
4
8
  // src/studio-pages/builder/BuilderPageEditor.tsx
5
9
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
@@ -1002,7 +1006,6 @@ async function parsePayloadErrorMessage(response, fallbackMessage) {
1002
1006
  }
1003
1007
  return fallbackMessage;
1004
1008
  }
1005
- var MAX_DIRECT_UPLOAD_BYTES = 4e6;
1006
1009
  var MEDIA_LIBRARY_SYNC_EVENT = "orion-media-library-updated";
1007
1010
  var notifyMediaLibraryUpdated = () => {
1008
1011
  if (typeof window === "undefined") {
@@ -1021,59 +1024,6 @@ var notifyMediaLibraryUpdated = () => {
1021
1024
  } catch {
1022
1025
  }
1023
1026
  };
1024
- async function optimizeImageForUpload(file) {
1025
- if (!file.type.startsWith("image/")) {
1026
- return file;
1027
- }
1028
- if (file.size <= MAX_DIRECT_UPLOAD_BYTES) {
1029
- return file;
1030
- }
1031
- const objectURL = URL.createObjectURL(file);
1032
- try {
1033
- const image = await new Promise((resolve, reject) => {
1034
- const nextImage = new Image();
1035
- nextImage.onload = () => resolve(nextImage);
1036
- nextImage.onerror = () => reject(new Error("Could not read image for upload optimization."));
1037
- nextImage.src = objectURL;
1038
- });
1039
- const canvas = document.createElement("canvas");
1040
- canvas.width = Math.max(1, image.width);
1041
- canvas.height = Math.max(1, image.height);
1042
- const context = canvas.getContext("2d");
1043
- if (!context) {
1044
- return file;
1045
- }
1046
- context.drawImage(image, 0, 0, canvas.width, canvas.height);
1047
- const outputMime = file.type === "image/webp" ? "image/webp" : "image/jpeg";
1048
- const qualityPasses = [0.82, 0.74, 0.66, 0.58, 0.5, 0.42, 0.36];
1049
- let bestFile = null;
1050
- for (const quality of qualityPasses) {
1051
- const blob = await new Promise((resolve) => {
1052
- canvas.toBlob((value) => resolve(value), outputMime, quality);
1053
- });
1054
- if (!blob) {
1055
- continue;
1056
- }
1057
- const optimizedName = file.name.replace(/\.[^/.]+$/, outputMime === "image/webp" ? ".webp" : ".jpg");
1058
- const optimized = new File([blob], optimizedName, {
1059
- lastModified: Date.now(),
1060
- type: outputMime
1061
- });
1062
- if (!bestFile || optimized.size < bestFile.size) {
1063
- bestFile = optimized;
1064
- }
1065
- if (optimized.size <= MAX_DIRECT_UPLOAD_BYTES) {
1066
- break;
1067
- }
1068
- }
1069
- if (!bestFile) {
1070
- return file;
1071
- }
1072
- return bestFile.size < file.size ? bestFile : file;
1073
- } finally {
1074
- URL.revokeObjectURL(objectURL);
1075
- }
1076
- }
1077
1027
  function InlineText({
1078
1028
  as = "p",
1079
1029
  className,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orion-studios/payload-studio",
3
- "version": "0.5.0-beta.63",
3
+ "version": "0.5.0-beta.65",
4
4
  "description": "Unified Payload CMS toolkit for Orion Studios",
5
5
  "types": "./dist/index.d.ts",
6
6
  "main": "./dist/index.js",