@linxin666/dsh-pet 0.1.18 → 0.1.19
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 +12 -5
- package/README.zh.md +12 -5
- package/lib/client.js +48 -11
- package/lib/client.js.map +1 -1
- package/lib/index.js +100 -30
- package/lib/invariant.js +2 -2
- package/lib/state-Diiq8vjk.js +394 -0
- package/lib/types/affinity.d.ts +31 -3
- package/lib/types/affinity.d.ts.map +1 -1
- package/lib/types/affinity.js +21 -7
- package/lib/types/client/PetSettingsCard.d.ts +5 -0
- package/lib/types/client/PetSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PetSettingsCard.js +7 -0
- package/lib/types/client/PetSprite.d.ts.map +1 -1
- package/lib/types/client/PetSprite.js +8 -2
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +14 -8
- package/lib/types/client/settings-form.d.ts +8 -0
- package/lib/types/client/settings-form.d.ts.map +1 -1
- package/lib/types/client/settings-form.js +15 -1
- package/lib/types/event-projection.d.ts.map +1 -1
- package/lib/types/event-projection.js +3 -1
- package/lib/types/index.d.ts +4 -2
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +2 -1
- package/lib/types/ledger.d.ts +16 -1
- package/lib/types/ledger.d.ts.map +1 -1
- package/lib/types/ledger.js +29 -4
- package/lib/types/registry.d.ts +10 -0
- package/lib/types/registry.d.ts.map +1 -1
- package/lib/types/registry.js +3 -0
- package/lib/types/remarks.d.ts +56 -0
- package/lib/types/remarks.d.ts.map +1 -0
- package/lib/types/remarks.js +157 -0
- package/lib/types/service.d.ts +40 -3
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +90 -18
- package/lib/types/state.js +5 -5
- package/lib/types/treats.d.ts +3 -0
- package/lib/types/treats.d.ts.map +1 -1
- package/lib/types/treats.js +5 -2
- package/package.json +1 -1
- package/src/affinity.test.ts +19 -0
- package/src/affinity.ts +22 -7
- package/src/client/PetSettingsCard.tsx +8 -0
- package/src/client/PetSprite.test.tsx +33 -0
- package/src/client/PetSprite.tsx +16 -1
- package/src/client/index.ts +14 -8
- package/src/client/pet.module.css +19 -0
- package/src/client/settings-form.ts +15 -1
- package/src/event-projection.ts +3 -1
- package/src/index.ts +12 -1
- package/src/ledger.test.ts +40 -9
- package/src/ledger.ts +33 -4
- package/src/persist.test.ts +1 -1
- package/src/registry.test.ts +27 -0
- package/src/registry.ts +12 -0
- package/src/remarks.test.ts +89 -0
- package/src/remarks.ts +177 -0
- package/src/service.ts +116 -18
- package/src/state.test.ts +9 -0
- package/src/state.ts +5 -5
- package/src/treats.test.ts +13 -11
- package/src/treats.ts +5 -2
- package/lib/state-CFyJv0sQ.js +0 -213
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
//#region src/remarks.ts
|
|
2
|
+
/** Every remark slot, in a stable order. */
|
|
3
|
+
const REMARK_KINDS = [
|
|
4
|
+
"pet",
|
|
5
|
+
"petCooldown",
|
|
6
|
+
"feed",
|
|
7
|
+
"feedCooldown",
|
|
8
|
+
"noTreats"
|
|
9
|
+
];
|
|
10
|
+
/** Longest accepted reaction line (characters, trimmed before slicing). */
|
|
11
|
+
const REMARK_LINE_MAX = 120;
|
|
12
|
+
/** Longest accepted pool per slot. */
|
|
13
|
+
const REMARK_LINES_MAX = 64;
|
|
14
|
+
/**
|
|
15
|
+
* Built-in default remark library. Every pool is plain zh copy in the
|
|
16
|
+
* whale-girl voice; the first line of each pool is the legacy reaction the
|
|
17
|
+
* plugin has always spoken, so existing installs and tests keep their
|
|
18
|
+
* wording while the pool adds variety. No emoji characters anywhere (the
|
|
19
|
+
* repo bans them); ~ is the whale-girl's signature.
|
|
20
|
+
*/
|
|
21
|
+
const BUILTIN_REMARKS = {
|
|
22
|
+
pet: [
|
|
23
|
+
"咕噜咕噜~被摸摸好舒服!",
|
|
24
|
+
"再摸摸这里,痒痒的~",
|
|
25
|
+
"头顶温度刚刚好,安心~",
|
|
26
|
+
"被摸到耳朵啦,扑通扑通!",
|
|
27
|
+
"你的手掌好温暖,舍不得你走~",
|
|
28
|
+
"呼噜呼噜~就靠在这里不走了!",
|
|
29
|
+
"今天的摸头也收货成功!",
|
|
30
|
+
"蹭蹭你的手心,这是回礼~",
|
|
31
|
+
"多摸摸我,亲密度会涨哦!",
|
|
32
|
+
"闭眼享受中,请勿打扰~"
|
|
33
|
+
],
|
|
34
|
+
petCooldown: [
|
|
35
|
+
"摸过头啦,让鲸鱼娘歇口气~",
|
|
36
|
+
"羽毛都快被摸秃啦,缓一缓~",
|
|
37
|
+
"呼……先让我喘口气嘛!",
|
|
38
|
+
"再摸就要睡着了哦~",
|
|
39
|
+
"稍微休息一下,待会儿再摸~",
|
|
40
|
+
"头顶要冒烟啦,停一停!",
|
|
41
|
+
"我知道你喜欢我,但也要节制呀~",
|
|
42
|
+
"歇一歇,摸摸的手感会更好哦~",
|
|
43
|
+
"咕……等我回个蓝~",
|
|
44
|
+
"让我先消化一下刚才的爱!"
|
|
45
|
+
],
|
|
46
|
+
feed: [
|
|
47
|
+
"呜哇!小鱼干好好吃!",
|
|
48
|
+
"咔嚓咔嚓,美味到尾巴打结~",
|
|
49
|
+
"这条小鱼干是刚晒好的,好香!",
|
|
50
|
+
"谢谢你,胃里暖暖的~",
|
|
51
|
+
"囤粮 +1,今天也有好好被爱!",
|
|
52
|
+
"好吃到想转圈圈~",
|
|
53
|
+
"小鱼干最好吃了,再来亿条!",
|
|
54
|
+
"饱餐一顿,马上满血复活~",
|
|
55
|
+
"这个味道,是幸福的味道!",
|
|
56
|
+
"吃完了还不忘舔舔爪子~"
|
|
57
|
+
],
|
|
58
|
+
feedCooldown: [
|
|
59
|
+
"吃饱啦,晚点再喂~",
|
|
60
|
+
"肚子圆滚滚的,装不下啦~",
|
|
61
|
+
"再喂就要变成球啦!",
|
|
62
|
+
"让我慢慢消化这份心意~",
|
|
63
|
+
"小鱼干的香气还没散呢~",
|
|
64
|
+
"呼……满足得动不了了~",
|
|
65
|
+
"先散步一圈再吃下一顿!",
|
|
66
|
+
"肚皮已经鼓鼓的啦~",
|
|
67
|
+
"好吃是好吃,可也得节制呀~",
|
|
68
|
+
"等我饿了会告诉你哦~"
|
|
69
|
+
],
|
|
70
|
+
noTreats: [
|
|
71
|
+
"没有小鱼干了,多陪我工作一会儿吧~",
|
|
72
|
+
"粮仓空空,陪我完成几轮任务就会有小鱼干啦~",
|
|
73
|
+
"小鱼干在路上啦,先一起加油工作!",
|
|
74
|
+
"嘴巴寂寞了……快去完成一轮任务!",
|
|
75
|
+
"陪我多工作一会儿,鱼干自动到账~",
|
|
76
|
+
"现在喂我也只会饿着肚子说谢谢哦~",
|
|
77
|
+
"粮仓见底啦,用几轮任务换一条鱼干吧~",
|
|
78
|
+
"饿着肚子等你完成下一轮任务~",
|
|
79
|
+
"小鱼干藏在你的工作里,去找找看!",
|
|
80
|
+
"先工作后干饭,我们的约定哦~"
|
|
81
|
+
]
|
|
82
|
+
};
|
|
83
|
+
/** The legacy first line of one kind (direct callers' fallback copy). */
|
|
84
|
+
function builtinRemark(kind) {
|
|
85
|
+
return BUILTIN_REMARKS[kind][0];
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Normalize a manifest 'remarks' block into per-kind line pools. Unknown
|
|
89
|
+
* slots and non-string entries are skipped with a warning; empty pools are
|
|
90
|
+
* dropped so the built-in library takes the slot. Returns undefined when no
|
|
91
|
+
* usable slot remains.
|
|
92
|
+
*/
|
|
93
|
+
function normalizePetRemarks(raw, onWarning = () => {}) {
|
|
94
|
+
if (raw === void 0) return void 0;
|
|
95
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
96
|
+
onWarning("remarks must be an object with pet/petCooldown/feed/feedCooldown/noTreats slots");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const remarks = {};
|
|
100
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
101
|
+
if (!REMARK_KINDS.includes(key)) {
|
|
102
|
+
onWarning("unknown remarks slot " + JSON.stringify(key));
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const lines = (Array.isArray(value) ? value : [value]).filter((line) => typeof line === "string").map((line) => line.trim()).filter((line) => line !== "").slice(0, 64).map((line) => line.slice(0, 120));
|
|
106
|
+
if (lines.length === 0) {
|
|
107
|
+
onWarning("remarks slot " + key + " carries no usable lines");
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
remarks[key] = lines;
|
|
111
|
+
}
|
|
112
|
+
return Object.keys(remarks).length === 0 ? void 0 : remarks;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Round-robin reaction picker over the effective pools (per-pet custom lines
|
|
116
|
+
* override the built-in pool per slot). Counters are per slot, so each slot
|
|
117
|
+
* cycles its own list independently and picks stay deterministic for tests.
|
|
118
|
+
*/
|
|
119
|
+
var RemarkPicker = class {
|
|
120
|
+
counters = /* @__PURE__ */ new Map();
|
|
121
|
+
pools;
|
|
122
|
+
constructor(overrides) {
|
|
123
|
+
this.pools = {};
|
|
124
|
+
for (const kind of REMARK_KINDS) {
|
|
125
|
+
const custom = overrides?.[kind];
|
|
126
|
+
this.pools[kind] = custom !== void 0 && custom.length > 0 ? custom : BUILTIN_REMARKS[kind];
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/** The effective pool for one slot (custom override or built-in). */
|
|
130
|
+
pool(kind) {
|
|
131
|
+
return this.pools[kind];
|
|
132
|
+
}
|
|
133
|
+
/** The next line for one slot (round-robin within its pool). */
|
|
134
|
+
pick(kind) {
|
|
135
|
+
const pool = this.pools[kind];
|
|
136
|
+
const index = (this.counters.get(kind) ?? 0) % pool.length;
|
|
137
|
+
this.counters.set(kind, index + 1);
|
|
138
|
+
return pool[index];
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
//#endregion
|
|
142
|
+
//#region src/affinity.ts
|
|
143
|
+
/**
|
|
144
|
+
* Affinity score — pure, clock-injected. The pet grows closer the more you
|
|
145
|
+
* work together and care for it: every completed turn earns a small reward,
|
|
146
|
+
* petting earns a tiny one (cooldown-gated), feeding earns the most.
|
|
147
|
+
* Persistence lives in the service; this module only computes transitions.
|
|
148
|
+
* Reaction copy resolves from the shared remark library (first line of each
|
|
149
|
+
* built-in pool); the ledger layers per-pet custom remarks on top.
|
|
150
|
+
* @module @linxin666/dsh-pet/affinity
|
|
151
|
+
*/
|
|
152
|
+
/**
|
|
153
|
+
* Affinity points cap. Historically 100; removed so long-term companions
|
|
154
|
+
* keep growing — the default limit is now the full 999,999,999 range.
|
|
155
|
+
*/
|
|
156
|
+
const AFFINITY_MAX = 999999999;
|
|
157
|
+
/** Affinity ranks by points; the pet visibly grows with its rank.
|
|
158
|
+
* The original four tiers (0/25/50/80) are unchanged; higher tiers reach
|
|
159
|
+
* into the extended cap so the ladder stays meaningful for veteran
|
|
160
|
+
* companions. Marker glyphs are plain ASCII (the repo bans all emoji
|
|
161
|
+
* characters); they read as a growing star trail alongside the rank name. */
|
|
162
|
+
const AFFINITY_RANKS = [
|
|
163
|
+
{
|
|
164
|
+
min: 0,
|
|
165
|
+
name: "幼鲸",
|
|
166
|
+
emoji: "*"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
min: 25,
|
|
170
|
+
name: "伙伴",
|
|
171
|
+
emoji: "**"
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
min: 50,
|
|
175
|
+
name: "挚友",
|
|
176
|
+
emoji: "***"
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
min: 80,
|
|
180
|
+
name: "深海羁绊",
|
|
181
|
+
emoji: "****"
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
min: 200,
|
|
185
|
+
name: "心有灵犀",
|
|
186
|
+
emoji: "*****"
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
min: 500,
|
|
190
|
+
name: "传说羁绊",
|
|
191
|
+
emoji: "******"
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
min: 2e3,
|
|
195
|
+
name: "神话羁绊",
|
|
196
|
+
emoji: "*******"
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
min: 1e4,
|
|
200
|
+
name: "永恒之契",
|
|
201
|
+
emoji: "********"
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
min: 1e5,
|
|
205
|
+
name: "鲸生共渡",
|
|
206
|
+
emoji: "*********"
|
|
207
|
+
}
|
|
208
|
+
];
|
|
209
|
+
const defaultAffinityConfig = {
|
|
210
|
+
turnReward: 1,
|
|
211
|
+
petReward: 1,
|
|
212
|
+
petCooldownMs: 1e4,
|
|
213
|
+
feedReward: 5,
|
|
214
|
+
feedCooldownMs: 3e4
|
|
215
|
+
};
|
|
216
|
+
function emptyAffinity() {
|
|
217
|
+
return {
|
|
218
|
+
points: 0,
|
|
219
|
+
lastPetAt: 0,
|
|
220
|
+
lastFeedAt: 0,
|
|
221
|
+
pets: 0,
|
|
222
|
+
feeds: 0,
|
|
223
|
+
turns: 0
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/** Rank for a point total. */
|
|
227
|
+
function rankOf(points) {
|
|
228
|
+
let rank = AFFINITY_RANKS[0];
|
|
229
|
+
for (const candidate of AFFINITY_RANKS) if (points >= candidate.min) rank = candidate;
|
|
230
|
+
return rank;
|
|
231
|
+
}
|
|
232
|
+
/** Derive the read-only view of one affinity state at a wall-clock instant. */
|
|
233
|
+
function affinityViewOf(state, nowMs, config = defaultAffinityConfig) {
|
|
234
|
+
const rank = rankOf(state.points);
|
|
235
|
+
return {
|
|
236
|
+
points: state.points,
|
|
237
|
+
rank: rank.name,
|
|
238
|
+
rankEmoji: rank.emoji,
|
|
239
|
+
pets: state.pets,
|
|
240
|
+
feeds: state.feeds,
|
|
241
|
+
turns: state.turns,
|
|
242
|
+
petCooldown: nowMs - state.lastPetAt < config.petCooldownMs,
|
|
243
|
+
feedCooldown: nowMs - state.lastFeedAt < config.feedCooldownMs
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
function clamp(points) {
|
|
247
|
+
return Math.min(AFFINITY_MAX, Math.max(0, points));
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Apply one interaction to a copy of the state (immutable style: returns a
|
|
251
|
+
* new object; the caller replaces the persisted state). Cooldowns only
|
|
252
|
+
* apply once the pet has been interacted with at least once (last*At === 0
|
|
253
|
+
* means "never", so the first pet/feed always lands).
|
|
254
|
+
*/
|
|
255
|
+
function applyInteraction(state, kind, nowMs, config = defaultAffinityConfig) {
|
|
256
|
+
const next = { ...state };
|
|
257
|
+
if (kind === "pet") {
|
|
258
|
+
if (state.lastPetAt !== 0 && nowMs - state.lastPetAt < config.petCooldownMs) return {
|
|
259
|
+
affinity: state,
|
|
260
|
+
delta: 0,
|
|
261
|
+
reaction: builtinRemark("petCooldown"),
|
|
262
|
+
accepted: false
|
|
263
|
+
};
|
|
264
|
+
next.lastPetAt = nowMs;
|
|
265
|
+
next.pets += 1;
|
|
266
|
+
next.points = clamp(state.points + config.petReward);
|
|
267
|
+
return {
|
|
268
|
+
affinity: next,
|
|
269
|
+
delta: config.petReward,
|
|
270
|
+
reaction: builtinRemark("pet"),
|
|
271
|
+
accepted: true
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
if (kind === "feed") {
|
|
275
|
+
if (state.lastFeedAt !== 0 && nowMs - state.lastFeedAt < config.feedCooldownMs) return {
|
|
276
|
+
affinity: state,
|
|
277
|
+
delta: 0,
|
|
278
|
+
reaction: builtinRemark("feedCooldown"),
|
|
279
|
+
accepted: false
|
|
280
|
+
};
|
|
281
|
+
next.lastFeedAt = nowMs;
|
|
282
|
+
next.feeds += 1;
|
|
283
|
+
next.points = clamp(state.points + config.feedReward);
|
|
284
|
+
return {
|
|
285
|
+
affinity: next,
|
|
286
|
+
delta: config.feedReward,
|
|
287
|
+
reaction: builtinRemark("feed"),
|
|
288
|
+
accepted: true
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
affinity: state,
|
|
293
|
+
delta: 0,
|
|
294
|
+
reaction: "",
|
|
295
|
+
accepted: false
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
/** Reward one completed turn (called by the host on `done`). */
|
|
299
|
+
function applyTurnReward(state, config = defaultAffinityConfig) {
|
|
300
|
+
const next = { ...state };
|
|
301
|
+
next.turns += 1;
|
|
302
|
+
next.points = clamp(state.points + config.turnReward);
|
|
303
|
+
return next;
|
|
304
|
+
}
|
|
305
|
+
//#endregion
|
|
306
|
+
//#region src/state.ts
|
|
307
|
+
const defaultPetStateConfig = { celebrateMs: 2400 };
|
|
308
|
+
/**
|
|
309
|
+
* Map one activity phase onto the animation contract.
|
|
310
|
+
* - thinking → `running` and tool → `running-right` (focused work).
|
|
311
|
+
* - review → `review` while answer text is streaming.
|
|
312
|
+
* - waiting → `waiting` (expectant pose, needs user input).
|
|
313
|
+
* - done → `jumping` (celebration), then back to `idle` after the window.
|
|
314
|
+
* - failed → `failed` until another activity event arrives.
|
|
315
|
+
* - idle → `idle` (calm breathing loop).
|
|
316
|
+
*/
|
|
317
|
+
function animationForPhase(phase) {
|
|
318
|
+
switch (phase) {
|
|
319
|
+
case "thinking": return "running";
|
|
320
|
+
case "tool": return "running-right";
|
|
321
|
+
case "review": return "review";
|
|
322
|
+
case "waiting": return "waiting";
|
|
323
|
+
case "done": return "jumping";
|
|
324
|
+
case "failed": return "failed";
|
|
325
|
+
case "idle": return "idle";
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
/** The spritesheet row index for one animation track. */
|
|
329
|
+
function rowOf(animation) {
|
|
330
|
+
return {
|
|
331
|
+
"idle": 0,
|
|
332
|
+
"running-right": 1,
|
|
333
|
+
"running-left": 2,
|
|
334
|
+
"waving": 3,
|
|
335
|
+
"jumping": 4,
|
|
336
|
+
"failed": 5,
|
|
337
|
+
"waiting": 6,
|
|
338
|
+
"running": 7,
|
|
339
|
+
"review": 8
|
|
340
|
+
}[animation];
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* PetStateMachine — one instance per host process. Holds only the latest
|
|
344
|
+
* input snapshot and the celebration timing; no storage, no side effects.
|
|
345
|
+
*/
|
|
346
|
+
var PetStateMachine = class {
|
|
347
|
+
config;
|
|
348
|
+
now;
|
|
349
|
+
phase = "idle";
|
|
350
|
+
line;
|
|
351
|
+
phrase;
|
|
352
|
+
sessionActive = false;
|
|
353
|
+
doneAt;
|
|
354
|
+
constructor(config = defaultPetStateConfig, now = Date.now) {
|
|
355
|
+
this.config = config;
|
|
356
|
+
this.now = now;
|
|
357
|
+
}
|
|
358
|
+
/** Consume one projected activity update. */
|
|
359
|
+
onActivityStatus(input) {
|
|
360
|
+
this.phase = input.phase;
|
|
361
|
+
this.line = input.line;
|
|
362
|
+
this.phrase = input.phrase;
|
|
363
|
+
if (input.phase === "done") this.doneAt = this.now();
|
|
364
|
+
}
|
|
365
|
+
/** A session became the active one (or a fresh session started). */
|
|
366
|
+
onSessionActive() {
|
|
367
|
+
this.sessionActive = true;
|
|
368
|
+
}
|
|
369
|
+
/** The active session was disposed (or none left). */
|
|
370
|
+
onSessionDisposed() {
|
|
371
|
+
this.sessionActive = false;
|
|
372
|
+
this.phase = "idle";
|
|
373
|
+
this.line = void 0;
|
|
374
|
+
this.phrase = void 0;
|
|
375
|
+
this.doneAt = void 0;
|
|
376
|
+
}
|
|
377
|
+
/** Render the current animation decision. */
|
|
378
|
+
render() {
|
|
379
|
+
const nowMs = this.now();
|
|
380
|
+
let animation = animationForPhase(this.phase);
|
|
381
|
+
if (this.phase === "done" && this.doneAt !== void 0) if (nowMs - this.doneAt < this.config.celebrateMs) animation = "jumping";
|
|
382
|
+
else animation = "idle";
|
|
383
|
+
const bubble = this.phase === "idle" || this.phase === "done" && this.doneAt !== void 0 && nowMs - this.doneAt >= this.config.celebrateMs ? void 0 : this.phrase ?? this.line;
|
|
384
|
+
return {
|
|
385
|
+
animation,
|
|
386
|
+
...bubble === void 0 ? {} : { bubble },
|
|
387
|
+
animationStartedAt: nowMs,
|
|
388
|
+
phase: this.phase,
|
|
389
|
+
sessionActive: this.sessionActive
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
//#endregion
|
|
394
|
+
export { RemarkPicker as _, AFFINITY_MAX as a, applyInteraction as c, emptyAffinity as d, rankOf as f, REMARK_LINE_MAX as g, REMARK_LINES_MAX as h, rowOf as i, applyTurnReward as l, REMARK_KINDS as m, animationForPhase as n, AFFINITY_RANKS as o, BUILTIN_REMARKS as p, defaultPetStateConfig as r, affinityViewOf as s, PetStateMachine as t, defaultAffinityConfig as u, builtinRemark as v, normalizePetRemarks as y };
|
package/lib/types/affinity.d.ts
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* work together and care for it: every completed turn earns a small reward,
|
|
4
4
|
* petting earns a tiny one (cooldown-gated), feeding earns the most.
|
|
5
5
|
* Persistence lives in the service; this module only computes transitions.
|
|
6
|
+
* Reaction copy resolves from the shared remark library (first line of each
|
|
7
|
+
* built-in pool); the ledger layers per-pet custom remarks on top.
|
|
6
8
|
* @module @linxin666/dsh-pet/affinity
|
|
7
9
|
*/
|
|
8
10
|
/** One interaction the user can perform on the pet. */
|
|
@@ -22,10 +24,16 @@ export interface AffinityState {
|
|
|
22
24
|
/** Total completed turns witnessed (lifetime). */
|
|
23
25
|
turns: number;
|
|
24
26
|
}
|
|
25
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Affinity points cap. Historically 100; removed so long-term companions
|
|
29
|
+
* keep growing — the default limit is now the full 999,999,999 range.
|
|
30
|
+
*/
|
|
31
|
+
export declare const AFFINITY_MAX = 999999999;
|
|
26
32
|
/** Affinity ranks by points; the pet visibly grows with its rank.
|
|
27
|
-
*
|
|
28
|
-
*
|
|
33
|
+
* The original four tiers (0/25/50/80) are unchanged; higher tiers reach
|
|
34
|
+
* into the extended cap so the ladder stays meaningful for veteran
|
|
35
|
+
* companions. Marker glyphs are plain ASCII (the repo bans all emoji
|
|
36
|
+
* characters); they read as a growing star trail alongside the rank name. */
|
|
29
37
|
export declare const AFFINITY_RANKS: readonly [{
|
|
30
38
|
readonly min: 0;
|
|
31
39
|
readonly name: "幼鲸";
|
|
@@ -42,6 +50,26 @@ export declare const AFFINITY_RANKS: readonly [{
|
|
|
42
50
|
readonly min: 80;
|
|
43
51
|
readonly name: "深海羁绊";
|
|
44
52
|
readonly emoji: "****";
|
|
53
|
+
}, {
|
|
54
|
+
readonly min: 200;
|
|
55
|
+
readonly name: "心有灵犀";
|
|
56
|
+
readonly emoji: "*****";
|
|
57
|
+
}, {
|
|
58
|
+
readonly min: 500;
|
|
59
|
+
readonly name: "传说羁绊";
|
|
60
|
+
readonly emoji: "******";
|
|
61
|
+
}, {
|
|
62
|
+
readonly min: 2000;
|
|
63
|
+
readonly name: "神话羁绊";
|
|
64
|
+
readonly emoji: "*******";
|
|
65
|
+
}, {
|
|
66
|
+
readonly min: 10000;
|
|
67
|
+
readonly name: "永恒之契";
|
|
68
|
+
readonly emoji: "********";
|
|
69
|
+
}, {
|
|
70
|
+
readonly min: 100000;
|
|
71
|
+
readonly name: "鲸生共渡";
|
|
72
|
+
readonly emoji: "*********";
|
|
45
73
|
}];
|
|
46
74
|
/** Interaction tuning (all in points / ms). */
|
|
47
75
|
export interface AffinityConfig {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"affinity.d.ts","sourceRoot":"","sources":["../../src/affinity.ts"],"names":[],"mappings":"AAAA
|
|
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,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,CAE7C;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;AAMD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,cAAsC,GAC7C,kBAAkB,CA+BpB;AAED,gEAAgE;AAChE,wBAAgB,eAAe,CAC7B,KAAK,EAAE,aAAa,EACpB,MAAM,GAAE,cAAsC,GAC7C,aAAa,CAKf"}
|
package/lib/types/affinity.js
CHANGED
|
@@ -3,17 +3,31 @@
|
|
|
3
3
|
* work together and care for it: every completed turn earns a small reward,
|
|
4
4
|
* petting earns a tiny one (cooldown-gated), feeding earns the most.
|
|
5
5
|
* Persistence lives in the service; this module only computes transitions.
|
|
6
|
+
* Reaction copy resolves from the shared remark library (first line of each
|
|
7
|
+
* built-in pool); the ledger layers per-pet custom remarks on top.
|
|
6
8
|
* @module @linxin666/dsh-pet/affinity
|
|
7
9
|
*/
|
|
8
|
-
|
|
10
|
+
import { builtinRemark } from "./remarks.js";
|
|
11
|
+
/**
|
|
12
|
+
* Affinity points cap. Historically 100; removed so long-term companions
|
|
13
|
+
* keep growing — the default limit is now the full 999,999,999 range.
|
|
14
|
+
*/
|
|
15
|
+
export const AFFINITY_MAX = 999_999_999;
|
|
9
16
|
/** Affinity ranks by points; the pet visibly grows with its rank.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
17
|
+
* The original four tiers (0/25/50/80) are unchanged; higher tiers reach
|
|
18
|
+
* into the extended cap so the ladder stays meaningful for veteran
|
|
19
|
+
* companions. Marker glyphs are plain ASCII (the repo bans all emoji
|
|
20
|
+
* characters); they read as a growing star trail alongside the rank name. */
|
|
12
21
|
export const AFFINITY_RANKS = [
|
|
13
22
|
{ min: 0, name: '幼鲸', emoji: '*' },
|
|
14
23
|
{ min: 25, name: '伙伴', emoji: '**' },
|
|
15
24
|
{ min: 50, name: '挚友', emoji: '***' },
|
|
16
25
|
{ min: 80, name: '深海羁绊', emoji: '****' },
|
|
26
|
+
{ min: 200, name: '心有灵犀', emoji: '*****' },
|
|
27
|
+
{ min: 500, name: '传说羁绊', emoji: '******' },
|
|
28
|
+
{ min: 2_000, name: '神话羁绊', emoji: '*******' },
|
|
29
|
+
{ min: 10_000, name: '永恒之契', emoji: '********' },
|
|
30
|
+
{ min: 100_000, name: '鲸生共渡', emoji: '*********' },
|
|
17
31
|
];
|
|
18
32
|
export const defaultAffinityConfig = {
|
|
19
33
|
turnReward: 1,
|
|
@@ -61,7 +75,7 @@ export function applyInteraction(state, kind, nowMs, config = defaultAffinityCon
|
|
|
61
75
|
const next = { ...state };
|
|
62
76
|
if (kind === 'pet') {
|
|
63
77
|
if (state.lastPetAt !== 0 && nowMs - state.lastPetAt < config.petCooldownMs) {
|
|
64
|
-
return { affinity: state, delta: 0, reaction: '
|
|
78
|
+
return { affinity: state, delta: 0, reaction: builtinRemark('petCooldown'), accepted: false };
|
|
65
79
|
}
|
|
66
80
|
next.lastPetAt = nowMs;
|
|
67
81
|
next.pets += 1;
|
|
@@ -69,13 +83,13 @@ export function applyInteraction(state, kind, nowMs, config = defaultAffinityCon
|
|
|
69
83
|
return {
|
|
70
84
|
affinity: next,
|
|
71
85
|
delta: config.petReward,
|
|
72
|
-
reaction: '
|
|
86
|
+
reaction: builtinRemark('pet'),
|
|
73
87
|
accepted: true,
|
|
74
88
|
};
|
|
75
89
|
}
|
|
76
90
|
if (kind === 'feed') {
|
|
77
91
|
if (state.lastFeedAt !== 0 && nowMs - state.lastFeedAt < config.feedCooldownMs) {
|
|
78
|
-
return { affinity: state, delta: 0, reaction: '
|
|
92
|
+
return { affinity: state, delta: 0, reaction: builtinRemark('feedCooldown'), accepted: false };
|
|
79
93
|
}
|
|
80
94
|
next.lastFeedAt = nowMs;
|
|
81
95
|
next.feeds += 1;
|
|
@@ -83,7 +97,7 @@ export function applyInteraction(state, kind, nowMs, config = defaultAffinityCon
|
|
|
83
97
|
return {
|
|
84
98
|
affinity: next,
|
|
85
99
|
delta: config.feedReward,
|
|
86
|
-
reaction: '
|
|
100
|
+
reaction: builtinRemark('feed'),
|
|
87
101
|
accepted: true,
|
|
88
102
|
};
|
|
89
103
|
}
|
|
@@ -70,6 +70,11 @@ export declare class PetSettingsCardController {
|
|
|
70
70
|
* @returns the card's snapshot and its form actions.
|
|
71
71
|
*/
|
|
72
72
|
inject(): PetSettingsCardFace;
|
|
73
|
+
/**
|
|
74
|
+
* Release the card's scope subscription and bound stores; the slot
|
|
75
|
+
* disposer calls this on teardown.
|
|
76
|
+
*/
|
|
77
|
+
dispose(): void;
|
|
73
78
|
}
|
|
74
79
|
/** Props the renderer binds for the pet settings card. */
|
|
75
80
|
export type PetSettingsCardProps = PropsLocale<'pet'> & InjectFace<PetSettingsCardFace>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PetSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PetSettingsCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAC7F,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAI1F,OAAO,EAAoD,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,IAAI,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAG1J,+EAA+E;AAC/E,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,qBAAqB;IACrB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,0CAA0C;AAC1C,MAAM,WAAW,oBAAqB,SAAQ,SAAS;IACrD,4BAA4B;IAC5B,OAAO,EAAE,cAAc,CAAA;IACvB,qBAAqB;IACrB,OAAO,EAAE,cAAc,CAAA;IACvB,iBAAiB;IACjB,IAAI,EAAE,cAAc,CAAA;IACpB,mBAAmB;IACnB,KAAK,EAAE,cAAc,CAAA;IACrB,oBAAoB;IACpB,MAAM,EAAE,cAAc,CAAA;IACtB,oBAAoB;IACpB,KAAK,EAAE,cAAc,CAAA;IACrB,wEAAwE;IACxE,UAAU,EAAE,SAAS;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CACxD;AAED,gEAAgE;AAChE,MAAM,WAAW,mBAAoB,SAAQ,WAAW;IACtD,KAAK,EAAE;QACL,iEAAiE;QACjE,eAAe,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAA;KACrD,CAAA;CACF;AAeD,2DAA2D;AAC3D,qBAAa,yBAAyB;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAuB;IAC5C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;IAI3D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAe;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IACtD,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,QAAQ,CAAI;IAEpB,uEAAuE;gBAC3D,KAAK,EAAE,aAAa,CAAC,WAAW,CAAC;IAa7C,0EAA0E;YAC5D,QAAQ;IAgBtB,OAAO,CAAC,UAAU;IAalB;;;OAGG;IACH,MAAM,IAAI,mBAAmB;
|
|
1
|
+
{"version":3,"file":"PetSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PetSettingsCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAC7F,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAI1F,OAAO,EAAoD,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,IAAI,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAG1J,+EAA+E;AAC/E,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,qBAAqB;IACrB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,0CAA0C;AAC1C,MAAM,WAAW,oBAAqB,SAAQ,SAAS;IACrD,4BAA4B;IAC5B,OAAO,EAAE,cAAc,CAAA;IACvB,qBAAqB;IACrB,OAAO,EAAE,cAAc,CAAA;IACvB,iBAAiB;IACjB,IAAI,EAAE,cAAc,CAAA;IACpB,mBAAmB;IACnB,KAAK,EAAE,cAAc,CAAA;IACrB,oBAAoB;IACpB,MAAM,EAAE,cAAc,CAAA;IACtB,oBAAoB;IACpB,KAAK,EAAE,cAAc,CAAA;IACrB,wEAAwE;IACxE,UAAU,EAAE,SAAS;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CACxD;AAED,gEAAgE;AAChE,MAAM,WAAW,mBAAoB,SAAQ,WAAW;IACtD,KAAK,EAAE;QACL,iEAAiE;QACjE,eAAe,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAA;KACrD,CAAA;CACF;AAeD,2DAA2D;AAC3D,qBAAa,yBAAyB;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAuB;IAC5C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;IAI3D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAe;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IACtD,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,QAAQ,CAAI;IAEpB,uEAAuE;gBAC3D,KAAK,EAAE,aAAa,CAAC,WAAW,CAAC;IAa7C,0EAA0E;YAC5D,QAAQ;IAgBtB,OAAO,CAAC,UAAU;IAalB;;;OAGG;IACH,MAAM,IAAI,mBAAmB;IAI7B;;;OAGG;IACH,OAAO,IAAI,IAAI;CAGhB;AAED,0DAA0D;AAC1D,MAAM,MAAM,oBAAoB,GAC9B,WAAW,CAAC,KAAK,CAAC,GAChB,UAAU,CAAC,mBAAmB,CAAC,CAAA;AAEnC;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,+BAuF1D;AAED,8DAA8D;AAC9D,MAAM,MAAM,uBAAuB,GACjC,YAAY,CAAC,kBAAkB,CAAC,GAC9B,WAAW,CAAC,KAAK,CAAC,GAClB,UAAU,CAAC,mBAAmB,CAAC,CAAA;AAEnC,mEAAmE;AACnE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,GAAG,SAAS,CAO5E"}
|
|
@@ -71,6 +71,13 @@ export class PetSettingsCardController {
|
|
|
71
71
|
inject() {
|
|
72
72
|
return { hooks: { petSettingsCard: this.store }, ...this.form.actions() };
|
|
73
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Release the card's scope subscription and bound stores; the slot
|
|
76
|
+
* disposer calls this on teardown.
|
|
77
|
+
*/
|
|
78
|
+
dispose() {
|
|
79
|
+
this.form.dispose();
|
|
80
|
+
}
|
|
74
81
|
}
|
|
75
82
|
/**
|
|
76
83
|
* Render the pet settings card.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PetSprite.d.ts","sourceRoot":"","sources":["../../../src/client/PetSprite.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAqC,WAAW,EAAE,MAAM,OAAO,CAAA;AAG3E,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;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAGjD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAA;IAC7B,8EAA8E;IAC9E,UAAU,EAAE,aAAa,CAAA;IACzB,qDAAqD;IACrD,OAAO,EAAE,gBAAgB,CAAA;IACzB,sCAAsC;IACtC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,8BAA8B;IAC9B,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,sCAAsC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,sCAAsC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,+BAA+B;IAC/B,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,uDAAuD;IACvD,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,2DAA2D;IAC3D,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B,+CAA+C;IAC/C,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;CAC1B;AAOD;;;;;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,EAAqC,WAAW,EAAE,MAAM,OAAO,CAAA;AAG3E,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;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAGjD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAA;IAC7B,8EAA8E;IAC9E,UAAU,EAAE,aAAa,CAAA;IACzB,qDAAqD;IACrD,OAAO,EAAE,gBAAgB,CAAA;IACzB,sCAAsC;IACtC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,8BAA8B;IAC9B,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,sCAAsC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,sCAAsC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,+BAA+B;IAC/B,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,uDAAuD;IACvD,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,2DAA2D;IAC3D,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B,+CAA+C;IAC/C,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;CAC1B;AAOD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,WAAW,CAsT5D"}
|
|
@@ -174,7 +174,13 @@ export function PetSprite(props) {
|
|
|
174
174
|
const pos = dragPos ?? { right: display.right, bottom: display.bottom };
|
|
175
175
|
const spriteWidth = Math.round(cell.width * spriteScale);
|
|
176
176
|
const spriteHeight = Math.round(cell.height * spriteScale);
|
|
177
|
-
|
|
177
|
+
// Concurrent sessions each render their own bubble (stacked above the
|
|
178
|
+
// sprite); the legacy single 'bubble' is the fallback when the host serves
|
|
179
|
+
// no per-session list.
|
|
180
|
+
const sessionBubbles = snapshot?.sessions ?? [];
|
|
181
|
+
const statusBubble = feedback === null && !hovered && sessionBubbles.length === 0
|
|
182
|
+
? snapshot?.bubble
|
|
183
|
+
: undefined;
|
|
178
184
|
const displayName = snapshot?.name ?? definition.displayName;
|
|
179
185
|
const float = (_jsxs("div", { ref: floatRef, className: styles.float, style: { right: pos.right, bottom: pos.bottom, zIndex: 2147483000 }, onPointerEnter: () => {
|
|
180
186
|
clearHideTimer();
|
|
@@ -206,7 +212,7 @@ export function PetSprite(props) {
|
|
|
206
212
|
if (draggedRef.current)
|
|
207
213
|
return;
|
|
208
214
|
props.onPet();
|
|
209
|
-
}, role: "button", "aria-label": definition.displayName }), feedback !== null && (_jsx("div", { className: clsx(styles.bubble, feedback.kind === 'feed' ? styles.bubbleFeed : styles.bubblePet), children: feedback.text }, feedback.at)), statusBubble !== undefined && (_jsx("div", { className: clsx(styles.bubble, styles.bubbleStatus), role: "status", "aria-live": "polite", children: statusBubble })), hovered && dragRef.current === null && (_jsx("div", { className: styles.panel, onPointerEnter: () => {
|
|
215
|
+
}, role: "button", "aria-label": definition.displayName }), feedback !== null && (_jsx("div", { className: clsx(styles.bubble, feedback.kind === 'feed' ? styles.bubbleFeed : styles.bubblePet), children: feedback.text }, feedback.at)), feedback === null && !hovered && sessionBubbles.length > 0 && (_jsx("div", { className: styles.bubbleStack, role: "status", "aria-live": "polite", children: sessionBubbles.map(session => (_jsx("div", { className: clsx(styles.bubble, styles.bubbleStatus), children: session.bubble }, session.sessionId))) })), statusBubble !== undefined && (_jsx("div", { className: clsx(styles.bubble, styles.bubbleStatus), role: "status", "aria-live": "polite", children: statusBubble })), hovered && dragRef.current === null && (_jsx("div", { className: styles.panel, onPointerEnter: () => {
|
|
210
216
|
// Reaching the panel (or its bridge) must cancel any hide timer
|
|
211
217
|
// the container's pointerleave may have armed while the pointer
|
|
212
218
|
// crossed the sliver between the sprite and the panel.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAA;AA6D7G,yBAAyB;AACzB,eAAO,MAAM,MAAM,UAA+D,CAAA;AAElF,qEAAqE;AACrE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACxE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACrD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC7D,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AACtF,YAAY,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AACpE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf;;;;WAIG;QACH,aAAa,CAAC,EAAE;YAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;SAAE,CAAA;KAC1E;CACF;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAA;AA6D7G,yBAAyB;AACzB,eAAO,MAAM,MAAM,UAA+D,CAAA;AAElF,qEAAqE;AACrE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACxE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACrD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC7D,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AACtF,YAAY,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AACpE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf;;;;WAIG;QACH,aAAa,CAAC,EAAE;YAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;SAAE,CAAA;KAC1E;CACF;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CA0L9C"}
|
|
@@ -66,14 +66,20 @@ export function apply(ctx) {
|
|
|
66
66
|
// namespace, registered as a top-level settings page. The controller loads
|
|
67
67
|
// the petId choices from the registry endpoint itself.
|
|
68
68
|
const petSettings = new PetSettingsCardController(settingsScope);
|
|
69
|
-
ctx.slots.inject('settings.section', () =>
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
69
|
+
ctx.slots.inject('settings.section', () => {
|
|
70
|
+
const unregister = ctx.slots.register({
|
|
71
|
+
name: 'settings.section',
|
|
72
|
+
id: 'pet',
|
|
73
|
+
order: 130,
|
|
74
|
+
label: () => ctx.locale.bind('pet')('settings.title'),
|
|
75
|
+
locale: 'pet',
|
|
76
|
+
inject: () => petSettings.inject(),
|
|
77
|
+
}, PetSettingsSection);
|
|
78
|
+
return () => {
|
|
79
|
+
petSettings.dispose();
|
|
80
|
+
unregister();
|
|
81
|
+
};
|
|
82
|
+
});
|
|
77
83
|
// The global pet entry, its store, and the poll loop live while the plugin
|
|
78
84
|
// is enabled; toggling the setting off hides the pet and stops polling.
|
|
79
85
|
let disposeUi;
|
|
@@ -149,11 +149,19 @@ export declare class CardForm<T> {
|
|
|
149
149
|
private readonly specs;
|
|
150
150
|
private readonly staged;
|
|
151
151
|
private readonly listeners;
|
|
152
|
+
/** The scope subscription installed in the constructor; released by dispose(). */
|
|
153
|
+
private readonly disposeScope;
|
|
154
|
+
private disposed;
|
|
152
155
|
private saving;
|
|
153
156
|
private failed;
|
|
154
157
|
private failedReason;
|
|
155
158
|
/** @param scope - the bound settings scope for this card's namespace. */
|
|
156
159
|
constructor(scope: SettingsScope<T>, specs: FieldSpec[]);
|
|
160
|
+
/**
|
|
161
|
+
* Release the scope subscription and every bound store listener. The card
|
|
162
|
+
* must call this on teardown; later calls are no-ops.
|
|
163
|
+
*/
|
|
164
|
+
dispose(): void;
|
|
157
165
|
/** Publish a projection of this form, rebuilt whenever the scope or a draft changes. */
|
|
158
166
|
bind<S>(project: () => S): SnapshotStore<S>;
|
|
159
167
|
/** Read the card-level state: what the Host serves, and what a save would do. */
|