dsh-delete-session 0.1.2 → 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/README.md CHANGED
@@ -16,6 +16,10 @@ Built for DeepSeek Harness (DSH) Web, tested on `0.1.5-rc.1`.
16
16
  action always targets the row you clicked).
17
17
  - **Risk-consent dialog**: shows the session title and id; the confirm button
18
18
  stays disabled until the acknowledgement checkbox is ticked.
19
+ - **Confirm once, then skip**: tick "Don't ask again" in the dialog and later
20
+ deletes run directly from the menu (a toast reports the result). Hold Shift
21
+ while clicking to force the dialog again, or clear
22
+ `localStorage['dsh-delete-session:skip-confirm']` to restore it permanently.
19
23
  - **Real deletion**: stops a running agent (cancel + quiescence), flushes and
20
24
  detaches the live session, removes every on-disk session directory (both the
21
25
  raw and `session-` prefixed id), verifies through the persistence service,
package/README.zh.md CHANGED
@@ -13,6 +13,7 @@
13
13
  - **侧边栏会话菜单里的「删除会话」**:无需切换会话(会话 ID 从菜单的 React fiber 读取,
14
14
  永远作用于你点击的那一行)。
15
15
  - **风险确认对话框**:显示会话标题与 ID;勾选「我已了解,永久删除」前确认按钮不可用。
16
+ - **确认一次后可跳过弹窗**:在对话框里勾选「不再询问,以后直接删除」,之后点菜单项会直接删除(结果以 toast 提示);按住 Shift 点击可临时强制弹窗;清除 `localStorage['dsh-delete-session:skip-confirm']` 可永久恢复弹窗。
16
17
  - **真正的删除**:停止运行中的 agent(cancel + 等待空闲)、flush 并 detach 活跃会话、
17
18
  删除磁盘上的全部会话目录(原始 ID 与 `session-` 前缀两种写法)、经持久化服务复核,
18
19
  最后从工作区记账中移除,避免侧栏残留「未分组」孤儿。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-delete-session",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Delete sessions from the DSH Web sidebar: adds a Delete action to the session row menu with a risk-consent dialog, then removes the session log, workspace accounting and live session state.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/client.js CHANGED
