cms-renderer 0.6.11 → 0.6.13

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.
@@ -466,11 +466,14 @@ function generateCmsOverlayScript(cmsParentOrigin) {
466
466
  var style = document.createElement('style');
467
467
  style.setAttribute('data-cms-overlay', '');
468
468
  style.textContent = \`
469
+ [data-cms-block],
469
470
  [data-cms-editable] {
470
471
  cursor: pointer;
472
+ }
473
+ [data-cms-editable] {
471
474
  border-radius: 2px;
472
475
  }
473
- [data-cms-editable]:hover {
476
+ [data-cms-editable]:not([data-cms-block]):hover {
474
477
  outline: 2px solid #3b82f6;
475
478
  outline-offset: 2px;
476
479
  }
@@ -483,8 +486,8 @@ function generateCmsOverlayScript(cmsParentOrigin) {
483
486
  pointer-events: none;
484
487
  z-index: 99998;
485
488
  }
486
- #cms-overlay-root > * {
487
- pointer-events: auto;
489
+ #cms-overlay-root > *:not(.cms-block-toolbar) {
490
+ pointer-events: none;
488
491
  }
489
492
  .cms-block-outline {
490
493
  position: fixed;
@@ -580,10 +583,69 @@ function generateCmsOverlayScript(cmsParentOrigin) {
580
583
  blockEl.setAttribute('data-cms-block', '');
581
584
  blockEl.setAttribute('data-block-id', blockId);
582
585
  blockEl.setAttribute('data-block-type', blockType);
583
- blockEl.setAttribute('data-cms-editable', '');
586
+
587
+ injectEditableSpans(
588
+ blockEl,
589
+ blockId,
590
+ blockType,
591
+ sentinel.getAttribute('data-content-entries')
592
+ );
584
593
  });
585
594
  }
586
595
 
596
+ function injectEditableSpans(blockEl, blockId, blockType, rawEntries) {
597
+ if (!rawEntries || blockEl.querySelector('[data-cms-editable][data-content-path]')) return;
598
+
599
+ var entries;
600
+ try {
601
+ entries = JSON.parse(rawEntries);
602
+ } catch (_) {
603
+ return;
604
+ }
605
+ if (!Array.isArray(entries) || entries.length === 0) return;
606
+
607
+ var used = {};
608
+ var walker = document.createTreeWalker(blockEl, NodeFilter.SHOW_TEXT, null);
609
+ var textNodes = [];
610
+ var node = walker.nextNode();
611
+ while (node !== null) {
612
+ if (node.nodeValue && node.nodeValue.trim()) {
613
+ textNodes.push(node);
614
+ }
615
+ node = walker.nextNode();
616
+ }
617
+
618
+ for (var i = 0; i < textNodes.length; i++) {
619
+ var textNode = textNodes[i];
620
+ var parentEl = textNode.parentElement;
621
+ if (!parentEl || parentEl.closest('svg') || parentEl.closest('[data-cms-editable]')) {
622
+ continue;
623
+ }
624
+
625
+ var text = textNode.nodeValue;
626
+ if (!text) continue;
627
+
628
+ for (var j = 0; j < entries.length; j++) {
629
+ var entry = entries[j];
630
+ if (!entry || typeof entry.v !== 'string' || typeof entry.p !== 'string' || used[entry.p]) {
631
+ continue;
632
+ }
633
+ if (text.trim() !== entry.v.trim()) continue;
634
+
635
+ used[entry.p] = true;
636
+ var span = document.createElement('span');
637
+ span.setAttribute('data-cms-editable', '');
638
+ span.setAttribute('data-block-id', blockId);
639
+ span.setAttribute('data-block-type', blockType);
640
+ span.setAttribute('data-content-path', entry.p);
641
+ span.setAttribute('contenteditable', 'true');
642
+ parentEl.insertBefore(span, textNode);
643
+ span.appendChild(textNode);
644
+ break;
645
+ }
646
+ }
647
+ }
648
+
587
649
  stampBlockElements();
588
650
 
589
651
  var stampObserver = new MutationObserver(function(mutations) {
@@ -638,6 +700,38 @@ function generateCmsOverlayScript(cmsParentOrigin) {
638
700
  var toolbarVisible = false;
639
701
  var selectedBlockId = null;
640
702
 
703
+ function decoratePreviewUrl(rawHref) {
704
+ if (!rawHref) return rawHref;
705
+ try {
706
+ var url = new URL(rawHref, window.location.href);
707
+ if (url.origin !== window.location.origin) return rawHref;
708
+ url.searchParams.set('edit_mode', 'true');
709
+ if (CMS_PARENT_ORIGIN) {
710
+ url.searchParams.set('cms_parent_origin', CMS_PARENT_ORIGIN);
711
+ }
712
+ return url.toString();
713
+ } catch (_) {
714
+ return rawHref;
715
+ }
716
+ }
717
+
718
+ function preserveEditParamsOnNavigation(e) {
719
+ if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {
720
+ return;
721
+ }
722
+
723
+ var anchor = e.target.closest('a[href]');
724
+ if (!anchor || (anchor.target && anchor.target !== '_self') || anchor.hasAttribute('download')) {
725
+ return;
726
+ }
727
+
728
+ var href = anchor.getAttribute('href');
729
+ var decorated = decoratePreviewUrl(href);
730
+ if (decorated && decorated !== href) {
731
+ anchor.setAttribute('href', decorated);
732
+ }
733
+ }
734
+
641
735
  function postToParent(message) {
642
736
  if (!CMS_PARENT_ORIGIN || !window.parent || window.parent === window) {
643
737
  return;
@@ -756,6 +850,8 @@ function generateCmsOverlayScript(cmsParentOrigin) {
756
850
  });
757
851
 
758
852
  document.addEventListener('click', function(e) {
853
+ preserveEditParamsOnNavigation(e);
854
+
759
855
  if (toolbarVisible && !e.target.closest('[data-cms-toolbar]')) {
760
856
  var block = e.target.closest('[data-cms-block]');
761
857
  if (!block || block.getAttribute('data-block-id') !== currentBlockId) {
@@ -785,23 +881,23 @@ function generateCmsOverlayScript(cmsParentOrigin) {
785
881
  contentPath: null
786
882
  });
787
883
  }
788
- });
884
+ }, true);
789
885
 
790
886
  toolbar.querySelector('.move-up').addEventListener('click', function() {
791
887
  if (!currentBlockId) return;
792
- postToParent({ type: 'cms-block-move', blockId: currentBlockId, direction: 'up' });
888
+ postToParent({ type: 'cms-block-action', action: 'move-up', blockId: currentBlockId });
793
889
  hideToolbar();
794
890
  });
795
891
 
796
892
  toolbar.querySelector('.move-down').addEventListener('click', function() {
797
893
  if (!currentBlockId) return;
798
- postToParent({ type: 'cms-block-move', blockId: currentBlockId, direction: 'down' });
894
+ postToParent({ type: 'cms-block-action', action: 'move-down', blockId: currentBlockId });
799
895
  hideToolbar();
800
896
  });
801
897
 
802
898
  toolbar.querySelector('.delete').addEventListener('click', function() {
803
899
  if (!currentBlockId) return;
804
- postToParent({ type: 'cms-block-delete', blockId: currentBlockId });
900
+ postToParent({ type: 'cms-block-action', action: 'delete', blockId: currentBlockId });
805
901
  hideToolbar();
806
902
  });
807
903
 
@@ -895,6 +991,27 @@ function getCmsParentTargetOrigin(preferredCmsUrl, explicitParentOrigin) {
895
991
 
896
992
  // lib/block-renderer.tsx
897
993
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
994
+ function extractContentValues(content, basePath = []) {
995
+ const map = /* @__PURE__ */ new Map();
996
+ function walk(obj, path) {
997
+ if (typeof obj === "string" && obj.trim() !== "") {
998
+ const contentPath = path.join(".");
999
+ const existing = map.get(obj) || [];
1000
+ existing.push({ contentPath, value: obj });
1001
+ map.set(obj, existing);
1002
+ } else if (Array.isArray(obj)) {
1003
+ for (let index = 0; index < obj.length; index++) {
1004
+ walk(obj[index], [...path, String(index)]);
1005
+ }
1006
+ } else if (obj && typeof obj === "object") {
1007
+ for (const [key, value] of Object.entries(obj)) {
1008
+ walk(value, [...path, key]);
1009
+ }
1010
+ }
1011
+ }
1012
+ walk(content, basePath);
1013
+ return map;
1014
+ }
898
1015
  function CmsEditableInit({
899
1016
  cmsUrl,
900
1017
  cmsParentOrigin
@@ -943,6 +1060,7 @@ function BlockRenderer({
943
1060
  block,
944
1061
  registry,
945
1062
  disableEditable,
1063
+ enableContentEditable,
946
1064
  routeParams,
947
1065
  path
948
1066
  }) {
@@ -958,6 +1076,7 @@ function BlockRenderer({
958
1076
  if (disableEditable) {
959
1077
  return component;
960
1078
  }
1079
+ const contentEntries = enableContentEditable ? [...extractContentValues(block.content).values()].flat().map(({ value, contentPath }) => ({ v: value, p: contentPath })) : void 0;
961
1080
  return /* @__PURE__ */ jsxs(Fragment, { children: [
962
1081
  /* @__PURE__ */ jsx(
963
1082
  "span",
@@ -965,6 +1084,7 @@ function BlockRenderer({
965
1084
  "data-cms-sentinel": "",
966
1085
  "data-block-id": block.id,
967
1086
  "data-block-type": block.type,
1087
+ "data-content-entries": contentEntries ? JSON.stringify(contentEntries) : void 0,
968
1088
  style: { display: "none" },
969
1089
  "aria-hidden": "true"
970
1090
  }
@@ -998,7 +1118,7 @@ async function renderParametricRoute({
998
1118
  apiKey,
999
1119
  cmsUrl,
1000
1120
  websiteId: providedWebsiteId
1001
- }) {
1121
+ }, { preview = false } = {}) {
1002
1122
  const websiteId = getWebsiteId(providedWebsiteId);
1003
1123
  const { slug } = "then" in params ? await params : params;
1004
1124
  const resolvedSearchParams = searchParams && "then" in searchParams ? await searchParams : searchParams;
@@ -1013,10 +1133,11 @@ async function renderParametricRoute({
1013
1133
  }
1014
1134
  }
1015
1135
  }
1016
- const editModeParam = resolvedSearchParams?.edit_mode;
1017
- const editMode = editModeParam === true || editModeParam === "true" || editModeParam === "1";
1018
1136
  const cmsParentOriginParam = resolvedSearchParams?.cms_parent_origin;
1019
1137
  const cmsParentOrigin = typeof cmsParentOriginParam === "boolean" ? void 0 : Array.isArray(cmsParentOriginParam) ? cmsParentOriginParam[0] : cmsParentOriginParam;
1138
+ const previewMode = preview === true;
1139
+ const editModeParam = resolvedSearchParams?.edit_mode;
1140
+ const editMode = previewMode || editModeParam === true || editModeParam === "true" || editModeParam === "1";
1020
1141
  const rawPath = `/${slug.join("/")}`;
1021
1142
  const path = normalizePath(rawPath);
1022
1143
  if (/\.[a-zA-Z0-9]+$/.test(path)) {
@@ -1056,7 +1177,9 @@ async function renderParametricRoute({
1056
1177
  ]);
1057
1178
  const blocks = [];
1058
1179
  for (const block of blockResults) {
1059
- if (!block || block.published_content === null) continue;
1180
+ if (!block) continue;
1181
+ const blockContent = previewMode ? block.draft_content : block.published_content;
1182
+ if (blockContent == null) continue;
1060
1183
  let content = null;
1061
1184
  if (aiPreviewIndex !== null) {
1062
1185
  const generatedBlock = generatedBlocks[block.id];
@@ -1066,7 +1189,7 @@ async function renderParametricRoute({
1066
1189
  content = variants[variantIndex].content;
1067
1190
  }
1068
1191
  }
1069
- content = content ?? block.published_content;
1192
+ content = content ?? blockContent;
1070
1193
  if (!content) continue;
1071
1194
  if (block.schema_name === "article") {
1072
1195
  const article = normalizeArticleContent(content);
@@ -1114,6 +1237,7 @@ async function renderParametricRoute({
1114
1237
  block,
1115
1238
  registry: registry ?? {},
1116
1239
  disableEditable: !editMode,
1240
+ enableContentEditable: previewMode,
1117
1241
  routeParams,
1118
1242
  path
1119
1243
  },
@@ -1142,6 +1266,16 @@ async function ParametricRoutePage(props) {
1142
1266
  }
1143
1267
  return result.node;
1144
1268
  }
1269
+ async function ParametricRoutePreviewPage(props) {
1270
+ const result = await renderParametricRoute(props, { preview: true });
1271
+ if (result.status === "not_found") {
1272
+ notFound();
1273
+ }
1274
+ if (result.status === "error") {
1275
+ throw result.error;
1276
+ }
1277
+ return result.node;
1278
+ }
1145
1279
  async function generateMetadata({
1146
1280
  params,
1147
1281
  apiKey,
@@ -1168,6 +1302,7 @@ async function generateMetadata({
1168
1302
  }
1169
1303
  }
1170
1304
  export {
1305
+ ParametricRoutePreviewPage,
1171
1306
  ParametricRoutePage as default,
1172
1307
  generateMetadata
1173
1308
  };