@linxin666/dsh-pet 0.3.24 → 0.4.2

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.
Files changed (72) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +4 -3
  3. package/README.zh.md +4 -3
  4. package/icon.svg +3 -0
  5. package/lib/client.js +522 -194
  6. package/lib/client.js.map +1 -1
  7. package/lib/index.js +706 -422
  8. package/lib/live2d-vendor.js +96 -96
  9. package/lib/live2d-vendor.js.map +1 -1
  10. package/lib/types/client/PetDockEntry.d.ts.map +1 -1
  11. package/lib/types/client/PetDockEntry.js +1 -1
  12. package/lib/types/client/PetSettingsCard.d.ts +20 -9
  13. package/lib/types/client/PetSettingsCard.d.ts.map +1 -1
  14. package/lib/types/client/PetSettingsCard.js +116 -5
  15. package/lib/types/client/PetSprite.d.ts +6 -0
  16. package/lib/types/client/PetSprite.d.ts.map +1 -1
  17. package/lib/types/client/PetSprite.js +95 -0
  18. package/lib/types/client/PluginSettingsCard.d.ts +4 -0
  19. package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
  20. package/lib/types/client/PluginSettingsCard.js +3 -2
  21. package/lib/types/client/gameplay-hud.d.ts +10 -1
  22. package/lib/types/client/gameplay-hud.d.ts.map +1 -1
  23. package/lib/types/client/gameplay-hud.js +137 -23
  24. package/lib/types/client/index.d.ts +8 -5
  25. package/lib/types/client/index.d.ts.map +1 -1
  26. package/lib/types/client/index.js +61 -27
  27. package/lib/types/client/main-session.d.ts +33 -0
  28. package/lib/types/client/main-session.d.ts.map +1 -0
  29. package/lib/types/client/main-session.js +34 -0
  30. package/lib/types/client/settings-form.d.ts +17 -17
  31. package/lib/types/client/settings-form.d.ts.map +1 -1
  32. package/lib/types/client/settings-form.js +30 -27
  33. package/lib/types/event-projection.d.ts.map +1 -1
  34. package/lib/types/event-projection.js +5 -3
  35. package/lib/types/gameplay.d.ts +79 -5
  36. package/lib/types/gameplay.d.ts.map +1 -1
  37. package/lib/types/gameplay.js +212 -17
  38. package/lib/types/http.d.ts +18 -1
  39. package/lib/types/http.d.ts.map +1 -1
  40. package/lib/types/http.js +22 -1
  41. package/lib/types/index.d.ts +100 -29
  42. package/lib/types/index.d.ts.map +1 -1
  43. package/lib/types/index.js +113 -64
  44. package/lib/types/persist.d.ts.map +1 -1
  45. package/lib/types/persist.js +3 -1
  46. package/lib/types/routes.d.ts.map +1 -1
  47. package/lib/types/routes.js +3 -1
  48. package/lib/types/service.d.ts +8 -3
  49. package/lib/types/service.d.ts.map +1 -1
  50. package/lib/types/service.js +26 -4
  51. package/package.json +46 -26
  52. package/src/client/PetDockEntry.tsx +1 -0
  53. package/src/client/PetSettingsCard.test.tsx +103 -0
  54. package/src/client/PetSettingsCard.tsx +129 -26
  55. package/src/client/PetSprite.tsx +96 -0
  56. package/src/client/PluginSettingsCard.tsx +7 -2
  57. package/src/client/gameplay-hud.test.tsx +106 -8
  58. package/src/client/gameplay-hud.tsx +143 -28
  59. package/src/client/index.test.tsx +37 -4
  60. package/src/client/index.ts +74 -34
  61. package/src/client/main-session.ts +43 -0
  62. package/src/client/settings-form.ts +34 -31
  63. package/src/event-projection.ts +5 -3
  64. package/src/gameplay.test.ts +138 -1
  65. package/src/gameplay.ts +253 -16
  66. package/src/http.ts +23 -1
  67. package/src/index.test.ts +107 -11
  68. package/src/index.ts +162 -73
  69. package/src/persist.ts +3 -1
  70. package/src/registry.test.ts +3 -2
  71. package/src/routes.ts +3 -1
  72. package/src/service.ts +27 -5
