@linxin666/dsh-pet 0.3.2 → 0.3.4
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/README.i18n.yaml +2 -2
- package/README.md +25 -7
- package/README.zh.md +25 -7
- package/assets/ouo-neko/pet.json +31 -0
- package/assets/ouo-neko/previews/failed.gif +0 -0
- package/assets/ouo-neko/previews/idle.gif +0 -0
- package/assets/ouo-neko/previews/jumping.gif +0 -0
- package/assets/ouo-neko/previews/review.gif +0 -0
- package/assets/ouo-neko/previews/running-left.gif +0 -0
- package/assets/ouo-neko/previews/running-right.gif +0 -0
- package/assets/ouo-neko/previews/running.gif +0 -0
- package/assets/ouo-neko/previews/waiting.gif +0 -0
- package/assets/ouo-neko/previews/waving.gif +0 -0
- package/assets/ouo-neko/spritesheet.webp +0 -0
- package/contracts/pet-manifest-v2.schema.json +102 -1
- package/lib/client.js +830 -26
- package/lib/client.js.map +1 -1
- package/lib/index.js +1226 -24
- package/lib/types/client/PetDockEntry.d.ts +3 -0
- package/lib/types/client/PetDockEntry.d.ts.map +1 -1
- package/lib/types/client/PetDockEntry.js +18 -2
- package/lib/types/client/PetSprite.d.ts +23 -0
- package/lib/types/client/PetSprite.d.ts.map +1 -1
- package/lib/types/client/PetSprite.js +34 -29
- package/lib/types/client/PluginSettingsCard.d.ts +2 -0
- package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PluginSettingsCard.js +2 -2
- package/lib/types/client/drag-stream.d.ts +19 -0
- package/lib/types/client/drag-stream.d.ts.map +1 -0
- package/lib/types/client/drag-stream.js +26 -0
- package/lib/types/client/gameplay-hud.d.ts +50 -0
- package/lib/types/client/gameplay-hud.d.ts.map +1 -0
- package/lib/types/client/gameplay-hud.js +274 -0
- package/lib/types/client/index.d.ts +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +16 -0
- package/lib/types/client/locales.d.ts +34 -0
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/locales.js +36 -0
- package/lib/types/client/pet-store.d.ts +7 -1
- package/lib/types/client/pet-store.d.ts.map +1 -1
- package/lib/types/client/pet-store.js +4 -0
- package/lib/types/client/renderers/Frames2dVisualMount.d.ts +29 -0
- package/lib/types/client/renderers/Frames2dVisualMount.d.ts.map +1 -0
- package/lib/types/client/renderers/Frames2dVisualMount.js +83 -0
- package/lib/types/client/renderers/PetRendererSwitch.d.ts +6 -0
- package/lib/types/client/renderers/PetRendererSwitch.d.ts.map +1 -1
- package/lib/types/client/renderers/PetRendererSwitch.js +14 -1
- package/lib/types/client/renderers/frames2d.d.ts +36 -0
- package/lib/types/client/renderers/frames2d.d.ts.map +1 -0
- package/lib/types/client/renderers/frames2d.js +196 -0
- package/lib/types/client/settings-form.d.ts +1 -1
- package/lib/types/client/settings-form.js +1 -1
- package/lib/types/client/telemetry.d.ts +27 -0
- package/lib/types/client/telemetry.d.ts.map +1 -0
- package/lib/types/client/telemetry.js +97 -0
- package/lib/types/defaults.d.ts +6 -0
- package/lib/types/defaults.d.ts.map +1 -0
- package/lib/types/defaults.js +5 -0
- package/lib/types/dsh-home.d.ts.map +1 -1
- package/lib/types/dsh-home.js +9 -3
- package/lib/types/gameplay.d.ts +182 -0
- package/lib/types/gameplay.d.ts.map +1 -0
- package/lib/types/gameplay.js +618 -0
- package/lib/types/http.d.ts +1 -1
- package/lib/types/http.js +1 -1
- package/lib/types/index.d.ts +1 -1
- package/lib/types/index.js +1 -1
- package/lib/types/ledger.d.ts +14 -0
- package/lib/types/ledger.d.ts.map +1 -1
- package/lib/types/ledger.js +30 -0
- package/lib/types/loopback.d.ts +1 -1
- package/lib/types/loopback.js +1 -1
- package/lib/types/manifest-v2.d.ts +38 -1
- package/lib/types/manifest-v2.d.ts.map +1 -1
- package/lib/types/manifest-v2.js +182 -2
- package/lib/types/mount-once.d.ts +1 -1
- package/lib/types/mount-once.js +2 -2
- package/lib/types/persist.d.ts +4 -4
- package/lib/types/persist.d.ts.map +1 -1
- package/lib/types/persist.js +41 -4
- package/lib/types/registry.d.ts +29 -0
- package/lib/types/registry.d.ts.map +1 -1
- package/lib/types/registry.js +170 -1
- package/lib/types/routes.d.ts.map +1 -1
- package/lib/types/routes.js +22 -0
- package/lib/types/service.d.ts +56 -0
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +181 -0
- package/package.json +6 -3
- package/src/client/PetDockEntry.test.tsx +6 -0
- package/src/client/PetDockEntry.tsx +32 -1
- package/src/client/PetSprite.test.tsx +36 -6
- package/src/client/PetSprite.tsx +42 -21
- package/src/client/PluginSettingsCard.tsx +4 -2
- package/src/client/drag-stream.ts +35 -0
- package/src/client/gameplay-hud.test.tsx +338 -0
- package/src/client/gameplay-hud.tsx +403 -0
- package/src/client/index.ts +23 -2
- package/src/client/locales.ts +36 -0
- package/src/client/pet-store.ts +10 -1
- package/src/client/pet.module.css +192 -29
- package/src/client/renderers/Frames2dVisualMount.tsx +111 -0
- package/src/client/renderers/PetRendererSwitch.tsx +28 -1
- package/src/client/renderers/frames2d.test.ts +137 -0
- package/src/client/renderers/frames2d.ts +216 -0
- package/src/client/settings-card.module.css +5 -0
- package/src/client/settings-form.ts +1 -1
- package/src/client/telemetry.ts +105 -0
- package/src/defaults.ts +7 -0
- package/src/dsh-home.ts +9 -3
- package/src/gameplay.test.ts +239 -0
- package/src/gameplay.ts +692 -0
- package/src/http.ts +1 -1
- package/src/index.ts +1 -1
- package/src/ledger.test.ts +16 -0
- package/src/ledger.ts +31 -0
- package/src/loopback.ts +1 -1
- package/src/manifest-v2.test.ts +130 -1
- package/src/manifest-v2.ts +181 -2
- package/src/mount-once.ts +2 -2
- package/src/persist.test.ts +3 -0
- package/src/persist.ts +43 -6
- package/src/registry.test.ts +168 -1
- package/src/registry.ts +199 -1
- package/src/routes.ts +19 -0
- package/src/service.ts +208 -0
package/lib/client.js
CHANGED
|
@@ -42,6 +42,12 @@ window.__ModuleLoader__.load({
|
|
|
42
42
|
},
|
|
43
43
|
setFeedback: (draft, feedback) => {
|
|
44
44
|
draft.feedback = feedback;
|
|
45
|
+
},
|
|
46
|
+
setGameplayView: (draft, view) => {
|
|
47
|
+
if (draft.snapshot !== null) draft.snapshot = {
|
|
48
|
+
...draft.snapshot,
|
|
49
|
+
gameplay: view
|
|
50
|
+
};
|
|
45
51
|
}
|
|
46
52
|
}
|
|
47
53
|
});
|
|
@@ -165,7 +171,7 @@ window.__ModuleLoader__.load({
|
|
|
165
171
|
}
|
|
166
172
|
//#endregion
|
|
167
173
|
//#region \0dsh-css:packages/dsh-pet/src/client/pet.module.css.mjs
|
|
168
|
-
const css$2 = ".kz2Bea_float{pointer-events:auto;-webkit-user-select:none;user-select:none;will-change:transform;contain:layout style;flex-direction:column;align-items:center;display:flex;position:fixed}.kz2Bea_sprite{image-rendering:auto;touch-action:none;contain:paint}.kz2Bea_spriteWrap{flex:none;position:relative}.
|
|
174
|
+
const css$2 = ".kz2Bea_float{pointer-events:auto;-webkit-user-select:none;user-select:none;will-change:transform;contain:layout style;flex-direction:column;align-items:center;display:flex;position:fixed}.kz2Bea_sprite{image-rendering:auto;touch-action:none;contain:paint}.kz2Bea_spriteWrap{flex:none;position:relative}.kz2Bea_bubble{white-space:nowrap;color:#f4f7ff;pointer-events:none;border-radius:999px;margin-bottom:6px;padding:4px 10px;font-size:12px;line-height:1.4;animation:2.6s ease-out forwards kz2Bea_pet-bubble-pop;position:absolute;bottom:100%;box-shadow:0 2px 8px #00000040}.kz2Bea_bubblePet{background:#4d6bfef2}.kz2Bea_bubbleFeed{background:#0369a1f2}.kz2Bea_bubbleStatus{text-overflow:ellipsis;backdrop-filter:blur(8px);letter-spacing:.02em;background:linear-gradient(160deg,#131c36e6,#070b1af0);border:1px solid #7e98ff73;max-width:min(280px,100vw - 24px);padding:5px 12px;animation:.24s ease-out kz2Bea_pet-bubble-in;overflow:hidden;box-shadow:0 4px 12px #02061759,inset 0 1px #e2e8ff1a,0 0 12px #4d6bfe2e}.kz2Bea_bubbleWhisper{letter-spacing:.03em;animation:.32s cubic-bezier(.22,1,.36,1) kz2Bea_pet-whisper-in}.kz2Bea_bubbleWhisper:before{content:\"「\";opacity:.7;margin-right:2px}.kz2Bea_bubbleWhisper:after{content:\"」\";opacity:.7;margin-left:2px}.kz2Bea_bubbleStack{pointer-events:auto;flex-direction:column-reverse;align-items:center;gap:4px;display:flex;position:absolute;bottom:100%}.kz2Bea_bubbleAnchor{display:inline-flex;position:relative}.kz2Bea_bubbleMore{z-index:1;color:#eef2ff;cursor:pointer;background:linear-gradient(160deg,#4a68f5f2,#2f44b8f2);border:1px solid #7e98ff8c;border-radius:999px;justify-content:center;align-items:center;min-width:18px;height:18px;padding:0 5px;font-size:10px;font-weight:600;line-height:1;transition:filter .12s;display:inline-flex;position:absolute;top:-9px;right:-10px;box-shadow:0 2px 6px #02061766}.kz2Bea_bubbleMore:hover{filter:brightness(1.15)}.kz2Bea_bubbleMore:focus-visible{outline:none;box-shadow:0 0 0 2px #7e98ffe6}.kz2Bea_bubbleStack .kz2Bea_bubble{pointer-events:auto;margin-bottom:0;position:relative;bottom:auto}.kz2Bea_bubbleClickable{cursor:pointer;font:inherit;text-align:center;appearance:none;transition:filter .12s,box-shadow .12s}.kz2Bea_bubbleClickable:hover{filter:brightness(1.12)}.kz2Bea_bubbleClickable:focus-visible{outline:none;box-shadow:0 0 0 2px #7e98ffe6}@keyframes kz2Bea_pet-bubble-in{0%{opacity:0;transform:translateY(6px)scale(.96)}to{opacity:1;transform:translateY(0)scale(1)}}@keyframes kz2Bea_pet-whisper-in{0%{opacity:0;transform:translateY(8px)scale(.9)}60%{transform:translateY(-1px)scale(1.02)}to{opacity:1;transform:translateY(0)scale(1)}}@keyframes kz2Bea_pet-panel-in{0%{opacity:0;transform:translateY(4px)scale(.97)}to{opacity:1;transform:translateY(0)scale(1)}}@keyframes kz2Bea_pet-bubble-pop{0%{opacity:0;transform:translateY(6px)scale(.85)}15%{opacity:1;transform:translateY(0)scale(1.05)}25%{transform:translateY(0)scale(1)}75%{opacity:1}to{opacity:0;transform:translateY(-8px)scale(.95)}}.kz2Bea_panel{color:#e6ebf8;backdrop-filter:blur(10px);transform-origin:50% 0;background:linear-gradient(165deg,#131c36f2,#070b1af7);border:1px solid #7e98ff4d;border-radius:12px;flex-direction:column;gap:7px;min-width:148px;margin-top:8px;padding:10px 12px;font-size:12px;animation:.2s cubic-bezier(.22,1,.36,1) kz2Bea_pet-panel-in;display:flex;position:absolute;top:100%;box-shadow:0 8px 24px #02061773,0 0 0 1px #4d6bfe1a,inset 0 1px #e2e8ff14}.kz2Bea_panel:after{content:\"\";height:14px;position:absolute;bottom:100%;left:0;right:0}.kz2Bea_panelAbove{transform-origin:50% 100%;margin-top:0;margin-bottom:8px;top:auto;bottom:100%}.kz2Bea_panelAbove:after{top:100%;bottom:auto}.kz2Bea_rankRow{white-space:nowrap;justify-content:space-between;gap:10px;display:flex}.kz2Bea_nameCell{letter-spacing:.02em;background:linear-gradient(90deg,#a9c1ff,#6c8bff 60%,#4d6bfe);color:#0000;-webkit-background-clip:text;background-clip:text;font-weight:600}.kz2Bea_statRank{color:#9db4ff;font-weight:600}.kz2Bea_statTreats{color:#7dd3fc;font-weight:600}.kz2Bea_statPoints{color:#fcd34d;font-weight:600}.kz2Bea_renameRow{align-items:center;gap:6px;display:flex}.kz2Bea_nameInput{color:#e6ebf8;background:#131c36e6;border:1px solid #7e98ff80;border-radius:6px;outline:none;flex:1;min-width:0;padding:3px 6px;font-size:12px}.kz2Bea_nameInput:focus{border-color:#4d6bfe;box-shadow:0 0 0 2px #4d6bfe73}.kz2Bea_actions{gap:6px;display:flex}.kz2Bea_action{cursor:pointer;color:#fff;background:linear-gradient(#4a68f5,#3a55e0);border:none;border-radius:6px;flex:1;padding:4px 8px;font-size:12px;font-weight:600;transition:filter .12s,box-shadow .12s,transform .12s;box-shadow:0 2px 6px #4d6bfe4d}.kz2Bea_action:hover{filter:brightness(1.08);transform:translateY(-1px);box-shadow:0 4px 10px #4d6bfe66}.kz2Bea_action:active{filter:brightness(.94);transform:translateY(0);box-shadow:0 1px 4px #4d6bfe4d}.kz2Bea_action:focus-visible{outline:none;box-shadow:0 0 0 2px #7e98ffe6}.kz2Bea_summon{color:#8ea6ff;cursor:pointer;background:#070b1abf;border:1px dashed #7e98ff99;border-radius:999px;padding:2px 10px;font-size:11px;transition:border-color .12s,color .12s,background .12s,box-shadow .12s}.kz2Bea_summon:hover{color:#c3d3ff;background:#070b1ae6;border-color:#7e98fff2}.kz2Bea_summon:active{color:#8ea6ff;border-color:#7e98ffcc}.kz2Bea_summon:focus-visible{outline:none;box-shadow:0 0 0 2px #7e98ffe6}@media (prefers-reduced-motion:reduce){.kz2Bea_bubble,.kz2Bea_bubbleStatus,.kz2Bea_bubbleWhisper,.kz2Bea_panel{opacity:1;animation:none}.kz2Bea_action,.kz2Bea_summon,.kz2Bea_bubbleMore{transition:none}}.kz2Bea_gameplayHud{width:0;height:0;position:absolute;bottom:0;left:0}.kz2Bea_gameplayModeChip{color:#cdd7ff;white-space:nowrap;pointer-events:none;background:#7e98ff2e;border-radius:999px;padding:1px 8px;font-size:10px;position:absolute;top:0;left:0;transform:translateY(-100%)translateY(-4px)}.kz2Bea_gameplayCard{color:#dfe6ff;background:#070b1aeb;border:1px solid #7e98ff40;border-radius:10px;min-width:180px;max-height:340px;padding:10px 12px;font-size:12px;position:absolute;bottom:0;left:0;overflow-y:auto;transform:none;box-shadow:0 8px 24px #0006}.kz2Bea_gameplayBars{flex-direction:column;gap:4px;margin-bottom:8px;display:flex}.kz2Bea_gameplayBarRow{align-items:center;gap:6px;display:flex}.kz2Bea_gameplayBarLabel{color:#9aa8d8;flex-shrink:0;width:34px}.kz2Bea_gameplayBarTrack{background:#7e98ff26;border-radius:3px;flex:1;height:6px;overflow:hidden}.kz2Bea_gameplayBarFill{background:linear-gradient(90deg,#7e98ff,#9db4ff);border-radius:3px;height:100%;transition:width .3s;display:block}.kz2Bea_gameplayActions{flex-wrap:wrap;gap:6px;display:flex}.kz2Bea_gameplayShopItems{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin-bottom:8px;display:grid}.kz2Bea_gameplayShopItem{color:inherit;cursor:pointer;background:#7e98ff14;border:1px solid #7e98ff40;border-radius:8px;flex-direction:column;align-items:center;gap:4px;padding:8px 6px;display:flex}.kz2Bea_gameplayShopItem:hover{background:#7e98ff29;border-color:#7e98ffb3}.kz2Bea_gameplayShopItemImage{object-fit:contain;width:36px;height:36px;image-rendering:pixelated;pointer-events:none}.kz2Bea_gameplayShopItemLabel{text-align:center;font-size:11px}.kz2Bea_gameplayShopItemPrice{color:#ffd27e;font-size:10px}.kz2Bea_gameplayClose{color:#9aa8d8;cursor:pointer;background:0 0;border:none;padding:0 2px;font-size:14px;position:absolute;top:4px;right:8px}.kz2Bea_gameplayClose:hover{color:#fff}.kz2Bea_gameplayFloat{color:#3a2a00;white-space:nowrap;pointer-events:none;background:#ffd27eeb;border-radius:999px;padding:2px 10px;font-size:11px;animation:1.1s ease-out forwards kz2Bea_pet-gameplay-float;position:absolute;top:-8px;left:50%;transform:translate(-50%)}@keyframes kz2Bea_pet-gameplay-float{0%{opacity:0;transform:translate(-50%)translateY(6px)}20%{opacity:1}to{opacity:0;transform:translate(-50%)translateY(-18px)}}@media (prefers-reduced-motion:reduce){.kz2Bea_gameplayFloat{opacity:1;animation:none}.kz2Bea_gameplayBarFill{transition:none}}";
|
|
169
175
|
const tagId$2 = "@linxin666/dsh-pet/pet.module.css";
|
|
170
176
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
|
|
171
177
|
const tag = document.createElement("style");
|
|
@@ -186,14 +192,30 @@ window.__ModuleLoader__.load({
|
|
|
186
192
|
"bubbleStack": "kz2Bea_bubbleStack",
|
|
187
193
|
"bubbleStatus": "kz2Bea_bubbleStatus",
|
|
188
194
|
"bubbleWhisper": "kz2Bea_bubbleWhisper",
|
|
189
|
-
"closeButton": "kz2Bea_closeButton",
|
|
190
195
|
"float": "kz2Bea_float",
|
|
196
|
+
"gameplayActions": "kz2Bea_gameplayActions",
|
|
197
|
+
"gameplayBarFill": "kz2Bea_gameplayBarFill",
|
|
198
|
+
"gameplayBarLabel": "kz2Bea_gameplayBarLabel",
|
|
199
|
+
"gameplayBarRow": "kz2Bea_gameplayBarRow",
|
|
200
|
+
"gameplayBarTrack": "kz2Bea_gameplayBarTrack",
|
|
201
|
+
"gameplayBars": "kz2Bea_gameplayBars",
|
|
202
|
+
"gameplayCard": "kz2Bea_gameplayCard",
|
|
203
|
+
"gameplayClose": "kz2Bea_gameplayClose",
|
|
204
|
+
"gameplayFloat": "kz2Bea_gameplayFloat",
|
|
205
|
+
"gameplayHud": "kz2Bea_gameplayHud",
|
|
206
|
+
"gameplayModeChip": "kz2Bea_gameplayModeChip",
|
|
207
|
+
"gameplayShopItem": "kz2Bea_gameplayShopItem",
|
|
208
|
+
"gameplayShopItemImage": "kz2Bea_gameplayShopItemImage",
|
|
209
|
+
"gameplayShopItemLabel": "kz2Bea_gameplayShopItemLabel",
|
|
210
|
+
"gameplayShopItemPrice": "kz2Bea_gameplayShopItemPrice",
|
|
211
|
+
"gameplayShopItems": "kz2Bea_gameplayShopItems",
|
|
191
212
|
"nameCell": "kz2Bea_nameCell",
|
|
192
213
|
"nameInput": "kz2Bea_nameInput",
|
|
193
214
|
"panel": "kz2Bea_panel",
|
|
194
215
|
"panelAbove": "kz2Bea_panelAbove",
|
|
195
216
|
"pet-bubble-in": "kz2Bea_pet-bubble-in",
|
|
196
217
|
"pet-bubble-pop": "kz2Bea_pet-bubble-pop",
|
|
218
|
+
"pet-gameplay-float": "kz2Bea_pet-gameplay-float",
|
|
197
219
|
"pet-panel-in": "kz2Bea_pet-panel-in",
|
|
198
220
|
"pet-whisper-in": "kz2Bea_pet-whisper-in",
|
|
199
221
|
"rankRow": "kz2Bea_rankRow",
|
|
@@ -448,6 +470,7 @@ window.__ModuleLoader__.load({
|
|
|
448
470
|
};
|
|
449
471
|
(0, react.useEffect)(() => () => clearHideTimer(), []);
|
|
450
472
|
const onPointerDown = (e) => {
|
|
473
|
+
if (props.dragDisabled === true) return;
|
|
451
474
|
e.preventDefault();
|
|
452
475
|
e.target.setPointerCapture?.(e.pointerId);
|
|
453
476
|
const current = dragPos ?? {
|
|
@@ -467,7 +490,10 @@ window.__ModuleLoader__.load({
|
|
|
467
490
|
if (drag === null) return;
|
|
468
491
|
const dx = e.clientX - drag.startX;
|
|
469
492
|
const dy = e.clientY - drag.startY;
|
|
470
|
-
if (Math.abs(dx) > 4 || Math.abs(dy) > 4)
|
|
493
|
+
if (Math.abs(dx) > 4 || Math.abs(dy) > 4) {
|
|
494
|
+
if (!draggedRef.current) props.onDraggingChange?.(true);
|
|
495
|
+
draggedRef.current = true;
|
|
496
|
+
}
|
|
471
497
|
const right = clampOffset(drag.right - dx, window.innerWidth - 40);
|
|
472
498
|
const bottom = clampOffset(drag.bottom - dy, window.innerHeight - 40);
|
|
473
499
|
setDragPos({
|
|
@@ -478,6 +504,7 @@ window.__ModuleLoader__.load({
|
|
|
478
504
|
const onPointerUp = () => {
|
|
479
505
|
if (dragRef.current === null) return;
|
|
480
506
|
dragRef.current = null;
|
|
507
|
+
if (draggedRef.current) props.onDraggingChange?.(false);
|
|
481
508
|
if (dragPos !== null) props.onDragEnd(dragPos.right, dragPos.bottom);
|
|
482
509
|
};
|
|
483
510
|
const pos = dragPos ?? {
|
|
@@ -546,13 +573,13 @@ window.__ModuleLoader__.load({
|
|
|
546
573
|
hideTimerRef.current = window.setTimeout(() => setHovered(false), 300);
|
|
547
574
|
},
|
|
548
575
|
children: [
|
|
549
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
576
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
550
577
|
className: pet_module_css_default.spriteWrap,
|
|
551
578
|
style: {
|
|
552
579
|
width: spriteWidth,
|
|
553
580
|
height: spriteHeight
|
|
554
581
|
},
|
|
555
|
-
children:
|
|
582
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
556
583
|
ref: spriteRef,
|
|
557
584
|
className: pet_module_css_default.sprite,
|
|
558
585
|
style: {
|
|
@@ -569,29 +596,20 @@ window.__ModuleLoader__.load({
|
|
|
569
596
|
onPointerDown,
|
|
570
597
|
onPointerMove,
|
|
571
598
|
onPointerUp,
|
|
572
|
-
onClick: () => {
|
|
599
|
+
onClick: (e) => {
|
|
573
600
|
if (draggedRef.current) return;
|
|
601
|
+
if (props.onGameplayTap !== void 0 && spriteRef.current !== null) {
|
|
602
|
+
const rect = spriteRef.current.getBoundingClientRect();
|
|
603
|
+
if (rect.width > 0 && rect.height > 0) props.onGameplayTap((e.clientX - rect.left) / rect.width, (e.clientY - rect.top) / rect.height);
|
|
604
|
+
}
|
|
574
605
|
props.onPet();
|
|
575
606
|
},
|
|
576
607
|
role: "button",
|
|
577
608
|
"aria-label": definition.displayName,
|
|
578
609
|
children: props.visual
|
|
579
|
-
})
|
|
580
|
-
type: "button",
|
|
581
|
-
className: pet_module_css_default.closeButton,
|
|
582
|
-
"aria-label": panelLabel("hide", props.t("pet.hide")),
|
|
583
|
-
title: panelLabel("hide", props.t("pet.hide")),
|
|
584
|
-
"data-testid": "pet-close",
|
|
585
|
-
onPointerDown: (e) => {
|
|
586
|
-
e.stopPropagation();
|
|
587
|
-
},
|
|
588
|
-
onClick: (e) => {
|
|
589
|
-
e.stopPropagation();
|
|
590
|
-
props.onHide();
|
|
591
|
-
},
|
|
592
|
-
children: "×"
|
|
593
|
-
})]
|
|
610
|
+
})
|
|
594
611
|
}),
|
|
612
|
+
props.hud,
|
|
595
613
|
feedback !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
596
614
|
ref: bubbleRef,
|
|
597
615
|
className: clsx(pet_module_css_default.bubble, feedback.kind === "feed" ? pet_module_css_default.bubbleFeed : pet_module_css_default.bubblePet),
|
|
@@ -732,6 +750,12 @@ window.__ModuleLoader__.load({
|
|
|
732
750
|
className: pet_module_css_default.action,
|
|
733
751
|
onClick: props.onHide,
|
|
734
752
|
children: panelLabel("hide", props.t("pet.hide"))
|
|
753
|
+
}),
|
|
754
|
+
props.onGameplayMenu !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
755
|
+
type: "button",
|
|
756
|
+
className: pet_module_css_default.action,
|
|
757
|
+
onClick: props.onGameplayMenu,
|
|
758
|
+
children: props.t("pet.gameplay.menu")
|
|
735
759
|
})
|
|
736
760
|
]
|
|
737
761
|
})
|
|
@@ -741,6 +765,27 @@ window.__ModuleLoader__.load({
|
|
|
741
765
|
}), document.body);
|
|
742
766
|
}
|
|
743
767
|
//#endregion
|
|
768
|
+
//#region src/client/drag-stream.ts
|
|
769
|
+
/** Create the stream (one per pet activation, owned by the renderer switch). */
|
|
770
|
+
function createDragStream() {
|
|
771
|
+
let current = false;
|
|
772
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
773
|
+
return {
|
|
774
|
+
get: () => current,
|
|
775
|
+
subscribe(listener) {
|
|
776
|
+
listeners.add(listener);
|
|
777
|
+
return () => {
|
|
778
|
+
listeners.delete(listener);
|
|
779
|
+
};
|
|
780
|
+
},
|
|
781
|
+
push(dragging) {
|
|
782
|
+
if (dragging === current) return;
|
|
783
|
+
current = dragging;
|
|
784
|
+
for (const listener of [...listeners]) listener(dragging);
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
//#endregion
|
|
744
789
|
//#region src/client/renderers/registry.ts
|
|
745
790
|
/** Renderer dispatch table. */
|
|
746
791
|
var RendererRegistry = class {
|
|
@@ -888,6 +933,93 @@ window.__ModuleLoader__.load({
|
|
|
888
933
|
});
|
|
889
934
|
}
|
|
890
935
|
//#endregion
|
|
936
|
+
//#region src/client/renderers/Frames2dVisualMount.tsx
|
|
937
|
+
/**
|
|
938
|
+
* Frames2d visual mount — the React bridge between the pet center chrome
|
|
939
|
+
* and the imperative frames2d renderer, mirroring the live2d mount. The
|
|
940
|
+
* bridge owns the contract context (asset base, phase stream, interaction
|
|
941
|
+
* write-back, activation cleanups), feeds the polled phase into the stream,
|
|
942
|
+
* forwards the chrome's drag gesture onto the conventional 'drag' track
|
|
943
|
+
* (when the pet declares one), and renders the localized fallback card when
|
|
944
|
+
* the served config is invalid.
|
|
945
|
+
* @module @linxin666/dsh-pet/client/renderers/Frames2dVisualMount
|
|
946
|
+
*/
|
|
947
|
+
/** Mount the frames2d renderer as the sprite's visual (inside the chrome). */
|
|
948
|
+
function Frames2dVisualMount(props) {
|
|
949
|
+
const containerRef = (0, react.useRef)(null);
|
|
950
|
+
const streamRef = (0, react.useRef)(null);
|
|
951
|
+
const handleRef = (0, react.useRef)(null);
|
|
952
|
+
const [invalid, setInvalid] = (0, react.useState)(false);
|
|
953
|
+
(0, react.useEffect)(() => {
|
|
954
|
+
setInvalid(false);
|
|
955
|
+
const container = containerRef.current;
|
|
956
|
+
const frames2d = props.definition.frames2d;
|
|
957
|
+
if (container === null || frames2d === void 0) return void 0;
|
|
958
|
+
streamRef.current ??= createPhaseStream(props.phase);
|
|
959
|
+
const cleanups = [];
|
|
960
|
+
const ctx = {
|
|
961
|
+
petId: props.definition.id,
|
|
962
|
+
assetBase: "/pet/" + encodeURIComponent(props.definition.id),
|
|
963
|
+
container,
|
|
964
|
+
phase: streamRef.current,
|
|
965
|
+
interact: props.onPet,
|
|
966
|
+
onCleanup: (fn) => {
|
|
967
|
+
cleanups.push(fn);
|
|
968
|
+
}
|
|
969
|
+
};
|
|
970
|
+
let handle;
|
|
971
|
+
try {
|
|
972
|
+
handle = defaultPetRendererRegistry.mount("frames2d", ctx, frames2d);
|
|
973
|
+
} catch {
|
|
974
|
+
setInvalid(true);
|
|
975
|
+
return () => {
|
|
976
|
+
for (const fn of cleanups.splice(0)) fn();
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
handleRef.current = handle;
|
|
980
|
+
if (props.bus !== void 0) {
|
|
981
|
+
const gameplayBus = props.bus;
|
|
982
|
+
gameplayBus.setTrack = (track) => {
|
|
983
|
+
handleRef.current?.setState(track);
|
|
984
|
+
};
|
|
985
|
+
cleanups.push(() => {
|
|
986
|
+
gameplayBus.setTrack = void 0;
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
const dragTrack = props.definition.gameplay?.dragState ?? (frames2d.tracks.drag === void 0 ? void 0 : "drag");
|
|
990
|
+
const offDrag = props.drag.subscribe((dragging) => {
|
|
991
|
+
if (dragTrack === void 0) return;
|
|
992
|
+
if (dragging) {
|
|
993
|
+
handle.setState(dragTrack);
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
996
|
+
handle.setState(props.definition.gameplay?.dragEndState);
|
|
997
|
+
});
|
|
998
|
+
cleanups.push(offDrag);
|
|
999
|
+
return () => {
|
|
1000
|
+
handleRef.current = null;
|
|
1001
|
+
for (const fn of cleanups.splice(0)) fn();
|
|
1002
|
+
handle.dispose();
|
|
1003
|
+
};
|
|
1004
|
+
}, [props.definition]);
|
|
1005
|
+
(0, react.useEffect)(() => {
|
|
1006
|
+
streamRef.current?.push(props.phase);
|
|
1007
|
+
}, [props.phase]);
|
|
1008
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1009
|
+
ref: containerRef,
|
|
1010
|
+
"data-dsh-pet-frames2d": props.definition.id,
|
|
1011
|
+
style: {
|
|
1012
|
+
width: "100%",
|
|
1013
|
+
height: "100%",
|
|
1014
|
+
pointerEvents: "none"
|
|
1015
|
+
},
|
|
1016
|
+
children: invalid && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1017
|
+
"data-dsh-pet-frames2d-error": "invalid-config",
|
|
1018
|
+
children: props.t("pet.renderer.unavailable", { renderer: "frames2d" })
|
|
1019
|
+
})
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
//#endregion
|
|
891
1023
|
//#region src/client/renderers/PetRendererSwitch.tsx
|
|
892
1024
|
/**
|
|
893
1025
|
* Renderer switch — the client dispatch seam of the pet center (issue #623,
|
|
@@ -901,7 +1033,27 @@ window.__ModuleLoader__.load({
|
|
|
901
1033
|
/** Dispatch one pet definition to its renderer; unknown kinds get a card. */
|
|
902
1034
|
function PetRendererSwitch(props) {
|
|
903
1035
|
const renderer = props.definition.renderer ?? "sprite2d";
|
|
1036
|
+
const dragRef = (0, react.useRef)(null);
|
|
1037
|
+
if (dragRef.current === null || dragRef.current.id !== props.definition.id) dragRef.current = {
|
|
1038
|
+
id: props.definition.id,
|
|
1039
|
+
stream: createDragStream()
|
|
1040
|
+
};
|
|
1041
|
+
const drag = props.drag ?? dragRef.current.stream;
|
|
904
1042
|
if (renderer === "sprite2d") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: props.children });
|
|
1043
|
+
if (renderer === "frames2d" && defaultPetRendererRegistry.has("frames2d") && (0, react.isValidElement)(props.children)) {
|
|
1044
|
+
const visual = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Frames2dVisualMount, {
|
|
1045
|
+
definition: props.definition,
|
|
1046
|
+
phase: props.phase,
|
|
1047
|
+
onPet: props.onPet,
|
|
1048
|
+
drag,
|
|
1049
|
+
...props.bus === void 0 ? {} : { bus: props.bus },
|
|
1050
|
+
t: props.t
|
|
1051
|
+
});
|
|
1052
|
+
return (0, react.cloneElement)(props.children, {
|
|
1053
|
+
visual,
|
|
1054
|
+
onDraggingChange: (dragging) => drag.push(dragging)
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
905
1057
|
if (renderer === "live2d" && defaultPetRendererRegistry.has("live2d") && (0, react.isValidElement)(props.children)) {
|
|
906
1058
|
const visual = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Live2dVisualMount, {
|
|
907
1059
|
definition: props.definition,
|
|
@@ -917,6 +1069,362 @@ window.__ModuleLoader__.load({
|
|
|
917
1069
|
});
|
|
918
1070
|
}
|
|
919
1071
|
//#endregion
|
|
1072
|
+
//#region src/gameplay.ts
|
|
1073
|
+
/** The zone one normalized hit-box point lands in, if any. */
|
|
1074
|
+
function touchZoneAt(touch, yFraction) {
|
|
1075
|
+
return touch.zones.find((zone) => yFraction >= zone.y0 && yFraction < zone.y1);
|
|
1076
|
+
}
|
|
1077
|
+
//#endregion
|
|
1078
|
+
//#region src/client/gameplay-hud.tsx
|
|
1079
|
+
/**
|
|
1080
|
+
* Gameplay HUD — the client half of the manifest 'gameplay' block (miku-pet
|
|
1081
|
+
* generalization). One component owns everything the block needs: the stat
|
|
1082
|
+
* bars and shop card (menu / shop pages), the touch-zone tap
|
|
1083
|
+
* handling, the idle director rolls, the work and sleep loops, and the
|
|
1084
|
+
* float-text toasts. It talks to the host through the injected verb API,
|
|
1085
|
+
* writes results straight back into the store (the 2 s poll stays the
|
|
1086
|
+
* backstop), and steers the frames2d renderer through the per-pet bus.
|
|
1087
|
+
* @module @linxin666/dsh-pet/client/GameplayHud
|
|
1088
|
+
*/
|
|
1089
|
+
let floatSeq = 0;
|
|
1090
|
+
/** The gameplay overlay for one frames2d pet that declares 'gameplay'. */
|
|
1091
|
+
function GameplayHud(props) {
|
|
1092
|
+
const { definition, store, api, bus } = props;
|
|
1093
|
+
const ui = (0, react.useSyncExternalStore)(store.subscribe, store.getSnapshot);
|
|
1094
|
+
const def = definition.gameplay;
|
|
1095
|
+
const view = ui.snapshot?.gameplay;
|
|
1096
|
+
const phase = ui.snapshot?.phase ?? "idle";
|
|
1097
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
1098
|
+
const [page, setPage] = (0, react.useState)("root");
|
|
1099
|
+
const hudRef = (0, react.useRef)(null);
|
|
1100
|
+
const cardRef = (0, react.useRef)(null);
|
|
1101
|
+
const [floats, setFloats] = (0, react.useState)([]);
|
|
1102
|
+
const modeRef = (0, react.useRef)(view?.mode ?? null);
|
|
1103
|
+
modeRef.current = view?.mode ?? null;
|
|
1104
|
+
const phaseRef = (0, react.useRef)(phase);
|
|
1105
|
+
phaseRef.current = phase;
|
|
1106
|
+
const draggingRef = (0, react.useRef)(false);
|
|
1107
|
+
const touchLockUntilRef = (0, react.useRef)(0);
|
|
1108
|
+
const missRef = (0, react.useRef)(0);
|
|
1109
|
+
const busyRef = (0, react.useRef)(false);
|
|
1110
|
+
const tr = props.t;
|
|
1111
|
+
const statLabel = (name) => tr("pet.gameplay.stat." + name);
|
|
1112
|
+
const currencyLabel = (name) => tr("pet.gameplay.currency." + name);
|
|
1113
|
+
const pushFloat = (text) => {
|
|
1114
|
+
const id = ++floatSeq;
|
|
1115
|
+
setFloats((list) => [...list.slice(-3), {
|
|
1116
|
+
id,
|
|
1117
|
+
text
|
|
1118
|
+
}]);
|
|
1119
|
+
window.setTimeout(() => {
|
|
1120
|
+
setFloats((list) => list.filter((entry) => entry.id !== id));
|
|
1121
|
+
}, 1100);
|
|
1122
|
+
};
|
|
1123
|
+
const applyResult = (result) => {
|
|
1124
|
+
if (result.view !== void 0) store.actions.setGameplayView(result.view);
|
|
1125
|
+
};
|
|
1126
|
+
(0, react.useEffect)(() => {
|
|
1127
|
+
if (def === void 0) return void 0;
|
|
1128
|
+
bus.tap = (fx, fy) => {
|
|
1129
|
+
if (modeRef.current === "sleep") {
|
|
1130
|
+
api.setMode(null).then(applyResult, () => void 0);
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1133
|
+
if (modeRef.current === "work") return;
|
|
1134
|
+
const box = def.hitBox ?? {
|
|
1135
|
+
x0: 0,
|
|
1136
|
+
y0: 0,
|
|
1137
|
+
x1: 1,
|
|
1138
|
+
y1: 1
|
|
1139
|
+
};
|
|
1140
|
+
const hx = (fx - box.x0) / (box.x1 - box.x0);
|
|
1141
|
+
const hy = (fy - box.y0) / (box.y1 - box.y0);
|
|
1142
|
+
if (hx < 0 || hx > 1 || hy < 0 || hy > 1) return;
|
|
1143
|
+
if (Date.now() < touchLockUntilRef.current) {
|
|
1144
|
+
api.touch().then(applyResult, () => void 0);
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
const zone = def.touch === void 0 ? void 0 : touchZoneAt(def.touch, hy);
|
|
1148
|
+
if (zone === void 0) return;
|
|
1149
|
+
api.touch(zone.name).then((result) => {
|
|
1150
|
+
applyResult(result);
|
|
1151
|
+
if (result.hit !== true) return;
|
|
1152
|
+
if (result.state !== void 0) {
|
|
1153
|
+
bus.setTrack?.(result.state);
|
|
1154
|
+
const holdMs = result.stateMs ?? 3e3;
|
|
1155
|
+
touchLockUntilRef.current = Date.now() + holdMs;
|
|
1156
|
+
window.setTimeout(() => {
|
|
1157
|
+
if (Date.now() >= touchLockUntilRef.current) bus.setTrack?.(void 0);
|
|
1158
|
+
}, holdMs);
|
|
1159
|
+
}
|
|
1160
|
+
if (result.phrase !== void 0) store.actions.setFeedback({
|
|
1161
|
+
text: result.phrase,
|
|
1162
|
+
kind: "none",
|
|
1163
|
+
at: Date.now()
|
|
1164
|
+
});
|
|
1165
|
+
}, () => void 0);
|
|
1166
|
+
};
|
|
1167
|
+
return () => {
|
|
1168
|
+
bus.tap = void 0;
|
|
1169
|
+
};
|
|
1170
|
+
}, [definition.id, def]);
|
|
1171
|
+
(0, react.useEffect)(() => {
|
|
1172
|
+
bus.openCard = (next) => {
|
|
1173
|
+
setOpen((prev) => next ?? !prev);
|
|
1174
|
+
setPage("root");
|
|
1175
|
+
};
|
|
1176
|
+
return () => {
|
|
1177
|
+
bus.openCard = void 0;
|
|
1178
|
+
};
|
|
1179
|
+
}, [bus]);
|
|
1180
|
+
(0, react.useLayoutEffect)(() => {
|
|
1181
|
+
if (!open) return void 0;
|
|
1182
|
+
const hud = hudRef.current;
|
|
1183
|
+
const card = cardRef.current;
|
|
1184
|
+
if (hud === null || card === null) return void 0;
|
|
1185
|
+
const place = () => {
|
|
1186
|
+
const box = hud.parentElement?.getBoundingClientRect();
|
|
1187
|
+
if (box === void 0) return;
|
|
1188
|
+
const gap = 8;
|
|
1189
|
+
const width = card.getBoundingClientRect().width;
|
|
1190
|
+
const x = window.innerWidth - box.right >= width + gap ? box.width + gap : -(width + gap);
|
|
1191
|
+
card.style.transform = "translate(" + Math.round(x) + "px, " + Math.round(-box.height / 2) + "px) translateY(50%)";
|
|
1192
|
+
card.style.maxHeight = Math.round(box.height) + "px";
|
|
1193
|
+
};
|
|
1194
|
+
place();
|
|
1195
|
+
window.addEventListener("resize", place);
|
|
1196
|
+
return () => {
|
|
1197
|
+
window.removeEventListener("resize", place);
|
|
1198
|
+
};
|
|
1199
|
+
}, [open, page]);
|
|
1200
|
+
(0, react.useEffect)(() => {
|
|
1201
|
+
return props.drag.subscribe((dragging) => {
|
|
1202
|
+
draggingRef.current = dragging;
|
|
1203
|
+
if (dragging && modeRef.current === "sleep") api.setMode(null).then(applyResult, () => void 0);
|
|
1204
|
+
});
|
|
1205
|
+
}, [props.drag]);
|
|
1206
|
+
(0, react.useEffect)(() => {
|
|
1207
|
+
const director = def?.idleDirector;
|
|
1208
|
+
if (def === void 0 || director === void 0) return void 0;
|
|
1209
|
+
const total = director.idleWeight + director.acts.reduce((sum, act) => sum + act.weight, 0);
|
|
1210
|
+
if (total <= 0) return void 0;
|
|
1211
|
+
const timer = window.setInterval(() => {
|
|
1212
|
+
if (phaseRef.current !== "idle") return;
|
|
1213
|
+
if (modeRef.current !== null || draggingRef.current) return;
|
|
1214
|
+
if (Date.now() < touchLockUntilRef.current) return;
|
|
1215
|
+
let pickedAct;
|
|
1216
|
+
if (missRef.current >= director.maxMiss) {
|
|
1217
|
+
const actTotal = director.acts.reduce((sum, act) => sum + act.weight, 0);
|
|
1218
|
+
let actRoll = Math.random() * actTotal;
|
|
1219
|
+
for (const act of director.acts) {
|
|
1220
|
+
actRoll -= act.weight;
|
|
1221
|
+
if (actRoll < 0) {
|
|
1222
|
+
pickedAct = act;
|
|
1223
|
+
break;
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
} else {
|
|
1227
|
+
let roll = Math.random() * total;
|
|
1228
|
+
for (const act of director.acts) {
|
|
1229
|
+
roll -= act.weight;
|
|
1230
|
+
if (roll < 0) {
|
|
1231
|
+
pickedAct = act;
|
|
1232
|
+
break;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
if (pickedAct === void 0) {
|
|
1237
|
+
missRef.current += 1;
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
missRef.current = 0;
|
|
1241
|
+
bus.setTrack?.(pickedAct.track);
|
|
1242
|
+
if (pickedAct.phrases !== void 0 && pickedAct.phrases.length > 0) {
|
|
1243
|
+
const phrase = pickedAct.phrases[Math.floor(Math.random() * pickedAct.phrases.length)];
|
|
1244
|
+
store.actions.setFeedback({
|
|
1245
|
+
text: phrase,
|
|
1246
|
+
kind: "none",
|
|
1247
|
+
at: Date.now()
|
|
1248
|
+
});
|
|
1249
|
+
}
|
|
1250
|
+
}, director.intervalMs);
|
|
1251
|
+
return () => window.clearInterval(timer);
|
|
1252
|
+
}, [definition.id, def]);
|
|
1253
|
+
(0, react.useEffect)(() => {
|
|
1254
|
+
const work = def?.work;
|
|
1255
|
+
if (def === void 0 || work === void 0 || view?.mode !== "work") return void 0;
|
|
1256
|
+
bus.setTrack?.(work.state);
|
|
1257
|
+
let resultTimer = 0;
|
|
1258
|
+
const timer = window.setInterval(() => {
|
|
1259
|
+
if (busyRef.current) return;
|
|
1260
|
+
busyRef.current = true;
|
|
1261
|
+
api.workTick().then((result) => {
|
|
1262
|
+
busyRef.current = false;
|
|
1263
|
+
applyResult(result);
|
|
1264
|
+
if (result.ok !== true || result.outcome === void 0) return;
|
|
1265
|
+
const resultTrack = result.outcome === "success" ? work.successState : work.failState;
|
|
1266
|
+
const hold = result.outcome === "success" ? work.resultMs?.success ?? 1300 : work.resultMs?.fail ?? 1900;
|
|
1267
|
+
bus.setTrack?.(resultTrack);
|
|
1268
|
+
resultTimer = window.setTimeout(() => {
|
|
1269
|
+
if (modeRef.current === "work") bus.setTrack?.(work.state);
|
|
1270
|
+
}, hold);
|
|
1271
|
+
}, () => {
|
|
1272
|
+
busyRef.current = false;
|
|
1273
|
+
});
|
|
1274
|
+
}, work.tickMs);
|
|
1275
|
+
return () => {
|
|
1276
|
+
window.clearInterval(timer);
|
|
1277
|
+
window.clearTimeout(resultTimer);
|
|
1278
|
+
bus.setTrack?.(void 0);
|
|
1279
|
+
};
|
|
1280
|
+
}, [
|
|
1281
|
+
definition.id,
|
|
1282
|
+
def,
|
|
1283
|
+
view?.mode
|
|
1284
|
+
]);
|
|
1285
|
+
(0, react.useEffect)(() => {
|
|
1286
|
+
const sleep = def?.sleep;
|
|
1287
|
+
if (def === void 0 || sleep === void 0 || view?.mode !== "sleep") return void 0;
|
|
1288
|
+
bus.setTrack?.(sleep.state);
|
|
1289
|
+
return () => bus.setTrack?.(void 0);
|
|
1290
|
+
}, [
|
|
1291
|
+
definition.id,
|
|
1292
|
+
def,
|
|
1293
|
+
view?.mode
|
|
1294
|
+
]);
|
|
1295
|
+
if (def === void 0 || view === void 0) return null;
|
|
1296
|
+
const mode = view.mode;
|
|
1297
|
+
const stats = def.stats ?? {};
|
|
1298
|
+
const shop = def.shop;
|
|
1299
|
+
const buy = (itemId) => {
|
|
1300
|
+
api.buy(itemId).then((result) => {
|
|
1301
|
+
applyResult(result);
|
|
1302
|
+
if (result.ok !== true) {
|
|
1303
|
+
if (result.error === "insufficient-funds") {
|
|
1304
|
+
const item = shop?.items.find((entry) => entry.id === itemId);
|
|
1305
|
+
pushFloat(tr("pet.gameplay.insufficient", { currency: currencyLabel(item?.currency ?? "treats") }));
|
|
1306
|
+
}
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
if (result.prize !== void 0) pushFloat(tr("pet.gameplay.prize", {
|
|
1310
|
+
amount: result.prize.amount,
|
|
1311
|
+
currency: currencyLabel(result.prize.currency)
|
|
1312
|
+
}));
|
|
1313
|
+
}, () => void 0);
|
|
1314
|
+
};
|
|
1315
|
+
const setMode = (next) => {
|
|
1316
|
+
api.setMode(next).then(applyResult, () => void 0);
|
|
1317
|
+
};
|
|
1318
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1319
|
+
ref: hudRef,
|
|
1320
|
+
className: pet_module_css_default.gameplayHud,
|
|
1321
|
+
"data-dsh-pet-gameplay": definition.id,
|
|
1322
|
+
children: [
|
|
1323
|
+
floats.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1324
|
+
className: pet_module_css_default.gameplayFloat,
|
|
1325
|
+
children: entry.text
|
|
1326
|
+
}, entry.id)),
|
|
1327
|
+
mode !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1328
|
+
className: pet_module_css_default.gameplayModeChip,
|
|
1329
|
+
children: tr(mode === "work" ? "pet.gameplay.working" : "pet.gameplay.sleeping")
|
|
1330
|
+
}),
|
|
1331
|
+
open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1332
|
+
ref: cardRef,
|
|
1333
|
+
className: pet_module_css_default.gameplayCard,
|
|
1334
|
+
"data-page": page,
|
|
1335
|
+
children: [
|
|
1336
|
+
page === "root" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1337
|
+
className: pet_module_css_default.gameplayBars,
|
|
1338
|
+
children: Object.entries(stats).map(([name, stat]) => {
|
|
1339
|
+
const value = view.stats[name] ?? 0;
|
|
1340
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1341
|
+
className: pet_module_css_default.gameplayBarRow,
|
|
1342
|
+
title: statLabel(name) + " " + String(value) + "/" + String(stat.max),
|
|
1343
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1344
|
+
className: pet_module_css_default.gameplayBarLabel,
|
|
1345
|
+
children: statLabel(name)
|
|
1346
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1347
|
+
className: pet_module_css_default.gameplayBarTrack,
|
|
1348
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1349
|
+
className: pet_module_css_default.gameplayBarFill,
|
|
1350
|
+
style: { width: Math.round(value / stat.max * 100) + "%" }
|
|
1351
|
+
})
|
|
1352
|
+
})]
|
|
1353
|
+
}, name);
|
|
1354
|
+
})
|
|
1355
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1356
|
+
className: pet_module_css_default.gameplayActions,
|
|
1357
|
+
children: [
|
|
1358
|
+
def.work !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1359
|
+
type: "button",
|
|
1360
|
+
className: pet_module_css_default.action,
|
|
1361
|
+
onClick: () => setMode(mode === "work" ? null : "work"),
|
|
1362
|
+
children: tr(mode === "work" ? "pet.gameplay.stopWork" : "pet.gameplay.work")
|
|
1363
|
+
}),
|
|
1364
|
+
def.sleep !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1365
|
+
type: "button",
|
|
1366
|
+
className: pet_module_css_default.action,
|
|
1367
|
+
onClick: () => setMode(mode === "sleep" ? null : "sleep"),
|
|
1368
|
+
children: tr(mode === "sleep" ? "pet.gameplay.wake" : "pet.gameplay.sleep")
|
|
1369
|
+
}),
|
|
1370
|
+
shop !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1371
|
+
type: "button",
|
|
1372
|
+
className: pet_module_css_default.action,
|
|
1373
|
+
onClick: () => setPage("shop"),
|
|
1374
|
+
children: tr("pet.gameplay.shop")
|
|
1375
|
+
})
|
|
1376
|
+
]
|
|
1377
|
+
})] }),
|
|
1378
|
+
page === "shop" && shop !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1379
|
+
className: pet_module_css_default.gameplayShopItems,
|
|
1380
|
+
children: shop.items.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1381
|
+
type: "button",
|
|
1382
|
+
className: pet_module_css_default.gameplayShopItem,
|
|
1383
|
+
onClick: () => buy(item.id),
|
|
1384
|
+
title: item.label + " — " + String(item.price) + " " + currencyLabel(item.currency),
|
|
1385
|
+
children: [
|
|
1386
|
+
item.image !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
1387
|
+
className: pet_module_css_default.gameplayShopItemImage,
|
|
1388
|
+
src: item.image,
|
|
1389
|
+
alt: "",
|
|
1390
|
+
draggable: false
|
|
1391
|
+
}),
|
|
1392
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1393
|
+
className: pet_module_css_default.gameplayShopItemLabel,
|
|
1394
|
+
children: item.label
|
|
1395
|
+
}),
|
|
1396
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1397
|
+
className: pet_module_css_default.gameplayShopItemPrice,
|
|
1398
|
+
children: [
|
|
1399
|
+
item.price,
|
|
1400
|
+
" ",
|
|
1401
|
+
currencyLabel(item.currency)
|
|
1402
|
+
]
|
|
1403
|
+
})
|
|
1404
|
+
]
|
|
1405
|
+
}, item.id))
|
|
1406
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1407
|
+
className: pet_module_css_default.gameplayActions,
|
|
1408
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1409
|
+
type: "button",
|
|
1410
|
+
className: pet_module_css_default.action,
|
|
1411
|
+
onClick: () => setPage("root"),
|
|
1412
|
+
children: tr("pet.gameplay.back")
|
|
1413
|
+
})
|
|
1414
|
+
})] }),
|
|
1415
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1416
|
+
type: "button",
|
|
1417
|
+
className: pet_module_css_default.gameplayClose,
|
|
1418
|
+
"aria-label": tr("pet.gameplay.back"),
|
|
1419
|
+
onClick: () => setOpen(false),
|
|
1420
|
+
children: "×"
|
|
1421
|
+
})
|
|
1422
|
+
]
|
|
1423
|
+
})
|
|
1424
|
+
]
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
//#endregion
|
|
920
1428
|
//#region src/client/PetDockEntry.tsx
|
|
921
1429
|
/**
|
|
922
1430
|
* Global floating pet entry. The pet is host-global (its state, display and
|
|
@@ -953,6 +1461,14 @@ window.__ModuleLoader__.load({
|
|
|
953
1461
|
(0, react.useEffect)(() => {
|
|
954
1462
|
ensure();
|
|
955
1463
|
}, [ensure]);
|
|
1464
|
+
const auxRef = (0, react.useRef)(null);
|
|
1465
|
+
if (definition !== null && (auxRef.current === null || auxRef.current.id !== definition.id)) auxRef.current = {
|
|
1466
|
+
id: definition.id,
|
|
1467
|
+
bus: {},
|
|
1468
|
+
drag: createDragStream()
|
|
1469
|
+
};
|
|
1470
|
+
const aux = auxRef.current;
|
|
1471
|
+
const gameplay = definition?.gameplay;
|
|
956
1472
|
if (visible) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
957
1473
|
"data-pet-dock": true,
|
|
958
1474
|
"data-testid": "pet-dock",
|
|
@@ -960,6 +1476,10 @@ window.__ModuleLoader__.load({
|
|
|
960
1476
|
definition,
|
|
961
1477
|
phase: snapshot?.phase ?? "idle",
|
|
962
1478
|
onPet: props.pet,
|
|
1479
|
+
...aux === null ? {} : {
|
|
1480
|
+
drag: aux.drag,
|
|
1481
|
+
bus: aux.bus
|
|
1482
|
+
},
|
|
963
1483
|
t: props.t,
|
|
964
1484
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PetSprite, {
|
|
965
1485
|
snapshot,
|
|
@@ -973,6 +1493,19 @@ window.__ModuleLoader__.load({
|
|
|
973
1493
|
onRename: props.rename,
|
|
974
1494
|
onOpenSession: props.openSession,
|
|
975
1495
|
onFeedbackDone: props.feedbackDone,
|
|
1496
|
+
dragDisabled: snapshot.gameplay?.mode === "work",
|
|
1497
|
+
...gameplay === void 0 || aux === null ? {} : {
|
|
1498
|
+
onGameplayTap: (fx, fy) => aux.bus.tap?.(fx, fy),
|
|
1499
|
+
onGameplayMenu: () => aux.bus.openCard?.(),
|
|
1500
|
+
hud: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GameplayHud, {
|
|
1501
|
+
definition,
|
|
1502
|
+
store,
|
|
1503
|
+
api: props.gameplay,
|
|
1504
|
+
bus: aux.bus,
|
|
1505
|
+
drag: aux.drag,
|
|
1506
|
+
t: props.t
|
|
1507
|
+
})
|
|
1508
|
+
},
|
|
976
1509
|
t: props.t
|
|
977
1510
|
})
|
|
978
1511
|
})
|
|
@@ -1268,6 +1801,161 @@ window.__ModuleLoader__.load({
|
|
|
1268
1801
|
}
|
|
1269
1802
|
};
|
|
1270
1803
|
//#endregion
|
|
1804
|
+
//#region src/client/renderers/frames2d.ts
|
|
1805
|
+
/**
|
|
1806
|
+
* frames2d renderer — plays the free-form named frame-sequence tracks of a
|
|
1807
|
+
* frames2d pet (manifest v2 'frames2d' block). The pet center picks the
|
|
1808
|
+
* track from the ActivityPhase stream (phase -> track map, idle fallback);
|
|
1809
|
+
* the gameplay driver may force a track through the handle's setState
|
|
1810
|
+
* override (drag/work/sleep/shop...), and a finished non-loop track settles
|
|
1811
|
+
* into its fallback, releasing the override when the fallback matches the
|
|
1812
|
+
* phase-mapped track. Rendering never throws: a broken track list degrades
|
|
1813
|
+
* to the first decodable frame, and the 1.2 s stall watchdog re-kicks the
|
|
1814
|
+
* playback chain after timer throttling.
|
|
1815
|
+
* @module @linxin666/dsh-pet/client/renderers/frames2d
|
|
1816
|
+
*/
|
|
1817
|
+
/** Stall watchdog period: a looping track stuck longer re-kicks its chain. */
|
|
1818
|
+
const WATCHDOG_MS = 1200;
|
|
1819
|
+
function isRecord(value) {
|
|
1820
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1821
|
+
}
|
|
1822
|
+
/** Fail-closed client-side validation of the served frames2d block. */
|
|
1823
|
+
function validateFrames2dConfig(config) {
|
|
1824
|
+
if (!isRecord(config) || !isRecord(config.tracks) || !isRecord(config.phases)) throw new Error("frames2d config requires tracks and phases objects");
|
|
1825
|
+
const tracks = {};
|
|
1826
|
+
for (const [name, raw] of Object.entries(config.tracks)) {
|
|
1827
|
+
if (!isRecord(raw) || !Array.isArray(raw.frames) || raw.frames.length === 0 || raw.frames.some((f) => typeof f !== "string" || f === "") || !Array.isArray(raw.durations) || raw.durations.length !== raw.frames.length || raw.durations.some((d) => typeof d !== "number" || !(d > 0))) throw new Error("frames2d track " + JSON.stringify(name) + " needs same-length frames/durations");
|
|
1828
|
+
tracks[name] = {
|
|
1829
|
+
frames: raw.frames,
|
|
1830
|
+
durations: raw.durations,
|
|
1831
|
+
loop: raw.loop !== false,
|
|
1832
|
+
...typeof raw.fallback === "string" ? { fallback: raw.fallback } : {}
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1835
|
+
const phases = config.phases;
|
|
1836
|
+
if (typeof phases.idle !== "string" || tracks[phases.idle] === void 0) throw new Error("frames2d phases.idle must name an existing track");
|
|
1837
|
+
return {
|
|
1838
|
+
tracks,
|
|
1839
|
+
phases
|
|
1840
|
+
};
|
|
1841
|
+
}
|
|
1842
|
+
const frames2dRenderer = {
|
|
1843
|
+
id: "frames2d",
|
|
1844
|
+
apiVersion: PET_RENDERER_API_VERSION,
|
|
1845
|
+
validateConfig: validateFrames2dConfig,
|
|
1846
|
+
mount(ctx, config) {
|
|
1847
|
+
const reducedMotion = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1848
|
+
const img = document.createElement("img");
|
|
1849
|
+
img.dataset.dshPetFrames2d = ctx.petId;
|
|
1850
|
+
img.alt = "";
|
|
1851
|
+
img.draggable = false;
|
|
1852
|
+
img.style.width = "100%";
|
|
1853
|
+
img.style.height = "100%";
|
|
1854
|
+
img.style.objectFit = "contain";
|
|
1855
|
+
img.style.pointerEvents = "none";
|
|
1856
|
+
ctx.container.appendChild(img);
|
|
1857
|
+
for (const track of Object.values(config.tracks)) for (const url of track.frames) {
|
|
1858
|
+
const pre = new Image();
|
|
1859
|
+
pre.src = url;
|
|
1860
|
+
}
|
|
1861
|
+
let disposed = false;
|
|
1862
|
+
let timer;
|
|
1863
|
+
let watchdog;
|
|
1864
|
+
let track = config.phases.idle;
|
|
1865
|
+
let frameIndex = 0;
|
|
1866
|
+
let lastAdvance = Date.now();
|
|
1867
|
+
let override;
|
|
1868
|
+
const trackForPhase = (phase) => {
|
|
1869
|
+
const mapped = config.phases[phase];
|
|
1870
|
+
return mapped !== void 0 && config.tracks[mapped] !== void 0 ? mapped : config.phases.idle;
|
|
1871
|
+
};
|
|
1872
|
+
const show = (trackId, index) => {
|
|
1873
|
+
const url = config.tracks[trackId]?.frames[index];
|
|
1874
|
+
if (url !== void 0 && img.getAttribute("src") !== url) img.src = url;
|
|
1875
|
+
};
|
|
1876
|
+
const schedule = (ms) => {
|
|
1877
|
+
if (disposed) return;
|
|
1878
|
+
timer = setTimeout(tick, ms);
|
|
1879
|
+
};
|
|
1880
|
+
function tick() {
|
|
1881
|
+
if (disposed) return;
|
|
1882
|
+
const def = config.tracks[track];
|
|
1883
|
+
if (def === void 0 || def.frames.length === 0) return;
|
|
1884
|
+
lastAdvance = Date.now();
|
|
1885
|
+
const next = frameIndex + 1;
|
|
1886
|
+
if (next < def.frames.length) {
|
|
1887
|
+
frameIndex = next;
|
|
1888
|
+
show(track, frameIndex);
|
|
1889
|
+
schedule(def.durations[frameIndex] ?? 200);
|
|
1890
|
+
return;
|
|
1891
|
+
}
|
|
1892
|
+
if (def.loop) {
|
|
1893
|
+
frameIndex = 0;
|
|
1894
|
+
show(track, frameIndex);
|
|
1895
|
+
schedule(def.durations[frameIndex] ?? 200);
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1898
|
+
const target = def.fallback !== void 0 && config.tracks[def.fallback] !== void 0 ? def.fallback : config.phases.idle;
|
|
1899
|
+
if (target === trackForPhase(ctx.phase.get())) override = void 0;
|
|
1900
|
+
play(target);
|
|
1901
|
+
}
|
|
1902
|
+
function play(trackId) {
|
|
1903
|
+
if (disposed) return;
|
|
1904
|
+
if (config.tracks[trackId] === void 0) trackId = config.phases.idle;
|
|
1905
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1906
|
+
track = trackId;
|
|
1907
|
+
frameIndex = 0;
|
|
1908
|
+
lastAdvance = Date.now();
|
|
1909
|
+
show(track, frameIndex);
|
|
1910
|
+
if (reducedMotion) return;
|
|
1911
|
+
const def = config.tracks[track];
|
|
1912
|
+
schedule(def.durations[0] ?? 200);
|
|
1913
|
+
}
|
|
1914
|
+
const unsubscribe = ctx.phase.subscribe((phase) => {
|
|
1915
|
+
if (override !== void 0) return;
|
|
1916
|
+
const target = trackForPhase(phase);
|
|
1917
|
+
if (target !== track) play(target);
|
|
1918
|
+
});
|
|
1919
|
+
if (!reducedMotion) watchdog = setInterval(() => {
|
|
1920
|
+
if (disposed) return;
|
|
1921
|
+
const def = config.tracks[track];
|
|
1922
|
+
if (def === void 0 || !def.loop) return;
|
|
1923
|
+
const expected = (def.durations[frameIndex] ?? 200) + WATCHDOG_MS;
|
|
1924
|
+
if (Date.now() - lastAdvance > expected) tick();
|
|
1925
|
+
}, WATCHDOG_MS);
|
|
1926
|
+
play(track);
|
|
1927
|
+
let disposedOnce = false;
|
|
1928
|
+
const dispose = () => {
|
|
1929
|
+
if (disposedOnce) return;
|
|
1930
|
+
disposedOnce = true;
|
|
1931
|
+
disposed = true;
|
|
1932
|
+
unsubscribe();
|
|
1933
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1934
|
+
if (watchdog !== void 0) clearInterval(watchdog);
|
|
1935
|
+
img.remove();
|
|
1936
|
+
};
|
|
1937
|
+
ctx.onCleanup(dispose);
|
|
1938
|
+
return {
|
|
1939
|
+
dispose,
|
|
1940
|
+
setState(next) {
|
|
1941
|
+
if (disposed) return;
|
|
1942
|
+
if (next === void 0) {
|
|
1943
|
+
override = void 0;
|
|
1944
|
+
const target = trackForPhase(ctx.phase.get());
|
|
1945
|
+
if (target !== track) play(target);
|
|
1946
|
+
return;
|
|
1947
|
+
}
|
|
1948
|
+
if (config.tracks[next] === void 0) return;
|
|
1949
|
+
override = next;
|
|
1950
|
+
if (next !== track) play(next);
|
|
1951
|
+
},
|
|
1952
|
+
currentTrack() {
|
|
1953
|
+
return track;
|
|
1954
|
+
}
|
|
1955
|
+
};
|
|
1956
|
+
}
|
|
1957
|
+
};
|
|
1958
|
+
//#endregion
|
|
1271
1959
|
//#region src/client/ui-teardown.ts
|
|
1272
1960
|
/**
|
|
1273
1961
|
* Cross-bundle-instance teardown slot for the page-global pet root
|
|
@@ -1311,7 +1999,7 @@ window.__ModuleLoader__.load({
|
|
|
1311
1999
|
}
|
|
1312
2000
|
//#endregion
|
|
1313
2001
|
//#region \0dsh-css:packages/dsh-pet/src/client/settings-card.module.css.mjs
|
|
1314
|
-
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}}";
|
|
2002
|
+
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;box-sizing:border-box;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{box-sizing:border-box;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}}";
|
|
1315
2003
|
const tagId$1 = "@linxin666/dsh-pet/settings-card.module.css";
|
|
1316
2004
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
1317
2005
|
const tag = document.createElement("style");
|
|
@@ -1396,7 +2084,7 @@ window.__ModuleLoader__.load({
|
|
|
1396
2084
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1397
2085
|
className: settings_card_module_css_default.description,
|
|
1398
2086
|
title: description,
|
|
1399
|
-
children: description
|
|
2087
|
+
children: props.descriptionNode ?? description
|
|
1400
2088
|
})]
|
|
1401
2089
|
}), state.dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1402
2090
|
className: settings_card_module_css_default.pending,
|
|
@@ -1421,7 +2109,7 @@ window.__ModuleLoader__.load({
|
|
|
1421
2109
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1422
2110
|
className: settings_card_module_css_default.description,
|
|
1423
2111
|
title: description,
|
|
1424
|
-
children: description
|
|
2112
|
+
children: props.descriptionNode ?? description
|
|
1425
2113
|
})]
|
|
1426
2114
|
}),
|
|
1427
2115
|
state.dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -1964,7 +2652,7 @@ window.__ModuleLoader__.load({
|
|
|
1964
2652
|
/**
|
|
1965
2653
|
* Write every staged edit, then re-seed from what the Host accepted.
|
|
1966
2654
|
*
|
|
1967
|
-
* When the scope carries the optional batch surface (the dsh-web
|
|
2655
|
+
* When the scope carries the optional batch surface (the dsh-web
|
|
1968
2656
|
* bridge scope), every planned write rides one mutation so cross-field
|
|
1969
2657
|
* validate hooks (baseURL+model) judge the batch as a unit instead of
|
|
1970
2658
|
* deadlocking on per-field writes. Otherwise the per-field loop runs.
|
|
@@ -2403,6 +3091,23 @@ window.__ModuleLoader__.load({
|
|
|
2403
3091
|
"pet.live2d.vendor-missing": "Live2D 组件缺失,请升级宠物插件。",
|
|
2404
3092
|
"pet.live2d.load-failed": "Live2D 模型加载失败,请检查该宠物目录的完整性。",
|
|
2405
3093
|
"pet.openSessionHint": "点击跳转到对应会话",
|
|
3094
|
+
"pet.gameplay.menu": "玩法",
|
|
3095
|
+
"pet.gameplay.work": "打工",
|
|
3096
|
+
"pet.gameplay.stopWork": "收工",
|
|
3097
|
+
"pet.gameplay.sleep": "睡觉",
|
|
3098
|
+
"pet.gameplay.wake": "起床",
|
|
3099
|
+
"pet.gameplay.shop": "商店",
|
|
3100
|
+
"pet.gameplay.back": "返回",
|
|
3101
|
+
"pet.gameplay.buy": "购买",
|
|
3102
|
+
"pet.gameplay.insufficient": "{currency}不足",
|
|
3103
|
+
"pet.gameplay.prize": "中奖 +{amount} {currency}",
|
|
3104
|
+
"pet.gameplay.working": "打工中",
|
|
3105
|
+
"pet.gameplay.sleeping": "睡觉中",
|
|
3106
|
+
"pet.gameplay.stat.hunger": "饱食",
|
|
3107
|
+
"pet.gameplay.stat.mood": "心情",
|
|
3108
|
+
"pet.gameplay.stat.energy": "精力",
|
|
3109
|
+
"pet.gameplay.stat.affection": "好感",
|
|
3110
|
+
"pet.gameplay.currency.treats": "小鱼干",
|
|
2406
3111
|
"pet.moreSessions": "展开其余 {n} 个会话的气泡",
|
|
2407
3112
|
"pet.collapseSessions": "收起会话气泡",
|
|
2408
3113
|
"settings.title": "宠物",
|
|
@@ -2456,6 +3161,23 @@ window.__ModuleLoader__.load({
|
|
|
2456
3161
|
"pet.live2d.vendor-missing": "The Live2D component is missing; please update the pet plugin.",
|
|
2457
3162
|
"pet.live2d.load-failed": "The Live2D model failed to load; check the pet directory is complete.",
|
|
2458
3163
|
"pet.openSessionHint": "Click to jump to this session",
|
|
3164
|
+
"pet.gameplay.menu": "Play",
|
|
3165
|
+
"pet.gameplay.work": "Work",
|
|
3166
|
+
"pet.gameplay.stopWork": "Stop work",
|
|
3167
|
+
"pet.gameplay.sleep": "Sleep",
|
|
3168
|
+
"pet.gameplay.wake": "Wake up",
|
|
3169
|
+
"pet.gameplay.shop": "Shop",
|
|
3170
|
+
"pet.gameplay.back": "Back",
|
|
3171
|
+
"pet.gameplay.buy": "Buy",
|
|
3172
|
+
"pet.gameplay.insufficient": "Not enough {currency}",
|
|
3173
|
+
"pet.gameplay.prize": "Prize +{amount} {currency}",
|
|
3174
|
+
"pet.gameplay.working": "Working",
|
|
3175
|
+
"pet.gameplay.sleeping": "Sleeping",
|
|
3176
|
+
"pet.gameplay.stat.hunger": "Hunger",
|
|
3177
|
+
"pet.gameplay.stat.mood": "Mood",
|
|
3178
|
+
"pet.gameplay.stat.energy": "Energy",
|
|
3179
|
+
"pet.gameplay.stat.affection": "Affection",
|
|
3180
|
+
"pet.gameplay.currency.treats": "Treats",
|
|
2459
3181
|
"pet.moreSessions": "Expand {n} more session bubbles",
|
|
2460
3182
|
"pet.collapseSessions": "Collapse session bubbles",
|
|
2461
3183
|
"settings.title": "Pet",
|
|
@@ -2514,6 +3236,76 @@ window.__ModuleLoader__.load({
|
|
|
2514
3236
|
return text;
|
|
2515
3237
|
}
|
|
2516
3238
|
//#endregion
|
|
3239
|
+
//#region src/client/telemetry.ts
|
|
3240
|
+
const VISITOR_KEY = "dsh-web-ui-telemetry-visitor";
|
|
3241
|
+
const DAY_KEY_PREFIX = "dsh-web-ui-telemetry-day:";
|
|
3242
|
+
const ENDPOINT = "https://dsh-market.com/api/telemetry/event";
|
|
3243
|
+
/** The building package's version, when the bundle carries it. */
|
|
3244
|
+
function bakedVersion() {
|
|
3245
|
+
try {
|
|
3246
|
+
return "0.3.4";
|
|
3247
|
+
} catch {
|
|
3248
|
+
return;
|
|
3249
|
+
}
|
|
3250
|
+
}
|
|
3251
|
+
/** Read or lazily create the anonymous visitor id; null when storage is unavailable. */
|
|
3252
|
+
function visitorId() {
|
|
3253
|
+
try {
|
|
3254
|
+
const existing = localStorage.getItem(VISITOR_KEY);
|
|
3255
|
+
if (existing && /^[A-Za-z0-9_-]{16,64}$/.test(existing)) return existing;
|
|
3256
|
+
const fresh = crypto.randomUUID().replaceAll("-", "");
|
|
3257
|
+
localStorage.setItem(VISITOR_KEY, fresh);
|
|
3258
|
+
return fresh;
|
|
3259
|
+
} catch {
|
|
3260
|
+
return null;
|
|
3261
|
+
}
|
|
3262
|
+
}
|
|
3263
|
+
/** Drop stale per-day dedup keys so localStorage does not grow forever. */
|
|
3264
|
+
function pruneDayKeys(today) {
|
|
3265
|
+
try {
|
|
3266
|
+
for (let index = localStorage.length - 1; index >= 0; index -= 1) {
|
|
3267
|
+
const key = localStorage.key(index);
|
|
3268
|
+
if (key !== null && key.startsWith(DAY_KEY_PREFIX) && key !== DAY_KEY_PREFIX + today) localStorage.removeItem(key);
|
|
3269
|
+
}
|
|
3270
|
+
} catch {}
|
|
3271
|
+
}
|
|
3272
|
+
/**
|
|
3273
|
+
* Fire the daily heartbeat for the given items at most once per UTC day per
|
|
3274
|
+
* browser. Never throws and never blocks the caller. Items without an explicit
|
|
3275
|
+
* version inherit the bundle's baked build version.
|
|
3276
|
+
*/
|
|
3277
|
+
function reportDailyHeartbeat(items) {
|
|
3278
|
+
try {
|
|
3279
|
+
if (items.length === 0) return;
|
|
3280
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
3281
|
+
if (navigator.webdriver) return;
|
|
3282
|
+
if (localStorage.getItem(DAY_KEY_PREFIX + today) !== null) return;
|
|
3283
|
+
const visitor = visitorId();
|
|
3284
|
+
if (visitor === null) return;
|
|
3285
|
+
pruneDayKeys(today);
|
|
3286
|
+
const payloadItems = items.map((item) => {
|
|
3287
|
+
const out = { name: item.name };
|
|
3288
|
+
const version = item.version ?? bakedVersion();
|
|
3289
|
+
if (version !== void 0) out.version = version;
|
|
3290
|
+
if (item.channel !== void 0) out.channel = item.channel;
|
|
3291
|
+
return out;
|
|
3292
|
+
});
|
|
3293
|
+
const body = JSON.stringify({
|
|
3294
|
+
kind: "heartbeat",
|
|
3295
|
+
visitor,
|
|
3296
|
+
items: payloadItems
|
|
3297
|
+
});
|
|
3298
|
+
fetch(ENDPOINT, {
|
|
3299
|
+
method: "POST",
|
|
3300
|
+
headers: { "content-type": "application/json" },
|
|
3301
|
+
body,
|
|
3302
|
+
keepalive: true
|
|
3303
|
+
}).then((response) => {
|
|
3304
|
+
if (response.ok) localStorage.setItem(DAY_KEY_PREFIX + today, "1");
|
|
3305
|
+
}).catch(() => {});
|
|
3306
|
+
} catch {}
|
|
3307
|
+
}
|
|
3308
|
+
//#endregion
|
|
2517
3309
|
//#region src/client/index.ts
|
|
2518
3310
|
/** Same-origin JSON fetch helper (GET without body, POST with JSON body). */
|
|
2519
3311
|
async function petFetch(path, body) {
|
|
@@ -2533,7 +3325,11 @@ window.__ModuleLoader__.load({
|
|
|
2533
3325
|
setVisible: (visible) => petFetch("/api/pet/set-visible", { visible }),
|
|
2534
3326
|
setConfig: (patch) => petFetch("/api/pet/set-config", patch),
|
|
2535
3327
|
setName: (name) => petFetch("/api/pet/set-name", { name }),
|
|
2536
|
-
setPet: (petId) => petFetch("/api/pet/set-pet", { petId })
|
|
3328
|
+
setPet: (petId) => petFetch("/api/pet/set-pet", { petId }),
|
|
3329
|
+
gameplayTouch: (zone) => petFetch("/api/pet/gameplay/touch", zone === void 0 ? {} : { zone }),
|
|
3330
|
+
gameplaySetMode: (mode) => petFetch("/api/pet/gameplay/mode", { mode }),
|
|
3331
|
+
gameplayWorkTick: () => petFetch("/api/pet/gameplay/work-tick", {}),
|
|
3332
|
+
gameplayBuy: (item) => petFetch("/api/pet/gameplay/buy", { item })
|
|
2537
3333
|
};
|
|
2538
3334
|
/** Poll interval for the host snapshot. */
|
|
2539
3335
|
const POLL_MS = 2e3;
|
|
@@ -2555,6 +3351,7 @@ window.__ModuleLoader__.load({
|
|
|
2555
3351
|
* @param ctx - client root context.
|
|
2556
3352
|
*/
|
|
2557
3353
|
function apply(ctx) {
|
|
3354
|
+
reportDailyHeartbeat([{ name: "@linxin666/dsh-pet" }]);
|
|
2558
3355
|
ctx.effect(() => {
|
|
2559
3356
|
try {
|
|
2560
3357
|
return ctx.locale.register("pet", {
|
|
@@ -2566,6 +3363,7 @@ window.__ModuleLoader__.load({
|
|
|
2566
3363
|
}
|
|
2567
3364
|
}, "pet: dictionaries");
|
|
2568
3365
|
defaultPetRendererRegistry.register(live2dRenderer);
|
|
3366
|
+
defaultPetRendererRegistry.register(frames2dRenderer);
|
|
2569
3367
|
const settingsScope = (ctx.get("webUiSettings") ?? ctx.settingsScope).bind({ namespace: PET_SETTINGS_NS });
|
|
2570
3368
|
const enabled = () => {
|
|
2571
3369
|
const snapshot = settingsScope.getSnapshot();
|
|
@@ -2701,6 +3499,12 @@ window.__ModuleLoader__.load({
|
|
|
2701
3499
|
},
|
|
2702
3500
|
feedbackDone: () => {
|
|
2703
3501
|
setFeedback(null);
|
|
3502
|
+
},
|
|
3503
|
+
gameplay: {
|
|
3504
|
+
touch: (zone) => petApi.gameplayTouch(zone),
|
|
3505
|
+
setMode: (mode) => petApi.gameplaySetMode(mode),
|
|
3506
|
+
workTick: () => petApi.gameplayWorkTick(),
|
|
3507
|
+
buy: (item) => petApi.gameplayBuy(item)
|
|
2704
3508
|
}
|
|
2705
3509
|
});
|
|
2706
3510
|
takeoverPetUiTeardown();
|