dsh-sessions-manager 3.6.2 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -152,6 +152,167 @@ function toastDurationFor(text, kind) {
152
152
  const scaled = kind === "err" ? Math.round(readingMs * 1.25) : readingMs;
153
153
  return Math.min(TOAST_MAX_MS, Math.max(TOAST_MIN_MS, scaled));
154
154
  }
155
+ function pathTail(p) {
156
+ const parts = String(p == null ? "" : p).split(/[\\/]+/).filter(Boolean);
157
+ return parts.length ? parts[parts.length - 1] : "";
158
+ }
159
+ function moveNoticeText(n) {
160
+ const id = shortId(n && n.sessionId);
161
+ if (n && n.kind === "moved") {
162
+ const where = pathTail(n.targetPath);
163
+ return `\u6392\u961F\u4E2D\u7684\u79FB\u52A8\u5DF2\u5B8C\u6210\uFF1A${id}${where ? ` \u2192 \u300C${where}\u300D` : ""}`;
164
+ }
165
+ const reason = String(n && n.reason || "").split("\n")[0].slice(0, 120);
166
+ return `\u6392\u961F\u4E2D\u7684\u79FB\u52A8\u591A\u6B21\u5931\u8D25\u5DF2\u653E\u5F03\uFF1A${id}${reason ? `\uFF08${reason}\uFF09` : ""}\uFF0C\u53EF\u5728 \u8BBE\u7F6E \u2192 \u4F1A\u8BDD\u7BA1\u7406 \u2192 \u5F85\u79FB\u52A8\u961F\u5217 \u91CD\u65B0\u53D1\u8D77\u79FB\u52A8`;
167
+ }
168
+ function noticeToastPlan(raw, seenIds, max = 2) {
169
+ const list = (Array.isArray(raw) ? raw : []).filter((n) => n && typeof n.id === "string" && typeof n.sessionId === "string" && (n.kind === "moved" || n.kind === "abandoned") && !seenIds.has(String(n.id))).sort((a, b) => (a.at || 0) - (b.at || 0));
170
+ if (!list.length) return { text: null, kind: "ok", ackIds: [] };
171
+ const shown = list.slice(Math.max(0, list.length - Math.max(1, max)));
172
+ const parts = shown.map(moveNoticeText);
173
+ if (list.length > shown.length) parts.push(`\u53E6\u6709 ${list.length - shown.length} \u6761\u6392\u961F\u79FB\u52A8\u7684\u7ED3\u679C\uFF0C\u89C1 \u8BBE\u7F6E \u2192 \u4F1A\u8BDD\u7BA1\u7406 \u2192 \u5F85\u79FB\u52A8\u961F\u5217`);
174
+ return {
175
+ text: parts.join("\uFF1B"),
176
+ kind: shown.some((n) => n.kind === "abandoned") ? "err" : "ok",
177
+ ackIds: shown.map((n) => String(n.id))
178
+ };
179
+ }
180
+ function branchTimeKey(item) {
181
+ const c = Number(item && item.createdAt);
182
+ if (Number.isFinite(c) && c > 0) return c;
183
+ const u = Number(item && item.updatedAt);
184
+ if (Number.isFinite(u) && u > 0) return u;
185
+ return 0;
186
+ }
187
+ function ascBranchTime(a, b) {
188
+ const ta = String((a && a.sessionId) ?? "");
189
+ const tb = String((b && b.sessionId) ?? "");
190
+ return branchTimeKey(a) - branchTimeKey(b) || ta.localeCompare(tb);
191
+ }
192
+ function branchParentOf(id, table) {
193
+ const info = table[id];
194
+ if (!info || typeof info !== "object") return null;
195
+ if (info.origin === "subagent") return null;
196
+ const p = info.parentSession ? String(info.parentSession) : null;
197
+ if (!p || p === id) return null;
198
+ return p;
199
+ }
200
+ function foldBranches(items, lineage) {
201
+ const list = Array.isArray(items) ? items : [];
202
+ const table = lineage && typeof lineage === "object" ? lineage : {};
203
+ const byId = /* @__PURE__ */ new Map();
204
+ for (const it of list) {
205
+ if (it == null || it.sessionId == null) continue;
206
+ byId.set(String(it.sessionId), it);
207
+ }
208
+ const maxWalk = Object.keys(table).length + list.length + 2;
209
+ const anchorMemo = /* @__PURE__ */ new Map();
210
+ function resolveAnchor(id) {
211
+ if (anchorMemo.has(id)) return anchorMemo.get(id);
212
+ const seen = /* @__PURE__ */ new Set([id]);
213
+ let cur = id;
214
+ let steps = 0;
215
+ let anchor = null;
216
+ while (true) {
217
+ const p = branchParentOf(cur, table);
218
+ if (!p) {
219
+ anchor = steps === 0 ? null : cur;
220
+ break;
221
+ }
222
+ if (seen.has(p)) break;
223
+ seen.add(p);
224
+ steps++;
225
+ if (byId.has(p)) {
226
+ if (!branchParentOf(p, table)) {
227
+ anchor = p;
228
+ break;
229
+ }
230
+ cur = p;
231
+ if (steps > maxWalk) break;
232
+ continue;
233
+ }
234
+ if (!branchParentOf(p, table)) {
235
+ anchor = p;
236
+ break;
237
+ }
238
+ cur = p;
239
+ if (steps > maxWalk) break;
240
+ }
241
+ anchorMemo.set(id, anchor);
242
+ return anchor;
243
+ }
244
+ const groupsOf = /* @__PURE__ */ new Map();
245
+ const anchorOfRow = /* @__PURE__ */ new Map();
246
+ let foldedCount = 0;
247
+ for (const it of list) {
248
+ if (it == null || it.sessionId == null) continue;
249
+ const id = String(it.sessionId);
250
+ if (!branchParentOf(id, table)) continue;
251
+ const anchor = resolveAnchor(id);
252
+ if (!anchor || anchor === id) continue;
253
+ if (!groupsOf.has(anchor)) groupsOf.set(anchor, []);
254
+ groupsOf.get(anchor).push(it);
255
+ anchorOfRow.set(it, anchor);
256
+ foldedCount++;
257
+ }
258
+ const earliest = /* @__PURE__ */ new Set();
259
+ for (const [anchor, members] of groupsOf) {
260
+ members.sort(ascBranchTime);
261
+ if (!byId.has(anchor)) earliest.add(members[0]);
262
+ }
263
+ const topList = [];
264
+ const emitted = /* @__PURE__ */ new Set();
265
+ for (const it of list) {
266
+ const anchor = it == null ? void 0 : anchorOfRow.get(it);
267
+ if (anchor === void 0) {
268
+ topList.push(it);
269
+ continue;
270
+ }
271
+ if (!byId.has(anchor) && !emitted.has(anchor) && earliest.has(it)) {
272
+ emitted.add(anchor);
273
+ topList.push({ syntheticRoot: anchor, sessionId: "dsm-src:" + anchor });
274
+ }
275
+ }
276
+ for (const [anchor, members] of groupsOf) {
277
+ if (!members.length) groupsOf.delete(anchor);
278
+ }
279
+ return { topList, branchGroupsOf: groupsOf, foldedCount, groupCount: groupsOf.size };
280
+ }
281
+ function applyTagFilter(list, tagId) {
282
+ const items = Array.isArray(list) ? list : [];
283
+ if (!tagId) return items;
284
+ const want = String(tagId);
285
+ return items.filter((item) => {
286
+ const tags = item && item.tags;
287
+ return Array.isArray(tags) && tags.some((t) => String(t) === want);
288
+ });
289
+ }
290
+ function tagDeleteConfirm(name) {
291
+ return `\u5220\u9664\u6807\u7B7E\u300C${String(name == null ? "" : name) || "(\u672A\u547D\u540D)"}\u300D\uFF1F\u53EA\u5220\u6807\u7B7E\uFF0C\u4E0D\u4F1A\u5220\u9664\u4F1A\u8BDD\uFF1B\u6253\u8FC7\u8FD9\u4E2A\u6807\u7B7E\u7684\u4F1A\u8BDD\u53EA\u662F\u5931\u53BB\u5B83\u3002`;
292
+ }
293
+ var SAVED_VIEWS = ["all", "active", "archived", "starred", "empty", "trash"];
294
+ var SAVED_SORTS = ["newest", "oldest", "title"];
295
+ function filterSnapshotOf({ filter = "all", workspaceFilter = "all", sortBy = "newest", tagFilter = "" } = {}) {
296
+ return {
297
+ view: SAVED_VIEWS.includes(filter) ? filter : "all",
298
+ workspace: typeof workspaceFilter === "string" && workspaceFilter ? workspaceFilter : "all",
299
+ sort: SAVED_SORTS.includes(sortBy) ? sortBy : "newest",
300
+ tag: typeof tagFilter === "string" ? tagFilter : ""
301
+ };
302
+ }
303
+ function filterShapeFromSaved(item) {
304
+ const f = item && item.filters && typeof item.filters === "object" && !Array.isArray(item.filters) ? item.filters : {};
305
+ const degraded = [];
306
+ let view = typeof f.view === "string" && SAVED_VIEWS.includes(f.view) ? f.view : "all";
307
+ if (view === "all" && f.view !== "all") degraded.push("view");
308
+ let workspace = typeof f.workspace === "string" && f.workspace ? f.workspace : "all";
309
+ if (workspace === "all" && f.workspace !== "all") degraded.push("workspace");
310
+ let sort = typeof f.sort === "string" && SAVED_SORTS.includes(f.sort) ? f.sort : "newest";
311
+ if (sort === "newest" && f.sort !== "newest") degraded.push("sort");
312
+ let tag = typeof f.tag === "string" ? f.tag : "";
313
+ if (tag === "" && f.tag !== "") degraded.push("tag");
314
+ return { view, workspace, sort, tag, degraded };
315
+ }
155
316
 
156
317
  // src/client/index.jsx
157
318
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -193,10 +354,14 @@ var CSS = `
193
354
  .sess-fbtn-on{background:var(--dsw-alias-interactive-bg-active);color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l3)}
194
355
  .sess-tools{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:8px;margin:0 0 8px}
195
356
  .sess-tools-4{grid-template-columns:repeat(auto-fit,minmax(132px,1fr))}
357
+ /* T4 \u8D77\u641C\u7D22\u884C\u6709 5 \u4E2A\u5B57\u6BB5\uFF08\u641C\u7D22/\u5DE5\u4F5C\u533A/\u6807\u7B7E/\u6392\u5E8F/\u5206\u7EC4\uFF09\uFF1A\u6536\u7A84\u4E0B\u9650\u4FDD\u8BC1 800px \u5185\u4E00\u884C\u6392\u6EE1\u3002 */
358
+ .sess-tools-5{grid-template-columns:repeat(auto-fit,minmax(124px,1fr))}
196
359
  .dsm-kids{display:flex;flex-direction:column;gap:6px;margin:10px 0 2px;margin-left:43px;padding-left:10px;border-left:2px solid var(--dsw-alias-border-l3)}
197
360
  .dsm-kid .dsm-kids{margin-left:8px;margin-top:6px}
198
361
  .dsm-kids-toggle{appearance:none;min-height:22px;padding:0 9px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-business-primary) 40%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 10%,transparent);color:var(--dsw-alias-state-business-primary);border-radius:var(--dsm-radius-tag);font:inherit;font-size:11px;font-weight:500;cursor:pointer;flex:none;white-space:nowrap}
199
362
  .dsm-kids-toggle:hover{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 18%,transparent)}
363
+ .dsm-src-head{display:flex;align-items:center;gap:8px;padding:5px 9px;border:1px dashed var(--dsw-alias-border-l3);border-radius:var(--dsm-radius-ctl);font-size:12px;color:var(--dsw-alias-label-secondary)}
364
+ .dsm-src-head .dsm-src-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
200
365
  .dsm-kid{display:flex;flex-direction:column;gap:6px;min-width:0}
201
366
  .dsm-kid-row{display:flex;align-items:center;gap:8px;min-width:0}
202
367
  .dsm-kid-name{flex:1 1 auto;min-width:0;font-size:12px;color:var(--dsw-alias-label-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@@ -206,7 +371,9 @@ var CSS = `
206
371
  .sess-field{display:flex;flex-direction:column;gap:5px;min-width:0}
207
372
  .sess-field label{font-size:11px;font-weight:600;color:var(--dsw-alias-label-secondary)}
