@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
@@ -1,95 +1,154 @@
1
1
  "use client";
2
- import { cn, Button2, Button } from './chunk-SGNRWWG3.js';
3
- import * as React2 from 'react';
4
- import { useRef, useState, useId, useEffect } from 'react';
2
+ import { cn } from './chunk-J7Z4UW45.js';
5
3
  import { jsx, jsxs } from 'react/jsx-runtime';
6
- import { Play, X, Sun, Moon } from 'lucide-react';
7
- export { TextRenderer, truncateTextEditorContent } from '@frontlit/text-editor';
4
+ import * as React from 'react';
5
+ import { useState, useEffect, useRef } from 'react';
6
+ export { TextRenderer } from '@frontlit/text-editor/renderer';
7
+ import { Play, X } from 'lucide-react';
8
8
 
9
- var SandboxedEmbed = ({
10
- content,
11
- className,
12
- style,
13
- id
14
- }) => {
15
- const iframeRef = useRef(null);
16
- const [height, setHeight] = useState("100px");
17
- const generatedId = useId();
18
- const iframeId = id || generatedId;
19
- useEffect(() => {
20
- const handleMessage = (event) => {
21
- if (event.data?.type === "embed-resize" && event.data?.id === iframeId && event.data?.height) {
22
- setHeight(`${event.data.height}px`);
9
+ var imageAttributesResolver = null;
10
+ function setImageAttributesResolver(resolver) {
11
+ imageAttributesResolver = resolver;
12
+ }
13
+ var roundedMap = {
14
+ 0: "rounded-none",
15
+ 1: "rounded-sm",
16
+ 2: "rounded",
17
+ 3: "rounded-md",
18
+ 4: "rounded-lg",
19
+ 5: "rounded-xl",
20
+ 6: "rounded-2xl",
21
+ 7: "rounded-3xl",
22
+ 8: "rounded-full"
23
+ };
24
+ var objectFitClass = {
25
+ cover: "object-cover",
26
+ contain: "object-contain",
27
+ fill: "object-fill",
28
+ none: "object-none",
29
+ "scale-down": "object-scale-down"
30
+ };
31
+ var Image = (props) => {
32
+ const {
33
+ src,
34
+ alt = "",
35
+ defaultImage,
36
+ priority = false,
37
+ loading = priority ? "eager" : "lazy",
38
+ sizes,
39
+ noDefaultImage = false,
40
+ className = "",
41
+ classes = "",
42
+ width = "w-full",
43
+ height = "h-auto",
44
+ objectFit = "cover",
45
+ borderRadius
46
+ } = props;
47
+ const source = src || defaultImage;
48
+ const radiusClass = typeof borderRadius === "number" ? roundedMap[borderRadius] ?? "rounded" : borderRadius;
49
+ if (!source) {
50
+ if (noDefaultImage) return null;
51
+ return /* @__PURE__ */ jsx(
52
+ "div",
53
+ {
54
+ className: cn(
55
+ "relative aspect-video overflow-hidden bg-muted",
56
+ width,
57
+ height,
58
+ radiusClass,
59
+ classes,
60
+ className
61
+ )
23
62
  }
24
- };
25
- window.addEventListener("message", handleMessage);
26
- return () => window.removeEventListener("message", handleMessage);
27
- }, [iframeId]);
28
- const srcDoc = `
29
- <!DOCTYPE html>
30
- <html>
31
- <head>
32
- <style>
33
- html, body { margin: 0; padding: 0; overflow: hidden; }
34
- #content-wrapper { overflow: hidden; height: auto; }
35
- </style>
36
- </head>
37
- <body>
38
- <div id="content-wrapper">
39
- ${content || ""}
40
- </div>
41
- <script>
42
- const iframeId = "${iframeId}";
43
-
44
- function sendHeight() {
45
- const wrapper = document.getElementById('content-wrapper');
46
- const height = wrapper ? wrapper.scrollHeight : document.body.scrollHeight;
47
- window.parent.postMessage({ type: "embed-resize", id: iframeId, height: height }, "*");
48
- }
49
-
50
- // Use a ResizeObserver to detect size changes
51
- const resizeObserver = new ResizeObserver(entries => {
52
- sendHeight();
53
- });
54
- const target = document.getElementById('content-wrapper') || document.body;
55
- resizeObserver.observe(target);
56
-
57
- // Use MutationObserver to detect DOM changes
58
- const mutationObserver = new MutationObserver(() => {
59
- sendHeight();
60
- });
61
- mutationObserver.observe(target, { childList: true, subtree: true, attributes: true });
62
-
63
- // Poll for a few seconds to catch late-loading content
64
- let interval = setInterval(sendHeight, 500);
65
- setTimeout(() => clearInterval(interval), 10000);
66
-
67
- window.addEventListener('load', sendHeight);
68
- window.addEventListener('resize', sendHeight);
69
-
70
- // Initial send
71
- sendHeight();
72
- </script>
73
- </body>
74
- </html>
75
- `;
63
+ );
64
+ }
65
+ const optimized = imageAttributesResolver?.({ src: source, alt, sizes, priority });
76
66
  return /* @__PURE__ */ jsx(
77
- "iframe",
67
+ "div",
78
68
  {
79
- ref: iframeRef,
80
- srcDoc,
81
- className,
82
- style: {
83
- width: "100%",
69
+ className: cn(
70
+ "relative aspect-video overflow-hidden",
71
+ width,
84
72
  height,
85
- border: "none",
86
- ...style
87
- },
88
- sandbox: "allow-scripts allow-popups allow-forms allow-same-origin",
89
- title: "Embedded Content"
73
+ radiusClass,
74
+ classes,
75
+ className
76
+ ),
77
+ children: /* @__PURE__ */ jsx(
78
+ "img",
79
+ {
80
+ src: source,
81
+ alt,
82
+ sizes,
83
+ loading,
84
+ decoding: "async",
85
+ fetchPriority: priority ? "high" : void 0,
86
+ ...optimized,
87
+ className: cn(
88
+ "absolute inset-0 h-full w-full",
89
+ radiusClass,
90
+ objectFitClass[objectFit]
91
+ )
92
+ }
93
+ )
90
94
  }
91
95
  );
92
96
  };
97
+ var LINK_CONTEXT_KEY = /* @__PURE__ */ Symbol.for("@frontlit/page-builder/link-context");
98
+ var globalRegistry = globalThis;
99
+ var PageBuilderLinkContext = globalRegistry[LINK_CONTEXT_KEY] ?? (globalRegistry[LINK_CONTEXT_KEY] = React.createContext(null));
100
+ function PageBuilderLinkProvider({
101
+ component,
102
+ children
103
+ }) {
104
+ return /* @__PURE__ */ jsx(PageBuilderLinkContext.Provider, { value: component ?? null, children });
105
+ }
106
+ function usePageBuilderLinkComponent() {
107
+ return React.useContext(PageBuilderLinkContext);
108
+ }
109
+ function NavLink({
110
+ href,
111
+ children,
112
+ openInSameTab = true,
113
+ style,
114
+ className = "",
115
+ onClick
116
+ }) {
117
+ const LinkComponent = usePageBuilderLinkComponent();
118
+ const isInternal = Boolean(href && href.startsWith("/"));
119
+ const isInPageNavigation = Boolean(href && href.startsWith("#"));
120
+ const target = openInSameTab ? void 0 : "_blank";
121
+ const rel = target === "_blank" ? "noopener noreferrer" : void 0;
122
+ const handleClick = () => {
123
+ onClick?.();
124
+ };
125
+ if (!isInPageNavigation && isInternal && LinkComponent) {
126
+ return /* @__PURE__ */ jsx(
127
+ LinkComponent,
128
+ {
129
+ href,
130
+ style,
131
+ className,
132
+ onClick: handleClick,
133
+ target,
134
+ rel,
135
+ children
136
+ }
137
+ );
138
+ }
139
+ return /* @__PURE__ */ jsx(
140
+ "a",
141
+ {
142
+ href,
143
+ style,
144
+ className,
145
+ target,
146
+ rel,
147
+ onClick: handleClick,
148
+ children
149
+ }
150
+ );
151
+ }
93
152
 
94
153
  // src/utils/extract-video-id.ts
95
154
  var YOUTUBE_HOSTNAMES = /* @__PURE__ */ new Set([
@@ -347,185 +406,80 @@ function VideoWithPreview({
347
406
  ] })
348
407
  ] });
349
408
  }
