dsh-plugin-t-expert 0.3.91 → 0.4.8

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 CHANGED
@@ -41,370 +41,9 @@ __export(index_exports, {
41
41
  module.exports = __toCommonJS(index_exports);
42
42
  var import_react7 = __toESM(require("react"), 1);
43
43
 
44
- // src/client/attention.jsx
45
- var import_react2 = __toESM(require("react"), 1);
46
-
47
- // src/client/jobs.jsx
48
- function lookupUiWorkspace(ctx) {
49
- if (typeof ctx?.get !== "function") return void 0;
50
- try {
51
- return ctx.get("uiWorkspace");
52
- } catch {
53
- return void 0;
54
- }
55
- }
56
- function openHostSession(ctx, target, slotOpen) {
57
- if (target === void 0 || target === null || target === "") return;
58
- if (typeof target === "string" && typeof slotOpen === "function") {
59
- try {
60
- slotOpen(target);
61
- return;
62
- } catch (error62) {
63
- console.error("[t-team] \u6253\u5F00\u4F1A\u8BDD\u5931\u8D25\uFF1A", error62);
64
- }
65
- }
66
- try {
67
- const uiWorkspace = lookupUiWorkspace(ctx);
68
- if (typeof uiWorkspace?.openSession === "function") {
69
- uiWorkspace.openSession(target);
70
- return;
71
- }
72
- if (typeof target === "string") {
73
- ctx?.sessions?.open?.(target);
74
- ctx?.layout?.selectPanel?.(null);
75
- return;
76
- }
77
- if (typeof ctx?.sessions?.openSubagent === "function") {
78
- ctx.sessions.openSubagent(target);
79
- return;
80
- }
81
- const childId = target?.childSessionId;
82
- if (typeof childId === "string") ctx?.sessions?.open?.(childId);
83
- } catch (error62) {
84
- console.error("[t-team] \u6253\u5F00\u4F1A\u8BDD\u5931\u8D25\uFF1A", error62);
85
- }
86
- }
87
- var popoverClaim = { owner: "", listeners: /* @__PURE__ */ new Set() };
88
- function claimPopover(id) {
89
- if (popoverClaim.owner === id) return;
90
- popoverClaim.owner = id;
91
- for (const listener of [...popoverClaim.listeners]) listener(popoverClaim.owner);
92
- }
93
- function releasePopover(id) {
94
- if (popoverClaim.owner !== id) return;
95
- popoverClaim.owner = "";
96
- for (const listener of [...popoverClaim.listeners]) listener(popoverClaim.owner);
97
- }
98
- function subscribePopoverClaims(listener) {
99
- popoverClaim.listeners.add(listener);
100
- return () => popoverClaim.listeners.delete(listener);
101
- }
102
-
103
- // src/client/state.js
104
- var import_react = __toESM(require("react"), 1);
105
- var NS = "t-team";
106
- var CLIENT_BUILD_VERSION = "0.3.91";
107
- var uiPrefs = { showActivePulse: true };
108
- var UI_PREF_KEYS = Object.keys(uiPrefs);
109
- var uiPrefsListeners = /* @__PURE__ */ new Set();
110
- function setUiPrefs(next) {
111
- let changed = false;
112
- for (const key of Object.keys(uiPrefs)) {
113
- const value = next?.[key];
114
- if (typeof value !== "boolean" || value === uiPrefs[key]) continue;
115
- uiPrefs[key] = value;
116
- changed = true;
117
- }
118
- if (!changed) return;
119
- for (const listener of uiPrefsListeners) listener();
120
- }
121
- function useUiPref(key, fallback = true) {
122
- return import_react.default.useSyncExternalStore(
123
- (onChange) => {
124
- uiPrefsListeners.add(onChange);
125
- return () => uiPrefsListeners.delete(onChange);
126
- },
127
- () => uiPrefs[key],
128
- () => fallback
129
- );
130
- }
131
- function useShowActivePulse() {
132
- return useUiPref("showActivePulse", true);
133
- }
134
- var PLUGIN_ID = "dsh-plugin-t-expert";
135
- var DIVISION_FALLBACK = "specialized";
136
- var NEW_DIVISION = "__new";
137
-
138
- // src/client/attention.jsx
139
- var attentionCache = { key: "", list: [] };
140
- function useAttentionSessions(ctx) {
141
- const subscribe = import_react2.default.useCallback((onChange) => {
142
- const list = ctx?.sessions?.list;
143
- if (typeof list?.subscribe !== "function") return () => {
144
- };
145
- return list.subscribe(onChange);
146
- }, [ctx]);
147
- const read = import_react2.default.useCallback(() => {
148
- const byId = ctx?.sessions?.list?.getSnapshot?.()?.byId ?? {};
149
- const runningSubagents = /* @__PURE__ */ new Map();
150
- for (const id of Object.keys(byId)) {
151
- const child = byId[id];
152
- if (child?.origin !== "subagent" || child.running !== true) continue;
153
- const seen = /* @__PURE__ */ new Set();
154
- let parent = child.parentId;
155
- while (typeof parent === "string" && parent !== "" && !seen.has(parent)) {
156
- seen.add(parent);
157
- runningSubagents.set(parent, (runningSubagents.get(parent) ?? 0) + 1);
158
- parent = byId[parent]?.parentId;
159
- }
160
- }
161
- const rows = [];
162
- for (const id of Object.keys(byId)) {
163
- const session = byId[id];
164
- if (session === void 0 || session === null) continue;
165
- if (session.origin === "subagent" || session.blank === true) continue;
166
- const running = session.running === true || (runningSubagents.get(id) ?? 0) > 0;
167
- const pending = session.completed === true;
168
- if (!running && !pending) continue;
169
- rows.push({
170
- id,
171
- title: session.displayTitle || session.title || id,
172
- running,
173
- pending,
174
- at: typeof session.updatedAt === "number" ? session.updatedAt : 0
175
- });
176
- }
177
- rows.sort((a, b) => a.running === b.running ? b.at - a.at : a.running ? -1 : 1);
178
- const key = rows.map((row) => `${row.id}:${row.running ? 1 : 0}${row.pending ? 1 : 0}:${row.title}`).join("|");
179
- if (key !== attentionCache.key) {
180
- attentionCache.key = key;
181
- attentionCache.list = rows;
182
- }
183
- return attentionCache.list;
184
- }, [ctx]);
185
- return import_react2.default.useSyncExternalStore(subscribe, read, () => attentionCache.list);
186
- }
187
- var expandGroupFailureReported = false;
188
- function reportExpandFailure(cause) {
189
- if (expandGroupFailureReported) return;
190
- expandGroupFailureReported = true;
191
- console.warn("[t-team] \u5C55\u5F00\u5DE5\u4F5C\u533A\u5206\u7EC4\u5931\u8D25\uFF08\u53EA\u63D0\u793A\u4E00\u6B21\uFF0C\u4E0D\u5F71\u54CD\u8DF3\u8F6C\uFF09\uFF1A", cause);
192
- }
193
- function owningWorkspaceKey(items, sessionId) {
194
- return items.find((item) => Array.isArray(item?.sessionIds) && item.sessionIds.includes(sessionId))?.workspaceId ?? "";
195
- }
196
- function expandOwningWorkspaceGroup(ctx, sessionId) {
197
- try {
198
- if (typeof sessionId !== "string" || sessionId === "") return;
199
- const items = ctx?.get?.("workspaces")?.list?.getSnapshot?.()?.items;
200
- if (!Array.isArray(items) || items.length === 0) return;
201
- const entries = ctx?.slots?.entries?.("sidebar.workspaces");
202
- if (!Array.isArray(entries) || entries.length === 0) return;
203
- const entry = entries.find((row) => row?.store?.spec?.actions?.setGroupExpanded !== void 0);
204
- if (entry === void 0) {
205
- reportExpandFailure("\u4FA7\u680F\u69FD\u91CC\u627E\u4E0D\u5230\u5E26 setGroupExpanded \u7684 store \u58F0\u660E\uFF08\u4E0A\u6E38\u63A5\u53E3\u53EF\u80FD\u6539\u4E86\uFF09");
206
- return;
207
- }
208
- const store = ctx?.slots?.hostFace?.()?.storeOf?.(entry);
209
- if (store?.actions?.setGroupExpanded === void 0) {
210
- reportExpandFailure("\u62FF\u5230\u7684\u4FA7\u680F store \u5B9E\u4F8B\u6CA1\u6709 setGroupExpanded\uFF08\u4E0A\u6E38\u63A5\u53E3\u53EF\u80FD\u6539\u4E86\uFF09");
211
- return;
212
- }
213
- const key = owningWorkspaceKey(items, sessionId);
214
- if (store.getSnapshot?.()?.groupExpansion?.[key] === true) return;
215
- store.actions.setGroupExpanded(key, true);
216
- } catch (error62) {
217
- reportExpandFailure(error62);
218
- }
219
- }
220
- function copyAttentionText(value) {
221
- try {
222
- if (typeof navigator !== "undefined" && typeof navigator.clipboard?.writeText === "function") {
223
- navigator.clipboard.writeText(value).catch(() => fallbackAttentionCopy(value));
224
- return true;
225
- }
226
- } catch {
227
- }
228
- return fallbackAttentionCopy(value);
229
- }
230
- function fallbackAttentionCopy(value) {
231
- try {
232
- const ta = document.createElement("textarea");
233
- ta.value = value;
234
- ta.setAttribute("readonly", "");
235
- ta.style.position = "fixed";
236
- ta.style.top = "0";
237
- ta.style.opacity = "0";
238
- document.body.appendChild(ta);
239
- ta.select();
240
- const ok = document.execCommand("copy");
241
- document.body.removeChild(ta);
242
- return ok;
243
- } catch {
244
- return false;
245
- }
246
- }
247
- function deriveCurrentSession({ sessionId, sessionSnapshot, listEntry }) {
248
- const id = typeof sessionId === "string" && sessionId !== "" ? sessionId : "";
249
- const entry = id !== "" ? listEntry : void 0;
250
- const snap = sessionSnapshot ?? entry ?? void 0;
251
- const title = (snap && (snap.displayTitle ?? snap.title)) ?? (entry && (entry.displayTitle ?? entry.title)) ?? "";
252
- const running = snap && snap.running === true || entry && entry.running === true || false;
253
- return { ready: id !== "", id, title, running };
254
- }
255
- function AttentionPanel({ t, sessions, anchor: anchor2, onClose, onOpen, current }) {
256
- const [currentCopied, setCurrentCopied] = import_react2.default.useState(false);
257
- import_react2.default.useEffect(() => {
258
- if (!currentCopied) return void 0;
259
- const timer = window.setTimeout(() => setCurrentCopied(false), 1500);
260
- return () => window.clearTimeout(timer);
261
- }, [currentCopied]);
262
- import_react2.default.useEffect(() => {
263
- const onKey = (event) => {
264
- if (event.key === "Escape") onClose();
265
- };
266
- const onDown = (event) => {
267
- const target = event.target;
268
- if (target instanceof Element && target.closest(".t-team-pulse-pop, .t-team-pulse") !== null) return;
269
- onClose();
270
- };
271
- window.addEventListener("keydown", onKey);
272
- window.addEventListener("mousedown", onDown, true);
273
- return () => {
274
- window.removeEventListener("keydown", onKey);
275
- window.removeEventListener("mousedown", onDown, true);
276
- };
277
- }, [onClose]);
278
- const safeCurrent = current ?? { ready: false, id: "", title: "", running: false };
279
- const currentDisplayId = safeCurrent.id !== "" ? safeCurrent.id : safeCurrent.ready ? "\u2014" : "\xB7";
280
- const currentHintKey = safeCurrent.id === "" ? "pulse.idPending" : currentCopied ? "pulse.copied" : "pulse.copy";
281
- return /* @__PURE__ */ import_react2.default.createElement("div", { className: "t-team-pulse-pop", style: { right: anchor2.right, top: anchor2.top }, role: "dialog", "aria-label": t("pulse.title") }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "t-team-pulse-pop-head" }, t("pulse.title")), /* @__PURE__ */ import_react2.default.createElement(
282
- "button",
283
- {
284
- type: "button",
285
- className: "t-team-pulse-pop-current",
286
- "data-copied": currentCopied,
287
- "data-pending": safeCurrent.id === "",
288
- onClick: () => {
289
- if (safeCurrent.id === "") return;
290
- const ok = copyAttentionText(safeCurrent.id);
291
- if (ok) setCurrentCopied(true);
292
- },
293
- title: safeCurrent.id === "" ? t("pulse.idPendingHint") : t("pulse.copyHint")
294
- },
295
- /* @__PURE__ */ import_react2.default.createElement("span", { className: "t-team-pulse-pop-current-label" }, t("pulse.current")),
296
- /* @__PURE__ */ import_react2.default.createElement("span", { className: "t-team-pulse-pop-id", "data-monospace": "true" }, currentDisplayId),
297
- safeCurrent.title !== "" && /* @__PURE__ */ import_react2.default.createElement("span", { className: "t-team-pulse-pop-current-title", title: safeCurrent.title }, safeCurrent.title),
298
- /* @__PURE__ */ import_react2.default.createElement("span", { className: "t-team-pulse-pop-current-hint" }, t(currentHintKey))
299
- ), sessions.length === 0 ? /* @__PURE__ */ import_react2.default.createElement("div", { className: "t-team-pulse-pop-empty" }, t("pulse.empty")) : sessions.map((row) => /* @__PURE__ */ import_react2.default.createElement("div", { key: row.id, className: "t-team-pulse-pop-item" }, /* @__PURE__ */ import_react2.default.createElement("button", { type: "button", className: "t-team-pulse-pop-row", onClick: () => onOpen(row.id), title: row.title }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "t-team-pulse-pop-dot", "data-state": row.running ? "running" : "pending", "aria-hidden": "true" }), /* @__PURE__ */ import_react2.default.createElement("span", { className: "t-team-pulse-pop-title" }, row.title), /* @__PURE__ */ import_react2.default.createElement("span", { className: "t-team-pulse-pop-tag" }, t(row.running ? "pulse.running" : "pulse.pending"))), /* @__PURE__ */ import_react2.default.createElement(
300
- "button",
301
- {
302
- type: "button",
303
- className: "t-team-pulse-pop-id-btn",
304
- onClick: (event) => {
305
- event.stopPropagation();
306
- copyAttentionText(row.id);
307
- },
308
- title: t("pulse.copyHint")
309
- },
310
- /* @__PURE__ */ import_react2.default.createElement("span", { className: "t-team-pulse-pop-id", "data-monospace": "true" }, row.id)
311
- ))));
312
- }
313
- function ActivePulse({ ctx, hostCtx, t, sessionId, useSession, open: open2 }) {
314
- const show = useShowActivePulse();
315
- const sessions = useAttentionSessions(ctx);
316
- let current = { ready: false, id: "", title: "", running: false };
317
- if (typeof sessionId === "string" && sessionId !== "") {
318
- let snap;
319
- if (typeof useSession === "function") {
320
- try {
321
- const got = useSession((s) => s ?? null);
322
- if (got && typeof got === "object") snap = got;
323
- } catch {
324
- }
325
- }
326
- let entry;
327
- if (snap === void 0) {
328
- const byId = ctx?.sessions?.list?.getSnapshot?.()?.byId ?? {};
329
- entry = byId[sessionId];
330
- }
331
- current = deriveCurrentSession({ sessionId, sessionSnapshot: snap, listEntry: entry });
332
- } else if (ctx?.sessions?.list?.getSnapshot !== void 0) {
333
- const snap = ctx.sessions.list.getSnapshot();
334
- const byId = snap?.byId ?? {};
335
- const first = Object.values(byId).find((e) => e?.origin !== "subagent");
336
- if (first !== void 0) {
337
- current = deriveCurrentSession({ sessionId: first.id, listEntry: first });
338
- }
339
- }
340
- const [anchor2, setAnchor] = import_react2.default.useState(null);
341
- import_react2.default.useEffect(() => subscribePopoverClaims((owner) => {
342
- if (owner !== "pulse") setAnchor(null);
343
- }), []);
344
- const close = () => {
345
- setAnchor(null);
346
- releasePopover("pulse");
347
- };
348
- const attention = sessions.length > 0;
349
- if (!show) return null;
350
- const label = t(attention ? "pulse.on" : "pulse.off");
351
- const toggle = (event) => {
352
- if (anchor2 !== null) {
353
- close();
354
- return;
355
- }
356
- const rect = event.currentTarget.getBoundingClientRect();
357
- claimPopover("pulse");
358
- setAnchor({ right: Math.max(8, window.innerWidth - rect.right - 24), top: Math.round(rect.bottom) + 6 });
359
- };
360
- return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(
361
- "button",
362
- {
363
- type: "button",
364
- className: "t-team-pulse",
365
- "data-on": attention,
366
- onClick: toggle,
367
- "aria-label": label,
368
- "aria-expanded": anchor2 !== null,
369
- title: label
370
- },
371
- /* @__PURE__ */ import_react2.default.createElement(
372
- "svg",
373
- {
374
- width: "16",
375
- height: "16",
376
- viewBox: "0 0 16 16",
377
- fill: "none",
378
- stroke: "currentColor",
379
- strokeWidth: "1.5",
380
- strokeLinecap: "round",
381
- strokeLinejoin: "round",
382
- "aria-hidden": "true",
383
- focusable: "false"
384
- },
385
- /* @__PURE__ */ import_react2.default.createElement("path", { pathLength: "100", d: "M1 8h3.2l1.4-4.2 2.6 8.4 1.5-5.2 1 1H15" })
386
- )
387
- ), anchor2 !== null && /* @__PURE__ */ import_react2.default.createElement(
388
- AttentionPanel,
389
- {
390
- t,
391
- sessions,
392
- anchor: anchor2,
393
- current,
394
- onClose: close,
395
- onOpen: (id) => {
396
- const navCtx = hostCtx ?? ctx;
397
- expandOwningWorkspaceGroup(navCtx, id);
398
- openHostSession(navCtx, id, open2);
399
- close();
400
- }
401
- }
402
- ));
403
- }
404
-
405
44
  // src/client/boundary.jsx