package/lib/types/http.js CHANGED
@@ -2,7 +2,9 @@
2
2
  /**
3
3
  * Shared JSON body/response helpers for the host route families: one strict
4
4
  * bounded body reader, one lenient bounded body reader, one JSON object
5
- * narrow, and one JSON writer. Previously these were copy-pasted across the
5
+ * narrow, one JSON writer, and the outbound request-init helper that keeps a
6
+ * host fetch body decodable (withIdentityEncoding). Previously the readers
7
+ * were copy-pasted across the
6
8
  * package route files (routes.ts, update-routes.ts, mobile-api.ts, and each
7
9
  * family's route module) with drifting contracts: body caps ranging 4 KiB to
8
10
  * 1 MiB and four distinct overflow behaviors (reject, undefined, null, throw).
@@ -80,6 +82,25 @@ function isJsonObject(value) {
80
82
  export function asJsonObject(value) {
81
83
  return isJsonObject(value) ? value : undefined;
82
84
  }
85
+ /**
86
+ * Request init that asks a remote origin for an uncompressed body.
87
+ *
88
+ * The DSH host boots `@deepseek-ai/dsh-http-proxy`, whose top-level import of
89
+ * the npm `undici` copy replaces the legacy `undici.globalDispatcher.1` slot
90
+ * Node's built-in `fetch()` reads. That cross-major wrapper drops the
91
+ * `content-encoding` header and automatic decompression, so a plain fetch
92
+ * resolves to raw gzip/brotli/zstd bytes — compressed noise no JSON or text
93
+ * consumer can read. Every host fetch that parses a remote body must request
94
+ * identity encoding. Caller headers survive; `accept-encoding` is forced to
95
+ * `identity` because a caller value has no decoder behind it in this host.
96
+ * @param init - the caller's request init (signal, headers, method, ...).
97
+ * @returns a copy with `accept-encoding: identity` merged in.
98
+ */
99
+ export function withIdentityEncoding(init = {}) {
100
+ const headers = new Headers(init.headers);
101
+ headers.set('accept-encoding', 'identity');
102
+ return { ...init, headers };
103
+ }
83
104
  /**
84
105
  * Write one JSON response. Default headers are the family defaults
85
106
  * (content-type and referrer-policy); caller headers are appended or
@@ -10,9 +10,9 @@
10
10
  * cordis.patch.yml inserts this plugin row.
11
11
  * @module @linxin666/dsh-pet
12
12
  */
13
- import { Context } from '@deepseek-ai/cordis';
14
- import z from 'schemastery';
15
- import { type PetConfig } from './service.ts';
13
+ import { Context, type Volatile } from '@deepseek-ai/cordis';
14
+ import z from '@deepseek-ai/schemastery';
15
+ import { type PetConfig, type PetSettingsSection } from './service.ts';
16
16
  export { PetService, MAX_SESSION_BUBBLES } from './service.ts';
17
17
  export type { PetConfig, PetInteractResult, PetSettingsSection, PetSessionView, PetStateView, } from './service.ts';
18
18
  export { AFFINITY_MAX, AFFINITY_RANKS, applyInteraction, applyTurnReward, emptyAffinity, rankOf, } from './affinity.ts';
@@ -33,33 +33,104 @@ export declare const name = "pet";
33
33
  /** Services required before the pet can mount its surfaces. */
34
34
  export declare const inject: string[];
35
35
  /**
36
- * Settings section schema: pet selection and display fields the web settings
37
- * surface edits. petId is a plain string on purpose: the service clamps the
38
- * resolved value against the registry, so a stored selection that points at
39
- * a removed pet cannot invalidate the section (a strict union would refuse
40
- * the whole registration). The settings card renders the actual registry
41
- * choices itself from '/api/pet/pets'.
36
+ * Defaults of the fields the pet's settings page edits. They are the schema
37
+ * defaults of the pet row's own config, i.e. what a field the profile entry
38
+ * never set resolves to.
42
39
  */
