@omniharmonic/openspace-renderer 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2023 @@
1
+ import {
2
+ BASE_CSS,
3
+ BEHAVIORS,
4
+ BEHAVIOR_BUDGETS,
5
+ BEHAVIOR_LOADER,
6
+ behaviorsInHtml,
7
+ isBehaviorName
8
+ } from "./chunk-RO5BKIKN.js";
9
+
10
+ // src/block.tsx
11
+ import {
12
+ migrateBlock,
13
+ resolve as resolve2
14
+ } from "@omniharmonic/openspace-schema";
15
+ import { imageTreatmentClass, TOKEN_VARS as TOKEN_VARS2, themeClass } from "@omniharmonic/openspace-theme";
16
+ import { Component, useContext as useContext2 } from "react";
17
+
18
+ // src/context.tsx
19
+ import { createContext, useContext } from "react";
20
+ var DEFAULT_CONTEXT = {
21
+ doc: void 0,
22
+ page: void 0,
23
+ mode: "production",
24
+ basePath: "",
25
+ hubUrl: void 0
26
+ };
27
+ var RenderContext = createContext(DEFAULT_CONTEXT);
28
+ function useRenderContext() {
29
+ return useContext(RenderContext);
30
+ }
31
+ var ImageResolverContext = createContext({});
32
+ function useImageMap() {
33
+ return useContext(ImageResolverContext);
34
+ }
35
+ var BlockRegistryContext = createContext(void 0);
36
+ var EditorContext = createContext(void 0);
37
+ function useEditor() {
38
+ return useContext(EditorContext);
39
+ }
40
+ var BlockFieldsContext = createContext(void 0);
41
+ function fieldPathOf(fields, value, depth = 4) {
42
+ if (!value || typeof value !== "object") return void 0;
43
+ const visit = (node, path, d) => {
44
+ if (node === value) return path.join(".");
45
+ if (d === 0 || !node || typeof node !== "object") return void 0;
46
+ if (node.type === "doc") return void 0;
47
+ for (const [k, v] of Object.entries(node)) {
48
+ if (v && typeof v === "object") {
49
+ const found = visit(v, [...path, k], d - 1);
50
+ if (found) return found;
51
+ }
52
+ }
53
+ return void 0;
54
+ };
55
+ for (const [k, v] of Object.entries(fields)) {
56
+ const found = visit(v, [k], depth);
57
+ if (found) return found;
58
+ }
59
+ return void 0;
60
+ }
61
+ var SectionContext = createContext(void 0);
62
+
63
+ // src/cx.ts
64
+ function cx(...parts) {
65
+ return parts.filter(Boolean).join(" ");
66
+ }
67
+
68
+ // src/layout-vars.ts
69
+ import {
70
+ GRID_COLUMNS,
71
+ resolve
72
+ } from "@omniharmonic/openspace-schema";
73
+ import { colorVar, TOKEN_VARS } from "@omniharmonic/openspace-theme";
74
+ import { fallbackMobileRects } from "@omniharmonic/openspace-schema";
75
+ var PREFIX = { d: "", t: "t", m: "m" };
76
+ var BPS = ["d", "t", "m"];
77
+ function responsiveVars(name2, value, map) {
78
+ const out = {};
79
+ if (!value) return out;
80
+ for (const bp of BPS) out[`--${PREFIX[bp]}${name2}`] = map(resolve(value, bp));
81
+ return out;
82
+ }
83
+ var spaceVar = (token) => `var(${TOKEN_VARS.space[token] ?? TOKEN_VARS.space.md})`;
84
+ function sectionStyleVars(style) {
85
+ return {
86
+ ...responsiveVars("pad", style.padding, spaceVar),
87
+ ...responsiveVars("minh", style.minHeight, (v) => v)
88
+ };
89
+ }
90
+ var fr = (cols) => cols.map((c) => `minmax(0, ${c}fr)`).join(" ");
91
+ function stackVars(layout) {
92
+ return {
93
+ ...responsiveVars("cols", layout.columns, fr),
94
+ ...layout.gap ? { "--gap": spaceVar(layout.gap) } : {}
95
+ };
96
+ }
97
+ function stackColumnVars(layout, index) {
98
+ const out = {};
99
+ const t = layout.order?.t;
100
+ const m = layout.order?.m ?? t;
101
+ if (t) out["--to"] = t.includes(index) ? t.indexOf(index) : t.length + index;
102
+ if (m) out["--mo"] = m.includes(index) ? m.indexOf(index) : m.length + index;
103
+ return out;
104
+ }
105
+ function blocksByColumn(layout, blocks2) {
106
+ const count = layout.columns.d.length;
107
+ const cols = Array.from({ length: count }, () => []);
108
+ for (const b of blocks2) {
109
+ const i = Math.min(Math.max(b.column ?? 0, 0), count - 1);
110
+ cols[i]?.push(b);
111
+ }
112
+ return cols;
113
+ }
114
+ function gridVars(layout) {
115
+ return responsiveVars("rows", layout.rows, (v) => v);
116
+ }
117
+ function rectVars(rect, bp) {
118
+ const p = PREFIX[bp];
119
+ const out = {
120
+ [`--${p}x`]: rect.x,
121
+ [`--${p}y`]: rect.y,
122
+ [`--${p}w`]: rect.w,
123
+ [`--${p}h`]: rect.h
124
+ };
125
+ if (rect.z !== void 0) out[`--${p}z`] = rect.z;
126
+ if (rect.bleed === "left" || rect.bleed === "both") out[`--${p}cs`] = "full-start";
127
+ if (rect.bleed === "right" || rect.bleed === "both") out[`--${p}ce`] = "full-end";
128
+ if (rect.pin === "top") out[`--${p}rs`] = 1;
129
+ if (rect.pin === "bottom") {
130
+ out[`--${p}rs`] = `span ${rect.h}`;
131
+ out[`--${p}re`] = -1;
132
+ }
133
+ return out;
134
+ }
135
+ function gridItemVars(layout, id, mobileFallback) {
136
+ const item = layout.items[id];
137
+ if (!item) return {};
138
+ const m = item.m ?? mobileFallback?.[id] ?? { ...item.d, x: 1, w: GRID_COLUMNS.m };
139
+ return {
140
+ ...rectVars(item.d, "d"),
141
+ ...rectVars(item.t ?? item.d, "t"),
142
+ ...rectVars(clampRect(m, GRID_COLUMNS.m), "m")
143
+ };
144
+ }
145
+ function clampRect(rect, cols) {
146
+ if (rect.x + rect.w - 1 <= cols) return rect;
147
+ const x = Math.min(rect.x, cols);
148
+ return { ...rect, x, w: Math.max(1, cols - x + 1) };
149
+ }
150
+ function mobileRows(layout, fallback) {
151
+ let rows = resolve(layout.rows, "m");
152
+ for (const r of Object.values(fallback)) rows = Math.max(rows, r.y + r.h - 1);
153
+ return rows;
154
+ }
155
+ function colorValue(value) {
156
+ return /^[a-z]/.test(value) && !/^(?:oklch|oklab|rgb|rgba|hsl|hsla|lab|lch)\(/.test(value) ? `var(${colorVar(value)})` : value;
157
+ }
158
+
159
+ // src/registry.ts
160
+ var blocks = /* @__PURE__ */ new Map();
161
+ function registerBlocks(defs) {
162
+ for (const def of defs) blocks.set(def.type, def);
163
+ }
164
+ function getBlock(type) {
165
+ return blocks.get(type);
166
+ }
167
+ function listBlocks() {
168
+ return [...blocks.values()];
169
+ }
170
+ function unregisterBlocks(...types) {
171
+ if (types.length === 0) blocks.clear();
172
+ for (const t of types) blocks.delete(t);
173
+ }
174
+ var globalRegistry = { get: (type) => blocks.get(type) };
175
+
176
+ // src/block.tsx
177
+ import { Fragment, jsx } from "react/jsx-runtime";
178
+ var blockTypeClass = (type) => `os-block--${type.replace(/\//g, "-")}`;
179
+ var spaceVar2 = (t) => `var(${TOKEN_VARS2.space[t] ?? TOKEN_VARS2.space.md})`;
180
+ function alignClasses(align) {
181
+ return `os-align-${resolve2(align, "d")} os-talign-${resolve2(align, "t")} os-malign-${resolve2(align, "m")}`;
182
+ }
183
+ function blockStyleProps(style, mode) {
184
+ const vars = {};
185
+ const attrs = {};
186
+ if (!style) return { className: "", vars, attrs };
187
+ Object.assign(vars, responsiveVars("bpad", style.padding, spaceVar2));
188
+ if (style.radius) vars["--radius"] = `var(${TOKEN_VARS2.radius[style.radius]})`;
189
+ if (style.shadow) vars["--shadow"] = `var(${TOKEN_VARS2.shadow[style.shadow]})`;
190
+ const entrance = style.animation?.entrance;
191
+ if (entrance && entrance !== "none" && mode !== "editor") {
192
+ attrs["data-os-behavior"] = "reveal";
193
+ attrs["data-os-reveal"] = entrance;
194
+ }
195
+ const className = cx(
196
+ style.colorTheme && `${themeClass(style.colorTheme)} os-themed`,
197
+ style.imageTreatment && imageTreatmentClass(style.imageTreatment),
198
+ style.padding && "has-pad",
199
+ style.align && alignClasses(style.align),
200
+ style.radius && "has-radius",
201
+ style.shadow && style.shadow !== "none" && "has-shadow",
202
+ style.border && style.border !== "none" && `os-border-${style.border}`
203
+ );
204
+ return { className, vars, attrs };
205
+ }
206
+ function MissingBlock({ block, reason }) {
207
+ const editor = useEditor();
208
+ const { mode } = useRenderContext();
209
+ if (mode === "production") return null;
210
+ if (editor?.renderMissingBlock) return /* @__PURE__ */ jsx(Fragment, { children: editor.renderMissingBlock({ block, reason }) });
211
+ return /* @__PURE__ */ jsx("div", { className: "os-block-missing", role: "note", children: reason });
212
+ }
213
+ function prepare(def, raw, mode) {
214
+ if (!def) return { ok: false, reason: `Unknown block type \u201C${raw.type}\u201D` };
215
+ let block = raw;
216
+ try {
217
+ if (raw.v < def.version) block = migrateBlock(def, raw);
218
+ } catch (err) {
219
+ return { ok: false, reason: `Could not upgrade this ${def.label} block: ${String(err)}` };
220
+ }
221
+ const parsed = def.fieldsSchema.safeParse(block.fields);
222
+ if (parsed.success)
223
+ return { ok: true, def, block, fields: parsed.data };
224
+ if (mode !== "production")
225
+ return {
226
+ ok: false,
227
+ reason: `This ${def.label} block has invalid content: ${parsed.error.issues[0]?.message ?? "unknown error"}`
228
+ };
229
+ const repaired = repairFields(def, block.fields, parsed.error.issues);
230
+ const again = def.fieldsSchema.safeParse(repaired);
231
+ if (again.success) return { ok: true, def, block, fields: again.data };
232
+ return { ok: false, reason: `This ${def.label} block has invalid content` };
233
+ }
234
+ function repairFields(def, fields, issues) {
235
+ const out = { ...fields };
236
+ const defaults = def.meta.defaults.fields;
237
+ for (const issue of issues) {
238
+ const key = issue.path[0];
239
+ if (typeof key !== "string") continue;
240
+ if (defaults[key] !== void 0) out[key] = structuredClone(defaults[key]);
241
+ else delete out[key];
242
+ }
243
+ return out;
244
+ }
245
+ function RenderBlock(props) {
246
+ const { def, block, fields } = props;
247
+ try {
248
+ return /* @__PURE__ */ jsx(BlockFieldsContext.Provider, { value: { block, fields }, children: def.render({
249
+ id: block.id,
250
+ fields,
251
+ variant: block.variant ?? def.defaults.variant,
252
+ style: block.style,
253
+ block
254
+ }) });
255
+ } catch (err) {
256
+ return /* @__PURE__ */ jsx(MissingBlock, { block, reason: `This block failed to render: ${String(err)}` });
257
+ }
258
+ }
259
+ var BlockErrorBoundary = class extends Component {
260
+ state = { error: null };
261
+ static getDerivedStateFromError(err) {
262
+ return { error: String(err) };
263
+ }
264
+ render() {
265
+ if (this.state.error)
266
+ return /* @__PURE__ */ jsx(
267
+ MissingBlock,
268
+ {
269
+ block: this.props.block,
270
+ reason: `This block failed to render: ${this.state.error}`
271
+ }
272
+ );
273
+ return this.props.children;
274
+ }
275
+ };
276
+ function BlockView({ block, section }) {
277
+ const registry = useContext2(BlockRegistryContext) ?? globalRegistry;
278
+ const ctx = useRenderContext();
279
+ const editor = useEditor();
280
+ const def = registry.get(block.type);
281
+ if (block.hidden && ctx.mode !== "editor") return null;
282
+ const prepared = prepare(def, block, ctx.mode);
283
+ const content = prepared.ok ? /* @__PURE__ */ jsx(BlockErrorBoundary, { block, children: /* @__PURE__ */ jsx(RenderBlock, { def: prepared.def, block: prepared.block, fields: prepared.fields }) }) : /* @__PURE__ */ jsx(MissingBlock, { block, reason: prepared.reason });
284
+ if (!prepared.ok && ctx.mode === "production" && !editor) return null;
285
+ const variant = prepared.ok ? prepared.block.variant ?? prepared.def.defaults.variant : void 0;
286
+ const s = blockStyleProps(block.style, ctx.mode);
287
+ const el = /* @__PURE__ */ jsx(
288
+ "div",
289
+ {
290
+ className: cx(
291
+ "os-block",
292
+ blockTypeClass(block.type),
293
+ variant && `is-${variant}`,
294
+ s.className
295
+ ),
296
+ "data-os-id": block.id,
297
+ "data-os-block": block.type,
298
+ "data-os-hidden": block.hidden ? "" : void 0,
299
+ style: Object.keys(s.vars).length ? s.vars : void 0,
300
+ ...s.attrs,
301
+ children: content
302
+ }
303
+ );
304
+ return editor?.wrapBlock ? editor.wrapBlock({ block, section, children: el }) : el;
305
+ }
306
+
307
+ // src/chrome.tsx
308
+ import { themeClass as themeClass3 } from "@omniharmonic/openspace-theme";
309
+ import { useContext as useContext5 } from "react";
310
+
311
+ // src/image.tsx
312
+ import { imageTreatmentClass as imageTreatmentClass2 } from "@omniharmonic/openspace-theme";
313
+ import { createContext as createContext2, useContext as useContext3 } from "react";
314
+
315
+ // src/link.tsx
316
+ import { jsx as jsx2 } from "react/jsx-runtime";
317
+ function normalizeBasePath(basePath) {
318
+ if (!basePath) return "";
319
+ const trimmed = basePath.replace(/^\/+|\/+$/g, "");
320
+ return trimmed ? `/${trimmed}` : "";
321
+ }
322
+ function pagePath(page, basePath = "") {
323
+ const base = normalizeBasePath(basePath);
324
+ return page.slug ? `${base}/${page.slug}/` : `${base}/`;
325
+ }
326
+ function itemPath(collection, slug, basePath = "") {
327
+ return `${normalizeBasePath(basePath)}/${collection}/${slug}/`;
328
+ }
329
+ function mediaUrl(src, basePath = "") {
330
+ if (/^https?:\/\//.test(src) || src.startsWith("data:")) return src;
331
+ return `${normalizeBasePath(basePath)}/${src.replace(/^\/+/, "")}`;
332
+ }
333
+ function withBase(href, basePath = "") {
334
+ const base = normalizeBasePath(basePath);
335
+ if (!base || !href.startsWith("/") || href.startsWith("//")) return href;
336
+ if (href === base || href.startsWith(`${base}/`)) return href;
337
+ return `${base}${href}`;
338
+ }
339
+ function sectionFragment(section) {
340
+ return section.anchor ?? section.id;
341
+ }
342
+ function resolveLink(link, opts = {}) {
343
+ const base = opts.basePath ?? "";
344
+ const ok = (href, newTab = false, external = false) => ({
345
+ href,
346
+ newTab,
347
+ broken: false,
348
+ external
349
+ });
350
+ switch (link.type) {
351
+ case "page": {
352
+ const page = opts.doc?.pages[link.page];
353
+ if (opts.doc && !page) return { href: "#", newTab: false, broken: true, external: false };
354
+ const path = page ? pagePath(page, base) : `${normalizeBasePath(base)}/`;
355
+ const hash = link.anchor ? `#${link.anchor}` : "";
356
+ if (hash && link.page === opts.currentPageId) return ok(hash);
357
+ return ok(path + hash);
358
+ }
359
+ case "item": {
360
+ const exists = opts.doc ? Boolean(opts.doc.collections[link.collection]?.[link.slug]) : true;
361
+ return {
362
+ href: itemPath(link.collection, link.slug, base),
363
+ newTab: false,
364
+ broken: !exists,
365
+ external: false
366
+ };
367
+ }
368
+ case "url": {
369
+ const external = /^https?:\/\//.test(link.url);
370
+ return ok(external ? link.url : withBase(link.url, base), link.newTab ?? false, external);
371
+ }
372
+ case "email": {
373
+ const subject = link.subject ? `?subject=${encodeURIComponent(link.subject)}` : "";
374
+ return ok(`mailto:${link.email}${subject}`);
375
+ }
376
+ case "phone":
377
+ return ok(`tel:${link.phone.replace(/[^+0-9]/g, "")}`);
378
+ case "file":
379
+ return ok(mediaUrl(link.src, base), link.newTab ?? false, /^https?:\/\//.test(link.src));
380
+ case "anchor": {
381
+ if (!opts.doc) return ok(`#${link.section}`);
382
+ for (const page of Object.values(opts.doc.pages)) {
383
+ const section = page.sections.find((s) => s.id === link.section);
384
+ if (!section) continue;
385
+ const hash = `#${sectionFragment(section)}`;
386
+ return ok(page.id === opts.currentPageId ? hash : pagePath(page, base) + hash);
387
+ }
388
+ return { href: "#", newTab: false, broken: true, external: false };
389
+ }
390
+ }
391
+ }
392
+ function Link({ to, children, ...rest }) {
393
+ const ctx = useRenderContext();
394
+ const r = resolveLink(to, { doc: ctx.doc, basePath: ctx.basePath, currentPageId: ctx.page?.id });
395
+ const extra = {};
396
+ if (r.newTab) {
397
+ extra.target = "_blank";
398
+ extra.rel = "noopener";
399
+ }
400
+ if (r.broken && ctx.mode !== "production") extra["data-os-broken-link"] = "";
401
+ return /* @__PURE__ */ jsx2("a", { href: r.href, ...extra, ...rest, children });
402
+ }
403
+
404
+ // src/image.tsx
405
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
406
+ var REMOTE_WIDTHS = [480, 640, 768, 960, 1080, 1440, 1920, 2400];
407
+ function remoteSrcset(src, intrinsicWidth) {
408
+ let url;
409
+ try {
410
+ url = new URL(src);
411
+ } catch {
412
+ return void 0;
413
+ }
414
+ if (url.hostname !== "images.unsplash.com" && !url.hostname.endsWith(".imgix.net"))
415
+ return void 0;
416
+ const widths = REMOTE_WIDTHS.filter((w) => !intrinsicWidth || w <= intrinsicWidth);
417
+ if (widths.length === 0) return void 0;
418
+ return widths.map((w) => {
419
+ const u = new URL(url.toString());
420
+ u.searchParams.set("w", String(w));
421
+ if (!u.searchParams.has("auto")) u.searchParams.set("auto", "format");
422
+ if (!u.searchParams.has("fit")) u.searchParams.set("fit", "max");
423
+ return `${u.toString()} ${w}w`;
424
+ }).join(", ");
425
+ }
426
+ function resolveImage(src, opts = {}) {
427
+ const hit = opts.imageMap?.[src];
428
+ if (hit) {
429
+ return {
430
+ src: hit.src,
431
+ srcset: hit.srcset,
432
+ sources: hit.sources ?? [],
433
+ width: hit.width,
434
+ height: hit.height,
435
+ placeholder: hit.placeholder
436
+ };
437
+ }
438
+ const meta = opts.doc?.media[src];
439
+ const width = opts.width ?? meta?.width;
440
+ const height = opts.height ?? meta?.height;
441
+ return {
442
+ src: mediaUrl(src, opts.basePath),
443
+ srcset: remoteSrcset(src, width),
444
+ sources: [],
445
+ width,
446
+ height,
447
+ placeholder: meta?.placeholder
448
+ };
449
+ }
450
+ function ratio(aspect) {
451
+ if (!aspect || aspect === "original") return void 0;
452
+ const [w, h] = aspect.split(":");
453
+ return `${w} / ${h}`;
454
+ }
455
+ var pct = (n) => `${Math.round(n * 1e3) / 10}%`;
456
+ var AboveFoldContext = createContext2(null);
457
+ function Img(props) {
458
+ const ctx = useRenderContext();
459
+ const imageMap = useImageMap();
460
+ const value = typeof props.src === "string" ? void 0 : props.src;
461
+ const src = typeof props.src === "string" ? props.src : props.src.src;
462
+ const alt = props.alt ?? value?.alt ?? "";
463
+ const focal = props.focal ?? value?.focal ?? ctx.doc?.media[src]?.focal;
464
+ const r = resolveImage(src, {
465
+ imageMap,
466
+ doc: ctx.doc,
467
+ basePath: ctx.basePath,
468
+ width: props.width ?? value?.width,
469
+ height: props.height ?? value?.height
470
+ });
471
+ const sizes = props.sizes ?? "100vw";
472
+ const aspect = props.fill ? void 0 : ratio(props.aspect);
473
+ const style = {};
474
+ if (aspect) style["--aspect"] = aspect;
475
+ if (focal) style["--focal"] = `${pct(focal[0])} ${pct(focal[1])}`;
476
+ const imgStyle = r.placeholder ? { backgroundImage: `url("${r.placeholder}")` } : void 0;
477
+ const fold = useContext3(AboveFoldContext);
478
+ const high = props.priority ?? fold?.has(src) ?? false;
479
+ const eager = high;
480
+ return /* @__PURE__ */ jsx3(
481
+ "span",
482
+ {
483
+ className: cx(
484
+ "os-img",
485
+ props.treatment && imageTreatmentClass2(props.treatment),
486
+ aspect && "has-aspect",
487
+ props.fit === "contain" && "is-contain",
488
+ props.fill && "is-fill",
489
+ !props.fill && !aspect && (r.width === void 0 || r.height === void 0) && "is-unsized",
490
+ props.className
491
+ ),
492
+ style: Object.keys(style).length ? style : void 0,
493
+ children: /* @__PURE__ */ jsxs("picture", { children: [
494
+ r.sources.map((s) => /* @__PURE__ */ jsx3("source", { type: s.type, srcSet: s.srcset, sizes }, s.type)),
495
+ /* @__PURE__ */ jsx3(
496
+ "img",
497
+ {
498
+ className: cx("os-img__img", r.placeholder && "has-placeholder"),
499
+ src: r.src,
500
+ srcSet: r.srcset,
501
+ sizes: r.srcset || r.sources.length ? sizes : void 0,
502
+ width: r.width,
503
+ height: r.height,
504
+ alt,
505
+ loading: eager ? "eager" : "lazy",
506
+ decoding: high ? "sync" : "async",
507
+ fetchPriority: high ? "high" : void 0,
508
+ style: imgStyle
509
+ }
510
+ )
511
+ ] })
512
+ }
513
+ );
514
+ }
515
+
516
+ // src/primitives.tsx
517
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
518
+ function Button({
519
+ to,
520
+ role = "primary",
521
+ type = "button",
522
+ className,
523
+ children,
524
+ attrs
525
+ }) {
526
+ const cls = cx("os-button", `os-button--${role}`, className);
527
+ const label = /* @__PURE__ */ jsx4("span", { className: "os-button__label", children });
528
+ if (to)
529
+ return /* @__PURE__ */ jsx4(Link, { to, className: cls, ...attrs, children: label });
530
+ return /* @__PURE__ */ jsx4("button", { type, className: cls, ...attrs, children: label });
531
+ }
532
+ var ICONS = {
533
+ instagram: /* @__PURE__ */ jsxs2(Fragment2, { children: [
534
+ /* @__PURE__ */ jsx4("rect", { x: "3", y: "3", width: "18", height: "18", rx: "5" }),
535
+ /* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "4" }),
536
+ /* @__PURE__ */ jsx4("circle", { cx: "17.5", cy: "6.5", r: "0.6", fill: "currentColor" })
537
+ ] }),
538
+ facebook: /* @__PURE__ */ jsx4("path", { d: "M14 8h3V4h-3a4 4 0 0 0-4 4v3H7v4h3v7h4v-7h3l1-4h-4V8z" }),
539
+ x: /* @__PURE__ */ jsx4("path", { d: "M4 4l16 16M20 4L4 20" }),
540
+ threads: /* @__PURE__ */ jsx4("path", { d: "M16 11c-1-4-8-4-8 1s7 5 7 1-5-3-5-1M12 21a9 9 0 1 1 8-12" }),
541
+ bluesky: /* @__PURE__ */ jsx4("path", { d: "M12 11C10 7 6 4 4 5s0 7 4 8c-3 1-3 4 0 5s4-3 4-5c0 2 1 6 4 5s3-4 0-5c4-1 6-7 4-8s-6 2-8 6z" }),
542
+ linkedin: /* @__PURE__ */ jsxs2(Fragment2, { children: [
543
+ /* @__PURE__ */ jsx4("rect", { x: "3", y: "3", width: "18", height: "18", rx: "3" }),
544
+ /* @__PURE__ */ jsx4("path", { d: "M8 10v7M8 7v.01M12 17v-4a2 2 0 0 1 4 0v4M12 10v7" })
545
+ ] }),
546
+ youtube: /* @__PURE__ */ jsxs2(Fragment2, { children: [
547
+ /* @__PURE__ */ jsx4("rect", { x: "2", y: "5", width: "20", height: "14", rx: "4" }),
548
+ /* @__PURE__ */ jsx4("path", { d: "M10 9l5 3-5 3z", fill: "currentColor" })
549
+ ] }),
550
+ tiktok: /* @__PURE__ */ jsx4("path", { d: "M14 3v11a4 4 0 1 1-4-4M14 3c0 3 2 5 5 5" }),
551
+ email: /* @__PURE__ */ jsxs2(Fragment2, { children: [
552
+ /* @__PURE__ */ jsx4("rect", { x: "3", y: "5", width: "18", height: "14", rx: "2" }),
553
+ /* @__PURE__ */ jsx4("path", { d: "M3 7l9 6 9-6" })
554
+ ] }),
555
+ github: /* @__PURE__ */ jsx4("path", { d: "M9 19c-4 1.5-4-2-6-2m12 4v-3.5c0-1 .1-1.4-.5-2 2.8-.3 5.5-1.4 5.5-6a4.6 4.6 0 0 0-1.3-3.2 4.2 4.2 0 0 0-.1-3.2s-1.1-.3-3.5 1.3a12 12 0 0 0-6.2 0C6.5 2.8 5.4 3.1 5.4 3.1a4.2 4.2 0 0 0-.1 3.2A4.6 4.6 0 0 0 4 9.5c0 4.6 2.7 5.7 5.5 6-.6.6-.6 1.2-.5 2V21" })
556
+ };
557
+ var GENERIC_ICON = /* @__PURE__ */ jsxs2(Fragment2, { children: [
558
+ /* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "9" }),
559
+ /* @__PURE__ */ jsx4("path", { d: "M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18" })
560
+ ] });
561
+ var NETWORK_LABEL = {
562
+ x: "X",
563
+ linkedin: "LinkedIn",
564
+ youtube: "YouTube",
565
+ tiktok: "TikTok",
566
+ github: "GitHub",
567
+ soundcloud: "SoundCloud",
568
+ tripadvisor: "Tripadvisor"
569
+ };
570
+ function networkLabel(network) {
571
+ return NETWORK_LABEL[network] ?? network.charAt(0).toUpperCase() + network.slice(1);
572
+ }
573
+ function SocialIcon({ network }) {
574
+ return /* @__PURE__ */ jsx4(
575
+ "svg",
576
+ {
577
+ className: "os-icon",
578
+ viewBox: "0 0 24 24",
579
+ width: "20",
580
+ height: "20",
581
+ fill: "none",
582
+ stroke: "currentColor",
583
+ strokeWidth: "1.6",
584
+ strokeLinecap: "round",
585
+ strokeLinejoin: "round",
586
+ "aria-hidden": "true",
587
+ focusable: "false",
588
+ children: ICONS[network] ?? GENERIC_ICON
589
+ }
590
+ );
591
+ }
592
+ function SocialLinks({
593
+ profiles,
594
+ className
595
+ }) {
596
+ if (profiles.length === 0) return null;
597
+ return /* @__PURE__ */ jsx4("ul", { className: cx("os-social", className), children: profiles.map((p) => /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsxs2(
598
+ "a",
599
+ {
600
+ href: p.url,
601
+ className: "os-social__link",
602
+ rel: "me noopener",
603
+ target: p.network === "email" ? void 0 : "_blank",
604
+ children: [
605
+ /* @__PURE__ */ jsx4(SocialIcon, { network: p.network }),
606
+ /* @__PURE__ */ jsx4("span", { className: "os-visually-hidden", children: networkLabel(p.network) })
607
+ ]
608
+ }
609
+ ) }, p.url)) });
610
+ }
611
+
612
+ // src/section.tsx
613
+ import { themeClass as themeClass2 } from "@omniharmonic/openspace-theme";
614
+ import { createContext as createContext3, useContext as useContext4 } from "react";
615
+
616
+ // src/image-map.ts
617
+ function collectImageSrcs(value, out = /* @__PURE__ */ new Set()) {
618
+ if (typeof value === "string") {
619
+ if (/^media\/.+\.(?:jpe?g|png|webp|avif|gif|svg)$/i.test(value) || isRemoteImage(value))
620
+ out.add(value);
621
+ } else if (Array.isArray(value)) {
622
+ for (const v of value) collectImageSrcs(v, out);
623
+ } else if (value && typeof value === "object") {
624
+ for (const v of Object.values(value)) collectImageSrcs(v, out);
625
+ }
626
+ return out;
627
+ }
628
+ function isRemoteImage(value) {
629
+ return /^https:\/\/images\.unsplash\.com\//.test(value) || /^https:\/\/\S+\.(?:jpe?g|png|webp|avif|gif)(?:\?\S*)?$/i.test(value);
630
+ }
631
+
632
+ // src/section.tsx
633
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
634
+ var FirstSectionContext = createContext3(false);
635
+ function overlayVars(overlay) {
636
+ if (!overlay) return void 0;
637
+ return { "--overlay": colorValue(overlay.color), "--overlay-opacity": overlay.opacity };
638
+ }
639
+ function backgroundVars(bg) {
640
+ if (!bg) return {};
641
+ if (bg.type === "color") return { "--bg": colorValue(bg.color) };
642
+ if (bg.type === "gradient") {
643
+ const stops = [bg.from, bg.via, bg.to].filter(Boolean).map((c) => colorValue(c));
644
+ return { "--bg": `linear-gradient(${bg.angle ?? 180}deg, ${stops.join(", ")})` };
645
+ }
646
+ return {};
647
+ }
648
+ function SectionBackgroundLayer({
649
+ background
650
+ }) {
651
+ const { basePath } = useRenderContext();
652
+ const first = useContext4(FirstSectionContext);
653
+ if (!background) return null;
654
+ if (background.type === "image") {
655
+ return /* @__PURE__ */ jsxs3(
656
+ "div",
657
+ {
658
+ className: cx("os-section__bg", background.parallax && "is-parallax"),
659
+ "aria-hidden": "true",
660
+ children: [
661
+ /* @__PURE__ */ jsx5(
662
+ Img,
663
+ {
664
+ src: background.src,
665
+ alt: "",
666
+ sizes: "100vw",
667
+ treatment: "none",
668
+ fill: true,
669
+ ...background.focal ? { focal: background.focal } : {},
670
+ priority: first,
671
+ className: "os-section__media"
672
+ }
673
+ ),
674
+ background.overlay && /* @__PURE__ */ jsx5("div", { className: "os-section__overlay", style: overlayVars(background.overlay) })
675
+ ]
676
+ }
677
+ );
678
+ }
679
+ if (background.type === "video") {
680
+ const focal = background.focal;
681
+ return /* @__PURE__ */ jsxs3("div", { className: "os-section__bg", "aria-hidden": "true", children: [
682
+ /* @__PURE__ */ jsx5(
683
+ "video",
684
+ {
685
+ className: "os-section__media os-section__video",
686
+ "data-os-behavior": "video-lite",
687
+ "data-os-autoplay": "",
688
+ muted: true,
689
+ loop: true,
690
+ playsInline: true,
691
+ preload: "none",
692
+ poster: background.poster ? mediaUrl(background.poster, basePath) : void 0,
693
+ style: focal ? { objectPosition: `${focal[0] * 100}% ${focal[1] * 100}%` } : void 0,
694
+ children: /* @__PURE__ */ jsx5("source", { src: mediaUrl(background.src, basePath) })
695
+ }
696
+ ),
697
+ background.overlay && /* @__PURE__ */ jsx5("div", { className: "os-section__overlay", style: overlayVars(background.overlay) })
698
+ ] });
699
+ }
700
+ return null;
701
+ }
702
+ function StackBody({ section }) {
703
+ const { layout } = section;
704
+ const { mode } = useRenderContext();
705
+ const cols = blocksByColumn(
706
+ layout,
707
+ section.blocks.filter((b) => !b.hidden || mode === "editor")
708
+ );
709
+ return /* @__PURE__ */ jsx5(
710
+ "div",
711
+ {
712
+ className: cx(
713
+ "os-section__inner os-stack",
714
+ `os-w-${layout.width ?? "content"}`,
715
+ layout.align && `is-align-${layout.align}`,
716
+ cols.length > 1 && "is-multi"
717
+ ),
718
+ style: stackVars(layout),
719
+ children: cols.map((blocks2, i) => {
720
+ const vars = stackColumnVars(layout, i);
721
+ return /* @__PURE__ */ jsx5(
722
+ "div",
723
+ {
724
+ className: "os-col",
725
+ "data-os-col": i,
726
+ style: Object.keys(vars).length ? vars : void 0,
727
+ children: blocks2.map((b) => /* @__PURE__ */ jsx5(BlockView, { block: b, section }, b.id))
728
+ },
729
+ i
730
+ );
731
+ })
732
+ }
733
+ );
734
+ }
735
+ function GridBody({ section }) {
736
+ const { layout } = section;
737
+ const { mode } = useRenderContext();
738
+ const fallback = fallbackMobileRects(layout);
739
+ const vars = { ...gridVars(layout), "--mrows": mobileRows(layout, fallback) };
740
+ const ordered = section.blocks.filter((b) => !b.hidden || mode === "editor").sort((a, b) => {
741
+ const ra = layout.items[a.id]?.d;
742
+ const rb = layout.items[b.id]?.d;
743
+ if (!ra || !rb) return 0;
744
+ return ra.y - rb.y || ra.x - rb.x;
745
+ });
746
+ return /* @__PURE__ */ jsx5(
747
+ "div",
748
+ {
749
+ className: cx("os-section__inner os-grid", `os-w-${layout.width ?? "content"}`),
750
+ style: vars,
751
+ children: ordered.map(
752
+ (b) => layout.items[b.id] ? /* @__PURE__ */ jsx5(
753
+ "div",
754
+ {
755
+ className: "os-grid__item",
756
+ "data-os-item": b.id,
757
+ style: gridItemVars(layout, b.id, fallback),
758
+ children: /* @__PURE__ */ jsx5(BlockView, { block: b, section })
759
+ },
760
+ b.id
761
+ ) : mode !== "production" ? /* @__PURE__ */ jsx5("div", { className: "os-grid__item os-grid__item--unplaced", "data-os-unplaced": b.id, children: /* @__PURE__ */ jsx5(MissingBlock, { block: b, reason: "This block has no position in the grid" }) }, b.id) : null
762
+ )
763
+ }
764
+ );
765
+ }
766
+ function leadImages(section, count = 2) {
767
+ const layout = section.layout;
768
+ const order = (bp) => layout.mode === "stack" ? [...section.blocks].sort((a, b) => (a.column ?? 0) - (b.column ?? 0)) : section.blocks.map((b, i) => {
769
+ const item = layout.items[b.id];
770
+ return { b, i, r: bp === "m" ? item?.m ?? item?.d : item?.d };
771
+ }).sort(
772
+ (p, q) => p.r && q.r ? p.r.y - q.r.y || p.r.x - q.r.x || p.i - q.i : p.r ? -1 : q.r ? 1 : p.i - q.i
773
+ ).map((p) => p.b);
774
+ const images = (blocks2) => {
775
+ const srcs = /* @__PURE__ */ new Set();
776
+ for (const b of blocks2) collectImageSrcs(b.fields, srcs);
777
+ return [...srcs];
778
+ };
779
+ const desktop = images(order("d"));
780
+ const phone = layout.mode === "grid" ? images(order("m")) : desktop;
781
+ const out = [];
782
+ const bg = section.style.background;
783
+ if (bg?.type === "image" && bg.src) out.push(bg.src);
784
+ for (const src of [phone[0], desktop[0], ...desktop])
785
+ if (src && !out.includes(src) && out.length < count) out.push(src);
786
+ return new Set(out);
787
+ }
788
+ function SectionView({ section, as: Tag = "section", className }) {
789
+ const { mode } = useRenderContext();
790
+ const editor = useEditor();
791
+ const first = useContext4(FirstSectionContext);
792
+ if (section.hidden && mode !== "editor") return null;
793
+ const { style, layout } = section;
794
+ const vars = { ...sectionStyleVars(style), ...backgroundVars(style.background) };
795
+ const bgType = style.background?.type ?? "none";
796
+ const entrance = style.animation?.entrance;
797
+ const reveal = entrance && entrance !== "none" && mode !== "editor" ? {
798
+ "data-os-behavior": "reveal",
799
+ "data-os-reveal": entrance,
800
+ ...style.animation?.stagger ? { "data-os-stagger": "" } : {}
801
+ } : {};
802
+ const el = /* @__PURE__ */ jsx5(
803
+ Tag,
804
+ {
805
+ id: sectionFragment(section),
806
+ className: cx(
807
+ "os-section",
808
+ themeClass2(style.colorTheme ?? "light"),
809
+ `os-section--${layout.mode}`,
810
+ bgType !== "none" && `has-bg-${bgType}`,
811
+ style.minHeight && "has-minh",
812
+ style.padding && "has-pad",
813
+ first && "os-section--first",
814
+ className
815
+ ),
816
+ "data-os-id": section.id,
817
+ "data-os-section": "",
818
+ "data-os-hidden": section.hidden ? "" : void 0,
819
+ style: Object.keys(vars).length ? vars : void 0,
820
+ ...reveal,
821
+ children: /* @__PURE__ */ jsx5(SectionContext.Provider, { value: section, children: /* @__PURE__ */ jsxs3(AboveFoldContext.Provider, { value: first ? leadImages(section) : null, children: [
822
+ /* @__PURE__ */ jsx5(SectionBackgroundLayer, { background: style.background }),
823
+ layout.mode === "stack" ? /* @__PURE__ */ jsx5(StackBody, { section }) : /* @__PURE__ */ jsx5(GridBody, { section })
824
+ ] }) })
825
+ }
826
+ );
827
+ return editor?.wrapSection ? editor.wrapSection({ section, children: el }) : el;
828
+ }
829
+
830
+ // src/chrome.tsx
831
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
832
+ function shortHash(text) {
833
+ let h = 2166136261;
834
+ for (let i = 0; i < text.length; i++) {
835
+ h ^= text.charCodeAt(i);
836
+ h = Math.imul(h, 16777619);
837
+ }
838
+ return (h >>> 0).toString(36);
839
+ }
840
+ function CloseIcon() {
841
+ return /* @__PURE__ */ jsx6("svg", { viewBox: "0 0 24 24", width: "20", height: "20", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx6(
842
+ "path",
843
+ {
844
+ d: "M6 6l12 12M18 6L6 18",
845
+ fill: "none",
846
+ stroke: "currentColor",
847
+ strokeWidth: "1.6",
848
+ strokeLinecap: "round"
849
+ }
850
+ ) });
851
+ }
852
+ function AnnouncementBar({
853
+ announcement
854
+ }) {
855
+ if (!announcement?.enabled) return null;
856
+ const dismissible = announcement.dismissible ?? true;
857
+ return /* @__PURE__ */ jsxs4(
858
+ "aside",
859
+ {
860
+ className: cx("os-announcement", themeClass3(announcement.colorTheme ?? "dark-accent")),
861
+ "aria-label": "Announcement",
862
+ "data-os-behavior": dismissible ? "announcement" : void 0,
863
+ "data-os-key": shortHash(announcement.message),
864
+ children: [
865
+ /* @__PURE__ */ jsx6("p", { className: "os-announcement__text", children: announcement.link ? /* @__PURE__ */ jsx6(Link, { to: announcement.link, className: "os-announcement__link", children: announcement.message }) : announcement.message }),
866
+ dismissible && /* @__PURE__ */ jsxs4("button", { type: "button", className: "os-announcement__close", "data-os-dismiss": "", children: [
867
+ /* @__PURE__ */ jsx6(CloseIcon, {}),
868
+ /* @__PURE__ */ jsx6("span", { className: "os-visually-hidden", children: "Dismiss announcement" })
869
+ ] })
870
+ ]
871
+ }
872
+ );
873
+ }
874
+ function Footer({ site, children }) {
875
+ return /* @__PURE__ */ jsxs4("footer", { className: "os-footer", children: [
876
+ /* @__PURE__ */ jsx6(SectionView, { section: site.footer, as: "div", className: "os-footer__section" }),
877
+ children
878
+ ] });
879
+ }
880
+ function Popup({
881
+ id,
882
+ trigger,
883
+ frequencyDays = 7,
884
+ title,
885
+ colorTheme = "light",
886
+ children
887
+ }) {
888
+ const triggerAttrs = { "data-os-trigger": trigger.type };
889
+ if (trigger.type === "time") triggerAttrs["data-os-delay"] = String(trigger.seconds);
890
+ if (trigger.type === "scroll") triggerAttrs["data-os-scroll"] = String(trigger.percent);
891
+ const titleId = `${id}-title`;
892
+ return /* @__PURE__ */ jsxs4(
893
+ "dialog",
894
+ {
895
+ id,
896
+ className: cx("os-popup", themeClass3(colorTheme)),
897
+ "aria-labelledby": titleId,
898
+ "data-os-behavior": "popup",
899
+ "data-os-frequency": String(frequencyDays),
900
+ ...triggerAttrs,
901
+ children: [
902
+ /* @__PURE__ */ jsxs4("div", { className: "os-popup__body", children: [
903
+ /* @__PURE__ */ jsx6("h2", { id: titleId, className: "os-popup__title os-type-h3", children: title }),
904
+ children
905
+ ] }),
906
+ /* @__PURE__ */ jsxs4("button", { type: "button", className: "os-popup__close", "data-os-close": "", children: [
907
+ /* @__PURE__ */ jsx6(CloseIcon, {}),
908
+ /* @__PURE__ */ jsx6("span", { className: "os-visually-hidden", children: "Close" })
909
+ ] })
910
+ ]
911
+ }
912
+ );
913
+ }
914
+ function popupTrigger(popup) {
915
+ if (popup.trigger === "scroll") return { type: "scroll", percent: popup.scrollPercent ?? 50 };
916
+ if (popup.trigger === "exit-intent") return { type: "exit" };
917
+ return { type: "time", seconds: popup.delaySeconds ?? 8 };
918
+ }
919
+ var POPUP_NEWSLETTER_ID = "blk_popupnews0";
920
+ function SitePopup({ popup }) {
921
+ const registry = useContext5(BlockRegistryContext) ?? globalRegistry;
922
+ if (!popup?.enabled) return null;
923
+ const newsletter = popup.newsletter && registry.get("newsletter") ? {
924
+ id: POPUP_NEWSLETTER_ID,
925
+ type: "newsletter",
926
+ v: 1,
927
+ variant: "stacked",
928
+ fields: {
929
+ askName: false,
930
+ placeholder: "you@example.com",
931
+ buttonLabel: "Subscribe",
932
+ successMessage: "Almost there \u2014 check your inbox to confirm your subscription."
933
+ }
934
+ } : null;
935
+ return /* @__PURE__ */ jsxs4(
936
+ Popup,
937
+ {
938
+ id: `os-popup-${shortHash(popup.heading)}`,
939
+ trigger: popupTrigger(popup),
940
+ frequencyDays: popup.frequencyDays,
941
+ title: popup.heading,
942
+ colorTheme: popup.colorTheme ?? "light",
943
+ children: [
944
+ popup.image ? /* @__PURE__ */ jsx6(Img, { src: popup.image, className: "os-popup__image", sizes: "34rem", aspect: "16:9" }) : null,
945
+ popup.body ? /* @__PURE__ */ jsx6("p", { className: "os-popup__text", children: popup.body }) : null,
946
+ popup.cta ? /* @__PURE__ */ jsx6("div", { className: "os-popup__actions", children: /* @__PURE__ */ jsx6(Button, { to: popup.cta.link, children: popup.cta.label }) }) : null,
947
+ newsletter ? /* @__PURE__ */ jsx6(BlockView, { block: newsletter }) : null
948
+ ]
949
+ }
950
+ );
951
+ }
952
+
953
+ // src/collections.tsx
954
+ import { createContext as createContext4, useContext as useContext6 } from "react";
955
+
956
+ // src/recurrence.ts
957
+ var DAYS = ["SU", "MO", "TU", "WE", "TH", "FR", "SA"];
958
+ var WEEK = ["MO", "TU", "WE", "TH", "FR", "SA", "SU"];
959
+ var DAY_MS = 864e5;
960
+ function parseRecurrence(rule) {
961
+ const map = /* @__PURE__ */ new Map();
962
+ for (const part of rule.replace(/^RRULE:/i, "").split(";")) {
963
+ const [k, v] = part.split("=");
964
+ if (k && v) map.set(k.toUpperCase(), v.toUpperCase());
965
+ }
966
+ const freq = map.get("FREQ");
967
+ if (freq !== "DAILY" && freq !== "WEEKLY" && freq !== "MONTHLY" && freq !== "YEARLY") return null;
968
+ const interval = Math.max(1, Number(map.get("INTERVAL") ?? 1) || 1);
969
+ const out = { freq, interval, byDay: [] };
970
+ for (const d of (map.get("BYDAY") ?? "").split(",").filter(Boolean)) {
971
+ const m = /^([+-]?\d)?(MO|TU|WE|TH|FR|SA|SU)$/.exec(d);
972
+ if (!m) continue;
973
+ if (m[1] && freq === "MONTHLY") out.nth = { n: Number(m[1]), day: m[2] };
974
+ else out.byDay.push(m[2]);
975
+ }
976
+ const bmd = Number(map.get("BYMONTHDAY"));
977
+ if (Number.isInteger(bmd) && bmd > 0) out.byMonthDay = bmd;
978
+ const count = Number(map.get("COUNT"));
979
+ if (Number.isInteger(count) && count > 0) out.count = count;
980
+ const until = map.get("UNTIL");
981
+ if (until && /^\d{8}/.test(until))
982
+ out.until = `${until.slice(0, 4)}-${until.slice(4, 6)}-${until.slice(6, 8)}`;
983
+ return out;
984
+ }
985
+ var utc = (y, m, d) => new Date(Date.UTC(y, m - 1, d));
986
+ var iso = (d) => d.toISOString().slice(0, 10);
987
+ function occurrenceDates(rule, start, options = {}) {
988
+ const r = typeof rule === "string" ? parseRecurrence(rule) : rule;
989
+ const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(start);
990
+ if (!m) return [];
991
+ const y = Number(m[1]);
992
+ const mo = Number(m[2]);
993
+ const d = Number(m[3]);
994
+ const first = utc(y, mo, d);
995
+ const from = options.from ?? iso(first);
996
+ const max = options.max ?? 500;
997
+ if (!r) return iso(first) >= from && max > 0 ? [iso(first)] : [];
998
+ const last = [r.until, options.through].filter(Boolean).sort()[0];
999
+ const over = (date) => last ? iso(date) > last : false;
1000
+ const out = [];
1001
+ const seen = /* @__PURE__ */ new Set();
1002
+ let total = 0;
1003
+ const take = (date) => {
1004
+ const s = iso(date);
1005
+ if (date < first || seen.has(s)) return;
1006
+ seen.add(s);
1007
+ total += 1;
1008
+ if (s >= from) out.push(s);
1009
+ };
1010
+ const done = () => out.length >= max || r.count !== void 0 && total >= r.count;
1011
+ let i0 = 0;
1012
+ if (r.count === void 0 && from > iso(first)) {
1013
+ const f = /* @__PURE__ */ new Date(`${from}T00:00:00Z`);
1014
+ const days = Math.floor((f.getTime() - first.getTime()) / DAY_MS);
1015
+ const months = (f.getUTCFullYear() - y) * 12 + (f.getUTCMonth() + 1 - mo);
1016
+ const periods = r.freq === "DAILY" ? days / r.interval : r.freq === "WEEKLY" ? days / (7 * r.interval) : r.freq === "MONTHLY" ? months / r.interval : (f.getUTCFullYear() - y) / r.interval;
1017
+ i0 = Math.max(0, Math.floor(periods) - 1);
1018
+ }
1019
+ if (i0 === 0) take(first);
1020
+ for (let i = i0; !done() && i < i0 + 2e4; i++) {
1021
+ const batch = [];
1022
+ if (r.freq === "DAILY") batch.push(utc(y, mo, d + i * r.interval));
1023
+ else if (r.freq === "WEEKLY") {
1024
+ const monday = utc(y, mo, d - (first.getUTCDay() + 6) % 7 + i * 7 * r.interval);
1025
+ const days = r.byDay.length ? r.byDay : [DAYS[first.getUTCDay()]];
1026
+ for (const day of WEEK)
1027
+ if (days.includes(day))
1028
+ batch.push(
1029
+ utc(
1030
+ monday.getUTCFullYear(),
1031
+ monday.getUTCMonth() + 1,
1032
+ monday.getUTCDate() + WEEK.indexOf(day)
1033
+ )
1034
+ );
1035
+ } else if (r.freq === "MONTHLY") {
1036
+ const month = utc(y, mo + i * r.interval, 1);
1037
+ if (r.nth) {
1038
+ const want = DAYS.indexOf(r.nth.day);
1039
+ if (r.nth.n > 0) {
1040
+ const offset = (want - month.getUTCDay() + 7) % 7;
1041
+ const date = utc(
1042
+ month.getUTCFullYear(),
1043
+ month.getUTCMonth() + 1,
1044
+ 1 + offset + (r.nth.n - 1) * 7
1045
+ );
1046
+ if (date.getUTCMonth() === month.getUTCMonth()) batch.push(date);
1047
+ } else {
1048
+ const end = utc(month.getUTCFullYear(), month.getUTCMonth() + 2, 0);
1049
+ const back = (end.getUTCDay() - want + 7) % 7;
1050
+ batch.push(utc(end.getUTCFullYear(), end.getUTCMonth() + 1, end.getUTCDate() - back));
1051
+ }
1052
+ } else {
1053
+ const day = r.byMonthDay ?? d;
1054
+ const date = utc(month.getUTCFullYear(), month.getUTCMonth() + 1, day);
1055
+ if (date.getUTCDate() === day) batch.push(date);
1056
+ }
1057
+ } else {
1058
+ const date = utc(y + i * r.interval, mo, d);
1059
+ if (date.getUTCDate() === d) batch.push(date);
1060
+ }
1061
+ let stop = false;
1062
+ for (const date of batch) {
1063
+ if (done()) break;
1064
+ if (over(date)) {
1065
+ stop = true;
1066
+ break;
1067
+ }
1068
+ take(date);
1069
+ }
1070
+ if (stop) break;
1071
+ }
1072
+ return out;
1073
+ }
1074
+ function zoneOffsetMinutes(timeZone, at) {
1075
+ try {
1076
+ const parts = new Intl.DateTimeFormat("en-US", {
1077
+ timeZone,
1078
+ hourCycle: "h23",
1079
+ year: "numeric",
1080
+ month: "2-digit",
1081
+ day: "2-digit",
1082
+ hour: "2-digit",
1083
+ minute: "2-digit",
1084
+ second: "2-digit"
1085
+ }).formatToParts(new Date(at));
1086
+ const n = (t) => Number(parts.find((p) => p.type === t)?.value);
1087
+ const wall = Date.UTC(n("year"), n("month") - 1, n("day"), n("hour"), n("minute"), n("second"));
1088
+ return Math.round((wall - at) / 6e4);
1089
+ } catch {
1090
+ return null;
1091
+ }
1092
+ }
1093
+ var fmtOffset = (min) => {
1094
+ const sign = min < 0 ? "-" : "+";
1095
+ const a = Math.abs(min);
1096
+ return `${sign}${String(Math.floor(a / 60)).padStart(2, "0")}:${String(a % 60).padStart(2, "0")}`;
1097
+ };
1098
+ function shiftDateTime(value, days, timeZone) {
1099
+ const day = new Date(Date.parse(`${value.slice(0, 10)}T00:00:00Z`) + days * DAY_MS).toISOString().slice(0, 10);
1100
+ const m = /^\d{4}-\d{2}-\d{2}T(\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?)(Z|[+-]\d{2}:\d{2})?$/.exec(
1101
+ value
1102
+ );
1103
+ if (!m) return `${day}${value.slice(10)}`;
1104
+ const time2 = m[1];
1105
+ if (!timeZone) return `${day}T${time2}${m[2] ?? ""}`;
1106
+ const wall = Date.parse(`${day}T${time2.length === 5 ? `${time2}:00` : time2}Z`);
1107
+ let off = zoneOffsetMinutes(timeZone, wall);
1108
+ if (off === null) return `${day}T${time2}${m[2] ?? ""}`;
1109
+ off = zoneOffsetMinutes(timeZone, wall - off * 6e4) ?? off;
1110
+ return `${day}T${time2}${fmtOffset(off)}`;
1111
+ }
1112
+ function eventOn(item, date) {
1113
+ const fm = item.frontmatter;
1114
+ const days = Math.round(
1115
+ (Date.parse(`${date}T00:00:00Z`) - Date.parse(`${fm.start.slice(0, 10)}T00:00:00Z`)) / DAY_MS
1116
+ );
1117
+ const move = (v) => shiftDateTime(v, days, fm.timezone);
1118
+ return {
1119
+ ...item,
1120
+ frontmatter: { ...fm, start: move(fm.start), ...fm.end ? { end: move(fm.end) } : {} }
1121
+ };
1122
+ }
1123
+ function eventLengthDays(item) {
1124
+ if (item.kind !== "events" || !item.frontmatter.end) return 0;
1125
+ const fm = item.frontmatter;
1126
+ return Math.max(
1127
+ 0,
1128
+ Math.round(
1129
+ (Date.parse(`${fm.end.slice(0, 10)}T00:00:00Z`) - Date.parse(`${fm.start.slice(0, 10)}T00:00:00Z`)) / DAY_MS
1130
+ )
1131
+ );
1132
+ }
1133
+ function expandOccurrences(items, window) {
1134
+ const out = [];
1135
+ for (const item of items) {
1136
+ if (item.kind !== "events" || !item.frontmatter.recurrence) {
1137
+ out.push(item);
1138
+ continue;
1139
+ }
1140
+ const fm = item.frontmatter;
1141
+ const lengthDays = eventLengthDays(item);
1142
+ const fromStart = iso(new Date(Date.parse(`${window.from}T00:00:00Z`) - lengthDays * DAY_MS));
1143
+ const per = window.perEvent ?? 12;
1144
+ const all = occurrenceDates(fm.recurrence, fm.start, {
1145
+ from: fromStart,
1146
+ through: window.through,
1147
+ max: window.newest ? 2e3 : per
1148
+ });
1149
+ const dates = window.newest ? all.slice(-per) : all;
1150
+ for (const date of dates) out.push(eventOn(item, date));
1151
+ }
1152
+ return out;
1153
+ }
1154
+ function itemKey(item) {
1155
+ return item.kind === "events" && item.frontmatter.recurrence ? `${item.slug}@${item.frontmatter.start}` : item.slug;
1156
+ }
1157
+
1158
+ // src/collections.tsx
1159
+ var CollectionsContext = createContext4(void 0);
1160
+ function feedKey(network, handle) {
1161
+ return `${network}:${handle.replace(/^@/, "")}`.toLowerCase();
1162
+ }
1163
+ function useCollections() {
1164
+ const ctx = useRenderContext();
1165
+ const value = useContext6(CollectionsContext);
1166
+ return {
1167
+ collections: value?.collections ?? ctx.doc?.collections ?? {},
1168
+ configs: value?.configs ?? ctx.doc?.collectionConfigs ?? {},
1169
+ now: value?.now === void 0 ? /* @__PURE__ */ new Date() : new Date(value.now),
1170
+ includeDrafts: value?.includeDrafts ?? ctx.mode !== "production",
1171
+ feeds: value?.feeds ?? {}
1172
+ };
1173
+ }
1174
+ function itemDate(item) {
1175
+ const fm = item.frontmatter;
1176
+ return item.kind === "events" ? fm.start : fm.date;
1177
+ }
1178
+ function isLive(item, now) {
1179
+ const fm = item.frontmatter;
1180
+ if (fm.status === "published") return true;
1181
+ if (fm.status === "scheduled" && fm.publishAt) return Date.parse(fm.publishAt) <= now.getTime();
1182
+ return false;
1183
+ }
1184
+ function todayIn(now, timeZone) {
1185
+ for (const tz of [timeZone, "UTC"]) {
1186
+ try {
1187
+ return new Intl.DateTimeFormat("en-CA", {
1188
+ timeZone: tz ?? "UTC",
1189
+ year: "numeric",
1190
+ month: "2-digit",
1191
+ day: "2-digit"
1192
+ }).format(now);
1193
+ } catch {
1194
+ }
1195
+ }
1196
+ return now.toISOString().slice(0, 10);
1197
+ }
1198
+ function isUpcoming(item, now) {
1199
+ if (item.kind !== "events") return true;
1200
+ const fm = item.frontmatter;
1201
+ const last = fm.end ?? fm.start;
1202
+ if (/^\d{4}-\d{2}-\d{2}$/.test(last)) return last >= todayIn(now, fm.timezone);
1203
+ return Date.parse(last) >= now.getTime();
1204
+ }
1205
+ var time = (item) => {
1206
+ const d = itemDate(item);
1207
+ return d ? Date.parse(d) : 0;
1208
+ };
1209
+ var isoDay = (d) => d.toISOString().slice(0, 10);
1210
+ var plusDays = (d, n) => isoDay(new Date(d.getTime() + n * 864e5));
1211
+ var RECURRENCE_HORIZON_DAYS = 366;
1212
+ function nextOccurrence(item, now) {
1213
+ if (item.kind !== "events" || !item.frontmatter.recurrence) return item;
1214
+ const rule = item.frontmatter.recurrence;
1215
+ const start = item.frontmatter.start;
1216
+ const soon = occurrenceDates(rule, start, {
1217
+ from: plusDays(now, -(eventLengthDays(item) + 1)),
1218
+ through: plusDays(now, RECURRENCE_HORIZON_DAYS),
1219
+ max: 4
1220
+ });
1221
+ const upcoming = soon.map((d) => eventOn(item, d)).find((e) => isUpcoming(e, now));
1222
+ if (upcoming) return upcoming;
1223
+ for (let back = 1; back <= 50; back++) {
1224
+ const past = occurrenceDates(rule, start, {
1225
+ from: plusDays(now, -RECURRENCE_HORIZON_DAYS * back),
1226
+ through: isoDay(now)
1227
+ });
1228
+ if (past.length) return eventOn(item, past[past.length - 1]);
1229
+ if (plusDays(now, -RECURRENCE_HORIZON_DAYS * back) < start.slice(0, 10)) break;
1230
+ }
1231
+ return item;
1232
+ }
1233
+ function queryCollection(ref, collections, opts = {}) {
1234
+ const now = opts.now ?? /* @__PURE__ */ new Date();
1235
+ const all = collections[ref.collection] ?? {};
1236
+ const visible = (item) => !!item && (opts.includeDrafts === true || isLive(item, now));
1237
+ let items;
1238
+ if (ref.items) {
1239
+ items = ref.items.map((slug) => all[slug]).filter(visible).map((item) => nextOccurrence(item, now));
1240
+ } else {
1241
+ const f = ref.filter;
1242
+ const live = Object.values(all).filter(visible);
1243
+ const expanded = f?.upcoming === true ? expandOccurrences(live, {
1244
+ from: isoDay(now),
1245
+ through: plusDays(now, RECURRENCE_HORIZON_DAYS)
1246
+ }) : f?.upcoming === false ? expandOccurrences(live, {
1247
+ from: plusDays(now, -RECURRENCE_HORIZON_DAYS),
1248
+ through: isoDay(now),
1249
+ newest: true
1250
+ }) : live.map((item) => nextOccurrence(item, now));
1251
+ items = expanded.filter((item) => {
1252
+ const fm = item.frontmatter;
1253
+ if (f?.categories?.length && !f.categories.some((c) => fm.categories?.includes(c)))
1254
+ return false;
1255
+ if (f?.tags?.length) {
1256
+ const tags = (fm.tags ?? []).map((t) => t.toLowerCase());
1257
+ if (!f.tags.some((t) => tags.includes(t.toLowerCase()))) return false;
1258
+ }
1259
+ if (f?.featured !== void 0 && fm.featured === true !== f.featured) return false;
1260
+ if (f?.upcoming !== void 0 && isUpcoming(item, now) !== f.upcoming) return false;
1261
+ return true;
1262
+ });
1263
+ }
1264
+ const sort = ref.sort ?? (ref.items ? "manual" : ref.filter?.upcoming ? "date-asc" : "date-desc");
1265
+ if (sort === "date-desc") items.sort((a, b) => time(b) - time(a));
1266
+ else if (sort === "date-asc") items.sort((a, b) => time(a) - time(b));
1267
+ else if (sort === "title")
1268
+ items.sort((a, b) => a.frontmatter.title.localeCompare(b.frontmatter.title));
1269
+ return ref.limit ? items.slice(0, ref.limit) : items;
1270
+ }
1271
+ function useCollectionQuery(ref) {
1272
+ const { collections, now, includeDrafts } = useCollections();
1273
+ return queryCollection(ref, collections, { now, includeDrafts });
1274
+ }
1275
+
1276
+ // src/header.tsx
1277
+ import { themeClass as themeClass4 } from "@omniharmonic/openspace-theme";
1278
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1279
+ var DRAWER_VARIANTS = ["minimal-burger", "sidebar-drawer"];
1280
+ function effectiveHeader(site, page) {
1281
+ const o = page?.header;
1282
+ const variant = o?.variant ?? site.header.variant;
1283
+ const transparentOnHero = o?.transparentOnHero ?? site.header.transparentOnHero;
1284
+ const hasHero = (page?.sections.filter((s) => !s.hidden).length ?? 0) > 0;
1285
+ return {
1286
+ ...site.header,
1287
+ variant,
1288
+ transparentOnHero,
1289
+ hidden: o?.hidden ?? false,
1290
+ overlay: hasHero && (variant === "transparent-overlay" || transparentOnHero)
1291
+ };
1292
+ }
1293
+ function resolveLeaf(item, doc, basePath, currentId) {
1294
+ if (item.hidden) return null;
1295
+ switch (item.type) {
1296
+ case "page": {
1297
+ const page = doc?.pages[item.page];
1298
+ if (doc && !page) return null;
1299
+ return {
1300
+ kind: "leaf",
1301
+ label: item.label ?? page?.title ?? "Page",
1302
+ href: page ? pagePath(page, basePath) : `${normalizeBasePath(basePath)}/`,
1303
+ current: item.page === currentId,
1304
+ newTab: false
1305
+ };
1306
+ }
1307
+ case "collection":
1308
+ return {
1309
+ kind: "leaf",
1310
+ label: item.label,
1311
+ href: `${normalizeBasePath(basePath)}/${item.collection}/`,
1312
+ current: false,
1313
+ newTab: false
1314
+ };
1315
+ case "link":
1316
+ return {
1317
+ kind: "leaf",
1318
+ label: item.label,
1319
+ href: item.href,
1320
+ current: false,
1321
+ newTab: item.newTab ?? false
1322
+ };
1323
+ }
1324
+ }
1325
+ function resolveNav(nav, doc, basePath, currentId) {
1326
+ const out = [];
1327
+ for (const item of nav) {
1328
+ if (item.hidden || item.type === "button") continue;
1329
+ if (item.type === "folder") {
1330
+ const items = [];
1331
+ if (item.page) {
1332
+ const landing = resolveLeaf({ type: "page", page: item.page }, doc, basePath, currentId);
1333
+ if (landing) items.push(landing);
1334
+ }
1335
+ for (const leaf of item.items) {
1336
+ const r2 = resolveLeaf(leaf, doc, basePath, currentId);
1337
+ if (r2) items.push(r2);
1338
+ }
1339
+ if (items.length)
1340
+ out.push({
1341
+ kind: "folder",
1342
+ label: item.label,
1343
+ current: items.some((i) => i.current),
1344
+ items
1345
+ });
1346
+ continue;
1347
+ }
1348
+ const r = resolveLeaf(item, doc, basePath, currentId);
1349
+ if (r) out.push(r);
1350
+ }
1351
+ return out;
1352
+ }
1353
+ function NavLeafLink({ item, className }) {
1354
+ return /* @__PURE__ */ jsx7(
1355
+ "a",
1356
+ {
1357
+ className,
1358
+ href: item.href,
1359
+ "aria-current": item.current ? "page" : void 0,
1360
+ target: item.newTab ? "_blank" : void 0,
1361
+ rel: item.newTab ? "noopener" : void 0,
1362
+ children: item.label
1363
+ }
1364
+ );
1365
+ }
1366
+ function NavList({ items, className }) {
1367
+ return /* @__PURE__ */ jsx7("ul", { className: cx("os-nav__list", className), children: items.map(
1368
+ (item, i) => item.kind === "leaf" ? (
1369
+ // biome-ignore lint/suspicious/noArrayIndexKey: nav order is positional
1370
+ /* @__PURE__ */ jsx7("li", { className: "os-nav__item", children: /* @__PURE__ */ jsx7(NavLeafLink, { item, className: "os-nav__link" }) }, i)
1371
+ ) : (
1372
+ // biome-ignore lint/suspicious/noArrayIndexKey: nav order is positional
1373
+ /* @__PURE__ */ jsx7("li", { className: "os-nav__item os-nav__item--folder", children: /* @__PURE__ */ jsxs5("details", { className: "os-nav__folder", "data-os-folder": "", children: [
1374
+ /* @__PURE__ */ jsxs5("summary", { className: cx("os-nav__link", item.current && "is-current"), children: [
1375
+ item.label,
1376
+ /* @__PURE__ */ jsx7(
1377
+ "svg",
1378
+ {
1379
+ className: "os-nav__chevron",
1380
+ viewBox: "0 0 12 12",
1381
+ width: "12",
1382
+ height: "12",
1383
+ "aria-hidden": "true",
1384
+ focusable: "false",
1385
+ children: /* @__PURE__ */ jsx7("path", { d: "M3 4.5l3 3 3-3", fill: "none", stroke: "currentColor", strokeWidth: "1.5" })
1386
+ }
1387
+ )
1388
+ ] }),
1389
+ /* @__PURE__ */ jsx7("ul", { className: "os-nav__sub", children: item.items.map((leaf, j) => (
1390
+ // biome-ignore lint/suspicious/noArrayIndexKey: nav order is positional
1391
+ /* @__PURE__ */ jsx7("li", { children: /* @__PURE__ */ jsx7(NavLeafLink, { item: leaf, className: "os-nav__sublink" }) }, j)
1392
+ )) })
1393
+ ] }) }, i)
1394
+ )
1395
+ ) });
1396
+ }
1397
+ function Brand({ site, className }) {
1398
+ const { basePath } = useRenderContext();
1399
+ const logo = site.logo;
1400
+ return /* @__PURE__ */ jsx7("a", { className: cx("os-header__brand", className), href: `${normalizeBasePath(basePath)}/`, children: logo ? /* @__PURE__ */ jsx7(
1401
+ Img,
1402
+ {
1403
+ src: logo.src,
1404
+ alt: logo.alt || site.title,
1405
+ ...logo.width ? { width: logo.width } : {},
1406
+ ...logo.height ? { height: logo.height } : {},
1407
+ sizes: "240px",
1408
+ treatment: "none",
1409
+ priority: true,
1410
+ className: "os-header__logo"
1411
+ }
1412
+ ) : /* @__PURE__ */ jsx7("span", { className: "os-header__title", children: site.title }) });
1413
+ }
1414
+ function MenuIcon() {
1415
+ return /* @__PURE__ */ jsx7(
1416
+ "svg",
1417
+ {
1418
+ className: "os-header__burger",
1419
+ viewBox: "0 0 24 24",
1420
+ width: "24",
1421
+ height: "24",
1422
+ "aria-hidden": "true",
1423
+ focusable: "false",
1424
+ children: /* @__PURE__ */ jsx7(
1425
+ "path",
1426
+ {
1427
+ d: "M4 7h16M4 12h16M4 17h16",
1428
+ fill: "none",
1429
+ stroke: "currentColor",
1430
+ strokeWidth: "1.6",
1431
+ strokeLinecap: "round"
1432
+ }
1433
+ )
1434
+ }
1435
+ );
1436
+ }
1437
+ function CartButton() {
1438
+ return /* @__PURE__ */ jsxs5(
1439
+ "button",
1440
+ {
1441
+ type: "button",
1442
+ className: "os-header__cart",
1443
+ "data-os-behavior": "cart",
1444
+ "data-os-cart-toggle": "",
1445
+ "aria-label": "Cart",
1446
+ children: [
1447
+ /* @__PURE__ */ jsx7("svg", { viewBox: "0 0 24 24", width: "22", height: "22", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx7(
1448
+ "path",
1449
+ {
1450
+ d: "M5 7h14l-1.5 12h-11zM9 7a3 3 0 0 1 6 0",
1451
+ fill: "none",
1452
+ stroke: "currentColor",
1453
+ strokeWidth: "1.6",
1454
+ strokeLinejoin: "round"
1455
+ }
1456
+ ) }),
1457
+ /* @__PURE__ */ jsx7("span", { className: "os-header__cart-count", "data-os-cart-count": "" })
1458
+ ]
1459
+ }
1460
+ );
1461
+ }
1462
+ function Header({ site, page, heroTheme, cart }) {
1463
+ const ctx = useRenderContext();
1464
+ const h = effectiveHeader(site, page);
1465
+ if (h.hidden) return null;
1466
+ const nav = resolveNav(site.nav, ctx.doc, ctx.basePath, page?.id ?? ctx.page?.id);
1467
+ const buttons = site.nav.filter(
1468
+ (i) => i.type === "button" && !i.hidden
1469
+ );
1470
+ const social = h.showSocial ? site.social ?? [] : [];
1471
+ const theme = h.overlay && heroTheme ? heroTheme : h.colorTheme ?? "light";
1472
+ const drawer = DRAWER_VARIANTS.includes(h.variant);
1473
+ const split = h.variant === "split-nav";
1474
+ const half = Math.ceil(nav.length / 2);
1475
+ const menuId = "os-menu";
1476
+ const actions = /* @__PURE__ */ jsxs5("div", { className: "os-header__actions", children: [
1477
+ social.length > 0 && /* @__PURE__ */ jsx7(SocialLinks, { profiles: social, className: "os-header__social" }),
1478
+ buttons.map((b, i) => (
1479
+ // biome-ignore lint/suspicious/noArrayIndexKey: nav order is positional
1480
+ /* @__PURE__ */ jsx7(Button, { to: b.link, role: b.style, className: "os-header__button", children: b.label }, i)
1481
+ ))
1482
+ ] });
1483
+ let desktopNav = null;
1484
+ if (!drawer && nav.length) {
1485
+ desktopNav = split ? /* @__PURE__ */ jsxs5("nav", { className: "os-nav os-nav--split", "aria-label": "Main", children: [
1486
+ /* @__PURE__ */ jsx7(NavList, { items: nav.slice(0, half), className: "os-nav__list--start" }),
1487
+ /* @__PURE__ */ jsx7(Brand, { site, className: "os-header__brand--split" }),
1488
+ /* @__PURE__ */ jsx7(NavList, { items: nav.slice(half), className: "os-nav__list--end" })
1489
+ ] }) : /* @__PURE__ */ jsx7("nav", { className: "os-nav", "aria-label": "Main", children: /* @__PURE__ */ jsx7(NavList, { items: nav }) });
1490
+ }
1491
+ return /* @__PURE__ */ jsxs5(
1492
+ "header",
1493
+ {
1494
+ className: cx(
1495
+ "os-header",
1496
+ `os-header--${h.variant}`,
1497
+ themeClass4(theme),
1498
+ h.sticky && "is-sticky",
1499
+ h.overlay && "is-overlay",
1500
+ drawer && "is-drawer",
1501
+ `has-menu-${h.variant === "sidebar-drawer" ? "drawer" : h.mobileMenu}`
1502
+ ),
1503
+ "data-os-header": "",
1504
+ "data-os-behavior": "menu",
1505
+ children: [
1506
+ /* @__PURE__ */ jsxs5("div", { className: "os-header__bar", children: [
1507
+ /* @__PURE__ */ jsx7(Brand, { site, className: split ? "os-header__brand--main" : void 0 }),
1508
+ desktopNav,
1509
+ actions,
1510
+ cart && /* @__PURE__ */ jsx7(CartButton, {}),
1511
+ /* @__PURE__ */ jsxs5(
1512
+ "button",
1513
+ {
1514
+ type: "button",
1515
+ className: "os-header__toggle",
1516
+ "aria-expanded": "false",
1517
+ "aria-controls": menuId,
1518
+ "data-os-menu-toggle": "",
1519
+ children: [
1520
+ /* @__PURE__ */ jsx7(MenuIcon, {}),
1521
+ /* @__PURE__ */ jsx7("span", { className: "os-visually-hidden", children: "Menu" })
1522
+ ]
1523
+ }
1524
+ )
1525
+ ] }),
1526
+ /* @__PURE__ */ jsxs5(
1527
+ "div",
1528
+ {
1529
+ id: menuId,
1530
+ className: cx(
1531
+ "os-menu",
1532
+ `os-menu--${h.variant === "sidebar-drawer" ? "drawer" : h.mobileMenu}`,
1533
+ themeClass4(h.colorTheme ?? "light")
1534
+ ),
1535
+ "data-os-menu": "",
1536
+ children: [
1537
+ /* @__PURE__ */ jsxs5("div", { className: "os-menu__head", children: [
1538
+ /* @__PURE__ */ jsx7("span", { className: "os-menu__title", children: site.title }),
1539
+ /* @__PURE__ */ jsxs5("button", { type: "button", className: "os-menu__close", "data-os-menu-close": "", children: [
1540
+ /* @__PURE__ */ jsx7("svg", { viewBox: "0 0 24 24", width: "24", height: "24", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx7(
1541
+ "path",
1542
+ {
1543
+ d: "M6 6l12 12M18 6L6 18",
1544
+ fill: "none",
1545
+ stroke: "currentColor",
1546
+ strokeWidth: "1.6",
1547
+ strokeLinecap: "round"
1548
+ }
1549
+ ) }),
1550
+ /* @__PURE__ */ jsx7("span", { className: "os-visually-hidden", children: "Close menu" })
1551
+ ] })
1552
+ ] }),
1553
+ /* @__PURE__ */ jsx7("nav", { className: "os-menu__nav", "aria-label": "Menu", children: /* @__PURE__ */ jsx7(NavList, { items: nav, className: "os-menu__list" }) }),
1554
+ (buttons.length > 0 || (site.social?.length ?? 0) > 0) && /* @__PURE__ */ jsxs5("div", { className: "os-menu__foot", children: [
1555
+ buttons.map((b, i) => (
1556
+ // biome-ignore lint/suspicious/noArrayIndexKey: nav order is positional
1557
+ /* @__PURE__ */ jsx7(Button, { to: b.link, role: b.style, children: b.label }, i)
1558
+ )),
1559
+ site.social && /* @__PURE__ */ jsx7(SocialLinks, { profiles: site.social })
1560
+ ] })
1561
+ ]
1562
+ }
1563
+ )
1564
+ ]
1565
+ }
1566
+ );
1567
+ }
1568
+
1569
+ // src/html.ts
1570
+ import {
1571
+ chosenCustomFont,
1572
+ customFontFaces,
1573
+ customFontPreloads,
1574
+ googleFontsUrl
1575
+ } from "@omniharmonic/openspace-theme";
1576
+ import { createElement } from "react";
1577
+ import { renderToStaticMarkup } from "react-dom/server";
1578
+
1579
+ // src/html-meta.ts
1580
+ import { compileStyles } from "@omniharmonic/openspace-theme";
1581
+ var ESC = {
1582
+ "&": "&amp;",
1583
+ "<": "&lt;",
1584
+ ">": "&gt;",
1585
+ '"': "&quot;",
1586
+ "'": "&#39;"
1587
+ };
1588
+ var escapeHtml = (s) => s.replace(/[&<>"']/g, (ch) => ESC[ch]);
1589
+ function jsonForScript(value) {
1590
+ return JSON.stringify(value).replace(/</g, "\\u003c").replace(LINE_SEP, "\\u2028").replace(PARA_SEP, "\\u2029");
1591
+ }
1592
+ var LINE_SEP = new RegExp(String.fromCharCode(8232), "g");
1593
+ var PARA_SEP = new RegExp(String.fromCharCode(8233), "g");
1594
+ var safeCss = (css) => css.replace(/<\/style/gi, "<\\/style");
1595
+ var compiled = /* @__PURE__ */ new WeakMap();
1596
+ function compiledStylesFor(styles) {
1597
+ let hit = compiled.get(styles);
1598
+ if (!hit) {
1599
+ hit = compileStyles(styles);
1600
+ compiled.set(styles, hit);
1601
+ }
1602
+ return hit;
1603
+ }
1604
+ function siteUrlFor(doc, override) {
1605
+ const raw = override ?? (doc.config.deploy.customDomain ? `https://${doc.config.deploy.customDomain}` : doc.config.deploy.url);
1606
+ return raw.endsWith("/") ? raw : `${raw}/`;
1607
+ }
1608
+ function absolute(siteUrl, path) {
1609
+ if (/^https?:\/\//.test(path)) return path;
1610
+ return new URL(path.replace(/^\/+/, ""), siteUrl).toString();
1611
+ }
1612
+ function ogImageUrl(src, siteUrl, imageMap) {
1613
+ const mapped = imageMap?.[src]?.src;
1614
+ if (mapped) return new URL(mapped, siteUrl).toString();
1615
+ return absolute(siteUrl, mediaUrl(src));
1616
+ }
1617
+ function pageTitle(doc, page) {
1618
+ const own = page.seo?.title;
1619
+ if (own) return own;
1620
+ const template = doc.site.seo?.titleTemplate;
1621
+ if (page.slug === "") {
1622
+ return doc.site.tagline ? `${doc.site.title} \u2014 ${doc.site.tagline}` : doc.site.title;
1623
+ }
1624
+ return template ? template.replace("%s", page.title) : `${page.title} \u2014 ${doc.site.title}`;
1625
+ }
1626
+ function pageDescription(doc, page) {
1627
+ return page.seo?.description ?? doc.site.seo?.description ?? doc.site.tagline ?? void 0;
1628
+ }
1629
+
1630
+ // src/page.tsx
1631
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1632
+ function resolvePage(doc, page) {
1633
+ if (typeof page !== "string") return page;
1634
+ const found = doc.pages[page];
1635
+ if (!found) throw new Error(`Page ${page} does not exist`);
1636
+ return found;
1637
+ }
1638
+ function PageView(props) {
1639
+ const { doc, mode = "production", imageMap = {}, extras } = props;
1640
+ const page = resolvePage(doc, props.page);
1641
+ const basePath = normalizeBasePath(props.basePath);
1642
+ const hubUrl = props.hubUrl ?? doc.config.hub;
1643
+ const visible = page.sections.filter((s) => !s.hidden || mode === "editor");
1644
+ const header = effectiveHeader(doc.site, page);
1645
+ const heroTheme = visible[0]?.style.colorTheme ?? "light";
1646
+ const cart = doc.site.integrations?.stripeConnected ?? false;
1647
+ const logoVars = header.logoHeight ? responsiveVars("logo-h", header.logoHeight, (px) => `${px}px`) : void 0;
1648
+ const content = /* @__PURE__ */ jsx8(RenderContext.Provider, { value: { doc, page, mode, basePath, hubUrl }, children: /* @__PURE__ */ jsx8(ImageResolverContext.Provider, { value: imageMap, children: /* @__PURE__ */ jsxs6(
1649
+ "div",
1650
+ {
1651
+ className: cx("os-page", header.overlay && !header.hidden && "has-overlay-header"),
1652
+ "data-os-page": page.id,
1653
+ style: logoVars,
1654
+ children: [
1655
+ /* @__PURE__ */ jsx8("a", { className: "os-skip-link", href: "#main", children: "Skip to content" }),
1656
+ /* @__PURE__ */ jsx8(AnnouncementBar, { announcement: doc.site.announcement }),
1657
+ /* @__PURE__ */ jsx8(Header, { site: doc.site, page, heroTheme, cart }),
1658
+ /* @__PURE__ */ jsx8("main", { id: "main", className: "os-main", tabIndex: -1, children: visible.map((section, i) => /* @__PURE__ */ jsx8(FirstSectionContext.Provider, { value: i === 0, children: /* @__PURE__ */ jsx8(SectionView, { section }) }, section.id)) }),
1659
+ /* @__PURE__ */ jsx8(Footer, { site: doc.site }),
1660
+ mode !== "editor" ? /* @__PURE__ */ jsx8(SitePopup, { popup: doc.site.popup }) : null,
1661
+ extras
1662
+ ]
1663
+ }
1664
+ ) }) });
1665
+ return props.editor ? /* @__PURE__ */ jsx8(EditorContext.Provider, { value: props.editor, children: content }) : content;
1666
+ }
1667
+
1668
+ // src/html.ts
1669
+ function monogramIcon(title, brand) {
1670
+ const letter = escapeHtml((title.trim().match(/\p{L}|\p{N}/u)?.[0] ?? "\u2022").toUpperCase());
1671
+ const bg = brand?.background ?? "#222222";
1672
+ const fg = brand?.heading ?? "#ffffff";
1673
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="14" fill="${bg}"/><text x="32" y="43" font-family="system-ui,sans-serif" font-size="34" font-weight="600" text-anchor="middle" fill="${fg}">${letter}</text></svg>`;
1674
+ return `data:image/svg+xml,${encodeURIComponent(svg)}`;
1675
+ }
1676
+ var attr = (tag, name2) => new RegExp(`\\s${name2}="([^"]*)"`).exec(tag)?.[1];
1677
+ function phoneShare(sizes) {
1678
+ const last = (sizes ?? "100vw").split(",").pop()?.trim() ?? "100vw";
1679
+ const vw = /^(\d+(?:\.\d+)?)vw$/.exec(last);
1680
+ if (vw) return Number(vw[1]) / 100;
1681
+ const times = /\*\s*(\d+(?:\.\d+)?)\s*\)$/.exec(last);
1682
+ if (times) return Number(times[1]);
1683
+ return /^calc\(100vw/.test(last) ? 1 : 0.5;
1684
+ }
1685
+ function lcpImagePreload(body) {
1686
+ const imgs = [...body.matchAll(/<img\b[^>]*\bfetchpriority="high"[^>]*>/gi)].slice(0, 4);
1687
+ let best = null;
1688
+ for (const m of imgs) {
1689
+ const share = phoneShare(attr(m[0], "sizes"));
1690
+ const w = Number(attr(m[0], "width")) || 1;
1691
+ const h = Number(attr(m[0], "height")) || w;
1692
+ const score = share * share * (h / w);
1693
+ if (!best || score > best.score) best = { m, score };
1694
+ }
1695
+ if (!best || best.m.index === void 0) return "";
1696
+ const img = best.m;
1697
+ const at = best.m.index;
1698
+ const pictureStart = body.lastIndexOf("<picture", at);
1699
+ const between = pictureStart >= 0 ? body.slice(pictureStart, at) : "";
1700
+ const source = pictureStart >= 0 && !between.includes("</picture>") ? /<source\b[^>]*>/i.exec(between)?.[0] : void 0;
1701
+ const tag = source ?? img[0];
1702
+ const srcset = attr(tag, "srcset") ?? attr(tag, "srcSet");
1703
+ const sizes = attr(tag, "sizes");
1704
+ const src = attr(img[0], "src");
1705
+ const type = source ? attr(source, "type") : void 0;
1706
+ if (!srcset && !src) return "";
1707
+ return `<link rel="preload" as="image"${srcset ? ` imagesrcset="${srcset}"` : ` href="${src}"`}${srcset && sizes ? ` imagesizes="${sizes}"` : ""}${type ? ` type="${type}"` : ""} fetchpriority="high">`;
1708
+ }
1709
+ function renderPageHtml(opts) {
1710
+ const { doc } = opts;
1711
+ const mode = opts.mode ?? "production";
1712
+ const page = resolvePage(doc, opts.pageId);
1713
+ const basePath = normalizeBasePath(opts.basePath);
1714
+ const hubUrl = (opts.hubUrl ?? doc.config.hub).replace(/\/+$/, "");
1715
+ const behaviorsUrl = (opts.behaviorsUrl ?? `${basePath}/_os/behaviors`).replace(/\/+$/, "");
1716
+ const siteUrl = siteUrlFor(doc, opts.siteUrl);
1717
+ const site = doc.site;
1718
+ const styles = compiledStylesFor(doc.styles);
1719
+ const body = renderToStaticMarkup(
1720
+ createElement(PageView, {
1721
+ doc,
1722
+ page,
1723
+ mode,
1724
+ basePath,
1725
+ imageMap: opts.imageMap ?? {},
1726
+ hubUrl
1727
+ })
1728
+ );
1729
+ const analytics = mode === "production" && doc.config.features.analytics && site.integrations?.analytics !== false;
1730
+ const behaviors = behaviorsInHtml(body);
1731
+ if (analytics) behaviors.push("beacon");
1732
+ const title = pageTitle(doc, page);
1733
+ const description = pageDescription(doc, page);
1734
+ const canonical = page.seo?.canonical ?? absolute(siteUrl, pagePath(page));
1735
+ const noindex = mode === "preview" || page.seo?.noindex || site.seo?.noindex;
1736
+ const ogImageSrc = page.seo?.image ?? site.seo?.image;
1737
+ const ogImage = ogImageSrc ? ogImageUrl(ogImageSrc, siteUrl, opts.imageMap) : void 0;
1738
+ const customHeading = chosenCustomFont(doc.styles, doc.styles.choices.headingFont);
1739
+ const customBody = chosenCustomFont(doc.styles, doc.styles.choices.bodyFont);
1740
+ const fonts = customHeading && customBody ? [] : opts.fontStylesheets ?? [googleFontsUrl(styles.tokens.pairing)];
1741
+ const fontFaces = customFontFaces(doc.styles, (src) => mediaUrl(src, basePath));
1742
+ const fontPreloads = customFontPreloads(doc.styles, {
1743
+ heading: styles.tokens.pairing.headingWeight,
1744
+ body: styles.tokens.pairing.bodyWeight
1745
+ });
1746
+ const usesGoogle = fonts.some((f) => f.startsWith("https://fonts.googleapis.com/"));
1747
+ const meta = (name2, content, attr2 = "name") => content ? `<meta ${attr2}="${name2}" content="${escapeHtml(content)}">` : "";
1748
+ const head = [
1749
+ '<meta charset="utf-8">',
1750
+ '<meta name="viewport" content="width=device-width, initial-scale=1">',
1751
+ `<title>${escapeHtml(title)}</title>`,
1752
+ // The LCP image first: discovered before the stylesheets and fonts below.
1753
+ lcpImagePreload(body),
1754
+ meta("description", description),
1755
+ noindex ? '<meta name="robots" content="noindex">' : "",
1756
+ `<link rel="canonical" href="${escapeHtml(canonical)}">`,
1757
+ meta("og:type", "website", "property"),
1758
+ meta("og:site_name", site.title, "property"),
1759
+ meta("og:title", page.seo?.title ?? page.title, "property"),
1760
+ meta("og:description", description, "property"),
1761
+ meta("og:url", canonical, "property"),
1762
+ meta("og:image", ogImage, "property"),
1763
+ meta("og:locale", site.locale.replace("-", "_"), "property"),
1764
+ meta("twitter:card", ogImage ? "summary_large_image" : "summary"),
1765
+ `<link rel="icon" href="${escapeHtml(site.favicon ? mediaUrl(site.favicon, basePath) : monogramIcon(site.title, styles.tokens.themes.brand))}">`,
1766
+ '<meta name="generator" content="OpenSpace">',
1767
+ "<script>document.documentElement.classList.add('os-js')</script>",
1768
+ usesGoogle ? '<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>' : "",
1769
+ ...fontPreloads.map(
1770
+ (src) => `<link rel="preload" as="font" type="font/woff2" href="${escapeHtml(mediaUrl(src, basePath))}" crossorigin>`
1771
+ ),
1772
+ ...fonts.map((href) => `<link rel="stylesheet" href="${escapeHtml(href)}">`),
1773
+ fontFaces ? `<style data-os="fonts">${safeCss(fontFaces)}</style>` : "",
1774
+ opts.stylesheets?.tokens ? `<link rel="stylesheet" href="${escapeHtml(opts.stylesheets.tokens)}">` : `<style data-os="tokens">${styles.css}</style>`,
1775
+ opts.stylesheets?.base ? `<link rel="stylesheet" href="${escapeHtml(opts.stylesheets.base)}">` : `<style data-os="base">${BASE_CSS}</style>`,
1776
+ site.customCss ? `<style data-os="custom">${safeCss(site.customCss)}</style>` : "",
1777
+ ...[opts.jsonLd ?? []].flat().map((ld) => `<script type="application/ld+json">${jsonForScript(ld)}</script>`),
1778
+ ...behaviors.map(
1779
+ (b) => `<link rel="modulepreload" href="${escapeHtml(`${behaviorsUrl}/${b}.js`)}">`
1780
+ ),
1781
+ opts.extraHead ?? "",
1782
+ site.headInjection ?? "",
1783
+ page.headInjection ?? ""
1784
+ ];
1785
+ const bodyAttrs = [
1786
+ `class="os-body"`,
1787
+ `data-os-site="${escapeHtml(doc.config.siteId)}"`,
1788
+ `data-os-hub="${escapeHtml(hubUrl)}"`,
1789
+ `data-os-base="${escapeHtml(basePath)}"`,
1790
+ `data-os-mode="${mode}"`,
1791
+ analytics ? `data-os-behavior="beacon"` : ""
1792
+ ].filter(Boolean).join(" ");
1793
+ const scripts = behaviors.length ? `<script type="module" src="${escapeHtml(`${behaviorsUrl}/${BEHAVIOR_LOADER}.js`)}"></script>` : "";
1794
+ return `<!doctype html>
1795
+ <html lang="${escapeHtml(site.locale)}">
1796
+ <head>
1797
+ ${head.filter(Boolean).join("\n")}
1798
+ </head>
1799
+ <body ${bodyAttrs}>
1800
+ ${body}
1801
+ ${scripts}${site.footerInjection ?? ""}${page.footerInjection ?? ""}
1802
+ </body>
1803
+ </html>
1804
+ `;
1805
+ }
1806
+ function behaviorsForPage(input) {
1807
+ if (typeof input === "string") return behaviorsInHtml(input);
1808
+ return behaviorsInHtml(renderPageHtml(input));
1809
+ }
1810
+
1811
+ // src/richtext.tsx
1812
+ import { colorVar as colorVar2 } from "@omniharmonic/openspace-theme";
1813
+ import { Fragment as Fragment3, useContext as useContext7 } from "react";
1814
+ import { Fragment as Fragment4, jsx as jsx9 } from "react/jsx-runtime";
1815
+ var typeClass = (preset) => `os-type-${preset}`;
1816
+ var MARK_ORDER = [
1817
+ "code",
1818
+ "strike",
1819
+ "underline",
1820
+ "italic",
1821
+ "bold",
1822
+ "highlight",
1823
+ "link"
1824
+ ];
1825
+ function wrapMark(mark, child, key, basePath) {
1826
+ switch (mark.type) {
1827
+ case "bold":
1828
+ return /* @__PURE__ */ jsx9("strong", { children: child }, key);
1829
+ case "italic":
1830
+ return /* @__PURE__ */ jsx9("em", { children: child }, key);
1831
+ case "underline":
1832
+ return /* @__PURE__ */ jsx9("u", { children: child }, key);
1833
+ case "strike":
1834
+ return /* @__PURE__ */ jsx9("s", { children: child }, key);
1835
+ case "code":
1836
+ return /* @__PURE__ */ jsx9("code", { children: child }, key);
1837
+ case "highlight": {
1838
+ const color = mark.attrs?.color;
1839
+ return /* @__PURE__ */ jsx9(
1840
+ "mark",
1841
+ {
1842
+ className: "os-highlight",
1843
+ style: color ? { "--hl": `var(${colorVar2(color)})` } : void 0,
1844
+ children: child
1845
+ },
1846
+ key
1847
+ );
1848
+ }
1849
+ case "link": {
1850
+ const { href, title, newTab } = mark.attrs;
1851
+ return /* @__PURE__ */ jsx9(
1852
+ "a",
1853
+ {
1854
+ href: withBase(href, basePath),
1855
+ title,
1856
+ target: newTab ? "_blank" : void 0,
1857
+ rel: newTab ? "noopener" : void 0,
1858
+ children: child
1859
+ },
1860
+ key
1861
+ );
1862
+ }
1863
+ }
1864
+ }
1865
+ function renderInline(nodes, basePath) {
1866
+ if (!nodes) return [];
1867
+ return nodes.map((node, i) => {
1868
+ if (node.type === "hardBreak") return /* @__PURE__ */ jsx9("br", {}, i);
1869
+ const marks = [...node.marks ?? []].sort(
1870
+ (a, b) => MARK_ORDER.indexOf(a.type) - MARK_ORDER.indexOf(b.type)
1871
+ );
1872
+ let out = node.text;
1873
+ for (const m of marks) out = wrapMark(m, out, i, basePath);
1874
+ return marks.length ? out : /* @__PURE__ */ jsx9(Fragment3, { children: node.text }, i);
1875
+ });
1876
+ }
1877
+ function renderBlock(node, key, basePath) {
1878
+ switch (node.type) {
1879
+ case "paragraph": {
1880
+ const preset = node.attrs?.preset;
1881
+ const children = renderInline(node.content, basePath);
1882
+ return /* @__PURE__ */ jsx9("p", { className: preset && preset !== "body" ? typeClass(preset) : void 0, children: children.length ? children : /* @__PURE__ */ jsx9("br", {}) }, key);
1883
+ }
1884
+ case "heading": {
1885
+ const { level, preset } = node.attrs;
1886
+ const Tag = `h${level}`;
1887
+ return /* @__PURE__ */ jsx9(Tag, { className: typeClass(preset ?? `h${level}`), children: renderInline(node.content, basePath) }, key);
1888
+ }
1889
+ case "bulletList":
1890
+ return /* @__PURE__ */ jsx9("ul", { children: node.content.map((li, i) => (
1891
+ // biome-ignore lint/suspicious/noArrayIndexKey: rich text nodes have no ids
1892
+ /* @__PURE__ */ jsx9("li", { children: li.content.map((c, j) => renderBlock(c, j, basePath)) }, i)
1893
+ )) }, key);
1894
+ case "orderedList":
1895
+ return /* @__PURE__ */ jsx9("ol", { start: node.attrs?.start, children: node.content.map((li, i) => (
1896
+ // biome-ignore lint/suspicious/noArrayIndexKey: rich text nodes have no ids
1897
+ /* @__PURE__ */ jsx9("li", { children: li.content.map((c, j) => renderBlock(c, j, basePath)) }, i)
1898
+ )) }, key);
1899
+ case "blockquote":
1900
+ return /* @__PURE__ */ jsx9("blockquote", { children: node.content.map((c, j) => renderBlock(c, j, basePath)) }, key);
1901
+ }
1902
+ }
1903
+ function RichText({ doc, className, as: Tag = "div" }) {
1904
+ const { basePath } = useRenderContext();
1905
+ const editor = useEditor();
1906
+ const owner = useContext7(BlockFieldsContext);
1907
+ const out = !doc || doc.content.length === 0 ? null : /* @__PURE__ */ jsx9(Tag, { className: cx("os-rich", className), children: doc.content.map((node, i) => renderBlock(node, i, basePath)) });
1908
+ if (editor?.renderRichText && owner && doc) {
1909
+ const fieldPath = fieldPathOf(owner.fields, doc);
1910
+ if (fieldPath)
1911
+ return /* @__PURE__ */ jsx9(Fragment4, { children: editor.renderRichText({
1912
+ block: owner.block,
1913
+ fieldPath,
1914
+ doc,
1915
+ className,
1916
+ as: Tag,
1917
+ children: out
1918
+ }) });
1919
+ }
1920
+ return out;
1921
+ }
1922
+
1923
+ // src/index.ts
1924
+ var name = "@omniharmonic/openspace-renderer";
1925
+ export {
1926
+ AboveFoldContext,
1927
+ AnnouncementBar,
1928
+ BASE_CSS,
1929
+ BEHAVIORS,
1930
+ BEHAVIOR_BUDGETS,
1931
+ BEHAVIOR_LOADER,
1932
+ BlockFieldsContext,
1933
+ BlockRegistryContext,
1934
+ BlockView,
1935
+ Button,
1936
+ CollectionsContext,
1937
+ DRAWER_VARIANTS,
1938
+ EditorContext,
1939
+ FirstSectionContext,
1940
+ Footer,
1941
+ Header,
1942
+ ImageResolverContext,
1943
+ Img,
1944
+ Link,
1945
+ PageView,
1946
+ Popup,
1947
+ RECURRENCE_HORIZON_DAYS,
1948
+ REMOTE_WIDTHS,
1949
+ RenderContext,
1950
+ RichText,
1951
+ SectionBackgroundLayer,
1952
+ SectionContext,
1953
+ SectionView,
1954
+ SitePopup,
1955
+ SocialIcon,
1956
+ SocialLinks,
1957
+ alignClasses,
1958
+ backgroundVars,
1959
+ behaviorsForPage,
1960
+ behaviorsInHtml,
1961
+ blockStyleProps,
1962
+ blockTypeClass,
1963
+ blocksByColumn,
1964
+ collectImageSrcs,
1965
+ colorValue,
1966
+ compiledStylesFor,
1967
+ cx,
1968
+ effectiveHeader,
1969
+ escapeHtml,
1970
+ eventOn,
1971
+ expandOccurrences,
1972
+ fallbackMobileRects,
1973
+ feedKey,
1974
+ fieldPathOf,
1975
+ getBlock,
1976
+ globalRegistry,
1977
+ gridItemVars,
1978
+ gridVars,
1979
+ isBehaviorName,
1980
+ isLive,
1981
+ isUpcoming,
1982
+ itemDate,
1983
+ itemKey,
1984
+ itemPath,
1985
+ jsonForScript,
1986
+ listBlocks,
1987
+ mediaUrl,
1988
+ mobileRows,
1989
+ name,
1990
+ networkLabel,
1991
+ nextOccurrence,
1992
+ normalizeBasePath,
1993
+ occurrenceDates,
1994
+ pageDescription,
1995
+ pagePath,
1996
+ pageTitle,
1997
+ parseRecurrence,
1998
+ popupTrigger,
1999
+ queryCollection,
2000
+ registerBlocks,
2001
+ remoteSrcset,
2002
+ renderPageHtml,
2003
+ resolveImage,
2004
+ resolveLink,
2005
+ resolveNav,
2006
+ resolvePage,
2007
+ responsiveVars,
2008
+ sectionFragment,
2009
+ sectionStyleVars,
2010
+ shortHash,
2011
+ siteUrlFor,
2012
+ stackColumnVars,
2013
+ stackVars,
2014
+ typeClass,
2015
+ unregisterBlocks,
2016
+ useCollectionQuery,
2017
+ useCollections,
2018
+ useEditor,
2019
+ useImageMap,
2020
+ useRenderContext,
2021
+ withBase
2022
+ };
2023
+ //# sourceMappingURL=index.js.map