gesso-core 0.6.29 → 0.7.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/CHANGELOG.md +32 -0
- package/dist/{CanvasSurface-BTZ3A3MN.js → CanvasSurface-BpgbOku2.js} +1799 -588
- package/dist/CanvasSurface-BpgbOku2.js.map +1 -0
- package/dist/{index-v2fhm7Pn.d.ts → index-B5r1ETCh.d.ts} +1881 -1166
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1520 -36
- package/dist/index.js.map +1 -1
- package/dist/testing.d.ts +46 -3
- package/dist/testing.js +99 -10
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
- package/dist/CanvasSurface-BTZ3A3MN.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as UiPlatformAdapter, $a as UiNode, $i as themeExtension, $n as isVideoSurface, $r as pathValuesEqual, $t as textRunDecorations, A as formatNumber, Aa as hiddenRangesOf, Ai as propertyValuesEqual, An as isPressable, Ao as UiBinding, Ar as resolveString, At as paragraphGeometryFrom, B as MAX_MEASURES_PER_CHILD, Ba as lightColors, Bi as containerBands, Bn as CARET_BLINK_MS, Br as UiProperties, Bt as isPrintable, C as LayoutEngine, Ca as shapesEqual, Ci as borderRadiusCorners, Cn as selectionRangeOf, Co as UiTextChangeEvent, Cr as wordRangeAt, Ct as scrollbarThumbs, D as describeOverrides, Da as defaultTextStyle, Di as normalizeBorderRadius, Dn as isNodeInert, Do as wheelDeltaYOf, Dr as resolveNumber, Dt as hasDrawnText, E as describeLength, Ea as typographyEqual, Ei as borderRadiusValuesEqual, En as isNodeHitTestable, Eo as noKeyModifiers, Er as resolveBoolean, Et as UiInputDispatcher, F as LayoutRecord, Fa as textContentOf, Fi as insetsEqual, Fn as hasTextLinks, Fr as inheritedPropertyNames, Ft as comparePositions, G as sizeGridTracks, Ga as colorsEqual, Gi as withContrast, Gn as editorOf, Gr as normalizeStates, Gt as drawText, H as FlexDirection, Ha as colorToHex, Hi as darkTheme, Hn as EDITOR_PROP, Hr as UI_SEMANTIC_STATES, Ht as applyCanvasTextStyle, I as Constraints, Ia as textSpansEqual, Ii as noInsets, In as linkHoverOf, Ir as propertyEffects, It as edgeField, J as MainAxisAlignment, Ja as rgb8, Ji as lengths, Jn as isReadOnly, Jr as validateStates, Jt as runCanvasStyleInto, K as AlignContent, Ka as normalizeColor, Ki as withDensity, Kn as isEditableNode, Kr as statesEqual, Kt as fillTextRects, L as clampSize, La as textStylesEqual, Li as observeViewportInsets, Ln as linkOf, Lr as propertyIsInherited, Lt as editingGroupOf, M as withOverrideSource, Ma as spanAtOffset, Mi as withPointer, Mn as pointerEventsOf, Mr as findPropertyDefinition, Mt as wordRangeIn, N as accumulatedOffsetTo, Na as spannedTextOf, Ni as UiEnvironmentKeys, Nn as resolveCursor, Nr as getPropertyNames, Nt as textInputOf, O as formatConstraints, Oa as editableSpansOf, Oi as uniformBorderRadius, On as isNodeSelectable, Oo as UiNodeType, Or as resolveProperty, Ot as offsetAtPointIn, P as contentOffset, Pa as spansDifferOnlyInPaint, Pi as UiInsetRegistry, Pn as clearLinkHover, Pr as inheritedPropertyFlags, Pt as adjacentField, Q as CanvasPlatformSurface, Qa as UiEnvironment, Qi as noThemeExtensions, Qn as resolveColorValue, Qr as paintValuesEqual, Qt as textRunBackgrounds, R as constraintsEqual, Ra as colorsEqualPalette, Ri as UiContainerSizeSource, Rn as linkRunAtPointIn, Rr as propertyNarrowers, Rt as commandForKey, S as tracePathData, Sa as isShapeName, Si as borderRadius, Sn as selectableTextOf, So as UiSelectionChangeEvent, Sr as previousWordStart, St as scrollbarThumb, T as SCROLLBAR_LINGER_MS, Ta as isTypographyRole, Ti as borderRadiusIsZero, Tn as isNodeFocusable, To as UiWheelEvent, Tr as DEFAULT_LINE_HEIGHT_FACTOR, Tt as scrollsFromRight, U as parseFlexDirection, Ua as colorToRgba, Ui as lightTheme, Un as caretVisibleAt, Ur as isUiRole, Ut as buildFontString, V as isLayoutProtocol, Va as UiBasicColors, Vi as unknownContainerSize, Vn as CARET_WIDTH, Vr as UI_ROLES, Vt as EditableLayout, W as placeGridItems, Wa as colorValuesEqual, Wi as themesEqual, Wn as editorFor, Wr as isUiSemanticState, Wt as createCanvasTextStyle, X as parseCrossAxisAlignment, Xa as createEnvironmentKey, Xi as defineThemeExtension, Xn as nextCaretToggle, Xr as validateSubgrid, Xt as spanOf, Y as parseAlignContent, Ya as rgba, Yi as unitsEqual, Yn as maskText, Yr as UI_SUBGRID_AXES, Yt as runFontString, Z as parseMainAxisAlignment, Za as findEnvironmentKey, Zi as hasThemeExtension, Zn as syncEditorValue, Zr as inputsEqual, Zt as textMeasureRequest, _ as paintsAfterChildren, _a as boxShadowReach, _i as minmax, _n as matchRangesOf, _o as UiInputEvent, _r as lineEndAt, _t as SCROLLBAR_MIN_THUMB, a as layerRegion, aa as relativeLuminance, ai as MAX_GRADIENT_STOPS, an as DEFAULT_FONT_SIZE, ao as writeOverrideProperty, ar as layoutParagraph, at as UiEditingController, b as labelOrigin, ba as shadowsOf, bi as resolveLength, bn as clearSelectionRange, bo as UiPinchEvent, br as nextWordEnd, bt as scrollLeaderOf, c as paintPictures, ca as scaleSpacing, ci as linearGradient, cn as colorToCss, co as hasVisualState, cr as lineIndexAtY, ct as UiPinchRecognizer, d as PaintRecorder, da as defaultShadows, di as auto, dn as isPaintVisible, do as MOUSE_POINTER, dr as offsetAtPoint, dt as UiKeyboardController, ea as themeExtensionsEqual, ei as UiTransforms, en as bumpFontStack, eo as DirtyNodeSet, er as videoFrameSize, et as capturePointer, f as replayPaint, fa as shadowsEqual, fi as fr, fn as normalizeTextAlign, fo as UiBeforeInputEvent, fr as offsetAtX, ft as UiPointerController, g as hasDecorationPhase, ga as boxShadowRadius, gi as isPercentLength, gn as clearMatchRanges, go as UiGestureEvent, gr as graphemeBoundaries, gt as SCROLLBAR_INSET, h as decorationRect, ha as boxShadowArraysEqual, hi as isMinMaxTrack, hn as TEXT_MATCHES_PROP, ho as UiFocusEvent, hr as EditableTextModel, ht as SCROLLBAR_HOVER_ZONE, i as traceRoundedRect, ia as raiseContrast, ii as transformsEqual, in as DEFAULT_FONT_FAMILY, io as writeDeclaredProperty, ir as ELLIPSIS, it as touchActionFor, j as labelNode, ja as resolvedSpansOf, ji as TOUCH_TARGET, jn as isSubtreeInert, jr as closestPropertyName, jt as selectionRectsIn, k as formatExplanation, ka as flattenTextSpans, ki as defineProperty, kn as isNodeTabStop, ko as DirtyFlags, kr as resolvePropertyByName, kt as paragraphGeometry, l as PaintTarget, la as spacingEqual, li as radialGradient, ln as computeObjectFitRect, lo as visualState, lr as lineIndexForOffset, lt as UiWheelController, m as decorationColor, ma as boxShadow, mi as isFrLength, mn as resolvePaintState, mo as UiEventType, mr as selectionRects, mt as UiHitTester, n as createCanvasSurface, na as contrastRatio, ni as transform, nn as fontStackFor, no as overrideSources, nr as ParagraphTextMeasurer, nt as pointerDeviceOf, o as opacityNeedsLayer, oa as densityFactors, oi as gradientPaint, on as DEFAULT_FONT_WEIGHT, oo as UiVisualState, or as proportionalFontMetrics, ot as UiTouchScroller, p as ScaledImageCache, pa as BOX_SHADOW_BLUR_REACH, pi as isAutoLength, pn as normalizeVerticalAlign, po as UiCompositionEvent, pr as offsetForVerticalMove, pt as UiFocusManager, q as CrossAxisAlignment, qa as parseColor, qi as defaultUnits, qn as isMultiline, qr as validateRole, qt as layoutTextLines, r as Canvas2DRenderer, ra as highContrastColors, ri as transformIsIdentity, rn as registerFontStack, ro as resetOverrideWarnings, rr as spannedRunsFor, rt as prepareInputSurface, s as PaintPictureCache, sa as defaultSpacing, si as gradientsEqual, sn as DEFAULT_TEXT_COLOR, so as defaultVisualState, sr as caretRectFor, st as UiGestureRecognizer, t as CanvasSurface, ta as withThemeExtension, ti as parseTransform, tn as clearFontStacks, to as clearOverrideProperty, tr as CharacterCountTextMeasurer, tt as isImeKey, u as EMPTY_RECORDING, ua as spacingSteps, ui as validateGradient, un as createPaintState, uo as visualStatesEqual, ur as lineLimit, ut as isScrollContainer, v as LABEL_PADDING_X, va as boxShadowValuesEqual, vi as percent, vn as setMatchRanges, vo as UiKeyboardEvent, vr as lineStartAt, vt as SCROLLBAR_THICKNESS, w as SCROLLBAR_FADE_MS, wa as defaultTypography, wi as borderRadiusEqual, wn as setSelectionRange, wo as UiWheelDeltaMode, wr as UiGraph, wt as scrollbarZoneAt, x as snapScroll, xa as defaultShapes, xi as UiBorderRadiuses, xn as selectableTextNodes, xo as UiPointerEvent, xr as previousGraphemeStart, xt as scrollRange, y as drawOverlayShapes, ya as boxShadowsEqual, yi as repeat, yn as TEXT_SELECTION_PROP, yo as UiPasteEvent, yr as nextGraphemeEnd, yt as scrollBounds, z as tightenConstraints, za as darkColors, zi as bandOf, zn as setLinkHover, zr as propertyValuesEqualByName, zt as detectEditingPlatform } from "./CanvasSurface-BpgbOku2.js";
|
|
2
2
|
import { BehaviorSubject, EMPTY, Subject, combineLatest, distinctUntilChanged, isObservable as isObservable$1, map, of } from "rxjs";
|
|
3
3
|
//#region src/animation/UiEasing.ts
|
|
4
4
|
/** Progress equals time. What a spinner and a sweep want. */
|
|
@@ -2307,8 +2307,9 @@ var UiGraphBuilder = class {
|
|
|
2307
2307
|
* the fragment's children on each emission.
|
|
2308
2308
|
*/
|
|
2309
2309
|
reconcileChildren(parent, definitions) {
|
|
2310
|
+
const present = definitions.some(isNothing) ? definitions.filter((each) => !isNothing(each)) : definitions;
|
|
2310
2311
|
if (this.reconciling.has(parent)) {
|
|
2311
|
-
this.deferredChildren.set(parent,
|
|
2312
|
+
this.deferredChildren.set(parent, present);
|
|
2312
2313
|
return {
|
|
2313
2314
|
nodes: this.collectChildren(parent),
|
|
2314
2315
|
changed: false
|
|
@@ -2318,7 +2319,7 @@ var UiGraphBuilder = class {
|
|
|
2318
2319
|
this.reconcileDepth++;
|
|
2319
2320
|
let result;
|
|
2320
2321
|
try {
|
|
2321
|
-
result = this.reconcile(parent,
|
|
2322
|
+
result = this.reconcile(parent, present);
|
|
2322
2323
|
} finally {
|
|
2323
2324
|
this.reconcileDepth--;
|
|
2324
2325
|
this.reconciling.delete(parent);
|
|
@@ -2524,7 +2525,7 @@ var UiGraphBuilder = class {
|
|
|
2524
2525
|
matchNode(parent, definition, unkeyed, matched) {
|
|
2525
2526
|
const key = this.elementKey(definition);
|
|
2526
2527
|
if (key !== void 0) {
|
|
2527
|
-
const candidate = this.graph.getNode(`${parent.id}:${key}`);
|
|
2528
|
+
const candidate = this.graph.getNode(`${parent.id}:${idSegment(key)}`);
|
|
2528
2529
|
if (candidate === void 0 || candidate.parent !== parent || candidate.type !== definition.type) return;
|
|
2529
2530
|
if (matched.has(candidate)) throw new Error(`Duplicate key '${key}' in parent '${parent.id}'.`);
|
|
2530
2531
|
return candidate;
|
|
@@ -2552,8 +2553,13 @@ var UiGraphBuilder = class {
|
|
|
2552
2553
|
createNode(parent, definition, index) {
|
|
2553
2554
|
const id = this.createNodeId(parent, definition, index);
|
|
2554
2555
|
const node = this.graph.createNode(id, definition.type);
|
|
2555
|
-
|
|
2556
|
-
|
|
2556
|
+
try {
|
|
2557
|
+
this.reconcileProps(node, definition.props, parent);
|
|
2558
|
+
this.reconcileChildren(node, definition.children);
|
|
2559
|
+
} catch (error) {
|
|
2560
|
+
this.removeSubtree(node);
|
|
2561
|
+
throw error;
|
|
2562
|
+
}
|
|
2557
2563
|
return node;
|
|
2558
2564
|
}
|
|
2559
2565
|
/**
|
|
@@ -2583,7 +2589,7 @@ var UiGraphBuilder = class {
|
|
|
2583
2589
|
if (isObservable(definition) || isComponentLikeElement(definition)) continue;
|
|
2584
2590
|
const key = this.elementKey(definition);
|
|
2585
2591
|
if (key === void 0) continue;
|
|
2586
|
-
const candidate = this.graph.getNode(`${parent.id}:${key}`);
|
|
2592
|
+
const candidate = this.graph.getNode(`${parent.id}:${idSegment(key)}`);
|
|
2587
2593
|
const position = candidate === void 0 ? void 0 : at.get(candidate);
|
|
2588
2594
|
if (candidate === void 0 || position === void 0 || candidate.type !== definition.type) continue;
|
|
2589
2595
|
nodes.push(candidate);
|
|
@@ -2731,6 +2737,19 @@ var UiGraphBuilder = class {
|
|
|
2731
2737
|
return this.modifiers.get(node);
|
|
2732
2738
|
}
|
|
2733
2739
|
/**
|
|
2740
|
+
* Where the element a node was last reconciled from was written, for
|
|
2741
|
+
* the inspector and for tools that edit the source. For a component's
|
|
2742
|
+
* anchor it is where the component was used, so walking up from a
|
|
2743
|
+
* node gives the line that made it and then each place it was used.
|
|
2744
|
+
*
|
|
2745
|
+
* Read off the definition the builder already keeps for each node
|
|
2746
|
+
* rather than stored on the node, so it costs a tree nothing when no
|
|
2747
|
+
* element carries one, which is every element in production.
|
|
2748
|
+
*/
|
|
2749
|
+
sourceOf(node) {
|
|
2750
|
+
return this.reconciledFrom.get(node)?.source;
|
|
2751
|
+
}
|
|
2752
|
+
/**
|
|
2734
2753
|
* Rejects a prop nothing will ever read.
|
|
2735
2754
|
*
|
|
2736
2755
|
* Every property layout, paint, input or the environment consults is
|
|
@@ -2834,7 +2853,7 @@ var UiGraphBuilder = class {
|
|
|
2834
2853
|
*/
|
|
2835
2854
|
createNodeId(parent, definition, index) {
|
|
2836
2855
|
const key = this.elementKey(definition);
|
|
2837
|
-
if (key !== void 0) return `${parent.id}:${key}`;
|
|
2856
|
+
if (key !== void 0) return `${parent.id}:${idSegment(key)}`;
|
|
2838
2857
|
return `${parent.id}:${index}`;
|
|
2839
2858
|
}
|
|
2840
2859
|
/**
|
|
@@ -2853,7 +2872,7 @@ var UiGraphBuilder = class {
|
|
|
2853
2872
|
*/
|
|
2854
2873
|
createComponentAnchorId(parent, element, index) {
|
|
2855
2874
|
const key = element.key === void 0 || element.key === null ? index : element.key;
|
|
2856
|
-
return `${parent.id}:component:${String(key)}`;
|
|
2875
|
+
return `${parent.id}:component:${idSegment(String(key))}`;
|
|
2857
2876
|
}
|
|
2858
2877
|
};
|
|
2859
2878
|
const NO_NODES = /* @__PURE__ */ new Set();
|
|
@@ -2882,6 +2901,24 @@ function longestIncreasing(values) {
|
|
|
2882
2901
|
for (let index = tops.length > 0 ? tops[tops.length - 1] : -1; index !== -1; index = previous[index]) run.push(index);
|
|
2883
2902
|
return run.reverse();
|
|
2884
2903
|
}
|
|
2904
|
+
/**
|
|
2905
|
+
* A key as one segment of a node id, which joins segments with colons.
|
|
2906
|
+
*
|
|
2907
|
+
* A key holding a colon would otherwise read as more than one segment,
|
|
2908
|
+
* and could spell another node's id: a row keyed `a:0` beside a row
|
|
2909
|
+
* keyed `a` whose first child is `a:0` again, which threw "already used
|
|
2910
|
+
* outside parent" for a list keyed by ids (a layers panel's). Escaped,
|
|
2911
|
+
* a key is one segment whatever it holds, and a key with no colon in it
|
|
2912
|
+
* is unchanged.
|
|
2913
|
+
*/
|
|
2914
|
+
function idSegment(key) {
|
|
2915
|
+
const text = String(key);
|
|
2916
|
+
return text.includes(":") || text.includes("%") ? text.replace(/%/g, "%25").replace(/:/g, "%3A") : text;
|
|
2917
|
+
}
|
|
2918
|
+
/** A child that draws nothing: null, undefined or a boolean, from plain script. */
|
|
2919
|
+
function isNothing(child) {
|
|
2920
|
+
return child === null || child === void 0 || typeof child === "boolean";
|
|
2921
|
+
}
|
|
2885
2922
|
//#endregion
|
|
2886
2923
|
//#region src/composition/UiVirtualWindow.ts
|
|
2887
2924
|
/** Marks an item wrapper with its index so the host can measure it. */
|
|
@@ -4828,14 +4865,17 @@ var UiShortcutRegistry = class {
|
|
|
4828
4865
|
* `preventDefault()` so the keyboard controller's own defaults (Tab
|
|
4829
4866
|
* navigation, Enter on a button, an editable's typing) do not also
|
|
4830
4867
|
* act on a key that has already been spent.
|
|
4868
|
+
*
|
|
4869
|
+
* `code` is the physical key, which a digit written with Shift
|
|
4870
|
+
* matches on (see `sameStep`).
|
|
4831
4871
|
*/
|
|
4832
|
-
handleKey(key, modifiers, focused) {
|
|
4872
|
+
handleKey(key, modifiers, focused, code) {
|
|
4833
4873
|
if (MODIFIER_KEYS.has(key)) return false;
|
|
4834
4874
|
if (focused !== null && isTypingInto(focused) && this.isEditingKey(key, modifiers)) {
|
|
4835
4875
|
this.pending = [];
|
|
4836
4876
|
return false;
|
|
4837
4877
|
}
|
|
4838
|
-
const step = stepFor(key, modifiers);
|
|
4878
|
+
const step = stepFor(key, modifiers, code);
|
|
4839
4879
|
const at = this.now();
|
|
4840
4880
|
if (this.pending.length > 0 && at - this.pendingAt > this.chordTimeout) this.pending = [];
|
|
4841
4881
|
const sequence = [...this.pending, step];
|
|
@@ -5009,7 +5049,7 @@ function matchesPrefix(steps, sequence) {
|
|
|
5009
5049
|
return sequence.every((step, index) => sameStep(steps[index], step));
|
|
5010
5050
|
}
|
|
5011
5051
|
function sameStep(want, got) {
|
|
5012
|
-
if (want.key !== got.key) return false;
|
|
5052
|
+
if (want.key !== got.key && !shiftedDigit(want, got)) return false;
|
|
5013
5053
|
if (isSymbol(want.key)) {
|
|
5014
5054
|
if (isBare(want) && got.alt && !got.meta) return true;
|
|
5015
5055
|
} else if (want.shift !== got.shift) return false;
|
|
@@ -5018,6 +5058,15 @@ function sameStep(want, got) {
|
|
|
5018
5058
|
return want.ctrl === got.ctrl && want.meta === got.meta;
|
|
5019
5059
|
}
|
|
5020
5060
|
/**
|
|
5061
|
+
* A digit written with Shift, pressed: `Shift+1`, as a design tool's
|
|
5062
|
+
* "zoom to fit" is written, is the 1 key with Shift held, whatever
|
|
5063
|
+
* character that types. On a US keyboard it types `!`, on a French one
|
|
5064
|
+
* `1` itself, so the character cannot say; the physical key can.
|
|
5065
|
+
*/
|
|
5066
|
+
function shiftedDigit(want, got) {
|
|
5067
|
+
return want.shift && got.shift && /^[0-9]$/.test(want.key) && got.code === `Digit${want.key}`;
|
|
5068
|
+
}
|
|
5069
|
+
/**
|
|
5021
5070
|
* Whether a key is a character that isn't a letter: punctuation, a digit
|
|
5022
5071
|
* or a symbol, which different layouts put under different keys and
|
|
5023
5072
|
* reach with or without Shift. A letter keeps its Shift, which is what
|
|
@@ -5027,14 +5076,15 @@ function isSymbol(key) {
|
|
|
5027
5076
|
return key.length === 1 && key !== " " && key.toLowerCase() === key.toUpperCase();
|
|
5028
5077
|
}
|
|
5029
5078
|
/** The press the person actually made. */
|
|
5030
|
-
function stepFor(key, modifiers) {
|
|
5079
|
+
function stepFor(key, modifiers, code) {
|
|
5031
5080
|
return {
|
|
5032
5081
|
key: normalizeKey(key),
|
|
5033
5082
|
mod: false,
|
|
5034
5083
|
ctrl: modifiers.ctrl,
|
|
5035
5084
|
meta: modifiers.meta,
|
|
5036
5085
|
shift: modifiers.shift,
|
|
5037
|
-
alt: modifiers.alt
|
|
5086
|
+
alt: modifiers.alt,
|
|
5087
|
+
...code === void 0 ? {} : { code }
|
|
5038
5088
|
};
|
|
5039
5089
|
}
|
|
5040
5090
|
/**
|
|
@@ -6526,7 +6576,7 @@ var Pinchable = class {
|
|
|
6526
6576
|
if (box === null) return;
|
|
6527
6577
|
const clamped = Math.min(Math.max(scale, this.options.minScale ?? 1), this.options.maxScale ?? 8);
|
|
6528
6578
|
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);
|
|
6579
|
+
const [reappliedX, reappliedY] = apply$1(unrotatedX, unrotatedY, clamped, rotation);
|
|
6530
6580
|
this.scale = clamped;
|
|
6531
6581
|
this.rotation = rotation;
|
|
6532
6582
|
this.translateX = anchorX + panX - box.x - reappliedX;
|
|
@@ -6547,7 +6597,7 @@ var Pinchable = class {
|
|
|
6547
6597
|
}
|
|
6548
6598
|
};
|
|
6549
6599
|
/** Rotates and scales a node-local point, the way the renderers do. */
|
|
6550
|
-
function apply(x, y, scale, degrees) {
|
|
6600
|
+
function apply$1(x, y, scale, degrees) {
|
|
6551
6601
|
const radians = degrees * Math.PI / 180;
|
|
6552
6602
|
const cos = Math.cos(radians);
|
|
6553
6603
|
const sin = Math.sin(radians);
|
|
@@ -6596,7 +6646,7 @@ const shortcutsKind = defineModifier({
|
|
|
6596
6646
|
host.onRoot("keydown", (event) => {
|
|
6597
6647
|
if (event.defaultPrevented) return;
|
|
6598
6648
|
const key = event;
|
|
6599
|
-
if (registry.handleKey(key.key, key.modifiers, key.target)) {
|
|
6649
|
+
if (registry.handleKey(key.key, key.modifiers, key.target, key.code)) {
|
|
6600
6650
|
event.preventDefault();
|
|
6601
6651
|
event.stopPropagation();
|
|
6602
6652
|
}
|
|
@@ -6861,6 +6911,1129 @@ function numberOr(value, fallback) {
|
|
|
6861
6911
|
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
6862
6912
|
}
|
|
6863
6913
|
//#endregion
|
|
6914
|
+
//#region src/rendering/svg/SvgXml.ts
|
|
6915
|
+
/**
|
|
6916
|
+
* The document's outermost element, or null when there is none.
|
|
6917
|
+
*/
|
|
6918
|
+
function parseSvgXml(source) {
|
|
6919
|
+
const stack = [];
|
|
6920
|
+
let root = null;
|
|
6921
|
+
let index = 0;
|
|
6922
|
+
const close = () => {
|
|
6923
|
+
const element = stack.pop();
|
|
6924
|
+
const parent = stack.at(-1);
|
|
6925
|
+
if (parent === void 0) root ??= element;
|
|
6926
|
+
else parent.children.push(element);
|
|
6927
|
+
};
|
|
6928
|
+
while (index < source.length) {
|
|
6929
|
+
const open = source.indexOf("<", index);
|
|
6930
|
+
if (open === -1) break;
|
|
6931
|
+
if (open > index && stack.length > 0) stack.at(-1).text += decodeEntities(source.slice(index, open));
|
|
6932
|
+
if (source.startsWith("<!--", open)) {
|
|
6933
|
+
const end = source.indexOf("-->", open + 4);
|
|
6934
|
+
index = end === -1 ? source.length : end + 3;
|
|
6935
|
+
continue;
|
|
6936
|
+
}
|
|
6937
|
+
if (source.startsWith("<![CDATA[", open)) {
|
|
6938
|
+
const end = source.indexOf("]]>", open + 9);
|
|
6939
|
+
const text = source.slice(open + 9, end === -1 ? source.length : end);
|
|
6940
|
+
if (stack.length > 0) stack.at(-1).text += text;
|
|
6941
|
+
index = end === -1 ? source.length : end + 3;
|
|
6942
|
+
continue;
|
|
6943
|
+
}
|
|
6944
|
+
if (source[open + 1] === "?" || source[open + 1] === "!") {
|
|
6945
|
+
index = skipDeclaration(source, open);
|
|
6946
|
+
continue;
|
|
6947
|
+
}
|
|
6948
|
+
if (source[open + 1] === "/") {
|
|
6949
|
+
const end = source.indexOf(">", open);
|
|
6950
|
+
const name = source.slice(open + 2, end === -1 ? source.length : end).trim();
|
|
6951
|
+
const at = stack.map((element) => element.name).lastIndexOf(name);
|
|
6952
|
+
if (at !== -1) while (stack.length > at) close();
|
|
6953
|
+
index = end === -1 ? source.length : end + 1;
|
|
6954
|
+
continue;
|
|
6955
|
+
}
|
|
6956
|
+
const tag = readTag(source, open);
|
|
6957
|
+
stack.push({
|
|
6958
|
+
name: tag.name,
|
|
6959
|
+
attributes: tag.attributes,
|
|
6960
|
+
children: [],
|
|
6961
|
+
text: ""
|
|
6962
|
+
});
|
|
6963
|
+
if (tag.selfClosing) close();
|
|
6964
|
+
index = tag.end;
|
|
6965
|
+
}
|
|
6966
|
+
while (stack.length > 0) close();
|
|
6967
|
+
return root;
|
|
6968
|
+
}
|
|
6969
|
+
/** Past a `<?…?>` or a `<!…>`, minding a doctype's bracketed subset. */
|
|
6970
|
+
function skipDeclaration(source, open) {
|
|
6971
|
+
let depth = 0;
|
|
6972
|
+
for (let i = open + 1; i < source.length; i++) {
|
|
6973
|
+
const char = source[i];
|
|
6974
|
+
if (char === "[") depth++;
|
|
6975
|
+
else if (char === "]") depth--;
|
|
6976
|
+
else if (char === ">" && depth <= 0) return i + 1;
|
|
6977
|
+
}
|
|
6978
|
+
return source.length;
|
|
6979
|
+
}
|
|
6980
|
+
function readTag(source, open) {
|
|
6981
|
+
let i = open + 1;
|
|
6982
|
+
const nameStart = i;
|
|
6983
|
+
while (i < source.length && !/[\s/>]/.test(source[i])) i++;
|
|
6984
|
+
const name = source.slice(nameStart, i);
|
|
6985
|
+
const attributes = {};
|
|
6986
|
+
for (;;) {
|
|
6987
|
+
while (i < source.length && /\s/.test(source[i])) i++;
|
|
6988
|
+
if (i >= source.length) return {
|
|
6989
|
+
name,
|
|
6990
|
+
attributes,
|
|
6991
|
+
selfClosing: false,
|
|
6992
|
+
end: i
|
|
6993
|
+
};
|
|
6994
|
+
if (source[i] === ">") return {
|
|
6995
|
+
name,
|
|
6996
|
+
attributes,
|
|
6997
|
+
selfClosing: false,
|
|
6998
|
+
end: i + 1
|
|
6999
|
+
};
|
|
7000
|
+
if (source[i] === "/") {
|
|
7001
|
+
const end = source.indexOf(">", i);
|
|
7002
|
+
return {
|
|
7003
|
+
name,
|
|
7004
|
+
attributes,
|
|
7005
|
+
selfClosing: true,
|
|
7006
|
+
end: end === -1 ? source.length : end + 1
|
|
7007
|
+
};
|
|
7008
|
+
}
|
|
7009
|
+
const keyStart = i;
|
|
7010
|
+
while (i < source.length && !/[\s=/>]/.test(source[i])) i++;
|
|
7011
|
+
const key = source.slice(keyStart, i);
|
|
7012
|
+
while (i < source.length && /\s/.test(source[i])) i++;
|
|
7013
|
+
if (source[i] !== "=") {
|
|
7014
|
+
if (key !== "") attributes[key] = "";
|
|
7015
|
+
else i++;
|
|
7016
|
+
continue;
|
|
7017
|
+
}
|
|
7018
|
+
i++;
|
|
7019
|
+
while (i < source.length && /\s/.test(source[i])) i++;
|
|
7020
|
+
const quote = source[i];
|
|
7021
|
+
if (quote === "\"" || quote === "'") {
|
|
7022
|
+
const end = source.indexOf(quote, i + 1);
|
|
7023
|
+
attributes[key] = decodeEntities(source.slice(i + 1, end === -1 ? source.length : end));
|
|
7024
|
+
i = end === -1 ? source.length : end + 1;
|
|
7025
|
+
} else {
|
|
7026
|
+
const valueStart = i;
|
|
7027
|
+
while (i < source.length && !/[\s>]/.test(source[i])) i++;
|
|
7028
|
+
attributes[key] = decodeEntities(source.slice(valueStart, i));
|
|
7029
|
+
}
|
|
7030
|
+
}
|
|
7031
|
+
}
|
|
7032
|
+
const NAMED_ENTITIES = {
|
|
7033
|
+
amp: "&",
|
|
7034
|
+
lt: "<",
|
|
7035
|
+
gt: ">",
|
|
7036
|
+
quot: "\"",
|
|
7037
|
+
apos: "'"
|
|
7038
|
+
};
|
|
7039
|
+
function decodeEntities(text) {
|
|
7040
|
+
if (!text.includes("&")) return text;
|
|
7041
|
+
return text.replace(/&(#x[0-9a-f]+|#\d+|[a-z]+);/gi, (whole, entity) => {
|
|
7042
|
+
if (entity[0] === "#") {
|
|
7043
|
+
const code = entity[1] === "x" || entity[1] === "X" ? parseInt(entity.slice(2), 16) : parseInt(entity.slice(1), 10);
|
|
7044
|
+
return Number.isFinite(code) ? String.fromCodePoint(code) : whole;
|
|
7045
|
+
}
|
|
7046
|
+
return NAMED_ENTITIES[entity] ?? whole;
|
|
7047
|
+
});
|
|
7048
|
+
}
|
|
7049
|
+
//#endregion
|
|
7050
|
+
//#region src/rendering/svg/SvgDocument.ts
|
|
7051
|
+
/** Elements drawn as nothing, because nothing here draws them yet. */
|
|
7052
|
+
const UNSUPPORTED_ELEMENTS = /* @__PURE__ */ new Set([
|
|
7053
|
+
"text",
|
|
7054
|
+
"image",
|
|
7055
|
+
"foreignObject",
|
|
7056
|
+
"mask",
|
|
7057
|
+
"filter",
|
|
7058
|
+
"pattern",
|
|
7059
|
+
"marker"
|
|
7060
|
+
]);
|
|
7061
|
+
/** Attributes that point at one of those. */
|
|
7062
|
+
const UNSUPPORTED_ATTRIBUTES = {
|
|
7063
|
+
mask: "mask",
|
|
7064
|
+
filter: "filter",
|
|
7065
|
+
"marker-start": "marker",
|
|
7066
|
+
"marker-mid": "marker",
|
|
7067
|
+
"marker-end": "marker"
|
|
7068
|
+
};
|
|
7069
|
+
/**
|
|
7070
|
+
* Reads an SVG file. Throws when it holds no `<svg>` element, since
|
|
7071
|
+
* there is then nothing to draw and the caller should fall back.
|
|
7072
|
+
*/
|
|
7073
|
+
function parseSvg(source) {
|
|
7074
|
+
const root = parseSvgXml(source);
|
|
7075
|
+
if (root === null || localName(root.name) !== "svg") throw new Error("This is not an SVG file: it has no <svg> element.");
|
|
7076
|
+
const ids = /* @__PURE__ */ new Map();
|
|
7077
|
+
const rules = [];
|
|
7078
|
+
const unsupported = /* @__PURE__ */ new Set();
|
|
7079
|
+
const visit = (element) => {
|
|
7080
|
+
const name = localName(element.name);
|
|
7081
|
+
const id = element.attributes.id;
|
|
7082
|
+
if (id !== void 0 && !ids.has(id)) ids.set(id, element);
|
|
7083
|
+
if (name === "style") rules.push(...parseStyleSheet(element.text));
|
|
7084
|
+
if (UNSUPPORTED_ELEMENTS.has(name)) unsupported.add(name);
|
|
7085
|
+
for (const [attribute, feature] of Object.entries(UNSUPPORTED_ATTRIBUTES)) if (element.attributes[attribute] !== void 0 && element.attributes[attribute] !== "none") unsupported.add(feature);
|
|
7086
|
+
element.children.forEach(visit);
|
|
7087
|
+
};
|
|
7088
|
+
visit(root);
|
|
7089
|
+
const viewBox = parseViewBox(root.attributes.viewBox);
|
|
7090
|
+
const width = absoluteLength(root.attributes.width) ?? viewBox?.width ?? 300;
|
|
7091
|
+
const height = absoluteLength(root.attributes.height) ?? viewBox?.height ?? 150;
|
|
7092
|
+
return {
|
|
7093
|
+
root,
|
|
7094
|
+
width,
|
|
7095
|
+
height,
|
|
7096
|
+
viewBox: viewBox ?? {
|
|
7097
|
+
x: 0,
|
|
7098
|
+
y: 0,
|
|
7099
|
+
width,
|
|
7100
|
+
height
|
|
7101
|
+
},
|
|
7102
|
+
ids,
|
|
7103
|
+
rules,
|
|
7104
|
+
unsupported: [...unsupported]
|
|
7105
|
+
};
|
|
7106
|
+
}
|
|
7107
|
+
/** An element's name without its namespace prefix: `svg:path` is `path`. */
|
|
7108
|
+
function localName(name) {
|
|
7109
|
+
const colon = name.indexOf(":");
|
|
7110
|
+
return colon === -1 ? name : name.slice(colon + 1);
|
|
7111
|
+
}
|
|
7112
|
+
/** A `viewBox`, or undefined when it is absent or describes nothing. */
|
|
7113
|
+
function parseViewBox(value) {
|
|
7114
|
+
if (value === void 0) return;
|
|
7115
|
+
const [x, y, width, height] = numbersIn(value);
|
|
7116
|
+
if (x === void 0 || y === void 0 || width === void 0 || height === void 0 || width <= 0 || height <= 0) return;
|
|
7117
|
+
return {
|
|
7118
|
+
x,
|
|
7119
|
+
y,
|
|
7120
|
+
width,
|
|
7121
|
+
height
|
|
7122
|
+
};
|
|
7123
|
+
}
|
|
7124
|
+
/** Every number in a list separated by commas and spaces. */
|
|
7125
|
+
function numbersIn(value) {
|
|
7126
|
+
return (value.match(/[-+]?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?/gi) ?? []).map(Number);
|
|
7127
|
+
}
|
|
7128
|
+
/** A length in pixels, or undefined for a percentage or anything that is not one. */
|
|
7129
|
+
function absoluteLength(value) {
|
|
7130
|
+
if (value === void 0 || value.trim().endsWith("%")) return;
|
|
7131
|
+
const number = parseFloat(value);
|
|
7132
|
+
if (!Number.isFinite(number) || number <= 0) return;
|
|
7133
|
+
const unit = value.trim().replace(/^[-+\d.e]+/i, "");
|
|
7134
|
+
return number * (UNIT_PIXELS[unit] ?? 1);
|
|
7135
|
+
}
|
|
7136
|
+
/** CSS's absolute units in pixels, 96 to the inch. */
|
|
7137
|
+
const UNIT_PIXELS = {
|
|
7138
|
+
"": 1,
|
|
7139
|
+
px: 1,
|
|
7140
|
+
pt: 96 / 72,
|
|
7141
|
+
pc: 16,
|
|
7142
|
+
in: 96,
|
|
7143
|
+
cm: 96 / 2.54,
|
|
7144
|
+
mm: 96 / 25.4
|
|
7145
|
+
};
|
|
7146
|
+
/** The declarations of an inline `style` attribute or a rule's block. */
|
|
7147
|
+
function parseDeclarations(text) {
|
|
7148
|
+
const declarations = {};
|
|
7149
|
+
for (const part of text.split(";")) {
|
|
7150
|
+
const colon = part.indexOf(":");
|
|
7151
|
+
if (colon === -1) continue;
|
|
7152
|
+
const property = part.slice(0, colon).trim().toLowerCase();
|
|
7153
|
+
const value = part.slice(colon + 1).replace(/!important/i, "").trim();
|
|
7154
|
+
if (property !== "" && value !== "") declarations[property] = value;
|
|
7155
|
+
}
|
|
7156
|
+
return declarations;
|
|
7157
|
+
}
|
|
7158
|
+
/**
|
|
7159
|
+
* A `<style>`'s rules. Only compound selectors are understood, which is
|
|
7160
|
+
* what an exporter writes (`.st0{fill:#fff}`); a rule whose selector
|
|
7161
|
+
* has a combinator or a pseudo-class is skipped rather than misapplied.
|
|
7162
|
+
*/
|
|
7163
|
+
function parseStyleSheet(text) {
|
|
7164
|
+
const rules = [];
|
|
7165
|
+
const source = text.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
7166
|
+
for (const match of source.matchAll(/([^{}]+)\{([^}]*)\}/g)) {
|
|
7167
|
+
const declarations = parseDeclarations(match[2]);
|
|
7168
|
+
for (const raw of match[1].split(",")) {
|
|
7169
|
+
const selector = parseSelector(raw.trim());
|
|
7170
|
+
if (selector !== null) {
|
|
7171
|
+
const specificity = (selector.id === void 0 ? 0 : 100) + selector.classes.length * 10 + (selector.name === void 0 ? 0 : 1);
|
|
7172
|
+
rules.push({
|
|
7173
|
+
selector,
|
|
7174
|
+
declarations,
|
|
7175
|
+
specificity
|
|
7176
|
+
});
|
|
7177
|
+
}
|
|
7178
|
+
}
|
|
7179
|
+
}
|
|
7180
|
+
return rules;
|
|
7181
|
+
}
|
|
7182
|
+
function parseSelector(text) {
|
|
7183
|
+
if (text === "" || /[\s>+~:[\]*]/.test(text)) return null;
|
|
7184
|
+
const parts = text.match(/[.#]?[^.#]+/g) ?? [];
|
|
7185
|
+
let name;
|
|
7186
|
+
let id;
|
|
7187
|
+
const classes = [];
|
|
7188
|
+
for (const part of parts) if (part.startsWith("#")) id = part.slice(1);
|
|
7189
|
+
else if (part.startsWith(".")) classes.push(part.slice(1));
|
|
7190
|
+
else name = part;
|
|
7191
|
+
return {
|
|
7192
|
+
...name === void 0 ? {} : { name },
|
|
7193
|
+
...id === void 0 ? {} : { id },
|
|
7194
|
+
classes
|
|
7195
|
+
};
|
|
7196
|
+
}
|
|
7197
|
+
/** Whether a rule's selector matches an element. */
|
|
7198
|
+
function selectorMatches(selector, element) {
|
|
7199
|
+
if (selector.name !== void 0 && localName(element.name) !== selector.name) return false;
|
|
7200
|
+
if (selector.id !== void 0 && element.attributes.id !== selector.id) return false;
|
|
7201
|
+
if (selector.classes.length > 0) {
|
|
7202
|
+
const classes = (element.attributes.class ?? "").split(/\s+/);
|
|
7203
|
+
return selector.classes.every((name) => classes.includes(name));
|
|
7204
|
+
}
|
|
7205
|
+
return true;
|
|
7206
|
+
}
|
|
7207
|
+
//#endregion
|
|
7208
|
+
//#region src/rendering/svg/SvgDraw.ts
|
|
7209
|
+
/**
|
|
7210
|
+
* Draws a read SVG file into a box, through the surface every painted
|
|
7211
|
+
* node draws through, so it is vectors on either renderer and is drawn
|
|
7212
|
+
* again at whatever size the box is laid out at.
|
|
7213
|
+
*/
|
|
7214
|
+
function drawSvg(document, surface, box, options = {}) {
|
|
7215
|
+
const { viewBox } = document;
|
|
7216
|
+
const fit = options.fit ?? "contain";
|
|
7217
|
+
let scaleX = box.width / viewBox.width;
|
|
7218
|
+
let scaleY = box.height / viewBox.height;
|
|
7219
|
+
if (fit !== "fill") {
|
|
7220
|
+
const contain = Math.min(scaleX, scaleY);
|
|
7221
|
+
const natural = document.width / viewBox.width;
|
|
7222
|
+
const uniform = fit === "cover" ? Math.max(scaleX, scaleY) : fit === "none" ? natural : fit === "scale-down" ? Math.min(contain, natural) : contain;
|
|
7223
|
+
scaleX = uniform;
|
|
7224
|
+
scaleY = uniform;
|
|
7225
|
+
}
|
|
7226
|
+
surface.save();
|
|
7227
|
+
if (fit === "cover" || fit === "none") {
|
|
7228
|
+
surface.beginPath();
|
|
7229
|
+
surface.rect(0, 0, box.width, box.height);
|
|
7230
|
+
surface.clip();
|
|
7231
|
+
}
|
|
7232
|
+
surface.translate((box.width - viewBox.width * scaleX) / 2, (box.height - viewBox.height * scaleY) / 2);
|
|
7233
|
+
surface.scale(scaleX, scaleY);
|
|
7234
|
+
surface.translate(-viewBox.x, -viewBox.y);
|
|
7235
|
+
const context = {
|
|
7236
|
+
document,
|
|
7237
|
+
surface,
|
|
7238
|
+
currentColor: options.color ?? "currentColor",
|
|
7239
|
+
using: /* @__PURE__ */ new Set()
|
|
7240
|
+
};
|
|
7241
|
+
drawChildren(document.root, cascade(INITIAL_STYLE, declarationsOf(document.root, document), context), viewBox, context);
|
|
7242
|
+
surface.restore();
|
|
7243
|
+
}
|
|
7244
|
+
const paints = /* @__PURE__ */ new WeakMap();
|
|
7245
|
+
/**
|
|
7246
|
+
* A read SVG file as a node's `paint`: drawn to fill the node, and the
|
|
7247
|
+
* file's own size as the node's when the layout gives it none.
|
|
7248
|
+
*
|
|
7249
|
+
* The same object for the same file and options, so a node given it
|
|
7250
|
+
* again is not drawn again; see `paintValuesEqual`.
|
|
7251
|
+
*/
|
|
7252
|
+
function svgPaint(document, options = {}) {
|
|
7253
|
+
let byOptions = paints.get(document);
|
|
7254
|
+
if (byOptions === void 0) {
|
|
7255
|
+
byOptions = /* @__PURE__ */ new Map();
|
|
7256
|
+
paints.set(document, byOptions);
|
|
7257
|
+
}
|
|
7258
|
+
const key = JSON.stringify([options.fit ?? "contain", options.color ?? "currentColor"]);
|
|
7259
|
+
let paint = byOptions.get(key);
|
|
7260
|
+
if (paint === void 0) {
|
|
7261
|
+
paint = {
|
|
7262
|
+
draw: (surface, box) => drawSvg(document, surface, box, options),
|
|
7263
|
+
inputs: [document, key],
|
|
7264
|
+
intrinsicWidth: document.width,
|
|
7265
|
+
intrinsicHeight: document.height
|
|
7266
|
+
};
|
|
7267
|
+
byOptions.set(key, paint);
|
|
7268
|
+
}
|
|
7269
|
+
return paint;
|
|
7270
|
+
}
|
|
7271
|
+
const BLACK = {
|
|
7272
|
+
kind: "color",
|
|
7273
|
+
color: {
|
|
7274
|
+
r: 0,
|
|
7275
|
+
g: 0,
|
|
7276
|
+
b: 0,
|
|
7277
|
+
a: 1
|
|
7278
|
+
}
|
|
7279
|
+
};
|
|
7280
|
+
const NONE = { kind: "none" };
|
|
7281
|
+
const INITIAL_STYLE = {
|
|
7282
|
+
fill: BLACK,
|
|
7283
|
+
fillOpacity: 1,
|
|
7284
|
+
fillRule: "nonzero",
|
|
7285
|
+
stroke: NONE,
|
|
7286
|
+
strokeOpacity: 1,
|
|
7287
|
+
strokeWidth: 1,
|
|
7288
|
+
lineCap: "butt",
|
|
7289
|
+
lineJoin: "miter",
|
|
7290
|
+
miterLimit: 4,
|
|
7291
|
+
dash: [],
|
|
7292
|
+
dashOffset: 0,
|
|
7293
|
+
clipRule: "nonzero",
|
|
7294
|
+
color: null,
|
|
7295
|
+
visible: true
|
|
7296
|
+
};
|
|
7297
|
+
/** Drawn only when something points at them, or not at all. */
|
|
7298
|
+
const NOT_DRAWN = /* @__PURE__ */ new Set([
|
|
7299
|
+
"defs",
|
|
7300
|
+
"clipPath",
|
|
7301
|
+
"linearGradient",
|
|
7302
|
+
"radialGradient",
|
|
7303
|
+
"symbol",
|
|
7304
|
+
"style",
|
|
7305
|
+
"title",
|
|
7306
|
+
"desc",
|
|
7307
|
+
"metadata",
|
|
7308
|
+
"mask",
|
|
7309
|
+
"filter",
|
|
7310
|
+
"pattern",
|
|
7311
|
+
"marker",
|
|
7312
|
+
"text",
|
|
7313
|
+
"image",
|
|
7314
|
+
"foreignObject"
|
|
7315
|
+
]);
|
|
7316
|
+
function drawChildren(element, style, viewport, context) {
|
|
7317
|
+
for (const child of element.children) drawElement(child, style, viewport, context);
|
|
7318
|
+
}
|
|
7319
|
+
function drawElement(element, inherited, viewport, context) {
|
|
7320
|
+
const name = localName(element.name);
|
|
7321
|
+
if (NOT_DRAWN.has(name)) return;
|
|
7322
|
+
const declared = declarationsOf(element, context.document);
|
|
7323
|
+
if (declared.display === "none") return;
|
|
7324
|
+
const style = cascade(inherited, declared, context);
|
|
7325
|
+
const { surface } = context;
|
|
7326
|
+
surface.save();
|
|
7327
|
+
if (declared.transform !== void 0) applyTransform$1(surface, declared.transform);
|
|
7328
|
+
const opacity = unit(declared.opacity);
|
|
7329
|
+
if (opacity < 1) surface.alpha(opacity);
|
|
7330
|
+
if (declared["clip-path"] !== void 0) clipTo(declared["clip-path"], viewport, context);
|
|
7331
|
+
switch (name) {
|
|
7332
|
+
case "g":
|
|
7333
|
+
case "a":
|
|
7334
|
+
case "switch":
|
|
7335
|
+
drawChildren(element, style, viewport, context);
|
|
7336
|
+
break;
|
|
7337
|
+
case "svg":
|
|
7338
|
+
drawNested(element, style, viewport, context);
|
|
7339
|
+
break;
|
|
7340
|
+
case "use":
|
|
7341
|
+
drawUse(element, style, viewport, context);
|
|
7342
|
+
break;
|
|
7343
|
+
default: {
|
|
7344
|
+
const shape = shapeOf(element, name, viewport);
|
|
7345
|
+
if (shape !== null && style.visible) paintShape(shape, style, viewport, context);
|
|
7346
|
+
}
|
|
7347
|
+
}
|
|
7348
|
+
surface.restore();
|
|
7349
|
+
}
|
|
7350
|
+
/** A nested `<svg>`: a viewport of its own, at its x and y. */
|
|
7351
|
+
function drawNested(element, style, viewport, context) {
|
|
7352
|
+
const { surface } = context;
|
|
7353
|
+
const x = length(element.attributes.x, viewport.width);
|
|
7354
|
+
const y = length(element.attributes.y, viewport.height);
|
|
7355
|
+
const width = length(element.attributes.width ?? "100%", viewport.width);
|
|
7356
|
+
const height = length(element.attributes.height ?? "100%", viewport.height);
|
|
7357
|
+
const viewBox = parseViewBox(element.attributes.viewBox);
|
|
7358
|
+
surface.translate(x, y);
|
|
7359
|
+
if (viewBox !== void 0) {
|
|
7360
|
+
const scale = Math.min(width / viewBox.width, height / viewBox.height);
|
|
7361
|
+
surface.translate((width - viewBox.width * scale) / 2, (height - viewBox.height * scale) / 2);
|
|
7362
|
+
surface.scale(scale, scale);
|
|
7363
|
+
surface.translate(-viewBox.x, -viewBox.y);
|
|
7364
|
+
}
|
|
7365
|
+
drawChildren(element, style, viewBox ?? {
|
|
7366
|
+
x: 0,
|
|
7367
|
+
y: 0,
|
|
7368
|
+
width,
|
|
7369
|
+
height
|
|
7370
|
+
}, context);
|
|
7371
|
+
}
|
|
7372
|
+
/** A `<use>`: the element it names, drawn at its x and y, inheriting from the `<use>`. */
|
|
7373
|
+
function drawUse(element, style, viewport, context) {
|
|
7374
|
+
const target = referenced(element, context.document);
|
|
7375
|
+
if (target === void 0 || context.using.has(element)) return;
|
|
7376
|
+
const { surface } = context;
|
|
7377
|
+
surface.translate(length(element.attributes.x, viewport.width), length(element.attributes.y, viewport.height));
|
|
7378
|
+
context.using.add(element);
|
|
7379
|
+
if (localName(target.name) === "symbol") drawNested(target, style, viewport, context);
|
|
7380
|
+
else {
|
|
7381
|
+
const name = localName(target.name);
|
|
7382
|
+
if (name === "g" || name === "svg" || name === "use" || !NOT_DRAWN.has(name)) drawElement(target, style, viewport, context);
|
|
7383
|
+
}
|
|
7384
|
+
context.using.delete(element);
|
|
7385
|
+
}
|
|
7386
|
+
function referenced(element, document) {
|
|
7387
|
+
const href = element.attributes.href ?? element.attributes["xlink:href"];
|
|
7388
|
+
return href?.startsWith("#") ? document.ids.get(href.slice(1)) : void 0;
|
|
7389
|
+
}
|
|
7390
|
+
function shapeOf(element, name, viewport) {
|
|
7391
|
+
const a = element.attributes;
|
|
7392
|
+
const w = viewport.width;
|
|
7393
|
+
const h = viewport.height;
|
|
7394
|
+
const diagonal = Math.sqrt((w * w + h * h) / 2);
|
|
7395
|
+
switch (name) {
|
|
7396
|
+
case "path": {
|
|
7397
|
+
const d = a.d;
|
|
7398
|
+
return d === void 0 || d.trim() === "" ? null : { trace: (surface) => surface.path(d) };
|
|
7399
|
+
}
|
|
7400
|
+
case "rect": {
|
|
7401
|
+
const x = length(a.x, w);
|
|
7402
|
+
const y = length(a.y, h);
|
|
7403
|
+
const width = length(a.width, w);
|
|
7404
|
+
const height = length(a.height, h);
|
|
7405
|
+
if (width <= 0 || height <= 0) return null;
|
|
7406
|
+
const rx = a.rx === void 0 ? void 0 : length(a.rx, w);
|
|
7407
|
+
const ry = a.ry === void 0 ? void 0 : length(a.ry, h);
|
|
7408
|
+
const radius = Math.min(rx ?? ry ?? 0, ry ?? rx ?? 0);
|
|
7409
|
+
return { trace: (surface) => radius > 0 ? surface.roundRect(x, y, width, height, radius) : surface.rect(x, y, width, height) };
|
|
7410
|
+
}
|
|
7411
|
+
case "circle": return ellipse(length(a.cx, w), length(a.cy, h), length(a.r, diagonal), length(a.r, diagonal));
|
|
7412
|
+
case "ellipse": return ellipse(length(a.cx, w), length(a.cy, h), length(a.rx, w), length(a.ry, h));
|
|
7413
|
+
case "line": {
|
|
7414
|
+
const [x1, y1, x2, y2] = [
|
|
7415
|
+
length(a.x1, w),
|
|
7416
|
+
length(a.y1, h),
|
|
7417
|
+
length(a.x2, w),
|
|
7418
|
+
length(a.y2, h)
|
|
7419
|
+
];
|
|
7420
|
+
return { trace: (surface) => {
|
|
7421
|
+
surface.moveTo(x1, y1);
|
|
7422
|
+
surface.lineTo(x2, y2);
|
|
7423
|
+
} };
|
|
7424
|
+
}
|
|
7425
|
+
case "polyline":
|
|
7426
|
+
case "polygon": {
|
|
7427
|
+
const points = numbersIn(a.points ?? "");
|
|
7428
|
+
if (points.length < 4) return null;
|
|
7429
|
+
return { trace: (surface) => {
|
|
7430
|
+
surface.moveTo(points[0], points[1]);
|
|
7431
|
+
for (let i = 2; i + 1 < points.length; i += 2) surface.lineTo(points[i], points[i + 1]);
|
|
7432
|
+
if (name === "polygon") surface.closePath();
|
|
7433
|
+
} };
|
|
7434
|
+
}
|
|
7435
|
+
default: return null;
|
|
7436
|
+
}
|
|
7437
|
+
}
|
|
7438
|
+
/** An ellipse as two arcs of path data, which the surface turns into curves. */
|
|
7439
|
+
function ellipse(cx, cy, rx, ry) {
|
|
7440
|
+
if (rx <= 0 || ry <= 0) return null;
|
|
7441
|
+
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`;
|
|
7442
|
+
return { trace: (surface) => surface.path(d) };
|
|
7443
|
+
}
|
|
7444
|
+
function paintShape(shape, style, viewport, context) {
|
|
7445
|
+
const { surface } = context;
|
|
7446
|
+
surface.beginPath();
|
|
7447
|
+
shape.trace(surface);
|
|
7448
|
+
if (style.fill.kind !== "none" && style.fillOpacity > 0) {
|
|
7449
|
+
surface.save();
|
|
7450
|
+
if (style.fillOpacity < 1) surface.alpha(style.fillOpacity);
|
|
7451
|
+
if (setPaint(style.fill, "fill", shape, style, viewport, context)) surface.fill(style.fillRule);
|
|
7452
|
+
surface.restore();
|
|
7453
|
+
}
|
|
7454
|
+
if (style.stroke.kind !== "none" && style.strokeOpacity > 0 && style.strokeWidth > 0) {
|
|
7455
|
+
surface.save();
|
|
7456
|
+
if (style.strokeOpacity < 1) surface.alpha(style.strokeOpacity);
|
|
7457
|
+
if (setPaint(style.stroke, "stroke", shape, style, viewport, context)) {
|
|
7458
|
+
surface.lineWidth(style.strokeWidth);
|
|
7459
|
+
surface.lineCap(style.lineCap);
|
|
7460
|
+
surface.lineJoin(style.lineJoin);
|
|
7461
|
+
surface.miterLimit(style.miterLimit);
|
|
7462
|
+
if (style.dash.length > 0) surface.lineDash(style.dash, style.dashOffset);
|
|
7463
|
+
surface.stroke();
|
|
7464
|
+
}
|
|
7465
|
+
surface.restore();
|
|
7466
|
+
}
|
|
7467
|
+
}
|
|
7468
|
+
/**
|
|
7469
|
+
* Sets a fill or a stroke on the surface. False when there is nothing
|
|
7470
|
+
* to draw with: a gradient with no stops, or a reference to nothing and
|
|
7471
|
+
* no fallback.
|
|
7472
|
+
*
|
|
7473
|
+
* A gradient stroke is drawn in its last stop's colour, because the
|
|
7474
|
+
* surface strokes only in a colour.
|
|
7475
|
+
*/
|
|
7476
|
+
function setPaint(paint, role, shape, style, viewport, context) {
|
|
7477
|
+
if (paint.kind === "none") return false;
|
|
7478
|
+
if (paint.kind === "color") {
|
|
7479
|
+
if (role === "fill") context.surface.fillColor(paint.color);
|
|
7480
|
+
else context.surface.strokeColor(paint.color);
|
|
7481
|
+
return true;
|
|
7482
|
+
}
|
|
7483
|
+
const target = context.document.ids.get(paint.id);
|
|
7484
|
+
const gradient = target === void 0 ? void 0 : gradientOf(target, shape, style, viewport, context);
|
|
7485
|
+
if (gradient === void 0) return setPaint(paint.fallback, role, shape, style, viewport, context);
|
|
7486
|
+
if (gradient.stops.length === 0) return false;
|
|
7487
|
+
if (role === "stroke" || gradient.stops.length === 1) {
|
|
7488
|
+
const color = gradient.stops.at(-1).color;
|
|
7489
|
+
if (role === "fill") context.surface.fillColor(color);
|
|
7490
|
+
else context.surface.strokeColor(color);
|
|
7491
|
+
return true;
|
|
7492
|
+
}
|
|
7493
|
+
context.surface.fillGradient(gradient.paint, gradient.rect.x, gradient.rect.y, gradient.rect.width, gradient.rect.height);
|
|
7494
|
+
return true;
|
|
7495
|
+
}
|
|
7496
|
+
/**
|
|
7497
|
+
* An SVG gradient as Gesso's, and the rectangle to place it in.
|
|
7498
|
+
*
|
|
7499
|
+
* SVG gives a linear gradient as the two ends of its line and Gesso as
|
|
7500
|
+
* an angle across a rectangle, CSS's way, so the rectangle is chosen to
|
|
7501
|
+
* make the two agree: a square about the line's midpoint whose
|
|
7502
|
+
* gradient line, at that angle, is exactly as long as the SVG's. A
|
|
7503
|
+
* radial one is its centre and radius in pixels, placed at the origin.
|
|
7504
|
+
*/
|
|
7505
|
+
function gradientOf(element, shape, style, viewport, context) {
|
|
7506
|
+
const name = localName(element.name);
|
|
7507
|
+
if (name !== "linearGradient" && name !== "radialGradient") return;
|
|
7508
|
+
const attributes = gradientAttributes(element, context.document);
|
|
7509
|
+
const stops = gradientStops(element, style, context);
|
|
7510
|
+
const userSpace = attributes.gradientUnits === "userSpaceOnUse";
|
|
7511
|
+
const box = userSpace ? viewport : boundsOf(shape);
|
|
7512
|
+
const matrix = attributes.gradientTransform === void 0 ? IDENTITY$1 : parseTransform$1(attributes.gradientTransform);
|
|
7513
|
+
/** A point in the gradient's units, in the drawing's. */
|
|
7514
|
+
const place = (x, y, fallbackX, fallbackY) => {
|
|
7515
|
+
const u = gradientLength(x ?? fallbackX, userSpace, viewport.width);
|
|
7516
|
+
const v = gradientLength(y ?? fallbackY, userSpace, viewport.height);
|
|
7517
|
+
const [tx, ty] = apply(matrix, u, v);
|
|
7518
|
+
return userSpace ? [tx, ty] : [box.x + tx * box.width, box.y + ty * box.height];
|
|
7519
|
+
};
|
|
7520
|
+
if (name === "linearGradient") {
|
|
7521
|
+
const [x1, y1] = place(attributes.x1, attributes.y1, "0%", "0%");
|
|
7522
|
+
const [x2, y2] = place(attributes.x2, attributes.y2, "100%", "0%");
|
|
7523
|
+
const dx = x2 - x1;
|
|
7524
|
+
const dy = y2 - y1;
|
|
7525
|
+
const span = Math.hypot(dx, dy);
|
|
7526
|
+
if (span === 0) return {
|
|
7527
|
+
paint: {
|
|
7528
|
+
kind: "linear",
|
|
7529
|
+
angle: 0,
|
|
7530
|
+
stops: stops.slice(-1)
|
|
7531
|
+
},
|
|
7532
|
+
stops: stops.slice(-1),
|
|
7533
|
+
rect: box
|
|
7534
|
+
};
|
|
7535
|
+
const angle = Math.atan2(dx, -dy);
|
|
7536
|
+
const side = span / (Math.abs(Math.sin(angle)) + Math.abs(Math.cos(angle)));
|
|
7537
|
+
return {
|
|
7538
|
+
paint: {
|
|
7539
|
+
kind: "linear",
|
|
7540
|
+
angle,
|
|
7541
|
+
stops
|
|
7542
|
+
},
|
|
7543
|
+
stops,
|
|
7544
|
+
rect: {
|
|
7545
|
+
x: (x1 + x2) / 2 - side / 2,
|
|
7546
|
+
y: (y1 + y2) / 2 - side / 2,
|
|
7547
|
+
width: side,
|
|
7548
|
+
height: side
|
|
7549
|
+
}
|
|
7550
|
+
};
|
|
7551
|
+
}
|
|
7552
|
+
const [cx, cy] = place(attributes.cx, attributes.cy, "50%", "50%");
|
|
7553
|
+
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);
|
|
7554
|
+
if (radius <= 0) return {
|
|
7555
|
+
paint: {
|
|
7556
|
+
kind: "radial",
|
|
7557
|
+
stops: stops.slice(-1)
|
|
7558
|
+
},
|
|
7559
|
+
stops: stops.slice(-1),
|
|
7560
|
+
rect: box
|
|
7561
|
+
};
|
|
7562
|
+
return {
|
|
7563
|
+
paint: {
|
|
7564
|
+
kind: "radial",
|
|
7565
|
+
centerX: cx,
|
|
7566
|
+
centerY: cy,
|
|
7567
|
+
radius,
|
|
7568
|
+
stops
|
|
7569
|
+
},
|
|
7570
|
+
stops,
|
|
7571
|
+
rect: {
|
|
7572
|
+
x: 0,
|
|
7573
|
+
y: 0,
|
|
7574
|
+
width: 1,
|
|
7575
|
+
height: 1
|
|
7576
|
+
}
|
|
7577
|
+
};
|
|
7578
|
+
}
|
|
7579
|
+
/** A gradient's attributes, with the ones it inherits through `href` from the gradient it names. */
|
|
7580
|
+
function gradientAttributes(element, document) {
|
|
7581
|
+
const chain = [];
|
|
7582
|
+
for (let at = element; at !== void 0 && !chain.includes(at); at = referenced(at, document)) chain.push(at);
|
|
7583
|
+
return Object.assign({}, ...chain.reverse().map((link) => link.attributes));
|
|
7584
|
+
}
|
|
7585
|
+
/** Its stops, or those of the gradient it names when it has none. */
|
|
7586
|
+
function gradientStops(element, style, context) {
|
|
7587
|
+
const seen = [];
|
|
7588
|
+
let at = element;
|
|
7589
|
+
while (at !== void 0 && !seen.includes(at)) {
|
|
7590
|
+
seen.push(at);
|
|
7591
|
+
const stops = at.children.filter((child) => localName(child.name) === "stop");
|
|
7592
|
+
if (stops.length > 0) {
|
|
7593
|
+
let last = 0;
|
|
7594
|
+
return stops.map((stop) => {
|
|
7595
|
+
const declared = declarationsOf(stop, context.document);
|
|
7596
|
+
const raw = declared.offset ?? "0";
|
|
7597
|
+
const offset = Math.max(last, Math.min(1, raw.trim().endsWith("%") ? parseFloat(raw) / 100 : parseFloat(raw) || 0));
|
|
7598
|
+
last = offset;
|
|
7599
|
+
const color = colorValue(declared["stop-color"] ?? "black", style, context) ?? BLACK_COLOR;
|
|
7600
|
+
return {
|
|
7601
|
+
offset: percent(offset * 100),
|
|
7602
|
+
color: withOpacity(color, unit(declared["stop-opacity"]))
|
|
7603
|
+
};
|
|
7604
|
+
});
|
|
7605
|
+
}
|
|
7606
|
+
at = referenced(at, context.document);
|
|
7607
|
+
}
|
|
7608
|
+
return [];
|
|
7609
|
+
}
|
|
7610
|
+
const BLACK_COLOR = {
|
|
7611
|
+
r: 0,
|
|
7612
|
+
g: 0,
|
|
7613
|
+
b: 0,
|
|
7614
|
+
a: 1
|
|
7615
|
+
};
|
|
7616
|
+
function withOpacity(color, opacity) {
|
|
7617
|
+
if (opacity >= 1 || typeof color === "string") return color;
|
|
7618
|
+
return {
|
|
7619
|
+
...color,
|
|
7620
|
+
a: color.a * opacity
|
|
7621
|
+
};
|
|
7622
|
+
}
|
|
7623
|
+
/** A gradient coordinate: a fraction of the box, or pixels in user space. */
|
|
7624
|
+
function gradientLength(value, userSpace, extent) {
|
|
7625
|
+
const trimmed = value.trim();
|
|
7626
|
+
if (trimmed.endsWith("%")) {
|
|
7627
|
+
const fraction = parseFloat(trimmed) / 100;
|
|
7628
|
+
return userSpace ? fraction * extent : fraction;
|
|
7629
|
+
}
|
|
7630
|
+
return parseFloat(trimmed) || 0;
|
|
7631
|
+
}
|
|
7632
|
+
/** The box a shape's outline and control points lie in, for a gradient sized to it. */
|
|
7633
|
+
function boundsOf(shape) {
|
|
7634
|
+
const bounds = new BoundsSurface();
|
|
7635
|
+
shape.trace(bounds);
|
|
7636
|
+
return bounds.bounds();
|
|
7637
|
+
}
|
|
7638
|
+
/** A surface that only notes where a path goes. */
|
|
7639
|
+
var BoundsSurface = class {
|
|
7640
|
+
minX = Infinity;
|
|
7641
|
+
minY = Infinity;
|
|
7642
|
+
maxX = -Infinity;
|
|
7643
|
+
maxY = -Infinity;
|
|
7644
|
+
note(x, y) {
|
|
7645
|
+
this.minX = Math.min(this.minX, x);
|
|
7646
|
+
this.minY = Math.min(this.minY, y);
|
|
7647
|
+
this.maxX = Math.max(this.maxX, x);
|
|
7648
|
+
this.maxY = Math.max(this.maxY, y);
|
|
7649
|
+
}
|
|
7650
|
+
bounds() {
|
|
7651
|
+
if (this.minX > this.maxX) return {
|
|
7652
|
+
x: 0,
|
|
7653
|
+
y: 0,
|
|
7654
|
+
width: 0,
|
|
7655
|
+
height: 0
|
|
7656
|
+
};
|
|
7657
|
+
return {
|
|
7658
|
+
x: this.minX,
|
|
7659
|
+
y: this.minY,
|
|
7660
|
+
width: this.maxX - this.minX,
|
|
7661
|
+
height: this.maxY - this.minY
|
|
7662
|
+
};
|
|
7663
|
+
}
|
|
7664
|
+
moveTo(x, y) {
|
|
7665
|
+
this.note(x, y);
|
|
7666
|
+
}
|
|
7667
|
+
lineTo(x, y) {
|
|
7668
|
+
this.note(x, y);
|
|
7669
|
+
}
|
|
7670
|
+
quadraticCurveTo(cx, cy, x, y) {
|
|
7671
|
+
this.note(cx, cy);
|
|
7672
|
+
this.note(x, y);
|
|
7673
|
+
}
|
|
7674
|
+
bezierCurveTo(c1x, c1y, c2x, c2y, x, y) {
|
|
7675
|
+
this.note(c1x, c1y);
|
|
7676
|
+
this.note(c2x, c2y);
|
|
7677
|
+
this.note(x, y);
|
|
7678
|
+
}
|
|
7679
|
+
arc(x, y, radius) {
|
|
7680
|
+
this.note(x - radius, y - radius);
|
|
7681
|
+
this.note(x + radius, y + radius);
|
|
7682
|
+
}
|
|
7683
|
+
rect(x, y, width, height) {
|
|
7684
|
+
this.note(x, y);
|
|
7685
|
+
this.note(x + width, y + height);
|
|
7686
|
+
}
|
|
7687
|
+
roundRect(x, y, width, height) {
|
|
7688
|
+
this.rect(x, y, width, height);
|
|
7689
|
+
}
|
|
7690
|
+
closePath() {}
|
|
7691
|
+
path(d) {
|
|
7692
|
+
tracePathData(this, d);
|
|
7693
|
+
}
|
|
7694
|
+
};
|
|
7695
|
+
/** Intersects the clip with a `<clipPath>`'s shapes, in the drawing's own coordinates. */
|
|
7696
|
+
function clipTo(value, viewport, context) {
|
|
7697
|
+
const id = /url\(\s*['"]?#([^'")]+)['"]?\s*\)/.exec(value)?.[1];
|
|
7698
|
+
const clip = id === void 0 ? void 0 : context.document.ids.get(id);
|
|
7699
|
+
if (clip === void 0 || localName(clip.name) !== "clipPath") return;
|
|
7700
|
+
const { surface } = context;
|
|
7701
|
+
surface.beginPath();
|
|
7702
|
+
let rule = "nonzero";
|
|
7703
|
+
const add = (element) => {
|
|
7704
|
+
const name = localName(element.name);
|
|
7705
|
+
const declared = declarationsOf(element, context.document);
|
|
7706
|
+
if (name === "use") {
|
|
7707
|
+
const target = referenced(element, context.document);
|
|
7708
|
+
if (target !== void 0 && localName(target.name) !== "use") add(target);
|
|
7709
|
+
return;
|
|
7710
|
+
}
|
|
7711
|
+
if (name === "g") {
|
|
7712
|
+
element.children.forEach(add);
|
|
7713
|
+
return;
|
|
7714
|
+
}
|
|
7715
|
+
const shape = shapeOf(element, name, viewport);
|
|
7716
|
+
if (shape === null) return;
|
|
7717
|
+
if (declared["clip-rule"] === "evenodd") rule = "evenodd";
|
|
7718
|
+
if (declared.transform !== void 0) {
|
|
7719
|
+
const matrix = parseTransform$1(declared.transform);
|
|
7720
|
+
shape.trace(new TransformedSurface(surface, matrix));
|
|
7721
|
+
} else shape.trace(surface);
|
|
7722
|
+
};
|
|
7723
|
+
clip.children.forEach(add);
|
|
7724
|
+
surface.clip(rule);
|
|
7725
|
+
}
|
|
7726
|
+
/** Forwards path calls with every point through a matrix. */
|
|
7727
|
+
var TransformedSurface = class {
|
|
7728
|
+
target;
|
|
7729
|
+
matrix;
|
|
7730
|
+
constructor(target, matrix) {
|
|
7731
|
+
this.target = target;
|
|
7732
|
+
this.matrix = matrix;
|
|
7733
|
+
}
|
|
7734
|
+
at(x, y) {
|
|
7735
|
+
return apply(this.matrix, x, y);
|
|
7736
|
+
}
|
|
7737
|
+
moveTo(x, y) {
|
|
7738
|
+
this.target.moveTo(...this.at(x, y));
|
|
7739
|
+
}
|
|
7740
|
+
lineTo(x, y) {
|
|
7741
|
+
this.target.lineTo(...this.at(x, y));
|
|
7742
|
+
}
|
|
7743
|
+
quadraticCurveTo(cx, cy, x, y) {
|
|
7744
|
+
this.target.quadraticCurveTo(...this.at(cx, cy), ...this.at(x, y));
|
|
7745
|
+
}
|
|
7746
|
+
bezierCurveTo(c1x, c1y, c2x, c2y, x, y) {
|
|
7747
|
+
this.target.bezierCurveTo(...this.at(c1x, c1y), ...this.at(c2x, c2y), ...this.at(x, y));
|
|
7748
|
+
}
|
|
7749
|
+
rect(x, y, width, height) {
|
|
7750
|
+
this.moveTo(x, y);
|
|
7751
|
+
this.lineTo(x + width, y);
|
|
7752
|
+
this.lineTo(x + width, y + height);
|
|
7753
|
+
this.lineTo(x, y + height);
|
|
7754
|
+
this.closePath();
|
|
7755
|
+
}
|
|
7756
|
+
roundRect(x, y, width, height) {
|
|
7757
|
+
this.rect(x, y, width, height);
|
|
7758
|
+
}
|
|
7759
|
+
closePath() {
|
|
7760
|
+
this.target.closePath();
|
|
7761
|
+
}
|
|
7762
|
+
path(d) {
|
|
7763
|
+
tracePathData(this, d);
|
|
7764
|
+
}
|
|
7765
|
+
};
|
|
7766
|
+
/**
|
|
7767
|
+
* What an element declares, in the cascade's order: its presentation
|
|
7768
|
+
* attributes, then the `<style>` rules that match it by specificity,
|
|
7769
|
+
* then its own `style` attribute.
|
|
7770
|
+
*/
|
|
7771
|
+
function declarationsOf(element, document) {
|
|
7772
|
+
const declared = { ...element.attributes };
|
|
7773
|
+
if (document.rules.length > 0) {
|
|
7774
|
+
const matching = document.rules.map((rule, order) => ({
|
|
7775
|
+
rule,
|
|
7776
|
+
order
|
|
7777
|
+
})).filter(({ rule }) => selectorMatches(rule.selector, element)).sort((a, b) => a.rule.specificity - b.rule.specificity || a.order - b.order);
|
|
7778
|
+
for (const { rule } of matching) Object.assign(declared, rule.declarations);
|
|
7779
|
+
}
|
|
7780
|
+
if (element.attributes.style !== void 0) Object.assign(declared, parseDeclarations(element.attributes.style));
|
|
7781
|
+
return declared;
|
|
7782
|
+
}
|
|
7783
|
+
/** The inherited style at an element, from its parent's and what it declares. */
|
|
7784
|
+
function cascade(parent, declared, context) {
|
|
7785
|
+
const color = declared.color === void 0 || declared.color === "inherit" ? parent.color : colorValue(declared.color, parent, context) ?? parent.color;
|
|
7786
|
+
const here = {
|
|
7787
|
+
...parent,
|
|
7788
|
+
color
|
|
7789
|
+
};
|
|
7790
|
+
const pick = (key, property, read) => {
|
|
7791
|
+
const value = declared[property];
|
|
7792
|
+
return value === void 0 || value === "inherit" ? here[key] : read(value) ?? here[key];
|
|
7793
|
+
};
|
|
7794
|
+
const number = (value) => {
|
|
7795
|
+
const parsed = parseFloat(value);
|
|
7796
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
7797
|
+
};
|
|
7798
|
+
return {
|
|
7799
|
+
...here,
|
|
7800
|
+
fill: pick("fill", "fill", (value) => paintValue(value, here, context)),
|
|
7801
|
+
fillOpacity: pick("fillOpacity", "fill-opacity", (value) => unit(value)),
|
|
7802
|
+
fillRule: pick("fillRule", "fill-rule", (value) => value === "evenodd" ? "evenodd" : "nonzero"),
|
|
7803
|
+
stroke: pick("stroke", "stroke", (value) => paintValue(value, here, context)),
|
|
7804
|
+
strokeOpacity: pick("strokeOpacity", "stroke-opacity", (value) => unit(value)),
|
|
7805
|
+
strokeWidth: pick("strokeWidth", "stroke-width", number),
|
|
7806
|
+
lineCap: pick("lineCap", "stroke-linecap", (value) => value === "round" || value === "square" || value === "butt" ? value : void 0),
|
|
7807
|
+
lineJoin: pick("lineJoin", "stroke-linejoin", (value) => value === "round" || value === "bevel" || value === "miter" ? value : value === "arcs" || value === "miter-clip" ? "miter" : void 0),
|
|
7808
|
+
miterLimit: pick("miterLimit", "stroke-miterlimit", number),
|
|
7809
|
+
dash: pick("dash", "stroke-dasharray", (value) => {
|
|
7810
|
+
if (value === "none") return [];
|
|
7811
|
+
const segments = numbersIn(value);
|
|
7812
|
+
if (segments.some((segment) => segment < 0) || segments.every((segment) => segment === 0)) return [];
|
|
7813
|
+
return segments.length % 2 === 1 ? [...segments, ...segments] : segments;
|
|
7814
|
+
}),
|
|
7815
|
+
dashOffset: pick("dashOffset", "stroke-dashoffset", number),
|
|
7816
|
+
clipRule: pick("clipRule", "clip-rule", (value) => value === "evenodd" ? "evenodd" : "nonzero"),
|
|
7817
|
+
visible: pick("visible", "visibility", (value) => value === "visible" || value === "inherit" ? true : value === "hidden" || value === "collapse" ? false : void 0)
|
|
7818
|
+
};
|
|
7819
|
+
}
|
|
7820
|
+
function paintValue(value, style, context) {
|
|
7821
|
+
const trimmed = value.trim();
|
|
7822
|
+
if (trimmed === "none" || trimmed === "transparent") return NONE;
|
|
7823
|
+
const reference = /^url\(\s*['"]?#([^'")]+)['"]?\s*\)\s*(.*)$/.exec(trimmed);
|
|
7824
|
+
if (reference !== null) {
|
|
7825
|
+
const fallback = reference[2] === "" || reference[2] === void 0 ? NONE : paintValue(reference[2], style, context) ?? NONE;
|
|
7826
|
+
return {
|
|
7827
|
+
kind: "ref",
|
|
7828
|
+
id: reference[1],
|
|
7829
|
+
fallback
|
|
7830
|
+
};
|
|
7831
|
+
}
|
|
7832
|
+
const color = colorValue(trimmed, style, context);
|
|
7833
|
+
return color === void 0 ? void 0 : {
|
|
7834
|
+
kind: "color",
|
|
7835
|
+
color
|
|
7836
|
+
};
|
|
7837
|
+
}
|
|
7838
|
+
/** A colour, with `currentColor` resolved to whatever it is here. */
|
|
7839
|
+
function colorValue(value, style, context) {
|
|
7840
|
+
const trimmed = value.trim();
|
|
7841
|
+
if (trimmed.toLowerCase() === "currentcolor") return style.color ?? context.currentColor;
|
|
7842
|
+
return parseColor(trimmed);
|
|
7843
|
+
}
|
|
7844
|
+
/** An opacity: a number or a percentage, clamped to 0..1, and 1 when absent. */
|
|
7845
|
+
function unit(value) {
|
|
7846
|
+
if (value === void 0) return 1;
|
|
7847
|
+
const trimmed = value.trim();
|
|
7848
|
+
const parsed = trimmed.endsWith("%") ? parseFloat(trimmed) / 100 : parseFloat(trimmed);
|
|
7849
|
+
return Number.isFinite(parsed) ? Math.max(0, Math.min(1, parsed)) : 1;
|
|
7850
|
+
}
|
|
7851
|
+
/** A length in the drawing's units, a percentage being of `extent`. */
|
|
7852
|
+
function length(value, extent) {
|
|
7853
|
+
if (value === void 0) return 0;
|
|
7854
|
+
const trimmed = value.trim();
|
|
7855
|
+
if (trimmed.endsWith("%")) return parseFloat(trimmed) / 100 * extent;
|
|
7856
|
+
const parsed = parseFloat(trimmed);
|
|
7857
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
7858
|
+
}
|
|
7859
|
+
const IDENTITY$1 = [
|
|
7860
|
+
1,
|
|
7861
|
+
0,
|
|
7862
|
+
0,
|
|
7863
|
+
1,
|
|
7864
|
+
0,
|
|
7865
|
+
0
|
|
7866
|
+
];
|
|
7867
|
+
function multiply(m, n) {
|
|
7868
|
+
return [
|
|
7869
|
+
m[0] * n[0] + m[2] * n[1],
|
|
7870
|
+
m[1] * n[0] + m[3] * n[1],
|
|
7871
|
+
m[0] * n[2] + m[2] * n[3],
|
|
7872
|
+
m[1] * n[2] + m[3] * n[3],
|
|
7873
|
+
m[0] * n[4] + m[2] * n[5] + m[4],
|
|
7874
|
+
m[1] * n[4] + m[3] * n[5] + m[5]
|
|
7875
|
+
];
|
|
7876
|
+
}
|
|
7877
|
+
function apply(m, x, y) {
|
|
7878
|
+
return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
|
|
7879
|
+
}
|
|
7880
|
+
/** A `transform` list as one matrix, its functions applied left to right as SVG says. */
|
|
7881
|
+
function parseTransform$1(value) {
|
|
7882
|
+
let matrix = IDENTITY$1;
|
|
7883
|
+
for (const match of value.matchAll(/(matrix|translate|scale|rotate|skewX|skewY)\s*\(([^)]*)\)/g)) {
|
|
7884
|
+
const args = numbersIn(match[2]);
|
|
7885
|
+
let step = IDENTITY$1;
|
|
7886
|
+
switch (match[1]) {
|
|
7887
|
+
case "matrix":
|
|
7888
|
+
if (args.length >= 6) step = [
|
|
7889
|
+
args[0],
|
|
7890
|
+
args[1],
|
|
7891
|
+
args[2],
|
|
7892
|
+
args[3],
|
|
7893
|
+
args[4],
|
|
7894
|
+
args[5]
|
|
7895
|
+
];
|
|
7896
|
+
break;
|
|
7897
|
+
case "translate":
|
|
7898
|
+
step = [
|
|
7899
|
+
1,
|
|
7900
|
+
0,
|
|
7901
|
+
0,
|
|
7902
|
+
1,
|
|
7903
|
+
args[0] ?? 0,
|
|
7904
|
+
args[1] ?? 0
|
|
7905
|
+
];
|
|
7906
|
+
break;
|
|
7907
|
+
case "scale":
|
|
7908
|
+
step = [
|
|
7909
|
+
args[0] ?? 1,
|
|
7910
|
+
0,
|
|
7911
|
+
0,
|
|
7912
|
+
args[1] ?? args[0] ?? 1,
|
|
7913
|
+
0,
|
|
7914
|
+
0
|
|
7915
|
+
];
|
|
7916
|
+
break;
|
|
7917
|
+
case "rotate": {
|
|
7918
|
+
const angle = (args[0] ?? 0) * Math.PI / 180;
|
|
7919
|
+
const cos = Math.cos(angle);
|
|
7920
|
+
const sin = Math.sin(angle);
|
|
7921
|
+
const [cx, cy] = [args[1] ?? 0, args[2] ?? 0];
|
|
7922
|
+
step = multiply(multiply([
|
|
7923
|
+
1,
|
|
7924
|
+
0,
|
|
7925
|
+
0,
|
|
7926
|
+
1,
|
|
7927
|
+
cx,
|
|
7928
|
+
cy
|
|
7929
|
+
], [
|
|
7930
|
+
cos,
|
|
7931
|
+
sin,
|
|
7932
|
+
-sin,
|
|
7933
|
+
cos,
|
|
7934
|
+
0,
|
|
7935
|
+
0
|
|
7936
|
+
]), [
|
|
7937
|
+
1,
|
|
7938
|
+
0,
|
|
7939
|
+
0,
|
|
7940
|
+
1,
|
|
7941
|
+
-cx,
|
|
7942
|
+
-cy
|
|
7943
|
+
]);
|
|
7944
|
+
break;
|
|
7945
|
+
}
|
|
7946
|
+
case "skewX":
|
|
7947
|
+
step = [
|
|
7948
|
+
1,
|
|
7949
|
+
0,
|
|
7950
|
+
Math.tan((args[0] ?? 0) * Math.PI / 180),
|
|
7951
|
+
1,
|
|
7952
|
+
0,
|
|
7953
|
+
0
|
|
7954
|
+
];
|
|
7955
|
+
break;
|
|
7956
|
+
case "skewY": step = [
|
|
7957
|
+
1,
|
|
7958
|
+
Math.tan((args[0] ?? 0) * Math.PI / 180),
|
|
7959
|
+
0,
|
|
7960
|
+
1,
|
|
7961
|
+
0,
|
|
7962
|
+
0
|
|
7963
|
+
];
|
|
7964
|
+
}
|
|
7965
|
+
matrix = multiply(matrix, step);
|
|
7966
|
+
}
|
|
7967
|
+
return matrix;
|
|
7968
|
+
}
|
|
7969
|
+
function applyTransform$1(surface, value) {
|
|
7970
|
+
const [a, b, c, d, e, f] = parseTransform$1(value);
|
|
7971
|
+
surface.transform(a, b, c, d, e, f);
|
|
7972
|
+
}
|
|
7973
|
+
//#endregion
|
|
7974
|
+
//#region src/rendering/svg/SvgResolver.ts
|
|
7975
|
+
/**
|
|
7976
|
+
* Whether a source is an SVG file, from its name or its data URL's
|
|
7977
|
+
* type: what `Image` asks before it decodes, since the platform
|
|
7978
|
+
* decoder refuses SVG and this one draws it.
|
|
7979
|
+
*/
|
|
7980
|
+
function isSvgSource(source) {
|
|
7981
|
+
if (/^data:image\/svg\+xml[;,]/i.test(source)) return true;
|
|
7982
|
+
const path = source.split(/[?#]/)[0] ?? "";
|
|
7983
|
+
return /\.svgz?$/i.test(path);
|
|
7984
|
+
}
|
|
7985
|
+
const DEFAULT_CAPACITY$3 = 64;
|
|
7986
|
+
/**
|
|
7987
|
+
* Fetch, read, keep.
|
|
7988
|
+
*
|
|
7989
|
+
* Kept by recency and never closed, unlike a bitmap: a read file holds
|
|
7990
|
+
* no decoded pixels, so there is nothing to release promptly, and a
|
|
7991
|
+
* file a screen shows a hundred times (an icon in every row) is read
|
|
7992
|
+
* once. A failed fetch is not kept, so the next node tries again.
|
|
7993
|
+
*/
|
|
7994
|
+
var DefaultSvgResolver = class {
|
|
7995
|
+
entries = /* @__PURE__ */ new Map();
|
|
7996
|
+
settled = /* @__PURE__ */ new Map();
|
|
7997
|
+
capacity;
|
|
7998
|
+
fetchText;
|
|
7999
|
+
constructor(options = {}) {
|
|
8000
|
+
this.capacity = options.capacity ?? DEFAULT_CAPACITY$3;
|
|
8001
|
+
this.fetchText = options.fetch ?? defaultFetch$2;
|
|
8002
|
+
}
|
|
8003
|
+
resolve(source) {
|
|
8004
|
+
const existing = this.entries.get(source);
|
|
8005
|
+
if (existing !== void 0) {
|
|
8006
|
+
this.entries.delete(source);
|
|
8007
|
+
this.entries.set(source, existing);
|
|
8008
|
+
return existing;
|
|
8009
|
+
}
|
|
8010
|
+
const entry = this.fetchText(source).then((text) => {
|
|
8011
|
+
const document = parseSvg(text);
|
|
8012
|
+
if (this.entries.get(source) === entry) this.settled.set(source, document);
|
|
8013
|
+
return document;
|
|
8014
|
+
}).catch((error) => {
|
|
8015
|
+
if (this.entries.get(source) === entry) this.entries.delete(source);
|
|
8016
|
+
throw error;
|
|
8017
|
+
});
|
|
8018
|
+
entry.catch(() => {});
|
|
8019
|
+
this.entries.set(source, entry);
|
|
8020
|
+
while (this.entries.size > this.capacity) {
|
|
8021
|
+
const oldest = this.entries.keys().next().value;
|
|
8022
|
+
this.entries.delete(oldest);
|
|
8023
|
+
this.settled.delete(oldest);
|
|
8024
|
+
}
|
|
8025
|
+
return entry;
|
|
8026
|
+
}
|
|
8027
|
+
peek(source) {
|
|
8028
|
+
return this.settled.get(source) ?? null;
|
|
8029
|
+
}
|
|
8030
|
+
};
|
|
8031
|
+
async function defaultFetch$2(source) {
|
|
8032
|
+
const response = await fetch(source);
|
|
8033
|
+
if (!response.ok) throw new Error(`Fetching the SVG '${source}' failed with ${response.status} ${response.statusText}.`);
|
|
8034
|
+
return response.text();
|
|
8035
|
+
}
|
|
8036
|
+
//#endregion
|
|
6864
8037
|
//#region src/modifiers/media.ts
|
|
6865
8038
|
/**
|
|
6866
8039
|
* A value or an Observable of one, as one Observable. The media
|
|
@@ -6870,6 +8043,29 @@ function numberOr(value, fallback) {
|
|
|
6870
8043
|
function follow(value) {
|
|
6871
8044
|
return isObservable$1(value) ? value : of(value);
|
|
6872
8045
|
}
|
|
8046
|
+
/** Pictures this thread's media modifiers are fetching or decoding now. */
|
|
8047
|
+
let inFlight = 0;
|
|
8048
|
+
/**
|
|
8049
|
+
* How many pictures the media modifiers in this thread are still
|
|
8050
|
+
* fetching or decoding: an `Image`'s bitmap or its SVG.
|
|
8051
|
+
*
|
|
8052
|
+
* A tree waiting on a picture asks for no frames while it waits, so a
|
|
8053
|
+
* caller that judges a tree finished by its quiet (`renderFrame`, which
|
|
8054
|
+
* draws a mock once, off screen) would draw it without the picture.
|
|
8055
|
+
* This is the other half of finished: nothing still on its way.
|
|
8056
|
+
*/
|
|
8057
|
+
function mediaLoadsInFlight() {
|
|
8058
|
+
return inFlight;
|
|
8059
|
+
}
|
|
8060
|
+
/** Counts a load from its start until it settles, either way. */
|
|
8061
|
+
function counted(loading) {
|
|
8062
|
+
inFlight++;
|
|
8063
|
+
const done = () => {
|
|
8064
|
+
inFlight--;
|
|
8065
|
+
};
|
|
8066
|
+
loading.then(done, done);
|
|
8067
|
+
return loading;
|
|
8068
|
+
}
|
|
6873
8069
|
/**
|
|
6874
8070
|
* Resolves a source to a bitmap and writes it onto the node's `image`.
|
|
6875
8071
|
*
|
|
@@ -6905,6 +8101,8 @@ function load$1(host, args, sources) {
|
|
|
6905
8101
|
let held = null;
|
|
6906
8102
|
args.onState?.("loading");
|
|
6907
8103
|
host.clear("image");
|
|
8104
|
+
host.clear("paint");
|
|
8105
|
+
host.clear("src");
|
|
6908
8106
|
const attempt = (index, lastError) => {
|
|
6909
8107
|
if (!live) return;
|
|
6910
8108
|
const source = sources[index];
|
|
@@ -6912,16 +8110,22 @@ function load$1(host, args, sources) {
|
|
|
6912
8110
|
args.onState?.("failed", lastError ?? /* @__PURE__ */ new Error("No image source was given."));
|
|
6913
8111
|
return;
|
|
6914
8112
|
}
|
|
8113
|
+
if (args.vectors !== void 0 && isSvgSource(source)) {
|
|
8114
|
+
drawVector(host, args, args.vectors, source, () => live, (error) => attempt(index + 1, error));
|
|
8115
|
+
return;
|
|
8116
|
+
}
|
|
6915
8117
|
held = source;
|
|
6916
8118
|
const ready = args.resolver.peek?.(source) ?? null;
|
|
6917
8119
|
if (ready !== null) {
|
|
6918
8120
|
host.set("image", ready);
|
|
8121
|
+
host.set("src", source);
|
|
6919
8122
|
args.onState?.("loaded");
|
|
6920
8123
|
return;
|
|
6921
8124
|
}
|
|
6922
|
-
args.resolver.resolve(source).then((bitmap) => {
|
|
8125
|
+
counted(args.resolver.resolve(source)).then((bitmap) => {
|
|
6923
8126
|
if (!live) return;
|
|
6924
8127
|
host.set("image", bitmap);
|
|
8128
|
+
host.set("src", source);
|
|
6925
8129
|
args.onState?.("loaded");
|
|
6926
8130
|
}).catch((error) => {
|
|
6927
8131
|
if (held === source) held = null;
|
|
@@ -6937,6 +8141,36 @@ function load$1(host, args, sources) {
|
|
|
6937
8141
|
}
|
|
6938
8142
|
};
|
|
6939
8143
|
}
|
|
8144
|
+
/** SVG sources already warned about, so a file shown in every row says what it lacks once. */
|
|
8145
|
+
const warnedSvg = /* @__PURE__ */ new Set();
|
|
8146
|
+
/**
|
|
8147
|
+
* An SVG source, read and set as the node's `paint`: vectors, drawn at
|
|
8148
|
+
* whatever size the node is laid out at, with `currentColor` the
|
|
8149
|
+
* node's inherited text colour. Nothing is held, because a read file
|
|
8150
|
+
* holds no pixels to release.
|
|
8151
|
+
*/
|
|
8152
|
+
function drawVector(host, args, vectors, source, live, fail) {
|
|
8153
|
+
const show = (document) => {
|
|
8154
|
+
const fit = host.node.getProperty("objectFit") ?? "contain";
|
|
8155
|
+
host.set("paint", svgPaint(document, { fit }));
|
|
8156
|
+
host.set("src", source);
|
|
8157
|
+
if (document.unsupported.length > 0 && !warnedSvg.has(source)) {
|
|
8158
|
+
warnedSvg.add(source);
|
|
8159
|
+
console.warn(`[gesso] ${source.slice(0, 80)} uses ${document.unsupported.join(", ")}, which Gesso does not draw yet; the rest of it is drawn.`);
|
|
8160
|
+
}
|
|
8161
|
+
args.onState?.("loaded");
|
|
8162
|
+
};
|
|
8163
|
+
const ready = vectors.peek?.(source) ?? null;
|
|
8164
|
+
if (ready !== null) {
|
|
8165
|
+
show(ready);
|
|
8166
|
+
return;
|
|
8167
|
+
}
|
|
8168
|
+
counted(vectors.resolve(source)).then((document) => {
|
|
8169
|
+
if (live()) show(document);
|
|
8170
|
+
}, (error) => {
|
|
8171
|
+
if (live()) fail(error);
|
|
8172
|
+
});
|
|
8173
|
+
}
|
|
6940
8174
|
function sameSources(a, b) {
|
|
6941
8175
|
if (typeof a === "string" || typeof b === "string") return a === b;
|
|
6942
8176
|
return a.length === b.length && a.every((url, index) => url === b[index]);
|
|
@@ -8630,11 +9864,9 @@ const DEFAULT_CAPACITY$2 = 32;
|
|
|
8630
9864
|
*
|
|
8631
9865
|
* **What it cannot decode.** `createImageBitmap` takes a `Blob`, and
|
|
8632
9866
|
* Chrome refuses an SVG one — "The source image could not be decoded".
|
|
8633
|
-
*
|
|
8634
|
-
*
|
|
8635
|
-
*
|
|
8636
|
-
* pointing the playground's Media card at an SVG data URL and getting
|
|
8637
|
-
* a placeholder.
|
|
9867
|
+
* Found by pointing the playground's Media card at an SVG data URL and
|
|
9868
|
+
* getting a placeholder. So an `Image` never sends one here: an SVG
|
|
9869
|
+
* source is read by `SvgResolver` and drawn as vectors (`svg/`).
|
|
8638
9870
|
*
|
|
8639
9871
|
* **Eviction.** Reference counting first, LRU second. A bitmap
|
|
8640
9872
|
* something on screen is holding is never evicted, however long the
|
|
@@ -9282,6 +10514,19 @@ fn fs(in: VertexOutput) -> @location(0) vec4f {
|
|
|
9282
10514
|
let color = textureSample(tTexture, tSampler, in.uv);
|
|
9283
10515
|
return vec4f(color.rgb, color.a * in.opacity * alpha);
|
|
9284
10516
|
}
|
|
10517
|
+
|
|
10518
|
+
// An opacity group's layer, blended onto what holds it. The layer was
|
|
10519
|
+
// drawn into with the same blend as the frame, so it holds premultiplied
|
|
10520
|
+
// colour, and is scaled whole — colour and alpha alike — by the group's
|
|
10521
|
+
// opacity and the clip's coverage.
|
|
10522
|
+
@fragment
|
|
10523
|
+
fn fsLayer(in: VertexOutput) -> @location(0) vec4f {
|
|
10524
|
+
let alpha = clipCoverage(in.position.xy, uView.z, in.clipIndex);
|
|
10525
|
+
if (alpha <= 0.0) {
|
|
10526
|
+
discard;
|
|
10527
|
+
}
|
|
10528
|
+
return textureSample(tTexture, tSampler, in.uv) * (in.opacity * alpha);
|
|
10529
|
+
}
|
|
9285
10530
|
`;
|
|
9286
10531
|
//#endregion
|
|
9287
10532
|
//#region src/rendering/webgpu/WebGPUGlyphAtlas.ts
|
|
@@ -10109,6 +11354,13 @@ let CommandKind = /* @__PURE__ */ function(CommandKind) {
|
|
|
10109
11354
|
CommandKind[CommandKind["Glyphs"] = 1] = "Glyphs";
|
|
10110
11355
|
/** One textured instance drawn with an image. */
|
|
10111
11356
|
CommandKind[CommandKind["Image"] = 2] = "Image";
|
|
11357
|
+
/**
|
|
11358
|
+
* Everything until the matching `EndLayer` is drawn into a cleared
|
|
11359
|
+
* layer of its own, the start of an opacity group (`GroupOpacity.ts`).
|
|
11360
|
+
*/
|
|
11361
|
+
CommandKind[CommandKind["BeginLayer"] = 3] = "BeginLayer";
|
|
11362
|
+
/** The layer is done, and is blended onto what holds it at the group's opacity. */
|
|
11363
|
+
CommandKind[CommandKind["EndLayer"] = 4] = "EndLayer";
|
|
10112
11364
|
return CommandKind;
|
|
10113
11365
|
}({});
|
|
10114
11366
|
const IDENTITY = [
|
|
@@ -10223,8 +11475,21 @@ function buildRenderList(root, layout, measurer, logicalWidth, logicalHeight, dp
|
|
|
10223
11475
|
height: logicalHeight
|
|
10224
11476
|
}
|
|
10225
11477
|
};
|
|
10226
|
-
/**
|
|
11478
|
+
/**
|
|
11479
|
+
* The nodes asking for `lift`, met on the walk and drawn after it,
|
|
11480
|
+
* with their opacity as a product of everything above them: inside a
|
|
11481
|
+
* layer `state.opacity` counts from the layer, and a lifted node is
|
|
11482
|
+
* drawn outside the layers it was met in.
|
|
11483
|
+
*/
|
|
10227
11484
|
const liftedPass = [];
|
|
11485
|
+
/**
|
|
11486
|
+
* The opacity each open layer will be blended at, multiplied out from
|
|
11487
|
+
* the frame: the last entry is what `state.opacity` is relative to.
|
|
11488
|
+
* One entry, 1, while no layer is open.
|
|
11489
|
+
*/
|
|
11490
|
+
const layerOpacities = [1];
|
|
11491
|
+
const physicalWidth = toPhysicalPixels(logicalWidth, dpr);
|
|
11492
|
+
const physicalHeight = toPhysicalPixels(logicalHeight, dpr);
|
|
10228
11493
|
function closePrimitives() {
|
|
10229
11494
|
const end = instanceData.length / 24;
|
|
10230
11495
|
if (end > openStart) commands.push({
|
|
@@ -10344,7 +11609,7 @@ function buildRenderList(root, layout, measurer, logicalWidth, logicalHeight, dp
|
|
|
10344
11609
|
if (rec.lifted && !lifted) {
|
|
10345
11610
|
liftedPass.push({
|
|
10346
11611
|
node,
|
|
10347
|
-
opacity: state.opacity,
|
|
11612
|
+
opacity: state.opacity * layerOpacities[layerOpacities.length - 1],
|
|
10348
11613
|
ctm: state.ctm
|
|
10349
11614
|
});
|
|
10350
11615
|
return;
|
|
@@ -10352,9 +11617,33 @@ function buildRenderList(root, layout, measurer, logicalWidth, logicalHeight, dp
|
|
|
10352
11617
|
const sticky = rec.stickyOffsetX !== 0 || rec.stickyOffsetY !== 0;
|
|
10353
11618
|
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
11619
|
const paint = resolvePaintState(node, paintScratch);
|
|
10355
|
-
const
|
|
11620
|
+
const layered = paint.opacity < 1 && opacityNeedsLayer(node, rec, paint, layout);
|
|
11621
|
+
const opacity = paint.opacity;
|
|
11622
|
+
const outerOpacity = state.opacity;
|
|
11623
|
+
const outerClip = state.clip;
|
|
11624
|
+
const outerRounded = state.rounded;
|
|
11625
|
+
const effectiveOpacity = layered ? 1 : state.opacity * paint.opacity;
|
|
10356
11626
|
const baseCtm = sticky ? translateTransform(state.ctm, rec.stickyOffsetX, rec.stickyOffsetY) : state.ctm;
|
|
10357
11627
|
const nodeCtm = paint.hasTransform ? multiplyTransform(baseCtm, buildOwnTransform(rec, paint.transform)) : baseCtm;
|
|
11628
|
+
const region = layered ? layerRegion(node, rec, {
|
|
11629
|
+
a: nodeCtm[0] * dpr,
|
|
11630
|
+
b: nodeCtm[1] * dpr,
|
|
11631
|
+
c: nodeCtm[2] * dpr,
|
|
11632
|
+
d: nodeCtm[3] * dpr,
|
|
11633
|
+
e: nodeCtm[4] * dpr,
|
|
11634
|
+
f: nodeCtm[5] * dpr
|
|
11635
|
+
}, layout, physicalWidth, physicalHeight, dpr) : null;
|
|
11636
|
+
if (layered) {
|
|
11637
|
+
closePrimitives();
|
|
11638
|
+
commands.push({
|
|
11639
|
+
kind: 3,
|
|
11640
|
+
scissor: null
|
|
11641
|
+
});
|
|
11642
|
+
layerOpacities.push(layerOpacities[layerOpacities.length - 1] * outerOpacity * opacity);
|
|
11643
|
+
state.opacity = 1;
|
|
11644
|
+
state.clip = null;
|
|
11645
|
+
state.rounded = -1;
|
|
11646
|
+
}
|
|
10358
11647
|
const ownScissor = state.clip;
|
|
10359
11648
|
const ownRounded = state.rounded;
|
|
10360
11649
|
let nextClip = state.clip;
|
|
@@ -10624,6 +11913,24 @@ function buildRenderList(root, layout, measurer, logicalWidth, logicalHeight, dp
|
|
|
10624
11913
|
beginPrimitives(ownScissor);
|
|
10625
11914
|
pushScrollbars(instanceData, rec, effectiveOpacity, nodeCtm, ownRounded, now);
|
|
10626
11915
|
}
|
|
11916
|
+
if (region !== null) {
|
|
11917
|
+
closePrimitives();
|
|
11918
|
+
layerOpacities.pop();
|
|
11919
|
+
state.opacity = outerOpacity;
|
|
11920
|
+
state.clip = outerClip;
|
|
11921
|
+
state.rounded = outerRounded;
|
|
11922
|
+
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, {
|
|
11923
|
+
u: region.x / physicalWidth,
|
|
11924
|
+
v: region.y / physicalHeight,
|
|
11925
|
+
uw: region.width / physicalWidth,
|
|
11926
|
+
vh: region.height / physicalHeight
|
|
11927
|
+
}) : null;
|
|
11928
|
+
commands.push({
|
|
11929
|
+
kind: 4,
|
|
11930
|
+
instance,
|
|
11931
|
+
scissor: outerClip
|
|
11932
|
+
});
|
|
11933
|
+
}
|
|
10627
11934
|
}
|
|
10628
11935
|
/**
|
|
10629
11936
|
* The top layer, drawn once the tree is done: no clip, no scissor,
|
|
@@ -10643,7 +11950,7 @@ function buildRenderList(root, layout, measurer, logicalWidth, logicalHeight, dp
|
|
|
10643
11950
|
};
|
|
10644
11951
|
for (let i = from; i < liftedPass.length; i++) {
|
|
10645
11952
|
const entry = liftedPass[i];
|
|
10646
|
-
state.opacity = entry.opacity;
|
|
11953
|
+
state.opacity = entry.opacity / layerOpacities[layerOpacities.length - 1];
|
|
10647
11954
|
state.ctm = entry.ctm;
|
|
10648
11955
|
state.clip = clip;
|
|
10649
11956
|
state.rounded = rounded;
|
|
@@ -11125,6 +12432,19 @@ const STRAIGHT_ALPHA_BLEND = {
|
|
|
11125
12432
|
operation: "add"
|
|
11126
12433
|
}
|
|
11127
12434
|
};
|
|
12435
|
+
/** Source-over for a premultiplied source: an opacity group's layer. */
|
|
12436
|
+
const PREMULTIPLIED_BLEND = {
|
|
12437
|
+
color: {
|
|
12438
|
+
srcFactor: "one",
|
|
12439
|
+
dstFactor: "one-minus-src-alpha",
|
|
12440
|
+
operation: "add"
|
|
12441
|
+
},
|
|
12442
|
+
alpha: {
|
|
12443
|
+
srcFactor: "one",
|
|
12444
|
+
dstFactor: "one-minus-src-alpha",
|
|
12445
|
+
operation: "add"
|
|
12446
|
+
}
|
|
12447
|
+
};
|
|
11128
12448
|
/**
|
|
11129
12449
|
* Creates the primitive pipeline: rounded fills and borders.
|
|
11130
12450
|
*
|
|
@@ -11204,8 +12524,9 @@ function createTexturedPipeline(device, format, frameLayout) {
|
|
|
11204
12524
|
texture: {}
|
|
11205
12525
|
}
|
|
11206
12526
|
] });
|
|
12527
|
+
const layout = device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout, frameLayout] });
|
|
11207
12528
|
const pipeline = device.createRenderPipeline({
|
|
11208
|
-
layout
|
|
12529
|
+
layout,
|
|
11209
12530
|
vertex: {
|
|
11210
12531
|
module,
|
|
11211
12532
|
entryPoint: "vs",
|
|
@@ -11224,7 +12545,27 @@ function createTexturedPipeline(device, format, frameLayout) {
|
|
|
11224
12545
|
cullMode: "none"
|
|
11225
12546
|
}
|
|
11226
12547
|
});
|
|
11227
|
-
|
|
12548
|
+
const layerPipeline = device.createRenderPipeline({
|
|
12549
|
+
layout,
|
|
12550
|
+
vertex: {
|
|
12551
|
+
module,
|
|
12552
|
+
entryPoint: "vs",
|
|
12553
|
+
buffers: [quadVertexLayout(), texturedInstanceLayout()]
|
|
12554
|
+
},
|
|
12555
|
+
fragment: {
|
|
12556
|
+
module,
|
|
12557
|
+
entryPoint: "fsLayer",
|
|
12558
|
+
targets: [{
|
|
12559
|
+
format,
|
|
12560
|
+
blend: PREMULTIPLIED_BLEND
|
|
12561
|
+
}]
|
|
12562
|
+
},
|
|
12563
|
+
primitive: {
|
|
12564
|
+
topology: "triangle-list",
|
|
12565
|
+
cullMode: "none"
|
|
12566
|
+
}
|
|
12567
|
+
});
|
|
12568
|
+
if (pipeline === void 0 || layerPipeline === void 0) throw new WebGPUError("Failed to create textured render pipeline.", "pipeline");
|
|
11228
12569
|
const { vertexBuffer, indexBuffer } = createQuad(device);
|
|
11229
12570
|
const uniformBuffer = device.createBuffer({
|
|
11230
12571
|
size: 16,
|
|
@@ -11244,7 +12585,8 @@ function createTexturedPipeline(device, format, frameLayout) {
|
|
|
11244
12585
|
imageSampler: device.createSampler({
|
|
11245
12586
|
magFilter: "linear",
|
|
11246
12587
|
minFilter: "linear"
|
|
11247
|
-
})
|
|
12588
|
+
}),
|
|
12589
|
+
layerPipeline
|
|
11248
12590
|
};
|
|
11249
12591
|
}
|
|
11250
12592
|
function createQuad(device) {
|
|
@@ -11673,6 +13015,7 @@ function createCanvas(width, height) {
|
|
|
11673
13015
|
const vertexBufferUsage = typeof GPUBufferUsage !== "undefined" ? GPUBufferUsage.VERTEX : 32;
|
|
11674
13016
|
const storageBufferUsage = typeof GPUBufferUsage !== "undefined" ? GPUBufferUsage.STORAGE : 128;
|
|
11675
13017
|
const copyDstBufferUsage = typeof GPUBufferUsage !== "undefined" ? GPUBufferUsage.COPY_DST : 8;
|
|
13018
|
+
const layerTextureUsage = (typeof GPUTextureUsage !== "undefined" ? GPUTextureUsage.RENDER_ATTACHMENT : 16) | (typeof GPUTextureUsage !== "undefined" ? GPUTextureUsage.TEXTURE_BINDING : 4);
|
|
11676
13019
|
const copyDstUsage = typeof GPUBufferUsage !== "undefined" ? GPUBufferUsage.COPY_DST : 8;
|
|
11677
13020
|
const mapReadUsage = typeof GPUBufferUsage !== "undefined" ? GPUBufferUsage.MAP_READ : 1;
|
|
11678
13021
|
const mapModeRead = typeof GPUMapMode !== "undefined" ? GPUMapMode.READ : 1;
|
|
@@ -11705,6 +13048,12 @@ var WebGPURenderer = class {
|
|
|
11705
13048
|
frameLayout = null;
|
|
11706
13049
|
frameBindGroup = null;
|
|
11707
13050
|
cachedInstanceData = null;
|
|
13051
|
+
/**
|
|
13052
|
+
* The textures opacity groups are drawn in, one per depth of nesting,
|
|
13053
|
+
* so a group inside a group never clears what holds it. Made the first
|
|
13054
|
+
* time a frame reaches that depth and again when the surface resizes.
|
|
13055
|
+
*/
|
|
13056
|
+
groupLayers = [];
|
|
11708
13057
|
/** Draw calls of the most recent frame. */
|
|
11709
13058
|
lastDraws = {
|
|
11710
13059
|
primitiveDraws: 0,
|
|
@@ -11847,8 +13196,9 @@ var WebGPURenderer = class {
|
|
|
11847
13196
|
const encodeStart = this.hooks.onEncodeStart !== void 0 || this.hooks.onEncodeEnd !== void 0 ? performance.now() : 0;
|
|
11848
13197
|
const commandEncoder = device.createCommandEncoder();
|
|
11849
13198
|
const texture = this.surface.getCurrentTexture();
|
|
11850
|
-
const
|
|
11851
|
-
|
|
13199
|
+
const targets = [texture.createView()];
|
|
13200
|
+
let pass = commandEncoder.beginRenderPass({ colorAttachments: [{
|
|
13201
|
+
view: targets[0],
|
|
11852
13202
|
clearValue: {
|
|
11853
13203
|
r: 0,
|
|
11854
13204
|
g: 0,
|
|
@@ -11868,6 +13218,49 @@ var WebGPURenderer = class {
|
|
|
11868
13218
|
const commands = list.commands;
|
|
11869
13219
|
for (let i = 0; i < commands.length; i++) {
|
|
11870
13220
|
const command = commands[i];
|
|
13221
|
+
if (command.kind === 3 || command.kind === 4) {
|
|
13222
|
+
pass.end();
|
|
13223
|
+
const depth = targets.length - 1;
|
|
13224
|
+
let layer;
|
|
13225
|
+
if (command.kind === 3) {
|
|
13226
|
+
layer = this.groupLayer(device, depth, texture.width, texture.height);
|
|
13227
|
+
targets.push(layer.view);
|
|
13228
|
+
} else {
|
|
13229
|
+
layer = this.groupLayers[depth - 1];
|
|
13230
|
+
targets.pop();
|
|
13231
|
+
}
|
|
13232
|
+
pass = commandEncoder.beginRenderPass({ colorAttachments: [command.kind === 3 ? {
|
|
13233
|
+
view: layer.view,
|
|
13234
|
+
clearValue: {
|
|
13235
|
+
r: 0,
|
|
13236
|
+
g: 0,
|
|
13237
|
+
b: 0,
|
|
13238
|
+
a: 0
|
|
13239
|
+
},
|
|
13240
|
+
loadOp: "clear",
|
|
13241
|
+
storeOp: "store"
|
|
13242
|
+
} : {
|
|
13243
|
+
view: targets[targets.length - 1],
|
|
13244
|
+
loadOp: "load",
|
|
13245
|
+
storeOp: "store"
|
|
13246
|
+
}] });
|
|
13247
|
+
pass.setBindGroup(1, this.frameBindGroup);
|
|
13248
|
+
currentScissor = null;
|
|
13249
|
+
currentKind = null;
|
|
13250
|
+
if (command.kind === 3) continue;
|
|
13251
|
+
const blendScissor = command.scissor ?? viewport;
|
|
13252
|
+
if (command.instance === null || blendScissor.width <= 0 || blendScissor.height <= 0) continue;
|
|
13253
|
+
pass.setPipeline(textured.layerPipeline);
|
|
13254
|
+
pass.setVertexBuffer(0, textured.vertexBuffer);
|
|
13255
|
+
pass.setVertexBuffer(1, this.texturedBuffer);
|
|
13256
|
+
pass.setIndexBuffer(textured.indexBuffer, "uint16");
|
|
13257
|
+
pass.setBindGroup(0, layer.bindGroup);
|
|
13258
|
+
currentScissor = setScissor(pass, currentScissor, blendScissor);
|
|
13259
|
+
pass.drawIndexed(textured.indexCount, 1, 0, 0, command.instance);
|
|
13260
|
+
this.lastDraws.texturedDraws++;
|
|
13261
|
+
this.lastDraws.texturedInstances++;
|
|
13262
|
+
continue;
|
|
13263
|
+
}
|
|
11871
13264
|
const scissor = command.scissor ?? viewport;
|
|
11872
13265
|
if (scissor.width <= 0 || scissor.height <= 0) continue;
|
|
11873
13266
|
if (command.kind === 0) {
|
|
@@ -11909,6 +13302,24 @@ var WebGPURenderer = class {
|
|
|
11909
13302
|
capture?.();
|
|
11910
13303
|
if (this.hooks.onEncodeEnd !== void 0) this.hooks.onEncodeEnd(performance.now() - encodeStart);
|
|
11911
13304
|
}
|
|
13305
|
+
/** The layer for an opacity group at `depth`, made or remade at the surface's size. */
|
|
13306
|
+
groupLayer(device, depth, width, height) {
|
|
13307
|
+
const existing = this.groupLayers[depth];
|
|
13308
|
+
if (existing !== void 0 && existing.texture.width === width && existing.texture.height === height) return existing;
|
|
13309
|
+
existing?.texture.destroy();
|
|
13310
|
+
const texture = device.createTexture({
|
|
13311
|
+
size: [width, height],
|
|
13312
|
+
format: this.format,
|
|
13313
|
+
usage: layerTextureUsage
|
|
13314
|
+
});
|
|
13315
|
+
const layer = {
|
|
13316
|
+
texture,
|
|
13317
|
+
view: texture.createView(),
|
|
13318
|
+
bindGroup: createTexturedBindGroup(device, this.textured, texture, this.textured.textSampler)
|
|
13319
|
+
};
|
|
13320
|
+
this.groupLayers[depth] = layer;
|
|
13321
|
+
return layer;
|
|
13322
|
+
}
|
|
11912
13323
|
/**
|
|
11913
13324
|
* When a capture is pending, encodes a copy of the frame into a
|
|
11914
13325
|
* mappable buffer and returns the step that maps it after submit.
|
|
@@ -12034,6 +13445,8 @@ var WebGPURenderer = class {
|
|
|
12034
13445
|
this.clipBuffer = null;
|
|
12035
13446
|
this.gradientBuffer?.destroy();
|
|
12036
13447
|
this.gradientBuffer = null;
|
|
13448
|
+
for (const layer of this.groupLayers) layer.texture.destroy();
|
|
13449
|
+
this.groupLayers = [];
|
|
12037
13450
|
this.frameBindGroup = null;
|
|
12038
13451
|
this.frameLayout = null;
|
|
12039
13452
|
this.primitives = null;
|
|
@@ -14723,6 +16136,14 @@ const NEW_DISPLAY = .15;
|
|
|
14723
16136
|
const BEAT_PASSES = 3;
|
|
14724
16137
|
/** Refreshes apart the two an interval is read between must be, at least. */
|
|
14725
16138
|
const MIN_BEAT_SPAN = 6;
|
|
16139
|
+
/**
|
|
16140
|
+
* The most refreshes a frame waits for a tick that has not sat in this
|
|
16141
|
+
* thread's queue (see `tick`): a frame is drawn on a stale one after
|
|
16142
|
+
* that, so a thread that is always behind still draws.
|
|
16143
|
+
*/
|
|
16144
|
+
const MAX_QUEUED_REFRESHES = 8;
|
|
16145
|
+
/** How long, at least, between timing this thread's refreshes again once they were given up; see `retryLocal`. */
|
|
16146
|
+
const RETRY_LOCAL_MS = 2e3;
|
|
14726
16147
|
/** The most refreshes a frame is given: two, sixty at 120 Hz; slower than that is no smoother. */
|
|
14727
16148
|
const MAX_STRIDE = 2;
|
|
14728
16149
|
function localAnimationFrame() {
|
|
@@ -14776,6 +16197,8 @@ var UiHostFrameClock = class {
|
|
|
14776
16197
|
/** Gaps between this thread's own refreshes while frames ran back to back. */
|
|
14777
16198
|
localIntervals = [];
|
|
14778
16199
|
lastLocalTime = null;
|
|
16200
|
+
/** How long the frame drawn at `lastLocalTime` took, which says whether the gap after it was the refresh's or the frame's. */
|
|
16201
|
+
lastLocalCost = 0;
|
|
14779
16202
|
/** The display's interval and the host's time origin, once the host has said. */
|
|
14780
16203
|
display = null;
|
|
14781
16204
|
/**
|
|
@@ -14784,6 +16207,11 @@ var UiHostFrameClock = class {
|
|
|
14784
16207
|
* show (see \`displayInterval\`).
|
|
14785
16208
|
*/
|
|
14786
16209
|
locked = false;
|
|
16210
|
+
/** This thread's own `requestAnimationFrame` once given up, kept to see whether it keeps time after all. */
|
|
16211
|
+
givenUp = null;
|
|
16212
|
+
/** When this thread's refreshes were last timed again, and their times while they are being. */
|
|
16213
|
+
lastRetry = -Infinity;
|
|
16214
|
+
retrying = null;
|
|
14787
16215
|
/** The host's recent refresh times, which give the beat its phase. */
|
|
14788
16216
|
refreshes = [];
|
|
14789
16217
|
/** Host refreshes since the last frame drawn on the beat. */
|
|
@@ -14818,7 +16246,11 @@ var UiHostFrameClock = class {
|
|
|
14818
16246
|
if (this.localHandle === null) this.localHandle = this.local((time) => {
|
|
14819
16247
|
this.localHandle = null;
|
|
14820
16248
|
this.recordLocal(time);
|
|
14821
|
-
if (this.pending)
|
|
16249
|
+
if (this.pending) {
|
|
16250
|
+
const started = this.now();
|
|
16251
|
+
this.deliver(time);
|
|
16252
|
+
this.lastLocalCost = this.now() - started;
|
|
16253
|
+
} else this.lastLocalCost = 0;
|
|
14822
16254
|
});
|
|
14823
16255
|
return;
|
|
14824
16256
|
}
|
|
@@ -14873,7 +16305,7 @@ var UiHostFrameClock = class {
|
|
|
14873
16305
|
this.lastLocalTime = time;
|
|
14874
16306
|
if (previous !== null && this.pending) {
|
|
14875
16307
|
const interval = time - previous;
|
|
14876
|
-
if (interval > 0 && interval < MAX_CADENCE_MS) {
|
|
16308
|
+
if (interval > 0 && interval < MAX_CADENCE_MS && this.lastLocalCost < interval / 2) {
|
|
14877
16309
|
this.localIntervals.push(interval);
|
|
14878
16310
|
if (this.localIntervals.length > LOCAL_SAMPLES) this.localIntervals.shift();
|
|
14879
16311
|
}
|
|
@@ -14891,9 +16323,59 @@ var UiHostFrameClock = class {
|
|
|
14891
16323
|
if (this.local === null || this.display === null || this.localIntervals.length < LOCAL_SAMPLES) return;
|
|
14892
16324
|
const sorted = [...this.localIntervals].sort((a, b) => a - b);
|
|
14893
16325
|
if (sorted[Math.floor(sorted.length / 2)] / this.display.interval <= 1.06) return;
|
|
16326
|
+
this.givenUp = this.local;
|
|
14894
16327
|
this.local = null;
|
|
14895
16328
|
this.locked = true;
|
|
14896
16329
|
}
|
|
16330
|
+
/**
|
|
16331
|
+
* Times this thread's own refreshes again, now and then, once they were
|
|
16332
|
+
* given up, and takes them back if they keep the display's time.
|
|
16333
|
+
*
|
|
16334
|
+
* Given up for good, a worker's refreshes stayed given up whatever had
|
|
16335
|
+
* made them slow. A Chrome worker busy between its frames, opening a
|
|
16336
|
+
* large file or scrolling through lines it has never drawn, refreshes
|
|
16337
|
+
* late and unevenly, which no measure of the gaps alone told from
|
|
16338
|
+
* WebKit's timer: 5.5, 25.5, 10.9, 25 ms on a 165 Hz display one time,
|
|
16339
|
+
* 12.1, 6.1, 12.2, 24.2 the next. Its frames then went through the
|
|
16340
|
+
* host's ticks for as long as the page was open, a message and a hop
|
|
16341
|
+
* through the page's thread each. Timed again while frames are cheap,
|
|
16342
|
+
* they come a refresh apart and are taken back; WebKit's come 15 ms
|
|
16343
|
+
* apart on a 120 Hz display however idle the thread is, and stay given
|
|
16344
|
+
* up. Nine refreshes every two seconds or more, while frames come cheap.
|
|
16345
|
+
*/
|
|
16346
|
+
retryLocal(interval) {
|
|
16347
|
+
if (this.givenUp === null || this.hidden || this.costs.length < SLOW_SAMPLES) return;
|
|
16348
|
+
const now = this.now();
|
|
16349
|
+
if (now - this.lastRetry < RETRY_LOCAL_MS) return;
|
|
16350
|
+
const sorted = [...this.costs].sort((a, b) => a - b);
|
|
16351
|
+
if (sorted[Math.floor(sorted.length / 2)] > interval * FAST_FRAME) return;
|
|
16352
|
+
this.lastRetry = now;
|
|
16353
|
+
const local = this.givenUp;
|
|
16354
|
+
const times = [];
|
|
16355
|
+
this.retrying = times;
|
|
16356
|
+
const step = (time) => {
|
|
16357
|
+
if (this.retrying !== times) return;
|
|
16358
|
+
times.push(time);
|
|
16359
|
+
if (times.length <= LOCAL_SAMPLES) {
|
|
16360
|
+
local(step);
|
|
16361
|
+
return;
|
|
16362
|
+
}
|
|
16363
|
+
this.retrying = null;
|
|
16364
|
+
const gaps = times.slice(1).map((value, index) => value - times[index]).sort((a, b) => a - b);
|
|
16365
|
+
if (this.givenUp === null || this.display === null || gaps[Math.floor(gaps.length / 2)] / this.display.interval > 1.06) return;
|
|
16366
|
+
this.local = this.givenUp;
|
|
16367
|
+
this.givenUp = null;
|
|
16368
|
+
this.locked = false;
|
|
16369
|
+
this.localIntervals.length = 0;
|
|
16370
|
+
this.lastLocalTime = null;
|
|
16371
|
+
this.costs.length = 0;
|
|
16372
|
+
this.stride = 1;
|
|
16373
|
+
this.clearFallback();
|
|
16374
|
+
this.setActive(false);
|
|
16375
|
+
if (this.pending) this.requestFrame();
|
|
16376
|
+
};
|
|
16377
|
+
local(step);
|
|
16378
|
+
}
|
|
14897
16379
|
/** A time on the host's clock, on this thread's. */
|
|
14898
16380
|
toLocal(host) {
|
|
14899
16381
|
return this.display === null ? host : host - this.timeOrigin + this.display.origin;
|
|
@@ -14959,6 +16441,7 @@ var UiHostFrameClock = class {
|
|
|
14959
16441
|
const started = this.now();
|
|
14960
16442
|
this.deliver(this.toLocal(refresh));
|
|
14961
16443
|
this.recordCost(this.now() - started, beat.interval);
|
|
16444
|
+
this.retryLocal(beat.interval);
|
|
14962
16445
|
}
|
|
14963
16446
|
/**
|
|
14964
16447
|
* Keeps the stride to what frames cost: every refresh while the typical
|
|
@@ -15120,7 +16603,8 @@ var UiHostFrameClock = class {
|
|
|
15120
16603
|
if (this.pending) {
|
|
15121
16604
|
this.clearFallback();
|
|
15122
16605
|
this.idleTicks = 0;
|
|
15123
|
-
|
|
16606
|
+
const queued = this.now() - this.toLocal(time) > this.display.interval;
|
|
16607
|
+
if (this.refreshesSinceFrame >= this.stride && (!queued || this.refreshesSinceFrame >= MAX_QUEUED_REFRESHES)) this.deliverOnBeat(time);
|
|
15124
16608
|
return;
|
|
15125
16609
|
}
|
|
15126
16610
|
}
|
|
@@ -16114,6 +17598,6 @@ function recordsEqual(a, b) {
|
|
|
16114
17598
|
return true;
|
|
16115
17599
|
}
|
|
16116
17600
|
//#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 };
|
|
17601
|
+
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, 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, 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, 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, defineProperty, defineThemeExtension, demuxMp4Audio, demuxMp4Video, densityFactors, describeLength, describeOverrides, describeShortcut, detectEditingPlatform, diffSemantics, dragSessionFor, dragSource, draggable, drawOverlayShapes, drawSvg, 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, 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, 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, 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, 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
17602
|
|
|
16119
17603
|
//# sourceMappingURL=index.js.map
|