@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.
@@ -0,0 +1,53 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { HeadingProps, TextProps, PortableTextDocument, ImageProps, ButtonProps, SpacerProps, ColumnsProps, ContentBlock, VideoProps } from '@bettercms-ai/types';
3
+ import React from 'react';
4
+
5
+ /** Preview-only attributes understood by the BetterCMS visual editor. */
6
+ type BcmsBindingProps = {
7
+ "data-bcms-field"?: string;
8
+ "data-bcms-kind"?: "text" | "richtext" | "image";
9
+ };
10
+
11
+ interface HeadingBlockProps extends HeadingProps, BcmsBindingProps {
12
+ className?: string;
13
+ align?: "left" | "center" | "right";
14
+ }
15
+ declare function Heading({ text, level, align, className, "data-bcms-field": field, "data-bcms-kind": kind, }: HeadingBlockProps): react_jsx_runtime.JSX.Element;
16
+
17
+ interface TextBlockProps extends TextProps, BcmsBindingProps {
18
+ className?: string;
19
+ align?: "left" | "center" | "right";
20
+ /** Portable Text source. WINS over `html`, which is the derived cache. */
21
+ content?: PortableTextDocument;
22
+ }
23
+ declare function Text({ html, content, align, className, "data-bcms-field": field, "data-bcms-kind": kind, }: TextBlockProps): react_jsx_runtime.JSX.Element;
24
+
25
+ interface ImageBlockProps extends ImageProps, BcmsBindingProps {
26
+ className?: string;
27
+ sizes?: string;
28
+ }
29
+ declare function Image({ src, alt, width, height, caption, className, sizes, "data-bcms-field": field, "data-bcms-kind": kind, }: ImageBlockProps): react_jsx_runtime.JSX.Element;
30
+
31
+ interface ButtonBlockProps extends ButtonProps, BcmsBindingProps {
32
+ className?: string;
33
+ align?: "left" | "center" | "right";
34
+ }
35
+ declare function Button({ text, href, variant, align, className, "data-bcms-field": field, "data-bcms-kind": kind, }: ButtonBlockProps): react_jsx_runtime.JSX.Element;
36
+
37
+ interface SpacerBlockProps extends SpacerProps {
38
+ className?: string;
39
+ }
40
+ declare function Spacer({ height, className }: SpacerBlockProps): react_jsx_runtime.JSX.Element;
41
+
42
+ interface ColumnsBlockProps extends ColumnsProps {
43
+ className?: string;
44
+ renderBlock?: (block: ContentBlock) => React.ReactNode;
45
+ }
46
+ declare function Columns({ columns, gap, className, renderBlock, }: ColumnsBlockProps): react_jsx_runtime.JSX.Element;
47
+
48
+ interface VideoBlockProps extends VideoProps {
49
+ className?: string;
50
+ }
51
+ declare function Video({ url, poster, autoplay, loop, className, }: VideoBlockProps): react_jsx_runtime.JSX.Element;
52
+
53
+ export { type BcmsBindingProps as B, Columns as C, Heading as H, Image as I, Spacer as S, Text as T, Video as V, Button as a };
package/dist/blocks.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- export { B as Button, C as Columns, H as Heading, I as Image, S as Spacer, T as Text, V as Video } from './Video-Dd3TLlXA.js';
1
+ export { a as Button, C as Columns, H as Heading, I as Image, S as Spacer, T as Text, V as Video } from './Video-DRC_fPjV.js';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
- import { SectionProps, ContentBlock, NavbarProps, FooterProps, SliderProps, TabsProps, ComponentProps, FormProps } from '@betttercms/types';
3
+ import { SectionProps, ContentBlock, NavbarProps, FooterProps, SliderProps, TabsProps, ComponentProps, FormProps } from '@bettercms-ai/types';
4
4
  import React from 'react';
5
5
 
6
6
  interface SectionBlockProps extends SectionProps {
@@ -40,7 +40,7 @@ declare function Tabs({ tabs, className, renderBlock }: TabsBlockProps): react_j
40
40
  declare function ComponentInstance({ componentId, className, }: ComponentProps & {
41
41
  className?: string;
42
42
  }): react_jsx_runtime.JSX.Element;
43
- /** Builder-preview placeholder for a `form` block (rendered live by @betttercms/next). */
43
+ /** Builder-preview placeholder for a `form` block (rendered live by @bettercms-ai/next). */
44
44
  declare function FormPlaceholder({ formId, className }: FormProps & {
45
45
  className?: string;
46
46
  }): react_jsx_runtime.JSX.Element;
package/dist/blocks.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,13 +379,13 @@ 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 })
326
389
  ] });
327
390
  }
