cms-renderer 0.6.12 → 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.
@@ -1,4 +1,4 @@
1
- import * as React from 'react';
1
+ import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
 
4
4
  interface DocsMarkdownProps {
@@ -13,6 +13,6 @@ interface DocsMarkdownProps {
13
13
  }
14
14
  declare function markdownStartsWithHeading(markdown: string): boolean;
15
15
  declare function initMarkdown(): Promise<void>;
16
- declare function DocsMarkdown({ content, className, renderImage, }: DocsMarkdownProps): Promise<React.JSX.Element>;
16
+ declare function DocsMarkdown({ content, className, renderImage, }: DocsMarkdownProps): Promise<react.JSX.Element>;
17
17
 
18
18
  export { DocsMarkdown, type DocsMarkdownProps, initMarkdown, markdownStartsWithHeading };
@@ -25,6 +25,19 @@ type ParametricRouteProps = CmsConfig & {
25
25
  };
26
26
  registry?: Partial<BlockComponentRegistry>;
27
27
  };
28
+ /**
29
+ * Render-time options that are NOT part of the page props surface. `preview` is
30
+ * supplied only by the dedicated preview page wrapper (`ParametricRoutePreviewPage`),
31
+ * never by query params, so the production page can never accidentally opt into
32
+ * edit overlays or draft content.
33
+ */
34
+ type RenderParametricRouteOptions = {
35
+ /**
36
+ * Renders the preview experience: forces the edit-mode overlay/hovers and pulls
37
+ * block content from `draft_content` instead of `published_content`.
38
+ */
39
+ preview?: boolean;
40
+ };
28
41
  type ParametricRouteResult = {
29
42
  status: 'ok';
30
43
  node: ReactNode;
@@ -40,6 +53,6 @@ declare function getWebsiteId(providedWebsiteId?: string): string;
40
53
  * Renders CMS-backed blocks for a multi-segment path. Maps NOT_FOUND-style API
41
54
  * outcomes to `{ status: 'not_found' }` instead of throwing.
42
55
  */
43
- declare function renderParametricRoute({ params, searchParams, registry, apiKey, cmsUrl, websiteId: providedWebsiteId, }: ParametricRouteProps): Promise<ParametricRouteResult>;
56
+ declare function renderParametricRoute({ params, searchParams, registry, apiKey, cmsUrl, websiteId: providedWebsiteId, }: ParametricRouteProps, { preview }?: RenderParametricRouteOptions): Promise<ParametricRouteResult>;
44
57
 
45
- export { type ParametricRouteProps, type ParametricRouteResult, getWebsiteId, renderParametricRoute };
58
+ export { type ParametricRouteProps, type ParametricRouteResult, type RenderParametricRouteOptions, getWebsiteId, renderParametricRoute };
@@ -414,7 +414,7 @@ function generateCmsOverlayScript(cmsParentOrigin) {
414
414
  pointer-events: none;
415
415
  z-index: 99998;
416
416
  }
417
- #cms-overlay-root > * {
417
+ #cms-overlay-root > *:not(.cms-block-toolbar) {
418
418
  pointer-events: none;
419
419
  }
420
420
  .cms-block-outline {
@@ -511,9 +511,69 @@ function generateCmsOverlayScript(cmsParentOrigin) {
511
511
  blockEl.setAttribute('data-cms-block', '');
512
512
  blockEl.setAttribute('data-block-id', blockId);
513
513
  blockEl.setAttribute('data-block-type', blockType);
514
+
515
+ injectEditableSpans(
516
+ blockEl,
517
+ blockId,
518
+ blockType,
519
+ sentinel.getAttribute('data-content-entries')
520
+ );
514
521
  });
515
522
  }
516
523
 
524
+ function injectEditableSpans(blockEl, blockId, blockType, rawEntries) {
525
+ if (!rawEntries || blockEl.querySelector('[data-cms-editable][data-content-path]')) return;
526
+
527
+ var entries;
528
+ try {
529
+ entries = JSON.parse(rawEntries);
530
+ } catch (_) {
531
+ return;
532
+ }
533
+ if (!Array.isArray(entries) || entries.length === 0) return;
534
+
535
+ var used = {};
536
+ var walker = document.createTreeWalker(blockEl, NodeFilter.SHOW_TEXT, null);
537
+ var textNodes = [];
538
+ var node = walker.nextNode();
539
+ while (node !== null) {
540
+ if (node.nodeValue && node.nodeValue.trim()) {
541
+ textNodes.push(node);
542
+ }
543
+ node = walker.nextNode();
544
+ }
545
+
546
+ for (var i = 0; i < textNodes.length; i++) {
547
+ var textNode = textNodes[i];
548
+ var parentEl = textNode.parentElement;
549
+ if (!parentEl || parentEl.closest('svg') || parentEl.closest('[data-cms-editable]')) {
550
+ continue;
551
+ }
552
+
553
+ var text = textNode.nodeValue;
554
+ if (!text) continue;
555
+
556
+ for (var j = 0; j < entries.length; j++) {
557
+ var entry = entries[j];
558
+ if (!entry || typeof entry.v !== 'string' || typeof entry.p !== 'string' || used[entry.p]) {
559
+ continue;
560
+ }
561
+ if (text.trim() !== entry.v.trim()) continue;
562
+
563
+ used[entry.p] = true;
564
+ var span = document.createElement('span');
565
+ span.setAttribute('data-cms-editable', '');
566
+ span.setAttribute('data-block-id', blockId);
567
+ span.setAttribute('data-block-type', blockType);
568
+ span.setAttribute('data-content-path', entry.p);
569
+ span.setAttribute('contenteditable', 'true');
570
+ parentEl.insertBefore(span, textNode);
571
+ span.appendChild(textNode);
572
+ break;
573
+ }
574
+ }
575
+ }
576
+
517
577
  stampBlockElements();