350
- var roundedMap = {
351
- 0: "rounded-none",
352
- 1: "rounded-sm",
353
- 2: "rounded",
354
- 3: "rounded-md",
355
- 4: "rounded-lg",
356
- 5: "rounded-xl",
357
- 6: "rounded-2xl",
358
- 7: "rounded-3xl",
359
- 8: "rounded-full"
360
- };
361
- var objectFitClass = {
362
- cover: "object-cover",
363
- contain: "object-contain",
364
- fill: "object-fill",
365
- none: "object-none",
366
- "scale-down": "object-scale-down"
367
- };
368
- var Image = (props) => {
369
- const {
370
- src,
371
- alt = "",
372
- defaultImage,
373
- loading = "lazy",
374
- noDefaultImage = false,
375
- className = "",
376
- classes = "",
377
- width = "w-full",
378
- height = "h-auto",
379
- objectFit = "cover",
380
- borderRadius
381
- } = props;
382
- const source = src || defaultImage;
383
- const radiusClass = typeof borderRadius === "number" ? roundedMap[borderRadius] ?? "rounded" : borderRadius;
384
- if (!source) {
385
- if (noDefaultImage) return null;
386
- return /* @__PURE__ */ jsx(
387
- "div",
388
- {
389
- className: cn(
390
- "relative aspect-video overflow-hidden bg-muted",
391
- width,
392
- height,
393
- radiusClass,
394
- classes,
395
- className
396
- )
397
- }
398
- );
399
- }
400
- return /* @__PURE__ */ jsx(
401
- "div",
402
- {
403
- className: cn(
404
- "relative aspect-video overflow-hidden",
405
- width,
406
- height,
407
- radiusClass,
408
- classes,
409
- className
410
- ),
411
- children: /* @__PURE__ */ jsx(
412
- "img",
413
- {
414
- src: source,
415
- alt,
416
- loading,
417
- className: cn(
418
- "absolute inset-0 h-full w-full",
419
- radiusClass,
420
- objectFitClass[objectFit]
421
- )
422
- }
423
- )
409
+ var HOISTED_LINK = /<link\b[^>]*(?:rel=["']?(?:preconnect|dns-prefetch|preload)|href=["'][^"']*fonts?\.)[^>]*>/gi;
410
+ function prepareEmbed(content) {
411
+ const links = content.match(HOISTED_LINK)?.join("") ?? "";
412
+ const markup = content.replace(HOISTED_LINK, "").replace(/<!doctype[^>]*>|<\/?(?:html|head|body)\b[^>]*>/gi, "").replace(
413
+ /<script\b[^>]*>(?:(?!<\/script>)[\s\S])*window\.parent[\s\S]*?<\/script>/gi,
414
+ ""
415
+ ).replace(
416
+ /<style\b([^>]*)>([\s\S]*?)<\/style>/gi,
417
+ (_, attrs, css) => {
418
+ const scoped = css.replace(/:root\b/g, ":host").replace(/(^|[\s,{}])html\.dark\b/g, "$1:host(.dark)").replace(/(^|[\s,{}])html(?=[\s,{:.[>~+])/g, "$1:host").replace(/(^|[\s,{}])body(?=[\s,{:.[>~+])/g, "$1[data-embed-body]").replace(/min-height\s*:\s*100d?vh/gi, "min-height:0").replace(/(\d*\.?\d+)vw\b/gi, "$1cqw").replace(
419
+ /@media\s+(?:only\s+)?(?:screen\s+and\s+)?(\([^{]*\bwidth\b[^{]*)\{/gi,
420
+ "@container $1{"
421
+ );
422
+ return `<style${attrs}>${scoped}</style>`;
424
423
  }
425
424
  );
426
- };
427
- var PageBuilderLinkContext = React2.createContext(
428
- null
429
- );
430
- function PageBuilderLinkProvider({
431
- component,
432
- children
433
- }) {
434
- return /* @__PURE__ */ jsx(PageBuilderLinkContext.Provider, { value: component ?? null, children });
435
- }
436
- function usePageBuilderLinkComponent() {
437
- return React2.useContext(PageBuilderLinkContext);
425
+ const body = `<style>:host{display:block;container-type:inline-size;overflow-x:clip}</style><div data-embed-body>${markup}</div>`;
426
+ return { links, body };
438
427
  }
439
- function NavLink({
440
- href,
441
- children,
442
- openInSameTab = true,
443
- style,
444
- className = "",
445
- onClick
446
- }) {
447
- const LinkComponent = usePageBuilderLinkComponent();
448
- const isInternal = Boolean(href && href.startsWith("/"));
449
- const isInPageNavigation = Boolean(href && href.startsWith("#"));
450
- const target = openInSameTab ? void 0 : "_blank";
451
- const rel = target === "_blank" ? "noopener noreferrer" : void 0;
452
- const handleClick = () => {
453
- onClick?.();
454
- };
455
- if (!isInPageNavigation && isInternal && LinkComponent) {
456
- return /* @__PURE__ */ jsx(
457
- LinkComponent,
458
- {
459
- href,
460
- style,
461
- className,
462
- onClick: handleClick,
463
- target,
464
- rel,
465
- children
466
- }
467
- );
428
+ function runScripts(root) {
429
+ for (const old of root.querySelectorAll("script")) {
430
+ const script = document.createElement("script");
431
+ for (const { name, value } of old.attributes) script.setAttribute(name, value);
432
+ script.textContent = old.textContent;
433
+ old.replaceWith(script);
468
434
  }
469
- return /* @__PURE__ */ jsx(
470
- "a",
471
- {
472
- href,
473
- style,
474
- className,
475
- target,
476
- rel,
477
- onClick: handleClick,
478
- children
479
- }
480
- );
481
435
  }
482
- function ThemeModeSwitcher({
483
- resolved,
484
- onToggle,
485
- theme,
486
- className,
487
- size,
488
- variant
489
- }) {
490
- const [mounted, setMounted] = useState(false);
436
+ var InlineEmbed = ({ content, className, style }) => {
437
+ const hostRef = useRef(null);
438
+ const appliedRef = useRef(null);
439
+ const { links, body } = prepareEmbed(content);
491
440
  useEffect(() => {
492
- setMounted(true);
493
- }, []);
494
- const painted = mounted ? resolved : "light";
495
- const next = painted === "dark" ? "light" : "dark";
496
- const label = next === "dark" ? "Switch to dark mode" : "Switch to light mode";
497
- const Icon = painted === "dark" ? Sun : Moon;
498
- if (theme) {
499
- return /* @__PURE__ */ jsx(
500
- Button2,
501
- {
502
- type: "button",
503
- variant: variant ?? "outline",
504
- size: size ?? "sm",
505
- theme,
506
- title: label,
507
- "aria-label": label,
508
- className,
509
- onClick: onToggle,
510
- children: /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" })
511
- }
441
+ const host = hostRef.current;
442
+ if (!host) return;
443
+ const root = host.shadowRoot ?? host.attachShadow({ mode: "open" });
444
+ if (appliedRef.current === body) return;
445
+ if (appliedRef.current !== null || !root.hasChildNodes()) {
446
+ root.innerHTML = body;
447
+ runScripts(root);
448
+ }
449
+ appliedRef.current = body;
450
+ }, [body]);
451
+ useEffect(() => {
452
+ const host = hostRef.current;
453
+ if (!host) return;
454
+ const sync = () => host.classList.toggle(
455
+ "dark",
456
+ !!host.parentElement?.closest(".dark") || document.documentElement.classList.contains("dark")
512
457
  );
513
- }
458
+ sync();
459
+ const observer = new MutationObserver(sync);
460
+ observer.observe(document.documentElement, {
461
+ attributes: true,
462
+ attributeFilter: ["class"],
463
+ subtree: true
464
+ });
465
+ return () => observer.disconnect();
466
+ }, []);
514
467
  return /* @__PURE__ */ jsx(
515
- Button,
468
+ "div",
516
469
  {
517
- type: "button",
518
- variant: "outline",
519
- size: "icon",
520
- title: label,
521
- "aria-label": label,
470
+ ref: hostRef,
522
471
  className,
523
- onClick: onToggle,
524
- children: /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" })
472
+ style,
473
+ suppressHydrationWarning: true,
474
+ dangerouslySetInnerHTML: {
475
+ // Only the HTML parser turns the template into a shadow root; on the
476
+ // client the effect attaches it instead.
477
+ __html: typeof window === "undefined" ? `${links}<template shadowrootmode="open">${body}</template>` : links
478
+ }
525
479
  }
526
480
  );
527
- }
481
+ };
528
482
 
529
- export { Image, NavLink, PageBuilderLinkProvider, SandboxedEmbed, ThemeModeSwitcher, VideoWithPreview, roundedMap, usePageBuilderLinkComponent };
530
- //# sourceMappingURL=chunk-H4P5MFJI.js.map
531
- //# sourceMappingURL=chunk-H4P5MFJI.js.map
483
+ export { Image, InlineEmbed, NavLink, PageBuilderLinkProvider, VideoWithPreview, prepareEmbed, roundedMap, setImageAttributesResolver, usePageBuilderLinkComponent };
484
+ //# sourceMappingURL=chunk-36QHCIPM.js.map
485
+ //# sourceMappingURL=chunk-36QHCIPM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/image.tsx","../src/components/nav-link.tsx","../src/utils/extract-video-id.ts","../src/components/video-with-preview.tsx","../src/components/inline-embed.tsx"],"names":["jsx","useEffect"],"mappings":";;;;;;;AAoBA,IAAI,uBAAA,GAA0D,IAAA;AAMvD,SAAS,2BAA2B,QAAA,EAA0C;AACnF,EAAA,uBAAA,GAA0B,QAAA;AAC5B;AAIO,IAAM,UAAA,GAAqC;AAAA,EAChD,CAAA,EAAG,cAAA;AAAA,EACH,CAAA,EAAG,YAAA;AAAA,EACH,CAAA,EAAG,SAAA;AAAA,EACH,CAAA,EAAG,YAAA;AAAA,EACH,CAAA,EAAG,YAAA;AAAA,EACH,CAAA,EAAG,YAAA;AAAA,EACH,CAAA,EAAG,aAAA;AAAA,EACH,CAAA,EAAG,aAAA;AAAA,EACH,CAAA,EAAG;AACL;AAmBA,IAAM,cAAA,GAAiD;AAAA,EACrD,KAAA,EAAO,cAAA;AAAA,EACP,OAAA,EAAS,gBAAA;AAAA,EACT,IAAA,EAAM,aAAA;AAAA,EACN,IAAA,EAAM,aAAA;AAAA,EACN,YAAA,EAAc;AAChB,CAAA;AAEO,IAAM,KAAA,GAAQ,CAAC,KAAA,KAAoB;AACxC,EAAA,MAAM;AAAA,IACJ,GAAA;AAAA,IACA,GAAA,GAAM,EAAA;AAAA,IACN,YAAA;AAAA,IACA,QAAA,GAAW,KAAA;AAAA,IACX,OAAA,GAAU,WAAW,OAAA,GAAU,MAAA;AAAA,IAC/B,KAAA;AAAA,IACA,cAAA,GAAiB,KAAA;AAAA,IACjB,SAAA,GAAY,EAAA;AAAA,IACZ,OAAA,GAAU,EAAA;AAAA,IACV,KAAA,GAAQ,QAAA;AAAA,IACR,MAAA,GAAS,QAAA;AAAA,IACT,SAAA,GAAY,OAAA;AAAA,IACZ;AAAA,GACF,GAAI,KAAA;AACJ,EAAA,MAAM,SAAS,GAAA,IAAO,YAAA;AAEtB,EAAA,MAAM,cACJ,OAAO,YAAA,KAAiB,WACnB,UAAA,CAAW,YAAY,KAAK,SAAA,GAC7B,YAAA;AAEN,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,IAAI,gBAAgB,OAAO,IAAA;AAC3B,IAAA,uBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAW,EAAA;AAAA,UACT,gDAAA;AAAA,UACA,KAAA;AAAA,UACA,MAAA;AAAA,UACA,WAAA;AAAA,UACA,OAAA;AAAA,UACA;AAAA;AACF;AAAA,KACF;AAAA,EAEJ;AAEA,EAAA,MAAM,SAAA,GAAY,0BAA0B,EAAE,GAAA,EAAK,QAAQ,GAAA,EAAK,KAAA,EAAO,UAAU,CAAA;AAEjF,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,EAAA;AAAA,QACT,uCAAA;AAAA,QACA,KAAA;AAAA,QACA,MAAA;AAAA,QACA,WAAA;AAAA,QACA,OAAA;AAAA,QACA;AAAA,OACF;AAAA,MAEA,QAAA,kBAAA,GAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,GAAA,EAAK,MAAA;AAAA,UACL,GAAA;AAAA,UACA,KAAA;AAAA,UACA,OAAA;AAAA,UACA,QAAA,EAAS,OAAA;AAAA,UACT,aAAA,EAAe,WAAW,MAAA,GAAS,MAAA;AAAA,UAClC,GAAG,SAAA;AAAA,UACJ,SAAA,EAAW,EAAA;AAAA,YACT,gCAAA;AAAA,YACA,WAAA;AAAA,YACA,eAAe,SAAS;AAAA;AAC1B;AAAA;AACF;AAAA,GACF;AAEJ;ACjHA,IAAM,gBAAA,mBAAmB,MAAA,CAAO,GAAA,CAAI,qCAAqC,CAAA;AACzE,IAAM,cAAA,GAAiB,UAAA;AAGvB,IAAM,sBAAA,GAA0B,cAAA,CAAA,gBAAA,CAAA,KAAA,cAAA,CAAA,gBAAA,CAAA,GACxB,KAAA,CAAA,aAAA,CAA+C,IAAI,CAAA,CAAA;AAEpD,SAAS,uBAAA,CAAwB;AAAA,EACtC,SAAA;AAAA,EACA;AACF,CAAA,EAGG;AACD,EAAA,uBACEA,IAAC,sBAAA,CAAuB,QAAA,EAAvB,EAAgC,KAAA,EAAO,SAAA,IAAa,MAClD,QAAA,EACH,CAAA;AAEJ;AAEO,SAAS,2BAAA,GAA8B;AAC5C,EAAA,OAAa,iBAAW,sBAAsB,CAAA;AAChD;AAgBO,SAAS,OAAA,CAAQ;AAAA,EACtB,IAAA;AAAA,EACA,QAAA;AAAA,EACA,aAAA,GAAgB,IAAA;AAAA,EAChB,KAAA;AAAA,EACA,SAAA,GAAY,EAAA;AAAA,EACZ;AACF,CAAA,EAAiB;AACf,EAAA,MAAM,gBAAgB,2BAAA,EAA4B;AAClD,EAAA,MAAM,aAAa,OAAA,CAAQ,IAAA,IAAQ,IAAA,CAAK,UAAA,CAAW,GAAG,CAAC,CAAA;AACvD,EAAA,MAAM,qBAAqB,OAAA,CAAQ,IAAA,IAAQ,IAAA,CAAK,UAAA,CAAW,GAAG,CAAC,CAAA;AAC/D,EAAA,MAAM,MAAA,GAAS,gBAAgB,MAAA,GAAY,QAAA;AAC3C,EAAA,MAAM,GAAA,GAAM,MAAA,KAAW,QAAA,GAAW,qBAAA,GAAwB,MAAA;AAC1D,EAAA,MAAM,cAA0D,MAAM;AACpE,IAAA,OAAA,IAAU;AAAA,EACZ,CAAA;AAEA,EAAA,IAAI,CAAC,kBAAA,IAAsB,UAAA,IAAc,aAAA,EAAe;AACtD,IAAA,uBACEA,GAAAA;AAAA,MAAC,aAAA;AAAA,MAAA;AAAA,QACC,IAAA;AAAA,QACA,KAAA;AAAA,QACA,SAAA;AAAA,QACA,OAAA,EAAS,WAAA;AAAA,QACT,MAAA;AAAA,QACA,GAAA;AAAA,QAEC;AAAA;AAAA,KACH;AAAA,EAEJ;AAEA,EAAA,uBACEA,GAAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,IAAA;AAAA,MACA,KAAA;AAAA,MACA,SAAA;AAAA,MACA,MAAA;AAAA,MACA,GAAA;AAAA,MACA,OAAA,EAAS,WAAA;AAAA,MAER;AAAA;AAAA,GACH;AAEJ;;;ACzGA,IAAM,iBAAA,uBAAwB,GAAA,CAAI;AAAA,EAChC,aAAA;AAAA,EACA,iBAAA;AAAA,EACA,eAAA;AAAA,EACA,mBAAA;AAAA,EACA,sBAAA;AAAA,EACA,0BAAA;AAAA,EACA;AACF,CAAC,CAAA;AAED,IAAM,kCAAkB,IAAI,GAAA,CAAI,CAAC,WAAA,EAAa,eAAA,EAAiB,kBAAkB,CAAC,CAAA;AAElF,IAAM,SAAA,GAAY,CAAC,KAAA,KAAkB,OAAA,CAAQ,KAAK,KAAK,CAAA;AAEvD,IAAM,qBAAA,GAAwB,CAAC,SAAA,KAAmB;AAChD,EAAA,MAAM,QAAA,GAAW,SAAA,CAAU,QAAA,CAAS,WAAA,EAAY;AAEhD,EAAA,IAAI,CAAC,iBAAA,CAAkB,GAAA,CAAI,QAAQ,CAAA,EAAG;AACpC,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,IAAI,aAAa,UAAA,EAAY;AAC3B,IAAA,OAAO,SAAA,CAAU,SAAS,KAAA,CAAM,GAAG,EAAE,MAAA,CAAO,OAAO,CAAA,CAAE,CAAC,CAAA,IAAK,IAAA;AAAA,EAC7D;AAEA,EAAA,IAAI,SAAA,CAAU,aAAa,QAAA,EAAU;AACnC,IAAA,OAAO,SAAA,CAAU,YAAA,CAAa,GAAA,CAAI,GAAG,CAAA;AAAA,EACvC;AAEA,EAAA,MAAM,YAAY,SAAA,CAAU,QAAA,CAAS,MAAM,GAAG,CAAA,CAAE,OAAO,OAAO,CAAA;AAC9D,EAAA,MAAM,SAAA,GAAY,UAAU,CAAC,CAAA;AAC7B,EAAA,MAAM,UAAA,GAAa,UAAU,CAAC,CAAA;AAE9B,EAAA,IAAA,CACG,SAAA,KAAc,WACb,SAAA,KAAc,GAAA,IACd,cAAc,QAAA,IACd,SAAA,KAAc,WAChB,UAAA,EACA;AACA,IAAA,OAAO,UAAA;AAAA,EACT;AAEA,EAAA,OAAO,IAAA;AACT,CAAA;AAEA,IAAM,mBAAA,GAAsB,CAAC,SAAA,KAAmB;AAC9C,EAAA,MAAM,QAAA,GAAW,SAAA,CAAU,QAAA,CAAS,WAAA,EAAY;AAEhD,EAAA,IAAI,CAAC,eAAA,CAAgB,GAAA,CAAI,QAAQ,CAAA,EAAG;AAClC,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,YAAY,SAAA,CAAU,QAAA,CAAS,MAAM,GAAG,CAAA,CAAE,OAAO,OAAO,CAAA;AAE9D,EAAA,KAAA,IAAS,QAAQ,SAAA,CAAU,MAAA,GAAS,CAAA,EAAG,KAAA,IAAS,GAAG,KAAA,EAAA,EAAS;AAC1D,IAAA,IAAI,SAAA,CAAU,SAAA,CAAU,KAAK,CAAC,CAAA,EAAG;AAC/B,MAAA,OAAO,UAAU,KAAK,CAAA;AAAA,IACxB;AAAA,EACF;AAEA,EAAA,OAAO,IAAA;AACT,CAAA;AAEO,SAAS,cAAA,CAAe,KAAc,QAAA,EAAmC;AAC9E,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,gBAAgB,0BAAA,CAA2B,IAAA,CAAK,GAAG,CAAA,GAAI,GAAA,GAAM,WAAW,GAAG,CAAA,CAAA;AAEjF,EAAA,IAAI;AACF,IAAA,MAAM,SAAA,GAAY,IAAI,GAAA,CAAI,aAAa,CAAA;AAEvC,IAAA,IAAI,aAAa,SAAA,EAAW;AAC1B,MAAA,OAAO,sBAAsB,SAAS,CAAA;AAAA,IACxC;AAEA,IAAA,IAAI,aAAa,OAAA,EAAS;AACxB,MAAA,OAAO,oBAAoB,SAAS,CAAA;AAAA,IACtC;AAEA,IAAA,OAAO,qBAAA,CAAsB,SAAS,CAAA,IAAK,mBAAA,CAAoB,SAAS,CAAA;AAAA,EAC1E,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AChEO,SAAS,gBAAA,CAAiB;AAAA,EAC/B,KAAA,GAAQ,OAAA;AAAA,EACR,YAAA;AAAA,EACA,QAAA,GAAW,6CAAA;AAAA,EACX,WAAA,GAAc,MAAA;AAAA,EACd,KAAA,GAAQ;AACV,CAAA,EAAwB;AACtB,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAS,KAAK,CAAA;AACpD,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,KAAK,CAAA;AAChD,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAI,SAAwB,IAAI,CAAA;AAExE,EAAA,MAAM,kBAAkB,MAAiB;AACvC,IAAA,IAAI,cAAA,CAAe,QAAA,EAAU,SAAS,CAAA,EAAG;AACvC,MAAA,OAAO,SAAA;AAAA,IACT;AAEA,IAAA,IAAI,cAAA,CAAe,QAAA,EAAU,OAAO,CAAA,EAAG;AACrC,MAAA,OAAO,OAAA;AAAA,IACT;AAEA,IAAA,OAAO,aAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAM,YAAY,eAAA,EAAgB;AAElC,EAAA,MAAM,aAAa,MAAM;AACvB,IAAA,IAAI,cAAc,SAAA,EAAW;AAC3B,MAAA,OAAO,cAAA,CAAe,UAAU,SAAS,CAAA;AAAA,IAC3C;AAEA,IAAA,IAAI,cAAc,OAAA,EAAS;AACzB,MAAA,OAAO,cAAA,CAAe,UAAU,OAAO,CAAA;AAAA,IACzC;AAEA,IAAA,OAAO,IAAA;AAAA,EACT,CAAA;AAGA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,sBAAsB,YAAY;AAEtC,MAAA,IAAI,YAAA,IAAgB,cAAc,OAAA,EAAS;AAE3C,MAAA,MAAM,UAAU,UAAA,EAAW;AAC3B,MAAA,IAAI,CAAC,OAAA,EAAS;AAEd,MAAA,IAAI;AAEF,QAAA,MAAM,WAAW,MAAM,KAAA;AAAA,UACrB,2DAA2D,OAAO,CAAA;AAAA,SACpE;AAEA,QAAA,IAAI,SAAS,EAAA,EAAI;AACf,UAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AACjC,UAAA,iBAAA,CAAkB,KAAK,aAAa,CAAA;AAAA,QACtC;AAAA,MACF,SAAS,KAAA,EAAO;AACd,QAAA,OAAA,CAAQ,KAAA,CAAM,mCAAmC,KAAK,CAAA;AAAA,MACxD;AAAA,IACF,CAAA;AAEA,IAAA,mBAAA,EAAoB;AAAA,EACtB,CAAA,EAAG,CAAC,QAAA,EAAU,SAAA,EAAW,YAAY,CAAC,CAAA;AAGtC,EAAA,MAAM,kBAAkB,MAAM;AAE5B,IAAA,IAAI,cAAc,OAAO,YAAA;AAGzB,IAAA,IAAI,cAAc,SAAA,EAAW;AAC3B,MAAA,MAAM,UAAU,UAAA,EAAW;AAC3B,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,OAAO,8BAA8B,OAAO,CAAA,cAAA,CAAA;AAAA,MAC9C;AAAA,IACF;AAGA,IAAA,IAAI,SAAA,KAAc,WAAW,cAAA,EAAgB;AAC3C,MAAA,OAAO,cAAA;AAAA,IACT;AAGA,IAAA,OAAO,uCAAA;AAAA,EACT,CAAA;AAGA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,WAAA,EAAa;AACf,MAAA,QAAA,CAAS,IAAA,CAAK,MAAM,QAAA,GAAW,QAAA;AAAA,IACjC,CAAA,MAAO;AACL,MAAA,QAAA,CAAS,IAAA,CAAK,MAAM,QAAA,GAAW,EAAA;AAAA,IACjC;AAEA,IAAA,OAAO,MAAM;AACX,MAAA,QAAA,CAAS,IAAA,CAAK,MAAM,QAAA,GAAW,EAAA;AAAA,IACjC,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,WAAW,CAAC,CAAA;AAGhB,EAAA,MAAM,cAAc,MAAM;AACxB,IAAA,MAAM,UAAU,UAAA,EAAW;AAE3B,IAAA,MAAM,cAAA,GAAiB,SAAA;AAEvB,IAAA,IAAI,cAAc,SAAA,EAAW;AAC3B,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,OAAO,CAAA,8BAAA,EAAiC,OAAO,CAAA,EAAG,cAAA,GAAiB,gBAAgB,EAAE,CAAA,CAAA;AAAA,MACvF;AAGA,MAAA,MAAM,SAAA,GAAY,QAAA,CAAS,QAAA,CAAS,GAAG,CAAA;AACvC,MAAA,OACE,YACC,cAAA,IAAkB,CAAC,YAChB,aAAA,GACA,cAAA,IAAkB,YAChB,aAAA,GACA,EAAA,CAAA;AAAA,IAEV;AAEA,IAAA,IAAI,SAAA,KAAc,WAAW,OAAA,EAAS;AACpC,MAAA,OAAO,CAAA,+BAAA,EAAkC,OAAO,CAAA,EAAG,cAAA,GAAiB,gBAAgB,EAAE,CAAA,CAAA;AAAA,IACxF;AAGA,IAAA,OAAO,QAAA;AAAA,EACT,CAAA;AAGA,EAAA,MAAM,mBAAmB,MAAM;AAC7B,IAAA,IAAI,CAAC,WAAA,CAAY,QAAA,CAAS,GAAG,CAAA,SAAU,EAAC;AAExC,IAAA,MAAM,CAAC,OAAO,MAAM,CAAA,GAAI,YAAY,KAAA,CAAM,GAAG,CAAA,CAAE,GAAA,CAAI,MAAM,CAAA;AACzD,IAAA,IAAI,CAAC,KAAA,IAAS,CAAC,MAAA,SAAe,EAAC;AAE/B,IAAA,OAAO;AAAA,MACL,WAAA,EAAa,CAAA,EAAG,KAAK,CAAA,CAAA,EAAI,MAAM,CAAA;AAAA,KACjC;AAAA,EACF,CAAA;AAGA,EAAA,MAAM,uBAAuB,MAAM;AACjC,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,cAAA,CAAe,IAAI,CAAA;AAAA,IACrB,CAAA,MAAO;AACL,MAAA,YAAA,CAAa,IAAI,CAAA;AAAA,IACnB;AAAA,EACF,CAAA;AAEA,EAAA,4BACG,KAAA,EAAA,EAAI,SAAA,EAAW,WAAA,KAAgB,MAAA,GAAS,qBAAqB,EAAA,EAE5D,QAAA,EAAA;AAAA,oBAAAA,GAAAA,CAAC,SAAI,SAAA,EAAU,0BAAA,EAA2B,OAAO,gBAAA,EAAiB,EAC/D,WAAC,KAAA,IAAS,SAAA;AAAA;AAAA,sBAETA,GAAAA,CAAC,KAAA,EAAA,EAAI,WAAU,eAAA,EACZ,QAAA,EAAA,SAAA,KAAc,gCACbA,GAAAA;AAAA,QAAC,QAAA;AAAA,QAAA;AAAA,UACC,KAAK,WAAA,EAAY;AAAA,UACjB,SAAA,EAAU,eAAA;AAAA,UACV,KAAA,EAAM,0FAAA;AAAA,UACN,eAAA,EAAe;AAAA;AAAA,0BAGjBA,GAAAA;AAAA,QAAC,OAAA;AAAA,QAAA;AAAA,UACC,GAAA,EAAK,QAAA;AAAA,UACL,QAAA,EAAQ,IAAA;AAAA,UACR,YAAA,EAAa,YAAA;AAAA,UACb,aAAA,EAAe,CAAC,CAAA,KAAM,CAAA,CAAE,cAAA,EAAe;AAAA,UACvC,QAAA,EAAQ,IAAA;AAAA,UACR,SAAA,EAAU,eAAA;AAAA,UACX,QAAA,EAAA;AAAA;AAAA,OAED,EAEJ;AAAA;AAAA;AAAA,sBAGA,IAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,SAAA,EAAU,oCAAA;AAAA,UACV,OAAA,EAAS,oBAAA;AAAA,UAET,QAAA,EAAA;AAAA,4BAAAA,GAAAA;AAAA,cAAC,KAAA;AAAA,cAAA;AAAA,gBACC,GAAA,EAAK,iBAAgB,IAAK,kBAAA;AAAA,gBAC1B,GAAA,EAAK,iBAAiB,KAAK,CAAA,CAAA;AAAA,gBAC3B,SAAA,EAAU;AAAA;AAAA,aACZ;AAAA,4BACAA,IAAC,KAAA,EAAA,EAAI,SAAA,EAAU,iEACb,QAAA,kBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,UAAA,EAEb,QAAA,EAAA;AAAA,8BAAAA,GAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,iFAAA,EAAkF,CAAA;AAAA,8BAGlGA,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,kEAAA,EACb,QAAA,kBAAAA,GAAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAU,SAAA,EAAU,IAAA,EAAK,cAAA,EAAe,CAAA,EAChD;AAAA,aAAA,EACF,CAAA,EACF;AAAA;AAAA;AAAA;AACF,KAAA,EAEJ,CAAA;AAAA,IAGC,KAAA,IAAS,WAAA,oBACR,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,oFAAA,EACb,QAAA,EAAA;AAAA,sBAAAA,GAAAA;AAAA,QAAC,QAAA;AAAA,QAAA;AAAA,UACC,OAAA,EAAS,MAAM,cAAA,CAAe,KAAK,CAAA;AAAA,UACnC,SAAA,EAAU,mGAAA;AAAA,UACV,YAAA,EAAW,OAAA;AAAA,UAEX,QAAA,kBAAAA,GAAAA,CAAC,CAAA,EAAA,EAAE,SAAA,EAAU,uBAAA,EAAwB;AAAA;AAAA,OACvC;AAAA,sBAEAA,GAAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,SAAA,EAAU,2CAAA;AAAA,UACV,OAAO,gBAAA,EAAiB;AAAA,UAEvB,QAAA,EAAA,SAAA,KAAc,aAAA;AAAA;AAAA,4BAEbA,GAAAA;AAAA,cAAC,QAAA;AAAA,cAAA;AAAA,gBACC,KAAK,WAAA,EAAY;AAAA,gBACjB,SAAA,EAAU,eAAA;AAAA,gBACV,KAAA,EAAM,0FAAA;AAAA,gBACN,eAAA,EAAe;AAAA;AAAA;AAChB;AAAA;AAAA,4BAGDA,GAAAA;AAAA,cAAC,OAAA;AAAA,cAAA;AAAA,gBACC,GAAA,EAAK,QAAA;AAAA,gBACL,QAAA,EAAQ,IAAA;AAAA,gBACR,YAAA,EAAa,YAAA;AAAA,gBACb,aAAA,EAAe,CAAC,CAAA,KAAM,CAAA,CAAE,cAAA,EAAe;AAAA,gBACvC,QAAA,EAAQ,IAAA;AAAA,gBACR,SAAA,EAAU,eAAA;AAAA,gBACX,QAAA,EAAA;AAAA;AAAA;AAED;AAAA;AAAA;AAEJ,KAAA,EACF;AAAA,GAAA,EAEJ,CAAA;AAEJ;AChQA,IAAM,YAAA,GACJ,8FAAA;AAWK,SAAS,aAAa,OAAA,EAAiB;AAC5C,EAAA,MAAM,QAAQ,OAAA,CAAQ,KAAA,CAAM,YAAY,CAAA,EAAG,IAAA,CAAK,EAAE,CAAA,IAAK,EAAA;AACvD,EAAA,MAAM,MAAA,GAAS,QACZ,OAAA,CAAQ,YAAA,EAAc,EAAE,CAAA,CACxB,OAAA,CAAQ,kDAAA,EAAoD,EAAE,CAAA,CAC9D,OAAA;AAAA,IACC,4EAAA;AAAA,IACA;AAAA,GACF,CACC,OAAA;AAAA,IACC,uCAAA;AAAA,IACA,CAAC,CAAA,EAAG,KAAA,EAAe,GAAA,KAAgB;AACjC,MAAA,MAAM,MAAA,GAAS,GAAA,CACZ,OAAA,CAAQ,UAAA,EAAY,OAAO,EAC3B,OAAA,CAAQ,0BAAA,EAA4B,gBAAgB,CAAA,CACpD,OAAA,CAAQ,kCAAA,EAAoC,SAAS,CAAA,CACrD,OAAA,CAAQ,kCAAA,EAAoC,qBAAqB,CAAA,CACjE,OAAA,CAAQ,4BAAA,EAA8B,cAAc,CAAA,CACpD,OAAA,CAAQ,mBAAA,EAAqB,OAAO,CAAA,CACpC,OAAA;AAAA,QACC,sEAAA;AAAA,QACA;AAAA,OACF;AACF,MAAA,OAAO,CAAA,MAAA,EAAS,KAAK,CAAA,CAAA,EAAI,MAAM,CAAA,QAAA,CAAA;AAAA,IACjC;AAAA,GACF;AAEF,EAAA,MAAM,IAAA,GAAO,sGAAsG,MAAM,CAAA,MAAA,CAAA;AACzH,EAAA,OAAO,EAAE,OAAO,IAAA,EAAK;AACvB;AAEA,SAAS,WAAW,IAAA,EAAkB;AACpC,EAAA,KAAA,MAAW,GAAA,IAAO,IAAA,CAAK,gBAAA,CAAiB,QAAQ,CAAA,EAAG;AACjD,IAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,IAAA,KAAA,MAAW,EAAE,MAAM,KAAA,EAAM,IAAK,IAAI,UAAA,EAAY,MAAA,CAAO,YAAA,CAAa,IAAA,EAAM,KAAK,CAAA;AAC7E,IAAA,MAAA,CAAO,cAAc,GAAA,CAAI,WAAA;AACzB,IAAA,GAAA,CAAI,YAAY,MAAM,CAAA;AAAA,EACxB;AACF;AAOO,IAAM,cAAc,CAAC,EAAE,OAAA,EAAS,SAAA,EAAW,OAAM,KAAwB;AAC9E,EAAA,MAAM,OAAA,GAAU,OAAuB,IAAI,CAAA;AAC3C,EAAA,MAAM,UAAA,GAAa,OAAsB,IAAI,CAAA;AAC7C,EAAA,MAAM,EAAE,KAAA,EAAO,IAAA,EAAK,GAAI,aAAa,OAAO,CAAA;AAE5C,EAAAC,UAAU,MAAM;AACd,IAAA,MAAM,OAAO,OAAA,CAAQ,OAAA;AACrB,IAAA,IAAI,CAAC,IAAA,EAAM;AAKX,IAAA,MAAM,IAAA,GAAO,KAAK,UAAA,IAAc,IAAA,CAAK,aAAa,EAAE,IAAA,EAAM,QAAQ,CAAA;AAClE,IAAA,IAAI,UAAA,CAAW,YAAY,IAAA,EAAM;AAIjC,IAAA,IAAI,WAAW,OAAA,KAAY,IAAA,IAAQ,CAAC,IAAA,CAAK,eAAc,EAAG;AACxD,MAAA,IAAA,CAAK,SAAA,GAAY,IAAA;AACjB,MAAA,UAAA,CAAW,IAAI,CAAA;AAAA,IACjB;AACA,IAAA,UAAA,CAAW,OAAA,GAAU,IAAA;AAAA,EACvB,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAIT,EAAAA,UAAU,MAAM;AACd,IAAA,MAAM,OAAO,OAAA,CAAQ,OAAA;AACrB,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,MAAM,IAAA,GAAO,MACX,IAAA,CAAK,SAAA,CAAU,MAAA;AAAA,MACb,MAAA;AAAA,MACA,CAAC,CAAC,IAAA,CAAK,aAAA,EAAe,OAAA,CAAQ,OAAO,CAAA,IACnC,QAAA,CAAS,eAAA,CAAgB,SAAA,CAAU,QAAA,CAAS,MAAM;AAAA,KACtD;AACF,IAAA,IAAA,EAAK;AACL,IAAA,MAAM,QAAA,GAAW,IAAI,gBAAA,CAAiB,IAAI,CAAA;AAC1C,IAAA,QAAA,CAAS,OAAA,CAAQ,SAAS,eAAA,EAAiB;AAAA,MACzC,UAAA,EAAY,IAAA;AAAA,MACZ,eAAA,EAAiB,CAAC,OAAO,CAAA;AAAA,MACzB,OAAA,EAAS;AAAA,KACV,CAAA;AACD,IAAA,OAAO,MAAM,SAAS,UAAA,EAAW;AAAA,EACnC,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,uBACED,GAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,OAAA;AAAA,MACL,SAAA;AAAA,MACA,KAAA;AAAA,MACA,wBAAA,EAAwB,IAAA;AAAA,MACxB,uBAAA,EAAyB;AAAA;AAAA;AAAA,QAGvB,MAAA,EACE,OAAO,MAAA,KAAW,WAAA,GACd,GAAG,KAAK,CAAA,gCAAA,EAAmC,IAAI,CAAA,WAAA,CAAA,GAC/C;AAAA;AACR;AAAA,GACF;AAEJ","file":"chunk-36QHCIPM.js","sourcesContent":["import type React from \"react\";\nimport { cn } from \"../primitives/lib/utils\";\n\nexport interface ImageAttributesInput {\n src: string;\n alt: string;\n sizes?: string;\n /** Above-the-fold image (e.g. LCP): fetched eagerly at high priority. */\n priority: boolean;\n}\n\n/**\n * Lets a host swap an image's `src`/`srcSet` for its own optimizer's URLs\n * (e.g. Next.js's `getImageProps`) without this package depending on the\n * host framework. Return `null` to keep the original URL.\n */\nexport type ImageAttributesResolver = (\n input: ImageAttributesInput,\n) => React.ImgHTMLAttributes<HTMLImageElement> | null;\n\nlet imageAttributesResolver: ImageAttributesResolver | null = null;\n\n/**\n * Registers the host's resolver. Call once at server start-up; renders that\n * run without one emit the original URL.\n */\nexport function setImageAttributesResolver(resolver: ImageAttributesResolver | null) {\n imageAttributesResolver = resolver;\n}\n\nexport type ImageObjectFit = \"cover\" | \"contain\" | \"fill\" | \"none\" | \"scale-down\";\n\nexport const roundedMap: Record<number, string> = {\n 0: \"rounded-none\",\n 1: \"rounded-sm\",\n 2: \"rounded\",\n 3: \"rounded-md\",\n 4: \"rounded-lg\",\n 5: \"rounded-xl\",\n 6: \"rounded-2xl\",\n 7: \"rounded-3xl\",\n 8: \"rounded-full\",\n};\n\ninterface ImgProps {\n src: string;\n classes?: string;\n alt?: string;\n defaultImage?: string;\n loading?: \"eager\" | \"lazy\";\n /** Marks the image as likely LCP: eager load with high fetch priority. */\n priority?: boolean;\n height?: string;\n sizes?: string;\n width?: string;\n borderRadius?: number | string;\n noDefaultImage?: boolean;\n className?: string;\n objectFit?: ImageObjectFit;\n}\n\nconst objectFitClass: Record<ImageObjectFit, string> = {\n cover: \"object-cover\",\n contain: \"object-contain\",\n fill: \"object-fill\",\n none: \"object-none\",\n \"scale-down\": \"object-scale-down\",\n};\n\nexport const Image = (props: ImgProps) => {\n const {\n src,\n alt = \"\",\n defaultImage,\n priority = false,\n loading = priority ? \"eager\" : \"lazy\",\n sizes,\n noDefaultImage = false,\n className = \"\",\n classes = \"\",\n width = \"w-full\",\n height = \"h-auto\",\n objectFit = \"cover\",\n borderRadius,\n } = props;\n const source = src || defaultImage;\n\n const radiusClass =\n typeof borderRadius === \"number\"\n ? (roundedMap[borderRadius] ?? \"rounded\")\n : borderRadius;\n\n if (!source) {\n if (noDefaultImage) return null;\n return (\n <div\n className={cn(\n \"relative aspect-video overflow-hidden bg-muted\",\n width,\n height,\n radiusClass,\n classes,\n className,\n )}\n />\n );\n }\n\n const optimized = imageAttributesResolver?.({ src: source, alt, sizes, priority });\n\n return (\n <div\n className={cn(\n \"relative aspect-video overflow-hidden\",\n width,\n height,\n radiusClass,\n classes,\n className,\n )}\n >\n <img\n src={source}\n alt={alt}\n sizes={sizes}\n loading={loading}\n decoding=\"async\"\n fetchPriority={priority ? \"high\" : undefined}\n {...optimized}\n className={cn(\n \"absolute inset-0 h-full w-full\",\n radiusClass,\n objectFitClass[objectFit],\n )}\n />\n </div>\n );\n};\n","\"use client\";\n\nimport * as React from \"react\";\n\nexport interface PageBuilderLinkProps {\n href: string;\n children?: React.ReactNode;\n className?: string;\n style?: React.CSSProperties;\n onClick?: React.MouseEventHandler<HTMLAnchorElement>;\n target?: string;\n rel?: string;\n}\n\n/**\n * Optional host-supplied link. Next.js apps can pass `next/link`; Vite / Remix\n * / plain React hosts can omit it and NavLink will render a native `<a>`.\n */\nexport type PageBuilderLinkComponent = React.ComponentType<PageBuilderLinkProps>;\n\n// Server-rendered pages load this module more than once (each client island is\n// bundled on its own), so the context is shared through the global registry\n// rather than created per copy. Otherwise a provider from one copy would be\n// invisible to a NavLink from another.\nconst LINK_CONTEXT_KEY = Symbol.for(\"@frontlit/page-builder/link-context\");\nconst globalRegistry = globalThis as typeof globalThis & {\n [LINK_CONTEXT_KEY]?: React.Context<PageBuilderLinkComponent | null>;\n};\nconst PageBuilderLinkContext = (globalRegistry[LINK_CONTEXT_KEY] ??=\n React.createContext<PageBuilderLinkComponent | null>(null));\n\nexport function PageBuilderLinkProvider({\n component,\n children,\n}: {\n component?: PageBuilderLinkComponent | null;\n children: React.ReactNode;\n}) {\n return (\n <PageBuilderLinkContext.Provider value={component ?? null}>\n {children}\n </PageBuilderLinkContext.Provider>\n );\n}\n\nexport function usePageBuilderLinkComponent() {\n return React.useContext(PageBuilderLinkContext);\n}\n\ninterface NavLinkProps {\n href: string;\n children?: React.ReactNode;\n openInSameTab?: boolean;\n style?: React.CSSProperties;\n className?: string;\n onClick?: () => void;\n}\n\n/**\n * A navigation-aware link used inside block widgets. In-page anchors scroll\n * to the target. Internal paths (`/...`) use the host `linkComponent` when\n * one is provided (e.g. Next.js `Link`); everything else is a native `<a>`.\n */\nexport function NavLink({\n href,\n children,\n openInSameTab = true,\n style,\n className = \"\",\n onClick,\n}: NavLinkProps) {\n const LinkComponent = usePageBuilderLinkComponent();\n const isInternal = Boolean(href && href.startsWith(\"/\"));\n const isInPageNavigation = Boolean(href && href.startsWith(\"#\"));\n const target = openInSameTab ? undefined : \"_blank\";\n const rel = target === \"_blank\" ? \"noopener noreferrer\" : undefined;\n const handleClick: React.MouseEventHandler<HTMLAnchorElement> = () => {\n onClick?.();\n };\n\n if (!isInPageNavigation && isInternal && LinkComponent) {\n return (\n <LinkComponent\n href={href}\n style={style}\n className={className}\n onClick={handleClick}\n target={target}\n rel={rel}\n >\n {children}\n </LinkComponent>\n );\n }\n\n return (\n <a\n href={href}\n style={style}\n className={className}\n target={target}\n rel={rel}\n onClick={handleClick}\n >\n {children}\n </a>\n );\n}\n","export type SupportedVideoPlatform = \"youtube\" | \"vimeo\";\n\nconst YOUTUBE_HOSTNAMES = new Set([\n \"youtube.com\",\n \"www.youtube.com\",\n \"m.youtube.com\",\n \"music.youtube.com\",\n \"youtube-nocookie.com\",\n \"www.youtube-nocookie.com\",\n \"youtu.be\",\n]);\n\nconst VIMEO_HOSTNAMES = new Set([\"vimeo.com\", \"www.vimeo.com\", \"player.vimeo.com\"]);\n\nconst isNumeric = (value: string) => /^\\d+$/.test(value);\n\nconst extractYouTubeVideoId = (parsedUrl: URL) => {\n const hostname = parsedUrl.hostname.toLowerCase();\n\n if (!YOUTUBE_HOSTNAMES.has(hostname)) {\n return null;\n }\n\n if (hostname === \"youtu.be\") {\n return parsedUrl.pathname.split(\"/\").filter(Boolean)[0] || null;\n }\n\n if (parsedUrl.pathname === \"/watch\") {\n return parsedUrl.searchParams.get(\"v\");\n }\n\n const pathParts = parsedUrl.pathname.split(\"/\").filter(Boolean);\n const firstPart = pathParts[0];\n const secondPart = pathParts[1];\n\n if (\n (firstPart === \"embed\" ||\n firstPart === \"v\" ||\n firstPart === \"shorts\" ||\n firstPart === \"live\") &&\n secondPart\n ) {\n return secondPart;\n }\n\n return null;\n};\n\nconst extractVimeoVideoId = (parsedUrl: URL) => {\n const hostname = parsedUrl.hostname.toLowerCase();\n\n if (!VIMEO_HOSTNAMES.has(hostname)) {\n return null;\n }\n\n const pathParts = parsedUrl.pathname.split(\"/\").filter(Boolean);\n\n for (let index = pathParts.length - 1; index >= 0; index--) {\n if (isNumeric(pathParts[index])) {\n return pathParts[index];\n }\n }\n\n return null;\n};\n\nexport function extractVideoId(url?: string, platform?: SupportedVideoPlatform) {\n if (!url) {\n return null;\n }\n\n const normalizedUrl = /^[a-z][a-z\\d+\\-.]*:\\/\\//i.test(url) ? url : `https://${url}`;\n\n try {\n const parsedUrl = new URL(normalizedUrl);\n\n if (platform === \"youtube\") {\n return extractYouTubeVideoId(parsedUrl);\n }\n\n if (platform === \"vimeo\") {\n return extractVimeoVideoId(parsedUrl);\n }\n\n return extractYouTubeVideoId(parsedUrl) || extractVimeoVideoId(parsedUrl);\n } catch {\n return null;\n }\n}\n","import { useState, useEffect } from \"react\";\nimport { Play, X } from \"lucide-react\";\nimport { extractVideoId } from \"../utils\";\n\ntype VideoType = \"youtube\" | \"vimeo\" | \"self-hosted\";\n\nexport type AspectRatio =\n | \"16/9\" // Widescreen\n | \"4/3\" // Standard\n | \"1/1\" // Square\n | \"21/9\" // Ultrawide\n | \"9/16\" // Vertical\n | \"2/1\" // Panoramic\n | \"3/2\" // Classic\n | string; // Allow custom aspect ratios\n\nexport interface VideoThumbnailProps {\n title?: string;\n thumbnailUrl?: string;\n videoUrl: string;\n aspectRatio?: AspectRatio;\n modal?: boolean;\n}\n\nexport function VideoWithPreview({\n title = \"Video\",\n thumbnailUrl,\n videoUrl = \"https://www.youtube.com/watch?v=7OP2bU9RWVE\",\n aspectRatio = \"16/9\",\n modal = false,\n}: VideoThumbnailProps) {\n const [isModalOpen, setIsModalOpen] = useState(false);\n const [isPlaying, setIsPlaying] = useState(false);\n const [vimeoThumbnail, setVimeoThumbnail] = useState<string | null>(null);\n\n const detectVideoType = (): VideoType => {\n if (extractVideoId(videoUrl, \"youtube\")) {\n return \"youtube\";\n }\n\n if (extractVideoId(videoUrl, \"vimeo\")) {\n return \"vimeo\";\n }\n\n return \"self-hosted\";\n };\n\n const videoType = detectVideoType();\n\n const getVideoId = () => {\n if (videoType === \"youtube\") {\n return extractVideoId(videoUrl, \"youtube\");\n }\n\n if (videoType === \"vimeo\") {\n return extractVideoId(videoUrl, \"vimeo\");\n }\n\n return null;\n };\n\n // Fetch Vimeo thumbnail if needed\n useEffect(() => {\n const fetchVimeoThumbnail = async () => {\n // Skip fetching if a thumbnail URL is already provided\n if (thumbnailUrl || videoType !== \"vimeo\") return;\n\n const videoId = getVideoId();\n if (!videoId) return;\n\n try {\n // Use Vimeo's oEmbed API to get thumbnail (no token required)\n const response = await fetch(\n `https://vimeo.com/api/oembed.json?url=https://vimeo.com/${videoId}`,\n );\n\n if (response.ok) {\n const data = await response.json();\n setVimeoThumbnail(data.thumbnail_url);\n }\n } catch (error) {\n console.error(\"Error fetching Vimeo thumbnail:\", error);\n }\n };\n\n fetchVimeoThumbnail();\n }, [videoUrl, videoType, thumbnailUrl]);\n\n // Get the appropriate thumbnail URL\n const getThumbnailUrl = () => {\n // If a thumbnail URL is explicitly provided, use that\n if (thumbnailUrl) return thumbnailUrl;\n\n // For YouTube videos, generate thumbnail URL\n if (videoType === \"youtube\") {\n const videoId = getVideoId();\n if (videoId) {\n return `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`;\n }\n }\n\n // For Vimeo videos, use the fetched thumbnail\n if (videoType === \"vimeo\" && vimeoThumbnail) {\n return vimeoThumbnail;\n }\n\n // Fall back to placeholder\n return \"/placeholder.svg?height=480&width=854\";\n };\n\n // Lock body scroll when modal is open\n useEffect(() => {\n if (isModalOpen) {\n document.body.style.overflow = \"hidden\";\n } else {\n document.body.style.overflow = \"\";\n }\n\n return () => {\n document.body.style.overflow = \"\";\n };\n }, [isModalOpen]);\n\n // Get embed URL based on video type\n const getEmbedUrl = () => {\n const videoId = getVideoId();\n // Add autoplay parameter for in-place videos\n const shouldAutoplay = isPlaying;\n\n if (videoType === \"youtube\") {\n if (videoId) {\n return `https://www.youtube.com/embed/${videoId}${shouldAutoplay ? \"?autoplay=1\" : \"\"}`;\n }\n\n // If we couldn't extract the ID but it's a YouTube URL, try to use it directly\n const hasParams = videoUrl.includes(\"?\");\n return (\n videoUrl +\n (shouldAutoplay && !hasParams\n ? \"?autoplay=1\"\n : shouldAutoplay && hasParams\n ? \"&autoplay=1\"\n : \"\")\n );\n }\n\n if (videoType === \"vimeo\" && videoId) {\n return `https://player.vimeo.com/video/${videoId}${shouldAutoplay ? \"?autoplay=1\" : \"\"}`;\n }\n\n // For self-hosted videos, return the URL as is\n return videoUrl;\n };\n\n // Calculate aspect ratio style\n const aspectRatioStyle = () => {\n if (!aspectRatio.includes(\"/\")) return {};\n\n const [width, height] = aspectRatio.split(\"/\").map(Number);\n if (!width || !height) return {};\n\n return {\n aspectRatio: `${width}/${height}`,\n };\n };\n\n // Handle thumbnail click\n const handleThumbnailClick = () => {\n if (modal) {\n setIsModalOpen(true);\n } else {\n setIsPlaying(true);\n }\n };\n\n return (\n <div className={aspectRatio === \"9/16\" ? \"max-w-xs mx-auto\" : \"\"}>\n {/* Thumbnail with play button or in-place video */}\n <div className=\"relative overflow-hidden\" style={aspectRatioStyle()}>\n {!modal && isPlaying ? (\n // In-place video player\n <div className=\"w-full h-full\">\n {videoType !== \"self-hosted\" ? (\n <iframe\n src={getEmbedUrl()}\n className=\"w-full h-full\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n ></iframe>\n ) : (\n <video\n src={videoUrl}\n controls\n controlsList=\"nodownload\" // eslint-disable-line react/no-unknown-property\n onContextMenu={(e) => e.preventDefault()}\n autoPlay\n className=\"w-full h-full\"\n >\n Your browser does not support the video tag.\n </video>\n )}\n </div>\n ) : (\n // Thumbnail with play button\n <div\n className=\"cursor-pointer group w-full h-full\"\n onClick={handleThumbnailClick}\n >\n <img\n src={getThumbnailUrl() || \"/placeholder.svg\"}\n alt={`Thumbnail for ${title}`}\n className=\"w-full h-full object-cover transition-transform duration-300 group-hover:scale-105\"\n />\n <div className=\"absolute inset-0 bg-black/30 flex items-center justify-center\">\n <div className=\"relative\">\n {/* Smaller grayish concentric wave animation */}\n <span className=\"absolute -inset-1.5 rounded-full bg-gray-200/50 animate-pulse duration-[3000ms]\"></span>\n\n {/* Play button */}\n <div className=\"relative z-1 rounded-full bg-primary p-4 text-primary-foreground\">\n <Play className=\"h-8 w-8\" fill=\"currentColor\" />\n </div>\n </div>\n </div>\n </div>\n )}\n </div>\n\n {/* Custom Video Modal (only used when modal is true) */}\n {modal && isModalOpen && (\n <div className=\"fixed inset-0 bg-black/95 z-50 flex items-center justify-center p-4 sm:p-6 md:p-10\">\n <button\n onClick={() => setIsModalOpen(false)}\n className=\"absolute top-2 right-2 sm:top-4 sm:right-4 text-white hover:text-gray-300 focus:outline-none z-10\"\n aria-label=\"Close\"\n >\n <X className=\"h-6 w-6 sm:h-8 sm:w-8\" />\n </button>\n\n <div\n className=\"w-full max-w-xl sm:max-w-2xl md:max-w-3xl\"\n style={aspectRatioStyle()}\n >\n {videoType !== \"self-hosted\" ? (\n // YouTube or Vimeo video\n <iframe\n src={getEmbedUrl()}\n className=\"w-full h-full\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n ></iframe>\n ) : (\n // Self-hosted video\n <video\n src={videoUrl}\n controls\n controlsList=\"nodownload\" // eslint-disable-line react/no-unknown-property\n onContextMenu={(e) => e.preventDefault()}\n autoPlay\n className=\"w-full h-full\"\n >\n Your browser does not support the video tag.\n </video>\n )}\n </div>\n </div>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport { useEffect, useRef } from \"react\";\n\ninterface InlineEmbedProps {\n content: string;\n className?: string;\n style?: React.CSSProperties;\n}\n\n// Font stylesheets and connection hints only work at document level (a\n// shadow root ignores @font-face), so these links are hoisted out of it.\nconst HOISTED_LINK =\n /<link\\b[^>]*(?:rel=[\"']?(?:preconnect|dns-prefetch|preload)|href=[\"'][^\"']*fonts?\\.)[^>]*>/gi;\n\n/**\n * Snippets are usually written as a standalone document (they used to live in\n * an iframe). Recreate that inside the shadow root: the host plays `html` and\n * is a size container, a wrapper plays `body`, and viewport units and width\n * media queries are rewritten to measure the embed (as they measured the\n * iframe) instead of the browser window. \"Fill the frame\" heights are dropped,\n * as are scripts that only synced state with `window.parent`, which the shadow\n * host handles itself.\n */\nexport function prepareEmbed(content: string) {\n const links = content.match(HOISTED_LINK)?.join(\"\") ?? \"\";\n const markup = content\n .replace(HOISTED_LINK, \"\")\n .replace(/<!doctype[^>]*>|<\\/?(?:html|head|body)\\b[^>]*>/gi, \"\")\n .replace(\n /<script\\b[^>]*>(?:(?!<\\/script>)[\\s\\S])*window\\.parent[\\s\\S]*?<\\/script>/gi,\n \"\",\n )\n .replace(\n /<style\\b([^>]*)>([\\s\\S]*?)<\\/style>/gi,\n (_, attrs: string, css: string) => {\n const scoped = css\n .replace(/:root\\b/g, \":host\")\n .replace(/(^|[\\s,{}])html\\.dark\\b/g, \"$1:host(.dark)\")\n .replace(/(^|[\\s,{}])html(?=[\\s,{:.[>~+])/g, \"$1:host\")\n .replace(/(^|[\\s,{}])body(?=[\\s,{:.[>~+])/g, \"$1[data-embed-body]\")\n .replace(/min-height\\s*:\\s*100d?vh/gi, \"min-height:0\")\n .replace(/(\\d*\\.?\\d+)vw\\b/gi, \"$1cqw\")\n .replace(\n /@media\\s+(?:only\\s+)?(?:screen\\s+and\\s+)?(\\([^{]*\\bwidth\\b[^{]*)\\{/gi,\n \"@container $1{\",\n );\n return `<style${attrs}>${scoped}</style>`;\n },\n );\n\n const body = `<style>:host{display:block;container-type:inline-size;overflow-x:clip}</style><div data-embed-body>${markup}</div>`;\n return { links, body };\n}\n\nfunction runScripts(root: ShadowRoot) {\n for (const old of root.querySelectorAll(\"script\")) {\n const script = document.createElement(\"script\");\n for (const { name, value } of old.attributes) script.setAttribute(name, value);\n script.textContent = old.textContent;\n old.replaceWith(script);\n }\n}\n\n/**\n * Renders a raw HTML snippet inline, inside a declarative shadow root. The\n * markup is part of the server-rendered page (so crawlers index it) while its\n * styles stay isolated from the site's in both directions.\n */\nexport const InlineEmbed = ({ content, className, style }: InlineEmbedProps) => {\n const hostRef = useRef<HTMLDivElement>(null);\n const appliedRef = useRef<string | null>(null);\n const { links, body } = prepareEmbed(content);\n\n useEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n\n // A server-rendered host already has its shadow root from the HTML\n // parser; a client-rendered one (editor, client navigation) does not,\n // because innerHTML ignores declarative shadow roots.\n const root = host.shadowRoot ?? host.attachShadow({ mode: \"open\" });\n if (appliedRef.current === body) return;\n // The HTML parser already ran the scripts of a server-rendered shadow\n // root; only markup set through innerHTML (which never runs scripts)\n // needs them re-created, or they'd run twice.\n if (appliedRef.current !== null || !root.hasChildNodes()) {\n root.innerHTML = body;\n runScripts(root);\n }\n appliedRef.current = body;\n }, [body]);\n\n // Mirror the site's color mode onto the host so snippets can use\n // `:host(.dark)`.\n useEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n const sync = () =>\n host.classList.toggle(\n \"dark\",\n !!host.parentElement?.closest(\".dark\") ||\n document.documentElement.classList.contains(\"dark\"),\n );\n sync();\n const observer = new MutationObserver(sync);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [\"class\"],\n subtree: true,\n });\n return () => observer.disconnect();\n }, []);\n\n return (\n <div\n ref={hostRef}\n className={className}\n style={style}\n suppressHydrationWarning\n dangerouslySetInnerHTML={{\n // Only the HTML parser turns the template into a shadow root; on the\n // client the effect attaches it instead.\n __html:\n typeof window === \"undefined\"\n ? `${links}<template shadowrootmode=\"open\">${body}</template>`\n : links,\n }}\n />\n );\n};\n"]}
@@ -0,0 +1,56 @@
1
+ "use client";
2
+ import { Button2, Button } from './chunk-J7Z4UW45.js';
3
+ import { useState, useEffect } from 'react';
4
+ import { Sun, Moon } from 'lucide-react';
5
+ import { jsx } from 'react/jsx-runtime';
6
+
7
+ function ThemeModeSwitcher({
8
+ resolved,
9
+ onToggle,
10
+ theme,
11
+ className,
12
+ size,
13
+ variant
14
+ }) {
15
+ const [mounted, setMounted] = useState(false);
16
+ useEffect(() => {
17
+ setMounted(true);
18
+ }, []);
19
+ const painted = mounted ? resolved : "light";
20
+ const next = painted === "dark" ? "light" : "dark";
21
+ const label = next === "dark" ? "Switch to dark mode" : "Switch to light mode";
22
+ const Icon = painted === "dark" ? Sun : Moon;
23
+ if (theme) {
24
+ return /* @__PURE__ */ jsx(
25
+ Button2,
26
+ {
27
+ type: "button",
28
+ variant: variant ?? "outline",
29
+ size: size ?? "sm",
30
+ theme,
31
+ title: label,
32
+ "aria-label": label,
33
+ className,
34
+ onClick: onToggle,
35
+ children: /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" })
36
+ }
37
+ );
38
+ }
39
+ return /* @__PURE__ */ jsx(
40
+ Button,
41
+ {
42
+ type: "button",
43
+ variant: "outline",
44
+ size: "icon",
45
+ title: label,
46
+ "aria-label": label,
47
+ className,
48
+ onClick: onToggle,
49
+ children: /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" })
50
+ }
51
+ );
52
+ }
53
+
54
+ export { ThemeModeSwitcher };
55
+ //# sourceMappingURL=chunk-BBI7UHOA.js.map
56
+ //# sourceMappingURL=chunk-BBI7UHOA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/theme-mode-switcher.tsx"],"names":["Button"],"mappings":";;;;;AAiBO,SAAS,iBAAA,CAAkB;AAAA,EAChC,QAAA;AAAA,EACA,QAAA;AAAA,EACA,KAAA;AAAA,EACA,SAAA;AAAA,EACA,IAAA;AAAA,EACA;AACF,CAAA,EAOG;AACD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,KAAK,CAAA;AAC5C,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,UAAA,CAAW,IAAI,CAAA;AAAA,EACjB,CAAA,EAAG,EAAE,CAAA;AACL,EAAA,MAAM,OAAA,GAAU,UAAU,QAAA,GAAW,OAAA;AACrC,EAAA,MAAM,IAAA,GAAO,OAAA,KAAY,MAAA,GAAS,OAAA,GAAU,MAAA;AAC5C,EAAA,MAAM,KAAA,GAAQ,IAAA,KAAS,MAAA,GAAS,qBAAA,GAAwB,sBAAA;AACxD,EAAA,MAAM,IAAA,GAAO,OAAA,KAAY,MAAA,GAAS,GAAA,GAAM,IAAA;AAExC,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,uBACE,GAAA;AAAA,MAACA,OAAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAK,QAAA;AAAA,QACL,SAAS,OAAA,IAAW,SAAA;AAAA,QACpB,MAAM,IAAA,IAAQ,IAAA;AAAA,QACd,KAAA;AAAA,QACA,KAAA,EAAO,KAAA;AAAA,QACP,YAAA,EAAY,KAAA;AAAA,QACZ,SAAA;AAAA,QACA,OAAA,EAAS,QAAA;AAAA,QAET,QAAA,kBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAU,SAAA,EAAU;AAAA;AAAA,KAC5B;AAAA,EAEJ;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAK,QAAA;AAAA,MACL,OAAA,EAAQ,SAAA;AAAA,MACR,IAAA,EAAK,MAAA;AAAA,MACL,KAAA,EAAO,KAAA;AAAA,MACP,YAAA,EAAY,KAAA;AAAA,MACZ,SAAA;AAAA,MACA,OAAA,EAAS,QAAA;AAAA,MAET,QAAA,kBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAU,SAAA,EAAU;AAAA;AAAA,GAC5B;AAEJ","file":"chunk-BBI7UHOA.js","sourcesContent":["\"use client\";\n\nimport React, { useEffect, useState } from \"react\";\nimport { Moon, Sun } from \"lucide-react\";\nimport { Button, type ButtonProps } from \"../primitives\";\nimport { Button as ChromeButton } from \"../primitives/components/ui/button\";\nimport type { ThemeStyle } from \"../models\";\nimport type { ResolvedColorMode } from \"../utils\";\n\n/**\n * Light / dark toggle. Public sites pass `theme` so the button uses\n * page-builder primitives; the builder chrome omits it and matches the\n * other icon buttons in the top bar.\n *\n * Preference starts as system (OS). The first click pins an explicit\n * light or dark side; later clicks flip between those two.\n */\nexport function ThemeModeSwitcher({\n resolved,\n onToggle,\n theme,\n className,\n size,\n variant,\n}: {\n resolved: ResolvedColorMode;\n onToggle: () => void;\n theme?: ThemeStyle;\n className?: string;\n size?: ButtonProps[\"size\"];\n variant?: ButtonProps[\"variant\"];\n}) {\n const [mounted, setMounted] = useState(false);\n useEffect(() => {\n setMounted(true);\n }, []);\n const painted = mounted ? resolved : \"light\";\n const next = painted === \"dark\" ? \"light\" : \"dark\";\n const label = next === \"dark\" ? \"Switch to dark mode\" : \"Switch to light mode\";\n const Icon = painted === \"dark\" ? Sun : Moon;\n\n if (theme) {\n return (\n <Button\n type=\"button\"\n variant={variant ?? \"outline\"}\n size={size ?? \"sm\"}\n theme={theme}\n title={label}\n aria-label={label}\n className={className}\n onClick={onToggle}\n >\n <Icon className=\"h-4 w-4\" />\n </Button>\n );\n }\n\n return (\n <ChromeButton\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n title={label}\n aria-label={label}\n className={className}\n onClick={onToggle}\n >\n <Icon className=\"h-4 w-4\" />\n </ChromeButton>\n );\n}\n"]}