@lmzhen/dsh-evolution-settings-ui 0.6.1 → 0.7.0
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 +28 -5
- package/lib/client.js +561 -90
- package/lib/types/client/ParamCard.d.ts +10 -3
- package/lib/types/client/SettingsSection.d.ts +18 -0
- package/lib/types/client/generated-params.d.ts +11 -1
- package/lib/types/client/index.d.ts +18 -8
- package/lib/types/client/messages.d.ts +42 -19
- package/lib/types/client/seam.d.ts +45 -6
- package/lib/types/client/styles.d.ts +34 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,12 +1,34 @@
|
|
|
1
1
|
# @lmzhen/dsh-evolution-settings-ui
|
|
2
2
|
|
|
3
|
-
Web settings
|
|
3
|
+
Web settings section (**自进化**) for the evolution family's parameter namespaces. The Host half registers nothing: the namespaces belong to the plugins that own them (evolution-review, memory-files, tool-memory, evolution-curator, tool-skill-manage), and this package's browser half registers one settings section that hosts one card per namespace.
|
|
4
|
+
|
|
5
|
+
## Where it lives
|
|
6
|
+
|
|
7
|
+
The section goes through the platform's own seam — the same call the platform's 插件 section and the out-of-repo market / skins / Web-plugins sections use:
|
|
8
|
+
|
|
9
|
+
ctx.slots.register({
|
|
10
|
+
name: 'settings.section', id: 'evolution', order: 25,
|
|
11
|
+
label: () => t('title'), locale: NS,
|
|
12
|
+
children: { 'evolution.namespace.card': { kind: 'keyed', scope: 'root' } },
|
|
13
|
+
}, SettingsSection)
|
|
14
|
+
|
|
15
|
+
The cards claim the keyed child slot per namespace, so a namespace the Host does not serve renders nothing, and a deployment that drops this row loses the whole section — no Host-side behaviour rides on this pair. Until 0.6.1 the cards lived inside the platform's 插件 section; 0.7.0 moved them into their own section, which is how every other feature area presents itself.
|
|
4
16
|
|
|
5
17
|
## What a card does
|
|
6
18
|
|
|
7
|
-
|
|
19
|
+
A card renders the namespace's user-writable (E3) fields with the registry's Chinese label, its unit, a 用户/部署 source chip, a control **typed from the registry** (switch / select / number / text) holding the current value, the registry's hint, and a per-field 恢复部署默认 action when a user override exists. The card's foot carries one 放弃修改 / 保存 pair: edits live in a draft, 保存 writes only the dirty fields in order and clears the draft on success. Success is decided by READING BACK the raw user section — the client settings scope resolves a refused write (it recovers the snapshot and returns; the remote call never rejects), so a field the Host refused simply leaves no user key. When that happens the card reports it under the fields and keeps the draft, so nothing the operator typed is lost and a refusal never looks like a silent revert.
|
|
20
|
+
|
|
21
|
+
Writes go through the client settings scope (`set`/`unset`), which carries the revision it read as the write's fence; a field whose key is present in the raw user section reads as a user override even when its value equals the deployment value.
|
|
22
|
+
|
|
23
|
+
The field list AND the per-field UI metadata are GENERATED from the parameter registry (`packages/scripts/gen-param-client-view.mjs` writes `src/client/generated-params.ts`): id, group, the English summary, and the E3 rows' label / hint / control / unit / values. The browser half therefore cannot name a parameter the Host does not register, and cannot invent a control the registry does not declare. Regenerate after every registry edit; the family gate runs the generator with `--check`.
|
|
24
|
+
|
|
25
|
+
## Styling
|
|
26
|
+
|
|
27
|
+
The design system's CSS is not exported to packages outside the platform repository, so this bundle carries its own stylesheet and injects it once behind a `<style data-plugin-css="…">` tag — the mechanism the platform's own client bundles use. Every rule reads a `--dsw-alias-*` design token, so light and dark follow the theme without a colour of our own, and the field metrics (12px padding, 6px gap, 13px label, 12px hint) copy the platform's settings fields.
|
|
28
|
+
|
|
29
|
+
## Copy and locale
|
|
8
30
|
|
|
9
|
-
|
|
31
|
+
Section and card copy lives in `src/client/messages.ts` as a zh/en dictionary, registered through the platform's locale seat (`ctx.locale.register(namespace, { zh, en })` plus `bind`), so the nav entry and its copy follow the UI language. The earlier note in this file — that an outside package cannot bind the locale seat — was wrong: the out-of-repo market and skins plugins bind it exactly this way.
|
|
10
32
|
|
|
11
33
|
## Build
|
|
12
34
|
|
|
@@ -14,6 +36,7 @@ The browser half ships as the client module system's lazy CJS factory artifact (
|
|
|
14
36
|
|
|
15
37
|
## Known limitations and deferred work
|
|
16
38
|
|
|
17
|
-
- Card copy lives in this package's own dictionary (`src/client/messages.ts`) and reaches components through the inject face; the platform locale seat is not bound yet (an outside package cannot declare an in-repo locale namespace type).
|
|
18
|
-
- The cards render plain controls with class names and no stylesheet: the family ships no CSS build step for client packages yet.
|
|
19
39
|
- Only E3 (user-writable) rows are rendered; deployment tiers are reported by `/evolution params` and the doctor divergence section instead.
|
|
40
|
+
- The section renders plain controls styled with the design tokens rather than the platform's `@deepseek-ai/dsh-client-ui-primitives` kit: that module IS available to out-of-repo bundles (the market plugin requires it), but its prop shapes are not published, and guessing them would break the live GUI.
|
|
41
|
+
- An unsaved draft lives in the card's own component state and the settings shell renders only the active section, so switching to another section drops a draft that was not saved yet. Values already written are unaffected; moving the draft into an apply-time store is the 0.7.x follow-up.
|
|
42
|
+
- A deployment that overrides a field through the `evolution-policy` row does not show on the card: the card reports the deployment value the settings scope serves, while the policy snapshot can differ. That divergence stays a doctor / `/evolution params` matter.
|
package/lib/client.js
CHANGED
|
@@ -13,62 +13,122 @@ window.__ModuleLoader__.load({
|
|
|
13
13
|
{
|
|
14
14
|
id: "curatorIntervalHours",
|
|
15
15
|
group: "curator",
|
|
16
|
-
doc: "Minimum hours between deterministic curation passes."
|
|
16
|
+
doc: "Minimum hours between deterministic curation passes.",
|
|
17
|
+
label: "策展间隔",
|
|
18
|
+
hint: "两次确定性策展之间至少间隔多少小时。",
|
|
19
|
+
control: "number",
|
|
20
|
+
unit: "小时",
|
|
21
|
+
values: []
|
|
17
22
|
},
|
|
18
23
|
{
|
|
19
24
|
id: "staleAfterDays",
|
|
20
25
|
group: "curator",
|
|
21
|
-
doc: "Inactive days before a skill counts as stale."
|
|
26
|
+
doc: "Inactive days before a skill counts as stale.",
|
|
27
|
+
label: "判为陈旧的天数",
|
|
28
|
+
hint: "技能多少天没被使用即算陈旧。",
|
|
29
|
+
control: "number",
|
|
30
|
+
unit: "天",
|
|
31
|
+
values: []
|
|
22
32
|
},
|
|
23
33
|
{
|
|
24
34
|
id: "archiveAfterDays",
|
|
25
35
|
group: "curator",
|
|
26
|
-
doc: "Inactive days before a stale skill is archived (must be >= staleAfterDays)."
|
|
36
|
+
doc: "Inactive days before a stale skill is archived (must be >= staleAfterDays).",
|
|
37
|
+
label: "归档天数",
|
|
38
|
+
hint: "陈旧技能再过多少天归档(须 ≥ 判为陈旧的天数)。",
|
|
39
|
+
control: "number",
|
|
40
|
+
unit: "天",
|
|
41
|
+
values: []
|
|
27
42
|
},
|
|
28
43
|
{
|
|
29
44
|
id: "qualityWarnStaleAfterDays",
|
|
30
45
|
group: "curator",
|
|
31
|
-
doc: "Age at which a low quality score starts warning."
|
|
46
|
+
doc: "Age at which a low quality score starts warning.",
|
|
47
|
+
label: "低质量告警天数",
|
|
48
|
+
hint: "质量分偏低时从多少天开始告警。",
|
|
49
|
+
control: "number",
|
|
50
|
+
unit: "天",
|
|
51
|
+
values: []
|
|
32
52
|
},
|
|
33
53
|
{
|
|
34
54
|
id: "minIdleHours",
|
|
35
55
|
group: "curator",
|
|
36
|
-
doc: "Idle hours required before an automatic curation pass runs."
|
|
56
|
+
doc: "Idle hours required before an automatic curation pass runs.",
|
|
57
|
+
label: "空闲门槛",
|
|
58
|
+
hint: "至少空闲多少小时才跑自动策展。",
|
|
59
|
+
control: "number",
|
|
60
|
+
unit: "小时",
|
|
61
|
+
values: []
|
|
37
62
|
},
|
|
38
63
|
{
|
|
39
64
|
id: "minIdleFailOpen",
|
|
40
65
|
group: "curator",
|
|
41
|
-
doc: "Let the idle gate open when the activity probe is unavailable."
|
|
66
|
+
doc: "Let the idle gate open when the activity probe is unavailable.",
|
|
67
|
+
label: "空闲探针失败时放行",
|
|
68
|
+
hint: "活动探针不可用时,是否让空闲门槛直接放行。",
|
|
69
|
+
control: "switch",
|
|
70
|
+
unit: "",
|
|
71
|
+
values: []
|
|
42
72
|
},
|
|
43
73
|
{
|
|
44
74
|
id: "llmReview",
|
|
45
75
|
group: "curator",
|
|
46
|
-
doc: "Enable the LLM nomination pass on top of the deterministic lifecycle."
|
|
76
|
+
doc: "Enable the LLM nomination pass on top of the deterministic lifecycle.",
|
|
77
|
+
label: "启用 LLM 提名",
|
|
78
|
+
hint: "在确定性生命周期之上再跑一遍 LLM 提名。",
|
|
79
|
+
control: "switch",
|
|
80
|
+
unit: "",
|
|
81
|
+
values: []
|
|
47
82
|
},
|
|
48
83
|
{
|
|
49
84
|
id: "curatorReviewMaxTokens",
|
|
50
85
|
group: "curator",
|
|
51
|
-
doc: "Token budget of the curator LLM review."
|
|
86
|
+
doc: "Token budget of the curator LLM review.",
|
|
87
|
+
label: "策展 LLM token 预算",
|
|
88
|
+
hint: "策展 LLM 审查可用的 token 预算。",
|
|
89
|
+
control: "number",
|
|
90
|
+
unit: "token",
|
|
91
|
+
values: []
|
|
52
92
|
},
|
|
53
93
|
{
|
|
54
94
|
id: "curatorReviewTimeoutMs",
|
|
55
95
|
group: "curator",
|
|
56
|
-
doc: "Wall-clock bound of the curator LLM review."
|
|
96
|
+
doc: "Wall-clock bound of the curator LLM review.",
|
|
97
|
+
label: "策展 LLM 超时",
|
|
98
|
+
hint: "策展 LLM 审查的墙钟上限。",
|
|
99
|
+
control: "number",
|
|
100
|
+
unit: "毫秒",
|
|
101
|
+
values: []
|
|
57
102
|
},
|
|
58
103
|
{
|
|
59
104
|
id: "healthSoftBodyChars",
|
|
60
105
|
group: "curator",
|
|
61
|
-
doc: "Body character line the health view judges against."
|
|
106
|
+
doc: "Body character line the health view judges against.",
|
|
107
|
+
label: "正文软带",
|
|
108
|
+
hint: "健康视图判定正文长度的软带。",
|
|
109
|
+
control: "number",
|
|
110
|
+
unit: "字符",
|
|
111
|
+
values: []
|
|
62
112
|
},
|
|
63
113
|
{
|
|
64
114
|
id: "healthStampDensityPerKb",
|
|
65
115
|
group: "curator",
|
|
66
|
-
doc: "Stamp density per KB that flags log-like content in a body."
|
|
116
|
+
doc: "Stamp density per KB that flags log-like content in a body.",
|
|
117
|
+
label: "标记密度阈值",
|
|
118
|
+
hint: "每 KB 出现多少个时间标记就算「像日志」。",
|
|
119
|
+
control: "number",
|
|
120
|
+
unit: "处/KB",
|
|
121
|
+
values: []
|
|
67
122
|
},
|
|
68
123
|
{
|
|
69
124
|
id: "healthChurnMinPatches",
|
|
70
125
|
group: "curator",
|
|
71
|
-
doc: "Patches without a read that flag a write-ghost skill."
|
|
126
|
+
doc: "Patches without a read that flag a write-ghost skill.",
|
|
127
|
+
label: "空写判定补丁数",
|
|
128
|
+
hint: "多少次改动都没有任何读取,即判为「写幽灵」。",
|
|
129
|
+
control: "number",
|
|
130
|
+
unit: "处",
|
|
131
|
+
values: []
|
|
72
132
|
}
|
|
73
133
|
]
|
|
74
134
|
},
|
|
@@ -78,22 +138,42 @@ window.__ModuleLoader__.load({
|
|
|
78
138
|
{
|
|
79
139
|
id: "memoryChars",
|
|
80
140
|
group: "memory",
|
|
81
|
-
doc: "Character budget the memory store enforces for MEMORY.md."
|
|
141
|
+
doc: "Character budget the memory store enforces for MEMORY.md.",
|
|
142
|
+
label: "MEMORY.md 字符预算",
|
|
143
|
+
hint: "memory 存储对 MEMORY.md 的字符上限。",
|
|
144
|
+
control: "number",
|
|
145
|
+
unit: "字符",
|
|
146
|
+
values: []
|
|
82
147
|
},
|
|
83
148
|
{
|
|
84
149
|
id: "userChars",
|
|
85
150
|
group: "memory",
|
|
86
|
-
doc: "Character budget the memory store enforces for USER.md."
|
|
151
|
+
doc: "Character budget the memory store enforces for USER.md.",
|
|
152
|
+
label: "USER.md 字符预算",
|
|
153
|
+
hint: "memory 存储对 USER.md 的字符上限。",
|
|
154
|
+
control: "number",
|
|
155
|
+
unit: "字符",
|
|
156
|
+
values: []
|
|
87
157
|
},
|
|
88
158
|
{
|
|
89
159
|
id: "addDatePrefix",
|
|
90
160
|
group: "memory",
|
|
91
|
-
doc: "Prefix stored memory entries with their date heading."
|
|
161
|
+
doc: "Prefix stored memory entries with their date heading.",
|
|
162
|
+
label: "记忆条目加日期前缀",
|
|
163
|
+
hint: "写入记忆时为条目加上日期标题。",
|
|
164
|
+
control: "switch",
|
|
165
|
+
unit: "",
|
|
166
|
+
values: []
|
|
92
167
|
},
|
|
93
168
|
{
|
|
94
169
|
id: "maxConsolidationFailures",
|
|
95
170
|
group: "memory",
|
|
96
|
-
doc: "Consolidation failures one turn tolerates before the tool gives up."
|
|
171
|
+
doc: "Consolidation failures one turn tolerates before the tool gives up.",
|
|
172
|
+
label: "合并失败容忍次数",
|
|
173
|
+
hint: "一轮内合并失败多少次后工具放弃。",
|
|
174
|
+
control: "number",
|
|
175
|
+
unit: "次",
|
|
176
|
+
values: []
|
|
97
177
|
}
|
|
98
178
|
]
|
|
99
179
|
},
|
|
@@ -103,37 +183,76 @@ window.__ModuleLoader__.load({
|
|
|
103
183
|
{
|
|
104
184
|
id: "reviewSkillInterval",
|
|
105
185
|
group: "review",
|
|
106
|
-
doc: "Activity units between skill-review injections."
|
|
186
|
+
doc: "Activity units between skill-review injections.",
|
|
187
|
+
label: "技能审查间隔",
|
|
188
|
+
hint: "每多少活动单位触发一次技能审查注入。",
|
|
189
|
+
control: "number",
|
|
190
|
+
unit: "次",
|
|
191
|
+
values: []
|
|
107
192
|
},
|
|
108
193
|
{
|
|
109
194
|
id: "reviewMemoryInterval",
|
|
110
195
|
group: "review",
|
|
111
|
-
doc: "Activity units between memory-review injections."
|
|
196
|
+
doc: "Activity units between memory-review injections.",
|
|
197
|
+
label: "记忆审查间隔",
|
|
198
|
+
hint: "每多少活动单位触发一次记忆审查注入。",
|
|
199
|
+
control: "number",
|
|
200
|
+
unit: "次",
|
|
201
|
+
values: []
|
|
112
202
|
},
|
|
113
203
|
{
|
|
114
204
|
id: "skillReviewTrigger",
|
|
115
205
|
group: "review",
|
|
116
|
-
doc: "Which channel may inject a skill review (cadence, completion, both)."
|
|
206
|
+
doc: "Which channel may inject a skill review (cadence, completion, both).",
|
|
207
|
+
label: "审查触发通道",
|
|
208
|
+
hint: "哪个通道可以注入技能审查:cadence=按间隔、completion=按完成、both=两者。",
|
|
209
|
+
control: "select",
|
|
210
|
+
unit: "",
|
|
211
|
+
values: [
|
|
212
|
+
"cadence",
|
|
213
|
+
"completion",
|
|
214
|
+
"both"
|
|
215
|
+
]
|
|
117
216
|
},
|
|
118
217
|
{
|
|
119
218
|
id: "skillReviewCompletionMinToolCalls",
|
|
120
219
|
group: "review",
|
|
121
|
-
doc: "Tool calls a task needs before the completion channel injects."
|
|
220
|
+
doc: "Tool calls a task needs before the completion channel injects.",
|
|
221
|
+
label: "完成通道最少工具调用",
|
|
222
|
+
hint: "走完成通道时,任务至少经过多少次工具调用才注入。",
|
|
223
|
+
control: "number",
|
|
224
|
+
unit: "次",
|
|
225
|
+
values: []
|
|
122
226
|
},
|
|
123
227
|
{
|
|
124
228
|
id: "reviewEnabled",
|
|
125
229
|
group: "review",
|
|
126
|
-
doc: "Master switch for the review plugin."
|
|
230
|
+
doc: "Master switch for the review plugin.",
|
|
231
|
+
label: "启用审查插件",
|
|
232
|
+
hint: "审查插件的总开关。",
|
|
233
|
+
control: "switch",
|
|
234
|
+
unit: "",
|
|
235
|
+
values: []
|
|
127
236
|
},
|
|
128
237
|
{
|
|
129
238
|
id: "reviewMode",
|
|
130
239
|
group: "review",
|
|
131
|
-
doc: "Run the review in the parent session (inject) or on a subagent."
|
|
240
|
+
doc: "Run the review in the parent session (inject) or on a subagent.",
|
|
241
|
+
label: "审查运行方式",
|
|
242
|
+
hint: "inject=在父会话内注入,subagent=交给子代理运行。",
|
|
243
|
+
control: "select",
|
|
244
|
+
unit: "",
|
|
245
|
+
values: ["subagent", "inject"]
|
|
132
246
|
},
|
|
133
247
|
{
|
|
134
248
|
id: "reviewWakeInject",
|
|
135
249
|
group: "review",
|
|
136
|
-
doc: "Deliver the deferred review as a waking follow-up message."
|
|
250
|
+
doc: "Deliver the deferred review as a waking follow-up message.",
|
|
251
|
+
label: "审查后续唤醒注入",
|
|
252
|
+
hint: "把延后的审查作为唤醒消息投递。",
|
|
253
|
+
control: "switch",
|
|
254
|
+
unit: "",
|
|
255
|
+
values: []
|
|
137
256
|
}
|
|
138
257
|
]
|
|
139
258
|
},
|
|
@@ -143,42 +262,82 @@ window.__ModuleLoader__.load({
|
|
|
143
262
|
{
|
|
144
263
|
id: "skillContentChars",
|
|
145
264
|
group: "write-caps",
|
|
146
|
-
doc: "Character cap on a SKILL.md body (tighten-only)."
|
|
265
|
+
doc: "Character cap on a SKILL.md body (tighten-only).",
|
|
266
|
+
label: "SKILL.md 正文字符上限",
|
|
267
|
+
hint: "SKILL.md 正文的字符上限(只许收紧)。",
|
|
268
|
+
control: "number",
|
|
269
|
+
unit: "字符",
|
|
270
|
+
values: []
|
|
147
271
|
},
|
|
148
272
|
{
|
|
149
273
|
id: "maxSkillFileBytes",
|
|
150
274
|
group: "write-caps",
|
|
151
|
-
doc: "Byte cap on one support file (tighten-only)."
|
|
275
|
+
doc: "Byte cap on one support file (tighten-only).",
|
|
276
|
+
label: "支持文件字节上限",
|
|
277
|
+
hint: "单个支持文件的字节上限(只许收紧)。",
|
|
278
|
+
control: "number",
|
|
279
|
+
unit: "字节",
|
|
280
|
+
values: []
|
|
152
281
|
},
|
|
153
282
|
{
|
|
154
283
|
id: "maxSkillNameLength",
|
|
155
284
|
group: "write-caps",
|
|
156
|
-
doc: "Character cap on a skill name (tighten-only)."
|
|
285
|
+
doc: "Character cap on a skill name (tighten-only).",
|
|
286
|
+
label: "技能名字符上限",
|
|
287
|
+
hint: "技能名的字符上限(只许收紧)。",
|
|
288
|
+
control: "number",
|
|
289
|
+
unit: "字符",
|
|
290
|
+
values: []
|
|
157
291
|
},
|
|
158
292
|
{
|
|
159
293
|
id: "maxDescriptionLength",
|
|
160
294
|
group: "write-caps",
|
|
161
|
-
doc: "Character cap on a skill description (tighten-only)."
|
|
295
|
+
doc: "Character cap on a skill description (tighten-only).",
|
|
296
|
+
label: "技能描述字符上限",
|
|
297
|
+
hint: "技能描述的字符上限(只许收紧)。",
|
|
298
|
+
control: "number",
|
|
299
|
+
unit: "字符",
|
|
300
|
+
values: []
|
|
162
301
|
},
|
|
163
302
|
{
|
|
164
303
|
id: "descriptionStrict",
|
|
165
304
|
group: "write-caps",
|
|
166
|
-
doc: "Refuse a description over the authoring bar instead of advising."
|
|
305
|
+
doc: "Refuse a description over the authoring bar instead of advising.",
|
|
306
|
+
label: "描述严格档",
|
|
307
|
+
hint: "描述超过写作门槛时拒绝写入,而不是只提示。",
|
|
308
|
+
control: "switch",
|
|
309
|
+
unit: "",
|
|
310
|
+
values: []
|
|
167
311
|
},
|
|
168
312
|
{
|
|
169
313
|
id: "strictCrossSource",
|
|
170
314
|
group: "write-caps",
|
|
171
|
-
doc: "Refuse writes whose catalog entry resolves outside the family."
|
|
315
|
+
doc: "Refuse writes whose catalog entry resolves outside the family.",
|
|
316
|
+
label: "跨源严格档",
|
|
317
|
+
hint: "目录项解析到家族之外时拒绝写入。",
|
|
318
|
+
control: "switch",
|
|
319
|
+
unit: "",
|
|
320
|
+
values: []
|
|
172
321
|
},
|
|
173
322
|
{
|
|
174
323
|
id: "citationPolicy",
|
|
175
324
|
group: "write-caps",
|
|
176
|
-
doc: "Refuse a move that would leave a dangling reference, or verify it."
|
|
325
|
+
doc: "Refuse a move that would leave a dangling reference, or verify it.",
|
|
326
|
+
label: "引用校验档",
|
|
327
|
+
hint: "移动或合并时对引用的处置:verify=校验、refuse=直接拒绝。",
|
|
328
|
+
control: "select",
|
|
329
|
+
unit: "",
|
|
330
|
+
values: ["verify", "refuse"]
|
|
177
331
|
},
|
|
178
332
|
{
|
|
179
333
|
id: "supportFileCharPolicy",
|
|
180
334
|
group: "write-caps",
|
|
181
|
-
doc: "Warn about an oversize support file, or refuse the write."
|
|
335
|
+
doc: "Warn about an oversize support file, or refuse the write.",
|
|
336
|
+
label: "支持文件超限档",
|
|
337
|
+
hint: "支持文件超出字符上限时:report=只报告、enforce=拒写。",
|
|
338
|
+
control: "select",
|
|
339
|
+
unit: "",
|
|
340
|
+
values: ["report", "enforce"]
|
|
182
341
|
}
|
|
183
342
|
]
|
|
184
343
|
},
|
|
@@ -187,11 +346,95 @@ window.__ModuleLoader__.load({
|
|
|
187
346
|
fields: [{
|
|
188
347
|
id: "entryPreviewChars",
|
|
189
348
|
group: "memory",
|
|
190
|
-
doc: "Characters of one memory entry shown in a tool result preview."
|
|
349
|
+
doc: "Characters of one memory entry shown in a tool result preview.",
|
|
350
|
+
label: "条目预览字符数",
|
|
351
|
+
hint: "单条记忆预览保留多少字符。",
|
|
352
|
+
control: "number",
|
|
353
|
+
unit: "字符",
|
|
354
|
+
values: []
|
|
191
355
|
}]
|
|
192
356
|
}
|
|
193
357
|
];
|
|
194
358
|
//#endregion
|
|
359
|
+
//#region src/client/messages.ts
|
|
360
|
+
/**
|
|
361
|
+
* Copy for the evolution settings section and its parameter cards.
|
|
362
|
+
*
|
|
363
|
+
* The section is registered through the platform's locale seat (`ctx.locale`),
|
|
364
|
+
* which is the same route the other out-of-repo client plugins take. The earlier
|
|
365
|
+
* note in this package's README — that an outside package cannot bind the locale
|
|
366
|
+
* seat — is wrong: `locale.register(namespace, { zh, en })` is the documented
|
|
367
|
+
* call, and it is what makes the section title and its copy follow the UI
|
|
368
|
+
* language instead of shipping one hard-coded dictionary.
|
|
369
|
+
*/
|
|
370
|
+
/** Locale namespace this bundle registers. */
|
|
371
|
+
const NS = "evolution-settings";
|
|
372
|
+
/** Chinese copy (the family's primary language). */
|
|
373
|
+
const zh = {
|
|
374
|
+
title: "自进化",
|
|
375
|
+
subtitle: "审查/记忆/策展/技能四个命名空间的参数可在用户层覆盖;此处只列可改项,其余仍由部署面决定。",
|
|
376
|
+
overridden: "用户",
|
|
377
|
+
deployment: "部署",
|
|
378
|
+
loading: "正在读取设置文档…",
|
|
379
|
+
unavailable: "这个命名空间没有宿主提供服务(对应插件行未挂载)",
|
|
380
|
+
readonly: "本次会话的设置文档是只读的(内存模式)",
|
|
381
|
+
apply: "保存",
|
|
382
|
+
reset: "恢复部署默认",
|
|
383
|
+
empty: "这个命名空间没有可改项",
|
|
384
|
+
changed: "{n} 项已改",
|
|
385
|
+
discard: "放弃修改",
|
|
386
|
+
saving: "保存中…",
|
|
387
|
+
refused: "写入未被接受——宿主的校验拒绝了它(值保持原样)。原因见 /evolution params 或 /evolution doctor。",
|
|
388
|
+
cardReview: "审查",
|
|
389
|
+
cardMemory: "记忆",
|
|
390
|
+
cardToolMemory: "记忆工具",
|
|
391
|
+
cardCurator: "策展",
|
|
392
|
+
cardSkills: "技能"
|
|
393
|
+
};
|
|
394
|
+
/** English copy, key for key with {@link zh}. */
|
|
395
|
+
const en = {
|
|
396
|
+
title: "Self-evolution",
|
|
397
|
+
subtitle: "Parameters of the review, memory, curation and skill namespaces may be overridden per user; only the writable ones are listed here, everything else still comes from the deployment.",
|
|
398
|
+
overridden: "user",
|
|
399
|
+
deployment: "deployment",
|
|
400
|
+
loading: "reading the settings document…",
|
|
401
|
+
unavailable: "this namespace is not served by the Host (its owning plugin row is not mounted)",
|
|
402
|
+
readonly: "the settings document is read-only in this session (memory mode)",
|
|
403
|
+
apply: "Save",
|
|
404
|
+
reset: "Reset to deployment",
|
|
405
|
+
empty: "no writable field in this namespace",
|
|
406
|
+
changed: "{n} changed",
|
|
407
|
+
discard: "Discard",
|
|
408
|
+
saving: "Saving…",
|
|
409
|
+
refused: "The write was not accepted — the Host refused it and the value is unchanged. See /evolution params or /evolution doctor for the reason.",
|
|
410
|
+
cardReview: "Review",
|
|
411
|
+
cardMemory: "Memory",
|
|
412
|
+
cardToolMemory: "Memory tools",
|
|
413
|
+
cardCurator: "Curation",
|
|
414
|
+
cardSkills: "Skills"
|
|
415
|
+
};
|
|
416
|
+
/**
|
|
417
|
+
* Card titles per settings namespace. A namespace without an entry falls back to
|
|
418
|
+
* its raw name, so a Host that registers a new namespace still renders — the
|
|
419
|
+
* title is presentation, the namespace is the join key.
|
|
420
|
+
*/
|
|
421
|
+
const NAMESPACE_TITLES = Object.freeze({
|
|
422
|
+
"evolution-review": "cardReview",
|
|
423
|
+
"evolution-memory": "cardMemory",
|
|
424
|
+
"evolution-tool-memory": "cardToolMemory",
|
|
425
|
+
"evolution-curator": "cardCurator",
|
|
426
|
+
"evolution-skills": "cardSkills"
|
|
427
|
+
});
|
|
428
|
+
/**
|
|
429
|
+
* Resolve one message without the locale seat (tests and the fallback path).
|
|
430
|
+
* @param key - the message key.
|
|
431
|
+
* @param language - which dictionary to read; defaults to Chinese.
|
|
432
|
+
* @returns the copy this bundle carries.
|
|
433
|
+
*/
|
|
434
|
+
function message(key, language = "zh") {
|
|
435
|
+
return language === "zh" ? zh[key] : en[key];
|
|
436
|
+
}
|
|
437
|
+
//#endregion
|
|
195
438
|
//#region src/client/ParamCard.ts
|
|
196
439
|
/**
|
|
197
440
|
* One namespace's parameter card.
|
|
@@ -199,48 +442,106 @@ window.__ModuleLoader__.load({
|
|
|
199
442
|
* Structural local types on purpose: this package is distributed outside the
|
|
200
443
|
* platform repository and a value import of another plugin is forbidden, so the
|
|
201
444
|
* settings scope and its snapshot are declared by the shape the renderer hands
|
|
202
|
-
* over (
|
|
445
|
+
* over (\`getSnapshot\`/\`subscribe\`, status/value/user/writable).
|
|
446
|
+
*
|
|
447
|
+
* Layout and behaviour follow the platform's own settings fields: a collapsed card
|
|
448
|
+
* header (namespace title, change count, chevron), then one block per field —
|
|
449
|
+
* label with its unit and source chip, a control HOLDING THE CURRENT VALUE, the
|
|
450
|
+
* Chinese hint — and one 放弃修改/保存 pair at the card's foot. The control is
|
|
451
|
+
* typed from the registry (\`control\`), so booleans are switches, enums are
|
|
452
|
+
* selects and numbers are numeric inputs with the unit shown beside the label.
|
|
203
453
|
*/
|
|
204
|
-
/** One cell's text: absent reads as
|
|
454
|
+
/** One cell's text: absent reads as an empty control, containers as JSON. */
|
|
205
455
|
function format(value) {
|
|
206
|
-
if (value === void 0) return "
|
|
456
|
+
if (value === void 0) return "";
|
|
207
457
|
if (typeof value === "string") return value;
|
|
208
458
|
return JSON.stringify(value);
|
|
209
459
|
}
|
|
210
|
-
/**
|
|
211
|
-
|
|
212
|
-
|
|
460
|
+
/**
|
|
461
|
+
* Parse one edited value into what the scope should receive.
|
|
462
|
+
* @param control - the registry's control kind for the field.
|
|
463
|
+
* @param text - the text the operator left in the control.
|
|
464
|
+
* @returns the value handed to \`set\`; a string stays raw so the Host's own
|
|
465
|
+
* validation produces the error message the operator reads.
|
|
466
|
+
*/
|
|
467
|
+
function parseFor(control, text) {
|
|
468
|
+
const trimmed = text.trim();
|
|
469
|
+
if (control === "switch") return trimmed === "true";
|
|
470
|
+
if (control === "number") {
|
|
471
|
+
const parsed = Number(trimmed);
|
|
472
|
+
return trimmed !== "" && Number.isFinite(parsed) ? parsed : trimmed;
|
|
473
|
+
}
|
|
213
474
|
try {
|
|
214
|
-
return JSON.parse(
|
|
475
|
+
return JSON.parse(trimmed);
|
|
215
476
|
} catch {
|
|
216
|
-
return
|
|
477
|
+
return trimmed;
|
|
217
478
|
}
|
|
218
479
|
}
|
|
219
480
|
/** Whether a value is a plain section object. */
|
|
220
481
|
function isSection(value) {
|
|
221
482
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
222
483
|
}
|
|
223
|
-
/**
|
|
224
|
-
function
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
484
|
+
/** Let the settings store settle one write before the read-back (it batches updates). */
|
|
485
|
+
async function settle() {
|
|
486
|
+
await new Promise((done) => {
|
|
487
|
+
setTimeout(done, 60);
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
/** One field's control, typed from the registry. */
|
|
491
|
+
function FieldControl(props) {
|
|
492
|
+
const { field, text } = props;
|
|
493
|
+
const common = {
|
|
494
|
+
id: "evolution-param-" + field.id,
|
|
228
495
|
disabled: props.disabled,
|
|
229
|
-
|
|
496
|
+
className: "evolution-param-input"
|
|
497
|
+
};
|
|
498
|
+
if (field.control === "switch") return (0, react.createElement)("input", {
|
|
499
|
+
...common,
|
|
500
|
+
className: "evolution-param-check",
|
|
501
|
+
type: "checkbox",
|
|
502
|
+
checked: text === "true",
|
|
230
503
|
onChange: (event) => {
|
|
231
|
-
|
|
504
|
+
props.onChange(field.id, String(event.target.checked));
|
|
232
505
|
}
|
|
233
|
-
})
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
506
|
+
});
|
|
507
|
+
if (field.control === "select") return (0, react.createElement)("select", {
|
|
508
|
+
...common,
|
|
509
|
+
className: "evolution-param-select",
|
|
510
|
+
value: text,
|
|
511
|
+
onChange: (event) => {
|
|
512
|
+
props.onChange(field.id, event.target.value);
|
|
513
|
+
}
|
|
514
|
+
}, [field.values.includes(text) || text === "" ? null : (0, react.createElement)("option", {
|
|
515
|
+
key: text,
|
|
516
|
+
value: text
|
|
517
|
+
}, text), ...field.values.map((value) => (0, react.createElement)("option", {
|
|
518
|
+
key: value,
|
|
519
|
+
value
|
|
520
|
+
}, value))]);
|
|
521
|
+
return (0, react.createElement)("input", {
|
|
522
|
+
...common,
|
|
523
|
+
type: field.control === "number" ? "number" : "text",
|
|
524
|
+
value: text,
|
|
525
|
+
onChange: (event) => {
|
|
526
|
+
props.onChange(field.id, event.target.value);
|
|
237
527
|
}
|
|
238
|
-
}
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
/** One parameter field: label, control, hint, and its reset-to-deployment action. */
|
|
531
|
+
function FieldBlock(props) {
|
|
532
|
+
const { field, t } = props;
|
|
533
|
+
const unit = field.unit === "" ? null : (0, react.createElement)("span", { className: "evolution-param-unit" }, "(" + field.unit + ")");
|
|
534
|
+
return (0, react.createElement)("div", { className: "evolution-param-field" }, (0, react.createElement)("div", { className: "evolution-param-label" }, (0, react.createElement)("label", { htmlFor: "evolution-param-" + field.id }, field.label === "" ? field.id : field.label), unit, (0, react.createElement)("span", {
|
|
535
|
+
className: "evolution-param-source",
|
|
536
|
+
"data-user": props.overridden ? "true" : "false"
|
|
537
|
+
}, props.overridden ? t("overridden") : t("deployment"))), FieldControl(props), (0, react.createElement)("p", { className: "evolution-param-hint" }, field.hint === "" ? field.doc : field.hint), props.overridden ? (0, react.createElement)("div", { className: "evolution-param-actions" }, (0, react.createElement)("button", {
|
|
538
|
+
type: "button",
|
|
539
|
+
className: "evolution-param-button",
|
|
239
540
|
disabled: props.disabled,
|
|
240
541
|
onClick: () => {
|
|
241
|
-
props.clear(
|
|
542
|
+
props.clear(field.id);
|
|
242
543
|
}
|
|
243
|
-
},
|
|
544
|
+
}, t("reset"))) : null);
|
|
244
545
|
}
|
|
245
546
|
/**
|
|
246
547
|
* Render one namespace's card.
|
|
@@ -248,78 +549,246 @@ window.__ModuleLoader__.load({
|
|
|
248
549
|
* @returns the card.
|
|
249
550
|
*/
|
|
250
551
|
function ParamCard(props) {
|
|
251
|
-
const { t, fields, write, clear } = props;
|
|
552
|
+
const { t, fields, write, clear, namespace } = props;
|
|
553
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
554
|
+
const [draft, setDraft] = (0, react.useState)({});
|
|
555
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
556
|
+
const [error, setError] = (0, react.useState)("");
|
|
252
557
|
const snapshot = props.useParamSection((state) => state);
|
|
253
|
-
if (snapshot.status === "loading") return (0, react.createElement)("p",
|
|
254
|
-
if (snapshot.status === "unavailable") return (0, react.createElement)("p",
|
|
558
|
+
if (snapshot.status === "loading") return (0, react.createElement)("p", { className: "evolution-param-note" }, t("loading"));
|
|
559
|
+
if (snapshot.status === "unavailable") return (0, react.createElement)("p", { className: "evolution-param-note" }, t("unavailable"));
|
|
255
560
|
const user = isSection(snapshot.user) ? snapshot.user : {};
|
|
256
561
|
const value = isSection(snapshot.value) ? snapshot.value : {};
|
|
257
562
|
const disabled = !snapshot.writable;
|
|
258
|
-
|
|
563
|
+
const titleKey = NAMESPACE_TITLES[namespace];
|
|
564
|
+
const title = titleKey === void 0 ? namespace : t(titleKey);
|
|
565
|
+
const changed = fields.filter((field) => Object.hasOwn(user, field.id)).length;
|
|
566
|
+
const textOf = (field) => draft[field.id] ?? format(value[field.id]);
|
|
567
|
+
const dirty = fields.filter((field) => draft[field.id] !== void 0 && draft[field.id] !== format(value[field.id]));
|
|
568
|
+
const change = (id, next) => {
|
|
569
|
+
setDraft({
|
|
570
|
+
...draft,
|
|
571
|
+
[id]: next
|
|
572
|
+
});
|
|
573
|
+
};
|
|
574
|
+
const save = () => {
|
|
575
|
+
setBusy(true);
|
|
576
|
+
setError("");
|
|
577
|
+
(async () => {
|
|
578
|
+
try {
|
|
579
|
+
for (const field of dirty) await write(field.id, parseFor(field.control, textOf(field)));
|
|
580
|
+
let landed = isSection(props.hooks.paramSection.getSnapshot().user) ? props.hooks.paramSection.getSnapshot().user : {};
|
|
581
|
+
for (let attempt = 0; attempt < 4 && dirty.some((field) => !Object.hasOwn(landed, field.id)); attempt += 1) {
|
|
582
|
+
await settle();
|
|
583
|
+
const fresh = props.hooks.paramSection.getSnapshot();
|
|
584
|
+
landed = isSection(fresh.user) ? fresh.user : {};
|
|
585
|
+
}
|
|
586
|
+
if (dirty.some((field) => !Object.hasOwn(landed, field.id))) {
|
|
587
|
+
setError(t("refused"));
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
setDraft({});
|
|
591
|
+
} catch (caught) {
|
|
592
|
+
setError(caught instanceof Error && caught.message !== "" ? caught.message : t("refused"));
|
|
593
|
+
} finally {
|
|
594
|
+
setBusy(false);
|
|
595
|
+
}
|
|
596
|
+
})();
|
|
597
|
+
};
|
|
598
|
+
return (0, react.createElement)("section", { className: "evolution-param-card" }, (0, react.createElement)("button", {
|
|
599
|
+
type: "button",
|
|
600
|
+
className: "evolution-param-head",
|
|
601
|
+
"aria-expanded": open ? "true" : "false",
|
|
602
|
+
onClick: () => {
|
|
603
|
+
setOpen(!open);
|
|
604
|
+
}
|
|
605
|
+
}, (0, react.createElement)("span", { className: "evolution-param-card-title" }, title), changed === 0 ? null : (0, react.createElement)("span", { className: "evolution-param-count" }, t("changed").replace("{n}", String(changed))), (0, react.createElement)("span", { className: "evolution-param-chevron" }, open ? "▲" : "▼")), open ? (0, react.createElement)("div", { className: "evolution-param-body" }, disabled ? (0, react.createElement)("p", { className: "evolution-param-note" }, t("readonly")) : null, fields.length === 0 ? (0, react.createElement)("p", { className: "evolution-param-note" }, t("empty")) : fields.map((field) => (0, react.createElement)(FieldBlock, {
|
|
259
606
|
key: field.id,
|
|
260
607
|
field,
|
|
261
|
-
|
|
608
|
+
text: textOf(field),
|
|
262
609
|
overridden: Object.hasOwn(user, field.id),
|
|
263
610
|
disabled,
|
|
264
611
|
t,
|
|
265
|
-
|
|
612
|
+
onChange: change,
|
|
266
613
|
clear
|
|
267
|
-
})))
|
|
614
|
+
})), error === "" ? null : (0, react.createElement)("p", { className: "evolution-param-error" }, error), fields.length === 0 ? null : (0, react.createElement)("div", { className: "evolution-param-footer" }, (0, react.createElement)("button", {
|
|
615
|
+
type: "button",
|
|
616
|
+
className: "evolution-param-button",
|
|
617
|
+
disabled: busy || dirty.length === 0,
|
|
618
|
+
onClick: () => {
|
|
619
|
+
setDraft({});
|
|
620
|
+
setError("");
|
|
621
|
+
}
|
|
622
|
+
}, t("discard")), (0, react.createElement)("button", {
|
|
623
|
+
type: "button",
|
|
624
|
+
className: "evolution-param-button",
|
|
625
|
+
"data-primary": "true",
|
|
626
|
+
disabled: disabled || busy || dirty.length === 0,
|
|
627
|
+
onClick: save
|
|
628
|
+
}, busy ? t("saving") : t("apply")))) : null);
|
|
268
629
|
}
|
|
269
630
|
//#endregion
|
|
270
|
-
//#region src/client/
|
|
631
|
+
//#region src/client/seam.ts
|
|
632
|
+
/** Our card slot, hosted by the section this bundle registers. */
|
|
633
|
+
const CARD_SLOT = "evolution.namespace.card";
|
|
634
|
+
//#endregion
|
|
635
|
+
//#region src/client/SettingsSection.ts
|
|
271
636
|
/**
|
|
272
|
-
*
|
|
637
|
+
* The section shell: title, one-line explanation, then one card per namespace.
|
|
273
638
|
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
639
|
+
* This is the shape the platform's own settings sections use — a heading plus the
|
|
640
|
+
* child slots it declared in its registration. The cards themselves are separate
|
|
641
|
+
* registrations keyed by namespace, so a namespace the Host does not serve simply
|
|
642
|
+
* renders nothing and a deployment that drops this row loses the whole section.
|
|
643
|
+
* @module @lmzhen/dsh-evolution-settings-ui/client
|
|
278
644
|
*/
|
|
279
|
-
/** Message keys the cards render. */
|
|
280
|
-
const MESSAGES = {
|
|
281
|
-
title: "Evolution parameters",
|
|
282
|
-
overridden: "user override",
|
|
283
|
-
deployment: "deployment value",
|
|
284
|
-
loading: "reading the settings document…",
|
|
285
|
-
unavailable: "this namespace is not served by the Host (its owning plugin row is not mounted)",
|
|
286
|
-
readonly: "the settings document is read-only in this session (memory mode)",
|
|
287
|
-
apply: "Apply",
|
|
288
|
-
reset: "Reset to deployment",
|
|
289
|
-
empty: "no writable field in this namespace"
|
|
290
|
-
};
|
|
291
645
|
/**
|
|
292
|
-
*
|
|
293
|
-
* @param
|
|
294
|
-
* @returns the
|
|
646
|
+
* Render the section.
|
|
647
|
+
* @param face - the shell's inject face: translator and child-slot renderer.
|
|
648
|
+
* @returns the section element.
|
|
649
|
+
*/
|
|
650
|
+
function SettingsSection(face) {
|
|
651
|
+
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")), ...CLIENT_PARAM_SECTIONS.map((section_) => (0, react.createElement)("div", {
|
|
652
|
+
key: section_.namespace,
|
|
653
|
+
className: "evolution-params-group"
|
|
654
|
+
}, face.renderSlot(CARD_SLOT, {}, { entryKey: section_.namespace }))));
|
|
655
|
+
}
|
|
656
|
+
//#endregion
|
|
657
|
+
//#region src/client/styles.ts
|
|
658
|
+
/**
|
|
659
|
+
* The section's stylesheet.
|
|
660
|
+
*
|
|
661
|
+
* The platform's own client bundles compile CSS Modules into a string and inject
|
|
662
|
+
* it once behind a \`<style data-plugin-css="<pkg>/<file>">\` tag; this bundle is
|
|
663
|
+
* built outside that pipeline, so it carries its own string and injects it the
|
|
664
|
+
* same way. Every rule reads the platform's design tokens (\`--dsw-alias-*\`), so
|
|
665
|
+
* the section follows the light and dark themes without owning a colour, and the
|
|
666
|
+
* field metrics (12px/6px padding-gap, 13px label, 12px hint) match the ones the
|
|
667
|
+
* platform's own settings fields use.
|
|
668
|
+
* @module @lmzhen/dsh-evolution-settings-ui/client
|
|
295
669
|
*/
|
|
296
|
-
|
|
297
|
-
|
|
670
|
+
/** Tag id that makes the injection idempotent. */
|
|
671
|
+
const CSS_TAG_ID = "@lmzhen/dsh-evolution-settings-ui/settings.css";
|
|
672
|
+
/** The stylesheet the section injects once. */
|
|
673
|
+
const CSS = [
|
|
674
|
+
".evolution-params{display:flex;flex-direction:column}",
|
|
675
|
+
".evolution-params-title{margin:0 0 4px;color:var(--dsw-alias-label-primary);font-size:16px;font-weight:600;line-height:1.5}",
|
|
676
|
+
".evolution-params-subtitle{margin:0 0 16px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}",
|
|
677
|
+
".evolution-param-card{border:.5px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-1);margin-bottom:12px}",
|
|
678
|
+
".evolution-param-head{display:flex;align-items:center;gap:8px;width:100%;padding:12px 14px;background:none;border:0;text-align:left;cursor:pointer;color:inherit;font:inherit}",
|
|
679
|
+
".evolution-param-head:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
680
|
+
".evolution-param-card-title{flex:1;min-width:0;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:1.5}",
|
|
681
|
+
".evolution-param-count{color:var(--dsw-alias-brand-primary);font-size:12px;line-height:1.5}",
|
|
682
|
+
".evolution-param-chevron{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}",
|
|
683
|
+
".evolution-param-body{padding:0 14px 12px;border-top:.5px solid var(--dsw-alias-border-l3)}",
|
|
684
|
+
".evolution-param-field{display:flex;flex-direction:column;gap:6px;padding:12px 0}",
|
|
685
|
+
".evolution-param-field+.evolution-param-field{border-top:.5px solid var(--dsw-alias-border-l4)}",
|
|
686
|
+
".evolution-param-label{display:flex;align-items:center;gap:6px;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:1.5}",
|
|
687
|
+
".evolution-param-unit{color:var(--dsw-alias-label-tertiary);font-weight:400}",
|
|
688
|
+
".evolution-param-hint{margin:0;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}",
|
|
689
|
+
".evolution-param-value{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.5}",
|
|
690
|
+
".evolution-param-input{width:100%;box-sizing:border-box;padding:8px 10px;border:.5px solid var(--dsw-alias-border-l2);border-radius:6px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.5}",
|
|
691
|
+
".evolution-param-input:focus{outline:none;border-color:var(--dsw-alias-brand-primary)}",
|
|
692
|
+
".evolution-param-input:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}",
|
|
693
|
+
".evolution-param-source{display:inline-flex;align-items:center;height:20px;padding:0 6px;border-radius:4px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-secondary);font-size:12px;line-height:20px}",
|
|
694
|
+
".evolution-param-source[data-user=\"true\"]{background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-label-primary-foreground)}",
|
|
695
|
+
".evolution-param-actions{display:flex;justify-content:flex-end;gap:8px;padding-top:12px}",
|
|
696
|
+
".evolution-param-button{padding:6px 12px;border:.5px solid var(--dsw-alias-border-l2);border-radius:6px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.5;cursor:pointer}",
|
|
697
|
+
".evolution-param-button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}",
|
|
698
|
+
".evolution-param-button[data-primary=\"true\"]{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border-color:transparent}",
|
|
699
|
+
".evolution-param-button[data-primary=\"true\"]:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}",
|
|
700
|
+
".evolution-param-button:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}",
|
|
701
|
+
".evolution-param-select{width:100%;box-sizing:border-box;padding:8px 10px;border:.5px solid var(--dsw-alias-border-l2);border-radius:6px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.5}",
|
|
702
|
+
".evolution-param-select:focus{outline:none;border-color:var(--dsw-alias-brand-primary)}",
|
|
703
|
+
".evolution-param-select:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}",
|
|
704
|
+
".evolution-param-check{width:16px;height:16px;margin:0;accent-color:var(--dsw-alias-brand-primary);cursor:pointer}",
|
|
705
|
+
".evolution-param-check:disabled{cursor:default;opacity:.5}",
|
|
706
|
+
".evolution-param-footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 0 0;border-top:.5px solid var(--dsw-alias-border-l3)}",
|
|
707
|
+
".evolution-param-note{margin:0;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}",
|
|
708
|
+
".evolution-param-error{margin:0;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:1.5}"
|
|
709
|
+
].join("");
|
|
710
|
+
/**
|
|
711
|
+
* Inject the stylesheet once per document.
|
|
712
|
+
* @param host - the document to inject into; defaults to the global one.
|
|
713
|
+
*/
|
|
714
|
+
function injectStyles(host) {
|
|
715
|
+
const doc = host ?? globalThis.document;
|
|
716
|
+
if (doc === void 0) return;
|
|
717
|
+
if (doc.querySelector("style[data-plugin-css=\"@lmzhen/dsh-evolution-settings-ui/settings.css\"]") !== null) return;
|
|
718
|
+
const tag = doc.createElement("style");
|
|
719
|
+
tag.dataset.plugin = "@lmzhen/dsh-evolution-settings-ui";
|
|
720
|
+
tag.dataset.pluginCss = CSS_TAG_ID;
|
|
721
|
+
tag.textContent = CSS;
|
|
722
|
+
doc.head.appendChild(tag);
|
|
298
723
|
}
|
|
299
724
|
//#endregion
|
|
300
725
|
//#region src/client/index.ts
|
|
301
|
-
/** Required client services: the slot registry
|
|
302
|
-
const inject = [
|
|
726
|
+
/** Required client services: the slot registry, the settings transport, the locale seat. */
|
|
727
|
+
const inject = [
|
|
728
|
+
"slots",
|
|
729
|
+
"settingsScope",
|
|
730
|
+
"locale"
|
|
731
|
+
];
|
|
732
|
+
/** Section id and nav order (the platform's own sections sit at 0/10/15/20). */
|
|
733
|
+
const SECTION_ID = "evolution";
|
|
734
|
+
const SECTION_ORDER = 25;
|
|
303
735
|
/**
|
|
304
|
-
* Register
|
|
736
|
+
* Register the locale namespace, the section and its cards.
|
|
305
737
|
* @param ctx - client cordis context.
|
|
306
738
|
*/
|
|
307
739
|
function apply(ctx) {
|
|
740
|
+
injectStyles();
|
|
308
741
|
const seam = ctx;
|
|
309
|
-
|
|
742
|
+
let registered = false;
|
|
743
|
+
ctx.effect(() => {
|
|
744
|
+
try {
|
|
745
|
+
const dispose = seam.locale.register(NS, {
|
|
746
|
+
zh,
|
|
747
|
+
en
|
|
748
|
+
});
|
|
749
|
+
registered = true;
|
|
750
|
+
return dispose;
|
|
751
|
+
} catch {
|
|
752
|
+
return () => {};
|
|
753
|
+
}
|
|
754
|
+
}, "evolution-settings: dictionaries");
|
|
755
|
+
let bound = null;
|
|
756
|
+
const t = (key) => {
|
|
757
|
+
if (!registered) return message(key);
|
|
758
|
+
if (bound === null) try {
|
|
759
|
+
bound = seam.locale.bind(NS);
|
|
760
|
+
} catch {
|
|
761
|
+
return message(key);
|
|
762
|
+
}
|
|
763
|
+
return bound(key);
|
|
764
|
+
};
|
|
765
|
+
seam.slots.inject("settings.section", function* () {
|
|
766
|
+
yield seam.slots.register({
|
|
767
|
+
name: "settings.section",
|
|
768
|
+
id: SECTION_ID,
|
|
769
|
+
order: 25,
|
|
770
|
+
label: () => t("title"),
|
|
771
|
+
locale: NS,
|
|
772
|
+
children: { [CARD_SLOT]: {
|
|
773
|
+
kind: "keyed",
|
|
774
|
+
scope: "root"
|
|
775
|
+
} }
|
|
776
|
+
}, SettingsSection);
|
|
777
|
+
});
|
|
778
|
+
seam.slots.inject(CARD_SLOT, function* () {
|
|
310
779
|
for (const section of CLIENT_PARAM_SECTIONS) {
|
|
311
780
|
const scope = seam.settingsScope.bind({ namespace: section.namespace });
|
|
312
781
|
const source = scope;
|
|
313
782
|
const face = () => ({
|
|
314
783
|
namespace: section.namespace,
|
|
315
784
|
fields: section.fields,
|
|
316
|
-
t
|
|
785
|
+
t,
|
|
317
786
|
write: (field, value) => scope.set(field, value),
|
|
318
787
|
clear: (field) => scope.unset(field),
|
|
319
788
|
hooks: { paramSection: source }
|
|
320
789
|
});
|
|
321
790
|
yield seam.slots.register({
|
|
322
|
-
name:
|
|
791
|
+
name: CARD_SLOT,
|
|
323
792
|
key: section.namespace,
|
|
324
793
|
inject: face
|
|
325
794
|
}, ParamCard);
|
|
@@ -327,6 +796,8 @@ window.__ModuleLoader__.load({
|
|
|
327
796
|
});
|
|
328
797
|
}
|
|
329
798
|
//#endregion
|
|
799
|
+
exports.SECTION_ID = SECTION_ID;
|
|
800
|
+
exports.SECTION_ORDER = SECTION_ORDER;
|
|
330
801
|
exports.apply = apply;
|
|
331
802
|
exports.inject = inject;
|
|
332
803
|
|
|
@@ -4,11 +4,18 @@
|
|
|
4
4
|
* Structural local types on purpose: this package is distributed outside the
|
|
5
5
|
* platform repository and a value import of another plugin is forbidden, so the
|
|
6
6
|
* settings scope and its snapshot are declared by the shape the renderer hands
|
|
7
|
-
* over (
|
|
7
|
+
* over (\`getSnapshot\`/\`subscribe\`, status/value/user/writable).
|
|
8
|
+
*
|
|
9
|
+
* Layout and behaviour follow the platform's own settings fields: a collapsed card
|
|
10
|
+
* header (namespace title, change count, chevron), then one block per field —
|
|
11
|
+
* label with its unit and source chip, a control HOLDING THE CURRENT VALUE, the
|
|
12
|
+
* Chinese hint — and one 放弃修改/保存 pair at the card's foot. The control is
|
|
13
|
+
* typed from the registry (\`control\`), so booleans are switches, enums are
|
|
14
|
+
* selects and numbers are numeric inputs with the unit shown beside the label.
|
|
8
15
|
*/
|
|
9
16
|
import { type ReactNode } from 'react';
|
|
10
17
|
import type { ClientParamField } from './generated-params.ts';
|
|
11
|
-
import type
|
|
18
|
+
import { type MessageKey } from './messages.ts';
|
|
12
19
|
import type { ParamSectionSnapshot, ParamSectionSource } from './seam.ts';
|
|
13
20
|
/** Injected face: plain data and callbacks, plus the hook seat. */
|
|
14
21
|
export interface ParamCardFace {
|
|
@@ -23,7 +30,7 @@ export interface ParamCardFace {
|
|
|
23
30
|
}
|
|
24
31
|
/** Props the renderer binds for one card: the inject face plus its hook seat. */
|
|
25
32
|
export type ParamCardProps = ParamCardFace & {
|
|
26
|
-
/** Bound from
|
|
33
|
+
/** Bound from \`hooks.paramSection\` by the renderer. */
|
|
27
34
|
readonly useParamSection: <T>(selector: (state: ParamSectionSnapshot) => T) => T;
|
|
28
35
|
};
|
|
29
36
|
/**
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The section shell: title, one-line explanation, then one card per namespace.
|
|
3
|
+
*
|
|
4
|
+
* This is the shape the platform's own settings sections use — a heading plus the
|
|
5
|
+
* child slots it declared in its registration. The cards themselves are separate
|
|
6
|
+
* registrations keyed by namespace, so a namespace the Host does not serve simply
|
|
7
|
+
* renders nothing and a deployment that drops this row loses the whole section.
|
|
8
|
+
* @module @lmzhen/dsh-evolution-settings-ui/client
|
|
9
|
+
*/
|
|
10
|
+
import { type ReactNode } from 'react';
|
|
11
|
+
import { type SectionFace } from './seam.ts';
|
|
12
|
+
/**
|
|
13
|
+
* Render the section.
|
|
14
|
+
* @param face - the shell's inject face: translator and child-slot renderer.
|
|
15
|
+
* @returns the section element.
|
|
16
|
+
*/
|
|
17
|
+
export declare function SettingsSection(face: SectionFace): ReactNode;
|
|
18
|
+
//# sourceMappingURL=SettingsSection.d.ts.map
|
|
@@ -8,8 +8,18 @@
|
|
|
8
8
|
export interface ClientParamField {
|
|
9
9
|
id: string;
|
|
10
10
|
group: string;
|
|
11
|
-
/** The registry summary,
|
|
11
|
+
/** The registry summary (English), kept for parity with PARAMETERS.md. */
|
|
12
12
|
doc: string;
|
|
13
|
+
/** Card label, from the registry UI tail (Chinese). */
|
|
14
|
+
label: string;
|
|
15
|
+
/** Card help text, from the registry UI tail (Chinese). */
|
|
16
|
+
hint: string;
|
|
17
|
+
/** Which control the card renders. */
|
|
18
|
+
control: 'number' | 'switch' | 'select' | 'text';
|
|
19
|
+
/** Unit suffix for a number field ('' when it carries none). */
|
|
20
|
+
unit: string;
|
|
21
|
+
/** Allowed values for a select field ([] otherwise). */
|
|
22
|
+
values: readonly string[];
|
|
13
23
|
}
|
|
14
24
|
/** One card: the settings namespace plus the fields it exposes. */
|
|
15
25
|
export interface ClientParamSection {
|
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Browser half: one
|
|
2
|
+
* Browser half: one settings section ("自进化") hosting one card per namespace.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Until 0.7.0 the cards lived inside the platform's own 插件 section, because
|
|
5
|
+
* this bundle injected `@deepseek-ai/dsh-client-ui-settings-plugins` and claimed
|
|
6
|
+
* its keyed `settings.plugin.item` slot. The parameters are not a plugin
|
|
7
|
+
* inventory though — the other feature areas (market, skins, Web plugins, side
|
|
8
|
+
* cards) each register their OWN section, so this bundle now does the same:
|
|
9
|
+
* inject the settings shell, register `settings.section`, and host the cards in
|
|
10
|
+
* a keyed child slot of that section.
|
|
11
|
+
*
|
|
12
|
+
* The join key is still the namespace itself: the Host half of each owning plugin
|
|
13
|
+
* registers it, this half claims it in the keyed card slot, and the section pairs
|
|
14
|
+
* them. A namespace the Host does not serve renders nothing, and a deployment that
|
|
15
|
+
* drops this row shows no section at all: the pair carries no Host-side behaviour.
|
|
9
16
|
* @module @lmzhen/dsh-evolution-settings-ui/client
|
|
10
17
|
*/
|
|
11
18
|
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
12
|
-
/** Required client services: the slot registry
|
|
19
|
+
/** Required client services: the slot registry, the settings transport, the locale seat. */
|
|
13
20
|
export declare const inject: string[];
|
|
21
|
+
/** Section id and nav order (the platform's own sections sit at 0/10/15/20). */
|
|
22
|
+
export declare const SECTION_ID = "evolution";
|
|
23
|
+
export declare const SECTION_ORDER = 25;
|
|
14
24
|
/**
|
|
15
|
-
* Register
|
|
25
|
+
* Register the locale namespace, the section and its cards.
|
|
16
26
|
* @param ctx - client cordis context.
|
|
17
27
|
*/
|
|
18
28
|
export declare function apply(ctx: ClientContext): void;
|
|
@@ -1,29 +1,52 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copy for the evolution parameter cards.
|
|
2
|
+
* Copy for the evolution settings section and its parameter cards.
|
|
3
3
|
*
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The section is registered through the platform's locale seat (`ctx.locale`),
|
|
5
|
+
* which is the same route the other out-of-repo client plugins take. The earlier
|
|
6
|
+
* note in this package's README — that an outside package cannot bind the locale
|
|
7
|
+
* seat — is wrong: `locale.register(namespace, { zh, en })` is the documented
|
|
8
|
+
* call, and it is what makes the section title and its copy follow the UI
|
|
9
|
+
* language instead of shipping one hard-coded dictionary.
|
|
8
10
|
*/
|
|
9
|
-
/**
|
|
10
|
-
export declare const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
readonly
|
|
14
|
-
readonly
|
|
15
|
-
readonly
|
|
16
|
-
readonly
|
|
17
|
-
readonly
|
|
18
|
-
readonly
|
|
19
|
-
readonly
|
|
11
|
+
/** Locale namespace this bundle registers. */
|
|
12
|
+
export declare const NS = "evolution-settings";
|
|
13
|
+
/** Chinese copy (the family's primary language). */
|
|
14
|
+
export declare const zh: {
|
|
15
|
+
readonly title: "自进化";
|
|
16
|
+
readonly subtitle: "审查/记忆/策展/技能四个命名空间的参数可在用户层覆盖;此处只列可改项,其余仍由部署面决定。";
|
|
17
|
+
readonly overridden: "用户";
|
|
18
|
+
readonly deployment: "部署";
|
|
19
|
+
readonly loading: "正在读取设置文档…";
|
|
20
|
+
readonly unavailable: "这个命名空间没有宿主提供服务(对应插件行未挂载)";
|
|
21
|
+
readonly readonly: "本次会话的设置文档是只读的(内存模式)";
|
|
22
|
+
readonly apply: "保存";
|
|
23
|
+
readonly reset: "恢复部署默认";
|
|
24
|
+
readonly empty: "这个命名空间没有可改项";
|
|
25
|
+
readonly changed: "{n} 项已改";
|
|
26
|
+
readonly discard: "放弃修改";
|
|
27
|
+
readonly saving: "保存中…";
|
|
28
|
+
readonly refused: "写入未被接受——宿主的校验拒绝了它(值保持原样)。原因见 /evolution params 或 /evolution doctor。";
|
|
29
|
+
readonly cardReview: "审查";
|
|
30
|
+
readonly cardMemory: "记忆";
|
|
31
|
+
readonly cardToolMemory: "记忆工具";
|
|
32
|
+
readonly cardCurator: "策展";
|
|
33
|
+
readonly cardSkills: "技能";
|
|
20
34
|
};
|
|
35
|
+
/** English copy, key for key with {@link zh}. */
|
|
36
|
+
export declare const en: Record<keyof typeof zh, string>;
|
|
37
|
+
/**
|
|
38
|
+
* Card titles per settings namespace. A namespace without an entry falls back to
|
|
39
|
+
* its raw name, so a Host that registers a new namespace still renders — the
|
|
40
|
+
* title is presentation, the namespace is the join key.
|
|
41
|
+
*/
|
|
42
|
+
export declare const NAMESPACE_TITLES: Readonly<Record<string, string>>;
|
|
21
43
|
/** One message key. */
|
|
22
|
-
export type MessageKey = keyof typeof
|
|
44
|
+
export type MessageKey = keyof typeof zh;
|
|
23
45
|
/**
|
|
24
|
-
* Resolve one message.
|
|
46
|
+
* Resolve one message without the locale seat (tests and the fallback path).
|
|
25
47
|
* @param key - the message key.
|
|
48
|
+
* @param language - which dictionary to read; defaults to Chinese.
|
|
26
49
|
* @returns the copy this bundle carries.
|
|
27
50
|
*/
|
|
28
|
-
export declare function message(key: MessageKey): string;
|
|
51
|
+
export declare function message(key: MessageKey, language?: 'zh' | 'en'): string;
|
|
29
52
|
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -4,11 +4,15 @@
|
|
|
4
4
|
* The package is distributed OUTSIDE the platform repository, so it declares the
|
|
5
5
|
* two seams it consumes instead of importing another plugin's values (forbidden
|
|
6
6
|
* by the client bundle-purity rule) or its types (which would drag the platform
|
|
7
|
-
* sources into this project). The shapes are the
|
|
8
|
-
* `settings.
|
|
9
|
-
* per-namespace settings scope the write path fences with the
|
|
7
|
+
* sources into this project). The shapes are the shell's contract: a
|
|
8
|
+
* `settings.section` registration hosting a keyed card slot, the inject faces
|
|
9
|
+
* both hand back, the per-namespace settings scope the write path fences with the
|
|
10
|
+
* revision it read, and the locale seat the section's copy resolves through.
|
|
10
11
|
* @module @lmzhen/dsh-evolution-settings-ui/client
|
|
11
12
|
*/
|
|
13
|
+
import type { ReactNode } from 'react';
|
|
14
|
+
/** Our card slot, hosted by the section this bundle registers. */
|
|
15
|
+
export declare const CARD_SLOT = "evolution.namespace.card";
|
|
12
16
|
/** Snapshot of one settings namespace, as the client scope reports it. */
|
|
13
17
|
export interface ParamSectionSnapshot {
|
|
14
18
|
status: 'loading' | 'ready' | 'unavailable';
|
|
@@ -27,23 +31,58 @@ export interface SettingsScopeLike {
|
|
|
27
31
|
set(field: string, value: unknown): Promise<void>;
|
|
28
32
|
unset(field: string): Promise<void>;
|
|
29
33
|
}
|
|
30
|
-
/** Registration options the keyed slot takes. */
|
|
34
|
+
/** Registration options the keyed card slot under our own section takes. */
|
|
31
35
|
export interface SlotRegisterOptions {
|
|
32
|
-
name:
|
|
36
|
+
name: typeof CARD_SLOT;
|
|
33
37
|
/** The settings namespace this card edits — the slot's key. */
|
|
34
38
|
key: string;
|
|
35
39
|
inject: () => unknown;
|
|
36
40
|
}
|
|
41
|
+
/** Options a `settings.section` registration carries (the shell's own shape). */
|
|
42
|
+
export interface SectionRegisterOptions {
|
|
43
|
+
name: 'settings.section';
|
|
44
|
+
/** Section id; the shell derives the nav entry's DOM id from it. */
|
|
45
|
+
id: string;
|
|
46
|
+
/** Nav order: the platform's own sections sit at 0/10/15/20. */
|
|
47
|
+
order: number;
|
|
48
|
+
/** Lazy label: the shell re-reads it when the locale changes. */
|
|
49
|
+
label: () => string;
|
|
50
|
+
/** Locale namespace the label and the section's copy resolve through. */
|
|
51
|
+
locale: string;
|
|
52
|
+
/** Child slots this section hosts, declared for the shell's renderer. */
|
|
53
|
+
children?: Record<string, {
|
|
54
|
+
kind: 'keyed' | 'list' | 'single';
|
|
55
|
+
scope: 'root';
|
|
56
|
+
}>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The face the shell hands a section component: a translator bound to our locale
|
|
60
|
+
* namespace and the renderer for the child slots we declared.
|
|
61
|
+
*/
|
|
62
|
+
export interface SectionFace {
|
|
63
|
+
t: (key: string) => string;
|
|
64
|
+
/** Render one child slot; `filter` selects keyed entries (e.g. `{ entryKey }`). */
|
|
65
|
+
renderSlot: (name: string, props?: Record<string, unknown>, filter?: Record<string, unknown>) => ReactNode;
|
|
66
|
+
}
|
|
67
|
+
/** The locale seat: register this bundle's namespace, bind a translator. */
|
|
68
|
+
export interface LocaleSeat {
|
|
69
|
+
register(namespace: string, dictionaries: {
|
|
70
|
+
zh: Record<string, string>;
|
|
71
|
+
en: Record<string, string>;
|
|
72
|
+
}): () => void;
|
|
73
|
+
bind(namespace: string): (key: string) => string;
|
|
74
|
+
}
|
|
37
75
|
/** The slice of the client context this plugin consumes. */
|
|
38
76
|
export interface ClientSeam {
|
|
39
77
|
slots: {
|
|
40
78
|
inject(name: string, callback: () => Generator<unknown, void, unknown>): unknown;
|
|
41
|
-
register(options: SlotRegisterOptions, component: (props: never) => unknown): () => void;
|
|
79
|
+
register(options: SlotRegisterOptions | SectionRegisterOptions, component: (props: never) => unknown): () => void;
|
|
42
80
|
};
|
|
43
81
|
settingsScope: {
|
|
44
82
|
bind(spec: {
|
|
45
83
|
namespace: string;
|
|
46
84
|
}): SettingsScopeLike;
|
|
47
85
|
};
|
|
86
|
+
locale: LocaleSeat;
|
|
48
87
|
}
|
|
49
88
|
//# sourceMappingURL=seam.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The section's stylesheet.
|
|
3
|
+
*
|
|
4
|
+
* The platform's own client bundles compile CSS Modules into a string and inject
|
|
5
|
+
* it once behind a \`<style data-plugin-css="<pkg>/<file>">\` tag; this bundle is
|
|
6
|
+
* built outside that pipeline, so it carries its own string and injects it the
|
|
7
|
+
* same way. Every rule reads the platform's design tokens (\`--dsw-alias-*\`), so
|
|
8
|
+
* the section follows the light and dark themes without owning a colour, and the
|
|
9
|
+
* field metrics (12px/6px padding-gap, 13px label, 12px hint) match the ones the
|
|
10
|
+
* platform's own settings fields use.
|
|
11
|
+
* @module @lmzhen/dsh-evolution-settings-ui/client
|
|
12
|
+
*/
|
|
13
|
+
/** Tag id that makes the injection idempotent. */
|
|
14
|
+
export declare const CSS_TAG_ID = "@lmzhen/dsh-evolution-settings-ui/settings.css";
|
|
15
|
+
/** The stylesheet the section injects once. */
|
|
16
|
+
export declare const CSS: string;
|
|
17
|
+
/** The slice of the DOM the injection touches (this package typechecks without DOM lib). */
|
|
18
|
+
interface StyleHost {
|
|
19
|
+
querySelector(selector: string): unknown;
|
|
20
|
+
createElement(tag: string): {
|
|
21
|
+
dataset: Record<string, string>;
|
|
22
|
+
textContent: string;
|
|
23
|
+
};
|
|
24
|
+
head: {
|
|
25
|
+
appendChild(node: unknown): void;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Inject the stylesheet once per document.
|
|
30
|
+
* @param host - the document to inject into; defaults to the global one.
|
|
31
|
+
*/
|
|
32
|
+
export declare function injectStyles(host?: StyleHost): void;
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=styles.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmzhen/dsh-evolution-settings-ui",
|
|
3
3
|
"description": "Web settings cards for the evolution family's parameter namespaces (community build)",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.7.0",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
"client": {
|
|
34
34
|
"platform": "web",
|
|
35
35
|
"inject": [
|
|
36
|
-
"@deepseek-ai/dsh-client-ui-settings
|
|
36
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
37
|
+
"@deepseek-ai/dsh-client-locale"
|
|
37
38
|
]
|
|
38
39
|
}
|
|
39
40
|
},
|