518
578
 
519
579
  var stampObserver = new MutationObserver(function(mutations) {
@@ -859,6 +919,27 @@ function getCmsParentTargetOrigin(preferredCmsUrl, explicitParentOrigin) {
859
919
 
860
920
  // lib/block-renderer.tsx
861
921
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
922
+ function extractContentValues(content, basePath = []) {
923
+ const map = /* @__PURE__ */ new Map();
924
+ function walk(obj, path) {
925
+ if (typeof obj === "string" && obj.trim() !== "") {
926
+ const contentPath = path.join(".");
927
+ const existing = map.get(obj) || [];
928
+ existing.push({ contentPath, value: obj });
929
+ map.set(obj, existing);
930
+ } else if (Array.isArray(obj)) {
931
+ for (let index = 0; index < obj.length; index++) {
932
+ walk(obj[index], [...path, String(index)]);
933
+ }
934
+ } else if (obj && typeof obj === "object") {
935
+ for (const [key, value] of Object.entries(obj)) {
936
+ walk(value, [...path, key]);
937
+ }
938
+ }
939
+ }
940
+ walk(content, basePath);
941
+ return map;
942
+ }
862
943
  function CmsEditableInit({
863
944
  cmsUrl,
864
945
  cmsParentOrigin
@@ -907,6 +988,7 @@ function BlockRenderer({
907
988
  block,
908
989
  registry,
909
990
  disableEditable,
991
+ enableContentEditable,
910
992
  routeParams,
911
993
  path
912
994
  }) {
@@ -922,6 +1004,7 @@ function BlockRenderer({
922
1004
  if (disableEditable) {
923
1005
  return component;
924
1006
  }
1007
+ const contentEntries = enableContentEditable ? [...extractContentValues(block.content).values()].flat().map(({ value, contentPath }) => ({ v: value, p: contentPath })) : void 0;
925
1008
  return /* @__PURE__ */ jsxs(Fragment, { children: [
926
1009
  /* @__PURE__ */ jsx(
927
1010
  "span",
@@ -929,6 +1012,7 @@ function BlockRenderer({
929
1012
  "data-cms-sentinel": "",
930
1013
  "data-block-id": block.id,
931
1014
  "data-block-type": block.type,
1015
+ "data-content-entries": contentEntries ? JSON.stringify(contentEntries) : void 0,
932
1016
  style: { display: "none" },
933
1017
  "aria-hidden": "true"
934
1018
  }
@@ -1010,7 +1094,7 @@ async function renderParametricRoute({
1010
1094
  apiKey,
1011
1095
  cmsUrl,
1012
1096
  websiteId: providedWebsiteId
1013
- }) {
1097
+ }, { preview = false } = {}) {
1014
1098
  const websiteId = getWebsiteId(providedWebsiteId);
1015
1099
  const { slug } = "then" in params ? await params : params;
1016
1100
  const resolvedSearchParams = searchParams && "then" in searchParams ? await searchParams : searchParams;
@@ -1025,10 +1109,11 @@ async function renderParametricRoute({
1025
1109
  }
1026
1110
  }
1027
1111
  }
1028
- const editModeParam = resolvedSearchParams?.edit_mode;
1029
- const editMode = editModeParam === true || editModeParam === "true" || editModeParam === "1";
1030
1112
  const cmsParentOriginParam = resolvedSearchParams?.cms_parent_origin;
1031
1113
  const cmsParentOrigin = typeof cmsParentOriginParam === "boolean" ? void 0 : Array.isArray(cmsParentOriginParam) ? cmsParentOriginParam[0] : cmsParentOriginParam;
1114
+ const previewMode = preview === true;
1115
+ const editModeParam = resolvedSearchParams?.edit_mode;
1116
+ const editMode = previewMode || editModeParam === true || editModeParam === "true" || editModeParam === "1";
1032
1117
  const rawPath = `/${slug.join("/")}`;
1033
1118
  const path = normalizePath(rawPath);
1034
1119
  if (/\.[a-zA-Z0-9]+$/.test(path)) {
@@ -1068,7 +1153,9 @@ async function renderParametricRoute({
1068
1153
  ]);
1069
1154
  const blocks = [];
1070
1155
  for (const block of blockResults) {
1071
- if (!block || block.published_content === null) continue;
1156
+ if (!block) continue;
1157
+ const blockContent = previewMode ? block.draft_content : block.published_content;
1158
+ if (blockContent == null) continue;
1072
1159
  let content = null;
1073
1160
  if (aiPreviewIndex !== null) {
1074
1161
  const generatedBlock = generatedBlocks[block.id];
@@ -1078,7 +1165,7 @@ async function renderParametricRoute({
1078
1165
  content = variants[variantIndex].content;
1079
1166
  }
1080
1167
  }
1081
- content = content ?? block.published_content;
1168
+ content = content ?? blockContent;
1082
1169
  if (!content) continue;
1083
1170
  if (block.schema_name === "article") {
1084
1171
  const article = normalizeArticleContent(content);
@@ -1126,6 +1213,7 @@ async function renderParametricRoute({
1126
1213
  block,
1127
1214
  registry: registry ?? {},
1128
1215
  disableEditable: !editMode,
1216
+ enableContentEditable: previewMode,
1129
1217
  routeParams,
1130
1218
  path
1131
1219
  },