dsh-sessions-manager 3.5.4 → 3.6.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
@@ -85,6 +85,58 @@ function workspaceForNodes(nodes) {
85
85
  function starredOf(items) {
86
86
  return (items || []).filter((item) => item && item.starred);
87
87
  }
88
+ function foldSubagents(items, lineage) {
89
+ const list = items || [];
90
+ const table = lineage || {};
91
+ const kidsOf = /* @__PURE__ */ new Map();
92
+ const foldedIds = /* @__PURE__ */ new Set();
93
+ const byId = /* @__PURE__ */ new Map();
94
+ for (const item of list) byId.set(String(item.sessionId), item);
95
+ for (const item of list) {
96
+ const id = String(item.sessionId);
97
+ const info = table[id];
98
+ const parentId = info && info.origin === "subagent" && info.parentSession ? String(info.parentSession) : null;
99
+ if (!parentId || parentId === id || !byId.has(parentId)) continue;
100
+ if (!kidsOf.has(parentId)) kidsOf.set(parentId, []);
101
+ kidsOf.get(parentId).push(item);
102
+ foldedIds.add(id);
103
+ }
104
+ return {
105
+ topList: foldedIds.size ? list.filter((item) => !foldedIds.has(String(item.sessionId))) : list,
106
+ kidsOf,
107
+ foldedCount: foldedIds.size
108
+ };
109
+ }
110
+ function shortId(id) {
111
+ const raw = String(id == null ? "" : id);
112
+ const tail = raw.startsWith("session-") ? raw.slice(8) : raw;
113
+ if (!tail) return "";
114
+ return tail.length <= 10 ? tail : tail.slice(0, 8) + "\u2026";
115
+ }
116
+ function openSubagentToast(result, name, where) {
117
+ const label = name || "\u5B50\u4EE3\u7406";
118
+ if (result === "ok") {
119
+ return {
120
+ kind: "ok",
121
+ text: where === "sidebar" ? `\u5DF2\u6253\u5F00\u300C${label}\u300D` : `\u5DF2\u6253\u5F00\u300C${label}\u300D\u2014 \u5173\u6389\u8BBE\u7F6E\u5373\u53EF\u770B\u5230`
122
+ };
123
+ }
124
+ if (result === "no-service") {
125
+ return { kind: "err", text: "\u8FD9\u4E2A DSH \u7248\u672C\u6CA1\u6709\u5F00\u653E\u4F1A\u8BDD\u5207\u6362\u63A5\u53E3\uFF0C\u8BF7\u5728\u4FA7\u8FB9\u680F\u624B\u52A8\u5207\u6362" };
126
+ }
127
+ return {
128
+ kind: "err",
129
+ text: where === "sidebar" ? "\u6253\u4E0D\u5F00\u8FD9\u4E2A\u5B50\u4EE3\u7406\uFF1A\u8BF7\u9009\u4E2D\u5B83\u7684\u7236\u4F1A\u8BDD\uFF0C\u70B9\u6807\u9898\u680F\u7684\u300C/ N\u300D\u5B50\u4EE3\u7406\u76EE\u5F55" : "\u6253\u4E0D\u5F00\uFF1A\u8BF7\u5728\u4FA7\u8FB9\u680F\u9009\u4E2D\u5B83\u7684\u7236\u4F1A\u8BDD\uFF0C\u70B9\u6807\u9898\u680F\u7684\u300C/ N\u300D\u5B50\u4EE3\u7406\u76EE\u5F55"
130
+ };
131
+ }
132
+ var TOAST_MIN_MS = 2600;
133
+ var TOAST_MAX_MS = 11e3;
134
+ function toastDurationFor(text, kind) {
135
+ const chars = String(text == null ? "" : text).length;
136
+ const readingMs = Math.round(chars / 6 * 1e3 * 1.4);
137
+ const scaled = kind === "err" ? Math.round(readingMs * 1.25) : readingMs;
138
+ return Math.min(TOAST_MAX_MS, Math.max(TOAST_MIN_MS, scaled));
139
+ }
88
140
 
89
141
  // src/client/index.jsx
90
142
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -98,43 +150,75 @@ function loadPanelPrefs() {
98
150
  }
99
151
  }
100
152
  var CSS = `
101
- .archv{--dsm-radius-tag:4px;--dsm-radius-ctl:9px;--dsm-radius-sheet:10px;--dsm-radius-card:12px;display:flex;flex-direction:column;gap:4px;max-width:800px;padding:8px 2px 28px}
153
+ .archv{--dsm-radius-tag:9px;--dsm-radius-ctl:9px;--dsm-radius-sheet:10px;--dsm-radius-card:12px;display:flex;flex-direction:column;gap:4px;max-width:800px;padding:8px 2px 28px}
102
154
  .archv-head{display:flex;align-items:center;gap:10px;margin:0 0 2px}
103
155
  .archv-title{font-size:16px;font-weight:650;color:var(--dsw-alias-label-primary);letter-spacing:-0.01em;margin:0}
156
+ .archv-stamp{font-size:11px;font-weight:500;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-fill-secondary);border:1px solid var(--dsw-alias-border-l1);border-radius:999px;padding:2px 8px;white-space:nowrap;cursor:default}
104
157
  .archv-count{font-size:11px;font-weight:600;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-fill-subtle);border:1px solid var(--dsw-alias-border-l2);border-radius:999px;padding:1px 8px;flex:none}
105
158
  .archv-sub{font-size:12px;line-height:1.55;color:var(--dsw-alias-label-tertiary);margin:0 0 12px;max-width:64ch}
106
159
  .archv-err{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:9px 12px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-error-primary) 40%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 8%,transparent);border-radius:var(--dsm-radius-sheet);color:var(--dsw-alias-state-error-primary);font-size:12px;margin-bottom:10px}
107
160
  .archv-errretry{appearance:none;border:1px solid color-mix(in srgb,var(--dsw-alias-state-error-primary) 45%,transparent);background:transparent;color:inherit;border-radius:8px;padding:4px 10px;font-size:11px;cursor:pointer;flex:none}
108
- .sess-filter{display:flex;align-items:center;gap:6px;margin:0 0 12px}
161
+ .sess-fwrap{margin:0 0 12px}
162
+ .sess-filter{display:flex;align-items:center;gap:6px}
163
+ /* \u4F4E\u9891\u5206\u7C7B\uFF08\u5DF2\u6536\u85CF/\u7A7A\u767D/\u56DE\u6536\u7AD9\uFF09\u6A2A\u5411\u6536\u7EB3\uFF1A\u9ED8\u8BA4 max-width:0 \u9690\u85CF\uFF0C\u70B9\u6309\u7BAD\u5934
164
+ \uFF08sess-fmore-open\uFF09\u6216\u4F4E\u9891\u5206\u7C7B\u5904\u4E8E\u9009\u4E2D\u6001\uFF08sess-fsec-on\uFF09\u65F6\u5411\u53F3\u5C55\u5F00\uFF1B
165
+ max-width \u8FC7\u6E21\u505A\u6A2A\u5411\u6ED1\u51FA\u52A8\u753B\uFF0Coverflow \u88C1\u526A\u56DE\u7F29\u8FC7\u7A0B\u3002 */
166
+ .sess-fmore{display:inline-flex;align-items:center;gap:6px;max-width:0;opacity:0;overflow:hidden;white-space:nowrap;transform:translateX(-8px);visibility:hidden;transition:max-width .22s ease,opacity .18s ease,transform .22s ease,visibility 0s .22s}
167
+ .sess-fwrap.sess-fmore-open .sess-fmore,.sess-fwrap.sess-fsec-on .sess-fmore{max-width:30em;opacity:1;transform:none;visibility:visible;transition:max-width .22s ease,opacity .18s ease,transform .22s ease}
168
+ .sess-farrow{appearance:none;display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border:none;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;flex:none;padding:0;opacity:0;pointer-events:none;transition:opacity .18s ease,color .18s ease}
169
+ /* \u7BAD\u5934\u9ED8\u8BA4\u7D27\u8DDF\u4E09\u4E2A\u4E3B tab \u53F3\u8FB9\uFF08\u60AC\u505C\u7B5B\u9009\u884C\u624D\u6D6E\u73B0\uFF09\uFF1B\u5C55\u5F00\u7EC4\u6491\u5F00\u540E\u628A\u5B83\u63A8\u5230
170
+ \u884C\u5C3E\u3002\u65E0\u5916\u5708\u5E95\u8272\uFF0C\u53EA\u6E32\u67D3 svg \u672C\u4F53\u3002\u9690\u85CF\u6001\u7528 pointer-events:none \u6321\u4F4F
171
+ \u8BEF\u70B9\uFF1B\u5C55\u5F00\u6001\uFF08\u542B\u9009\u4E2D\u4F4E\u9891\u5206\u7C7B\u7684\u515C\u5E95\uFF09\u5E38\u9A7B\u2014\u2014\u6536\u8D77\u5165\u53E3\u4E0D\u80FD\u6D88\u5931\u3002 */
172
+ .sess-fwrap:hover .sess-farrow,.sess-fwrap.sess-fmore-open .sess-farrow,.sess-fwrap.sess-fsec-on .sess-farrow{opacity:1;pointer-events:auto}
173
+ .sess-farrow:hover{color:var(--dsw-alias-label-primary)}
174
+ .sess-farrow svg{display:block;transition:transform .22s ease}
175
+ .sess-fwrap.sess-fmore-open .sess-farrow svg,.sess-fwrap.sess-fsec-on .sess-farrow svg{transform:rotate(180deg)}
109
176
  .sess-fbtn{appearance:none;min-height:30px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-fill-subtle);color:var(--dsw-alias-label-secondary);border-radius:999px;font-size:12px;font-weight:500;cursor:pointer}
110
177
  .sess-fbtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
111
178
  .sess-fbtn-on{background:var(--dsw-alias-interactive-bg-active);color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l3)}
112
- .sess-tools{display:grid;grid-template-columns:minmax(180px,1fr) minmax(140px,.7fr) minmax(130px,.55fr);gap:8px;margin:0 0 8px}
179
+ .sess-tools{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:8px;margin:0 0 8px}
180
+ .sess-tools-4{grid-template-columns:repeat(auto-fit,minmax(132px,1fr))}
181
+ .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)}
182
+ .dsm-kid .dsm-kids{margin-left:8px;margin-top:6px}
183
+ .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}
184
+ .dsm-kids-toggle:hover{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 18%,transparent)}
185
+ .dsm-kid{display:flex;flex-direction:column;gap:6px;min-width:0}
186
+ .dsm-kid-row{display:flex;align-items:center;gap:8px;min-width:0}
187
+ .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}
188
+ .dsm-kid-meta{font-size:11px;color:var(--dsw-alias-label-tertiary);flex:none;white-space:nowrap}
189
+ .dsm-kid-acts{display:flex;align-items:center;gap:6px;flex:none;margin-left:auto}
190
+ .dsm-kid-acts .archv-btn{min-height:26px;padding:0 9px;font-size:11px}
113
191
  .sess-field{display:flex;flex-direction:column;gap:5px;min-width:0}
114
192
  .sess-field label{font-size:11px;font-weight:600;color:var(--dsw-alias-label-secondary)}
115
193
  .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}
116
- .sess-results{font-size:11px;color:var(--dsw-alias-label-tertiary);margin:0 0 4px}
194
+ .sess-results{display:flex;flex-direction:column;gap:1px;font-size:11px;color:var(--dsw-alias-label-tertiary);margin:0 0 4px}
117
195
  .archv button:focus-visible,.archv input:focus-visible,.archv select:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}
118
196
  .sess-batch{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:8px 2px 4px;margin-bottom:4px}
197
+ /* 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 */
198
+ .sess-batch-pin{position:sticky;top:0;z-index:30;margin:0 -2px 4px;padding:8px 4px 6px;background:var(--dsw-alias-bg-layer-1);border-bottom:1px solid var(--dsw-alias-border-l2);box-shadow:0 6px 14px rgb(0 0 0/.08)}
119
199
  .sess-btntext{font-size:12px;color:var(--dsw-alias-label-tertiary);flex:none}
120
200
  .archv-list{display:flex;flex-direction:column;gap:8px}
121
201
  .archv-card{display:flex;flex-direction:column;align-items:stretch;gap:0;padding:12px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsm-radius-card);background:var(--dsw-alias-fill-elevated);transition:border-color .15s ease,background-color .15s ease}
122
202
  .archv-card:hover{border-color:var(--dsw-alias-border-l4)}
123
203
  .archv-card-exp{border-color:var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-1)}
124
204
  .archv-row{display:flex;align-items:center;gap:14px;width:100%;min-width:0}
125
- .archv-main{min-width:0;display:flex;flex-direction:column;gap:4px}
126
- .archv-name{font-size:13px;font-weight:550;color:var(--dsw-alias-label-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
127
- .archv-meta{display:flex;align-items:center;gap:7px;flex-wrap:wrap;min-width:0}
128
- .archv-wtag{display:inline-flex;align-items:center;gap:4px;max-width:100%;font-size:11px;font-weight:500;line-height:1;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-fill-subtle);border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsm-radius-tag);padding:3px 8px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
205
+ .archv-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:5px}
206
+ .archv-titlerow{display:flex;align-items:center;gap:8px;min-width:0}
207
+ .archv-name{font-size:13px;font-weight:550;color:var(--dsw-alias-label-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;flex:0 1 auto}
208
+ .archv-meta{display:flex;align-items:center;gap:7px;flex-wrap:nowrap;min-width:0;overflow:hidden}
209
+ .archv-wtag{display:inline-flex;align-items:center;gap:4px;min-width:0;flex:0 1 auto;font-size:11px;font-weight:500;line-height:1;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-fill-subtle);border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsm-radius-tag);padding:3px 8px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
129
210
  .archv-wgone{border-style:dashed;color:var(--dsw-alias-state-error-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 55%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 7%,transparent)}
130
211
  .archv-active{color:var(--dsw-alias-state-success-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-success-primary) 45%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 8%,transparent)}
131
212
  .archv-date{font-size:11px;color:var(--dsw-alias-label-tertiary);white-space:nowrap;flex:none}
132
- .archv-id{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10.5px;color:var(--dsw-alias-label-tertiary);flex:none}
213
+ .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}
214
+ .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}
215
+ .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}
133
216
  .archv-dot{color:var(--dsw-alias-border-l3);flex:none}
134
217
  .archv-check{width:15px;height:15px;accent-color:var(--dsw-alias-state-business-primary);flex:none;cursor:pointer}
135
- .archv-star{appearance:none;width:24px;height:24px;flex:none;display:inline-flex;align-items:center;justify-content:center;border:none;background:0 0;border-radius:7px;cursor:pointer;color:var(--dsw-alias-label-tertiary);transition:color .15s ease,background-color .15s ease}
136
- .archv-star:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}
137
- .archv-star svg{fill:none;stroke:currentColor;stroke-width:1.5;stroke-linejoin:round}
218
+ .archv-star{appearance:none;width:22px;height:22px;flex:none;display:inline-flex;align-items:center;justify-content:center;border:none;background:0 0;padding:0;line-height:0;color:var(--dsw-alias-label-tertiary);cursor:pointer;border-radius:50%;transition:color .15s ease,transform .12s ease}
219
+ .archv-star:hover{background:0 0;color:var(--dsw-alias-label-secondary);transform:scale(1.12)}
220
+ .archv-star:active{transform:scale(.92)}
221
+ .archv-star svg{fill:none;stroke:currentColor;stroke-width:1.5;stroke-linejoin:round;stroke-linecap:round;display:block}
138
222
  .archv-star-on,.archv-star-on:hover{color:var(--dsw-alias-state-business-primary)}
139
223
  .archv-star-on svg{fill:currentColor}
140
224
  .archv-body{flex:1;min-width:0;display:flex;align-items:center;gap:12px}
@@ -150,13 +234,16 @@ var CSS = `
150
234
  .archv-skel{display:flex;flex-direction:column;gap:8px}
