@lijian-ui/dsh-session-cleaner 0.1.1 → 0.1.3
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 +151 -62
- package/lib/index.js +44 -6
- package/package.json +8 -8
package/lib/client.js
CHANGED
|
@@ -27,12 +27,12 @@ window.__ModuleLoader__.load({
|
|
|
27
27
|
justifyContent: "space-between",
|
|
28
28
|
padding: "14px 0",
|
|
29
29
|
flexShrink: 0,
|
|
30
|
-
borderBottom: "1px solid var(--dsw-alias-border-l2
|
|
30
|
+
borderBottom: "1px solid var(--dsw-alias-border-l2)"
|
|
31
31
|
};
|
|
32
32
|
const title = {
|
|
33
33
|
fontSize: 17,
|
|
34
34
|
fontWeight: 500,
|
|
35
|
-
color: "var(--dsw-alias-label-primary
|
|
35
|
+
color: "var(--dsw-alias-label-primary)",
|
|
36
36
|
margin: 0,
|
|
37
37
|
whiteSpace: "nowrap"
|
|
38
38
|
};
|
|
@@ -44,8 +44,8 @@ window.__ModuleLoader__.load({
|
|
|
44
44
|
height: 22,
|
|
45
45
|
padding: "0 7px",
|
|
46
46
|
borderRadius: 999,
|
|
47
|
-
background: "var(--dsw-alias-bg-layer-3
|
|
48
|
-
color: "var(--dsw-alias-label-secondary
|
|
47
|
+
background: "var(--dsw-alias-bg-layer-3)",
|
|
48
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
49
49
|
fontSize: 11,
|
|
50
50
|
fontWeight: 500,
|
|
51
51
|
marginLeft: 8
|
|
@@ -62,8 +62,8 @@ window.__ModuleLoader__.load({
|
|
|
62
62
|
height: 30,
|
|
63
63
|
padding: "0 12px",
|
|
64
64
|
background: "transparent",
|
|
65
|
-
color: "var(--dsw-alias-label-primary
|
|
66
|
-
border: "1px solid var(--dsw-alias-border-l2
|
|
65
|
+
color: "var(--dsw-alias-label-primary)",
|
|
66
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
67
67
|
borderRadius: 6,
|
|
68
68
|
fontSize: 12,
|
|
69
69
|
cursor: "pointer"
|
|
@@ -75,8 +75,8 @@ window.__ModuleLoader__.load({
|
|
|
75
75
|
height: 26,
|
|
76
76
|
padding: "0 10px",
|
|
77
77
|
background: "transparent",
|
|
78
|
-
color: "var(--dsw-alias-state-error-primary
|
|
79
|
-
border: "1px solid var(--dsw-alias-state-error-primary
|
|
78
|
+
color: "var(--dsw-alias-state-error-primary)",
|
|
79
|
+
border: "1px solid var(--dsw-alias-state-error-primary)",
|
|
80
80
|
borderRadius: 6,
|
|
81
81
|
fontSize: 12,
|
|
82
82
|
cursor: "pointer"
|
|
@@ -87,8 +87,8 @@ window.__ModuleLoader__.load({
|
|
|
87
87
|
gap: 6,
|
|
88
88
|
height: 30,
|
|
89
89
|
padding: "0 14px",
|
|
90
|
-
background: "var(--dsw-alias-state-error-primary
|
|
91
|
-
color: "
|
|
90
|
+
background: "var(--dsw-alias-state-error-primary)",
|
|
91
|
+
color: "var(--dsw-alias-label-primary-foreground)",
|
|
92
92
|
border: "none",
|
|
93
93
|
borderRadius: 6,
|
|
94
94
|
fontSize: 12,
|
|
@@ -98,13 +98,13 @@ window.__ModuleLoader__.load({
|
|
|
98
98
|
const tabs = {
|
|
99
99
|
display: "flex",
|
|
100
100
|
gap: 4,
|
|
101
|
-
borderBottom: "1px solid var(--dsw-alias-border-l2
|
|
101
|
+
borderBottom: "1px solid var(--dsw-alias-border-l2)",
|
|
102
102
|
paddingBottom: 0
|
|
103
103
|
};
|
|
104
104
|
const tabStyle = {
|
|
105
105
|
padding: "8px 14px",
|
|
106
106
|
fontSize: 13,
|
|
107
|
-
color: "var(--dsw-alias-label-secondary
|
|
107
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
108
108
|
background: "transparent",
|
|
109
109
|
border: "none",
|
|
110
110
|
borderBottom: "2px solid transparent",
|
|
@@ -112,8 +112,8 @@ window.__ModuleLoader__.load({
|
|
|
112
112
|
};
|
|
113
113
|
const tabStyleActive = {
|
|
114
114
|
...tabStyle,
|
|
115
|
-
color: "var(--dsw-alias-state-business-primary
|
|
116
|
-
borderBottomColor: "var(--dsw-alias-state-business-primary
|
|
115
|
+
color: "var(--dsw-alias-state-business-primary)",
|
|
116
|
+
borderBottomColor: "var(--dsw-alias-state-business-primary)",
|
|
117
117
|
fontWeight: 500
|
|
118
118
|
};
|
|
119
119
|
const list = {
|
|
@@ -127,8 +127,8 @@ window.__ModuleLoader__.load({
|
|
|
127
127
|
gap: 12,
|
|
128
128
|
padding: "10px 12px",
|
|
129
129
|
borderRadius: 8,
|
|
130
|
-
background: "var(--dsw-alias-bg-layer-2
|
|
131
|
-
border: "1px solid var(--dsw-alias-border-l3
|
|
130
|
+
background: "var(--dsw-alias-bg-layer-2)",
|
|
131
|
+
border: "1px solid var(--dsw-alias-border-l3)",
|
|
132
132
|
cursor: "pointer"
|
|
133
133
|
};
|
|
134
134
|
const rowMain = {
|
|
@@ -141,14 +141,14 @@ window.__ModuleLoader__.load({
|
|
|
141
141
|
const rowTitle = {
|
|
142
142
|
fontSize: 13,
|
|
143
143
|
fontWeight: 500,
|
|
144
|
-
color: "var(--dsw-alias-label-primary
|
|
144
|
+
color: "var(--dsw-alias-label-primary)",
|
|
145
145
|
overflow: "hidden",
|
|
146
146
|
textOverflow: "ellipsis",
|
|
147
147
|
whiteSpace: "nowrap"
|
|
148
148
|
};
|
|
149
149
|
const rowMeta = {
|
|
150
150
|
fontSize: 11,
|
|
151
|
-
color: "var(--dsw-alias-label-secondary
|
|
151
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
152
152
|
};
|
|
153
153
|
const rowActions = {
|
|
154
154
|
display: "flex",
|
|
@@ -162,18 +162,18 @@ window.__ModuleLoader__.load({
|
|
|
162
162
|
padding: "1px 8px",
|
|
163
163
|
borderRadius: 999,
|
|
164
164
|
fontSize: 10,
|
|
165
|
-
background: "var(--dsw-alias-bg-layer-3
|
|
166
|
-
color: "var(--dsw-alias-label-secondary
|
|
165
|
+
background: "var(--dsw-alias-bg-layer-3)",
|
|
166
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
167
167
|
};
|
|
168
168
|
const badgeArchived = {
|
|
169
169
|
...badge,
|
|
170
|
-
color: "var(--dsw-alias-state-warning-primary
|
|
171
|
-
background: "
|
|
170
|
+
color: "var(--dsw-alias-state-warning-primary)",
|
|
171
|
+
background: "color-mix(in srgb, var(--dsw-alias-state-warning-primary) 12%, transparent)"
|
|
172
172
|
};
|
|
173
173
|
const badgeStray = {
|
|
174
174
|
...badge,
|
|
175
|
-
color: "var(--dsw-alias-state-error-primary
|
|
176
|
-
background: "
|
|
175
|
+
color: "var(--dsw-alias-state-error-primary)",
|
|
176
|
+
background: "color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent)"
|
|
177
177
|
};
|
|
178
178
|
const empty = {
|
|
179
179
|
display: "flex",
|
|
@@ -182,35 +182,35 @@ window.__ModuleLoader__.load({
|
|
|
182
182
|
justifyContent: "center",
|
|
183
183
|
gap: 6,
|
|
184
184
|
padding: "40px 24px",
|
|
185
|
-
border: "1px dashed var(--dsw-alias-border-l3
|
|
185
|
+
border: "1px dashed var(--dsw-alias-border-l3)",
|
|
186
186
|
borderRadius: 10,
|
|
187
|
-
background: "var(--dsw-alias-bg-layer-1
|
|
187
|
+
background: "var(--dsw-alias-bg-layer-1)",
|
|
188
188
|
textAlign: "center"
|
|
189
189
|
};
|
|
190
190
|
const emptyText = {
|
|
191
191
|
margin: 0,
|
|
192
192
|
fontSize: 13,
|
|
193
|
-
color: "var(--dsw-alias-label-secondary
|
|
193
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
194
194
|
};
|
|
195
195
|
const errorStyle = {
|
|
196
196
|
fontSize: 12,
|
|
197
197
|
lineHeight: 1.5,
|
|
198
|
-
color: "var(--dsw-alias-state-error-primary
|
|
198
|
+
color: "var(--dsw-alias-state-error-primary)",
|
|
199
199
|
padding: "10px 14px",
|
|
200
|
-
background: "
|
|
201
|
-
border: "1px solid
|
|
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
202
|
borderRadius: 8,
|
|
203
203
|
whiteSpace: "pre-wrap"
|
|
204
204
|
};
|
|
205
205
|
const hint = {
|
|
206
206
|
fontSize: 12,
|
|
207
|
-
color: "var(--dsw-alias-label-secondary
|
|
207
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
208
208
|
lineHeight: 1.6
|
|
209
209
|
};
|
|
210
210
|
const modalOverlay = {
|
|
211
211
|
position: "fixed",
|
|
212
212
|
inset: 0,
|
|
213
|
-
background: "
|
|
213
|
+
background: "var(--dsw-alias-bg-mask-1)",
|
|
214
214
|
display: "flex",
|
|
215
215
|
alignItems: "center",
|
|
216
216
|
justifyContent: "center",
|
|
@@ -219,10 +219,10 @@ window.__ModuleLoader__.load({
|
|
|
219
219
|
const modalCard = {
|
|
220
220
|
width: 420,
|
|
221
221
|
maxWidth: "86vw",
|
|
222
|
-
background: "var(--dsw-alias-bg-layer-3
|
|
223
|
-
color: "var(--dsw-alias-label-primary
|
|
222
|
+
background: "var(--dsw-alias-bg-layer-3)",
|
|
223
|
+
color: "var(--dsw-alias-label-primary)",
|
|
224
224
|
borderRadius: 12,
|
|
225
|
-
boxShadow: "
|
|
225
|
+
boxShadow: "var(--dsw-shadow-lv2)",
|
|
226
226
|
padding: "20px 22px",
|
|
227
227
|
display: "flex",
|
|
228
228
|
flexDirection: "column",
|
|
@@ -236,12 +236,12 @@ window.__ModuleLoader__.load({
|
|
|
236
236
|
const modalBody = {
|
|
237
237
|
fontSize: 13,
|
|
238
238
|
lineHeight: 1.6,
|
|
239
|
-
color: "var(--dsw-alias-label-secondary
|
|
239
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
240
240
|
};
|
|
241
241
|
const modalBodyStrong = {
|
|
242
242
|
fontSize: 13,
|
|
243
243
|
lineHeight: 1.6,
|
|
244
|
-
color: "var(--dsw-alias-state-error-primary
|
|
244
|
+
color: "var(--dsw-alias-state-error-primary)",
|
|
245
245
|
fontWeight: 500
|
|
246
246
|
};
|
|
247
247
|
const modalActions = {
|
|
@@ -279,18 +279,18 @@ window.__ModuleLoader__.load({
|
|
|
279
279
|
const msgUser = {
|
|
280
280
|
...msgBubble,
|
|
281
281
|
alignSelf: "flex-end",
|
|
282
|
-
background: "var(--dsw-alias-state-business-primary
|
|
283
|
-
color: "
|
|
282
|
+
background: "var(--dsw-alias-state-business-primary)",
|
|
283
|
+
color: "var(--dsw-alias-label-primary-foreground)"
|
|
284
284
|
};
|
|
285
285
|
const msgAssistant = {
|
|
286
286
|
...msgBubble,
|
|
287
287
|
alignSelf: "flex-start",
|
|
288
|
-
background: "var(--dsw-alias-bg-layer-3
|
|
289
|
-
color: "var(--dsw-alias-label-primary
|
|
288
|
+
background: "var(--dsw-alias-bg-layer-3)",
|
|
289
|
+
color: "var(--dsw-alias-label-primary)"
|
|
290
290
|
};
|
|
291
291
|
const previewMeta = {
|
|
292
292
|
fontSize: 11,
|
|
293
|
-
color: "var(--dsw-alias-label-secondary
|
|
293
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
294
294
|
};
|
|
295
295
|
function fmtBytes(n) {
|
|
296
296
|
if (n < 1024) return `${n} B`;
|
|
@@ -315,7 +315,7 @@ window.__ModuleLoader__.load({
|
|
|
315
315
|
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`;
|
|
316
316
|
}
|
|
317
317
|
function SessionManagerSection(props) {
|
|
318
|
-
const { api } = props;
|
|
318
|
+
const { api, t } = props;
|
|
319
319
|
const [sessions, setSessions] = (0, react.useState)([]);
|
|
320
320
|
const [loading, setLoading] = (0, react.useState)(true);
|
|
321
321
|
const [error, setError] = (0, react.useState)("");
|
|
@@ -333,7 +333,7 @@ window.__ModuleLoader__.load({
|
|
|
333
333
|
if (!mountedRef.current) return;
|
|
334
334
|
setLoading(false);
|
|
335
335
|
if (!res.ok) {
|
|
336
|
-
setError(
|
|
336
|
+
setError(t("error.loadFailed", { message: res.message ?? res.code ?? "" }));
|
|
337
337
|
return;
|
|
338
338
|
}
|
|
339
339
|
setSessions(res.value ?? []);
|
|
@@ -362,7 +362,7 @@ window.__ModuleLoader__.load({
|
|
|
362
362
|
const res = await fn();
|
|
363
363
|
setBusy(false);
|
|
364
364
|
if (!res.ok) {
|
|
365
|
-
setError(
|
|
365
|
+
setError(t("error.operationFailed", { message: res.message ?? "" }));
|
|
366
366
|
return;
|
|
367
367
|
}
|
|
368
368
|
await refresh();
|
|
@@ -374,7 +374,7 @@ window.__ModuleLoader__.load({
|
|
|
374
374
|
const res = await api.preview(s.id);
|
|
375
375
|
setPreviewLoading(false);
|
|
376
376
|
if (res.ok) setPreviewData(res.value ?? null);
|
|
377
|
-
else setError(
|
|
377
|
+
else setError(t("error.previewFailed", { message: res.message ?? "" }));
|
|
378
378
|
};
|
|
379
379
|
const handleDelete = (s) => {
|
|
380
380
|
setConfirmTarget(s);
|
|
@@ -394,7 +394,7 @@ window.__ModuleLoader__.load({
|
|
|
394
394
|
setError("");
|
|
395
395
|
const ok = await archiveSession(s.id);
|
|
396
396
|
setBusy(false);
|
|
397
|
-
if (!ok) setError("
|
|
397
|
+
if (!ok) setError(t("error.archiveFailed"));
|
|
398
398
|
await refresh();
|
|
399
399
|
})();
|
|
400
400
|
return;
|
|
@@ -405,11 +405,14 @@ window.__ModuleLoader__.load({
|
|
|
405
405
|
run(() => api.unarchive(s.id));
|
|
406
406
|
};
|
|
407
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 }, "
|
|
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", {
|
|
409
412
|
style: btnGhost,
|
|
410
413
|
onClick: () => void refresh(),
|
|
411
414
|
disabled: loading
|
|
412
|
-
}, loading ? "
|
|
415
|
+
}, loading ? t("btn.loading") : t("btn.refresh")))), (0, react.createElement)("div", { style: tabs }, [
|
|
413
416
|
"all",
|
|
414
417
|
"archived",
|
|
415
418
|
"stray"
|
|
@@ -417,58 +420,62 @@ window.__ModuleLoader__.load({
|
|
|
417
420
|
key: k,
|
|
418
421
|
style: tab === k ? tabStyleActive : tabStyle,
|
|
419
422
|
onClick: () => setTab(k)
|
|
420
|
-
}, k === "all" ?
|
|
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", {
|
|
421
424
|
key: s.id,
|
|
422
425
|
style: row,
|
|
423
426
|
onClick: () => void openPreview(s)
|
|
424
|
-
}, (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 }, "
|
|
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", {
|
|
425
428
|
style: rowActions,
|
|
426
429
|
onClick: (e) => e.stopPropagation()
|
|
427
430
|
}, s.archived ? (0, react.createElement)("button", {
|
|
428
431
|
style: btnGhost,
|
|
429
432
|
onClick: () => handleUnarchive(s),
|
|
430
433
|
disabled: busy
|
|
431
|
-
}, "
|
|
434
|
+
}, t("btn.unarchive")) : (0, react.createElement)("button", {
|
|
432
435
|
style: btnGhost,
|
|
433
436
|
onClick: () => handleArchive(s),
|
|
434
437
|
disabled: busy
|
|
435
|
-
}, "
|
|
438
|
+
}, t("btn.archive")), (0, react.createElement)("button", {
|
|
436
439
|
style: btnDanger,
|
|
437
440
|
onClick: () => handleDelete(s),
|
|
438
441
|
disabled: busy
|
|
439
|
-
}, "
|
|
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", {
|
|
440
443
|
style: modalOverlay,
|
|
441
444
|
onClick: () => setConfirmTarget(null)
|
|
442
445
|
}, (0, react.createElement)("div", {
|
|
443
446
|
style: modalCard,
|
|
444
447
|
onClick: (e) => e.stopPropagation()
|
|
445
|
-
}, (0, react.createElement)("h3", { style: modalTitle }, "
|
|
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", {
|
|
446
449
|
style: btnGhost,
|
|
447
450
|
onClick: () => setConfirmTarget(null),
|
|
448
451
|
disabled: busy
|
|
449
|
-
}, "
|
|
452
|
+
}, t("btn.cancel")), (0, react.createElement)("button", {
|
|
450
453
|
style: btnDangerSolid,
|
|
451
454
|
onClick: () => void confirmDelete(),
|
|
452
455
|
disabled: busy
|
|
453
|
-
}, busy ? "
|
|
456
|
+
}, busy ? t("btn.deleting") : t("btn.permanentDelete"))))) : null, previewTarget ? (0, react.createElement)("div", {
|
|
454
457
|
style: modalOverlay,
|
|
455
458
|
onClick: () => setPreviewTarget(null)
|
|
456
459
|
}, (0, react.createElement)("div", {
|
|
457
460
|
style: previewModalCard,
|
|
458
461
|
onClick: (e) => e.stopPropagation()
|
|
459
|
-
}, (0, react.createElement)("h3", { style: modalTitle }, fmtSessionName(previewTarget)), (0, react.createElement)("div", { style: previewMeta },
|
|
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", {
|
|
460
467
|
key: i,
|
|
461
468
|
style: m.role === "user" ? msgUser : msgAssistant
|
|
462
|
-
}, m.text)) : (0, react.createElement)("p", { style: emptyText }, "
|
|
469
|
+
}, m.text)) : (0, react.createElement)("p", { style: emptyText }, t("preview.empty"))), (0, react.createElement)("div", { style: modalActions }, (0, react.createElement)("button", {
|
|
463
470
|
style: btnDanger,
|
|
464
471
|
onClick: () => {
|
|
465
472
|
setPreviewTarget(null);
|
|
466
473
|
handleDelete(previewTarget);
|
|
467
474
|
}
|
|
468
|
-
}, "
|
|
475
|
+
}, t("btn.deleteSession")), (0, react.createElement)("button", {
|
|
469
476
|
style: btnGhost,
|
|
470
477
|
onClick: () => setPreviewTarget(null)
|
|
471
|
-
}, "
|
|
478
|
+
}, t("btn.close"))))) : null);
|
|
472
479
|
}
|
|
473
480
|
//#endregion
|
|
474
481
|
//#region src/client/config-api.ts
|
|
@@ -595,11 +602,87 @@ window.__ModuleLoader__.load({
|
|
|
595
602
|
};
|
|
596
603
|
}
|
|
597
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": "关闭"
|
|
643
|
+
};
|
|
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
|
|
598
679
|
//#region src/client/index.ts
|
|
599
680
|
/** Section identity in the Settings left-nav rail. */
|
|
600
681
|
const SECTION_ID = "session-cleaner";
|
|
601
682
|
/** Sits below Models (10) and IM 通道 (20). */
|
|
602
683
|
const SECTION_ORDER = 30;
|
|
684
|
+
/** i18n namespace — matches the plugin package name for disambiguation. */
|
|
685
|
+
const I18N_NS = "session-cleaner";
|
|
603
686
|
/** Services this browser plugin requires (cordis fiber inject). */
|
|
604
687
|
const inject = [
|
|
605
688
|
"slots",
|
|
@@ -613,6 +696,11 @@ window.__ModuleLoader__.load({
|
|
|
613
696
|
*/
|
|
614
697
|
function apply(ctx) {
|
|
615
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);
|
|
616
704
|
let officialArchive = null;
|
|
617
705
|
ctx.inject(["workspaces"], (sctx) => {
|
|
618
706
|
officialArchive = async (sessionId) => {
|
|
@@ -627,9 +715,10 @@ window.__ModuleLoader__.load({
|
|
|
627
715
|
name: "settings.section",
|
|
628
716
|
id: SECTION_ID,
|
|
629
717
|
order: SECTION_ORDER,
|
|
630
|
-
label: () => "
|
|
718
|
+
label: () => t("section.label"),
|
|
631
719
|
inject: () => ({
|
|
632
720
|
api,
|
|
721
|
+
t,
|
|
633
722
|
archiveSession: (sessionId) => officialArchive ? officialArchive(sessionId) : api.archive(sessionId).then((r) => r.ok)
|
|
634
723
|
})
|
|
635
724
|
}, SessionManagerSection));
|
package/lib/index.js
CHANGED
|
@@ -24,6 +24,41 @@ import { zstdDecompressSync } from "node:zlib";
|
|
|
24
24
|
* 官方 UI 的隐藏/恢复在 dsh 重启后同步。删除同理(已删除的会话从文件层消失,官方 UI
|
|
25
25
|
* 重启后不再列出)。
|
|
26
26
|
*/
|
|
27
|
+
/**
|
|
28
|
+
* dsh 会话 ID ↔ 文件系统目录名 编解码(与 dsh-session-persistence-jsonl 的 encodeSegment 对齐)。
|
|
29
|
+
* dsh 把 sessionId 中的非 [A-Za-z0-9._-] 字符编码为 ~XXXX(charCode 的 4 位十六进制大写)。
|
|
30
|
+
* 例如 `im:qq-s5ey:c2c:...` → `im~003Aqq-s5ey~003Ac2c~003A...`。
|
|
31
|
+
* session-cleaner 扫描文件系统得到编码后的目录名,需要解码回原始 ID 才能与 workspace.json 的 sessionIds 匹配。
|
|
32
|
+
*/
|
|
33
|
+
function encodeSegment(raw) {
|
|
34
|
+
if (raw.length === 0) return raw;
|
|
35
|
+
if (raw === ".") return "~002E";
|
|
36
|
+
if (raw === "..") return "~002E~002E";
|
|
37
|
+
let out = "";
|
|
38
|
+
for (let i = 0; i < raw.length; i++) {
|
|
39
|
+
const code = raw.charCodeAt(i);
|
|
40
|
+
const ch = String.fromCharCode(code);
|
|
41
|
+
if (ch !== "~" && /^[A-Za-z0-9._-]$/.test(ch)) out += ch;
|
|
42
|
+
else out += "~" + code.toString(16).toUpperCase().padStart(4, "0");
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
function decodeSegment(encoded) {
|
|
47
|
+
let out = "";
|
|
48
|
+
for (let i = 0; i < encoded.length;) {
|
|
49
|
+
if (encoded[i] === "~" && i + 5 <= encoded.length) {
|
|
50
|
+
const hex = encoded.slice(i + 1, i + 5);
|
|
51
|
+
if (/^[0-9A-Fa-f]{4}$/.test(hex)) {
|
|
52
|
+
out += String.fromCharCode(parseInt(hex, 16));
|
|
53
|
+
i += 5;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
out += encoded[i];
|
|
58
|
+
i++;
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
27
62
|
/** dsh 用户数据目录(与桌面壳 buildDshEnv 的 DSH_HOME 保持一致) */
|
|
28
63
|
function dshHome() {
|
|
29
64
|
return process.env.DSH_HOME ?? path.join(homedir(), ".dsh");
|
|
@@ -106,13 +141,13 @@ function listSessions() {
|
|
|
106
141
|
continue;
|
|
107
142
|
}
|
|
108
143
|
out.push(stripUndefined({
|
|
109
|
-
id: entry.name,
|
|
144
|
+
id: decodeSegment(entry.name),
|
|
110
145
|
workspaceDir: workspaceDir.name,
|
|
111
146
|
dir,
|
|
112
147
|
sizeBytes,
|
|
113
148
|
mtimeMs,
|
|
114
|
-
archived: archived.has(entry.name),
|
|
115
|
-
active: activeIds.has(entry.name),
|
|
149
|
+
archived: archived.has(decodeSegment(entry.name)),
|
|
150
|
+
active: activeIds.has(decodeSegment(entry.name)),
|
|
116
151
|
title: readSessionTitle(logFile)
|
|
117
152
|
}));
|
|
118
153
|
}
|
|
@@ -136,11 +171,12 @@ function removeFromProjcache(id) {
|
|
|
136
171
|
if (!fs.existsSync(file)) return;
|
|
137
172
|
const data = readJson(file, null);
|
|
138
173
|
if (data === null) return;
|
|
174
|
+
const encodedId = encodeSegment(id);
|
|
139
175
|
const walk = (o) => {
|
|
140
176
|
if (!o || typeof o !== "object") return false;
|
|
141
177
|
let changed = false;
|
|
142
178
|
for (const k of Object.keys(o)) {
|
|
143
|
-
if (String(k).includes(id)) {
|
|
179
|
+
if (String(k).includes(id) || String(k).includes(encodedId)) {
|
|
144
180
|
delete o[k];
|
|
145
181
|
changed = true;
|
|
146
182
|
continue;
|
|
@@ -158,10 +194,11 @@ function removeFromProjcache(id) {
|
|
|
158
194
|
*/
|
|
159
195
|
function deleteSession(id) {
|
|
160
196
|
const root = sessionsRoot();
|
|
197
|
+
const encodedId = encodeSegment(id);
|
|
161
198
|
let target = null;
|
|
162
199
|
if (fs.existsSync(root)) for (const workspaceDir of fs.readdirSync(root, { withFileTypes: true })) {
|
|
163
200
|
if (!workspaceDir.isDirectory()) continue;
|
|
164
|
-
const dir = path.join(root, workspaceDir.name,
|
|
201
|
+
const dir = path.join(root, workspaceDir.name, encodedId);
|
|
165
202
|
if (fs.existsSync(path.join(dir, "session.jsonl.zstd"))) {
|
|
166
203
|
target = dir;
|
|
167
204
|
break;
|
|
@@ -292,10 +329,11 @@ function readSessionTitle(logFile) {
|
|
|
292
329
|
*/
|
|
293
330
|
function previewSession(id, limit = 12) {
|
|
294
331
|
const root = sessionsRoot();
|
|
332
|
+
const encodedId = encodeSegment(id);
|
|
295
333
|
let logFile = null;
|
|
296
334
|
if (fs.existsSync(root)) for (const workspaceDir of fs.readdirSync(root, { withFileTypes: true })) {
|
|
297
335
|
if (!workspaceDir.isDirectory()) continue;
|
|
298
|
-
const candidate = path.join(root, workspaceDir.name,
|
|
336
|
+
const candidate = path.join(root, workspaceDir.name, encodedId, "session.jsonl.zstd");
|
|
299
337
|
if (fs.existsSync(candidate)) {
|
|
300
338
|
logFile = candidate;
|
|
301
339
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lijian-ui/dsh-session-cleaner",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Session management plugin for DeepSeek Harness (dsh): list / delete / archive & restore sessions, with a built-in \"Archived\" group and a delete confirm dialog (real delete, not trash). 为 DeepSeek Harness 提供会话管理:列表 / 真删除(二次确认)/ 归档分组与恢复。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -61,23 +61,23 @@
|
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
63
|
"@deepseek-ai/cordis": "*",
|
|
64
|
-
"@deepseek-ai/schemastery": "*",
|
|
65
64
|
"@deepseek-ai/dsh-settings": "*",
|
|
66
|
-
"@deepseek-ai/dsh-typert-protocol": "*"
|
|
65
|
+
"@deepseek-ai/dsh-typert-protocol": "*",
|
|
66
|
+
"@deepseek-ai/schemastery": "*"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@deepseek-ai/cordis": "4.0.1",
|
|
70
|
-
"@deepseek-ai/
|
|
71
|
-
"@deepseek-ai/dsh-settings": "0.1.0-rc.6",
|
|
72
|
-
"@deepseek-ai/dsh-typert-protocol": "0.1.0-rc.6",
|
|
70
|
+
"@deepseek-ai/dsh-api-gateway": "0.1.0-rc.6",
|
|
73
71
|
"@deepseek-ai/dsh-client-connection": "0.1.0-rc.6",
|
|
74
72
|
"@deepseek-ai/dsh-client-locale": "0.1.0-rc.6",
|
|
75
73
|
"@deepseek-ai/dsh-client-runtime": "0.1.0-rc.6",
|
|
76
74
|
"@deepseek-ai/dsh-client-schema-form": "0.1.0-rc.6",
|
|
77
|
-
"@deepseek-ai/dsh-client-ui-slots": "0.1.0-rc.6",
|
|
78
75
|
"@deepseek-ai/dsh-client-ui-settings": "0.1.0-rc.6",
|
|
79
76
|
"@deepseek-ai/dsh-client-ui-settings-plugins": "0.1.0-rc.6",
|
|
80
|
-
"@deepseek-ai/dsh-
|
|
77
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.0-rc.6",
|
|
78
|
+
"@deepseek-ai/dsh-settings": "0.1.0-rc.6",
|
|
79
|
+
"@deepseek-ai/dsh-typert-protocol": "0.1.0-rc.6",
|
|
80
|
+
"@deepseek-ai/schemastery": "3.18.1",
|
|
81
81
|
"@types/node": "^22.0.0",
|
|
82
82
|
"@types/react": "^18.3.0",
|
|
83
83
|
"react": "^18.2.0",
|