406
- var import_react3 = __toESM(require("react"), 1);
407
- var SectionBoundary = class extends import_react3.default.Component {
45
+ var import_react = __toESM(require("react"), 1);
46
+ var SectionBoundary = class extends import_react.default.Component {
408
47
  constructor(props) {
409
48
  super(props);
410
49
  this.state = { error: "" };
@@ -420,12 +59,19 @@ var SectionBoundary = class extends import_react3.default.Component {
420
59
  }
421
60
  render() {
422
61
  if (this.state.error !== "") {
423
- return /* @__PURE__ */ import_react3.default.createElement("div", { className: "t-team-error" }, this.props.t("settings.crashed", { detail: this.state.error }));
62
+ return /* @__PURE__ */ import_react.default.createElement("div", { className: "t-team-error" }, this.props.t("settings.crashed", { detail: this.state.error }));
424
63
  }
425
64
  return this.props.children;
426
65
  }
427
66
  };
428
67
 
68
+ // src/client/state.js
69
+ var NS = "t-team";
70
+ var CLIENT_BUILD_VERSION = "0.4.8";
71
+ var PLUGIN_ID = "dsh-plugin-t-expert";
72
+ var DIVISION_FALLBACK = "specialized";
73
+ var NEW_DIVISION = "__new";
74
+
429
75
  // src/client/catalog.js
430
76
  var catalogCache = /* @__PURE__ */ new Map();
431
77
  function snapshotOf(remote) {
@@ -495,18 +141,16 @@ var CSS = `
495
141
  .t-team-editor-modal{width:min(860px,94vw);max-height:min(86vh,760px)}
496
142
  .t-team-editor-body{padding:14px;overflow:auto;display:flex;flex-direction:column;gap:10px}
497
143
  .t-team-editor-body .t-team-picker-list{max-height:300px}
498
- .t-team-section-head{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);border-top:1px solid var(--dsw-alias-border-l2);padding-top:10px}
499
- .t-team-section-head:first-child{border-top:0;padding-top:0}
144
+ /* \u5C0F\u8282\u6807\u9898\uFF08\u81EA\u5EFA\u5206\u7C7B / \u5B98\u65B9\u5206\u7C7B\uFF09\uFF1A**\u4E0D\u753B\u4E0A\u8FB9\u7EBF**\u3002
145
+ 2026-09-28 \u7528\u6237\u6307\u51FA\u4E24\u5904\u591A\u4F59\u7684\u6A2A\u7EBF\uFF0C\u8FD9\u662F\u7B2C\u4E00\u5904 \u2014\u2014 \u6807\u9898\u4E0A\u65B9\u7684\u7EBF\u7D27\u8D34\u7740\u4E0A\u4E00\u4E2A\u533A\u5757
146
+ \uFF08\u8F93\u5165\u6846\u90A3\u4E00\u884C / \u4E0A\u4E00\u4E2A\u5C0F\u8282\uFF09\uFF0C\u770B\u7740\u50CF"\u7EBF\u8D34\u5230\u4E86\u6309\u94AE\u4E0A"\u3002\u53BB\u6389\u8FB9\u6846\uFF0C\u9760\u5B57\u91CD\u4E0E\u95F4\u8DDD\u5206\u5C42\u5C31\u591F\u4E86\u3002 */
147
+ .t-team-section-head{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary)}
148
+ /* \u5206\u7C7B\u9875\u7684\u95F4\u8DDD\uFF082026-09-28 \u7528\u6237\uFF1A\u300C\u81EA\u5EFA\u5206\u7C7B / \u5B98\u65B9\u5206\u7C7B \u91CC\u4E0B\u65B9\u5143\u7D20\u589E\u52A0\u70B9\u95F4\u9694\u300D\uFF09\uFF1A
149
+ \u9875\u7EA7 14px \u9694\u5F00\u533A\u5757\uFF0C\u5C0F\u8282\u7EA7 8px \u9694\u5F00\u300C\u6807\u9898 \u2192 \u8BF4\u660E \u2192 \u5217\u8868\u300D\u3002
150
+ \u95F4\u8DDD\u4E00\u5F8B\u7531\u5BB9\u5668\u7684 gap \u63D0\u4F9B\uFF0C\u5143\u7D20\u81EA\u5DF1\u4E0D\u518D\u5199 margin \u2014\u2014 \u514D\u5F97\u4E24\u5904\u76F8\u52A0\u3001\u4EE5\u540E\u6539\u4E0D\u52A8\u3002 */
151
+ .t-team-cat-page{display:flex;flex-direction:column;gap:14px}
152
+ .t-team-cat-section{display:flex;flex-direction:column;gap:8px}
500
153
  .t-team-tabs{display:flex;gap:6px;border-bottom:1px solid var(--dsw-alias-border-l2);padding-bottom:6px}
501
- .t-team-setting-row{display:flex;flex-direction:column;gap:6px;padding:9px 11px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-2)}
502
- .t-team-setting-head{display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0}
503
- .t-team-setting-name{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
504
- .t-team-setting-hint{font-size:12px;color:var(--dsw-alias-label-caption);line-height:1.35}
505
- .t-team-switch{flex:none;box-sizing:border-box;width:38px;min-width:38px;height:22px;min-height:22px;padding:0;border:1px solid var(--dsw-alias-border-l2);border-radius:11px;background:var(--dsw-alias-bg-layer-3);cursor:pointer;position:relative;line-height:0;transition:background-color .15s,border-color .15s}
506
- .t-team-switch[data-on="true"]{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);box-shadow:none}
507
- .t-team-switch-knob{position:absolute;top:2px;left:2px;box-sizing:border-box;display:block;width:16px;min-width:16px;height:16px;min-height:16px;border-radius:50%;background:var(--dsw-alias-label-primary-foreground) !important;box-shadow:0 1px 3px rgba(0,0,0,.3);transition:transform .15s}
508
- .t-team-switch[data-on="true"] .t-team-switch-knob{transform:translateX(16px)}
509
- .t-team-switch:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}
510
154
  .t-team-tab{border:0;background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-size:13px;padding:4px 10px;border-radius:6px;cursor:pointer}
511
155
  .t-team-tab[data-active="true"]{background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);font-weight:600}
512
156
  .t-team-tabbody{display:flex;flex-direction:column;gap:12px;min-height:0}
@@ -514,7 +158,7 @@ var CSS = `
514
158
  .t-team-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center}
515
159
  .t-team-label{font-size:12px;color:var(--dsw-alias-label-caption);min-width:52px}
516
160
  .t-team-emoji{font-size:14px;line-height:1}
517
- .t-team-groups{display:flex;flex-direction:column;gap:10px}
161
+ .t-team-groups{display:flex;flex-direction:column;gap:6px}
518
162
  .t-team-editor{display:flex;flex-direction:column;gap:10px;border-top:1px dashed var(--dsw-alias-border-l2);padding-top:10px}
519
163
  .t-team-field{display:flex;flex-direction:column;gap:4px}
520
164
  .t-team-field > span{font-size:12px;color:var(--dsw-alias-label-caption)}
@@ -537,63 +181,57 @@ var CSS = `
537
181
  .t-team-badge{font-size:11px;border-radius:999px;padding:1px 8px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-secondary)}
538
182
  .t-team-badge[data-tone="warn"]{color:var(--dsw-alias-state-warn-primary)}
539
183
  .t-team-badge[data-tone="error"]{color:var(--dsw-alias-state-error-primary)}
540
- /* \u6D3B\u8DC3\u6307\u793A\uFF1A\u4F1A\u8BDD\u6807\u9898\u680F\u91CC\u7684\u4E00\u679A\u5FC3\u7535\u56FE\u56FE\u6807 \u2014\u2014 \u7D27\u8D34 agent preset \u80F6\u56CA\u5DE6\u4FA7\uFF08order \u6BD4\u5B83\u5C0F\uFF09\u3002
541
- \u5F69\u8272 = \u6709\u6D3B\u52A8\u6216\u6709\u5F85\u770B\uFF0C\u7070 = \u5168\u7A7A\u95F2\u3002pointer-events:none\uFF1A\u5B83\u53EA\u662F\u6307\u793A\uFF0C\u4E0D\u8BE5\u6321\u4F4F\u70B9\u51FB\u3002 */
542
- .t-team-pulse{display:inline-flex;align-items:center;justify-content:center;flex:none;width:24px;height:24px;margin-right:2px;padding:0;border:0;font:inherit;border-radius:7px;cursor:pointer;color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 14%,transparent)}
543
- .t-team-pulse[data-on="false"]{color:var(--dsw-alias-label-caption);background:color-mix(in srgb,var(--dsw-alias-label-caption) 12%,transparent)}
544
- .t-team-pulse:hover{background:color-mix(in srgb,var(--dsw-alias-label-primary) 12%,transparent)}
545
- /* \u6D6E\u5C42\u6750\u8D28\uFF082026-09-22\uFF09\uFF1A\u5BBF\u4E3B 0.1.7 \u7684\u89C6\u89C9\u7EDF\u4E00\u6539\u7248\u628A --dsw-specific-menu \u4ECE
546
- \u300C= \u4E0D\u900F\u660E\u7684 --dsw-alias-bg-layer-3\u300D\u6539\u6210\u4E86**\u534A\u900F\u660E\u83DC\u5355\u6750\u8D28**
547
- \uFF08\u4EAE\u8272 rgba(248,249,250,.58) / \u6697\u8272 rgba(48,49,54,.5)\uFF09\uFF0C\u5E76\u914D\u5957
548
- --dsw-menu-backdrop-filter = blur(40px) saturate(150%) \u624D\u6709\u6BDB\u73BB\u7483\u89C2\u611F\u3002
549
- \u6240\u4EE5\u51E1\u662F background \u7528\u4E86 --dsw-specific-menu \u7684\u89C4\u5219\uFF0C\u90FD\u5FC5\u987B\u7D27\u8DDF\u4E00\u6761 backdrop-filter\uFF0C
550
- \u5426\u5219\u5728 0.1.7 \u4E0A\u5C31\u662F\u300C\u5F39\u7A97\u534A\u900F\u660E\u3001\u80FD\u770B\u89C1\u5E95\u4E0B\u7684\u5BF9\u8BDD\u300D\uFF08\u7528\u6237\u5B9E\u6D4B\u62A5\u969C\uFF09\uFF1B\u65E7\u5BBF\u4E3B\u4E0A\u8BE5\u53D8\u91CF
551
- \u4ECD\u662F\u4E0D\u900F\u660E\u8272\uFF0C\u5E26\u4E86 filter \u4E5F\u6CA1\u6709\u526F\u4F5C\u7528\uFF08\u80CC\u540E\u5185\u5BB9\u672C\u6765\u5C31\u770B\u4E0D\u89C1\uFF09\u3002
552
- fallback \u7528\u56FA\u5B9A\u7684 blur \u503C\uFF0C\u53EA\u662F\u4E3A\u4E86\u5728\u62FF\u4E0D\u5230\u5BBF\u4E3B\u53D8\u91CF\u7684\u5BBF\u4E3B\u4E0A\u4E5F\u4E0D\u81F3\u4E8E\u5B8C\u5168\u6CA1\u6750\u8D28\u3002 */
553
- /* \u70B9\u5FBD\u7AE0\u5F39\u51FA\u7684\u300C\u9700\u8981\u6CE8\u610F\u7684\u4F1A\u8BDD\u300D\u6E05\u5355\uFF1A\u8D34\u5FBD\u7AE0\u53F3\u7F18\u3001\u56FE\u6807\u4E0B\u65B9\u3002 */
554
- .t-team-pulse-pop{position:fixed;z-index:70;width:300px;max-height:min(420px,60vh);overflow:auto;box-sizing:border-box;display:flex;flex-direction:column;gap:2px;padding:8px;border-radius:12px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-3));backdrop-filter:var(--dsw-menu-backdrop-filter,blur(20px) saturate(1.4));box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary)}
555
- .t-team-pulse-pop-head{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary);padding:4px 6px 6px}
556
- .t-team-pulse-pop-empty{font-size:12px;color:var(--dsw-alias-label-caption);padding:8px 6px}
557
- .t-team-pulse-pop-row{display:flex;align-items:center;gap:8px;width:100%;box-sizing:border-box;border:0;background:transparent;color:inherit;font:inherit;font-size:13px;text-align:left;padding:7px 8px;border-radius:8px;cursor:pointer}
558
- .t-team-pulse-pop-row:hover{background:var(--dsw-alias-interactive-bg-hover)}
559
- .t-team-pulse-pop-dot{flex:none;width:6px;height:6px;border-radius:50%;background:var(--dsw-alias-state-success-primary)}
560
- .t-team-pulse-pop-dot[data-state="pending"]{background:var(--dsw-alias-state-warn-primary)}
561
- .t-team-pulse-pop-title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
562
- .t-team-pulse-pop-tag{flex:none;font-size:11px;color:var(--dsw-alias-label-caption)}
563
- /* \u6BCF\u6761\u4F1A\u8BDD\u7684\u5BB9\u5668\uFF1A\u6807\u9898\u884C + id \u884C\u7EB5\u5411\u53E0\u653E\u3002 */
564
- .t-team-pulse-pop-item{display:flex;flex-direction:column;gap:0;border-radius:8px;overflow:hidden}
565
- .t-team-pulse-pop-item:hover{background:var(--dsw-alias-interactive-bg-hover)}
566
- /* \u5355\u6761\u4F1A\u8BDD\u4E0B\u65B9\u7684\u5C0F id \u884C\uFF1A\u7B49\u5BBD\u3001\u4F4E\u5BF9\u6BD4\uFF0C\u70B9\u51FB\u89E6\u53D1\u590D\u5236\uFF08\u4E0E\u6807\u9898\u884C\u540C hover \u533A\u57DF\uFF09\u3002 */
567
- .t-team-pulse-pop-id-btn{display:flex;align-items:center;gap:6px;width:100%;box-sizing:border-box;border:0;background:transparent;color:inherit;font:inherit;padding:0 8px 6px 22px;text-align:left;border-radius:0;cursor:copy}
568
- .t-team-pulse-pop-id{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;line-height:14px;color:var(--dsw-alias-label-tertiary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0}
569
- .t-team-pulse-pop-id[data-monospace="true"]{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
570
- /* \u9762\u677F head \u4E0B\u7684"\u5F53\u524D\u4F1A\u8BDD"\u884C\uFF1A\u5DE6\u6807\u7B7E + \u7B49\u5BBD id + \u6807\u9898(\u6324\u538B) + \u53F3 hint\uFF0C\u6574\u884C\u53EF\u70B9\u590D\u5236\uFF1B\u4E0D\u8BBA\u4E0B\u9762\u6709\u6CA1\u6709\u300C\u9700\u8981\u6CE8\u610F\u300D\u7684\u4F1A\u8BDD\u90FD\u663E\u793A\u3002
571
- \u5F53\u524D\u4F1A\u8BDD id \u8FD8\u6CA1\u5C31\u7EEA\u65F6\u6574\u884C\u70B9 cursor:default\uFF0C\u4E14\u7528\u70B9\u72B6\u8FB9\u6846 + \u7425\u73C0\u8272 hint\uFF0C\u660E\u663E\u533A\u522B\u4E8E"\u5DF2\u5C31\u7EEA\u53EF\u590D\u5236"\u3002*/
572
- .t-team-pulse-pop-current{display:flex;align-items:center;gap:6px;width:calc(100% - 4px);margin:0 2px 6px;box-sizing:border-box;border:1px dashed color-mix(in srgb,var(--dsw-alias-border-l2) 70%,transparent);background:color-mix(in srgb,var(--dsw-alias-bg-layer-2) 60%,transparent);color:inherit;font:inherit;padding:5px 8px;border-radius:8px;cursor:copy;text-align:left}
573
- .t-team-pulse-pop-current:hover{border-color:color-mix(in srgb,var(--dsw-alias-border-l3) 80%,transparent)}
574
- .t-team-pulse-pop-current[data-copied="true"]{border-style:solid;border-color:color-mix(in srgb,var(--dsw-alias-state-success-primary) 50%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 10%,transparent)}
575
- .t-team-pulse-pop-current[data-pending="true"]{cursor:default;border-style:dotted;background:transparent}
576
- .t-team-pulse-pop-current-label{flex:none;font-size:11px;color:var(--dsw-alias-label-secondary)}
577
- .t-team-pulse-pop-current-title{flex:1;min-width:0;font-size:11.5px;line-height:14px;color:var(--dsw-alias-label-caption);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
578
- .t-team-pulse-pop-current-hint{flex:none;font-size:11px;color:var(--dsw-alias-label-caption);white-space:nowrap}
579
- .t-team-pulse-pop-current[data-copied="true"] .t-team-pulse-pop-current-hint{color:var(--dsw-alias-state-success-primary)}
580
- .t-team-pulse-pop-current[data-pending="true"] .t-team-pulse-pop-current-hint{color:var(--dsw-alias-state-warn-primary)}
581
- /* \u6D3B\u52A8\u6001\u52A8\u6548\uFF1A\u4E00\u6BB5\u9AD8\u4EAE\u6CBF\u5FC3\u7535\u56FE\u8DEF\u5F84\u6EDA\u52A8\uFF08\u8D70\u7EB8\uFF0F\u76D1\u62A4\u4EEA\u626B\u8FC7\u90A3\u4E00\u4E0B\uFF09\uFF0C\u53EA\u5BF9\u6709\u6D3B\u52A8\u6216\u6709\u5F85\u770B\u65F6\u751F\u6548\uFF1B
582
- pathLength=100 \u8BA9\u8FD9\u91CC\u7684 100 \u5C31\u662F"\u6574\u6761\u8DEF\u5F84"\uFF0C\u4E0D\u5FC5\u77E5\u9053\u771F\u5B9E\u957F\u5EA6\u3002\u7A7A\u95F2\uFF08\u7070\uFF09\u65F6\u662F\u9759\u6B62\u5B9E\u7EBF\u3002
583
- \u53C2\u6570\uFF08\u7528\u6237 2026-09-15 \u8C03\u8FC7\u4E24\u8F6E\uFF09\uFF1A\u9AD8\u4EAE\u7EA6\u5360\u4E00\u534A\u8DEF\u5F84\u30011.2s \u8DD1\u5B8C\u4E00\u5708 \u2014\u2014 \u60F3\u518D\u8C03\u5C31\u6539\u8FD9\u4E24\u4E2A\u6570\u3002
584
- \u6CE8\u610F\u522B\u628A\u9AD8\u4EAE\u8C03\u5230\u63A5\u8FD1 100\uFF1A\u6697\u6BB5\u592A\u77ED\u5C31\u770B\u4E0D\u51FA"\u5728\u6EDA\u52A8"\u4E86\u3002 */
585
- @keyframes t-team-pulse-scan{from{stroke-dashoffset:100}to{stroke-dashoffset:0}}
586
- .t-team-pulse[data-on="true"] path{stroke-dasharray:48 52;animation:t-team-pulse-scan 1.2s linear infinite}
587
- @media (prefers-reduced-motion:reduce){.t-team-pulse[data-on="true"] path{animation:none;stroke-dasharray:none}}
588
184
  .t-team-team-meta{font-size:12px;color:var(--dsw-alias-label-caption);display:flex;gap:10px;flex-wrap:wrap}
589
185
  .t-team-note{font-size:11px;color:var(--dsw-alias-label-caption)}
590
186
  .t-team-ok{font-size:12px;color:var(--dsw-alias-state-success-primary)}
187
+ /* ---- \u8BBE\u7F6E\u9762\u677F\u5916\u58F3\u4E0E\u5361\u7247\uFF082026-09-28\uFF09----
188
+ \u9762\u677F\u4ECE\u300C\u8BBE\u7F6E \u2192 T\u4E13\u5BB6\u300D\u642C\u5230\u300C\u63D2\u4EF6\u5217\u8868 \u2192 T\u4E13\u5BB6\u300D\u7684\u63D2\u4EF6\u4FE1\u606F\u9875\uFF0C\u5916\u89C2\u5BF9\u9F50 dsh-helper-patch \u7684\u8BBE\u7F6E\u5361\u7247\uFF1A
189
+ 1px \u8FB9\u6846 + 12px \u5706\u89D2\u3002\u8FB9\u6846\u523B\u610F\u4E0D\u7528\u5B98\u65B9\u7684 0.5px \u2014\u2014 \u6D45\u8272\u4E3B\u9898\u4E0B --dsw-alias-border-l3 \u53EA\u6709 12% \u9ED1\uFF0C
190
+ 0.5px \u5728\u767D\u5E95\u4E0A\u51E0\u4E4E\u770B\u4E0D\u89C1\u8FB9\u754C\uFF0C\u800C\u5206\u7EC4\u5168\u9760\u8FD9\u6761\u7EBF\uFF08bg-layer-* \u5728\u6D45\u8272\u4E0B\u5168\u662F #fff\uFF0C\u5E95\u8272\u6307\u671B\u4E0D\u4E0A\uFF09\u3002 */
191
+ .t-team-panel{display:flex;flex-direction:column;gap:12px;min-height:0;color:var(--dsw-alias-label-primary)}
192
+ .t-team-panel-card{display:flex;flex-direction:column;gap:10px;padding:12px 14px;border:1px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md,12px);min-width:0}
193
+ .t-team-panel-card-head{display:flex;flex-direction:column;gap:2px}
194
+ .t-team-panel-card-title{font-size:13.5px;line-height:20px;font-weight:600;color:var(--dsw-alias-label-primary)}
195
+ .t-team-panel-card-hint{font-size:12px;line-height:18px;color:var(--dsw-alias-label-caption)}
196
+ /* \u5B98\u65B9 SegmentedTabs \u7684\u5E95\u69FD\u662F grid\uFF0C\u843D\u5728 flex \u5217\u5BB9\u5668\u91CC\u4F1A\u88AB\u62C9\u4F38\u5230\u6574\u884C\u5BBD\uFF08\u6807\u7B7E\u6324\u5728\u5DE6\u8FB9\uFF09\u3002
197
+ \u8FD9\u91CC\u53EA\u8BA9\u5B83\u6536\u7F29\u5230\u5185\u5BB9\u5BBD\u5EA6 \u2014\u2014 \u522B\u7684\u5E03\u5C40\u5C5E\u6027\u4E00\u4E2A\u90FD\u522B\u5199\uFF1A\u4E0E\u5B98\u65B9 .tabs \u7279\u5F02\u6027\u76F8\u540C\uFF0C
198
+ \u6211\u4EEC\u7684\u6837\u5F0F\u8868\u540E\u6CE8\u5165\uFF0C\u591A\u5199\u4E00\u6761\u5C31\u76D6\u6389\u5B98\u65B9\u4E00\u6761\uFF08\u7B2C\u4E00\u7248\u590D\u7528 .t-team-tabs \u5C31\u662F\u8FD9\u4E48\u574F\u7684\uFF09\u3002 */
199
+ .t-team-panel-tabs{align-self:flex-start;width:fit-content;max-width:100%}
591
200
  .t-team-settings{display:flex;flex-direction:column;gap:12px;min-height:0;color:var(--dsw-alias-label-primary)}
592
201
  .t-team-head{display:flex;flex-direction:column;gap:4px}
593
202
  .t-team-title{font-size:15px;font-weight:600;color:var(--dsw-alias-label-primary)}
594
203
  .t-team-meta{font-size:12px;color:var(--dsw-alias-label-secondary)}
595
204
  .t-team-bar{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
596
- .t-team-input{flex:1 1 200px;min-width:160px;box-sizing:border-box;padding:6px 10px;border-radius:8px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;outline:none}
205
+ /* \u5206\u7C7B\u7BA1\u7406\u9875\u300C\u65B0\u5EFA\u5206\u7C7B\u300D\u680F\u7684\u4E24\u4E2A\u8F93\u5165\u6846\uFF082026-09-28 \u7528\u6237\u62A5\u300C\u6837\u5F0F\u4E0D\u6B63\u5E38\uFF0C\u592A\u5927\u4E86\u300D\uFF0C\u968F\u540E\u8865\u4E00\u53E5
206
+ \u300C\u660E\u663E\u8F93\u5165\u6846\u91CC\u9762\u7684\u5B57\u4F53\u6BD4\u522B\u7684\u5730\u65B9\u5927\u300D\uFF09\uFF1A
207
+ \u5B83\u4EEC\u539F\u5148\u5B8C\u5168\u5403 .t-team-input \u7684\u901A\u7528\u9ED8\u8BA4\u503C \u2014\u2014
208
+ \xB7 flex:1 1 200px \u8BA9\u4E24\u4E2A\u6846\u5728\u6574\u884C\u91CC\u5BF9\u534A\u6491\u5F00\uFF08\u5B9E\u6D4B\u5404\u7EA6 236px\uFF0C\u51E0\u4E4E\u5360\u6EE1\u4E00\u884C\uFF09\uFF1B
209
+ \xB7 \u6CA1\u6709\u663E\u5F0F height / font-size\uFF0C\u5B57\u53F7\u4E0E\u884C\u9AD8\u5168\u90E8\u7EE7\u627F\u5BBF\u4E3B\u8BBE\u7F6E\u9875\uFF0814px / 1.6\uFF09\uFF0C
210
+ padding \u53C8\u662F\u7ED9\u6A2A\u5411\u641C\u7D22\u6761\u7528\u7684 6px 10px\u3002
211
+ \u4E8E\u662F\u5B83\u4EEC\u6BD4\u540C\u9875\u5176\u5B83\u63A7\u4EF6\uFF08\u80F6\u56CA\u6309\u94AE 12px \u5B57\uFF09\u5927\u4E00\u53F7\uFF0C\u7A84\u9762\u677F\u4E0B\u8FD8\u628A\u300C\u521B\u5EFA\u5206\u7C7B\u300D\u6324\u5230\u7B2C\u4E8C\u884C\u3002
212
+ \u8FD9\u91CC\u6309\u540C\u9875\u63A7\u4EF6\u90A3\u4E00\u6863\u9489\u6B7B\uFF1A28px \u9AD8 / 12px \u5B57\u53F7\u3001padding 4px 8px\u3001\u884C\u9AD8 18px
213
+ \uFF0828 - 4\xD72 - 1\xD72 = 18\uFF0C\u6B63\u597D\u88C5\u4E0B\uFF09\uFF1B\u5BBD\u5EA6\u300C\u57FA\u51C6 190px + \u4E0A\u9650 280px\u300D\u2014\u2014 \u7A84\u9762\u677F\u4E0B\u4E0E\u6309\u94AE\u540C\u6392\uFF0C
214
+ \u5BBD\u9762\u677F\u4E0B\u4E5F\u4E0D\u4F1A\u88AB\u62C9\u6210\u5927\u957F\u6761\u3002
215
+ \u26A0\uFE0F \u9009\u62E9\u5668**\u5FC5\u987B\u5E26 .t-team-bar \u524D\u7F00**\uFF08= \u7279\u5F02\u6027 0,2,0\uFF09\uFF1A\u901A\u7528\u89C4\u5219 .t-team-input \u662F 0,1,0\uFF0C
216
+ \u4F46\u5B83\u5B9A\u4E49\u5728\u672C\u6761**\u540E\u9762**\uFF0C\u540C\u7EA7\u7279\u5F02\u6027\u4E0B\u540E\u8005\u80DC \u2014\u2014 2026-09-28 \u7B2C\u4E00\u7248\u5C31\u662F\u53EA\u7528 .t-team-cat-new\uFF080,1,0\uFF09\uFF0C
217
+ \u7ED3\u679C font:inherit / padding / flex \u4E09\u6837\u5168\u88AB\u5B83\u76D6\u56DE\u53BB\uFF1A\u6846\u77EE\u4E86\uFF08height \u751F\u6548\uFF09\u4F46\u5B57\u6CA1\u53D8\u5C0F\uFF0C
218
+ \u4E8E\u662F\u7528\u6237\u770B\u5230\u7684\u6B63\u662F\u300C\u6846\u53D8\u5C0F\u4E86\u3001\u5B57\u8FD8\u662F\u5927\u7684\u300D\u3002\u524D\u7F00\u4E00\u52A0\u5C31\u7A33\uFF08\u4E0D\u4F9D\u8D56\u4E66\u5199\u987A\u5E8F\uFF09\u3002
219
+ \u26A0\uFE0F \u7C7B\u540D\u5199\u5728 input \u81EA\u5DF1\u8EAB\u4E0A\uFF08\u4E0D\u5199\u5BB9\u5668\uFF09\uFF0C\u662F\u672C\u4ED3\u5E93 CSS \u4E0E verify \u7684\u5171\u540C\u7EA6\u5B9A\uFF1A
220
+ t-team-cat-new \u5FC5\u987B\u662F**\u7D27\u8DDF\u89C4\u5219\u4F53**\u7684\u7C7B\u540D\uFF08verify \u53EA\u8BA4\u300C.\u7C7B\u540D + \u5DE6\u82B1\u62EC\u53F7\u300D\uFF09\u3002 */
221
+ .t-team-bar .t-team-cat-new{flex:1 1 190px;min-width:0;max-width:280px;height:28px;box-sizing:border-box;padding:4px 8px;font-size:12px;line-height:18px}
222
+ /* \u4E13\u5BB6\u9875\u641C\u7D22\u6846\uFF082026-09-28 \u7528\u6237\u62A5\u300C\u8FD9\u4E2A\u8F93\u5165\u6846\u4E5F\u4FEE\u590D\u4E0B\u300D\uFF09\uFF1A\u4E0E\u5206\u7C7B\u65B0\u5EFA\u680F\u540C\u6E90 \u2014\u2014 \u5C3A\u5BF8\u5168\u9760\u4E0B\u9762\u7684
223
+ \u901A\u7528\u9ED8\u8BA4\u503C\u3002\u5B57\u53F7\u7531\u57FA\u7C7B\u7EDF\u4E00\u9489\uFF08\u89C1 .t-team-input\uFF09\uFF0C\u8FD9\u91CC\u53EA\u9489\u9AD8\u5EA6\uFF1A28px\uFF08\u4E0E\u540C\u6392\u7684\u80F6\u56CA\u6309\u94AE\u3001
224
+ \u5206\u7C7B\u65B0\u5EFA\u680F\u4E00\u81F4\uFF0C\u4E0D\u518D\u662F\u7EE7\u627F\u6765\u7684 32px\uFF09\u3002\u5BBD\u5EA6\u7EE7\u7EED\u8D70\u57FA\u7C7B\u7684 flex:1 1 200px\uFF0C\u81EA\u9002\u5E94\u5360\u6EE1\u641C\u7D22\u6761\u4F59\u91CF\u3002 */
225
+ /* \u7B5B\u9009\u680F\u91CC\u7684\u8F93\u5165\u6846\u6491\u5F00\u4F59\u91CF\uFF08\u7C7B\u540D\u7531 ui.jsx \u7684 TextInput \u52A0\u5728\u5B98\u65B9 Input \u7684 wrapper \u4E0A\uFF09\u3002
226
+ 2026-09-28 \u4ECE .t-team-search \u6539\u540D\uFF1A\u539F\u540D\u5B57\u6697\u793A\u5B83\u53EA\u5C5E\u4E8E\u4E13\u5BB6\u9875\u641C\u7D22\u6846\uFF0C\u5B9E\u9645\u5206\u7C7B\u9875\u65B0\u5EFA\u680F\u3001
227
+ \u7F16\u8F91\u5668\u5B57\u6BB5\u4E5F\u7528\u540C\u4E00\u4E2A\u7EC4\u4EF6 \u2014\u2014 \u540D\u5B57\u4E0E\u7528\u9014\u5BF9\u9F50\uFF0C\u6539\u6837\u5F0F\u65F6\u624D\u4E0D\u4F1A\u8BEF\u4F24\u3002 */
228
+ .t-team-bar .t-team-input-field{flex:1 1 220px;min-width:0}
229
+ /* \u26A0\uFE0F font-size / line-height \u5FC5\u987B\u5199\u5728 font:inherit **\u4E4B\u540E**\uFF08\u540C\u4E00\u89C4\u5219\u4F53\u5185\u540E\u58F0\u660E\u8005\u80DC\uFF09\uFF1A
230
+ font \u7B80\u5199\u4F1A\u628A size/line-height \u4E00\u8D77\u91CD\u7F6E\u6210\u7EE7\u627F\u503C\u30022026-09-28 \u7684\u6559\u8BAD \u2014\u2014 \u63D2\u4EF6\u5185\u6240\u6709\u6CA1\u663E\u5F0F\u5199
231
+ font-size \u7684\u8F93\u5165\u6846\uFF08\u5206\u7C7B\u65B0\u5EFA\u680F\u3001\u4E13\u5BB6\u9875\u641C\u7D22\u6846\u3001\u7F16\u8F91\u5668\u5B57\u6BB5\u3001@ \u53EC\u5524\u5F39\u7A97\u7684\u641C\u7D22\u6846\u2026\uFF09\u90FD\u5728\u7EE7\u627F
232
+ \u5BBF\u4E3B\u8BBE\u7F6E\u9875\u7684 14px\uFF0C\u6BD4\u65C1\u8FB9\u7684\u80F6\u56CA\u6309\u94AE\uFF0812px\uFF09\u5927\u4E00\u53F7\uFF0C\u7528\u6237\u662F\u4E00\u4E2A\u4E00\u4E2A\u62A5\u8FC7\u6765\u7684\u3002\u8FD9\u91CC\u4ECE\u57FA\u7C7B
233
+ \u7EDF\u4E00\u9489\u6B7B 12px / 18px\uFF0C\u5404\u5904\u4E0D\u7528\u518D\u5404\u5199\u4E00\u904D\u3002 */
234
+ .t-team-input{flex:1 1 200px;min-width:160px;box-sizing:border-box;padding:6px 10px;border-radius:8px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;line-height:18px;outline:none}
597
235
  .t-team-input:focus{border-color:var(--dsw-alias-button-primary-fill)}
598
236
  .t-team-select{box-sizing:border-box;height:30px;padding:0 8px;border-radius:8px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;cursor:pointer;max-width:180px}
599
237
  .t-team-select:hover{background:var(--dsw-alias-interactive-bg-hover)}
@@ -605,11 +243,31 @@ var CSS = `
605
243
  .t-team-chip[data-tone="error"]{color:var(--dsw-alias-state-error-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 40%,transparent)}
606
244
  .t-team-chip[data-tone="error"]:hover{background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 12%,transparent);color:var(--dsw-alias-state-error-primary)}
607
245
  .t-team-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:10px 12px}
