dsh-group-status 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.en.md +25 -12
- package/README.md +25 -12
- package/lib/client.js +709 -191
- package/lib/client.js.map +1 -1
- package/lib/types/client/group-status.d.ts +9 -0
- package/lib/types/client/group-status.d.ts.map +1 -1
- package/lib/types/client/group-status.js +32 -0
- package/lib/types/client/group-status.js.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/rows/GroupBadge.d.ts +10 -6
- package/lib/types/client/rows/GroupBadge.d.ts.map +1 -1
- package/lib/types/client/rows/GroupBadge.js +56 -45
- package/lib/types/client/rows/GroupBadge.js.map +1 -1
- package/lib/types/client/rows/Rows.d.ts.map +1 -1
- package/lib/types/client/rows/Rows.js +5 -5
- package/lib/types/client/rows/Rows.js.map +1 -1
- package/lib/types/client/rows/WorkspaceBrowser.d.ts.map +1 -1
- package/lib/types/client/rows/WorkspaceBrowser.js +45 -15
- package/lib/types/client/rows/WorkspaceBrowser.js.map +1 -1
- package/lib/types/client/tree.d.ts +6 -5
- package/lib/types/client/tree.d.ts.map +1 -1
- package/lib/types/client/tree.js +7 -14
- package/lib/types/client/tree.js.map +1 -1
- package/package.json +4 -4
- package/lib/tsconfig.tsbuildinfo +0 -1
package/lib/client.js
CHANGED
|
@@ -5,10 +5,10 @@ window.__ModuleLoader__.load({
|
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
|
|
8
|
-
let _deepseek_ai_cordis = require("@deepseek-ai/cordis");
|
|
9
8
|
let react = require("react");
|
|
10
9
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
11
10
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
11
|
+
let _deepseek_ai_cordis = require("@deepseek-ai/cordis");
|
|
12
12
|
//#region src/client/contract/slots.ts
|
|
13
13
|
/**
|
|
14
14
|
* Bind the row's render occurrence into the entries' `useMenuOpenState` hook:
|
|
@@ -223,19 +223,47 @@ window.__ModuleLoader__.load({
|
|
|
223
223
|
* 引用——每次都构造新对象会让选择器的 `Object.is` 比较永不成立,触发
|
|
224
224
|
* 「Maximum update depth exceeded」死循环(React #185),折叠行徽标会整块崩掉。
|
|
225
225
|
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
226
|
+
* 设置面缺席或未就绪时,回落到**默认全部显示**,与 `Config` 各字段的 schema 默认值同源:
|
|
227
|
+
* 读不到配置不是「关掉」。
|
|
228
228
|
*/
|
|
229
229
|
/** Loader 条目 id —— 设置页的寻址键,也是宿主 `describe()` 的 ns。 */
|
|
230
230
|
const ENTRY_ID = "dsh-group-status";
|
|
231
|
-
/** 默认值:与 `Config
|
|
232
|
-
const DEFAULT_BADGE_SETTINGS = Object.freeze({
|
|
233
|
-
|
|
231
|
+
/** 默认值:与 `Config` 各字段的 schema 默认同源(全部显示,数字阈值 2 即 count=1 时省略)。 */
|
|
232
|
+
const DEFAULT_BADGE_SETTINGS = Object.freeze({
|
|
233
|
+
showCompletedInBadge: true,
|
|
234
|
+
showRunningInBadge: true,
|
|
235
|
+
showPendingInBadge: true,
|
|
236
|
+
showScheduledInBadge: true,
|
|
237
|
+
showWaitingReviewInBadge: true,
|
|
238
|
+
showHoverDetails: true,
|
|
239
|
+
countThreshold: 2
|
|
240
|
+
});
|
|
241
|
+
/** 两个设置对象是否逐字段相等(用于发布前比较,避免无谓通知)。 */
|
|
242
|
+
function settingsEqual(a, b) {
|
|
243
|
+
return a.showCompletedInBadge === b.showCompletedInBadge && a.showRunningInBadge === b.showRunningInBadge && a.showPendingInBadge === b.showPendingInBadge && a.showScheduledInBadge === b.showScheduledInBadge && a.showWaitingReviewInBadge === b.showWaitingReviewInBadge && a.showHoverDetails === b.showHoverDetails && a.countThreshold === b.countThreshold;
|
|
244
|
+
}
|
|
245
|
+
/** 从原始快照派生:只有**显式 false** 才隐藏,缺失/非法一律显示(countThreshold 默认 2)。 */
|
|
234
246
|
function resolveFromSnapshot(snap) {
|
|
235
247
|
if (snap === void 0 || snap.status !== "ready") return DEFAULT_BADGE_SETTINGS;
|
|
236
248
|
const value = snap.value;
|
|
237
249
|
if (value === void 0 || value === null || typeof value !== "object") return DEFAULT_BADGE_SETTINGS;
|
|
238
|
-
|
|
250
|
+
const getBool = (field, def) => {
|
|
251
|
+
const v = value[field];
|
|
252
|
+
return typeof v === "boolean" ? v : def;
|
|
253
|
+
};
|
|
254
|
+
const getNumber = (field, def) => {
|
|
255
|
+
const v = value[field];
|
|
256
|
+
return typeof v === "number" && Number.isFinite(v) && v >= 0 ? Math.floor(v) : def;
|
|
257
|
+
};
|
|
258
|
+
return {
|
|
259
|
+
showCompletedInBadge: getBool("showCompletedInBadge", true),
|
|
260
|
+
showRunningInBadge: getBool("showRunningInBadge", true),
|
|
261
|
+
showPendingInBadge: getBool("showPendingInBadge", true),
|
|
262
|
+
showScheduledInBadge: getBool("showScheduledInBadge", true),
|
|
263
|
+
showWaitingReviewInBadge: getBool("showWaitingReviewInBadge", true),
|
|
264
|
+
showHoverDetails: getBool("showHoverDetails", true),
|
|
265
|
+
countThreshold: getNumber("countThreshold", 2)
|
|
266
|
+
};
|
|
239
267
|
}
|
|
240
268
|
/**
|
|
241
269
|
* 建一个恒在、取值稳定的徽标设置源。
|
|
@@ -263,13 +291,324 @@ window.__ModuleLoader__.load({
|
|
|
263
291
|
seen = snapshot;
|
|
264
292
|
seenSet = true;
|
|
265
293
|
const next = resolveFromSnapshot(snapshot);
|
|
266
|
-
if (next
|
|
294
|
+
if (settingsEqual(next, resolved)) return;
|
|
267
295
|
resolved = next;
|
|
268
296
|
for (const listener of [...listeners]) listener();
|
|
269
297
|
}
|
|
270
298
|
};
|
|
271
299
|
}
|
|
272
300
|
//#endregion
|
|
301
|
+
//#region src/client/badge-field.ts
|
|
302
|
+
/** 卡片字段名(与 `src/index.ts` 的 `Config` 同名同义)。 */
|
|
303
|
+
const BADGE_FIELDS = {
|
|
304
|
+
showCompletedInBadge: "showCompletedInBadge",
|
|
305
|
+
showRunningInBadge: "showRunningInBadge",
|
|
306
|
+
showPendingInBadge: "showPendingInBadge",
|
|
307
|
+
showScheduledInBadge: "showScheduledInBadge",
|
|
308
|
+
showWaitingReviewInBadge: "showWaitingReviewInBadge",
|
|
309
|
+
showHoverDetails: "showHoverDetails",
|
|
310
|
+
countThreshold: "countThreshold"
|
|
311
|
+
};
|
|
312
|
+
/**
|
|
313
|
+
* 草稿文本解析成开关状态:与 {@link isFieldEnabled} 不同,这里比较的是模型给的
|
|
314
|
+
* **文本**,别把字符串喂给上面那个函数(`'false' !== false` 恒真)。
|
|
315
|
+
*/
|
|
316
|
+
function isDraftEnabled(text) {
|
|
317
|
+
return text !== "false";
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* 构造一个布尔字段的字段转换规格。
|
|
321
|
+
*
|
|
322
|
+
* `parse` 只接受 `'true'` / `'false'`:空串、任意其它文本都返回 `undefined`,
|
|
323
|
+
* 让模型把字段判为 invalid 并拒绝保存(官方模型的既定行为)。
|
|
324
|
+
*
|
|
325
|
+
* @param field - 字段名(与 Config 同名)。
|
|
326
|
+
* @param defaultValue - 缺失值时的默认(true = 默认显示,false = 默认隐藏)。
|
|
327
|
+
*/
|
|
328
|
+
function makeBooleanField(field, defaultValue) {
|
|
329
|
+
return {
|
|
330
|
+
field,
|
|
331
|
+
format: (value) => {
|
|
332
|
+
return (value === void 0 || value === null || typeof value !== "boolean" ? defaultValue : value) ? "true" : "false";
|
|
333
|
+
},
|
|
334
|
+
parse: (text) => text === "true" ? {
|
|
335
|
+
kind: "set",
|
|
336
|
+
value: true
|
|
337
|
+
} : text === "false" ? {
|
|
338
|
+
kind: "set",
|
|
339
|
+
value: false
|
|
340
|
+
} : void 0
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
/** 所有字段规格(按卡片渲染顺序)。 */
|
|
344
|
+
const ALL_BADGE_FIELDS = [
|
|
345
|
+
makeBooleanField(BADGE_FIELDS.showCompletedInBadge, true),
|
|
346
|
+
makeBooleanField(BADGE_FIELDS.showRunningInBadge, true),
|
|
347
|
+
makeBooleanField(BADGE_FIELDS.showPendingInBadge, true),
|
|
348
|
+
makeBooleanField(BADGE_FIELDS.showScheduledInBadge, true),
|
|
349
|
+
makeBooleanField(BADGE_FIELDS.showWaitingReviewInBadge, true),
|
|
350
|
+
makeBooleanField(BADGE_FIELDS.showHoverDetails, true),
|
|
351
|
+
{
|
|
352
|
+
field: BADGE_FIELDS.countThreshold,
|
|
353
|
+
format: (value) => {
|
|
354
|
+
const n = value === void 0 || value === null || typeof value !== "number" || !Number.isFinite(value) ? 2 : Math.floor(value);
|
|
355
|
+
return String(n < 0 ? 0 : n);
|
|
356
|
+
},
|
|
357
|
+
parse: (text) => {
|
|
358
|
+
const n = Number(text);
|
|
359
|
+
if (!Number.isFinite(n) || n < 0 || n !== Math.floor(n)) return void 0;
|
|
360
|
+
return {
|
|
361
|
+
kind: "set",
|
|
362
|
+
value: n
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
];
|
|
367
|
+
//#endregion
|
|
368
|
+
//#region \0dsh-css:/home/wujue/workspace/dsh-plugins/dsh-group-status/src/client/BadgeSettingsCard.module.css.mjs
|
|
369
|
+
const css$4 = ".EgC0Ja_section{min-width:0;padding:16px 0}.EgC0Ja_section:last-child{padding-bottom:0}.EgC0Ja_heading{color:var(--dsw-alias-label-primary);margin:0;font-size:13px;font-weight:600;line-height:1.5}.EgC0Ja_badgeField{gap:6px;padding:12px 0;display:grid}.EgC0Ja_badgeToggleRow{color:var(--dsw-alias-label-primary);justify-content:space-between;align-items:flex-start;gap:16px;font-size:13px;line-height:1.5;display:flex}.EgC0Ja_badgeToggleLabel{flex:1;min-width:0}.EgC0Ja_badgeNumberControl{align-items:center;gap:4px;display:flex}.EgC0Ja_badgeNumberButton{width:28px;height:28px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-elevated,#2a2a2a);border:1px solid var(--dsw-alias-border-primary,#555);cursor:pointer;user-select:none;border-radius:6px;justify-content:center;align-items:center;padding:0;font-size:16px;font-weight:600;line-height:1;display:flex}.EgC0Ja_badgeNumberButton:hover:not(:disabled){border-color:var(--dsw-alias-border-focus,#0078d4);background:var(--dsw-alias-bg-hover,#3a3a3a)}.EgC0Ja_badgeNumberButton:active:not(:disabled){background:var(--dsw-alias-bg-active,#4a4a4a)}.EgC0Ja_badgeNumberButton:disabled{opacity:.3;cursor:not-allowed}.EgC0Ja_badgeNumberValue{text-align:center;min-width:24px;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500}.EgC0Ja_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}";
|
|
370
|
+
const tagId$4 = "dsh-group-status/BadgeSettingsCard.module.css";
|
|
371
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
|
|
372
|
+
const tag = document.createElement("style");
|
|
373
|
+
tag.dataset.plugin = "dsh-group-status";
|
|
374
|
+
tag.dataset.pluginCss = tagId$4;
|
|
375
|
+
tag.textContent = css$4;
|
|
376
|
+
document.head.appendChild(tag);
|
|
377
|
+
}
|
|
378
|
+
var BadgeSettingsCard_module_css_default = {
|
|
379
|
+
"badgeField": "EgC0Ja_badgeField",
|
|
380
|
+
"badgeNumberButton": "EgC0Ja_badgeNumberButton",
|
|
381
|
+
"badgeNumberControl": "EgC0Ja_badgeNumberControl",
|
|
382
|
+
"badgeNumberValue": "EgC0Ja_badgeNumberValue",
|
|
383
|
+
"badgeToggleLabel": "EgC0Ja_badgeToggleLabel",
|
|
384
|
+
"badgeToggleRow": "EgC0Ja_badgeToggleRow",
|
|
385
|
+
"heading": "EgC0Ja_heading",
|
|
386
|
+
"hint": "EgC0Ja_hint",
|
|
387
|
+
"section": "EgC0Ja_section"
|
|
388
|
+
};
|
|
389
|
+
//#endregion
|
|
390
|
+
//#region src/client/BadgeSettingsCard.tsx
|
|
391
|
+
/**
|
|
392
|
+
* 折叠徽标的设置卡片:插件 → dsh-group-status(bundle 详情页的配置表单)。
|
|
393
|
+
*
|
|
394
|
+
* 为什么需要这张卡片:0.2.0 起宿主把插件 `Config`(`src/index.ts`,根节点
|
|
395
|
+
* `.volatile()`)投影到客户端 `configForms` 服务上,但**不会**自动把表单画进
|
|
396
|
+
* 「插件」页——插件页只在客户端往 `plugins.bundle.config` 注册了卡片时才渲染
|
|
397
|
+
* (见 dsh-later / dsh-gitea-dispatch 的同款做法)。没有它,README「设置项」一节
|
|
398
|
+
* 描述的配置表单在 GUI 里根本不存在。
|
|
399
|
+
*
|
|
400
|
+
* 视觉与交互**不自造**,全部交给官方组件(`@deepseek-ai/dsh-client-ui-primitives`):
|
|
401
|
+
* - `SettingsForm`:表单框架——只读提示、不可用提示、保存按钮、失败行、间距;
|
|
402
|
+
* - `SettingsFormModel`:分步草稿与保存(revision 围栏、宿主拒绝时不丢草稿);
|
|
403
|
+
* - `Switch`:开关本体。
|
|
404
|
+
* 官方设置页(设置 → 内置插件 → 子智能体 / 网页搜索)用的就是这三件,本卡片的
|
|
405
|
+
* 类名与取值也逐条对齐它们的 CSS(见 `BadgeSettingsCard.module.css` 头部)。
|
|
406
|
+
*
|
|
407
|
+
* 字段定义在 `badge-field.ts`(纯逻辑,可单测);这里只管把它们接到模型与槽位上。
|
|
408
|
+
*/
|
|
409
|
+
/**
|
|
410
|
+
* 渲染折叠徽标的设置表单。
|
|
411
|
+
*
|
|
412
|
+
* 模型与投影都按 `scope` 记忆:宿主换一份表单(条目重新服务)时整体重建,旧模型
|
|
413
|
+
* 在 effect 清理里 `dispose()`,不会留下悬挂订阅。
|
|
414
|
+
* @param props - 宿主设置表单与文案取值器。
|
|
415
|
+
* @returns 官方表单框架包着本插件的开关字段。
|
|
416
|
+
*/
|
|
417
|
+
function BadgeSettingsCard({ scope, t }) {
|
|
418
|
+
const model = (0, react.useMemo)(() => new _deepseek_ai_dsh_client_ui_primitives.SettingsFormModel(scope, ALL_BADGE_FIELDS), [scope]);
|
|
419
|
+
(0, react.useEffect)(() => () => {
|
|
420
|
+
model.dispose();
|
|
421
|
+
}, [model]);
|
|
422
|
+
const store = (0, react.useMemo)(() => model.bind(() => ({
|
|
423
|
+
...model.shell(),
|
|
424
|
+
fields: ALL_BADGE_FIELDS.map((spec) => ({
|
|
425
|
+
field: spec.field,
|
|
426
|
+
text: model.field(spec.field).text
|
|
427
|
+
}))
|
|
428
|
+
})), [model]);
|
|
429
|
+
const state = (0, react.useSyncExternalStore)(store.subscribe, store.getSnapshot);
|
|
430
|
+
const actions = (0, react.useMemo)(() => model.actions(), [model]);
|
|
431
|
+
const badgeSectionId = (0, react.useId)();
|
|
432
|
+
const hoverSectionId = (0, react.useId)();
|
|
433
|
+
const countSectionId = (0, react.useId)();
|
|
434
|
+
const labels = {
|
|
435
|
+
unavailable: t("settings.unavailable"),
|
|
436
|
+
readOnly: t("settings.readOnly"),
|
|
437
|
+
saveFailed: t("settings.saveFailed"),
|
|
438
|
+
save: t("settings.save"),
|
|
439
|
+
saving: t("settings.saving")
|
|
440
|
+
};
|
|
441
|
+
const fieldText = (field) => {
|
|
442
|
+
return state.fields.find((f) => f.field === field)?.text ?? "true";
|
|
443
|
+
};
|
|
444
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.SettingsForm, {
|
|
445
|
+
labels,
|
|
446
|
+
state,
|
|
447
|
+
onSave: () => {
|
|
448
|
+
actions.save();
|
|
449
|
+
},
|
|
450
|
+
onDiscard: () => {
|
|
451
|
+
actions.discard();
|
|
452
|
+
},
|
|
453
|
+
children: [
|
|
454
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
455
|
+
className: BadgeSettingsCard_module_css_default.section,
|
|
456
|
+
"aria-labelledby": badgeSectionId,
|
|
457
|
+
children: [
|
|
458
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
459
|
+
className: BadgeSettingsCard_module_css_default.heading,
|
|
460
|
+
id: badgeSectionId,
|
|
461
|
+
children: t("settings.badge.section")
|
|
462
|
+
}),
|
|
463
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleRow, {
|
|
464
|
+
field: BADGE_FIELDS.showRunningInBadge,
|
|
465
|
+
text: fieldText(BADGE_FIELDS.showRunningInBadge),
|
|
466
|
+
label: t("settings.badge.showRunning"),
|
|
467
|
+
hint: t("settings.badge.showRunningHint"),
|
|
468
|
+
state,
|
|
469
|
+
actions
|
|
470
|
+
}),
|
|
471
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleRow, {
|
|
472
|
+
field: BADGE_FIELDS.showPendingInBadge,
|
|
473
|
+
text: fieldText(BADGE_FIELDS.showPendingInBadge),
|
|
474
|
+
label: t("settings.badge.showPending"),
|
|
475
|
+
hint: t("settings.badge.showPendingHint"),
|
|
476
|
+
state,
|
|
477
|
+
actions
|
|
478
|
+
}),
|
|
479
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleRow, {
|
|
480
|
+
field: BADGE_FIELDS.showCompletedInBadge,
|
|
481
|
+
text: fieldText(BADGE_FIELDS.showCompletedInBadge),
|
|
482
|
+
label: t("settings.badge.showCompleted"),
|
|
483
|
+
hint: t("settings.badge.showCompletedHint"),
|
|
484
|
+
state,
|
|
485
|
+
actions
|
|
486
|
+
}),
|
|
487
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleRow, {
|
|
488
|
+
field: BADGE_FIELDS.showScheduledInBadge,
|
|
489
|
+
text: fieldText(BADGE_FIELDS.showScheduledInBadge),
|
|
490
|
+
label: t("settings.badge.showScheduled"),
|
|
491
|
+
hint: t("settings.badge.showScheduledHint"),
|
|
492
|
+
state,
|
|
493
|
+
actions
|
|
494
|
+
}),
|
|
495
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleRow, {
|
|
496
|
+
field: BADGE_FIELDS.showWaitingReviewInBadge,
|
|
497
|
+
text: fieldText(BADGE_FIELDS.showWaitingReviewInBadge),
|
|
498
|
+
label: t("settings.badge.showWaitingReview"),
|
|
499
|
+
hint: t("settings.badge.showWaitingReviewHint"),
|
|
500
|
+
state,
|
|
501
|
+
actions
|
|
502
|
+
})
|
|
503
|
+
]
|
|
504
|
+
}),
|
|
505
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
506
|
+
className: BadgeSettingsCard_module_css_default.section,
|
|
507
|
+
"aria-labelledby": hoverSectionId,
|
|
508
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
509
|
+
className: BadgeSettingsCard_module_css_default.heading,
|
|
510
|
+
id: hoverSectionId,
|
|
511
|
+
children: t("settings.hover.section")
|
|
512
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleRow, {
|
|
513
|
+
field: BADGE_FIELDS.showHoverDetails,
|
|
514
|
+
text: fieldText(BADGE_FIELDS.showHoverDetails),
|
|
515
|
+
label: t("settings.hover.showDetails"),
|
|
516
|
+
hint: t("settings.hover.showDetailsHint"),
|
|
517
|
+
state,
|
|
518
|
+
actions
|
|
519
|
+
})]
|
|
520
|
+
}),
|
|
521
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
522
|
+
className: BadgeSettingsCard_module_css_default.section,
|
|
523
|
+
"aria-labelledby": countSectionId,
|
|
524
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
525
|
+
className: BadgeSettingsCard_module_css_default.heading,
|
|
526
|
+
id: countSectionId,
|
|
527
|
+
children: t("settings.count.section")
|
|
528
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NumberRow, {
|
|
529
|
+
field: BADGE_FIELDS.countThreshold,
|
|
530
|
+
text: fieldText(BADGE_FIELDS.countThreshold),
|
|
531
|
+
label: t("settings.count.threshold"),
|
|
532
|
+
hint: t("settings.count.thresholdHint"),
|
|
533
|
+
state,
|
|
534
|
+
actions
|
|
535
|
+
})]
|
|
536
|
+
})
|
|
537
|
+
]
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
/** 单行开关:label + Switch + hint。 */
|
|
541
|
+
function ToggleRow({ field, text, label, hint, state, actions }) {
|
|
542
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
543
|
+
className: BadgeSettingsCard_module_css_default.badgeField,
|
|
544
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
545
|
+
className: BadgeSettingsCard_module_css_default.badgeToggleRow,
|
|
546
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
547
|
+
className: BadgeSettingsCard_module_css_default.badgeToggleLabel,
|
|
548
|
+
children: label
|
|
549
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
550
|
+
checked: isDraftEnabled(text),
|
|
551
|
+
label,
|
|
552
|
+
disabled: !state.writable || state.saving,
|
|
553
|
+
onChange: (next) => {
|
|
554
|
+
actions.edit(field, next ? "true" : "false");
|
|
555
|
+
}
|
|
556
|
+
})]
|
|
557
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
558
|
+
className: BadgeSettingsCard_module_css_default.hint,
|
|
559
|
+
children: hint
|
|
560
|
+
})]
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
/** 单行数字输入:label + 带加减按钮的数字输入 + hint。 */
|
|
564
|
+
function NumberRow({ field, text, label, hint, state, actions }) {
|
|
565
|
+
const disabled = !state.writable || state.saving;
|
|
566
|
+
const currentValue = Number(text) || 0;
|
|
567
|
+
const decrement = () => {
|
|
568
|
+
if (!disabled && currentValue > 0) actions.edit(field, String(currentValue - 1));
|
|
569
|
+
};
|
|
570
|
+
const increment = () => {
|
|
571
|
+
if (!disabled) actions.edit(field, String(currentValue + 1));
|
|
572
|
+
};
|
|
573
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
574
|
+
className: BadgeSettingsCard_module_css_default.badgeField,
|
|
575
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
576
|
+
className: BadgeSettingsCard_module_css_default.badgeToggleRow,
|
|
577
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
578
|
+
className: BadgeSettingsCard_module_css_default.badgeToggleLabel,
|
|
579
|
+
children: label
|
|
580
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
581
|
+
className: BadgeSettingsCard_module_css_default.badgeNumberControl,
|
|
582
|
+
children: [
|
|
583
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
584
|
+
type: "button",
|
|
585
|
+
className: BadgeSettingsCard_module_css_default.badgeNumberButton,
|
|
586
|
+
disabled: disabled || currentValue <= 0,
|
|
587
|
+
onClick: decrement,
|
|
588
|
+
"aria-label": "减少",
|
|
589
|
+
children: "−"
|
|
590
|
+
}),
|
|
591
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
592
|
+
className: BadgeSettingsCard_module_css_default.badgeNumberValue,
|
|
593
|
+
children: text
|
|
594
|
+
}),
|
|
595
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
596
|
+
type: "button",
|
|
597
|
+
className: BadgeSettingsCard_module_css_default.badgeNumberButton,
|
|
598
|
+
disabled,
|
|
599
|
+
onClick: increment,
|
|
600
|
+
"aria-label": "增加",
|
|
601
|
+
children: "+"
|
|
602
|
+
})
|
|
603
|
+
]
|
|
604
|
+
})]
|
|
605
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
606
|
+
className: BadgeSettingsCard_module_css_default.hint,
|
|
607
|
+
children: hint
|
|
608
|
+
})]
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
//#endregion
|
|
273
612
|
//#region node_modules/.pnpm/@deepseek-ai+dsh-util-values@0.2.0-rc.2_@deepseek-ai+cordis@4.0.4/node_modules/@deepseek-ai/dsh-util-values/lib/index.js
|
|
274
613
|
/** Duplicate-install-safe JSON and immutable-value helpers. @module @deepseek-ai/dsh-util-values */
|
|
275
614
|
/**
|
|
@@ -318,118 +657,6 @@ window.__ModuleLoader__.load({
|
|
|
318
657
|
const separator = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
|
|
319
658
|
return trimmed.slice(separator + 1);
|
|
320
659
|
}
|
|
321
|
-
//#endregion
|
|
322
|
-
//#region src/client/group-status.ts
|
|
323
|
-
/**
|
|
324
|
-
* Zero-valued sentinel for {@link GroupLiveStatus}.
|
|
325
|
-
*
|
|
326
|
-
* Used when a group is expanded (badge is hidden, so aggregation is wasted
|
|
327
|
-
* work) or when no cross-plugin data is available. Kept as a frozen constant
|
|
328
|
-
* so the reference is stable across renders — `Object.is` comparisons in
|
|
329
|
-
* React selectors never trigger spurious re-renders on it.
|
|
330
|
-
*/
|
|
331
|
-
const EMPTY_LIVE_STATUS = Object.freeze({
|
|
332
|
-
running: 0,
|
|
333
|
-
approval: 0,
|
|
334
|
-
planReview: 0,
|
|
335
|
-
question: 0,
|
|
336
|
-
done: 0,
|
|
337
|
-
subagents: 0,
|
|
338
|
-
scheduledSessions: 0,
|
|
339
|
-
scheduledCount: 0,
|
|
340
|
-
scheduleState: void 0,
|
|
341
|
-
waitingReviewCount: 0,
|
|
342
|
-
waitingReviewState: void 0
|
|
343
|
-
});
|
|
344
|
-
/**
|
|
345
|
-
* Strip the `session-` / `webhook-` prefix from a Session id.
|
|
346
|
-
*
|
|
347
|
-
* dsh-gitea-dispatch publishes its waiting map keyed by the host SessionId, but
|
|
348
|
-
* its HTTP endpoints key by this normalized form. Accepting both here keeps the
|
|
349
|
-
* badge working across that publisher's conventions without knowing which is in
|
|
350
|
-
* play.
|
|
351
|
-
*/
|
|
352
|
-
function stripSessionPrefix(id) {
|
|
353
|
-
return id.replace(/^(?:session|webhook)-/, "");
|
|
354
|
-
}
|
|
355
|
-
/**
|
|
356
|
-
* Fold every member row of a group into counts so a collapsed folder can hint at
|
|
357
|
-
* hidden live sessions. Mirrors the session-row status priority (pending
|
|
358
|
-
* outranks running outranks completed) at the counting level; labels resolve with
|
|
359
|
-
* locale at render time.
|
|
360
|
-
* @param nodes - member rows of the group.
|
|
361
|
-
* @param sessionIds - the same members' Session ids (schedule and waiting facts are keyed by id).
|
|
362
|
-
* @param waitingReviewSummary - dsh-gitea-dispatch waiting map, when that plugin is loaded.
|
|
363
|
-
* @param scheduleSummary - dsh-later schedule map, when that plugin is loaded.
|
|
364
|
-
*/
|
|
365
|
-
function summarizeGroupStatus(nodes, sessionIds, waitingReviewSummary, scheduleSummary) {
|
|
366
|
-
let running = 0;
|
|
367
|
-
let approval = 0;
|
|
368
|
-
let planReview = 0;
|
|
369
|
-
let question = 0;
|
|
370
|
-
let done = 0;
|
|
371
|
-
let subagents = 0;
|
|
372
|
-
for (const n of nodes) {
|
|
373
|
-
if (n.blank || n.archived) continue;
|
|
374
|
-
if (n.pendingInteraction === "approval") {
|
|
375
|
-
approval += 1;
|
|
376
|
-
continue;
|
|
377
|
-
}
|
|
378
|
-
if (n.pendingInteraction === "plan-review") {
|
|
379
|
-
planReview += 1;
|
|
380
|
-
continue;
|
|
381
|
-
}
|
|
382
|
-
if (n.pendingInteraction === "question") {
|
|
383
|
-
question += 1;
|
|
384
|
-
continue;
|
|
385
|
-
}
|
|
386
|
-
if (n.running) running += 1;
|
|
387
|
-
else if (n.completed) done += 1;
|
|
388
|
-
subagents += n.runningSubagentCount;
|
|
389
|
-
}
|
|
390
|
-
let scheduledSessions = 0;
|
|
391
|
-
let scheduledCount = 0;
|
|
392
|
-
let scheduleState = void 0;
|
|
393
|
-
if (scheduleSummary && sessionIds.length > 0) {
|
|
394
|
-
let totalSessions = 0;
|
|
395
|
-
let totalCount = 0;
|
|
396
|
-
let highestPriority = 0;
|
|
397
|
-
for (const id of sessionIds) {
|
|
398
|
-
const entry = scheduleSummary.get(id);
|
|
399
|
-
if (entry) {
|
|
400
|
-
totalSessions += 1;
|
|
401
|
-
totalCount += entry.count;
|
|
402
|
-
const priority = entry.state === "overdue" ? 3 : entry.state === "urgent" ? 2 : 1;
|
|
403
|
-
if (priority > highestPriority) highestPriority = priority;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
scheduledSessions = totalSessions;
|
|
407
|
-
scheduledCount = totalCount;
|
|
408
|
-
scheduleState = highestPriority === 3 ? "overdue" : highestPriority === 2 ? "urgent" : highestPriority === 1 ? "scheduled" : void 0;
|
|
409
|
-
}
|
|
410
|
-
let waitingReviewCount = 0;
|
|
411
|
-
let waitingReviewState = void 0;
|
|
412
|
-
if (waitingReviewSummary && sessionIds.length > 0) for (const id of sessionIds) {
|
|
413
|
-
const entry = waitingReviewSummary.get(id) ?? waitingReviewSummary.get(stripSessionPrefix(id));
|
|
414
|
-
if (entry === void 0) continue;
|
|
415
|
-
waitingReviewCount += 1;
|
|
416
|
-
if (entry.state === "overdue") waitingReviewState = "overdue";
|
|
417
|
-
else if (waitingReviewState === void 0) waitingReviewState = "waiting";
|
|
418
|
-
}
|
|
419
|
-
return {
|
|
420
|
-
running,
|
|
421
|
-
approval,
|
|
422
|
-
planReview,
|
|
423
|
-
question,
|
|
424
|
-
done,
|
|
425
|
-
subagents,
|
|
426
|
-
scheduledSessions,
|
|
427
|
-
scheduledCount,
|
|
428
|
-
scheduleState,
|
|
429
|
-
waitingReviewCount,
|
|
430
|
-
waitingReviewState
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
660
|
/**
|
|
434
661
|
* Resolve the Workspace browser group that owns one Session.
|
|
435
662
|
* @param workspaces - authoritative Workspace membership.
|
|
@@ -666,7 +893,7 @@ window.__ModuleLoader__.load({
|
|
|
666
893
|
* @param scheduleSummary - dsh-later schedule map (session id → schedule entry).
|
|
667
894
|
* @returns group sections in render order.
|
|
668
895
|
*/
|
|
669
|
-
function deriveGroups(list, workspaces, rowState, statuses, view
|
|
896
|
+
function deriveGroups(list, workspaces, rowState, statuses, view) {
|
|
670
897
|
const archived = new Set(rowState.archivedSessionIds);
|
|
671
898
|
const pinned = new Set(rowState.pinnedSessionIds);
|
|
672
899
|
const expandedGroups = new Set(view.expandedGroups);
|
|
@@ -676,7 +903,6 @@ window.__ModuleLoader__.load({
|
|
|
676
903
|
for (const g of groupByWorkspace(list, workspaces, archived, rowState.archivedFilter, view.ungroupedOrder)) {
|
|
677
904
|
const expanded = expandedGroups.has(g.key);
|
|
678
905
|
const members = sectionMembers(g.sessions, pinned, archived).map((session) => sessionNode(session, list, statuses, pinned, archived));
|
|
679
|
-
const memberIds = members.map((m) => m.id);
|
|
680
906
|
groups.push({
|
|
681
907
|
key: g.key,
|
|
682
908
|
workspaceId: g.workspaceId,
|
|
@@ -686,8 +912,7 @@ window.__ModuleLoader__.load({
|
|
|
686
912
|
sessionCount: g.sessions.length,
|
|
687
913
|
expanded,
|
|
688
914
|
containsCurrent: g.key === currentGroup,
|
|
689
|
-
|
|
690
|
-
sessions: expanded ? members : []
|
|
915
|
+
sessions: members
|
|
691
916
|
});
|
|
692
917
|
}
|
|
693
918
|
return groups;
|
|
@@ -1245,6 +1470,209 @@ window.__ModuleLoader__.load({
|
|
|
1245
1470
|
return title === "default-workspace" ? localizedDefault : title;
|
|
1246
1471
|
}
|
|
1247
1472
|
//#endregion
|
|
1473
|
+
//#region src/client/group-status.ts
|
|
1474
|
+
/**
|
|
1475
|
+
* Zero-valued sentinel for {@link GroupLiveStatus}.
|
|
1476
|
+
*
|
|
1477
|
+
* Used when a group is expanded (badge is hidden, so aggregation is wasted
|
|
1478
|
+
* work) or when no cross-plugin data is available. Kept as a frozen constant
|
|
1479
|
+
* so the reference is stable across renders — `Object.is` comparisons in
|
|
1480
|
+
* React selectors never trigger spurious re-renders on it.
|
|
1481
|
+
*/
|
|
1482
|
+
const EMPTY_LIVE_STATUS = Object.freeze({
|
|
1483
|
+
running: 0,
|
|
1484
|
+
approval: 0,
|
|
1485
|
+
planReview: 0,
|
|
1486
|
+
question: 0,
|
|
1487
|
+
done: 0,
|
|
1488
|
+
subagents: 0,
|
|
1489
|
+
scheduledSessions: 0,
|
|
1490
|
+
scheduledCount: 0,
|
|
1491
|
+
scheduleState: void 0,
|
|
1492
|
+
waitingReviewCount: 0,
|
|
1493
|
+
waitingReviewState: void 0
|
|
1494
|
+
});
|
|
1495
|
+
/**
|
|
1496
|
+
* Merge two {@link GroupLiveStatus} objects by summing counters and taking the
|
|
1497
|
+
* highest-priority state (overdue > urgent > scheduled for schedule; overdue > waiting
|
|
1498
|
+
* for waiting-review).
|
|
1499
|
+
*
|
|
1500
|
+
* Used for multi-level aggregation: when a parent group is collapsed, its badge
|
|
1501
|
+
* must reflect all descendant sessions, not just direct members.
|
|
1502
|
+
*/
|
|
1503
|
+
function mergeLiveStatus(a, b) {
|
|
1504
|
+
const priorityOf = (s) => s === "overdue" ? 3 : s === "urgent" ? 2 : s === "scheduled" ? 1 : 0;
|
|
1505
|
+
const scheduleState = priorityOf(a.scheduleState) >= priorityOf(b.scheduleState) ? a.scheduleState : b.scheduleState;
|
|
1506
|
+
const waitingReviewState = (a.waitingReviewState === "overdue" ? 2 : a.waitingReviewState === "waiting" ? 1 : 0) >= (b.waitingReviewState === "overdue" ? 2 : b.waitingReviewState === "waiting" ? 1 : 0) ? a.waitingReviewState : b.waitingReviewState;
|
|
1507
|
+
return {
|
|
1508
|
+
running: a.running + b.running,
|
|
1509
|
+
approval: a.approval + b.approval,
|
|
1510
|
+
planReview: a.planReview + b.planReview,
|
|
1511
|
+
question: a.question + b.question,
|
|
1512
|
+
done: a.done + b.done,
|
|
1513
|
+
subagents: a.subagents + b.subagents,
|
|
1514
|
+
scheduledSessions: a.scheduledSessions + b.scheduledSessions,
|
|
1515
|
+
scheduledCount: a.scheduledCount + b.scheduledCount,
|
|
1516
|
+
scheduleState,
|
|
1517
|
+
waitingReviewCount: a.waitingReviewCount + b.waitingReviewCount,
|
|
1518
|
+
waitingReviewState
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1521
|
+
/**
|
|
1522
|
+
* Strip the `session-` / `webhook-` prefix from a Session id.
|
|
1523
|
+
*
|
|
1524
|
+
* dsh-gitea-dispatch publishes its waiting map keyed by the host SessionId, but
|
|
1525
|
+
* its HTTP endpoints key by this normalized form. Accepting both here keeps the
|
|
1526
|
+
* badge working across that publisher's conventions without knowing which is in
|
|
1527
|
+
* play.
|
|
1528
|
+
*/
|
|
1529
|
+
function stripSessionPrefix(id) {
|
|
1530
|
+
return id.replace(/^(?:session|webhook)-/, "");
|
|
1531
|
+
}
|
|
1532
|
+
/**
|
|
1533
|
+
* Fold every member row of a group into counts so a collapsed folder can hint at
|
|
1534
|
+
* hidden live sessions. Mirrors the session-row status priority (pending
|
|
1535
|
+
* outranks running outranks completed) at the counting level; labels resolve with
|
|
1536
|
+
* locale at render time.
|
|
1537
|
+
* @param nodes - member rows of the group.
|
|
1538
|
+
* @param sessionIds - the same members' Session ids (schedule and waiting facts are keyed by id).
|
|
1539
|
+
* @param waitingReviewSummary - dsh-gitea-dispatch waiting map, when that plugin is loaded.
|
|
1540
|
+
* @param scheduleSummary - dsh-later schedule map, when that plugin is loaded.
|
|
1541
|
+
*/
|
|
1542
|
+
function summarizeGroupStatus(nodes, sessionIds, waitingReviewSummary, scheduleSummary) {
|
|
1543
|
+
let running = 0;
|
|
1544
|
+
let approval = 0;
|
|
1545
|
+
let planReview = 0;
|
|
1546
|
+
let question = 0;
|
|
1547
|
+
let done = 0;
|
|
1548
|
+
let subagents = 0;
|
|
1549
|
+
for (const n of nodes) {
|
|
1550
|
+
if (n.blank || n.archived) continue;
|
|
1551
|
+
if (n.pendingInteraction === "approval") {
|
|
1552
|
+
approval += 1;
|
|
1553
|
+
continue;
|
|
1554
|
+
}
|
|
1555
|
+
if (n.pendingInteraction === "plan-review") {
|
|
1556
|
+
planReview += 1;
|
|
1557
|
+
continue;
|
|
1558
|
+
}
|
|
1559
|
+
if (n.pendingInteraction === "question") {
|
|
1560
|
+
question += 1;
|
|
1561
|
+
continue;
|
|
1562
|
+
}
|
|
1563
|
+
if (n.running) running += 1;
|
|
1564
|
+
else if (n.completed) done += 1;
|
|
1565
|
+
subagents += n.runningSubagentCount;
|
|
1566
|
+
}
|
|
1567
|
+
let scheduledSessions = 0;
|
|
1568
|
+
let scheduledCount = 0;
|
|
1569
|
+
let scheduleState = void 0;
|
|
1570
|
+
if (scheduleSummary && sessionIds.length > 0) {
|
|
1571
|
+
let totalSessions = 0;
|
|
1572
|
+
let totalCount = 0;
|
|
1573
|
+
let highestPriority = 0;
|
|
1574
|
+
for (const id of sessionIds) {
|
|
1575
|
+
const entry = scheduleSummary.get(id);
|
|
1576
|
+
if (entry) {
|
|
1577
|
+
totalSessions += 1;
|
|
1578
|
+
totalCount += entry.count;
|
|
1579
|
+
const priority = entry.state === "overdue" ? 3 : entry.state === "urgent" ? 2 : 1;
|
|
1580
|
+
if (priority > highestPriority) highestPriority = priority;
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
scheduledSessions = totalSessions;
|
|
1584
|
+
scheduledCount = totalCount;
|
|
1585
|
+
scheduleState = highestPriority === 3 ? "overdue" : highestPriority === 2 ? "urgent" : highestPriority === 1 ? "scheduled" : void 0;
|
|
1586
|
+
}
|
|
1587
|
+
let waitingReviewCount = 0;
|
|
1588
|
+
let waitingReviewState = void 0;
|
|
1589
|
+
if (waitingReviewSummary && sessionIds.length > 0) for (const id of sessionIds) {
|
|
1590
|
+
const entry = waitingReviewSummary.get(id) ?? waitingReviewSummary.get(stripSessionPrefix(id));
|
|
1591
|
+
if (entry === void 0) continue;
|
|
1592
|
+
waitingReviewCount += 1;
|
|
1593
|
+
if (entry.state === "overdue") waitingReviewState = "overdue";
|
|
1594
|
+
else if (waitingReviewState === void 0) waitingReviewState = "waiting";
|
|
1595
|
+
}
|
|
1596
|
+
return {
|
|
1597
|
+
running,
|
|
1598
|
+
approval,
|
|
1599
|
+
planReview,
|
|
1600
|
+
question,
|
|
1601
|
+
done,
|
|
1602
|
+
subagents,
|
|
1603
|
+
scheduledSessions,
|
|
1604
|
+
scheduledCount,
|
|
1605
|
+
scheduleState,
|
|
1606
|
+
waitingReviewCount,
|
|
1607
|
+
waitingReviewState
|
|
1608
|
+
};
|
|
1609
|
+
}
|
|
1610
|
+
/**
|
|
1611
|
+
* Measure each group's own members, as the input to {@link aggregateGroupStatuses}.
|
|
1612
|
+
*
|
|
1613
|
+
* Every group is measured, expanded ones included. A folder row shows no badge while
|
|
1614
|
+
* it is expanded, but its sessions still need attention and a collapsed ancestor must
|
|
1615
|
+
* keep counting them — so expansion is a rendering fact, never an input to the
|
|
1616
|
+
* aggregation. Short-circuiting expanded groups to `EMPTY_LIVE_STATUS` (the shape this
|
|
1617
|
+
* had before the tree-wide aggregator existed, harmless while nothing aggregated) makes
|
|
1618
|
+
* every collapsed parent undercount by the size of its expanded descendants.
|
|
1619
|
+
*
|
|
1620
|
+
* @param groups - derived groups, each with its member rows already populated.
|
|
1621
|
+
* @param waitingReviewSummary - dsh-gitea-dispatch waiting map, when that plugin is loaded.
|
|
1622
|
+
* @param scheduleSummary - dsh-later schedule map, when that plugin is loaded.
|
|
1623
|
+
*/
|
|
1624
|
+
function directStatusesForGroups(groups, waitingReviewSummary, scheduleSummary) {
|
|
1625
|
+
const map = /* @__PURE__ */ new Map();
|
|
1626
|
+
for (const group of groups) map.set(group.key, summarizeGroupStatus(group.sessions, group.sessions.map((s) => s.id), waitingReviewSummary, scheduleSummary));
|
|
1627
|
+
return map;
|
|
1628
|
+
}
|
|
1629
|
+
/**
|
|
1630
|
+
* Fold every group's DIRECT status up the workspace tree, so a collapsed folder
|
|
1631
|
+
* counts every descendant session. A group's own row shows no badge while it is
|
|
1632
|
+
* expanded, but its sessions still need attention and its collapsed ancestor must
|
|
1633
|
+
* keep counting them — so the walk never depends on a group's expanded state.
|
|
1634
|
+
*
|
|
1635
|
+
* @param direct - each group's own-member status, keyed by group key.
|
|
1636
|
+
* @param childrenByParent - the rendered tree: parent key (`undefined` = roots) → children.
|
|
1637
|
+
* @returns aggregated status per group key: own members plus the whole subtree.
|
|
1638
|
+
*/
|
|
1639
|
+
function aggregateGroupStatuses(direct, childrenByParent) {
|
|
1640
|
+
const aggregated = /* @__PURE__ */ new Map();
|
|
1641
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
1642
|
+
const fold = (key) => {
|
|
1643
|
+
const cached = aggregated.get(key);
|
|
1644
|
+
if (cached !== void 0) return cached;
|
|
1645
|
+
if (visiting.has(key)) return direct.get(key) ?? EMPTY_LIVE_STATUS;
|
|
1646
|
+
visiting.add(key);
|
|
1647
|
+
let total = direct.get(key) ?? EMPTY_LIVE_STATUS;
|
|
1648
|
+
for (const child of childrenByParent.get(key) ?? []) total = mergeLiveStatus(total, fold(child.key));
|
|
1649
|
+
visiting.delete(key);
|
|
1650
|
+
aggregated.set(key, total);
|
|
1651
|
+
return total;
|
|
1652
|
+
};
|
|
1653
|
+
for (const children of childrenByParent.values()) for (const child of children) fold(child.key);
|
|
1654
|
+
return aggregated;
|
|
1655
|
+
}
|
|
1656
|
+
/**
|
|
1657
|
+
* Attach each group's subtree-wide status, in derivation order. This is the producer
|
|
1658
|
+
* the sidebar renders from.
|
|
1659
|
+
*
|
|
1660
|
+
* Expanded groups carry their status too: their own row draws no badge (a decision
|
|
1661
|
+
* inside `groupLiveBadge`), but a collapsed ancestor still has to count them, so
|
|
1662
|
+
* expansion never suppresses the measurement.
|
|
1663
|
+
*
|
|
1664
|
+
* @param groups - derived rows, in the order they render.
|
|
1665
|
+
* @param direct - each group's own-member status, from {@link directStatusesForGroups}.
|
|
1666
|
+
* @param childrenByParent - the rendered tree: parent key (`undefined` = roots) → children.
|
|
1667
|
+
*/
|
|
1668
|
+
function withAggregatedStatus(groups, direct, childrenByParent) {
|
|
1669
|
+
const aggregated = aggregateGroupStatuses(direct, childrenByParent);
|
|
1670
|
+
return groups.map((group) => ({
|
|
1671
|
+
...group,
|
|
1672
|
+
liveStatus: aggregated.get(group.key) ?? EMPTY_LIVE_STATUS
|
|
1673
|
+
}));
|
|
1674
|
+
}
|
|
1675
|
+
//#endregion
|
|
1248
1676
|
//#region \0dsh-css:/home/wujue/workspace/dsh-plugins/dsh-group-status/src/client/rows/Rows.module.css.mjs
|
|
1249
1677
|
const css$3 = ".eCE9Ta_projectRow,.eCE9Ta_sessionRow{border-radius:var(--dsw-radius-md);padding:0 8px;cursor:pointer;user-select:none;color:var(--dsw-alias-label-primary);align-items:center;gap:6px;padding-inline-start:calc(8px + var(--dsh-workspace-indent,0px));display:flex}.eCE9Ta_projectRow:hover,.eCE9Ta_sessionRow:hover,.eCE9Ta_sessionRow.eCE9Ta_selected{background:var(--dsw-alias-interactive-bg-hover)}.eCE9Ta_searchResultRow{box-sizing:border-box;border-radius:var(--dsw-radius-lg);cursor:pointer;text-align:left;width:100%;min-height:48px;color:var(--dsw-alias-label-primary);background:0 0;border:none;flex-direction:column;align-items:stretch;padding:4px 8px;display:flex}.eCE9Ta_searchResultRow:hover,.eCE9Ta_searchResultRow.eCE9Ta_selected{background:var(--dsw-alias-interactive-bg-hover)}.eCE9Ta_searchResultHeading{align-items:center;min-width:0;display:flex}.eCE9Ta_searchResultTitle{text-overflow:ellipsis;white-space:nowrap;flex:0 auto;min-width:0;margin-left:4px;font-size:14px;line-height:20px;overflow:hidden}.eCE9Ta_searchResultMeta{align-items:center;gap:6px;min-width:0;margin-left:20px;display:flex}.eCE9Ta_searchResultWorkspace,.eCE9Ta_searchResultSnippet{text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:17px;overflow:hidden}.eCE9Ta_searchResultWorkspace{max-width:40%;color:var(--dsw-alias-label-tertiary);flex:none}.eCE9Ta_searchResultWorkspace:only-child{max-width:100%}.eCE9Ta_searchResultSnippet{min-width:0;color:var(--dsw-alias-label-secondary);flex:1}.eCE9Ta_projectRow{box-sizing:border-box;align-items:center;height:34px}.eCE9Ta_projectRow .eCE9Ta_rowActions{height:20px}.eCE9Ta_sessionRow{gap:0;height:32px}.eCE9Ta_sessionRow .eCE9Ta_title{margin:0 6px 0 4px}.eCE9Ta_slot{width:16px;height:20px;color:var(--dsw-alias-label-tertiary);flex:none;justify-content:center;align-items:center;display:inline-flex}.eCE9Ta_visuallyHidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}.eCE9Ta_folderActive{color:var(--dsw-alias-state-business-primary)}.eCE9Ta_projectRow .eCE9Ta_chevron{display:none}.eCE9Ta_projectRow:hover .eCE9Ta_chevron{display:inline-flex}.eCE9Ta_projectRow:hover .eCE9Ta_folder{display:none}.eCE9Ta_arrow{transition:transform .15s var(--ds-ease-in-out)}.eCE9Ta_arrowOpen{transform:rotate(90deg)}.eCE9Ta_projectText{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.eCE9Ta_title{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:14px;line-height:20px;overflow:hidden}.eCE9Ta_renameInput{border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-button-elevated-fill);min-width:0;color:inherit;outline:none;padding:0 2px;font-size:14px;line-height:20px}.eCE9Ta_sessionRow .eCE9Ta_title{flex:1}.eCE9Ta_sessionRow .eCE9Ta_title[data-scrolled]{mask-image:linear-gradient(90deg,#0000,#000 12px)}.eCE9Ta_sessionRow .eCE9Ta_title[data-clipped]{mask-image:linear-gradient(270deg,#0000,#000 12px)}.eCE9Ta_sessionRow .eCE9Ta_title[data-scrolled][data-clipped]{mask-image:linear-gradient(90deg,#0000,#000 12px calc(100% - 12px),#0000)}.eCE9Ta_meta{text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:20px;overflow:hidden}.eCE9Ta_time{color:var(--dsw-alias-label-tertiary);flex:none;font-size:10px;line-height:16px}.eCE9Ta_pinIndicator{width:16px;height:20px;color:var(--dsw-alias-label-caption);flex:none;justify-content:center;align-items:center;margin-left:6px;display:inline-flex}.eCE9Ta_sessionRow.eCE9Ta_archived .eCE9Ta_title,.eCE9Ta_searchResultRow.eCE9Ta_archived .eCE9Ta_searchResultTitle,.eCE9Ta_searchResultRow.eCE9Ta_archived .eCE9Ta_searchResultWorkspace,.eCE9Ta_searchResultRow.eCE9Ta_archived .eCE9Ta_searchResultSnippet{color:var(--dsw-alias-label-caption)}.eCE9Ta_dot{flex:none}.eCE9Ta_rowActions{flex:none;align-items:center;gap:10px;display:none}.eCE9Ta_projectRow:hover .eCE9Ta_rowActions,.eCE9Ta_sessionRow:hover .eCE9Ta_rowActions,.eCE9Ta_searchResultRow:hover .eCE9Ta_rowActions,.eCE9Ta_projectRow.eCE9Ta_menuOpen .eCE9Ta_rowActions,.eCE9Ta_sessionRow.eCE9Ta_menuOpen .eCE9Ta_rowActions{display:inline-flex}.eCE9Ta_searchResultHeading .eCE9Ta_rowActions{margin-left:auto}.eCE9Ta_sessionRow:hover .eCE9Ta_time,.eCE9Ta_sessionRow.eCE9Ta_menuOpen .eCE9Ta_time,.eCE9Ta_sessionRow:hover .eCE9Ta_pinIndicator,.eCE9Ta_sessionRow.eCE9Ta_menuOpen .eCE9Ta_pinIndicator{display:none}@media (hover:hover){.eCE9Ta_sessionRow:hover .eCE9Ta_title,.eCE9Ta_sessionRow.eCE9Ta_menuOpen .eCE9Ta_title{text-overflow:clip}}.eCE9Ta_projectRow.eCE9Ta_menuOpen,.eCE9Ta_sessionRow.eCE9Ta_menuOpen{background:var(--dsw-alias-interactive-bg-hover)}.eCE9Ta_sessionRow.eCE9Ta_dropBefore,.eCE9Ta_sessionRow.eCE9Ta_dropAfter{position:relative}.eCE9Ta_sessionRow.eCE9Ta_dropBefore:before,.eCE9Ta_sessionRow.eCE9Ta_dropAfter:after{content:\"\";z-index:1;background:linear-gradient(55deg, transparent calc(50% - 1px), var(--dsw-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 0 / 5px 7px no-repeat, linear-gradient(125deg, transparent calc(50% - 1px), var(--dsw-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 5px / 5px 7px no-repeat, linear-gradient(var(--dsw-alias-state-business-primary) 0 0) 4px 5px / calc(100% - 4px) 2px no-repeat;pointer-events:none;height:12px;position:absolute;left:0;right:4px}.eCE9Ta_sessionRow.eCE9Ta_dropBefore:before{top:-7px}.eCE9Ta_sessionRow.eCE9Ta_dropAfter:after{bottom:-7px}.eCE9Ta_hoverContent{flex-direction:column;gap:8px;display:flex}.eCE9Ta_hoverTitle{color:var(--dsw-alias-label-primary);overflow-wrap:break-word;font-size:14px;line-height:20px}.eCE9Ta_hoverPath{color:var(--dsw-alias-label-secondary);word-break:break-all;font-size:12px;line-height:16px}.eCE9Ta_hoverTime{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:16px}.eCE9Ta_hoverStatus{color:var(--dsw-alias-label-tertiary);align-items:center;gap:8px;font-size:12px;line-height:20px;display:flex}.eCE9Ta_groupBadge{flex:none;align-items:center;gap:3px;margin-inline-start:4px;display:inline-flex}.eCE9Ta_groupBadgeCount{color:var(--dsw-alias-label-tertiary);border:1px solid;border-radius:6px;padding:0 3px;font-size:9px;line-height:12px}@keyframes eCE9Ta_groupBadgePulse{0%,to{opacity:1}50%{opacity:.6}}.eCE9Ta_groupBadgeUrgent{animation:2s ease-in-out infinite eCE9Ta_groupBadgePulse}.eCE9Ta_hoverArchived svg{flex-shrink:0;margin:0 -4px}.eCE9Ta_iconButton{border-radius:var(--dsw-radius-xs);cursor:pointer;width:16px;height:16px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.eCE9Ta_iconButton:hover{color:var(--dsw-alias-label-primary)}.eCE9Ta_chevron{color:var(--dsw-alias-label-caption)}@media (prefers-reduced-motion:reduce){.eCE9Ta_arrow{transition:none;animation:none}}";
|
|
1250
1678
|
const tagId$3 = "dsh-group-status/Rows.module.css";
|
|
@@ -1301,17 +1729,23 @@ window.__ModuleLoader__.load({
|
|
|
1301
1729
|
/**
|
|
1302
1730
|
* Accessible label for a folded group's aggregated status, reusing the
|
|
1303
1731
|
* session-row locale keys so both surfaces read identically.
|
|
1732
|
+
*
|
|
1733
|
+
* 按设置过滤:关掉的来源不出现在标签里(aria-label 与徽标视觉一致)。
|
|
1304
1734
|
*/
|
|
1305
|
-
function groupLiveLabel(s, t) {
|
|
1735
|
+
function groupLiveLabel(s, t, settings) {
|
|
1306
1736
|
const parts = [];
|
|
1307
|
-
if (
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
if (
|
|
1313
|
-
|
|
1314
|
-
|
|
1737
|
+
if (settings.showPendingInBadge) {
|
|
1738
|
+
if (s.approval > 0) parts.push(`${s.approval} ${t("status.waitingApproval")}`);
|
|
1739
|
+
if (s.planReview > 0) parts.push(`${s.planReview} ${t("status.planReview")}`);
|
|
1740
|
+
if (s.question > 0) parts.push(`${s.question} ${t("status.waitingAnswer")}`);
|
|
1741
|
+
}
|
|
1742
|
+
if (settings.showRunningInBadge) {
|
|
1743
|
+
if (s.running > 0) parts.push(`${s.running} ${t("status.running")}`);
|
|
1744
|
+
if (s.subagents > 0) parts.push(t(s.subagents === 1 ? "status.subagentsRunning.one" : "status.subagentsRunning.other", { n: s.subagents }));
|
|
1745
|
+
}
|
|
1746
|
+
if (settings.showWaitingReviewInBadge && s.waitingReviewCount > 0) parts.push(`${s.waitingReviewCount} ${t("status.waitingReview")}`);
|
|
1747
|
+
if (settings.showScheduledInBadge && s.scheduledSessions > 0) parts.push(`${s.scheduledSessions} ${t("status.scheduled")}`);
|
|
1748
|
+
if (settings.showCompletedInBadge && s.done > 0) parts.push(`${s.done} ${t("status.completed")}`);
|
|
1315
1749
|
return parts.join(" · ");
|
|
1316
1750
|
}
|
|
1317
1751
|
/**
|
|
@@ -1321,20 +1755,19 @@ window.__ModuleLoader__.load({
|
|
|
1321
1755
|
*
|
|
1322
1756
|
* Count matches the dots: pending + active + completed + waiting-review + scheduled sessions.
|
|
1323
1757
|
*
|
|
1324
|
-
* `
|
|
1325
|
-
*
|
|
1326
|
-
* 自身的绿点不受影响:那是行级状态,不是分组提示。
|
|
1758
|
+
* `settings` 按来源过滤:关掉的来源不画点、不计数、不进 aria-label、不参与「是否显示徽标」
|
|
1759
|
+
* 的判断。会话行自身的状态不受影响:那是行级状态,不是分组提示。
|
|
1327
1760
|
*/
|
|
1328
|
-
function groupLiveBadge(group, t,
|
|
1761
|
+
function groupLiveBadge(group, t, settings) {
|
|
1329
1762
|
const s = group.liveStatus;
|
|
1330
1763
|
if (group.expanded || s === void 0) return null;
|
|
1331
|
-
const pending = s.approval + s.planReview + s.question;
|
|
1332
|
-
const active = s.running + s.subagents;
|
|
1333
|
-
const scheduled = s.scheduledSessions;
|
|
1334
|
-
const hasWaitingReview = s.waitingReviewCount > 0;
|
|
1335
|
-
const completed =
|
|
1764
|
+
const pending = settings.showPendingInBadge ? s.approval + s.planReview + s.question : 0;
|
|
1765
|
+
const active = settings.showRunningInBadge ? s.running + s.subagents : 0;
|
|
1766
|
+
const scheduled = settings.showScheduledInBadge ? s.scheduledSessions : 0;
|
|
1767
|
+
const hasWaitingReview = settings.showWaitingReviewInBadge && s.waitingReviewCount > 0;
|
|
1768
|
+
const completed = settings.showCompletedInBadge ? s.done : 0;
|
|
1336
1769
|
if (pending === 0 && active === 0 && completed === 0 && scheduled === 0 && !hasWaitingReview) return null;
|
|
1337
|
-
const label = groupLiveLabel(s, t);
|
|
1770
|
+
const label = groupLiveLabel(s, t, settings);
|
|
1338
1771
|
const dots = [];
|
|
1339
1772
|
if (pending > 0) dots.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "warning" }, "warn"));
|
|
1340
1773
|
if (active > 0) dots.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "ongoing" }, "run"));
|
|
@@ -1361,8 +1794,8 @@ window.__ModuleLoader__.load({
|
|
|
1361
1794
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconListPenOutlineRegular, { size: 14 })
|
|
1362
1795
|
}, "waiting-review"));
|
|
1363
1796
|
}
|
|
1364
|
-
const count = pending + active + completed + scheduled + s.waitingReviewCount;
|
|
1365
|
-
const countLabel = count > 99 ? "99+" : count
|
|
1797
|
+
const count = pending + active + completed + scheduled + (hasWaitingReview ? s.waitingReviewCount : 0);
|
|
1798
|
+
const countLabel = count > 99 ? "99+" : count >= settings.countThreshold ? String(count) : "";
|
|
1366
1799
|
const badgeClass = s.scheduleState === "overdue" || s.scheduleState === "urgent" || s.waitingReviewState === "overdue" ? `${Rows_module_css_default.groupBadge} ${Rows_module_css_default.groupBadgeUrgent}` : Rows_module_css_default.groupBadge;
|
|
1367
1800
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1368
1801
|
className: badgeClass,
|
|
@@ -1380,39 +1813,45 @@ window.__ModuleLoader__.load({
|
|
|
1380
1813
|
*
|
|
1381
1814
|
* Extracted so `WorkspaceHoverContent` in `Rows.tsx` only needs to map the
|
|
1382
1815
|
* returned array — the fork-specific status→line logic lives here.
|
|
1816
|
+
*
|
|
1817
|
+
* 按设置过滤:关掉的来源不出现在明细行里;`showHoverDetails=false` 时整段返回空。
|
|
1383
1818
|
*/
|
|
1384
|
-
function hoverStatusLines(status, t) {
|
|
1819
|
+
function hoverStatusLines(status, t, settings) {
|
|
1385
1820
|
const lines = [];
|
|
1386
|
-
if (status === void 0) return lines;
|
|
1387
|
-
if (
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1821
|
+
if (status === void 0 || !settings.showHoverDetails) return lines;
|
|
1822
|
+
if (settings.showPendingInBadge) {
|
|
1823
|
+
if (status.approval > 0) lines.push({
|
|
1824
|
+
state: "warning",
|
|
1825
|
+
text: `${status.approval} ${t("status.waitingApproval")}`
|
|
1826
|
+
});
|
|
1827
|
+
if (status.planReview > 0) lines.push({
|
|
1828
|
+
state: "warning",
|
|
1829
|
+
text: `${status.planReview} ${t("status.planReview")}`
|
|
1830
|
+
});
|
|
1831
|
+
if (status.question > 0) lines.push({
|
|
1832
|
+
state: "warning",
|
|
1833
|
+
text: `${status.question} ${t("status.waitingAnswer")}`
|
|
1834
|
+
});
|
|
1835
|
+
}
|
|
1836
|
+
if (settings.showRunningInBadge) {
|
|
1837
|
+
if (status.running > 0) lines.push({
|
|
1838
|
+
state: "ongoing",
|
|
1839
|
+
text: `${status.running} ${t("status.running")}`
|
|
1840
|
+
});
|
|
1841
|
+
if (status.subagents > 0) lines.push({
|
|
1842
|
+
state: "ongoing",
|
|
1843
|
+
text: t(status.subagents === 1 ? "status.subagentsRunning.one" : "status.subagentsRunning.other", { n: status.subagents })
|
|
1844
|
+
});
|
|
1845
|
+
}
|
|
1846
|
+
if (settings.showWaitingReviewInBadge && status.waitingReviewCount > 0) lines.push({
|
|
1408
1847
|
state: "warning",
|
|
1409
1848
|
text: `${status.waitingReviewCount} ${t("status.waitingReview")}`
|
|
1410
1849
|
});
|
|
1411
|
-
if (status.scheduledSessions > 0) lines.push({
|
|
1850
|
+
if (settings.showScheduledInBadge && status.scheduledSessions > 0) lines.push({
|
|
1412
1851
|
state: "warning",
|
|
1413
1852
|
text: `${status.scheduledSessions} ${t("status.scheduled")}`
|
|
1414
1853
|
});
|
|
1415
|
-
if (status.done > 0) lines.push({
|
|
1854
|
+
if (settings.showCompletedInBadge && status.done > 0) lines.push({
|
|
1416
1855
|
state: "done",
|
|
1417
1856
|
text: `${status.done} ${t("status.completed")}`
|
|
1418
1857
|
});
|
|
@@ -1543,8 +1982,8 @@ window.__ModuleLoader__.load({
|
|
|
1543
1982
|
})} ${pad2(d.getHours())}:${pad2(d.getMinutes())}` });
|
|
1544
1983
|
}
|
|
1545
1984
|
/** Hover-card body: workspace title, display directory path, absolute creation time. */
|
|
1546
|
-
function WorkspaceHoverContent({ label, cwd, createdAt, status, t }) {
|
|
1547
|
-
const lines = hoverStatusLines(status, t);
|
|
1985
|
+
function WorkspaceHoverContent({ label, cwd, createdAt, status, settings, t }) {
|
|
1986
|
+
const lines = hoverStatusLines(status, t, settings);
|
|
1548
1987
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1549
1988
|
className: Rows_module_css_default.hoverContent,
|
|
1550
1989
|
children: [
|
|
@@ -1586,7 +2025,7 @@ window.__ModuleLoader__.load({
|
|
|
1586
2025
|
* @param props.t - the browser root's locale seat.
|
|
1587
2026
|
* @returns the row element.
|
|
1588
2027
|
*/
|
|
1589
|
-
function ProjectRowItem({ group, containsCurrentDescendant = false, onToggle, onCreate, actions, drag, home, newShortcut,
|
|
2028
|
+
function ProjectRowItem({ group, containsCurrentDescendant = false, onToggle, onCreate, actions, drag, home, newShortcut, settings, t }) {
|
|
1590
2029
|
const row = group;
|
|
1591
2030
|
const label = row.workspaceId === void 0 ? t("group.ungrouped") : row.label;
|
|
1592
2031
|
const active = containsCurrentDescendant || group.expanded && group.containsCurrent;
|
|
@@ -1630,7 +2069,7 @@ window.__ModuleLoader__.load({
|
|
|
1630
2069
|
children: label
|
|
1631
2070
|
})
|
|
1632
2071
|
}),
|
|
1633
|
-
groupLiveBadge(
|
|
2072
|
+
groupLiveBadge(group, t, settings),
|
|
1634
2073
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1635
2074
|
className: Rows_module_css_default.rowActions,
|
|
1636
2075
|
children: [actions !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
@@ -1687,6 +2126,7 @@ window.__ModuleLoader__.load({
|
|
|
1687
2126
|
cwd: row.cwd === void 0 ? void 0 : abbreviateHomePath(row.cwd, home),
|
|
1688
2127
|
createdAt: row.createdAt,
|
|
1689
2128
|
status: row.expanded ? void 0 : row.liveStatus,
|
|
2129
|
+
settings,
|
|
1690
2130
|
t
|
|
1691
2131
|
}),
|
|
1692
2132
|
openDelayMs: 800,
|
|
@@ -2645,7 +3085,7 @@ window.__ModuleLoader__.load({
|
|
|
2645
3085
|
/** The scrolling session tree; unmounting drops the sessions subscription and local row limits. */
|
|
2646
3086
|
function SessionTree({ list, useSessionStatus, startSession, open, workspaces, ungroupedSessionIds, rowState, onLeaveArchivedOnly, workspaceReady, animationResetKey, usePanelInfo, onRenameRequest, onDeleteRequest, onSessionRenameRequest, renderSlot, insertWorkspaceBefore, nestWorkspaces, groupExpansion, setGroupExpanded, setSessionOrder, home, t, revealSessionId, onSessionRevealed, shortcuts, useBadgeSettings }) {
|
|
2647
3087
|
const panelActive = usePanelInfo((info) => info.activePanelId !== null);
|
|
2648
|
-
const
|
|
3088
|
+
const badgeSettings = useBadgeSettings((settings) => settings);
|
|
2649
3089
|
const statuses = useSessionStatus((s) => s);
|
|
2650
3090
|
const scheduleSummary = useScheduleSummaryHook?.();
|
|
2651
3091
|
const waitingReviewSummary = useWaitingSummaryHook?.();
|
|
@@ -2690,20 +3130,16 @@ window.__ModuleLoader__.load({
|
|
|
2690
3130
|
parents,
|
|
2691
3131
|
workspaces
|
|
2692
3132
|
]);
|
|
2693
|
-
const allGroupsExpanded = expandedGroups.length >= workspaces.length + 1 && workspaces.every((w) => expandedGroups.includes(w.workspaceId)) && expandedGroups.includes("");
|
|
2694
3133
|
const groups = (0, react.useMemo)(() => deriveGroups(list, workspaces, rowState, statuses, {
|
|
2695
3134
|
expandedGroups,
|
|
2696
3135
|
ungroupedOrder: ungroupedSessionIds
|
|
2697
|
-
}
|
|
3136
|
+
}), [
|
|
2698
3137
|
list,
|
|
2699
3138
|
workspaces,
|
|
2700
3139
|
rowState,
|
|
2701
3140
|
statuses,
|
|
2702
3141
|
expandedGroups,
|
|
2703
|
-
ungroupedSessionIds
|
|
2704
|
-
scheduleSummary,
|
|
2705
|
-
waitingReviewSummary,
|
|
2706
|
-
allGroupsExpanded
|
|
3142
|
+
ungroupedSessionIds
|
|
2707
3143
|
]);
|
|
2708
3144
|
(0, react.useEffect)(() => {
|
|
2709
3145
|
for (let key = revealGroup; key !== void 0; key = parents.get(key)) if (groupExpansion[key] === false || key === revealGroup && groupExpansion[key] !== true) setGroupExpanded(key, true);
|
|
@@ -2758,7 +3194,7 @@ window.__ModuleLoader__.load({
|
|
|
2758
3194
|
console.warn("workspace reorder rejected:", reason);
|
|
2759
3195
|
});
|
|
2760
3196
|
};
|
|
2761
|
-
const
|
|
3197
|
+
const childrenByParentForAggregation = (0, react.useMemo)(() => {
|
|
2762
3198
|
const rendered = new Set(groups.map((group) => group.key));
|
|
2763
3199
|
const children = /* @__PURE__ */ new Map();
|
|
2764
3200
|
for (const group of groups) {
|
|
@@ -2770,9 +3206,31 @@ window.__ModuleLoader__.load({
|
|
|
2770
3206
|
}
|
|
2771
3207
|
return children;
|
|
2772
3208
|
}, [groups, parents]);
|
|
3209
|
+
const directStatusByGroup = (0, react.useMemo)(() => directStatusesForGroups(groups, waitingReviewSummary, scheduleSummary), [
|
|
3210
|
+
groups,
|
|
3211
|
+
waitingReviewSummary,
|
|
3212
|
+
scheduleSummary
|
|
3213
|
+
]);
|
|
3214
|
+
const groupsWithAggregatedStatus = (0, react.useMemo)(() => withAggregatedStatus(groups, directStatusByGroup, childrenByParentForAggregation), [
|
|
3215
|
+
groups,
|
|
3216
|
+
directStatusByGroup,
|
|
3217
|
+
childrenByParentForAggregation
|
|
3218
|
+
]);
|
|
3219
|
+
const childrenByParent = (0, react.useMemo)(() => {
|
|
3220
|
+
const rendered = new Set(groupsWithAggregatedStatus.map((group) => group.key));
|
|
3221
|
+
const children = /* @__PURE__ */ new Map();
|
|
3222
|
+
for (const group of groupsWithAggregatedStatus) {
|
|
3223
|
+
let parent = parents.get(group.key);
|
|
3224
|
+
while (parent !== void 0 && !rendered.has(parent)) parent = parents.get(parent);
|
|
3225
|
+
const siblings = children.get(parent);
|
|
3226
|
+
if (siblings === void 0) children.set(parent, [group]);
|
|
3227
|
+
else siblings.push(group);
|
|
3228
|
+
}
|
|
3229
|
+
return children;
|
|
3230
|
+
}, [groupsWithAggregatedStatus, parents]);
|
|
2773
3231
|
const rootGroups = childrenByParent.get(void 0) ?? [];
|
|
2774
3232
|
const workspaceDropAtListStart = rootGroups[0]?.workspaceId !== void 0 && workspaceDrag?.over?.id === rootGroups[0].workspaceId && workspaceDrag.over.half === "before";
|
|
2775
|
-
const rowKeys =
|
|
3233
|
+
const rowKeys = groupsWithAggregatedStatus.length === 0 ? ["empty"] : [];
|
|
2776
3234
|
const renderGroup = (group, depth) => {
|
|
2777
3235
|
const workspaceId = group.workspaceId;
|
|
2778
3236
|
const children = childrenByParent.get(group.key) ?? [];
|
|
@@ -2848,7 +3306,7 @@ window.__ModuleLoader__.load({
|
|
|
2848
3306
|
group,
|
|
2849
3307
|
containsCurrentDescendant: currentAncestors.has(group.key),
|
|
2850
3308
|
home,
|
|
2851
|
-
|
|
3309
|
+
settings: badgeSettings,
|
|
2852
3310
|
t,
|
|
2853
3311
|
onToggle: () => {
|
|
2854
3312
|
if (group.expanded) setSessionLimits((limits) => ({
|
|
@@ -2879,7 +3337,7 @@ window.__ModuleLoader__.load({
|
|
|
2879
3337
|
role: "group",
|
|
2880
3338
|
children: childRows
|
|
2881
3339
|
}),
|
|
2882
|
-
sessions.map((node) => {
|
|
3340
|
+
group.expanded && sessions.map((node) => {
|
|
2883
3341
|
const sameGroupDrag = drag !== null && drag.accountKey === group.key;
|
|
2884
3342
|
const compatibleTarget = sameGroupDrag && drag.pinned === node.pinned;
|
|
2885
3343
|
const normalizeHalf = (half) => node.blank ? "after" : half;
|
|
@@ -2933,7 +3391,7 @@ window.__ModuleLoader__.load({
|
|
|
2933
3391
|
t
|
|
2934
3392
|
}, node.id);
|
|
2935
3393
|
}),
|
|
2936
|
-
collapsed.hiddenCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3394
|
+
group.expanded && collapsed.hiddenCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2937
3395
|
type: "button",
|
|
2938
3396
|
className: WorkspaceBrowser_module_css_default.sessionOverflowButton,
|
|
2939
3397
|
"data-row-key": `overflow:${group.key}`,
|
|
@@ -2962,8 +3420,8 @@ window.__ModuleLoader__.load({
|
|
|
2962
3420
|
label: t("section.sessions"),
|
|
2963
3421
|
rowKeys,
|
|
2964
3422
|
ready: list.phase === "ready" && workspaceReady && !nativeDragActive,
|
|
2965
|
-
resetKey:
|
|
2966
|
-
children: [
|
|
3423
|
+
resetKey: animationResetKey,
|
|
3424
|
+
children: [groupsWithAggregatedStatus.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmptySessions, {
|
|
2967
3425
|
rowState,
|
|
2968
3426
|
onLeaveArchivedOnly,
|
|
2969
3427
|
t
|
|
@@ -4319,7 +4777,29 @@ window.__ModuleLoader__.load({
|
|
|
4319
4777
|
"time.days": "{n}天",
|
|
4320
4778
|
"time.months": "{n}个月",
|
|
4321
4779
|
"time.years": "{n}年",
|
|
4322
|
-
"time.ago": "{t}前"
|
|
4780
|
+
"time.ago": "{t}前",
|
|
4781
|
+
"settings.badge.section": "折叠徽标",
|
|
4782
|
+
"settings.badge.showRunning": "显示「进行中」转圈",
|
|
4783
|
+
"settings.badge.showRunningHint": "关闭后,进行中的会话(含子智能体)不再画转圈,也不再计入折叠徽标的数字与悬停明细。",
|
|
4784
|
+
"settings.badge.showPending": "显示「待审批」黄点",
|
|
4785
|
+
"settings.badge.showPendingHint": "关闭后,等待审批 / 计划评审 / 等待回答的会话不再画黄点,也不再计入折叠徽标的数字与悬停明细。",
|
|
4786
|
+
"settings.badge.showCompleted": "显示「已完成」绿点",
|
|
4787
|
+
"settings.badge.showCompletedHint": "关闭后,「已完成」的会话不再画绿点,也不再计入折叠徽标的数字与悬停明细。会话行自身的绿点不受影响。",
|
|
4788
|
+
"settings.badge.showScheduled": "显示「定时任务」时钟",
|
|
4789
|
+
"settings.badge.showScheduledHint": "关闭后,带定时提醒的会话(来自 dsh-later)不再画时钟图标,也不再计入折叠徽标的数字与悬停明细。",
|
|
4790
|
+
"settings.badge.showWaitingReview": "显示「等待评审」列表笔",
|
|
4791
|
+
"settings.badge.showWaitingReviewHint": "关闭后,等待代码评审的会话(来自 dsh-gitea-dispatch)不再画列表笔图标,也不再计入折叠徽标的数字与悬停明细。",
|
|
4792
|
+
"settings.hover.section": "悬停明细",
|
|
4793
|
+
"settings.hover.showDetails": "显示状态明细行",
|
|
4794
|
+
"settings.hover.showDetailsHint": "关闭后,悬停卡只显示工作区标题、路径和创建时间——不再显示聚合状态明细行。",
|
|
4795
|
+
"settings.count.section": "数字显示",
|
|
4796
|
+
"settings.count.threshold": "数字显示阈值",
|
|
4797
|
+
"settings.count.thresholdHint": "数量 >= 此值时才在徽标上显示数字。默认 2(即 count=1 时省略,因为状态点已表达\"有东西\")。设为 1 则始终显示。",
|
|
4798
|
+
"settings.save": "保存",
|
|
4799
|
+
"settings.saving": "保存中…",
|
|
4800
|
+
"settings.saveFailed": "本部署没有接受这些值,已保留供你修改。",
|
|
4801
|
+
"settings.readOnly": "本部署的设置为只读。",
|
|
4802
|
+
"settings.unavailable": "该插件当前未加载,暂时无法配置。"
|
|
4323
4803
|
};
|
|
4324
4804
|
/** English dictionary, checked complete against the zh key set. */
|
|
4325
4805
|
const en = {
|
|
@@ -4436,12 +4916,41 @@ window.__ModuleLoader__.load({
|
|
|
4436
4916
|
"time.days": "{n}d",
|
|
4437
4917
|
"time.months": "{n}mo",
|
|
4438
4918
|
"time.years": "{n}y",
|
|
4439
|
-
"time.ago": "{t} ago"
|
|
4919
|
+
"time.ago": "{t} ago",
|
|
4920
|
+
"settings.badge.section": "Collapsed badge",
|
|
4921
|
+
"settings.badge.showRunning": "Show the running dot",
|
|
4922
|
+
"settings.badge.showRunningHint": "When off, running Sessions (and subagents) stop drawing the spinning dot and drop out of the collapsed badge count and its hover details.",
|
|
4923
|
+
"settings.badge.showPending": "Show the pending dot",
|
|
4924
|
+
"settings.badge.showPendingHint": "When off, Sessions waiting for approval / plan review / answer stop drawing the yellow dot and drop out of the collapsed badge count and its hover details.",
|
|
4925
|
+
"settings.badge.showCompleted": "Show the completed dot",
|
|
4926
|
+
"settings.badge.showCompletedHint": "When off, completed Sessions stop drawing the dot and drop out of the collapsed badge count and its hover details. The Session row's own dot is unaffected.",
|
|
4927
|
+
"settings.badge.showScheduled": "Show the scheduled icon",
|
|
4928
|
+
"settings.badge.showScheduledHint": "When off, Sessions with scheduled reminders (from dsh-later) stop drawing the clock icon and drop out of the collapsed badge count and its hover details.",
|
|
4929
|
+
"settings.badge.showWaitingReview": "Show the waiting-for-review icon",
|
|
4930
|
+
"settings.badge.showWaitingReviewHint": "When off, Sessions waiting for code review (from dsh-gitea-dispatch) stop drawing the list-pen icon and drop out of the collapsed badge count and its hover details.",
|
|
4931
|
+
"settings.hover.section": "Hover details",
|
|
4932
|
+
"settings.hover.showDetails": "Show status detail lines",
|
|
4933
|
+
"settings.hover.showDetailsHint": "When off, the hover card shows only the workspace title, path, and creation time — no aggregated status lines.",
|
|
4934
|
+
"settings.count.section": "Count display",
|
|
4935
|
+
"settings.count.threshold": "Count display threshold",
|
|
4936
|
+
"settings.count.thresholdHint": "The count is shown on the badge only when the total is >= this value. Default 2 (omits the count when it equals 1 — the dots already convey \"something is happening\"). Set to 1 to always show.",
|
|
4937
|
+
"settings.save": "Save",
|
|
4938
|
+
"settings.saving": "Saving…",
|
|
4939
|
+
"settings.saveFailed": "The deployment did not accept these values; they were left for you to correct.",
|
|
4940
|
+
"settings.readOnly": "This deployment stores settings read-only.",
|
|
4941
|
+
"settings.unavailable": "This plugin is not loaded, so it cannot be configured right now."
|
|
4440
4942
|
};
|
|
4441
4943
|
//#endregion
|
|
4442
4944
|
//#region src/client/index.ts
|
|
4443
4945
|
/** Dictionary namespace owned by this plugin. */
|
|
4444
4946
|
const NS = "workspace";
|
|
4947
|
+
/** 插件详情页配置表单的槽位名(按包名寻址;契约见 `plugin-manager-slot.d.ts`)。 */
|
|
4948
|
+
const CONFIG_CARD_SLOT = "plugins.bundle.config";
|
|
4949
|
+
/**
|
|
4950
|
+
* 插件管理器分发 `plugins.bundle.config` 用的键 = **包名**(不是条目 id)。
|
|
4951
|
+
* 本包的补丁把行 id 也写成同名,但两者是不同概念,别改成 ENTRY_ID 的引用。
|
|
4952
|
+
*/
|
|
4953
|
+
const CONFIG_CARD_KEY = "dsh-group-status";
|
|
4445
4954
|
/**
|
|
4446
4955
|
* Required services (cordis fiber inject). The target slots are declared by
|
|
4447
4956
|
* the ui-sidebar / ui-conversation applies, whose activation order relative
|
|
@@ -4494,12 +5003,21 @@ window.__ModuleLoader__.load({
|
|
|
4494
5003
|
ctx.slots.provideRoot({ hooks: { badgeSettings } });
|
|
4495
5004
|
ctx.inject(["configForms"], (configCtx) => {
|
|
4496
5005
|
const forms = configCtx.get("configForms");
|
|
4497
|
-
|
|
5006
|
+
if (forms === void 0 || typeof forms.get !== "function") return;
|
|
5007
|
+
const scope = forms.get(ENTRY_ID);
|
|
4498
5008
|
if (scope === void 0) return;
|
|
4499
5009
|
badgeSettings.publish(scope.getSnapshot());
|
|
4500
5010
|
configCtx.effect(() => scope.subscribe(() => {
|
|
4501
5011
|
badgeSettings.publish(scope.getSnapshot());
|
|
4502
5012
|
}), "dsh-group-status: badge settings");
|
|
5013
|
+
const whileServed = forms.whileServed?.bind(forms);
|
|
5014
|
+
if (whileServed === void 0) return;
|
|
5015
|
+
configCtx.effect(() => whileServed([ENTRY_ID], () => ctx.slots.inject(CONFIG_CARD_SLOT, () => ctx.slots.register({
|
|
5016
|
+
name: CONFIG_CARD_SLOT,
|
|
5017
|
+
key: CONFIG_CARD_KEY,
|
|
5018
|
+
locale: NS,
|
|
5019
|
+
inject: () => ({ scope })
|
|
5020
|
+
}, BadgeSettingsCard))), "dsh-group-status: plugins-page card");
|
|
4503
5021
|
});
|
|
4504
5022
|
const searchSessions = async (query, signal) => {
|
|
4505
5023
|
const result = await sessions.search(query, signal);
|