208
373
  .sess-field input,.sess-field select{box-sizing:border-box;width:100%;min-height:36px;padding:0 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsm-radius-ctl);background:var(--dsw-alias-fill-elevated);color:var(--dsw-alias-label-primary);font:inherit;font-size:12px}
209
- .sess-results{display:flex;flex-direction:column;gap:1px;font-size:11px;color:var(--dsw-alias-label-tertiary);margin:0 0 4px}
374
+ .sess-results{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:11px;color:var(--dsw-alias-label-tertiary);margin:0 0 4px}
375
+ /* T4 \u540E\u8BA1\u6570\u884C\u53F3\u4FA7\u6302\u4E86\u300C\u4FDD\u5B58\u7B5B\u9009\u300D\u63A7\u4EF6\u7EC4\uFF0C\u6B63\u6587\u72EC\u5360\u5DE6\u4FA7\u53EF\u6536\u7F29\u3002 */
376
+ .sess-results-main{flex:1 1 auto;min-width:0}
210
377
  .archv button:focus-visible,.archv input:focus-visible,.archv select:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}
211
378
  .sess-batch{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:8px 2px 4px;margin-bottom:4px}
212
379
  /* issue #7\uFF1A\u52FE\u9009\u540E\u6279\u91CF\u64CD\u4F5C\u680F\u5438\u9876\u2014\u2014\u5217\u8868\u518D\u957F\uFF0C\u64CD\u4F5C\u6309\u94AE\u4E5F\u4E00\u76F4\u5728\u624B\u8FB9\u3002 */
@@ -227,6 +394,10 @@ var CSS = `
227
394
  .archv-date{font-size:11px;color:var(--dsw-alias-label-tertiary);white-space:nowrap;flex:none}
228
395
  .dsm-branch-chip{display:inline-flex;align-items:center;flex:none;min-height:22px;padding:0 9px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-success-primary) 45%,transparent);border-radius:var(--dsm-radius-tag);background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 10%,transparent);color:var(--dsw-alias-state-success-primary);font-size:11px;font-weight:500;line-height:1;white-space:nowrap}
229
396
  .dsm-empty-chip{display:inline-flex;align-items:center;flex:none;min-height:22px;padding:0 9px;border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsm-radius-tag);background:var(--dsw-alias-fill-subtle);color:var(--dsw-alias-label-tertiary);font-size:11px;font-weight:500;line-height:1;white-space:nowrap}
397
+ /* 3.7.0 T4 \u6807\u7B7E chip \u7CFB\uFF1A\u5361\u7247\u6807\u9898\u884C\u5185\u3001\u5206\u652F/\u7A7A\u767D chip \u4E4B\u540E\u3002\u8B66\u793A\u8272\u7CFB\u533A\u522B\u4E8E
398
+ \u5206\u652F\uFF08\u7EFF\uFF09/\u7A7A\u767D\uFF08\u7070\uFF09\uFF0C\u540D\u5B57\u8D85\u957F\u7701\u7565\u53F7\uFF1B+N \u8BA1\u6570 chip \u4E2D\u6027\u8272\u3002 */
399
+ .dsm-tagchip{display:inline-flex;align-items:center;flex:none;max-width:9em;min-height:22px;padding:0 8px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-warn-primary,#EAB308) 45%,transparent);border-radius:var(--dsm-radius-tag);background:color-mix(in srgb,var(--dsw-alias-state-warn-primary,#EAB308) 10%,transparent);color:var(--dsw-alias-label-secondary);font-size:11px;font-weight:500;line-height:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
400
+ .dsm-tagchip-more{max-width:none;color:var(--dsw-alias-label-tertiary);border-color:var(--dsw-alias-border-l2);background:var(--dsw-alias-fill-subtle)}
230
401
  .archv-id{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10.5px;color:var(--dsw-alias-label-tertiary);flex:none;margin-left:auto;white-space:nowrap}
231
402
  .archv-dot{color:var(--dsw-alias-border-l3);flex:none}
232
403
  .archv-check{width:15px;height:15px;accent-color:var(--dsw-alias-state-business-primary);flex:none;cursor:pointer}
@@ -258,7 +429,7 @@ var CSS = `
258
429
  /* \u5931\u8D25\u63D0\u793A\u7528\u8B66\u793A\u8272 + \u52A0\u7C97\uFF0C\u5E76\u505C\u7559\u66F4\u4E45\uFF08\u9700\u8981\u7528\u6237\u8BFB\u5B8C\u53BB\u505A\u4E0B\u4E00\u6B65\u64CD\u4F5C\uFF09\u3002 */
259
430
  .archv-status-err{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary);font-weight:500}
260
431
  @media (prefers-reduced-motion:reduce){.archv-skel-card::after{animation:none}.archv-card,.archv-btn,.archv-star{transition:none}.archv-star:hover,.archv-star:active{transform:none}.archv-status,.archv-spin{animation:none}}
261
- @media (max-width:640px){.archv-card{flex-direction:column;align-items:stretch;gap:10px}.archv-actions{justify-content:flex-end}.sess-tools,.sess-tools-4{grid-template-columns:1fr}.sess-fbtn,.archv-btn{min-height:40px}.archv-star{width:30px;height:30px}.archv-star svg{width:22px;height:22px}.dsm-kids{margin-left:10px}.archv-titlerow{flex-wrap:wrap}.dsm-kid-row{flex-wrap:wrap}.dsm-kid-acts{margin-left:0}}
432
+ @media (max-width:640px){.archv-card{flex-direction:column;align-items:stretch;gap:10px}.archv-actions{justify-content:flex-end}.sess-tools,.sess-tools-4,.sess-tools-5{grid-template-columns:1fr}.sess-fbtn,.archv-btn{min-height:40px}.archv-star{width:30px;height:30px}.archv-star svg{width:22px;height:22px}.dsm-kids{margin-left:10px}.archv-titlerow{flex-wrap:wrap}.dsm-kid-row{flex-wrap:wrap}.dsm-kid-acts{margin-left:0}}
262
433
  .mv-sheet{width:100%;box-sizing:border-box;display:flex;flex-direction:column;gap:12px;margin-top:12px;padding:14px;border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsm-radius-sheet);background:var(--dsw-alias-fill-subtle)}
263
434
  .mv-sheet-head{display:flex;align-items:center;justify-content:space-between;gap:10px}
264
435
  .mv-sheet-title{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);margin:0}
@@ -307,6 +478,25 @@ var CSS = `
307
478
  .dlg-title{font-size:15px;font-weight:650;color:var(--dsw-alias-label-primary);margin:0}
308
479
  .dlg-text{font-size:13px;line-height:1.6;color:var(--dsw-alias-label-secondary);margin:0;word-break:break-all}
309
480
  .dlg-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:2px}
481
+ /* ---- T4 \u6807\u7B7E\u7F16\u8F91/\u7BA1\u7406 sheet \u4E0E\u7B5B\u9009\u4FDD\u5B58\u63A7\u4EF6\uFF08\u5168\u90E8\u8D70 --dsw token\uFF09 ---- */
482
+ .dsm-taglist{display:flex;flex-direction:column;gap:2px;max-height:240px;overflow:auto}
483
+ .dsm-tagcheck{display:flex;align-items:center;gap:8px;min-width:0;padding:5px 2px;font-size:12px;color:var(--dsw-alias-label-secondary);cursor:pointer;border-radius:7px}
484
+ .dsm-tagcheck:hover{background:var(--dsw-alias-interactive-bg-hover)}
485
+ .dsm-tagcheck input{width:15px;height:15px;accent-color:var(--dsw-alias-state-business-primary);cursor:pointer;flex:none}
486
+ .dsm-tagcheck-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
487
+ .dsm-tagrow{display:flex;align-items:center;gap:8px;min-width:0;padding:6px 0;border-bottom:1px solid var(--dsw-alias-border-l2)}
488
+ .dsm-tagrow:last-child{border-bottom:none}
489
+ .dsm-tag-name{flex:0 1 auto;min-width:0;font-size:12.5px;color:var(--dsw-alias-label-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
490
+ .dsm-tag-input{box-sizing:border-box;flex:1 1 auto;min-width:0;min-height:28px;padding:0 8px;border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsm-radius-ctl);background:var(--dsw-alias-fill-elevated);color:var(--dsw-alias-label-primary);font-size:12px;font-family:inherit}
491
+ .dsm-tag-input:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px}
492
+ .dsm-tag-acts{display:flex;align-items:center;gap:6px;flex:none;margin-left:auto}
493
+ .dsm-tag-acts .archv-btn{min-height:26px;padding:0 9px;font-size:11px}
494
+ .dsm-tag-acts select{appearance:none;min-height:26px;padding:0 6px;border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsm-radius-ctl);background:var(--dsw-alias-fill-elevated);color:var(--dsw-alias-label-secondary);font-size:11px;font-family:inherit;max-width:10em}
495
+ .dsm-fbar{display:flex;align-items:center;gap:6px;flex:none;margin-left:auto;flex-wrap:wrap}
496
+ .dsm-fbar .archv-btn{min-height:24px;padding:0 8px;font-size:11px}
497
+ .dsm-fbar select{appearance:none;min-height:24px;padding:0 6px;border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsm-radius-ctl);background:var(--dsw-alias-fill-elevated);color:var(--dsw-alias-label-secondary);font-size:11px;font-family:inherit;max-width:12em}
498
+ .dsm-fbar input{box-sizing:border-box;min-width:9em;min-height:24px;padding:0 8px;border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsm-radius-ctl);background:var(--dsw-alias-fill-elevated);color:var(--dsw-alias-label-primary);font-size:11px;font-family:inherit}
499
+ @media (max-width:640px){.dsm-fbar{margin-left:0;width:100%;justify-content:flex-end}.dsm-tag-acts{margin-left:auto;flex-wrap:wrap;justify-content:flex-end}}
310
500
  `;