608
- .t-team-group{margin-top:6px}
609
- .t-team-division{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary);margin:2px 0 6px}
610
- .t-team-card{position:relative;display:grid;min-width:0;min-height:132px;overflow:hidden;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-2)}
611
- .t-team-card[data-enabled="true"]{border-color:var(--dsw-alias-button-primary-fill)}
612
- .t-team-card:hover{background:var(--dsw-alias-interactive-bg-hover)}
246
+ .t-team-group{margin-top:0}
247
+ .t-team-division{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary)}
248
+ /* \u5206\u7C7B\u6298\u53E0\u5934 = **\u6761\u72B6\u5361\u7247**\uFF082026-09-28 \u7528\u6237\uFF1A\u300C\u6298\u53E0\u4F4D\u7F6E\u6709\u70B9\u4E0D\u660E\u663E\uFF0C\u505A\u4E00\u4E2A\u5E95\u5361\u7247\u300D\uFF09\uFF1A
249
+ \u65E9\u5148\u53EA\u6709\u4E00\u884C\u88F8\u6587\u672C + \u4E00\u4E2A\u5C0F chevron\uFF0C14 \u4E2A\u5206\u7C7B\u6392\u4E0B\u6765\u50CF\u4E00\u4E32\u6563\u5F00\u7684\u5B57\uFF0C\u770B\u4E0D\u51FA\u54EA\u5757\u80FD\u70B9\u3002
250
+ \u73B0\u5728\u6574\u6761\u6709\u8FB9\u6846\u3001\u5706\u89D2\u4E0E\u5E95\u8272\uFF0Cchevron \u63A8\u5230\u53F3\u7AEF\uFF1B\u5C55\u5F00\u6001\u6362\u6210\u767D\u5E95 + \u6DF1\u4E00\u6863\u8FB9\u6846\uFF0C
251
+ \u4E00\u773C\u80FD\u770B\u51FA\u54EA\u4E2A\u5206\u7C7B\u662F\u5F00\u7740\u7684\u3002
252
+ \u5E95\u8272\u7528 --dsw-alias-interactive-bg-hover \u800C\u4E0D\u662F bg-layer-*\uFF1A\u6D45\u8272\u4E3B\u9898\u4E0B\u540E\u8005\u662F #fff\uFF0C
253
+ \u4E0E\u9875\u9762\u540C\u8272\uFF08\u5B9E\u6D4B\u8FC7\uFF09\uFF0C\u5361\u7247\u5C31\u767D\u505A\u4E86\u3002 */
254
+ .t-team-group-head{display:flex;align-items:center;gap:8px;width:100%;box-sizing:border-box;padding:9px 12px;margin:0 0 8px;border:1px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md,10px);background:var(--dsw-alias-interactive-bg-hover);color:inherit;font:inherit;cursor:pointer;text-align:left}
255
+ .t-team-group-head:hover{border-color:var(--dsw-alias-border-l2)}
256
+ .t-team-group-head[aria-expanded="true"]{background:var(--dsw-alias-bg-layer-1);border-color:var(--dsw-alias-border-l2)}
257
+ .t-team-group-head:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}
258
+ .t-team-group-chevron{margin-left:auto;flex:none;font-size:11px;line-height:1;color:var(--dsw-alias-label-caption)}
259
+ /* \u7EDF\u8BA1\u884C\u4E0E\u300C\u5168\u90E8\u5C55\u5F00 / \u5168\u90E8\u6536\u8D77\u300D\u540C\u4E00\u884C\uFF1A\u5DE6\u7EDF\u8BA1\u3001\u53F3\u6309\u94AE\u3002 */
260
+ .t-team-meta-row{display:flex;align-items:center;justify-content:space-between;gap:10px;min-width:0}
261
+ /* \u5361\u7247\u8FB9\u6846\uFF082026-09-26 \u7528\u6237\u53CD\u9988\u300C\u8FB9\u6846\u592A\u660E\u663E\u4E86\uFF0C\u4E0D\u534F\u8C03\u300D\uFF09\uFF1A
262
+ \u539F\u5148\u300C\u5DF2\u542F\u7528\u300D\u7684\u5361\u7247\u7528 button-primary-fill \u63CF\u8FB9\uFF0C\u4F46\u9010\u4E13\u5BB6\u542F\u505C\u5F00\u5173\u65E9\u5DF2\u4E0B\u7EBF
263
+ \uFF08\u89C1 settings.jsx \u91CC\u90A3\u6BB5\u6CE8\u91CA\uFF09\uFF0C\u540D\u5355\u9ED8\u8BA4\u5168\u90E8\u542F\u7528 \u2014\u2014 \u4E8E\u662F 323 \u5F20\u5361\u65E0\u4E00\u4F8B\u5916\u90FD\u6302\u4E0A
264
+ \u8FD1\u767D\u4E3B\u8272\u63CF\u8FB9\uFF08\u6DF1\u8272\u4E3B\u9898\u4E0B\u5C31\u662F --dsw-static-neutral-bluish-50 = #f9fafb\uFF09\uFF0C
265
+ \u5728 #2c2c2e \u7684\u5361\u7247\u5E95\u4E0A\u4EAE\u6210\u4E00\u6761\u6761\u767D\u6846\uFF0C\u65E2\u6CA1\u6709\u533A\u5206\u610F\u4E49\u53C8\u5F88\u624E\u773C\u3002
266
+ \u73B0\u5728\uFF1A\u5E38\u6001\u7528\u6700\u6DE1\u7684 border-l1\uFF0C\u60AC\u505C\u624D\u62AC\u5230 border-l2\uFF1B\u5361\u7247\u4E0E\u9875\u9762\u7684\u5C42\u6B21\u4EA4\u7ED9
267
+ bg-layer-2 \u672C\u8EAB\u53BB\u8868\u8FBE\u3002\u82E5\u4EE5\u540E\u6062\u590D\u300C\u6309\u4E13\u5BB6\u542F\u505C\u300D\uFF0C\u518D\u6309\u9700\u7ED9\u505C\u7528\u7684\u5361\u7247\u52A0\u533A\u5206
268
+ \uFF08\u522B\u518D\u7528\u4E3B\u8272\u63CF\u8FB9\uFF0C\u5B83\u662F\u7ED9\u6309\u94AE\u7528\u7684\uFF09\u3002 */
269
+ .t-team-card{position:relative;display:grid;min-width:0;min-height:132px;overflow:hidden;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;background:var(--dsw-alias-bg-layer-2)}
270
+ .t-team-card:hover{background:var(--dsw-alias-interactive-bg-hover);border-color:var(--dsw-alias-border-l2)}
613
271
  /* \u53F3\u4E0A\u89D2\u5DE5\u5177\u4F4D\uFF1A\u67E5\u770B\u63D0\u793A\u8BCD + \u542F\u505C\u5F00\u5173 */
614
272
  .t-team-card-tools{position:absolute;top:8px;right:8px;display:flex;align-items:center;gap:6px;z-index:1}
615
273
  .t-team-icon-btn{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;padding:0;border-radius:6px;border:1px solid transparent;background:transparent;color:var(--dsw-alias-label-tertiary);font:inherit;font-size:13px;line-height:1;cursor:pointer}
@@ -627,9 +285,17 @@ var CSS = `
627
285
  .t-team-card-division{font-size:13px;line-height:18px;margin-top:2px;color:var(--dsw-alias-label-secondary)}
628
286
  .t-team-card-slug{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
629
287
  .t-team-card-desc{grid-column:1/-1;margin:0;min-height:60px;font-size:14px;line-height:20px;color:var(--dsw-alias-label-secondary);display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow:hidden}
630
- .t-team-cat-list{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-2);overflow:hidden}
288
+ /* \u5206\u7C7B\u5217\u8868 = \u4E00\u5F20\u5361\u7247\u3002\u8FD9\u662F\u4E24\u8F6E\u53CD\u9988\u5F97\u51FA\u7684\u7ED3\u8BBA\uFF1A
289
+ \u7B2C\u4E00\u8F6E\u300C\u7EBF\u8D34\u4F4F\u4E86\u6309\u94AE\u300D\u2192 \u884C\u9AD8\u653E\u5F00\u5230 40px\uFF1B\u7B2C\u4E8C\u8F6E\u6211\u628A\u5916\u6846\u6574\u4E2A\u5220\u6389 \u2192 \u7528\u6237\u300C\u6CA1\u8FB9\u6846\u4E86\u300D\u3002
290
+ \u4E5F\u5C31\u662F\u8BF4**\u8FB9\u6846\u662F\u8981\u7684**\uFF0C\u6BDB\u75C5\u53EA\u662F\u5B83\u79BB\u5185\u5BB9\u592A\u8FD1\u3002\u73B0\u5728\u6062\u590D 1px \u8FB9\u6846 + 10px \u5706\u89D2 + \u8F7B\u5E95\u8272\uFF0C
291
+ \u5E76\u7ED9\u5217\u8868\u52A0 4px \u5185\u8FB9\u8DDD\uFF1A\u6846\u4E0E\u884C\u4E4B\u95F4 4px\u3001\u884C\u81EA\u5DF1\u8FD8\u6709 6px\uFF0C\u4E00\u5171 10px \u7559\u767D\u3002
292
+ \u89C2\u611F\u4E0E\u9762\u677F\u91CC\u5176\u5B83\u5361\u7247\uFF08\u5206\u7C7B\u6298\u53E0\u5934\u3001\u4E13\u5BB6\u5361\u7247\uFF09\u4E00\u81F4\u3002 */
293
+ .t-team-cat-list{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md,10px);background:var(--dsw-alias-bg-layer-2);padding:4px 0;overflow:hidden}
631
294
  .t-team-cat-list[data-scroll="true"]{max-height:300px;overflow-y:auto}
632
- .t-team-cat-row{display:flex;flex-direction:row;align-items:center;gap:8px;min-height:28px;padding:3px 10px;border-top:1px solid var(--dsw-alias-border-l2)}
295
+ /* 2026-09-28 \u7528\u6237\uFF1A\u300C\u6709 2 \u6761\u6A2A\u7EBF\u90FD\u8D34\u5230\u4E86\u6309\u94AE\u300D\u2014\u2014 \u5206\u7C7B\u5217\u8868\u7684\u5916\u6846\u4E0A\u4E0B\u8FB9\u7D27\u8D34\u884C\u91CC\u90A3\u9897
296
+ \u5B98\u65B9 Button\uFF0828px \u9AD8\uFF09\uFF0C\u800C\u539F\u5148\u884C\u5185\u8FB9\u8DDD\u53EA\u6709 3px\uFF0C\u7B49\u4E8E\u7EBF\u4E0E\u6309\u94AE\u4E4B\u95F4\u6CA1\u6709\u7F1D\u3002
297
+ \u73B0\u5728\u884C\u9AD8 40px\uFF0828 + \u4E0A\u4E0B\u5404 6\uFF09\uFF0C\u7EBF\u5C31\u843D\u5728\u7559\u767D\u91CC\u4E86\u3002 */
298
+ .t-team-cat-row{display:flex;flex-direction:row;align-items:center;gap:8px;min-height:40px;padding:6px 10px;box-sizing:border-box;border-top:1px solid var(--dsw-alias-border-l2)}
633
299
  .t-team-cat-row:first-child{border-top:0}
634
300
  .t-team-cat-row:hover{background:var(--dsw-alias-interactive-bg-hover)}
635
301
  .t-team-cat-name{flex:0 1 auto;min-width:0;font-size:13px;font-weight:600;line-height:18px;color:var(--dsw-alias-label-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@@ -641,10 +307,11 @@ var CSS = `
641
307
  .t-team-cat-empty{padding:8px 10px;font-size:12px;color:var(--dsw-alias-label-caption)}
642
308
  .t-team-cat-list .t-team-badge{flex:none}
643
309
  .t-team-badge[data-tone="custom"]{color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);background:transparent}
644
- /* \u4E13\u5BB6\u5361\u7247\u4E0A\u7684\u542F\u7528\u5F00\u5173\u3002\u26A0\uFE0F class \u5FC5\u987B\u4E0E\u8BBE\u7F6E\u9875\u90A3\u5957**\u9519\u5F00**\uFF1A\u4E24\u5904\u66FE\u5171\u7528 .t-team-switch\uFF0C
310
+ /* \u4E13\u5BB6\u5361\u7247\u4E0A\u7684\u542F\u7528\u5F00\u5173\u3002\u26A0\uFE0F \u7C7B\u540D\u5FC5\u987B**\u81EA\u6210\u4E00\u5957**\uFF1A\u5B83\u66FE\u4E0E\u8BBE\u7F6E\u9875\u90A3\u4E2A\u5F00\u5173\u5171\u7528 .t-team-switch\uFF0C
645
311
  * \u4E8E\u662F\u540E\u5B9A\u4E49\u7684\u8FD9\u5957\uFF0828\xD716\u3001\u5F00\u542F\u8272\u6302\u5728 data-enabled \u4E0A\uFF09\u6574\u4F53\u8986\u76D6\u4E86\u8BBE\u7F6E\u9875\u90A3\u5957
646
312
  * \uFF0838\xD722\u3001\u5F00\u542F\u8272\u6302\u5728 data-on \u4E0A\uFF09\u2014\u2014 \u5BBD\u5EA6\u88AB\u538B\u6210 28px\u3001\u5F00\u542F\u8272\u6C38\u4E0D\u751F\u6548\uFF0C
647
- * \u8BBE\u7F6E\u9875\u90A3\u4E24\u4E2A\u5F00\u5173\u5728\u754C\u9762\u4E0A\u5C31\u53D8\u6210"\u770B\u4E0D\u89C1\u7684\u5C0F\u80F6\u56CA"\u3002\u73B0\u5728\u5404\u7528\u5404\u7684\u540D\u5B57\u3002 */
313
+ * \u754C\u9762\u4E0A\u5C31\u53D8\u6210"\u770B\u4E0D\u89C1\u7684\u5C0F\u80F6\u56CA"\u3002\u8BBE\u7F6E\u9875\u90A3\u4E2A\u5F00\u5173\u5DF2\u968F\u6D3B\u8DC3\u6307\u793A\u8FC1\u53BB dsh-helper-patch
314
+ * \uFF082026-09-28\uFF09\uFF0C\u4F46\u8FD9\u6761\u6559\u8BAD\u7559\u7740\uFF1A\u8FD9\u4E2A\u7C7B\u540D\u4E0D\u8981\u518D\u88AB\u522B\u7684\u5F00\u5173\u590D\u7528\u3002 */
648
315
  .t-team-card-switch{position:relative;flex:none;width:28px;height:16px;padding:0;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);cursor:pointer;transition:background .15s ease,border-color .15s ease}
