nimbus-docs 0.1.5 → 0.1.7

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/react.js ADDED
@@ -0,0 +1,542 @@
1
+ import { Component, createContext, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useReducer, useRef, useState } from "react";
2
+ import { clsx } from "clsx";
3
+ import { twMerge } from "tailwind-merge";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+
6
+ //#region src/react/cn.ts
7
+ /**
8
+ * Compose class names with Tailwind conflict resolution. Vendored into
9
+ * lab/diagram so the framework-surface files don't depend on apps/www's
10
+ * `@/lib/cn` alias — keeps the lift to `nimbus-docs/react` verbatim.
11
+ */
12
+ function cn(...inputs) {
13
+ return twMerge(clsx(inputs));
14
+ }
15
+
16
+ //#endregion
17
+ //#region src/react/registry.ts
18
+ var DiagramRegistry = class {
19
+ entries = /* @__PURE__ */ new Map();
20
+ listeners = /* @__PURE__ */ new Set();
21
+ /** Bumped on every membership change — useSyncExternalStore snapshot key. */
22
+ version = 0;
23
+ register(entry) {
24
+ this.entries.set(entry.id, entry);
25
+ this.version++;
26
+ this.notify();
27
+ return () => {
28
+ this.entries.delete(entry.id);
29
+ this.version++;
30
+ this.notify();
31
+ };
32
+ }
33
+ toggleAll() {
34
+ for (const entry of this.entries.values()) entry.toggle();
35
+ }
36
+ count = () => this.entries.size;
37
+ getVersion = () => this.version;
38
+ subscribe = (listener) => {
39
+ this.listeners.add(listener);
40
+ return () => {
41
+ this.listeners.delete(listener);
42
+ };
43
+ };
44
+ notify() {
45
+ for (const listener of this.listeners) listener();
46
+ }
47
+ };
48
+ const diagramRegistry = new DiagramRegistry();
49
+
50
+ //#endregion
51
+ //#region src/react/diagram.tsx
52
+ function reducer(state, event) {
53
+ switch (event.type) {
54
+ case "READY": return state.ready ? state : {
55
+ ...state,
56
+ ready: true
57
+ };
58
+ case "VISIBILITY": return state.visible === event.visible ? state : {
59
+ ...state,
60
+ visible: event.visible
61
+ };
62
+ case "TAB_VISIBILITY": return state.tabVisible === event.tabVisible ? state : {
63
+ ...state,
64
+ tabVisible: event.tabVisible
65
+ };
66
+ case "REDUCED_MOTION": return state.reducedMotion === event.reduced ? state : {
67
+ ...state,
68
+ reducedMotion: event.reduced
69
+ };
70
+ case "TOGGLE": return {
71
+ ...state,
72
+ userPaused: !state.userPaused
73
+ };
74
+ }
75
+ }
76
+ function derivePhase(state, reducedMotionMode, pauseWhenOffscreen) {
77
+ if (!state.ready) return "idle";
78
+ if (reducedMotionMode === "respect" && state.reducedMotion) return "paused";
79
+ if (pauseWhenOffscreen && !state.visible) return "paused";
80
+ if (!state.tabVisible) return "paused";
81
+ if (state.userPaused) return "paused";
82
+ return "playing";
83
+ }
84
+ const DiagramContext = createContext(null);
85
+ function useDiagram() {
86
+ return useContext(DiagramContext);
87
+ }
88
+ const DEFAULT_CONTEXT = {
89
+ id: "__no_wrapper__",
90
+ phase: "playing",
91
+ playing: true,
92
+ visible: true,
93
+ tabVisible: true,
94
+ reducedMotion: false,
95
+ depth: "working",
96
+ theme: { id: "default" },
97
+ reset: () => {},
98
+ toggle: () => {}
99
+ };
100
+ const WARNED = /* @__PURE__ */ new Set();
101
+ /**
102
+ * Returns the wrapper context, or a default + one-time dev warning when
103
+ * called outside a `<Diagram>`. Used by every hook in nimbus-docs/react
104
+ * so authors can prototype without forgetting the wrapper.
105
+ */
106
+ function useDiagramOrDefault(hookName) {
107
+ const ctx = useContext(DiagramContext);
108
+ if (ctx) return ctx;
109
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "production" && !WARNED.has(hookName)) {
110
+ WARNED.add(hookName);
111
+ console.warn(`[nimbus-docs/react] ${hookName} called outside a <Diagram> wrapper. Defaults: playing=true, visible=true, reducedMotion=false. Wrap with <Diagram> for off-screen / reduced-motion / tab-hidden gating.`);
112
+ }
113
+ return DEFAULT_CONTEXT;
114
+ }
115
+ const MARGIN_ROOTMARGIN_VH = 2;
116
+ /**
117
+ * Debounce window for READY dispatch — only applied when scroll activity
118
+ * was observed in the last `SCROLL_VELOCITY_WINDOW_MS`. On a settled page
119
+ * (initial load, no scrolling), READY fires immediately.
120
+ *
121
+ * Lab finding: the spec's flat 500ms gate caused a visible delay on
122
+ * initial render. Scroll-aware gating preserves the mass-init protection
123
+ * for fast scrolls while keeping page-load latency at zero.
124
+ */
125
+ const SCROLL_IDLE_MS = 200;
126
+ const SCROLL_VELOCITY_WINDOW_MS = 200;
127
+ /**
128
+ * Catches render errors thrown by the diagram subtree. React 19 still
129
+ * requires class components for error boundaries — no hook equivalent
130
+ * yet. Falls back to a static "Diagram failed" message + a Reset button
131
+ * that both clears local error state AND bumps the outer resetKey so
132
+ * children remount fresh.
133
+ */
134
+ var DiagramErrorBoundary = class extends Component {
135
+ state = { error: null };
136
+ static getDerivedStateFromError(error) {
137
+ return { error };
138
+ }
139
+ componentDidCatch(error, info) {
140
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") console.error("[nimbus-docs/react] Diagram render error:", error, info);
141
+ }
142
+ handleReset = () => {
143
+ this.setState({ error: null });
144
+ this.props.onReset();
145
+ };
146
+ render() {
147
+ if (this.state.error) return /* @__PURE__ */ jsxs("div", {
148
+ role: "alert",
149
+ className: "flex flex-col items-start gap-2 rounded-md border border-red-300 bg-red-50 p-3 text-sm text-red-900 dark:border-red-800 dark:bg-red-950 dark:text-red-200",
150
+ children: [/* @__PURE__ */ jsx("span", {
151
+ className: "font-medium",
152
+ children: "Diagram failed to render."
153
+ }), /* @__PURE__ */ jsx("button", {
154
+ type: "button",
155
+ onClick: this.handleReset,
156
+ className: "rounded border border-red-400 px-2 py-0.5 text-xs font-medium hover:bg-red-100 dark:border-red-700 dark:hover:bg-red-900",
157
+ children: "Reset"
158
+ })]
159
+ });
160
+ return this.props.children;
161
+ }
162
+ };
163
+ /** Inline visually-hidden style — avoids depending on `.sr-only` Tailwind class. */
164
+ const SR_ONLY = {
165
+ position: "absolute",
166
+ width: 1,
167
+ height: 1,
168
+ padding: 0,
169
+ margin: -1,
170
+ overflow: "hidden",
171
+ clip: "rect(0,0,0,0)",
172
+ whiteSpace: "nowrap",
173
+ border: 0
174
+ };
175
+ /** True when the keypress originated from a native interactive element — skip our shortcut to let the element's own behaviour fire. */
176
+ function isInteractiveTarget(t) {
177
+ if (!t || !(t instanceof HTMLElement)) return false;
178
+ const tag = t.tagName;
179
+ if (tag === "BUTTON" || tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
180
+ if (tag === "A" && t.hasAttribute("href")) return true;
181
+ if (t.isContentEditable) return true;
182
+ return false;
183
+ }
184
+ function DiagramRoot(props) {
185
+ const { children, pauseWhenOffscreen = true, reducedMotion: reducedMotionMode = "respect", errorBoundary = true, keyboard = true, label, className } = props;
186
+ const id = useId();
187
+ const rootRef = useRef(null);
188
+ const [resetKey, setResetKey] = useState(0);
189
+ const [state, dispatch] = useReducer(reducer, {
190
+ ready: false,
191
+ visible: false,
192
+ tabVisible: true,
193
+ reducedMotion: false,
194
+ userPaused: false
195
+ });
196
+ useEffect(() => {
197
+ const doc = (rootRef.current?.ownerDocument.defaultView ?? window).document;
198
+ const onChange = () => dispatch({
199
+ type: "TAB_VISIBILITY",
200
+ tabVisible: doc.visibilityState === "visible"
201
+ });
202
+ onChange();
203
+ doc.addEventListener("visibilitychange", onChange);
204
+ return () => doc.removeEventListener("visibilitychange", onChange);
205
+ }, []);
206
+ useEffect(() => {
207
+ const mq = (rootRef.current?.ownerDocument.defaultView ?? window).matchMedia("(prefers-reduced-motion: reduce)");
208
+ const onChange = (e) => dispatch({
209
+ type: "REDUCED_MOTION",
210
+ reduced: e.matches
211
+ });
212
+ onChange(mq);
213
+ mq.addEventListener("change", onChange);
214
+ return () => mq.removeEventListener("change", onChange);
215
+ }, []);
216
+ useEffect(() => {
217
+ const el = rootRef.current;
218
+ if (!el || typeof IntersectionObserver === "undefined") return;
219
+ const win = el.ownerDocument.defaultView ?? window;
220
+ const vh = win.innerHeight || 800;
221
+ let lastScrollAt = 0;
222
+ const onScroll = () => {
223
+ lastScrollAt = Date.now();
224
+ };
225
+ win.addEventListener("scroll", onScroll, { passive: true });
226
+ let scrollIdleTimer = null;
227
+ let pendingReady = false;
228
+ const marginObserver = new IntersectionObserver((entries) => {
229
+ for (const entry of entries) if (entry.isIntersecting) {
230
+ if (Date.now() - lastScrollAt > SCROLL_VELOCITY_WINDOW_MS) {
231
+ dispatch({ type: "READY" });
232
+ continue;
233
+ }
234
+ pendingReady = true;
235
+ if (scrollIdleTimer) clearTimeout(scrollIdleTimer);
236
+ scrollIdleTimer = setTimeout(() => {
237
+ if (pendingReady) {
238
+ dispatch({ type: "READY" });
239
+ pendingReady = false;
240
+ }
241
+ }, SCROLL_IDLE_MS);
242
+ }
243
+ }, { rootMargin: `${MARGIN_ROOTMARGIN_VH * vh}px 0px ${MARGIN_ROOTMARGIN_VH * vh}px 0px` });
244
+ marginObserver.observe(el);
245
+ const directObserver = new IntersectionObserver((entries) => {
246
+ for (const entry of entries) dispatch({
247
+ type: "VISIBILITY",
248
+ visible: entry.isIntersecting
249
+ });
250
+ }, {
251
+ rootMargin: "0px",
252
+ threshold: [0, 1]
253
+ });
254
+ directObserver.observe(el);
255
+ return () => {
256
+ marginObserver.disconnect();
257
+ directObserver.disconnect();
258
+ win.removeEventListener("scroll", onScroll);
259
+ if (scrollIdleTimer) clearTimeout(scrollIdleTimer);
260
+ };
261
+ }, []);
262
+ const toggle = useCallback(() => dispatch({ type: "TOGGLE" }), []);
263
+ const reset = useCallback(() => setResetKey((k) => k + 1), []);
264
+ const handleKeyDown = useCallback((event) => {
265
+ if (!keyboard) return;
266
+ if (isInteractiveTarget(event.target)) return;
267
+ if (event.key === " " || event.code === "Space") {
268
+ event.preventDefault();
269
+ toggle();
270
+ } else if (event.key === "r" || event.key === "R") {
271
+ event.preventDefault();
272
+ reset();
273
+ }
274
+ }, [
275
+ keyboard,
276
+ toggle,
277
+ reset
278
+ ]);
279
+ useEffect(() => {
280
+ return diagramRegistry.register({
281
+ id,
282
+ toggle
283
+ });
284
+ }, [id, toggle]);
285
+ const phase = derivePhase(state, reducedMotionMode, pauseWhenOffscreen);
286
+ const playing = phase === "playing";
287
+ const reducedMotionEffective = reducedMotionMode === "respect" ? state.reducedMotion : false;
288
+ const visibleEffective = pauseWhenOffscreen ? state.visible : true;
289
+ const ctx = useMemo(() => ({
290
+ id,
291
+ phase,
292
+ playing,
293
+ visible: visibleEffective,
294
+ tabVisible: state.tabVisible,
295
+ reducedMotion: reducedMotionEffective,
296
+ depth: "working",
297
+ theme: { id: "default" },
298
+ reset,
299
+ toggle
300
+ }), [
301
+ id,
302
+ phase,
303
+ playing,
304
+ visibleEffective,
305
+ state.tabVisible,
306
+ reducedMotionEffective,
307
+ reset,
308
+ toggle
309
+ ]);
310
+ const liveText = phase === "playing" ? "Diagram playing" : phase === "paused" ? "Diagram paused" : "";
311
+ const renderBody = /* @__PURE__ */ jsx("div", {
312
+ className: "diagram-render",
313
+ children
314
+ }, resetKey);
315
+ return /* @__PURE__ */ jsx(DiagramContext.Provider, {
316
+ value: ctx,
317
+ children: /* @__PURE__ */ jsxs("div", {
318
+ ref: rootRef,
319
+ "data-nb-diagram": true,
320
+ "data-diagram-id": id,
321
+ "data-phase": phase,
322
+ "data-playing": String(playing),
323
+ "data-visible": String(visibleEffective),
324
+ "data-tab-visible": String(state.tabVisible),
325
+ "data-reduced-motion": String(reducedMotionEffective),
326
+ "aria-label": label,
327
+ role: "region",
328
+ onKeyDown: handleKeyDown,
329
+ className: cn("flex flex-col", className),
330
+ children: [/* @__PURE__ */ jsx("span", {
331
+ "aria-live": "polite",
332
+ role: "status",
333
+ style: SR_ONLY,
334
+ children: liveText
335
+ }), errorBoundary ? /* @__PURE__ */ jsx(DiagramErrorBoundary, {
336
+ onReset: reset,
337
+ children: renderBody
338
+ }) : renderBody]
339
+ })
340
+ });
341
+ }
342
+ const Diagram = DiagramRoot;
343
+
344
+ //#endregion
345
+ //#region src/react/hooks/use-phase.ts
346
+ /**
347
+ * Predicate-gated phase walker. Reads `playing` / `visible` / `tabVisible`
348
+ * / `reducedMotion` from the surrounding `<Diagram>` and pauses
349
+ * automatically when any gate fails.
350
+ *
351
+ * Steps may carry a `when(ctx)` predicate that filters them out for a
352
+ * given cycle. Mode toggles, branches, alternating loops — anything
353
+ * cycle-dependent — composes via the predicate plus user-supplied
354
+ * `context`.
355
+ */
356
+ function usePhase({ steps, context, loop = true }) {
357
+ const ctx = useDiagramOrDefault("usePhase");
358
+ const [cycle, setCycle] = useState(0);
359
+ const [index, setIndex] = useState(0);
360
+ const sequence = useMemo(() => {
361
+ const seq = [];
362
+ let prev = "";
363
+ const userContext = context ?? {};
364
+ for (const step of steps) {
365
+ const when = step.when;
366
+ if (!when || when({
367
+ cycle,
368
+ current: prev,
369
+ ...userContext
370
+ })) {
371
+ seq.push(step);
372
+ prev = step.id;
373
+ }
374
+ }
375
+ return seq;
376
+ }, [
377
+ cycle,
378
+ context,
379
+ steps
380
+ ]);
381
+ const currentStep = sequence[index] ?? sequence[sequence.length - 1] ?? null;
382
+ const currentId = currentStep?.id ?? "";
383
+ useEffect(() => {
384
+ if (!ctx.playing) return;
385
+ if (ctx.reducedMotion) return;
386
+ if (!currentStep || sequence.length === 0) return;
387
+ const t = setTimeout(() => {
388
+ if (index + 1 >= sequence.length) {
389
+ if (loop) {
390
+ setCycle((c) => c + 1);
391
+ setIndex(0);
392
+ }
393
+ } else setIndex((i) => i + 1);
394
+ }, currentStep.hold);
395
+ return () => clearTimeout(t);
396
+ }, [
397
+ index,
398
+ cycle,
399
+ ctx.playing,
400
+ ctx.reducedMotion,
401
+ currentStep,
402
+ sequence.length,
403
+ loop
404
+ ]);
405
+ return {
406
+ current: currentId,
407
+ index,
408
+ cycle,
409
+ advance: useCallback(() => {
410
+ if (index + 1 >= sequence.length) {
411
+ if (loop) {
412
+ setCycle((c) => c + 1);
413
+ setIndex(0);
414
+ }
415
+ } else setIndex((i) => i + 1);
416
+ }, [
417
+ index,
418
+ sequence.length,
419
+ loop
420
+ ]),
421
+ goto: useCallback((id) => {
422
+ const i = sequence.findIndex((s) => s.id === id);
423
+ if (i >= 0) setIndex(i);
424
+ }, [sequence]),
425
+ reset: useCallback(() => {
426
+ setCycle(0);
427
+ setIndex(0);
428
+ }, [])
429
+ };
430
+ }
431
+
432
+ //#endregion
433
+ //#region src/react/hooks/use-measure.ts
434
+ /**
435
+ * DOM rect + optional selector callback. The selector receives the
436
+ * observed element and its rect, and returns whatever derived geometry
437
+ * the author needs (NodeRect, EdgeData, etc.). Subsumes momito's
438
+ * `useWeldedMeasure` pattern.
439
+ *
440
+ * For multi-element measurements (e.g. measuring a container plus 5
441
+ * child nodes), pass the container ref and read the other refs from
442
+ * closure inside the selector. The selector recomputes when the
443
+ * container resizes — which it typically does when children grow
444
+ * because the container's intrinsic size is content-derived. For
445
+ * state-driven re-layouts that the ResizeObserver might miss, pass
446
+ * `options.deps`.
447
+ */
448
+ function useMeasure(ref, selector, options = {}) {
449
+ const { deps = [] } = options;
450
+ const [rect, setRect] = useState(null);
451
+ const [selected, setSelected] = useState(null);
452
+ const selectorRef = useRef(selector);
453
+ selectorRef.current = selector;
454
+ const sampleRef = useRef(() => {});
455
+ useLayoutEffect(() => {
456
+ const el = ref.current;
457
+ if (!el) return;
458
+ const sample = (r) => {
459
+ setRect(r);
460
+ const s = selectorRef.current;
461
+ if (s) setSelected(s(el, r));
462
+ };
463
+ sampleRef.current = () => {
464
+ sample(el.getBoundingClientRect());
465
+ };
466
+ const win = el.ownerDocument.defaultView ?? window;
467
+ if (typeof win.ResizeObserver === "undefined") {
468
+ sampleRef.current();
469
+ return;
470
+ }
471
+ const ro = new win.ResizeObserver((entries) => {
472
+ for (const entry of entries) sample(entry.contentRect);
473
+ });
474
+ ro.observe(el);
475
+ sampleRef.current();
476
+ return () => ro.disconnect();
477
+ }, [ref]);
478
+ useLayoutEffect(() => {
479
+ sampleRef.current();
480
+ }, deps);
481
+ return {
482
+ rect,
483
+ selected
484
+ };
485
+ }
486
+
487
+ //#endregion
488
+ //#region src/react/hooks/use-tab-indicator.ts
489
+ /**
490
+ * Headless sliding-indicator measurement for a tab group.
491
+ *
492
+ * Pure state + lifecycle — no DOM produced, no styling assumed. The
493
+ * consumer renders the indicator however it wants, applying `style`
494
+ * positionally.
495
+ *
496
+ * Re-measures on `activeId` change and on container resize. Returns
497
+ * `null` style when `activeId` is null or its tab ref isn't mounted —
498
+ * consumer renders nothing in that case, which also prevents a
499
+ * "mount-from-zero" transition the first time the indicator appears.
500
+ */
501
+ function useTabIndicator(containerRef, getTab, activeId) {
502
+ const [style, setStyle] = useState(null);
503
+ const measure = useCallback(() => {
504
+ const container = containerRef.current;
505
+ if (!container || activeId == null) {
506
+ setStyle(null);
507
+ return;
508
+ }
509
+ const btn = getTab(activeId);
510
+ if (!btn) {
511
+ setStyle(null);
512
+ return;
513
+ }
514
+ const cRect = container.getBoundingClientRect();
515
+ const bRect = btn.getBoundingClientRect();
516
+ setStyle({
517
+ top: bRect.top - cRect.top,
518
+ left: bRect.left - cRect.left,
519
+ width: bRect.width,
520
+ height: bRect.height
521
+ });
522
+ }, [
523
+ containerRef,
524
+ getTab,
525
+ activeId
526
+ ]);
527
+ useLayoutEffect(() => {
528
+ measure();
529
+ }, [measure]);
530
+ useEffect(() => {
531
+ const el = containerRef.current;
532
+ if (!el || typeof ResizeObserver === "undefined") return;
533
+ const ro = new ResizeObserver(() => measure());
534
+ ro.observe(el);
535
+ return () => ro.disconnect();
536
+ }, [containerRef, measure]);
537
+ return { style };
538
+ }
539
+
540
+ //#endregion
541
+ export { Diagram, cn, diagramRegistry, useDiagram, useDiagramOrDefault, useMeasure, usePhase, useTabIndicator };
542
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.js","names":[],"sources":["../src/react/cn.ts","../src/react/registry.ts","../src/react/diagram.tsx","../src/react/hooks/use-phase.ts","../src/react/hooks/use-measure.ts","../src/react/hooks/use-tab-indicator.ts"],"sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Compose class names with Tailwind conflict resolution. Vendored into\n * lab/diagram so the framework-surface files don't depend on apps/www's\n * `@/lib/cn` alias — keeps the lift to `nimbus-docs/react` verbatim.\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","/**\n * Page-scoped Diagram registry.\n *\n * Module-level singleton — shared across Astro islands because Vite\n * de-duplicates module imports per page (both in dev HMR and in prod\n * via shared chunks). React Context cannot bridge separate islands;\n * a plain singleton can.\n *\n * Subscribed via useSyncExternalStore from <DiagramPauseAll>.\n */\n\ninterface RegistryEntry {\n id: string;\n toggle: () => void;\n}\n\nclass DiagramRegistry {\n private entries = new Map<string, RegistryEntry>();\n private listeners = new Set<() => void>();\n /** Bumped on every membership change — useSyncExternalStore snapshot key. */\n private version = 0;\n\n register(entry: RegistryEntry): () => void {\n this.entries.set(entry.id, entry);\n this.version++;\n this.notify();\n return () => {\n this.entries.delete(entry.id);\n this.version++;\n this.notify();\n };\n }\n\n toggleAll(): void {\n for (const entry of this.entries.values()) {\n entry.toggle();\n }\n }\n\n // Arrow fields so these stay bound when passed to useSyncExternalStore.\n count = (): number => this.entries.size;\n\n getVersion = (): number => this.version;\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n };\n\n private notify(): void {\n for (const listener of this.listeners) listener();\n }\n}\n\nexport const diagramRegistry = new DiagramRegistry();\n","\"use client\";\n\nimport {\n Component,\n createContext,\n useCallback,\n useContext,\n useEffect,\n useId,\n useMemo,\n useReducer,\n useRef,\n useState,\n type CSSProperties,\n type ErrorInfo,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { cn } from \"./cn\";\nimport { diagramRegistry } from \"./registry\";\n\n// ─── Types ──────────────────────────────────────────────────\n\nexport type Depth = \"intuition\" | \"working\" | \"mechanism\";\nexport type ReducedMotionMode = \"respect\" | \"ignore\";\nexport type DiagramPhase = \"idle\" | \"ready\" | \"playing\" | \"paused\";\n\nexport interface DiagramContextValue {\n /** Stable per-instance id from useId. */\n id: string;\n /** Derived lifecycle state. */\n phase: DiagramPhase;\n /** True when the diagram should be advancing animation. */\n playing: boolean;\n /** Element intersects the viewport (direct IntersectionObserver). */\n visible: boolean;\n /** Browser tab is visible (Page Visibility API). */\n tabVisible: boolean;\n /** Reduced-motion is in effect (OS + reducedMotion=\"respect\" mode). */\n reducedMotion: boolean;\n /** Reader's depth setting. Stub until the depth dial ships. */\n depth: Depth;\n /** Theme tokens. Stub until row 4 wires real values. */\n theme: { id: string };\n /** Trigger a reset — bumps the subtree key; children's useState resets. */\n reset: () => void;\n /** Toggle play/pause from user action. */\n toggle: () => void;\n}\n\n// ─── Lifecycle reducer ──────────────────────────────────────\n\ninterface ReducerState {\n /** Margin-observer pre-warm has fired and scroll has settled. */\n ready: boolean;\n /** Direct IntersectionObserver says the diagram intersects the viewport. */\n visible: boolean;\n /** Page Visibility API says the tab is visible. */\n tabVisible: boolean;\n /** OS prefers-reduced-motion is set. */\n reducedMotion: boolean;\n /** User clicked Pause; persists across visibility changes. */\n userPaused: boolean;\n}\n\ntype ReducerEvent =\n | { type: \"READY\" }\n | { type: \"VISIBILITY\"; visible: boolean }\n | { type: \"TAB_VISIBILITY\"; tabVisible: boolean }\n | { type: \"REDUCED_MOTION\"; reduced: boolean }\n | { type: \"TOGGLE\" };\n\nfunction reducer(state: ReducerState, event: ReducerEvent): ReducerState {\n switch (event.type) {\n case \"READY\":\n return state.ready ? state : { ...state, ready: true };\n case \"VISIBILITY\":\n return state.visible === event.visible ? state : { ...state, visible: event.visible };\n case \"TAB_VISIBILITY\":\n return state.tabVisible === event.tabVisible\n ? state\n : { ...state, tabVisible: event.tabVisible };\n case \"REDUCED_MOTION\":\n return state.reducedMotion === event.reduced\n ? state\n : { ...state, reducedMotion: event.reduced };\n case \"TOGGLE\":\n return { ...state, userPaused: !state.userPaused };\n }\n}\n\nfunction derivePhase(\n state: ReducerState,\n reducedMotionMode: ReducedMotionMode,\n pauseWhenOffscreen: boolean,\n): DiagramPhase {\n if (!state.ready) return \"idle\";\n if (reducedMotionMode === \"respect\" && state.reducedMotion) return \"paused\";\n if (pauseWhenOffscreen && !state.visible) return \"paused\";\n if (!state.tabVisible) return \"paused\";\n if (state.userPaused) return \"paused\";\n return \"playing\";\n}\n\n// ─── Context ──────────────────────────────────────────────\n\nconst DiagramContext = createContext<DiagramContextValue | null>(null);\n\nexport function useDiagram(): DiagramContextValue | null {\n return useContext(DiagramContext);\n}\n\nconst DEFAULT_CONTEXT: DiagramContextValue = {\n id: \"__no_wrapper__\",\n phase: \"playing\",\n playing: true,\n visible: true,\n tabVisible: true,\n reducedMotion: false,\n depth: \"working\",\n theme: { id: \"default\" },\n reset: () => {},\n toggle: () => {},\n};\n\nconst WARNED = new Set<string>();\n\n/**\n * Returns the wrapper context, or a default + one-time dev warning when\n * called outside a `<Diagram>`. Used by every hook in nimbus-docs/react\n * so authors can prototype without forgetting the wrapper.\n */\nexport function useDiagramOrDefault(hookName: string): DiagramContextValue {\n const ctx = useContext(DiagramContext);\n if (ctx) return ctx;\n if (\n typeof process !== \"undefined\" &&\n process.env.NODE_ENV !== \"production\" &&\n !WARNED.has(hookName)\n ) {\n WARNED.add(hookName);\n // eslint-disable-next-line no-console\n console.warn(\n `[nimbus-docs/react] ${hookName} called outside a <Diagram> wrapper. ` +\n \"Defaults: playing=true, visible=true, reducedMotion=false. \" +\n \"Wrap with <Diagram> for off-screen / reduced-motion / tab-hidden gating.\",\n );\n }\n return DEFAULT_CONTEXT;\n}\n\n// ─── Diagram component ──────────────────────────────────────\n\nconst MARGIN_ROOTMARGIN_VH = 2;\n/**\n * Debounce window for READY dispatch — only applied when scroll activity\n * was observed in the last `SCROLL_VELOCITY_WINDOW_MS`. On a settled page\n * (initial load, no scrolling), READY fires immediately.\n *\n * Lab finding: the spec's flat 500ms gate caused a visible delay on\n * initial render. Scroll-aware gating preserves the mass-init protection\n * for fast scrolls while keeping page-load latency at zero.\n */\nconst SCROLL_IDLE_MS = 200;\nconst SCROLL_VELOCITY_WINDOW_MS = 200;\n\nexport interface DiagramProps {\n children: ReactNode;\n fallback?: string;\n pauseWhenOffscreen?: boolean;\n reducedMotion?: ReducedMotionMode;\n /** Forwarded; Astro's client:* directive dictates the actual hydration timing. */\n hydration?: \"visible\" | \"idle\" | \"load\";\n /** Wrap children in an error boundary that exposes a Reset button on render failure. Default: true. */\n errorBoundary?: boolean;\n /** Listen for space (toggle) / r (reset) keypresses bubbling from inside the region. Skipped when target is an interactive element. Default: true. */\n keyboard?: boolean;\n /** ARIA region label. */\n label?: string;\n className?: string;\n}\n\n// ─── Error boundary ─────────────────────────────────────────\n\ninterface ErrorBoundaryProps {\n children: ReactNode;\n /** Called when the user clicks Reset in the fallback. Should bump the resetKey on the keyed subtree. */\n onReset: () => void;\n}\n\ninterface ErrorBoundaryState {\n error: Error | null;\n}\n\n/**\n * Catches render errors thrown by the diagram subtree. React 19 still\n * requires class components for error boundaries — no hook equivalent\n * yet. Falls back to a static \"Diagram failed\" message + a Reset button\n * that both clears local error state AND bumps the outer resetKey so\n * children remount fresh.\n */\nclass DiagramErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {\n state: ErrorBoundaryState = { error: null };\n\n static getDerivedStateFromError(error: Error): ErrorBoundaryState {\n return { error };\n }\n\n componentDidCatch(error: Error, info: ErrorInfo): void {\n if (typeof process !== \"undefined\" && process.env.NODE_ENV !== \"production\") {\n // eslint-disable-next-line no-console\n console.error(\"[nimbus-docs/react] Diagram render error:\", error, info);\n }\n }\n\n private handleReset = (): void => {\n this.setState({ error: null });\n this.props.onReset();\n };\n\n render(): ReactNode {\n if (this.state.error) {\n return (\n <div\n role=\"alert\"\n className=\"flex flex-col items-start gap-2 rounded-md border border-red-300 bg-red-50 p-3 text-sm text-red-900 dark:border-red-800 dark:bg-red-950 dark:text-red-200\"\n >\n <span className=\"font-medium\">Diagram failed to render.</span>\n <button\n type=\"button\"\n onClick={this.handleReset}\n className=\"rounded border border-red-400 px-2 py-0.5 text-xs font-medium hover:bg-red-100 dark:border-red-700 dark:hover:bg-red-900\"\n >\n Reset\n </button>\n </div>\n );\n }\n return this.props.children;\n }\n}\n\n// ─── A11y helpers ────────────────────────────────────────────\n\n/** Inline visually-hidden style — avoids depending on `.sr-only` Tailwind class. */\nconst SR_ONLY: CSSProperties = {\n position: \"absolute\",\n width: 1,\n height: 1,\n padding: 0,\n margin: -1,\n overflow: \"hidden\",\n clip: \"rect(0,0,0,0)\",\n whiteSpace: \"nowrap\",\n border: 0,\n};\n\n/** True when the keypress originated from a native interactive element — skip our shortcut to let the element's own behaviour fire. */\nfunction isInteractiveTarget(t: EventTarget | null): boolean {\n if (!t || !(t instanceof HTMLElement)) return false;\n const tag = t.tagName;\n if (tag === \"BUTTON\" || tag === \"INPUT\" || tag === \"TEXTAREA\" || tag === \"SELECT\") {\n return true;\n }\n if (tag === \"A\" && t.hasAttribute(\"href\")) return true;\n if (t.isContentEditable) return true;\n return false;\n}\n\nfunction DiagramRoot(props: DiagramProps) {\n const {\n children,\n pauseWhenOffscreen = true,\n reducedMotion: reducedMotionMode = \"respect\",\n errorBoundary = true,\n keyboard = true,\n label,\n className,\n } = props;\n\n const id = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const [resetKey, setResetKey] = useState(0);\n\n const [state, dispatch] = useReducer(reducer, {\n ready: false,\n visible: false,\n tabVisible: true,\n reducedMotion: false,\n userPaused: false,\n });\n\n // ─── visibilitychange (tab visibility) — env-aware globals\n useEffect(() => {\n const win = rootRef.current?.ownerDocument.defaultView ?? window;\n const doc = win.document;\n const onChange = () =>\n dispatch({ type: \"TAB_VISIBILITY\", tabVisible: doc.visibilityState === \"visible\" });\n onChange();\n doc.addEventListener(\"visibilitychange\", onChange);\n return () => doc.removeEventListener(\"visibilitychange\", onChange);\n }, []);\n\n // ─── prefers-reduced-motion — env-aware globals\n useEffect(() => {\n const win = rootRef.current?.ownerDocument.defaultView ?? window;\n const mq = win.matchMedia(\"(prefers-reduced-motion: reduce)\");\n const onChange = (e: MediaQueryListEvent | MediaQueryList) =>\n dispatch({ type: \"REDUCED_MOTION\", reduced: e.matches });\n onChange(mq);\n mq.addEventListener(\"change\", onChange);\n return () => mq.removeEventListener(\"change\", onChange);\n }, []);\n\n // ─── Two IntersectionObservers (pre-warm + direct)\n useEffect(() => {\n const el = rootRef.current;\n if (!el || typeof IntersectionObserver === \"undefined\") return;\n const win = el.ownerDocument.defaultView ?? window;\n const vh = win.innerHeight || 800;\n\n // Scroll-velocity tracker. The margin observer debounces READY only\n // when scroll happened recently; on a settled page, READY fires\n // immediately.\n let lastScrollAt = 0;\n const onScroll = () => {\n lastScrollAt = Date.now();\n };\n win.addEventListener(\"scroll\", onScroll, { passive: true });\n\n let scrollIdleTimer: ReturnType<typeof setTimeout> | null = null;\n let pendingReady = false;\n\n const marginObserver = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n const sinceScroll = Date.now() - lastScrollAt;\n if (sinceScroll > SCROLL_VELOCITY_WINDOW_MS) {\n dispatch({ type: \"READY\" });\n continue;\n }\n pendingReady = true;\n if (scrollIdleTimer) clearTimeout(scrollIdleTimer);\n scrollIdleTimer = setTimeout(() => {\n if (pendingReady) {\n dispatch({ type: \"READY\" });\n pendingReady = false;\n }\n }, SCROLL_IDLE_MS);\n }\n }\n },\n { rootMargin: `${MARGIN_ROOTMARGIN_VH * vh}px 0px ${MARGIN_ROOTMARGIN_VH * vh}px 0px` },\n );\n marginObserver.observe(el);\n\n const directObserver = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n dispatch({ type: \"VISIBILITY\", visible: entry.isIntersecting });\n }\n },\n { rootMargin: \"0px\", threshold: [0, 1.0] },\n );\n directObserver.observe(el);\n\n return () => {\n marginObserver.disconnect();\n directObserver.disconnect();\n win.removeEventListener(\"scroll\", onScroll);\n if (scrollIdleTimer) clearTimeout(scrollIdleTimer);\n };\n }, []);\n\n // ─── Event handlers (not effects — see React design rules)\n const toggle = useCallback(() => dispatch({ type: \"TOGGLE\" }), []);\n const reset = useCallback(() => setResetKey((k) => k + 1), []);\n\n const handleKeyDown = useCallback(\n (event: ReactKeyboardEvent<HTMLDivElement>) => {\n if (!keyboard) return;\n if (isInteractiveTarget(event.target)) return;\n if (event.key === \" \" || event.code === \"Space\") {\n event.preventDefault();\n toggle();\n } else if (event.key === \"r\" || event.key === \"R\") {\n event.preventDefault();\n reset();\n }\n },\n [keyboard, toggle, reset],\n );\n\n // ─── Page-scoped registry membership\n useEffect(() => {\n return diagramRegistry.register({ id, toggle });\n }, [id, toggle]);\n\n // ─── Derived\n const phase = derivePhase(state, reducedMotionMode, pauseWhenOffscreen);\n const playing = phase === \"playing\";\n const reducedMotionEffective =\n reducedMotionMode === \"respect\" ? state.reducedMotion : false;\n const visibleEffective = pauseWhenOffscreen ? state.visible : true;\n\n // ─── Context value (useMemo for re-render perf only, not for semantics)\n const ctx: DiagramContextValue = useMemo(\n () => ({\n id,\n phase,\n playing,\n visible: visibleEffective,\n tabVisible: state.tabVisible,\n reducedMotion: reducedMotionEffective,\n depth: \"working\",\n theme: { id: \"default\" },\n reset,\n toggle,\n }),\n [id, phase, playing, visibleEffective, state.tabVisible, reducedMotionEffective, reset, toggle],\n );\n\n // Live-region content: only narrate the two states a reader cares about\n // (\"playing\", \"paused\"). Idle/ready render as empty string — aria-live\n // doesn't announce initial content, only changes, so silence on mount.\n const liveText =\n phase === \"playing\" ? \"Diagram playing\" :\n phase === \"paused\" ? \"Diagram paused\" :\n \"\";\n\n const renderBody = (\n <div className=\"diagram-render\" key={resetKey}>\n {children}\n </div>\n );\n\n return (\n <DiagramContext.Provider value={ctx}>\n <div\n ref={rootRef}\n data-nb-diagram\n data-diagram-id={id}\n data-phase={phase}\n data-playing={String(playing)}\n data-visible={String(visibleEffective)}\n data-tab-visible={String(state.tabVisible)}\n data-reduced-motion={String(reducedMotionEffective)}\n aria-label={label}\n role=\"region\"\n onKeyDown={handleKeyDown}\n className={cn(\"flex flex-col\", className)}\n >\n <span aria-live=\"polite\" role=\"status\" style={SR_ONLY}>\n {liveText}\n </span>\n {errorBoundary ? (\n <DiagramErrorBoundary onReset={reset}>{renderBody}</DiagramErrorBoundary>\n ) : (\n renderBody\n )}\n </div>\n </DiagramContext.Provider>\n );\n}\n\n// ─── Public component ───────────────────────────────────────\n\nexport const Diagram = DiagramRoot;\n","\"use client\";\n\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { useDiagramOrDefault } from \"../diagram\";\n\nexport interface UsePhaseStepBase {\n /** Stable step id; appears as `current`. */\n id: string;\n /** Milliseconds to hold this step before advancing. */\n hold: number;\n}\n\nexport interface UsePhaseStep<C extends Record<string, unknown> = Record<string, never>>\n extends UsePhaseStepBase {\n /**\n * Predicate gate. When false, this step is skipped for the current cycle.\n * Receives `{ cycle, current, ...context }` where `current` is the id of\n * the previous step that survived the predicate pass (empty string at\n * cycle start).\n */\n when?: (ctx: { cycle: number; current: string } & C) => boolean;\n}\n\nexport interface UsePhaseOptions<C extends Record<string, unknown> = Record<string, never>> {\n steps: UsePhaseStep<C>[];\n /** Values made available to each step's `when` predicate. */\n context?: C;\n /** When true (default), the sequence restarts after the last step. */\n loop?: boolean;\n}\n\nexport interface UsePhaseReturn {\n /** Id of the currently-active step. Empty string if `steps` is empty. */\n current: string;\n /** Index into the *filtered* sequence for the current cycle. */\n index: number;\n /** How many times the sequence has fully completed. Starts at 0. */\n cycle: number;\n /** Advance one step (or wrap to next cycle if at the end). */\n advance: () => void;\n /** Jump to a named step in the current cycle's sequence. */\n goto: (id: string) => void;\n /** Reset cycle and index to 0. */\n reset: () => void;\n}\n\n/**\n * Predicate-gated phase walker. Reads `playing` / `visible` / `tabVisible`\n * / `reducedMotion` from the surrounding `<Diagram>` and pauses\n * automatically when any gate fails.\n *\n * Steps may carry a `when(ctx)` predicate that filters them out for a\n * given cycle. Mode toggles, branches, alternating loops — anything\n * cycle-dependent — composes via the predicate plus user-supplied\n * `context`.\n */\nexport function usePhase<C extends Record<string, unknown> = Record<string, never>>({\n steps,\n context,\n loop = true,\n}: UsePhaseOptions<C>): UsePhaseReturn {\n const ctx = useDiagramOrDefault(\"usePhase\");\n\n const [cycle, setCycle] = useState(0);\n const [index, setIndex] = useState(0);\n\n // Build the filtered sequence for the current cycle. The `current` field\n // in each predicate's ctx is the id of the previous step that survived\n // (empty string at cycle start).\n const sequence = useMemo(() => {\n const seq: UsePhaseStep<C>[] = [];\n let prev = \"\";\n const userContext = context ?? ({} as C);\n for (const step of steps) {\n const when = step.when;\n if (!when || when({ cycle, current: prev, ...userContext })) {\n seq.push(step);\n prev = step.id;\n }\n }\n return seq;\n }, [cycle, context, steps]);\n\n const currentStep = sequence[index] ?? sequence[sequence.length - 1] ?? null;\n const currentId = currentStep?.id ?? \"\";\n\n // External-system sync: setTimeout-based scheduler. Pauses when any gate\n // fails; reduced-motion freezes by also gating advancement.\n useEffect(() => {\n if (!ctx.playing) return;\n if (ctx.reducedMotion) return;\n if (!currentStep || sequence.length === 0) return;\n\n const t = setTimeout(() => {\n if (index + 1 >= sequence.length) {\n if (loop) {\n setCycle((c) => c + 1);\n setIndex(0);\n }\n } else {\n setIndex((i) => i + 1);\n }\n }, currentStep.hold);\n\n return () => clearTimeout(t);\n }, [index, cycle, ctx.playing, ctx.reducedMotion, currentStep, sequence.length, loop]);\n\n // Event handlers (not effects).\n const advance = useCallback(() => {\n if (index + 1 >= sequence.length) {\n if (loop) {\n setCycle((c) => c + 1);\n setIndex(0);\n }\n } else {\n setIndex((i) => i + 1);\n }\n }, [index, sequence.length, loop]);\n\n const goto = useCallback(\n (id: string) => {\n const i = sequence.findIndex((s) => s.id === id);\n if (i >= 0) setIndex(i);\n },\n [sequence],\n );\n\n const reset = useCallback(() => {\n setCycle(0);\n setIndex(0);\n }, []);\n\n return {\n current: currentId,\n index,\n cycle,\n advance,\n goto,\n reset,\n };\n}\n","\"use client\";\n\nimport {\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type RefObject,\n} from \"react\";\n\nexport interface UseMeasureOptions {\n /**\n * Additional dependencies that should re-trigger measurement when they\n * change. Use when component state changes the layout in a way the\n * container's ResizeObserver might miss — e.g. an animated CSS `gap`\n * that grows the container only as the transition progresses, or a\n * state-driven layout swap that doesn't change the container's size.\n */\n deps?: unknown[];\n}\n\n/**\n * DOM rect + optional selector callback. The selector receives the\n * observed element and its rect, and returns whatever derived geometry\n * the author needs (NodeRect, EdgeData, etc.). Subsumes momito's\n * `useWeldedMeasure` pattern.\n *\n * For multi-element measurements (e.g. measuring a container plus 5\n * child nodes), pass the container ref and read the other refs from\n * closure inside the selector. The selector recomputes when the\n * container resizes — which it typically does when children grow\n * because the container's intrinsic size is content-derived. For\n * state-driven re-layouts that the ResizeObserver might miss, pass\n * `options.deps`.\n */\nexport function useMeasure<T extends HTMLElement | SVGElement, R = DOMRectReadOnly>(\n ref: RefObject<T | null>,\n selector?: (el: T, rect: DOMRectReadOnly) => R,\n options: UseMeasureOptions = {},\n): { rect: DOMRectReadOnly | null; selected: R | null } {\n const { deps = [] } = options;\n const [rect, setRect] = useState<DOMRectReadOnly | null>(null);\n const [selected, setSelected] = useState<R | null>(null);\n\n // Pin the latest selector to a ref so the observer effect doesn't\n // re-bind on every render when selector identity changes.\n const selectorRef = useRef(selector);\n selectorRef.current = selector;\n\n // Manual re-sample, used by both the observer effect and the deps\n // layout-effect below.\n const sampleRef = useRef<() => void>(() => {});\n\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n\n const sample = (r: DOMRectReadOnly) => {\n setRect(r);\n const s = selectorRef.current;\n if (s) setSelected(s(el, r));\n };\n\n sampleRef.current = () => {\n const r = el.getBoundingClientRect();\n sample(r);\n };\n\n const win = el.ownerDocument.defaultView ?? window;\n if (typeof win.ResizeObserver === \"undefined\") {\n sampleRef.current();\n return;\n }\n\n const ro = new win.ResizeObserver((entries) => {\n for (const entry of entries) sample(entry.contentRect);\n });\n ro.observe(el);\n\n // Synchronous initial sample so selector results land on first paint.\n sampleRef.current();\n\n return () => ro.disconnect();\n }, [ref]);\n\n // Re-sample when caller-supplied deps change.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(() => {\n sampleRef.current();\n }, deps);\n\n return { rect, selected };\n}\n","\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useState,\n type RefObject,\n} from \"react\";\n\nexport interface UseTabIndicatorReturn {\n /** Inline rect of the active tab, relative to the container. Null until first measure. */\n style: { top: number; left: number; width: number; height: number } | null;\n}\n\n/**\n * Headless sliding-indicator measurement for a tab group.\n *\n * Pure state + lifecycle — no DOM produced, no styling assumed. The\n * consumer renders the indicator however it wants, applying `style`\n * positionally.\n *\n * Re-measures on `activeId` change and on container resize. Returns\n * `null` style when `activeId` is null or its tab ref isn't mounted —\n * consumer renders nothing in that case, which also prevents a\n * \"mount-from-zero\" transition the first time the indicator appears.\n */\nexport function useTabIndicator(\n containerRef: RefObject<HTMLElement | null>,\n getTab: (id: string) => HTMLElement | null,\n activeId: string | null,\n): UseTabIndicatorReturn {\n const [style, setStyle] = useState<UseTabIndicatorReturn[\"style\"]>(null);\n\n const measure = useCallback(() => {\n const container = containerRef.current;\n if (!container || activeId == null) {\n setStyle(null);\n return;\n }\n const btn = getTab(activeId);\n if (!btn) {\n setStyle(null);\n return;\n }\n const cRect = container.getBoundingClientRect();\n const bRect = btn.getBoundingClientRect();\n setStyle({\n top: bRect.top - cRect.top,\n left: bRect.left - cRect.left,\n width: bRect.width,\n height: bRect.height,\n });\n }, [containerRef, getTab, activeId]);\n\n useLayoutEffect(() => {\n measure();\n }, [measure]);\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el || typeof ResizeObserver === \"undefined\") return;\n const ro = new ResizeObserver(() => measure());\n ro.observe(el);\n return () => ro.disconnect();\n }, [containerRef, measure]);\n\n return { style };\n}\n"],"mappings":"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"}