easter-egg-quest 1.0.15 → 1.0.16
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.
|
@@ -462,6 +462,12 @@ class InputTracker {
|
|
|
462
462
|
this._phases = [];
|
|
463
463
|
this._currentPhaseType = "still";
|
|
464
464
|
this._currentPhaseStart = Date.now();
|
|
465
|
+
this._snapshot.isMoving = false;
|
|
466
|
+
this._snapshot.velocity = 0;
|
|
467
|
+
if (this._stillTimer) {
|
|
468
|
+
clearTimeout(this._stillTimer);
|
|
469
|
+
this._stillTimer = null;
|
|
470
|
+
}
|
|
465
471
|
}
|
|
466
472
|
resetCounts() {
|
|
467
473
|
this._snapshot.totalClicks = 0;
|
|
@@ -567,6 +573,10 @@ class HiddenEntry {
|
|
|
567
573
|
this._hintVisibleStart = 0;
|
|
568
574
|
this._hintCheckInterval = null;
|
|
569
575
|
this._hintVisibilityHandler = null;
|
|
576
|
+
this._domObserver = null;
|
|
577
|
+
this._intersectionObserver = null;
|
|
578
|
+
this._mutationDebounce = null;
|
|
579
|
+
this._navigationHandler = null;
|
|
570
580
|
this.config = config;
|
|
571
581
|
this.script = script;
|
|
572
582
|
this.onFound = onFound;
|
|
@@ -600,6 +610,7 @@ class HiddenEntry {
|
|
|
600
610
|
this.targetElement.style.removeProperty("animation");
|
|
601
611
|
this.targetElement.classList.remove("eeq-entry-target");
|
|
602
612
|
}
|
|
613
|
+
this._stopWatchdog();
|
|
603
614
|
(_a2 = this._injectedElement) == null ? void 0 : _a2.remove();
|
|
604
615
|
this._injectedElement = null;
|
|
605
616
|
(_b2 = this.hintContainer) == null ? void 0 : _b2.remove();
|
|
@@ -620,8 +631,8 @@ class HiddenEntry {
|
|
|
620
631
|
).filter((el) => {
|
|
621
632
|
var _a2;
|
|
622
633
|
const text = ((_a2 = el.textContent) == null ? void 0 : _a2.trim()) ?? "";
|
|
623
|
-
|
|
624
|
-
return
|
|
634
|
+
if (text.length < 2 || el.closest("[data-eeq]")) return false;
|
|
635
|
+
return isElementVisible(el);
|
|
625
636
|
});
|
|
626
637
|
const sorted = candidates.sort((a, b) => {
|
|
627
638
|
const aNav = a.closest("nav") !== null || a.closest("header") !== null ? 1 : 0;
|
|
@@ -708,10 +719,113 @@ class HiddenEntry {
|
|
|
708
719
|
this._injectedElement = span;
|
|
709
720
|
this.targetElement = span;
|
|
710
721
|
this._attachToElement(span);
|
|
722
|
+
this._startWatchdog();
|
|
711
723
|
return;
|
|
712
724
|
}
|
|
713
725
|
this._createFallbackEntry();
|
|
714
726
|
}
|
|
727
|
+
/**
|
|
728
|
+
* Watch for the injected trigger element being removed from the DOM
|
|
729
|
+
* (SPA re-render, route change, virtual scroll) or scrolled out of view.
|
|
730
|
+
*
|
|
731
|
+
* Uses IntersectionObserver (zero layout cost) for visibility,
|
|
732
|
+
* a MutationObserver on a stable ancestor (survives Angular/React re-renders),
|
|
733
|
+
* and navigation event listeners for SPA route changes.
|
|
734
|
+
*/
|
|
735
|
+
_startWatchdog() {
|
|
736
|
+
this._stopWatchdog();
|
|
737
|
+
if (!this._injectedElement) return;
|
|
738
|
+
this._intersectionObserver = new IntersectionObserver(
|
|
739
|
+
(entries) => {
|
|
740
|
+
if (this._destroyed || !this._injectedElement) return;
|
|
741
|
+
const entry = entries[0];
|
|
742
|
+
if (entry && !entry.isIntersecting) {
|
|
743
|
+
this._reinject();
|
|
744
|
+
}
|
|
745
|
+
},
|
|
746
|
+
{ threshold: 0 }
|
|
747
|
+
);
|
|
748
|
+
this._intersectionObserver.observe(this._injectedElement);
|
|
749
|
+
const observeTarget = this._findStableAncestor(this._injectedElement);
|
|
750
|
+
this._domObserver = new MutationObserver(() => {
|
|
751
|
+
if (this._destroyed || !this._injectedElement) return;
|
|
752
|
+
if (this._mutationDebounce) clearTimeout(this._mutationDebounce);
|
|
753
|
+
this._mutationDebounce = setTimeout(() => {
|
|
754
|
+
if (this._destroyed || !this._injectedElement) return;
|
|
755
|
+
if (!document.body.contains(this._injectedElement)) {
|
|
756
|
+
this._reinject();
|
|
757
|
+
}
|
|
758
|
+
}, 300);
|
|
759
|
+
});
|
|
760
|
+
this._domObserver.observe(observeTarget, { childList: true, subtree: true });
|
|
761
|
+
this._navigationHandler = () => {
|
|
762
|
+
if (this._destroyed || !this._injectedElement) return;
|
|
763
|
+
setTimeout(() => {
|
|
764
|
+
if (this._destroyed || !this._injectedElement) return;
|
|
765
|
+
if (!document.body.contains(this._injectedElement)) {
|
|
766
|
+
this._reinject();
|
|
767
|
+
}
|
|
768
|
+
}, 500);
|
|
769
|
+
};
|
|
770
|
+
window.addEventListener("popstate", this._navigationHandler);
|
|
771
|
+
window.addEventListener("hashchange", this._navigationHandler);
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Walk up the DOM to find a stable ancestor that won't be replaced
|
|
775
|
+
* by SPA framework routing (Angular <router-outlet>, React root, etc.).
|
|
776
|
+
*/
|
|
777
|
+
_findStableAncestor(el) {
|
|
778
|
+
let node = el.parentElement;
|
|
779
|
+
while (node && node !== document.body) {
|
|
780
|
+
const tag = node.tagName.toLowerCase();
|
|
781
|
+
if (tag === "main" || tag === "body" || tag.includes("app-") || // Angular app-root, app-component, etc.
|
|
782
|
+
tag.includes("-root") || // custom element roots
|
|
783
|
+
node.querySelector("router-outlet") !== null || node.id === "app" || node.id === "root" || node.id === "__next") {
|
|
784
|
+
return node;
|
|
785
|
+
}
|
|
786
|
+
node = node.parentElement;
|
|
787
|
+
}
|
|
788
|
+
return document.body;
|
|
789
|
+
}
|
|
790
|
+
_stopWatchdog() {
|
|
791
|
+
if (this._domObserver) {
|
|
792
|
+
this._domObserver.disconnect();
|
|
793
|
+
this._domObserver = null;
|
|
794
|
+
}
|
|
795
|
+
if (this._intersectionObserver) {
|
|
796
|
+
this._intersectionObserver.disconnect();
|
|
797
|
+
this._intersectionObserver = null;
|
|
798
|
+
}
|
|
799
|
+
if (this._mutationDebounce) {
|
|
800
|
+
clearTimeout(this._mutationDebounce);
|
|
801
|
+
this._mutationDebounce = null;
|
|
802
|
+
}
|
|
803
|
+
if (this._navigationHandler) {
|
|
804
|
+
window.removeEventListener("popstate", this._navigationHandler);
|
|
805
|
+
window.removeEventListener("hashchange", this._navigationHandler);
|
|
806
|
+
this._navigationHandler = null;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
/** Remove old trigger (if still in DOM) and re-inject into a new element. */
|
|
810
|
+
_reinject() {
|
|
811
|
+
this._stopWatchdog();
|
|
812
|
+
if (this.clickHandler && this.targetElement) {
|
|
813
|
+
this.targetElement.removeEventListener("click", this.clickHandler);
|
|
814
|
+
}
|
|
815
|
+
if (this._injectedElement && document.body.contains(this._injectedElement)) {
|
|
816
|
+
this._injectedElement.remove();
|
|
817
|
+
}
|
|
818
|
+
this._injectedElement = null;
|
|
819
|
+
this.targetElement = null;
|
|
820
|
+
setTimeout(() => {
|
|
821
|
+
if (this._destroyed) return;
|
|
822
|
+
const eligible = findEligibleEntryElements(
|
|
823
|
+
this.config.hiddenEntry.selector,
|
|
824
|
+
this.config.hiddenEntry.excludeSelectors
|
|
825
|
+
);
|
|
826
|
+
this._injectIntoExisting(eligible);
|
|
827
|
+
}, 1200);
|
|
828
|
+
}
|
|
715
829
|
/**
|
|
716
830
|
* Try to insert the trigger span between words inside a text node.
|
|
717
831
|
* Picks a random word boundary in the element's first direct text node.
|
|
@@ -1059,7 +1173,7 @@ class NarrativeRenderer {
|
|
|
1059
1173
|
width: "100%",
|
|
1060
1174
|
height: "100%",
|
|
1061
1175
|
pointerEvents: "none",
|
|
1062
|
-
zIndex: "
|
|
1176
|
+
zIndex: "999993"
|
|
1063
1177
|
});
|
|
1064
1178
|
document.body.appendChild(this.host);
|
|
1065
1179
|
this.shadow = this.host.attachShadow({ mode: "closed" });
|
|
@@ -1889,6 +2003,13 @@ class ThreeRenderer {
|
|
|
1889
2003
|
}
|
|
1890
2004
|
}
|
|
1891
2005
|
}
|
|
2006
|
+
/** Mark all ambient particles for rapid fade-out (rhythm lost). */
|
|
2007
|
+
fadeOutAmbientParticles() {
|
|
2008
|
+
for (const p of this._ambientParticles) {
|
|
2009
|
+
p.fadeIn = false;
|
|
2010
|
+
if (p.life > 0.3) p.life = 0.3;
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
1892
2013
|
/** Start the finale composition. */
|
|
1893
2014
|
startFinale() {
|
|
1894
2015
|
this._finaleActive = true;
|
|
@@ -2209,17 +2330,17 @@ class ThreeRenderer {
|
|
|
2209
2330
|
this.eggBodies.push(body3);
|
|
2210
2331
|
}
|
|
2211
2332
|
// ── Mini egg helpers for particles ─────────────────────────────────────
|
|
2212
|
-
/** Create a tiny egg-shaped geometry
|
|
2333
|
+
/** Create a tiny egg-shaped geometry — rounder, less elongated. */
|
|
2213
2334
|
_createMiniEggGeo(T, radius) {
|
|
2214
|
-
const geo = new T.SphereGeometry(radius,
|
|
2335
|
+
const geo = new T.SphereGeometry(radius, 12, 12);
|
|
2215
2336
|
const pos = geo.attributes.position;
|
|
2216
2337
|
for (let i = 0; i < pos.count; i++) {
|
|
2217
2338
|
let y = pos.getY(i);
|
|
2218
2339
|
const x = pos.getX(i);
|
|
2219
2340
|
const z = pos.getZ(i);
|
|
2220
|
-
const topSquash = y > 0 ? 0.
|
|
2221
|
-
y = y * 1.
|
|
2222
|
-
const narrowing = 1 - Math.abs(y) * 0.
|
|
2341
|
+
const topSquash = y > 0 ? 0.95 : 1;
|
|
2342
|
+
y = y * 1.08 * topSquash;
|
|
2343
|
+
const narrowing = 1 - Math.abs(y) * 0.03;
|
|
2223
2344
|
pos.setX(i, x * narrowing);
|
|
2224
2345
|
pos.setY(i, y);
|
|
2225
2346
|
pos.setZ(i, z * narrowing);
|
|
@@ -2235,8 +2356,8 @@ class ThreeRenderer {
|
|
|
2235
2356
|
c.height = 64;
|
|
2236
2357
|
const ctx = c.getContext("2d");
|
|
2237
2358
|
const hue = Math.random() * 360;
|
|
2238
|
-
const sat =
|
|
2239
|
-
const light =
|
|
2359
|
+
const sat = 60 + Math.random() * 25;
|
|
2360
|
+
const light = 68 + Math.random() * 14;
|
|
2240
2361
|
ctx.fillStyle = `hsl(${hue}, ${sat}%, ${light}%)`;
|
|
2241
2362
|
ctx.fillRect(0, 0, 64, 64);
|
|
2242
2363
|
const accentHue = (hue + 80 + Math.random() * 100) % 360;
|
|
@@ -2307,7 +2428,7 @@ class ThreeRenderer {
|
|
|
2307
2428
|
return new T.MeshBasicMaterial({
|
|
2308
2429
|
map: tex,
|
|
2309
2430
|
transparent: true,
|
|
2310
|
-
opacity
|
|
2431
|
+
opacity: opacity || 0.85
|
|
2311
2432
|
});
|
|
2312
2433
|
}
|
|
2313
2434
|
_createEggGeometry(T) {
|
|
@@ -3037,12 +3158,12 @@ class ThreeRenderer {
|
|
|
3037
3158
|
p.mesh.rotation.z += dt * 0.5;
|
|
3038
3159
|
if (p.fadeIn && p.life > 0.7) {
|
|
3039
3160
|
p.mesh.material.opacity = Math.min(
|
|
3040
|
-
0.
|
|
3041
|
-
p.mesh.material.opacity + dt *
|
|
3161
|
+
0.9,
|
|
3162
|
+
p.mesh.material.opacity + dt * 1
|
|
3042
3163
|
);
|
|
3043
|
-
if (p.mesh.material.opacity >= 0.
|
|
3164
|
+
if (p.mesh.material.opacity >= 0.85) p.fadeIn = false;
|
|
3044
3165
|
} else {
|
|
3045
|
-
p.mesh.material.opacity = Math.max(0, p.life * 0.
|
|
3166
|
+
p.mesh.material.opacity = Math.max(0, p.life * 0.85);
|
|
3046
3167
|
}
|
|
3047
3168
|
if (p.life <= 0) {
|
|
3048
3169
|
this.scene.remove(p.mesh);
|
|
@@ -4511,7 +4632,7 @@ class RhythmStage {
|
|
|
4511
4632
|
if (isGood) {
|
|
4512
4633
|
this._goodCycles++;
|
|
4513
4634
|
} else {
|
|
4514
|
-
this._goodCycles = 0;
|
|
4635
|
+
this._goodCycles = Math.max(0, this._goodCycles - 1);
|
|
4515
4636
|
this._showReaction();
|
|
4516
4637
|
}
|
|
4517
4638
|
this._lastPhaseCount += 2;
|
|
@@ -4829,11 +4950,13 @@ class GameController {
|
|
|
4829
4950
|
(_a3 = this.eggRenderer) == null ? void 0 : _a3.addTrail(data.x, data.y, data.velocity);
|
|
4830
4951
|
});
|
|
4831
4952
|
this.bus.on("rhythm:breathe", (data) => {
|
|
4832
|
-
var _a3, _b3
|
|
4953
|
+
var _a3, _b3;
|
|
4833
4954
|
(_a3 = this.eggRenderer) == null ? void 0 : _a3.setBreathIntensity(data.accuracy);
|
|
4834
4955
|
(_b3 = this.pageBreather) == null ? void 0 : _b3.update(data.accuracy, data.isMoving);
|
|
4835
|
-
if (
|
|
4956
|
+
if (data.accuracy > 0 && this.threeRenderer) {
|
|
4836
4957
|
this.threeRenderer.showProgressParticles(0.6 + data.accuracy * 0.4);
|
|
4958
|
+
} else if (this.threeRenderer) {
|
|
4959
|
+
this.threeRenderer.fadeOutAmbientParticles();
|
|
4837
4960
|
}
|
|
4838
4961
|
});
|
|
4839
4962
|
this.overlay = new OverlayManager(this.config);
|
|
@@ -5151,6 +5274,7 @@ class GameController {
|
|
|
5151
5274
|
}
|
|
5152
5275
|
const successLines = eggIndex === 0 ? this.script.stage1Success : eggIndex === 1 ? this.script.stage2Success : this.script.stage3Success;
|
|
5153
5276
|
await ((_c = this.narrative) == null ? void 0 : _c.showSequence(successLines, 4500));
|
|
5277
|
+
await this._wait(3e3);
|
|
5154
5278
|
(_d = this.narrative) == null ? void 0 : _d.clear();
|
|
5155
5279
|
await this._wait(800);
|
|
5156
5280
|
if (this.threeRenderer) {
|