@frontlit/page-builder 0.1.0-alpha.2 → 0.1.0-alpha.3

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.
Files changed (98) hide show
  1. package/README.md +32 -0
  2. package/dist/blocks/index.d.ts +33 -21
  3. package/dist/blocks/index.js +3 -3
  4. package/dist/builder/index.d.ts +7 -8
  5. package/dist/builder/index.js +6 -5
  6. package/dist/{button-Bzt1GDO-.d.ts → button-OLKN3wPf.d.ts} +2 -2
  7. package/dist/{chunk-H4P5MFJI.js → chunk-36QHCIPM.js} +205 -251
  8. package/dist/chunk-36QHCIPM.js.map +1 -0
  9. package/dist/chunk-BBI7UHOA.js +56 -0
  10. package/dist/chunk-BBI7UHOA.js.map +1 -0
  11. package/dist/{chunk-KQKSLI3L.js → chunk-GIKTOAQS.js} +335 -122
  12. package/dist/chunk-GIKTOAQS.js.map +1 -0
  13. package/dist/chunk-HWOVSHIW.js +4 -0
  14. package/dist/{chunk-RUYOL7JZ.js.map → chunk-HWOVSHIW.js.map} +1 -1
  15. package/dist/{chunk-HDINUNI2.js → chunk-IU4KATI2.js} +69 -96
  16. package/dist/chunk-IU4KATI2.js.map +1 -0
  17. package/dist/{chunk-SGNRWWG3.js → chunk-J7Z4UW45.js} +530 -135
  18. package/dist/chunk-J7Z4UW45.js.map +1 -0
  19. package/dist/{chunk-UO7XGBGM.js → chunk-K4WCLHMS.js} +1182 -1033
  20. package/dist/chunk-K4WCLHMS.js.map +1 -0
  21. package/dist/components/index.d.ts +33 -8
  22. package/dist/components/index.js +42 -2
  23. package/dist/components/index.js.map +1 -1
  24. package/dist/data-slots.d.ts +3 -3
  25. package/dist/fonts/archivo/archivo-latin-variable.woff2 +0 -0
  26. package/dist/fonts/hanken-grotesk/hanken-grotesk-latin-variable.woff2 +0 -0
  27. package/dist/fonts/jetbrains-mono/jetbrains-mono-latin-variable.woff2 +0 -0
  28. package/dist/fonts/spline-sans-mono/spline-sans-mono-latin-variable.woff2 +0 -0
  29. package/dist/heading-DQCxQt38.d.ts +20 -0
  30. package/dist/image-Bk8kPXwP.d.ts +52 -0
  31. package/dist/index.d.ts +17 -10
  32. package/dist/index.js +7 -6
  33. package/dist/models/index.d.ts +5 -5
  34. package/dist/models/index.js +1 -1
  35. package/dist/{page-W3hQga4w.d.ts → page-B5Gmm-Ys.d.ts} +1 -1
  36. package/dist/primitives/index.d.ts +44 -9
  37. package/dist/primitives/index.js +1 -1
  38. package/dist/renderer/index.d.ts +12 -8
  39. package/dist/renderer/index.js +5 -4
  40. package/dist/{section-background-MsdE40Rk.d.ts → section-background-6V_LCi5e.d.ts} +1 -1
  41. package/dist/server/index.d.ts +103 -0
  42. package/dist/server/index.js +2649 -0
  43. package/dist/server/index.js.map +1 -0
  44. package/dist/server/islands/code-snippet.d.ts +17 -0
  45. package/dist/server/islands/code-snippet.js +72 -0
  46. package/dist/server/islands/code-snippet.js.map +1 -0
  47. package/dist/server/islands/email-form.d.ts +50 -0
  48. package/dist/server/islands/email-form.js +406 -0
  49. package/dist/server/islands/email-form.js.map +1 -0
  50. package/dist/server/islands/header-mobile-menu.d.ts +22 -0
  51. package/dist/server/islands/header-mobile-menu.js +422 -0
  52. package/dist/server/islands/header-mobile-menu.js.map +1 -0
  53. package/dist/server/islands/inline-embed.d.ts +28 -0
  54. package/dist/server/islands/inline-embed.js +84 -0
  55. package/dist/server/islands/inline-embed.js.map +1 -0
  56. package/dist/server/islands/marquee-track.d.ts +18 -0
  57. package/dist/server/islands/marquee-track.js +67 -0
  58. package/dist/server/islands/marquee-track.js.map +1 -0
  59. package/dist/server/islands/nav-link.d.ts +37 -0
  60. package/dist/server/islands/nav-link.js +67 -0
  61. package/dist/server/islands/nav-link.js.map +1 -0
  62. package/dist/server/islands/support-form.d.ts +17 -0
  63. package/dist/server/islands/support-form.js +469 -0
  64. package/dist/server/islands/support-form.js.map +1 -0
  65. package/dist/server/islands/theme-mode-switcher.d.ts +38 -0
  66. package/dist/server/islands/theme-mode-switcher.js +169 -0
  67. package/dist/server/islands/theme-mode-switcher.js.map +1 -0
  68. package/dist/server/islands/video-with-preview.d.ts +13 -0
  69. package/dist/server/islands/video-with-preview.js +269 -0
  70. package/dist/server/islands/video-with-preview.js.map +1 -0
  71. package/dist/server/primitives.d.ts +200 -0
  72. package/dist/server/primitives.js +888 -0
  73. package/dist/server/primitives.js.map +1 -0
  74. package/dist/server/schema.d.ts +11308 -0
  75. package/dist/server/schema.js +6179 -0
  76. package/dist/server/schema.js.map +1 -0
  77. package/dist/server/theme-CF3fIKvw.d.ts +9 -0
  78. package/dist/server/theme-style-5_AGy-4V.d.ts +183 -0
  79. package/dist/server/theme-style-BPaMwk88.d.ts +183 -0
  80. package/dist/server/theme.d.ts +28 -0
  81. package/dist/server/theme.js +1671 -0
  82. package/dist/server/theme.js.map +1 -0
  83. package/dist/server/widget-YH9BrOwY.d.ts +77 -0
  84. package/dist/styles.css +121 -15
  85. package/dist/{theme-style-Cbl7NM-T.d.ts → theme-style-BVd5tOsc.d.ts} +20 -0
  86. package/dist/theme.css +12 -0
  87. package/dist/{widget-B_RoG6se.d.ts → widget-CpE2HdgU.d.ts} +17 -14
  88. package/package.json +60 -11
  89. package/schema/page.v1.json +3855 -0
  90. package/schema/site.v1.json +3811 -0
  91. package/dist/alignment-dZDzN96G.d.ts +0 -5
  92. package/dist/chunk-H4P5MFJI.js.map +0 -1
  93. package/dist/chunk-HDINUNI2.js.map +0 -1
  94. package/dist/chunk-KQKSLI3L.js.map +0 -1
  95. package/dist/chunk-RUYOL7JZ.js +0 -4
  96. package/dist/chunk-SGNRWWG3.js.map +0 -1
  97. package/dist/chunk-UO7XGBGM.js.map +0 -1
  98. package/dist/image-DAWVx1zQ.d.ts +0 -31
