@moku-labs/game 0.15.32 → 0.15.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/page.d.mts +1 -1
- package/dist/app/page.mjs +1 -1
- package/dist/app/system.d.mts +2 -2
- package/dist/app.d.mts +2 -2
- package/dist/app.mjs +1 -1
- package/dist/{commands-C8N_rpwt.d.mts → commands-DSYOsn8r.d.mts} +8 -8
- package/dist/control.d.mts +1 -1
- package/dist/control.mjs +1 -1
- package/dist/{index-DMeqKGSu.d.mts → index-ZPmKRr0j.d.mts} +2 -2
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +1 -1
- package/dist/inspect.d.mts +4 -4
- package/dist/inspect.mjs +1 -1
- package/dist/jsx-dev-runtime.d.mts +1 -1
- package/dist/jsx-runtime.d.mts +1 -1
- package/dist/{run-BnriyKUu.mjs → run-G4m1APMv.mjs} +3 -12
- package/dist/{runtime-HwTBjkIL.d.mts → runtime-CgJuBIYZ.d.mts} +1 -1
- package/dist/{sources-DM5Zcl4o.mjs → sources-MfRpM8sV.mjs} +2 -5
- package/dist/{src-5qdNjLKN.mjs → src-DFhuDrm7.mjs} +544 -133
- package/dist/testing.d.mts +2 -2
- package/dist/testing.mjs +1 -1
- package/dist/{types-DHamyZq1.d.mts → types-CrZvIBB9.d.mts} +3 -2
- package/dist/{types-BAp9HdR4.d.mts → types-DGHVGP5i.d.mts} +33 -25
- package/dist/{types-DmU_qlvA.d.mts → types-DrtsLNnT.d.mts} +3 -3
- package/dist/{visual-DEo5Hndx.d.mts → visual-CYxR9E2v.d.mts} +3 -3
- package/dist/visual.d.mts +1 -1
- package/dist/visual.mjs +2 -2
- package/llms.txt +1 -1
- package/package.json +1 -1
|
@@ -27678,7 +27678,7 @@ function writeBlend(sctx, entity, object, blend) {
|
|
|
27678
27678
|
* @param texture - The texture to draw, or `undefined` to keep the one it has.
|
|
27679
27679
|
* @param tint - The tint.
|
|
27680
27680
|
*/
|
|
27681
|
-
function writeLook(sctx, entity, object, texture, tint) {
|
|
27681
|
+
function writeLook$1(sctx, entity, object, texture, tint) {
|
|
27682
27682
|
if (texture !== void 0 && object.texture !== texture) {
|
|
27683
27683
|
markView(sctx, entity);
|
|
27684
27684
|
object.texture = texture;
|
|
@@ -27701,7 +27701,7 @@ function writeLook(sctx, entity, object, texture, tint) {
|
|
|
27701
27701
|
*/
|
|
27702
27702
|
function drawPlaceholder(sctx, entity, object, view, value, texture) {
|
|
27703
27703
|
const box = boxInto(LAYOUT.box, PLACEHOLDER_BOX, value);
|
|
27704
|
-
writeLook(sctx, entity, object, texture, PLACEHOLDER_TINT);
|
|
27704
|
+
writeLook$1(sctx, entity, object, texture, PLACEHOLDER_TINT);
|
|
27705
27705
|
writePoint(sctx, entity, object.anchor, value.anchor.x, value.anchor.y);
|
|
27706
27706
|
view.drawScale.x = box.width;
|
|
27707
27707
|
view.drawScale.y = box.height;
|
|
@@ -27842,10 +27842,10 @@ function writeSprite(sctx, entity, view) {
|
|
|
27842
27842
|
const layout = layoutSpriteInto(LAYOUT, texture, value);
|
|
27843
27843
|
if (layout.cover) {
|
|
27844
27844
|
const cover = coverTexture(sctx, entity, value.texture, texture, layout.box, layout.crop);
|
|
27845
|
-
writeLook(sctx, entity, object, cover.texture, value.tint);
|
|
27845
|
+
writeLook$1(sctx, entity, object, cover.texture, value.tint);
|
|
27846
27846
|
showCover(sctx, entity, view, cover.frameKey);
|
|
27847
27847
|
} else {
|
|
27848
|
-
writeLook(sctx, entity, object, texture, value.tint);
|
|
27848
|
+
writeLook$1(sctx, entity, object, texture, value.tint);
|
|
27849
27849
|
showCover(sctx, entity, view, "");
|
|
27850
27850
|
}
|
|
27851
27851
|
writePoint(sctx, entity, object.anchor, layout.anchor.x, layout.anchor.y);
|
|
@@ -33535,6 +33535,145 @@ function dropEnded(list) {
|
|
|
33535
33535
|
while (list.length > kept) list.pop();
|
|
33536
33536
|
}
|
|
33537
33537
|
//#endregion
|
|
33538
|
+
//#region src/plugins/anim/timeline/checks.ts
|
|
33539
|
+
/**
|
|
33540
|
+
* Checks the keyframe segments of a `tween` step: at least one, every `at` in 0..1, each after the
|
|
33541
|
+
* one before, and the last at 1, because `to` is where the walk lands.
|
|
33542
|
+
*
|
|
33543
|
+
* @param component - Name of the component the step drives, for the error.
|
|
33544
|
+
* @param segments - The segments the author gave.
|
|
33545
|
+
* @throws {Error} When the list is empty, an `at` is out of range or out of order, or the last
|
|
33546
|
+
* segment ends before 1.
|
|
33547
|
+
* @example
|
|
33548
|
+
* ```ts
|
|
33549
|
+
* checkSegments("Transform", [{ at: 0.5 }, { at: 1 }]); // returns; [{ at: 0.5 }] throws
|
|
33550
|
+
* ```
|
|
33551
|
+
*/
|
|
33552
|
+
function checkSegments(component, segments) {
|
|
33553
|
+
if (segments.length === 0) throw new Error(`[game] A tween of ${component} has no segments.\n Give it at least one segment, the last one at 1.`);
|
|
33554
|
+
let previous = Number.NEGATIVE_INFINITY;
|
|
33555
|
+
for (const { at } of segments) {
|
|
33556
|
+
if (Number.isNaN(at) || at < 0 || at > 1) throw new Error(`[game] A tween of ${component} has a segment at ${at}.\n Keep every at between 0 and 1.`);
|
|
33557
|
+
if (at <= previous) throw new Error(`[game] A tween of ${component} has its segments out of order at ${at}.\n Sort the segments by at, each one after the one before.`);
|
|
33558
|
+
previous = at;
|
|
33559
|
+
}
|
|
33560
|
+
if (previous !== 1) throw new Error(`[game] A tween of ${component} ends its last segment at ${previous}.\n End the last segment at 1: to is where the walk lands.`);
|
|
33561
|
+
}
|
|
33562
|
+
/**
|
|
33563
|
+
* Checks the space of a keyframe walk: an additive one names offsets, and the root space would
|
|
33564
|
+
* turn each offset as if it were a root position, so a chest under a slot at root y 500 would jump
|
|
33565
|
+
* by about 500 at its first segment. An additive walk stays in the local space.
|
|
33566
|
+
*
|
|
33567
|
+
* @param component - Name of the component the step drives, for the error.
|
|
33568
|
+
* @param additive - Whether the walk adds offsets.
|
|
33569
|
+
* @param space - The space the step names.
|
|
33570
|
+
* @throws {Error} When an additive walk names the root space.
|
|
33571
|
+
* @example
|
|
33572
|
+
* ```ts
|
|
33573
|
+
* checkWalkSpace("Transform", true, "local"); // returns; (…, true, "root") throws
|
|
33574
|
+
* ```
|
|
33575
|
+
*/
|
|
33576
|
+
function checkWalkSpace(component, additive, space) {
|
|
33577
|
+
if (!additive || space === "local") return;
|
|
33578
|
+
throw new Error(`[game] An additive tween of ${component} walks segments in the root space.\n Use space "local" for an additive walk.`);
|
|
33579
|
+
}
|
|
33580
|
+
/**
|
|
33581
|
+
* Checks the repeat of a `tween` step: a whole number of extra runs, 0 or more, or `"forever"`.
|
|
33582
|
+
*
|
|
33583
|
+
* @param component - Name of the component the step drives, for the error.
|
|
33584
|
+
* @param repeat - The repeat the author gave.
|
|
33585
|
+
* @throws {Error} When the repeat is negative, not whole or not a number.
|
|
33586
|
+
* @example
|
|
33587
|
+
* ```ts
|
|
33588
|
+
* checkRepeat("Transform", "forever"); // returns; checkRepeat("Transform", 1.5) throws
|
|
33589
|
+
* ```
|
|
33590
|
+
*/
|
|
33591
|
+
function checkRepeat(component, repeat) {
|
|
33592
|
+
if (repeat === "forever" || Number.isInteger(repeat) && repeat >= 0) return;
|
|
33593
|
+
throw new Error(`[game] A tween of ${component} has repeat ${repeat}.\n Give repeat a whole number of extra runs, 0 or more, or "forever".`);
|
|
33594
|
+
}
|
|
33595
|
+
/**
|
|
33596
|
+
* Tells whether a step never ends by itself: a `tween` that repeats forever, a `parallel` or a
|
|
33597
|
+
* `sequence` that holds one, and a `use` of one. Only a cancel or a finish ends it.
|
|
33598
|
+
*
|
|
33599
|
+
* @param step - The step to look into.
|
|
33600
|
+
* @returns True when the step never ends by itself.
|
|
33601
|
+
* @example
|
|
33602
|
+
* ```ts
|
|
33603
|
+
* neverEnds(wait(100)); // false
|
|
33604
|
+
* neverEnds(tween(1_048_576, Transform, { y: 0 }, { ms: 2400, repeat: "forever" })); // true
|
|
33605
|
+
* ```
|
|
33606
|
+
*/
|
|
33607
|
+
function neverEnds(step) {
|
|
33608
|
+
switch (step.kind) {
|
|
33609
|
+
case "tween": return step.repeat === "forever";
|
|
33610
|
+
case "sequence":
|
|
33611
|
+
case "parallel": return step.steps.some((child) => neverEnds(child));
|
|
33612
|
+
case "use": return neverEnds(step.step);
|
|
33613
|
+
default: return false;
|
|
33614
|
+
}
|
|
33615
|
+
}
|
|
33616
|
+
/**
|
|
33617
|
+
* Checks the children of a `sequence`: a child that never ends may only be the last one, since
|
|
33618
|
+
* the children after it would never play. A `parallel` takes one anywhere.
|
|
33619
|
+
*
|
|
33620
|
+
* @param steps - The children, in order.
|
|
33621
|
+
* @throws {Error} When a child that never ends has another child after it.
|
|
33622
|
+
* @example
|
|
33623
|
+
* ```ts
|
|
33624
|
+
* checkSequence([wait(100), mark("landed")]); // returns; a forever tween before the mark throws
|
|
33625
|
+
* ```
|
|
33626
|
+
*/
|
|
33627
|
+
function checkSequence(steps) {
|
|
33628
|
+
for (let index = 0; index < steps.length - 1; index++) {
|
|
33629
|
+
const step = steps[index];
|
|
33630
|
+
if (step !== void 0 && neverEnds(step)) throw new Error(`[game] Step ${index + 1} of a sequence never ends, so the steps after it would never play.\n Put the forever step last, or run it in a parallel.`);
|
|
33631
|
+
}
|
|
33632
|
+
}
|
|
33633
|
+
/**
|
|
33634
|
+
* Tells whether a step loops a frame list for ever: a `frames` step with `loop`, or a `parallel`,
|
|
33635
|
+
* a `sequence` or a `use` that holds one where it never lets go.
|
|
33636
|
+
*
|
|
33637
|
+
* @param step - The step to look into.
|
|
33638
|
+
* @returns True when a looping frame list keeps the step from ending.
|
|
33639
|
+
* @example
|
|
33640
|
+
* ```ts
|
|
33641
|
+
* loopsFrames(frames(1_048_576, { keys: ["fx.spark-1"], fps: 12, loop: true })); // true
|
|
33642
|
+
* ```
|
|
33643
|
+
*/
|
|
33644
|
+
function loopsFrames(step) {
|
|
33645
|
+
switch (step.kind) {
|
|
33646
|
+
case "frames": return step.loop;
|
|
33647
|
+
case "parallel": return step.steps.some((child) => loopsFrames(child));
|
|
33648
|
+
case "sequence": {
|
|
33649
|
+
const last = step.steps.at(-1);
|
|
33650
|
+
return last !== void 0 && loopsFrames(last);
|
|
33651
|
+
}
|
|
33652
|
+
case "use": return loopsFrames(step.step);
|
|
33653
|
+
default: return false;
|
|
33654
|
+
}
|
|
33655
|
+
}
|
|
33656
|
+
/**
|
|
33657
|
+
* Tells whether a step tree holds a `sequence` whose step before the last loops a frame list for
|
|
33658
|
+
* ever, so the steps after it never play. A forever `tween` there already throws when it is built;
|
|
33659
|
+
* a looping `frames` only warns, while games written before the rule are fixed.
|
|
33660
|
+
*
|
|
33661
|
+
* @param step - The root of the tree.
|
|
33662
|
+
* @returns True when a looping frame list blocks the rest of a sequence somewhere in the tree.
|
|
33663
|
+
* @example
|
|
33664
|
+
* ```ts
|
|
33665
|
+
* loopBlocksSequence(sequence(frames(1_048_576, { keys: ["fx.spark-1"], fps: 12, loop: true }), mark("lit"))); // true
|
|
33666
|
+
* ```
|
|
33667
|
+
*/
|
|
33668
|
+
function loopBlocksSequence(step) {
|
|
33669
|
+
switch (step.kind) {
|
|
33670
|
+
case "sequence": return step.steps.slice(0, -1).some((child) => loopsFrames(child)) || step.steps.some((child) => loopBlocksSequence(child));
|
|
33671
|
+
case "parallel": return step.steps.some((child) => loopBlocksSequence(child));
|
|
33672
|
+
case "use": return loopBlocksSequence(step.step);
|
|
33673
|
+
default: return false;
|
|
33674
|
+
}
|
|
33675
|
+
}
|
|
33676
|
+
//#endregion
|
|
33538
33677
|
//#region src/plugins/anim/timeline/play.ts
|
|
33539
33678
|
/**
|
|
33540
33679
|
* @file anim/timeline — one running timeline: the bookkeeping around a cursor. It builds the step
|
|
@@ -33640,6 +33779,20 @@ function endTimeline(actx, running, finished) {
|
|
|
33640
33779
|
running.resolve();
|
|
33641
33780
|
}
|
|
33642
33781
|
/**
|
|
33782
|
+
* Warns once per animation, in a dev build, when a looping `frames` step stands before another step of a `sequence`:
|
|
33783
|
+
* the steps after it never play. A warning, not an error, while the games written before the rule are fixed.
|
|
33784
|
+
*
|
|
33785
|
+
* @param actx - Domain context of the anim plugin.
|
|
33786
|
+
* @param animation - The id of the animation that is played.
|
|
33787
|
+
* @param step - Its built step tree.
|
|
33788
|
+
*/
|
|
33789
|
+
function warnLoopBlocks(actx, animation, step) {
|
|
33790
|
+
if (typeof __MOKU_GAME_DEV__ === "undefined" || !__MOKU_GAME_DEV__) return;
|
|
33791
|
+
if (actx.state.warnedLoops.has(animation) || !loopBlocksSequence(step)) return;
|
|
33792
|
+
actx.state.warnedLoops.add(animation);
|
|
33793
|
+
actx.log.warn("anim:loop-blocks-sequence", { animation });
|
|
33794
|
+
}
|
|
33795
|
+
/**
|
|
33643
33796
|
* Builds the step tree of an animation with the given targets and starts it. The build runs here,
|
|
33644
33797
|
* once per play, so the same animation may play twice at once, each play with its own spawn table.
|
|
33645
33798
|
* Every `{ projection, key }` target is resolved here, once: each step writes the entity that held
|
|
@@ -33656,6 +33809,7 @@ function startTimeline(actx, rt, definition, slots) {
|
|
|
33656
33809
|
const spawned = emptySpawnTable();
|
|
33657
33810
|
const step = definition.build(slots, { at: (target) => restPoseOf(actx, target, spawned) });
|
|
33658
33811
|
assertUniqueSpawns(definition.id, step);
|
|
33812
|
+
warnLoopBlocks(actx, definition.id, step);
|
|
33659
33813
|
const id = actx.state.nextId;
|
|
33660
33814
|
actx.state.nextId += 1;
|
|
33661
33815
|
const cursor = createCursor(step);
|
|
@@ -35220,7 +35374,8 @@ function createAnimState(ctx) {
|
|
|
35220
35374
|
framesHeld: emptyMap$5(),
|
|
35221
35375
|
frameMutes: emptyMap$5(),
|
|
35222
35376
|
offFrames: [],
|
|
35223
|
-
loopsHeld: void 0
|
|
35377
|
+
loopsHeld: void 0,
|
|
35378
|
+
warnedLoops: emptySet$3()
|
|
35224
35379
|
};
|
|
35225
35380
|
}
|
|
35226
35381
|
/**
|
|
@@ -49917,102 +50072,6 @@ function createHandlers$1(ctx) {
|
|
|
49917
50072
|
} };
|
|
49918
50073
|
}
|
|
49919
50074
|
//#endregion
|
|
49920
|
-
//#region src/plugins/anim/timeline/checks.ts
|
|
49921
|
-
/**
|
|
49922
|
-
* Checks the keyframe segments of a `tween` step: at least one, every `at` in 0..1, each after the
|
|
49923
|
-
* one before, and the last at 1, because `to` is where the walk lands.
|
|
49924
|
-
*
|
|
49925
|
-
* @param component - Name of the component the step drives, for the error.
|
|
49926
|
-
* @param segments - The segments the author gave.
|
|
49927
|
-
* @throws {Error} When the list is empty, an `at` is out of range or out of order, or the last
|
|
49928
|
-
* segment ends before 1.
|
|
49929
|
-
* @example
|
|
49930
|
-
* ```ts
|
|
49931
|
-
* checkSegments("Transform", [{ at: 0.5 }, { at: 1 }]); // returns; [{ at: 0.5 }] throws
|
|
49932
|
-
* ```
|
|
49933
|
-
*/
|
|
49934
|
-
function checkSegments(component, segments) {
|
|
49935
|
-
if (segments.length === 0) throw new Error(`[game] A tween of ${component} has no segments.\n Give it at least one segment, the last one at 1.`);
|
|
49936
|
-
let previous = Number.NEGATIVE_INFINITY;
|
|
49937
|
-
for (const { at } of segments) {
|
|
49938
|
-
if (Number.isNaN(at) || at < 0 || at > 1) throw new Error(`[game] A tween of ${component} has a segment at ${at}.\n Keep every at between 0 and 1.`);
|
|
49939
|
-
if (at <= previous) throw new Error(`[game] A tween of ${component} has its segments out of order at ${at}.\n Sort the segments by at, each one after the one before.`);
|
|
49940
|
-
previous = at;
|
|
49941
|
-
}
|
|
49942
|
-
if (previous !== 1) throw new Error(`[game] A tween of ${component} ends its last segment at ${previous}.\n End the last segment at 1: to is where the walk lands.`);
|
|
49943
|
-
}
|
|
49944
|
-
/**
|
|
49945
|
-
* Checks the space of a keyframe walk: an additive one names offsets, and the root space would
|
|
49946
|
-
* turn each offset as if it were a root position, so a chest under a slot at root y 500 would jump
|
|
49947
|
-
* by about 500 at its first segment. An additive walk stays in the local space.
|
|
49948
|
-
*
|
|
49949
|
-
* @param component - Name of the component the step drives, for the error.
|
|
49950
|
-
* @param additive - Whether the walk adds offsets.
|
|
49951
|
-
* @param space - The space the step names.
|
|
49952
|
-
* @throws {Error} When an additive walk names the root space.
|
|
49953
|
-
* @example
|
|
49954
|
-
* ```ts
|
|
49955
|
-
* checkWalkSpace("Transform", true, "local"); // returns; (…, true, "root") throws
|
|
49956
|
-
* ```
|
|
49957
|
-
*/
|
|
49958
|
-
function checkWalkSpace(component, additive, space) {
|
|
49959
|
-
if (!additive || space === "local") return;
|
|
49960
|
-
throw new Error(`[game] An additive tween of ${component} walks segments in the root space.\n Use space "local" for an additive walk.`);
|
|
49961
|
-
}
|
|
49962
|
-
/**
|
|
49963
|
-
* Checks the repeat of a `tween` step: a whole number of extra runs, 0 or more, or `"forever"`.
|
|
49964
|
-
*
|
|
49965
|
-
* @param component - Name of the component the step drives, for the error.
|
|
49966
|
-
* @param repeat - The repeat the author gave.
|
|
49967
|
-
* @throws {Error} When the repeat is negative, not whole or not a number.
|
|
49968
|
-
* @example
|
|
49969
|
-
* ```ts
|
|
49970
|
-
* checkRepeat("Transform", "forever"); // returns; checkRepeat("Transform", 1.5) throws
|
|
49971
|
-
* ```
|
|
49972
|
-
*/
|
|
49973
|
-
function checkRepeat(component, repeat) {
|
|
49974
|
-
if (repeat === "forever" || Number.isInteger(repeat) && repeat >= 0) return;
|
|
49975
|
-
throw new Error(`[game] A tween of ${component} has repeat ${repeat}.\n Give repeat a whole number of extra runs, 0 or more, or "forever".`);
|
|
49976
|
-
}
|
|
49977
|
-
/**
|
|
49978
|
-
* Tells whether a step never ends by itself: a `tween` that repeats forever, a `parallel` or a
|
|
49979
|
-
* `sequence` that holds one, and a `use` of one. Only a cancel or a finish ends it.
|
|
49980
|
-
*
|
|
49981
|
-
* @param step - The step to look into.
|
|
49982
|
-
* @returns True when the step never ends by itself.
|
|
49983
|
-
* @example
|
|
49984
|
-
* ```ts
|
|
49985
|
-
* neverEnds(wait(100)); // false
|
|
49986
|
-
* neverEnds(tween(1_048_576, Transform, { y: 0 }, { ms: 2400, repeat: "forever" })); // true
|
|
49987
|
-
* ```
|
|
49988
|
-
*/
|
|
49989
|
-
function neverEnds(step) {
|
|
49990
|
-
switch (step.kind) {
|
|
49991
|
-
case "tween": return step.repeat === "forever";
|
|
49992
|
-
case "sequence":
|
|
49993
|
-
case "parallel": return step.steps.some((child) => neverEnds(child));
|
|
49994
|
-
case "use": return neverEnds(step.step);
|
|
49995
|
-
default: return false;
|
|
49996
|
-
}
|
|
49997
|
-
}
|
|
49998
|
-
/**
|
|
49999
|
-
* Checks the children of a `sequence`: a child that never ends may only be the last one, since
|
|
50000
|
-
* the children after it would never play. A `parallel` takes one anywhere.
|
|
50001
|
-
*
|
|
50002
|
-
* @param steps - The children, in order.
|
|
50003
|
-
* @throws {Error} When a child that never ends has another child after it.
|
|
50004
|
-
* @example
|
|
50005
|
-
* ```ts
|
|
50006
|
-
* checkSequence([wait(100), mark("landed")]); // returns; a forever tween before the mark throws
|
|
50007
|
-
* ```
|
|
50008
|
-
*/
|
|
50009
|
-
function checkSequence(steps) {
|
|
50010
|
-
for (let index = 0; index < steps.length - 1; index++) {
|
|
50011
|
-
const step = steps[index];
|
|
50012
|
-
if (step !== void 0 && neverEnds(step)) throw new Error(`[game] Step ${index + 1} of a sequence never ends, so the steps after it would never play.\n Put the forever step last, or run it in a parallel.`);
|
|
50013
|
-
}
|
|
50014
|
-
}
|
|
50015
|
-
//#endregion
|
|
50016
50075
|
//#region src/plugins/anim/timeline/steps.ts
|
|
50017
50076
|
/**
|
|
50018
50077
|
* Every haptic tick `haptic(kind)` takes, in one frozen list: the three impacts, the selection
|
|
@@ -56667,6 +56726,243 @@ function forgetInstances(state, identity) {
|
|
|
56667
56726
|
for (const key of state.instances.keys()) if (key === identity || key.startsWith(`${identity}|`)) state.instances.delete(key);
|
|
56668
56727
|
}
|
|
56669
56728
|
//#endregion
|
|
56729
|
+
//#region src/plugins/ui/jsx/look.ts
|
|
56730
|
+
/**
|
|
56731
|
+
* What moved, from whether the numbers and the other fields of a look did.
|
|
56732
|
+
*
|
|
56733
|
+
* @param numbers - A numeric field moved.
|
|
56734
|
+
* @param others - Another field moved.
|
|
56735
|
+
* @returns What moved.
|
|
56736
|
+
* @example
|
|
56737
|
+
* ```ts
|
|
56738
|
+
* moveOf(true, false); // "numbers"
|
|
56739
|
+
* ```
|
|
56740
|
+
*/
|
|
56741
|
+
function moveOf(numbers, others) {
|
|
56742
|
+
if (numbers) return others ? "both" : "numbers";
|
|
56743
|
+
return others ? "others" : "none";
|
|
56744
|
+
}
|
|
56745
|
+
/**
|
|
56746
|
+
* Writes the visual of a live element whose `change` hook of that visual may play (F85). The look
|
|
56747
|
+
* of this render is compared with the one the element was last written with: the same look writes
|
|
56748
|
+
* nothing, so a running motion of the hook survives a render that changes nothing of it; other
|
|
56749
|
+
* fields alone are written at once; a look whose numbers moved plays the hook with the look before
|
|
56750
|
+
* and this one. When the hook returns a motion the track brings the numbers home and the other
|
|
56751
|
+
* fields are written only when they moved too; with none the look is written whole. ui's part of
|
|
56752
|
+
* a numbers-only change allocates nothing (`__tests__/perf/look-write.perf.ts`).
|
|
56753
|
+
*
|
|
56754
|
+
* @param writer - The reconciler's side: the pass, the values, the play and the writes.
|
|
56755
|
+
* @param element - The live element; its `lookPass` and `lookFrom` note a play of this pass.
|
|
56756
|
+
* @param value - The visual of this render.
|
|
56757
|
+
* @param previous - The look the element was last written with.
|
|
56758
|
+
* @param next - The fields of the visual of this render.
|
|
56759
|
+
*/
|
|
56760
|
+
function writeLook(writer, element, value, previous, next) {
|
|
56761
|
+
const moved = lookMoved(value.type, previous, next);
|
|
56762
|
+
if (moved === "none") return;
|
|
56763
|
+
if (moved === "others") {
|
|
56764
|
+
writer.others(element, value.type, next);
|
|
56765
|
+
return;
|
|
56766
|
+
}
|
|
56767
|
+
const from = element.lookPass === writer.pass && element.lookFrom !== void 0 ? element.lookFrom : previous;
|
|
56768
|
+
writer.values.previous = from;
|
|
56769
|
+
writer.values.next = next;
|
|
56770
|
+
if (!writer.play(element, value.type.componentName, writer.values)) {
|
|
56771
|
+
writer.whole(element, value);
|
|
56772
|
+
return;
|
|
56773
|
+
}
|
|
56774
|
+
element.lookPass = writer.pass;
|
|
56775
|
+
element.lookFrom = from;
|
|
56776
|
+
if (moved === "both") writer.others(element, value.type, next);
|
|
56777
|
+
}
|
|
56778
|
+
/**
|
|
56779
|
+
* Compares the value of a visual a live element was last written with and the value of this
|
|
56780
|
+
* render: whether its numeric fields moved, its other fields, both or none. Every
|
|
56781
|
+
* visual has its own comparator that reads its fields by name, so each read site meets one shape
|
|
56782
|
+
* of value and a fractional number is compared unboxed: per write of an element with a `change`
|
|
56783
|
+
* hook of its visual, 0 B (`__tests__/perf/look-moved.perf.ts`). A field the new value leaves out
|
|
56784
|
+
* stays on the entity, as a patch leaves it. A type that is none of the four visuals is reported
|
|
56785
|
+
* as `"numbers"`: its hook decides.
|
|
56786
|
+
*
|
|
56787
|
+
* @param type - The component type of the visual.
|
|
56788
|
+
* @param previous - The value the element was last written with.
|
|
56789
|
+
* @param next - The value of this render.
|
|
56790
|
+
* @returns What moved.
|
|
56791
|
+
* @example
|
|
56792
|
+
* ```ts
|
|
56793
|
+
* lookMoved(Shape, Shape({ kind: "arc", sweep: 0.25 }).value, Shape({ kind: "arc", sweep: 0.5 }).value); // "numbers"
|
|
56794
|
+
* ```
|
|
56795
|
+
*/
|
|
56796
|
+
function lookMoved(type, previous, next) {
|
|
56797
|
+
if (type === Shape) return shapeMoved(previous, next);
|
|
56798
|
+
if (type === Sprite) return spriteMoved(previous, next);
|
|
56799
|
+
if (type === NineSlice) return nineSliceMoved(previous, next);
|
|
56800
|
+
if (type === Text) return textMoved(previous, next);
|
|
56801
|
+
return "numbers";
|
|
56802
|
+
}
|
|
56803
|
+
/**
|
|
56804
|
+
* What moved in a `Shape`: its sizes, colours, alphas, radius, stroke and dash are numbers, and
|
|
56805
|
+
* the sweep of two arcs; its kind, its clip and the ends of two arcs are not. Only an arc carries
|
|
56806
|
+
* `sweep` and `cap`, so they are read between two arcs alone.
|
|
56807
|
+
*
|
|
56808
|
+
* @param before - The look last written.
|
|
56809
|
+
* @param after - The look of this render.
|
|
56810
|
+
* @returns What moved.
|
|
56811
|
+
* @example
|
|
56812
|
+
* ```ts
|
|
56813
|
+
* shapeMoved(Shape({ kind: "arc", sweep: 0.2, cap: "round" }).value, Shape({ kind: "arc", sweep: 0.2, cap: "butt" }).value); // "others"
|
|
56814
|
+
* ```
|
|
56815
|
+
*/
|
|
56816
|
+
function shapeMoved(before, after) {
|
|
56817
|
+
const numbers = before.w !== after.w || before.h !== after.h || before.fill !== after.fill || before.fillAlpha !== after.fillAlpha || before.alpha !== after.alpha || before.radius !== after.radius || before.stroke !== after.stroke || before.strokeWidth !== after.strokeWidth || before.dash !== after.dash;
|
|
56818
|
+
const arcs = before.kind === "arc" && after.kind === "arc";
|
|
56819
|
+
const others = before.kind !== after.kind || before.clip !== after.clip || arcs && before.cap !== after.cap;
|
|
56820
|
+
return moveOf(numbers || arcs && before.sweep !== after.sweep, others);
|
|
56821
|
+
}
|
|
56822
|
+
/**
|
|
56823
|
+
* What moved in a `Sprite`: its tint, alpha and size are numbers; its texture, fit, anchor and
|
|
56824
|
+
* blend are not.
|
|
56825
|
+
*
|
|
56826
|
+
* @param before - The look last written.
|
|
56827
|
+
* @param after - The look of this render.
|
|
56828
|
+
* @returns What moved.
|
|
56829
|
+
* @example
|
|
56830
|
+
* ```ts
|
|
56831
|
+
* spriteMoved(Sprite({ texture: "ui.coin", width: 40 }).value, Sprite({ texture: "ui.coin", width: 80 }).value); // "numbers"
|
|
56832
|
+
* ```
|
|
56833
|
+
*/
|
|
56834
|
+
function spriteMoved(before, after) {
|
|
56835
|
+
return moveOf(before.tint !== after.tint || before.alpha !== after.alpha || before.width !== after.width || before.height !== after.height, before.texture !== after.texture || before.fit !== after.fit || before.anchor.x !== after.anchor.x || before.anchor.y !== after.anchor.y || after.blend !== void 0 && before.blend !== after.blend);
|
|
56836
|
+
}
|
|
56837
|
+
/**
|
|
56838
|
+
* What moved in a `NineSlice`: its size, alpha and tint are numbers; its texture, outline, clip,
|
|
56839
|
+
* rim and blend are not.
|
|
56840
|
+
*
|
|
56841
|
+
* @param before - The look last written.
|
|
56842
|
+
* @param after - The look of this render.
|
|
56843
|
+
* @returns What moved.
|
|
56844
|
+
* @example
|
|
56845
|
+
* ```ts
|
|
56846
|
+
* nineSliceMoved(NineSlice({ texture: "ui.panel" }).value, NineSlice({ texture: "ui.panel", hollow: true }).value); // "others"
|
|
56847
|
+
* ```
|
|
56848
|
+
*/
|
|
56849
|
+
function nineSliceMoved(before, after) {
|
|
56850
|
+
return moveOf(before.width !== after.width || before.height !== after.height || before.alpha !== after.alpha || before.tint !== after.tint, before.texture !== after.texture || before.debug !== after.debug || before.clip !== after.clip || after.hollow !== void 0 && before.hollow !== after.hollow || after.blend !== void 0 && before.blend !== after.blend);
|
|
56851
|
+
}
|
|
56852
|
+
/**
|
|
56853
|
+
* What moved in a `Text`: its alpha and the box of two texts that wrap at one are numbers; its
|
|
56854
|
+
* content, style, bind and anchor are not. A message or a bind written anew compares by what it
|
|
56855
|
+
* says. `resolved` is the text plugin's and never compared.
|
|
56856
|
+
*
|
|
56857
|
+
* @param before - The look last written.
|
|
56858
|
+
* @param after - The look of this render.
|
|
56859
|
+
* @returns What moved.
|
|
56860
|
+
* @example
|
|
56861
|
+
* ```ts
|
|
56862
|
+
* textMoved(Text({ content: "Play" }).value, Text({ content: "Play", alpha: 0.5 }).value); // "numbers"
|
|
56863
|
+
* ```
|
|
56864
|
+
*/
|
|
56865
|
+
function textMoved(before, after) {
|
|
56866
|
+
const boxes = "box" in before && "box" in after;
|
|
56867
|
+
return moveOf(before.alpha !== after.alpha || boxes && before.box !== after.box, !boxes && "box" in after || before.style !== after.style || before.anchor.x !== after.anchor.x || before.anchor.y !== after.anchor.y || before.content !== after.content && !sameMessage(before.content, after.content) || before.bind !== after.bind && !sameBind(before.bind, after.bind));
|
|
56868
|
+
}
|
|
56869
|
+
/**
|
|
56870
|
+
* Tells whether two contents of a text say the same: two messages of one key with the same
|
|
56871
|
+
* arguments, each compared by identity. Two strings are the same only when they are equal.
|
|
56872
|
+
*
|
|
56873
|
+
* @param before - The content last written.
|
|
56874
|
+
* @param after - The content of this render.
|
|
56875
|
+
* @returns True when the text says the same.
|
|
56876
|
+
* @example
|
|
56877
|
+
* ```ts
|
|
56878
|
+
* sameMessage({ key: "hud.coins", params: { n: 5 } }, { key: "hud.coins", params: { n: 5 } }); // true
|
|
56879
|
+
* ```
|
|
56880
|
+
*/
|
|
56881
|
+
function sameMessage(before, after) {
|
|
56882
|
+
if (typeof before === "string" || typeof after === "string") return before === after;
|
|
56883
|
+
return before.key === after.key && sameArguments(before.params, after.params);
|
|
56884
|
+
}
|
|
56885
|
+
/**
|
|
56886
|
+
* Tells whether the arguments of two messages hold the same values under the same names: a
|
|
56887
|
+
* `for…in` over each, every value compared by identity.
|
|
56888
|
+
*
|
|
56889
|
+
* @param before - The arguments last written.
|
|
56890
|
+
* @param after - The arguments of this render.
|
|
56891
|
+
* @returns True when both hold the same.
|
|
56892
|
+
* @example
|
|
56893
|
+
* ```ts
|
|
56894
|
+
* sameArguments({ n: 5 }, { n: 6 }); // false
|
|
56895
|
+
* ```
|
|
56896
|
+
*/
|
|
56897
|
+
function sameArguments(before, after) {
|
|
56898
|
+
if (before === after) return true;
|
|
56899
|
+
if (before === void 0 || after === void 0) return false;
|
|
56900
|
+
for (const name in after) if (!(name in before) || before[name] !== after[name]) return false;
|
|
56901
|
+
for (const name in before) if (!(name in after)) return false;
|
|
56902
|
+
return true;
|
|
56903
|
+
}
|
|
56904
|
+
/**
|
|
56905
|
+
* Tells whether two binds of a text show the same field of the same component in one format.
|
|
56906
|
+
*
|
|
56907
|
+
* @param before - The bind last written.
|
|
56908
|
+
* @param after - The bind of this render.
|
|
56909
|
+
* @returns True when both show the same.
|
|
56910
|
+
* @example
|
|
56911
|
+
* ```ts
|
|
56912
|
+
* sameBind({ component: "Counter", field: "value", format: "int" }, { component: "Counter", field: "value", format: "mm:ss" }); // false
|
|
56913
|
+
* ```
|
|
56914
|
+
*/
|
|
56915
|
+
function sameBind(before, after) {
|
|
56916
|
+
if (before === void 0 || after === void 0) return before === after;
|
|
56917
|
+
return before.component === after.component && before.field === after.field && before.format === after.format;
|
|
56918
|
+
}
|
|
56919
|
+
/**
|
|
56920
|
+
* The fields of a visual a render writes directly while its `change` hook brings the numbers
|
|
56921
|
+
* home: every field that is no number, without the ones the component type gives to a plugin.
|
|
56922
|
+
*
|
|
56923
|
+
* @param value - The value of this render.
|
|
56924
|
+
* @param owned - The fields the component type gives to a plugin.
|
|
56925
|
+
* @returns The patch, or `undefined` when every field is a number.
|
|
56926
|
+
* @example
|
|
56927
|
+
* ```ts
|
|
56928
|
+
* otherFields({ kind: "arc", w: 32, sweep: 0.5, cap: "butt" }, []); // { kind: "arc", cap: "butt" }
|
|
56929
|
+
* ```
|
|
56930
|
+
*/
|
|
56931
|
+
function otherFields(value, owned) {
|
|
56932
|
+
let patch;
|
|
56933
|
+
for (const [field, entry] of Object.entries(value)) {
|
|
56934
|
+
if (typeof entry === "number" || owned.includes(field)) continue;
|
|
56935
|
+
patch ??= {};
|
|
56936
|
+
patch[field] = entry;
|
|
56937
|
+
}
|
|
56938
|
+
return patch;
|
|
56939
|
+
}
|
|
56940
|
+
/**
|
|
56941
|
+
* Names, once per element and hook name, every `change` hook of an element `ui` never plays: not
|
|
56942
|
+
* `Box`, not `Transform`, not the visual it is drawn with, not an extra of its `components` prop.
|
|
56943
|
+
* Read when the element mounts and when its `motion` prop changes, against what it carries then.
|
|
56944
|
+
* Its effect is the log and the note: each such hook is logged at warn as `ui:change-never-plays`
|
|
56945
|
+
* `{ key, name }` and noted in the element's `warned`, which keeps it from being named again.
|
|
56946
|
+
*
|
|
56947
|
+
* @param ctx - Domain context of the ui plugin.
|
|
56948
|
+
* @param element - The live element, its visual and its extras written.
|
|
56949
|
+
*/
|
|
56950
|
+
function warnIdleChanges(ctx, element) {
|
|
56951
|
+
const change = element.motion?.change;
|
|
56952
|
+
if (change === void 0) return;
|
|
56953
|
+
const visual = element.look?.type.componentName;
|
|
56954
|
+
for (const name of Object.keys(change)) {
|
|
56955
|
+
const plays = name === "Box" || name === "Transform" || name === visual || element.extras.has(name);
|
|
56956
|
+
const once = `change-never-plays:${name}`;
|
|
56957
|
+
if (plays || element.warned.has(once)) continue;
|
|
56958
|
+
element.warned.add(once);
|
|
56959
|
+
ctx.log.warn("ui:change-never-plays", {
|
|
56960
|
+
key: element.key ?? element.identity,
|
|
56961
|
+
name
|
|
56962
|
+
});
|
|
56963
|
+
}
|
|
56964
|
+
}
|
|
56965
|
+
//#endregion
|
|
56670
56966
|
//#region src/plugins/ui/jsx/order.ts
|
|
56671
56967
|
/**
|
|
56672
56968
|
* @file ui — the draw order of a child element among its siblings: a composite `Order` below its `zIndex` step.
|
|
@@ -57259,6 +57555,8 @@ function concatTypes(...lists) {
|
|
|
57259
57555
|
}
|
|
57260
57556
|
/** Every type a live element may carry and drop again: the visuals, then the inputs. */
|
|
57261
57557
|
const LIVE_TYPES = concatTypes(VISUALS, INPUTS);
|
|
57558
|
+
/** What the values of a look write hold before the first play: no look. */
|
|
57559
|
+
const NO_LOOK = Object.freeze({});
|
|
57262
57560
|
/** The owned fields of a type that gives none to a plugin: one list, never a new one. */
|
|
57263
57561
|
const NO_OWNED = [];
|
|
57264
57562
|
/** The time of a reconcile a test steps by hand: no ms pass, so no scroll moves. */
|
|
@@ -57686,6 +57984,16 @@ function entitySet(entities) {
|
|
|
57686
57984
|
return new Set(entities);
|
|
57687
57985
|
}
|
|
57688
57986
|
/**
|
|
57987
|
+
* Collects the identities of the children of one render. Its own function, because lint rule L5
|
|
57988
|
+
* refuses a collection built inside an exported declaration.
|
|
57989
|
+
*
|
|
57990
|
+
* @param identities - The identities, in markup order.
|
|
57991
|
+
* @returns The identities as a set.
|
|
57992
|
+
*/
|
|
57993
|
+
function identitySet(identities) {
|
|
57994
|
+
return new Set(identities);
|
|
57995
|
+
}
|
|
57996
|
+
/**
|
|
57689
57997
|
* Tells whether an element whose `Box` just came arrives with an enter: new, and not a row a
|
|
57690
57998
|
* range change scrolled in.
|
|
57691
57999
|
*
|
|
@@ -57731,6 +58039,16 @@ function createReconciler(ctx, modules, fields) {
|
|
|
57731
58039
|
let rendered = 0;
|
|
57732
58040
|
const isAside = (entity) => ctx.deps.world.projection.isBuilding(entity);
|
|
57733
58041
|
let released = false;
|
|
58042
|
+
const lookWriter = {
|
|
58043
|
+
pass: 0,
|
|
58044
|
+
values: {
|
|
58045
|
+
previous: NO_LOOK,
|
|
58046
|
+
next: NO_LOOK
|
|
58047
|
+
},
|
|
58048
|
+
play: (element, name, values) => modules.layout.changeExtra(element, name, values, hintOf(state, element)),
|
|
58049
|
+
whole: (element, value) => writeValue(element, value),
|
|
58050
|
+
others: (element, type, next) => writeOthers(element, next, type)
|
|
58051
|
+
};
|
|
57734
58052
|
/**
|
|
57735
58053
|
* Registers the key of an element under its root, so `entityOf` and `find` answer for it. A key
|
|
57736
58054
|
* `ui` gave a node of its own, a spacer's, is never registered.
|
|
@@ -57822,7 +58140,8 @@ function createReconciler(ctx, modules, fields) {
|
|
|
57822
58140
|
extraHandles: handleMap(),
|
|
57823
58141
|
warnedOwned: nameSet(),
|
|
57824
58142
|
warned: nameSet(),
|
|
57825
|
-
scrolledIn: range.isMoving()
|
|
58143
|
+
scrolledIn: range.isMoving(),
|
|
58144
|
+
look: void 0
|
|
57826
58145
|
};
|
|
57827
58146
|
state.styled += 1;
|
|
57828
58147
|
state.elements.set(entity, element);
|
|
@@ -57976,12 +58295,13 @@ function createReconciler(ctx, modules, fields) {
|
|
|
57976
58295
|
/**
|
|
57977
58296
|
* Writes the visual and input components of a live element again. A visual or an input the
|
|
57978
58297
|
* element no longer carries is removed, so a variant can trade the rectangle for a nine-slice and
|
|
57979
|
-
* back, and a button that became disabled stops answering the gate
|
|
57980
|
-
*
|
|
57981
|
-
*
|
|
57982
|
-
*
|
|
57983
|
-
*
|
|
57984
|
-
*
|
|
58298
|
+
* back, and a button that became disabled stops answering the gate; a visual traded away takes
|
|
58299
|
+
* the motion of its `change` hook with it. `Scroll`, `Scrollable` and `ScrollState` are only ever
|
|
58300
|
+
* added: where a scroll stands belongs to the finger and the scroll step, and a new look or a new
|
|
58301
|
+
* rect never resets it. The look is recorded as the rest of the visual before it is written, so
|
|
58302
|
+
* the rest the world holds is always the one the element is drawn with (a restore brings it back
|
|
58303
|
+
* to exactly that) and a `change` hook of the visual brings it home with `toRest`. The extras of
|
|
58304
|
+
* the `components` prop are diffed last. Every call is one write pass of `UiCounters`.
|
|
57985
58305
|
*
|
|
57986
58306
|
* @param element - The element that changed.
|
|
57987
58307
|
*/
|
|
@@ -57990,13 +58310,50 @@ function createReconciler(ctx, modules, fields) {
|
|
|
57990
58310
|
writeOrder(element);
|
|
57991
58311
|
const visuals = visualOf$1(element);
|
|
57992
58312
|
const inputs = inputOf(element);
|
|
57993
|
-
for (const type of LIVE_TYPES) if (!carries(visuals, type) && !carries(inputs, type) && ecs.has(element.entity, type))
|
|
57994
|
-
|
|
57995
|
-
|
|
58313
|
+
for (const type of LIVE_TYPES) if (!carries(visuals, type) && !carries(inputs, type) && ecs.has(element.entity, type)) {
|
|
58314
|
+
cancelExtra(element, type.componentName);
|
|
58315
|
+
ecs.remove(element.entity, type);
|
|
58316
|
+
}
|
|
57996
58317
|
for (const value of visuals) recordRest(ctx, element, value);
|
|
58318
|
+
for (const value of visuals) writeVisual(element, value);
|
|
58319
|
+
for (const value of inputs) writeValue(element, value);
|
|
57997
58320
|
writeExtras(element);
|
|
57998
58321
|
}
|
|
57999
58322
|
/**
|
|
58323
|
+
* Writes the visual of a live element. Without a `change` hook of its name, or for a visual
|
|
58324
|
+
* traded in, it is written like every other value. With one, the look decides what is written
|
|
58325
|
+
* (`writeLook`, F85): nothing for the same look, the other fields alone, or the hook's play, after
|
|
58326
|
+
* which the track brings the numbers home; an element held for the swap of a scene build plays
|
|
58327
|
+
* none and is written whole.
|
|
58328
|
+
*
|
|
58329
|
+
* @param element - The live element.
|
|
58330
|
+
* @param value - The visual of this render.
|
|
58331
|
+
*/
|
|
58332
|
+
function writeVisual(element, value) {
|
|
58333
|
+
const look = element.look;
|
|
58334
|
+
element.look = value;
|
|
58335
|
+
const isHooked = element.motion?.change?.[value.type.componentName] !== void 0;
|
|
58336
|
+
const previous = look === void 0 || look.type !== value.type ? true : look.value;
|
|
58337
|
+
const next = value.value;
|
|
58338
|
+
if (!isHooked || previous === true || next === true || !ecs.has(element.entity, value.type)) {
|
|
58339
|
+
writeValue(element, value);
|
|
58340
|
+
return;
|
|
58341
|
+
}
|
|
58342
|
+
writeLook(lookWriter, element, value, previous, next);
|
|
58343
|
+
}
|
|
58344
|
+
/**
|
|
58345
|
+
* Writes the fields of a visual that are no number, while a `change` hook brings the numbers
|
|
58346
|
+
* home, never a field its type gives to a plugin.
|
|
58347
|
+
*
|
|
58348
|
+
* @param element - The live element.
|
|
58349
|
+
* @param value - The fields of the visual of this render.
|
|
58350
|
+
* @param type - Its component type.
|
|
58351
|
+
*/
|
|
58352
|
+
function writeOthers(element, value, type) {
|
|
58353
|
+
const others = otherFields(value, ecs.typeOf(type.componentName)?.owned ?? NO_OWNED);
|
|
58354
|
+
if (others !== void 0) ecs.set(element.entity, asHandle(type), others);
|
|
58355
|
+
}
|
|
58356
|
+
/**
|
|
58000
58357
|
* Writes one visual or input value onto a live element: added when the entity lacks it, else
|
|
58001
58358
|
* its fields patched, but never a tag or the state of a scroll, and never a field its type gives
|
|
58002
58359
|
* to a plugin.
|
|
@@ -58092,6 +58449,7 @@ function createReconciler(ctx, modules, fields) {
|
|
|
58092
58449
|
const change = compare(root, element, node);
|
|
58093
58450
|
const motion = node.props.motion;
|
|
58094
58451
|
const loopChanged = !sameLoop(element.motion?.loop, motion?.loop);
|
|
58452
|
+
const motionChanged = motion !== element.motion;
|
|
58095
58453
|
if (typeof __MOKU_GAME_DEV__ !== "undefined" && __MOKU_GAME_DEV__) countRebuild(element, element.motion?.loop, motion?.loop);
|
|
58096
58454
|
if (!change.unchanged) delete element.basis;
|
|
58097
58455
|
element.node = node;
|
|
@@ -58109,6 +58467,7 @@ function createReconciler(ctx, modules, fields) {
|
|
|
58109
58467
|
rowHeight: change.rowHeight
|
|
58110
58468
|
};
|
|
58111
58469
|
}
|
|
58470
|
+
if (typeof __MOKU_GAME_DEV__ !== "undefined" && __MOKU_GAME_DEV__ && motionChanged && element.live) warnIdleChanges(ctx, element);
|
|
58112
58471
|
diffChildren(root, element, childrenOf(node, range.slotsFor(element), element.scroll?.axis), instance);
|
|
58113
58472
|
}
|
|
58114
58473
|
/**
|
|
@@ -58167,18 +58526,20 @@ function createReconciler(ctx, modules, fields) {
|
|
|
58167
58526
|
};
|
|
58168
58527
|
}
|
|
58169
58528
|
/**
|
|
58170
|
-
* Places one node: enter, patch, or exit and enter when the
|
|
58529
|
+
* Places one node: enter, patch, or exit and enter when the element at its identity is another
|
|
58530
|
+
* one now: a component whose view returned another tag or another key. The old element leaves
|
|
58531
|
+
* first, with its exit, so the new one takes the keys they share; the component itself stays and
|
|
58532
|
+
* keeps its local state.
|
|
58171
58533
|
*
|
|
58172
58534
|
* @param root - The root being reconciled.
|
|
58173
58535
|
* @param parent - The parent element, or nothing.
|
|
58174
58536
|
* @param node - The node the markup wrote.
|
|
58175
58537
|
* @param index - Its position among its siblings.
|
|
58176
|
-
* @param
|
|
58538
|
+
* @param identity - Its identity: `identityOf` the parent, the node and the index.
|
|
58177
58539
|
* @param instance - The identity of the nearest component instance.
|
|
58178
58540
|
* @returns The entity of the element, or `undefined` when a component view threw.
|
|
58179
58541
|
*/
|
|
58180
|
-
function diffNode(root, parent, node, index,
|
|
58181
|
-
const identity = identityOf(parentIdentity, node, index);
|
|
58542
|
+
function diffNode(root, parent, node, index, identity, instance) {
|
|
58182
58543
|
const expanded = expand(node, identity);
|
|
58183
58544
|
const view = expanded.node;
|
|
58184
58545
|
const owner = expanded.instance ?? instance;
|
|
@@ -58186,15 +58547,29 @@ function createReconciler(ctx, modules, fields) {
|
|
|
58186
58547
|
if (effective === void 0) return state.byIdentity.get(identity);
|
|
58187
58548
|
const existing = state.byIdentity.get(identity);
|
|
58188
58549
|
const element = existing === void 0 ? void 0 : state.elements.get(existing);
|
|
58189
|
-
if (element !== void 0 && element.type === effective.type) {
|
|
58550
|
+
if (element !== void 0 && element.type === effective.type && element.key === effective.key) {
|
|
58190
58551
|
patch(root, element, effective, owner);
|
|
58191
58552
|
return element.entity;
|
|
58192
58553
|
}
|
|
58193
|
-
if (element !== void 0)
|
|
58554
|
+
if (element !== void 0) replaceElement(element, identity);
|
|
58194
58555
|
return enter(root, parent, effective, identity, owner, index);
|
|
58195
58556
|
}
|
|
58196
58557
|
/**
|
|
58197
|
-
*
|
|
58558
|
+
* Exits the element a component's view returned before, at the component's own identity. The
|
|
58559
|
+
* instances under it are forgotten with it; the component's own instance stays: the local state
|
|
58560
|
+
* belongs to the component, which is still there.
|
|
58561
|
+
*
|
|
58562
|
+
* @param element - The element the view returned before.
|
|
58563
|
+
* @param identity - The identity of the component node.
|
|
58564
|
+
*/
|
|
58565
|
+
function replaceElement(element, identity) {
|
|
58566
|
+
const own = state.instances.get(identity);
|
|
58567
|
+
exitElement(element, "change");
|
|
58568
|
+
if (own !== void 0) state.instances.set(identity, own);
|
|
58569
|
+
}
|
|
58570
|
+
/**
|
|
58571
|
+
* Diffs the children of one element and re-places them once when the list changed. The old
|
|
58572
|
+
* children the render leaves out start their exit before any new child enters.
|
|
58198
58573
|
*
|
|
58199
58574
|
* @param root - The root being reconciled.
|
|
58200
58575
|
* @param parent - The element whose children are diffed.
|
|
@@ -58202,19 +58577,18 @@ function createReconciler(ctx, modules, fields) {
|
|
|
58202
58577
|
* @param instance - The identity of the nearest component instance.
|
|
58203
58578
|
*/
|
|
58204
58579
|
function diffChildren(root, parent, nodes, instance) {
|
|
58580
|
+
const identities = nodes.map((node, index) => identityOf(parent.identity, node, index));
|
|
58581
|
+
leaveOut(parent, identities);
|
|
58205
58582
|
const next = [];
|
|
58206
58583
|
for (const [index, node] of nodes.entries()) {
|
|
58207
|
-
const entity = diffNode(root, parent, node, index, parent.identity, instance);
|
|
58584
|
+
const entity = diffNode(root, parent, node, index, identities[index] ?? identityOf(parent.identity, node, index), instance);
|
|
58208
58585
|
if (entity !== void 0) next.push(entity);
|
|
58209
58586
|
}
|
|
58210
58587
|
const kept = entitySet(next);
|
|
58211
|
-
const dropsRows = range.dropsRowsOf(parent);
|
|
58212
58588
|
for (const old of parent.children) {
|
|
58213
|
-
if (kept.has(old)) continue;
|
|
58589
|
+
if (kept.has(old) || state.exiting.has(old)) continue;
|
|
58214
58590
|
const element = state.elements.get(old);
|
|
58215
|
-
if (element
|
|
58216
|
-
if (dropsRows) range.dropRow(element);
|
|
58217
|
-
else exitElement(element, "change");
|
|
58591
|
+
if (element !== void 0) removeChild(parent, element);
|
|
58218
58592
|
}
|
|
58219
58593
|
const same = next.length === parent.children.length && next.every((entity, index) => parent.children[index] === entity);
|
|
58220
58594
|
parent.children = next;
|
|
@@ -58224,6 +58598,33 @@ function createReconciler(ctx, modules, fields) {
|
|
|
58224
58598
|
root.needsSolve = true;
|
|
58225
58599
|
}
|
|
58226
58600
|
/**
|
|
58601
|
+
* Takes out every old child of an element whose identity no node of this render has: its exit
|
|
58602
|
+
* starts, and everything under it plays its own, before a new child enters.
|
|
58603
|
+
*
|
|
58604
|
+
* @param parent - The element whose children are diffed.
|
|
58605
|
+
* @param identities - The identities of the children of this render, in markup order.
|
|
58606
|
+
*/
|
|
58607
|
+
function leaveOut(parent, identities) {
|
|
58608
|
+
let listed;
|
|
58609
|
+
for (const [index, old] of parent.children.entries()) {
|
|
58610
|
+
const element = state.elements.get(old);
|
|
58611
|
+
if (element === void 0 || identities[index] === element.identity) continue;
|
|
58612
|
+
listed ??= identitySet(identities);
|
|
58613
|
+
if (!listed.has(element.identity)) removeChild(parent, element);
|
|
58614
|
+
}
|
|
58615
|
+
}
|
|
58616
|
+
/**
|
|
58617
|
+
* Removes one child a render left out: its exit starts. A row that a range change took out of a
|
|
58618
|
+
* windowed scroll is not removed: it is dropped at once, with no exit motion.
|
|
58619
|
+
*
|
|
58620
|
+
* @param parent - The element whose children are diffed.
|
|
58621
|
+
* @param element - The child that leaves.
|
|
58622
|
+
*/
|
|
58623
|
+
function removeChild(parent, element) {
|
|
58624
|
+
if (range.dropsRowsOf(parent)) range.dropRow(element);
|
|
58625
|
+
else exitElement(element, "change");
|
|
58626
|
+
}
|
|
58627
|
+
/**
|
|
58227
58628
|
* Starts the exit of an element and of everything under it. An element of a root built aside was
|
|
58228
58629
|
* never seen: it goes at once, with no exit (spec 31).
|
|
58229
58630
|
*
|
|
@@ -58412,8 +58813,12 @@ function createReconciler(ctx, modules, fields) {
|
|
|
58412
58813
|
warnOwned(element);
|
|
58413
58814
|
for (const value of element.extras.values()) recordRest(ctx, element, value);
|
|
58414
58815
|
if (overlayOf(element) !== void 0) ecs.add(element.entity, OverlayLevel());
|
|
58415
|
-
for (const value of componentsOf(element, root))
|
|
58816
|
+
for (const value of componentsOf(element, root)) {
|
|
58817
|
+
ecs.add(element.entity, value);
|
|
58818
|
+
if (VISUALS.includes(value.type)) element.look = value;
|
|
58819
|
+
}
|
|
58416
58820
|
element.live = true;
|
|
58821
|
+
if (typeof __MOKU_GAME_DEV__ !== "undefined" && __MOKU_GAME_DEV__) warnIdleChanges(ctx, element);
|
|
58417
58822
|
if (state.hosts.has(element.entity)) state.risen.add(element.entity);
|
|
58418
58823
|
}
|
|
58419
58824
|
/**
|
|
@@ -58777,14 +59182,17 @@ function createReconciler(ctx, modules, fields) {
|
|
|
58777
59182
|
/**
|
|
58778
59183
|
* Reconciles one root from the node its `Tree` carries. Its first build reads how the root came,
|
|
58779
59184
|
* from the world's view record, which exists by now; the elements entered by this diff take that
|
|
58780
|
-
* cause, and the elements of any later diff take `"change"` (spec 16 U4).
|
|
59185
|
+
* cause, and the elements of any later diff take `"change"` (spec 16 U4). A tree whose top has
|
|
59186
|
+
* another identity, another key or tag, replaces the root element: the old one exits first.
|
|
58781
59187
|
*
|
|
58782
59188
|
* @param root - The root to reconcile.
|
|
58783
59189
|
*/
|
|
58784
59190
|
function reconcileRoot(root) {
|
|
58785
|
-
const identity = String(root.entity);
|
|
59191
|
+
const identity = identityOf(String(root.entity), root.tree, 0);
|
|
58786
59192
|
const first = root.element === void 0;
|
|
58787
59193
|
if (first) root.cause ??= ctx.deps.world.projection.causeOf(root.entity) ?? "change";
|
|
59194
|
+
const previous = root.element === void 0 ? void 0 : lookup(root.element);
|
|
59195
|
+
if (previous !== void 0 && previous.identity !== identity) exitElement(previous, "change");
|
|
58788
59196
|
state.building = first ? root.cause : "change";
|
|
58789
59197
|
try {
|
|
58790
59198
|
root.element = diffNode(root, void 0, root.tree, 0, identity);
|
|
@@ -58932,6 +59340,7 @@ function createReconciler(ctx, modules, fields) {
|
|
|
58932
59340
|
function reconcile(time = NO_TIME) {
|
|
58933
59341
|
sweep();
|
|
58934
59342
|
state.reconciles += 1;
|
|
59343
|
+
lookWriter.pass += 1;
|
|
58935
59344
|
settleAside();
|
|
58936
59345
|
if (state.remount) remountRoots();
|
|
58937
59346
|
reorderRoots();
|
|
@@ -59103,6 +59512,7 @@ function createReconciler(ctx, modules, fields) {
|
|
|
59103
59512
|
const element = state.elements.get(entity);
|
|
59104
59513
|
if (element === void 0) return;
|
|
59105
59514
|
element.is[flag] = on;
|
|
59515
|
+
lookWriter.pass += 1;
|
|
59106
59516
|
const style = styleFor(parentOf(element), element.node, element.is);
|
|
59107
59517
|
const root = state.roots.get(element.root);
|
|
59108
59518
|
const moved = modules.layout.affectsRect(element.style, style);
|
|
@@ -59888,14 +60298,15 @@ function noteEndless(element, motion) {
|
|
|
59888
60298
|
element.endless.add(motion);
|
|
59889
60299
|
}
|
|
59890
60300
|
/**
|
|
59891
|
-
* Plays the `change` hook of one extra component whose fields changed
|
|
59892
|
-
* of that name started is cancelled first, so its
|
|
59893
|
-
* starts from there; the new motion is kept with the
|
|
59894
|
-
* An element held for the swap of a scene build plays
|
|
60301
|
+
* Plays the `change` hook of one extra component whose fields changed, or of the visual whose
|
|
60302
|
+
* numbers moved (F85). The motion the last change of that name started is cancelled first, so its
|
|
60303
|
+
* value stays where it is and the new motion starts from there; the new motion is kept with the
|
|
60304
|
+
* element's handles, so an exit waits for it. An element held for the swap of a scene build plays
|
|
60305
|
+
* none: the caller writes the fields.
|
|
59895
60306
|
*
|
|
59896
60307
|
* @param ctx - Domain context of the ui plugin.
|
|
59897
|
-
* @param element - The element that carries the
|
|
59898
|
-
* @param name - The component name of the extra.
|
|
60308
|
+
* @param element - The element that carries the component.
|
|
60309
|
+
* @param name - The component name of the extra or the visual.
|
|
59899
60310
|
* @param values - The value of the last render and of this one.
|
|
59900
60311
|
* @param values.previous - The value of the last render.
|
|
59901
60312
|
* @param values.next - The value of this render.
|