649
316
  .t-team-card-switch-knob{position:absolute;top:1px;left:1px;width:12px;height:12px;border-radius:50%;background:var(--dsw-alias-label-tertiary);transition:transform .15s ease,background .15s ease}
650
317
  .t-team-card-switch[data-enabled="true"]{background:var(--dsw-alias-button-primary-fill);border-color:var(--dsw-alias-button-primary-fill)}
@@ -652,6 +319,9 @@ var CSS = `
652
319
  .t-team-card-switch:disabled{opacity:.45;cursor:not-allowed}
653
320
  .t-team-conflict{font-size:11px;color:var(--dsw-alias-state-warn-primary)}
654
321
  .t-team-error{font-size:12px;color:var(--dsw-alias-state-error-primary)}
322
+ /* \u540D\u518C\u5065\u5EB7\u63D0\u793A\uFF08sidecar \u7F3A\u5931 / \u6587\u4EF6\u88AB\u8DF3\u8FC7 / \u5206\u7C7B\u8BFB\u4E0D\u51FA\uFF09\uFF1A\u529F\u80FD\u4ECD\u5728\uFF0C\u6240\u4EE5\u4E0D\u662F error\uFF1B
323
+ \u4F46\u5B83\u8BF4\u7684\u90FD\u662F"\u540D\u518C\u6B63\u5728\u9759\u9ED8\u7F3A\u4E1C\u897F"\uFF0C\u6BD4 note \u90A3\u79CD\u7070\u8272\u8BF4\u660E\u5FC5\u987B\u9192\u76EE \u2014\u2014 \u7528\u8B66\u544A\u8272\u3002 */
324
+ .t-team-warn{font-size:12px;line-height:1.6;color:var(--dsw-alias-state-warn-primary)}
655
325
  /* \u9632\u5FA1\uFF1ArootRef <span> \u82E5\u88AB\u7236\u7EA7 flex/grid \u5BB9\u5668\u62C9\u6210\u5168\u5BBD\uFF08\u66FE\u89C1 1265px\uFF09\uFF0C\u6309\u94AE\u4F4D\u7F6E\u4F1A\u6F02\u8D70\u3002
656
326
  position:relative \u7ED9 pop \u63D0\u4F9B left/bottom \u5B9A\u4F4D\u4E0A\u4E0B\u6587\uFF1Bdisplay:inline-flex + align-self:flex-start +
657
327
  max-width:fit-content \u9501\u5B9A\u81EA\u8EAB\u5BBD\u5EA6\u4E3A\u6309\u94AE\u5185\u5BB9\u5BBD\u5EA6\u3002 */
@@ -671,6 +341,14 @@ var CSS = `
671
341
  0 12px 32px \u5411\u4E0B\u504F\u79FB\u7684\u5927\u6295\u5F71\u3002\u7528\u6237\u8981\u6C42\u53BB\u6389\u300C\u5E95\u90E8\u300D\u7684\u6295\u5F71\u2014\u2014\u5373\u7B2C\u4E09\u5C42\u5411\u4E0B 12px \u90A3\u9053
672
342
  \uFF08\u843D\u5728\u8F93\u5165\u6846\u4E0A\u5F88\u660E\u663E\uFF09\uFF0C\u6545\u8FD9\u91CC\u53EA\u4FDD\u7559\u524D\u4E24\u5C42\u5747\u5300\u63CF\u8FB9/\u67D4\u5149\uFF08\u5199\u6B7B\u6570\u503C\uFF0C\u5BBF\u4E3B\u6CA1\u6709\u53EA\u542B\u524D\u4E24\u5C42\u7684\u53D8\u91CF\uFF09\u3002
673
343
  Retina \u4E0A .5px \u6B63\u597D\u662F\u4E00\u4E2A\u7269\u7406\u50CF\u7D20\uFF1B\u5BBF\u4E3B\u81EA\u5DF1\u7684\u6309\u94AE\u4E5F\u7528 .5px\u3002 */