311
501
  function fmtDate(iso) {
312
502
  if (!iso) return null;
@@ -463,6 +653,16 @@ function SessionPanel({ workspacesSvc }) {
463
653
  const [toast, setToast] = (0, import_react.useState)(null);
464
654
  const [picking, setPicking] = (0, import_react.useState)(false);
465
655
  const [trash, setTrash] = (0, import_react.useState)([]);
656
+ const [pendingQueue, setPendingQueue] = (0, import_react.useState)([]);
657
+ const cancelQueuedMove = async (sid) => {
658
+ try {
659
+ await postJSON("/archived-sessions/pending-moves/cancel", { sessionIds: [sid] });
660
+ showToast("\u5DF2\u53D6\u6D88\u6392\u961F\uFF0C\u4F1A\u8BDD\u7559\u5728\u539F\u5DE5\u4F5C\u533A", "ok");
661
+ setPendingQueue((q) => q.filter((x) => String(x.sessionId) !== String(sid)));
662
+ } catch (e) {
663
+ showToast("\u53D6\u6D88\u5931\u8D25\uFF1A" + String(e && e.message || e), "err");
664
+ }
665
+ };
466
666
  const [trashBusy, setTrashBusy] = (0, import_react.useState)(null);
467
667
  const [trashSettings, setTrashSettings] = (0, import_react.useState)({ retentionDays: 0 });
468
668
  const [trashCheck, setTrashCheck] = (0, import_react.useState)(null);
@@ -488,7 +688,297 @@ function SessionPanel({ workspacesSvc }) {
488
688
  const dialogRef = (0, import_react.useRef)(null);
489
689
  const [lineage, setLineage] = (0, import_react.useState)({});
490
690
  const [openKids, setOpenKids] = (0, import_react.useState)({});
691
+ const [openBranches, setOpenBranches] = (0, import_react.useState)(() => {
692
+ try {
693
+ const v = JSON.parse(localStorage.getItem("dsm-branch-open-v1") || "[]");
694
+ const o = {};
695
+ for (const k of Array.isArray(v) ? v : []) if (typeof k === "string") o[k] = true;
696
+ return o;
697
+ } catch (e) {
698
+ return {};
699
+ }
700
+ });
701
+ const toggleBranch = (rootId) => setOpenBranches((prev) => {
702
+ const next = Object.assign({}, prev);
703
+ if (next[rootId]) delete next[rootId];
704
+ else next[rootId] = true;
705
+ try {
706
+ localStorage.setItem("dsm-branch-open-v1", JSON.stringify(Object.keys(next)));
707
+ } catch (e) {
708
+ }
709
+ return next;
710
+ });
491
711
  const [groupByLineage, setGroupByLineage] = (0, import_react.useState)(() => initialPrefs.groupByLineage !== false);
712
+ const [tagDefs, setTagDefs] = (0, import_react.useState)([]);
713
+ const [assignments, setAssignments] = (0, import_react.useState)({});
714
+ const [tagsState, setTagsState] = (0, import_react.useState)("loading");
715
+ const tagsReady = tagsState === "ready";
716
+ const [tagFilter, setTagFilter] = (0, import_react.useState)("");
717
+ const [savedFilters, setSavedFilters] = (0, import_react.useState)([]);
718
+ const [openTags, setOpenTags] = (0, import_react.useState)(null);
719
+ const [openTagMgr, setOpenTagMgr] = (0, import_react.useState)(false);
720
+ const [tagBusy, setTagBusy] = (0, import_react.useState)(null);
721
+ const [mgrBusy, setMgrBusy] = (0, import_react.useState)(false);
722
+ const [cardTagName, setCardTagName] = (0, import_react.useState)("");
723
+ const [mgrTagName, setMgrTagName] = (0, import_react.useState)("");
724
+ const [renamingTagId, setRenamingTagId] = (0, import_react.useState)(null);
725
+ const [renameTagValue, setRenameTagValue] = (0, import_react.useState)("");
726
+ const [mergeTarget, setMergeTarget] = (0, import_react.useState)({});
727
+ const [saveFilterOpen, setSaveFilterOpen] = (0, import_react.useState)(false);
728
+ const [saveFilterName, setSaveFilterName] = (0, import_react.useState)("");
729
+ const [appliedSavedId, setAppliedSavedId] = (0, import_react.useState)("");
730
+ const tagFilterRef = (0, import_react.useRef)("");
731
+ tagFilterRef.current = tagFilter;
732
+ const tagMap = (0, import_react.useMemo)(() => new Map(tagDefs.map((t) => [String(t.id), String(t.name)])), [tagDefs]);
733
+ const tagUsage = (0, import_react.useMemo)(() => {
734
+ const m = {};
735
+ for (const ids of Object.values(assignments)) for (const id of Array.isArray(ids) ? ids : []) m[String(id)] = (m[String(id)] || 0) + 1;
736
+ return m;
737
+ }, [assignments]);
738
+ const tagsBlockedTitle = tagsState === "failed" ? "\u6807\u7B7E\u6570\u636E\u672A\u80FD\u52A0\u8F7D\uFF0C\u6682\u65F6\u65E0\u6CD5\u4F7F\u7528\u6807\u7B7E\u529F\u80FD\uFF08\u91CD\u65B0\u6253\u5F00\u8BBE\u7F6E\u9762\u677F\u53EF\u91CD\u8BD5\uFF09" : "\u6807\u7B7E\u6570\u636E\u52A0\u8F7D\u4E2D\uFF0C\u7A0D\u5019\u5373\u53EF\u4F7F\u7528";
739
+ const loadTags = () => postJSON("/archived-sessions/tags/list", {}).then((r) => {
740
+ const defs = r && Array.isArray(r.tags) ? r.tags : [];
741
+ setTagDefs(defs);
742
+ setAssignments(r && r.assignments && typeof r.assignments === "object" && !Array.isArray(r.assignments) ? r.assignments : {});
743
+ setTagsState("ready");
744
+ const cur = tagFilterRef.current;
745
+ if (cur && !defs.some((t) => String(t.id) === cur)) setTagFilter("");
746
+ }).catch(() => {
747
+ setTagDefs([]);
748
+ setAssignments({});
749
+ setTagsState("failed");
750
+ if (tagFilterRef.current) setTagFilter("");
751
+ });
752
+ const loadSavedFilters = () => postJSON("/archived-sessions/filters/list", {}).then((r) => setSavedFilters(r && Array.isArray(r.items) ? r.items : [])).catch(() => setSavedFilters([]));
753
+ const setSessionTags = async (sid, nextIds) => {
754
+ const key = String(sid);
755
+ const prev = Array.isArray(assignments[key]) ? assignments[key].slice() : [];
756
+ if (prev.length === nextIds.length && prev.every((t, i) => String(t) === String(nextIds[i]))) return;
757
+ const writeLocal = (ids) => {
758
+ setAssignments((a) => {
759
+ const n = Object.assign({}, a);
760
+ if (ids.length) n[key] = ids;
761
+ else delete n[key];
762
+ return n;
763
+ });
764
+ setSessions((s) => s && s.map((x) => String(x.sessionId) === key ? Object.assign({}, x, { tags: ids }) : x));
765
+ };
766
+ writeLocal(nextIds);
767
+ setTagBusy(key);
768
+ try {
769
+ const r = await postJSON("/archived-sessions/tags/set", { sessionId: sid, tagIds: nextIds });
770
+ const row = r && r.assignments && Array.isArray(r.assignments[key]) ? r.assignments[key].map(String) : nextIds.map(String);
771
+ writeLocal(row);
772
+ } catch (e) {
773
+ writeLocal(prev);
774
+ showToast("\u6807\u7B7E\u8BBE\u7F6E\u5931\u8D25\uFF1A" + String(e && e.message || e), "err");
775
+ } finally {
776
+ setTagBusy(null);
777
+ }
778
+ };
779
+ const toggleTagFor = (sid, tagId) => {
780
+ if (tagBusy !== null) return;
781
+ const key = String(sid);
782
+ const cur = Array.isArray(assignments[key]) ? assignments[key] : [];
783
+ const has = cur.some((t) => String(t) === String(tagId));
784
+ const next = has ? cur.filter((t) => String(t) !== String(tagId)) : cur.concat(String(tagId));
785
+ setSessionTags(sid, next);
786
+ };
787
+ const createTagAndAttach = async (sid) => {
788
+ const name = cardTagName.trim();
789
+ if (!name || tagBusy !== null) return;
790
+ setTagBusy(String(sid));
791
+ let fresh = null;
792
+ try {
793
+ const r = await postJSON("/archived-sessions/tags/create", { name });
794
+ if (r && r.tag && r.tag.id != null) fresh = r.tag;
795
+ else throw new Error("\u670D\u52A1\u7AEF\u672A\u8FD4\u56DE\u65B0\u6807\u7B7E");
796
+ } catch (e) {
797
+ showToast("\u65B0\u5EFA\u6807\u7B7E\u5931\u8D25\uFF1A" + String(e && e.message || e), "err");
798
+ }
799
+ if (!fresh) {
800
+ setTagBusy(null);
801
+ return;
802
+ }
803
+ setTagDefs((d) => d.some((t) => String(t.id) === String(fresh.id)) ? d : d.concat(fresh));
804
+ const cur = Array.isArray(assignments[String(sid)]) ? assignments[String(sid)].slice() : [];
805
+ setCardTagName("");
806
+ await setSessionTags(sid, cur.concat(String(fresh.id)));
807
+ };
808
+ const createTag = async () => {
809
+ const name = mgrTagName.trim();
810
+ if (!name || mgrBusy) return;
811
+ setMgrBusy(true);
812
+ try {
813
+ const r = await postJSON("/archived-sessions/tags/create", { name });
814
+ if (r && r.tag && r.tag.id != null) {
815
+ const fresh = r.tag;
816
+ setTagDefs((d) => d.some((t) => String(t.id) === String(fresh.id)) ? d : d.concat(fresh));
817
+ setMgrTagName("");
818
+ }
819
+ } catch (e) {
820
+ showToast("\u65B0\u5EFA\u6807\u7B7E\u5931\u8D25\uFF1A" + String(e && e.message || e), "err");
821
+ } finally {
822
+ setMgrBusy(false);
823
+ }
824
+ };
825
+ const commitTagRename = async (tag) => {
826
+ const name = renameTagValue.trim();
827
+ if (!name || mgrBusy) {
828
+ setRenamingTagId(null);
829
+ return;
830
+ }
831
+ if (name === tag.name) {
832
+ setRenamingTagId(null);
833
+ return;
834
+ }
835
+ setMgrBusy(true);
836
+ try {
837
+ await postJSON("/archived-sessions/tags/rename", { id: tag.id, name });
838
+ setTagDefs((d) => d.map((t) => String(t.id) === String(tag.id) ? Object.assign({}, t, { name }) : t));
839
+ setRenamingTagId(null);
840
+ } catch (e) {
841
+ showToast("\u91CD\u547D\u540D\u5931\u8D25\uFF1A" + String(e && e.message || e), "err");
842
+ } finally {
843
+ setMgrBusy(false);
844
+ }
845
+ };
846
+ const deleteTag = async (tag) => {
847
+ if (mgrBusy) return;
848
+ if (!window.confirm(tagDeleteConfirm(tag.name))) return;
849
+ setMgrBusy(true);
850
+ try {
851
+ await postJSON("/archived-sessions/tags/delete", { id: tag.id });
852
+ const gone = String(tag.id);
853
+ setTagDefs((d) => d.filter((t) => String(t.id) !== gone));
854
+ setAssignments((a) => {
855
+ const n = {};
856
+ for (const [sid, ids] of Object.entries(a)) {
857
+ const kept = (Array.isArray(ids) ? ids : []).filter((id) => String(id) !== gone);
858
+ if (kept.length) n[sid] = kept;
859
+ }
860
+ return n;
861
+ });
862
+ setSessions((s) => s && s.map((x) => {
863
+ const curT = Array.isArray(x.tags) ? x.tags : [];
864
+ if (!curT.some((id) => String(id) === gone)) return x;
865
+ return Object.assign({}, x, { tags: curT.filter((id) => String(id) !== gone) });
866
+ }));
867
+ if (tagFilterRef.current === gone) setTagFilter("");
868
+ setMergeTarget((m) => {
869
+ const n = Object.assign({}, m);
870
+ delete n[gone];
871
+ return n;
872
+ });
873
+ } catch (e) {
874
+ showToast("\u5220\u9664\u6807\u7B7E\u5931\u8D25\uFF1A" + String(e && e.message || e), "err");
875
+ } finally {
876
+ setMgrBusy(false);
877
+ }
878
+ };
879
+ const mergeTag = async (from) => {
880
+ const toId = mergeTarget[String(from.id)];
881
+ if (!toId || mgrBusy) return;
882
+ setMgrBusy(true);
883
+ try {
884
+ await postJSON("/archived-sessions/tags/merge", { fromId: from.id, toId });
885
+ const gone = String(from.id);
886
+ setTagDefs((d) => d.filter((t) => String(t.id) !== gone));
887
+ setAssignments((a) => {
888
+ const n = {};
889
+ for (const [sid, ids] of Object.entries(a)) {
890
+ const kept = [];
891
+ const seen = /* @__PURE__ */ new Set();
892
+ for (const id of Array.isArray(ids) ? ids : []) {
893
+ const v = String(id) === gone ? String(toId) : String(id);
894
+ if (seen.has(v)) continue;
895
+ seen.add(v);
896
+ kept.push(v);
897
+ }
898
+ if (kept.length) n[sid] = kept;
899
+ }
900
+ return n;
901
+ });
902
+ if (tagFilterRef.current === gone) setTagFilter(String(toId));
903
+ setMergeTarget((m) => {
904
+ const n = Object.assign({}, m);
905
+ delete n[gone];
906
+ return n;
907
+ });
908
+ setSessions((s) => s && s.map((x) => {
909
+ const curT = Array.isArray(x.tags) ? x.tags : [];
910
+ if (!curT.some((id) => String(id) === gone)) return x;
911
+ const nextT = [];
912
+ const seenT = /* @__PURE__ */ new Set();
913
+ for (const id of curT) {
914
+ const v = String(id) === gone ? String(toId) : String(id);
915
+ if (seenT.has(v)) continue;
916
+ seenT.add(v);
917
+ nextT.push(v);
918
+ }
919
+ return Object.assign({}, x, { tags: nextT });
920
+ }));
921
+ showToast(`\u5DF2\u5E76\u5165\u300C${tagMap.get(String(toId)) || "\u76EE\u6807\u6807\u7B7E"}\u300D`);
922
+ } catch (e) {
923
+ showToast("\u5408\u5E76\u6807\u7B7E\u5931\u8D25\uFF1A" + String(e && e.message || e), "err");
924
+ } finally {
925
+ setMgrBusy(false);
926
+ }
927
+ };
928
+ const saveCurrentFilter = async () => {
929
+ const name = saveFilterName.trim();
930
+ if (!name || mgrBusy) return;
931
+ setMgrBusy(true);
932
+ try {
933
+ const r = await postJSON("/archived-sessions/filters/save", {
934
+ name,
935
+ filters: filterSnapshotOf({ filter, workspaceFilter, sortBy, tagFilter })
936
+ });
937
+ if (r && r.item) setSavedFilters((l) => l.some((x) => String(x.id) === String(r.item.id)) ? l : l.concat(r.item));
938
+ setSaveFilterOpen(false);
939
+ setSaveFilterName("");
940
+ showToast(`\u5DF2\u4FDD\u5B58\u7B5B\u9009\u300C${r && r.item && r.item.name || name}\u300D`);
941
+ } catch (e) {
942
+ showToast("\u4FDD\u5B58\u7B5B\u9009\u5931\u8D25\uFF1A" + String(e && e.message || e), "err");
943
+ } finally {
944
+ setMgrBusy(false);
945
+ }
946
+ };
947
+ const applySavedFilter = (id) => {
948
+ const key = String(id);
949
+ setAppliedSavedId(key);
950
+ if (!key) return;
951
+ const item = savedFilters.find((x) => String(x.id) === key);
952
+ if (!item) return;
953
+ const shape = filterShapeFromSaved(item);
954
+ const tagUnavailable = !!shape.tag && (!tagsReady || !tagMap.has(shape.tag));
955
+ const degraded = shape.degraded.slice();
956
+ if (tagUnavailable) degraded.push("tag");
957
+ setFilter(shape.view);
958
+ clearSel();
959
+ setConfirmBatch(false);
960
+ if (shape.view === "starred" || shape.view === "empty" || shape.view === "trash") setMoreOpen(true);
961
+ setWorkspaceFilter(shape.workspace);
962
+ setSortBy(shape.sort);
963
+ setTagFilter(tagUnavailable ? "" : shape.tag);
964
+ if (degraded.length) {
965
+ const labels = { view: "\u89C6\u56FE", workspace: "\u5DE5\u4F5C\u533A", sort: "\u6392\u5E8F", tag: "\u6807\u7B7E" };
966
+ showToast(`\u5DF2\u5E94\u7528\u7B5B\u9009\u300C${item.name}\u300D\uFF0C\u4F46\u5176\u4E2D ${[...new Set(degraded)].map((k) => labels[k] || k).join("\u3001")} \u6761\u4EF6\u5DF2\u5931\u6548\u5E76\u56DE\u843D\u9ED8\u8BA4`, "err");
967
+ }
968
+ };
969
+ const deleteSavedFilter = async (id) => {
970
+ if (mgrBusy) return;
971
+ setMgrBusy(true);
972
+ try {
973
+ await postJSON("/archived-sessions/filters/delete", { ids: [id] });
974
+ setSavedFilters((l) => l.filter((x) => String(x.id) !== String(id)));
975
+ if (String(appliedSavedId) === String(id)) setAppliedSavedId("");
976
+ } catch (e) {
977
+ showToast("\u5220\u9664\u5DF2\u5B58\u7B5B\u9009\u5931\u8D25\uFF1A" + String(e && e.message || e), "err");
978
+ } finally {
979
+ setMgrBusy(false);
980
+ }
981
+ };
492
982
  const loadLineage = () => postJSON("/archived-sessions/sidebar-state", {}).then((r) => setLineage(r && r.lineage || {})).catch(() => {
493
983
  });
494
984
  const showToast = (msg, kind) => {
@@ -515,6 +1005,10 @@ function SessionPanel({ workspacesSvc }) {
515
1005
  loadLineage();
516
1006
  if (!targetWs && works.items && works.items.length) setTargetWs(works.items[0].workspaceId);
517
1007
  loadTrash();
1008
+ loadTags();
1009
+ loadSavedFilters();
1010
+ postJSON("/archived-sessions/pending-moves", {}).then((q) => setPendingQueue(q && q.items || [])).catch(() => {
1011
+ });
518
1012
  if (storageOpen) loadStorage();
519
1013
  else storageDirty.current = true;
520
1014
  }).catch((e) => setError(String(e && e.message || e)));
@@ -530,6 +1024,7 @@ function SessionPanel({ workspacesSvc }) {
530
1024
  (0, import_react.useEffect)(() => dsmOnWarmDone(() => {
531
1025
  if (refreshRef.current) refreshRef.current();
532
1026
  }), []);
1027
+ (0, import_react.useEffect)(() => dsmOnNotice((text, kind) => showToast(text, kind)), []);
533
1028
  (0, import_react.useEffect)(() => {
534
1029
  try {
535
1030
  localStorage.setItem(PANEL_PREFS_KEY, JSON.stringify({ filter, workspaceFilter, sortBy, groupByLineage }));
@@ -631,8 +1126,9 @@ function SessionPanel({ workspacesSvc }) {
631
1126
  const list = (0, import_react.useMemo)(() => {
632
1127
  if (filter === "trash") return [];
633
1128
  const base = filter === "archived" ? archivedList : filter === "active" ? activeList : filter === "starred" ? starredList : filter === "empty" ? emptyList : sessions || [];
1129
+ const tagPassed = applyTagFilter(base, tagsReady ? tagFilter : "");
634
1130
  const needle = query.trim().toLocaleLowerCase();
635
- const filtered = base.filter((item) => {
1131
+ const filtered = tagPassed.filter((item) => {
636
1132
  if (workspaceFilter !== "all" && (item.workspacePath || "") !== workspaceFilter) return false;
637
1133
  if (!needle) return true;
638
1134
  return [effectiveTitleOf(item, dsmAuthoritativeTitles), item.sessionId, item.workspaceTitle, item.workspacePath].some((value) => String(value || "").toLocaleLowerCase().includes(needle));
@@ -642,18 +1138,28 @@ function SessionPanel({ workspacesSvc }) {
642
1138
  if (sortBy === "title") return effectiveTitleOf(a, dsmAuthoritativeTitles).localeCompare(effectiveTitleOf(b, dsmAuthoritativeTitles), "zh-CN");
643
1139
  return Number(b.createdAt || 0) - Number(a.createdAt || 0);
644
1140
  });
645
- }, [sessions, filter, query, workspaceFilter, sortBy, emptyList, starredList]);
1141
+ }, [sessions, filter, query, workspaceFilter, sortBy, emptyList, starredList, tagFilter, tagsReady]);
646
1142
  const selIds = Object.keys(selected).filter((k) => selected[k]);
647
1143
  const showSessionList = filter !== "trash";
648
- const { topList, kidsOf, foldedCount } = (0, import_react.useMemo)(() => {
649
- if (!groupByLineage) return { topList: list, kidsOf: /* @__PURE__ */ new Map(), foldedCount: 0 };
650
- return foldSubagents(list, lineage);
1144
+ const { topList, kidsOf, foldedCount, branchGroupsOf, branchFolded, branchGroupCount } = (0, import_react.useMemo)(() => {
1145
+ if (!groupByLineage) return { topList: list, kidsOf: /* @__PURE__ */ new Map(), foldedCount: 0, branchGroupsOf: /* @__PURE__ */ new Map(), branchFolded: 0, branchGroupCount: 0 };
1146
+ const kids = foldSubagents(list, lineage);
1147
+ const br = foldBranches(kids.topList, lineage);
1148
+ return { topList: br.topList, kidsOf: kids.kidsOf, foldedCount: kids.foldedCount, branchGroupsOf: br.branchGroupsOf, branchFolded: br.foldedCount, branchGroupCount: br.groupCount };
651
1149
  }, [list, lineage, groupByLineage]);
1150
+ const matchIds = (0, import_react.useMemo)(() => {
1151
+ const needle = query.trim().toLocaleLowerCase();
1152
+ if (!needle) return null;
1153
+ return new Set((sessions || []).filter((item) => [effectiveTitleOf(item, dsmAuthoritativeTitles), item.sessionId, item.workspaceTitle, item.workspacePath].some((v) => String(v || "").toLocaleLowerCase().includes(needle))).map((i) => String(i.sessionId)));
1154
+ }, [sessions, query]);
1155
+ const kidsHit = (parentId) => !!(matchIds && (kidsOf.get(String(parentId)) || []).some((k) => matchIds.has(String(k.sessionId))));
1156
+ const branchHit = (rootId) => !!(matchIds && (branchGroupsOf.get(String(rootId)) || []).some((m) => matchIds.has(String(m.sessionId))));
1157
+ const syntheticCount = groupByLineage ? topList.reduce((n, it) => n + (it && it.syntheticRoot ? 1 : 0), 0) : 0;
652
1158
  const kidsBadge = (sessionId) => {
653
1159
  if (!groupByLineage) return null;
654
1160
  const kids = kidsOf.get(String(sessionId)) || [];
655
1161
  if (!kids.length) return null;
656
- const open = !!openKids[sessionId];
1162
+ const open = !!openKids[sessionId] || kidsHit(sessionId);
657
1163
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
658
1164
  "button",
659
1165
  {
@@ -684,9 +1190,28 @@ function SessionPanel({ workspacesSvc }) {
684
1190
  if (!li || !li.empty) return null;
685
1191
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-empty-chip", title: "\u65E0\u5185\u5BB9\u7684\u7A7A\u767D\u4F1A\u8BDD", children: "\u7A7A\u767D" });
686
1192
  };
1193
+ const tagChips = (sessionId) => {
1194
+ if (!tagsReady) return null;
1195
+ const ids = Array.isArray(assignments[String(sessionId)]) ? assignments[String(sessionId)] : [];
1196
+ const names = [];
1197
+ for (const id of ids) {
1198
+ const n = tagMap.get(String(id));
1199
+ if (n) names.push(n);
1200
+ }
1201
+ if (!names.length) return null;
1202
+ const shown = names.slice(0, 3);
1203
+ const rest = names.slice(3);
1204
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1205
+ shown.map((n, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-tagchip", title: "\u6807\u7B7E\uFF1A" + n, children: n }, i)),
1206
+ rest.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsm-tagchip dsm-tagchip-more", title: "\u53E6\u6709\u6807\u7B7E\uFF1A" + rest.join("\u3001"), children: [
1207
+ "+",
1208
+ rest.length
1209
+ ] })
1210
+ ] });
1211
+ };
687
1212
  const renderKids = (parentId, depth) => {
688
1213
  const kids = kidsOf.get(String(parentId)) || [];
689
- if (!kids.length || !openKids[parentId]) return null;
1214
+ if (!kids.length || !(openKids[parentId] || kidsHit(parentId))) return null;
690
1215
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsm-kids", children: kids.map((k) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsm-kid", children: [
691
1216
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsm-kid-row", children: [
692
1217
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-kid-name", title: k.title || k.sessionId, children: k.title || "(\u65E0\u6807\u9898)" }),
@@ -708,6 +1233,56 @@ function SessionPanel({ workspacesSvc }) {
708
1233
  (depth || 0) < 4 && renderKids(k.sessionId, (depth || 0) + 1)
709
1234
  ] }, k.sessionId)) });
710
1235
  };
1236
+ const branchGroupBadge = (sessionId) => {
1237
+ if (!groupByLineage) return null;
1238
+ const members = branchGroupsOf.get(String(sessionId)) || [];
1239
+ if (!members.length) return null;
1240
+ const open = !!openBranches[sessionId] || branchHit(sessionId);
1241
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1242
+ "button",
1243
+ {
1244
+ type: "button",
1245
+ className: "dsm-kids-toggle",
1246
+ "aria-expanded": open,
1247
+ title: (open ? "\u6536\u8D77 " : "\u5C55\u5F00 ") + members.length + " \u4E2A\u5206\u652F\u4F1A\u8BDD",
1248
+ onClick: () => toggleBranch(sessionId),
1249
+ children: [
1250
+ open ? "\u25BE" : "\u25B8",
1251
+ " ",
1252
+ members.length,
1253
+ " \u5206\u652F"
1254
+ ]
1255
+ }
1256
+ );
1257
+ };
1258
+ const renderBranchGroup = (rootId) => {
1259
+ const members = branchGroupsOf.get(String(rootId)) || [];
1260
+ if (!members.length || !(openBranches[rootId] || branchHit(rootId))) return null;
1261
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsm-kids", "aria-label": "\u5206\u652F\u4F1A\u8BDD", children: members.map((k) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsm-kid", children: [
1262
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsm-kid-row", children: [
1263
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-kid-name", title: k.title || k.sessionId, children: k.title || "(\u65E0\u6807\u9898)" }),
1264
+ branchBadge(k.sessionId),
1265
+ emptyBadge(k.sessionId),
1266
+ kidsBadge(k.sessionId),
1267
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsm-kid-meta", children: [
1268
+ k.archived ? "\u5DF2\u5F52\u6863" : "\u6D3B\u52A8",
1269
+ fmtDate(k.createdAt) ? ` \xB7 ${fmtDate(k.createdAt)}` : ""
1270
+ ] }),
1271
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsm-kid-acts", children: [
1272
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", disabled: busy !== null, title: "\u5207\u6362\u5230\u8FD9\u4E2A\u5206\u652F\u4F1A\u8BDD\uFF08\u8BBE\u7F6E\u9762\u677F\u6321\u7740\u4F1A\u8BDD\u533A\uFF0C\u5173\u6389\u5373\u53EF\u770B\u5230\uFF09", onClick: async () => {
1273
+ const name = k.title || String(k.sessionId).slice(0, 8) + "\u2026";
1274
+ const li = lineage[String(k.sessionId)];
1275
+ const res = await dsmOpenSessionById(k.sessionId, li && li.parentSession || rootId);
1276
+ const msg = openSubagentToast(res, name, "panel");
1277
+ showToast(msg.text, msg.kind);
1278
+ }, children: "\u6253\u5F00" }),
1279
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", disabled: busy !== null, onClick: () => act(k.archived ? "restore" : "archive", k), children: k.archived ? "\u6062\u590D" : "\u5F52\u6863" }),
1280
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn archv-del", disabled: busy !== null, title: "\u79FB\u5165\u56DE\u6536\u7AD9\uFF0C\u53EF\u5728\u56DE\u6536\u7AD9\u6062\u590D", onClick: () => setDelTarget(k), children: "\u5220\u9664" })
1281
+ ] })
1282
+ ] }),
1283
+ renderKids(k.sessionId, 1)
1284
+ ] }, k.sessionId)) });
1285
+ };
711
1286
  const toggle = (id) => setSelected((s) => ({ ...s, [id]: !s[id] }));
