dsh-sidebar-suite 0.2.9 → 0.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/CHANGELOG.md +36 -1
- package/DETAIL.md +57 -1
- package/README.md +72 -2
- package/lib/client.js +1016 -4
- package/lib/temp-session-host.mjs +69 -1
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +3 -3
- package/temp-session-config.txt +7 -0
- package//344/275/277/347/224/250/350/257/264/346/230/216.md +40 -8
|
@@ -58,6 +58,10 @@ const DEFAULT_CONFIG = {
|
|
|
58
58
|
// 0.2.4: the host's workspace list folds after five sessions and paints a
|
|
59
59
|
// "展开其余 N 个会话" button. This plugin expands every group instead.
|
|
60
60
|
expandAllSessions: true,
|
|
61
|
+
// 0.4.0: right-clicking a temporary session row offers "keep as a normal
|
|
62
|
+
// session" and "delete now". Off means the row behaves like a plain session
|
|
63
|
+
// row again — the escape hatch if another plugin owns the context menu.
|
|
64
|
+
rowMenu: true,
|
|
61
65
|
deleteOnExit: true,
|
|
62
66
|
// Daily cleanup (0.2.2). Independent of deleteOnExit: a user who keeps DSH
|
|
63
67
|
// open for days wants "delete at 03:00 every night", and a user who closes it
|
|
@@ -148,6 +152,7 @@ const CONFIG_KEYS = {
|
|
|
148
152
|
inChatMode: { key: 'TEMP_IN_CHAT_MODE', text: (value) => String(value) },
|
|
149
153
|
buttonStyle: { key: 'TEMP_BUTTON_STYLE', text: (value) => String(value) },
|
|
150
154
|
expandAllSessions: { key: 'EXPAND_ALL_SESSIONS', text: (value) => (value ? 'on' : 'off') },
|
|
155
|
+
rowMenu: { key: 'TEMP_ROW_MENU', text: (value) => (value ? 'on' : 'off') },
|
|
151
156
|
deleteOnExit: { key: 'DELETE_ON_EXIT', text: (value) => (value ? 'on' : 'off') },
|
|
152
157
|
scheduleDelete: { key: 'SCHEDULE_DELETE', text: (value) => (value ? 'on' : 'off') },
|
|
153
158
|
scheduleTime: { key: 'SCHEDULE_TIME', text: (value) => String(value) },
|
|
@@ -185,6 +190,7 @@ function parseConfig() {
|
|
|
185
190
|
if (kv.EXPAND_ALL_SESSIONS !== undefined) {
|
|
186
191
|
next.expandAllSessions = kv.EXPAND_ALL_SESSIONS.toLowerCase() !== 'off'
|
|
187
192
|
}
|
|
193
|
+
if (kv.TEMP_ROW_MENU !== undefined) next.rowMenu = kv.TEMP_ROW_MENU.toLowerCase() !== 'off'
|
|
188
194
|
if (kv.DELETE_ON_EXIT !== undefined) next.deleteOnExit = kv.DELETE_ON_EXIT.toLowerCase() !== 'off'
|
|
189
195
|
if (kv.SCHEDULE_DELETE !== undefined) next.scheduleDelete = kv.SCHEDULE_DELETE.toLowerCase() !== 'off'
|
|
190
196
|
if (kv.SCHEDULE_TIME !== undefined) {
|
|
@@ -224,7 +230,7 @@ function refreshConfigIfChanged() {
|
|
|
224
230
|
state.configStamp = stamp
|
|
225
231
|
const next = parseConfig()
|
|
226
232
|
const fields = [
|
|
227
|
-
'showButton', 'inChatMode', 'buttonStyle', 'expandAllSessions',
|
|
233
|
+
'showButton', 'inChatMode', 'buttonStyle', 'expandAllSessions', 'rowMenu',
|
|
228
234
|
'deleteOnExit', 'scheduleDelete', 'scheduleTime', 'maxKept',
|
|
229
235
|
]
|
|
230
236
|
const changed = fields.some((field) => next[field] !== state.config[field])
|
|
@@ -255,6 +261,7 @@ function saveConfig(patch) {
|
|
|
255
261
|
if (input.inChatMode === 'show' || input.inChatMode === 'hide') next.inChatMode = input.inChatMode
|
|
256
262
|
if (input.buttonStyle === 'own' || input.buttonStyle === 'pair') next.buttonStyle = input.buttonStyle
|
|
257
263
|
if (typeof input.expandAllSessions === 'boolean') next.expandAllSessions = input.expandAllSessions
|
|
264
|
+
if (typeof input.rowMenu === 'boolean') next.rowMenu = input.rowMenu
|
|
258
265
|
if (typeof input.deleteOnExit === 'boolean') next.deleteOnExit = input.deleteOnExit
|
|
259
266
|
if (typeof input.scheduleDelete === 'boolean') next.scheduleDelete = input.scheduleDelete
|
|
260
267
|
const clock = normalizeClock(input.scheduleTime)
|
|
@@ -324,6 +331,7 @@ function publicConfig() {
|
|
|
324
331
|
inChatMode: state.config.inChatMode,
|
|
325
332
|
buttonStyle: state.config.buttonStyle,
|
|
326
333
|
expandAllSessions: state.config.expandAllSessions,
|
|
334
|
+
rowMenu: state.config.rowMenu,
|
|
327
335
|
deleteOnExit: state.config.deleteOnExit,
|
|
328
336
|
scheduleDelete: state.config.scheduleDelete,
|
|
329
337
|
scheduleTime: state.config.scheduleTime,
|
|
@@ -536,6 +544,59 @@ async function purgeAll(reason, options) {
|
|
|
536
544
|
return report
|
|
537
545
|
}
|
|
538
546
|
|
|
547
|
+
/**
|
|
548
|
+
* Delete one tracked session from the sidebar menu (0.4.0).
|
|
549
|
+
*
|
|
550
|
+
* Same steps as `purgeAll` for a single id: remove the directories, verify they
|
|
551
|
+
* are really gone, fall back to hiding the row when a file is locked. Two
|
|
552
|
+
* deliberate differences:
|
|
553
|
+
*
|
|
554
|
+
* - **Only tracked ids are accepted.** This route is reachable from the page, so
|
|
555
|
+
* it must never become "delete any session by id". An untracked id is refused
|
|
556
|
+
* before any filesystem call.
|
|
557
|
+
* - **A failed delete keeps the id tracked**, exactly like `purgeAll`, so the
|
|
558
|
+
* next launch retries it instead of leaving an undeletable row that silently
|
|
559
|
+
* stopped being temporary.
|
|
560
|
+
*/
|
|
561
|
+
async function removeOne(sessionId) {
|
|
562
|
+
await ensureReady()
|
|
563
|
+
const id = normalizeId(sessionId)
|
|
564
|
+
if (id.length === 0) return { ok: false, error: 'invalid session id' }
|
|
565
|
+
if (!state.tracked.includes(id)) return { ok: false, error: 'not a temporary session' }
|
|
566
|
+
let removed = 0
|
|
567
|
+
try {
|
|
568
|
+
removed = await removeSession(id)
|
|
569
|
+
} catch (error) {
|
|
570
|
+
void error
|
|
571
|
+
}
|
|
572
|
+
const gone = removed > 0 && sessionDirsSync(id).length === 0
|
|
573
|
+
let hidden = false
|
|
574
|
+
if (!gone) hidden = await hideSession(id)
|
|
575
|
+
const report = {
|
|
576
|
+
reason: 'single',
|
|
577
|
+
at: new Date().toISOString(),
|
|
578
|
+
tried: 1,
|
|
579
|
+
removed: gone ? 1 : 0,
|
|
580
|
+
hidden: hidden ? 1 : 0,
|
|
581
|
+
skipped: 0,
|
|
582
|
+
failed: [],
|
|
583
|
+
}
|
|
584
|
+
if (gone || hidden) {
|
|
585
|
+
state.tracked = state.tracked.filter((item) => item !== id)
|
|
586
|
+
await saveStore()
|
|
587
|
+
state.lastPurge = report
|
|
588
|
+
return { ok: true, removed: gone, hidden, ids: state.tracked.slice(), count: state.tracked.length }
|
|
589
|
+
}
|
|
590
|
+
report.failed.push(id)
|
|
591
|
+
state.lastPurge = report
|
|
592
|
+
return {
|
|
593
|
+
ok: false,
|
|
594
|
+
error: 'delete failed — the session files are locked',
|
|
595
|
+
ids: state.tracked.slice(),
|
|
596
|
+
count: state.tracked.length,
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
539
600
|
/** Delete every tracked session synchronously — the quit path has no `await`. */
|
|
540
601
|
function purgeSync(reason) {
|
|
541
602
|
if (state.tracked.length === 0) return { reason, tried: 0, removed: 0, kept: [] }
|
|
@@ -723,6 +784,7 @@ function status() {
|
|
|
723
784
|
chatModePolicy: state.config.inChatMode,
|
|
724
785
|
buttonStyle: state.config.buttonStyle,
|
|
725
786
|
expandAllSessions: state.config.expandAllSessions,
|
|
787
|
+
rowMenu: state.config.rowMenu,
|
|
726
788
|
deleteOnExit: state.config.deleteOnExit,
|
|
727
789
|
scheduleDelete: state.config.scheduleDelete,
|
|
728
790
|
scheduleTime: state.config.scheduleTime,
|
|
@@ -860,6 +922,12 @@ export function createTempSessionHost(initialCtx) {
|
|
|
860
922
|
if (sessionId.length === 0) return json({ ok: false, error: 'sessionId required' }, 400)
|
|
861
923
|
return json(await untrack(sessionId))
|
|
862
924
|
}],
|
|
925
|
+
// 0.4.0 — delete one temporary session from the row menu.
|
|
926
|
+
['/api/dsh-temp-session.remove', ['POST', 'GET'], async (request) => {
|
|
927
|
+
const { sessionId } = queryOf(request)
|
|
928
|
+
if (sessionId.length === 0) return json({ ok: false, error: 'sessionId required' }, 400)
|
|
929
|
+
return json(await removeOne(sessionId))
|
|
930
|
+
}],
|
|
863
931
|
['/api/dsh-temp-session.purge', ['POST', 'GET'], async () => {
|
|
864
932
|
await ensureReady()
|
|
865
933
|
return json({ ok: true, ...(await purgeAll('manual')) })
|
package/locale/en.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
3
|
"title": "侧栏布局大师 · Sidebar Suite",
|
|
4
|
-
"description": "一款专门针对改进 DSH 左侧栏体验的插件:三种布局一键切换、会话默认全展开、临时会话默认在退出 DSH
|
|
4
|
+
"description": "一款专门针对改进 DSH 左侧栏体验的插件:三种布局一键切换、会话默认全展开、临时会话默认在退出 DSH 时删除(每天定时清理默认关,可在本页开启并设时间)、临时会话可右键保留为正式会话或立即删除、设置与反馈都在本页(全部 9 项配置都有开关,改配置文件约 3 秒自动生效、免重启),强实用性;界面沿用宿主原生配色、圆角与间距,只加属性不重构宿主结构,克制又好看。|A plugin dedicated to a better DSH sidebar: one-click layouts, always-expanded sessions, temp sessions deleted when DSH quits by default (daily cleanup off by default and opt-in) and a right-click menu to keep or delete one temp session, in-page settings/feedback — every config key has a switch and file edits hot-reload — seriously practical; native colours, radii and spacing with zero host-DOM rewrites — restrained and good-looking.",
|
|
5
5
|
"detail": "DETAIL.md"
|
|
6
6
|
}
|
|
7
7
|
}
|
package/locale/zh.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
3
|
"title": "侧栏布局大师 · Sidebar Suite",
|
|
4
|
-
"description": "一款专门针对改进 DSH 左侧栏体验的插件:三种布局一键切换、会话默认全展开、临时会话默认在退出 DSH
|
|
4
|
+
"description": "一款专门针对改进 DSH 左侧栏体验的插件:三种布局一键切换、会话默认全展开、临时会话默认在退出 DSH 时删除(每天定时清理默认关,可在本页开启并设时间)、临时会话可右键保留为正式会话或立即删除、设置与反馈都在本页(全部 9 项配置都有开关,改配置文件约 3 秒自动生效、免重启),强实用性;界面沿用宿主原生配色、圆角与间距,只加属性不重构宿主结构,克制又好看。|A plugin dedicated to a better DSH sidebar: one-click layouts, always-expanded sessions, temp sessions deleted when DSH quits by default (daily cleanup off by default and opt-in) and a right-click menu to keep or delete one temp session, in-page settings/feedback — every config key has a switch and file edits hot-reload — seriously practical; native colours, radii and spacing with zero host-DOM rewrites — restrained and good-looking.",
|
|
5
5
|
"detail": "DETAIL.md"
|
|
6
6
|
}
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-sidebar-suite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Yonan",
|
|
6
6
|
"repository": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"publishConfig": {
|
|
15
15
|
"access": "public"
|
|
16
16
|
},
|
|
17
|
-
"description": "一款专门针对改进 DSH 左侧栏体验的插件:三种布局一键切换、默认布局在详情页顶部直接设、会话列表默认全展开、「临时会话」开原生会话且默认在退出 DSH 时删除(DELETE_ON_EXIT
|
|
17
|
+
"description": "一款专门针对改进 DSH 左侧栏体验的插件:三种布局一键切换、默认布局在详情页顶部直接设、会话列表默认全展开、「临时会话」开原生会话且默认在退出 DSH 时删除(DELETE_ON_EXIT 默认开;每天定时清理默认关,可在详情页开启并自设时间)、详情页三种反馈入口、临时会话可右键「保留为正式会话」或「立即删除」、全部 9 项配置都有图形开关且改文件免重启 —— 强实用性;界面沿用宿主原生配色、圆角与间距,只加属性与一条控制栏、不重构宿主结构 —— 克制美观。|A plugin dedicated to the DSH sidebar experience: one-click layouts, always-expanded sessions, native temp sessions deleted when DSH quits by default (DELETE_ON_EXIT on; daily cleanup off by default and opt-in) in-page settings/feedback, a right-click menu to keep or delete one temp session, a control for every config key and hot-reloaded config edits — seriously practical; native colours, radii and spacing with zero host-DOM rewrites — restrained and good-looking.",
|
|
18
18
|
"keywords": [
|
|
19
19
|
"dsh-plugin",
|
|
20
20
|
"dsh",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
],
|
|
28
28
|
"type": "module",
|
|
29
29
|
"scripts": {
|
|
30
|
-
"test": "node tests/test_020.mjs && node tests/test_022.mjs && node tests/test_023.mjs && node tests/test_024.mjs && node tests/test_025.mjs && node tests/test_028.mjs && node tests/test_029.mjs",
|
|
30
|
+
"test": "node tests/test_020.mjs && node tests/test_022.mjs && node tests/test_023.mjs && node tests/test_024.mjs && node tests/test_025.mjs && node tests/test_028.mjs && node tests/test_029.mjs && node tests/test_030.mjs && node tests/test_031.mjs && node tests/test_032.mjs && node tests/test_033.mjs",
|
|
31
31
|
"check": "node --check lib/index.js && node --check lib/client.js && node --check lib/temp-session-host.mjs && npm pack --dry-run"
|
|
32
32
|
},
|
|
33
33
|
"main": "lib/index.js",
|
package/temp-session-config.txt
CHANGED
|
@@ -33,6 +33,13 @@ TEMP_BUTTON_STYLE=pair
|
|
|
33
33
|
# off = keep DSH's own folding and leave that button visible
|
|
34
34
|
EXPAND_ALL_SESSIONS=on
|
|
35
35
|
|
|
36
|
+
# 右键临时会话时,是否弹出「保留为正式会话 / 立即删除」菜单 / Right-click menu
|
|
37
|
+
# on = 弹出(默认)。选「保留为正式会话」后,这个会话就不再算临时,退出时不会删。
|
|
38
|
+
# off = 不接管右键菜单(别的插件抢右键时用这个)
|
|
39
|
+
# on = show the menu (default). "Keep as a normal session" makes it permanent.
|
|
40
|
+
# off = leave the context menu alone (use this if another plugin owns it)
|
|
41
|
+
TEMP_ROW_MENU=on
|
|
42
|
+
|
|
36
43
|
# 退出 DSH 时删除临时会话 / Delete temporary sessions when DSH quits
|
|
37
44
|
# on = 退出时删除(默认);off = 不删除,临时会话就变成普通会话
|
|
38
45
|
DELETE_ON_EXIT=on
|
|
@@ -117,6 +117,29 @@
|
|
|
117
117
|
|
|
118
118
|
---
|
|
119
119
|
|
|
120
|
+
### 3.4 第三方插件加的页签(0.3.0)
|
|
121
|
+
|
|
122
|
+
这一版给**别的插件**开了一个口:它们可以往你侧栏里加自己的一页。
|
|
123
|
+
|
|
124
|
+
你不需要做任何设置。什么时候会看到:
|
|
125
|
+
|
|
126
|
+
- 装了某个使用这个接口的插件之后,**控制栏下面会多出一排页签**,比如 `扩展 │ 工作区 │ 那个插件的名字`。
|
|
127
|
+
- 点哪个,侧栏中间那块就换成哪一页。
|
|
128
|
+
- **再点一次当前页签**,就退回原来的会话列表 / 扩展列表。
|
|
129
|
+
- 你在哪一页会被记住,下次打开还在那一页。
|
|
130
|
+
|
|
131
|
+
三种布局下的差别:
|
|
132
|
+
|
|
133
|
+
| 你选的布局 | 页签条长什么样 |
|
|
134
|
+
|---|---|
|
|
135
|
+
| 标签页(默认) | `扩展 / 工作区 / 插件页` 三种并排 |
|
|
136
|
+
| 折叠 | 只有插件页进条;扩展和工作区还是用原来那两个折叠按钮 |
|
|
137
|
+
| 浮层 | 只有插件页进条;扩展还是用「浮出面板」按钮 |
|
|
138
|
+
|
|
139
|
+
**一个都没装的话,这排页签根本不会出现** —— 界面和 0.2.x 一模一样,不会平白多一行东西。
|
|
140
|
+
|
|
141
|
+
> 想自己写一个?接口是 `ctx.sidebarSuite.registerTab({ id, title, icon, mount })`,`mount` 拿到一个空容器,你爱画什么画什么。完整示例在 README 和 DETAIL 里。
|
|
142
|
+
|
|
120
143
|
## 四、临时会话(重点)
|
|
121
144
|
|
|
122
145
|
### 4.1 它到底是什么
|
|
@@ -160,9 +183,16 @@ DSH **没有公开的「删除会话」接口**(官方界面的「归档」只
|
|
|
160
183
|
|
|
161
184
|
为什么必须这样:要用原生窗口、还要能中途切走再切回,它就只能是真会话。如果要求「连磁盘都不许写」,那就只能是不在侧栏、不能多任务的纯内存方案 —— 和你要的正好相反。
|
|
162
185
|
|
|
163
|
-
### 4.5
|
|
186
|
+
### 4.5 想留住某一个 / 想全都不删
|
|
187
|
+
|
|
188
|
+
**只想留住某一个(推荐,0.4.0)**:在侧栏里**右键那个带「临时」标签的会话**,选「保留为正式会话」。
|
|
189
|
+
|
|
190
|
+
它立刻变成普通会话 —— 退出不删、定时清理也不碰,**其它临时会话照旧按时删掉**。同一个菜单里还有「立即删除」,用来当场删掉这一个,不用等退出。
|
|
191
|
+
|
|
192
|
+
- 右键**普通会话**时插件完全不插手,你的其它右键菜单照常出现。
|
|
193
|
+
- 右键没反应?看配置第 41 行 `TEMP_ROW_MENU` 是不是被改成 `off` 了(详情页的「临时会话右键菜单」开关写的是同一行)。
|
|
164
194
|
|
|
165
|
-
|
|
195
|
+
**想全部保留**:把配置第 45 行 `DELETE_ON_EXIT=off`(见下一节)。关掉后所有临时会话都不再删除,等于普通会话。
|
|
166
196
|
|
|
167
197
|
---
|
|
168
198
|
|
|
@@ -178,18 +208,19 @@ DSH **没有公开的「删除会话」接口**(官方界面的「归档」只
|
|
|
178
208
|
| **19** | `TEMP_IN_CHAT_MODE=hide` | `hide` / `show` | 装了 `dsh-deepseek-chat` 时,处于 CHAT 官网模式要不要显示按钮 |
|
|
179
209
|
| **27** | `TEMP_BUTTON_STYLE=pair` | `pair` / `own` | `pair` = 和「新会话」并排;`own` = 单独占一整行,完全不改「新会话」 |
|
|
180
210
|
| **34** | `EXPAND_ALL_SESSIONS=on` | `on` / `off` | `on` = 会话列表全部展开、隐藏「展开其余 N 个会话」;`off` = 恢复 DSH 原来的折叠 |
|
|
181
|
-
| **
|
|
182
|
-
| **
|
|
183
|
-
| **
|
|
184
|
-
| **
|
|
211
|
+
| **41** | `TEMP_ROW_MENU=on` | `on` / `off` | `on` = 右键临时会话可「保留为正式会话 / 立即删除」;`off` = 不接管右键(别的插件抢右键时用) |
|
|
212
|
+
| **45** | `DELETE_ON_EXIT=on` | `on` / `off` | **退出 DSH 时**删不删 |
|
|
213
|
+
| **51** | `SCHEDULE_DELETE=off` | `on` / `off` | **每天定时清理**要不要开 |
|
|
214
|
+
| **56** | `SCHEDULE_TIME=03:00` | `HH:MM`(如 `03:00`、`23:30`) | 定时清理的时间点,24 小时制、本机时间 |
|
|
215
|
+
| **60** | `MAX_KEPT=20` | 1–200 | 最多同时留几个临时会话,超了先删最早的 |
|
|
185
216
|
|
|
186
|
-
> 这一整节都可以不动手改文件 —— 0.2.8 起详情页有**全部
|
|
217
|
+
> 这一整节都可以不动手改文件 —— 0.2.8 起详情页有**全部 9 项的图形化开关**,见 5.1。两种改法写的是**同一份文件**,改完都在约 3 秒内生效,不会打架。
|
|
187
218
|
|
|
188
219
|
### 5.1 全部设置都在详情页里改(推荐,不用改文件)
|
|
189
220
|
|
|
190
221
|
打开 DSH → 插件 → `dsh-sidebar-suite` → 详情页最上面那张卡。0.2.8 起这里是**两块设置**:
|
|
191
222
|
|
|
192
|
-
**第一块「侧栏与临时会话」**(这一节表里
|
|
223
|
+
**第一块「侧栏与临时会话」**(这一节表里 9 个键里的前 5 个 + 保留上限,全都能点):
|
|
193
224
|
|
|
194
225
|
```
|
|
195
226
|
侧栏与临时会话 · Sidebar & temp session options
|
|
@@ -199,6 +230,7 @@ DSH **没有公开的「删除会话」接口**(官方界面的「归档」只
|
|
|
199
230
|
CHAT 官网模式 [隐藏按钮] / [照常显示]
|
|
200
231
|
按钮摆法 [并排] / [独占一行]
|
|
201
232
|
会话默认全展开 [开] / [关]
|
|
233
|
+
临时会话右键菜单 [开] / [关]
|
|
202
234
|
最多保留 [ 20 ] 个临时会话(1–200)
|
|
203
235
|
设置版本 12 · …/temp-session-config.txt
|
|
204
236
|
```
|