328
391
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/blocks/Heading.tsx","../src/blocks/Text.tsx","../src/lib/utils.ts","../src/blocks/Image.tsx","../src/blocks/Button.tsx","../src/blocks/Spacer.tsx","../src/blocks/Columns.tsx","../src/blocks/Video.tsx","../src/blocks/Section.tsx","../src/blocks/Navbar.tsx","../src/blocks/Footer.tsx","../src/blocks/Slider.tsx","../src/blocks/Tabs.tsx","../src/blocks/Placeholders.tsx"],"sourcesContent":["import type { HeadingProps } from \"@betttercms/types\";\n\ninterface HeadingBlockProps extends HeadingProps {\n className?: string;\n align?: \"left\" | \"center\" | \"right\";\n}\n\nexport function Heading({\n text,\n level = 2,\n align = \"left\",\n className = \"\",\n}: HeadingBlockProps) {\n const alignClass = {\n left: \"text-left\",\n center: \"text-center\",\n right: \"text-right\",\n }[align];\n\n const classNames = `font-heading font-bold tracking-tight text-foreground ${alignClass} ${className}`;\n\n switch (level) {\n case 1:\n return <h1 className={classNames}>{text}</h1>;\n case 2:\n return <h2 className={classNames}>{text}</h2>;\n case 3:\n return <h3 className={classNames}>{text}</h3>;\n default:\n return <h2 className={classNames}>{text}</h2>;\n }\n}\n","import type { TextProps } from \"@betttercms/types\";\n\ninterface TextBlockProps extends TextProps {\n className?: string;\n align?: \"left\" | \"center\" | \"right\";\n}\n\nexport function Text({ html, align = \"left\", className = \"\" }: TextBlockProps) {\n const alignClass = {\n left: \"text-left\",\n center: \"text-center\",\n right: \"text-right\",\n }[align];\n\n return (\n <div\n className={`text-muted-foreground leading-relaxed ${alignClass} ${className}`}\n // eslint-disable-next-line react/no-danger\n dangerouslySetInnerHTML={{ __html: html }}\n />\n );\n}\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import type { ImageProps } from \"@betttercms/types\";\nimport { cn } from \"../lib/utils\";\n\ninterface ImageBlockProps extends ImageProps {\n className?: string;\n sizes?: string;\n}\n\nexport function Image({\n src,\n alt,\n width,\n height,\n caption,\n className = \"\",\n sizes,\n}: ImageBlockProps) {\n return (\n <figure className={cn(\"w-full\", className)}>\n <img\n src={src}\n alt={alt}\n width={width}\n height={height}\n sizes={sizes}\n className=\"w-full h-auto rounded-md object-cover\"\n loading=\"lazy\"\n />\n {caption && (\n <figcaption className=\"mt-2 text-sm text-muted-foreground text-center italic\">\n {caption}\n </figcaption>\n )}\n </figure>\n );\n}\n","import type { ButtonProps } from \"@betttercms/types\";\nimport { cn } from \"../lib/utils\";\n\ninterface ButtonBlockProps extends ButtonProps {\n className?: string;\n align?: \"left\" | \"center\" | \"right\";\n}\n\nexport function Button({\n text,\n href,\n variant = \"primary\",\n align = \"left\",\n className = \"\",\n}: ButtonBlockProps) {\n const baseStyles =\n \"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\";\n\n const variantStyles = {\n primary:\n \"bg-primary text-primary-foreground shadow hover:bg-primary/90\",\n secondary:\n \"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80\",\n };\n\n const alignClass = {\n left: \"justify-start\",\n center: \"justify-center\",\n right: \"justify-end\",\n }[align];\n\n return (\n <a\n href={href}\n className={cn(baseStyles, variantStyles[variant], alignClass, \"px-4 py-2\", className)}\n >\n {text}\n </a>\n );\n}\n","import type { SpacerProps } from \"@betttercms/types\";\n\ninterface SpacerBlockProps extends SpacerProps {\n className?: string;\n}\n\nexport function Spacer({ height = 32, className = \"\" }: SpacerBlockProps) {\n return (\n <div\n className={`w-full border border-dashed border-muted-foreground/20 rounded flex items-center justify-center ${className}`}\n style={{ height: `${height}px` }}\n aria-hidden=\"true\"\n >\n <span className=\"text-xs text-muted-foreground/40 select-none\">\n {height}px\n </span>\n </div>\n );\n}\n","import type { ColumnsProps, ContentBlock } from \"@betttercms/types\";\nimport { cn } from \"../lib/utils\";\nimport React from \"react\";\n\ninterface ColumnsBlockProps extends ColumnsProps {\n className?: string;\n renderBlock?: (block: ContentBlock) => React.ReactNode;\n}\n\nexport function Columns({\n columns,\n gap = 16,\n className = \"\",\n renderBlock,\n}: ColumnsBlockProps) {\n return (\n <div\n className={cn(\"grid w-full\", className)}\n style={{\n gridTemplateColumns: `repeat(${columns.length}, minmax(0, 1fr))`,\n gap: `${gap}px`,\n }}\n >\n {columns.map((columnBlocks, colIndex) => (\n <div key={colIndex} className=\"flex flex-col gap-4 min-w-0\">\n {columnBlocks.map((block) => (\n <span key={block.id}>\n {renderBlock ? renderBlock(block) : null}\n </span>\n ))}\n </div>\n ))}\n </div>\n );\n}\n","import type { VideoProps } from \"@betttercms/types\";\nimport { useState } from \"react\";\nimport { cn } from \"../lib/utils\";\n\ninterface VideoBlockProps extends VideoProps {\n className?: string;\n}\n\nfunction getEmbedUrl(url: string): { embedUrl: string; provider: \"youtube\" | \"vimeo\" | \"unknown\" } {\n // YouTube\n const ytMatch = url.match(\n /(?:youtube\\.com\\/(?:watch\\?v=|embed\\/)|youtu\\.be\\/)([a-zA-Z0-9_-]{11})/\n );\n if (ytMatch) {\n return {\n embedUrl: `https://www.youtube.com/embed/${ytMatch[1]}`,\n provider: \"youtube\",\n };\n }\n\n // Vimeo\n const vimeoMatch = url.match(/vimeo\\.com\\/(\\d+)/);\n if (vimeoMatch) {\n return {\n embedUrl: `https://player.vimeo.com/video/${vimeoMatch[1]}`,\n provider: \"vimeo\",\n };\n }\n\n return { embedUrl: url, provider: \"unknown\" };\n}\n\nexport function Video({\n url,\n poster,\n autoplay = false,\n loop = false,\n className = \"\",\n}: VideoBlockProps) {\n const [isLoaded, setIsLoaded] = useState(false);\n const { embedUrl, provider } = getEmbedUrl(url);\n\n // If not a recognized embed, render a native video tag\n if (provider === \"unknown\") {\n return (\n <video\n src={url}\n poster={poster}\n autoPlay={autoplay}\n loop={loop}\n controls\n className={className}\n playsInline\n />\n );\n }\n\n return (\n <div className={cn(\"w-full overflow-hidden rounded-md bg-black aspect-video\", className)}>\n {!isLoaded && poster && (\n // eslint-disable-next-line @next/next/no-img-element\n <img\n src={poster}\n alt=\"Video thumbnail\"\n className=\"w-full h-full object-cover absolute inset-0\"\n />\n )}\n <iframe\n src={`${embedUrl}?autoplay=${autoplay ? 1 : 0}&loop=${loop ? 1 : 0}&mute=${autoplay ? 1 : 0}`}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n loading=\"lazy\"\n onLoad={() => setIsLoaded(true)}\n className=\"w-full h-full\"\n />\n </div>\n );\n}\n","import type { SectionProps, ContentBlock } from \"@betttercms/types\";\nimport { cn } from \"../lib/utils\";\nimport React from \"react\";\n\ninterface SectionBlockProps extends SectionProps {\n className?: string;\n renderBlock?: (block: ContentBlock) => React.ReactNode;\n}\n\nexport function Section({\n children,\n background,\n paddingY,\n maxWidth,\n className = \"\",\n renderBlock,\n}: SectionBlockProps) {\n const inner = (children ?? []).map((block) => (\n <span key={block.id}>{renderBlock ? renderBlock(block) : null}</span>\n ));\n return (\n <section\n className={cn(\"w-full\", className)}\n style={{ background, paddingTop: paddingY, paddingBottom: paddingY }}\n >\n {maxWidth ? <div style={{ maxWidth, marginInline: \"auto\" }}>{inner}</div> : inner}\n </section>\n );\n}\n","import type { NavbarProps } from \"@betttercms/types\";\nimport { cn } from \"../lib/utils\";\n\ninterface NavbarBlockProps extends NavbarProps {\n className?: string;\n}\n\nexport function Navbar({ logo, links, cta, className = \"\" }: NavbarBlockProps) {\n return (\n <nav className={cn(\"flex items-center gap-6 py-4\", className)}>\n {logo?.src ? (\n <a href={logo.href ?? \"/\"} className=\"shrink-0\">\n <img src={logo.src} alt={logo.alt} className=\"h-8 w-auto\" />\n </a>\n ) : null}\n <div className=\"flex flex-1 gap-5\">\n {(links ?? []).map((l, i) => (\n <a key={i} href={l.href} className=\"text-inherit no-underline\">\n {l.label}\n </a>\n ))}\n </div>\n {cta ? (\n <a\n href={cta.href}\n className=\"rounded-lg bg-black px-4 py-2 font-semibold text-white no-underline\"\n >\n {cta.text}\n </a>\n ) : null}\n </nav>\n );\n}\n","import type { FooterProps } from \"@betttercms/types\";\nimport { cn } from \"../lib/utils\";\n\ninterface FooterBlockProps extends FooterProps {\n className?: string;\n}\n\nexport function Footer({ columns, copyright, className = \"\" }: FooterBlockProps) {\n return (\n <footer className={cn(\"mt-12 border-t pt-8\", className)}>\n <div className=\"flex flex-wrap gap-12\">\n {(columns ?? []).map((col, i) => (\n <div key={i} className=\"flex flex-col gap-1.5\">\n {col.heading ? <h4 className=\"mb-2 font-semibold\">{col.heading}</h4> : null}\n {(col.links ?? []).map((l, j) => (\n <a key={j} href={l.href} className=\"text-neutral-500 no-underline\">\n {l.label}\n </a>\n ))}\n </div>\n ))}\n </div>\n {copyright ? <p className=\"mt-6 text-sm text-neutral-500\">{copyright}</p> : null}\n </footer>\n );\n}\n","\"use client\";\n\nimport type { SliderProps, ContentBlock } from \"@betttercms/types\";\nimport { cn } from \"../lib/utils\";\nimport React, { useState } from \"react\";\n\ninterface SliderBlockProps extends SliderProps {\n className?: string;\n renderBlock?: (block: ContentBlock) => React.ReactNode;\n}\n\nexport function Slider({ slides, className = \"\", renderBlock }: SliderBlockProps) {\n const [i, setI] = useState(0);\n const list = slides ?? [];\n const go = (n: number) => setI((n + list.length) % (list.length || 1));\n const btn =\n \"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\";\n return (\n <div className={cn(\"relative w-full\", className)}>\n {list.map((s, idx) => (\n <div key={s.id} hidden={idx !== i} className=\"flex flex-col gap-4\">\n {(s.children ?? []).map((b) => (\n <span key={b.id}>{renderBlock ? renderBlock(b) : null}</span>\n ))}\n </div>\n ))}\n <button type=\"button\" aria-label=\"Previous\" onClick={() => go(i - 1)} className={cn(btn, \"left-2\")}>\n &#8249;\n </button>\n <button type=\"button\" aria-label=\"Next\" onClick={() => go(i + 1)} className={cn(btn, \"right-2\")}>\n &#8250;\n </button>\n </div>\n );\n}\n","\"use client\";\n\nimport type { TabsProps, ContentBlock } from \"@betttercms/types\";\nimport { cn } from \"../lib/utils\";\nimport React, { useState } from \"react\";\n\ninterface TabsBlockProps extends TabsProps {\n className?: string;\n renderBlock?: (block: ContentBlock) => React.ReactNode;\n}\n\nexport function Tabs({ tabs, className = \"\", renderBlock }: TabsBlockProps) {\n const [active, setActive] = useState(0);\n const list = tabs ?? [];\n return (\n <div className={cn(\"w-full\", className)}>\n <div role=\"tablist\" className=\"mb-4 flex gap-1 border-b\">\n {list.map((t, idx) => (\n <button\n key={t.id}\n role=\"tab\"\n type=\"button\"\n aria-selected={idx === active}\n onClick={() => setActive(idx)}\n className={cn(\n \"cursor-pointer border-b-2 border-transparent bg-transparent px-4 py-2\",\n idx === active && \"border-black font-semibold\",\n )}\n >\n {t.label}\n </button>\n ))}\n </div>\n {list.map((t, idx) => (\n <div key={t.id} role=\"tabpanel\" hidden={idx !== active} className=\"flex flex-col gap-4\">\n {(t.children ?? []).map((b) => (\n <span key={b.id}>{renderBlock ? renderBlock(b) : null}</span>\n ))}\n </div>\n ))}\n </div>\n );\n}\n","import type { ComponentProps as BcmsComponentProps, FormProps } from \"@betttercms/types\";\nimport { cn } from \"../lib/utils\";\n\nconst box =\n \"rounded border border-dashed border-neutral-300 bg-neutral-50 p-4 text-sm text-neutral-500\";\n\n/**\n * Builder-preview placeholder for a `component` instance. Live instance resolution\n * (definition lookup + override merge) happens server-side in the delivery renderer\n * and in the page-builder, which pass a resolver — the standalone library preview\n * just shows what's referenced.\n */\nexport function ComponentInstance({\n componentId,\n className = \"\",\n}: BcmsComponentProps & { className?: string }) {\n return (\n <div className={cn(box, className)}>\n Component instance: <code>{componentId}</code>\n </div>\n );\n}\n\n/** Builder-preview placeholder for a `form` block (rendered live by @betttercms/next). */\nexport function FormPlaceholder({ formId, className = \"\" }: FormProps & { className?: string }) {\n return (\n <div className={cn(box, className)}>\n Form: <code>{formId}</code>\n </div>\n );\n}\n"],"mappings":"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","names":["jsx","jsx","jsx","jsx","jsxs","jsx","jsx","jsxs","jsx","jsx","jsxs","jsx","jsxs","useState","jsx","jsxs","useState","jsx","jsxs","jsx","jsxs"]}
1
+ {"version":3,"sources":["../src/blocks/Heading.tsx","../src/blocks/portableText.tsx","../src/blocks/Text.tsx","../src/lib/utils.ts","../src/blocks/Image.tsx","../src/blocks/Button.tsx","../src/blocks/Spacer.tsx","../src/blocks/Columns.tsx","../src/blocks/Video.tsx","../src/blocks/Section.tsx","../src/blocks/Navbar.tsx","../src/blocks/Footer.tsx","../src/blocks/Slider.tsx","../src/blocks/Tabs.tsx","../src/blocks/Placeholders.tsx"],"sourcesContent":["import type { HeadingProps } from \"@bettercms-ai/types\";\nimport type { BcmsBindingProps } from \"../binding\";\n\ninterface HeadingBlockProps extends HeadingProps, BcmsBindingProps {\n className?: string;\n align?: \"left\" | \"center\" | \"right\";\n}\n\nexport function Heading({\n text,\n level = 2,\n align = \"left\",\n className = \"\",\n \"data-bcms-field\": field,\n \"data-bcms-kind\": kind,\n}: HeadingBlockProps) {\n const alignClass = {\n left: \"text-left\",\n center: \"text-center\",\n right: \"text-right\",\n }[align];\n\n const classNames = `font-heading font-bold tracking-tight text-foreground ${alignClass} ${className}`;\n\n switch (level) {\n case 1:\n return <h1 className={classNames} data-bcms-field={field} data-bcms-kind={kind}>{text}</h1>;\n case 2:\n return <h2 className={classNames} data-bcms-field={field} data-bcms-kind={kind}>{text}</h2>;\n case 3:\n return <h3 className={classNames} data-bcms-field={field} data-bcms-kind={kind}>{text}</h3>;\n default:\n return <h2 className={classNames} data-bcms-field={field} data-bcms-kind={kind}>{text}</h2>;\n }\n}\n","import React from \"react\";\nimport type { PortableTextDocument, PortableTextNode } from \"@bettercms-ai/types\";\n\n/**\n * Portable Text → React.\n *\n * React rather than an HTML string, deliberately: React escapes text children by default, so\n * author data cannot become markup here at all. The `dangerouslySetInnerHTML` path that the\n * `html` cache still uses has to be trusted; this one does not.\n *\n * Unknown marks and unknown styles are DROPPED, never passed through. A colour mark carries a\n * token NAME (`color:brand-accent`) which becomes a class the consuming site styles — so it is\n * theme-aware and survives a rebrand, which a baked hex does not.\n */\n\nconst MARK_TAGS: Record<string, keyof React.JSX.IntrinsicElements> = {\n strong: \"strong\",\n em: \"em\",\n underline: \"u\",\n \"strike-through\": \"s\",\n code: \"code\",\n};\n\nconst BLOCK_TAGS: Record<string, keyof React.JSX.IntrinsicElements> = {\n normal: \"p\",\n h1: \"h1\",\n h2: \"h2\",\n h3: \"h3\",\n h4: \"h4\",\n h5: \"h5\",\n h6: \"h6\",\n blockquote: \"blockquote\",\n};\n\nfunction Span({ node, index }: { node: PortableTextNode; index: number }) {\n const marks = node.marks ?? [];\n const colour = marks.find((m) => m.startsWith(\"color:\"))?.slice(\"color:\".length);\n\n // Text is a React child, so it is escaped by construction.\n let out: React.ReactNode = node.text ?? \"\";\n for (const m of marks) {\n const Tag = MARK_TAGS[m];\n if (Tag) out = <Tag>{out}</Tag>;\n }\n if (colour && /^[a-z0-9-]+$/i.test(colour)) {\n out = <span className={`bcms-color-${colour}`}>{out}</span>;\n }\n return <React.Fragment key={node._key ?? index}>{out}</React.Fragment>;\n}\n\nexport function PortableText({ doc }: { doc: PortableTextDocument }) {\n return (\n <>\n {doc.content.map((block, i) => {\n const Tag = BLOCK_TAGS[block.style ?? \"normal\"] ?? \"p\";\n return (\n <Tag key={block._key ?? i}>\n {(block.children ?? []).map((c, j) => (\n <Span key={c._key ?? j} node={c} index={j} />\n ))}\n </Tag>\n );\n })}\n </>\n );\n}\n","import type { PortableTextDocument, TextProps } from \"@bettercms-ai/types\";\nimport { PortableText } from \"./portableText\";\nimport type { BcmsBindingProps } from \"../binding\";\n\ninterface TextBlockProps extends TextProps, BcmsBindingProps {\n className?: string;\n align?: \"left\" | \"center\" | \"right\";\n /** Portable Text source. WINS over `html`, which is the derived cache. */\n content?: PortableTextDocument;\n}\n\nexport function Text({\n html,\n content,\n align = \"left\",\n className = \"\",\n \"data-bcms-field\": field,\n \"data-bcms-kind\": kind,\n}: TextBlockProps) {\n const alignClass = {\n left: \"text-left\",\n center: \"text-center\",\n right: \"text-right\",\n }[align];\n\n const cls = `text-muted-foreground leading-relaxed ${alignClass} ${className}`;\n\n // `content` wins when present — it is the source of truth and renders through React, which\n // escapes by construction. `html` remains the fallback so every block authored before\n // Portable Text existed renders exactly as it always did.\n if (content && content.content.length > 0) {\n return (\n <div className={cls}>\n <PortableText doc={content} />\n </div>\n );\n }\n\n return (\n <div\n className={cls}\n data-bcms-field={field}\n data-bcms-kind={kind}\n // eslint-disable-next-line react/no-danger\n dangerouslySetInnerHTML={{ __html: html }}\n />\n );\n}\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import type { ImageProps } from \"@bettercms-ai/types\";\nimport { cn } from \"../lib/utils\";\nimport type { BcmsBindingProps } from \"../binding\";\n\ninterface ImageBlockProps extends ImageProps, BcmsBindingProps {\n className?: string;\n sizes?: string;\n}\n\nexport function Image({\n src,\n alt,\n width,\n height,\n caption,\n className = \"\",\n sizes,\n \"data-bcms-field\": field,\n \"data-bcms-kind\": kind,\n}: ImageBlockProps) {\n return (\n <figure className={cn(\"w-full\", className)}>\n <img\n src={src}\n alt={alt}\n width={width}\n height={height}\n sizes={sizes}\n data-bcms-field={field}\n data-bcms-kind={kind}\n className=\"w-full h-auto rounded-md object-cover\"\n loading=\"lazy\"\n />\n {caption && (\n <figcaption className=\"mt-2 text-sm text-muted-foreground text-center italic\">\n {caption}\n </figcaption>\n )}\n </figure>\n );\n}\n","import type { ButtonProps } from \"@bettercms-ai/types\";\nimport { cn } from \"../lib/utils\";\nimport type { BcmsBindingProps } from \"../binding\";\n\ninterface ButtonBlockProps extends ButtonProps, BcmsBindingProps {\n className?: string;\n align?: \"left\" | \"center\" | \"right\";\n}\n\nexport function Button({\n text,\n href,\n variant = \"primary\",\n align = \"left\",\n className = \"\",\n \"data-bcms-field\": field,\n \"data-bcms-kind\": kind,\n}: ButtonBlockProps) {\n const baseStyles =\n \"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\";\n\n const variantStyles = {\n primary:\n \"bg-primary text-primary-foreground shadow hover:bg-primary/90\",\n secondary:\n \"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80\",\n };\n\n const alignClass = {\n left: \"justify-start\",\n center: \"justify-center\",\n right: \"justify-end\",\n }[align];\n\n return (\n <a\n href={href}\n data-bcms-field={field}\n data-bcms-kind={kind}\n className={cn(baseStyles, variantStyles[variant], alignClass, \"px-4 py-2\", className)}\n >\n {text}\n </a>\n );\n}\n","import type { SpacerProps } from \"@bettercms-ai/types\";\n\ninterface SpacerBlockProps extends SpacerProps {\n className?: string;\n}\n\nexport function Spacer({ height = 32, className = \"\" }: SpacerBlockProps) {\n return (\n <div\n className={`w-full border border-dashed border-muted-foreground/20 rounded flex items-center justify-center ${className}`}\n style={{ height: `${height}px` }}\n aria-hidden=\"true\"\n >\n <span className=\"text-xs text-muted-foreground/40 select-none\">\n {height}px\n </span>\n </div>\n );\n}\n","import type { ColumnsProps, ContentBlock } from \"@bettercms-ai/types\";\nimport { cn } from \"../lib/utils\";\nimport React from \"react\";\n\ninterface ColumnsBlockProps extends ColumnsProps {\n className?: string;\n renderBlock?: (block: ContentBlock) => React.ReactNode;\n}\n\nexport function Columns({\n columns,\n gap = 16,\n className = \"\",\n renderBlock,\n}: ColumnsBlockProps) {\n return (\n <div\n className={cn(\"grid w-full\", className)}\n style={{\n gridTemplateColumns: `repeat(${columns.length}, minmax(0, 1fr))`,\n gap: `${gap}px`,\n }}\n >\n {columns.map((columnBlocks, colIndex) => (\n <div key={colIndex} className=\"flex flex-col gap-4 min-w-0\">\n {columnBlocks.map((block) => (\n <span key={block.id}>\n {renderBlock ? renderBlock(block) : null}\n </span>\n ))}\n </div>\n ))}\n </div>\n );\n}\n","import type { VideoProps } from \"@bettercms-ai/types\";\nimport { useState } from \"react\";\nimport { cn } from \"../lib/utils\";\n\ninterface VideoBlockProps extends VideoProps {\n className?: string;\n}\n\nfunction getEmbedUrl(url: string): { embedUrl: string; provider: \"youtube\" | \"vimeo\" | \"unknown\" } {\n // YouTube\n const ytMatch = url.match(\n /(?:youtube\\.com\\/(?:watch\\?v=|embed\\/)|youtu\\.be\\/)([a-zA-Z0-9_-]{11})/\n );\n if (ytMatch) {\n return {\n embedUrl: `https://www.youtube.com/embed/${ytMatch[1]}`,\n provider: \"youtube\",\n };\n }\n\n // Vimeo\n const vimeoMatch = url.match(/vimeo\\.com\\/(\\d+)/);\n if (vimeoMatch) {\n return {\n embedUrl: `https://player.vimeo.com/video/${vimeoMatch[1]}`,\n provider: \"vimeo\",\n };\n }\n\n return { embedUrl: url, provider: \"unknown\" };\n}\n\nexport function Video({\n url,\n poster,\n autoplay = false,\n loop = false,\n className = \"\",\n}: VideoBlockProps) {\n const [isLoaded, setIsLoaded] = useState(false);\n const { embedUrl, provider } = getEmbedUrl(url);\n\n // If not a recognized embed, render a native video tag\n if (provider === \"unknown\") {\n return (\n <video\n src={url}\n poster={poster}\n autoPlay={autoplay}\n loop={loop}\n controls\n className={className}\n playsInline\n />\n );\n }\n\n return (\n <div className={cn(\"w-full overflow-hidden rounded-md bg-black aspect-video\", className)}>\n {!isLoaded && poster && (\n // eslint-disable-next-line @next/next/no-img-element\n <img\n src={poster}\n alt=\"Video thumbnail\"\n className=\"w-full h-full object-cover absolute inset-0\"\n />\n )}\n <iframe\n src={`${embedUrl}?autoplay=${autoplay ? 1 : 0}&loop=${loop ? 1 : 0}&mute=${autoplay ? 1 : 0}`}\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n loading=\"lazy\"\n onLoad={() => setIsLoaded(true)}\n className=\"w-full h-full\"\n />\n </div>\n );\n}\n","import type { SectionProps, ContentBlock } from \"@bettercms-ai/types\";\nimport { cn } from \"../lib/utils\";\nimport React from \"react\";\n\ninterface SectionBlockProps extends SectionProps {\n className?: string;\n renderBlock?: (block: ContentBlock) => React.ReactNode;\n}\n\nexport function Section({\n children,\n background,\n paddingY,\n maxWidth,\n className = \"\",\n renderBlock,\n}: SectionBlockProps) {\n const inner = (children ?? []).map((block) => (\n <span key={block.id}>{renderBlock ? renderBlock(block) : null}</span>\n ));\n return (\n <section\n className={cn(\"w-full\", className)}\n style={{ background, paddingTop: paddingY, paddingBottom: paddingY }}\n >\n {maxWidth ? <div style={{ maxWidth, marginInline: \"auto\" }}>{inner}</div> : inner}\n </section>\n );\n}\n","import type { NavbarProps } from \"@bettercms-ai/types\";\nimport { cn } from \"../lib/utils\";\n\ninterface NavbarBlockProps extends NavbarProps {\n className?: string;\n}\n\nexport function Navbar({ logo, links, cta, className = \"\" }: NavbarBlockProps) {\n return (\n <nav className={cn(\"flex items-center gap-6 py-4\", className)}>\n {logo?.src ? (\n <a href={logo.href ?? \"/\"} className=\"shrink-0\">\n <img src={logo.src} alt={logo.alt} className=\"h-8 w-auto\" />\n </a>\n ) : null}\n <div className=\"flex flex-1 gap-5\">\n {(links ?? []).map((l, i) => (\n <a key={i} href={l.href} className=\"text-inherit no-underline\">\n {l.label}\n </a>\n ))}\n </div>\n {cta ? (\n <a\n href={cta.href}\n className=\"rounded-lg bg-black px-4 py-2 font-semibold text-white no-underline\"\n >\n {cta.text}\n </a>\n ) : null}\n </nav>\n );\n}\n","import type { FooterProps } from \"@bettercms-ai/types\";\nimport { cn } from \"../lib/utils\";\n\ninterface FooterBlockProps extends FooterProps {\n className?: string;\n}\n\nexport function Footer({ columns, copyright, className = \"\" }: FooterBlockProps) {\n return (\n <footer className={cn(\"mt-12 border-t pt-8\", className)}>\n <div className=\"flex flex-wrap gap-12\">\n {(columns ?? []).map((col, i) => (\n <div key={i} className=\"flex flex-col gap-1.5\">\n {col.heading ? <h4 className=\"mb-2 font-semibold\">{col.heading}</h4> : null}\n {(col.links ?? []).map((l, j) => (\n <a key={j} href={l.href} className=\"text-neutral-500 no-underline\">\n {l.label}\n </a>\n ))}\n </div>\n ))}\n </div>\n {copyright ? <p className=\"mt-6 text-sm text-neutral-500\">{copyright}</p> : null}\n </footer>\n );\n}\n","\"use client\";\n\nimport type { SliderProps, ContentBlock } from \"@bettercms-ai/types\";\nimport { cn } from \"../lib/utils\";\nimport React, { useState } from \"react\";\n\ninterface SliderBlockProps extends SliderProps {\n className?: string;\n renderBlock?: (block: ContentBlock) => React.ReactNode;\n}\n\nexport function Slider({ slides, className = \"\", renderBlock }: SliderBlockProps) {\n const [i, setI] = useState(0);\n const list = slides ?? [];\n const go = (n: number) => setI((n + list.length) % (list.length || 1));\n const btn =\n \"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\";\n return (\n <div className={cn(\"relative w-full\", className)}>\n {list.map((s, idx) => (\n <div key={s.id} hidden={idx !== i} className=\"flex flex-col gap-4\">\n {(s.children ?? []).map((b) => (\n <span key={b.id}>{renderBlock ? renderBlock(b) : null}</span>\n ))}\n </div>\n ))}\n <button type=\"button\" aria-label=\"Previous\" onClick={() => go(i - 1)} className={cn(btn, \"left-2\")}>\n &#8249;\n </button>\n <button type=\"button\" aria-label=\"Next\" onClick={() => go(i + 1)} className={cn(btn, \"right-2\")}>\n &#8250;\n </button>\n </div>\n );\n}\n","\"use client\";\n\nimport type { TabsProps, ContentBlock } from \"@bettercms-ai/types\";\nimport { cn } from \"../lib/utils\";\nimport React, { useState } from \"react\";\n\ninterface TabsBlockProps extends TabsProps {\n className?: string;\n renderBlock?: (block: ContentBlock) => React.ReactNode;\n}\n\nexport function Tabs({ tabs, className = \"\", renderBlock }: TabsBlockProps) {\n const [active, setActive] = useState(0);\n const list = tabs ?? [];\n return (\n <div className={cn(\"w-full\", className)}>\n <div role=\"tablist\" className=\"mb-4 flex gap-1 border-b\">\n {list.map((t, idx) => (\n <button\n key={t.id}\n role=\"tab\"\n type=\"button\"\n aria-selected={idx === active}\n onClick={() => setActive(idx)}\n className={cn(\n \"cursor-pointer border-b-2 border-transparent bg-transparent px-4 py-2\",\n idx === active && \"border-black font-semibold\",\n )}\n >\n {t.label}\n </button>\n ))}\n </div>\n {list.map((t, idx) => (\n <div key={t.id} role=\"tabpanel\" hidden={idx !== active} className=\"flex flex-col gap-4\">\n {(t.children ?? []).map((b) => (\n <span key={b.id}>{renderBlock ? renderBlock(b) : null}</span>\n ))}\n </div>\n ))}\n </div>\n );\n}\n","import type { ComponentProps as BcmsComponentProps, FormProps, CollectionProps } from \"@bettercms-ai/types\";\nimport { cn } from \"../lib/utils\";\n\nconst box =\n \"rounded border border-dashed border-neutral-300 bg-neutral-50 p-4 text-sm text-neutral-500\";\n\n/**\n * Builder-preview placeholder for a `component` instance. Live instance resolution\n * (definition lookup + override merge) happens server-side in the delivery renderer\n * and in the page-builder, which pass a resolver — the standalone library preview\n * just shows what's referenced.\n */\nexport function ComponentInstance({\n componentId,\n className = \"\",\n}: BcmsComponentProps & { className?: string }) {\n return (\n <div className={cn(box, className)}>\n Component instance: <code>{componentId}</code>\n </div>\n );\n}\n\n/** Builder-preview placeholder for a `form` block (rendered live by @bettercms-ai/next). */\nexport function FormPlaceholder({ formId, className = \"\" }: FormProps & { className?: string }) {\n return (\n <div className={cn(box, className)}>\n Form: <code>{formId}</code>\n </div>\n );\n}\n\n/**\n * Builder-preview placeholder for a `collection` block.\n *\n * The builder canvas has no entries to list — the list is fetched per request by the site\n * renderer, deliberately, so that a published snapshot can never bake in a stale set of\n * posts. A placeholder is therefore the honest render here, not a limitation.\n */\nexport function CollectionPlaceholder({\n cardComponentId,\n className = \"\",\n}: CollectionProps & { className?: string }) {\n return (\n <div className={cn(box, className)}>\n Collection: entries of this page\n {cardComponentId ? <> as <code>{cardComponentId}</code></> : <> (default card)</>}\n </div>\n );\n}\n"],"mappings":";;;AA0Ba;AAlBN,SAAS,QAAQ;AAAA,EACtB;AAAA,EACA,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,mBAAmB;AAAA,EACnB,kBAAkB;AACpB,GAAsB;AACpB,QAAM,aAAa;AAAA,IACjB,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,OAAO;AAAA,EACT,EAAE,KAAK;AAEP,QAAM,aAAa,yDAAyD,UAAU,IAAI,SAAS;AAEnG,UAAQ,OAAO;AAAA,IACb,KAAK;AACH,aAAO,oBAAC,QAAG,WAAW,YAAY,mBAAiB,OAAO,kBAAgB,MAAO,gBAAK;AAAA,IACxF,KAAK;AACH,aAAO,oBAAC,QAAG,WAAW,YAAY,mBAAiB,OAAO,kBAAgB,MAAO,gBAAK;AAAA,IACxF,KAAK;AACH,aAAO,oBAAC,QAAG,WAAW,YAAY,mBAAiB,OAAO,kBAAgB,MAAO,gBAAK;AAAA,IACxF;AACE,aAAO,oBAAC,QAAG,WAAW,YAAY,mBAAiB,OAAO,kBAAgB,MAAO,gBAAK;AAAA,EAC1F;AACF;;;AClCA,OAAO,WAAW;AA0CC,SAUf,UAVe,OAAAA,YAAA;AA3BnB,IAAM,YAA+D;AAAA,EACnE,QAAQ;AAAA,EACR,IAAI;AAAA,EACJ,WAAW;AAAA,EACX,kBAAkB;AAAA,EAClB,MAAM;AACR;AAEA,IAAM,aAAgE;AAAA,EACpE,QAAQ;AAAA,EACR,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,YAAY;AACd;AAEA,SAAS,KAAK,EAAE,MAAM,MAAM,GAA8C;AACxE,QAAM,QAAQ,KAAK,SAAS,CAAC;AAC7B,QAAM,SAAS,MAAM,KAAK,CAAC,MAAM,EAAE,WAAW,QAAQ,CAAC,GAAG,MAAM,SAAS,MAAM;AAG/E,MAAI,MAAuB,KAAK,QAAQ;AACxC,aAAW,KAAK,OAAO;AACrB,UAAM,MAAM,UAAU,CAAC;AACvB,QAAI,IAAK,OAAM,gBAAAA,KAAC,OAAK,eAAI;AAAA,EAC3B;AACA,MAAI,UAAU,gBAAgB,KAAK,MAAM,GAAG;AAC1C,UAAM,gBAAAA,KAAC,UAAK,WAAW,cAAc,MAAM,IAAK,eAAI;AAAA,EACtD;AACA,SAAO,gBAAAA,KAAC,MAAM,UAAN,EAAyC,iBAArB,KAAK,QAAQ,KAAY;AACvD;AAEO,SAAS,aAAa,EAAE,IAAI,GAAkC;AACnE,SACE,gBAAAA,KAAA,YACG,cAAI,QAAQ,IAAI,CAAC,OAAO,MAAM;AAC7B,UAAM,MAAM,WAAW,MAAM,SAAS,QAAQ,KAAK;AACnD,WACE,gBAAAA,KAAC,OACG,iBAAM,YAAY,CAAC,GAAG,IAAI,CAAC,GAAG,MAC9B,gBAAAA,KAAC,QAAuB,MAAM,GAAG,OAAO,KAA7B,EAAE,QAAQ,CAAsB,CAC5C,KAHO,MAAM,QAAQ,CAIxB;AAAA,EAEJ,CAAC,GACH;AAEJ;;;AChCQ,gBAAAC,YAAA;AAtBD,SAAS,KAAK;AAAA,EACnB;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,mBAAmB;AAAA,EACnB,kBAAkB;AACpB,GAAmB;AACjB,QAAM,aAAa;AAAA,IACjB,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,OAAO;AAAA,EACT,EAAE,KAAK;AAEP,QAAM,MAAM,yCAAyC,UAAU,IAAI,SAAS;AAK5E,MAAI,WAAW,QAAQ,QAAQ,SAAS,GAAG;AACzC,WACE,gBAAAA,KAAC,SAAI,WAAW,KACd,0BAAAA,KAAC,gBAAa,KAAK,SAAS,GAC9B;AAAA,EAEJ;AAEA,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,MACX,mBAAiB;AAAA,MACjB,kBAAgB;AAAA,MAEhB,yBAAyB,EAAE,QAAQ,KAAK;AAAA;AAAA,EAC1C;AAEJ;;;AC/CA,SAAS,YAA6B;AACtC,SAAS,eAAe;AAEjB,SAAS,MAAM,QAAsB;AAC1C,SAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B;;;ACgBI,SACE,OAAAC,MADF;AAZG,SAAS,MAAM;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA,mBAAmB;AAAA,EACnB,kBAAkB;AACpB,GAAoB;AAClB,SACE,qBAAC,YAAO,WAAW,GAAG,UAAU,SAAS,GACvC;AAAA,oBAAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,mBAAiB;AAAA,QACjB,kBAAgB;AAAA,QAChB,WAAU;AAAA,QACV,SAAQ;AAAA;AAAA,IACV;AAAA,IACC,WACC,gBAAAA,KAAC,gBAAW,WAAU,yDACnB,mBACH;AAAA,KAEJ;AAEJ;;;ACLI,gBAAAC,YAAA;AA1BG,SAAS,OAAO;AAAA,EACrB;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,mBAAmB;AAAA,EACnB,kBAAkB;AACpB,GAAqB;AACnB,QAAM,aACJ;AAEF,QAAM,gBAAgB;AAAA,IACpB,SACE;AAAA,IACF,WACE;AAAA,EACJ;AAEA,QAAM,aAAa;AAAA,IACjB,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,OAAO;AAAA,EACT,EAAE,KAAK;AAEP,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,mBAAiB;AAAA,MACjB,kBAAgB;AAAA,MAChB,WAAW,GAAG,YAAY,cAAc,OAAO,GAAG,YAAY,aAAa,SAAS;AAAA,MAEnF;AAAA;AAAA,EACH;AAEJ;;;ACpCI,gBAAAC,MAKE,QAAAC,aALF;AAFG,SAAS,OAAO,EAAE,SAAS,IAAI,YAAY,GAAG,GAAqB;AACxE,SACE,gBAAAD;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,mGAAmG,SAAS;AAAA,MACvH,OAAO,EAAE,QAAQ,GAAG,MAAM,KAAK;AAAA,MAC/B,eAAY;AAAA,MAEZ,0BAAAC,MAAC,UAAK,WAAU,gDACb;AAAA;AAAA,QAAO;AAAA,SACV;AAAA;AAAA,EACF;AAEJ;;;AChBA,OAAkB;AAwBN,gBAAAC,YAAA;AAjBL,SAAS,QAAQ;AAAA,EACtB;AAAA,EACA,MAAM;AAAA,EACN,YAAY;AAAA,EACZ;AACF,GAAsB;AACpB,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,eAAe,SAAS;AAAA,MACtC,OAAO;AAAA,QACL,qBAAqB,UAAU,QAAQ,MAAM;AAAA,QAC7C,KAAK,GAAG,GAAG;AAAA,MACb;AAAA,MAEC,kBAAQ,IAAI,CAAC,cAAc,aAC1B,gBAAAA,KAAC,SAAmB,WAAU,+BAC3B,uBAAa,IAAI,CAAC,UACjB,gBAAAA,KAAC,UACE,wBAAc,YAAY,KAAK,IAAI,QAD3B,MAAM,EAEjB,CACD,KALO,QAMV,CACD;AAAA;AAAA,EACH;AAEJ;;;ACjCA,SAAS,gBAAgB;AA4CnB,gBAAAC,MAaF,QAAAC,aAbE;AArCN,SAAS,YAAY,KAA8E;AAEjG,QAAM,UAAU,IAAI;AAAA,IAClB;AAAA,EACF;AACA,MAAI,SAAS;AACX,WAAO;AAAA,MACL,UAAU,iCAAiC,QAAQ,CAAC,CAAC;AAAA,MACrD,UAAU;AAAA,IACZ;AAAA,EACF;AAGA,QAAM,aAAa,IAAI,MAAM,mBAAmB;AAChD,MAAI,YAAY;AACd,WAAO;AAAA,MACL,UAAU,kCAAkC,WAAW,CAAC,CAAC;AAAA,MACzD,UAAU;AAAA,IACZ;AAAA,EACF;AAEA,SAAO,EAAE,UAAU,KAAK,UAAU,UAAU;AAC9C;AAEO,SAAS,MAAM;AAAA,EACpB;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,OAAO;AAAA,EACP,YAAY;AACd,GAAoB;AAClB,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,EAAE,UAAU,SAAS,IAAI,YAAY,GAAG;AAG9C,MAAI,aAAa,WAAW;AAC1B,WACE,gBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL;AAAA,QACA,UAAU;AAAA,QACV;AAAA,QACA,UAAQ;AAAA,QACR;AAAA,QACA,aAAW;AAAA;AAAA,IACb;AAAA,EAEJ;AAEA,SACE,gBAAAC,MAAC,SAAI,WAAW,GAAG,2DAA2D,SAAS,GACpF;AAAA,KAAC,YAAY;AAAA,IAEZ,gBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,KAAI;AAAA,QACJ,WAAU;AAAA;AAAA,IACZ;AAAA,IAEF,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,KAAK,GAAG,QAAQ,aAAa,WAAW,IAAI,CAAC,SAAS,OAAO,IAAI,CAAC,SAAS,WAAW,IAAI,CAAC;AAAA,QAC3F,OAAM;AAAA,QACN,iBAAe;AAAA,QACf,SAAQ;AAAA,QACR,QAAQ,MAAM,YAAY,IAAI;AAAA,QAC9B,WAAU;AAAA;AAAA,IACZ;AAAA,KACF;AAEJ;;;AC3EA,OAAkB;AAgBd,gBAAAE,YAAA;AATG,SAAS,QAAQ;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AACF,GAAsB;AACpB,QAAM,SAAS,YAAY,CAAC,GAAG,IAAI,CAAC,UAClC,gBAAAA,KAAC,UAAqB,wBAAc,YAAY,KAAK,IAAI,QAA9C,MAAM,EAA6C,CAC/D;AACD,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,UAAU,SAAS;AAAA,MACjC,OAAO,EAAE,YAAY,YAAY,UAAU,eAAe,SAAS;AAAA,MAElE,qBAAW,gBAAAA,KAAC,SAAI,OAAO,EAAE,UAAU,cAAc,OAAO,GAAI,iBAAM,IAAS;AAAA;AAAA,EAC9E;AAEJ;;;ACnBI,SAGM,OAAAC,OAHN,QAAAC,aAAA;AAFG,SAAS,OAAO,EAAE,MAAM,OAAO,KAAK,YAAY,GAAG,GAAqB;AAC7E,SACE,gBAAAA,MAAC,SAAI,WAAW,GAAG,gCAAgC,SAAS,GACzD;AAAA,UAAM,MACL,gBAAAD,MAAC,OAAE,MAAM,KAAK,QAAQ,KAAK,WAAU,YACnC,0BAAAA,MAAC,SAAI,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,WAAU,cAAa,GAC5D,IACE;AAAA,IACJ,gBAAAA,MAAC,SAAI,WAAU,qBACX,oBAAS,CAAC,GAAG,IAAI,CAAC,GAAG,MACrB,gBAAAA,MAAC,OAAU,MAAM,EAAE,MAAM,WAAU,6BAChC,YAAE,SADG,CAER,CACD,GACH;AAAA,IACC,MACC,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,IAAI;AAAA,QACV,WAAU;AAAA,QAET,cAAI;AAAA;AAAA,IACP,IACE;AAAA,KACN;AAEJ;;;ACpBU,SACiB,OAAAE,OADjB,QAAAC,aAAA;AALH,SAAS,OAAO,EAAE,SAAS,WAAW,YAAY,GAAG,GAAqB;AAC/E,SACE,gBAAAA,MAAC,YAAO,WAAW,GAAG,uBAAuB,SAAS,GACpD;AAAA,oBAAAD,MAAC,SAAI,WAAU,yBACX,sBAAW,CAAC,GAAG,IAAI,CAAC,KAAK,MACzB,gBAAAC,MAAC,SAAY,WAAU,yBACpB;AAAA,UAAI,UAAU,gBAAAD,MAAC,QAAG,WAAU,sBAAsB,cAAI,SAAQ,IAAQ;AAAA,OACrE,IAAI,SAAS,CAAC,GAAG,IAAI,CAAC,GAAG,MACzB,gBAAAA,MAAC,OAAU,MAAM,EAAE,MAAM,WAAU,iCAChC,YAAE,SADG,CAER,CACD;AAAA,SANO,CAOV,CACD,GACH;AAAA,IACC,YAAY,gBAAAA,MAAC,OAAE,WAAU,iCAAiC,qBAAU,IAAO;AAAA,KAC9E;AAEJ;;;ACrBA,SAAgB,YAAAE,iBAAgB;AAc5B,SAIQ,OAAAC,OAJR,QAAAC,aAAA;AAPG,SAAS,OAAO,EAAE,QAAQ,YAAY,IAAI,YAAY,GAAqB;AAChF,QAAM,CAAC,GAAG,IAAI,IAAIF,UAAS,CAAC;AAC5B,QAAM,OAAO,UAAU,CAAC;AACxB,QAAM,KAAK,CAAC,MAAc,MAAM,IAAI,KAAK,WAAW,KAAK,UAAU,EAAE;AACrE,QAAM,MACJ;AACF,SACE,gBAAAE,MAAC,SAAI,WAAW,GAAG,mBAAmB,SAAS,GAC5C;AAAA,SAAK,IAAI,CAAC,GAAG,QACZ,gBAAAD,MAAC,SAAe,QAAQ,QAAQ,GAAG,WAAU,uBACzC,aAAE,YAAY,CAAC,GAAG,IAAI,CAAC,MACvB,gBAAAA,MAAC,UAAiB,wBAAc,YAAY,CAAC,IAAI,QAAtC,EAAE,EAAyC,CACvD,KAHO,EAAE,EAIZ,CACD;AAAA,IACD,gBAAAA,MAAC,YAAO,MAAK,UAAS,cAAW,YAAW,SAAS,MAAM,GAAG,IAAI,CAAC,GAAG,WAAW,GAAG,KAAK,QAAQ,GAAG,oBAEpG;AAAA,IACA,gBAAAA,MAAC,YAAO,MAAK,UAAS,cAAW,QAAO,SAAS,MAAM,GAAG,IAAI,CAAC,GAAG,WAAW,GAAG,KAAK,SAAS,GAAG,oBAEjG;AAAA,KACF;AAEJ;;;AC9BA,SAAgB,YAAAE,iBAAgB;AAW5B,SAGM,OAAAC,OAHN,QAAAC,aAAA;AAJG,SAAS,KAAK,EAAE,MAAM,YAAY,IAAI,YAAY,GAAmB;AAC1E,QAAM,CAAC,QAAQ,SAAS,IAAIF,UAAS,CAAC;AACtC,QAAM,OAAO,QAAQ,CAAC;AACtB,SACE,gBAAAE,MAAC,SAAI,WAAW,GAAG,UAAU,SAAS,GACpC;AAAA,oBAAAD,MAAC,SAAI,MAAK,WAAU,WAAU,4BAC3B,eAAK,IAAI,CAAC,GAAG,QACZ,gBAAAA;AAAA,MAAC;AAAA;AAAA,QAEC,MAAK;AAAA,QACL,MAAK;AAAA,QACL,iBAAe,QAAQ;AAAA,QACvB,SAAS,MAAM,UAAU,GAAG;AAAA,QAC5B,WAAW;AAAA,UACT;AAAA,UACA,QAAQ,UAAU;AAAA,QACpB;AAAA,QAEC,YAAE;AAAA;AAAA,MAVE,EAAE;AAAA,IAWT,CACD,GACH;AAAA,IACC,KAAK,IAAI,CAAC,GAAG,QACZ,gBAAAA,MAAC,SAAe,MAAK,YAAW,QAAQ,QAAQ,QAAQ,WAAU,uBAC9D,aAAE,YAAY,CAAC,GAAG,IAAI,CAAC,MACvB,gBAAAA,MAAC,UAAiB,wBAAc,YAAY,CAAC,IAAI,QAAtC,EAAE,EAAyC,CACvD,KAHO,EAAE,EAIZ,CACD;AAAA,KACH;AAEJ;;;ACzBI,SA6BqB,YAAAE,WA5BC,OAAAC,OADtB,QAAAC,aAAA;AAdJ,IAAM,MACJ;AAQK,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA,YAAY;AACd,GAAgD;AAC9C,SACE,gBAAAA,MAAC,SAAI,WAAW,GAAG,KAAK,SAAS,GAAG;AAAA;AAAA,IACd,gBAAAD,MAAC,UAAM,uBAAY;AAAA,KACzC;AAEJ;AAGO,SAAS,gBAAgB,EAAE,QAAQ,YAAY,GAAG,GAAuC;AAC9F,SACE,gBAAAC,MAAC,SAAI,WAAW,GAAG,KAAK,SAAS,GAAG;AAAA;AAAA,IAC5B,gBAAAD,MAAC,UAAM,kBAAO;AAAA,KACtB;AAEJ;","names":["jsx","jsx","jsx","jsx","jsx","jsxs","jsx","jsx","jsxs","jsx","jsx","jsxs","jsx","jsxs","useState","jsx","jsxs","useState","jsx","jsxs","Fragment","jsx","jsxs"]}
package/dist/index.d.ts CHANGED
@@ -1,11 +1,10 @@
1
- export { B as Button, C as Columns, H as Heading, I as Image, S as Spacer, T as Text, V as Video } from './Video-Dd3TLlXA.js';
1
+ export { B as BcmsBindingProps, a as Button, C as Columns, H as Heading, I as Image, S as Spacer, T as Text, V as Video } from './Video-DRC_fPjV.js';
2
2
  export { BLOCK_REGISTRY, BLOCK_TYPES, BlockRegistryEntry } from './registry.js';
3
3
  export { BlockRenderer, BlocksRenderer } from './renderer.js';
4
4
  import { ClassValue } from 'clsx';
5
5
  import 'react/jsx-runtime';
6
- import '@betttercms/types';
7
- import 'react';
8
6
  import '@bettercms-ai/types';
7
+ import 'react';
9
8
 
10
9
  declare function cn(...inputs: ClassValue[]): string;
11
10