@linxin666/dsh-pet 0.3.5 → 0.3.6
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/contracts/voice-pack-v1.schema.json +30 -0
- package/cordis.patch.yml +1 -1
- package/lib/client.js +144 -16
- package/lib/client.js.map +1 -1
- package/lib/index.js +56 -17
- package/lib/invariant.js +1 -1
- package/lib/{state-DrMX22GL.js → state-NvDEoCln.js} +5 -2
- package/lib/types/access.d.ts +5 -4
- package/lib/types/access.d.ts.map +1 -1
- package/lib/types/access.js +2 -12
- package/lib/types/affinity.d.ts +2 -0
- package/lib/types/affinity.d.ts.map +1 -1
- package/lib/types/affinity.js +12 -0
- package/lib/types/client/PetSprite.d.ts.map +1 -1
- package/lib/types/client/PetSprite.js +7 -1
- package/lib/types/client/locales.d.ts +18 -0
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/locales.js +18 -0
- package/lib/types/client/renderers/frames2d.d.ts +12 -0
- package/lib/types/client/renderers/frames2d.d.ts.map +1 -1
- package/lib/types/client/renderers/frames2d.js +143 -18
- package/lib/types/ledger.d.ts +4 -2
- package/lib/types/ledger.d.ts.map +1 -1
- package/lib/types/ledger.js +4 -4
- package/lib/types/pair-access.d.ts +35 -0
- package/lib/types/pair-access.d.ts.map +1 -0
- package/lib/types/pair-access.js +19 -0
- package/lib/types/remarks.d.ts +1 -1
- package/lib/types/remarks.d.ts.map +1 -1
- package/lib/types/remarks.js +11 -2
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +4 -1
- package/lib/types/voice-pack.d.ts +5 -0
- package/lib/types/voice-pack.d.ts.map +1 -1
- package/lib/types/voice-pack.js +36 -2
- package/package.json +1 -1
- package/src/access.ts +7 -27
- package/src/affinity.ts +13 -0
- package/src/client/PetSprite.test.tsx +18 -0
- package/src/client/PetSprite.tsx +9 -1
- package/src/client/locales.ts +18 -0
- package/src/client/renderers/frames2d.test.ts +101 -0
- package/src/client/renderers/frames2d.ts +138 -18
- package/src/ledger.ts +6 -4
- package/src/pair-access.ts +49 -0
- package/src/remarks.test.ts +10 -0
- package/src/remarks.ts +9 -2
- package/src/service.ts +4 -1
- package/src/voice-pack.test.ts +39 -0
- package/src/voice-pack.ts +37 -2
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as RemarkPicker, a as AFFINITY_MAX, c as applyInteraction, d as emptyAffinity, f as rankOf, g as REMARK_LINE_MAX, h as REMARK_LINES_MAX, i as rowOf, l as applyTurnReward, m as REMARK_KINDS, n as animationForPhase, o as AFFINITY_RANKS, p as BUILTIN_REMARKS, r as defaultPetStateConfig, s as affinityViewOf, t as PetStateMachine, u as defaultAffinityConfig, v as builtinRemark, y as normalizePetRemarks } from "./state-
|
|
1
|
+
import { _ as RemarkPicker, a as AFFINITY_MAX, c as applyInteraction, d as emptyAffinity, f as rankOf, g as REMARK_LINE_MAX, h as REMARK_LINES_MAX, i as rowOf, l as applyTurnReward, m as REMARK_KINDS, n as animationForPhase, o as AFFINITY_RANKS, p as BUILTIN_REMARKS, r as defaultPetStateConfig, s as affinityViewOf, t as PetStateMachine, u as defaultAffinityConfig, v as builtinRemark, y as normalizePetRemarks } from "./state-NvDEoCln.js";
|
|
2
2
|
import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
3
3
|
import z from "schemastery";
|
|
4
4
|
import { Service } from "@deepseek-ai/cordis";
|
|
@@ -1037,7 +1037,7 @@ var PetLedger = class {
|
|
|
1037
1037
|
...defaultTreatConfig,
|
|
1038
1038
|
...config.treats ?? {}
|
|
1039
1039
|
};
|
|
1040
|
-
this.picker = new RemarkPicker(config.remarks);
|
|
1040
|
+
this.picker = new RemarkPicker(config.remarks, config.voiceRemarks);
|
|
1041
1041
|
this.current = persist;
|
|
1042
1042
|
}
|
|
1043
1043
|
/** Affinity cooldown/rank tuning (read-only). */
|
|
@@ -1106,10 +1106,10 @@ var PetLedger = class {
|
|
|
1106
1106
|
}
|
|
1107
1107
|
/**
|
|
1108
1108
|
* Swap the reaction pools to another pet's custom remarks (called on pet
|
|
1109
|
-
* selection). Slots the pet does not declare fall back to built-ins.
|
|
1109
|
+
* selection). Slots the pet does not declare fall back to voice packs or built-ins.
|
|
1110
1110
|
*/
|
|
1111
|
-
setRemarks(remarks) {
|
|
1112
|
-
this.picker = new RemarkPicker(remarks);
|
|
1111
|
+
setRemarks(remarks, voiceRemarks) {
|
|
1112
|
+
this.picker = new RemarkPicker(remarks, voiceRemarks);
|
|
1113
1113
|
}
|
|
1114
1114
|
/**
|
|
1115
1115
|
* Settle the treat economy (work + time output since the last settlement).
|
|
@@ -1600,7 +1600,9 @@ const VOICE_PACK_KEYS = /* @__PURE__ */ new Set([
|
|
|
1600
1600
|
"tools",
|
|
1601
1601
|
"toolRemaining",
|
|
1602
1602
|
"whispers",
|
|
1603
|
-
"panel"
|
|
1603
|
+
"panel",
|
|
1604
|
+
"remarks",
|
|
1605
|
+
"ranks"
|
|
1604
1606
|
]);
|
|
1605
1607
|
/** Allowed whisper-section fields (drift-locked in tests). */
|
|
1606
1608
|
const WHISPER_KEYS = /* @__PURE__ */ new Set(["categories", "results"]);
|
|
@@ -1663,10 +1665,22 @@ function normalizeVoicePack(raw, onWarning = () => {}) {
|
|
|
1663
1665
|
};
|
|
1664
1666
|
}
|
|
1665
1667
|
const panel = raw.panel === void 0 ? void 0 : normalizePanel(raw.panel, onWarning);
|
|
1666
|
-
|
|
1668
|
+
const remarks = raw.remarks === void 0 ? void 0 : normalizePetRemarks(raw.remarks, onWarning);
|
|
1669
|
+
const ranksRaw = raw.ranks;
|
|
1670
|
+
let ranks;
|
|
1671
|
+
if (ranksRaw !== void 0) if (!isRecord$3(ranksRaw)) onWarning("ranks must be an object");
|
|
1672
|
+
else {
|
|
1673
|
+
const out = {};
|
|
1674
|
+
for (const [key, value] of Object.entries(ranksRaw)) if (typeof value === "string" && value.trim() !== "") out[key] = value.trim().slice(0, 80);
|
|
1675
|
+
else onWarning("invalid rank name for " + key);
|
|
1676
|
+
if (Object.keys(out).length > 0) ranks = out;
|
|
1677
|
+
}
|
|
1678
|
+
if (overrides.status === void 0 && overrides.tools === void 0 && overrides.toolRemaining === void 0 && overrides.whispers === void 0 && panel === void 0 && remarks === void 0 && ranks === void 0) return;
|
|
1667
1679
|
return {
|
|
1668
1680
|
overrides,
|
|
1669
|
-
...panel === void 0 ? {} : { panel }
|
|
1681
|
+
...panel === void 0 ? {} : { panel },
|
|
1682
|
+
...remarks === void 0 ? {} : { remarks },
|
|
1683
|
+
...ranks === void 0 ? {} : { ranks }
|
|
1670
1684
|
};
|
|
1671
1685
|
}
|
|
1672
1686
|
/**
|
|
@@ -1679,6 +1693,8 @@ function mergeVoicePacks(...layers) {
|
|
|
1679
1693
|
const overrides = {};
|
|
1680
1694
|
const labels = {};
|
|
1681
1695
|
const stats = {};
|
|
1696
|
+
let remarks;
|
|
1697
|
+
let ranks;
|
|
1682
1698
|
let actions;
|
|
1683
1699
|
let panelSeen = false;
|
|
1684
1700
|
let any = false;
|
|
@@ -1704,6 +1720,14 @@ function mergeVoicePacks(...layers) {
|
|
|
1704
1720
|
if (layer.panel.stats !== void 0) Object.assign(stats, layer.panel.stats);
|
|
1705
1721
|
if (layer.panel.actions !== void 0) actions = layer.panel.actions;
|
|
1706
1722
|
}
|
|
1723
|
+
if (layer.remarks !== void 0) remarks = {
|
|
1724
|
+
...remarks ?? {},
|
|
1725
|
+
...layer.remarks
|
|
1726
|
+
};
|
|
1727
|
+
if (layer.ranks !== void 0) ranks = {
|
|
1728
|
+
...ranks ?? {},
|
|
1729
|
+
...layer.ranks
|
|
1730
|
+
};
|
|
1707
1731
|
}
|
|
1708
1732
|
if (!any) return void 0;
|
|
1709
1733
|
const panel = {
|
|
@@ -1714,7 +1738,9 @@ function mergeVoicePacks(...layers) {
|
|
|
1714
1738
|
const panelEmpty = panel.labels === void 0 && panel.stats === void 0 && panel.actions === void 0;
|
|
1715
1739
|
return {
|
|
1716
1740
|
overrides,
|
|
1717
|
-
...panelSeen && !panelEmpty ? { panel } : {}
|
|
1741
|
+
...panelSeen && !panelEmpty ? { panel } : {},
|
|
1742
|
+
...remarks !== void 0 ? { remarks } : {},
|
|
1743
|
+
...ranks !== void 0 ? { ranks } : {}
|
|
1718
1744
|
};
|
|
1719
1745
|
}
|
|
1720
1746
|
//#endregion
|
|
@@ -4099,10 +4125,12 @@ var PetService = class extends Service {
|
|
|
4099
4125
|
petId: this.registry.defaultEntry().id
|
|
4100
4126
|
};
|
|
4101
4127
|
const selected = this.registry.byId(persist.petId) ?? this.registry.defaultEntry();
|
|
4128
|
+
const voiceRemarks = mergeVoicePacks(this.registry.globalVoice, selected.voice)?.remarks;
|
|
4102
4129
|
const ledgerConfig = {
|
|
4103
4130
|
affinity: config.affinity,
|
|
4104
4131
|
treats: config.treats,
|
|
4105
|
-
remarks: selected.remarks
|
|
4132
|
+
remarks: selected.remarks,
|
|
4133
|
+
voiceRemarks
|
|
4106
4134
|
};
|
|
4107
4135
|
this.ledger = new PetLedger(persist, ledgerConfig);
|
|
4108
4136
|
this.stateConfig = {
|
|
@@ -4186,7 +4214,8 @@ var PetService = class extends Service {
|
|
|
4186
4214
|
error: "unknown-pet"
|
|
4187
4215
|
};
|
|
4188
4216
|
this.ledger.setPetId(entry.id);
|
|
4189
|
-
this.
|
|
4217
|
+
const voiceRemarks = mergeVoicePacks(this.registry.globalVoice, entry.voice)?.remarks;
|
|
4218
|
+
this.ledger.setRemarks(entry.remarks, voiceRemarks);
|
|
4190
4219
|
this.flush();
|
|
4191
4220
|
this.syncSettingsFromPet();
|
|
4192
4221
|
return {
|
|
@@ -4691,23 +4720,33 @@ function isLoopbackRequest(request) {
|
|
|
4691
4720
|
}
|
|
4692
4721
|
}
|
|
4693
4722
|
//#endregion
|
|
4694
|
-
//#region src/access.ts
|
|
4723
|
+
//#region src/pair-access.ts
|
|
4695
4724
|
/**
|
|
4696
|
-
* Whether this request may enter
|
|
4725
|
+
* Whether this request may enter the plugin's host routes.
|
|
4697
4726
|
* @param ctx - host context; may expose remoteWebUiPairing.
|
|
4698
4727
|
* @param request - the incoming HTTP request.
|
|
4699
4728
|
* @returns true for loopback, or a live paired-device cookie.
|
|
4700
4729
|
*/
|
|
4701
|
-
function
|
|
4730
|
+
function isPairedOrLoopbackAllowed(ctx, request) {
|
|
4702
4731
|
if (isLoopbackRequest(request)) return true;
|
|
4703
|
-
const
|
|
4704
|
-
|
|
4705
|
-
return (isPairingAccess(fromGet) ? fromGet : bag.remoteWebUiPairing)?.isPairedDevice(request) === true;
|
|
4732
|
+
const fromGet = typeof ctx.get === "function" ? ctx.get("remoteWebUiPairing", false) : void 0;
|
|
4733
|
+
return (isPairingAccess(fromGet) ? fromGet : ctx.remoteWebUiPairing)?.isPairedDevice(request) === true;
|
|
4706
4734
|
}
|
|
4707
4735
|
function isPairingAccess(value) {
|
|
4708
4736
|
return value !== void 0 && value !== null && typeof value.isPairedDevice === "function";
|
|
4709
4737
|
}
|
|
4710
4738
|
//#endregion
|
|
4739
|
+
//#region src/access.ts
|
|
4740
|
+
/**
|
|
4741
|
+
* Whether this request may enter any /api/pet or /pet asset route.
|
|
4742
|
+
* @param ctx - host context; may expose remoteWebUiPairing.
|
|
4743
|
+
* @param request - the incoming HTTP request.
|
|
4744
|
+
* @returns true for loopback, or a live paired-device cookie.
|
|
4745
|
+
*/
|
|
4746
|
+
function isPetAllowed(ctx, request) {
|
|
4747
|
+
return isPairedOrLoopbackAllowed(ctx, request);
|
|
4748
|
+
}
|
|
4749
|
+
//#endregion
|
|
4711
4750
|
//#region src/http.ts
|
|
4712
4751
|
/** Default body cap for readJsonBody: 64 KiB. */
|
|
4713
4752
|
const DEFAULT_JSON_BODY_MAX_BYTES = 64 * 1024;
|
package/lib/invariant.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as AFFINITY_MAX, n as animationForPhase, o as AFFINITY_RANKS, u as defaultAffinityConfig } from "./state-
|
|
1
|
+
import { a as AFFINITY_MAX, n as animationForPhase, o as AFFINITY_RANKS, u as defaultAffinityConfig } from "./state-NvDEoCln.js";
|
|
2
2
|
//#region src/invariant.ts
|
|
3
3
|
/**
|
|
4
4
|
* Package invariants — cheap structural checks run at import time on the
|
|
@@ -149,11 +149,14 @@ function normalizePetRemarks(raw, onWarning = () => {}) {
|
|
|
149
149
|
var RemarkPicker = class {
|
|
150
150
|
counters = /* @__PURE__ */ new Map();
|
|
151
151
|
pools;
|
|
152
|
-
constructor(overrides) {
|
|
152
|
+
constructor(overrides, voiceOverrides) {
|
|
153
153
|
this.pools = {};
|
|
154
154
|
for (const kind of REMARK_KINDS) {
|
|
155
155
|
const custom = overrides?.[kind];
|
|
156
|
-
|
|
156
|
+
const voice = voiceOverrides?.[kind];
|
|
157
|
+
if (custom !== void 0 && custom.length > 0) this.pools[kind] = custom;
|
|
158
|
+
else if (voice !== void 0 && voice.length > 0) this.pools[kind] = voice;
|
|
159
|
+
else this.pools[kind] = BUILTIN_REMARKS[kind];
|
|
157
160
|
}
|
|
158
161
|
}
|
|
159
162
|
/** The effective pool for one slot (custom override or built-in). */
|
package/lib/types/access.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pet trust fence: loopback (the desktop) always passes; a live
|
|
3
|
-
* cookie is an additional allow path when remote-web-ui is
|
|
4
|
-
* never depends on that plugin — without the service the
|
|
5
|
-
* loopback-only
|
|
2
|
+
* Pet trust fence: loopback (the desktop) always passes; a live
|
|
3
|
+
* paired-device cookie is an additional allow path when remote-web-ui is
|
|
4
|
+
* loaded. The pet never depends on that plugin — without the service the
|
|
5
|
+
* fence stays loopback-only. The decision logic lives in the generated
|
|
6
|
+
* pair-access.ts copy (shared by git-graph / pet / skill-explorer).
|
|
6
7
|
*/
|
|
7
8
|
import type { IncomingMessage } from 'node:http';
|
|
8
9
|
import type { Context } from '@deepseek-ai/cordis';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../../src/access.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../../src/access.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAE5E"}
|
package/lib/types/access.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isPairedOrLoopbackAllowed } from "./pair-access.js";
|
|
2
2
|
/**
|
|
3
3
|
* Whether this request may enter any /api/pet or /pet asset route.
|
|
4
4
|
* @param ctx - host context; may expose remoteWebUiPairing.
|
|
@@ -6,15 +6,5 @@ import { isLoopbackRequest } from "./loopback.js";
|
|
|
6
6
|
* @returns true for loopback, or a live paired-device cookie.
|
|
7
7
|
*/
|
|
8
8
|
export function isPetAllowed(ctx, request) {
|
|
9
|
-
|
|
10
|
-
return true;
|
|
11
|
-
const bag = ctx;
|
|
12
|
-
const fromGet = typeof bag.get === 'function' ? bag.get('remoteWebUiPairing', false) : undefined;
|
|
13
|
-
const pairing = (isPairingAccess(fromGet) ? fromGet : bag.remoteWebUiPairing);
|
|
14
|
-
return pairing?.isPairedDevice(request) === true;
|
|
15
|
-
}
|
|
16
|
-
function isPairingAccess(value) {
|
|
17
|
-
return value !== undefined
|
|
18
|
-
&& value !== null
|
|
19
|
-
&& typeof value.isPairedDevice === 'function';
|
|
9
|
+
return isPairedOrLoopbackAllowed(ctx, request);
|
|
20
10
|
}
|
package/lib/types/affinity.d.ts
CHANGED
|
@@ -75,6 +75,8 @@ export declare const AFFINITY_RANKS: readonly [{
|
|
|
75
75
|
readonly name: "鲸生共渡";
|
|
76
76
|
readonly emoji: "*********";
|
|
77
77
|
}];
|
|
78
|
+
/** English translations for the 9 built-in affinity rank names (#1226). */
|
|
79
|
+
export declare const AFFINITY_RANKS_EN: Readonly<Record<string, string>>;
|
|
78
80
|
/** Interaction tuning (all in points / ms). */
|
|
79
81
|
export interface AffinityConfig {
|
|
80
82
|
/** Points per completed turn. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"affinity.d.ts","sourceRoot":"","sources":["../../src/affinity.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,uDAAuD;AACvD,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,CAAA;AAE3C,mCAAmC;AACnC,MAAM,WAAW,aAAa;IAC5B,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAA;IACd,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAA;IACjB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,0DAA0D;IAC1D,UAAU,EAAE,MAAM,CAAA;IAClB,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAA;IACnB,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,YAAc,CAAA;AAEvC;;;;8EAI8E;AAC9E,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUjB,CAAA;AAEV,+CAA+C;AAC/C,MAAM,WAAW,cAAc;IAC7B,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAA;IACjB,iCAAiC;IACjC,aAAa,EAAE,MAAM,CAAA;IACrB,uBAAuB;IACvB,UAAU,EAAE,MAAM,CAAA;IAClB,kCAAkC;IAClC,cAAc,EAAE,MAAM,CAAA;CACvB;AAED,eAAO,MAAM,qBAAqB,EAAE,cAMnC,CAAA;AAED,wBAAgB,aAAa,IAAI,aAAa,CAW7C;AAED,kCAAkC;AAClC,MAAM,WAAW,kBAAkB;IACjC,mDAAmD;IACnD,QAAQ,EAAE,aAAa,CAAA;IACvB,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAA;IACb,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAA;IAChB,iEAAiE;IACjE,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,8BAA8B;AAC9B,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAMtE;AAED,iEAAiE;AACjE,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,6DAA6D;IAC7D,WAAW,EAAE,OAAO,CAAA;IACpB,kDAAkD;IAClD,YAAY,EAAE,OAAO,CAAA;CACtB;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAC5B,KAAK,EAAE,aAAa,EACpB,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,cAAsC,GAC7C,eAAe,CAYjB;AAYD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,cAAsC,GAC7C,kBAAkB,CA2CpB;AAED,gEAAgE;AAChE,wBAAgB,eAAe,CAC7B,KAAK,EAAE,aAAa,EACpB,MAAM,GAAE,cAAsC,GAC7C,aAAa,CAKf"}
|
|
1
|
+
{"version":3,"file":"affinity.d.ts","sourceRoot":"","sources":["../../src/affinity.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,uDAAuD;AACvD,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,CAAA;AAE3C,mCAAmC;AACnC,MAAM,WAAW,aAAa;IAC5B,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAA;IACd,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAA;IACjB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,0DAA0D;IAC1D,UAAU,EAAE,MAAM,CAAA;IAClB,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAA;IACnB,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,YAAc,CAAA;AAEvC;;;;8EAI8E;AAC9E,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUjB,CAAA;AAEV,2EAA2E;AAC3E,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAU9D,CAAA;AAED,+CAA+C;AAC/C,MAAM,WAAW,cAAc;IAC7B,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAA;IACjB,iCAAiC;IACjC,aAAa,EAAE,MAAM,CAAA;IACrB,uBAAuB;IACvB,UAAU,EAAE,MAAM,CAAA;IAClB,kCAAkC;IAClC,cAAc,EAAE,MAAM,CAAA;CACvB;AAED,eAAO,MAAM,qBAAqB,EAAE,cAMnC,CAAA;AAED,wBAAgB,aAAa,IAAI,aAAa,CAW7C;AAED,kCAAkC;AAClC,MAAM,WAAW,kBAAkB;IACjC,mDAAmD;IACnD,QAAQ,EAAE,aAAa,CAAA;IACvB,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAA;IACb,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAA;IAChB,iEAAiE;IACjE,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,8BAA8B;AAC9B,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAMtE;AAED,iEAAiE;AACjE,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,6DAA6D;IAC7D,WAAW,EAAE,OAAO,CAAA;IACpB,kDAAkD;IAClD,YAAY,EAAE,OAAO,CAAA;CACtB;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAC5B,KAAK,EAAE,aAAa,EACpB,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,cAAsC,GAC7C,eAAe,CAYjB;AAYD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,cAAsC,GAC7C,kBAAkB,CA2CpB;AAED,gEAAgE;AAChE,wBAAgB,eAAe,CAC7B,KAAK,EAAE,aAAa,EACpB,MAAM,GAAE,cAAsC,GAC7C,aAAa,CAKf"}
|
package/lib/types/affinity.js
CHANGED
|
@@ -29,6 +29,18 @@ export const AFFINITY_RANKS = [
|
|
|
29
29
|
{ min: 10_000, name: '永恒之契', emoji: '********' },
|
|
30
30
|
{ min: 100_000, name: '鲸生共渡', emoji: '*********' },
|
|
31
31
|
];
|
|
32
|
+
/** English translations for the 9 built-in affinity rank names (#1226). */
|
|
33
|
+
export const AFFINITY_RANKS_EN = {
|
|
34
|
+
'幼鲸': 'Baby Whale',
|
|
35
|
+
'伙伴': 'Companion',
|
|
36
|
+
'挚友': 'Close Friend',
|
|
37
|
+
'深海羁绊': 'Deep Sea Bond',
|
|
38
|
+
'心有灵犀': 'Kindred Spirit',
|
|
39
|
+
'传说羁绊': 'Legendary Bond',
|
|
40
|
+
'神话羁绊': 'Mythic Bond',
|
|
41
|
+
'永恒之契': 'Eternal Covenant',
|
|
42
|
+
'鲸生共渡': 'Lifelong Companion',
|
|
43
|
+
};
|
|
32
44
|
export const defaultAffinityConfig = {
|
|
33
45
|
turnReward: 1,
|
|
34
46
|
petReward: 1,
|
|
@@ -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,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;IAC9C,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;;;;OAIG;IACH,GAAG,CAAC,EAAE,SAAS,CAAA;IACf;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9D;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,IAAI,CAAA;IAC3B,qEAAqE;IACrE,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,+CAA+C;IAC/C,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;CAC1B;AAgGD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,WAAW,
|
|
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,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;IAC9C,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;;;;OAIG;IACH,GAAG,CAAC,EAAE,SAAS,CAAA;IACf;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9D;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,IAAI,CAAA;IAC3B,qEAAqE;IACrE,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,+CAA+C;IAC/C,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;CAC1B;AAgGD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,WAAW,CA6iB5D"}
|
|
@@ -498,7 +498,13 @@ export function PetSprite(props) {
|
|
|
498
498
|
props.onRename(trimmed);
|
|
499
499
|
setRenaming(false);
|
|
500
500
|
}
|
|
501
|
-
}, children: panelLabel('confirm', props.t('pet.confirm')) })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: styles.rankRow, children: [_jsx("span", { className: styles.nameCell, children: displayName }), _jsx("span", { className: styles.statRank, children:
|
|
501
|
+
}, children: panelLabel('confirm', props.t('pet.confirm')) })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: styles.rankRow, children: [_jsx("span", { className: styles.nameCell, children: displayName }), _jsx("span", { className: styles.statRank, children: (() => {
|
|
502
|
+
const rawRank = snapshot?.affinity.rank ?? '?';
|
|
503
|
+
const rankKey = `pet.rank.name.${rawRank}`;
|
|
504
|
+
const localized = props.t(rankKey);
|
|
505
|
+
const rankName = localized !== rankKey ? localized : rawRank;
|
|
506
|
+
return panelStat('rank', 'pet.rank', { rank: rankName });
|
|
507
|
+
})() })] }), _jsxs("div", { className: styles.rankRow, children: [_jsx("span", { className: styles.statTreats, children: panelStat('treats', 'pet.treats', { n: snapshot?.treats.stocked ?? 0 }) }), _jsx("span", { className: styles.statPoints, children: panelStat('points', 'pet.points', { points: snapshot?.affinity.points ?? 0 }) })] }), _jsxs("div", { className: styles.actions, children: [panelShows('feed') && (_jsx("button", { type: "button", className: styles.action, onClick: props.onFeed, children: panelLabel('feed', props.t('pet.feed')) })), panelShows('rename') && (_jsx("button", { type: "button", className: styles.action, onClick: () => {
|
|
502
508
|
// Cancel any pending hide so the rename box cannot
|
|
503
509
|
// unmount right as the user starts typing (#303).
|
|
504
510
|
clearHideTimer();
|
|
@@ -13,6 +13,15 @@ export declare const zh: {
|
|
|
13
13
|
readonly 'pet.namePlaceholder': "输入新名字";
|
|
14
14
|
readonly 'pet.summon': "召唤{name}";
|
|
15
15
|
readonly 'pet.rank': "亲密度 {rank}";
|
|
16
|
+
readonly 'pet.rank.name.\u5E7C\u9CB8': "幼鲸";
|
|
17
|
+
readonly 'pet.rank.name.\u4F19\u4F34': "伙伴";
|
|
18
|
+
readonly 'pet.rank.name.\u631A\u53CB': "挚友";
|
|
19
|
+
readonly 'pet.rank.name.\u6DF1\u6D77\u7F81\u7ECA': "深海羁绊";
|
|
20
|
+
readonly 'pet.rank.name.\u5FC3\u6709\u7075\u7280': "心有灵犀";
|
|
21
|
+
readonly 'pet.rank.name.\u4F20\u8BF4\u7F81\u7ECA': "传说羁绊";
|
|
22
|
+
readonly 'pet.rank.name.\u795E\u8BDD\u7F81\u7ECA': "神话羁绊";
|
|
23
|
+
readonly 'pet.rank.name.\u6C38\u6052\u4E4B\u5951': "永恒之契";
|
|
24
|
+
readonly 'pet.rank.name.\u9CB8\u751F\u5171\u6E21': "鲸生共渡";
|
|
16
25
|
readonly 'pet.points': "{points} 点";
|
|
17
26
|
readonly 'pet.treats': "小鱼干 ×{n}";
|
|
18
27
|
readonly 'pet.state.loading': "宠物正在赶来…";
|
|
@@ -83,6 +92,15 @@ export declare const en: {
|
|
|
83
92
|
readonly 'pet.namePlaceholder': "Enter a new name";
|
|
84
93
|
readonly 'pet.summon': "Summon {name}";
|
|
85
94
|
readonly 'pet.rank': "Affinity {rank}";
|
|
95
|
+
readonly 'pet.rank.name.\u5E7C\u9CB8': "Baby Whale";
|
|
96
|
+
readonly 'pet.rank.name.\u4F19\u4F34': "Companion";
|
|
97
|
+
readonly 'pet.rank.name.\u631A\u53CB': "Close Friend";
|
|
98
|
+
readonly 'pet.rank.name.\u6DF1\u6D77\u7F81\u7ECA': "Deep Sea Bond";
|
|
99
|
+
readonly 'pet.rank.name.\u5FC3\u6709\u7075\u7280': "Kindred Spirit";
|
|
100
|
+
readonly 'pet.rank.name.\u4F20\u8BF4\u7F81\u7ECA': "Legendary Bond";
|
|
101
|
+
readonly 'pet.rank.name.\u795E\u8BDD\u7F81\u7ECA': "Mythic Bond";
|
|
102
|
+
readonly 'pet.rank.name.\u6C38\u6052\u4E4B\u5951': "Eternal Covenant";
|
|
103
|
+
readonly 'pet.rank.name.\u9CB8\u751F\u5171\u6E21': "Lifelong Companion";
|
|
86
104
|
readonly 'pet.points': "{points} pts";
|
|
87
105
|
readonly 'pet.treats': "Treats ×{n}";
|
|
88
106
|
readonly 'pet.state.loading': "The pet is on its way…";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,mDAAmD;AACnD,eAAO,MAAM,EAAE,QAAQ,CAAA;AAEvB,oBAAoB;AACpB,eAAO,MAAM,EAAE
|
|
1
|
+
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,mDAAmD;AACnD,eAAO,MAAM,EAAE,QAAQ,CAAA;AAEvB,oBAAoB;AACpB,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+EL,CAAA;AAEV,oBAAoB;AACpB,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+EL,CAAA;AAEV,oCAAoC;AACpC,MAAM,MAAM,MAAM,GAAG,MAAM,OAAO,EAAE,CAAA;AAEpC,qDAAqD;AACrD,MAAM,MAAM,eAAe,GAAG,MAAM,CAAA;AAEpC;;;;;GAKG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAGnD;AAED;;;;;;;GAOG;AACH,wBAAgB,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAQvE;AAED,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,uBAAuB;QACvB,GAAG,EAAE,MAAM,CAAA;KACZ;CACF"}
|
|
@@ -13,6 +13,15 @@ export const zh = {
|
|
|
13
13
|
'pet.namePlaceholder': '输入新名字',
|
|
14
14
|
'pet.summon': '召唤{name}',
|
|
15
15
|
'pet.rank': '亲密度 {rank}',
|
|
16
|
+
'pet.rank.name.幼鲸': '幼鲸',
|
|
17
|
+
'pet.rank.name.伙伴': '伙伴',
|
|
18
|
+
'pet.rank.name.挚友': '挚友',
|
|
19
|
+
'pet.rank.name.深海羁绊': '深海羁绊',
|
|
20
|
+
'pet.rank.name.心有灵犀': '心有灵犀',
|
|
21
|
+
'pet.rank.name.传说羁绊': '传说羁绊',
|
|
22
|
+
'pet.rank.name.神话羁绊': '神话羁绊',
|
|
23
|
+
'pet.rank.name.永恒之契': '永恒之契',
|
|
24
|
+
'pet.rank.name.鲸生共渡': '鲸生共渡',
|
|
16
25
|
'pet.points': '{points} 点',
|
|
17
26
|
'pet.treats': '小鱼干 ×{n}',
|
|
18
27
|
'pet.state.loading': '宠物正在赶来…',
|
|
@@ -85,6 +94,15 @@ export const en = {
|
|
|
85
94
|
'pet.namePlaceholder': 'Enter a new name',
|
|
86
95
|
'pet.summon': 'Summon {name}',
|
|
87
96
|
'pet.rank': 'Affinity {rank}',
|
|
97
|
+
'pet.rank.name.幼鲸': 'Baby Whale',
|
|
98
|
+
'pet.rank.name.伙伴': 'Companion',
|
|
99
|
+
'pet.rank.name.挚友': 'Close Friend',
|
|
100
|
+
'pet.rank.name.深海羁绊': 'Deep Sea Bond',
|
|
101
|
+
'pet.rank.name.心有灵犀': 'Kindred Spirit',
|
|
102
|
+
'pet.rank.name.传说羁绊': 'Legendary Bond',
|
|
103
|
+
'pet.rank.name.神话羁绊': 'Mythic Bond',
|
|
104
|
+
'pet.rank.name.永恒之契': 'Eternal Covenant',
|
|
105
|
+
'pet.rank.name.鲸生共渡': 'Lifelong Companion',
|
|
88
106
|
'pet.points': '{points} pts',
|
|
89
107
|
'pet.treats': 'Treats ×{n}',
|
|
90
108
|
'pet.state.loading': 'The pet is on its way…',
|
|
@@ -8,6 +8,18 @@
|
|
|
8
8
|
* phase-mapped track. Rendering never throws: a broken track list degrades
|
|
9
9
|
* to the first decodable frame, and the 1.2 s stall watchdog re-kicks the
|
|
10
10
|
* playback chain after timer throttling.
|
|
11
|
+
*
|
|
12
|
+
* Frame presentation has two modes picked once at mount by capability
|
|
13
|
+
* probing:
|
|
14
|
+
* - Canvas bitmap buffer (default where createImageBitmap/fetch/2D context
|
|
15
|
+
* exist): every frame is decoded exactly once into an ImageBitmap during
|
|
16
|
+
* the warm pass and drawn onto one <canvas> - steady-state playback issues
|
|
17
|
+
* zero DOM mutations and zero re-decodes (measured hotspot: swapping
|
|
18
|
+
* <img>.src per frame drove image decode + invalidation every tick).
|
|
19
|
+
* - Classic <img> fallback (jsdom/tests or missing APIs): identical to the
|
|
20
|
+
* historical behavior - cache-warm Image elements plus guarded src swaps,
|
|
21
|
+
* so environments without modern decoding keep working unchanged.
|
|
22
|
+
*
|
|
11
23
|
* @module @linxin666/dsh-pet/client/renderers/frames2d
|
|
12
24
|
*/
|
|
13
25
|
import { type PetRenderer, type PetRendererHandle } from '../../contracts/renderer.ts';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frames2d.d.ts","sourceRoot":"","sources":["../../../../src/client/renderers/frames2d.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"frames2d.d.ts","sourceRoot":"","sources":["../../../../src/client/renderers/frames2d.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAA4B,KAAK,WAAW,EAA2B,KAAK,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AACzI,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,oEAAoE;AACpE,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,IAAI,EAAE,OAAO,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,8DAA8D;AAC9D,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAA;IAC3C,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAClE;AAED,MAAM,WAAW,sBAAuB,SAAQ,iBAAiB;IAC/D,gFAAgF;IAChF,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAA;IACzC,2DAA2D;IAC3D,YAAY,IAAI,MAAM,CAAA;CACvB;AA0CD,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,iBAAiB,CAsP3D,CAAA"}
|
|
@@ -8,6 +8,18 @@
|
|
|
8
8
|
* phase-mapped track. Rendering never throws: a broken track list degrades
|
|
9
9
|
* to the first decodable frame, and the 1.2 s stall watchdog re-kicks the
|
|
10
10
|
* playback chain after timer throttling.
|
|
11
|
+
*
|
|
12
|
+
* Frame presentation has two modes picked once at mount by capability
|
|
13
|
+
* probing:
|
|
14
|
+
* - Canvas bitmap buffer (default where createImageBitmap/fetch/2D context
|
|
15
|
+
* exist): every frame is decoded exactly once into an ImageBitmap during
|
|
16
|
+
* the warm pass and drawn onto one <canvas> - steady-state playback issues
|
|
17
|
+
* zero DOM mutations and zero re-decodes (measured hotspot: swapping
|
|
18
|
+
* <img>.src per frame drove image decode + invalidation every tick).
|
|
19
|
+
* - Classic <img> fallback (jsdom/tests or missing APIs): identical to the
|
|
20
|
+
* historical behavior - cache-warm Image elements plus guarded src swaps,
|
|
21
|
+
* so environments without modern decoding keep working unchanged.
|
|
22
|
+
*
|
|
11
23
|
* @module @linxin666/dsh-pet/client/renderers/frames2d
|
|
12
24
|
*/
|
|
13
25
|
import { PET_RENDERER_API_VERSION } from "../../contracts/renderer.js";
|
|
@@ -50,23 +62,83 @@ export const frames2dRenderer = {
|
|
|
50
62
|
const reducedMotion = typeof window !== 'undefined'
|
|
51
63
|
&& typeof window.matchMedia === 'function'
|
|
52
64
|
&& window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
img
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const pre = new Image();
|
|
67
|
-
pre.src = url;
|
|
65
|
+
// Capability probe: the canvas path needs decode-to-bitmap, same-origin
|
|
66
|
+
// fetch, and a real 2D context (jsdom returns null and falls back below).
|
|
67
|
+
let canvas = null;
|
|
68
|
+
let context2d = null;
|
|
69
|
+
let img = null;
|
|
70
|
+
try {
|
|
71
|
+
if (typeof createImageBitmap === 'function' && typeof fetch === 'function') {
|
|
72
|
+
const probe = document.createElement('canvas');
|
|
73
|
+
const c2d = probe.getContext('2d');
|
|
74
|
+
if (c2d !== null) {
|
|
75
|
+
canvas = probe;
|
|
76
|
+
context2d = c2d;
|
|
77
|
+
}
|
|
68
78
|
}
|
|
69
79
|
}
|
|
80
|
+
catch {
|
|
81
|
+
canvas = null;
|
|
82
|
+
context2d = null;
|
|
83
|
+
}
|
|
84
|
+
if (canvas !== null && context2d !== null) {
|
|
85
|
+
canvas.dataset.dshPetFrames2d = ctx.petId;
|
|
86
|
+
canvas.draggable = false;
|
|
87
|
+
canvas.style.width = '100%';
|
|
88
|
+
canvas.style.height = '100%';
|
|
89
|
+
canvas.style.objectFit = 'contain';
|
|
90
|
+
canvas.style.pointerEvents = 'none';
|
|
91
|
+
ctx.container.appendChild(canvas);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
img = document.createElement('img');
|
|
95
|
+
img.dataset.dshPetFrames2d = ctx.petId;
|
|
96
|
+
img.alt = '';
|
|
97
|
+
img.draggable = false;
|
|
98
|
+
img.style.width = '100%';
|
|
99
|
+
img.style.height = '100%';
|
|
100
|
+
img.style.objectFit = 'contain';
|
|
101
|
+
img.style.pointerEvents = 'none';
|
|
102
|
+
ctx.container.appendChild(img);
|
|
103
|
+
}
|
|
104
|
+
// Decode cache: one concurrent decode per URL, memoized forever (frames
|
|
105
|
+
// are tiny same-origin webp files; failures resolve undefined and keep
|
|
106
|
+
// the last painted frame instead of breaking playback).
|
|
107
|
+
const decoding = new Map();
|
|
108
|
+
const decodedAll = [];
|
|
109
|
+
const loadFrame = (url) => {
|
|
110
|
+
const cached = decoding.get(url);
|
|
111
|
+
if (cached !== undefined)
|
|
112
|
+
return cached;
|
|
113
|
+
const job = (async () => {
|
|
114
|
+
try {
|
|
115
|
+
const response = await fetch(url);
|
|
116
|
+
if (!response.ok)
|
|
117
|
+
throw new Error('http ' + response.status);
|
|
118
|
+
const bitmap = await createImageBitmap(await response.blob());
|
|
119
|
+
return { source: bitmap, width: bitmap.width, height: bitmap.height };
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// Fail-open: classic Image decode keeps non-modern runtimes alive.
|
|
123
|
+
return await new Promise((resolve) => {
|
|
124
|
+
try {
|
|
125
|
+
const pre = new Image();
|
|
126
|
+
pre.onload = () => {
|
|
127
|
+
resolve(pre.naturalWidth > 0 ? { source: pre, width: pre.naturalWidth, height: pre.naturalHeight } : undefined);
|
|
128
|
+
};
|
|
129
|
+
pre.onerror = () => resolve(undefined);
|
|
130
|
+
pre.src = url;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
resolve(undefined);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
})();
|
|
138
|
+
decoding.set(url, job);
|
|
139
|
+
decodedAll.push(job.then(() => undefined, () => undefined));
|
|
140
|
+
return job;
|
|
141
|
+
};
|
|
70
142
|
let disposed = false;
|
|
71
143
|
let timer;
|
|
72
144
|
let watchdog;
|
|
@@ -74,15 +146,45 @@ export const frames2dRenderer = {
|
|
|
74
146
|
let frameIndex = 0;
|
|
75
147
|
let lastAdvance = Date.now();
|
|
76
148
|
let override;
|
|
149
|
+
let drawToken = 0;
|
|
150
|
+
let lastDrawnUrl;
|
|
77
151
|
const trackForPhase = (phase) => {
|
|
78
152
|
const mapped = config.phases[phase];
|
|
79
153
|
return mapped !== undefined && config.tracks[mapped] !== undefined ? mapped : config.phases.idle;
|
|
80
154
|
};
|
|
155
|
+
/** Canvas path: paints the newest requested frame; stale draws drop out. */
|
|
156
|
+
const paintCanvas = (url) => {
|
|
157
|
+
if (context2d === null || canvas === null)
|
|
158
|
+
return;
|
|
159
|
+
const myToken = ++drawToken;
|
|
160
|
+
void loadFrame(url).then((frame) => {
|
|
161
|
+
if (disposed || frame === undefined || myToken !== drawToken)
|
|
162
|
+
return;
|
|
163
|
+
if (lastDrawnUrl === url)
|
|
164
|
+
return;
|
|
165
|
+
lastDrawnUrl = url;
|
|
166
|
+
// Resizing clears the canvas, so size only when it actually differs.
|
|
167
|
+
if (canvas.width !== frame.width || canvas.height !== frame.height) {
|
|
168
|
+
canvas.width = frame.width;
|
|
169
|
+
canvas.height = frame.height;
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
context2d.clearRect(0, 0, canvas.width, canvas.height);
|
|
173
|
+
}
|
|
174
|
+
context2d.drawImage(frame.source, 0, 0);
|
|
175
|
+
}).catch(() => { });
|
|
176
|
+
};
|
|
81
177
|
const show = (trackId, index) => {
|
|
82
178
|
const def = config.tracks[trackId];
|
|
83
179
|
const url = def?.frames[index];
|
|
84
|
-
if (url
|
|
85
|
-
|
|
180
|
+
if (url === undefined)
|
|
181
|
+
return;
|
|
182
|
+
if (img !== null) {
|
|
183
|
+
if (img.getAttribute('src') !== url)
|
|
184
|
+
img.src = url;
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
paintCanvas(url);
|
|
86
188
|
};
|
|
87
189
|
const schedule = (ms) => {
|
|
88
190
|
if (disposed)
|
|
@@ -155,6 +257,13 @@ export const frames2dRenderer = {
|
|
|
155
257
|
tick();
|
|
156
258
|
}, WATCHDOG_MS);
|
|
157
259
|
}
|
|
260
|
+
// Warm pass: decode every frame up front (tiny same-origin webp files)
|
|
261
|
+
// so loops and phase switches never wait on a first decode - same intent
|
|
262
|
+
// as the historical Image-cache warm loop, now feeding the decode cache.
|
|
263
|
+
for (const warmTrack of Object.values(config.tracks)) {
|
|
264
|
+
for (const warmUrl of warmTrack.frames)
|
|
265
|
+
void loadFrame(warmUrl);
|
|
266
|
+
}
|
|
158
267
|
play(track);
|
|
159
268
|
let disposedOnce = false;
|
|
160
269
|
const dispose = () => {
|
|
@@ -167,7 +276,23 @@ export const frames2dRenderer = {
|
|
|
167
276
|
clearTimeout(timer);
|
|
168
277
|
if (watchdog !== undefined)
|
|
169
278
|
clearInterval(watchdog);
|
|
170
|
-
|
|
279
|
+
// Release decoded bitmaps after pending decodes settle; close() is
|
|
280
|
+
// browser-only, so guard it for exotic hosts.
|
|
281
|
+
void Promise.allSettled(decodedAll).then(() => {
|
|
282
|
+
for (const job of decoding.values()) {
|
|
283
|
+
void job.then((frame) => {
|
|
284
|
+
try {
|
|
285
|
+
const maybeClose = frame?.source?.close;
|
|
286
|
+
if (typeof maybeClose === 'function' && frame !== undefined)
|
|
287
|
+
maybeClose.call(frame.source);
|
|
288
|
+
}
|
|
289
|
+
catch { /* already released */ }
|
|
290
|
+
}).catch(() => { });
|
|
291
|
+
}
|
|
292
|
+
decoding.clear();
|
|
293
|
+
});
|
|
294
|
+
canvas?.remove();
|
|
295
|
+
img?.remove();
|
|
171
296
|
};
|
|
172
297
|
ctx.onCleanup(dispose);
|
|
173
298
|
return {
|
package/lib/types/ledger.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export interface LedgerConfig {
|
|
|
17
17
|
treats?: Partial<TreatConfig>;
|
|
18
18
|
/** Per-pet remark pools for the selected pet (custom slots override built-ins). */
|
|
19
19
|
remarks?: PetRemarks;
|
|
20
|
+
/** Voice-pack fallback remark pools (global or pet voice pack). */
|
|
21
|
+
voiceRemarks?: PetRemarks;
|
|
20
22
|
}
|
|
21
23
|
/** Result of one ledger interaction (the shape the pet RPC returns). */
|
|
22
24
|
export interface LedgerInteractionResult {
|
|
@@ -66,9 +68,9 @@ export declare class PetLedger {
|
|
|
66
68
|
setPetName(petId: string, name: string): void;
|
|
67
69
|
/**
|
|
68
70
|
* Swap the reaction pools to another pet's custom remarks (called on pet
|
|
69
|
-
* selection). Slots the pet does not declare fall back to built-ins.
|
|
71
|
+
* selection). Slots the pet does not declare fall back to voice packs or built-ins.
|
|
70
72
|
*/
|
|
71
|
-
setRemarks(remarks?: PetRemarks): void;
|
|
73
|
+
setRemarks(remarks?: PetRemarks, voiceRemarks?: PetRemarks): void;
|
|
72
74
|
/**
|
|
73
75
|
* Settle the treat economy (work + time output since the last settlement).
|
|
74
76
|
* A zero-gain first settlement still starts the time clock (anchor write),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../src/ledger.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAKL,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,eAAe,CAAA;AACtB,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAA;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAErD,iDAAiD;AACjD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAA;IAClC,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IAC7B,mFAAmF;IACnF,OAAO,CAAC,EAAE,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../src/ledger.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAKL,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,eAAe,CAAA;AACtB,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAA;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAErD,iDAAiD;AACjD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAA;IAClC,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IAC7B,mFAAmF;IACnF,OAAO,CAAC,EAAE,UAAU,CAAA;IACpB,mEAAmE;IACnE,YAAY,CAAC,EAAE,UAAU,CAAA;CAC1B;AAED,wEAAwE;AACxE,MAAM,WAAW,uBAAuB;IACtC,4BAA4B;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAA;IACb,6DAA6D;IAC7D,QAAQ,EAAE,eAAe,CAAA;CAC1B;AAED;;;;GAIG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgB;IAC/C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAa;IACzC,0EAA0E;IAC1E,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,OAAO,CAAY;IAC3B,oFAAoF;IACpF,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,sBAAsB,CAAI;IAClC,OAAO,CAAC,KAAK,CAAQ;gBAET,OAAO,EAAE,UAAU,EAAE,MAAM,GAAE,YAAiB;IAO1D,iDAAiD;IACjD,IAAI,QAAQ,IAAI,cAAc,CAE7B;IAED,qEAAqE;IACrE,IAAI,QAAQ,IAAI,UAAU,CAEzB;IAED,qCAAqC;IACrC,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,+DAA+D;IAC/D,SAAS,IAAI,OAAO;IAMpB;;;OAGG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAItC,mEAAmE;IACnE,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAK3C,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAM7B,qFAAqF;IACrF,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI;IAK5D,0EAA0E;IAC1E,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAK7C;;;OAGG;IACH,UAAU,CAAC,OAAO,CAAC,EAAE,UAAU,EAAE,YAAY,CAAC,EAAE,UAAU,GAAG,IAAI;IAIjE;;;;;OAKG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAapC;;;;;OAKG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IASpC,8EAA8E;IAC9E,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE;IAQ5C;;;;OAIG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IASnE,8EAA8E;IAC9E,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IASxC,OAAO,CAAC,eAAe;IASvB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,uBAAuB;IAwCtE,kDAAkD;IAClD,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe;CAG7C"}
|