712
1287
  const clearSel = () => setSelected({});
713
1288
  const selectAll = () => {
@@ -981,6 +1556,7 @@ function SessionPanel({ workspacesSvc }) {
981
1556
  setOpenMove(null);
982
1557
  return;
983
1558
  }
1559
+ setOpenTags(null);
984
1560
  setTargetWs(workspaces.length ? targetWs || workspaces[0].workspaceId : "");
985
1561
  setMoveMode("existing");
986
1562
  setNewPath("");
@@ -1040,21 +1616,36 @@ function SessionPanel({ workspacesSvc }) {
1040
1616
  if (wName) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "archv-wtag", title: it.workspacePath || "", children: wName });
1041
1617
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "archv-wtag", children: "\u672A\u5206\u7EC4" });
1042
1618
  };
1619
+ const openTagsFor = (it) => {
1620
+ if (!tagsReady) {
1621
+ showToast(tagsState === "failed" ? "\u6807\u7B7E\u6570\u636E\u672A\u80FD\u52A0\u8F7D\uFF0C\u6682\u65F6\u65E0\u6CD5\u7F16\u8F91\u6807\u7B7E\uFF1B\u91CD\u65B0\u6253\u5F00\u8BBE\u7F6E\u9762\u677F\u53EF\u91CD\u8BD5" : "\u6807\u7B7E\u6570\u636E\u8FD8\u5728\u52A0\u8F7D\u4E2D\uFF0C\u7A0D\u5019\u518D\u8BD5", "err");
1622
+ return;
1623
+ }
1624
+ if (openTags === it.sessionId) {
1625
+ setOpenTags(null);
1626
+ return;
1627
+ }
1628
+ setOpenMove(null);
1629
+ setCardTagName("");
1630
+ setOpenTags(it.sessionId);
1631
+ };
1043
1632
  const runMenu = (id, it) => {
1044
1633
  setOpenMenu(null);
1045
1634
  if (id === "restore") act("restore", it);
1046
1635
  else if (id === "archive") act("archive", it);
1047
1636
  else if (id === "delete") setDelTarget(it);
1048
1637
  else if (id === "move") openMoveFor(it);
1638
+ else if (id === "tags") openTagsFor(it);
1049
1639
  else if (id === "details") toggleDetails(it);
1050
1640
  };