344
+ /* \u6D6E\u5C42\u6750\u8D28\uFF082026-09-22\uFF09\uFF1A\u5BBF\u4E3B 0.1.7 \u7684\u89C6\u89C9\u7EDF\u4E00\u6539\u7248\u628A --dsw-specific-menu \u4ECE
345
+ \u300C= \u4E0D\u900F\u660E\u7684 --dsw-alias-bg-layer-3\u300D\u6539\u6210\u4E86**\u534A\u900F\u660E\u83DC\u5355\u6750\u8D28**
346
+ \uFF08\u4EAE\u8272 rgba(248,249,250,.58) / \u6697\u8272 rgba(48,49,54,.5)\uFF09\uFF0C\u5E76\u914D\u5957
347
+ --dsw-menu-backdrop-filter = blur(40px) saturate(150%) \u624D\u6709\u6BDB\u73BB\u7483\u89C2\u611F\u3002
348
+ \u6240\u4EE5\u51E1\u662F background \u7528\u4E86 --dsw-specific-menu \u7684\u89C4\u5219\uFF0C\u90FD\u5FC5\u987B\u7D27\u8DDF\u4E00\u6761 backdrop-filter\uFF0C
349
+ \u5426\u5219\u5728 0.1.7 \u4E0A\u5C31\u662F\u300C\u5F39\u7A97\u534A\u900F\u660E\u3001\u80FD\u770B\u89C1\u5E95\u4E0B\u7684\u5BF9\u8BDD\u300D\uFF08\u7528\u6237\u5B9E\u6D4B\u62A5\u969C\uFF09\uFF1B\u65E7\u5BBF\u4E3B\u4E0A\u8BE5\u53D8\u91CF
350
+ \u4ECD\u662F\u4E0D\u900F\u660E\u8272\uFF0C\u5E26\u4E86 filter \u4E5F\u6CA1\u6709\u526F\u4F5C\u7528\uFF08\u80CC\u540E\u5185\u5BB9\u672C\u6765\u5C31\u770B\u4E0D\u89C1\uFF09\u3002
351
+ fallback \u7528\u56FA\u5B9A\u7684 blur \u503C\uFF0C\u53EA\u662F\u4E3A\u4E86\u5728\u62FF\u4E0D\u5230\u5BBF\u4E3B\u53D8\u91CF\u7684\u5BBF\u4E3B\u4E0A\u4E5F\u4E0D\u81F3\u4E8E\u5B8C\u5168\u6CA1\u6750\u8D28\u3002 */
674
352
  .t-team-pop{position:fixed;z-index:60;width:var(--t-team-pop-w, 720px);max-width:calc(100vw - 16px);max-height:min(600px, calc(100vh - 16px));overflow:hidden;box-sizing:border-box;display:flex;flex-direction:column;gap:8px;padding:10px 0 10px 10px;border-radius:12px;border:.5px solid var(--dsw-alias-border-l1);background:var(--dsw-specific-menu, var(--dsw-alias-bg-layer-3));backdrop-filter:var(--dsw-menu-backdrop-filter,blur(20px) saturate(1.4));box-shadow:0 0 1px 0 #0003, 0 0 4px 0 #00000005;color:var(--dsw-alias-label-primary)}
675
353
  /* \u4E0D\u5199 width:100%\uFF1A\u5B83\u91CF\u7684\u662F\u5BB9\u5668 content \u5BBD\uFF0C\u518D\u53E0\u4E0A\u300C> *\u300D\u7684 margin-right \u5C31\u4F1A\u6A2A\u5411\u6EA2\u51FA\uFF1B
676
354
  \u9760 flex \u5BB9\u5668\u9ED8\u8BA4\u7684 align-items:stretch \u81EA\u52A8\u6491\u6EE1\u51CF\u6389 margin \u540E\u7684\u5BBD\u5EA6\u3002 */
@@ -846,36 +524,45 @@ var CSS = `
846
524
  .t-team-expert-pick-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
847
525
  .t-team-expert-pick-meta{flex:none;font-size:11px;color:var(--dsw-alias-label-tertiary)}
848
526
  .t-team-expert-pick-empty{font-size:12px;color:var(--dsw-alias-label-tertiary);padding:6px}
527
+
528
+ /* ---- \u6280\u80FD\u5F00\u5173\uFF082026-09-28 \u4ECE dsh-plugin-skill-gate \u5E76\u5165\uFF09----
529
+ \u53EA\u7559\u5E03\u5C40\u7C7B\uFF1A\u6309\u94AE / \u8F93\u5165\u6846 / \u4E0B\u62C9 / \u72B6\u6001\u7B5B\u9009 / \u5F00\u5173\u90FD\u6362\u6210\u4E86\u5B98\u65B9 primitives \u7684\u7EC4\u4EF6\uFF0C
530
+ \u90A3\u51E0\u5957 sg-* \u63A7\u4EF6\u6837\u5F0F\u968F\u4E4B\u4E0B\u7EBF\uFF08\u5B98\u65B9\u7684\u5728\u5B83\u81EA\u5DF1\u7684 CSS Module \u91CC\uFF09\u3002 */
531
+
532
+ .sg-settings{display:flex;flex-direction:column;gap:12px;min-height:0}
533
+ .sg-head{display:flex;flex-direction:column;gap:4px}
534
+ .sg-meta{font-size:12px;color:var(--dsw-alias-label-caption)}
535
+ .sg-hint{font-size:12px;line-height:1.45;color:var(--dsw-alias-label-secondary)}
536
+ .sg-error{font-size:12px;color:var(--dsw-alias-state-error-primary);line-height:1.4}
537
+ .sg-toolbar{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
538
+ .sg-chips{display:flex;flex-wrap:wrap;gap:6px;align-items:center}
539
+ .sg-filter-count{font-size:12px;color:var(--dsw-alias-label-caption);margin-left:auto}
540
+ .sg-actions{display:flex;flex-wrap:wrap;gap:6px}
541
+ .sg-groups{display:flex;flex-direction:column;gap:14px;min-height:0}
542
+ .sg-group{display:flex;flex-direction:column;gap:8px}
543
+ .sg-group-head{display:flex;align-items:baseline;justify-content:space-between;gap:8px;font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary)}
544
+ .sg-list{display:flex;flex-direction:column;gap:8px}
545
+ .sg-row{display:flex;flex-direction:column;gap:4px;padding:9px 11px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-2)}
546
+ .sg-row[data-off="true"]{opacity:.72}
547
+ .sg-row-head{display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0}
548
+ .sg-name{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--dsw-font-markdown-code-block-small, ui-monospace, Menlo, monospace)}
549
+ .sg-desc{font-size:12px;line-height:1.4;color:var(--dsw-alias-label-caption)}
550
+ .sg-badge{display:inline-block;margin-left:6px;padding:0 6px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);font-size:10px;line-height:16px;vertical-align:middle;color:var(--dsw-alias-label-caption);font-weight:500}
551
+ .sg-empty{font-size:13px;color:var(--dsw-alias-label-caption);padding:16px 4px}
849
552
  `;
850
553
 
851
554
  // src/client/i18n.js
852
555
  var zh = {
853
556
  "settings.title": "T\u4E13\u5BB6",
854
- "settings.uiPrefsUnreadable": "\u8BFB\u53D6\u8BE5\u8BBE\u7F6E\u5931\u8D25",
855
- "settings.uiPrefsWriteFailed": "\u4FDD\u5B58\u8BE5\u8BBE\u7F6E\u5931\u8D25",
856
- "settings.showActivePulse": "\u663E\u793A\u6D3B\u8DC3\u6307\u793A",
857
- "settings.showActivePulseHint": "\u4F1A\u8BDD\u6807\u9898\u680F\u91CC\u90A3\u679A\u5FC3\u7535\u56FE\u56FE\u6807\uFF1A\u6709\u4F1A\u8BDD\u6B63\u5728\u8DD1\u3001\u6216\u6709\u4E8B\u7B49\u4F60\u67E5\u770B\u65F6\u663E\u793A\u5F69\u8272\uFF0C\u5168\u90E8\u7A7A\u95F2\u4E14\u65E0\u5F85\u770B\u65F6\u663E\u793A\u7070\u8272\u3002\u70B9\u5B83\u53EF\u4EE5\u5217\u51FA\u9700\u8981\u6CE8\u610F\u7684\u4F1A\u8BDD\uFF0C\u70B9\u5176\u4E2D\u4E00\u6761\u5C31\u76F4\u63A5\u63A5\u4E0A\u90A3\u4E2A\u4F1A\u8BDD",
858
- "pulse.on": "\u6709\u4F1A\u8BDD\u6B63\u5728\u6D3B\u52A8\uFF0C\u6216\u6709\u4E8B\u7B49\u4F60\u67E5\u770B",
859
- "pulse.off": "\u5168\u90E8\u7A7A\u95F2\uFF0C\u4E14\u6CA1\u6709\u5F85\u4F60\u67E5\u770B\u7684",
860
- "pulse.title": "\u9700\u8981\u6CE8\u610F\u7684\u4F1A\u8BDD",
861
- "pulse.empty": "\u73B0\u5728\u6CA1\u6709\u9700\u8981\u6CE8\u610F\u7684\u4F1A\u8BDD",
862
- "pulse.running": "\u8FD0\u884C\u4E2D",
863
- "pulse.pending": "\u5F85\u67E5\u770B",
864
- "pulse.current": "\u5F53\u524D\u4F1A\u8BDD",
865
- "pulse.copy": "\u70B9\u6B64\u590D\u5236",
866
- "pulse.copyHint": "\u590D\u5236\u4F1A\u8BDD id",
867
- "pulse.copied": "\u5DF2\u590D\u5236",
868
- "pulse.idPending": "\u6682\u65E0 id",
869
- "pulse.idPendingHint": "\u5F53\u524D\u4F1A\u8BDD\u7684 id \u5C1A\u672A\u5C31\u7EEA",
557
+ "settings.summary": "\u4E13\u5BB6\u540D\u518C\u3001\u5206\u7C7B\u7BA1\u7406\u4E0E @ \u53EC\u5524",
558
+ "group.expandAll": "\u5168\u90E8\u5C55\u5F00",
559
+ "group.collapseAll": "\u5168\u90E8\u6536\u8D77",
560
+ "group.toggle": "\u5C55\u5F00\u6216\u6536\u8D77\u300C{name}\u300D",
870
561
  "settings.loading": "\u6B63\u5728\u8BFB\u53D6\u540D\u518C\u2026",
871
562
  "settings.retry": "\u91CD\u8BD5",
872
563
  "settings.search": "\u641C\u7D22\u4E13\u5BB6\u540D\u6216\u7B80\u4ECB",
873
564
  "filter.division": "\u5206\u7C7B",
874
- "filter.status": "\u72B6\u6001",
875
565
  "filter.allDivisions": "\u5168\u90E8\u5206\u7C7B",
876
- "filter.allStatus": "\u5168\u90E8\u72B6\u6001",
877
- "filter.statusOn": "\u5DF2\u542F\u7528",
878
- "filter.statusOff": "\u672A\u542F\u7528",
879
566
  "filter.clear": "\u6E05\u9664\u7B5B\u9009",
880
567
  "filter.summary": "\u663E\u793A {shown} / {total}",
881
568
  "settings.enabledCount": "\u5171 {total} \u4F4D\u4E13\u5BB6",
@@ -887,8 +574,6 @@ var zh = {
887
574
  "card.promptFailed": "\u8BFB\u53D6\u63D0\u793A\u8BCD\u5931\u8D25\uFF1A{detail}",
888
575
  "card.promptEmpty": "\u8BE5\u4E13\u5BB6\u6CA1\u6709\u53EF\u663E\u793A\u7684\u63D0\u793A\u8BCD\u6B63\u6587\u3002",
889
576
  "card.close": "\u5173\u95ED",
890
- "settings.enable": "\u542F\u7528",
891
- "settings.disable": "\u505C\u7528",
892
577
  "settings.saveFailed": "\u4FDD\u5B58\u5931\u8D25\uFF1A{detail}",
893
578
  "settings.loadFailed": "\u8BFB\u53D6\u540D\u518C\u5931\u8D25\uFF1A{detail}",
894
579
  "settings.conflictRetry": "\u8BBE\u7F6E\u5DF2\u88AB\u5176\u4ED6\u7A97\u53E3\u4FEE\u6539\uFF0C\u5DF2\u91CD\u65B0\u8BFB\u53D6\u540D\u518C\uFF0C\u8BF7\u91CD\u8BD5\u3002",
@@ -911,6 +596,12 @@ var zh = {
911
596
  "pop.skillNoMatch": "\u6CA1\u6709\u5339\u914D\u7684\u6280\u80FD\u3002",
912
597
  "pop.skillUserOnly": "\u4EC5\u7528\u6237",
913
598
  "roster.expertsHint": "\u540D\u518C\uFF1A\u80FD\u88AB @ \u53EC\u5524\u7684\u4E13\u5BB6\u3002\u5E26\u300C\u81EA\u5EFA\u300D\u6807\u8BB0\u7684\u53EF\u4EE5\u5728\u8FD9\u91CC\u6539\u6216\u5220\u3002",
599
+ // 名册健康提示(数据来自快照的 sidecar 字段)。三种情况都是**静默**的:
600
+ // 侧车缺失 → 名册整个变英文;文件被跳过 → 专家凭空少几位;分类目录读不出 → 那一类不出现。
601
+ // host 一直在报,但 0.4.6 之前 schema 没声明 sidecar、zod 把它剥掉了,面板收不到。
602
+ "roster.sidecarMissing": "\u4E2D\u6587\u4FA7\u8F66\u6CA1\u627E\u5230\uFF08{path}\uFF09\uFF1A\u540D\u518C\u4F1A\u5168\u90E8\u9000\u56DE\u82F1\u6587\u540D\u3002",
603
+ "roster.skippedFiles": "\u6709 {count} \u4E2A\u4E13\u5BB6\u6587\u4EF6\u56E0\u683C\u5F0F\u95EE\u9898\u88AB\u8DF3\u8FC7\uFF0C\u540D\u518C\u91CC\u4F1A\u5C11\u8FD9\u51E0\u4F4D\u3002",
604
+ "roster.unreadableDivisions": "\u8FD9\u4E9B\u5206\u7C7B\u76EE\u5F55\u8BFB\u4E0D\u51FA\u6765\uFF1A{list}\u3002",
914
605
  "custom.new": "\uFF0B \u65B0\u5EFA\u4E13\u5BB6",
915
606
  "custom.titleNew": "\u65B0\u5EFA\u4E13\u5BB6",
916
607
  "custom.titleEdit": "\u7F16\u8F91\u300C{name}\u300D",
@@ -949,39 +640,63 @@ var zh = {
949
640
  "custom.deleteHint": "\u5220\u9664\u300C{name}\u300D\uFF1F\u53EA\u5220\u8FD9\u4E2A\u81EA\u5EFA\u4E13\u5BB6\u6587\u4EF6\uFF0C\u4E0D\u53EF\u64A4\u9500\u3002",
950
641
  "custom.badge": "\u81EA\u5EFA",
951
642
  "custom.loadingBody": "\u6B63\u5728\u8BFB\u53D6\u4EBA\u683C\u6B63\u6587\u2026",
952
- "pop.title": "T\u4E13\u5BB6"
643
+ "pop.title": "T\u4E13\u5BB6",
953
644
  // 列表过滤与搜索(与官方「自动化任务」页同一套:标签行 + 搜索框)
954
645
  // 三档:全部 / 已启用 / 已停用 —— 停用 ≠ 删除(停用的任务保留数据、只是不触发)。
955
646
  // 运行时间卡片(对齐官方详情页的「运行时间」:重复 / 时间 / 星期 三行)
956
647
  // 周几:键必须**逐行**写、并且在代码里以字面量引用(字典无人引用自检会盯着这一点)。
648
+ /* ---- 技能开关(2026-09-28 从 dsh-plugin-skill-gate 并入)----
649
+ 键名统一加 `sg.` 前缀:两套字典现在住在同一个命名空间(t-team)里,不加前缀会与
650
+ 专家/分类那边的 settings.* / filter.* 直接撞车。 */
651
+ "sg.settings.meta": "{scope}\uFF1A\u6A21\u578B\u53EF\u89C1 {visible} \xB7 \u5DF2\u5173 {gated} \xB7 \u539F\u751F\u5173\u95ED {native} \xB7 \u5171 {total} \xB7 v{version}",
652
+ "sg.settings.hint": "\u5173\u6389\u7684\u6280\u80FD\u4E0D\u4F1A\u8FDB\u5165\u6BCF\u6B21\u8BF7\u6C42\u7684\u6280\u80FD\u6E05\u5355\uFF0C\u4ECE\u800C\u5C11\u5360\u4E0A\u4E0B\u6587\u3002\u659C\u6760\u83DC\u5355\u91CC\u4ECD\u53EF\u624B\u52A8\u70B9\u3002\u4E0B\u4E00\u8F6E\u5BF9\u8BDD\u751F\u6548\uFF0C\u4E0D\u7528\u91CD\u542F\u3002\u5217\u8868\u662F\u5404\u6765\u6E90\u7684\u5E76\u96C6\uFF1B\u5207\u6362\u89C6\u89D2\u53EF\u770B\u67D0\u4E2A\u4F1A\u8BDD\u6216\u9ED8\u8BA4 preset \u5B9E\u9645\u8BFB\u5230\u54EA\u4E9B\u6280\u80FD\u3002",
653
+ "sg.settings.search": "\u641C\u7D22\u6280\u80FD\u540D\u6216\u7B80\u4ECB",
654
+ "sg.filter.all": "\u5168\u90E8",
655
+ "sg.filter.on": "\u6A21\u578B\u53EF\u89C1",
656
+ "sg.filter.off": "\u5DF2\u5173\u95ED",
657
+ "sg.filter.native": "\u539F\u751F\u5173\u95ED",
658
+ "sg.filter.source": "\u6765\u6E90",
659
+ "sg.filter.status": "\u72B6\u6001",
660
+ "sg.settings.unavailable": "\u6280\u80FD\u5F00\u5173\u670D\u52A1\u4E0D\u53EF\u7528\uFF1A\u5BBF\u4E3B\u6CA1\u6302\u4E0A skillGate \u8FDC\u7A0B\u670D\u52A1\uFF08\u91CD\u542F DSH \u540E\u4ECD\u672A\u51FA\u73B0\u7684\u8BDD\uFF0C\u68C0\u67E5\u63D2\u4EF6\u662F\u5426\u52A0\u8F7D\u6210\u529F\uFF09\u3002",
661
+ "sg.filter.view": "\u89C6\u89D2",
662
+ "sg.filter.allSources": "\u5168\u90E8\u6765\u6E90",
663
+ "sg.view.all": "\u5168\u90E8\u6765\u6E90\uFF08\u5E76\u96C6\uFF09",
664
+ "sg.view.host": "\u5BBF\u4E3B\u5168\u5C40\u5C42",
665
+ "sg.view.preset": "\u9ED8\u8BA4 preset \xB7 {name}",
666
+ "sg.view.agent": "\u4F1A\u8BDD \xB7 {name}",
667
+ "sg.view.disk": "\u78C1\u76D8\u626B\u63CF\uFF08~/.dsh/skills \u4E0E ~/.agents/skills\uFF09",
668
+ "sg.view.option": "{label}\uFF08{visible}/{total}\uFF09",
669
+ "sg.source.user-dsh": "\u7528\u6237\u6280\u80FD\uFF08~/.dsh/skills\uFF09",
670
+ "sg.source.user-agents": "\u7528\u6237\u6280\u80FD\uFF08~/.agents\uFF09",
671
+ "sg.source.bundled": "\u968F\u5305\u6280\u80FD",
672
+ "sg.source.project-dsh": "\u9879\u76EE .dsh",
673
+ "sg.source.project-agents": "\u9879\u76EE .agents",
674
+ "sg.source.custom": "\u81EA\u5B9A\u4E49\u76EE\u5F55",
675
+ "sg.source.runtime": "\u8FD0\u884C\u65F6",
676
+ "sg.source.unknown": "\u672A\u77E5\u6765\u6E90",
677
+ "sg.actions.enableFiltered": "\u542F\u7528\u5F53\u524D\u7B5B\u9009",
678
+ "sg.actions.disableFiltered": "\u505C\u7528\u5F53\u524D\u7B5B\u9009",
679
+ "sg.actions.needSearch": "\u5148\u5728\u641C\u7D22\u6846\u8F93\u5165\u5173\u952E\u8BCD\uFF0C\u624D\u80FD\u6279\u91CF\u542F\u7528\u6216\u505C\u7528\u5F53\u524D\u7ED3\u679C\u3002",
680
+ "sg.settings.filterCount": "\u5F53\u524D\u5217\u51FA {shown}\uFF08{groups} \u7EC4\uFF09",
681
+ "sg.settings.empty": "\u6CA1\u6709\u5339\u914D\u7684\u6280\u80FD\u3002",
682
+ "sg.settings.loading": "\u6B63\u5728\u8BFB\u53D6\u6280\u80FD\u6E05\u5355\u2026",
683
+ "sg.settings.retry": "\u91CD\u8BD5",
684
+ "sg.settings.loadFailed": "\u8BFB\u53D6\u5931\u8D25\uFF1A{detail}",
685
+ "sg.settings.saveFailed": "\u4FDD\u5B58\u5931\u8D25\uFF1A{detail}",
686
+ "sg.settings.conflict": "\u8BBE\u7F6E\u5DF2\u88AB\u5176\u4ED6\u7A97\u53E3\u4FEE\u6539\uFF0C\u5DF2\u91CD\u65B0\u8BFB\u53D6\uFF0C\u8BF7\u91CD\u8BD5\u3002",
687
+ "sg.settings.nativeOff": "\u6280\u80FD\u6587\u4EF6\u91CC\u5DF2\u58F0\u660E disable-model-invocation\uFF0C\u4E0D\u80FD\u7528\u672C\u63D2\u4EF6\u6253\u5F00\u3002"
957
688
  };
958
689
  var en = {
959
690
  "settings.title": "T Expert",
960
- "settings.showActivePulse": "Show activity indicator",
961
- "settings.showActivePulseHint": "The ECG icon in the session header: coloured when a session is running or something awaits your review, grey when everything is idle. Click it to list the sessions that need attention and jump straight into one",
962
- "pulse.on": "A session is running, or something awaits your review",
963
- "pulse.off": "Everything is idle and nothing awaits you",
964
- "pulse.title": "Sessions needing attention",
965
- "pulse.empty": "Nothing needs your attention right now",
966
- "pulse.running": "Running",
967
- "pulse.pending": "Unread",
968
- "pulse.current": "Current session",
969
- "pulse.copy": "Copy",
970
- "pulse.copyHint": "Copy session id",
971
- "pulse.copied": "Copied",
972
- "pulse.idPending": "no id yet",
973
- "pulse.idPendingHint": "Current session id not ready",
974
- "settings.uiPrefsUnreadable": "Failed to read this setting",
975
- "settings.uiPrefsWriteFailed": "Failed to save this setting",
691
+ "settings.summary": "Expert roster (323 experts \xB7 22 divisions), category management and @ summoning",
692
+ "group.expandAll": "Expand all",
693
+ "group.collapseAll": "Collapse all",
694
+ "group.toggle": "Expand or collapse \u201C{name}\u201D",
976
695
  "settings.loading": "Loading roster\u2026",
977
696
  "settings.retry": "Retry",
978
697
  "settings.search": "Search expert name or description",
979
698
  "filter.division": "Division",
980
- "filter.status": "Status",
981
699
  "filter.allDivisions": "All divisions",
982
- "filter.allStatus": "All statuses",
983
- "filter.statusOn": "Enabled",
984
- "filter.statusOff": "Disabled",
985
700
  "filter.clear": "Clear filters",
986
701
  "filter.summary": "Showing {shown} / {total}",
987
702
  "settings.enabledCount": "{total} experts",
@@ -993,8 +708,6 @@ var en = {
993
708
  "card.promptFailed": "Failed to load prompt: {detail}",
994
709
  "card.promptEmpty": "This expert has no prompt body to show.",
995
710
  "card.close": "Close",
996
- "settings.enable": "Enable",
997
- "settings.disable": "Disable",
998
711
  "settings.saveFailed": "Save failed: {detail}",
999
712
  "settings.loadFailed": "Failed to load roster: {detail}",
1000
713
  "settings.conflictRetry": "Settings changed elsewhere; roster reloaded, please retry.",
@@ -1017,6 +730,12 @@ var en = {
1017
730
  "pop.skillNoMatch": "No matching skill.",
1018
731
  "pop.skillUserOnly": "user-only",
1019
732
  "roster.expertsHint": "The roster: experts available for @-mention. Custom ones can be edited or deleted here.",
733
+ // Roster health (from the snapshot's `sidecar` field). All three fail silently:
734
+ // missing sidecar → the whole roster falls back to English; skipped files → experts
735
+ // simply absent; unreadable division → that category never shows up.
736
+ "roster.sidecarMissing": "Chinese sidecar not found ({path}): the roster falls back to English names.",
737
+ "roster.skippedFiles": "{count} expert file(s) were skipped due to format problems and are missing from the roster.",
738
+ "roster.unreadableDivisions": "These division directories could not be read: {list}.",
1020
739
  "custom.new": "\uFF0B New expert",
1021
740
  "custom.titleNew": "New expert",
1022
741
  "custom.titleEdit": "Edit \u201C{name}\u201D",
@@ -1055,10 +774,47 @@ var en = {
1055
774
  "custom.deleteHint": "Delete \u201C{name}\u201D? Only this custom expert file is removed; this cannot be undone.",
1056
775
  "custom.badge": "Custom",
1057
776
  "custom.loadingBody": "Loading persona body\u2026",
1058
- "pop.title": "T Expert"
777
+ "pop.title": "T Expert",
1059
778
  // List filter and search (same shape as the built-in Automation page: tab row + search field).
1060
779
  // Three lanes: All / Active / Disabled — disabled keeps its data and simply never fires.
1061
780
  // Run-time card (mirrors the built-in page's "Run time": repeat / time / weekday rows)
781
+ "sg.settings.meta": "{scope}: visible {visible} \xB7 gated {gated} \xB7 native-off {native} \xB7 total {total} \xB7 v{version}",
782
+ "sg.settings.hint": "Off skills leave the per-request catalog (saving context). They stay in the slash menu. Takes effect on the next model turn, no restart. The list is the union of every source; switch views to see what one session or the default preset actually reads.",
783
+ "sg.settings.search": "Search skill name or description",
784
+ "sg.filter.all": "All",
785
+ "sg.filter.on": "Visible",
786
+ "sg.filter.off": "Gated",
787
+ "sg.filter.native": "Natively off",
788
+ "sg.filter.source": "Source",
789
+ "sg.filter.status": "Status",
790
+ "sg.settings.unavailable": "Skill gate service unavailable: the skillGate remote is not mounted (if it persists after restarting DSH, check that the plugin loaded).",
791
+ "sg.filter.view": "View",
792
+ "sg.filter.allSources": "All sources",
793
+ "sg.view.all": "All sources (union)",
794
+ "sg.view.host": "Host layer",
795
+ "sg.view.preset": "Default preset \xB7 {name}",
796
+ "sg.view.agent": "Session \xB7 {name}",
797
+ "sg.view.disk": "Disk scan (~/.dsh/skills and ~/.agents/skills)",
798
+ "sg.view.option": "{label} ({visible}/{total})",
799
+ "sg.source.user-dsh": "User skills (~/.dsh/skills)",
800
+ "sg.source.user-agents": "User skills (~/.agents)",
801
+ "sg.source.bundled": "Bundled",
802
+ "sg.source.project-dsh": "Project .dsh",
803
+ "sg.source.project-agents": "Project .agents",
804
+ "sg.source.custom": "Custom dirs",
805
+ "sg.source.runtime": "Runtime",
806
+ "sg.source.unknown": "Unknown source",
807
+ "sg.actions.enableFiltered": "Enable filtered",
808
+ "sg.actions.disableFiltered": "Disable filtered",
809
+ "sg.actions.needSearch": "Type a search query first to enable or disable the filtered results.",
810
+ "sg.settings.filterCount": "Showing {shown} ({groups} groups)",
811
+ "sg.settings.empty": "No matching skills.",
812
+ "sg.settings.loading": "Loading skills\u2026",
813
+ "sg.settings.retry": "Retry",
814
+ "sg.settings.loadFailed": "Failed to load: {detail}",
815
+ "sg.settings.saveFailed": "Failed to save: {detail}",
816
+ "sg.settings.conflict": "Updated in another window; reloaded. Please retry.",
817
+ "sg.settings.nativeOff": "This skill already sets disable-model-invocation and cannot be turned on here."
1062
818
  };
1063
819
 
1064
820
  // src/client/insert.js
@@ -20821,6 +20577,22 @@ var catalogSnapshotSchema = external_exports.object({
20821
20577
  // 漏声明时面板拿到的快照里根本没有它们,界面上表现为"分区标签是空的"。
20822
20578
  customDivision: external_exports.string().optional(),
20823
20579
  customDivisionLabel: external_exports.string().optional(),
20580
+ /**
20581
+ * 名册健康状态。
20582
+ *
20583
+ * ⚠️ 这个字段在 0.4.6 之前**一直没声明**:host 侧早就在快照里返回它、注释也写着
20584
+ * 「面板可以据此提示『中文侧车没找到 / 有 N 个专家文件被跳过』」,但 zod 会把它剥掉 ——
20585
+ * 面板永远收不到。于是"中文侧车没找到"这种会让整个名册**静默变全英文**的状况,
20586
+ * 用户在界面上看不到任何线索,只能对着一片英文名猜。
20587
+ * 与上面 `customDivision` 那两个字段是同一个坑(那里的注释已经警告过"漏声明会被剥掉"),
20588
+ * 只是这次踩在一个嵌套对象上,更不容易被发现。
20589
+ */
20590
+ sidecar: external_exports.object({
20591
+ zhRoot: external_exports.string(),
20592
+ present: external_exports.boolean(),
20593
+ skippedFiles: external_exports.number().int().min(0),
20594
+ unreadableDivisions: external_exports.array(external_exports.string())
20595
+ }).optional(),
20824
20596
  /** 可选的落点分类(面板「分类」下拉与管理页):官方在前,其余按目录/声明发现。 */
20825
20597
  categories: external_exports.array(external_exports.object({
20826
20598
  key: external_exports.string(),
@@ -20834,31 +20606,82 @@ var enabledStateSchema = external_exports.object({
20834
20606
  enabled: external_exports.array(external_exports.string()),
20835
20607
  revision: external_exports.number().int().min(0)
20836
20608
  });
20837
- var uiPrefsSchema = external_exports.object({
20838
- // 窗口右上角「活跃指示」是否显示。宿主**总是**返回布尔(缺省按 true 算),所以这里必填。
20839
- showActivePulse: external_exports.boolean()
20609
+ var promptSchema = external_exports.object({ prompt: external_exports.string() });
20610
+
20611
+ // lib/skill-gate/remote-schemas.js
20612
+ var MAX_VIEWS = 256;
20613
+ var skillRowSchema = external_exports.object({
20614
+ name: external_exports.string().min(1).max(128),
20615
+ description: external_exports.string().max(400),
20616
+ source: external_exports.string().min(1).max(64),
20617
+ provider: external_exports.string().min(1).max(64),
20618
+ nativeModel: external_exports.boolean(),
20619
+ nativeUser: external_exports.boolean(),
20620
+ gated: external_exports.boolean(),
20621
+ modelVisible: external_exports.boolean(),
20622
+ /** 这条技能出现在哪些视角里;面板按视角过滤时用它。 */
20623
+ views: external_exports.array(external_exports.string().min(1).max(128)).max(MAX_VIEWS)
20624
+ });
20625
+ var countsSchema = external_exports.object({
20626
+ total: external_exports.number().int().min(0),
20627
+ modelVisible: external_exports.number().int().min(0),
20628
+ gated: external_exports.number().int().min(0),
20629
+ nativeOff: external_exports.number().int().min(0)
20840
20630
  });
20841
- var uiPrefsPatchSchema = external_exports.object({
20842
- showActivePulse: external_exports.boolean().optional()
20631
+ var viewRowSchema = external_exports.object({
20632
+ key: external_exports.string().min(1).max(128),
20633
+ kind: external_exports.string().min(1).max(32),
20634
+ name: external_exports.string().max(120),
20635
+ total: external_exports.number().int().min(0),
20636
+ modelVisible: external_exports.number().int().min(0),
20637
+ gated: external_exports.number().int().min(0),
20638
+ nativeOff: external_exports.number().int().min(0)
20639
+ });
20640
+ var snapshotSchema = external_exports.object({
20641
+ revision: external_exports.number().int().min(0),
20642
+ storePath: external_exports.string().min(1).max(1024),
20643
+ skills: external_exports.array(skillRowSchema).max(4e3),
20644
+ counts: countsSchema,
20645
+ views: external_exports.array(viewRowSchema).max(MAX_VIEWS)
20843
20646
  });
20844
- var promptSchema = external_exports.object({ prompt: external_exports.string() });
20845
20647
 
20846
20648
  // src/client/remote.js
20847
- function direct(method, parameters, typeSymbol, schema) {
20649
+ function directIn(namespace, method, parameters, typeSymbol, schema) {
20848
20650
  return {
20849
- id: `${PLUGIN_ID}#tTeam/${method}`,
20850
- service: "tTeam",
20851
- namespace: "tTeam",
20651
+ id: `${PLUGIN_ID}#${namespace}/${method}`,
20652
+ service: namespace,
20653
+ namespace,
20852
20654
  method,
20853
20655
  invocation: { kind: "direct" },
20854
20656
  parameters,
20855
20657
  result: { mode: "strict", typeSymbol, schema, create: () => schema }
20856
20658
  };
20857
20659
  }
20660
+ function direct(method, parameters, typeSymbol, schema) {
20661
+ return directIn("tTeam", method, parameters, typeSymbol, schema);
20662
+ }
20858
20663
  var TYPERT_REMOTE = {
20859
20664
  package: PLUGIN_ID,
20860
20665
  descriptors: [
20861
20666
  direct("getCatalog", [], "TTeamCatalog", catalogSnapshotSchema),
20667
+ // ---- 技能开关(2026-09-28 从 dsh-plugin-skill-gate 并入)----
20668
+ // 挂在同一个 tTeam 命名空间下:合并时先做成独立入口(skillGate),实机上没能挂上
20669
+ // (`ctx.get("remote.skillGate")` 始终 undefined),改走这条已被证明可用的通路。
20670
+ direct("getSnapshot", [], "SkillGateSnapshot", snapshotSchema),
20671
+ direct("setDisabled", [
20672
+ {
20673
+ name: "disabled",
20674
+ wire: "disabled",
20675
+ source: "json",
20676
+ codec: { mode: "strict", typeSymbol: "string[]", schema: external_exports.array(external_exports.string().max(128)).max(4e3), create: () => external_exports.array(external_exports.string().max(128)).max(4e3) }
20677
+ },
20678
+ {
20679
+ name: "expectedRevision",
20680
+ wire: "expectedRevision",
20681
+ source: "json",
20682
+ codec: { mode: "strict", typeSymbol: "number", schema: external_exports.number().int().min(0), create: () => external_exports.number().int().min(0) }
20683
+ }
20684
+ ], "SkillGateSnapshot", snapshotSchema),
20862
20685
  direct("setEnabled", [
20863
20686
  { name: "enabled", wire: "enabled", source: "json", codec: { mode: "strict", typeSymbol: "string[]", schema: external_exports.array(external_exports.string()), create: () => external_exports.array(external_exports.string()) } },
20864
20687
  { name: "expectedRevision", wire: "expectedRevision", source: "json", codec: { mode: "strict", typeSymbol: "number", schema: external_exports.number().int().min(0), create: () => external_exports.number().int().min(0) } }
@@ -20867,12 +20690,6 @@ var TYPERT_REMOTE = {
20867
20690
  { name: "slug", wire: "slug", source: "json", codec: { mode: "strict", typeSymbol: "string", schema: external_exports.string().min(1).max(128), create: () => external_exports.string().min(1).max(128) } },
20868
20691
  { name: "division", wire: "division", source: "json", codec: { mode: "strict", typeSymbol: "string", schema: external_exports.string().min(1).max(64), create: () => external_exports.string().min(1).max(64) } }
20869
20692
  ], "TTeamPrompt", promptSchema),
20870
- direct("getUiPrefs", [], "TTeamUiPrefs", uiPrefsSchema),
20871
- // 入参是 patch 对象(与 host 侧 descriptor 一致):界面偏好不止一项,
20872
- // 加键不必再加方法。
20873
- direct("setUiPrefs", [
20874
- { name: "patch", wire: "patch", source: "json", codec: { mode: "strict", typeSymbol: "TTeamUiPrefsPatch", schema: uiPrefsPatchSchema, create: () => uiPrefsPatchSchema } }
20875
- ], "TTeamUiPrefs", uiPrefsSchema),
20876
20693
  direct("createExpert", [
20877
20694
  { name: "expert", wire: "expert", source: "json", codec: { mode: "strict", typeSymbol: "TTeamCustomExpertInput", schema: customExpertInputSchema, create: () => customExpertInputSchema } }
20878
20695
  ], "TTeamCatalog", catalogSnapshotSchema),
@@ -20898,19 +20715,188 @@ var TYPERT_REMOTE = {
20898
20715
  ], "TTeamCatalog", catalogSnapshotSchema)
20899
20716
  ]
20900
20717
  };
20718
+ function unwrap2(result, action) {
20719
+ if (result !== null && typeof result === "object" && typeof result.ok === "boolean") {
20720
+ if (result.ok !== true) {
20721
+ const error62 = new Error(result.error?.message ?? `${action} failed`);
20722
+ error62.code = result.error?.code;
20723
+ throw error62;
20724
+ }
20725
+ return result.value;
20726
+ }
20727
+ return result;
20728
+ }
20901
20729
 
20902
20730
  // src/client/settings.jsx
20903
20731
  var import_react5 = __toESM(require("react"), 1);
20904
20732
 
20905
20733
  // src/client/tabs.jsx
20906
- var import_react4 = __toESM(require("react"), 1);
20734
+ var import_react3 = __toESM(require("react"), 1);
20735
+
20736
+ // src/client/ui.jsx
20737
+ var import_react2 = __toESM(require("react"), 1);
20738
+ var primitives = __toESM(require("@deepseek-ai/dsh-client-ui-primitives"), 1);
20739
+ var PButton = typeof primitives.Button === "function" ? primitives.Button : null;
20740
+ var PInput = typeof primitives.Input === "function" ? primitives.Input : null;
20741
+ var PTabs = typeof primitives.SegmentedTabs === "function" ? primitives.SegmentedTabs : null;
20742
+ var PModal = typeof primitives.Modal === "function" ? primitives.Modal : null;
20743
+ var PTag = typeof primitives.Tag === "function" ? primitives.Tag : null;
20744
+ var PSwitch = typeof primitives.Switch === "function" ? primitives.Switch : null;
20745
+ var PSegmentedControl = typeof primitives.SegmentedControl === "function" ? primitives.SegmentedControl : null;
20746
+ function Card({ title, hint, children }) {
20747
+ return /* @__PURE__ */ import_react2.default.createElement("section", { className: "t-team-panel-card", "aria-label": title }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "t-team-panel-card-head" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "t-team-panel-card-title" }, title), hint === void 0 ? null : /* @__PURE__ */ import_react2.default.createElement("div", { className: "t-team-panel-card-hint" }, hint)), children);
20748
+ }
20749
+ function Button2({ children, onClick, disabled, title, variant = "outline", size = "sm", icon, type = "button", className }) {
20750
+ if (PButton !== null) {
20751
+ return /* @__PURE__ */ import_react2.default.createElement(PButton, { variant, size, onClick, disabled, title, icon, type, className }, children);
20752
+ }
20753
+ return /* @__PURE__ */ import_react2.default.createElement("button", { className: "t-team-chip", type, onClick, disabled, title }, icon, children);
20754
+ }
20755
+ function TextInput({ icon, className, ...rest }) {
20756
+ const cls = className === void 0 ? "t-team-input-field" : `t-team-input-field ${className}`;
20757
+ if (PInput !== null) return /* @__PURE__ */ import_react2.default.createElement(PInput, { icon, className: cls, ...rest });
20758
+ return /* @__PURE__ */ import_react2.default.createElement("input", { className: `t-team-input ${cls}`, ...rest });
20759
+ }
20760
+ function Select({ value, onChange, title, options, ariaLabel }) {
20761
+ return /* @__PURE__ */ import_react2.default.createElement(
20762
+ "select",
20763
+ {
20764
+ className: "t-team-select",
20765
+ value,
20766
+ title,
20767
+ "aria-label": ariaLabel,
20768
+ onChange: (event) => onChange(event.target.value)
20769
+ },
20770
+ options.map((item) => /* @__PURE__ */ import_react2.default.createElement("option", { key: item.key, value: item.key }, item.label))
20771
+ );
20772
+ }
20773
+ function Tabs({ items, value, onChange, label }) {
20774
+ if (PTabs !== null) {
20775
+ const official = items.map((item) => ({
20776
+ value: item.value,
20777
+ label: item.label,
20778
+ id: item.id,
20779
+ panelId: item.panelId
20780
+ }));
20781
+ return /* @__PURE__ */ import_react2.default.createElement(PTabs, { items: official, value, onChange, label, className: "t-team-panel-tabs" });
20782
+ }
20783
+ return /* @__PURE__ */ import_react2.default.createElement("div", { className: "t-team-tabs", role: "tablist", "aria-label": label }, items.map((item) => /* @__PURE__ */ import_react2.default.createElement(
20784
+ "button",
20785
+ {
20786
+ className: "t-team-tab",
20787
+ type: "button",
20788
+ role: "tab",
20789
+ key: item.value,
20790
+ id: item.id,
20791
+ "aria-controls": item.panelId,
20792
+ "data-active": value === item.value,
20793
+ "aria-selected": value === item.value,
20794
+ onClick: () => onChange(item.value)
20795
+ },
20796
+ item.label
20797
+ )));
20798
+ }
20799
+ function Modal2({ open: open2, title, onClose, closeLabel, description, children, footer }) {
20800
+ if (!open2) return null;
20801
+ if (PModal !== null) {
20802
+ return /* @__PURE__ */ import_react2.default.createElement(PModal, { open: open2, title, onClose, closeLabel, description, footer }, children);
20803
+ }
20804
+ return /* @__PURE__ */ import_react2.default.createElement(
20805
+ "div",
20806
+ {
20807
+ className: "t-team-mask",
20808
+ onClick: (event) => {
20809
+ if (event.target === event.currentTarget) onClose();
20810
+ }
20811
+ },
20812
+ /* @__PURE__ */ import_react2.default.createElement("div", { className: "t-team-modal", role: "dialog", "aria-modal": "true", "aria-label": title }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "t-team-modal-head" }, /* @__PURE__ */ import_react2.default.createElement("span", null, title), /* @__PURE__ */ import_react2.default.createElement("button", { className: "t-team-chip", type: "button", onClick: onClose }, closeLabel)), description === void 0 ? null : /* @__PURE__ */ import_react2.default.createElement("div", { className: "t-team-note" }, description), /* @__PURE__ */ import_react2.default.createElement("div", { className: "t-team-modal-body" }, children), footer === void 0 ? null : /* @__PURE__ */ import_react2.default.createElement("div", { className: "t-team-editor-actions" }, footer))
20813
+ );
20814
+ }
20815
+ function Tag2({ tone = "outline", children }) {
20816
+ if (PTag !== null) return /* @__PURE__ */ import_react2.default.createElement(PTag, { tone }, children);
20817
+ return /* @__PURE__ */ import_react2.default.createElement("span", { className: "t-team-badge", "data-tone": tone === "outline" ? void 0 : tone }, children);
20818
+ }
20819
+ function Field({ label, children }) {
20820
+ return /* @__PURE__ */ import_react2.default.createElement("label", { className: "t-team-field" }, /* @__PURE__ */ import_react2.default.createElement("span", null, label), children);
20821
+ }
20822
+ function Switch2({ checked, onChange, label, disabled, title }) {
20823
+ if (PSwitch !== null) {
20824
+ return /* @__PURE__ */ import_react2.default.createElement(PSwitch, { checked: checked === true, onChange, label, disabled, title });
20825
+ }
20826
+ return /* @__PURE__ */ import_react2.default.createElement(
20827
+ "button",
20828
+ {
20829
+ className: "t-team-switch-fallback",
20830
+ type: "button",
20831
+ role: "switch",
20832
+ "aria-checked": checked === true,
20833
+ "aria-label": label,
20834
+ title,
20835
+ disabled,
20836
+ onClick: () => onChange(!(checked === true)),
20837
+ style: {
20838
+ boxSizing: "border-box",
20839
+ position: "relative",
20840
+ flex: "0 0 auto",
20841
+ width: 36,
20842
+ height: 20,
20843
+ padding: 2,
20844
+ border: 0,
20845
+ borderRadius: 999,
20846
+ cursor: "pointer",
20847
+ background: checked === true ? "var(--dsw-alias-brand-primary, #0f1115)" : "var(--dsw-alias-border-l3, #d8dadc)"
20848
+ }
20849
+ },
20850
+ /* @__PURE__ */ import_react2.default.createElement("span", { style: {
20851
+ display: "block",
20852
+ width: 16,
20853
+ height: 16,
20854
+ borderRadius: "50%",
20855
+ background: "var(--dsw-alias-label-primary-foreground, #fff)",
20856
+ transform: checked === true ? "translateX(16px)" : "none"
20857
+ } })
20858
+ );
20859
+ }
20860
+ function ChoiceGroup({ label, options, value, onSelect, disabled, id }) {
20861
+ const autoId = import_react2.default.useId();
20862
+ const baseId = id ?? `t-team-choice-${autoId}`;
20863
+ if (PSegmentedControl !== null) {
20864
+ return /* @__PURE__ */ import_react2.default.createElement(
20865
+ PSegmentedControl,
20866
+ {
20867
+ id: baseId,
20868
+ label,
20869
+ value,
20870
+ disabled,
20871
+ onChange: onSelect,
20872
+ options: options.map((item) => ({ value: item.id, label: item.label, disabled: item.disabled, title: item.title }))
20873
+ }
20874
+ );
20875
+ }
20876
+ return /* @__PURE__ */ import_react2.default.createElement("div", { className: "t-team-choice-fallback", role: "radiogroup", "aria-label": label, style: { display: "flex", gap: 6, margin: "6px 0 2px", flexWrap: "wrap" } }, options.map((item) => /* @__PURE__ */ import_react2.default.createElement(
20877
+ "button",
20878
+ {
20879
+ key: item.id,
20880
+ className: "t-team-chip",
20881
+ type: "button",
20882
+ role: "radio",
20883
+ "aria-checked": item.id === value,
20884
+ "data-active": item.id === value,
20885
+ disabled,
20886
+ onClick: () => onSelect(item.id)
20887
+ },
20888
+ item.label
20889
+ )));
20890
+ }
20891
+
20892
+ // src/client/tabs.jsx
20907
20893
  function CategoriesTab({ t, remote, categories, onChanged }) {
20908
- const [key, setKey] = import_react4.default.useState("");
20909
- const [label, setLabel] = import_react4.default.useState("");
20910
- const [editing, setEditing] = import_react4.default.useState(null);
20911
- const [pendingDelete, setPendingDelete] = import_react4.default.useState("");
20912
- const [error62, setError] = import_react4.default.useState("");
20913
- const [busy, setBusy] = import_react4.default.useState(false);
20894
+ const [key, setKey] = import_react3.default.useState("");
20895
+ const [label, setLabel] = import_react3.default.useState("");
20896
+ const [editing, setEditing] = import_react3.default.useState(null);
20897
+ const [pendingDelete, setPendingDelete] = import_react3.default.useState("");
20898
+ const [error62, setError] = import_react3.default.useState("");
20899
+ const [busy, setBusy] = import_react3.default.useState(false);
20914
20900
  const list = categories ?? [];
20915
20901
  const mine = list.filter((item) => !item.official);
20916
20902
  const official = list.filter((item) => item.official);
@@ -20930,75 +20916,274 @@ function CategoriesTab({ t, remote, categories, onChanged }) {
20930
20916
  setBusy(false);
20931
20917
  }
20932
20918
  }
20933
- const row = (item) => /* @__PURE__ */ import_react4.default.createElement("div", { className: "t-team-cat-row", key: item.key }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "t-team-cat-name", title: item.label }, item.label), /* @__PURE__ */ import_react4.default.createElement("span", { className: "t-team-badge", "data-tone": item.official ? void 0 : "custom" }, item.official ? t("cat.officialBadge") : t("cat.customBadge")), /* @__PURE__ */ import_react4.default.createElement("span", { className: "t-team-cat-key", title: item.key }, item.key), /* @__PURE__ */ import_react4.default.createElement("span", { className: "t-team-cat-count" }, t("cat.count", { count: item.count, custom: item.customCount })), !item.official && /* @__PURE__ */ import_react4.default.createElement("span", { className: "t-team-cat-actions" }, editing !== null && editing.key === item.key ? /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, /* @__PURE__ */ import_react4.default.createElement(
20934
- "input",
20919
+ const row = (item) => /* @__PURE__ */ import_react3.default.createElement("div", { className: "t-team-cat-row", key: item.key }, /* @__PURE__ */ import_react3.default.createElement("span", { className: "t-team-cat-name", title: item.label }, item.label), /* @__PURE__ */ import_react3.default.createElement(Tag2, { tone: item.official ? "outline" : "info" }, item.official ? t("cat.officialBadge") : t("cat.customBadge")), /* @__PURE__ */ import_react3.default.createElement("span", { className: "t-team-cat-key", title: item.key }, item.key), /* @__PURE__ */ import_react3.default.createElement("span", { className: "t-team-cat-count" }, t("cat.count", { count: item.count, custom: item.customCount })), !item.official && /* @__PURE__ */ import_react3.default.createElement("span", { className: "t-team-cat-actions" }, editing !== null && editing.key === item.key ? /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement(
20920
+ TextInput,
20935
20921
  {
20936
- className: "t-team-input",
20937
20922
  value: editing.label,
20938
20923
  placeholder: t("cat.label"),
20924
+ "aria-label": t("cat.label"),
20939
20925
  onChange: (event) => setEditing({ key: item.key, label: event.target.value })
20940
20926
  }
20941
- ), /* @__PURE__ */ import_react4.default.createElement(
20942
- "button",
20927
+ ), /* @__PURE__ */ import_react3.default.createElement(
20928
+ Button2,
20943
20929
  {
20944
- className: "t-team-chip",
20945
- type: "button",
20946
20930
  disabled: busy,
20931
+ variant: "primary",
20947
20932
  onClick: () => void run(() => remote.updateCategory(item.key, editing.label.trim()))
20948
20933
  },
20949
20934
  t("cat.save")
20950
- ), /* @__PURE__ */ import_react4.default.createElement("button", { className: "t-team-chip", type: "button", disabled: busy, onClick: () => setEditing(null) }, t("custom.cancel"))) : /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, /* @__PURE__ */ import_react4.default.createElement(
20951
- "button",
20952
- {
20953
- className: "t-team-chip",
20954
- type: "button",
20955
- disabled: busy,
20956
- onClick: () => setEditing({ key: item.key, label: item.label })
20957
- },
20958
- t("cat.rename")
20959
- ), pendingDelete === item.key ? /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, /* @__PURE__ */ import_react4.default.createElement(
20960
- "button",
20935
+ ), /* @__PURE__ */ import_react3.default.createElement(Button2, { disabled: busy, onClick: () => setEditing(null) }, t("custom.cancel"))) : /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement(Button2, { disabled: busy, onClick: () => setEditing({ key: item.key, label: item.label }) }, t("cat.rename")), pendingDelete === item.key ? /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement(
20936
+ Button2,
20961
20937
  {
20962
- className: "t-team-chip",
20963
- type: "button",
20964
- "data-active": "true",
20965
20938
  disabled: busy,
20939
+ variant: "primary",
20966
20940
  onClick: () => void run(() => remote.deleteCategory(item.key))
20967
20941
  },
20968
20942
  t("cat.confirmDelete")
20969
- ), /* @__PURE__ */ import_react4.default.createElement("button", { className: "t-team-chip", type: "button", disabled: busy, onClick: () => setPendingDelete("") }, t("custom.cancel"))) : /* @__PURE__ */ import_react4.default.createElement("button", { className: "t-team-chip", type: "button", disabled: busy, onClick: () => setPendingDelete(item.key) }, t("cat.delete")))));
20970
- return /* @__PURE__ */ import_react4.default.createElement("div", { className: "t-team-tabbody" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "t-team-note" }, t("cat.hint")), /* @__PURE__ */ import_react4.default.createElement("div", { className: "t-team-bar" }, /* @__PURE__ */ import_react4.default.createElement(
20971
- "input",
20943
+ ), /* @__PURE__ */ import_react3.default.createElement(Button2, { disabled: busy, onClick: () => setPendingDelete("") }, t("custom.cancel"))) : /* @__PURE__ */ import_react3.default.createElement(Button2, { disabled: busy, onClick: () => setPendingDelete(item.key) }, t("cat.delete")))));
20944
+ return (
20945
+ // 间距交给两层 flex 容器(2026-09-28 用户:「自建分类 / 官方分类 里下方元素增加点间隔」):
20946
+ // 页级 14px 隔开三个区块,小节级 8px 隔开「标题 → 说明 → 列表」。
20947
+ // 用容器 gap 而不是给每个元素写 margin:margin 叠起来既难算也难改,而且 .t-team-meta
20948
+ // 这种类别处也在用,加全局 margin 会误伤。
20949
+ /* @__PURE__ */ import_react3.default.createElement("div", { className: "t-team-cat-page" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "t-team-note" }, t("cat.hint")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "t-team-bar" }, /* @__PURE__ */ import_react3.default.createElement(
20950
+ TextInput,
20951
+ {
20952
+ placeholder: t("cat.key"),
20953
+ "aria-label": t("cat.key"),
20954
+ spellCheck: false,
20955
+ value: key,
20956
+ onChange: (event) => setKey(event.target.value)
20957
+ }
20958
+ ), /* @__PURE__ */ import_react3.default.createElement(
20959
+ TextInput,
20960
+ {
20961
+ placeholder: t("cat.label"),
20962
+ "aria-label": t("cat.label"),
20963
+ value: label,
20964
+ onChange: (event) => setLabel(event.target.value)
20965
+ }
20966
+ ), /* @__PURE__ */ import_react3.default.createElement(
20967
+ Button2,
20968
+ {
20969
+ variant: "primary",
20970
+ disabled: busy || key.trim() === "",
20971
+ onClick: () => void run(() => remote.createCategory(key.trim(), label.trim()))
20972
+ },
20973
+ t("cat.create")
20974
+ )), error62 !== "" && /* @__PURE__ */ import_react3.default.createElement("div", { className: "t-team-error" }, error62), /* @__PURE__ */ import_react3.default.createElement("div", { className: "t-team-cat-section" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "t-team-section-head" }, t("cat.mine")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "t-team-cat-list" }, mine.length === 0 ? /* @__PURE__ */ import_react3.default.createElement("div", { className: "t-team-cat-empty" }, t("cat.empty")) : mine.map(row))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "t-team-cat-section" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "t-team-section-head" }, t("cat.official")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "t-team-meta" }, t("cat.officialNote")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "t-team-cat-list", "data-scroll": "true" }, official.map(row))))
20975
+ );
20976
+ }
20977
+
20978
+ // src/client/skills-panel.jsx
20979
+ var import_react4 = __toESM(require("react"), 1);
20980
+ var SOURCE_KEYS = {
20981
+ "user-dsh": "sg.source.user-dsh",
20982
+ "user-agents": "sg.source.user-agents",
20983
+ bundled: "sg.source.bundled",
20984
+ "project-dsh": "sg.source.project-dsh",
20985
+ "project-agents": "sg.source.project-agents",
20986
+ custom: "sg.source.custom",
20987
+ runtime: "sg.source.runtime",
20988
+ unknown: "sg.source.unknown"
20989
+ };
20990
+ function sourceLabel(t, source) {
20991
+ const key = SOURCE_KEYS[source];
20992
+ return key === void 0 ? source : t(key);
20993
+ }
20994
+ function matchSkill(skill, query) {
20995
+ if (query === "") return true;
20996
+ const hay = `${skill.name} ${skill.description}`.toLowerCase();
20997
+ return hay.includes(query);
20998
+ }
20999
+ function statusOf(skill) {
21000
+ if (!skill.nativeModel) return "native";
21001
+ if (skill.gated) return "off";
21002
+ return "on";
21003
+ }
21004
+ function viewLabel(t, view) {
21005
+ if (view === void 0) return "";
21006
+ if (view.kind === "host") return t("sg.view.host");
21007
+ if (view.kind === "preset") return t("sg.view.preset", { name: view.name });
21008
+ if (view.kind === "agent") return t("sg.view.agent", { name: view.name });
21009
+ if (view.kind === "disk") return t("sg.view.disk");
21010
+ return view.key;
21011
+ }
21012
+ function SkillsPanel({ t, remote, initialSnapshot }) {
21013
+ const [snapshot, setSnapshot] = import_react4.default.useState(initialSnapshot ?? null);
21014
+ const [query, setQuery] = import_react4.default.useState("");
21015
+ const [status, setStatus] = import_react4.default.useState("all");
21016
+ const [source, setSource] = import_react4.default.useState("");
21017
+ const [view, setView] = import_react4.default.useState("");
21018
+ const [error62, setError] = import_react4.default.useState("");
21019
+ const [busy, setBusy] = import_react4.default.useState(false);
21020
+ const reload = import_react4.default.useCallback(async () => {
21021
+ try {
21022
+ const next = unwrap2(await remote.getSnapshot(), "getSnapshot");
21023
+ setSnapshot(next);
21024
+ setError("");
21025
+ return next;
21026
+ } catch (cause) {
21027
+ setError(cause instanceof Error ? cause.message : String(cause));
21028
+ return void 0;
21029
+ }
21030
+ }, [remote]);
21031
+ import_react4.default.useEffect(() => {
21032
+ let alive = true;
21033
+ (async () => {
21034
+ const next = await reload();
21035
+ if (alive && next !== void 0) setSnapshot(next);
21036
+ })();
21037
+ return () => {
21038
+ alive = false;
21039
+ };
21040
+ }, [reload]);
21041
+ async function save(disabled) {
21042
+ if (snapshot === null || busy) return;
21043
+ setBusy(true);
21044
+ setError("");
21045
+ try {
21046
+ const next = unwrap2(await remote.setDisabled(disabled, snapshot.revision), "setDisabled");
21047
+ setSnapshot(next);
21048
+ } catch (cause) {
21049
+ const code = cause && typeof cause === "object" ? cause.code : void 0;
21050
+ if (code === "tTeam/conflict") {
21051
+ await reload();
21052
+ setError(t("sg.settings.conflict"));
21053
+ } else {
21054
+ setError(t("sg.settings.saveFailed", { detail: cause instanceof Error ? cause.message : String(cause) }));
21055
+ }
21056
+ } finally {
21057
+ setBusy(false);
21058
+ }
21059
+ }
21060
+ if (snapshot === null && error62 === "") {
21061
+ return /* @__PURE__ */ import_react4.default.createElement("div", { className: "sg-settings" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "sg-empty" }, t("sg.settings.loading")));
21062
+ }
21063
+ if (snapshot === null) {
21064
+ return /* @__PURE__ */ import_react4.default.createElement("div", { className: "sg-settings" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "sg-error" }, t("sg.settings.loadFailed", { detail: error62 })), /* @__PURE__ */ import_react4.default.createElement(Button2, { onClick: () => void reload() }, t("sg.settings.retry")));
21065
+ }
21066
+ const needle = query.trim().toLowerCase();
21067
+ const skills = snapshot.skills ?? [];
21068
+ const views = snapshot.views ?? [];
21069
+ const viewKey = view !== "" && views.some((item) => item.key === view) ? view : "";
21070
+ const activeView = views.find((item) => item.key === viewKey);
21071
+ const inView = (skill) => viewKey === "" || (skill.views ?? []).includes(viewKey);
21072
+ const scoped = skills.filter(inView);
21073
+ const sources = [...new Set(scoped.map((item) => item.source))].sort();
21074
+ const sourceKey = source !== "" && sources.includes(source) ? source : "";
21075
+ const filtered = scoped.filter((skill) => {
21076
+ if (!matchSkill(skill, needle)) return false;
21077
+ if (sourceKey !== "" && skill.source !== sourceKey) return false;
21078
+ if (status !== "all" && statusOf(skill) !== status) return false;
21079
+ return true;
21080
+ });
21081
+ const scopeCounts = viewKey === "" ? snapshot.counts : activeView ?? snapshot.counts;
21082
+ const groups = /* @__PURE__ */ new Map();
21083
+ for (const skill of filtered) {
21084
+ const list = groups.get(skill.source) ?? [];
21085
+ list.push(skill);
21086
+ groups.set(skill.source, list);
21087
+ }
21088
+ const gatedNow = new Set(skills.filter((skill) => skill.gated).map((skill) => skill.name));
21089
+ function toggle(skill) {
21090
+ if (!skill.nativeModel) return;
21091
+ const next = new Set(gatedNow);
21092
+ if (skill.gated) next.delete(skill.name);
21093
+ else next.add(skill.name);
21094
+ void save([...next].sort());
21095
+ }
21096
+ function setFiltered(gated) {
21097
+ if (needle === "") return;
21098
+ const next = new Set(gatedNow);
21099
+ for (const skill of filtered) {
21100
+ if (!skill.nativeModel) continue;
21101
+ if (gated) next.add(skill.name);
21102
+ else next.delete(skill.name);
21103
+ }
21104
+ void save([...next].sort());
21105
+ }
21106
+ const hasSearch = needle !== "";
21107
+ const toggleable = filtered.filter((skill) => skill.nativeModel);
21108
+ const canBulk = !busy && hasSearch && toggleable.length > 0;
21109
+ const bulkTitle = hasSearch ? void 0 : t("sg.actions.needSearch");
21110
+ return /* @__PURE__ */ import_react4.default.createElement("div", { className: "sg-settings" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "sg-head" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "sg-meta" }, t("sg.settings.meta", {
21111
+ scope: viewKey === "" ? t("sg.view.all") : viewLabel(t, activeView) || viewKey,
21112
+ visible: scopeCounts.modelVisible,
21113
+ gated: scopeCounts.gated,
21114
+ native: scopeCounts.nativeOff,
21115
+ total: scopeCounts.total,
21116
+ version: CLIENT_BUILD_VERSION
21117
+ })), /* @__PURE__ */ import_react4.default.createElement("div", { className: "sg-hint" }, t("sg.settings.hint"))), error62 !== "" && /* @__PURE__ */ import_react4.default.createElement("div", { className: "sg-error" }, error62), /* @__PURE__ */ import_react4.default.createElement("div", { className: "sg-toolbar" }, /* @__PURE__ */ import_react4.default.createElement(
21118
+ TextInput,
20972
21119
  {
20973
- className: "t-team-input",
20974
- placeholder: t("cat.key"),
20975
- spellCheck: false,
20976
- value: key,
20977
- onChange: (event) => setKey(event.target.value)
21120
+ value: query,
21121
+ placeholder: t("sg.settings.search"),
21122
+ "aria-label": t("sg.settings.search"),
21123
+ onChange: (event) => setQuery(event.target.value)
20978
21124
  }
20979
21125
  ), /* @__PURE__ */ import_react4.default.createElement(
20980
- "input",
21126
+ Select,
20981
21127
  {
20982
- className: "t-team-input",
20983
- placeholder: t("cat.label"),
20984
- value: label,
20985
- onChange: (event) => setLabel(event.target.value)
21128
+ value: viewKey,
21129
+ onChange: (value) => {
21130
+ setView(value);
21131
+ setSource("");
21132
+ },
21133
+ title: t("sg.filter.view"),
21134
+ ariaLabel: t("sg.filter.view"),
21135
+ options: [
21136
+ { key: "", label: t("sg.view.all") },
21137
+ ...views.map((item) => ({
21138
+ key: item.key,
21139
+ label: t("sg.view.option", { label: viewLabel(t, item), visible: item.modelVisible, total: item.total })
21140
+ }))
21141
+ ]
20986
21142
  }
20987
21143
  ), /* @__PURE__ */ import_react4.default.createElement(
20988
- "button",
21144
+ Select,
20989
21145
  {
20990
- className: "t-team-chip",
20991
- type: "button",
20992
- "data-active": "true",
20993
- disabled: busy || key.trim() === "",
20994
- onClick: () => void run(() => remote.createCategory(key.trim(), label.trim()))
20995
- },
20996
- t("cat.create")
20997
- )), error62 !== "" && /* @__PURE__ */ import_react4.default.createElement("div", { className: "t-team-error" }, error62), /* @__PURE__ */ import_react4.default.createElement("div", { className: "t-team-section-head" }, t("cat.mine")), /* @__PURE__ */ import_react4.default.createElement("div", { className: "t-team-cat-list" }, mine.length === 0 ? /* @__PURE__ */ import_react4.default.createElement("div", { className: "t-team-cat-empty" }, t("cat.empty")) : mine.map(row)), /* @__PURE__ */ import_react4.default.createElement("div", { className: "t-team-section-head" }, t("cat.official")), /* @__PURE__ */ import_react4.default.createElement("div", { className: "t-team-meta" }, t("cat.officialNote")), /* @__PURE__ */ import_react4.default.createElement("div", { className: "t-team-cat-list", "data-scroll": "true" }, official.map(row)));
21146
+ value: sourceKey,
21147
+ onChange: setSource,
21148
+ title: t("sg.filter.source"),
21149
+ ariaLabel: t("sg.filter.source"),
21150
+ options: [
21151
+ { key: "", label: t("sg.filter.allSources") },
21152
+ ...sources.map((key) => ({ key, label: sourceLabel(t, key) }))
21153
+ ]
21154
+ }
21155
+ )), /* @__PURE__ */ import_react4.default.createElement("div", { className: "sg-chips" }, /* @__PURE__ */ import_react4.default.createElement(
21156
+ ChoiceGroup,
21157
+ {
21158
+ id: "sg-status-filter",
21159
+ label: t("sg.filter.status"),
21160
+ value: status,
21161
+ onSelect: setStatus,
21162
+ options: [
21163
+ ["all", "sg.filter.all"],
21164
+ ["on", "sg.filter.on"],
21165
+ ["off", "sg.filter.off"],
21166
+ ["native", "sg.filter.native"]
21167
+ ].map(([id, key]) => ({ id, label: t(key) }))
21168
+ }
21169
+ ), /* @__PURE__ */ import_react4.default.createElement("span", { className: "sg-filter-count" }, t("sg.settings.filterCount", { shown: filtered.length, groups: groups.size }))), /* @__PURE__ */ import_react4.default.createElement("div", { className: "sg-actions" }, /* @__PURE__ */ import_react4.default.createElement(Button2, { disabled: !canBulk, title: bulkTitle, onClick: () => setFiltered(false) }, t("sg.actions.enableFiltered")), /* @__PURE__ */ import_react4.default.createElement(Button2, { disabled: !canBulk, title: bulkTitle, onClick: () => setFiltered(true) }, t("sg.actions.disableFiltered"))), filtered.length === 0 ? /* @__PURE__ */ import_react4.default.createElement("div", { className: "sg-empty" }, t("sg.settings.empty")) : /* @__PURE__ */ import_react4.default.createElement("div", { className: "sg-groups" }, [...groups.entries()].map(([key, rows]) => /* @__PURE__ */ import_react4.default.createElement("section", { className: "sg-group", key }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "sg-group-head" }, /* @__PURE__ */ import_react4.default.createElement("span", null, sourceLabel(t, key)), /* @__PURE__ */ import_react4.default.createElement("span", null, rows.length)), /* @__PURE__ */ import_react4.default.createElement("div", { className: "sg-list" }, rows.map((skill) => {
21170
+ const on = skill.modelVisible;
21171
+ const locked = !skill.nativeModel;
21172
+ return /* @__PURE__ */ import_react4.default.createElement("div", { className: "sg-row", key: skill.name, "data-off": !on }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "sg-row-head" }, /* @__PURE__ */ import_react4.default.createElement("span", { className: "sg-name" }, skill.name, locked && /* @__PURE__ */ import_react4.default.createElement("span", { className: "sg-badge" }, t("sg.filter.native"))), /* @__PURE__ */ import_react4.default.createElement(
21173
+ Switch2,
21174
+ {
21175
+ checked: on,
21176
+ disabled: locked || busy,
21177
+ label: skill.name,
21178
+ title: locked ? t("sg.settings.nativeOff") : skill.name,
21179
+ onChange: () => toggle(skill)
21180
+ }
21181
+ )), skill.description !== "" && /* @__PURE__ */ import_react4.default.createElement("div", { className: "sg-desc" }, skill.description));
21182
+ }))))));
20998
21183
  }
