dsh-session-manager 0.7.0 → 0.7.2

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 CHANGED
@@ -200,14 +200,14 @@ function isTopmostDialog(z) {
200
200
  "update.autoCheck": "自动检查更新",
201
201
  "update.autoCheck.enable": "开启",
202
202
  "settings.registry": "安装源",
203
- "settings.registry.npm": "npm 官方源 registry.npmjs.org",
204
- "settings.registry.china": "中国大陆镜像源 registry.npmmirror.com",
203
+ "settings.registry.npm": "npm 官方源 (npmjs.org)",
204
+ "settings.registry.china": "国内镜像源 (npmmirror.com)",
205
205
  "settings.title": "会话管理",
206
206
  "settings.pluginName": "dsh-session-manager",
207
207
  "settings.versionSection": "插件版本",
208
208
  "settings.prefSection": "偏好设置",
209
209
  "settings.dialogOpacity": "弹窗透明度",
210
- "settings.dialogOpacity.hint": "仅调整插件弹窗的背景透明度;文字、按钮、输入框和边框保持不透明。",
210
+ "settings.dialogOpacity.hint": "仅调整插件弹窗的背景透明度",
211
211
  "settings.dialogOpacity.value": "{value}%",
212
212
  "settings.dialogOpacity.reset": "恢复默认",
213
213
  "settings.projectSection": "项目",
@@ -264,6 +264,9 @@ function isTopmostDialog(z) {
264
264
  "confirm.delete.title": "删除会话",
265
265
  "confirm.delete.desc": "「{title}」:删除,不可恢复",
266
266
  "confirm.delete.running": "该会话正在运行,删除将立即中断它。",
267
+ "confirm.delete.subagents": "将连带删除 {count} 个关联的子智能体会话。",
268
+ "confirm.delete.previewLoading": "正在检查关联的子智能体会话…",
269
+ "confirm.delete.previewError": "检查关联子智能体会话失败,无法确认删除安全,请稍后重试。",
267
270
  "confirm.delete.confirm": "确认删除",
268
271
  "confirm.move.title": "移动会话到工作区",
269
272
  "confirm.move.desc": "「{title}」:移动至目标工作区",
@@ -468,14 +471,14 @@ function isTopmostDialog(z) {
468
471
  "update.autoCheck": "Automatically check for updates",
469
472
  "update.autoCheck.enable": "Enable",
470
473
  "settings.registry": "Install Source",
471
- "settings.registry.npm": "npm official registry.npmjs.org",
472
- "settings.registry.china": "China mainland mirror registry.npmmirror.com",
474
+ "settings.registry.npm": "Official npm (npmjs.org)",
475
+ "settings.registry.china": "China mirror (npmmirror.com)",
473
476
  "settings.title": "Session Manager",
474
477
  "settings.pluginName": "dsh-session-manager",
475
478
  "settings.versionSection": "Plugin Version",
476
479
  "settings.prefSection": "Preferences",
477
480
  "settings.dialogOpacity": "Dialog opacity",
478
- "settings.dialogOpacity.hint": "Adjusts the plugin dialog background only; text, buttons, inputs and borders stay opaque.",
481
+ "settings.dialogOpacity.hint": "Adjusts dialog background opacity only",
479
482
  "settings.dialogOpacity.value": "{value}%",
480
483
  "settings.dialogOpacity.reset": "Reset to default",
481
484
  "settings.projectSection": "Project",
@@ -599,6 +602,9 @@ function isTopmostDialog(z) {
599
602
  "confirm.delete.title": "Delete session",
600
603
  "confirm.delete.desc": "\"{title}\": Delete, cannot be undone",
601
604
  "confirm.delete.running": "This session is running; deleting will interrupt it immediately.",
605
+ "confirm.delete.subagents": "Will also delete {count} associated subagent session(s).",
606
+ "confirm.delete.previewLoading": "Checking for associated subagent sessions...",
607
+ "confirm.delete.previewError": "Failed to check associated subagents. Cannot verify deletion safety. Please try again later.",
602
608
  "confirm.delete.confirm": "Delete",
603
609
  "confirm.move.title": "Move session to workspace",
604
610
  "confirm.move.desc": "\"{title}\": Move to target workspace",
@@ -726,7 +732,7 @@ function isTopmostDialog(z) {
726
732
  // applied). Tooltip visibility is governed by the layering contract
727
733
  // above, not by this rule; it is kept as the scoped replacement for the
728
734
  // unscoped selector Issue #21 removed.
729
- ".sm-panelHeaderClose{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;min-height:26px;padding:0;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:7px;background:transparent;color:var(--dsw-alias-label-secondary,#555);cursor:pointer;flex:none;box-sizing:border-box;outline:none}.sm-panelHeaderClose:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#eee);color:var(--dsw-alias-label-primary,#111)}.sm-panelHeaderClose svg{display:block;width:14px;height:14px;margin:auto}.sm-panelHeaderGithub{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border-radius:6px;color:var(--dsw-alias-label-secondary,#555);text-decoration:none;flex:none;transition:background .15s,color .15s;cursor:pointer}.sm-panelHeaderGithub:hover{background:var(--dsw-alias-interactive-bg-hover,#eee);color:var(--dsw-alias-label-primary,#111)}.sm-panelHeaderGithub svg{display:block;width:16px;height:16px;fill:currentColor}.sm-panelHeaderLeft{display:flex;align-items:center;gap:4px;flex:none;position:relative;z-index:1}.sm-panelHeaderWhale{display:inline-flex;align-items:center;justify-content:center;position:relative;width:26px;height:26px;border-radius:6px;color:var(--dsw-alias-label-secondary,#555);text-decoration:none;flex:none;transition:background .15s,color .15s;cursor:pointer}.sm-panelHeaderWhale:hover{background:var(--dsw-alias-interactive-bg-hover,#eee);color:var(--dsw-alias-label-primary,#111)}.sm-panelHeaderWhale svg{display:block;width:16px;height:16px;margin:auto}.sm-panelHeaderWhale .sm-updateDot{position:absolute;top:2px;right:2px;width:7px;height:7px;border-radius:50%;background:var(--dsw-alias-state-error-primary,#e53e3e);border:1.5px solid var(--dsw-alias-bg-layer-1,#fff)}.sm-panelHeaderWhale .sm-updateSpinner{position:absolute;inset:2px;border:2px solid transparent;border-top-color:var(--dsw-alias-state-business-primary,#356ae6);border-radius:50%;animation:sm-spin .8s linear infinite}@keyframes sm-spin{to{transform:rotate(360deg)}}.sm-updateDialogLayer{display:flex!important;align-items:center!important;justify-content:center!important;position:fixed;inset:0;padding:16px;box-sizing:border-box;background:transparent;border:0;box-shadow:none;z-index:10010;pointer-events:none}.sm-updateDialogLayer .sm-nativeDialog{position:relative;width:380px;max-width:calc(100vw - 32px);box-sizing:border-box;background:var(--sm-dialog-fill);color:var(--dsw-alias-label-primary,#111);border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:12px;box-shadow:0 16px 48px rgba(0,0,0,.25);overflow:hidden;animation:sm-confirmPop .14s ease-out;pointer-events:auto}.sm-updateDialog .sm-nativeDialogHeader{position:relative;display:flex;align-items:center;justify-content:space-between;gap:8px;padding:12px 16px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee);flex:none;min-height:46px;box-sizing:border-box}.sm-updateDialog .sm-nativeDialogTitle{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin:0;font-size:15px;line-height:20px;font-weight:600;text-align:center;width:auto;max-width:calc(100% - 80px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;z-index:0}.sm-updateDialog .sm-nativeDialogHeader .sm-panelHeaderClose{position:relative;z-index:1;margin-left:auto}.sm-updateDialog .sm-nativeDialogBody{padding:14px 16px;display:flex;flex-direction:column;gap:10px;font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary,#111)}.sm-updateDialog .sm-nativeDialogFooter{display:flex;padding:10px 16px;border-top:1px solid var(--dsw-alias-border-l2,#eee);flex:none;flex-wrap:wrap;justify-content:space-between;gap:8px;margin-top:0}.sm-updateDialog .sm-nativeDialogFooter .sm-nativeDialogConfirm:only-child{margin-left:auto}.sm-updateDialog .sm-nativeDialogButton{min-height:28px;padding:3px 14px;font-size:12px;line-height:20px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;cursor:pointer;background:transparent;color:var(--dsw-alias-label-primary,#111);text-align:center}.sm-updateDialog .sm-nativeDialogButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.sm-updateDialog .sm-nativeDialogConfirm{background:var(--dsw-alias-state-business-primary,#356ae6);color:var(--dsw-alias-label-primary-foreground,#fff);border-color:var(--dsw-alias-state-business-primary,#356ae6)}.sm-updateDialog .sm-nativeDialogConfirm:hover:not(:disabled){filter:brightness(.94)}.sm-updateVersionGrid{display:grid;grid-template-columns:auto 1fr;gap:6px 12px;padding:12px;background:var(--dsw-alias-bg-layer-2,#fff);border:1px solid var(--dsw-alias-border-l2,#eee);border-radius:8px;font-size:13px;line-height:20px}.sm-updateVersionLabel{color:var(--dsw-alias-label-secondary,#666);font-weight:500}.sm-updateVersionValue{color:var(--dsw-alias-label-primary,#111);font-family:monospace;font-size:13px}.sm-updateStatusBox{margin-top:10px;font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary,#111)}.sm-updateStatusBox.sm-warn{color:var(--dsw-alias-state-warn-label,#dd8629);background:var(--dsw-alias-state-warn-tertiary,#fef5e7);padding:8px 10px;border-radius:6px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-warn-primary,#f59e0b) 25%,transparent)}.sm-updateStatusBox.sm-error{color:var(--dsw-alias-state-error-primary,#ec1313);background:color-mix(in srgb,var(--dsw-alias-state-error-primary,#ec1313) 8%,transparent);padding:8px 10px;border-radius:6px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-error-primary,#ec1313) 22%,transparent)}.sm-updateStatusBox.sm-success{color:var(--dsw-alias-state-success-primary,#22c55e);background:var(--dsw-alias-state-success-tertiary,#e6faed);padding:8px 10px;border-radius:6px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-success-primary,#22c55e) 25%,transparent)}.sm-settingsCard{background:var(--sm-dialog-surface);border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:12px;padding:16px 20px;font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary,#111);box-sizing:border-box;width:100%}.sm-settingsHeader{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px;padding-bottom:10px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee)}.sm-settingsTitle{margin:0;font-size:16px;line-height:22px;font-weight:600;display:flex;align-items:center;gap:8px}.sm-settingsTag{font-size:12px;padding:2px 8px;border-radius:999px;background:var(--dsw-alias-markdown-tag,#f1f3f5);color:var(--dsw-alias-label-secondary,#666);font-family:monospace}.sm-settingsSection{margin-top:14px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l2,#eee)}.sm-settingsSectionTitle{font-size:13px;font-weight:600;color:var(--dsw-alias-label-secondary,#666);margin-bottom:8px;text-transform:uppercase;letter-spacing:.5px}.sm-settingsRow{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:32px}.sm-settingsActions{display:flex;align-items:center;gap:8px;margin-top:10px}.sm-settingsLink{color:var(--dsw-alias-state-business-primary,#356ae6);text-decoration:none}.sm-settingsLink:hover{text-decoration:underline}.sm-settingsCheckbox{display:inline-flex;align-items:center;gap:6px;cursor:pointer}.sm-settingsSelect{min-height:28px;height:28px;font-size:12px;line-height:20px;padding:3px 26px 3px 10px;border-radius:8px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#111);cursor:pointer}.sm-panelHeaderClose:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#eee);color:var(--dsw-alias-label-primary,#111)}.sm-panelHeaderClose:disabled{opacity:.5;cursor:not-allowed}.sm-migrateCurrent{padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;background:var(--dsw-alias-bg-layer-2,#fff);font-size:13px;line-height:18px;color:var(--dsw-alias-label-primary,#111)}.sm-migrateConfirmLayer.sm-confirmDialog.sm-nativeDialogLayer{width:360px;max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);inset:calc(50vh - 90px) auto auto calc(50vw + 308px)}.sm-migrateConfirmLayer.sm-confirmDialog.sm-nativeDialogLayer .sm-migrateDialog{position:relative;left:auto;top:auto;bottom:auto;transform:none;width:100%;max-width:calc(100vw - 32px);max-height:min(70vh,calc(100vh - 32px));z-index:auto;display:flex;flex-direction:column;overflow:hidden}.sm-migrateConfirmLayer .sm-migratePanel{min-width:0;max-height:calc(100vh - 120px);overflow-y:auto}.sm-migrateConfirmLayer .sm-nativeDialogButton.sm-nativeDialogConfirm:hover:not(:disabled){background-color:var(--dsw-alias-button-info-hover,#4576f0);color:var(--dsw-alias-label-primary-foreground,#fff);border-color:var(--dsw-alias-button-info-hover,#4576f0);filter:none}.sm-settingsSectionHeader{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}.sm-settingsSectionHeader .sm-settingsSectionTitle{margin-bottom:0}.sm-settingsVersionActions{display:flex;align-items:center;gap:8px}.sm-settingsCheckBtn{min-height:26px;padding:2px 12px;font-size:12px;line-height:18px;border-radius:6px;border:1px solid var(--dsw-alias-border-l2,#ddd);background:transparent;color:var(--dsw-alias-label-primary,#111);cursor:pointer;white-space:nowrap}.sm-settingsCheckBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.sm-settingsRowInline{justify-content:flex-start;flex-wrap:wrap;gap:8px;align-items:center}.sm-settingsBadgeLink{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:6px;border:1px solid var(--dsw-alias-border-l2,#ddd);background:var(--dsw-alias-markdown-tag,#f1f3f5);color:var(--dsw-alias-label-secondary,#555);font-size:12px;line-height:18px;text-decoration:none;cursor:pointer;transition:all .15s}.sm-settingsBadgeLink:hover{background:var(--dsw-alias-interactive-bg-hover,#eee);color:var(--dsw-alias-state-business-primary,#356ae6);border-color:var(--dsw-alias-state-business-primary,#356ae6)}.sm-settingsBadgeLink svg{flex:none}.sm-tooltip{z-index:1100!important;box-shadow:none!important;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.15))!important;filter:none!important}",
735
+ ".sm-panelHeaderClose{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;min-height:26px;padding:0;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:7px;background:transparent;color:var(--dsw-alias-label-secondary,#555);cursor:pointer;flex:none;box-sizing:border-box;outline:none}.sm-panelHeaderClose:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#eee);color:var(--dsw-alias-label-primary,#111)}.sm-panelHeaderClose svg{display:block;width:14px;height:14px;margin:auto}.sm-panelHeaderGithub{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border-radius:6px;color:var(--dsw-alias-label-secondary,#555);text-decoration:none;flex:none;transition:background .15s,color .15s;cursor:pointer}.sm-panelHeaderGithub:hover{background:var(--dsw-alias-interactive-bg-hover,#eee);color:var(--dsw-alias-label-primary,#111)}.sm-panelHeaderGithub svg{display:block;width:16px;height:16px;fill:currentColor}.sm-panelHeaderLeft{display:flex;align-items:center;gap:4px;flex:none;position:relative;z-index:1}.sm-panelHeaderWhale{display:inline-flex;align-items:center;justify-content:center;position:relative;width:26px;height:26px;border-radius:6px;color:var(--dsw-alias-label-secondary,#555);text-decoration:none;flex:none;transition:background .15s,color .15s;cursor:pointer}.sm-panelHeaderWhale:hover{background:var(--dsw-alias-interactive-bg-hover,#eee);color:var(--dsw-alias-label-primary,#111)}.sm-panelHeaderWhale svg{display:block;width:16px;height:16px;margin:auto}.sm-panelHeaderWhale .sm-updateDot{position:absolute;top:2px;right:2px;width:7px;height:7px;border-radius:50%;background:var(--dsw-alias-state-error-primary,#e53e3e);border:1.5px solid var(--dsw-alias-bg-layer-1,#fff)}.sm-panelHeaderWhale .sm-updateSpinner{position:absolute;inset:2px;border:2px solid transparent;border-top-color:var(--dsw-alias-state-business-primary,#356ae6);border-radius:50%;animation:sm-spin .8s linear infinite}@keyframes sm-spin{to{transform:rotate(360deg)}}.sm-updateDialogLayer{display:flex!important;align-items:center!important;justify-content:center!important;position:fixed;inset:0;padding:16px;box-sizing:border-box;background:transparent;border:0;box-shadow:none;z-index:10010;pointer-events:none}.sm-updateDialogLayer .sm-nativeDialog{position:relative;width:380px;max-width:calc(100vw - 32px);box-sizing:border-box;background:var(--sm-dialog-fill);color:var(--dsw-alias-label-primary,#111);border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:12px;box-shadow:0 16px 48px rgba(0,0,0,.25);overflow:hidden;animation:sm-confirmPop .14s ease-out;pointer-events:auto}.sm-updateDialog .sm-nativeDialogHeader{position:relative;display:flex;align-items:center;justify-content:space-between;gap:8px;padding:12px 16px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee);flex:none;min-height:46px;box-sizing:border-box}.sm-updateDialog .sm-nativeDialogTitle{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin:0;font-size:15px;line-height:20px;font-weight:600;text-align:center;width:auto;max-width:calc(100% - 80px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;z-index:0}.sm-updateDialog .sm-nativeDialogHeader .sm-panelHeaderClose{position:relative;z-index:1;margin-left:auto}.sm-updateDialog .sm-nativeDialogBody{padding:14px 16px;display:flex;flex-direction:column;gap:10px;font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary,#111)}.sm-updateDialog .sm-nativeDialogFooter{display:flex;padding:10px 16px;border-top:1px solid var(--dsw-alias-border-l2,#eee);flex:none;flex-wrap:wrap;justify-content:space-between;gap:8px;margin-top:0}.sm-updateDialog .sm-nativeDialogFooter .sm-nativeDialogConfirm:only-child{margin-left:auto}.sm-updateDialog .sm-nativeDialogButton{min-height:28px;padding:3px 14px;font-size:12px;line-height:20px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;cursor:pointer;background:transparent;color:var(--dsw-alias-label-primary,#111);text-align:center}.sm-updateDialog .sm-nativeDialogButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.sm-updateDialog .sm-nativeDialogConfirm{background:var(--dsw-alias-state-business-primary,#356ae6);color:var(--dsw-alias-label-primary-foreground,#fff);border-color:var(--dsw-alias-state-business-primary,#356ae6)}.sm-updateDialog .sm-nativeDialogConfirm:hover:not(:disabled){filter:brightness(.94)}.sm-updateVersionGrid{display:grid;grid-template-columns:auto 1fr;gap:6px 12px;padding:12px;background:var(--dsw-alias-bg-layer-2,#fff);border:1px solid var(--dsw-alias-border-l2,#eee);border-radius:8px;font-size:13px;line-height:20px}.sm-updateVersionLabel{color:var(--dsw-alias-label-secondary,#666);font-weight:500}.sm-updateVersionValue{color:var(--dsw-alias-label-primary,#111);font-family:monospace;font-size:13px}.sm-updateStatusBox{margin-top:10px;font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary,#111)}.sm-updateStatusBox.sm-warn{color:var(--dsw-alias-state-warn-label,#dd8629);background:var(--dsw-alias-state-warn-tertiary,#fef5e7);padding:8px 10px;border-radius:6px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-warn-primary,#f59e0b) 25%,transparent)}.sm-updateStatusBox.sm-error{color:var(--dsw-alias-state-error-primary,#ec1313);background:color-mix(in srgb,var(--dsw-alias-state-error-primary,#ec1313) 8%,transparent);padding:8px 10px;border-radius:6px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-error-primary,#ec1313) 22%,transparent)}.sm-updateStatusBox.sm-success{color:var(--dsw-alias-state-success-primary,#22c55e);background:var(--dsw-alias-state-success-tertiary,#e6faed);padding:8px 10px;border-radius:6px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-success-primary,#22c55e) 25%,transparent)}.sm-settingsCard{background:var(--sm-dialog-surface);border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:12px;padding:16px 20px;font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary,#111);box-sizing:border-box;width:100%}.sm-settingsHeader{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px;padding-bottom:10px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee)}.sm-settingsTitle{margin:0;font-size:16px;line-height:22px;font-weight:600;display:flex;align-items:center;gap:8px}.sm-settingsTag{font-size:12px;padding:2px 8px;border-radius:999px;background:var(--dsw-alias-markdown-tag,#f1f3f5);color:var(--dsw-alias-label-secondary,#666);font-family:monospace}.sm-settingsSection{margin-top:14px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l2,#eee)}.sm-settingsSectionTitle{font-size:13px;font-weight:600;color:var(--dsw-alias-label-secondary,#666);margin-bottom:8px;text-transform:uppercase;letter-spacing:.5px}.sm-settingsRow{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:36px;margin-top:8px}.sm-settingsRow:first-of-type{margin-top:0}.sm-settingsRow>span:first-child{white-space:nowrap;flex-shrink:0}.sm-settingsActions{display:flex;align-items:center;gap:8px;margin-top:10px}.sm-settingsLink{color:var(--dsw-alias-state-business-primary,#356ae6);text-decoration:none}.sm-settingsLink:hover{text-decoration:underline}.sm-settingsCheckbox{display:inline-flex;align-items:center;gap:6px;cursor:pointer}.sm-settingsSelect{min-height:30px;height:30px;width:280px;min-width:280px;max-width:280px;flex:0 0 280px;box-sizing:border-box;font-size:12px;line-height:20px;padding:4px 26px 4px 10px;border-radius:8px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#111);cursor:pointer}.sm-settingsRowLeft{display:flex;flex-direction:column;align-items:flex-start;justify-content:center;gap:2px;min-width:0;flex:1 1 auto}.sm-settingsRowLeft>span:first-child{white-space:nowrap;flex-shrink:0}.sm-panelHeaderClose:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#eee);color:var(--dsw-alias-label-primary,#111)}.sm-panelHeaderClose:disabled{opacity:.5;cursor:not-allowed}.sm-migrateCurrent{padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;background:var(--dsw-alias-bg-layer-2,#fff);font-size:13px;line-height:18px;color:var(--dsw-alias-label-primary,#111)}.sm-migrateConfirmLayer.sm-confirmDialog.sm-nativeDialogLayer{width:360px;max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);inset:calc(50vh - 90px) auto auto calc(50vw + 308px)}.sm-migrateConfirmLayer.sm-confirmDialog.sm-nativeDialogLayer .sm-migrateDialog{position:relative;left:auto;top:auto;bottom:auto;transform:none;width:100%;max-width:calc(100vw - 32px);max-height:min(70vh,calc(100vh - 32px));z-index:auto;display:flex;flex-direction:column;overflow:hidden}.sm-migrateConfirmLayer .sm-migratePanel{min-width:0;max-height:calc(100vh - 120px);overflow-y:auto}.sm-migrateConfirmLayer .sm-nativeDialogButton.sm-nativeDialogConfirm:hover:not(:disabled){background-color:var(--dsw-alias-button-info-hover,#4576f0);color:var(--dsw-alias-label-primary-foreground,#fff);border-color:var(--dsw-alias-button-info-hover,#4576f0);filter:none}.sm-settingsSectionHeader{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}.sm-settingsSectionHeader .sm-settingsSectionTitle{margin-bottom:0}.sm-settingsVersionActions{display:flex;align-items:center;gap:8px}.sm-settingsCheckBtn{min-height:26px;padding:2px 12px;font-size:12px;line-height:18px;border-radius:6px;border:1px solid var(--dsw-alias-border-l2,#ddd);background:transparent;color:var(--dsw-alias-label-primary,#111);cursor:pointer;white-space:nowrap}.sm-settingsCheckBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.sm-settingsRowInline{justify-content:flex-start;flex-wrap:wrap;gap:8px;align-items:center}.sm-settingsProjectLeft{display:flex;align-items:center;gap:8px;min-width:0;flex-wrap:nowrap}.sm-settingsProjectLabel{color:var(--dsw-alias-label-primary,#111);white-space:nowrap;flex-shrink:0}.sm-settingsBadgeActions{display:flex;align-items:center;gap:8px;margin-left:auto;flex:0 0 280px;width:280px;min-width:280px;max-width:280px;box-sizing:border-box;justify-content:flex-end}.sm-settingsBadgeActions>*{display:inline-flex;flex:1 1 0;min-width:0}.sm-settingsBadgeLink{display:inline-flex;align-items:center;justify-content:center;gap:6px;width:100%;padding:4px 10px;min-height:28px;height:28px;box-sizing:border-box;border-radius:6px;border:1px solid var(--dsw-alias-border-l2,#ddd);background:var(--dsw-alias-markdown-tag,#f1f3f5);color:var(--dsw-alias-label-secondary,#555);font-size:12px;line-height:18px;text-decoration:none;cursor:pointer;white-space:nowrap;transition:all .15s}.sm-settingsBadgeLink:hover{background:var(--dsw-alias-interactive-bg-hover,#eee);color:var(--dsw-alias-state-business-primary,#356ae6);border-color:var(--dsw-alias-state-business-primary,#356ae6)}.sm-settingsBadgeLink svg{flex:none}.sm-tooltip{z-index:1100!important;box-shadow:none!important;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.15))!important;filter:none!important}",
730
736
 
731
737
  // Issue #23 — dialog opacity.
732
738
  //
@@ -768,14 +774,14 @@ function isTopmostDialog(z) {
768
774
  ".sm-panelDialog option:checked,.sm-nativeDialog option:checked,.sm-settingsCard option:checked,.sm-headerMenu option:checked{background-color:var(--dsw-alias-bg-layer-3,#fff);color:var(--dsw-alias-state-business-primary,#356ae6)}",
769
775
  ".sm-panelDialog option:disabled,.sm-nativeDialog option:disabled,.sm-settingsCard option:disabled,.sm-headerMenu option:disabled{color:var(--dsw-alias-label-tertiary,#888)}",
770
776
  ".sm-nativeDialog,.sm-panelDialog,.sm-migrateDialog,.sm-settingsCard{--sm-dialog-surface:var(--dsw-alias-bg-layer-1,var(--sm-dialog-surface-fallback,#fff));--sm-dialog-fill:color-mix(in srgb,var(--sm-dialog-surface) var(--sm-dialog-opacity,100%),transparent)}",
771
- ".sm-settingsSlider{display:flex;align-items:center;gap:8px;flex:1 1 auto;justify-content:flex-end;min-width:0}",
772
- ".sm-settingsRange{flex:1 1 120px;min-width:96px;max-width:220px;margin:0;cursor:pointer;accent-color:var(--dsw-alias-state-business-primary,#356ae6)}",
777
+ ".sm-settingsSlider{display:flex;align-items:center;gap:8px;flex:0 0 280px;width:280px;min-width:280px;max-width:280px;box-sizing:border-box;justify-content:flex-end}",
778
+ ".sm-settingsRange{flex:1 1 auto;min-width:70px;margin:0;cursor:pointer;accent-color:var(--dsw-alias-state-business-primary,#356ae6)}",
773
779
  ".sm-settingsRange:disabled{cursor:not-allowed}",
774
- ".sm-settingsRangeValue{min-width:38px;text-align:right;font-size:12px;line-height:18px;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-secondary,#666)}",
775
- ".sm-settingsResetBtn{min-height:26px;padding:2px 10px;font-size:12px;line-height:18px;border-radius:6px;border:1px solid var(--dsw-alias-border-l2,#ddd);background:transparent;color:var(--dsw-alias-label-secondary,#666);cursor:pointer;white-space:nowrap}",
780
+ ".sm-settingsRangeValue{min-width:38px;text-align:right;font-size:12px;line-height:18px;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-secondary,#666);flex-shrink:0}",
781
+ ".sm-settingsResetBtn{min-height:28px;height:28px;padding:2px 10px;font-size:12px;line-height:18px;border-radius:6px;border:1px solid var(--dsw-alias-border-l2,#ddd);background:transparent;color:var(--dsw-alias-label-secondary,#666);cursor:pointer;white-space:nowrap;flex:0 0 auto;box-sizing:border-box}",
776
782
  ".sm-settingsResetBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary,#111)}",
777
783
  ".sm-settingsResetBtn:disabled{opacity:.5;cursor:not-allowed}",
778
- ".sm-settingsHint{margin-top:4px;font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#888)}",
784
+ ".sm-settingsHint{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#888)}.sm-settingsHintInline{margin-left:0;font-weight:normal}",
779
785
 
780
786
  // DSH's own theme is the single source of colour. Every rule above reads
781
787
  // --dsw-alias-* tokens directly and none of them is redefined here: the
@@ -842,7 +848,7 @@ function isTopmostDialog(z) {
842
848
  ".sm-bulkDialog .sm-nativeDialog{width:100%;max-height:min(640px,calc(100vh - 32px))}",
843
849
  ".sm-bulkDialogBody{display:flex;flex-direction:column;gap:12px;min-height:0}",
844
850
  ".sm-bulkDialogSummary{font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary,#111)}",
845
- ".sm-bulkDialogWarn{display:flex;align-items:flex-start;gap:8px;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px;background:color-mix(in srgb,var(--dsw-alias-state-error-primary,#ec1313) 8%,transparent);border:1px solid color-mix(in srgb,var(--dsw-alias-state-error-primary,#ec1313) 25%,transparent);border-radius:8px;padding:8px 10px}",
851
+ ".sm-bulkDialogWarn{display:flex;align-items:flex-start;gap:8px;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px;background:color-mix(in srgb,var(--dsw-alias-state-error-primary,#ec1313) 8%,transparent);border:1px solid color-mix(in srgb,var(--dsw-alias-state-error-primary,#ec1313) 25%,transparent);border-radius:8px;padding:8px 10px;white-space:pre-line}",
846
852
  ".sm-bulkSessionList{display:flex;flex-direction:column;gap:4px;max-height:200px;overflow:auto;border:1px solid var(--dsw-alias-border-l2,#eee);border-radius:8px;padding:6px}",
847
853
  ".sm-bulkSessionItem{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#666);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
848
854
  ".sm-bulkProgressBar{position:relative;height:8px;background:var(--dsw-alias-bg-skeleton,#0000000a);border-radius:4px;overflow:hidden}",
@@ -856,7 +862,8 @@ function isTopmostDialog(z) {
856
862
  ".sm-bulkResultItemReason{color:var(--dsw-alias-state-error-primary);font-size:11px;line-height:16px;word-break:break-word}",
857
863
  ".sm-bulkResultItemReasonSkip{color:var(--dsw-alias-label-tertiary,#888)}",
858
864
  ".sm-bulkResultFooter{display:flex;justify-content:space-between;align-items:center;gap:8px;flex-wrap:wrap}",
859
- ".sm-footerBtn-wide{flex:none;display:flex;align-items:center;gap:8px;width:calc(100% + 8px);height:34px;margin:4px -4px 4px;padding:6px 2px 6px 10px;box-sizing:border-box;border:none;border-radius:12px;cursor:pointer;overflow:hidden;font-size:14px;line-height:22px}",
865
+ "[class*=footerActions]:has(.sm-footerBtn-wide),[class*=footer-actions]:has(.sm-footerBtn-wide),[class*=footer]:has(> .sm-footerBtn-wide),[class*=footer] > div:has(> .sm-footerBtn-wide){flex-wrap:wrap;align-content:flex-start;height:auto}",
866
+ ".sm-footerBtn-wide{flex:1 1 100%;display:flex;align-items:center;gap:8px;width:calc(100% + 8px);height:34px;margin:4px -4px 4px;padding:6px 2px 6px 10px;box-sizing:border-box;border:none;border-radius:12px;cursor:pointer;overflow:hidden;font-size:14px;line-height:22px}",
860
867
  ".sm-footerBtn-wide:hover,.sm-footerBtn-wide:focus-visible{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}",
861
868
  ".sm-footerBtn-rail{width:36px;height:36px;padding:0;justify-content:center;border-radius:12px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;display:inline-flex;align-items:center}",
862
869
  ".sm-footerBtn-rail:hover,.sm-footerBtn-rail:focus-visible{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}",
@@ -1131,6 +1138,10 @@ function isTopmostDialog(z) {
1131
1138
  if (zRef.current === null) {
1132
1139
  zRef.current = nextDialogZ();
1133
1140
  }
1141
+ useEffect(() => {
1142
+ registerDialogZ(zRef.current);
1143
+ return () => unregisterDialogZ(zRef.current);
1144
+ }, []);
1134
1145
  const root = getOverlayRoot();
1135
1146
  const portal = getPortalFn();
1136
1147
 
@@ -1158,6 +1169,8 @@ function isTopmostDialog(z) {
1158
1169
  zIndex: -1
1159
1170
  },
1160
1171
  onClick: (e) => {
1172
+ if (e && typeof e.stopPropagation === "function") e.stopPropagation();
1173
+ if (e && typeof e.stopImmediatePropagation === "function") e.stopImmediatePropagation();
1161
1174
  if (e.target === e.currentTarget && onBackdropClick) {
1162
1175
  onBackdropClick(e);
1163
1176
  }
@@ -2088,6 +2101,18 @@ function isTopmostDialog(z) {
2088
2101
  }
2089
2102
  }
2090
2103
 
2104
+ function getTargetSmSelect(target) {
2105
+ if (!target || typeof target.closest !== "function") return null;
2106
+ const direct = target.closest(SM_SELECT_SELECTOR);
2107
+ if (direct && direct.tagName === "SELECT") return direct;
2108
+ const container = target.closest(".sm-wsSortField, .sm-annotationPriority, .sm-viewField, .sm-moveSection, .sm-settingsRow, .sm-customSelectWrapper");
2109
+ if (container) {
2110
+ const sel = container.querySelector("select");
2111
+ if (sel && sel.tagName === "SELECT") return sel;
2112
+ }
2113
+ return null;
2114
+ }
2115
+
2091
2116
  function installCustomDropdownInterceptor() {
2092
2117
  if (typeof document === "undefined" || typeof document.addEventListener !== "function") {
2093
2118
  return () => {};
@@ -2101,24 +2126,8 @@ function isTopmostDialog(z) {
2101
2126
  if (activeCustomMenu && activeCustomMenu.contains(e.target)) return;
2102
2127
  if (e.target?.closest?.("button, input, textarea, a")) return;
2103
2128
 
2104
- let select = null;
2105
- if (e.target?.matches?.("select")) {
2106
- select = e.target;
2107
- } else if (e.target?.closest?.("select")) {
2108
- select = e.target.closest("select");
2109
- } else {
2110
- const container = e.target?.closest?.(".sm-wsSortField, .sm-annotationPriority, .sm-viewField, .sm-moveSection, .sm-settingsRow, .sm-customSelectWrapper");
2111
- if (container) {
2112
- select = container.querySelector("select");
2113
- } else {
2114
- const nearest = e.target?.closest?.(SM_SELECT_SELECTOR);
2115
- if (nearest && nearest.tagName === "SELECT") {
2116
- select = nearest;
2117
- }
2118
- }
2119
- }
2120
-
2121
- if (select && select.tagName === "SELECT") {
2129
+ const select = getTargetSmSelect(e.target);
2130
+ if (select) {
2122
2131
  e.preventDefault();
2123
2132
  e.stopPropagation();
2124
2133
  if (select.disabled) return;
@@ -2134,14 +2143,8 @@ function isTopmostDialog(z) {
2134
2143
 
2135
2144
  const onClick = (e) => {
2136
2145
  if (e.target?.closest?.("button, input, textarea, a")) return;
2137
- let select = null;
2138
- if (e.target?.matches?.("select")) {
2139
- select = e.target;
2140
- } else {
2141
- const container = e.target?.closest?.(".sm-wsSortField, .sm-annotationPriority, .sm-viewField, .sm-moveSection, .sm-settingsRow, .sm-customSelectWrapper");
2142
- if (container) select = container.querySelector("select");
2143
- }
2144
- if (select && select.tagName === "SELECT") {
2146
+ const select = getTargetSmSelect(e.target);
2147
+ if (select) {
2145
2148
  e.preventDefault();
2146
2149
  e.stopPropagation();
2147
2150
  }
@@ -2158,7 +2161,7 @@ function isTopmostDialog(z) {
2158
2161
  }
2159
2162
 
2160
2163
  const focused = document.activeElement;
2161
- const targetSelect = activeCustomSelect || (focused?.matches?.(SM_SELECT_SELECTOR) ? focused : null) || (focused?.closest?.(".sm-wsSortField, .sm-annotationPriority, .sm-viewField, .sm-moveSection, .sm-settingsRow")?.querySelector("select"));
2164
+ const targetSelect = activeCustomSelect || getTargetSmSelect(focused);
2162
2165
  if (!targetSelect || targetSelect.tagName !== "SELECT") return;
2163
2166
 
2164
2167
  if (e.key === "Enter" || e.key === " ") {
@@ -2184,7 +2187,14 @@ function isTopmostDialog(z) {
2184
2187
  }
2185
2188
  };
2186
2189
 
2187
- const onResizeOrScroll = () => {
2190
+ const onResizeOrScroll = (e) => {
2191
+ // Ignore scrolls that originate inside the open menu: the listener is bound on
2192
+ // window with capture=true, so scrolling the menu's own option list (wheel or
2193
+ // scrollbar drag) would otherwise close it immediately.
2194
+ // Reference: PR #37
2195
+ if (activeCustomMenu && e && e.type === "scroll" && e.target && typeof activeCustomMenu.contains === "function" && activeCustomMenu.contains(e.target)) {
2196
+ return;
2197
+ }
2188
2198
  if (activeCustomMenu) closeCustomSelect();
2189
2199
  };
2190
2200
 
@@ -2591,18 +2601,51 @@ function isTopmostDialog(z) {
2591
2601
  ].join(String.fromCharCode(10))
2592
2602
  };
2593
2603
  const promptFor = lang => CLIPBOARD_PROMPT_TEXT[lang] || CLIPBOARD_PROMPT_TEXT.zh;
2604
+ function isClickInsideDialog(e, dialogRef, layerRef) {
2605
+ if (!e || !e.target) return false;
2606
+ const target = e.target;
2607
+ if (dialogRef && dialogRef.current && typeof dialogRef.current.contains === "function" && dialogRef.current.contains(target)) {
2608
+ return true;
2609
+ }
2610
+ if (layerRef && layerRef.current && typeof layerRef.current.contains === "function" && layerRef.current.contains(target)) {
2611
+ return true;
2612
+ }
2613
+ if (typeof target.closest === "function") {
2614
+ if (dialogRef && dialogRef.current && target.closest(".sm-nativeDialog") === dialogRef.current) {
2615
+ return true;
2616
+ }
2617
+ if (layerRef && layerRef.current && target.closest(".sm-nativeDialogLayer") === layerRef.current) {
2618
+ return true;
2619
+ }
2620
+ }
2621
+ if (activeCustomMenu && typeof activeCustomMenu.contains === "function" && activeCustomMenu.contains(target)) {
2622
+ if (activeCustomSelect) {
2623
+ if (dialogRef && dialogRef.current && typeof dialogRef.current.contains === "function" && dialogRef.current.contains(activeCustomSelect)) {
2624
+ return true;
2625
+ }
2626
+ if (layerRef && layerRef.current && typeof layerRef.current.contains === "function" && layerRef.current.contains(activeCustomSelect)) {
2627
+ return true;
2628
+ }
2629
+ }
2630
+ }
2631
+ return false;
2632
+ }
2633
+
2594
2634
  function BatchPreviewDialog(props) {
2595
2635
  const t = typeof props.t === "function" ? props.t : (key) => key;
2596
2636
  const open = props.open === true;
2597
2637
  const onCancel = typeof props.onCancel === "function" ? props.onCancel : () => {};
2598
2638
  const onExecute = typeof props.onExecute === "function" ? props.onExecute : () => {};
2639
+ const danger = props.danger === true;
2599
2640
  const actionLabel = props.actionLabel || "";
2600
2641
  const summaryAction = props.summaryAction || actionLabel;
2642
+ const title = props.title || (props.action === "delete" ? (props.t ? t("confirm.delete.title") : "删除会话") : t("bulk.dialog.title", { action: actionLabel }));
2601
2643
  const sessions = Array.isArray(props.sessions) ? props.sessions : [];
2602
2644
  const warning = typeof props.warning === "string" ? props.warning : "";
2603
2645
  const confirmLabel = props.confirmLabel || t("bulk.dialog.execute");
2604
2646
  const cancelLabel = props.cancelLabel || t("bulk.dialog.cancel");
2605
- const danger = props.danger === true;
2647
+ const dialogRef = useRef(null);
2648
+ const layerRef = useRef(null);
2606
2649
  const [expanded, setExpanded] = useState(false);
2607
2650
  const [zIndex] = useState(() => open ? nextDialogZ() : 0);
2608
2651
  const floatingCoords = useFloatingPosition(props.anchor, open, { placement: "bottom-end", dialogWidth: 360, dialogHeight: 320 });
@@ -2614,15 +2657,33 @@ function isTopmostDialog(z) {
2614
2657
  const handler = (e) => {
2615
2658
  if (e.key !== "Escape" || e.defaultPrevented) return;
2616
2659
  if (!isTopmostDialog(zIndex)) return;
2660
+ if (props.busy) return;
2617
2661
  e.preventDefault(); e.stopPropagation(); escStateRef.current.onCancel();
2618
2662
  };
2619
2663
  window.addEventListener("keydown", handler);
2664
+ const mountTime = Date.now();
2665
+ const clickHandler = (e) => {
2666
+ if (Date.now() - mountTime < 50) return;
2667
+ if (e.defaultPrevented) return;
2668
+ if (props.busy) return;
2669
+ if (!isTopmostDialog(zIndex)) return;
2670
+ if (isClickInsideDialog(e, dialogRef, layerRef)) return;
2671
+ if (e && typeof e.preventDefault === "function") e.preventDefault();
2672
+ if (e && typeof e.stopPropagation === "function") e.stopPropagation();
2673
+ if (e && typeof e.stopImmediatePropagation === "function") e.stopImmediatePropagation();
2674
+ escStateRef.current.onCancel();
2675
+ };
2676
+ window.addEventListener("click", clickHandler, true);
2620
2677
  return () => {
2621
2678
  unregisterDialogZ(zIndex);
2622
2679
  window.removeEventListener("keydown", handler);
2680
+ window.removeEventListener("click", clickHandler, true);
2623
2681
  };
2624
2682
  }, [open, zIndex]);
2625
2683
  if (!open) return null;
2684
+ const loading = props.loading === true;
2685
+ const hasError = Boolean(props.error);
2686
+ const confirmDisabled = loading || hasError;
2626
2687
  const showExpand = sessions.length > 12;
2627
2688
  const visible = expanded ? sessions : sessions.slice(0, 12);
2628
2689
  const topPx = floatingCoords ? floatingCoords.top : null;
@@ -2644,9 +2705,9 @@ function isTopmostDialog(z) {
2644
2705
  : {})
2645
2706
  };
2646
2707
  const portalOverlay = renderInOverlay;
2647
- return portalOverlay(h("div", { className: "sm-nativeDialogLayer sm-bulkDialog" + (hasCoords ? " sm-bulkDialog-anchored" : ""), role: "presentation", style: layerStyle },
2648
- h("section", { className: "sm-nativeDialog", style: { pointerEvents: "auto" }, role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-batch-preview-title" },
2649
- h("div", { className: "sm-nativeDialogHeader" }, h("h2", { id: "sm-batch-preview-title", className: "sm-nativeDialogTitle" }, t("bulk.dialog.title", { action: actionLabel }))),
2708
+ return portalOverlay(h("div", { ref: layerRef, className: "sm-nativeDialogLayer sm-bulkDialog" + (hasCoords ? " sm-bulkDialog-anchored" : ""), role: "presentation", style: layerStyle },
2709
+ h("section", { ref: dialogRef, className: "sm-nativeDialog", style: { pointerEvents: "auto" }, role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-batch-preview-title" },
2710
+ h("div", { className: "sm-nativeDialogHeader" }, h("h2", { id: "sm-batch-preview-title", className: "sm-nativeDialogTitle" }, title)),
2650
2711
  h("div", { className: "sm-nativeDialogBody" },
2651
2712
  h("div", { className: "sm-bulkDialogSummary" }, t("bulk.dialog.summary", { count: sessions.length, action: actionLabel })),
2652
2713
  warning ? h("div", { className: "sm-bulkDialogWarn" }, warning) : null,
@@ -2657,7 +2718,12 @@ function isTopmostDialog(z) {
2657
2718
  ),
2658
2719
  h("div", { className: "sm-nativeDialogFooter" },
2659
2720
  h("button", { type: "button", className: "sm-nativeDialogButton sm-nativeDialogCancel", onClick: onCancel }, cancelLabel),
2660
- h("button", { type: "button", className: "sm-nativeDialogButton" + (danger ? " sm-nativeDialogDanger" : " sm-nativeDialogConfirm"), onClick: onExecute }, confirmLabel)
2721
+ h("button", {
2722
+ type: "button",
2723
+ className: "sm-nativeDialogButton" + (danger ? " sm-nativeDialogDanger" : " sm-nativeDialogConfirm"),
2724
+ disabled: props.busy || confirmDisabled,
2725
+ onClick: () => { if (!props.busy && !confirmDisabled) onExecute(); }
2726
+ }, confirmLabel)
2661
2727
  )
2662
2728
  )
2663
2729
  ));
@@ -2667,6 +2733,11 @@ function isTopmostDialog(z) {
2667
2733
  const t = typeof props.t === "function" ? props.t : (key) => key;
2668
2734
  const open = props.open === true;
2669
2735
  const [zIndex] = useState(() => open ? nextDialogZ() : 0);
2736
+ useEffect(() => {
2737
+ if (!open) return;
2738
+ registerDialogZ(zIndex);
2739
+ return () => unregisterDialogZ(zIndex);
2740
+ }, [open, zIndex]);
2670
2741
  const actionLabel = props.actionLabel || "";
2671
2742
  const done = Number.isInteger(props.done) ? props.done : 0;
2672
2743
  const total = Number.isInteger(props.total) ? props.total : 0;
@@ -2714,6 +2785,8 @@ function isTopmostDialog(z) {
2714
2785
  function BatchResultDialog(props) {
2715
2786
  const t = typeof props.t === "function" ? props.t : (key) => key;
2716
2787
  const open = props.open === true;
2788
+ const dialogRef = useRef(null);
2789
+ const layerRef = useRef(null);
2717
2790
  const [zIndex] = useState(() => open ? nextDialogZ() : 0);
2718
2791
  const actionLabel = props.actionLabel || "";
2719
2792
  const items = Array.isArray(props.items) ? props.items : [];
@@ -2733,9 +2806,22 @@ function isTopmostDialog(z) {
2733
2806
  e.preventDefault(); e.stopPropagation(); escStateRef.current.onClose();
2734
2807
  };
2735
2808
  window.addEventListener("keydown", handler);
2809
+ const mountTime = Date.now();
2810
+ const clickHandler = (e) => {
2811
+ if (Date.now() - mountTime < 50) return;
2812
+ if (e.defaultPrevented) return;
2813
+ if (!isTopmostDialog(zIndex)) return;
2814
+ if (isClickInsideDialog(e, dialogRef, layerRef)) return;
2815
+ if (e && typeof e.preventDefault === "function") e.preventDefault();
2816
+ if (e && typeof e.stopPropagation === "function") e.stopPropagation();
2817
+ if (e && typeof e.stopImmediatePropagation === "function") e.stopImmediatePropagation();
2818
+ escStateRef.current.onClose();
2819
+ };
2820
+ window.addEventListener("click", clickHandler, true);
2736
2821
  return () => {
2737
2822
  unregisterDialogZ(zIndex);
2738
2823
  window.removeEventListener("keydown", handler);
2824
+ window.removeEventListener("click", clickHandler, true);
2739
2825
  };
2740
2826
  }, [open, zIndex]);
2741
2827
  if (!open) return null;
@@ -2777,8 +2863,8 @@ function isTopmostDialog(z) {
2777
2863
  : {})
2778
2864
  };
2779
2865
  const portalOverlay = renderInOverlay;
2780
- return portalOverlay(h("div", { className: "sm-nativeDialogLayer sm-bulkDialog" + (hasCoords ? " sm-bulkDialog-anchored" : ""), role: "presentation", style: layerStyle },
2781
- h("section", { className: "sm-nativeDialog", style: { pointerEvents: "auto" }, role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-batch-result-title" },
2866
+ return portalOverlay(h("div", { ref: layerRef, className: "sm-nativeDialogLayer sm-bulkDialog" + (hasCoords ? " sm-bulkDialog-anchored" : ""), role: "presentation", style: layerStyle },
2867
+ h("section", { ref: dialogRef, className: "sm-nativeDialog", style: { pointerEvents: "auto" }, role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-batch-result-title" },
2782
2868
  h("div", { className: "sm-nativeDialogHeader" }, h("h2", { id: "sm-batch-result-title", className: "sm-nativeDialogTitle" }, t("bulk.result.title", { action: actionLabel }))),
2783
2869
  h("div", { className: "sm-nativeDialogBody" },
2784
2870
  renderGroup("bulk.result.successGroup", groups.success),
@@ -2826,6 +2912,8 @@ function isTopmostDialog(z) {
2826
2912
  function BulkTagInputDialog(props) {
2827
2913
  const t = typeof props.t === "function" ? props.t : (key) => key;
2828
2914
  const open = props.open === true;
2915
+ const dialogRef = useRef(null);
2916
+ const layerRef = useRef(null);
2829
2917
  const [zIndex] = useState(() => open ? nextDialogZ() : 0);
2830
2918
  const title = props.title || t("bulk.tagsAdd");
2831
2919
  const description = props.description || "";
@@ -2847,9 +2935,22 @@ function isTopmostDialog(z) {
2847
2935
  e.preventDefault(); e.stopPropagation(); escStateRef.current.onCancel();
2848
2936
  };
2849
2937
  window.addEventListener("keydown", handler);
2938
+ const mountTime = Date.now();
2939
+ const clickHandler = (e) => {
2940
+ if (Date.now() - mountTime < 50) return;
2941
+ if (e.defaultPrevented) return;
2942
+ if (!isTopmostDialog(zIndex)) return;
2943
+ if (isClickInsideDialog(e, dialogRef, layerRef)) return;
2944
+ if (e && typeof e.preventDefault === "function") e.preventDefault();
2945
+ if (e && typeof e.stopPropagation === "function") e.stopPropagation();
2946
+ if (e && typeof e.stopImmediatePropagation === "function") e.stopImmediatePropagation();
2947
+ escStateRef.current.onCancel();
2948
+ };
2949
+ window.addEventListener("click", clickHandler, true);
2850
2950
  return () => {
2851
2951
  unregisterDialogZ(zIndex);
2852
2952
  window.removeEventListener("keydown", handler);
2953
+ window.removeEventListener("click", clickHandler, true);
2853
2954
  };
2854
2955
  }, [open, zIndex]);
2855
2956
  if (!open) return null;
@@ -2876,8 +2977,8 @@ function isTopmostDialog(z) {
2876
2977
  : {})
2877
2978
  };
2878
2979
  const portalOverlay = renderInOverlay;
2879
- return portalOverlay(h("div", { className: "sm-nativeDialogLayer sm-bulkDialog" + (hasCoords ? " sm-bulkDialog-anchored" : ""), role: "presentation", style: layerStyle },
2880
- h("section", { className: "sm-nativeDialog", style: { pointerEvents: "auto" }, role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-bulk-tags-title" },
2980
+ return portalOverlay(h("div", { ref: layerRef, className: "sm-nativeDialogLayer sm-bulkDialog" + (hasCoords ? " sm-bulkDialog-anchored" : ""), role: "presentation", style: layerStyle },
2981
+ h("section", { ref: dialogRef, className: "sm-nativeDialog", style: { pointerEvents: "auto" }, role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-bulk-tags-title" },
2881
2982
  h("div", { className: "sm-nativeDialogHeader" }, h("h2", { id: "sm-bulk-tags-title", className: "sm-nativeDialogTitle" }, title)),
2882
2983
  h("div", { className: "sm-nativeDialogBody" },
2883
2984
  description ? h("div", { className: "sm-bulkDialogSummary" }, description) : null,
@@ -2894,6 +2995,8 @@ function isTopmostDialog(z) {
2894
2995
  function BulkChoiceDialog(props) {
2895
2996
  const t = typeof props.t === "function" ? props.t : (key) => key;
2896
2997
  const open = props.open === true;
2998
+ const dialogRef = useRef(null);
2999
+ const layerRef = useRef(null);
2897
3000
  const [zIndex] = useState(() => open ? nextDialogZ() : 0);
2898
3001
  const title = props.title || "";
2899
3002
  const description = props.description || "";
@@ -2915,9 +3018,22 @@ function isTopmostDialog(z) {
2915
3018
  e.preventDefault(); e.stopPropagation(); escStateRef.current.onCancel();
2916
3019
  };
2917
3020
  window.addEventListener("keydown", handler);
3021
+ const mountTime = Date.now();
3022
+ const clickHandler = (e) => {
3023
+ if (Date.now() - mountTime < 50) return;
3024
+ if (e.defaultPrevented) return;
3025
+ if (!isTopmostDialog(zIndex)) return;
3026
+ if (isClickInsideDialog(e, dialogRef, layerRef)) return;
3027
+ if (e && typeof e.preventDefault === "function") e.preventDefault();
3028
+ if (e && typeof e.stopPropagation === "function") e.stopPropagation();
3029
+ if (e && typeof e.stopImmediatePropagation === "function") e.stopImmediatePropagation();
3030
+ escStateRef.current.onCancel();
3031
+ };
3032
+ window.addEventListener("click", clickHandler, true);
2918
3033
  return () => {
2919
3034
  unregisterDialogZ(zIndex);
2920
3035
  window.removeEventListener("keydown", handler);
3036
+ window.removeEventListener("click", clickHandler, true);
2921
3037
  };
2922
3038
  }, [open, zIndex]);
2923
3039
  if (!open) return null;
@@ -2940,8 +3056,8 @@ function isTopmostDialog(z) {
2940
3056
  : {})
2941
3057
  };
2942
3058
  const portalOverlay = renderInOverlay;
2943
- return portalOverlay(h("div", { className: "sm-nativeDialogLayer sm-bulkDialog" + (hasCoords ? " sm-bulkDialog-anchored" : ""), role: "presentation", style: layerStyle },
2944
- h("section", { className: "sm-nativeDialog", style: { pointerEvents: "auto" }, role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-bulk-choice-title" },
3059
+ return portalOverlay(h("div", { ref: layerRef, className: "sm-nativeDialogLayer sm-bulkDialog" + (hasCoords ? " sm-bulkDialog-anchored" : ""), role: "presentation", style: layerStyle },
3060
+ h("section", { ref: dialogRef, className: "sm-nativeDialog", style: { pointerEvents: "auto" }, role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-bulk-choice-title" },
2945
3061
  h("div", { className: "sm-nativeDialogHeader" }, h("h2", { id: "sm-bulk-choice-title", className: "sm-nativeDialogTitle" }, title)),
2946
3062
  h("div", { className: "sm-nativeDialogBody" },
2947
3063
  description ? h("div", { className: "sm-bulkDialogSummary" }, description) : null,
@@ -3167,6 +3283,8 @@ function isTopmostDialog(z) {
3167
3283
  const onConfirm = typeof props.onConfirm === "function" ? props.onConfirm : () => {};
3168
3284
  const busy = props.busy === true;
3169
3285
  const danger = props.danger === true;
3286
+ const dialogRef = useRef(null);
3287
+ const layerRef = useRef(null);
3170
3288
  // Claim a fresh z-index so this popup sits above any earlier popup.
3171
3289
  const [zIndex] = useState(() => open ? nextDialogZ() : 0);
3172
3290
  const title = props.title || "";
@@ -3202,9 +3320,23 @@ function isTopmostDialog(z) {
3202
3320
  if (ae && typeof ae.blur === "function" && ae !== document.body) ae.blur();
3203
3321
  };
3204
3322
  window.addEventListener("keydown", handler);
3323
+ const mountTime = Date.now();
3324
+ const clickHandler = (e) => {
3325
+ if (Date.now() - mountTime < 50) return;
3326
+ if (e.defaultPrevented) return;
3327
+ if (escStateRef.current.busy) return;
3328
+ if (!isTopmostDialog(effectiveZ)) return;
3329
+ if (isClickInsideDialog(e, dialogRef, layerRef)) return;
3330
+ if (e && typeof e.preventDefault === "function") e.preventDefault();
3331
+ if (e && typeof e.stopPropagation === "function") e.stopPropagation();
3332
+ if (e && typeof e.stopImmediatePropagation === "function") e.stopImmediatePropagation();
3333
+ escStateRef.current.onCancel();
3334
+ };
3335
+ window.addEventListener("click", clickHandler, true);
3205
3336
  return () => {
3206
3337
  unregisterDialogZ(effectiveZ);
3207
3338
  window.removeEventListener("keydown", handler);
3339
+ window.removeEventListener("click", clickHandler, true);
3208
3340
  };
3209
3341
  }, [open, zIndex]);
3210
3342
 
@@ -3232,13 +3364,17 @@ function isTopmostDialog(z) {
3232
3364
  : {})
3233
3365
  };
3234
3366
 
3367
+ const confirmDisabled = props.confirmDisabled === true;
3368
+
3235
3369
  return renderInOverlay(
3236
3370
  h("div", {
3371
+ ref: layerRef,
3237
3372
  className: "sm-nativeDialogLayer sm-confirmDialog" + (hasCoords ? " sm-confirmDialog-anchored" : ""),
3238
3373
  style: layerStyle,
3239
3374
  role: "presentation"
3240
3375
  },
3241
3376
  h("section", {
3377
+ ref: dialogRef,
3242
3378
  className: "sm-nativeDialog",
3243
3379
  style: { pointerEvents: "auto" },
3244
3380
  role: "dialog",
@@ -3262,8 +3398,8 @@ function isTopmostDialog(z) {
3262
3398
  h("button", {
3263
3399
  type: "button",
3264
3400
  className: "sm-nativeDialogButton" + (danger ? " sm-nativeDialogDanger" : " sm-nativeDialogConfirm"),
3265
- disabled: busy,
3266
- onClick: () => { if (!busy) onConfirm(); }
3401
+ disabled: busy || confirmDisabled,
3402
+ onClick: () => { if (!busy && !confirmDisabled) onConfirm(); }
3267
3403
  }, confirmLabel)
3268
3404
  )
3269
3405
  )
@@ -3276,6 +3412,8 @@ function isTopmostDialog(z) {
3276
3412
  const onCancel = typeof props.onCancel === "function" ? props.onCancel : () => {};
3277
3413
  const onConfirm = typeof props.onConfirm === "function" ? props.onConfirm : () => {};
3278
3414
  const busy = props.busy === true;
3415
+ const dialogRef = useRef(null);
3416
+ const layerRef = useRef(null);
3279
3417
  const title = props.title || t("confirm.move.title");
3280
3418
  const description = props.description || "";
3281
3419
  const currentWorkspaceId = props.currentWorkspaceId || "";
@@ -3324,9 +3462,23 @@ function isTopmostDialog(z) {
3324
3462
  if (ae && typeof ae.blur === "function" && ae !== document.body) ae.blur();
3325
3463
  };
3326
3464
  window.addEventListener("keydown", handler);
3465
+ const mountTime = Date.now();
3466
+ const clickHandler = (e) => {
3467
+ if (Date.now() - mountTime < 50) return;
3468
+ if (e.defaultPrevented) return;
3469
+ if (escStateRef.current.busy) return;
3470
+ if (!isTopmostDialog(effectiveZ)) return;
3471
+ if (isClickInsideDialog(e, dialogRef, layerRef)) return;
3472
+ if (e && typeof e.preventDefault === "function") e.preventDefault();
3473
+ if (e && typeof e.stopPropagation === "function") e.stopPropagation();
3474
+ if (e && typeof e.stopImmediatePropagation === "function") e.stopImmediatePropagation();
3475
+ escStateRef.current.onCancel();
3476
+ };
3477
+ window.addEventListener("click", clickHandler, true);
3327
3478
  return () => {
3328
3479
  unregisterDialogZ(effectiveZ);
3329
3480
  window.removeEventListener("keydown", handler);
3481
+ window.removeEventListener("click", clickHandler, true);
3330
3482
  };
3331
3483
  }, [open, zIndex]);
3332
3484
  if (!open) return null;
@@ -3384,12 +3536,14 @@ function isTopmostDialog(z) {
3384
3536
  };
3385
3537
  return renderInOverlay(
3386
3538
  h("div", {
3539
+ ref: layerRef,
3387
3540
  className: "sm-nativeDialogLayer sm-confirmDialog" + (hasCoords ? " sm-confirmDialog-anchored" : ""),
3388
3541
  style: layerStyle,
3389
3542
  role: "presentation",
3390
3543
  onKeyDown
3391
3544
  },
3392
3545
  h("section", {
3546
+ ref: dialogRef,
3393
3547
  className: "sm-nativeDialog",
3394
3548
  style: { pointerEvents: "auto" },
3395
3549
  role: "dialog",
@@ -3425,6 +3579,8 @@ function isTopmostDialog(z) {
3425
3579
  const onCancel = typeof props.onCancel === "function" ? props.onCancel : () => {};
3426
3580
  const onRun = typeof props.onRun === "function" ? props.onRun : () => {};
3427
3581
  const busy = props.busy === true;
3582
+ const dialogRef = useRef(null);
3583
+ const layerRef = useRef(null);
3428
3584
  const sessionId = props.sessionId || "";
3429
3585
  const description = props.description || "";
3430
3586
  const fallbackPreset = props.currentPreset || "";
@@ -3483,9 +3639,23 @@ function isTopmostDialog(z) {
3483
3639
  if (ae && typeof ae.blur === "function" && ae !== document.body) ae.blur();
3484
3640
  };
3485
3641
  window.addEventListener("keydown", handler);
3642
+ const mountTime = Date.now();
3643
+ const clickHandler = (e) => {
3644
+ if (Date.now() - mountTime < 50) return;
3645
+ if (e.defaultPrevented) return;
3646
+ if (escStateRef.current.busy) return;
3647
+ if (!isTopmostDialog(effectiveZ)) return;
3648
+ if (isClickInsideDialog(e, dialogRef, layerRef)) return;
3649
+ if (e && typeof e.preventDefault === "function") e.preventDefault();
3650
+ if (e && typeof e.stopPropagation === "function") e.stopPropagation();
3651
+ if (e && typeof e.stopImmediatePropagation === "function") e.stopImmediatePropagation();
3652
+ escStateRef.current.onCancel();
3653
+ };
3654
+ window.addEventListener("click", clickHandler, true);
3486
3655
  return () => {
3487
3656
  unregisterDialogZ(effectiveZ);
3488
3657
  window.removeEventListener("keydown", handler);
3658
+ window.removeEventListener("click", clickHandler, true);
3489
3659
  };
3490
3660
  }, [open, zIndex]);
3491
3661
  if (!open) return null;
@@ -3542,11 +3712,13 @@ function isTopmostDialog(z) {
3542
3712
  };
3543
3713
  return renderInOverlay(
3544
3714
  h("div", {
3715
+ ref: layerRef,
3545
3716
  className: "sm-nativeDialogLayer sm-confirmDialog" + (hasCoords ? " sm-confirmDialog-anchored" : ""),
3546
3717
  role: "presentation",
3547
3718
  style: layerStyle
3548
3719
  },
3549
3720
  h("section", {
3721
+ ref: dialogRef,
3550
3722
  className: "sm-nativeDialog",
3551
3723
  style: { pointerEvents: "auto" },
3552
3724
  role: "dialog",
@@ -3575,7 +3747,7 @@ function isTopmostDialog(z) {
3575
3747
  ));
3576
3748
  }
3577
3749
 
3578
- const CURRENT_VERSION = "0.7.0";
3750
+ const CURRENT_VERSION = "0.7.2";
3579
3751
 
3580
3752
  function compareSemver(v1, v2) {
3581
3753
  if (typeof v1 !== "string" || typeof v2 !== "string") return 0;
@@ -3636,7 +3808,11 @@ function isTopmostDialog(z) {
3636
3808
  application: null,
3637
3809
  dialogOpen: false,
3638
3810
  checkedAt: null,
3811
+ lastAttemptAt: null,
3639
3812
  },
3813
+ CHECK_INTERVAL_MS: 6 * 60 * 60 * 1000,
3814
+ RETRY_COOLDOWN_MS: 30 * 60 * 1000,
3815
+ lastFailedAt: null,
3640
3816
  listeners: new Set(),
3641
3817
  getSnapshot() { return this.state; },
3642
3818
  subscribe(cb) {
@@ -3671,6 +3847,29 @@ function isTopmostDialog(z) {
3671
3847
  } catch (_) {}
3672
3848
  this._notify();
3673
3849
  },
3850
+ shouldAutoCheck(now = Date.now()) {
3851
+ if (!this.getAutoCheck()) return false;
3852
+ if (this.state.status === "checking" || this.state.status === "updating") return false;
3853
+ if (this.state.status === "available") return false;
3854
+ if (this.lastFailedAt && (now - this.lastFailedAt < this.RETRY_COOLDOWN_MS)) {
3855
+ return false;
3856
+ }
3857
+ if (this.state.checkedAt && (now - this.state.checkedAt < this.CHECK_INTERVAL_MS)) {
3858
+ return false;
3859
+ }
3860
+ return true;
3861
+ },
3862
+ async maybeAutoCheck(options = {}) {
3863
+ const now = typeof options.now === "number" ? options.now : Date.now();
3864
+ if (!this.shouldAutoCheck(now)) return this.state;
3865
+ return this.checkForUpdate({
3866
+ silent: true,
3867
+ openOnFound: false,
3868
+ openDialog: false,
3869
+ ...options,
3870
+ manual: false
3871
+ });
3872
+ },
3674
3873
  // Dialog opacity preference (Issue #23). Reuses the same localStorage
3675
3874
  // backed preference store as autoCheck / registry instead of adding a
3676
3875
  // second storage mechanism.
@@ -3733,7 +3932,17 @@ function isTopmostDialog(z) {
3733
3932
  }
3734
3933
  this._notify();
3735
3934
 
3935
+ const timeoutMs = typeof options.timeoutMs === "number" ? options.timeoutMs : 10000;
3936
+ let timeoutTimer = null;
3937
+ const abortController = typeof AbortController !== "undefined" ? new AbortController() : null;
3938
+
3736
3939
  try {
3940
+ if (abortController) {
3941
+ timeoutTimer = setTimeout(() => {
3942
+ abortController.abort(new Error("检查更新超时"));
3943
+ }, timeoutMs);
3944
+ }
3945
+
3737
3946
  let res = null;
3738
3947
  if (typeof window !== "undefined" && window.__DSH_SM_TEST_UPDATE__) {
3739
3948
  const testOverride = window.__DSH_SM_TEST_UPDATE__;
@@ -3747,12 +3956,16 @@ function isTopmostDialog(z) {
3747
3956
  } else {
3748
3957
  const registryUrl = this.getRegistryUrl();
3749
3958
  const registryParam = encodeURIComponent(registryUrl);
3959
+ const fetchOpts = abortController ? { signal: abortController.signal } : undefined;
3750
3960
  try {
3751
- res = await fetchApi("/update/check?registry=" + registryParam);
3961
+ res = await fetchApi("/update/check?registry=" + registryParam, fetchOpts);
3752
3962
  } catch (fetchErr) {
3963
+ if (abortController?.signal?.aborted || fetchErr.name === "AbortError" || fetchErr.message?.includes("超时")) {
3964
+ throw new Error("检查更新超时");
3965
+ }
3753
3966
  try {
3754
3967
  const base = registryUrl.endsWith("/") ? registryUrl : registryUrl + "/";
3755
- const npmRes = await fetch(base + "dsh-session-manager/latest");
3968
+ const npmRes = await fetch(base + "dsh-session-manager/latest", fetchOpts);
3756
3969
  if (npmRes.ok) {
3757
3970
  const npmData = await npmRes.json();
3758
3971
  if (npmData && npmData.version) {
@@ -3762,7 +3975,11 @@ function isTopmostDialog(z) {
3762
3975
  res = { ok: true, result: { currentVersion: cur, latestVersion: lat, hasUpdate } };
3763
3976
  }
3764
3977
  }
3765
- } catch (_) {}
3978
+ } catch (_) {
3979
+ if (abortController?.signal?.aborted || fetchErr.name === "AbortError" || fetchErr.message?.includes("超时")) {
3980
+ throw new Error("检查更新超时");
3981
+ }
3982
+ }
3766
3983
  if (!res) throw fetchErr;
3767
3984
  }
3768
3985
  }
@@ -3778,7 +3995,10 @@ function isTopmostDialog(z) {
3778
3995
 
3779
3996
  this.state.currentVersion = currentVersion;
3780
3997
  this.state.latestVersion = latestVersion;
3781
- this.state.checkedAt = Date.now();
3998
+ const now = Date.now();
3999
+ this.state.checkedAt = now;
4000
+ this.state.lastAttemptAt = now;
4001
+ this.lastFailedAt = null;
3782
4002
 
3783
4003
  if (hasUpdate) {
3784
4004
  this.state.status = "available";
@@ -3794,13 +4014,18 @@ function isTopmostDialog(z) {
3794
4014
  this._notify();
3795
4015
  return this.state;
3796
4016
  } catch (err) {
4017
+ const now = Date.now();
3797
4018
  this.state.status = "error";
3798
- this.state.error = err.message || "检查更新失败";
4019
+ this.state.error = (abortController?.signal?.aborted ? "检查更新超时" : (err.message || "检查更新失败"));
4020
+ this.state.lastAttemptAt = now;
4021
+ this.lastFailedAt = now;
3799
4022
  if (shouldOpenDialog) {
3800
4023
  this.state.dialogOpen = true;
3801
4024
  }
3802
4025
  this._notify();
3803
4026
  return this.state;
4027
+ } finally {
4028
+ if (timeoutTimer) clearTimeout(timeoutTimer);
3804
4029
  }
3805
4030
  },
3806
4031
  async startUpdate(options = {}) {
@@ -3864,8 +4089,15 @@ function isTopmostDialog(z) {
3864
4089
  listeners: new Set(),
3865
4090
  getSnapshot() { return this.open; },
3866
4091
  subscribe(cb) { this.listeners.add(cb); return () => this.listeners.delete(cb); },
3867
- set(v) { if (this.open === v) return; this.open = v; for (const fn of [...this.listeners]) fn(); },
3868
- toggle() { this.open = !this.open; for (const fn of [...this.listeners]) fn(); },
4092
+ set(v) {
4093
+ if (this.open === v) return;
4094
+ this.open = v;
4095
+ if (this.open) {
4096
+ try { updateStore.maybeAutoCheck().catch(() => {}); } catch (_) {}
4097
+ }
4098
+ for (const fn of [...this.listeners]) fn();
4099
+ },
4100
+ toggle() { this.set(!this.open); },
3869
4101
  // Pending action queued by the title-bar buttons (e.g. 移动至工作区).
3870
4102
  // The SessionManagerPanel consumes this on mount so it can pre-open
3871
4103
  // the matching inner dialog (move / delete / migrate) next to itself.
@@ -3979,6 +4211,29 @@ function isTopmostDialog(z) {
3979
4211
  const [now, setNow] = useState(() => Date.now());
3980
4212
  const [moveFor, setMoveFor] = useState(null);
3981
4213
  const [confirmFor, setConfirmFor] = useState(null);
4214
+ const [confirmPreviewState, setConfirmPreviewState] = useState({ loading: false, error: null, subagentCount: 0 });
4215
+ useEffect(() => {
4216
+ if (!confirmFor || !confirmFor.id) {
4217
+ setConfirmPreviewState({ loading: false, error: null, subagentCount: 0 });
4218
+ return;
4219
+ }
4220
+ let alive = true;
4221
+ setConfirmPreviewState({ loading: true, error: null, subagentCount: 0 });
4222
+ fetchApi("/delete-preview?sessionId=" + encodeURIComponent(confirmFor.id))
4223
+ .then((res) => {
4224
+ if (!alive) return;
4225
+ if (res && res.ok && res.result && typeof res.result.subagentCount === "number") {
4226
+ setConfirmPreviewState({ loading: false, error: null, subagentCount: res.result.subagentCount });
4227
+ } else {
4228
+ setConfirmPreviewState({ loading: false, error: res?.error || "获取子智能体预览失败", subagentCount: 0 });
4229
+ }
4230
+ })
4231
+ .catch((err) => {
4232
+ if (!alive) return;
4233
+ setConfirmPreviewState({ loading: false, error: err instanceof Error ? err.message : String(err), subagentCount: 0 });
4234
+ });
4235
+ return () => { alive = false; };
4236
+ }, [confirmFor?.id]);
3982
4237
  const [workspaces, setWorkspaces] = useState([]);
3983
4238
  const [createdTimes, setCreatedTimes] = useState({});
3984
4239
  const [updatedTimes, setUpdatedTimes] = useState({});
@@ -4167,7 +4422,59 @@ function isTopmostDialog(z) {
4167
4422
  };
4168
4423
  const summaryAction = actionSummaryMap[action] || resolvedLabel;
4169
4424
  if (needsConfirm) {
4170
- setBulkPreview({ action, payload, actionLabel: summaryAction, sessions, warning: action === "delete" ? t("bulk.dialog.warning.delete") : "" });
4425
+ const baseWarning = action === "delete" ? t("bulk.dialog.warning.delete") : "";
4426
+ if (action === "delete") {
4427
+ setBulkPreview({
4428
+ action,
4429
+ payload,
4430
+ actionLabel: summaryAction,
4431
+ sessions,
4432
+ warning: baseWarning,
4433
+ loading: true,
4434
+ error: null
4435
+ });
4436
+ fetchApi("/delete-preview?sessionIds=" + encodeURIComponent(ids.join(",")))
4437
+ .then((res) => {
4438
+ if (res && res.ok && res.result && typeof res.result.subagentCount === "number") {
4439
+ const subCount = res.result.subagentCount;
4440
+ setBulkPreview((prev) => {
4441
+ if (!prev || prev.action !== "delete") return prev;
4442
+ const subText = subCount > 0 ? t("confirm.delete.subagents", { count: subCount }) : "";
4443
+ return {
4444
+ ...prev,
4445
+ loading: false,
4446
+ error: null,
4447
+ warning: subText ? `${subText}\n${baseWarning}` : baseWarning
4448
+ };
4449
+ });
4450
+ } else {
4451
+ const errMsg = (res && res.error) || t("confirm.delete.previewError") || "检查关联子智能体会话失败,请稍后重试";
4452
+ setBulkPreview((prev) => {
4453
+ if (!prev || prev.action !== "delete") return prev;
4454
+ return {
4455
+ ...prev,
4456
+ loading: false,
4457
+ error: errMsg,
4458
+ warning: `${errMsg}\n${baseWarning}`
4459
+ };
4460
+ });
4461
+ }
4462
+ })
4463
+ .catch((err) => {
4464
+ const errMsg = (err && err.message) || t("confirm.delete.previewError") || "检查关联子智能体会话失败,请稍后重试";
4465
+ setBulkPreview((prev) => {
4466
+ if (!prev || prev.action !== "delete") return prev;
4467
+ return {
4468
+ ...prev,
4469
+ loading: false,
4470
+ error: errMsg,
4471
+ warning: `${errMsg}\n${baseWarning}`
4472
+ };
4473
+ });
4474
+ });
4475
+ return;
4476
+ }
4477
+ setBulkPreview({ action, payload, actionLabel: summaryAction, sessions, warning: baseWarning });
4171
4478
  return;
4172
4479
  }
4173
4480
  void executeBulk({ action, payload, actionLabel: resolvedLabel, ids, sessions });
@@ -4460,28 +4767,56 @@ function isTopmostDialog(z) {
4460
4767
  : h("div", { className: "sm-list" }, rows.map((s, i) => renderRow(s, i)));
4461
4768
 
4462
4769
  const allWorkspaceRows = useMemo(() => {
4463
- const byId = list?.byId || {};
4770
+ const hasSessionList = Array.isArray(list?.ids);
4771
+ const sessionsByWorkspace = new Map();
4772
+ for (const ws of workspaces) {
4773
+ sessionsByWorkspace.set(ws.id, []);
4774
+ }
4775
+ if (hasSessionList) {
4776
+ for (const s of allRows) {
4777
+ const matchedWsIds = rowWorkspaces(s, workspaceIndex);
4778
+ for (const wsId of matchedWsIds) {
4779
+ const wsList = sessionsByWorkspace.get(wsId);
4780
+ if (wsList) wsList.push(s);
4781
+ }
4782
+ }
4783
+ }
4464
4784
  return workspaces.map(ws => {
4465
- const sids = Array.isArray(ws.sessionIds) ? ws.sessionIds : [];
4785
+ const wsSessions = sessionsByWorkspace.get(ws.id) || [];
4786
+ let sessionCount = 0;
4466
4787
  let latestSessionTime = null;
4467
- for (const sid of sids) {
4468
- const s = byId[sid];
4469
- const rawTime = s ? (s.updatedAt ?? s.lastModified ?? s.mtime ?? s.createdAt) : null;
4470
- const st = sessionTime(rawTime) ?? sessionTime(updatedTimes[sid]) ?? sessionTime(createdTimes[sid]);
4471
- if (st !== null) {
4472
- if (latestSessionTime === null || st > latestSessionTime) {
4473
- latestSessionTime = st;
4788
+ if (hasSessionList) {
4789
+ sessionCount = wsSessions.length;
4790
+ for (const s of wsSessions) {
4791
+ const st = sessionTime(s?.updatedAt) ?? sessionTime(s?.createdAt);
4792
+ if (st !== null) {
4793
+ if (latestSessionTime === null || st > latestSessionTime) {
4794
+ latestSessionTime = st;
4795
+ }
4796
+ }
4797
+ }
4798
+ } else {
4799
+ const sids = Array.isArray(ws.sessionIds) ? ws.sessionIds : [];
4800
+ const byId = list?.byId || {};
4801
+ for (const sid of sids) {
4802
+ const s = byId[sid];
4803
+ if (s && (s.origin === "subagent" || (s.blank && s.id !== list?.current))) continue;
4804
+ sessionCount++;
4805
+ const st = sessionTime(s?.updatedAt) ?? sessionTime(s?.createdAt);
4806
+ if (st !== null) {
4807
+ if (latestSessionTime === null || st > latestSessionTime) {
4808
+ latestSessionTime = st;
4809
+ }
4474
4810
  }
4475
4811
  }
4476
4812
  }
4477
- const wsSelfTime = sessionTime(ws.updatedAt) ?? sessionTime(ws.createdAt);
4478
- const resolvedTime = latestSessionTime !== null ? latestSessionTime : wsSelfTime;
4479
4813
  return {
4480
4814
  ...ws,
4481
- latestUpdatedAt: resolvedTime
4815
+ sessionCount,
4816
+ latestUpdatedAt: latestSessionTime
4482
4817
  };
4483
4818
  });
4484
- }, [workspaces, list?.byId, updatedTimes, createdTimes]);
4819
+ }, [workspaces, allRows, workspaceIndex, list?.ids, list?.byId, list?.current]);
4485
4820
 
4486
4821
  const filteredWorkspaceRows = useMemo(() => {
4487
4822
  const needle = wsQuery.trim().toLowerCase();
@@ -4632,7 +4967,7 @@ function isTopmostDialog(z) {
4632
4967
  const isBusy = workspaceArchiveStore.isBusy(ws.id);
4633
4968
  const title = ws.title || ws.name || ws.id;
4634
4969
  const path = ws.path || "";
4635
- const sessionCount = Array.isArray(ws.sessionIds) ? ws.sessionIds.length : 0;
4970
+ const sessionCount = typeof ws.sessionCount === "number" ? ws.sessionCount : (Array.isArray(ws.sessionIds) ? ws.sessionIds.length : 0);
4636
4971
  const timeFormatted = ws.latestUpdatedAt !== null
4637
4972
  ? t("workspace.lastUpdated", { time: formatRelative(ws.latestUpdatedAt, now) })
4638
4973
  : "—";
@@ -4689,13 +5024,12 @@ function isTopmostDialog(z) {
4689
5024
  },
4690
5025
  h("div", { className: "sm-rowCheckboxSlot" + ((wsSelectedIds.size > 0 || wsSelectModeEnabled) ? " sm-rowCheckboxSlot-visible" : "") }, checkbox),
4691
5026
  h("div", { className: "sm-rowMain" },
4692
- h("div", { className: "sm-rowTitle" },
5027
+ h("div", { className: "sm-rowTitle", title: path || title },
4693
5028
  h("span", null, title),
4694
5029
  isArchived ? h("span", { className: "sm-badge sm-badgeArchived" }, t("workspace.badge.archived")) : null
4695
5030
  ),
4696
5031
  h("div", { className: "sm-rowSecondary" },
4697
5032
  h("div", { className: "sm-rowMeta" },
4698
- (path ? (shortPath(path) + " · ") : "") +
4699
5033
  t("workspace.sessions", { count: sessionCount }) +
4700
5034
  " · " +
4701
5035
  timeFormatted
@@ -4726,8 +5060,23 @@ function isTopmostDialog(z) {
4726
5060
  // without re-subscribing on every render. The panel listener
4727
5061
  // returns early when any inner dialog is open so the child
4728
5062
  // dialog's own window listener gets the first shot at Esc.
5063
+ const panelRef = useRef(null);
4729
5064
  const escStateRef = useRef({});
4730
- escStateRef.current = { busyId, confirmFor, moveFor, migrateFor, annotationFor, wsBulkBusy, onPanelClose };
5065
+ escStateRef.current = {
5066
+ busyId,
5067
+ confirmFor,
5068
+ moveFor,
5069
+ migrateFor,
5070
+ annotationFor,
5071
+ bulkPreview,
5072
+ bulkProgress,
5073
+ bulkResult,
5074
+ bulkBusy,
5075
+ bulkTagDraft,
5076
+ bulkPresetDraft,
5077
+ wsBulkBusy,
5078
+ onPanelClose
5079
+ };
4731
5080
  useEffect(() => {
4732
5081
  if (!open) return;
4733
5082
  const handler = (e) => {
@@ -4735,8 +5084,10 @@ function isTopmostDialog(z) {
4735
5084
  if (e.defaultPrevented) return;
4736
5085
  if (e.isComposing || e.keyCode === 229) return;
4737
5086
  const s = escStateRef.current;
4738
- if (s.busyId !== null || s.wsBulkBusy) return;
4739
- if (s.confirmFor || s.moveFor || s.migrateFor || s.annotationFor || (activeDialogZStack && activeDialogZStack.length > 0)) return;
5087
+ if (s.busyId !== null || s.wsBulkBusy || s.bulkBusy) return;
5088
+ if (s.confirmFor || s.moveFor || s.migrateFor || s.annotationFor || s.bulkPreview || s.bulkProgress || s.bulkResult || s.bulkTagDraft || s.bulkPresetDraft) return;
5089
+ if (typeof updateStore !== "undefined" && updateStore && typeof updateStore.getSnapshot === "function" && updateStore.getSnapshot()?.dialogOpen) return;
5090
+ if (activeDialogZStack && activeDialogZStack.length > 0) return;
4740
5091
  e.preventDefault();
4741
5092
  e.stopPropagation();
4742
5093
  s.onPanelClose();
@@ -4750,7 +5101,28 @@ function isTopmostDialog(z) {
4750
5101
  if (ae && typeof ae.blur === "function" && ae !== document.body) ae.blur();
4751
5102
  };
4752
5103
  window.addEventListener("keydown", handler);
4753
- return () => window.removeEventListener("keydown", handler);
5104
+ const mountTime = Date.now();
5105
+ const clickHandler = (e) => {
5106
+ if (Date.now() - mountTime < 50) return;
5107
+ if (e.defaultPrevented) return;
5108
+ const s = escStateRef.current;
5109
+ if (s.busyId !== null || s.wsBulkBusy || s.bulkBusy) return;
5110
+ if (s.annotationFor !== null) return;
5111
+ if (s.confirmFor || s.moveFor || s.migrateFor) return;
5112
+ if (s.bulkPreview || s.bulkProgress || s.bulkResult || s.bulkTagDraft || s.bulkPresetDraft) return;
5113
+ if (typeof updateStore !== "undefined" && updateStore && typeof updateStore.getSnapshot === "function" && updateStore.getSnapshot()?.dialogOpen) return;
5114
+ if (activeDialogZStack && activeDialogZStack.length > 0) return;
5115
+ if (panelRef.current && typeof panelRef.current.contains === "function" && panelRef.current.contains(e.target)) return;
5116
+ if (e.target && typeof e.target.closest === "function") {
5117
+ if (e.target.closest(".sm-nativeDialog, .sm-panelDialog, .sm-bulkDialog, .sm-annotationDialog, .sm-annotationSurface, .sm-tooltip, .sm-footerBtn")) return;
5118
+ }
5119
+ s.onPanelClose();
5120
+ };
5121
+ window.addEventListener("click", clickHandler);
5122
+ return () => {
5123
+ window.removeEventListener("keydown", handler);
5124
+ window.removeEventListener("click", clickHandler);
5125
+ };
4754
5126
  }, [open]);
4755
5127
  // The panel is a document-level floating window, so it is portaled into
4756
5128
  // the plugin's overlay root like every other dialog here (issue #28).
@@ -4773,6 +5145,7 @@ function isTopmostDialog(z) {
4773
5145
  role: "presentation"
4774
5146
  },
4775
5147
  h("section", {
5148
+ ref: panelRef,
4776
5149
  className: "sm-nativeDialog sm-panelDialog",
4777
5150
  role: "dialog",
4778
5151
  "aria-modal": "true",
@@ -5076,13 +5449,27 @@ function isTopmostDialog(z) {
5076
5449
  onClose: () => { if (busyId === null) setConfirmFor(null); },
5077
5450
  title: t("confirm.delete.title"),
5078
5451
  description: (confirmFor.displayTitle && t("confirm.delete.desc", { title: confirmFor.displayTitle })) || "",
5079
- warning: confirmFor.running ? (t("confirm.delete.running") + " " + t("bulk.dialog.warning.delete")) : t("bulk.dialog.warning.delete"),
5452
+ warning: (() => {
5453
+ const baseWarning = (confirmFor.running ? (t("confirm.delete.running") + " " + t("bulk.dialog.warning.delete")) : t("bulk.dialog.warning.delete"));
5454
+ if (confirmPreviewState.loading) {
5455
+ const loadingMsg = t("confirm.delete.previewLoading") || "正在检查关联的子智能体会话…";
5456
+ return `${loadingMsg}\n${baseWarning}`;
5457
+ }
5458
+ if (confirmPreviewState.error) {
5459
+ const errorMsg = confirmPreviewState.error;
5460
+ return `${errorMsg}\n${baseWarning}`;
5461
+ }
5462
+ const subagentWarning = confirmPreviewState.subagentCount > 0 ? t("confirm.delete.subagents", { count: confirmPreviewState.subagentCount }) : "";
5463
+ return subagentWarning ? `${subagentWarning}\n${baseWarning}` : baseWarning;
5464
+ })(),
5465
+ confirmDisabled: confirmPreviewState.loading || Boolean(confirmPreviewState.error),
5080
5466
  confirmLabel: t("confirm.delete.confirm"),
5081
5467
  cancelLabel: t("confirm.cancel"),
5082
5468
  busy: busyId !== null,
5083
5469
  danger: true,
5084
5470
  onCancel: () => { if (busyId === null) setConfirmFor(null); },
5085
5471
  onConfirm: () => {
5472
+ if (confirmPreviewState.loading || confirmPreviewState.error) return;
5086
5473
  const target = confirmFor;
5087
5474
  setConfirmFor(null);
5088
5475
  runRow(target.id, () => onRemove(target.id));
@@ -5168,12 +5555,16 @@ function isTopmostDialog(z) {
5168
5555
  delete: t("bulk.confirm.delete")
5169
5556
  })[action] || t("bulk.dialog.execute");
5170
5557
  const bulkPreviewDialog = bulkPreview ? h(BatchPreviewDialog, {
5171
- open: true, t, actionLabel: bulkPreview.actionLabel,
5558
+ open: true, t, action: bulkPreview.action, actionLabel: bulkPreview.actionLabel,
5559
+ title: bulkPreview.action === "delete" ? t("confirm.delete.title") : undefined,
5172
5560
  sessions: bulkPreview.sessions, warning: bulkPreview.warning,
5173
5561
  confirmLabel: bulkConfirmLabelFor(bulkPreview.action),
5174
5562
  danger: bulkPreview.action === "delete",
5563
+ loading: bulkPreview.loading === true,
5564
+ error: bulkPreview.error || null,
5175
5565
  onCancel: () => setBulkPreview(null),
5176
5566
  onExecute: () => {
5567
+ if (bulkPreview.loading || bulkPreview.error) return;
5177
5568
  const preview = bulkPreview;
5178
5569
  setBulkPreview(null);
5179
5570
  void executeBulk({
@@ -5279,6 +5670,29 @@ function isTopmostDialog(z) {
5279
5670
 
5280
5671
  const [confirmFor, setConfirmFor] = useState(null);
5281
5672
  const [confirmAnchor, setConfirmAnchor] = useState(null);
5673
+ const [confirmPreviewState, setConfirmPreviewState] = useState({ loading: false, error: null, subagentCount: 0 });
5674
+ useEffect(() => {
5675
+ if (!confirmFor || !confirmFor.id) {
5676
+ setConfirmPreviewState({ loading: false, error: null, subagentCount: 0 });
5677
+ return;
5678
+ }
5679
+ let alive = true;
5680
+ setConfirmPreviewState({ loading: true, error: null, subagentCount: 0 });
5681
+ fetchApi("/delete-preview?sessionId=" + encodeURIComponent(confirmFor.id))
5682
+ .then((res) => {
5683
+ if (!alive) return;
5684
+ if (res && res.ok && res.result && typeof res.result.subagentCount === "number") {
5685
+ setConfirmPreviewState({ loading: false, error: null, subagentCount: res.result.subagentCount });
5686
+ } else {
5687
+ setConfirmPreviewState({ loading: false, error: res?.error || (t ? t("confirm.delete.previewError") : "检查关联子智能体会话失败,请稍后重试"), subagentCount: 0 });
5688
+ }
5689
+ })
5690
+ .catch((err) => {
5691
+ if (!alive) return;
5692
+ setConfirmPreviewState({ loading: false, error: (err && err.message) || (t ? t("confirm.delete.previewError") : "检查关联子智能体会话失败,请稍后重试"), subagentCount: 0 });
5693
+ });
5694
+ return () => { alive = false; };
5695
+ }, [confirmFor?.id]);
5282
5696
  const [moveFor, setMoveFor] = useState(null);
5283
5697
  const [moveAnchor, setMoveAnchor] = useState(null);
5284
5698
  const [workspaces, setWorkspaces] = useState([]);
@@ -5608,13 +6022,30 @@ function isTopmostDialog(z) {
5608
6022
  onClose: () => setConfirmFor(null),
5609
6023
  onCancel: () => setConfirmFor(null),
5610
6024
  onConfirm: () => {
6025
+ if (confirmPreviewState.loading || confirmPreviewState.error) return;
5611
6026
  const target = confirmFor;
5612
6027
  setConfirmFor(null);
5613
6028
  runWithAlert("delete", () => onRemove(target.id));
5614
6029
  },
5615
6030
  title: t ? t("confirm.delete.title") : "删除会话",
5616
6031
  description: (confirmFor && confirmFor.displayTitle && (t ? t("confirm.delete.desc", { title: confirmFor.displayTitle }) : ("「" + confirmFor.displayTitle + "」:删除,不可恢复"))) || "",
5617
- warning: (confirmFor && confirmFor.running && t ? t("confirm.delete.running") + " " : "") + (t ? t("bulk.dialog.warning.delete") : "删除操作不可撤销,请确认后再继续。"),
6032
+ warning: (() => {
6033
+ const baseWarn = (confirmFor && confirmFor.running && t ? t("confirm.delete.running") + " " : "") +
6034
+ (t ? t("bulk.dialog.warning.delete") : "删除操作不可撤销,请确认后再继续。");
6035
+ if (confirmPreviewState.loading) {
6036
+ const loadingMsg = (t ? t("confirm.delete.previewLoading") : "") || "正在检查关联的子智能体会话…";
6037
+ return `${loadingMsg}\n${baseWarn}`;
6038
+ }
6039
+ if (confirmPreviewState.error) {
6040
+ const errorMsg = confirmPreviewState.error;
6041
+ return `${errorMsg}\n${baseWarn}`;
6042
+ }
6043
+ const subagentsWarn = confirmPreviewState.subagentCount > 0
6044
+ ? (t ? t("confirm.delete.subagents", { count: confirmPreviewState.subagentCount }) : `将连带删除 ${confirmPreviewState.subagentCount} 个关联的子智能体会话。`)
6045
+ : "";
6046
+ return subagentsWarn ? `${subagentsWarn}\n${baseWarn}` : baseWarn;
6047
+ })(),
6048
+ confirmDisabled: confirmPreviewState.loading || Boolean(confirmPreviewState.error),
5618
6049
  confirmLabel: t ? t("confirm.delete.confirm") : "确认删除",
5619
6050
  cancelLabel: t ? t("confirm.cancel") : "取消",
5620
6051
  busy: false,
@@ -6011,18 +6442,6 @@ function isTopmostDialog(z) {
6011
6442
  ),
6012
6443
  h("div", { className: "sm-settingsSection" },
6013
6444
  h("div", { className: "sm-settingsSectionTitle" }, t("settings.prefSection")),
6014
- h("div", { className: "sm-settingsRow" },
6015
- h("span", null, t("settings.registry")),
6016
- h("select", {
6017
- className: "sm-settingsSelect sm-viewSelect",
6018
- value: currentRegistry,
6019
- onChange: onRegistryChange,
6020
- "aria-label": t("settings.registry")
6021
- },
6022
- h("option", { value: "registry.npmjs.org" }, t("settings.registry.npm")),
6023
- h("option", { value: "registry.npmmirror.com" }, t("settings.registry.china"))
6024
- )
6025
- ),
6026
6445
  h("div", { className: "sm-settingsRow" },
6027
6446
  h("span", null, t("update.autoCheck")),
6028
6447
  h("label", { className: "sm-settingsCheckbox" },
@@ -6036,7 +6455,22 @@ function isTopmostDialog(z) {
6036
6455
  )
6037
6456
  ),
6038
6457
  h("div", { className: "sm-settingsRow" },
6039
- h("span", null, t("settings.dialogOpacity")),
6458
+ h("span", null, t("settings.registry")),
6459
+ h("select", {
6460
+ className: "sm-settingsSelect",
6461
+ value: currentRegistry,
6462
+ onChange: onRegistryChange,
6463
+ "aria-label": t("settings.registry")
6464
+ },
6465
+ h("option", { value: "registry.npmjs.org" }, t("settings.registry.npm")),
6466
+ h("option", { value: "registry.npmmirror.com" }, t("settings.registry.china"))
6467
+ )
6468
+ ),
6469
+ h("div", { className: "sm-settingsRow" },
6470
+ h("div", { className: "sm-settingsRowLeft" },
6471
+ h("span", null, t("settings.dialogOpacity")),
6472
+ h("span", { className: "sm-settingsHint sm-settingsHintInline" }, t("settings.dialogOpacity.hint"))
6473
+ ),
6040
6474
  h("div", { className: "sm-settingsSlider" },
6041
6475
  h("input", {
6042
6476
  type: "range",
@@ -6060,39 +6494,42 @@ function isTopmostDialog(z) {
6060
6494
  onClick: () => updateStore.resetDialogOpacity()
6061
6495
  }, t("settings.dialogOpacity.reset"))
6062
6496
  )
6063
- ),
6064
- h("div", { className: "sm-settingsHint" }, t("settings.dialogOpacity.hint"))
6497
+ )
6065
6498
  ),
6066
6499
  h("div", { className: "sm-settingsSection" },
6067
6500
  h("div", { className: "sm-settingsSectionTitle" }, t("settings.projectSection")),
6068
- h("div", { className: "sm-settingsRow sm-settingsRowInline" },
6069
- h("span", null, t("settings.github")),
6070
- h("a", {
6071
- href: "https://github.com/hkkz9522/dsh-session-manager",
6072
- target: "_blank",
6073
- rel: "noopener noreferrer",
6074
- className: "sm-settingsLink"
6075
- }, "hkkz9522/dsh-session-manager"),
6076
- renderTip(t("settings.star.tip"), h("a", {
6077
- href: "https://github.com/hkkz9522/dsh-session-manager",
6078
- target: "_blank",
6079
- rel: "noopener noreferrer",
6080
- className: "sm-settingsBadgeLink",
6081
- "aria-label": t("settings.star.tip")
6082
- },
6083
- h(StarIcon, { size: 14 }),
6084
- h("span", null, t("settings.star"))
6085
- )),
6086
- renderTip(t("settings.feedback.tip"), h("a", {
6087
- href: "https://github.com/hkkz9522/dsh-session-manager/issues",
6088
- target: "_blank",
6089
- rel: "noopener noreferrer",
6090
- className: "sm-settingsBadgeLink",
6091
- "aria-label": t("settings.feedback.tip")
6092
- },
6093
- h(IssueIcon, { size: 14 }),
6094
- h("span", null, t("settings.feedback"))
6095
- ))
6501
+ h("div", { className: "sm-settingsRow" },
6502
+ h("div", { className: "sm-settingsProjectLeft" },
6503
+ h("span", { className: "sm-settingsProjectLabel" }, t("settings.github")),
6504
+ h("a", {
6505
+ href: "https://github.com/hkkz9522/dsh-session-manager",
6506
+ target: "_blank",
6507
+ rel: "noopener noreferrer",
6508
+ className: "sm-settingsLink"
6509
+ }, "hkkz9522/dsh-session-manager")
6510
+ ),
6511
+ h("div", { className: "sm-settingsBadgeActions" },
6512
+ renderTip(t("settings.star.tip"), h("a", {
6513
+ href: "https://github.com/hkkz9522/dsh-session-manager",
6514
+ target: "_blank",
6515
+ rel: "noopener noreferrer",
6516
+ className: "sm-settingsBadgeLink",
6517
+ "aria-label": t("settings.star.tip")
6518
+ },
6519
+ h(StarIcon, { size: 14 }),
6520
+ h("span", null, t("settings.star"))
6521
+ )),
6522
+ renderTip(t("settings.feedback.tip"), h("a", {
6523
+ href: "https://github.com/hkkz9522/dsh-session-manager/issues",
6524
+ target: "_blank",
6525
+ rel: "noopener noreferrer",
6526
+ className: "sm-settingsBadgeLink",
6527
+ "aria-label": t("settings.feedback.tip")
6528
+ },
6529
+ h(IssueIcon, { size: 14 }),
6530
+ h("span", null, t("settings.feedback"))
6531
+ ))
6532
+ )
6096
6533
  )
6097
6534
  )
6098
6535
  );
@@ -6186,7 +6623,18 @@ function isTopmostDialog(z) {
6186
6623
  onRemove: async (sessionId) => {
6187
6624
  const wasCurrent = isCurrentSession(sessionId);
6188
6625
  const response = await callApi("/delete", { sessionId });
6189
- if (!response.ok) throw new Error(response.error || "delete failed");
6626
+ if (!response.ok) {
6627
+ if (Array.isArray(response.deletedSubagents) && response.deletedSubagents.length > 0) {
6628
+ try { await annotationStore.load(true); } catch (_) {}
6629
+ try { await ctx.sessions?.refresh?.(); } catch (_) {}
6630
+ try { await ctx.workspaces?.refresh?.(); } catch (_) {}
6631
+ }
6632
+ const err = new Error(response.error || "delete failed");
6633
+ err.deletedSubagents = response.deletedSubagents || [];
6634
+ err.remainingSessions = response.remainingSessions || [sessionId];
6635
+ err.partialFailure = Boolean(response.partialFailure);
6636
+ throw err;
6637
+ }
6190
6638
  await annotationStore.load(true);
6191
6639
  if (response.result?.warning) window.alert(response.result.warning);
6192
6640
  if (wasCurrent || isCurrentSession(sessionId)) {
@@ -6320,7 +6768,7 @@ function isTopmostDialog(z) {
6320
6768
  ctx.effect(() => {
6321
6769
  if (!updateStore.getAutoCheck()) return;
6322
6770
  const timer = setTimeout(() => {
6323
- updateStore.checkForUpdate({ silent: true, openOnFound: false, openDialog: false }).catch(() => {});
6771
+ updateStore.maybeAutoCheck({ isStartup: true }).catch(() => {});
6324
6772
  }, 1500);
6325
6773
  return () => {
6326
6774
  try {
@@ -6381,8 +6829,18 @@ function isTopmostDialog(z) {
6381
6829
  exports.apply = apply;
6382
6830
  exports.inject = inject;
6383
6831
  exports.updateStore = updateStore;
6832
+ exports.panelStore = panelStore;
6384
6833
  exports.compareSemver = compareSemver;
6834
+ exports.OverlayLayer = OverlayLayer;
6385
6835
  exports.UpdateDialog = UpdateDialog;
6836
+ exports.ConfirmDialog = ConfirmDialog;
6837
+ exports.MoveDialog = MoveDialog;
6838
+ exports.MigratePresetDialog = MigratePresetDialog;
6839
+ exports.BatchPreviewDialog = BatchPreviewDialog;
6840
+ exports.BatchResultDialog = BatchResultDialog;
6841
+ exports.BulkTagInputDialog = BulkTagInputDialog;
6842
+ exports.BulkChoiceDialog = BulkChoiceDialog;
6843
+ exports.AnnotationDialog = AnnotationDialog;
6386
6844
  exports.SessionManagerSettingsCard = SessionManagerSettingsCard;
6387
6845
  exports.WhaleIcon = WhaleIcon;
6388
6846
  exports.NpmIcon = NpmIcon;
@@ -6396,6 +6854,7 @@ function isTopmostDialog(z) {
6396
6854
  exports.openCustomSelect = openCustomSelect;
6397
6855
  exports.closeCustomSelect = closeCustomSelect;
6398
6856
  exports.installCustomDropdownInterceptor = installCustomDropdownInterceptor;
6857
+ exports.getTargetSmSelect = getTargetSmSelect;
6399
6858
  exports.SM_SELECT_SELECTOR = SM_SELECT_SELECTOR;
6400
6859
  return module.exports;
6401
6860
  }