@@ -0,0 +1,4 @@
1
+ "use client";
2
+
3
+ //# sourceMappingURL=chunk-HWOVSHIW.js.map
4
+ //# sourceMappingURL=chunk-HWOVSHIW.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-RUYOL7JZ.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-HWOVSHIW.js"}
@@ -1,12 +1,16 @@
1
1
  "use client";
2
- import { DataSlot, Embed, Marquee, MediaBlock, Grid, RichText, Footer, SupportForm, EmailForm, Banner, Pricing, Faq, Featured, Content, Hero, Header } from './chunk-UO7XGBGM.js';
3
- import { PageBuilderLinkProvider } from './chunk-H4P5MFJI.js';
4
- import { cn } from './chunk-SGNRWWG3.js';
5
- import React, { useRef, useId, useEffect } from 'react';
6
- import { Settings2, ArrowUp, ArrowDown, Plus, Trash2 } from 'lucide-react';
2
+ import { ThemeModeSwitcher } from './chunk-BBI7UHOA.js';
3
+ import { DataSlot, CodeSnippetBlock, Embed, Marquee, MediaBlock, Grid, RichText, Footer, SupportForm, EmailForm, Banner, Pricing, Faq, Featured, Content, Hero, Header } from './chunk-K4WCLHMS.js';
4
+ import { PageBuilderLinkProvider } from './chunk-36QHCIPM.js';
5
+ import { cn } from './chunk-J7Z4UW45.js';
6
+ import { useRef, useId, useEffect } from 'react';
7
+ import { Settings2, ArrowUp, ArrowDown, Trash2, Plus } from 'lucide-react';
7
8
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
8
9
  import convert from 'color-convert';
9
10
 
11
+ // src/registry-names.ts
12
+ var LAYOUT_BLOCK_NAMES = ["header", "footer"];
13
+
10
14
  // src/registry.ts