1051
1641
  const rowMenu = (it) => {
1052
1642
  const items = it.archived ? [
1053
1643
  ["restore", "\u6062\u590D"],
1054
1644
  ["move", openMove === it.sessionId ? "\u6536\u8D77\u79FB\u52A8" : "\u79FB\u52A8"],
1645
+ ["tags", "\u6807\u7B7E"],
1055
1646
  ["details", openDetails === it.sessionId ? "\u6536\u8D77\u8BE6\u60C5" : "\u8BE6\u60C5"],
1056
1647
  ["delete", "\u5220\u9664"]
1057
- ] : [["archive", "\u5F52\u6863"], ["move", "\u79FB\u52A8"], ["details", "\u8BE6\u60C5"], ["delete", "\u5220\u9664"]];
1648
+ ] : [["archive", "\u5F52\u6863"], ["move", "\u79FB\u52A8"], ["tags", openTags === it.sessionId ? "\u6536\u8D77\u6807\u7B7E" : "\u6807\u7B7E"], ["details", "\u8BE6\u60C5"], ["delete", "\u5220\u9664"]];
1058
1649
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref: openMenu === it.sessionId ? menuRef : null, className: "more-wrap", children: [
1059
1650
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1060
1651
  "button",
@@ -1072,7 +1663,8 @@ function SessionPanel({ workspacesSvc }) {
1072
1663
  }
1073
1664
  ),
1074
1665
  openMenu === it.sessionId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "more-menu", role: "menu", children: items.map(([id, label]) => {
1075
- const blocked = id === "move" && !canMove.available;
1666
+ const blocked = id === "move" && !canMove.available || id === "tags" && !tagsReady;
1667
+ const blockedTitle = id === "move" ? canMove.reason : id === "tags" ? tagsBlockedTitle : void 0;
1076
1668
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1077
1669
  "button",
1078
1670
  {
@@ -1080,7 +1672,7 @@ function SessionPanel({ workspacesSvc }) {
1080
1672
  role: "menuitem",
1081
1673
  className: "more-item" + (id === "delete" ? " more-item-danger" : ""),
1082
1674
  disabled: blocked,
1083
- title: blocked ? canMove.reason : void 0,
1675
+ title: blocked ? blockedTitle : void 0,
1084
1676
  onClick: () => runMenu(id, it),
1085
1677
  children: label
1086
1678
  },
@@ -1174,8 +1766,122 @@ function SessionPanel({ workspacesSvc }) {
1174
1766
  ")"
1175
1767
  ] })
1176
1768
  ] }),
1177
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "sess-farrow", "aria-expanded": moreOpen, "aria-label": moreOpen ? "\u6536\u8D77\u66F4\u591A\u7B5B\u9009" : "\u5C55\u5F00\u66F4\u591A\u7B5B\u9009", title: moreOpen ? "\u6536\u8D77\u66F4\u591A\u7B5B\u9009" : "\u5C55\u5F00\u66F4\u591A\u7B5B\u9009\uFF08\u5DF2\u6536\u85CF / \u7A7A\u767D / \u56DE\u6536\u7AD9\uFF09", onClick: () => setMoreOpen(!moreOpen), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 12 12", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 2l4 4-4 4", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }) }) })
1769
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "sess-farrow", "aria-expanded": moreOpen, "aria-label": moreOpen ? "\u6536\u8D77\u66F4\u591A\u7B5B\u9009" : "\u5C55\u5F00\u66F4\u591A\u7B5B\u9009", title: moreOpen ? "\u6536\u8D77\u66F4\u591A\u7B5B\u9009" : "\u5C55\u5F00\u66F4\u591A\u7B5B\u9009\uFF08\u5DF2\u6536\u85CF / \u7A7A\u767D / \u56DE\u6536\u7AD9\uFF09", onClick: () => setMoreOpen(!moreOpen), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 12 12", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 2l4 4-4 4", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
1770
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1771
+ "button",
1772
+ {
1773
+ type: "button",
1774
+ className: "archv-btn",
1775
+ "aria-expanded": openTagMgr,
1776
+ disabled: !tagsReady && !openTagMgr,
1777
+ title: tagsReady ? "\u65B0\u5EFA / \u91CD\u547D\u540D / \u5408\u5E76 / \u5220\u9664\u6807\u7B7E" : tagsBlockedTitle,
1778
+ onClick: () => {
1779
+ setOpenTagMgr(!openTagMgr);
1780
+ setRenamingTagId(null);
1781
+ },
1782
+ children: [
1783
+ "\u6807\u7B7E\u7BA1\u7406",
1784
+ tagsReady && tagDefs.length ? ` (${tagDefs.length})` : ""
1785
+ ]
1786
+ }
1787
+ )
1178
1788
  ] }) }),
