gesso-core 0.6.30 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,81 +1,5 @@
1
- import { $ as isImeKey, $a as hasVisualState, $i as densityFactors, $n as CharacterCountTextMeasurer, $r as parseTransform, $t as clearFontStacks, A as withOverrideSource, Aa as darkColors, Ai as UiInsetRegistry, An as pointerEventsOf, Ar as findPropertyDefinition, At as wordRangeIn, B as FlexDirection, Ba as rgba, Bi as themesEqual, Bn as EDITOR_PROP, Br as UI_SEMANTIC_STATES, Bt as applyCanvasTextStyle, C as SCROLLBAR_LINGER_MS, Ca as spanAtOffset, Ci as borderRadiusIsZero, Cn as isNodeFocusable, Cr as DEFAULT_LINE_HEIGHT_FACTOR, Ct as scrollsFromRight, D as formatExplanation, Da as textSpansEqual, Di as defineProperty, Dn as isNodeTabStop, Dr as resolvePropertyByName, Dt as paragraphGeometry, E as formatConstraints, Ea as textContentOf, Ei as uniformBorderRadius, En as isNodeSelectable, Er as resolveProperty, Et as offsetAtPointIn, F as clampSize, Fa as colorValuesEqual, Fi as bandOf, Fn as linkOf, Fr as propertyIsInherited, Ft as editingGroupOf, G as CrossAxisAlignment, Ga as DirtyNodeSet, Gi as noThemeExtensions, Gn as isMultiline, Gr as validateRole, Gt as layoutTextLines, H as placeGridItems, Ha as findEnvironmentKey, Hi as withDensity, Hn as editorFor, Hr as isUiSemanticState, Ht as createCanvasTextStyle, I as constraintsEqual, Ia as colorsEqual, Ii as containerBands, In as linkRunAtPointIn, Ir as propertyNarrowers, It as commandForKey, J as parseCrossAxisAlignment, Ja as resetOverrideWarnings, Ji as withThemeExtension, Jn as nextCaretToggle, Jr as validateSubgrid, Jt as spanOf, K as MainAxisAlignment, Ka as clearOverrideProperty, Ki as themeExtension, Kn as isReadOnly, Kr as validateStates, Kt as runCanvasStyleInto, L as tightenConstraints, La as normalizeColor, Li as unknownContainerSize, Ln as setLinkHover, Lr as propertyValuesEqualByName, Lt as detectEditingPlatform, M as contentOffset, Ma as UiBasicColors, Mi as noInsets, Mn as clearLinkHover, Mr as inheritedPropertyFlags, Mt as adjacentField, N as LayoutRecord, Na as colorToHex, Ni as observeViewportInsets, Nn as hasTextLinks, Nr as inheritedPropertyNames, Nt as comparePositions, O as formatNumber, Oa as textStylesEqual, Oi as propertyValuesEqual, On as isPressable, Or as resolveString, Ot as paragraphGeometryFrom, P as Constraints, Pa as colorToRgba, Pi as UiContainerSizeSource, Pn as linkHoverOf, Pr as propertyEffects, Pt as edgeField, Q as capturePointer, Qa as defaultVisualState, Qi as relativeLuminance, Qn as videoFrameSize, Qr as UiTransforms, Qt as bumpFontStack, R as MAX_MEASURES_PER_CHILD, Ra as parseColor, Ri as darkTheme, Rn as CARET_BLINK_MS, Rr as UiProperties, Rt as isPrintable, S as SCROLLBAR_FADE_MS, Sa as resolvedSpansOf, Si as borderRadiusEqual, Sn as setSelectionRange, Sr as UiGraph, St as scrollbarZoneAt, T as describeOverrides, Ta as spansDifferOnlyInPaint, Ti as normalizeBorderRadius, Tn as isNodeInert, Tr as resolveNumber, Tt as hasDrawnText, U as sizeGridTracks, Ua as UiEnvironment, Ui as defineThemeExtension, Un as editorOf, Ur as normalizeStates, Ut as drawText, V as parseFlexDirection, Va as createEnvironmentKey, Vi as withContrast, Vn as caretVisibleAt, Vr as isUiRole, Vt as buildFontString, W as AlignContent, Wa as UiNode, Wi as hasThemeExtension, Wn as isEditableNode, Wr as statesEqual, Wt as fillTextRects, X as CanvasPlatformSurface, Xa as writeOverrideProperty, Xi as highContrastColors, Xn as resolveColorValue, Xr as paintValuesEqual, Xt as textRunBackgrounds, Y as parseMainAxisAlignment, Ya as writeDeclaredProperty, Yi as contrastRatio, Yn as syncEditorValue, Yr as inputsEqual, Yt as textMeasureRequest, Z as UiPlatformAdapter, Za as UiVisualState, Zi as raiseContrast, Zn as isVideoSurface, Zr as pathValuesEqual, Zt as textRunDecorations, _ as LABEL_PADDING_X, _a as typographyEqual, _i as repeat, _n as TEXT_SELECTION_PROP, _o as noKeyModifiers, _r as nextGraphemeEnd, _t as scrollBounds, a as PaintPictureCache, aa as shadowsEqual, ai as gradientsEqual, an as DEFAULT_TEXT_COLOR, ao as UiEventType, ar as caretRectFor, at as UiGestureRecognizer, b as snapScroll, ba as flattenTextSpans, bi as borderRadius, bn as selectableTextOf, bo as DirtyFlags, br as previousWordStart, bt as scrollbarThumb, c as EMPTY_RECORDING, ca as boxShadowArraysEqual, ci as validateGradient, cn as createPaintState, co as UiInputEvent, cr as lineLimit, ct as isScrollContainer, d as tracePathData, da as boxShadowsEqual, di as isAutoLength, dn as normalizeVerticalAlign, do as UiPinchEvent, dr as offsetForVerticalMove, dt as UiFocusManager, ea as defaultSpacing, ei as transform, en as fontStackFor, eo as visualState, er as ParagraphTextMeasurer, et as pointerDeviceOf, f as ScaledImageCache, fa as defaultShapes, fi as isFrLength, fn as resolvePaintState, fo as UiPointerEvent, fr as selectionRects, ft as UiHitTester, g as paintsAfterChildren, ga as isTypographyRole, gi as percent, gn as setMatchRanges, go as UiWheelEvent, gr as lineStartAt, gt as SCROLLBAR_THICKNESS, h as hasDecorationPhase, ha as defaultTypography, hi as minmax, hn as matchRangesOf, ho as UiWheelDeltaMode, hr as lineEndAt, ht as SCROLLBAR_MIN_THUMB, i as traceRoundedRect, ia as defaultShadows, ii as gradientPaint, in as DEFAULT_FONT_WEIGHT, io as UiCompositionEvent, ir as proportionalFontMetrics, it as UiTouchScroller, j as accumulatedOffsetTo, ja as lightColors, ji as insetsEqual, jn as resolveCursor, jr as getPropertyNames, jt as textInputOf, k as labelNode, ka as colorsEqualPalette, ki as UiEnvironmentKeys, kn as isSubtreeInert, kr as closestPropertyName, kt as selectionRectsIn, l as PaintRecorder, la as boxShadowRadius, li as auto, ln as isPaintVisible, lo as UiKeyboardEvent, lr as offsetAtPoint, lt as UiKeyboardController, m as decorationRect, ma as shapesEqual, mi as isPercentLength, mn as clearMatchRanges, mo as UiTextChangeEvent, mr as graphemeBoundaries, mt as SCROLLBAR_INSET, n as createCanvasSurface, na as spacingEqual, ni as transformsEqual, nn as DEFAULT_FONT_FAMILY, no as MOUSE_POINTER, nr as ELLIPSIS, nt as touchActionFor, o as paintPictures, oa as BOX_SHADOW_BLUR_REACH, oi as linearGradient, on as colorToCss, oo as UiFocusEvent, or as lineIndexAtY, ot as UiPinchRecognizer, p as decorationColor, pa as isShapeName, pi as isMinMaxTrack, pn as TEXT_MATCHES_PROP, po as UiSelectionChangeEvent, pr as EditableTextModel, pt as SCROLLBAR_HOVER_ZONE, q as parseAlignContent, qa as overrideSources, qi as themeExtensionsEqual, qn as maskText, qr as UI_SUBGRID_AXES, qt as runFontString, r as Canvas2DRenderer, ra as spacingSteps, ri as MAX_GRADIENT_STOPS, rn as DEFAULT_FONT_SIZE, ro as UiBeforeInputEvent, rr as layoutParagraph, rt as UiEditingController, s as PaintTarget, sa as boxShadow, si as radialGradient, sn as computeObjectFitRect, so as UiGestureEvent, sr as lineIndexForOffset, st as UiWheelController, t as CanvasSurface, ta as scaleSpacing, ti as transformIsIdentity, tn as registerFontStack, to as visualStatesEqual, tr as spannedRunsFor, tt as prepareInputSurface, u as replayPaint, ua as boxShadowReach, ui as fr, un as normalizeTextAlign, uo as UiPasteEvent, ur as offsetAtX, ut as UiPointerController, v as drawOverlayShapes, va as defaultTextStyle, vi as resolveLength, vn as clearSelectionRange, vo as wheelDeltaYOf, vr as nextWordEnd, vt as scrollLeaderOf, w as describeLength, wa as spannedTextOf, wi as borderRadiusValuesEqual, wn as isNodeHitTestable, wr as resolveBoolean, wt as UiInputDispatcher, x as LayoutEngine, xa as hiddenRangesOf, xi as borderRadiusCorners, xn as selectionRangeOf, xo as UiBinding, xr as wordRangeAt, xt as scrollbarThumbs, y as labelOrigin, ya as editableSpansOf, yi as UiBorderRadiuses, yn as selectableTextNodes, yo as UiNodeType, yr as previousGraphemeStart, yt as scrollRange, z as isLayoutProtocol, za as rgb8, zi as lightTheme, zn as CARET_WIDTH, zr as UI_ROLES, zt as EditableLayout } from "./CanvasSurface-CspYfnND.js";
1
+ import { $ as UiPlatformAdapter, $a as boxShadowArraysEqual, $i as UiKeyboardEvent, $n as isVideoSurface, $r as pathValuesEqual, $t as textRunDecorations, A as formatNumber, Aa as withDensity, Ai as propertyValuesEqual, An as isPressable, Ao as colorsEqual, Ar as resolveString, At as paragraphGeometryFrom, B as MAX_MEASURES_PER_CHILD, Ba as contrastRatio, Bi as writeOverrideProperty, Bn as CARET_BLINK_MS, Bo as easings, Br as UiProperties, Bt as isPrintable, C as LayoutEngine, Ca as bandOf, Ci as borderRadiusCorners, Cn as selectionRangeOf, Co as colorsEqualPalette, Cr as wordRangeAt, Ct as scrollbarThumbs, D as describeOverrides, Da as lightTheme, Di as normalizeBorderRadius, Dn as isNodeInert, Do as colorToHex, Dr as resolveNumber, Dt as hasDrawnText, E as describeLength, Ea as darkTheme, Ei as borderRadiusValuesEqual, En as isNodeHitTestable, Eo as UiBasicColors, Er as resolveBoolean, Et as UiInputDispatcher, F as LayoutRecord, Fa as hasThemeExtension, Fi as DirtyNodeSet, Fn as hasTextLinks, Fo as createEnvironmentKey, Fr as inheritedPropertyNames, Ft as comparePositions, G as sizeGridTracks, Ga as defaultSpacing, Gi as visualStatesEqual, Gn as editorOf, Gr as normalizeStates, Gt as drawText, H as FlexDirection, Ha as raiseContrast, Hi as defaultVisualState, Hn as EDITOR_PROP, Ho as steps, Hr as UI_SEMANTIC_STATES, Ht as applyCanvasTextStyle, I as Constraints, Ia as noThemeExtensions, Ii as clearOverrideProperty, In as linkHoverOf, Io as findEnvironmentKey, Ir as propertyEffects, It as edgeField, J as MainAxisAlignment, Ja as spacingSteps, Ji as UiCompositionEvent, Jn as isReadOnly, Jr as validateStates, Jt as runCanvasStyleInto, K as AlignContent, Ka as scaleSpacing, Ki as MOUSE_POINTER, Kn as isEditableNode, Kr as statesEqual, Kt as fillTextRects, L as clampSize, La as themeExtension, Li as overrideSources, Ln as linkOf, Lo as provided, Lr as propertyIsInherited, Lt as editingGroupOf, M as withOverrideSource, Ma as lengths, Mi as withPointer, Mn as pointerEventsOf, Mo as parseColor, Mr as findPropertyDefinition, Mt as wordRangeIn, N as accumulatedOffsetTo, Na as unitsEqual, Ni as UiEnvironment, Nn as resolveCursor, No as rgb8, Nr as getPropertyNames, Nt as textInputOf, O as formatConstraints, Oa as themesEqual, Oi as uniformBorderRadius, On as isNodeSelectable, Oo as colorToRgba, Or as resolveProperty, Ot as offsetAtPointIn, P as contentOffset, Pa as defineThemeExtension, Pi as UiNode, Pn as clearLinkHover, Po as rgba, Pr as inheritedPropertyFlags, Pt as adjacentField, Q as CanvasPlatformSurface, Qa as boxShadow, Qi as UiInputEvent, Qn as resolveColorValue, Qr as paintValuesEqual, Qt as textRunBackgrounds, R as constraintsEqual, Ra as themeExtensionsEqual, Ri as resetOverrideWarnings, Rn as linkRunAtPointIn, Ro as cubicBezier, Rr as propertyNarrowers, Rt as commandForKey, S as tracePathData, Sa as UiContainerSizeSource, Si as borderRadius, Sn as selectableTextOf, So as textStylesEqual, Sr as previousWordStart, St as scrollbarThumb, T as SCROLLBAR_LINGER_MS, Ta as unknownContainerSize, Ti as borderRadiusIsZero, Tn as isNodeFocusable, To as lightColors, Tr as DEFAULT_LINE_HEIGHT_FACTOR, Tt as scrollsFromRight, U as parseFlexDirection, Ua as relativeLuminance, Ui as hasVisualState, Un as caretVisibleAt, Ur as isUiRole, Ut as buildFontString, V as isLayoutProtocol, Va as highContrastColors, Vi as UiVisualState, Vn as CARET_WIDTH, Vo as linear, Vr as UI_ROLES, Vt as EditableLayout, W as placeGridItems, Wa as densityFactors, Wi as visualState, Wn as editorFor, Wr as isUiSemanticState, Wt as createCanvasTextStyle, X as parseCrossAxisAlignment, Xa as shadowsEqual, Xi as UiFocusEvent, Xn as nextCaretToggle, Xr as validateSubgrid, Xt as spanOf, Y as parseAlignContent, Ya as defaultShadows, Yi as UiEventType, Yn as maskText, Yr as UI_SUBGRID_AXES, Yt as runFontString, Z as parseMainAxisAlignment, Za as BOX_SHADOW_BLUR_REACH, Zi as UiGestureEvent, Zn as syncEditorValue, Zr as inputsEqual, Zt as textMeasureRequest, _ as paintsAfterChildren, _a as springIn, _i as minmax, _n as matchRangesOf, _o as spanAtOffset, _r as lineEndAt, _t as SCROLLBAR_MIN_THUMB, a as layerRegion, aa as UiWheelDeltaMode, ai as MAX_GRADIENT_STOPS, an as DEFAULT_FONT_SIZE, ao as shadowsOf, ar as layoutParagraph, at as UiEditingController, b as labelOrigin, ba as noInsets, bi as resolveLength, bn as clearSelectionRange, bo as textContentOf, br as nextWordEnd, bt as scrollLeaderOf, c as paintPictures, ca as wheelDeltaYOf, ci as linearGradient, cn as colorToCss, co as shapesEqual, cr as lineIndexAtY, ct as UiPinchRecognizer, d as PaintRecorder, da as UiBinding, di as auto, dn as isPaintVisible, do as typographyEqual, dr as offsetAtPoint, dt as UiKeyboardController, ea as UiPasteEvent, ei as UiTransforms, en as bumpFontStack, eo as boxShadowRadius, er as videoFrameSize, et as capturePointer, f as replayPaint, fa as UiEnvironmentKeys, fi as fr, fn as normalizeTextAlign, fo as defaultTextStyle, fr as offsetAtX, ft as UiPointerController, g as hasDecorationPhase, ga as easingIn, gi as isPercentLength, gn as clearMatchRanges, go as resolvedSpansOf, gr as graphemeBoundaries, gt as SCROLLBAR_INSET, h as decorationRect, ha as durationIn, hi as isMinMaxTrack, hn as TEXT_MATCHES_PROP, ho as hiddenRangesOf, hr as EditableTextModel, ht as SCROLLBAR_HOVER_ZONE, i as traceRoundedRect, ia as UiTextChangeEvent, ii as transformsEqual, in as DEFAULT_FONT_FAMILY, io as fittedShadowRadius, ir as ELLIPSIS, it as touchActionFor, j as labelNode, ja as defaultUnits, ji as TOUCH_TARGET, jn as isSubtreeInert, jo as normalizeColor, jr as closestPropertyName, jt as selectionRectsIn, k as formatExplanation, ka as withContrast, ki as defineProperty, kn as isNodeTabStop, ko as colorValuesEqual, kr as resolvePropertyByName, kt as paragraphGeometry, l as PaintTarget, la as UiNodeType, li as radialGradient, ln as computeObjectFitRect, lo as defaultTypography, lr as lineIndexForOffset, lt as UiWheelController, m as decorationColor, ma as defineMotion, mi as isFrLength, mn as resolvePaintState, mo as flattenTextSpans, mr as selectionRects, mt as UiHitTester, n as createCanvasSurface, na as UiPointerEvent, ni as transform, nn as fontStackFor, no as boxShadowValuesEqual, nr as ParagraphTextMeasurer, nt as pointerDeviceOf, o as opacityNeedsLayer, oa as UiWheelEvent, oi as gradientPaint, on as DEFAULT_FONT_WEIGHT, oo as defaultShapes, or as proportionalFontMetrics, ot as UiTouchScroller, p as ScaledImageCache, pa as defaultMotion, pi as isAutoLength, pn as normalizeVerticalAlign, po as editableSpansOf, pr as offsetForVerticalMove, pt as UiFocusManager, q as CrossAxisAlignment, qa as spacingEqual, qi as UiBeforeInputEvent, qn as isMultiline, qr as validateRole, qt as layoutTextLines, r as Canvas2DRenderer, ra as UiSelectionChangeEvent, ri as transformIsIdentity, rn as registerFontStack, ro as boxShadowsEqual, rr as spannedRunsFor, rt as prepareInputSurface, s as PaintPictureCache, sa as noKeyModifiers, si as gradientsEqual, sn as DEFAULT_TEXT_COLOR, so as isShapeName, sr as caretRectFor, st as UiGestureRecognizer, t as CanvasSurface, ta as UiPinchEvent, ti as parseTransform, tn as clearFontStacks, to as boxShadowReach, tr as CharacterCountTextMeasurer, tt as isImeKey, u as EMPTY_RECORDING, ua as DirtyFlags, ui as validateGradient, un as createPaintState, uo as isTypographyRole, ur as lineLimit, ut as isScrollContainer, v as LABEL_PADDING_X, va as UiInsetRegistry, vi as percent, vn as setMatchRanges, vo as spannedTextOf, vr as lineStartAt, vt as SCROLLBAR_THICKNESS, w as SCROLLBAR_FADE_MS, wa as containerBands, wi as borderRadiusEqual, wn as setSelectionRange, wo as darkColors, wr as UiGraph, wt as scrollbarZoneAt, x as snapScroll, xa as observeViewportInsets, xi as UiBorderRadiuses, xn as selectableTextNodes, xo as textSpansEqual, xr as previousGraphemeStart, xt as scrollRange, y as drawOverlayShapes, ya as insetsEqual, yi as repeat, yn as TEXT_SELECTION_PROP, yo as spansDifferOnlyInPaint, yr as nextGraphemeEnd, yt as scrollBounds, z as tightenConstraints, za as withThemeExtension, zi as writeDeclaredProperty, zn as setLinkHover, zo as easingCss, zr as propertyValuesEqualByName, zt as detectEditingPlatform } from "./CanvasSurface-Cy2UErXI.js";
2
2
  import { BehaviorSubject, EMPTY, Subject, combineLatest, distinctUntilChanged, isObservable as isObservable$1, map, of } from "rxjs";
3
- //#region src/animation/UiEasing.ts
4
- /** Progress equals time. What a spinner and a sweep want. */
5
- const linear = (t) => t;
6
- /**
7
- * A cubic Bézier easing, in the CSS `cubic-bezier(x1, y1, x2, y2)`
8
- * sense: the curve runs from (0, 0) to (1, 1) through the two control
9
- * points, and the easing is y at the t where x equals the input.
10
- *
11
- * Solved by Newton's method with a bisection fallback, which is what
12
- * every browser engine does; eight iterations is well past the point
13
- * where the result stops moving in a float.
14
- */
15
- function cubicBezier(x1, y1, x2, y2) {
16
- const ax = 3 * x1 - 3 * x2 + 1;
17
- const bx = 3 * x2 - 6 * x1;
18
- const cx = 3 * x1;
19
- const ay = 3 * y1 - 3 * y2 + 1;
20
- const by = 3 * y2 - 6 * y1;
21
- const cy = 3 * y1;
22
- const sampleX = (t) => ((ax * t + bx) * t + cx) * t;
23
- const sampleY = (t) => ((ay * t + by) * t + cy) * t;
24
- const slopeX = (t) => (3 * ax * t + 2 * bx) * t + cx;
25
- return (input) => {
26
- if (input <= 0) return 0;
27
- if (input >= 1) return 1;
28
- let t = input;
29
- for (let i = 0; i < 8; i++) {
30
- const error = sampleX(t) - input;
31
- if (Math.abs(error) < 1e-6) return sampleY(t);
32
- const slope = slopeX(t);
33
- if (Math.abs(slope) < 1e-6) break;
34
- t -= error / slope;
35
- }
36
- let low = 0;
37
- let high = 1;
38
- t = input;
39
- for (let i = 0; i < 24; i++) {
40
- const x = sampleX(t);
41
- if (Math.abs(x - input) < 1e-6) break;
42
- if (x > input) high = t;
43
- else low = t;
44
- t = (low + high) / 2;
45
- }
46
- return sampleY(t);
47
- };
48
- }
49
- /**
50
- * An easing that only takes `count` distinct values.
51
- *
52
- * The reason it exists is the `Spinner`: eight blades have eight
53
- * positions promised the F4 version would keep a
54
- * turn at eight writes a second rather than sixty. Paired with an
55
- * animation's `stepMs`, this is how that promise is kept — the value
56
- * is stepped and the frames are too.
57
- */
58
- function steps(count) {
59
- if (!(count >= 1)) throw new Error(`steps() needs at least one step, got ${count}.`);
60
- return (t) => Math.min(count - 1, Math.floor(t * count)) / count;
61
- }
62
- /**
63
- * The named curves a motion vocabulary is built from.
64
- *
65
- * `standard` is the one to reach for: something entering and leaving
66
- * at a natural pace. `decelerate` is for something arriving (it starts
67
- * fast and settles), `accelerate` for something leaving. `emphasized`
68
- * is `standard` with more of a lean, for the one movement on a screen
69
- * that should be noticed.
70
- */
71
- const easings = {
72
- linear,
73
- standard: cubicBezier(.2, 0, 0, 1),
74
- decelerate: cubicBezier(0, 0, 0, 1),
75
- accelerate: cubicBezier(.3, 0, 1, 1),
76
- emphasized: cubicBezier(.05, .7, .1, 1)
77
- };
78
- //#endregion
79
3
  //#region src/animation/Interpolate.ts
