@stofone/dsh-agent-roster 0.5.1 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/README.md +33 -3
- package/README.zh.md +25 -2
- package/client.js +485 -61
- package/cordis.patch.yml +2 -2
- package/index.js +50 -2
- package/lib/apply.js +186 -0
- package/lib/captain.js +259 -0
- package/lib/config-view.js +6 -0
- package/lib/remote.js +37 -1
- package/lib/render.js +25 -0
- package/lib/service.js +52 -0
- package/lib/tools.js +22 -2
- package/locale/en.json +51 -3
- package/locale/zh.json +51 -3
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -33,9 +33,11 @@ window.__ModuleLoader__.load({
|
|
|
33
33
|
const ROSTERS_ROUTE = "/plugins/dsh-agent-roster/rosters";
|
|
34
34
|
const INSTALL_ROUTE = "/plugins/dsh-agent-roster/install";
|
|
35
35
|
const COLLECTION_ROUTE = "/plugins/dsh-agent-roster/collection";
|
|
36
|
+
const APPLY_ROUTE = "/plugins/dsh-agent-roster/apply";
|
|
36
37
|
|
|
37
38
|
const EFFORTS = ["low", "medium", "high", "default"];
|
|
38
39
|
const LANGUAGES = ["auto", "zh", "en"];
|
|
40
|
+
const REGIONS = ["auto", "global", "china"];
|
|
39
41
|
const FORMATS = ["yaml", "json"];
|
|
40
42
|
const STYLE_ID = "dsh-agent-roster-style";
|
|
41
43
|
|
|
@@ -156,10 +158,12 @@ window.__ModuleLoader__.load({
|
|
|
156
158
|
|
|
157
159
|
function useLibrary() {
|
|
158
160
|
const [state, setState] = useState({ status: "loading", data: null, error: false });
|
|
159
|
-
const load = useCallback(async () => {
|
|
161
|
+
const load = useCallback(async (options) => {
|
|
162
|
+
// 只有档案源那处会传 { refresh: true }:绕过 registry 的 TTL,强制回源。
|
|
163
|
+
const forced = options !== null && typeof options === "object" && options.refresh === true;
|
|
160
164
|
setState((current) => ({ ...current, status: current.data === null ? "loading" : "refreshing" }));
|
|
161
165
|
try {
|
|
162
|
-
const result = await readJson(LIBRARY_ROUTE);
|
|
166
|
+
const result = await readJson(forced ? `${LIBRARY_ROUTE}?refresh=1` : LIBRARY_ROUTE);
|
|
163
167
|
if (!result.ok || result.body === null) throw new Error(String(result.status));
|
|
164
168
|
setState({ status: "ready", data: result.body, error: false });
|
|
165
169
|
} catch {
|
|
@@ -169,7 +173,7 @@ window.__ModuleLoader__.load({
|
|
|
169
173
|
useEffect(() => {
|
|
170
174
|
load();
|
|
171
175
|
}, [load]);
|
|
172
|
-
return { ...state, reload: load };
|
|
176
|
+
return { ...state, reload: () => load(), refresh: () => load({ refresh: true }) };
|
|
173
177
|
}
|
|
174
178
|
|
|
175
179
|
/* ------------------------------------------------------------------ */
|
|
@@ -503,7 +507,152 @@ window.__ModuleLoader__.load({
|
|
|
503
507
|
);
|
|
504
508
|
}
|
|
505
509
|
|
|
506
|
-
|
|
510
|
+
/**
|
|
511
|
+
* 队长卡:显示档案里声明的队长 + 与部署实际值的核对结果 + “应用”入口。
|
|
512
|
+
*
|
|
513
|
+
* 应用只做两件事:切会话模型、往会话里排一段建团队提示词。它不会、也不能
|
|
514
|
+
* 直接改部署配置——provider/model 不一致时,界面如实说“要改部署”,而不是假装成功。
|
|
515
|
+
*/
|
|
516
|
+
function CaptainPanel({ roster, report, sessions, onToast }) {
|
|
517
|
+
useTranslation();
|
|
518
|
+
const captain = roster.captain ?? {};
|
|
519
|
+
const [mode, setMode] = useState("existing");
|
|
520
|
+
const [sessionId, setSessionId] = useState("");
|
|
521
|
+
const [cwd, setCwd] = useState("");
|
|
522
|
+
const [busy, setBusy] = useState(false);
|
|
523
|
+
const [result, setResult] = useState(null);
|
|
524
|
+
|
|
525
|
+
useEffect(() => {
|
|
526
|
+
if (sessionId === "" && sessions.length > 0) setSessionId(sessions[0].sessionId);
|
|
527
|
+
}, [sessions, sessionId]);
|
|
528
|
+
|
|
529
|
+
const checks = report?.checks ?? [];
|
|
530
|
+
const canApply = mode === "new" || sessionId !== "";
|
|
531
|
+
|
|
532
|
+
async function apply() {
|
|
533
|
+
if (!canApply || busy) return;
|
|
534
|
+
setBusy(true);
|
|
535
|
+
setResult(null);
|
|
536
|
+
try {
|
|
537
|
+
const payload = { rosterId: roster.id, mode };
|
|
538
|
+
if (mode === "existing") payload.sessionId = sessionId;
|
|
539
|
+
else if (cwd.trim() !== "") payload.cwd = cwd.trim();
|
|
540
|
+
const response = await readJson(APPLY_ROUTE, {
|
|
541
|
+
method: "POST",
|
|
542
|
+
headers: { "content-type": "application/json" },
|
|
543
|
+
body: JSON.stringify(payload),
|
|
544
|
+
});
|
|
545
|
+
if (!response.ok || response.body?.ok !== true) {
|
|
546
|
+
setResult({ ok: false, code: response.body?.code ?? response.body?.error?.code ?? String(response.status) });
|
|
547
|
+
onToast(t("captain.applyFailed"));
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
setResult({ ok: true, sessionId: response.body.sessionId });
|
|
551
|
+
onToast(t("captain.applied"));
|
|
552
|
+
} catch {
|
|
553
|
+
setResult({ ok: false, code: "network" });
|
|
554
|
+
onToast(t("misc.failed"));
|
|
555
|
+
} finally {
|
|
556
|
+
setBusy(false);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
return h(
|
|
561
|
+
"section",
|
|
562
|
+
{ className: "rstr-captain" },
|
|
563
|
+
h("div", { className: "rstr-section-title rstr-mt" }, t("captain.title")),
|
|
564
|
+
h("p", { className: "rstr-hint" }, t("captain.hint")),
|
|
565
|
+
h(
|
|
566
|
+
"div",
|
|
567
|
+
{ className: "rstr-meta-grid" },
|
|
568
|
+
h("span", { className: "rstr-label" }, t("captain.declared")),
|
|
569
|
+
h("span", { className: "rstr-mono" }, captain.provider || "?") ,
|
|
570
|
+
h("span", { className: "rstr-mono" }, captain.model || "?"),
|
|
571
|
+
h("span", { className: "rstr-mono" }, captain.reasoningEffort || t("captain.effortDefault")),
|
|
572
|
+
h(
|
|
573
|
+
"span",
|
|
574
|
+
{ className: "rstr-muted" },
|
|
575
|
+
String(captain.personaPrefix ?? "").trim() === "" ? t("captain.noPersona") : t("captain.hasPersona"),
|
|
576
|
+
),
|
|
577
|
+
),
|
|
578
|
+
checks.length === 0
|
|
579
|
+
? h("p", { className: "rstr-hint" }, t("captain.noChecks"))
|
|
580
|
+
: h(
|
|
581
|
+
"ul",
|
|
582
|
+
{ className: "rstr-checks" },
|
|
583
|
+
checks.map((check) =>
|
|
584
|
+
h(
|
|
585
|
+
"li",
|
|
586
|
+
{ key: check.field, className: "rstr-check" },
|
|
587
|
+
h("span", null, t(`captain.check.${check.field}`)),
|
|
588
|
+
h(
|
|
589
|
+
"span",
|
|
590
|
+
{ className: check.status === "match" ? "rstr-ok" : check.status === "differ" ? "rstr-bad" : "rstr-muted" },
|
|
591
|
+
t(`captain.status.${check.status}`, { declared: check.declared || "?", actual: check.actual || "?" }),
|
|
592
|
+
),
|
|
593
|
+
),
|
|
594
|
+
),
|
|
595
|
+
),
|
|
596
|
+
h(
|
|
597
|
+
"div",
|
|
598
|
+
{ className: "rstr-apply" },
|
|
599
|
+
h("div", { className: "rstr-label" }, t("captain.applyTo")),
|
|
600
|
+
h(
|
|
601
|
+
"div",
|
|
602
|
+
{ className: "rstr-segmented" },
|
|
603
|
+
h(
|
|
604
|
+
"button",
|
|
605
|
+
{ type: "button", className: mode === "existing" ? "rstr-seg rstr-seg-on" : "rstr-seg", onClick: () => setMode("existing") },
|
|
606
|
+
t("captain.modeExisting"),
|
|
607
|
+
),
|
|
608
|
+
h(
|
|
609
|
+
"button",
|
|
610
|
+
{ type: "button", className: mode === "new" ? "rstr-seg rstr-seg-on" : "rstr-seg", onClick: () => setMode("new") },
|
|
611
|
+
t("captain.modeNew"),
|
|
612
|
+
),
|
|
613
|
+
),
|
|
614
|
+
mode === "existing"
|
|
615
|
+
? h(
|
|
616
|
+
"select",
|
|
617
|
+
{
|
|
618
|
+
className: "rstr-select",
|
|
619
|
+
value: sessionId,
|
|
620
|
+
onChange: (event) => setSessionId(event.target.value),
|
|
621
|
+
},
|
|
622
|
+
sessions.length === 0
|
|
623
|
+
? h("option", { value: "" }, t("captain.noSessions"))
|
|
624
|
+
: sessions.map((session) =>
|
|
625
|
+
h(
|
|
626
|
+
"option",
|
|
627
|
+
{ key: session.sessionId, value: session.sessionId },
|
|
628
|
+
`${session.sessionId.slice(0, 12)} · ${session.cwd || "?"}${
|
|
629
|
+
session.model === null ? "" : ` · ${session.model.model}`
|
|
630
|
+
}`,
|
|
631
|
+
),
|
|
632
|
+
),
|
|
633
|
+
)
|
|
634
|
+
: h("input", {
|
|
635
|
+
className: "rstr-input",
|
|
636
|
+
placeholder: t("captain.cwdPlaceholder"),
|
|
637
|
+
value: cwd,
|
|
638
|
+
onChange: (event) => setCwd(event.target.value),
|
|
639
|
+
}),
|
|
640
|
+
h(
|
|
641
|
+
"button",
|
|
642
|
+
{ type: "button", className: "rstr-btn rstr-btn-primary", disabled: !canApply || busy, onClick: apply },
|
|
643
|
+
busy ? t("captain.applying") : t("captain.apply"),
|
|
644
|
+
),
|
|
645
|
+
),
|
|
646
|
+
result !== null && result.ok === false
|
|
647
|
+
? h("p", { className: "rstr-bad rstr-small" }, t("captain.applyFailedCode", { code: result.code }))
|
|
648
|
+
: null,
|
|
649
|
+
result !== null && result.ok === true
|
|
650
|
+
? h("p", { className: "rstr-ok rstr-small" }, t("captain.appliedTo", { session: result.sessionId }))
|
|
651
|
+
: null,
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function RosterCard({ roster, deployment, captainReport, sessions, onToast, onEdit, onDuplicate, onDelete, onCopy }) {
|
|
507
656
|
const [open, setOpen] = useState(false);
|
|
508
657
|
const errors = (roster.diagnostics ?? []).filter((item) => item?.severity === "error").length;
|
|
509
658
|
const deployed = deployment !== undefined && deployment.deployed > 0;
|
|
@@ -573,6 +722,9 @@ window.__ModuleLoader__.load({
|
|
|
573
722
|
roster.members.length === 0
|
|
574
723
|
? h("p", { className: "rstr-muted" }, t("roster.noMembers"))
|
|
575
724
|
: roster.members.map((member) => h(MemberBlock, { key: member.name, member })),
|
|
725
|
+
roster.captain === null || roster.captain === undefined
|
|
726
|
+
? null
|
|
727
|
+
: h(CaptainPanel, { roster, report: captainReport, sessions: sessions ?? [], onToast }),
|
|
576
728
|
h(DiagnosticList, { items: roster.diagnostics }),
|
|
577
729
|
)
|
|
578
730
|
: null,
|
|
@@ -1064,11 +1216,16 @@ window.__ModuleLoader__.load({
|
|
|
1064
1216
|
/* 主面板 */
|
|
1065
1217
|
/* ------------------------------------------------------------------ */
|
|
1066
1218
|
|
|
1219
|
+
// 面板本身拿不到 ctx:这里由 apply() 把宿主服务放进来,供设置弹窗使用。
|
|
1220
|
+
const HOST = { form: null, openPanel: null };
|
|
1221
|
+
|
|
1067
1222
|
function Panel() {
|
|
1068
1223
|
useTranslation();
|
|
1069
1224
|
const library = useLibrary();
|
|
1070
1225
|
const [tab, setTab] = useState("rosters");
|
|
1071
1226
|
const [query, setQuery] = useState("");
|
|
1227
|
+
const [selectedTags, setSelectedTags] = useState(() => new Set());
|
|
1228
|
+
const [settingsOpen, setSettingsOpen] = useState(false);
|
|
1072
1229
|
const [editor, setEditor] = useState(null);
|
|
1073
1230
|
const [importing, setImporting] = useState(false);
|
|
1074
1231
|
const [removing, setRemoving] = useState(null);
|
|
@@ -1082,17 +1239,47 @@ window.__ModuleLoader__.load({
|
|
|
1082
1239
|
|
|
1083
1240
|
const snapshot = library.data;
|
|
1084
1241
|
const rosters = useMemo(() => {
|
|
1085
|
-
|
|
1242
|
+
return snapshot?.workspaces?.flatMap((workspace) =>
|
|
1086
1243
|
(workspace.rosters ?? []).map((roster) => ({ ...roster, rootDir: workspace.rootDir })),
|
|
1087
1244
|
) ?? [];
|
|
1245
|
+
}, [snapshot]);
|
|
1246
|
+
|
|
1247
|
+
// 标签池:整个库出现过的标签 + 各标签档案数,按出现次数降序(同数按字母)。
|
|
1248
|
+
const tagPool = useMemo(() => {
|
|
1249
|
+
const counts = new Map();
|
|
1250
|
+
for (const roster of rosters) {
|
|
1251
|
+
for (const tag of roster.tags ?? []) counts.set(tag, (counts.get(tag) ?? 0) + 1);
|
|
1252
|
+
}
|
|
1253
|
+
return [...counts.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
|
|
1254
|
+
}, [rosters]);
|
|
1255
|
+
|
|
1256
|
+
const filtered = useMemo(() => {
|
|
1088
1257
|
const needle = query.trim().toLowerCase();
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1258
|
+
return rosters.filter((roster) => {
|
|
1259
|
+
if (selectedTags.size > 0) {
|
|
1260
|
+
const tags = roster.tags ?? [];
|
|
1261
|
+
// 多选是“且”:勾了 revision 和 testing,就只看同时带这两个标签的档案。
|
|
1262
|
+
for (const tag of selectedTags) if (!tags.includes(tag)) return false;
|
|
1263
|
+
}
|
|
1264
|
+
if (needle === "") return true;
|
|
1265
|
+
return [roster.id, roster.title, roster.description, ...(roster.tags ?? []), ...(roster.members ?? []).map((member) => member.name)]
|
|
1092
1266
|
.filter((value) => typeof value === "string")
|
|
1093
|
-
.some((value) => value.toLowerCase().includes(needle))
|
|
1094
|
-
);
|
|
1095
|
-
}, [
|
|
1267
|
+
.some((value) => value.toLowerCase().includes(needle));
|
|
1268
|
+
});
|
|
1269
|
+
}, [rosters, query, selectedTags]);
|
|
1270
|
+
|
|
1271
|
+
function toggleTag(tag) {
|
|
1272
|
+
setSelectedTags((current) => {
|
|
1273
|
+
const next = new Set(current);
|
|
1274
|
+
if (next.has(tag)) next.delete(tag);
|
|
1275
|
+
else next.add(tag);
|
|
1276
|
+
return next;
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
function clearTags() {
|
|
1281
|
+
setSelectedTags(() => new Set());
|
|
1282
|
+
}
|
|
1096
1283
|
|
|
1097
1284
|
const teams = snapshot?.teams?.teams ?? [];
|
|
1098
1285
|
const hints = snapshot?.hints ?? [];
|
|
@@ -1153,9 +1340,18 @@ window.__ModuleLoader__.load({
|
|
|
1153
1340
|
h(
|
|
1154
1341
|
"div",
|
|
1155
1342
|
{ className: "rstr-head-actions" },
|
|
1156
|
-
|
|
1157
|
-
h(
|
|
1158
|
-
|
|
1343
|
+
// 头部只留一个入口:导入/新建属于档案页,刷新索引属于档案源,各自回到自己的页签。
|
|
1344
|
+
h(
|
|
1345
|
+
"button",
|
|
1346
|
+
{
|
|
1347
|
+
type: "button",
|
|
1348
|
+
className: "rstr-btn rstr-settings-btn",
|
|
1349
|
+
onClick: () => setSettingsOpen(true),
|
|
1350
|
+
title: t("panel.settings"),
|
|
1351
|
+
},
|
|
1352
|
+
h("span", { className: "rstr-settings-glyph", "aria-hidden": "true" }, "\u2699"),
|
|
1353
|
+
t("panel.settings"),
|
|
1354
|
+
),
|
|
1159
1355
|
),
|
|
1160
1356
|
);
|
|
1161
1357
|
|
|
@@ -1216,31 +1412,93 @@ window.__ModuleLoader__.load({
|
|
|
1216
1412
|
} else {
|
|
1217
1413
|
content = h(
|
|
1218
1414
|
"div",
|
|
1219
|
-
|
|
1220
|
-
h("
|
|
1221
|
-
className: "rstr-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1415
|
+
{ className: "rstr-rosters" },
|
|
1416
|
+
h("aside", { className: "rstr-tagpool", "aria-label": t("panel.tagPool") }, [
|
|
1417
|
+
h("span", { className: "rstr-tagpool-title" }, t("panel.tagPool")),
|
|
1418
|
+
tagPool.length === 0
|
|
1419
|
+
? h("p", { className: "rstr-muted rstr-small" }, t("panel.tagPoolEmpty"))
|
|
1420
|
+
: h(
|
|
1421
|
+
"div",
|
|
1422
|
+
{ className: "rstr-tagpool-list" },
|
|
1423
|
+
tagPool.map(([tag, count]) =>
|
|
1424
|
+
h(
|
|
1425
|
+
"button",
|
|
1426
|
+
{
|
|
1427
|
+
key: tag,
|
|
1428
|
+
type: "button",
|
|
1429
|
+
className: `rstr-tag${selectedTags.has(tag) ? " rstr-tag-on" : ""}`,
|
|
1430
|
+
"aria-pressed": selectedTags.has(tag),
|
|
1431
|
+
onClick: () => toggleTag(tag),
|
|
1432
|
+
},
|
|
1433
|
+
h("span", { className: "rstr-tag-name" }, tag),
|
|
1434
|
+
h("span", { className: "rstr-tag-count" }, String(count)),
|
|
1435
|
+
),
|
|
1436
|
+
),
|
|
1437
|
+
),
|
|
1438
|
+
selectedTags.size === 0
|
|
1439
|
+
? null
|
|
1440
|
+
: h(
|
|
1441
|
+
"button",
|
|
1442
|
+
{ type: "button", className: "rstr-btn rstr-btn-sm rstr-tag-clear", onClick: clearTags },
|
|
1443
|
+
t("panel.tagClear"),
|
|
1444
|
+
),
|
|
1445
|
+
]),
|
|
1446
|
+
h(
|
|
1447
|
+
"div",
|
|
1448
|
+
{ className: "rstr-rosters-main" },
|
|
1449
|
+
h(
|
|
1450
|
+
"div",
|
|
1451
|
+
{ className: "rstr-toolbar" },
|
|
1452
|
+
h("input", {
|
|
1453
|
+
className: "rstr-input rstr-search",
|
|
1454
|
+
value: query,
|
|
1455
|
+
placeholder: t("panel.searchPlaceholder"),
|
|
1456
|
+
"aria-label": t("panel.search"),
|
|
1457
|
+
onChange: (event) => setQuery(event.target.value),
|
|
1458
|
+
}),
|
|
1459
|
+
h(
|
|
1230
1460
|
"div",
|
|
1231
|
-
{ className: "rstr-
|
|
1232
|
-
|
|
1461
|
+
{ className: "rstr-toolbar-actions" },
|
|
1462
|
+
h(
|
|
1463
|
+
"button",
|
|
1464
|
+
{ type: "button", className: "rstr-btn", onClick: () => setImporting(true), disabled: targets.length === 0 },
|
|
1465
|
+
t("panel.import"),
|
|
1466
|
+
),
|
|
1467
|
+
h(
|
|
1468
|
+
"button",
|
|
1469
|
+
{
|
|
1470
|
+
type: "button",
|
|
1471
|
+
className: "rstr-btn rstr-btn-primary",
|
|
1472
|
+
onClick: () => setEditor({ roster: null, rootDir: defaultRoot }),
|
|
1473
|
+
disabled: targets.length === 0,
|
|
1474
|
+
},
|
|
1475
|
+
t("panel.create"),
|
|
1476
|
+
),
|
|
1477
|
+
),
|
|
1478
|
+
),
|
|
1479
|
+
filtered.length === 0
|
|
1480
|
+
? h("p", { className: "rstr-muted" }, t("panel.noMatch"))
|
|
1481
|
+
: h(
|
|
1482
|
+
"div",
|
|
1483
|
+
{ className: "rstr-cards" },
|
|
1484
|
+
filtered.map((roster) =>
|
|
1233
1485
|
h(RosterCard, {
|
|
1234
1486
|
key: `${roster.rootDir}::${roster.id}`,
|
|
1235
1487
|
roster,
|
|
1236
1488
|
deployment: snapshot?.deployments?.[roster.id],
|
|
1489
|
+
captainReport: (snapshot?.captain?.reports ?? []).find(
|
|
1490
|
+
(item) => item.rosterId === roster.id && item.rootDir === roster.rootDir,
|
|
1491
|
+
),
|
|
1492
|
+
sessions: snapshot?.sessions?.sessions ?? [],
|
|
1493
|
+
onToast: setToast,
|
|
1237
1494
|
onEdit: () => setEditor({ roster, rootDir: roster.rootDir }),
|
|
1238
1495
|
onDuplicate: () => setEditor({ roster, rootDir: roster.rootDir, duplicate: true }),
|
|
1239
1496
|
onDelete: () => setRemoving(roster),
|
|
1240
1497
|
onCopy: copyPayload,
|
|
1241
1498
|
}),
|
|
1242
1499
|
),
|
|
1243
|
-
|
|
1500
|
+
),
|
|
1501
|
+
),
|
|
1244
1502
|
);
|
|
1245
1503
|
}
|
|
1246
1504
|
}
|
|
@@ -1257,6 +1515,8 @@ window.__ModuleLoader__.load({
|
|
|
1257
1515
|
remote: snapshot?.remote ?? null,
|
|
1258
1516
|
onToast: setToast,
|
|
1259
1517
|
onPulled: () => library.reload(),
|
|
1518
|
+
onRefresh: () => library.refresh(),
|
|
1519
|
+
refreshing: library.status === "refreshing",
|
|
1260
1520
|
});
|
|
1261
1521
|
}
|
|
1262
1522
|
|
|
@@ -1290,6 +1550,22 @@ window.__ModuleLoader__.load({
|
|
|
1290
1550
|
: null,
|
|
1291
1551
|
),
|
|
1292
1552
|
toast !== null ? h("div", { className: "rstr-toast", role: "status" }, toast) : null,
|
|
1553
|
+
settingsOpen
|
|
1554
|
+
? h(
|
|
1555
|
+
Modal,
|
|
1556
|
+
{ title: t("panel.settings"), onClose: () => setSettingsOpen(false), width: "560px" },
|
|
1557
|
+
h(SettingsSection, {
|
|
1558
|
+
form: HOST.form,
|
|
1559
|
+
library,
|
|
1560
|
+
onSaved: () => library.reload(),
|
|
1561
|
+
inModal: true,
|
|
1562
|
+
onOpenPanel: () => {
|
|
1563
|
+
setSettingsOpen(false);
|
|
1564
|
+
HOST.openPanel?.();
|
|
1565
|
+
},
|
|
1566
|
+
}),
|
|
1567
|
+
)
|
|
1568
|
+
: null,
|
|
1293
1569
|
editor !== null
|
|
1294
1570
|
? h(Editor, {
|
|
1295
1571
|
target: { roster: editor.roster, rootDir: editor.rootDir, duplicate: editor.duplicate === true },
|
|
@@ -1332,7 +1608,7 @@ window.__ModuleLoader__.load({
|
|
|
1332
1608
|
* 拉取是**显式动作**:后端按内容比对,相同的跳过、不同的默认不覆盖(会把冲突数报回来)。
|
|
1333
1609
|
* 前端不做这份判断——它只把结果说清楚,避免"点一下按钮,本地改动就没了"。
|
|
1334
1610
|
*/
|
|
1335
|
-
function SourcesTab({ remote, onToast, onPulled }) {
|
|
1611
|
+
function SourcesTab({ remote, onToast, onPulled, onRefresh, refreshing }) {
|
|
1336
1612
|
useTranslation();
|
|
1337
1613
|
const [pulling, setPulling] = useState("");
|
|
1338
1614
|
const [openId, setOpenId] = useState("");
|
|
@@ -1387,10 +1663,33 @@ window.__ModuleLoader__.load({
|
|
|
1387
1663
|
}
|
|
1388
1664
|
}
|
|
1389
1665
|
|
|
1666
|
+
// 刷新是档案源自己的动作:绕过 TTL 重新回源,而不是重读本地快照。
|
|
1667
|
+
const head = h(
|
|
1668
|
+
"div",
|
|
1669
|
+
{ className: "rstr-sources-head" },
|
|
1670
|
+
h(
|
|
1671
|
+
"p",
|
|
1672
|
+
{ className: "rstr-muted rstr-small" },
|
|
1673
|
+
indexes.length > 0
|
|
1674
|
+
? indexes
|
|
1675
|
+
.map((index) =>
|
|
1676
|
+
`${index.label} · ${index.stale ? t("source.stale") : t("source.fetchedAt", { when: index.fetchedAt ?? "-" })}`,
|
|
1677
|
+
)
|
|
1678
|
+
.join(" ")
|
|
1679
|
+
: t("source.refreshHint"),
|
|
1680
|
+
),
|
|
1681
|
+
h(
|
|
1682
|
+
"button",
|
|
1683
|
+
{ type: "button", className: "rstr-btn", onClick: onRefresh, disabled: refreshing === true },
|
|
1684
|
+
refreshing === true ? t("panel.refreshing") : t("source.refresh"),
|
|
1685
|
+
),
|
|
1686
|
+
);
|
|
1687
|
+
|
|
1390
1688
|
if (collections.length === 0) {
|
|
1391
1689
|
return h(
|
|
1392
1690
|
"div",
|
|
1393
|
-
|
|
1691
|
+
{ className: "rstr-sources" },
|
|
1692
|
+
head,
|
|
1394
1693
|
h(EmptyState, { title: t("source.empty"), body: t("source.emptyHint") }),
|
|
1395
1694
|
problems.length > 0 ? h(DiagnosticList, { items: problems }) : null,
|
|
1396
1695
|
);
|
|
@@ -1399,17 +1698,7 @@ window.__ModuleLoader__.load({
|
|
|
1399
1698
|
return h(
|
|
1400
1699
|
"div",
|
|
1401
1700
|
{ className: "rstr-sources" },
|
|
1402
|
-
|
|
1403
|
-
? h(
|
|
1404
|
-
"p",
|
|
1405
|
-
{ className: "rstr-muted rstr-small" },
|
|
1406
|
-
indexes
|
|
1407
|
-
.map((index) =>
|
|
1408
|
-
`${index.label} · ${index.stale ? t("source.stale") : t("source.fetchedAt", { when: index.fetchedAt ?? "-" })}`,
|
|
1409
|
-
)
|
|
1410
|
-
.join(" "),
|
|
1411
|
-
)
|
|
1412
|
-
: null,
|
|
1701
|
+
head,
|
|
1413
1702
|
h(
|
|
1414
1703
|
"div",
|
|
1415
1704
|
{ className: "rstr-cards" },
|
|
@@ -1553,14 +1842,22 @@ window.__ModuleLoader__.load({
|
|
|
1553
1842
|
* 语言之外的配置项只读展示——档案目录与状态目录属于"部署事实",
|
|
1554
1843
|
* 让它们出现在两个地方反而会让人分不清哪个生效。
|
|
1555
1844
|
*/
|
|
1556
|
-
function SettingsSection({ form, library, onOpenPanel }) {
|
|
1845
|
+
function SettingsSection({ form, library, onOpenPanel, onSaved, inModal }) {
|
|
1557
1846
|
useTranslation();
|
|
1558
|
-
const [language, setLanguage] = useState(() =>
|
|
1847
|
+
const [language, setLanguage] = useState(() => readLanguage(form));
|
|
1848
|
+
const [region, setRegion] = useState(() => readFormValue(form, "region", "auto"));
|
|
1849
|
+
const [mirror, setMirror] = useState(() => readFormValue(form, "registryMirror", ""));
|
|
1850
|
+
const [dirs, setDirs] = useState(() => (library?.data?.config?.rosterDirs ?? []).join(", "));
|
|
1851
|
+
const [saved, setSaved] = useState("");
|
|
1559
1852
|
const [version, setVersion] = useState("");
|
|
1560
1853
|
|
|
1561
1854
|
useEffect(() => {
|
|
1562
1855
|
if (form?.subscribe === undefined) return undefined;
|
|
1563
|
-
const off = form.subscribe(() =>
|
|
1856
|
+
const off = form.subscribe(() => {
|
|
1857
|
+
setLanguage(readLanguage(form));
|
|
1858
|
+
setRegion(readFormValue(form, "region", "auto"));
|
|
1859
|
+
setMirror(readFormValue(form, "registryMirror", ""));
|
|
1860
|
+
});
|
|
1564
1861
|
return () => {
|
|
1565
1862
|
if (typeof off === "function") off();
|
|
1566
1863
|
};
|
|
@@ -1568,12 +1865,48 @@ window.__ModuleLoader__.load({
|
|
|
1568
1865
|
|
|
1569
1866
|
useEffect(() => {
|
|
1570
1867
|
setVersion(String(library?.data?.version ?? ""));
|
|
1571
|
-
|
|
1868
|
+
setDirs((library?.data?.config?.rosterDirs ?? []).join(", "));
|
|
1869
|
+
}, [library?.data?.version, library?.data?.config?.rosterDirs]);
|
|
1870
|
+
|
|
1871
|
+
useEffect(() => {
|
|
1872
|
+
if (saved === "") return undefined;
|
|
1873
|
+
const timer = setTimeout(() => setSaved(""), 2000);
|
|
1874
|
+
return () => clearTimeout(timer);
|
|
1875
|
+
}, [saved]);
|
|
1876
|
+
|
|
1877
|
+
function write(key, value) {
|
|
1878
|
+
try {
|
|
1879
|
+
form?.set?.(key, value);
|
|
1880
|
+
setSaved(t("settings.saved"));
|
|
1881
|
+
} catch {
|
|
1882
|
+
setSaved(t("settings.saveFailed"));
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1572
1885
|
|
|
1573
|
-
function
|
|
1886
|
+
function changeLanguage(next) {
|
|
1574
1887
|
setLanguage(next);
|
|
1575
|
-
|
|
1576
|
-
|
|
1888
|
+
write("language", next);
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
function changeRegion(next) {
|
|
1892
|
+
setRegion(next);
|
|
1893
|
+
write("region", next);
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
function saveMirror() {
|
|
1897
|
+
// 镜像地址原样存:路径前缀(公司 Nexus 常带 /repository/npm)是它的一部分,
|
|
1898
|
+
// 规整交给宿主(lib/remote.js 的 normalizeRegistryUrl),界面不在写盘前偷偷改写。
|
|
1899
|
+
write("registryMirror", mirror.trim());
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
function saveDirs() {
|
|
1903
|
+
// 逗号/分号/空白都当分隔符:用户从别处粘路径时不该因为分隔符被卡住。
|
|
1904
|
+
const list = dirs
|
|
1905
|
+
.split(/[,;\n]/)
|
|
1906
|
+
.map((item) => item.trim())
|
|
1907
|
+
.filter((item) => item !== "");
|
|
1908
|
+
write("rosterDirs", list);
|
|
1909
|
+
onSaved?.();
|
|
1577
1910
|
}
|
|
1578
1911
|
|
|
1579
1912
|
return h(
|
|
@@ -1596,7 +1929,7 @@ window.__ModuleLoader__.load({
|
|
|
1596
1929
|
type: "button",
|
|
1597
1930
|
className: `rstr-seg${language === value ? " rstr-seg-on" : ""}`,
|
|
1598
1931
|
"aria-pressed": language === value,
|
|
1599
|
-
onClick: () =>
|
|
1932
|
+
onClick: () => changeLanguage(value),
|
|
1600
1933
|
},
|
|
1601
1934
|
value === "auto" ? t("settings.languageAuto") : value === "zh" ? t("settings.languageZh") : t("settings.languageEn"),
|
|
1602
1935
|
),
|
|
@@ -1606,14 +1939,60 @@ window.__ModuleLoader__.load({
|
|
|
1606
1939
|
h(
|
|
1607
1940
|
"div",
|
|
1608
1941
|
{ className: "rstr-settings-row" },
|
|
1942
|
+
h("span", { className: "rstr-label" }, t("settings.region")),
|
|
1943
|
+
h(
|
|
1944
|
+
"div",
|
|
1945
|
+
{ className: "rstr-segmented" },
|
|
1946
|
+
REGIONS.map((value) =>
|
|
1947
|
+
h(
|
|
1948
|
+
"button",
|
|
1949
|
+
{
|
|
1950
|
+
key: value,
|
|
1951
|
+
type: "button",
|
|
1952
|
+
className: `rstr-seg${region === value ? " rstr-seg-on" : ""}`,
|
|
1953
|
+
"aria-pressed": region === value,
|
|
1954
|
+
onClick: () => changeRegion(value),
|
|
1955
|
+
},
|
|
1956
|
+
value === "auto" ? t("settings.regionAuto") : value === "global" ? t("settings.regionGlobal") : t("settings.regionChina"),
|
|
1957
|
+
),
|
|
1958
|
+
),
|
|
1959
|
+
),
|
|
1960
|
+
),
|
|
1961
|
+
h(
|
|
1962
|
+
"div",
|
|
1963
|
+
{ className: "rstr-settings-field" },
|
|
1964
|
+
h("span", { className: "rstr-label" }, t("settings.mirror")),
|
|
1965
|
+
h(
|
|
1966
|
+
"div",
|
|
1967
|
+
{ className: "rstr-settings-inline" },
|
|
1968
|
+
h("input", {
|
|
1969
|
+
className: "rstr-input",
|
|
1970
|
+
value: mirror,
|
|
1971
|
+
placeholder: t("settings.mirrorPlaceholder"),
|
|
1972
|
+
"aria-label": t("settings.mirror"),
|
|
1973
|
+
onChange: (event) => setMirror(event.target.value),
|
|
1974
|
+
}),
|
|
1975
|
+
h("button", { type: "button", className: "rstr-btn rstr-btn-sm", onClick: saveMirror }, t("settings.save")),
|
|
1976
|
+
),
|
|
1977
|
+
h("span", { className: "rstr-muted rstr-small" }, t("settings.mirrorHint")),
|
|
1978
|
+
),
|
|
1979
|
+
h(
|
|
1980
|
+
"div",
|
|
1981
|
+
{ className: "rstr-settings-field" },
|
|
1609
1982
|
h("span", { className: "rstr-label" }, t("settings.rosterDirs")),
|
|
1610
1983
|
h(
|
|
1611
|
-
"
|
|
1612
|
-
{ className: "rstr-
|
|
1613
|
-
(
|
|
1614
|
-
|
|
1615
|
-
:
|
|
1984
|
+
"div",
|
|
1985
|
+
{ className: "rstr-settings-inline" },
|
|
1986
|
+
h("input", {
|
|
1987
|
+
className: "rstr-input",
|
|
1988
|
+
value: dirs,
|
|
1989
|
+
placeholder: library?.data?.config?.defaultRoot ?? "~/.dsh/agent-roster",
|
|
1990
|
+
"aria-label": t("settings.rosterDirs"),
|
|
1991
|
+
onChange: (event) => setDirs(event.target.value),
|
|
1992
|
+
}),
|
|
1993
|
+
h("button", { type: "button", className: "rstr-btn rstr-btn-sm", onClick: saveDirs }, t("settings.save")),
|
|
1616
1994
|
),
|
|
1995
|
+
h("span", { className: "rstr-muted rstr-small" }, t("settings.rosterDirsHint")),
|
|
1617
1996
|
),
|
|
1618
1997
|
h(
|
|
1619
1998
|
"div",
|
|
@@ -1636,11 +2015,28 @@ window.__ModuleLoader__.load({
|
|
|
1636
2015
|
{ className: "rstr-settings-row" },
|
|
1637
2016
|
h("span", { className: "rstr-label" }, t("settings.version")),
|
|
1638
2017
|
h("code", { className: "rstr-code" }, version),
|
|
1639
|
-
h("button", { type: "button", className: "rstr-btn rstr-btn-sm", onClick: onOpenPanel }, t("settings.openPanel")),
|
|
2018
|
+
inModal === true ? null : h("button", { type: "button", className: "rstr-btn rstr-btn-sm", onClick: onOpenPanel }, t("settings.openPanel")),
|
|
1640
2019
|
),
|
|
2020
|
+
saved === "" ? null : h("p", { className: "rstr-muted rstr-small" }, saved),
|
|
1641
2021
|
);
|
|
1642
2022
|
}
|
|
1643
2023
|
|
|
2024
|
+
/* 通用读表单值:volatile 字段在 getSnapshot().value 里,取不到就用兜底。 */
|
|
2025
|
+
function readFormValue(form, key, fallback) {
|
|
2026
|
+
try {
|
|
2027
|
+
const value = form?.getSnapshot?.()?.value;
|
|
2028
|
+
if (value !== null && typeof value === "object" && typeof value[key] === "string") return value[key];
|
|
2029
|
+
} catch {
|
|
2030
|
+
/* 表单不可用时按兜底值。 */
|
|
2031
|
+
}
|
|
2032
|
+
return fallback;
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
function readLanguage(form) {
|
|
2036
|
+
const value = readFormValue(form, "language", "auto");
|
|
2037
|
+
return LANGUAGES.includes(value) ? value : "auto";
|
|
2038
|
+
}
|
|
2039
|
+
|
|
1644
2040
|
function readFormLanguage(form) {
|
|
1645
2041
|
let value = "auto";
|
|
1646
2042
|
try {
|
|
@@ -1668,7 +2064,7 @@ window.__ModuleLoader__.load({
|
|
|
1668
2064
|
* 文本色一律显式给出(不用 inherit),避免继承到与背景同色的文本。
|
|
1669
2065
|
*/
|
|
1670
2066
|
const CSS = `
|
|
1671
|
-
.rstr-page { display: flex; flex-direction: column; gap: 16px; padding: 20px 24px 32px;
|
|
2067
|
+
.rstr-page { display: flex; flex-direction: column; gap: 16px; padding: 20px 24px 32px; width: 100%; box-sizing: border-box; color: var(--dsw-alias-label-primary); font-size: 14px; line-height: 1.55; }
|
|
1672
2068
|
.rstr-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
|
|
1673
2069
|
.rstr-h1 { margin: 0; font-size: 20px; font-weight: 600; letter-spacing: -0.01em; color: var(--dsw-alias-label-primary); }
|
|
1674
2070
|
.rstr-muted { color: var(--dsw-alias-label-secondary); margin: 0; }
|
|
@@ -1678,6 +2074,13 @@ window.__ModuleLoader__.load({
|
|
|
1678
2074
|
.rstr-code-strong { font-size: 13px; color: var(--dsw-alias-label-primary); font-weight: 600; }
|
|
1679
2075
|
.rstr-truncate { display: inline-block; max-width: 420px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
|
|
1680
2076
|
.rstr-head-actions { display: flex; gap: 8px; flex-wrap: wrap; }
|
|
2077
|
+
.rstr-settings-btn { display: inline-flex; align-items: center; gap: 6px; }
|
|
2078
|
+
.rstr-settings-glyph { font-size: 14px; line-height: 1; }
|
|
2079
|
+
.rstr-toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
|
|
2080
|
+
.rstr-toolbar-actions { display: flex; gap: 8px; margin-left: auto; }
|
|
2081
|
+
.rstr-sources-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
|
|
2082
|
+
.rstr-settings { display: flex; flex-direction: column; gap: 10px; }
|
|
2083
|
+
.rstr-settings-title { margin: 0; font-size: 14px; font-weight: 600; }
|
|
1681
2084
|
|
|
1682
2085
|
.rstr-btn { border: 1px solid var(--dsw-alias-border-l3); background: var(--dsw-alias-bg-layer-1); color: var(--dsw-alias-label-primary); border-radius: 8px; padding: 6px 12px; font-size: 13px; font-family: inherit; cursor: pointer; transition: background .12s ease, border-color .12s ease; }
|
|
1683
2086
|
.rstr-btn:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }
|
|
@@ -1708,7 +2111,19 @@ window.__ModuleLoader__.load({
|
|
|
1708
2111
|
.rstr-tab-on { color: var(--dsw-alias-label-primary); border-bottom-color: var(--dsw-alias-state-business-primary); font-weight: 600; }
|
|
1709
2112
|
.rstr-tab-count { font-size: 11px; padding: 0 6px; border-radius: 999px; background: var(--dsw-alias-button-ghost-active-fill); color: var(--dsw-alias-label-secondary); }
|
|
1710
2113
|
|
|
1711
|
-
.rstr-
|
|
2114
|
+
.rstr-rosters { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: 16px; align-items: start; }
|
|
2115
|
+
@media (max-width: 860px) { .rstr-rosters { grid-template-columns: minmax(0, 1fr); } }
|
|
2116
|
+
.rstr-tagpool { display: flex; flex-direction: column; gap: 8px; padding: 12px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 12px; background: var(--dsw-alias-bg-layer-2); position: sticky; top: 12px; }
|
|
2117
|
+
.rstr-tagpool-title { font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-secondary); text-transform: uppercase; letter-spacing: 0.04em; }
|
|
2118
|
+
.rstr-tagpool-list { display: flex; flex-direction: column; gap: 4px; }
|
|
2119
|
+
.rstr-tag { display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; border: 1px solid transparent; background: transparent; color: var(--dsw-alias-label-secondary); border-radius: 6px; padding: 4px 8px; font-size: 12px; font-family: inherit; cursor: pointer; text-align: left; }
|
|
2120
|
+
.rstr-tag:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }
|
|
2121
|
+
.rstr-tag:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary); outline-offset: 1px; }
|
|
2122
|
+
.rstr-tag-on { background: var(--dsw-alias-button-ghost-active-fill); border-color: var(--dsw-alias-border-l3); color: var(--dsw-alias-label-primary); font-weight: 600; }
|
|
2123
|
+
.rstr-tag-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2124
|
+
.rstr-tag-count { font-size: 11px; color: var(--dsw-alias-label-secondary); font-variant-numeric: tabular-nums; }
|
|
2125
|
+
.rstr-tag-clear { align-self: flex-start; }
|
|
2126
|
+
.rstr-cards { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(380px, 1fr)); align-items: start; }
|
|
1712
2127
|
.rstr-card { border: 1px solid var(--dsw-alias-border-l2); border-radius: 12px; padding: 14px 16px; background: var(--dsw-alias-bg-layer-1); color: var(--dsw-alias-label-primary); display: flex; flex-direction: column; gap: 10px; }
|
|
1713
2128
|
.rstr-card-bad { border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 50%, transparent); }
|
|
1714
2129
|
.rstr-card-id { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
|
@@ -1768,6 +2183,13 @@ window.__ModuleLoader__.load({
|
|
|
1768
2183
|
|
|
1769
2184
|
.rstr-editor-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
|
|
1770
2185
|
.rstr-editor-toolbar .rstr-input { width: 110px; }
|
|
2186
|
+
.rstr-captain { display: flex; flex-direction: column; gap: 8px; border-top: 1px dashed var(--dsw-alias-border-l3); margin-top: 12px; padding-top: 10px; }
|
|
2187
|
+
.rstr-meta-grid { display: grid; grid-template-columns: auto repeat(4, minmax(0, auto)); gap: 6px 12px; align-items: baseline; font-size: 12px; }
|
|
2188
|
+
.rstr-checks { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
|
|
2189
|
+
.rstr-check { display: flex; justify-content: space-between; gap: 12px; font-size: 12px; border-bottom: 1px solid var(--dsw-alias-border-l1); padding-bottom: 3px; }
|
|
2190
|
+
.rstr-apply { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
|
|
2191
|
+
.rstr-select { background: var(--dsw-alias-bg-l2); color: var(--dsw-alias-label-primary); border: 1px solid var(--dsw-alias-border-l3); border-radius: 8px; padding: 5px 8px; font-size: 12px; max-width: 320px; }
|
|
2192
|
+
.rstr-bad { color: var(--dsw-alias-state-error-primary); }
|
|
1771
2193
|
.rstr-segmented { display: inline-flex; border: 1px solid var(--dsw-alias-border-l3); border-radius: 8px; overflow: hidden; }
|
|
1772
2194
|
.rstr-seg { border: none; background: transparent; padding: 5px 12px; font-size: 12px; cursor: pointer; color: var(--dsw-alias-label-secondary); }
|
|
1773
2195
|
.rstr-seg:hover { background: var(--dsw-alias-interactive-bg-hover); }
|
|
@@ -1786,10 +2208,11 @@ window.__ModuleLoader__.load({
|
|
|
1786
2208
|
|
|
1787
2209
|
.rstr-foot { display: flex; justify-content: space-between; gap: 12px; border-top: 1px solid var(--dsw-alias-border-l1); padding-top: 10px; margin-top: 4px; color: var(--dsw-alias-label-secondary); }
|
|
1788
2210
|
|
|
1789
|
-
.rstr-settings { display: flex; flex-direction: column; gap: 10px; }
|
|
1790
|
-
.rstr-settings-title { margin: 0; font-size: 14px; font-weight: 600; }
|
|
1791
2211
|
.rstr-settings-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
|
|
1792
2212
|
.rstr-settings-row .rstr-label { min-width: 120px; }
|
|
2213
|
+
.rstr-settings-field { display: flex; flex-direction: column; gap: 6px; }
|
|
2214
|
+
.rstr-settings-inline { display: flex; align-items: center; gap: 8px; }
|
|
2215
|
+
.rstr-settings-inline .rstr-input { flex: 1 1 auto; min-width: 0; }
|
|
1793
2216
|
`;
|
|
1794
2217
|
|
|
1795
2218
|
function ensureStyle() {
|
|
@@ -1889,7 +2312,8 @@ window.__ModuleLoader__.load({
|
|
|
1889
2312
|
"agent-roster: language sync",
|
|
1890
2313
|
);
|
|
1891
2314
|
|
|
1892
|
-
|
|
2315
|
+
HOST.form = form;
|
|
2316
|
+
HOST.openPanel = () => {
|
|
1893
2317
|
try {
|
|
1894
2318
|
layout?.selectPanel?.(PANEL_ID);
|
|
1895
2319
|
} catch {
|
|
@@ -1938,7 +2362,7 @@ window.__ModuleLoader__.load({
|
|
|
1938
2362
|
{ name: "settings.section", id: PANEL_ID, order: 55, locale: NS, label: () => t("settings.nav") },
|
|
1939
2363
|
function AgentRosterSettings() {
|
|
1940
2364
|
const library = useLibrary();
|
|
1941
|
-
return h(SettingsSection, { form, library, onOpenPanel: openPanel });
|
|
2365
|
+
return h(SettingsSection, { form, library, onOpenPanel: HOST.openPanel, inModal: false });
|
|
1942
2366
|
},
|
|
1943
2367
|
),
|
|
1944
2368
|
);
|