@bettercms-ai/ui 0.15.1 → 0.17.0

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.
package/dist/registry.js CHANGED
@@ -29,10 +29,136 @@ function Heading({
29
29
  }
30
30
 
31
31
  // src/blocks/portableText.tsx
32
- import React from "react";
32
+ import React2 from "react";
33
33
  import { PortableText as PT } from "@portabletext/react";
34
34
 
35
+ // src/renderer.tsx
36
+ import { RESPONSIVE_CSS, hiddenEverywhere, hideOnAttr, splitButtonStyle, usesHideOn, visibleBlocks, windowState } from "@bettercms-ai/types";
37
+
38
+ // src/blockStyle.ts
39
+ import { blockStyleToCss } from "@bettercms-ai/types";
40
+ function blockStyleToReactStyle(style) {
41
+ return blockStyleToCss(style);
42
+ }
43
+
44
+ // src/renderer.tsx
45
+ import "react";
46
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
47
+ var CONTAINER_TYPES = /* @__PURE__ */ new Set(["columns", "section", "slider", "tabs"]);
48
+ function styleOf(block) {
49
+ if (block.type !== "button") return { wrapper: blockStyleToReactStyle(block.style), paint: {} };
50
+ const { placement, paint } = splitButtonStyle(block.style);
51
+ return { wrapper: blockStyleToReactStyle(placement), paint: paint ? { style: paint } : {} };
52
+ }
53
+ function withHide(block, wrapperStyle, el, stampAt) {
54
+ const hide = hideOnAttr(block.style);
55
+ const state = stampAt ? windowState(block, stampAt) : "live";
56
+ const hidden = !!stampAt && hiddenEverywhere(block);
57
+ if (!wrapperStyle && !hide && state === "live" && !hidden) return el;
58
+ return /* @__PURE__ */ jsx2("div", { style: wrapperStyle, ...hide ? { "data-bcms-hide": hide } : {}, ...state === "live" ? {} : { "data-bcms-window": state }, ...hidden ? { "data-bcms-hidden": "all" } : {}, children: el });
59
+ }
60
+ function bindingProps(block, annotate) {
61
+ if (!annotate || !block.id) return {};
62
+ switch (block.type) {
63
+ case "heading":
64
+ case "button":
65
+ return { "data-bcms-field": `${block.id}__text`, "data-bcms-kind": "text" };
66
+ case "text":
67
+ case "richtext":
68
+ return { "data-bcms-field": `${block.id}__html`, "data-bcms-kind": "richtext" };
69
+ case "image":
70
+ return { "data-bcms-field": `${block.id}__src`, "data-bcms-kind": "image" };
71
+ default:
72
+ return {};
73
+ }
74
+ }
75
+ function resolveBlock(block, className, annotate = false, stampAt) {
76
+ const entry = BLOCK_REGISTRY[block.type];
77
+ if (!entry) {
78
+ return /* @__PURE__ */ jsxs(
79
+ "div",
80
+ {
81
+ className: `w-full p-4 border border-dashed border-red-400 bg-red-50 text-red-600 text-sm rounded ${className ?? ""}`,
82
+ children: [
83
+ "Unknown block type: ",
84
+ /* @__PURE__ */ jsx2("code", { children: block.type })
85
+ ]
86
+ }
87
+ );
88
+ }
89
+ const Component = entry.component;
90
+ const { wrapper: blockStyle, paint } = styleOf(block);
91
+ const el = /* @__PURE__ */ jsx2(Component, { ...block.props, ...paint, className, ...bindingProps(block, annotate) });
92
+ return withHide(block, blockStyle, el, stampAt);
93
+ }
94
+ function BlockRenderer({
95
+ block,
96
+ className = "",
97
+ renderBlock,
98
+ annotate = false,
99
+ stampAt
100
+ }) {
101
+ const entry = BLOCK_REGISTRY[block.type];
102
+ if (!entry) {
103
+ return /* @__PURE__ */ jsxs(
104
+ "div",
105
+ {
106
+ className: `w-full p-4 border border-dashed border-red-400 bg-red-50 text-red-600 text-sm rounded ${className}`,
107
+ children: [
108
+ "Unknown block type: ",
109
+ /* @__PURE__ */ jsx2("code", { children: block.type })
110
+ ]
111
+ }
112
+ );
113
+ }
114
+ const Component = entry.component;
115
+ const containerProps = CONTAINER_TYPES.has(block.type) ? { renderBlock: renderBlock ?? ((child) => resolveBlock(child, void 0, annotate, stampAt)) } : {};
116
+ const { wrapper: blockStyle, paint } = styleOf(block);
117
+ const el = /* @__PURE__ */ jsx2(
118
+ Component,
119
+ {
120
+ ...block.props,
121
+ ...paint,
122
+ className,
123
+ ...containerProps,
124
+ ...bindingProps(block, annotate)
125
+ }
126
+ );
127
+ return withHide(block, blockStyle, el, stampAt);
128
+ }
129
+ function BlocksRenderer({
130
+ blocks: all,
131
+ className = "",
132
+ renderBlock,
133
+ annotate = false,
134
+ now
135
+ }) {
136
+ const at = now ?? /* @__PURE__ */ new Date();
137
+ const blocks = annotate ? all : visibleBlocks(all, at);
138
+ if (blocks.length === 0) return null;
139
+ return /* @__PURE__ */ jsxs("div", { className, children: [
140
+ usesHideOn(blocks) && /* @__PURE__ */ jsx2("style", { dangerouslySetInnerHTML: { __html: RESPONSIVE_CSS } }),
141
+ blocks.map((block) => /* @__PURE__ */ jsx2(
142
+ BlockRenderer,
143
+ {
144
+ block,
145
+ renderBlock,
146
+ annotate,
147
+ stampAt: annotate ? at : void 0
148
+ },
149
+ block.id
150
+ ))
151
+ ] });
152
+ }
153
+
35
154
  // ../richtext/src/types.ts
155
+ function pullQuoteHref(link) {
156
+ if (typeof link === "string") return link.trim();
157
+ if (link && typeof link === "object" && typeof link.href === "string") {
158
+ return link.href.trim();
159
+ }
160
+ return "";
161
+ }
36
162
  var PT_TYPES = {
37
163
  /** A generic embedded block: `{schemaKey, schemaLabel, fields}`, the successor to Lexical's BlockNode. */
38
164
  block: "bcmsBlock",
@@ -50,6 +176,10 @@ var PT_TYPES = {
50
176
  video: "video",
51
177
  /** A downloadable document — a PDF, a spec sheet. `<a href download>`, nothing more. */
52
178
  file: "file",
179
+ /** A pull quote — see `PortableTextPullQuote`. The V2 kit's `<figure data-rt="pullquote">`. */
180
+ pullQuote: "pullQuote",
181
+ /** Key takeaways — see `PortableTextKeyTakeaways`. The V2 kit's `<aside data-rt="takeaways">`. */
182
+ keyTakeaways: "keyTakeaways",
53
183
  /**
54
184
  * A table. Spelled `table`, unprefixed, because that is what every other Portable Text
55
185
  * producer calls one — the Sanity-shaped `{_type:"table", rows}` this platform used to refuse
@@ -74,8 +204,11 @@ var STORABLE_BLOCK_TYPES = /* @__PURE__ */ new Set([
74
204
  PT_TYPES.divider,
75
205
  PT_TYPES.mention,
76
206
  PT_TYPES.video,
77
- PT_TYPES.file
207
+ PT_TYPES.file,
208
+ PT_TYPES.pullQuote,
209
+ PT_TYPES.keyTakeaways
78
210
  ]);
211
+ var EMBEDDED_COMPONENT_KEY = "component";
79
212
 
80
213
  // ../richtext/src/styled.ts
81
214
  var STYLED_TAGS = [
@@ -246,7 +379,8 @@ function normalizeCell(raw, at, mint, read) {
246
379
  content: Array.isArray(raw.content) ? read(raw.content, inner) : textContent(typeof raw.text === "string" ? raw.text : "", inner),
247
380
  ...raw.header === true ? { header: true } : {},
248
381
  ...span === void 0 ? {} : { span },
249
- ...rowSpan === void 0 ? {} : { rowSpan }
382
+ ...rowSpan === void 0 ? {} : { rowSpan },
383
+ ...typeof raw.group === "string" ? { group: raw.group } : {}
250
384
  };
251
385
  }
252
386
  function normalizeRow(raw, at, mint, read) {
@@ -254,7 +388,8 @@ function normalizeRow(raw, at, mint, read) {
254
388
  return {
255
389
  _key: isRecord(raw) && isValidKey(raw._key) ? raw._key : mint("r", at),
256
390
  cells: cells.map((c, col) => normalizeCell(c, [at, col], mint, read)),
257
- ...isRecord(raw) && raw.head === true ? { head: true } : {}
391
+ ...isRecord(raw) && raw.head === true ? { head: true } : {},
392
+ ...isRecord(raw) && typeof raw.group === "string" ? { group: raw.group } : {}
258
393
  };
259
394
  }
260
395
  function builtInTableToTable(node, key = randomKey) {
@@ -299,24 +434,93 @@ function migrateTable(node, key = randomKey, read = asIs) {
299
434
  rows: rows.map((r, i) => normalizeRow(r, i, mint, read)),
300
435
  // Carried through: a migrated table that an author saves back is still the same table, and
301
436
  // dropping the flag on that save would change the page the next time it renders.
302
- ...node.legacy === true ? { legacy: true } : {}
437
+ ...node.legacy === true ? { legacy: true } : {},
438
+ ...tableExtras(node)
439
+ };
440
+ }
441
+ function tableExtras(node) {
442
+ const groups = (list) => Array.isArray(list) ? list.filter((g) => isRecord(g) && typeof g._key === "string" && g._key !== "").map((g) => ({ _key: g._key, label: typeof g.label === "string" ? g.label : "" })) : void 0;
443
+ const rowGroups = groups(node.rowGroups);
444
+ const columnGroups = groups(node.columnGroups);
445
+ return {
446
+ ...typeof node.caption === "string" ? { caption: node.caption } : {},
447
+ ...typeof node.notes === "string" ? { notes: node.notes } : {},
448
+ ...rowGroups ? { rowGroups } : {},
449
+ ...columnGroups ? { columnGroups } : {}
450
+ };
451
+ }
452
+ function tableGroupLayout(table) {
453
+ const rows = Array.isArray(table.rows) ? table.rows : [];
454
+ const labels = (list) => new Map(
455
+ (Array.isArray(list) ? list : []).filter((g) => isRecord(g) && typeof g._key === "string").map((g) => [g._key, typeof g.label === "string" ? g.label : ""])
456
+ );
457
+ const cellsOf = (row) => isRecord(row) && Array.isArray(row.cells) ? row.cells : [];
458
+ const spanOf2 = (cell) => isRecord(cell) && typeof cell.span === "number" && Number.isInteger(cell.span) && cell.span > 1 ? cell.span : 1;
459
+ const width = rows.reduce((max, row) => Math.max(max, cellsOf(row).reduce((n, c) => n + spanOf2(c), 0)), 0);
460
+ const colLabels = labels(table.columnGroups);
461
+ const columns = [];
462
+ for (const cell of cellsOf(rows[0])) {
463
+ const key = isRecord(cell) && typeof cell.group === "string" && colLabels.has(cell.group) ? cell.group : void 0;
464
+ const last = columns[columns.length - 1];
465
+ if (last && last.key === key) last.width += spanOf2(cell);
466
+ else columns.push({ width: spanOf2(cell), ...key === void 0 ? {} : { key, label: colLabels.get(key) } });
467
+ }
468
+ const rowLabels = labels(table.rowGroups);
469
+ const headCount = rows.findIndex((r) => !isRecord(r) || r.head !== true);
470
+ const body = headCount === -1 ? [] : rows.slice(headCount);
471
+ const runs = [];
472
+ for (const row of body) {
473
+ const key = isRecord(row) && typeof row.group === "string" && rowLabels.has(row.group) ? row.group : void 0;
474
+ const last = runs[runs.length - 1];
475
+ if (last && last.key === key) last.rows.push(row);
476
+ else runs.push({ rows: [row], ...key === void 0 ? {} : { key, label: rowLabels.get(key) } });
477
+ }
478
+ return {
479
+ columns: columns.some((r) => r.key !== void 0) ? columns : [],
480
+ rows: runs.some((r) => r.key !== void 0) ? runs : [],
481
+ width
303
482
  };
304
483
  }
305
484
 
306
- // ../richtext/src/read.ts
485
+ // ../richtext/src/key-takeaways.ts
307
486
  var isRecord2 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
487
+ function keyTakeawaysView(value) {
488
+ const v = isRecord2(value) ? value : {};
489
+ const title = typeof v.title === "string" ? v.title.trim() : "";
490
+ const points = (Array.isArray(v.points) ? v.points : []).map((p) => typeof p === "string" ? p.trim() : "").filter(Boolean);
491
+ return { title, points };
492
+ }
493
+
494
+ // ../richtext/src/component-copy.ts
495
+ var isRecord3 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
496
+ var fieldsOf = (value) => isRecord3(value) && value.schemaKey === EMBEDDED_COMPONENT_KEY && isRecord3(value.fields) ? value.fields : null;
497
+ function detachedComponentCopy(value) {
498
+ const f = fieldsOf(value);
499
+ if (!f || f.detached !== true || !Array.isArray(f.blocks)) return null;
500
+ return {
501
+ id: isRecord3(value) && typeof value._key === "string" ? value._key : "",
502
+ componentId: typeof f.componentId === "string" ? f.componentId : "",
503
+ props: isRecord3(f.props) ? f.props : {},
504
+ blocks: f.blocks,
505
+ ...typeof f.sourcePublishedAt === "string" ? { sourcePublishedAt: f.sourcePublishedAt } : {},
506
+ ...typeof f.variant === "string" ? { variant: f.variant } : {}
507
+ };
508
+ }
509
+
510
+ // ../richtext/src/read.ts
511
+ var isRecord4 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
308
512
  function normalizePortableText(value, key = randomKey) {
309
513
  if (!Array.isArray(value)) return [];
310
514
  return value.flatMap((raw) => {
311
- if (!isRecord2(raw) || typeof raw._type !== "string") return [];
515
+ if (!isRecord4(raw) || typeof raw._type !== "string") return [];
312
516
  const table = migrateTable(raw, key, (v, cellKey) => normalizePortableText(v, cellKey));
313
517
  if (table) return [table];
314
- const node = { ...raw, _key: isRecord2(raw) && typeof raw._key === "string" && raw._key ? raw._key : key() };
518
+ const node = { ...raw, _key: isRecord4(raw) && typeof raw._key === "string" && raw._key ? raw._key : key() };
315
519
  if (node._type === "block") {
316
520
  if (!Array.isArray(node.markDefs)) node.markDefs = [];
317
521
  if (typeof node.style !== "string") node.style = "normal";
318
522
  node.children = (Array.isArray(node.children) ? node.children : []).map((c) => {
319
- if (!isRecord2(c)) return c;
523
+ if (!isRecord4(c)) return c;
320
524
  const child = { ...c, _key: typeof c._key === "string" && c._key ? c._key : key() };
321
525
  if (child._type === "span" && !Array.isArray(child.marks)) child.marks = [];
322
526
  return child;
@@ -326,14 +530,14 @@ function normalizePortableText(value, key = randomKey) {
326
530
  });
327
531
  }
328
532
  function readStructuredContent(props) {
329
- if (!isRecord2(props)) return null;
533
+ if (!isRecord4(props)) return null;
330
534
  const candidate = props.content ?? props.value;
331
- const arr = Array.isArray(candidate) ? candidate : isRecord2(candidate) ? Array.isArray(candidate.content) ? candidate.content : Array.isArray(candidate.value) ? candidate.value : null : null;
535
+ const arr = Array.isArray(candidate) ? candidate : isRecord4(candidate) ? Array.isArray(candidate.content) ? candidate.content : Array.isArray(candidate.value) ? candidate.value : null : null;
332
536
  return arr && arr.length > 0 ? arr : null;
333
537
  }
334
538
 
335
539
  // src/blocks/portableText.tsx
336
- import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
540
+ import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
337
541
  var TOKEN = /^[a-z0-9-]+$/i;
338
542
  function styleObject(css) {
339
543
  const out = {};
@@ -352,42 +556,63 @@ function Bound({
352
556
  nodeKey,
353
557
  children
354
558
  }) {
355
- if (!base || !nodeKey) return /* @__PURE__ */ jsx2(Fragment, { children });
356
- return /* @__PURE__ */ jsx2("div", { "data-bcms-field": `${base}#${nodeKey}`, "data-bcms-kind": "richtext", children });
559
+ if (!base || !nodeKey) return /* @__PURE__ */ jsx3(Fragment, { children });
560
+ return /* @__PURE__ */ jsx3("div", { "data-bcms-field": `${base}#${nodeKey}`, "data-bcms-kind": "richtext", children });
357
561
  }
358
562
  function Table({ value }) {
359
- const components = React.useMemo(buildComponents, []);
360
- const inline = React.useMemo(
361
- () => ({ ...components, block: { normal: ({ children }) => /* @__PURE__ */ jsx2(Fragment, { children }) } }),
563
+ const components = React2.useMemo(buildComponents, []);
564
+ const inline = React2.useMemo(
565
+ () => ({ ...components, block: { normal: ({ children }) => /* @__PURE__ */ jsx3(Fragment, { children }) } }),
362
566
  [components]
363
567
  );
364
568
  const rows = Array.isArray(value?.rows) ? value.rows : [];
365
569
  if (rows.length === 0) return null;
366
570
  const cells = (row) => (Array.isArray(row?.cells) ? row.cells : []).map((cell) => {
367
571
  const Tag = cell?.header === true ? "th" : "td";
368
- const span = typeof cell?.span === "number" && cell.span > 1 ? cell.span : void 0;
572
+ const span2 = typeof cell?.span === "number" && cell.span > 1 ? cell.span : void 0;
369
573
  const rowSpan = typeof cell?.rowSpan === "number" && cell.rowSpan > 1 ? cell.rowSpan : void 0;
370
574
  const content = Array.isArray(cell?.content) ? cell.content : [];
371
575
  const lone = content.length === 1 && content[0]._type === "block" && content[0].style === "normal" && !content[0].listItem;
372
- return /* @__PURE__ */ jsx2(Tag, { ...span ? { colSpan: span } : {}, ...rowSpan ? { rowSpan } : {}, children: /* @__PURE__ */ jsx2(PT, { value: content, components: lone ? inline : components }) }, cell?._key);
576
+ return /* @__PURE__ */ jsx3(Tag, { ...span2 ? { colSpan: span2 } : {}, ...rowSpan ? { rowSpan } : {}, children: /* @__PURE__ */ jsx3(PT, { value: content, components: lone ? inline : components }) }, cell?._key);
373
577
  });
374
- const rendered = rows.map((row) => /* @__PURE__ */ jsx2("tr", { children: cells(row) }, row?._key));
578
+ const rowEl = (row) => /* @__PURE__ */ jsx3("tr", { children: cells(row) }, row?._key);
375
579
  const headCount = rows.findIndex((r) => r?.head !== true);
376
580
  const head = headCount === -1 ? rows.length : headCount;
377
- return /* @__PURE__ */ jsxs("table", { ...value.legacy === true ? { className: "bcms-table" } : {}, children: [
378
- head > 0 && /* @__PURE__ */ jsx2("thead", { children: rendered.slice(0, head) }),
379
- head < rows.length && /* @__PURE__ */ jsx2("tbody", { children: rendered.slice(head) })
581
+ const layout = tableGroupLayout(value);
582
+ const caption = typeof value.caption === "string" ? value.caption.trim() : "";
583
+ const notes = typeof value.notes === "string" ? value.notes.trim() : "";
584
+ const notesId = notes && typeof value._key === "string" && value._key ? `${value._key}-notes` : void 0;
585
+ const span = (n) => n > 1 ? n : void 0;
586
+ const groupRow = layout.columns.length ? /* @__PURE__ */ jsx3("tr", { children: layout.columns.flatMap(
587
+ (run, i) => run.label === void 0 ? Array.from({ length: run.width }, (_, k) => /* @__PURE__ */ jsx3("td", {}, `${i}-${k}`)) : [/* @__PURE__ */ jsx3("th", { scope: "colgroup", colSpan: span(run.width), "data-group": run.key, children: run.label }, i)]
588
+ ) }) : null;
589
+ const bodies = layout.rows.length ? layout.rows.map((run, i) => /* @__PURE__ */ jsxs2("tbody", { "data-key": run.key, children: [
590
+ run.label !== void 0 && /* @__PURE__ */ jsx3("tr", { children: /* @__PURE__ */ jsx3("th", { scope: "rowgroup", colSpan: span(layout.width), children: run.label }) }),
591
+ run.rows.map(rowEl)
592
+ ] }, i)) : head < rows.length && /* @__PURE__ */ jsx3("tbody", { children: rows.slice(head).map(rowEl) });
593
+ const table = /* @__PURE__ */ jsxs2("table", { ...value.legacy === true ? { className: "bcms-table" } : {}, ...notesId ? { "aria-describedby": notesId } : {}, children: [
594
+ caption && /* @__PURE__ */ jsx3("caption", { children: caption }),
595
+ layout.columns.map((run, i) => /* @__PURE__ */ jsx3("colgroup", { span: span(run.width) }, i)),
596
+ (groupRow || head > 0) && /* @__PURE__ */ jsxs2("thead", { children: [
597
+ groupRow,
598
+ rows.slice(0, head).map(rowEl)
599
+ ] }),
600
+ bodies
380
601
  ] });
602
+ return notes ? /* @__PURE__ */ jsxs2("figure", { "data-rt": "table", children: [
603
+ table,
604
+ /* @__PURE__ */ jsx3("p", { "data-notes": "", id: notesId, children: notes })
605
+ ] }) : table;
381
606
  }
382
607
  function buildComponents(renderBlock) {
383
608
  return {
384
609
  types: {
385
- [PT_TYPES.divider]: () => /* @__PURE__ */ jsx2("hr", {}),
386
- [PT_TYPES.table]: ({ value }) => /* @__PURE__ */ jsx2(Table, { value }),
610
+ [PT_TYPES.divider]: () => /* @__PURE__ */ jsx3("hr", {}),
611
+ [PT_TYPES.table]: ({ value }) => /* @__PURE__ */ jsx3(Table, { value }),
387
612
  [PT_TYPES.code]: ({ value }) => {
388
613
  const v = value;
389
614
  const lang = typeof v.language === "string" && TOKEN.test(v.language) ? v.language : void 0;
390
- return /* @__PURE__ */ jsx2("pre", { children: /* @__PURE__ */ jsx2("code", { className: lang ? `language-${lang}` : void 0, children: typeof v.code === "string" ? v.code : "" }) });
615
+ return /* @__PURE__ */ jsx3("pre", { children: /* @__PURE__ */ jsx3("code", { className: lang ? `language-${lang}` : void 0, children: typeof v.code === "string" ? v.code : "" }) });
391
616
  },
392
617
  /**
393
618
  * A generic embedded block renders as a marker, not as content: its payload references
@@ -398,14 +623,51 @@ function buildComponents(renderBlock) {
398
623
  */
399
624
  [PT_TYPES.block]: ({ value }) => {
400
625
  const rendered = renderBlock?.(value);
401
- if (rendered !== void 0) return /* @__PURE__ */ jsx2(Fragment, { children: rendered });
626
+ if (rendered !== void 0) return /* @__PURE__ */ jsx3(Fragment, { children: rendered });
627
+ const copy = detachedComponentCopy(value);
628
+ if (copy) {
629
+ return /* @__PURE__ */ jsx3("div", { className: "bcms-block bcms-block--component", "data-bcms-component-copy": copy.componentId, "data-bcms-instance": copy.id, children: /* @__PURE__ */ jsx3(BlocksRenderer, { blocks: copy.blocks }) });
630
+ }
402
631
  const v = value;
403
632
  const key = typeof v.schemaKey === "string" ? v.schemaKey : "block";
404
- return /* @__PURE__ */ jsx2("div", { className: "bcms-block", "data-bcms-schema-key": key });
633
+ return /* @__PURE__ */ jsx3("div", { className: "bcms-block", "data-bcms-schema-key": key });
634
+ },
635
+ /**
636
+ * A pull quote — the React half of `portableTextToHtml`'s, and the same DOM: the V2 kit's
637
+ * `<figure data-rt="pullquote">`, the name linked (and the quote cited) only through
638
+ * `safeHref`. pull-quote.test.tsx compares the two.
639
+ */
640
+ [PT_TYPES.pullQuote]: ({ value }) => {
641
+ const v = value;
642
+ const text = (x) => typeof x === "string" ? x.trim() : "";
643
+ const quote = typeof v.quote === "string" ? v.quote : "";
644
+ const name = text(v.name);
645
+ const role = text(v.role);
646
+ const href = safeHref(pullQuoteHref(v.link)) ?? void 0;
647
+ return /* @__PURE__ */ jsxs2("figure", { "data-rt": "pullquote", children: [
648
+ /* @__PURE__ */ jsx3("blockquote", { cite: href, children: /* @__PURE__ */ jsx3("p", { children: quote }) }),
649
+ name || role ? /* @__PURE__ */ jsxs2("figcaption", { children: [
650
+ name ? href ? /* @__PURE__ */ jsx3("a", { href, "data-name": "", children: name }) : /* @__PURE__ */ jsx3("span", { "data-name": "", children: name }) : null,
651
+ name && role ? ", " : null,
652
+ role ? /* @__PURE__ */ jsx3("span", { "data-role": "", children: role }) : null
653
+ ] }) : null
654
+ ] });
655
+ },
656
+ /**
657
+ * Key takeaways — the React half of `portableTextToHtml`'s, and the same DOM: the V2 kit's
658
+ * `<aside data-rt="takeaways">`, read through the one `keyTakeawaysView` both renderers share.
659
+ * portable-text-key-takeaways.test.tsx compares the two.
660
+ */
661
+ [PT_TYPES.keyTakeaways]: ({ value }) => {
662
+ const { title, points } = keyTakeawaysView(value);
663
+ return /* @__PURE__ */ jsxs2("aside", { "data-rt": "takeaways", children: [
664
+ title ? /* @__PURE__ */ jsx3("p", { "data-title": "", children: title }) : null,
665
+ /* @__PURE__ */ jsx3("ul", { children: points.map((p, i) => /* @__PURE__ */ jsx3("li", { children: p }, i)) })
666
+ ] });
405
667
  },
406
668
  [PT_TYPES.mention]: ({ value }) => {
407
669
  const v = value;
408
- return /* @__PURE__ */ jsx2("span", { className: "bcms-mention", children: typeof v.label === "string" ? v.label : "" });
670
+ return /* @__PURE__ */ jsx3("span", { className: "bcms-mention", children: typeof v.label === "string" ? v.label : "" });
409
671
  }
410
672
  },
411
673
  marks: {
@@ -416,9 +678,9 @@ function buildComponents(renderBlock) {
416
678
  [PT_TYPES.link]: ({ value, children }) => {
417
679
  const v = value ?? {};
418
680
  const href = typeof v.href === "string" ? safeHref(v.href) : null;
419
- if (!href) return /* @__PURE__ */ jsx2(Fragment, { children });
681
+ if (!href) return /* @__PURE__ */ jsx3(Fragment, { children });
420
682
  const blank = v.target === "_blank";
421
- return /* @__PURE__ */ jsx2("a", { href, ...blank ? { target: "_blank", rel: "noopener noreferrer" } : {}, children });
683
+ return /* @__PURE__ */ jsx3("a", { href, ...blank ? { target: "_blank", rel: "noopener noreferrer" } : {}, children });
422
684
  },
423
685
  /**
424
686
  * The site's own inline markup, rendered as itself — the React half of
@@ -435,12 +697,12 @@ function buildComponents(renderBlock) {
435
697
  [PT_TYPES.styled]: ({ value, children }) => {
436
698
  const v = value ?? {};
437
699
  const tag = typeof v.tag === "string" ? v.tag.toLowerCase() : "";
438
- if (!isStyledTag(tag)) return /* @__PURE__ */ jsx2(Fragment, { children });
700
+ if (!isStyledTag(tag)) return /* @__PURE__ */ jsx3(Fragment, { children });
439
701
  const Tag = tag;
440
702
  const className = typeof v.className === "string" && v.className ? v.className : void 0;
441
703
  const attrs = styledAttrEntries(v.attrs);
442
704
  const style = attrs.find(([name]) => name === "style")?.[1];
443
- return /* @__PURE__ */ jsx2(
705
+ return /* @__PURE__ */ jsx3(
444
706
  Tag,
445
707
  {
446
708
  className,
@@ -450,22 +712,22 @@ function buildComponents(renderBlock) {
450
712
  }
451
713
  );
452
714
  },
453
- highlight: ({ children }) => /* @__PURE__ */ jsx2("mark", { children }),
454
- "strike-through": ({ children }) => /* @__PURE__ */ jsx2("s", { children }),
715
+ highlight: ({ children }) => /* @__PURE__ */ jsx3("mark", { children }),
716
+ "strike-through": ({ children }) => /* @__PURE__ */ jsx3("s", { children }),
455
717
  /**
456
718
  * A colour mark carries a TOKEN name (`color:brand-accent`), never a baked hex, so the
457
719
  * consuming site styles it and a rebrand does not orphan the content.
458
720
  */
459
721
  color: ({ children, markType }) => {
460
722
  const token = markType.startsWith("color:") ? markType.slice(6) : "";
461
- if (!TOKEN.test(token)) return /* @__PURE__ */ jsx2(Fragment, { children });
462
- return /* @__PURE__ */ jsx2("span", { className: `bcms-color-${token}`, children });
723
+ if (!TOKEN.test(token)) return /* @__PURE__ */ jsx3(Fragment, { children });
724
+ return /* @__PURE__ */ jsx3("span", { className: `bcms-color-${token}`, children });
463
725
  }
464
726
  },
465
727
  /** Unknown marks and types render their children. Never swallow content. */
466
- unknownMark: ({ children }) => /* @__PURE__ */ jsx2(Fragment, { children }),
467
- unknownType: ({ children }) => /* @__PURE__ */ jsx2(Fragment, { children: children ?? null }),
468
- unknownBlockStyle: ({ children }) => /* @__PURE__ */ jsx2("p", { children })
728
+ unknownMark: ({ children }) => /* @__PURE__ */ jsx3(Fragment, { children }),
729
+ unknownType: ({ children }) => /* @__PURE__ */ jsx3(Fragment, { children: children ?? null }),
730
+ unknownBlockStyle: ({ children }) => /* @__PURE__ */ jsx3("p", { children })
469
731
  };
470
732
  }
471
733
  var isListItem = (node) => node._type === "block" && typeof node.listItem === "string" && !!node.listItem;
@@ -479,20 +741,20 @@ function runsOf(blocks) {
479
741
  return runs;
480
742
  }
481
743
  function PortableText({ value, bindingBase, renderBlock }) {
482
- const blocks = React.useMemo(() => normalizePortableText(readValue(value)), [value]);
483
- const components = React.useMemo(() => buildComponents(renderBlock), [renderBlock]);
484
- const listComponents = React.useMemo(() => {
744
+ const blocks = React2.useMemo(() => normalizePortableText(readValue(value)), [value]);
745
+ const components = React2.useMemo(() => buildComponents(renderBlock), [renderBlock]);
746
+ const listComponents = React2.useMemo(() => {
485
747
  if (!bindingBase) return null;
486
748
  const keys = new Set(blocks.map((b) => b._key));
487
749
  return {
488
750
  ...components,
489
- listItem: ({ value: item, children }) => keys.has(item._key) ? /* @__PURE__ */ jsx2("li", { "data-bcms-field": `${bindingBase}#${item._key}`, "data-bcms-kind": "richtext", children }) : /* @__PURE__ */ jsx2("li", { children })
751
+ listItem: ({ value: item, children }) => keys.has(item._key) ? /* @__PURE__ */ jsx3("li", { "data-bcms-field": `${bindingBase}#${item._key}`, "data-bcms-kind": "richtext", children }) : /* @__PURE__ */ jsx3("li", { children })
490
752
  };
491
753
  }, [bindingBase, blocks, components]);
492
754
  if (blocks.length === 0) return null;
493
- if (!bindingBase || !listComponents) return /* @__PURE__ */ jsx2(PT, { value: blocks, components });
494
- return /* @__PURE__ */ jsx2(Fragment, { children: runsOf(blocks).map(
495
- (run) => isListItem(run[0]) ? /* @__PURE__ */ jsx2(PT, { value: run, components: listComponents }, run[0]._key) : /* @__PURE__ */ jsx2(Bound, { base: bindingBase, nodeKey: run[0]._key, children: /* @__PURE__ */ jsx2(PT, { value: run, components }) }, run[0]._key)
755
+ if (!bindingBase || !listComponents) return /* @__PURE__ */ jsx3(PT, { value: blocks, components });
756
+ return /* @__PURE__ */ jsx3(Fragment, { children: runsOf(blocks).map(
757
+ (run) => isListItem(run[0]) ? /* @__PURE__ */ jsx3(PT, { value: run, components: listComponents }, run[0]._key) : /* @__PURE__ */ jsx3(Bound, { base: bindingBase, nodeKey: run[0]._key, children: /* @__PURE__ */ jsx3(PT, { value: run, components }) }, run[0]._key)
496
758
  ) });
497
759
  }
498
760
  function readValue(value) {
@@ -506,7 +768,7 @@ function readValue(value) {
506
768
  }
507
769
 
508
770
  // src/blocks/Text.tsx
509
- import { jsx as jsx3 } from "react/jsx-runtime";
771
+ import { jsx as jsx4 } from "react/jsx-runtime";
510
772
  function Text({
511
773
  html,
512
774
  content,
@@ -522,14 +784,14 @@ function Text({
522
784
  }[align];
523
785
  const cls = `text-muted-foreground leading-relaxed ${alignClass} ${className}`;
524
786
  const structured = readStructuredContent({ content });
525
- return /* @__PURE__ */ jsx3(
787
+ return /* @__PURE__ */ jsx4(
526
788
  "div",
527
789
  {
528
790
  className: cls,
529
791
  ...structured ? {} : { "data-bcms-field": field, "data-bcms-kind": kind },
530
- children: structured ? /* @__PURE__ */ jsx3(PortableText, { value: structured, bindingBase: field }) : (
792
+ children: structured ? /* @__PURE__ */ jsx4(PortableText, { value: structured, bindingBase: field }) : (
531
793
  // eslint-disable-next-line react/no-danger
532
- /* @__PURE__ */ jsx3("span", { dangerouslySetInnerHTML: { __html: html } })
794
+ /* @__PURE__ */ jsx4("span", { dangerouslySetInnerHTML: { __html: html } })
533
795
  )
534
796
  }
535
797
  );
@@ -543,7 +805,7 @@ function cn(...inputs) {
543
805
  }
544
806
 
545
807
  // src/blocks/Image.tsx
546
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
808
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
547
809
  function Image({
548
810
  src,
549
811
  alt,
@@ -556,8 +818,8 @@ function Image({
556
818
  "data-bcms-kind": kind
557
819
  }) {
558
820
  const sized = typeof width === "number" && width > 0;
559
- return /* @__PURE__ */ jsxs2("figure", { className: cn(sized ? "max-w-full" : "w-full", className), children: [
560
- /* @__PURE__ */ jsx4(
821
+ return /* @__PURE__ */ jsxs3("figure", { className: cn(sized ? "max-w-full" : "w-full", className), children: [
822
+ /* @__PURE__ */ jsx5(
561
823
  "img",
562
824
  {
563
825
  src,
@@ -572,13 +834,13 @@ function Image({
572
834
  loading: "lazy"
573
835
  }
574
836
  ),
575
- caption && /* @__PURE__ */ jsx4("figcaption", { className: "mt-2 text-sm text-muted-foreground text-center italic", children: caption })
837
+ caption && /* @__PURE__ */ jsx5("figcaption", { className: "mt-2 text-sm text-muted-foreground text-center italic", children: caption })
576
838
  ] });
577
839
  }
578
840
 
579
841
  // src/blocks/Button.tsx
580
842
  import { LINK_RELS, buttonIconOf, buttonIconSvg, buttonVariantOf, isScriptHref } from "@bettercms-ai/types";
581
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
843
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
582
844
  function Button({
583
845
  text,
584
846
  href,
@@ -589,6 +851,7 @@ function Button({
589
851
  icon: iconProp,
590
852
  newTab = false,
591
853
  rel,
854
+ action,
592
855
  align = "left",
593
856
  className = "",
594
857
  style,
@@ -620,7 +883,14 @@ function Button({
620
883
  const sizeClass = size === "sm" || size === "lg" ? sizeStyles[size] : sizeStyles.md;
621
884
  const fullClass = fullWidth === true ? "flex w-full justify-center" : "";
622
885
  const relTokens = [...newTab === true ? ["noopener", "noreferrer"] : [], ...LINK_RELS.includes(rel) ? [rel] : []];
623
- return /* @__PURE__ */ jsxs3(
886
+ if (action === "code") {
887
+ return /* @__PURE__ */ jsxs4("button", { type: "button", "data-bcms-field": field, "data-bcms-kind": kind, style, className: cn(baseStyles, variantStyles[buttonVariantOf(variant)], alignClass, sizeClass, fullClass, className), children: [
888
+ icon && /* @__PURE__ */ jsx6("span", { className: "mr-2 inline-flex", dangerouslySetInnerHTML: { __html: buttonIconSvg(icon, "bcms-btn-icon") } }),
889
+ text,
890
+ arrow === true && /* @__PURE__ */ jsx6("span", { className: "ml-2 inline-flex", dangerouslySetInnerHTML: { __html: buttonIconSvg("arrow-right", "bcms-btn-arrow") } })
891
+ ] });
892
+ }
893
+ return /* @__PURE__ */ jsxs4(
624
894
  "a",
625
895
  {
626
896
  href: typeof href === "string" && isScriptHref(href) ? "#" : href,
@@ -631,24 +901,24 @@ function Button({
631
901
  style,
632
902
  className: cn(baseStyles, variantStyles[buttonVariantOf(variant)], alignClass, sizeClass, fullClass, className),
633
903
  children: [
634
- icon && /* @__PURE__ */ jsx5("span", { className: "mr-2 inline-flex", dangerouslySetInnerHTML: { __html: buttonIconSvg(icon, "bcms-btn-icon") } }),
904
+ icon && /* @__PURE__ */ jsx6("span", { className: "mr-2 inline-flex", dangerouslySetInnerHTML: { __html: buttonIconSvg(icon, "bcms-btn-icon") } }),
635
905
  text,
636
- arrow === true && /* @__PURE__ */ jsx5("span", { className: "ml-2 inline-flex", dangerouslySetInnerHTML: { __html: buttonIconSvg("arrow-right", "bcms-btn-arrow") } })
906
+ arrow === true && /* @__PURE__ */ jsx6("span", { className: "ml-2 inline-flex", dangerouslySetInnerHTML: { __html: buttonIconSvg("arrow-right", "bcms-btn-arrow") } })
637
907
  ]
638
908
  }
639
909
  );
640
910
  }
641
911
 
642
912
  // src/blocks/Spacer.tsx
643
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
913
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
644
914
  function Spacer({ height = 32, className = "" }) {
645
- return /* @__PURE__ */ jsx6(
915
+ return /* @__PURE__ */ jsx7(
646
916
  "div",
647
917
  {
648
918
  className: `w-full border border-dashed border-muted-foreground/20 rounded flex items-center justify-center ${className}`,
649
919
  style: { height: `${height}px` },
650
920
  "aria-hidden": "true",
651
- children: /* @__PURE__ */ jsxs4("span", { className: "text-xs text-muted-foreground/40 select-none", children: [
921
+ children: /* @__PURE__ */ jsxs5("span", { className: "text-xs text-muted-foreground/40 select-none", children: [
652
922
  height,
653
923
  "px"
654
924
  ] })
@@ -658,14 +928,14 @@ function Spacer({ height = 32, className = "" }) {
658
928
 
659
929
  // src/blocks/Columns.tsx
660
930
  import "react";
661
- import { jsx as jsx7 } from "react/jsx-runtime";
931
+ import { jsx as jsx8 } from "react/jsx-runtime";
662
932
  function Columns({
663
933
  columns,
664
934
  gap = 16,
665
935
  className = "",
666
936
  renderBlock
667
937
  }) {
668
- return /* @__PURE__ */ jsx7(
938
+ return /* @__PURE__ */ jsx8(
669
939
  "div",
670
940
  {
671
941
  className: cn("grid w-full", className),
@@ -673,14 +943,14 @@ function Columns({
673
943
  gridTemplateColumns: `repeat(${columns.length}, minmax(0, 1fr))`,
674
944
  gap: `${gap}px`
675
945
  },
676
- children: columns.map((columnBlocks, colIndex) => /* @__PURE__ */ jsx7("div", { className: "flex flex-col gap-4 min-w-0", children: columnBlocks.map((block) => /* @__PURE__ */ jsx7("span", { children: renderBlock ? renderBlock(block) : null }, block.id)) }, colIndex))
946
+ children: columns.map((columnBlocks, colIndex) => /* @__PURE__ */ jsx8("div", { className: "flex flex-col gap-4 min-w-0", children: columnBlocks.map((block) => /* @__PURE__ */ jsx8("span", { children: renderBlock ? renderBlock(block) : null }, block.id)) }, colIndex))
677
947
  }
678
948
  );
679
949
  }
680
950
 
681
951
  // src/blocks/Video.tsx
682
952
  import { useState } from "react";
683
- import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
953
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
684
954
  function getEmbedUrl(url) {
685
955
  const ytMatch = url.match(
686
956
  /(?:youtube\.com\/(?:watch\?v=|embed\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/
@@ -710,7 +980,7 @@ function Video({
710
980
  const [isLoaded, setIsLoaded] = useState(false);
711
981
  const { embedUrl, provider } = getEmbedUrl(url);
712
982
  if (provider === "unknown") {
713
- return /* @__PURE__ */ jsx8(
983
+ return /* @__PURE__ */ jsx9(
714
984
  "video",
715
985
  {
716
986
  src: url,
@@ -723,9 +993,9 @@ function Video({
723
993
  }
724
994
  );
725
995
  }
726
- return /* @__PURE__ */ jsxs5("div", { className: cn("w-full overflow-hidden rounded-md bg-black aspect-video", className), children: [
996
+ return /* @__PURE__ */ jsxs6("div", { className: cn("w-full overflow-hidden rounded-md bg-black aspect-video", className), children: [
727
997
  !isLoaded && poster && // eslint-disable-next-line @next/next/no-img-element
728
- /* @__PURE__ */ jsx8(
998
+ /* @__PURE__ */ jsx9(
729
999
  "img",
730
1000
  {
731
1001
  src: poster,
@@ -733,7 +1003,7 @@ function Video({
733
1003
  className: "w-full h-full object-cover absolute inset-0"
734
1004
  }
735
1005
  ),
736
- /* @__PURE__ */ jsx8(
1006
+ /* @__PURE__ */ jsx9(
737
1007
  "iframe",
738
1008
  {
739
1009
  src: `${embedUrl}?autoplay=${autoplay ? 1 : 0}&loop=${loop ? 1 : 0}&mute=${autoplay ? 1 : 0}`,
@@ -749,7 +1019,7 @@ function Video({
749
1019
 
750
1020
  // src/blocks/Section.tsx
751
1021
  import "react";
752
- import { jsx as jsx9 } from "react/jsx-runtime";
1022
+ import { jsx as jsx10 } from "react/jsx-runtime";
753
1023
  function Section({
754
1024
  children,
755
1025
  background,
@@ -758,24 +1028,24 @@ function Section({
758
1028
  className = "",
759
1029
  renderBlock
760
1030
  }) {
761
- const inner = (children ?? []).map((block) => /* @__PURE__ */ jsx9("span", { children: renderBlock ? renderBlock(block) : null }, block.id));
762
- return /* @__PURE__ */ jsx9(
1031
+ const inner = (children ?? []).map((block) => /* @__PURE__ */ jsx10("span", { children: renderBlock ? renderBlock(block) : null }, block.id));
1032
+ return /* @__PURE__ */ jsx10(
763
1033
  "section",
764
1034
  {
765
1035
  className: cn("w-full", className),
766
1036
  style: { background, paddingTop: paddingY, paddingBottom: paddingY },
767
- children: maxWidth ? /* @__PURE__ */ jsx9("div", { style: { maxWidth, marginInline: "auto" }, children: inner }) : inner
1037
+ children: maxWidth ? /* @__PURE__ */ jsx10("div", { style: { maxWidth, marginInline: "auto" }, children: inner }) : inner
768
1038
  }
769
1039
  );
770
1040
  }
771
1041
 
772
1042
  // src/blocks/Navbar.tsx
773
- import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
1043
+ import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
774
1044
  function Navbar({ logo, links, cta, className = "" }) {
775
- return /* @__PURE__ */ jsxs6("nav", { className: cn("flex items-center gap-6 py-4", className), children: [
776
- logo?.src ? /* @__PURE__ */ jsx10("a", { href: logo.href ?? "/", className: "shrink-0", children: /* @__PURE__ */ jsx10("img", { src: logo.src, alt: logo.alt, className: "h-8 w-auto" }) }) : null,
777
- /* @__PURE__ */ jsx10("div", { className: "flex flex-1 gap-5", children: (links ?? []).map((l, i) => /* @__PURE__ */ jsx10("a", { href: l.href, className: "text-inherit no-underline", children: l.label }, i)) }),
778
- cta ? /* @__PURE__ */ jsx10(
1045
+ return /* @__PURE__ */ jsxs7("nav", { className: cn("flex items-center gap-6 py-4", className), children: [
1046
+ logo?.src ? /* @__PURE__ */ jsx11("a", { href: logo.href ?? "/", className: "shrink-0", children: /* @__PURE__ */ jsx11("img", { src: logo.src, alt: logo.alt, className: "h-8 w-auto" }) }) : null,
1047
+ /* @__PURE__ */ jsx11("div", { className: "flex flex-1 gap-5", children: (links ?? []).map((l, i) => /* @__PURE__ */ jsx11("a", { href: l.href, className: "text-inherit no-underline", children: l.label }, i)) }),
1048
+ cta ? /* @__PURE__ */ jsx11(
779
1049
  "a",
780
1050
  {
781
1051
  href: cta.href,
@@ -787,40 +1057,40 @@ function Navbar({ logo, links, cta, className = "" }) {
787
1057
  }
788
1058
 
789
1059
  // src/blocks/Footer.tsx
790
- import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
1060
+ import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
791
1061
  function Footer({ columns, copyright, className = "" }) {
792
- return /* @__PURE__ */ jsxs7("footer", { className: cn("mt-12 border-t pt-8", className), children: [
793
- /* @__PURE__ */ jsx11("div", { className: "flex flex-wrap gap-12", children: (columns ?? []).map((col, i) => /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-1.5", children: [
794
- col.heading ? /* @__PURE__ */ jsx11("h4", { className: "mb-2 font-semibold", children: col.heading }) : null,
795
- (col.links ?? []).map((l, j) => /* @__PURE__ */ jsx11("a", { href: l.href, className: "text-neutral-500 no-underline", children: l.label }, j))
1062
+ return /* @__PURE__ */ jsxs8("footer", { className: cn("mt-12 border-t pt-8", className), children: [
1063
+ /* @__PURE__ */ jsx12("div", { className: "flex flex-wrap gap-12", children: (columns ?? []).map((col, i) => /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-1.5", children: [
1064
+ col.heading ? /* @__PURE__ */ jsx12("h4", { className: "mb-2 font-semibold", children: col.heading }) : null,
1065
+ (col.links ?? []).map((l, j) => /* @__PURE__ */ jsx12("a", { href: l.href, className: "text-neutral-500 no-underline", children: l.label }, j))
796
1066
  ] }, i)) }),
797
- copyright ? /* @__PURE__ */ jsx11("p", { className: "mt-6 text-sm text-neutral-500", children: copyright }) : null
1067
+ copyright ? /* @__PURE__ */ jsx12("p", { className: "mt-6 text-sm text-neutral-500", children: copyright }) : null
798
1068
  ] });
799
1069
  }
800
1070
 
801
1071
  // src/blocks/Slider.tsx
802
1072
  import { useState as useState2 } from "react";
803
- import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
1073
+ import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
804
1074
  function Slider({ slides, className = "", renderBlock }) {
805
1075
  const [i, setI] = useState2(0);
806
1076
  const list = slides ?? [];
807
1077
  const go = (n) => setI((n + list.length) % (list.length || 1));
808
1078
  const btn = "absolute top-1/2 -translate-y-1/2 flex h-9 w-9 items-center justify-center rounded-full border-0 bg-black/50 text-white cursor-pointer";
809
- return /* @__PURE__ */ jsxs8("div", { className: cn("relative w-full", className), children: [
810
- list.map((s, idx) => /* @__PURE__ */ jsx12("div", { hidden: idx !== i, className: "flex flex-col gap-4", children: (s.children ?? []).map((b) => /* @__PURE__ */ jsx12("span", { children: renderBlock ? renderBlock(b) : null }, b.id)) }, s.id)),
811
- /* @__PURE__ */ jsx12("button", { type: "button", "aria-label": "Previous", onClick: () => go(i - 1), className: cn(btn, "left-2"), children: "\u2039" }),
812
- /* @__PURE__ */ jsx12("button", { type: "button", "aria-label": "Next", onClick: () => go(i + 1), className: cn(btn, "right-2"), children: "\u203A" })
1079
+ return /* @__PURE__ */ jsxs9("div", { className: cn("relative w-full", className), children: [
1080
+ list.map((s, idx) => /* @__PURE__ */ jsx13("div", { hidden: idx !== i, className: "flex flex-col gap-4", children: (s.children ?? []).map((b) => /* @__PURE__ */ jsx13("span", { children: renderBlock ? renderBlock(b) : null }, b.id)) }, s.id)),
1081
+ /* @__PURE__ */ jsx13("button", { type: "button", "aria-label": "Previous", onClick: () => go(i - 1), className: cn(btn, "left-2"), children: "\u2039" }),
1082
+ /* @__PURE__ */ jsx13("button", { type: "button", "aria-label": "Next", onClick: () => go(i + 1), className: cn(btn, "right-2"), children: "\u203A" })
813
1083
  ] });
814
1084
  }
815
1085
 
816
1086
  // src/blocks/Tabs.tsx
817
1087
  import { useState as useState3 } from "react";
818
- import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
1088
+ import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
819
1089
  function Tabs({ tabs, className = "", renderBlock }) {
820
1090
  const [active, setActive] = useState3(0);
821
1091
  const list = tabs ?? [];
822
- return /* @__PURE__ */ jsxs9("div", { className: cn("w-full", className), children: [
823
- /* @__PURE__ */ jsx13("div", { role: "tablist", className: "mb-4 flex gap-1 border-b", children: list.map((t, idx) => /* @__PURE__ */ jsx13(
1092
+ return /* @__PURE__ */ jsxs10("div", { className: cn("w-full", className), children: [
1093
+ /* @__PURE__ */ jsx14("div", { role: "tablist", className: "mb-4 flex gap-1 border-b", children: list.map((t, idx) => /* @__PURE__ */ jsx14(
824
1094
  "button",
825
1095
  {
826
1096
  role: "tab",
@@ -835,38 +1105,38 @@ function Tabs({ tabs, className = "", renderBlock }) {
835
1105
  },
836
1106
  t.id
837
1107
  )) }),
838
- list.map((t, idx) => /* @__PURE__ */ jsx13("div", { role: "tabpanel", hidden: idx !== active, className: "flex flex-col gap-4", children: (t.children ?? []).map((b) => /* @__PURE__ */ jsx13("span", { children: renderBlock ? renderBlock(b) : null }, b.id)) }, t.id))
1108
+ list.map((t, idx) => /* @__PURE__ */ jsx14("div", { role: "tabpanel", hidden: idx !== active, className: "flex flex-col gap-4", children: (t.children ?? []).map((b) => /* @__PURE__ */ jsx14("span", { children: renderBlock ? renderBlock(b) : null }, b.id)) }, t.id))
839
1109
  ] });
840
1110
  }
841
1111
 
842
1112
  // src/blocks/Placeholders.tsx
843
- import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
1113
+ import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
844
1114
  var box = "rounded border border-dashed border-neutral-300 bg-neutral-50 p-4 text-sm text-neutral-500";
845
1115
  function ComponentInstance({
846
1116
  componentId,
847
1117
  className = ""
848
1118
  }) {
849
- return /* @__PURE__ */ jsxs10("div", { className: cn(box, className), children: [
1119
+ return /* @__PURE__ */ jsxs11("div", { className: cn(box, className), children: [
850
1120
  "Component instance: ",
851
- /* @__PURE__ */ jsx14("code", { children: componentId })
1121
+ /* @__PURE__ */ jsx15("code", { children: componentId })
852
1122
  ] });
853
1123
  }
854
1124
  function FormPlaceholder({ formId, className = "" }) {
855
- return /* @__PURE__ */ jsxs10("div", { className: cn(box, className), children: [
1125
+ return /* @__PURE__ */ jsxs11("div", { className: cn(box, className), children: [
856
1126
  "Form: ",
857
- /* @__PURE__ */ jsx14("code", { children: formId })
1127
+ /* @__PURE__ */ jsx15("code", { children: formId })
858
1128
  ] });
859
1129
  }
860
1130
  function CollectionPlaceholder({
861
1131
  cardComponentId,
862
1132
  className = ""
863
1133
  }) {
864
- return /* @__PURE__ */ jsxs10("div", { className: cn(box, className), children: [
1134
+ return /* @__PURE__ */ jsxs11("div", { className: cn(box, className), children: [
865
1135
  "Collection: entries of this page",
866
- cardComponentId ? /* @__PURE__ */ jsxs10(Fragment2, { children: [
1136
+ cardComponentId ? /* @__PURE__ */ jsxs11(Fragment2, { children: [
867
1137
  " as ",
868
- /* @__PURE__ */ jsx14("code", { children: cardComponentId })
869
- ] }) : /* @__PURE__ */ jsx14(Fragment2, { children: " (default card)" })
1138
+ /* @__PURE__ */ jsx15("code", { children: cardComponentId })
1139
+ ] }) : /* @__PURE__ */ jsx15(Fragment2, { children: " (default card)" })
870
1140
  ] });
871
1141
  }
872
1142