80
4
  const interpolateNumber = (from, to, t) => from + (to - from) * t;
81
5
  /**
@@ -283,7 +207,8 @@ var UiAnimation = class {
283
207
  this.begin(previous.lastAdvancedAt);
284
208
  }
285
209
  onBegin() {}
286
- advance(now) {
210
+ /** `hold`: at its end, stay there, unfinished, to be sampled again at an earlier time (`UiClock.holding`). */
211
+ advance(now, hold = false) {
287
212
  if (this.finished) return;
288
213
  this.begin(now);
289
214
  this.lastAdvancedAt = now;
@@ -296,7 +221,7 @@ var UiAnimation = class {
296
221
  this.cell.value = value;
297
222
  this.subject.next(value);
298
223
  }
299
- if (done) this.finish();
224
+ if (done && !hold) this.finish();
300
225
  }
301
226
  /**
302
227
  * Moves the ideal timeline on by one step.
@@ -519,40 +444,6 @@ function createTween(cell, to, options) {
519
444
  return new UiTween(cell, to, options, interpolate);
520
445
  }
521
446
  //#endregion
522
- //#region src/environment/UiMotion.ts
523
- const defaultMotion = {
524
- durations: {
525
- /** No motion. What `reducedMotion` turns everything else into. */
526
- instant: 0,
527
- /** A control acknowledging a press; too short to watch. */
528
- fast: 120,
529
- /** The default: something appearing, moving or changing colour. */
530
- normal: 200,
531
- /** A panel or a dialog, which is bigger and travels further. */
532
- slow: 320,
533
- /** A movement meant to be followed by the eye. Rare. */
534
- deliberate: 500
535
- },
536
- easings,
537
- springs: {
538
- gentle: {
539
- stiffness: 120,
540
- damping: 20,
541
- mass: 1
542
- },
543
- snappy: {
544
- stiffness: 220,
545
- damping: 24,
546
- mass: 1
547
- },
548
- stiff: {
549
- stiffness: 400,
550
- damping: 32,
551
- mass: 1
552
- }
553
- }
554
- };
555
- //#endregion
556
447
  //#region src/animation/UiTransition.ts
557
448
  /** A tween of `duration` milliseconds along `easing`. */
558
449
  function tween(duration, options = {}) {
@@ -571,12 +462,12 @@ function tween(duration, options = {}) {
571
462
  function spring(spec = "snappy", options = {}) {
572
463
  return {
573
464
  kind: "spring",
574
- spring: typeof spec === "string" ? defaultMotion.springs[spec] : spec,
465
+ spring: spec,
575
466
  ...options
576
467
  };
577
468
  }
578
469
  function normalizeTransition(value) {
579
- return typeof value === "number" ? {
470
+ return typeof value === "number" || typeof value === "string" ? {
580
471
  kind: "tween",
581
472
  duration: value
582
473
  } : value;
@@ -598,11 +489,18 @@ function assertTransitionMap(nodeId, value, isKnownProperty) {
598
489
  });
599
490
  continue;
600
491
  }
492
+ if (typeof entry === "string" && entry !== "") {
493
+ specs.set(property, {
494
+ kind: "tween",
495
+ duration: entry
496
+ });
497
+ continue;
498
+ }
601
499
  if (typeof entry === "object" && entry !== null && (entry.kind === "tween" || entry.kind === "spring")) {
602
500
  specs.set(property, entry);
603
501
  continue;
604
502
  }
605
- throw new Error(`The 'transition' for '${property}' on node '${nodeId}' must be a duration in milliseconds or a spec built by tween() or spring(), got ${describe$1(entry)}.`);
503
+ throw new Error(`The 'transition' for '${property}' on node '${nodeId}' must be a duration in milliseconds or by name, or a spec built by tween() or spring(), got ${describe$1(entry)}.`);
606
504
  }
607
505
  return specs;
608
506
  }
