dsh-plugin-t-expert 0.3.92 → 0.4.9

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.92";
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.9";
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,8 +243,21 @@ 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}
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}
610
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
611
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
612
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
@@ -634,9 +285,17 @@ var CSS = `
634
285
  .t-team-card-division{font-size:13px;line-height:18px;margin-top:2px;color:var(--dsw-alias-label-secondary)}
635
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}
636
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}
637
- .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}
638
294
  .t-team-cat-list[data-scroll="true"]{max-height:300px;overflow-y:auto}
639
- .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)}
640
299
  .t-team-cat-row:first-child{border-top:0}
641
300
  .t-team-cat-row:hover{background:var(--dsw-alias-interactive-bg-hover)}
642
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}
@@ -648,10 +307,11 @@ var CSS = `
648
307
  .t-team-cat-empty{padding:8px 10px;font-size:12px;color:var(--dsw-alias-label-caption)}
649
308
  .t-team-cat-list .t-team-badge{flex:none}
650
309
  .t-team-badge[data-tone="custom"]{color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);background:transparent}
651
- /* \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
652
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
653
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
654
- * \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 */
655
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}
656
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}
657
317
  .t-team-card-switch[data-enabled="true"]{background:var(--dsw-alias-button-primary-fill);border-color:var(--dsw-alias-button-primary-fill)}
@@ -659,6 +319,9 @@ var CSS = `
659
319
  .t-team-card-switch:disabled{opacity:.45;cursor:not-allowed}
660
320
  .t-team-conflict{font-size:11px;color:var(--dsw-alias-state-warn-primary)}
661
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)}
662
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
663
326
  position:relative \u7ED9 pop \u63D0\u4F9B left/bottom \u5B9A\u4F4D\u4E0A\u4E0B\u6587\uFF1Bdisplay:inline-flex + align-self:flex-start +
664
327
  max-width:fit-content \u9501\u5B9A\u81EA\u8EAB\u5BBD\u5EA6\u4E3A\u6309\u94AE\u5185\u5BB9\u5BBD\u5EA6\u3002 */
@@ -678,6 +341,14 @@ var CSS = `
678
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
679
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
680
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 */
681
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)}
682
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
683
354
  \u9760 flex \u5BB9\u5668\u9ED8\u8BA4\u7684 align-items:stretch \u81EA\u52A8\u6491\u6EE1\u51CF\u6389 margin \u540E\u7684\u5BBD\u5EA6\u3002 */
@@ -853,36 +524,45 @@ var CSS = `
853
524
  .t-team-expert-pick-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
854
525
  .t-team-expert-pick-meta{flex:none;font-size:11px;color:var(--dsw-alias-label-tertiary)}
855
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}
856
552
  `;
857
553
 
858
554
  // src/client/i18n.js
