@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/client.js
CHANGED
|
@@ -245,26 +245,29 @@ window.__ModuleLoader__.load({
|
|
|
245
245
|
const reduceMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches === true;
|
|
246
246
|
el.style.backgroundPosition = position(segment.from);
|
|
247
247
|
if (reduceMotion || segment.from === segment.to) return;
|
|
248
|
-
let
|
|
248
|
+
let timer = 0;
|
|
249
249
|
let index = segment.from;
|
|
250
250
|
let elapsed = 0;
|
|
251
251
|
let last = performance.now();
|
|
252
|
-
const tick = (
|
|
253
|
-
const
|
|
254
|
-
|
|
252
|
+
const tick = () => {
|
|
253
|
+
const now = performance.now();
|
|
254
|
+
const delta = now - last;
|
|
255
|
+
last = now;
|
|
255
256
|
elapsed += delta;
|
|
256
|
-
const duration = decoration.durations[index] ??
|
|
257
|
+
const duration = decoration.durations[index] ?? 120;
|
|
257
258
|
if (elapsed >= duration) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
259
|
+
do {
|
|
260
|
+
elapsed -= duration;
|
|
261
|
+
if (index < segment.to) index += 1;
|
|
262
|
+
else if (decoration.loop) index = segment.from;
|
|
263
|
+
} while (elapsed >= duration);
|
|
261
264
|
el.style.backgroundPosition = position(index);
|
|
262
265
|
}
|
|
263
266
|
if (!decoration.loop && index === segment.to) return;
|
|
264
|
-
|
|
267
|
+
timer = window.setTimeout(tick, Math.max(1, duration - elapsed));
|
|
265
268
|
};
|
|
266
|
-
|
|
267
|
-
return () =>
|
|
269
|
+
timer = window.setTimeout(tick, 0);
|
|
270
|
+
return () => window.clearTimeout(timer);
|
|
268
271
|
}, [
|
|
269
272
|
shown,
|
|
270
273
|
segmentKey,
|
|
@@ -1233,6 +1236,48 @@ window.__ModuleLoader__.load({
|
|
|
1233
1236
|
}
|
|
1234
1237
|
};
|
|
1235
1238
|
//#endregion
|
|
1239
|
+
//#region src/client/ui-teardown.ts
|
|
1240
|
+
/**
|
|
1241
|
+
* Cross-bundle-instance teardown slot for the page-global pet root
|
|
1242
|
+
* (issue #785).
|
|
1243
|
+
*
|
|
1244
|
+
* A client bundle swap (HMR rebuilt frame, plugin update, duplicate
|
|
1245
|
+
* injection) runs a new apply body while the previous instance's fiber
|
|
1246
|
+
* may still be draining. Module state does not survive the swap, so a
|
|
1247
|
+
* closure guard only sees one apply body and the previous instance's
|
|
1248
|
+
* container keeps sitting on document.body: the page shows two pets
|
|
1249
|
+
* until a full refresh. The slot rides globalThis (which does survive)
|
|
1250
|
+
* so a re-apply can find the previous instance and unmount its React
|
|
1251
|
+
* root cleanly before mounting its own; the previous fiber's later
|
|
1252
|
+
* disposal stays a no-op through the idempotent teardowns.
|
|
1253
|
+
*/
|
|
1254
|
+
const SLOT = Symbol.for("dsh-pet.client-ui-teardown");
|
|
1255
|
+
/**
|
|
1256
|
+
* Claim the page-global pet UI slot with the current instance's teardown
|
|
1257
|
+
* (React root unmount + container removal + poll stop).
|
|
1258
|
+
* @param teardown - what a later instance runs to take the slot over.
|
|
1259
|
+
* @returns a disposer that clears the slot when the current instance's
|
|
1260
|
+
* UI is torn down (settings toggle, takeover, or fiber disposal).
|
|
1261
|
+
*/
|
|
1262
|
+
function registerPetUiTeardown(teardown) {
|
|
1263
|
+
const slot = globalThis;
|
|
1264
|
+
slot[SLOT] = teardown;
|
|
1265
|
+
return () => {
|
|
1266
|
+
if (slot[SLOT] === teardown) slot[SLOT] = void 0;
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1269
|
+
/**
|
|
1270
|
+
* Run the previous instance's teardown if one is registered, so the
|
|
1271
|
+
* re-applying instance becomes the sole owner of the page-global pet
|
|
1272
|
+
* root. No-op when the previous fiber already tore down cleanly.
|
|
1273
|
+
*/
|
|
1274
|
+
function takeoverPetUiTeardown() {
|
|
1275
|
+
const slot = globalThis;
|
|
1276
|
+
const teardown = slot[SLOT];
|
|
1277
|
+
slot[SLOT] = void 0;
|
|
1278
|
+
teardown?.();
|
|
1279
|
+
}
|
|
1280
|
+
//#endregion
|
|
1236
1281
|
//#region \0dsh-css:packages/dsh-pet/src/client/settings-card.module.css.mjs
|
|
1237
1282
|
const css$1 = ".kKk9aW_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.kKk9aW_card:hover{border-color:var(--dsw-alias-label-dimmed)}.kKk9aW_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.kKk9aW_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.kKk9aW_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.kKk9aW_headerStatic{border-radius:12px;align-items:center;gap:12px;width:100%;padding:14px 16px;display:flex}.kKk9aW_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.kKk9aW_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.kKk9aW_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.kKk9aW_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.kKk9aW_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.kKk9aW_chevronOpen{transform:rotate(180deg)}.kKk9aW_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.kKk9aW_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.kKk9aW_notExposed{color:var(--dsw-alias-state-warn-primary);margin:12px 0 0;font-size:12px;line-height:1.5}.kKk9aW_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.kKk9aW_failed{min-width:0;color:var(--dsw-alias-label-error);text-overflow:ellipsis;white-space:nowrap;flex:1;margin:0;font-size:12px;line-height:1.5;overflow:hidden}.kKk9aW_discard,.kKk9aW_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.kKk9aW_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.kKk9aW_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.kKk9aW_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.kKk9aW_discard:disabled,.kKk9aW_save:disabled{opacity:.4;cursor:default}.kKk9aW_discard:focus-visible,.kKk9aW_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.kKk9aW_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.kKk9aW_field+.kKk9aW_field{border-top:1px solid var(--dsw-alias-border-l2)}.kKk9aW_head{align-items:center;gap:8px;display:flex}.kKk9aW_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.kKk9aW_badges{align-items:center;gap:8px;display:inline-flex}.kKk9aW_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.kKk9aW_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.kKk9aW_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.kKk9aW_reset:disabled{cursor:default}.kKk9aW_reset:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px;outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.kKk9aW_input,.kKk9aW_select{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.kKk9aW_input:focus-visible,.kKk9aW_select:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.kKk9aW_input:disabled,.kKk9aW_select:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.kKk9aW_inputInvalid{border:1px solid var(--dsw-alias-label-error);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.kKk9aW_inputInvalid:focus-visible{outline:2px solid var(--dsw-alias-label-error);outline-offset:1px;border-color:var(--dsw-alias-label-error)}.kKk9aW_selectWrap{position:relative}.kKk9aW_selectButton{appearance:none;text-align:left;cursor:pointer;justify-content:space-between;align-items:center;gap:8px;width:100%;display:flex}.kKk9aW_selectLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.kKk9aW_selectChevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.kKk9aW_selectChevronOpen{transform:rotate(180deg)}.kKk9aW_selectPopup{z-index:40;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);max-height:240px;box-shadow:0 8px 24px var(--dsw-alias-bg-mask-2);opacity:0;border-radius:8px;flex-direction:column;padding:4px;transition:opacity .1s,transform .1s;display:flex;position:absolute;top:calc(100% + 4px);left:0;right:0;overflow-y:auto;transform:translateY(-4px)}.kKk9aW_selectPopupOpen{opacity:1;transform:none}.kKk9aW_selectPopupClose{opacity:0;pointer-events:none;transform:translateY(-4px)}.kKk9aW_selectOption{color:var(--dsw-alias-label-primary);cursor:pointer;white-space:nowrap;text-overflow:ellipsis;border-radius:6px;flex-shrink:0;padding:6px 10px;font-size:13px;line-height:1.5;overflow:hidden}.kKk9aW_selectOption:hover,.kKk9aW_selectOptionActive{background:var(--dsw-alias-interactive-bg-hover)}.kKk9aW_selectOptionSelected{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 10%, transparent);font-weight:500}.kKk9aW_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.kKk9aW_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}@media (prefers-reduced-motion:reduce){.kKk9aW_card,.kKk9aW_header,.kKk9aW_chevron,.kKk9aW_chevronOpen,.kKk9aW_discard,.kKk9aW_save,.kKk9aW_selectChevron,.kKk9aW_selectChevronOpen,.kKk9aW_selectPopup{transition:none}}";
|
|
1238
1283
|
const tagId$1 = "@linxin666/dsh-pet/settings-card.module.css";
|
|
@@ -2488,8 +2533,18 @@ window.__ModuleLoader__.load({
|
|
|
2488
2533
|
};
|
|
2489
2534
|
});
|
|
2490
2535
|
let disposeUi;
|
|
2536
|
+
let clearUiTeardown;
|
|
2537
|
+
let uiDead = false;
|
|
2538
|
+
const killUi = () => {
|
|
2539
|
+
if (uiDead) return;
|
|
2540
|
+
uiDead = true;
|
|
2541
|
+
clearUiTeardown?.();
|
|
2542
|
+
clearUiTeardown = void 0;
|
|
2543
|
+
disposeUi?.();
|
|
2544
|
+
disposeUi = void 0;
|
|
2545
|
+
};
|
|
2491
2546
|
const syncUi = () => {
|
|
2492
|
-
if (enabled() && disposeUi === void 0) {
|
|
2547
|
+
if (!uiDead && enabled() && disposeUi === void 0) {
|
|
2493
2548
|
const petStore = createPetStore().create();
|
|
2494
2549
|
const setSnapshot = petStore.actions.setSnapshot;
|
|
2495
2550
|
const setPets = petStore.actions.setPets;
|
|
@@ -2590,6 +2645,8 @@ window.__ModuleLoader__.load({
|
|
|
2590
2645
|
setFeedback(null);
|
|
2591
2646
|
}
|
|
2592
2647
|
});
|
|
2648
|
+
takeoverPetUiTeardown();
|
|
2649
|
+
for (const stale of Array.from(document.querySelectorAll("div[data-dsh-pet-root]"))) stale.remove();
|
|
2593
2650
|
const container = document.createElement("div");
|
|
2594
2651
|
container.dataset.dshPetRoot = "";
|
|
2595
2652
|
container.dataset.dshPlugin = "pet";
|
|
@@ -2599,18 +2656,31 @@ window.__ModuleLoader__.load({
|
|
|
2599
2656
|
...injected(),
|
|
2600
2657
|
t
|
|
2601
2658
|
}));
|
|
2659
|
+
let uiGone = false;
|
|
2602
2660
|
disposeUi = () => {
|
|
2661
|
+
if (uiGone) return;
|
|
2662
|
+
uiGone = true;
|
|
2663
|
+
clearUiTeardown?.();
|
|
2664
|
+
clearUiTeardown = void 0;
|
|
2603
2665
|
petRoot.unmount();
|
|
2604
2666
|
container.remove();
|
|
2605
2667
|
disposePoll();
|
|
2606
2668
|
disposeUi = void 0;
|
|
2607
2669
|
};
|
|
2608
|
-
|
|
2670
|
+
clearUiTeardown = registerPetUiTeardown(() => {
|
|
2671
|
+
uiDead = true;
|
|
2672
|
+
disposeUi?.();
|
|
2673
|
+
});
|
|
2674
|
+
} else if (!uiDead && !enabled() && disposeUi !== void 0) {
|
|
2609
2675
|
disposeUi();
|
|
2610
2676
|
disposeUi = void 0;
|
|
2611
2677
|
}
|
|
2612
2678
|
};
|
|
2613
|
-
settingsScope.subscribe(syncUi);
|
|
2679
|
+
const unsubscribeSettings = settingsScope.subscribe(syncUi);
|
|
2680
|
+
ctx.effect(() => () => {
|
|
2681
|
+
unsubscribeSettings();
|
|
2682
|
+
killUi();
|
|
2683
|
+
}, "pet: client lifecycle");
|
|
2614
2684
|
syncUi();
|
|
2615
2685
|
}
|
|
2616
2686
|
//#endregion
|