151
235
  .archv-skel-card{height:58px;border-radius:var(--dsm-radius-card);background:var(--dsw-alias-fill-subtle);position:relative;overflow:hidden}
152
236
  .archv-skel-card::after{content:'';position:absolute;inset:0;transform:translateX(-100%);background:linear-gradient(90deg,transparent,color-mix(in srgb,var(--dsw-alias-fill-elevated) 75%,transparent),transparent);animation:archv-shimmer 1.4s infinite}
153
- .archv-status{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);z-index:60;background:var(--dsw-alias-fill-elevated);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);padding:9px 16px;border-radius:999px;font-size:12px;box-shadow:0 8px 24px rgb(0 0 0/.18);display:flex;align-items:center;gap:8px;animation:archv-pop .18s ease-out;max-width:min(90vw,420px)}
237
+ .archv-status{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);z-index:2147483601;background:linear-gradient(var(--dsw-alias-fill-elevated),var(--dsw-alias-fill-elevated)),var(--dsw-alias-bg-layer-1,Canvas);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);padding:9px 16px;border-radius:999px;font-size:12px;line-height:1.5;text-align:center;box-shadow:0 8px 24px rgb(0 0 0/.25);display:flex;align-items:center;gap:8px;animation:archv-pop .18s ease-out;max-width:min(92vw,480px);cursor:pointer}
238
+ .archv-status-long{border-radius:14px;text-align:left}
154
239
  .archv-spin{width:12px;height:12px;border:2px solid color-mix(in srgb,var(--dsw-alias-label-secondary) 35%,transparent);border-top-color:var(--dsw-alias-label-secondary);border-radius:50%;animation:archv-rot .8s linear infinite;flex:none}
155
240
  @keyframes archv-shimmer{100%{transform:translateX(100%)}}
156
241
  @keyframes archv-rot{to{transform:rotate(360deg)}}
157
242
  @keyframes archv-pop{from{opacity:0;transform:translateX(-50%) translateY(10px)}}
158
- @media (prefers-reduced-motion:reduce){.archv-skel-card::after{animation:none}.archv-card,.archv-btn{transition:none}.archv-status,.archv-spin{animation:none}}
159
- @media (max-width:640px){.archv-card{flex-direction:column;align-items:stretch;gap:10px}.archv-actions{justify-content:flex-end}.sess-tools{grid-template-columns:1fr}.sess-fbtn,.archv-btn{min-height:40px}}
243
+ /* \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 */
244
+ .archv-status-err{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary);font-weight:500}
245
+ @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}}
246
+ @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}}
160
247
  .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)}
161
248
  .mv-sheet-head{display:flex;align-items:center;justify-content:space-between;gap:10px}
162
249
  .mv-sheet-title{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);margin:0}
@@ -233,6 +320,7 @@ function pathName(p) {
233
320
  const parts = String(p).replace(/\\+$/, "").split(/[/\\]/);
234
321
  return parts[parts.length - 1] || p;
235
322
  }
