@glyphjs/runtime 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.cjs ADDED
@@ -0,0 +1,1766 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+ var schemas = require('@glyphjs/schemas');
6
+ var DOMPurify = require('dompurify');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var DOMPurify__default = /*#__PURE__*/_interopDefault(DOMPurify);
11
+
12
+ // src/create-runtime.tsx
13
+
14
+ // src/plugins/validate.ts
15
+ var UI_TYPE_PATTERN = /^ui:.+$/;
16
+ function validateComponentDefinition(definition) {
17
+ const errors = [];
18
+ if (!definition.type) {
19
+ errors.push('Component definition must have a "type" field.');
20
+ } else if (!UI_TYPE_PATTERN.test(definition.type)) {
21
+ errors.push(
22
+ `Component type "${definition.type}" must match the "ui:*" format (e.g. "ui:chart", "ui:graph").`
23
+ );
24
+ }
25
+ if (!definition.schema) {
26
+ errors.push('Component definition must have a "schema" field.');
27
+ } else {
28
+ if (typeof definition.schema.parse !== "function") {
29
+ errors.push(
30
+ 'Component schema must have a "parse" method (expected a Zod-compatible schema).'
31
+ );
32
+ }
33
+ if (typeof definition.schema.safeParse !== "function") {
34
+ errors.push(
35
+ 'Component schema must have a "safeParse" method (expected a Zod-compatible schema).'
36
+ );
37
+ }
38
+ }
39
+ if (!definition.render) {
40
+ errors.push(
41
+ 'Component definition must have a "render" field (function or class component).'
42
+ );
43
+ } else if (typeof definition.render !== "function") {
44
+ errors.push(
45
+ 'Component "render" must be a function (functional component) or a class (class component).'
46
+ );
47
+ }
48
+ return {
49
+ valid: errors.length === 0,
50
+ errors
51
+ };
52
+ }
53
+
54
+ // src/plugins/registry.ts
55
+ var PluginRegistry = class {
56
+ components = /* @__PURE__ */ new Map();
57
+ overrides = /* @__PURE__ */ new Map();
58
+ listeners = /* @__PURE__ */ new Set();
59
+ themeDefaults = {};
60
+ // ─── Registration ────────────────────────────────────────────
61
+ /**
62
+ * Register a `ui:*` component plugin definition.
63
+ * Validates the definition first; throws if invalid.
64
+ * Merges any `themeDefaults` from the definition into
65
+ * the accumulated theme defaults map.
66
+ *
67
+ * @param definition - The component definition to register.
68
+ * @throws Error if the definition fails validation.
69
+ */
70
+ registerComponent(definition) {
71
+ const result = validateComponentDefinition(definition);
72
+ if (!result.valid) {
73
+ throw new Error(
74
+ `Invalid component definition for "${definition.type}":
75
+ - ${result.errors.join("\n - ")}`
76
+ );
77
+ }
78
+ this.components.set(definition.type, definition);
79
+ if (definition.themeDefaults) {
80
+ Object.assign(this.themeDefaults, definition.themeDefaults);
81
+ }
82
+ this.notify();
83
+ }
84
+ /** Bulk-register an array of component definitions. */
85
+ registerAll(definitions) {
86
+ for (const def of definitions) {
87
+ this.registerComponent(def);
88
+ }
89
+ }
90
+ // ─── Overrides ───────────────────────────────────────────────
91
+ /** Set override renderers (keyed by block type). */
92
+ setOverrides(overrides) {
93
+ for (const [type, renderer] of Object.entries(overrides)) {
94
+ if (renderer) {
95
+ this.overrides.set(type, renderer);
96
+ }
97
+ }
98
+ this.notify();
99
+ }
100
+ // ─── Lookups ─────────────────────────────────────────────────
101
+ /** Get a registered `ui:*` component definition. */
102
+ getRenderer(blockType) {
103
+ return this.components.get(blockType);
104
+ }
105
+ /** Get an override renderer for any block type. */
106
+ getOverride(blockType) {
107
+ return this.overrides.get(blockType);
108
+ }
109
+ /** Check if a component type is registered. */
110
+ has(blockType) {
111
+ return this.components.has(blockType);
112
+ }
113
+ /** Get all registered component type names. */
114
+ getRegisteredTypes() {
115
+ return Array.from(this.components.keys());
116
+ }
117
+ // ─── Theme Defaults ──────────────────────────────────────────
118
+ /**
119
+ * Returns the accumulated theme defaults from all registered
120
+ * component definitions. These can be merged into a GlyphTheme
121
+ * to provide sensible defaults for plugin-specific variables.
122
+ */
123
+ getThemeDefaults() {
124
+ return { ...this.themeDefaults };
125
+ }
126
+ /**
127
+ * Merge accumulated plugin theme defaults into a GlyphTheme.
128
+ * Plugin defaults have lower priority than existing theme variables.
129
+ *
130
+ * @param theme - The base theme to merge defaults into.
131
+ * @returns A new GlyphTheme with plugin defaults applied under existing variables.
132
+ */
133
+ mergeThemeDefaults(theme) {
134
+ return {
135
+ ...theme,
136
+ variables: {
137
+ ...this.themeDefaults,
138
+ ...theme.variables
139
+ }
140
+ };
141
+ }
142
+ // ─── Change Notification ─────────────────────────────────────
143
+ /**
144
+ * Subscribe to registry changes.
145
+ *
146
+ * @param listener - Callback invoked whenever a component or override is registered.
147
+ * @returns An unsubscribe function that removes the listener.
148
+ */
149
+ subscribe(listener) {
150
+ this.listeners.add(listener);
151
+ return () => {
152
+ this.listeners.delete(listener);
153
+ };
154
+ }
155
+ notify() {
156
+ for (const listener of this.listeners) {
157
+ listener();
158
+ }
159
+ }
160
+ };
161
+
162
+ // src/theme/light.ts
163
+ var lightTheme = {
164
+ name: "light",
165
+ variables: {
166
+ // Colors
167
+ "--glyph-bg": "#f4f6fa",
168
+ "--glyph-text": "#1a2035",
169
+ "--glyph-text-muted": "#6b7a94",
170
+ "--glyph-heading": "#0a0e1a",
171
+ "--glyph-link": "#0a9d7c",
172
+ "--glyph-link-hover": "#088a6c",
173
+ "--glyph-border": "#d0d8e4",
174
+ "--glyph-border-strong": "#a8b5c8",
175
+ "--glyph-surface": "#e8ecf3",
176
+ "--glyph-surface-raised": "#f4f6fa",
177
+ // Accent
178
+ "--glyph-accent": "#0a9d7c",
179
+ "--glyph-accent-hover": "#088a6c",
180
+ "--glyph-accent-subtle": "#e6f6f2",
181
+ "--glyph-accent-muted": "#b0ddd0",
182
+ // Code
183
+ "--glyph-code-bg": "#e8ecf3",
184
+ "--glyph-code-text": "#1a2035",
185
+ // Blockquote
186
+ "--glyph-blockquote-border": "#0a9d7c",
187
+ "--glyph-blockquote-bg": "#e6f6f2",
188
+ // Grid / Tooltip
189
+ "--glyph-grid": "#d0d8e4",
190
+ "--glyph-tooltip-bg": "rgba(10, 14, 26, 0.9)",
191
+ "--glyph-tooltip-text": "#f4f6fa",
192
+ // Callouts
193
+ "--glyph-callout-info-bg": "#e6f2fa",
194
+ "--glyph-callout-info-border": "#38bdf8",
195
+ "--glyph-callout-warning-bg": "#fef3e2",
196
+ "--glyph-callout-warning-border": "#fb923c",
197
+ "--glyph-callout-error-bg": "#fde8e8",
198
+ "--glyph-callout-error-border": "#f87171",
199
+ "--glyph-callout-tip-bg": "#e6f6f0",
200
+ "--glyph-callout-tip-border": "#22c55e",
201
+ // Spacing
202
+ "--glyph-spacing-xs": "0.25rem",
203
+ "--glyph-spacing-sm": "0.5rem",
204
+ "--glyph-spacing-md": "1rem",
205
+ "--glyph-spacing-lg": "1.5rem",
206
+ "--glyph-spacing-xl": "2rem",
207
+ // Typography
208
+ "--glyph-font-body": '"Inter", "Helvetica Neue", system-ui, sans-serif',
209
+ "--glyph-font-heading": '"Inter", "Helvetica Neue", system-ui, sans-serif',
210
+ "--glyph-font-mono": 'ui-monospace, "Cascadia Code", "Fira Code", monospace',
211
+ // Border radius
212
+ "--glyph-radius-sm": "0.375rem",
213
+ "--glyph-radius-md": "0.5rem",
214
+ "--glyph-radius-lg": "0.75rem",
215
+ // Effects
216
+ "--glyph-shadow-sm": "0 1px 3px rgba(0,0,0,0.1)",
217
+ "--glyph-shadow-md": "0 4px 12px rgba(0,0,0,0.15)",
218
+ "--glyph-shadow-lg": "0 8px 30px rgba(0,0,0,0.2)",
219
+ "--glyph-shadow-glow": "none",
220
+ "--glyph-text-shadow": "none",
221
+ "--glyph-backdrop": "none",
222
+ "--glyph-gradient-accent": "linear-gradient(135deg, #0a9d7c, #22c55e)",
223
+ "--glyph-transition": "0.2s ease",
224
+ "--glyph-opacity-muted": "0.7",
225
+ "--glyph-opacity-disabled": "0.4",
226
+ "--glyph-focus-ring": "0 0 0 2px #0a9d7c",
227
+ // SVG / Data Visualization
228
+ "--glyph-node-fill-opacity": "0.85",
229
+ "--glyph-node-radius": "3",
230
+ "--glyph-node-stroke-width": "1.5",
231
+ "--glyph-node-label-color": "#fff",
232
+ "--glyph-edge-color": "#a8b5c8",
233
+ "--glyph-icon-stroke": "#fff",
234
+ "--glyph-icon-stroke-width": "1.5"
235
+ }
236
+ };
237
+
238
+ // src/theme/dark.ts
239
+ var darkTheme = {
240
+ name: "dark",
241
+ variables: {
242
+ // Colors
243
+ "--glyph-bg": "#0a0e1a",
244
+ "--glyph-text": "#d4dae3",
245
+ "--glyph-text-muted": "#6b7a94",
246
+ "--glyph-heading": "#edf0f5",
247
+ "--glyph-link": "#00d4aa",
248
+ "--glyph-link-hover": "#33e0be",
249
+ "--glyph-border": "#1a2035",
250
+ "--glyph-border-strong": "#2a3550",
251
+ "--glyph-surface": "#0f1526",
252
+ "--glyph-surface-raised": "#162038",
253
+ // Accent
254
+ "--glyph-accent": "#00d4aa",
255
+ "--glyph-accent-hover": "#33e0be",
256
+ "--glyph-accent-subtle": "#0a1a1a",
257
+ "--glyph-accent-muted": "#1a4a3a",
258
+ // Code
259
+ "--glyph-code-bg": "#0f1526",
260
+ "--glyph-code-text": "#d4dae3",
261
+ // Blockquote
262
+ "--glyph-blockquote-border": "#00d4aa",
263
+ "--glyph-blockquote-bg": "#0a1a1a",
264
+ // Grid / Tooltip
265
+ "--glyph-grid": "#1a2035",
266
+ "--glyph-tooltip-bg": "rgba(0, 0, 0, 0.9)",
267
+ "--glyph-tooltip-text": "#d4dae3",
268
+ // Callouts
269
+ "--glyph-callout-info-bg": "#0a1526",
270
+ "--glyph-callout-info-border": "#38bdf8",
271
+ "--glyph-callout-warning-bg": "#1a1608",
272
+ "--glyph-callout-warning-border": "#fb923c",
273
+ "--glyph-callout-error-bg": "#1f0e0e",
274
+ "--glyph-callout-error-border": "#f87171",
275
+ "--glyph-callout-tip-bg": "#0a1a14",
276
+ "--glyph-callout-tip-border": "#22c55e",
277
+ // Spacing (same as light — spacing is mode-independent)
278
+ "--glyph-spacing-xs": "0.25rem",
279
+ "--glyph-spacing-sm": "0.5rem",
280
+ "--glyph-spacing-md": "1rem",
281
+ "--glyph-spacing-lg": "1.5rem",
282
+ "--glyph-spacing-xl": "2rem",
283
+ // Typography (same as light — font stacks are mode-independent)
284
+ "--glyph-font-body": '"Inter", "Helvetica Neue", system-ui, sans-serif',
285
+ "--glyph-font-heading": '"Inter", "Helvetica Neue", system-ui, sans-serif',
286
+ "--glyph-font-mono": 'ui-monospace, "Cascadia Code", "Fira Code", monospace',
287
+ // Border radius
288
+ "--glyph-radius-sm": "0.375rem",
289
+ "--glyph-radius-md": "0.5rem",
290
+ "--glyph-radius-lg": "0.75rem",
291
+ // Effects
292
+ "--glyph-shadow-sm": "0 1px 3px rgba(0,0,0,0.4)",
293
+ "--glyph-shadow-md": "0 4px 12px rgba(0,0,0,0.5)",
294
+ "--glyph-shadow-lg": "0 8px 30px rgba(0,0,0,0.6)",
295
+ "--glyph-shadow-glow": "0 0 15px rgba(0,212,170,0.3)",
296
+ "--glyph-text-shadow": "none",
297
+ "--glyph-backdrop": "none",
298
+ "--glyph-gradient-accent": "linear-gradient(135deg, #00d4aa, #00e5ff)",
299
+ "--glyph-transition": "0.2s ease",
300
+ "--glyph-opacity-muted": "0.7",
301
+ "--glyph-opacity-disabled": "0.4",
302
+ "--glyph-focus-ring": "0 0 0 2px #00d4aa",
303
+ // SVG / Data Visualization
304
+ "--glyph-node-fill-opacity": "0.85",
305
+ "--glyph-node-radius": "3",
306
+ "--glyph-node-stroke-width": "1.5",
307
+ "--glyph-node-label-color": "#fff",
308
+ "--glyph-edge-color": "#6b7a94",
309
+ "--glyph-icon-stroke": "#fff",
310
+ "--glyph-icon-stroke-width": "1.5"
311
+ }
312
+ };
313
+
314
+ // src/theme/resolve.ts
315
+ function resolveTheme(theme) {
316
+ if (!theme || theme === "light") {
317
+ return lightTheme;
318
+ }
319
+ if (theme === "dark") {
320
+ return darkTheme;
321
+ }
322
+ return theme;
323
+ }
324
+ function mergeThemeDefaults(theme, defaults) {
325
+ return {
326
+ ...theme,
327
+ variables: { ...defaults, ...theme.variables }
328
+ };
329
+ }
330
+ function createResolveVar(theme) {
331
+ return (varName) => theme.variables[varName] ?? "";
332
+ }
333
+ function isDarkTheme(theme) {
334
+ const bg = theme.variables["--glyph-bg"];
335
+ if (bg) {
336
+ const luminance = perceivedLuminance(bg);
337
+ if (luminance !== null) {
338
+ return luminance < 0.5;
339
+ }
340
+ }
341
+ return theme.name.toLowerCase().includes("dark");
342
+ }
343
+ function perceivedLuminance(hex) {
344
+ const trimmed = hex.trim();
345
+ if (!trimmed.startsWith("#")) return null;
346
+ let r;
347
+ let g;
348
+ let b;
349
+ if (trimmed.length === 4) {
350
+ const rChar = trimmed.charAt(1);
351
+ const gChar = trimmed.charAt(2);
352
+ const bChar = trimmed.charAt(3);
353
+ r = parseInt(rChar + rChar, 16);
354
+ g = parseInt(gChar + gChar, 16);
355
+ b = parseInt(bChar + bChar, 16);
356
+ } else if (trimmed.length === 7) {
357
+ r = parseInt(trimmed.slice(1, 3), 16);
358
+ g = parseInt(trimmed.slice(3, 5), 16);
359
+ b = parseInt(trimmed.slice(5, 7), 16);
360
+ } else {
361
+ return null;
362
+ }
363
+ if (isNaN(r) || isNaN(g) || isNaN(b)) return null;
364
+ return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255;
365
+ }
366
+ var ThemeContext = react.createContext(null);
367
+ function ThemeProvider({
368
+ theme,
369
+ className,
370
+ style: consumerStyle,
371
+ children
372
+ }) {
373
+ const resolved = react.useMemo(() => resolveTheme(theme), [theme]);
374
+ const themeContext = react.useMemo(
375
+ () => ({
376
+ name: resolved.name,
377
+ resolveVar: createResolveVar(resolved),
378
+ isDark: isDarkTheme(resolved)
379
+ }),
380
+ [resolved]
381
+ );
382
+ const style = react.useMemo(
383
+ () => ({ ...resolved.variables, ...consumerStyle }),
384
+ [resolved, consumerStyle]
385
+ );
386
+ return /* @__PURE__ */ jsxRuntime.jsx(ThemeContext, { value: themeContext, children: /* @__PURE__ */ jsxRuntime.jsx("div", { "data-glyph-theme": resolved.name, className, style, children }) });
387
+ }
388
+ function useGlyphTheme() {
389
+ const ctx = react.useContext(ThemeContext);
390
+ if (!ctx) {
391
+ throw new Error(
392
+ "useGlyphTheme() must be used within a <ThemeProvider> or <RuntimeProvider>. Did you forget to wrap your component tree?"
393
+ );
394
+ }
395
+ return ctx;
396
+ }
397
+ var noop = () => {
398
+ };
399
+ var RuntimeContext = react.createContext(null);
400
+ function RuntimeProvider({
401
+ registry,
402
+ references,
403
+ theme,
404
+ className,
405
+ style: consumerStyle,
406
+ onDiagnostic,
407
+ onNavigate,
408
+ children
409
+ }) {
410
+ const resolvedThemeObject = react.useMemo(() => resolveTheme(theme), [theme]);
411
+ const resolvedTheme = react.useMemo(
412
+ () => ({
413
+ name: resolvedThemeObject.name,
414
+ resolveVar: createResolveVar(resolvedThemeObject),
415
+ isDark: isDarkTheme(resolvedThemeObject)
416
+ }),
417
+ [resolvedThemeObject]
418
+ );
419
+ const value = react.useMemo(
420
+ () => ({
421
+ registry,
422
+ references,
423
+ theme: resolvedTheme,
424
+ onDiagnostic: onDiagnostic ?? noop,
425
+ onNavigate: onNavigate ?? noop
426
+ }),
427
+ [registry, references, resolvedTheme, onDiagnostic, onNavigate]
428
+ );
429
+ const style = react.useMemo(
430
+ () => ({ ...resolvedThemeObject.variables, ...consumerStyle }),
431
+ [resolvedThemeObject, consumerStyle]
432
+ );
433
+ return /* @__PURE__ */ jsxRuntime.jsx(RuntimeContext, { value, children: /* @__PURE__ */ jsxRuntime.jsx(ThemeContext, { value: resolvedTheme, children: /* @__PURE__ */ jsxRuntime.jsx("div", { "data-glyph-theme": resolvedThemeObject.name, className, style, children }) }) });
434
+ }
435
+ function useRuntime() {
436
+ const ctx = react.useContext(RuntimeContext);
437
+ if (!ctx) {
438
+ throw new Error(
439
+ "useRuntime() must be used within a <RuntimeProvider>. Did you forget to use createGlyphRuntime()?"
440
+ );
441
+ }
442
+ return ctx;
443
+ }
444
+ function useReferences(blockId) {
445
+ const { references } = useRuntime();
446
+ return react.useMemo(() => {
447
+ const incoming = [];
448
+ const outgoing = [];
449
+ for (const ref of references) {
450
+ if (ref.sourceBlockId === blockId) {
451
+ outgoing.push(ref);
452
+ }
453
+ if (ref.targetBlockId === blockId) {
454
+ incoming.push(ref);
455
+ }
456
+ if (ref.bidirectional) {
457
+ if (ref.targetBlockId === blockId && ref.sourceBlockId !== blockId) {
458
+ outgoing.push(ref);
459
+ }
460
+ if (ref.sourceBlockId === blockId && ref.targetBlockId !== blockId) {
461
+ incoming.push(ref);
462
+ }
463
+ }
464
+ }
465
+ return { incomingRefs: incoming, outgoingRefs: outgoing };
466
+ }, [references, blockId]);
467
+ }
468
+ var ErrorBoundary = class extends react.Component {
469
+ constructor(props) {
470
+ super(props);
471
+ this.state = { hasError: false, error: null };
472
+ }
473
+ static getDerivedStateFromError(error) {
474
+ return { hasError: true, error };
475
+ }
476
+ componentDidCatch(error, info) {
477
+ const { blockId, blockType, onDiagnostic } = this.props;
478
+ onDiagnostic({
479
+ severity: "error",
480
+ code: "RUNTIME_RENDER_ERROR",
481
+ message: `Error rendering block "${blockId}" (type: ${blockType}): ${error.message}`,
482
+ source: "runtime",
483
+ details: {
484
+ blockId,
485
+ blockType,
486
+ errorMessage: error.message,
487
+ componentStack: info.componentStack
488
+ }
489
+ });
490
+ }
491
+ render() {
492
+ if (this.state.hasError) {
493
+ const { blockId, blockType } = this.props;
494
+ const { error } = this.state;
495
+ return /* @__PURE__ */ jsxRuntime.jsxs(
496
+ "div",
497
+ {
498
+ style: {
499
+ border: "1px solid #e53e3e",
500
+ borderRadius: "4px",
501
+ padding: "8px 12px",
502
+ margin: "4px 0",
503
+ backgroundColor: "#fff5f5",
504
+ fontSize: "13px",
505
+ fontFamily: "monospace"
506
+ },
507
+ children: [
508
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { color: "#e53e3e", fontWeight: 600 }, children: [
509
+ 'Render error in block "',
510
+ blockId,
511
+ '" (',
512
+ blockType,
513
+ ")"
514
+ ] }),
515
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#742a2a", marginTop: "4px" }, children: error.message })
516
+ ]
517
+ }
518
+ );
519
+ }
520
+ return this.props.children;
521
+ }
522
+ };
523
+ function FallbackRenderer({ block }) {
524
+ let dataPreview;
525
+ try {
526
+ dataPreview = JSON.stringify(block.data, null, 2);
527
+ } catch {
528
+ dataPreview = "[Unable to serialize block data]";
529
+ }
530
+ return /* @__PURE__ */ jsxRuntime.jsxs(
531
+ "div",
532
+ {
533
+ style: {
534
+ border: "1px dashed #a0aec0",
535
+ borderRadius: "4px",
536
+ padding: "8px 12px",
537
+ margin: "4px 0",
538
+ backgroundColor: "#f7fafc",
539
+ fontSize: "12px",
540
+ fontFamily: "monospace",
541
+ color: "#718096"
542
+ },
543
+ children: [
544
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontWeight: 600, marginBottom: "4px" }, children: [
545
+ "Unknown block type: ",
546
+ block.type
547
+ ] }),
548
+ /* @__PURE__ */ jsxRuntime.jsx(
549
+ "pre",
550
+ {
551
+ style: {
552
+ margin: 0,
553
+ whiteSpace: "pre-wrap",
554
+ wordBreak: "break-word",
555
+ maxHeight: "200px",
556
+ overflow: "auto"
557
+ },
558
+ children: dataPreview
559
+ }
560
+ )
561
+ ]
562
+ }
563
+ );
564
+ }
565
+ var HIGHLIGHT_DURATION_MS = 1500;
566
+ var SCROLL_BEHAVIOR = "smooth";
567
+ var BLOCK_SELECTOR_PREFIX = "data-glyph-block";
568
+ function useNavigation() {
569
+ const { onNavigate, references } = useRuntime();
570
+ const announcerRef = react.useRef(null);
571
+ const navigateTo = react.useCallback(
572
+ (blockId, ref) => {
573
+ const el = document.querySelector(
574
+ `[${BLOCK_SELECTOR_PREFIX}="${blockId}"]`
575
+ );
576
+ if (!el || !(el instanceof HTMLElement)) {
577
+ return;
578
+ }
579
+ el.scrollIntoView({ behavior: SCROLL_BEHAVIOR, block: "center" });
580
+ el.setAttribute("data-glyph-highlight", "true");
581
+ setTimeout(() => {
582
+ el.removeAttribute("data-glyph-highlight");
583
+ }, HIGHLIGHT_DURATION_MS);
584
+ if (!el.hasAttribute("tabindex")) {
585
+ el.setAttribute("tabindex", "-1");
586
+ }
587
+ el.focus({ preventScroll: true });
588
+ announceNavigation(blockId, announcerRef);
589
+ if (ref) {
590
+ const targetBlock = {
591
+ id: blockId,
592
+ type: "paragraph",
593
+ data: {},
594
+ position: {
595
+ start: { line: 0, column: 0 },
596
+ end: { line: 0, column: 0 }
597
+ }
598
+ };
599
+ onNavigate(ref, targetBlock);
600
+ } else if (references.length > 0) {
601
+ const matchedRef = references.find(
602
+ (r) => r.targetBlockId === blockId || r.sourceBlockId === blockId
603
+ );
604
+ if (matchedRef) {
605
+ const targetBlock = {
606
+ id: blockId,
607
+ type: "paragraph",
608
+ data: {},
609
+ position: {
610
+ start: { line: 0, column: 0 },
611
+ end: { line: 0, column: 0 }
612
+ }
613
+ };
614
+ onNavigate(matchedRef, targetBlock);
615
+ }
616
+ }
617
+ },
618
+ [onNavigate, references]
619
+ );
620
+ return { navigateTo };
621
+ }
622
+ function announceNavigation(blockId, ref) {
623
+ let announcer = ref.current;
624
+ if (!announcer) {
625
+ announcer = document.createElement("div");
626
+ announcer.setAttribute("aria-live", "polite");
627
+ announcer.setAttribute("aria-atomic", "true");
628
+ announcer.setAttribute("role", "status");
629
+ Object.assign(announcer.style, {
630
+ position: "absolute",
631
+ width: "1px",
632
+ height: "1px",
633
+ padding: "0",
634
+ margin: "-1px",
635
+ overflow: "hidden",
636
+ clip: "rect(0, 0, 0, 0)",
637
+ whiteSpace: "nowrap",
638
+ border: "0"
639
+ });
640
+ document.body.appendChild(announcer);
641
+ ref.current = announcer;
642
+ }
643
+ announcer.textContent = `Navigated to block ${blockId}`;
644
+ }
645
+ var GLYPH_LINK_PREFIX = "#glyph:";
646
+ function GlyphLink({
647
+ blockId,
648
+ title,
649
+ children
650
+ }) {
651
+ const { navigateTo } = useNavigation();
652
+ const handleClick = react.useCallback(
653
+ (e) => {
654
+ e.preventDefault();
655
+ navigateTo(blockId);
656
+ },
657
+ [navigateTo, blockId]
658
+ );
659
+ return /* @__PURE__ */ jsxRuntime.jsx(
660
+ "a",
661
+ {
662
+ href: `${GLYPH_LINK_PREFIX}${blockId}`,
663
+ title,
664
+ onClick: handleClick,
665
+ "data-glyph-ref": blockId,
666
+ role: "link",
667
+ children
668
+ }
669
+ );
670
+ }
671
+ function isGlyphLink(url) {
672
+ return url.startsWith(GLYPH_LINK_PREFIX);
673
+ }
674
+ function extractBlockId(url) {
675
+ return url.slice(GLYPH_LINK_PREFIX.length);
676
+ }
677
+ function renderInlineNode(node, index) {
678
+ switch (node.type) {
679
+ case "text":
680
+ return node.value;
681
+ case "strong":
682
+ return /* @__PURE__ */ jsxRuntime.jsx("strong", { children: /* @__PURE__ */ jsxRuntime.jsx(InlineRenderer, { nodes: node.children }) }, index);
683
+ case "emphasis":
684
+ return /* @__PURE__ */ jsxRuntime.jsx("em", { children: /* @__PURE__ */ jsxRuntime.jsx(InlineRenderer, { nodes: node.children }) }, index);
685
+ case "delete":
686
+ return /* @__PURE__ */ jsxRuntime.jsx("del", { children: /* @__PURE__ */ jsxRuntime.jsx(InlineRenderer, { nodes: node.children }) }, index);
687
+ case "inlineCode":
688
+ return /* @__PURE__ */ jsxRuntime.jsx("code", { children: node.value }, index);
689
+ case "link":
690
+ if (isGlyphLink(node.url)) {
691
+ return /* @__PURE__ */ jsxRuntime.jsx(
692
+ GlyphLink,
693
+ {
694
+ blockId: extractBlockId(node.url),
695
+ title: node.title,
696
+ children: /* @__PURE__ */ jsxRuntime.jsx(InlineRenderer, { nodes: node.children })
697
+ },
698
+ index
699
+ );
700
+ }
701
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { href: node.url, title: node.title, children: /* @__PURE__ */ jsxRuntime.jsx(InlineRenderer, { nodes: node.children }) }, index);
702
+ case "image":
703
+ return /* @__PURE__ */ jsxRuntime.jsx("img", { src: node.src, alt: node.alt, title: node.title }, index);
704
+ case "break":
705
+ return /* @__PURE__ */ jsxRuntime.jsx("br", {}, index);
706
+ default:
707
+ return null;
708
+ }
709
+ }
710
+ function InlineRenderer({ nodes }) {
711
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: nodes.map((node, i) => renderInlineNode(node, i)) });
712
+ }
713
+ function extractText(nodes) {
714
+ return nodes.map((node) => {
715
+ switch (node.type) {
716
+ case "text":
717
+ return node.value;
718
+ case "inlineCode":
719
+ return node.value;
720
+ case "strong":
721
+ case "emphasis":
722
+ case "delete":
723
+ case "link":
724
+ return extractText(node.children);
725
+ default:
726
+ return "";
727
+ }
728
+ }).join("");
729
+ }
730
+ function slugify(text) {
731
+ return text.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_]+/g, "-").replace(/^-+|-+$/g, "");
732
+ }
733
+ function GlyphHeading({ block }) {
734
+ const data = block.data;
735
+ const Tag = `h${data.depth}`;
736
+ const id = slugify(extractText(data.children));
737
+ return /* @__PURE__ */ jsxRuntime.jsx(Tag, { id, children: /* @__PURE__ */ jsxRuntime.jsx(InlineRenderer, { nodes: data.children }) });
738
+ }
739
+ function GlyphParagraph({ block }) {
740
+ const data = block.data;
741
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { children: /* @__PURE__ */ jsxRuntime.jsx(InlineRenderer, { nodes: data.children }) });
742
+ }
743
+ function renderSubList(list) {
744
+ const items = list.items.map((item, i) => renderListItem(item, i));
745
+ if (list.ordered) {
746
+ return /* @__PURE__ */ jsxRuntime.jsx("ol", { start: list.start, children: items });
747
+ }
748
+ return /* @__PURE__ */ jsxRuntime.jsx("ul", { children: items });
749
+ }
750
+ function renderListItem(item, index) {
751
+ return /* @__PURE__ */ jsxRuntime.jsxs("li", { children: [
752
+ /* @__PURE__ */ jsxRuntime.jsx(InlineRenderer, { nodes: item.children }),
753
+ item.subList ? renderSubList(item.subList) : null
754
+ ] }, index);
755
+ }
756
+ function GlyphList({ block }) {
757
+ const data = block.data;
758
+ return renderSubList(data);
759
+ }
760
+ function GlyphCodeBlock({ block }) {
761
+ const data = block.data;
762
+ const language = data.language ?? void 0;
763
+ return /* @__PURE__ */ jsxRuntime.jsx("pre", { "data-language": language, "aria-label": language ? `Code block (${language})` : "Code block", children: /* @__PURE__ */ jsxRuntime.jsx("code", { className: language ? `language-${language}` : void 0, children: data.value }) });
764
+ }
765
+ function GlyphBlockquote({ block }) {
766
+ const data = block.data;
767
+ return /* @__PURE__ */ jsxRuntime.jsx("blockquote", { children: /* @__PURE__ */ jsxRuntime.jsx(InlineRenderer, { nodes: data.children }) });
768
+ }
769
+ function GlyphImage({ block }) {
770
+ const data = block.data;
771
+ return /* @__PURE__ */ jsxRuntime.jsxs("figure", { children: [
772
+ /* @__PURE__ */ jsxRuntime.jsx("img", { src: data.src, alt: data.alt ?? "", loading: "lazy" }),
773
+ data.title ? /* @__PURE__ */ jsxRuntime.jsx("figcaption", { children: data.title }) : null
774
+ ] });
775
+ }
776
+ function GlyphThematicBreak(_props) {
777
+ return /* @__PURE__ */ jsxRuntime.jsx("hr", {});
778
+ }
779
+ function GlyphRawHtml({ block }) {
780
+ const data = block.data;
781
+ const clean = DOMPurify__default.default.sanitize(data.value);
782
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { dangerouslySetInnerHTML: { __html: clean } });
783
+ }
784
+
785
+ // src/renderers/index.ts
786
+ var builtInRenderers = {
787
+ heading: GlyphHeading,
788
+ paragraph: GlyphParagraph,
789
+ list: GlyphList,
790
+ code: GlyphCodeBlock,
791
+ blockquote: GlyphBlockquote,
792
+ image: GlyphImage,
793
+ "thematic-break": GlyphThematicBreak,
794
+ html: GlyphRawHtml
795
+ };
796
+
797
+ // src/plugins/resolve-props.ts
798
+ function resolveComponentProps(block, definition, references, onNavigate, themeContext, layoutHints, containerContext) {
799
+ const parseResult = definition.schema.safeParse(block.data);
800
+ const data = parseResult.success ? parseResult.data : (() => {
801
+ const err = parseResult.error;
802
+ const issues = err != null && typeof err === "object" && "issues" in err && Array.isArray(err.issues) ? err.issues.map(
803
+ (i) => `${i.path.join(".")}: ${i.message}`
804
+ ).join("; ") : "unknown error";
805
+ console.warn(
806
+ `[GlyphJS] Schema validation failed for block "${block.id}" (${block.type}). Falling back to raw data. Issues: ${issues}`
807
+ );
808
+ return block.data;
809
+ })();
810
+ const outgoingRefs = [];
811
+ const incomingRefs = [];
812
+ for (const ref of references) {
813
+ if (ref.sourceBlockId === block.id) {
814
+ outgoingRefs.push(ref);
815
+ }
816
+ if (ref.targetBlockId === block.id) {
817
+ incomingRefs.push(ref);
818
+ }
819
+ if (ref.bidirectional) {
820
+ if (ref.targetBlockId === block.id && ref.sourceBlockId !== block.id) {
821
+ outgoingRefs.push(ref);
822
+ }
823
+ if (ref.sourceBlockId === block.id && ref.targetBlockId !== block.id) {
824
+ incomingRefs.push(ref);
825
+ }
826
+ }
827
+ }
828
+ return {
829
+ data,
830
+ block,
831
+ outgoingRefs,
832
+ incomingRefs,
833
+ onNavigate,
834
+ theme: themeContext,
835
+ layout: layoutHints,
836
+ container: containerContext
837
+ };
838
+ }
839
+ var defaultConfig = {
840
+ enabled: true,
841
+ duration: 300,
842
+ easing: "ease-out",
843
+ staggerDelay: 50
844
+ };
845
+ var AnimationContext = react.createContext(defaultConfig);
846
+ function AnimationProvider({
847
+ config,
848
+ children
849
+ }) {
850
+ const state = react.useMemo(
851
+ () => ({
852
+ ...defaultConfig,
853
+ ...config
854
+ }),
855
+ [config]
856
+ );
857
+ return /* @__PURE__ */ jsxRuntime.jsx(AnimationContext, { value: state, children });
858
+ }
859
+ function useAnimation() {
860
+ return react.useContext(AnimationContext);
861
+ }
862
+
863
+ // src/animation/useBlockAnimation.ts
864
+ function usePrefersReducedMotion() {
865
+ const [prefersReduced, setPrefersReduced] = react.useState(false);
866
+ react.useEffect(() => {
867
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
868
+ return;
869
+ }
870
+ const mql = window.matchMedia("(prefers-reduced-motion: reduce)");
871
+ setPrefersReduced(mql.matches);
872
+ const handler = (e) => {
873
+ setPrefersReduced(e.matches);
874
+ };
875
+ mql.addEventListener("change", handler);
876
+ return () => {
877
+ mql.removeEventListener("change", handler);
878
+ };
879
+ }, []);
880
+ return prefersReduced;
881
+ }
882
+ function useBlockAnimation(index) {
883
+ const config = useAnimation();
884
+ const prefersReduced = usePrefersReducedMotion();
885
+ const ref = react.useRef(null);
886
+ const [isVisible, setIsVisible] = react.useState(false);
887
+ const disabled = !config.enabled || prefersReduced;
888
+ react.useEffect(() => {
889
+ if (disabled) {
890
+ setIsVisible(true);
891
+ return;
892
+ }
893
+ const element = ref.current;
894
+ if (!element) {
895
+ return;
896
+ }
897
+ if (typeof IntersectionObserver === "undefined") {
898
+ setIsVisible(true);
899
+ return;
900
+ }
901
+ const observer = new IntersectionObserver(
902
+ (entries) => {
903
+ const entry = entries[0];
904
+ if (entry && entry.isIntersecting) {
905
+ setIsVisible(true);
906
+ observer.unobserve(element);
907
+ }
908
+ },
909
+ { threshold: 0.1 }
910
+ );
911
+ observer.observe(element);
912
+ return () => {
913
+ observer.disconnect();
914
+ };
915
+ }, [disabled]);
916
+ const style = react.useMemo(() => {
917
+ if (disabled) {
918
+ return {};
919
+ }
920
+ const delay = index * config.staggerDelay;
921
+ return isVisible ? {
922
+ opacity: 1,
923
+ transform: "translateY(0)",
924
+ transition: `opacity ${config.duration}ms ${config.easing} ${delay}ms, transform ${config.duration}ms ${config.easing} ${delay}ms`
925
+ } : {
926
+ opacity: 0,
927
+ transform: "translateY(10px)",
928
+ transition: `opacity ${config.duration}ms ${config.easing} ${delay}ms, transform ${config.duration}ms ${config.easing} ${delay}ms`
929
+ };
930
+ }, [disabled, isVisible, index, config.duration, config.easing, config.staggerDelay]);
931
+ return { ref, style, isVisible };
932
+ }
933
+ var containerStyle = {
934
+ display: "flex",
935
+ flexWrap: "wrap",
936
+ gap: "4px",
937
+ marginTop: "4px"
938
+ };
939
+ var badgeBaseStyle = {
940
+ display: "inline-flex",
941
+ alignItems: "center",
942
+ padding: "1px 6px",
943
+ borderRadius: "9999px",
944
+ fontSize: "11px",
945
+ lineHeight: "1.4",
946
+ fontFamily: "system-ui, -apple-system, sans-serif",
947
+ cursor: "pointer",
948
+ border: "1px solid",
949
+ textDecoration: "none",
950
+ transition: "opacity 150ms ease"
951
+ };
952
+ var outgoingBadgeStyle = {
953
+ ...badgeBaseStyle,
954
+ backgroundColor: "#eff6ff",
955
+ borderColor: "#bfdbfe",
956
+ color: "#1d4ed8"
957
+ };
958
+ var incomingBadgeStyle = {
959
+ ...badgeBaseStyle,
960
+ backgroundColor: "#f0fdf4",
961
+ borderColor: "#bbf7d0",
962
+ color: "#15803d"
963
+ };
964
+ function ReferenceIndicator({
965
+ blockId
966
+ }) {
967
+ const { incomingRefs, outgoingRefs } = useReferences(blockId);
968
+ const { navigateTo } = useNavigation();
969
+ const handleClick = react.useCallback(
970
+ (targetBlockId, ref) => {
971
+ navigateTo(targetBlockId, ref);
972
+ },
973
+ [navigateTo]
974
+ );
975
+ if (outgoingRefs.length === 0 && incomingRefs.length === 0) {
976
+ return null;
977
+ }
978
+ return /* @__PURE__ */ jsxRuntime.jsxs(
979
+ "nav",
980
+ {
981
+ "aria-label": `References for block ${blockId}`,
982
+ style: containerStyle,
983
+ children: [
984
+ outgoingRefs.map((ref) => {
985
+ const targetId = ref.sourceBlockId === blockId ? ref.targetBlockId : ref.sourceBlockId;
986
+ const label = ref.label ?? targetId;
987
+ return /* @__PURE__ */ jsxRuntime.jsxs(
988
+ "button",
989
+ {
990
+ type: "button",
991
+ style: outgoingBadgeStyle,
992
+ onClick: () => handleClick(targetId, ref),
993
+ "aria-label": `Navigate to referenced block: ${label}`,
994
+ title: `Go to: ${label} (${ref.type})`,
995
+ children: [
996
+ "\u2192 ",
997
+ label
998
+ ]
999
+ },
1000
+ `out-${ref.id}`
1001
+ );
1002
+ }),
1003
+ incomingRefs.map((ref) => {
1004
+ const sourceId = ref.targetBlockId === blockId ? ref.sourceBlockId : ref.targetBlockId;
1005
+ const label = ref.label ?? sourceId;
1006
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1007
+ "button",
1008
+ {
1009
+ type: "button",
1010
+ style: incomingBadgeStyle,
1011
+ onClick: () => handleClick(sourceId, ref),
1012
+ "aria-label": `Navigate to referencing block: ${label}`,
1013
+ title: `Referenced by: ${label} (${ref.type})`,
1014
+ children: [
1015
+ "\u2190 ",
1016
+ label
1017
+ ]
1018
+ },
1019
+ `in-${ref.id}`
1020
+ );
1021
+ })
1022
+ ]
1023
+ }
1024
+ );
1025
+ }
1026
+ function BlockDispatch({ block, layout, container }) {
1027
+ const { registry, references, theme, onNavigate } = useRuntime();
1028
+ const { incomingRefs, outgoingRefs } = useReferences(block.id);
1029
+ const hasRefs = incomingRefs.length > 0 || outgoingRefs.length > 0;
1030
+ if (block.type.startsWith("ui:")) {
1031
+ const componentName = block.type.slice(3);
1032
+ const schema = schemas.componentSchemas.get(componentName);
1033
+ if (schema) {
1034
+ const result = schema.safeParse(block.data);
1035
+ if (!result.success) {
1036
+ console.warn(
1037
+ `[GlyphJS] Schema validation failed for block "${block.id}" (${block.type}):`,
1038
+ result.error.issues
1039
+ );
1040
+ }
1041
+ }
1042
+ }
1043
+ let content;
1044
+ const overrideDef = registry.getOverride(block.type);
1045
+ if (overrideDef) {
1046
+ const Override = overrideDef;
1047
+ content = /* @__PURE__ */ jsxRuntime.jsx(Override, { block, layout });
1048
+ } else {
1049
+ const definition = registry.getRenderer(block.type);
1050
+ if (definition) {
1051
+ const handleNavigate = (ref) => {
1052
+ onNavigate(ref, block);
1053
+ };
1054
+ const props = resolveComponentProps(
1055
+ block,
1056
+ definition,
1057
+ references,
1058
+ handleNavigate,
1059
+ theme,
1060
+ layout,
1061
+ container
1062
+ );
1063
+ const Renderer = definition.render;
1064
+ content = /* @__PURE__ */ jsxRuntime.jsx(Renderer, { ...props });
1065
+ } else {
1066
+ const BuiltIn = builtInRenderers[block.type];
1067
+ if (BuiltIn) {
1068
+ content = /* @__PURE__ */ jsxRuntime.jsx(BuiltIn, { block, layout });
1069
+ } else {
1070
+ content = /* @__PURE__ */ jsxRuntime.jsx(FallbackRenderer, { block });
1071
+ }
1072
+ }
1073
+ }
1074
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-glyph-block": block.id, children: [
1075
+ content,
1076
+ hasRefs && /* @__PURE__ */ jsxRuntime.jsx(ReferenceIndicator, { blockId: block.id })
1077
+ ] });
1078
+ }
1079
+ function BlockRenderer({
1080
+ block,
1081
+ layout,
1082
+ index = 0,
1083
+ container
1084
+ }) {
1085
+ const { onDiagnostic } = useRuntime();
1086
+ const { ref, style } = useBlockAnimation(index);
1087
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, style, "data-glyph-block-anim": block.id, children: /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { blockId: block.id, blockType: block.type, onDiagnostic, children: /* @__PURE__ */ jsxRuntime.jsx(BlockDispatch, { block, layout, container }) }) });
1088
+ }
1089
+ var defaultLayout = {
1090
+ mode: "document",
1091
+ spacing: "normal"
1092
+ };
1093
+ var LayoutContext = react.createContext(defaultLayout);
1094
+ function LayoutProvider({
1095
+ layout,
1096
+ children
1097
+ }) {
1098
+ return /* @__PURE__ */ jsxRuntime.jsx(LayoutContext, { value: layout, children });
1099
+ }
1100
+ function useLayout() {
1101
+ return react.useContext(LayoutContext);
1102
+ }
1103
+ var spacingMap = {
1104
+ compact: "0.5rem",
1105
+ normal: "1rem",
1106
+ relaxed: "2rem"
1107
+ };
1108
+ function DocumentLayout({
1109
+ blocks,
1110
+ layout,
1111
+ renderBlock
1112
+ }) {
1113
+ const gap = spacingMap[layout.spacing ?? "normal"];
1114
+ const containerStyle2 = {
1115
+ maxWidth: layout.maxWidth ?? "none",
1116
+ margin: "0 auto",
1117
+ display: "flex",
1118
+ flexDirection: "column",
1119
+ gap
1120
+ };
1121
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: containerStyle2, "data-glyph-layout": "document", children: blocks.map((block, index) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderBlock(block, index) }, block.id)) });
1122
+ }
1123
+ function DashboardLayout({
1124
+ blocks,
1125
+ layout,
1126
+ renderBlock
1127
+ }) {
1128
+ const columns = layout.columns ?? 2;
1129
+ const containerStyle2 = {
1130
+ display: "grid",
1131
+ gridTemplateColumns: `repeat(${String(columns)}, 1fr)`,
1132
+ gap: "1rem"
1133
+ };
1134
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: containerStyle2, "data-glyph-layout": "dashboard", children: blocks.map((block, index) => {
1135
+ const override = layout.blockLayout?.[block.id];
1136
+ const cellStyle = {};
1137
+ if (override) {
1138
+ if (override.gridColumn) {
1139
+ cellStyle.gridColumn = override.gridColumn;
1140
+ }
1141
+ if (override.gridRow) {
1142
+ cellStyle.gridRow = override.gridRow;
1143
+ }
1144
+ if (override.span) {
1145
+ cellStyle.gridColumn = `span ${String(override.span)}`;
1146
+ }
1147
+ }
1148
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: cellStyle, children: renderBlock(block, index) }, block.id);
1149
+ }) });
1150
+ }
1151
+ function PresentationLayout({
1152
+ blocks,
1153
+ renderBlock
1154
+ }) {
1155
+ const [currentIndex, setCurrentIndex] = react.useState(0);
1156
+ const total = blocks.length;
1157
+ const goNext = react.useCallback(() => {
1158
+ setCurrentIndex((i) => Math.min(i + 1, total - 1));
1159
+ }, [total]);
1160
+ const goPrev = react.useCallback(() => {
1161
+ setCurrentIndex((i) => Math.max(i - 1, 0));
1162
+ }, []);
1163
+ react.useEffect(() => {
1164
+ function handleKeyDown(e) {
1165
+ switch (e.key) {
1166
+ case "ArrowRight":
1167
+ case "ArrowDown":
1168
+ case " ":
1169
+ e.preventDefault();
1170
+ goNext();
1171
+ break;
1172
+ case "ArrowLeft":
1173
+ case "ArrowUp":
1174
+ e.preventDefault();
1175
+ goPrev();
1176
+ break;
1177
+ }
1178
+ }
1179
+ window.addEventListener("keydown", handleKeyDown);
1180
+ return () => {
1181
+ window.removeEventListener("keydown", handleKeyDown);
1182
+ };
1183
+ }, [goNext, goPrev]);
1184
+ if (total === 0) {
1185
+ return null;
1186
+ }
1187
+ const currentBlock = blocks[currentIndex];
1188
+ const containerStyle2 = {
1189
+ position: "relative",
1190
+ width: "100vw",
1191
+ height: "100vh",
1192
+ overflow: "hidden"
1193
+ };
1194
+ const slideStyle = {
1195
+ width: "100%",
1196
+ height: "100%",
1197
+ display: "flex",
1198
+ alignItems: "center",
1199
+ justifyContent: "center",
1200
+ padding: "2rem",
1201
+ boxSizing: "border-box"
1202
+ };
1203
+ const indicatorStyle = {
1204
+ position: "absolute",
1205
+ bottom: "1rem",
1206
+ right: "1rem",
1207
+ fontSize: "0.875rem",
1208
+ color: "#718096",
1209
+ fontFamily: "sans-serif",
1210
+ userSelect: "none"
1211
+ };
1212
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: containerStyle2, "data-glyph-layout": "presentation", children: [
1213
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: slideStyle, children: currentBlock ? renderBlock(currentBlock, currentIndex) : null }),
1214
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: indicatorStyle, children: [
1215
+ String(currentIndex + 1),
1216
+ " / ",
1217
+ String(total)
1218
+ ] })
1219
+ ] });
1220
+ }
1221
+ var severityColors = {
1222
+ error: "#dc2626",
1223
+ warning: "#d97706",
1224
+ info: "#2563eb"
1225
+ };
1226
+ var severityBackgrounds = {
1227
+ error: "#fef2f2",
1228
+ warning: "#fffbeb",
1229
+ info: "#eff6ff"
1230
+ };
1231
+ var severityIcons = {
1232
+ error: "\u2716",
1233
+ // heavy multiplication sign
1234
+ warning: "\u26A0",
1235
+ // warning sign
1236
+ info: "\u2139"
1237
+ // information source
1238
+ };
1239
+ function formatPosition(diagnostic) {
1240
+ if (!diagnostic.position) return "";
1241
+ const { start } = diagnostic.position;
1242
+ return `${String(start.line)}:${String(start.column)}`;
1243
+ }
1244
+ var overlayStyle = {
1245
+ position: "fixed",
1246
+ bottom: "16px",
1247
+ right: "16px",
1248
+ maxWidth: "480px",
1249
+ maxHeight: "60vh",
1250
+ overflowY: "auto",
1251
+ backgroundColor: "#ffffff",
1252
+ border: "1px solid #e5e7eb",
1253
+ borderRadius: "8px",
1254
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
1255
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
1256
+ fontSize: "13px",
1257
+ zIndex: 9999
1258
+ };
1259
+ var headerStyle = {
1260
+ display: "flex",
1261
+ alignItems: "center",
1262
+ justifyContent: "space-between",
1263
+ padding: "10px 14px",
1264
+ borderBottom: "1px solid #e5e7eb",
1265
+ backgroundColor: "#f9fafb",
1266
+ borderRadius: "8px 8px 0 0"
1267
+ };
1268
+ var closeButtonStyle = {
1269
+ background: "none",
1270
+ border: "none",
1271
+ cursor: "pointer",
1272
+ fontSize: "16px",
1273
+ color: "#6b7280",
1274
+ padding: "2px 6px",
1275
+ borderRadius: "4px",
1276
+ lineHeight: 1
1277
+ };
1278
+ var itemStyle = {
1279
+ padding: "8px 14px",
1280
+ borderBottom: "1px solid #f3f4f6"
1281
+ };
1282
+ var itemHeaderStyle = {
1283
+ display: "flex",
1284
+ alignItems: "center",
1285
+ gap: "6px"
1286
+ };
1287
+ var codeStyle = {
1288
+ color: "#6b7280",
1289
+ fontSize: "11px"
1290
+ };
1291
+ var messageStyle = {
1292
+ marginTop: "2px",
1293
+ color: "#1f2937",
1294
+ lineHeight: "1.4"
1295
+ };
1296
+ var positionStyle = {
1297
+ color: "#9ca3af",
1298
+ fontSize: "11px",
1299
+ marginTop: "2px"
1300
+ };
1301
+ function DiagnosticsOverlay({
1302
+ diagnostics
1303
+ }) {
1304
+ const [dismissed, setDismissed] = react.useState(false);
1305
+ react.useEffect(() => {
1306
+ setDismissed(false);
1307
+ }, [diagnostics]);
1308
+ const handleDismiss = react.useCallback(() => {
1309
+ setDismissed(true);
1310
+ }, []);
1311
+ react.useEffect(() => {
1312
+ function handleKeyDown(e) {
1313
+ if (e.key === "Escape") {
1314
+ setDismissed(true);
1315
+ }
1316
+ }
1317
+ document.addEventListener("keydown", handleKeyDown);
1318
+ return () => {
1319
+ document.removeEventListener("keydown", handleKeyDown);
1320
+ };
1321
+ }, []);
1322
+ const summary = react.useMemo(() => {
1323
+ let errors = 0;
1324
+ let warnings = 0;
1325
+ let infos = 0;
1326
+ for (const d of diagnostics) {
1327
+ if (d.severity === "error") errors++;
1328
+ else if (d.severity === "warning") warnings++;
1329
+ else infos++;
1330
+ }
1331
+ const parts = [];
1332
+ if (errors > 0) parts.push(`${String(errors)} error${errors > 1 ? "s" : ""}`);
1333
+ if (warnings > 0)
1334
+ parts.push(`${String(warnings)} warning${warnings > 1 ? "s" : ""}`);
1335
+ if (infos > 0) parts.push(`${String(infos)} info`);
1336
+ return parts.join(", ");
1337
+ }, [diagnostics]);
1338
+ if (diagnostics.length === 0 || dismissed) {
1339
+ return null;
1340
+ }
1341
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1342
+ "div",
1343
+ {
1344
+ style: overlayStyle,
1345
+ "data-glyph-diagnostics-overlay": true,
1346
+ role: "complementary",
1347
+ "aria-label": "Diagnostics overlay",
1348
+ children: [
1349
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: headerStyle, children: [
1350
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600, color: "#1f2937" }, children: summary }),
1351
+ /* @__PURE__ */ jsxRuntime.jsx(
1352
+ "button",
1353
+ {
1354
+ type: "button",
1355
+ style: closeButtonStyle,
1356
+ onClick: handleDismiss,
1357
+ "aria-label": "Dismiss diagnostics",
1358
+ children: "\u2715"
1359
+ }
1360
+ )
1361
+ ] }),
1362
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children: diagnostics.map((diagnostic, index) => {
1363
+ const color = severityColors[diagnostic.severity];
1364
+ const bg = severityBackgrounds[diagnostic.severity];
1365
+ const icon = severityIcons[diagnostic.severity];
1366
+ const pos = formatPosition(diagnostic);
1367
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1368
+ "div",
1369
+ {
1370
+ style: { ...itemStyle, backgroundColor: bg },
1371
+ children: [
1372
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: itemHeaderStyle, children: [
1373
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color, fontWeight: 700 }, children: icon }),
1374
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color, fontWeight: 600 }, children: diagnostic.severity }),
1375
+ diagnostic.code && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: codeStyle, children: [
1376
+ "[",
1377
+ diagnostic.code,
1378
+ "]"
1379
+ ] })
1380
+ ] }),
1381
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: messageStyle, children: diagnostic.message }),
1382
+ pos && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: positionStyle, children: [
1383
+ "at ",
1384
+ pos
1385
+ ] })
1386
+ ]
1387
+ },
1388
+ `${diagnostic.code}-${String(index)}`
1389
+ );
1390
+ }) })
1391
+ ]
1392
+ }
1393
+ );
1394
+ }
1395
+ var severityColors2 = {
1396
+ error: "#dc2626",
1397
+ warning: "#d97706",
1398
+ info: "#2563eb"
1399
+ };
1400
+ var severityBackgrounds2 = {
1401
+ error: "#fef2f2",
1402
+ warning: "#fffbeb",
1403
+ info: "#eff6ff"
1404
+ };
1405
+ var severityIcons2 = {
1406
+ error: "\u2716",
1407
+ warning: "\u26A0",
1408
+ info: "\u2139"
1409
+ };
1410
+ function highestSeverity(diagnostics) {
1411
+ let has = "info";
1412
+ for (const d of diagnostics) {
1413
+ if (d.severity === "error") return "error";
1414
+ if (d.severity === "warning") has = "warning";
1415
+ }
1416
+ return has;
1417
+ }
1418
+ function formatPosition2(diagnostic) {
1419
+ if (!diagnostic.position) return "";
1420
+ const { start } = diagnostic.position;
1421
+ return `${String(start.line)}:${String(start.column)}`;
1422
+ }
1423
+ var wrapperStyle = {
1424
+ position: "relative",
1425
+ display: "inline-block"
1426
+ };
1427
+ var detailsStyle = {
1428
+ position: "absolute",
1429
+ top: "100%",
1430
+ left: 0,
1431
+ marginTop: "4px",
1432
+ minWidth: "280px",
1433
+ maxWidth: "400px",
1434
+ backgroundColor: "#ffffff",
1435
+ border: "1px solid #e5e7eb",
1436
+ borderRadius: "6px",
1437
+ boxShadow: "0 2px 8px rgba(0, 0, 0, 0.12)",
1438
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
1439
+ fontSize: "12px",
1440
+ zIndex: 9998,
1441
+ overflow: "hidden"
1442
+ };
1443
+ var detailItemStyle = {
1444
+ padding: "6px 10px",
1445
+ borderBottom: "1px solid #f3f4f6"
1446
+ };
1447
+ function BlockDiagnosticIndicator({
1448
+ diagnostics
1449
+ }) {
1450
+ const [expanded, setExpanded] = react.useState(false);
1451
+ const toggle = react.useCallback(() => {
1452
+ setExpanded((prev) => !prev);
1453
+ }, []);
1454
+ if (diagnostics.length === 0) {
1455
+ return null;
1456
+ }
1457
+ const severity = highestSeverity(diagnostics);
1458
+ const color = severityColors2[severity];
1459
+ const icon = severityIcons2[severity];
1460
+ const badgeStyle = {
1461
+ display: "inline-flex",
1462
+ alignItems: "center",
1463
+ justifyContent: "center",
1464
+ width: "20px",
1465
+ height: "20px",
1466
+ borderRadius: "50%",
1467
+ backgroundColor: color,
1468
+ color: "#ffffff",
1469
+ fontSize: "11px",
1470
+ fontWeight: 700,
1471
+ cursor: "pointer",
1472
+ border: "none",
1473
+ lineHeight: 1,
1474
+ padding: 0
1475
+ };
1476
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { style: wrapperStyle, "data-glyph-diagnostic-indicator": true, children: [
1477
+ /* @__PURE__ */ jsxRuntime.jsx(
1478
+ "button",
1479
+ {
1480
+ type: "button",
1481
+ style: badgeStyle,
1482
+ onClick: toggle,
1483
+ "aria-label": `${String(diagnostics.length)} diagnostic${diagnostics.length > 1 ? "s" : ""}`,
1484
+ "aria-expanded": expanded,
1485
+ title: `${String(diagnostics.length)} diagnostic${diagnostics.length > 1 ? "s" : ""}`,
1486
+ children: icon
1487
+ }
1488
+ ),
1489
+ expanded && /* @__PURE__ */ jsxRuntime.jsx("div", { style: detailsStyle, role: "tooltip", children: diagnostics.map((diagnostic, index) => {
1490
+ const dColor = severityColors2[diagnostic.severity];
1491
+ const bg = severityBackgrounds2[diagnostic.severity];
1492
+ const dIcon = severityIcons2[diagnostic.severity];
1493
+ const pos = formatPosition2(diagnostic);
1494
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1495
+ "div",
1496
+ {
1497
+ style: { ...detailItemStyle, backgroundColor: bg },
1498
+ children: [
1499
+ /* @__PURE__ */ jsxRuntime.jsxs(
1500
+ "div",
1501
+ {
1502
+ style: {
1503
+ display: "flex",
1504
+ alignItems: "center",
1505
+ gap: "4px"
1506
+ },
1507
+ children: [
1508
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: dColor, fontWeight: 700 }, children: dIcon }),
1509
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: dColor, fontWeight: 600 }, children: diagnostic.severity }),
1510
+ diagnostic.code && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { color: "#6b7280", fontSize: "10px" }, children: [
1511
+ "[",
1512
+ diagnostic.code,
1513
+ "]"
1514
+ ] })
1515
+ ]
1516
+ }
1517
+ ),
1518
+ /* @__PURE__ */ jsxRuntime.jsx(
1519
+ "div",
1520
+ {
1521
+ style: {
1522
+ color: "#1f2937",
1523
+ marginTop: "2px",
1524
+ lineHeight: "1.4"
1525
+ },
1526
+ children: diagnostic.message
1527
+ }
1528
+ ),
1529
+ pos && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { color: "#9ca3af", fontSize: "10px", marginTop: "2px" }, children: [
1530
+ "at ",
1531
+ pos
1532
+ ] })
1533
+ ]
1534
+ },
1535
+ `${diagnostic.code}-${String(index)}`
1536
+ );
1537
+ }) })
1538
+ ] });
1539
+ }
1540
+ function ContainerMeasure({ children, onMeasure }) {
1541
+ const ref = react.useRef(null);
1542
+ react.useEffect(() => {
1543
+ const el = ref.current;
1544
+ if (!el) return;
1545
+ const observer = new ResizeObserver((entries) => {
1546
+ for (const entry of entries) {
1547
+ if (entry.contentRect.width > 0) {
1548
+ onMeasure(entry.contentRect.width);
1549
+ }
1550
+ }
1551
+ });
1552
+ onMeasure(el.clientWidth);
1553
+ observer.observe(el);
1554
+ return () => observer.disconnect();
1555
+ }, [onMeasure]);
1556
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, style: { width: "100%" }, children });
1557
+ }
1558
+
1559
+ // src/container/breakpoints.ts
1560
+ var COMPACT_UP = 500;
1561
+ var COMPACT_DOWN = 484;
1562
+ var WIDE_UP = 900;
1563
+ var WIDE_DOWN = 884;
1564
+ function resolveTier(width, previous) {
1565
+ if (width === 0) return "wide";
1566
+ switch (previous) {
1567
+ case "compact":
1568
+ if (width >= WIDE_UP) return "wide";
1569
+ if (width >= COMPACT_UP) return "standard";
1570
+ return "compact";
1571
+ case "standard":
1572
+ if (width >= WIDE_UP) return "wide";
1573
+ if (width < COMPACT_DOWN) return "compact";
1574
+ return "standard";
1575
+ case "wide":
1576
+ if (width < COMPACT_DOWN) return "compact";
1577
+ if (width < WIDE_DOWN) return "standard";
1578
+ return "wide";
1579
+ }
1580
+ }
1581
+ function GlyphDocument({
1582
+ ir,
1583
+ className,
1584
+ animation,
1585
+ diagnostics
1586
+ }) {
1587
+ const { layout, blocks } = ir;
1588
+ const [containerWidth, setContainerWidth] = react.useState(0);
1589
+ const tierRef = react.useRef("wide");
1590
+ const containerTier = react.useMemo(() => {
1591
+ const next = resolveTier(containerWidth, tierRef.current);
1592
+ tierRef.current = next;
1593
+ return next;
1594
+ }, [containerWidth]);
1595
+ const container = react.useMemo(
1596
+ () => ({ width: containerWidth, tier: containerTier }),
1597
+ [containerWidth, containerTier]
1598
+ );
1599
+ const renderBlock = react.useCallback(
1600
+ (block, index) => /* @__PURE__ */ jsxRuntime.jsx(
1601
+ BlockRenderer,
1602
+ {
1603
+ block,
1604
+ layout,
1605
+ index,
1606
+ container
1607
+ },
1608
+ block.id
1609
+ ),
1610
+ [layout, container]
1611
+ );
1612
+ let content;
1613
+ switch (layout.mode) {
1614
+ case "dashboard":
1615
+ content = /* @__PURE__ */ jsxRuntime.jsx(DashboardLayout, { blocks, layout, renderBlock });
1616
+ break;
1617
+ case "presentation":
1618
+ content = /* @__PURE__ */ jsxRuntime.jsx(PresentationLayout, { blocks, renderBlock });
1619
+ break;
1620
+ case "document":
1621
+ default:
1622
+ content = /* @__PURE__ */ jsxRuntime.jsx(DocumentLayout, { blocks, layout, renderBlock });
1623
+ break;
1624
+ }
1625
+ return /* @__PURE__ */ jsxRuntime.jsx(AnimationProvider, { config: animation, children: /* @__PURE__ */ jsxRuntime.jsx(LayoutProvider, { layout, children: /* @__PURE__ */ jsxRuntime.jsx(ContainerMeasure, { onMeasure: setContainerWidth, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className, "data-glyph-document": ir.id, children: [
1626
+ content,
1627
+ diagnostics && diagnostics.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(DiagnosticsOverlay, { diagnostics })
1628
+ ] }) }) }) });
1629
+ }
1630
+ function createGlyphRuntime(config) {
1631
+ const registry = new PluginRegistry();
1632
+ if (config.components) {
1633
+ registry.registerAll(config.components);
1634
+ }
1635
+ if (config.overrides) {
1636
+ registry.setOverrides(config.overrides);
1637
+ }
1638
+ let currentTheme = config.theme;
1639
+ let registryVersion = 0;
1640
+ const listeners = /* @__PURE__ */ new Set();
1641
+ function notify() {
1642
+ for (const listener of listeners) {
1643
+ listener();
1644
+ }
1645
+ }
1646
+ function WrappedDocument({ ir, className }) {
1647
+ const [theme, setThemeState] = react.useState(currentTheme);
1648
+ const [, setVersion] = react.useState(registryVersion);
1649
+ const forceUpdate = react.useCallback(() => {
1650
+ setThemeState(currentTheme);
1651
+ setVersion(registryVersion);
1652
+ }, []);
1653
+ react.useMemo(() => {
1654
+ listeners.add(forceUpdate);
1655
+ return () => {
1656
+ listeners.delete(forceUpdate);
1657
+ };
1658
+ }, [forceUpdate]);
1659
+ return /* @__PURE__ */ jsxRuntime.jsx(
1660
+ RuntimeProvider,
1661
+ {
1662
+ registry,
1663
+ references: ir.references,
1664
+ theme,
1665
+ onDiagnostic: config.onDiagnostic,
1666
+ onNavigate: config.onNavigate,
1667
+ children: /* @__PURE__ */ jsxRuntime.jsx(GlyphDocument, { ir, className, animation: config.animation })
1668
+ }
1669
+ );
1670
+ }
1671
+ return {
1672
+ GlyphDocument: WrappedDocument,
1673
+ registerComponent(definition) {
1674
+ registry.registerComponent(definition);
1675
+ registryVersion++;
1676
+ notify();
1677
+ },
1678
+ setTheme(theme) {
1679
+ currentTheme = theme;
1680
+ notify();
1681
+ }
1682
+ };
1683
+ }
1684
+ function useIsClient() {
1685
+ const [isClient, setIsClient] = react.useState(false);
1686
+ react.useEffect(() => {
1687
+ setIsClient(true);
1688
+ }, []);
1689
+ return isClient;
1690
+ }
1691
+ function SSRPlaceholder({
1692
+ width = "100%",
1693
+ height = 300,
1694
+ className,
1695
+ children
1696
+ }) {
1697
+ const isClient = useIsClient();
1698
+ if (!isClient) {
1699
+ const style = {
1700
+ width,
1701
+ height,
1702
+ // Subtle background so the reserved space is visible in the layout
1703
+ backgroundColor: "var(--glyph-ssr-placeholder-bg, #f0f0f0)"
1704
+ };
1705
+ return /* @__PURE__ */ jsxRuntime.jsx(
1706
+ "div",
1707
+ {
1708
+ className,
1709
+ style,
1710
+ "data-ssr-placeholder": "true",
1711
+ "aria-hidden": "true"
1712
+ }
1713
+ );
1714
+ }
1715
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
1716
+ }
1717
+
1718
+ exports.AnimationContext = AnimationContext;
1719
+ exports.AnimationProvider = AnimationProvider;
1720
+ exports.BlockDiagnosticIndicator = BlockDiagnosticIndicator;
1721
+ exports.BlockRenderer = BlockRenderer;
1722
+ exports.ComponentRegistry = PluginRegistry;
1723
+ exports.ContainerMeasure = ContainerMeasure;
1724
+ exports.DashboardLayout = DashboardLayout;
1725
+ exports.DiagnosticsOverlay = DiagnosticsOverlay;
1726
+ exports.DocumentLayout = DocumentLayout;
1727
+ exports.ErrorBoundary = ErrorBoundary;
1728
+ exports.FallbackRenderer = FallbackRenderer;
1729
+ exports.GlyphBlockquote = GlyphBlockquote;
1730
+ exports.GlyphCodeBlock = GlyphCodeBlock;
1731
+ exports.GlyphDocument = GlyphDocument;
1732
+ exports.GlyphHeading = GlyphHeading;
1733
+ exports.GlyphImage = GlyphImage;
1734
+ exports.GlyphList = GlyphList;
1735
+ exports.GlyphParagraph = GlyphParagraph;
1736
+ exports.GlyphRawHtml = GlyphRawHtml;
1737
+ exports.GlyphThematicBreak = GlyphThematicBreak;
1738
+ exports.InlineRenderer = InlineRenderer;
1739
+ exports.LayoutProvider = LayoutProvider;
1740
+ exports.PluginRegistry = PluginRegistry;
1741
+ exports.PresentationLayout = PresentationLayout;
1742
+ exports.ReferenceIndicator = ReferenceIndicator;
1743
+ exports.RuntimeProvider = RuntimeProvider;
1744
+ exports.SSRPlaceholder = SSRPlaceholder;
1745
+ exports.ThemeProvider = ThemeProvider;
1746
+ exports.builtInRenderers = builtInRenderers;
1747
+ exports.createGlyphRuntime = createGlyphRuntime;
1748
+ exports.createResolveVar = createResolveVar;
1749
+ exports.darkTheme = darkTheme;
1750
+ exports.isDarkTheme = isDarkTheme;
1751
+ exports.lightTheme = lightTheme;
1752
+ exports.mergeThemeDefaults = mergeThemeDefaults;
1753
+ exports.resolveComponentProps = resolveComponentProps;
1754
+ exports.resolveTheme = resolveTheme;
1755
+ exports.resolveTier = resolveTier;
1756
+ exports.useAnimation = useAnimation;
1757
+ exports.useBlockAnimation = useBlockAnimation;
1758
+ exports.useGlyphTheme = useGlyphTheme;
1759
+ exports.useIsClient = useIsClient;
1760
+ exports.useLayout = useLayout;
1761
+ exports.useNavigation = useNavigation;
1762
+ exports.useReferences = useReferences;
1763
+ exports.useRuntime = useRuntime;
1764
+ exports.validateComponentDefinition = validateComponentDefinition;
1765
+ //# sourceMappingURL=index.cjs.map
1766
+ //# sourceMappingURL=index.cjs.map