@@ -27,6 +27,7 @@ window.__ModuleLoader__.load({
27
27
  confirm: '删除',
28
28
  deleting: '删除中…',
29
29
  ack: '我已了解,永久删除',
30
+ dontAsk: '不再询问,以后直接删除',
30
31
  done: '已删除会话',
31
32
  failed: '删除失败:',
32
33
  busy: '该会话正被 DSH 打开,暂时无法删除;请重启 DSH 后再试。',
@@ -42,6 +43,7 @@ window.__ModuleLoader__.load({
42
43
  confirm: 'Delete',
43
44
  deleting: 'Deleting...',
44
45
  ack: 'I understand - delete permanently',
46
+ dontAsk: "Don't ask again",
45
47
  done: 'Session deleted',
46
48
  failed: 'Delete failed: ',
47
49
  busy: 'This session is currently open in DSH and cannot be deleted yet. Restart DSH and try again.',
@@ -52,6 +54,49 @@ window.__ModuleLoader__.load({
52
54
  let localeSvc = null
53
55
  let sessionsSvc = null
54
56
 
57
+ // Once the user ticks "don't ask again" the confirmation dialog is
58
+ // skipped and the menu item deletes directly. Shift+click always opens
59
+ // the dialog again, and clearing the key in localStorage restores it.
60
+ const SKIP_KEY = 'dsh-delete-session:skip-confirm'
61
+
62
+ function skipConfirm() {
63
+ try { return localStorage.getItem(SKIP_KEY) === '1' } catch { return false }
64
+ }
65
+
66
+ function setSkipConfirm() {
67
+ try { localStorage.setItem(SKIP_KEY, '1') } catch { /* private mode: ignore */ }
68
+ }
69
+
70
+ function postDelete(sessionId) {
71
+ return fetch('/dsh-delete-session/delete', {
72
+ method: 'POST',
73
+ headers: { 'content-type': 'application/json' },
74
+ body: JSON.stringify({ sessionId }),
75
+ }).then(async (res) => {
76
+ let data = {}
77
+ try { data = await res.json() } catch { /* keep {} */ }
78
+ if (!res.ok || !data.ok) {
79
+ const code = data && data.code
80
+ const message = code === 'busy'
81
+ ? t('busy')
82
+ : code === 'not-found'
83
+ ? t('notFound')
84
+ : (data && data.error) || `HTTP ${res.status}`
85
+ throw new Error(message)
86
+ }
87
+ return data
88
+ })
89
+ }
90
+
91
+ function deleteNow(info) {
92
+ postDelete(info.id)
93
+ .then(() => {
94
+ toast(t('done'))
95
+ refreshList()
96
+ })
97
+ .catch((reason) => toast(t('failed') + String((reason && reason.message) || reason), true))
98
+ }
99
+
55
100
  function browserLang() {
56
101
  if (typeof navigator === 'undefined') return 'zh'
57
102
  for (const tag of (navigator.languages || []).concat([navigator.language])) {
@@ -188,6 +233,15 @@ window.__ModuleLoader__.load({
188
233
  ack.appendChild(checkbox)
189
234
  ack.appendChild(ackText)
190
235
 
236
+ const skip = document.createElement('label')
237
+ skip.className = 'dsdel-ack'
238
+ const skipBox = document.createElement('input')
239
+ skipBox.type = 'checkbox'
240
+ const skipText = document.createElement('span')
241
+ skipText.textContent = t('dontAsk')
242
+ skip.appendChild(skipBox)
243
+ skip.appendChild(skipText)
244
+
191
245
  const error = document.createElement('p')
192
246
  error.className = 'dsdel-error'
193
247
  error.style.display = 'none'
@@ -210,6 +264,7 @@ window.__ModuleLoader__.load({
210
264
  dialog.appendChild(meta)
211
265
  dialog.appendChild(text)
212
266
  dialog.appendChild(ack)
267
+ dialog.appendChild(skip)
213
268
  dialog.appendChild(error)
214
269
  dialog.appendChild(actions)
215
270
  backdrop.appendChild(dialog)
@@ -232,23 +287,9 @@ window.__ModuleLoader__.load({
232
287
  cancel.disabled = true
233
288
  confirm.textContent = t('deleting')
234
289
  error.style.display = 'none'
235
- fetch('/dsh-delete-session/delete', {
236
- method: 'POST',
237
- headers: { 'content-type': 'application/json' },
238
- body: JSON.stringify({ sessionId: info.id }),
239
- })
240
- .then(async (res) => {
241
- let data = {}
242
- try { data = await res.json() } catch { /* keep {} */ }
243
- if (!res.ok || !data.ok) {
244
- const code = data && data.code
245
- const message = code === 'busy'
246
- ? t('busy')
247
- : code === 'not-found'
248
- ? t('notFound')
249
- : (data && data.error) || `HTTP ${res.status}`
250
- throw new Error(message)
251
- }
290
+ if (skipBox.checked) setSkipConfirm()
291
+ postDelete(info.id)
292
+ .then(() => {
252
293
  backdrop.remove()
253
294
  toast(t('done'))
254
295
  refreshList()
@@ -298,7 +339,8 @@ window.__ModuleLoader__.load({
298
339
  e.stopPropagation()
299
340
  e.preventDefault()
300
341
  closeMenu()
301
- openDialog(info)
342
+ if (skipConfirm() && !e.shiftKey) deleteNow(info)
343
+ else openDialog(info)
302
344
  })
303
345
  wrap.appendChild(button)
304
346
  viewport.appendChild(wrap)