dsh-sessions-manager 3.2.2 → 3.4.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/README.en.md +24 -19
- package/README.md +24 -19
- package/assets/screenshot-session-autoarch.png +0 -0
- package/assets/screenshot-session-details.png +0 -0
- package/assets/screenshot-session-settings.png +0 -0
- package/assets/screenshot-session-starred.png +0 -0
- package/assets/screenshot-session-storage.png +0 -0
- package/assets/screenshot-session-trash.png +0 -0
- package/lib/client.js +258 -7
- package/lib/client.js.map +2 -2
- package/lib/index.js +584 -29
- package/lib/index.js.map +4 -4
- package/package.json +1 -1
- package/src/auto-archive.js +168 -0
- package/src/client/index.jsx +266 -10
- package/src/client/logic.js +6 -0
- package/src/index.js +238 -5
- package/src/markdown.js +175 -0
- package/src/star-index.js +109 -0
- package/src/storage-stats.js +94 -0
- package/assets/screenshot-session-settingsmenu.png +0 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// Storage usage aggregation (pure, no I/O).
|
|
2
|
+
//
|
|
3
|
+
// Kept out of the host bundle so it can be unit-tested directly
|
|
4
|
+
// (tests/storage-stats.test.js). Takes the session items the host already
|
|
5
|
+
// builds (with `sizeBytes` filled in) and rolls them up per workspace plus a
|
|
6
|
+
// "largest sessions" leaderboard.
|
|
7
|
+
//
|
|
8
|
+
// Sessions without a workspace path land in a single "未分组" bucket — that is
|
|
9
|
+
// DSH's own label for orphans, so the panel speaks the same language as the
|
|
10
|
+
// sidebar.
|
|
11
|
+
|
|
12
|
+
export const UNGROUPED_KEY = '__ungrouped__'
|
|
13
|
+
|
|
14
|
+
function isFiniteSize(value) {
|
|
15
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Roll session sizes up per workspace and pick the largest sessions.
|
|
20
|
+
*
|
|
21
|
+
* @param {Array<{sessionId: string, title?: string|null, workspacePath?: string|null,
|
|
22
|
+
* workspaceTitle?: string|null, sizeBytes?: number|null}>} items
|
|
23
|
+
* @param {object} [options]
|
|
24
|
+
* @param {number} [options.topN=10] - How many entries the leaderboard holds.
|
|
25
|
+
* @returns {{
|
|
26
|
+
* totalBytes: number,
|
|
27
|
+
* sessionCount: number,
|
|
28
|
+
* sizedSessions: number,
|
|
29
|
+
* unknownSessions: number,
|
|
30
|
+
* workspaces: Array<{key: string, path: string|null, title: string|null,
|
|
31
|
+
* bytes: number, sessions: number, share: number}>,
|
|
32
|
+
* top: Array<{sessionId: string, title: string|null, workspacePath: string|null,
|
|
33
|
+
* workspaceTitle: string|null, sizeBytes: number}>
|
|
34
|
+
* }}
|
|
35
|
+
*/
|
|
36
|
+
export function aggregateStorage(items, options = {}) {
|
|
37
|
+
const topN = Number.isInteger(options.topN) && options.topN > 0 ? options.topN : 10
|
|
38
|
+
const list = Array.isArray(items) ? items : []
|
|
39
|
+
|
|
40
|
+
const buckets = new Map()
|
|
41
|
+
const sized = []
|
|
42
|
+
let totalBytes = 0
|
|
43
|
+
let unknownSessions = 0
|
|
44
|
+
// Counted entries only: sessionCount must always equal
|
|
45
|
+
// sizedSessions + unknownSessions, or the panel would show a total that
|
|
46
|
+
// disagrees with its own breakdown.
|
|
47
|
+
let counted = 0
|
|
48
|
+
|
|
49
|
+
for (const item of list) {
|
|
50
|
+
if (!item || item.sessionId == null) continue
|
|
51
|
+
counted++
|
|
52
|
+
const id = String(item.sessionId)
|
|
53
|
+
const path = item.workspacePath ? String(item.workspacePath) : null
|
|
54
|
+
const key = path || UNGROUPED_KEY
|
|
55
|
+
|
|
56
|
+
let bucket = buckets.get(key)
|
|
57
|
+
if (!bucket) {
|
|
58
|
+
bucket = { key, path, title: item.workspaceTitle ? String(item.workspaceTitle) : null, bytes: 0, sessions: 0 }
|
|
59
|
+
buckets.set(key, bucket)
|
|
60
|
+
}
|
|
61
|
+
bucket.sessions++
|
|
62
|
+
|
|
63
|
+
if (isFiniteSize(item.sizeBytes)) {
|
|
64
|
+
bucket.bytes += item.sizeBytes
|
|
65
|
+
totalBytes += item.sizeBytes
|
|
66
|
+
sized.push({
|
|
67
|
+
sessionId: id,
|
|
68
|
+
title: item.title || null,
|
|
69
|
+
workspacePath: path,
|
|
70
|
+
workspaceTitle: bucket.title,
|
|
71
|
+
sizeBytes: item.sizeBytes,
|
|
72
|
+
})
|
|
73
|
+
} else {
|
|
74
|
+
unknownSessions++
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const workspaces = [...buckets.values()]
|
|
79
|
+
.sort((a, b) => (b.bytes - a.bytes) || (b.sessions - a.sessions) || a.key.localeCompare(b.key))
|
|
80
|
+
.map((bucket) => ({ ...bucket, share: totalBytes > 0 ? bucket.bytes / totalBytes : 0 }))
|
|
81
|
+
|
|
82
|
+
const top = sized
|
|
83
|
+
.sort((a, b) => (b.sizeBytes - a.sizeBytes) || a.sessionId.localeCompare(b.sessionId))
|
|
84
|
+
.slice(0, topN)
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
totalBytes,
|
|
88
|
+
sessionCount: counted,
|
|
89
|
+
sizedSessions: sized.length,
|
|
90
|
+
unknownSessions,
|
|
91
|
+
workspaces,
|
|
92
|
+
top,
|
|
93
|
+
}
|
|
94
|
+
}
|
|
Binary file
|