@linxin666/dsh-pet 0.3.6 → 0.3.10
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 +4 -0
- package/README.zh.md +4 -0
- package/lib/client.js +187 -88
- package/lib/client.js.map +1 -1
- package/lib/index.js +89 -11
- package/lib/live2d-vendor.js +106 -95
- package/lib/live2d-vendor.js.map +1 -1
- package/lib/types/announce.d.ts +53 -0
- package/lib/types/announce.d.ts.map +1 -0
- package/lib/types/announce.js +80 -0
- package/lib/types/client/PetDockEntry.d.ts +14 -5
- package/lib/types/client/PetDockEntry.d.ts.map +1 -1
- package/lib/types/client/PetDockEntry.js +6 -5
- package/lib/types/client/PetSettingsCard.d.ts +2 -1
- package/lib/types/client/PetSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PetSprite.d.ts +8 -0
- package/lib/types/client/PetSprite.d.ts.map +1 -1
- package/lib/types/client/PetSprite.js +29 -4
- package/lib/types/client/index.d.ts +8 -5
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +11 -5
- package/lib/types/client/locales.d.ts +2 -2
- package/lib/types/client/locales.js +2 -2
- package/lib/types/client/pet-store.d.ts +1 -1
- package/lib/types/client/pet-store.d.ts.map +1 -1
- package/lib/types/client/pet-store.js +1 -1
- package/lib/types/client/settings-form.d.ts +35 -43
- package/lib/types/client/settings-form.d.ts.map +1 -1
- package/lib/types/client/settings-form.js +60 -53
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +10 -9
- package/lib/types/service.d.ts +18 -0
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +22 -0
- package/package.json +17 -13
- package/src/announce.ts +103 -0
- package/src/client/PetDockEntry.test.tsx +1 -1
- package/src/client/PetDockEntry.tsx +15 -4
- package/src/client/PetSettingsCard.tsx +2 -1
- package/src/client/PetSprite.test.tsx +18 -0
- package/src/client/PetSprite.tsx +66 -3
- package/src/client/gameplay-hud.test.tsx +1 -1
- package/src/client/index.test.tsx +2 -2
- package/src/client/index.ts +17 -6
- package/src/client/locales.ts +2 -2
- package/src/client/pet-store.ts +2 -2
- package/src/client/pet.module.css +104 -0
- package/src/client/settings-form.ts +78 -92
- package/src/dsh-home.ts +1 -1
- package/src/index.ts +17 -15
- package/src/service.ts +28 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-form.d.ts","sourceRoot":"","sources":["../../../src/client/settings-form.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAyB,aAAa,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"settings-form.d.ts","sourceRoot":"","sources":["../../../src/client/settings-form.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* each plugin's client bundle; mirrors the official ui-plugin-config
|
|
8
8
|
* card-store pattern.
|
|
9
9
|
*/
|
|
10
|
-
import { createSnapshotStore } from '@deepseek-ai/dsh-client-
|
|
10
|
+
import { createSnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
11
11
|
/** A whole- or decimal-number field. An empty draft clears the field; any other draft that is not a finite number within the constraints blocks the save. */
|
|
12
12
|
export function numberField(field, constraints = {}) {
|
|
13
13
|
const { integer = false, min } = constraints;
|
|
@@ -43,8 +43,8 @@ export function textField(field) {
|
|
|
43
43
|
/**
|
|
44
44
|
* A free-text field the Host treats as a secret and redacts from the read-back
|
|
45
45
|
* (role('secret') in the section schema). The card still edits it like text,
|
|
46
|
-
* but a save never compares the redacted value back
|
|
47
|
-
*
|
|
46
|
+
* but a save never compares the redacted value back: the staged set is judged
|
|
47
|
+
* by the mutation settling (see {@link FieldSpec.secret}).
|
|
48
48
|
*/
|
|
49
49
|
export function secretField(field) {
|
|
50
50
|
return { ...textField(field), secret: true };
|
|
@@ -129,7 +129,7 @@ export class CardForm {
|
|
|
129
129
|
exposed: snapshot.status === 'ready',
|
|
130
130
|
writable: snapshot.writable,
|
|
131
131
|
dirty: plan.length > 0,
|
|
132
|
-
invalid: plan.some(item => item.
|
|
132
|
+
invalid: plan.some(item => item.judge === undefined),
|
|
133
133
|
saving: this.saving,
|
|
134
134
|
failed: this.failed,
|
|
135
135
|
...this.failedReason === undefined ? {} : { failedReason: this.failedReason },
|
|
@@ -168,22 +168,26 @@ export class CardForm {
|
|
|
168
168
|
};
|
|
169
169
|
}
|
|
170
170
|
/**
|
|
171
|
-
* Write every staged edit, then re-seed from
|
|
171
|
+
* Write every staged edit in one atomic scope mutation, then re-seed from
|
|
172
|
+
* what the Host accepted.
|
|
172
173
|
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
174
|
+
* The whole batch rides one mutate, so cross-field validate hooks
|
|
175
|
+
* (baseURL+model) judge it as a unit: the Host either applies every write
|
|
176
|
+
* or refuses the batch. The 0.1.2 scope contract never rejects a refused
|
|
177
|
+
* mutation — the scope recovers with a fresh Host view and resolves — so
|
|
178
|
+
* resolution alone proves nothing: the outcome is judged by reading the
|
|
179
|
+
* settled snapshot back, one planned write at a time, and one missed write
|
|
180
|
+
* fails the whole save. A scope that still rejects on refusal (the dsh-web
|
|
181
|
+
* bridge scope) reports through the same failure path with its rejection
|
|
182
|
+
* message. A save that did not land keeps its drafts, so the user can
|
|
183
|
+
* correct them instead of retyping.
|
|
184
|
+
* @returns settlement after the mutation and the read-back.
|
|
180
185
|
*/
|
|
181
186
|
async save() {
|
|
182
187
|
const plan = this.plan();
|
|
183
|
-
const valid = plan.filter(item => item.
|
|
188
|
+
const valid = plan.filter((item) => item.judge !== undefined);
|
|
184
189
|
if (plan.length === 0 || this.saving || valid.length !== plan.length)
|
|
185
190
|
return;
|
|
186
|
-
const plannedWrites = valid.map(item => item.op);
|
|
187
191
|
// Snapshot the staged entries this save writes, so an edit staged while it
|
|
188
192
|
// is in flight (which replaces the same key) survives: only delete the key
|
|
189
193
|
// when the entry is still the one this save started from.
|
|
@@ -194,39 +198,35 @@ export class CardForm {
|
|
|
194
198
|
this.failed = false;
|
|
195
199
|
this.failedReason = undefined;
|
|
196
200
|
this.publish();
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
else {
|
|
208
|
-
this.failedReason = result.message;
|
|
209
|
-
}
|
|
201
|
+
// One atomic namespace mutation: the 0.1.2 scope contract takes ordered
|
|
202
|
+
// path operations, so the whole staged batch is validated, persisted, and
|
|
203
|
+
// recovered together — either every write lands or none does.
|
|
204
|
+
const ops = valid.map(item => item.op.op === 'set'
|
|
205
|
+
? { op: 'set', path: [item.field], value: item.op.value }
|
|
206
|
+
: { op: 'unset', path: [item.field] });
|
|
207
|
+
let failedReason;
|
|
208
|
+
try {
|
|
209
|
+
await this.scope.mutate(ops);
|
|
210
210
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if (await item.run())
|
|
214
|
-
landed.add(item.field);
|
|
215
|
-
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
failedReason = error instanceof Error ? error.message : String(error);
|
|
216
213
|
}
|
|
214
|
+
// The 0.1.2 scope resolves even a refused mutation (it recovers with a
|
|
215
|
+
// fresh view instead of throwing), so resolution alone proves nothing:
|
|
216
|
+
// judge every planned write against the settled snapshot. The mutation is
|
|
217
|
+
// atomic, so one missed write fails the whole save and keeps the drafts.
|
|
218
|
+
const landed = failedReason === undefined && valid.every(item => item.judge());
|
|
217
219
|
for (const [field, before] of pending) {
|
|
218
|
-
if (landed
|
|
220
|
+
if (landed && this.staged.get(field) === before)
|
|
219
221
|
this.staged.delete(field);
|
|
220
222
|
}
|
|
221
223
|
this.saving = false;
|
|
222
|
-
this.failed = landed
|
|
224
|
+
this.failed = !landed;
|
|
225
|
+
// A read-back failure carries no server reason: the card surfaces its
|
|
226
|
+
// generic failure copy; a rejecting scope (the bridge) adds its message.
|
|
227
|
+
this.failedReason = failedReason;
|
|
223
228
|
this.publish();
|
|
224
229
|
}
|
|
225
|
-
/** The scope's batch surface when it supports one; undefined conservatively otherwise. */
|
|
226
|
-
batchedScope() {
|
|
227
|
-
const candidate = this.scope;
|
|
228
|
-
return typeof candidate?.mutate === 'function' ? candidate : undefined;
|
|
229
|
-
}
|
|
230
230
|
/**
|
|
231
231
|
* Every staged edit a save would write. An entry whose draft is not a value
|
|
232
232
|
* its field accepts carries no write: the form is still dirty, and the save
|
|
@@ -240,35 +240,42 @@ export class CardForm {
|
|
|
240
240
|
const spec = this.specOf(field);
|
|
241
241
|
if (staged.clear) {
|
|
242
242
|
if (this.stored(field))
|
|
243
|
-
plan.push({ field, op: { field, op: 'unset' },
|
|
243
|
+
plan.push({ field, op: { field, op: 'unset' }, judge: () => this.landedUnset(field) });
|
|
244
244
|
continue;
|
|
245
245
|
}
|
|
246
246
|
if (staged.text === spec.format(this.sectionValue(field)))
|
|
247
247
|
continue;
|
|
248
248
|
const write = spec.parse(staged.text);
|
|
249
249
|
if (write === undefined)
|
|
250
|
-
plan.push({ field, op: { field, op: 'unset' },
|
|
250
|
+
plan.push({ field, op: { field, op: 'unset' }, judge: undefined });
|
|
251
251
|
else if (write.kind === 'clear')
|
|
252
|
-
plan.push({ field, op: { field, op: 'unset' },
|
|
252
|
+
plan.push({ field, op: { field, op: 'unset' }, judge: () => this.landedUnset(field) });
|
|
253
253
|
else
|
|
254
|
-
plan.push({ field, op: { field, op: 'set', value: write.value },
|
|
254
|
+
plan.push({ field, op: { field, op: 'set', value: write.value }, judge: () => this.landedSet(field, write.value) });
|
|
255
255
|
}
|
|
256
256
|
return plan;
|
|
257
257
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
//
|
|
265
|
-
//
|
|
266
|
-
//
|
|
267
|
-
//
|
|
258
|
+
/**
|
|
259
|
+
* Read-back judgment for a planned set: the user layer must hold the
|
|
260
|
+
* intended value once the mutation has settled.
|
|
261
|
+
*/
|
|
262
|
+
landedSet(field, value) {
|
|
263
|
+
// A redacted secret never appears in any wire view layer: the Host strips
|
|
264
|
+
// role('secret') fields and reports them through a sidecar the scope
|
|
265
|
+
// snapshot does not expose, so there is nothing to compare the draft
|
|
266
|
+
// against. Settling is the only signal the form has; the rest of the
|
|
267
|
+
// batch, when one exists, still carries the atomic verdict by read-back.
|
|
268
268
|
if (this.specOf(field).secret)
|
|
269
269
|
return true;
|
|
270
270
|
return this.userLayer()?.[field] === value;
|
|
271
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* Read-back judgment for a planned unset: the field must be gone from the
|
|
274
|
+
* user layer once the mutation has settled.
|
|
275
|
+
*/
|
|
276
|
+
landedUnset(field) {
|
|
277
|
+
return !this.stored(field);
|
|
278
|
+
}
|
|
272
279
|
stage(field, edit) {
|
|
273
280
|
this.staged.set(field, edit);
|
|
274
281
|
this.failed = false;
|
package/lib/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAG7C,OAAO,CAAC,MAAM,aAAa,CAAA;AAC3B,OAAO,EAAsC,KAAK,SAAS,EAA2B,MAAM,cAAc,CAAA;AAM1G,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAC9D,YAAY,EACV,SAAS,EACT,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EACd,YAAY,GACb,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,MAAM,GACP,MAAM,eAAe,CAAA;AACtB,YAAY,EACV,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,cAAc,GACf,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,KAAK,GACN,MAAM,YAAY,CAAA;AACnB,YAAY,EACV,aAAa,EACb,YAAY,EACZ,cAAc,EACd,aAAa,EACb,gBAAgB,GACjB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC5E,OAAO,EACL,eAAe,EACf,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,mBAAmB,GACpB,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,UAAU,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9E,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,cAAc,EACd,UAAU,EACV,cAAc,GACf,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAChE,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,EACtB,aAAa,EACb,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,cAAc,EACd,kBAAkB,GACnB,MAAM,eAAe,CAAA;AACtB,YAAY,EACV,aAAa,EACb,QAAQ,EACR,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,WAAW,EACX,gBAAgB,GACjB,MAAM,eAAe,CAAA;AAEtB,OAAO,EACL,aAAa,EACb,cAAc,EACd,gBAAgB,GACjB,MAAM,aAAa,CAAA;AAEpB,sEAAsE;AACtE,eAAO,MAAM,IAAI,QAAQ,CAAA;AAEzB,+DAA+D;AAC/D,eAAO,MAAM,MAAM,UAAgB,CAAA;AAEnC;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM;;;;;;;;;;;;;;;;IAU1D;AAED,0EAA0E;AAC1E,eAAO,MAAM,KAAK,kBAA6C,CAAA;AAE/D,iBAAS,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,SAAc,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAG7C,OAAO,CAAC,MAAM,aAAa,CAAA;AAC3B,OAAO,EAAsC,KAAK,SAAS,EAA2B,MAAM,cAAc,CAAA;AAM1G,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAC9D,YAAY,EACV,SAAS,EACT,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EACd,YAAY,GACb,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,MAAM,GACP,MAAM,eAAe,CAAA;AACtB,YAAY,EACV,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,cAAc,GACf,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,KAAK,GACN,MAAM,YAAY,CAAA;AACnB,YAAY,EACV,aAAa,EACb,YAAY,EACZ,cAAc,EACd,aAAa,EACb,gBAAgB,GACjB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC5E,OAAO,EACL,eAAe,EACf,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,mBAAmB,GACpB,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,UAAU,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9E,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,cAAc,EACd,UAAU,EACV,cAAc,GACf,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAChE,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,EACtB,aAAa,EACb,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,cAAc,EACd,kBAAkB,GACnB,MAAM,eAAe,CAAA;AACtB,YAAY,EACV,aAAa,EACb,QAAQ,EACR,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,WAAW,EACX,gBAAgB,GACjB,MAAM,eAAe,CAAA;AAEtB,OAAO,EACL,aAAa,EACb,cAAc,EACd,gBAAgB,GACjB,MAAM,aAAa,CAAA;AAEpB,sEAAsE;AACtE,eAAO,MAAM,IAAI,QAAQ,CAAA;AAEzB,+DAA+D;AAC/D,eAAO,MAAM,MAAM,UAAgB,CAAA;AAEnC;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM;;;;;;;;;;;;;;;;IAU1D;AAED,0EAA0E;AAC1E,eAAO,MAAM,KAAK,kBAA6C,CAAA;AAE/D,iBAAS,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,SAAc,GAAG,IAAI,CAkE7D"}
|
package/lib/types/index.js
CHANGED
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
* cordis.patch.yml inserts this plugin row.
|
|
11
11
|
* @module @linxin666/dsh-pet
|
|
12
12
|
*/
|
|
13
|
-
import { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings';
|
|
14
13
|
import z from 'schemastery';
|
|
15
14
|
import { PetService, PET_SETTINGS_NAMESPACE } from "./service.js";
|
|
16
15
|
import { makePetRoutes } from "./routes.js";
|
|
@@ -96,14 +95,16 @@ function applyImpl(ctx, config = {}) {
|
|
|
96
95
|
disposeRoutes = undefined;
|
|
97
96
|
}
|
|
98
97
|
};
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
98
|
+
ctx.inject(['settings'], (settingsCtx) => {
|
|
99
|
+
settingsCtx.settings.installSection(ctx, PET_SETTINGS_NAMESPACE, makePetSettingsSchema(service.selectedPetId()), base, {
|
|
100
|
+
setSource: (source) => { current = source; },
|
|
101
|
+
onChange: () => {
|
|
102
|
+
const section = current();
|
|
103
|
+
service.applySettingsSection(section);
|
|
104
|
+
service.setEnabled(section.enabled ?? true);
|
|
105
|
+
syncRoutes();
|
|
106
|
+
},
|
|
107
|
+
});
|
|
107
108
|
});
|
|
108
109
|
syncRoutes();
|
|
109
110
|
}
|
package/lib/types/service.d.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { Context, Service } from '@deepseek-ai/cordis';
|
|
15
15
|
import type { AffinityConfig, PetAffinityView, PetInteraction } from './affinity.ts';
|
|
16
|
+
import { type PetAnnouncement } from './announce.ts';
|
|
16
17
|
import type { TreatConfig } from './treats.ts';
|
|
17
18
|
import { type LedgerInteractionResult } from './ledger.ts';
|
|
18
19
|
import { type PetDisplayConfig } from './persist.ts';
|
|
@@ -135,6 +136,12 @@ export interface PetStateView {
|
|
|
135
136
|
* settles the view in memory but never writes pet.json.
|
|
136
137
|
*/
|
|
137
138
|
gameplay?: PetGameplayStateView;
|
|
139
|
+
/**
|
|
140
|
+
* The freshest plugin-authored announcement (dsh-usage linkage), within
|
|
141
|
+
* its TTL; absent when none is fresh. Rendered as a dedicated styled
|
|
142
|
+
* bubble above the session stack.
|
|
143
|
+
*/
|
|
144
|
+
announcement?: PetAnnouncement;
|
|
138
145
|
}
|
|
139
146
|
/** Result of `pet.interact`. */
|
|
140
147
|
export type PetInteractResult = LedgerInteractionResult;
|
|
@@ -183,6 +190,8 @@ export declare class PetService extends Service {
|
|
|
183
190
|
private enabled;
|
|
184
191
|
/** Status-decoration master switch (M5, #567); mirrored from settings. */
|
|
185
192
|
private decorationEnabled;
|
|
193
|
+
/** The freshest plugin-authored announcement (dsh-usage linkage). */
|
|
194
|
+
private announcement;
|
|
186
195
|
private disposeActivity;
|
|
187
196
|
/** Session whose most recent meaningful event currently drives the global pet. */
|
|
188
197
|
private displaySession;
|
|
@@ -215,6 +224,15 @@ export declare class PetService extends Service {
|
|
|
215
224
|
isEnabled(): boolean;
|
|
216
225
|
/** RPC: current pet state snapshot. */
|
|
217
226
|
state(currentSessionId?: string): Promise<PetStateView>;
|
|
227
|
+
/**
|
|
228
|
+
* RPC: one plugin-authored announcement bubble (dsh-usage linkage). The
|
|
229
|
+
* payload is validated into a bounded PetAnnouncement; a malformed one is
|
|
230
|
+
* dropped silently — a sibling plugin's bug must never surface as pet
|
|
231
|
+
* breakage. The announcement is in-memory only.
|
|
232
|
+
*/
|
|
233
|
+
announce(input: unknown): {
|
|
234
|
+
ok: boolean;
|
|
235
|
+
};
|
|
218
236
|
/** Current persisted display config (read-only view). */
|
|
219
237
|
display(): PetDisplayConfig;
|
|
220
238
|
/** RPC: the registry entries the browser half renders and selects from. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/service.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AACpF,OAAO,EAAwC,KAAK,eAAe,EAAE,MAAM,eAAe,CAAA;AAC1F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAQ9C,OAAO,EAAgC,KAAK,uBAAuB,EAAE,MAAM,aAAa,CAAA;AACxF,OAAO,EASL,KAAK,gBAAgB,EACtB,MAAM,cAAc,CAAA;AACrB,OAAO,EAML,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,qBAAqB,EAC3B,MAAM,eAAe,CAAA;AAGtB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAA;AACtE,OAAO,EAGL,KAAK,cAAc,EAEnB,KAAK,gBAAgB,EACtB,MAAM,YAAY,CAAA;AAenB,4BAA4B;AAC5B,MAAM,WAAW,SAAS;IACxB,uBAAuB;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAA;IAClC,4BAA4B;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAA;IAC/B,4BAA4B;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IAC7B,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,6EAA6E;IAC7E,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,WAAW,CAAA;IACtB,oEAAoE;IACpE,IAAI,CAAC,EAAE,SAAS,WAAW,EAAE,CAAA;CAC9B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qBAAqB;IACrB,OAAO,EAAE,OAAO,CAAA;IAChB,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAA;IACZ,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAA;IACb,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED,2HAA2H;AAC3H,eAAO,MAAM,sBAAsB,QAAQ,CAAA;AAE3C;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAA;IACjB,uDAAuD;IACvD,SAAS,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAA;IACxC,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAA;IACd,yCAAyC;IACzC,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAA;IAChC,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,gFAAgF;AAChF,eAAO,MAAM,mBAAmB,KAAK,CAAA;AAErC,wCAAwC;AACxC,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAA;IACxC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAA;IAChC,aAAa,EAAE,OAAO,CAAA;IACtB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAA;IAC3B,gCAAgC;IAChC,QAAQ,EAAE,eAAe,CAAA;IACzB,6BAA6B;IAC7B,OAAO,EAAE,gBAAgB,CAAA;IACzB,4CAA4C;IAC5C,GAAG,EAAE;QACH,mBAAmB;QACnB,EAAE,EAAE,MAAM,CAAA;QACV,iDAAiD;QACjD,WAAW,EAAE,MAAM,CAAA;QACnB,4BAA4B;QAC5B,WAAW,EAAE,MAAM,CAAA;KACpB,CAAA;IACD,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAA;IACZ,kCAAkC;IAClC,MAAM,EAAE;QACN,0BAA0B;QAC1B,OAAO,EAAE,MAAM,CAAA;QACf,iBAAiB;QACjB,GAAG,EAAE,MAAM,CAAA;KACZ,CAAA;IACD;;;;OAIG;IACH,UAAU,CAAC,EAAE,cAAc,CAAA;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,oBAAoB,CAAA;IAC/B;;;;OAIG;IACH,YAAY,CAAC,EAAE,eAAe,CAAA;CAC/B;AAED,gCAAgC;AAChC,MAAM,MAAM,iBAAiB,GAAG,uBAAuB,CAAA;AAEvD,+FAA+F;AAC/F,MAAM,WAAW,oBAAoB;IACnC,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAAA;CAC9B;AAED,uEAAuE;AACvE,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,OAAO,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,0DAA0D;IAC1D,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,yBAAyB;IACzB,OAAO,CAAC,EAAE,SAAS,GAAG,MAAM,CAAA;IAC5B,mEAAmE;IACnE,KAAK,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;IAC5C,IAAI,CAAC,EAAE,oBAAoB,CAAA;CAC5B;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf,GAAG,EAAE,UAAU,CAAA;KAChB;CACF;AAgBD;;;;;GAKG;AACH,qBAAa,UAAW,SAAQ,OAAO;IACrC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAK;IAE5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAgB;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAW;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAa;IACtC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,OAAO,CAAS;IACxB,0EAA0E;IAC1E,OAAO,CAAC,iBAAiB,CAAS;IAClC,qEAAqE;IACrE,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,eAAe,CAA0B;IACjD,kFAAkF;IAClF,OAAO,CAAC,cAAc,CAAqB;IAC3C;;;;OAIG;IACH,OAAO,CAAC,UAAU,CAA8D;IAChF;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAsC;IACtE;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAyB;gBAEnD,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,SAAc;IAkChD;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAYlB,uEAAuE;IACvE,SAAS,IAAI,OAAO;IAIpB,uCAAuC;IACjC,KAAK,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAI7D;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE;IAOzC,yDAAyD;IACzD,OAAO,IAAI,gBAAgB;IAI3B,2EAA2E;IACrE,IAAI,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IAItC,gEAAgE;IAChE,gBAAgB,IAAI,WAAW;IAI/B,wEAAwE;IAClE,WAAW,IAAI,OAAO,CAAC;QAAE,WAAW,EAAE,qBAAqB,EAAE,CAAA;KAAE,CAAC;IAItE;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAMxB,yCAAyC;IACzC,WAAW,IAAI,WAAW,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;IAI1D,6CAA6C;IAC7C,aAAa,IAAI,MAAM;IAIvB,yEAAyE;IACzE,OAAO,CAAC,KAAK,GAAE,MAA6B,GAAG,MAAM;IAMrD,4EAA4E;IACtE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAWlG,uEAAuE;IACvE,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAMlC,OAAO,CAAC,YAAY;IA8DpB,uGAAuG;IACvG,OAAO,CAAC,aAAa;IAMrB,0EAA0E;IAC1E,OAAO,CAAC,UAAU;IAclB;;;;OAIG;IACH,OAAO,CAAC,aAAa;IAqBrB,gCAAgC;IAC1B,QAAQ,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAahE,2DAA2D;IAC3D,OAAO,CAAC,WAAW;IAInB,mEAAmE;IACnE,OAAO,CAAC,aAAa;IAWrB,+FAA+F;IAC/F,OAAO,CAAC,cAAc;IAMtB;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAM3B,2DAA2D;IAC3D,OAAO,CAAC,cAAc;IAKtB;;;OAGG;IACG,aAAa,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAuClE,qEAAqE;IAC/D,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAcpF,yEAAyE;IACnE,gBAAgB,IAAI,OAAO,CAAC,qBAAqB,CAAC;IAexD,0EAA0E;IACpE,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAiCjE,iCAAiC;IAC3B,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,OAAO,EAAE,gBAAgB,CAAA;KAAE,CAAC;IAOpF,wFAAwF;IAClF,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,OAAO,EAAE,gBAAgB,CAAA;KAAE,CAAC;IAWnG,2EAA2E;IACrE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAS/F;;;;;OAKG;IACH,oBAAoB,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI;IAoBvD,oFAAoF;IACpF,OAAO,CAAC,mBAAmB;IAe3B,qFAAqF;IACrF,OAAO,CAAC,UAAU;IAIlB,8EAA8E;IAC9E,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,IAAI;IAgFZ,OAAO,CAAC,KAAK;CAOd"}
|
package/lib/types/service.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* @module @linxin666/dsh-pet/service
|
|
13
13
|
*/
|
|
14
14
|
import { Service } from '@deepseek-ai/cordis';
|
|
15
|
+
import { announcementFresh, parseAnnouncement } from "./announce.js";
|
|
15
16
|
import { emptyProjectionRuntime, isActivityPhase, projectOfficialEvent, } from "./event-projection.js";
|
|
16
17
|
import { PetLedger } from "./ledger.js";
|
|
17
18
|
import { DEFAULT_PET_NAME, DISPLAY_INSET_MAX, DISPLAY_SIZE_MAX, DISPLAY_SIZE_MIN, PET_NAME_MAX_LENGTH, loadPetPersist, petHomeDir, savePetPersist, } from "./persist.js";
|
|
@@ -42,6 +43,8 @@ export class PetService extends Service {
|
|
|
42
43
|
enabled;
|
|
43
44
|
/** Status-decoration master switch (M5, #567); mirrored from settings. */
|
|
44
45
|
decorationEnabled;
|
|
46
|
+
/** The freshest plugin-authored announcement (dsh-usage linkage). */
|
|
47
|
+
announcement;
|
|
45
48
|
disposeActivity;
|
|
46
49
|
/** Session whose most recent meaningful event currently drives the global pet. */
|
|
47
50
|
displaySession;
|
|
@@ -119,6 +122,19 @@ export class PetService extends Service {
|
|
|
119
122
|
async state(currentSessionId) {
|
|
120
123
|
return this.view(currentSessionId);
|
|
121
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* RPC: one plugin-authored announcement bubble (dsh-usage linkage). The
|
|
127
|
+
* payload is validated into a bounded PetAnnouncement; a malformed one is
|
|
128
|
+
* dropped silently — a sibling plugin's bug must never surface as pet
|
|
129
|
+
* breakage. The announcement is in-memory only.
|
|
130
|
+
*/
|
|
131
|
+
announce(input) {
|
|
132
|
+
const parsed = parseAnnouncement(input, Date.now());
|
|
133
|
+
if (parsed === undefined)
|
|
134
|
+
return { ok: false };
|
|
135
|
+
this.announcement = parsed;
|
|
136
|
+
return { ok: true };
|
|
137
|
+
}
|
|
122
138
|
/** Current persisted display config (read-only view). */
|
|
123
139
|
display() {
|
|
124
140
|
return { ...this.ledger.snapshot.display };
|
|
@@ -605,6 +621,11 @@ export class PetService extends Service {
|
|
|
605
621
|
}
|
|
606
622
|
// Read-only: the ledger settles on economic events only, never on a read,
|
|
607
623
|
// so polling the state cannot trigger pet.json writes.
|
|
624
|
+
// An expired announcement simply stops appearing (the client's 2 s poll
|
|
625
|
+
// drops it); no timer owns its removal.
|
|
626
|
+
const announcement = this.announcement !== undefined && announcementFresh(this.announcement, Date.now())
|
|
627
|
+
? this.announcement
|
|
628
|
+
: undefined;
|
|
608
629
|
return {
|
|
609
630
|
animation: snapshot.animation,
|
|
610
631
|
...(snapshot.bubble === undefined ? {} : { bubble: snapshot.bubble }),
|
|
@@ -612,6 +633,7 @@ export class PetService extends Service {
|
|
|
612
633
|
sessionActive: snapshot.sessionActive,
|
|
613
634
|
sessions,
|
|
614
635
|
...(decoration === undefined ? {} : { decoration }),
|
|
636
|
+
...(announcement === undefined ? {} : { announcement }),
|
|
615
637
|
affinity: this.ledger.affinityView(Date.now()),
|
|
616
638
|
display: { ...this.ledger.snapshot.display },
|
|
617
639
|
pet: {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linxin666/dsh-pet",
|
|
3
3
|
"description": "Multi-pet companion plugin for the dsh web GUI: a registry-driven floating pet that reacts to model activity, with per-pet naming, petting/feeding interactions and an affinity score",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.10",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "^22.19.0 || >=24.0.0"
|
|
@@ -40,17 +40,17 @@
|
|
|
40
40
|
"license": "Apache-2.0",
|
|
41
41
|
"dsh": {
|
|
42
42
|
"engines": {
|
|
43
|
-
"dsh": ">=0.1.
|
|
43
|
+
"dsh": ">=0.1.2-alpha.1"
|
|
44
44
|
},
|
|
45
45
|
"bundle": {
|
|
46
46
|
"patch": "./cordis.patch.yml"
|
|
47
47
|
},
|
|
48
48
|
"client": {
|
|
49
49
|
"inject": [
|
|
50
|
-
"@deepseek-ai/dsh-client-runtime",
|
|
51
50
|
"@deepseek-ai/dsh-client-connection",
|
|
52
51
|
"@deepseek-ai/dsh-client-ui-settings",
|
|
53
|
-
"@deepseek-ai/dsh-client-ui-conversation"
|
|
52
|
+
"@deepseek-ai/dsh-client-ui-conversation",
|
|
53
|
+
"@deepseek-ai/dsh-api-session-controller"
|
|
54
54
|
],
|
|
55
55
|
"platform": "web"
|
|
56
56
|
}
|
|
@@ -64,15 +64,19 @@
|
|
|
64
64
|
"react-dom": "^18.2.0"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
68
|
-
"@deepseek-ai/dsh-
|
|
69
|
-
"@deepseek-ai/dsh-
|
|
70
|
-
"@deepseek-ai/dsh-
|
|
71
|
-
"@deepseek-ai/dsh-client-
|
|
72
|
-
"@deepseek-ai/dsh-client-
|
|
73
|
-
"@deepseek-ai/dsh-
|
|
74
|
-
"@deepseek-ai/dsh-
|
|
75
|
-
"@deepseek-ai/dsh-settings": "^0.1.
|
|
67
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
68
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.2",
|
|
69
|
+
"@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.2",
|
|
70
|
+
"@deepseek-ai/dsh-api-workspace-controller": "^0.1.2-alpha.2",
|
|
71
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.2",
|
|
72
|
+
"@deepseek-ai/dsh-client-store": "^0.1.2-alpha.2",
|
|
73
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.2",
|
|
74
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.2",
|
|
75
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.2",
|
|
76
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.2-alpha.2",
|
|
77
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.2",
|
|
78
|
+
"@deepseek-ai/dsh-session": "^0.1.2-alpha.2",
|
|
79
|
+
"@deepseek-ai/dsh-settings": "^0.1.2-alpha.2",
|
|
76
80
|
"@testing-library/dom": "^10.4.1",
|
|
77
81
|
"@testing-library/react": "^16.3.2",
|
|
78
82
|
"@types/node": "^22.20.0",
|
package/src/announce.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pet announcement-bubble contract (dsh-usage linkage): a sibling plugin
|
|
3
|
+
* pushes one structured announcement through `pet.announce(...)`, the host
|
|
4
|
+
* validates it into a bounded payload, and the browser half renders it as a
|
|
5
|
+
* dedicated, specially styled bubble above the session bubble stack.
|
|
6
|
+
*
|
|
7
|
+
* The validation lives in this pure module so the wire contract has exactly
|
|
8
|
+
* one home and stays testable without the cordis service.
|
|
9
|
+
* @module @linxin666/dsh-pet/announce
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** One plugin-authored announcement bubble. */
|
|
13
|
+
export interface PetAnnouncement {
|
|
14
|
+
/** Authoring plugin's source tag (`dsh-usage` for the usage statistics). */
|
|
15
|
+
source: string
|
|
16
|
+
/** Bubble content kind: a spend estimate, an account balance, or a plan-quota status. */
|
|
17
|
+
kind: 'balance' | 'cost' | 'plan'
|
|
18
|
+
/** Lead text (usually the provider display name). */
|
|
19
|
+
title: string
|
|
20
|
+
/** Balance or today-spend amount, formatted for display (kinds `balance` and `cost`). */
|
|
21
|
+
amount?: string
|
|
22
|
+
/** Plan usage percent 0-100 (kind `plan`). */
|
|
23
|
+
percent?: number
|
|
24
|
+
/** ISO 8601 reset instant (kind `plan`). */
|
|
25
|
+
resetAt?: string
|
|
26
|
+
/** Short trailing note (plan tier name, peak-period status, currency code, ...). */
|
|
27
|
+
note?: string
|
|
28
|
+
/** Visual tone; drives the bubble's accent color. */
|
|
29
|
+
tone: 'ok' | 'warn' | 'low'
|
|
30
|
+
/** Freshness window in ms; an expired announcement stops rendering. */
|
|
31
|
+
ttlMs: number
|
|
32
|
+
/** Epoch ms the announcement arrived. */
|
|
33
|
+
at: number
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Default freshness window. */
|
|
37
|
+
export const ANNOUNCE_DEFAULT_TTL_MS = 10_000
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Hard TTL ceiling. A repeating announcer (dsh-usage) declares its poll
|
|
41
|
+
* cadence as the TTL, so an `always`-mode bubble stays continuous across
|
|
42
|
+
* polls; the ceiling means a source that dies unmounts its bubble within at
|
|
43
|
+
* most one missed refresh cycle rather than lingering forever.
|
|
44
|
+
*/
|
|
45
|
+
export const ANNOUNCE_MAX_TTL_MS = 7_200_000
|
|
46
|
+
|
|
47
|
+
/** Hard bounds: lengths and the TTL range (1 s .. 60 s). */
|
|
48
|
+
const TITLE_MAX = 80
|
|
49
|
+
const TEXT_MAX = 120
|
|
50
|
+
const SOURCE_MAX = 64
|
|
51
|
+
|
|
52
|
+
function boundedString(value: unknown, max: number): string | undefined {
|
|
53
|
+
if (typeof value !== 'string') return undefined
|
|
54
|
+
const trimmed = value.trim()
|
|
55
|
+
if (trimmed === '') return undefined
|
|
56
|
+
return trimmed.length <= max ? trimmed : trimmed.slice(0, max)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Validate one announce payload. Unknown fields are dropped, oversized text
|
|
61
|
+
* is truncated, and anything structurally wrong resolves to undefined — a
|
|
62
|
+
* malformed announcement never reaches the pet's bubble surface.
|
|
63
|
+
* @param input - the payload a sibling plugin passed to `pet.announce`.
|
|
64
|
+
* @param now - epoch ms the announcement arrived.
|
|
65
|
+
*/
|
|
66
|
+
export function parseAnnouncement(input: unknown, now: number): PetAnnouncement | undefined {
|
|
67
|
+
if (typeof input !== 'object' || input === null) return undefined
|
|
68
|
+
const data = input as Record<string, unknown>
|
|
69
|
+
const source = boundedString(data.source, SOURCE_MAX)
|
|
70
|
+
const title = boundedString(data.title, TITLE_MAX)
|
|
71
|
+
if (source === undefined || title === undefined) return undefined
|
|
72
|
+
const kind = data.kind === 'balance' || data.kind === 'cost' || data.kind === 'plan' ? data.kind : undefined
|
|
73
|
+
if (kind === undefined) return undefined
|
|
74
|
+
const amount = boundedString(data.amount, TEXT_MAX)
|
|
75
|
+
const resetAt = boundedString(data.resetAt, TEXT_MAX)
|
|
76
|
+
const note = boundedString(data.note, TITLE_MAX)
|
|
77
|
+
const percent = typeof data.percent === 'number' && Number.isFinite(data.percent)
|
|
78
|
+
? Math.max(0, Math.min(100, data.percent))
|
|
79
|
+
: undefined
|
|
80
|
+
if ((kind === 'balance' || kind === 'cost') && amount === undefined) return undefined
|
|
81
|
+
if (kind === 'plan' && percent === undefined) return undefined
|
|
82
|
+
const tone = data.tone === 'warn' || data.tone === 'low' ? data.tone : 'ok'
|
|
83
|
+
const ttlMs = typeof data.ttlMs === 'number' && Number.isFinite(data.ttlMs)
|
|
84
|
+
? Math.max(1000, Math.min(ANNOUNCE_MAX_TTL_MS, data.ttlMs))
|
|
85
|
+
: ANNOUNCE_DEFAULT_TTL_MS
|
|
86
|
+
return {
|
|
87
|
+
source,
|
|
88
|
+
kind,
|
|
89
|
+
title,
|
|
90
|
+
...(amount !== undefined ? { amount } : {}),
|
|
91
|
+
...(percent !== undefined ? { percent } : {}),
|
|
92
|
+
...(resetAt !== undefined ? { resetAt } : {}),
|
|
93
|
+
...(note !== undefined ? { note } : {}),
|
|
94
|
+
tone,
|
|
95
|
+
ttlMs,
|
|
96
|
+
at: now,
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Whether an announcement is still fresh at `now`. */
|
|
101
|
+
export function announcementFresh(announcement: PetAnnouncement, now: number): boolean {
|
|
102
|
+
return now - announcement.at < announcement.ttlMs
|
|
103
|
+
}
|
|
@@ -9,7 +9,7 @@ import { cleanup, render, screen } from '@testing-library/react'
|
|
|
9
9
|
// The npm SDK's client half is a closure-factory bundle for the GUI's
|
|
10
10
|
// __ModuleLoader__ (not importable under vitest); provide the defineStore
|
|
11
11
|
// the pet store needs (same fake-store pattern as the settings-card tests).
|
|
12
|
-
vi.mock('@deepseek-ai/dsh-client-
|
|
12
|
+
vi.mock('@deepseek-ai/dsh-client-store', () => ({
|
|
13
13
|
defineStore: (spec: {
|
|
14
14
|
init: () => unknown
|
|
15
15
|
actions: Record<string, (draft: never, ...args: never[]) => void>
|
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
* it must not ride a session-scoped slot — on the new-conversation screen no
|
|
5
5
|
* session exists to scope a slot by, and the pet would vanish (issue #48).
|
|
6
6
|
* The client half therefore mounts this entry straight onto 'document.body'
|
|
7
|
-
* (see index.ts): while visible it renders the floating PetSprite (a
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* the registry list — no per-pet
|
|
7
|
+
* (see index.ts): while visible it renders the floating PetSprite (a portal
|
|
8
|
+
* into the plugin root, so the root owns the whole surface), while hidden it
|
|
9
|
+
* renders a fixed-position summon button. Which sprite renders is decided by
|
|
10
|
+
* the host snapshot's pet id resolved against the registry list — no per-pet
|
|
11
|
+
* component exists.
|
|
11
12
|
* @module @linxin666/dsh-pet/client/PetDockEntry
|
|
12
13
|
*/
|
|
13
14
|
|
|
@@ -52,6 +53,15 @@ export interface PetInjected {
|
|
|
52
53
|
export type PetDockEntryProps =
|
|
53
54
|
PetInjected
|
|
54
55
|
& PropsLocale<typeof NS>
|
|
56
|
+
& {
|
|
57
|
+
/**
|
|
58
|
+
* DOM node the floating sprite chrome portals into. Defaults to
|
|
59
|
+
* document.body; the plugin apply passes its [data-dsh-plugin="pet"]
|
|
60
|
+
* root so root-keyed suppressors (the portrait mobile layer) and skins
|
|
61
|
+
* own the whole pet surface as one unit.
|
|
62
|
+
*/
|
|
63
|
+
portalTarget?: Element
|
|
64
|
+
}
|
|
55
65
|
|
|
56
66
|
const DEFAULT_DISPLAY: PetDisplayConfig = { visible: true, size: 160, right: 24, bottom: 20 }
|
|
57
67
|
|
|
@@ -108,6 +118,7 @@ export function PetDockEntry(props: PetDockEntryProps): ReactElement {
|
|
|
108
118
|
onRename={props.rename}
|
|
109
119
|
onOpenSession={props.openSession}
|
|
110
120
|
onFeedbackDone={props.feedbackDone}
|
|
121
|
+
portalTarget={props.portalTarget}
|
|
111
122
|
dragDisabled={snapshot.gameplay?.mode === 'work'}
|
|
112
123
|
{...(gameplay === undefined || aux === null
|
|
113
124
|
? {}
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
import type { ReactNode } from 'react'
|
|
11
11
|
import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
|
|
12
|
-
import type { SettingsScope
|
|
12
|
+
import type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client'
|
|
13
|
+
import type { SnapshotStore } from '@deepseek-ai/dsh-client-store'
|
|
13
14
|
// Type-only: pulls the settings-surface SlotMap merge (the 'settings.section' entry).
|
|
14
15
|
import type {} from '@deepseek-ai/dsh-client-ui-settings/client'
|
|
15
16
|
import { PluginSettingsCard, ValueField, BooleanField, ChoiceField } from './PluginSettingsCard.tsx'
|
|
@@ -1122,3 +1122,21 @@ describe('PetSprite status decoration (pet-center M5, #567)', () => {
|
|
|
1122
1122
|
expect(timerSpy.mock.calls.length).toBe(schedulesBefore + 1)
|
|
1123
1123
|
})
|
|
1124
1124
|
})
|
|
1125
|
+
|
|
1126
|
+
|
|
1127
|
+
describe('PetSprite portal target', () => {
|
|
1128
|
+
it('portals the float into the provided target instead of document.body', () => {
|
|
1129
|
+
const host = document.createElement('div')
|
|
1130
|
+
document.body.appendChild(host)
|
|
1131
|
+
renderPet({ portalTarget: host })
|
|
1132
|
+
// The float (sprite chrome) lands inside the owning root, so a
|
|
1133
|
+
// root-keyed suppressor (the portrait mobile layer) hides it as one unit.
|
|
1134
|
+
expect(host.querySelector('[role="button"]')).not.toBeNull()
|
|
1135
|
+
})
|
|
1136
|
+
|
|
1137
|
+
it('portals the float into document.body when no target is given', () => {
|
|
1138
|
+
renderPet()
|
|
1139
|
+
expect(document.body.querySelector('[role="button"]')).not.toBeNull()
|
|
1140
|
+
})
|
|
1141
|
+
})
|
|
1142
|
+
|