@lmzhen/dsh-evolution-settings-ui 0.16.0 → 0.17.0-rc.1
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.md +7 -0
- package/lib/client.js +305 -97
- package/lib/types/client/ParamCard.d.ts +24 -1
- package/lib/types/client/RowConfigCard.d.ts +30 -0
- package/lib/types/client/SettingsSection.d.ts +2 -1
- package/lib/types/client/generated-params.d.ts +11 -0
- package/lib/types/client/index.d.ts +31 -17
- package/lib/types/client/messages.d.ts +2 -1
- package/lib/types/client/seam.d.ts +95 -18
- package/lib/types/client/source.d.ts +41 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,6 +31,13 @@ writes and carries the two decisions plus a preview. It reads the approval plugi
|
|
|
31
31
|
(`src/client/api.ts` + `approval-routes.ts`; the path literals are compared against the host table by a spec),
|
|
32
32
|
wears the same card shell and button vocabulary as the parameter cards, and invents no state of its own.
|
|
33
33
|
|
|
34
|
+
The card rides that keyed slot with a key of its own (`approval-pending`): it has no namespace, so no
|
|
35
|
+
namespace-driven loop can name it — and a keyed slot renders ONLY the keys its section asks for, so
|
|
36
|
+
`SettingsSection` names it explicitly and puts it FIRST (it is the one card in this section that waits on a
|
|
37
|
+
decision). A registration nobody asks for renders nowhere: 0.16.0 shipped exactly that, which is why
|
|
38
|
+
`tests/section.client.spec.ts` now drives the real `apply()` and requires the section's `renderSlot` calls to cover
|
|
39
|
+
every key the bundle registers into the slot.
|
|
40
|
+
|
|
34
41
|
Its states are deliberately distinct: **reading** ("正在读取待批项…"), **empty** ("没有待批的写入") and
|
|
35
42
|
**failed** (the reason plus a retry). Merging the first two is what once made a slow answer read as lost data
|
|
36
43
|
(E7), so the state machine lives in `pending-state.ts` and the three are asserted without a browser.
|
package/lib/client.js
CHANGED
|
@@ -214,55 +214,6 @@ window.__ModuleLoader__.load({
|
|
|
214
214
|
}
|
|
215
215
|
]
|
|
216
216
|
},
|
|
217
|
-
{
|
|
218
|
-
namespace: "evolution-memory",
|
|
219
|
-
fields: [
|
|
220
|
-
{
|
|
221
|
-
id: "memoryChars",
|
|
222
|
-
group: "memory",
|
|
223
|
-
doc: "Character budget the memory store enforces for MEMORY.md.",
|
|
224
|
-
label: "记忆库上限",
|
|
225
|
-
hint: "长期记忆文件(MEMORY.md)最多多少字符。",
|
|
226
|
-
control: "number",
|
|
227
|
-
unit: "字符",
|
|
228
|
-
values: [],
|
|
229
|
-
valueLabels: []
|
|
230
|
-
},
|
|
231
|
-
{
|
|
232
|
-
id: "userChars",
|
|
233
|
-
group: "memory",
|
|
234
|
-
doc: "Character budget the memory store enforces for USER.md.",
|
|
235
|
-
label: "用户画像上限",
|
|
236
|
-
hint: "用户画像文件(USER.md)最多多少字符。",
|
|
237
|
-
control: "number",
|
|
238
|
-
unit: "字符",
|
|
239
|
-
values: [],
|
|
240
|
-
valueLabels: []
|
|
241
|
-
},
|
|
242
|
-
{
|
|
243
|
-
id: "addDatePrefix",
|
|
244
|
-
group: "memory",
|
|
245
|
-
doc: "Prefix stored memory entries with their date heading.",
|
|
246
|
-
label: "记忆条目加日期",
|
|
247
|
-
hint: "写入时在条目开头加上日期。",
|
|
248
|
-
control: "switch",
|
|
249
|
-
unit: "",
|
|
250
|
-
values: [],
|
|
251
|
-
valueLabels: []
|
|
252
|
-
},
|
|
253
|
-
{
|
|
254
|
-
id: "maxConsolidationFailures",
|
|
255
|
-
group: "memory",
|
|
256
|
-
doc: "Consolidation failures one turn tolerates before the tool gives up.",
|
|
257
|
-
label: "合并失败重试",
|
|
258
|
-
hint: "合并记忆连续失败多少次后停止。",
|
|
259
|
-
control: "number",
|
|
260
|
-
unit: "次",
|
|
261
|
-
values: [],
|
|
262
|
-
valueLabels: []
|
|
263
|
-
}
|
|
264
|
-
]
|
|
265
|
-
},
|
|
266
217
|
{
|
|
267
218
|
namespace: "evolution-review",
|
|
268
219
|
fields: [
|
|
@@ -354,7 +305,70 @@ window.__ModuleLoader__.load({
|
|
|
354
305
|
]
|
|
355
306
|
},
|
|
356
307
|
{
|
|
357
|
-
namespace: "
|
|
308
|
+
namespace: "memory-files",
|
|
309
|
+
fields: [
|
|
310
|
+
{
|
|
311
|
+
id: "memoryChars",
|
|
312
|
+
group: "memory",
|
|
313
|
+
doc: "Character budget the memory store enforces for MEMORY.md.",
|
|
314
|
+
label: "记忆库上限",
|
|
315
|
+
hint: "长期记忆文件(MEMORY.md)最多多少字符。",
|
|
316
|
+
control: "number",
|
|
317
|
+
unit: "字符",
|
|
318
|
+
values: [],
|
|
319
|
+
valueLabels: []
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
id: "userChars",
|
|
323
|
+
group: "memory",
|
|
324
|
+
doc: "Character budget the memory store enforces for USER.md.",
|
|
325
|
+
label: "用户画像上限",
|
|
326
|
+
hint: "用户画像文件(USER.md)最多多少字符。",
|
|
327
|
+
control: "number",
|
|
328
|
+
unit: "字符",
|
|
329
|
+
values: [],
|
|
330
|
+
valueLabels: []
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
id: "addDatePrefix",
|
|
334
|
+
group: "memory",
|
|
335
|
+
doc: "Prefix stored memory entries with their date heading.",
|
|
336
|
+
label: "记忆条目加日期",
|
|
337
|
+
hint: "写入时在条目开头加上日期。",
|
|
338
|
+
control: "switch",
|
|
339
|
+
unit: "",
|
|
340
|
+
values: [],
|
|
341
|
+
valueLabels: []
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
id: "maxConsolidationFailures",
|
|
345
|
+
group: "memory",
|
|
346
|
+
doc: "Consolidation failures one turn tolerates before the tool gives up.",
|
|
347
|
+
label: "合并失败重试",
|
|
348
|
+
hint: "合并记忆连续失败多少次后停止。",
|
|
349
|
+
control: "number",
|
|
350
|
+
unit: "次",
|
|
351
|
+
values: [],
|
|
352
|
+
valueLabels: []
|
|
353
|
+
}
|
|
354
|
+
]
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
namespace: "tool-memory",
|
|
358
|
+
fields: [{
|
|
359
|
+
id: "entryPreviewChars",
|
|
360
|
+
group: "memory",
|
|
361
|
+
doc: "Characters of one memory entry shown in a tool result preview.",
|
|
362
|
+
label: "记忆预览长度",
|
|
363
|
+
hint: "列表里每条记忆最多显示多少字符。",
|
|
364
|
+
control: "number",
|
|
365
|
+
unit: "字符",
|
|
366
|
+
values: [],
|
|
367
|
+
valueLabels: []
|
|
368
|
+
}]
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
namespace: "tool-skill-manage",
|
|
358
372
|
fields: [
|
|
359
373
|
{
|
|
360
374
|
id: "skillContentChars",
|
|
@@ -475,20 +489,37 @@ window.__ModuleLoader__.load({
|
|
|
475
489
|
valueLabels: []
|
|
476
490
|
}
|
|
477
491
|
]
|
|
492
|
+
}
|
|
493
|
+
];
|
|
494
|
+
/** Every (bundle, row) pair the Plugins page can open a configuration page for. */
|
|
495
|
+
const CLIENT_ROW_SEATS = [
|
|
496
|
+
{
|
|
497
|
+
bundle: "@lmzhen/dsh-evolution-all",
|
|
498
|
+
rows: [
|
|
499
|
+
"evolution-curator",
|
|
500
|
+
"evolution-review",
|
|
501
|
+
"memory-files",
|
|
502
|
+
"tool-memory",
|
|
503
|
+
"tool-skill-manage"
|
|
504
|
+
]
|
|
478
505
|
},
|
|
479
506
|
{
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
507
|
+
bundle: "@lmzhen/dsh-evolution-host",
|
|
508
|
+
rows: [
|
|
509
|
+
"evolution-curator",
|
|
510
|
+
"evolution-review",
|
|
511
|
+
"memory-files"
|
|
512
|
+
]
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
bundle: "@lmzhen/dsh-evolution-preset",
|
|
516
|
+
rows: [
|
|
517
|
+
"evolution-curator",
|
|
518
|
+
"evolution-review",
|
|
519
|
+
"memory-files",
|
|
520
|
+
"tool-memory",
|
|
521
|
+
"tool-skill-manage"
|
|
522
|
+
]
|
|
492
523
|
}
|
|
493
524
|
];
|
|
494
525
|
//#endregion
|
|
@@ -769,11 +800,12 @@ window.__ModuleLoader__.load({
|
|
|
769
800
|
/** Chinese copy (the family's primary language). */
|
|
770
801
|
const zh = {
|
|
771
802
|
title: "自进化",
|
|
772
|
-
subtitle: "
|
|
803
|
+
subtitle: "待你决定的写入在最上面;下面按功能列出你能改的参数,没有列出的由安装时的配置决定。",
|
|
773
804
|
overridden: "我改过",
|
|
774
805
|
deployment: "默认",
|
|
775
806
|
loading: "正在读取设置…",
|
|
776
807
|
unavailable: "这个功能没有启动(对应的插件没有安装)",
|
|
808
|
+
seatMissing: "这个界面没有提供设置服务,参数只能看不能改",
|
|
777
809
|
readonly: "本次会话不允许写入设置(临时运行模式)",
|
|
778
810
|
apply: "保存",
|
|
779
811
|
reset: "恢复默认值",
|
|
@@ -814,11 +846,12 @@ window.__ModuleLoader__.load({
|
|
|
814
846
|
/** English copy, key for key with {@link zh}. */
|
|
815
847
|
const en = {
|
|
816
848
|
title: "Self-evolution",
|
|
817
|
-
subtitle: "
|
|
849
|
+
subtitle: "Writes waiting for your decision come first; the changeable parameters are listed by feature below, and anything not listed comes from the deployment configuration.",
|
|
818
850
|
overridden: "edited by you",
|
|
819
851
|
deployment: "default",
|
|
820
852
|
loading: "reading settings…",
|
|
821
853
|
unavailable: "this feature is not running (its plugin is not installed)",
|
|
854
|
+
seatMissing: "this shell serves no settings surface, so the parameters cannot be edited here",
|
|
822
855
|
readonly: "settings cannot be written in this session (temporary mode)",
|
|
823
856
|
apply: "Save",
|
|
824
857
|
reset: "Reset to default",
|
|
@@ -863,10 +896,10 @@ window.__ModuleLoader__.load({
|
|
|
863
896
|
*/
|
|
864
897
|
const NAMESPACE_TITLES = Object.freeze({
|
|
865
898
|
"evolution-review": "cardReview",
|
|
866
|
-
"
|
|
867
|
-
"
|
|
899
|
+
"memory-files": "cardMemory",
|
|
900
|
+
"tool-memory": "cardToolMemory",
|
|
868
901
|
"evolution-curator": "cardCurator",
|
|
869
|
-
"
|
|
902
|
+
"tool-skill-manage": "cardSkills"
|
|
870
903
|
});
|
|
871
904
|
/**
|
|
872
905
|
* Resolve one message without the locale seat (tests and the fallback path).
|
|
@@ -910,7 +943,7 @@ window.__ModuleLoader__.load({
|
|
|
910
943
|
* settings scope and its snapshot are declared by the shape the renderer hands
|
|
911
944
|
* over (\`getSnapshot\`/\`subscribe\`, status/value/user/writable).
|
|
912
945
|
*
|
|
913
|
-
*
|
|
946
|
+
* header (namespace title, change count, chevron), then one block per field —
|
|
914
947
|
* header (namespace title, change count, chevron), then one block per field —
|
|
915
948
|
* label with its unit and source chip, a control HOLDING THE CURRENT VALUE, the
|
|
916
949
|
* Chinese hint — and one 放弃修改/保存 pair at the card's foot. The control is
|
|
@@ -944,7 +977,7 @@ window.__ModuleLoader__.load({
|
|
|
944
977
|
}
|
|
945
978
|
}
|
|
946
979
|
/** Whether a value is a plain section object. */
|
|
947
|
-
function isSection(value) {
|
|
980
|
+
function isSection$1(value) {
|
|
948
981
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
949
982
|
}
|
|
950
983
|
/** One field's control, typed from the registry. */
|
|
@@ -1004,22 +1037,21 @@ window.__ModuleLoader__.load({
|
|
|
1004
1037
|
}, t("reset"))) : null);
|
|
1005
1038
|
}
|
|
1006
1039
|
/**
|
|
1007
|
-
* Render one
|
|
1008
|
-
* @param props - the card data, its callbacks, and the
|
|
1040
|
+
* Render one row's card body from a supplied snapshot.
|
|
1041
|
+
* @param props - the card data, its callbacks, and the row state to render.
|
|
1009
1042
|
* @returns the card.
|
|
1010
1043
|
*/
|
|
1011
|
-
function
|
|
1012
|
-
const { t, fields, write, clear, namespace } = props;
|
|
1044
|
+
function ParamCardView(props) {
|
|
1045
|
+
const { t, fields, write, clear, namespace, snapshot } = props;
|
|
1013
1046
|
const [open, setOpen] = (0, react.useState)(false);
|
|
1014
1047
|
const [draft, setDraft] = (0, react.useState)({});
|
|
1015
1048
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
1016
1049
|
const [error, setError] = (0, react.useState)("");
|
|
1017
1050
|
const [pending, setPending] = (0, react.useState)(null);
|
|
1018
|
-
const snapshot = props.useParamSection((state) => state);
|
|
1019
1051
|
if (snapshot.status === "loading") return (0, react.createElement)("p", { className: "evolution-param-note" }, t("loading"));
|
|
1020
|
-
if (snapshot.status === "unavailable") return (0, react.createElement)("p", { className: "evolution-param-note" }, t("unavailable"));
|
|
1021
|
-
const user = isSection(snapshot.user) ? snapshot.user : {};
|
|
1022
|
-
const value = isSection(snapshot.value) ? snapshot.value : {};
|
|
1052
|
+
if (snapshot.status === "unavailable") return (0, react.createElement)("p", { className: "evolution-param-note" }, t(snapshot.reason === "seat-missing" ? "seatMissing" : "unavailable"));
|
|
1053
|
+
const user = isSection$1(snapshot.user) ? snapshot.user : {};
|
|
1054
|
+
const value = isSection$1(snapshot.value) ? snapshot.value : {};
|
|
1023
1055
|
const disabled = !snapshot.writable;
|
|
1024
1056
|
const locked = disabled || busy;
|
|
1025
1057
|
const titleKey = NAMESPACE_TITLES[namespace];
|
|
@@ -1105,14 +1137,174 @@ window.__ModuleLoader__.load({
|
|
|
1105
1137
|
onClick: save
|
|
1106
1138
|
}, busy ? t("saving") : t("apply")))) : null);
|
|
1107
1139
|
}
|
|
1140
|
+
/**
|
|
1141
|
+
* The card as our own section renders it: the row state comes from the hook seat the
|
|
1142
|
+
* renderer binds out of the inject face's `hooks` compartment (components never hold a
|
|
1143
|
+
* subscription of their own).
|
|
1144
|
+
* @param props - the card data, its callbacks, and the bound row hook.
|
|
1145
|
+
* @returns the card.
|
|
1146
|
+
*/
|
|
1147
|
+
function ParamCard(props) {
|
|
1148
|
+
return ParamCardView({
|
|
1149
|
+
namespace: props.namespace,
|
|
1150
|
+
fields: props.fields,
|
|
1151
|
+
t: props.t,
|
|
1152
|
+
write: props.write,
|
|
1153
|
+
clear: props.clear,
|
|
1154
|
+
snapshot: props.useParamSection((state) => state)
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
//#endregion
|
|
1158
|
+
//#region src/client/source.ts
|
|
1159
|
+
/** What a card shows when this UI found no settings seat at all. */
|
|
1160
|
+
const SEAT_MISSING = Object.freeze({
|
|
1161
|
+
status: "unavailable",
|
|
1162
|
+
value: void 0,
|
|
1163
|
+
user: void 0,
|
|
1164
|
+
writable: false,
|
|
1165
|
+
reason: "seat-missing"
|
|
1166
|
+
});
|
|
1167
|
+
/**
|
|
1168
|
+
* Whether a value is a plain section object.
|
|
1169
|
+
* @param value - the value to test.
|
|
1170
|
+
* @returns true for a non-null, non-array object.
|
|
1171
|
+
*/
|
|
1172
|
+
function isSection(value) {
|
|
1173
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Project one platform snapshot into the card's own shape.
|
|
1177
|
+
* @param snapshot - the seat's snapshot.
|
|
1178
|
+
* @returns the card's snapshot, with the reason an unavailable one is unavailable.
|
|
1179
|
+
*/
|
|
1180
|
+
function sectionSnapshotOf(snapshot) {
|
|
1181
|
+
return {
|
|
1182
|
+
status: snapshot.status,
|
|
1183
|
+
value: isSection(snapshot.value) ? snapshot.value : void 0,
|
|
1184
|
+
user: snapshot.user,
|
|
1185
|
+
writable: snapshot.writable,
|
|
1186
|
+
...snapshot.status === "unavailable" ? { reason: "not-served" } : {}
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
/**
|
|
1190
|
+
* The live source one row's card binds.
|
|
1191
|
+
* @param seat - the probed settings seat, when the deployment composed it.
|
|
1192
|
+
* @param namespace - the row's entry id.
|
|
1193
|
+
* @returns the source; without a seat it reports the stated empty state and never notifies.
|
|
1194
|
+
*/
|
|
1195
|
+
function sourceFor(seat, namespace) {
|
|
1196
|
+
if (seat === void 0) return {
|
|
1197
|
+
getSnapshot: () => SEAT_MISSING,
|
|
1198
|
+
subscribe: () => () => {}
|
|
1199
|
+
};
|
|
1200
|
+
const form = seat.get(namespace);
|
|
1201
|
+
return {
|
|
1202
|
+
getSnapshot: () => sectionSnapshotOf(form.getSnapshot()),
|
|
1203
|
+
subscribe: (listener) => form.subscribe(listener)
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* The write pair for one row.
|
|
1208
|
+
* @param seat - the probed settings seat, when the deployment composed it.
|
|
1209
|
+
* @param namespace - the row's entry id.
|
|
1210
|
+
* @returns the two calls a card's save/reset path uses; without a seat they resolve without writing.
|
|
1211
|
+
*/
|
|
1212
|
+
function writeFaceFor(seat, namespace) {
|
|
1213
|
+
const form = () => seat?.get(namespace);
|
|
1214
|
+
return {
|
|
1215
|
+
write: async (field, value) => {
|
|
1216
|
+
await form()?.set(field, value);
|
|
1217
|
+
},
|
|
1218
|
+
clear: async (field) => {
|
|
1219
|
+
await form()?.unset(field);
|
|
1220
|
+
}
|
|
1221
|
+
};
|
|
1222
|
+
}
|
|
1223
|
+
//#endregion
|
|
1224
|
+
//#region src/client/RowConfigCard.ts
|
|
1225
|
+
/**
|
|
1226
|
+
* The Plugins page's per-row configuration entry for a family row.
|
|
1227
|
+
*
|
|
1228
|
+
* The page owns the settings subscription — it re-renders the entry — and passes the row's
|
|
1229
|
+
* form in, so this component renders the SAME card body as our own section, fed by the
|
|
1230
|
+
* page's state instead of the bound hook and therefore holding no subscription of its own.
|
|
1231
|
+
* `summary` answers the page's one-line slot: a row's description fallback, which stays
|
|
1232
|
+
* empty until the row actually overrides something, exactly as the page expects.
|
|
1233
|
+
* @module @lmzhen/dsh-evolution-settings-ui/client
|
|
1234
|
+
*/
|
|
1235
|
+
/**
|
|
1236
|
+
* Count the keys this row's user layer holds.
|
|
1237
|
+
* @param user - the raw user section, as the page reports it.
|
|
1238
|
+
* @param fields - the row's fields.
|
|
1239
|
+
* @returns how many of them the user overrode.
|
|
1240
|
+
*/
|
|
1241
|
+
function overridden(user, fields) {
|
|
1242
|
+
const layer = isSection(user) ? user : {};
|
|
1243
|
+
return fields.filter((field) => Object.hasOwn(layer, field.id)).length;
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Render one row's configuration entry.
|
|
1247
|
+
* @param props - the page's view/form seat plus this row's face.
|
|
1248
|
+
* @returns the entry; `summary` renders nothing for an untouched row (the page then keeps the row's own description).
|
|
1249
|
+
*/
|
|
1250
|
+
function RowConfigCard(props) {
|
|
1251
|
+
const { t, fields, namespace, view, form } = props;
|
|
1252
|
+
if (form === void 0) return view === "summary" ? null : (0, react.createElement)("p", { className: "evolution-param-note" }, t("seatMissing"));
|
|
1253
|
+
const changed = overridden(form.state.user, fields);
|
|
1254
|
+
if (view === "summary") return changed === 0 ? null : (0, react.createElement)("span", null, t("changed").replace("{n}", String(changed)));
|
|
1255
|
+
return ParamCardView({
|
|
1256
|
+
namespace,
|
|
1257
|
+
fields,
|
|
1258
|
+
t,
|
|
1259
|
+
snapshot: sectionSnapshotOf(form.state),
|
|
1260
|
+
write: async (field, value) => {
|
|
1261
|
+
await form.mutate([{
|
|
1262
|
+
op: "set",
|
|
1263
|
+
path: [field],
|
|
1264
|
+
value
|
|
1265
|
+
}], form.state.revision);
|
|
1266
|
+
},
|
|
1267
|
+
clear: async (field) => {
|
|
1268
|
+
await form.mutate([{
|
|
1269
|
+
op: "unset",
|
|
1270
|
+
path: [field]
|
|
1271
|
+
}], form.state.revision);
|
|
1272
|
+
}
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1108
1275
|
//#endregion
|
|
1109
1276
|
//#region src/client/seam.ts
|
|
1110
1277
|
/** Our card slot, hosted by the section this bundle registers. */
|
|
1111
1278
|
const CARD_SLOT = "evolution.namespace.card";
|
|
1279
|
+
/** The Plugins page's per-row configuration seat (declared by `ui-plugin-manager`). */
|
|
1280
|
+
const ROW_CONFIG_SLOT = "plugins.row.config";
|
|
1281
|
+
/**
|
|
1282
|
+
* The key the Plugins page files a row's configuration under.
|
|
1283
|
+
*
|
|
1284
|
+
* Spelled here because the page's own helper cannot be imported (a value import from
|
|
1285
|
+
* another plugin's client half is forbidden):
|
|
1286
|
+
* `ui-plugin-manager/src/client/config-ledger.ts:36` is the definition this mirrors.
|
|
1287
|
+
* @param bundle - the bundle package name that declares the row.
|
|
1288
|
+
* @param rowId - the row id that bundle's patch declares.
|
|
1289
|
+
* @returns the `plugins.row.config` key.
|
|
1290
|
+
*/
|
|
1291
|
+
function rowConfigKey(bundle, rowId) {
|
|
1292
|
+
return bundle + "#" + rowId;
|
|
1293
|
+
}
|
|
1294
|
+
/**
|
|
1295
|
+
* The pending-window card's slot key.
|
|
1296
|
+
*
|
|
1297
|
+
* The section pairs cards by key and the parameter cards use their settings namespace; this card has no
|
|
1298
|
+
* namespace (its data comes from the host's approval routes), so it carries a key of its own. The constant
|
|
1299
|
+
* lives HERE and not in `index.ts` because the SECTION has to name it too, and `index.ts` already imports
|
|
1300
|
+
* the section — one definition in the module both sides already share is what keeps that pair acyclic.
|
|
1301
|
+
*/
|
|
1302
|
+
const PENDING_CARD_KEY = "approval-pending";
|
|
1112
1303
|
//#endregion
|
|
1113
1304
|
//#region src/client/SettingsSection.ts
|
|
1114
1305
|
/**
|
|
1115
|
-
* The section shell: title, one-line explanation, then one card per namespace
|
|
1306
|
+
* The section shell: title, one-line explanation, then one card per namespace — with the staged-write
|
|
1307
|
+
* window's card ahead of them all, because it is the only card here that waits on a decision.
|
|
1116
1308
|
*
|
|
1117
1309
|
* This is the shape the platform's own settings sections use — a heading plus the
|
|
1118
1310
|
* child slots it declared in its registration. The cards themselves are separate
|
|
@@ -1126,7 +1318,10 @@ window.__ModuleLoader__.load({
|
|
|
1126
1318
|
* @returns the section element.
|
|
1127
1319
|
*/
|
|
1128
1320
|
function SettingsSection(face) {
|
|
1129
|
-
return (0, react.createElement)("div", { className: "evolution-params" }, (0, react.createElement)("h2", { className: "evolution-params-title" }, face.t("title")), (0, react.createElement)("p", { className: "evolution-params-subtitle" }, face.t("subtitle")),
|
|
1321
|
+
return (0, react.createElement)("div", { className: "evolution-params" }, (0, react.createElement)("h2", { className: "evolution-params-title" }, face.t("title")), (0, react.createElement)("p", { className: "evolution-params-subtitle" }, face.t("subtitle")), (0, react.createElement)("div", {
|
|
1322
|
+
key: PENDING_CARD_KEY,
|
|
1323
|
+
className: "evolution-params-group"
|
|
1324
|
+
}, face.renderSlot(CARD_SLOT, {}, { entryKey: PENDING_CARD_KEY })), ...CLIENT_PARAM_SECTIONS.map((section_) => (0, react.createElement)("div", {
|
|
1130
1325
|
key: section_.namespace,
|
|
1131
1326
|
className: "evolution-params-group"
|
|
1132
1327
|
}, face.renderSlot(CARD_SLOT, {}, { entryKey: section_.namespace }))));
|
|
@@ -1225,24 +1420,20 @@ window.__ModuleLoader__.load({
|
|
|
1225
1420
|
}
|
|
1226
1421
|
//#endregion
|
|
1227
1422
|
//#region src/client/index.ts
|
|
1228
|
-
/** Required client services: the slot registry, the settings transport, the locale seat. */
|
|
1229
|
-
const inject = [
|
|
1230
|
-
"slots",
|
|
1231
|
-
"settingsScope",
|
|
1232
|
-
"locale"
|
|
1233
|
-
];
|
|
1234
|
-
/** Section id and nav order (the platform's own sections sit at 0/10/15/20). */
|
|
1235
|
-
const SECTION_ID = "evolution";
|
|
1236
|
-
const SECTION_ORDER = 25;
|
|
1237
1423
|
/**
|
|
1238
|
-
*
|
|
1424
|
+
* Required client services: the slot registry and the locale seat.
|
|
1239
1425
|
*
|
|
1240
|
-
* The
|
|
1241
|
-
*
|
|
1426
|
+
* The settings seat (`configForms`) is deliberately NOT here. A required inject for a
|
|
1427
|
+
* service the deployment did not compose leaves this whole browser half PENDING forever —
|
|
1428
|
+
* no error, no section, no UI — and the family is also installed without a settings
|
|
1429
|
+
* surface. It is probed instead, and the cards say what is missing.
|
|
1242
1430
|
*/
|
|
1243
|
-
const
|
|
1431
|
+
const inject = ["slots", "locale"];
|
|
1432
|
+
/** Section id and nav order (the platform's own sections sit at 0/10/15/20). */
|
|
1433
|
+
const SECTION_ID = "evolution";
|
|
1434
|
+
const SECTION_ORDER = 25;
|
|
1244
1435
|
/**
|
|
1245
|
-
* Register the locale namespace, the section and
|
|
1436
|
+
* Register the locale namespace, the section, its cards, and the per-row configuration entries.
|
|
1246
1437
|
* @param ctx - client cordis context.
|
|
1247
1438
|
*/
|
|
1248
1439
|
function apply(ctx) {
|
|
@@ -1271,6 +1462,7 @@ window.__ModuleLoader__.load({
|
|
|
1271
1462
|
}
|
|
1272
1463
|
return bound(key);
|
|
1273
1464
|
};
|
|
1465
|
+
const configForms = ctx.get("configForms");
|
|
1274
1466
|
seam.slots.inject("settings.section", function* () {
|
|
1275
1467
|
yield seam.slots.register({
|
|
1276
1468
|
name: "settings.section",
|
|
@@ -1286,14 +1478,14 @@ window.__ModuleLoader__.load({
|
|
|
1286
1478
|
});
|
|
1287
1479
|
seam.slots.inject(CARD_SLOT, function* () {
|
|
1288
1480
|
for (const section of CLIENT_PARAM_SECTIONS) {
|
|
1289
|
-
const
|
|
1290
|
-
const
|
|
1481
|
+
const source = sourceFor(configForms, section.namespace);
|
|
1482
|
+
const { write, clear } = writeFaceFor(configForms, section.namespace);
|
|
1291
1483
|
const face = () => ({
|
|
1292
1484
|
namespace: section.namespace,
|
|
1293
1485
|
fields: section.fields,
|
|
1294
1486
|
t,
|
|
1295
|
-
write
|
|
1296
|
-
clear
|
|
1487
|
+
write,
|
|
1488
|
+
clear,
|
|
1297
1489
|
hooks: { paramSection: source }
|
|
1298
1490
|
});
|
|
1299
1491
|
yield seam.slots.register({
|
|
@@ -1312,6 +1504,22 @@ window.__ModuleLoader__.load({
|
|
|
1312
1504
|
})
|
|
1313
1505
|
}, PendingCard);
|
|
1314
1506
|
});
|
|
1507
|
+
seam.slots.inject(ROW_CONFIG_SLOT, function* () {
|
|
1508
|
+
for (const seat of CLIENT_ROW_SEATS) for (const rowId of seat.rows) {
|
|
1509
|
+
const section = CLIENT_PARAM_SECTIONS.find((candidate) => candidate.namespace === rowId);
|
|
1510
|
+
if (section === void 0) continue;
|
|
1511
|
+
const face = () => ({
|
|
1512
|
+
namespace: rowId,
|
|
1513
|
+
fields: section.fields,
|
|
1514
|
+
t
|
|
1515
|
+
});
|
|
1516
|
+
yield seam.slots.register({
|
|
1517
|
+
name: ROW_CONFIG_SLOT,
|
|
1518
|
+
key: rowConfigKey(seat.bundle, rowId),
|
|
1519
|
+
inject: face
|
|
1520
|
+
}, RowConfigCard);
|
|
1521
|
+
}
|
|
1522
|
+
});
|
|
1315
1523
|
}
|
|
1316
1524
|
//#endregion
|
|
1317
1525
|
exports.PENDING_CARD_KEY = PENDING_CARD_KEY;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* settings scope and its snapshot are declared by the shape the renderer hands
|
|
7
7
|
* over (\`getSnapshot\`/\`subscribe\`, status/value/user/writable).
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* header (namespace title, change count, chevron), then one block per field —
|
|
10
10
|
* header (namespace title, change count, chevron), then one block per field —
|
|
11
11
|
* label with its unit and source chip, a control HOLDING THE CURRENT VALUE, the
|
|
12
12
|
* Chinese hint — and one 放弃修改/保存 pair at the card's foot. The control is
|
|
@@ -49,5 +49,28 @@ export type ParamCardProps = Omit<ParamCardFace, 'hooks'> & {
|
|
|
49
49
|
* @param props - the card data, its callbacks, and the bound section hook.
|
|
50
50
|
* @returns the card.
|
|
51
51
|
*/
|
|
52
|
+
/** Props of the shared body: the card's own data plus the row state it renders. */
|
|
53
|
+
export interface ParamCardViewProps {
|
|
54
|
+
namespace: string;
|
|
55
|
+
fields: readonly ClientParamField[];
|
|
56
|
+
t: (key: MessageKey) => string;
|
|
57
|
+
write: (field: string, value: unknown) => Promise<void>;
|
|
58
|
+
clear: (field: string) => Promise<void>;
|
|
59
|
+
/** The current row state, from whichever source owns it (the bound hook or the page). */
|
|
60
|
+
snapshot: ParamSectionSnapshot;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Render one row's card body from a supplied snapshot.
|
|
64
|
+
* @param props - the card data, its callbacks, and the row state to render.
|
|
65
|
+
* @returns the card.
|
|
66
|
+
*/
|
|
67
|
+
export declare function ParamCardView(props: ParamCardViewProps): ReactNode;
|
|
68
|
+
/**
|
|
69
|
+
* The card as our own section renders it: the row state comes from the hook seat the
|
|
70
|
+
* renderer binds out of the inject face's `hooks` compartment (components never hold a
|
|
71
|
+
* subscription of their own).
|
|
72
|
+
* @param props - the card data, its callbacks, and the bound row hook.
|
|
73
|
+
* @returns the card.
|
|
74
|
+
*/
|
|
52
75
|
export declare function ParamCard(props: ParamCardProps): ReactNode;
|
|
53
76
|
//# sourceMappingURL=ParamCard.d.ts.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Plugins page's per-row configuration entry for a family row.
|
|
3
|
+
*
|
|
4
|
+
* The page owns the settings subscription — it re-renders the entry — and passes the row's
|
|
5
|
+
* form in, so this component renders the SAME card body as our own section, fed by the
|
|
6
|
+
* page's state instead of the bound hook and therefore holding no subscription of its own.
|
|
7
|
+
* `summary` answers the page's one-line slot: a row's description fallback, which stays
|
|
8
|
+
* empty until the row actually overrides something, exactly as the page expects.
|
|
9
|
+
* @module @lmzhen/dsh-evolution-settings-ui/client
|
|
10
|
+
*/
|
|
11
|
+
import { type ReactNode } from 'react';
|
|
12
|
+
import type { ClientParamField } from './generated-params.ts';
|
|
13
|
+
import type { MessageKey } from './messages.ts';
|
|
14
|
+
import type { RowConfigViewProps } from './seam.ts';
|
|
15
|
+
/** The inject face every row registration carries (the page supplies `view` and `form`). */
|
|
16
|
+
export interface RowConfigFace {
|
|
17
|
+
/** The row's entry id, for the card title. */
|
|
18
|
+
namespace: string;
|
|
19
|
+
fields: readonly ClientParamField[];
|
|
20
|
+
t: (key: MessageKey) => string;
|
|
21
|
+
}
|
|
22
|
+
/** Props one row entry is rendered with: the page's seat plus this row's own face. */
|
|
23
|
+
export type RowConfigCardProps = RowConfigViewProps & RowConfigFace;
|
|
24
|
+
/**
|
|
25
|
+
* Render one row's configuration entry.
|
|
26
|
+
* @param props - the page's view/form seat plus this row's face.
|
|
27
|
+
* @returns the entry; `summary` renders nothing for an untouched row (the page then keeps the row's own description).
|
|
28
|
+
*/
|
|
29
|
+
export declare function RowConfigCard(props: RowConfigCardProps): ReactNode;
|
|
30
|
+
//# sourceMappingURL=RowConfigCard.d.ts.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The section shell: title, one-line explanation, then one card per namespace
|
|
2
|
+
* The section shell: title, one-line explanation, then one card per namespace — with the staged-write
|
|
3
|
+
* window's card ahead of them all, because it is the only card here that waits on a decision.
|
|
3
4
|
*
|
|
4
5
|
* This is the shape the platform's own settings sections use — a heading plus the
|
|
5
6
|
* child slots it declared in its registration. The cards themselves are separate
|
|
@@ -29,4 +29,15 @@ export interface ClientParamSection {
|
|
|
29
29
|
fields: readonly ClientParamField[];
|
|
30
30
|
}
|
|
31
31
|
export declare const CLIENT_PARAM_SECTIONS: readonly ClientParamSection[];
|
|
32
|
+
/**
|
|
33
|
+
* One bundle that mounts family rows: the plugins.row.config key of a row is
|
|
34
|
+
* <bundle package name>#<row id>. The names are the SOURCE-plane spellings -
|
|
35
|
+
* the release rescopes them with every other package literal.
|
|
36
|
+
*/
|
|
37
|
+
export interface ClientRowSeat {
|
|
38
|
+
bundle: string;
|
|
39
|
+
rows: readonly string[];
|
|
40
|
+
}
|
|
41
|
+
/** Every (bundle, row) pair the Plugins page can open a configuration page for. */
|
|
42
|
+
export declare const CLIENT_ROW_SEATS: readonly ClientRowSeat[];
|
|
32
43
|
//# sourceMappingURL=generated-params.d.ts.map
|
|
@@ -1,35 +1,49 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Browser half: one settings section ("自进化") hosting one card per
|
|
2
|
+
* Browser half: one settings section ("自进化") hosting one card per family row, plus the
|
|
3
|
+
* Plugins page's per-row configuration entry for each of those rows.
|
|
3
4
|
*
|
|
4
5
|
* Until 0.7.0 the cards lived inside the platform's own 插件 section, because
|
|
5
6
|
* this bundle injected `@deepseek-ai/dsh-client-ui-settings-plugins` and claimed
|
|
6
7
|
* its keyed `settings.plugin.item` slot. The parameters are not a plugin
|
|
7
8
|
* inventory though — the other feature areas (market, skins, Web plugins, side
|
|
8
|
-
* cards) each register their OWN section, so this bundle
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* cards) each register their OWN section, so this bundle does the same: inject
|
|
10
|
+
* the settings shell, register `settings.section`, and host the cards in a keyed
|
|
11
|
+
* child slot of that section.
|
|
11
12
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* Since G2 that section is one of TWO render sites for the same card body. The other is
|
|
14
|
+
* the platform's Plugins page: each family row can be opened there, and the page renders
|
|
15
|
+
* whatever a plugin registered under key `<bundle package name>#<row id>`
|
|
16
|
+
* (`plugins.row.config`). The page owns that entry's subscription and hands the row's form
|
|
17
|
+
* in, so that path needs no seat of ours; our own section binds the same card to the
|
|
18
|
+
* client settings seat (`configForms`).
|
|
19
|
+
*
|
|
20
|
+
* The join key is the row's ENTRY ID — the platform derives a settings namespace from the
|
|
21
|
+
* Loader entry id, and that id is the registry's owner name — so the card, the seat lookup
|
|
22
|
+
* and the page key all spell one id. The legacy namespace strings in the registry
|
|
23
|
+
* (`evolution-memory`, …) are the G3 migration source, never a live lookup key.
|
|
24
|
+
*
|
|
25
|
+
* A row the Host does not serve renders nothing, and a deployment that drops this row
|
|
26
|
+
* shows no section at all: the pair carries no Host-side behaviour.
|
|
16
27
|
* @module @lmzhen/dsh-evolution-settings-ui/client
|
|
17
28
|
*/
|
|
18
29
|
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
19
|
-
|
|
30
|
+
import { PENDING_CARD_KEY } from './seam.ts';
|
|
31
|
+
/**
|
|
32
|
+
* Required client services: the slot registry and the locale seat.
|
|
33
|
+
*
|
|
34
|
+
* The settings seat (`configForms`) is deliberately NOT here. A required inject for a
|
|
35
|
+
* service the deployment did not compose leaves this whole browser half PENDING forever —
|
|
36
|
+
* no error, no section, no UI — and the family is also installed without a settings
|
|
37
|
+
* surface. It is probed instead, and the cards say what is missing.
|
|
38
|
+
*/
|
|
20
39
|
export declare const inject: string[];
|
|
21
40
|
/** Section id and nav order (the platform's own sections sit at 0/10/15/20). */
|
|
22
41
|
export declare const SECTION_ID = "evolution";
|
|
23
42
|
export declare const SECTION_ORDER = 25;
|
|
43
|
+
/** The pending-window card's key, re-exported from `seam.ts` (the section names it as well). */
|
|
44
|
+
export { PENDING_CARD_KEY };
|
|
24
45
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* The section pairs cards by key and the parameter cards use their settings namespace; this card has no
|
|
28
|
-
* namespace (its data comes from the host's approval routes), so it carries a key of its own.
|
|
29
|
-
*/
|
|
30
|
-
export declare const PENDING_CARD_KEY = "approval-pending";
|
|
31
|
-
/**
|
|
32
|
-
* Register the locale namespace, the section and its cards.
|
|
46
|
+
* Register the locale namespace, the section, its cards, and the per-row configuration entries.
|
|
33
47
|
* @param ctx - client cordis context.
|
|
34
48
|
*/
|
|
35
49
|
export declare function apply(ctx: ClientContext): void;
|
|
@@ -13,11 +13,12 @@ export declare const NS = "evolution-settings";
|
|
|
13
13
|
/** Chinese copy (the family's primary language). */
|
|
14
14
|
export declare const zh: {
|
|
15
15
|
readonly title: "自进化";
|
|
16
|
-
readonly subtitle: "
|
|
16
|
+
readonly subtitle: "待你决定的写入在最上面;下面按功能列出你能改的参数,没有列出的由安装时的配置决定。";
|
|
17
17
|
readonly overridden: "我改过";
|
|
18
18
|
readonly deployment: "默认";
|
|
19
19
|
readonly loading: "正在读取设置…";
|
|
20
20
|
readonly unavailable: "这个功能没有启动(对应的插件没有安装)";
|
|
21
|
+
readonly seatMissing: "这个界面没有提供设置服务,参数只能看不能改";
|
|
21
22
|
readonly readonly: "本次会话不允许写入设置(临时运行模式)";
|
|
22
23
|
readonly apply: "保存";
|
|
23
24
|
readonly reset: "恢复默认值";
|
|
@@ -2,39 +2,104 @@
|
|
|
2
2
|
* Structural view of the client seams this bundle uses.
|
|
3
3
|
*
|
|
4
4
|
* The package is distributed OUTSIDE the platform repository, so it declares the
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
5
|
+
* seams it consumes instead of importing another plugin's values (forbidden by the
|
|
6
|
+
* client bundle-purity rule) or its types (which would drag the platform sources
|
|
7
|
+
* into this project). The shapes are the shell's contract: a `settings.section`
|
|
8
|
+
* registration hosting a keyed card slot, the keyed `plugins.row.config` seat the
|
|
9
|
+
* Plugins page opens per row, the client settings seat (`configForms`, one form per
|
|
10
|
+
* Host plugin entry id), and the locale seat the section's copy resolves through.
|
|
11
11
|
* @module @lmzhen/dsh-evolution-settings-ui/client
|
|
12
12
|
*/
|
|
13
13
|
import type { ReactNode } from 'react';
|
|
14
14
|
/** Our card slot, hosted by the section this bundle registers. */
|
|
15
15
|
export declare const CARD_SLOT = "evolution.namespace.card";
|
|
16
|
-
/**
|
|
16
|
+
/** The Plugins page's per-row configuration seat (declared by `ui-plugin-manager`). */
|
|
17
|
+
export declare const ROW_CONFIG_SLOT = "plugins.row.config";
|
|
18
|
+
/**
|
|
19
|
+
* The key the Plugins page files a row's configuration under.
|
|
20
|
+
*
|
|
21
|
+
* Spelled here because the page's own helper cannot be imported (a value import from
|
|
22
|
+
* another plugin's client half is forbidden):
|
|
23
|
+
* `ui-plugin-manager/src/client/config-ledger.ts:36` is the definition this mirrors.
|
|
24
|
+
* @param bundle - the bundle package name that declares the row.
|
|
25
|
+
* @param rowId - the row id that bundle's patch declares.
|
|
26
|
+
* @returns the `plugins.row.config` key.
|
|
27
|
+
*/
|
|
28
|
+
export declare function rowConfigKey(bundle: string, rowId: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* The pending-window card's slot key.
|
|
31
|
+
*
|
|
32
|
+
* The section pairs cards by key and the parameter cards use their settings namespace; this card has no
|
|
33
|
+
* namespace (its data comes from the host's approval routes), so it carries a key of its own. The constant
|
|
34
|
+
* lives HERE and not in `index.ts` because the SECTION has to name it too, and `index.ts` already imports
|
|
35
|
+
* the section — one definition in the module both sides already share is what keeps that pair acyclic.
|
|
36
|
+
*/
|
|
37
|
+
export declare const PENDING_CARD_KEY = "approval-pending";
|
|
38
|
+
/** Snapshot of one settings row, as the client seat reports it. */
|
|
17
39
|
export interface ParamSectionSnapshot {
|
|
18
40
|
status: 'loading' | 'ready' | 'unavailable';
|
|
19
41
|
value: Record<string, unknown> | undefined;
|
|
20
42
|
/** Raw user section: a key's PRESENCE marks the override, not its value. */
|
|
21
43
|
user: unknown;
|
|
22
44
|
writable: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Why an `unavailable` snapshot is unavailable: this bundle found no settings seat at
|
|
47
|
+
* all, or the seat is there and serves no such row. The two read differently to the
|
|
48
|
+
* operator (one is the UI's own gap, the other is a row that is not composed).
|
|
49
|
+
*/
|
|
50
|
+
reason?: 'seat-missing' | 'not-served';
|
|
23
51
|
}
|
|
24
52
|
/** The reactive source the renderer binds to a `use<Name>` seat. */
|
|
25
53
|
export interface ParamSectionSource {
|
|
26
54
|
getSnapshot(): ParamSectionSnapshot;
|
|
27
55
|
subscribe(listener: () => void): () => void;
|
|
28
56
|
}
|
|
29
|
-
/** One
|
|
30
|
-
export interface
|
|
31
|
-
|
|
32
|
-
|
|
57
|
+
/** One ordered field operation, as the client settings seat takes them. */
|
|
58
|
+
export interface SettingsPathOpLike {
|
|
59
|
+
readonly op: 'set' | 'unset';
|
|
60
|
+
readonly path: readonly string[];
|
|
61
|
+
readonly value?: unknown;
|
|
62
|
+
}
|
|
63
|
+
/** One row's client form state, as `configForms.get(entryId)` reports it. */
|
|
64
|
+
export interface ConfigFormSnapshotLike<T = Record<string, unknown>> {
|
|
65
|
+
status: 'loading' | 'ready' | 'unavailable';
|
|
66
|
+
value: T | undefined;
|
|
67
|
+
/** Composition layer the Host resolved `value` over; what a cleared field reverts to. */
|
|
68
|
+
base: unknown;
|
|
69
|
+
/** Raw user layer: a key's PRESENCE marks the override. */
|
|
70
|
+
user: unknown;
|
|
71
|
+
revision: number | undefined;
|
|
72
|
+
writable: boolean;
|
|
73
|
+
mode: 'host' | 'memory';
|
|
74
|
+
}
|
|
75
|
+
/** One row's client write face (0.2.x `configForms`). */
|
|
76
|
+
export interface ConfigFormLike<T = Record<string, unknown>> {
|
|
77
|
+
getSnapshot(): ConfigFormSnapshotLike<T>;
|
|
78
|
+
subscribe(listener: () => void): () => void;
|
|
79
|
+
set(field: string, value: unknown): Promise<boolean>;
|
|
80
|
+
unset(field: string): Promise<boolean>;
|
|
81
|
+
mutate(ops: readonly SettingsPathOpLike[], expectedRevision?: number): Promise<boolean>;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The client settings seat, probed rather than injected: a deployment may compose the
|
|
85
|
+
* family without the settings surface, and a REQUIRED inject for a missing service
|
|
86
|
+
* leaves the whole browser half pending forever (no error, no UI).
|
|
87
|
+
*/
|
|
88
|
+
export interface ConfigFormsSeat {
|
|
89
|
+
/** One Host plugin entry id to its form. */
|
|
90
|
+
get<T>(entryId: string): ConfigFormLike<T>;
|
|
33
91
|
}
|
|
34
92
|
/** Registration options the keyed card slot under our own section takes. */
|
|
35
93
|
export interface SlotRegisterOptions {
|
|
36
94
|
name: typeof CARD_SLOT;
|
|
37
|
-
/** The settings
|
|
95
|
+
/** The settings row this card edits — the slot's key. */
|
|
96
|
+
key: string;
|
|
97
|
+
inject: () => unknown;
|
|
98
|
+
}
|
|
99
|
+
/** Registration options the Plugins page's per-row config seat takes. */
|
|
100
|
+
export interface RowSeatRegisterOptions {
|
|
101
|
+
name: typeof ROW_CONFIG_SLOT;
|
|
102
|
+
/** `<bundle package name>#<row id>`, as the page's ledger builds it. */
|
|
38
103
|
key: string;
|
|
39
104
|
inject: () => unknown;
|
|
40
105
|
}
|
|
@@ -64,6 +129,23 @@ export interface SectionFace {
|
|
|
64
129
|
/** Render one child slot; `filter` selects keyed entries (e.g. `{ entryKey }`). */
|
|
65
130
|
renderSlot: (name: string, props?: Record<string, unknown>, filter?: Record<string, unknown>) => ReactNode;
|
|
66
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* The values and commands the Plugins page passes to a `plugins.row.config` entry:
|
|
134
|
+
* the page owns the subscription (it re-renders the entry), so the entry renders from
|
|
135
|
+
* `state` and writes through `mutate`. Both views receive this face; `summary` uses it
|
|
136
|
+
* only to say whether anything is overridden.
|
|
137
|
+
*/
|
|
138
|
+
export interface RowConfigViewProps {
|
|
139
|
+
/** `summary` is the row's one-line description fallback; `page` is the whole form. */
|
|
140
|
+
readonly view: 'summary' | 'page';
|
|
141
|
+
/** Absent when the Host serves no configuration for this row. */
|
|
142
|
+
readonly form?: RowConfigForm | undefined;
|
|
143
|
+
}
|
|
144
|
+
/** The row form the page derived from its settings seat. */
|
|
145
|
+
export interface RowConfigForm {
|
|
146
|
+
readonly state: ConfigFormSnapshotLike;
|
|
147
|
+
readonly mutate: (ops: readonly SettingsPathOpLike[], revision?: number) => Promise<boolean>;
|
|
148
|
+
}
|
|
67
149
|
/** The locale seat: register this bundle's namespace, bind a translator. */
|
|
68
150
|
export interface LocaleSeat {
|
|
69
151
|
register(namespace: string, dictionaries: {
|
|
@@ -76,12 +158,7 @@ export interface LocaleSeat {
|
|
|
76
158
|
export interface ClientSeam {
|
|
77
159
|
slots: {
|
|
78
160
|
inject(name: string, callback: () => Generator<unknown, void, unknown>): unknown;
|
|
79
|
-
register(options: SlotRegisterOptions | SectionRegisterOptions, component: (props: never) => unknown): () => void;
|
|
80
|
-
};
|
|
81
|
-
settingsScope: {
|
|
82
|
-
bind(spec: {
|
|
83
|
-
namespace: string;
|
|
84
|
-
}): SettingsScopeLike;
|
|
161
|
+
register(options: SlotRegisterOptions | RowSeatRegisterOptions | SectionRegisterOptions, component: (props: never) => unknown): () => void;
|
|
85
162
|
};
|
|
86
163
|
locale: LocaleSeat;
|
|
87
164
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The adapter between the platform's client settings seat and this bundle's cards.
|
|
3
|
+
*
|
|
4
|
+
* One place maps `configForms.get(entryId)` to the three things a card needs — a
|
|
5
|
+
* snapshot, a subscription and the two write calls — so our own section's cards and the
|
|
6
|
+
* Plugins page's per-row entry cannot drift apart in how they read or write a row.
|
|
7
|
+
* @module @lmzhen/dsh-evolution-settings-ui/client
|
|
8
|
+
*/
|
|
9
|
+
import type { ConfigFormSnapshotLike, ConfigFormsSeat, ParamSectionSnapshot, ParamSectionSource } from './seam.ts';
|
|
10
|
+
/** What a card shows when this UI found no settings seat at all. */
|
|
11
|
+
export declare const SEAT_MISSING: ParamSectionSnapshot;
|
|
12
|
+
/**
|
|
13
|
+
* Whether a value is a plain section object.
|
|
14
|
+
* @param value - the value to test.
|
|
15
|
+
* @returns true for a non-null, non-array object.
|
|
16
|
+
*/
|
|
17
|
+
export declare function isSection(value: unknown): value is Record<string, unknown>;
|
|
18
|
+
/**
|
|
19
|
+
* Project one platform snapshot into the card's own shape.
|
|
20
|
+
* @param snapshot - the seat's snapshot.
|
|
21
|
+
* @returns the card's snapshot, with the reason an unavailable one is unavailable.
|
|
22
|
+
*/
|
|
23
|
+
export declare function sectionSnapshotOf(snapshot: ConfigFormSnapshotLike): ParamSectionSnapshot;
|
|
24
|
+
/**
|
|
25
|
+
* The live source one row's card binds.
|
|
26
|
+
* @param seat - the probed settings seat, when the deployment composed it.
|
|
27
|
+
* @param namespace - the row's entry id.
|
|
28
|
+
* @returns the source; without a seat it reports the stated empty state and never notifies.
|
|
29
|
+
*/
|
|
30
|
+
export declare function sourceFor(seat: ConfigFormsSeat | undefined, namespace: string): ParamSectionSource;
|
|
31
|
+
/**
|
|
32
|
+
* The write pair for one row.
|
|
33
|
+
* @param seat - the probed settings seat, when the deployment composed it.
|
|
34
|
+
* @param namespace - the row's entry id.
|
|
35
|
+
* @returns the two calls a card's save/reset path uses; without a seat they resolve without writing.
|
|
36
|
+
*/
|
|
37
|
+
export declare function writeFaceFor(seat: ConfigFormsSeat | undefined, namespace: string): {
|
|
38
|
+
write: (field: string, value: unknown) => Promise<void>;
|
|
39
|
+
clear: (field: string) => Promise<void>;
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=source.d.ts.map
|
package/package.json
CHANGED