43
- export declare function makePetSettingsSchema(fallbackPetId: string): z<Schemastery.ObjectS<{
44
- visible: z<boolean, boolean>;
45
- size: z<number, number>;
46
- right: z<number, number>;
47
- bottom: z<number, number>;
48
- bubbleScale: z<number, number>;
49
- petId: z<string, string>;
50
- enabled: z<boolean, boolean>;
51
- decorationEnabled: z<boolean, boolean>;
52
- }>, Schemastery.ObjectT<{
53
- visible: z<boolean, boolean>;
54
- size: z<number, number>;
55
- right: z<number, number>;
56
- bottom: z<number, number>;
57
- bubbleScale: z<number, number>;
58
- petId: z<string, string>;
59
- enabled: z<boolean, boolean>;
60
- decorationEnabled: z<boolean, boolean>;
61
- }>>;
40
+ export declare const PET_FORM_DEFAULTS: {
41
+ readonly visible: true;
42
+ readonly size: 160;
43
+ readonly right: 24;
44
+ readonly bottom: 20;
45
+ readonly bubbleScale: 1;
46
+ readonly petId: "whale-girl";
47
+ readonly enabled: true;
48
+ readonly decorationEnabled: true;
49
+ };
50
+ /**
51
+ * One settings field as the config carries it. The Host commits an edit into
52
+ * the running config through a live reference rather than remounting the row,
53
+ * so a field usually arrives as that reference; a plain value appears when the
54
+ * plugin runs outside a Loader (tests, direct mounts).
55
+ */
56
+ export type LiveField<T> = Volatile<T> | T;
57
+ /** The pet's settings fields, as a profile entry's config declares them. */
58
+ export interface PetFormConfig {
59
+ /** Master switch for the plugin (browser half + host routes). */
60
+ enabled?: LiveField<boolean>;
61
+ /** Status-decoration master switch (pet-center M5, #567); defaults to on. */
62
+ decorationEnabled?: LiveField<boolean>;
63
+ /** Master switch for the pet surface. */
64
+ visible?: LiveField<boolean>;
65
+ /** Scale of the rendered pet in px (sprite cell height). */
66
+ size?: LiveField<number>;
67
+ /** Horizontal inset from the viewport right edge, px. */
68
+ right?: LiveField<number>;
69
+ /** Vertical inset from the viewport bottom edge, px. */
70
+ bottom?: LiveField<number>;
71
+ /** Bubble typography multiplier on the automatic size following (#1549). */
72
+ bubbleScale?: LiveField<number>;
73
+ /** Selected pet id (a registry entry; the service clamps stale values). */
74
+ petId?: LiveField<string | undefined>;
75
+ }
76
+ /**
77
+ * Plugin configuration. Under the 0.1.7 settings model a plugin's own Cordis
78
+ * Config IS its settings page: the Host derives one form per profile entry
79
+ * from this schema, so the fields the pet card edits live here with the
80
+ * defaults the card inherits, next to the tuning block a profile may still
81
+ * declare.
82
+ *
83
+ * Every page field is `volatile()` on purpose. The Host serves exactly the
84
+ * volatile fields of a Config and refuses writes to any other path, and a
85
+ * volatile field is the one it can commit into the RUNNING config: the pet
86
+ * reads the edited value from the live reference instead of being remounted
87
+ * for each edit (see `syncSettings` in `apply`). petId stays a plain string
88
+ * because the service clamps the value against the registry, so a stored
89
+ * selection naming a removed pet cannot invalidate the entry.
90
+ */
91
+ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
92
+ visible: z<boolean, boolean, "volatile-defined">;
93
+ size: z<number, number, "volatile-defined">;
94
+ right: z<number, number, "volatile-defined">;
95
+ bottom: z<number, number, "volatile-defined">;
96
+ bubbleScale: z<number, number, "volatile-defined">;
97
+ petId: z<string, string, "volatile">;
98
+ enabled: z<boolean, boolean, "volatile-defined">;
99
+ decorationEnabled: z<boolean, boolean, "volatile-defined">;
100
+ }>>, Schemastery.ObjectT<NoInfer<{
101
+ visible: z<boolean, boolean, "volatile-defined">;
102
+ size: z<number, number, "volatile-defined">;
103
+ right: z<number, number, "volatile-defined">;
104
+ bottom: z<number, number, "volatile-defined">;
105
+ bubbleScale: z<number, number, "volatile-defined">;
106
+ petId: z<string, string, "volatile">;
107
+ enabled: z<boolean, boolean, "volatile-defined">;
108
+ decorationEnabled: z<boolean, boolean, "volatile-defined">;
109
+ }>>, "plain">;
110
+ /**
111
+ * The settings section the pet runs with: the effective values of the row's
112
+ * own config. `fallbackPetId` covers a mount whose config names no pet at all
113
+ * (a direct mount outside a Loader) — under a Loader the schema default is
114
+ * always present, so the persisted selection stands whenever the config
115
+ * carries it.
116
+ * @param config - the effective config of the pet row.
117
+ * @param fallbackPetId - pet id to use when the config names none.
118
+ * @returns the resolved settings section.
119
+ */
120
+ export declare function petSettingsSection(config: PetFormConfig, fallbackPetId: string): PetSettingsSection;
62
121
  /** Register the pet service and its API + asset routes on the context. */
63
122
  export declare const apply: typeof applyImpl;
64
- declare function applyImpl(ctx: Context, config?: PetConfig): void;
123
+ /** Plugin config: the tuning block a profile may declare plus the pet's settings fields. */
124
+ export type PetPluginConfig = Omit<PetConfig, 'enabled' | 'decorationEnabled'> & PetFormConfig;
125
+ declare function applyImpl(ctx: Context, config?: PetPluginConfig): void;
126
+ declare module '@deepseek-ai/cordis' {
127
+ interface Events {
128
+ /**
129
+ * Volatile config values were committed into the running fiber without a
130
+ * remount; dispatched to the owning fiber only. Declared by the Loader and
131
+ * restated here because this package carries no dependency on its types.
132
+ */
133
+ 'loader/volatile-update'(paths: readonly (readonly string[])[]): void;
134
+ }
135
+ }
65
136
  //# sourceMappingURL=index.d.ts.map
@@ -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;;;;;;;;;;;;;;;;;;IAW1D;AAED,0EAA0E;AAC1E,eAAO,MAAM,KAAK,kBAA6C,CAAA;AAE/D,iBAAS,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,SAAc,GAAG,IAAI,CAkF7D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -10,11 +10,11 @@
10
10
  * cordis.patch.yml inserts this plugin row.
11
11
  * @module @linxin666/dsh-pet
12
12
  */
