dsh-session-manager 0.7.1 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -181,8 +181,11 @@ function isTopmostDialog(z) {
181
181
  "panel.update.title": "检查更新",
182
182
  "panel.update.badge": "有新版本可用",
183
183
  "update.title": "检查更新",
184
+ "update.done.title": "升级成功",
184
185
  "update.newVersion": "发现新版本",
185
186
  "update.currentVersion": "当前版本",
187
+ "update.runningVersion": "当前运行版本",
188
+ "update.installedVersion": "已安装版本",
186
189
  "update.latestVersion": "最新版本",
187
190
  "update.isLatest": "当前已是最新版本",
188
191
  "update.checking": "正在检查更新…",
@@ -192,16 +195,26 @@ function isTopmostDialog(z) {
192
195
  "update.btn.updating": "更新中…",
193
196
  "update.btn.later": "稍后",
194
197
  "update.btn.done": "好的",
195
- "update.btn.retry": "重试",
196
198
  "update.btn.close": "关闭",
197
- "update.done.restart": "更新已安装(v{from} → v{to}),重启 DSH 后生效。",
198
- "update.done.applied": "更新已应用!",
199
+ "update.btn.retry": "重试",
200
+ "update.done.restart": "升级成功!已安装 v{to}(当前运行 v{from})。请重启 DSH 使新版本生效。",
201
+ "update.done.refresh": "升级成功!已安装 v{to}。请刷新界面使新版本生效。",
202
+ "update.done.applied": "升级成功!新版本已立即生效。",
203
+ "update.rolledBack.title": "已安全回滚",
204
+ "update.rolledBack": "升级未成功,已安全回滚至旧版本 v{version}。",
205
+ "update.rolledBack.reason": "原因:{reason}",
206
+ "update.source.linked": "开发模式(本地链接),请手动修改或拉取代码。",
207
+ "update.source.github": "通过 Git/GitHub 安装,请通过 DSH 插件市场或 git 更新。",
208
+ "update.source.installation": "系统内置插件,无法独立更新。",
209
+ "update.source.unknown": "无法确认插件安装来源,为保证安全已禁止自动更新。",
210
+ "update.hmrUnsupported": "当前处于 HMR 开发环境,无法在运行中直接自我更新。请使用 DSH 插件市场或命令行执行更新。",
199
211
  "update.error": "更新遇到问题",
200
212
  "update.autoCheck": "自动检查更新",
201
213
  "update.autoCheck.enable": "开启",
202
214
  "settings.registry": "安装源",
203
215
  "settings.registry.npm": "npm 官方源 (npmjs.org)",
204
216
  "settings.registry.china": "国内镜像源 (npmmirror.com)",
217
+ "settings.registry.local": "本地测试源 (127.0.0.1:4873)",
205
218
  "settings.title": "会话管理",
206
219
  "settings.pluginName": "dsh-session-manager",
207
220
  "settings.versionSection": "插件版本",
@@ -452,8 +465,11 @@ function isTopmostDialog(z) {
452
465
  "panel.update.title": "Check for updates",
453
466
  "panel.update.badge": "New version available",
454
467
  "update.title": "Check for Updates",
468
+ "update.done.title": "Upgrade Succeeded",
455
469
  "update.newVersion": "New Version Available",
456
470
  "update.currentVersion": "Current Version",
471
+ "update.runningVersion": "Running Version",
472
+ "update.installedVersion": "Installed Version",
457
473
  "update.latestVersion": "Latest Version",
458
474
  "update.isLatest": "Up to date",
459
475
  "update.checking": "Checking for updates…",
@@ -463,16 +479,26 @@ function isTopmostDialog(z) {
463
479
  "update.btn.updating": "Updating…",
464
480
  "update.btn.later": "Later",
465
481
  "update.btn.done": "OK",
466
- "update.btn.retry": "Retry",
467
482
  "update.btn.close": "Close",
468
- "update.done.restart": "Update installed (v{from} → v{to}). Please restart DSH.",
469
- "update.done.applied": "Update applied successfully!",
483
+ "update.btn.retry": "Retry",
484
+ "update.done.restart": "Upgrade succeeded! Installed v{to} (running v{from}). Please restart DSH to apply.",
485
+ "update.done.refresh": "Upgrade succeeded! Installed v{to}. Please refresh the UI to apply.",
486
+ "update.done.applied": "Upgrade succeeded! Update applied and active now.",
487
+ "update.rolledBack.title": "Safely Rolled Back",
488
+ "update.rolledBack": "Upgrade was not completed. Safely rolled back to previous version v{version}.",
489
+ "update.rolledBack.reason": "Reason: {reason}",
490
+ "update.source.linked": "Development mode (linked package). Please update local code directly.",
491
+ "update.source.github": "Installed via Git/GitHub. Please update via DSH Market or git.",
492
+ "update.source.installation": "Built-in package. Cannot be updated independently.",
493
+ "update.source.unknown": "Unknown installation source. Self-update disabled for safety.",
494
+ "update.hmrUnsupported": "Running under HMR development mode; self-update cannot proceed while active. Please update via DSH Market or CLI.",
470
495
  "update.error": "Update Error",
471
496
  "update.autoCheck": "Automatically check for updates",
472
497
  "update.autoCheck.enable": "Enable",
473
498
  "settings.registry": "Install Source",
474
499
  "settings.registry.npm": "Official npm (npmjs.org)",
475
500
  "settings.registry.china": "China mirror (npmmirror.com)",
501
+ "settings.registry.local": "Local test registry (127.0.0.1:4873)",
476
502
  "settings.title": "Session Manager",
477
503
  "settings.pluginName": "dsh-session-manager",
478
504
  "settings.versionSection": "Plugin Version",
@@ -732,7 +758,7 @@ function isTopmostDialog(z) {
732
758
  // applied). Tooltip visibility is governed by the layering contract
733
759
  // above, not by this rule; it is kept as the scoped replacement for the
734
760
  // unscoped selector Issue #21 removed.
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}",
761
+ ".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);height:248px!important;min-height:248px!important;max-height:248px!important;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:none!important;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;height:46px;min-height:46px;max-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;height:152px!important;min-height:152px!important;max-height:152px!important;box-sizing:border-box;font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary,#111);overflow:hidden}.sm-updateDialog .sm-nativeDialogFooter{display:flex;padding:10px 16px;border-top:1px solid var(--dsw-alias-border-l2,#eee);flex:none;height:50px;min-height:50px;max-height:50px;box-sizing:border-box;flex-wrap:nowrap;justify-content:space-between;align-items:center;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;white-space:nowrap}.sm-nativeDialogButton:disabled{opacity:.5!important;cursor:not-allowed!important}.sm-updateDialog .sm-nativeDialogButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.sm-updateDialog .sm-nativeDialogConfirm,.sm-updateDialog .sm-nativeDialogButton.sm-nativeDialogConfirm,.sm-settingsCard .sm-nativeDialogConfirm,.sm-settingsCard .sm-nativeDialogButton.sm-nativeDialogConfirm{background:var(--dsw-alias-state-business-primary,#356ae6)!important;color:var(--dsw-alias-label-primary-foreground,#fff)!important;border-color:var(--dsw-alias-state-business-primary,#356ae6)!important}.sm-updateDialog .sm-nativeDialogCancel,.sm-updateDialog .sm-nativeDialogButton.sm-nativeDialogCancel{background:transparent!important;color:var(--dsw-alias-label-primary,#111)!important;border-color:var(--dsw-alias-border-l2,#ddd)!important}.sm-updateDialog .sm-nativeDialogConfirm:hover:not(:disabled),.sm-updateDialog .sm-nativeDialogButton.sm-nativeDialogConfirm:hover:not(:disabled),.sm-settingsCard .sm-nativeDialogConfirm:hover:not(:disabled),.sm-settingsCard .sm-nativeDialogButton.sm-nativeDialogConfirm:hover:not(:disabled){background:var(--dsw-alias-state-business-primary,#356ae6)!important;color:var(--dsw-alias-label-primary-foreground,#fff)!important;border-color:var(--dsw-alias-state-business-primary,#356ae6)!important;filter:brightness(.94)!important}.sm-updateDialog .sm-nativeDialogCancel:hover:not(:disabled),.sm-updateDialog .sm-nativeDialogButton.sm-nativeDialogCancel:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)!important;color:var(--dsw-alias-label-primary,#111)!important}.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;box-sizing:border-box;flex:none}.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;box-sizing:border-box;min-height:38px;height:38px;padding:8px 10px;border-radius:6px;border:1px solid transparent;font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary,#111);display:flex;align-items:center;flex:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sm-updateDialog .sm-updateStatusBox{margin-top:0}.sm-updateStatusBox.sm-checking{color:var(--dsw-alias-label-secondary,#666);background:var(--dsw-alias-bg-layer-2,#f5f5f5);border:1px solid var(--dsw-alias-border-l2,#eee)}.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-state-business-primary,#356ae6);color:var(--dsw-alias-label-primary-foreground,#fff);border-color:var(--dsw-alias-state-business-primary,#356ae6);filter:brightness(.94)}.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}",
736
762
 
737
763
  // Issue #23 — dialog opacity.
738
764
  //
@@ -862,7 +888,8 @@ function isTopmostDialog(z) {
862
888
  ".sm-bulkResultItemReason{color:var(--dsw-alias-state-error-primary);font-size:11px;line-height:16px;word-break:break-word}",
863
889
  ".sm-bulkResultItemReasonSkip{color:var(--dsw-alias-label-tertiary,#888)}",
864
890
  ".sm-bulkResultFooter{display:flex;justify-content:space-between;align-items:center;gap:8px;flex-wrap:wrap}",
865
- ".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}",
891
+ "[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}",
892
+ ".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}",
866
893
  ".sm-footerBtn-wide:hover,.sm-footerBtn-wide:focus-visible{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}",
867
894
  ".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}",
868
895
  ".sm-footerBtn-rail:hover,.sm-footerBtn-rail:focus-visible{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}",
@@ -2100,6 +2127,18 @@ function isTopmostDialog(z) {
2100
2127
  }
2101
2128
  }
2102
2129
 
2130
+ function getTargetSmSelect(target) {
2131
+ if (!target || typeof target.closest !== "function") return null;
2132
+ const direct = target.closest(SM_SELECT_SELECTOR);
2133
+ if (direct && direct.tagName === "SELECT") return direct;
2134
+ const container = target.closest(".sm-wsSortField, .sm-annotationPriority, .sm-viewField, .sm-moveSection, .sm-settingsRow, .sm-customSelectWrapper");
2135
+ if (container) {
2136
+ const sel = container.querySelector("select");
2137
+ if (sel && sel.tagName === "SELECT") return sel;
2138
+ }
2139
+ return null;
2140
+ }
2141
+
2103
2142
  function installCustomDropdownInterceptor() {
2104
2143
  if (typeof document === "undefined" || typeof document.addEventListener !== "function") {
2105
2144
  return () => {};
@@ -2113,24 +2152,8 @@ function isTopmostDialog(z) {
2113
2152
  if (activeCustomMenu && activeCustomMenu.contains(e.target)) return;
2114
2153
  if (e.target?.closest?.("button, input, textarea, a")) return;
2115
2154
 
2116
- let select = null;
2117
- if (e.target?.matches?.("select")) {
2118
- select = e.target;
2119
- } else if (e.target?.closest?.("select")) {
2120
- select = e.target.closest("select");
2121
- } else {
2122
- const container = e.target?.closest?.(".sm-wsSortField, .sm-annotationPriority, .sm-viewField, .sm-moveSection, .sm-settingsRow, .sm-customSelectWrapper");
2123
- if (container) {
2124
- select = container.querySelector("select");
2125
- } else {
2126
- const nearest = e.target?.closest?.(SM_SELECT_SELECTOR);
2127
- if (nearest && nearest.tagName === "SELECT") {
2128
- select = nearest;
2129
- }
2130
- }
2131
- }
2132
-
2133
- if (select && select.tagName === "SELECT") {
2155
+ const select = getTargetSmSelect(e.target);
2156
+ if (select) {
2134
2157
  e.preventDefault();
2135
2158
  e.stopPropagation();
2136
2159
  if (select.disabled) return;
@@ -2146,14 +2169,8 @@ function isTopmostDialog(z) {
2146
2169
 
2147
2170
  const onClick = (e) => {
2148
2171
  if (e.target?.closest?.("button, input, textarea, a")) return;
2149
- let select = null;
2150
- if (e.target?.matches?.("select")) {
2151
- select = e.target;
2152
- } else {
2153
- const container = e.target?.closest?.(".sm-wsSortField, .sm-annotationPriority, .sm-viewField, .sm-moveSection, .sm-settingsRow, .sm-customSelectWrapper");
2154
- if (container) select = container.querySelector("select");
2155
- }
2156
- if (select && select.tagName === "SELECT") {
2172
+ const select = getTargetSmSelect(e.target);
2173
+ if (select) {
2157
2174
  e.preventDefault();
2158
2175
  e.stopPropagation();
2159
2176
  }
@@ -2170,7 +2187,7 @@ function isTopmostDialog(z) {
2170
2187
  }
2171
2188
 
2172
2189
  const focused = document.activeElement;
2173
- const targetSelect = activeCustomSelect || (focused?.matches?.(SM_SELECT_SELECTOR) ? focused : null) || (focused?.closest?.(".sm-wsSortField, .sm-annotationPriority, .sm-viewField, .sm-moveSection, .sm-settingsRow")?.querySelector("select"));
2190
+ const targetSelect = activeCustomSelect || getTargetSmSelect(focused);
2174
2191
  if (!targetSelect || targetSelect.tagName !== "SELECT") return;
2175
2192
 
2176
2193
  if (e.key === "Enter" || e.key === " ") {
@@ -2196,7 +2213,14 @@ function isTopmostDialog(z) {
2196
2213
  }
2197
2214
  };
2198
2215
 
2199
- const onResizeOrScroll = () => {
2216
+ const onResizeOrScroll = (e) => {
2217
+ // Ignore scrolls that originate inside the open menu: the listener is bound on
2218
+ // window with capture=true, so scrolling the menu's own option list (wheel or
2219
+ // scrollbar drag) would otherwise close it immediately.
2220
+ // Reference: PR #37
2221
+ if (activeCustomMenu && e && e.type === "scroll" && e.target && typeof activeCustomMenu.contains === "function" && activeCustomMenu.contains(e.target)) {
2222
+ return;
2223
+ }
2200
2224
  if (activeCustomMenu) closeCustomSelect();
2201
2225
  };
2202
2226
 
@@ -3749,7 +3773,7 @@ function isTopmostDialog(z) {
3749
3773
  ));
3750
3774
  }
3751
3775
 
3752
- const CURRENT_VERSION = "0.7.1";
3776
+ const CURRENT_VERSION = "0.7.3";
3753
3777
 
3754
3778
  function compareSemver(v1, v2) {
3755
3779
  if (typeof v1 !== "string" || typeof v2 !== "string") return 0;
@@ -3805,17 +3829,25 @@ function isTopmostDialog(z) {
3805
3829
  state: {
3806
3830
  status: "idle",
3807
3831
  currentVersion: CURRENT_VERSION,
3832
+ runningVersion: CURRENT_VERSION,
3833
+ installedVersion: null,
3808
3834
  latestVersion: null,
3809
3835
  error: null,
3810
3836
  application: null,
3811
3837
  dialogOpen: false,
3812
3838
  checkedAt: null,
3813
3839
  lastAttemptAt: null,
3840
+ source: null,
3841
+ notes: null,
3842
+ channelSwitch: null,
3843
+ rollbackInfo: null,
3844
+ taskId: null,
3814
3845
  },
3815
3846
  CHECK_INTERVAL_MS: 6 * 60 * 60 * 1000,
3816
3847
  RETRY_COOLDOWN_MS: 30 * 60 * 1000,
3817
3848
  lastFailedAt: null,
3818
3849
  listeners: new Set(),
3850
+ _statusPromise: null,
3819
3851
  getSnapshot() { return this.state; },
3820
3852
  subscribe(cb) {
3821
3853
  this.listeners.add(cb);
@@ -3830,9 +3862,92 @@ function isTopmostDialog(z) {
3830
3862
  this._notify();
3831
3863
  },
3832
3864
  closeDialog() {
3865
+ try {
3866
+ if (typeof localStorage !== "undefined") {
3867
+ const currentTaskId = this.state.taskId || this.state.rollbackInfo?.taskId;
3868
+ if (currentTaskId && this.state.status !== "updating") {
3869
+ localStorage.setItem("dsh-sm-ack-task", currentTaskId);
3870
+ }
3871
+ }
3872
+ } catch (_) {}
3833
3873
  this.state.dialogOpen = false;
3874
+ if (this.state.status !== "updating") {
3875
+ this.state.status = "idle";
3876
+ }
3834
3877
  this._notify();
3835
3878
  },
3879
+ async checkRecentStatus() {
3880
+ if (this._statusPromise) return this._statusPromise;
3881
+ if (this.state.status === "updating") return;
3882
+ this._statusPromise = (async () => {
3883
+ try {
3884
+ const statusData = await fetchApi("/update/status");
3885
+ if (statusData && statusData.ok && statusData.result) {
3886
+ const r = statusData.result;
3887
+ const ackTaskId = (typeof localStorage !== "undefined") ? localStorage.getItem("dsh-sm-ack-task") : null;
3888
+ if (r.taskId && r.taskId === ackTaskId) return;
3889
+ if (this.state.taskId === r.taskId && (this.state.status === "done" || this.state.status === "error")) return;
3890
+
3891
+ const runningV = r.runningVersion || r.currentVersion || this.state.runningVersion || this.state.currentVersion || CURRENT_VERSION;
3892
+ const installedV = r.installedVersion || (r.status === "completed" ? (r.targetVersion || runningV) : runningV);
3893
+ this.state.runningVersion = runningV;
3894
+ this.state.currentVersion = runningV;
3895
+ if (compareSemver(installedV, runningV) > 0) {
3896
+ this.state.installedVersion = installedV;
3897
+ }
3898
+
3899
+ if (r.status === "rolled-back" || (r.status === "failed" && r.rolledBack)) {
3900
+ this.state.status = "error";
3901
+ this.state.taskId = r.taskId;
3902
+ this.state.rollbackInfo = {
3903
+ rolledBack: true,
3904
+ currentVersion: r.currentVersion || runningV,
3905
+ reason: r.error || "未知原因",
3906
+ taskId: r.taskId
3907
+ };
3908
+ this.state.error = (r.error || "升级未成功") + "(已安全回滚至旧版本 v" + (r.currentVersion || runningV) + ")";
3909
+ this.state.dialogOpen = true;
3910
+ this._notify();
3911
+ } else if (r.status === "completed") {
3912
+ const targetV = r.targetVersion || this.state.latestVersion || installedV;
3913
+ this.state.taskId = r.taskId;
3914
+ this.state.latestVersion = targetV;
3915
+ this.state.installedVersion = targetV;
3916
+
3917
+ // If DSH was restarted and already running targetV or higher, do not popup again!
3918
+ if (compareSemver(runningV, targetV) >= 0) {
3919
+ try {
3920
+ if (typeof localStorage !== "undefined" && r.taskId) {
3921
+ localStorage.setItem("dsh-sm-ack-task", r.taskId);
3922
+ }
3923
+ } catch (_) {}
3924
+ return;
3925
+ }
3926
+
3927
+ if (r.application === "applied") {
3928
+ this.state.runningVersion = targetV;
3929
+ this.state.currentVersion = targetV;
3930
+ }
3931
+
3932
+ this.state.status = "done";
3933
+ this.state.application = r.application || "restart-required";
3934
+ this.state.dialogOpen = true;
3935
+ this._notify();
3936
+ } else if (r.status === "recovery-required") {
3937
+ this.state.status = "error";
3938
+ this.state.taskId = r.taskId;
3939
+ this.state.error = "更新失败且自动恢复旧版本失败: " + (r.rollbackError || r.error) + "。请手动重新安装插件。";
3940
+ this.state.dialogOpen = true;
3941
+ this._notify();
3942
+ }
3943
+ }
3944
+ } catch (_) {}
3945
+ finally {
3946
+ this._statusPromise = null;
3947
+ }
3948
+ })();
3949
+ return this._statusPromise;
3950
+ },
3836
3951
  getAutoCheck() {
3837
3952
  try {
3838
3953
  if (typeof localStorage !== "undefined") {
@@ -3853,6 +3968,9 @@ function isTopmostDialog(z) {
3853
3968
  if (!this.getAutoCheck()) return false;
3854
3969
  if (this.state.status === "checking" || this.state.status === "updating") return false;
3855
3970
  if (this.state.status === "available") return false;
3971
+ if (this.state.installedVersion && compareSemver(this.state.installedVersion, this.state.currentVersion) > 0) {
3972
+ return false;
3973
+ }
3856
3974
  if (this.lastFailedAt && (now - this.lastFailedAt < this.RETRY_COOLDOWN_MS)) {
3857
3975
  return false;
3858
3976
  }
@@ -3896,13 +4014,14 @@ function isTopmostDialog(z) {
3896
4014
  REGISTRY_OPTIONS: {
3897
4015
  "registry.npmjs.org": "https://registry.npmjs.org",
3898
4016
  "registry.npmmirror.com": "https://registry.npmmirror.com",
4017
+ "127.0.0.1:4873": "http://127.0.0.1:4873",
3899
4018
  },
3900
4019
  DEFAULT_REGISTRY: "registry.npmjs.org",
3901
4020
  getRegistry() {
3902
4021
  try {
3903
4022
  if (typeof localStorage !== "undefined") {
3904
4023
  const v = localStorage.getItem("dsh-session-manager-registry");
3905
- if (v && this.REGISTRY_OPTIONS[v]) return v;
4024
+ if (v && (this.REGISTRY_OPTIONS[v] || /^https?:\/\//i.test(v))) return v;
3906
4025
  }
3907
4026
  } catch (_) {}
3908
4027
  return this.DEFAULT_REGISTRY;
@@ -3910,7 +4029,7 @@ function isTopmostDialog(z) {
3910
4029
  setRegistry(key) {
3911
4030
  try {
3912
4031
  if (typeof localStorage !== "undefined") {
3913
- if (this.REGISTRY_OPTIONS[key]) {
4032
+ if (this.REGISTRY_OPTIONS[key] || (key && /^https?:\/\//i.test(key))) {
3914
4033
  localStorage.setItem("dsh-session-manager-registry", key);
3915
4034
  } else {
3916
4035
  localStorage.setItem("dsh-session-manager-registry", this.DEFAULT_REGISTRY);
@@ -3920,7 +4039,9 @@ function isTopmostDialog(z) {
3920
4039
  this._notify();
3921
4040
  },
3922
4041
  getRegistryUrl() {
3923
- return this.REGISTRY_OPTIONS[this.getRegistry()] || this.REGISTRY_OPTIONS[this.DEFAULT_REGISTRY];
4042
+ const reg = this.getRegistry();
4043
+ if (/^https?:\/\//i.test(reg)) return reg;
4044
+ return this.REGISTRY_OPTIONS[reg] || this.REGISTRY_OPTIONS[this.DEFAULT_REGISTRY];
3924
4045
  },
3925
4046
  async checkForUpdate(options = {}) {
3926
4047
  const { silent = false, openOnFound = true, manual = false } = options;
@@ -3929,6 +4050,7 @@ function isTopmostDialog(z) {
3929
4050
 
3930
4051
  this.state.status = "checking";
3931
4052
  this.state.error = null;
4053
+ this.state.notes = null;
3932
4054
  if (shouldOpenDialog) {
3933
4055
  this.state.dialogOpen = true;
3934
4056
  }
@@ -3953,12 +4075,15 @@ function isTopmostDialog(z) {
3953
4075
  }
3954
4076
  const cur = testOverride.currentVersion || this.state.currentVersion;
3955
4077
  const lat = testOverride.latestVersion || "9.9.9";
3956
- const hasUpdate = compareSemver(lat, cur) > 0;
3957
- res = { ok: true, result: { currentVersion: cur, latestVersion: lat, hasUpdate } };
4078
+ const src = testOverride.source || "npm";
4079
+ const hasUpdate = typeof testOverride.hasUpdate === "boolean"
4080
+ ? testOverride.hasUpdate
4081
+ : (src === "npm" && compareSemver(lat, cur) > 0);
4082
+ res = { ok: true, result: { currentVersion: cur, latestVersion: lat, hasUpdate, source: src } };
3958
4083
  } else {
4084
+ const fetchOpts = abortController ? { signal: abortController.signal } : undefined;
3959
4085
  const registryUrl = this.getRegistryUrl();
3960
4086
  const registryParam = encodeURIComponent(registryUrl);
3961
- const fetchOpts = abortController ? { signal: abortController.signal } : undefined;
3962
4087
  try {
3963
4088
  res = await fetchApi("/update/check?registry=" + registryParam, fetchOpts);
3964
4089
  } catch (fetchErr) {
@@ -3974,7 +4099,7 @@ function isTopmostDialog(z) {
3974
4099
  const cur = this.state.currentVersion;
3975
4100
  const lat = npmData.version;
3976
4101
  const hasUpdate = compareSemver(lat, cur) > 0;
3977
- res = { ok: true, result: { currentVersion: cur, latestVersion: lat, hasUpdate } };
4102
+ res = { ok: true, result: { currentVersion: cur, latestVersion: lat, hasUpdate, source: "npm" } };
3978
4103
  }
3979
4104
  }
3980
4105
  } catch (_) {
@@ -3991,24 +4116,51 @@ function isTopmostDialog(z) {
3991
4116
  }
3992
4117
 
3993
4118
  const result = res.result || {};
3994
- const currentVersion = result.currentVersion || this.state.currentVersion;
3995
- const latestVersion = result.latestVersion || currentVersion;
3996
- const hasUpdate = !!result.hasUpdate || compareSemver(latestVersion, currentVersion) > 0;
3997
-
3998
- this.state.currentVersion = currentVersion;
4119
+ const runningVersion = result.runningVersion || result.currentVersion || this.state.runningVersion || CURRENT_VERSION;
4120
+ const installedVersion = result.installedVersion || (this.state.installedVersion && compareSemver(this.state.installedVersion, runningVersion) > 0 ? this.state.installedVersion : runningVersion);
4121
+ const latestVersion = result.latestVersion || installedVersion;
4122
+ const source = result.source || "npm";
4123
+ const hasUpdate = typeof result.hasUpdate === "boolean" ? result.hasUpdate : compareSemver(latestVersion, installedVersion) > 0;
4124
+
4125
+ this.state.runningVersion = runningVersion;
4126
+ this.state.currentVersion = runningVersion;
4127
+ this.state.installedVersion = installedVersion;
3999
4128
  this.state.latestVersion = latestVersion;
4129
+ this.state.source = source;
4130
+ this.state.channelSwitch = result.channelSwitch || null;
4000
4131
  const now = Date.now();
4001
4132
  this.state.checkedAt = now;
4002
4133
  this.state.lastAttemptAt = now;
4003
4134
  this.lastFailedAt = null;
4004
4135
 
4005
- if (hasUpdate) {
4136
+ if (source === "linked") {
4137
+ this.state.notes = "update.source.linked";
4138
+ this.state.status = "idle";
4139
+ } else if (source === "github" && !hasUpdate) {
4140
+ this.state.notes = "update.source.github";
4141
+ this.state.status = "idle";
4142
+ } else if (source === "installation") {
4143
+ this.state.notes = "update.source.installation";
4144
+ this.state.status = "idle";
4145
+ } else if (source === "unknown") {
4146
+ this.state.notes = "update.source.unknown";
4147
+ this.state.status = "idle";
4148
+ } else if (compareSemver(installedVersion, runningVersion) > 0) {
4149
+ this.state.status = "done";
4150
+ this.state.application = "restart-required";
4151
+ this.state.notes = null;
4152
+ if (shouldOpenDialog) {
4153
+ this.state.dialogOpen = true;
4154
+ }
4155
+ } else if (hasUpdate) {
4006
4156
  this.state.status = "available";
4157
+ this.state.notes = null;
4007
4158
  if (openOnFound && !silent) {
4008
4159
  this.state.dialogOpen = true;
4009
4160
  }
4010
4161
  } else {
4011
4162
  this.state.status = "idle";
4163
+ this.state.notes = null;
4012
4164
  if (shouldOpenDialog) {
4013
4165
  this.state.dialogOpen = true;
4014
4166
  }
@@ -4031,7 +4183,7 @@ function isTopmostDialog(z) {
4031
4183
  }
4032
4184
  },
4033
4185
  async startUpdate(options = {}) {
4034
- const { openDialog = false } = options;
4186
+ const { openDialog = true } = options;
4035
4187
  if (this.state.status === "updating") return this.state;
4036
4188
  this.state.status = "updating";
4037
4189
  this.state.error = null;
@@ -4042,6 +4194,10 @@ function isTopmostDialog(z) {
4042
4194
 
4043
4195
  const targetVersion = this.state.latestVersion || "latest";
4044
4196
  const spec = "dsh-session-manager@" + targetVersion;
4197
+ const registryUrl = this.getRegistryUrl();
4198
+ const clientTaskId = "update-" + Date.now() + "-" + Math.random().toString(36).slice(2, 8);
4199
+ let activeTaskId = clientTaskId;
4200
+ this.state.taskId = activeTaskId;
4045
4201
 
4046
4202
  try {
4047
4203
  let outcome = null;
@@ -4057,31 +4213,127 @@ function isTopmostDialog(z) {
4057
4213
  outcome = { application: testOverride.application || "restart-required" };
4058
4214
  }
4059
4215
  } else {
4060
- const remotePm = activeClientCtx?.remote?.pluginManager || activeClientCtx?.pluginManager;
4061
- if (remotePm && typeof remotePm.installBundle === "function") {
4062
- outcome = await remotePm.installBundle(spec);
4063
- } else {
4064
- const res = await callApi("/update/install", { version: targetVersion, registry: this.getRegistryUrl() });
4065
- if (!res || !res.ok) {
4066
- throw new Error(res?.error || "安装更新失败");
4067
- }
4216
+ let res = null;
4217
+ let shouldPoll = false;
4218
+ let callError = null;
4219
+ try {
4220
+ res = await callApi("/update/install", { version: targetVersion, registry: registryUrl, taskId: clientTaskId });
4221
+ } catch (err) {
4222
+ shouldPoll = true;
4223
+ callError = err;
4224
+ }
4225
+
4226
+ if (res && res.ok) {
4068
4227
  outcome = res.result;
4228
+ } else if (res && !res.ok) {
4229
+ if (res.code === "update-in-progress" && res.taskId) {
4230
+ // Another update task is already running: adopt its taskId and poll
4231
+ activeTaskId = res.taskId;
4232
+ this.state.taskId = activeTaskId;
4233
+ shouldPoll = true;
4234
+ } else {
4235
+ const errObj = new Error(res.error || "安装更新失败");
4236
+ if (res.rolledBack) {
4237
+ errObj.rolledBack = true;
4238
+ errObj.reason = res.error;
4239
+ errObj.currentVersion = this.state.currentVersion;
4240
+ }
4241
+ throw errObj;
4242
+ }
4243
+ }
4244
+
4245
+ if (shouldPoll) {
4246
+ const pollStart = Date.now();
4247
+ const maxPollMs = (typeof window !== "undefined" && typeof window.__DSH_SM_POLL_TIMEOUT_MS__ === "number")
4248
+ ? window.__DSH_SM_POLL_TIMEOUT_MS__
4249
+ : 90000;
4250
+ let reconciled = false;
4251
+ while (Date.now() - pollStart < maxPollMs) {
4252
+ await new Promise((resolve) => {
4253
+ const timer = setTimeout(resolve, 1500);
4254
+ if (typeof timer === "number" && timer === 1) {
4255
+ Promise.resolve().then(resolve);
4256
+ }
4257
+ });
4258
+
4259
+ let statusData = null;
4260
+ try {
4261
+ statusData = await fetchApi("/update/status?taskId=" + encodeURIComponent(activeTaskId));
4262
+ } catch (netErr) {
4263
+ // Only network or HTTP transport errors during status fetching allow polling retry
4264
+ continue;
4265
+ }
4266
+
4267
+ if (statusData && statusData.ok && statusData.result) {
4268
+ const r = statusData.result;
4269
+ if (r.status === "completed") {
4270
+ outcome = {
4271
+ application: r.application || "restart-required",
4272
+ spec,
4273
+ version: r.targetVersion || targetVersion,
4274
+ taskId: activeTaskId,
4275
+ alreadyInstalled: !!r.alreadyInstalled
4276
+ };
4277
+ reconciled = true;
4278
+ break;
4279
+ } else if (r.status === "rolled-back" || (r.status === "failed" && r.rolledBack)) {
4280
+ const rollErr = new Error((r.error || "安装更新失败") + "(已自动恢复旧版本 v" + (r.currentVersion || "") + ")");
4281
+ rollErr.rolledBack = true;
4282
+ rollErr.currentVersion = r.currentVersion || this.state.currentVersion;
4283
+ rollErr.reason = r.error;
4284
+ throw rollErr;
4285
+ } else if (r.status === "recovery-required") {
4286
+ throw new Error("更新失败且自动恢复旧版本失败: " + (r.rollbackError || r.error || "未知原因") + "。请手动重新安装插件。");
4287
+ } else if (r.status === "failed") {
4288
+ throw new Error(r.error || "安装更新失败");
4289
+ } else if (r.status === "superseded") {
4290
+ throw new Error("更新任务已被其他操作覆盖,请重新检查更新");
4291
+ }
4292
+ }
4293
+ }
4294
+
4295
+ if (!reconciled) {
4296
+ throw new Error("更新结果尚未确认,请刷新页面或重启 DSH 查看状态");
4297
+ }
4069
4298
  }
4070
4299
  }
4071
4300
 
4072
4301
  const application = outcome?.application || "restart-required";
4073
4302
  this.state.status = "done";
4074
4303
  this.state.application = application;
4304
+ this.state.taskId = activeTaskId;
4305
+ const installedV = outcome?.version || targetVersion;
4306
+ this.state.installedVersion = installedV;
4307
+ this.state.latestVersion = installedV;
4308
+ // Running version is NOT modified before restart/reload!
4309
+ this.state.runningVersion = CURRENT_VERSION;
4310
+ this.state.currentVersion = CURRENT_VERSION;
4311
+ this.state.rollbackInfo = null;
4312
+ this.state.error = null;
4313
+ if (openDialog || this.state.dialogOpen) {
4314
+ this.state.dialogOpen = true;
4315
+ }
4075
4316
  this._notify();
4076
4317
  return outcome;
4077
4318
  } catch (err) {
4078
4319
  this.state.status = "error";
4079
- this.state.error = err.message || "更新安装失败";
4080
- if (openDialog) {
4320
+ if (err.rolledBack) {
4321
+ this.state.rollbackInfo = {
4322
+ rolledBack: true,
4323
+ currentVersion: err.currentVersion || this.state.currentVersion,
4324
+ reason: err.reason || err.message,
4325
+ taskId: activeTaskId
4326
+ };
4327
+ this.state.error = err.message || ("安装更新失败(已自动恢复旧版本 v" + (err.currentVersion || this.state.currentVersion) + ")");
4328
+ } else {
4329
+ this.state.rollbackInfo = null;
4330
+ this.state.error = err.message || "更新安装失败";
4331
+ }
4332
+ if (openDialog || this.state.dialogOpen) {
4081
4333
  this.state.dialogOpen = true;
4082
4334
  }
4083
4335
  this._notify();
4084
- return { ok: false, error: this.state.error };
4336
+ return { ok: false, error: this.state.error, rolledBack: !!err.rolledBack };
4085
4337
  }
4086
4338
  }
4087
4339
  };
@@ -6142,6 +6394,9 @@ function isTopmostDialog(z) {
6142
6394
  () => updateStore.getSnapshot(),
6143
6395
  () => updateStore.getSnapshot()
6144
6396
  );
6397
+ useEffect(() => {
6398
+ try { updateStore.checkRecentStatus().catch(() => {}); } catch (_) {}
6399
+ }, []);
6145
6400
  const btnClass = "sm-footerBtn" + (wide ? " sm-footerBtn-wide" : " sm-footerBtn-rail");
6146
6401
  const btnLabel = t("footer.aria");
6147
6402
  return h(React.Fragment, null,
@@ -6255,8 +6510,8 @@ function isTopmostDialog(z) {
6255
6510
  let dialogTitle = t("update.title");
6256
6511
  if (isAvailable) dialogTitle = t("update.newVersion");
6257
6512
  else if (isUpdating) dialogTitle = t("update.installing");
6258
- else if (isDone) dialogTitle = t("update.title");
6259
- else if (isError) dialogTitle = t("update.error");
6513
+ else if (isDone) dialogTitle = t("update.done.title");
6514
+ else if (isError) dialogTitle = updateState.rollbackInfo ? t("update.rolledBack.title") : t("update.error");
6260
6515
 
6261
6516
  const handleClose = () => {
6262
6517
  if (!isUpdating) {
@@ -6288,22 +6543,64 @@ function isTopmostDialog(z) {
6288
6543
  }, h(CloseIcon, { size: 14 }))) : null
6289
6544
  ),
6290
6545
  h("div", { className: "sm-nativeDialogBody" },
6291
- h("div", { className: "sm-updateVersionGrid" },
6292
- h("span", { className: "sm-updateVersionLabel" }, t("update.currentVersion")),
6293
- h("span", { className: "sm-updateVersionValue" }, "v" + updateState.currentVersion),
6294
- h("span", { className: "sm-updateVersionLabel" }, t("update.latestVersion")),
6295
- h("span", { className: "sm-updateVersionValue" }, updateState.latestVersion ? "v" + updateState.latestVersion : (isChecking ? t("update.checking") : "-"))
6296
- ),
6297
- isChecking ? h("div", { className: "sm-updateStatusBox" }, t("update.checking")) : null,
6298
- isAvailable ? h("div", { className: "sm-updateStatusBox sm-warn" }, t("update.newVersion") + " (v" + updateState.latestVersion + ")") : null,
6299
- isUpdating ? h("div", { className: "sm-updateStatusBox sm-warn" }, t("update.installing")) : null,
6300
- isDone ? h("div", { className: "sm-updateStatusBox sm-success" },
6301
- updateState.application === "applied"
6302
- ? t("update.done.applied")
6303
- : t("update.done.restart", { from: updateState.currentVersion, to: updateState.latestVersion || updateState.currentVersion })
6304
- ) : null,
6305
- isError ? h("div", { className: "sm-updateStatusBox sm-error" }, (updateState.error || t("update.error"))) : null,
6306
- (isIdle && updateState.latestVersion && !isAvailable) ? h("div", { className: "sm-updateStatusBox sm-success" }, t("update.isLatest")) : null
6546
+ (() => {
6547
+ const runningV = updateState.currentVersion || updateState.runningVersion || CURRENT_VERSION;
6548
+ const hasInstalledDiff = !!(updateState.installedVersion && compareSemver(updateState.installedVersion, runningV) > 0);
6549
+ const targetV = hasInstalledDiff ? updateState.installedVersion : (updateState.latestVersion || runningV);
6550
+ return h("div", { className: "sm-updateVersionGrid" },
6551
+ h("span", { className: "sm-updateVersionLabel" }, hasInstalledDiff ? t("update.runningVersion") : t("update.currentVersion")),
6552
+ h("span", { className: "sm-updateVersionValue" }, "v" + runningV),
6553
+ h("span", { className: "sm-updateVersionLabel" }, hasInstalledDiff ? t("update.installedVersion") : t("update.latestVersion")),
6554
+ h("span", { className: "sm-updateVersionValue" }, hasInstalledDiff
6555
+ ? ("v" + updateState.installedVersion)
6556
+ : (updateState.latestVersion ? "v" + updateState.latestVersion : (isChecking ? t("update.checking") : "-")))
6557
+ );
6558
+ })(),
6559
+ (() => {
6560
+ const runningV = updateState.currentVersion || updateState.runningVersion || CURRENT_VERSION;
6561
+ const hasInstalledDiff = !!(updateState.installedVersion && compareSemver(updateState.installedVersion, runningV) > 0);
6562
+ const targetV = hasInstalledDiff ? updateState.installedVersion : (updateState.latestVersion || runningV);
6563
+ let statusBox = null;
6564
+ if (isChecking) {
6565
+ statusBox = h("div", { className: "sm-updateStatusBox sm-checking" }, t("update.checking"));
6566
+ } else if (isUpdating) {
6567
+ statusBox = h("div", { className: "sm-updateStatusBox sm-warn" }, t("update.installing"));
6568
+ } else if (isDone) {
6569
+ statusBox = h("div", { className: "sm-updateStatusBox sm-success", style: { textAlign: "left", whiteSpace: "pre-wrap" } },
6570
+ updateState.application === "applied" && runningV === targetV
6571
+ ? t("update.done.applied", { to: targetV, version: targetV })
6572
+ : (updateState.application === "refresh-required"
6573
+ ? t("update.done.refresh", { from: runningV, to: targetV })
6574
+ : t("update.done.restart", { from: runningV, to: targetV })
6575
+ )
6576
+ );
6577
+ } else if (isError) {
6578
+ if (updateState.rollbackInfo) {
6579
+ statusBox = h("div", { className: "sm-updateStatusBox sm-warn", style: { textAlign: "left", whiteSpace: "pre-wrap" } },
6580
+ h("div", { style: { fontWeight: "bold", marginBottom: "4px" } },
6581
+ t("update.rolledBack.title")
6582
+ ),
6583
+ h("div", null,
6584
+ t("update.rolledBack", { version: updateState.rollbackInfo.currentVersion })
6585
+ ),
6586
+ h("div", { style: { marginTop: "4px", opacity: 0.9, fontSize: "12px" } },
6587
+ t("update.rolledBack.reason", { reason: updateState.rollbackInfo.reason })
6588
+ )
6589
+ );
6590
+ } else {
6591
+ statusBox = h("div", { className: "sm-updateStatusBox sm-error", style: { textAlign: "left", whiteSpace: "pre-wrap" } },
6592
+ (updateState.error || t("update.error"))
6593
+ );
6594
+ }
6595
+ } else if (isAvailable) {
6596
+ statusBox = h("div", { className: "sm-updateStatusBox sm-warn" }, t("update.newVersion") + " (v" + updateState.latestVersion + ")");
6597
+ } else if (updateState.notes) {
6598
+ statusBox = h("div", { className: "sm-updateStatusBox sm-warn" }, t(updateState.notes));
6599
+ } else if (isIdle && updateState.latestVersion) {
6600
+ statusBox = h("div", { className: "sm-updateStatusBox sm-success" }, t("update.isLatest"));
6601
+ }
6602
+ return statusBox || h("div", { className: "sm-updateStatusBox sm-placeholder", style: { visibility: "hidden" } }, "-");
6603
+ })()
6307
6604
  ),
6308
6605
  h("div", { className: "sm-nativeDialogFooter" },
6309
6606
  isAvailable ? h(React.Fragment, null,
@@ -6315,7 +6612,7 @@ function isTopmostDialog(z) {
6315
6612
  h("button", {
6316
6613
  type: "button",
6317
6614
  className: "sm-nativeDialogButton sm-nativeDialogConfirm",
6318
- onClick: () => updateStore.startUpdate()
6615
+ onClick: () => updateStore.startUpdate({ openDialog: true })
6319
6616
  }, t("update.btn.update", { version: updateState.latestVersion }))
6320
6617
  ) : null,
6321
6618
  isUpdating ? h("button", {
@@ -6339,7 +6636,7 @@ function isTopmostDialog(z) {
6339
6636
  className: "sm-nativeDialogButton sm-nativeDialogConfirm",
6340
6637
  onClick: () => {
6341
6638
  if (updateState.latestVersion && compareSemver(updateState.latestVersion, updateState.currentVersion) > 0) {
6342
- updateStore.startUpdate();
6639
+ updateStore.startUpdate({ openDialog: true });
6343
6640
  } else {
6344
6641
  updateStore.checkForUpdate({ manual: true, openDialog: true });
6345
6642
  }
@@ -6352,11 +6649,15 @@ function isTopmostDialog(z) {
6352
6649
  className: "sm-nativeDialogButton sm-nativeDialogCancel",
6353
6650
  onClick: handleClose
6354
6651
  }, t("update.btn.close")),
6355
- !isChecking ? h("button", {
6652
+ h("button", {
6356
6653
  type: "button",
6357
- className: "sm-nativeDialogButton",
6358
- onClick: () => updateStore.checkForUpdate({ manual: true, openDialog: true })
6359
- }, t("update.btn.check")) : null
6654
+ className: "sm-nativeDialogButton sm-nativeDialogConfirm",
6655
+ disabled: isChecking,
6656
+ style: { minWidth: "96px", whiteSpace: "nowrap" },
6657
+ onClick: () => {
6658
+ if (!isChecking) updateStore.checkForUpdate({ manual: true, openDialog: true });
6659
+ }
6660
+ }, isChecking ? t("update.checking") : t("update.btn.check"))
6360
6661
  ) : null
6361
6662
  )
6362
6663
  )
@@ -6393,12 +6694,20 @@ function isTopmostDialog(z) {
6393
6694
  updateStore.setDialogOpacity(e.target.value);
6394
6695
  };
6395
6696
 
6697
+ useEffect(() => {
6698
+ try { updateStore.checkRecentStatus().catch(() => {}); } catch (_) {}
6699
+ }, []);
6700
+
6701
+ const runningV = updateState.currentVersion || updateState.runningVersion || CURRENT_VERSION;
6702
+ const hasInstalledDiff = !!(updateState.installedVersion && compareSemver(updateState.installedVersion, runningV) > 0);
6703
+ const targetV = hasInstalledDiff ? updateState.installedVersion : (updateState.latestVersion || runningV);
6704
+
6396
6705
  return h("div", { className: "sm-settingsCard" },
6397
6706
  h("div", { className: "sm-settingsHeader" },
6398
6707
  h("h3", { className: "sm-settingsTitle" },
6399
6708
  h(NpmIcon, { size: 18 }),
6400
6709
  h("span", null, t("settings.title")),
6401
- h("span", { className: "sm-settingsTag" }, "v" + updateState.currentVersion)
6710
+ h("span", { className: "sm-settingsTag" }, "v" + runningV)
6402
6711
  )
6403
6712
  ),
6404
6713
  h("div", { className: "sm-settingsSection" },
@@ -6415,31 +6724,48 @@ function isTopmostDialog(z) {
6415
6724
  type: "button",
6416
6725
  className: "sm-nativeDialogButton sm-nativeDialogConfirm sm-settingsCheckBtn",
6417
6726
  disabled: isUpdating,
6418
- onClick: () => updateStore.startUpdate({ openDialog: false })
6727
+ onClick: () => updateStore.startUpdate({ openDialog: true })
6419
6728
  }, isUpdating ? t("update.btn.updating") : t("update.btn.update", { version: updateState.latestVersion })) : null
6420
6729
  )
6421
6730
  ),
6422
6731
  h("div", { className: "sm-settingsRow" },
6423
- h("span", null, t("update.currentVersion")),
6424
- h("span", { style: { fontFamily: "monospace" } }, "v" + updateState.currentVersion)
6732
+ h("span", null, hasInstalledDiff ? t("update.runningVersion") : t("update.currentVersion")),
6733
+ h("span", { style: { fontFamily: "monospace" } }, "v" + runningV)
6425
6734
  ),
6735
+ hasInstalledDiff ? h("div", { className: "sm-settingsRow" },
6736
+ h("span", null, t("update.installedVersion")),
6737
+ h("span", { style: { fontFamily: "monospace" } }, "v" + updateState.installedVersion)
6738
+ ) : null,
6426
6739
  h("div", { className: "sm-settingsRow" },
6427
6740
  h("span", null, t("update.latestVersion")),
6428
6741
  h("span", { style: { fontFamily: "monospace" } },
6429
6742
  updateState.latestVersion ? "v" + updateState.latestVersion : (isChecking ? t("update.checking") : "-")
6430
6743
  )
6431
6744
  ),
6745
+ updateState.notes ? h("div", { className: "sm-updateStatusBox sm-warn" },
6746
+ t(updateState.notes)
6747
+ ) : null,
6432
6748
  updateState.status === "available" ? h("div", { className: "sm-updateStatusBox sm-warn" },
6433
6749
  t("update.newVersion") + " (v" + updateState.latestVersion + ")"
6434
6750
  ) : null,
6435
6751
  updateState.status === "updating" ? h("div", { className: "sm-updateStatusBox sm-warn" },
6436
6752
  t("update.installing")
6437
6753
  ) : null,
6438
- updateState.status === "done" ? h("div", { className: "sm-updateStatusBox sm-success" },
6439
- updateState.application === "applied" ? t("update.done.applied") : t("update.done.restart", { from: updateState.currentVersion, to: updateState.latestVersion || updateState.currentVersion })
6754
+ updateState.status === "done" ? h("div", { className: "sm-updateStatusBox sm-success", style: { whiteSpace: "pre-wrap" } },
6755
+ updateState.application === "applied" && runningV === targetV
6756
+ ? t("update.done.applied", { to: targetV, version: targetV })
6757
+ : (updateState.application === "refresh-required"
6758
+ ? t("update.done.refresh", { from: runningV, to: targetV })
6759
+ : t("update.done.restart", { from: runningV, to: targetV })
6760
+ )
6440
6761
  ) : null,
6441
- updateState.status === "error" ? h("div", { className: "sm-updateStatusBox sm-error" },
6442
- updateState.error || t("update.error")
6762
+ updateState.status === "error" ? h("div", {
6763
+ className: "sm-updateStatusBox " + (updateState.rollbackInfo ? "sm-warn" : "sm-error"),
6764
+ style: { whiteSpace: "pre-wrap" }
6765
+ },
6766
+ updateState.rollbackInfo
6767
+ ? (t("update.rolledBack", { version: updateState.rollbackInfo.currentVersion }) + "\n" + t("update.rolledBack.reason", { reason: updateState.rollbackInfo.reason }))
6768
+ : (updateState.error || t("update.error"))
6443
6769
  ) : null
6444
6770
  ),
6445
6771
  h("div", { className: "sm-settingsSection" },
@@ -6465,7 +6791,8 @@ function isTopmostDialog(z) {
6465
6791
  "aria-label": t("settings.registry")
6466
6792
  },
6467
6793
  h("option", { value: "registry.npmjs.org" }, t("settings.registry.npm")),
6468
- h("option", { value: "registry.npmmirror.com" }, t("settings.registry.china"))
6794
+ h("option", { value: "registry.npmmirror.com" }, t("settings.registry.china")),
6795
+ h("option", { value: "127.0.0.1:4873" }, t("settings.registry.local"))
6469
6796
  )
6470
6797
  ),
6471
6798
  h("div", { className: "sm-settingsRow" },
@@ -6767,6 +7094,12 @@ function isTopmostDialog(z) {
6767
7094
  inject: () => actions()
6768
7095
  }, SessionManagerSettingsCard));
6769
7096
 
7097
+ ctx.effect(() => {
7098
+ try {
7099
+ updateStore.checkRecentStatus().catch(() => {});
7100
+ } catch (_) {}
7101
+ }, "session-manager: check update status on boot");
7102
+
6770
7103
  ctx.effect(() => {
6771
7104
  if (!updateStore.getAutoCheck()) return;
6772
7105
  const timer = setTimeout(() => {
@@ -6856,6 +7189,7 @@ function isTopmostDialog(z) {
6856
7189
  exports.openCustomSelect = openCustomSelect;
6857
7190
  exports.closeCustomSelect = closeCustomSelect;
6858
7191
  exports.installCustomDropdownInterceptor = installCustomDropdownInterceptor;
7192
+ exports.getTargetSmSelect = getTargetSmSelect;
6859
7193
  exports.SM_SELECT_SELECTOR = SM_SELECT_SELECTOR;
6860
7194
  return module.exports;
6861
7195
  }