@dsh-plus/subagent-model 0.3.19 → 0.3.21
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 +215 -82
- package/package.json +2 -2
- package/src/client/card.tsx +126 -63
- package/src/client/styles.ts +5 -0
package/lib/client.js
CHANGED
|
@@ -12,9 +12,72 @@ window.__ModuleLoader__.load({
|
|
|
12
12
|
kind: "idle",
|
|
13
13
|
text: ""
|
|
14
14
|
};
|
|
15
|
+
/** 状态行 + 动作按钮(两种形态共用,类名随形态切换度量)。 */
|
|
16
|
+
function CardFoot(props) {
|
|
17
|
+
const p = props.prefix;
|
|
18
|
+
const section = props.variant === "section";
|
|
19
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
20
|
+
className: section ? `${p}-sectionFooter` : `${p}-footer`,
|
|
21
|
+
children: [props.status !== void 0 && props.status.kind !== "idle" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
22
|
+
className: `${p}-status${props.status.kind === "error" ? ` ${p}-statusError` : ""}`,
|
|
23
|
+
role: "status",
|
|
24
|
+
children: props.status.text
|
|
25
|
+
}) : null, props.actions.map((action) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
26
|
+
type: "button",
|
|
27
|
+
className: `${p}-btn ${action.variant === "primary" ? `${p}-btnPrimary` : `${p}-btnGhost`}`,
|
|
28
|
+
disabled: action.disabled === true,
|
|
29
|
+
onClick: action.onClick,
|
|
30
|
+
children: action.label
|
|
31
|
+
}, action.key))]
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/** 只读提示 + 字段区 + 页脚(两种形态共用)。 */
|
|
35
|
+
function CardBody(props) {
|
|
36
|
+
const p = props.prefix;
|
|
37
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
38
|
+
props.readOnlyNotice !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
39
|
+
className: `${p}-readOnly`,
|
|
40
|
+
role: "status",
|
|
41
|
+
children: props.readOnlyNotice
|
|
42
|
+
}) : null,
|
|
43
|
+
props.children,
|
|
44
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardFoot, { ...props })
|
|
45
|
+
] });
|
|
46
|
+
}
|
|
15
47
|
function CardChrome(props) {
|
|
16
48
|
const p = props.prefix;
|
|
17
49
|
const badge = props.statusBadge;
|
|
50
|
+
if (props.variant === "section") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
51
|
+
className: `${p}-section`,
|
|
52
|
+
"aria-label": props.title,
|
|
53
|
+
children: [
|
|
54
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
55
|
+
className: `${p}-sectionHead`,
|
|
56
|
+
children: [
|
|
57
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
58
|
+
className: `${p}-sectionTitle`,
|
|
59
|
+
children: props.title
|
|
60
|
+
}),
|
|
61
|
+
badge !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
62
|
+
className: `${p}-sectionBadge ${badge.on ? `${p}-statusOn` : `${p}-statusOff`}`,
|
|
63
|
+
children: badge.text
|
|
64
|
+
}) : null,
|
|
65
|
+
props.dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
66
|
+
className: `${p}-pending`,
|
|
67
|
+
children: props.dirtyLabel
|
|
68
|
+
}) : null
|
|
69
|
+
]
|
|
70
|
+
}),
|
|
71
|
+
props.description === "" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
72
|
+
className: `${p}-sectionDesc`,
|
|
73
|
+
children: props.description
|
|
74
|
+
}),
|
|
75
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
76
|
+
className: `${p}-sectionBody`,
|
|
77
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardBody, { ...props })
|
|
78
|
+
})
|
|
79
|
+
]
|
|
80
|
+
});
|
|
18
81
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
19
82
|
className: `${p}-card${props.open ? ` ${p}-cardOpen` : ""}`,
|
|
20
83
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
@@ -44,38 +107,44 @@ window.__ModuleLoader__.load({
|
|
|
44
107
|
}) : null,
|
|
45
108
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `${p}-chevron${props.open ? ` ${p}-chevronOpen` : ""}` })
|
|
46
109
|
]
|
|
47
|
-
}), props.open ? /* @__PURE__ */ (0, react_jsx_runtime.
|
|
110
|
+
}), props.open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
48
111
|
className: `${p}-body`,
|
|
49
|
-
children:
|
|
50
|
-
props.readOnlyNotice !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
51
|
-
className: `${p}-readOnly`,
|
|
52
|
-
role: "status",
|
|
53
|
-
children: props.readOnlyNotice
|
|
54
|
-
}) : null,
|
|
55
|
-
props.children,
|
|
56
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
57
|
-
className: `${p}-footer`,
|
|
58
|
-
children: [props.status !== void 0 && props.status.kind !== "idle" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
59
|
-
className: `${p}-status${props.status.kind === "error" ? ` ${p}-statusError` : ""}`,
|
|
60
|
-
role: "status",
|
|
61
|
-
children: props.status.text
|
|
62
|
-
}) : null, props.actions.map((action) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
63
|
-
type: "button",
|
|
64
|
-
className: `${p}-btn ${action.variant === "primary" ? `${p}-btnPrimary` : `${p}-btnGhost`}`,
|
|
65
|
-
disabled: action.disabled === true,
|
|
66
|
-
onClick: action.onClick,
|
|
67
|
-
children: action.label
|
|
68
|
-
}, action.key))]
|
|
69
|
-
})
|
|
70
|
-
]
|
|
112
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardBody, { ...props })
|
|
71
113
|
}) : null]
|
|
72
114
|
});
|
|
73
115
|
}
|
|
116
|
+
/** 取值未到达时的占位:与 CardChrome 同形态,避免宿主页里闪一次卡片外壳。 */
|
|
117
|
+
function CardLoading(props) {
|
|
118
|
+
if (props.variant === "section") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
|
|
119
|
+
className: `${props.prefix}-section`,
|
|
120
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
121
|
+
className: `${props.prefix}-readOnly`,
|
|
122
|
+
role: "status",
|
|
123
|
+
children: props.text
|
|
124
|
+
})
|
|
125
|
+
});
|
|
126
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
|
|
127
|
+
className: `${props.prefix}-card`,
|
|
128
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
129
|
+
className: `${props.prefix}-readOnly`,
|
|
130
|
+
role: "status",
|
|
131
|
+
children: props.text
|
|
132
|
+
})
|
|
133
|
+
});
|
|
134
|
+
}
|
|
74
135
|
//#endregion
|
|
75
136
|
//#region ../shared/src/client/config-slots.ts
|
|
76
137
|
/** dsh-plus 聚合 bundle 的包名(各插件 row 均经其补丁层插入 profile)。 */
|
|
77
138
|
const DSH_PLUS_BUNDLE = "@dsh-plus/bundle-main";
|
|
78
139
|
/**
|
|
140
|
+
* 视图 → 外壳形态:插件页 page 视图渲染在官方页面容器内(宿主已画标题、图标、
|
|
141
|
+
* 面包屑与页面级内边距),再套卡片会形成卡片套卡片并压缩移动端可用宽度,
|
|
142
|
+
* 故用无边框分节;summary 与 legacy 槽位保持卡片形态。
|
|
143
|
+
*/
|
|
144
|
+
function cardVariantFor(view) {
|
|
145
|
+
return view === "page" ? "section" : "card";
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
79
148
|
* 把一张配置卡片同时注册进 legacy 与新版三个槽位。
|
|
80
149
|
* 任一槽位在当前 dsh 版本未声明时对应 inject 挂起等待,版本不命中即 no-op。
|
|
81
150
|
*/
|
|
@@ -101,6 +170,17 @@ window.__ModuleLoader__.load({
|
|
|
101
170
|
}, reg.component));
|
|
102
171
|
}
|
|
103
172
|
//#endregion
|
|
173
|
+
//#region ../shared/src/client/draft.ts
|
|
174
|
+
/**
|
|
175
|
+
* 单命名空间 staged 草稿(简单配置卡片共用):读 scope 快照、播种草稿、
|
|
176
|
+
* 脏判定、保存(revision fencing + 重新装载)与状态行。
|
|
177
|
+
*
|
|
178
|
+
* 语义与官方 staged 表单一致:Host 更新不覆盖在途编辑,只有保存才写回;
|
|
179
|
+
* 保存成功后按 Host 解析值重置草稿。`from`/`patch` 参与 effect 与 useMemo
|
|
180
|
+
* 依赖,必须是模块级稳定函数(不要内联箭头函数)。
|
|
181
|
+
* @module @dsh-plus/shared/client/draft
|
|
182
|
+
*/
|
|
183
|
+
//#endregion
|
|
104
184
|
//#region ../shared/src/client/fields.tsx
|
|
105
185
|
/**
|
|
106
186
|
* 勾选行:自绘圆角扁平开关(switch),对齐官方 WebUI 无原生 checkbox 的
|
|
@@ -302,6 +382,9 @@ window.__ModuleLoader__.load({
|
|
|
302
382
|
* - 窄屏 input/textarea 16px(防 iOS 聚焦缩放)、可点目标 ≥44px;
|
|
303
383
|
* - footer 按钮等宽换行、状态文字独占一行;
|
|
304
384
|
* - 展开态 body 底部 sticky 保存栏(长表单滚动中始终可达)。
|
|
385
|
+
* - 分节形态(-section,插件页 page 视图内嵌宿主页面):无边框/背景、横向零
|
|
386
|
+
* 内边距,页脚不 sticky——官方插件页已提供页面级内边距,再叠一层卡片会
|
|
387
|
+
* 缩窄移动端可用宽度(卡片套卡片)。
|
|
305
388
|
* 沿用官方 data-plugin / data-plugin-css 约定(HMR 据此卸载),不覆盖上游选择器。
|
|
306
389
|
* @module @dsh-plus/shared/client/styles
|
|
307
390
|
*/
|
|
@@ -348,6 +431,15 @@ window.__ModuleLoader__.load({
|
|
|
348
431
|
.${prefix}-checkRow input:checked::after{left:16px;background:var(--dsw-alias-bg-base)}
|
|
349
432
|
.${prefix}-checkRow input:disabled{opacity:.4;cursor:default}
|
|
350
433
|
.${prefix}-checkRow input:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
434
|
+
/* 宿主内嵌分节形态(插件页 page 视图):官方设置页同构——无边框/背景,
|
|
435
|
+
横向零内边距(宽度交给宿主页面容器),小节标题 13px/600,页脚非 sticky。 */
|
|
436
|
+
.${prefix}-section{box-sizing:border-box;flex-direction:column;width:100%;min-width:0;padding:16px 0;display:flex}
|
|
437
|
+
.${prefix}-sectionHead{align-items:center;gap:8px;min-width:0;display:flex}
|
|
438
|
+
.${prefix}-sectionTitle{min-width:0;color:var(--dsw-alias-label-primary);flex:1;margin:0;font-size:13px;font-weight:600;line-height:1.5}
|
|
439
|
+
.${prefix}-sectionBadge{white-space:nowrap;border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}
|
|
440
|
+
.${prefix}-sectionDesc{color:var(--dsw-alias-label-tertiary);margin:4px 0 0;font-size:12px;line-height:1.5}
|
|
441
|
+
.${prefix}-sectionBody{flex-direction:column;min-width:0;display:flex}
|
|
442
|
+
.${prefix}-sectionFooter{align-items:center;gap:8px;padding-top:16px;display:flex;flex-wrap:wrap}
|
|
351
443
|
.${prefix}-groupLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600;padding:12px 0 2px;margin:0}
|
|
352
444
|
.${prefix}-readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}
|
|
353
445
|
.${prefix}-warn{color:var(--dsw-alias-label-error);margin:12px 0 0;font-size:12px;line-height:1.5}
|
|
@@ -369,6 +461,8 @@ window.__ModuleLoader__.load({
|
|
|
369
461
|
.${prefix}-status{flex-basis:100%;flex:none}
|
|
370
462
|
.${prefix}-btn{min-height:44px;flex:1;min-width:96px}
|
|
371
463
|
.${prefix}-statusBadge{display:none}
|
|
464
|
+
.${prefix}-sectionBadge{display:inline-block}
|
|
465
|
+
.${prefix}-sectionFooter{position:static}
|
|
372
466
|
}
|
|
373
467
|
@media (pointer:coarse){
|
|
374
468
|
.${prefix}-btn{min-height:44px}
|
|
@@ -585,16 +679,84 @@ ${extra}
|
|
|
585
679
|
]
|
|
586
680
|
});
|
|
587
681
|
}
|
|
682
|
+
/** 目录状态行:拉取失败给重试入口,拉取中与空行集各给一行提示。 */
|
|
683
|
+
function CatalogStatus(props) {
|
|
684
|
+
if (props.catalogFailed) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
685
|
+
className: "dsm-banner",
|
|
686
|
+
role: "status",
|
|
687
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: props.t("catalogError") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
688
|
+
type: "button",
|
|
689
|
+
className: "dsm-bannerRetry",
|
|
690
|
+
onClick: props.onRetry,
|
|
691
|
+
children: props.t("catalogRetry")
|
|
692
|
+
})]
|
|
693
|
+
});
|
|
694
|
+
if (props.catalog === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
695
|
+
className: "dsm-empty",
|
|
696
|
+
role: "status",
|
|
697
|
+
children: props.t("catalogLoading")
|
|
698
|
+
});
|
|
699
|
+
if (props.rowCount === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
700
|
+
className: "dsm-empty",
|
|
701
|
+
role: "status",
|
|
702
|
+
children: props.t("noRows")
|
|
703
|
+
});
|
|
704
|
+
return null;
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* 子代理 provider 目录(展开后拉取一次;失败保留重试入口,重试即回到未加载态)。
|
|
708
|
+
* 与草稿播种共用同一个 catalog:目录到达后由调用方补缺失的空行。
|
|
709
|
+
*/
|
|
710
|
+
function useCatalog(open) {
|
|
711
|
+
const [catalog, setCatalog] = (0, react.useState)(null);
|
|
712
|
+
const [catalogFailed, setCatalogFailed] = (0, react.useState)(false);
|
|
713
|
+
(0, react.useEffect)(() => {
|
|
714
|
+
if (!open || catalog !== null) return;
|
|
715
|
+
let alive = true;
|
|
716
|
+
fetchCatalog().then((loaded) => {
|
|
717
|
+
if (!alive) return;
|
|
718
|
+
setCatalog(loaded);
|
|
719
|
+
setCatalogFailed(false);
|
|
720
|
+
}).catch(() => {
|
|
721
|
+
if (alive) setCatalogFailed(true);
|
|
722
|
+
});
|
|
723
|
+
return () => {
|
|
724
|
+
alive = false;
|
|
725
|
+
};
|
|
726
|
+
}, [open, catalog]);
|
|
727
|
+
return {
|
|
728
|
+
catalog,
|
|
729
|
+
catalogFailed,
|
|
730
|
+
retry: () => {
|
|
731
|
+
setCatalog(null);
|
|
732
|
+
setCatalogFailed(false);
|
|
733
|
+
}
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
/** 卡片动作:放弃(回到 Host 值)与保存(无效行或只读时禁用)。 */ function cardActions(options) {
|
|
737
|
+
const { t } = options;
|
|
738
|
+
return [{
|
|
739
|
+
key: "discard",
|
|
740
|
+
label: t("discard"),
|
|
741
|
+
disabled: !options.dirty || options.saving,
|
|
742
|
+
onClick: options.onDiscard
|
|
743
|
+
}, {
|
|
744
|
+
key: "save",
|
|
745
|
+
label: t(options.saving ? "saving" : "save"),
|
|
746
|
+
variant: "primary",
|
|
747
|
+
disabled: !options.dirty || options.invalid || options.saving || options.disabled,
|
|
748
|
+
onClick: options.onSave
|
|
749
|
+
}];
|
|
750
|
+
}
|
|
588
751
|
function SubagentModelCard(props) {
|
|
589
752
|
const { t, scope, api } = props;
|
|
590
753
|
const snapshot = (0, react.useSyncExternalStore)((listener) => scope.subscribe(listener), () => scope.getSnapshot());
|
|
591
754
|
const value = snapshot.value;
|
|
592
755
|
const [open, setOpen] = (0, react.useState)(props.view === "page");
|
|
593
756
|
const [draft, setDraft] = (0, react.useState)(null);
|
|
594
|
-
const [catalog, setCatalog] = (0, react.useState)(null);
|
|
595
|
-
const [catalogFailed, setCatalogFailed] = (0, react.useState)(false);
|
|
596
757
|
const [saving, setSaving] = (0, react.useState)(false);
|
|
597
758
|
const [status, setStatus] = (0, react.useState)(IDLE_STATUS);
|
|
759
|
+
const { catalog, catalogFailed, retry: retryCatalog } = useCatalog(open);
|
|
598
760
|
(0, react.useEffect)(() => {
|
|
599
761
|
if (value === void 0) return;
|
|
600
762
|
setDraft((current) => {
|
|
@@ -613,20 +775,6 @@ ${extra}
|
|
|
613
775
|
} : current;
|
|
614
776
|
});
|
|
615
777
|
}, [value, catalog]);
|
|
616
|
-
(0, react.useEffect)(() => {
|
|
617
|
-
if (!open || catalog !== null) return;
|
|
618
|
-
let alive = true;
|
|
619
|
-
fetchCatalog().then((loaded) => {
|
|
620
|
-
if (!alive) return;
|
|
621
|
-
setCatalog(loaded);
|
|
622
|
-
setCatalogFailed(false);
|
|
623
|
-
}).catch(() => {
|
|
624
|
-
if (alive) setCatalogFailed(true);
|
|
625
|
-
});
|
|
626
|
-
return () => {
|
|
627
|
-
alive = false;
|
|
628
|
-
};
|
|
629
|
-
}, [open, catalog]);
|
|
630
778
|
const dirty = (0, react.useMemo)(() => value !== void 0 && draft !== null && JSON.stringify(toPatch(draft)) !== JSON.stringify(toPatch(draftFrom(value, catalog))), [
|
|
631
779
|
value,
|
|
632
780
|
draft,
|
|
@@ -634,12 +782,11 @@ ${extra}
|
|
|
634
782
|
]);
|
|
635
783
|
const invalid = (0, react.useMemo)(() => draft !== null && Object.values(draft.rows).some((row) => row.model.length > 0 && row.provider.length === 0), [draft]);
|
|
636
784
|
if (props.view === "summary") return t("summaryLine");
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
})
|
|
785
|
+
const variant = cardVariantFor(props.view);
|
|
786
|
+
if (value === void 0 || draft === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardLoading, {
|
|
787
|
+
prefix: "dsm",
|
|
788
|
+
variant,
|
|
789
|
+
text: t("loading")
|
|
643
790
|
});
|
|
644
791
|
const edit = (key, editValue) => {
|
|
645
792
|
setDraft({
|
|
@@ -673,10 +820,6 @@ ${extra}
|
|
|
673
820
|
});
|
|
674
821
|
}).finally(() => setSaving(false));
|
|
675
822
|
};
|
|
676
|
-
const onRetryCatalog = () => {
|
|
677
|
-
setCatalog(null);
|
|
678
|
-
setCatalogFailed(false);
|
|
679
|
-
};
|
|
680
823
|
const disabled = !snapshot.writable;
|
|
681
824
|
const rowNames = Object.keys(draft.rows).sort();
|
|
682
825
|
const providerCount = (catalog?.providers ?? []).length;
|
|
@@ -686,6 +829,7 @@ ${extra}
|
|
|
686
829
|
description: t("description"),
|
|
687
830
|
open,
|
|
688
831
|
onToggle: setOpen,
|
|
832
|
+
variant,
|
|
689
833
|
statusBadge: {
|
|
690
834
|
text: t(draft.enabled ? "enabledOn" : "enabledOff"),
|
|
691
835
|
on: draft.enabled
|
|
@@ -694,21 +838,18 @@ ${extra}
|
|
|
694
838
|
dirtyLabel: t("unsaved"),
|
|
695
839
|
readOnlyNotice: disabled ? t("readOnly") : void 0,
|
|
696
840
|
status,
|
|
697
|
-
actions:
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
841
|
+
actions: cardActions({
|
|
842
|
+
t,
|
|
843
|
+
dirty,
|
|
844
|
+
invalid,
|
|
845
|
+
saving,
|
|
846
|
+
disabled,
|
|
847
|
+
onDiscard: () => {
|
|
702
848
|
setDraft(draftFrom(value, catalog));
|
|
703
849
|
setStatus(IDLE_STATUS);
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
label: t(saving ? "saving" : "save"),
|
|
708
|
-
variant: "primary",
|
|
709
|
-
disabled: !dirty || invalid || saving || disabled,
|
|
710
|
-
onClick: onSave
|
|
711
|
-
}],
|
|
850
|
+
},
|
|
851
|
+
onSave
|
|
852
|
+
}),
|
|
712
853
|
children: [
|
|
713
854
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckRow, {
|
|
714
855
|
prefix: "dsm",
|
|
@@ -718,26 +859,13 @@ ${extra}
|
|
|
718
859
|
disabled,
|
|
719
860
|
onEdit: (v) => edit("enabled", v)
|
|
720
861
|
}),
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
children: t("catalogRetry")
|
|
729
|
-
})]
|
|
730
|
-
}) : null,
|
|
731
|
-
catalog === null && !catalogFailed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
732
|
-
className: "dsm-empty",
|
|
733
|
-
role: "status",
|
|
734
|
-
children: t("catalogLoading")
|
|
735
|
-
}) : null,
|
|
736
|
-
rowNames.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
737
|
-
className: "dsm-empty",
|
|
738
|
-
role: "status",
|
|
739
|
-
children: t("noRows")
|
|
740
|
-
}) : null,
|
|
862
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CatalogStatus, {
|
|
863
|
+
t,
|
|
864
|
+
catalog,
|
|
865
|
+
catalogFailed,
|
|
866
|
+
rowCount: rowNames.length,
|
|
867
|
+
onRetry: retryCatalog
|
|
868
|
+
}),
|
|
741
869
|
rowNames.map((name) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowBlock, {
|
|
742
870
|
name,
|
|
743
871
|
row: draft.rows[name] ?? { ...EMPTY_ROW },
|
|
@@ -830,9 +958,14 @@ ${extra}
|
|
|
830
958
|
.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}
|
|
831
959
|
.dsm-bannerRetry{color:inherit;font:inherit;cursor:pointer;background:0 0;border:none;flex:none;padding:0;font-weight:600}
|
|
832
960
|
.dsm-empty{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}
|
|
961
|
+
/* 分节形态(插件页 page 视图):provider 行块改为分隔线分组,去掉第二层内边距,
|
|
962
|
+
移动端可用宽度不再被卡片与行块两级内边距同时扣减。 */
|
|
963
|
+
.dsm-section .dsm-row{background:0 0;border:0;border-top:1px solid var(--dsw-alias-border-l2);border-radius:0;margin:0;padding:10px 0 2px}
|
|
964
|
+
.dsm-section .dsm-row:first-of-type{border-top:0}
|
|
833
965
|
@media (max-width:767px){
|
|
834
966
|
.dsm-row{padding:2px 8px 8px}
|
|
835
967
|
.dsm-rowHead{flex-wrap:wrap}
|
|
968
|
+
.dsm-section .dsm-row{padding:10px 0 2px}
|
|
836
969
|
}
|
|
837
970
|
`);
|
|
838
971
|
/** 幂等注入样式标签;返回标签(已存在或环境无 document 时为 null)。 */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dsh-plus/subagent-model",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.21",
|
|
4
4
|
"description": "dsh-plus service+ui plugin: 为 subagent/subagent_fork 等子代理按提供商配置模型与思考程度(或继承主代理)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"src"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@dsh-plus/shared": "0.1.
|
|
22
|
+
"@dsh-plus/shared": "0.1.31"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
25
|
"@deepseek-ai/cordis": "^4.0.4",
|
package/src/client/card.tsx
CHANGED
|
@@ -11,9 +11,12 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import {
|
|
14
|
+
type CardAction,
|
|
14
15
|
CardChrome,
|
|
16
|
+
CardLoading,
|
|
15
17
|
type CardStatusState,
|
|
16
18
|
CheckRow,
|
|
19
|
+
cardVariantFor,
|
|
17
20
|
IDLE_STATUS,
|
|
18
21
|
type NamespaceSettingsApi,
|
|
19
22
|
type PluginConfigViewProps,
|
|
@@ -147,6 +150,105 @@ function RowBlock(props: RowBlockProps): ReactElement {
|
|
|
147
150
|
)
|
|
148
151
|
}
|
|
149
152
|
|
|
153
|
+
/** 目录状态行:拉取失败给重试入口,拉取中与空行集各给一行提示。 */
|
|
154
|
+
function CatalogStatus(props: {
|
|
155
|
+
t: Translate
|
|
156
|
+
catalog: ModelCatalog | null
|
|
157
|
+
catalogFailed: boolean
|
|
158
|
+
rowCount: number
|
|
159
|
+
onRetry(): void
|
|
160
|
+
}): ReactElement | null {
|
|
161
|
+
if (props.catalogFailed) {
|
|
162
|
+
return (
|
|
163
|
+
<div className="dsm-banner" role="status">
|
|
164
|
+
<span>{props.t('catalogError')}</span>
|
|
165
|
+
<button type="button" className="dsm-bannerRetry" onClick={props.onRetry}>
|
|
166
|
+
{props.t('catalogRetry')}
|
|
167
|
+
</button>
|
|
168
|
+
</div>
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
if (props.catalog === null) {
|
|
172
|
+
return (
|
|
173
|
+
<p className="dsm-empty" role="status">
|
|
174
|
+
{props.t('catalogLoading')}
|
|
175
|
+
</p>
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
if (props.rowCount === 0) {
|
|
179
|
+
return (
|
|
180
|
+
<p className="dsm-empty" role="status">
|
|
181
|
+
{props.t('noRows')}
|
|
182
|
+
</p>
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
return null
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* 子代理 provider 目录(展开后拉取一次;失败保留重试入口,重试即回到未加载态)。
|
|
190
|
+
* 与草稿播种共用同一个 catalog:目录到达后由调用方补缺失的空行。
|
|
191
|
+
*/
|
|
192
|
+
function useCatalog(open: boolean): {
|
|
193
|
+
catalog: ModelCatalog | null
|
|
194
|
+
catalogFailed: boolean
|
|
195
|
+
retry(): void
|
|
196
|
+
} {
|
|
197
|
+
const [catalog, setCatalog] = useState<ModelCatalog | null>(null)
|
|
198
|
+
const [catalogFailed, setCatalogFailed] = useState(false)
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
if (!open || catalog !== null) return
|
|
201
|
+
let alive = true
|
|
202
|
+
fetchCatalog()
|
|
203
|
+
.then((loaded) => {
|
|
204
|
+
if (!alive) return
|
|
205
|
+
setCatalog(loaded)
|
|
206
|
+
setCatalogFailed(false)
|
|
207
|
+
})
|
|
208
|
+
.catch(() => {
|
|
209
|
+
if (alive) setCatalogFailed(true)
|
|
210
|
+
})
|
|
211
|
+
return () => {
|
|
212
|
+
alive = false
|
|
213
|
+
}
|
|
214
|
+
}, [open, catalog])
|
|
215
|
+
return {
|
|
216
|
+
catalog,
|
|
217
|
+
catalogFailed,
|
|
218
|
+
retry: () => {
|
|
219
|
+
setCatalog(null)
|
|
220
|
+
setCatalogFailed(false)
|
|
221
|
+
},
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** 卡片动作:放弃(回到 Host 值)与保存(无效行或只读时禁用)。 */ function cardActions(options: {
|
|
226
|
+
t: Translate
|
|
227
|
+
dirty: boolean
|
|
228
|
+
invalid: boolean
|
|
229
|
+
saving: boolean
|
|
230
|
+
disabled: boolean
|
|
231
|
+
onDiscard(): void
|
|
232
|
+
onSave(): void
|
|
233
|
+
}): CardAction[] {
|
|
234
|
+
const { t } = options
|
|
235
|
+
return [
|
|
236
|
+
{
|
|
237
|
+
key: 'discard',
|
|
238
|
+
label: t('discard'),
|
|
239
|
+
disabled: !options.dirty || options.saving,
|
|
240
|
+
onClick: options.onDiscard,
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
key: 'save',
|
|
244
|
+
label: t(options.saving ? 'saving' : 'save'),
|
|
245
|
+
variant: 'primary',
|
|
246
|
+
disabled: !options.dirty || options.invalid || options.saving || options.disabled,
|
|
247
|
+
onClick: options.onSave,
|
|
248
|
+
},
|
|
249
|
+
]
|
|
250
|
+
}
|
|
251
|
+
|
|
150
252
|
export function SubagentModelCard(props: CardProps): ReactElement | string | null {
|
|
151
253
|
const { t, scope, api } = props
|
|
152
254
|
const snapshot = useSyncExternalStore(
|
|
@@ -157,10 +259,9 @@ export function SubagentModelCard(props: CardProps): ReactElement | string | nul
|
|
|
157
259
|
// 0.1.6-alpha.2 插件页 page 视图为表单落地页,默认展开;旧槽位无 view,保持折叠。
|
|
158
260
|
const [open, setOpen] = useState(props.view === 'page')
|
|
159
261
|
const [draft, setDraft] = useState<Draft | null>(null)
|
|
160
|
-
const [catalog, setCatalog] = useState<ModelCatalog | null>(null)
|
|
161
|
-
const [catalogFailed, setCatalogFailed] = useState(false)
|
|
162
262
|
const [saving, setSaving] = useState(false)
|
|
163
263
|
const [status, setStatus] = useState<CardStatusState>(IDLE_STATUS)
|
|
264
|
+
const { catalog, catalogFailed, retry: retryCatalog } = useCatalog(open)
|
|
164
265
|
|
|
165
266
|
// 播种草稿(行集 = 已配置条目);catalog 到达后只补缺失的 provider 空行,
|
|
166
267
|
// 不覆盖在途编辑;后续 Host 更新同样不覆盖。
|
|
@@ -180,23 +281,6 @@ export function SubagentModelCard(props: CardProps): ReactElement | string | nul
|
|
|
180
281
|
})
|
|
181
282
|
}, [value, catalog])
|
|
182
283
|
|
|
183
|
-
useEffect(() => {
|
|
184
|
-
if (!open || catalog !== null) return
|
|
185
|
-
let alive = true
|
|
186
|
-
fetchCatalog()
|
|
187
|
-
.then((loaded) => {
|
|
188
|
-
if (!alive) return
|
|
189
|
-
setCatalog(loaded)
|
|
190
|
-
setCatalogFailed(false)
|
|
191
|
-
})
|
|
192
|
-
.catch(() => {
|
|
193
|
-
if (alive) setCatalogFailed(true)
|
|
194
|
-
})
|
|
195
|
-
return () => {
|
|
196
|
-
alive = false
|
|
197
|
-
}
|
|
198
|
-
}, [open, catalog])
|
|
199
|
-
|
|
200
284
|
const dirty = useMemo(
|
|
201
285
|
() =>
|
|
202
286
|
value !== undefined &&
|
|
@@ -214,12 +298,11 @@ export function SubagentModelCard(props: CardProps): ReactElement | string | nul
|
|
|
214
298
|
// 插件页 summary 视图只出一行简介(hooks 已全部落定,可安全提前返回)。
|
|
215
299
|
if (props.view === 'summary') return t('summaryLine')
|
|
216
300
|
|
|
301
|
+
// 插件页 page 视图内嵌宿主页面容器(已带页面级内边距与标题),用无边框分节。
|
|
302
|
+
const variant = cardVariantFor(props.view)
|
|
303
|
+
|
|
217
304
|
if (value === undefined || draft === null) {
|
|
218
|
-
return (
|
|
219
|
-
<li className="dsm-card">
|
|
220
|
-
<p className="dsm-empty">{t('loading')}</p>
|
|
221
|
-
</li>
|
|
222
|
-
)
|
|
305
|
+
return <CardLoading prefix="dsm" variant={variant} text={t('loading')} />
|
|
223
306
|
}
|
|
224
307
|
const edit = <K extends keyof Draft>(key: K, editValue: Draft[K]): void => {
|
|
225
308
|
setDraft({ ...draft, [key]: editValue })
|
|
@@ -248,10 +331,6 @@ export function SubagentModelCard(props: CardProps): ReactElement | string | nul
|
|
|
248
331
|
})
|
|
249
332
|
.finally(() => setSaving(false))
|
|
250
333
|
}
|
|
251
|
-
const onRetryCatalog = (): void => {
|
|
252
|
-
setCatalog(null)
|
|
253
|
-
setCatalogFailed(false)
|
|
254
|
-
}
|
|
255
334
|
|
|
256
335
|
const disabled = !snapshot.writable
|
|
257
336
|
const rowNames = Object.keys(draft.rows).sort()
|
|
@@ -263,29 +342,24 @@ export function SubagentModelCard(props: CardProps): ReactElement | string | nul
|
|
|
263
342
|
description={t('description')}
|
|
264
343
|
open={open}
|
|
265
344
|
onToggle={setOpen}
|
|
345
|
+
variant={variant}
|
|
266
346
|
statusBadge={{ text: t(draft.enabled ? 'enabledOn' : 'enabledOff'), on: draft.enabled }}
|
|
267
347
|
dirty={dirty}
|
|
268
348
|
dirtyLabel={t('unsaved')}
|
|
269
349
|
readOnlyNotice={disabled ? t('readOnly') : undefined}
|
|
270
350
|
status={status}
|
|
271
|
-
actions={
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
351
|
+
actions={cardActions({
|
|
352
|
+
t,
|
|
353
|
+
dirty,
|
|
354
|
+
invalid,
|
|
355
|
+
saving,
|
|
356
|
+
disabled,
|
|
357
|
+
onDiscard: () => {
|
|
358
|
+
setDraft(draftFrom(value, catalog))
|
|
359
|
+
setStatus(IDLE_STATUS)
|
|
280
360
|
},
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
label: t(saving ? 'saving' : 'save'),
|
|
284
|
-
variant: 'primary',
|
|
285
|
-
disabled: !dirty || invalid || saving || disabled,
|
|
286
|
-
onClick: onSave,
|
|
287
|
-
},
|
|
288
|
-
]}
|
|
361
|
+
onSave,
|
|
362
|
+
})}
|
|
289
363
|
>
|
|
290
364
|
<CheckRow
|
|
291
365
|
prefix="dsm"
|
|
@@ -295,24 +369,13 @@ export function SubagentModelCard(props: CardProps): ReactElement | string | nul
|
|
|
295
369
|
disabled={disabled}
|
|
296
370
|
onEdit={(v) => edit('enabled', v)}
|
|
297
371
|
/>
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
) : null}
|
|
306
|
-
{catalog === null && !catalogFailed ? (
|
|
307
|
-
<p className="dsm-empty" role="status">
|
|
308
|
-
{t('catalogLoading')}
|
|
309
|
-
</p>
|
|
310
|
-
) : null}
|
|
311
|
-
{rowNames.length === 0 ? (
|
|
312
|
-
<p className="dsm-empty" role="status">
|
|
313
|
-
{t('noRows')}
|
|
314
|
-
</p>
|
|
315
|
-
) : null}
|
|
372
|
+
<CatalogStatus
|
|
373
|
+
t={t}
|
|
374
|
+
catalog={catalog}
|
|
375
|
+
catalogFailed={catalogFailed}
|
|
376
|
+
rowCount={rowNames.length}
|
|
377
|
+
onRetry={retryCatalog}
|
|
378
|
+
/>
|
|
316
379
|
{rowNames.map((name) => (
|
|
317
380
|
<RowBlock
|
|
318
381
|
key={name}
|
package/src/client/styles.ts
CHANGED
|
@@ -16,9 +16,14 @@ const extraCss = `
|
|
|
16
16
|
.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}
|
|
17
17
|
.dsm-bannerRetry{color:inherit;font:inherit;cursor:pointer;background:0 0;border:none;flex:none;padding:0;font-weight:600}
|
|
18
18
|
.dsm-empty{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}
|
|
19
|
+
/* 分节形态(插件页 page 视图):provider 行块改为分隔线分组,去掉第二层内边距,
|
|
20
|
+
移动端可用宽度不再被卡片与行块两级内边距同时扣减。 */
|
|
21
|
+
.dsm-section .dsm-row{background:0 0;border:0;border-top:1px solid var(--dsw-alias-border-l2);border-radius:0;margin:0;padding:10px 0 2px}
|
|
22
|
+
.dsm-section .dsm-row:first-of-type{border-top:0}
|
|
19
23
|
@media (max-width:767px){
|
|
20
24
|
.dsm-row{padding:2px 8px 8px}
|
|
21
25
|
.dsm-rowHead{flex-wrap:wrap}
|
|
26
|
+
.dsm-section .dsm-row{padding:10px 0 2px}
|
|
22
27
|
}
|
|
23
28
|
`
|
|
24
29
|
|