@dsh-plus/llm-pi 0.1.11 → 0.1.12
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 +526 -336
- package/package.json +3 -2
- package/src/client/card.tsx +112 -134
- package/src/client/client.ts +5 -82
- package/src/client/fields.tsx +24 -42
- package/src/client/i18n.ts +7 -20
- package/src/client/styles.ts +26 -50
- package/src/client/views/models.tsx +4 -2
- package/src/client/views/provider-fields.tsx +4 -2
- package/src/client/scope.ts +0 -59
package/lib/client.js
CHANGED
|
@@ -4,8 +4,338 @@ 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/card.tsx
|
|
10
|
+
const IDLE_STATUS = {
|
|
11
|
+
kind: "idle",
|
|
12
|
+
text: ""
|
|
13
|
+
};
|
|
14
|
+
function CardChrome(props) {
|
|
15
|
+
const p = props.prefix;
|
|
16
|
+
const badge = props.statusBadge;
|
|
17
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
18
|
+
className: `${p}-card${props.open ? ` ${p}-cardOpen` : ""}`,
|
|
19
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
20
|
+
type: "button",
|
|
21
|
+
className: `${p}-header`,
|
|
22
|
+
"aria-expanded": props.open,
|
|
23
|
+
"aria-label": props.title,
|
|
24
|
+
onClick: () => props.onToggle(!props.open),
|
|
25
|
+
children: [
|
|
26
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
27
|
+
className: `${p}-headText`,
|
|
28
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
29
|
+
className: `${p}-name`,
|
|
30
|
+
children: props.title
|
|
31
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
32
|
+
className: `${p}-description`,
|
|
33
|
+
children: props.description
|
|
34
|
+
})]
|
|
35
|
+
}),
|
|
36
|
+
badge !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
37
|
+
className: `${p}-statusBadge ${badge.on ? `${p}-statusOn` : `${p}-statusOff`}`,
|
|
38
|
+
children: badge.text
|
|
39
|
+
}) : null,
|
|
40
|
+
props.dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
41
|
+
className: `${p}-pending`,
|
|
42
|
+
children: props.dirtyLabel
|
|
43
|
+
}) : null,
|
|
44
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
45
|
+
className: `${p}-chevron${props.open ? ` ${p}-chevronOpen` : ""}`,
|
|
46
|
+
children: "▾"
|
|
47
|
+
})
|
|
48
|
+
]
|
|
49
|
+
}), props.open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
50
|
+
className: `${p}-body`,
|
|
51
|
+
children: [
|
|
52
|
+
props.readOnlyNotice !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
53
|
+
className: `${p}-readOnly`,
|
|
54
|
+
role: "status",
|
|
55
|
+
children: props.readOnlyNotice
|
|
56
|
+
}) : null,
|
|
57
|
+
props.children,
|
|
58
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
59
|
+
className: `${p}-footer`,
|
|
60
|
+
children: [props.status !== void 0 && props.status.kind !== "idle" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
61
|
+
className: `${p}-status${props.status.kind === "error" ? ` ${p}-statusError` : ""}`,
|
|
62
|
+
role: "status",
|
|
63
|
+
children: props.status.text
|
|
64
|
+
}) : null, props.actions.map((action) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
65
|
+
type: "button",
|
|
66
|
+
className: `${p}-btn ${action.variant === "primary" ? `${p}-btnPrimary` : `${p}-btnGhost`}`,
|
|
67
|
+
disabled: action.disabled === true,
|
|
68
|
+
onClick: action.onClick,
|
|
69
|
+
children: action.label
|
|
70
|
+
}, action.key))]
|
|
71
|
+
})
|
|
72
|
+
]
|
|
73
|
+
}) : null]
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region ../shared/src/client/fields.tsx
|
|
78
|
+
function TextField$1(props) {
|
|
79
|
+
const p = props.prefix ?? "dshp";
|
|
80
|
+
const invalid = props.invalid === true;
|
|
81
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
82
|
+
className: `${p}-field`,
|
|
83
|
+
children: [
|
|
84
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
85
|
+
className: `${p}-head`,
|
|
86
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
87
|
+
className: `${p}-label`,
|
|
88
|
+
htmlFor: props.id,
|
|
89
|
+
children: props.label
|
|
90
|
+
}), props.badge !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
91
|
+
className: `${p}-badge ${props.badge.set ? `${p}-badgeSet` : `${p}-badgeUnset`}`,
|
|
92
|
+
children: props.badge.text
|
|
93
|
+
}) : null]
|
|
94
|
+
}),
|
|
95
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
96
|
+
id: props.id,
|
|
97
|
+
className: `${p}-input${invalid ? ` ${p}-inputInvalid` : ""}`,
|
|
98
|
+
type: props.password === true ? "password" : "text",
|
|
99
|
+
inputMode: props.numeric === true ? "numeric" : void 0,
|
|
100
|
+
autoComplete: props.password === true ? "off" : void 0,
|
|
101
|
+
"aria-invalid": invalid || void 0,
|
|
102
|
+
value: props.value,
|
|
103
|
+
disabled: props.disabled === true,
|
|
104
|
+
onChange: (event) => props.onEdit(event.target.value)
|
|
105
|
+
}),
|
|
106
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
107
|
+
className: invalid ? `${p}-invalid` : `${p}-hint`,
|
|
108
|
+
children: invalid ? props.invalidLabel ?? "" : props.hint ?? ""
|
|
109
|
+
})
|
|
110
|
+
]
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function CheckRow(props) {
|
|
114
|
+
const p = props.prefix ?? "dshp";
|
|
115
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
116
|
+
className: `${p}-checkRow`,
|
|
117
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
118
|
+
id: props.id,
|
|
119
|
+
type: "checkbox",
|
|
120
|
+
checked: props.checked,
|
|
121
|
+
disabled: props.disabled === true,
|
|
122
|
+
onChange: (event) => props.onEdit(event.target.checked)
|
|
123
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
124
|
+
htmlFor: props.id,
|
|
125
|
+
children: props.label
|
|
126
|
+
})]
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
//#region ../shared/src/client/i18n.ts
|
|
131
|
+
/** 卡片通用文案(save/discard/unsaved/loading 等四插件共有键)。 */
|
|
132
|
+
const commonZh = {
|
|
133
|
+
save: "保存",
|
|
134
|
+
saving: "保存中…",
|
|
135
|
+
discard: "放弃",
|
|
136
|
+
unsaved: "未保存",
|
|
137
|
+
loading: "加载中…",
|
|
138
|
+
unavailable: "配置服务不可用。",
|
|
139
|
+
readOnly: "当前部署无 settings provider,配置为只读;请编辑 settings.yaml。",
|
|
140
|
+
invalidNumber: "请输入正整数。",
|
|
141
|
+
collapse: "收起",
|
|
142
|
+
expand: "展开",
|
|
143
|
+
enabledOn: "已启用",
|
|
144
|
+
enabledOff: "未启用",
|
|
145
|
+
saveFailed: "保存失败,请检查填写内容。",
|
|
146
|
+
confirm: "确认",
|
|
147
|
+
cancel: "取消"
|
|
148
|
+
};
|
|
149
|
+
const commonEn = {
|
|
150
|
+
save: "Save",
|
|
151
|
+
saving: "Saving…",
|
|
152
|
+
discard: "Discard",
|
|
153
|
+
unsaved: "Unsaved",
|
|
154
|
+
loading: "Loading…",
|
|
155
|
+
unavailable: "Settings service unavailable.",
|
|
156
|
+
readOnly: "No settings provider in this deployment; config is read-only. Edit settings.yaml.",
|
|
157
|
+
invalidNumber: "Enter a positive integer.",
|
|
158
|
+
collapse: "Collapse",
|
|
159
|
+
expand: "Expand",
|
|
160
|
+
enabledOn: "On",
|
|
161
|
+
enabledOff: "Off",
|
|
162
|
+
saveFailed: "Save failed; check the fields.",
|
|
163
|
+
confirm: "Confirm",
|
|
164
|
+
cancel: "Cancel"
|
|
165
|
+
};
|
|
166
|
+
/** own 覆盖同键合并(返回新对象,入参不可变)。 */
|
|
167
|
+
function mergeDict(base, own) {
|
|
168
|
+
return {
|
|
169
|
+
...base,
|
|
170
|
+
...own
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
//#endregion
|
|
174
|
+
//#region ../shared/src/client/scope.ts
|
|
175
|
+
/**
|
|
176
|
+
* api 直连实现的命名空间 scope(官方配置页 tab 同款模式)。
|
|
177
|
+
* 读:describe 取命名空间视图(脱敏 value/revision + 顶层 writable);
|
|
178
|
+
* 刷新:remote `settings/document-updated`(按命名空间过滤)与
|
|
179
|
+
* `connection/reset`;generation 防旧读覆盖新发布。任何页面 origin
|
|
180
|
+
* (loopback 或 tailnet 信任域名)下行为一致。
|
|
181
|
+
* effect 归属调用方 fiber(label 需含插件名以便诊断)。
|
|
182
|
+
*/
|
|
183
|
+
function createApiScope(api, ns, c, label) {
|
|
184
|
+
let state = {
|
|
185
|
+
status: "loading",
|
|
186
|
+
value: void 0,
|
|
187
|
+
revision: void 0,
|
|
188
|
+
writable: false
|
|
189
|
+
};
|
|
190
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
191
|
+
let generation = 0;
|
|
192
|
+
const publish = (next) => {
|
|
193
|
+
state = next;
|
|
194
|
+
for (const listener of [...listeners]) listener();
|
|
195
|
+
};
|
|
196
|
+
const load = async () => {
|
|
197
|
+
const gen = ++generation;
|
|
198
|
+
let response;
|
|
199
|
+
try {
|
|
200
|
+
response = await api.describe({});
|
|
201
|
+
} catch {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (gen !== generation || !response.result.ok) return;
|
|
205
|
+
const writable = response.result.value?.writable ?? false;
|
|
206
|
+
const view = response.result.value?.namespaces.find((candidate) => candidate.ns === ns);
|
|
207
|
+
if (view === void 0) {
|
|
208
|
+
publish({
|
|
209
|
+
status: "unavailable",
|
|
210
|
+
value: void 0,
|
|
211
|
+
revision: void 0,
|
|
212
|
+
writable
|
|
213
|
+
});
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
publish({
|
|
217
|
+
status: "ready",
|
|
218
|
+
value: view.value,
|
|
219
|
+
revision: view.revision,
|
|
220
|
+
writable
|
|
221
|
+
});
|
|
222
|
+
};
|
|
223
|
+
const refresh = (namespace) => {
|
|
224
|
+
if (namespace !== void 0 && namespace !== ns) return;
|
|
225
|
+
load();
|
|
226
|
+
};
|
|
227
|
+
const disposers = [c.get("remote").$on("settings/document-updated", refresh), c.on("connection/reset", () => {
|
|
228
|
+
load();
|
|
229
|
+
})];
|
|
230
|
+
c.effect(() => () => {
|
|
231
|
+
for (const dispose of disposers) dispose();
|
|
232
|
+
}, label);
|
|
233
|
+
load();
|
|
234
|
+
return {
|
|
235
|
+
getSnapshot: () => state,
|
|
236
|
+
subscribe: (listener) => {
|
|
237
|
+
listeners.add(listener);
|
|
238
|
+
return () => {
|
|
239
|
+
listeners.delete(listener);
|
|
240
|
+
};
|
|
241
|
+
},
|
|
242
|
+
load
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
//#endregion
|
|
246
|
+
//#region ../shared/src/client/styles.ts
|
|
247
|
+
/**
|
|
248
|
+
* 配置卡片样式工厂(各插件 client 的公共收编版)。
|
|
249
|
+
* 视觉规则与迁移前逐字一致(类名前缀换为各插件自身,DOM 类名不变),
|
|
250
|
+
* 另补移动端与可达性增强(767px 断点 + pointer:coarse,对齐 ui-mobile-fit 约定):
|
|
251
|
+
* - 窄屏 input/textarea 16px(防 iOS 聚焦缩放)、可点目标 ≥44px;
|
|
252
|
+
* - footer 按钮等宽换行、状态文字独占一行;
|
|
253
|
+
* - 展开态 body 底部 sticky 保存栏(长表单滚动中始终可达)。
|
|
254
|
+
* 沿用官方 data-plugin / data-plugin-css 约定(HMR 据此卸载),不覆盖上游选择器。
|
|
255
|
+
* @module @dsh-plus/shared/client/styles
|
|
256
|
+
*/
|
|
257
|
+
/** 生成全套卡片 CSS;prefix 为插件类名前缀(如 'dne'),extra 为插件特有规则。 */
|
|
258
|
+
function cardCss(prefix, extra = "") {
|
|
259
|
+
return `
|
|
260
|
+
.${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}
|
|
261
|
+
.${prefix}-card:hover{border-color:var(--dsw-alias-label-dimmed)}
|
|
262
|
+
.${prefix}-cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}
|
|
263
|
+
.${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}
|
|
264
|
+
.${prefix}-header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}
|
|
265
|
+
.${prefix}-headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}
|
|
266
|
+
.${prefix}-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}
|
|
267
|
+
.${prefix}-description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}
|
|
268
|
+
.${prefix}-chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s;font-size:12px}
|
|
269
|
+
.${prefix}-chevronOpen{transform:rotate(180deg)}
|
|
270
|
+
.${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}
|
|
271
|
+
.${prefix}-statusBadge{white-space:nowrap;border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}
|
|
272
|
+
.${prefix}-statusOn{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary)}
|
|
273
|
+
.${prefix}-statusOff{color:var(--dsw-alias-label-tertiary)}
|
|
274
|
+
.${prefix}-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}
|
|
275
|
+
.${prefix}-field{flex-direction:column;gap:6px;padding:12px 0;display:flex}
|
|
276
|
+
.${prefix}-field+.${prefix}-field{border-top:1px solid var(--dsw-alias-border-l2)}
|
|
277
|
+
.${prefix}-head{align-items:center;gap:8px;display:flex}
|
|
278
|
+
.${prefix}-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}
|
|
279
|
+
.${prefix}-badge{white-space:nowrap;border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}
|
|
280
|
+
.${prefix}-badgeSet{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary)}
|
|
281
|
+
.${prefix}-badgeUnset{color:var(--dsw-alias-label-tertiary)}
|
|
282
|
+
.${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}
|
|
283
|
+
.${prefix}-input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}
|
|
284
|
+
.${prefix}-input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}
|
|
285
|
+
.${prefix}-inputInvalid{border-color:var(--dsw-alias-label-error)}
|
|
286
|
+
.${prefix}-select{appearance:none;background-image:linear-gradient(45deg,transparent 50%,var(--dsw-alias-label-tertiary) 50%),linear-gradient(135deg,var(--dsw-alias-label-tertiary) 50%,transparent 50%);background-position:calc(100% - 16px) 50%,calc(100% - 11px) 50%;background-size:5px 5px;background-repeat:no-repeat;padding-right:28px}
|
|
287
|
+
.${prefix}-hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}
|
|
288
|
+
.${prefix}-invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}
|
|
289
|
+
.${prefix}-checkRow{align-items:center;gap:8px;display:flex;padding:6px 0}
|
|
290
|
+
.${prefix}-checkRow input{accent-color:var(--dsw-alias-brand-primary)}
|
|
291
|
+
.${prefix}-checkRow label{color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.5;cursor:pointer}
|
|
292
|
+
.${prefix}-groupLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600;padding:12px 0 2px;margin:0}
|
|
293
|
+
.${prefix}-readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}
|
|
294
|
+
.${prefix}-warn{color:var(--dsw-alias-label-error);margin:12px 0 0;font-size:12px;line-height:1.5}
|
|
295
|
+
.${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}
|
|
296
|
+
.${prefix}-status{min-width:0;color:var(--dsw-alias-label-secondary);flex:1;margin:0;font-size:12px;line-height:1.5}
|
|
297
|
+
.${prefix}-statusError{color:var(--dsw-alias-label-error)}
|
|
298
|
+
.${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}
|
|
299
|
+
.${prefix}-btnGhost{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}
|
|
300
|
+
.${prefix}-btnGhost:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}
|
|
301
|
+
.${prefix}-btnPrimary{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}
|
|
302
|
+
.${prefix}-btn:disabled{opacity:.4;cursor:default}
|
|
303
|
+
.${prefix}-btn:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}
|
|
304
|
+
@media (max-width:767px){
|
|
305
|
+
.${prefix}-body{margin:0 10px}
|
|
306
|
+
.${prefix}-header{padding:12px 12px}
|
|
307
|
+
.${prefix}-input,.${prefix}-select{font-size:16px;height:40px}
|
|
308
|
+
.${prefix}-checkRow{min-height:44px;padding:8px 0}
|
|
309
|
+
.${prefix}-checkRow input{width:18px;height:18px;flex:none}
|
|
310
|
+
.${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}
|
|
311
|
+
.${prefix}-status{flex-basis:100%;flex:none}
|
|
312
|
+
.${prefix}-btn{min-height:44px;flex:1;min-width:96px}
|
|
313
|
+
.${prefix}-statusBadge{display:none}
|
|
314
|
+
}
|
|
315
|
+
@media (pointer:coarse){
|
|
316
|
+
.${prefix}-btn{min-height:44px}
|
|
317
|
+
.${prefix}-checkRow{min-height:44px}
|
|
318
|
+
.${prefix}-input,.${prefix}-select{height:40px}
|
|
319
|
+
}
|
|
320
|
+
${extra}
|
|
321
|
+
`;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* 幂等注入样式标签(官方 data-plugin-css 约定);返回新标签,
|
|
325
|
+
* 已存在或环境无 document 时返回 null(调用方 effect 卸载用)。
|
|
326
|
+
*/
|
|
327
|
+
function injectCardStyle(pluginId, css) {
|
|
328
|
+
if (typeof document === "undefined") return null;
|
|
329
|
+
const tagId = `${pluginId}/card.css`;
|
|
330
|
+
if (document.querySelector(`style[data-plugin-css=${JSON.stringify(tagId)}]`) !== null) return null;
|
|
331
|
+
const tag = document.createElement("style");
|
|
332
|
+
tag.dataset.plugin = pluginId;
|
|
333
|
+
tag.dataset.pluginCss = tagId;
|
|
334
|
+
tag.textContent = css;
|
|
335
|
+
document.head.appendChild(tag);
|
|
336
|
+
return tag;
|
|
337
|
+
}
|
|
338
|
+
//#endregion
|
|
9
339
|
//#region src/ns.ts
|
|
10
340
|
/**
|
|
11
341
|
* settings 命名空间字面量(纯常量,零依赖,浏览器半可安全引入)。
|
|
@@ -318,8 +648,9 @@ window.__ModuleLoader__.load({
|
|
|
318
648
|
//#endregion
|
|
319
649
|
//#region src/client/fields.tsx
|
|
320
650
|
/**
|
|
321
|
-
*
|
|
322
|
-
*
|
|
651
|
+
* llm-pi 特有卡片控件:可折叠小节、键值对编辑、JSON 文本框、带 datalist 的
|
|
652
|
+
* 文本字段。通用控件(TextField/CheckRow)走 @dsh-plus/shared/client;
|
|
653
|
+
* 本文件的 SelectField 是 llm-pi 的窄变体(readonly string[] 选项 + 未设置项)。
|
|
323
654
|
* @module llm-pi/client/fields
|
|
324
655
|
*/
|
|
325
656
|
function CollapseSection(props) {
|
|
@@ -376,21 +707,6 @@ window.__ModuleLoader__.load({
|
|
|
376
707
|
]
|
|
377
708
|
});
|
|
378
709
|
}
|
|
379
|
-
function CheckRow(props) {
|
|
380
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
381
|
-
className: "lpc-checkRow",
|
|
382
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
383
|
-
id: props.id,
|
|
384
|
-
type: "checkbox",
|
|
385
|
-
checked: props.checked,
|
|
386
|
-
disabled: props.disabled === true,
|
|
387
|
-
onChange: (event) => props.onEdit(event.target.checked)
|
|
388
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
389
|
-
htmlFor: props.id,
|
|
390
|
-
children: props.label
|
|
391
|
-
})]
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
710
|
function SelectField(props) {
|
|
395
711
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
396
712
|
className: `lpc-field${props.wide === true ? " lpc-wide" : ""}`,
|
|
@@ -418,17 +734,8 @@ window.__ModuleLoader__.load({
|
|
|
418
734
|
});
|
|
419
735
|
}
|
|
420
736
|
function KeyValueEditor(props) {
|
|
421
|
-
const update = (index, patch) => {
|
|
422
|
-
props.onEdit(props.pairs.map((pair, i) => i === index ? {
|
|
423
|
-
...pair,
|
|
424
|
-
...patch
|
|
425
|
-
} : pair));
|
|
426
|
-
};
|
|
427
|
-
const remove = (index) => {
|
|
428
|
-
props.onEdit(props.pairs.filter((_, i) => i !== index));
|
|
429
|
-
};
|
|
430
737
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
431
|
-
className: "lpc-field
|
|
738
|
+
className: "lpc-field",
|
|
432
739
|
children: [
|
|
433
740
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
434
741
|
className: "lpc-head",
|
|
@@ -442,29 +749,45 @@ window.__ModuleLoader__.load({
|
|
|
442
749
|
className: "lpc-kvRow",
|
|
443
750
|
children: [
|
|
444
751
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
445
|
-
id: index === 0 ? props.id : void 0,
|
|
446
|
-
className: "lpc-input",
|
|
447
|
-
value: pair.key,
|
|
448
752
|
placeholder: props.keyPlaceholder,
|
|
753
|
+
className: "lpc-input lpc-kvKey",
|
|
754
|
+
type: "text",
|
|
755
|
+
value: pair.key,
|
|
449
756
|
disabled: props.disabled === true,
|
|
450
|
-
onChange: (event) =>
|
|
757
|
+
onChange: (event) => {
|
|
758
|
+
const next = [...props.pairs];
|
|
759
|
+
next[index] = {
|
|
760
|
+
...pair,
|
|
761
|
+
key: event.target.value
|
|
762
|
+
};
|
|
763
|
+
props.onEdit(next);
|
|
764
|
+
}
|
|
451
765
|
}),
|
|
452
766
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
453
|
-
className: "lpc-input",
|
|
454
|
-
value: pair.value,
|
|
455
767
|
placeholder: props.valuePlaceholder,
|
|
768
|
+
className: "lpc-input lpc-kvValue",
|
|
769
|
+
type: "text",
|
|
770
|
+
value: pair.value,
|
|
456
771
|
disabled: props.disabled === true,
|
|
457
|
-
onChange: (event) =>
|
|
772
|
+
onChange: (event) => {
|
|
773
|
+
const next = [...props.pairs];
|
|
774
|
+
next[index] = {
|
|
775
|
+
...pair,
|
|
776
|
+
value: event.target.value
|
|
777
|
+
};
|
|
778
|
+
props.onEdit(next);
|
|
779
|
+
}
|
|
458
780
|
}),
|
|
459
781
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
460
782
|
type: "button",
|
|
461
783
|
className: "lpc-btn lpc-btnGhost lpc-btnSmall",
|
|
462
784
|
disabled: props.disabled === true,
|
|
463
|
-
|
|
464
|
-
|
|
785
|
+
"aria-label": props.removeLabel,
|
|
786
|
+
onClick: () => props.onEdit(props.pairs.filter((_, i) => i !== index)),
|
|
787
|
+
children: "✕"
|
|
465
788
|
})
|
|
466
789
|
]
|
|
467
|
-
}, index)),
|
|
790
|
+
}, `${props.id}-${index}`)),
|
|
468
791
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
469
792
|
className: "lpc-kvAdd",
|
|
470
793
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -940,6 +1263,7 @@ window.__ModuleLoader__.load({
|
|
|
940
1263
|
className: "lpc-label",
|
|
941
1264
|
children: t("input")
|
|
942
1265
|
}), MODALITIES.map((modality) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckRow, {
|
|
1266
|
+
prefix: "lpc",
|
|
943
1267
|
id: `${id}-input-${modality}`,
|
|
944
1268
|
label: modality,
|
|
945
1269
|
checked: model.input[modality],
|
|
@@ -1021,6 +1345,12 @@ window.__ModuleLoader__.load({
|
|
|
1021
1345
|
}
|
|
1022
1346
|
//#endregion
|
|
1023
1347
|
//#region src/client/views/provider-fields.tsx
|
|
1348
|
+
/**
|
|
1349
|
+
* Provider route 的标量字段组:基础字段网格(继承/显示名/端点/凭据/默认容量/
|
|
1350
|
+
* 超时/默认模态/预算)与协议/档位/缓存/传输下拉组。compat、headers、
|
|
1351
|
+
* retryPolicy 与模型目录在 views/providers.tsx 的 route 小节内另行渲染。
|
|
1352
|
+
* @module llm-pi/client/views/provider-fields
|
|
1353
|
+
*/
|
|
1024
1354
|
function ProviderScalarFields(props) {
|
|
1025
1355
|
const { draft, t } = props;
|
|
1026
1356
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1111,6 +1441,7 @@ window.__ModuleLoader__.load({
|
|
|
1111
1441
|
className: "lpc-label",
|
|
1112
1442
|
children: t("defaultInput")
|
|
1113
1443
|
}), MODALITIES.map((modality) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckRow, {
|
|
1444
|
+
prefix: "lpc",
|
|
1114
1445
|
id: `${props.id}-input-${modality}`,
|
|
1115
1446
|
label: modality,
|
|
1116
1447
|
checked: draft.input[modality],
|
|
@@ -1368,14 +1699,9 @@ window.__ModuleLoader__.load({
|
|
|
1368
1699
|
* 顶部:enabled / catalogUrl / catalogRefreshHours / 只读状态行(kitSource、
|
|
1369
1700
|
* modelsDevStatus,来自模型目录端点)+ 保存(settings.replace 全量)与
|
|
1370
1701
|
* 错误/成功提示;下方为 providers 路由列表(新增/删除/字段编辑/compat/模型
|
|
1371
|
-
* 目录,见 views
|
|
1372
|
-
* 交互对齐官方卡片与 notify-email:折叠/展开、staged draft、未保存标记。
|
|
1702
|
+
* 目录,见 views/)。外壳与基础控件走 @dsh-plus/shared/client 套件。
|
|
1373
1703
|
* @module llm-pi/client/card
|
|
1374
1704
|
*/
|
|
1375
|
-
const IDLE_STATUS = {
|
|
1376
|
-
kind: "idle",
|
|
1377
|
-
text: ""
|
|
1378
|
-
};
|
|
1379
1705
|
function modelsDevText(status, t) {
|
|
1380
1706
|
if (status === null) return t("modelsDevEmpty");
|
|
1381
1707
|
if (status.error !== null) return `${t("modelsDevError")}${status.error}`;
|
|
@@ -1501,167 +1827,141 @@ window.__ModuleLoader__.load({
|
|
|
1501
1827
|
}).finally(() => setRefreshing(false));
|
|
1502
1828
|
};
|
|
1503
1829
|
const disabled = !snapshot.writable;
|
|
1504
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
disabled
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
}
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
}
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
}),
|
|
1629
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1630
|
-
className: "lpc-footer",
|
|
1631
|
-
children: [
|
|
1632
|
-
status.kind !== "idle" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1633
|
-
className: `lpc-status${status.kind === "error" ? " lpc-statusError" : ""}`,
|
|
1634
|
-
role: "status",
|
|
1635
|
-
children: status.text
|
|
1636
|
-
}) : null,
|
|
1637
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1638
|
-
type: "button",
|
|
1639
|
-
className: "lpc-btn lpc-btnGhost",
|
|
1640
|
-
disabled: !dirty || saving,
|
|
1641
|
-
onClick: onDiscard,
|
|
1642
|
-
children: t("discard")
|
|
1643
|
-
}),
|
|
1644
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1645
|
-
type: "button",
|
|
1646
|
-
className: "lpc-btn lpc-btnPrimary",
|
|
1647
|
-
disabled: !dirty || invalid || saving || disabled,
|
|
1648
|
-
onClick: onSave,
|
|
1649
|
-
children: t(saving ? "saving" : "save")
|
|
1650
|
-
})
|
|
1651
|
-
]
|
|
1652
|
-
})
|
|
1653
|
-
]
|
|
1654
|
-
}) : null]
|
|
1830
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CardChrome, {
|
|
1831
|
+
prefix: "lpc",
|
|
1832
|
+
title: t("title"),
|
|
1833
|
+
description: t("description"),
|
|
1834
|
+
open,
|
|
1835
|
+
onToggle: setOpen,
|
|
1836
|
+
statusBadge: {
|
|
1837
|
+
text: t(draft.enabled ? "enabledOn" : "enabledOff"),
|
|
1838
|
+
on: draft.enabled
|
|
1839
|
+
},
|
|
1840
|
+
dirty,
|
|
1841
|
+
dirtyLabel: t("unsaved"),
|
|
1842
|
+
readOnlyNotice: disabled ? t("readOnly") : void 0,
|
|
1843
|
+
status,
|
|
1844
|
+
actions: [
|
|
1845
|
+
{
|
|
1846
|
+
key: "refresh",
|
|
1847
|
+
label: t(refreshing ? "refreshingCatalog" : "refreshCatalog"),
|
|
1848
|
+
disabled: disabled || refreshing,
|
|
1849
|
+
onClick: onRefreshCatalog
|
|
1850
|
+
},
|
|
1851
|
+
{
|
|
1852
|
+
key: "discard",
|
|
1853
|
+
label: t("discard"),
|
|
1854
|
+
disabled: !dirty || saving,
|
|
1855
|
+
onClick: onDiscard
|
|
1856
|
+
},
|
|
1857
|
+
{
|
|
1858
|
+
key: "save",
|
|
1859
|
+
label: t(saving ? "saving" : "save"),
|
|
1860
|
+
variant: "primary",
|
|
1861
|
+
disabled: !dirty || invalid || saving || disabled,
|
|
1862
|
+
onClick: onSave
|
|
1863
|
+
}
|
|
1864
|
+
],
|
|
1865
|
+
children: [
|
|
1866
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckRow, {
|
|
1867
|
+
prefix: "lpc",
|
|
1868
|
+
id: "lpc-enabled",
|
|
1869
|
+
label: t("enabled"),
|
|
1870
|
+
checked: draft.enabled,
|
|
1871
|
+
disabled,
|
|
1872
|
+
onEdit: (value) => {
|
|
1873
|
+
setDraft({
|
|
1874
|
+
...draft,
|
|
1875
|
+
enabled: value
|
|
1876
|
+
});
|
|
1877
|
+
setStatus(IDLE_STATUS);
|
|
1878
|
+
}
|
|
1879
|
+
}),
|
|
1880
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TextField$1, {
|
|
1881
|
+
prefix: "lpc",
|
|
1882
|
+
id: "lpc-catalogUrl",
|
|
1883
|
+
label: t("catalogUrl"),
|
|
1884
|
+
hint: t("catalogUrlHint"),
|
|
1885
|
+
value: draft.catalogUrl,
|
|
1886
|
+
disabled,
|
|
1887
|
+
onEdit: (value) => {
|
|
1888
|
+
setDraft({
|
|
1889
|
+
...draft,
|
|
1890
|
+
catalogUrl: value
|
|
1891
|
+
});
|
|
1892
|
+
setStatus(IDLE_STATUS);
|
|
1893
|
+
}
|
|
1894
|
+
}),
|
|
1895
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TextField$1, {
|
|
1896
|
+
prefix: "lpc",
|
|
1897
|
+
id: "lpc-catalogRefresh",
|
|
1898
|
+
label: t("catalogRefreshHours"),
|
|
1899
|
+
hint: t("catalogRefreshHoursHint"),
|
|
1900
|
+
value: draft.catalogRefreshHours,
|
|
1901
|
+
numeric: true,
|
|
1902
|
+
disabled,
|
|
1903
|
+
invalid: !numTextOk(draft.catalogRefreshHours),
|
|
1904
|
+
invalidLabel: t("invalidNumber"),
|
|
1905
|
+
onEdit: (value) => {
|
|
1906
|
+
setDraft({
|
|
1907
|
+
...draft,
|
|
1908
|
+
catalogRefreshHours: value
|
|
1909
|
+
});
|
|
1910
|
+
setStatus(IDLE_STATUS);
|
|
1911
|
+
}
|
|
1912
|
+
}),
|
|
1913
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TextField$1, {
|
|
1914
|
+
prefix: "lpc",
|
|
1915
|
+
id: "lpc-catalogProxy",
|
|
1916
|
+
label: t("catalogProxy"),
|
|
1917
|
+
hint: t("catalogProxyHint"),
|
|
1918
|
+
value: draft.catalogProxy,
|
|
1919
|
+
disabled,
|
|
1920
|
+
onEdit: (value) => {
|
|
1921
|
+
setDraft({
|
|
1922
|
+
...draft,
|
|
1923
|
+
catalogProxy: value
|
|
1924
|
+
});
|
|
1925
|
+
setStatus(IDLE_STATUS);
|
|
1926
|
+
}
|
|
1927
|
+
}),
|
|
1928
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
1929
|
+
className: "lpc-statusRow",
|
|
1930
|
+
children: [
|
|
1931
|
+
t("kitSource"),
|
|
1932
|
+
":",
|
|
1933
|
+
kitSource ?? ""
|
|
1934
|
+
]
|
|
1935
|
+
}),
|
|
1936
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1937
|
+
className: "lpc-statusRow",
|
|
1938
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
1939
|
+
t("modelsDevStatus"),
|
|
1940
|
+
":",
|
|
1941
|
+
modelsDevText(modelsDevStatus, t)
|
|
1942
|
+
] })
|
|
1943
|
+
}),
|
|
1944
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProvidersSection, {
|
|
1945
|
+
providers: draft.providers,
|
|
1946
|
+
epoch,
|
|
1947
|
+
disabled,
|
|
1948
|
+
t,
|
|
1949
|
+
onAddRoute,
|
|
1950
|
+
onRemoveRoute,
|
|
1951
|
+
onPatchProvider: setProvider
|
|
1952
|
+
})
|
|
1953
|
+
]
|
|
1655
1954
|
});
|
|
1656
1955
|
}
|
|
1657
1956
|
//#endregion
|
|
1658
1957
|
//#region src/client/i18n.ts
|
|
1659
1958
|
/**
|
|
1660
1959
|
* 配置卡片文案(zh/en)。经 ctx.locale.register 注册、bind 取用,与官方卡片同机制。
|
|
1960
|
+
* 公共键(save/discard/unsaved 等)来自 shared 的 common 字典,本文件只维护业务键。
|
|
1661
1961
|
* @module llm-pi/client/i18n
|
|
1662
1962
|
*/
|
|
1663
1963
|
const NS = "dsh-plus-llm-pi";
|
|
1664
|
-
const
|
|
1964
|
+
const ownZh = {
|
|
1665
1965
|
title: "LLM 路由(llm-pi)",
|
|
1666
1966
|
description: "自定义 LLM 路由:协议、compat、模型目录与 models.dev 目录兜底。",
|
|
1667
1967
|
enabled: "启用插件",
|
|
@@ -1738,19 +2038,10 @@ window.__ModuleLoader__.load({
|
|
|
1738
2038
|
catalogProvider: "候选 provider",
|
|
1739
2039
|
catalogLoading: "目录加载中…",
|
|
1740
2040
|
catalogFailed: "目录加载失败。",
|
|
1741
|
-
save: "保存",
|
|
1742
|
-
saving: "保存中…",
|
|
1743
|
-
discard: "放弃",
|
|
1744
|
-
unsaved: "未保存",
|
|
1745
2041
|
saveOk: "已保存。",
|
|
1746
|
-
saveFailed: "保存失败:"
|
|
1747
|
-
loading: "加载中…",
|
|
1748
|
-
readOnly: "当前部署无 settings provider,配置为只读;请编辑 settings.yaml。",
|
|
1749
|
-
expand: "展开",
|
|
1750
|
-
collapse: "收起",
|
|
1751
|
-
invalidNumber: "请输入有效数字"
|
|
2042
|
+
saveFailed: "保存失败:"
|
|
1752
2043
|
};
|
|
1753
|
-
const
|
|
2044
|
+
const ownEn = {
|
|
1754
2045
|
title: "LLM routes (llm-pi)",
|
|
1755
2046
|
description: "Custom LLM routes: protocol, compat, model catalog and models.dev fallback.",
|
|
1756
2047
|
enabled: "Enable plugin",
|
|
@@ -1827,67 +2118,29 @@ window.__ModuleLoader__.load({
|
|
|
1827
2118
|
catalogProvider: "Candidate provider",
|
|
1828
2119
|
catalogLoading: "Loading catalog…",
|
|
1829
2120
|
catalogFailed: "Failed to load catalog.",
|
|
1830
|
-
save: "Save",
|
|
1831
|
-
saving: "Saving…",
|
|
1832
|
-
discard: "Discard",
|
|
1833
|
-
unsaved: "Unsaved",
|
|
1834
2121
|
saveOk: "Saved.",
|
|
1835
|
-
saveFailed: "Save failed: "
|
|
1836
|
-
loading: "Loading…",
|
|
1837
|
-
readOnly: "No settings provider in this deployment; edit settings.yaml instead.",
|
|
1838
|
-
expand: "Expand",
|
|
1839
|
-
collapse: "Collapse",
|
|
1840
|
-
invalidNumber: "Enter a valid number"
|
|
2122
|
+
saveFailed: "Save failed: "
|
|
1841
2123
|
};
|
|
2124
|
+
const zh = mergeDict(commonZh, ownZh);
|
|
2125
|
+
const en = mergeDict(commonEn, ownEn);
|
|
1842
2126
|
//#endregion
|
|
1843
2127
|
//#region src/client/styles.ts
|
|
1844
2128
|
/**
|
|
1845
|
-
*
|
|
1846
|
-
*
|
|
2129
|
+
* 配置卡片样式:基础规则走 @dsh-plus/shared/client 的 cardCss('lpc'),
|
|
2130
|
+
* 本文件补插件特有规则(route/model 折叠块、键值对行、网格、JSON 文本框)
|
|
2131
|
+
* 及窄屏响应式:views 的双列网格在窄屏转单列、route 头部按钮换行、
|
|
2132
|
+
* 键值对行纵向堆叠、文本框 16px 防 iOS 缩放。
|
|
2133
|
+
* 沿用官方 data-plugin / data-plugin-css 约定(HMR 据此卸载)。
|
|
1847
2134
|
* @module llm-pi/client/styles
|
|
1848
2135
|
*/
|
|
1849
2136
|
const PLUGIN_ID = "@dsh-plus/llm-pi";
|
|
1850
|
-
const
|
|
1851
|
-
|
|
1852
|
-
.lpc-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}
|
|
1853
|
-
.lpc-card:hover{border-color:var(--dsw-alias-label-dimmed)}
|
|
1854
|
-
.lpc-cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}
|
|
1855
|
-
.lpc-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}
|
|
1856
|
-
.lpc-header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}
|
|
1857
|
-
.lpc-headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}
|
|
1858
|
-
.lpc-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}
|
|
1859
|
-
.lpc-description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}
|
|
1860
|
-
.lpc-chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s;font-size:12px}
|
|
1861
|
-
.lpc-chevronOpen{transform:rotate(180deg)}
|
|
1862
|
-
.lpc-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}
|
|
1863
|
-
.lpc-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}
|
|
1864
|
-
.lpc-field{flex-direction:column;gap:6px;padding:12px 0;display:flex;min-width:0}
|
|
1865
|
-
.lpc-field+.lpc-field{border-top:1px solid var(--dsw-alias-border-l2)}
|
|
1866
|
-
.lpc-head{align-items:center;gap:8px;display:flex}
|
|
1867
|
-
.lpc-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}
|
|
1868
|
-
.lpc-input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;width:100%;box-sizing:border-box;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px}
|
|
1869
|
-
.lpc-input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}
|
|
1870
|
-
.lpc-input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}
|
|
1871
|
-
.lpc-inputInvalid{border-color:var(--dsw-alias-label-error)}
|
|
2137
|
+
const cardCssAll = cardCss("lpc", `
|
|
2138
|
+
.lpc-input{width:100%;box-sizing:border-box}
|
|
1872
2139
|
.lpc-select{appearance:none}
|
|
1873
2140
|
.lpc-textarea{height:auto;min-height:72px;padding:8px 12px;line-height:1.5;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;resize:vertical}
|
|
1874
|
-
.lpc-
|
|
1875
|
-
.lpc-invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}
|
|
1876
|
-
.lpc-checkRow{align-items:center;gap:8px;display:flex;padding:3px 0}
|
|
1877
|
-
.lpc-checkRow input{accent-color:var(--dsw-alias-brand-primary)}
|
|
1878
|
-
.lpc-checkRow label{color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.5;cursor:pointer}
|
|
1879
|
-
.lpc-groupLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600;padding:14px 0 2px;margin:0}
|
|
1880
|
-
.lpc-readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}
|
|
2141
|
+
.lpc-checkRow{padding:3px 0}
|
|
1881
2142
|
.lpc-statusRow{color:var(--dsw-alias-label-tertiary);margin:6px 0 0;font-size:12px;line-height:1.6;word-break:break-all}
|
|
1882
|
-
.lpc-
|
|
1883
|
-
.lpc-status{min-width:0;color:var(--dsw-alias-label-secondary);flex:1;margin:0;font-size:12px;line-height:1.5}
|
|
1884
|
-
.lpc-statusError{color:var(--dsw-alias-label-error)}
|
|
1885
|
-
.lpc-btn{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5;flex:none}
|
|
1886
|
-
.lpc-btnGhost{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}
|
|
1887
|
-
.lpc-btnGhost:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}
|
|
1888
|
-
.lpc-btnPrimary{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}
|
|
1889
|
-
.lpc-btn:disabled{opacity:.4;cursor:default}
|
|
1890
|
-
.lpc-btn:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}
|
|
2143
|
+
.lpc-btn{flex:none}
|
|
1891
2144
|
.lpc-btnSmall{padding:2px 10px;font-size:12px}
|
|
1892
2145
|
.lpc-grid{display:grid;grid-template-columns:1fr 1fr;gap:0 16px}
|
|
1893
2146
|
.lpc-gridNested{margin-top:2px}
|
|
@@ -1916,17 +2169,23 @@ window.__ModuleLoader__.load({
|
|
|
1916
2169
|
.lpc-collapseTitle{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600}
|
|
1917
2170
|
.lpc-collapseBody{border-top:1px dashed var(--dsw-alias-border-l2);padding-bottom:6px}
|
|
1918
2171
|
.lpc-refreshBtn{margin-left:8px;vertical-align:middle}
|
|
1919
|
-
|
|
2172
|
+
@media (max-width:767px){
|
|
2173
|
+
.lpc-grid{grid-template-columns:1fr}
|
|
2174
|
+
.lpc-routeHead{flex-wrap:wrap}
|
|
2175
|
+
.lpc-routeKey{white-space:normal;word-break:break-all}
|
|
2176
|
+
.lpc-routeBody{margin:0 8px}
|
|
2177
|
+
.lpc-modelRow{padding:0 8px}
|
|
2178
|
+
.lpc-modelHead{flex-wrap:wrap}
|
|
2179
|
+
.lpc-kvRow{flex-direction:column;align-items:stretch}
|
|
2180
|
+
.lpc-kvRow .lpc-btn{align-self:flex-end}
|
|
2181
|
+
.lpc-textarea{font-size:16px}
|
|
2182
|
+
.lpc-statusRow{display:flex;flex-direction:column;gap:4px}
|
|
2183
|
+
.lpc-catalogBar .lpc-input{flex:1;min-width:140px}
|
|
2184
|
+
}
|
|
2185
|
+
`);
|
|
1920
2186
|
/** 幂等注入样式标签;返回标签(已存在或环境无 document 时为 null)。 */
|
|
1921
2187
|
function injectStyle() {
|
|
1922
|
-
|
|
1923
|
-
if (document.querySelector(`style[data-plugin-css=${JSON.stringify(STYLE_TAG_ID)}]`) !== null) return null;
|
|
1924
|
-
const tag = document.createElement("style");
|
|
1925
|
-
tag.dataset.plugin = PLUGIN_ID;
|
|
1926
|
-
tag.dataset.pluginCss = STYLE_TAG_ID;
|
|
1927
|
-
tag.textContent = cardCss;
|
|
1928
|
-
document.head.appendChild(tag);
|
|
1929
|
-
return tag;
|
|
2188
|
+
return injectCardStyle(PLUGIN_ID, cardCssAll);
|
|
1930
2189
|
}
|
|
1931
2190
|
//#endregion
|
|
1932
2191
|
//#region src/client/client.ts
|
|
@@ -1938,75 +2197,6 @@ window.__ModuleLoader__.load({
|
|
|
1938
2197
|
"connection",
|
|
1939
2198
|
"remote"
|
|
1940
2199
|
];
|
|
1941
|
-
/**
|
|
1942
|
-
* api 直连实现的命名空间 scope(官方配置页 tab 同款模式)。
|
|
1943
|
-
* 读:describe 取命名空间视图(脱敏 value/revision + 顶层 writable);
|
|
1944
|
-
* 刷新:remote `settings/document-updated`(按命名空间过滤)与
|
|
1945
|
-
* `connection/reset`;generation 防旧读覆盖新发布。任何页面 origin
|
|
1946
|
-
* (loopback 或 tailnet 信任域名)下行为一致。
|
|
1947
|
-
*/
|
|
1948
|
-
function createApiScope(api, ns, c) {
|
|
1949
|
-
let state = {
|
|
1950
|
-
status: "loading",
|
|
1951
|
-
value: void 0,
|
|
1952
|
-
revision: void 0,
|
|
1953
|
-
writable: false
|
|
1954
|
-
};
|
|
1955
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
1956
|
-
let generation = 0;
|
|
1957
|
-
const publish = (next) => {
|
|
1958
|
-
state = next;
|
|
1959
|
-
for (const listener of [...listeners]) listener();
|
|
1960
|
-
};
|
|
1961
|
-
const load = async () => {
|
|
1962
|
-
const gen = ++generation;
|
|
1963
|
-
let response;
|
|
1964
|
-
try {
|
|
1965
|
-
response = await api.describe({});
|
|
1966
|
-
} catch {
|
|
1967
|
-
return;
|
|
1968
|
-
}
|
|
1969
|
-
if (gen !== generation || !response.result.ok) return;
|
|
1970
|
-
const writable = response.result.value?.writable ?? false;
|
|
1971
|
-
const view = response.result.value?.namespaces.find((candidate) => candidate.ns === ns);
|
|
1972
|
-
if (view === void 0) {
|
|
1973
|
-
publish({
|
|
1974
|
-
status: "unavailable",
|
|
1975
|
-
value: void 0,
|
|
1976
|
-
revision: void 0,
|
|
1977
|
-
writable
|
|
1978
|
-
});
|
|
1979
|
-
return;
|
|
1980
|
-
}
|
|
1981
|
-
publish({
|
|
1982
|
-
status: "ready",
|
|
1983
|
-
value: view.value,
|
|
1984
|
-
revision: view.revision,
|
|
1985
|
-
writable
|
|
1986
|
-
});
|
|
1987
|
-
};
|
|
1988
|
-
const refresh = (namespace) => {
|
|
1989
|
-
if (namespace !== void 0 && namespace !== ns) return;
|
|
1990
|
-
load();
|
|
1991
|
-
};
|
|
1992
|
-
const disposers = [c.get("remote").$on("settings/document-updated", refresh), c.on("connection/reset", () => {
|
|
1993
|
-
load();
|
|
1994
|
-
})];
|
|
1995
|
-
c.effect(() => () => {
|
|
1996
|
-
for (const dispose of disposers) dispose();
|
|
1997
|
-
}, "llm-pi: settings scope");
|
|
1998
|
-
load();
|
|
1999
|
-
return {
|
|
2000
|
-
getSnapshot: () => state,
|
|
2001
|
-
subscribe: (listener) => {
|
|
2002
|
-
listeners.add(listener);
|
|
2003
|
-
return () => {
|
|
2004
|
-
listeners.delete(listener);
|
|
2005
|
-
};
|
|
2006
|
-
},
|
|
2007
|
-
load
|
|
2008
|
-
};
|
|
2009
|
-
}
|
|
2010
2200
|
function apply(ctx) {
|
|
2011
2201
|
const c = ctx;
|
|
2012
2202
|
const tag = injectStyle();
|
|
@@ -2018,7 +2208,7 @@ window.__ModuleLoader__.load({
|
|
|
2018
2208
|
en
|
|
2019
2209
|
}), "llm-pi: locale");
|
|
2020
2210
|
const api = c.get("connection").api.settings;
|
|
2021
|
-
const scope = createApiScope(api, SETTINGS_NS, c);
|
|
2211
|
+
const scope = createApiScope(api, SETTINGS_NS, c, "llm-pi: settings scope");
|
|
2022
2212
|
c.slots.inject("settings.plugin.item", () => c.slots.register({
|
|
2023
2213
|
name: "settings.plugin.item",
|
|
2024
2214
|
key: SETTINGS_NS,
|