11
15
  var blockRegistry = {
12
16
  header: Header,
@@ -24,9 +28,9 @@ var blockRegistry = {
24
28
  media: MediaBlock,
25
29
  marquee: Marquee,
26
30
  embed: Embed,
31
+ "code-snippet": CodeSnippetBlock,
27
32
  "data-slot": DataSlot
28
33
  };
29
- var LAYOUT_BLOCK_NAMES = ["header", "footer"];
30
34
  var NON_ADDABLE_BLOCK_NAMES = [...LAYOUT_BLOCK_NAMES, "data-slot"];
31
35
  function registerBlock(widget) {
32
36
  const name = widget.metadata.name.trim();
@@ -72,47 +76,6 @@ function ToolbarButton({
72
76
  }
73
77
  );
74
78
  }
75
- function DeleteButton({ onDelete }) {
76
- const [confirming, setConfirming] = React.useState(false);
77
- React.useEffect(() => {
78
- if (!confirming) return;
79
- const timeout = setTimeout(() => setConfirming(false), 3e3);
80
- return () => clearTimeout(timeout);
81
- }, [confirming]);
82
- if (!onDelete) return null;
83
- if (confirming) {
84
- return /* @__PURE__ */ jsx(
85
- "button",
86
- {
87
- type: "button",
88
- title: "Confirm delete",
89
- "aria-label": "Confirm delete",
90
- className: "inline-flex h-7 items-center justify-center rounded bg-destructive px-2 text-xs font-medium text-destructive-foreground shadow-sm hover:bg-destructive/90",
91
- onMouseLeave: () => setConfirming(false),
92
- onClick: (e) => {
93
- e.stopPropagation();
94
- setConfirming(false);
95
- onDelete();
96
- },
97
- children: "Sure?"
98
- }
99
- );
100
- }
101
- return /* @__PURE__ */ jsx(
102
- "button",
103
- {
104
- type: "button",
105
- title: "Delete",
106
- "aria-label": "Delete",
107
- className: "inline-flex h-7 w-7 items-center justify-center rounded bg-background text-foreground shadow-sm hover:bg-muted",
108
- onClick: (e) => {
109
- e.stopPropagation();
110
- setConfirming(true);
111
- },
112
- children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
113
- }
114
- );
115
- }
116
79
  function EditableBlock({
117
80
  children,
118
81
  editing,
@@ -155,10 +118,10 @@ function EditableBlock({
155
118
  /* @__PURE__ */ jsx(ToolbarButton, { icon: Settings2, label: "Edit settings", onClick: onSelect }),
156
119
  /* @__PURE__ */ jsx(ToolbarButton, { icon: ArrowUp, label: "Move up", onClick: onMoveUp }),
157
120
  /* @__PURE__ */ jsx(ToolbarButton, { icon: ArrowDown, label: "Move down", onClick: onMoveDown }),
158
- /* @__PURE__ */ jsx(DeleteButton, { onDelete })
121
+ /* @__PURE__ */ jsx(ToolbarButton, { icon: Trash2, label: "Delete", onClick: onDelete })
159
122
  ] }) }),
160
123
  children,
161
- onAddBelow && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-x-0 -bottom-3 z-20 flex justify-center opacity-0 transition-opacity group-hover/block:opacity-100", children: /* @__PURE__ */ jsx(
124
+ onAddBelow && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-x-0 -bottom-3 z-30 flex justify-center opacity-0 transition-opacity group-hover/block:opacity-100", children: /* @__PURE__ */ jsx(
162
125
  "button",