20999
21184
 
21000
21185
  // src/client/settings.jsx
21001
- function SettingsPanel({ t, ctx, remote, getActive, initialTab }) {
21186
+ function SettingsPanel({ t, ctx, remote, skillRemote, getActive, initialTab, view }) {
21002
21187
  const [snapshot, setSnapshot] = import_react5.default.useState(() => snapshotOf(remote) ?? null);
21003
21188
  const [query, setQuery] = import_react5.default.useState("");
21004
21189
  const [division, setDivision] = import_react5.default.useState("");
@@ -21009,6 +21194,7 @@ function SettingsPanel({ t, ctx, remote, getActive, initialTab }) {
21009
21194
  const [editor, setEditor] = import_react5.default.useState(null);
21010
21195
  const [pendingDelete, setPendingDelete] = import_react5.default.useState("");
21011
21196
  const [tab, setTab] = import_react5.default.useState(initialTab ?? "experts");
21197
+ const [expandedGroups, setExpandedGroups] = import_react5.default.useState(null);
21012
21198
  const reload = import_react5.default.useCallback(async (force) => {
21013
21199
  try {
21014
21200
  const next = await refresh(remote, force);
@@ -21138,7 +21324,10 @@ function SettingsPanel({ t, ctx, remote, getActive, initialTab }) {
21138
21324
  await reload(true);
21139
21325
  } catch (cause) {
21140
21326
  const message = cause instanceof Error ? cause.message : String(cause);
21141
- if (message.includes("custom-slug-taken") || message.includes("custom-name-taken")) await reload(true);
21327
+ const code = cause && typeof cause === "object" ? cause.code : void 0;
21328
+ if (code === "tTeam/custom-slug-taken" || code === "tTeam/custom-name-taken" || code === "tTeam/custom-conflict") {
21329
+ await reload(true);
21330
+ }
21142
21331
  patchEditor({ saving: false, error: message });
21143
21332
  }
21144
21333
  }
@@ -21201,242 +21390,214 @@ function SettingsPanel({ t, ctx, remote, getActive, initialTab }) {
21201
21390
  setBusy(false);
21202
21391
  }
21203
21392
  }
21204
- const [showActivePulse, setShowActivePulseState] = import_react5.default.useState(true);
21205
- const [uiPrefsError, setUiPrefsError] = import_react5.default.useState("");
21206
- const [uiPrefsWriteError, setUiPrefsWriteError] = import_react5.default.useState("");
21207
- const uiPrefQueue = import_react5.default.useRef(Promise.resolve());
21208
- import_react5.default.useEffect(() => {
21209
- let alive = true;
21210
- void (async () => {
21211
- try {
21212
- const data = unwrap(await remote.getUiPrefs(), "getUiPrefs");
21213
- if (!alive) return;
21214
- if (typeof data?.showActivePulse !== "boolean") {
21215
- setUiPrefsError(t("settings.uiPrefsUnreadable"));
21216
- return;
21217
- }
21218
- setUiPrefsError("");
21219
- setShowActivePulseState(data.showActivePulse);
21220
- setUiPrefs({ showActivePulse: data.showActivePulse });
21221
- } catch (cause) {
21222
- console.error("[t-team] \u754C\u9762\u504F\u597D\u8BFB\u53D6\u5931\u8D25\uFF0C\u6682\u6309\u9ED8\u8BA4\u503C\u5904\u7406\uFF1A", cause);
21223
- if (alive) setUiPrefsError(cause instanceof Error ? cause.message : String(cause));
21224
- }
21225
- })();
21226
- return () => {
21227
- alive = false;
21228
- };
21229
- }, [remote]);
21230
- if (snapshot === null) {
21231
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-settings" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-title" }, t("settings.title")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-meta" }, t("settings.loading")), error62 !== "" && /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-error" }, t("settings.loadFailed", { detail: error62 })), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-row" }, /* @__PURE__ */ import_react5.default.createElement("button", { className: "t-team-chip", type: "button", onClick: () => void reload(true) }, t("settings.retry")))));
21232
- }
21233
- async function writeUiPref(patch, rollback) {
21234
- setUiPrefs(patch);
21235
- const run = uiPrefQueue.current.then(async () => {
21236
- try {
21237
- const next = unwrap(await remote.setUiPrefs(patch), "setUiPrefs");
21238
- setUiPrefs(next);
21239
- setShowActivePulseState(next.showActivePulse);
21240
- setUiPrefsWriteError("");
21241
- } catch (cause) {
21242
- setUiPrefs(rollback);
21243
- setShowActivePulseState(rollback.showActivePulse ?? showActivePulse);
21244
- setUiPrefsWriteError(cause instanceof Error ? cause.message : String(cause));
21245
- }
21246
- });
21247
- uiPrefQueue.current = run.catch(() => {
21248
- });
21249
- await run;
21393
+ if (view === "summary") {
21394
+ return /* @__PURE__ */ import_react5.default.createElement("span", null, t("settings.summary"));
21250
21395
  }
21251
- async function setShowActivePulse(next) {
21252
- setShowActivePulseState(next);
21253
- await writeUiPref({ showActivePulse: next }, { showActivePulse: !next });
21396
+ const panelId = (key) => `t-team-panel-${key}`;
21397
+ const tabId = (key) => `t-team-tab-${key}`;
21398
+ if (snapshot === null) {
21399
+ return /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-panel" }, /* @__PURE__ */ import_react5.default.createElement(Card, { title: t("settings.title") }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-meta" }, t("settings.loading")), error62 !== "" && /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-error" }, t("settings.loadFailed", { detail: error62 })), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-row" }, /* @__PURE__ */ import_react5.default.createElement(Button2, { onClick: () => void reload(true) }, t("settings.retry"))))));
21254
21400
  }