859
555
  var zh = {
860
556
  "settings.title": "T\u4E13\u5BB6",
861
- "settings.uiPrefsUnreadable": "\u8BFB\u53D6\u8BE5\u8BBE\u7F6E\u5931\u8D25",
862
- "settings.uiPrefsWriteFailed": "\u4FDD\u5B58\u8BE5\u8BBE\u7F6E\u5931\u8D25",
863
- "settings.showActivePulse": "\u663E\u793A\u6D3B\u8DC3\u6307\u793A",
864
- "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",
865
- "pulse.on": "\u6709\u4F1A\u8BDD\u6B63\u5728\u6D3B\u52A8\uFF0C\u6216\u6709\u4E8B\u7B49\u4F60\u67E5\u770B",
866
- "pulse.off": "\u5168\u90E8\u7A7A\u95F2\uFF0C\u4E14\u6CA1\u6709\u5F85\u4F60\u67E5\u770B\u7684",
867
- "pulse.title": "\u9700\u8981\u6CE8\u610F\u7684\u4F1A\u8BDD",
868
- "pulse.empty": "\u73B0\u5728\u6CA1\u6709\u9700\u8981\u6CE8\u610F\u7684\u4F1A\u8BDD",
869
- "pulse.running": "\u8FD0\u884C\u4E2D",
870
- "pulse.pending": "\u5F85\u67E5\u770B",
871
- "pulse.current": "\u5F53\u524D\u4F1A\u8BDD",
872
- "pulse.copy": "\u70B9\u6B64\u590D\u5236",
873
- "pulse.copyHint": "\u590D\u5236\u4F1A\u8BDD id",
874
- "pulse.copied": "\u5DF2\u590D\u5236",
875
- "pulse.idPending": "\u6682\u65E0 id",
876
- "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",
877
561
  "settings.loading": "\u6B63\u5728\u8BFB\u53D6\u540D\u518C\u2026",
878
562
  "settings.retry": "\u91CD\u8BD5",
879
563
  "settings.search": "\u641C\u7D22\u4E13\u5BB6\u540D\u6216\u7B80\u4ECB",
880
564
  "filter.division": "\u5206\u7C7B",
881
- "filter.status": "\u72B6\u6001",
882
565
  "filter.allDivisions": "\u5168\u90E8\u5206\u7C7B",
883
- "filter.allStatus": "\u5168\u90E8\u72B6\u6001",
884
- "filter.statusOn": "\u5DF2\u542F\u7528",
885
- "filter.statusOff": "\u672A\u542F\u7528",
886
566
  "filter.clear": "\u6E05\u9664\u7B5B\u9009",
887
567
  "filter.summary": "\u663E\u793A {shown} / {total}",
888
568
  "settings.enabledCount": "\u5171 {total} \u4F4D\u4E13\u5BB6",
@@ -894,8 +574,6 @@ var zh = {
894
574
  "card.promptFailed": "\u8BFB\u53D6\u63D0\u793A\u8BCD\u5931\u8D25\uFF1A{detail}",
895
575
  "card.promptEmpty": "\u8BE5\u4E13\u5BB6\u6CA1\u6709\u53EF\u663E\u793A\u7684\u63D0\u793A\u8BCD\u6B63\u6587\u3002",
896
576
  "card.close": "\u5173\u95ED",
897
- "settings.enable": "\u542F\u7528",
898
- "settings.disable": "\u505C\u7528",
899
577
  "settings.saveFailed": "\u4FDD\u5B58\u5931\u8D25\uFF1A{detail}",
900
578
  "settings.loadFailed": "\u8BFB\u53D6\u540D\u518C\u5931\u8D25\uFF1A{detail}",
901
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",
@@ -918,6 +596,12 @@ var zh = {
918
596
  "pop.skillNoMatch": "\u6CA1\u6709\u5339\u914D\u7684\u6280\u80FD\u3002",
919
597
  "pop.skillUserOnly": "\u4EC5\u7528\u6237",
920
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",
921
605
  "custom.new": "\uFF0B \u65B0\u5EFA\u4E13\u5BB6",
922
606
  "custom.titleNew": "\u65B0\u5EFA\u4E13\u5BB6",
923
607
  "custom.titleEdit": "\u7F16\u8F91\u300C{name}\u300D",
@@ -956,39 +640,63 @@ var zh = {
956
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",
957
641
  "custom.badge": "\u81EA\u5EFA",
958
642
  "custom.loadingBody": "\u6B63\u5728\u8BFB\u53D6\u4EBA\u683C\u6B63\u6587\u2026",
959
- "pop.title": "T\u4E13\u5BB6"
643
+ "pop.title": "T\u4E13\u5BB6",
960
644
  // 列表过滤与搜索(与官方「自动化任务」页同一套:标签行 + 搜索框)
961
645
  // 三档:全部 / 已启用 / 已停用 —— 停用 ≠ 删除(停用的任务保留数据、只是不触发)。
962
646
  // 运行时间卡片(对齐官方详情页的「运行时间」:重复 / 时间 / 星期 三行)
963
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"
964
688
  };
965
689
  var en = {
966
690
  "settings.title": "T Expert",
967
- "settings.showActivePulse": "Show activity indicator",
968
- "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",
969
- "pulse.on": "A session is running, or something awaits your review",
970
- "pulse.off": "Everything is idle and nothing awaits you",
971
- "pulse.title": "Sessions needing attention",
972
- "pulse.empty": "Nothing needs your attention right now",
973
- "pulse.running": "Running",
974
- "pulse.pending": "Unread",
975
- "pulse.current": "Current session",
976
- "pulse.copy": "Copy",
977
- "pulse.copyHint": "Copy session id",
978
- "pulse.copied": "Copied",
979
- "pulse.idPending": "no id yet",
980
- "pulse.idPendingHint": "Current session id not ready",
981
- "settings.uiPrefsUnreadable": "Failed to read this setting",
982
- "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",
983
695
  "settings.loading": "Loading roster\u2026",
984
696
  "settings.retry": "Retry",
985
697
  "settings.search": "Search expert name or description",
986
698
  "filter.division": "Division",
987
- "filter.status": "Status",
988
699
  "filter.allDivisions": "All divisions",
989
- "filter.allStatus": "All statuses",
990
- "filter.statusOn": "Enabled",
991
- "filter.statusOff": "Disabled",
992
700
  "filter.clear": "Clear filters",
993
701
  "filter.summary": "Showing {shown} / {total}",
994
702
  "settings.enabledCount": "{total} experts",
@@ -1000,8 +708,6 @@ var en = {
1000
708
  "card.promptFailed": "Failed to load prompt: {detail}",
1001
709
  "card.promptEmpty": "This expert has no prompt body to show.",
1002
710
  "card.close": "Close",
1003
- "settings.enable": "Enable",
1004
- "settings.disable": "Disable",
1005
711
  "settings.saveFailed": "Save failed: {detail}",
1006
712
  "settings.loadFailed": "Failed to load roster: {detail}",
1007
713
  "settings.conflictRetry": "Settings changed elsewhere; roster reloaded, please retry.",
@@ -1024,6 +730,12 @@ var en = {
1024
730
  "pop.skillNoMatch": "No matching skill.",
1025
731
  "pop.skillUserOnly": "user-only",
1026
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}.",
1027
739
  "custom.new": "\uFF0B New expert",
1028
740
  "custom.titleNew": "New expert",
1029
741
  "custom.titleEdit": "Edit \u201C{name}\u201D",
@@ -1062,10 +774,47 @@ var en = {
1062
774
  "custom.deleteHint": "Delete \u201C{name}\u201D? Only this custom expert file is removed; this cannot be undone.",
1063
775
  "custom.badge": "Custom",
1064
776
  "custom.loadingBody": "Loading persona body\u2026",
1065
- "pop.title": "T Expert"
777
+ "pop.title": "T Expert",
1066
778
  // List filter and search (same shape as the built-in Automation page: tab row + search field).
1067
779
  // Three lanes: All / Active / Disabled — disabled keeps its data and simply never fires.
1068
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."
1069
818
  };
1070
819
 
1071
820
  // src/client/insert.js
@@ -20828,6 +20577,22 @@ var catalogSnapshotSchema = external_exports.object({
20828
20577
  // 漏声明时面板拿到的快照里根本没有它们,界面上表现为"分区标签是空的"。
20829
20578
  customDivision: external_exports.string().optional(),
20830
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(),
20831
20596
  /** 可选的落点分类(面板「分类」下拉与管理页):官方在前,其余按目录/声明发现。 */
20832
20597
  categories: external_exports.array(external_exports.object({
20833
20598
  key: external_exports.string(),
@@ -20841,31 +20606,82 @@ var enabledStateSchema = external_exports.object({
20841
20606
  enabled: external_exports.array(external_exports.string()),
20842
20607
  revision: external_exports.number().int().min(0)
20843
20608
  });
20844
- var uiPrefsSchema = external_exports.object({
20845
- // 窗口右上角「活跃指示」是否显示。宿主**总是**返回布尔(缺省按 true 算),所以这里必填。
20846
- 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)
20847
20630
  });
20848
- var uiPrefsPatchSchema = external_exports.object({
20849
- 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)
20850
20646
  });
20851
- var promptSchema = external_exports.object({ prompt: external_exports.string() });
20852
20647
 
20853
20648
  // src/client/remote.js
20854
- function direct(method, parameters, typeSymbol, schema) {
20649
+ function directIn(namespace, method, parameters, typeSymbol, schema) {
20855
20650
  return {
20856
- id: `${PLUGIN_ID}#tTeam/${method}`,
20857
- service: "tTeam",
20858
- namespace: "tTeam",
20651
+ id: `${PLUGIN_ID}#${namespace}/${method}`,
20652
+ service: namespace,
20653
+ namespace,
20859
20654
  method,
20860
20655
  invocation: { kind: "direct" },
20861
20656
  parameters,
20862
20657
  result: { mode: "strict", typeSymbol, schema, create: () => schema }
20863
20658
  };
20864
20659
  }
20660
+ function direct(method, parameters, typeSymbol, schema) {
20661
+ return directIn("tTeam", method, parameters, typeSymbol, schema);
20662
+ }
20865
20663
  var TYPERT_REMOTE = {
20866
20664
  package: PLUGIN_ID,
20867
20665
  descriptors: [
20868
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),
20869
20685
  direct("setEnabled", [
20870
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()) } },
20871
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) } }
@@ -20874,12 +20690,6 @@ var TYPERT_REMOTE = {
20874
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) } },
20875
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) } }
20876
20692
  ], "TTeamPrompt", promptSchema),