13
- import z from 'schemastery';
14
- import { PetService, PET_SETTINGS_NAMESPACE } from "./service.js";
13
+ import z from '@deepseek-ai/schemastery';
14
+ import { PetService } from "./service.js";
15
15
  import { makePetRoutes } from "./routes.js";
16
16
  import { loadPetRegistry, petPackageRoot } from "./registry.js";
17
- import { BUBBLE_SCALE_MAX, BUBBLE_SCALE_MIN, DISPLAY_INSET_MAX, DISPLAY_SIZE_MAX, DISPLAY_SIZE_MIN } from "./persist.js";
17
+ import { BUBBLE_SCALE_MAX, BUBBLE_SCALE_MIN, DEFAULT_PET_ID, DISPLAY_INSET_MAX, DISPLAY_SIZE_MAX, DISPLAY_SIZE_MIN } from "./persist.js";
18
18
  import { mountOnce } from "./mount-once.js";
19
19
  export { PetService, MAX_SESSION_BUBBLES } from "./service.js";
20
20
  export { AFFINITY_MAX, AFFINITY_RANKS, applyInteraction, applyTurnReward, emptyAffinity, rankOf, } from "./affinity.js";
@@ -29,24 +29,80 @@ export const name = 'pet';
29
29
  /** Services required before the pet can mount its surfaces. */
30
30
  export const inject = ['webServer'];
31
31
  /**
32
- * Settings section schema: pet selection and display fields the web settings
33
- * surface edits. petId is a plain string on purpose: the service clamps the
34
- * resolved value against the registry, so a stored selection that points at
35
- * a removed pet cannot invalidate the section (a strict union would refuse
36
- * the whole registration). The settings card renders the actual registry
37
- * choices itself from '/api/pet/pets'.
32
+ * Defaults of the fields the pet's settings page edits. They are the schema
33
+ * defaults of the pet row's own config, i.e. what a field the profile entry
34
+ * never set resolves to.
38
35
  */
39
- export function makePetSettingsSchema(fallbackPetId) {
40
- return z.object({
41
- visible: z.boolean().default(true),
42
- size: z.number().step(1).min(DISPLAY_SIZE_MIN).max(DISPLAY_SIZE_MAX).default(160),
43
- right: z.number().step(1).min(0).max(DISPLAY_INSET_MAX).default(24),
44
- bottom: z.number().step(1).min(0).max(DISPLAY_INSET_MAX).default(20),
45
- bubbleScale: z.number().step(0.05).min(BUBBLE_SCALE_MIN).max(BUBBLE_SCALE_MAX).default(1),
46
- petId: z.string().default(fallbackPetId),
47
- enabled: z.boolean().default(true),
48
- decorationEnabled: z.boolean().default(true),
49
- });
36
+ export const PET_FORM_DEFAULTS = {
37
+ visible: true,
38
+ size: 160,
39
+ right: 24,
40
+ bottom: 20,
41
+ bubbleScale: 1,
42
+ petId: DEFAULT_PET_ID,
43
+ enabled: true,
44
+ decorationEnabled: true,
45
+ };
46
+ /**
47
+ * Plugin configuration. Under the 0.1.7 settings model a plugin's own Cordis
48
+ * Config IS its settings page: the Host derives one form per profile entry
49
+ * from this schema, so the fields the pet card edits live here with the
50
+ * defaults the card inherits, next to the tuning block a profile may still
51
+ * declare.
52
+ *
53
+ * Every page field is `volatile()` on purpose. The Host serves exactly the
54
+ * volatile fields of a Config and refuses writes to any other path, and a
55
+ * volatile field is the one it can commit into the RUNNING config: the pet
56
+ * reads the edited value from the live reference instead of being remounted
57
+ * for each edit (see `syncSettings` in `apply`). petId stays a plain string
58
+ * because the service clamps the value against the registry, so a stored
59
+ * selection naming a removed pet cannot invalidate the entry.
60
+ */
61
+ export const Config = z.object({
62
+ visible: z.boolean().default(PET_FORM_DEFAULTS.visible).volatile(),
63
+ size: z.number().step(1).min(DISPLAY_SIZE_MIN).max(DISPLAY_SIZE_MAX).default(PET_FORM_DEFAULTS.size).volatile(),
64
+ right: z.number().step(1).min(0).max(DISPLAY_INSET_MAX).default(PET_FORM_DEFAULTS.right).volatile(),
65
+ bottom: z.number().step(1).min(0).max(DISPLAY_INSET_MAX).default(PET_FORM_DEFAULTS.bottom).volatile(),
66
+ bubbleScale: z.number().step(0.05).min(BUBBLE_SCALE_MIN).max(BUBBLE_SCALE_MAX).default(PET_FORM_DEFAULTS.bubbleScale).volatile(),
67
+ // An absent profile choice must leave the selection persisted in pet.json
68
+ // intact across restarts (aggregate rows have no served Host pet form).
69
+ petId: z.string().volatile(),
70
+ enabled: z.boolean().default(PET_FORM_DEFAULTS.enabled).volatile(),
71
+ decorationEnabled: z.boolean().default(PET_FORM_DEFAULTS.decorationEnabled).volatile(),
72
+ });
73
+ /**
74
+ * Read one live config field.
75
+ * @param field - the field's live reference, plain value, or nothing at all.
76
+ * @param fallback - value used when the field is absent.
77
+ * @returns the current field value.
78
+ */
79
+ function readLive(field, fallback) {
80
+ if (field === undefined)
81
+ return fallback;
82
+ const ref = field;
83
+ return typeof ref.get === 'function' ? ref.get() ?? fallback : field;
84
+ }
85
+ /**
86
+ * The settings section the pet runs with: the effective values of the row's
87
+ * own config. `fallbackPetId` covers a mount whose config names no pet at all
88
+ * (a direct mount outside a Loader) — under a Loader the schema default is
89
+ * always present, so the persisted selection stands whenever the config
90
+ * carries it.
91
+ * @param config - the effective config of the pet row.
92
+ * @param fallbackPetId - pet id to use when the config names none.
93
+ * @returns the resolved settings section.
94
+ */
95
+ export function petSettingsSection(config, fallbackPetId) {
96
+ return {
97
+ visible: readLive(config.visible, PET_FORM_DEFAULTS.visible),
98
+ size: readLive(config.size, PET_FORM_DEFAULTS.size),
99
+ right: readLive(config.right, PET_FORM_DEFAULTS.right),
100
+ bottom: readLive(config.bottom, PET_FORM_DEFAULTS.bottom),
101
+ bubbleScale: readLive(config.bubbleScale, PET_FORM_DEFAULTS.bubbleScale),
102
+ petId: readLive(config.petId, fallbackPetId),
103
+ enabled: readLive(config.enabled, PET_FORM_DEFAULTS.enabled),
104
+ decorationEnabled: readLive(config.decorationEnabled, PET_FORM_DEFAULTS.decorationEnabled),
105
+ };
50
106
  }
51
107
  /** Register the pet service and its API + asset routes on the context. */
52
108
  export const apply = mountOnce('@linxin666/dsh-pet', applyImpl);
@@ -56,24 +112,18 @@ function applyImpl(ctx, config = {}) {
56
112
  packageRoot: petPackageRoot(import.meta.url),
57
113
  ...(config.pets === undefined ? {} : { extra: config.pets }),
58
114
  });