@@ -632,8 +530,13 @@ function erase(animation) {
632
530
  */
633
531
  var AnimationDriver = class {
634
532
  running = /* @__PURE__ */ new Map();
533
+ /** The clock each running animation was started on, when it was not real time; see `UiClock`. */
534
+ clocks = /* @__PURE__ */ new Map();
535
+ /** Each clock's `moves` as last drawn: one moved since (a seek) is drawn at its new time at once. */
536
+ seenMoves = /* @__PURE__ */ new WeakMap();
635
537
  reducedMotion = false;
636
538
  hidden = false;
539
+ advancing = false;
637
540
  wake = null;
638
541
  /**
639
542
  * Called when an animation enters the running set on an idle driver.
@@ -648,6 +551,14 @@ var AnimationDriver = class {
648
551
  setWakeListener(listener) {
649
552
  this.wake = listener;
650
553
  }
554
+ /**
555
+ * Whether a frame of the running animations is being written now: a
556
+ * value written meanwhile is a step of an animation, not a change for
557
+ * a transition to animate again.
558
+ */
559
+ get isAdvancing() {
560
+ return this.advancing;
561
+ }
651
562
  /** Whether anything is running, so the `ticks` phase can report 0. */
652
563
  get isRunning() {
653
564
  return this.running.size > 0;
@@ -669,8 +580,8 @@ var AnimationDriver = class {
669
580
  * animation says its movement is the information rather than the
670
581
  * decoration.
671
582
  */
672
- lands(policy) {
673
- return policy === "snap" && (this.reducedMotion || this.hidden);
583
+ lands(policy, clock) {
584
+ return clock?.settled === true || policy === "snap" && (this.reducedMotion || this.hidden);
674
585
  }
675
586
  /**
676
587
  * Starts an animation, replacing whatever was driving its cell.
@@ -682,16 +593,18 @@ var AnimationDriver = class {
682
593
  * So a reduced-motion app runs no animation frames, rather than
683
594
  * running them and drawing the same thing sixty times.
684
595
  */
685
- start(animation) {
596
+ start(animation, clock = null) {
686
597
  const cell = animation.cell;
687
598
  this.running.get(cell)?.cancel();
688
599
  this.running.delete(cell);
689
- if (this.lands(animation.reducedMotionPolicy)) {
600
+ this.clocks.delete(cell);
601
+ if (this.lands(animation.reducedMotionPolicy, clock ?? void 0)) {
690
602
  animation.snap();
691
603
  return animation.values;
692
604
  }
693
605
  const wasIdle = this.running.size === 0;
694
606
  this.running.set(cell, erase(animation));
607
+ if (clock !== null) this.clocks.set(cell, clock);
695
608
  if (wasIdle) this.wake?.();
696
609
  return animation.values;
697
610
  }
@@ -711,12 +624,14 @@ var AnimationDriver = class {
711
624
  const animation = this.running.get(cell);
712
625
  if (animation === void 0) return false;
713
626
  this.running.delete(cell);
627
+ this.clocks.delete(cell);
714
628
  animation.cancel();
715
629
  return true;
716
630
  }
717
631
  stopAll() {
718
632
  const animations = [...this.running.values()];
719
633
  this.running.clear();
634
+ this.clocks.clear();
720
635
  for (const animation of animations) animation.cancel();
721
636
  }
722
637
  /**
@@ -770,8 +685,9 @@ var AnimationDriver = class {
770
685
  }
771
686
  /** Puts everything that may be landed where it was going. */
772
687
  landRunning() {
773
- for (const [cell, animation] of this.running) if (this.lands(animation.reducedMotionPolicy)) {
688
+ for (const [cell, animation] of this.running) if (this.lands(animation.reducedMotionPolicy, this.clocks.get(cell))) {
774
689
  this.running.delete(cell);
690
+ this.clocks.delete(cell);
775
691
  animation.snap();
776
692
  }
777
693
  }
@@ -788,8 +704,14 @@ var AnimationDriver = class {
788
704
  */
789
705
  nextTickAt(now) {
790
706
  let earliest;
791
- for (const animation of this.running.values()) {
792
- const due = animation.dueAt(now) - animation.dueSlackMs;
707
+ for (const [cell, animation] of this.running) {
708
+ const clock = this.clocks.get(cell);
709
+ if (clock?.settled === true) return now;
710
+ if (clock !== void 0 && clock.moves !== (this.seenMoves.get(clock) ?? 0)) return now;
711
+ const at = clock === void 0 ? now : clock.local(now);
712
+ const dueAt = animation.dueAt(at) - animation.dueSlackMs;
713
+ const due = clock === void 0 ? dueAt : clock.real(dueAt);
714
+ if (!Number.isFinite(due)) continue;
793
715
  if (earliest === void 0 || due < earliest) earliest = due;
794
716
  }
795
717
  return earliest;
@@ -805,14 +727,144 @@ var AnimationDriver = class {
805
727
  * behind.
806
728
  */
807
729
  advance(now) {
730
+ this.advancing = true;
731
+ try {
732
+ this.advanceRunning(now);
733
+ } finally {
734
+ this.advancing = false;
735
+ }
736
+ }
737
+ advanceRunning(now) {
738
+ const moved = [...new Set(this.clocks.values())].filter((clock) => clock.moves !== (this.seenMoves.get(clock) ?? 0));
739
+ this.advanceEach(now);
740
+ for (const clock of moved) this.seenMoves.set(clock, clock.moves);
741
+ }
742
+ advanceEach(now) {
808
743
  for (const [cell, animation] of this.running) {
809
- if (animation.dueAt(now) - animation.dueSlackMs > now) continue;
810
- animation.advance(now);
811
- if (animation.isFinished && this.running.get(cell) === animation) this.running.delete(cell);
744
+ const clock = this.clocks.get(cell);
745
+ if (clock?.settled === true) {
746
+ this.running.delete(cell);
747
+ this.clocks.delete(cell);
748
+ animation.snap();
749
+ continue;
750
+ }
751
+ const at = clock === void 0 ? now : clock.local(now);
752
+ if (!(clock !== void 0 && clock.moves !== (this.seenMoves.get(clock) ?? 0)) && animation.dueAt(at) - animation.dueSlackMs > at) continue;
753
+ animation.advance(at, clock?.holding === true);
754
+ if (animation.isFinished && this.running.get(cell) === animation) {
755
+ this.running.delete(cell);
756
+ this.clocks.delete(cell);
757
+ }
812
758
  }
813
759
  }
814
760
  };
815
761
  //#endregion
762
+ //#region src/animation/UiClock.ts
763
+ /**
764
+ * How time passes for the animations under a node: at a speed of its
765
+ * own, or not at all.
766
+ *
767
+ * A node provides one to its subtree,
768
+ * `provide={[provided(UiEnvironmentKeys.clock, clock)]}`, and every
769
+ * animation started there runs on it: a transition, a `motion` state,
770
+ * hover and press, and `AnimationService.animate` given the node it
771
+ * animates `on`. Nothing provided is real time.
772
+ *
773
+ * What a design tool needs to show one screen two ways at once. The
774
+ * frames on its board are pictures: under a `settled` clock every
775
+ * animation lands where it is going the moment it starts, and a loop
776
+ * holds still. The screen it plays runs at the person's own speed, a
777
+ * quarter of it to watch a press closely, or paused. Reduced motion and
778
+ * a hidden page are the whole app's; this is one part of it.
779
+ *
780
+ * An animation takes all its time from the driver, which hands each
781
+ * the local time of its clock, so nothing inside a tween or a spring
782
+ * knows; and the driver asks for the next frame in real time, so a
783
+ * slowed clock asks for frames as often as it needs them and a paused
784
+ * one asks for none.
785
+ */
786
+ var UiClock = class {
787
+ rate;
788
+ /** A real time and the local time it was, from the last change of speed: where local time is measured from. */
789
+ anchor = null;
790
+ /** The real time last asked about, where a change of speed takes effect. */
791
+ lastReal = null;
792
+ /** Every animation lands where it is going, and none runs. */
793
+ settled;
794
+ /**
795
+ * Animations that reach their end stay, held there, rather than
796
+ * finishing: so moving the time back (`seek`) plays them back. What a
797
+ * design tool scrubs a transition with; let go, they finish as they
798
+ * would have. A spring, which is worked out step by step rather than
799
+ * from the time, holds but does not run back.
800
+ */
801
+ holding = false;
802
+ /** Bumped by `seek`, so a driver knows to draw again at the new time. */
803
+ moves = 0;
804
+ constructor(options = {}) {
805
+ this.rate = checkedSpeed(options.speed ?? 1);
806
+ this.settled = options.settled ?? false;
807
+ }
808
+ /** How fast time passes: 1 is real time, 0.25 a quarter of it, 0 paused. */
809
+ get speed() {
810
+ return this.rate;
811
+ }
812
+ set speed(next) {
813
+ const speed = checkedSpeed(next);
814
+ if (this.anchor !== null && this.lastReal !== null) this.anchor = {
815
+ real: this.lastReal,
816
+ local: this.local(this.lastReal)
817
+ };
818
+ this.rate = speed;
819
+ }
820
+ /** The local time now, as last asked: where a seek is measured from. */
821
+ get now() {
822
+ return this.lastReal === null ? 0 : this.local(this.lastReal);
823
+ }
824
+ /**
825
+ * Puts local time at `at`, from now on: forward, or back to replay
826
+ * what is held (`holding`). Paused, it stays there; running, it goes
827
+ * on from there.
828
+ */
829
+ seek(at) {
830
+ const real = this.lastReal ?? 0;
831
+ this.anchor = {
832
+ real,
833
+ local: at
834
+ };
835
+ this.moves += 1;
836
+ }
837
+ /** The local time at real time `now`. The first time asked, the two agree. */
838
+ local(now) {
839
+ this.anchor ??= {
840
+ real: now,
841
+ local: now
842
+ };
843
+ this.lastReal = now;
844
+ return this.anchor.local + (now - this.anchor.real) * this.rate;
845
+ }
846
+ /** The real time at which local time `at` comes; Infinity while paused. */
847
+ real(at) {
848
+ if (this.anchor === null) return at;
849
+ if (this.rate === 0) return Infinity;
850
+ return this.anchor.real + (at - this.anchor.local) / this.rate;
851
+ }
852
+ };
853
+ function checkedSpeed(speed) {
854
+ if (!(speed >= 0) || !Number.isFinite(speed)) throw new Error(`A clock's speed is a number of 0 or more (0 is paused, 1 real time), not ${speed}.`);
855
+ return speed;
856
+ }
857
+ /** The motion vocabulary names resolve to under `node`: the nearest provided above it, or Gesso's. */
858
+ function motionOf(node) {
859
+ for (let at = node ?? null; at !== null; at = at.parent) if (at.environment !== null) return at.environment.get(UiEnvironmentKeys.motion);
860
+ return UiEnvironmentKeys.motion.defaultValue;
861
+ }
862
+ /** The clock the animations under `node` run on: the nearest one provided above it, or null for real time. */
863
+ function clockOf(node) {
864
+ for (let at = node ?? null; at !== null; at = at.parent) if (at.environment !== null) return at.environment.get(UiEnvironmentKeys.clock);
865
+ return null;
866
+ }
867
+ //#endregion
816
868
  //#region src/animation/UiSharedElements.ts
817
869
  /**
818
870
  * Who currently owns each shared-element name, and where they were.
@@ -1099,9 +1151,7 @@ var UiChildrenBinding = class {
1099
1151
  handleValue(value) {
1100
1152
  const definitions = Array.isArray(value) ? value : [value];
1101
1153
  const fragment = this.graph.requireNode(this.fragmentId);
1102
- this.builder.reconcileChildren(fragment, definitions);
1103
- const parent = this.graph.requireNode(this.parentId);
1104
- this.graph.markDirty(parent, 16);
1154
+ if (this.builder.reconcileChildren(fragment, definitions).changed) this.graph.markDirty(this.graph.requireNode(this.parentId), 16);
1105
1155
  }
1106
1156
  };
1107
1157
  //#endregion
@@ -1211,6 +1261,19 @@ function isUiModifier(value) {
1211
1261
  }
1212
1262
  //#endregion
1213
1263
  //#region src/modifiers/interaction.ts
1264
+ /**
1265
+ * Draws what is under a node as though it were hovered, pressed or
1266
+ * focused: every `interactive` modifier there shows its hover or press
1267
+ * look, and every `focusRing` its ring. What a design tool draws a
1268
+ * component's pointer states with, beside its other states, where no
1269
+ * pointer is over it. Read as each modifier attaches, so a node drawn
1270
+ * under it starts so and stays so; the pointer still adds its own.
1271
+ * Pressed is hovered too, as a press under a pointer is.
1272
+ */
1273
+ const ShownAsKey = createEnvironmentKey({
1274
+ name: "gesso.shownAs",
1275
+ defaultValue: null
1276
+ });
1214
1277
  const BOTH = Object.freeze({
1215
1278
  hover: true,
1216
1279
  press: true
@@ -1228,19 +1291,25 @@ const kind$5 = defineModifier({
1228
1291
  attach(host, options) {
1229
1292
  let hovered = false;
1230
1293
  let pressed = false;
1294
+ const shown = host.environment(ShownAsKey);
1295
+ const shownPressed = shown === "pressed";
1296
+ const shownHovered = shownPressed || shown === "hovered";
1231
1297
  const styled = /* @__PURE__ */ new Set([...Object.keys(options.hovered ?? {}), ...Object.keys(options.pressed ?? {})]);
1232
1298
  const sync = () => {
1299
+ const isHovered = hovered || shownHovered;
1300
+ const isPressed = pressed || shownPressed;
1233
1301
  const states = /* @__PURE__ */ new Set();
1234
- if (hovered) states.add("hovered");
1235
- if (pressed) states.add("pressed");
1302
+ if (isHovered) states.add("hovered");
1303
+ if (isPressed) states.add("pressed");
1236
1304
  if (states.size === 0) states.add("normal");
1237
1305
  if (!sameStates(host.get("visualState"), states)) host.set("visualState", states);
1238
1306
  for (const property of styled) {
1239
- const value = (pressed ? options.pressed?.[property] : void 0) ?? (hovered ? options.hovered?.[property] : void 0);
1307
+ const value = (isPressed ? options.pressed?.[property] : void 0) ?? (isHovered ? options.hovered?.[property] : void 0);
1240
1308
  if (value === void 0) host.clear(property);
1241
1309
  else host.set(property, value);
1242
1310
  }
1243
1311
  };
1312
+ if (shownHovered) sync();
1244
1313
  if (options.hover) {
1245
1314
  host.on("pointerenter", () => {
1246
1315
  hovered = true;
@@ -1337,7 +1406,7 @@ const measureFlow = defineModifier({
1337
1406
  target.next(box);
1338
1407
  };
1339
1408
  report();
1340
- host.onLayout(report);
1409
+ host.onFlowLayout(report);
1341
1410
  }
1342
1411
  });
1343
1412
  //#endregion
@@ -1618,6 +1687,13 @@ var UiModifierSet = class {
1618
1687
  get names() {
1619
1688
  return this.attached.map((entry) => entry.kind.name);
1620
1689
  }
1690
+ /** What is attached, each kind with its current arguments, in order: for a tool reading what a node does. */
1691
+ get modifiers() {
1692
+ return this.attached.map((entry) => ({
1693
+ kind: entry.kind,
1694
+ args: entry.args
1695
+ }));
1696
+ }
1621
1697
  get size() {
1622
1698
  return this.attached.length;
1623
1699
  }
@@ -1815,6 +1891,17 @@ var Host = class {
1815
1891
  }
1816
1892
  this.own(layout.onLayout(this.node, listener));
1817
1893
  }
1894
+ onFlowLayout(listener) {
1895
+ const layout = this.services.layout;
1896
+ if (layout === void 0) {
1897
+ warnMissingLayout(this.name);
1898
+ return;
1899
+ }
1900
+ this.own(layout.onFlowLayout === void 0 ? layout.onLayout(this.node, () => {
1901
+ const box = layout.flowBox(this.node);
1902
+ if (box !== null) listener(box);
1903
+ }) : layout.onFlowLayout(this.node, listener));
1904
+ }
1818
1905
  environment(key, of = this.node) {
1819
1906
  const environment = this.services.environment;
1820
1907
  if (environment === void 0) return key.defaultValue;
@@ -1863,7 +1950,7 @@ var Host = class {
1863
1950
  return EMPTY;
1864
1951
  }
1865
1952
  this.animated.add(cell);
1866
- return driver.start(tween);
1953
+ return driver.start(tween, clockOf(this.node));
1867
1954
  }
1868
1955
  spring(cell, to, options) {
1869
1956
  const driver = this.services.animations;
@@ -1878,7 +1965,7 @@ var Host = class {
1878
1965
  return driver.start(new UiSpring(cell, to, {
1879
1966
  ...options,
1880
1967
  velocity
1881
- }));
1968
+ }), clockOf(this.node));
1882
1969
  }
1883
1970
  own(teardown) {
1884
1971
  this.teardowns.push(teardown);
@@ -2063,6 +2150,10 @@ var NodeTransitions = class {
2063
2150
  write(property, value, flags) {
2064
2151
  const spec = this.specs.get(property);
2065
2152
  if (spec === void 0) return false;
2153
+ if (this.driver.isAdvancing) {
2154
+ this.stopProperty(property);
2155
+ return false;
2156
+ }
2066
2157
  if (!this.node.properties.has(property)) return false;
2067
2158
  const current = this.node.properties.get(property);
2068
2159
  if (Object.is(current, value)) return false;
@@ -2082,6 +2173,12 @@ var NodeTransitions = class {
2082
2173
  this.targets.clear();
2083
2174
  this.specs = /* @__PURE__ */ new Map();
2084
2175
  }
2176
+ cancel(property) {
2177
+ this.stopProperty(property);
2178
+ }
2179
+ get declared() {
2180
+ return this.specs;
2181
+ }
2085
2182
  stopProperty(property) {
2086
2183
  const cell = this.cells.get(property);
2087
2184
  if (cell !== void 0) {
@@ -2095,10 +2192,9 @@ var NodeTransitions = class {
2095
2192
  * node: the driver keys its running set by cell identity, so a fresh
2096
2193
  * adapter per write would leave the previous animation running.
2097
2194
  *
2098
- * Writing goes through `updateNodePropertyNow`, which is the path a
2099
- * plain write takes minus this class — so a modifier's override still
2100
- * sits on top of an animated value, exactly as it sits on top of a
2101
- * bound one.
2195
+ * It writes what is drawn (`applyAnimatedValue`): the target already
2196
+ * is the value the element and any modifier over it settled on, so a
2197
+ * frame of the way there goes onto the node as it is.
2102
2198
  */
2103
2199
  cellFor(property, flags) {
2104
2200
  let cell = this.cells.get(property);
@@ -2110,7 +2206,7 @@ var NodeTransitions = class {
2110
2206
  return node.properties.get(property);
2111
2207
  },
2112
2208
  set value(next) {
2113
- graph.updateNodePropertyNow(node, property, next, flags);
2209
+ graph.applyAnimatedValue(node, property, next, flags);
2114
2210
  }
2115
2211
  };
2116
2212
  this.cells.set(property, cell);
@@ -2125,16 +2221,17 @@ var NodeTransitions = class {
2125
2221
  const previous = this.driver.animationFor(cell);
2126
2222
  const velocity = previous instanceof UiSpring ? previous.currentVelocity : 0;
2127
2223
  this.driver.start(new UiSpring(cell, to, {
2128
- spring: spec.spring,
2224
+ spring: springIn(motionOf(this.node), spec.spring),
2129
2225
  velocity,
2130
2226
  restDelta: spec.restDelta,
2131
2227
  reducedMotion: spec.reducedMotion
2132
- }));
2228
+ }), clockOf(this.node));
2133
2229
  return true;
2134
2230
  }
2231
+ const motion = motionOf(this.node);
2135
2232
  const tween = createTween(cell, to, {
2136
- duration: spec.duration,
2137
- easing: spec.easing,
2233
+ duration: durationIn(motion, spec.duration),
2234
+ easing: easingIn(motion, spec.easing),
2138
2235
  stepMs: spec.stepMs,
2139
2236
  reducedMotion: spec.reducedMotion
2140
2237
  });
@@ -2142,7 +2239,7 @@ var NodeTransitions = class {
2142
2239
  warnNotInterpolable(property);
2143
2240
  return false;
2144
2241
  }
2145
- this.driver.start(tween);
2242
+ this.driver.start(tween, clockOf(this.node));
2146
2243
  return true;
2147
2244
  }
2148
2245
  };
@@ -2307,8 +2404,9 @@ var UiGraphBuilder = class {
2307
2404
  * the fragment's children on each emission.
2308
2405
  */
2309
2406
  reconcileChildren(parent, definitions) {
2407
+ const present = definitions.some(isNothing) ? definitions.filter((each) => !isNothing(each)) : definitions;
2310
2408
  if (this.reconciling.has(parent)) {
2311
- this.deferredChildren.set(parent, definitions);
2409
+ this.deferredChildren.set(parent, present);
2312
2410
  return {
2313
2411
  nodes: this.collectChildren(parent),
2314
2412
  changed: false
@@ -2318,7 +2416,7 @@ var UiGraphBuilder = class {
2318
2416
  this.reconcileDepth++;
2319
2417
  let result;
2320
2418
  try {
2321
- result = this.reconcile(parent, definitions);
2419
+ result = this.reconcile(parent, present);
2322
2420
  } finally {
2323
2421
  this.reconcileDepth--;
2324
2422
  this.reconciling.delete(parent);
@@ -2326,7 +2424,11 @@ var UiGraphBuilder = class {
2326
2424
  const deferred = this.deferredChildren.get(parent);
2327
2425
  if (deferred !== void 0) {
2328
2426
  this.deferredChildren.delete(parent);
2329
- return this.reconcileChildren(parent, deferred);
2427
+ const again = this.reconcileChildren(parent, deferred);
2428
+ return {
2429
+ nodes: again.nodes,
2430
+ changed: result.changed || again.changed
2431
+ };
2330
2432
  }
2331
2433
  if (this.reconcileDepth === 0) this.components?.flushMounts();
2332
2434
  return result;
@@ -2458,7 +2560,7 @@ var UiGraphBuilder = class {
2458
2560
  matched.add(anchor);
2459
2561
  if (this.reconciledFrom.get(anchor) === element) return anchor;
2460
2562
  }
2461
- this.reconcileChildren(anchor, [resolver.resolve(element, anchorId)]);
2563
+ this.reconcileChildren(anchor, [resolver.resolve(element, anchorId, anchor)]);
2462
2564
  this.reconciledFrom.set(anchor, element);
2463
2565
  return anchor;
2464
2566
  }
@@ -2524,7 +2626,7 @@ var UiGraphBuilder = class {
2524
2626
  matchNode(parent, definition, unkeyed, matched) {
2525
2627
  const key = this.elementKey(definition);
2526
2628
  if (key !== void 0) {
2527
- const candidate = this.graph.getNode(`${parent.id}:${key}`);
2629
+ const candidate = this.graph.getNode(`${parent.id}:${idSegment(key)}`);
2528
2630
  if (candidate === void 0 || candidate.parent !== parent || candidate.type !== definition.type) return;
2529
2631
  if (matched.has(candidate)) throw new Error(`Duplicate key '${key}' in parent '${parent.id}'.`);
2530
2632
  return candidate;
@@ -2552,8 +2654,13 @@ var UiGraphBuilder = class {
2552
2654
  createNode(parent, definition, index) {
2553
2655
  const id = this.createNodeId(parent, definition, index);
2554
2656
  const node = this.graph.createNode(id, definition.type);
2555
- this.reconcileProps(node, definition.props, parent);
2556
- this.reconcileChildren(node, definition.children);
2657
+ try {
2658
+ this.reconcileProps(node, definition.props, parent);
2659
+ this.reconcileChildren(node, definition.children);
2660
+ } catch (error) {
2661
+ this.removeSubtree(node);
2662
+ throw error;
2663
+ }
2557
2664
  return node;
2558
2665
  }
2559
2666
  /**
@@ -2583,7 +2690,7 @@ var UiGraphBuilder = class {
2583
2690
  if (isObservable(definition) || isComponentLikeElement(definition)) continue;
2584
2691
  const key = this.elementKey(definition);
2585
2692
  if (key === void 0) continue;
2586
- const candidate = this.graph.getNode(`${parent.id}:${key}`);
2693
+ const candidate = this.graph.getNode(`${parent.id}:${idSegment(key)}`);
2587
2694
  const position = candidate === void 0 ? void 0 : at.get(candidate);
2588
2695
  if (candidate === void 0 || position === void 0 || candidate.type !== definition.type) continue;
2589
2696
  nodes.push(candidate);
@@ -2731,6 +2838,19 @@ var UiGraphBuilder = class {
2731
2838
  return this.modifiers.get(node);
2732
2839
  }
2733
2840
  /**
2841
+ * Where the element a node was last reconciled from was written, for
2842
+ * the inspector and for tools that edit the source. For a component's
2843
+ * anchor it is where the component was used, so walking up from a
2844
+ * node gives the line that made it and then each place it was used.
2845
+ *
2846
+ * Read off the definition the builder already keeps for each node
2847
+ * rather than stored on the node, so it costs a tree nothing when no
2848
+ * element carries one, which is every element in production.
2849
+ */
2850
+ sourceOf(node) {
2851
+ return this.reconciledFrom.get(node)?.source;
2852
+ }
2853
+ /**
2734
2854
  * Rejects a prop nothing will ever read.
2735
2855
  *
2736
2856
  * Every property layout, paint, input or the environment consults is
@@ -2834,7 +2954,7 @@ var UiGraphBuilder = class {
2834
2954
  */
2835
2955
  createNodeId(parent, definition, index) {
2836
2956
  const key = this.elementKey(definition);
2837
- if (key !== void 0) return `${parent.id}:${key}`;
2957
+ if (key !== void 0) return `${parent.id}:${idSegment(key)}`;
2838
2958
  return `${parent.id}:${index}`;
2839
2959
  }
2840
2960
  /**
@@ -2853,7 +2973,7 @@ var UiGraphBuilder = class {
2853
2973
  */
2854
2974
  createComponentAnchorId(parent, element, index) {
2855
2975
  const key = element.key === void 0 || element.key === null ? index : element.key;
2856
- return `${parent.id}:component:${String(key)}`;
2976
+ return `${parent.id}:component:${idSegment(String(key))}`;
2857
2977
  }
2858
2978
  };
2859
2979
  const NO_NODES = /* @__PURE__ */ new Set();
@@ -2882,6 +3002,24 @@ function longestIncreasing(values) {
2882
3002
  for (let index = tops.length > 0 ? tops[tops.length - 1] : -1; index !== -1; index = previous[index]) run.push(index);
2883
3003
  return run.reverse();
2884
3004
  }
3005
+ /**
3006
+ * A key as one segment of a node id, which joins segments with colons.
3007
+ *
3008
+ * A key holding a colon would otherwise read as more than one segment,
3009
+ * and could spell another node's id: a row keyed `a:0` beside a row
3010
+ * keyed `a` whose first child is `a:0` again, which threw "already used
3011
+ * outside parent" for a list keyed by ids (a layers panel's). Escaped,
3012
+ * a key is one segment whatever it holds, and a key with no colon in it
3013
+ * is unchanged.
3014
+ */
3015
+ function idSegment(key) {
3016
+ const text = String(key);
3017
+ return text.includes(":") || text.includes("%") ? text.replace(/%/g, "%25").replace(/:/g, "%3A") : text;
3018
+ }
3019
+ /** A child that draws nothing: null, undefined or a boolean, from plain script. */
3020
+ function isNothing(child) {
3021
+ return child === null || child === void 0 || typeof child === "boolean";
3022
+ }
2885
3023
  //#endregion
2886
3024
  //#region src/composition/UiVirtualWindow.ts
2887
3025
  /** Marks an item wrapper with its index so the host can measure it. */
@@ -3535,7 +3673,7 @@ const sizeContainer = defineModifier({
3535
3673
  const vertical = inset("paddingTop", "paddingY") + inset("paddingBottom", "paddingY");
3536
3674
  args.source.report(Math.max(0, box.width - horizontal), Math.max(0, box.height - vertical));
3537
3675
  };
3538
- host.onLayout(report);
3676
+ host.onFlowLayout(report);
3539
3677
  report();
3540
3678
  }
3541
3679
  });
@@ -3580,7 +3718,7 @@ const breakpoint = defineModifier({
3580
3718
  }
3581
3719
  for (const [name, value] of written) host.set(name, value);
3582
3720
  };
3583
- host.onLayout(apply);
3721
+ host.onFlowLayout(apply);
3584
3722
  apply();
3585
3723
  }
3586
3724
  });
@@ -4828,14 +4966,17 @@ var UiShortcutRegistry = class {
4828
4966
  * `preventDefault()` so the keyboard controller's own defaults (Tab
4829
4967
  * navigation, Enter on a button, an editable's typing) do not also
4830
4968
  * act on a key that has already been spent.
4969
+ *
4970
+ * `code` is the physical key, which a digit written with Shift
4971
+ * matches on (see `sameStep`).
4831
4972
  */
4832
- handleKey(key, modifiers, focused) {
4973
+ handleKey(key, modifiers, focused, code) {
4833
4974
  if (MODIFIER_KEYS.has(key)) return false;
4834
4975
  if (focused !== null && isTypingInto(focused) && this.isEditingKey(key, modifiers)) {
4835
4976
  this.pending = [];
4836
4977
  return false;
4837
4978
  }
4838
- const step = stepFor(key, modifiers);
4979
+ const step = stepFor(key, modifiers, code);
4839
4980
  const at = this.now();
4840
4981
  if (this.pending.length > 0 && at - this.pendingAt > this.chordTimeout) this.pending = [];
4841
4982
  const sequence = [...this.pending, step];
@@ -5009,7 +5150,7 @@ function matchesPrefix(steps, sequence) {
5009
5150
  return sequence.every((step, index) => sameStep(steps[index], step));
5010
5151
  }
5011
5152
  function sameStep(want, got) {
5012
- if (want.key !== got.key) return false;
5153
+ if (want.key !== got.key && !shiftedDigit(want, got)) return false;
5013
5154
  if (isSymbol(want.key)) {
5014
5155
  if (isBare(want) && got.alt && !got.meta) return true;
5015
5156
  } else if (want.shift !== got.shift) return false;
@@ -5018,6 +5159,15 @@ function sameStep(want, got) {
5018
5159
  return want.ctrl === got.ctrl && want.meta === got.meta;
5019
5160
  }
5020
5161
  /**
5162
+ * A digit written with Shift, pressed: `Shift+1`, as a design tool's
5163
+ * "zoom to fit" is written, is the 1 key with Shift held, whatever
5164
+ * character that types. On a US keyboard it types `!`, on a French one
5165
+ * `1` itself, so the character cannot say; the physical key can.
5166
+ */
5167
+ function shiftedDigit(want, got) {
5168
+ return want.shift && got.shift && /^[0-9]$/.test(want.key) && got.code === `Digit${want.key}`;
5169
+ }
5170
+ /**
5021
5171
  * Whether a key is a character that isn't a letter: punctuation, a digit
5022
5172
  * or a symbol, which different layouts put under different keys and
5023
5173
  * reach with or without Shift. A letter keeps its Shift, which is what
@@ -5027,14 +5177,15 @@ function isSymbol(key) {
5027
5177
  return key.length === 1 && key !== " " && key.toLowerCase() === key.toUpperCase();
5028
5178
  }
5029
5179
  /** The press the person actually made. */
5030
- function stepFor(key, modifiers) {
5180
+ function stepFor(key, modifiers, code) {
5031
5181
  return {
5032
5182
  key: normalizeKey(key),
5033
5183
  mod: false,
5034
5184
  ctrl: modifiers.ctrl,
5035
5185
  meta: modifiers.meta,
5036
5186
  shift: modifiers.shift,
5037
- alt: modifiers.alt
5187
+ alt: modifiers.alt,
5188
+ ...code === void 0 ? {} : { code }
5038
5189
  };
5039
5190
  }
5040
5191
  /**
@@ -5407,6 +5558,12 @@ function dragSessionFor(node) {
5407
5558
  var LayoutNotifier = class {
5408
5559
  listeners = /* @__PURE__ */ new Map();
5409
5560
  last = /* @__PURE__ */ new Map();
5561
+ /**
5562
+ * Listeners told about the node's place in the layout and its scroll
5563
+ * offset, and not about where it is drawn (`addFlow`).
5564
+ */
5565
+ flowListeners = /* @__PURE__ */ new Map();
5566
+ flowLast = /* @__PURE__ */ new Map();
5410
5567
  /** Registers a listener; the returned function removes it. */
5411
5568
  add(node, listener) {
5412
5569
  let forNode = this.listeners.get(node);
@@ -5425,36 +5582,73 @@ var LayoutNotifier = class {
5425
5582
  }
5426
5583
  };
5427
5584
  }
5585
+ /**
5586
+ * Registers a listener told when the node's laid-out box or its own
5587
+ * scroll offset changes, and not when a scroll or a transform above it
5588
+ * moves it on screen; the returned function removes it.
5589
+ *
5590
+ * Most listeners are this kind: a size container, a breakpoint, a
5591
+ * layout animation, a scroll position read only the node's own box
5592
+ * and offset. Told of every move on screen, each of them was asked
5593
+ * on every step of a pan over a design tool's board, 2,600 of them,
5594
+ * to find nothing it reads had changed; and each answer was where
5595
+ * the node was drawn, worked out through every ancestor. Its place in
5596
+ * the layout is its record, read at once.
5597
+ */
5598
+ addFlow(node, listener) {
5599
+ let forNode = this.flowListeners.get(node);
5600
+ if (forNode === void 0) {
5601
+ forNode = /* @__PURE__ */ new Set();
5602
+ this.flowListeners.set(node, forNode);
5603
+ }
5604
+ forNode.add(listener);
5605
+ return () => {
5606
+ const set = this.flowListeners.get(node);
5607
+ if (set === void 0) return;
5608
+ set.delete(listener);
5609
+ if (set.size === 0) {
5610
+ this.flowListeners.delete(node);
5611
+ this.flowLast.delete(node);
5612
+ }
5613
+ };
5614
+ }
5428
5615
  get size() {
5429
- return this.listeners.size;
5616
+ return this.listeners.size + this.flowListeners.size;
5430
5617
  }
5431
5618
  /** Whether anything is listening, so a frame can skip the walk. */
5432
5619
  isEmpty() {
5433
- return this.listeners.size === 0;
5620
+ return this.listeners.size === 0 && this.flowListeners.size === 0;
5434
5621
  }
5435
5622
  /**
5436
5623
  * Calls the listeners of every watched node whose box or scroll
5437
5624
  * offset differs from what it was last told about.
5438
5625
  */
5439
- notify(read) {
5440
- for (const [node, listeners] of this.listeners) {
5441
- const current = read(node);
5442
- const previous = this.last.get(node);
5443
- if (previous !== void 0 && sameLayout(previous, current)) continue;
5444
- this.last.set(node, {
5445
- box: { ...current.box },
5446
- scrollX: current.scrollX,
5447
- scrollY: current.scrollY
5448
- });
5449
- for (const listener of listeners) listener(current.box);
5450
- }
5626
+ notify(read, readFlow = read) {
5627
+ notifyEach(this.listeners, this.last, read);
5628
+ notifyEach(this.flowListeners, this.flowLast, readFlow);
5451
5629
  }
5452
5630
  /** Drops a removed node's listeners, so nothing keeps it alive. */
5453
5631
  handleNodeRemoved(node) {
5454
5632
  this.listeners.delete(node);
5455
5633
  this.last.delete(node);
5634
+ this.flowListeners.delete(node);
5635
+ this.flowLast.delete(node);
5456
5636
  }
5457
5637
  };
5638
+ /** Calls the listeners of each node whose layout, as `read` gives it, differs from what they were last told. */
5639
+ function notifyEach(listeners, last, read) {
5640
+ for (const [node, forNode] of listeners) {
5641
+ const current = read(node);
5642
+ const previous = last.get(node);
5643
+ if (previous !== void 0 && sameLayout(previous, current)) continue;
5644
+ last.set(node, {
5645
+ box: { ...current.box },
5646
+ scrollX: current.scrollX,
5647
+ scrollY: current.scrollY
5648
+ });
5649
+ for (const listener of forNode) listener(current.box);
5650
+ }
5651
+ }
5458
5652
  function sameLayout(a, b) {
5459
5653
  return a.scrollX === b.scrollX && a.scrollY === b.scrollY && sameBox(a.box, b.box);
5460
5654
  }
@@ -5565,8 +5759,9 @@ const kind$4 = defineModifier({
5565
5759
  name: "focusRing",
5566
5760
  attach(host, options) {
5567
5761
  const shapes = ringShapes(options);
5762
+ const shown = host.environment(ShownAsKey) === "focused";
5568
5763
  const sync = () => {
5569
- host.decorate(host.isFocused() && host.isFocusVisible() ? shapes : null);
5764
+ host.decorate(shown || host.isFocused() && host.isFocusVisible() ? shapes : null);
5570
5765
  };
5571
5766
  sync();
5572
5767
  host.onFocusChange(sync);
@@ -5722,7 +5917,7 @@ const kind$3 = defineModifier({
5722
5917
  name: "autoFocus",
5723
5918
  attach(host, options) {
5724
5919
  let done = false;
5725
- host.onLayout(() => {
5920
+ host.onFlowLayout(() => {
5726
5921
  if (done) return;
5727
5922
  done = true;
5728
5923
  host.focus(options);
@@ -6048,7 +6243,7 @@ var DropTarget = class {
6048
6243
  }
6049
6244
  attach() {
6050
6245
  this.registration.sync();
6051
- this.host.onLayout(() => this.registration.sync());
6246
+ this.host.onFlowLayout(() => this.registration.sync());
6052
6247
  this.host.own(() => {
6053
6248
  this.stopScrolling();
6054
6249
  this.registration.dispose();
@@ -6526,7 +6721,7 @@ var Pinchable = class {
6526
6721
  if (box === null) return;
6527
6722
  const clamped = Math.min(Math.max(scale, this.options.minScale ?? 1), this.options.maxScale ?? 8);
6528
6723
  const [unrotatedX, unrotatedY] = unapply(anchorX - box.x - this.translateX, anchorY - box.y - this.translateY, this.scale, this.rotation);
6529
- const [reappliedX, reappliedY] = apply(unrotatedX, unrotatedY, clamped, rotation);
6724
+ const [reappliedX, reappliedY] = apply$1(unrotatedX, unrotatedY, clamped, rotation);
6530
6725
  this.scale = clamped;
6531
6726
  this.rotation = rotation;
6532
6727
  this.translateX = anchorX + panX - box.x - reappliedX;
@@ -6547,7 +6742,7 @@ var Pinchable = class {
6547
6742
  }
6548
6743
  };
6549
6744
  /** Rotates and scales a node-local point, the way the renderers do. */
6550
- function apply(x, y, scale, degrees) {
6745
+ function apply$1(x, y, scale, degrees) {
6551
6746
  const radians = degrees * Math.PI / 180;
6552
6747
  const cos = Math.cos(radians);
6553
6748
  const sin = Math.sin(radians);
@@ -6596,7 +6791,7 @@ const shortcutsKind = defineModifier({
6596
6791
  host.onRoot("keydown", (event) => {
6597
6792
  if (event.defaultPrevented) return;
6598
6793
  const key = event;
6599
- if (registry.handleKey(key.key, key.modifiers, key.target)) {
6794
+ if (registry.handleKey(key.key, key.modifiers, key.target, key.code)) {
6600
6795
  event.preventDefault();
6601
6796
  event.stopPropagation();
6602
6797
  }
@@ -6775,7 +6970,7 @@ var LayoutAnimation = class {
6775
6970
  this.baseScaleY = numberOr(declared.scaleY, 1);
6776
6971
  this.baseRotation = numberOr(declared.rotation, 0);
6777
6972
  }
6778
- this.host.onLayout(() => this.handleLayout());
6973
+ this.host.onFlowLayout(() => this.handleLayout());
6779
6974
  }
6780
6975
  makeCell(read, write) {
6781
6976
  return {
@@ -6852,7 +7047,7 @@ var LayoutAnimation = class {
6852
7047
  }
6853
7048
  const named = this.options.spring ?? "snappy";
6854
7049
  this.host.spring(cell, 0, {
6855
- spring: typeof named === "string" ? defaultMotion.springs[named] : named,
7050
+ spring: springIn(motionOf(this.host.node), named),
6856
7051
  restDelta: .5
6857
7052
  });
6858
7053
  }
@@ -6861,6 +7056,1340 @@ function numberOr(value, fallback) {
6861
7056
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
6862
7057
  }
6863
7058
  //#endregion
7059
+ //#region src/rendering/svg/SvgXml.ts
7060
+ /**
7061
+ * The document's outermost element, or null when there is none.
7062
+ */
7063
+ function parseSvgXml(source) {
7064
+ const stack = [];
7065
+ let root = null;
7066
+ let index = 0;
7067
+ const close = () => {
7068
+ const element = stack.pop();
7069
+ const parent = stack.at(-1);
7070
+ if (parent === void 0) root ??= element;
7071
+ else parent.children.push(element);
7072
+ };
7073
+ while (index < source.length) {
7074
+ const open = source.indexOf("<", index);
7075
+ if (open === -1) break;
7076
+ if (open > index && stack.length > 0) stack.at(-1).text += decodeEntities(source.slice(index, open));
7077
+ if (source.startsWith("<!--", open)) {
7078
+ const end = source.indexOf("-->", open + 4);
7079
+ index = end === -1 ? source.length : end + 3;
7080
+ continue;
7081
+ }
7082
+ if (source.startsWith("<![CDATA[", open)) {
7083
+ const end = source.indexOf("]]>", open + 9);
7084
+ const text = source.slice(open + 9, end === -1 ? source.length : end);
7085
+ if (stack.length > 0) stack.at(-1).text += text;
7086
+ index = end === -1 ? source.length : end + 3;
7087
+ continue;
7088
+ }
7089
+ if (source[open + 1] === "?" || source[open + 1] === "!") {
7090
+ index = skipDeclaration(source, open);
7091
+ continue;
7092
+ }
7093
+ if (source[open + 1] === "/") {
7094
+ const end = source.indexOf(">", open);
7095
+ const name = source.slice(open + 2, end === -1 ? source.length : end).trim();
7096
+ const at = stack.map((element) => element.name).lastIndexOf(name);
7097
+ if (at !== -1) while (stack.length > at) close();
7098
+ index = end === -1 ? source.length : end + 1;
7099
+ continue;
7100
+ }
7101
+ const tag = readTag(source, open);
7102
+ stack.push({
7103
+ name: tag.name,
7104
+ attributes: tag.attributes,
7105
+ children: [],
7106
+ text: ""
7107
+ });
7108
+ if (tag.selfClosing) close();
7109
+ index = tag.end;
7110
+ }
7111
+ while (stack.length > 0) close();
7112
+ return root;
7113
+ }
7114
+ /** Past a `<?…?>` or a `<!…>`, minding a doctype's bracketed subset. */
7115
+ function skipDeclaration(source, open) {
7116
+ let depth = 0;
7117
+ for (let i = open + 1; i < source.length; i++) {
7118
+ const char = source[i];
7119
+ if (char === "[") depth++;
7120
+ else if (char === "]") depth--;
7121
+ else if (char === ">" && depth <= 0) return i + 1;
7122
+ }
7123
+ return source.length;
7124
+ }
7125
+ function readTag(source, open) {
7126
+ let i = open + 1;
7127
+ const nameStart = i;
7128
+ while (i < source.length && !/[\s/>]/.test(source[i])) i++;
7129
+ const name = source.slice(nameStart, i);
7130
+ const attributes = {};
7131
+ for (;;) {
7132
+ while (i < source.length && /\s/.test(source[i])) i++;
7133
+ if (i >= source.length) return {
7134
+ name,
7135
+ attributes,
7136
+ selfClosing: false,
7137
+ end: i
7138
+ };
7139
+ if (source[i] === ">") return {
7140
+ name,
7141
+ attributes,
7142
+ selfClosing: false,
7143
+ end: i + 1
7144
+ };
7145
+ if (source[i] === "/") {
7146
+ const end = source.indexOf(">", i);
7147
+ return {
7148
+ name,
7149
+ attributes,
7150
+ selfClosing: true,
7151
+ end: end === -1 ? source.length : end + 1
7152
+ };
7153
+ }
7154
+ const keyStart = i;
7155
+ while (i < source.length && !/[\s=/>]/.test(source[i])) i++;
7156
+ const key = source.slice(keyStart, i);
7157
+ while (i < source.length && /\s/.test(source[i])) i++;
7158
+ if (source[i] !== "=") {
7159
+ if (key !== "") attributes[key] = "";
7160
+ else i++;
7161
+ continue;
7162
+ }
7163
+ i++;
7164
+ while (i < source.length && /\s/.test(source[i])) i++;
7165
+ const quote = source[i];
7166
+ if (quote === "\"" || quote === "'") {
7167
+ const end = source.indexOf(quote, i + 1);
7168
+ attributes[key] = decodeEntities(source.slice(i + 1, end === -1 ? source.length : end));
7169
+ i = end === -1 ? source.length : end + 1;
7170
+ } else {
7171
+ const valueStart = i;
7172
+ while (i < source.length && !/[\s>]/.test(source[i])) i++;
7173
+ attributes[key] = decodeEntities(source.slice(valueStart, i));
7174
+ }
7175
+ }
7176
+ }
7177
+ const NAMED_ENTITIES = {
7178
+ amp: "&",
7179
+ lt: "<",
7180
+ gt: ">",
7181
+ quot: "\"",
7182
+ apos: "'"
7183
+ };
7184
+ function decodeEntities(text) {
7185
+ if (!text.includes("&")) return text;
7186
+ return text.replace(/&(#x[0-9a-f]+|#\d+|[a-z]+);/gi, (whole, entity) => {
7187
+ if (entity[0] === "#") {
7188
+ const code = entity[1] === "x" || entity[1] === "X" ? parseInt(entity.slice(2), 16) : parseInt(entity.slice(1), 10);
7189
+ return Number.isFinite(code) ? String.fromCodePoint(code) : whole;
7190
+ }
7191
+ return NAMED_ENTITIES[entity] ?? whole;
7192
+ });
7193
+ }
7194
+ //#endregion
7195
+ //#region src/rendering/svg/SvgDocument.ts
7196
+ /** Elements drawn as nothing, because nothing here draws them yet. */
7197
+ const UNSUPPORTED_ELEMENTS = /* @__PURE__ */ new Set([
7198
+ "text",
7199
+ "image",
7200
+ "foreignObject",
7201
+ "pattern",
7202
+ "marker"
7203
+ ]);
7204
+ /** Attributes that point at one of those. */
7205
+ const UNSUPPORTED_ATTRIBUTES = {
7206
+ "marker-start": "marker",
7207
+ "marker-mid": "marker",
7208
+ "marker-end": "marker"
7209
+ };
7210
+ /**
7211
+ * Reads an SVG file. Throws when it holds no `<svg>` element, since
7212
+ * there is then nothing to draw and the caller should fall back.
7213
+ */
7214
+ function parseSvg(source) {
7215
+ const root = parseSvgXml(source);
7216
+ if (root === null || localName(root.name) !== "svg") throw new Error("This is not an SVG file: it has no <svg> element.");
7217
+ const ids = /* @__PURE__ */ new Map();
7218
+ const rules = [];
7219
+ const unsupported = /* @__PURE__ */ new Set();
7220
+ const visit = (element) => {
7221
+ const name = localName(element.name);
7222
+ const id = element.attributes.id;
7223
+ if (id !== void 0 && !ids.has(id)) ids.set(id, element);
7224
+ if (name === "style") rules.push(...parseStyleSheet(element.text));
7225
+ if (UNSUPPORTED_ELEMENTS.has(name)) unsupported.add(name);
7226
+ if (name === "filter" && dropShadowOf(element) === void 0) unsupported.add("filter");
7227
+ for (const [attribute, feature] of Object.entries(UNSUPPORTED_ATTRIBUTES)) if (element.attributes[attribute] !== void 0 && element.attributes[attribute] !== "none") unsupported.add(feature);
7228
+ for (const attribute of ["mask", "filter"]) {
7229
+ const value = element.attributes[attribute];
7230
+ if (value !== void 0 && value !== "none") references.push([attribute, value]);
7231
+ }
7232
+ element.children.forEach(visit);
7233
+ };
7234
+ const references = [];
7235
+ visit(root);
7236
+ for (const [attribute, value] of references) {
7237
+ const id = referencedId(value);
7238
+ const target = id === void 0 ? void 0 : ids.get(id);
7239
+ if (target === void 0 || localName(target.name) !== attribute) unsupported.add(attribute);
7240
+ }
7241
+ const viewBox = parseViewBox(root.attributes.viewBox);
7242
+ const width = absoluteLength(root.attributes.width) ?? viewBox?.width ?? 300;
7243
+ const height = absoluteLength(root.attributes.height) ?? viewBox?.height ?? 150;
7244
+ return {
7245
+ root,
7246
+ width,
7247
+ height,
7248
+ viewBox: viewBox ?? {
7249
+ x: 0,
7250
+ y: 0,
7251
+ width,
7252
+ height
7253
+ },
7254
+ ids,
7255
+ rules,
7256
+ unsupported: [...unsupported]
7257
+ };
7258
+ }
7259
+ /** The id a value of the form `url(#id)` names, or undefined for anything else. */
7260
+ function referencedId(value) {
7261
+ return /^\s*url\(\s*['"]?#([^'")]+)['"]?\s*\)\s*$/.exec(value)?.[1];
7262
+ }
7263
+ /**
7264
+ * The `<feDropShadow>` a `<filter>` is, when it is exactly one drop
7265
+ * shadow of the element itself; undefined for any other filter, which
7266
+ * is not drawn.
7267
+ */
7268
+ function dropShadowOf(filter) {
7269
+ const primitives = filter.children.filter((child) => !NOT_PRIMITIVES.has(localName(child.name)));
7270
+ const [primitive] = primitives;
7271
+ if (primitives.length !== 1 || localName(primitive.name) !== "feDropShadow") return;
7272
+ const input = primitive.attributes.in;
7273
+ return input === void 0 || input === "SourceGraphic" ? primitive : void 0;
7274
+ }
7275
+ /** What a `<filter>` may hold that is not a step of it. */
7276
+ const NOT_PRIMITIVES = /* @__PURE__ */ new Set([
7277
+ "title",
7278
+ "desc",
7279
+ "metadata"
7280
+ ]);
7281
+ /** An element's name without its namespace prefix: `svg:path` is `path`. */
7282
+ function localName(name) {
7283
+ const colon = name.indexOf(":");
7284
+ return colon === -1 ? name : name.slice(colon + 1);
7285
+ }
7286
+ /** A `viewBox`, or undefined when it is absent or describes nothing. */
7287
+ function parseViewBox(value) {
7288
+ if (value === void 0) return;
7289
+ const [x, y, width, height] = numbersIn(value);
7290
+ if (x === void 0 || y === void 0 || width === void 0 || height === void 0 || width <= 0 || height <= 0) return;
7291
+ return {
7292
+ x,
7293
+ y,
7294
+ width,
7295
+ height
7296
+ };
7297
+ }
7298
+ /** Every number in a list separated by commas and spaces. */
7299
+ function numbersIn(value) {
7300
+ return (value.match(/[-+]?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?/gi) ?? []).map(Number);
7301
+ }
7302
+ /** A length in pixels, or undefined for a percentage or anything that is not one. */
7303
+ function absoluteLength(value) {
7304
+ if (value === void 0 || value.trim().endsWith("%")) return;
7305
+ const number = parseFloat(value);
7306
+ if (!Number.isFinite(number) || number <= 0) return;
7307
+ const unit = value.trim().replace(/^[-+\d.e]+/i, "");
7308
+ return number * (UNIT_PIXELS[unit] ?? 1);
7309
+ }
7310
+ /** CSS's absolute units in pixels, 96 to the inch. */
7311
+ const UNIT_PIXELS = {
7312
+ "": 1,
7313
+ px: 1,
7314
+ pt: 96 / 72,
7315
+ pc: 16,
7316
+ in: 96,
7317
+ cm: 96 / 2.54,
7318
+ mm: 96 / 25.4
7319
+ };
7320
+ /** The declarations of an inline `style` attribute or a rule's block. */
7321
+ function parseDeclarations(text) {
7322
+ const declarations = {};
7323
+ for (const part of text.split(";")) {
7324
+ const colon = part.indexOf(":");
7325
+ if (colon === -1) continue;
7326
+ const property = part.slice(0, colon).trim().toLowerCase();
7327
+ const value = part.slice(colon + 1).replace(/!important/i, "").trim();
7328
+ if (property !== "" && value !== "") declarations[property] = value;
7329
+ }
7330
+ return declarations;
7331
+ }
7332
+ /**
7333
+ * A `<style>`'s rules. Only compound selectors are understood, which is
7334
+ * what an exporter writes (`.st0{fill:#fff}`); a rule whose selector
7335
+ * has a combinator or a pseudo-class is skipped rather than misapplied.
7336
+ */
7337
+ function parseStyleSheet(text) {
7338
+ const rules = [];
7339
+ const source = text.replace(/\/\*[\s\S]*?\*\//g, "");
7340
+ for (const match of source.matchAll(/([^{}]+)\{([^}]*)\}/g)) {
7341
+ const declarations = parseDeclarations(match[2]);
7342
+ for (const raw of match[1].split(",")) {
7343
+ const selector = parseSelector(raw.trim());
7344
+ if (selector !== null) {
7345
+ const specificity = (selector.id === void 0 ? 0 : 100) + selector.classes.length * 10 + (selector.name === void 0 ? 0 : 1);
7346
+ rules.push({
7347
+ selector,
7348
+ declarations,
7349
+ specificity
7350
+ });
7351
+ }
7352
+ }
7353
+ }
7354
+ return rules;
7355
+ }
7356
+ function parseSelector(text) {
7357
+ if (text === "" || /[\s>+~:[\]*]/.test(text)) return null;
7358
+ const parts = text.match(/[.#]?[^.#]+/g) ?? [];
7359
+ let name;
7360
+ let id;
7361
+ const classes = [];
7362
+ for (const part of parts) if (part.startsWith("#")) id = part.slice(1);
7363
+ else if (part.startsWith(".")) classes.push(part.slice(1));
7364
+ else name = part;
7365
+ return {
7366
+ ...name === void 0 ? {} : { name },
7367
+ ...id === void 0 ? {} : { id },
7368
+ classes
7369
+ };
7370
+ }
7371
+ /** Whether a rule's selector matches an element. */
7372
+ function selectorMatches(selector, element) {
7373
+ if (selector.name !== void 0 && localName(element.name) !== selector.name) return false;
7374
+ if (selector.id !== void 0 && element.attributes.id !== selector.id) return false;
7375
+ if (selector.classes.length > 0) {
7376
+ const classes = (element.attributes.class ?? "").split(/\s+/);
7377
+ return selector.classes.every((name) => classes.includes(name));
7378
+ }
7379
+ return true;
7380
+ }
7381
+ //#endregion
7382
+ //#region src/rendering/svg/SvgDraw.ts
7383
+ /**
7384
+ * Draws a read SVG file into a box, through the surface every painted
7385
+ * node draws through, so it is vectors on either renderer and is drawn
7386
+ * again at whatever size the box is laid out at.
7387
+ */
7388
+ function drawSvg(document, surface, box, options = {}) {
7389
+ const { viewBox } = document;
7390
+ const fit = options.fit ?? "contain";
7391
+ let scaleX = box.width / viewBox.width;
7392
+ let scaleY = box.height / viewBox.height;
7393
+ if (fit !== "fill") {
7394
+ const contain = Math.min(scaleX, scaleY);
7395
+ const natural = document.width / viewBox.width;
7396
+ const uniform = fit === "cover" ? Math.max(scaleX, scaleY) : fit === "none" ? natural : fit === "scale-down" ? Math.min(contain, natural) : contain;
7397
+ scaleX = uniform;
7398
+ scaleY = uniform;
7399
+ }
7400
+ surface.save();
7401
+ if (fit === "cover" || fit === "none") {
7402
+ surface.beginPath();
7403
+ surface.rect(0, 0, box.width, box.height);
7404
+ surface.clip();
7405
+ }
7406
+ surface.translate((box.width - viewBox.width * scaleX) / 2, (box.height - viewBox.height * scaleY) / 2);
7407
+ surface.scale(scaleX, scaleY);
7408
+ surface.translate(-viewBox.x, -viewBox.y);
7409
+ const context = {
7410
+ document,
7411
+ surface,
7412
+ currentColor: options.color ?? "currentColor",
7413
+ using: /* @__PURE__ */ new Set()
7414
+ };
7415
+ drawChildren(document.root, cascade(INITIAL_STYLE, declarationsOf(document.root, document), context), viewBox, context);
7416
+ surface.restore();
7417
+ }
7418
+ const paints = /* @__PURE__ */ new WeakMap();
7419
+ /**
7420
+ * A read SVG file as a node's `paint`: drawn to fill the node, and the
7421
+ * file's own size as the node's when the layout gives it none.
7422
+ *
7423
+ * The same object for the same file and options, so a node given it
7424
+ * again is not drawn again; see `paintValuesEqual`.
7425
+ */
7426
+ function svgPaint(document, options = {}) {
7427
+ let byOptions = paints.get(document);
7428
+ if (byOptions === void 0) {
7429
+ byOptions = /* @__PURE__ */ new Map();
7430
+ paints.set(document, byOptions);
7431
+ }
7432
+ const key = JSON.stringify([options.fit ?? "contain", options.color ?? "currentColor"]);
7433
+ let paint = byOptions.get(key);
7434
+ if (paint === void 0) {
7435
+ paint = {
7436
+ draw: (surface, box) => drawSvg(document, surface, box, options),
7437
+ inputs: [document, key],
7438
+ intrinsicWidth: document.width,
7439
+ intrinsicHeight: document.height
7440
+ };
7441
+ byOptions.set(key, paint);
7442
+ }
7443
+ return paint;
7444
+ }
7445
+ const BLACK = {
7446
+ kind: "color",
7447
+ color: {
7448
+ r: 0,
7449
+ g: 0,
7450
+ b: 0,
7451
+ a: 1
7452
+ }
7453
+ };
7454
+ const NONE = { kind: "none" };
7455
+ const INITIAL_STYLE = {
7456
+ fill: BLACK,
7457
+ fillOpacity: 1,
7458
+ fillRule: "nonzero",
7459
+ stroke: NONE,
7460
+ strokeOpacity: 1,
7461
+ strokeWidth: 1,
7462
+ lineCap: "butt",
7463
+ lineJoin: "miter",
7464
+ miterLimit: 4,
7465
+ dash: [],
7466
+ dashOffset: 0,
7467
+ clipRule: "nonzero",
7468
+ color: null,
7469
+ visible: true
7470
+ };
7471
+ /** Drawn only when something points at them, or not at all. */
7472
+ const NOT_DRAWN = /* @__PURE__ */ new Set([
7473
+ "defs",
7474
+ "clipPath",
7475
+ "linearGradient",
7476
+ "radialGradient",
7477
+ "symbol",
7478
+ "style",
7479
+ "title",
7480
+ "desc",
7481
+ "metadata",
7482
+ "mask",
7483
+ "filter",
7484
+ "pattern",
7485
+ "marker",
7486
+ "text",
7487
+ "image",
7488
+ "foreignObject"
7489
+ ]);
7490
+ function drawChildren(element, style, viewport, context) {
7491
+ for (const child of element.children) drawElement(child, style, viewport, context);
7492
+ }
7493
+ function drawElement(element, inherited, viewport, context) {
7494
+ const name = localName(element.name);
7495
+ if (NOT_DRAWN.has(name)) return;
7496
+ const declared = declarationsOf(element, context.document);
7497
+ if (declared.display === "none") return;
7498
+ const style = cascade(inherited, declared, context);
7499
+ const { surface } = context;
7500
+ surface.save();
7501
+ if (declared.transform !== void 0) applyTransform$1(surface, declared.transform);
7502
+ const opacity = unit(declared.opacity);
7503
+ if (opacity < 1) surface.alpha(opacity);
7504
+ if (declared["clip-path"] !== void 0) clipTo(declared["clip-path"], viewport, context);
7505
+ const content = (at) => {
7506
+ switch (name) {
7507
+ case "g":
7508
+ case "a":
7509
+ case "switch":
7510
+ drawChildren(element, style, viewport, at);
7511
+ break;
7512
+ case "svg":
7513
+ drawNested(element, style, viewport, at);
7514
+ break;
7515
+ case "use":
7516
+ drawUse(element, style, viewport, at);
7517
+ break;
7518
+ default: {
7519
+ const shape = shapeOf(element, name, viewport);
7520
+ if (shape !== null && style.visible) paintShape(shape, style, viewport, at);
7521
+ }
7522
+ }
7523
+ };
7524
+ const filtered = () => {
7525
+ const filter = declared.filter === void 0 ? void 0 : referencedElement(declared.filter, "filter", context);
7526
+ const shadow = filter === void 0 ? void 0 : dropShadowOf(filter);
7527
+ if (filter === void 0 || shadow === void 0) content(context);
7528
+ else drawWithShadow(element, filter, shadow, style, viewport, context, content);
7529
+ };
7530
+ const mask = declared.mask === void 0 ? void 0 : referencedElement(declared.mask, "mask", context);
7531
+ if (mask === void 0 || context.using.has(mask)) filtered();
7532
+ else drawMasked(element, mask, viewport, context, filtered);
7533
+ surface.restore();
7534
+ }
7535
+ /** The element of a kind that a `url(#id)` value names, if it is one. */
7536
+ function referencedElement(value, kind, context) {
7537
+ const id = referencedId(value);
7538
+ const target = id === void 0 ? void 0 : context.document.ids.get(id);
7539
+ return target !== void 0 && localName(target.name) === kind ? target : void 0;
7540
+ }
7541
+ /**
7542
+ * Draws an element through a `<mask>`: its drawing is a layer, and the
7543
+ * mask's children, drawn into the layer's mask, say how much of it
7544
+ * shows. By their luminance, or by their alpha for `mask-type: alpha`.
7545
+ *
7546
+ * The mask's region and its content are placed in the element's user
7547
+ * space or in fractions of its bounding box, as `maskUnits` and
7548
+ * `maskContentUnits` say; the region is cut to as the layer is.
7549
+ */
7550
+ function drawMasked(element, mask, viewport, context, draw) {
7551
+ const { surface } = context;
7552
+ const attributes = mask.attributes;
7553
+ const declared = declarationsOf(mask, context.document);
7554
+ const bounds = attributes.maskUnits === "userSpaceOnUse" && attributes.maskContentUnits !== "objectBoundingBox" ? void 0 : elementBounds(element, viewport, context);
7555
+ const region = regionOf(attributes, attributes.maskUnits === "userSpaceOnUse", bounds, viewport);
7556
+ surface.beginMaskedLayer(region.x, region.y, region.width, region.height);
7557
+ draw();
7558
+ surface.layerMask(declared["mask-type"] === "alpha" ? "alpha" : "luminance");
7559
+ if (attributes.maskContentUnits === "objectBoundingBox") surface.transform(bounds.width, 0, 0, bounds.height, bounds.x, bounds.y);
7560
+ const root = cascade(INITIAL_STYLE, declarationsOf(context.document.root, context.document), context);
7561
+ context.using.add(mask);
7562
+ drawChildren(mask, cascade(root, declared, context), viewport, {
7563
+ ...context,
7564
+ flood: void 0
7565
+ });
7566
+ context.using.delete(mask);
7567
+ surface.endMaskedLayer();
7568
+ }
7569
+ /**
7570
+ * Draws an element with the drop shadow its filter is, and then the
7571
+ * element over it, both cut to the filter's region as a filter's result
7572
+ * is.
7573
+ *
7574
+ * The shadow is the element again, every paint of it the flood colour,
7575
+ * moved by `dx` and `dy` and blurred by `stdDeviation`: its alpha in
7576
+ * the flood colour, which is what `<feDropShadow>` makes. It is drawn
7577
+ * as a layer composited at the flood opacity, so where two of a group's
7578
+ * shapes overlap their shadow is not darker; a host without layers
7579
+ * draws it shape by shape, which differs only there.
7580
+ */
7581
+ function drawWithShadow(element, filter, shadow, style, viewport, context, draw) {
7582
+ const { surface } = context;
7583
+ const attributes = filter.attributes;
7584
+ const userSpace = attributes.filterUnits === "userSpaceOnUse";
7585
+ const primitivesInBox = attributes.primitiveUnits === "objectBoundingBox";
7586
+ const bounds = userSpace && !primitivesInBox ? void 0 : elementBounds(element, viewport, context);
7587
+ const region = regionOf(attributes, userSpace, bounds, viewport);
7588
+ if (!(region.width > 0) || !(region.height > 0)) return;
7589
+ const declared = declarationsOf(shadow, context.document);
7590
+ const number = (value, fallback) => {
7591
+ const parsed = value === void 0 ? NaN : parseFloat(value);
7592
+ return Number.isFinite(parsed) ? parsed : fallback;
7593
+ };
7594
+ let dx = number(declared.dx, 2);
7595
+ let dy = number(declared.dy, 2);
7596
+ const [sx, sy] = numbersIn(declared.stdDeviation ?? "2");
7597
+ let deviation = Math.max(0, sy === void 0 ? sx ?? 2 : ((sx ?? 0) + sy) / 2);
7598
+ if (primitivesInBox) {
7599
+ dx *= bounds.width;
7600
+ dy *= bounds.height;
7601
+ deviation *= Math.sqrt((bounds.width ** 2 + bounds.height ** 2) / 2);
7602
+ }
7603
+ const color = colorValue(declared["flood-color"] ?? "black", style, context) ?? BLACK_COLOR;
7604
+ const opacity = unit(declared["flood-opacity"]);
7605
+ surface.save();
7606
+ surface.beginPath();
7607
+ surface.rect(region.x, region.y, region.width, region.height);
7608
+ surface.clip();
7609
+ if (opacity > 0) {
7610
+ surface.save();
7611
+ if (opacity < 1) surface.alpha(opacity);
7612
+ surface.beginMaskedLayer(region.x, region.y, region.width, region.height);
7613
+ surface.translate(dx, dy);
7614
+ if (deviation > 0) surface.blur(deviation);
7615
+ draw({
7616
+ ...context,
7617
+ flood: color
7618
+ });
7619
+ surface.endMaskedLayer();
7620
+ surface.restore();
7621
+ }
7622
+ draw(context);
7623
+ surface.restore();
7624
+ }
7625
+ /**
7626
+ * A mask's or a filter's region: its `x`, `y`, `width` and `height` in
7627
+ * user space, or in fractions of the element's bounding box, which is
7628
+ * where they are by default and where they default to a tenth beyond
7629
+ * it on every side.
7630
+ */
7631
+ function regionOf(attributes, userSpace, bounds, viewport) {
7632
+ if (userSpace) return {
7633
+ x: length(attributes.x ?? "-10%", viewport.width),
7634
+ y: length(attributes.y ?? "-10%", viewport.height),
7635
+ width: length(attributes.width ?? "120%", viewport.width),
7636
+ height: length(attributes.height ?? "120%", viewport.height)
7637
+ };
7638
+ const box = bounds;
7639
+ return {
7640
+ x: box.x + fraction(attributes.x ?? "-10%") * box.width,
7641
+ y: box.y + fraction(attributes.y ?? "-10%") * box.height,
7642
+ width: fraction(attributes.width ?? "120%") * box.width,
7643
+ height: fraction(attributes.height ?? "120%") * box.height
7644
+ };
7645
+ }
7646
+ /** A fraction written as a number or a percentage: `0.5` and `50%` alike. */
7647
+ function fraction(value) {
7648
+ const trimmed = value.trim();
7649
+ const parsed = trimmed.endsWith("%") ? parseFloat(trimmed) / 100 : parseFloat(trimmed);
7650
+ return Number.isFinite(parsed) ? parsed : 0;
7651
+ }
7652
+ /**
7653
+ * An element's bounding box in its own user space, as `getBBox` gives
7654
+ * it: its geometry and its children's through their transforms, without
7655
+ * a stroke and without its own transform.
7656
+ */
7657
+ function elementBounds(element, viewport, context) {
7658
+ const bounds = new BoundsSurface();
7659
+ addBounds(element, IDENTITY$1, viewport, context, bounds, /* @__PURE__ */ new Set());
7660
+ return bounds.bounds();
7661
+ }
7662
+ function addBounds(element, matrix, viewport, context, bounds, using) {
7663
+ const name = localName(element.name);
7664
+ const into = new TransformedSurface(bounds, matrix);
7665
+ /** A child, or a used element, under its own transform. */
7666
+ const add = (child, at) => {
7667
+ const declared = declarationsOf(child, context.document);
7668
+ if (declared.display === "none") return;
7669
+ addBounds(child, declared.transform === void 0 ? at : multiply(at, parseTransform$1(declared.transform)), viewport, context, bounds, using);
7670
+ };
7671
+ switch (name) {
7672
+ case "g":
7673
+ case "a":
7674
+ case "switch":
7675
+ for (const child of element.children) if (!NOT_DRAWN.has(localName(child.name))) add(child, matrix);
7676
+ return;
7677
+ case "use": {
7678
+ const target = referenced(element, context.document);
7679
+ if (target === void 0 || using.has(element)) return;
7680
+ using.add(element);
7681
+ add(target, multiply(matrix, [
7682
+ 1,
7683
+ 0,
7684
+ 0,
7685
+ 1,
7686
+ length(element.attributes.x, viewport.width),
7687
+ length(element.attributes.y, viewport.height)
7688
+ ]));
7689
+ using.delete(element);
7690
+ return;
7691
+ }
7692
+ case "svg":
7693
+ case "symbol": {
7694
+ const a = element.attributes;
7695
+ into.rect(length(a.x, viewport.width), length(a.y, viewport.height), length(a.width ?? "100%", viewport.width), length(a.height ?? "100%", viewport.height));
7696
+ return;
7697
+ }
7698
+ default: shapeOf(element, name, viewport)?.trace(into);
7699
+ }
7700
+ }
7701
+ /** A nested `<svg>`: a viewport of its own, at its x and y. */
7702
+ function drawNested(element, style, viewport, context) {
7703
+ const { surface } = context;
7704
+ const x = length(element.attributes.x, viewport.width);
7705
+ const y = length(element.attributes.y, viewport.height);
7706
+ const width = length(element.attributes.width ?? "100%", viewport.width);
7707
+ const height = length(element.attributes.height ?? "100%", viewport.height);
7708
+ const viewBox = parseViewBox(element.attributes.viewBox);
7709
+ surface.translate(x, y);
7710
+ if (viewBox !== void 0) {
7711
+ const scale = Math.min(width / viewBox.width, height / viewBox.height);
7712
+ surface.translate((width - viewBox.width * scale) / 2, (height - viewBox.height * scale) / 2);
7713
+ surface.scale(scale, scale);
7714
+ surface.translate(-viewBox.x, -viewBox.y);
7715
+ }
7716
+ drawChildren(element, style, viewBox ?? {
7717
+ x: 0,
7718
+ y: 0,
7719
+ width,
7720
+ height
7721
+ }, context);
7722
+ }
7723
+ /** A `<use>`: the element it names, drawn at its x and y, inheriting from the `<use>`. */
7724
+ function drawUse(element, style, viewport, context) {
7725
+ const target = referenced(element, context.document);
7726
+ if (target === void 0 || context.using.has(element)) return;
7727
+ const { surface } = context;
7728
+ surface.translate(length(element.attributes.x, viewport.width), length(element.attributes.y, viewport.height));
7729
+ context.using.add(element);
7730
+ if (localName(target.name) === "symbol") drawNested(target, style, viewport, context);
7731
+ else {
7732
+ const name = localName(target.name);
7733
+ if (name === "g" || name === "svg" || name === "use" || !NOT_DRAWN.has(name)) drawElement(target, style, viewport, context);
7734
+ }
7735
+ context.using.delete(element);
7736
+ }
7737
+ function referenced(element, document) {
7738
+ const href = element.attributes.href ?? element.attributes["xlink:href"];
7739
+ return href?.startsWith("#") ? document.ids.get(href.slice(1)) : void 0;
7740
+ }
7741
+ function shapeOf(element, name, viewport) {
7742
+ const a = element.attributes;
7743
+ const w = viewport.width;
7744
+ const h = viewport.height;
7745
+ const diagonal = Math.sqrt((w * w + h * h) / 2);
7746
+ switch (name) {
7747
+ case "path": {
7748
+ const d = a.d;
7749
+ return d === void 0 || d.trim() === "" ? null : { trace: (surface) => surface.path(d) };
7750
+ }
7751
+ case "rect": {
7752
+ const x = length(a.x, w);
7753
+ const y = length(a.y, h);
7754
+ const width = length(a.width, w);
7755
+ const height = length(a.height, h);
7756
+ if (width <= 0 || height <= 0) return null;
7757
+ const rx = a.rx === void 0 ? void 0 : length(a.rx, w);
7758
+ const ry = a.ry === void 0 ? void 0 : length(a.ry, h);
7759
+ const radius = Math.min(rx ?? ry ?? 0, ry ?? rx ?? 0);
7760
+ return { trace: (surface) => radius > 0 ? surface.roundRect(x, y, width, height, radius) : surface.rect(x, y, width, height) };
7761
+ }
7762
+ case "circle": return ellipse(length(a.cx, w), length(a.cy, h), length(a.r, diagonal), length(a.r, diagonal));
7763
+ case "ellipse": return ellipse(length(a.cx, w), length(a.cy, h), length(a.rx, w), length(a.ry, h));
7764
+ case "line": {
7765
+ const [x1, y1, x2, y2] = [
7766
+ length(a.x1, w),
7767
+ length(a.y1, h),
7768
+ length(a.x2, w),
7769
+ length(a.y2, h)
7770
+ ];
7771
+ return { trace: (surface) => {
7772
+ surface.moveTo(x1, y1);
7773
+ surface.lineTo(x2, y2);
7774
+ } };
7775
+ }
7776
+ case "polyline":
7777
+ case "polygon": {
7778
+ const points = numbersIn(a.points ?? "");
7779
+ if (points.length < 4) return null;
7780
+ return { trace: (surface) => {
7781
+ surface.moveTo(points[0], points[1]);
7782
+ for (let i = 2; i + 1 < points.length; i += 2) surface.lineTo(points[i], points[i + 1]);
7783
+ if (name === "polygon") surface.closePath();
7784
+ } };
7785
+ }
7786
+ default: return null;
7787
+ }
7788
+ }
7789
+ /** An ellipse as two arcs of path data, which the surface turns into curves. */
7790
+ function ellipse(cx, cy, rx, ry) {
7791
+ if (rx <= 0 || ry <= 0) return null;
7792
+ const d = `M${cx - rx} ${cy}A${rx} ${ry} 0 1 0 ${cx + rx} ${cy}A${rx} ${ry} 0 1 0 ${cx - rx} ${cy}Z`;
7793
+ return { trace: (surface) => surface.path(d) };
7794
+ }
7795
+ function paintShape(shape, style, viewport, context) {
7796
+ const { surface } = context;
7797
+ surface.beginPath();
7798
+ shape.trace(surface);
7799
+ if (style.fill.kind !== "none" && style.fillOpacity > 0) {
7800
+ surface.save();
7801
+ if (style.fillOpacity < 1) surface.alpha(style.fillOpacity);
7802
+ if (setPaint(style.fill, "fill", shape, style, viewport, context)) surface.fill(style.fillRule);
7803
+ surface.restore();
7804
+ }
7805
+ if (style.stroke.kind !== "none" && style.strokeOpacity > 0 && style.strokeWidth > 0) {
7806
+ surface.save();
7807
+ if (style.strokeOpacity < 1) surface.alpha(style.strokeOpacity);
7808
+ if (setPaint(style.stroke, "stroke", shape, style, viewport, context)) {
7809
+ surface.lineWidth(style.strokeWidth);
7810
+ surface.lineCap(style.lineCap);
7811
+ surface.lineJoin(style.lineJoin);
7812
+ surface.miterLimit(style.miterLimit);
7813
+ if (style.dash.length > 0) surface.lineDash(style.dash, style.dashOffset);
7814
+ surface.stroke();
7815
+ }
7816
+ surface.restore();
7817
+ }
7818
+ }
7819
+ /**
7820
+ * Sets a fill or a stroke on the surface. False when there is nothing
7821
+ * to draw with: a gradient with no stops, or a reference to nothing and
7822
+ * no fallback.
7823
+ *
7824
+ * A gradient stroke is drawn in its last stop's colour, because the
7825
+ * surface strokes only in a colour.
7826
+ */
7827
+ function setPaint(paint, role, shape, style, viewport, context) {
7828
+ if (paint.kind === "none") return false;
7829
+ if (context.flood !== void 0) {
7830
+ if (role === "fill") context.surface.fillColor(context.flood);
7831
+ else context.surface.strokeColor(context.flood);
7832
+ return true;
7833
+ }
7834
+ if (paint.kind === "color") {
7835
+ if (role === "fill") context.surface.fillColor(paint.color);
7836
+ else context.surface.strokeColor(paint.color);
7837
+ return true;
7838
+ }
7839
+ const target = context.document.ids.get(paint.id);
7840
+ const gradient = target === void 0 ? void 0 : gradientOf(target, shape, style, viewport, context);
7841
+ if (gradient === void 0) return setPaint(paint.fallback, role, shape, style, viewport, context);
7842
+ if (gradient.stops.length === 0) return false;
7843
+ if (role === "stroke" || gradient.stops.length === 1) {
7844
+ const color = gradient.stops.at(-1).color;
7845
+ if (role === "fill") context.surface.fillColor(color);
7846
+ else context.surface.strokeColor(color);
7847
+ return true;
7848
+ }
7849
+ context.surface.fillGradient(gradient.paint, gradient.rect.x, gradient.rect.y, gradient.rect.width, gradient.rect.height);
7850
+ return true;
7851
+ }
7852
+ /**
7853
+ * An SVG gradient as Gesso's, and the rectangle to place it in.
7854
+ *
7855
+ * SVG gives a linear gradient as the two ends of its line and Gesso as
7856
+ * an angle across a rectangle, CSS's way, so the rectangle is chosen to
7857
+ * make the two agree: a square about the line's midpoint whose
7858
+ * gradient line, at that angle, is exactly as long as the SVG's. A
7859
+ * radial one is its centre and radius in pixels, placed at the origin.
7860
+ */
7861
+ function gradientOf(element, shape, style, viewport, context) {
7862
+ const name = localName(element.name);
7863
+ if (name !== "linearGradient" && name !== "radialGradient") return;
7864
+ const attributes = gradientAttributes(element, context.document);
7865
+ const stops = gradientStops(element, style, context);
7866
+ const userSpace = attributes.gradientUnits === "userSpaceOnUse";
7867
+ const box = userSpace ? viewport : boundsOf(shape);
7868
+ const matrix = attributes.gradientTransform === void 0 ? IDENTITY$1 : parseTransform$1(attributes.gradientTransform);
7869
+ /** A point in the gradient's units, in the drawing's. */
7870
+ const place = (x, y, fallbackX, fallbackY) => {
7871
+ const u = gradientLength(x ?? fallbackX, userSpace, viewport.width);
7872
+ const v = gradientLength(y ?? fallbackY, userSpace, viewport.height);
7873
+ const [tx, ty] = apply(matrix, u, v);
7874
+ return userSpace ? [tx, ty] : [box.x + tx * box.width, box.y + ty * box.height];
7875
+ };
7876
+ if (name === "linearGradient") {
7877
+ const [x1, y1] = place(attributes.x1, attributes.y1, "0%", "0%");
7878
+ const [x2, y2] = place(attributes.x2, attributes.y2, "100%", "0%");
7879
+ const dx = x2 - x1;
7880
+ const dy = y2 - y1;
7881
+ const span = Math.hypot(dx, dy);
7882
+ if (span === 0) return {
7883
+ paint: {
7884
+ kind: "linear",
7885
+ angle: 0,
7886
+ stops: stops.slice(-1)
7887
+ },
7888
+ stops: stops.slice(-1),
7889
+ rect: box
7890
+ };
7891
+ const angle = Math.atan2(dx, -dy);
7892
+ const side = span / (Math.abs(Math.sin(angle)) + Math.abs(Math.cos(angle)));
7893
+ return {
7894
+ paint: {
7895
+ kind: "linear",
7896
+ angle,
7897
+ stops
7898
+ },
7899
+ stops,
7900
+ rect: {
7901
+ x: (x1 + x2) / 2 - side / 2,
7902
+ y: (y1 + y2) / 2 - side / 2,
7903
+ width: side,
7904
+ height: side
7905
+ }
7906
+ };
7907
+ }
7908
+ const [cx, cy] = place(attributes.cx, attributes.cy, "50%", "50%");
7909
+ const radius = gradientLength(attributes.r ?? "50%", userSpace, Math.sqrt((viewport.width ** 2 + viewport.height ** 2) / 2)) * Math.sqrt(Math.abs(matrix[0] * matrix[3] - matrix[1] * matrix[2])) * (userSpace ? 1 : (box.width + box.height) / 2);
7910
+ if (radius <= 0) return {
7911
+ paint: {
7912
+ kind: "radial",
7913
+ stops: stops.slice(-1)
7914
+ },
7915
+ stops: stops.slice(-1),
7916
+ rect: box
7917
+ };
7918
+ return {
7919
+ paint: {
7920
+ kind: "radial",
7921
+ centerX: cx,
7922
+ centerY: cy,
7923
+ radius,
7924
+ stops
7925
+ },
7926
+ stops,
7927
+ rect: {
7928
+ x: 0,
7929
+ y: 0,
7930
+ width: 1,
7931
+ height: 1
7932
+ }
7933
+ };
7934
+ }
7935
+ /** A gradient's attributes, with the ones it inherits through `href` from the gradient it names. */
7936
+ function gradientAttributes(element, document) {
7937
+ const chain = [];
7938
+ for (let at = element; at !== void 0 && !chain.includes(at); at = referenced(at, document)) chain.push(at);
7939
+ return Object.assign({}, ...chain.reverse().map((link) => link.attributes));
7940
+ }
7941
+ /** Its stops, or those of the gradient it names when it has none. */
7942
+ function gradientStops(element, style, context) {
7943
+ const seen = [];
7944
+ let at = element;
7945
+ while (at !== void 0 && !seen.includes(at)) {
7946
+ seen.push(at);
7947
+ const stops = at.children.filter((child) => localName(child.name) === "stop");
7948
+ if (stops.length > 0) {
7949
+ let last = 0;
7950
+ return stops.map((stop) => {
7951
+ const declared = declarationsOf(stop, context.document);
7952
+ const raw = declared.offset ?? "0";
7953
+ const offset = Math.max(last, Math.min(1, raw.trim().endsWith("%") ? parseFloat(raw) / 100 : parseFloat(raw) || 0));
7954
+ last = offset;
7955
+ const color = colorValue(declared["stop-color"] ?? "black", style, context) ?? BLACK_COLOR;
7956
+ return {
7957
+ offset: percent(offset * 100),
7958
+ color: withOpacity(color, unit(declared["stop-opacity"]))
7959
+ };
7960
+ });
7961
+ }
7962
+ at = referenced(at, context.document);
7963
+ }
7964
+ return [];
7965
+ }
7966
+ const BLACK_COLOR = {
7967
+ r: 0,
7968
+ g: 0,
7969
+ b: 0,
7970
+ a: 1
7971
+ };
7972
+ function withOpacity(color, opacity) {
7973
+ if (opacity >= 1 || typeof color === "string") return color;
7974
+ return {
7975
+ ...color,
7976
+ a: color.a * opacity
7977
+ };
7978
+ }
7979
+ /** A gradient coordinate: a fraction of the box, or pixels in user space. */
7980
+ function gradientLength(value, userSpace, extent) {
7981
+ const trimmed = value.trim();
7982
+ if (trimmed.endsWith("%")) {
7983
+ const fraction = parseFloat(trimmed) / 100;
7984
+ return userSpace ? fraction * extent : fraction;
7985
+ }
7986
+ return parseFloat(trimmed) || 0;
7987
+ }
7988
+ /** The box a shape's outline and control points lie in, for a gradient sized to it. */
7989
+ function boundsOf(shape) {
7990
+ const bounds = new BoundsSurface();
7991
+ shape.trace(bounds);
7992
+ return bounds.bounds();
7993
+ }
7994
+ /** A surface that only notes where a path goes. */
7995
+ var BoundsSurface = class {
7996
+ minX = Infinity;
7997
+ minY = Infinity;
7998
+ maxX = -Infinity;
7999
+ maxY = -Infinity;
8000
+ note(x, y) {
8001
+ this.minX = Math.min(this.minX, x);
8002
+ this.minY = Math.min(this.minY, y);
8003
+ this.maxX = Math.max(this.maxX, x);
8004
+ this.maxY = Math.max(this.maxY, y);
8005
+ }
8006
+ bounds() {
8007
+ if (this.minX > this.maxX) return {
8008
+ x: 0,
8009
+ y: 0,
8010
+ width: 0,
8011
+ height: 0
8012
+ };
8013
+ return {
8014
+ x: this.minX,
8015
+ y: this.minY,
8016
+ width: this.maxX - this.minX,
8017
+ height: this.maxY - this.minY
8018
+ };
8019
+ }
8020
+ moveTo(x, y) {
8021
+ this.note(x, y);
8022
+ }
8023
+ lineTo(x, y) {
8024
+ this.note(x, y);
8025
+ }
8026
+ quadraticCurveTo(cx, cy, x, y) {
8027
+ this.note(cx, cy);
8028
+ this.note(x, y);
8029
+ }
8030
+ bezierCurveTo(c1x, c1y, c2x, c2y, x, y) {
8031
+ this.note(c1x, c1y);
8032
+ this.note(c2x, c2y);
8033
+ this.note(x, y);
8034
+ }
8035
+ arc(x, y, radius) {
8036
+ this.note(x - radius, y - radius);
8037
+ this.note(x + radius, y + radius);
8038
+ }
8039
+ rect(x, y, width, height) {
8040
+ this.note(x, y);
8041
+ this.note(x + width, y + height);
8042
+ }
8043
+ roundRect(x, y, width, height) {
8044
+ this.rect(x, y, width, height);
8045
+ }
8046
+ closePath() {}
8047
+ path(d) {
8048
+ tracePathData(this, d);
8049
+ }
8050
+ };
8051
+ /** Intersects the clip with a `<clipPath>`'s shapes, in the drawing's own coordinates. */
8052
+ function clipTo(value, viewport, context) {
8053
+ const id = /url\(\s*['"]?#([^'")]+)['"]?\s*\)/.exec(value)?.[1];
8054
+ const clip = id === void 0 ? void 0 : context.document.ids.get(id);
8055
+ if (clip === void 0 || localName(clip.name) !== "clipPath") return;
8056
+ const { surface } = context;
8057
+ surface.beginPath();
8058
+ let rule = "nonzero";
8059
+ const add = (element) => {
8060
+ const name = localName(element.name);
8061
+ const declared = declarationsOf(element, context.document);
8062
+ if (name === "use") {
8063
+ const target = referenced(element, context.document);
8064
+ if (target !== void 0 && localName(target.name) !== "use") add(target);
8065
+ return;
8066
+ }
8067
+ if (name === "g") {
8068
+ element.children.forEach(add);
8069
+ return;
8070
+ }
8071
+ const shape = shapeOf(element, name, viewport);
8072
+ if (shape === null) return;
8073
+ if (declared["clip-rule"] === "evenodd") rule = "evenodd";
8074
+ if (declared.transform !== void 0) {
8075
+ const matrix = parseTransform$1(declared.transform);
8076
+ shape.trace(new TransformedSurface(surface, matrix));
8077
+ } else shape.trace(surface);
8078
+ };
8079
+ clip.children.forEach(add);
8080
+ surface.clip(rule);
8081
+ }
8082
+ /** Forwards path calls with every point through a matrix. */
8083
+ var TransformedSurface = class {
8084
+ target;
8085
+ matrix;
8086
+ constructor(target, matrix) {
8087
+ this.target = target;
8088
+ this.matrix = matrix;
8089
+ }
8090
+ at(x, y) {
8091
+ return apply(this.matrix, x, y);
8092
+ }
8093
+ moveTo(x, y) {
8094
+ this.target.moveTo(...this.at(x, y));
8095
+ }
8096
+ lineTo(x, y) {
8097
+ this.target.lineTo(...this.at(x, y));
8098
+ }
8099
+ quadraticCurveTo(cx, cy, x, y) {
8100
+ this.target.quadraticCurveTo(...this.at(cx, cy), ...this.at(x, y));
8101
+ }
8102
+ bezierCurveTo(c1x, c1y, c2x, c2y, x, y) {
8103
+ this.target.bezierCurveTo(...this.at(c1x, c1y), ...this.at(c2x, c2y), ...this.at(x, y));
8104
+ }
8105
+ rect(x, y, width, height) {
8106
+ this.moveTo(x, y);
8107
+ this.lineTo(x + width, y);
8108
+ this.lineTo(x + width, y + height);
8109
+ this.lineTo(x, y + height);
8110
+ this.closePath();
8111
+ }
8112
+ roundRect(x, y, width, height) {
8113
+ this.rect(x, y, width, height);
8114
+ }
8115
+ closePath() {
8116
+ this.target.closePath();
8117
+ }
8118
+ path(d) {
8119
+ tracePathData(this, d);
8120
+ }
8121
+ };
8122
+ /**
8123
+ * What an element declares, in the cascade's order: its presentation
8124
+ * attributes, then the `<style>` rules that match it by specificity,
8125
+ * then its own `style` attribute.
8126
+ */
8127
+ function declarationsOf(element, document) {
8128
+ const declared = { ...element.attributes };
8129
+ if (document.rules.length > 0) {
8130
+ const matching = document.rules.map((rule, order) => ({
8131
+ rule,
8132
+ order
8133
+ })).filter(({ rule }) => selectorMatches(rule.selector, element)).sort((a, b) => a.rule.specificity - b.rule.specificity || a.order - b.order);
8134
+ for (const { rule } of matching) Object.assign(declared, rule.declarations);
8135
+ }
8136
+ if (element.attributes.style !== void 0) Object.assign(declared, parseDeclarations(element.attributes.style));
8137
+ return declared;
8138
+ }
8139
+ /** The inherited style at an element, from its parent's and what it declares. */
8140
+ function cascade(parent, declared, context) {
8141
+ const color = declared.color === void 0 || declared.color === "inherit" ? parent.color : colorValue(declared.color, parent, context) ?? parent.color;
8142
+ const here = {
8143
+ ...parent,
8144
+ color
8145
+ };
8146
+ const pick = (key, property, read) => {
8147
+ const value = declared[property];
8148
+ return value === void 0 || value === "inherit" ? here[key] : read(value) ?? here[key];
8149
+ };
8150
+ const number = (value) => {
8151
+ const parsed = parseFloat(value);
8152
+ return Number.isFinite(parsed) ? parsed : void 0;
8153
+ };
8154
+ return {
8155
+ ...here,
8156
+ fill: pick("fill", "fill", (value) => paintValue(value, here, context)),
8157
+ fillOpacity: pick("fillOpacity", "fill-opacity", (value) => unit(value)),
8158
+ fillRule: pick("fillRule", "fill-rule", (value) => value === "evenodd" ? "evenodd" : "nonzero"),
8159
+ stroke: pick("stroke", "stroke", (value) => paintValue(value, here, context)),
8160
+ strokeOpacity: pick("strokeOpacity", "stroke-opacity", (value) => unit(value)),
8161
+ strokeWidth: pick("strokeWidth", "stroke-width", number),
8162
+ lineCap: pick("lineCap", "stroke-linecap", (value) => value === "round" || value === "square" || value === "butt" ? value : void 0),
8163
+ lineJoin: pick("lineJoin", "stroke-linejoin", (value) => value === "round" || value === "bevel" || value === "miter" ? value : value === "arcs" || value === "miter-clip" ? "miter" : void 0),
8164
+ miterLimit: pick("miterLimit", "stroke-miterlimit", number),
8165
+ dash: pick("dash", "stroke-dasharray", (value) => {
8166
+ if (value === "none") return [];
8167
+ const segments = numbersIn(value);
8168
+ if (segments.some((segment) => segment < 0) || segments.every((segment) => segment === 0)) return [];
8169
+ return segments.length % 2 === 1 ? [...segments, ...segments] : segments;
8170
+ }),
8171
+ dashOffset: pick("dashOffset", "stroke-dashoffset", number),
8172
+ clipRule: pick("clipRule", "clip-rule", (value) => value === "evenodd" ? "evenodd" : "nonzero"),
8173
+ visible: pick("visible", "visibility", (value) => value === "visible" || value === "inherit" ? true : value === "hidden" || value === "collapse" ? false : void 0)
8174
+ };
8175
+ }
8176
+ function paintValue(value, style, context) {
8177
+ const trimmed = value.trim();
8178
+ if (trimmed === "none" || trimmed === "transparent") return NONE;
8179
+ const reference = /^url\(\s*['"]?#([^'")]+)['"]?\s*\)\s*(.*)$/.exec(trimmed);
8180
+ if (reference !== null) {
8181
+ const fallback = reference[2] === "" || reference[2] === void 0 ? NONE : paintValue(reference[2], style, context) ?? NONE;
8182
+ return {
8183
+ kind: "ref",
8184
+ id: reference[1],
8185
+ fallback
8186
+ };
8187
+ }
8188
+ const color = colorValue(trimmed, style, context);
8189
+ return color === void 0 ? void 0 : {
8190
+ kind: "color",
8191
+ color
8192
+ };
8193
+ }
8194
+ /** A colour, with `currentColor` resolved to whatever it is here. */
8195
+ function colorValue(value, style, context) {
8196
+ const trimmed = value.trim();
8197
+ if (trimmed.toLowerCase() === "currentcolor") return style.color ?? context.currentColor;
8198
+ return parseColor(trimmed);
8199
+ }
8200
+ /** An opacity: a number or a percentage, clamped to 0..1, and 1 when absent. */
8201
+ function unit(value) {
8202
+ if (value === void 0) return 1;
8203
+ const trimmed = value.trim();
8204
+ const parsed = trimmed.endsWith("%") ? parseFloat(trimmed) / 100 : parseFloat(trimmed);
8205
+ return Number.isFinite(parsed) ? Math.max(0, Math.min(1, parsed)) : 1;
8206
+ }
8207
+ /** A length in the drawing's units, a percentage being of `extent`. */
8208
+ function length(value, extent) {
8209
+ if (value === void 0) return 0;
8210
+ const trimmed = value.trim();
8211
+ if (trimmed.endsWith("%")) return parseFloat(trimmed) / 100 * extent;
8212
+ const parsed = parseFloat(trimmed);
8213
+ return Number.isFinite(parsed) ? parsed : 0;
8214
+ }
8215
+ const IDENTITY$1 = [
8216
+ 1,
8217
+ 0,
8218
+ 0,
8219
+ 1,
8220
+ 0,
8221
+ 0
8222
+ ];
8223
+ function multiply(m, n) {
8224
+ return [
8225
+ m[0] * n[0] + m[2] * n[1],
8226
+ m[1] * n[0] + m[3] * n[1],
8227
+ m[0] * n[2] + m[2] * n[3],
8228
+ m[1] * n[2] + m[3] * n[3],
8229
+ m[0] * n[4] + m[2] * n[5] + m[4],
8230
+ m[1] * n[4] + m[3] * n[5] + m[5]
8231
+ ];
8232
+ }
8233
+ function apply(m, x, y) {
8234
+ return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
8235
+ }
8236
+ /** A `transform` list as one matrix, its functions applied left to right as SVG says. */
8237
+ function parseTransform$1(value) {
8238
+ let matrix = IDENTITY$1;
8239
+ for (const match of value.matchAll(/(matrix|translate|scale|rotate|skewX|skewY)\s*\(([^)]*)\)/g)) {
8240
+ const args = numbersIn(match[2]);
8241
+ let step = IDENTITY$1;
8242
+ switch (match[1]) {
8243
+ case "matrix":
8244
+ if (args.length >= 6) step = [
8245
+ args[0],
8246
+ args[1],
8247
+ args[2],
8248
+ args[3],
8249
+ args[4],
8250
+ args[5]
8251
+ ];
8252
+ break;
8253
+ case "translate":
8254
+ step = [
8255
+ 1,
8256
+ 0,
8257
+ 0,
8258
+ 1,
8259
+ args[0] ?? 0,
8260
+ args[1] ?? 0
8261
+ ];
8262
+ break;
8263
+ case "scale":
8264
+ step = [
8265
+ args[0] ?? 1,
8266
+ 0,
8267
+ 0,
8268
+ args[1] ?? args[0] ?? 1,
8269
+ 0,
8270
+ 0
8271
+ ];
8272
+ break;
8273
+ case "rotate": {
8274
+ const angle = (args[0] ?? 0) * Math.PI / 180;
8275
+ const cos = Math.cos(angle);
8276
+ const sin = Math.sin(angle);
8277
+ const [cx, cy] = [args[1] ?? 0, args[2] ?? 0];
8278
+ step = multiply(multiply([
8279
+ 1,
8280
+ 0,
8281
+ 0,
8282
+ 1,
8283
+ cx,
8284
+ cy
8285
+ ], [
8286
+ cos,
8287
+ sin,
8288
+ -sin,
8289
+ cos,
8290
+ 0,
8291
+ 0
8292
+ ]), [
8293
+ 1,
8294
+ 0,
8295
+ 0,
8296
+ 1,
8297
+ -cx,
8298
+ -cy
8299
+ ]);
8300
+ break;
8301
+ }
8302
+ case "skewX":
8303
+ step = [
8304
+ 1,
8305
+ 0,
8306
+ Math.tan((args[0] ?? 0) * Math.PI / 180),
8307
+ 1,
8308
+ 0,
8309
+ 0
8310
+ ];
8311
+ break;
8312
+ case "skewY": step = [
8313
+ 1,
8314
+ Math.tan((args[0] ?? 0) * Math.PI / 180),
8315
+ 0,
8316
+ 1,
8317
+ 0,
8318
+ 0
8319
+ ];
8320
+ }
8321
+ matrix = multiply(matrix, step);
8322
+ }
8323
+ return matrix;
8324
+ }
8325
+ function applyTransform$1(surface, value) {
8326
+ const [a, b, c, d, e, f] = parseTransform$1(value);
8327
+ surface.transform(a, b, c, d, e, f);
8328
+ }
8329
+ //#endregion
8330
+ //#region src/rendering/svg/SvgResolver.ts
8331
+ /**
8332
+ * Whether a source is an SVG file, from its name or its data URL's
8333
+ * type: what `Image` asks before it decodes, since the platform
8334
+ * decoder refuses SVG and this one draws it.
8335
+ */
8336
+ function isSvgSource(source) {
8337
+ if (/^data:image\/svg\+xml[;,]/i.test(source)) return true;
8338
+ const path = source.split(/[?#]/)[0] ?? "";
8339
+ return /\.svgz?$/i.test(path);
8340
+ }
8341
+ const DEFAULT_CAPACITY$3 = 64;
8342
+ /**
8343
+ * Fetch, read, keep.
8344
+ *
8345
+ * Kept by recency and never closed, unlike a bitmap: a read file holds
8346
+ * no decoded pixels, so there is nothing to release promptly, and a
8347
+ * file a screen shows a hundred times (an icon in every row) is read
8348
+ * once. A failed fetch is not kept, so the next node tries again.
8349
+ */
8350
+ var DefaultSvgResolver = class {
8351
+ entries = /* @__PURE__ */ new Map();
8352
+ settled = /* @__PURE__ */ new Map();
8353
+ capacity;
8354
+ fetchText;
8355
+ constructor(options = {}) {
8356
+ this.capacity = options.capacity ?? DEFAULT_CAPACITY$3;
8357
+ this.fetchText = options.fetch ?? defaultFetch$2;
8358
+ }
8359
+ resolve(source) {
8360
+ const existing = this.entries.get(source);
8361
+ if (existing !== void 0) {
8362
+ this.entries.delete(source);
8363
+ this.entries.set(source, existing);
8364
+ return existing;
8365
+ }
8366
+ const entry = this.fetchText(source).then((text) => {
8367
+ const document = parseSvg(text);
8368
+ if (this.entries.get(source) === entry) this.settled.set(source, document);
8369
+ return document;
8370
+ }).catch((error) => {
8371
+ if (this.entries.get(source) === entry) this.entries.delete(source);
8372
+ throw error;
8373
+ });
8374
+ entry.catch(() => {});
8375
+ this.entries.set(source, entry);
8376
+ while (this.entries.size > this.capacity) {
8377
+ const oldest = this.entries.keys().next().value;
8378
+ this.entries.delete(oldest);
8379
+ this.settled.delete(oldest);
8380
+ }
8381
+ return entry;
8382
+ }
8383
+ peek(source) {
8384
+ return this.settled.get(source) ?? null;
8385
+ }
8386
+ };
8387
+ async function defaultFetch$2(source) {
8388
+ const response = await fetch(source);
8389
+ if (!response.ok) throw new Error(`Fetching the SVG '${source}' failed with ${response.status} ${response.statusText}.`);
8390
+ return response.text();
8391
+ }
8392
+ //#endregion
6864
8393
  //#region src/modifiers/media.ts
6865
8394
  /**
6866
8395
  * A value or an Observable of one, as one Observable. The media
@@ -6870,6 +8399,29 @@ function numberOr(value, fallback) {
6870
8399
  function follow(value) {
6871
8400
  return isObservable$1(value) ? value : of(value);
6872
8401
  }
8402
+ /** Pictures this thread's media modifiers are fetching or decoding now. */
8403
+ let inFlight = 0;
8404
+ /**
8405
+ * How many pictures the media modifiers in this thread are still
8406
+ * fetching or decoding: an `Image`'s bitmap or its SVG.
8407
+ *
8408
+ * A tree waiting on a picture asks for no frames while it waits, so a
8409
+ * caller that judges a tree finished by its quiet (`renderFrame`, which
8410
+ * draws a mock once, off screen) would draw it without the picture.
8411
+ * This is the other half of finished: nothing still on its way.
8412
+ */
8413
+ function mediaLoadsInFlight() {
8414
+ return inFlight;
8415
+ }
8416
+ /** Counts a load from its start until it settles, either way. */
8417
+ function counted(loading) {
8418
+ inFlight++;
8419
+ const done = () => {
8420
+ inFlight--;
8421
+ };
8422
+ loading.then(done, done);
8423
+ return loading;
8424
+ }
6873
8425
  /**
6874
8426
  * Resolves a source to a bitmap and writes it onto the node's `image`.
6875
8427
  *
@@ -6905,6 +8457,8 @@ function load$1(host, args, sources) {
6905
8457
  let held = null;
6906
8458
  args.onState?.("loading");
6907
8459
  host.clear("image");
8460
+ host.clear("paint");
8461
+ host.clear("src");
6908
8462
  const attempt = (index, lastError) => {
6909
8463
  if (!live) return;
6910
8464
  const source = sources[index];
@@ -6912,16 +8466,22 @@ function load$1(host, args, sources) {
6912
8466
  args.onState?.("failed", lastError ?? /* @__PURE__ */ new Error("No image source was given."));
6913
8467
  return;
6914
8468
  }
8469
+ if (args.vectors !== void 0 && isSvgSource(source)) {
8470
+ drawVector(host, args, args.vectors, source, () => live, (error) => attempt(index + 1, error));
8471
+ return;
8472
+ }
6915
8473
  held = source;
6916
8474
  const ready = args.resolver.peek?.(source) ?? null;
6917
8475
  if (ready !== null) {
6918
8476
  host.set("image", ready);
8477
+ host.set("src", source);
6919
8478
  args.onState?.("loaded");
6920
8479
  return;
6921
8480
  }
6922
- args.resolver.resolve(source).then((bitmap) => {
8481
+ counted(args.resolver.resolve(source)).then((bitmap) => {
6923
8482
  if (!live) return;
6924
8483
  host.set("image", bitmap);
8484
+ host.set("src", source);
6925
8485
  args.onState?.("loaded");
6926
8486
  }).catch((error) => {
6927
8487
  if (held === source) held = null;
@@ -6937,6 +8497,36 @@ function load$1(host, args, sources) {
6937
8497
  }
6938
8498
  };
6939
8499
  }
8500
+ /** SVG sources already warned about, so a file shown in every row says what it lacks once. */
8501
+ const warnedSvg = /* @__PURE__ */ new Set();
8502
+ /**
8503
+ * An SVG source, read and set as the node's `paint`: vectors, drawn at
8504
+ * whatever size the node is laid out at, with `currentColor` the
8505
+ * node's inherited text colour. Nothing is held, because a read file
8506
+ * holds no pixels to release.
8507
+ */
8508
+ function drawVector(host, args, vectors, source, live, fail) {
8509
+ const show = (document) => {
8510
+ const fit = host.node.getProperty("objectFit") ?? "contain";
8511
+ host.set("paint", svgPaint(document, { fit }));
8512
+ host.set("src", source);
8513
+ if (document.unsupported.length > 0 && !warnedSvg.has(source)) {
8514
+ warnedSvg.add(source);
8515
+ console.warn(`[gesso] ${source.slice(0, 80)} uses ${document.unsupported.join(", ")}, which Gesso does not draw yet; the rest of it is drawn.`);
8516
+ }
8517
+ args.onState?.("loaded");
8518
+ };
8519
+ const ready = vectors.peek?.(source) ?? null;
8520
+ if (ready !== null) {
8521
+ show(ready);
8522
+ return;
8523
+ }
8524
+ counted(vectors.resolve(source)).then((document) => {
8525
+ if (live()) show(document);
8526
+ }, (error) => {
8527
+ if (live()) fail(error);
8528
+ });
8529
+ }
6940
8530
  function sameSources(a, b) {
6941
8531
  if (typeof a === "string" || typeof b === "string") return a === b;
6942
8532
  return a.length === b.length && a.every((url, index) => url === b[index]);
@@ -7243,14 +8833,14 @@ var MotionLayer = class {
7243
8833
  }
7244
8834
  drive(cell, to, timing) {
7245
8835
  if (timing.spring !== void 0) return this.host.spring(cell, to, {
7246
- spring: springSpec(timing.spring),
8836
+ spring: springIn(motionOf(this.host.node), timing.spring),
7247
8837
  delay: timing.delay,
7248
8838
  reducedMotion: timing.reducedMotion,
7249
8839
  restDelta: .002
7250
8840
  });
7251
8841
  return this.host.animate(cell, to, {
7252
- duration: resolveDuration(timing.duration),
7253
- easing: resolveEasing(timing.easing),
8842
+ duration: durationIn(motionOf(this.host.node), timing.duration),
8843
+ easing: easingIn(motionOf(this.host.node), timing.easing),
7254
8844
  delay: timing.delay,
7255
8845
  reducedMotion: timing.reducedMotion
7256
8846
  });
@@ -7266,17 +8856,6 @@ var MotionLayer = class {
7266
8856
  function channelEpsilon(channel) {
7267
8857
  return channel === "x" || channel === "y" ? .01 : 1e-4;
7268
8858
  }
7269
- function springSpec(spring) {
7270
- return typeof spring === "string" ? defaultMotion.springs[spring] : spring;
7271
- }
7272
- function resolveDuration(duration) {
7273
- if (duration === void 0) return defaultMotion.durations.normal;
7274
- return typeof duration === "number" ? duration : defaultMotion.durations[duration];
7275
- }
7276
- function resolveEasing(easing) {
7277
- if (easing === void 0) return easings.standard;
7278
- return typeof easing === "function" ? easing : easings[easing] ?? easings.standard;
7279
- }
7280
8859
  /**
7281
8860
  * Moves an element to a visual state, and optionally in from another.
7282
8861
  *
@@ -7319,7 +8898,7 @@ var MotionController = class {
7319
8898
  attach() {
7320
8899
  this.layer.measure(this.host.flowBox());
7321
8900
  if (this.args.initial !== void 0) this.layer.snapTo(resolveMotionState(this.args.initial));
7322
- this.host.onLayout(() => this.layer.measure(this.host.flowBox()));
8901
+ this.host.onFlowLayout(() => this.layer.measure(this.host.flowBox()));
7323
8902
  this.aim();
7324
8903
  }
7325
8904
  update(args) {
@@ -7615,13 +9194,13 @@ var SharedElementController = class {
7615
9194
  const cell = geometry.cell(field);
7616
9195
  const to = targets[field];
7617
9196
  (timing.spring !== void 0 ? this.host.spring(cell, to, {
7618
- spring: springSpec(timing.spring),
9197
+ spring: springIn(motionOf(this.host.node), timing.spring),
7619
9198
  delay: timing.delay,
7620
9199
  reducedMotion: timing.reducedMotion,
7621
9200
  restDelta: .5
7622
9201
  }) : this.host.animate(cell, to, {
7623
- duration: resolveDuration(timing.duration),
7624
- easing: resolveEasing(timing.easing),
9202
+ duration: durationIn(motionOf(this.host.node), timing.duration),
9203
+ easing: easingIn(motionOf(this.host.node), timing.easing),
7625
9204
  delay: timing.delay,
7626
9205
  reducedMotion: timing.reducedMotion
7627
9206
  })).subscribe({ complete: done });
@@ -8143,7 +9722,7 @@ const scrollPosition = defineModifier({
8143
9722
  if (settling === 1) args.onSettled?.(offset);
8144
9723
  } });
8145
9724
  };
8146
- host.onLayout(() => {
9725
+ host.onFlowLayout(() => {
8147
9726
  const offset = host.scrollOffset();
8148
9727
  if (offset === null || last !== null && last.x === offset.x && last.y === offset.y) return;
8149
9728
  last = offset;
@@ -8193,7 +9772,7 @@ const publishInset = defineModifier({
8193
9772
  if (write === null) write = source.publish(insets);
8194
9773
  else write(insets);
8195
9774
  };
8196
- host.onLayout(publish);
9775
+ host.onFlowLayout(publish);
8197
9776
  publish();
8198
9777
  host.own(() => {
8199
9778
  write?.();
@@ -8630,11 +10209,9 @@ const DEFAULT_CAPACITY$2 = 32;
8630
10209
  *
8631
10210
  * **What it cannot decode.** `createImageBitmap` takes a `Blob`, and
8632
10211
  * Chrome refuses an SVG one — "The source image could not be decoded".
8633
- * So an `Image` pointed at an `.svg` fails on a browser where the same
8634
- * file in an `<img>` would work, and a vector glyph belongs in `Icon`,
8635
- * which draws a path rather than decoding a document. Found by
8636
- * pointing the playground's Media card at an SVG data URL and getting
8637
- * a placeholder.
10212
+ * Found by pointing the playground's Media card at an SVG data URL and
10213
+ * getting a placeholder. So an `Image` never sends one here: an SVG
10214
+ * source is read by `SvgResolver` and drawn as vectors (`svg/`).
8638
10215
  *
8639
10216
  * **Eviction.** Reference counting first, LRU second. A bitmap
8640
10217
  * something on screen is holding is never evicted, however long the
@@ -9282,6 +10859,19 @@ fn fs(in: VertexOutput) -> @location(0) vec4f {
9282
10859
  let color = textureSample(tTexture, tSampler, in.uv);
9283
10860
  return vec4f(color.rgb, color.a * in.opacity * alpha);
9284
10861
  }
10862
+
10863
+ // An opacity group's layer, blended onto what holds it. The layer was
10864
+ // drawn into with the same blend as the frame, so it holds premultiplied
10865
+ // colour, and is scaled whole — colour and alpha alike — by the group's
10866
+ // opacity and the clip's coverage.
10867
+ @fragment
10868
+ fn fsLayer(in: VertexOutput) -> @location(0) vec4f {
10869
+ let alpha = clipCoverage(in.position.xy, uView.z, in.clipIndex);
10870
+ if (alpha <= 0.0) {
10871
+ discard;
10872
+ }
10873
+ return textureSample(tTexture, tSampler, in.uv) * (in.opacity * alpha);
10874
+ }
9285
10875
  `;
9286
10876
  //#endregion
9287
10877
  //#region src/rendering/webgpu/WebGPUGlyphAtlas.ts
@@ -10109,6 +11699,13 @@ let CommandKind = /* @__PURE__ */ function(CommandKind) {
10109
11699
  CommandKind[CommandKind["Glyphs"] = 1] = "Glyphs";
10110
11700
  /** One textured instance drawn with an image. */
10111
11701
  CommandKind[CommandKind["Image"] = 2] = "Image";
11702
+ /**
11703
+ * Everything until the matching `EndLayer` is drawn into a cleared
11704
+ * layer of its own, the start of an opacity group (`GroupOpacity.ts`).
11705
+ */
11706
+ CommandKind[CommandKind["BeginLayer"] = 3] = "BeginLayer";
11707
+ /** The layer is done, and is blended onto what holds it at the group's opacity. */
11708
+ CommandKind[CommandKind["EndLayer"] = 4] = "EndLayer";
10112
11709
  return CommandKind;
10113
11710
  }({});
10114
11711
  const IDENTITY = [
@@ -10223,8 +11820,21 @@ function buildRenderList(root, layout, measurer, logicalWidth, logicalHeight, dp
10223
11820
  height: logicalHeight
10224
11821
  }
10225
11822
  };
10226
- /** The nodes asking for `lift`, met on the walk and drawn after it. */
11823
+ /**
11824
+ * The nodes asking for `lift`, met on the walk and drawn after it,
11825
+ * with their opacity as a product of everything above them: inside a
11826
+ * layer `state.opacity` counts from the layer, and a lifted node is
11827
+ * drawn outside the layers it was met in.
11828
+ */
10227
11829
  const liftedPass = [];
11830
+ /**
11831
+ * The opacity each open layer will be blended at, multiplied out from
11832
+ * the frame: the last entry is what `state.opacity` is relative to.
11833
+ * One entry, 1, while no layer is open.
11834
+ */
11835
+ const layerOpacities = [1];
11836
+ const physicalWidth = toPhysicalPixels(logicalWidth, dpr);
11837
+ const physicalHeight = toPhysicalPixels(logicalHeight, dpr);
10228
11838
  function closePrimitives() {
10229
11839
  const end = instanceData.length / 24;
10230
11840
  if (end > openStart) commands.push({
@@ -10344,7 +11954,7 @@ function buildRenderList(root, layout, measurer, logicalWidth, logicalHeight, dp
10344
11954
  if (rec.lifted && !lifted) {
10345
11955
  liftedPass.push({
10346
11956
  node,
10347
- opacity: state.opacity,
11957
+ opacity: state.opacity * layerOpacities[layerOpacities.length - 1],
10348
11958
  ctm: state.ctm
10349
11959
  });
10350
11960
  return;
@@ -10352,9 +11962,33 @@ function buildRenderList(root, layout, measurer, logicalWidth, logicalHeight, dp
10352
11962
  const sticky = rec.stickyOffsetX !== 0 || rec.stickyOffsetY !== 0;
10353
11963
  if (state.cull !== null && !intersectsCull(state.cull, rec.extentMinX + rec.stickyOffsetX, rec.extentMinY + rec.stickyOffsetY, rec.extentMaxX - rec.extentMinX, rec.extentMaxY - rec.extentMinY)) return;
10354
11964
  const paint = resolvePaintState(node, paintScratch);
10355
- const effectiveOpacity = state.opacity * paint.opacity;
11965
+ const layered = paint.opacity < 1 && opacityNeedsLayer(node, rec, paint, layout);
11966
+ const opacity = paint.opacity;
11967
+ const outerOpacity = state.opacity;
11968
+ const outerClip = state.clip;
11969
+ const outerRounded = state.rounded;
11970
+ const effectiveOpacity = layered ? 1 : state.opacity * paint.opacity;
10356
11971
  const baseCtm = sticky ? translateTransform(state.ctm, rec.stickyOffsetX, rec.stickyOffsetY) : state.ctm;
10357
11972
  const nodeCtm = paint.hasTransform ? multiplyTransform(baseCtm, buildOwnTransform(rec, paint.transform)) : baseCtm;
11973
+ const region = layered ? layerRegion(node, rec, {
11974
+ a: nodeCtm[0] * dpr,
11975
+ b: nodeCtm[1] * dpr,
11976
+ c: nodeCtm[2] * dpr,
11977
+ d: nodeCtm[3] * dpr,
11978
+ e: nodeCtm[4] * dpr,
11979
+ f: nodeCtm[5] * dpr
11980
+ }, layout, physicalWidth, physicalHeight, dpr) : null;
11981
+ if (layered) {
11982
+ closePrimitives();
11983
+ commands.push({
11984
+ kind: 3,
11985
+ scissor: null
11986
+ });
11987
+ layerOpacities.push(layerOpacities[layerOpacities.length - 1] * outerOpacity * opacity);
11988
+ state.opacity = 1;
11989
+ state.clip = null;
11990
+ state.rounded = -1;
11991
+ }
10358
11992
  const ownScissor = state.clip;
10359
11993
  const ownRounded = state.rounded;
10360
11994
  let nextClip = state.clip;
@@ -10624,6 +12258,24 @@ function buildRenderList(root, layout, measurer, logicalWidth, logicalHeight, dp
10624
12258
  beginPrimitives(ownScissor);
10625
12259
  pushScrollbars(instanceData, rec, effectiveOpacity, nodeCtm, ownRounded, now);
10626
12260
  }
12261
+ if (region !== null) {
12262
+ closePrimitives();
12263
+ layerOpacities.pop();
12264
+ state.opacity = outerOpacity;
12265
+ state.clip = outerClip;
12266
+ state.rounded = outerRounded;
12267
+ const instance = region.width > 0 && region.height > 0 ? pushTextured(texturedData, region.x / dpr, region.y / dpr, region.width / dpr, region.height / dpr, outerOpacity * opacity, IDENTITY, outerRounded, {
12268
+ u: region.x / physicalWidth,
12269
+ v: region.y / physicalHeight,
12270
+ uw: region.width / physicalWidth,
12271
+ vh: region.height / physicalHeight
12272
+ }) : null;
12273
+ commands.push({
12274
+ kind: 4,
12275
+ instance,
12276
+ scissor: outerClip
12277
+ });
12278
+ }
10627
12279
  }
10628
12280
  /**
10629
12281
  * The top layer, drawn once the tree is done: no clip, no scissor,
@@ -10643,7 +12295,7 @@ function buildRenderList(root, layout, measurer, logicalWidth, logicalHeight, dp
10643
12295
  };
10644
12296
  for (let i = from; i < liftedPass.length; i++) {
10645
12297
  const entry = liftedPass[i];
10646
- state.opacity = entry.opacity;
12298
+ state.opacity = entry.opacity / layerOpacities[layerOpacities.length - 1];
10647
12299
  state.ctm = entry.ctm;
10648
12300
  state.clip = clip;
10649
12301
  state.rounded = rounded;
@@ -10809,7 +12461,7 @@ function pushInstance(out, x, y, width, height, color, radius, opacity, borderWi
10809
12461
  */
10810
12462
  function pushShadows(out, rec, paint, opacity, transform, clip, inset) {
10811
12463
  const shadows = paint.boxShadows;
10812
- const radius = borderRadiusIsZero(paint.borderRadius) ? 0 : uniformBorderRadius(paint.borderRadius);
12464
+ const radius = borderRadiusIsZero(paint.borderRadius) ? 0 : fittedShadowRadius(uniformBorderRadius(paint.borderRadius), rec.width, rec.height);
10813
12465
  for (let i = shadows.length - 1; i >= 0; i--) {
10814
12466
  const shadow = shadows[i];
10815
12467
  if (shadow.inset === true !== inset || shadow.color.a <= 0) continue;
@@ -11125,6 +12777,19 @@ const STRAIGHT_ALPHA_BLEND = {
11125
12777
  operation: "add"
11126
12778
  }
11127
12779
  };
12780
+ /** Source-over for a premultiplied source: an opacity group's layer. */
12781
+ const PREMULTIPLIED_BLEND = {
12782
+ color: {
12783
+ srcFactor: "one",
12784
+ dstFactor: "one-minus-src-alpha",
12785
+ operation: "add"
12786
+ },
12787
+ alpha: {
12788
+ srcFactor: "one",
12789
+ dstFactor: "one-minus-src-alpha",
12790
+ operation: "add"
12791
+ }
12792
+ };
11128
12793
  /**
11129
12794
  * Creates the primitive pipeline: rounded fills and borders.
11130
12795
  *
@@ -11204,8 +12869,9 @@ function createTexturedPipeline(device, format, frameLayout) {
11204
12869
  texture: {}
11205
12870
  }
11206
12871
  ] });
12872
+ const layout = device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout, frameLayout] });
11207
12873
  const pipeline = device.createRenderPipeline({
11208
- layout: device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout, frameLayout] }),
12874
+ layout,
11209
12875
  vertex: {
11210
12876
  module,
11211
12877
  entryPoint: "vs",
@@ -11224,7 +12890,27 @@ function createTexturedPipeline(device, format, frameLayout) {
11224
12890
  cullMode: "none"
11225
12891
  }
11226
12892
  });
11227
- if (pipeline === void 0) throw new WebGPUError("Failed to create textured render pipeline.", "pipeline");
12893
+ const layerPipeline = device.createRenderPipeline({
12894
+ layout,
12895
+ vertex: {
12896
+ module,
12897
+ entryPoint: "vs",
12898
+ buffers: [quadVertexLayout(), texturedInstanceLayout()]
12899
+ },
12900
+ fragment: {
12901
+ module,
12902
+ entryPoint: "fsLayer",
12903
+ targets: [{
12904
+ format,
12905
+ blend: PREMULTIPLIED_BLEND
12906
+ }]
12907
+ },
12908
+ primitive: {
12909
+ topology: "triangle-list",
12910
+ cullMode: "none"
12911
+ }
12912
+ });
12913
+ if (pipeline === void 0 || layerPipeline === void 0) throw new WebGPUError("Failed to create textured render pipeline.", "pipeline");
11228
12914
  const { vertexBuffer, indexBuffer } = createQuad(device);
11229
12915
  const uniformBuffer = device.createBuffer({
11230
12916
  size: 16,
@@ -11244,7 +12930,8 @@ function createTexturedPipeline(device, format, frameLayout) {
11244
12930
  imageSampler: device.createSampler({
11245
12931
  magFilter: "linear",
11246
12932
  minFilter: "linear"
11247
- })
12933
+ }),
12934
+ layerPipeline
11248
12935
  };
11249
12936
  }
11250
12937
  function createQuad(device) {
@@ -11673,6 +13360,7 @@ function createCanvas(width, height) {
11673
13360
  const vertexBufferUsage = typeof GPUBufferUsage !== "undefined" ? GPUBufferUsage.VERTEX : 32;
11674
13361
  const storageBufferUsage = typeof GPUBufferUsage !== "undefined" ? GPUBufferUsage.STORAGE : 128;
11675
13362
  const copyDstBufferUsage = typeof GPUBufferUsage !== "undefined" ? GPUBufferUsage.COPY_DST : 8;
13363
+ const layerTextureUsage = (typeof GPUTextureUsage !== "undefined" ? GPUTextureUsage.RENDER_ATTACHMENT : 16) | (typeof GPUTextureUsage !== "undefined" ? GPUTextureUsage.TEXTURE_BINDING : 4);
11676
13364
  const copyDstUsage = typeof GPUBufferUsage !== "undefined" ? GPUBufferUsage.COPY_DST : 8;
11677
13365
  const mapReadUsage = typeof GPUBufferUsage !== "undefined" ? GPUBufferUsage.MAP_READ : 1;
11678
13366
  const mapModeRead = typeof GPUMapMode !== "undefined" ? GPUMapMode.READ : 1;
@@ -11705,6 +13393,12 @@ var WebGPURenderer = class {
11705
13393
  frameLayout = null;
11706
13394
  frameBindGroup = null;
11707
13395
  cachedInstanceData = null;
13396
+ /**
13397
+ * The textures opacity groups are drawn in, one per depth of nesting,
13398
+ * so a group inside a group never clears what holds it. Made the first
13399
+ * time a frame reaches that depth and again when the surface resizes.
13400
+ */
13401
+ groupLayers = [];
11708
13402
  /** Draw calls of the most recent frame. */
11709
13403
  lastDraws = {
11710
13404
  primitiveDraws: 0,
@@ -11847,8 +13541,9 @@ var WebGPURenderer = class {
11847
13541
  const encodeStart = this.hooks.onEncodeStart !== void 0 || this.hooks.onEncodeEnd !== void 0 ? performance.now() : 0;
11848
13542
  const commandEncoder = device.createCommandEncoder();
11849
13543
  const texture = this.surface.getCurrentTexture();
11850
- const pass = commandEncoder.beginRenderPass({ colorAttachments: [{
11851
- view: texture.createView(),
13544
+ const targets = [texture.createView()];
13545
+ let pass = commandEncoder.beginRenderPass({ colorAttachments: [{
13546
+ view: targets[0],
11852
13547
  clearValue: {
11853
13548
  r: 0,
11854
13549
  g: 0,
@@ -11868,6 +13563,49 @@ var WebGPURenderer = class {
11868
13563
  const commands = list.commands;
11869
13564
  for (let i = 0; i < commands.length; i++) {
11870
13565
  const command = commands[i];
13566
+ if (command.kind === 3 || command.kind === 4) {
13567
+ pass.end();
13568
+ const depth = targets.length - 1;
13569
+ let layer;
13570
+ if (command.kind === 3) {
13571
+ layer = this.groupLayer(device, depth, texture.width, texture.height);
13572
+ targets.push(layer.view);
13573
+ } else {
13574
+ layer = this.groupLayers[depth - 1];
13575
+ targets.pop();
13576
+ }
13577
+ pass = commandEncoder.beginRenderPass({ colorAttachments: [command.kind === 3 ? {
13578
+ view: layer.view,
13579
+ clearValue: {
13580
+ r: 0,
13581
+ g: 0,
13582
+ b: 0,
13583
+ a: 0
13584
+ },
13585
+ loadOp: "clear",
13586
+ storeOp: "store"
13587
+ } : {
13588
+ view: targets[targets.length - 1],
13589
+ loadOp: "load",
13590
+ storeOp: "store"
13591
+ }] });
13592
+ pass.setBindGroup(1, this.frameBindGroup);
13593
+ currentScissor = null;
13594
+ currentKind = null;
13595
+ if (command.kind === 3) continue;
13596
+ const blendScissor = command.scissor ?? viewport;
13597
+ if (command.instance === null || blendScissor.width <= 0 || blendScissor.height <= 0) continue;
13598
+ pass.setPipeline(textured.layerPipeline);
13599
+ pass.setVertexBuffer(0, textured.vertexBuffer);
13600
+ pass.setVertexBuffer(1, this.texturedBuffer);
13601
+ pass.setIndexBuffer(textured.indexBuffer, "uint16");
13602
+ pass.setBindGroup(0, layer.bindGroup);
13603
+ currentScissor = setScissor(pass, currentScissor, blendScissor);
13604
+ pass.drawIndexed(textured.indexCount, 1, 0, 0, command.instance);
13605
+ this.lastDraws.texturedDraws++;
13606
+ this.lastDraws.texturedInstances++;
13607
+ continue;
13608
+ }
11871
13609
  const scissor = command.scissor ?? viewport;
11872
13610
  if (scissor.width <= 0 || scissor.height <= 0) continue;
11873
13611
  if (command.kind === 0) {
@@ -11909,6 +13647,24 @@ var WebGPURenderer = class {
11909
13647
  capture?.();
11910
13648
  if (this.hooks.onEncodeEnd !== void 0) this.hooks.onEncodeEnd(performance.now() - encodeStart);
11911
13649
  }
13650
+ /** The layer for an opacity group at `depth`, made or remade at the surface's size. */
13651
+ groupLayer(device, depth, width, height) {
13652
+ const existing = this.groupLayers[depth];
13653
+ if (existing !== void 0 && existing.texture.width === width && existing.texture.height === height) return existing;
13654
+ existing?.texture.destroy();
13655
+ const texture = device.createTexture({
13656
+ size: [width, height],
13657
+ format: this.format,
13658
+ usage: layerTextureUsage
13659
+ });
13660
+ const layer = {
13661
+ texture,
13662
+ view: texture.createView(),
13663
+ bindGroup: createTexturedBindGroup(device, this.textured, texture, this.textured.textSampler)
13664
+ };
13665
+ this.groupLayers[depth] = layer;
13666
+ return layer;
13667
+ }
11912
13668
  /**
11913
13669
  * When a capture is pending, encodes a copy of the frame into a
11914
13670
  * mappable buffer and returns the step that maps it after submit.
@@ -12034,6 +13790,8 @@ var WebGPURenderer = class {
12034
13790
  this.clipBuffer = null;
12035
13791
  this.gradientBuffer?.destroy();
12036
13792
  this.gradientBuffer = null;
13793
+ for (const layer of this.groupLayers) layer.texture.destroy();
13794
+ this.groupLayers = [];
12037
13795
  this.frameBindGroup = null;
12038
13796
  this.frameLayout = null;
12039
13797
  this.primitives = null;
@@ -14723,6 +16481,14 @@ const NEW_DISPLAY = .15;
14723
16481
  const BEAT_PASSES = 3;
14724
16482
  /** Refreshes apart the two an interval is read between must be, at least. */
14725
16483
  const MIN_BEAT_SPAN = 6;
16484
+ /**
16485
+ * The most refreshes a frame waits for a tick that has not sat in this
16486
+ * thread's queue (see `tick`): a frame is drawn on a stale one after
16487
+ * that, so a thread that is always behind still draws.
16488
+ */
16489
+ const MAX_QUEUED_REFRESHES = 8;
16490
+ /** How long, at least, between timing this thread's refreshes again once they were given up; see `retryLocal`. */
16491
+ const RETRY_LOCAL_MS = 2e3;
14726
16492
  /** The most refreshes a frame is given: two, sixty at 120 Hz; slower than that is no smoother. */
14727
16493
  const MAX_STRIDE = 2;
14728
16494
  function localAnimationFrame() {
@@ -14776,6 +16542,8 @@ var UiHostFrameClock = class {
14776
16542
  /** Gaps between this thread's own refreshes while frames ran back to back. */
14777
16543
  localIntervals = [];
14778
16544
  lastLocalTime = null;
16545
+ /** How long the frame drawn at `lastLocalTime` took, which says whether the gap after it was the refresh's or the frame's. */
16546
+ lastLocalCost = 0;
14779
16547
  /** The display's interval and the host's time origin, once the host has said. */
14780
16548
  display = null;
14781
16549
  /**
@@ -14784,6 +16552,11 @@ var UiHostFrameClock = class {
14784
16552
  * show (see \`displayInterval\`).
14785
16553
  */
14786
16554
  locked = false;
16555
+ /** This thread's own `requestAnimationFrame` once given up, kept to see whether it keeps time after all. */
16556
+ givenUp = null;
16557
+ /** When this thread's refreshes were last timed again, and their times while they are being. */
16558
+ lastRetry = -Infinity;
16559
+ retrying = null;
14787
16560
  /** The host's recent refresh times, which give the beat its phase. */
14788
16561
  refreshes = [];
14789
16562
  /** Host refreshes since the last frame drawn on the beat. */
@@ -14818,7 +16591,11 @@ var UiHostFrameClock = class {
14818
16591
  if (this.localHandle === null) this.localHandle = this.local((time) => {
14819
16592
  this.localHandle = null;
14820
16593
  this.recordLocal(time);
14821
- if (this.pending) this.deliver(time);
16594
+ if (this.pending) {
16595
+ const started = this.now();
16596
+ this.deliver(time);
16597
+ this.lastLocalCost = this.now() - started;
16598
+ } else this.lastLocalCost = 0;
14822
16599
  });
14823
16600
  return;
14824
16601
  }
@@ -14873,7 +16650,7 @@ var UiHostFrameClock = class {
14873
16650
  this.lastLocalTime = time;
14874
16651
  if (previous !== null && this.pending) {
14875
16652
  const interval = time - previous;
14876
- if (interval > 0 && interval < MAX_CADENCE_MS) {
16653
+ if (interval > 0 && interval < MAX_CADENCE_MS && this.lastLocalCost < interval / 2) {
14877
16654
  this.localIntervals.push(interval);
14878
16655
  if (this.localIntervals.length > LOCAL_SAMPLES) this.localIntervals.shift();
14879
16656
  }
@@ -14891,9 +16668,59 @@ var UiHostFrameClock = class {
14891
16668
  if (this.local === null || this.display === null || this.localIntervals.length < LOCAL_SAMPLES) return;
14892
16669
  const sorted = [...this.localIntervals].sort((a, b) => a - b);
14893
16670
  if (sorted[Math.floor(sorted.length / 2)] / this.display.interval <= 1.06) return;
16671
+ this.givenUp = this.local;
14894
16672
  this.local = null;
14895
16673
  this.locked = true;
14896
16674
  }
16675
+ /**
16676
+ * Times this thread's own refreshes again, now and then, once they were
16677
+ * given up, and takes them back if they keep the display's time.
16678
+ *
16679
+ * Given up for good, a worker's refreshes stayed given up whatever had
16680
+ * made them slow. A Chrome worker busy between its frames, opening a
16681
+ * large file or scrolling through lines it has never drawn, refreshes
16682
+ * late and unevenly, which no measure of the gaps alone told from
16683
+ * WebKit's timer: 5.5, 25.5, 10.9, 25 ms on a 165 Hz display one time,
16684
+ * 12.1, 6.1, 12.2, 24.2 the next. Its frames then went through the
16685
+ * host's ticks for as long as the page was open, a message and a hop
16686
+ * through the page's thread each. Timed again while frames are cheap,
16687
+ * they come a refresh apart and are taken back; WebKit's come 15 ms
16688
+ * apart on a 120 Hz display however idle the thread is, and stay given
16689
+ * up. Nine refreshes every two seconds or more, while frames come cheap.
16690
+ */
16691
+ retryLocal(interval) {
16692
+ if (this.givenUp === null || this.hidden || this.costs.length < SLOW_SAMPLES) return;
16693
+ const now = this.now();
16694
+ if (now - this.lastRetry < RETRY_LOCAL_MS) return;
16695
+ const sorted = [...this.costs].sort((a, b) => a - b);
16696
+ if (sorted[Math.floor(sorted.length / 2)] > interval * FAST_FRAME) return;
16697
+ this.lastRetry = now;
16698
+ const local = this.givenUp;
16699
+ const times = [];
16700
+ this.retrying = times;
16701
+ const step = (time) => {
16702
+ if (this.retrying !== times) return;
16703
+ times.push(time);
16704
+ if (times.length <= LOCAL_SAMPLES) {
16705
+ local(step);
16706
+ return;
16707
+ }
16708
+ this.retrying = null;
16709
+ const gaps = times.slice(1).map((value, index) => value - times[index]).sort((a, b) => a - b);
16710
+ if (this.givenUp === null || this.display === null || gaps[Math.floor(gaps.length / 2)] / this.display.interval > 1.06) return;
16711
+ this.local = this.givenUp;
16712
+ this.givenUp = null;
16713
+ this.locked = false;
16714
+ this.localIntervals.length = 0;
16715
+ this.lastLocalTime = null;
16716
+ this.costs.length = 0;
16717
+ this.stride = 1;
16718
+ this.clearFallback();
16719
+ this.setActive(false);
16720
+ if (this.pending) this.requestFrame();
16721
+ };
16722
+ local(step);
16723
+ }
14897
16724
  /** A time on the host's clock, on this thread's. */
14898
16725
  toLocal(host) {
14899
16726
  return this.display === null ? host : host - this.timeOrigin + this.display.origin;
@@ -14959,6 +16786,7 @@ var UiHostFrameClock = class {
14959
16786
  const started = this.now();
14960
16787
  this.deliver(this.toLocal(refresh));
14961
16788
  this.recordCost(this.now() - started, beat.interval);
16789
+ this.retryLocal(beat.interval);
14962
16790
  }
14963
16791
  /**
14964
16792
  * Keeps the stride to what frames cost: every refresh while the typical
@@ -15120,7 +16948,8 @@ var UiHostFrameClock = class {
15120
16948
  if (this.pending) {
15121
16949
  this.clearFallback();
15122
16950
  this.idleTicks = 0;
15123
- if (this.refreshesSinceFrame >= this.stride) this.deliverOnBeat(time);
16951
+ const queued = this.now() - this.toLocal(time) > this.display.interval;
16952
+ if (this.refreshesSinceFrame >= this.stride && (!queued || this.refreshesSinceFrame >= MAX_QUEUED_REFRESHES)) this.deliverOnBeat(time);
15124
16953
  return;
15125
16954
  }
15126
16955
  }
@@ -16114,6 +17943,6 @@ function recordsEqual(a, b) {
16114
17943
  return true;
16115
17944
  }
16116
17945
  //#endregion
16117
- export { AlignContent, AnimationDriver, BOX_SHADOW_BLUR_REACH, BUTTON_INTERACTION, Box, Button, CARET_BLINK_MS, CARET_WIDTH, CLIP_STRIDE_BYTES, CLIP_STRIDE_FLOATS, Canvas2DRenderer, CanvasPlatformSurface, CanvasSurface, CanvasTextMeasurer, CharacterCountTextMeasurer, Column, CommandKind, Constraints, CrossAxisAlignment, DEFAULT_BLOCK_SIZE, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, DEFAULT_FONT_WEIGHT, DEFAULT_LINE_HEIGHT_FACTOR, DEFAULT_TEXT_COLOR, DefaultImageResolver, DefaultVideoResolver, DirtyFlags, DirtyNodeSet, EDITOR_PROP, ELLIPSIS, EMPTY_RECORDING, EXTERNAL_FILES, EditableLayout, EditableText, EditableTextModel, EnvironmentNotifier, FlexDirection, FocusNotifier, GLYPH_SUBPIXEL_PHASES, GlyphShaper, Grid, INSPECTOR_HEAT_MS, INSTANCE_STRIDE_BYTES, INSTANCE_STRIDE_FLOATS, IconRasterizer, LABEL_PADDING_X, LayoutEngine, LayoutInspector, LayoutNotifier, LayoutRecord, LazyColumn, LazyGrid, LazyRow, LazySheet, MARK_PREFIX, MAX_GRADIENT_STOPS, MAX_MEASURES_PER_CHILD, MOTION_CHANNELS, MOTION_REST, MOUSE_POINTER, MainAxisAlignment, Mp3Frames, NO_CLIP_INDEX, NodeTransitions, Paint, PaintPictureCache, PaintRecorder, PaintTarget, ParagraphTextMeasurer, PrimitiveKind, Responsive, Row, SCROLLBAR_FADE_MS, SCROLLBAR_HOVER_ZONE, SCROLLBAR_INSET, SCROLLBAR_LINGER_MS, SCROLLBAR_MIN_THUMB, SCROLLBAR_THICKNESS, ScrollView, Stack, TEXTURED_STRIDE_BYTES, TEXTURED_STRIDE_FLOATS, TEXT_MATCHES_PROP, TEXT_RUN_ID_SEPARATOR, TEXT_SELECTION_PROP, Text, UI_ROLES, UI_SEMANTIC_STATES, UI_SUBGRID_AXES, UiAnimation, UiAnimationFrameClock, UiBasicColors, UiBeforeInputEvent, UiBinding, UiBorderRadiuses, UiChildrenBinding, UiCompositionEvent, UiContainerSizeSource, UiDragSession, UiEditingController, UiEnvironment, UiEnvironmentKeys, UiEventBinding, UiEventType, UiFindController, UiFocusEvent, UiFocusManager, UiFrame, UiGestureEvent, UiGestureRecognizer, UiGraph, UiGraphBuilder, UiHitTester, UiHostFrameClock, UiInputDispatcher, UiInputEvent, UiInsetRegistry, UiKeyboardController, UiKeyboardEvent, UiManualFrameClock, UiModifierSet, UiNode, UiNodeType, UiPasteEvent, UiPinchEvent, UiPinchRecognizer, UiPlatformAdapter, UiPointerController, UiPointerEvent, UiProperties, UiScheduler, UiSelectionChangeEvent, UiSelectionController, UiSharedElements, UiShortcutRegistry, UiSpring, UiTextChangeEvent, UiTimerFrameClock, UiTouchScroller, UiTransforms, UiTween, UiUnknownModifierPropertyError, UiVirtualSheet, UiVirtualWindow, UiVisualState, UiWheelController, UiWheelDeltaMode, UiWheelEvent, VIRTUAL_INDEX_PROP, VIRTUAL_LEAD_PROP, VIRTUAL_SHEET_PROP, VIRTUAL_WINDOW_PROP, WebGPUError, WebGPUGlyphAtlas, WebGPUGlyphPages, WebGPURenderer, WebGPUSurface, WebGPUTextureCache, accumulatedOffsetTo, adjacentField, animateLayout, applyCanvasTextStyle, assertModifierList, assertTransitionMap, auto, autoFocus, bandOf, borderRadius, borderRadiusCorners, borderRadiusEqual, borderRadiusIsZero, borderRadiusValuesEqual, borderShapes, borders, boxShadow, boxShadowArraysEqual, boxShadowRadius, boxShadowReach, boxShadowsEqual, breakpoint, bufferSource, buildFontString, buildRenderList, buildSemanticsSubtree, buildSemanticsTree, bumpFontStack, bundle, canDecodeVideo, capturePointer, caretRectFor, caretVisibleAt, clampSize, clearFontStacks, clearLinkHover, clearMatchRanges, clearOverrideProperty, clearSelectionRange, clickOutside, closestPropertyName, colorToCss, colorToHex, colorToRgba, colorValuesEqual, colorsEqual, colorsEqualPalette, commandForKey, comparePositions, computeObjectFitRect, constraintsEqual, containerBands, contentOffset, contextMenu, contrastRatio, createCanvasSurface, createCanvasTextStyle, createElement, createEnvironmentKey, createPaintState, createTextCache, createTween, createWebGPUSurface, cubicBezier, cueAt, darkColors, darkTheme, decorated, decorationColor, decorationRect, defaultMotion, defaultShadows, defaultShapes, defaultSpacing, defaultTextStyle, defaultTypography, defaultVisualState, defineModifier, defineProperty, defineThemeExtension, demuxMp4Audio, demuxMp4Video, densityFactors, describeLength, describeOverrides, describeShortcut, detectEditingPlatform, diffSemantics, dragSessionFor, dragSource, draggable, drawOverlayShapes, drawText, dropIndexShifts, dropTarget, easings, edgeField, editableSpansOf, editingGroupOf, editorFor, editorOf, fade, fillTextRects, findEnvironmentKey, findMatchesIn, findPropertyDefinition, flattenTextSpans, focusRing, fontStackFor, formatConstraints, formatExplanation, formatShortcut, fr, getPropertyNames, glyphCount, gradientPaint, gradientsEqual, graphemeBoundaries, hasDecorationPhase, hasDrawnText, hasTextLinks, hasThemeExtension, hasVisualState, hiddenRangesOf, highContrastColors, hoverable, iconKey, iconSource, imageSource, inheritedPropertyFlags, inheritedPropertyNames, initializeWebGPU, inputsEqual, insetPadding, insetsEqual, interactive, interpolateColor, interpolateNumber, interpolateTransform, interpolatorFor, isAutoLength, isComponentLikeElement, isEditableNode, isFrLength, isImeKey, isLayoutProtocol, isMinMaxTrack, isMotionRest, isMultiline, isNodeFocusable, isNodeHitTestable, isNodeInert, isNodeSelectable, isNodeTabStop, isObservable, isPaintVisible, isPercentLength, isPrintable, isReadOnly, isScrollContainer, isShapeName, isSubtreeInert, isTypographyRole, isUiChild, isUiElement, isUiModifier, isUiRole, isUiSemanticState, isVideoSurface, isWebGPUAvailable, labelNode, labelOrigin, layoutParagraph, layoutTextLines, lazySource, lightColors, lightTheme, lineEndAt, lineIndexAtY, lineIndexForOffset, lineLimit, lineStartAt, linear, linearGradient, linkHoverOf, linkOf, linkRunAtPointIn, markInstant, markNow, matchRangesOf, measure, measureFlow, measureSpan, minmax, motion, nextCaretToggle, nextGraphemeEnd, nextWordEnd, noInsets, noKeyModifiers, noModifiers, noThemeExtensions, normalizeBorderRadius, normalizeColor, normalizeStates, normalizeTextAlign, normalizeTransition, normalizeVerticalAlign, observeViewportInsets, offsetAtPoint, offsetAtPointIn, offsetAtX, offsetForVerticalMove, overrideSources, paintPictures, paintValuesEqual, paintsAfterChildren, paragraphGeometry, paragraphGeometryFrom, parseAlignContent, parseColor, parseCrossAxisAlignment, parseFlexDirection, parseMainAxisAlignment, parseShortcut, parseTransform, parseWebVtt, pathValuesEqual, percent, performanceMarksEnabled, pinchable, placeGridItems, pointerDeviceOf, pointerEventsOf, pop, prepareInputSurface, pressable, previousGraphemeStart, previousWordStart, propertyEffects, propertyIsInherited, propertyNarrowers, propertyValuesEqual, propertyValuesEqualByName, proportionalFontMetrics, provideEnvironment, publishInset, radialGradient, raiseContrast, rangeSource, readMp3Header, recordsEqual, registerFontStack, relativeLuminance, reorderable, repeat, replayPaint, resetOverrideWarnings, resetTransitionWarnings, resolveBoolean, resolveCursor, resolveLength, resolveMotionState, resolveNumber, resolvePaintState, resolveProperty, resolvePropertyByName, resolveString, resolvedSpansOf, rewalkSemantics, rgb8, rgba, rotateFrom, runCanvasStyleInto, runFontString, sameArgs, scaleFrom, scaleSpacing, scrollBounds, scrollLeaderOf, scrollPosition, scrollRange, scrollbarThumb, scrollbarThumbs, scrollbarZoneAt, scrollsFromRight, selectableTextNodes, selectableTextOf, selectionRangeOf, selectionRects, selectionRectsIn, semanticsInertAbove, semanticsMemory, setLinkHover, setMatchRanges, setPerformanceMarks, setSelectionRange, shadowsEqual, shapesEqual, sharedElement, sheetSource, shortcut, shortcutKeyCaps, shortcuts, sizeContainer, sizeGridTracks, slideDown, slideFrom, slideUp, spacingEqual, spacingSteps, spanAtOffset, spannedRunsFor, spannedTextOf, spansDifferOnlyInPaint, splitMp3Frames, spring, statesEqual, steps, syncEditorValue, textContentOf, textInputOf, textRunBackgrounds, textRunDecorations, textRunOfRecordId, textRuns, textSpansEqual, textStylesEqual, themeExtension, themeExtensionsEqual, themeTokens, themesEqual, tightenConstraints, touchActionFor, tracePathData, traceRoundedRect, transform, transformIsIdentity, transformsEqual, tween, typographyEqual, uniformBorderRadius, unknownContainerSize, validateGradient, validateRole, validateStates, validateSubgrid, videoFrameSize, videoSource, visualState, visualStatesEqual, wheelDeltaYOf, withContrast, withDensity, withOverrideSource, withThemeExtension, wordRangeAt, wordRangeIn, writeDeclaredProperty, writeOverrideProperty };
17946
+ export { AlignContent, AnimationDriver, BOX_SHADOW_BLUR_REACH, BUTTON_INTERACTION, Box, Button, CARET_BLINK_MS, CARET_WIDTH, CLIP_STRIDE_BYTES, CLIP_STRIDE_FLOATS, Canvas2DRenderer, CanvasPlatformSurface, CanvasSurface, CanvasTextMeasurer, CharacterCountTextMeasurer, Column, CommandKind, Constraints, CrossAxisAlignment, DEFAULT_BLOCK_SIZE, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, DEFAULT_FONT_WEIGHT, DEFAULT_LINE_HEIGHT_FACTOR, DEFAULT_TEXT_COLOR, DefaultImageResolver, DefaultSvgResolver, DefaultVideoResolver, DirtyFlags, DirtyNodeSet, EDITOR_PROP, ELLIPSIS, EMPTY_RECORDING, EXTERNAL_FILES, EditableLayout, EditableText, EditableTextModel, EnvironmentNotifier, FlexDirection, FocusNotifier, GLYPH_SUBPIXEL_PHASES, GlyphShaper, Grid, INSPECTOR_HEAT_MS, INSTANCE_STRIDE_BYTES, INSTANCE_STRIDE_FLOATS, IconRasterizer, LABEL_PADDING_X, LayoutEngine, LayoutInspector, LayoutNotifier, LayoutRecord, LazyColumn, LazyGrid, LazyRow, LazySheet, MARK_PREFIX, MAX_GRADIENT_STOPS, MAX_MEASURES_PER_CHILD, MOTION_CHANNELS, MOTION_REST, MOUSE_POINTER, MainAxisAlignment, Mp3Frames, NO_CLIP_INDEX, NodeTransitions, Paint, PaintPictureCache, PaintRecorder, PaintTarget, ParagraphTextMeasurer, PrimitiveKind, Responsive, Row, SCROLLBAR_FADE_MS, SCROLLBAR_HOVER_ZONE, SCROLLBAR_INSET, SCROLLBAR_LINGER_MS, SCROLLBAR_MIN_THUMB, SCROLLBAR_THICKNESS, ScrollView, ShownAsKey, Stack, TEXTURED_STRIDE_BYTES, TEXTURED_STRIDE_FLOATS, TEXT_MATCHES_PROP, TEXT_RUN_ID_SEPARATOR, TEXT_SELECTION_PROP, TOUCH_TARGET, Text, UI_ROLES, UI_SEMANTIC_STATES, UI_SUBGRID_AXES, UiAnimation, UiAnimationFrameClock, UiBasicColors, UiBeforeInputEvent, UiBinding, UiBorderRadiuses, UiChildrenBinding, UiClock, UiCompositionEvent, UiContainerSizeSource, UiDragSession, UiEditingController, UiEnvironment, UiEnvironmentKeys, UiEventBinding, UiEventType, UiFindController, UiFocusEvent, UiFocusManager, UiFrame, UiGestureEvent, UiGestureRecognizer, UiGraph, UiGraphBuilder, UiHitTester, UiHostFrameClock, UiInputDispatcher, UiInputEvent, UiInsetRegistry, UiKeyboardController, UiKeyboardEvent, UiManualFrameClock, UiModifierSet, UiNode, UiNodeType, UiPasteEvent, UiPinchEvent, UiPinchRecognizer, UiPlatformAdapter, UiPointerController, UiPointerEvent, UiProperties, UiScheduler, UiSelectionChangeEvent, UiSelectionController, UiSharedElements, UiShortcutRegistry, UiSpring, UiTextChangeEvent, UiTimerFrameClock, UiTouchScroller, UiTransforms, UiTween, UiUnknownModifierPropertyError, UiVirtualSheet, UiVirtualWindow, UiVisualState, UiWheelController, UiWheelDeltaMode, UiWheelEvent, VIRTUAL_INDEX_PROP, VIRTUAL_LEAD_PROP, VIRTUAL_SHEET_PROP, VIRTUAL_WINDOW_PROP, WebGPUError, WebGPUGlyphAtlas, WebGPUGlyphPages, WebGPURenderer, WebGPUSurface, WebGPUTextureCache, accumulatedOffsetTo, adjacentField, animateLayout, applyCanvasTextStyle, assertModifierList, assertTransitionMap, auto, autoFocus, bandOf, borderRadius, borderRadiusCorners, borderRadiusEqual, borderRadiusIsZero, borderRadiusValuesEqual, borderShapes, borders, boxShadow, boxShadowArraysEqual, boxShadowRadius, boxShadowReach, boxShadowValuesEqual, boxShadowsEqual, breakpoint, bufferSource, buildFontString, buildRenderList, buildSemanticsSubtree, buildSemanticsTree, bumpFontStack, bundle, canDecodeVideo, capturePointer, caretRectFor, caretVisibleAt, clampSize, clearFontStacks, clearLinkHover, clearMatchRanges, clearOverrideProperty, clearSelectionRange, clickOutside, clockOf, closestPropertyName, colorToCss, colorToHex, colorToRgba, colorValuesEqual, colorsEqual, colorsEqualPalette, commandForKey, comparePositions, computeObjectFitRect, constraintsEqual, containerBands, contentOffset, contextMenu, contrastRatio, createCanvasSurface, createCanvasTextStyle, createElement, createEnvironmentKey, createPaintState, createTextCache, createTween, createWebGPUSurface, cubicBezier, cueAt, darkColors, darkTheme, decorated, decorationColor, decorationRect, defaultMotion, defaultShadows, defaultShapes, defaultSpacing, defaultTextStyle, defaultTypography, defaultUnits, defaultVisualState, defineModifier, defineMotion, defineProperty, defineThemeExtension, demuxMp4Audio, demuxMp4Video, densityFactors, describeLength, describeOverrides, describeShortcut, detectEditingPlatform, diffSemantics, dragSessionFor, dragSource, draggable, drawOverlayShapes, drawSvg, drawText, dropIndexShifts, dropTarget, durationIn, easingCss, easingIn, easings, edgeField, editableSpansOf, editingGroupOf, editorFor, editorOf, fade, fillTextRects, findEnvironmentKey, findMatchesIn, findPropertyDefinition, fittedShadowRadius, flattenTextSpans, focusRing, fontStackFor, formatConstraints, formatExplanation, formatShortcut, fr, getPropertyNames, glyphCount, gradientPaint, gradientsEqual, graphemeBoundaries, hasDecorationPhase, hasDrawnText, hasTextLinks, hasThemeExtension, hasVisualState, hiddenRangesOf, highContrastColors, hoverable, iconKey, iconSource, imageSource, inheritedPropertyFlags, inheritedPropertyNames, initializeWebGPU, inputsEqual, insetPadding, insetsEqual, interactive, interpolateColor, interpolateNumber, interpolateTransform, interpolatorFor, isAutoLength, isComponentLikeElement, isEditableNode, isFrLength, isImeKey, isLayoutProtocol, isMinMaxTrack, isMotionRest, isMultiline, isNodeFocusable, isNodeHitTestable, isNodeInert, isNodeSelectable, isNodeTabStop, isObservable, isPaintVisible, isPercentLength, isPrintable, isReadOnly, isScrollContainer, isShapeName, isSubtreeInert, isSvgSource, isTypographyRole, isUiChild, isUiElement, isUiModifier, isUiRole, isUiSemanticState, isVideoSurface, isWebGPUAvailable, labelNode, labelOrigin, layoutParagraph, layoutTextLines, lazySource, lengths, lightColors, lightTheme, lineEndAt, lineIndexAtY, lineIndexForOffset, lineLimit, lineStartAt, linear, linearGradient, linkHoverOf, linkOf, linkRunAtPointIn, markInstant, markNow, matchRangesOf, measure, measureFlow, measureSpan, mediaLoadsInFlight, minmax, motion, motionOf, nextCaretToggle, nextGraphemeEnd, nextWordEnd, noInsets, noKeyModifiers, noModifiers, noThemeExtensions, normalizeBorderRadius, normalizeColor, normalizeStates, normalizeTextAlign, normalizeTransition, normalizeVerticalAlign, observeViewportInsets, offsetAtPoint, offsetAtPointIn, offsetAtX, offsetForVerticalMove, overrideSources, paintPictures, paintValuesEqual, paintsAfterChildren, paragraphGeometry, paragraphGeometryFrom, parseAlignContent, parseColor, parseCrossAxisAlignment, parseFlexDirection, parseMainAxisAlignment, parseShortcut, parseSvg, parseTransform, parseWebVtt, pathValuesEqual, percent, performanceMarksEnabled, pinchable, placeGridItems, pointerDeviceOf, pointerEventsOf, pop, prepareInputSurface, pressable, previousGraphemeStart, previousWordStart, propertyEffects, propertyIsInherited, propertyNarrowers, propertyValuesEqual, propertyValuesEqualByName, proportionalFontMetrics, provideEnvironment, provided, publishInset, radialGradient, raiseContrast, rangeSource, readMp3Header, recordsEqual, registerFontStack, relativeLuminance, reorderable, repeat, replayPaint, resetOverrideWarnings, resetTransitionWarnings, resolveBoolean, resolveCursor, resolveLength, resolveMotionState, resolveNumber, resolvePaintState, resolveProperty, resolvePropertyByName, resolveString, resolvedSpansOf, rewalkSemantics, rgb8, rgba, rotateFrom, runCanvasStyleInto, runFontString, sameArgs, scaleFrom, scaleSpacing, scrollBounds, scrollLeaderOf, scrollPosition, scrollRange, scrollbarThumb, scrollbarThumbs, scrollbarZoneAt, scrollsFromRight, selectableTextNodes, selectableTextOf, selectionRangeOf, selectionRects, selectionRectsIn, semanticsInertAbove, semanticsMemory, setLinkHover, setMatchRanges, setPerformanceMarks, setSelectionRange, shadowsEqual, shadowsOf, shapesEqual, sharedElement, sheetSource, shortcut, shortcutKeyCaps, shortcuts, sizeContainer, sizeGridTracks, slideDown, slideFrom, slideUp, spacingEqual, spacingSteps, spanAtOffset, spannedRunsFor, spannedTextOf, spansDifferOnlyInPaint, splitMp3Frames, spring, springIn, statesEqual, steps, svgPaint, syncEditorValue, textContentOf, textInputOf, textRunBackgrounds, textRunDecorations, textRunOfRecordId, textRuns, textSpansEqual, textStylesEqual, themeExtension, themeExtensionsEqual, themeTokens, themesEqual, tightenConstraints, touchActionFor, tracePathData, traceRoundedRect, transform, transformIsIdentity, transformsEqual, tween, typographyEqual, uniformBorderRadius, unitsEqual, unknownContainerSize, validateGradient, validateRole, validateStates, validateSubgrid, videoFrameSize, videoSource, visualState, visualStatesEqual, wheelDeltaYOf, withContrast, withDensity, withOverrideSource, withPointer, withThemeExtension, wordRangeAt, wordRangeIn, writeDeclaredProperty, writeOverrideProperty };
16118
17947
 
16119
17948
  //# sourceMappingURL=index.js.map