gesso-core 0.6.19 → 0.6.20
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 +14 -0
- package/dist/{CanvasSurface-tAao5BLh.js → CanvasSurface-VUJRrcXl.js} +586 -80
- package/dist/CanvasSurface-VUJRrcXl.js.map +1 -0
- package/dist/{index-8yhrkCnb.d.ts → index-lzNLHD-1.d.ts} +241 -25
- package/dist/index.d.ts +1 -1
- package/dist/index.js +248 -9
- package/dist/index.js.map +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/package.json +1 -1
- package/dist/CanvasSurface-tAao5BLh.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as isImeKey, $a as MOUSE_POINTER, $i as spacingEqual, $n as ELLIPSIS, $r as transformsEqual, $t as registerFontStack, A as withOverrideSource, Aa as colorToHex, Ai as observeViewportInsets, An as clearLinkHover, Ar as inheritedPropertyNames, At as adjacentField, B as FlexDirection, Ba as UiEnvironment, Bi as defineThemeExtension, Bn as editorFor, Br as normalizeStates, Bt as createCanvasTextStyle, C as SCROLLBAR_LINGER_MS, Ca as textContentOf, Ci as uniformBorderRadius, Cn as isNodeInert, Cr as resolveProperty, Ct as hasDrawnText, D as formatExplanation, Da as darkColors, Di as UiInsetRegistry, Dn as isSubtreeInert, Dr as findPropertyDefinition, Dt as selectionRectsIn, E as formatConstraints, Ea as colorsEqualPalette, Ei as UiEnvironmentKeys, En as isPressable, Er as closestPropertyName, Et as paragraphGeometryFrom, F as clampSize, Fa as parseColor, Fi as darkTheme, Fn as setLinkHover, Fr as UiProperties, Ft as detectEditingPlatform, G as CrossAxisAlignment, Ga as resetOverrideWarnings, Gi as withThemeExtension, Gn as nextCaretToggle, Gr as validateSubgrid, Gt as runFontString, H as placeGridItems, Ha as DirtyNodeSet, Hi as noThemeExtensions, Hn as isEditableNode, Hr as validateRole, Ht as fillTextRects, I as constraintsEqual, Ia as rgb8, Ii as lightTheme, In as CARET_BLINK_MS, Ir as UI_ROLES, It as isPrintable, J as parseCrossAxisAlignment, Ja as UiVisualState, Ji as raiseContrast, Jn as isVideoSurface, Jr as pathValuesEqual, Jt as textRunBackgrounds, K as MainAxisAlignment, Ka as writeDeclaredProperty, Ki as contrastRatio, Kn as syncEditorValue, Kr as inputsEqual, Kt as spanOf, L as tightenConstraints, La as rgba, Li as themesEqual, Ln as CARET_WIDTH, Lr as UI_SEMANTIC_STATES, Lt as EditableLayout, M as contentOffset, Ma as colorValuesEqual, Mi as bandOf, Mn as linkHoverOf, Mr as propertyIsInherited, Mt as edgeField, N as LayoutRecord, Na as colorsEqual, Ni as containerBands, Nn as linkOf, Nr as propertyNarrowers, Nt as editingGroupOf, O as formatNumber, Oa as lightColors, Oi as insetsEqual, On as pointerEventsOf, Or as getPropertyNames, Ot as wordRangeIn, P as Constraints, Pa as normalizeColor, Pi as unknownContainerSize, Pn as linkRunAtPointIn, Pr as propertyValuesEqualByName, Pt as commandForKey, Q as capturePointer, Qa as visualStatesEqual, Qi as scaleSpacing, Qn as spannedRunsFor, Qr as transformIsIdentity, Qt as fontStackFor, R as MAX_MEASURES_PER_CHILD, Ra as createEnvironmentKey, Ri as withContrast, Rn as EDITOR_PROP, Rr as isUiRole, Rt as applyCanvasTextStyle, S as SCROLLBAR_FADE_MS, Sa as spansDifferOnlyInPaint, Si as normalizeBorderRadius, Sn as isNodeHitTestable, Sr as resolveNumber, St as UiInputDispatcher, T as describeOverrides, Ta as textStylesEqual, Ti as propertyValuesEqual, Tn as isNodeTabStop, Tr as resolveString, Tt as paragraphGeometry, U as sizeGridTracks, Ua as clearOverrideProperty, Ui as themeExtension, Un as isMultiline, Ur as validateStates, Ut as layoutTextLines, V as parseFlexDirection, Va as UiNode, Vi as hasThemeExtension, Vn as editorOf, Vr as statesEqual, Vt as drawText, W as AlignContent, Wa as overrideSources, Wi as themeExtensionsEqual, Wn as isReadOnly, Wr as UI_SUBGRID_AXES, Wt as runCanvasStyleInto, X as CanvasPlatformSurface, Xa as hasVisualState, Xi as densityFactors, Xn as CharacterCountTextMeasurer, Xr as parseTransform, Xt as bumpFontStack, Y as parseMainAxisAlignment, Ya as defaultVisualState, Yi as relativeLuminance, Yn as videoFrameSize, Yr as UiTransforms, Yt as textRunDecorations, Z as UiPlatformAdapter, Za as visualState, Zi as defaultSpacing, Zn as ParagraphTextMeasurer, Zr as transform, Zt as clearFontStacks, _ as LABEL_PADDING_X, _a as flattenTextSpans, _i as borderRadius, _n as selectableTextNodes, _o as DirtyFlags, _r as previousWordStart, _t as scrollLeaderOf, a as PaintPictureCache, aa as boxShadowArraysEqual, ai as validateGradient, an as computeObjectFitRect, ao as UiInputEvent, ar as lineLimit, at as UiGestureRecognizer, b as snapScroll, ba as spanAtOffset, bi as borderRadiusIsZero, bn as setSelectionRange, br as DEFAULT_LINE_HEIGHT_FACTOR, bt as scrollbarThumbs, c as EMPTY_RECORDING, ca as boxShadowsEqual, ci as isAutoLength, cn as normalizeTextAlign, co as UiPinchEvent, cr as offsetForVerticalMove, ct as isScrollContainer, d as tracePathData, da as shapesEqual, di as isPercentLength, dn as TEXT_MATCHES_PROP, do as UiTextChangeEvent, dr as graphemeBoundaries, dt as UiFocusManager, ea as spacingSteps, ei as MAX_GRADIENT_STOPS, en as DEFAULT_FONT_FAMILY, eo as UiBeforeInputEvent, er as layoutParagraph, et as pointerDeviceOf, f as ScaledImageCache, fa as defaultTypography, fi as minmax, fn as clearMatchRanges, fo as UiWheelDeltaMode, fr as lineEndAt, ft as UiHitTester, g as paintsAfterChildren, ga as editableSpansOf, gi as UiBorderRadiuses, gn as clearSelectionRange, go as UiNodeType, gr as previousGraphemeStart, gt as SCROLLBAR_THICKNESS, h as hasDecorationPhase, ha as defaultTextStyle, hi as resolveLength, hn as TEXT_SELECTION_PROP, ho as wheelDeltaYOf, hr as nextWordEnd, ht as SCROLLBAR_MIN_THUMB, i as traceRoundedRect, ia as boxShadow, ii as radialGradient, in as colorToCss, io as UiGestureEvent, ir as lineIndexForOffset, it as UiTouchScroller, j as accumulatedOffsetTo, ja as colorToRgba, ji as UiContainerSizeSource, jn as hasTextLinks, jr as propertyEffects, jt as comparePositions, k as labelNode, ka as UiBasicColors, ki as noInsets, kn as resolveCursor, kr as inheritedPropertyFlags, kt as textInputOf, l as PaintRecorder, la as defaultShapes, li as isFrLength, ln as normalizeVerticalAlign, lo as UiPointerEvent, lr as selectionRects, lt as UiKeyboardController, m as decorationRect, ma as typographyEqual, mi as repeat, mn as setMatchRanges, mo as noKeyModifiers, mr as nextGraphemeEnd, mt as SCROLLBAR_INSET, n as createCanvasSurface, na as shadowsEqual, ni as gradientsEqual, nn as DEFAULT_FONT_WEIGHT, no as UiEventType, nr as caretRectFor, nt as touchActionFor, o as paintPictures, oa as boxShadowRadius, oi as auto, on as createPaintState, oo as UiKeyboardEvent, or as offsetAtPoint, ot as UiPinchRecognizer, p as decorationColor, pa as isTypographyRole, pi as percent, pn as matchRangesOf, po as UiWheelEvent, pr as lineStartAt, pt as SCROLLBAR_HOVER_ZONE, q as parseAlignContent, qa as writeOverrideProperty, qi as highContrastColors, qn as resolveColorValue, qr as paintValuesEqual, qt as textMeasureRequest, r as Canvas2DRenderer, ra as BOX_SHADOW_BLUR_REACH, ri as linearGradient, rn as DEFAULT_TEXT_COLOR, ro as UiFocusEvent, rr as lineIndexAtY, rt as UiEditingController, s as PaintTarget, sa as boxShadowReach, si as fr, sn as isPaintVisible, so as UiPasteEvent, sr as offsetAtX, st as UiWheelController, t as CanvasSurface, ta as defaultShadows, ti as gradientPaint, tn as DEFAULT_FONT_SIZE, to as UiCompositionEvent, tr as proportionalFontMetrics, tt as prepareInputSurface, u as replayPaint, ua as isShapeName, ui as isMinMaxTrack, un as resolvePaintState, uo as UiSelectionChangeEvent, ur as EditableTextModel, ut as UiPointerController, v as drawOverlayShapes, va as hiddenRangesOf, vi as borderRadiusCorners, vn as selectableTextOf, vo as UiBinding, vr as wordRangeAt, vt as scrollRange, w as describeLength, wa as textSpansEqual, wi as defineProperty, wn as isNodeSelectable, wr as resolvePropertyByName, wt as offsetAtPointIn, x as LayoutEngine, xa as spannedTextOf, xi as borderRadiusValuesEqual, xn as isNodeFocusable, xr as resolveBoolean, xt as scrollbarZoneAt, y as labelOrigin, ya as resolvedSpansOf, yi as borderRadiusEqual, yn as selectionRangeOf, yr as UiGraph, yt as scrollbarThumb, z as isLayoutProtocol, za as findEnvironmentKey, zi as withDensity, zn as caretVisibleAt, zr as isUiSemanticState, zt as buildFontString } from "./CanvasSurface-
|
|
1
|
+
import { $ as isImeKey, $a as MOUSE_POINTER, $i as spacingEqual, $n as ELLIPSIS, $r as transformsEqual, $t as registerFontStack, A as withOverrideSource, Aa as colorToHex, Ai as observeViewportInsets, An as clearLinkHover, Ar as inheritedPropertyNames, At as adjacentField, B as FlexDirection, Ba as UiEnvironment, Bi as defineThemeExtension, Bn as editorFor, Br as normalizeStates, Bt as createCanvasTextStyle, C as SCROLLBAR_LINGER_MS, Ca as textContentOf, Ci as uniformBorderRadius, Cn as isNodeInert, Cr as resolveProperty, Ct as hasDrawnText, D as formatExplanation, Da as darkColors, Di as UiInsetRegistry, Dn as isSubtreeInert, Dr as findPropertyDefinition, Dt as selectionRectsIn, E as formatConstraints, Ea as colorsEqualPalette, Ei as UiEnvironmentKeys, En as isPressable, Er as closestPropertyName, Et as paragraphGeometryFrom, F as clampSize, Fa as parseColor, Fi as darkTheme, Fn as setLinkHover, Fr as UiProperties, Ft as detectEditingPlatform, G as CrossAxisAlignment, Ga as resetOverrideWarnings, Gi as withThemeExtension, Gn as nextCaretToggle, Gr as validateSubgrid, Gt as runFontString, H as placeGridItems, Ha as DirtyNodeSet, Hi as noThemeExtensions, Hn as isEditableNode, Hr as validateRole, Ht as fillTextRects, I as constraintsEqual, Ia as rgb8, Ii as lightTheme, In as CARET_BLINK_MS, Ir as UI_ROLES, It as isPrintable, J as parseCrossAxisAlignment, Ja as UiVisualState, Ji as raiseContrast, Jn as isVideoSurface, Jr as pathValuesEqual, Jt as textRunBackgrounds, K as MainAxisAlignment, Ka as writeDeclaredProperty, Ki as contrastRatio, Kn as syncEditorValue, Kr as inputsEqual, Kt as spanOf, L as tightenConstraints, La as rgba, Li as themesEqual, Ln as CARET_WIDTH, Lr as UI_SEMANTIC_STATES, Lt as EditableLayout, M as contentOffset, Ma as colorValuesEqual, Mi as bandOf, Mn as linkHoverOf, Mr as propertyIsInherited, Mt as edgeField, N as LayoutRecord, Na as colorsEqual, Ni as containerBands, Nn as linkOf, Nr as propertyNarrowers, Nt as editingGroupOf, O as formatNumber, Oa as lightColors, Oi as insetsEqual, On as pointerEventsOf, Or as getPropertyNames, Ot as wordRangeIn, P as Constraints, Pa as normalizeColor, Pi as unknownContainerSize, Pn as linkRunAtPointIn, Pr as propertyValuesEqualByName, Pt as commandForKey, Q as capturePointer, Qa as visualStatesEqual, Qi as scaleSpacing, Qn as spannedRunsFor, Qr as transformIsIdentity, Qt as fontStackFor, R as MAX_MEASURES_PER_CHILD, Ra as createEnvironmentKey, Ri as withContrast, Rn as EDITOR_PROP, Rr as isUiRole, Rt as applyCanvasTextStyle, S as SCROLLBAR_FADE_MS, Sa as spansDifferOnlyInPaint, Si as normalizeBorderRadius, Sn as isNodeHitTestable, Sr as resolveNumber, St as UiInputDispatcher, T as describeOverrides, Ta as textStylesEqual, Ti as propertyValuesEqual, Tn as isNodeTabStop, Tr as resolveString, Tt as paragraphGeometry, U as sizeGridTracks, Ua as clearOverrideProperty, Ui as themeExtension, Un as isMultiline, Ur as validateStates, Ut as layoutTextLines, V as parseFlexDirection, Va as UiNode, Vi as hasThemeExtension, Vn as editorOf, Vr as statesEqual, Vt as drawText, W as AlignContent, Wa as overrideSources, Wi as themeExtensionsEqual, Wn as isReadOnly, Wr as UI_SUBGRID_AXES, Wt as runCanvasStyleInto, X as CanvasPlatformSurface, Xa as hasVisualState, Xi as densityFactors, Xn as CharacterCountTextMeasurer, Xr as parseTransform, Xt as bumpFontStack, Y as parseMainAxisAlignment, Ya as defaultVisualState, Yi as relativeLuminance, Yn as videoFrameSize, Yr as UiTransforms, Yt as textRunDecorations, Z as UiPlatformAdapter, Za as visualState, Zi as defaultSpacing, Zn as ParagraphTextMeasurer, Zr as transform, Zt as clearFontStacks, _ as LABEL_PADDING_X, _a as flattenTextSpans, _i as borderRadius, _n as selectableTextNodes, _o as DirtyFlags, _r as previousWordStart, _t as scrollLeaderOf, a as PaintPictureCache, aa as boxShadowArraysEqual, ai as validateGradient, an as computeObjectFitRect, ao as UiInputEvent, ar as lineLimit, at as UiGestureRecognizer, b as snapScroll, ba as spanAtOffset, bi as borderRadiusIsZero, bn as setSelectionRange, br as DEFAULT_LINE_HEIGHT_FACTOR, bt as scrollbarThumbs, c as EMPTY_RECORDING, ca as boxShadowsEqual, ci as isAutoLength, cn as normalizeTextAlign, co as UiPinchEvent, cr as offsetForVerticalMove, ct as isScrollContainer, d as tracePathData, da as shapesEqual, di as isPercentLength, dn as TEXT_MATCHES_PROP, do as UiTextChangeEvent, dr as graphemeBoundaries, dt as UiFocusManager, ea as spacingSteps, ei as MAX_GRADIENT_STOPS, en as DEFAULT_FONT_FAMILY, eo as UiBeforeInputEvent, er as layoutParagraph, et as pointerDeviceOf, f as ScaledImageCache, fa as defaultTypography, fi as minmax, fn as clearMatchRanges, fo as UiWheelDeltaMode, fr as lineEndAt, ft as UiHitTester, g as paintsAfterChildren, ga as editableSpansOf, gi as UiBorderRadiuses, gn as clearSelectionRange, go as UiNodeType, gr as previousGraphemeStart, gt as SCROLLBAR_THICKNESS, h as hasDecorationPhase, ha as defaultTextStyle, hi as resolveLength, hn as TEXT_SELECTION_PROP, ho as wheelDeltaYOf, hr as nextWordEnd, ht as SCROLLBAR_MIN_THUMB, i as traceRoundedRect, ia as boxShadow, ii as radialGradient, in as colorToCss, io as UiGestureEvent, ir as lineIndexForOffset, it as UiTouchScroller, j as accumulatedOffsetTo, ja as colorToRgba, ji as UiContainerSizeSource, jn as hasTextLinks, jr as propertyEffects, jt as comparePositions, k as labelNode, ka as UiBasicColors, ki as noInsets, kn as resolveCursor, kr as inheritedPropertyFlags, kt as textInputOf, l as PaintRecorder, la as defaultShapes, li as isFrLength, ln as normalizeVerticalAlign, lo as UiPointerEvent, lr as selectionRects, lt as UiKeyboardController, m as decorationRect, ma as typographyEqual, mi as repeat, mn as setMatchRanges, mo as noKeyModifiers, mr as nextGraphemeEnd, mt as SCROLLBAR_INSET, n as createCanvasSurface, na as shadowsEqual, ni as gradientsEqual, nn as DEFAULT_FONT_WEIGHT, no as UiEventType, nr as caretRectFor, nt as touchActionFor, o as paintPictures, oa as boxShadowRadius, oi as auto, on as createPaintState, oo as UiKeyboardEvent, or as offsetAtPoint, ot as UiPinchRecognizer, p as decorationColor, pa as isTypographyRole, pi as percent, pn as matchRangesOf, po as UiWheelEvent, pr as lineStartAt, pt as SCROLLBAR_HOVER_ZONE, q as parseAlignContent, qa as writeOverrideProperty, qi as highContrastColors, qn as resolveColorValue, qr as paintValuesEqual, qt as textMeasureRequest, r as Canvas2DRenderer, ra as BOX_SHADOW_BLUR_REACH, ri as linearGradient, rn as DEFAULT_TEXT_COLOR, ro as UiFocusEvent, rr as lineIndexAtY, rt as UiEditingController, s as PaintTarget, sa as boxShadowReach, si as fr, sn as isPaintVisible, so as UiPasteEvent, sr as offsetAtX, st as UiWheelController, t as CanvasSurface, ta as defaultShadows, ti as gradientPaint, tn as DEFAULT_FONT_SIZE, to as UiCompositionEvent, tr as proportionalFontMetrics, tt as prepareInputSurface, u as replayPaint, ua as isShapeName, ui as isMinMaxTrack, un as resolvePaintState, uo as UiSelectionChangeEvent, ur as EditableTextModel, ut as UiPointerController, v as drawOverlayShapes, va as hiddenRangesOf, vi as borderRadiusCorners, vn as selectableTextOf, vo as UiBinding, vr as wordRangeAt, vt as scrollRange, w as describeLength, wa as textSpansEqual, wi as defineProperty, wn as isNodeSelectable, wr as resolvePropertyByName, wt as offsetAtPointIn, x as LayoutEngine, xa as spannedTextOf, xi as borderRadiusValuesEqual, xn as isNodeFocusable, xr as resolveBoolean, xt as scrollbarZoneAt, y as labelOrigin, ya as resolvedSpansOf, yi as borderRadiusEqual, yn as selectionRangeOf, yr as UiGraph, yt as scrollbarThumb, z as isLayoutProtocol, za as findEnvironmentKey, zi as withDensity, zn as caretVisibleAt, zr as isUiSemanticState, zt as buildFontString } from "./CanvasSurface-VUJRrcXl.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. */
|
|
@@ -2200,6 +2200,15 @@ function describeValueType(value) {
|
|
|
2200
2200
|
var UiGraphBuilder = class {
|
|
2201
2201
|
graph;
|
|
2202
2202
|
refs = /* @__PURE__ */ new Map();
|
|
2203
|
+
/**
|
|
2204
|
+
* The definition each node was last reconciled from. A definition is
|
|
2205
|
+
* a description that is never changed once made, so the same object
|
|
2206
|
+
* again has nothing new to say: a lazy list hands back the row it made
|
|
2207
|
+
* for an index on every scroll, and reconciling seventy unchanged rows
|
|
2208
|
+
* (their props, then every node of each row's component) to mount the
|
|
2209
|
+
* four that scrolled in cost a 165 Hz frame its budget.
|
|
2210
|
+
*/
|
|
2211
|
+
reconciledFrom = /* @__PURE__ */ new WeakMap();
|
|
2203
2212
|
modifiers = /* @__PURE__ */ new Map();
|
|
2204
2213
|
nextChildrenBindingId = 0;
|
|
2205
2214
|
/**
|
|
@@ -2340,9 +2349,12 @@ var UiGraphBuilder = class {
|
|
|
2340
2349
|
changed = true;
|
|
2341
2350
|
} else {
|
|
2342
2351
|
matched.add(node);
|
|
2343
|
-
this.
|
|
2344
|
-
|
|
2352
|
+
if (this.reconciledFrom.get(node) !== definition) {
|
|
2353
|
+
this.reconcileProps(node, definition.props);
|
|
2354
|
+
if (this.reconcileChildren(node, definition.children).changed) changed = true;
|
|
2355
|
+
}
|
|
2345
2356
|
}
|
|
2357
|
+
this.reconciledFrom.set(node, definition);
|
|
2346
2358
|
if (this.moveBefore(parent, node, cursor)) changed = true;
|
|
2347
2359
|
cursor = node.nextSibling;
|
|
2348
2360
|
result.push(node);
|
|
@@ -2412,8 +2424,12 @@ var UiGraphBuilder = class {
|
|
|
2412
2424
|
if (anchor === void 0) {
|
|
2413
2425
|
anchor = this.graph.createNode(anchorId, "fragment");
|
|
2414
2426
|
this.graph.insertBefore(parent, anchor, cursor);
|
|
2415
|
-
} else
|
|
2427
|
+
} else {
|
|
2428
|
+
matched.add(anchor);
|
|
2429
|
+
if (this.reconciledFrom.get(anchor) === element) return anchor;
|
|
2430
|
+
}
|
|
2416
2431
|
this.reconcileChildren(anchor, [resolver.resolve(element, anchorId)]);
|
|
2432
|
+
this.reconciledFrom.set(anchor, element);
|
|
2417
2433
|
return anchor;
|
|
2418
2434
|
}
|
|
2419
2435
|
/**
|
|
@@ -2519,8 +2535,7 @@ var UiGraphBuilder = class {
|
|
|
2519
2535
|
return true;
|
|
2520
2536
|
}
|
|
2521
2537
|
if (node === reference || node.nextSibling === reference) return false;
|
|
2522
|
-
this.graph.
|
|
2523
|
-
this.graph.insertBefore(parent, node, reference);
|
|
2538
|
+
this.graph.moveWithin(node, reference);
|
|
2524
2539
|
return true;
|
|
2525
2540
|
}
|
|
2526
2541
|
/**
|
|
@@ -2810,6 +2825,15 @@ var UiVirtualWindow = class {
|
|
|
2810
2825
|
count;
|
|
2811
2826
|
estimate;
|
|
2812
2827
|
overscan;
|
|
2828
|
+
recycle;
|
|
2829
|
+
/**
|
|
2830
|
+
* The slot each mounted item is keyed by, when recycling: an item keeps
|
|
2831
|
+
* its slot while it stays, and one leaving gives its slot to the next
|
|
2832
|
+
* one to come, whose element then matches the node it leaves behind.
|
|
2833
|
+
*/
|
|
2834
|
+
slotOf = /* @__PURE__ */ new Map();
|
|
2835
|
+
freeSlots = [];
|
|
2836
|
+
nextSlot = 0;
|
|
2813
2837
|
keyOf;
|
|
2814
2838
|
renderItem;
|
|
2815
2839
|
grid;
|
|
@@ -2841,6 +2865,7 @@ var UiVirtualWindow = class {
|
|
|
2841
2865
|
this.count = count;
|
|
2842
2866
|
this.estimate = options.estimatedExtent;
|
|
2843
2867
|
this.overscan = options.overscan ?? 3;
|
|
2868
|
+
this.recycle = options.recycle === true;
|
|
2844
2869
|
this.keyOf = options.key ?? ((index) => index);
|
|
2845
2870
|
this.renderItem = renderItem;
|
|
2846
2871
|
this.grid = options.grid;
|
|
@@ -3027,6 +3052,13 @@ var UiVirtualWindow = class {
|
|
|
3027
3052
|
});
|
|
3028
3053
|
}
|
|
3029
3054
|
buildChildren() {
|
|
3055
|
+
if (this.recycle) {
|
|
3056
|
+
for (const [index, slot] of this.slotOf) if (index < this.first || index > this.last) {
|
|
3057
|
+
this.slotOf.delete(index);
|
|
3058
|
+
this.freeSlots.push(slot);
|
|
3059
|
+
}
|
|
3060
|
+
for (let index = this.first; index <= this.last; index++) if (!this.slotOf.has(index)) this.slotOf.set(index, this.freeSlots.pop() ?? this.nextSlot++);
|
|
3061
|
+
}
|
|
3030
3062
|
const children = [];
|
|
3031
3063
|
if (this.grid?.header !== void 0) children.push(markRow(this.grid.header, {
|
|
3032
3064
|
key: "lazy:header",
|
|
@@ -3080,7 +3112,7 @@ var UiVirtualWindow = class {
|
|
|
3080
3112
|
*/
|
|
3081
3113
|
wrap(index, item) {
|
|
3082
3114
|
const props = {
|
|
3083
|
-
key: `lazy:${String(this.keyOf(index))}`,
|
|
3115
|
+
key: this.recycle ? `lazy:slot:${this.slotOf.get(index)}` : `lazy:${String(this.keyOf(index))}`,
|
|
3084
3116
|
[VIRTUAL_INDEX_PROP]: index
|
|
3085
3117
|
};
|
|
3086
3118
|
if (this.grid !== void 0) return markRow(item, props, `Row ${index} of`);
|
|
@@ -3200,11 +3232,12 @@ function LazyGrid(props, renderRow) {
|
|
|
3200
3232
|
});
|
|
3201
3233
|
}
|
|
3202
3234
|
function lazyList(axis, props, renderItem, grid) {
|
|
3203
|
-
const { count, estimatedExtent, overscan, itemKey, initialViewportExtent, revision, modifiers, windowRef, ...rest } = props;
|
|
3235
|
+
const { count, estimatedExtent, overscan, recycle, itemKey, initialViewportExtent, revision, modifiers, windowRef, ...rest } = props;
|
|
3204
3236
|
const window = new UiVirtualWindow(axis, {
|
|
3205
3237
|
count,
|
|
3206
3238
|
estimatedExtent,
|
|
3207
3239
|
overscan,
|
|
3240
|
+
recycle,
|
|
3208
3241
|
key: itemKey,
|
|
3209
3242
|
initialViewportExtent,
|
|
3210
3243
|
revision,
|
|
@@ -14375,6 +14408,26 @@ var UiTimerFrameClock = class {
|
|
|
14375
14408
|
* has had one for years, and a *nested* worker on Chromium has not.
|
|
14376
14409
|
* The feature test is the whole guard; there is no version to compare.
|
|
14377
14410
|
*/
|
|
14411
|
+
/** This thread's refreshes seen before they are compared with the display's. */
|
|
14412
|
+
const LOCAL_SAMPLES = 8;
|
|
14413
|
+
/** The host's refreshes the beat's phase is read from: about a sixth of a second at 120 Hz. */
|
|
14414
|
+
const BEAT_SAMPLES = 20;
|
|
14415
|
+
/** Frames whose cost decides the stride. */
|
|
14416
|
+
const COST_SAMPLES = 30;
|
|
14417
|
+
/** Frames seen before the stride is lengthened. */
|
|
14418
|
+
const SLOW_SAMPLES = 12;
|
|
14419
|
+
/** A frame costing more than this share of its refreshes is too slow for them. */
|
|
14420
|
+
const SLOW_FRAME = .75;
|
|
14421
|
+
/** And less than this share of one fewer is cheap enough to take one fewer. */
|
|
14422
|
+
const FAST_FRAME = .6;
|
|
14423
|
+
/** How far off the measured interval the refreshes must come to be another display's, as a fraction. */
|
|
14424
|
+
const NEW_DISPLAY = .15;
|
|
14425
|
+
/** Times the interval is read again on its own result. */
|
|
14426
|
+
const BEAT_PASSES = 3;
|
|
14427
|
+
/** Refreshes apart the two an interval is read between must be, at least. */
|
|
14428
|
+
const MIN_BEAT_SPAN = 6;
|
|
14429
|
+
/** The most refreshes a frame is given: two, sixty at 120 Hz; slower than that is no smoother. */
|
|
14430
|
+
const MAX_STRIDE = 2;
|
|
14378
14431
|
function localAnimationFrame() {
|
|
14379
14432
|
const host = globalThis;
|
|
14380
14433
|
return typeof host.requestAnimationFrame === "function" ? host.requestAnimationFrame.bind(host) : null;
|
|
@@ -14420,6 +14473,28 @@ var UiHostFrameClock = class {
|
|
|
14420
14473
|
/** This thread's own `requestAnimationFrame`, or null where there is none. */
|
|
14421
14474
|
local;
|
|
14422
14475
|
localHandle = null;
|
|
14476
|
+
timeOrigin;
|
|
14477
|
+
/** Gaps between this thread's own refreshes while frames ran back to back. */
|
|
14478
|
+
localIntervals = [];
|
|
14479
|
+
lastLocalTime = null;
|
|
14480
|
+
/** The display's interval and the host's time origin, once the host has said. */
|
|
14481
|
+
display = null;
|
|
14482
|
+
/**
|
|
14483
|
+
* Set once this thread's refreshes have shown they keep no display's
|
|
14484
|
+
* time: frames are then timed here, on the beat the host's refreshes
|
|
14485
|
+
* show (see \`displayInterval\`).
|
|
14486
|
+
*/
|
|
14487
|
+
locked = false;
|
|
14488
|
+
/** The host's recent refresh times, which give the beat its phase. */
|
|
14489
|
+
refreshes = [];
|
|
14490
|
+
/** Host refreshes since the last frame drawn on the beat. */
|
|
14491
|
+
refreshesSinceFrame = 0;
|
|
14492
|
+
/** The refresh the last frame here was drawn for: a frame is never drawn for one twice. */
|
|
14493
|
+
lastLocked = -Infinity;
|
|
14494
|
+
/** How long recent frames took here, which decides how many refreshes each one gets. */
|
|
14495
|
+
costs = [];
|
|
14496
|
+
/** Refreshes per frame on the beat: 1, or more when frames cost more than a refresh can hold. */
|
|
14497
|
+
stride = 1;
|
|
14423
14498
|
constructor(onFrame, onActive, options = {}) {
|
|
14424
14499
|
this.onFrame = onFrame;
|
|
14425
14500
|
this.onActive = onActive;
|
|
@@ -14428,6 +14503,7 @@ var UiHostFrameClock = class {
|
|
|
14428
14503
|
this.minStallMs = options.minStallMs ?? 12;
|
|
14429
14504
|
this.now = options.now ?? (() => performance.now());
|
|
14430
14505
|
this.local = options.requestAnimationFrame === void 0 ? localAnimationFrame() : options.requestAnimationFrame;
|
|
14506
|
+
this.timeOrigin = options.timeOrigin ?? (typeof performance !== "undefined" ? performance.timeOrigin : 0);
|
|
14431
14507
|
}
|
|
14432
14508
|
/** Whether this clock is driving itself from the compositor. */
|
|
14433
14509
|
get isLocal() {
|
|
@@ -14438,6 +14514,7 @@ var UiHostFrameClock = class {
|
|
|
14438
14514
|
if (this.local !== null) {
|
|
14439
14515
|
if (this.localHandle === null) this.localHandle = this.local((time) => {
|
|
14440
14516
|
this.localHandle = null;
|
|
14517
|
+
this.recordLocal(time);
|
|
14441
14518
|
if (this.pending) this.deliver(time);
|
|
14442
14519
|
});
|
|
14443
14520
|
return;
|
|
@@ -14446,6 +14523,157 @@ var UiHostFrameClock = class {
|
|
|
14446
14523
|
this.armTimer();
|
|
14447
14524
|
}
|
|
14448
14525
|
/**
|
|
14526
|
+
* Tells the clock the display's interval, measured on the host's own
|
|
14527
|
+
* refreshes, and the host's \`timeOrigin\`, which puts the host's frame
|
|
14528
|
+
* times on this thread's clock.
|
|
14529
|
+
*
|
|
14530
|
+
* A worker's own refreshes are not always the display's. WebKit's come
|
|
14531
|
+
* from a timer near 65 Hz that keeps no display's time: on a 120 Hz
|
|
14532
|
+
* MacBook its frames fell 15 to 16.4 ms apart, so each was shown after
|
|
14533
|
+
* one refresh, two or three, and a scroll juddered at a steady sixty.
|
|
14534
|
+
* Driving frames from the host's ticks instead put the host's thread in
|
|
14535
|
+
* every frame, and WebKit's is uneven while scrolling: at 120 a seventh
|
|
14536
|
+
* of the frames missed their refresh. So once this thread's refreshes
|
|
14537
|
+
* have shown they keep no display's time, frames follow the host's
|
|
14538
|
+
* refreshes, which are the only ones a worker's canvas is shown on in
|
|
14539
|
+
* WebKit anyway, with two differences from a plain hosted clock: each
|
|
14540
|
+
* frame is stamped with its refresh on the display's beat and on this
|
|
14541
|
+
* thread's clock, which is the clock a wheel step's time is on; and
|
|
14542
|
+
* frames too costly for one refresh are given an even two (see
|
|
14543
|
+
* \`recordCost\`).
|
|
14544
|
+
*/
|
|
14545
|
+
displayInterval(ms, origin) {
|
|
14546
|
+
if (ms > 0 && ms < MAX_CADENCE_MS) {
|
|
14547
|
+
this.display = {
|
|
14548
|
+
interval: ms,
|
|
14549
|
+
origin
|
|
14550
|
+
};
|
|
14551
|
+
this.compareLocal();
|
|
14552
|
+
}
|
|
14553
|
+
}
|
|
14554
|
+
/** Whether frames are timed here on the display's beat; see \`displayInterval\`. */
|
|
14555
|
+
get isDisplayLocked() {
|
|
14556
|
+
return this.locked;
|
|
14557
|
+
}
|
|
14558
|
+
recordLocal(time) {
|
|
14559
|
+
const previous = this.lastLocalTime;
|
|
14560
|
+
this.lastLocalTime = time;
|
|
14561
|
+
if (previous !== null && this.pending) {
|
|
14562
|
+
const interval = time - previous;
|
|
14563
|
+
if (interval > 0 && interval < MAX_CADENCE_MS) {
|
|
14564
|
+
this.localIntervals.push(interval);
|
|
14565
|
+
if (this.localIntervals.length > LOCAL_SAMPLES) this.localIntervals.shift();
|
|
14566
|
+
}
|
|
14567
|
+
}
|
|
14568
|
+
this.compareLocal();
|
|
14569
|
+
}
|
|
14570
|
+
/**
|
|
14571
|
+
* Gives this thread's refreshes up when they come well slower than the
|
|
14572
|
+
* display's: they cost frames, and in WebKit keep no display's beat
|
|
14573
|
+
* either. Faster than the display is left alone, because that is the
|
|
14574
|
+
* display the measurement was taken on going stale (a window dragged to
|
|
14575
|
+
* a faster screen), not a worker failing to keep up with it.
|
|
14576
|
+
*/
|
|
14577
|
+
compareLocal() {
|
|
14578
|
+
if (this.local === null || this.display === null || this.localIntervals.length < LOCAL_SAMPLES) return;
|
|
14579
|
+
const sorted = [...this.localIntervals].sort((a, b) => a - b);
|
|
14580
|
+
if (sorted[Math.floor(sorted.length / 2)] / this.display.interval <= 1.06) return;
|
|
14581
|
+
this.local = null;
|
|
14582
|
+
this.locked = true;
|
|
14583
|
+
}
|
|
14584
|
+
/** A time on the host's clock, on this thread's. */
|
|
14585
|
+
toLocal(host) {
|
|
14586
|
+
return this.display === null ? host : host - this.timeOrigin + this.display.origin;
|
|
14587
|
+
}
|
|
14588
|
+
/**
|
|
14589
|
+
* When the display's refreshes start, on the host's clock: the earliest
|
|
14590
|
+
* in phase of the host's recent refresh times, since a host's callback
|
|
14591
|
+
* can only run late, never early.
|
|
14592
|
+
*/
|
|
14593
|
+
beat() {
|
|
14594
|
+
if (this.display === null || this.refreshes.length === 0) return null;
|
|
14595
|
+
const cadence = this.cadenceMs();
|
|
14596
|
+
let interval = cadence !== null && Math.abs(cadence - this.display.interval) > this.display.interval * NEW_DISPLAY ? cadence : this.display.interval;
|
|
14597
|
+
for (let pass = 0; pass < BEAT_PASSES; pass++) interval = this.refineInterval(interval);
|
|
14598
|
+
const latest = this.refreshes[this.refreshes.length - 1];
|
|
14599
|
+
let earliest = Infinity;
|
|
14600
|
+
for (const time of this.refreshes) {
|
|
14601
|
+
let residual = ((time - latest) % interval + interval) % interval;
|
|
14602
|
+
if (residual > interval / 2) residual -= interval;
|
|
14603
|
+
earliest = Math.min(earliest, residual);
|
|
14604
|
+
}
|
|
14605
|
+
return {
|
|
14606
|
+
start: latest + earliest,
|
|
14607
|
+
interval
|
|
14608
|
+
};
|
|
14609
|
+
}
|
|
14610
|
+
/** The interval between the most punctual early and late refreshes, judged by `rough`. */
|
|
14611
|
+
refineInterval(rough) {
|
|
14612
|
+
const half = Math.floor(this.refreshes.length / 2);
|
|
14613
|
+
const first = this.refreshes[0];
|
|
14614
|
+
let early = null;
|
|
14615
|
+
let late = null;
|
|
14616
|
+
let index = 0;
|
|
14617
|
+
for (let i = 0; i < this.refreshes.length; i++) {
|
|
14618
|
+
const time = this.refreshes[i];
|
|
14619
|
+
if (i > 0) index += Math.max(1, Math.round((time - this.refreshes[i - 1]) / rough));
|
|
14620
|
+
const residual = time - first - index * rough;
|
|
14621
|
+
if (i < half) {
|
|
14622
|
+
if (early === null || residual < early.residual) early = {
|
|
14623
|
+
index,
|
|
14624
|
+
residual,
|
|
14625
|
+
time
|
|
14626
|
+
};
|
|
14627
|
+
} else if (late === null || residual < late.residual) late = {
|
|
14628
|
+
index,
|
|
14629
|
+
residual,
|
|
14630
|
+
time
|
|
14631
|
+
};
|
|
14632
|
+
}
|
|
14633
|
+
return early !== null && late !== null && late.index - early.index >= MIN_BEAT_SPAN ? (late.time - early.time) / (late.index - early.index) : rough;
|
|
14634
|
+
}
|
|
14635
|
+
/**
|
|
14636
|
+
* Draws the frame a host refresh asked for, stamped with that refresh on
|
|
14637
|
+
* the display's beat rather than with when its callback happened to run,
|
|
14638
|
+
* and on this thread's clock.
|
|
14639
|
+
*/
|
|
14640
|
+
deliverOnBeat(time) {
|
|
14641
|
+
const beat = this.beat();
|
|
14642
|
+
let refresh = time - ((time - beat.start) % beat.interval + beat.interval) % beat.interval;
|
|
14643
|
+
while (refresh < this.lastLocked + (this.stride - .5) * beat.interval) refresh += beat.interval;
|
|
14644
|
+
this.lastLocked = refresh;
|
|
14645
|
+
this.refreshesSinceFrame = 0;
|
|
14646
|
+
const started = this.now();
|
|
14647
|
+
this.deliver(this.toLocal(refresh));
|
|
14648
|
+
this.recordCost(this.now() - started, beat.interval);
|
|
14649
|
+
}
|
|
14650
|
+
/**
|
|
14651
|
+
* Keeps the stride to what frames cost: every refresh while the typical
|
|
14652
|
+
* frame fits in most of one, two once it no longer does. Decided by the
|
|
14653
|
+
* typical frame, not the slow ones: a slow frame (a few rows mounted at
|
|
14654
|
+
* once) shown a refresh late is a moment's hitch, where two refreshes a
|
|
14655
|
+
* frame for a whole scroll halves the rate. Ruled by the slow quarter,
|
|
14656
|
+
* a 165 Hz display drew at 82 with its typical frame in half a refresh.
|
|
14657
|
+
*/
|
|
14658
|
+
recordCost(cost, interval) {
|
|
14659
|
+
this.costs.push(cost);
|
|
14660
|
+
if (this.costs.length > COST_SAMPLES) this.costs.shift();
|
|
14661
|
+
if (this.costs.length < SLOW_SAMPLES) return;
|
|
14662
|
+
const sorted = [...this.costs].sort((a, b) => a - b);
|
|
14663
|
+
const typical = sorted[Math.floor(sorted.length / 2)];
|
|
14664
|
+
if (typical > interval * this.stride * SLOW_FRAME && this.stride < MAX_STRIDE) {
|
|
14665
|
+
this.stride++;
|
|
14666
|
+
this.costs.length = 0;
|
|
14667
|
+
} else if (this.stride > 1 && this.costs.length >= COST_SAMPLES && typical < interval * (this.stride - 1) * FAST_FRAME) {
|
|
14668
|
+
this.stride--;
|
|
14669
|
+
this.costs.length = 0;
|
|
14670
|
+
}
|
|
14671
|
+
}
|
|
14672
|
+
/** Refreshes per frame while timed on the display's beat; see \`recordCost\`. */
|
|
14673
|
+
get displayStride() {
|
|
14674
|
+
return this.stride;
|
|
14675
|
+
}
|
|
14676
|
+
/**
|
|
14449
14677
|
* Arms the timer that draws a frame the host did not ask for.
|
|
14450
14678
|
*
|
|
14451
14679
|
* It plays two roles with one mechanism. Before any tick has arrived
|
|
@@ -14471,7 +14699,7 @@ var UiHostFrameClock = class {
|
|
|
14471
14699
|
this.handle = null;
|
|
14472
14700
|
if (!this.pending) return;
|
|
14473
14701
|
this.stalled = this.hosted;
|
|
14474
|
-
this.deliver(this.hostTime());
|
|
14702
|
+
this.deliver(this.locked ? this.now() : this.hostTime());
|
|
14475
14703
|
}, watching ? this.watchMs() : this.selfPaceMs());
|
|
14476
14704
|
}
|
|
14477
14705
|
/**
|
|
@@ -14572,6 +14800,17 @@ var UiHostFrameClock = class {
|
|
|
14572
14800
|
this.stalled = false;
|
|
14573
14801
|
this.hostOffset = time - this.now();
|
|
14574
14802
|
this.recordCadence(time);
|
|
14803
|
+
if (this.locked && this.display !== null) {
|
|
14804
|
+
this.refreshes.push(time);
|
|
14805
|
+
if (this.refreshes.length > BEAT_SAMPLES) this.refreshes.shift();
|
|
14806
|
+
this.refreshesSinceFrame++;
|
|
14807
|
+
if (this.pending) {
|
|
14808
|
+
this.clearFallback();
|
|
14809
|
+
this.idleTicks = 0;
|
|
14810
|
+
if (this.refreshesSinceFrame >= this.stride) this.deliverOnBeat(time);
|
|
14811
|
+
return;
|
|
14812
|
+
}
|
|
14813
|
+
}
|
|
14575
14814
|
this.clearFallback();
|
|
14576
14815
|
if (!this.pending) {
|
|
14577
14816
|
this.idleTicks++;
|