dsh-session-manager 0.6.4 → 0.7.1

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
@@ -174,8 +174,8 @@ function isTopmostDialog(z) {
174
174
  "menu.delete": "删除会话…",
175
175
  "menu.move": "移动到工作区…",
176
176
  "menu.manage": "打开会话管理…",
177
- "footer.aria": "会话管理",
178
- "footer.label": "会话管理",
177
+ "footer.aria": "会话与工作区",
178
+ "footer.label": "会话与工作区",
179
179
  "panel.title": "会话管理",
180
180
  "panel.github.title": "欢迎Star和提交Issue",
181
181
  "panel.update.title": "检查更新",
@@ -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": "项目",
@@ -229,6 +229,8 @@ function isTopmostDialog(z) {
229
229
  "panel.sort.updated-asc": "最早更新",
230
230
  "panel.sort.created-desc": "最新创建",
231
231
  "panel.sort.created-asc": "最早创建",
232
+ "panel.sort.name-asc": "名称升序",
233
+ "panel.sort.name-desc": "名称降序",
232
234
  "panel.count": "显示 {shown} / {total} 个会话",
233
235
  "panel.reset": "重置筛选",
234
236
  "panel.empty.filtered": "没有匹配的会话,请调整搜索或筛选条件",
@@ -238,6 +240,9 @@ function isTopmostDialog(z) {
238
240
  "panel.empty": "暂无会话",
239
241
  "panel.empty.archived": "归档为空",
240
242
  "panel.archived": "已归档",
243
+ "panel.workspaceArchived": "工作区已归档",
244
+ "notice.workspaceReAdded": "工作区已恢复显示,但旧会话继续归档",
245
+ "error.workspaceArchived": "所属工作区已归档,无法取消归档该会话。请先在工作区管理中恢复工作区。",
241
246
  "panel.running": "运行中",
242
247
  "panel.current": "当前",
243
248
  "panel.session.new": "新建会话",
@@ -259,6 +264,9 @@ function isTopmostDialog(z) {
259
264
  "confirm.delete.title": "删除会话",
260
265
  "confirm.delete.desc": "「{title}」:删除,不可恢复",
261
266
  "confirm.delete.running": "该会话正在运行,删除将立即中断它。",
267
+ "confirm.delete.subagents": "将连带删除 {count} 个关联的子智能体会话。",
268
+ "confirm.delete.previewLoading": "正在检查关联的子智能体会话…",
269
+ "confirm.delete.previewError": "检查关联子智能体会话失败,无法确认删除安全,请稍后重试。",
262
270
  "confirm.delete.confirm": "确认删除",
263
271
  "confirm.move.title": "移动会话到工作区",
264
272
  "confirm.move.desc": "「{title}」:移动至目标工作区",
@@ -273,9 +281,34 @@ function isTopmostDialog(z) {
273
281
  "error.operation": "操作失败:{message}",
274
282
  "workspace.default": "默认工作区",
275
283
  "workspace.sessions": "{count} 个会话",
284
+ "panel.tab.sessions": "会话管理",
285
+ "panel.tab.workspaces": "工作区管理",
286
+ "workspace.search": "搜索工作区名称或路径",
287
+ "workspace.filter.all": "全部",
288
+ "workspace.filter.active": "未归档",
289
+ "workspace.filter.archived": "已归档",
290
+ "workspace.sort.updated-desc": "最近更新",
291
+ "workspace.sort.updated-asc": "最早更新",
292
+ "workspace.sort.name-asc": "名称升序",
293
+ "workspace.sort.name-desc": "名称降序",
294
+ "workspace.bulk.archive": "归档",
295
+ "workspace.bulk.unarchive": "取消归档",
296
+ "workspace.count": "显示 {shown} / {total} 个工作区",
297
+ "workspace.reset": "重置筛选",
298
+ "workspace.empty.filtered": "没有匹配的工作区,请调整搜索或筛选条件",
299
+ "workspace.empty": "暂无工作区",
300
+ "workspace.badge.archived": "已归档",
301
+ "workspace.lastUpdated": "最近更新:{time}",
302
+ "workspace.switch.active": "已在原生侧边栏显示(点击归档隐藏)",
303
+ "workspace.switch.archived": "已在原生侧边栏隐藏(点击恢复显示)",
304
+ "workspace.switch.aria": "工作区显示开关:{name}",
305
+ "workspace.archive.confirm.title": "归档工作区",
306
+ "workspace.archive.confirm.desc": "确认归档工作区「{name}」?此操作将同时归档该工作区下的 {count} 个未归档会话并在原生侧边栏隐藏该工作区。",
307
+ "workspace.archive.failed": "归档工作区失败:{message}",
308
+ "workspace.unarchive.failed": "恢复工作区失败:{message}",
276
309
  "bulk.title": "批量管理",
277
310
  "bulk.selectedFiltered": "已选 {selected} / {filtered} 项",
278
- "bulk.selectAllFiltered": "全选当前筛选",
311
+ "bulk.selectAllFiltered": "全选筛选",
279
312
  "bulk.clear": "清空选择",
280
313
  "bulk.archive": "归档",
281
314
  "bulk.unarchive": "取消归档",
@@ -412,8 +445,8 @@ function isTopmostDialog(z) {
412
445
  "menu.delete": "Delete session…",
413
446
  "menu.move": "Move to workspace…",
414
447
  "menu.manage": "Open session manager…",
415
- "footer.aria": "Session manager",
416
- "footer.label": "Sessions",
448
+ "footer.aria": "Sessions & Workspaces",
449
+ "footer.label": "Sessions & Workspaces",
417
450
  "panel.title": "Session manager",
418
451
  "panel.github.title": "Star the project or open an issue",
419
452
  "panel.update.title": "Check for updates",
@@ -438,14 +471,14 @@ function isTopmostDialog(z) {
438
471
  "update.autoCheck": "Automatically check for updates",
439
472
  "update.autoCheck.enable": "Enable",
440
473
  "settings.registry": "Install Source",
441
- "settings.registry.npm": "npm official registry.npmjs.org",
442
- "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)",
443
476
  "settings.title": "Session Manager",
444
477
  "settings.pluginName": "dsh-session-manager",
445
478
  "settings.versionSection": "Plugin Version",
446
479
  "settings.prefSection": "Preferences",
447
480
  "settings.dialogOpacity": "Dialog opacity",
448
- "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",
449
482
  "settings.dialogOpacity.value": "{value}%",
450
483
  "settings.dialogOpacity.reset": "Reset to default",
451
484
  "settings.projectSection": "Project",
@@ -467,6 +500,8 @@ function isTopmostDialog(z) {
467
500
  "panel.sort.updated-asc": "Least recently updated",
468
501
  "panel.sort.created-desc": "Newest created",
469
502
  "panel.sort.created-asc": "Oldest created",
503
+ "panel.sort.name-asc": "Name (A-Z)",
504
+ "panel.sort.name-desc": "Name (Z-A)",
470
505
  "panel.count": "Showing {shown} / {total} sessions",
471
506
  "panel.reset": "Reset filters",
472
507
  "panel.empty.filtered": "No matching sessions. Adjust your search or filters.",
@@ -543,6 +578,9 @@ function isTopmostDialog(z) {
543
578
  "panel.empty": "No sessions",
544
579
  "panel.empty.archived": "No archived sessions",
545
580
  "panel.archived": "Archived",
581
+ "panel.workspaceArchived": "Workspace Archived",
582
+ "notice.workspaceReAdded": "Workspace restored to view, but previous sessions remain archived.",
583
+ "error.workspaceArchived": "The workspace for this session is archived. Please restore the workspace in Workspace Management first.",
546
584
  "panel.running": "Running",
547
585
  "panel.current": "Current",
548
586
  "panel.session.new": "New session",
@@ -564,6 +602,9 @@ function isTopmostDialog(z) {
564
602
  "confirm.delete.title": "Delete session",
565
603
  "confirm.delete.desc": "\"{title}\": Delete, cannot be undone",
566
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.",
567
608
  "confirm.delete.confirm": "Delete",
568
609
  "confirm.move.title": "Move session to workspace",
569
610
  "confirm.move.desc": "\"{title}\": Move to target workspace",
@@ -577,7 +618,52 @@ function isTopmostDialog(z) {
577
618
  "busy.processing": "Working…",
578
619
  "error.operation": "Operation failed: {message}",
579
620
  "workspace.default": "Default workspace",
580
- "workspace.sessions": "{count} sessions"
621
+ "workspace.sessions": "{count} sessions",
622
+ "panel.tab.sessions": "Sessions",
623
+ "panel.tab.workspaces": "Workspaces",
624
+ "workspace.search": "Search by name or path",
625
+ "workspace.filter.all": "All",
626
+ "workspace.filter.active": "Active",
627
+ "workspace.filter.archived": "Archived",
628
+ "workspace.sort.updated-desc": "Recently updated",
629
+ "workspace.sort.updated-asc": "Earliest updated",
630
+ "workspace.sort.name-asc": "Name (A-Z)",
631
+ "workspace.sort.name-desc": "Name (Z-A)",
632
+ "workspace.bulk.archive": "Archive",
633
+ "workspace.bulk.unarchive": "Unarchive",
634
+ "workspace.count": "Showing {shown} / {total} workspaces",
635
+ "workspace.reset": "Reset filters",
636
+ "workspace.empty.filtered": "No matching workspaces",
637
+ "workspace.empty": "No workspaces",
638
+ "workspace.badge.archived": "Archived",
639
+ "workspace.lastUpdated": "Updated: {time}",
640
+ "workspace.switch.active": "Shown in sidebar (click to archive)",
641
+ "workspace.switch.archived": "Hidden from sidebar (click to restore)",
642
+ "workspace.switch.aria": "Workspace visibility toggle: {name}",
643
+ "workspace.archive.confirm.title": "Archive Workspace",
644
+ "workspace.archive.confirm.desc": "Archive workspace \"{name}\"? This will also archive {count} active sessions in this workspace and hide it from the sidebar.",
645
+ "workspace.archive.failed": "Failed to archive workspace: {message}",
646
+ "workspace.unarchive.failed": "Failed to restore workspace: {message}"
647
+ };
648
+
649
+ const smTranslate = (k, vars) => {
650
+ const lang = (typeof window !== "undefined" && window.__smActiveLanguage) || "zh";
651
+ const dict = lang === "en" ? (en[k] || zh[k] || k) : (zh[k] || en[k] || k);
652
+ if (!vars || typeof dict !== "string") return dict;
653
+ return dict.replace(/\{(\w+)\}/g, (_, key) => vars[key] !== undefined ? vars[key] : "{" + key + "}");
654
+ };
655
+
656
+ const showNotice = (msg) => {
657
+ if (!msg) return;
658
+ try {
659
+ if (activeClientCtx?.notifications?.info && typeof activeClientCtx.notifications.info === "function") {
660
+ activeClientCtx.notifications.info(msg);
661
+ return;
662
+ }
663
+ } catch (_) {}
664
+ if (typeof window !== "undefined" && typeof window.alert === "function") {
665
+ window.alert(msg);
666
+ }
581
667
  };
582
668
 
583
669
  const css = [
@@ -593,10 +679,15 @@ function isTopmostDialog(z) {
593
679
  ".sm-footerBtn svg{flex:none}",
594
680
  ".sm-panel{display:flex;flex-direction:column;gap:10px;min-height:280px;max-height:min(70vh,640px);min-width:480px}",
595
681
  ".sm-filter{display:flex;gap:4px;flex-wrap:wrap}",
596
- ".sm-filterBtn{cursor:pointer;display:inline-flex;align-items:center;color:var(--dsw-alias-label-secondary);background:0 0;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;height:28px;padding:3px 10px;font-size:12px;line-height:18px;white-space:nowrap;box-sizing:border-box}",
597
- ".sm-filterBtn:hover{background:var(--dsw-alias-interactive-bg-hover)}",
682
+ ".sm-filterBtn{cursor:pointer;display:inline-flex;align-items:center;color:var(--dsw-alias-label-secondary);background:0 0;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;height:28px;padding:3px 10px;font-size:12px;line-height:18px;white-space:nowrap;box-sizing:border-box;transition:all .15s ease}",
683
+ ".sm-filterBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}",
684
+ ".sm-filterBtn:disabled{opacity:.4;cursor:not-allowed}",
598
685
  ".sm-filterBtn.sm-on{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-module-platform);border-color:transparent}",
599
- ".sm-list{display:flex;flex-direction:column;gap:4px;min-height:0;overflow:auto;flex:1}",
686
+ ".sm-list{display:flex;flex-direction:column;gap:4px;min-height:0;overflow:auto;flex:1;scrollbar-gutter:stable}",
687
+ ".sm-list::-webkit-scrollbar{width:6px;height:6px}",
688
+ ".sm-list::-webkit-scrollbar-track{background:transparent}",
689
+ ".sm-list::-webkit-scrollbar-thumb{background:var(--dsw-alias-scrollbar-bg-l1,rgba(128,128,128,.2));border-radius:999px}",
690
+ ".sm-list::-webkit-scrollbar-thumb:hover{background:var(--dsw-alias-scrollbar-hover-l1,rgba(128,128,128,.35))}",
600
691
  ".sm-row{box-sizing:border-box;display:flex;align-items:center;gap:10px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px}",
601
692
  ".sm-row:hover{background:var(--dsw-alias-interactive-bg-hover)}",
602
693
  ".sm-rowCurrent{border-color:var(--dsw-alias-state-business-primary,var(--dsw-alias-label-secondary))}",
@@ -607,6 +698,7 @@ function isTopmostDialog(z) {
607
698
  ".sm-rowActions{display:flex;gap:4px;flex:none;flex-wrap:wrap;justify-content:flex-end}",
608
699
  ".sm-badge{flex:none;border-radius:999px;padding:0 6px;font-size:10px;line-height:16px}",
609
700
  ".sm-badgeArchived{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-markdown-tag)}",
701
+ ".sm-badgeWorkspaceArchived{color:var(--dsw-alias-state-warn-label,#dd8629);background:var(--dsw-alias-state-warn-tertiary,#fef5e7)}",
610
702
  ".sm-badgeRunning{color:var(--dsw-alias-state-warn-label,#dd8629);background:var(--dsw-alias-state-warn-tertiary,#fef5e7)}",
611
703
  ".sm-badgeCurrent{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-markdown-tag)}",
612
704
  // The former .sm-danger / .sm-dangerText rules were removed: no element
@@ -628,7 +720,7 @@ function isTopmostDialog(z) {
628
720
  ".sm-moveItemSelected{border-color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-tertiary)}",
629
721
  ".sm-moveItemName{color:var(--dsw-alias-label-primary);font-size:13px;line-height:18px}",
630
722
  ".sm-moveItemMeta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}.sm-panelModal{width:760px;max-width:92vw}.sm-panelModal .sm-rowActions{flex-wrap:wrap;justify-content:flex-end}.sm-headerMenu{position:absolute;right:0;top:100%;margin-top:4px;z-index:100005;display:flex;flex-direction:column;min-width:140px;padding:4px;background:var(--dsw-alias-bg-layer-2,#fff);border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;box-shadow:0 4px 16px rgba(0,0,0,.15)}.sm-headerMenuItem{display:flex;align-items:center;gap:8px;width:100%;text-align:left;background:0 0;border:0;border-radius:6px;color:var(--dsw-alias-label-primary,#111);cursor:pointer;padding:6px 10px;font-size:13px;line-height:18px;white-space:nowrap}.sm-headerMenuItem:hover{background:var(--dsw-alias-interactive-bg-hover)}.sm-headerMenuItem:disabled{opacity:.5;cursor:not-allowed}.sm-headerMenuItem .sm-headerBtnIcon{width:20px;min-width:20px;text-align:center;flex-shrink:0}.sm-headerMenuItemDanger{color:var(--dsw-alias-state-error-primary,#d92d20)}.sm-headerMenuItemDanger:hover{background:var(--dsw-alias-interactive-bg-hover-danger)}.sm-headerMenuUltraOnly{display:none!important}.sm-headerBtn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:32px;height:32px;box-sizing:border-box;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-button-floating-fill,#fff);border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;cursor:pointer;padding:0 10px;font-size:13px;line-height:1;white-space:nowrap;transition:background .15s,color .15s,border-color .15s;flex-shrink:0}.sm-headerBtn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l1)}.sm-headerBtn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px}.sm-headerBtn svg{width:16px;height:16px;display:block;margin:auto;flex-shrink:0}.sm-migratePanel{display:flex;flex-direction:column;gap:14px;min-width:480px}.sm-migrateRow{display:flex;flex-direction:column;gap:8px}.sm-migrateRow>label{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:16px;font-weight:500}.sm-migrateChips{display:flex;flex-wrap:wrap;gap:6px}.sm-migrateChip{padding:6px 12px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);background:transparent;cursor:pointer;font-size:13px;line-height:18px;color:var(--dsw-alias-label-primary);white-space:nowrap}.sm-migrateChip:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.sm-migrateChip:disabled{opacity:.5;cursor:not-allowed}.sm-migrateChipOn{background:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-label-primary-foreground,#fff);border-color:var(--dsw-alias-state-business-primary)}.sm-migrateEmpty{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.sm-migratePreview{padding:10px 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font-size:13px;line-height:18px}.sm-migrateWarn{padding:10px 12px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-warn-primary,#f59e0b) 30%,transparent);border-radius:8px;background:var(--dsw-alias-state-warn-tertiary,#fef5e7);color:var(--dsw-alias-label-secondary);font-size:13px;line-height:18px}.sm-migrateList{max-height:200px;overflow:auto;border:1px solid var(--dsw-alias-border-l2);border-radius:8px}.sm-headerBtnDanger{background:var(--dsw-alias-button-floating-fill,#fff);color:var(--dsw-alias-state-error-primary,#d92d20);border-color:var(--dsw-alias-state-error-primary,#d92d20);font-weight:500}.sm-headerBtnDanger:hover:not(:disabled){background:var(--dsw-alias-state-error-primary,#d92d20);color:var(--dsw-alias-label-primary-foreground,#fff);border-color:var(--dsw-alias-state-error-primary,#d92d20)}.sm-header{display:inline-flex;align-items:center;gap:6px;flex-wrap:nowrap}.sm-headerSecondaryActions{display:inline-flex;align-items:center;gap:6px;flex-wrap:nowrap}.sm-headerCompactMenu{display:none;position:relative}.sm-headerCompactMenu.sm-headerCompactMenuOpen{position:relative;z-index:100004}.sm-headerCompactMenuBtn{width:32px;min-width:32px;height:32px;padding:0;justify-content:center;font-size:16px;line-height:1}.sm-headerBtnIcon{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;font-size:14px;line-height:1;vertical-align:middle;flex-shrink:0}.sm-headerBtn.sm-markIcon{width:32px;min-width:32px;height:32px;padding:0;justify-content:center}.sm-headerBtn.sm-markIcon .sm-headerBtnIcon{width:16px;height:16px;font-size:15px;line-height:1}.sm-headerCompactMenuBtn .sm-headerBtnIcon{width:16px;height:16px;font-size:16px;line-height:1}.sm-annotationToggleOpen{position:relative;z-index:100004}.sm-headerBtnActive{background:var(--dsw-alias-button-ghost-active-fill);color:var(--dsw-alias-label-primary)}@container (max-width:1000px){.sm-headerBtn{width:32px;min-width:32px;height:32px;padding:0;justify-content:center}.sm-headerBtnLabel,.sm-priorityText,.sm-priorityBadge{display:none}.sm-headerBtn.sm-headerBtn-withBadge{width:auto;min-width:32px;padding:0 6px}}@container (max-width:520px){.sm-headerSecondaryActions,.sm-headerSecondaryAction{display:none!important}.sm-headerCompactMenu{display:inline-flex}}@container (max-width:420px){.sm-header>:not(.sm-headerCompactMenu){display:none!important}.sm-header .sm-headerBtn:not(.sm-headerCompactMenuBtn){display:none!important}.sm-headerCompactMenu{display:inline-flex!important}.sm-headerMenu{right:auto!important;left:0!important}.sm-headerMenuUltraOnly{display:flex!important}}",
631
- ".sm-panelDialog{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);width:600px;max-width:calc(100vw - 32px);max-height:560px;height:auto;box-sizing:border-box;display:flex;flex-direction:column;gap:0;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;z-index:1020}.sm-panelDialog .sm-nativeDialogHeader{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px 16px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee);flex:none;position:relative;min-height:46px;box-sizing:border-box}.sm-panelDialog .sm-nativeDialogTitle{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin:0;font-size:14px;line-height:20px;font-weight:600;min-width:0;max-width:calc(100% - 140px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:center;pointer-events:auto;z-index:0}.sm-panelDialog .sm-nativeDialogHeaderActions{display:flex;align-items:center;gap:6px;flex:none;position:relative;z-index:1;margin-left:auto}.sm-nativeDialogFooter,.sm-bulkDialog .sm-nativeDialogFooter,.sm-confirmDialog .sm-nativeDialogFooter,.sm-migrateDialog .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:6px;margin-top:0}.sm-panelDialog .sm-panel{display:flex;flex-direction:column;gap:6px;min-height:0;flex:1 1 auto;overflow:auto;padding:8px 12px}.sm-panelDialog .sm-list{display:flex;flex-direction:column;gap:2px;flex:1 1 auto;min-height:0;overflow:auto}.sm-panelDialog .sm-row{display:flex;align-items:center;gap:8px;padding:6px 10px;min-height:36px;border:1px solid transparent;border-radius:8px;background:transparent;flex-shrink:0}.sm-panelDialog .sm-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.sm-panelDialog .sm-rowCurrent{background:var(--dsw-alias-bg-module-platform);border-color:var(--dsw-alias-state-business-primary,#356ae6)}.sm-panelDialog .sm-rowMain{display:flex;align-items:stretch!important;gap:8px;min-width:0;flex:1 1 auto;overflow:visible;text-align:left!important}.sm-panelDialog .sm-rowTitle{min-width:0;flex:1 1 0%;width:100%;align-self:stretch;display:flex;align-items:center;justify-content:flex-start;gap:6px;font-size:12px;line-height:16px;overflow:hidden;text-align:left!important;direction:ltr!important;unicode-bidi:plaintext}.sm-confirmDialog{z-index:10000}.sm-confirmDialog.sm-nativeDialogLayer{position:fixed;left:calc(50vw + 184px);top:calc(50vh - 90px);width:360px;max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);display:block;align-items:initial;justify-content:initial;box-sizing:border-box;background:transparent;border:0;box-shadow:none;padding:0;inset:calc(50vh - 90px) auto auto calc(50vw + 308px)}.sm-confirmDialog.sm-nativeDialogLayer .sm-nativeDialogBackdrop{display:none}.sm-confirmDialog.sm-nativeDialogLayer .sm-nativeDialog{position:relative;left:auto;top:auto;transform:none;width:auto;max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);display:flex;flex-direction:column;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);padding:0;overflow:hidden;animation:sm-confirmPop .14s ease-out}.sm-confirmDialog .sm-nativeDialogHeader{position:relative;padding:12px 16px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee);display:flex;align-items:center;justify-content:center;min-height:44px;box-sizing:border-box}.sm-confirmDialog .sm-nativeDialogTitle{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin:0;font-size:14px;line-height:20px;font-weight:600;text-align:center;width:auto;max-width:calc(100% - 32px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sm-confirmDialog .sm-nativeDialogBody{padding:10px 16px;display:flex;flex-direction:column;gap:8px;min-height:0;max-height:calc(100vh - 120px);overflow-y:auto}.sm-confirmDialog .sm-nativeDialogDescription{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);white-space:pre-wrap}.sm-confirmDialog .sm-warn{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;margin-top:0}.sm-confirmDialog .sm-nativeDialogFooter{padding:10px 16px;border-top:1px solid var(--dsw-alias-border-l2,#eee);flex-wrap:wrap;justify-content:space-between;gap:6px;margin-top:0}.sm-confirmDialog .sm-nativeDialogButton{min-height:28px;padding:3px 14px;font-size:12px;line-height:20px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;cursor:pointer;background:transparent;color:var(--dsw-alias-label-primary);text-align:center}.sm-confirmDialog .sm-nativeDialogCancel:hover:not(:disabled),.sm-migrateDialog .sm-nativeDialogCancel:hover:not(:disabled),.sm-updateDialog .sm-nativeDialogCancel:hover:not(:disabled),.sm-bulkDialog .sm-nativeDialogCancel:hover:not(:disabled),.sm-panelDialog .sm-nativeDialogCancel:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.sm-confirmDialog .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-confirmDialog .sm-nativeDialogConfirm:hover:not(:disabled){filter:brightness(.94)}@keyframes sm-confirmPop{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.sm-confirmDialog.sm-nativeDialog{padding:0;width:auto;max-width:none}.sm-panelDialog .sm-rowTitle>span:first-child{display:block;min-width:0;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;flex:1 1 auto;width:auto;text-align:left!important;direction:ltr!important;unicode-bidi:plaintext}.sm-panelDialog .sm-rowMeta{display:none}.sm-panelDialog .sm-rowActions{display:flex;gap:2px;flex:none;justify-content:flex-end}.sm-panelDialog .sm-rowActions .sm-rowBtn{min-height:22px;padding:0 7px;font-size:11px;line-height:18px;border-radius:6px;border:1px solid transparent;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap}.sm-rowBtn.sm-markIcon{padding:0 6px;font-size:13px;line-height:18px}.sm-panelDialog .sm-rowActions .sm-rowBtn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.sm-panelDialog .sm-rowActions .sm-rowBtnDanger{color:var(--dsw-alias-state-error-primary)}.sm-panelDialog .sm-rowActions .sm-rowBtnDanger:hover{background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary)}.sm-panelDialog .sm-filter{flex:none;padding:6px 12px 4px;gap:4px;display:flex;flex-wrap:wrap}.sm-panelDialog .sm-filterBtn{min-height:24px;padding:2px 10px;font-size:11px;line-height:16px;border-radius:4px;border:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer}.sm-panelDialog .sm-filterBtn.sm-on{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary)}.sm-panelDialog .sm-badge{font-size:10px;line-height:14px;padding:0 5px;border-radius:6px;flex:none}.sm-panelDialog .sm-error{padding:6px 10px;margin:0;font-size:12px;line-height:16px}.sm-panelDialog .sm-nativeDialogButton{min-height:28px;padding:3px 14px;font-size:12px;line-height:20px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:transparent;color:var(--dsw-alias-label-primary);cursor:pointer;text-align:center}.sm-panelDialog .sm-nativeDialogCancel{background:transparent}.sm-panelDialog .sm-nativeDialogConfirm,.sm-annotationDialog .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-panelDialog .sm-nativeDialogConfirm:hover:not(:disabled),.sm-annotationDialog .sm-nativeDialogConfirm:hover:not(:disabled){filter:brightness(.94)}.sm-nativeDialogDanger{background:var(--dsw-alias-state-error-primary,#d92d20);color:var(--dsw-alias-label-primary-foreground,#fff);border-color:var(--dsw-alias-state-error-primary,#d92d20)}.sm-nativeDialogDanger:hover:not(:disabled){filter:brightness(.92)}.sm-confirmDialog .sm-nativeDialogDanger,.sm-migrateDialog .sm-nativeDialogDanger,.sm-bulkDialog .sm-nativeDialogDanger{background:var(--dsw-alias-state-error-primary,#d92d20);color:var(--dsw-alias-label-primary-foreground,#fff);border-color:var(--dsw-alias-state-error-primary,#d92d20)}.sm-confirmDialog .sm-nativeDialogDanger:hover:not(:disabled),.sm-migrateDialog .sm-nativeDialogDanger:hover:not(:disabled),.sm-bulkDialog .sm-nativeDialogDanger:hover:not(:disabled){filter:brightness(.92)}.sm-panelDialog .sm-empty{padding:18px 0;font-size:12px;color:var(--dsw-alias-label-tertiary);text-align:center}.sm-panelDialog .sm-footError{font-size:11px;color:var(--dsw-alias-state-error-primary);margin-right:auto;line-height:20px}.sm-panelDialog .sm-moveLabel{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}.sm-migrateDialog .sm-nativeDialogFooter{flex-wrap:wrap;justify-content:space-between;gap:6px;margin-top:0}.sm-migrateDialog .sm-migrateChip{cursor:pointer;pointer-events:auto;position:relative;z-index:1}.sm-migrateDialog .sm-migrateChip[disabled]{opacity:.5;cursor:not-allowed}.sm-migrateDialog.sm-nativeDialogLayer{position:fixed;left:50vw;top:auto;bottom:48px;transform:translateX(-50%);width:min(560px,calc(100vw - 32px));max-height:none;display:block;align-items:initial;justify-content:initial;box-sizing:border-box;background:transparent;border:0;box-shadow:none;padding:0;inset:auto;z-index:10001}.sm-migrateDialog.sm-nativeDialogLayer .sm-nativeDialogBackdrop{display:none}.sm-migrateDialog.sm-nativeDialogLayer .sm-nativeDialog{position:relative;left:auto;top:auto;transform:none;width:360px;max-width:calc(100vw - 32px);max-height:min(70vh,520px);box-sizing:border-box;background-color: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,.45);overflow:hidden;padding:0;display:flex;flex-direction:column;gap:0}.sm-migrateDialog{position:fixed;left:50vw;top:auto;bottom:48px;transform:translateX(-50%);width:min(560px,calc(100vw - 32px));max-height:min(70vh,520px);box-sizing:border-box;display:flex;flex-direction:column;gap:0;background-color: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,.45);overflow:hidden;z-index:10001}.sm-migrateDialog .sm-nativeDialogHeader{padding:12px 18px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee);flex:none;position:relative;display:flex;align-items:center;justify-content:center;min-height:44px;box-sizing:border-box}.sm-migrateDialog .sm-nativeDialogTitle{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin:0;font-size:14px;line-height:20px;font-weight:600;color:var(--dsw-alias-label-primary,#111);text-align:center;width:auto;max-width:calc(100% - 36px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sm-migrateDialog .sm-migratePanel{padding:12px 18px;display:flex;flex-direction:column;gap:14px}.sm-migrateDialog .sm-nativeDialogBody{padding:0}.sm-migrateDialog .sm-nativeDialogFooter{padding:10px 16px;border-top:1px solid var(--dsw-alias-border-l2,#eee);flex:none;flex-wrap:wrap;justify-content:space-between;gap:6px;margin-top:0;gap:8px}.sm-migrateDialog .sm-migrateRow>label{color:var(--dsw-alias-label-secondary,#444)}.sm-migrateDialog .sm-migrateChip{padding:6px 12px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2,#ddd);background-color:var(--dsw-alias-markdown-tag,#f1f3f5);color:var(--dsw-alias-label-primary,#111);font-size:13px;line-height:18px;cursor:pointer;pointer-events:auto;position:relative;z-index:1;white-space:nowrap}.sm-migrateDialog .sm-migrateChip:hover:not(:disabled){background-color:var(--dsw-alias-interactive-bg-hover,#e8e8e8)}.sm-migrateDialog .sm-migrateChipOn{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)}.sm-migrateDialog .sm-migrateChip.sm-migrateChipOn: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-migrateDialog .sm-migrateChipOn:hover:not(:disabled){filter:brightness(.92)}.sm-migrateDialog .sm-migrateChip[disabled]{opacity:.5;cursor:not-allowed}.sm-migrateDialog .sm-migrateEmpty{color:var(--dsw-alias-label-tertiary,#888)}.sm-migrateDialog .sm-migratePreview{padding:10px 12px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;background-color:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#111);font-size:13px;line-height:18px}.sm-migrateDialog .sm-migrateWarn{padding:10px 12px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-warn-primary,#f59e0b) 30%,transparent);border-radius:8px;background-color:var(--dsw-alias-state-warn-tertiary,#fef5e7);color:var(--dsw-alias-label-secondary,#444);font-size:13px;line-height:18px}.sm-migrateDialog .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-color:transparent;color:var(--dsw-alias-label-primary,#111);text-align:center}.sm-migrateDialog .sm-nativeDialogButton:hover:not(:disabled){background-color:var(--dsw-alias-interactive-bg-hover,#e8e8e8)}.sm-migrateDialog .sm-nativeDialogConfirm{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)}.sm-migrateDialog .sm-nativeDialogConfirm:hover:not(:disabled){filter:brightness(.94)}",
723
+ ".sm-panelDialog{position:fixed;inset:0;margin:auto;transform:none;width:680px;max-width:calc(100vw - 32px);height:560px;max-height:calc(100vh - 32px);box-sizing:border-box;display:flex;flex-direction:column;gap:0;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;z-index:1020}.sm-panelDialog .sm-nativeDialogHeader{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px 16px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee);flex:none;position:relative;min-height:46px;box-sizing:border-box}.sm-panelDialog .sm-nativeDialogTitle{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin:0;font-size:14px;line-height:20px;font-weight:600;min-width:0;max-width:calc(100% - 140px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:center;pointer-events:auto;z-index:0}.sm-panelDialog .sm-nativeDialogHeaderActions{display:flex;align-items:center;gap:6px;flex:none;position:relative;z-index:1;margin-left:auto}.sm-nativeDialogFooter,.sm-bulkDialog .sm-nativeDialogFooter,.sm-confirmDialog .sm-nativeDialogFooter,.sm-migrateDialog .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:6px;margin-top:0}.sm-panelDialog .sm-panel{display:flex;flex-direction:column;gap:6px;min-height:0;flex:1 1 auto;overflow:auto;padding:8px 12px}.sm-panelDialog .sm-list{display:flex;flex-direction:column;gap:2px;flex:1 1 auto;min-height:0;overflow:auto;scrollbar-gutter:stable}.sm-panelDialog .sm-row{display:flex;align-items:center;gap:8px;padding:6px 10px;min-height:36px;border:1px solid transparent;border-radius:8px;background:transparent;flex-shrink:0;transition:background .12s ease,border-color .12s ease}.sm-panelDialog .sm-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.sm-panelDialog .sm-rowCurrent{background:var(--dsw-alias-bg-module-platform);border-color:var(--dsw-alias-state-business-primary,#356ae6)}.sm-panelDialog .sm-rowMain{display:flex;align-items:stretch!important;gap:8px;min-width:0;flex:1 1 auto;overflow:visible;text-align:left!important}.sm-panelDialog .sm-rowTitle{min-width:0;flex:1 1 0%;width:100%;align-self:stretch;display:flex;align-items:center;justify-content:flex-start;gap:6px;font-size:12px;line-height:16px;overflow:hidden;text-align:left!important;direction:ltr!important;unicode-bidi:plaintext}.sm-confirmDialog{z-index:10000}.sm-confirmDialog.sm-nativeDialogLayer{position:fixed;left:calc(50vw + 184px);top:calc(50vh - 90px);width:360px;max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);display:block;align-items:initial;justify-content:initial;box-sizing:border-box;background:transparent;border:0;box-shadow:none;padding:0;inset:calc(50vh - 90px) auto auto calc(50vw + 308px)}.sm-confirmDialog.sm-nativeDialogLayer .sm-nativeDialogBackdrop{display:none}.sm-confirmDialog.sm-nativeDialogLayer .sm-nativeDialog{position:relative;left:auto;top:auto;transform:none;width:auto;max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);display:flex;flex-direction:column;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);padding:0;overflow:hidden;animation:sm-confirmPop .14s ease-out}.sm-confirmDialog .sm-nativeDialogHeader{position:relative;padding:12px 16px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee);display:flex;align-items:center;justify-content:center;min-height:44px;box-sizing:border-box}.sm-confirmDialog .sm-nativeDialogTitle{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin:0;font-size:14px;line-height:20px;font-weight:600;text-align:center;width:auto;max-width:calc(100% - 32px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sm-confirmDialog .sm-nativeDialogBody{padding:10px 16px;display:flex;flex-direction:column;gap:8px;min-height:0;max-height:calc(100vh - 120px);overflow-y:auto}.sm-confirmDialog .sm-nativeDialogDescription{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);white-space:pre-wrap}.sm-confirmDialog .sm-warn{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;margin-top:0}.sm-confirmDialog .sm-nativeDialogFooter{padding:10px 16px;border-top:1px solid var(--dsw-alias-border-l2,#eee);flex-wrap:wrap;justify-content:space-between;gap:6px;margin-top:0}.sm-confirmDialog .sm-nativeDialogButton{min-height:28px;padding:3px 14px;font-size:12px;line-height:20px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;cursor:pointer;background:transparent;color:var(--dsw-alias-label-primary);text-align:center}.sm-confirmDialog .sm-nativeDialogCancel:hover:not(:disabled),.sm-migrateDialog .sm-nativeDialogCancel:hover:not(:disabled),.sm-updateDialog .sm-nativeDialogCancel:hover:not(:disabled),.sm-bulkDialog .sm-nativeDialogCancel:hover:not(:disabled),.sm-panelDialog .sm-nativeDialogCancel:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.sm-confirmDialog .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-confirmDialog .sm-nativeDialogConfirm:hover:not(:disabled){filter:brightness(.94)}@keyframes sm-confirmPop{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.sm-confirmDialog.sm-nativeDialog{padding:0;width:auto;max-width:none}.sm-panelDialog .sm-rowTitle>span:first-child{display:block;min-width:0;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;flex:1 1 auto;width:auto;text-align:left!important;direction:ltr!important;unicode-bidi:plaintext}.sm-panelDialog .sm-rowMeta{display:none}.sm-panelDialog .sm-rowActions{display:flex;gap:2px;flex:none;justify-content:flex-end}.sm-panelDialog .sm-rowActions .sm-rowBtn{min-height:22px;padding:0 7px;font-size:11px;line-height:18px;border-radius:6px;border:1px solid transparent;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap}.sm-rowBtn.sm-markIcon{padding:0 6px;font-size:13px;line-height:18px}.sm-panelDialog .sm-rowActions .sm-rowBtn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.sm-panelDialog .sm-rowActions .sm-rowBtnDanger{color:var(--dsw-alias-state-error-primary)}.sm-panelDialog .sm-rowActions .sm-rowBtnDanger:hover{background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary)}.sm-panelDialog .sm-filter{flex:none;padding:6px 12px 4px;gap:4px;display:flex;flex-wrap:wrap}.sm-panelDialog .sm-filterBtn{height:28px;min-height:28px;padding:3px 10px;font-size:12px;line-height:18px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;box-sizing:border-box;white-space:nowrap;flex-shrink:0;transition:all .15s ease}.sm-panelDialog .sm-filterBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.sm-panelDialog .sm-filterBtn:disabled{opacity:.4;cursor:not-allowed}.sm-panelDialog .sm-filterBtn.sm-on{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary)}.sm-panelDialog .sm-badge{font-size:10px;line-height:14px;padding:0 5px;border-radius:6px;flex:none}.sm-panelDialog .sm-error{padding:6px 10px;margin:0;font-size:12px;line-height:16px}.sm-panelDialog .sm-nativeDialogButton{min-height:28px;padding:3px 14px;font-size:12px;line-height:20px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:transparent;color:var(--dsw-alias-label-primary);cursor:pointer;text-align:center}.sm-panelDialog .sm-nativeDialogCancel{background:transparent}.sm-panelDialog .sm-nativeDialogConfirm,.sm-annotationDialog .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-panelDialog .sm-nativeDialogConfirm:hover:not(:disabled),.sm-annotationDialog .sm-nativeDialogConfirm:hover:not(:disabled){filter:brightness(.94)}.sm-nativeDialogDanger{background:var(--dsw-alias-state-error-primary,#d92d20);color:var(--dsw-alias-label-primary-foreground,#fff);border-color:var(--dsw-alias-state-error-primary,#d92d20)}.sm-nativeDialogDanger:hover:not(:disabled){filter:brightness(.92)}.sm-confirmDialog .sm-nativeDialogDanger,.sm-migrateDialog .sm-nativeDialogDanger,.sm-bulkDialog .sm-nativeDialogDanger{background:var(--dsw-alias-state-error-primary,#d92d20);color:var(--dsw-alias-label-primary-foreground,#fff);border-color:var(--dsw-alias-state-error-primary,#d92d20)}.sm-confirmDialog .sm-nativeDialogDanger:hover:not(:disabled),.sm-migrateDialog .sm-nativeDialogDanger:hover:not(:disabled),.sm-bulkDialog .sm-nativeDialogDanger:hover:not(:disabled){filter:brightness(.92)}.sm-panelDialog .sm-empty{padding:18px 0;font-size:12px;color:var(--dsw-alias-label-tertiary);text-align:center}.sm-panelDialog .sm-footError{font-size:11px;color:var(--dsw-alias-state-error-primary);margin-right:auto;line-height:20px}.sm-panelDialog .sm-moveLabel{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}.sm-migrateDialog .sm-nativeDialogFooter{flex-wrap:wrap;justify-content:space-between;gap:6px;margin-top:0}.sm-migrateDialog .sm-migrateChip{cursor:pointer;pointer-events:auto;position:relative;z-index:1}.sm-migrateDialog .sm-migrateChip[disabled]{opacity:.5;cursor:not-allowed}.sm-migrateDialog.sm-nativeDialogLayer{position:fixed;left:50vw;top:auto;bottom:48px;transform:translateX(-50%);width:min(560px,calc(100vw - 32px));max-height:none;display:block;align-items:initial;justify-content:initial;box-sizing:border-box;background:transparent;border:0;box-shadow:none;padding:0;inset:auto;z-index:10001}.sm-migrateDialog.sm-nativeDialogLayer .sm-nativeDialogBackdrop{display:none}.sm-migrateDialog.sm-nativeDialogLayer .sm-nativeDialog{position:relative;left:auto;top:auto;transform:none;width:360px;max-width:calc(100vw - 32px);max-height:min(70vh,520px);box-sizing:border-box;background-color: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,.45);overflow:hidden;padding:0;display:flex;flex-direction:column;gap:0}.sm-migrateDialog{position:fixed;left:50vw;top:auto;bottom:48px;transform:translateX(-50%);width:min(560px,calc(100vw - 32px));max-height:min(70vh,520px);box-sizing:border-box;display:flex;flex-direction:column;gap:0;background-color: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,.45);overflow:hidden;z-index:10001}.sm-migrateDialog .sm-nativeDialogHeader{padding:12px 18px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee);flex:none;position:relative;display:flex;align-items:center;justify-content:center;min-height:44px;box-sizing:border-box}.sm-migrateDialog .sm-nativeDialogTitle{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin:0;font-size:14px;line-height:20px;font-weight:600;color:var(--dsw-alias-label-primary,#111);text-align:center;width:auto;max-width:calc(100% - 36px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sm-migrateDialog .sm-migratePanel{padding:12px 18px;display:flex;flex-direction:column;gap:14px}.sm-migrateDialog .sm-nativeDialogBody{padding:0}.sm-migrateDialog .sm-nativeDialogFooter{padding:10px 16px;border-top:1px solid var(--dsw-alias-border-l2,#eee);flex:none;flex-wrap:wrap;justify-content:space-between;gap:6px;margin-top:0;gap:8px}.sm-migrateDialog .sm-migrateRow>label{color:var(--dsw-alias-label-secondary,#444)}.sm-migrateDialog .sm-migrateChip{padding:6px 12px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2,#ddd);background-color:var(--dsw-alias-markdown-tag,#f1f3f5);color:var(--dsw-alias-label-primary,#111);font-size:13px;line-height:18px;cursor:pointer;pointer-events:auto;position:relative;z-index:1;white-space:nowrap}.sm-migrateDialog .sm-migrateChip:hover:not(:disabled){background-color:var(--dsw-alias-interactive-bg-hover,#e8e8e8)}.sm-migrateDialog .sm-migrateChipOn{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)}.sm-migrateDialog .sm-migrateChip.sm-migrateChipOn: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-migrateDialog .sm-migrateChipOn:hover:not(:disabled){filter:brightness(.92)}.sm-migrateDialog .sm-migrateChip[disabled]{opacity:.5;cursor:not-allowed}.sm-migrateDialog .sm-migrateEmpty{color:var(--dsw-alias-label-tertiary,#888)}.sm-migrateDialog .sm-migratePreview{padding:10px 12px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;background-color:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#111);font-size:13px;line-height:18px}.sm-migrateDialog .sm-migrateWarn{padding:10px 12px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-warn-primary,#f59e0b) 30%,transparent);border-radius:8px;background-color:var(--dsw-alias-state-warn-tertiary,#fef5e7);color:var(--dsw-alias-label-secondary,#444);font-size:13px;line-height:18px}.sm-migrateDialog .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-color:transparent;color:var(--dsw-alias-label-primary,#111);text-align:center}.sm-migrateDialog .sm-nativeDialogButton:hover:not(:disabled){background-color:var(--dsw-alias-interactive-bg-hover,#e8e8e8)}.sm-migrateDialog .sm-nativeDialogConfirm{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)}.sm-migrateDialog .sm-nativeDialogConfirm:hover:not(:disabled){filter:brightness(.94)}",
632
724
  // The .sm-tooltip rule below deliberately keeps !important: it targets
633
725
  // DSH's Tooltip primitive (used through renderTip), which ships its own
634
726
  // inline styles, so a plugin rule cannot win on specificity alone
@@ -640,7 +732,7 @@ function isTopmostDialog(z) {
640
732
  // applied). Tooltip visibility is governed by the layering contract
641
733
  // above, not by this rule; it is kept as the scoped replacement for the
642
734
  // unscoped selector Issue #21 removed.
643
- ".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;padding:3px 8px;font-size:12px;line-height:20px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:6px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#111);cursor:pointer;outline:none}.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}",
644
736
 
645
737
  // Issue #23 — dialog opacity.
646
738
  //
@@ -682,14 +774,14 @@ function isTopmostDialog(z) {
682
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)}",
683
775
  ".sm-panelDialog option:disabled,.sm-nativeDialog option:disabled,.sm-settingsCard option:disabled,.sm-headerMenu option:disabled{color:var(--dsw-alias-label-tertiary,#888)}",
684
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)}",
685
- ".sm-settingsSlider{display:flex;align-items:center;gap:8px;flex:1 1 auto;justify-content:flex-end;min-width:0}",
686
- ".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)}",
687
779
  ".sm-settingsRange:disabled{cursor:not-allowed}",
688
- ".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)}",
689
- ".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}",
690
782
  ".sm-settingsResetBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary,#111)}",
691
783
  ".sm-settingsResetBtn:disabled{opacity:.5;cursor:not-allowed}",
692
- ".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}",
693
785
 
694
786
  // DSH's own theme is the single source of colour. Every rule above reads
695
787
  // --dsw-alias-* tokens directly and none of them is redefined here: the
@@ -703,12 +795,18 @@ function isTopmostDialog(z) {
703
795
  "[data-sm-theme=dark] .sm-nativeDialog,[data-sm-theme=dark] .sm-panelDialog,[data-sm-theme=dark] .sm-migrateDialog,[data-sm-theme=dark] .sm-settingsCard{--sm-dialog-surface-fallback:#1f1f23}",
704
796
  ".sm-panelDialog{max-height:min(640px,calc(100vh - 32px))}.sm-panelDialog .sm-panel{min-width:0;overflow:hidden}",
705
797
  ".sm-panelTools{display:flex;flex-direction:column;gap:8px;flex:none;padding:2px 0 6px}",
706
- ".sm-queryInput,.sm-viewSelect{box-sizing:border-box;width:100%;min-width:0;height:32px;padding:5px 9px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:7px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#111);font:inherit;font-size:12px;line-height:20px}",
707
- ".sm-queryInput::placeholder{color:var(--dsw-alias-label-tertiary,#888)}.sm-queryInput:focus-visible,.sm-viewSelect:focus-visible,.sm-resetFilters:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#356ae6);outline-offset:2px}.sm-viewSelect:disabled{opacity:.6;cursor:wait}",
708
- ".sm-panelSelects{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.sm-panelSelects .sm-viewField>span{display:none}.sm-panelSelects .sm-viewField .sm-viewSelect{font-size:11px;padding:4px 7px;height:28px;line-height:18px}.sm-viewField{display:flex;flex-direction:column;gap:3px;min-width:0;color:var(--dsw-alias-label-secondary,#666);font-size:11px;line-height:16px}",
709
- ".sm-filterBar{display:flex;align-items:center;justify-content:space-between;gap:6px;flex-wrap:wrap}.sm-resultSummary{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:11px;color:var(--dsw-alias-label-tertiary,#888)}.sm-resetFilters{padding:2px 0;border:0;background:none;color:var(--dsw-alias-state-business-primary,#356ae6);font:inherit;cursor:pointer}.sm-resetFilters:hover{text-decoration:underline}.sm-workspaceNotice{display:flex;gap:8px;align-items:center;font-size:12px;color:var(--dsw-alias-label-secondary,#666)}",
798
+ ".sm-queryInput,.sm-viewSelect{box-sizing:border-box;width:100%;min-width:0;height:32px;padding:5px 9px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#111);font:inherit;font-size:12px;line-height:20px;transition:border-color .15s ease,box-shadow .15s ease}",
799
+ ".sm-viewSelect,.sm-settingsSelect,.sm-wsSortSelect,.sm-prioritySelectInline,.sm-panelDialog select,.sm-nativeDialog select,.sm-settingsCard select{box-sizing:border-box!important;appearance:none;-webkit-appearance:none;-moz-appearance:none;border:1px solid var(--dsw-alias-border-l2,#ddd)!important;border-radius:8px!important;background-color:var(--dsw-alias-bg-layer-2,#fff)!important;color:var(--dsw-alias-label-primary,#111)!important;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%23888888' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E\")!important;background-repeat:no-repeat!important;background-position:right 8px center!important;background-size:10px 10px!important;padding-right:26px!important;cursor:pointer!important;outline:none!important;font:inherit;transition:border-color .15s ease,box-shadow .15s ease!important}",
800
+ ".sm-viewSelect:hover:not(:disabled),.sm-settingsSelect:hover:not(:disabled),.sm-wsSortSelect:hover:not(:disabled),.sm-prioritySelectInline:hover:not(:disabled),.sm-panelDialog select:hover:not(:disabled),.sm-nativeDialog select:hover:not(:disabled),.sm-settingsCard select:hover:not(:disabled){border-color:var(--dsw-alias-border-l1,#aaa)!important}",
801
+ ".sm-queryInput::placeholder{color:var(--dsw-alias-label-tertiary,#888)}.sm-queryInput:focus-visible,.sm-viewSelect:focus-visible,.sm-settingsSelect:focus-visible,.sm-wsSortSelect:focus-visible,.sm-prioritySelectInline:focus-visible,.sm-panelDialog select:focus-visible,.sm-nativeDialog select:focus-visible,.sm-settingsCard select:focus-visible,.sm-resetFilters:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#356ae6)!important;outline-offset:1px!important}.sm-viewSelect:disabled,.sm-settingsSelect:disabled,.sm-wsSortSelect:disabled,.sm-prioritySelectInline:disabled,.sm-panelDialog select:disabled,.sm-nativeDialog select:disabled,.sm-settingsCard select:disabled{opacity:.5!important;cursor:not-allowed!important}",
802
+ ".sm-selectExpanded{border-color:var(--dsw-alias-state-business-primary,#356ae6)!important;box-shadow:0 0 0 1px var(--dsw-alias-state-business-primary,#356ae6)!important;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%23356ae6' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m18 15-6-6-6 6'/%3E%3C/svg%3E\")!important}",
803
+ ".sm-customSelectMenu{position:fixed;z-index:100030;box-sizing:border-box;min-width:120px;max-height:260px;overflow-y:auto;overflow-x:hidden;padding:4px;background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-bg-layer-2,#fff));border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;box-shadow:none!important;font-family:inherit;font-size:12px;line-height:18px;color:var(--dsw-alias-label-primary,#111);user-select:none;scrollbar-width:thin;scrollbar-color:var(--dsw-alias-scrollbar-bg-l1,var(--dsw-alias-border-l1,rgba(128,128,128,.3))) transparent}.sm-customSelectMenu-dropUp{box-shadow:none!important}.sm-customSelectOption{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:6px 10px;border-radius:6px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-primary,#111);cursor:pointer;white-space:nowrap;user-select:none;transition:background-color .1s ease,color .1s ease}.sm-customSelectOption:hover:not(.sm-customSelectOptionDisabled){background-color:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.05))}.sm-customSelectOptionSelected{background-color:var(--dsw-alias-interactive-bg-active,rgba(53,106,230,.08));color:var(--dsw-alias-state-business-primary,#356ae6);font-weight:500}.sm-customSelectOptionDisabled{opacity:.45;cursor:not-allowed;color:var(--dsw-alias-label-tertiary,#888)!important}.sm-customSelectOptionLabel{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1 1 auto}.sm-customSelectOptionCheck{font-size:11px;font-weight:bold;color:var(--dsw-alias-state-business-primary,#356ae6);margin-left:6px;flex-shrink:0}",
804
+ ".sm-wsSortSelect,.sm-prioritySelectInline,.sm-panelSelects .sm-viewSelect{pointer-events:none!important}",
805
+ ".sm-wsSortField,.sm-annotationPriority,.sm-viewField,.sm-moveSection,.sm-settingsRow{cursor:pointer}",
806
+ ".sm-panelSelects{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.sm-panelSelects .sm-viewField>span{display:none}.sm-panelSelects .sm-viewField .sm-viewSelect{font-size:11px;padding:4px 24px 4px 8px;height:28px;line-height:18px;background-position:right 6px center}.sm-viewField{display:flex;flex-direction:column;gap:3px;min-width:0;color:var(--dsw-alias-label-secondary,#666);font-size:11px;line-height:16px}.sm-wsSortField{display:inline-flex;margin:0;vertical-align:middle}.sm-wsSortSelect{height:28px;font-size:11px;padding:3px 24px 3px 8px;width:auto;min-width:120px;border-radius:8px;line-height:18px;background-position:right 6px center;cursor:pointer}",
807
+ ".sm-filterBar{display:flex;align-items:center;justify-content:space-between;gap:6px;flex-wrap:wrap}.sm-resultSummary{display:flex;align-items:center;gap:8px;flex-wrap:nowrap;height:20px;min-height:20px;max-height:20px;line-height:20px;font-size:11px;color:var(--dsw-alias-label-tertiary,#888);box-sizing:border-box}.sm-resetFilters{padding:0 4px;height:20px;line-height:20px;border:0;background:none;color:var(--dsw-alias-state-business-primary,#356ae6);font:inherit;font-size:11px;cursor:pointer;display:inline-flex;align-items:center;white-space:nowrap;border-radius:4px;box-sizing:border-box}.sm-resetFilters:hover{text-decoration:underline}.sm-workspaceNotice{display:flex;gap:8px;align-items:center;font-size:12px;color:var(--dsw-alias-label-secondary,#666)}",
710
808
  "@media(max-width:540px){.sm-panelSelects{grid-template-columns:minmax(0,1fr)}.sm-panelDialog .sm-row{height:auto;min-height:36px;flex-shrink:0;flex-wrap:wrap;padding:6px 8px}.sm-panelDialog .sm-rowMain{flex-basis:100%}.sm-panelDialog .sm-rowActions{width:100%;min-width:0;flex:1 1 100%;justify-content:flex-start}}",
711
- ".sm-panelDialog:not(.sm-annotationSurface){width:680px}.sm-panelDialog .sm-row{flex-shrink:0;padding:5px 8px}.sm-panelDialog .sm-rowMain{flex:1 1 200px;flex-direction:column;gap:4px}.sm-panelDialog .sm-rowActions{max-width:100%}.sm-rowSecondary{display:flex;flex-wrap:nowrap;align-items:center;gap:6px;min-width:0;line-height:18px}.sm-rowBadges{display:flex;align-items:center;gap:5px;min-width:0;flex:1 1 auto;flex-wrap:wrap;font-size:11px;line-height:18px}.sm-rowMeta{color:var(--dsw-alias-label-tertiary,#888);font-size:11px;line-height:18px;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;flex:none}.sm-rowAnnotations{display:flex;align-items:center;gap:5px;min-width:0;font-size:10px;line-height:16px}",
809
+ ".sm-panelDialog:not(.sm-annotationSurface){position:fixed;inset:0;margin:auto;transform:none;width:680px;height:560px;max-height:calc(100vh - 32px)}.sm-panelDialog .sm-row{flex-shrink:0;padding:5px 8px}.sm-panelDialog .sm-rowMain{flex:1 1 200px;flex-direction:column;gap:4px}.sm-panelDialog .sm-rowActions{max-width:100%}.sm-rowSecondary{display:flex;flex-wrap:nowrap;align-items:center;gap:6px;min-width:0;line-height:18px}.sm-rowBadges{display:flex;align-items:center;gap:5px;min-width:0;flex:1 1 auto;flex-wrap:wrap;font-size:11px;line-height:18px}.sm-rowMeta{color:var(--dsw-alias-label-tertiary,#888);font-size:11px;line-height:18px;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;flex:none}.sm-rowAnnotations{display:flex;align-items:center;gap:5px;min-width:0;font-size:10px;line-height:16px}",
712
810
  // .sm-markIcon.sm-markOn keeps !important because the generic
713
811
  // `.sm-panelDialog .sm-rowActions .sm-rowBtn` colour rule (3 classes)
714
812
  // outranks this 2-class selector; it overrides the plugin's own rule,
@@ -730,7 +828,7 @@ function isTopmostDialog(z) {
730
828
  // 1000..1100 gap -- the panel at 1020 and this overlay root at 1050 --
731
829
  // and everything portaled into this root is relative to its context.
732
830
  "#dsh-session-manager-overlay-root{position:fixed!important;inset:0!important;width:100%!important;height:100%!important;z-index:1050!important;pointer-events:none!important;overflow:visible!important}.sm-annotationDialog.sm-nativeDialogLayer{position:fixed;display:block;pointer-events:none;z-index:100002;background:transparent}.sm-annotationDialog .sm-nativeDialogBackdrop{display:none}.sm-annotationDialog.sm-annotationDialog-anchored.sm-nativeDialogLayer,.sm-confirmDialog.sm-confirmDialog-anchored.sm-nativeDialogLayer,.sm-bulkDialog.sm-bulkDialog-anchored.sm-nativeDialogLayer{inset:auto;background:transparent}.sm-annotationDialog.sm-confirmDialog.sm-nativeDialogLayer:not(.sm-annotationDialog-anchored){inset:50% auto auto calc(50vw + 308px);transform:translateY(-50%);max-height:calc(100vh - 32px)}.sm-panelDialog.sm-annotationSurface{position:absolute;width:360px;max-height:min(680px,calc(100vh - 32px));pointer-events:auto;z-index:100003;box-shadow:0 16px 48px rgba(0,0,0,.25)}.sm-panelDialog.sm-annotationSurface.sm-annotationSurface-anchored{position:relative;transform:none;left:auto;top:auto;margin:0}.sm-annotationCloseHint{margin:0 16px 8px;font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary)}",
733
- ".sm-annotationBody{padding:14px 16px;display:flex;flex-direction:column;gap:12px;min-height:0;overflow:auto;flex:1 1 auto}.sm-annotationTarget{font-size:12px;line-height:18px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-secondary,#666)}.sm-annotationChecks{display:grid;grid-template-columns:auto 1fr;column-gap:14px;row-gap:4px;align-items:center;font-size:13px}.sm-annotationChecks>label{display:flex;gap:6px;align-items:center;cursor:pointer}.sm-annotationChecks .sm-annotationPriority{display:inline-flex;align-items:center;gap:6px;font-size:13px;line-height:18px;color:inherit}.sm-annotationChecks .sm-prioritySelectInline{height:24px;padding:2px 6px;font-size:12px;line-height:18px;min-width:104px;width:auto;border-radius:6px;color:var(--dsw-alias-label-primary,#111)}.sm-annotationChecks>small{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#888);justify-self:start}.sm-annotationHint,.sm-annotationBody small{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#888)}",
831
+ ".sm-annotationBody{padding:14px 16px;display:flex;flex-direction:column;gap:12px;min-height:0;overflow:auto;flex:1 1 auto}.sm-annotationTarget{font-size:12px;line-height:18px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-secondary,#666)}.sm-annotationChecks{display:grid;grid-template-columns:auto 1fr;column-gap:14px;row-gap:4px;align-items:center;font-size:13px}.sm-annotationChecks>label{display:flex;gap:6px;align-items:center;cursor:pointer}.sm-annotationChecks .sm-annotationPriority{display:inline-flex;align-items:center;gap:6px;font-size:13px;line-height:18px;color:inherit}.sm-annotationChecks .sm-prioritySelectInline{height:26px;padding:2px 24px 2px 8px;font-size:12px;line-height:18px;min-width:104px;width:auto;border-radius:8px!important;background-position:right 6px center!important;color:var(--dsw-alias-label-primary,#111)}.sm-annotationChecks>small{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#888);justify-self:start}.sm-annotationHint,.sm-annotationBody small{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary,#888)}",
734
832
  ".sm-noteInput{box-sizing:border-box;width:100%;min-height:60px;resize:vertical;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:7px;padding:8px;background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#111);font:inherit;font-size:13px;line-height:19px}.sm-noteInput:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#356ae6);outline-offset:2px}.sm-markConflict{display:flex;flex-direction:column;gap:5px;font-size:12px;padding:8px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:6px}.sm-fieldWithClear .sm-fieldRow{position:relative;display:flex;align-items:flex-start;gap:4px}.sm-fieldRow .sm-queryInput{flex:1 1 auto;min-width:0}.sm-fieldRowNote{align-items:stretch}.sm-fieldClear{position:absolute;top:5px;right:6px;width:22px;height:22px;border:0;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer;border-radius:4px;font-size:14px;line-height:18px;display:inline-flex;align-items:center;justify-content:center;padding:0}.sm-fieldClear:hover,.sm-fieldClear:focus-visible{color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-interactive-bg-hover);outline:none}.sm-fieldClearNote{top:6px}.sm-importGroup{display:flex;flex-direction:column;gap:8px;padding:12px 16px;border-top:1px solid var(--dsw-alias-border-l2,#eee)}.sm-importGroupHeader{display:flex;align-items:center;justify-content:space-between;gap:8px;min-height:20px}.sm-importLabel{color:var(--dsw-alias-label-secondary,#666);font-size:11px;line-height:16px}.sm-importGroupButtons{display:flex;align-items:center;gap:6px;flex:none}.sm-importBtn{cursor:pointer;color:var(--dsw-alias-label-primary,#111);background:transparent;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;min-height:28px;padding:3px 14px;font-size:12px;line-height:20px;text-align:center}.sm-importBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.sm-importBtn:disabled{opacity:.5;cursor:not-allowed}.sm-importStatus{margin:0;padding:8px 10px;font-size:12px;line-height:18px;background:var(--dsw-alias-state-business-tertiary,rgba(53,106,230,.08));color:var(--dsw-alias-label-secondary);border-radius:6px;white-space:pre-wrap;word-break:break-word}",
735
833
  ".sm-bulkBar{box-sizing:border-box;display:grid;grid-template-columns:repeat(6,minmax(0,1fr));align-items:stretch;gap:6px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff)}.sm-bulkBar .sm-bulkSummary{grid-column:1/-1;color:var(--dsw-alias-label-primary,#111);font-size:12px;line-height:18px;font-weight:500;margin:0 0 2px}",
736
834
  ".sm-bulkSummary{color:var(--dsw-alias-label-primary,#111);font-size:12px;line-height:18px;font-weight:500}",
@@ -746,11 +844,11 @@ function isTopmostDialog(z) {
746
844
  ".sm-bulkRowCheckbox input{width:14px;height:14px;cursor:pointer;margin:0}",
747
845
  ".sm-bulkRowCheckbox input:disabled{cursor:not-allowed}",
748
846
  ".sm-rowBulkSelected{border-color:var(--dsw-alias-state-business-primary,#356ae6);background:var(--dsw-alias-state-business-tertiary,rgba(53,106,230,.06))}",
749
- ".sm-bulkDialog.sm-nativeDialogLayer{position:fixed;left:calc(50vw + 184px);top:calc(50vh - 90px);width:360px;max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);display:block;align-items:initial;justify-content:initial;box-sizing:border-box;background:transparent;border:0;box-shadow:none;padding:0;inset:calc(50vh - 90px) auto auto calc(50vw + 308px);z-index:10002}.sm-bulkDialog.sm-nativeDialogLayer .sm-nativeDialogBackdrop{display:none}.sm-bulkDialog.sm-nativeDialogLayer .sm-nativeDialog{position:relative;left:auto;top:auto;transform:none;width:auto;max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);display:flex;flex-direction:column;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);padding:0;overflow:hidden;animation:sm-confirmPop .14s ease-out}.sm-bulkDialog .sm-nativeDialogHeader{padding:10px 16px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee);flex:none;position:relative;display:flex;align-items:center;justify-content:center;min-height:44px;box-sizing:border-box}.sm-bulkDialog .sm-nativeDialogTitle{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin:0;font-size:14px;line-height:20px;font-weight:600;text-align:center;width:auto;max-width:calc(100% - 32px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sm-bulkDialog .sm-nativeDialogBody{padding:12px 16px;display:flex;flex-direction:column;gap:10px;font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary,#111);min-height:0;max-height:calc(100vh - 120px);overflow-y:auto}.sm-bulkDialog .sm-nativeDialogFooter{padding:10px 16px;display:flex;flex-wrap:wrap;justify-content:space-between;gap:6px}.sm-bulkDialog .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-bulkDialog .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-bulkDialog .sm-nativeDialogConfirm:hover:not(:disabled){filter:brightness(.94)}.sm-bulkDialog .sm-nativeDialogDanger{background:var(--dsw-alias-state-error-primary,#d92d20);color:var(--dsw-alias-label-primary-foreground,#fff);border-color:var(--dsw-alias-state-error-primary,#d92d20)}.sm-bulkDialog .sm-nativeDialogDanger:hover:not(:disabled){filter:brightness(.92)}.sm-bulkDialog .sm-nativeDialog select.sm-viewSelect{font-size:13px;line-height:20px;height:32px;padding:5px 9px}",
847
+ ".sm-bulkDialog.sm-nativeDialogLayer{position:fixed;left:calc(50vw + 184px);top:calc(50vh - 90px);width:360px;max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);display:block;align-items:initial;justify-content:initial;box-sizing:border-box;background:transparent;border:0;box-shadow:none;padding:0;inset:calc(50vh - 90px) auto auto calc(50vw + 308px);z-index:10002}.sm-bulkDialog.sm-nativeDialogLayer .sm-nativeDialogBackdrop{display:none}.sm-bulkDialog.sm-nativeDialogLayer .sm-nativeDialog{position:relative;left:auto;top:auto;transform:none;width:auto;max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);display:flex;flex-direction:column;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);padding:0;overflow:hidden;animation:sm-confirmPop .14s ease-out}.sm-bulkDialog .sm-nativeDialogHeader{padding:10px 16px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee);flex:none;position:relative;display:flex;align-items:center;justify-content:center;min-height:44px;box-sizing:border-box}.sm-bulkDialog .sm-nativeDialogTitle{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin:0;font-size:14px;line-height:20px;font-weight:600;text-align:center;width:auto;max-width:calc(100% - 32px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sm-bulkDialog .sm-nativeDialogBody{padding:12px 16px;display:flex;flex-direction:column;gap:10px;font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary,#111);min-height:0;max-height:calc(100vh - 120px);overflow-y:auto}.sm-bulkDialog .sm-nativeDialogFooter{padding:10px 16px;display:flex;flex-wrap:wrap;justify-content:space-between;gap:6px}.sm-bulkDialog .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-bulkDialog .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-bulkDialog .sm-nativeDialogConfirm:hover:not(:disabled){filter:brightness(.94)}.sm-bulkDialog .sm-nativeDialogDanger{background:var(--dsw-alias-state-error-primary,#d92d20);color:var(--dsw-alias-label-primary-foreground,#fff);border-color:var(--dsw-alias-state-error-primary,#d92d20)}.sm-bulkDialog .sm-nativeDialogDanger:hover:not(:disabled){filter:brightness(.92)}.sm-bulkDialog .sm-nativeDialog select.sm-viewSelect{font-size:13px;line-height:20px;height:32px;padding:5px 26px 5px 10px}",
750
848
  ".sm-bulkDialog .sm-nativeDialog{width:100%;max-height:min(640px,calc(100vh - 32px))}",
751
849
  ".sm-bulkDialogBody{display:flex;flex-direction:column;gap:12px;min-height:0}",
752
850
  ".sm-bulkDialogSummary{font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary,#111)}",
753
- ".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}",
754
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}",
755
853
  ".sm-bulkSessionItem{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#666);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
756
854
  ".sm-bulkProgressBar{position:relative;height:8px;background:var(--dsw-alias-bg-skeleton,#0000000a);border-radius:4px;overflow:hidden}",
@@ -772,6 +870,28 @@ function isTopmostDialog(z) {
772
870
  ".sm-overlayLayer{position:fixed;inset:0;pointer-events:none}",
773
871
  ".sm-overlayLayer>*{pointer-events:auto}",
774
872
  ".sm-overlayBackdrop{position:fixed;inset:0;background:transparent;pointer-events:auto}",
873
+ ".sm-panelNav{display:inline-flex;align-items:center;background:var(--dsw-alias-bg-layer-2,rgba(128,128,128,0.12));border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;padding:2px;gap:2px;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);z-index:0}",
874
+ ".sm-panelNavTab{border:0;background:transparent;color:var(--dsw-alias-label-secondary,#666);font-size:13px;line-height:20px;font-weight:500;padding:2px 14px;border-radius:6px;cursor:pointer;transition:all .15s ease;white-space:nowrap;outline:none}",
875
+ ".sm-panelNavTab:hover:not(:disabled){color:var(--dsw-alias-label-primary,#111)}",
876
+ ".sm-panelNavTab.sm-on{background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#111);box-shadow:0 1px 3px rgba(0,0,0,0.12),0 1px 2px rgba(0,0,0,0.06);font-weight:600}",
877
+ ".sm-switch{position:relative;width:38px;height:22px;flex:0 0 38px;min-width:38px;min-height:22px;max-width:38px;max-height:22px;padding:0;border-radius:11px;border:1px solid var(--dsw-alias-border-l2,#ddd);background:var(--dsw-alias-bg-layer-2,rgba(128,128,128,0.25));cursor:pointer;transition:background .2s cubic-bezier(0.4,0,0.2,1),border-color .2s cubic-bezier(0.4,0,0.2,1);box-sizing:border-box;display:inline-flex;align-items:center;overflow:hidden}",
878
+ ".sm-switch:hover:not(:disabled){border-color:var(--dsw-alias-border-l1,#bbb)}",
879
+ ".sm-switch.sm-switchChecked{background:var(--dsw-alias-state-business-primary,#356ae6);border-color:var(--dsw-alias-state-business-primary,#356ae6)}",
880
+ ".sm-switch:disabled{opacity:0.4;cursor:not-allowed}",
881
+ ".sm-switchThumb{position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:var(--dsw-alias-switch-thumb,#ffffff);transition:left 0.2s cubic-bezier(0.4,0,0.2,1);box-shadow:0 1px 3px rgba(0,0,0,0.22);display:flex;align-items:center;justify-content:center}",
882
+ ".sm-switchChecked .sm-switchThumb{left:18px}",
883
+ ".sm-switchSpinner{width:10px;height:10px;border:1.5px solid transparent;border-top-color:var(--dsw-alias-state-business-primary,#356ae6);border-radius:50%;animation:sm-spin 0.7s linear infinite}",
884
+ ".sm-rowArchived{opacity:0.72}",
885
+ ".sm-workspaceRowActions{display:flex;align-items:center;justify-content:flex-end;flex:0 0 44px;min-width:44px;max-width:44px}",
886
+ ".sm-workspaceRow .sm-rowMeta{display:block!important}",
887
+ ".sm-wsFilters{display:flex;flex-wrap:wrap;align-items:center;gap:6px;width:100%;min-width:0;box-sizing:border-box}",
888
+ ".sm-wsFilters .sm-markFiltersGroup{display:flex;flex-wrap:wrap;align-items:center;gap:6px}",
889
+ ".sm-wsFilters .sm-markFiltersSpacer{flex:1 1 auto;min-width:8px}",
890
+ ".sm-wsFilters .sm-wsSortSelect{height:28px;min-height:28px;font-size:11px;padding:3px 24px 3px 8px;line-height:18px;width:auto;min-width:120px;border-radius:8px;box-sizing:border-box;background-position:right 6px center;cursor:pointer}",
891
+ ".sm-wsSortSelect-en{min-width:138px}",
892
+ ".sm-wsFiltersBulk{display:flex;flex-wrap:wrap;align-items:center;gap:6px}",
893
+ "@media(max-width:720px){.sm-wsFilters .sm-markFiltersSpacer{display:none}.sm-wsFilters .sm-wsFiltersBulk{width:100%}}",
894
+ ".sm-visuallyHidden{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}",
775
895
  "@media(max-width:720px){.sm-panelSelects{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}.sm-panelDialog .sm-row{flex-wrap:wrap}.sm-panelDialog .sm-rowMain{flex-basis:100%}.sm-panelDialog .sm-rowActions{width:100%;min-width:0;flex:1 1 100%;justify-content:flex-start}}@media(max-width:540px){.sm-panelSelects{grid-template-columns:minmax(0,1fr)}}",
776
896
  ].join("");
777
897
 
@@ -1017,6 +1137,10 @@ function isTopmostDialog(z) {
1017
1137
  if (zRef.current === null) {
1018
1138
  zRef.current = nextDialogZ();
1019
1139
  }
1140
+ useEffect(() => {
1141
+ registerDialogZ(zRef.current);
1142
+ return () => unregisterDialogZ(zRef.current);
1143
+ }, []);
1020
1144
  const root = getOverlayRoot();
1021
1145
  const portal = getPortalFn();
1022
1146
 
@@ -1044,6 +1168,8 @@ function isTopmostDialog(z) {
1044
1168
  zIndex: -1
1045
1169
  },
1046
1170
  onClick: (e) => {
1171
+ if (e && typeof e.stopPropagation === "function") e.stopPropagation();
1172
+ if (e && typeof e.stopImmediatePropagation === "function") e.stopImmediatePropagation();
1047
1173
  if (e.target === e.currentTarget && onBackdropClick) {
1048
1174
  onBackdropClick(e);
1049
1175
  }
@@ -1368,6 +1494,885 @@ function isTopmostDialog(z) {
1368
1494
  return { sessionId, status: "success", via: "endpoint" };
1369
1495
  }
1370
1496
 
1497
+ const WORKSPACE_ARCHIVE_STORAGE_KEY = "dsh-session-manager-workspace-archive-v1";
1498
+ const WORKSPACE_ARCHIVE_LEGACY_KEY = "dsh-workspace-hide.hidden.v2";
1499
+ const WORKSPACE_MODEL_MARK = "__dshSmWorkspaceModel";
1500
+ const WORKSPACE_CREATE_MARK = "__dshSmWorkspaceCreate";
1501
+ const WORKSPACE_ORIGINAL_CREATE = "__dshSmWorkspaceOrigCreate";
1502
+ const UI_CONNECT_MARK = "__dshSmUiConnect";
1503
+ const UI_ORIGINAL_CONNECT = "__dshSmUiOrigConnect";
1504
+
1505
+ const pathKey = (value) => {
1506
+ if (typeof value !== "string" || value === "") return "";
1507
+ const path = value.replace(/\\/g, "/").replace(/\/+$/, "") || "/";
1508
+ return /^[a-z]:(?:\/|$)/i.test(path) || path.startsWith("//") ? path.toLowerCase() : path;
1509
+ };
1510
+
1511
+ const workspaceArchiveStore = (() => {
1512
+ const archives = new Map();
1513
+ const busyWorkspaces = new Set();
1514
+ const listeners = new Set();
1515
+ let version = 0;
1516
+ let readRaw = null;
1517
+ let viewCache = null;
1518
+ let viewRaw = null;
1519
+ let viewVersion = -1;
1520
+
1521
+ const isId = v => typeof v === "string" && v !== "";
1522
+
1523
+ function loadLocal() {
1524
+ try {
1525
+ if (typeof localStorage === "undefined") return;
1526
+ const text = localStorage.getItem(WORKSPACE_ARCHIVE_STORAGE_KEY);
1527
+ if (text) {
1528
+ const parsed = JSON.parse(text);
1529
+ if (parsed && typeof parsed.archives === "object" && !Array.isArray(parsed.archives)) {
1530
+ for (const [wsId, rec] of Object.entries(parsed.archives)) {
1531
+ if (!isId(wsId) || !rec) continue;
1532
+ archives.set(wsId, {
1533
+ workspaceId: wsId,
1534
+ archived: rec.archived === true,
1535
+ archivedAt: typeof rec.archivedAt === "number" ? rec.archivedAt : Date.now(),
1536
+ sessionIds: Array.isArray(rec.sessionIds) ? rec.sessionIds.filter(isId) : [],
1537
+ pending: Array.isArray(rec.pending) ? rec.pending.filter(isId) : []
1538
+ });
1539
+ }
1540
+ return;
1541
+ }
1542
+ }
1543
+ const legacy = localStorage.getItem(WORKSPACE_ARCHIVE_LEGACY_KEY);
1544
+ if (legacy) {
1545
+ const parsed = JSON.parse(legacy);
1546
+ if (parsed && typeof parsed.hidden === "object" && !Array.isArray(parsed.hidden)) {
1547
+ for (const [wsId, ids] of Object.entries(parsed.hidden)) {
1548
+ if (!isId(wsId)) continue;
1549
+ archives.set(wsId, {
1550
+ workspaceId: wsId,
1551
+ archived: true,
1552
+ archivedAt: Date.now(),
1553
+ sessionIds: Array.isArray(ids) ? ids.filter(isId) : [],
1554
+ pending: []
1555
+ });
1556
+ }
1557
+ saveLocal();
1558
+ }
1559
+ }
1560
+ } catch (_) {}
1561
+ }
1562
+
1563
+ function saveLocal() {
1564
+ try {
1565
+ if (typeof localStorage === "undefined") return;
1566
+ const obj = {};
1567
+ for (const [wsId, rec] of archives) {
1568
+ if (rec.archived === true || (Array.isArray(rec.pending) && rec.pending.length > 0) || (Array.isArray(rec.sessionIds) && rec.sessionIds.length > 0)) {
1569
+ obj[wsId] = {
1570
+ workspaceId: wsId,
1571
+ archived: rec.archived === true,
1572
+ archivedAt: rec.archivedAt,
1573
+ sessionIds: rec.sessionIds,
1574
+ pending: rec.pending
1575
+ };
1576
+ }
1577
+ }
1578
+ localStorage.setItem(WORKSPACE_ARCHIVE_STORAGE_KEY, JSON.stringify({ version: 1, archives: obj }));
1579
+ } catch (_) {}
1580
+ }
1581
+
1582
+ const EMPTY_WORKSPACES_SNAPSHOT = Object.freeze({ items: Object.freeze([]) });
1583
+ let snapshotCache = { archives, version, busyWorkspaces };
1584
+
1585
+ loadLocal();
1586
+
1587
+ function notify() {
1588
+ version += 1;
1589
+ snapshotCache = { archives, version, busyWorkspaces };
1590
+ viewCache = null;
1591
+ viewRaw = null;
1592
+ viewVersion = -1;
1593
+ for (const listener of [...listeners]) {
1594
+ try { listener(); } catch (_) {}
1595
+ }
1596
+ }
1597
+
1598
+ function rawSnapshot() {
1599
+ if (typeof readRaw === "function") {
1600
+ try { return readRaw() ?? EMPTY_WORKSPACES_SNAPSHOT; } catch (_) {}
1601
+ }
1602
+ return EMPTY_WORKSPACES_SNAPSHOT;
1603
+ }
1604
+
1605
+ function getViewSnapshot() {
1606
+ const raw = rawSnapshot();
1607
+ if (viewCache !== null && viewRaw === raw && viewVersion === version) {
1608
+ return viewCache;
1609
+ }
1610
+ viewRaw = raw;
1611
+ viewVersion = version;
1612
+
1613
+ const items = Array.isArray(raw?.items) ? raw.items : [];
1614
+ const baseArchived = Array.isArray(raw?.archivedSessionIds) ? raw.archivedSessionIds : [];
1615
+
1616
+ const hasArchived = [...archives.values()].some(r => r.archived === true);
1617
+ if (!hasArchived) {
1618
+ viewCache = raw;
1619
+ return viewCache;
1620
+ }
1621
+
1622
+ const visible = items.filter(item => {
1623
+ const rec = archives.get(item.workspaceId || item.id);
1624
+ return !rec || rec.archived !== true;
1625
+ });
1626
+
1627
+ const archivedSet = new Set(baseArchived);
1628
+ let changed = false;
1629
+
1630
+ for (const item of items) {
1631
+ const wsId = item.workspaceId || item.id;
1632
+ const rec = archives.get(wsId);
1633
+ if (rec && rec.archived === true) {
1634
+ const sids = Array.isArray(item.sessionIds) ? item.sessionIds : [];
1635
+ for (const sid of sids) {
1636
+ if (!archivedSet.has(sid)) {
1637
+ archivedSet.add(sid);
1638
+ changed = true;
1639
+ }
1640
+ }
1641
+ }
1642
+ }
1643
+ for (const rec of archives.values()) {
1644
+ if (rec.archived === true && Array.isArray(rec.sessionIds)) {
1645
+ for (const sid of rec.sessionIds) {
1646
+ if (!archivedSet.has(sid)) {
1647
+ archivedSet.add(sid);
1648
+ changed = true;
1649
+ }
1650
+ }
1651
+ }
1652
+ }
1653
+
1654
+ const finalArchived = changed ? [...archivedSet] : baseArchived;
1655
+ viewCache = (visible.length === items.length && finalArchived === baseArchived)
1656
+ ? raw
1657
+ : { ...raw, items: visible, archivedSessionIds: finalArchived };
1658
+ return viewCache;
1659
+ }
1660
+
1661
+ function revealWorkspace(wsId, options = {}) {
1662
+ if (!isId(wsId)) return null;
1663
+ const existing = archives.get(wsId);
1664
+ if (!existing || existing.archived !== true) return null;
1665
+ const record = {
1666
+ ...existing,
1667
+ workspaceId: wsId,
1668
+ archived: false,
1669
+ sessionIds: Array.isArray(existing.sessionIds) ? [...existing.sessionIds] : [],
1670
+ pending: Array.isArray(existing.pending) ? [...existing.pending] : []
1671
+ };
1672
+ archives.set(wsId, record);
1673
+ saveLocal();
1674
+ notify();
1675
+ try {
1676
+ callApi("/workspace-unarchive", { workspaceId: wsId, retainSessionIds: true }).catch(() => {});
1677
+ } catch (_) {}
1678
+ if (options && options.silent === false) {
1679
+ const msg = smTranslate("notice.workspaceReAdded");
1680
+ showNotice(msg);
1681
+ }
1682
+ return record;
1683
+ }
1684
+
1685
+ function installWorkspaceCreate(workspacesService) {
1686
+ if (!workspacesService || typeof workspacesService.create !== "function") return null;
1687
+
1688
+ let originalCreate = workspacesService[WORKSPACE_ORIGINAL_CREATE] || workspacesService.create;
1689
+ while (originalCreate && (originalCreate.__dshSmWrapper || originalCreate[WORKSPACE_CREATE_MARK])) {
1690
+ if (originalCreate.__dshSmUnderlying) originalCreate = originalCreate.__dshSmUnderlying;
1691
+ else break;
1692
+ }
1693
+ workspacesService[WORKSPACE_ORIGINAL_CREATE] = originalCreate;
1694
+
1695
+ const wrappedCreate = async function (...args) {
1696
+ const options = args[0];
1697
+ const targetPath = typeof options === "string" ? options : (options && typeof options.path === "string" ? options.path : "");
1698
+ let knownArchivedId = null;
1699
+ if (targetPath) {
1700
+ const raw = rawSnapshot();
1701
+ const items = Array.isArray(raw?.items) ? raw.items : [];
1702
+ const match = items.find(item => item.path && pathKey(item.path) === pathKey(targetPath));
1703
+ if (match) {
1704
+ const id = match.workspaceId || match.id;
1705
+ if (id && archives.get(id)?.archived === true) {
1706
+ knownArchivedId = id;
1707
+ revealWorkspace(id, { silent: false });
1708
+ }
1709
+ }
1710
+ }
1711
+
1712
+ const res = await originalCreate.apply(this, args);
1713
+
1714
+ const createdId = typeof res === "string" ? res : (res && (res.id || res.workspaceId));
1715
+ const targetId = createdId || knownArchivedId;
1716
+ if (targetId && archives.get(targetId)?.archived === true) {
1717
+ revealWorkspace(targetId, { silent: false });
1718
+ }
1719
+
1720
+ return res;
1721
+ };
1722
+ wrappedCreate.__dshSmWrapper = true;
1723
+ wrappedCreate.__dshSmUnderlying = originalCreate;
1724
+
1725
+ const ownCreate = Object.getOwnPropertyDescriptor(workspacesService, "create");
1726
+ try {
1727
+ workspacesService.create = wrappedCreate;
1728
+ Object.defineProperty(workspacesService, WORKSPACE_CREATE_MARK, { value: true, configurable: true, writable: true });
1729
+ } catch (err) {
1730
+ console.warn("[session-manager] Failed to wrap workspaces.create:", err);
1731
+ return null;
1732
+ }
1733
+
1734
+ return function dispose() {
1735
+ try {
1736
+ if (workspacesService.create === wrappedCreate) {
1737
+ if (ownCreate !== undefined) Object.defineProperty(workspacesService, "create", ownCreate);
1738
+ else workspacesService.create = originalCreate;
1739
+ }
1740
+ delete workspacesService[WORKSPACE_CREATE_MARK];
1741
+ delete workspacesService[WORKSPACE_ORIGINAL_CREATE];
1742
+ } catch (_) {}
1743
+ };
1744
+ }
1745
+
1746
+ function installConnectWorkspace(uiWorkspaceService) {
1747
+ if (!uiWorkspaceService || typeof uiWorkspaceService.connectWorkspace !== "function") return null;
1748
+
1749
+ let originalConnect = uiWorkspaceService[UI_ORIGINAL_CONNECT] || uiWorkspaceService.connectWorkspace;
1750
+ while (originalConnect && (originalConnect.__dshSmWrapper || originalConnect[UI_CONNECT_MARK])) {
1751
+ if (originalConnect.__dshSmUnderlying) originalConnect = originalConnect.__dshSmUnderlying;
1752
+ else break;
1753
+ }
1754
+ uiWorkspaceService[UI_ORIGINAL_CONNECT] = originalConnect;
1755
+
1756
+ const wrappedConnect = async function (workspaceId, ...rest) {
1757
+ if (typeof workspaceId === "string" && archives.get(workspaceId)?.archived === true) {
1758
+ revealWorkspace(workspaceId, { silent: true });
1759
+ }
1760
+ return await originalConnect.call(this, workspaceId, ...rest);
1761
+ };
1762
+ wrappedConnect.__dshSmWrapper = true;
1763
+ wrappedConnect.__dshSmUnderlying = originalConnect;
1764
+
1765
+ const ownConnect = Object.getOwnPropertyDescriptor(uiWorkspaceService, "connectWorkspace");
1766
+ try {
1767
+ uiWorkspaceService.connectWorkspace = wrappedConnect;
1768
+ Object.defineProperty(uiWorkspaceService, UI_CONNECT_MARK, { value: true, configurable: true, writable: true });
1769
+ } catch (err) {
1770
+ console.warn("[session-manager] Failed to wrap uiWorkspace.connectWorkspace:", err);
1771
+ return null;
1772
+ }
1773
+
1774
+ return function dispose() {
1775
+ try {
1776
+ if (uiWorkspaceService.connectWorkspace === wrappedConnect) {
1777
+ if (ownConnect !== undefined) Object.defineProperty(uiWorkspaceService, "connectWorkspace", ownConnect);
1778
+ else uiWorkspaceService.connectWorkspace = originalConnect;
1779
+ }
1780
+ delete uiWorkspaceService[UI_CONNECT_MARK];
1781
+ delete uiWorkspaceService[UI_ORIGINAL_CONNECT];
1782
+ } catch (_) {}
1783
+ };
1784
+ }
1785
+
1786
+ return {
1787
+ getSnapshot: () => snapshotCache,
1788
+ getVersion: () => version,
1789
+ subscribe(listener) {
1790
+ listeners.add(listener);
1791
+ return () => listeners.delete(listener);
1792
+ },
1793
+ isArchived(wsId) {
1794
+ const rec = archives.get(wsId);
1795
+ return rec ? rec.archived === true : false;
1796
+ },
1797
+ isBusy(wsId) {
1798
+ return busyWorkspaces.has(wsId);
1799
+ },
1800
+ getRecord(wsId) {
1801
+ return archives.get(wsId) || null;
1802
+ },
1803
+ getAllRecords() {
1804
+ return archives;
1805
+ },
1806
+ rawSnapshot,
1807
+ getViewSnapshot,
1808
+ setReadRaw(fn) {
1809
+ readRaw = fn;
1810
+ },
1811
+ notify,
1812
+ syncFromHost(hostArchives) {
1813
+ if (!hostArchives || typeof hostArchives !== "object" || Array.isArray(hostArchives)) return;
1814
+ let changed = false;
1815
+ for (const [wsId, rec] of Object.entries(hostArchives)) {
1816
+ if (!isId(wsId) || !rec) continue;
1817
+ const existing = archives.get(wsId);
1818
+ if (!existing || existing.archived !== (rec.archived === true) || existing.sessionIds?.length !== (rec.sessionIds || []).length) {
1819
+ archives.set(wsId, {
1820
+ workspaceId: wsId,
1821
+ archived: rec.archived === true,
1822
+ archivedAt: typeof rec.archivedAt === "number" ? rec.archivedAt : Date.now(),
1823
+ sessionIds: Array.isArray(rec.sessionIds) ? rec.sessionIds.filter(isId) : [],
1824
+ pending: Array.isArray(rec.pending) ? rec.pending.filter(isId) : []
1825
+ });
1826
+ changed = true;
1827
+ }
1828
+ }
1829
+ if (changed) {
1830
+ saveLocal();
1831
+ notify();
1832
+ }
1833
+ },
1834
+ markBusy(wsId, busy) {
1835
+ if (busy) busyWorkspaces.add(wsId);
1836
+ else busyWorkspaces.delete(wsId);
1837
+ notify();
1838
+ },
1839
+ saveArchiveRecord(wsId, record) {
1840
+ archives.set(wsId, record);
1841
+ saveLocal();
1842
+ notify();
1843
+ },
1844
+ removeArchiveRecord(wsId) {
1845
+ archives.delete(wsId);
1846
+ saveLocal();
1847
+ notify();
1848
+ },
1849
+ revealWorkspace,
1850
+ installWorkspaceCreate,
1851
+ installConnectWorkspace,
1852
+ installWorkspaceModel(model) {
1853
+ if (!model || model[WORKSPACE_MODEL_MARK] === true) return null;
1854
+ const originalGetSnapshot = model.getSnapshot;
1855
+ const originalSubscribe = model.subscribe;
1856
+ if (typeof originalGetSnapshot !== "function" || typeof originalSubscribe !== "function") return null;
1857
+
1858
+ readRaw = () => {
1859
+ try { return originalGetSnapshot.call(model); }
1860
+ catch (err) { console.warn("[session-manager] Failed to read raw workspaces snapshot:", err); return { items: [] }; }
1861
+ };
1862
+
1863
+ const wrappedGetSnapshot = function () {
1864
+ return getViewSnapshot();
1865
+ };
1866
+ const wrappedSubscribe = function (listener) {
1867
+ const off = originalSubscribe.call(model, listener);
1868
+ listeners.add(listener);
1869
+ return () => {
1870
+ listeners.delete(listener);
1871
+ if (typeof off === "function") off();
1872
+ };
1873
+ };
1874
+
1875
+ const ownGetSnapshot = Object.getOwnPropertyDescriptor(model, "getSnapshot");
1876
+ const ownSubscribe = Object.getOwnPropertyDescriptor(model, "subscribe");
1877
+
1878
+ try {
1879
+ model.getSnapshot = wrappedGetSnapshot;
1880
+ model.subscribe = wrappedSubscribe;
1881
+ Object.defineProperty(model, WORKSPACE_MODEL_MARK, { value: true, configurable: true });
1882
+ } catch (err) {
1883
+ console.warn("[session-manager] Failed to wrap workspaces.list:", err);
1884
+ return null;
1885
+ }
1886
+
1887
+ return function dispose() {
1888
+ try {
1889
+ if (model.getSnapshot === wrappedGetSnapshot) {
1890
+ if (ownGetSnapshot !== undefined) Object.defineProperty(model, "getSnapshot", ownGetSnapshot);
1891
+ else delete model.getSnapshot;
1892
+ }
1893
+ if (model.subscribe === wrappedSubscribe) {
1894
+ if (ownSubscribe !== undefined) Object.defineProperty(model, "subscribe", ownSubscribe);
1895
+ else delete model.subscribe;
1896
+ }
1897
+ if (model[WORKSPACE_MODEL_MARK] === true) delete model[WORKSPACE_MODEL_MARK];
1898
+ } catch (_) {}
1899
+ listeners.clear();
1900
+ viewCache = null;
1901
+ viewRaw = null;
1902
+ viewVersion = -1;
1903
+ };
1904
+ }
1905
+ };
1906
+ })();
1907
+
1908
+ if (typeof window !== "undefined") {
1909
+ window.revealWorkspace = (id, opts) => workspaceArchiveStore.revealWorkspace(id, opts);
1910
+ }
1911
+ if (typeof globalThis !== "undefined") {
1912
+ globalThis.revealWorkspace = (id, opts) => workspaceArchiveStore.revealWorkspace(id, opts);
1913
+ }
1914
+
1915
+ const SM_SELECT_SELECTOR = ".sm-viewSelect, .sm-settingsSelect, .sm-wsSortSelect, .sm-prioritySelectInline, .sm-panelDialog select, .sm-nativeDialog select, .sm-settingsCard select";
1916
+
1917
+ let activeCustomSelect = null;
1918
+ let activeCustomMenu = null;
1919
+
1920
+ function closeCustomSelect() {
1921
+ if (activeCustomMenu) {
1922
+ try { activeCustomMenu.remove(); } catch (_) {}
1923
+ activeCustomMenu = null;
1924
+ }
1925
+ if (typeof document !== "undefined") {
1926
+ try {
1927
+ const oldMenus = document.querySelectorAll(".sm-customSelectMenu");
1928
+ for (let i = 0; i < oldMenus.length; i++) {
1929
+ try { oldMenus[i].remove(); } catch (_) {}
1930
+ }
1931
+ } catch (_) {}
1932
+ }
1933
+ if (activeCustomSelect) {
1934
+ try { activeCustomSelect.classList.remove("sm-selectExpanded"); } catch (_) {}
1935
+ activeCustomSelect = null;
1936
+ }
1937
+ }
1938
+
1939
+ function applyCustomSelection(select, val) {
1940
+ closeCustomSelect();
1941
+ if (!select) return;
1942
+ if (select.value !== val) {
1943
+ try {
1944
+ const descriptor = Object.getOwnPropertyDescriptor(window.HTMLSelectElement?.prototype || {}, "value");
1945
+ if (descriptor && descriptor.set) {
1946
+ descriptor.set.call(select, val);
1947
+ } else {
1948
+ select.value = val;
1949
+ }
1950
+ } catch (_) {
1951
+ select.value = val;
1952
+ }
1953
+ try {
1954
+ const Evt = typeof Event !== "undefined" ? Event : (typeof window !== "undefined" ? window.Event : null);
1955
+ if (Evt) {
1956
+ select.dispatchEvent(new Evt("input", { bubbles: true }));
1957
+ select.dispatchEvent(new Evt("change", { bubbles: true }));
1958
+ } else if (typeof select.dispatchEvent === "function") {
1959
+ select.dispatchEvent({ type: "input", bubbles: true });
1960
+ select.dispatchEvent({ type: "change", bubbles: true });
1961
+ }
1962
+ } catch (_) {
1963
+ try {
1964
+ select.dispatchEvent({ type: "input", bubbles: true });
1965
+ select.dispatchEvent({ type: "change", bubbles: true });
1966
+ } catch (_) {}
1967
+ }
1968
+ try {
1969
+ const key = Object.keys(select).find(k => k.startsWith("__reactProps") || k.startsWith("__reactEventHandlers"));
1970
+ if (key && typeof select[key]?.onChange === "function") {
1971
+ select[key].onChange({ target: select, currentTarget: select });
1972
+ }
1973
+ } catch (_) {}
1974
+ }
1975
+ }
1976
+
1977
+ function openCustomSelect(select) {
1978
+ if (!select || select.disabled) return;
1979
+ if (activeCustomSelect === select && activeCustomMenu) {
1980
+ closeCustomSelect();
1981
+ return;
1982
+ }
1983
+ closeCustomSelect();
1984
+
1985
+ const options = Array.from(select.options || []);
1986
+ if (options.length === 0) return;
1987
+
1988
+ const doc = select.ownerDocument || (typeof document !== "undefined" ? document : null);
1989
+ if (!doc || !doc.body) return;
1990
+
1991
+ activeCustomSelect = select;
1992
+ select.classList.add("sm-selectExpanded");
1993
+
1994
+ const menu = doc.createElement("div");
1995
+ menu.className = "sm-customSelectMenu";
1996
+ menu.setAttribute("role", "listbox");
1997
+ const ariaLabel = select.getAttribute("aria-label");
1998
+ if (ariaLabel) menu.setAttribute("aria-label", ariaLabel);
1999
+
2000
+ try {
2001
+ const theme = doc.documentElement?.getAttribute?.("data-sm-theme") || (doc.body?.getAttribute?.("data-ds-dark-theme") === "true" ? "dark" : null);
2002
+ if (theme && typeof menu.setAttribute === "function") menu.setAttribute("data-sm-theme", theme);
2003
+ const cs = typeof window !== "undefined" && typeof window.getComputedStyle === "function" ? window.getComputedStyle(select) : null;
2004
+ if (cs) {
2005
+ if (cs.colorScheme) menu.style.colorScheme = cs.colorScheme;
2006
+ if (cs.fontFamily) menu.style.fontFamily = cs.fontFamily;
2007
+ }
2008
+ } catch (_) {}
2009
+
2010
+ options.forEach((opt) => {
2011
+ const item = doc.createElement("div");
2012
+ const isSelected = opt.value === select.value || opt.selected;
2013
+ item.className = "sm-customSelectOption" +
2014
+ (isSelected ? " sm-customSelectOptionSelected" : "") +
2015
+ (opt.disabled ? " sm-customSelectOptionDisabled" : "");
2016
+ item.setAttribute("role", "option");
2017
+ item.setAttribute("aria-selected", isSelected ? "true" : "false");
2018
+ item.title = opt.title || opt.text || opt.label || opt.value || "";
2019
+
2020
+ const labelSpan = doc.createElement("span");
2021
+ labelSpan.className = "sm-customSelectOptionLabel";
2022
+ labelSpan.textContent = opt.text || opt.label || opt.value;
2023
+ if (typeof item.appendChild === "function") {
2024
+ item.appendChild(labelSpan);
2025
+ } else if (item.children && Array.isArray(item.children)) {
2026
+ item.children.push(labelSpan);
2027
+ }
2028
+
2029
+ if (isSelected) {
2030
+ const checkSpan = doc.createElement("span");
2031
+ checkSpan.className = "sm-customSelectOptionCheck";
2032
+ checkSpan.textContent = "✓";
2033
+ if (typeof item.appendChild === "function") {
2034
+ item.appendChild(checkSpan);
2035
+ } else if (item.children && Array.isArray(item.children)) {
2036
+ item.children.push(checkSpan);
2037
+ }
2038
+ }
2039
+
2040
+ const handleOptClick = (e) => {
2041
+ if (e && e.preventDefault) e.preventDefault();
2042
+ if (e && e.stopPropagation) e.stopPropagation();
2043
+ if (opt.disabled) return;
2044
+ applyCustomSelection(select, opt.value);
2045
+ };
2046
+ if (typeof item.addEventListener === "function") {
2047
+ item.addEventListener("mousedown", handleOptClick);
2048
+ } else {
2049
+ item.onmousedown = handleOptClick;
2050
+ }
2051
+
2052
+ if (typeof menu.appendChild === "function") {
2053
+ menu.appendChild(item);
2054
+ } else if (menu.children && Array.isArray(menu.children)) {
2055
+ menu.children.push(item);
2056
+ }
2057
+ });
2058
+
2059
+ if (typeof doc.body?.appendChild === "function") {
2060
+ doc.body.appendChild(menu);
2061
+ }
2062
+ activeCustomMenu = menu;
2063
+
2064
+ const rect = select.getBoundingClientRect();
2065
+ const spaceBelow = (typeof window !== "undefined" ? window.innerHeight : 800) - rect.bottom;
2066
+ const spaceAbove = rect.top;
2067
+ const estimatedHeight = Math.min(260, Math.max(36, options.length * 30 + 8));
2068
+ const dropUp = spaceBelow < estimatedHeight && spaceAbove > spaceBelow;
2069
+
2070
+ if (dropUp) {
2071
+ menu.style.bottom = ((typeof window !== "undefined" ? window.innerHeight : 800) - rect.top + 4) + "px";
2072
+ menu.style.top = "auto";
2073
+ menu.classList.add("sm-customSelectMenu-dropUp");
2074
+ } else {
2075
+ menu.style.top = (rect.bottom + 4) + "px";
2076
+ menu.style.bottom = "auto";
2077
+ }
2078
+
2079
+ const selectWidth = rect.width;
2080
+ if (selectWidth > 0) {
2081
+ menu.style.width = selectWidth + "px";
2082
+ menu.style.minWidth = selectWidth + "px";
2083
+ menu.style.maxWidth = selectWidth + "px";
2084
+ menu.style.boxSizing = "border-box";
2085
+ } else {
2086
+ menu.style.minWidth = "120px";
2087
+ }
2088
+ const spaceRight = (typeof window !== "undefined" ? window.innerWidth : 1200) - rect.left;
2089
+ if (spaceRight < 220 && rect.right > 220) {
2090
+ menu.style.right = ((typeof window !== "undefined" ? window.innerWidth : 1200) - rect.right) + "px";
2091
+ menu.style.left = "auto";
2092
+ } else {
2093
+ menu.style.left = rect.left + "px";
2094
+ menu.style.right = "auto";
2095
+ }
2096
+
2097
+ const selectedEl = typeof menu.querySelector === "function" ? menu.querySelector(".sm-customSelectOptionSelected") : null;
2098
+ if (selectedEl && typeof selectedEl.scrollIntoView === "function") {
2099
+ selectedEl.scrollIntoView({ block: "nearest" });
2100
+ }
2101
+ }
2102
+
2103
+ function installCustomDropdownInterceptor() {
2104
+ if (typeof document === "undefined" || typeof document.addEventListener !== "function") {
2105
+ return () => {};
2106
+ }
2107
+
2108
+ if (typeof window !== "undefined" && typeof window.__dshSmDropdownInterceptorOff === "function") {
2109
+ try { window.__dshSmDropdownInterceptorOff(); } catch (_) {}
2110
+ }
2111
+
2112
+ const onMouseDown = (e) => {
2113
+ if (activeCustomMenu && activeCustomMenu.contains(e.target)) return;
2114
+ if (e.target?.closest?.("button, input, textarea, a")) return;
2115
+
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") {
2134
+ e.preventDefault();
2135
+ e.stopPropagation();
2136
+ if (select.disabled) return;
2137
+ try { select.focus(); } catch (_) {}
2138
+ openCustomSelect(select);
2139
+ return;
2140
+ }
2141
+
2142
+ if (activeCustomMenu) {
2143
+ closeCustomSelect();
2144
+ }
2145
+ };
2146
+
2147
+ const onClick = (e) => {
2148
+ 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") {
2157
+ e.preventDefault();
2158
+ e.stopPropagation();
2159
+ }
2160
+ };
2161
+
2162
+ const onKeyDown = (e) => {
2163
+ if (e.key === "Escape") {
2164
+ if (activeCustomMenu) {
2165
+ e.preventDefault();
2166
+ e.stopPropagation();
2167
+ closeCustomSelect();
2168
+ }
2169
+ return;
2170
+ }
2171
+
2172
+ 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"));
2174
+ if (!targetSelect || targetSelect.tagName !== "SELECT") return;
2175
+
2176
+ if (e.key === "Enter" || e.key === " ") {
2177
+ if (!activeCustomMenu) {
2178
+ e.preventDefault();
2179
+ e.stopPropagation();
2180
+ openCustomSelect(targetSelect);
2181
+ } else {
2182
+ e.preventDefault();
2183
+ e.stopPropagation();
2184
+ closeCustomSelect();
2185
+ }
2186
+ } else if (e.key === "ArrowDown" || e.key === "ArrowUp") {
2187
+ e.preventDefault();
2188
+ e.stopPropagation();
2189
+ const opts = Array.from(targetSelect.options || []).filter(o => !o.disabled);
2190
+ if (opts.length === 0) return;
2191
+ const currentIdx = opts.findIndex(o => o.value === targetSelect.value);
2192
+ const step = e.key === "ArrowDown" ? 1 : -1;
2193
+ const nextIdx = (currentIdx + step + opts.length) % opts.length;
2194
+ applyCustomSelection(targetSelect, opts[nextIdx].value);
2195
+ openCustomSelect(targetSelect);
2196
+ }
2197
+ };
2198
+
2199
+ const onResizeOrScroll = () => {
2200
+ if (activeCustomMenu) closeCustomSelect();
2201
+ };
2202
+
2203
+ document.addEventListener("mousedown", onMouseDown, true);
2204
+ document.addEventListener("click", onClick, true);
2205
+ document.addEventListener("keydown", onKeyDown, true);
2206
+ if (typeof window !== "undefined" && typeof window.addEventListener === "function") {
2207
+ window.addEventListener("resize", onResizeOrScroll, { passive: true });
2208
+ window.addEventListener("scroll", onResizeOrScroll, { passive: true, capture: true });
2209
+ }
2210
+
2211
+ const dispose = function dispose() {
2212
+ closeCustomSelect();
2213
+ document.removeEventListener("mousedown", onMouseDown, true);
2214
+ document.removeEventListener("click", onClick, true);
2215
+ document.removeEventListener("keydown", onKeyDown, true);
2216
+ if (typeof window !== "undefined" && typeof window.removeEventListener === "function") {
2217
+ window.removeEventListener("resize", onResizeOrScroll);
2218
+ window.removeEventListener("scroll", onResizeOrScroll, true);
2219
+ }
2220
+ if (typeof window !== "undefined" && window.__dshSmDropdownInterceptorOff === dispose) {
2221
+ window.__dshSmDropdownInterceptorOff = null;
2222
+ }
2223
+ };
2224
+ if (typeof window !== "undefined") {
2225
+ window.__dshSmDropdownInterceptorOff = dispose;
2226
+ }
2227
+ return dispose;
2228
+ }
2229
+
2230
+ async function executeArchiveWorkspace(workspaceId, { allWorkspaces, t } = {}) {
2231
+ if (workspaceArchiveStore.isBusy(workspaceId)) return false;
2232
+ const raw = workspaceArchiveStore.rawSnapshot();
2233
+ const rawItems = Array.isArray(raw?.items) ? raw.items : [];
2234
+ const rawItem = rawItems.find(w => (w.workspaceId || w.id) === workspaceId);
2235
+ const ws = (Array.isArray(allWorkspaces) ? allWorkspaces.find(w => w.id === workspaceId) : null) || rawItem;
2236
+ if (!ws) return false;
2237
+
2238
+ const hostArchivedSet = new Set(Array.isArray(raw?.archivedSessionIds) ? raw.archivedSessionIds : []);
2239
+ const allSessionIds = Array.isArray(ws.sessionIds) ? ws.sessionIds : [];
2240
+ const toArchive = allSessionIds.filter(id => !hostArchivedSet.has(id));
2241
+
2242
+ workspaceArchiveStore.markBusy(workspaceId, true);
2243
+ const succeeded = [];
2244
+ let failureError = null;
2245
+
2246
+ try {
2247
+ for (const sid of toArchive) {
2248
+ try {
2249
+ await runArchiveCommand("archive", sid, { stopActivity: false });
2250
+ succeeded.push(sid);
2251
+ } catch (err) {
2252
+ failureError = err;
2253
+ break;
2254
+ }
2255
+ }
2256
+
2257
+ if (failureError) {
2258
+ // Rollback: unarchive whatever we archived in this run
2259
+ for (const sid of succeeded) {
2260
+ try { await runArchiveCommand("unarchive", sid); } catch (_) {}
2261
+ }
2262
+ const msg = failureError?.message || String(failureError);
2263
+ const alertMsg = (t ? t("workspace.archive.failed", { message: msg }) : "归档工作区失败:{message}").replace("{message}", msg);
2264
+ if (typeof window !== "undefined" && typeof window.alert === "function") {
2265
+ window.alert(alertMsg);
2266
+ }
2267
+ return false;
2268
+ }
2269
+
2270
+ const prevRecord = workspaceArchiveStore.getRecord(workspaceId);
2271
+ const combinedSessionIds = Array.from(new Set([...(prevRecord?.sessionIds || []), ...succeeded]));
2272
+ const record = {
2273
+ workspaceId,
2274
+ archived: true,
2275
+ archivedAt: Date.now(),
2276
+ sessionIds: combinedSessionIds,
2277
+ pending: []
2278
+ };
2279
+ workspaceArchiveStore.saveArchiveRecord(workspaceId, record);
2280
+ try {
2281
+ await callApi("/workspace-archive", record);
2282
+ } catch (e) {
2283
+ console.warn("[session-manager] Failed to persist workspace archive to host:", e);
2284
+ }
2285
+
2286
+ const currentId = activeClientCtx?.sessions?.list?.getSnapshot?.()?.current
2287
+ || activeClientCtx?.uiWorkspace?.mainReference?.sessionId;
2288
+ if (currentId && toArchive.includes(currentId)) {
2289
+ try {
2290
+ activeClientCtx?.uiWorkspace?.clearMain?.();
2291
+ } catch (_) {}
2292
+ }
2293
+
2294
+ try { await activeClientCtx?.sessions?.refresh?.(); } catch (_) {}
2295
+ try { await activeClientCtx?.workspaces?.refresh?.(); } catch (_) {}
2296
+ return true;
2297
+ } finally {
2298
+ workspaceArchiveStore.markBusy(workspaceId, false);
2299
+ }
2300
+ }
2301
+
2302
+ async function executeUnarchiveWorkspace(workspaceId, { t } = {}) {
2303
+ if (workspaceArchiveStore.isBusy(workspaceId)) return false;
2304
+ const record = workspaceArchiveStore.getRecord(workspaceId);
2305
+ workspaceArchiveStore.markBusy(workspaceId, true);
2306
+ try {
2307
+ const raw = workspaceArchiveStore.rawSnapshot();
2308
+ const hostArchivedSet = new Set(Array.isArray(raw?.archivedSessionIds) ? raw.archivedSessionIds : []);
2309
+ const idsToRestore = [...(record?.sessionIds || []), ...(record?.pending || [])];
2310
+ const failed = [];
2311
+
2312
+ for (const sid of idsToRestore) {
2313
+ if (!hostArchivedSet.has(sid)) continue;
2314
+ try {
2315
+ await runArchiveCommand("unarchive", sid);
2316
+ } catch (err) {
2317
+ failed.push(sid);
2318
+ }
2319
+ }
2320
+
2321
+ if (failed.length > 0) {
2322
+ const updatedRecord = {
2323
+ ...(record || { workspaceId }),
2324
+ archived: true,
2325
+ pending: failed
2326
+ };
2327
+ workspaceArchiveStore.saveArchiveRecord(workspaceId, updatedRecord);
2328
+ try {
2329
+ await callApi("/workspace-archive", updatedRecord);
2330
+ } catch (_) {}
2331
+ const msg = (t ? t("workspace.unarchive.failed", { message: failed.length + " 个会话恢复失败" }) : "恢复工作区失败:{message}").replace("{message}", failed.length + " 个会话恢复失败");
2332
+ if (typeof window !== "undefined" && typeof window.alert === "function") {
2333
+ window.alert(msg);
2334
+ }
2335
+ return false;
2336
+ }
2337
+
2338
+ workspaceArchiveStore.removeArchiveRecord(workspaceId);
2339
+ try {
2340
+ await callApi("/workspace-unarchive", { workspaceId });
2341
+ } catch (_) {}
2342
+
2343
+ try { await activeClientCtx?.sessions?.refresh?.(); } catch (_) {}
2344
+ try { await activeClientCtx?.workspaces?.refresh?.(); } catch (_) {}
2345
+ return true;
2346
+ } finally {
2347
+ workspaceArchiveStore.markBusy(workspaceId, false);
2348
+ }
2349
+ }
2350
+
2351
+ function SmSwitch(props) {
2352
+ const checked = props.checked === true;
2353
+ const disabled = props.disabled === true;
2354
+ const loading = props.loading === true;
2355
+ const onChange = typeof props.onChange === "function" ? props.onChange : () => {};
2356
+ return h("button", {
2357
+ type: "button",
2358
+ role: "switch",
2359
+ className: "sm-switch" + (checked ? " sm-switchChecked" : "") + (loading ? " sm-switchLoading" : ""),
2360
+ "aria-checked": checked,
2361
+ "aria-label": props.ariaLabel,
2362
+ disabled: disabled || loading,
2363
+ onClick: (e) => {
2364
+ if (e && typeof e.stopPropagation === "function") e.stopPropagation();
2365
+ if (disabled || loading) return;
2366
+ if (typeof props.onClick === "function") props.onClick(e);
2367
+ else onChange(!checked);
2368
+ }
2369
+ },
2370
+ h("span", { className: "sm-switchThumb" },
2371
+ loading ? h("span", { className: "sm-switchSpinner" }) : null
2372
+ )
2373
+ );
2374
+ }
2375
+
1371
2376
  const EMPTY_ANNOTATION = Object.freeze({ favorite: false, reviewLater: false, tags: Object.freeze([]), note: "", priority: 3, revision: 0 });
1372
2377
  const validAnnotation = value => value && typeof value.favorite === "boolean" && typeof value.reviewLater === "boolean" && Array.isArray(value.tags) && value.tags.every(tag => typeof tag === "string") && typeof value.note === "string" && (value.priority === null || Number.isInteger(value.priority) && value.priority >= 1 && value.priority <= 5) && Number.isSafeInteger(value.revision) && value.revision >= 0;
1373
2378
  const annotationOf = (state, id) => Object.hasOwn(state.entries, id) ? state.entries[id] : EMPTY_ANNOTATION;
@@ -1598,18 +2603,51 @@ function isTopmostDialog(z) {
1598
2603
  ].join(String.fromCharCode(10))
1599
2604
  };
1600
2605
  const promptFor = lang => CLIPBOARD_PROMPT_TEXT[lang] || CLIPBOARD_PROMPT_TEXT.zh;
2606
+ function isClickInsideDialog(e, dialogRef, layerRef) {
2607
+ if (!e || !e.target) return false;
2608
+ const target = e.target;
2609
+ if (dialogRef && dialogRef.current && typeof dialogRef.current.contains === "function" && dialogRef.current.contains(target)) {
2610
+ return true;
2611
+ }
2612
+ if (layerRef && layerRef.current && typeof layerRef.current.contains === "function" && layerRef.current.contains(target)) {
2613
+ return true;
2614
+ }
2615
+ if (typeof target.closest === "function") {
2616
+ if (dialogRef && dialogRef.current && target.closest(".sm-nativeDialog") === dialogRef.current) {
2617
+ return true;
2618
+ }
2619
+ if (layerRef && layerRef.current && target.closest(".sm-nativeDialogLayer") === layerRef.current) {
2620
+ return true;
2621
+ }
2622
+ }
2623
+ if (activeCustomMenu && typeof activeCustomMenu.contains === "function" && activeCustomMenu.contains(target)) {
2624
+ if (activeCustomSelect) {
2625
+ if (dialogRef && dialogRef.current && typeof dialogRef.current.contains === "function" && dialogRef.current.contains(activeCustomSelect)) {
2626
+ return true;
2627
+ }
2628
+ if (layerRef && layerRef.current && typeof layerRef.current.contains === "function" && layerRef.current.contains(activeCustomSelect)) {
2629
+ return true;
2630
+ }
2631
+ }
2632
+ }
2633
+ return false;
2634
+ }
2635
+
1601
2636
  function BatchPreviewDialog(props) {
1602
2637
  const t = typeof props.t === "function" ? props.t : (key) => key;
1603
2638
  const open = props.open === true;
1604
2639
  const onCancel = typeof props.onCancel === "function" ? props.onCancel : () => {};
1605
2640
  const onExecute = typeof props.onExecute === "function" ? props.onExecute : () => {};
2641
+ const danger = props.danger === true;
1606
2642
  const actionLabel = props.actionLabel || "";
1607
2643
  const summaryAction = props.summaryAction || actionLabel;
2644
+ const title = props.title || (props.action === "delete" ? (props.t ? t("confirm.delete.title") : "删除会话") : t("bulk.dialog.title", { action: actionLabel }));
1608
2645
  const sessions = Array.isArray(props.sessions) ? props.sessions : [];
1609
2646
  const warning = typeof props.warning === "string" ? props.warning : "";
1610
2647
  const confirmLabel = props.confirmLabel || t("bulk.dialog.execute");
1611
2648
  const cancelLabel = props.cancelLabel || t("bulk.dialog.cancel");
1612
- const danger = props.danger === true;
2649
+ const dialogRef = useRef(null);
2650
+ const layerRef = useRef(null);
1613
2651
  const [expanded, setExpanded] = useState(false);
1614
2652
  const [zIndex] = useState(() => open ? nextDialogZ() : 0);
1615
2653
  const floatingCoords = useFloatingPosition(props.anchor, open, { placement: "bottom-end", dialogWidth: 360, dialogHeight: 320 });
@@ -1621,15 +2659,33 @@ function isTopmostDialog(z) {
1621
2659
  const handler = (e) => {
1622
2660
  if (e.key !== "Escape" || e.defaultPrevented) return;
1623
2661
  if (!isTopmostDialog(zIndex)) return;
2662
+ if (props.busy) return;
1624
2663
  e.preventDefault(); e.stopPropagation(); escStateRef.current.onCancel();
1625
2664
  };
1626
2665
  window.addEventListener("keydown", handler);
2666
+ const mountTime = Date.now();
2667
+ const clickHandler = (e) => {
2668
+ if (Date.now() - mountTime < 50) return;
2669
+ if (e.defaultPrevented) return;
2670
+ if (props.busy) return;
2671
+ if (!isTopmostDialog(zIndex)) return;
2672
+ if (isClickInsideDialog(e, dialogRef, layerRef)) return;
2673
+ if (e && typeof e.preventDefault === "function") e.preventDefault();
2674
+ if (e && typeof e.stopPropagation === "function") e.stopPropagation();
2675
+ if (e && typeof e.stopImmediatePropagation === "function") e.stopImmediatePropagation();
2676
+ escStateRef.current.onCancel();
2677
+ };
2678
+ window.addEventListener("click", clickHandler, true);
1627
2679
  return () => {
1628
2680
  unregisterDialogZ(zIndex);
1629
2681
  window.removeEventListener("keydown", handler);
2682
+ window.removeEventListener("click", clickHandler, true);
1630
2683
  };
1631
2684
  }, [open, zIndex]);
1632
2685
  if (!open) return null;
2686
+ const loading = props.loading === true;
2687
+ const hasError = Boolean(props.error);
2688
+ const confirmDisabled = loading || hasError;
1633
2689
  const showExpand = sessions.length > 12;
1634
2690
  const visible = expanded ? sessions : sessions.slice(0, 12);
1635
2691
  const topPx = floatingCoords ? floatingCoords.top : null;
@@ -1651,9 +2707,9 @@ function isTopmostDialog(z) {
1651
2707
  : {})
1652
2708
  };
1653
2709
  const portalOverlay = renderInOverlay;
1654
- return portalOverlay(h("div", { className: "sm-nativeDialogLayer sm-bulkDialog" + (hasCoords ? " sm-bulkDialog-anchored" : ""), role: "presentation", style: layerStyle },
1655
- h("section", { className: "sm-nativeDialog", style: { pointerEvents: "auto" }, role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-batch-preview-title" },
1656
- h("div", { className: "sm-nativeDialogHeader" }, h("h2", { id: "sm-batch-preview-title", className: "sm-nativeDialogTitle" }, t("bulk.dialog.title", { action: actionLabel }))),
2710
+ return portalOverlay(h("div", { ref: layerRef, className: "sm-nativeDialogLayer sm-bulkDialog" + (hasCoords ? " sm-bulkDialog-anchored" : ""), role: "presentation", style: layerStyle },
2711
+ h("section", { ref: dialogRef, className: "sm-nativeDialog", style: { pointerEvents: "auto" }, role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-batch-preview-title" },
2712
+ h("div", { className: "sm-nativeDialogHeader" }, h("h2", { id: "sm-batch-preview-title", className: "sm-nativeDialogTitle" }, title)),
1657
2713
  h("div", { className: "sm-nativeDialogBody" },
1658
2714
  h("div", { className: "sm-bulkDialogSummary" }, t("bulk.dialog.summary", { count: sessions.length, action: actionLabel })),
1659
2715
  warning ? h("div", { className: "sm-bulkDialogWarn" }, warning) : null,
@@ -1664,7 +2720,12 @@ function isTopmostDialog(z) {
1664
2720
  ),
1665
2721
  h("div", { className: "sm-nativeDialogFooter" },
1666
2722
  h("button", { type: "button", className: "sm-nativeDialogButton sm-nativeDialogCancel", onClick: onCancel }, cancelLabel),
1667
- h("button", { type: "button", className: "sm-nativeDialogButton" + (danger ? " sm-nativeDialogDanger" : " sm-nativeDialogConfirm"), onClick: onExecute }, confirmLabel)
2723
+ h("button", {
2724
+ type: "button",
2725
+ className: "sm-nativeDialogButton" + (danger ? " sm-nativeDialogDanger" : " sm-nativeDialogConfirm"),
2726
+ disabled: props.busy || confirmDisabled,
2727
+ onClick: () => { if (!props.busy && !confirmDisabled) onExecute(); }
2728
+ }, confirmLabel)
1668
2729
  )
1669
2730
  )
1670
2731
  ));
@@ -1674,6 +2735,11 @@ function isTopmostDialog(z) {
1674
2735
  const t = typeof props.t === "function" ? props.t : (key) => key;
1675
2736
  const open = props.open === true;
1676
2737
  const [zIndex] = useState(() => open ? nextDialogZ() : 0);
2738
+ useEffect(() => {
2739
+ if (!open) return;
2740
+ registerDialogZ(zIndex);
2741
+ return () => unregisterDialogZ(zIndex);
2742
+ }, [open, zIndex]);
1677
2743
  const actionLabel = props.actionLabel || "";
1678
2744
  const done = Number.isInteger(props.done) ? props.done : 0;
1679
2745
  const total = Number.isInteger(props.total) ? props.total : 0;
@@ -1721,6 +2787,8 @@ function isTopmostDialog(z) {
1721
2787
  function BatchResultDialog(props) {
1722
2788
  const t = typeof props.t === "function" ? props.t : (key) => key;
1723
2789
  const open = props.open === true;
2790
+ const dialogRef = useRef(null);
2791
+ const layerRef = useRef(null);
1724
2792
  const [zIndex] = useState(() => open ? nextDialogZ() : 0);
1725
2793
  const actionLabel = props.actionLabel || "";
1726
2794
  const items = Array.isArray(props.items) ? props.items : [];
@@ -1740,9 +2808,22 @@ function isTopmostDialog(z) {
1740
2808
  e.preventDefault(); e.stopPropagation(); escStateRef.current.onClose();
1741
2809
  };
1742
2810
  window.addEventListener("keydown", handler);
2811
+ const mountTime = Date.now();
2812
+ const clickHandler = (e) => {
2813
+ if (Date.now() - mountTime < 50) return;
2814
+ if (e.defaultPrevented) return;
2815
+ if (!isTopmostDialog(zIndex)) return;
2816
+ if (isClickInsideDialog(e, dialogRef, layerRef)) return;
2817
+ if (e && typeof e.preventDefault === "function") e.preventDefault();
2818
+ if (e && typeof e.stopPropagation === "function") e.stopPropagation();
2819
+ if (e && typeof e.stopImmediatePropagation === "function") e.stopImmediatePropagation();
2820
+ escStateRef.current.onClose();
2821
+ };
2822
+ window.addEventListener("click", clickHandler, true);
1743
2823
  return () => {
1744
2824
  unregisterDialogZ(zIndex);
1745
2825
  window.removeEventListener("keydown", handler);
2826
+ window.removeEventListener("click", clickHandler, true);
1746
2827
  };
1747
2828
  }, [open, zIndex]);
1748
2829
  if (!open) return null;
@@ -1784,8 +2865,8 @@ function isTopmostDialog(z) {
1784
2865
  : {})
1785
2866
  };
1786
2867
  const portalOverlay = renderInOverlay;
1787
- return portalOverlay(h("div", { className: "sm-nativeDialogLayer sm-bulkDialog" + (hasCoords ? " sm-bulkDialog-anchored" : ""), role: "presentation", style: layerStyle },
1788
- h("section", { className: "sm-nativeDialog", style: { pointerEvents: "auto" }, role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-batch-result-title" },
2868
+ return portalOverlay(h("div", { ref: layerRef, className: "sm-nativeDialogLayer sm-bulkDialog" + (hasCoords ? " sm-bulkDialog-anchored" : ""), role: "presentation", style: layerStyle },
2869
+ h("section", { ref: dialogRef, className: "sm-nativeDialog", style: { pointerEvents: "auto" }, role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-batch-result-title" },
1789
2870
  h("div", { className: "sm-nativeDialogHeader" }, h("h2", { id: "sm-batch-result-title", className: "sm-nativeDialogTitle" }, t("bulk.result.title", { action: actionLabel }))),
1790
2871
  h("div", { className: "sm-nativeDialogBody" },
1791
2872
  renderGroup("bulk.result.successGroup", groups.success),
@@ -1833,6 +2914,8 @@ function isTopmostDialog(z) {
1833
2914
  function BulkTagInputDialog(props) {
1834
2915
  const t = typeof props.t === "function" ? props.t : (key) => key;
1835
2916
  const open = props.open === true;
2917
+ const dialogRef = useRef(null);
2918
+ const layerRef = useRef(null);
1836
2919
  const [zIndex] = useState(() => open ? nextDialogZ() : 0);
1837
2920
  const title = props.title || t("bulk.tagsAdd");
1838
2921
  const description = props.description || "";
@@ -1854,9 +2937,22 @@ function isTopmostDialog(z) {
1854
2937
  e.preventDefault(); e.stopPropagation(); escStateRef.current.onCancel();
1855
2938
  };
1856
2939
  window.addEventListener("keydown", handler);
2940
+ const mountTime = Date.now();
2941
+ const clickHandler = (e) => {
2942
+ if (Date.now() - mountTime < 50) return;
2943
+ if (e.defaultPrevented) return;
2944
+ if (!isTopmostDialog(zIndex)) return;
2945
+ if (isClickInsideDialog(e, dialogRef, layerRef)) return;
2946
+ if (e && typeof e.preventDefault === "function") e.preventDefault();
2947
+ if (e && typeof e.stopPropagation === "function") e.stopPropagation();
2948
+ if (e && typeof e.stopImmediatePropagation === "function") e.stopImmediatePropagation();
2949
+ escStateRef.current.onCancel();
2950
+ };
2951
+ window.addEventListener("click", clickHandler, true);
1857
2952
  return () => {
1858
2953
  unregisterDialogZ(zIndex);
1859
2954
  window.removeEventListener("keydown", handler);
2955
+ window.removeEventListener("click", clickHandler, true);
1860
2956
  };
1861
2957
  }, [open, zIndex]);
1862
2958
  if (!open) return null;
@@ -1883,8 +2979,8 @@ function isTopmostDialog(z) {
1883
2979
  : {})
1884
2980
  };
1885
2981
  const portalOverlay = renderInOverlay;
1886
- return portalOverlay(h("div", { className: "sm-nativeDialogLayer sm-bulkDialog" + (hasCoords ? " sm-bulkDialog-anchored" : ""), role: "presentation", style: layerStyle },
1887
- h("section", { className: "sm-nativeDialog", style: { pointerEvents: "auto" }, role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-bulk-tags-title" },
2982
+ return portalOverlay(h("div", { ref: layerRef, className: "sm-nativeDialogLayer sm-bulkDialog" + (hasCoords ? " sm-bulkDialog-anchored" : ""), role: "presentation", style: layerStyle },
2983
+ h("section", { ref: dialogRef, className: "sm-nativeDialog", style: { pointerEvents: "auto" }, role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-bulk-tags-title" },
1888
2984
  h("div", { className: "sm-nativeDialogHeader" }, h("h2", { id: "sm-bulk-tags-title", className: "sm-nativeDialogTitle" }, title)),
1889
2985
  h("div", { className: "sm-nativeDialogBody" },
1890
2986
  description ? h("div", { className: "sm-bulkDialogSummary" }, description) : null,
@@ -1901,6 +2997,8 @@ function isTopmostDialog(z) {
1901
2997
  function BulkChoiceDialog(props) {
1902
2998
  const t = typeof props.t === "function" ? props.t : (key) => key;
1903
2999
  const open = props.open === true;
3000
+ const dialogRef = useRef(null);
3001
+ const layerRef = useRef(null);
1904
3002
  const [zIndex] = useState(() => open ? nextDialogZ() : 0);
1905
3003
  const title = props.title || "";
1906
3004
  const description = props.description || "";
@@ -1922,9 +3020,22 @@ function isTopmostDialog(z) {
1922
3020
  e.preventDefault(); e.stopPropagation(); escStateRef.current.onCancel();
1923
3021
  };
1924
3022
  window.addEventListener("keydown", handler);
3023
+ const mountTime = Date.now();
3024
+ const clickHandler = (e) => {
3025
+ if (Date.now() - mountTime < 50) return;
3026
+ if (e.defaultPrevented) return;
3027
+ if (!isTopmostDialog(zIndex)) return;
3028
+ if (isClickInsideDialog(e, dialogRef, layerRef)) return;
3029
+ if (e && typeof e.preventDefault === "function") e.preventDefault();
3030
+ if (e && typeof e.stopPropagation === "function") e.stopPropagation();
3031
+ if (e && typeof e.stopImmediatePropagation === "function") e.stopImmediatePropagation();
3032
+ escStateRef.current.onCancel();
3033
+ };
3034
+ window.addEventListener("click", clickHandler, true);
1925
3035
  return () => {
1926
3036
  unregisterDialogZ(zIndex);
1927
3037
  window.removeEventListener("keydown", handler);
3038
+ window.removeEventListener("click", clickHandler, true);
1928
3039
  };
1929
3040
  }, [open, zIndex]);
1930
3041
  if (!open) return null;
@@ -1947,8 +3058,8 @@ function isTopmostDialog(z) {
1947
3058
  : {})
1948
3059
  };
1949
3060
  const portalOverlay = renderInOverlay;
1950
- return portalOverlay(h("div", { className: "sm-nativeDialogLayer sm-bulkDialog" + (hasCoords ? " sm-bulkDialog-anchored" : ""), role: "presentation", style: layerStyle },
1951
- h("section", { className: "sm-nativeDialog", style: { pointerEvents: "auto" }, role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-bulk-choice-title" },
3061
+ return portalOverlay(h("div", { ref: layerRef, className: "sm-nativeDialogLayer sm-bulkDialog" + (hasCoords ? " sm-bulkDialog-anchored" : ""), role: "presentation", style: layerStyle },
3062
+ h("section", { ref: dialogRef, className: "sm-nativeDialog", style: { pointerEvents: "auto" }, role: "dialog", "aria-modal": "true", "aria-labelledby": "sm-bulk-choice-title" },
1952
3063
  h("div", { className: "sm-nativeDialogHeader" }, h("h2", { id: "sm-bulk-choice-title", className: "sm-nativeDialogTitle" }, title)),
1953
3064
  h("div", { className: "sm-nativeDialogBody" },
1954
3065
  description ? h("div", { className: "sm-bulkDialogSummary" }, description) : null,
@@ -2174,6 +3285,8 @@ function isTopmostDialog(z) {
2174
3285
  const onConfirm = typeof props.onConfirm === "function" ? props.onConfirm : () => {};
2175
3286
  const busy = props.busy === true;
2176
3287
  const danger = props.danger === true;
3288
+ const dialogRef = useRef(null);
3289
+ const layerRef = useRef(null);
2177
3290
  // Claim a fresh z-index so this popup sits above any earlier popup.
2178
3291
  const [zIndex] = useState(() => open ? nextDialogZ() : 0);
2179
3292
  const title = props.title || "";
@@ -2209,9 +3322,23 @@ function isTopmostDialog(z) {
2209
3322
  if (ae && typeof ae.blur === "function" && ae !== document.body) ae.blur();
2210
3323
  };
2211
3324
  window.addEventListener("keydown", handler);
3325
+ const mountTime = Date.now();
3326
+ const clickHandler = (e) => {
3327
+ if (Date.now() - mountTime < 50) return;
3328
+ if (e.defaultPrevented) return;
3329
+ if (escStateRef.current.busy) return;
3330
+ if (!isTopmostDialog(effectiveZ)) return;
3331
+ if (isClickInsideDialog(e, dialogRef, layerRef)) return;
3332
+ if (e && typeof e.preventDefault === "function") e.preventDefault();
3333
+ if (e && typeof e.stopPropagation === "function") e.stopPropagation();
3334
+ if (e && typeof e.stopImmediatePropagation === "function") e.stopImmediatePropagation();
3335
+ escStateRef.current.onCancel();
3336
+ };
3337
+ window.addEventListener("click", clickHandler, true);
2212
3338
  return () => {
2213
3339
  unregisterDialogZ(effectiveZ);
2214
3340
  window.removeEventListener("keydown", handler);
3341
+ window.removeEventListener("click", clickHandler, true);
2215
3342
  };
2216
3343
  }, [open, zIndex]);
2217
3344
 
@@ -2239,13 +3366,17 @@ function isTopmostDialog(z) {
2239
3366
  : {})
2240
3367
  };
2241
3368
 
3369
+ const confirmDisabled = props.confirmDisabled === true;
3370
+
2242
3371
  return renderInOverlay(
2243
3372
  h("div", {
3373
+ ref: layerRef,
2244
3374
  className: "sm-nativeDialogLayer sm-confirmDialog" + (hasCoords ? " sm-confirmDialog-anchored" : ""),
2245
3375
  style: layerStyle,
2246
3376
  role: "presentation"
2247
3377
  },
2248
3378
  h("section", {
3379
+ ref: dialogRef,
2249
3380
  className: "sm-nativeDialog",
2250
3381
  style: { pointerEvents: "auto" },
2251
3382
  role: "dialog",
@@ -2269,8 +3400,8 @@ function isTopmostDialog(z) {
2269
3400
  h("button", {
2270
3401
  type: "button",
2271
3402
  className: "sm-nativeDialogButton" + (danger ? " sm-nativeDialogDanger" : " sm-nativeDialogConfirm"),
2272
- disabled: busy,
2273
- onClick: () => { if (!busy) onConfirm(); }
3403
+ disabled: busy || confirmDisabled,
3404
+ onClick: () => { if (!busy && !confirmDisabled) onConfirm(); }
2274
3405
  }, confirmLabel)
2275
3406
  )
2276
3407
  )
@@ -2283,6 +3414,8 @@ function isTopmostDialog(z) {
2283
3414
  const onCancel = typeof props.onCancel === "function" ? props.onCancel : () => {};
2284
3415
  const onConfirm = typeof props.onConfirm === "function" ? props.onConfirm : () => {};
2285
3416
  const busy = props.busy === true;
3417
+ const dialogRef = useRef(null);
3418
+ const layerRef = useRef(null);
2286
3419
  const title = props.title || t("confirm.move.title");
2287
3420
  const description = props.description || "";
2288
3421
  const currentWorkspaceId = props.currentWorkspaceId || "";
@@ -2331,9 +3464,23 @@ function isTopmostDialog(z) {
2331
3464
  if (ae && typeof ae.blur === "function" && ae !== document.body) ae.blur();
2332
3465
  };
2333
3466
  window.addEventListener("keydown", handler);
3467
+ const mountTime = Date.now();
3468
+ const clickHandler = (e) => {
3469
+ if (Date.now() - mountTime < 50) return;
3470
+ if (e.defaultPrevented) return;
3471
+ if (escStateRef.current.busy) return;
3472
+ if (!isTopmostDialog(effectiveZ)) return;
3473
+ if (isClickInsideDialog(e, dialogRef, layerRef)) return;
3474
+ if (e && typeof e.preventDefault === "function") e.preventDefault();
3475
+ if (e && typeof e.stopPropagation === "function") e.stopPropagation();
3476
+ if (e && typeof e.stopImmediatePropagation === "function") e.stopImmediatePropagation();
3477
+ escStateRef.current.onCancel();
3478
+ };
3479
+ window.addEventListener("click", clickHandler, true);
2334
3480
  return () => {
2335
3481
  unregisterDialogZ(effectiveZ);
2336
3482
  window.removeEventListener("keydown", handler);
3483
+ window.removeEventListener("click", clickHandler, true);
2337
3484
  };
2338
3485
  }, [open, zIndex]);
2339
3486
  if (!open) return null;
@@ -2391,12 +3538,14 @@ function isTopmostDialog(z) {
2391
3538
  };
2392
3539
  return renderInOverlay(
2393
3540
  h("div", {
3541
+ ref: layerRef,
2394
3542
  className: "sm-nativeDialogLayer sm-confirmDialog" + (hasCoords ? " sm-confirmDialog-anchored" : ""),
2395
3543
  style: layerStyle,
2396
3544
  role: "presentation",
2397
3545
  onKeyDown
2398
3546
  },
2399
3547
  h("section", {
3548
+ ref: dialogRef,
2400
3549
  className: "sm-nativeDialog",
2401
3550
  style: { pointerEvents: "auto" },
2402
3551
  role: "dialog",
@@ -2432,6 +3581,8 @@ function isTopmostDialog(z) {
2432
3581
  const onCancel = typeof props.onCancel === "function" ? props.onCancel : () => {};
2433
3582
  const onRun = typeof props.onRun === "function" ? props.onRun : () => {};
2434
3583
  const busy = props.busy === true;
3584
+ const dialogRef = useRef(null);
3585
+ const layerRef = useRef(null);
2435
3586
  const sessionId = props.sessionId || "";
2436
3587
  const description = props.description || "";
2437
3588
  const fallbackPreset = props.currentPreset || "";
@@ -2490,9 +3641,23 @@ function isTopmostDialog(z) {
2490
3641
  if (ae && typeof ae.blur === "function" && ae !== document.body) ae.blur();
2491
3642
  };
2492
3643
  window.addEventListener("keydown", handler);
3644
+ const mountTime = Date.now();
3645
+ const clickHandler = (e) => {
3646
+ if (Date.now() - mountTime < 50) return;
3647
+ if (e.defaultPrevented) return;
3648
+ if (escStateRef.current.busy) return;
3649
+ if (!isTopmostDialog(effectiveZ)) return;
3650
+ if (isClickInsideDialog(e, dialogRef, layerRef)) return;
3651
+ if (e && typeof e.preventDefault === "function") e.preventDefault();
3652
+ if (e && typeof e.stopPropagation === "function") e.stopPropagation();
3653
+ if (e && typeof e.stopImmediatePropagation === "function") e.stopImmediatePropagation();
3654
+ escStateRef.current.onCancel();
3655
+ };
3656
+ window.addEventListener("click", clickHandler, true);
2493
3657
  return () => {
2494
3658
  unregisterDialogZ(effectiveZ);
2495
3659
  window.removeEventListener("keydown", handler);
3660
+ window.removeEventListener("click", clickHandler, true);
2496
3661
  };
2497
3662
  }, [open, zIndex]);
2498
3663
  if (!open) return null;
@@ -2549,11 +3714,13 @@ function isTopmostDialog(z) {
2549
3714
  };
2550
3715
  return renderInOverlay(
2551
3716
  h("div", {
3717
+ ref: layerRef,
2552
3718
  className: "sm-nativeDialogLayer sm-confirmDialog" + (hasCoords ? " sm-confirmDialog-anchored" : ""),
2553
3719
  role: "presentation",
2554
3720
  style: layerStyle
2555
3721
  },
2556
3722
  h("section", {
3723
+ ref: dialogRef,
2557
3724
  className: "sm-nativeDialog",
2558
3725
  style: { pointerEvents: "auto" },
2559
3726
  role: "dialog",
@@ -2582,7 +3749,7 @@ function isTopmostDialog(z) {
2582
3749
  ));
2583
3750
  }
2584
3751
 
2585
- const CURRENT_VERSION = "0.6.4";
3752
+ const CURRENT_VERSION = "0.7.1";
2586
3753
 
2587
3754
  function compareSemver(v1, v2) {
2588
3755
  if (typeof v1 !== "string" || typeof v2 !== "string") return 0;
@@ -2643,7 +3810,11 @@ function isTopmostDialog(z) {
2643
3810
  application: null,
2644
3811
  dialogOpen: false,
2645
3812
  checkedAt: null,
3813
+ lastAttemptAt: null,
2646
3814
  },
3815
+ CHECK_INTERVAL_MS: 6 * 60 * 60 * 1000,
3816
+ RETRY_COOLDOWN_MS: 30 * 60 * 1000,
3817
+ lastFailedAt: null,
2647
3818
  listeners: new Set(),
2648
3819
  getSnapshot() { return this.state; },
2649
3820
  subscribe(cb) {
@@ -2678,6 +3849,29 @@ function isTopmostDialog(z) {
2678
3849
  } catch (_) {}
2679
3850
  this._notify();
2680
3851
  },
3852
+ shouldAutoCheck(now = Date.now()) {
3853
+ if (!this.getAutoCheck()) return false;
3854
+ if (this.state.status === "checking" || this.state.status === "updating") return false;
3855
+ if (this.state.status === "available") return false;
3856
+ if (this.lastFailedAt && (now - this.lastFailedAt < this.RETRY_COOLDOWN_MS)) {
3857
+ return false;
3858
+ }
3859
+ if (this.state.checkedAt && (now - this.state.checkedAt < this.CHECK_INTERVAL_MS)) {
3860
+ return false;
3861
+ }
3862
+ return true;
3863
+ },
3864
+ async maybeAutoCheck(options = {}) {
3865
+ const now = typeof options.now === "number" ? options.now : Date.now();
3866
+ if (!this.shouldAutoCheck(now)) return this.state;
3867
+ return this.checkForUpdate({
3868
+ silent: true,
3869
+ openOnFound: false,
3870
+ openDialog: false,
3871
+ ...options,
3872
+ manual: false
3873
+ });
3874
+ },
2681
3875
  // Dialog opacity preference (Issue #23). Reuses the same localStorage
2682
3876
  // backed preference store as autoCheck / registry instead of adding a
2683
3877
  // second storage mechanism.
@@ -2740,7 +3934,17 @@ function isTopmostDialog(z) {
2740
3934
  }
2741
3935
  this._notify();
2742
3936
 
3937
+ const timeoutMs = typeof options.timeoutMs === "number" ? options.timeoutMs : 10000;
3938
+ let timeoutTimer = null;
3939
+ const abortController = typeof AbortController !== "undefined" ? new AbortController() : null;
3940
+
2743
3941
  try {
3942
+ if (abortController) {
3943
+ timeoutTimer = setTimeout(() => {
3944
+ abortController.abort(new Error("检查更新超时"));
3945
+ }, timeoutMs);
3946
+ }
3947
+
2744
3948
  let res = null;
2745
3949
  if (typeof window !== "undefined" && window.__DSH_SM_TEST_UPDATE__) {
2746
3950
  const testOverride = window.__DSH_SM_TEST_UPDATE__;
@@ -2754,12 +3958,16 @@ function isTopmostDialog(z) {
2754
3958
  } else {
2755
3959
  const registryUrl = this.getRegistryUrl();
2756
3960
  const registryParam = encodeURIComponent(registryUrl);
3961
+ const fetchOpts = abortController ? { signal: abortController.signal } : undefined;
2757
3962
  try {
2758
- res = await fetchApi("/update/check?registry=" + registryParam);
3963
+ res = await fetchApi("/update/check?registry=" + registryParam, fetchOpts);
2759
3964
  } catch (fetchErr) {
3965
+ if (abortController?.signal?.aborted || fetchErr.name === "AbortError" || fetchErr.message?.includes("超时")) {
3966
+ throw new Error("检查更新超时");
3967
+ }
2760
3968
  try {
2761
3969
  const base = registryUrl.endsWith("/") ? registryUrl : registryUrl + "/";
2762
- const npmRes = await fetch(base + "dsh-session-manager/latest");
3970
+ const npmRes = await fetch(base + "dsh-session-manager/latest", fetchOpts);
2763
3971
  if (npmRes.ok) {
2764
3972
  const npmData = await npmRes.json();
2765
3973
  if (npmData && npmData.version) {
@@ -2769,7 +3977,11 @@ function isTopmostDialog(z) {
2769
3977
  res = { ok: true, result: { currentVersion: cur, latestVersion: lat, hasUpdate } };
2770
3978
  }
2771
3979
  }
2772
- } catch (_) {}
3980
+ } catch (_) {
3981
+ if (abortController?.signal?.aborted || fetchErr.name === "AbortError" || fetchErr.message?.includes("超时")) {
3982
+ throw new Error("检查更新超时");
3983
+ }
3984
+ }
2773
3985
  if (!res) throw fetchErr;
2774
3986
  }
2775
3987
  }
@@ -2785,7 +3997,10 @@ function isTopmostDialog(z) {
2785
3997
 
2786
3998
  this.state.currentVersion = currentVersion;
2787
3999
  this.state.latestVersion = latestVersion;
2788
- this.state.checkedAt = Date.now();
4000
+ const now = Date.now();
4001
+ this.state.checkedAt = now;
4002
+ this.state.lastAttemptAt = now;
4003
+ this.lastFailedAt = null;
2789
4004
 
2790
4005
  if (hasUpdate) {
2791
4006
  this.state.status = "available";
@@ -2801,13 +4016,18 @@ function isTopmostDialog(z) {
2801
4016
  this._notify();
2802
4017
  return this.state;
2803
4018
  } catch (err) {
4019
+ const now = Date.now();
2804
4020
  this.state.status = "error";
2805
- this.state.error = err.message || "检查更新失败";
4021
+ this.state.error = (abortController?.signal?.aborted ? "检查更新超时" : (err.message || "检查更新失败"));
4022
+ this.state.lastAttemptAt = now;
4023
+ this.lastFailedAt = now;
2806
4024
  if (shouldOpenDialog) {
2807
4025
  this.state.dialogOpen = true;
2808
4026
  }
2809
4027
  this._notify();
2810
4028
  return this.state;
4029
+ } finally {
4030
+ if (timeoutTimer) clearTimeout(timeoutTimer);
2811
4031
  }
2812
4032
  },
2813
4033
  async startUpdate(options = {}) {
@@ -2871,8 +4091,15 @@ function isTopmostDialog(z) {
2871
4091
  listeners: new Set(),
2872
4092
  getSnapshot() { return this.open; },
2873
4093
  subscribe(cb) { this.listeners.add(cb); return () => this.listeners.delete(cb); },
2874
- set(v) { if (this.open === v) return; this.open = v; for (const fn of [...this.listeners]) fn(); },
2875
- toggle() { this.open = !this.open; for (const fn of [...this.listeners]) fn(); },
4094
+ set(v) {
4095
+ if (this.open === v) return;
4096
+ this.open = v;
4097
+ if (this.open) {
4098
+ try { updateStore.maybeAutoCheck().catch(() => {}); } catch (_) {}
4099
+ }
4100
+ for (const fn of [...this.listeners]) fn();
4101
+ },
4102
+ toggle() { this.set(!this.open); },
2876
4103
  // Pending action queued by the title-bar buttons (e.g. 移动至工作区).
2877
4104
  // The SessionManagerPanel consumes this on mount so it can pre-open
2878
4105
  // the matching inner dialog (move / delete / migrate) next to itself.
@@ -2886,13 +4113,8 @@ function isTopmostDialog(z) {
2886
4113
  const lastBatchContext = { action: null, ids: [] };
2887
4114
 
2888
4115
  const DEFAULT_SORT = "updated-desc";
2889
- const SORT_ORDERS = ["updated-desc", "updated-asc", "created-desc", "created-asc", "priority"];
4116
+ const SORT_ORDERS = ["updated-desc", "updated-asc", "created-desc", "created-asc", "name-asc", "name-desc", "priority"];
2890
4117
  const workspaceValue = (id) => "workspace:" + id;
2891
- const pathKey = (value) => {
2892
- if (typeof value !== "string" || value === "") return "";
2893
- const path = value.replace(/\\/g, "/").replace(/\/+$/, "") || "/";
2894
- return /^[a-z]:(?:\/|$)/i.test(path) || path.startsWith("//") ? path.toLowerCase() : path;
2895
- };
2896
4118
  const sessionTime = (value) => {
2897
4119
  if (typeof value !== "number" && typeof value !== "string") return null;
2898
4120
  if (typeof value === "string" && value.trim() === "") return null;
@@ -2926,17 +4148,33 @@ function isTopmostDialog(z) {
2926
4148
  };
2927
4149
 
2928
4150
  function SessionManagerPanel(props) {
2929
- const t = props.t;
4151
+ const t = typeof props.t === "function" ? props.t : (k => k);
2930
4152
  const open = props.open;
2931
4153
  const onClose = props.onClose;
2932
- const useSessions = props.useSessions;
2933
- const useWorkspaces = props.useWorkspaces;
2934
4154
  const onOpen = props.onOpen;
2935
4155
  const onArchive = props.onArchive;
2936
4156
  const onUnarchive = props.onUnarchive;
2937
4157
  const onRemove = props.onRemove;
2938
4158
  const onMove = props.onMove;
2939
4159
  const onMigrate = props.onMigrate;
4160
+ const defaultUseSessions = (selector = s => s) => {
4161
+ const model = activeClientCtx?.sessions?.list;
4162
+ return useSyncExternalStore(
4163
+ (cb) => (model?.subscribe ? model.subscribe(cb) : () => {}),
4164
+ () => (model?.getSnapshot ? selector(model.getSnapshot()) : selector({ ids: [], byId: {} })),
4165
+ () => (model?.getSnapshot ? selector(model.getSnapshot()) : selector({ ids: [], byId: {} }))
4166
+ );
4167
+ };
4168
+ const defaultUseWorkspaces = (selector = s => s) => {
4169
+ const model = activeClientCtx?.workspaces?.list;
4170
+ return useSyncExternalStore(
4171
+ (cb) => (model?.subscribe ? model.subscribe(cb) : () => {}),
4172
+ () => (model?.getSnapshot ? selector(model.getSnapshot()) : selector({ items: [] })),
4173
+ () => (model?.getSnapshot ? selector(model.getSnapshot()) : selector({ items: [] }))
4174
+ );
4175
+ };
4176
+ const useSessions = typeof props.useSessions === "function" ? props.useSessions : defaultUseSessions;
4177
+ const useWorkspaces = typeof props.useWorkspaces === "function" ? props.useWorkspaces : defaultUseWorkspaces;
2940
4178
  const annotations = useAnnotations(true);
2941
4179
  const marksReady = annotations.loaded && !annotations.error;
2942
4180
  const updateState = useSyncExternalStore(
@@ -2950,9 +4188,22 @@ function isTopmostDialog(z) {
2950
4188
  const [priorityFilter, setPriorityFilter] = useState("all");
2951
4189
  const [annotationFor, setAnnotationFor] = useState(null);
2952
4190
  const [annotationAnchor, setAnnotationAnchor] = useState(null);
2953
- const list = useSessions((s) => s);
2954
- const workspaceSnapshot = useWorkspaces((s) => s);
4191
+ const list = (typeof useSessions === "function" ? useSessions((s) => s) : null) || { ids: [], byId: {} };
4192
+ const workspaceSnapshot = (typeof useWorkspaces === "function" ? useWorkspaces((s) => s) : null) || { items: [] };
2955
4193
  const archivedIds = workspaceSnapshot.archivedSessionIds || [];
4194
+ const [panelTab, setPanelTab] = useState("sessions");
4195
+ const [wsQuery, setWsQuery] = useState("");
4196
+ const [wsFilter, setWsFilter] = useState("all");
4197
+ const [wsSort, setWsSort] = useState("updated-desc");
4198
+ const [wsSelectedIds, setWsSelectedIds] = useState(() => new Set());
4199
+ const [wsSelectModeEnabled, setWsSelectModeEnabled] = useState(false);
4200
+ const [wsBulkBusy, setWsBulkBusy] = useState(false);
4201
+ const [wsLastClickedRank, setWsLastClickedRank] = useState(null);
4202
+ const archiveVersion = useSyncExternalStore(
4203
+ (cb) => workspaceArchiveStore.subscribe(cb),
4204
+ () => workspaceArchiveStore.getVersion(),
4205
+ () => workspaceArchiveStore.getVersion()
4206
+ );
2956
4207
  const [filter, setFilter] = useState("all");
2957
4208
  const [query, setQuery] = useState("");
2958
4209
  const [workspaceFilter, setWorkspaceFilter] = useState("all");
@@ -2962,14 +4213,39 @@ function isTopmostDialog(z) {
2962
4213
  const [now, setNow] = useState(() => Date.now());
2963
4214
  const [moveFor, setMoveFor] = useState(null);
2964
4215
  const [confirmFor, setConfirmFor] = useState(null);
4216
+ const [confirmPreviewState, setConfirmPreviewState] = useState({ loading: false, error: null, subagentCount: 0 });
4217
+ useEffect(() => {
4218
+ if (!confirmFor || !confirmFor.id) {
4219
+ setConfirmPreviewState({ loading: false, error: null, subagentCount: 0 });
4220
+ return;
4221
+ }
4222
+ let alive = true;
4223
+ setConfirmPreviewState({ loading: true, error: null, subagentCount: 0 });
4224
+ fetchApi("/delete-preview?sessionId=" + encodeURIComponent(confirmFor.id))
4225
+ .then((res) => {
4226
+ if (!alive) return;
4227
+ if (res && res.ok && res.result && typeof res.result.subagentCount === "number") {
4228
+ setConfirmPreviewState({ loading: false, error: null, subagentCount: res.result.subagentCount });
4229
+ } else {
4230
+ setConfirmPreviewState({ loading: false, error: res?.error || "获取子智能体预览失败", subagentCount: 0 });
4231
+ }
4232
+ })
4233
+ .catch((err) => {
4234
+ if (!alive) return;
4235
+ setConfirmPreviewState({ loading: false, error: err instanceof Error ? err.message : String(err), subagentCount: 0 });
4236
+ });
4237
+ return () => { alive = false; };
4238
+ }, [confirmFor?.id]);
2965
4239
  const [workspaces, setWorkspaces] = useState([]);
2966
4240
  const [createdTimes, setCreatedTimes] = useState({});
4241
+ const [updatedTimes, setUpdatedTimes] = useState({});
2967
4242
  const [workspacesLoading, setWorkspacesLoading] = useState(true);
2968
4243
  const [workspacesError, setWorkspacesError] = useState(null);
2969
4244
  const [workspaceRefresh, setWorkspaceRefresh] = useState(0);
2970
4245
  const [migrating, setMigrating] = useState(false);
2971
4246
  const [migrateFor, setMigrateFor] = useState(null);
2972
- const sessionMembershipKey = useMemo(() => [...list.ids].sort().join("\0"), [list.ids]);
4247
+ const sessionMembershipKey = useMemo(() => (Array.isArray(list?.ids) ? [...list.ids].sort().join("\0") : ""), [list?.ids]);
4248
+ let isSessionWorkspaceArchived = () => false;
2973
4249
  // ----- Bulk-action selection state -----
2974
4250
  const [selectedIds, setSelectedIds] = useState(() => new Set());
2975
4251
  const [lastClickedRank, setLastClickedRank] = useState(null);
@@ -2997,6 +4273,10 @@ function isTopmostDialog(z) {
2997
4273
  setBulkPresetOptions([]);
2998
4274
  setBulkBusy(false);
2999
4275
  setSelectModeEnabled(false);
4276
+ setWsSelectedIds(new Set());
4277
+ setWsSelectModeEnabled(false);
4278
+ setWsBulkBusy(false);
4279
+ setWsLastClickedRank(null);
3000
4280
  }
3001
4281
  }, [open]);
3002
4282
  const toggleSelectMode = () => {
@@ -3111,6 +4391,15 @@ function isTopmostDialog(z) {
3111
4391
  if (ids.length === 0) return;
3112
4392
  if (ids.length > 200) { setError(t("bulk.error.tooMany")); return; }
3113
4393
  const sessions = selectedSessions;
4394
+ if (action === "unarchive") {
4395
+ const hasBlocked = sessions.some(s => isSessionWorkspaceArchived(s));
4396
+ if (hasBlocked) {
4397
+ if (typeof window !== "undefined" && typeof window.alert === "function") {
4398
+ window.alert(t("error.workspaceArchived"));
4399
+ }
4400
+ return;
4401
+ }
4402
+ }
3114
4403
  const resolvedLabel = actionLabel || ({
3115
4404
  archive: t("bulk.archive"), unarchive: t("bulk.unarchive"), delete: t("bulk.delete"),
3116
4405
  move: t("bulk.move"), "preset-migrate": t("bulk.presetMigrate"),
@@ -3135,7 +4424,59 @@ function isTopmostDialog(z) {
3135
4424
  };
3136
4425
  const summaryAction = actionSummaryMap[action] || resolvedLabel;
3137
4426
  if (needsConfirm) {
3138
- setBulkPreview({ action, payload, actionLabel: summaryAction, sessions, warning: action === "delete" ? t("bulk.dialog.warning.delete") : "" });
4427
+ const baseWarning = action === "delete" ? t("bulk.dialog.warning.delete") : "";
4428
+ if (action === "delete") {
4429
+ setBulkPreview({
4430
+ action,
4431
+ payload,
4432
+ actionLabel: summaryAction,
4433
+ sessions,
4434
+ warning: baseWarning,
4435
+ loading: true,
4436
+ error: null
4437
+ });
4438
+ fetchApi("/delete-preview?sessionIds=" + encodeURIComponent(ids.join(",")))
4439
+ .then((res) => {
4440
+ if (res && res.ok && res.result && typeof res.result.subagentCount === "number") {
4441
+ const subCount = res.result.subagentCount;
4442
+ setBulkPreview((prev) => {
4443
+ if (!prev || prev.action !== "delete") return prev;
4444
+ const subText = subCount > 0 ? t("confirm.delete.subagents", { count: subCount }) : "";
4445
+ return {
4446
+ ...prev,
4447
+ loading: false,
4448
+ error: null,
4449
+ warning: subText ? `${subText}\n${baseWarning}` : baseWarning
4450
+ };
4451
+ });
4452
+ } else {
4453
+ const errMsg = (res && res.error) || t("confirm.delete.previewError") || "检查关联子智能体会话失败,请稍后重试";
4454
+ setBulkPreview((prev) => {
4455
+ if (!prev || prev.action !== "delete") return prev;
4456
+ return {
4457
+ ...prev,
4458
+ loading: false,
4459
+ error: errMsg,
4460
+ warning: `${errMsg}\n${baseWarning}`
4461
+ };
4462
+ });
4463
+ }
4464
+ })
4465
+ .catch((err) => {
4466
+ const errMsg = (err && err.message) || t("confirm.delete.previewError") || "检查关联子智能体会话失败,请稍后重试";
4467
+ setBulkPreview((prev) => {
4468
+ if (!prev || prev.action !== "delete") return prev;
4469
+ return {
4470
+ ...prev,
4471
+ loading: false,
4472
+ error: errMsg,
4473
+ warning: `${errMsg}\n${baseWarning}`
4474
+ };
4475
+ });
4476
+ });
4477
+ return;
4478
+ }
4479
+ setBulkPreview({ action, payload, actionLabel: summaryAction, sessions, warning: baseWarning });
3139
4480
  return;
3140
4481
  }
3141
4482
  void executeBulk({ action, payload, actionLabel: resolvedLabel, ids, sessions });
@@ -3213,15 +4554,20 @@ function isTopmostDialog(z) {
3213
4554
  if (!open) return;
3214
4555
  let alive = true;
3215
4556
  const controller = new AbortController();
3216
- setWorkspacesLoading(true);
4557
+ if (workspaces.length === 0) setWorkspacesLoading(true);
3217
4558
  setWorkspacesError(null);
3218
4559
  fetchApi("/workspaces", { signal: controller.signal }).then((data) => {
3219
4560
  if (!data?.ok || !Array.isArray(data.result?.workspaces)) throw new Error(data?.error || "Invalid workspace response");
3220
4561
  const next = data.result.workspaces.filter(workspace => workspace && typeof workspace.id === "string");
3221
4562
  if (!alive) return;
4563
+ if (data.result.workspaceArchives) {
4564
+ workspaceArchiveStore.syncFromHost(data.result.workspaceArchives);
4565
+ }
3222
4566
  setWorkspaces(next);
3223
4567
  const times = data.result.sessionCreatedAt;
3224
4568
  setCreatedTimes(times && typeof times === "object" && !Array.isArray(times) ? times : {});
4569
+ const updated = data.result.sessionUpdatedAt;
4570
+ setUpdatedTimes(updated && typeof updated === "object" && !Array.isArray(updated) ? updated : {});
3225
4571
  setWorkspaceFilter(value => value.startsWith("workspace:") && !next.some(workspace => workspaceValue(workspace.id) === value) ? "all" : value);
3226
4572
  }).catch(error => {
3227
4573
  if (alive) setWorkspacesError(error instanceof Error ? error.message : String(error));
@@ -3242,8 +4588,26 @@ function isTopmostDialog(z) {
3242
4588
  }, [open]);
3243
4589
  const archived = useMemo(() => new Set(archivedIds), [archivedIds]);
3244
4590
  const workspaceIndex = useMemo(() => indexWorkspaces(workspaces), [workspaces]);
3245
- const allRows = useMemo(() => list.ids.map(id => list.byId[id])
3246
- .filter(session => session != null && session.origin !== "subagent" && (!session.blank || session.id === list.current)), [list]);
4591
+ isSessionWorkspaceArchived = (session) => {
4592
+ if (!session) return false;
4593
+ if (session.workspaceId && workspaceArchiveStore.isArchived(session.workspaceId)) return true;
4594
+ const memberships = rowWorkspaces(session, workspaceIndex);
4595
+ for (const wsId of memberships) {
4596
+ if (workspaceArchiveStore.isArchived(wsId)) return true;
4597
+ }
4598
+ for (const [, rec] of workspaceArchiveStore.getAllRecords()) {
4599
+ if (rec.archived === true && Array.isArray(rec.sessionIds) && rec.sessionIds.includes(session.id)) {
4600
+ return true;
4601
+ }
4602
+ }
4603
+ return false;
4604
+ };
4605
+ const allRows = useMemo(() => {
4606
+ const ids = Array.isArray(list?.ids) ? list.ids : [];
4607
+ const byId = list?.byId || {};
4608
+ return ids.map(id => byId[id])
4609
+ .filter(session => session != null && session.origin !== "subagent" && (!session.blank || session.id === list?.current));
4610
+ }, [list]);
3247
4611
  const availableTags = useMemo(() => {
3248
4612
  const tags = new Map();
3249
4613
  for (const session of allRows) for (const tag of annotationOf(annotations, session.id).tags) if (!tags.has(tag.toLowerCase())) tags.set(tag.toLowerCase(), tag);
@@ -3273,6 +4637,16 @@ function isTopmostDialog(z) {
3273
4637
  }
3274
4638
  return true;
3275
4639
  });
4640
+ if (sortOrder.startsWith("name-")) {
4641
+ const dir = sortOrder === "name-asc" ? 1 : -1;
4642
+ const nameOf = s => (s.blank ? t("panel.session.new") : (s.displayTitle || s.id || "")).toLowerCase();
4643
+ return selected.map((session, rank) => ({ session, rank, name: nameOf(session) }))
4644
+ .sort((a, b) => {
4645
+ const cmp = a.name.localeCompare(b.name);
4646
+ return cmp !== 0 ? dir * cmp : a.rank - b.rank;
4647
+ })
4648
+ .map(item => item.session);
4649
+ }
3276
4650
  const field = sortOrder.startsWith("created-") ? "createdAt" : "updatedAt";
3277
4651
  const direction = sortOrder === "priority" || sortOrder.endsWith("-asc") ? 1 : -1;
3278
4652
  // Sort a derived array only. Equal timestamps preserve source order;
@@ -3308,8 +4682,6 @@ function isTopmostDialog(z) {
3308
4682
  }, [currentFilterIds]);
3309
4683
  const creationTimeUnavailable = sortOrder.startsWith("created-") && allRows.length > 0
3310
4684
  && !allRows.some(session => (sessionTime(session.createdAt) ?? sessionTime(createdTimes[session.id])) !== null);
3311
- const hasViewChanges = onlyFavorite || onlyReview || tagFilter !== "all" || priorityFilter !== "all" || query !== "" || filter !== "all" || workspaceFilter !== "all" || sortOrder !== DEFAULT_SORT;
3312
- const resetView = () => { setQuery(""); setFilter("all"); setWorkspaceFilter("all"); setSortOrder(DEFAULT_SORT); setOnlyFavorite(false); setOnlyReview(false); setTagFilter("all"); setPriorityFilter("all"); };
3313
4685
  const runRow = async (sessionId, fn) => {
3314
4686
  if (busyId !== null) return;
3315
4687
  setBusyId(sessionId);
@@ -3329,12 +4701,24 @@ function isTopmostDialog(z) {
3329
4701
  "aria-pressed": filter === id,
3330
4702
  onClick: () => setFilter(id)
3331
4703
  }, label);
4704
+ const hasViewChanges = query !== "" || filter !== "all" || workspaceFilter !== "all" || sortOrder !== DEFAULT_SORT || onlyFavorite || onlyReview || tagFilter !== "all" || priorityFilter !== "all";
4705
+ const resetView = () => {
4706
+ setQuery("");
4707
+ setFilter("all");
4708
+ setWorkspaceFilter("all");
4709
+ setSortOrder(DEFAULT_SORT);
4710
+ setOnlyFavorite(false);
4711
+ setOnlyReview(false);
4712
+ setTagFilter("all");
4713
+ setPriorityFilter("all");
4714
+ };
3332
4715
  const renderRow = (s, rank) => {
3333
4716
  const isArchived = archived.has(s.id);
3334
4717
  const isCurrent = s.id === list.current;
3335
4718
  const busy = busyId === s.id;
3336
4719
  const mark = annotationOf(annotations, s.id);
3337
4720
  const isSelected = selectedIds.has(s.id);
4721
+ const isWsArchived = isSessionWorkspaceArchived(s);
3338
4722
  const checkbox = (selectedIds.size === 0 && !selectModeEnabled)
3339
4723
  ? null
3340
4724
  : h("label", { className: "sm-bulkRowCheckbox", onClick: (e) => e.stopPropagation() },
@@ -3355,6 +4739,7 @@ function isTopmostDialog(z) {
3355
4739
  h("div", { className: "sm-rowMain" },
3356
4740
  h("div", { className: "sm-rowTitle" },
3357
4741
  h("span", null, s.blank ? t("panel.session.new") : (s.displayTitle || s.id)),
4742
+ isWsArchived ? h("span", { className: "sm-badge sm-badgeWorkspaceArchived" }, t("panel.workspaceArchived")) : null,
3358
4743
  isArchived ? h("span", { className: "sm-badge sm-badgeArchived" }, t("panel.archived")) : null,
3359
4744
  s.running ? h("span", { className: "sm-badge sm-badgeRunning" }, t("panel.running")) : null,
3360
4745
  isCurrent ? h("span", { className: "sm-badge sm-badgeCurrent" }, t("panel.current")) : null
@@ -3368,8 +4753,8 @@ function isTopmostDialog(z) {
3368
4753
  ),
3369
4754
  h("div", { className: "sm-rowMeta" }, shortPath(s.cwd) + (s.cwd ? " · " : "") + formatRelative(s.updatedAt, now)),
3370
4755
  h("div", { className: "sm-rowActions" },
3371
- h("button", { type: "button", className: "sm-rowBtn", disabled: busy, onClick: () => { if (isArchived) { /* DSH refuses to surface an archived session as the current view: clicking "Open" on an archived row flashes an empty chat panel and bounces back to "no session selected". Unarchive first, then open. (issue #17.4) */ runRow(s.id, async () => { await onUnarchive(s.id); await onOpen(s.id); }); } else { onOpen(s.id); } } }, t("row.open")),
3372
- h("button", { type: "button", className: "sm-rowBtn", disabled: busy, onClick: () => runRow(s.id, () => isArchived ? onUnarchive(s.id) : onArchive(s.id)) }, t(isArchived ? "row.unarchive" : "row.archive")),
4756
+ h("button", { type: "button", className: "sm-rowBtn", disabled: busy, onClick: () => { if (isArchived) { if (isWsArchived) { if (typeof window !== "undefined" && typeof window.alert === "function") { window.alert(t("error.workspaceArchived")); } return; } /* DSH refuses to surface an archived session as the current view: clicking "Open" on an archived row flashes an empty chat panel and bounces back to "no session selected". Unarchive first, then open. (issue #17.4) */ runRow(s.id, async () => { await onUnarchive(s.id); await onOpen(s.id); }); } else { onOpen(s.id); } } }, t("row.open")),
4757
+ h("button", { type: "button", className: "sm-rowBtn", disabled: busy, onClick: () => runRow(s.id, () => { if (isArchived) { if (isWsArchived) { if (typeof window !== "undefined" && typeof window.alert === "function") { window.alert(t("error.workspaceArchived")); } return; } return onUnarchive(s.id); } return onArchive(s.id); }) }, t(isArchived ? "row.unarchive" : "row.archive")),
3373
4758
  annotationButtons(s.id, s.displayTitle || s.id, t, annotations, "sm-rowBtn", patch => runRow(s.id, () => annotationStore.save(s.id, patch)), () => { setAnnotationAnchor(null); setAnnotationFor(current => current?.id === s.id ? null : s); }, busy),
3374
4759
  h("button", { type: "button", className: "sm-rowBtn", disabled: busy, onClick: () => { if (moveFor && moveFor.id === s.id) setMoveFor(null); else setMoveFor(s); } }, t("row.move")),
3375
4760
  h("button", { type: "button", className: "sm-rowBtn", disabled: busy, onClick: () => { if (migrateFor && migrateFor.id === s.id) setMigrateFor(null); else setMigrateFor(s); } }, t("row.migrate")),
@@ -3382,8 +4767,288 @@ function isTopmostDialog(z) {
3382
4767
  const rowsList = rows.length === 0
3383
4768
  ? h("div", { className: "sm-empty" }, allRows.length > 0 ? t("panel.empty.filtered") : filter === "archived" ? t("panel.empty.archived") : t("panel.empty"))
3384
4769
  : h("div", { className: "sm-list" }, rows.map((s, i) => renderRow(s, i)));
4770
+
4771
+ const allWorkspaceRows = useMemo(() => {
4772
+ const hasSessionList = Array.isArray(list?.ids);
4773
+ const sessionsByWorkspace = new Map();
4774
+ for (const ws of workspaces) {
4775
+ sessionsByWorkspace.set(ws.id, []);
4776
+ }
4777
+ if (hasSessionList) {
4778
+ for (const s of allRows) {
4779
+ const matchedWsIds = rowWorkspaces(s, workspaceIndex);
4780
+ for (const wsId of matchedWsIds) {
4781
+ const wsList = sessionsByWorkspace.get(wsId);
4782
+ if (wsList) wsList.push(s);
4783
+ }
4784
+ }
4785
+ }
4786
+ return workspaces.map(ws => {
4787
+ const wsSessions = sessionsByWorkspace.get(ws.id) || [];
4788
+ let sessionCount = 0;
4789
+ let latestSessionTime = null;
4790
+ if (hasSessionList) {
4791
+ sessionCount = wsSessions.length;
4792
+ for (const s of wsSessions) {
4793
+ const st = sessionTime(s?.updatedAt) ?? sessionTime(s?.createdAt);
4794
+ if (st !== null) {
4795
+ if (latestSessionTime === null || st > latestSessionTime) {
4796
+ latestSessionTime = st;
4797
+ }
4798
+ }
4799
+ }
4800
+ } else {
4801
+ const sids = Array.isArray(ws.sessionIds) ? ws.sessionIds : [];
4802
+ const byId = list?.byId || {};
4803
+ for (const sid of sids) {
4804
+ const s = byId[sid];
4805
+ if (s && (s.origin === "subagent" || (s.blank && s.id !== list?.current))) continue;
4806
+ sessionCount++;
4807
+ const st = sessionTime(s?.updatedAt) ?? sessionTime(s?.createdAt);
4808
+ if (st !== null) {
4809
+ if (latestSessionTime === null || st > latestSessionTime) {
4810
+ latestSessionTime = st;
4811
+ }
4812
+ }
4813
+ }
4814
+ }
4815
+ return {
4816
+ ...ws,
4817
+ sessionCount,
4818
+ latestUpdatedAt: latestSessionTime
4819
+ };
4820
+ });
4821
+ }, [workspaces, allRows, workspaceIndex, list?.ids, list?.byId, list?.current]);
4822
+
4823
+ const filteredWorkspaceRows = useMemo(() => {
4824
+ const needle = wsQuery.trim().toLowerCase();
4825
+ const selected = allWorkspaceRows.filter(ws => {
4826
+ const isArchived = workspaceArchiveStore.isArchived(ws.id);
4827
+ if (wsFilter === "active" && isArchived) return false;
4828
+ if (wsFilter === "archived" && !isArchived) return false;
4829
+ if (needle) {
4830
+ const title = ws.title || ws.name || ws.id;
4831
+ const path = ws.path || "";
4832
+ const matchesTitle = title.toLowerCase().includes(needle);
4833
+ const matchesPath = path.toLowerCase().includes(needle);
4834
+ const matchesId = ws.id.toLowerCase().includes(needle);
4835
+ if (!matchesTitle && !matchesPath && !matchesId) return false;
4836
+ }
4837
+ return true;
4838
+ });
4839
+
4840
+ if (wsSort.startsWith("name-")) {
4841
+ const dir = wsSort === "name-asc" ? 1 : -1;
4842
+ return selected.map((ws, rank) => {
4843
+ const name = (ws.title || ws.name || ws.id || "").toLowerCase();
4844
+ return { ws, rank, name };
4845
+ }).sort((a, b) => {
4846
+ const cmp = a.name.localeCompare(b.name);
4847
+ return cmp !== 0 ? dir * cmp : a.rank - b.rank;
4848
+ }).map(item => item.ws);
4849
+ }
4850
+
4851
+ const direction = wsSort === "updated-asc" ? 1 : -1;
4852
+ return selected.map((ws, rank) => ({ ws, rank, time: ws.latestUpdatedAt }))
4853
+ .sort((a, b) => {
4854
+ if (a.time === b.time) return a.rank - b.rank;
4855
+ if (a.time === null) return 1;
4856
+ if (b.time === null) return -1;
4857
+ return direction * (a.time - b.time);
4858
+ })
4859
+ .map(item => item.ws);
4860
+ }, [allWorkspaceRows, wsQuery, wsFilter, wsSort, archiveVersion]);
4861
+
4862
+ const currentWsFilterIds = useMemo(() => filteredWorkspaceRows.map(ws => ws.id), [filteredWorkspaceRows]);
4863
+ useEffect(() => {
4864
+ const allowed = new Set(currentWsFilterIds);
4865
+ setWsSelectedIds(prev => {
4866
+ let changed = false;
4867
+ const next = new Set();
4868
+ for (const id of prev) if (allowed.has(id)) next.add(id); else changed = true;
4869
+ return changed ? next : prev;
4870
+ });
4871
+ }, [currentWsFilterIds]);
4872
+
4873
+ const toggleWsSelectMode = () => {
4874
+ if (wsSelectModeEnabled) {
4875
+ setWsSelectModeEnabled(false);
4876
+ setWsSelectedIds(new Set());
4877
+ setWsLastClickedRank(null);
4878
+ } else {
4879
+ setWsSelectModeEnabled(true);
4880
+ }
4881
+ };
4882
+
4883
+ const toggleWsSelect = (id, isRange, rank) => {
4884
+ if (wsBulkBusy) return;
4885
+ setWsSelectedIds(prev => {
4886
+ const next = new Set(prev);
4887
+ if (isRange && wsLastClickedRank !== null && typeof rank === "number") {
4888
+ const low = Math.min(wsLastClickedRank, rank);
4889
+ const high = Math.max(wsLastClickedRank, rank);
4890
+ for (let i = low; i <= high; i++) {
4891
+ const row = filteredWorkspaceRows[i];
4892
+ if (row) next.add(row.id);
4893
+ }
4894
+ } else {
4895
+ if (next.has(id)) next.delete(id); else next.add(id);
4896
+ }
4897
+ return next;
4898
+ });
4899
+ setWsLastClickedRank(rank);
4900
+ };
4901
+
4902
+ const onWsBulkSelectAll = () => {
4903
+ setWsSelectModeEnabled(true);
4904
+ setWsSelectedIds(new Set(filteredWorkspaceRows.map(ws => ws.id)));
4905
+ };
4906
+
4907
+ const onWsBulkClear = () => {
4908
+ setWsSelectedIds(new Set());
4909
+ setWsLastClickedRank(null);
4910
+ };
4911
+
4912
+ const onWsBulkArchive = async () => {
4913
+ if (wsBulkBusy || !wsSelectModeEnabled || wsSelectedIds.size === 0) return;
4914
+ const targetWs = workspaces.filter(ws => wsSelectedIds.has(ws.id) && !workspaceArchiveStore.isArchived(ws.id));
4915
+ if (targetWs.length === 0) return;
4916
+
4917
+ const hostArchivedSet = new Set(workspaceSnapshot.archivedSessionIds || []);
4918
+ for (const ws of targetWs) {
4919
+ const toArchive = (ws.sessionIds || []).filter(id => !hostArchivedSet.has(id));
4920
+ const runningSession = toArchive.map(id => list.byId[id]).find(s => s && s.running);
4921
+ if (runningSession) {
4922
+ const rTitle = runningSession.displayTitle || runningSession.id;
4923
+ const wsTitle = ws.title || ws.name || ws.id;
4924
+ const msg = (t("confirm.delete.running") || "该会话正在运行,无法归档。") + " (" + wsTitle + ": " + rTitle + ")";
4925
+ if (typeof window !== "undefined" && typeof window.alert === "function") {
4926
+ window.alert(msg);
4927
+ }
4928
+ return;
4929
+ }
4930
+ }
4931
+
4932
+ setWsBulkBusy(true);
4933
+ try {
4934
+ for (const ws of targetWs) {
4935
+ await executeArchiveWorkspace(ws.id, { allWorkspaces: workspaces, t });
4936
+ }
4937
+ setWsSelectedIds(new Set());
4938
+ } catch (err) {
4939
+ setError(err?.message || String(err));
4940
+ } finally {
4941
+ setWsBulkBusy(false);
4942
+ }
4943
+ };
4944
+
4945
+ const onWsBulkUnarchive = async () => {
4946
+ if (wsBulkBusy || !wsSelectModeEnabled || wsSelectedIds.size === 0) return;
4947
+ const targetWs = workspaces.filter(ws => wsSelectedIds.has(ws.id) && workspaceArchiveStore.isArchived(ws.id));
4948
+ if (targetWs.length === 0) return;
4949
+
4950
+ setWsBulkBusy(true);
4951
+ try {
4952
+ for (const ws of targetWs) {
4953
+ await executeUnarchiveWorkspace(ws.id, { t });
4954
+ }
4955
+ setWsSelectedIds(new Set());
4956
+ } catch (err) {
4957
+ setError(err?.message || String(err));
4958
+ } finally {
4959
+ setWsBulkBusy(false);
4960
+ }
4961
+ };
4962
+
4963
+ const hasWsViewChanges = wsQuery !== "" || wsFilter !== "all" || wsSort !== "updated-desc";
4964
+ const resetWsView = () => { setWsQuery(""); setWsFilter("all"); setWsSort("updated-desc"); };
4965
+ const wsIsEn = typeof t === "function" && (t("workspace.filter.all") === "All" || t("marks.favorite") === "Favorite");
4966
+
4967
+ const renderWorkspaceRow = (ws, rank) => {
4968
+ const isArchived = workspaceArchiveStore.isArchived(ws.id);
4969
+ const isBusy = workspaceArchiveStore.isBusy(ws.id);
4970
+ const title = ws.title || ws.name || ws.id;
4971
+ const path = ws.path || "";
4972
+ const sessionCount = typeof ws.sessionCount === "number" ? ws.sessionCount : (Array.isArray(ws.sessionIds) ? ws.sessionIds.length : 0);
4973
+ const timeFormatted = ws.latestUpdatedAt !== null
4974
+ ? t("workspace.lastUpdated", { time: formatRelative(ws.latestUpdatedAt, now) })
4975
+ : "—";
4976
+
4977
+ const isWsSelected = wsSelectedIds.has(ws.id);
4978
+ const checkbox = (wsSelectedIds.size === 0 && !wsSelectModeEnabled)
4979
+ ? null
4980
+ : h("label", { className: "sm-bulkRowCheckbox", onClick: (e) => e.stopPropagation() },
4981
+ h("input", {
4982
+ type: "checkbox",
4983
+ checked: isWsSelected,
4984
+ disabled: isBusy || wsBulkBusy,
4985
+ "aria-label": t("bulk.checkbox.aria", { title: title }),
4986
+ onChange: (e) => toggleWsSelect(ws.id, e.shiftKey === true, rank)
4987
+ })
4988
+ );
4989
+
4990
+ const handleToggleArchive = (e) => {
4991
+ if (e && typeof e.stopPropagation === "function") e.stopPropagation();
4992
+ if (isBusy || wsBulkBusy) return;
4993
+ if (!isArchived) {
4994
+ const hostArchivedSet = new Set(workspaceSnapshot.archivedSessionIds || []);
4995
+ const toArchive = (ws.sessionIds || []).filter(id => !hostArchivedSet.has(id));
4996
+ const runningSession = toArchive.map(id => list.byId[id]).find(s => s && s.running);
4997
+ if (runningSession) {
4998
+ const rTitle = runningSession.displayTitle || runningSession.id;
4999
+ const msg = (t("confirm.delete.running") || "该会话正在运行,无法归档。") + " (" + rTitle + ")";
5000
+ if (typeof window !== "undefined" && typeof window.alert === "function") {
5001
+ window.alert(msg);
5002
+ }
5003
+ return;
5004
+ }
5005
+ void executeArchiveWorkspace(ws.id, { allWorkspaces: workspaces, t });
5006
+ } else {
5007
+ void executeUnarchiveWorkspace(ws.id, { t });
5008
+ }
5009
+ };
5010
+
5011
+ const switchNode = h(SmSwitch, {
5012
+ role: "switch",
5013
+ "aria-checked": !isArchived,
5014
+ checked: !isArchived,
5015
+ loading: isBusy,
5016
+ disabled: isBusy || wsBulkBusy,
5017
+ ariaLabel: t("workspace.switch.aria", { name: title }),
5018
+ onClick: handleToggleArchive,
5019
+ onChange: handleToggleArchive
5020
+ });
5021
+
5022
+ return h("div", {
5023
+ key: ws.id,
5024
+ "data-workspace-id": ws.id,
5025
+ className: "sm-row sm-workspaceRow" + (isArchived ? " sm-rowArchived" : "") + (isWsSelected ? " sm-rowBulkSelected" : "")
5026
+ },
5027
+ h("div", { className: "sm-rowCheckboxSlot" + ((wsSelectedIds.size > 0 || wsSelectModeEnabled) ? " sm-rowCheckboxSlot-visible" : "") }, checkbox),
5028
+ h("div", { className: "sm-rowMain" },
5029
+ h("div", { className: "sm-rowTitle", title: path || title },
5030
+ h("span", null, title),
5031
+ isArchived ? h("span", { className: "sm-badge sm-badgeArchived" }, t("workspace.badge.archived")) : null
5032
+ ),
5033
+ h("div", { className: "sm-rowSecondary" },
5034
+ h("div", { className: "sm-rowMeta" },
5035
+ t("workspace.sessions", { count: sessionCount }) +
5036
+ " · " +
5037
+ timeFormatted
5038
+ )
5039
+ )
5040
+ ),
5041
+ h("div", { className: "sm-rowActions sm-workspaceRowActions" },
5042
+ switchNode
5043
+ )
5044
+ );
5045
+ };
5046
+ const workspaceRowsList = filteredWorkspaceRows.length === 0
5047
+ ? h("div", { className: "sm-empty" }, allWorkspaceRows.length > 0 ? t("workspace.empty.filtered") : t("workspace.empty"))
5048
+ : h("div", { className: "sm-list" }, filteredWorkspaceRows.map((ws, rank) => renderWorkspaceRow(ws, rank)));
5049
+
3385
5050
  const onPanelClose = () => {
3386
- if (busyId !== null) return;
5051
+ if (busyId !== null || wsBulkBusy) return;
3387
5052
  setConfirmFor(null);
3388
5053
  setMoveFor(null);
3389
5054
  setMigrateFor(null);
@@ -3397,8 +5062,23 @@ function isTopmostDialog(z) {
3397
5062
  // without re-subscribing on every render. The panel listener
3398
5063
  // returns early when any inner dialog is open so the child
3399
5064
  // dialog's own window listener gets the first shot at Esc.
5065
+ const panelRef = useRef(null);
3400
5066
  const escStateRef = useRef({});
3401
- escStateRef.current = { busyId, confirmFor, moveFor, migrateFor, annotationFor, onPanelClose };
5067
+ escStateRef.current = {
5068
+ busyId,
5069
+ confirmFor,
5070
+ moveFor,
5071
+ migrateFor,
5072
+ annotationFor,
5073
+ bulkPreview,
5074
+ bulkProgress,
5075
+ bulkResult,
5076
+ bulkBusy,
5077
+ bulkTagDraft,
5078
+ bulkPresetDraft,
5079
+ wsBulkBusy,
5080
+ onPanelClose
5081
+ };
3402
5082
  useEffect(() => {
3403
5083
  if (!open) return;
3404
5084
  const handler = (e) => {
@@ -3406,8 +5086,10 @@ function isTopmostDialog(z) {
3406
5086
  if (e.defaultPrevented) return;
3407
5087
  if (e.isComposing || e.keyCode === 229) return;
3408
5088
  const s = escStateRef.current;
3409
- if (s.busyId !== null) return;
3410
- if (s.confirmFor || s.moveFor || s.migrateFor || s.annotationFor || (activeDialogZStack && activeDialogZStack.length > 0)) return;
5089
+ if (s.busyId !== null || s.wsBulkBusy || s.bulkBusy) return;
5090
+ if (s.confirmFor || s.moveFor || s.migrateFor || s.annotationFor || s.bulkPreview || s.bulkProgress || s.bulkResult || s.bulkTagDraft || s.bulkPresetDraft) return;
5091
+ if (typeof updateStore !== "undefined" && updateStore && typeof updateStore.getSnapshot === "function" && updateStore.getSnapshot()?.dialogOpen) return;
5092
+ if (activeDialogZStack && activeDialogZStack.length > 0) return;
3411
5093
  e.preventDefault();
3412
5094
  e.stopPropagation();
3413
5095
  s.onPanelClose();
@@ -3421,7 +5103,28 @@ function isTopmostDialog(z) {
3421
5103
  if (ae && typeof ae.blur === "function" && ae !== document.body) ae.blur();
3422
5104
  };
3423
5105
  window.addEventListener("keydown", handler);
3424
- return () => window.removeEventListener("keydown", handler);
5106
+ const mountTime = Date.now();
5107
+ const clickHandler = (e) => {
5108
+ if (Date.now() - mountTime < 50) return;
5109
+ if (e.defaultPrevented) return;
5110
+ const s = escStateRef.current;
5111
+ if (s.busyId !== null || s.wsBulkBusy || s.bulkBusy) return;
5112
+ if (s.annotationFor !== null) return;
5113
+ if (s.confirmFor || s.moveFor || s.migrateFor) return;
5114
+ if (s.bulkPreview || s.bulkProgress || s.bulkResult || s.bulkTagDraft || s.bulkPresetDraft) return;
5115
+ if (typeof updateStore !== "undefined" && updateStore && typeof updateStore.getSnapshot === "function" && updateStore.getSnapshot()?.dialogOpen) return;
5116
+ if (activeDialogZStack && activeDialogZStack.length > 0) return;
5117
+ if (panelRef.current && typeof panelRef.current.contains === "function" && panelRef.current.contains(e.target)) return;
5118
+ if (e.target && typeof e.target.closest === "function") {
5119
+ if (e.target.closest(".sm-nativeDialog, .sm-panelDialog, .sm-bulkDialog, .sm-annotationDialog, .sm-annotationSurface, .sm-tooltip, .sm-footerBtn")) return;
5120
+ }
5121
+ s.onPanelClose();
5122
+ };
5123
+ window.addEventListener("click", clickHandler);
5124
+ return () => {
5125
+ window.removeEventListener("keydown", handler);
5126
+ window.removeEventListener("click", clickHandler);
5127
+ };
3425
5128
  }, [open]);
3426
5129
  // The panel is a document-level floating window, so it is portaled into
3427
5130
  // the plugin's overlay root like every other dialog here (issue #28).
@@ -3444,6 +5147,7 @@ function isTopmostDialog(z) {
3444
5147
  role: "presentation"
3445
5148
  },
3446
5149
  h("section", {
5150
+ ref: panelRef,
3447
5151
  className: "sm-nativeDialog sm-panelDialog",
3448
5152
  role: "dialog",
3449
5153
  "aria-modal": "true",
@@ -3509,106 +5213,235 @@ function isTopmostDialog(z) {
3509
5213
  updateState.status === "checking" ? h("span", { className: "sm-updateSpinner", "aria-hidden": "true" }) : null
3510
5214
  )),
3511
5215
  ),
3512
- h("h2", { id: "sm-panel-dialog-title", className: "sm-nativeDialogTitle" }, t("panel.title")),
5216
+ h("h2", { id: "sm-panel-dialog-title", className: "sm-nativeDialogTitle sm-visuallyHidden" }, t("panel.title")),
5217
+ h("nav", { className: "sm-panelNav", "aria-label": t("panel.tab.sessions") || "页面切换" },
5218
+ h("button", {
5219
+ type: "button",
5220
+ role: "tab",
5221
+ "aria-selected": panelTab === "sessions",
5222
+ className: "sm-panelNavTab" + (panelTab === "sessions" ? " sm-on" : ""),
5223
+ onClick: () => setPanelTab("sessions")
5224
+ }, t("panel.tab.sessions")),
5225
+ h("button", {
5226
+ type: "button",
5227
+ role: "tab",
5228
+ "aria-selected": panelTab === "workspaces",
5229
+ className: "sm-panelNavTab" + (panelTab === "workspaces" ? " sm-on" : ""),
5230
+ onClick: () => setPanelTab("workspaces")
5231
+ }, t("panel.tab.workspaces"))
5232
+ ),
3513
5233
  h("div", { className: "sm-nativeDialogHeaderActions" },
3514
5234
  renderTip(t("panel.close") || "关闭", h("button", {
3515
5235
  type: "button",
3516
5236
  className: "sm-panelHeaderClose",
3517
- disabled: busyId !== null || migrating,
5237
+ disabled: busyId !== null || migrating || wsBulkBusy,
3518
5238
  onClick: onPanelClose,
3519
5239
  "aria-label": t("panel.close")
3520
5240
  }, h(CloseIcon, { size: 14 })))
3521
5241
  )
3522
5242
  ),
3523
5243
  h("div", { className: "sm-panel" },
3524
- h("div", { className: "sm-panelTools" },
3525
- h("input", {
3526
- type: "search", className: "sm-queryInput", value: query,
3527
- placeholder: t("panel.search"), "aria-label": t("panel.search"), autoComplete: "off",
3528
- onChange: event => setQuery(event.target.value)
3529
- }),
3530
- h("div", { className: "sm-panelSelects" },
3531
- h("label", { className: "sm-viewField" },
3532
- h("span", null, t("panel.workspace")),
3533
- h("select", {
3534
- className: "sm-viewSelect", "aria-label": t("panel.workspace"), value: workspaceFilter,
3535
- disabled: workspacesLoading || workspacesError !== null,
3536
- onChange: event => setWorkspaceFilter(event.target.value)
3537
- },
3538
- h("option", { value: "all" }, t("panel.workspace.all")),
3539
- h("option", { value: "ungrouped" }, t("panel.workspace.ungrouped")),
3540
- workspaces.map(workspace => h("option", { key: workspace.id, value: workspaceValue(workspace.id), title: workspace.path || workspace.id }, workspace.title || workspace.name || workspace.id))
5244
+ panelTab === "workspaces" ? h(React.Fragment, null,
5245
+ h("div", { className: "sm-panelTools" },
5246
+ h("input", {
5247
+ type: "search", className: "sm-queryInput", value: wsQuery,
5248
+ placeholder: t("workspace.search"), "aria-label": t("workspace.search"), autoComplete: "off",
5249
+ onChange: event => setWsQuery(event.target.value)
5250
+ }),
5251
+ h("div", { className: "sm-markFilters sm-wsFilters" },
5252
+ h("div", { className: "sm-markFiltersGroup" },
5253
+ h("button", {
5254
+ type: "button", key: "all",
5255
+ className: "sm-filterBtn" + (wsFilter === "all" ? " sm-on" : ""),
5256
+ "aria-pressed": wsFilter === "all",
5257
+ onClick: () => setWsFilter("all")
5258
+ }, t("workspace.filter.all")),
5259
+ h("button", {
5260
+ type: "button", key: "active",
5261
+ className: "sm-filterBtn" + (wsFilter === "active" ? " sm-on" : ""),
5262
+ "aria-pressed": wsFilter === "active",
5263
+ onClick: () => setWsFilter("active")
5264
+ }, t("workspace.filter.active")),
5265
+ h("button", {
5266
+ type: "button", key: "archived",
5267
+ className: "sm-filterBtn" + (wsFilter === "archived" ? " sm-on" : ""),
5268
+ "aria-pressed": wsFilter === "archived",
5269
+ onClick: () => setWsFilter("archived")
5270
+ }, t("workspace.filter.archived")),
5271
+ h("div", { className: "sm-viewField sm-wsSortField" },
5272
+ h("select", {
5273
+ className: "sm-viewSelect sm-wsSortSelect" + (wsIsEn ? " sm-wsSortSelect-en" : ""), "aria-label": t("panel.sort"), value: wsSort,
5274
+ onMouseDown: e => e.stopPropagation(),
5275
+ onClick: e => e.stopPropagation(),
5276
+ onChange: event => setWsSort(event.target.value)
5277
+ },
5278
+ h("option", { value: "updated-desc" }, t("workspace.sort.updated-desc")),
5279
+ h("option", { value: "updated-asc" }, t("workspace.sort.updated-asc")),
5280
+ h("option", { value: "name-asc" }, t("workspace.sort.name-asc")),
5281
+ h("option", { value: "name-desc" }, t("workspace.sort.name-desc"))
5282
+ )
5283
+ )
5284
+ ),
5285
+ h("div", { className: "sm-markFiltersSpacer" }),
5286
+ h("div", { className: "sm-markFiltersGroup sm-wsFiltersBulk" },
5287
+ h("button", {
5288
+ type: "button",
5289
+ className: "sm-filterBtn" + (wsSelectModeEnabled ? " sm-on" : ""),
5290
+ disabled: busyId !== null || migrating || wsBulkBusy,
5291
+ "aria-pressed": wsSelectModeEnabled,
5292
+ "aria-label": t(wsSelectModeEnabled ? "panel.selectMode.exit" : "panel.selectMode.aria"),
5293
+ onClick: toggleWsSelectMode
5294
+ }, wsSelectModeEnabled ? t("panel.selectMode.exit") : t("panel.selectMode")),
5295
+ h("button", {
5296
+ type: "button",
5297
+ className: "sm-filterBtn sm-bulkSelectBtn",
5298
+ disabled: filteredWorkspaceRows.length === 0 || wsSelectedIds.size === filteredWorkspaceRows.length || wsBulkBusy,
5299
+ onClick: onWsBulkSelectAll,
5300
+ "aria-label": t("bulk.selectAllFiltered")
5301
+ }, t("bulk.selectAllFiltered")),
5302
+ h("button", {
5303
+ type: "button",
5304
+ className: "sm-filterBtn sm-bulkSelectBtn",
5305
+ disabled: wsSelectedIds.size === 0 || wsBulkBusy,
5306
+ onClick: onWsBulkClear,
5307
+ "aria-label": t("bulk.clear")
5308
+ }, t("bulk.clear")),
5309
+ h("button", {
5310
+ type: "button",
5311
+ className: "sm-filterBtn",
5312
+ disabled: !wsSelectModeEnabled || wsSelectedIds.size === 0 || wsBulkBusy,
5313
+ onClick: onWsBulkArchive,
5314
+ "aria-label": t("workspace.bulk.archive")
5315
+ }, t("workspace.bulk.archive")),
5316
+ h("button", {
5317
+ type: "button",
5318
+ className: "sm-filterBtn",
5319
+ disabled: !wsSelectModeEnabled || wsSelectedIds.size === 0 || wsBulkBusy,
5320
+ onClick: onWsBulkUnarchive,
5321
+ "aria-label": t("workspace.bulk.unarchive")
5322
+ }, t("workspace.bulk.unarchive"))
3541
5323
  )
3542
5324
  ),
3543
- h("label", { className: "sm-viewField" },
3544
- h("span", null, t("panel.sort")),
3545
- h("select", {
3546
- className: "sm-viewSelect", "aria-label": t("panel.sort"), value: sortOrder,
3547
- onChange: event => setSortOrder(event.target.value)
3548
- }, SORT_ORDERS.map(value => h("option", { key: value, value, disabled: value === "priority" && !marksReady }, t(value === "priority" ? "marks.sort" : "panel.sort." + value))))
5325
+ h("div", { className: "sm-resultSummary" },
5326
+ h("span", { role: "status", "aria-live": "polite", "aria-atomic": "true" },
5327
+ (wsSelectModeEnabled || wsSelectedIds.size > 0)
5328
+ ? t("bulk.selectedFiltered", { selected: wsSelectedIds.size, filtered: filteredWorkspaceRows.length })
5329
+ : t("workspace.count", { shown: filteredWorkspaceRows.length, total: allWorkspaceRows.length })
5330
+ ),
5331
+ hasWsViewChanges ? h("button", { type: "button", className: "sm-resetFilters", onClick: resetWsView }, t("workspace.reset")) : null
3549
5332
  ),
3550
- h("label", { className: "sm-viewField" },
3551
- h("span", null, t("marks.tags")),
3552
- h("select", { className: "sm-viewSelect", "aria-label": t("marks.filter.tags"), value: tagFilter, disabled: !marksReady, onChange: e => setTagFilter(e.target.value) },
3553
- h("option", { value: "all" }, t("marks.tags.all")), h("option", { value: "none" }, t("marks.tags.none")),
3554
- availableTags.map(([key, tag]) => h("option", { key, value: "tag:" + key }, tag)),
3555
- tagFilter.startsWith("tag:") && !availableTags.some(([key]) => "tag:" + key === tagFilter) ? h("option", { value: tagFilter }, t("marks.tags.missing", { tag: tagFilter.slice(4) })) : null
5333
+ (workspacesLoading && workspaces.length === 0) ? h("div", { className: "sm-workspaceNotice", role: "status" }, t("panel.workspace.loading")) : null,
5334
+ workspacesError ? h("div", { className: "sm-workspaceNotice", role: "alert" },
5335
+ h("span", null, t("panel.workspace.error", { message: workspacesError })),
5336
+ h("button", { type: "button", className: "sm-resetFilters", onClick: () => setWorkspaceRefresh(value => value + 1) }, t("panel.workspace.retry"))
5337
+ ) : null
5338
+ ),
5339
+ error ? h("div", { className: "sm-error", role: "alert" }, t("error.operation", { message: error })) : null,
5340
+ workspaceRowsList
5341
+ ) : h(React.Fragment, null,
5342
+ h("div", { className: "sm-panelTools" },
5343
+ h("input", {
5344
+ type: "search", className: "sm-queryInput", value: query,
5345
+ placeholder: t("panel.search"), "aria-label": t("panel.search"), autoComplete: "off",
5346
+ onChange: event => setQuery(event.target.value)
5347
+ }),
5348
+ h("div", { className: "sm-panelSelects" },
5349
+ h("div", { className: "sm-viewField" },
5350
+ h("span", null, t("panel.workspace")),
5351
+ h("select", {
5352
+ className: "sm-viewSelect", "aria-label": t("panel.workspace"), value: workspaceFilter,
5353
+ disabled: workspacesLoading || workspacesError !== null,
5354
+ onMouseDown: e => e.stopPropagation(),
5355
+ onClick: e => e.stopPropagation(),
5356
+ onChange: event => setWorkspaceFilter(event.target.value)
5357
+ },
5358
+ h("option", { value: "all" }, t("panel.workspace.all")),
5359
+ h("option", { value: "ungrouped" }, t("panel.workspace.ungrouped")),
5360
+ workspaces.map(workspace => h("option", { key: workspace.id, value: workspaceValue(workspace.id), title: workspace.path || workspace.id }, workspace.title || workspace.name || workspace.id))
5361
+ )
5362
+ ),
5363
+ h("div", { className: "sm-viewField" },
5364
+ h("span", null, t("panel.sort")),
5365
+ h("select", {
5366
+ className: "sm-viewSelect", "aria-label": t("panel.sort"), value: sortOrder,
5367
+ onMouseDown: e => e.stopPropagation(),
5368
+ onClick: e => e.stopPropagation(),
5369
+ onChange: event => setSortOrder(event.target.value)
5370
+ }, SORT_ORDERS.map(value => h("option", { key: value, value, disabled: value === "priority" && !marksReady }, t(value === "priority" ? "marks.sort" : "panel.sort." + value))))
5371
+ ),
5372
+ h("div", { className: "sm-viewField" },
5373
+ h("span", null, t("marks.tags")),
5374
+ h("select", {
5375
+ className: "sm-viewSelect", "aria-label": t("marks.filter.tags"), value: tagFilter, disabled: !marksReady,
5376
+ onMouseDown: e => e.stopPropagation(),
5377
+ onClick: e => e.stopPropagation(),
5378
+ onChange: e => setTagFilter(e.target.value)
5379
+ },
5380
+ h("option", { value: "all" }, t("marks.tags.all")), h("option", { value: "none" }, t("marks.tags.none")),
5381
+ availableTags.map(([key, tag]) => h("option", { key, value: "tag:" + key }, tag)),
5382
+ tagFilter.startsWith("tag:") && !availableTags.some(([key]) => "tag:" + key === tagFilter) ? h("option", { value: tagFilter }, t("marks.tags.missing", { tag: tagFilter.slice(4) })) : null
5383
+ )
5384
+ ),
5385
+ h("div", { className: "sm-viewField" },
5386
+ h("span", null, t("marks.priority")),
5387
+ h("select", {
5388
+ className: "sm-viewSelect", "aria-label": t("marks.filter.priority"), value: priorityFilter, disabled: !marksReady,
5389
+ onMouseDown: e => e.stopPropagation(),
5390
+ onClick: e => e.stopPropagation(),
5391
+ onChange: e => setPriorityFilter(e.target.value)
5392
+ },
5393
+ h("option", { value: "all" }, t("marks.priority.all")),
5394
+ [1,2,3,4,5].map(value => h("option", { key: value, value: String(value) }, t("marks.priority." + value)))
5395
+ )
3556
5396
  )
3557
5397
  ),
3558
- h("label", { className: "sm-viewField" },
3559
- h("span", null, t("marks.priority")),
3560
- h("select", { className: "sm-viewSelect", "aria-label": t("marks.filter.priority"), value: priorityFilter, disabled: !marksReady, onChange: e => setPriorityFilter(e.target.value) },
3561
- h("option", { value: "all" }, t("marks.priority.all")),
3562
- [1,2,3,4,5].map(value => h("option", { key: value, value: String(value) }, t("marks.priority." + value)))
5398
+ h("div", { className: "sm-markFilters" },
5399
+ h("div", { className: "sm-markFiltersGroup" },
5400
+ filterBtn("all", t("panel.filter.all")),
5401
+ filterBtn("active", t("panel.filter.active")),
5402
+ filterBtn("archived", t("panel.filter.archived")),
5403
+ h("button", { type: "button", className: "sm-filterBtn sm-markFilterToggle" + (onlyFavorite ? " sm-on" : ""), "aria-label": t("marks.filter.favorite.aria"), "aria-pressed": onlyFavorite, disabled: !marksReady, onClick: () => setOnlyFavorite(value => !value) }, "★ " + t("marks.filter.favorite")),
5404
+ h("button", { type: "button", className: "sm-filterBtn sm-markFilterToggle" + (onlyReview ? " sm-on" : ""), "aria-label": t("marks.filter.review.aria"), "aria-pressed": onlyReview, disabled: !marksReady, onClick: () => setOnlyReview(value => !value) }, "◷ " + t("marks.filter.review"))
5405
+ ),
5406
+ h("div", { className: "sm-markFiltersSpacer" }),
5407
+ h("div", { className: "sm-markFiltersGroup" },
5408
+ h("button", {
5409
+ type: "button",
5410
+ className: "sm-filterBtn" + (selectModeEnabled ? " sm-on" : ""),
5411
+ disabled: busyId !== null || migrating,
5412
+ "aria-pressed": selectModeEnabled,
5413
+ "aria-label": t(selectModeEnabled ? "panel.selectMode.active.aria" : "panel.selectMode.aria"),
5414
+ onClick: toggleSelectMode
5415
+ }, selectModeEnabled ? t("panel.selectMode.exit") : t("panel.selectMode")),
5416
+ h("button", { type: "button", className: "sm-filterBtn sm-bulkSelectBtn", disabled: rows.length === 0 || rows.length === selectedIds.size || bulkBusy, onClick: onBulkSelectAll, "aria-label": t("bulk.selectAllFiltered") }, t("bulk.selectAllFiltered")),
5417
+ h("button", { type: "button", className: "sm-filterBtn sm-bulkSelectBtn", disabled: selectedIds.size === 0 || bulkBusy, onClick: onBulkClear, "aria-label": t("bulk.clear") }, t("bulk.clear"))
3563
5418
  )
3564
- )
3565
- ),
3566
- h("div", { className: "sm-markFilters" },
3567
- h("div", { className: "sm-markFiltersGroup" },
3568
- filterBtn("all", t("panel.filter.all")),
3569
- filterBtn("active", t("panel.filter.active")),
3570
- filterBtn("archived", t("panel.filter.archived")),
3571
- h("button", { type: "button", className: "sm-filterBtn sm-markFilterToggle" + (onlyFavorite ? " sm-on" : ""), "aria-label": t("marks.filter.favorite.aria"), "aria-pressed": onlyFavorite, disabled: !marksReady, onClick: () => setOnlyFavorite(value => !value) }, "★ " + t("marks.filter.favorite")),
3572
- h("button", { type: "button", className: "sm-filterBtn sm-markFilterToggle" + (onlyReview ? " sm-on" : ""), "aria-label": t("marks.filter.review.aria"), "aria-pressed": onlyReview, disabled: !marksReady, onClick: () => setOnlyReview(value => !value) }, "◷ " + t("marks.filter.review"))
3573
5419
  ),
3574
- h("div", { className: "sm-markFiltersSpacer" }),
3575
- h("div", { className: "sm-markFiltersGroup" },
3576
- h("button", {
3577
- type: "button",
3578
- className: "sm-filterBtn" + (selectModeEnabled ? " sm-on" : ""),
3579
- disabled: busyId !== null || migrating,
3580
- "aria-pressed": selectModeEnabled,
3581
- "aria-label": t(selectModeEnabled ? "panel.selectMode.active.aria" : "panel.selectMode.aria"),
3582
- onClick: toggleSelectMode
3583
- }, selectModeEnabled ? t("panel.selectMode.exit") : t("panel.selectMode")),
3584
- h("button", { type: "button", className: "sm-filterBtn sm-bulkSelectBtn", disabled: rows.length === 0 || rows.length === selectedIds.size || bulkBusy, onClick: onBulkSelectAll, "aria-label": t("bulk.selectAllFiltered") }, t("bulk.selectAllFiltered")),
3585
- h("button", { type: "button", className: "sm-filterBtn sm-bulkSelectBtn", disabled: selectedIds.size === 0 || bulkBusy, onClick: onBulkClear, "aria-label": t("bulk.clear") }, t("bulk.clear"))
3586
- )
3587
- ),
3588
- h("div", { className: "sm-resultSummary" },
3589
- h("span", { role: "status", "aria-live": "polite", "aria-atomic": "true" }, t("panel.count", { shown: rows.length, total: allRows.length })),
3590
- hasViewChanges ? h("button", { type: "button", className: "sm-resetFilters", onClick: resetView }, t("panel.reset")) : null
5420
+ h("div", { className: "sm-resultSummary" },
5421
+ h("span", { role: "status", "aria-live": "polite", "aria-atomic": "true" }, t("panel.count", { shown: rows.length, total: allRows.length })),
5422
+ hasViewChanges ? h("button", { type: "button", className: "sm-resetFilters", onClick: resetView }, t("panel.reset")) : null
5423
+ ),
5424
+ annotations.loading && !annotations.loaded ? h("div", { className: "sm-workspaceNotice", role: "status" }, t("marks.loading")) : null,
5425
+ annotations.error ? h("div", { className: "sm-workspaceNotice", role: "alert" }, t("marks.error", { message: annotations.error }), h("button", { type: "button", className: "sm-resetFilters", onClick: () => annotationStore.load(true) }, t("marks.retry"))) : null,
5426
+ creationTimeUnavailable && !(workspacesLoading && workspaces.length === 0) ? h("div", { className: "sm-workspaceNotice", role: "status" }, t("panel.sort.unavailable")) : null,
5427
+ (workspacesLoading && workspaces.length === 0) ? h("div", { className: "sm-workspaceNotice", role: "status" }, t("panel.workspace.loading")) : null,
5428
+ workspacesError ? h("div", { className: "sm-workspaceNotice", role: "alert" },
5429
+ h("span", null, t("panel.workspace.error", { message: workspacesError })),
5430
+ h("button", { type: "button", className: "sm-resetFilters", onClick: () => setWorkspaceRefresh(value => value + 1) }, t("panel.workspace.retry"))
5431
+ ) : null
3591
5432
  ),
3592
- annotations.loading && !annotations.loaded ? h("div", { className: "sm-workspaceNotice", role: "status" }, t("marks.loading")) : null,
3593
- annotations.error ? h("div", { className: "sm-workspaceNotice", role: "alert" }, t("marks.error", { message: annotations.error }), h("button", { type: "button", className: "sm-resetFilters", onClick: () => annotationStore.load(true) }, t("marks.retry"))) : null,
3594
- creationTimeUnavailable && !workspacesLoading ? h("div", { className: "sm-workspaceNotice", role: "status" }, t("panel.sort.unavailable")) : null,
3595
- workspacesLoading ? h("div", { className: "sm-workspaceNotice", role: "status" }, t("panel.workspace.loading")) : null,
3596
- workspacesError ? h("div", { className: "sm-workspaceNotice", role: "alert" },
3597
- h("span", null, t("panel.workspace.error", { message: workspacesError })),
3598
- h("button", { type: "button", className: "sm-resetFilters", onClick: () => setWorkspaceRefresh(value => value + 1) }, t("panel.workspace.retry"))
3599
- ) : null
3600
- ),
3601
- error ? h("div", { className: "sm-error", role: "alert" }, t("error.operation", { message: error })) : null,
3602
- (selectedIds.size > 0 || selectModeEnabled) ? h(BulkActionBar, {
3603
- t, selectedCount: selectedIds.size, filteredCount: rows.length, busy: bulkBusy,
3604
- onSelectAll: onBulkSelectAll, onClear: onBulkClear,
3605
- onAction: onBulkAction,
3606
- onOpenTagDialog: onBulkTagsAdd,
3607
- onOpenPriorityDialog: onBulkPriority,
3608
- onOpenMoveDialog: onBulkMove,
3609
- onOpenPresetDialog: onBulkPreset
3610
- }) : null,
3611
- rowsList
5433
+ error ? h("div", { className: "sm-error", role: "alert" }, t("error.operation", { message: error })) : null,
5434
+ (selectedIds.size > 0 || selectModeEnabled) ? h(BulkActionBar, {
5435
+ t, selectedCount: selectedIds.size, filteredCount: rows.length, busy: bulkBusy,
5436
+ onSelectAll: onBulkSelectAll, onClear: onBulkClear,
5437
+ onAction: onBulkAction,
5438
+ onOpenTagDialog: onBulkTagsAdd,
5439
+ onOpenPriorityDialog: onBulkPriority,
5440
+ onOpenMoveDialog: onBulkMove,
5441
+ onOpenPresetDialog: onBulkPreset
5442
+ }) : null,
5443
+ rowsList
5444
+ )
3612
5445
  )
3613
5446
  )
3614
5447
  )));
@@ -3618,13 +5451,27 @@ function isTopmostDialog(z) {
3618
5451
  onClose: () => { if (busyId === null) setConfirmFor(null); },
3619
5452
  title: t("confirm.delete.title"),
3620
5453
  description: (confirmFor.displayTitle && t("confirm.delete.desc", { title: confirmFor.displayTitle })) || "",
3621
- warning: confirmFor.running ? (t("confirm.delete.running") + " " + t("bulk.dialog.warning.delete")) : t("bulk.dialog.warning.delete"),
5454
+ warning: (() => {
5455
+ const baseWarning = (confirmFor.running ? (t("confirm.delete.running") + " " + t("bulk.dialog.warning.delete")) : t("bulk.dialog.warning.delete"));
5456
+ if (confirmPreviewState.loading) {
5457
+ const loadingMsg = t("confirm.delete.previewLoading") || "正在检查关联的子智能体会话…";
5458
+ return `${loadingMsg}\n${baseWarning}`;
5459
+ }
5460
+ if (confirmPreviewState.error) {
5461
+ const errorMsg = confirmPreviewState.error;
5462
+ return `${errorMsg}\n${baseWarning}`;
5463
+ }
5464
+ const subagentWarning = confirmPreviewState.subagentCount > 0 ? t("confirm.delete.subagents", { count: confirmPreviewState.subagentCount }) : "";
5465
+ return subagentWarning ? `${subagentWarning}\n${baseWarning}` : baseWarning;
5466
+ })(),
5467
+ confirmDisabled: confirmPreviewState.loading || Boolean(confirmPreviewState.error),
3622
5468
  confirmLabel: t("confirm.delete.confirm"),
3623
5469
  cancelLabel: t("confirm.cancel"),
3624
5470
  busy: busyId !== null,
3625
5471
  danger: true,
3626
5472
  onCancel: () => { if (busyId === null) setConfirmFor(null); },
3627
5473
  onConfirm: () => {
5474
+ if (confirmPreviewState.loading || confirmPreviewState.error) return;
3628
5475
  const target = confirmFor;
3629
5476
  setConfirmFor(null);
3630
5477
  runRow(target.id, () => onRemove(target.id));
@@ -3710,12 +5557,16 @@ function isTopmostDialog(z) {
3710
5557
  delete: t("bulk.confirm.delete")
3711
5558
  })[action] || t("bulk.dialog.execute");
3712
5559
  const bulkPreviewDialog = bulkPreview ? h(BatchPreviewDialog, {
3713
- open: true, t, actionLabel: bulkPreview.actionLabel,
5560
+ open: true, t, action: bulkPreview.action, actionLabel: bulkPreview.actionLabel,
5561
+ title: bulkPreview.action === "delete" ? t("confirm.delete.title") : undefined,
3714
5562
  sessions: bulkPreview.sessions, warning: bulkPreview.warning,
3715
5563
  confirmLabel: bulkConfirmLabelFor(bulkPreview.action),
3716
5564
  danger: bulkPreview.action === "delete",
5565
+ loading: bulkPreview.loading === true,
5566
+ error: bulkPreview.error || null,
3717
5567
  onCancel: () => setBulkPreview(null),
3718
5568
  onExecute: () => {
5569
+ if (bulkPreview.loading || bulkPreview.error) return;
3719
5570
  const preview = bulkPreview;
3720
5571
  setBulkPreview(null);
3721
5572
  void executeBulk({
@@ -3771,10 +5622,11 @@ function isTopmostDialog(z) {
3771
5622
  onCancel: () => setBulkPresetDraft(null),
3772
5623
  onApply: onBulkPresetApply
3773
5624
  }) : null;
5625
+ const workspaceArchiveConfirmDialog = null;
3774
5626
  // Bulk dialogs are portaled into the overlay root like every other
3775
5627
  // dialog, so their static z-index (10002) only orders them inside that
3776
5628
  // context; the root's own 1050 keeps them below DSH's 1100 tooltip tier.
3777
- return h(React.Fragment, null, panelModal, annotationDialog, moveDialog, deleteDialog, migrateDialog, bulkPreviewDialog, bulkProgressDialog, bulkResultDialog, bulkTagDialog, bulkPriorityDialog, bulkMoveDialog, bulkPresetDialog);
5629
+ return h(React.Fragment, null, panelModal, annotationDialog, moveDialog, deleteDialog, migrateDialog, bulkPreviewDialog, bulkProgressDialog, bulkResultDialog, bulkTagDialog, bulkPriorityDialog, bulkMoveDialog, bulkPresetDialog, workspaceArchiveConfirmDialog);
3778
5630
  }
3779
5631
 
3780
5632
  function HeaderAction(props) {
@@ -3820,6 +5672,29 @@ function isTopmostDialog(z) {
3820
5672
 
3821
5673
  const [confirmFor, setConfirmFor] = useState(null);
3822
5674
  const [confirmAnchor, setConfirmAnchor] = useState(null);
5675
+ const [confirmPreviewState, setConfirmPreviewState] = useState({ loading: false, error: null, subagentCount: 0 });
5676
+ useEffect(() => {
5677
+ if (!confirmFor || !confirmFor.id) {
5678
+ setConfirmPreviewState({ loading: false, error: null, subagentCount: 0 });
5679
+ return;
5680
+ }
5681
+ let alive = true;
5682
+ setConfirmPreviewState({ loading: true, error: null, subagentCount: 0 });
5683
+ fetchApi("/delete-preview?sessionId=" + encodeURIComponent(confirmFor.id))
5684
+ .then((res) => {
5685
+ if (!alive) return;
5686
+ if (res && res.ok && res.result && typeof res.result.subagentCount === "number") {
5687
+ setConfirmPreviewState({ loading: false, error: null, subagentCount: res.result.subagentCount });
5688
+ } else {
5689
+ setConfirmPreviewState({ loading: false, error: res?.error || (t ? t("confirm.delete.previewError") : "检查关联子智能体会话失败,请稍后重试"), subagentCount: 0 });
5690
+ }
5691
+ })
5692
+ .catch((err) => {
5693
+ if (!alive) return;
5694
+ setConfirmPreviewState({ loading: false, error: (err && err.message) || (t ? t("confirm.delete.previewError") : "检查关联子智能体会话失败,请稍后重试"), subagentCount: 0 });
5695
+ });
5696
+ return () => { alive = false; };
5697
+ }, [confirmFor?.id]);
3823
5698
  const [moveFor, setMoveFor] = useState(null);
3824
5699
  const [moveAnchor, setMoveAnchor] = useState(null);
3825
5700
  const [workspaces, setWorkspaces] = useState([]);
@@ -4149,13 +6024,30 @@ function isTopmostDialog(z) {
4149
6024
  onClose: () => setConfirmFor(null),
4150
6025
  onCancel: () => setConfirmFor(null),
4151
6026
  onConfirm: () => {
6027
+ if (confirmPreviewState.loading || confirmPreviewState.error) return;
4152
6028
  const target = confirmFor;
4153
6029
  setConfirmFor(null);
4154
6030
  runWithAlert("delete", () => onRemove(target.id));
4155
6031
  },
4156
6032
  title: t ? t("confirm.delete.title") : "删除会话",
4157
6033
  description: (confirmFor && confirmFor.displayTitle && (t ? t("confirm.delete.desc", { title: confirmFor.displayTitle }) : ("「" + confirmFor.displayTitle + "」:删除,不可恢复"))) || "",
4158
- warning: (confirmFor && confirmFor.running && t ? t("confirm.delete.running") + " " : "") + (t ? t("bulk.dialog.warning.delete") : "删除操作不可撤销,请确认后再继续。"),
6034
+ warning: (() => {
6035
+ const baseWarn = (confirmFor && confirmFor.running && t ? t("confirm.delete.running") + " " : "") +
6036
+ (t ? t("bulk.dialog.warning.delete") : "删除操作不可撤销,请确认后再继续。");
6037
+ if (confirmPreviewState.loading) {
6038
+ const loadingMsg = (t ? t("confirm.delete.previewLoading") : "") || "正在检查关联的子智能体会话…";
6039
+ return `${loadingMsg}\n${baseWarn}`;
6040
+ }
6041
+ if (confirmPreviewState.error) {
6042
+ const errorMsg = confirmPreviewState.error;
6043
+ return `${errorMsg}\n${baseWarn}`;
6044
+ }
6045
+ const subagentsWarn = confirmPreviewState.subagentCount > 0
6046
+ ? (t ? t("confirm.delete.subagents", { count: confirmPreviewState.subagentCount }) : `将连带删除 ${confirmPreviewState.subagentCount} 个关联的子智能体会话。`)
6047
+ : "";
6048
+ return subagentsWarn ? `${subagentsWarn}\n${baseWarn}` : baseWarn;
6049
+ })(),
6050
+ confirmDisabled: confirmPreviewState.loading || Boolean(confirmPreviewState.error),
4159
6051
  confirmLabel: t ? t("confirm.delete.confirm") : "确认删除",
4160
6052
  cancelLabel: t ? t("confirm.cancel") : "取消",
4161
6053
  busy: false,
@@ -4185,36 +6077,55 @@ function isTopmostDialog(z) {
4185
6077
  }
4186
6078
 
4187
6079
  class SafePanel extends React.Component {
4188
- constructor(p) { super(p); this.state = { failed: false }; }
4189
- static getDerivedStateFromError() { return { failed: true }; }
6080
+ constructor(p) { super(p); this.state = { failed: false, error: null }; }
6081
+ static getDerivedStateFromError(error) { return { failed: true, error }; }
4190
6082
  componentDidCatch(error) { try { console.error("[dsh-session-manager] SessionManagerPanel crashed:", error); } catch (_) {} }
4191
6083
  render() {
4192
- if (this.state.failed) return null;
6084
+ if (this.state.failed) {
6085
+ const t = typeof this.props.t === "function" ? this.props.t : (k => k);
6086
+ const errorOverlay = renderInOverlay(h("div", { className: "sm-overlayLayer" },
6087
+ h("div", { className: "sm-nativeDialogLayer" },
6088
+ h("div", { className: "sm-nativeDialog sm-panelDialog", style: { padding: "20px" } },
6089
+ h("h3", null, t("panel.title") || "会话管理"),
6090
+ h("div", { className: "sm-error" }, "面板加载出错: " + (this.state.error?.message || String(this.state.error))),
6091
+ h("button", {
6092
+ type: "button",
6093
+ className: "sm-resetFilters",
6094
+ onClick: () => {
6095
+ this.setState({ failed: false, error: null });
6096
+ try { this.props.panelStore.set(false); } catch (_) {}
6097
+ }
6098
+ }, t("confirm.cancel") || "关闭")
6099
+ )
6100
+ )
6101
+ ));
6102
+ return errorOverlay;
6103
+ }
4193
6104
  return h(SessionManagerPanel, {
4194
6105
  open: true,
4195
6106
  onClose: () => { try { this.props.panelStore.set(false); } catch (_) {} },
4196
- useSessions: this.props.useSessions,
4197
- useWorkspaces: this.props.useWorkspaces,
6107
+ useSessions: this.props.useSessions || this.props.props?.useSessions,
6108
+ useWorkspaces: this.props.useWorkspaces || this.props.props?.useWorkspaces,
4198
6109
  t: this.props.t,
4199
6110
  // Bulk operations need to refresh ctx.sessions.list and the
4200
6111
  // workspace projection after /batch mutates disk state so the
4201
6112
  // panel reflects the new layout.
4202
6113
  onRefreshAfterBulk: async (...args) => {
4203
- try { await this.props.props.onBulkRefresh?.(...args); } catch (_) { /* ignore */ }
6114
+ try { await this.props.props?.onBulkRefresh?.(...args); } catch (_) { /* ignore */ }
4204
6115
  },
4205
- onOpen: (id) => { try { this.props.panelStore.set(false); this.props.props.onOpen(id); } catch (_) {} },
4206
- onArchive: this.props.props.onArchive,
4207
- onUnarchive: this.props.props.onUnarchive,
4208
- onRemove: this.props.props.onRemove,
4209
- onMove: this.props.props.onMove,
4210
- onMigrate: this.props.props.onMigrate
6116
+ onOpen: (id) => { try { this.props.panelStore.set(false); this.props.props?.onOpen?.(id); } catch (_) {} },
6117
+ onArchive: this.props.props?.onArchive,
6118
+ onUnarchive: this.props.props?.onUnarchive,
6119
+ onRemove: this.props.props?.onRemove,
6120
+ onMove: this.props.props?.onMove,
6121
+ onMigrate: this.props.props?.onMigrate
4211
6122
  });
4212
6123
  }
4213
6124
  }
4214
6125
 
4215
6126
  function FooterAction(props) {
4216
- const t = props.t;
4217
- const onOpenPanel = props.onOpenPanel;
6127
+ const t = typeof props.t === "function" ? props.t : (k => k);
6128
+ const onOpenPanel = typeof props.onOpenPanel === "function" ? props.onOpenPanel : () => panelStore.toggle();
4218
6129
  // props.wide comes from SidebarRoot via SidebarFooterActionOwnerProps.
4219
6130
  // When the sidebar is collapsed into the 56px rail, render a 36x36
4220
6131
  // icon-only button so the Chinese label does not word-wrap vertically
@@ -4245,6 +6156,7 @@ function isTopmostDialog(z) {
4245
6156
  wide ? h("span", { className: "sm-footerBtn-label" }, t("footer.label")) : null
4246
6157
  ),
4247
6158
  open ? h(SafePanel, {
6159
+ key: "sm-safe-panel",
4248
6160
  panelStore: panelStore,
4249
6161
  props: props,
4250
6162
  t: t,
@@ -4532,18 +6444,6 @@ function isTopmostDialog(z) {
4532
6444
  ),
4533
6445
  h("div", { className: "sm-settingsSection" },
4534
6446
  h("div", { className: "sm-settingsSectionTitle" }, t("settings.prefSection")),
4535
- h("div", { className: "sm-settingsRow" },
4536
- h("span", null, t("settings.registry")),
4537
- h("select", {
4538
- className: "sm-settingsSelect",
4539
- value: currentRegistry,
4540
- onChange: onRegistryChange,
4541
- "aria-label": t("settings.registry")
4542
- },
4543
- h("option", { value: "registry.npmjs.org" }, t("settings.registry.npm")),
4544
- h("option", { value: "registry.npmmirror.com" }, t("settings.registry.china"))
4545
- )
4546
- ),
4547
6447
  h("div", { className: "sm-settingsRow" },
4548
6448
  h("span", null, t("update.autoCheck")),
4549
6449
  h("label", { className: "sm-settingsCheckbox" },
@@ -4557,7 +6457,22 @@ function isTopmostDialog(z) {
4557
6457
  )
4558
6458
  ),
4559
6459
  h("div", { className: "sm-settingsRow" },
4560
- h("span", null, t("settings.dialogOpacity")),
6460
+ h("span", null, t("settings.registry")),
6461
+ h("select", {
6462
+ className: "sm-settingsSelect",
6463
+ value: currentRegistry,
6464
+ onChange: onRegistryChange,
6465
+ "aria-label": t("settings.registry")
6466
+ },
6467
+ h("option", { value: "registry.npmjs.org" }, t("settings.registry.npm")),
6468
+ h("option", { value: "registry.npmmirror.com" }, t("settings.registry.china"))
6469
+ )
6470
+ ),
6471
+ h("div", { className: "sm-settingsRow" },
6472
+ h("div", { className: "sm-settingsRowLeft" },
6473
+ h("span", null, t("settings.dialogOpacity")),
6474
+ h("span", { className: "sm-settingsHint sm-settingsHintInline" }, t("settings.dialogOpacity.hint"))
6475
+ ),
4561
6476
  h("div", { className: "sm-settingsSlider" },
4562
6477
  h("input", {
4563
6478
  type: "range",
@@ -4581,39 +6496,42 @@ function isTopmostDialog(z) {
4581
6496
  onClick: () => updateStore.resetDialogOpacity()
4582
6497
  }, t("settings.dialogOpacity.reset"))
4583
6498
  )
4584
- ),
4585
- h("div", { className: "sm-settingsHint" }, t("settings.dialogOpacity.hint"))
6499
+ )
4586
6500
  ),
4587
6501
  h("div", { className: "sm-settingsSection" },
4588
6502
  h("div", { className: "sm-settingsSectionTitle" }, t("settings.projectSection")),
4589
- h("div", { className: "sm-settingsRow sm-settingsRowInline" },
4590
- h("span", null, t("settings.github")),
4591
- h("a", {
4592
- href: "https://github.com/hkkz9522/dsh-session-manager",
4593
- target: "_blank",
4594
- rel: "noopener noreferrer",
4595
- className: "sm-settingsLink"
4596
- }, "hkkz9522/dsh-session-manager"),
4597
- renderTip(t("settings.star.tip"), h("a", {
4598
- href: "https://github.com/hkkz9522/dsh-session-manager",
4599
- target: "_blank",
4600
- rel: "noopener noreferrer",
4601
- className: "sm-settingsBadgeLink",
4602
- "aria-label": t("settings.star.tip")
4603
- },
4604
- h(StarIcon, { size: 14 }),
4605
- h("span", null, t("settings.star"))
4606
- )),
4607
- renderTip(t("settings.feedback.tip"), h("a", {
4608
- href: "https://github.com/hkkz9522/dsh-session-manager/issues",
4609
- target: "_blank",
4610
- rel: "noopener noreferrer",
4611
- className: "sm-settingsBadgeLink",
4612
- "aria-label": t("settings.feedback.tip")
4613
- },
4614
- h(IssueIcon, { size: 14 }),
4615
- h("span", null, t("settings.feedback"))
4616
- ))
6503
+ h("div", { className: "sm-settingsRow" },
6504
+ h("div", { className: "sm-settingsProjectLeft" },
6505
+ h("span", { className: "sm-settingsProjectLabel" }, t("settings.github")),
6506
+ h("a", {
6507
+ href: "https://github.com/hkkz9522/dsh-session-manager",
6508
+ target: "_blank",
6509
+ rel: "noopener noreferrer",
6510
+ className: "sm-settingsLink"
6511
+ }, "hkkz9522/dsh-session-manager")
6512
+ ),
6513
+ h("div", { className: "sm-settingsBadgeActions" },
6514
+ renderTip(t("settings.star.tip"), h("a", {
6515
+ href: "https://github.com/hkkz9522/dsh-session-manager",
6516
+ target: "_blank",
6517
+ rel: "noopener noreferrer",
6518
+ className: "sm-settingsBadgeLink",
6519
+ "aria-label": t("settings.star.tip")
6520
+ },
6521
+ h(StarIcon, { size: 14 }),
6522
+ h("span", null, t("settings.star"))
6523
+ )),
6524
+ renderTip(t("settings.feedback.tip"), h("a", {
6525
+ href: "https://github.com/hkkz9522/dsh-session-manager/issues",
6526
+ target: "_blank",
6527
+ rel: "noopener noreferrer",
6528
+ className: "sm-settingsBadgeLink",
6529
+ "aria-label": t("settings.feedback.tip")
6530
+ },
6531
+ h(IssueIcon, { size: 14 }),
6532
+ h("span", null, t("settings.feedback"))
6533
+ ))
6534
+ )
4617
6535
  )
4618
6536
  )
4619
6537
  );
@@ -4691,6 +6609,15 @@ function isTopmostDialog(z) {
4691
6609
  await runArchiveCommand("archive", sessionId);
4692
6610
  },
4693
6611
  onUnarchive: async (sessionId) => {
6612
+ for (const [, rec] of workspaceArchiveStore.getAllRecords()) {
6613
+ if (rec.archived === true && Array.isArray(rec.sessionIds) && rec.sessionIds.includes(sessionId)) {
6614
+ if (typeof window !== "undefined" && typeof window.alert === "function") {
6615
+ const msg = (typeof t === "function" ? t("error.workspaceArchived") : null) || "所属工作区已归档,无法取消归档该会话。请先在工作区管理中恢复工作区。";
6616
+ window.alert(msg);
6617
+ }
6618
+ return;
6619
+ }
6620
+ }
4694
6621
  // DSH ships the inverse command in 0.2.x; the old assumption that only
4695
6622
  // archive existed is what made unarchive bypass client state.
4696
6623
  await runArchiveCommand("unarchive", sessionId);
@@ -4698,7 +6625,18 @@ function isTopmostDialog(z) {
4698
6625
  onRemove: async (sessionId) => {
4699
6626
  const wasCurrent = isCurrentSession(sessionId);
4700
6627
  const response = await callApi("/delete", { sessionId });
4701
- if (!response.ok) throw new Error(response.error || "delete failed");
6628
+ if (!response.ok) {
6629
+ if (Array.isArray(response.deletedSubagents) && response.deletedSubagents.length > 0) {
6630
+ try { await annotationStore.load(true); } catch (_) {}
6631
+ try { await ctx.sessions?.refresh?.(); } catch (_) {}
6632
+ try { await ctx.workspaces?.refresh?.(); } catch (_) {}
6633
+ }
6634
+ const err = new Error(response.error || "delete failed");
6635
+ err.deletedSubagents = response.deletedSubagents || [];
6636
+ err.remainingSessions = response.remainingSessions || [sessionId];
6637
+ err.partialFailure = Boolean(response.partialFailure);
6638
+ throw err;
6639
+ }
4702
6640
  await annotationStore.load(true);
4703
6641
  if (response.result?.warning) window.alert(response.result.warning);
4704
6642
  if (wasCurrent || isCurrentSession(sessionId)) {
@@ -4771,6 +6709,16 @@ function isTopmostDialog(z) {
4771
6709
  }
4772
6710
  }
4773
6711
  },
6712
+ useSessions: (selector = s => s) => useSyncExternalStore(
6713
+ (cb) => (ctx.sessions?.list?.subscribe ? ctx.sessions.list.subscribe(cb) : () => {}),
6714
+ () => (ctx.sessions?.list?.getSnapshot ? selector(ctx.sessions.list.getSnapshot()) : selector({ ids: [], byId: {} })),
6715
+ () => (ctx.sessions?.list?.getSnapshot ? selector(ctx.sessions.list.getSnapshot()) : selector({ ids: [], byId: {} }))
6716
+ ),
6717
+ useWorkspaces: (selector = s => s) => useSyncExternalStore(
6718
+ (cb) => (ctx.workspaces?.list?.subscribe ? ctx.workspaces.list.subscribe(cb) : () => {}),
6719
+ () => (ctx.workspaces?.list?.getSnapshot ? selector(ctx.workspaces.list.getSnapshot()) : selector({ items: [] })),
6720
+ () => (ctx.workspaces?.list?.getSnapshot ? selector(ctx.workspaces.list.getSnapshot()) : selector({ items: [] }))
6721
+ ),
4774
6722
  });
4775
6723
  ctx.slots.inject("conversation.session.header.actions", () => ctx.slots.register({
4776
6724
  name: "conversation.session.header.actions",
@@ -4822,7 +6770,7 @@ function isTopmostDialog(z) {
4822
6770
  ctx.effect(() => {
4823
6771
  if (!updateStore.getAutoCheck()) return;
4824
6772
  const timer = setTimeout(() => {
4825
- updateStore.checkForUpdate({ silent: true, openOnFound: false, openDialog: false }).catch(() => {});
6773
+ updateStore.maybeAutoCheck({ isStartup: true }).catch(() => {});
4826
6774
  }, 1500);
4827
6775
  return () => {
4828
6776
  try {
@@ -4832,6 +6780,34 @@ function isTopmostDialog(z) {
4832
6780
  };
4833
6781
  }, "session-manager: auto update check");
4834
6782
 
6783
+ ctx.effect(() => {
6784
+ const disposes = [];
6785
+ if (ctx.workspaces?.list) {
6786
+ const d = workspaceArchiveStore.installWorkspaceModel(ctx.workspaces.list);
6787
+ if (typeof d === "function") disposes.push(d);
6788
+ }
6789
+ if (ctx.workspaces) {
6790
+ const d = workspaceArchiveStore.installWorkspaceCreate(ctx.workspaces);
6791
+ if (typeof d === "function") disposes.push(d);
6792
+ }
6793
+ if (ctx.uiWorkspace) {
6794
+ const d = workspaceArchiveStore.installConnectWorkspace(ctx.uiWorkspace);
6795
+ if (typeof d === "function") disposes.push(d);
6796
+ }
6797
+ return () => {
6798
+ for (const d of disposes) {
6799
+ try { d(); } catch (_) {}
6800
+ }
6801
+ };
6802
+ }, "session-manager: workspace sidebar hide");
6803
+
6804
+ ctx.effect(() => {
6805
+ const off = installCustomDropdownInterceptor();
6806
+ return () => {
6807
+ try { if (typeof off === "function") off(); } catch (_) {}
6808
+ };
6809
+ }, "session-manager: custom select popups");
6810
+
4835
6811
  // Keep <html data-sm-theme> in step with DSH through the official theme
4836
6812
  // event. ui-layout's ThemePresenter subscribes to the same event and
4837
6813
  // writes documentElement.colorScheme plus body[data-ds-dark-theme], so
@@ -4855,14 +6831,32 @@ function isTopmostDialog(z) {
4855
6831
  exports.apply = apply;
4856
6832
  exports.inject = inject;
4857
6833
  exports.updateStore = updateStore;
6834
+ exports.panelStore = panelStore;
4858
6835
  exports.compareSemver = compareSemver;
6836
+ exports.OverlayLayer = OverlayLayer;
4859
6837
  exports.UpdateDialog = UpdateDialog;
6838
+ exports.ConfirmDialog = ConfirmDialog;
6839
+ exports.MoveDialog = MoveDialog;
6840
+ exports.MigratePresetDialog = MigratePresetDialog;
6841
+ exports.BatchPreviewDialog = BatchPreviewDialog;
6842
+ exports.BatchResultDialog = BatchResultDialog;
6843
+ exports.BulkTagInputDialog = BulkTagInputDialog;
6844
+ exports.BulkChoiceDialog = BulkChoiceDialog;
6845
+ exports.AnnotationDialog = AnnotationDialog;
4860
6846
  exports.SessionManagerSettingsCard = SessionManagerSettingsCard;
4861
6847
  exports.WhaleIcon = WhaleIcon;
4862
6848
  exports.NpmIcon = NpmIcon;
4863
6849
  exports.CloseIcon = CloseIcon;
4864
6850
  exports.StarIcon = StarIcon;
4865
6851
  exports.IssueIcon = IssueIcon;
6852
+ exports.workspaceArchiveStore = workspaceArchiveStore;
6853
+ exports.SmSwitch = SmSwitch;
6854
+ exports.executeArchiveWorkspace = executeArchiveWorkspace;
6855
+ exports.executeUnarchiveWorkspace = executeUnarchiveWorkspace;
6856
+ exports.openCustomSelect = openCustomSelect;
6857
+ exports.closeCustomSelect = closeCustomSelect;
6858
+ exports.installCustomDropdownInterceptor = installCustomDropdownInterceptor;
6859
+ exports.SM_SELECT_SELECTOR = SM_SELECT_SELECTOR;
4866
6860
  return module.exports;
4867
6861
  }
4868
6862
  });