@linxin666/dsh-pet 0.4.2 → 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/README.i18n.yaml +2 -2
- package/README.md +16 -28
- package/README.zh.md +16 -28
- package/lib/client.js +74 -65
- package/lib/client.js.map +1 -1
- package/lib/index.js +169 -21
- package/lib/types/client/PetSettingsCard.d.ts +16 -12
- package/lib/types/client/PetSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PetSettingsCard.js +77 -49
- package/lib/types/client/locales.d.ts +0 -2
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/locales.js +0 -2
- 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/mount-once.d.ts +38 -7
- package/lib/types/mount-once.d.ts.map +1 -1
- package/lib/types/mount-once.js +96 -12
- 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/client/PetSettingsCard.test.tsx +115 -4
- package/src/client/PetSettingsCard.tsx +76 -66
- package/src/client/locales.ts +0 -2
- package/src/client/pet-css.test.ts +8 -0
- package/src/client/settings-section.module.css +0 -23
- package/src/index.test.ts +39 -0
- package/src/index.ts +105 -11
- package/src/mount-once.ts +107 -13
- package/src/service.ts +11 -1
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,
|
|
@@ -6007,38 +6017,111 @@ function makePetRoutes(deps) {
|
|
|
6007
6017
|
* the loader accepts a standalone install of the same package side by side;
|
|
6008
6018
|
* without this guard the second instance would still re-register the same
|
|
6009
6019
|
* webserver routes, tools, settings namespaces, and system-prompt sections
|
|
6010
|
-
* and fail the boot. mountOnce makes
|
|
6011
|
-
*
|
|
6012
|
-
* package name in the client module host).
|
|
6020
|
+
* and fail the boot. mountOnce makes a mount of an already-mounted package a
|
|
6021
|
+
* no-op for as long as the first instance lives (the browser half is already
|
|
6022
|
+
* deduped by package name in the client module host).
|
|
6023
|
+
*
|
|
6024
|
+
* A no-op is only safe while the holder is ALIVE. The holder can be disposed
|
|
6025
|
+
* long after the refused mount ran its course: the Host reloads a profile by
|
|
6026
|
+
* creating the new loader entries before the old ones are torn down (a
|
|
6027
|
+
* plugin-manager enable/disable/install write, a settings-driven row reload,
|
|
6028
|
+
* HMR), so the new aggregate shell entry mounts its family plugin while the
|
|
6029
|
+
* previous entry still owns the name. Dropping that refused mount lost the
|
|
6030
|
+
* plugin for good - the previous entry then disposed its own mount, releasing
|
|
6031
|
+
* the name with nobody left to take it, and the family row stayed listed as
|
|
6032
|
+
* active while its host routes 404ed (the task-board panel showed
|
|
6033
|
+
* "board.hostError.notMounted", no degraded record appeared, and only a Host
|
|
6034
|
+
* restart recovered it).
|
|
6035
|
+
*
|
|
6036
|
+
* The refused mount is therefore QUEUED, not dropped, and replayed the moment
|
|
6037
|
+
* the holder releases the name - if the waiting fiber is still alive then. The
|
|
6038
|
+
* single-instance guarantee is unchanged: exactly one mount is live per
|
|
6039
|
+
* package name, and the replay re-enters the guard so a later mount still
|
|
6040
|
+
* dedupes against it.
|
|
6013
6041
|
*
|
|
6014
6042
|
* The registry rides a global symbol so two module instances of the same
|
|
6015
|
-
* package (npm copy vs repository link) still share one verdict.
|
|
6016
|
-
*
|
|
6043
|
+
* package (npm copy vs repository link) still share one verdict. That symbol
|
|
6044
|
+
* is a CROSS-REPOSITORY contract, not this file's private state: the four
|
|
6045
|
+
* satellite packages (dsh-skins / dsh-pet / dsh-presets /
|
|
6046
|
+
* dsh-community-plugins) are separate repositories carrying their own copy of
|
|
6047
|
+
* this guard, rebuilt on their own schedule, so the value under `MOUNTED`
|
|
6048
|
+
* must keep the shape every published copy reads (a `Set` of package names
|
|
6049
|
+
* with `has`/`add`/`delete`). The wait queues this guard added therefore live
|
|
6050
|
+
* under their own additive key, and the registry reads back a `Set` even when
|
|
6051
|
+
* some other build left a different value there. Changing `MOUNTED`'s shape
|
|
6052
|
+
* in place broke that contract once: a satellite's legacy copy created a
|
|
6053
|
+
* `Set`, the family's new copy read it as a `Map`, and every family row
|
|
6054
|
+
* mounted after it failed with "claims.get is not a function".
|
|
6055
|
+
*
|
|
6056
|
+
* cordis `ctx.effect` runs its callback immediately and treats the callback's
|
|
6017
6057
|
* return value as the fiber disposer, so the unmarker is returned, not run.
|
|
6018
6058
|
*/
|
|
6059
|
+
/** Published cross-repository contract: package names currently mounted. */
|
|
6019
6060
|
const MOUNTED = Symbol.for("dsh-web.mounted-plugins");
|
|
6061
|
+
/** Additive key this guard owns: refused mounts waiting for the name. */
|
|
6062
|
+
const WAITERS = Symbol.for("dsh-web.mounted-plugins.waiters");
|
|
6063
|
+
/**
|
|
6064
|
+
* The shared name registry, always a `Set` whatever another build stored here:
|
|
6065
|
+
* a foreign value (an interim shape, a hand-written global) must not take every
|
|
6066
|
+
* family plugin down with it.
|
|
6067
|
+
* @returns the process-wide set of mounted package names.
|
|
6068
|
+
*/
|
|
6020
6069
|
function mountedSet() {
|
|
6021
6070
|
const registry = globalThis;
|
|
6022
|
-
|
|
6071
|
+
const existing = registry[MOUNTED];
|
|
6072
|
+
if (existing instanceof Set) return existing;
|
|
6073
|
+
const created = /* @__PURE__ */ new Set();
|
|
6074
|
+
registry[MOUNTED] = created;
|
|
6075
|
+
return created;
|
|
6076
|
+
}
|
|
6077
|
+
/** Queue per package name for mounts refused while a holder was alive. */
|
|
6078
|
+
function mountWaiters() {
|
|
6079
|
+
const registry = globalThis;
|
|
6080
|
+
return registry[WAITERS] ??= /* @__PURE__ */ new Map();
|
|
6023
6081
|
}
|
|
6024
6082
|
/**
|
|
6025
6083
|
* Wrap a cordis plugin apply so the package runs at most once per process.
|
|
6026
|
-
* The first mount registers normally and
|
|
6027
|
-
*
|
|
6084
|
+
* The first mount registers normally and releases the name when its fiber
|
|
6085
|
+
* disposes; a mount refused while that name is held waits for the release and
|
|
6086
|
+
* then runs, unless its own fiber disposes first.
|
|
6028
6087
|
* @param packageName - npm package identity shared by every install source.
|
|
6029
6088
|
* @param fn - the original plugin apply.
|
|
6030
6089
|
* @returns an apply of the same shape.
|
|
6031
6090
|
*/
|
|
6032
6091
|
function mountOnce(packageName, fn) {
|
|
6033
|
-
|
|
6092
|
+
const mount = (...args) => {
|
|
6034
6093
|
const mounted = mountedSet();
|
|
6035
|
-
|
|
6094
|
+
const ctx = args[0];
|
|
6095
|
+
if (mounted.has(packageName)) {
|
|
6096
|
+
const waiters = mountWaiters();
|
|
6097
|
+
const queue = waiters.get(packageName) ?? [];
|
|
6098
|
+
let alive = true;
|
|
6099
|
+
const pending = { run: () => {
|
|
6100
|
+
if (alive) mount(...args);
|
|
6101
|
+
} };
|
|
6102
|
+
ctx?.effect?.(() => () => {
|
|
6103
|
+
alive = false;
|
|
6104
|
+
const index = queue.indexOf(pending);
|
|
6105
|
+
if (index >= 0) queue.splice(index, 1);
|
|
6106
|
+
});
|
|
6107
|
+
queue.push(pending);
|
|
6108
|
+
waiters.set(packageName, queue);
|
|
6109
|
+
return;
|
|
6110
|
+
}
|
|
6036
6111
|
mounted.add(packageName);
|
|
6037
|
-
|
|
6112
|
+
ctx?.effect?.(() => () => {
|
|
6038
6113
|
mounted.delete(packageName);
|
|
6114
|
+
const waiters = mountWaiters();
|
|
6115
|
+
const queue = waiters.get(packageName);
|
|
6116
|
+
if (queue === void 0) return;
|
|
6117
|
+
waiters.delete(packageName);
|
|
6118
|
+
for (const waiter of queue.splice(0)) queueMicrotask(() => {
|
|
6119
|
+
waiter.run();
|
|
6120
|
+
});
|
|
6039
6121
|
});
|
|
6040
6122
|
return fn(...args);
|
|
6041
|
-
}
|
|
6123
|
+
};
|
|
6124
|
+
return mount;
|
|
6042
6125
|
}
|
|
6043
6126
|
//#endregion
|
|
6044
6127
|
//#region src/index.ts
|
|
@@ -6098,27 +6181,91 @@ function readLive(field, fallback) {
|
|
|
6098
6181
|
return typeof ref.get === "function" ? ref.get() ?? fallback : field;
|
|
6099
6182
|
}
|
|
6100
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
|
+
/**
|
|
6101
6214
|
* The settings section the pet runs with: the effective values of the row's
|
|
6102
6215
|
* own config. `fallbackPetId` covers a mount whose config names no pet at all
|
|
6103
6216
|
* (a direct mount outside a Loader) — under a Loader the schema default is
|
|
6104
6217
|
* always present, so the persisted selection stands whenever the config
|
|
6105
|
-
* carries it.
|
|
6218
|
+
* carries it. `persisted` and `committed` give the display fields the same
|
|
6219
|
+
* treatment; see {@link displayField}.
|
|
6106
6220
|
* @param config - the effective config of the pet row.
|
|
6107
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.
|
|
6108
6224
|
* @returns the resolved settings section.
|
|
6109
6225
|
*/
|
|
6110
|
-
function petSettingsSection(config, fallbackPetId) {
|
|
6226
|
+
function petSettingsSection(config, fallbackPetId, persisted = {}, committed = {}) {
|
|
6111
6227
|
return {
|
|
6112
|
-
visible:
|
|
6113
|
-
size:
|
|
6114
|
-
right:
|
|
6115
|
-
bottom:
|
|
6116
|
-
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),
|
|
6117
6233
|
petId: readLive(config.petId, fallbackPetId),
|
|
6118
6234
|
enabled: readLive(config.enabled, PET_FORM_DEFAULTS.enabled),
|
|
6119
6235
|
decorationEnabled: readLive(config.decorationEnabled, PET_FORM_DEFAULTS.decorationEnabled)
|
|
6120
6236
|
};
|
|
6121
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
|
+
}
|
|
6122
6269
|
/** Register the pet service and its API + asset routes on the context. */
|
|
6123
6270
|
const apply = mountOnce("@linxin666/dsh-pet", applyImpl);
|
|
6124
6271
|
function applyImpl(ctx, config = {}) {
|
|
@@ -6132,7 +6279,8 @@ function applyImpl(ctx, config = {}) {
|
|
|
6132
6279
|
decorationEnabled: readLive(config.decorationEnabled, PET_FORM_DEFAULTS.decorationEnabled),
|
|
6133
6280
|
registry
|
|
6134
6281
|
});
|
|
6135
|
-
|
|
6282
|
+
service.settingsNamespaceProvider = () => servedRow(ctx)?.ns;
|
|
6283
|
+
const current = () => petSettingsSection(config, service.selectedPetId(), service.display(), servedRow(ctx)?.user);
|
|
6136
6284
|
const routes = makePetRoutes({
|
|
6137
6285
|
service,
|
|
6138
6286
|
ctx
|
|
@@ -57,8 +57,6 @@ export interface PetSettingsCardState extends CardShell {
|
|
|
57
57
|
value: string;
|
|
58
58
|
label: string;
|
|
59
59
|
}[];
|
|
60
|
-
/** Registry diagnostics (v1 migration hints, invalid entries), host-served. */
|
|
61
|
-
petDiagnostics: readonly PetDiagnosticView[];
|
|
62
60
|
}
|
|
63
61
|
/** The registration-side face the card's slot entry injects. */
|
|
64
62
|
export interface PetSettingsCardFace extends CardActions {
|
|
@@ -67,20 +65,16 @@ export interface PetSettingsCardFace extends CardActions {
|
|
|
67
65
|
petSettingsCard: SnapshotStore<PetSettingsCardState>;
|
|
68
66
|
};
|
|
69
67
|
}
|
|
70
|
-
/** One registry diagnostic as served by '/api/pet/diagnostics' (#623). */
|
|
71
|
-
export interface PetDiagnosticView {
|
|
72
|
-
level: 'error' | 'warning';
|
|
73
|
-
message: string;
|
|
74
|
-
}
|
|
75
68
|
/** Bridges the 'pet' scope onto the card's staged form. */
|
|
76
69
|
export declare class PetSettingsCardController {
|
|
77
70
|
private readonly form;
|
|
78
71
|
private readonly store;
|
|
79
72
|
private readonly petChoices;
|
|
80
73
|
private readonly petLabels;
|
|
81
|
-
private diagnostics;
|
|
82
74
|
private selectedPetId;
|
|
75
|
+
private selectedVisible;
|
|
83
76
|
private stagedPetId;
|
|
77
|
+
private stagedVisible;
|
|
84
78
|
private savingPet;
|
|
85
79
|
private petSaveFailed;
|
|
86
80
|
private loaded;
|
|
@@ -90,13 +84,23 @@ export declare class PetSettingsCardController {
|
|
|
90
84
|
private pendingTimer;
|
|
91
85
|
/** @param scope - the bound configuration form for the 'pet' entry. */
|
|
92
86
|
constructor(scope: ConfigForm<PetSettings>);
|
|
93
|
-
/** Fetch registry diagnostics once (soft-fail: an empty list on error). */
|
|
94
|
-
private loadDiagnostics;
|
|
95
87
|
/** Resolve the registry choices once (retried a few times on failure). */
|
|
96
88
|
private loadPets;
|
|
97
|
-
private
|
|
89
|
+
private loadPetState;
|
|
98
90
|
private fallback;
|
|
99
|
-
|
|
91
|
+
/**
|
|
92
|
+
* The visibility the fallback switch renders: the staged draft when the user
|
|
93
|
+
* moved it, the persisted value otherwise. There is no user layer to override
|
|
94
|
+
* here, so the field is never marked overridden.
|
|
95
|
+
*/
|
|
96
|
+
private fallbackVisible;
|
|
97
|
+
/**
|
|
98
|
+
* Persist the staged selection and visibility through the pet API — the only
|
|
99
|
+
* writer available when the aggregate shell serves this card, since there is
|
|
100
|
+
* no Host settings form to mutate — then confirm the read-back before the
|
|
101
|
+
* drafts are cleared.
|
|
102
|
+
*/
|
|
103
|
+
private saveFallback;
|
|
100
104
|
private projection;
|
|
101
105
|
/**
|
|
102
106
|
* 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;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAC7F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4CAA4C,CAAA;AAC5E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAIlE,OAAO,EAAoD,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,IAAI,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAG1J,+EAA+E;AAC/E,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,qBAAqB;IACrB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6DAA6D;IAC7D,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED,0CAA0C;AAC1C,MAAM,WAAW,oBAAqB,SAAQ,SAAS;IACrD,+FAA+F;IAC/F,oBAAoB,EAAE,OAAO,CAAA;IAC7B,4BAA4B;IAC5B,OAAO,EAAE,cAAc,CAAA;IACvB,qBAAqB;IACrB,OAAO,EAAE,cAAc,CAAA;IACvB,iBAAiB;IACjB,IAAI,EAAE,cAAc,CAAA;IACpB,mBAAmB;IACnB,KAAK,EAAE,cAAc,CAAA;IACrB,oBAAoB;IACpB,MAAM,EAAE,cAAc,CAAA;IACtB,oCAAoC;IACpC,WAAW,EAAE,cAAc,CAAA;IAC3B,oBAAoB;IACpB,KAAK,EAAE,cAAc,CAAA;IACrB,uCAAuC;IACvC,iBAAiB,EAAE,cAAc,CAAA;IACjC,wEAAwE;IACxE,UAAU,EAAE,SAAS;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"PetSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PetSettingsCard.tsx"],"names":[],"mappings":"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"}
|
|
@@ -9,23 +9,16 @@ async function fetchPetChoices() {
|
|
|
9
9
|
throw new Error('pet pets failed: ' + response.status);
|
|
10
10
|
return (await response.json());
|
|
11
11
|
}
|
|
12
|
-
/**
|
|
13
|
-
async function
|
|
14
|
-
const response = await fetch('/api/pet/diagnostics');
|
|
15
|
-
if (!response.ok)
|
|
16
|
-
throw new Error('pet diagnostics failed: ' + response.status);
|
|
17
|
-
const body = (await response.json());
|
|
18
|
-
return body.diagnostics ?? [];
|
|
19
|
-
}
|
|
20
|
-
/** Read the selection from the same persisted state the pet renders. */
|
|
21
|
-
async function fetchSelectedPetId() {
|
|
12
|
+
/** Read the selection and visibility from the same persisted state the pet renders. */
|
|
13
|
+
async function fetchPetState() {
|
|
22
14
|
const response = await fetch('/api/pet/state');
|
|
23
15
|
if (!response.ok)
|
|
24
16
|
throw new Error('pet state failed: ' + response.status);
|
|
25
17
|
const body = (await response.json());
|
|
26
18
|
if (typeof body.pet?.id !== 'string')
|
|
27
19
|
throw new Error('pet state has no selected pet');
|
|
28
|
-
|
|
20
|
+
// A Host without the display block predates the visibility switch; the pet shows.
|
|
21
|
+
return { petId: body.pet.id, visible: body.display?.visible !== false };
|
|
29
22
|
}
|
|
30
23
|
/** Bridges the 'pet' scope onto the card's staged form. */
|
|
31
24
|
export class PetSettingsCardController {
|
|
@@ -36,9 +29,10 @@ export class PetSettingsCardController {
|
|
|
36
29
|
// without rebuilding the form.
|
|
37
30
|
petChoices = [];
|
|
38
31
|
petLabels = new Map();
|
|
39
|
-
diagnostics = [];
|
|
40
32
|
selectedPetId;
|
|
33
|
+
selectedVisible;
|
|
41
34
|
stagedPetId;
|
|
35
|
+
stagedVisible;
|
|
42
36
|
savingPet = false;
|
|
43
37
|
petSaveFailed = false;
|
|
44
38
|
loaded = false;
|
|
@@ -68,22 +62,9 @@ export class PetSettingsCardController {
|
|
|
68
62
|
if (this.disposed)
|
|
69
63
|
return;
|
|
70
64
|
void this.loadPets();
|
|
71
|
-
void this.
|
|
72
|
-
void this.loadSelectedPet();
|
|
65
|
+
void this.loadPetState();
|
|
73
66
|
}, 0);
|
|
74
67
|
}
|
|
75
|
-
/** Fetch registry diagnostics once (soft-fail: an empty list on error). */
|
|
76
|
-
async loadDiagnostics() {
|
|
77
|
-
try {
|
|
78
|
-
this.diagnostics = await fetchPetDiagnostics();
|
|
79
|
-
if (this.disposed)
|
|
80
|
-
return;
|
|
81
|
-
this.store.set(this.projection());
|
|
82
|
-
}
|
|
83
|
-
catch {
|
|
84
|
-
this.diagnostics = [];
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
68
|
/** Resolve the registry choices once (retried a few times on failure). */
|
|
88
69
|
async loadPets() {
|
|
89
70
|
if (this.loaded || this.disposed)
|
|
@@ -112,12 +93,13 @@ export class PetSettingsCardController {
|
|
|
112
93
|
}
|
|
113
94
|
}
|
|
114
95
|
}
|
|
115
|
-
async
|
|
96
|
+
async loadPetState() {
|
|
116
97
|
try {
|
|
117
|
-
const
|
|
98
|
+
const state = await fetchPetState();
|
|
118
99
|
if (this.disposed)
|
|
119
100
|
return;
|
|
120
|
-
this.selectedPetId = petId;
|
|
101
|
+
this.selectedPetId = state.petId;
|
|
102
|
+
this.selectedVisible = state.visible;
|
|
121
103
|
this.store.set(this.projection());
|
|
122
104
|
}
|
|
123
105
|
catch {
|
|
@@ -129,26 +111,65 @@ export class PetSettingsCardController {
|
|
|
129
111
|
const shell = this.form.shell();
|
|
130
112
|
return shell.available && !shell.exposed && this.selectedPetId !== undefined;
|
|
131
113
|
}
|
|
132
|
-
|
|
114
|
+
/**
|
|
115
|
+
* The visibility the fallback switch renders: the staged draft when the user
|
|
116
|
+
* moved it, the persisted value otherwise. There is no user layer to override
|
|
117
|
+
* here, so the field is never marked overridden.
|
|
118
|
+
*/
|
|
119
|
+
fallbackVisible() {
|
|
120
|
+
const value = this.stagedVisible ?? this.selectedVisible;
|
|
121
|
+
return { text: value === undefined ? '' : String(value), overridden: false, invalid: false };
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Persist the staged selection and visibility through the pet API — the only
|
|
125
|
+
* writer available when the aggregate shell serves this card, since there is
|
|
126
|
+
* no Host settings form to mutate — then confirm the read-back before the
|
|
127
|
+
* drafts are cleared.
|
|
128
|
+
*/
|
|
129
|
+
async saveFallback() {
|
|
133
130
|
const petId = this.stagedPetId;
|
|
134
|
-
|
|
131
|
+
const visible = this.stagedVisible;
|
|
132
|
+
if (this.savingPet)
|
|
133
|
+
return;
|
|
134
|
+
if (petId === undefined && visible === undefined)
|
|
135
|
+
return;
|
|
136
|
+
if (petId !== undefined && !this.petChoices.includes(petId))
|
|
135
137
|
return;
|
|
136
138
|
this.savingPet = true;
|
|
137
139
|
this.petSaveFailed = false;
|
|
138
140
|
this.store.set(this.projection());
|
|
139
141
|
try {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
142
|
+
if (petId !== undefined) {
|
|
143
|
+
const response = await fetch('/api/pet/set-pet', {
|
|
144
|
+
method: 'POST',
|
|
145
|
+
headers: { 'content-type': 'application/json' },
|
|
146
|
+
body: JSON.stringify({ petId }),
|
|
147
|
+
});
|
|
148
|
+
const result = (await response.json());
|
|
149
|
+
if (!response.ok || result.ok !== true || result.petId !== petId) {
|
|
150
|
+
throw new Error('pet selection was not persisted');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (visible !== undefined) {
|
|
154
|
+
const response = await fetch('/api/pet/set-visible', {
|
|
155
|
+
method: 'POST',
|
|
156
|
+
headers: { 'content-type': 'application/json' },
|
|
157
|
+
body: JSON.stringify({ visible }),
|
|
158
|
+
});
|
|
159
|
+
if (!response.ok)
|
|
160
|
+
throw new Error('pet visibility was not persisted');
|
|
148
161
|
}
|
|
149
|
-
|
|
162
|
+
const persisted = await fetchPetState();
|
|
163
|
+
if (petId !== undefined && persisted.petId !== petId)
|
|
164
|
+
throw new Error('pet selection was not persisted');
|
|
165
|
+
if (visible !== undefined && persisted.visible !== visible)
|
|
166
|
+
throw new Error('pet visibility was not persisted');
|
|
167
|
+
this.selectedPetId = persisted.petId;
|
|
168
|
+
this.selectedVisible = persisted.visible;
|
|
150
169
|
if (this.stagedPetId === petId)
|
|
151
170
|
this.stagedPetId = undefined;
|
|
171
|
+
if (this.stagedVisible === visible)
|
|
172
|
+
this.stagedVisible = undefined;
|
|
152
173
|
}
|
|
153
174
|
catch {
|
|
154
175
|
this.petSaveFailed = true;
|
|
@@ -168,7 +189,8 @@ export class PetSettingsCardController {
|
|
|
168
189
|
...(fallback ? {
|
|
169
190
|
exposed: true,
|
|
170
191
|
writable: true,
|
|
171
|
-
dirty: this.stagedPetId !== undefined && this.stagedPetId !== this.selectedPetId
|
|
192
|
+
dirty: this.stagedPetId !== undefined && this.stagedPetId !== this.selectedPetId
|
|
193
|
+
|| this.stagedVisible !== undefined && this.stagedVisible !== this.selectedVisible,
|
|
172
194
|
invalid: this.stagedPetId !== undefined && !this.petChoices.includes(this.stagedPetId),
|
|
173
195
|
saving: this.savingPet,
|
|
174
196
|
failed: this.petSaveFailed,
|
|
@@ -177,7 +199,7 @@ export class PetSettingsCardController {
|
|
|
177
199
|
petSelectionFallback: fallback,
|
|
178
200
|
enabled: this.form.field('enabled'),
|
|
179
201
|
decorationEnabled: this.form.field('decorationEnabled'),
|
|
180
|
-
visible: this.form.field('visible'),
|
|
202
|
+
visible: fallback ? this.fallbackVisible() : this.form.field('visible'),
|
|
181
203
|
size: this.form.field('size'),
|
|
182
204
|
right: this.form.field('right'),
|
|
183
205
|
bottom: this.form.field('bottom'),
|
|
@@ -188,7 +210,6 @@ export class PetSettingsCardController {
|
|
|
188
210
|
? { ...configuredPet, text: this.selectedPetId }
|
|
189
211
|
: configuredPet,
|
|
190
212
|
petChoices: this.petChoices.map(id => ({ value: id, label: this.petLabels.get(id) ?? id })),
|
|
191
|
-
petDiagnostics: this.diagnostics,
|
|
192
213
|
};
|
|
193
214
|
}
|
|
194
215
|
/**
|
|
@@ -202,29 +223,36 @@ export class PetSettingsCardController {
|
|
|
202
223
|
edit: (field, value) => {
|
|
203
224
|
if (!this.fallback())
|
|
204
225
|
return actions.edit(field, value);
|
|
205
|
-
if (field
|
|
226
|
+
if (field === 'petId')
|
|
227
|
+
this.stagedPetId = value === '' ? undefined : value;
|
|
228
|
+
else if (field === 'visible')
|
|
229
|
+
this.stagedVisible = value === '' ? undefined : value === 'true';
|
|
230
|
+
else
|
|
206
231
|
return;
|
|
207
|
-
this.stagedPetId = value === '' ? undefined : value;
|
|
208
232
|
this.petSaveFailed = false;
|
|
209
233
|
this.store.set(this.projection());
|
|
210
234
|
},
|
|
211
235
|
resetField: (field) => {
|
|
212
236
|
if (!this.fallback())
|
|
213
237
|
return actions.resetField(field);
|
|
214
|
-
if (field
|
|
238
|
+
if (field === 'petId')
|
|
239
|
+
this.stagedPetId = undefined;
|
|
240
|
+
else if (field === 'visible')
|
|
241
|
+
this.stagedVisible = undefined;
|
|
242
|
+
else
|
|
215
243
|
return;
|
|
216
|
-
this.stagedPetId = undefined;
|
|
217
244
|
this.petSaveFailed = false;
|
|
218
245
|
this.store.set(this.projection());
|
|
219
246
|
},
|
|
220
247
|
save: () => { if (this.fallback())
|
|
221
|
-
void this.
|
|
248
|
+
void this.saveFallback();
|
|
222
249
|
else
|
|
223
250
|
actions.save(); },
|
|
224
251
|
discard: () => {
|
|
225
252
|
if (!this.fallback())
|
|
226
253
|
return actions.discard();
|
|
227
254
|
this.stagedPetId = undefined;
|
|
255
|
+
this.stagedVisible = undefined;
|
|
228
256
|
this.petSaveFailed = false;
|
|
229
257
|
this.store.set(this.projection());
|
|
230
258
|
},
|
|
@@ -260,7 +288,7 @@ export function PetSettingsCard(props) {
|
|
|
260
288
|
invalidLabel: t('settings.invalidNumber'),
|
|
261
289
|
disabled,
|
|
262
290
|
};
|
|
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'); } }),
|
|
291
|
+
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'); } }), _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'); } })] }));
|
|
264
292
|
}
|
|
265
293
|
/** Render the pet settings card as a first-level settings page. */
|
|
266
294
|
export function PetSettingsSection(props) {
|
|
@@ -53,7 +53,6 @@ export declare const zh: {
|
|
|
53
53
|
readonly 'pet.moreSessions': "展开其余 {n} 个会话的气泡";
|
|
54
54
|
readonly 'pet.collapseSessions': "收起会话气泡";
|
|
55
55
|
readonly 'settings.title': "宠物";
|
|
56
|
-
readonly 'settings.diagnosticsTitle': "宠物目录诊断";
|
|
57
56
|
readonly 'settings.description': "选择宠物并调整它的显示布局。";
|
|
58
57
|
readonly 'settings.pet': "宠物";
|
|
59
58
|
readonly 'settings.petHint': "选择显示哪只宠物;每只宠物独立命名,可在宠物悬浮面板改名。";
|
|
@@ -136,7 +135,6 @@ export declare const en: {
|
|
|
136
135
|
readonly 'pet.moreSessions': "Expand {n} more session bubbles";
|
|
137
136
|
readonly 'pet.collapseSessions': "Collapse session bubbles";
|
|
138
137
|
readonly 'settings.title': "Pet";
|
|
139
|
-
readonly 'settings.diagnosticsTitle': "Pet directory diagnostics";
|
|
140
138
|
readonly 'settings.description': "Pick a pet and tune its display layout.";
|
|
141
139
|
readonly 'settings.pet': "Pet";
|
|
142
140
|
readonly 'settings.petHint': "Choose which pet shows. Names are stored per pet; rename from the pet hover panel.";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,mDAAmD;AACnD,eAAO,MAAM,EAAE,QAAQ,CAAA;AAEvB,oBAAoB;AACpB,eAAO,MAAM,EAAE
|
|
1
|
+
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,mDAAmD;AACnD,eAAO,MAAM,EAAE,QAAQ,CAAA;AAEvB,oBAAoB;AACpB,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkFL,CAAA;AAEV,oBAAoB;AACpB,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkFL,CAAA;AAEV,oCAAoC;AACpC,MAAM,MAAM,MAAM,GAAG,MAAM,OAAO,EAAE,CAAA;AAEpC,qDAAqD;AACrD,MAAM,MAAM,eAAe,GAAG,MAAM,CAAA;AAEpC;;;;;GAKG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAGnD;AAED;;;;;;;GAOG;AACH,wBAAgB,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAQvE;AAED,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,uBAAuB;QACvB,GAAG,EAAE,MAAM,CAAA;KACZ;CACF"}
|
|
@@ -55,7 +55,6 @@ export const zh = {
|
|
|
55
55
|
'pet.collapseSessions': '收起会话气泡',
|
|
56
56
|
// 一级设置页(settings.section 席位)。
|
|
57
57
|
'settings.title': '宠物',
|
|
58
|
-
'settings.diagnosticsTitle': '宠物目录诊断',
|
|
59
58
|
'settings.description': '选择宠物并调整它的显示布局。',
|
|
60
59
|
'settings.pet': '宠物',
|
|
61
60
|
'settings.petHint': '选择显示哪只宠物;每只宠物独立命名,可在宠物悬浮面板改名。',
|
|
@@ -140,7 +139,6 @@ export const en = {
|
|
|
140
139
|
'pet.collapseSessions': 'Collapse session bubbles',
|
|
141
140
|
// First-level settings section (the `settings.section` seat).
|
|
142
141
|
'settings.title': 'Pet',
|
|
143
|
-
'settings.diagnosticsTitle': 'Pet directory diagnostics',
|
|
144
142
|
'settings.description': 'Pick a pet and tune its display layout.',
|
|
145
143
|
'settings.pet': 'Pet',
|
|
146
144
|
'settings.petHint': 'Choose which pet shows. Names are stored per pet; rename from the pet hover panel.',
|
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. */
|