dsh-project-room 0.2.7 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/client.js +89 -24
  2. package/package.json +1 -1
package/client.js CHANGED
@@ -356,7 +356,7 @@ window.__ModuleLoader__.load({
356
356
  .pr-menu-item:hover { background: var(--dsw-alias-bg-layer-1); }
357
357
  .pr-menu-msg { padding: 4px 8px; font-size: calc(10.5px + var(--dsh-content-font-delta, 0px)); color: var(--dsw-alias-label-secondary); }
358
358
  .pr-modal { position: fixed; inset: 0; z-index: 1001; display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,.45); }
359
- .pr-modal-box { width: min(420px, 88vw); padding: 14px; border-radius: 10px; background: var(--dsw-alias-bg-base); border: 1px solid var(--dsw-alias-border-l2); }
359
+ .pr-modal-box { width: min(420px, 88vw); padding: 14px; border-radius: 10px; background-color: #141a1b; background-image: linear-gradient(var(--dsw-alias-bg-base), var(--dsw-alias-bg-base)); border: 1px solid var(--dsw-alias-border-l2); box-shadow: 0 18px 48px rgba(0,0,0,.5); }
360
360
  .pr-modal-title { font-size: calc(13px + var(--dsh-content-font-delta, 0px)); font-weight: 600; margin-bottom: 8px; }
361
361
  .pr-modal-input { width: 100%; box-sizing: border-box; height: 30px; padding: 0 8px; border-radius: 6px; border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-bg-layer-1); color: var(--dsw-alias-label-primary); font-size: calc(12.5px + var(--dsh-content-font-delta, 0px)); outline: none; }
362
362
  .pr-modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }
@@ -924,7 +924,11 @@ function summarizeMessage(raw) {
924
924
  const groups = React.useMemo(() => {
925
925
  const map = new Map();
926
926
  for (const [id, session] of Object.entries(byId)) {
927
- if (!session || session.blank) continue;
927
+ if (!session) continue;
928
+ // 2026-11-12:blank 会话原本一律不显示。但「+ 新项目」刚建出来的会话在首轮对话前
929
+ // 就是 blank,标题已是「项目 · 成员」——一并过滤会让新项目在侧栏彻底看不见
930
+ // (用户报「新项目建不了,没反应」)。所以 blank 只过滤**还没被命名**的空壳。
931
+ if (session.blank && !projectOfTitle(session.displayTitle || session.title)) continue;
928
932
  if (archived.has(String(id))) continue;
929
933
  // 2026-11-10(用户令):白名单改「事后隐藏」——只跳过**归档失败过**的会话,
930
934
  // 不再用磁盘白名单全量过滤(那样会让新建会话因轮询延迟而整段生命期不可见)。
@@ -1384,27 +1388,67 @@ function summarizeMessage(raw) {
1384
1388
  /** 设置:项目管理者名称(默认 Yat2,存 localStorage) */
1385
1389
  const renderSettings = () => {
1386
1390
  if (!settings) return null;
1387
- const save = () => {
1391
+ /**
1392
+ * 改名(2026-11-12 用户令「设置里面重命名之后所有管理者应该都重命名呀」):
1393
+ * 以前只写 localStorage,已有会话标题仍带旧名 → 排序/房间投递按新名匹配不到,
1394
+ * 管理者看着像「消失了」。现在改名后把所有 `<项目> · <旧名>` 的会话一并改成新名。
1395
+ */
1396
+ const save = async () => {
1388
1397
  const name = String(settings.manager || '').trim();
1389
1398
  if (!name) { setSettings({ ...settings, msg: '名称不能为空' }); return; }
1399
+ const oldName = managerName();
1390
1400
  try { window.localStorage.setItem('projectRoom.managerName', name); } catch { /* 存不了就本次生效 */ }
1391
- setSettings(null);
1401
+ if (name === oldName) { setSettings(null); return; }
1402
+
1403
+ // 找出所有「某项目 · 旧管理者名」的会话,逐个改标题
1404
+ const targets = [];
1405
+ for (const [id, session] of Object.entries(byId)) {
1406
+ if (!session) continue;
1407
+ const title = String(session.displayTitle || session.title || '');
1408
+ const parsed = projectOfTitle(title);
1409
+ if (!parsed) continue;
1410
+ if (parsed.member !== oldName) continue;
1411
+ targets.push({ id, project: parsed.project });
1412
+ }
1413
+ if (!targets.length) { setSettings(null); return; }
1414
+
1415
+ setSettings({ ...settings, busy: true, msg: `正在把 ${targets.length} 个管理者会话改名为「${name}」…` });
1416
+ let ok = 0;
1417
+ const failed = [];
1418
+ for (const t of targets) {
1419
+ try {
1420
+ const res = await fetch(`${API}/rename`, {
1421
+ method: 'POST', headers: { 'content-type': 'application/json' },
1422
+ body: JSON.stringify({ sessionId: t.id, title: `${t.project}${SEP}${name}` }),
1423
+ });
1424
+ const data = await res.json();
1425
+ // 坑:/project-room/rename 失败也返回 HTTP 200 + {ok:false},只看 res.ok 会漏判
1426
+ if (data && data.ok) ok += 1; else failed.push(`${t.project}(${(data && data.error) || '未知'})`);
1427
+ } catch (error) {
1428
+ failed.push(`${t.project}(${String((error && error.message) || error)})`);
1429
+ }
1430
+ }
1431
+ if (!failed.length) { setSettings(null); return; }
1432
+ setSettings({
1433
+ ...settings, busy: false,
1434
+ msg: `已改名 ${ok} 个;失败 ${failed.length} 个:${failed.slice(0, 3).join('、')}${failed.length > 3 ? '…' : ''}`,
1435
+ });
1392
1436
  };
1393
1437
  return h('div', { className: 'pr-modal', onClick: () => setSettings(null) },
1394
1438
  h('div', { className: 'pr-modal-box', onClick: (event) => event.stopPropagation() },
1395
1439
  h('div', { className: 'pr-modal-title' }, '设置'),
1396
- h('div', { className: 'pr-hint' }, '项目管理者名称(排序时永远排第一、新建项目的默认成员名)'),
1440
+ h('div', { className: 'pr-hint' }, '管理者名(每个项目排第一、新建项目的默认成员名;改名会把所有管理者会话一并改名)'),
1397
1441
  h('input', {
1398
1442
  className: 'pr-modal-input',
1399
1443
  value: settings.manager || '',
1400
1444
  autoFocus: true,
1401
1445
  placeholder: DEFAULT_MANAGER,
1446
+ disabled: Boolean(settings.busy),
1402
1447
  onChange: (event) => setSettings({ ...settings, manager: event.target.value, msg: '' }),
1403
1448
  onKeyDown: (event) => { if (event.key === 'Enter') save(); if (event.key === 'Escape') setSettings(null); },
1404
1449
  }),
1405
1450
  h('div', { className: 'pr-hint', style: { marginTop: '6px' } },
1406
- '注意:这只影响本插件的排序与默认名。若要让「管理者协议注入」也认这个名字,'
1407
- + '还需要改 hub-desktop-ui 里的 MANAGER_NAMES(那是另一个插件的硬编码)。'),
1451
+ '提示:改名只影响侧栏的排序、新建项目默认名与会话标题,DSH 里既有的会话标题会同步改。'),
1408
1452
  settings.msg ? h('div', { className: 'pr-err' }, settings.msg) : null,
1409
1453
  // 2026-11-10(用户令「事后隐藏」):归档失败过的会话被记进 localStorage 隐藏集,
1410
1454
  // 这里提供恢复入口,免得误隐藏之后找不回来。
@@ -1452,7 +1496,8 @@ function summarizeMessage(raw) {
1452
1496
  const submit = async () => {
1453
1497
  const project = String(newProject.name || '').trim();
1454
1498
  const member = String(newProject.member || managerName()).trim() || managerName();
1455
- if (!project) return;
1499
+ // 2026-11-12:空项目名原来直接 return,点「创建」毫无反馈(用户报「没反应」)。
1500
+ if (!project) { setNewProject({ ...newProject, msg: '请先填项目名' }); return; }
1456
1501
  setNewProject({ ...newProject, busy: true, msg: '创建中…' });
1457
1502
  try {
1458
1503
  const svc = clientCtx && typeof clientCtx.get === 'function' ? clientCtx.get('uiWorkspace') : null;
@@ -1464,17 +1509,37 @@ function summarizeMessage(raw) {
1464
1509
  const raw = await svc.connectWorkspace(workspaceId, { agentPreset: 'cordis' });
1465
1510
  sessionId = typeof raw === 'string' ? raw : ((raw && (raw.sessionId || raw.id)) || '');
1466
1511
  }
1467
- if (!sessionId && typeof svc.startSession === 'function') {
1468
- svc.startSession(workspaceId, { agentPreset: 'cordis' });
1512
+ // 2026-11-12:拿不到 id 时改走 `sessions.create`。不能只调 startSession——
1513
+ // 它返回 void,之后 sessionId 仍为空就不改名,弹窗静默关闭、列表毫无变化。
1514
+ if (!sessionId) {
1515
+ const sessionsSvc = clientCtx && typeof clientCtx.get === 'function' ? clientCtx.get('sessions') : null;
1516
+ if (sessionsSvc && typeof sessionsSvc.create === 'function') {
1517
+ sessionId = await sessionsSvc.create(
1518
+ workspaceId !== undefined ? { workspaceId, agentPreset: 'cordis' } : { agentPreset: 'cordis' },
1519
+ );
1520
+ }
1521
+ }
1522
+ if (!sessionId) {
1523
+ setNewProject({ ...newProject, busy: false, msg: '创建失败:没拿到新会话 id(先切一个工作区再试)' });
1524
+ return;
1469
1525
  }
1470
- if (sessionId) {
1471
- await fetch(`${API}/rename`, {
1472
- method: 'POST', headers: { 'content-type': 'application/json' },
1473
- body: JSON.stringify({ sessionId, title: `${project} · ${member}` }),
1474
- });
1526
+ const renameRes = await fetch(`${API}/rename`, {
1527
+ method: 'POST', headers: { 'content-type': 'application/json' },
1528
+ body: JSON.stringify({ sessionId, title: `${project} · ${member}` }),
1529
+ });
1530
+ // 宿主这条路由失败时也是 HTTP 200 + {ok:false},只看 res.ok 会漏判。
1531
+ let renameData = null;
1532
+ try { renameData = await renameRes.json(); } catch { renameData = null; }
1533
+ if (!renameRes.ok || !renameData || renameData.ok === false) {
1534
+ const why = (renameData && renameData.error) ? `:${renameData.error}` : '';
1535
+ setNewProject({ ...newProject, busy: false, msg: `会话已建,但改名失败(HTTP ${renameRes.status}${why})` });
1536
+ return;
1475
1537
  }
1476
1538
  // 初始化房间看板(不存在则建模板)
1477
1539
  try { await fetch(`${API}/board?project=${encodeURIComponent(project)}`, { method: 'POST' }); } catch { /* 看板可选 */ }
1540
+ // 2026-11-12:建完立刻打开该会话。此前只建不打开,而侧栏又不显示 blank 会话,
1541
+ // 用户点「创建」后界面上什么都没发生——这就是「新项目建不了,没反应」。
1542
+ openSessionById(sessionId);
1478
1543
  setNewProject(null);
1479
1544
  } catch (error) {
1480
1545
  setNewProject({ ...newProject, busy: false, msg: `失败:${String((error && error.message) || error)}` });
@@ -1492,15 +1557,15 @@ function summarizeMessage(raw) {
1492
1557
  onKeyDown: (event) => { if (event.key === 'Enter') submit(); if (event.key === 'Escape') setNewProject(null); },
1493
1558
  }),
1494
1559
  h('div', { style: { height: '8px' } }),
1495
- h('input', {
1496
- className: 'pr-modal-input',
1497
- placeholder: `成员的 agent 名(默认 ${managerName()})`,
1498
- value: newProject.member || '',
1499
- onChange: (event) => setNewProject({ ...newProject, member: event.target.value, msg: '' }),
1500
- onKeyDown: (event) => { if (event.key === 'Enter') submit(); if (event.key === 'Escape') setNewProject(null); },
1501
- }),
1502
- h('div', { style: { marginTop: '6px', fontSize: '10.5px', color: 'var(--dsw-alias-label-secondary)' } },
1503
- `创建后会话标题为「${String(newProject.name || '项目名').trim()} · ${String(newProject.member || 'Yat2').trim() || 'Yat2'}」`),
1560
+ // 2026-11-12 用户令:新建项目不再让用户填管理者名称,只留项目名;
1561
+ // 管理者名统一取 ⚙ 设置里的值(submit 里 member 为空即回落 managerName())。
1562
+ h('div', {
1563
+ style: {
1564
+ fontSize: 'calc(10.5px + var(--dsh-content-font-delta, 0px))',
1565
+ color: 'var(--dsw-alias-label-secondary)',
1566
+ },
1567
+ },
1568
+ `创建后会话标题为「${String(newProject.name || '项目名').trim()} · ${String(newProject.member || managerName()).trim() || managerName()}」,管理者名可在 ⚙ 设置里改`),
1504
1569
  newProject.msg ? h('div', { className: 'pr-hint' }, newProject.msg) : null,
1505
1570
  h('div', { className: 'pr-modal-actions' },
1506
1571
  h('button', { className: 'pr-btn', onClick: () => setNewProject(null), disabled: newProject.busy }, '取消'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-project-room",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
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",