163
126
  {
164
127
  type: "button",
@@ -179,13 +142,17 @@ function EditableBlock({
179
142
  function formatHSL(hsl) {
180
143
  return `${hsl[0]} ${hsl[1]}% ${hsl[2]}%`;
181
144
  }
182
- function cssColorFromHex(hex) {
183
- if (!hex) return "";
184
- try {
185
- return `hsl(${formatHSL(convert.hex.hsl(hex.replace("#", "")))})`;
186
- } catch {
187
- return "";
145
+ var SHORT_OR_LONG_HEX = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i;
146
+ var HEX_WITH_ALPHA = /^#([0-9a-f]{4}|[0-9a-f]{8})$/i;
147
+ var CSS_COLOR_FUNCTION = /^(oklch|oklab|lab|lch|hwb|hsla?|rgba?|color)\(\s*[-+0-9a-z%.,/\s]+\)$/i;
148
+ function resolveThemeColor(value) {
149
+ const color = value?.trim();
150
+ if (!color) return "";
151
+ if (SHORT_OR_LONG_HEX.test(color)) {
152
+ return `hsl(${formatHSL(convert.hex.hsl(color.replace("#", "")))})`;
188
153
  }
154
+ if (HEX_WITH_ALPHA.test(color) || CSS_COLOR_FUNCTION.test(color)) return color;
155
+ return "";
189
156
  }
190
157
  function generateThemeStyles(theme, selector = ".pb-theme") {
191
158
  const colors = theme?.theme?.colors;
@@ -197,37 +164,37 @@ function generateThemeStyles(theme, selector = ".pb-theme") {
197
164
  return "";
198
165
  }
199
166
  const block = (c) => `
200
- --background: ${cssColorFromHex(c.background)};
201
- --foreground: ${cssColorFromHex(c.foreground)};
202
- --card: ${cssColorFromHex(c.card)};
203
- --card-foreground: ${cssColorFromHex(c.cardForeground)};
204
- --popover: ${cssColorFromHex(c.popover)};
205
- --popover-foreground: ${cssColorFromHex(c.popoverForeground)};
206
- --primary: ${cssColorFromHex(c.primary)};
207
- --primary-foreground: ${cssColorFromHex(c.primaryForeground)};
208
- --secondary: ${cssColorFromHex(c.secondary)};
209
- --secondary-foreground: ${cssColorFromHex(c.secondaryForeground)};
210
- --muted: ${cssColorFromHex(c.muted)};
211
- --muted-foreground: ${cssColorFromHex(c.mutedForeground)};
212
- --accent: ${cssColorFromHex(c.accent)};
213
- --accent-foreground: ${cssColorFromHex(c.accentForeground)};
214
- --destructive: ${cssColorFromHex(c.destructive)};
215
- --border: ${cssColorFromHex(c.border)};
216
- --input: ${cssColorFromHex(c.input)};
217
- --ring: ${cssColorFromHex(c.ring)};
218
- --chart-1: ${cssColorFromHex(c.chart1)};
219
- --chart-2: ${cssColorFromHex(c.chart2)};
220
- --chart-3: ${cssColorFromHex(c.chart3)};
221
- --chart-4: ${cssColorFromHex(c.chart4)};
222
- --chart-5: ${cssColorFromHex(c.chart5)};
223
- --sidebar-background: ${cssColorFromHex(c.sidebar)};
224
- --sidebar-foreground: ${cssColorFromHex(c.sidebarForeground)};
225
- --sidebar-primary: ${cssColorFromHex(c.sidebarPrimary)};
226
- --sidebar-primary-foreground: ${cssColorFromHex(c.sidebarPrimaryForeground)};
227
- --sidebar-accent: ${cssColorFromHex(c.sidebarAccent)};
228
- --sidebar-accent-foreground: ${cssColorFromHex(c.sidebarAccentForeground)};
229
- --sidebar-border: ${cssColorFromHex(c.sidebarBorder)};
230
- --sidebar-ring: ${cssColorFromHex(c.sidebarRing)};
167
+ --background: ${resolveThemeColor(c.background)};
168
+ --foreground: ${resolveThemeColor(c.foreground)};
169
+ --card: ${resolveThemeColor(c.card)};
170
+ --card-foreground: ${resolveThemeColor(c.cardForeground)};
171
+ --popover: ${resolveThemeColor(c.popover)};
172
+ --popover-foreground: ${resolveThemeColor(c.popoverForeground)};
173
+ --primary: ${resolveThemeColor(c.primary)};
174
+ --primary-foreground: ${resolveThemeColor(c.primaryForeground)};
175
+ --secondary: ${resolveThemeColor(c.secondary)};
176
+ --secondary-foreground: ${resolveThemeColor(c.secondaryForeground)};
177
+ --muted: ${resolveThemeColor(c.muted)};
178
+ --muted-foreground: ${resolveThemeColor(c.mutedForeground)};
179
+ --accent: ${resolveThemeColor(c.accent)};
180
+ --accent-foreground: ${resolveThemeColor(c.accentForeground)};
181
+ --destructive: ${resolveThemeColor(c.destructive)};
182
+ --border: ${resolveThemeColor(c.border)};
183
+ --input: ${resolveThemeColor(c.input)};
184
+ --ring: ${resolveThemeColor(c.ring)};
185
+ --chart-1: ${resolveThemeColor(c.chart1)};
186
+ --chart-2: ${resolveThemeColor(c.chart2)};
187
+ --chart-3: ${resolveThemeColor(c.chart3)};
188
+ --chart-4: ${resolveThemeColor(c.chart4)};
189
+ --chart-5: ${resolveThemeColor(c.chart5)};
190
+ --sidebar-background: ${resolveThemeColor(c.sidebar)};
191
+ --sidebar-foreground: ${resolveThemeColor(c.sidebarForeground)};
192
+ --sidebar-primary: ${resolveThemeColor(c.sidebarPrimary)};
193
+ --sidebar-primary-foreground: ${resolveThemeColor(c.sidebarPrimaryForeground)};
194
+ --sidebar-accent: ${resolveThemeColor(c.sidebarAccent)};
195
+ --sidebar-accent-foreground: ${resolveThemeColor(c.sidebarAccentForeground)};
196
+ --sidebar-border: ${resolveThemeColor(c.sidebarBorder)};
197
+ --sidebar-ring: ${resolveThemeColor(c.sidebarRing)};
231
198
  --shadow-2xs: ${c.shadow2xs ?? ""};
232
199
  --shadow-xs: ${c.shadowXs ?? ""};
233
200
  --shadow-sm: ${c.shadowSm ?? ""};
@@ -252,7 +219,6 @@ function PageRenderer({
252
219
  children,
253
220
  dataSlots,
254
221
  nextTheme = "light",
255
- themeMode = "light",
256
222
  onThemeModeChange,
257
223
  editing = false,
258
224
  selectedWidgetId,
@@ -276,6 +242,16 @@ function PageRenderer({
276
242
  const header = layout.find((widget) => widget.name === "header");
277
243
  const footer = layout.find((widget) => widget.name === "footer");
278
244
  const body = layout.filter((widget) => !LAYOUT_BLOCK_NAMES.includes(widget.name));
245
+ const themeSwitcher = onThemeModeChange ? /* @__PURE__ */ jsx(
246
+ ThemeModeSwitcher,
247
+ {
248
+ resolved: nextTheme,
249
+ onToggle: () => onThemeModeChange(nextTheme === "dark" ? "light" : "dark"),
250
+ theme: theme.theme,
251
+ size: "sm",
252
+ variant: "outline"
253
+ }
254
+ ) : void 0;
279
255
  const resolvedPageData = {
280
256
  ...pageData
281
257
  };
@@ -314,10 +290,7 @@ function PageRenderer({
314
290
  pageData: resolvedPageData,
315
291
  data: instance.name === "data-slot" ? dataSlots?.[String(settings.slot ?? "")] : void 0,
316
292
  editing,
317
- nextTheme,
318
- themeMode,
319
- setThemeMode: onThemeModeChange,
320
- toggleTheme: onThemeModeChange ? () => onThemeModeChange(nextTheme === "dark" ? "light" : "dark") : void 0
293
+ themeSwitcher
321
294
  }
322
295
  )
323
296
  },
@@ -334,6 +307,7 @@ function PageRenderer({
334
307
  className
335
308
  ),
336
309
  children: [
310
+ injectThemeStyles && /* @__PURE__ */ jsx("style", { children: generateThemeStyles(theme, themeSelector) }),
337
311
  header && renderInstance(header, {
338
312
  allowUp: false,
339
313
  allowDown: false,
@@ -353,13 +327,12 @@ function PageRenderer({
353
327
  allowDown: false,
354
328
  locked: true,
355
329
  allowAddBelow: false
356
- }),
357
- injectThemeStyles && /* @__PURE__ */ jsx("style", { children: generateThemeStyles(theme, themeSelector) })
330
+ })
358
331
  ]
359
332
  }
360
333
  ) });
361
334
  }
362
335
 
363
- export { EditableBlock, LAYOUT_BLOCK_NAMES, NON_ADDABLE_BLOCK_NAMES, PageRenderer, blockRegistry, generateThemeStyles, getAddableBlocks, getWidget, registerBlock };
364
- //# sourceMappingURL=chunk-HDINUNI2.js.map
365
- //# sourceMappingURL=chunk-HDINUNI2.js.map
336
+ export { EditableBlock, LAYOUT_BLOCK_NAMES, NON_ADDABLE_BLOCK_NAMES, PageRenderer, blockRegistry, generateThemeStyles, getAddableBlocks, getWidget, registerBlock, resolveThemeColor };
337
+ //# sourceMappingURL=chunk-IU4KATI2.js.map
338
+ //# sourceMappingURL=chunk-IU4KATI2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/registry-names.ts","../src/registry.ts","../src/renderer/editable-block.tsx","../src/renderer/generate-theme-styles.ts","../src/renderer/page-renderer.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"chunk-IU4KATI2.js","sourcesContent":["/** Blocks placed outside the normal flow by the renderer. */\nexport const LAYOUT_BLOCK_NAMES = [\"header\", \"footer\"];\n","import type { Widget } from \"./models\";\nimport { LAYOUT_BLOCK_NAMES } from \"./registry-names\";\nimport {\n Header,\n Hero,\n Content,\n Featured,\n Faq,\n Pricing,\n Banner,\n EmailForm,\n SupportForm,\n Footer,\n RichText,\n Grid,\n MediaBlock,\n Marquee,\n Embed,\n CodeSnippetBlock,\n DataSlot,\n} from \"./blocks\";\n\n/**\n * Every block available to the page builder, keyed by its\n * `metadata.name`. `header` and `footer` are singleton, layout-level\n * blocks placed outside the normal flow by the renderer.\n */\nexport const blockRegistry: Record<string, Widget<any>> = {\n header: Header,\n hero: Hero,\n content: Content,\n featured: Featured,\n faq: Faq,\n pricing: Pricing,\n banner: Banner,\n \"email-form\": EmailForm,\n \"support-form\": SupportForm,\n footer: Footer,\n \"rich-text\": RichText,\n grid: Grid,\n media: MediaBlock,\n marquee: Marquee,\n embed: Embed,\n \"code-snippet\": CodeSnippetBlock,\n \"data-slot\": DataSlot,\n};\n\nexport { LAYOUT_BLOCK_NAMES };\nexport const NON_ADDABLE_BLOCK_NAMES = [...LAYOUT_BLOCK_NAMES, \"data-slot\"];\n\n/**\n * Adds a host application's block to the shared renderer and builder\n * registry. Register blocks while the application module initializes, before\n * rendering either `PageBuilder` or `PageRenderer`.\n *\n * Built-in names are intentionally protected: a persisted page must always\n * have one stable implementation for a block name. Give host blocks a\n * namespaced name such as `acme-event-list`.\n */\nexport function registerBlock(widget: Widget<any>): void {\n const name = widget.metadata.name.trim();\n if (!name) {\n throw new Error(\"A page-builder block must have a non-empty name.\");\n }\n if (blockRegistry[name]) {\n throw new Error(`A page-builder block named \\\"${name}\\\" is already registered.`);\n }\n if (NON_ADDABLE_BLOCK_NAMES.includes(name)) {\n throw new Error(\n `\\\"${name}\\\" is a reserved page-builder block name and cannot be registered by a host application.`,\n );\n }\n blockRegistry[name] = widget;\n}\n\nexport function getWidget(name: string): Widget<any> | undefined {\n return blockRegistry[name];\n}\n\n/** Blocks a user can add to the body of a page. */\nexport function getAddableBlocks(): Widget<any>[] {\n return Object.values(blockRegistry).filter(\n (widget) => !NON_ADDABLE_BLOCK_NAMES.includes(widget.metadata.name),\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { ArrowDown, ArrowUp, Plus, Settings2, Trash2 } from \"lucide-react\";\nimport { cn } from \"../primitives/lib/utils\";\n\nexport interface EditableBlockProps {\n children: React.ReactNode;\n editing: boolean;\n /** Ref to the wrapper element so the canvas can scroll the block into view. */\n containerRef?: React.Ref<HTMLDivElement>;\n selected?: boolean;\n /** Draws a temporary attention flash, e.g. right after the block was moved. */\n highlighted?: boolean;\n label?: string;\n onSelect?: () => void;\n onMoveUp?: () => void;\n onMoveDown?: () => void;\n onDelete?: () => void;\n onAddBelow?: () => void;\n}\n\nfunction ToolbarButton({\n icon: Icon,\n label,\n onClick,\n}: {\n icon: React.ComponentType<{ className?: string }>;\n label: string;\n onClick?: () => void;\n}) {\n if (!onClick) return null;\n return (\n <button\n type=\"button\"\n title={label}\n aria-label={label}\n className=\"inline-flex h-7 w-7 items-center justify-center rounded bg-background text-foreground shadow-sm hover:bg-muted\"\n onClick={(e) => {\n e.stopPropagation();\n onClick();\n }}\n >\n <Icon className=\"h-3.5 w-3.5\" />\n </button>\n );\n}\n\n/**\n * Wraps a rendered block with hover/selection affordances when the page\n * builder is in editing mode. In non-editing (plain display) mode it's a\n * transparent passthrough.\n */\nexport function EditableBlock({\n children,\n editing,\n containerRef,\n selected,\n highlighted,\n label,\n onSelect,\n onMoveUp,\n onMoveDown,\n onDelete,\n onAddBelow,\n}: EditableBlockProps) {\n if (!editing) {\n return <>{children}</>;\n }\n\n return (\n <div\n ref={containerRef}\n className={cn(\n \"group/block relative outline outline-1 outline-transparent transition-colors hover:outline-primary/40\",\n selected && \"outline-2 outline-primary\",\n highlighted && \"outline-2 outline-primary/70\",\n )}\n onClick={onSelect}\n >\n <div\n aria-hidden\n className={cn(\n \"pointer-events-none absolute inset-0 z-10 bg-primary/10 opacity-0 transition-opacity duration-500\",\n highlighted && \"opacity-100\",\n )}\n />\n <div className=\"pointer-events-none absolute inset-x-0 top-0 z-20 flex justify-center opacity-0 transition-opacity group-hover/block:opacity-100\">\n <div className=\"pointer-events-auto flex items-center gap-1 rounded-b-md border border-t-0 border-border bg-background px-1.5 py-1 shadow-sm\">\n {label && (\n <span className=\"px-1.5 text-xs font-medium text-muted-foreground\">\n {label}\n </span>\n )}\n <ToolbarButton icon={Settings2} label=\"Edit settings\" onClick={onSelect} />\n <ToolbarButton icon={ArrowUp} label=\"Move up\" onClick={onMoveUp} />\n <ToolbarButton icon={ArrowDown} label=\"Move down\" onClick={onMoveDown} />\n <ToolbarButton icon={Trash2} label=\"Delete\" onClick={onDelete} />\n </div>\n </div>\n {children}\n {/* z-30: stay above the next block's (invisible) toolbar, which overlaps this strip. */}\n {onAddBelow && (\n <div className=\"pointer-events-none absolute inset-x-0 -bottom-3 z-30 flex justify-center opacity-0 transition-opacity group-hover/block:opacity-100\">\n <button\n type=\"button\"\n title=\"Add block below\"\n aria-label=\"Add block below\"\n className=\"pointer-events-auto inline-flex h-6 w-6 items-center justify-center rounded-full border border-border bg-background text-foreground shadow-sm hover:bg-primary hover:text-primary-foreground\"\n onClick={(e) => {\n e.stopPropagation();\n onAddBelow();\n }}\n >\n <Plus className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n )}\n </div>\n );\n}\n","import convert, { HSL } from \"color-convert\";\nimport type { Theme } from \"../models\";\n\nfunction formatHSL(hsl: HSL): string {\n return `${hsl[0]} ${hsl[1]}% ${hsl[2]}%`;\n}\n\nconst SHORT_OR_LONG_HEX = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i;\nconst HEX_WITH_ALPHA = /^#([0-9a-f]{4}|[0-9a-f]{8})$/i;\nconst CSS_COLOR_FUNCTION =\n /^(oklch|oklab|lab|lch|hwb|hsla?|rgba?|color)\\(\\s*[-+0-9a-z%.,/\\s]+\\)$/i;\n\n/**\n * Turns a theme colour into a CSS colour value, or \"\" when it isn't a\n * colour we can use. Hex (the stored format) is converted to hsl();\n * modern CSS colour functions such as `oklch(...)` are passed through.\n */\nexport function resolveThemeColor(value?: string): string {\n const color = value?.trim();\n if (!color) return \"\";\n if (SHORT_OR_LONG_HEX.test(color)) {\n return `hsl(${formatHSL(convert.hex.hsl(color.replace(\"#\", \"\")))})`;\n }\n if (HEX_WITH_ALPHA.test(color) || CSS_COLOR_FUNCTION.test(color)) return color;\n return \"\";\n}\n\n/**\n * Generates full CSS color values for the custom properties every primitive\n * reads. Full colors (rather than bare HSL channels) let the renderer coexist\n * with Tailwind v4 hosts whose semantic utilities use `var(--primary)` while\n * still accepting CourseLit-derived hex theme data.\n *\n * Callers rendering more than one theme in a document should pass a unique\n * selector so one renderer cannot overwrite another renderer's variables.\n */\nexport function generateThemeStyles(theme: Theme, selector = \".pb-theme\"): string {\n const colors = theme?.theme?.colors;\n if (!colors) {\n return \"\";\n }\n\n const { light, dark } = colors;\n if (!light || !dark) {\n return \"\";\n }\n\n const block = (c: typeof light) => `\n --background: ${resolveThemeColor(c.background)};\n --foreground: ${resolveThemeColor(c.foreground)};\n --card: ${resolveThemeColor(c.card)};\n --card-foreground: ${resolveThemeColor(c.cardForeground)};\n --popover: ${resolveThemeColor(c.popover)};\n --popover-foreground: ${resolveThemeColor(c.popoverForeground)};\n --primary: ${resolveThemeColor(c.primary)};\n --primary-foreground: ${resolveThemeColor(c.primaryForeground)};\n --secondary: ${resolveThemeColor(c.secondary)};\n --secondary-foreground: ${resolveThemeColor(c.secondaryForeground)};\n --muted: ${resolveThemeColor(c.muted)};\n --muted-foreground: ${resolveThemeColor(c.mutedForeground)};\n --accent: ${resolveThemeColor(c.accent)};\n --accent-foreground: ${resolveThemeColor(c.accentForeground)};\n --destructive: ${resolveThemeColor(c.destructive)};\n --border: ${resolveThemeColor(c.border)};\n --input: ${resolveThemeColor(c.input)};\n --ring: ${resolveThemeColor(c.ring)};\n --chart-1: ${resolveThemeColor(c.chart1)};\n --chart-2: ${resolveThemeColor(c.chart2)};\n --chart-3: ${resolveThemeColor(c.chart3)};\n --chart-4: ${resolveThemeColor(c.chart4)};\n --chart-5: ${resolveThemeColor(c.chart5)};\n --sidebar-background: ${resolveThemeColor(c.sidebar)};\n --sidebar-foreground: ${resolveThemeColor(c.sidebarForeground)};\n --sidebar-primary: ${resolveThemeColor(c.sidebarPrimary)};\n --sidebar-primary-foreground: ${resolveThemeColor(c.sidebarPrimaryForeground)};\n --sidebar-accent: ${resolveThemeColor(c.sidebarAccent)};\n --sidebar-accent-foreground: ${resolveThemeColor(c.sidebarAccentForeground)};\n --sidebar-border: ${resolveThemeColor(c.sidebarBorder)};\n --sidebar-ring: ${resolveThemeColor(c.sidebarRing)};\n --shadow-2xs: ${c.shadow2xs ?? \"\"};\n --shadow-xs: ${c.shadowXs ?? \"\"};\n --shadow-sm: ${c.shadowSm ?? \"\"};\n --shadow-md: ${c.shadowMd ?? \"\"};\n --shadow-lg: ${c.shadowLg ?? \"\"};\n --shadow-xl: ${c.shadowXl ?? \"\"};\n --shadow-2xl: ${c.shadow2xl ?? \"\"};\n `;\n\n return `\n ${selector} {\n ${block(light)}\n }\n ${selector}:is(.dark, .dark *) {\n ${block(dark)}\n }\n `;\n}\n","\"use client\";\n\nimport React, { useEffect, useId, useRef } from \"react\";\nimport type { PageData, Theme, WidgetInstance } from \"../models\";\nimport { getWidget, LAYOUT_BLOCK_NAMES } from \"../registry\";\nimport { EditableBlock } from \"./editable-block\";\nimport { ThemeModeSwitcher } from \"../components/theme-mode-switcher\";\nimport { generateThemeStyles } from \"./generate-theme-styles\";\nimport { cn } from \"../primitives/lib/utils\";\nimport {\n PageBuilderLinkProvider,\n type PageBuilderLinkComponent,\n} from \"../components/nav-link\";\n\nexport interface PageRendererProps {\n layout: WidgetInstance[];\n theme: Theme;\n pageData: PageData;\n /** Optional content rendered between the shared header and footer. */\n children?: React.ReactNode;\n /** Host-resolved data keyed by a generic data-slot's configured name. */\n dataSlots?: Record<string, React.ReactNode>;\n /** Resolved palette currently painted (`system` is already applied). */\n nextTheme?: \"light\" | \"dark\";\n onThemeModeChange?: (mode: \"light\" | \"dark\" | \"system\") => void;\n /** When true, renders hover controls (edit/move/delete/add) for use inside the builder canvas. */\n editing?: boolean;\n selectedWidgetId?: string;\n /** Widget to briefly highlight, e.g. right after it was moved. */\n highlightedWidgetId?: string;\n onSelectWidget?: (widgetId: string) => void;\n onMoveWidgetUp?: (widgetId: string) => void;\n onMoveWidgetDown?: (widgetId: string) => void;\n onDeleteWidget?: (widgetId: string) => void;\n onAddWidgetBelow?: (widgetId: string) => void;\n /** Injects the theme's generated CSS custom properties as a `<style>` tag. Disable if the host app renders it elsewhere. */\n injectThemeStyles?: boolean;\n /**\n * Optional host link (e.g. Next.js `Link`) used for internal `/...` paths\n * inside block widgets. Omit it to render native `<a>` tags — no\n * framework dependency.\n */\n linkComponent?: PageBuilderLinkComponent;\n className?: string;\n}\n\n/**\n * Renders a page's widget layout using the block registry. Used both for\n * plain (non-editable) display and, with `editing`, as the live canvas\n * inside the page builder.\n */\nexport function PageRenderer({\n layout,\n theme,\n pageData,\n children,\n dataSlots,\n nextTheme = \"light\",\n onThemeModeChange,\n editing = false,\n selectedWidgetId,\n highlightedWidgetId,\n onSelectWidget,\n onMoveWidgetUp,\n onMoveWidgetDown,\n onDeleteWidget,\n onAddWidgetBelow,\n injectThemeStyles = true,\n linkComponent,\n className,\n}: PageRendererProps) {\n // Refs to each block's wrapper, keyed by widget id. Refs (unlike DOM\n // attribute lookups) stay correct even when SSR and client render\n // different generated widget ids.\n const blockRefs = useRef(new Map<string, HTMLDivElement>());\n const themeScope = `pb-${useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n const themeSelector = `[data-pb-theme=\"${themeScope}\"]`;\n\n // Keep the highlighted (just-moved) block in view. Keyed on `layout` too\n // so repeated move clicks on the same widget re-scroll as it travels.\n useEffect(() => {\n if (!highlightedWidgetId) return;\n blockRefs.current\n .get(highlightedWidgetId)\n ?.scrollIntoView({ behavior: \"smooth\", block: \"nearest\" });\n }, [highlightedWidgetId, layout]);\n\n const header = layout.find((widget) => widget.name === \"header\");\n const footer = layout.find((widget) => widget.name === \"footer\");\n const body = layout.filter((widget) => !LAYOUT_BLOCK_NAMES.includes(widget.name));\n\n const themeSwitcher = onThemeModeChange ? (\n <ThemeModeSwitcher\n resolved={nextTheme}\n onToggle={() => onThemeModeChange(nextTheme === \"dark\" ? \"light\" : \"dark\")}\n theme={theme.theme}\n size=\"sm\"\n variant=\"outline\"\n />\n ) : undefined;\n\n const resolvedPageData: PageData = {\n ...pageData,\n };\n\n function renderInstance(\n instance: WidgetInstance,\n options: {\n allowUp: boolean;\n allowDown: boolean;\n locked: boolean;\n allowAddBelow: boolean;\n },\n ) {\n const definition = getWidget(instance.name);\n if (!definition) return null;\n\n const Widget = definition.widget;\n const settings = {\n ...definition.getDefaultSettings?.(),\n ...instance.settings,\n };\n\n return (\n <EditableBlock\n key={instance.widgetId}\n editing={editing}\n containerRef={(node) => {\n if (node) {\n blockRefs.current.set(instance.widgetId, node);\n } else {\n blockRefs.current.delete(instance.widgetId);\n }\n }}\n selected={selectedWidgetId === instance.widgetId}\n highlighted={highlightedWidgetId === instance.widgetId}\n label={definition.metadata.displayName}\n onSelect={() => onSelectWidget?.(instance.widgetId)}\n onMoveUp={\n options.allowUp ? () => onMoveWidgetUp?.(instance.widgetId) : undefined\n }\n onMoveDown={\n options.allowDown ? () => onMoveWidgetDown?.(instance.widgetId) : undefined\n }\n onDelete={\n !options.locked ? () => onDeleteWidget?.(instance.widgetId) : undefined\n }\n onAddBelow={\n options.allowAddBelow && onAddWidgetBelow\n ? () => onAddWidgetBelow(instance.widgetId)\n : undefined\n }\n >\n <Widget\n settings={settings as any}\n state={{ theme }}\n pageData={resolvedPageData}\n data={\n instance.name === \"data-slot\"\n ? dataSlots?.[String(settings.slot ?? \"\")]\n : undefined\n }\n editing={editing}\n themeSwitcher={themeSwitcher}\n />\n </EditableBlock>\n );\n }\n\n return (\n <PageBuilderLinkProvider component={linkComponent}>\n <div\n data-pb-theme={themeScope}\n className={cn(\n \"pb-theme flex min-h-full flex-col bg-background text-foreground\",\n nextTheme === \"dark\" && \"dark\",\n className,\n )}\n >\n {/* Emitted first so the variables exist before any content paints. */}\n {injectThemeStyles && (\n <style>{generateThemeStyles(theme, themeSelector)}</style>\n )}\n {header &&\n renderInstance(header, {\n allowUp: false,\n allowDown: false,\n locked: true,\n allowAddBelow: true,\n })}\n <div className=\"flex flex-1 flex-col\">\n {children !== undefined &&\n !body.some((instance) => instance.name === \"data-slot\")\n ? children\n : body.map((instance, index) =>\n renderInstance(instance, {\n allowUp: instance.moveable && index !== 0,\n allowDown: instance.moveable && index !== body.length - 1,\n locked: !instance.deletable,\n allowAddBelow: true,\n }),\n )}\n </div>\n {footer &&\n renderInstance(footer, {\n allowUp: false,\n allowDown: false,\n locked: true,\n allowAddBelow: false,\n })}\n </div>\n </PageBuilderLinkProvider>\n );\n}\n"]}