@solidrt/core 0.0.49 → 0.0.51
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/AGENTS.md +23 -0
- package/README.md +1 -1
- package/agents/painting.md +61 -0
- package/agents/performance.md +216 -0
- package/examples/README.md +2 -2
- package/examples/{sound.tsx → audio.tsx} +23 -3
- package/examples/gpu-sprites.tsx +102 -0
- package/jsx-runtime.d.ts +19 -14
- package/package.json +7 -6
- package/src/{sound.ts → audio.ts} +99 -18
- package/src/color.ts +17 -18
- package/src/core.ts +88 -0
- package/src/data.ts +99 -0
- package/src/gpu.ts +32 -10
- package/src/index.ts +4 -4
- package/src/renderer.ts +43 -27
- package/src/runtime-modules.d.ts +3 -2
- package/src/scroll.ts +1 -1
- package/src/text-input.ts +297 -60
- package/src/types.d.ts +233 -5
- package/src/window.ts +58 -7
package/src/types.d.ts
CHANGED
|
@@ -131,6 +131,21 @@ export interface LayoutProps extends FlexboxProps, GridProps {
|
|
|
131
131
|
overflow?: "visible" | "clip" | "hidden" | "scroll"
|
|
132
132
|
overflowX?: "visible" | "clip" | "hidden" | "scroll"
|
|
133
133
|
overflowY?: "visible" | "clip" | "hidden" | "scroll"
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* As an inline atom (an element child of <text>): leave the flow and sit
|
|
137
|
+
* against that side of the text, at the top of the line where the atom
|
|
138
|
+
* occurs; the lines it overlaps wrap around its margin box. Same-side
|
|
139
|
+
* floats overlapping vertically sit beside each other. The text's height
|
|
140
|
+
* includes the float. Meaningless outside a <text>.
|
|
141
|
+
*/
|
|
142
|
+
float?: "left" | "right"
|
|
143
|
+
/**
|
|
144
|
+
* As an inline atom: start a new line below the text's earlier floats on
|
|
145
|
+
* that side (a floated atom goes below them instead of beside). An empty
|
|
146
|
+
* `<view clear="both" />` is the section break after an image.
|
|
147
|
+
*/
|
|
148
|
+
clear?: "left" | "right" | "both"
|
|
134
149
|
}
|
|
135
150
|
|
|
136
151
|
/** Colors are CSS color strings, parsed to a packed u32 by `parseColor`. */
|
|
@@ -221,6 +236,14 @@ export interface PointerEvent {
|
|
|
221
236
|
*/
|
|
222
237
|
parentX: number
|
|
223
238
|
parentY: number
|
|
239
|
+
/**
|
|
240
|
+
* Pointer movement since the previous move event, in logical pixels. Mouse
|
|
241
|
+
* reports hardware deltas (summed, never lost, and the only motion signal
|
|
242
|
+
* while the pointer is locked); touch reports position diffs. 0 on
|
|
243
|
+
* non-move events.
|
|
244
|
+
*/
|
|
245
|
+
movementX: number
|
|
246
|
+
movementY: number
|
|
224
247
|
/** Node id whose handler is currently running (bubbling changes it per call). */
|
|
225
248
|
currentTarget: number
|
|
226
249
|
/** Deepest node id of the event's path (the hit leaf). */
|
|
@@ -351,6 +374,154 @@ export interface LineGeometryProps {
|
|
|
351
374
|
y2?: number
|
|
352
375
|
}
|
|
353
376
|
|
|
377
|
+
// Native transitions (okf/done/native-transitions.md): declared once on
|
|
378
|
+
// the element, applied by the runtime to every later write of the covered
|
|
379
|
+
// properties. JS hands over targets; Rust interpolates every frame, so a
|
|
380
|
+
// running animation costs no JS per frame.
|
|
381
|
+
|
|
382
|
+
/** A cubic-bezier timing curve: a CSS name or [x1, y1, x2, y2] control values. */
|
|
383
|
+
export type TransitionCurve = "linear" | "ease" | "ease-in" | "ease-out" | "ease-in-out" | [number, number, number, number]
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* A perceptual spring - the default kind: a bare `{ duration }` is a
|
|
387
|
+
* critically damped spring. `duration` (ms) is the perceptual settling
|
|
388
|
+
* time, `bounce` in (-1, 1] the springiness - 0 (the default) settles
|
|
389
|
+
* without overshoot, positive values overshoot, negative values settle
|
|
390
|
+
* sluggishly. A new target while the spring runs keeps position and
|
|
391
|
+
* velocity, so the motion stays continuous - use springs for anything
|
|
392
|
+
* retargeted while moving.
|
|
393
|
+
*/
|
|
394
|
+
export interface TransitionSpring {
|
|
395
|
+
duration: number
|
|
396
|
+
bounce?: number
|
|
397
|
+
/** Hold each write for this long (ms) before it applies; a newer write during the hold replaces it and restarts the delay. */
|
|
398
|
+
delay?: number
|
|
399
|
+
/**
|
|
400
|
+
* Mount-time enter animation: at the element's first attach the property
|
|
401
|
+
* snaps to this value and animates to the value it mounted with. Numbers
|
|
402
|
+
* for the scalar properties; the color property takes a CSS color string
|
|
403
|
+
* or packed number. Per-property entries only (not under `all`); a later
|
|
404
|
+
* move or reorder re-runs nothing.
|
|
405
|
+
*/
|
|
406
|
+
from?: number | string
|
|
407
|
+
/**
|
|
408
|
+
* Removal exit animation: an unmounted element stays visible, animates
|
|
409
|
+
* the property to this value (honoring `delay`), and is freed when its
|
|
410
|
+
* exit animations settle. Same value forms as `from`, per-property only.
|
|
411
|
+
* A move never plays it, the exiting element is hit-test invisible, its
|
|
412
|
+
* whole subtree stays painted with it, and no onTransitionEnd fires (the
|
|
413
|
+
* component is already disposed). An attached element keeps its layout
|
|
414
|
+
* slot until the exit finishes.
|
|
415
|
+
*/
|
|
416
|
+
exit?: number | string
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* A duration/curve tween, opted into by naming the curve (a tween is
|
|
421
|
+
* always a specific curve; without one the spec reads as a spring).
|
|
422
|
+
* Duration in ms. A new target while the tween runs restarts it from the
|
|
423
|
+
* current value with the full duration (CSS semantics) - designer-timed,
|
|
424
|
+
* one-shot motion.
|
|
425
|
+
*/
|
|
426
|
+
export interface TransitionTween {
|
|
427
|
+
duration: number
|
|
428
|
+
curve: TransitionCurve
|
|
429
|
+
/** Hold each write for this long (ms) before it applies; a newer write during the hold replaces it and restarts the delay. */
|
|
430
|
+
delay?: number
|
|
431
|
+
/**
|
|
432
|
+
* Mount-time enter animation: at the element's first attach the property
|
|
433
|
+
* snaps to this value and animates to the value it mounted with. Numbers
|
|
434
|
+
* for the scalar properties; the color property takes a CSS color string
|
|
435
|
+
* or packed number. Per-property entries only (not under `all`); a later
|
|
436
|
+
* move or reorder re-runs nothing.
|
|
437
|
+
*/
|
|
438
|
+
from?: number | string
|
|
439
|
+
/**
|
|
440
|
+
* Removal exit animation: an unmounted element stays visible, animates
|
|
441
|
+
* the property to this value (honoring `delay`), and is freed when its
|
|
442
|
+
* exit animations settle. Same value forms as `from`, per-property only.
|
|
443
|
+
* A move never plays it, the exiting element is hit-test invisible, its
|
|
444
|
+
* whole subtree stays painted with it, and no onTransitionEnd fires (the
|
|
445
|
+
* component is already disposed). An attached element keeps its layout
|
|
446
|
+
* slot until the exit finishes.
|
|
447
|
+
*/
|
|
448
|
+
exit?: number | string
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* The shorthand string: `"<duration>ms [curve] [<delay>ms]"` - `"300ms"` is
|
|
453
|
+
* a bounce-0 spring, `"300ms ease-out"` a tween, `"300ms ease-out 100ms"`
|
|
454
|
+
* delayed (first time value the duration, second the delay; ms only).
|
|
455
|
+
* Bounce, bezier control values and `from` need the object form.
|
|
456
|
+
*/
|
|
457
|
+
export type TransitionShorthand = string
|
|
458
|
+
|
|
459
|
+
export type Transition = TransitionSpring | TransitionTween | TransitionShorthand
|
|
460
|
+
|
|
461
|
+
/** The property names a transition can cover (numeric scalars). */
|
|
462
|
+
export type TransitionPropName =
|
|
463
|
+
| "x"
|
|
464
|
+
| "y"
|
|
465
|
+
| "w"
|
|
466
|
+
| "h"
|
|
467
|
+
| "x1"
|
|
468
|
+
| "y1"
|
|
469
|
+
| "x2"
|
|
470
|
+
| "y2"
|
|
471
|
+
| "opacity"
|
|
472
|
+
| "rotate"
|
|
473
|
+
| "rotateX"
|
|
474
|
+
| "rotateY"
|
|
475
|
+
| "scale"
|
|
476
|
+
| "scaleX"
|
|
477
|
+
| "scaleY"
|
|
478
|
+
| "strokeWidth"
|
|
479
|
+
| "radius"
|
|
480
|
+
| "color"
|
|
481
|
+
|
|
482
|
+
/** Payload of onTransitionEnd: which animated property finished. */
|
|
483
|
+
export interface TransitionEndEvent {
|
|
484
|
+
property: TransitionPropName
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
export interface TransitionProps {
|
|
488
|
+
/**
|
|
489
|
+
* A runtime-side transition of one of this element's properties reached
|
|
490
|
+
* its target (natural settles only; a cancelled or retargeted animation
|
|
491
|
+
* does not fire until it finally settles). Delivered to this element
|
|
492
|
+
* only, no bubbling.
|
|
493
|
+
*/
|
|
494
|
+
onTransitionEnd?: (event: TransitionEndEvent) => void
|
|
495
|
+
/**
|
|
496
|
+
* Animate later writes of the listed properties instead of snapping:
|
|
497
|
+
* `transition={{ x: { duration: 400, bounce: 0.2 }, opacity: "200ms ease-out" }}`.
|
|
498
|
+
* `all` covers every animatable property the element has, and a bare
|
|
499
|
+
* string is shorthand for it: `transition="300ms ease-out"`. Only
|
|
500
|
+
* properties the element carries animate (a d-rect has x, a view's x is
|
|
501
|
+
* its transform); the initial value never animates unless the entry sets
|
|
502
|
+
* `from` (an enter animation), and a non-numeric write (e.g. null)
|
|
503
|
+
* cancels the running animation and snaps. `null` clears the
|
|
504
|
+
* declaration; already-running animations finish.
|
|
505
|
+
*/
|
|
506
|
+
transition?:
|
|
507
|
+
| ({
|
|
508
|
+
all?: Omit<TransitionSpring, "from" | "exit"> | Omit<TransitionTween, "from" | "exit"> | TransitionShorthand
|
|
509
|
+
/**
|
|
510
|
+
* Group stagger (ms): every descendant enter (`from`) or exit that
|
|
511
|
+
* begins in the same frame under this element gets `index * stagger`
|
|
512
|
+
* of extra delay, in occurrence order (enters and exits cascade
|
|
513
|
+
* separately). Nearest declaring ancestor wins; it orchestrates
|
|
514
|
+
* descendants only - ordinary writes and this element's own
|
|
515
|
+
* lifecycle are unaffected. Adds on top of a per-entry `delay`.
|
|
516
|
+
*/
|
|
517
|
+
stagger?: number
|
|
518
|
+
} & {
|
|
519
|
+
[P in TransitionPropName]?: Transition
|
|
520
|
+
})
|
|
521
|
+
| TransitionShorthand
|
|
522
|
+
| null
|
|
523
|
+
}
|
|
524
|
+
|
|
354
525
|
// Primitives
|
|
355
526
|
|
|
356
527
|
export interface WindowProps extends LayoutProps, PointerProps {
|
|
@@ -447,9 +618,10 @@ export interface ViewOwnProps extends TransformProps, PointerProps {
|
|
|
447
618
|
repaintBoundary?: boolean | "snapshot" | "snapshot-no-aa"
|
|
448
619
|
/**
|
|
449
620
|
* Run this view's rasterized subtree through a GPU program and composite
|
|
450
|
-
* the result in its place. Requires
|
|
451
|
-
*
|
|
452
|
-
*
|
|
621
|
+
* the result in its place. Requires a snapshot boundary
|
|
622
|
+
* (repaintBoundary="snapshot" or "snapshot-no-aa"; the cost is snapshot
|
|
623
|
+
* semantics, kept explicit; declared without one the shader is ignored
|
|
624
|
+
* with a warning). The pass is region-sized and split from content
|
|
453
625
|
* invalidation: a params-only change re-runs just the pass against the
|
|
454
626
|
* cached snapshot, so animating an effect over a static subtree never
|
|
455
627
|
* re-rasterizes it.
|
|
@@ -545,8 +717,12 @@ export interface PathProps extends PaintProps, PointerProps {
|
|
|
545
717
|
fillRule?: "nonzero" | "evenodd"
|
|
546
718
|
}
|
|
547
719
|
|
|
548
|
-
|
|
549
|
-
|
|
720
|
+
/**
|
|
721
|
+
* Per-run text style: the paragraph default on <text>, an override on <span>.
|
|
722
|
+
* The cascade is intra-paragraph only: a span inherits from its enclosing
|
|
723
|
+
* span, then from the <text>; nothing inherits across the tree.
|
|
724
|
+
*/
|
|
725
|
+
export interface TextRunProps {
|
|
550
726
|
fontFamily?: "sans" | "serif" | "mono" | (string & {})
|
|
551
727
|
fontSize?: number
|
|
552
728
|
/**
|
|
@@ -557,8 +733,60 @@ export interface TextProps extends PaintProps, PointerProps {
|
|
|
557
733
|
lineHeight?: number
|
|
558
734
|
fontStyle?: "normal" | "italic"
|
|
559
735
|
fontWeight?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
|
|
736
|
+
/**
|
|
737
|
+
* Underline in the run's own color, drawn straight through descenders
|
|
738
|
+
* (no skip-ink). Position and thickness come from the font's own metrics
|
|
739
|
+
* unless overridden; a font Impeller resolves through the system fallback
|
|
740
|
+
* gets the shipped Noto values.
|
|
741
|
+
*/
|
|
742
|
+
textDecoration?: "none" | "underline"
|
|
743
|
+
/** Pixels from the baseline to the top of the underline. */
|
|
744
|
+
textUnderlineOffset?: number
|
|
745
|
+
/** Underline thickness in pixels. */
|
|
746
|
+
textDecorationThickness?: number
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* A styled run inside <text>. Inline text only: children are text and other
|
|
751
|
+
* spans. `color` takes what a text's color takes (solid or gradient).
|
|
752
|
+
* Pointer handlers fire for the boxes of the run's own text on each line it
|
|
753
|
+
* spans and bubble to the enclosing spans and text.
|
|
754
|
+
*/
|
|
755
|
+
export interface SpanProps extends TextRunProps, PointerProps {
|
|
756
|
+
children?: Children
|
|
757
|
+
color?: Color | Gradient
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
export interface TextProps extends PaintProps, PointerProps, TextRunProps {
|
|
761
|
+
children?: Children
|
|
560
762
|
textAlign?: "left" | "right" | "center" | "justify"
|
|
561
763
|
maxLines?: number
|
|
764
|
+
/**
|
|
765
|
+
* What happens to text cut off by maxLines: "clip" (default), "ellipsis"
|
|
766
|
+
* (a U+2026 at the end of the last line), or any other string to use as
|
|
767
|
+
* the ellipsis. Drawn in the paragraph's default style.
|
|
768
|
+
*/
|
|
769
|
+
textOverflow?: "clip" | "ellipsis" | (string & {})
|
|
770
|
+
/**
|
|
771
|
+
* A word (wrap unit) wider than the line: "anywhere" (default) splits it
|
|
772
|
+
* at grapheme boundaries so it stays inside the box, "normal" keeps it
|
|
773
|
+
* whole and lets it overflow (CSS's default).
|
|
774
|
+
*/
|
|
775
|
+
overflowWrap?: "normal" | "anywhere"
|
|
776
|
+
/**
|
|
777
|
+
* First-line indent in pixels. Negative hangs: the first line starts at 0
|
|
778
|
+
* and every following line is indented by the magnitude. A hard break does
|
|
779
|
+
* not start a new first line.
|
|
780
|
+
*/
|
|
781
|
+
textIndent?: number
|
|
782
|
+
/**
|
|
783
|
+
* How lines are chosen beyond greedy fitting (CSS text-wrap): "wrap"
|
|
784
|
+
* (default) is greedy; "balance" evens the line lengths while keeping the
|
|
785
|
+
* line count (headings, captions); "pretty" is greedy except that a lone
|
|
786
|
+
* word on the last line pulls one down from the line above. Neither
|
|
787
|
+
* applies once maxLines truncates.
|
|
788
|
+
*/
|
|
789
|
+
textWrap?: "wrap" | "balance" | "pretty"
|
|
562
790
|
}
|
|
563
791
|
|
|
564
792
|
/**
|
package/src/window.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createSignal, onCleanup, onSettled, flush } from "@solidjs/signals"
|
|
2
|
-
import { requestFrame } from "flux:rendertree"
|
|
2
|
+
import { requestFrame, setPointerLock } from "flux:rendertree"
|
|
3
3
|
import { renderFrame } from "srt:render"
|
|
4
4
|
import { on, once } from "srt:events"
|
|
5
5
|
import { exit } from "srt:app"
|
|
@@ -38,9 +38,12 @@ let refreshRate = 60
|
|
|
38
38
|
* the present count, and `rate` is the current refresh rate in Hz. `tick` is paced
|
|
39
39
|
* by the runtime (one refresh period per present, slow-corrected toward the wall
|
|
40
40
|
* clock) so animations driven off it stay smooth even when swap-return times
|
|
41
|
-
* jitter
|
|
42
|
-
*
|
|
43
|
-
*
|
|
41
|
+
* jitter, and it is continuous across hot reloads: every tick an instance
|
|
42
|
+
* sees is on one timebase, so dt is well-defined from the second call on.
|
|
43
|
+
* Timers freeze together with frame callbacks under the dev tools' clock
|
|
44
|
+
* control, but measure their delays against the wall clock, not this paced
|
|
45
|
+
* timeline. performance.now() is not on it either: it is real elapsed time
|
|
46
|
+
* for measuring work; Date.now() is calendar time.
|
|
44
47
|
* Returns a cleanup function; also auto-cleans within a reactive scope.
|
|
45
48
|
*/
|
|
46
49
|
export function onFrame(fn: (tick: number, frame: number, rate: number) => void) {
|
|
@@ -161,6 +164,33 @@ export function windowFocused(): boolean {
|
|
|
161
164
|
return focusedAccessor()
|
|
162
165
|
}
|
|
163
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Lock the pointer to the window (relative mouse mode) or release it. While
|
|
169
|
+
* locked the cursor is hidden and confined, clientX/clientY freeze at the
|
|
170
|
+
* lock point, and mouse motion keeps reporting through the pointer events'
|
|
171
|
+
* movementX/movementY - the mouse-look primitive. Window-level, no
|
|
172
|
+
* permission dance: one app, one window. Observe the applied state through
|
|
173
|
+
* pointerLocked(); the platform can refuse (no relative mode) and the OS
|
|
174
|
+
* may drop the lock, e.g. on focus loss.
|
|
175
|
+
*/
|
|
176
|
+
export function lockPointer(locked: boolean) {
|
|
177
|
+
if (typeof locked !== "boolean") throw new Error(`lockPointer: expected a boolean, got ${typeof locked}`)
|
|
178
|
+
setPointerLock(locked)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
let pointerLockedAccessor: (() => boolean) | undefined
|
|
182
|
+
|
|
183
|
+
/** Whether the pointer is currently locked (relative mouse mode), as a reactive accessor. */
|
|
184
|
+
export function pointerLocked(): boolean {
|
|
185
|
+
if (!pointerLockedAccessor) {
|
|
186
|
+
let [locked, setLocked] = createSignal(false)
|
|
187
|
+
// Sticky event: a subscriber after the lock still observes the state.
|
|
188
|
+
on("pointerLock", ({ locked }: { locked: boolean }) => setLocked(locked))
|
|
189
|
+
pointerLockedAccessor = locked
|
|
190
|
+
}
|
|
191
|
+
return pointerLockedAccessor()
|
|
192
|
+
}
|
|
193
|
+
|
|
164
194
|
let keyboardHeightAccessor: (() => number) | undefined
|
|
165
195
|
|
|
166
196
|
/**
|
|
@@ -255,6 +285,7 @@ export function attachWindow(nodeId: number) {
|
|
|
255
285
|
let unsubEnter: () => void = null!
|
|
256
286
|
let unsubLeave: () => void = null!
|
|
257
287
|
let unsubWheel: () => void = null!
|
|
288
|
+
let unsubTransitionEnd: () => void = null!
|
|
258
289
|
let unsubKeyDown: () => void = null!
|
|
259
290
|
let unsubKeyUp: () => void = null!
|
|
260
291
|
let unsubBack: () => void = null!
|
|
@@ -263,8 +294,16 @@ export function attachWindow(nodeId: number) {
|
|
|
263
294
|
let unsubRefreshRate: () => void = null!
|
|
264
295
|
let unsubFirstResize: (() => void) | null = null
|
|
265
296
|
|
|
266
|
-
|
|
267
|
-
|
|
297
|
+
// `bootstrap` marks the synthetic first frame (see the first-resize
|
|
298
|
+
// subscription below): it flushes and paints the freshly initialized graph
|
|
299
|
+
// but does not invoke onFrame callbacks, because its timestamp is not a
|
|
300
|
+
// reading of the frame timeline - handing apps a zero tick gave every
|
|
301
|
+
// reloaded instance one enormous dt on the next real frame
|
|
302
|
+
// (okf/done/onframe-tick-reset-on-reload.md). The callbacks stay
|
|
303
|
+
// registered and run on the first real render event, before the first
|
|
304
|
+
// paint with their writes applied.
|
|
305
|
+
function runFrame(t: number, frame: number, bootstrap = false) {
|
|
306
|
+
if (!bootstrap && animationFrames.size > 0) {
|
|
268
307
|
let frames = animationFrames
|
|
269
308
|
animationFrames = new Map()
|
|
270
309
|
for (let fn of frames.values()) fn(t, frame, refreshRate)
|
|
@@ -368,6 +407,17 @@ export function attachWindow(nodeId: number) {
|
|
|
368
407
|
bubble(raw, "onWheel")
|
|
369
408
|
})
|
|
370
409
|
|
|
410
|
+
// A native transition finished (runtime-side animation; see the
|
|
411
|
+
// `transition` prop). Target-only delivery, no bubbling: the element
|
|
412
|
+
// that declared the transition is the one interested in its end.
|
|
413
|
+
unsubTransitionEnd = on("transitionEnd", (raw: { target: number; property: string }) => {
|
|
414
|
+
try {
|
|
415
|
+
getEventHandler(raw.target, "onTransitionEnd")?.({ property: raw.property })
|
|
416
|
+
} catch (err) {
|
|
417
|
+
console.error("Error in onTransitionEnd handler:", err)
|
|
418
|
+
}
|
|
419
|
+
})
|
|
420
|
+
|
|
371
421
|
// Key events dispatch along the focused node's ancestor chain, leaf->root
|
|
372
422
|
// (the pointer bubbling contract), so a container hears keys from focused
|
|
373
423
|
// descendants and the window root hears everything: <window onKeyDown> is
|
|
@@ -429,7 +479,7 @@ export function attachWindow(nodeId: number) {
|
|
|
429
479
|
// where flush() is illegal (not reentrant). Defer runFrame to a microtask
|
|
430
480
|
// so the first frame always runs after this callback returns.
|
|
431
481
|
unsubFirstResize = once("resize", () => {
|
|
432
|
-
queueMicrotask(() => runFrame(0, 0))
|
|
482
|
+
queueMicrotask(() => runFrame(0, 0, true))
|
|
433
483
|
})
|
|
434
484
|
})
|
|
435
485
|
|
|
@@ -442,6 +492,7 @@ export function attachWindow(nodeId: number) {
|
|
|
442
492
|
if (unsubEnter) unsubEnter()
|
|
443
493
|
if (unsubLeave) unsubLeave()
|
|
444
494
|
if (unsubWheel) unsubWheel()
|
|
495
|
+
if (unsubTransitionEnd) unsubTransitionEnd()
|
|
445
496
|
if (unsubKeyDown) unsubKeyDown()
|
|
446
497
|
if (unsubKeyUp) unsubKeyUp()
|
|
447
498
|
if (unsubBack) unsubBack()
|