1789
+ openTagMgr && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-sheet", "aria-label": "\u6807\u7B7E\u7BA1\u7406", children: [
1790
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-sheet-head", children: [
1791
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h3", { className: "mv-sheet-title", children: [
1792
+ "\u6807\u7B7E\u7BA1\u7406 \xB7 ",
1793
+ tagDefs.length
1794
+ ] }),
1795
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "mv-sheet-close", "aria-label": "\u5173\u95ED", onClick: () => {
1796
+ setOpenTagMgr(false);
1797
+ setRenamingTagId(null);
1798
+ }, children: "\xD7" })
1799
+ ] }),
1800
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-field", children: [
1801
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "mv-field-label", htmlFor: "dsm-tag-new", children: "\u65B0\u5EFA\u6807\u7B7E" }),
1802
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-browse-row", children: [
1803
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1804
+ "input",
1805
+ {
1806
+ id: "dsm-tag-new",
1807
+ type: "text",
1808
+ value: mgrTagName,
1809
+ disabled: mgrBusy,
1810
+ maxLength: 24,
1811
+ placeholder: "\u6807\u7B7E\u540D\uFF08\u4E0D\u80FD\u542B\u659C\u6760\uFF0C\u6700\u957F 24 \u5B57\uFF09",
1812
+ onChange: (e) => setMgrTagName(e.target.value),
1813
+ onKeyDown: (e) => {
1814
+ if (e.key === "Enter") {
1815
+ e.preventDefault();
1816
+ createTag();
1817
+ }
1818
+ }
1819
+ }
1820
+ ),
1821
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", disabled: mgrBusy || !mgrTagName.trim(), onClick: createTag, children: "\u65B0\u5EFA" })
1822
+ ] })
1823
+ ] }),
1824
+ tagDefs.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtl-note", children: "\u8FD8\u6CA1\u6709\u6807\u7B7E\u3002\u5728\u4E0A\u65B9\u8F93\u5165\u540D\u5B57\u5373\u53EF\u521B\u5EFA\uFF1B\u7ED9\u4F1A\u8BDD\u8D34\u6807\u7B7E\u4E5F\u53EF\u4EE5\u5728\u4F1A\u8BDD\u5361\u7247\u7684\u300C\u22EF \u2192 \u6807\u7B7E\u300D\u91CC\u8FDB\u884C\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: tagDefs.map((t) => {
1825
+ const others = tagDefs.filter((o) => String(o.id) !== String(t.id));
1826
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsm-tagrow", children: renamingTagId === String(t.id) ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1827
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1828
+ "input",
1829
+ {
1830
+ className: "dsm-tag-input",
1831
+ type: "text",
1832
+ value: renameTagValue,
1833
+ disabled: mgrBusy,
1834
+ maxLength: 24,
1835
+ "aria-label": "\u91CD\u547D\u540D\u6807\u7B7E " + t.name,
1836
+ autoFocus: true,
1837
+ onChange: (e) => setRenameTagValue(e.target.value),
1838
+ onKeyDown: (e) => {
1839
+ if (e.key === "Enter") {
1840
+ e.preventDefault();
1841
+ commitTagRename(t);
1842
+ }
1843
+ if (e.key === "Escape") setRenamingTagId(null);
1844
+ }
1845
+ }
1846
+ ),
1847
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsm-tag-acts", children: [
1848
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn archv-go", disabled: mgrBusy || !renameTagValue.trim(), onClick: () => commitTagRename(t), children: "\u4FDD\u5B58" }),
1849
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", disabled: mgrBusy, onClick: () => setRenamingTagId(null), children: "\u53D6\u6D88" })
1850
+ ] })
1851
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1852
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-tag-name", title: t.name, children: t.name }),
1853
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "maint-note", children: [
1854
+ tagUsage[String(t.id)] || 0,
1855
+ " \u4E2A\u4F1A\u8BDD"
1856
+ ] }),
1857
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsm-tag-acts", children: [
1858
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", disabled: mgrBusy || renamingTagId !== null, title: "\u91CD\u547D\u540D\u8FD9\u4E2A\u6807\u7B7E", onClick: () => {
1859
+ setRenamingTagId(String(t.id));
1860
+ setRenameTagValue(t.name);
1861
+ }, children: "\u91CD\u547D\u540D" }),
1862
+ others.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1863
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1864
+ "select",
1865
+ {
1866
+ "aria-label": "\u628A\u6807\u7B7E\u300C" + t.name + "\u300D\u5E76\u5165",
1867
+ value: mergeTarget[String(t.id)] || "",
1868
+ disabled: mgrBusy,
1869
+ title: "\u628A\u8FD9\u4E2A\u6807\u7B7E\u7684\u6240\u6709\u4F1A\u8BDD\u5E76\u5165\u53E6\u4E00\u4E2A\u6807\u7B7E\uFF08\u5E76\u5165\u540E\u672C\u6807\u7B7E\u5220\u9664\uFF09",
1870
+ onChange: (e) => setMergeTarget((m) => Object.assign({}, m, { [String(t.id)]: e.target.value })),
1871
+ children: [
1872
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: "\u5E76\u5165\u2026" }),
1873
+ others.map((o) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: String(o.id), children: o.name }, String(o.id)))
1874
+ ]
1875
+ }
1876
+ ),
1877
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", disabled: mgrBusy || !mergeTarget[String(t.id)], title: mergeTarget[String(t.id)] ? "\u786E\u8BA4\u5E76\u5165\u6240\u9009\u6807\u7B7E" : "\u5148\u5728\u5DE6\u4FA7\u9009\u62E9\u5E76\u5165\u7684\u76EE\u6807\u6807\u7B7E", onClick: () => mergeTag(t), children: "\u786E\u8BA4" })
1878
+ ] }),
1879
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn archv-del", disabled: mgrBusy, title: "\u5220\u9664\u6807\u7B7E\uFF08\u53EA\u5220\u6807\u7B7E\uFF0C\u4E0D\u4F1A\u5220\u9664\u4F1A\u8BDD\uFF09", onClick: () => deleteTag(t), children: "\u5220\u9664" })
1880
+ ] })
1881
+ ] }) }, String(t.id));
1882
+ }) }),
1883
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtl-note", children: "\u6807\u7B7E\u662F\u4F1A\u8BDD\u7684\u81EA\u5B9A\u4E49\u6807\u8BB0\uFF1A\u65B0\u5EFA\u3001\u91CD\u547D\u540D\u3001\u5408\u5E76\u3001\u5220\u9664\u90FD\u53EA\u6539\u6807\u8BB0\u672C\u8EAB\uFF0C\u4E0D\u4F1A\u5220\u9664\u6216\u79FB\u52A8\u4EFB\u4F55\u4F1A\u8BDD\u3002\u5355\u4E2A\u4F1A\u8BDD\u7684\u6807\u7B7E\u6570\u91CF\u4E0E\u6807\u7B7E\u603B\u6570\u90FD\u6709\u670D\u52A1\u7AEF\u4E0A\u9650\uFF0C\u8D85\u9650\u65F6\u4F1A\u63D0\u793A\u5E76\u81EA\u52A8\u56DE\u9000\u672C\u5730\u6539\u52A8\u3002" })
1884
+ ] }),
1179
1885
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "maint-bar", children: [
1180
1886
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "archv-btn", "aria-expanded": storageOpen, onClick: () => {
1181
1887
  const next = !storageOpen;
@@ -1197,6 +1903,28 @@ function SessionPanel({ workspacesSvc }) {
1197
1903
  " \u4E2A"
1198
1904
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "maint-note", children: "\u5C1A\u672A\u68C0\u67E5" })
1199
1905
  ] }),
1906
+ pendingQueue.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-sheet", "aria-label": "\u5F85\u79FB\u52A8\u961F\u5217", children: [
1907
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mv-sheet-head", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h3", { className: "mv-sheet-title", children: [
1908
+ "\u5F85\u79FB\u52A8\u961F\u5217 \xB7 ",
1909
+ pendingQueue.length
1910
+ ] }) }),
1911
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtl-note", children: "\u8FD9\u4E9B\u4F1A\u8BDD\u6B63\u88AB DSH \u6253\u5F00\u7740\uFF0C\u5B98\u65B9\u53EA\u5728\u8FDB\u7A0B\u9000\u51FA\u65F6\u91CA\u653E\u5199\u6743\u9650\uFF1B\u91CA\u653E\u540E\u63D2\u4EF6\u4F1A\u81EA\u52A8\u5B8C\u6210\u79FB\u52A8\uFF0C\u4E5F\u53EF\u91CD\u542F DSH \u8BA9\u5B83\u5728\u542F\u52A8\u5934\u51E0\u79D2\u62A2\u5148\u8865\u8DD1\u3002" }),
1912
+ pendingQueue.map((q) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsm-kid-row", children: [
1913
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsm-kid-name", title: `${q.sessionId} \u2192 ${q.targetPath || ""}`, children: [
1914
+ shortId(q.sessionId),
1915
+ "\u2026 \u2192 ",
1916
+ pathTail(q.targetPath) || "\u76EE\u6807\u5DE5\u4F5C\u533A"
1917
+ ] }),
1918
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsm-kid-acts", children: [
1919
+ Number.isSafeInteger(q.attempts) && q.attempts > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "maint-note", children: [
1920
+ "\u5DF2\u5931\u8D25 ",
1921
+ q.attempts,
1922
+ " \u6B21"
1923
+ ] }) : null,
1924
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", onClick: () => cancelQueuedMove(q.sessionId), children: "\u53D6\u6D88" })
1925
+ ] })
1926
+ ] }, String(q.sessionId)))
1927
+ ] }),
1200
1928
  aaOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-sheet", "aria-label": "\u81EA\u52A8\u5F52\u6863\u8BBE\u7F6E", children: [
1201
1929
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-sheet-head", children: [
1202
1930
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "mv-sheet-title", children: "\u81EA\u52A8\u5F52\u6863" }),
@@ -1259,7 +1987,7 @@ function SessionPanel({ workspacesSvc }) {
1259
1987
  ] })
1260
1988
  ] }),
1261
1989
  showSessionList && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1262
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-tools sess-tools-4", "aria-label": "\u67E5\u627E\u548C\u6574\u7406\u4F1A\u8BDD", children: [
1990
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-tools sess-tools-5", "aria-label": "\u67E5\u627E\u548C\u6574\u7406\u4F1A\u8BDD", children: [
1263
1991
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-field", children: [
1264
1992
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "dsm-search", children: "\u641C\u7D22\u4F1A\u8BDD" }),
1265
1993
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { id: "dsm-search", type: "search", value: query, onChange: (e) => setQuery(e.target.value), placeholder: "\u6807\u9898\u3001\u4F1A\u8BDD ID \u6216\u5DE5\u4F5C\u533A" })
@@ -1268,9 +1996,31 @@ function SessionPanel({ workspacesSvc }) {
1268
1996
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "dsm-workspace-filter", children: "\u5DE5\u4F5C\u533A" }),
1269
1997
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { id: "dsm-workspace-filter", value: workspaceFilter, onChange: (e) => setWorkspaceFilter(e.target.value), children: [
1270
1998
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "all", children: "\u5168\u90E8\u5DE5\u4F5C\u533A" }),
1271
- workspaces.map((w) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: w.path, children: w.title }, w.workspaceId))
1999
+ workspaces.map((w) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: w.path, children: w.title }, w.workspaceId)),
2000
+ workspaceFilter !== "all" && !workspaces.some((w) => w.path === workspaceFilter) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("option", { value: workspaceFilter, children: [
2001
+ "\u5DF2\u5220\u5DE5\u4F5C\u533A \xB7 ",
2002
+ pathName(workspaceFilter) || "?"
2003
+ ] })
1272
2004
  ] })
1273
2005
  ] }),
2006
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-field", children: [
2007
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "dsm-tag-filter", children: "\u6807\u7B7E" }),
2008
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2009
+ "select",
2010
+ {
2011
+ id: "dsm-tag-filter",
2012
+ value: tagFilter,
2013
+ disabled: !tagsReady,
2014
+ title: !tagsReady ? tagsBlockedTitle : tagFilter ? "\u5F53\u524D\u6309\u300C" + (tagMap.get(tagFilter) || "\u6240\u9009\u6807\u7B7E") + "\u300D\u7B5B\u9009\uFF1B\u591A\u9009\u7EC4\u5408\u53EF\u7528\u300C\u4FDD\u5B58\u5F53\u524D\u7B5B\u9009\u300D\u56FA\u5316" : "\u6309\u6807\u7B7E\u7B5B\u9009\uFF08\u5355\u9009\uFF09",
2015
+ onChange: (e) => setTagFilter(e.target.value),
2016
+ children: [
2017
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: "\u5168\u90E8\u6807\u7B7E" }),
2018
+ tagDefs.map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: t.id, children: t.name }, t.id)),
2019
+ tagFilter && !tagMap.has(tagFilter) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: tagFilter, children: "\uFF08\u5DF2\u5220\u6807\u7B7E\uFF09" })
2020
+ ]
2021
+ }
2022
+ )
2023
+ ] }),
1274
2024
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-field", children: [
1275
2025
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "dsm-sort", children: "\u6392\u5E8F" }),
1276
2026
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { id: "dsm-sort", value: sortBy, onChange: (e) => setSortBy(e.target.value), children: [
@@ -1281,18 +2031,89 @@ function SessionPanel({ workspacesSvc }) {
1281
2031
  ] }),
1282
2032
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-field", children: [
1283
2033
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "dsm-group", children: "\u5206\u7EC4" }),
1284
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { id: "dsm-group", value: groupByLineage ? "lineage" : "flat", onChange: (e) => setGroupByLineage(e.target.value === "lineage"), title: "\u8840\u7F18\u5206\u7EC4\uFF1A\u5B50\u4EE3\u7406\u6298\u53E0\u5728\u7236\u4F1A\u8BDD\u4E0B\uFF1B\u5E73\u94FA\uFF1A\u4E0E DSH \u539F\u751F\u4E00\u81F4\uFF0C\u5168\u90E8\u5E76\u5217", children: [
1285
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "lineage", children: "\u8840\u7F18\uFF08\u5B50\u4EE3\u7406\u6298\u53E0\uFF09" }),
2034
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { id: "dsm-group", value: groupByLineage ? "lineage" : "flat", onChange: (e) => setGroupByLineage(e.target.value === "lineage"), title: "\u8840\u7F18\u5206\u7EC4\uFF1A\u5B50\u4EE3\u7406\u6298\u53E0\u3001\u5206\u652F\u805A\u62E2\u6210\u7EC4\uFF1B\u5E73\u94FA\uFF1A\u4E0E DSH \u539F\u751F\u4E00\u81F4\uFF0C\u5168\u90E8\u5E76\u5217", children: [
2035
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "lineage", children: "\u8840\u7F18\uFF08\u6298\u53E0\u5206\u7EC4\uFF09" }),
1286
2036
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "flat", children: "\u5E73\u94FA\uFF08\u5168\u90E8\u5E76\u5217\uFF09" })
1287
2037
  ] })
1288
2038
  ] })
1289
2039
  ] }),