21255
21401
  const enabledCount = snapshot.enabled.length;
21256
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-settings" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-head" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-title" }, t("settings.title")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-meta" }, t("settings.enabledCount", { enabled: enabledCount, total: experts.length }), " \xB7 v", CLIENT_BUILD_VERSION)), (uiPrefsError !== "" || uiPrefsWriteError !== "") && /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-setting-row" }, uiPrefsError !== "" && /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-error" }, t("settings.uiPrefsUnreadable"), "\uFF1A", uiPrefsError), uiPrefsWriteError !== "" && /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-error" }, t("settings.uiPrefsWriteFailed"), "\uFF1A", uiPrefsWriteError)), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-setting-row" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-setting-head" }, /* @__PURE__ */ import_react5.default.createElement("span", { className: "t-team-setting-name" }, t("settings.showActivePulse")), /* @__PURE__ */ import_react5.default.createElement(
21257
- "button",
21258
- {
21259
- className: "t-team-switch",
21260
- type: "button",
21261
- role: "switch",
21262
- "aria-checked": showActivePulse,
21263
- "data-on": showActivePulse,
21264
- onClick: () => void setShowActivePulse(!showActivePulse)
21265
- },
21266
- /* @__PURE__ */ import_react5.default.createElement("span", { className: "t-team-switch-knob" })
21267
- )), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-setting-hint" }, t("settings.showActivePulseHint"))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-tabs", role: "tablist" }, [["experts", "tab.experts"], ["categories", "tab.categories"]].map(([key, label]) => /* @__PURE__ */ import_react5.default.createElement(
21268
- "button",
21269
- {
21270
- className: "t-team-tab",
21271
- type: "button",
21272
- role: "tab",
21273
- key,
21274
- "data-active": tab === key,
21275
- "aria-selected": tab === key,
21276
- onClick: () => setTab(key)
21277
- },
21278
- t(label)
21279
- ))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-tabbody", hidden: tab !== "categories" }, /* @__PURE__ */ import_react5.default.createElement(SectionBoundary, { t, reloadKey: tab }, /* @__PURE__ */ import_react5.default.createElement(CategoriesTab, { t, remote, categories, onChanged: () => reload(true) }))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-tabbody", hidden: tab !== "experts" }, /* @__PURE__ */ import_react5.default.createElement(SectionBoundary, { t, reloadKey: tab }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-note" }, t("roster.expertsHint")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-bar" }, /* @__PURE__ */ import_react5.default.createElement(
21280
- "input",
21281
- {
21282
- className: "t-team-input",
21283
- placeholder: t("settings.search"),
21284
- value: query,
21285
- onChange: (event) => setQuery(event.target.value)
21402
+ const healthNotice = (() => {
21403
+ const sc = snapshot.sidecar;
21404
+ if (sc === null || typeof sc !== "object") return "";
21405
+ const parts = [];
21406
+ if (sc.present !== true) parts.push(t("roster.sidecarMissing", { path: sc.zhRoot ?? "?" }));
21407
+ if (typeof sc.skippedFiles === "number" && sc.skippedFiles > 0) {
21408
+ parts.push(t("roster.skippedFiles", { count: sc.skippedFiles }));
21286
21409
  }
21287
- ), /* @__PURE__ */ import_react5.default.createElement(
21288
- "select",
21289
- {
21290
- className: "t-team-select",
21291
- title: t("filter.division"),
21292
- "aria-label": t("filter.division"),
21293
- value: division,
21294
- onChange: (event) => setDivision(event.target.value)
21295
- },
21296
- /* @__PURE__ */ import_react5.default.createElement("option", { value: "" }, t("filter.allDivisions")),
21297
- divisions.map((item) => {
21298
- const count = experts.filter((expert) => expert.division === item).length;
21299
- return /* @__PURE__ */ import_react5.default.createElement("option", { key: item, value: item }, `${labelOf(item)} (${count})`);
21300
- })
21301
- ), filtered && /* @__PURE__ */ import_react5.default.createElement(
21302
- "button",
21410
+ if (Array.isArray(sc.unreadableDivisions) && sc.unreadableDivisions.length > 0) {
21411
+ parts.push(t("roster.unreadableDivisions", { list: sc.unreadableDivisions.join("\u3001") }));
21412
+ }
21413
+ return parts.join(" ");
21414
+ })();
21415
+ const defaultExpanded = grouped.length === 0 ? [] : [grouped[0].division];
21416
+ const openDivisions = expandedGroups ?? defaultExpanded;
21417
+ const isGroupOpen = (division2) => filtered || openDivisions.includes(division2);
21418
+ const allGroupsOpen = grouped.length > 0 && grouped.every((group) => openDivisions.includes(group.division));
21419
+ const toggleGroup = (division2) => {
21420
+ const current = [...openDivisions];
21421
+ setExpandedGroups(current.includes(division2) ? current.filter((item) => item !== division2) : [...current, division2]);
21422
+ };
21423
+ const toggleAllGroups = () => {
21424
+ setExpandedGroups(allGroupsOpen ? [] : grouped.map((group) => group.division));
21425
+ };
21426
+ return /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-panel" }, /* @__PURE__ */ import_react5.default.createElement(
21427
+ Card,
21303
21428
  {
21304
- className: "t-team-chip",
21305
- type: "button",
21306
- onClick: () => {
21307
- setQuery("");
21308
- setDivision("");
21309
- setStatus("");
21310
- }
21429
+ title: t("settings.title"),
21430
+ hint: `${t("settings.enabledCount", { enabled: enabledCount, total: experts.length })} \xB7 v${CLIENT_BUILD_VERSION}`
21311
21431
  },
21312
- t("filter.clear")
21313
- ), /* @__PURE__ */ import_react5.default.createElement("button", { className: "t-team-chip", type: "button", onClick: openEditorNew }, t("custom.new"))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-meta" }, t("filter.summary", { shown: visible.length, total: experts.length, enabled: enabledSet.size })), error62 !== "" && /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-error" }, error62 === "CONFLICT" ? t("settings.conflictRetry") : t("settings.saveFailed", { detail: error62 })), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-groups" }, grouped.map((group) => /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-group", key: group.division }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-division" }, divisionLabel(group.items[0], active), " \xB7 ", group.items.length), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-grid" }, group.items.map((expert) => {
21314
- const on = enabledSet.has(expert.slug);
21315
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-card", "data-enabled": on, key: expert.slug }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-card-body" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-avatar" }, expert.emoji || "\u{1F9E9}"), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-identity" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-card-name" }, displayName(expert, active), expert.custom === true && /* @__PURE__ */ import_react5.default.createElement("span", { className: "t-team-badge-custom" }, t("custom.badge"))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-card-division" }, divisionLabel(expert, active)), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-card-slug" }, expert.slug)), /* @__PURE__ */ import_react5.default.createElement("p", { className: "t-team-card-desc" }, displayDescription(expert, active)), expert.conflict === true && /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-conflict", style: { gridColumn: "1/-1" } }, t("settings.conflict"))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-card-tools" }, /* @__PURE__ */ import_react5.default.createElement(
21316
- "button",
21317
- {
21318
- className: "t-team-icon-btn",
21319
- type: "button",
21320
- title: t("card.prompt"),
21321
- "aria-label": t("card.prompt"),
21322
- onClick: () => void openPrompt(expert)
21323
- },
21324
- "\u{1F4C4}"
21325
- ), expert.custom === true && /* @__PURE__ */ import_react5.default.createElement(
21326
- "button",
21432
+ /* @__PURE__ */ import_react5.default.createElement(
21433
+ Tabs,
21327
21434
  {
21328
- className: "t-team-icon-btn",
21329
- type: "button",
21330
- title: t("custom.edit"),
21331
- "aria-label": t("custom.edit"),
21332
- onClick: () => void openEditorEdit(expert)
21333
- },
21334
- "\u270F\uFE0F"
21335
- ), expert.custom === true && (pendingDelete === expert.slug ? /* @__PURE__ */ import_react5.default.createElement(
21336
- "button",
21435
+ value: tab,
21436
+ onChange: setTab,
21437
+ label: t("settings.title"),
21438
+ items: [
21439
+ { value: "experts", label: t("tab.experts"), id: tabId("experts"), panelId: panelId("experts") },
21440
+ { value: "categories", label: t("tab.categories"), id: tabId("categories"), panelId: panelId("categories") },
21441
+ { value: "skills", label: t("tab.skills"), id: tabId("skills"), panelId: panelId("skills") }
21442
+ ]
21443
+ }
21444
+ ),
21445
+ /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-tabbody", id: panelId("categories"), role: "tabpanel", "aria-labelledby": tabId("categories"), hidden: tab !== "categories" }, /* @__PURE__ */ import_react5.default.createElement(SectionBoundary, { t, reloadKey: tab }, /* @__PURE__ */ import_react5.default.createElement(CategoriesTab, { t, remote, categories, onChanged: () => reload(true) }))),
21446
+ /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-tabbody", id: panelId("skills"), role: "tabpanel", "aria-labelledby": tabId("skills"), hidden: tab !== "skills" }, /* @__PURE__ */ import_react5.default.createElement(SectionBoundary, { t, reloadKey: tab }, skillRemote === void 0 ? /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-note" }, t("sg.settings.unavailable")) : /* @__PURE__ */ import_react5.default.createElement(SkillsPanel, { t, remote: skillRemote }))),
21447
+ /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-tabbody", id: panelId("experts"), role: "tabpanel", "aria-labelledby": tabId("experts"), hidden: tab !== "experts" }, /* @__PURE__ */ import_react5.default.createElement(SectionBoundary, { t, reloadKey: tab }, healthNotice !== "" && /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-warn", role: "status" }, healthNotice), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-note" }, t("roster.expertsHint")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-bar" }, /* @__PURE__ */ import_react5.default.createElement(
21448
+ TextInput,
21337
21449
  {
21338
- className: "t-team-chip t-team-danger",
21339
- type: "button",
21340
- title: t("custom.deleteHint", { name: displayName(expert, active) }),
21341
- onClick: () => void removeExpert(expert)
21342
- },
21343
- t("custom.confirmDelete")
21344
- ) : /* @__PURE__ */ import_react5.default.createElement(
21345
- "button",
21450
+ placeholder: t("settings.search"),
21451
+ "aria-label": t("settings.search"),
21452
+ value: query,
21453
+ onChange: (event) => setQuery(event.target.value)
21454
+ }
21455
+ ), /* @__PURE__ */ import_react5.default.createElement(
21456
+ Select,
21346
21457
  {
21347
- className: "t-team-icon-btn",
21348
- type: "button",
21349
- title: t("custom.delete"),
21350
- "aria-label": t("custom.delete"),
21351
- onClick: () => setPendingDelete(expert.slug)
21352
- },
21353
- "\u{1F5D1}"
21354
- ))));
21355
- })))), visible.length === 0 && /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-meta" }, t("settings.empty"))))), promptView !== null && /* @__PURE__ */ import_react5.default.createElement(
21356
- "div",
21458
+ value: division,
21459
+ onChange: setDivision,
21460
+ title: t("filter.division"),
21461
+ ariaLabel: t("filter.division"),
21462
+ options: [
21463
+ { key: "", label: t("filter.allDivisions") },
21464
+ ...divisions.map((item) => ({
21465
+ key: item,
21466
+ label: `${labelOf(item)} (${experts.filter((expert) => expert.division === item).length})`
21467
+ }))
21468
+ ]
21469
+ }
21470
+ ), filtered && /* @__PURE__ */ import_react5.default.createElement(Button2, { onClick: () => {
21471
+ setQuery("");
21472
+ setDivision("");
21473
+ setStatus("");
21474
+ } }, t("filter.clear")), /* @__PURE__ */ import_react5.default.createElement(Button2, { variant: "primary", onClick: openEditorNew }, t("custom.new"))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-meta t-team-meta-row" }, /* @__PURE__ */ import_react5.default.createElement("span", null, t("filter.summary", { shown: visible.length, total: experts.length, enabled: enabledSet.size })), filtered ? null : /* @__PURE__ */ import_react5.default.createElement(Button2, { onClick: toggleAllGroups }, allGroupsOpen ? t("group.collapseAll") : t("group.expandAll"))), error62 !== "" && /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-error" }, error62 === "CONFLICT" ? t("settings.conflictRetry") : t("settings.saveFailed", { detail: error62 })), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-groups" }, grouped.map((group) => {
21475
+ const open2 = isGroupOpen(group.division);
21476
+ return /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-group", key: group.division }, /* @__PURE__ */ import_react5.default.createElement(
21477
+ "button",
21478
+ {
21479
+ className: "t-team-group-head",
21480
+ type: "button",
21481
+ "aria-expanded": open2,
21482
+ title: t("group.toggle", { name: divisionLabel(group.items[0], active) }),
21483
+ onClick: () => toggleGroup(group.division)
21484
+ },
21485
+ /* @__PURE__ */ import_react5.default.createElement("span", { className: "t-team-division" }, divisionLabel(group.items[0], active), " \xB7 ", group.items.length),
21486
+ /* @__PURE__ */ import_react5.default.createElement("span", { className: "t-team-group-chevron", "aria-hidden": "true" }, open2 ? "\u25BE" : "\u25B8")
21487
+ ), open2 && /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-grid" }, group.items.map((expert) => {
21488
+ const on = enabledSet.has(expert.slug);
21489
+ return /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-card", "data-enabled": on, key: expert.slug }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-card-body" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-avatar" }, expert.emoji || "\u{1F9E9}"), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-identity" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-card-name" }, displayName(expert, active), expert.custom === true && /* @__PURE__ */ import_react5.default.createElement(Tag2, { tone: "info" }, t("custom.badge"))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-card-division" }, divisionLabel(expert, active)), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-card-slug" }, expert.slug)), /* @__PURE__ */ import_react5.default.createElement("p", { className: "t-team-card-desc" }, displayDescription(expert, active)), expert.conflict === true && /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-conflict", style: { gridColumn: "1/-1" } }, t("settings.conflict"))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-card-tools" }, /* @__PURE__ */ import_react5.default.createElement(
21490
+ Button2,
21491
+ {
21492
+ variant: "ghost",
21493
+ onClick: () => void openPrompt(expert),
21494
+ title: t("card.prompt"),
21495
+ "aria-label": t("card.prompt")
21496
+ },
21497
+ "\u{1F4C4}"
21498
+ ), expert.custom === true && /* @__PURE__ */ import_react5.default.createElement(
21499
+ Button2,
21500
+ {
21501
+ variant: "ghost",
21502
+ onClick: () => void openEditorEdit(expert),
21503
+ title: t("custom.edit"),
21504
+ "aria-label": t("custom.edit")
21505
+ },
21506
+ "\u270F\uFE0F"
21507
+ ), expert.custom === true && (pendingDelete === expert.slug ? /* @__PURE__ */ import_react5.default.createElement(
21508
+ Button2,
21509
+ {
21510
+ variant: "outline",
21511
+ title: t("custom.deleteHint", { name: displayName(expert, active) }),
21512
+ onClick: () => void removeExpert(expert)
21513
+ },
21514
+ t("custom.confirmDelete")
21515
+ ) : /* @__PURE__ */ import_react5.default.createElement(
21516
+ Button2,
21517
+ {
21518
+ variant: "ghost",
21519
+ onClick: () => setPendingDelete(expert.slug),
21520
+ title: t("custom.delete"),
21521
+ "aria-label": t("custom.delete")
21522
+ },
21523
+ "\u{1F5D1}"
21524
+ ))));
21525
+ })));
21526
+ }), visible.length === 0 && /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-meta" }, t("settings.empty")))))
21527
+ ), promptView !== null && /* @__PURE__ */ import_react5.default.createElement(
21528
+ Modal2,
21357
21529
  {
21358
- className: "t-team-mask",
21359
- onClick: (event) => {
21360
- if (event.target === event.currentTarget) setPromptView(null);
21361
- }
21530
+ open: true,
21531
+ title: t("card.promptTitle", { name: promptView.name }),
21532
+ onClose: () => setPromptView(null),
21533
+ closeLabel: t("card.close")
21362
21534
  },
21363
- /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-modal", role: "dialog", "aria-modal": "true", "aria-label": t("card.promptTitle", { name: promptView.name }) }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-modal-head" }, /* @__PURE__ */ import_react5.default.createElement("span", null, t("card.promptTitle", { name: promptView.name })), /* @__PURE__ */ import_react5.default.createElement("button", { className: "t-team-chip", type: "button", onClick: () => setPromptView(null) }, t("card.close"))), /* @__PURE__ */ import_react5.default.createElement("pre", { className: "t-team-modal-body" }, promptView.loading ? t("card.promptLoading") : promptView.error !== "" ? t("card.promptFailed", { detail: promptView.error }) : promptView.text === "" ? t("card.promptEmpty") : promptView.text))
21535
+ /* @__PURE__ */ import_react5.default.createElement("pre", { className: "t-team-modal-body" }, promptView.loading ? t("card.promptLoading") : promptView.error !== "" ? t("card.promptFailed", { detail: promptView.error }) : promptView.text === "" ? t("card.promptEmpty") : promptView.text)
21364
21536
  ), editor !== null && /* @__PURE__ */ import_react5.default.createElement(
21365
- "div",
21537
+ Modal2,
21366
21538
  {
21367
- className: "t-team-mask",
21368
- onClick: (event) => {
21369
- if (event.target === event.currentTarget && !editor.saving) setEditor(null);
21370
- }
21371
- },
21372
- /* @__PURE__ */ import_react5.default.createElement(
21373
- "div",
21374
- {
21375
- className: "t-team-modal",
21376
- role: "dialog",
21377
- "aria-modal": "true",
21378
- "aria-label": editor.mode === "new" ? t("custom.titleNew") : t("custom.titleEdit", { name: editor.name || editor.slug })
21539
+ open: true,
21540
+ title: editor.mode === "new" ? t("custom.titleNew") : t("custom.titleEdit", { name: editor.name || editor.slug }),
21541
+ onClose: () => {
21542
+ if (!editor.saving) setEditor(null);
21379
21543
  },
21380
- /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-modal-head" }, /* @__PURE__ */ import_react5.default.createElement("span", null, editor.mode === "new" ? t("custom.titleNew") : t("custom.titleEdit", { name: editor.name || editor.slug })), /* @__PURE__ */ import_react5.default.createElement("button", { className: "t-team-chip", type: "button", disabled: editor.saving, onClick: () => setEditor(null) }, t("custom.cancel"))),
21381
- /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-editor", style: { padding: 14, overflow: "auto" } }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-note" }, t("custom.hint")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-field" }, /* @__PURE__ */ import_react5.default.createElement("span", null, t("custom.division")), /* @__PURE__ */ import_react5.default.createElement(
21382
- "select",
21383
- {
21384
- className: "t-team-select",
21385
- value: editor.divisionNew ? NEW_DIVISION : editor.division,
21386
- onChange: (event) => {
21387
- const value = event.target.value;
21388
- if (value === NEW_DIVISION) patchEditor({ divisionNew: true, divisionKey: "", divisionLabel: "" });
21389
- else patchEditor({ divisionNew: false, division: value });
21390
- }
21391
- },
21392
- editorDivisionOptions.map((item) => /* @__PURE__ */ import_react5.default.createElement("option", { key: item.key, value: item.key }, `${item.label} \xB7 ${item.key}`)),
21393
- /* @__PURE__ */ import_react5.default.createElement("option", { value: NEW_DIVISION }, t("custom.divisionNew"))
21394
- ), editor.divisionNew && /* @__PURE__ */ import_react5.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 6 } }, /* @__PURE__ */ import_react5.default.createElement(
21395
- "input",
21396
- {
21397
- className: "t-team-input",
21398
- placeholder: t("custom.divisionKey"),
21399
- spellCheck: false,
21400
- value: editor.divisionKey,
21401
- onChange: (event) => patchEditor({ divisionKey: event.target.value })
21402
- }
21403
- ), /* @__PURE__ */ import_react5.default.createElement(
21404
- "input",
21405
- {
21406
- className: "t-team-input",
21407
- placeholder: t("custom.divisionLabel"),
21408
- value: editor.divisionLabel,
21409
- onChange: (event) => patchEditor({ divisionLabel: event.target.value })
21410
- }
21411
- ))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-field" }, /* @__PURE__ */ import_react5.default.createElement("span", null, t("custom.slug")), /* @__PURE__ */ import_react5.default.createElement(
21412
- "input",
21413
- {
21414
- className: "t-team-input",
21415
- value: editor.slug,
21416
- readOnly: editor.mode === "edit",
21417
- spellCheck: false,
21418
- onChange: (event) => patchEditor({ slug: event.target.value })
21419
- }
21420
- )), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-field" }, /* @__PURE__ */ import_react5.default.createElement("span", null, t("custom.name")), /* @__PURE__ */ import_react5.default.createElement("input", { className: "t-team-input", value: editor.name, onChange: (event) => patchEditor({ name: event.target.value }) })), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-field" }, /* @__PURE__ */ import_react5.default.createElement("span", null, t("custom.nameEn")), /* @__PURE__ */ import_react5.default.createElement("input", { className: "t-team-input", value: editor.nameEn, onChange: (event) => patchEditor({ nameEn: event.target.value }) })), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-field" }, /* @__PURE__ */ import_react5.default.createElement("span", null, t("custom.emoji")), /* @__PURE__ */ import_react5.default.createElement("input", { className: "t-team-input", maxLength: 8, value: editor.emoji, onChange: (event) => patchEditor({ emoji: event.target.value }) })), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-field" }, /* @__PURE__ */ import_react5.default.createElement("span", null, t("custom.description")), /* @__PURE__ */ import_react5.default.createElement("input", { className: "t-team-input", value: editor.description, onChange: (event) => patchEditor({ description: event.target.value }) })), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-field" }, /* @__PURE__ */ import_react5.default.createElement("span", null, t("custom.body")), /* @__PURE__ */ import_react5.default.createElement(
21421
- "textarea",
21422
- {
21423
- className: "t-team-input",
21424
- value: editor.body,
21425
- disabled: editor.loading,
21426
- onChange: (event) => patchEditor({ body: event.target.value })
21427
- }
21428
- )), editor.loading && /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-meta" }, t("custom.loadingBody")), editor.error !== "" && /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-error" }, editor.error), /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-editor-actions" }, /* @__PURE__ */ import_react5.default.createElement("button", { className: "t-team-chip", type: "button", disabled: editor.saving, onClick: () => setEditor(null) }, t("custom.cancel")), /* @__PURE__ */ import_react5.default.createElement(
21429
- "button",
21544
+ closeLabel: t("custom.cancel"),
21545
+ footer: /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement(Button2, { onClick: () => setEditor(null), disabled: editor.saving }, t("custom.cancel")), /* @__PURE__ */ import_react5.default.createElement(
21546
+ Button2,
21430
21547
  {
21431
- className: "t-team-chip",
21432
- type: "button",
21433
- "data-active": "true",
21434
- disabled: editor.saving || editor.loading,
21435
- onClick: () => void saveEditor()
21548
+ variant: "primary",
21549
+ onClick: () => void saveEditor(),
21550
+ disabled: editor.saving || editor.loading
21436
21551
  },
21437
21552
  editor.saving ? t("custom.saving") : t("custom.save")
21438
- )))
21439
- )
21553
+ ))
21554
+ },
21555
+ /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-editor" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-note" }, t("custom.hint")), /* @__PURE__ */ import_react5.default.createElement(Field, { label: t("custom.division") }, /* @__PURE__ */ import_react5.default.createElement(
21556
+ Select,
21557
+ {
21558
+ value: editor.divisionNew ? NEW_DIVISION : editor.division,
21559
+ onChange: (value) => {
21560
+ if (value === NEW_DIVISION) patchEditor({ divisionNew: true, divisionKey: "", divisionLabel: "" });
21561
+ else patchEditor({ divisionNew: false, division: value });
21562
+ },
21563
+ ariaLabel: t("custom.division"),
21564
+ options: [
21565
+ ...editorDivisionOptions.map((item) => ({ key: item.key, label: `${item.label} \xB7 ${item.key}` })),
21566
+ { key: NEW_DIVISION, label: t("custom.divisionNew") }
21567
+ ]
21568
+ }
21569
+ )), editor.divisionNew && /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement(Field, { label: t("custom.divisionKey") }, /* @__PURE__ */ import_react5.default.createElement(
21570
+ TextInput,
21571
+ {
21572
+ placeholder: t("custom.divisionKey"),
21573
+ spellCheck: false,
21574
+ value: editor.divisionKey,
21575
+ onChange: (event) => patchEditor({ divisionKey: event.target.value })
21576
+ }
21577
+ )), /* @__PURE__ */ import_react5.default.createElement(Field, { label: t("custom.divisionLabel") }, /* @__PURE__ */ import_react5.default.createElement(
21578
+ TextInput,
21579
+ {
21580
+ placeholder: t("custom.divisionLabel"),
21581
+ value: editor.divisionLabel,
21582
+ onChange: (event) => patchEditor({ divisionLabel: event.target.value })
21583
+ }
21584
+ ))), /* @__PURE__ */ import_react5.default.createElement(Field, { label: t("custom.slug") }, /* @__PURE__ */ import_react5.default.createElement(
21585
+ TextInput,
21586
+ {
21587
+ value: editor.slug,
21588
+ readOnly: editor.mode === "edit",
21589
+ spellCheck: false,
21590
+ onChange: (event) => patchEditor({ slug: event.target.value })
21591
+ }
21592
+ )), /* @__PURE__ */ import_react5.default.createElement(Field, { label: t("custom.name") }, /* @__PURE__ */ import_react5.default.createElement(TextInput, { value: editor.name, onChange: (event) => patchEditor({ name: event.target.value }) })), /* @__PURE__ */ import_react5.default.createElement(Field, { label: t("custom.nameEn") }, /* @__PURE__ */ import_react5.default.createElement(TextInput, { value: editor.nameEn, onChange: (event) => patchEditor({ nameEn: event.target.value }) })), /* @__PURE__ */ import_react5.default.createElement(Field, { label: t("custom.emoji") }, /* @__PURE__ */ import_react5.default.createElement(TextInput, { maxLength: 8, value: editor.emoji, onChange: (event) => patchEditor({ emoji: event.target.value }) })), /* @__PURE__ */ import_react5.default.createElement(Field, { label: t("custom.description") }, /* @__PURE__ */ import_react5.default.createElement(TextInput, { value: editor.description, onChange: (event) => patchEditor({ description: event.target.value }) })), /* @__PURE__ */ import_react5.default.createElement(Field, { label: t("custom.body") }, /* @__PURE__ */ import_react5.default.createElement(
21593
+ "textarea",
21594
+ {
21595
+ className: "t-team-input",
21596
+ value: editor.body,
21597
+ disabled: editor.loading,
21598
+ onChange: (event) => patchEditor({ body: event.target.value })
21599
+ }
21600
+ )), editor.loading && /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-meta" }, t("custom.loadingBody")), editor.error !== "" && /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-error" }, editor.error))
21440
21601
  ));
