@bettercms-ai/ui 0.3.0 → 0.5.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/renderer.js CHANGED
@@ -6,7 +6,9 @@ function Heading({
6
6
  text,
7
7
  level = 2,
8
8
  align = "left",
9
- className = ""
9
+ className = "",
10
+ "data-bcms-field": field,
11
+ "data-bcms-kind": kind
10
12
  }) {
11
13
  const alignClass = {
12
14
  left: "text-left",
@@ -16,28 +18,81 @@ function Heading({
16
18
  const classNames = `font-heading font-bold tracking-tight text-foreground ${alignClass} ${className}`;
17
19
  switch (level) {
18
20
  case 1:
19
- return /* @__PURE__ */ jsx("h1", { className: classNames, children: text });
21
+ return /* @__PURE__ */ jsx("h1", { className: classNames, "data-bcms-field": field, "data-bcms-kind": kind, children: text });
20
22
  case 2:
21
- return /* @__PURE__ */ jsx("h2", { className: classNames, children: text });
23
+ return /* @__PURE__ */ jsx("h2", { className: classNames, "data-bcms-field": field, "data-bcms-kind": kind, children: text });
22
24
  case 3:
23
- return /* @__PURE__ */ jsx("h3", { className: classNames, children: text });
25
+ return /* @__PURE__ */ jsx("h3", { className: classNames, "data-bcms-field": field, "data-bcms-kind": kind, children: text });
24
26
  default:
25
- return /* @__PURE__ */ jsx("h2", { className: classNames, children: text });
27
+ return /* @__PURE__ */ jsx("h2", { className: classNames, "data-bcms-field": field, "data-bcms-kind": kind, children: text });
28
+ }
29
+ }
30
+
31
+ // src/blocks/portableText.tsx
32
+ import React from "react";
33
+ import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
34
+ var MARK_TAGS = {
35
+ strong: "strong",
36
+ em: "em",
37
+ underline: "u",
38
+ "strike-through": "s",
39
+ code: "code"
40
+ };
41
+ var BLOCK_TAGS = {
42
+ normal: "p",
43
+ h1: "h1",
44
+ h2: "h2",
45
+ h3: "h3",
46
+ h4: "h4",
47
+ h5: "h5",
48
+ h6: "h6",
49
+ blockquote: "blockquote"
50
+ };
51
+ function Span({ node, index }) {
52
+ const marks = node.marks ?? [];
53
+ const colour = marks.find((m) => m.startsWith("color:"))?.slice("color:".length);
54
+ let out = node.text ?? "";
55
+ for (const m of marks) {
56
+ const Tag = MARK_TAGS[m];
57
+ if (Tag) out = /* @__PURE__ */ jsx2(Tag, { children: out });
58
+ }
59
+ if (colour && /^[a-z0-9-]+$/i.test(colour)) {
60
+ out = /* @__PURE__ */ jsx2("span", { className: `bcms-color-${colour}`, children: out });
26
61
  }
62
+ return /* @__PURE__ */ jsx2(React.Fragment, { children: out }, node._key ?? index);
63
+ }
64
+ function PortableText({ doc }) {
65
+ return /* @__PURE__ */ jsx2(Fragment, { children: doc.content.map((block, i) => {
66
+ const Tag = BLOCK_TAGS[block.style ?? "normal"] ?? "p";
67
+ return /* @__PURE__ */ jsx2(Tag, { children: (block.children ?? []).map((c, j) => /* @__PURE__ */ jsx2(Span, { node: c, index: j }, c._key ?? j)) }, block._key ?? i);
68
+ }) });
27
69
  }
28
70
 
29
71
  // src/blocks/Text.tsx
30
- import { jsx as jsx2 } from "react/jsx-runtime";
31
- function Text({ html, align = "left", className = "" }) {
72
+ import { jsx as jsx3 } from "react/jsx-runtime";
73
+ function Text({
74
+ html,
75
+ content,
76
+ align = "left",
77
+ className = "",
78
+ "data-bcms-field": field,
79
+ "data-bcms-kind": kind
80
+ }) {
32
81
  const alignClass = {
33
82
  left: "text-left",
34
83
  center: "text-center",
35
84
  right: "text-right"
36
85
  }[align];
37
- return /* @__PURE__ */ jsx2(
86
+ const cls = `text-muted-foreground leading-relaxed ${alignClass} ${className}`;
87
+ if (content && content.content.length > 0) {
88
+ return /* @__PURE__ */ jsx3("div", { className: cls, children: /* @__PURE__ */ jsx3(PortableText, { doc: content }) });
89
+ }
90
+ return /* @__PURE__ */ jsx3(
38
91
  "div",
39
92
  {
40
- className: `text-muted-foreground leading-relaxed ${alignClass} ${className}`,
93
+ className: cls,
94
+ "data-bcms-field": field,
95
+ "data-bcms-kind": kind,
41
96
  dangerouslySetInnerHTML: { __html: html }
42
97
  }
43
98
  );
@@ -51,7 +106,7 @@ function cn(...inputs) {
51
106
  }
52
107
 
53
108
  // src/blocks/Image.tsx
54
- import { jsx as jsx3, jsxs } from "react/jsx-runtime";
109
+ import { jsx as jsx4, jsxs } from "react/jsx-runtime";
55
110
  function Image({
56
111
  src,
57
112
  alt,
@@ -59,10 +114,12 @@ function Image({
59
114
  height,
60
115
  caption,
61
116
  className = "",
62
- sizes
117
+ sizes,
118
+ "data-bcms-field": field,
119
+ "data-bcms-kind": kind
63
120
  }) {
64
121
  return /* @__PURE__ */ jsxs("figure", { className: cn("w-full", className), children: [
65
- /* @__PURE__ */ jsx3(
122
+ /* @__PURE__ */ jsx4(
66
123
  "img",
67
124
  {
68
125
  src,
@@ -70,22 +127,26 @@ function Image({
70
127
  width,
71
128
  height,
72
129
  sizes,
130
+ "data-bcms-field": field,
131
+ "data-bcms-kind": kind,
73
132
  className: "w-full h-auto rounded-md object-cover",
74
133
  loading: "lazy"
75
134
  }
76
135
  ),
77
- caption && /* @__PURE__ */ jsx3("figcaption", { className: "mt-2 text-sm text-muted-foreground text-center italic", children: caption })
136
+ caption && /* @__PURE__ */ jsx4("figcaption", { className: "mt-2 text-sm text-muted-foreground text-center italic", children: caption })
78
137
  ] });
79
138
  }
80
139
 
81
140
  // src/blocks/Button.tsx
82
- import { jsx as jsx4 } from "react/jsx-runtime";
141
+ import { jsx as jsx5 } from "react/jsx-runtime";
83
142
  function Button({
84
143
  text,
85
144
  href,
86
145
  variant = "primary",
87
146
  align = "left",
88
- className = ""
147
+ className = "",
148
+ "data-bcms-field": field,
149
+ "data-bcms-kind": kind
89
150
  }) {
90
151
  const baseStyles = "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50";
91
152
  const variantStyles = {
@@ -97,10 +158,12 @@ function Button({
97
158
  center: "justify-center",
98
159
  right: "justify-end"
99
160
  }[align];
100
- return /* @__PURE__ */ jsx4(
161
+ return /* @__PURE__ */ jsx5(
101
162
  "a",
102
163
  {
103
164
  href,
165
+ "data-bcms-field": field,
166
+ "data-bcms-kind": kind,
104
167
  className: cn(baseStyles, variantStyles[variant], alignClass, "px-4 py-2", className),
105
168
  children: text
106
169
  }
@@ -108,9 +171,9 @@ function Button({
108
171
  }
109
172
 
110
173
  // src/blocks/Spacer.tsx
111
- import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
174
+ import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
112
175
  function Spacer({ height = 32, className = "" }) {
113
- return /* @__PURE__ */ jsx5(
176
+ return /* @__PURE__ */ jsx6(
114
177
  "div",
115
178
  {
116
179
  className: `w-full border border-dashed border-muted-foreground/20 rounded flex items-center justify-center ${className}`,
@@ -126,14 +189,14 @@ function Spacer({ height = 32, className = "" }) {
126
189
 
127
190
  // src/blocks/Columns.tsx
128
191
  import "react";
129
- import { jsx as jsx6 } from "react/jsx-runtime";
192
+ import { jsx as jsx7 } from "react/jsx-runtime";
130
193
  function Columns({
131
194
  columns,
132
195
  gap = 16,
133
196
  className = "",
134
197
  renderBlock
135
198
  }) {
136
- return /* @__PURE__ */ jsx6(
199
+ return /* @__PURE__ */ jsx7(
137
200
  "div",
138
201
  {
139
202
  className: cn("grid w-full", className),
@@ -141,14 +204,14 @@ function Columns({
141
204
  gridTemplateColumns: `repeat(${columns.length}, minmax(0, 1fr))`,
142
205
  gap: `${gap}px`
143
206
  },
144
- children: columns.map((columnBlocks, colIndex) => /* @__PURE__ */ jsx6("div", { className: "flex flex-col gap-4 min-w-0", children: columnBlocks.map((block) => /* @__PURE__ */ jsx6("span", { children: renderBlock ? renderBlock(block) : null }, block.id)) }, colIndex))
207
+ 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))
145
208
  }
146
209
  );
147
210
  }
148
211
 
149
212
  // src/blocks/Video.tsx
150
213
  import { useState } from "react";
151
- import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
214
+ import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
152
215
  function getEmbedUrl(url) {
153
216
  const ytMatch = url.match(
154
217
  /(?:youtube\.com\/(?:watch\?v=|embed\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/
@@ -178,7 +241,7 @@ function Video({
178
241
  const [isLoaded, setIsLoaded] = useState(false);
179
242
  const { embedUrl, provider } = getEmbedUrl(url);
180
243
  if (provider === "unknown") {
181
- return /* @__PURE__ */ jsx7(
244
+ return /* @__PURE__ */ jsx8(
182
245
  "video",
183
246
  {
184
247
  src: url,
@@ -193,7 +256,7 @@ function Video({
193
256
  }
194
257
  return /* @__PURE__ */ jsxs3("div", { className: cn("w-full overflow-hidden rounded-md bg-black aspect-video", className), children: [
195
258
  !isLoaded && poster && // eslint-disable-next-line @next/next/no-img-element
196
- /* @__PURE__ */ jsx7(
259
+ /* @__PURE__ */ jsx8(
197
260
  "img",
198
261
  {
199
262
  src: poster,
@@ -201,7 +264,7 @@ function Video({
201
264
  className: "w-full h-full object-cover absolute inset-0"
202
265
  }
203
266
  ),
204
- /* @__PURE__ */ jsx7(
267
+ /* @__PURE__ */ jsx8(
205
268
  "iframe",
206
269
  {
207
270
  src: `${embedUrl}?autoplay=${autoplay ? 1 : 0}&loop=${loop ? 1 : 0}&mute=${autoplay ? 1 : 0}`,
@@ -217,7 +280,7 @@ function Video({
217
280
 
218
281
  // src/blocks/Section.tsx
219
282
  import "react";
220
- import { jsx as jsx8 } from "react/jsx-runtime";
283
+ import { jsx as jsx9 } from "react/jsx-runtime";
221
284
  function Section({
222
285
  children,
223
286
  background,
@@ -226,24 +289,24 @@ function Section({
226
289
  className = "",
227
290
  renderBlock
228
291
  }) {
229
- const inner = (children ?? []).map((block) => /* @__PURE__ */ jsx8("span", { children: renderBlock ? renderBlock(block) : null }, block.id));
230
- return /* @__PURE__ */ jsx8(
292
+ const inner = (children ?? []).map((block) => /* @__PURE__ */ jsx9("span", { children: renderBlock ? renderBlock(block) : null }, block.id));
293
+ return /* @__PURE__ */ jsx9(
231
294
  "section",
232
295
  {
233
296
  className: cn("w-full", className),
234
297
  style: { background, paddingTop: paddingY, paddingBottom: paddingY },
235
- children: maxWidth ? /* @__PURE__ */ jsx8("div", { style: { maxWidth, marginInline: "auto" }, children: inner }) : inner
298
+ children: maxWidth ? /* @__PURE__ */ jsx9("div", { style: { maxWidth, marginInline: "auto" }, children: inner }) : inner
236
299
  }
237
300
  );
238
301
  }
239
302
 
240
303
  // src/blocks/Navbar.tsx
241
- import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
304
+ import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
242
305
  function Navbar({ logo, links, cta, className = "" }) {
243
306
  return /* @__PURE__ */ jsxs4("nav", { className: cn("flex items-center gap-6 py-4", className), children: [
244
- logo?.src ? /* @__PURE__ */ jsx9("a", { href: logo.href ?? "/", className: "shrink-0", children: /* @__PURE__ */ jsx9("img", { src: logo.src, alt: logo.alt, className: "h-8 w-auto" }) }) : null,
245
- /* @__PURE__ */ jsx9("div", { className: "flex flex-1 gap-5", children: (links ?? []).map((l, i) => /* @__PURE__ */ jsx9("a", { href: l.href, className: "text-inherit no-underline", children: l.label }, i)) }),
246
- cta ? /* @__PURE__ */ jsx9(
307
+ 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,
308
+ /* @__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)) }),
309
+ cta ? /* @__PURE__ */ jsx10(
247
310
  "a",
248
311
  {
249
312
  href: cta.href,
@@ -255,40 +318,40 @@ function Navbar({ logo, links, cta, className = "" }) {
255
318
  }
256
319
 
257
320
  // src/blocks/Footer.tsx
258
- import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
321
+ import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
259
322
  function Footer({ columns, copyright, className = "" }) {
260
323
  return /* @__PURE__ */ jsxs5("footer", { className: cn("mt-12 border-t pt-8", className), children: [
261
- /* @__PURE__ */ jsx10("div", { className: "flex flex-wrap gap-12", children: (columns ?? []).map((col, i) => /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1.5", children: [
262
- col.heading ? /* @__PURE__ */ jsx10("h4", { className: "mb-2 font-semibold", children: col.heading }) : null,
263
- (col.links ?? []).map((l, j) => /* @__PURE__ */ jsx10("a", { href: l.href, className: "text-neutral-500 no-underline", children: l.label }, j))
324
+ /* @__PURE__ */ jsx11("div", { className: "flex flex-wrap gap-12", children: (columns ?? []).map((col, i) => /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1.5", children: [
325
+ col.heading ? /* @__PURE__ */ jsx11("h4", { className: "mb-2 font-semibold", children: col.heading }) : null,
326
+ (col.links ?? []).map((l, j) => /* @__PURE__ */ jsx11("a", { href: l.href, className: "text-neutral-500 no-underline", children: l.label }, j))
264
327
  ] }, i)) }),
265
- copyright ? /* @__PURE__ */ jsx10("p", { className: "mt-6 text-sm text-neutral-500", children: copyright }) : null
328
+ copyright ? /* @__PURE__ */ jsx11("p", { className: "mt-6 text-sm text-neutral-500", children: copyright }) : null
266
329
  ] });
267
330
  }
268
331
 
269
332
  // src/blocks/Slider.tsx
270
333
  import { useState as useState2 } from "react";
271
- import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
334
+ import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
272
335
  function Slider({ slides, className = "", renderBlock }) {
273
336
  const [i, setI] = useState2(0);
274
337
  const list = slides ?? [];
275
338
  const go = (n) => setI((n + list.length) % (list.length || 1));
276
339
  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";
277
340
  return /* @__PURE__ */ jsxs6("div", { className: cn("relative w-full", className), children: [
278
- list.map((s, idx) => /* @__PURE__ */ jsx11("div", { hidden: idx !== i, className: "flex flex-col gap-4", children: (s.children ?? []).map((b) => /* @__PURE__ */ jsx11("span", { children: renderBlock ? renderBlock(b) : null }, b.id)) }, s.id)),
279
- /* @__PURE__ */ jsx11("button", { type: "button", "aria-label": "Previous", onClick: () => go(i - 1), className: cn(btn, "left-2"), children: "\u2039" }),
280
- /* @__PURE__ */ jsx11("button", { type: "button", "aria-label": "Next", onClick: () => go(i + 1), className: cn(btn, "right-2"), children: "\u203A" })
341
+ 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)),
342
+ /* @__PURE__ */ jsx12("button", { type: "button", "aria-label": "Previous", onClick: () => go(i - 1), className: cn(btn, "left-2"), children: "\u2039" }),
343
+ /* @__PURE__ */ jsx12("button", { type: "button", "aria-label": "Next", onClick: () => go(i + 1), className: cn(btn, "right-2"), children: "\u203A" })
281
344
  ] });
282
345
  }
283
346
 
284
347
  // src/blocks/Tabs.tsx
285
348
  import { useState as useState3 } from "react";
286
- import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
349
+ import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
287
350
  function Tabs({ tabs, className = "", renderBlock }) {
288
351
  const [active, setActive] = useState3(0);
289
352
  const list = tabs ?? [];
290
353
  return /* @__PURE__ */ jsxs7("div", { className: cn("w-full", className), children: [
291
- /* @__PURE__ */ jsx12("div", { role: "tablist", className: "mb-4 flex gap-1 border-b", children: list.map((t, idx) => /* @__PURE__ */ jsx12(
354
+ /* @__PURE__ */ jsx13("div", { role: "tablist", className: "mb-4 flex gap-1 border-b", children: list.map((t, idx) => /* @__PURE__ */ jsx13(
292
355
  "button",
293
356
  {
294
357
  role: "tab",
@@ -303,12 +366,12 @@ function Tabs({ tabs, className = "", renderBlock }) {
303
366
  },
304
367
  t.id
305
368
  )) }),
306
- list.map((t, idx) => /* @__PURE__ */ jsx12("div", { role: "tabpanel", hidden: idx !== active, className: "flex flex-col gap-4", children: (t.children ?? []).map((b) => /* @__PURE__ */ jsx12("span", { children: renderBlock ? renderBlock(b) : null }, b.id)) }, t.id))
369
+ 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))
307
370
  ] });
308
371
  }
309
372
 
310
373
  // src/blocks/Placeholders.tsx
311
- import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
374
+ import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
312
375
  var box = "rounded border border-dashed border-neutral-300 bg-neutral-50 p-4 text-sm text-neutral-500";
313
376
  function ComponentInstance({
314
377
  componentId,
@@ -316,18 +379,37 @@ function ComponentInstance({
316
379
  }) {
317
380
  return /* @__PURE__ */ jsxs8("div", { className: cn(box, className), children: [
318
381
  "Component instance: ",
319
- /* @__PURE__ */ jsx13("code", { children: componentId })
382
+ /* @__PURE__ */ jsx14("code", { children: componentId })
320
383
  ] });
321
384
  }
322
385
  function FormPlaceholder({ formId, className = "" }) {
323
386
  return /* @__PURE__ */ jsxs8("div", { className: cn(box, className), children: [
324
387
  "Form: ",
325
- /* @__PURE__ */ jsx13("code", { children: formId })
388
+ /* @__PURE__ */ jsx14("code", { children: formId })
389
+ ] });
390
+ }
391
+ function CollectionPlaceholder({
392
+ cardComponentId,
393
+ className = ""
394
+ }) {
395
+ return /* @__PURE__ */ jsxs8("div", { className: cn(box, className), children: [
396
+ "Collection: entries of this page",
397
+ cardComponentId ? /* @__PURE__ */ jsxs8(Fragment2, { children: [
398
+ " as ",
399
+ /* @__PURE__ */ jsx14("code", { children: cardComponentId })
400
+ ] }) : /* @__PURE__ */ jsx14(Fragment2, { children: " (default card)" })
326
401
  ] });
327
402
  }
328
403
 
329
404
  // src/registry.ts
330
405
  var BLOCK_REGISTRY = {
406
+ // The entries of the page this block sits on. No preview here by design: the list is
407
+ // fetched per request by the site renderer, so the builder has nothing to show.
408
+ collection: {
409
+ component: CollectionPlaceholder,
410
+ label: "Collection",
411
+ icon: "list"
412
+ },
331
413
  heading: {
332
414
  component: Heading,
333
415
  label: "Heading",
@@ -338,6 +420,14 @@ var BLOCK_REGISTRY = {
338
420
  label: "Text",
339
421
  icon: "align-left"
340
422
  },
423
+ // Same payload, same renderer — deliberately. `richtext` differs from `text` only in how
424
+ // an author EDITS it (a formatting surface vs a plain field); at delivery both are an HTML
425
+ // string, so duplicating the component would be two things to keep in sync for no gain.
426
+ richtext: {
427
+ component: Text,
428
+ label: "Rich text",
429
+ icon: "pilcrow"
430
+ },
341
431
  image: {
342
432
  component: Image,
343
433
  label: "Image",
@@ -401,45 +491,32 @@ var BLOCK_REGISTRY = {
401
491
  };
402
492
  var BLOCK_TYPES = Object.keys(BLOCK_REGISTRY);
403
493
 
404
- // ../types/dist/index.js
405
- var SPACE_PX = { none: "0", xs: "4px", sm: "8px", md: "16px", lg: "32px", xl: "64px" };
406
- var FONT_SIZE_REM = { sm: "0.875rem", base: "1rem", lg: "1.25rem", xl: "1.5rem", "2xl": "2rem" };
407
- var WEIGHT = { normal: 400, medium: 500, semibold: 600, bold: 700 };
408
- var RADIUS_PX = { none: "0", sm: "4px", md: "8px", lg: "16px", full: "9999px" };
409
- var COLOR_HEX = { white: "#ffffff", black: "#111111", muted: "#6b7280", mutedBg: "#f3f4f6", primary: "#111111", accent: "#4f46e5" };
410
- function blockStyleToCss(style) {
411
- if (!style || typeof style !== "object") return void 0;
412
- const s = style;
413
- const css = {};
414
- if (s.paddingY && SPACE_PX[s.paddingY] != null) {
415
- css.paddingTop = SPACE_PX[s.paddingY];
416
- css.paddingBottom = SPACE_PX[s.paddingY];
417
- }
418
- if (s.paddingX && SPACE_PX[s.paddingX] != null) {
419
- css.paddingLeft = SPACE_PX[s.paddingX];
420
- css.paddingRight = SPACE_PX[s.paddingX];
421
- }
422
- if (s.marginTop && SPACE_PX[s.marginTop] != null) css.marginTop = SPACE_PX[s.marginTop];
423
- if (s.marginBottom && SPACE_PX[s.marginBottom] != null) css.marginBottom = SPACE_PX[s.marginBottom];
424
- if (s.background && COLOR_HEX[s.background]) css.background = COLOR_HEX[s.background];
425
- if (s.textColor && COLOR_HEX[s.textColor]) css.color = COLOR_HEX[s.textColor];
426
- if (s.align === "left" || s.align === "center" || s.align === "right") css.textAlign = s.align;
427
- if (s.fontSize && FONT_SIZE_REM[s.fontSize]) css.fontSize = FONT_SIZE_REM[s.fontSize];
428
- if (s.weight && WEIGHT[s.weight] != null) css.fontWeight = WEIGHT[s.weight];
429
- if (s.radius && RADIUS_PX[s.radius] != null) css.borderRadius = RADIUS_PX[s.radius];
430
- return Object.keys(css).length ? css : void 0;
431
- }
432
-
433
494
  // src/blockStyle.ts
495
+ import { blockStyleToCss } from "@bettercms-ai/types";
434
496
  function blockStyleToReactStyle(style) {
435
497
  return blockStyleToCss(style);
436
498
  }
437
499
 
438
500
  // src/renderer.tsx
439
501
  import "react";
440
- import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
502
+ import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
441
503
  var CONTAINER_TYPES = /* @__PURE__ */ new Set(["columns", "section", "slider", "tabs"]);
442
- function resolveBlock(block, className) {
504
+ function bindingProps(block, annotate) {
505
+ if (!annotate || !block.id) return {};
506
+ switch (block.type) {
507
+ case "heading":
508
+ case "button":
509
+ return { "data-bcms-field": `${block.id}__text`, "data-bcms-kind": "text" };
510
+ case "text":
511
+ case "richtext":
512
+ return { "data-bcms-field": `${block.id}__html`, "data-bcms-kind": "richtext" };
513
+ case "image":
514
+ return { "data-bcms-field": `${block.id}__src`, "data-bcms-kind": "image" };
515
+ default:
516
+ return {};
517
+ }
518
+ }
519
+ function resolveBlock(block, className, annotate = false) {
443
520
  const entry = BLOCK_REGISTRY[block.type];
444
521
  if (!entry) {
445
522
  return /* @__PURE__ */ jsxs9(
@@ -448,20 +525,21 @@ function resolveBlock(block, className) {
448
525
  className: `w-full p-4 border border-dashed border-red-400 bg-red-50 text-red-600 text-sm rounded ${className ?? ""}`,
449
526
  children: [
450
527
  "Unknown block type: ",
451
- /* @__PURE__ */ jsx14("code", { children: block.type })
528
+ /* @__PURE__ */ jsx15("code", { children: block.type })
452
529
  ]
453
530
  }
454
531
  );
455
532
  }
456
533
  const Component = entry.component;
457
534
  const blockStyle = blockStyleToReactStyle(block.style);
458
- const el = /* @__PURE__ */ jsx14(Component, { ...block.props, className });
459
- return blockStyle ? /* @__PURE__ */ jsx14("div", { style: blockStyle, children: el }) : el;
535
+ const el = /* @__PURE__ */ jsx15(Component, { ...block.props, className, ...bindingProps(block, annotate) });
536
+ return blockStyle ? /* @__PURE__ */ jsx15("div", { style: blockStyle, children: el }) : el;
460
537
  }
461
538
  function BlockRenderer({
462
539
  block,
463
540
  className = "",
464
- renderBlock
541
+ renderBlock,
542
+ annotate = false
465
543
  }) {
466
544
  const entry = BLOCK_REGISTRY[block.type];
467
545
  if (!entry) {
@@ -471,28 +549,38 @@ function BlockRenderer({
471
549
  className: `w-full p-4 border border-dashed border-red-400 bg-red-50 text-red-600 text-sm rounded ${className}`,
472
550
  children: [
473
551
  "Unknown block type: ",
474
- /* @__PURE__ */ jsx14("code", { children: block.type })
552
+ /* @__PURE__ */ jsx15("code", { children: block.type })
475
553
  ]
476
554
  }
477
555
  );
478
556
  }
479
557
  const Component = entry.component;
480
- const containerProps = CONTAINER_TYPES.has(block.type) ? { renderBlock: renderBlock ?? resolveBlock } : {};
558
+ const containerProps = CONTAINER_TYPES.has(block.type) ? { renderBlock: renderBlock ?? ((child) => resolveBlock(child, void 0, annotate)) } : {};
481
559
  const blockStyle = blockStyleToReactStyle(block.style);
482
- const el = /* @__PURE__ */ jsx14(Component, { ...block.props, className, ...containerProps });
483
- return blockStyle ? /* @__PURE__ */ jsx14("div", { style: blockStyle, children: el }) : el;
560
+ const el = /* @__PURE__ */ jsx15(
561
+ Component,
562
+ {
563
+ ...block.props,
564
+ className,
565
+ ...containerProps,
566
+ ...bindingProps(block, annotate)
567
+ }
568
+ );
569
+ return blockStyle ? /* @__PURE__ */ jsx15("div", { style: blockStyle, children: el }) : el;
484
570
  }
485
571
  function BlocksRenderer({
486
572
  blocks,
487
573
  className = "",
488
- renderBlock
574
+ renderBlock,
575
+ annotate = false
489
576
  }) {
490
577
  if (blocks.length === 0) return null;
491
- return /* @__PURE__ */ jsx14("div", { className, children: blocks.map((block) => /* @__PURE__ */ jsx14(
578
+ return /* @__PURE__ */ jsx15("div", { className, children: blocks.map((block) => /* @__PURE__ */ jsx15(
492
579
  BlockRenderer,
493
580
  {
494
581
  block,
495
- renderBlock
582
+ renderBlock,
583
+ annotate
496
584
  },
497
585
  block.id
498
586
  )) });