dsh-project-room 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/client.js +169 -50
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -387,15 +387,44 @@ window.__ModuleLoader__.load({
|
|
|
387
387
|
}
|
|
388
388
|
.pr-loose-msg { font-size: 10px; color: var(--dsw-alias-label-secondary); padding: 0 6px 6px; line-height: 1.5; flex: 0 0 auto; }
|
|
389
389
|
|
|
390
|
-
/* 运行中快捷栏(对齐 AI-Agent-Hub
|
|
391
|
-
.pr-runbar { display: flex; flex-
|
|
390
|
+
/* 运行中快捷栏(对齐 AI-Agent-Hub):两段竖排 —— 上「运行中」、下「完成未读」 */
|
|
391
|
+
.pr-runbar { display: flex; flex-direction: column; gap: 4px; padding: 2px 4px 6px; flex: 0 0 auto; min-width: 0; }
|
|
392
|
+
/* 2026-11-11 用户令「快捷栏正在运行和完成未读的分开放」:
|
|
393
|
+
每段 = 一行小标题(色点/✓ + 文案 + 计数)+ 可折行的 pill 区,段间用极细线分隔。 */
|
|
394
|
+
.pr-runbar-sec { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
395
|
+
.pr-runbar-sec + .pr-runbar-sec {
|
|
396
|
+
border-top: 1px solid var(--dsw-alias-border-l1);
|
|
397
|
+
padding-top: 3px;
|
|
398
|
+
}
|
|
399
|
+
.pr-runbar-sec-title {
|
|
400
|
+
display: flex; align-items: center; gap: 4px; min-width: 0;
|
|
401
|
+
padding: 0 2px; font-size: 9px; line-height: 1.4;
|
|
402
|
+
color: var(--dsw-alias-label-secondary); letter-spacing: .03em;
|
|
403
|
+
}
|
|
404
|
+
.pr-runbar-sec[data-kind="done"] .pr-runbar-sec-title { color: var(--dsw-alias-brand-primary); }
|
|
405
|
+
.pr-runbar-sec-dot {
|
|
406
|
+
width: 5px; height: 5px; border-radius: 50%; flex: 0 0 auto;
|
|
407
|
+
background: var(--dsw-alias-state-success-primary);
|
|
408
|
+
}
|
|
409
|
+
.pr-runbar-sec-check {
|
|
410
|
+
width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto;
|
|
411
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
412
|
+
background: var(--dsw-alias-brand-primary); color: #fff;
|
|
413
|
+
font-size: 8px; font-weight: 700; line-height: 1;
|
|
414
|
+
}
|
|
415
|
+
.pr-runbar-sec-label { flex: 0 0 auto; white-space: nowrap; }
|
|
416
|
+
.pr-runbar-sec-count {
|
|
417
|
+
flex: 0 0 auto; font-variant-numeric: tabular-nums; opacity: .75;
|
|
418
|
+
}
|
|
419
|
+
.pr-runbar-sec-body { display: flex; flex-wrap: wrap; gap: 3px; min-width: 0; }
|
|
392
420
|
/* 快捷栏 pill 也显示分身:每个 pill 变成「按钮 + 树状子行」的竖排小单元,
|
|
393
421
|
子行样式与侧栏芯片的 pr-clone-row / pr-tree-rail 完全一致。 */
|
|
394
422
|
.pr-run-wrap { display: inline-flex; flex-direction: column; gap: 1px; align-items: flex-start; }
|
|
395
423
|
.pr-run-clones { display: flex; flex-direction: column; gap: 1px; padding-left: 6px; margin: 0 0 1px; }
|
|
396
424
|
.pr-run-clones .pr-clone-row { font-size: 9px; line-height: 1.4; }
|
|
397
425
|
.pr-readall {
|
|
398
|
-
|
|
426
|
+
/* 2026-11-11:一键已读搬进「完成未读」区的小标题里,靠右对齐 */
|
|
427
|
+
flex: 0 0 auto; margin: 0 0 0 auto; padding: 1px 6px; border-radius: 8px;
|
|
399
428
|
border: 1px solid var(--dsw-alias-border-l1); background: transparent;
|
|
400
429
|
color: var(--dsw-alias-brand-primary); font-size: 9.5px; line-height: 1.4;
|
|
401
430
|
white-space: nowrap; cursor: pointer;
|
|
@@ -819,23 +848,40 @@ function summarizeMessage(raw) {
|
|
|
819
848
|
return () => { cancelled = true; clearInterval(timer); };
|
|
820
849
|
|
|
821
850
|
}, []);
|
|
822
|
-
//
|
|
823
|
-
//
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
851
|
+
// ── 幽灵会话「事后隐藏」(2026-11-10 用户令:白名单改事后隐藏)──────────────
|
|
852
|
+
// 背景:曾经用「磁盘活会话白名单」全量过滤(每 120s 拉 /project-room/alive,
|
|
853
|
+
// 不在白名单里的会话直接不显示)。它确实能藏住「前端快照有、磁盘目录没了」的幽灵
|
|
854
|
+
// (归档必报 no such session,DSH 又没有删除 API),但代价是**新建会话要等下一次
|
|
855
|
+
// 轮询才认识** —— 分诊分身只活 30 秒,正好卡在两次轮询之间,侧栏从来没显示过它。
|
|
856
|
+
//
|
|
857
|
+
// 用户 2026-11-10 明确选了「事后隐藏」:默认**不过滤任何会话**,只有真的点归档且
|
|
858
|
+
// 失败时,才把那个 id 记进 localStorage 的隐藏集,下次渲染跳过它。
|
|
859
|
+
// 好处:新会话零延迟显示;幽灵删一次之后就消失。
|
|
860
|
+
const HIDDEN_KEY = 'project-room:hidden-sessions';
|
|
861
|
+
const bareId = (v) => String(v || '').replace(/^session-/, '').toLowerCase();
|
|
862
|
+
const [hiddenIds, setHiddenIds] = React.useState(() => {
|
|
863
|
+
try {
|
|
864
|
+
const raw = window.localStorage.getItem(HIDDEN_KEY);
|
|
865
|
+
const arr = raw ? JSON.parse(raw) : [];
|
|
866
|
+
return new Set(Array.isArray(arr) ? arr.map(bareId) : []);
|
|
867
|
+
} catch (error) { return new Set(); }
|
|
868
|
+
});
|
|
869
|
+
const hideSession = React.useCallback((id) => {
|
|
870
|
+
const bare = bareId(id);
|
|
871
|
+
if (!bare) return;
|
|
872
|
+
setHiddenIds((prev) => {
|
|
873
|
+
if (prev.has(bare)) return prev;
|
|
874
|
+
const next = new Set(prev);
|
|
875
|
+
next.add(bare);
|
|
828
876
|
try {
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
const timer = setInterval(load, 120000);
|
|
838
|
-
return () => { cancelled = true; clearInterval(timer); };
|
|
877
|
+
window.localStorage.setItem(HIDDEN_KEY, JSON.stringify([...next]));
|
|
878
|
+
} catch (error) { /* 存不下也不影响本次隐藏 */ }
|
|
879
|
+
return next;
|
|
880
|
+
});
|
|
881
|
+
}, []);
|
|
882
|
+
const unhideAll = React.useCallback(() => {
|
|
883
|
+
setHiddenIds(new Set());
|
|
884
|
+
try { window.localStorage.removeItem(HIDDEN_KEY); } catch (error) { /* ignore */ }
|
|
839
885
|
}, []);
|
|
840
886
|
|
|
841
887
|
// 1s 心跳:驱动「今日运行时长」与相对时间
|
|
@@ -853,10 +899,9 @@ function summarizeMessage(raw) {
|
|
|
853
899
|
for (const [id, session] of Object.entries(byId)) {
|
|
854
900
|
if (!session || session.blank) continue;
|
|
855
901
|
if (archived.has(String(id))) continue;
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
if (aliveIds && !aliveIds.has(String(id).replace(/^session-/, '').toLowerCase())) continue;
|
|
902
|
+
// 2026-11-10(用户令):白名单改「事后隐藏」——只跳过**归档失败过**的会话,
|
|
903
|
+
// 不再用磁盘白名单全量过滤(那样会让新建会话因轮询延迟而整段生命期不可见)。
|
|
904
|
+
if (hiddenIds.has(bareId(id))) continue;
|
|
860
905
|
const parsed = projectOfTitle(session.displayTitle || session.title);
|
|
861
906
|
const project = parsed ? parsed.project : LOOSE_GROUP;
|
|
862
907
|
const member = parsed ? parsed.member : (session.displayTitle || session.title || String(id).slice(-6));
|
|
@@ -878,7 +923,7 @@ function summarizeMessage(raw) {
|
|
|
878
923
|
});
|
|
879
924
|
}
|
|
880
925
|
return map;
|
|
881
|
-
}, [byId, archived,
|
|
926
|
+
}, [byId, archived, hiddenIds]);
|
|
882
927
|
|
|
883
928
|
// 空闲时长补全(2026-11-09):对还没有 lastActive 的会话拉 /hub/session-events?limit=1
|
|
884
929
|
// 取最后一条事件的 time。分身(标题带|)优先——用户最常盯的就是分身行。
|
|
@@ -999,10 +1044,9 @@ function summarizeMessage(raw) {
|
|
|
999
1044
|
// 「未分组」= 标题里没有分隔符的会话(历史测试/一次性对话)。
|
|
1000
1045
|
// 默认不显示,避免一大坨散落会话把项目列表挤下去;需要时点头部按钮展开。
|
|
1001
1046
|
const looseAll = groups.get(LOOSE_GROUP) || [];
|
|
1002
|
-
//
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
: looseAll;
|
|
1047
|
+
// 2026-11-10(用户令):白名单改「事后隐藏」后,groups 里已经按 hiddenIds 过滤过了,
|
|
1048
|
+
// 这里不再二次过滤(否则又回到「新建会话要等白名单」的老问题)。
|
|
1049
|
+
const looseMembers = looseAll;
|
|
1006
1050
|
const shownProjects = React.useMemo(
|
|
1007
1051
|
() => projectNames.filter((name) => showLoose || name !== LOOSE_GROUP),
|
|
1008
1052
|
[projectNames, showLoose],
|
|
@@ -1063,8 +1107,13 @@ function summarizeMessage(raw) {
|
|
|
1063
1107
|
});
|
|
1064
1108
|
}, [groups, unreadMap]);
|
|
1065
1109
|
|
|
1110
|
+
// 2026-11-11 用户令「快捷栏正在运行和完成未读的分开放」:
|
|
1111
|
+
// 原来两种 pill 混在一个 flex 行里(完成未读还置顶排最前),用户扫一眼分不清谁在跑。
|
|
1112
|
+
// 现在拆成两个区、各带标题;渲染顺序按用户第二次要求 = **完成未读在上、运行中在下**。
|
|
1113
|
+
const runningGroups = runGroups.filter((group) => group.mode === 'running');
|
|
1114
|
+
const doneUnreadGroups = runGroups.filter((group) => group.mode === 'done');
|
|
1066
1115
|
// 完成未读的组数(决定是否显示「全部已读」按钮)
|
|
1067
|
-
const doneGroups =
|
|
1116
|
+
const doneGroups = doneUnreadGroups.length;
|
|
1068
1117
|
|
|
1069
1118
|
const setPin = (name) => {
|
|
1070
1119
|
const next = pinned.includes(name) ? pinned.filter((n) => n !== name) : [...pinned, name];
|
|
@@ -1090,11 +1139,25 @@ function summarizeMessage(raw) {
|
|
|
1090
1139
|
const bodyIds = group.members.filter((m) => !m.isClone).map((m) => String(m.id));
|
|
1091
1140
|
const isCurrent = Boolean(cursorSessionId) && bodyIds.includes(String(cursorSessionId));
|
|
1092
1141
|
const done = group.mode === 'done';
|
|
1093
|
-
|
|
1094
|
-
|
|
1142
|
+
// 2026-11-11 修「本体没有运行、快捷栏还在显示计时,而侧边栏正常」(用户截图):
|
|
1143
|
+
// 这个 pill 代表的是**本体**——名字取 group.name(本体名)、点击开本体、data-session-ids
|
|
1144
|
+
// 也只有本体。所以绿点与已运行时长必须来自**本体自己的 running**。
|
|
1145
|
+
// 原实现取 `group.running` 里最近活跃的那个成员:分身还在跑时,那一项就是分身,
|
|
1146
|
+
// 于是分身的已运行时长被画到本体 pill 上(截图里 pill 的 2m29s 与分身子行的 2m29s
|
|
1147
|
+
// 完全相等,而侧栏本体芯片没有时长 = 本体当时是空闲的)。
|
|
1148
|
+
// 侧栏一直是对的:本体芯片按 m.running 判、分身行按 c.running 判,两条各算各的。
|
|
1149
|
+
// 分身的运行状态由下面 .pr-run-clones 子行自己显示,信息不丢。
|
|
1150
|
+
// 孤儿分身组(本体已归档/改名,group.name 就是分身名)没有本体可参照 → 仍用组内运行成员。
|
|
1151
|
+
const bodyMemberOfGroup = (() => {
|
|
1152
|
+
const list = (groups && typeof groups.get === 'function') ? (groups.get(group.project) || []) : [];
|
|
1153
|
+
return list.find((m) => !m.isClone && m.name === group.name) || null;
|
|
1154
|
+
})();
|
|
1155
|
+
const runMember = (bodyMemberOfGroup ? [bodyMemberOfGroup] : (group.running.length ? group.running : group.members))
|
|
1156
|
+
.filter((m) => m && m.running)
|
|
1095
1157
|
.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0))[0];
|
|
1158
|
+
const bodyRunning = Boolean(runMember);
|
|
1096
1159
|
const startedAt = runMember ? runStartRef.current.get(runMember.id) : undefined;
|
|
1097
|
-
const elapsed = !done &&
|
|
1160
|
+
const elapsed = !done && bodyRunning && startedAt ? formatDuration(tick - startedAt) : '';
|
|
1098
1161
|
const emoji = avatars && typeof avatars[group.name] === 'string' ? avatars[group.name].trim() : '';
|
|
1099
1162
|
const hue = hashHue(group.project || group.name);
|
|
1100
1163
|
return h('div', { className: 'pr-run-wrap', key: 'wrap-' + group.key }, h('button', {
|
|
@@ -1106,7 +1169,9 @@ function summarizeMessage(raw) {
|
|
|
1106
1169
|
// 原来放全组成员 → 点分身时 pill(代表本体)也一起高亮。
|
|
1107
1170
|
'data-session-ids': group.members.filter((mb) => !mb.isClone).map((mb) => String(mb.id)).join(','),
|
|
1108
1171
|
title: `${group.project ? group.project + ' · ' : ''}${group.name}`
|
|
1109
|
-
|
|
1172
|
+
// 2026-11-11:不再对空闲的本体谎称「运行中」——本体没跑时 pill 只报「分身运行中」。
|
|
1173
|
+
+ (done ? ' · 完成待查看'
|
|
1174
|
+
: (bodyRunning ? (elapsed ? ` · 运行中 ${elapsed}` : ' · 运行中') : ' · 本体空闲(分身运行中)'))
|
|
1110
1175
|
+ (group.extra ? `(含 ${group.extra} 个分身)` : ''),
|
|
1111
1176
|
onClick: (event) => {
|
|
1112
1177
|
event.stopPropagation();
|
|
@@ -1153,7 +1218,9 @@ function summarizeMessage(raw) {
|
|
|
1153
1218
|
group.extra > 0 ? h('span', { className: 'pr-run-badge', title: `${group.extra} 个分身` }, `+${group.extra}`) : null,
|
|
1154
1219
|
done
|
|
1155
1220
|
? h('span', { className: 'pr-run-done', title: '已完成,点开查看' }, h('span', { className: 'pr-run-check' }, '✓'), '完成')
|
|
1156
|
-
|
|
1221
|
+
// 2026-11-11:绿点也只跟本体走——本体空闲、只剩分身在跑时不给本体挂绿点
|
|
1222
|
+
// (分身子行自己有 pr-clone-dot.on,不丢状态)。
|
|
1223
|
+
: (bodyRunning ? h('span', { className: 'pr-running-dot', title: '运行中' }) : null),
|
|
1157
1224
|
elapsed ? h('span', { className: 'pr-run-elapsed' }, elapsed) : null,
|
|
1158
1225
|
),
|
|
1159
1226
|
group.clones && group.clones.length
|
|
@@ -1217,8 +1284,12 @@ function summarizeMessage(raw) {
|
|
|
1217
1284
|
});
|
|
1218
1285
|
const data = await res.json();
|
|
1219
1286
|
if (data && data.ok) {
|
|
1220
|
-
|
|
1221
|
-
|
|
1287
|
+
// 2026-11-10(用户令「事后隐藏」):归档失败 = 磁盘上根本没这个会话(幽灵)。
|
|
1288
|
+
// 既然删不掉,就记进隐藏集,下次渲染跳过它。
|
|
1289
|
+
const failedList = Array.isArray(data.failed) ? data.failed : [];
|
|
1290
|
+
for (const f of failedList) hideSession(f && f.id);
|
|
1291
|
+
const failNote = failedList.length
|
|
1292
|
+
? `,失败 ${failedList.length}(${String((failedList[0] && failedList[0].error) || '').slice(0, 60)};已隐藏)`
|
|
1222
1293
|
: '';
|
|
1223
1294
|
setClearMsg(`已归档 ${data.archived}/${data.total}${failNote}`);
|
|
1224
1295
|
console.log('[project-room] archive result', data);
|
|
@@ -1260,8 +1331,20 @@ function summarizeMessage(raw) {
|
|
|
1260
1331
|
body: JSON.stringify({ sessionIds: [menu.id] }),
|
|
1261
1332
|
});
|
|
1262
1333
|
const data = await res.json();
|
|
1263
|
-
|
|
1264
|
-
|
|
1334
|
+
const okArchived = Boolean(data && data.ok && data.archived);
|
|
1335
|
+
if (okArchived) {
|
|
1336
|
+
setMenuMsg('已归档');
|
|
1337
|
+
} else {
|
|
1338
|
+
// 2026-11-10(用户令「事后隐藏」):单个归档失败 = 幽灵会话,记进隐藏集。
|
|
1339
|
+
hideSession(menu.id);
|
|
1340
|
+
const reason = (data && data.failed && data.failed[0] && data.failed[0].error)
|
|
1341
|
+
|| (data && data.error) || '未知';
|
|
1342
|
+
setMenuMsg(`失败:${reason}(已隐藏)`.slice(0, 90));
|
|
1343
|
+
}
|
|
1344
|
+
} catch (error) {
|
|
1345
|
+
hideSession(menu.id);
|
|
1346
|
+
setMenuMsg(`失败:${String((error && error.message) || error)}(已隐藏)`.slice(0, 90));
|
|
1347
|
+
}
|
|
1265
1348
|
},
|
|
1266
1349
|
}, '归档这个会话'),
|
|
1267
1350
|
h('button', { className: 'pr-menu-item', onClick: () => { copy(menu.id, ' sessionId'); } }, '复制 sessionId'),
|
|
@@ -1296,6 +1379,24 @@ function summarizeMessage(raw) {
|
|
|
1296
1379
|
'注意:这只影响本插件的排序与默认名。若要让「管理者协议注入」也认这个名字,'
|
|
1297
1380
|
+ '还需要改 hub-desktop-ui 里的 MANAGER_NAMES(那是另一个插件的硬编码)。'),
|
|
1298
1381
|
settings.msg ? h('div', { className: 'pr-err' }, settings.msg) : null,
|
|
1382
|
+
// 2026-11-10(用户令「事后隐藏」):归档失败过的会话被记进 localStorage 隐藏集,
|
|
1383
|
+
// 这里提供恢复入口,免得误隐藏之后找不回来。
|
|
1384
|
+
h('div', { className: 'pr-hint', style: { marginTop: '10px' } },
|
|
1385
|
+
`已隐藏的会话:${hiddenIds.size} 个`),
|
|
1386
|
+
h('button', {
|
|
1387
|
+
type: 'button',
|
|
1388
|
+
disabled: hiddenIds.size === 0,
|
|
1389
|
+
style: {
|
|
1390
|
+
marginTop: '4px', padding: '4px 10px', borderRadius: '6px', fontSize: '11px',
|
|
1391
|
+
border: '1px solid var(--dsw-alias-border-secondary, #3a3a42)',
|
|
1392
|
+
background: 'transparent', color: 'inherit',
|
|
1393
|
+
cursor: hiddenIds.size === 0 ? 'default' : 'pointer',
|
|
1394
|
+
opacity: hiddenIds.size === 0 ? 0.45 : 1,
|
|
1395
|
+
},
|
|
1396
|
+
onClick: () => { unhideAll(); setSettings({ ...settings, msg: '已恢复全部隐藏的会话' }); },
|
|
1397
|
+
}, '恢复全部隐藏的会话'),
|
|
1398
|
+
h('div', { className: 'pr-hint', style: { fontSize: '10px' } },
|
|
1399
|
+
'(这些是归档时报 no such session 的幽灵会话,磁盘上已不存在;恢复后会重新出现在侧栏)'),
|
|
1299
1400
|
h('div', { className: 'pr-hint', style: { marginTop: '10px' } }, '显示'),
|
|
1300
1401
|
h('label', { style: { display: 'flex', alignItems: 'center', gap: '6px', fontSize: '11px', cursor: 'pointer', padding: '3px 0' } },
|
|
1301
1402
|
h('input', {
|
|
@@ -1427,19 +1528,37 @@ function summarizeMessage(raw) {
|
|
|
1427
1528
|
renderNewProject(),
|
|
1428
1529
|
renderSettings(),
|
|
1429
1530
|
h('div', { className: 'pr-runbar' },
|
|
1430
|
-
//
|
|
1431
|
-
|
|
1432
|
-
? h('
|
|
1433
|
-
className: 'pr-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1531
|
+
// ── 区①:完成未读(2026-11-11 用户令:这一区排在运行中前面;一键已读只清这一区)──
|
|
1532
|
+
doneUnreadGroups.length
|
|
1533
|
+
? h('div', { className: 'pr-runbar-sec', 'data-kind': 'done' },
|
|
1534
|
+
h('div', { className: 'pr-runbar-sec-title' },
|
|
1535
|
+
h('span', { className: 'pr-runbar-sec-check' }, '✓'),
|
|
1536
|
+
h('span', { className: 'pr-runbar-sec-label' }, '完成未读'),
|
|
1537
|
+
h('span', { className: 'pr-runbar-sec-count' }, String(doneUnreadGroups.length)),
|
|
1538
|
+
h('button', {
|
|
1539
|
+
className: 'pr-readall',
|
|
1540
|
+
title: `全部已读(清掉 ${doneGroups} 个完成待查看)`,
|
|
1541
|
+
onClick: (event) => {
|
|
1542
|
+
event.stopPropagation();
|
|
1543
|
+
saveJson('projectRoom.unread', {});
|
|
1544
|
+
setUnread({});
|
|
1545
|
+
},
|
|
1546
|
+
}, '全部已读'),
|
|
1547
|
+
),
|
|
1548
|
+
h('div', { className: 'pr-runbar-sec-body' }, doneUnreadGroups.map(renderRunPill)),
|
|
1549
|
+
)
|
|
1550
|
+
: null,
|
|
1551
|
+
// ── 区②:运行中 ──
|
|
1552
|
+
runningGroups.length
|
|
1553
|
+
? h('div', { className: 'pr-runbar-sec', 'data-kind': 'running' },
|
|
1554
|
+
h('div', { className: 'pr-runbar-sec-title' },
|
|
1555
|
+
h('span', { className: 'pr-runbar-sec-dot', title: '有 agent 正在运行' }),
|
|
1556
|
+
h('span', { className: 'pr-runbar-sec-label' }, '运行中'),
|
|
1557
|
+
h('span', { className: 'pr-runbar-sec-count' }, String(runningGroups.length)),
|
|
1558
|
+
),
|
|
1559
|
+
h('div', { className: 'pr-runbar-sec-body' }, runningGroups.map(renderRunPill)),
|
|
1560
|
+
)
|
|
1441
1561
|
: null,
|
|
1442
|
-
runGroups.map(renderRunPill),
|
|
1443
1562
|
),
|
|
1444
1563
|
h('div', { className: 'pr-head' },
|
|
1445
1564
|
h('span', { className: 'pr-head-title' }, 'Projects'),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-project-room",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Project rooms for DeepSeek Harness: group sessions into projects, see per-project runtime, and open a room with a shared board, task list, todo queue and message delivery to member sessions.",
|
|
6
6
|
"main": "index.js",
|