21441
21602
  }
21442
21603
 
@@ -21698,6 +21859,7 @@ async function apply(ctx) {
21698
21859
  ctx.effect(() => disposeRemote, "t-team: remote");
21699
21860
  return ctx.get("remote.tTeam");
21700
21861
  });
21862
+ const skillRemote = remote;
21701
21863
  if (remote === void 0) {
21702
21864
  console.error(`[t-team] remote \u547D\u540D\u7A7A\u95F4\u4E0D\u53EF\u7528\uFF08${ctx.locale.bind(NS)("error.remoteUnavailable")}\uFF09\uFF1A\u4E13\u5BB6\u540D\u518C\u3001\u9762\u677F\u4E0E @ \u53EC\u5524\u5747\u4E0D\u53EF\u7528\uFF0C\u4F46 GUI \u6B63\u5E38\u542F\u52A8\u3002`);
21703
21865
  return;
@@ -21705,38 +21867,13 @@ async function apply(ctx) {
21705
21867
  await refresh(remote, true).catch((cause) => {
21706
21868
  console.warn("[t-team] \u521D\u59CB\u540D\u518C\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BBE\u7F6E\u9875\u53EF\u91CD\u8BD5\uFF1A", cause);
21707
21869
  });
21708
- void (async () => {
21709
- try {
21710
- const data = unwrap(await remote.getUiPrefs(), "getUiPrefs");
21711
- const patch = {};
21712
- for (const key of UI_PREF_KEYS) if (typeof data?.[key] === "boolean") patch[key] = data[key];
21713
- setUiPrefs(patch);
21714
- } catch (cause) {
21715
- console.warn("[t-team] \u754C\u9762\u504F\u597D\u8BFB\u53D6\u5931\u8D25\uFF0C\u6682\u6309\u9ED8\u8BA4\uFF08\u663E\u793A\u5FBD\u7AE0\uFF09\u5904\u7406\uFF1A", cause);
21716
- }
21717
- })();
21718
21870
  await stage("UI \u6CE8\u518C", async () => {
21719
- ctx.slots.inject("settings.section", () => ctx.slots.register({
21720
- name: "settings.section",
21721
- id: NS,
21722
- order: 17,
21871
+ ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
21872
+ name: "plugins.bundle.config",
21873
+ key: PLUGIN_ID,
21723
21874
  // 文案一律走字典(规范:产品可见文案由 locale 拥有,不接受硬编码分支)
21724
- label: () => ctx.locale.bind(NS)("settings.title"),
21725
- locale: NS,
21726
- icon: "expert"
21727
- }, (props) => import_react7.default.createElement(SettingsPanel, { ...props, t: ctx.locale.bind(NS), remote, getActive })));
21728
- ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
21729
- name: "conversation.session.header.utilities",
21730
- id: `${NS}-pulse`,
21731
- order: 1,
21732
- label: () => ctx.locale.bind(NS)("pulse.on"),
21733
21875
  locale: NS
21734
- }, (props) => import_react7.default.createElement(ActivePulse, {
21735
- ...props,
21736
- t: ctx.locale.bind(NS),
21737
- ctx: props?.ctx ?? ctx,
21738
- hostCtx: ctx
21739
- })));
21876
+ }, (props) => import_react7.default.createElement(SettingsPanel, { ...props, t: ctx.locale.bind(NS), remote, skillRemote, getActive })));
21740
21877
  ctx.slots.inject("conversation.input.left", () => ctx.slots.register({
21741
21878
  name: "conversation.input.left",
21742
21879
  id: NS,