@orion-studios/payload-studio 0.6.0-beta.53 → 0.6.0-beta.55

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.
@@ -604,6 +604,7 @@ var decodeHtmlAttribute2 = (value) => {
604
604
  return decoded;
605
605
  };
606
606
  var attrToPropName = (name) => name.replace(/^data-orion-/, "").replace(/^data-/, "").replace(/-([a-z])/g, (_, char) => char.toUpperCase());
607
+ var propToAttrName = (name) => `data-orion-${name.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`)}`;
607
608
  var propsFromAttributes = (attributes = {}) => {
608
609
  const props = {};
609
610
  Object.keys(attributes).forEach((name) => {
@@ -646,6 +647,131 @@ var lockPreviewChildren = (component) => {
646
647
  lockPreviewChildren(child);
647
648
  });
648
649
  };
650
+ var parseJsonArray = (value) => {
651
+ const decoded = decodeHtmlAttribute2(value);
652
+ if (!decoded) {
653
+ return [];
654
+ }
655
+ try {
656
+ const parsed = JSON.parse(decoded);
657
+ return Array.isArray(parsed) ? parsed.filter((item) => Boolean(item && typeof item === "object" && !Array.isArray(item))) : [];
658
+ } catch {
659
+ return [];
660
+ }
661
+ };
662
+ var updateJsonListAttribute = ({
663
+ field,
664
+ index,
665
+ listAttr,
666
+ model,
667
+ value
668
+ }) => {
669
+ const attrs = model.getAttributes?.() || {};
670
+ const list = parseJsonArray(attrs[listAttr]);
671
+ if (!list[index]) {
672
+ return;
673
+ }
674
+ list[index] = {
675
+ ...list[index],
676
+ [field]: value
677
+ };
678
+ model.addAttributes?.({
679
+ [listAttr]: JSON.stringify(list)
680
+ });
681
+ };
682
+ var chooseAsset = (editor, currentSrc, callback) => {
683
+ const assetManager = editor?.AssetManager;
684
+ if (!assetManager?.open) {
685
+ const src = window.prompt("Image URL");
686
+ if (src) {
687
+ callback(src);
688
+ }
689
+ return;
690
+ }
691
+ if (currentSrc) {
692
+ assetManager.add?.({
693
+ name: `Current image - ${currentSrc.split("/").pop() || currentSrc}`,
694
+ src: currentSrc,
695
+ type: "image"
696
+ });
697
+ }
698
+ assetManager.open({
699
+ select(asset) {
700
+ const src = typeof asset === "string" ? asset : asset && typeof asset === "object" && "get" in asset && typeof asset.get === "function" ? String(asset.get("src") || "") : asset && typeof asset === "object" && "src" in asset ? String(asset.src || "") : "";
701
+ if (src) {
702
+ callback(src);
703
+ }
704
+ assetManager.close?.();
705
+ }
706
+ });
707
+ };
708
+ var bindEditablePreview = (view, editor) => {
709
+ const root = view.el;
710
+ if (!root) {
711
+ return;
712
+ }
713
+ root.querySelectorAll("[data-orion-edit-field]").forEach((element) => {
714
+ const field = element.dataset.orionEditField || "";
715
+ const listName = element.dataset.orionEditList || "";
716
+ const listIndex = Number(element.dataset.orionEditIndex);
717
+ const attrName = listName ? propToAttrName(listName) : propToAttrName(field);
718
+ const isImage = element.dataset.orionEditKind === "image" || element instanceof HTMLImageElement;
719
+ element.setAttribute("title", isImage ? "Click to replace image" : "Click and type to edit");
720
+ element.style.cursor = "text";
721
+ element.addEventListener("click", (event) => {
722
+ event.stopPropagation();
723
+ editor.select?.(view.model);
724
+ if (!isImage) {
725
+ return;
726
+ }
727
+ element.style.cursor = "pointer";
728
+ const currentSrc = element instanceof HTMLImageElement ? element.currentSrc || element.src || "" : element.style.backgroundImage.replace(/^url\(["']?/, "").replace(/["']?\)$/, "");
729
+ chooseAsset(editor, currentSrc, (src) => {
730
+ if (listName && Number.isInteger(listIndex)) {
731
+ updateJsonListAttribute({
732
+ field,
733
+ index: listIndex,
734
+ listAttr: attrName,
735
+ model: view.model,
736
+ value: src
737
+ });
738
+ return;
739
+ }
740
+ view.model.addAttributes?.({
741
+ [attrName]: src
742
+ });
743
+ });
744
+ });
745
+ if (isImage) {
746
+ return;
747
+ }
748
+ element.setAttribute("contenteditable", "true");
749
+ element.setAttribute("spellcheck", "true");
750
+ const commit = () => {
751
+ const value = element.innerText.trim();
752
+ if (listName && Number.isInteger(listIndex)) {
753
+ updateJsonListAttribute({
754
+ field,
755
+ index: listIndex,
756
+ listAttr: attrName,
757
+ model: view.model,
758
+ value
759
+ });
760
+ return;
761
+ }
762
+ view.model.addAttributes?.({
763
+ [attrName]: value
764
+ });
765
+ };
766
+ element.addEventListener("blur", commit);
767
+ element.addEventListener("keydown", (event) => {
768
+ if (event.key === "Enter" && !event.shiftKey) {
769
+ event.preventDefault();
770
+ element.blur();
771
+ }
772
+ });
773
+ });
774
+ };
649
775
  var registerProjectDynamicComponents = (editor, adapter) => {
650
776
  const components = adapter?.components || {};
651
777
  Object.keys(components).forEach((type) => {
@@ -689,6 +815,7 @@ var registerProjectDynamicComponents = (editor, adapter) => {
689
815
  const props = propsFromAttributes(attributes);
690
816
  this.model.components(previewForDefinition(definition, props));
691
817
  lockPreviewChildren(this.model);
818
+ queueMicrotask(() => bindEditablePreview(this, editor));
692
819
  }
693
820
  }
694
821
  });
@@ -739,10 +866,58 @@ var getRelationID = (value) => {
739
866
  const id = value.id;
740
867
  return typeof id === "number" || typeof id === "string" ? id : null;
741
868
  };
869
+ var normalizeAssetSrc = (value) => {
870
+ const trimmed = value.trim();
871
+ if (!trimmed) {
872
+ return "";
873
+ }
874
+ if (/^(https?:)?\/\//i.test(trimmed) || trimmed.startsWith("/")) {
875
+ return trimmed;
876
+ }
877
+ return `/${trimmed}`;
878
+ };
879
+ var mediaDocImageCandidates = (doc) => {
880
+ const candidates = [];
881
+ if (doc.sizes && typeof doc.sizes === "object") {
882
+ Object.values(doc.sizes).forEach((size) => {
883
+ if (size?.url) {
884
+ candidates.push({ src: normalizeAssetSrc(size.url), width: size.width });
885
+ } else if (size?.filename) {
886
+ candidates.push({ src: `/api/media/file/${encodeURIComponent(size.filename)}`, width: size.width });
887
+ }
888
+ });
889
+ }
890
+ if (typeof doc.thumbnailURL === "string" && doc.thumbnailURL.length > 0) {
891
+ candidates.push({ src: normalizeAssetSrc(doc.thumbnailURL), width: 360 });
892
+ }
893
+ if (typeof doc.url === "string" && doc.url.length > 0) {
894
+ candidates.push({ src: normalizeAssetSrc(doc.url), width: doc.width });
895
+ }
896
+ if (typeof doc.filename === "string" && doc.filename.length > 0) {
897
+ candidates.push({ src: `/api/media/file/${encodeURIComponent(doc.filename)}`, width: doc.width });
898
+ }
899
+ return candidates.filter((candidate, index, all) => candidate.src && all.findIndex((item) => item.src === candidate.src) === index);
900
+ };
901
+ var pickMediaAssetSrc = (doc) => {
902
+ const candidates = mediaDocImageCandidates(doc);
903
+ if (candidates.length === 0) {
904
+ return "";
905
+ }
906
+ const absolute = candidates.find((candidate) => /^(https?:)?\/\//i.test(candidate.src));
907
+ if (absolute) {
908
+ return absolute.src;
909
+ }
910
+ const fullSize = candidates.find((candidate) => candidate.src === doc.url);
911
+ if (fullSize) {
912
+ return fullSize.src;
913
+ }
914
+ const widest = [...candidates].sort((left, right) => (right.width || 0) - (left.width || 0))[0];
915
+ return widest?.src || candidates[0]?.src || "";
916
+ };
742
917
  var mediaDocToAsset = (doc) => {
743
918
  const id = getRelationID(doc);
744
919
  const filename = typeof doc.filename === "string" ? doc.filename : "";
745
- const src = typeof doc.url === "string" && doc.url.length > 0 ? doc.url : filename ? `/api/media/file/${encodeURIComponent(filename)}` : "";
920
+ const src = pickMediaAssetSrc(doc);
746
921
  if (id === null || !src) {
747
922
  return null;
748
923
  }
@@ -754,6 +929,26 @@ var mediaDocToAsset = (doc) => {
754
929
  type: "image"
755
930
  };
756
931
  };
932
+ var imageCanLoad = (src) => new Promise((resolve) => {
933
+ if (!src) {
934
+ resolve(false);
935
+ return;
936
+ }
937
+ const image = new Image();
938
+ image.onload = () => resolve(true);
939
+ image.onerror = () => resolve(false);
940
+ image.src = src;
941
+ });
942
+ var pruneBrokenImageAssets = async (editor) => {
943
+ const assetManager = editor.AssetManager;
944
+ const existingAssets = assetManager.getAll?.() || [];
945
+ for (const asset of existingAssets) {
946
+ const src = typeof asset.get === "function" ? String(asset.get("src") || "") : "";
947
+ if (src && !await imageCanLoad(src)) {
948
+ assetManager.remove?.(asset);
949
+ }
950
+ }
951
+ };
757
952
  var extractUploadedMedia = (value) => {
758
953
  const candidate = value && typeof value === "object" && "doc" in value ? value.doc : value;
759
954
  if (!candidate || typeof candidate !== "object") {
@@ -768,7 +963,10 @@ var extractUploadedMedia = (value) => {
768
963
  alt: typeof typed.alt === "string" ? typed.alt : "",
769
964
  filename: typeof typed.filename === "string" ? typed.filename : "",
770
965
  id,
771
- url: typeof typed.url === "string" ? typed.url : ""
966
+ sizes: typed.sizes && typeof typed.sizes === "object" ? typed.sizes : void 0,
967
+ thumbnailURL: typeof typed.thumbnailURL === "string" ? typed.thumbnailURL : "",
968
+ url: typeof typed.url === "string" ? typed.url : "",
969
+ width: typeof typed.width === "number" ? typed.width : void 0
772
970
  };
773
971
  };
774
972
  var loadPayloadMediaAssets = async (editor) => {
@@ -780,8 +978,17 @@ var loadPayloadMediaAssets = async (editor) => {
780
978
  return;
781
979
  }
782
980
  const json = await response.json();
783
- const assets = (Array.isArray(json.docs) ? json.docs : []).map((doc) => mediaDocToAsset(doc)).filter((asset) => asset !== null);
784
- editor.AssetManager.add(assets);
981
+ const candidateAssets = (Array.isArray(json.docs) ? json.docs : []).map((doc) => mediaDocToAsset(doc)).filter((asset) => asset !== null);
982
+ const assets = [];
983
+ for (const asset of candidateAssets) {
984
+ if (await imageCanLoad(asset.src)) {
985
+ assets.push(asset);
986
+ }
987
+ }
988
+ if (assets.length > 0) {
989
+ editor.AssetManager.add(assets);
990
+ }
991
+ await pruneBrokenImageAssets(editor);
785
992
  };
786
993
  var uploadPayloadMediaAssets = async (editor, files) => {
787
994
  const fileArray = Array.from(files);
@@ -480,6 +480,7 @@ var decodeHtmlAttribute2 = (value) => {
480
480
  return decoded;
481
481
  };
482
482
  var attrToPropName = (name) => name.replace(/^data-orion-/, "").replace(/^data-/, "").replace(/-([a-z])/g, (_, char) => char.toUpperCase());
483
+ var propToAttrName = (name) => `data-orion-${name.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`)}`;
483
484
  var propsFromAttributes = (attributes = {}) => {
484
485
  const props = {};
485
486
  Object.keys(attributes).forEach((name) => {
@@ -522,6 +523,131 @@ var lockPreviewChildren = (component) => {
522
523
  lockPreviewChildren(child);
523
524
  });
524
525
  };
526
+ var parseJsonArray = (value) => {
527
+ const decoded = decodeHtmlAttribute2(value);
528
+ if (!decoded) {
529
+ return [];
530
+ }
531
+ try {
532
+ const parsed = JSON.parse(decoded);
533
+ return Array.isArray(parsed) ? parsed.filter((item) => Boolean(item && typeof item === "object" && !Array.isArray(item))) : [];
534
+ } catch {
535
+ return [];
536
+ }
537
+ };
538
+ var updateJsonListAttribute = ({
539
+ field,
540
+ index,
541
+ listAttr,
542
+ model,
543
+ value
544
+ }) => {
545
+ const attrs = model.getAttributes?.() || {};
546
+ const list = parseJsonArray(attrs[listAttr]);
547
+ if (!list[index]) {
548
+ return;
549
+ }
550
+ list[index] = {
551
+ ...list[index],
552
+ [field]: value
553
+ };
554
+ model.addAttributes?.({
555
+ [listAttr]: JSON.stringify(list)
556
+ });
557
+ };
558
+ var chooseAsset = (editor, currentSrc, callback) => {
559
+ const assetManager = editor?.AssetManager;
560
+ if (!assetManager?.open) {
561
+ const src = window.prompt("Image URL");
562
+ if (src) {
563
+ callback(src);
564
+ }
565
+ return;
566
+ }
567
+ if (currentSrc) {
568
+ assetManager.add?.({
569
+ name: `Current image - ${currentSrc.split("/").pop() || currentSrc}`,
570
+ src: currentSrc,
571
+ type: "image"
572
+ });
573
+ }
574
+ assetManager.open({
575
+ select(asset) {
576
+ const src = typeof asset === "string" ? asset : asset && typeof asset === "object" && "get" in asset && typeof asset.get === "function" ? String(asset.get("src") || "") : asset && typeof asset === "object" && "src" in asset ? String(asset.src || "") : "";
577
+ if (src) {
578
+ callback(src);
579
+ }
580
+ assetManager.close?.();
581
+ }
582
+ });
583
+ };
584
+ var bindEditablePreview = (view, editor) => {
585
+ const root = view.el;
586
+ if (!root) {
587
+ return;
588
+ }
589
+ root.querySelectorAll("[data-orion-edit-field]").forEach((element) => {
590
+ const field = element.dataset.orionEditField || "";
591
+ const listName = element.dataset.orionEditList || "";
592
+ const listIndex = Number(element.dataset.orionEditIndex);
593
+ const attrName = listName ? propToAttrName(listName) : propToAttrName(field);
594
+ const isImage = element.dataset.orionEditKind === "image" || element instanceof HTMLImageElement;
595
+ element.setAttribute("title", isImage ? "Click to replace image" : "Click and type to edit");
596
+ element.style.cursor = "text";
597
+ element.addEventListener("click", (event) => {
598
+ event.stopPropagation();
599
+ editor.select?.(view.model);
600
+ if (!isImage) {
601
+ return;
602
+ }
603
+ element.style.cursor = "pointer";
604
+ const currentSrc = element instanceof HTMLImageElement ? element.currentSrc || element.src || "" : element.style.backgroundImage.replace(/^url\(["']?/, "").replace(/["']?\)$/, "");
605
+ chooseAsset(editor, currentSrc, (src) => {
606
+ if (listName && Number.isInteger(listIndex)) {
607
+ updateJsonListAttribute({
608
+ field,
609
+ index: listIndex,
610
+ listAttr: attrName,
611
+ model: view.model,
612
+ value: src
613
+ });
614
+ return;
615
+ }
616
+ view.model.addAttributes?.({
617
+ [attrName]: src
618
+ });
619
+ });
620
+ });
621
+ if (isImage) {
622
+ return;
623
+ }
624
+ element.setAttribute("contenteditable", "true");
625
+ element.setAttribute("spellcheck", "true");
626
+ const commit = () => {
627
+ const value = element.innerText.trim();
628
+ if (listName && Number.isInteger(listIndex)) {
629
+ updateJsonListAttribute({
630
+ field,
631
+ index: listIndex,
632
+ listAttr: attrName,
633
+ model: view.model,
634
+ value
635
+ });
636
+ return;
637
+ }
638
+ view.model.addAttributes?.({
639
+ [attrName]: value
640
+ });
641
+ };
642
+ element.addEventListener("blur", commit);
643
+ element.addEventListener("keydown", (event) => {
644
+ if (event.key === "Enter" && !event.shiftKey) {
645
+ event.preventDefault();
646
+ element.blur();
647
+ }
648
+ });
649
+ });
650
+ };
525
651
  var registerProjectDynamicComponents = (editor, adapter) => {
526
652
  const components = adapter?.components || {};
527
653
  Object.keys(components).forEach((type) => {
@@ -565,6 +691,7 @@ var registerProjectDynamicComponents = (editor, adapter) => {
565
691
  const props = propsFromAttributes(attributes);
566
692
  this.model.components(previewForDefinition(definition, props));
567
693
  lockPreviewChildren(this.model);
694
+ queueMicrotask(() => bindEditablePreview(this, editor));
568
695
  }
569
696
  }
570
697
  });
@@ -615,10 +742,58 @@ var getRelationID = (value) => {
615
742
  const id = value.id;
616
743
  return typeof id === "number" || typeof id === "string" ? id : null;
617
744
  };
745
+ var normalizeAssetSrc = (value) => {
746
+ const trimmed = value.trim();
747
+ if (!trimmed) {
748
+ return "";
749
+ }
750
+ if (/^(https?:)?\/\//i.test(trimmed) || trimmed.startsWith("/")) {
751
+ return trimmed;
752
+ }
753
+ return `/${trimmed}`;
754
+ };
755
+ var mediaDocImageCandidates = (doc) => {
756
+ const candidates = [];
757
+ if (doc.sizes && typeof doc.sizes === "object") {
758
+ Object.values(doc.sizes).forEach((size) => {
759
+ if (size?.url) {
760
+ candidates.push({ src: normalizeAssetSrc(size.url), width: size.width });
761
+ } else if (size?.filename) {
762
+ candidates.push({ src: `/api/media/file/${encodeURIComponent(size.filename)}`, width: size.width });
763
+ }
764
+ });
765
+ }
766
+ if (typeof doc.thumbnailURL === "string" && doc.thumbnailURL.length > 0) {
767
+ candidates.push({ src: normalizeAssetSrc(doc.thumbnailURL), width: 360 });
768
+ }
769
+ if (typeof doc.url === "string" && doc.url.length > 0) {
770
+ candidates.push({ src: normalizeAssetSrc(doc.url), width: doc.width });
771
+ }
772
+ if (typeof doc.filename === "string" && doc.filename.length > 0) {
773
+ candidates.push({ src: `/api/media/file/${encodeURIComponent(doc.filename)}`, width: doc.width });
774
+ }
775
+ return candidates.filter((candidate, index, all) => candidate.src && all.findIndex((item) => item.src === candidate.src) === index);
776
+ };
777
+ var pickMediaAssetSrc = (doc) => {
778
+ const candidates = mediaDocImageCandidates(doc);
779
+ if (candidates.length === 0) {
780
+ return "";
781
+ }
782
+ const absolute = candidates.find((candidate) => /^(https?:)?\/\//i.test(candidate.src));
783
+ if (absolute) {
784
+ return absolute.src;
785
+ }
786
+ const fullSize = candidates.find((candidate) => candidate.src === doc.url);
787
+ if (fullSize) {
788
+ return fullSize.src;
789
+ }
790
+ const widest = [...candidates].sort((left, right) => (right.width || 0) - (left.width || 0))[0];
791
+ return widest?.src || candidates[0]?.src || "";
792
+ };
618
793
  var mediaDocToAsset = (doc) => {
619
794
  const id = getRelationID(doc);
620
795
  const filename = typeof doc.filename === "string" ? doc.filename : "";
621
- const src = typeof doc.url === "string" && doc.url.length > 0 ? doc.url : filename ? `/api/media/file/${encodeURIComponent(filename)}` : "";
796
+ const src = pickMediaAssetSrc(doc);
622
797
  if (id === null || !src) {
623
798
  return null;
624
799
  }
@@ -630,6 +805,26 @@ var mediaDocToAsset = (doc) => {
630
805
  type: "image"
631
806
  };
632
807
  };
808
+ var imageCanLoad = (src) => new Promise((resolve) => {
809
+ if (!src) {
810
+ resolve(false);
811
+ return;
812
+ }
813
+ const image = new Image();
814
+ image.onload = () => resolve(true);
815
+ image.onerror = () => resolve(false);
816
+ image.src = src;
817
+ });
818
+ var pruneBrokenImageAssets = async (editor) => {
819
+ const assetManager = editor.AssetManager;
820
+ const existingAssets = assetManager.getAll?.() || [];
821
+ for (const asset of existingAssets) {
822
+ const src = typeof asset.get === "function" ? String(asset.get("src") || "") : "";
823
+ if (src && !await imageCanLoad(src)) {
824
+ assetManager.remove?.(asset);
825
+ }
826
+ }
827
+ };
633
828
  var extractUploadedMedia = (value) => {
634
829
  const candidate = value && typeof value === "object" && "doc" in value ? value.doc : value;
635
830
  if (!candidate || typeof candidate !== "object") {
@@ -644,7 +839,10 @@ var extractUploadedMedia = (value) => {
644
839
  alt: typeof typed.alt === "string" ? typed.alt : "",
645
840
  filename: typeof typed.filename === "string" ? typed.filename : "",
646
841
  id,
647
- url: typeof typed.url === "string" ? typed.url : ""
842
+ sizes: typed.sizes && typeof typed.sizes === "object" ? typed.sizes : void 0,
843
+ thumbnailURL: typeof typed.thumbnailURL === "string" ? typed.thumbnailURL : "",
844
+ url: typeof typed.url === "string" ? typed.url : "",
845
+ width: typeof typed.width === "number" ? typed.width : void 0
648
846
  };
649
847
  };
650
848
  var loadPayloadMediaAssets = async (editor) => {
@@ -656,8 +854,17 @@ var loadPayloadMediaAssets = async (editor) => {
656
854
  return;
657
855
  }
658
856
  const json = await response.json();
659
- const assets = (Array.isArray(json.docs) ? json.docs : []).map((doc) => mediaDocToAsset(doc)).filter((asset) => asset !== null);
660
- editor.AssetManager.add(assets);
857
+ const candidateAssets = (Array.isArray(json.docs) ? json.docs : []).map((doc) => mediaDocToAsset(doc)).filter((asset) => asset !== null);
858
+ const assets = [];
859
+ for (const asset of candidateAssets) {
860
+ if (await imageCanLoad(asset.src)) {
861
+ assets.push(asset);
862
+ }
863
+ }
864
+ if (assets.length > 0) {
865
+ editor.AssetManager.add(assets);
866
+ }
867
+ await pruneBrokenImageAssets(editor);
661
868
  };
662
869
  var uploadPayloadMediaAssets = async (editor, files) => {
663
870
  const fileArray = Array.from(files);
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
- import {
2
- admin_exports
3
- } from "./chunk-JC3UV74N.mjs";
4
1
  import {
5
2
  admin_app_exports
6
3
  } from "./chunk-RKTIFEUY.mjs";
4
+ import {
5
+ admin_exports
6
+ } from "./chunk-JC3UV74N.mjs";
7
7
  import "./chunk-W2UOCJDX.mjs";
8
8
  import {
9
9
  blocks_exports
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orion-studios/payload-studio",
3
- "version": "0.6.0-beta.53",
3
+ "version": "0.6.0-beta.55",
4
4
  "description": "Base CMS, builder, and custom admin toolkit for Orion Studios websites",
5
5
  "types": "./dist/index.d.ts",
6
6
  "main": "./dist/index.js",