20877
- direct("getUiPrefs", [], "TTeamUiPrefs", uiPrefsSchema),
20878
- // 入参是 patch 对象(与 host 侧 descriptor 一致):界面偏好不止一项,
20879
- // 加键不必再加方法。
20880
- direct("setUiPrefs", [
20881
- { name: "patch", wire: "patch", source: "json", codec: { mode: "strict", typeSymbol: "TTeamUiPrefsPatch", schema: uiPrefsPatchSchema, create: () => uiPrefsPatchSchema } }
20882
- ], "TTeamUiPrefs", uiPrefsSchema),
20883
20693
  direct("createExpert", [
20884
20694
  { name: "expert", wire: "expert", source: "json", codec: { mode: "strict", typeSymbol: "TTeamCustomExpertInput", schema: customExpertInputSchema, create: () => customExpertInputSchema } }
20885
20695
  ], "TTeamCatalog", catalogSnapshotSchema),
@@ -20905,19 +20715,188 @@ var TYPERT_REMOTE = {
20905
20715
  ], "TTeamCatalog", catalogSnapshotSchema)
20906
20716
  ]
20907
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
+ }
20908
20729
 
20909
20730
  // src/client/settings.jsx
20910
20731
  var import_react5 = __toESM(require("react"), 1);
20911
20732
 