1290
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "sess-results", role: "status", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "sess-results-main", children: [
1291
- "\u663E\u793A ",
1292
- topList.length,
1293
- " \u4E2A\u4F1A\u8BDD",
1294
- foldedCount ? `\uFF0C\u53E6\u6709 ${foldedCount} \u4E2A\u5B50\u4EE3\u7406\u6298\u53E0\u5728\u7236\u4F1A\u8BDD\u4E0B` : query || workspaceFilter !== "all" ? `\uFF0C\u5171 ${filter === "archived" ? archivedList.length : filter === "active" ? activeList.length : filter === "starred" ? starredList.length : filter === "empty" ? emptyList.length : sessions.length} \u4E2A` : topList.length !== list.length ? `\uFF0C\u5171 ${list.length} \u4E2A` : ""
1295
- ] }) })
2040
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-results", children: [
2041
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "sess-results-main", role: "status", children: [
2042
+ "\u663E\u793A ",
2043
+ topList.length - syntheticCount,
2044
+ " \u4E2A\u4F1A\u8BDD",
2045
+ foldedCount || branchFolded ? `\uFF0C\u53E6\u6709 ${[foldedCount ? `${foldedCount} \u4E2A\u5B50\u4EE3\u7406\u6298\u53E0\u5728\u7236\u4F1A\u8BDD\u4E0B` : "", branchFolded ? `${branchFolded} \u4E2A\u5206\u652F\u805A\u6210 ${branchGroupCount} \u7EC4` : ""].filter(Boolean).join("\u3001")}` : query || workspaceFilter !== "all" || tagFilter ? `\uFF0C\u5171 ${filter === "archived" ? archivedList.length : filter === "active" ? activeList.length : filter === "starred" ? starredList.length : filter === "empty" ? emptyList.length : sessions.length} \u4E2A` : topList.length !== list.length ? `\uFF0C\u5171 ${list.length} \u4E2A` : ""
2046
+ ] }),
2047
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsm-fbar", children: [
2048
+ saveFilterOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2049
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2050
+ "input",
2051
+ {
2052
+ type: "text",
2053
+ value: saveFilterName,
2054
+ maxLength: 40,
2055
+ placeholder: "\u7B5B\u9009\u540D\u79F0",
2056
+ "aria-label": "\u4E3A\u5F53\u524D\u7B5B\u9009\u547D\u540D",
2057
+ autoFocus: true,
2058
+ disabled: mgrBusy,
2059
+ onChange: (e) => setSaveFilterName(e.target.value),
2060
+ onKeyDown: (e) => {
2061
+ if (e.key === "Enter") {
2062
+ e.preventDefault();
2063
+ saveCurrentFilter();
2064
+ }
2065
+ if (e.key === "Escape") {
2066
+ setSaveFilterOpen(false);
2067
+ setSaveFilterName("");
2068
+ }
2069
+ }
2070
+ }
2071
+ ),
2072
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn archv-go", disabled: mgrBusy || !saveFilterName.trim(), onClick: saveCurrentFilter, children: "\u4FDD\u5B58" }),
2073
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", disabled: mgrBusy, onClick: () => {
2074
+ setSaveFilterOpen(false);
2075
+ setSaveFilterName("");
2076
+ }, children: "\u53D6\u6D88" })
2077
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2078
+ "button",
2079
+ {
2080
+ type: "button",
2081
+ className: "archv-btn",
2082
+ title: "\u628A\u5F53\u524D\u89C6\u56FE / \u5DE5\u4F5C\u533A / \u6807\u7B7E / \u6392\u5E8F\u5B58\u4E3A\u4E00\u4E2A\u53EF\u590D\u7528\u7684\u7B5B\u9009",
2083
+ onClick: () => {
2084
+ setSaveFilterOpen(true);
2085
+ setSaveFilterName("");
2086
+ },
2087
+ children: "\u4FDD\u5B58\u5F53\u524D\u7B5B\u9009"
2088
+ }
2089
+ ),
2090
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2091
+ "select",
2092
+ {
2093
+ "aria-label": "\u5DF2\u5B58\u7B5B\u9009",
2094
+ value: savedFilters.some((f) => String(f.id) === appliedSavedId) ? appliedSavedId : "",
2095
+ disabled: !savedFilters.length || mgrBusy,
2096
+ title: !savedFilters.length ? "\u8FD8\u6CA1\u6709\u5DF2\u5B58\u7B5B\u9009\uFF1B\u70B9\u5DE6\u4FA7\u300C\u4FDD\u5B58\u5F53\u524D\u7B5B\u9009\u300D\u521B\u5EFA" : "\u9009\u62E9\u4E00\u4E2A\u5DF2\u5B58\u7B5B\u9009\u5E76\u7ACB\u5373\u5E94\u7528\uFF1B\u9009\u4E2D\u540E\u53F3\u4FA7\u53EF\u5220\u9664",
2097
+ onChange: (e) => applySavedFilter(e.target.value),
2098
+ children: [
2099
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: savedFilters.length ? "\u5DF2\u5B58\u7B5B\u9009\u2026" : "\u65E0\u5DF2\u5B58\u7B5B\u9009" }),
2100
+ savedFilters.map((f) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: f.id, children: f.name }, f.id))
2101
+ ]
2102
+ }
2103
+ ),
2104
+ appliedSavedId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2105
+ "button",
2106
+ {
2107
+ type: "button",
2108
+ className: "archv-btn archv-del",
2109
+ disabled: mgrBusy,
2110
+ title: "\u5220\u9664\u5DF2\u5B58\u7B5B\u9009\u300C" + ((savedFilters.find((f) => String(f.id) === appliedSavedId) || {}).name || appliedSavedId) + "\u300D\uFF08\u53EA\u5220\u8FD9\u6761\u4FDD\u5B58\u7684\u7B5B\u9009\uFF0C\u4E0D\u52A8\u4F1A\u8BDD\uFF09",
2111
+ onClick: () => deleteSavedFilter(appliedSavedId),
2112
+ children: "\u5220\u9664"
2113
+ }
2114
+ )
2115
+ ] })
2116
+ ] })
1296
2117
  ] }),
1297
2118
  showSessionList && list.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-batch" + (selIds.length > 0 ? " sess-batch-pin" : ""), children: [
1298
2119
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "sess-btntext", children: selIds.length ? `\u5DF2\u9009 ${selIds.length} \u9879` : filter === "archived" ? `\u5171 ${archivedList.length} \u4E2A\u5F52\u6863\u4F1A\u8BDD` : filter === "starred" ? `\u5171 ${starredList.length} \u4E2A\u6536\u85CF\u4F1A\u8BDD` : `\u5171 ${sessions.length} \u4E2A\u4F1A\u8BDD\uFF08\u6D3B\u52A8 ${activeList.length} / \u5DF2\u5F52\u6863 ${archivedList.length}\uFF09` }),
@@ -1374,9 +2195,34 @@ function SessionPanel({ workspacesSvc }) {
1374
2195
  ] })
1375
2196
  ] })
1376
2197
  ] }),
1377
- showSessionList && list.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "archv-empty", children: query || workspaceFilter !== "all" ? "\u6CA1\u6709\u5339\u914D\u7684\u4F1A\u8BDD\u3002\u8BF7\u8C03\u6574\u641C\u7D22\u8BCD\u6216\u5DE5\u4F5C\u533A\u7B5B\u9009\u3002" : filter === "archived" ? "\u76EE\u524D\u6CA1\u6709\u5F52\u6863\u4F1A\u8BDD\u3002\u5728\u201C\u5168\u90E8\u201D\u91CC\u9009\u4E2D\u4F1A\u8BDD\u70B9\u201C\u5F52\u6863\u201D\u5373\u53EF\u6536\u7EB3\u8FDB\u6765\u3002" : filter === "active" ? "\u76EE\u524D\u6CA1\u6709\u6D3B\u52A8\u4F1A\u8BDD\u3002" : filter === "starred" ? "\u8FD8\u6CA1\u6709\u6536\u85CF\u7684\u4F1A\u8BDD\u3002\u70B9\u51FB\u4F1A\u8BDD\u5DE6\u4FA7\u7684\u661F\u6807\u5373\u53EF\u6536\u85CF\u3002" : filter === "empty" ? "\u6CA1\u6709\u7A7A\u767D\u4F1A\u8BDD\u3002\u65B0\u5F00\u4F1A\u8BDD\u8FD8\u6CA1\u4EA7\u751F\u5185\u5BB9\u65F6\u4F1A\u5F52\u5230\u8FD9\u91CC\uFF0C\u53EF\u5728\u4FA7\u680F\u7528\u300C\u7EAF\u51C0\u89C6\u56FE\u300D\u628A\u5B83\u4EEC\u4E00\u5E76\u6298\u53E0\u3002" : "\u6682\u65E0\u53EF\u7BA1\u7406\u7684\u4F1A\u8BDD\u3002" }) : showSessionList ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "archv-list", role: "list", children: topList.map((it) => {
2198
+ showSessionList && list.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "archv-empty", children: query || workspaceFilter !== "all" || tagFilter ? "\u6CA1\u6709\u5339\u914D\u7684\u4F1A\u8BDD\u3002\u8BF7\u8C03\u6574\u641C\u7D22\u8BCD\u3001\u6807\u7B7E\u6216\u5DE5\u4F5C\u533A\u7B5B\u9009\u3002" : filter === "archived" ? "\u76EE\u524D\u6CA1\u6709\u5F52\u6863\u4F1A\u8BDD\u3002\u5728\u201C\u5168\u90E8\u201D\u91CC\u9009\u4E2D\u4F1A\u8BDD\u70B9\u201C\u5F52\u6863\u201D\u5373\u53EF\u6536\u7EB3\u8FDB\u6765\u3002" : filter === "active" ? "\u76EE\u524D\u6CA1\u6709\u6D3B\u52A8\u4F1A\u8BDD\u3002" : filter === "starred" ? "\u8FD8\u6CA1\u6709\u6536\u85CF\u7684\u4F1A\u8BDD\u3002\u70B9\u51FB\u4F1A\u8BDD\u5DE6\u4FA7\u7684\u661F\u6807\u5373\u53EF\u6536\u85CF\u3002" : filter === "empty" ? "\u6CA1\u6709\u7A7A\u767D\u4F1A\u8BDD\u3002\u65B0\u5F00\u4F1A\u8BDD\u8FD8\u6CA1\u4EA7\u751F\u5185\u5BB9\u65F6\u4F1A\u5F52\u5230\u8FD9\u91CC\uFF0C\u4FA7\u680F\u4F1A\u81EA\u52A8\u9690\u85CF\u5B83\u4EEC\u3002" : "\u6682\u65E0\u53EF\u7BA1\u7406\u7684\u4F1A\u8BDD\u3002" }) : showSessionList ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "archv-list", role: "list", children: topList.map((it) => {
2199
+ if (it && it.syntheticRoot) {
2200
+ const root = String(it.syntheticRoot);
2201
+ const members = branchGroupsOf.get(root) || [];
2202
+ const open = !!openBranches[root] || branchHit(root);
2203
+ const srcTitle = dsmAuthoritativeTitles.get(root);
2204
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsm-src-head", role: "listitem", children: [
2205
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2206
+ "button",
2207
+ {
2208
+ type: "button",
2209
+ className: "dsm-kids-toggle",
2210
+ "aria-expanded": open,
2211
+ title: (open ? "\u6536\u8D77 " : "\u5C55\u5F00 ") + members.length + " \u4E2A\u5206\u652F\u4F1A\u8BDD\uFF08\u6765\u6E90\u4F1A\u8BDD\u4E0D\u5728\u5F53\u524D\u5217\u8868\uFF09",
2212
+ onClick: () => toggleBranch(root),
2213
+ children: [
2214
+ open ? "\u25BE" : "\u25B8",
2215
+ " ",
2216
+ members.length,
2217
+ " \u5206\u652F"
2218
+ ]
2219
+ }
2220
+ ),
2221
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-src-name", title: root, children: "\u6765\u6E90\uFF1A" + (srcTitle || shortId(root)) })
2222
+ ] }, it.sessionId);
2223
+ }
1378
2224
  const date = fmtDate(it.createdAt);
1379
- const expanded = openMove === it.sessionId;
2225
+ const expanded = openMove === it.sessionId || openTags === it.sessionId;
1380
2226
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "archv-card" + (expanded ? " archv-card-exp" : ""), role: "listitem", children: [
1381
2227
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "archv-row", children: [
1382
2228
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1410,7 +2256,9 @@ function SessionPanel({ workspacesSvc }) {
1410
2256
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "archv-name", title: it.title || "", children: it.title || "(\u65E0\u6807\u9898)" }),
1411
2257
  branchBadge(it.sessionId),
1412
2258
  emptyBadge(it.sessionId),
2259
+ tagChips(it.sessionId),
1413
2260
  kidsBadge(it.sessionId),
2261
+ branchGroupBadge(it.sessionId),
1414
2262
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "archv-id", title: it.sessionId, children: shortId(it.sessionId) })
1415
2263
  ] }),
1416
2264
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "archv-meta", children: [
@@ -1426,6 +2274,7 @@ function SessionPanel({ workspacesSvc }) {
1426
2274
  ] })
1427
2275
  ] }),
1428
2276
  groupByLineage && renderKids(it.sessionId, 0),