59
- const service = new PetService(ctx, { ...config, registry });
60
- // The settings surface edits the pet selection + display config through
61
- // the 'pet' namespace. The composition 'base' starts as the persisted
62
- // pet.json values (clamped to schema bounds), so an empty user layer
63
- // resolves to exactly what the pet already shows — a fresh deployment
64
- // never overwrites a customized layout, and reset re-inherits it. Runtime
65
- // drag interactions mirror back into the settings document through the
66
- // service (see syncSettingsFromPet), keeping both views consistent.
67
- let current = () => base;
68
- const base = {
69
- visible: service.display().visible,
70
- size: service.display().size,
71
- right: service.display().right,
72
- bottom: service.display().bottom,
73
- petId: service.selectedPetId(),
74
- enabled: config.enabled ?? true,
75
- decorationEnabled: config.decorationEnabled ?? true,
76
- };
115
+ const service = new PetService(ctx, {
116
+ ...config,
117
+ enabled: readLive(config.enabled, PET_FORM_DEFAULTS.enabled),
118
+ decorationEnabled: readLive(config.decorationEnabled, PET_FORM_DEFAULTS.decorationEnabled),
119
+ registry,
120
+ });
121
+ // The effective settings ARE this row's own config: the Host serves one form
122
+ // per profile entry from `Config` above, and every edit is committed into the
123
+ // running config, so the plugin re-reads the section here instead of holding
124
+ // a separate settings document (0.1.6 registered one through
125
+ // settings.installSection/register and re-resolved it on every change).
126
+ const current = () => petSettingsSection(config, service.selectedPetId());
77
127
  // The browser half talks to the pet through same-origin JSON endpoints and
78
128
  // loads each pet's atlas from the registry's own media route (RPC domains
79
129
  // are platform-registered, so the pet serves its own API — the same
@@ -96,34 +146,33 @@ function applyImpl(ctx, config = {}) {
96
146
  disposeRoutes = undefined;
97
147
  }
98
148
  };