323
+ var BUSY_CODE = "DSM_SESSION_BUSY";
236
324
  async function postJSON(path, body) {
237
325
  const res = await fetch(path, {
238
326
  method: "POST",
@@ -240,9 +328,55 @@ async function postJSON(path, body) {
240
328
  body: JSON.stringify(body || {})
241
329
  });
242
330
  const data = await res.json().catch(() => null);
243
- if (!res.ok || !data) throw new Error(data && (data.error || data.message) || `request failed (${res.status})`);
331
+ if (!res.ok || !data) {
332
+ const error = new Error(data && (data.error || data.message) || `request failed (${res.status})`);
333
+ if (data && data.code) error.code = data.code;
334
+ throw error;
335
+ }
244
336
  return data;
245
337
  }
338
+ var dsmClientCtx = null;
339
+ function dsmSessionsService() {
340
+ try {
341
+ return dsmClientCtx && typeof dsmClientCtx.get === "function" ? dsmClientCtx.get("sessions") : null;
342
+ } catch (e) {
343
+ return null;
344
+ }
345
+ }
346
+ function dsmCurrentSessionId(svc) {
347
+ try {
348
+ const snap = svc && svc.list && typeof svc.list.getSnapshot === "function" ? svc.list.getSnapshot() : null;
349
+ return snap && snap.current != null ? String(snap.current) : null;
350
+ } catch (e) {
351
+ return null;
352
+ }
353
+ }
354
+ async function dsmOpenSessionById(childId, directParentId) {
355
+ const sid = String(childId || "");
356
+ if (!sid) return "failed";
357
+ const svc = dsmSessionsService();
358
+ if (!svc) return "no-service";
359
+ const parent = directParentId ? String(directParentId) : null;
360
+ if (typeof svc.open === "function") {
361
+ try {
362
+ svc.open(sid);
363
+ await new Promise((r) => setTimeout(r, 350));
364
+ if (dsmCurrentSessionId(svc) === sid) return "ok";
365
+ } catch (e) {
366
+ }
367
+ }
368
+ if (parent && typeof svc.openSubagent === "function") {
369
+ try {
370
+ if (typeof svc.refreshSubagents === "function") await svc.refreshSubagents(parent);
371
+ svc.openSubagent({ parentSessionId: parent, childSessionId: sid, mode: "continuable" });
372
+ await new Promise((r) => setTimeout(r, 350));
373
+ if (dsmCurrentSessionId(svc) === sid) return "ok";
374
+ return "ok";
375
+ } catch (e) {
376
+ }
377
+ }
378
+ return "failed";
379
+ }
246
380
  var NAV_CSS = `
247
381
  [data-dsh-nav-sessions] > svg:first-child { display: none; }
248
382
  [data-dsh-nav-sessions]::before {
@@ -254,8 +388,11 @@ var NAV_CSS = `
254
388
  function markSettingsNav() {
255
389
  const LABEL = "\u4F1A\u8BDD\u7BA1\u7406";
256
390
  let disposed = false;
257
- const sync = () => {
391
+ let frame = 0;
392
+ const run = () => {
393
+ frame = 0;
258
394
  if (disposed) return;
395
+ if (!document.querySelector('[role="dialog"]')) return;
259
396
  const buttons = document.querySelectorAll('[role="dialog"] nav button');
260
397
  for (const b of buttons) {
261
398
  const t = (b.textContent || "").trim();
@@ -263,11 +400,16 @@ function markSettingsNav() {
263
400
  else b.removeAttribute("data-dsh-nav-sessions");
264
401
  }
265
402
  };
266
- sync();
403
+ const sync = () => {
404
+ if (!frame && !disposed) frame = requestAnimationFrame(run);
405
+ };
406
+ run();
267
407
  const obs = new MutationObserver(sync);
268
- obs.observe(document.body, { childList: true, subtree: true, characterData: true });
408
+ obs.observe(document.body, { childList: true, subtree: true });
269
409
  return () => {
270
410
  disposed = true;
411
+ if (frame) cancelAnimationFrame(frame);
412
+ frame = 0;
271
413
  obs.disconnect();
272
414
  document.querySelectorAll("[data-dsh-nav-sessions]").forEach((e) => e.removeAttribute("data-dsh-nav-sessions"));
273
415
  };
@@ -287,19 +429,22 @@ function SessionPanel({ workspacesSvc }) {
287
429
  const [workspaces, setWorkspaces] = (0, import_react.useState)([]);
288
430
  const [capabilities, setCapabilities] = (0, import_react.useState)(null);
289
431
  const initialPrefs = (0, import_react.useRef)(loadPanelPrefs()).current;
290
- const [filter, setFilter] = (0, import_react.useState)(() => ["all", "active", "archived", "starred", "trash"].includes(initialPrefs.filter) ? initialPrefs.filter : "all");
432
+ const [filter, setFilter] = (0, import_react.useState)(() => ["all", "active", "archived", "starred", "empty", "trash"].includes(initialPrefs.filter) ? initialPrefs.filter : "all");
433
+ const [moreOpen, setMoreOpen] = (0, import_react.useState)(() => ["starred", "empty", "trash"].includes(initialPrefs.filter));
291
434
  const [query, setQuery] = (0, import_react.useState)("");
292
435
  const [workspaceFilter, setWorkspaceFilter] = (0, import_react.useState)(() => initialPrefs.workspaceFilter || "all");
293
436
  const [sortBy, setSortBy] = (0, import_react.useState)(() => ["newest", "oldest", "title"].includes(initialPrefs.sortBy) ? initialPrefs.sortBy : "newest");
294
437
  const [selected, setSelected] = (0, import_react.useState)({});
295
438
  const [delTarget, setDelTarget] = (0, import_react.useState)(null);
296
439
  const [confirmBatch, setConfirmBatch] = (0, import_react.useState)(false);
440
+ const [batchMoveOpen, setBatchMoveOpen] = (0, import_react.useState)(false);
297
441
  const [busy, setBusy] = (0, import_react.useState)(null);
298
442
  const [openMove, setOpenMove] = (0, import_react.useState)(null);
299
443
  const [moveMode, setMoveMode] = (0, import_react.useState)("existing");
300
444
  const [targetWs, setTargetWs] = (0, import_react.useState)("");
301
445
  const [newPath, setNewPath] = (0, import_react.useState)("");
302
446
  const [error, setError] = (0, import_react.useState)(null);
447
+ const [retry, setRetry] = (0, import_react.useState)(null);
303
448
  const [toast, setToast] = (0, import_react.useState)(null);
304
449
  const [picking, setPicking] = (0, import_react.useState)(false);
305
450
  const [trash, setTrash] = (0, import_react.useState)([]);
@@ -325,10 +470,15 @@ function SessionPanel({ workspacesSvc }) {
325
470
  const storageDirty = (0, import_react.useRef)(false);
326
471
  const menuRef = (0, import_react.useRef)(null);
327
472
  const dialogRef = (0, import_react.useRef)(null);
328
- const showToast = (msg) => {
473
+ const [lineage, setLineage] = (0, import_react.useState)({});
474
+ const [openKids, setOpenKids] = (0, import_react.useState)({});
475
+ const [groupByLineage, setGroupByLineage] = (0, import_react.useState)(() => initialPrefs.groupByLineage !== false);
476
+ const loadLineage = () => postJSON("/archived-sessions/sidebar-state", {}).then((r) => setLineage(r && r.lineage || {})).catch(() => {
477
+ });
478
+ const showToast = (msg, kind) => {
329
479
  if (timer.current) clearTimeout(timer.current);
330
- setToast(msg);
331
- timer.current = setTimeout(() => setToast(null), 2400);
480
+ setToast({ msg, kind: kind === "err" ? "err" : "ok" });
481
+ timer.current = setTimeout(() => setToast(null), toastDurationFor(msg, kind));
332
482
  };
333
483
  const refresh = () => {
334
484
  setError(null);
@@ -342,9 +492,11 @@ function SessionPanel({ workspacesSvc }) {
342
492
  setSessions(visible);
343
493
  setWorkspaces(works.items || []);
344
494
  setCapabilities(caps || null);
495
+ if (caps && caps.buildStamp) console.info("[dsh-sessions-manager] host build:", caps.buildStamp);
345
496
  setSelected({});
346
497
  setDelTarget(null);
347
498
  setConfirmBatch(false);
499
+ loadLineage();
348
500
  if (!targetWs && works.items && works.items.length) setTargetWs(works.items[0].workspaceId);
349
501
  loadTrash();
350
502
  if (storageOpen) loadStorage();
@@ -359,10 +511,10 @@ function SessionPanel({ workspacesSvc }) {
359
511
  }, []);
360
512
  (0, import_react.useEffect)(() => {
361
513
  try {
362
- localStorage.setItem(PANEL_PREFS_KEY, JSON.stringify({ filter, workspaceFilter, sortBy }));
514
+ localStorage.setItem(PANEL_PREFS_KEY, JSON.stringify({ filter, workspaceFilter, sortBy, groupByLineage }));
363
515
  } catch (e) {
364
516
  }
365
- }, [filter, workspaceFilter, sortBy]);
517
+ }, [filter, workspaceFilter, sortBy, groupByLineage]);
366
518
  (0, import_react.useEffect)(() => {
367
519
  loadAutoArchive();
368
520
  }, []);
@@ -448,9 +600,16 @@ function SessionPanel({ workspacesSvc }) {
448
600
  const archivedList = sessions ? sessions.filter((x) => x.archived) : [];
449
601
  const activeList = sessions ? sessions.filter((x) => !x.archived) : [];
450
602
  const starredList = starredOf(sessions);
603
+ const emptyList = (0, import_react.useMemo)(() => {
604
+ if (!sessions) return [];
605
+ return sessions.filter((x) => {
606
+ const li = lineage[String(x.sessionId)];
607
+ return !!(li && li.empty);
608
+ });
609
+ }, [sessions, lineage]);
451
610
  const list = (0, import_react.useMemo)(() => {
452
611
  if (filter === "trash") return [];
453
- const base = filter === "archived" ? archivedList : filter === "active" ? activeList : filter === "starred" ? starredList : sessions || [];
612
+ const base = filter === "archived" ? archivedList : filter === "active" ? activeList : filter === "starred" ? starredList : filter === "empty" ? emptyList : sessions || [];
454
613
  const needle = query.trim().toLocaleLowerCase();
455
614
  const filtered = base.filter((item) => {
456
615
  if (workspaceFilter !== "all" && (item.workspacePath || "") !== workspaceFilter) return false;
@@ -462,9 +621,72 @@ function SessionPanel({ workspacesSvc }) {
462
621
  if (sortBy === "title") return String(a.title || "").localeCompare(String(b.title || ""), "zh-CN");
463
622
  return Number(b.createdAt || 0) - Number(a.createdAt || 0);
464
623
  });
465
- }, [sessions, filter, query, workspaceFilter, sortBy]);
624
+ }, [sessions, filter, query, workspaceFilter, sortBy, emptyList, starredList]);
466
625
  const selIds = Object.keys(selected).filter((k) => selected[k]);
467
626
  const showSessionList = filter !== "trash";
627
+ const { topList, kidsOf, foldedCount } = (0, import_react.useMemo)(() => {
628
+ if (!groupByLineage) return { topList: list, kidsOf: /* @__PURE__ */ new Map(), foldedCount: 0 };
629
+ return foldSubagents(list, lineage);
630
+ }, [list, lineage, groupByLineage]);
631
+ const kidsBadge = (sessionId) => {
632
+ if (!groupByLineage) return null;
633
+ const kids = kidsOf.get(String(sessionId)) || [];
634
+ if (!kids.length) return null;
635
+ const open = !!openKids[sessionId];
636
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
637
+ "button",
638
+ {
639
+ type: "button",
640
+ className: "dsm-kids-toggle",
641
+ "aria-expanded": open,
642
+ title: (open ? "\u6536\u8D77" : "\u5C55\u5F00") + " " + kids.length + " \u4E2A\u5B50\u4EE3\u7406\u4F1A\u8BDD",
643
+ onClick: () => setOpenKids((s) => ({ ...s, [sessionId]: !s[sessionId] })),
644
+ children: [
645
+ open ? "\u25BE" : "\u25B8",
646
+ " ",
647
+ kids.length,
648
+ " \u5B50\u4EE3\u7406"
649
+ ]
650
+ }
651
+ );
652
+ };
653
+ const sessionsById = (0, import_react.useMemo)(() => new Map((sessions || []).map((x) => [String(x.sessionId), x])), [sessions]);
654
+ const branchBadge = (sessionId) => {
655
+ const li = lineage[String(sessionId)];
656
+ if (!li || li.origin === "subagent" || !li.parentSession) return null;
657
+ const parent = sessionsById.get(String(li.parentSession));
658
+ const label = parent && parent.title ? `\u5206\u652F\u4E8E\uFF1A${parent.title}` : `\u5206\u652F\u4F1A\u8BDD\uFF08\u6765\u6E90\uFF1A${shortId(li.parentSession)}\uFF09`;
659
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-branch-chip", title: label, children: "\u2442 \u5206\u652F" });
660
+ };
661
+ const emptyBadge = (sessionId) => {
662
+ const li = lineage[String(sessionId)];
663
+ if (!li || !li.empty) return null;
664
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-empty-chip", title: "\u65E0\u5185\u5BB9\u7684\u7A7A\u767D\u4F1A\u8BDD", children: "\u7A7A\u767D" });
665
+ };
666
+ const renderKids = (parentId, depth) => {
667
+ const kids = kidsOf.get(String(parentId)) || [];
668
+ if (!kids.length || !openKids[parentId]) return null;
669
+ 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: [
670
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsm-kid-row", children: [
671
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-kid-name", title: k.title || k.sessionId, children: k.title || "(\u65E0\u6807\u9898)" }),
672
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsm-kid-meta", children: [
673
+ k.archived ? "\u5DF2\u5F52\u6863" : "\u6D3B\u52A8",
674
+ fmtDate(k.createdAt) ? ` \xB7 ${fmtDate(k.createdAt)}` : ""
675
+ ] }),
676
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsm-kid-acts", children: [
677
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", disabled: busy !== null, title: "\u5207\u6362\u5230\u8FD9\u4E2A\u5B50\u4EE3\u7406\u4F1A\u8BDD\uFF08\u8BBE\u7F6E\u9762\u677F\u6321\u7740\u4F1A\u8BDD\u533A\uFF0C\u5173\u6389\u5373\u53EF\u770B\u5230\uFF09", onClick: async () => {
678
+ const name = k.title || String(k.sessionId).slice(0, 8) + "\u2026";
679
+ const res = await dsmOpenSessionById(k.sessionId, parentId);
680
+ const msg = openSubagentToast(res, name, "panel");
681
+ showToast(msg.text, msg.kind);
682
+ }, children: "\u6253\u5F00" }),
683
+ /* @__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" }),
684
+ /* @__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" })
685
+ ] })
686
+ ] }),
687
+ (depth || 0) < 4 && renderKids(k.sessionId, (depth || 0) + 1)
688
+ ] }, k.sessionId)) });
689
+ };
468
690
  const toggle = (id) => setSelected((s) => ({ ...s, [id]: !s[id] }));
469
691
  const clearSel = () => setSelected({});
470
692
  const selectAll = () => {
@@ -647,11 +869,20 @@ function SessionPanel({ workspacesSvc }) {
647
869
  setOpenMove(null);
648
870
  setMoveMode("existing");
649
871
  setNewPath("");
872
+ if (r && r.queued) {
873
+ showToast(r.notes && r.notes.length ? r.notes.join(" ") : "\u4F1A\u8BDD\u6B63\u88AB DSH \u6253\u5F00\uFF0C\u5DF2\u6392\u961F\u5F85\u79FB\u52A8\u3002", "err");
874
+ setRetry(null);
875
+ refresh();
876
+ return;
877
+ }
650
878
  showToast(`\u5DF2\u628A\u300C${it.title || it.sessionId}\u300D\u79FB\u5230 ${r.workspaceTitle || targetPath}`);
879
+ if (Array.isArray(r.notes) && r.notes.length > 0) showToast(r.notes.join(" "), "err");
651
880
  setSessions((s) => s && s.map((x) => x.sessionId === it.sessionId ? { ...x, workspacePath: r.workspacePath || targetPath, workspaceTitle: r.workspaceTitle || targetPath } : x));
881
+ setRetry(null);
652
882
  }).catch((e) => {
653
883
  setBusy(null);
654
884
  setError(String(e && e.message || e));
885
+ setRetry(e && e.code === BUSY_CODE ? { run: () => doMove(it) } : null);
655
886
  });
656
887
  };
657
888
  const doBatch = (action) => {
@@ -675,6 +906,51 @@ function SessionPanel({ workspacesSvc }) {
675
906
  setError(String(e && e.message || e));
676
907
  });
677
908
  };
909
+ const doBatchMove = (retryIds) => {
910
+ const ids = Array.isArray(retryIds) && retryIds.length ? retryIds : selIds;
911
+ if (!ids.length || busy) return;
912
+ if (!canMove.available) {
913
+ setError(canMove.reason || "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u8DE8\u5DE5\u4F5C\u533A\u79FB\u52A8");
914
+ return;
915
+ }
916
+ const targetPath = moveMode === "new" ? newPath.trim() : wsPath(targetWs);
917
+ if (!targetPath) {
918
+ setError("\u8BF7\u9009\u62E9\u5DF2\u6709\u5DE5\u4F5C\u533A\u6216\u8F93\u5165\u65B0\u7684\u76EE\u6807\u76EE\u5F55\u8DEF\u5F84");
919
+ return;
920
+ }
921
+ setBusy("__batch__");
922
+ setError(null);
923
+ setRetry(null);
924
+ postJSON("/archived-sessions/move-many", { sessionIds: ids, targetPath }).then((r) => {
925
+ setBusy(null);
926
+ setBatchMoveOpen(false);
927
+ setMoveMode("existing");
928
+ setNewPath("");
929
+ const moved = r && r.moved || 0;
930
+ const failed = r && Array.isArray(r.failed) ? r.failed : [];
931
+ const queued = r && Array.isArray(r.queued) ? r.queued : [];
932
+ if (queued.length) {
933
+ showToast(`${queued.length} \u4E2A\u4F1A\u8BDD\u5DF2\u6392\u961F\uFF08\u6B63\u88AB DSH \u6253\u5F00\uFF09\uFF0C\u91CD\u542F DSH \u540E\u81EA\u52A8\u5B8C\u6210\uFF1B\u8BF7\u5148\u522B\u6253\u5F00\u5B83\u4EEC\u3002`, "err");
934
+ }
935
+ if (failed.length) {
936
+ const firstReason = failed[0] && failed[0].error ? `\uFF0C\u9996\u4E2A\u539F\u56E0\uFF1A${failed[0].error.split("\n")[0]}` : "";
937
+ setError(`\u5DF2\u79FB\u52A8 ${moved} \u4E2A\u4F1A\u8BDD\uFF0C${failed.length} \u4E2A\u5931\u8D25\uFF08${failed.map((f) => shortId(f.sessionId)).join("\u3001")}\uFF09${firstReason}`);
938
+ const busyIds = failed.filter((f) => f.code === BUSY_CODE).map((f) => f.sessionId);
939
+ setRetry(busyIds.length ? { run: () => doBatchMove(busyIds) } : null);
940
+ } else if (moved > 0) {
941
+ showToast(`\u5DF2\u628A ${moved} \u4E2A\u4F1A\u8BDD\u79FB\u5230 ${targetPath}`);
942
+ setRetry(null);
943
+ } else {
944
+ setRetry(null);
945
+ }
946
+ clearSel();
947
+ refresh();
948
+ }).catch((e) => {
949
+ setBusy(null);
950
+ setError(String(e && e.message || e));
951
+ setRetry(e && e.code === BUSY_CODE ? { run: () => doBatchMove(ids) } : null);
952
+ });
953
+ };
678
954
  const openMoveFor = (it) => {
679
955
  if (!canMove.available) {
680
956
  setError(canMove.reason || "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u8DE8\u5DE5\u4F5C\u533A\u79FB\u52A8");
@@ -787,15 +1063,31 @@ function SessionPanel({ workspacesSvc }) {
787
1063
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: CSS }),
788
1064
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "archv-head", children: [
789
1065
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "archv-title", children: "\u4F1A\u8BDD\u7BA1\u7406" }),
1066
+ capabilities && capabilities.buildStamp ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "archv-stamp", title: capabilities.buildStamp, onClick: () => navigator.clipboard && navigator.clipboard.writeText(capabilities.buildStamp).catch(() => {
1067
+ }), children: capabilities.buildStamp }) : null,
790
1068
  sessions !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "archv-count", "aria-label": `${sessions.length} \u4E2A\u4F1A\u8BDD`, children: sessions.length })
791
1069
  ] }),
792
1070
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "archv-sub", children: "\u7EDF\u4E00\u7BA1\u7406\u5168\u90E8\u4F1A\u8BDD\uFF1A\u5F52\u6863 / \u6062\u590D / \u79FB\u52A8\u5230\u5176\u4ED6\u5DE5\u4F5C\u533A / \u4F1A\u8BDD\u8BE6\u60C5 / \u6279\u91CF\u64CD\u4F5C\uFF0C\u5220\u9664\u4F1A\u5148\u8FDB\u5165\u56DE\u6536\u7AD9\uFF0C\u53EF\u5728\u56DE\u6536\u7AD9\u5185\u6062\u590D\u6216\u5F7B\u5E95\u6E05\u7406\u3002" }),
793
1071
  error && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "archv-err", role: "alert", children: [
794
1072
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: error }),
795
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-errretry", onClick: refresh, children: "\u91CD\u8BD5" })
1073
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1074
+ "button",
1075
+ {
1076
+ type: "button",
1077
+ className: "archv-errretry",
1078
+ title: retry ? "\u628A\u521A\u624D\u5931\u8D25\u7684\u64CD\u4F5C\u539F\u6837\u518D\u505A\u4E00\u6B21\uFF08\u4F8B\u5982\u5148\u5207\u8D70\u88AB\u5360\u7528\u7684\u4F1A\u8BDD\u540E\u91CD\u8BD5\uFF09" : "\u91CD\u65B0\u52A0\u8F7D\u4F1A\u8BDD\u5217\u8868",
1079
+ onClick: () => {
1080
+ const run = retry && retry.run;
1081
+ setRetry(null);
1082
+ if (run) run();
1083
+ else refresh();
1084
+ },
1085
+ children: "\u91CD\u8BD5"
1086
+ }
1087
+ )
796
1088
  ] }),
797
1089
  sessions === null ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "archv-skel", "aria-label": "\u52A0\u8F7D\u4E2D", children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "archv-skel-card" }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
798
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-filter", role: "tablist", "aria-label": "\u4F1A\u8BDD\u7B5B\u9009", children: [
1090
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "sess-fwrap" + (moreOpen ? " sess-fmore-open" : "") + (filter === "starred" || filter === "empty" || filter === "trash" ? " sess-fsec-on" : ""), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-filter", role: "tablist", "aria-label": "\u4F1A\u8BDD\u7B5B\u9009", children: [
799
1091
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "tab", "aria-selected": filter === "all", className: "sess-fbtn" + (filter === "all" ? " sess-fbtn-on" : ""), onClick: () => {
800
1092
  setFilter("all");
801
1093
  clearSel();
@@ -823,26 +1115,46 @@ function SessionPanel({ workspacesSvc }) {
823
1115
  archivedList.length,
824
1116
  ")"
825
1117
  ] }),
826
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "tab", "aria-selected": filter === "starred", className: "sess-fbtn" + (filter === "starred" ? " sess-fbtn-on" : ""), onClick: () => {
827
- setFilter("starred");
828
- clearSel();
829
- setConfirmBatch(false);
830
- }, children: [
831
- "\u5DF2\u6536\u85CF (",
832
- starredList.length,
833
- ")"
1118
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "sess-fmore", role: "tablist", "aria-label": "\u66F4\u591A\u4F1A\u8BDD\u7B5B\u9009", children: [
1119
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "tab", "aria-selected": filter === "starred", className: "sess-fbtn" + (filter === "starred" ? " sess-fbtn-on" : ""), onClick: () => {
1120
+ setFilter("starred");
1121
+ clearSel();
1122
+ setConfirmBatch(false);
1123
+ }, children: [
1124
+ "\u5DF2\u6536\u85CF (",
1125
+ starredList.length,
1126
+ ")"
1127
+ ] }),
1128
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "tab", "aria-selected": filter === "empty", className: "sess-fbtn" + (filter === "empty" ? " sess-fbtn-on" : ""), onClick: () => {
1129
+ setFilter("empty");
1130
+ clearSel();
1131
+ setConfirmBatch(false);
1132
+ }, children: [
1133
+ "\u7A7A\u767D (",
1134
+ emptyList.length,
1135
+ ")"
1136
+ ] }),
1137
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "tab", "aria-selected": filter === "trash", className: "sess-fbtn" + (filter === "trash" ? " sess-fbtn-on" : ""), onClick: () => {
1138
+ setFilter("trash");
1139
+ clearSel();
1140
+ setConfirmBatch(false);
1141
+ }, children: [
1142
+ "\u56DE\u6536\u7AD9 (",
1143
+ trash.length,
1144
+ ")"
1145
+ ] })
834
1146
  ] }),
835
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", role: "tab", "aria-selected": filter === "trash", className: "sess-fbtn" + (filter === "trash" ? " sess-fbtn-on" : ""), onClick: () => {
836
- setFilter("trash");
837
- clearSel();
838
- setConfirmBatch(false);
839
- }, children: [
840
- "\u56DE\u6536\u7AD9 (",
841
- trash.length,
842
- ")"
843
- ] })
844
- ] }),
1147
+ /* @__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" }) }) })
1148
+ ] }) }),
845
1149
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "maint-bar", children: [
1150
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "archv-btn", "aria-expanded": storageOpen, onClick: () => {
1151
+ const next = !storageOpen;
1152
+ setStorageOpen(next);
1153
+ if (next && (!storage || storageDirty.current) && !storageBusy) loadStorage();
1154
+ }, children: [
1155
+ "\u5B58\u50A8\u5360\u7528",
1156
+ storage ? ` \xB7 ${fmtBytes(storage.totalBytes) || "0 B"}` : ""
1157
+ ] }),
846
1158
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "archv-btn" + (aa.settings.inactiveDays ? " archv-go" : ""), "aria-expanded": aaOpen, onClick: () => setAaOpen(!aaOpen), children: [
847
1159
  "\u81EA\u52A8\u5F52\u6863",
848
1160
  aa.settings.inactiveDays ? `\uFF1A${aa.settings.inactiveDays} \u5929\u672A\u6D3B\u8DC3` : "\uFF1A\u672A\u542F\u7528"
@@ -853,15 +1165,7 @@ function SessionPanel({ workspacesSvc }) {
853
1165
  "\uFF0C\u5F52\u6863 ",
854
1166
  aa.lastArchivedCount,
855
1167
  " \u4E2A"
856
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "maint-note", children: "\u5C1A\u672A\u68C0\u67E5" }),
857
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "archv-btn maint-bar-right", "aria-expanded": storageOpen, onClick: () => {
858
- const next = !storageOpen;
859
- setStorageOpen(next);
860
- if (next && (!storage || storageDirty.current) && !storageBusy) loadStorage();
861
- }, children: [
862
- "\u5B58\u50A8\u5360\u7528",
863
- storage ? ` \xB7 ${fmtBytes(storage.totalBytes) || "0 B"}` : ""
864
- ] })
1168
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "maint-note", children: "\u5C1A\u672A\u68C0\u67E5" })
865
1169
  ] }),
866
1170
  aaOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-sheet", "aria-label": "\u81EA\u52A8\u5F52\u6863\u8BBE\u7F6E", children: [
867
1171
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-sheet-head", children: [
@@ -925,7 +1229,7 @@ function SessionPanel({ workspacesSvc }) {
925
1229
  ] })
926
1230
  ] }),
927
1231
  showSessionList && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
928
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-tools", "aria-label": "\u67E5\u627E\u548C\u6574\u7406\u4F1A\u8BDD", children: [
1232
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-tools sess-tools-4", "aria-label": "\u67E5\u627E\u548C\u6574\u7406\u4F1A\u8BDD", children: [
929
1233
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-field", children: [
930
1234
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "dsm-search", children: "\u641C\u7D22\u4F1A\u8BDD" }),
931
1235
  /* @__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" })
@@ -944,16 +1248,23 @@ function SessionPanel({ workspacesSvc }) {
944
1248
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "oldest", children: "\u6700\u65E9\u521B\u5EFA" }),
945
1249
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "title", children: "\u6807\u9898 A\u2013Z" })
946
1250
  ] })
1251
+ ] }),
1252
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-field", children: [
1253
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "dsm-group", children: "\u5206\u7EC4" }),
1254
+ /* @__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: [
1255
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "lineage", children: "\u8840\u7F18\uFF08\u5B50\u4EE3\u7406\u6298\u53E0\uFF09" }),
1256
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "flat", children: "\u5E73\u94FA\uFF08\u5168\u90E8\u5E76\u5217\uFF09" })
1257
+ ] })
947
1258
  ] })
948
1259
  ] }),
949
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-results", role: "status", children: [
1260
+ /* @__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: [
950
1261
  "\u663E\u793A ",
951
- list.length,
1262
+ topList.length,
952
1263
  " \u4E2A\u4F1A\u8BDD",
953
- query || workspaceFilter !== "all" ? `\uFF0C\u5171 ${filter === "archived" ? archivedList.length : filter === "active" ? activeList.length : filter === "starred" ? starredList.length : sessions.length} \u4E2A` : ""
954
- ] })
1264
+ 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` : ""
1265
+ ] }) })
955
1266
  ] }),
956
- showSessionList && list.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-batch", children: [
1267
+ showSessionList && list.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-batch" + (selIds.length > 0 ? " sess-batch-pin" : ""), children: [
957
1268
  /* @__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` }),
958
1269
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", disabled: list.length === 0, onClick: selectAll, children: "\u5168\u9009" }),
959
1270
  selIds.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
@@ -962,10 +1273,78 @@ function SessionPanel({ workspacesSvc }) {
962
1273
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn archv-del", disabled: busy !== null, onClick: () => doBatch("delete-many"), children: confirmBatch ? "\u786E\u8BA4\u5220\u9664\u6240\u9009?" : "\u5220\u9664\u6240\u9009" })
963
1274
  ] }),
964
1275
  filter !== "archived" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", disabled: busy !== null, onClick: () => doBatch("archive-many"), children: "\u5F52\u6863\u6240\u9009" }),
1276
+ canMove.available && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1277
+ "button",
1278
+ {
1279
+ type: "button",
1280
+ className: "archv-btn",
1281
+ disabled: busy !== null,
1282
+ "aria-expanded": batchMoveOpen,
1283
+ onClick: () => setBatchMoveOpen(!batchMoveOpen),
1284
+ children: "\u79FB\u52A8\u6240\u9009\u2026"
1285
+ }
1286
+ ),
965
1287
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", onClick: clearSel, children: "\u53D6\u6D88\u9009\u62E9" })
1288
+ ] }),
1289
+ selIds.length > 0 && batchMoveOpen && canMove.available && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-sheet", role: "region", "aria-label": "\u6279\u91CF\u79FB\u52A8\u5230\u5DE5\u4F5C\u533A", children: [
1290
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-sheet-head", children: [
1291
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h3", { className: "mv-sheet-title", children: [
1292
+ "\u6279\u91CF\u79FB\u52A8 ",
1293
+ selIds.length,
1294
+ " \u4E2A\u4F1A\u8BDD"
1295
+ ] }),
1296
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "mv-sheet-close", "aria-label": "\u5173\u95ED", onClick: () => setBatchMoveOpen(false), children: "\xD7" })
1297
+ ] }),
1298
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-seg", role: "tablist", children: [
1299
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", role: "tab", "aria-selected": moveMode === "existing", className: "mv-segbtn" + (moveMode === "existing" ? " mv-segbtn-on" : ""), onClick: () => setMoveMode("existing"), children: "\u5DF2\u6709\u5DE5\u4F5C\u533A" }),
1300
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", role: "tab", "aria-selected": moveMode === "new", className: "mv-segbtn" + (moveMode === "new" ? " mv-segbtn-on" : ""), onClick: () => setMoveMode("new"), children: "\u65B0\u5EFA\u76EE\u5F55" })
1301
+ ] }),
1302
+ moveMode === "existing" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-field", children: [
1303
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "mv-field-label", htmlFor: "mv-batch-ws", children: "\u76EE\u6807\u5DE5\u4F5C\u533A" }),
1304
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { id: "mv-batch-ws", value: targetWs, onChange: (e) => setTargetWs(e.target.value), children: [
1305
+ workspaces.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: "\uFF08\u6682\u65E0\u5DE5\u4F5C\u533A\uFF09" }),
1306
+ workspaces.map((w) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("option", { value: w.workspaceId, children: [
1307
+ w.title,
1308
+ " \xB7 ",
1309
+ w.path
1310
+ ] }, w.workspaceId))
1311
+ ] })
1312
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-field", children: [
1313
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "mv-field-label", htmlFor: "mv-batch-path", children: "\u65B0\u5DE5\u4F5C\u533A\u76EE\u5F55\u8DEF\u5F84" }),
1314
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-browse-row", children: [
1315
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1316
+ "input",
1317
+ {
1318
+ id: "mv-batch-path",
1319
+ type: "text",
1320
+ value: newPath,
1321
+ onChange: (e) => setNewPath(e.target.value),
1322
+ placeholder: "\u4F8B\u5982 /Users/you/Projects/demo \u6216 ~/demo"
1323
+ }
1324
+ ),
1325
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1326
+ "button",
1327
+ {
1328
+ type: "button",
1329
+ className: "archv-btn",
1330
+ onClick: pickDirectory,
1331
+ disabled: busy !== null || picking || !workspacesSvc,
1332
+ title: !workspacesSvc ? "\u5F53\u524D\u8FD0\u884C\u73AF\u5883\u4E0D\u652F\u6301\u7CFB\u7EDF\u76EE\u5F55\u9009\u62E9" : "\u6253\u5F00\u7CFB\u7EDF\u76EE\u5F55\u9009\u62E9\u7A97\u53E3",
1333
+ children: picking ? "\u9009\u62E9\u4E2D\u2026" : "\u6D4F\u89C8\u2026"
1334
+ }
1335
+ )
1336
+ ] })
1337
+ ] }),
1338
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-foot", children: [
1339
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", onClick: () => setBatchMoveOpen(false), children: "\u53D6\u6D88" }),
1340
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "archv-btn archv-go", disabled: busy !== null, onClick: () => doBatchMove(), children: [
1341
+ busy === "__batch__" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "archv-spin", "aria-hidden": "true" }),
1342
+ "\u786E\u8BA4\u79FB\u52A8"
1343
+ ] })
1344
+ ] })
966
1345
  ] })
967
1346
  ] }),
968
- 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" : "\u6682\u65E0\u53EF\u7BA1\u7406\u7684\u4F1A\u8BDD\u3002" }) : showSessionList ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "archv-list", role: "list", children: list.map((it) => {
1347
+ 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) => {
969
1348
  const date = fmtDate(it.createdAt);
970
1349
  const expanded = openMove === it.sessionId;
971
1350
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "archv-card" + (expanded ? " archv-card-exp" : ""), role: "listitem", children: [
@@ -992,25 +1371,31 @@ function SessionPanel({ workspacesSvc }) {
992
1371
  e.stopPropagation();
993
1372
  toggleStar(it);
994
1373
  },
995
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "20", height: "20", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 2.5l2.9 5.9 6.6.9-4.8 4.6 1.2 6.5-5.9-3.1-5.9 3.1 1.2-6.5L2.5 9.3l6.6-.9z" }) })
1374
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "18", height: "18", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 2.5l2.9 5.9 6.6.9-4.8 4.6 1.2 6.5-5.9-3.1-5.9 3.1 1.2-6.5L2.5 9.3l6.6-.9z" }) })
996
1375
  }
997
1376
  ),
998
1377
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "archv-body", children: [
999
1378
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "archv-main", children: [
1000
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "archv-name", title: it.title || "", children: it.title || "(\u65E0\u6807\u9898)" }),
1379
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "archv-titlerow", children: [
1380
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "archv-name", title: it.title || "", children: it.title || "(\u65E0\u6807\u9898)" }),
1381
+ branchBadge(it.sessionId),
1382
+ emptyBadge(it.sessionId),
1383
+ kidsBadge(it.sessionId),
1384
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "archv-id", title: it.sessionId, children: shortId(it.sessionId) })
1385
+ ] }),
1001
1386
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "archv-meta", children: [
1002
1387
  it.archived ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "archv-wtag archv-wgone", children: "\u5DF2\u5F52\u6863" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "archv-wtag archv-active", children: "\u6D3B\u52A8" }),
1003
1388
  workspaceTag(it),
1004
1389
  date && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1005
1390
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "archv-dot", children: "\xB7" }),
1006
1391
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "archv-date", children: date })
1007
- ] }),
1008
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "archv-id", children: it.sessionId })
1392
+ ] })
1009
1393
  ] })
1010
1394
  ] }),
1011
1395
  rowMenu(it)
1012
1396
  ] })
1013
1397
  ] }),
1398
+ groupByLineage && renderKids(it.sessionId, 0),
1014
1399
  expanded && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-sheet", role: "region", "aria-label": "\u79FB\u52A8\u5230\u5DE5\u4F5C\u533A", children: [
1015
1400
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mv-sheet-head", children: [
1016
1401
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "mv-sheet-title", children: "\u79FB\u52A8\u5230\u5DE5\u4F5C\u533A" }),
@@ -1189,7 +1574,7 @@ function SessionPanel({ workspacesSvc }) {
1189
1574
  ] }, it.sessionId);
1190
1575
  }) }) : null
1191
1576
  ] }),
1192
- toast && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "archv-status", role: "status", children: toast }),
1577
+ toast && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "archv-status" + (toast.kind === "err" ? " archv-status-err" : "") + (String(toast.msg).length > 28 ? " archv-status-long" : ""), role: "status", title: "\u70B9\u51FB\u5173\u95ED", onClick: () => setToast(null), children: toast.msg }),
1193
1578
  delTarget && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dlg-backdrop", onClick: () => setDelTarget(null), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref: dialogRef, className: "dlg", role: "alertdialog", "aria-modal": "true", "aria-label": "\u5220\u9664\u4F1A\u8BDD", onClick: (e) => e.stopPropagation(), children: [
1194
1579
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "dlg-title", children: "\u5220\u9664\u4F1A\u8BDD" }),
1195
1580
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "dlg-text", children: [
@@ -1288,7 +1673,9 @@ var SIDEBAR_AUG_CSS = `
1288
1673
  .dsm-btn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
1289
1674
  .dsm-del{color:var(--dsw-alias-state-error-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 45%,transparent)}
1290
1675
  .dsm-del:hover{background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent);color:var(--dsw-alias-state-error-primary)}
1291
- .dsm-toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);z-index:2147483601;background:var(--dsw-alias-fill-elevated);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);padding:9px 16px;border-radius:999px;font-size:12px;box-shadow:0 8px 24px rgb(0 0 0/.18);max-width:min(90vw,420px)}
1676
+ .dsm-toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);z-index:2147483601;background:Canvas;color:CanvasText;border:1px solid color-mix(in srgb,CanvasText 25%,transparent);padding:9px 16px;border-radius:999px;font-size:12px;box-shadow:0 8px 24px rgb(0 0 0/.25);max-width:min(92vw,460px);cursor:pointer}
1677
+ .dsm-toast-long{border-radius:14px;text-align:left;line-height:18px}
1678
+ .dsm-toast-err{background:#4A1D1D;color:#FFD9D9;border:1px solid var(--dsw-alias-state-error-primary)}
1292
1679
  .dsm-sub{position:fixed;z-index:1100;box-sizing:border-box;min-width:190px;max-width:320px;padding:4px;display:flex;flex-direction:column;gap:0;background:var(--dsw-specific-menu);border:1px solid var(--dsw-alias-border-inverted);border-radius:12px;box-shadow:var(--dsw-shadow-lv3)}
1293
1680
  .dsm-sub-loading,.dsm-sub-empty{font-size:12px;color:var(--dsw-alias-label-tertiary);padding:6px 10px}
1294
1681
  .dsm-sub-err{font-size:12px;color:var(--dsw-alias-state-error-primary);padding:6px 10px}
@@ -1324,11 +1711,54 @@ var SIDEBAR_AUG_CSS = `
1324
1711
  .dsm-storage-size{font-size:12px;color:var(--dsw-alias-label-secondary);flex:none;white-space:nowrap}
1325
1712
  .dsm-storage-count{font-size:11px;color:var(--dsw-alias-label-tertiary);flex:none;white-space:nowrap;max-width:32%;overflow:hidden;text-overflow:ellipsis}
1326
1713
  .maint-bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:0 0 8px}
1327
- .maint-bar-right{margin-left:auto}
1328
1714
  .maint-note{font-size:11px;color:var(--dsw-alias-label-tertiary)}
1329
1715
  .mv-sheet-actions{display:flex;align-items:center;gap:6px}
1330
1716
  .aa-check{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--dsw-alias-label-secondary);cursor:pointer}
1331
1717
  .aa-check input{width:15px;height:15px;accent-color:var(--dsw-alias-state-business-primary);cursor:pointer;flex:none}
1718
+ /* ---- \u8840\u7F18\u5206\u5C42\u62AB\u9732\uFF08issue #6\uFF09\uFF1A\u884C\u5185\u5FBD\u6807 ---- */
1719
+ .dsm-lineage-pick{display:flex;flex-direction:column;gap:6px;margin:8px 0}
1720
+ .dsm-lineage-pick-btn{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.3));border-radius:8px;background:transparent;color:inherit;cursor:pointer;font-size:12px;text-align:left}
1721
+ .dsm-lineage-pick-btn:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.08))}
1722
+ .dsm-lineage-sum{font-size:12px;opacity:.75;margin:6px 0;display:flex;align-items:center;gap:6px;overflow:hidden}
1723
+ .dsm-lineage-tree{display:flex;flex-direction:column;gap:2px;margin:8px 0;max-height:340px;overflow:auto}
1724
+ .dsm-lineage-row{display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:8px;font-size:12px;min-height:28px}
1725
+ .dsm-lineage-row:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.08))}
1726
+ .dsm-lineage-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
1727
+ .dsm-lineage-kids,.dsm-lineage-size{font-size:11px;opacity:.65;white-space:nowrap}
1728
+ .dsm-lineage-livechip{font-size:10px;padding:1px 6px;border-radius:999px;background:rgba(234,179,8,.18);color:#A16207;white-space:nowrap}
1729
+ .dsm-lineage-acts{display:flex;gap:4px}
1730
+ .dsm-kids-badge{appearance:none;display:inline-flex;align-items:center;gap:3px;margin-left:auto;padding:1px 7px;border:none;border-radius:999px;background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 10%,transparent);color:var(--dsw-alias-state-business-primary);font-size:10px;font-weight:600;line-height:15px;cursor:pointer;flex:none;pointer-events:auto}
1731
+ .dsm-subs{display:flex;flex-direction:column;gap:2px;padding:2px 0 6px 26px;pointer-events:auto}
1732
+ .dsm-sub-row{display:flex;align-items:center;gap:8px;min-height:24px;padding-right:6px;border-radius:7px;font-size:11.5px;color:var(--dsw-alias-label-secondary);position:relative}
1733
+ .dsm-sub-row::before{content:'';position:absolute;left:0;top:0;bottom:0;width:1px;background:var(--dsw-alias-border-l2)}
1734
+ .dsm-sub-row:hover{background:var(--dsw-alias-interactive-bg-hover);cursor:pointer}
1735
+ .dsm-sub-row:hover .dsm-sub-name{color:var(--dsw-alias-label-primary)}
1736
+ .dsm-sub-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
1737
+ .dsm-sub-meta{flex:none;font-size:10.5px;opacity:.8}
1738
+ .dsm-sub-live .dsm-sub-meta{color:var(--dsw-alias-state-warning-primary);opacity:1;font-weight:600}
1739
+ .dsm-sub-acts{display:none;align-items:center;gap:4px;flex:none}
1740
+ .dsm-sub-row:hover .dsm-sub-acts,.dsm-sub-row:focus-within .dsm-sub-acts{display:inline-flex}
1741
+ .dsm-sub-btn{appearance:none;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-fill-elevated);color:var(--dsw-alias-label-secondary);border-radius:6px;padding:1px 6px;font:inherit;font-size:10.5px;line-height:16px;cursor:pointer}
1742
+ .dsm-sub-btn:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l3)}
1743
+ .dsm-sub-btn-danger{color:var(--dsw-alias-state-error-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 40%,transparent)}
1744
+ .dsm-sub-note{font-size:11px;color:var(--dsw-alias-label-tertiary);padding:2px 0}
1745
+ .dsm-kids-badge:hover{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 18%,transparent)}
1746
+ .dsm-row-sub{transform:translateX(calc(10px + var(--dsm-indent,0px)));transition:transform .15s ease}
1747
+ .dsm-row-sub::before{content:'';position:absolute;left:calc(-6px - var(--dsm-indent,0px));top:0;bottom:0;width:2px;border-radius:1px;background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 30%,transparent);pointer-events:none}
1748
+ .dsm-tag{display:inline-flex;align-items:center;margin-left:auto;padding:1px 7px;border-radius:999px;background:var(--dsw-alias-fill-subtle);color:var(--dsw-alias-label-tertiary);font-size:10px;font-weight:500;line-height:15px;flex:none;pointer-events:auto;border:none;cursor:default}
1749
+ .dsm-tag-fork{cursor:pointer;background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 12%,transparent);color:var(--dsw-alias-state-success-primary);font-weight:600}
1750
+ .dsm-tag-fork:hover{background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 20%,transparent)}
1751
+ .dsm-row-flash{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px;border-radius:8px;transition:outline-color .6s ease}
1752
+ /* \u5FBD\u6807/\u6807\u7B7E\u7EDF\u4E00\u663E\u9690\uFF08\u5BF9\u9F50\u300C\u5B50\u4EE3\u7406\u6298\u53E0\u300D\u7684\u8282\u594F\uFF09\uFF1A\u9ED8\u8BA4\u4E00\u5F8B\u9690\u85CF\uFF0C\u7EF4\u6301 DSH
1753
+ \u539F\u751F\u884C\u5916\u89C2\uFF1B\u884C\u88AB\u9009\u4E2D\uFF08dsm-row-active\uFF0Cpaint \u6309 activeId \u5237\u65B0\uFF09\u3001\u9F20\u6807\u60AC
1754
+ \u505C\u3001\u952E\u76D8\u805A\u7126\u6216\u5DF2\u5C55\u5F00\u5B50\u4EE3\u7406\u5217\u8868\uFF08dsm-row-open\uFF09\u65F6\u624D\u51FA\u73B0\u3002display \u5207\u6362
1755
+ \u4E0D\u5360\u4F4D\uFF1A\u9690\u85CF\u65F6\u6807\u9898\u62FF\u5168\u5BBD\uFF0C\u60AC\u505C\u65F6\u624D\u8BA9\u4F4D\u7ED9\u5FBD\u6807\u3002 */
1756
+ [role="treeitem"]>.dsm-kids-badge,[role="treeitem"]>.dsm-tag{display:none}
1757
+ [role="treeitem"]:hover>.dsm-kids-badge,[role="treeitem"]:hover>.dsm-tag,
1758
+ [role="treeitem"].dsm-row-active>.dsm-kids-badge,[role="treeitem"].dsm-row-active>.dsm-tag,
1759
+ [role="treeitem"].dsm-row-open>.dsm-kids-badge,
1760
+ [role="treeitem"]:focus-within>.dsm-kids-badge,[role="treeitem"]:focus-within>.dsm-tag{display:inline-flex}
1761
+ @media (prefers-reduced-motion:reduce){.dsm-row-sub{transition:none}.dsm-row-flash{transition:none}}
1332
1762
  @media (max-width:640px){.dsm-storage-bar{display:none}.dsm-storage-count{max-width:40%}}