2277
+ groupByLineage && renderBranchGroup(it.sessionId),
1429
2278
  expanded && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-sheet", role: "region", "aria-label": "\u79FB\u52A8\u5230\u5DE5\u4F5C\u533A", children: [
1430
2279
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-sheet-head", children: [
1431
2280
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "mv-sheet-title", children: "\u79FB\u52A8\u5230\u5DE5\u4F5C\u533A" }),
@@ -1479,6 +2328,51 @@ function SessionPanel({ workspacesSvc }) {
1479
2328
  ] })
1480
2329
  ] })
1481
2330
  ] }),
2331
+ openTags === it.sessionId && (() => {
2332
+ const cur = Array.isArray(assignments[String(it.sessionId)]) ? assignments[String(it.sessionId)] : [];
2333
+ const locked = tagBusy !== null || !tagsReady;
2334
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-sheet", role: "region", "aria-label": "\u4F1A\u8BDD\u6807\u7B7E", children: [
2335
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-sheet-head", children: [
2336
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h3", { className: "mv-sheet-title", children: [
2337
+ "\u4F1A\u8BDD\u6807\u7B7E \xB7 ",
2338
+ it.title || shortId(it.sessionId)
2339
+ ] }),
2340
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "mv-sheet-close", "aria-label": "\u5173\u95ED", onClick: () => setOpenTags(null), children: "\xD7" })
2341
+ ] }),
2342
+ tagDefs.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtl-note", children: "\u8FD8\u6CA1\u6709\u6807\u7B7E\uFF0C\u5728\u4E0B\u65B9\u65B0\u5EFA\u7B2C\u4E00\u4E2A\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsm-taglist", children: tagDefs.map((t) => {
2343
+ const checked = cur.some((id) => String(id) === String(t.id));
2344
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "dsm-tagcheck", children: [
2345
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked, disabled: locked, onChange: () => toggleTagFor(it.sessionId, t.id) }),
2346
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-tagcheck-name", title: t.name, children: t.name })
2347
+ ] }, String(t.id));
2348
+ }) }),
2349
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-field", children: [
2350
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "mv-field-label", htmlFor: "dsm-tag-attach-" + String(it.sessionId), children: "\u65B0\u5EFA\u6807\u7B7E\u5E76\u8D34\u4E0A" }),
2351
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-browse-row", children: [
2352
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2353
+ "input",
2354
+ {
2355
+ id: "dsm-tag-attach-" + String(it.sessionId),
2356
+ type: "text",
2357
+ value: cardTagName,
2358
+ disabled: locked,
2359
+ maxLength: 24,
2360
+ placeholder: "\u6807\u7B7E\u540D\uFF08\u4E0D\u80FD\u542B\u659C\u6760\uFF0C\u6700\u957F 24 \u5B57\uFF09",
2361
+ onChange: (e) => setCardTagName(e.target.value),
2362
+ onKeyDown: (e) => {
2363
+ if (e.key === "Enter") {
2364
+ e.preventDefault();
2365
+ createTagAndAttach(it.sessionId);
2366
+ }
2367
+ }
2368
+ }
2369
+ ),
2370
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", disabled: locked || !cardTagName.trim(), title: locked && !tagsReady ? "\u6807\u7B7E\u6570\u636E\u672A\u80FD\u52A0\u8F7D\uFF0C\u6682\u65F6\u65E0\u6CD5\u65B0\u5EFA\u6807\u7B7E" : "\u65B0\u5EFA\u8FD9\u4E2A\u6807\u7B7E\u5E76\u7ACB\u5373\u8D34\u5230\u5F53\u524D\u4F1A\u8BDD", onClick: () => createTagAndAttach(it.sessionId), children: "\u8D34\u4E0A" })
2371
+ ] })
2372
+ ] }),
2373
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtl-note", children: "\u52FE\u9009\u5373\u65F6\u751F\u6548\uFF1B\u5355\u4E2A\u4F1A\u8BDD\u7684\u6807\u7B7E\u6570\u91CF\u4EE5\u670D\u52A1\u7AEF\u4E0A\u9650\u4E3A\u51C6\uFF0C\u8D85\u65F6\u4F1A\u63D0\u793A\u5E76\u56DE\u9000\u3002" })
2374
+ ] });
2375
+ })(),
1482
2376
  openDetails === it.sessionId && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtl-sheet", role: "region", "aria-label": "\u4F1A\u8BDD\u8BE6\u60C5", children: [
1483
2377
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtl-sheet-head", children: [
1484
2378
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "dtl-sheet-title", children: "\u4F1A\u8BDD\u8BE6\u60C5" }),
@@ -1560,7 +2454,7 @@ function SessionPanel({ workspacesSvc }) {
1560
2454
  ] }, i)) })
1561
2455
  ] }),
1562
2456
  d.lineage && (d.lineage.parentSessionId || d.lineage.children && d.lineage.children.length > 0 || d.lineage.subagents && d.lineage.subagents.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtl-sec", children: [
1563
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtl-sec-t", children: "\u8840\u7EDF" }),
2457
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtl-sec-t", children: "\u8840\u7F18" }),
1564
2458
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtl-paths", children: [
1565
2459
  d.lineage.parentSessionId && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1566
2460
  "\u7236\u4F1A\u8BDD: ",
@@ -1823,12 +2717,60 @@ var dsmTrashTick = 0;
1823
2717
  var dsmCapabilities = null;
1824
2718
  var dsmLineage = /* @__PURE__ */ new Map();
1825
2719
  var dsmWarmPending = false;
2720
+ var dsmRefinePending = false;
1826
2721
  var dsmTrashRetryTimer = null;
1827
2722
  var dsmWarmDoneHooks = /* @__PURE__ */ new Set();
1828
2723
  function dsmOnWarmDone(fn) {
1829
2724
  dsmWarmDoneHooks.add(fn);
1830
2725
  return () => dsmWarmDoneHooks.delete(fn);
1831
2726
  }
2727
+ var DSM_KEY_SEEN_NOTICES = "dsm-move-notices-seen-v1";
2728
+ var dsmSeenNotices = null;
2729
+ function dsmLoadSeenNotices() {
2730
+ if (dsmSeenNotices) return dsmSeenNotices;
2731
+ try {
2732
+ dsmSeenNotices = new Set(JSON.parse(localStorage.getItem(DSM_KEY_SEEN_NOTICES) || "[]").map(String));
2733
+ } catch (e) {
2734
+ dsmSeenNotices = /* @__PURE__ */ new Set();
2735
+ }
2736
+ return dsmSeenNotices;
2737
+ }
2738
+ function dsmSaveSeenNotices(set) {
2739
+ try {
2740
+ localStorage.setItem(DSM_KEY_SEEN_NOTICES, JSON.stringify([...set].slice(-200)));
2741
+ } catch (e) {
2742
+ }
2743
+ }
2744
+ var dsmNotifySink = null;
2745
+ var dsmNoticeHooks = /* @__PURE__ */ new Set();
2746
+ function dsmNotify(text, kind) {
2747
+ if (!text) return;
2748
+ if (dsmNotifySink && !sidebarAdapter.disabled) {
2749
+ try {
2750
+ dsmNotifySink(text, kind);
2751
+ return;
2752
+ } catch (e) {
2753
+ }
2754
+ }
2755
+ let done = false;
2756
+ try {
2757
+ for (const fn of dsmNoticeHooks) {
2758
+ fn(text, kind);
2759
+ done = true;
2760
+ }
2761
+ } catch (e) {
2762
+ }
2763
+ if (!done) {
2764
+ try {
2765
+ console.warn("[dsh-sessions-manager] " + text);
2766
+ } catch (e) {
2767
+ }
2768
+ }
2769
+ }
2770
+ function dsmOnNotice(fn) {
2771
+ dsmNoticeHooks.add(fn);
2772
+ return () => dsmNoticeHooks.delete(fn);
2773
+ }
1832
2774
  async function dsmLoadCapabilities() {
1833
2775
  try {
1834
2776
  dsmCapabilities = await postJSON("/archived-sessions/capabilities", {});
@@ -1853,14 +2795,26 @@ async function dsmLoadTrashIds() {
1853
2795
  for (const [id, info] of Object.entries(r && r.lineage || {})) {
1854
2796
  if (info && typeof info === "object") dsmLineage.set(String(id), info);
1855
2797
  }
1856
- const wasPending = dsmWarmPending;
2798
+ const wasBusy = dsmWarmPending || dsmRefinePending;
1857
2799
  dsmWarmPending = !!(r && r.warmPending);
1858
- if (wasPending && !dsmWarmPending) {
2800
+ dsmRefinePending = !!(r && r.refinePending);
2801
+ if (wasBusy && !(dsmWarmPending || dsmRefinePending)) {
1859
2802
  try {
1860
2803
  for (const fn of dsmWarmDoneHooks) fn();
1861
2804
  } catch (e) {
1862
2805
  }
1863
2806
  }
2807
+ {
2808
+ const plan = noticeToastPlan(r && r.moveNotices, dsmLoadSeenNotices(), 2);
2809
+ if (plan.ackIds.length) {
2810
+ const seen = dsmLoadSeenNotices();
2811
+ for (const id of plan.ackIds) seen.add(id);
2812
+ dsmSaveSeenNotices(seen);
2813
+ dsmNotify(plan.text, plan.kind);
2814
+ postJSON("/archived-sessions/pending-moves/notices/ack", { ids: plan.ackIds }).catch(() => {
2815
+ });
2816
+ }
2817
+ }
1864
2818
  if (dsmRepaintDots) dsmRepaintDots();
1865
2819
  } catch (e) {
1866
2820
  if (dsmTrashRetryTimer == null) {
@@ -2467,10 +3421,13 @@ function installSidebarStatusDots() {
2467
3421
  const t = document.createElement("div");
2468
3422
  t.className = "dsm-toast" + (kind === "err" ? " dsm-toast-err" : "");
2469
3423
  t.textContent = msg;
2470
- t.style.cssText = "position:fixed;left:50%;bottom:24px;transform:translateX(-50%);max-width:80%;padding:8px 14px;border-radius:8px;background:#2C2C2A;color:#F1EFE8;font-size:12px;line-height:1.5;z-index:9999";
3424
+ t.setAttribute("role", "status");
3425
+ t.style.cssText = "position:fixed;left:50%;bottom:24px;transform:translateX(-50%);max-width:80%;padding:8px 14px;border-radius:8px;background:#2C2C2A;color:#F1EFE8;font-size:12px;line-height:1.5;z-index:9999;cursor:pointer";
3426
+ t.addEventListener("click", () => t.remove());
2471
3427
  document.body.appendChild(t);
2472
- setTimeout(() => t.remove(), 4200);
3428
+ setTimeout(() => t.remove(), toastDurationFor(msg, kind));
2473
3429
  };
3430
+ dsmNotifySink = paintToast;
2474
3431
  const DSM_KEY_SUBS = "dsm-subs-open-v1";
2475
3432
  const dsmSubsOpen = /* @__PURE__ */ new Set();
2476
3433
  try {
@@ -2661,7 +3618,7 @@ function installSidebarStatusDots() {
2661
3618
  const FALLBACK_TICK_MS = 4e3;
2662
3619
  const tick = () => {
2663
3620
  if (typeof document !== "undefined" && document.hidden) return;
2664
- const cadence = dsmWarmPending ? 1 : 8;
3621
+ const cadence = dsmWarmPending || dsmRefinePending ? 1 : 8;
2665
3622
  if (++dsmTrashTick % cadence === 0) dsmLoadTrashIds();
2666
3623
  paint();
2667
3624
  };
@@ -2682,6 +3639,7 @@ function installSidebarStatusDots() {
2682
3639
  clearInterval(tickTimer);
2683
3640
  clearTimeout(coldSecondBeat);
2684
3641
  obs.disconnect();
3642
+ dsmNotifySink = null;
2685
3643
  dsmTeardownSidebarAug();
2686
3644
  };
2687
3645
  }
@@ -2714,7 +3672,7 @@ function installSidebarWorkspaceDrag() {
2714
3672
  el.setAttribute("role", "status");
2715
3673
  el.textContent = message;
2716
3674
  document.body.appendChild(el);
2717
- setTimeout(() => el.remove(), 2600);
3675
+ setTimeout(() => el.remove(), toastDurationFor(message));
2718
3676
  };
2719
3677
  const clearVisuals = () => {
2720
3678
  document.querySelectorAll(".dsm-drag-source,.dsm-drop-target").forEach((el) => el.classList.remove("dsm-drag-source", "dsm-drop-target"));