20912
20733
  // src/client/tabs.jsx
20913
- 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
20914
20893
  function CategoriesTab({ t, remote, categories, onChanged }) {
20915
- const [key, setKey] = import_react4.default.useState("");
20916
- const [label, setLabel] = import_react4.default.useState("");
20917
- const [editing, setEditing] = import_react4.default.useState(null);
20918
- const [pendingDelete, setPendingDelete] = import_react4.default.useState("");
20919
- const [error62, setError] = import_react4.default.useState("");
20920
- 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);
20921
20900
  const list = categories ?? [];
20922
20901
  const mine = list.filter((item) => !item.official);
20923
20902
  const official = list.filter((item) => item.official);
@@ -20937,75 +20916,274 @@ function CategoriesTab({ t, remote, categories, onChanged }) {
20937
20916
  setBusy(false);
20938
20917
  }
20939
20918
  }
20940
- 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(
20941
- "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,
20942
20921
  {
20943
- className: "t-team-input",
20944
20922
  value: editing.label,
20945
20923
  placeholder: t("cat.label"),
20924
+ "aria-label": t("cat.label"),
20946
20925
  onChange: (event) => setEditing({ key: item.key, label: event.target.value })
20947
20926
  }
20948
- ), /* @__PURE__ */ import_react4.default.createElement(
20949
- "button",
20927
+ ), /* @__PURE__ */ import_react3.default.createElement(
20928
+ Button2,
20950
20929
  {
20951
- className: "t-team-chip",
20952
- type: "button",
20953
20930
  disabled: busy,
20931
+ variant: "primary",
20954
20932
  onClick: () => void run(() => remote.updateCategory(item.key, editing.label.trim()))
20955
20933
  },
20956
20934
  t("cat.save")
20957
- ), /* @__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(
20958
- "button",
20959
- {
20960
- className: "t-team-chip",
20961
- type: "button",
20962
- disabled: busy,
20963
- onClick: () => setEditing({ key: item.key, label: item.label })
20964
- },
20965
- t("cat.rename")
20966
- ), pendingDelete === item.key ? /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, /* @__PURE__ */ import_react4.default.createElement(
20967
- "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,
20968
20937
  {
20969
- className: "t-team-chip",
20970
- type: "button",
20971
- "data-active": "true",
20972
20938
  disabled: busy,
20939
+ variant: "primary",
20973
20940
  onClick: () => void run(() => remote.deleteCategory(item.key))
20974
20941
  },
20975
20942
  t("cat.confirmDelete")
20976
- ), /* @__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")))));
20977
- 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(
20978
- "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,
20979
21119
  {
20980
- className: "t-team-input",
20981
- placeholder: t("cat.key"),
20982
- spellCheck: false,
20983
- value: key,
20984
- 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)
20985
21124
  }
20986
21125
  ), /* @__PURE__ */ import_react4.default.createElement(
20987
- "input",
21126
+ Select,
20988
21127
  {
20989
- className: "t-team-input",
20990
- placeholder: t("cat.label"),
20991
- value: label,
20992
- 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
+ ]
20993
21142
  }
