@linxin666/dsh-pet 0.3.23 → 0.3.25
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 +5 -4
- package/README.zh.md +5 -4
- package/icon.svg +3 -0
- package/lib/client.js +522 -194
- package/lib/client.js.map +1 -1
- package/lib/index.js +710 -426
- package/lib/live2d-vendor.js +96 -96
- package/lib/live2d-vendor.js.map +1 -1
- package/lib/types/announce.d.ts +6 -6
- package/lib/types/announce.d.ts.map +1 -1
- package/lib/types/announce.js +5 -5
- package/lib/types/client/PetDockEntry.d.ts.map +1 -1
- package/lib/types/client/PetDockEntry.js +1 -1
- package/lib/types/client/PetSettingsCard.d.ts +20 -9
- package/lib/types/client/PetSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PetSettingsCard.js +116 -5
- package/lib/types/client/PetSprite.d.ts +6 -0
- package/lib/types/client/PetSprite.d.ts.map +1 -1
- package/lib/types/client/PetSprite.js +95 -0
- package/lib/types/client/PluginSettingsCard.d.ts +4 -0
- package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PluginSettingsCard.js +3 -2
- package/lib/types/client/gameplay-hud.d.ts +10 -1
- package/lib/types/client/gameplay-hud.d.ts.map +1 -1
- package/lib/types/client/gameplay-hud.js +137 -23
- 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 +61 -27
- package/lib/types/client/main-session.d.ts +33 -0
- package/lib/types/client/main-session.d.ts.map +1 -0
- package/lib/types/client/main-session.js +34 -0
- package/lib/types/client/settings-form.d.ts +17 -17
- package/lib/types/client/settings-form.d.ts.map +1 -1
- package/lib/types/client/settings-form.js +30 -27
- package/lib/types/event-projection.d.ts.map +1 -1
- package/lib/types/event-projection.js +5 -3
- package/lib/types/gameplay.d.ts +79 -5
- package/lib/types/gameplay.d.ts.map +1 -1
- package/lib/types/gameplay.js +212 -17
- package/lib/types/http.d.ts +18 -1
- package/lib/types/http.d.ts.map +1 -1
- package/lib/types/http.js +22 -1
- package/lib/types/index.d.ts +100 -29
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +113 -64
- package/lib/types/persist.d.ts.map +1 -1
- package/lib/types/persist.js +3 -1
- package/lib/types/routes.d.ts.map +1 -1
- package/lib/types/routes.js +3 -1
- package/lib/types/service.d.ts +8 -3
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +26 -4
- package/package.json +46 -26
- package/src/announce.ts +6 -6
- package/src/client/PetDockEntry.tsx +1 -0
- package/src/client/PetSettingsCard.test.tsx +103 -0
- package/src/client/PetSettingsCard.tsx +129 -26
- package/src/client/PetSprite.tsx +96 -0
- package/src/client/PluginSettingsCard.tsx +7 -2
- package/src/client/gameplay-hud.test.tsx +106 -8
- package/src/client/gameplay-hud.tsx +143 -28
- package/src/client/index.test.tsx +37 -4
- package/src/client/index.ts +74 -34
- package/src/client/main-session.ts +43 -0
- package/src/client/settings-form.ts +34 -31
- package/src/event-projection.ts +5 -3
- package/src/gameplay.test.ts +138 -1
- package/src/gameplay.ts +253 -16
- package/src/http.ts +23 -1
- package/src/index.test.ts +107 -11
- package/src/index.ts +162 -73
- package/src/persist.ts +3 -1
- package/src/registry.test.ts +3 -2
- package/src/routes.ts +3 -1
- package/src/service.ts +27 -5
package/lib/types/announce.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The pet announcement-bubble contract
|
|
2
|
+
* The pet announcement-bubble contract: a sibling plugin
|
|
3
3
|
* pushes one structured announcement through `pet.announce(...)`, the host
|
|
4
4
|
* validates it into a bounded payload, and the browser half renders it as a
|
|
5
5
|
* dedicated, specially styled bubble above the session bubble stack.
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
/** One plugin-authored announcement bubble. */
|
|
12
12
|
export interface PetAnnouncement {
|
|
13
|
-
/** Authoring plugin's source tag (
|
|
13
|
+
/** Authoring plugin's source tag (its own package short name). */
|
|
14
14
|
source: string;
|
|
15
15
|
/** Bubble content kind: a spend estimate, an account balance, or a plan-quota status. */
|
|
16
16
|
kind: 'balance' | 'cost' | 'plan';
|
|
@@ -34,10 +34,10 @@ export interface PetAnnouncement {
|
|
|
34
34
|
/** Default freshness window. */
|
|
35
35
|
export declare const ANNOUNCE_DEFAULT_TTL_MS = 10000;
|
|
36
36
|
/**
|
|
37
|
-
* Hard TTL ceiling. A repeating announcer
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
37
|
+
* Hard TTL ceiling. A repeating announcer declares its poll cadence as
|
|
38
|
+
* the TTL, so an always-on bubble stays continuous across polls; the
|
|
39
|
+
* ceiling means a source that dies unmounts its bubble within at most one
|
|
40
|
+
* missed refresh cycle rather than lingering forever.
|
|
41
41
|
*/
|
|
42
42
|
export declare const ANNOUNCE_MAX_TTL_MS = 7200000;
|
|
43
43
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"announce.d.ts","sourceRoot":"","sources":["../../src/announce.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,+CAA+C;AAC/C,MAAM,WAAW,eAAe;IAC9B,
|
|
1
|
+
{"version":3,"file":"announce.d.ts","sourceRoot":"","sources":["../../src/announce.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,+CAA+C;AAC/C,MAAM,WAAW,eAAe;IAC9B,kEAAkE;IAClE,MAAM,EAAE,MAAM,CAAA;IACd,yFAAyF;IACzF,IAAI,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,CAAA;IACjC,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAA;IACb,yFAAyF;IACzF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,oFAAoF;IACpF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,qDAAqD;IACrD,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,KAAK,CAAA;IAC3B,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAA;IACb,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAA;CACX;AAED,gCAAgC;AAChC,eAAO,MAAM,uBAAuB,QAAS,CAAA;AAE7C;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,UAAY,CAAA;AAc5C;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAgC1F;AAED,uDAAuD;AACvD,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,eAAe,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAErF"}
|
package/lib/types/announce.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The pet announcement-bubble contract
|
|
2
|
+
* The pet announcement-bubble contract: a sibling plugin
|
|
3
3
|
* pushes one structured announcement through `pet.announce(...)`, the host
|
|
4
4
|
* validates it into a bounded payload, and the browser half renders it as a
|
|
5
5
|
* dedicated, specially styled bubble above the session bubble stack.
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
/** Default freshness window. */
|
|
12
12
|
export const ANNOUNCE_DEFAULT_TTL_MS = 10_000;
|
|
13
13
|
/**
|
|
14
|
-
* Hard TTL ceiling. A repeating announcer
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* Hard TTL ceiling. A repeating announcer declares its poll cadence as
|
|
15
|
+
* the TTL, so an always-on bubble stays continuous across polls; the
|
|
16
|
+
* ceiling means a source that dies unmounts its bubble within at most one
|
|
17
|
+
* missed refresh cycle rather than lingering forever.
|
|
18
18
|
*/
|
|
19
19
|
export const ANNOUNCE_MAX_TTL_MS = 7_200_000;
|
|
20
20
|
/** Hard bounds: lengths and the TTL range (1 s .. 60 s). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PetDockEntry.d.ts","sourceRoot":"","sources":["../../../src/client/PetDockEntry.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAA2C,KAAK,YAAY,EAAE,MAAM,OAAO,CAAA;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAEnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAItD,OAAO,EAAe,KAAK,WAAW,EAAoB,MAAM,oBAAoB,CAAA;AACpF,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,6EAA6E;IAC7E,KAAK,EAAE,gBAAgB,CAAA;IACvB,kFAAkF;IAClF,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,8BAA8B;IAC9B,GAAG,EAAE,MAAM,IAAI,CAAA;IACf,uBAAuB;IACvB,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,uBAAuB;IACvB,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,qCAAqC;IACrC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,+BAA+B;IAC/B,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAChD,uDAAuD;IACvD,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,2DAA2D;IAC3D,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,iCAAiC;IACjC,YAAY,EAAE,MAAM,IAAI,CAAA;IACxB,uGAAuG;IACvG,QAAQ,EAAE,WAAW,CAAA;CACtB;AAED,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAC3B,WAAW,GACT,WAAW,CAAC,OAAO,EAAE,CAAC,GACtB;IACA;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AAIH;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,YAAY,
|
|
1
|
+
{"version":3,"file":"PetDockEntry.d.ts","sourceRoot":"","sources":["../../../src/client/PetDockEntry.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAA2C,KAAK,YAAY,EAAE,MAAM,OAAO,CAAA;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAEnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAItD,OAAO,EAAe,KAAK,WAAW,EAAoB,MAAM,oBAAoB,CAAA;AACpF,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,6EAA6E;IAC7E,KAAK,EAAE,gBAAgB,CAAA;IACvB,kFAAkF;IAClF,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,8BAA8B;IAC9B,GAAG,EAAE,MAAM,IAAI,CAAA;IACf,uBAAuB;IACvB,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,uBAAuB;IACvB,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,qCAAqC;IACrC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,+BAA+B;IAC/B,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAChD,uDAAuD;IACvD,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,2DAA2D;IAC3D,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,iCAAiC;IACjC,YAAY,EAAE,MAAM,IAAI,CAAA;IACxB,uGAAuG;IACvG,QAAQ,EAAE,WAAW,CAAA;CACtB;AAED,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAC3B,WAAW,GACT,WAAW,CAAC,OAAO,EAAE,CAAC,GACtB;IACA;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AAIH;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,YAAY,CA0FnE"}
|
|
@@ -47,7 +47,7 @@ export function PetDockEntry(props) {
|
|
|
47
47
|
if (visible) {
|
|
48
48
|
return (_jsx("span", { "data-pet-dock": true, "data-testid": "pet-dock", children: snapshot === null || definition === null
|
|
49
49
|
? null
|
|
50
|
-
: (_jsx(PetRendererSwitch, { definition: definition, phase: snapshot?.phase ?? 'idle', onPet: props.pet, ...(aux === null ? {} : { drag: aux.drag, bus: aux.bus }), t: props.t, children: _jsx(PetSprite, { snapshot: snapshot, definition: definition, display: snapshot.display, feedback: feedback, onPet: props.pet, onFeed: props.feed, onHide: props.hide, onDragEnd: props.dragEnd, onRename: props.rename, onOpenSession: props.openSession, onFeedbackDone: props.feedbackDone, portalTarget: props.portalTarget, dragDisabled: snapshot.gameplay?.mode === 'work', ...(gameplay === undefined || aux === null
|
|
50
|
+
: (_jsx(PetRendererSwitch, { definition: definition, phase: snapshot?.phase ?? 'idle', onPet: props.pet, ...(aux === null ? {} : { drag: aux.drag, bus: aux.bus }), t: props.t, children: _jsx(PetSprite, { snapshot: snapshot, definition: definition, display: snapshot.display, feedback: feedback, onPet: props.pet, onFeed: props.feed, onHide: props.hide, onDragEnd: props.dragEnd, onRename: props.rename, onOpenSession: props.openSession, onFeedbackDone: props.feedbackDone, portalTarget: props.portalTarget, dragDisabled: snapshot.gameplay?.mode === 'work', ...(aux === null ? {} : { bus: aux.bus }), ...(gameplay === undefined || aux === null
|
|
51
51
|
? {}
|
|
52
52
|
: {
|
|
53
53
|
onGameplayTap: (fx, fy) => aux.bus.tap?.(fx, fy),
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The pet settings card: pet selection plus display layout,
|
|
3
|
-
* 'pet'
|
|
4
|
-
*
|
|
5
|
-
* as
|
|
6
|
-
*
|
|
7
|
-
*
|
|
2
|
+
* The pet settings card: pet selection plus display layout, staged over the
|
|
3
|
+
* 'pet' profile entry's own configuration (a plugin's settings ARE its Cordis
|
|
4
|
+
* Config since 0.1.7, so the Host serves one form per profile entry).
|
|
5
|
+
* Rendered as an always-open first-level settings page; the section wrapper
|
|
6
|
+
* below mounts it as the content of the top-level 'settings.section' nav
|
|
7
|
+
* entry. The petId choices come from the registry endpoint ('/api/pet/pets') —
|
|
8
|
+
* the same list the sprite renders from — so the card carries no per-pet
|
|
9
|
+
* knowledge.
|
|
8
10
|
*/
|
|
9
11
|
import type { ReactNode } from 'react';
|
|
10
12
|
import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
11
|
-
import type {
|
|
13
|
+
import type { ConfigForm } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
12
14
|
import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
13
15
|
import { type CardActions, type CardShell, type FieldState as CardFieldState } from './settings-form.ts';
|
|
14
16
|
/** The pet's settings fields this card edits (the namespace's full schema). */
|
|
@@ -32,6 +34,8 @@ export interface PetSettings {
|
|
|
32
34
|
}
|
|
33
35
|
/** What the pet settings card renders. */
|
|
34
36
|
export interface PetSettingsCardState extends CardShell {
|
|
37
|
+
/** The aggregate shell has no Host settings form; pet selection uses its own persisted API. */
|
|
38
|
+
petSelectionFallback: boolean;
|
|
35
39
|
/** Plugin master switch. */
|
|
36
40
|
enabled: CardFieldState;
|
|
37
41
|
/** Master switch. */
|
|
@@ -75,17 +79,24 @@ export declare class PetSettingsCardController {
|
|
|
75
79
|
private readonly petChoices;
|
|
76
80
|
private readonly petLabels;
|
|
77
81
|
private diagnostics;
|
|
82
|
+
private selectedPetId;
|
|
83
|
+
private stagedPetId;
|
|
84
|
+
private savingPet;
|
|
85
|
+
private petSaveFailed;
|
|
78
86
|
private loaded;
|
|
79
87
|
private attempts;
|
|
80
88
|
private disposed;
|
|
81
89
|
/** Pending deferred-load or retry timer; cancelled by dispose(). */
|
|
82
90
|
private pendingTimer;
|
|
83
|
-
/** @param scope - the bound
|
|
84
|
-
constructor(scope:
|
|
91
|
+
/** @param scope - the bound configuration form for the 'pet' entry. */
|
|
92
|
+
constructor(scope: ConfigForm<PetSettings>);
|
|
85
93
|
/** Fetch registry diagnostics once (soft-fail: an empty list on error). */
|
|
86
94
|
private loadDiagnostics;
|
|
87
95
|
/** Resolve the registry choices once (retried a few times on failure). */
|
|
88
96
|
private loadPets;
|
|
97
|
+
private loadSelectedPet;
|
|
98
|
+
private fallback;
|
|
99
|
+
private savePetSelection;
|
|
89
100
|
private projection;
|
|
90
101
|
/**
|
|
91
102
|
* Build the face the card's slot registration injects.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PetSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PetSettingsCard.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"PetSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PetSettingsCard.tsx"],"names":[],"mappings":"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"}
|
|
@@ -17,6 +17,16 @@ async function fetchPetDiagnostics() {
|
|
|
17
17
|
const body = (await response.json());
|
|
18
18
|
return body.diagnostics ?? [];
|
|
19
19
|
}
|
|
20
|
+
/** Read the selection from the same persisted state the pet renders. */
|
|
21
|
+
async function fetchSelectedPetId() {
|
|
22
|
+
const response = await fetch('/api/pet/state');
|
|
23
|
+
if (!response.ok)
|
|
24
|
+
throw new Error('pet state failed: ' + response.status);
|
|
25
|
+
const body = (await response.json());
|
|
26
|
+
if (typeof body.pet?.id !== 'string')
|
|
27
|
+
throw new Error('pet state has no selected pet');
|
|
28
|
+
return body.pet.id;
|
|
29
|
+
}
|
|
20
30
|
/** Bridges the 'pet' scope onto the card's staged form. */
|
|
21
31
|
export class PetSettingsCardController {
|
|
22
32
|
form;
|
|
@@ -27,12 +37,16 @@ export class PetSettingsCardController {
|
|
|
27
37
|
petChoices = [];
|
|
28
38
|
petLabels = new Map();
|
|
29
39
|
diagnostics = [];
|
|
40
|
+
selectedPetId;
|
|
41
|
+
stagedPetId;
|
|
42
|
+
savingPet = false;
|
|
43
|
+
petSaveFailed = false;
|
|
30
44
|
loaded = false;
|
|
31
45
|
attempts = 0;
|
|
32
46
|
disposed = false;
|
|
33
47
|
/** Pending deferred-load or retry timer; cancelled by dispose(). */
|
|
34
48
|
pendingTimer;
|
|
35
|
-
/** @param scope - the bound
|
|
49
|
+
/** @param scope - the bound configuration form for the 'pet' entry. */
|
|
36
50
|
constructor(scope) {
|
|
37
51
|
this.form = new CardForm(scope, [
|
|
38
52
|
booleanField('enabled'),
|
|
@@ -55,6 +69,7 @@ export class PetSettingsCardController {
|
|
|
55
69
|
return;
|
|
56
70
|
void this.loadPets();
|
|
57
71
|
void this.loadDiagnostics();
|
|
72
|
+
void this.loadSelectedPet();
|
|
58
73
|
}, 0);
|
|
59
74
|
}
|
|
60
75
|
/** Fetch registry diagnostics once (soft-fail: an empty list on error). */
|
|
@@ -97,9 +112,69 @@ export class PetSettingsCardController {
|
|
|
97
112
|
}
|
|
98
113
|
}
|
|
99
114
|
}
|
|
115
|
+
async loadSelectedPet() {
|
|
116
|
+
try {
|
|
117
|
+
const petId = await fetchSelectedPetId();
|
|
118
|
+
if (this.disposed)
|
|
119
|
+
return;
|
|
120
|
+
this.selectedPetId = petId;
|
|
121
|
+
this.store.set(this.projection());
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// The Host form remains the authority when it is available. An absent
|
|
125
|
+
// pet API leaves the fallback unavailable rather than inventing state.
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
fallback() {
|
|
129
|
+
const shell = this.form.shell();
|
|
130
|
+
return shell.available && !shell.exposed && this.selectedPetId !== undefined;
|
|
131
|
+
}
|
|
132
|
+
async savePetSelection() {
|
|
133
|
+
const petId = this.stagedPetId;
|
|
134
|
+
if (petId === undefined || this.savingPet || !this.petChoices.includes(petId))
|
|
135
|
+
return;
|
|
136
|
+
this.savingPet = true;
|
|
137
|
+
this.petSaveFailed = false;
|
|
138
|
+
this.store.set(this.projection());
|
|
139
|
+
try {
|
|
140
|
+
const response = await fetch('/api/pet/set-pet', {
|
|
141
|
+
method: 'POST',
|
|
142
|
+
headers: { 'content-type': 'application/json' },
|
|
143
|
+
body: JSON.stringify({ petId }),
|
|
144
|
+
});
|
|
145
|
+
const result = (await response.json());
|
|
146
|
+
if (!response.ok || result.ok !== true || result.petId !== petId || await fetchSelectedPetId() !== petId) {
|
|
147
|
+
throw new Error('pet selection was not persisted');
|
|
148
|
+
}
|
|
149
|
+
this.selectedPetId = petId;
|
|
150
|
+
if (this.stagedPetId === petId)
|
|
151
|
+
this.stagedPetId = undefined;
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
this.petSaveFailed = true;
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
this.savingPet = false;
|
|
158
|
+
if (!this.disposed)
|
|
159
|
+
this.store.set(this.projection());
|
|
160
|
+
}
|
|
161
|
+
}
|
|
100
162
|
projection() {
|
|
163
|
+
const fallback = this.fallback();
|
|
164
|
+
const shell = this.form.shell();
|
|
165
|
+
const configuredPet = this.form.field('petId');
|
|
101
166
|
return {
|
|
102
|
-
...
|
|
167
|
+
...shell,
|
|
168
|
+
...(fallback ? {
|
|
169
|
+
exposed: true,
|
|
170
|
+
writable: true,
|
|
171
|
+
dirty: this.stagedPetId !== undefined && this.stagedPetId !== this.selectedPetId,
|
|
172
|
+
invalid: this.stagedPetId !== undefined && !this.petChoices.includes(this.stagedPetId),
|
|
173
|
+
saving: this.savingPet,
|
|
174
|
+
failed: this.petSaveFailed,
|
|
175
|
+
failedReason: undefined,
|
|
176
|
+
} : {}),
|
|
177
|
+
petSelectionFallback: fallback,
|
|
103
178
|
enabled: this.form.field('enabled'),
|
|
104
179
|
decorationEnabled: this.form.field('decorationEnabled'),
|
|
105
180
|
visible: this.form.field('visible'),
|
|
@@ -107,7 +182,11 @@ export class PetSettingsCardController {
|
|
|
107
182
|
right: this.form.field('right'),
|
|
108
183
|
bottom: this.form.field('bottom'),
|
|
109
184
|
bubbleScale: this.form.field('bubbleScale'),
|
|
110
|
-
petId:
|
|
185
|
+
petId: fallback
|
|
186
|
+
? { text: this.stagedPetId ?? this.selectedPetId ?? '', overridden: false, invalid: this.stagedPetId !== undefined && !this.petChoices.includes(this.stagedPetId) }
|
|
187
|
+
: configuredPet.text === '' && this.selectedPetId !== undefined
|
|
188
|
+
? { ...configuredPet, text: this.selectedPetId }
|
|
189
|
+
: configuredPet,
|
|
111
190
|
petChoices: this.petChoices.map(id => ({ value: id, label: this.petLabels.get(id) ?? id })),
|
|
112
191
|
petDiagnostics: this.diagnostics,
|
|
113
192
|
};
|
|
@@ -117,7 +196,39 @@ export class PetSettingsCardController {
|
|
|
117
196
|
* @returns the card's snapshot and its form actions.
|
|
118
197
|
*/
|
|
119
198
|
inject() {
|
|
120
|
-
|
|
199
|
+
const actions = this.form.actions();
|
|
200
|
+
return {
|
|
201
|
+
hooks: { petSettingsCard: this.store },
|
|
202
|
+
edit: (field, value) => {
|
|
203
|
+
if (!this.fallback())
|
|
204
|
+
return actions.edit(field, value);
|
|
205
|
+
if (field !== 'petId')
|
|
206
|
+
return;
|
|
207
|
+
this.stagedPetId = value === '' ? undefined : value;
|
|
208
|
+
this.petSaveFailed = false;
|
|
209
|
+
this.store.set(this.projection());
|
|
210
|
+
},
|
|
211
|
+
resetField: (field) => {
|
|
212
|
+
if (!this.fallback())
|
|
213
|
+
return actions.resetField(field);
|
|
214
|
+
if (field !== 'petId')
|
|
215
|
+
return;
|
|
216
|
+
this.stagedPetId = undefined;
|
|
217
|
+
this.petSaveFailed = false;
|
|
218
|
+
this.store.set(this.projection());
|
|
219
|
+
},
|
|
220
|
+
save: () => { if (this.fallback())
|
|
221
|
+
void this.savePetSelection();
|
|
222
|
+
else
|
|
223
|
+
actions.save(); },
|
|
224
|
+
discard: () => {
|
|
225
|
+
if (!this.fallback())
|
|
226
|
+
return actions.discard();
|
|
227
|
+
this.stagedPetId = undefined;
|
|
228
|
+
this.petSaveFailed = false;
|
|
229
|
+
this.store.set(this.projection());
|
|
230
|
+
},
|
|
231
|
+
};
|
|
121
232
|
}
|
|
122
233
|
/**
|
|
123
234
|
* Release the card's scope subscription, bound stores and pending load
|
|
@@ -149,7 +260,7 @@ export function PetSettingsCard(props) {
|
|
|
149
260
|
invalidLabel: t('settings.invalidNumber'),
|
|
150
261
|
disabled,
|
|
151
262
|
};
|
|
152
|
-
return (_jsxs(PluginSettingsCard, { t: t, titleKey: "settings.title", descriptionKey: "settings.description", state: state, onSave: props.save, onDiscard: props.discard, alwaysOpen: true, children: [_jsx(BooleanField, { id: "settings-pet-enabled", label: t('settings.enabled'), hint: t('settings.enabledHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.enabled, onEdit: (text) => { props.edit('enabled', text); }, onReset: () => { props.resetField('enabled'); } }), _jsx(BooleanField, { id: "settings-pet-decoration", label: t('settings.decoration'), hint: t('settings.decorationHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.decorationEnabled, onEdit: (text) => { props.edit('decorationEnabled', text); }, onReset: () => { props.resetField('decorationEnabled'); } }), _jsx(ChoiceField, { id: "settings-pet-pet", label: t('settings.pet'), hint: t('settings.petHint'), inheritLabel: t('settings.inherit'), ...fieldProps, ...state.petId, choices: state.petChoices, onEdit: (text) => { props.edit('petId', text); }, onReset: () => { props.resetField('petId'); } }), state.petDiagnostics.length === 0 ? null : (_jsxs("li", { className: sectionCss.diagnostics, "data-dsh-part": "diagnostics", children: [_jsx("span", { className: sectionCss.diagnosticsTitle, children: t('settings.diagnosticsTitle') }), _jsx("ul", { children: state.petDiagnostics.map((diagnostic, index) => (_jsx("li", { "data-level": diagnostic.level, children: diagnostic.message }, index))) })] })), _jsx(BooleanField, { id: "settings-pet-visible", label: t('settings.visible'), hint: t('settings.visibleHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.visible, onEdit: (text) => { props.edit('visible', text); }, onReset: () => { props.resetField('visible'); } }), _jsx(ValueField, { id: "settings-pet-size", label: t('settings.size'), hint: t('settings.sizeHint'), numeric: true, ...fieldProps, ...state.size, onEdit: (text) => { props.edit('size', text); }, onReset: () => { props.resetField('size'); } }), _jsx(ValueField, { id: "settings-pet-right", label: t('settings.right'), hint: t('settings.rightHint'), numeric: true, ...fieldProps, ...state.right, onEdit: (text) => { props.edit('right', text); }, onReset: () => { props.resetField('right'); } }), _jsx(ValueField, { id: "settings-pet-bottom", label: t('settings.bottom'), hint: t('settings.bottomHint'), numeric: true, ...fieldProps, ...state.bottom, onEdit: (text) => { props.edit('bottom', text); }, onReset: () => { props.resetField('bottom'); } }), _jsx(ValueField, { id: "settings-pet-bubble-scale", label: t('settings.bubbleScale'), hint: t('settings.bubbleScaleHint'), numeric: true, ...fieldProps, ...state.bubbleScale, onEdit: (text) => { props.edit('bubbleScale', text); }, onReset: () => { props.resetField('bubbleScale'); } })] }));
|
|
263
|
+
return (_jsxs(PluginSettingsCard, { t: t, titleKey: "settings.title", descriptionKey: "settings.description", descriptionNode: state.petSelectionFallback ? t('settings.petHint') : undefined, state: state, onSave: props.save, onDiscard: props.discard, alwaysOpen: true, children: [state.petSelectionFallback ? null : _jsx(BooleanField, { id: "settings-pet-enabled", label: t('settings.enabled'), hint: t('settings.enabledHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.enabled, onEdit: (text) => { props.edit('enabled', text); }, onReset: () => { props.resetField('enabled'); } }), state.petSelectionFallback ? null : _jsx(BooleanField, { id: "settings-pet-decoration", label: t('settings.decoration'), hint: t('settings.decorationHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.decorationEnabled, onEdit: (text) => { props.edit('decorationEnabled', text); }, onReset: () => { props.resetField('decorationEnabled'); } }), _jsx(ChoiceField, { id: "settings-pet-pet", label: t('settings.pet'), hint: t('settings.petHint'), inheritLabel: t('settings.inherit'), ...fieldProps, ...state.petId, choices: state.petChoices, onEdit: (text) => { props.edit('petId', text); }, onReset: () => { props.resetField('petId'); } }), state.petDiagnostics.length === 0 ? null : (_jsxs("li", { className: sectionCss.diagnostics, "data-dsh-part": "diagnostics", children: [_jsx("span", { className: sectionCss.diagnosticsTitle, children: t('settings.diagnosticsTitle') }), _jsx("ul", { children: state.petDiagnostics.map((diagnostic, index) => (_jsx("li", { "data-level": diagnostic.level, children: diagnostic.message }, index))) })] })), state.petSelectionFallback ? null : _jsx(BooleanField, { id: "settings-pet-visible", label: t('settings.visible'), hint: t('settings.visibleHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.visible, onEdit: (text) => { props.edit('visible', text); }, onReset: () => { props.resetField('visible'); } }), state.petSelectionFallback ? null : _jsx(ValueField, { id: "settings-pet-size", label: t('settings.size'), hint: t('settings.sizeHint'), numeric: true, ...fieldProps, ...state.size, onEdit: (text) => { props.edit('size', text); }, onReset: () => { props.resetField('size'); } }), state.petSelectionFallback ? null : _jsx(ValueField, { id: "settings-pet-right", label: t('settings.right'), hint: t('settings.rightHint'), numeric: true, ...fieldProps, ...state.right, onEdit: (text) => { props.edit('right', text); }, onReset: () => { props.resetField('right'); } }), state.petSelectionFallback ? null : _jsx(ValueField, { id: "settings-pet-bottom", label: t('settings.bottom'), hint: t('settings.bottomHint'), numeric: true, ...fieldProps, ...state.bottom, onEdit: (text) => { props.edit('bottom', text); }, onReset: () => { props.resetField('bottom'); } }), state.petSelectionFallback ? null : _jsx(ValueField, { id: "settings-pet-bubble-scale", label: t('settings.bubbleScale'), hint: t('settings.bubbleScaleHint'), numeric: true, ...fieldProps, ...state.bubbleScale, onEdit: (text) => { props.edit('bubbleScale', text); }, onReset: () => { props.resetField('bubbleScale'); } })] }));
|
|
153
264
|
}
|
|
154
265
|
/** Render the pet settings card as a first-level settings page. */
|
|
155
266
|
export function PetSettingsSection(props) {
|
|
@@ -13,6 +13,7 @@ import type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots';
|
|
|
13
13
|
import { type PetDisplayConfig } from '../persist.ts';
|
|
14
14
|
import type { PetStateView } from '../service.ts';
|
|
15
15
|
import type { PetDefinition } from '../registry.ts';
|
|
16
|
+
import type { GameplayBus } from './gameplay-hud.ts';
|
|
16
17
|
import type { PetFeedback } from './pet-store.ts';
|
|
17
18
|
import { NS } from './locales.ts';
|
|
18
19
|
/** Props injected by the plugin apply body (store actions + locale). */
|
|
@@ -68,6 +69,11 @@ export interface PetSpriteProps {
|
|
|
68
69
|
onGameplayMenu?: () => void;
|
|
69
70
|
/** Disable the drag gesture (gameplay work mode blocks dragging). */
|
|
70
71
|
dragDisabled?: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Per-pet coordination bus. The chrome registers `walk` here so the gameplay
|
|
74
|
+
* HUD can roam the pet (the same two-way shape the HUD's `tap` uses).
|
|
75
|
+
*/
|
|
76
|
+
bus?: GameplayBus;
|
|
71
77
|
/**
|
|
72
78
|
* DOM node the floating chrome portals into. Defaults to document.body
|
|
73
79
|
* (the legacy behavior); the plugin apply passes its owning root (the
|
|
@@ -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,EAAkB,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;
|
|
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,EAAkB,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAGpD,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;;;OAGG;IACH,GAAG,CAAC,EAAE,WAAW,CAAA;IACjB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,+CAA+C;IAC/C,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;CAC1B;AAkJD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,WAAW,CAyrB5D"}
|
|
@@ -371,6 +371,7 @@ export function PetSprite(props) {
|
|
|
371
371
|
const onPointerDown = (e) => {
|
|
372
372
|
if (props.dragDisabled === true)
|
|
373
373
|
return;
|
|
374
|
+
endWalk(false);
|
|
374
375
|
e.preventDefault();
|
|
375
376
|
e.target.setPointerCapture?.(e.pointerId);
|
|
376
377
|
const current = dragPos ?? { right: display.right, bottom: display.bottom };
|
|
@@ -405,6 +406,99 @@ export function PetSprite(props) {
|
|
|
405
406
|
const pos = dragPos ?? { right: display.right, bottom: display.bottom };
|
|
406
407
|
const spriteWidth = Math.round(cell.width * spriteScale);
|
|
407
408
|
const spriteHeight = Math.round(cell.height * spriteScale);
|
|
409
|
+
// --- roaming ---------------------------------------------------------
|
|
410
|
+
// The gameplay HUD rolls when the pet may wander; this side owns the motion
|
|
411
|
+
// (clamped to the viewport), the walk art's facing and the persisted resting
|
|
412
|
+
// spot -- the walk ends on the same write a drag ends with. The walk art
|
|
413
|
+
// faces left, so a rightward walk mirrors the sprite (a vertical walk keeps
|
|
414
|
+
// the side view); every other track stays unmirrored.
|
|
415
|
+
const [facingRight, setFacingRight] = useState(false);
|
|
416
|
+
const walkRafRef = useRef(0);
|
|
417
|
+
const walkTargetRef = useRef(null);
|
|
418
|
+
const dragPosRef = useRef(dragPos);
|
|
419
|
+
dragPosRef.current = dragPos;
|
|
420
|
+
/** Stop an active walk; `persist` lands the pet on the spot it reached. */
|
|
421
|
+
const endWalk = (persist) => {
|
|
422
|
+
if (walkRafRef.current === 0)
|
|
423
|
+
return;
|
|
424
|
+
window.cancelAnimationFrame(walkRafRef.current);
|
|
425
|
+
walkRafRef.current = 0;
|
|
426
|
+
setFacingRight(false);
|
|
427
|
+
const settled = walkTargetRef.current;
|
|
428
|
+
walkTargetRef.current = null;
|
|
429
|
+
if (persist && settled !== null)
|
|
430
|
+
props.onDragEnd(settled.right, settled.bottom);
|
|
431
|
+
};
|
|
432
|
+
useEffect(() => {
|
|
433
|
+
const bus = props.bus;
|
|
434
|
+
if (bus === undefined)
|
|
435
|
+
return undefined;
|
|
436
|
+
bus.walk = (direction, distance, speed) => {
|
|
437
|
+
if (dragRef.current !== null || walkRafRef.current !== 0)
|
|
438
|
+
return 0;
|
|
439
|
+
const current = dragPosRef.current ?? { right: display.right, bottom: display.bottom };
|
|
440
|
+
// Keep the whole sprite on screen with an 8px breathing margin. `right`
|
|
441
|
+
// and `bottom` are CSS offsets: right grows leftwards, bottom upwards.
|
|
442
|
+
const margin = 8;
|
|
443
|
+
const maxRight = Math.max(margin, window.innerWidth - spriteWidth - margin);
|
|
444
|
+
const maxBottom = Math.max(margin, window.innerHeight - spriteHeight - margin);
|
|
445
|
+
const wanted = { ...current };
|
|
446
|
+
if (direction === 'left')
|
|
447
|
+
wanted.right = current.right + distance;
|
|
448
|
+
else if (direction === 'right')
|
|
449
|
+
wanted.right = current.right - distance;
|
|
450
|
+
else if (direction === 'up')
|
|
451
|
+
wanted.bottom = current.bottom + distance;
|
|
452
|
+
else
|
|
453
|
+
wanted.bottom = current.bottom - distance;
|
|
454
|
+
const target = {
|
|
455
|
+
right: Math.max(margin, clampOffset(wanted.right, maxRight)),
|
|
456
|
+
bottom: Math.max(margin, clampOffset(wanted.bottom, maxBottom)),
|
|
457
|
+
};
|
|
458
|
+
const travelled = direction === 'left' ? target.right - current.right
|
|
459
|
+
: direction === 'right' ? current.right - target.right
|
|
460
|
+
: direction === 'up' ? target.bottom - current.bottom
|
|
461
|
+
: current.bottom - target.bottom;
|
|
462
|
+
if (travelled < 1)
|
|
463
|
+
return 0;
|
|
464
|
+
const duration = Math.max(150, (travelled / Math.max(1, speed)) * 1000);
|
|
465
|
+
const startedAt = performance.now();
|
|
466
|
+
// The crawl art faces left, so only a rightward walk mirrors it; a
|
|
467
|
+
// vertical walk keeps the side view (there is no front/back art).
|
|
468
|
+
setFacingRight(direction === 'right');
|
|
469
|
+
walkTargetRef.current = current;
|
|
470
|
+
const step = (now) => {
|
|
471
|
+
const t = Math.min(1, (now - startedAt) / duration);
|
|
472
|
+
const next = {
|
|
473
|
+
right: current.right + (target.right - current.right) * t,
|
|
474
|
+
bottom: current.bottom + (target.bottom - current.bottom) * t,
|
|
475
|
+
};
|
|
476
|
+
walkTargetRef.current = next;
|
|
477
|
+
setDragPos(next);
|
|
478
|
+
if (t < 1) {
|
|
479
|
+
walkRafRef.current = window.requestAnimationFrame(step);
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
walkRafRef.current = 0;
|
|
483
|
+
walkTargetRef.current = null;
|
|
484
|
+
setFacingRight(false);
|
|
485
|
+
props.onDragEnd(next.right, next.bottom);
|
|
486
|
+
};
|
|
487
|
+
walkRafRef.current = window.requestAnimationFrame(step);
|
|
488
|
+
return travelled;
|
|
489
|
+
};
|
|
490
|
+
return () => {
|
|
491
|
+
bus.walk = undefined;
|
|
492
|
+
if (walkRafRef.current !== 0) {
|
|
493
|
+
window.cancelAnimationFrame(walkRafRef.current);
|
|
494
|
+
walkRafRef.current = 0;
|
|
495
|
+
}
|
|
496
|
+
// A cancelled walk must not leave the sprite mirrored.
|
|
497
|
+
setFacingRight(false);
|
|
498
|
+
walkTargetRef.current = null;
|
|
499
|
+
};
|
|
500
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- one registration per sprite
|
|
501
|
+
}, [props.bus, definition.id, display.right, display.bottom, spriteWidth]);
|
|
408
502
|
// Bubble typography follows the sprite's own scale (#1549), bounded so a
|
|
409
503
|
// shrunk pet never carries unreadably small text.
|
|
410
504
|
const bubbleScale = bubbleScaleFor(display);
|
|
@@ -508,6 +602,7 @@ export function PetSprite(props) {
|
|
|
508
602
|
}
|
|
509
603
|
: {}),
|
|
510
604
|
cursor: dragRef.current === null ? 'grab' : 'grabbing',
|
|
605
|
+
...(facingRight ? { transform: 'scaleX(-1)' } : {}),
|
|
511
606
|
}, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, onClick: (e) => {
|
|
512
607
|
// A pointer sequence that moved (dragged) still fires a trailing
|
|
513
608
|
// click; skip the pet when that happened.
|
|
@@ -47,6 +47,10 @@ export interface PluginSettingsCardProps<TKey extends string = string> {
|
|
|
47
47
|
* edits, so the footer would sit there permanently disabled.
|
|
48
48
|
*/
|
|
49
49
|
hideFooter?: boolean;
|
|
50
|
+
/** Render children even when the settings namespace is not exposed by the host. */
|
|
51
|
+
renderChildrenWhenNotExposed?: boolean;
|
|
52
|
+
/** Hide the 'not exposed' warning banner when the settings namespace is not exposed. */
|
|
53
|
+
hideNotExposedNotice?: boolean;
|
|
50
54
|
/** The plugin's controls. */
|
|
51
55
|
children: ReactNode;
|
|
52
56
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PluginSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PluginSettingsCard.tsx"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,EAAiF,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AACrH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAGnD,oGAAoG;AACpG,eAAO,MAAM,cAAc,kMAUjB,CAAA;AAEV,6CAA6C;AAC7C,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAEzD,oEAAoE;AACpE,MAAM,MAAM,eAAe,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,IAAI,IAAI,GAAG,WAAW,CAAA;AAE9E,wDAAwD;AACxD,MAAM,WAAW,uBAAuB,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM;IACnE,0CAA0C;IAC1C,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAAK,MAAM,CAAA;IACnF,uCAAuC;IACvC,QAAQ,EAAE,IAAI,CAAA;IACd,4EAA4E;IAC5E,cAAc,EAAE,IAAI,CAAA;IACpB,mIAAmI;IACnI,eAAe,CAAC,EAAE,SAAS,CAAA;IAC3B,kFAAkF;IAClF,KAAK,EAAE,SAAS,CAAA;IAChB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,8BAA8B;IAC9B,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,6BAA6B;IAC7B,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,uBAAuB,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"PluginSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PluginSettingsCard.tsx"],"names":[],"mappings":"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"}
|
|
@@ -46,9 +46,10 @@ export function PluginSettingsCard(props) {
|
|
|
46
46
|
// official settings allowlist omits third-party namespaces): show a card
|
|
47
47
|
// that explains the gap instead of vanishing, so a missing card never
|
|
48
48
|
// reads as a missing plugin.
|
|
49
|
-
if (!state.exposed) {
|
|
49
|
+
if (!state.exposed && props.renderChildrenWhenNotExposed !== true) {
|
|
50
|
+
const showNotice = props.hideNotExposedNotice !== true;
|
|
50
51
|
return (_jsxs("li", { className: cardClass, children: [header, expanded
|
|
51
|
-
? (_jsx("div", { className: css.body, children: _jsx("p", { className: css.notExposed, role: "status", children: props.t('settings.notExposed') }) }))
|
|
52
|
+
? (_jsx("div", { className: css.body, children: showNotice ? _jsx("p", { className: css.notExposed, role: "status", children: props.t('settings.notExposed') }) : null }))
|
|
52
53
|
: null] }));
|
|
53
54
|
}
|
|
54
55
|
return (_jsxs("li", { className: cardClass, children: [header, expanded
|
|
@@ -12,13 +12,14 @@ import { type ReactElement } from 'react';
|
|
|
12
12
|
import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
|
|
13
13
|
import type { PetDefinition } from '../registry.ts';
|
|
14
14
|
import type { PetGameplayVerbResult } from '../service.ts';
|
|
15
|
+
import { type PetRoamDirection } from '../gameplay.ts';
|
|
15
16
|
import type { PetStoreInstance } from './pet-store.ts';
|
|
16
17
|
import type { DragStream } from './drag-stream.ts';
|
|
17
18
|
import type { NS } from './locales.ts';
|
|
18
19
|
/** The gameplay verb API the plugin apply body injects (host-authoritative). */
|
|
19
20
|
export interface GameplayApi {
|
|
20
21
|
touch: (zone?: string) => Promise<PetGameplayVerbResult>;
|
|
21
|
-
setMode: (mode:
|
|
22
|
+
setMode: (mode: string | null) => Promise<PetGameplayVerbResult>;
|
|
22
23
|
workTick: () => Promise<PetGameplayVerbResult>;
|
|
23
24
|
buy: (item: string) => Promise<PetGameplayVerbResult>;
|
|
24
25
|
/**
|
|
@@ -54,6 +55,14 @@ export interface GameplayBus {
|
|
|
54
55
|
* while a boolean pins the state (same chrome -> HUD direction as tap).
|
|
55
56
|
*/
|
|
56
57
|
openCard?: (open?: boolean) => void;
|
|
58
|
+
/**
|
|
59
|
+
* Walk the sprite in `direction` by `distance` px at `speed` px/s, clamped
|
|
60
|
+
* to the viewport, and persist the resting spot like a drag. Returns the
|
|
61
|
+
* distance actually travelled (0 when it cannot move), so the caller can
|
|
62
|
+
* hold the walk art for exactly that long. Registered by the chrome
|
|
63
|
+
* (PetSprite); absent while the sprite is unmounted.
|
|
64
|
+
*/
|
|
65
|
+
walk?: (direction: PetRoamDirection, distance: number, speed: number) => number;
|
|
57
66
|
}
|
|
58
67
|
/** The gameplay overlay for one frames2d pet that declares 'gameplay'. */
|
|
59
68
|
export declare function GameplayHud(props: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gameplay-hud.d.ts","sourceRoot":"","sources":["../../../src/client/gameplay-hud.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAsE,KAAK,YAAY,EAAE,MAAM,OAAO,CAAA;AAC7G,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,aAAa,EAAqB,MAAM,gBAAgB,CAAA;AACtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"gameplay-hud.d.ts","sourceRoot":"","sources":["../../../src/client/gameplay-hud.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAsE,KAAK,YAAY,EAAE,MAAM,OAAO,CAAA;AAC7G,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,aAAa,EAAqB,MAAM,gBAAgB,CAAA;AACtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAC1D,OAAO,EAAgE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACpH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGtC,gFAAgF;AAChF,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAA;IACxD,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAA;IAChE,QAAQ,EAAE,MAAM,OAAO,CAAC,qBAAqB,CAAC,CAAA;IAC9C,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAA;IACrD;;;OAGG;IACH,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAChF;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,gFAAgF;IAChF,YAAY,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACnC;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;CAChF;AAYD,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,KAAK,EAAE;IACjC,UAAU,EAAE,aAAa,CAAA;IACzB,KAAK,EAAE,gBAAgB,CAAA;IACvB,GAAG,EAAE,WAAW,CAAA;IAChB,GAAG,EAAE,WAAW,CAAA;IAChB,IAAI,EAAE,UAAU,CAAA;IAChB,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAA;CAC/B,GAAG,YAAY,GAAG,IAAI,CA+jBtB"}
|