@linxin666/dsh-pet 0.2.7 → 0.2.9
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/LICENSE +202 -29
- package/README.i18n.yaml +2 -2
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/assets/whale/pet.json +54 -54
- package/assets/whale-refined/pet.json +96 -1
- package/lib/client.js +84 -14
- package/lib/client.js.map +1 -1
- package/lib/index.js +80 -59
- package/lib/types/client/PetSprite.d.ts.map +1 -1
- package/lib/types/client/PetSprite.js +23 -17
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +50 -3
- package/lib/types/client/ui-teardown.d.ts +29 -0
- package/lib/types/client/ui-teardown.d.ts.map +1 -0
- package/lib/types/client/ui-teardown.js +41 -0
- package/lib/types/registry.d.ts +5 -1
- package/lib/types/registry.d.ts.map +1 -1
- package/lib/types/registry.js +14 -10
- package/lib/types/service.d.ts +8 -0
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +19 -1
- package/package.json +12 -9
- package/src/client/PetSprite.test.tsx +168 -26
- package/src/client/PetSprite.tsx +21 -15
- package/src/client/index.test.tsx +118 -11
- package/src/client/index.ts +51 -3
- package/src/client/ui-teardown.ts +47 -0
- package/src/registry.test.ts +14 -0
- package/src/registry.ts +14 -10
- package/src/service.ts +19 -1
package/lib/index.js
CHANGED
|
@@ -2481,109 +2481,113 @@ function assetUrl(prefix, id, file) {
|
|
|
2481
2481
|
const path = file.split("/").filter((segment) => segment !== "").join("/");
|
|
2482
2482
|
return prefix + "/" + encodeURIComponent(id) + "/" + path;
|
|
2483
2483
|
}
|
|
2484
|
-
/**
|
|
2484
|
+
/**
|
|
2485
|
+
* Default per-track rhythm — the shared slow baseline every sprite2d pet
|
|
2486
|
+
* plays unless its manifest overrides a track (user request: all pets were
|
|
2487
|
+
* too fast at the legacy hatch-pet contract pace).
|
|
2488
|
+
*/
|
|
2485
2489
|
const DEFAULT_TRACK_PATTERNS = {
|
|
2486
2490
|
idle: {
|
|
2487
2491
|
durations: [
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2492
|
+
500,
|
|
2493
|
+
500,
|
|
2494
|
+
600,
|
|
2495
|
+
500,
|
|
2496
|
+
500,
|
|
2497
|
+
600
|
|
2494
2498
|
],
|
|
2495
2499
|
loop: true
|
|
2496
2500
|
},
|
|
2497
2501
|
"running-right": {
|
|
2498
2502
|
durations: [
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2503
|
+
300,
|
|
2504
|
+
300,
|
|
2505
|
+
300,
|
|
2506
|
+
300,
|
|
2507
|
+
300,
|
|
2508
|
+
300,
|
|
2509
|
+
300,
|
|
2510
|
+
400
|
|
2507
2511
|
],
|
|
2508
2512
|
loop: true
|
|
2509
2513
|
},
|
|
2510
2514
|
"running-left": {
|
|
2511
2515
|
durations: [
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2516
|
+
300,
|
|
2517
|
+
300,
|
|
2518
|
+
300,
|
|
2519
|
+
300,
|
|
2520
|
+
300,
|
|
2521
|
+
300,
|
|
2522
|
+
300,
|
|
2523
|
+
400
|
|
2520
2524
|
],
|
|
2521
2525
|
loop: true
|
|
2522
2526
|
},
|
|
2523
2527
|
waving: {
|
|
2524
2528
|
durations: [
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
+
450,
|
|
2530
|
+
450,
|
|
2531
|
+
450,
|
|
2532
|
+
450
|
|
2529
2533
|
],
|
|
2530
2534
|
loop: true
|
|
2531
2535
|
},
|
|
2532
2536
|
jumping: {
|
|
2533
2537
|
durations: [
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2538
|
+
400,
|
|
2539
|
+
400,
|
|
2540
|
+
400,
|
|
2541
|
+
450,
|
|
2542
|
+
450
|
|
2539
2543
|
],
|
|
2540
2544
|
loop: false,
|
|
2541
2545
|
fallback: "idle"
|
|
2542
2546
|
},
|
|
2543
2547
|
failed: {
|
|
2544
2548
|
durations: [
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2549
|
+
550,
|
|
2550
|
+
550,
|
|
2551
|
+
550,
|
|
2552
|
+
600,
|
|
2553
|
+
650,
|
|
2554
|
+
700,
|
|
2555
|
+
550,
|
|
2556
|
+
550
|
|
2553
2557
|
],
|
|
2554
2558
|
loop: false,
|
|
2555
2559
|
fallback: "idle"
|
|
2556
2560
|
},
|
|
2557
2561
|
waiting: {
|
|
2558
2562
|
durations: [
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2563
|
+
550,
|
|
2564
|
+
550,
|
|
2565
|
+
600,
|
|
2566
|
+
550,
|
|
2567
|
+
550,
|
|
2568
|
+
600
|
|
2565
2569
|
],
|
|
2566
2570
|
loop: true
|
|
2567
2571
|
},
|
|
2568
2572
|
running: {
|
|
2569
2573
|
durations: [
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2574
|
+
330,
|
|
2575
|
+
330,
|
|
2576
|
+
330,
|
|
2577
|
+
330,
|
|
2578
|
+
330,
|
|
2579
|
+
400
|
|
2576
2580
|
],
|
|
2577
2581
|
loop: true
|
|
2578
2582
|
},
|
|
2579
2583
|
review: {
|
|
2580
2584
|
durations: [
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2585
|
+
650,
|
|
2586
|
+
650,
|
|
2587
|
+
650,
|
|
2588
|
+
650,
|
|
2589
|
+
650,
|
|
2590
|
+
650
|
|
2587
2591
|
],
|
|
2588
2592
|
loop: true
|
|
2589
2593
|
}
|
|
@@ -3199,6 +3203,12 @@ var PetService = class extends Service {
|
|
|
3199
3203
|
* disposed sessions are removed by the 'session/disposed' listener.
|
|
3200
3204
|
*/
|
|
3201
3205
|
sessionActivity = /* @__PURE__ */ new Map();
|
|
3206
|
+
/**
|
|
3207
|
+
* Sessions whose reward source is the official event stream. This metadata
|
|
3208
|
+
* outlives transient visual resets so a derived legacy `done` cannot reward
|
|
3209
|
+
* the same turn again after the pet is disabled and re-enabled.
|
|
3210
|
+
*/
|
|
3211
|
+
officialEventSessions = /* @__PURE__ */ new WeakSet();
|
|
3202
3212
|
constructor(ctx, config = {}) {
|
|
3203
3213
|
super(ctx, "pet");
|
|
3204
3214
|
this.persistDir = config.persistDir ?? petHomeDir();
|
|
@@ -3312,6 +3322,7 @@ var PetService = class extends Service {
|
|
|
3312
3322
|
setEnabled(enabled) {
|
|
3313
3323
|
this.enabled = enabled;
|
|
3314
3324
|
this.syncActivity();
|
|
3325
|
+
if (!enabled) this.resetActivity();
|
|
3315
3326
|
}
|
|
3316
3327
|
syncActivity() {
|
|
3317
3328
|
if (this.disposeActivity !== void 0) {
|
|
@@ -3336,10 +3347,12 @@ var PetService = class extends Service {
|
|
|
3336
3347
|
const transition = projectOfficialEvent(event, runtime);
|
|
3337
3348
|
if (transition === void 0) return;
|
|
3338
3349
|
runtime.officialEventsSeen = true;
|
|
3350
|
+
this.officialEventSessions.add(session);
|
|
3339
3351
|
this.applyActivity(session, transition.input, transition.whisper);
|
|
3340
3352
|
if (transition.completedTurn !== void 0) this.rewardTurn(String(session.id), transition.completedTurn);
|
|
3341
3353
|
}), this.ctx.on("session/disposed", (session) => {
|
|
3342
3354
|
this.ledger.forgetSession(String(session.id));
|
|
3355
|
+
this.officialEventSessions.delete(session);
|
|
3343
3356
|
this.sessionActivity.delete(session);
|
|
3344
3357
|
if (session !== this.displaySession) return;
|
|
3345
3358
|
this.displaySession = void 0;
|
|
@@ -3356,12 +3369,20 @@ var PetService = class extends Service {
|
|
|
3356
3369
|
};
|
|
3357
3370
|
})();
|
|
3358
3371
|
}
|
|
3372
|
+
/** Drop transient activity because terminal events missed while disabled cannot be replayed safely. */
|
|
3373
|
+
resetActivity() {
|
|
3374
|
+
this.displaySession = void 0;
|
|
3375
|
+
this.sessionActivity.clear();
|
|
3376
|
+
this.machine.onSessionDisposed();
|
|
3377
|
+
}
|
|
3359
3378
|
/** Return the per-session activity record, creating it on first sight. */
|
|
3360
3379
|
activityOf(session) {
|
|
3361
3380
|
let activity = this.sessionActivity.get(session);
|
|
3362
3381
|
if (activity === void 0) {
|
|
3382
|
+
const runtime = emptyProjectionRuntime(this.voicePools());
|
|
3383
|
+
runtime.officialEventsSeen = this.officialEventSessions.has(session);
|
|
3363
3384
|
activity = {
|
|
3364
|
-
runtime
|
|
3385
|
+
runtime,
|
|
3365
3386
|
machine: new PetStateMachine(this.stateConfig)
|
|
3366
3387
|
};
|
|
3367
3388
|
this.sessionActivity.set(session, activity);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PetSprite.d.ts","sourceRoot":"","sources":["../../../src/client/PetSprite.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAkE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAGnH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAIjD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAA;IAC7B,8EAA8E;IAC9E,UAAU,EAAE,aAAa,CAAA;IACzB,qDAAqD;IACrD,OAAO,EAAE,gBAAgB,CAAA;IACzB,sCAAsC;IACtC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,8BAA8B;IAC9B,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,sCAAsC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,sCAAsC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,+BAA+B;IAC/B,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,uDAAuD;IACvD,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,4DAA4D;IAC5D,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,2DAA2D;IAC3D,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,+CAA+C;IAC/C,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;CAC1B;
|
|
1
|
+
{"version":3,"file":"PetSprite.d.ts","sourceRoot":"","sources":["../../../src/client/PetSprite.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAkE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAGnH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAIjD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAA;IAC7B,8EAA8E;IAC9E,UAAU,EAAE,aAAa,CAAA;IACzB,qDAAqD;IACrD,OAAO,EAAE,gBAAgB,CAAA;IACzB,sCAAsC;IACtC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,8BAA8B;IAC9B,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,sCAAsC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,sCAAsC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,+BAA+B;IAC/B,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,uDAAuD;IACvD,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,4DAA4D;IAC5D,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,2DAA2D;IAC3D,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,+CAA+C;IAC/C,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;CAC1B;AAgGD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,WAAW,CAygB5D"}
|
|
@@ -57,35 +57,41 @@ function StatusOrnament(props) {
|
|
|
57
57
|
// frame instead — same as the reduced-motion static hold.
|
|
58
58
|
if (reduceMotion || segment.from === segment.to)
|
|
59
59
|
return;
|
|
60
|
-
let
|
|
60
|
+
let timer = 0;
|
|
61
61
|
let index = segment.from;
|
|
62
62
|
let elapsed = 0;
|
|
63
63
|
let last = performance.now();
|
|
64
|
-
const tick = (
|
|
65
|
-
const
|
|
66
|
-
|
|
64
|
+
const tick = () => {
|
|
65
|
+
const now = performance.now();
|
|
66
|
+
const delta = now - last;
|
|
67
|
+
last = now;
|
|
67
68
|
elapsed += delta;
|
|
68
|
-
const duration = decoration.durations[index] ??
|
|
69
|
+
const duration = decoration.durations[index] ?? 120;
|
|
70
|
+
// The segment's frame rate (duration ms, typically 90-160) is far
|
|
71
|
+
// below the rAF cadence, so a 60fps loop would spend ~90% of its
|
|
72
|
+
// ticks doing nothing. Schedule by the remaining time to the next
|
|
73
|
+
// frame instead — the ornament wakes once per frame, not once per
|
|
74
|
+
// screen refresh. A late wake (background tab, jank) carries extra
|
|
75
|
+
// elapsed time, so catch up every due frame like the sprite loop.
|
|
69
76
|
if (elapsed >= duration) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
index
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
//
|
|
78
|
-
// would churn style recalculations for no visual change.
|
|
77
|
+
do {
|
|
78
|
+
elapsed -= duration;
|
|
79
|
+
if (index < segment.to)
|
|
80
|
+
index += 1;
|
|
81
|
+
else if (decoration.loop)
|
|
82
|
+
index = segment.from;
|
|
83
|
+
} while (elapsed >= duration);
|
|
84
|
+
// Only advance the background when the frame actually changes.
|
|
79
85
|
el.style.backgroundPosition = position(index);
|
|
80
86
|
}
|
|
81
87
|
// A non-looping segment settles on its last frame; stop scheduling
|
|
82
88
|
// instead of repainting the same position every frame.
|
|
83
89
|
if (!decoration.loop && index === segment.to)
|
|
84
90
|
return;
|
|
85
|
-
|
|
91
|
+
timer = window.setTimeout(tick, Math.max(1, duration - elapsed));
|
|
86
92
|
};
|
|
87
|
-
|
|
88
|
-
return () =>
|
|
93
|
+
timer = window.setTimeout(tick, 0);
|
|
94
|
+
return () => window.clearTimeout(timer);
|
|
89
95
|
}, [shown, segmentKey, frameWidth, decoration.loop, durationsKey]);
|
|
90
96
|
if (!shown)
|
|
91
97
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAwB,aAAa,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAwB,aAAa,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAA;AAgEnI,wEAAwE;AACxE,eAAO,MAAM,MAAM,UAA2E,CAAA;AAE9F,qEAAqE;AACrE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACxE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACrD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC7D,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AACtF,YAAY,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AACpE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf;;;;WAIG;QACH,aAAa,CAAC,EAAE;YAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;SAAE,CAAA;KAC1E;CACF;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAqQ9C"}
|
|
@@ -16,6 +16,7 @@ import { createPetStore } from "./pet-store.js";
|
|
|
16
16
|
import { PetDockEntry } from "./PetDockEntry.js";
|
|
17
17
|
import { defaultPetRendererRegistry } from "./renderers/registry.js";
|
|
18
18
|
import { live2dRenderer } from "./renderers/live2d.js";
|
|
19
|
+
import { registerPetUiTeardown, takeoverPetUiTeardown } from "./ui-teardown.js";
|
|
19
20
|
import { PetSettingsSection, PetSettingsCardController } from "./PetSettingsCard.js";
|
|
20
21
|
import { NS, en, zh, t } from "./locales.js";
|
|
21
22
|
/** Same-origin JSON fetch helper (GET without body, POST with JSON body). */
|
|
@@ -87,9 +88,23 @@ export function apply(ctx) {
|
|
|
87
88
|
});
|
|
88
89
|
// The global pet entry, its store, and the poll loop live while the plugin
|
|
89
90
|
// is enabled; toggling the setting off hides the pet and stops polling.
|
|
91
|
+
// 'uiDead' marks a terminal teardown (takeover by a later bundle instance
|
|
92
|
+
// or fiber disposal): a taken-over or disposed instance must never remount
|
|
93
|
+
// from a late settings callback (issue #785).
|
|
90
94
|
let disposeUi;
|
|
95
|
+
let clearUiTeardown;
|
|
96
|
+
let uiDead = false;
|
|
97
|
+
const killUi = () => {
|
|
98
|
+
if (uiDead)
|
|
99
|
+
return;
|
|
100
|
+
uiDead = true;
|
|
101
|
+
clearUiTeardown?.();
|
|
102
|
+
clearUiTeardown = undefined;
|
|
103
|
+
disposeUi?.();
|
|
104
|
+
disposeUi = undefined;
|
|
105
|
+
};
|
|
91
106
|
const syncUi = () => {
|
|
92
|
-
if (enabled() && disposeUi === undefined) {
|
|
107
|
+
if (!uiDead && enabled() && disposeUi === undefined) {
|
|
93
108
|
// ONE store instance for the whole app, owned by this apply body. The
|
|
94
109
|
// pet is host-global (state/display/interactions are /api/pet/*
|
|
95
110
|
// endpoints with no session dimension), so the slot system's per-session
|
|
@@ -244,24 +259,56 @@ export function apply(ctx) {
|
|
|
244
259
|
// The entry therefore mounts straight onto document.body via a single
|
|
245
260
|
// React root for the page lifetime: PetSprite portals itself to body
|
|
246
261
|
// when visible, and the hidden-state summon button is fixed-positioned.
|
|
262
|
+
//
|
|
263
|
+
// Cross-instance single-mount guard (issue #785): take over the
|
|
264
|
+
// page-global slot first — the previous bundle instance's fiber may
|
|
265
|
+
// still be draining during a client reload, so unmount its React root
|
|
266
|
+
// and remove its container — then sweep containers left behind by
|
|
267
|
+
// instances that predate the teardown registry, so this mount is the
|
|
268
|
+
// page's only [data-dsh-pet-root].
|
|
269
|
+
takeoverPetUiTeardown();
|
|
270
|
+
for (const stale of Array.from(document.querySelectorAll('div[data-dsh-pet-root]'))) {
|
|
271
|
+
stale.remove();
|
|
272
|
+
}
|
|
247
273
|
const container = document.createElement('div');
|
|
248
274
|
container.dataset.dshPetRoot = '';
|
|
249
275
|
container.dataset.dshPlugin = 'pet';
|
|
250
276
|
document.body.appendChild(container);
|
|
251
277
|
const petRoot = createRoot(container);
|
|
252
278
|
petRoot.render(createElement(PetDockEntry, { ...injected(), t }));
|
|
279
|
+
let uiGone = false;
|
|
253
280
|
disposeUi = () => {
|
|
281
|
+
if (uiGone)
|
|
282
|
+
return;
|
|
283
|
+
uiGone = true;
|
|
284
|
+
clearUiTeardown?.();
|
|
285
|
+
clearUiTeardown = undefined;
|
|
254
286
|
petRoot.unmount();
|
|
255
287
|
container.remove();
|
|
256
288
|
disposePoll();
|
|
257
289
|
disposeUi = undefined;
|
|
258
290
|
};
|
|
291
|
+
// The slot teardown is the takeover hook a later apply body runs; it
|
|
292
|
+
// marks this instance terminal so a late settings callback from the
|
|
293
|
+
// still-draining instance cannot remount a second pet.
|
|
294
|
+
clearUiTeardown = registerPetUiTeardown(() => {
|
|
295
|
+
uiDead = true;
|
|
296
|
+
disposeUi?.();
|
|
297
|
+
});
|
|
259
298
|
}
|
|
260
|
-
else if (!enabled() && disposeUi !== undefined) {
|
|
299
|
+
else if (!uiDead && !enabled() && disposeUi !== undefined) {
|
|
261
300
|
disposeUi();
|
|
262
301
|
disposeUi = undefined;
|
|
263
302
|
}
|
|
264
303
|
};
|
|
265
|
-
|
|
304
|
+
// The settings subscription and the pet UI lifetime follow the fiber
|
|
305
|
+
// (issue #785): disposal drops the subscription and tears the UI down
|
|
306
|
+
// (terminal), so a hot-reloaded or re-injected bundle never leaves the
|
|
307
|
+
// previous React root, container, or poll loop behind on document.body.
|
|
308
|
+
const unsubscribeSettings = settingsScope.subscribe(syncUi);
|
|
309
|
+
ctx.effect(() => () => {
|
|
310
|
+
unsubscribeSettings();
|
|
311
|
+
killUi();
|
|
312
|
+
}, 'pet: client lifecycle');
|
|
266
313
|
syncUi();
|
|
267
314
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-bundle-instance teardown slot for the page-global pet root
|
|
3
|
+
* (issue #785).
|
|
4
|
+
*
|
|
5
|
+
* A client bundle swap (HMR rebuilt frame, plugin update, duplicate
|
|
6
|
+
* injection) runs a new apply body while the previous instance's fiber
|
|
7
|
+
* may still be draining. Module state does not survive the swap, so a
|
|
8
|
+
* closure guard only sees one apply body and the previous instance's
|
|
9
|
+
* container keeps sitting on document.body: the page shows two pets
|
|
10
|
+
* until a full refresh. The slot rides globalThis (which does survive)
|
|
11
|
+
* so a re-apply can find the previous instance and unmount its React
|
|
12
|
+
* root cleanly before mounting its own; the previous fiber's later
|
|
13
|
+
* disposal stays a no-op through the idempotent teardowns.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Claim the page-global pet UI slot with the current instance's teardown
|
|
17
|
+
* (React root unmount + container removal + poll stop).
|
|
18
|
+
* @param teardown - what a later instance runs to take the slot over.
|
|
19
|
+
* @returns a disposer that clears the slot when the current instance's
|
|
20
|
+
* UI is torn down (settings toggle, takeover, or fiber disposal).
|
|
21
|
+
*/
|
|
22
|
+
export declare function registerPetUiTeardown(teardown: () => void): () => void;
|
|
23
|
+
/**
|
|
24
|
+
* Run the previous instance's teardown if one is registered, so the
|
|
25
|
+
* re-applying instance becomes the sole owner of the page-global pet
|
|
26
|
+
* root. No-op when the previous fiber already tore down cleanly.
|
|
27
|
+
*/
|
|
28
|
+
export declare function takeoverPetUiTeardown(): void;
|
|
29
|
+
//# sourceMappingURL=ui-teardown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-teardown.d.ts","sourceRoot":"","sources":["../../../src/client/ui-teardown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAMtE;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAK5C"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-bundle-instance teardown slot for the page-global pet root
|
|
3
|
+
* (issue #785).
|
|
4
|
+
*
|
|
5
|
+
* A client bundle swap (HMR rebuilt frame, plugin update, duplicate
|
|
6
|
+
* injection) runs a new apply body while the previous instance's fiber
|
|
7
|
+
* may still be draining. Module state does not survive the swap, so a
|
|
8
|
+
* closure guard only sees one apply body and the previous instance's
|
|
9
|
+
* container keeps sitting on document.body: the page shows two pets
|
|
10
|
+
* until a full refresh. The slot rides globalThis (which does survive)
|
|
11
|
+
* so a re-apply can find the previous instance and unmount its React
|
|
12
|
+
* root cleanly before mounting its own; the previous fiber's later
|
|
13
|
+
* disposal stays a no-op through the idempotent teardowns.
|
|
14
|
+
*/
|
|
15
|
+
const SLOT = Symbol.for('dsh-pet.client-ui-teardown');
|
|
16
|
+
/**
|
|
17
|
+
* Claim the page-global pet UI slot with the current instance's teardown
|
|
18
|
+
* (React root unmount + container removal + poll stop).
|
|
19
|
+
* @param teardown - what a later instance runs to take the slot over.
|
|
20
|
+
* @returns a disposer that clears the slot when the current instance's
|
|
21
|
+
* UI is torn down (settings toggle, takeover, or fiber disposal).
|
|
22
|
+
*/
|
|
23
|
+
export function registerPetUiTeardown(teardown) {
|
|
24
|
+
const slot = globalThis;
|
|
25
|
+
slot[SLOT] = teardown;
|
|
26
|
+
return () => {
|
|
27
|
+
if (slot[SLOT] === teardown)
|
|
28
|
+
slot[SLOT] = undefined;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Run the previous instance's teardown if one is registered, so the
|
|
33
|
+
* re-applying instance becomes the sole owner of the page-global pet
|
|
34
|
+
* root. No-op when the previous fiber already tore down cleanly.
|
|
35
|
+
*/
|
|
36
|
+
export function takeoverPetUiTeardown() {
|
|
37
|
+
const slot = globalThis;
|
|
38
|
+
const teardown = slot[SLOT];
|
|
39
|
+
slot[SLOT] = undefined;
|
|
40
|
+
teardown?.();
|
|
41
|
+
}
|
package/lib/types/registry.d.ts
CHANGED
|
@@ -64,7 +64,11 @@ export interface PetTrackDef {
|
|
|
64
64
|
/** Track to switch to after a non-looping track finishes. */
|
|
65
65
|
fallback?: PetAnimation;
|
|
66
66
|
}
|
|
67
|
-
/**
|
|
67
|
+
/**
|
|
68
|
+
* Default per-track rhythm — the shared slow baseline every sprite2d pet
|
|
69
|
+
* plays unless its manifest overrides a track (user request: all pets were
|
|
70
|
+
* too fast at the legacy hatch-pet contract pace).
|
|
71
|
+
*/
|
|
68
72
|
export declare const DEFAULT_TRACK_PATTERNS: Record<PetAnimation, {
|
|
69
73
|
durations: number[];
|
|
70
74
|
loop: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC7D,OAAO,EAAuB,KAAK,UAAU,EAAE,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAC5F,OAAO,EAAuC,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAGxG,OAAO,EAA8B,KAAK,cAAc,EAAE,MAAM,kCAAkC,CAAA;AAElG,OAAO,EAAgE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAGrH,yDAAyD;AACzD,eAAO,MAAM,aAAa,EAAE,SAAS,YAAY,EAUhD,CAAA;AAED,6BAA6B;AAC7B,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,wDAAwD;AACxD,eAAO,MAAM,gBAAgB,EAAE,OAAqC,CAAA;AACpE,8CAA8C;AAC9C,eAAO,MAAM,mBAAmB,IAAI,CAAA;AACpC,2DAA2D;AAC3D,eAAO,MAAM,qBAAqB,IAAI,CAAA;AAEtC;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,SAAS,MAAM,EAAgC,CAAA;AAElF,wEAAwE;AACxE,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,EAAE,IAAI,GAAE,MAAkB,GAAG,MAAM,CAQnG;AAeD,yDAAyD;AACzD,MAAM,WAAW,WAAW;IAC1B,+CAA+C;IAC/C,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,uDAAuD;IACvD,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,yEAAyE;IACzE,IAAI,EAAE,OAAO,CAAA;IACb,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,YAAY,CAAA;CACxB;AAED
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/registry.ts"],"names":[],"mappings":"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"}
|
package/lib/types/registry.js
CHANGED
|
@@ -86,17 +86,21 @@ function assetUrl(prefix, id, file) {
|
|
|
86
86
|
const path = file.split('/').filter(segment => segment !== '').join('/');
|
|
87
87
|
return prefix + '/' + encodeURIComponent(id) + '/' + path;
|
|
88
88
|
}
|
|
89
|
-
/**
|
|
89
|
+
/**
|
|
90
|
+
* Default per-track rhythm — the shared slow baseline every sprite2d pet
|
|
91
|
+
* plays unless its manifest overrides a track (user request: all pets were
|
|
92
|
+
* too fast at the legacy hatch-pet contract pace).
|
|
93
|
+
*/
|
|
90
94
|
export const DEFAULT_TRACK_PATTERNS = {
|
|
91
|
-
idle: { durations: [
|
|
92
|
-
'running-right': { durations: [
|
|
93
|
-
'running-left': { durations: [
|
|
94
|
-
waving: { durations: [
|
|
95
|
-
jumping: { durations: [
|
|
96
|
-
failed: { durations: [
|
|
97
|
-
waiting: { durations: [
|
|
98
|
-
running: { durations: [
|
|
99
|
-
review: { durations: [
|
|
95
|
+
idle: { durations: [500, 500, 600, 500, 500, 600], loop: true },
|
|
96
|
+
'running-right': { durations: [300, 300, 300, 300, 300, 300, 300, 400], loop: true },
|
|
97
|
+
'running-left': { durations: [300, 300, 300, 300, 300, 300, 300, 400], loop: true },
|
|
98
|
+
waving: { durations: [450, 450, 450, 450], loop: true },
|
|
99
|
+
jumping: { durations: [400, 400, 400, 450, 450], loop: false, fallback: 'idle' },
|
|
100
|
+
failed: { durations: [550, 550, 550, 600, 650, 700, 550, 550], loop: false, fallback: 'idle' },
|
|
101
|
+
waiting: { durations: [550, 550, 600, 550, 550, 600], loop: true },
|
|
102
|
+
running: { durations: [330, 330, 330, 330, 330, 400], loop: true },
|
|
103
|
+
review: { durations: [650, 650, 650, 650, 650, 650], loop: true },
|
|
100
104
|
};
|
|
101
105
|
/** Stable id charset: keeps asset URLs plain and filesystem-safe. */
|
|
102
106
|
const PET_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
|
package/lib/types/service.d.ts
CHANGED
|
@@ -172,6 +172,12 @@ export declare class PetService extends Service {
|
|
|
172
172
|
* disposed sessions are removed by the 'session/disposed' listener.
|
|
173
173
|
*/
|
|
174
174
|
private readonly sessionActivity;
|
|
175
|
+
/**
|
|
176
|
+
* Sessions whose reward source is the official event stream. This metadata
|
|
177
|
+
* outlives transient visual resets so a derived legacy `done` cannot reward
|
|
178
|
+
* the same turn again after the pet is disabled and re-enabled.
|
|
179
|
+
*/
|
|
180
|
+
private readonly officialEventSessions;
|
|
175
181
|
constructor(ctx: Context, config?: PetConfig);
|
|
176
182
|
/**
|
|
177
183
|
* The draw-time voice-pool provider handed to every projection runtime.
|
|
@@ -215,6 +221,8 @@ export declare class PetService extends Service {
|
|
|
215
221
|
/** Start or stop the session-activity listeners that drive the pet. */
|
|
216
222
|
setEnabled(enabled: boolean): void;
|
|
217
223
|
private syncActivity;
|
|
224
|
+
/** Drop transient activity because terminal events missed while disabled cannot be replayed safely. */
|
|
225
|
+
private resetActivity;
|
|
218
226
|
/** Return the per-session activity record, creating it on first sight. */
|
|
219
227
|
private activityOf;
|
|
220
228
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/service.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/service.ts"],"names":[],"mappings":"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"}
|
package/lib/types/service.js
CHANGED
|
@@ -54,6 +54,12 @@ export class PetService extends Service {
|
|
|
54
54
|
* disposed sessions are removed by the 'session/disposed' listener.
|
|
55
55
|
*/
|
|
56
56
|
sessionActivity = new Map();
|
|
57
|
+
/**
|
|
58
|
+
* Sessions whose reward source is the official event stream. This metadata
|
|
59
|
+
* outlives transient visual resets so a derived legacy `done` cannot reward
|
|
60
|
+
* the same turn again after the pet is disabled and re-enabled.
|
|
61
|
+
*/
|
|
62
|
+
officialEventSessions = new WeakSet();
|
|
57
63
|
constructor(ctx, config = {}) {
|
|
58
64
|
super(ctx, 'pet');
|
|
59
65
|
this.persistDir = config.persistDir ?? petHomeDir();
|
|
@@ -164,6 +170,8 @@ export class PetService extends Service {
|
|
|
164
170
|
setEnabled(enabled) {
|
|
165
171
|
this.enabled = enabled;
|
|
166
172
|
this.syncActivity();
|
|
173
|
+
if (!enabled)
|
|
174
|
+
this.resetActivity();
|
|
167
175
|
}
|
|
168
176
|
syncActivity() {
|
|
169
177
|
if (this.disposeActivity !== undefined) {
|
|
@@ -200,6 +208,7 @@ export class PetService extends Service {
|
|
|
200
208
|
if (transition === undefined)
|
|
201
209
|
return;
|
|
202
210
|
runtime.officialEventsSeen = true;
|
|
211
|
+
this.officialEventSessions.add(session);
|
|
203
212
|
this.applyActivity(session, transition.input, transition.whisper);
|
|
204
213
|
if (transition.completedTurn !== undefined) {
|
|
205
214
|
this.rewardTurn(String(session.id), transition.completedTurn);
|
|
@@ -207,6 +216,7 @@ export class PetService extends Service {
|
|
|
207
216
|
}),
|
|
208
217
|
this.ctx.on('session/disposed', (session) => {
|
|
209
218
|
this.ledger.forgetSession(String(session.id));
|
|
219
|
+
this.officialEventSessions.delete(session);
|
|
210
220
|
this.sessionActivity.delete(session);
|
|
211
221
|
if (session !== this.displaySession)
|
|
212
222
|
return;
|
|
@@ -230,12 +240,20 @@ export class PetService extends Service {
|
|
|
230
240
|
dispose(); };
|
|
231
241
|
})();
|
|
232
242
|
}
|
|
243
|
+
/** Drop transient activity because terminal events missed while disabled cannot be replayed safely. */
|
|
244
|
+
resetActivity() {
|
|
245
|
+
this.displaySession = undefined;
|
|
246
|
+
this.sessionActivity.clear();
|
|
247
|
+
this.machine.onSessionDisposed();
|
|
248
|
+
}
|
|
233
249
|
/** Return the per-session activity record, creating it on first sight. */
|
|
234
250
|
activityOf(session) {
|
|
235
251
|
let activity = this.sessionActivity.get(session);
|
|
236
252
|
if (activity === undefined) {
|
|
253
|
+
const runtime = emptyProjectionRuntime(this.voicePools());
|
|
254
|
+
runtime.officialEventsSeen = this.officialEventSessions.has(session);
|
|
237
255
|
activity = {
|
|
238
|
-
runtime
|
|
256
|
+
runtime,
|
|
239
257
|
machine: new PetStateMachine(this.stateConfig),
|
|
240
258
|
};
|
|
241
259
|
this.sessionActivity.set(session, activity);
|