20994
21143
  ), /* @__PURE__ */ import_react4.default.createElement(
20995
- "button",
21144
+ Select,
20996
21145
  {
20997
- className: "t-team-chip",
20998
- type: "button",
20999
- "data-active": "true",
21000
- disabled: busy || key.trim() === "",
21001
- onClick: () => void run(() => remote.createCategory(key.trim(), label.trim()))
21002
- },
21003
- t("cat.create")
21004
- )), 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
+ }))))));
21005
21183
  }
21006
21184
 
21007
21185
  // src/client/settings.jsx
21008
- function SettingsPanel({ t, ctx, remote, getActive, initialTab }) {
21186
+ function SettingsPanel({ t, ctx, remote, skillRemote, getActive, initialTab, view }) {
21009
21187
  const [snapshot, setSnapshot] = import_react5.default.useState(() => snapshotOf(remote) ?? null);
21010
21188
  const [query, setQuery] = import_react5.default.useState("");
21011
21189
  const [division, setDivision] = import_react5.default.useState("");
@@ -21016,6 +21194,7 @@ function SettingsPanel({ t, ctx, remote, getActive, initialTab }) {
21016
21194
  const [editor, setEditor] = import_react5.default.useState(null);
21017
21195
  const [pendingDelete, setPendingDelete] = import_react5.default.useState("");
21018
21196
  const [tab, setTab] = import_react5.default.useState(initialTab ?? "experts");
21197
+ const [expandedGroups, setExpandedGroups] = import_react5.default.useState(null);
21019
21198
  const reload = import_react5.default.useCallback(async (force) => {
21020
21199
  try {
21021
21200
  const next = await refresh(remote, force);
@@ -21145,7 +21324,10 @@ function SettingsPanel({ t, ctx, remote, getActive, initialTab }) {
21145
21324
  await reload(true);
21146
21325
  } catch (cause) {
21147
21326
  const message = cause instanceof Error ? cause.message : String(cause);
21148
- 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
+ }
21149
21331
  patchEditor({ saving: false, error: message });
21150
21332
  }
21151
21333
  }
@@ -21208,242 +21390,214 @@ function SettingsPanel({ t, ctx, remote, getActive, initialTab }) {
21208
21390
  setBusy(false);
21209
21391
  }
21210
21392
  }
21211
- const [showActivePulse, setShowActivePulseState] = import_react5.default.useState(true);
21212
- const [uiPrefsError, setUiPrefsError] = import_react5.default.useState("");
21213
- const [uiPrefsWriteError, setUiPrefsWriteError] = import_react5.default.useState("");
21214
- const uiPrefQueue = import_react5.default.useRef(Promise.resolve());
21215
- import_react5.default.useEffect(() => {
21216
- let alive = true;
21217
- void (async () => {
21218
- try {
21219
- const data = unwrap(await remote.getUiPrefs(), "getUiPrefs");
21220
- if (!alive) return;
21221
- if (typeof data?.showActivePulse !== "boolean") {
21222
- setUiPrefsError(t("settings.uiPrefsUnreadable"));
21223
- return;
21224
- }
21225
- setUiPrefsError("");
21226
- setShowActivePulseState(data.showActivePulse);
21227
- setUiPrefs({ showActivePulse: data.showActivePulse });
21228
- } catch (cause) {
21229
- console.error("[t-team] \u754C\u9762\u504F\u597D\u8BFB\u53D6\u5931\u8D25\uFF0C\u6682\u6309\u9ED8\u8BA4\u503C\u5904\u7406\uFF1A", cause);
21230
- if (alive) setUiPrefsError(cause instanceof Error ? cause.message : String(cause));
21231
- }
21232
- })();
21233
- return () => {
21234
- alive = false;
21235
- };
21236
- }, [remote]);
21237
- if (snapshot === null) {
21238
- 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")))));
21239
- }
21240
- async function writeUiPref(patch, rollback) {
21241
- setUiPrefs(patch);
21242
- const run = uiPrefQueue.current.then(async () => {
21243
- try {
21244
- const next = unwrap(await remote.setUiPrefs(patch), "setUiPrefs");
21245
- setUiPrefs(next);
21246
- setShowActivePulseState(next.showActivePulse);
21247
- setUiPrefsWriteError("");
21248
- } catch (cause) {
21249
- setUiPrefs(rollback);
21250
- setShowActivePulseState(rollback.showActivePulse ?? showActivePulse);
21251
- setUiPrefsWriteError(cause instanceof Error ? cause.message : String(cause));
21252
- }
21253
- });
21254
- uiPrefQueue.current = run.catch(() => {
21255
- });
21256
- await run;
21393
+ if (view === "summary") {
21394
+ return /* @__PURE__ */ import_react5.default.createElement("span", null, t("settings.summary"));
21257
21395
  }
21258
- async function setShowActivePulse(next) {
21259
- setShowActivePulseState(next);
21260
- 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"))))));
21261
21400
  }
21262
21401
  const enabledCount = snapshot.enabled.length;
21263
- 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(
21264
- "button",
21265
- {
21266
- className: "t-team-switch",
21267
- type: "button",
21268
- role: "switch",
21269
- "aria-checked": showActivePulse,
21270
- "data-on": showActivePulse,
21271
- onClick: () => void setShowActivePulse(!showActivePulse)
21272
- },
21273
- /* @__PURE__ */ import_react5.default.createElement("span", { className: "t-team-switch-knob" })
21274
- )), /* @__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(
21275
- "button",
21276
- {
21277
- className: "t-team-tab",
21278
- type: "button",
21279
- role: "tab",
21280
- key,
21281
- "data-active": tab === key,
21282
- "aria-selected": tab === key,
21283
- onClick: () => setTab(key)
21284
- },
21285
- t(label)
21286
- ))), /* @__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(
21287
- "input",
21288
- {
21289
- className: "t-team-input",
21290
- placeholder: t("settings.search"),
21291
- value: query,
21292
- 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 }));
21293
21409
  }
21294
- ), /* @__PURE__ */ import_react5.default.createElement(
21295
- "select",
21296
- {
21297
- className: "t-team-select",
21298
- title: t("filter.division"),
21299
- "aria-label": t("filter.division"),
21300
- value: division,
21301
- onChange: (event) => setDivision(event.target.value)
21302
- },
21303
- /* @__PURE__ */ import_react5.default.createElement("option", { value: "" }, t("filter.allDivisions")),
21304
- divisions.map((item) => {
21305
- const count = experts.filter((expert) => expert.division === item).length;
21306
- return /* @__PURE__ */ import_react5.default.createElement("option", { key: item, value: item }, `${labelOf(item)} (${count})`);
21307
- })
21308
- ), filtered && /* @__PURE__ */ import_react5.default.createElement(
21309
- "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,
21310
21428
  {
21311
- className: "t-team-chip",
21312
- type: "button",
21313
- onClick: () => {
21314
- setQuery("");
21315
- setDivision("");
21316
- setStatus("");
21317
- }
21429
+ title: t("settings.title"),
21430
+ hint: `${t("settings.enabledCount", { enabled: enabledCount, total: experts.length })} \xB7 v${CLIENT_BUILD_VERSION}`
21318
21431
  },
21319
- t("filter.clear")
21320
- ), /* @__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) => {
21321
- const on = enabledSet.has(expert.slug);
21322
- 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(
21323
- "button",
21324
- {
21325
- className: "t-team-icon-btn",
21326
- type: "button",
21327
- title: t("card.prompt"),
21328
- "aria-label": t("card.prompt"),
21329
- onClick: () => void openPrompt(expert)
21330
- },
21331
- "\u{1F4C4}"
21332
- ), expert.custom === true && /* @__PURE__ */ import_react5.default.createElement(
21333
- "button",
21432
+ /* @__PURE__ */ import_react5.default.createElement(
21433
+ Tabs,
21334
21434
  {
21335
- className: "t-team-icon-btn",
21336
- type: "button",
21337
- title: t("custom.edit"),
21338
- "aria-label": t("custom.edit"),
21339
- onClick: () => void openEditorEdit(expert)
21340
- },
21341
- "\u270F\uFE0F"
21342
- ), expert.custom === true && (pendingDelete === expert.slug ? /* @__PURE__ */ import_react5.default.createElement(
21343
- "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,
21344
21449
  {
21345
- className: "t-team-chip t-team-danger",
21346
- type: "button",
21347
- title: t("custom.deleteHint", { name: displayName(expert, active) }),
21348
- onClick: () => void removeExpert(expert)
21349
- },
21350
- t("custom.confirmDelete")
21351
- ) : /* @__PURE__ */ import_react5.default.createElement(
21352
- "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,
21353
21457
  {
21354
- className: "t-team-icon-btn",
21355
- type: "button",
21356
- title: t("custom.delete"),
21357
- "aria-label": t("custom.delete"),
21358
- onClick: () => setPendingDelete(expert.slug)
21359
- },
21360
- "\u{1F5D1}"
21361
- ))));
21362
- })))), visible.length === 0 && /* @__PURE__ */ import_react5.default.createElement("div", { className: "t-team-meta" }, t("settings.empty"))))), promptView !== null && /* @__PURE__ */ import_react5.default.createElement(
21363
- "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,
21364
21529
  {
21365
- className: "t-team-mask",
21366
- onClick: (event) => {
21367
- if (event.target === event.currentTarget) setPromptView(null);
21368
- }
21530
+ open: true,
21531
+ title: t("card.promptTitle", { name: promptView.name }),
21532
+ onClose: () => setPromptView(null),
21533
+ closeLabel: t("card.close")
21369
21534
  },
21370
- /* @__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)
21371
21536
  ), editor !== null && /* @__PURE__ */ import_react5.default.createElement(
21372
- "div",
21537
+ Modal2,
21373
21538
  {
21374
- className: "t-team-mask",
21375
- onClick: (event) => {
21376
- if (event.target === event.currentTarget && !editor.saving) setEditor(null);
21377
- }
21378
- },
21379
- /* @__PURE__ */ import_react5.default.createElement(
21380
- "div",
21381
- {
21382
- className: "t-team-modal",
21383
- role: "dialog",
21384
- "aria-modal": "true",
21385
- "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);
21386
21543
  },
21387
- /* @__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"))),
21388
- /* @__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(
21389
- "select",
21390
- {
21391
- className: "t-team-select",
21392
- value: editor.divisionNew ? NEW_DIVISION : editor.division,
21393
- onChange: (event) => {
21394
- const value = event.target.value;
21395
- if (value === NEW_DIVISION) patchEditor({ divisionNew: true, divisionKey: "", divisionLabel: "" });
21396
- else patchEditor({ divisionNew: false, division: value });
21397
- }
21398
- },
21399
- editorDivisionOptions.map((item) => /* @__PURE__ */ import_react5.default.createElement("option", { key: item.key, value: item.key }, `${item.label} \xB7 ${item.key}`)),
21400
- /* @__PURE__ */ import_react5.default.createElement("option", { value: NEW_DIVISION }, t("custom.divisionNew"))
21401
- ), editor.divisionNew && /* @__PURE__ */ import_react5.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 6 } }, /* @__PURE__ */ import_react5.default.createElement(
21402
- "input",
21403
- {
21404
- className: "t-team-input",
21405
- placeholder: t("custom.divisionKey"),
21406
- spellCheck: false,
21407
- value: editor.divisionKey,
21408
- onChange: (event) => patchEditor({ divisionKey: event.target.value })
21409
- }
21410
- ), /* @__PURE__ */ import_react5.default.createElement(
21411
- "input",
21412
- {
21413
- className: "t-team-input",
21414
- placeholder: t("custom.divisionLabel"),
21415
- value: editor.divisionLabel,
21416
- onChange: (event) => patchEditor({ divisionLabel: event.target.value })
21417
- }
21418
- ))), /* @__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(
21419
- "input",
21420
- {
21421
- className: "t-team-input",
21422
- value: editor.slug,
21423
- readOnly: editor.mode === "edit",
21424
- spellCheck: false,
21425
- onChange: (event) => patchEditor({ slug: event.target.value })
21426
- }
21427
- )), /* @__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(
21428
- "textarea",
21429
- {
21430
- className: "t-team-input",
21431
- value: editor.body,
21432
- disabled: editor.loading,
21433
- onChange: (event) => patchEditor({ body: event.target.value })
21434
- }
21435
- )), 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(
21436
- "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,
21437
21547
  {
21438
- className: "t-team-chip",
21439
- type: "button",
21440
- "data-active": "true",
21441
- disabled: editor.saving || editor.loading,
21442
- onClick: () => void saveEditor()
21548
+ variant: "primary",
21549
+ onClick: () => void saveEditor(),
21550
+ disabled: editor.saving || editor.loading
21443
21551
  },
21444
21552
  editor.saving ? t("custom.saving") : t("custom.save")
21445
- )))
21446
- )
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))
21447
21601
  ));
21448
21602
  }
21449
21603
 
@@ -21705,6 +21859,7 @@ async function apply(ctx) {
21705
21859
  ctx.effect(() => disposeRemote, "t-team: remote");
21706
21860
  return ctx.get("remote.tTeam");
21707
21861
  });
21862
+ const skillRemote = remote;
21708
21863
  if (remote === void 0) {
21709
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`);
21710
21865
  return;
@@ -21712,38 +21867,13 @@ async function apply(ctx) {
21712
21867
  await refresh(remote, true).catch((cause) => {
21713
21868
  console.warn("[t-team] \u521D\u59CB\u540D\u518C\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BBE\u7F6E\u9875\u53EF\u91CD\u8BD5\uFF1A", cause);
21714
21869
  });
21715
- void (async () => {
21716
- try {
21717
- const data = unwrap(await remote.getUiPrefs(), "getUiPrefs");
21718
- const patch = {};
21719
- for (const key of UI_PREF_KEYS) if (typeof data?.[key] === "boolean") patch[key] = data[key];
21720
- setUiPrefs(patch);
21721
- } catch (cause) {
21722
- 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);
21723
- }
21724
- })();
21725
21870
  await stage("UI \u6CE8\u518C", async () => {
21726
- ctx.slots.inject("settings.section", () => ctx.slots.register({
21727
- name: "settings.section",
21728
- id: NS,
21729
- order: 17,
21871
+ ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
21872
+ name: "plugins.bundle.config",
21873
+ key: PLUGIN_ID,
21730
21874
  // 文案一律走字典(规范:产品可见文案由 locale 拥有,不接受硬编码分支)
21731
- label: () => ctx.locale.bind(NS)("settings.title"),
21732
- locale: NS,
21733
- icon: "expert"
21734
- }, (props) => import_react7.default.createElement(SettingsPanel, { ...props, t: ctx.locale.bind(NS), remote, getActive })));
21735
- ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
21736
- name: "conversation.session.header.utilities",
21737
- id: `${NS}-pulse`,
21738
- order: 1,
21739
- label: () => ctx.locale.bind(NS)("pulse.on"),
21740
21875
  locale: NS
21741
- }, (props) => import_react7.default.createElement(ActivePulse, {
21742
- ...props,
21743
- t: ctx.locale.bind(NS),
21744
- ctx: props?.ctx ?? ctx,
21745
- hostCtx: ctx
21746
- })));
21876
+ }, (props) => import_react7.default.createElement(SettingsPanel, { ...props, t: ctx.locale.bind(NS), remote, skillRemote, getActive })));
21747
21877
  ctx.slots.inject("conversation.input.left", () => ctx.slots.register({
21748
21878
  name: "conversation.input.left",
21749
21879
  id: NS,