@dsh-plus/subagent-model 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +453 -300
- package/package.json +4 -2
- package/src/client/card.tsx +86 -102
- package/src/client/client.ts +5 -84
- package/src/client/i18n.ts +7 -22
- package/src/client/styles.ts +12 -48
- package/src/client/fields.tsx +0 -75
- package/src/client/scope.ts +0 -59
package/lib/client.js
CHANGED
|
@@ -4,8 +4,366 @@ window.__ModuleLoader__.load({
|
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
-
let react = require("react");
|
|
8
7
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
8
|
+
let react = require("react");
|
|
9
|
+
//#region ../shared/src/client/icons.tsx
|
|
10
|
+
/** 官方 IconChevronDownOutline14 的同构实现(path 数据逐字一致)。 */
|
|
11
|
+
function ChevronDownIcon(props) {
|
|
12
|
+
const size = props.size ?? 14;
|
|
13
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
14
|
+
width: size,
|
|
15
|
+
height: size,
|
|
16
|
+
className: props.className,
|
|
17
|
+
viewBox: "0 0 14 14",
|
|
18
|
+
fill: "none",
|
|
19
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
20
|
+
"aria-hidden": "true",
|
|
21
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
22
|
+
d: "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z",
|
|
23
|
+
fill: "currentColor"
|
|
24
|
+
})
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region ../shared/src/client/card.tsx
|
|
29
|
+
const IDLE_STATUS = {
|
|
30
|
+
kind: "idle",
|
|
31
|
+
text: ""
|
|
32
|
+
};
|
|
33
|
+
function CardChrome(props) {
|
|
34
|
+
const p = props.prefix;
|
|
35
|
+
const badge = props.statusBadge;
|
|
36
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
37
|
+
className: `${p}-card${props.open ? ` ${p}-cardOpen` : ""}`,
|
|
38
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
39
|
+
type: "button",
|
|
40
|
+
className: `${p}-header`,
|
|
41
|
+
"aria-expanded": props.open,
|
|
42
|
+
"aria-label": props.title,
|
|
43
|
+
onClick: () => props.onToggle(!props.open),
|
|
44
|
+
children: [
|
|
45
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
46
|
+
className: `${p}-headText`,
|
|
47
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
48
|
+
className: `${p}-name`,
|
|
49
|
+
children: props.title
|
|
50
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
51
|
+
className: `${p}-description`,
|
|
52
|
+
children: props.description
|
|
53
|
+
})]
|
|
54
|
+
}),
|
|
55
|
+
badge !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
56
|
+
className: `${p}-statusBadge ${badge.on ? `${p}-statusOn` : `${p}-statusOff`}`,
|
|
57
|
+
children: badge.text
|
|
58
|
+
}) : null,
|
|
59
|
+
props.dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
60
|
+
className: `${p}-pending`,
|
|
61
|
+
children: props.dirtyLabel
|
|
62
|
+
}) : null,
|
|
63
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronDownIcon, { className: `${p}-chevron${props.open ? ` ${p}-chevronOpen` : ""}` })
|
|
64
|
+
]
|
|
65
|
+
}), props.open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
66
|
+
className: `${p}-body`,
|
|
67
|
+
children: [
|
|
68
|
+
props.readOnlyNotice !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
69
|
+
className: `${p}-readOnly`,
|
|
70
|
+
role: "status",
|
|
71
|
+
children: props.readOnlyNotice
|
|
72
|
+
}) : null,
|
|
73
|
+
props.children,
|
|
74
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
75
|
+
className: `${p}-footer`,
|
|
76
|
+
children: [props.status !== void 0 && props.status.kind !== "idle" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
77
|
+
className: `${p}-status${props.status.kind === "error" ? ` ${p}-statusError` : ""}`,
|
|
78
|
+
role: "status",
|
|
79
|
+
children: props.status.text
|
|
80
|
+
}) : null, props.actions.map((action) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
81
|
+
type: "button",
|
|
82
|
+
className: `${p}-btn ${action.variant === "primary" ? `${p}-btnPrimary` : `${p}-btnGhost`}`,
|
|
83
|
+
disabled: action.disabled === true,
|
|
84
|
+
onClick: action.onClick,
|
|
85
|
+
children: action.label
|
|
86
|
+
}, action.key))]
|
|
87
|
+
})
|
|
88
|
+
]
|
|
89
|
+
}) : null]
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region ../shared/src/client/fields.tsx
|
|
94
|
+
/**
|
|
95
|
+
* 勾选行:自绘圆角扁平开关(switch),对齐官方 WebUI 无原生 checkbox 的
|
|
96
|
+
* 设计语言(官方选择类控件全走胶囊/菜单,见 cardCss 的 -switch 规则)。
|
|
97
|
+
* 语义保持 checkbox(无障碍树与表单语义不变),视觉自绘。
|
|
98
|
+
*/
|
|
99
|
+
function CheckRow(props) {
|
|
100
|
+
const p = props.prefix ?? "dshp";
|
|
101
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
102
|
+
className: `${p}-checkRow`,
|
|
103
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
104
|
+
id: props.id,
|
|
105
|
+
type: "checkbox",
|
|
106
|
+
role: "switch",
|
|
107
|
+
"aria-checked": props.checked,
|
|
108
|
+
checked: props.checked,
|
|
109
|
+
disabled: props.disabled === true,
|
|
110
|
+
onChange: (event) => props.onEdit(event.target.checked)
|
|
111
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
112
|
+
htmlFor: props.id,
|
|
113
|
+
children: props.label
|
|
114
|
+
})]
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
function SelectField(props) {
|
|
118
|
+
const p = props.prefix ?? "dshp";
|
|
119
|
+
const invalid = props.invalid === true;
|
|
120
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
121
|
+
className: `${p}-field`,
|
|
122
|
+
children: [
|
|
123
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
124
|
+
className: `${p}-head`,
|
|
125
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
126
|
+
className: `${p}-label`,
|
|
127
|
+
htmlFor: props.id,
|
|
128
|
+
children: props.label
|
|
129
|
+
})
|
|
130
|
+
}),
|
|
131
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
132
|
+
id: props.id,
|
|
133
|
+
className: `${p}-select${invalid ? ` ${p}-inputInvalid` : ""}`,
|
|
134
|
+
"aria-invalid": invalid || void 0,
|
|
135
|
+
value: props.value,
|
|
136
|
+
disabled: props.disabled === true,
|
|
137
|
+
onChange: (event) => props.onEdit(event.target.value),
|
|
138
|
+
children: props.options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
139
|
+
value: option.value,
|
|
140
|
+
children: option.label
|
|
141
|
+
}, option.value))
|
|
142
|
+
}),
|
|
143
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
144
|
+
className: invalid ? `${p}-invalid` : `${p}-hint`,
|
|
145
|
+
children: invalid ? props.invalidLabel ?? "" : props.hint ?? ""
|
|
146
|
+
})
|
|
147
|
+
]
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
//#endregion
|
|
151
|
+
//#region ../shared/src/client/i18n.ts
|
|
152
|
+
/** 卡片通用文案(save/discard/unsaved/loading 等四插件共有键)。 */
|
|
153
|
+
const commonZh = {
|
|
154
|
+
save: "保存",
|
|
155
|
+
saving: "保存中…",
|
|
156
|
+
discard: "放弃",
|
|
157
|
+
unsaved: "未保存",
|
|
158
|
+
loading: "加载中…",
|
|
159
|
+
unavailable: "配置服务不可用。",
|
|
160
|
+
readOnly: "当前部署无 settings provider,配置为只读;请编辑 settings.yaml。",
|
|
161
|
+
invalidNumber: "请输入正整数。",
|
|
162
|
+
collapse: "收起",
|
|
163
|
+
expand: "展开",
|
|
164
|
+
enabledOn: "已启用",
|
|
165
|
+
enabledOff: "未启用",
|
|
166
|
+
saveFailed: "保存失败,请检查填写内容。",
|
|
167
|
+
confirm: "确认",
|
|
168
|
+
cancel: "取消"
|
|
169
|
+
};
|
|
170
|
+
const commonEn = {
|
|
171
|
+
save: "Save",
|
|
172
|
+
saving: "Saving…",
|
|
173
|
+
discard: "Discard",
|
|
174
|
+
unsaved: "Unsaved",
|
|
175
|
+
loading: "Loading…",
|
|
176
|
+
unavailable: "Settings service unavailable.",
|
|
177
|
+
readOnly: "No settings provider in this deployment; config is read-only. Edit settings.yaml.",
|
|
178
|
+
invalidNumber: "Enter a positive integer.",
|
|
179
|
+
collapse: "Collapse",
|
|
180
|
+
expand: "Expand",
|
|
181
|
+
enabledOn: "On",
|
|
182
|
+
enabledOff: "Off",
|
|
183
|
+
saveFailed: "Save failed; check the fields.",
|
|
184
|
+
confirm: "Confirm",
|
|
185
|
+
cancel: "Cancel"
|
|
186
|
+
};
|
|
187
|
+
/** own 覆盖同键合并(返回新对象,入参不可变)。 */
|
|
188
|
+
function mergeDict(base, own) {
|
|
189
|
+
return {
|
|
190
|
+
...base,
|
|
191
|
+
...own
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
//#endregion
|
|
195
|
+
//#region ../shared/src/client/scope.ts
|
|
196
|
+
/**
|
|
197
|
+
* api 直连实现的命名空间 scope(官方配置页 tab 同款模式)。
|
|
198
|
+
* 读:describe 取命名空间视图(脱敏 value/revision + 顶层 writable);
|
|
199
|
+
* 刷新:remote `settings/document-updated`(按命名空间过滤)与
|
|
200
|
+
* `connection/reset`;generation 防旧读覆盖新发布。任何页面 origin
|
|
201
|
+
* (loopback 或 tailnet 信任域名)下行为一致。
|
|
202
|
+
* effect 归属调用方 fiber(label 需含插件名以便诊断)。
|
|
203
|
+
*/
|
|
204
|
+
function createApiScope(api, ns, c, label) {
|
|
205
|
+
let state = {
|
|
206
|
+
status: "loading",
|
|
207
|
+
value: void 0,
|
|
208
|
+
revision: void 0,
|
|
209
|
+
writable: false
|
|
210
|
+
};
|
|
211
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
212
|
+
let generation = 0;
|
|
213
|
+
const publish = (next) => {
|
|
214
|
+
state = next;
|
|
215
|
+
for (const listener of [...listeners]) listener();
|
|
216
|
+
};
|
|
217
|
+
const load = async () => {
|
|
218
|
+
const gen = ++generation;
|
|
219
|
+
let response;
|
|
220
|
+
try {
|
|
221
|
+
response = await api.describe({});
|
|
222
|
+
} catch {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (gen !== generation || !response.result.ok) return;
|
|
226
|
+
const writable = response.result.value?.writable ?? false;
|
|
227
|
+
const view = response.result.value?.namespaces.find((candidate) => candidate.ns === ns);
|
|
228
|
+
if (view === void 0) {
|
|
229
|
+
publish({
|
|
230
|
+
status: "unavailable",
|
|
231
|
+
value: void 0,
|
|
232
|
+
revision: void 0,
|
|
233
|
+
writable
|
|
234
|
+
});
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
publish({
|
|
238
|
+
status: "ready",
|
|
239
|
+
value: view.value,
|
|
240
|
+
revision: view.revision,
|
|
241
|
+
writable
|
|
242
|
+
});
|
|
243
|
+
};
|
|
244
|
+
const refresh = (namespace) => {
|
|
245
|
+
if (namespace !== void 0 && namespace !== ns) return;
|
|
246
|
+
load();
|
|
247
|
+
};
|
|
248
|
+
const disposers = [c.get("remote").$on("settings/document-updated", refresh), c.on("connection/reset", () => {
|
|
249
|
+
load();
|
|
250
|
+
})];
|
|
251
|
+
c.effect(() => () => {
|
|
252
|
+
for (const dispose of disposers) dispose();
|
|
253
|
+
}, label);
|
|
254
|
+
load();
|
|
255
|
+
return {
|
|
256
|
+
getSnapshot: () => state,
|
|
257
|
+
subscribe: (listener) => {
|
|
258
|
+
listeners.add(listener);
|
|
259
|
+
return () => {
|
|
260
|
+
listeners.delete(listener);
|
|
261
|
+
};
|
|
262
|
+
},
|
|
263
|
+
load
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
//#endregion
|
|
267
|
+
//#region ../shared/src/client/styles.ts
|
|
268
|
+
/**
|
|
269
|
+
* 配置卡片样式工厂(各插件 client 的公共收编版)。
|
|
270
|
+
* 视觉规则与迁移前逐字一致(类名前缀换为各插件自身,DOM 类名不变),
|
|
271
|
+
* 另补移动端与可达性增强(767px 断点 + pointer:coarse,对齐 ui-mobile-fit 约定):
|
|
272
|
+
* - 窄屏 input/textarea 16px(防 iOS 聚焦缩放)、可点目标 ≥44px;
|
|
273
|
+
* - footer 按钮等宽换行、状态文字独占一行;
|
|
274
|
+
* - 展开态 body 底部 sticky 保存栏(长表单滚动中始终可达)。
|
|
275
|
+
* 沿用官方 data-plugin / data-plugin-css 约定(HMR 据此卸载),不覆盖上游选择器。
|
|
276
|
+
* @module @dsh-plus/shared/client/styles
|
|
277
|
+
*/
|
|
278
|
+
/** 生成全套卡片 CSS;prefix 为插件类名前缀(如 'dne'),extra 为插件特有规则。 */
|
|
279
|
+
function cardCss(prefix, extra = "") {
|
|
280
|
+
return `
|
|
281
|
+
.${prefix}-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}
|
|
282
|
+
.${prefix}-card:hover{border-color:var(--dsw-alias-label-dimmed)}
|
|
283
|
+
.${prefix}-cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}
|
|
284
|
+
.${prefix}-header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}
|
|
285
|
+
.${prefix}-header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}
|
|
286
|
+
.${prefix}-headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}
|
|
287
|
+
.${prefix}-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}
|
|
288
|
+
.${prefix}-description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}
|
|
289
|
+
.${prefix}-chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s;display:inline-flex}
|
|
290
|
+
.${prefix}-chevronOpen{transform:rotate(180deg)}
|
|
291
|
+
.${prefix}-pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}
|
|
292
|
+
.${prefix}-statusBadge{white-space:nowrap;border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}
|
|
293
|
+
.${prefix}-statusOn{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary)}
|
|
294
|
+
.${prefix}-statusOff{color:var(--dsw-alias-label-tertiary)}
|
|
295
|
+
.${prefix}-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}
|
|
296
|
+
.${prefix}-field{flex-direction:column;gap:6px;padding:12px 0;display:flex}
|
|
297
|
+
.${prefix}-field+.${prefix}-field{border-top:1px solid var(--dsw-alias-border-l2)}
|
|
298
|
+
.${prefix}-head{align-items:center;gap:8px;display:flex}
|
|
299
|
+
.${prefix}-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}
|
|
300
|
+
.${prefix}-badge{white-space:nowrap;border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}
|
|
301
|
+
.${prefix}-badgeSet{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary)}
|
|
302
|
+
.${prefix}-badgeUnset{color:var(--dsw-alias-label-tertiary)}
|
|
303
|
+
.${prefix}-input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;transition:border-color .18s,background .18s}
|
|
304
|
+
.${prefix}-input:hover:not(:disabled):not(:focus-visible){border-color:var(--dsw-alias-label-dimmed)}
|
|
305
|
+
.${prefix}-input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}
|
|
306
|
+
.${prefix}-input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}
|
|
307
|
+
.${prefix}-inputInvalid{border-color:var(--dsw-alias-label-error)}
|
|
308
|
+
.${prefix}-select{appearance:none;background:var(--dsw-alias-bg-module-platform);border:none;cursor:pointer;background-image:linear-gradient(45deg,transparent 50%,var(--dsw-alias-label-secondary) 50%),linear-gradient(135deg,var(--dsw-alias-label-secondary) 50%,transparent 50%);background-position:calc(100% - 16px) 50%,calc(100% - 11px) 50%;background-size:5px 5px;background-repeat:no-repeat;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;height:34px;box-sizing:border-box;border-radius:8px;padding:0 30px 0 12px;transition:background-color .18s}
|
|
309
|
+
.${prefix}-select:hover:not(:disabled){background-color:var(--dsw-alias-interactive-bg-hover)}
|
|
310
|
+
.${prefix}-hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}
|
|
311
|
+
.${prefix}-invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}
|
|
312
|
+
.${prefix}-checkRow{align-items:center;gap:10px;display:flex;padding:6px 0}
|
|
313
|
+
.${prefix}-checkRow label{color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.5;cursor:pointer;flex:1;min-width:0}
|
|
314
|
+
/* 自绘圆角扁平开关(官方 UI 无可见原生 checkbox,选择类全走胶囊/菜单) */
|
|
315
|
+
.${prefix}-checkRow input{appearance:none;-webkit-appearance:none;position:relative;box-sizing:border-box;background:var(--dsw-alias-bg-module-platform);border:none;border-radius:999px;width:34px;height:20px;flex:none;cursor:pointer;transition:background .18s;margin:0}
|
|
316
|
+
.${prefix}-checkRow input::after{content:"";position:absolute;top:2px;left:2px;background:var(--dsw-alias-label-primary);border-radius:999px;width:16px;height:16px;transition:left .18s cubic-bezier(.4,0,.2,1)}
|
|
317
|
+
.${prefix}-checkRow input:checked{background:var(--dsw-alias-brand-primary)}
|
|
318
|
+
.${prefix}-checkRow input:checked::after{left:16px;background:var(--dsw-alias-bg-base)}
|
|
319
|
+
.${prefix}-checkRow input:disabled{opacity:.4;cursor:default}
|
|
320
|
+
.${prefix}-checkRow input:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
321
|
+
.${prefix}-groupLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600;padding:12px 0 2px;margin:0}
|
|
322
|
+
.${prefix}-readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}
|
|
323
|
+
.${prefix}-warn{color:var(--dsw-alias-label-error);margin:12px 0 0;font-size:12px;line-height:1.5}
|
|
324
|
+
.${prefix}-footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex;flex-wrap:wrap}
|
|
325
|
+
.${prefix}-status{min-width:0;color:var(--dsw-alias-label-secondary);flex:1;margin:0;font-size:12px;line-height:1.5}
|
|
326
|
+
.${prefix}-statusError{color:var(--dsw-alias-label-error)}
|
|
327
|
+
.${prefix}-btn{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}
|
|
328
|
+
.${prefix}-btnGhost{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}
|
|
329
|
+
.${prefix}-btnGhost:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}
|
|
330
|
+
.${prefix}-btnPrimary{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}
|
|
331
|
+
.${prefix}-btn:disabled{opacity:.4;cursor:default}
|
|
332
|
+
.${prefix}-btn:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}
|
|
333
|
+
@media (max-width:767px){
|
|
334
|
+
.${prefix}-body{margin:0 10px}
|
|
335
|
+
.${prefix}-header{padding:12px 12px}
|
|
336
|
+
.${prefix}-input,.${prefix}-select{font-size:16px;height:40px}
|
|
337
|
+
.${prefix}-checkRow{min-height:44px;padding:8px 0}
|
|
338
|
+
.${prefix}-footer{position:sticky;bottom:0;background:var(--dsw-alias-bg-layer-2);margin:0 -10px;padding:10px 10px 6px;border-top:1px solid var(--dsw-alias-border-l2);border-bottom-left-radius:12px;border-bottom-right-radius:12px}
|
|
339
|
+
.${prefix}-status{flex-basis:100%;flex:none}
|
|
340
|
+
.${prefix}-btn{min-height:44px;flex:1;min-width:96px}
|
|
341
|
+
.${prefix}-statusBadge{display:none}
|
|
342
|
+
}
|
|
343
|
+
@media (pointer:coarse){
|
|
344
|
+
.${prefix}-btn{min-height:44px}
|
|
345
|
+
.${prefix}-checkRow{min-height:44px}
|
|
346
|
+
.${prefix}-input,.${prefix}-select{height:40px}
|
|
347
|
+
}
|
|
348
|
+
${extra}
|
|
349
|
+
`;
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* 幂等注入样式标签(官方 data-plugin-css 约定);返回新标签,
|
|
353
|
+
* 已存在或环境无 document 时返回 null(调用方 effect 卸载用)。
|
|
354
|
+
*/
|
|
355
|
+
function injectCardStyle(pluginId, css) {
|
|
356
|
+
if (typeof document === "undefined") return null;
|
|
357
|
+
const tagId = `${pluginId}/card.css`;
|
|
358
|
+
if (document.querySelector(`style[data-plugin-css=${JSON.stringify(tagId)}]`) !== null) return null;
|
|
359
|
+
const tag = document.createElement("style");
|
|
360
|
+
tag.dataset.plugin = pluginId;
|
|
361
|
+
tag.dataset.pluginCss = tagId;
|
|
362
|
+
tag.textContent = css;
|
|
363
|
+
document.head.appendChild(tag);
|
|
364
|
+
return tag;
|
|
365
|
+
}
|
|
366
|
+
//#endregion
|
|
9
367
|
//#region src/ns.ts
|
|
10
368
|
/**
|
|
11
369
|
* settings 命名空间字面量(纯常量,零依赖,浏览器半可安全引入)。
|
|
@@ -64,69 +422,16 @@ window.__ModuleLoader__.load({
|
|
|
64
422
|
};
|
|
65
423
|
}
|
|
66
424
|
//#endregion
|
|
67
|
-
//#region src/client/fields.tsx
|
|
68
|
-
function CheckRow(props) {
|
|
69
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
70
|
-
className: "dsm-checkRow",
|
|
71
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
72
|
-
id: props.id,
|
|
73
|
-
type: "checkbox",
|
|
74
|
-
checked: props.checked,
|
|
75
|
-
disabled: props.disabled === true,
|
|
76
|
-
onChange: (event) => props.onEdit(event.target.checked)
|
|
77
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
78
|
-
htmlFor: props.id,
|
|
79
|
-
children: props.label
|
|
80
|
-
})]
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
function SelectField(props) {
|
|
84
|
-
const invalid = props.invalid === true;
|
|
85
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
86
|
-
className: "dsm-field",
|
|
87
|
-
children: [
|
|
88
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
89
|
-
className: "dsm-head",
|
|
90
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
91
|
-
className: "dsm-label",
|
|
92
|
-
htmlFor: props.id,
|
|
93
|
-
children: props.label
|
|
94
|
-
})
|
|
95
|
-
}),
|
|
96
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
97
|
-
id: props.id,
|
|
98
|
-
className: "dsm-select",
|
|
99
|
-
"aria-invalid": invalid || void 0,
|
|
100
|
-
value: props.value,
|
|
101
|
-
disabled: props.disabled === true,
|
|
102
|
-
onChange: (event) => props.onEdit(event.target.value),
|
|
103
|
-
children: props.options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
104
|
-
value: option.value,
|
|
105
|
-
children: option.label
|
|
106
|
-
}, option.value))
|
|
107
|
-
}),
|
|
108
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
109
|
-
className: invalid ? "dsm-invalid" : "dsm-hint",
|
|
110
|
-
children: invalid ? props.invalidLabel ?? "" : props.hint ?? ""
|
|
111
|
-
})
|
|
112
|
-
]
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
//#endregion
|
|
116
425
|
//#region src/client/card.tsx
|
|
117
426
|
/**
|
|
118
427
|
* 「子代理模型配置」配置卡片:注册进 settings.plugin.item 插槽(官方插件配置页)。
|
|
119
|
-
*
|
|
120
|
-
* 配置读写走官方 settingsScope
|
|
121
|
-
*
|
|
428
|
+
* 外壳与基础控件走 @dsh-plus/shared/client 套件(CardChrome/CheckRow/SelectField)。
|
|
429
|
+
* 配置读写走官方 settingsScope 传输:value 为 schema 解析后的命名空间值
|
|
430
|
+
* (enabled + entries),行集合 = 目录返回的已注册子代理 provider
|
|
122
431
|
* ∪ 已配置条目,保存时全量写回 entries(未配置的新 provider 行以默认空值
|
|
123
432
|
* 落盘,自文档化);「模型目录」为唯一保留的自定义端点。
|
|
124
433
|
* @module subagent-model/client/card
|
|
125
434
|
*/
|
|
126
|
-
const IDLE_STATUS = {
|
|
127
|
-
kind: "idle",
|
|
128
|
-
text: ""
|
|
129
|
-
};
|
|
130
435
|
const EFFORT_INHERIT = "inherit";
|
|
131
436
|
const EFFORT_DEFAULT = "default";
|
|
132
437
|
function unknownOption(value, label) {
|
|
@@ -188,6 +493,7 @@ window.__ModuleLoader__.load({
|
|
|
188
493
|
className: "dsm-rowName",
|
|
189
494
|
children: name
|
|
190
495
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckRow, {
|
|
496
|
+
prefix: "dsm",
|
|
191
497
|
id: `dsm-row-${name}`,
|
|
192
498
|
label: t("rowEnabled"),
|
|
193
499
|
checked: row.enabled,
|
|
@@ -203,6 +509,7 @@ window.__ModuleLoader__.load({
|
|
|
203
509
|
children: t("rowHint")
|
|
204
510
|
}),
|
|
205
511
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectField, {
|
|
512
|
+
prefix: "dsm",
|
|
206
513
|
id: `dsm-provider-${name}`,
|
|
207
514
|
label: t("provider"),
|
|
208
515
|
hint: t("providerHint"),
|
|
@@ -215,6 +522,7 @@ window.__ModuleLoader__.load({
|
|
|
215
522
|
})
|
|
216
523
|
}),
|
|
217
524
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectField, {
|
|
525
|
+
prefix: "dsm",
|
|
218
526
|
id: `dsm-model-${name}`,
|
|
219
527
|
label: t("model"),
|
|
220
528
|
hint: t("modelHint"),
|
|
@@ -229,6 +537,7 @@ window.__ModuleLoader__.load({
|
|
|
229
537
|
})
|
|
230
538
|
}),
|
|
231
539
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectField, {
|
|
540
|
+
prefix: "dsm",
|
|
232
541
|
id: `dsm-effort-${name}`,
|
|
233
542
|
label: t("effort"),
|
|
234
543
|
hint: t("effortHint"),
|
|
@@ -342,120 +651,88 @@ window.__ModuleLoader__.load({
|
|
|
342
651
|
const disabled = !snapshot.writable;
|
|
343
652
|
const rowNames = Object.keys(draft.rows).sort();
|
|
344
653
|
const providerCount = (catalog?.providers ?? []).length;
|
|
345
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
417
|
-
className: "dsm-hint dsm-rowHint",
|
|
418
|
-
children: providerCount > 0 ? t("rowDesc") : ""
|
|
419
|
-
}),
|
|
420
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
421
|
-
className: "dsm-footer",
|
|
422
|
-
children: [
|
|
423
|
-
status.kind !== "idle" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
424
|
-
className: `dsm-status${status.kind === "error" ? " dsm-statusError" : ""}`,
|
|
425
|
-
role: "status",
|
|
426
|
-
children: status.text
|
|
427
|
-
}) : null,
|
|
428
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
429
|
-
type: "button",
|
|
430
|
-
className: "dsm-btn dsm-btnGhost",
|
|
431
|
-
disabled: !dirty || saving,
|
|
432
|
-
onClick: () => {
|
|
433
|
-
setDraft(draftFrom(value, catalog));
|
|
434
|
-
setStatus(IDLE_STATUS);
|
|
435
|
-
},
|
|
436
|
-
children: t("discard")
|
|
437
|
-
}),
|
|
438
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
439
|
-
type: "button",
|
|
440
|
-
className: "dsm-btn dsm-btnPrimary",
|
|
441
|
-
disabled: !dirty || invalid || saving || disabled,
|
|
442
|
-
onClick: onSave,
|
|
443
|
-
children: t(saving ? "saving" : "save")
|
|
444
|
-
})
|
|
445
|
-
]
|
|
446
|
-
})
|
|
447
|
-
]
|
|
448
|
-
}) : null]
|
|
654
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CardChrome, {
|
|
655
|
+
prefix: "dsm",
|
|
656
|
+
title: t("title"),
|
|
657
|
+
description: t("description"),
|
|
658
|
+
open,
|
|
659
|
+
onToggle: setOpen,
|
|
660
|
+
statusBadge: {
|
|
661
|
+
text: t(draft.enabled ? "enabledOn" : "enabledOff"),
|
|
662
|
+
on: draft.enabled
|
|
663
|
+
},
|
|
664
|
+
dirty,
|
|
665
|
+
dirtyLabel: t("unsaved"),
|
|
666
|
+
readOnlyNotice: disabled ? t("readOnly") : void 0,
|
|
667
|
+
status,
|
|
668
|
+
actions: [{
|
|
669
|
+
key: "discard",
|
|
670
|
+
label: t("discard"),
|
|
671
|
+
disabled: !dirty || saving,
|
|
672
|
+
onClick: () => {
|
|
673
|
+
setDraft(draftFrom(value, catalog));
|
|
674
|
+
setStatus(IDLE_STATUS);
|
|
675
|
+
}
|
|
676
|
+
}, {
|
|
677
|
+
key: "save",
|
|
678
|
+
label: t(saving ? "saving" : "save"),
|
|
679
|
+
variant: "primary",
|
|
680
|
+
disabled: !dirty || invalid || saving || disabled,
|
|
681
|
+
onClick: onSave
|
|
682
|
+
}],
|
|
683
|
+
children: [
|
|
684
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckRow, {
|
|
685
|
+
prefix: "dsm",
|
|
686
|
+
id: "dsm-enabled",
|
|
687
|
+
label: t("enabled"),
|
|
688
|
+
checked: draft.enabled,
|
|
689
|
+
disabled,
|
|
690
|
+
onEdit: (v) => edit("enabled", v)
|
|
691
|
+
}),
|
|
692
|
+
catalogFailed ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
693
|
+
className: "dsm-banner",
|
|
694
|
+
role: "status",
|
|
695
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("catalogError") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
696
|
+
type: "button",
|
|
697
|
+
className: "dsm-bannerRetry",
|
|
698
|
+
onClick: onRetryCatalog,
|
|
699
|
+
children: t("catalogRetry")
|
|
700
|
+
})]
|
|
701
|
+
}) : null,
|
|
702
|
+
catalog === null && !catalogFailed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
703
|
+
className: "dsm-empty",
|
|
704
|
+
role: "status",
|
|
705
|
+
children: t("catalogLoading")
|
|
706
|
+
}) : null,
|
|
707
|
+
rowNames.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
708
|
+
className: "dsm-empty",
|
|
709
|
+
role: "status",
|
|
710
|
+
children: t("noRows")
|
|
711
|
+
}) : null,
|
|
712
|
+
rowNames.map((name) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowBlock, {
|
|
713
|
+
name,
|
|
714
|
+
row: draft.rows[name] ?? { ...EMPTY_ROW },
|
|
715
|
+
catalog,
|
|
716
|
+
disabled,
|
|
717
|
+
t,
|
|
718
|
+
onEdit: (row) => editRow(name, row)
|
|
719
|
+
}, name)),
|
|
720
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
721
|
+
className: "dsm-hint dsm-rowHint",
|
|
722
|
+
children: providerCount > 0 ? t("rowDesc") : ""
|
|
723
|
+
})
|
|
724
|
+
]
|
|
449
725
|
});
|
|
450
726
|
}
|
|
451
727
|
//#endregion
|
|
452
728
|
//#region src/client/i18n.ts
|
|
453
729
|
/**
|
|
454
730
|
* 配置卡片文案(zh/en)。经 ctx.locale.register 注册、bind 取用,与官方卡片同机制。
|
|
731
|
+
* 公共键(save/discard/unsaved 等)来自 shared 的 common 字典,本文件只维护业务键。
|
|
455
732
|
* @module subagent-model/client/i18n
|
|
456
733
|
*/
|
|
457
734
|
const NS = "dsh-plus-subagent-model";
|
|
458
|
-
const
|
|
735
|
+
const ownZh = {
|
|
459
736
|
title: "子代理模型配置",
|
|
460
737
|
description: "为 subagent / subagent_fork 等子代理单独指定提供商、模型与思考程度,或继承主代理。",
|
|
461
738
|
enabled: "启用插件",
|
|
@@ -477,19 +754,9 @@ window.__ModuleLoader__.load({
|
|
|
477
754
|
catalogRetry: "重试",
|
|
478
755
|
catalogLoading: "目录加载中…",
|
|
479
756
|
unknownValue: "目录外",
|
|
480
|
-
save: "保存",
|
|
481
|
-
saving: "保存中…",
|
|
482
|
-
discard: "放弃",
|
|
483
|
-
unsaved: "未保存",
|
|
484
|
-
saveFailed: "保存失败:",
|
|
485
|
-
loading: "加载中…",
|
|
486
|
-
unavailable: "配置服务不可用。",
|
|
487
|
-
readOnly: "当前部署无 settings provider,配置为只读;请编辑 settings.yaml。",
|
|
488
|
-
expand: "展开",
|
|
489
|
-
collapse: "收起",
|
|
490
757
|
invalidModel: "模型不能脱离提供商单独配置。"
|
|
491
758
|
};
|
|
492
|
-
const
|
|
759
|
+
const ownEn = {
|
|
493
760
|
title: "Subagent model config",
|
|
494
761
|
description: "Pick a provider, model, and thinking effort for subagent / subagent_fork children, or inherit from the main agent.",
|
|
495
762
|
enabled: "Enable plugin",
|
|
@@ -511,80 +778,35 @@ window.__ModuleLoader__.load({
|
|
|
511
778
|
catalogRetry: "Retry",
|
|
512
779
|
catalogLoading: "Loading catalog…",
|
|
513
780
|
unknownValue: "outside catalog",
|
|
514
|
-
save: "Save",
|
|
515
|
-
saving: "Saving…",
|
|
516
|
-
discard: "Discard",
|
|
517
|
-
unsaved: "Unsaved",
|
|
518
|
-
saveFailed: "Save failed: ",
|
|
519
|
-
loading: "Loading…",
|
|
520
|
-
unavailable: "Configuration service unavailable.",
|
|
521
|
-
readOnly: "No settings provider in this deployment; edit settings.yaml instead.",
|
|
522
|
-
expand: "Expand",
|
|
523
|
-
collapse: "Collapse",
|
|
524
781
|
invalidModel: "Model cannot be set without a provider."
|
|
525
782
|
};
|
|
783
|
+
const zh = mergeDict(commonZh, ownZh);
|
|
784
|
+
const en = mergeDict(commonEn, ownEn);
|
|
526
785
|
//#endregion
|
|
527
786
|
//#region src/client/styles.ts
|
|
528
787
|
/**
|
|
529
|
-
*
|
|
530
|
-
*
|
|
788
|
+
* 配置卡片样式:基础规则走 @dsh-plus/shared/client 的 cardCss('dsm'),
|
|
789
|
+
* 本文件只补插件特有规则(provider 行块 / banner / 目录空态)。
|
|
790
|
+
* 沿用官方 data-plugin / data-plugin-css 约定(HMR 据此卸载)。
|
|
531
791
|
* @module subagent-model/client/styles
|
|
532
792
|
*/
|
|
533
793
|
const PLUGIN_ID = "@dsh-plus/subagent-model";
|
|
534
|
-
const
|
|
535
|
-
const cardCss = `
|
|
536
|
-
.dsm-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}
|
|
537
|
-
.dsm-card:hover{border-color:var(--dsw-alias-label-dimmed)}
|
|
538
|
-
.dsm-cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}
|
|
539
|
-
.dsm-header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}
|
|
540
|
-
.dsm-header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}
|
|
541
|
-
.dsm-headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}
|
|
542
|
-
.dsm-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}
|
|
543
|
-
.dsm-description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}
|
|
544
|
-
.dsm-chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s;font-size:12px}
|
|
545
|
-
.dsm-chevronOpen{transform:rotate(180deg)}
|
|
546
|
-
.dsm-pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}
|
|
547
|
-
.dsm-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}
|
|
548
|
-
.dsm-field{flex-direction:column;gap:6px;padding:12px 0;display:flex}
|
|
549
|
-
.dsm-field+.dsm-field{border-top:1px solid var(--dsw-alias-border-l2)}
|
|
550
|
-
.dsm-head{align-items:center;gap:8px;display:flex}
|
|
551
|
-
.dsm-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}
|
|
552
|
-
.dsm-select{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;min-width:0;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 8px;font-size:13px}
|
|
553
|
-
.dsm-select:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}
|
|
554
|
-
.dsm-select:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}
|
|
555
|
-
.dsm-hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}
|
|
556
|
-
.dsm-invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}
|
|
557
|
-
.dsm-checkRow{align-items:center;gap:8px;display:flex;padding:6px 0}
|
|
558
|
-
.dsm-checkRow input{accent-color:var(--dsw-alias-brand-primary)}
|
|
559
|
-
.dsm-checkRow label{color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.5;cursor:pointer}
|
|
794
|
+
const cardCssAll = cardCss("dsm", `
|
|
560
795
|
.dsm-row{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:10px;margin:12px 0 0;padding:4px 12px 10px}
|
|
561
796
|
.dsm-rowHead{align-items:center;gap:8px;padding:8px 0 2px;display:flex}
|
|
562
797
|
.dsm-rowName{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;font-weight:600;line-height:1.5}
|
|
563
798
|
.dsm-rowHint{color:var(--dsw-alias-label-tertiary);margin:0 0 4px;font-size:12px;line-height:1.5}
|
|
564
|
-
.dsm-groupLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600;padding:12px 0 2px;margin:0}
|
|
565
|
-
.dsm-status{min-width:0;color:var(--dsw-alias-label-secondary);flex:1;margin:0;font-size:12px;line-height:1.5}
|
|
566
|
-
.dsm-statusError{color:var(--dsw-alias-label-error)}
|
|
567
799
|
.dsm-banner{background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary);justify-content:space-between;align-items:center;gap:8px;border-radius:8px;margin-top:12px;padding:7px 8px;font-size:12px;line-height:18px;display:flex}
|
|
568
800
|
.dsm-bannerRetry{color:inherit;font:inherit;cursor:pointer;background:0 0;border:none;flex:none;padding:0;font-weight:600}
|
|
569
801
|
.dsm-empty{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}
|
|
570
|
-
|
|
571
|
-
.dsm-
|
|
572
|
-
.dsm-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
.dsm-btn:disabled{opacity:.4;cursor:default}
|
|
576
|
-
.dsm-btn:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}
|
|
577
|
-
`;
|
|
802
|
+
@media (max-width:767px){
|
|
803
|
+
.dsm-row{padding:2px 8px 8px}
|
|
804
|
+
.dsm-rowHead{flex-wrap:wrap}
|
|
805
|
+
}
|
|
806
|
+
`);
|
|
578
807
|
/** 幂等注入样式标签;返回标签(已存在或环境无 document 时为 null)。 */
|
|
579
808
|
function injectStyle() {
|
|
580
|
-
|
|
581
|
-
if (document.querySelector(`style[data-plugin-css=${JSON.stringify(STYLE_TAG_ID)}]`) !== null) return null;
|
|
582
|
-
const tag = document.createElement("style");
|
|
583
|
-
tag.dataset.plugin = PLUGIN_ID;
|
|
584
|
-
tag.dataset.pluginCss = STYLE_TAG_ID;
|
|
585
|
-
tag.textContent = cardCss;
|
|
586
|
-
document.head.appendChild(tag);
|
|
587
|
-
return tag;
|
|
809
|
+
return injectCardStyle(PLUGIN_ID, cardCssAll);
|
|
588
810
|
}
|
|
589
811
|
//#endregion
|
|
590
812
|
//#region src/client/client.ts
|
|
@@ -596,75 +818,6 @@ window.__ModuleLoader__.load({
|
|
|
596
818
|
"connection",
|
|
597
819
|
"remote"
|
|
598
820
|
];
|
|
599
|
-
/**
|
|
600
|
-
* api 直连实现的命名空间 scope(官方配置页 tab 同款模式)。
|
|
601
|
-
* 读:describe 取命名空间视图(脱敏 value/revision + 顶层 writable);
|
|
602
|
-
* 刷新:remote `settings/document-updated`(按命名空间过滤)与
|
|
603
|
-
* `connection/reset`;generation 防旧读覆盖新发布。任何页面 origin
|
|
604
|
-
* (loopback 或 tailnet 信任域名)下行为一致。
|
|
605
|
-
*/
|
|
606
|
-
function createApiScope(api, ns, c) {
|
|
607
|
-
let state = {
|
|
608
|
-
status: "loading",
|
|
609
|
-
value: void 0,
|
|
610
|
-
revision: void 0,
|
|
611
|
-
writable: false
|
|
612
|
-
};
|
|
613
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
614
|
-
let generation = 0;
|
|
615
|
-
const publish = (next) => {
|
|
616
|
-
state = next;
|
|
617
|
-
for (const listener of [...listeners]) listener();
|
|
618
|
-
};
|
|
619
|
-
const load = async () => {
|
|
620
|
-
const gen = ++generation;
|
|
621
|
-
let response;
|
|
622
|
-
try {
|
|
623
|
-
response = await api.describe({});
|
|
624
|
-
} catch {
|
|
625
|
-
return;
|
|
626
|
-
}
|
|
627
|
-
if (gen !== generation || !response.result.ok) return;
|
|
628
|
-
const writable = response.result.value?.writable ?? false;
|
|
629
|
-
const view = response.result.value?.namespaces.find((candidate) => candidate.ns === ns);
|
|
630
|
-
if (view === void 0) {
|
|
631
|
-
publish({
|
|
632
|
-
status: "unavailable",
|
|
633
|
-
value: void 0,
|
|
634
|
-
revision: void 0,
|
|
635
|
-
writable
|
|
636
|
-
});
|
|
637
|
-
return;
|
|
638
|
-
}
|
|
639
|
-
publish({
|
|
640
|
-
status: "ready",
|
|
641
|
-
value: view.value,
|
|
642
|
-
revision: view.revision,
|
|
643
|
-
writable
|
|
644
|
-
});
|
|
645
|
-
};
|
|
646
|
-
const refresh = (namespace) => {
|
|
647
|
-
if (namespace !== void 0 && namespace !== ns) return;
|
|
648
|
-
load();
|
|
649
|
-
};
|
|
650
|
-
const disposers = [c.get("remote").$on("settings/document-updated", refresh), c.on("connection/reset", () => {
|
|
651
|
-
load();
|
|
652
|
-
})];
|
|
653
|
-
c.effect(() => () => {
|
|
654
|
-
for (const dispose of disposers) dispose();
|
|
655
|
-
}, "subagent-model: settings scope");
|
|
656
|
-
load();
|
|
657
|
-
return {
|
|
658
|
-
getSnapshot: () => state,
|
|
659
|
-
subscribe: (listener) => {
|
|
660
|
-
listeners.add(listener);
|
|
661
|
-
return () => {
|
|
662
|
-
listeners.delete(listener);
|
|
663
|
-
};
|
|
664
|
-
},
|
|
665
|
-
load
|
|
666
|
-
};
|
|
667
|
-
}
|
|
668
821
|
function apply(ctx) {
|
|
669
822
|
const c = ctx;
|
|
670
823
|
const tag = injectStyle();
|
|
@@ -676,7 +829,7 @@ window.__ModuleLoader__.load({
|
|
|
676
829
|
en
|
|
677
830
|
}), "subagent-model: locale");
|
|
678
831
|
const api = c.get("connection").api.settings;
|
|
679
|
-
const scope = createApiScope(api, SETTINGS_NS, c);
|
|
832
|
+
const scope = createApiScope(api, SETTINGS_NS, c, "subagent-model: settings scope");
|
|
680
833
|
c.slots.inject("settings.plugin.item", () => c.slots.register({
|
|
681
834
|
name: "settings.plugin.item",
|
|
682
835
|
key: SETTINGS_NS,
|