@lijian-ui/dsh-session-cleaner 0.1.2 → 0.1.4
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 +753 -736
- package/lib/index.js +26 -41
- package/package.json +87 -87
- /package/lib/{index.d.ts → index-DdPrDbW6.d.ts} +0 -0
package/lib/client.js
CHANGED
|
@@ -1,751 +1,768 @@
|
|
|
1
|
-
window.__ModuleLoader__.load({
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
};
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
};
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
}
|
|
304
|
-
const previewMeta = {
|
|
305
|
-
fontSize: 11,
|
|
306
|
-
color: "var(--dsw-alias-label-secondary, #888)"
|
|
307
|
-
};
|
|
308
|
-
function fmtBytes(n) {
|
|
309
|
-
if (n < 1024) return `${n} B`;
|
|
310
|
-
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
|
|
311
|
-
return `${(n / 1024 / 1024).toFixed(1)} MB`;
|
|
312
|
-
}
|
|
313
|
-
function fmtTime(ms) {
|
|
314
|
-
const d = new Date(ms);
|
|
315
|
-
const p = (x) => String(x).padStart(2, "0");
|
|
316
|
-
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`;
|
|
317
|
-
}
|
|
318
|
-
/** 会话展示名:优先标题,回退 workspace 解码 + id 短前缀 */
|
|
319
|
-
function fmtSessionName(s) {
|
|
320
|
-
if (s.title) return s.title;
|
|
321
|
-
const ws = s.workspaceDir.replace(/~003A/g, ":").replace(/^--/, "").replace(/--$/, "");
|
|
322
|
-
const short = s.id.length > 8 ? `${s.id.slice(0, 8)}…` : s.id;
|
|
323
|
-
return `${ws} / ${short}`;
|
|
324
|
-
}
|
|
325
|
-
/** 格式化时间戳 */
|
|
326
|
-
function fmtDateTime(ms) {
|
|
327
|
-
if (!ms) return "—";
|
|
328
|
-
const d = new Date(ms);
|
|
329
|
-
const p = (x) => String(x).padStart(2, "0");
|
|
330
|
-
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`;
|
|
331
|
-
}
|
|
332
|
-
function SessionManagerSection(props) {
|
|
333
|
-
const { api, t } = props;
|
|
334
|
-
const [sessions, setSessions] = (0, react.useState)([]);
|
|
335
|
-
const [loading, setLoading] = (0, react.useState)(true);
|
|
336
|
-
const [error, setError] = (0, react.useState)("");
|
|
337
|
-
const [tab, setTab] = (0, react.useState)("all");
|
|
338
|
-
const [confirmTarget, setConfirmTarget] = (0, react.useState)(null);
|
|
339
|
-
const [busy, setBusy] = (0, react.useState)(false);
|
|
340
|
-
const [previewTarget, setPreviewTarget] = (0, react.useState)(null);
|
|
341
|
-
const [previewData, setPreviewData] = (0, react.useState)(null);
|
|
342
|
-
const [previewLoading, setPreviewLoading] = (0, react.useState)(false);
|
|
343
|
-
const mountedRef = (0, react.useRef)(true);
|
|
344
|
-
const refresh = async () => {
|
|
345
|
-
setLoading(true);
|
|
346
|
-
setError("");
|
|
347
|
-
const res = await api.list();
|
|
348
|
-
if (!mountedRef.current) return;
|
|
349
|
-
setLoading(false);
|
|
350
|
-
if (!res.ok) {
|
|
351
|
-
setError(t("error.loadFailed", { message: res.message ?? res.code ?? "" }));
|
|
352
|
-
return;
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@lijian-ui/dsh-session-cleaner",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let react = require("react");
|
|
8
|
+
//#region src/client/SessionManagerSection.ts
|
|
9
|
+
/**
|
|
10
|
+
* 会话管理设置页(client half)
|
|
11
|
+
* ------------------------------------------------------------------
|
|
12
|
+
* 分组:全部 / 已归档 / 游离(无 workspace 归属)。
|
|
13
|
+
* 操作:归档(移入已归档分组)、恢复(移出)、删除(真删除 + 二次确认弹窗)。
|
|
14
|
+
* 样式走 dsh 设计 token(var(--dsw-alias-*))自动适配明暗主题。
|
|
15
|
+
*/
|
|
16
|
+
const page = {
|
|
17
|
+
display: "flex",
|
|
18
|
+
flexDirection: "column",
|
|
19
|
+
gap: 12,
|
|
20
|
+
padding: "4px 0 24px",
|
|
21
|
+
width: "100%",
|
|
22
|
+
minHeight: 400
|
|
23
|
+
};
|
|
24
|
+
const header = {
|
|
25
|
+
display: "flex",
|
|
26
|
+
alignItems: "center",
|
|
27
|
+
justifyContent: "space-between",
|
|
28
|
+
padding: "14px 0",
|
|
29
|
+
flexShrink: 0,
|
|
30
|
+
borderBottom: "1px solid var(--dsw-alias-border-l2)"
|
|
31
|
+
};
|
|
32
|
+
const title = {
|
|
33
|
+
fontSize: 17,
|
|
34
|
+
fontWeight: 500,
|
|
35
|
+
color: "var(--dsw-alias-label-primary)",
|
|
36
|
+
margin: 0,
|
|
37
|
+
whiteSpace: "nowrap"
|
|
38
|
+
};
|
|
39
|
+
const countBadge = {
|
|
40
|
+
display: "inline-flex",
|
|
41
|
+
alignItems: "center",
|
|
42
|
+
justifyContent: "center",
|
|
43
|
+
minWidth: 22,
|
|
44
|
+
height: 22,
|
|
45
|
+
padding: "0 7px",
|
|
46
|
+
borderRadius: 999,
|
|
47
|
+
background: "var(--dsw-alias-bg-layer-3)",
|
|
48
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
49
|
+
fontSize: 11,
|
|
50
|
+
fontWeight: 500,
|
|
51
|
+
marginLeft: 8
|
|
52
|
+
};
|
|
53
|
+
const toolbar = {
|
|
54
|
+
display: "flex",
|
|
55
|
+
alignItems: "center",
|
|
56
|
+
gap: 8
|
|
57
|
+
};
|
|
58
|
+
const btnGhost = {
|
|
59
|
+
display: "inline-flex",
|
|
60
|
+
alignItems: "center",
|
|
61
|
+
gap: 6,
|
|
62
|
+
height: 30,
|
|
63
|
+
padding: "0 12px",
|
|
64
|
+
background: "transparent",
|
|
65
|
+
color: "var(--dsw-alias-label-primary)",
|
|
66
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
67
|
+
borderRadius: 6,
|
|
68
|
+
fontSize: 12,
|
|
69
|
+
cursor: "pointer"
|
|
70
|
+
};
|
|
71
|
+
const btnDanger = {
|
|
72
|
+
display: "inline-flex",
|
|
73
|
+
alignItems: "center",
|
|
74
|
+
gap: 6,
|
|
75
|
+
height: 26,
|
|
76
|
+
padding: "0 10px",
|
|
77
|
+
background: "transparent",
|
|
78
|
+
color: "var(--dsw-alias-state-error-primary)",
|
|
79
|
+
border: "1px solid var(--dsw-alias-state-error-primary)",
|
|
80
|
+
borderRadius: 6,
|
|
81
|
+
fontSize: 12,
|
|
82
|
+
cursor: "pointer"
|
|
83
|
+
};
|
|
84
|
+
const btnDangerSolid = {
|
|
85
|
+
display: "inline-flex",
|
|
86
|
+
alignItems: "center",
|
|
87
|
+
gap: 6,
|
|
88
|
+
height: 30,
|
|
89
|
+
padding: "0 14px",
|
|
90
|
+
background: "var(--dsw-alias-state-error-primary)",
|
|
91
|
+
color: "var(--dsw-alias-label-primary-foreground)",
|
|
92
|
+
border: "none",
|
|
93
|
+
borderRadius: 6,
|
|
94
|
+
fontSize: 12,
|
|
95
|
+
fontWeight: 500,
|
|
96
|
+
cursor: "pointer"
|
|
97
|
+
};
|
|
98
|
+
const tabs = {
|
|
99
|
+
display: "flex",
|
|
100
|
+
gap: 4,
|
|
101
|
+
borderBottom: "1px solid var(--dsw-alias-border-l2)",
|
|
102
|
+
paddingBottom: 0
|
|
103
|
+
};
|
|
104
|
+
const tabStyle = {
|
|
105
|
+
padding: "8px 14px",
|
|
106
|
+
fontSize: 13,
|
|
107
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
108
|
+
background: "transparent",
|
|
109
|
+
border: "none",
|
|
110
|
+
borderBottom: "2px solid transparent",
|
|
111
|
+
cursor: "pointer"
|
|
112
|
+
};
|
|
113
|
+
const tabStyleActive = {
|
|
114
|
+
...tabStyle,
|
|
115
|
+
color: "var(--dsw-alias-state-business-primary)",
|
|
116
|
+
borderBottomColor: "var(--dsw-alias-state-business-primary)",
|
|
117
|
+
fontWeight: 500
|
|
118
|
+
};
|
|
119
|
+
const list = {
|
|
120
|
+
display: "flex",
|
|
121
|
+
flexDirection: "column",
|
|
122
|
+
gap: 6
|
|
123
|
+
};
|
|
124
|
+
const row = {
|
|
125
|
+
display: "flex",
|
|
126
|
+
alignItems: "center",
|
|
127
|
+
gap: 12,
|
|
128
|
+
padding: "10px 12px",
|
|
129
|
+
borderRadius: 8,
|
|
130
|
+
background: "var(--dsw-alias-bg-layer-2)",
|
|
131
|
+
border: "1px solid var(--dsw-alias-border-l3)",
|
|
132
|
+
cursor: "pointer"
|
|
133
|
+
};
|
|
134
|
+
const rowMain = {
|
|
135
|
+
flex: 1,
|
|
136
|
+
minWidth: 0,
|
|
137
|
+
display: "flex",
|
|
138
|
+
flexDirection: "column",
|
|
139
|
+
gap: 2
|
|
140
|
+
};
|
|
141
|
+
const rowTitle = {
|
|
142
|
+
fontSize: 13,
|
|
143
|
+
fontWeight: 500,
|
|
144
|
+
color: "var(--dsw-alias-label-primary)",
|
|
145
|
+
overflow: "hidden",
|
|
146
|
+
textOverflow: "ellipsis",
|
|
147
|
+
whiteSpace: "nowrap"
|
|
148
|
+
};
|
|
149
|
+
const rowMeta = {
|
|
150
|
+
fontSize: 11,
|
|
151
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
152
|
+
};
|
|
153
|
+
const rowActions = {
|
|
154
|
+
display: "flex",
|
|
155
|
+
alignItems: "center",
|
|
156
|
+
gap: 6,
|
|
157
|
+
flexShrink: 0
|
|
158
|
+
};
|
|
159
|
+
const badge = {
|
|
160
|
+
display: "inline-flex",
|
|
161
|
+
alignItems: "center",
|
|
162
|
+
padding: "1px 8px",
|
|
163
|
+
borderRadius: 999,
|
|
164
|
+
fontSize: 10,
|
|
165
|
+
background: "var(--dsw-alias-bg-layer-3)",
|
|
166
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
167
|
+
};
|
|
168
|
+
const badgeArchived = {
|
|
169
|
+
...badge,
|
|
170
|
+
color: "var(--dsw-alias-state-warning-primary)",
|
|
171
|
+
background: "color-mix(in srgb, var(--dsw-alias-state-warning-primary) 12%, transparent)"
|
|
172
|
+
};
|
|
173
|
+
const badgeStray = {
|
|
174
|
+
...badge,
|
|
175
|
+
color: "var(--dsw-alias-state-error-primary)",
|
|
176
|
+
background: "color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent)"
|
|
177
|
+
};
|
|
178
|
+
const empty = {
|
|
179
|
+
display: "flex",
|
|
180
|
+
flexDirection: "column",
|
|
181
|
+
alignItems: "center",
|
|
182
|
+
justifyContent: "center",
|
|
183
|
+
gap: 6,
|
|
184
|
+
padding: "40px 24px",
|
|
185
|
+
border: "1px dashed var(--dsw-alias-border-l3)",
|
|
186
|
+
borderRadius: 10,
|
|
187
|
+
background: "var(--dsw-alias-bg-layer-1)",
|
|
188
|
+
textAlign: "center"
|
|
189
|
+
};
|
|
190
|
+
const emptyText = {
|
|
191
|
+
margin: 0,
|
|
192
|
+
fontSize: 13,
|
|
193
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
194
|
+
};
|
|
195
|
+
const errorStyle = {
|
|
196
|
+
fontSize: 12,
|
|
197
|
+
lineHeight: 1.5,
|
|
198
|
+
color: "var(--dsw-alias-state-error-primary)",
|
|
199
|
+
padding: "10px 14px",
|
|
200
|
+
background: "color-mix(in srgb, var(--dsw-alias-state-error-primary) 8%, transparent)",
|
|
201
|
+
border: "1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 40%, transparent)",
|
|
202
|
+
borderRadius: 8,
|
|
203
|
+
whiteSpace: "pre-wrap"
|
|
204
|
+
};
|
|
205
|
+
const hint = {
|
|
206
|
+
fontSize: 12,
|
|
207
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
208
|
+
lineHeight: 1.6
|
|
209
|
+
};
|
|
210
|
+
const modalOverlay = {
|
|
211
|
+
position: "fixed",
|
|
212
|
+
inset: 0,
|
|
213
|
+
background: "var(--dsw-alias-bg-mask-1)",
|
|
214
|
+
display: "flex",
|
|
215
|
+
alignItems: "center",
|
|
216
|
+
justifyContent: "center",
|
|
217
|
+
zIndex: 1e3
|
|
218
|
+
};
|
|
219
|
+
const modalCard = {
|
|
220
|
+
width: 420,
|
|
221
|
+
maxWidth: "86vw",
|
|
222
|
+
background: "var(--dsw-alias-bg-layer-3)",
|
|
223
|
+
color: "var(--dsw-alias-label-primary)",
|
|
224
|
+
borderRadius: 12,
|
|
225
|
+
boxShadow: "var(--dsw-shadow-lv2)",
|
|
226
|
+
padding: "20px 22px",
|
|
227
|
+
display: "flex",
|
|
228
|
+
flexDirection: "column",
|
|
229
|
+
gap: 12
|
|
230
|
+
};
|
|
231
|
+
const modalTitle = {
|
|
232
|
+
margin: 0,
|
|
233
|
+
fontSize: 15,
|
|
234
|
+
fontWeight: 600
|
|
235
|
+
};
|
|
236
|
+
const modalBody = {
|
|
237
|
+
fontSize: 13,
|
|
238
|
+
lineHeight: 1.6,
|
|
239
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
240
|
+
};
|
|
241
|
+
const modalBodyStrong = {
|
|
242
|
+
fontSize: 13,
|
|
243
|
+
lineHeight: 1.6,
|
|
244
|
+
color: "var(--dsw-alias-state-error-primary)",
|
|
245
|
+
fontWeight: 500
|
|
246
|
+
};
|
|
247
|
+
const modalActions = {
|
|
248
|
+
display: "flex",
|
|
249
|
+
justifyContent: "flex-end",
|
|
250
|
+
gap: 8,
|
|
251
|
+
marginTop: 4
|
|
252
|
+
};
|
|
253
|
+
const previewModalCard = {
|
|
254
|
+
...modalCard,
|
|
255
|
+
width: 560,
|
|
256
|
+
maxHeight: "70vh",
|
|
257
|
+
overflow: "hidden",
|
|
258
|
+
display: "flex",
|
|
259
|
+
flexDirection: "column"
|
|
260
|
+
};
|
|
261
|
+
const previewBody = {
|
|
262
|
+
flex: 1,
|
|
263
|
+
minHeight: 0,
|
|
264
|
+
overflowY: "auto",
|
|
265
|
+
display: "flex",
|
|
266
|
+
flexDirection: "column",
|
|
267
|
+
gap: 8,
|
|
268
|
+
padding: "4px 2px"
|
|
269
|
+
};
|
|
270
|
+
const msgBubble = {
|
|
271
|
+
maxWidth: "92%",
|
|
272
|
+
padding: "8px 12px",
|
|
273
|
+
borderRadius: 8,
|
|
274
|
+
fontSize: 12.5,
|
|
275
|
+
lineHeight: 1.6,
|
|
276
|
+
whiteSpace: "pre-wrap",
|
|
277
|
+
wordBreak: "break-word"
|
|
278
|
+
};
|
|
279
|
+
const msgUser = {
|
|
280
|
+
...msgBubble,
|
|
281
|
+
alignSelf: "flex-end",
|
|
282
|
+
background: "var(--dsw-alias-state-business-primary)",
|
|
283
|
+
color: "var(--dsw-alias-label-primary-foreground)"
|
|
284
|
+
};
|
|
285
|
+
const msgAssistant = {
|
|
286
|
+
...msgBubble,
|
|
287
|
+
alignSelf: "flex-start",
|
|
288
|
+
background: "var(--dsw-alias-bg-layer-3)",
|
|
289
|
+
color: "var(--dsw-alias-label-primary)"
|
|
290
|
+
};
|
|
291
|
+
const previewMeta = {
|
|
292
|
+
fontSize: 11,
|
|
293
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
294
|
+
};
|
|
295
|
+
function fmtBytes(n) {
|
|
296
|
+
if (n < 1024) return `${n} B`;
|
|
297
|
+
if (n < 1048576) return `${(n / 1024).toFixed(1)} KB`;
|
|
298
|
+
return `${(n / 1024 / 1024).toFixed(1)} MB`;
|
|
299
|
+
}
|
|
300
|
+
function fmtTime(ms) {
|
|
301
|
+
const d = new Date(ms);
|
|
302
|
+
const p = (x) => String(x).padStart(2, "0");
|
|
303
|
+
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`;
|
|
353
304
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
refresh();
|
|
359
|
-
return () => {
|
|
360
|
-
mountedRef.current = false;
|
|
361
|
-
};
|
|
362
|
-
}, []);
|
|
363
|
-
const filtered = (0, react.useMemo)(() => {
|
|
364
|
-
if (tab === "archived") return sessions.filter((s) => s.archived);
|
|
365
|
-
if (tab === "stray") return sessions.filter((s) => !s.archived && !s.active);
|
|
366
|
-
return sessions;
|
|
367
|
-
}, [sessions, tab]);
|
|
368
|
-
const counts = (0, react.useMemo)(() => ({
|
|
369
|
-
all: sessions.length,
|
|
370
|
-
archived: sessions.filter((s) => s.archived).length,
|
|
371
|
-
stray: sessions.filter((s) => !s.archived && !s.active).length
|
|
372
|
-
}), [sessions]);
|
|
373
|
-
const run = async (fn) => {
|
|
374
|
-
if (busy) return;
|
|
375
|
-
setBusy(true);
|
|
376
|
-
setError("");
|
|
377
|
-
const res = await fn();
|
|
378
|
-
setBusy(false);
|
|
379
|
-
if (!res.ok) {
|
|
380
|
-
setError(t("error.operationFailed", { message: res.message ?? "" }));
|
|
381
|
-
return;
|
|
305
|
+
/** 会话展示名:优先标题,回退 workspace 解码 + id 短前缀 */
|
|
306
|
+
function fmtSessionName(s) {
|
|
307
|
+
if (s.title) return s.title;
|
|
308
|
+
return `${s.workspaceDir.replace(/~003A/g, ":").replace(/^--/, "").replace(/--$/, "")} / ${s.id.length > 8 ? `${s.id.slice(0, 8)}…` : s.id}`;
|
|
382
309
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
310
|
+
/** 格式化时间戳 */
|
|
311
|
+
function fmtDateTime(ms) {
|
|
312
|
+
if (!ms) return "—";
|
|
313
|
+
const d = new Date(ms);
|
|
314
|
+
const p = (x) => String(x).padStart(2, "0");
|
|
315
|
+
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`;
|
|
316
|
+
}
|
|
317
|
+
function SessionManagerSection(props) {
|
|
318
|
+
const { api, t } = props;
|
|
319
|
+
const [sessions, setSessions] = (0, react.useState)([]);
|
|
320
|
+
const [loading, setLoading] = (0, react.useState)(true);
|
|
321
|
+
const [error, setError] = (0, react.useState)("");
|
|
322
|
+
const [tab, setTab] = (0, react.useState)("all");
|
|
323
|
+
const [confirmTarget, setConfirmTarget] = (0, react.useState)(null);
|
|
324
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
325
|
+
const [previewTarget, setPreviewTarget] = (0, react.useState)(null);
|
|
326
|
+
const [previewData, setPreviewData] = (0, react.useState)(null);
|
|
327
|
+
const [previewLoading, setPreviewLoading] = (0, react.useState)(false);
|
|
328
|
+
const mountedRef = (0, react.useRef)(true);
|
|
329
|
+
const refresh = async () => {
|
|
330
|
+
setLoading(true);
|
|
331
|
+
setError("");
|
|
332
|
+
const res = await api.list();
|
|
333
|
+
if (!mountedRef.current) return;
|
|
334
|
+
setLoading(false);
|
|
335
|
+
if (!res.ok) {
|
|
336
|
+
setError(t("error.loadFailed", { message: res.message ?? res.code ?? "" }));
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
setSessions(res.value ?? []);
|
|
340
|
+
};
|
|
341
|
+
(0, react.useEffect)(() => {
|
|
342
|
+
mountedRef.current = true;
|
|
343
|
+
refresh();
|
|
344
|
+
return () => {
|
|
345
|
+
mountedRef.current = false;
|
|
346
|
+
};
|
|
347
|
+
}, []);
|
|
348
|
+
const filtered = (0, react.useMemo)(() => {
|
|
349
|
+
if (tab === "archived") return sessions.filter((s) => s.archived);
|
|
350
|
+
if (tab === "stray") return sessions.filter((s) => !s.archived && !s.active);
|
|
351
|
+
return sessions;
|
|
352
|
+
}, [sessions, tab]);
|
|
353
|
+
const counts = (0, react.useMemo)(() => ({
|
|
354
|
+
all: sessions.length,
|
|
355
|
+
archived: sessions.filter((s) => s.archived).length,
|
|
356
|
+
stray: sessions.filter((s) => !s.archived && !s.active).length
|
|
357
|
+
}), [sessions]);
|
|
358
|
+
const run = async (fn) => {
|
|
407
359
|
if (busy) return;
|
|
408
360
|
setBusy(true);
|
|
409
361
|
setError("");
|
|
410
|
-
const
|
|
362
|
+
const res = await fn();
|
|
411
363
|
setBusy(false);
|
|
412
|
-
if (!ok)
|
|
364
|
+
if (!res.ok) {
|
|
365
|
+
setError(t("error.operationFailed", { message: res.message ?? "" }));
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
413
368
|
await refresh();
|
|
414
|
-
})();
|
|
415
|
-
return;
|
|
416
|
-
}
|
|
417
|
-
run(() => api.archive(s.id));
|
|
418
|
-
};
|
|
419
|
-
const handleUnarchive = (s) => {
|
|
420
|
-
run(() => api.unarchive(s.id));
|
|
421
|
-
};
|
|
422
|
-
const totalBytes = (0, react.useMemo)(() => sessions.reduce((a, s) => a + s.sizeBytes, 0), [sessions]);
|
|
423
|
-
return (0, react.createElement)("div", { style: page }, (0, react.createElement)("div", { style: header }, (0, react.createElement)("h2", { style: title }, t("header.title"), (0, react.createElement)("span", { style: countBadge }, t("header.count", {
|
|
424
|
-
count: sessions.length,
|
|
425
|
-
size: fmtBytes(totalBytes)
|
|
426
|
-
}))), (0, react.createElement)("div", { style: toolbar }, (0, react.createElement)("button", {
|
|
427
|
-
style: btnGhost,
|
|
428
|
-
onClick: () => void refresh(),
|
|
429
|
-
disabled: loading
|
|
430
|
-
}, loading ? t("btn.loading") : t("btn.refresh")))), (0, react.createElement)("div", { style: tabs }, [
|
|
431
|
-
"all",
|
|
432
|
-
"archived",
|
|
433
|
-
"stray"
|
|
434
|
-
].map((k) => (0, react.createElement)("button", {
|
|
435
|
-
key: k,
|
|
436
|
-
style: tab === k ? tabStyleActive : tabStyle,
|
|
437
|
-
onClick: () => setTab(k)
|
|
438
|
-
}, k === "all" ? t("tab.all", { count: counts.all }) : k === "archived" ? t("tab.archived", { count: counts.archived }) : t("tab.stray", { count: counts.stray })))), error ? (0, react.createElement)("div", { style: errorStyle }, error) : null, (0, react.createElement)("div", { style: hint }, t("hint.text")), (0, react.createElement)("div", { style: list }, filtered.map((s) => (0, react.createElement)("div", {
|
|
439
|
-
key: s.id,
|
|
440
|
-
style: row,
|
|
441
|
-
onClick: () => void openPreview(s)
|
|
442
|
-
}, (0, react.createElement)("div", { style: rowMain }, (0, react.createElement)("div", { style: rowTitle }, fmtSessionName(s)), (0, react.createElement)("div", { style: rowMeta }, `${fmtTime(s.mtimeMs)} · ${fmtBytes(s.sizeBytes)} · ${s.id}`)), s.archived ? (0, react.createElement)("span", { style: badgeArchived }, t("badge.archived")) : !s.active ? (0, react.createElement)("span", { style: badgeStray }, t("badge.stray")) : null, (0, react.createElement)("div", {
|
|
443
|
-
style: rowActions,
|
|
444
|
-
onClick: (e) => e.stopPropagation()
|
|
445
|
-
}, s.archived ? (0, react.createElement)("button", {
|
|
446
|
-
style: btnGhost,
|
|
447
|
-
onClick: () => handleUnarchive(s),
|
|
448
|
-
disabled: busy
|
|
449
|
-
}, t("btn.unarchive")) : (0, react.createElement)("button", {
|
|
450
|
-
style: btnGhost,
|
|
451
|
-
onClick: () => handleArchive(s),
|
|
452
|
-
disabled: busy
|
|
453
|
-
}, t("btn.archive")), (0, react.createElement)("button", {
|
|
454
|
-
style: btnDanger,
|
|
455
|
-
onClick: () => handleDelete(s),
|
|
456
|
-
disabled: busy
|
|
457
|
-
}, t("btn.delete")))))), !loading && filtered.length === 0 ? (0, react.createElement)("div", { style: empty }, (0, react.createElement)("p", { style: emptyText }, tab === "archived" ? t("empty.noArchived") : tab === "stray" ? t("empty.noStray") : t("empty.noSessions"))) : null, confirmTarget ? (0, react.createElement)("div", {
|
|
458
|
-
style: modalOverlay,
|
|
459
|
-
onClick: () => setConfirmTarget(null)
|
|
460
|
-
}, (0, react.createElement)("div", {
|
|
461
|
-
style: modalCard,
|
|
462
|
-
onClick: (e) => e.stopPropagation()
|
|
463
|
-
}, (0, react.createElement)("h3", { style: modalTitle }, t("modal.delete.title")), (0, react.createElement)("div", { style: modalBody }, t("modal.delete.body", { name: fmtSessionName(confirmTarget) })), (0, react.createElement)("div", { style: modalBodyStrong }, t("modal.delete.warning")), (0, react.createElement)("div", { style: modalActions }, (0, react.createElement)("button", {
|
|
464
|
-
style: btnGhost,
|
|
465
|
-
onClick: () => setConfirmTarget(null),
|
|
466
|
-
disabled: busy
|
|
467
|
-
}, t("btn.cancel")), (0, react.createElement)("button", {
|
|
468
|
-
style: btnDangerSolid,
|
|
469
|
-
onClick: () => void confirmDelete(),
|
|
470
|
-
disabled: busy
|
|
471
|
-
}, busy ? t("btn.deleting") : t("btn.permanentDelete"))))) : null, previewTarget ? (0, react.createElement)("div", {
|
|
472
|
-
style: modalOverlay,
|
|
473
|
-
onClick: () => setPreviewTarget(null)
|
|
474
|
-
}, (0, react.createElement)("div", {
|
|
475
|
-
style: previewModalCard,
|
|
476
|
-
onClick: (e) => e.stopPropagation()
|
|
477
|
-
}, (0, react.createElement)("h3", { style: modalTitle }, fmtSessionName(previewTarget)), (0, react.createElement)("div", { style: previewMeta }, t("preview.meta", {
|
|
478
|
-
id: previewTarget.id,
|
|
479
|
-
createdAt: fmtDateTime(previewData?.createdAt),
|
|
480
|
-
size: fmtBytes(previewTarget.sizeBytes)
|
|
481
|
-
})), (0, react.createElement)("div", { style: previewBody }, previewLoading ? (0, react.createElement)("p", { style: emptyText }, t("preview.loading")) : previewData && previewData.messages.length > 0 ? previewData.messages.map((m, i) => (0, react.createElement)("div", {
|
|
482
|
-
key: i,
|
|
483
|
-
style: m.role === "user" ? msgUser : msgAssistant
|
|
484
|
-
}, m.text)) : (0, react.createElement)("p", { style: emptyText }, t("preview.empty"))), (0, react.createElement)("div", { style: modalActions }, (0, react.createElement)("button", {
|
|
485
|
-
style: btnDanger,
|
|
486
|
-
onClick: () => {
|
|
487
|
-
setPreviewTarget(null);
|
|
488
|
-
handleDelete(previewTarget);
|
|
489
|
-
}
|
|
490
|
-
}, t("btn.deleteSession")), (0, react.createElement)("button", {
|
|
491
|
-
style: btnGhost,
|
|
492
|
-
onClick: () => setPreviewTarget(null)
|
|
493
|
-
}, t("btn.close"))))) : null);
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
//#endregion
|
|
497
|
-
//#region src/client/config-api.ts
|
|
498
|
-
const PACKAGE = "@lijian-ui/dsh-session-cleaner";
|
|
499
|
-
const SERVICE = "sessionCleanerRemote";
|
|
500
|
-
/** Identity codec — the host manifest owns strict validation. */
|
|
501
|
-
const identity = (value) => value;
|
|
502
|
-
const codec = (symbol) => ({
|
|
503
|
-
mode: "strict",
|
|
504
|
-
typeSymbol: symbol,
|
|
505
|
-
schema: { parse: identity }
|
|
506
|
-
});
|
|
507
|
-
/** Wire descriptors mirroring the host contribution (src/remote.ts). */
|
|
508
|
-
const CONTRIBUTION = {
|
|
509
|
-
package: PACKAGE,
|
|
510
|
-
descriptors: [
|
|
511
|
-
{
|
|
512
|
-
id: `${PACKAGE}#${SERVICE}/listSessions`,
|
|
513
|
-
service: SERVICE,
|
|
514
|
-
namespace: SERVICE,
|
|
515
|
-
method: "listSessions",
|
|
516
|
-
invocation: { kind: "direct" },
|
|
517
|
-
parameters: [],
|
|
518
|
-
result: codec(`${PACKAGE}#SessionList`)
|
|
519
|
-
},
|
|
520
|
-
{
|
|
521
|
-
id: `${PACKAGE}#${SERVICE}/previewSession`,
|
|
522
|
-
service: SERVICE,
|
|
523
|
-
namespace: SERVICE,
|
|
524
|
-
method: "previewSession",
|
|
525
|
-
invocation: { kind: "direct" },
|
|
526
|
-
parameters: [{
|
|
527
|
-
name: "sessionId",
|
|
528
|
-
wire: "sessionId",
|
|
529
|
-
source: "json",
|
|
530
|
-
codec: codec(`${PACKAGE}#SessionId`)
|
|
531
|
-
}, {
|
|
532
|
-
name: "limit",
|
|
533
|
-
wire: "limit",
|
|
534
|
-
source: "json",
|
|
535
|
-
acceptsUndefined: true,
|
|
536
|
-
codec: codec(`${PACKAGE}#Limit`)
|
|
537
|
-
}],
|
|
538
|
-
result: codec(`${PACKAGE}#SessionPreview`)
|
|
539
|
-
},
|
|
540
|
-
{
|
|
541
|
-
id: `${PACKAGE}#${SERVICE}/deleteSession`,
|
|
542
|
-
service: SERVICE,
|
|
543
|
-
namespace: SERVICE,
|
|
544
|
-
method: "deleteSession",
|
|
545
|
-
invocation: { kind: "direct" },
|
|
546
|
-
parameters: [{
|
|
547
|
-
name: "sessionId",
|
|
548
|
-
wire: "sessionId",
|
|
549
|
-
source: "json",
|
|
550
|
-
codec: codec(`${PACKAGE}#SessionId`)
|
|
551
|
-
}],
|
|
552
|
-
result: codec(`${PACKAGE}#DeleteResult`)
|
|
553
|
-
},
|
|
554
|
-
{
|
|
555
|
-
id: `${PACKAGE}#${SERVICE}/archiveSession`,
|
|
556
|
-
service: SERVICE,
|
|
557
|
-
namespace: SERVICE,
|
|
558
|
-
method: "archiveSession",
|
|
559
|
-
invocation: { kind: "direct" },
|
|
560
|
-
parameters: [{
|
|
561
|
-
name: "sessionId",
|
|
562
|
-
wire: "sessionId",
|
|
563
|
-
source: "json",
|
|
564
|
-
codec: codec(`${PACKAGE}#SessionId`)
|
|
565
|
-
}],
|
|
566
|
-
result: codec(`${PACKAGE}#ArchiveResult`)
|
|
567
|
-
},
|
|
568
|
-
{
|
|
569
|
-
id: `${PACKAGE}#${SERVICE}/unarchiveSession`,
|
|
570
|
-
service: SERVICE,
|
|
571
|
-
namespace: SERVICE,
|
|
572
|
-
method: "unarchiveSession",
|
|
573
|
-
invocation: { kind: "direct" },
|
|
574
|
-
parameters: [{
|
|
575
|
-
name: "sessionId",
|
|
576
|
-
wire: "sessionId",
|
|
577
|
-
source: "json",
|
|
578
|
-
codec: codec(`${PACKAGE}#SessionId`)
|
|
579
|
-
}],
|
|
580
|
-
result: codec(`${PACKAGE}#ArchiveResult`)
|
|
581
|
-
}
|
|
582
|
-
]
|
|
583
|
-
};
|
|
584
|
-
/**
|
|
585
|
-
* Create the session api bound to this browser plugin's context.
|
|
586
|
-
* Mounts the remote contribution once; every call awaits the mount, then
|
|
587
|
-
* invokes the namespace service and unwraps the envelope.
|
|
588
|
-
*/
|
|
589
|
-
function createSessionApi(ctx) {
|
|
590
|
-
const mount = ctx.remote.$mount(CONTRIBUTION);
|
|
591
|
-
const call = async (method, ...args) => {
|
|
592
|
-
try {
|
|
593
|
-
await mount;
|
|
594
|
-
const remote = ctx.get(`remote.${SERVICE}`);
|
|
595
|
-
const result = await remote[method](...args);
|
|
596
|
-
if (!result.ok) return {
|
|
597
|
-
ok: false,
|
|
598
|
-
code: result.error?.code ?? "remote-error",
|
|
599
|
-
message: result.error?.message ?? `${SERVICE}.${method} failed`
|
|
600
369
|
};
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
370
|
+
const openPreview = async (s) => {
|
|
371
|
+
setPreviewTarget(s);
|
|
372
|
+
setPreviewData(null);
|
|
373
|
+
setPreviewLoading(true);
|
|
374
|
+
const res = await api.preview(s.id);
|
|
375
|
+
setPreviewLoading(false);
|
|
376
|
+
if (res.ok) setPreviewData(res.value ?? null);
|
|
377
|
+
else setError(t("error.previewFailed", { message: res.message ?? "" }));
|
|
378
|
+
};
|
|
379
|
+
const handleDelete = (s) => {
|
|
380
|
+
setConfirmTarget(s);
|
|
381
|
+
};
|
|
382
|
+
const confirmDelete = async () => {
|
|
383
|
+
if (!confirmTarget) return;
|
|
384
|
+
const target = confirmTarget;
|
|
385
|
+
setConfirmTarget(null);
|
|
386
|
+
await run(() => api.remove(target.id));
|
|
387
|
+
};
|
|
388
|
+
const handleArchive = (s) => {
|
|
389
|
+
const { archiveSession } = props;
|
|
390
|
+
if (archiveSession) {
|
|
391
|
+
(async () => {
|
|
392
|
+
if (busy) return;
|
|
393
|
+
setBusy(true);
|
|
394
|
+
setError("");
|
|
395
|
+
const ok = await archiveSession(s.id);
|
|
396
|
+
setBusy(false);
|
|
397
|
+
if (!ok) setError(t("error.archiveFailed"));
|
|
398
|
+
await refresh();
|
|
399
|
+
})();
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
run(() => api.archive(s.id));
|
|
403
|
+
};
|
|
404
|
+
const handleUnarchive = (s) => {
|
|
405
|
+
run(() => api.unarchive(s.id));
|
|
406
|
+
};
|
|
407
|
+
const totalBytes = (0, react.useMemo)(() => sessions.reduce((a, s) => a + s.sizeBytes, 0), [sessions]);
|
|
408
|
+
return (0, react.createElement)("div", { style: page }, (0, react.createElement)("div", { style: header }, (0, react.createElement)("h2", { style: title }, t("header.title"), (0, react.createElement)("span", { style: countBadge }, t("header.count", {
|
|
409
|
+
count: sessions.length,
|
|
410
|
+
size: fmtBytes(totalBytes)
|
|
411
|
+
}))), (0, react.createElement)("div", { style: toolbar }, (0, react.createElement)("button", {
|
|
412
|
+
style: btnGhost,
|
|
413
|
+
onClick: () => void refresh(),
|
|
414
|
+
disabled: loading
|
|
415
|
+
}, loading ? t("btn.loading") : t("btn.refresh")))), (0, react.createElement)("div", { style: tabs }, [
|
|
416
|
+
"all",
|
|
417
|
+
"archived",
|
|
418
|
+
"stray"
|
|
419
|
+
].map((k) => (0, react.createElement)("button", {
|
|
420
|
+
key: k,
|
|
421
|
+
style: tab === k ? tabStyleActive : tabStyle,
|
|
422
|
+
onClick: () => setTab(k)
|
|
423
|
+
}, k === "all" ? t("tab.all", { count: counts.all }) : k === "archived" ? t("tab.archived", { count: counts.archived }) : t("tab.stray", { count: counts.stray })))), error ? (0, react.createElement)("div", { style: errorStyle }, error) : null, (0, react.createElement)("div", { style: hint }, t("hint.text")), (0, react.createElement)("div", { style: list }, filtered.map((s) => (0, react.createElement)("div", {
|
|
424
|
+
key: s.id,
|
|
425
|
+
style: row,
|
|
426
|
+
onClick: () => void openPreview(s)
|
|
427
|
+
}, (0, react.createElement)("div", { style: rowMain }, (0, react.createElement)("div", { style: rowTitle }, fmtSessionName(s)), (0, react.createElement)("div", { style: rowMeta }, `${fmtTime(s.mtimeMs)} · ${fmtBytes(s.sizeBytes)} · ${s.id}`)), s.archived ? (0, react.createElement)("span", { style: badgeArchived }, t("badge.archived")) : !s.active ? (0, react.createElement)("span", { style: badgeStray }, t("badge.stray")) : null, (0, react.createElement)("div", {
|
|
428
|
+
style: rowActions,
|
|
429
|
+
onClick: (e) => e.stopPropagation()
|
|
430
|
+
}, s.archived ? (0, react.createElement)("button", {
|
|
431
|
+
style: btnGhost,
|
|
432
|
+
onClick: () => handleUnarchive(s),
|
|
433
|
+
disabled: busy
|
|
434
|
+
}, t("btn.unarchive")) : (0, react.createElement)("button", {
|
|
435
|
+
style: btnGhost,
|
|
436
|
+
onClick: () => handleArchive(s),
|
|
437
|
+
disabled: busy
|
|
438
|
+
}, t("btn.archive")), (0, react.createElement)("button", {
|
|
439
|
+
style: btnDanger,
|
|
440
|
+
onClick: () => handleDelete(s),
|
|
441
|
+
disabled: busy
|
|
442
|
+
}, t("btn.delete")))))), !loading && filtered.length === 0 ? (0, react.createElement)("div", { style: empty }, (0, react.createElement)("p", { style: emptyText }, tab === "archived" ? t("empty.noArchived") : tab === "stray" ? t("empty.noStray") : t("empty.noSessions"))) : null, confirmTarget ? (0, react.createElement)("div", {
|
|
443
|
+
style: modalOverlay,
|
|
444
|
+
onClick: () => setConfirmTarget(null)
|
|
445
|
+
}, (0, react.createElement)("div", {
|
|
446
|
+
style: modalCard,
|
|
447
|
+
onClick: (e) => e.stopPropagation()
|
|
448
|
+
}, (0, react.createElement)("h3", { style: modalTitle }, t("modal.delete.title")), (0, react.createElement)("div", { style: modalBody }, t("modal.delete.body", { name: fmtSessionName(confirmTarget) })), (0, react.createElement)("div", { style: modalBodyStrong }, t("modal.delete.warning")), (0, react.createElement)("div", { style: modalActions }, (0, react.createElement)("button", {
|
|
449
|
+
style: btnGhost,
|
|
450
|
+
onClick: () => setConfirmTarget(null),
|
|
451
|
+
disabled: busy
|
|
452
|
+
}, t("btn.cancel")), (0, react.createElement)("button", {
|
|
453
|
+
style: btnDangerSolid,
|
|
454
|
+
onClick: () => void confirmDelete(),
|
|
455
|
+
disabled: busy
|
|
456
|
+
}, busy ? t("btn.deleting") : t("btn.permanentDelete"))))) : null, previewTarget ? (0, react.createElement)("div", {
|
|
457
|
+
style: modalOverlay,
|
|
458
|
+
onClick: () => setPreviewTarget(null)
|
|
459
|
+
}, (0, react.createElement)("div", {
|
|
460
|
+
style: previewModalCard,
|
|
461
|
+
onClick: (e) => e.stopPropagation()
|
|
462
|
+
}, (0, react.createElement)("h3", { style: modalTitle }, fmtSessionName(previewTarget)), (0, react.createElement)("div", { style: previewMeta }, t("preview.meta", {
|
|
463
|
+
id: previewTarget.id,
|
|
464
|
+
createdAt: fmtDateTime(previewData?.createdAt),
|
|
465
|
+
size: fmtBytes(previewTarget.sizeBytes)
|
|
466
|
+
})), (0, react.createElement)("div", { style: previewBody }, previewLoading ? (0, react.createElement)("p", { style: emptyText }, t("preview.loading")) : previewData && previewData.messages.length > 0 ? previewData.messages.map((m, i) => (0, react.createElement)("div", {
|
|
467
|
+
key: i,
|
|
468
|
+
style: m.role === "user" ? msgUser : msgAssistant
|
|
469
|
+
}, m.text)) : (0, react.createElement)("p", { style: emptyText }, t("preview.empty"))), (0, react.createElement)("div", { style: modalActions }, (0, react.createElement)("button", {
|
|
470
|
+
style: btnDanger,
|
|
471
|
+
onClick: () => {
|
|
472
|
+
setPreviewTarget(null);
|
|
473
|
+
handleDelete(previewTarget);
|
|
474
|
+
}
|
|
475
|
+
}, t("btn.deleteSession")), (0, react.createElement)("button", {
|
|
476
|
+
style: btnGhost,
|
|
477
|
+
onClick: () => setPreviewTarget(null)
|
|
478
|
+
}, t("btn.close"))))) : null);
|
|
479
|
+
}
|
|
480
|
+
//#endregion
|
|
481
|
+
//#region src/client/config-api.ts
|
|
482
|
+
const PACKAGE = "@lijian-ui/dsh-session-cleaner";
|
|
483
|
+
const SERVICE = "sessionCleanerRemote";
|
|
484
|
+
/** Identity codec — the host manifest owns strict validation. */
|
|
485
|
+
const identity = (value) => value;
|
|
486
|
+
const codec = (symbol) => ({
|
|
487
|
+
mode: "strict",
|
|
488
|
+
typeSymbol: symbol,
|
|
489
|
+
schema: { parse: identity }
|
|
490
|
+
});
|
|
491
|
+
/** Wire descriptors mirroring the host contribution (src/remote.ts). */
|
|
492
|
+
const CONTRIBUTION = {
|
|
493
|
+
package: PACKAGE,
|
|
494
|
+
descriptors: [
|
|
495
|
+
{
|
|
496
|
+
id: `${PACKAGE}#${SERVICE}/listSessions`,
|
|
497
|
+
service: SERVICE,
|
|
498
|
+
namespace: SERVICE,
|
|
499
|
+
method: "listSessions",
|
|
500
|
+
invocation: { kind: "direct" },
|
|
501
|
+
parameters: [],
|
|
502
|
+
result: codec(`${PACKAGE}#SessionList`)
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
id: `${PACKAGE}#${SERVICE}/previewSession`,
|
|
506
|
+
service: SERVICE,
|
|
507
|
+
namespace: SERVICE,
|
|
508
|
+
method: "previewSession",
|
|
509
|
+
invocation: { kind: "direct" },
|
|
510
|
+
parameters: [{
|
|
511
|
+
name: "sessionId",
|
|
512
|
+
wire: "sessionId",
|
|
513
|
+
source: "json",
|
|
514
|
+
codec: codec(`${PACKAGE}#SessionId`)
|
|
515
|
+
}, {
|
|
516
|
+
name: "limit",
|
|
517
|
+
wire: "limit",
|
|
518
|
+
source: "json",
|
|
519
|
+
acceptsUndefined: true,
|
|
520
|
+
codec: codec(`${PACKAGE}#Limit`)
|
|
521
|
+
}],
|
|
522
|
+
result: codec(`${PACKAGE}#SessionPreview`)
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
id: `${PACKAGE}#${SERVICE}/deleteSession`,
|
|
526
|
+
service: SERVICE,
|
|
527
|
+
namespace: SERVICE,
|
|
528
|
+
method: "deleteSession",
|
|
529
|
+
invocation: { kind: "direct" },
|
|
530
|
+
parameters: [{
|
|
531
|
+
name: "sessionId",
|
|
532
|
+
wire: "sessionId",
|
|
533
|
+
source: "json",
|
|
534
|
+
codec: codec(`${PACKAGE}#SessionId`)
|
|
535
|
+
}],
|
|
536
|
+
result: codec(`${PACKAGE}#DeleteResult`)
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
id: `${PACKAGE}#${SERVICE}/archiveSession`,
|
|
540
|
+
service: SERVICE,
|
|
541
|
+
namespace: SERVICE,
|
|
542
|
+
method: "archiveSession",
|
|
543
|
+
invocation: { kind: "direct" },
|
|
544
|
+
parameters: [{
|
|
545
|
+
name: "sessionId",
|
|
546
|
+
wire: "sessionId",
|
|
547
|
+
source: "json",
|
|
548
|
+
codec: codec(`${PACKAGE}#SessionId`)
|
|
549
|
+
}],
|
|
550
|
+
result: codec(`${PACKAGE}#ArchiveResult`)
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
id: `${PACKAGE}#${SERVICE}/unarchiveSession`,
|
|
554
|
+
service: SERVICE,
|
|
555
|
+
namespace: SERVICE,
|
|
556
|
+
method: "unarchiveSession",
|
|
557
|
+
invocation: { kind: "direct" },
|
|
558
|
+
parameters: [{
|
|
559
|
+
name: "sessionId",
|
|
560
|
+
wire: "sessionId",
|
|
561
|
+
source: "json",
|
|
562
|
+
codec: codec(`${PACKAGE}#SessionId`)
|
|
563
|
+
}],
|
|
564
|
+
result: codec(`${PACKAGE}#ArchiveResult`)
|
|
565
|
+
}
|
|
566
|
+
]
|
|
567
|
+
};
|
|
568
|
+
/**
|
|
569
|
+
* Create the session api bound to this browser plugin's context.
|
|
570
|
+
* Mounts the remote contribution once; every call awaits the mount, then
|
|
571
|
+
* invokes the namespace service and unwraps the envelope.
|
|
572
|
+
*/
|
|
573
|
+
function createSessionApi(ctx) {
|
|
574
|
+
const mount = ctx.remote.$mount(CONTRIBUTION);
|
|
575
|
+
const call = async (method, ...args) => {
|
|
576
|
+
try {
|
|
577
|
+
await mount;
|
|
578
|
+
const result = await ctx.get(`remote.${SERVICE}`)[method](...args);
|
|
579
|
+
if (!result.ok) return {
|
|
580
|
+
ok: false,
|
|
581
|
+
code: result.error?.code ?? "remote-error",
|
|
582
|
+
message: result.error?.message ?? `${SERVICE}.${method} failed`
|
|
583
|
+
};
|
|
584
|
+
return {
|
|
585
|
+
ok: true,
|
|
586
|
+
value: result.value
|
|
587
|
+
};
|
|
588
|
+
} catch (err) {
|
|
589
|
+
return {
|
|
590
|
+
ok: false,
|
|
591
|
+
code: "remote-error",
|
|
592
|
+
message: err instanceof Error ? err.message : String(err)
|
|
593
|
+
};
|
|
594
|
+
}
|
|
604
595
|
};
|
|
605
|
-
} catch (err) {
|
|
606
596
|
return {
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
597
|
+
list: () => call("listSessions"),
|
|
598
|
+
preview: (sessionId) => call("previewSession", sessionId, void 0),
|
|
599
|
+
remove: (sessionId) => call("deleteSession", sessionId),
|
|
600
|
+
archive: (sessionId) => call("archiveSession", sessionId),
|
|
601
|
+
unarchive: (sessionId) => call("unarchiveSession", sessionId)
|
|
610
602
|
};
|
|
611
603
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
"modal.delete.body": "确定要删除会话「{name}」吗?",
|
|
652
|
-
"modal.delete.warning": "此操作将永久删除该会话及其日志文件,无法恢复(不会进入回收站)。",
|
|
653
|
-
"btn.cancel": "取消",
|
|
654
|
-
"btn.deleting": "删除中…",
|
|
655
|
-
"btn.permanentDelete": "永久删除",
|
|
656
|
-
"preview.meta": "ID: {id} · 创建: {createdAt} · {size}",
|
|
657
|
-
"preview.loading": "加载中…",
|
|
658
|
-
"preview.empty": "(该会话暂无文本消息)",
|
|
659
|
-
"btn.deleteSession": "删除此会话",
|
|
660
|
-
"btn.close": "关闭"
|
|
661
|
-
};
|
|
662
|
-
const en = {
|
|
663
|
-
"section.label": "Session Manager",
|
|
664
|
-
"header.title": "Session Manager",
|
|
665
|
-
"header.count": "{count} sessions · {size}",
|
|
666
|
-
"btn.refresh": "Refresh",
|
|
667
|
-
"btn.loading": "Loading…",
|
|
668
|
-
"tab.all": "All ({count})",
|
|
669
|
-
"tab.archived": "Archived ({count})",
|
|
670
|
-
"tab.stray": "Stray ({count})",
|
|
671
|
-
"badge.archived": "Archived",
|
|
672
|
-
"badge.stray": "Stray",
|
|
673
|
-
"btn.archive": "Archive",
|
|
674
|
-
"btn.unarchive": "Restore",
|
|
675
|
-
"btn.delete": "Delete",
|
|
676
|
-
"hint.text": "Sessions are stored as files in ~/.dsh/sessions. Archiving hides it from the official list (integrated with official archive). Deletion is permanent and irreversible.",
|
|
677
|
-
"empty.noSessions": "No sessions",
|
|
678
|
-
"empty.noArchived": "No archived sessions",
|
|
679
|
-
"empty.noStray": "No stray sessions",
|
|
680
|
-
"error.loadFailed": "Failed to load sessions: {message}",
|
|
681
|
-
"error.operationFailed": "Operation failed: {message}",
|
|
682
|
-
"error.archiveFailed": "Archive failed (official API unavailable, please retry or restart dsh)",
|
|
683
|
-
"error.previewFailed": "Failed to load preview: {message}",
|
|
684
|
-
"modal.delete.title": "Delete Session?",
|
|
685
|
-
"modal.delete.body": "Are you sure you want to delete \"{name}\"?",
|
|
686
|
-
"modal.delete.warning": "This action will permanently delete the session and its log files. This cannot be undone (no trash recovery).",
|
|
687
|
-
"btn.cancel": "Cancel",
|
|
688
|
-
"btn.deleting": "Deleting…",
|
|
689
|
-
"btn.permanentDelete": "Delete Forever",
|
|
690
|
-
"preview.meta": "ID: {id} · Created: {createdAt} · {size}",
|
|
691
|
-
"preview.loading": "Loading…",
|
|
692
|
-
"preview.empty": "(No text messages in this session)",
|
|
693
|
-
"btn.deleteSession": "Delete this session",
|
|
694
|
-
"btn.close": "Close"
|
|
695
|
-
};
|
|
696
|
-
|
|
697
|
-
//#endregion
|
|
698
|
-
//#region src/client/index.ts
|
|
699
|
-
/** Section identity in the Settings left-nav rail. */
|
|
700
|
-
const SECTION_ID = "session-cleaner";
|
|
701
|
-
/** Sits below Models (10) and IM 通道 (20). */
|
|
702
|
-
const SECTION_ORDER = 30;
|
|
703
|
-
/** i18n namespace — matches the plugin package name for disambiguation. */
|
|
704
|
-
const I18N_NS = "session-cleaner";
|
|
705
|
-
/** Services this browser plugin requires (cordis fiber inject). */
|
|
706
|
-
const inject = [
|
|
707
|
-
"slots",
|
|
708
|
-
"locale",
|
|
709
|
-
"connection",
|
|
710
|
-
"remote"
|
|
711
|
-
];
|
|
712
|
-
/**
|
|
713
|
-
* Mount the session management settings page.
|
|
714
|
-
* @param ctx - the browser plugin context (guarded facade).
|
|
715
|
-
*/
|
|
716
|
-
function apply(ctx) {
|
|
717
|
-
const api = createSessionApi(ctx);
|
|
718
|
-
ctx.effect(() => ctx.locale.register(I18N_NS, {
|
|
719
|
-
zh,
|
|
720
|
-
en
|
|
721
|
-
}), "session-cleaner: client dictionaries");
|
|
722
|
-
const t = ctx.locale.bind(I18N_NS);
|
|
723
|
-
let officialArchive = null;
|
|
724
|
-
ctx.inject(["workspaces"], (sctx) => {
|
|
725
|
-
officialArchive = async (sessionId) => {
|
|
726
|
-
try {
|
|
727
|
-
const res = await sctx.workspaces.archiveSession(sessionId);
|
|
728
|
-
return res.ok === true;
|
|
729
|
-
} catch {
|
|
730
|
-
return false;
|
|
731
|
-
}
|
|
604
|
+
//#endregion
|
|
605
|
+
//#region src/client/client-i18n.ts
|
|
606
|
+
/**
|
|
607
|
+
* session-cleaner 客户端翻译字典
|
|
608
|
+
* zh 为 key 源,en 与 zh 键集完整对照。
|
|
609
|
+
*/
|
|
610
|
+
const zh = {
|
|
611
|
+
"section.label": "会话管理",
|
|
612
|
+
"header.title": "会话管理",
|
|
613
|
+
"header.count": "{count} 个会话 · {size}",
|
|
614
|
+
"btn.refresh": "刷新",
|
|
615
|
+
"btn.loading": "加载中…",
|
|
616
|
+
"tab.all": "全部 ({count})",
|
|
617
|
+
"tab.archived": "已归档 ({count})",
|
|
618
|
+
"tab.stray": "游离 ({count})",
|
|
619
|
+
"badge.archived": "已归档",
|
|
620
|
+
"badge.stray": "游离",
|
|
621
|
+
"btn.archive": "归档",
|
|
622
|
+
"btn.unarchive": "恢复",
|
|
623
|
+
"btn.delete": "删除",
|
|
624
|
+
"hint.text": "会话以文件形式保存在 ~/.dsh/sessions。归档会从官方列表隐藏(与官方归档打通),删除为永久删除,不可恢复。",
|
|
625
|
+
"empty.noSessions": "没有会话",
|
|
626
|
+
"empty.noArchived": "没有已归档的会话",
|
|
627
|
+
"empty.noStray": "没有游离会话",
|
|
628
|
+
"error.loadFailed": "加载会话失败:{message}",
|
|
629
|
+
"error.operationFailed": "操作失败:{message}",
|
|
630
|
+
"error.archiveFailed": "归档失败(官方 API 不可用,请重试或重启 dsh)",
|
|
631
|
+
"error.previewFailed": "加载预览失败:{message}",
|
|
632
|
+
"modal.delete.title": "删除会话?",
|
|
633
|
+
"modal.delete.body": "确定要删除会话「{name}」吗?",
|
|
634
|
+
"modal.delete.warning": "此操作将永久删除该会话及其日志文件,无法恢复(不会进入回收站)。",
|
|
635
|
+
"btn.cancel": "取消",
|
|
636
|
+
"btn.deleting": "删除中…",
|
|
637
|
+
"btn.permanentDelete": "永久删除",
|
|
638
|
+
"preview.meta": "ID: {id} · 创建: {createdAt} · {size}",
|
|
639
|
+
"preview.loading": "加载中…",
|
|
640
|
+
"preview.empty": "(该会话暂无文本消息)",
|
|
641
|
+
"btn.deleteSession": "删除此会话",
|
|
642
|
+
"btn.close": "关闭"
|
|
732
643
|
};
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
644
|
+
const en = {
|
|
645
|
+
"section.label": "Session Manager",
|
|
646
|
+
"header.title": "Session Manager",
|
|
647
|
+
"header.count": "{count} sessions · {size}",
|
|
648
|
+
"btn.refresh": "Refresh",
|
|
649
|
+
"btn.loading": "Loading…",
|
|
650
|
+
"tab.all": "All ({count})",
|
|
651
|
+
"tab.archived": "Archived ({count})",
|
|
652
|
+
"tab.stray": "Stray ({count})",
|
|
653
|
+
"badge.archived": "Archived",
|
|
654
|
+
"badge.stray": "Stray",
|
|
655
|
+
"btn.archive": "Archive",
|
|
656
|
+
"btn.unarchive": "Restore",
|
|
657
|
+
"btn.delete": "Delete",
|
|
658
|
+
"hint.text": "Sessions are stored as files in ~/.dsh/sessions. Archiving hides it from the official list (integrated with official archive). Deletion is permanent and irreversible.",
|
|
659
|
+
"empty.noSessions": "No sessions",
|
|
660
|
+
"empty.noArchived": "No archived sessions",
|
|
661
|
+
"empty.noStray": "No stray sessions",
|
|
662
|
+
"error.loadFailed": "Failed to load sessions: {message}",
|
|
663
|
+
"error.operationFailed": "Operation failed: {message}",
|
|
664
|
+
"error.archiveFailed": "Archive failed (official API unavailable, please retry or restart dsh)",
|
|
665
|
+
"error.previewFailed": "Failed to load preview: {message}",
|
|
666
|
+
"modal.delete.title": "Delete Session?",
|
|
667
|
+
"modal.delete.body": "Are you sure you want to delete \"{name}\"?",
|
|
668
|
+
"modal.delete.warning": "This action will permanently delete the session and its log files. This cannot be undone (no trash recovery).",
|
|
669
|
+
"btn.cancel": "Cancel",
|
|
670
|
+
"btn.deleting": "Deleting…",
|
|
671
|
+
"btn.permanentDelete": "Delete Forever",
|
|
672
|
+
"preview.meta": "ID: {id} · Created: {createdAt} · {size}",
|
|
673
|
+
"preview.loading": "Loading…",
|
|
674
|
+
"preview.empty": "(No text messages in this session)",
|
|
675
|
+
"btn.deleteSession": "Delete this session",
|
|
676
|
+
"btn.close": "Close"
|
|
677
|
+
};
|
|
678
|
+
//#endregion
|
|
679
|
+
//#region src/client/index.ts
|
|
680
|
+
/** Section identity in the Settings left-nav rail. */
|
|
681
|
+
const SECTION_ID = "session-cleaner";
|
|
682
|
+
/** Sits below Models (10) and IM 通道 (20). */
|
|
683
|
+
const SECTION_ORDER = 30;
|
|
684
|
+
/** i18n namespace — matches the plugin package name for disambiguation. */
|
|
685
|
+
const I18N_NS = "session-cleaner";
|
|
686
|
+
/** Services this browser plugin requires (cordis fiber inject). */
|
|
687
|
+
const inject = [
|
|
688
|
+
"slots",
|
|
689
|
+
"locale",
|
|
690
|
+
"connection",
|
|
691
|
+
"remote"
|
|
692
|
+
];
|
|
693
|
+
/**
|
|
694
|
+
* Mount the session management settings page.
|
|
695
|
+
* @param ctx - the browser plugin context (guarded facade).
|
|
696
|
+
*/
|
|
697
|
+
function apply(ctx) {
|
|
698
|
+
const api = createSessionApi(ctx);
|
|
699
|
+
ctx.effect(() => ctx.locale.register(I18N_NS, {
|
|
700
|
+
zh,
|
|
701
|
+
en
|
|
702
|
+
}), "session-cleaner: client dictionaries");
|
|
703
|
+
const t = ctx.locale.bind(I18N_NS);
|
|
704
|
+
let officialArchive = null;
|
|
705
|
+
ctx.inject(["workspaces"], (sctx) => {
|
|
706
|
+
officialArchive = async (sessionId) => {
|
|
707
|
+
try {
|
|
708
|
+
return (await sctx.workspaces.archiveSession(sessionId)).ok === true;
|
|
709
|
+
} catch {
|
|
710
|
+
return false;
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
});
|
|
714
|
+
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
715
|
+
name: "settings.section",
|
|
716
|
+
id: SECTION_ID,
|
|
717
|
+
order: SECTION_ORDER,
|
|
718
|
+
label: () => t("section.label"),
|
|
719
|
+
inject: () => ({
|
|
720
|
+
api,
|
|
721
|
+
t,
|
|
722
|
+
archiveSession: (sessionId) => officialArchive ? officialArchive(sessionId) : api.archive(sessionId).then((r) => r.ok)
|
|
723
|
+
})
|
|
724
|
+
}, SessionManagerSection));
|
|
725
|
+
const navLabels = /* @__PURE__ */ new Set([zh["section.label"], en["section.label"]]);
|
|
726
|
+
const sessionNavSvg = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" data-scn-nav=\"1\"><path d=\"M8.00003 0.3237C3.76075 0.3237 0.32373 3.76072 0.32373 8C0.32373 9.17603 0.589121 10.2922 1.0632 11.2901L1.35291 11.8989L2.5705 11.3205L2.28079 10.7117C1.89079 9.89074 1.67301 8.97167 1.67301 8C1.67301 4.50546 4.50549 1.67298 8.00003 1.67298C11.4946 1.67298 14.3271 4.50546 14.3271 8C14.3271 11.4945 11.4946 14.327 8.00003 14.327C7.28473 14.327 6.76077 14.277 6.29621 14.1487C5.83857 14.0224 5.40441 13.8109 4.88514 13.4488C4.12569 12.919 3.03778 12.7316 2.141 13.2978L2.12682 13.307L2.11264 13.3171L1.34886 13.854L1.79659 15.188L2.86122 14.4384C3.19068 14.2305 3.68325 14.2542 4.11326 14.5539C4.72789 14.9826 5.30042 15.2724 5.93762 15.4484C6.56803 15.6224 7.22776 15.6763 8.00003 15.6763C12.2393 15.6763 15.6763 12.2393 15.6763 8C15.6763 3.76072 12.2393 0.3237 8.00003 0.3237ZM7.32033 4.82535V7.32536H4.82538V8.67464H7.32033V11.1747H8.6696V8.67464H11.1747V7.32536H8.6696V4.82535H7.32033Z\" fill=\"currentColor\"/></svg>";
|
|
727
|
+
const patchNavIcon = () => {
|
|
728
|
+
if (typeof document === "undefined") return;
|
|
729
|
+
for (const span of Array.from(document.querySelectorAll("button > span"))) {
|
|
730
|
+
if (!navLabels.has(span.textContent ?? "")) continue;
|
|
731
|
+
const button = span.parentElement;
|
|
732
|
+
if (button === null) continue;
|
|
733
|
+
const stale = button.querySelector("svg:not([data-scn-nav])");
|
|
734
|
+
if (stale === null) continue;
|
|
735
|
+
const template = document.createElement("template");
|
|
736
|
+
template.innerHTML = sessionNavSvg;
|
|
737
|
+
const next = template.content.firstElementChild;
|
|
738
|
+
if (next === null) continue;
|
|
739
|
+
const cls = stale.getAttribute("class");
|
|
740
|
+
if (cls !== null) next.setAttribute("class", cls);
|
|
741
|
+
stale.replaceWith(next);
|
|
742
|
+
}
|
|
743
|
+
};
|
|
744
|
+
if (typeof document !== "undefined" && typeof MutationObserver !== "undefined" && document.body !== null) {
|
|
745
|
+
let scheduled = false;
|
|
746
|
+
const schedule = () => {
|
|
747
|
+
if (scheduled) return;
|
|
748
|
+
scheduled = true;
|
|
749
|
+
queueMicrotask(() => {
|
|
750
|
+
scheduled = false;
|
|
751
|
+
patchNavIcon();
|
|
752
|
+
});
|
|
753
|
+
};
|
|
754
|
+
patchNavIcon();
|
|
755
|
+
new MutationObserver(schedule).observe(document.body, {
|
|
756
|
+
childList: true,
|
|
757
|
+
subtree: true
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
//#endregion
|
|
762
|
+
exports.apply = apply;
|
|
763
|
+
exports.inject = inject;
|
|
764
|
+
return module.exports;
|
|
765
|
+
}
|
|
766
|
+
});
|
|
746
767
|
|
|
747
|
-
//#endregion
|
|
748
|
-
exports.apply = apply;
|
|
749
|
-
exports.inject = inject;
|
|
750
|
-
return module.exports; } });
|
|
751
768
|
//# sourceMappingURL=client.js.map
|