@linxin666/dsh-pet 0.4.3 → 0.4.4
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/lib/client.js +1 -1
- package/lib/index.js +84 -9
- package/lib/types/index.d.ts +6 -2
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +82 -10
- package/lib/types/service.d.ts +9 -0
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +11 -1
- package/package.json +1 -1
- package/src/index.test.ts +39 -0
- package/src/index.ts +105 -11
- package/src/service.ts +11 -1
package/lib/client.js
CHANGED
package/lib/index.js
CHANGED
|
@@ -4638,6 +4638,15 @@ var PetService = class extends Service {
|
|
|
4638
4638
|
* the same turn again after the pet is disabled and re-enabled.
|
|
4639
4639
|
*/
|
|
4640
4640
|
officialEventSessions = /* @__PURE__ */ new WeakSet();
|
|
4641
|
+
/**
|
|
4642
|
+
* Resolve the settings namespace this plugin's own config is served as, when
|
|
4643
|
+
* the caller can read it off the composition. A plugin's settings ARE its own
|
|
4644
|
+
* config, so the namespace is the row's id — and an aggregate bundle renames
|
|
4645
|
+
* child rows (`pet` becomes `web-ui-pet`), which is why it cannot be assumed
|
|
4646
|
+
* to be {@link PET_SETTINGS_NAMESPACE}. `apply` sets this; absent means the
|
|
4647
|
+
* plugin runs outside a Loader and the package's own id stands.
|
|
4648
|
+
*/
|
|
4649
|
+
settingsNamespaceProvider = void 0;
|
|
4641
4650
|
constructor(ctx, config = {}) {
|
|
4642
4651
|
super(ctx, "pet");
|
|
4643
4652
|
this.persistDir = config.persistDir ?? petHomeDir();
|
|
@@ -5194,8 +5203,9 @@ var PetService = class extends Service {
|
|
|
5194
5203
|
syncSettingsFromPet() {
|
|
5195
5204
|
const settings = this.ctx.get("settings", false);
|
|
5196
5205
|
if (settings === void 0) return;
|
|
5206
|
+
const ns = this.settingsNamespaceProvider?.() ?? "pet";
|
|
5197
5207
|
const snapshot = this.ledger.snapshot;
|
|
5198
|
-
settings.update(
|
|
5208
|
+
settings.update(ns, {
|
|
5199
5209
|
visible: snapshot.display.visible,
|
|
5200
5210
|
size: snapshot.display.size,
|
|
5201
5211
|
right: snapshot.display.right,
|
|
@@ -6171,27 +6181,91 @@ function readLive(field, fallback) {
|
|
|
6171
6181
|
return typeof ref.get === "function" ? ref.get() ?? fallback : field;
|
|
6172
6182
|
}
|
|
6173
6183
|
/**
|
|
6184
|
+
* Resolve one display field of the section. The precedence is deliberate:
|
|
6185
|
+
*
|
|
6186
|
+
* 1. an explicit profile value (`committed`) — the user's own choice, and the
|
|
6187
|
+
* only signal that carries a value equal to the schema default on purpose;
|
|
6188
|
+
* 2. a live value the row committed at runtime, i.e. one that differs from the
|
|
6189
|
+
* schema default, which is all an untouched field ever resolves to;
|
|
6190
|
+
* 3. what the pet's own `pet.json` already holds (`persisted`), so a field the
|
|
6191
|
+
* profile never touched keeps the position the user dragged instead of
|
|
6192
|
+
* snapping back to the schema default on the next mount.
|
|
6193
|
+
*
|
|
6194
|
+
* Step 3 is what makes a drag survive a restart. The display fields carry
|
|
6195
|
+
* schema defaults (`right: 24`, `bottom: 20` — the bottom-right corner), and a
|
|
6196
|
+
* config that never set them reads back exactly like one that set them to the
|
|
6197
|
+
* defaults, so without `persisted` the startup section resets the layout every
|
|
6198
|
+
* time the profile commits none — which is what an aggregate install does.
|
|
6199
|
+
* @param key - the display field to resolve.
|
|
6200
|
+
* @param live - the row config's own field, absent when the entry declares none.
|
|
6201
|
+
* @param persisted - the display the pet already shows (its own `pet.json`).
|
|
6202
|
+
* @param committed - the profile layer's explicit display values, when known.
|
|
6203
|
+
* @returns the value the pet runs with.
|
|
6204
|
+
*/
|
|
6205
|
+
function displayField(key, live, persisted, committed) {
|
|
6206
|
+
const explicit = committed[key];
|
|
6207
|
+
if (explicit !== void 0) return explicit;
|
|
6208
|
+
const value = readLive(live, PET_FORM_DEFAULTS[key]);
|
|
6209
|
+
if (value !== PET_FORM_DEFAULTS[key]) return value;
|
|
6210
|
+
const kept = persisted[key];
|
|
6211
|
+
return kept === void 0 ? value : kept;
|
|
6212
|
+
}
|
|
6213
|
+
/**
|
|
6174
6214
|
* The settings section the pet runs with: the effective values of the row's
|
|
6175
6215
|
* own config. `fallbackPetId` covers a mount whose config names no pet at all
|
|
6176
6216
|
* (a direct mount outside a Loader) — under a Loader the schema default is
|
|
6177
6217
|
* always present, so the persisted selection stands whenever the config
|
|
6178
|
-
* carries it.
|
|
6218
|
+
* carries it. `persisted` and `committed` give the display fields the same
|
|
6219
|
+
* treatment; see {@link displayField}.
|
|
6179
6220
|
* @param config - the effective config of the pet row.
|
|
6180
6221
|
* @param fallbackPetId - pet id to use when the config names none.
|
|
6222
|
+
* @param persisted - the display the pet already shows (its own `pet.json`).
|
|
6223
|
+
* @param committed - the profile layer's explicit display values, when known.
|
|
6181
6224
|
* @returns the resolved settings section.
|
|
6182
6225
|
*/
|
|
6183
|
-
function petSettingsSection(config, fallbackPetId) {
|
|
6226
|
+
function petSettingsSection(config, fallbackPetId, persisted = {}, committed = {}) {
|
|
6184
6227
|
return {
|
|
6185
|
-
visible:
|
|
6186
|
-
size:
|
|
6187
|
-
right:
|
|
6188
|
-
bottom:
|
|
6189
|
-
bubbleScale:
|
|
6228
|
+
visible: displayField("visible", config.visible, persisted, committed),
|
|
6229
|
+
size: displayField("size", config.size, persisted, committed),
|
|
6230
|
+
right: displayField("right", config.right, persisted, committed),
|
|
6231
|
+
bottom: displayField("bottom", config.bottom, persisted, committed),
|
|
6232
|
+
bubbleScale: displayField("bubbleScale", config.bubbleScale, persisted, committed),
|
|
6190
6233
|
petId: readLive(config.petId, fallbackPetId),
|
|
6191
6234
|
enabled: readLive(config.enabled, PET_FORM_DEFAULTS.enabled),
|
|
6192
6235
|
decorationEnabled: readLive(config.decorationEnabled, PET_FORM_DEFAULTS.decorationEnabled)
|
|
6193
6236
|
};
|
|
6194
6237
|
}
|
|
6238
|
+
/** The settings row ids a pet install is served under, most specific first. */
|
|
6239
|
+
const PET_SETTINGS_ROW_IDS = [
|
|
6240
|
+
"web-ui-pet",
|
|
6241
|
+
"ui-pet",
|
|
6242
|
+
"pet"
|
|
6243
|
+
];
|
|
6244
|
+
/**
|
|
6245
|
+
* The settings row serving this plugin's own config, or undefined when the
|
|
6246
|
+
* settings service is not mounted (a direct mount) or serves no row for the pet.
|
|
6247
|
+
*
|
|
6248
|
+
* Every settings write has to address this row's id, and that id is not the
|
|
6249
|
+
* package's plugin name: an aggregate bundle renames child rows (`pet` becomes
|
|
6250
|
+
* `web-ui-pet`). The browser half resolves the same id through its own
|
|
6251
|
+
* `servedEntryId`, so both halves write to one namespace.
|
|
6252
|
+
* @param ctx - the plugin's context.
|
|
6253
|
+
* @returns the serving row, when there is one.
|
|
6254
|
+
*/
|
|
6255
|
+
function servedRow(ctx) {
|
|
6256
|
+
try {
|
|
6257
|
+
const settings = ctx.get("settings", false);
|
|
6258
|
+
if (settings === void 0) return void 0;
|
|
6259
|
+
const rows = settings.describe();
|
|
6260
|
+
for (const id of PET_SETTINGS_ROW_IDS) {
|
|
6261
|
+
const row = rows.find((candidate) => candidate.ns === id);
|
|
6262
|
+
if (row !== void 0) return row;
|
|
6263
|
+
}
|
|
6264
|
+
return;
|
|
6265
|
+
} catch {
|
|
6266
|
+
return;
|
|
6267
|
+
}
|
|
6268
|
+
}
|
|
6195
6269
|
/** Register the pet service and its API + asset routes on the context. */
|
|
6196
6270
|
const apply = mountOnce("@linxin666/dsh-pet", applyImpl);
|
|
6197
6271
|
function applyImpl(ctx, config = {}) {
|
|
@@ -6205,7 +6279,8 @@ function applyImpl(ctx, config = {}) {
|
|
|
6205
6279
|
decorationEnabled: readLive(config.decorationEnabled, PET_FORM_DEFAULTS.decorationEnabled),
|
|
6206
6280
|
registry
|
|
6207
6281
|
});
|
|
6208
|
-
|
|
6282
|
+
service.settingsNamespaceProvider = () => servedRow(ctx)?.ns;
|
|
6283
|
+
const current = () => petSettingsSection(config, service.selectedPetId(), service.display(), servedRow(ctx)?.user);
|
|
6209
6284
|
const routes = makePetRoutes({
|
|
6210
6285
|
service,
|
|
6211
6286
|
ctx
|
package/lib/types/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { Context, type Volatile } from '@deepseek-ai/cordis';
|
|
14
14
|
import z from '@deepseek-ai/schemastery';
|
|
15
15
|
import { type PetConfig, type PetSettingsSection } from './service.ts';
|
|
16
|
+
import { type PetDisplayConfig } from './persist.ts';
|
|
16
17
|
export { PetService, MAX_SESSION_BUBBLES } from './service.ts';
|
|
17
18
|
export type { PetConfig, PetInteractResult, PetSettingsSection, PetSessionView, PetStateView, } from './service.ts';
|
|
18
19
|
export { AFFINITY_MAX, AFFINITY_RANKS, applyInteraction, applyTurnReward, emptyAffinity, rankOf, } from './affinity.ts';
|
|
@@ -112,12 +113,15 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
|
112
113
|
* own config. `fallbackPetId` covers a mount whose config names no pet at all
|
|
113
114
|
* (a direct mount outside a Loader) — under a Loader the schema default is
|
|
114
115
|
* always present, so the persisted selection stands whenever the config
|
|
115
|
-
* carries it.
|
|
116
|
+
* carries it. `persisted` and `committed` give the display fields the same
|
|
117
|
+
* treatment; see {@link displayField}.
|
|
116
118
|
* @param config - the effective config of the pet row.
|
|
117
119
|
* @param fallbackPetId - pet id to use when the config names none.
|
|
120
|
+
* @param persisted - the display the pet already shows (its own `pet.json`).
|
|
121
|
+
* @param committed - the profile layer's explicit display values, when known.
|
|
118
122
|
* @returns the resolved settings section.
|
|
119
123
|
*/
|
|
120
|
-
export declare function petSettingsSection(config: PetFormConfig, fallbackPetId: string): PetSettingsSection;
|
|
124
|
+
export declare function petSettingsSection(config: PetFormConfig, fallbackPetId: string, persisted?: Partial<PetDisplayConfig>, committed?: Partial<PetDisplayConfig>): PetSettingsSection;
|
|
121
125
|
/** Register the pet service and its API + asset routes on the context. */
|
|
122
126
|
export declare const apply: typeof applyImpl;
|
|
123
127
|
/** Plugin config: the tuning block a profile may declare plus the pet's settings fields. */
|
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,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAI5D,OAAO,CAAC,MAAM,0BAA0B,CAAA;AACxC,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
package/lib/types/index.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* @module @linxin666/dsh-pet
|
|
12
12
|
*/
|
|
13
13
|
import z from '@deepseek-ai/schemastery';
|
|
14
|
-
import { PetService } from "./service.js";
|
|
14
|
+
import { PET_SETTINGS_NAMESPACE, PetService } from "./service.js";
|
|
15
15
|
import { makePetRoutes } from "./routes.js";
|
|
16
16
|
import { loadPetRegistry, petPackageRoot } from "./registry.js";
|
|
17
17
|
import { BUBBLE_SCALE_MAX, BUBBLE_SCALE_MIN, DEFAULT_PET_ID, DISPLAY_INSET_MAX, DISPLAY_SIZE_MAX, DISPLAY_SIZE_MIN } from "./persist.js";
|
|
@@ -82,28 +82,93 @@ function readLive(field, fallback) {
|
|
|
82
82
|
const ref = field;
|
|
83
83
|
return typeof ref.get === 'function' ? ref.get() ?? fallback : field;
|
|
84
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Resolve one display field of the section. The precedence is deliberate:
|
|
87
|
+
*
|
|
88
|
+
* 1. an explicit profile value (`committed`) — the user's own choice, and the
|
|
89
|
+
* only signal that carries a value equal to the schema default on purpose;
|
|
90
|
+
* 2. a live value the row committed at runtime, i.e. one that differs from the
|
|
91
|
+
* schema default, which is all an untouched field ever resolves to;
|
|
92
|
+
* 3. what the pet's own `pet.json` already holds (`persisted`), so a field the
|
|
93
|
+
* profile never touched keeps the position the user dragged instead of
|
|
94
|
+
* snapping back to the schema default on the next mount.
|
|
95
|
+
*
|
|
96
|
+
* Step 3 is what makes a drag survive a restart. The display fields carry
|
|
97
|
+
* schema defaults (`right: 24`, `bottom: 20` — the bottom-right corner), and a
|
|
98
|
+
* config that never set them reads back exactly like one that set them to the
|
|
99
|
+
* defaults, so without `persisted` the startup section resets the layout every
|
|
100
|
+
* time the profile commits none — which is what an aggregate install does.
|
|
101
|
+
* @param key - the display field to resolve.
|
|
102
|
+
* @param live - the row config's own field, absent when the entry declares none.
|
|
103
|
+
* @param persisted - the display the pet already shows (its own `pet.json`).
|
|
104
|
+
* @param committed - the profile layer's explicit display values, when known.
|
|
105
|
+
* @returns the value the pet runs with.
|
|
106
|
+
*/
|
|
107
|
+
function displayField(key, live, persisted, committed) {
|
|
108
|
+
const explicit = committed[key];
|
|
109
|
+
if (explicit !== undefined)
|
|
110
|
+
return explicit;
|
|
111
|
+
const value = readLive(live, PET_FORM_DEFAULTS[key]);
|
|
112
|
+
if (value !== PET_FORM_DEFAULTS[key])
|
|
113
|
+
return value;
|
|
114
|
+
const kept = persisted[key];
|
|
115
|
+
return kept === undefined ? value : kept;
|
|
116
|
+
}
|
|
85
117
|
/**
|
|
86
118
|
* The settings section the pet runs with: the effective values of the row's
|
|
87
119
|
* own config. `fallbackPetId` covers a mount whose config names no pet at all
|
|
88
120
|
* (a direct mount outside a Loader) — under a Loader the schema default is
|
|
89
121
|
* always present, so the persisted selection stands whenever the config
|
|
90
|
-
* carries it.
|
|
122
|
+
* carries it. `persisted` and `committed` give the display fields the same
|
|
123
|
+
* treatment; see {@link displayField}.
|
|
91
124
|
* @param config - the effective config of the pet row.
|
|
92
125
|
* @param fallbackPetId - pet id to use when the config names none.
|
|
126
|
+
* @param persisted - the display the pet already shows (its own `pet.json`).
|
|
127
|
+
* @param committed - the profile layer's explicit display values, when known.
|
|
93
128
|
* @returns the resolved settings section.
|
|
94
129
|
*/
|
|
95
|
-
export function petSettingsSection(config, fallbackPetId) {
|
|
130
|
+
export function petSettingsSection(config, fallbackPetId, persisted = {}, committed = {}) {
|
|
96
131
|
return {
|
|
97
|
-
visible:
|
|
98
|
-
size:
|
|
99
|
-
right:
|
|
100
|
-
bottom:
|
|
101
|
-
bubbleScale:
|
|
132
|
+
visible: displayField('visible', config.visible, persisted, committed),
|
|
133
|
+
size: displayField('size', config.size, persisted, committed),
|
|
134
|
+
right: displayField('right', config.right, persisted, committed),
|
|
135
|
+
bottom: displayField('bottom', config.bottom, persisted, committed),
|
|
136
|
+
bubbleScale: displayField('bubbleScale', config.bubbleScale, persisted, committed),
|
|
102
137
|
petId: readLive(config.petId, fallbackPetId),
|
|
103
138
|
enabled: readLive(config.enabled, PET_FORM_DEFAULTS.enabled),
|
|
104
139
|
decorationEnabled: readLive(config.decorationEnabled, PET_FORM_DEFAULTS.decorationEnabled),
|
|
105
140
|
};
|
|
106
141
|
}
|
|
142
|
+
/** The settings row ids a pet install is served under, most specific first. */
|
|
143
|
+
const PET_SETTINGS_ROW_IDS = ['web-ui-pet', 'ui-pet', PET_SETTINGS_NAMESPACE];
|
|
144
|
+
/**
|
|
145
|
+
* The settings row serving this plugin's own config, or undefined when the
|
|
146
|
+
* settings service is not mounted (a direct mount) or serves no row for the pet.
|
|
147
|
+
*
|
|
148
|
+
* Every settings write has to address this row's id, and that id is not the
|
|
149
|
+
* package's plugin name: an aggregate bundle renames child rows (`pet` becomes
|
|
150
|
+
* `web-ui-pet`). The browser half resolves the same id through its own
|
|
151
|
+
* `servedEntryId`, so both halves write to one namespace.
|
|
152
|
+
* @param ctx - the plugin's context.
|
|
153
|
+
* @returns the serving row, when there is one.
|
|
154
|
+
*/
|
|
155
|
+
function servedRow(ctx) {
|
|
156
|
+
try {
|
|
157
|
+
const settings = ctx.get('settings', false);
|
|
158
|
+
if (settings === undefined)
|
|
159
|
+
return undefined;
|
|
160
|
+
const rows = settings.describe();
|
|
161
|
+
for (const id of PET_SETTINGS_ROW_IDS) {
|
|
162
|
+
const row = rows.find(candidate => candidate.ns === id);
|
|
163
|
+
if (row !== undefined)
|
|
164
|
+
return row;
|
|
165
|
+
}
|
|
166
|
+
return undefined;
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
107
172
|
/** Register the pet service and its API + asset routes on the context. */
|
|
108
173
|
export const apply = mountOnce('@linxin666/dsh-pet', applyImpl);
|
|
109
174
|
function applyImpl(ctx, config = {}) {
|
|
@@ -118,12 +183,19 @@ function applyImpl(ctx, config = {}) {
|
|
|
118
183
|
decorationEnabled: readLive(config.decorationEnabled, PET_FORM_DEFAULTS.decorationEnabled),
|
|
119
184
|
registry,
|
|
120
185
|
});
|
|
186
|
+
// The mirror back into the settings document (see syncSettingsFromPet) has to
|
|
187
|
+
// address the row that serves this plugin's config, which an aggregate bundle
|
|
188
|
+
// renames; the id is resolved lazily, once the composition serves it.
|
|
189
|
+
service.settingsNamespaceProvider = () => servedRow(ctx)?.ns;
|
|
121
190
|
// The effective settings ARE this row's own config: the Host serves one form
|
|
122
191
|
// per profile entry from `Config` above, and every edit is committed into the
|
|
123
192
|
// running config, so the plugin re-reads the section here instead of holding
|
|
124
193
|
// a separate settings document (0.1.6 registered one through
|
|
125
|
-
// settings.installSection/register and re-resolved it on every change).
|
|
126
|
-
|
|
194
|
+
// settings.installSection/register and re-resolved it on every change). The
|
|
195
|
+
// display fields additionally read the persisted layout and the profile layer,
|
|
196
|
+
// so a mount with a config that never set them cannot reset a dragged
|
|
197
|
+
// position to the schema defaults (see displayField).
|
|
198
|
+
const current = () => petSettingsSection(config, service.selectedPetId(), service.display(), servedRow(ctx)?.user);
|
|
127
199
|
// The browser half talks to the pet through same-origin JSON endpoints and
|
|
128
200
|
// loads each pet's atlas from the registry's own media route (RPC domains
|
|
129
201
|
// are platform-registered, so the pet serves its own API — the same
|
package/lib/types/service.d.ts
CHANGED
|
@@ -222,6 +222,15 @@ export declare class PetService extends Service {
|
|
|
222
222
|
* the same turn again after the pet is disabled and re-enabled.
|
|
223
223
|
*/
|
|
224
224
|
private readonly officialEventSessions;
|
|
225
|
+
/**
|
|
226
|
+
* Resolve the settings namespace this plugin's own config is served as, when
|
|
227
|
+
* the caller can read it off the composition. A plugin's settings ARE its own
|
|
228
|
+
* config, so the namespace is the row's id — and an aggregate bundle renames
|
|
229
|
+
* child rows (`pet` becomes `web-ui-pet`), which is why it cannot be assumed
|
|
230
|
+
* to be {@link PET_SETTINGS_NAMESPACE}. `apply` sets this; absent means the
|
|
231
|
+
* plugin runs outside a Loader and the package's own id stands.
|
|
232
|
+
*/
|
|
233
|
+
settingsNamespaceProvider: (() => string | undefined) | undefined;
|
|
225
234
|
constructor(ctx: Context, config?: PetConfig);
|
|
226
235
|
/**
|
|
227
236
|
* The draw-time voice-pool provider handed to every projection runtime.
|
|
@@ -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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"}
|
package/lib/types/service.js
CHANGED
|
@@ -66,6 +66,15 @@ export class PetService extends Service {
|
|
|
66
66
|
* the same turn again after the pet is disabled and re-enabled.
|
|
67
67
|
*/
|
|
68
68
|
officialEventSessions = new WeakSet();
|
|
69
|
+
/**
|
|
70
|
+
* Resolve the settings namespace this plugin's own config is served as, when
|
|
71
|
+
* the caller can read it off the composition. A plugin's settings ARE its own
|
|
72
|
+
* config, so the namespace is the row's id — and an aggregate bundle renames
|
|
73
|
+
* child rows (`pet` becomes `web-ui-pet`), which is why it cannot be assumed
|
|
74
|
+
* to be {@link PET_SETTINGS_NAMESPACE}. `apply` sets this; absent means the
|
|
75
|
+
* plugin runs outside a Loader and the package's own id stands.
|
|
76
|
+
*/
|
|
77
|
+
settingsNamespaceProvider = undefined;
|
|
69
78
|
constructor(ctx, config = {}) {
|
|
70
79
|
super(ctx, 'pet');
|
|
71
80
|
this.persistDir = config.persistDir ?? petHomeDir();
|
|
@@ -609,8 +618,9 @@ export class PetService extends Service {
|
|
|
609
618
|
const settings = this.ctx.get('settings', false);
|
|
610
619
|
if (settings === undefined)
|
|
611
620
|
return;
|
|
621
|
+
const ns = this.settingsNamespaceProvider?.() ?? PET_SETTINGS_NAMESPACE;
|
|
612
622
|
const snapshot = this.ledger.snapshot;
|
|
613
|
-
void settings.update(
|
|
623
|
+
void settings.update(ns, {
|
|
614
624
|
visible: snapshot.display.visible,
|
|
615
625
|
size: snapshot.display.size,
|
|
616
626
|
right: snapshot.display.right,
|
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.4.
|
|
4
|
+
"version": "0.4.4",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
package/src/index.test.ts
CHANGED
|
@@ -117,3 +117,42 @@ describe('petSettingsSection', () => {
|
|
|
117
117
|
expect(section.petId).toBe('doro')
|
|
118
118
|
})
|
|
119
119
|
})
|
|
120
|
+
|
|
121
|
+
describe('petSettingsSection display persistence', () => {
|
|
122
|
+
/** The layout a user dragged the pet to, as its own pet.json holds it. */
|
|
123
|
+
const dragged = { visible: true, size: 160, right: 1428, bottom: 167, bubbleScale: 1 }
|
|
124
|
+
|
|
125
|
+
it('user keeps the dragged position when the profile never committed one', () => {
|
|
126
|
+
// Given a pet.json holding the layout the pet was dragged to
|
|
127
|
+
// And a row config that never set the display fields, so they read back as
|
|
128
|
+
// the schema defaults (the bottom-right corner)
|
|
129
|
+
// When the plugin starts and resolves its active settings
|
|
130
|
+
const section = petSettingsSection(Config({}), 'ouo-neko', dragged)
|
|
131
|
+
// Then the dragged layout stands instead of snapping back to those defaults
|
|
132
|
+
expect(section).toMatchObject({ right: 1428, bottom: 167 })
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('user commits a display field and the profile value outranks the persisted one', () => {
|
|
136
|
+
// Given a dragged layout and a profile layer that committed a right inset
|
|
137
|
+
const section = petSettingsSection(Config({}), 'ouo-neko', dragged, { right: 48 })
|
|
138
|
+
// Then the explicit choice wins and the untouched field keeps the dragged one
|
|
139
|
+
expect(section).toMatchObject({ right: 48, bottom: 167 })
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('user commits the schema default and the choice is not mistaken for unset', () => {
|
|
143
|
+
// Given a dragged layout and a profile layer that committed the default inset
|
|
144
|
+
const section = petSettingsSection(Config({}), 'ouo-neko', dragged, { right: 24 })
|
|
145
|
+
// Then the explicit (default-valued) choice stands over the persisted one
|
|
146
|
+
expect(section.right).toBe(24)
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('user commits a layout at runtime and the result outranks the persisted one', () => {
|
|
150
|
+
// Given a live reference the Host committed into the running config
|
|
151
|
+
const resolved = Config({})
|
|
152
|
+
commitLive(resolved.right, 500)
|
|
153
|
+
// When the plugin resolves the section against an older persisted layout
|
|
154
|
+
const section = petSettingsSection(resolved, 'ouo-neko', dragged)
|
|
155
|
+
// Then the live commit wins and the persisted value fills the other fields
|
|
156
|
+
expect(section).toMatchObject({ right: 500, bottom: 167 })
|
|
157
|
+
})
|
|
158
|
+
})
|
package/src/index.ts
CHANGED
|
@@ -16,10 +16,10 @@ import { Context, type Volatile } from '@deepseek-ai/cordis'
|
|
|
16
16
|
import type {} from '@deepseek-ai/dsh-settings'
|
|
17
17
|
import type {} from '@deepseek-ai/dsh-host-webserver'
|
|
18
18
|
import z from '@deepseek-ai/schemastery'
|
|
19
|
-
import { PetService, type PetConfig, type PetSettingsSection } from './service.ts'
|
|
19
|
+
import { PET_SETTINGS_NAMESPACE, PetService, type PetConfig, type PetSettingsSection } from './service.ts'
|
|
20
20
|
import { makePetRoutes } from './routes.ts'
|
|
21
21
|
import { loadPetRegistry, petPackageRoot } from './registry.ts'
|
|
22
|
-
import { BUBBLE_SCALE_MAX, BUBBLE_SCALE_MIN, DEFAULT_PET_ID, DISPLAY_INSET_MAX, DISPLAY_SIZE_MAX, DISPLAY_SIZE_MIN } from './persist.ts'
|
|
22
|
+
import { BUBBLE_SCALE_MAX, BUBBLE_SCALE_MIN, DEFAULT_PET_ID, DISPLAY_INSET_MAX, DISPLAY_SIZE_MAX, DISPLAY_SIZE_MIN, type PetDisplayConfig } from './persist.ts'
|
|
23
23
|
import { mountOnce } from './mount-once.ts'
|
|
24
24
|
|
|
25
25
|
export { PetService, MAX_SESSION_BUBBLES } from './service.ts'
|
|
@@ -203,29 +203,115 @@ function readLive<T>(field: LiveField<T> | undefined, fallback: T): T {
|
|
|
203
203
|
return typeof ref.get === 'function' ? ref.get() ?? fallback : field as T
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
+
/**
|
|
207
|
+
* Resolve one display field of the section. The precedence is deliberate:
|
|
208
|
+
*
|
|
209
|
+
* 1. an explicit profile value (`committed`) — the user's own choice, and the
|
|
210
|
+
* only signal that carries a value equal to the schema default on purpose;
|
|
211
|
+
* 2. a live value the row committed at runtime, i.e. one that differs from the
|
|
212
|
+
* schema default, which is all an untouched field ever resolves to;
|
|
213
|
+
* 3. what the pet's own `pet.json` already holds (`persisted`), so a field the
|
|
214
|
+
* profile never touched keeps the position the user dragged instead of
|
|
215
|
+
* snapping back to the schema default on the next mount.
|
|
216
|
+
*
|
|
217
|
+
* Step 3 is what makes a drag survive a restart. The display fields carry
|
|
218
|
+
* schema defaults (`right: 24`, `bottom: 20` — the bottom-right corner), and a
|
|
219
|
+
* config that never set them reads back exactly like one that set them to the
|
|
220
|
+
* defaults, so without `persisted` the startup section resets the layout every
|
|
221
|
+
* time the profile commits none — which is what an aggregate install does.
|
|
222
|
+
* @param key - the display field to resolve.
|
|
223
|
+
* @param live - the row config's own field, absent when the entry declares none.
|
|
224
|
+
* @param persisted - the display the pet already shows (its own `pet.json`).
|
|
225
|
+
* @param committed - the profile layer's explicit display values, when known.
|
|
226
|
+
* @returns the value the pet runs with.
|
|
227
|
+
*/
|
|
228
|
+
function displayField<K extends keyof PetDisplayConfig>(
|
|
229
|
+
key: K,
|
|
230
|
+
live: LiveField<PetDisplayConfig[K]> | undefined,
|
|
231
|
+
persisted: Partial<PetDisplayConfig>,
|
|
232
|
+
committed: Partial<PetDisplayConfig>,
|
|
233
|
+
): PetDisplayConfig[K] {
|
|
234
|
+
const explicit = committed[key]
|
|
235
|
+
if (explicit !== undefined) return explicit
|
|
236
|
+
const value = readLive<PetDisplayConfig[K]>(live, PET_FORM_DEFAULTS[key])
|
|
237
|
+
if (value !== PET_FORM_DEFAULTS[key]) return value
|
|
238
|
+
const kept = persisted[key]
|
|
239
|
+
return kept === undefined ? value : kept
|
|
240
|
+
}
|
|
241
|
+
|
|
206
242
|
/**
|
|
207
243
|
* The settings section the pet runs with: the effective values of the row's
|
|
208
244
|
* own config. `fallbackPetId` covers a mount whose config names no pet at all
|
|
209
245
|
* (a direct mount outside a Loader) — under a Loader the schema default is
|
|
210
246
|
* always present, so the persisted selection stands whenever the config
|
|
211
|
-
* carries it.
|
|
247
|
+
* carries it. `persisted` and `committed` give the display fields the same
|
|
248
|
+
* treatment; see {@link displayField}.
|
|
212
249
|
* @param config - the effective config of the pet row.
|
|
213
250
|
* @param fallbackPetId - pet id to use when the config names none.
|
|
251
|
+
* @param persisted - the display the pet already shows (its own `pet.json`).
|
|
252
|
+
* @param committed - the profile layer's explicit display values, when known.
|
|
214
253
|
* @returns the resolved settings section.
|
|
215
254
|
*/
|
|
216
|
-
export function petSettingsSection(
|
|
255
|
+
export function petSettingsSection(
|
|
256
|
+
config: PetFormConfig,
|
|
257
|
+
fallbackPetId: string,
|
|
258
|
+
persisted: Partial<PetDisplayConfig> = {},
|
|
259
|
+
committed: Partial<PetDisplayConfig> = {},
|
|
260
|
+
): PetSettingsSection {
|
|
217
261
|
return {
|
|
218
|
-
visible:
|
|
219
|
-
size:
|
|
220
|
-
right:
|
|
221
|
-
bottom:
|
|
222
|
-
bubbleScale:
|
|
262
|
+
visible: displayField('visible', config.visible, persisted, committed),
|
|
263
|
+
size: displayField('size', config.size, persisted, committed),
|
|
264
|
+
right: displayField('right', config.right, persisted, committed),
|
|
265
|
+
bottom: displayField('bottom', config.bottom, persisted, committed),
|
|
266
|
+
bubbleScale: displayField('bubbleScale', config.bubbleScale, persisted, committed),
|
|
223
267
|
petId: readLive(config.petId, fallbackPetId),
|
|
224
268
|
enabled: readLive(config.enabled, PET_FORM_DEFAULTS.enabled),
|
|
225
269
|
decorationEnabled: readLive(config.decorationEnabled, PET_FORM_DEFAULTS.decorationEnabled),
|
|
226
270
|
}
|
|
227
271
|
}
|
|
228
272
|
|
|
273
|
+
/** The settings row ids a pet install is served under, most specific first. */
|
|
274
|
+
const PET_SETTINGS_ROW_IDS: readonly string[] = ['web-ui-pet', 'ui-pet', PET_SETTINGS_NAMESPACE]
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* The fields this host half reads off one settings descriptor. The settings
|
|
278
|
+
* service serves one descriptor per plugin row (see `describe`), and a plugin's
|
|
279
|
+
* own config IS its settings page, so the two that matter here are the row's
|
|
280
|
+
* namespace and the profile layer's explicit values.
|
|
281
|
+
*/
|
|
282
|
+
interface ServedSettingsRow {
|
|
283
|
+
/** The profile entry's id, which is also this plugin's settings namespace. */
|
|
284
|
+
ns: string
|
|
285
|
+
/** The profile layer's explicit values; absent fields were never committed. */
|
|
286
|
+
user?: Partial<PetDisplayConfig>
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* The settings row serving this plugin's own config, or undefined when the
|
|
291
|
+
* settings service is not mounted (a direct mount) or serves no row for the pet.
|
|
292
|
+
*
|
|
293
|
+
* Every settings write has to address this row's id, and that id is not the
|
|
294
|
+
* package's plugin name: an aggregate bundle renames child rows (`pet` becomes
|
|
295
|
+
* `web-ui-pet`). The browser half resolves the same id through its own
|
|
296
|
+
* `servedEntryId`, so both halves write to one namespace.
|
|
297
|
+
* @param ctx - the plugin's context.
|
|
298
|
+
* @returns the serving row, when there is one.
|
|
299
|
+
*/
|
|
300
|
+
function servedRow(ctx: Context): ServedSettingsRow | undefined {
|
|
301
|
+
try {
|
|
302
|
+
const settings = ctx.get('settings', false) as { describe(): readonly ServedSettingsRow[] } | undefined
|
|
303
|
+
if (settings === undefined) return undefined
|
|
304
|
+
const rows = settings.describe()
|
|
305
|
+
for (const id of PET_SETTINGS_ROW_IDS) {
|
|
306
|
+
const row = rows.find(candidate => candidate.ns === id)
|
|
307
|
+
if (row !== undefined) return row
|
|
308
|
+
}
|
|
309
|
+
return undefined
|
|
310
|
+
} catch {
|
|
311
|
+
return undefined
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
229
315
|
/** Register the pet service and its API + asset routes on the context. */
|
|
230
316
|
export const apply = mountOnce('@linxin666/dsh-pet', applyImpl)
|
|
231
317
|
|
|
@@ -244,13 +330,21 @@ function applyImpl(ctx: Context, config: PetPluginConfig = {}): void {
|
|
|
244
330
|
decorationEnabled: readLive(config.decorationEnabled, PET_FORM_DEFAULTS.decorationEnabled),
|
|
245
331
|
registry,
|
|
246
332
|
})
|
|
333
|
+
// The mirror back into the settings document (see syncSettingsFromPet) has to
|
|
334
|
+
// address the row that serves this plugin's config, which an aggregate bundle
|
|
335
|
+
// renames; the id is resolved lazily, once the composition serves it.
|
|
336
|
+
service.settingsNamespaceProvider = () => servedRow(ctx)?.ns
|
|
247
337
|
|
|
248
338
|
// The effective settings ARE this row's own config: the Host serves one form
|
|
249
339
|
// per profile entry from `Config` above, and every edit is committed into the
|
|
250
340
|
// running config, so the plugin re-reads the section here instead of holding
|
|
251
341
|
// a separate settings document (0.1.6 registered one through
|
|
252
|
-
// settings.installSection/register and re-resolved it on every change).
|
|
253
|
-
|
|
342
|
+
// settings.installSection/register and re-resolved it on every change). The
|
|
343
|
+
// display fields additionally read the persisted layout and the profile layer,
|
|
344
|
+
// so a mount with a config that never set them cannot reset a dragged
|
|
345
|
+
// position to the schema defaults (see displayField).
|
|
346
|
+
const current = (): PetSettingsSection =>
|
|
347
|
+
petSettingsSection(config, service.selectedPetId(), service.display(), servedRow(ctx)?.user)
|
|
254
348
|
// The browser half talks to the pet through same-origin JSON endpoints and
|
|
255
349
|
// loads each pet's atlas from the registry's own media route (RPC domains
|
|
256
350
|
// are platform-registered, so the pet serves its own API — the same
|
package/src/service.ts
CHANGED
|
@@ -300,6 +300,15 @@ export class PetService extends Service {
|
|
|
300
300
|
* the same turn again after the pet is disabled and re-enabled.
|
|
301
301
|
*/
|
|
302
302
|
private readonly officialEventSessions = new WeakSet<Session>()
|
|
303
|
+
/**
|
|
304
|
+
* Resolve the settings namespace this plugin's own config is served as, when
|
|
305
|
+
* the caller can read it off the composition. A plugin's settings ARE its own
|
|
306
|
+
* config, so the namespace is the row's id — and an aggregate bundle renames
|
|
307
|
+
* child rows (`pet` becomes `web-ui-pet`), which is why it cannot be assumed
|
|
308
|
+
* to be {@link PET_SETTINGS_NAMESPACE}. `apply` sets this; absent means the
|
|
309
|
+
* plugin runs outside a Loader and the package's own id stands.
|
|
310
|
+
*/
|
|
311
|
+
settingsNamespaceProvider: (() => string | undefined) | undefined = undefined
|
|
303
312
|
|
|
304
313
|
constructor(ctx: Context, config: PetConfig = {}) {
|
|
305
314
|
super(ctx, 'pet')
|
|
@@ -836,8 +845,9 @@ export class PetService extends Service {
|
|
|
836
845
|
private syncSettingsFromPet(): void {
|
|
837
846
|
const settings = this.ctx.get('settings', false) as { update(ns: string, patch: object): Promise<void> } | undefined
|
|
838
847
|
if (settings === undefined) return
|
|
848
|
+
const ns = this.settingsNamespaceProvider?.() ?? PET_SETTINGS_NAMESPACE
|
|
839
849
|
const snapshot = this.ledger.snapshot
|
|
840
|
-
void settings.update(
|
|
850
|
+
void settings.update(ns, {
|
|
841
851
|
visible: snapshot.display.visible,
|
|
842
852
|
size: snapshot.display.size,
|
|
843
853
|
right: snapshot.display.right,
|