1333
1763
  `;
1334
1764
  var DSM_KEY_MANUAL = "dsm-manual-unread-v1";
@@ -1361,6 +1791,7 @@ var dsmServerPurgedIds = /* @__PURE__ */ new Set();
1361
1791
  var dsmAuthoritativeTitles = /* @__PURE__ */ new Map();
1362
1792
  var dsmTrashTick = 0;
1363
1793
  var dsmCapabilities = null;
1794
+ var dsmLineage = /* @__PURE__ */ new Map();
1364
1795
  async function dsmLoadCapabilities() {
1365
1796
  try {
1366
1797
  dsmCapabilities = await postJSON("/archived-sessions/capabilities", {});
@@ -1381,6 +1812,10 @@ async function dsmLoadTrashIds() {
1381
1812
  if (dsmServerPurgedIds.has(id)) dsmPendingPurged.delete(id);
1382
1813
  }
1383
1814
  dsmAuthoritativeTitles = new Map(Object.entries(r && r.titles || {}).map(([id, title]) => [String(id), String(title)]));
1815
+ dsmLineage.clear();
1816
+ for (const [id, info] of Object.entries(r && r.lineage || {})) {
1817
+ if (info && typeof info === "object") dsmLineage.set(String(id), info);
1818
+ }
1384
1819
  if (dsmRepaintDots) dsmRepaintDots();
1385
1820
  } catch (e) {
1386
1821
  }
@@ -1396,6 +1831,24 @@ function dsmMarkPurged(ids) {
1396
1831
  }
1397
1832
  var SIDEBAR_ADAPTER_VERSION = 3;
1398
1833
  var SIDEBAR_ADAPTER_MAX_MISSES = 200;
1834
+ function dsmTeardownSidebarAug() {
1835
+ if (typeof document === "undefined") return;
1836
+ const selectors = ["[data-dsm-dot]", "[data-dsm-kids]", "[data-dsm-subs]", "[data-dsm-tag]"];
1837
+ for (const sel of selectors) {
1838
+ try {
1839
+ document.querySelectorAll(sel).forEach((el) => el.remove());
1840
+ } catch (e) {
1841
+ }
1842
+ }
1843
+ try {
1844
+ document.querySelectorAll("[data-dsm-group-hidden]").forEach((owner) => {
1845
+ owner.style.display = owner.dataset.dsmPrevDisplay || "";
1846
+ owner.removeAttribute("data-dsm-group-hidden");
1847
+ delete owner.dataset.dsmPrevDisplay;
1848
+ });
1849
+ } catch (e) {
1850
+ }
1851
+ }
1399
1852
  function createSidebarAdapter() {
1400
1853
  let disabled = false;
1401
1854
  let misses = 0;
@@ -1415,8 +1868,9 @@ function createSidebarAdapter() {
1415
1868
  f = f.return;
1416
1869
  }
1417
1870
  if (!out.node && !out.group) {
1418
- if (++misses >= SIDEBAR_ADAPTER_MAX_MISSES) {
1871
+ if (++misses >= SIDEBAR_ADAPTER_MAX_MISSES && !disabled) {
1419
1872
  disabled = true;
1873
+ dsmTeardownSidebarAug();
1420
1874
  try {
1421
1875
  console.warn("[dsh-sessions-manager] \u4FA7\u680F\u6CE8\u5165 adapter v" + SIDEBAR_ADAPTER_VERSION + " \u5DF2\u505C\u7528\uFF1A\u672A\u8BC6\u522B\u5230\u5DF2\u77E5\u7684 DSH \u4FA7\u680F\u8282\u70B9\u7ED3\u6784\uFF08\u4E0A\u6E38\u53EF\u80FD\u5DF2\u6539\u7248\uFF09");
1422
1876
  } catch (e) {
@@ -1437,8 +1891,11 @@ function createSidebarAdapter() {
1437
1891
  };
1438
1892
  }
1439
1893
  var sidebarAdapter = createSidebarAdapter();
1894
+ var sidebarMenuAugInstalled = false;
1440
1895
  function installSidebarSessionMenuAug() {
1441
1896
  if (typeof document === "undefined") return;
1897
+ if (sidebarMenuAugInstalled) return;
1898
+ sidebarMenuAugInstalled = true;
1442
1899
  const AUG = "data-dsm-aug";
1443
1900
  let styleInjected = false;
1444
1901
  let activeSubClose = null;
@@ -1472,10 +1929,19 @@ function installSidebarSessionMenuAug() {
1472
1929
  };
1473
1930
  const toast = (msg) => {
1474
1931
  const t = document.createElement("div");
1475
- t.className = "dsm-toast";
1476
- t.textContent = msg;
1932
+ const text = String(msg == null ? "" : msg);
1933
+ t.className = "dsm-toast" + (text.length > 28 ? " dsm-toast-long" : "");
1934
+ t.textContent = text;
1935
+ t.title = "\u70B9\u51FB\u5173\u95ED";
1936
+ t.setAttribute("role", "status");
1477
1937
  document.body.appendChild(t);
1478
- setTimeout(() => t.remove(), 2600);
1938
+ let killTimer = null;
1939
+ const kill = () => {
1940
+ if (killTimer) clearTimeout(killTimer);
1941
+ t.remove();
1942
+ };
1943
+ killTimer = setTimeout(kill, toastDurationFor(text));
1944
+ t.addEventListener("click", kill);
1479
1945
  };
1480
1946
  const ensureStyle = () => {
1481
1947
  if (styleInjected) return;
@@ -1565,7 +2031,14 @@ function installSidebarSessionMenuAug() {
1565
2031
  e.preventDefault();
1566
2032
  closeSub();
1567
2033
  closeMenu();
1568
- postJSON("/archived-sessions/move", { sessionId: info.id, targetPath: w.path }).then(() => toast("\u79FB\u52A8\u6210\u529F")).catch((ee) => toast("\u79FB\u52A8\u5931\u8D25\uFF1A" + String(ee && ee.message || ee)));
2034
+ postJSON("/archived-sessions/move", { sessionId: info.id, targetPath: w.path }).then((r2) => {
2035
+ const label = w.title || pathName(w.path) || "\u76EE\u6807\u5DE5\u4F5C\u533A";
2036
+ if (r2 && r2.queued) {
2037
+ toast(r2.notes && r2.notes.length ? r2.notes.join(" ") : "\u4F1A\u8BDD\u6B63\u88AB DSH \u6253\u5F00\uFF0C\u5DF2\u6392\u961F\u5F85\u79FB\u52A8\uFF1B\u91CD\u542F DSH \u540E\u4F1A\u81EA\u52A8\u5B8C\u6210\uFF08\u8BF7\u5148\u522B\u6253\u5F00\u5B83\uFF09\u3002");
2038
+ return;
2039
+ }
2040
+ toast(r2 && r2.already ? `\u5DF2\u5728\u300C${label}\u300D` : `\u5DF2\u79FB\u5230\u300C${label}\u300D`);
2041
+ }).catch((ee) => toast("\u79FB\u52A8\u5931\u8D25\uFF1A" + String(ee && ee.message || ee)));
1569
2042
  });
1570
2043
  sub.appendChild(b);
1571
2044
  });
@@ -1721,6 +2194,36 @@ function installSidebarStatusDots() {
1721
2194
  };
1722
2195
  const manualUnread = dsmLoadManual();
1723
2196
  const titleInitializedRows = /* @__PURE__ */ new WeakSet();
2197
+ function dsmSyncEmptyGroups() {
2198
+ let groups = null;
2199
+ try {
2200
+ groups = document.querySelectorAll('[role="group"]');
2201
+ } catch (e) {
2202
+ return;
2203
+ }
2204
+ if (!groups || !groups.length) return;
2205
+ groups.forEach((g) => {
2206
+ const items = g.querySelectorAll('[role="treeitem"]');
2207
+ if (!items.length) return;
2208
+ let visible = 0;
2209
+ items.forEach((it) => {
2210
+ if (it.style.display !== "none") visible++;
2211
+ });
2212
+ const owner = g.closest('[role="treeitem"]') || g;
2213
+ if (visible === 0) {
2214
+ if (!owner.hasAttribute("data-dsm-group-hidden")) {
2215
+ owner.dataset.dsmPrevDisplay = owner.style.display || "";
2216
+ owner.setAttribute("data-dsm-group-hidden", "");
2217
+ }
2218
+ if (owner.style.display !== "none") owner.style.display = "none";
2219
+ } else if (owner.hasAttribute("data-dsm-group-hidden")) {
2220
+ const prev = owner.dataset.dsmPrevDisplay || "";
2221
+ if (owner.style.display !== prev) owner.style.display = prev;
2222
+ owner.removeAttribute("data-dsm-group-hidden");
2223
+ delete owner.dataset.dsmPrevDisplay;
2224
+ }
2225
+ });
2226
+ }
1724
2227
  const paint = () => {
1725
2228
  if (sidebarAdapter.disabled) return;
1726
2229
  const activeId = activeRowId();
@@ -1729,18 +2232,124 @@ function installSidebarStatusDots() {
1729
2232
  dsmSaveManual();
1730
2233
  }
1731
2234
  curActive = activeId;
2235
+ const purgedNow = dsmLoadPurged();
2236
+ const kidsByParent = (() => {
2237
+ const m = /* @__PURE__ */ new Map();
2238
+ for (const [id, info] of dsmLineage.entries()) {
2239
+ if (info.origin !== "subagent" || !info.parentSession) continue;
2240
+ if (dsmTrashIds && dsmTrashIds.has(id) || purgedNow.has(id)) continue;
2241
+ m.set(info.parentSession, (m.get(info.parentSession) || 0) + 1);
2242
+ }
2243
+ return m;
2244
+ })();
1732
2245
  const rows = document.querySelectorAll('[role="treeitem"]');
2246
+ document.querySelectorAll("[data-dsm-subs]").forEach((box) => {
2247
+ const pid = box.getAttribute("data-dsm-subs");
2248
+ const prev = box.previousElementSibling;
2249
+ if (!prev || rowId(prev) !== pid) box.remove();
2250
+ });
1733
2251
  rows.forEach((row) => {
1734
2252
  const id = rowId(row);
1735
2253
  if (!id) return;
1736
- const purged = dsmLoadPurged();
2254
+ const purged = purgedNow;
1737
2255
  if (dsmTrashIds && dsmTrashIds.has(id) || purged.has(id)) {
1738
2256
  if (row.style.display !== "none") row.style.display = "none";
1739
2257
  const d = row.querySelector("[" + DOT + "]");
1740
2258
  if (d) d.remove();
2259
+ dsmDropSubList(row, id);
2260
+ return;
2261
+ }
2262
+ const li = dsmLineage.get(id);
2263
+ const isSub = !!(li && li.origin === "subagent");
2264
+ const isEmpty = !!(li && li.empty);
2265
+ row.classList.remove("dsm-row-sub");
2266
+ row.classList.toggle("dsm-row-active", id === activeId);
2267
+ row.style.removeProperty("--dsm-indent");
2268
+ let hideLineage = false;
2269
+ if (li && isSub) {
2270
+ row.classList.add("dsm-row-sub");
2271
+ if (getComputedStyle(row).position === "static") row.style.position = "relative";
2272
+ const depth = Math.max(0, (li.delegationDepth || 1) - 1);
2273
+ if (depth > 0) row.style.setProperty("--dsm-indent", String(depth * 10) + "px");
2274
+ } else if (li && isEmpty) {
2275
+ hideLineage = true;
2276
+ }
2277
+ if (hideLineage) {
2278
+ if (row.style.display !== "none") row.style.display = "none";
2279
+ const d = row.querySelector("[" + DOT + "]");
2280
+ if (d) d.remove();
2281
+ dsmDropSubList(row, id);
1741
2282
  return;
1742
2283
  }
1743
2284
  if (row.style.display === "none") row.style.display = "";
2285
+ if (!isEmpty && li && li.parentSession && li.origin !== "subagent") {
2286
+ if (!row.querySelector('[data-dsm-tag="fork"]')) {
2287
+ const fork = document.createElement("button");
2288
+ fork.type = "button";
2289
+ fork.dataset.dsmTag = "fork";
2290
+ fork.className = "dsm-tag dsm-tag-fork";
2291
+ const parentTitle = dsmAuthoritativeTitles.get(li.parentSession);
2292
+ fork.textContent = "\u2442 \u5206\u652F";
2293
+ fork.title = parentTitle ? "\u5206\u652F\u4E8E\uFF1A" + parentTitle : "\u5206\u652F\u4F1A\u8BDD\uFF08\u70B9\u51FB\u67E5\u770B\u6765\u6E90\u4F1A\u8BDD\uFF09";
2294
+ fork.setAttribute("aria-label", fork.title);
2295
+ fork.addEventListener("click", (e) => {
2296
+ e.stopPropagation();
2297
+ e.preventDefault();
2298
+ const rows2 = document.querySelectorAll('[role="treeitem"]');
2299
+ for (const other of rows2) {
2300
+ if (rowId(other) === li.parentSession) {
2301
+ other.scrollIntoView({ block: "center", behavior: "smooth" });
2302
+ other.classList.add("dsm-row-flash");
2303
+ setTimeout(() => other.classList.remove("dsm-row-flash"), 1200);
2304
+ break;
2305
+ }
2306
+ }
2307
+ });
2308
+ row.appendChild(fork);
2309
+ }
2310
+ } else {
2311
+ const staleFork = row.querySelector('[data-dsm-tag="fork"]');
2312
+ if (staleFork) staleFork.remove();
2313
+ }
2314
+ if (!isEmpty && !isSub) {
2315
+ const kids = kidsByParent.get(id);
2316
+ const existingKids = row.querySelector("[data-dsm-kids]");
2317
+ const open = dsmSubsOpen.has(id);
2318
+ row.classList.toggle("dsm-row-open", open);
2319
+ if (kids) {
2320
+ const text = (open ? "\u25BE " : "\u25B8 ") + kids + " \u5B50\u4EE3\u7406";
2321
+ let badge = existingKids;
2322
+ if (!badge) {
2323
+ badge = document.createElement("button");
2324
+ badge.type = "button";
2325
+ badge.dataset.dsmKids = "";
2326
+ badge.className = "dsm-kids-badge";
2327
+ badge.addEventListener("click", (e) => {
2328
+ e.stopPropagation();
2329
+ e.preventDefault();
2330
+ dsmToggleSubs(badge);
2331
+ });
2332
+ row.appendChild(badge);
2333
+ }
2334
+ if (badge.dataset.dsmParentId !== id) badge.dataset.dsmParentId = id;
2335
+ if (badge.textContent !== text) badge.textContent = text;
2336
+ const label = (open ? "\u6536\u8D77" : "\u5C55\u5F00") + "\u8BE5\u4F1A\u8BDD\u7684 " + kids + " \u4E2A\u5B50\u4EE3\u7406";
2337
+ if (badge.getAttribute("aria-label") !== label) {
2338
+ badge.setAttribute("aria-label", label);
2339
+ badge.title = label;
2340
+ }
2341
+ if (badge.getAttribute("aria-expanded") !== String(open)) badge.setAttribute("aria-expanded", String(open));
2342
+ if (open) dsmSyncSubList(row, id);
2343
+ else dsmDropSubList(row, id);
2344
+ } else {
2345
+ if (existingKids) existingKids.remove();
2346
+ dsmDropSubList(row, id);
2347
+ }
2348
+ } else {
2349
+ row.classList.remove("dsm-row-open");
2350
+ const staleKids = row.querySelector("[data-dsm-kids]");
2351
+ if (staleKids) staleKids.remove();
2352
+ }
1744
2353
  const authoritativeTitle = dsmAuthoritativeTitles.get(id);
1745
2354
  if (!titleInitializedRows.has(row)) {
1746
2355
  const node = rowNode(row) || {};
@@ -1783,8 +2392,195 @@ function installSidebarStatusDots() {
1783
2392
  }
1784
2393
  dot.style.background = color;
1785
2394
  });
2395
+ dsmSyncEmptyGroups();
1786
2396
  };
1787
2397
  dsmRepaintDots = paint;
2398
+ const paintToast = (msg, kind) => {
2399
+ const t = document.createElement("div");
2400
+ t.className = "dsm-toast" + (kind === "err" ? " dsm-toast-err" : "");
2401
+ t.textContent = msg;
2402
+ 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";
2403
+ document.body.appendChild(t);
2404
+ setTimeout(() => t.remove(), 4200);
2405
+ };
2406
+ const DSM_KEY_SUBS = "dsm-subs-open-v1";
2407
+ const dsmSubsOpen = /* @__PURE__ */ new Set();
2408
+ try {
2409
+ for (const id of JSON.parse(localStorage.getItem(DSM_KEY_SUBS) || "[]")) if (typeof id === "string") dsmSubsOpen.add(id);
2410
+ } catch (e) {
2411
+ }
2412
+ const dsmSaveSubsOpen = () => {
2413
+ try {
2414
+ localStorage.setItem(DSM_KEY_SUBS, JSON.stringify([...dsmSubsOpen]));
2415
+ } catch (e) {
2416
+ }
2417
+ };
2418
+ const dsmSubTrees = /* @__PURE__ */ new Map();
2419
+ const SUB_TTL_MS = 15e3;
2420
+ async function dsmFetchSubTree(pid) {
2421
+ const cached = dsmSubTrees.get(pid);
2422
+ if (cached && Date.now() - cached.ts < SUB_TTL_MS) return cached;
2423
+ dsmSubTrees.set(pid, { ts: Date.now(), status: "loading", nodes: cached && cached.nodes || [] });
2424
+ try {
2425
+ const r = await postJSON("/archived-sessions/lineage-tree", { sessionId: pid });
2426
+ dsmSubTrees.set(pid, { ts: Date.now(), status: "ready", nodes: r && r.nodes || [] });
2427
+ } catch (e) {
2428
+ dsmSubTrees.set(pid, { ts: Date.now(), status: "error", nodes: [], error: String(e && e.message || e) });
2429
+ }
2430
+ if (dsmRepaintDots) dsmRepaintDots();
2431
+ return dsmSubTrees.get(pid);
2432
+ }
2433
+ async function dsmSubAction(sid, kind) {
2434
+ try {
2435
+ await postJSON("/archived-sessions/delete", { sessionId: sid });
2436
+ if (kind === "purge") {
2437
+ await postJSON("/archived-sessions/trash/purge", { sessionId: sid });
2438
+ dsmMarkPurged([sid]);
2439
+ paintToast("\u5DF2\u5F7B\u5E95\u5220\u9664\u8BE5\u5B50\u4EE3\u7406");
2440
+ } else {
2441
+ paintToast("\u5DF2\u79FB\u5165\u56DE\u6536\u7AD9");
2442
+ }
2443
+ dsmDropSubagentFromTrees(sid);
2444
+ for (const pid of [...dsmSubTrees.keys()]) dsmFetchSubTree(pid);
2445
+ dsmLoadTrashIds();
2446
+ if (dsmRepaintDots) dsmRepaintDots();
2447
+ } catch (e) {
2448
+ paintToast("\u64CD\u4F5C\u5931\u8D25\uFF1A" + String(e && e.message || e));
2449
+ }
2450
+ }
2451
+ function dsmDropSubagentFromTrees(sid) {
2452
+ const target = String(sid);
2453
+ const prune = (nodes) => (nodes || []).filter((n) => String(n.sessionId) !== target).map((n) => ({ ...n, children: prune(n.children) }));
2454
+ for (const [pid, rec] of dsmSubTrees) {
2455
+ if (!rec || !Array.isArray(rec.nodes)) continue;
2456
+ dsmSubTrees.set(pid, { ...rec, nodes: prune(rec.nodes) });
2457
+ }
2458
+ }
2459
+ function dsmToggleSubs(badge) {
2460
+ const pid = badge && badge.dataset ? badge.dataset.dsmParentId : null;
2461
+ if (!pid) return;
2462
+ if (sidebarAdapter.disabled) {
2463
+ paintToast("\u4FA7\u680F\u9002\u914D\u5668\u5DF2\u505C\u7528\uFF08\u4E0A\u6E38\u7ED3\u6784\u53D8\u5316\uFF09\uFF0C\u8BF7\u6539\u4ECE \u8BBE\u7F6E \u2192 \u4F1A\u8BDD\u7BA1\u7406 \u67E5\u770B\u5B50\u4EE3\u7406");
2464
+ return;
2465
+ }
2466
+ if (dsmSubsOpen.has(pid)) {
2467
+ dsmSubsOpen.delete(pid);
2468
+ dsmSubTrees.delete(pid);
2469
+ } else {
2470
+ dsmSubsOpen.add(pid);
2471
+ }
2472
+ dsmSaveSubsOpen();
2473
+ if (dsmRepaintDots) dsmRepaintDots();
2474
+ }
2475
+ function dsmDropSubList(row, pid) {
2476
+ const parent = row.parentNode;
2477
+ if (!parent) return;
2478
+ for (const el of parent.children) {
2479
+ if (el.getAttribute && el.getAttribute("data-dsm-subs") === pid) {
2480
+ el.remove();
2481
+ break;
2482
+ }
2483
+ }
2484
+ }
2485
+ function dsmSyncSubList(row, pid) {
2486
+ const parent = row.parentNode;
2487
+ if (!parent) return;
2488
+ const open = dsmSubsOpen.has(pid);
2489
+ let box = null;
2490
+ for (const el of parent.children) {
2491
+ if (el.getAttribute && el.getAttribute("data-dsm-subs") === pid) {
2492
+ box = el;
2493
+ break;
2494
+ }
2495
+ }
2496
+ if (!open) {
2497
+ if (box) box.remove();
2498
+ return;
2499
+ }
2500
+ const rec = dsmSubTrees.get(pid);
2501
+ if (!rec) {
2502
+ dsmFetchSubTree(pid);
2503
+ return;
2504
+ }
2505
+ if (!box) {
2506
+ box = document.createElement("div");
2507
+ box.setAttribute("data-dsm-subs", pid);
2508
+ box.className = "dsm-subs";
2509
+ parent.insertBefore(box, row.nextSibling);
2510
+ } else if (box.nextElementSibling !== row.nextSibling && box.previousElementSibling !== row) {
2511
+ parent.insertBefore(box, row.nextSibling);
2512
+ }
2513
+ const flat = [];
2514
+ const walk = (nodes, depth) => {
2515
+ for (const n of nodes || []) {
2516
+ flat.push([n, depth]);
2517
+ if (depth < 4) walk(n.children, depth + 1);
2518
+ }
2519
+ };
2520
+ walk(rec.nodes, 0);
2521
+ const sig = rec.status + "|" + flat.map(([n, d]) => [n.sessionId, n.title, n.live ? 1 : 0, n.sizeBytes, d].join(":")).join(";");
2522
+ if (box.dataset.dsmSig === sig) return;
2523
+ box.dataset.dsmSig = sig;
2524
+ box.textContent = "";
2525
+ if (rec.status === "loading" && !flat.length) {
2526
+ const p = document.createElement("div");
2527
+ p.className = "dsm-sub-note";
2528
+ p.textContent = "\u52A0\u8F7D\u5B50\u4EE3\u7406\u2026";
2529
+ box.appendChild(p);
2530
+ return;
2531
+ }
2532
+ if (rec.status === "error") {
2533
+ const p = document.createElement("div");
2534
+ p.className = "dsm-sub-note";
2535
+ p.textContent = "\u5B50\u4EE3\u7406\u52A0\u8F7D\u5931\u8D25\uFF1A" + (rec.error || "\u672A\u77E5\u9519\u8BEF");
2536
+ box.appendChild(p);
2537
+ return;
2538
+ }
2539
+ if (!flat.length) {
2540
+ const p = document.createElement("div");
2541
+ p.className = "dsm-sub-note";
2542
+ p.textContent = "\u8BE5\u4F1A\u8BDD\u6CA1\u6709\u5B50\u4EE3\u7406";
2543
+ box.appendChild(p);
2544
+ return;
2545
+ }
2546
+ const canPurgeSub = dsmActionCapability("physicalPurge", "purge");
2547
+ for (const [n, depth] of flat) {
2548
+ const r = document.createElement("div");
2549
+ r.className = "dsm-sub-row" + (n.live ? " dsm-sub-live" : "");
2550
+ r.style.paddingLeft = String(14 + depth * 12) + "px";
2551
+ r.title = "\u6253\u5F00\u8FD9\u4E2A\u5B50\u4EE3\u7406\u4F1A\u8BDD";
2552
+ r.addEventListener("click", async () => {
2553
+ const res = await dsmOpenSessionById(n.sessionId, n.parentSession || pid);
2554
+ if (res === "ok") return;
2555
+ const msg = openSubagentToast(res, n.title, "sidebar");
2556
+ paintToast(msg.text, msg.kind);
2557
+ });
2558
+ const name = document.createElement("span");
2559
+ name.className = "dsm-sub-name";
2560
+ name.textContent = n.title || String(n.sessionId).slice(0, 8) + "\u2026";
2561
+ name.title = n.sessionId;
2562
+ r.appendChild(name);
2563
+ const meta = document.createElement("span");
2564
+ meta.className = "dsm-sub-meta";
2565
+ meta.textContent = n.live ? "\u8FD0\u884C\u4E2D" : fmtBytes(n.sizeBytes) || "\u5DF2\u7ED3\u675F";
2566
+ r.appendChild(meta);
2567
+ const acts = document.createElement("span");
2568
+ acts.className = "dsm-sub-acts";
2569
+ const del = document.createElement("button");
2570
+ del.type = "button";
2571
+ del.className = "dsm-sub-btn";
2572
+ del.textContent = "\u5220\u9664";
2573
+ del.title = "\u79FB\u5165\u56DE\u6536\u7AD9\uFF0C\u53EF\u5728\u56DE\u6536\u7AD9\u6062\u590D";
2574
+ del.addEventListener("click", (e) => {
2575
+ e.stopPropagation();
2576
+ e.preventDefault();
2577
+ dsmSubAction(n.sessionId, "delete");
2578
+ });
2579
+ acts.appendChild(del);
2580
+ r.appendChild(acts);
2581
+ box.appendChild(r);
2582
+ }
2583
+ }
1788
2584
  let raf = 0;
1789
2585
  const schedulePaint = () => {
1790
2586
  if (raf) return;
@@ -1810,10 +2606,18 @@ function installSidebarStatusDots() {
1810
2606
  attributeFilter: ["data-state", "aria-selected", "class", "style"],
1811
2607
  characterData: true
1812
2608
  });
1813
- setInterval(tick, FALLBACK_TICK_MS);
2609
+ const tickTimer = setInterval(tick, FALLBACK_TICK_MS);
2610
+ return () => {
2611
+ clearInterval(tickTimer);
2612
+ obs.disconnect();
2613
+ dsmTeardownSidebarAug();
2614
+ };
1814
2615
  }
2616
+ var sidebarDragInstalled = false;
1815
2617
  function installSidebarWorkspaceDrag() {
1816
2618
  if (typeof document === "undefined") return;
2619
+ if (sidebarDragInstalled) return;
2620
+ sidebarDragInstalled = true;
1817
2621
  let sessions = /* @__PURE__ */ new Map();
1818
2622
  let dragging = null;
1819
2623
  let moving = false;
@@ -1903,9 +2707,14 @@ function installSidebarWorkspaceDrag() {
1903
2707
  row.classList.add("dsm-drag-busy");
1904
2708
  try {
1905
2709
  const result = await postJSON("/archived-sessions/move", { sessionId: item.sessionId, targetPath: target.path });
2710
+ if (result && result.queued) {
2711
+ await dsmLoadTrashIds();
2712
+ toast(result.notes && result.notes.length ? result.notes.join(" ") : "\u4F1A\u8BDD\u6B63\u88AB DSH \u6253\u5F00\uFF0C\u5DF2\u6392\u961F\u5F85\u79FB\u52A8\uFF1B\u91CD\u542F DSH \u540E\u4F1A\u81EA\u52A8\u5B8C\u6210\uFF08\u8BF7\u5148\u522B\u6253\u5F00\u5B83\uFF09\u3002");
2713
+ return;
2714
+ }
1906
2715
  sessions.set(item.sessionId, { ...item, workspacePath: result.workspacePath || target.path, workspaceTitle: result.workspaceTitle || target.title });
1907
2716
  await dsmLoadTrashIds();
1908
- toast(`\u5DF2\u79FB\u5230\u300C${result.workspaceTitle || target.title}\u300D`);
2717
+ toast(result && result.already ? `\u5DF2\u5728\u300C${result.workspaceTitle || target.title}\u300D` : `\u5DF2\u79FB\u5230\u300C${result.workspaceTitle || target.title}\u300D`);
1909
2718
  } catch (error) {
1910
2719
  toast("\u79FB\u52A8\u5931\u8D25\uFF1A" + String(error && error.message || error));
1911
2720
  } finally {
@@ -1951,10 +2760,12 @@ function installSidebarWorkspaceDrag() {
1951
2760
  }
1952
2761
  }
1953
2762
  function apply(ctx) {
2763
+ dsmClientCtx = ctx;
1954
2764
  dsmLoadCapabilities();
1955
2765
  installSettingsNavIcons(ctx);
1956
2766
  installSidebarSessionMenuAug();
1957
- installSidebarStatusDots();
2767
+ const disposeSidebarDots = installSidebarStatusDots();
2768
+ if (disposeSidebarDots) ctx.effect(() => disposeSidebarDots);
1958
2769
  installSidebarWorkspaceDrag();
1959
2770
  const workspacesSvc = ctx.get("workspaces");
1960
2771
  ctx.slots.inject(