149
+ // Apply the row's config to the running service: pet.json mirrors the live
150
+ // display the drag / hide / summon interactions write (see
151
+ // syncSettingsFromPet), so a section the settings surface never touched
152
+ // resolves back to exactly what the pet already shows (the composition
153
+ // 'base' of the pre-0.1.7 section).
154
+ const syncSettings = () => {
155
+ const section = current();
156
+ service.applySettingsSection(section);
157
+ service.setEnabled(section.enabled ?? true);
158
+ syncRoutes();
159
+ };
160
+ // A settings edit lands in this row's live config reference and is announced
161
+ // here; the entry is NOT remounted for it, so the pet applies the new section
162
+ // itself (the pre-0.1.7 host ran the section's onChange hook for this).
163
+ ctx.on('loader/volatile-update', () => { syncSettings(); });
99
164
  ctx.inject(['settings'], (settingsCtx) => {
100
- try {
101
- const schema = makePetSettingsSchema(service.selectedPetId());
102
- if (typeof settingsCtx.settings?.installSection === 'function') {
103
- settingsCtx.settings.installSection(ctx, PET_SETTINGS_NAMESPACE, schema, base, {
104
- setSource: (source) => { current = source; },
105
- onChange: () => {
106
- const section = current();
107
- service.applySettingsSection(section);
108
- service.setEnabled(section.enabled ?? true);
109
- syncRoutes();
110
- },
111
- });
165
+ // The pet ships its own settings card (the browser half's
166
+ // 'settings.section' page), so the Host must not also generate a page for
167
+ // this entry from Config.
168
+ settingsCtx.effect(() => {
169
+ try {
170
+ return settingsCtx.settings.configure({ auto: false }, ctx.fiber);
112
171
  }
113
- else if (typeof settingsCtx.settings?.register === 'function') {
114
- const scope = settingsCtx.settings.register(PET_SETTINGS_NAMESPACE, schema, { base });
115
- current = () => scope?.get?.() ?? base;
116
- scope?.watch?.(() => {
117
- const section = current();
118
- service.applySettingsSection(section);
119
- service.setEnabled(section.enabled ?? true);
120
- syncRoutes();
121
- });
172
+ catch {
173
+ return () => { };
122
174
  }
123
- }
124
- catch {
125
- // Defensive fallback against settings registration differences
126
- }
175
+ }, 'pet: settings page policy');
127
176
  });
128
- syncRoutes();
177
+ syncSettings();
129
178
  }
@@ -1 +1 @@
1
- {"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../src/persist.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAA+B,KAAK,aAAa,EAAE,MAAM,eAAe,CAAA;AAC/E,OAAO,EAAwC,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AAEpF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAErD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAEhE,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAC/B,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;;;;;OAKG;IACH,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,eAAO,MAAM,oBAAoB,EAAE,gBAMlC,CAAA;AAED,2EAA2E;AAC3E,eAAO,MAAM,gBAAgB,KAAK,CAAA;AAClC,eAAO,MAAM,gBAAgB,OAAO,CAAA;AACpC,eAAO,MAAM,iBAAiB,QAAS,CAAA;AAEvC,8CAA8C;AAC9C,eAAO,MAAM,gBAAgB,MAAM,CAAA;AACnC,eAAO,MAAM,gBAAgB,IAAI,CAAA;AACjC,+EAA+E;AAC/E,eAAO,MAAM,mBAAmB,KAAK,CAAA;AACrC,8EAA8E;AAC9E,eAAO,MAAM,mBAAmB,MAAM,CAAA;AACtC,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,KAAK,CAAA;AACpC,eAAO,MAAM,kBAAkB,KAAK,CAAA;AAEpC;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAWzG;AAED,wCAAwC;AACxC,MAAM,WAAW,UAAU;IACzB,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,QAAQ,EAAE,aAAa,CAAA;IACvB,gCAAgC;IAChC,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,EAAE,gBAAgB,CAAA;IACzB,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;CAC3C;AAED,wBAAwB;AACxB,eAAO,MAAM,mBAAmB,KAAK,CAAA;AAErC,wBAAgB,YAAY,IAAI,UAAU,CAUzC;AAED;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAmFD,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAqB,GAAG,UAAU,CAuDrE;AAED,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,GAAE,MAAqB,GAAG,IAAI,CAMjF"}
1
+ {"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../src/persist.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAA+B,KAAK,aAAa,EAAE,MAAM,eAAe,CAAA;AAC/E,OAAO,EAAwC,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AAEpF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAErD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAEhE,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAC/B,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;;;;;OAKG;IACH,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,eAAO,MAAM,oBAAoB,EAAE,gBAMlC,CAAA;AAED,2EAA2E;AAC3E,eAAO,MAAM,gBAAgB,KAAK,CAAA;AAClC,eAAO,MAAM,gBAAgB,OAAO,CAAA;AACpC,eAAO,MAAM,iBAAiB,QAAS,CAAA;AAEvC,8CAA8C;AAC9C,eAAO,MAAM,gBAAgB,MAAM,CAAA;AACnC,eAAO,MAAM,gBAAgB,IAAI,CAAA;AACjC,+EAA+E;AAC/E,eAAO,MAAM,mBAAmB,KAAK,CAAA;AACrC,8EAA8E;AAC9E,eAAO,MAAM,mBAAmB,MAAM,CAAA;AACtC,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,KAAK,CAAA;AACpC,eAAO,MAAM,kBAAkB,KAAK,CAAA;AAEpC;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAWzG;AAED,wCAAwC;AACxC,MAAM,WAAW,UAAU;IACzB,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,QAAQ,EAAE,aAAa,CAAA;IACvB,gCAAgC;IAChC,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,EAAE,gBAAgB,CAAA;IACzB,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;CAC3C;AAED,wBAAwB;AACxB,eAAO,MAAM,mBAAmB,KAAK,CAAA;AAErC,wBAAgB,YAAY,IAAI,UAAU,CAUzC;AAED;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAqFD,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAqB,GAAG,UAAU,CAuDrE;AAED,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,GAAE,MAAqB,GAAG,IAAI,CAMjF"}
@@ -146,7 +146,9 @@ function loadGameplay(parsed) {
146
146
  const item = {
147
147
  stats,
148
148
  currencies,
149
- mode: record.mode === 'work' || record.mode === 'sleep' ? record.mode : null,
149
+ // Any non-empty id survives the round trip; whether it is still declared
150
+ // is settled against the manifest when the mode is read back.
151
+ mode: typeof record.mode === 'string' && record.mode.length > 0 && record.mode.length <= 24 ? record.mode : null,
150
152
  settledAt: clamp(finiteNum(record.settledAt, 0), Number.MAX_SAFE_INTEGER),
151
153
  };
152
154
  if (typeof record.incomeCarryMs === 'number' && Number.isFinite(record.incomeCarryMs)) {
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAA;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAO9C,+CAA+C;AAC/C,eAAO,MAAM,cAAc,aAAa,CAAA;AAExC,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,SAAS,CAAA;AAMtC;;;;GAIG;AACH,eAAO,MAAM,cAAc;IACzB,yBAAyB;;IAEzB,iDAAiD;;IAEjD,8EAA8E;;CAEtE,CAAA;AAEV,6DAA6D;AAC7D,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd;AAsBD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAcrF;AAqPD,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB,QAA8B,CAAA;AAe7D,4EAA4E;AAC5E,eAAO,MAAM,eAAe,QAAmB,CAAA;AAE/C,6EAA6E;AAC7E,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AA4MD,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,UAAU,CAAC;IAAC,GAAG,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,YAAY,CAAA;CAAE,GAAG,eAAe,GAAG,QAAQ,EAAE,CAqFjI;AAGD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAA;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAO9C,+CAA+C;AAC/C,eAAO,MAAM,cAAc,aAAa,CAAA;AAExC,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,SAAS,CAAA;AAMtC;;;;GAIG;AACH,eAAO,MAAM,cAAc;IACzB,yBAAyB;;IAEzB,iDAAiD;;IAEjD,8EAA8E;;CAEtE,CAAA;AAEV,6DAA6D;AAC7D,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd;AAsBD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAcrF;AAqPD,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB,QAA8B,CAAA;AAe7D,4EAA4E;AAC5E,eAAO,MAAM,eAAe,QAAmB,CAAA;AAE/C,6EAA6E;AAC7E,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AA4MD,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,UAAU,CAAC;IAAC,GAAG,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,YAAY,CAAA;CAAE,GAAG,eAAe,GAAG,QAAQ,EAAE,CAuFjI;AAGD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA"}
@@ -609,7 +609,9 @@ export function makePetRoutes(deps) {
609
609
  }),
610
610
  postRoute(ctx, PET_API_PREFIX + '/gameplay/mode', (body) => {
611
611
  const mode = body.mode;
612
- if (mode !== null && mode !== 'work' && mode !== 'sleep')
612
+ // The id is checked against the pet's own mode set in the service (work,
613
+ // sleep, or any declared 'modes' entry).
614
+ if (mode !== null && typeof mode !== 'string')
613
615
  return Promise.reject(new Error('invalid-mode'));
614
616
  return service.gameplaySetMode(mode);
615
617
  }),
@@ -157,7 +157,8 @@ export type PetInteractResult = LedgerInteractionResult;
157
157
  export interface PetGameplayStateView {
158
158
  /** Stat values rounded for display. */
159
159
  stats: Record<string, number>;
160
- mode: 'work' | 'sleep' | null;
160
+ /** Active mode id ('work' | 'sleep' | a declared extra mode) or null. */
161
+ mode: string | null;
161
162
  }
162
163
  /** Result of the gameplay verbs (touch / setMode / workTick / buy). */
163
164
  export interface PetGameplayVerbResult {
@@ -305,8 +306,12 @@ export declare class PetService extends Service {
305
306
  * omitted means a plain click while a touch animation holds (clickBoost).
306
307
  */
307
308
  gameplayTouch(zone?: string): Promise<PetGameplayVerbResult>;
308
- /** RPC: enter or leave a gameplay mode ('work' | 'sleep' | null). */
309
- gameplaySetMode(mode: 'work' | 'sleep' | null): Promise<PetGameplayVerbResult>;
309
+ /**
310
+ * RPC: enter or leave a gameplay mode (null clears it). Every mode the
311
+ * manifest declares is accepted: 'work', 'sleep', or one of the extra
312
+ * 'modes' entries (a bath, a play session…).
313
+ */
314
+ gameplaySetMode(mode: string | null): Promise<PetGameplayVerbResult>;
310
315
  /** RPC: one work-round adjudication (only while the work mode holds). */
311
316
  gameplayWorkTick(): Promise<PetGameplayVerbResult>;
312
317
  /** RPC: buy one shop item (effects, currency swap, or a lottery draw). */
@@ -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":"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"}
@@ -20,7 +20,7 @@ import { DEFAULT_DECORATION_ID, decorationView, loadPetRegistry, petEntryView, p
20
20
  import { WHISPER_TTL_MS } from "./chatter.js";
21
21
  import { mergeVoicePacks } from "./voice-pack.js";
22
22
  import { defaultPetStateConfig, PetStateMachine, } from "./state.js";
23
- import { applyGameplayEffects, drawLotteryTier, initialGameplayState, rollTouchBranch, rollWorkOutcome, settleGameplay, } from "./gameplay.js";
23
+ import { applyGameplayEffects, drawLotteryTier, initialGameplayState, isDeclaredMode, rollTouchBranch, rollWorkOutcome, settleGameplay, } from "./gameplay.js";
24
24
  /** The unified gameplay currency: the shared treat (小鱼干) ledger. */
25
25
  const GAMEPLAY_TREATS_CURRENCY = 'treats';
26
26
  /** Settings namespace of the pet capability. Spelled here rather than imported: the browser half spells the same value. */
@@ -344,7 +344,12 @@ export class PetService extends Service {
344
344
  petId,
345
345
  state: stored === undefined
346
346
  ? initialGameplayState(def, now)
347
- : { stats: { ...stored.stats }, currencies: { ...stored.currencies }, mode: stored.mode, settledAt: stored.settledAt },
347
+ // Spread first so the settle remainders (restoreCarryMs / incomeCarryMs)
348
+ // survive a verb. Enumerating the fields dropped them, so every verb
349
+ // floored the elapsed time and threw the remainder away -- which starves
350
+ // any interval longer than the gap between verbs (30 s sleep restore,
351
+ // 30 min passive income).
352
+ : { ...stored, stats: { ...stored.stats }, currencies: { ...stored.currencies } },
348
353
  };
349
354
  }
350
355
  /** Display view of one gameplay state (rounded stats; treats ride the shared treat ledger). */
@@ -352,7 +357,14 @@ export class PetService extends Service {
352
357
  const stats = {};
353
358
  for (const [name, value] of Object.entries(state.stats))
354
359
  stats[name] = Math.round(value);
355
- return { stats, mode: state.mode };
360
+ // A persisted mode the manifest no longer declares (a pet.json edit after
361
+ // the pet was parked in it) reads as "no mode". Otherwise the client would
362
+ // latch a mode it cannot resolve: the mode chip prints a raw i18n key, and
363
+ // the client's roam roll — suppressed while any mode is active — would
364
+ // never fire again.
365
+ const def = this.gameplayDef();
366
+ const mode = state.mode !== null && def !== undefined && !isDeclaredMode(def, state.mode) ? null : state.mode;
367
+ return { stats, mode };
356
368
  }
357
369
  /**
358
370
  * Move gameplay 'treats' currency (the unified post-wallet currency) from
@@ -419,7 +431,11 @@ export class PetService extends Service {
419
431
  view: this.gameplayViewOf(state),
420
432
  };
421
433
  }
422
- /** RPC: enter or leave a gameplay mode ('work' | 'sleep' | null). */
434
+ /**
435
+ * RPC: enter or leave a gameplay mode (null clears it). Every mode the
436
+ * manifest declares is accepted: 'work', 'sleep', or one of the extra
437
+ * 'modes' entries (a bath, a play session…).
438
+ */
423
439
  async gameplaySetMode(mode) {
424
440
  const def = this.gameplayDef();
425
441
  if (def === undefined)
@@ -428,9 +444,15 @@ export class PetService extends Service {
428
444
  return { ok: false, error: 'no-work' };
429
445
  if (mode === 'sleep' && def.sleep === undefined)
430
446
  return { ok: false, error: 'no-sleep' };
447
+ if (mode !== null && !isDeclaredMode(def, mode))
448
+ return { ok: false, error: 'unknown-mode' };
431
449
  const now = Date.now();
432
450
  const { petId, state } = this.gameplayState(def, now);
433
451
  settleGameplay(state, def, now, { sessionActive: this.machine.render().sessionActive });
452
+ // Switching modes restarts the restore cadence: a leftover carry from the
453
+ // previous mode must not advance the new mode's first tick.
454
+ if (state.mode !== mode)
455
+ state.restoreCarryMs = 0;
434
456
  state.mode = mode;
435
457
  this.drainGameplayTreats(state);
436
458
  this.commitGameplay(petId, state);