dsh-session-manager 0.4.1 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -52
- package/README.zh.md +116 -116
- package/lib/client.js +1113 -1034
- package/package.json +54 -54
package/lib/client.js
CHANGED
|
@@ -1,1035 +1,1114 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @dsh-session-manager — Client half.
|
|
3
|
-
* v0.3.0 — replaces 0.2.1 client.js which had V8 strict-mode parse errors.
|
|
4
|
-
* Functionally equivalent: header actions (open/archive/move/delete/manage),
|
|
5
|
-
* footer action (open session manager panel), and the panel itself with
|
|
6
|
-
* delete/move confirm dialogs. The "preset migration" feature is registered
|
|
7
|
-
* as a separate slot in a sibling client bundle.
|
|
8
|
-
*/
|
|
9
|
-
window.__ModuleLoader__.load({
|
|
10
|
-
id: "dsh-session-manager",
|
|
11
|
-
factory: (require) => {
|
|
12
|
-
var module = { exports: {} };
|
|
13
|
-
var exports = module.exports;
|
|
14
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
15
|
-
|
|
16
|
-
const React = require("react");
|
|
17
|
-
const { useState, useEffect, useMemo, useRef, useSyncExternalStore } = React;
|
|
18
|
-
const P = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
19
|
-
if (typeof window !== "undefined") {
|
|
20
|
-
window.__smReact = React;
|
|
21
|
-
window.__smReactDOM = React.createPortal ? React : null;
|
|
22
|
-
// Tiny imperative portal helper: takes a React element + a host
|
|
23
|
-
// DOM node, creates a React root on the host (lazily), and re-renders
|
|
24
|
-
// the element into it on each call. Used by ConfirmDialog to escape
|
|
25
|
-
// the sidebar overflow:hidden ancestor.
|
|
26
|
-
try {
|
|
27
|
-
const RDOM = require("react-dom/client");
|
|
28
|
-
if (RDOM && typeof RDOM.createRoot === "function") {
|
|
29
|
-
window.__smRenderInto = (tree, host) => {
|
|
30
|
-
if (!tree || !host) return;
|
|
31
|
-
try {
|
|
32
|
-
if (!host.__smRoot) host.__smRoot = RDOM.createRoot(host);
|
|
33
|
-
host.__smRoot.render(tree);
|
|
34
|
-
} catch (_) { /* ignore render failure */ }
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
} catch (_) { /* react-dom/client not available */ }
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const NS = "session-manager";
|
|
41
|
-
const API = "/session-manager/api";
|
|
42
|
-
|
|
43
|
-
const zh = {
|
|
44
|
-
"header.aria": "会话操作",
|
|
45
|
-
"menu.open": "打开",
|
|
46
|
-
"menu.archive": "归档会话",
|
|
47
|
-
"menu.unarchive": "移出归档",
|
|
48
|
-
"menu.delete": "删除会话…",
|
|
49
|
-
"menu.move": "移动到工作区…",
|
|
50
|
-
"menu.manage": "打开会话管理…",
|
|
51
|
-
"footer.aria": "会话管理",
|
|
52
|
-
"footer.label": "会话管理",
|
|
53
|
-
"panel.title": "会话管理",
|
|
54
|
-
"panel.filter.all": "全部",
|
|
55
|
-
"panel.filter.active": "未归档",
|
|
56
|
-
"panel.filter.archived": "已归档",
|
|
57
|
-
"panel.empty": "暂无会话",
|
|
58
|
-
"panel.empty.archived": "归档为空",
|
|
59
|
-
"panel.archived": "已归档",
|
|
60
|
-
"panel.running": "运行中",
|
|
61
|
-
"panel.current": "当前",
|
|
62
|
-
"panel.session.new": "新建会话",
|
|
63
|
-
"panel.close": "关闭",
|
|
64
|
-
"row.open": "打开",
|
|
65
|
-
"row.archive": "归档",
|
|
66
|
-
"row.unarchive": "移出归档",
|
|
67
|
-
"row.move": "移动至工作区",
|
|
68
|
-
"row.delete": "删除会话",
|
|
69
|
-
"row.migrate": "迁移预设",
|
|
70
|
-
"migrate.title": "迁移 Agent 预设",
|
|
71
|
-
"migrate.current": "当前预设",
|
|
72
|
-
"migrate.target": "目标预设",
|
|
73
|
-
"migrate.noPresets": "没有可用的目标预设",
|
|
74
|
-
"migrate.same": "请选择与当前预设不同的目标预设",
|
|
75
|
-
"migrate.confirm": "确认迁移",
|
|
76
|
-
"migrate.completed": "Agent 预设已迁移为 {preset}",
|
|
77
|
-
"confirm.delete.title": "删除会话",
|
|
78
|
-
"confirm.delete.desc": "会话「{title}」将被永久删除,包括其全部消息记录与磁盘文件,此操作不可撤销。",
|
|
79
|
-
"confirm.delete.running": "该会话正在运行,删除将立即中断它。",
|
|
80
|
-
"confirm.delete.confirm": "确认删除",
|
|
81
|
-
"confirm.move.title": "移动会话到工作区",
|
|
82
|
-
"confirm.move.desc": "将会话「{title}」移动到目标工作区。",
|
|
83
|
-
"confirm.move.running": "该会话正在运行,移动会中断并关闭它;之后可在目标工作区重新打开继续。",
|
|
84
|
-
"confirm.move.empty": "没有可移动到的其他工作区。",
|
|
85
|
-
"confirm.move.select": "选择目标工作区",
|
|
86
|
-
"confirm.move.current": "当前工作区:{name}",
|
|
87
|
-
"confirm.move.confirm": "确认移动",
|
|
88
|
-
"confirm.cancel": "取消",
|
|
89
|
-
"busy.processing": "处理中…",
|
|
90
|
-
"error.operation": "操作失败:{message}",
|
|
91
|
-
"workspace.default": "默认工作区",
|
|
92
|
-
"workspace.sessions": "{count} 个会话"
|
|
93
|
-
};
|
|
94
|
-
const en = {
|
|
95
|
-
"header.aria": "Session actions",
|
|
96
|
-
"menu.open": "Open",
|
|
97
|
-
"menu.archive": "Archive session",
|
|
98
|
-
"menu.unarchive": "Unarchive session",
|
|
99
|
-
"menu.delete": "Delete session…",
|
|
100
|
-
"menu.move": "Move to workspace…",
|
|
101
|
-
"menu.manage": "Open session manager…",
|
|
102
|
-
"footer.aria": "Session manager",
|
|
103
|
-
"footer.label": "Sessions",
|
|
104
|
-
"panel.title": "Session manager",
|
|
105
|
-
"panel.filter.all": "All",
|
|
106
|
-
"panel.filter.active": "Active",
|
|
107
|
-
"panel.filter.archived": "Archived",
|
|
108
|
-
"panel.empty": "No sessions",
|
|
109
|
-
"panel.empty.archived": "No archived sessions",
|
|
110
|
-
"panel.archived": "Archived",
|
|
111
|
-
"panel.running": "Running",
|
|
112
|
-
"panel.current": "Current",
|
|
113
|
-
"panel.session.new": "New session",
|
|
114
|
-
"panel.close": "Close",
|
|
115
|
-
"row.open": "Open",
|
|
116
|
-
"row.archive": "Archive",
|
|
117
|
-
"row.unarchive": "Unarchive",
|
|
118
|
-
"row.move": "Move to workspace",
|
|
119
|
-
"row.delete": "Delete session",
|
|
120
|
-
"row.migrate": "Migrate preset",
|
|
121
|
-
"migrate.title": "Migrate Agent preset",
|
|
122
|
-
"migrate.current": "Current preset",
|
|
123
|
-
"migrate.target": "Target preset",
|
|
124
|
-
"migrate.noPresets": "No target presets are available",
|
|
125
|
-
"migrate.same": "Choose a target preset different from the current preset",
|
|
126
|
-
"migrate.confirm": "Migrate",
|
|
127
|
-
"migrate.completed": "Agent preset migrated to {preset}",
|
|
128
|
-
"confirm.delete.title": "Delete session",
|
|
129
|
-
"confirm.delete.desc": "Session \"{title}\" will be permanently deleted, including all its messages and files on disk. This cannot be undone.",
|
|
130
|
-
"confirm.delete.running": "This session is running; deleting will interrupt it immediately.",
|
|
131
|
-
"confirm.delete.confirm": "Delete",
|
|
132
|
-
"confirm.move.title": "Move session to workspace",
|
|
133
|
-
"confirm.move.desc": "Move session \"{title}\" to the target workspace.",
|
|
134
|
-
"confirm.move.running": "This session is running; moving will interrupt and close it; you can reopen it from the target workspace to continue.",
|
|
135
|
-
"confirm.move.empty": "No other workspaces to move to.",
|
|
136
|
-
"confirm.move.select": "Select target workspace",
|
|
137
|
-
"confirm.move.current": "Current workspace: {name}",
|
|
138
|
-
"confirm.move.confirm": "Move",
|
|
139
|
-
"confirm.cancel": "Cancel",
|
|
140
|
-
"busy.processing": "Working…",
|
|
141
|
-
"error.operation": "Operation failed: {message}",
|
|
142
|
-
"workspace.default": "Default workspace",
|
|
143
|
-
"workspace.sessions": "{count} sessions"
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
const css = [
|
|
147
|
-
".sm-header{display:flex;align-items:center;gap:6px}",
|
|
148
|
-
".sm-headerBtn{box-sizing:border-box;min-height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;align-items:center;gap:4px;padding:3px 10px;font-size:12px;line-height:18px;display:inline-flex;white-space:nowrap}",
|
|
149
|
-
".sm-headerBtn:hover:not(:disabled),.sm-headerBtn:focus-visible{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
150
|
-
".sm-headerBtn:disabled{opacity:.5;cursor:not-allowed}",
|
|
151
|
-
".sm-headerBtn svg{flex:none}",
|
|
152
|
-
".sm-busy{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:28px;margin-left:4px}",
|
|
153
|
-
".sm-footer{display:flex}",
|
|
154
|
-
".sm-footerBtn{min-height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:6px;align-items:center;gap:6px;padding:3px 8px;font-size:12px;line-height:18px;display:inline-flex}",
|
|
155
|
-
".sm-footerBtn:hover,.sm-footerBtn:focus-visible{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover)}",
|
|
156
|
-
".sm-footerBtn svg{flex:none}",
|
|
157
|
-
".sm-panel{display:flex;flex-direction:column;gap:10px;min-height:280px;max-height:min(70vh,640px);min-width:480px}",
|
|
158
|
-
".sm-filter{display:flex;gap:4px;flex-wrap:wrap}",
|
|
159
|
-
".sm-filterBtn{cursor:pointer;color:var(--dsw-alias-label-secondary);background:0 0;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;padding:3px 12px;font-size:12px;line-height:18px}",
|
|
160
|
-
".sm-filterBtn:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
161
|
-
".sm-filterBtn.sm-on{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-fill-l2);border-color:transparent}",
|
|
162
|
-
".sm-list{display:flex;flex-direction:column;gap:4px;min-height:0;overflow:auto;flex:1}",
|
|
163
|
-
".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}",
|
|
164
|
-
".sm-row:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
165
|
-
".sm-rowCurrent{border-color:var(--dsw-alias-accent-primary,var(--dsw-alias-label-secondary))}",
|
|
166
|
-
".sm-rowMain{min-width:0;display:flex;flex-direction:column;gap:2px;flex:1 1 280px}",
|
|
167
|
-
".sm-rowTitle{color:var(--dsw-alias-label-primary);font-size:13px;line-height:18px;display:flex;align-items:center;gap:6px;min-width:0}",
|
|
168
|
-
".sm-rowTitle>span:first-child{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}",
|
|
169
|
-
".sm-rowMeta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}",
|
|
170
|
-
".sm-rowActions{display:flex;gap:4px;flex:none;flex-wrap:wrap;justify-content:flex-end}",
|
|
171
|
-
".sm-badge{flex:none;border-radius:999px;padding:0 6px;font-size:10px;line-height:16px}",
|
|
172
|
-
".sm-badgeArchived{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-fill-l2)}",
|
|
173
|
-
".sm-badgeRunning{color:var(--dsw-alias-state-warning-primary,#d97706);background:rgba(217,119,6,.12)}",
|
|
174
|
-
".sm-badgeCurrent{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-fill-l2)}",
|
|
175
|
-
".sm-danger{color:var(--dsw-alias-state-error-primary) !important;border-color:var(--dsw-alias-state-error-primary) !important}",
|
|
176
|
-
".sm-dangerText{color:var(--dsw-alias-state-error-primary) !important}",
|
|
177
|
-
".sm-warn{display:flex;align-items:flex-start;gap:8px;color:var(--dsw-alias-label-secondary);background:rgba(217,119,6,.08);border:1px solid rgba(217,119,6,.25);border-radius:8px;padding:8px 10px;margin-top:4px}",
|
|
178
|
-
".sm-warn svg{flex:none;margin-top:1px}",
|
|
179
|
-
".sm-error{color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px;min-height:0;margin-top:4px}",
|
|
180
|
-
".sm-foot{display:flex;justify-content:flex-end;align-items:center;gap:8px;width:100%}",
|
|
181
|
-
".sm-footError{color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px;margin-right:auto}",
|
|
182
|
-
".sm-empty{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px;text-align:center;padding:24px 0}",
|
|
183
|
-
".sm-moveSection{display:flex;flex-direction:column;gap:8px}",
|
|
184
|
-
".sm-moveLabel{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}",
|
|
185
|
-
".sm-moveList{display:flex;flex-direction:column;gap:4px;max-height:200px;overflow:auto}",
|
|
186
|
-
".sm-moveItem{box-sizing:border-box;display:flex;align-items:center;justify-content:space-between;padding:8px 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;cursor:pointer;transition:all .15s}",
|
|
187
|
-
".sm-moveItem:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
188
|
-
".sm-moveItemSelected{border-color:var(--dsw-alias-accent-primary);background:var(--dsw-alias-accent-primary-bg)}",
|
|
189
|
-
".sm-moveItemName{color:var(--dsw-alias-label-primary);font-size:13px;line-height:18px}",
|
|
190
|
-
".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%;z-index:50;display:flex;flex-direction:column;min-width:160px;padding:4px;background:var(--dsw-alias-surface-l1);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;box-shadow:0 4px 16px rgba(0,0,0,.12)}.sm-headerMenuItem{display:flex;align-items:center;width:100%;text-align:left;background:0 0;border:0;border-radius:6px;color:var(--dsw-alias-label-primary);cursor:pointer;padding:6px 10px;font-size:13px;line-height:18px}.sm-headerMenuItem:hover{background:var(--dsw-alias-interactive-bg-hover)}.sm-headerMenuItemDanger{color:var(--dsw-alias-state-error-primary)}.sm-headerBtn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:32px;height:auto;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-fill-l2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;cursor:pointer;padding:4px 12px;font-size:13px;line-height:18px;white-space:nowrap;transition:background .15s,color .15s,border-color .15s}.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-accent-primary);outline-offset:1px}.sm-headerBtn svg{width:16px;height:16px}.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-accent-primary);color:#fff;border-color:var(--dsw-alias-accent-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-fill-l2);color:var(--dsw-alias-label-primary);font-size:13px;line-height:18px}.sm-migrateWarn{padding:10px 12px;border:1px solid rgba(217,119,6,.25);border-radius:8px;background:rgba(217,119,6,.08);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-headerBtn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l1)}.sm-headerBtnDanger{background:var(--dsw-alias-state-error-tertiary);color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary);font-weight:500}.sm-headerBtnDanger:hover:not(:disabled){background:#d92d20!important;color:#fff!important;border-color:#d92d20!important}.sm-header{display:inline-flex;align-items:center;gap:6px}.sm-headerBtnActive{background:var(--dsw-alias-fill-l3);color:var(--dsw-alias-label-primary)}",
|
|
191
|
-
".sm-panelDialog{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);width:560px;max-width:calc(100vw - 32px);max-height:540px;height:auto;box-sizing:border-box;display:flex;flex-direction:column;gap:0;background:var(--dsw-alias-surface-l1,#fff);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:9999;opacity:1}.sm-panelDialog .sm-nativeDialogHeader{padding:10px 16px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee);flex:none}.sm-panelDialog .sm-nativeDialogTitle{margin:0;font-size:14px;line-height:20px;font-weight:600}.sm-panelDialog .sm-nativeDialogFooter{padding:10px 16px;border-top:1px solid var(--dsw-alias-border-l2,#eee);flex:none;flex-wrap:wrap;justify-content:flex-end;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:0 10px;height:36px;min-height:36px;border:1px solid transparent;border-radius:8px;background:transparent}.sm-panelDialog .sm-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.sm-panelDialog .sm-rowCurrent{background:var(--dsw-alias-fill-l2);border-color:var(--dsw-alias-accent-primary,#356ae6)}.sm-panelDialog .sm-rowMain{display:flex;align-items:stretch!important;gap:8px;min-width:0;flex:1 1 auto;overflow:hidden;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}.sm-confirmDialog{z-index:10000}.sm-confirmDialog.sm-nativeDialogLayer{position:fixed;left:calc(50vw + 184px);top:calc(50vh - 90px);width:300px;max-width: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: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;box-sizing:border-box;background:var(--dsw-alias-surface-l1,#fff);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{padding:12px 16px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee)}.sm-confirmDialog .sm-nativeDialogTitle{margin:0;font-size:14px;line-height:20px;font-weight:600}.sm-confirmDialog .sm-nativeDialogBody{padding:10px 16px;display:flex;flex-direction:column;gap:8px;min-height:0}.sm-confirmDialog .sm-nativeDialogDescription{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);white-space:pre-wrap}.sm-confirmDialog .sm-warn{padding:6px 10px;font-size:12px;line-height:16px;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:flex-end;gap:8px;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)}.sm-confirmDialog .sm-nativeDialogCancel:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.sm-confirmDialog .sm-nativeDialogConfirm{background:var(--dsw-alias-state-error-primary);color:#fff;border-color:var(--dsw-alias-state-error-primary)}.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;height:22px;padding:0 8px;font-size:11px;line-height:14px;border-radius:6px;border:1px solid transparent;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer}.sm-panelDialog .sm-rowActions .sm-rowBtn:hover{background:var(--dsw-alias-fill-l2);color:var(--dsw-alias-label-primary)}.sm-panelDialog .sm-rowActions .sm-rowBtnDanger{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-fill-l2);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 12px;font-size:12px;line-height:18px;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;background:transparent;color:var(--dsw-alias-label-primary);cursor:pointer}.sm-panelDialog .sm-nativeDialogCancel{background:transparent}.sm-panelDialog .sm-nativeDialogGhost{background:transparent;border-color:var(--dsw-alias-border-l2)}.sm-panelDialog .sm-nativeDialogConfirm{background:var(--dsw-alias-accent-primary,#356ae6);color:#fff;border-color:var(--dsw-alias-accent-primary,#356ae6)}.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:flex-end;gap:8px;margin-top:14px}.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:auto;max-height:min(70vh,520px);box-sizing:border-box;background-color:#ffffff;background-color:var(--dsw-alias-surface-l1,#fff);color:var(--dsw-alias-label-primary,#111);border:1px solid #d4d4d8;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;opacity:1}.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:#ffffff;background-color:var(--dsw-alias-surface-l1,#fff);color:var(--dsw-alias-label-primary,#111);border:1px solid #d4d4d8;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:12px;box-shadow:0 16px 48px rgba(0,0,0,.45);overflow:hidden;opacity:1;z-index:10001;backdrop-filter:none;-webkit-backdrop-filter:none}.sm-migrateDialog .sm-nativeDialogHeader{padding:12px 18px;border-bottom:1px solid #eee;border-bottom:1px solid var(--dsw-alias-border-l2,#eee);flex:none;background-color:#fff;background-color:var(--dsw-alias-surface-l1,#fff)}.sm-migrateDialog .sm-nativeDialogTitle{margin:0;font-size:15px;line-height:22px;font-weight:600;color:#111;color:var(--dsw-alias-label-primary,#111)}.sm-migrateDialog .sm-migratePanel{padding:12px 18px;display:flex;flex-direction:column;gap:14px;background-color:#fff;background-color:var(--dsw-alias-surface-l1,#fff)}.sm-migrateDialog .sm-nativeDialogBody{padding:0;background-color:#fff;background-color:var(--dsw-alias-surface-l1,#fff)}.sm-migrateDialog .sm-nativeDialogFooter{padding:12px 18px;border-top:1px solid #eee;border-top:1px solid var(--dsw-alias-border-l2,#eee);flex:none;flex-wrap:wrap;justify-content:flex-end;gap:8px;background-color:#fff;background-color:var(--dsw-alias-surface-l1,#fff)}.sm-migrateDialog .sm-migrateRow>label{color:#444;color:var(--dsw-alias-label-secondary,#444)}.sm-migrateDialog .sm-migrateChip{padding:6px 12px;border-radius:999px;border:1px solid #d4d4d8;border:1px solid var(--dsw-alias-border-l2,#ddd);background-color:#f5f5f5;background-color:var(--dsw-alias-fill-l2,#f5f5f5);color:#111;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:#e8e8e8;background-color:var(--dsw-alias-interactive-bg-hover,#e8e8e8)}.sm-migrateDialog .sm-migrateChipOn{background-color:#356ae6;background-color:var(--dsw-alias-accent-primary,#356ae6);color:#fff;border-color:#356ae6;border-color:var(--dsw-alias-accent-primary,#356ae6)}.sm-migrateDialog .sm-migrateChip.sm-migrateChipOn:hover:not(:disabled){background-color:#4576f0;background-color:var(--dsw-alias-accent-primary-hover,#4576f0);color:#fff;border-color:#4576f0;border-color:var(--dsw-alias-accent-primary-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:#888;color:var(--dsw-alias-label-tertiary,#888)}.sm-migrateDialog .sm-migratePreview{padding:10px 12px;border:1px solid #d4d4d8;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;background-color:#f5f5f5;background-color:var(--dsw-alias-fill-l2,#f5f5f5);color:#111;color:var(--dsw-alias-label-primary,#111);font-size:13px;line-height:18px}.sm-migrateDialog .sm-migrateWarn{padding:10px 12px;border:1px solid rgba(217,119,6,.25);border-radius:8px;background-color:rgba(217,119,6,.08);color:#444;color:var(--dsw-alias-label-secondary,#444);font-size:13px;line-height:18px}.sm-migrateDialog .sm-nativeDialogButton{min-height:32px;padding:4px 14px;font-size:13px;line-height:20px;border:1px solid #d4d4d8;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;cursor:pointer;background-color:#fff;background-color:var(--dsw-alias-fill-l2,#fff);color:#111;color:var(--dsw-alias-label-primary,#111)}.sm-migrateDialog .sm-nativeDialogButton:hover:not(:disabled){background-color:#e8e8e8;background-color:var(--dsw-alias-interactive-bg-hover,#e8e8e8)}.sm-migrateDialog .sm-nativeDialogConfirm{background-color:#356ae6;background-color:var(--dsw-alias-accent-primary,#356ae6);color:#fff;border-color:#356ae6;border-color:var(--dsw-alias-accent-primary,#356ae6)}.sm-migrateDialog .sm-nativeDialogConfirm:hover:not(:disabled){filter:brightness(.94)}",
|
|
192
|
-
".sm-panelDialog .sm-nativeDialogHeader{display:flex;align-items:center;gap:8px}.sm-panelHeaderClose{min-height:26px;padding:2px 9px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:7px;background:transparent;color:var(--dsw-alias-label-secondary,#555);cursor:pointer;font-size:12px;line-height:18px}.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-fill-l2,#f5f5f5);font-size:13px;line-height:18px;color:var(--dsw-alias-label-primary,#111)}.sm-migrateConfirmLayer.sm-confirmDialog.sm-nativeDialogLayer{width:300px;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:none;max-height:min(70vh,520px);z-index:auto}.sm-migrateConfirmLayer .sm-migratePanel{min-width:0}.sm-migrateConfirmLayer .sm-nativeDialogButton.sm-nativeDialogConfirm:hover:not(:disabled){background-color:#4576f0;background-color:var(--dsw-alias-accent-primary-hover,#4576f0);color:#fff;border-color:#4576f0;border-color:var(--dsw-alias-accent-primary-hover,#4576f0);filter:none}",
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
"
|
|
222
|
-
"
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if (
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
"aria-
|
|
301
|
-
"
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
)
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
);
|
|
327
|
-
if (
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
const
|
|
347
|
-
const
|
|
348
|
-
const
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
const
|
|
432
|
-
|
|
433
|
-
const
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
})
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
if (!
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
);
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
//
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
const
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
},
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @dsh-session-manager — Client half.
|
|
3
|
+
* v0.3.0 — replaces 0.2.1 client.js which had V8 strict-mode parse errors.
|
|
4
|
+
* Functionally equivalent: header actions (open/archive/move/delete/manage),
|
|
5
|
+
* footer action (open session manager panel), and the panel itself with
|
|
6
|
+
* delete/move confirm dialogs. The "preset migration" feature is registered
|
|
7
|
+
* as a separate slot in a sibling client bundle.
|
|
8
|
+
*/
|
|
9
|
+
window.__ModuleLoader__.load({
|
|
10
|
+
id: "dsh-session-manager",
|
|
11
|
+
factory: (require) => {
|
|
12
|
+
var module = { exports: {} };
|
|
13
|
+
var exports = module.exports;
|
|
14
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
15
|
+
|
|
16
|
+
const React = require("react");
|
|
17
|
+
const { useState, useEffect, useMemo, useRef, useSyncExternalStore } = React;
|
|
18
|
+
const P = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
19
|
+
if (typeof window !== "undefined") {
|
|
20
|
+
window.__smReact = React;
|
|
21
|
+
window.__smReactDOM = React.createPortal ? React : null;
|
|
22
|
+
// Tiny imperative portal helper: takes a React element + a host
|
|
23
|
+
// DOM node, creates a React root on the host (lazily), and re-renders
|
|
24
|
+
// the element into it on each call. Used by ConfirmDialog to escape
|
|
25
|
+
// the sidebar overflow:hidden ancestor.
|
|
26
|
+
try {
|
|
27
|
+
const RDOM = require("react-dom/client");
|
|
28
|
+
if (RDOM && typeof RDOM.createRoot === "function") {
|
|
29
|
+
window.__smRenderInto = (tree, host) => {
|
|
30
|
+
if (!tree || !host) return;
|
|
31
|
+
try {
|
|
32
|
+
if (!host.__smRoot) host.__smRoot = RDOM.createRoot(host);
|
|
33
|
+
host.__smRoot.render(tree);
|
|
34
|
+
} catch (_) { /* ignore render failure */ }
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
} catch (_) { /* react-dom/client not available */ }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const NS = "session-manager";
|
|
41
|
+
const API = "/session-manager/api";
|
|
42
|
+
|
|
43
|
+
const zh = {
|
|
44
|
+
"header.aria": "会话操作",
|
|
45
|
+
"menu.open": "打开",
|
|
46
|
+
"menu.archive": "归档会话",
|
|
47
|
+
"menu.unarchive": "移出归档",
|
|
48
|
+
"menu.delete": "删除会话…",
|
|
49
|
+
"menu.move": "移动到工作区…",
|
|
50
|
+
"menu.manage": "打开会话管理…",
|
|
51
|
+
"footer.aria": "会话管理",
|
|
52
|
+
"footer.label": "会话管理",
|
|
53
|
+
"panel.title": "会话管理",
|
|
54
|
+
"panel.filter.all": "全部",
|
|
55
|
+
"panel.filter.active": "未归档",
|
|
56
|
+
"panel.filter.archived": "已归档",
|
|
57
|
+
"panel.empty": "暂无会话",
|
|
58
|
+
"panel.empty.archived": "归档为空",
|
|
59
|
+
"panel.archived": "已归档",
|
|
60
|
+
"panel.running": "运行中",
|
|
61
|
+
"panel.current": "当前",
|
|
62
|
+
"panel.session.new": "新建会话",
|
|
63
|
+
"panel.close": "关闭",
|
|
64
|
+
"row.open": "打开",
|
|
65
|
+
"row.archive": "归档",
|
|
66
|
+
"row.unarchive": "移出归档",
|
|
67
|
+
"row.move": "移动至工作区",
|
|
68
|
+
"row.delete": "删除会话",
|
|
69
|
+
"row.migrate": "迁移预设",
|
|
70
|
+
"migrate.title": "迁移 Agent 预设",
|
|
71
|
+
"migrate.current": "当前预设",
|
|
72
|
+
"migrate.target": "目标预设",
|
|
73
|
+
"migrate.noPresets": "没有可用的目标预设",
|
|
74
|
+
"migrate.same": "请选择与当前预设不同的目标预设",
|
|
75
|
+
"migrate.confirm": "确认迁移",
|
|
76
|
+
"migrate.completed": "Agent 预设已迁移为 {preset}",
|
|
77
|
+
"confirm.delete.title": "删除会话",
|
|
78
|
+
"confirm.delete.desc": "会话「{title}」将被永久删除,包括其全部消息记录与磁盘文件,此操作不可撤销。",
|
|
79
|
+
"confirm.delete.running": "该会话正在运行,删除将立即中断它。",
|
|
80
|
+
"confirm.delete.confirm": "确认删除",
|
|
81
|
+
"confirm.move.title": "移动会话到工作区",
|
|
82
|
+
"confirm.move.desc": "将会话「{title}」移动到目标工作区。",
|
|
83
|
+
"confirm.move.running": "该会话正在运行,移动会中断并关闭它;之后可在目标工作区重新打开继续。",
|
|
84
|
+
"confirm.move.empty": "没有可移动到的其他工作区。",
|
|
85
|
+
"confirm.move.select": "选择目标工作区",
|
|
86
|
+
"confirm.move.current": "当前工作区:{name}",
|
|
87
|
+
"confirm.move.confirm": "确认移动",
|
|
88
|
+
"confirm.cancel": "取消",
|
|
89
|
+
"busy.processing": "处理中…",
|
|
90
|
+
"error.operation": "操作失败:{message}",
|
|
91
|
+
"workspace.default": "默认工作区",
|
|
92
|
+
"workspace.sessions": "{count} 个会话"
|
|
93
|
+
};
|
|
94
|
+
const en = {
|
|
95
|
+
"header.aria": "Session actions",
|
|
96
|
+
"menu.open": "Open",
|
|
97
|
+
"menu.archive": "Archive session",
|
|
98
|
+
"menu.unarchive": "Unarchive session",
|
|
99
|
+
"menu.delete": "Delete session…",
|
|
100
|
+
"menu.move": "Move to workspace…",
|
|
101
|
+
"menu.manage": "Open session manager…",
|
|
102
|
+
"footer.aria": "Session manager",
|
|
103
|
+
"footer.label": "Sessions",
|
|
104
|
+
"panel.title": "Session manager",
|
|
105
|
+
"panel.filter.all": "All",
|
|
106
|
+
"panel.filter.active": "Active",
|
|
107
|
+
"panel.filter.archived": "Archived",
|
|
108
|
+
"panel.empty": "No sessions",
|
|
109
|
+
"panel.empty.archived": "No archived sessions",
|
|
110
|
+
"panel.archived": "Archived",
|
|
111
|
+
"panel.running": "Running",
|
|
112
|
+
"panel.current": "Current",
|
|
113
|
+
"panel.session.new": "New session",
|
|
114
|
+
"panel.close": "Close",
|
|
115
|
+
"row.open": "Open",
|
|
116
|
+
"row.archive": "Archive",
|
|
117
|
+
"row.unarchive": "Unarchive",
|
|
118
|
+
"row.move": "Move to workspace",
|
|
119
|
+
"row.delete": "Delete session",
|
|
120
|
+
"row.migrate": "Migrate preset",
|
|
121
|
+
"migrate.title": "Migrate Agent preset",
|
|
122
|
+
"migrate.current": "Current preset",
|
|
123
|
+
"migrate.target": "Target preset",
|
|
124
|
+
"migrate.noPresets": "No target presets are available",
|
|
125
|
+
"migrate.same": "Choose a target preset different from the current preset",
|
|
126
|
+
"migrate.confirm": "Migrate",
|
|
127
|
+
"migrate.completed": "Agent preset migrated to {preset}",
|
|
128
|
+
"confirm.delete.title": "Delete session",
|
|
129
|
+
"confirm.delete.desc": "Session \"{title}\" will be permanently deleted, including all its messages and files on disk. This cannot be undone.",
|
|
130
|
+
"confirm.delete.running": "This session is running; deleting will interrupt it immediately.",
|
|
131
|
+
"confirm.delete.confirm": "Delete",
|
|
132
|
+
"confirm.move.title": "Move session to workspace",
|
|
133
|
+
"confirm.move.desc": "Move session \"{title}\" to the target workspace.",
|
|
134
|
+
"confirm.move.running": "This session is running; moving will interrupt and close it; you can reopen it from the target workspace to continue.",
|
|
135
|
+
"confirm.move.empty": "No other workspaces to move to.",
|
|
136
|
+
"confirm.move.select": "Select target workspace",
|
|
137
|
+
"confirm.move.current": "Current workspace: {name}",
|
|
138
|
+
"confirm.move.confirm": "Move",
|
|
139
|
+
"confirm.cancel": "Cancel",
|
|
140
|
+
"busy.processing": "Working…",
|
|
141
|
+
"error.operation": "Operation failed: {message}",
|
|
142
|
+
"workspace.default": "Default workspace",
|
|
143
|
+
"workspace.sessions": "{count} sessions"
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const css = [
|
|
147
|
+
".sm-header{display:flex;align-items:center;gap:6px}",
|
|
148
|
+
".sm-headerBtn{box-sizing:border-box;min-height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;align-items:center;gap:4px;padding:3px 10px;font-size:12px;line-height:18px;display:inline-flex;white-space:nowrap}",
|
|
149
|
+
".sm-headerBtn:hover:not(:disabled),.sm-headerBtn:focus-visible{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
150
|
+
".sm-headerBtn:disabled{opacity:.5;cursor:not-allowed}",
|
|
151
|
+
".sm-headerBtn svg{flex:none}",
|
|
152
|
+
".sm-busy{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:28px;margin-left:4px}",
|
|
153
|
+
".sm-footer{display:flex}",
|
|
154
|
+
".sm-footerBtn{min-height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:6px;align-items:center;gap:6px;padding:3px 8px;font-size:12px;line-height:18px;display:inline-flex}",
|
|
155
|
+
".sm-footerBtn:hover,.sm-footerBtn:focus-visible{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover)}",
|
|
156
|
+
".sm-footerBtn svg{flex:none}",
|
|
157
|
+
".sm-panel{display:flex;flex-direction:column;gap:10px;min-height:280px;max-height:min(70vh,640px);min-width:480px}",
|
|
158
|
+
".sm-filter{display:flex;gap:4px;flex-wrap:wrap}",
|
|
159
|
+
".sm-filterBtn{cursor:pointer;color:var(--dsw-alias-label-secondary);background:0 0;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;padding:3px 12px;font-size:12px;line-height:18px}",
|
|
160
|
+
".sm-filterBtn:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
161
|
+
".sm-filterBtn.sm-on{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-fill-l2);border-color:transparent}",
|
|
162
|
+
".sm-list{display:flex;flex-direction:column;gap:4px;min-height:0;overflow:auto;flex:1}",
|
|
163
|
+
".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}",
|
|
164
|
+
".sm-row:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
165
|
+
".sm-rowCurrent{border-color:var(--dsw-alias-accent-primary,var(--dsw-alias-label-secondary))}",
|
|
166
|
+
".sm-rowMain{min-width:0;display:flex;flex-direction:column;gap:2px;flex:1 1 280px}",
|
|
167
|
+
".sm-rowTitle{color:var(--dsw-alias-label-primary);font-size:13px;line-height:18px;display:flex;align-items:center;gap:6px;min-width:0}",
|
|
168
|
+
".sm-rowTitle>span:first-child{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}",
|
|
169
|
+
".sm-rowMeta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}",
|
|
170
|
+
".sm-rowActions{display:flex;gap:4px;flex:none;flex-wrap:wrap;justify-content:flex-end}",
|
|
171
|
+
".sm-badge{flex:none;border-radius:999px;padding:0 6px;font-size:10px;line-height:16px}",
|
|
172
|
+
".sm-badgeArchived{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-fill-l2)}",
|
|
173
|
+
".sm-badgeRunning{color:var(--dsw-alias-state-warning-primary,#d97706);background:rgba(217,119,6,.12)}",
|
|
174
|
+
".sm-badgeCurrent{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-fill-l2)}",
|
|
175
|
+
".sm-danger{color:var(--dsw-alias-state-error-primary) !important;border-color:var(--dsw-alias-state-error-primary) !important}",
|
|
176
|
+
".sm-dangerText{color:var(--dsw-alias-state-error-primary) !important}",
|
|
177
|
+
".sm-warn{display:flex;align-items:flex-start;gap:8px;color:var(--dsw-alias-label-secondary);background:rgba(217,119,6,.08);border:1px solid rgba(217,119,6,.25);border-radius:8px;padding:8px 10px;margin-top:4px}",
|
|
178
|
+
".sm-warn svg{flex:none;margin-top:1px}",
|
|
179
|
+
".sm-error{color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px;min-height:0;margin-top:4px}",
|
|
180
|
+
".sm-foot{display:flex;justify-content:flex-end;align-items:center;gap:8px;width:100%}",
|
|
181
|
+
".sm-footError{color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px;margin-right:auto}",
|
|
182
|
+
".sm-empty{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px;text-align:center;padding:24px 0}",
|
|
183
|
+
".sm-moveSection{display:flex;flex-direction:column;gap:8px}",
|
|
184
|
+
".sm-moveLabel{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}",
|
|
185
|
+
".sm-moveList{display:flex;flex-direction:column;gap:4px;max-height:200px;overflow:auto}",
|
|
186
|
+
".sm-moveItem{box-sizing:border-box;display:flex;align-items:center;justify-content:space-between;padding:8px 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;cursor:pointer;transition:all .15s}",
|
|
187
|
+
".sm-moveItem:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
188
|
+
".sm-moveItemSelected{border-color:var(--dsw-alias-accent-primary);background:var(--dsw-alias-accent-primary-bg)}",
|
|
189
|
+
".sm-moveItemName{color:var(--dsw-alias-label-primary);font-size:13px;line-height:18px}",
|
|
190
|
+
".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%;z-index:50;display:flex;flex-direction:column;min-width:160px;padding:4px;background:var(--dsw-alias-surface-l1);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;box-shadow:0 4px 16px rgba(0,0,0,.12)}.sm-headerMenuItem{display:flex;align-items:center;width:100%;text-align:left;background:0 0;border:0;border-radius:6px;color:var(--dsw-alias-label-primary);cursor:pointer;padding:6px 10px;font-size:13px;line-height:18px}.sm-headerMenuItem:hover{background:var(--dsw-alias-interactive-bg-hover)}.sm-headerMenuItemDanger{color:var(--dsw-alias-state-error-primary)}.sm-headerBtn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:32px;height:auto;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-fill-l2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;cursor:pointer;padding:4px 12px;font-size:13px;line-height:18px;white-space:nowrap;transition:background .15s,color .15s,border-color .15s}.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-accent-primary);outline-offset:1px}.sm-headerBtn svg{width:16px;height:16px}.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-accent-primary);color:#fff;border-color:var(--dsw-alias-accent-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-fill-l2);color:var(--dsw-alias-label-primary);font-size:13px;line-height:18px}.sm-migrateWarn{padding:10px 12px;border:1px solid rgba(217,119,6,.25);border-radius:8px;background:rgba(217,119,6,.08);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-headerBtn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l1)}.sm-headerBtnDanger{background:var(--dsw-alias-state-error-tertiary);color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary);font-weight:500}.sm-headerBtnDanger:hover:not(:disabled){background:#d92d20!important;color:#fff!important;border-color:#d92d20!important}.sm-header{display:inline-flex;align-items:center;gap:6px}.sm-headerBtnActive{background:var(--dsw-alias-fill-l3);color:var(--dsw-alias-label-primary)}",
|
|
191
|
+
".sm-panelDialog{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);width:560px;max-width:calc(100vw - 32px);max-height:540px;height:auto;box-sizing:border-box;display:flex;flex-direction:column;gap:0;background:var(--dsw-alias-surface-l1,#fff);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:9999;opacity:1}.sm-panelDialog .sm-nativeDialogHeader{padding:10px 16px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee);flex:none}.sm-panelDialog .sm-nativeDialogTitle{margin:0;font-size:14px;line-height:20px;font-weight:600}.sm-panelDialog .sm-nativeDialogFooter{padding:10px 16px;border-top:1px solid var(--dsw-alias-border-l2,#eee);flex:none;flex-wrap:wrap;justify-content:flex-end;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:0 10px;height:36px;min-height:36px;border:1px solid transparent;border-radius:8px;background:transparent}.sm-panelDialog .sm-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.sm-panelDialog .sm-rowCurrent{background:var(--dsw-alias-fill-l2);border-color:var(--dsw-alias-accent-primary,#356ae6)}.sm-panelDialog .sm-rowMain{display:flex;align-items:stretch!important;gap:8px;min-width:0;flex:1 1 auto;overflow:hidden;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}.sm-confirmDialog{z-index:10000}.sm-confirmDialog.sm-nativeDialogLayer{position:fixed;left:calc(50vw + 184px);top:calc(50vh - 90px);width:300px;max-width: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: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;box-sizing:border-box;background:var(--dsw-alias-surface-l1,#fff);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{padding:12px 16px;border-bottom:1px solid var(--dsw-alias-border-l2,#eee)}.sm-confirmDialog .sm-nativeDialogTitle{margin:0;font-size:14px;line-height:20px;font-weight:600}.sm-confirmDialog .sm-nativeDialogBody{padding:10px 16px;display:flex;flex-direction:column;gap:8px;min-height:0}.sm-confirmDialog .sm-nativeDialogDescription{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);white-space:pre-wrap}.sm-confirmDialog .sm-warn{padding:6px 10px;font-size:12px;line-height:16px;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:flex-end;gap:8px;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)}.sm-confirmDialog .sm-nativeDialogCancel:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.sm-confirmDialog .sm-nativeDialogConfirm{background:var(--dsw-alias-state-error-primary);color:#fff;border-color:var(--dsw-alias-state-error-primary)}.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;height:22px;padding:0 8px;font-size:11px;line-height:14px;border-radius:6px;border:1px solid transparent;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer}.sm-panelDialog .sm-rowActions .sm-rowBtn:hover{background:var(--dsw-alias-fill-l2);color:var(--dsw-alias-label-primary)}.sm-panelDialog .sm-rowActions .sm-rowBtnDanger{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-fill-l2);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 12px;font-size:12px;line-height:18px;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;background:transparent;color:var(--dsw-alias-label-primary);cursor:pointer}.sm-panelDialog .sm-nativeDialogCancel{background:transparent}.sm-panelDialog .sm-nativeDialogGhost{background:transparent;border-color:var(--dsw-alias-border-l2)}.sm-panelDialog .sm-nativeDialogConfirm{background:var(--dsw-alias-accent-primary,#356ae6);color:#fff;border-color:var(--dsw-alias-accent-primary,#356ae6)}.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:flex-end;gap:8px;margin-top:14px}.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:auto;max-height:min(70vh,520px);box-sizing:border-box;background-color:#ffffff;background-color:var(--dsw-alias-surface-l1,#fff);color:var(--dsw-alias-label-primary,#111);border:1px solid #d4d4d8;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;opacity:1}.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:#ffffff;background-color:var(--dsw-alias-surface-l1,#fff);color:var(--dsw-alias-label-primary,#111);border:1px solid #d4d4d8;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:12px;box-shadow:0 16px 48px rgba(0,0,0,.45);overflow:hidden;opacity:1;z-index:10001;backdrop-filter:none;-webkit-backdrop-filter:none}.sm-migrateDialog .sm-nativeDialogHeader{padding:12px 18px;border-bottom:1px solid #eee;border-bottom:1px solid var(--dsw-alias-border-l2,#eee);flex:none;background-color:#fff;background-color:var(--dsw-alias-surface-l1,#fff)}.sm-migrateDialog .sm-nativeDialogTitle{margin:0;font-size:15px;line-height:22px;font-weight:600;color:#111;color:var(--dsw-alias-label-primary,#111)}.sm-migrateDialog .sm-migratePanel{padding:12px 18px;display:flex;flex-direction:column;gap:14px;background-color:#fff;background-color:var(--dsw-alias-surface-l1,#fff)}.sm-migrateDialog .sm-nativeDialogBody{padding:0;background-color:#fff;background-color:var(--dsw-alias-surface-l1,#fff)}.sm-migrateDialog .sm-nativeDialogFooter{padding:12px 18px;border-top:1px solid #eee;border-top:1px solid var(--dsw-alias-border-l2,#eee);flex:none;flex-wrap:wrap;justify-content:flex-end;gap:8px;background-color:#fff;background-color:var(--dsw-alias-surface-l1,#fff)}.sm-migrateDialog .sm-migrateRow>label{color:#444;color:var(--dsw-alias-label-secondary,#444)}.sm-migrateDialog .sm-migrateChip{padding:6px 12px;border-radius:999px;border:1px solid #d4d4d8;border:1px solid var(--dsw-alias-border-l2,#ddd);background-color:#f5f5f5;background-color:var(--dsw-alias-fill-l2,#f5f5f5);color:#111;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:#e8e8e8;background-color:var(--dsw-alias-interactive-bg-hover,#e8e8e8)}.sm-migrateDialog .sm-migrateChipOn{background-color:#356ae6;background-color:var(--dsw-alias-accent-primary,#356ae6);color:#fff;border-color:#356ae6;border-color:var(--dsw-alias-accent-primary,#356ae6)}.sm-migrateDialog .sm-migrateChip.sm-migrateChipOn:hover:not(:disabled){background-color:#4576f0;background-color:var(--dsw-alias-accent-primary-hover,#4576f0);color:#fff;border-color:#4576f0;border-color:var(--dsw-alias-accent-primary-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:#888;color:var(--dsw-alias-label-tertiary,#888)}.sm-migrateDialog .sm-migratePreview{padding:10px 12px;border:1px solid #d4d4d8;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;background-color:#f5f5f5;background-color:var(--dsw-alias-fill-l2,#f5f5f5);color:#111;color:var(--dsw-alias-label-primary,#111);font-size:13px;line-height:18px}.sm-migrateDialog .sm-migrateWarn{padding:10px 12px;border:1px solid rgba(217,119,6,.25);border-radius:8px;background-color:rgba(217,119,6,.08);color:#444;color:var(--dsw-alias-label-secondary,#444);font-size:13px;line-height:18px}.sm-migrateDialog .sm-nativeDialogButton{min-height:32px;padding:4px 14px;font-size:13px;line-height:20px;border:1px solid #d4d4d8;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:8px;cursor:pointer;background-color:#fff;background-color:var(--dsw-alias-fill-l2,#fff);color:#111;color:var(--dsw-alias-label-primary,#111)}.sm-migrateDialog .sm-nativeDialogButton:hover:not(:disabled){background-color:#e8e8e8;background-color:var(--dsw-alias-interactive-bg-hover,#e8e8e8)}.sm-migrateDialog .sm-nativeDialogConfirm{background-color:#356ae6;background-color:var(--dsw-alias-accent-primary,#356ae6);color:#fff;border-color:#356ae6;border-color:var(--dsw-alias-accent-primary,#356ae6)}.sm-migrateDialog .sm-nativeDialogConfirm:hover:not(:disabled){filter:brightness(.94)}",
|
|
192
|
+
".sm-panelDialog .sm-nativeDialogHeader{display:flex;align-items:center;gap:8px}.sm-panelHeaderClose{min-height:26px;padding:2px 9px;border:1px solid var(--dsw-alias-border-l2,#ddd);border-radius:7px;background:transparent;color:var(--dsw-alias-label-secondary,#555);cursor:pointer;font-size:12px;line-height:18px}.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-fill-l2,#f5f5f5);font-size:13px;line-height:18px;color:var(--dsw-alias-label-primary,#111)}.sm-migrateConfirmLayer.sm-confirmDialog.sm-nativeDialogLayer{width:300px;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:none;max-height:min(70vh,520px);z-index:auto}.sm-migrateConfirmLayer .sm-migratePanel{min-width:0}.sm-migrateConfirmLayer .sm-nativeDialogButton.sm-nativeDialogConfirm:hover:not(:disabled){background-color:#4576f0;background-color:var(--dsw-alias-accent-primary-hover,#4576f0);color:#fff;border-color:#4576f0;border-color:var(--dsw-alias-accent-primary-hover,#4576f0);filter:none}",
|
|
193
|
+
// Theme colors are scoped to the plugin dialogs. DSH exposes its
|
|
194
|
+
// actual theme through `body[data-ds-dark-theme]` and color-scheme;
|
|
195
|
+
// keeping these values local avoids changing DSH's global variables.
|
|
196
|
+
"[data-sm-theme=light] .sm-panelDialog,[data-sm-theme=light] .sm-confirmDialog.sm-nativeDialogLayer .sm-nativeDialog,[data-sm-theme=light] .sm-migrateDialog.sm-nativeDialogLayer .sm-nativeDialog{--dsw-alias-surface-l1:#fff;--dsw-alias-label-primary:#1f1f1f;--dsw-alias-label-secondary:#555;--dsw-alias-label-tertiary:#888;--dsw-alias-border-l1:#d4d4d8;--dsw-alias-border-l2:#e5e5e5;--dsw-alias-fill-l2:#f5f5f5;--dsw-alias-interactive-bg-hover:#eee;--dsw-alias-state-error-primary:#d92d20;--dsw-alias-state-warning-primary:#d97706;--dsw-alias-accent-primary:#356ae6;--dsw-alias-accent-primary-bg:rgba(53,106,230,.12);background:#fff!important;background-color:#fff!important;color:#1f1f1f!important;border-color:#e5e5e5!important}",
|
|
197
|
+
"[data-sm-theme=dark] .sm-panelDialog,[data-sm-theme=dark] .sm-confirmDialog.sm-nativeDialogLayer .sm-nativeDialog,[data-sm-theme=dark] .sm-migrateDialog.sm-nativeDialogLayer .sm-nativeDialog{--dsw-alias-surface-l1:#1f1f23;--dsw-alias-label-primary:#ececec;--dsw-alias-label-secondary:#b3b3b3;--dsw-alias-label-tertiary:#999;--dsw-alias-border-l1:#555;--dsw-alias-border-l2:#3a3a40;--dsw-alias-fill-l2:#2a2a30;--dsw-alias-interactive-bg-hover:rgba(255,255,255,.06);--dsw-alias-state-error-primary:#f47171;--dsw-alias-state-warning-primary:#f0a040;--dsw-alias-accent-primary:#5d8aff;--dsw-alias-accent-primary-bg:rgba(93,138,255,.18);background:#1f1f23!important;background-color:#1f1f23!important;color:#ececec!important;border-color:#3a3a40!important}",
|
|
198
|
+
"[data-sm-theme=light] .sm-panelDialog .sm-nativeDialogHeader,[data-sm-theme=light] .sm-panelDialog .sm-nativeDialogFooter,[data-sm-theme=light] .sm-confirmDialog .sm-nativeDialogHeader,[data-sm-theme=light] .sm-confirmDialog .sm-nativeDialogFooter,[data-sm-theme=light] .sm-migrateDialog .sm-nativeDialogHeader,[data-sm-theme=light] .sm-migrateDialog .sm-nativeDialogBody,[data-sm-theme=light] .sm-migrateDialog .sm-nativeDialogFooter{background:#fff!important;background-color:#fff!important;border-color:#e5e5e5!important}",
|
|
199
|
+
"[data-sm-theme=dark] .sm-panelDialog .sm-nativeDialogHeader,[data-sm-theme=dark] .sm-panelDialog .sm-nativeDialogFooter,[data-sm-theme=dark] .sm-confirmDialog .sm-nativeDialogHeader,[data-sm-theme=dark] .sm-confirmDialog .sm-nativeDialogFooter,[data-sm-theme=dark] .sm-migrateDialog .sm-nativeDialogHeader,[data-sm-theme=dark] .sm-migrateDialog .sm-nativeDialogBody,[data-sm-theme=dark] .sm-migrateDialog .sm-nativeDialogFooter{background:#1f1f23!important;background-color:#1f1f23!important;border-color:#3a3a40!important}",
|
|
200
|
+
].join("");
|
|
201
|
+
|
|
202
|
+
// Auto-follow DSH's theme. We detect DSH's current theme from
|
|
203
|
+
// body[data-ds-dark-theme] / color-scheme / data-theme / class tokens,
|
|
204
|
+
// mirror it onto <html data-sm-theme>, and apply inline dialog colors.
|
|
205
|
+
// The dialogs use that attribute via the scoped CSS rules above, and
|
|
206
|
+
// inline styles guarantee an opaque background even when DSH's tokens
|
|
207
|
+
// resolve to `unset` / `transparent`.
|
|
208
|
+
const SM_THEME_ATTR = "data-sm-theme";
|
|
209
|
+
function smReadDshTheme() {
|
|
210
|
+
try {
|
|
211
|
+
const html = document.documentElement;
|
|
212
|
+
const body = document.body;
|
|
213
|
+
if (body && body.hasAttribute("data-ds-dark-theme")) return "dark";
|
|
214
|
+
const elements = [html, body].filter(Boolean);
|
|
215
|
+
const dark = /\b(dark|dim)\b/i;
|
|
216
|
+
const light = /\b(light|bright)\b/i;
|
|
217
|
+
for (const el of elements) {
|
|
218
|
+
const value = ["theme", "data-theme", "data-bs-theme", "color-scheme", "data-color-mode", "data-mode"]
|
|
219
|
+
.map((name) => String(el.getAttribute(name) || ""))
|
|
220
|
+
.join(" ");
|
|
221
|
+
if (dark.test(value)) return "dark";
|
|
222
|
+
if (light.test(value)) return "light";
|
|
223
|
+
}
|
|
224
|
+
for (const el of elements) {
|
|
225
|
+
const scheme = String(getComputedStyle(el).colorScheme || "").toLowerCase();
|
|
226
|
+
if (scheme.includes("dark")) return "dark";
|
|
227
|
+
if (scheme.includes("light")) return "light";
|
|
228
|
+
}
|
|
229
|
+
const bg = body ? getComputedStyle(body).backgroundColor : "";
|
|
230
|
+
const match = bg.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i);
|
|
231
|
+
if (match) {
|
|
232
|
+
const luminance = (Number(match[1]) * 299 + Number(match[2]) * 587 + Number(match[3]) * 114) / 1000;
|
|
233
|
+
return luminance < 128 ? "dark" : "light";
|
|
234
|
+
}
|
|
235
|
+
} catch (_) { /* ignore */ }
|
|
236
|
+
return "light";
|
|
237
|
+
}
|
|
238
|
+
function smApplyInlineTheme(theme) {
|
|
239
|
+
try {
|
|
240
|
+
const values = theme === "dark"
|
|
241
|
+
? { background: "#1f1f23", color: "#ececec", border: "#3a3a40" }
|
|
242
|
+
: { background: "#fff", color: "#1f1f1f", border: "#e5e5e5" };
|
|
243
|
+
const roots = document.querySelectorAll(
|
|
244
|
+
".sm-panelDialog," +
|
|
245
|
+
".sm-confirmDialog.sm-nativeDialogLayer .sm-nativeDialog," +
|
|
246
|
+
".sm-migrateDialog.sm-nativeDialogLayer .sm-nativeDialog"
|
|
247
|
+
);
|
|
248
|
+
for (const el of roots) {
|
|
249
|
+
el.style.setProperty("background", values.background, "important");
|
|
250
|
+
el.style.setProperty("background-color", values.background, "important");
|
|
251
|
+
el.style.setProperty("color", values.color, "important");
|
|
252
|
+
el.style.setProperty("border-color", values.border, "important");
|
|
253
|
+
}
|
|
254
|
+
} catch (_) { /* ignore */ }
|
|
255
|
+
}
|
|
256
|
+
function smApplyTheme() {
|
|
257
|
+
try {
|
|
258
|
+
const theme = smReadDshTheme();
|
|
259
|
+
document.documentElement.setAttribute(SM_THEME_ATTR, theme);
|
|
260
|
+
smApplyInlineTheme(theme);
|
|
261
|
+
} catch (_) { /* ignore */ }
|
|
262
|
+
}
|
|
263
|
+
smApplyTheme();
|
|
264
|
+
if (typeof MutationObserver === "function") {
|
|
265
|
+
try {
|
|
266
|
+
const observer = new MutationObserver(() => smApplyTheme());
|
|
267
|
+
const options = { attributes: true, attributeFilter: ["class", "theme", "data-theme", "data-bs-theme", "data-ds-dark-theme", "data-color-mode", "data-mode", "color-scheme", "style"] };
|
|
268
|
+
if (document.documentElement) observer.observe(document.documentElement, options);
|
|
269
|
+
if (document.body) observer.observe(document.body, options);
|
|
270
|
+
observer.observe(document.body || document.documentElement, { childList: true, subtree: true });
|
|
271
|
+
} catch (_) { /* ignore */ }
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const CSS_ID = "dsh-session-manager/session-manager";
|
|
275
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(CSS_ID) + "]") === null) {
|
|
276
|
+
const tag = document.createElement("style");
|
|
277
|
+
tag.dataset.plugin = "dsh-session-manager";
|
|
278
|
+
tag.dataset.pluginCss = CSS_ID;
|
|
279
|
+
tag.textContent = css;
|
|
280
|
+
document.head.appendChild(tag);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
// Anchor the session manager panel to the sidebar right edge so it
|
|
285
|
+
// always fits within the sidebar area, regardless of viewport size.
|
|
286
|
+
const SM_PANEL_CSS_ID = "dsh-session-manager/session-manager-anchor";
|
|
287
|
+
const installAnchor = () => {
|
|
288
|
+
if (typeof document === "undefined") return;
|
|
289
|
+
if (document.querySelector("style[data-plugin-css=" + JSON.stringify(SM_PANEL_CSS_ID) + "]") !== null) return;
|
|
290
|
+
const tag = document.createElement("style");
|
|
291
|
+
tag.dataset.plugin = "dsh-session-manager";
|
|
292
|
+
tag.dataset.pluginCss = SM_PANEL_CSS_ID;
|
|
293
|
+
tag.textContent = ":root{--sm-panel-left:8px}";
|
|
294
|
+
document.head.appendChild(tag);
|
|
295
|
+
const updateLeft = () => {
|
|
296
|
+
let right = 8;
|
|
297
|
+
const candidates = [
|
|
298
|
+
"[data-dsh-sidebar]",
|
|
299
|
+
"[data-testid=sidebar]",
|
|
300
|
+
"[aria-label=会话][role=navigation]",
|
|
301
|
+
"aside",
|
|
302
|
+
".dsw-sidebar"
|
|
303
|
+
];
|
|
304
|
+
for (const sel of candidates) {
|
|
305
|
+
const el = document.querySelector(sel);
|
|
306
|
+
if (el !== null) {
|
|
307
|
+
const rect = el.getBoundingClientRect();
|
|
308
|
+
if (rect.right > 100 && rect.right < window.innerWidth - 40) {
|
|
309
|
+
right = Math.round(rect.right) + 8;
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
document.documentElement.style.setProperty("--sm-panel-left", right + "px");
|
|
315
|
+
};
|
|
316
|
+
updateLeft();
|
|
317
|
+
window.addEventListener("resize", updateLeft);
|
|
318
|
+
};
|
|
319
|
+
try { installAnchor(); } catch (_) {}
|
|
320
|
+
|
|
321
|
+
const h = (type, props, ...children) => React.createElement(type, props, ...children);
|
|
322
|
+
const callApi = (path, payload) => fetch(API + path, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(payload || {}) }).then((r) => r.json());
|
|
323
|
+
const fetchApi = (path) => fetch(API + path).then((r) => r.json());
|
|
324
|
+
const formatRelative = (ts, now) => {
|
|
325
|
+
const diff = Math.max(0, now - ts);
|
|
326
|
+
const s = Math.floor(diff / 1000);
|
|
327
|
+
if (s < 60) return s + "s";
|
|
328
|
+
const m = Math.floor(s / 60);
|
|
329
|
+
if (m < 60) return m + "m";
|
|
330
|
+
const hr = Math.floor(m / 60);
|
|
331
|
+
if (hr < 24) return hr + "h";
|
|
332
|
+
const d = Math.floor(hr / 24);
|
|
333
|
+
if (d < 30) return d + "d";
|
|
334
|
+
const mo = Math.floor(d / 30);
|
|
335
|
+
if (mo < 12) return mo + "mo";
|
|
336
|
+
return Math.floor(mo / 12) + "y";
|
|
337
|
+
};
|
|
338
|
+
const shortPath = (cwd) => {
|
|
339
|
+
if (!cwd) return "";
|
|
340
|
+
const parts = cwd.replace(/[\\\/]+$/, "").split(/[\\\/]/);
|
|
341
|
+
const last = parts[parts.length - 1] || "";
|
|
342
|
+
return last.length > 26 ? last.slice(0, 24) + "\u2026" : last;
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
function ConfirmDialog(props) {
|
|
346
|
+
const open = props.open === true;
|
|
347
|
+
const onCancel = typeof props.onCancel === "function" ? props.onCancel : () => {};
|
|
348
|
+
const onConfirm = typeof props.onConfirm === "function" ? props.onConfirm : () => {};
|
|
349
|
+
const busy = props.busy === true;
|
|
350
|
+
const title = props.title || "";
|
|
351
|
+
const description = props.description || "";
|
|
352
|
+
const warning = props.warning || "";
|
|
353
|
+
const confirmLabel = props.confirmLabel || "确认";
|
|
354
|
+
const cancelLabel = props.cancelLabel || "取消";
|
|
355
|
+
const R = (typeof window !== "undefined") ? window.__smReact : null;
|
|
356
|
+
const createPortal = (R && typeof R.createPortal === "function") ? R.createPortal : null;
|
|
357
|
+
const body = (typeof document === "undefined") ? null : document.body;
|
|
358
|
+
if (!open) return null;
|
|
359
|
+
if (!body) return tree || null;
|
|
360
|
+
const tree = h("div", {
|
|
361
|
+
className: "sm-nativeDialogLayer sm-confirmDialog",
|
|
362
|
+
style: props.anchor && typeof props.anchor.top === "number"
|
|
363
|
+
? { position: "fixed", top: props.anchor.top + "px", left: props.anchor.left + "px", zIndex: 10001 }
|
|
364
|
+
: null,
|
|
365
|
+
role: "presentation",
|
|
366
|
+
onKeyDown: (e) => {
|
|
367
|
+
if (e.key === "Escape" && !busy) { e.preventDefault(); onCancel(); }
|
|
368
|
+
else if (e.key === "Enter" && !busy) { e.preventDefault(); onConfirm(); }
|
|
369
|
+
},
|
|
370
|
+
tabIndex: -1
|
|
371
|
+
},
|
|
372
|
+
h("div", {
|
|
373
|
+
className: "sm-nativeDialogBackdrop",
|
|
374
|
+
onMouseDown: (e) => { if (e.target === e.currentTarget && !busy) onCancel(); }
|
|
375
|
+
}),
|
|
376
|
+
h("section", {
|
|
377
|
+
className: "sm-nativeDialog",
|
|
378
|
+
role: "dialog",
|
|
379
|
+
"aria-modal": "true",
|
|
380
|
+
"aria-labelledby": "sm-confirm-dialog-title",
|
|
381
|
+
ref: (el) => { if (el && typeof el.focus === "function") el.focus(); }
|
|
382
|
+
},
|
|
383
|
+
h("div", { className: "sm-nativeDialogHeader" },
|
|
384
|
+
h("h2", { id: "sm-confirm-dialog-title", className: "sm-nativeDialogTitle" }, title)
|
|
385
|
+
),
|
|
386
|
+
h("div", { className: "sm-nativeDialogBody" },
|
|
387
|
+
description ? h("div", { className: "sm-nativeDialogDescription" }, description) : null,
|
|
388
|
+
warning ? h("div", { className: "sm-warn" }, h("span", null, warning)) : null
|
|
389
|
+
),
|
|
390
|
+
h("div", { className: "sm-nativeDialogFooter" },
|
|
391
|
+
h("button", {
|
|
392
|
+
type: "button",
|
|
393
|
+
className: "sm-nativeDialogButton sm-nativeDialogCancel",
|
|
394
|
+
disabled: busy,
|
|
395
|
+
onClick: () => { if (!busy) onCancel(); }
|
|
396
|
+
}, cancelLabel),
|
|
397
|
+
h("button", {
|
|
398
|
+
type: "button",
|
|
399
|
+
className: "sm-nativeDialogButton sm-nativeDialogConfirm",
|
|
400
|
+
disabled: busy,
|
|
401
|
+
onClick: () => { if (!busy) onConfirm(); }
|
|
402
|
+
}, confirmLabel)
|
|
403
|
+
)
|
|
404
|
+
)
|
|
405
|
+
);
|
|
406
|
+
if (createPortal) return createPortal(tree, body);
|
|
407
|
+
// Default: render inline. position:fixed escapes the sidebar
|
|
408
|
+
// overflow:hidden ancestor at the DOM/CSS level, so the dialog
|
|
409
|
+
// appears next to the panel. Keeping the dialog in the same
|
|
410
|
+
// React tree as the panel preserves the onClick wiring (cancel /
|
|
411
|
+
// confirm call setConfirmFor, which only works when the click
|
|
412
|
+
// handler lives in the same root).
|
|
413
|
+
return tree;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function MoveDialog(props) {
|
|
417
|
+
const t = typeof props.t === "function" ? props.t : (key) => key;
|
|
418
|
+
const open = props.open === true;
|
|
419
|
+
const onCancel = typeof props.onCancel === "function" ? props.onCancel : () => {};
|
|
420
|
+
const onConfirm = typeof props.onConfirm === "function" ? props.onConfirm : () => {};
|
|
421
|
+
const busy = props.busy === true;
|
|
422
|
+
const title = props.title || t("confirm.move.title");
|
|
423
|
+
const description = props.description || "";
|
|
424
|
+
const currentWorkspaceId = props.currentWorkspaceId || "";
|
|
425
|
+
const confirmLabel = props.confirmLabel || t("confirm.move.confirm");
|
|
426
|
+
const cancelLabel = props.cancelLabel || t("confirm.cancel");
|
|
427
|
+
const error = props.error || "";
|
|
428
|
+
const [selectedWorkspaceId, setSelectedWorkspaceId] = useState("");
|
|
429
|
+
if (!open) return null;
|
|
430
|
+
|
|
431
|
+
const list = (Array.isArray(props.workspaces) ? props.workspaces : [])
|
|
432
|
+
.filter((ws) => ws && typeof ws === "object" && typeof ws.id === "string" && ws.id !== currentWorkspaceId);
|
|
433
|
+
const body = h("div", { className: "sm-nativeDialogBody" },
|
|
434
|
+
h("div", { className: "sm-nativeDialogDescription" }, description),
|
|
435
|
+
list.length === 0
|
|
436
|
+
? h("div", { className: "sm-moveLabel" }, t("confirm.move.empty"))
|
|
437
|
+
: h("div", { className: "sm-moveSection" },
|
|
438
|
+
h("div", { className: "sm-moveLabel" }, t("confirm.move.select")),
|
|
439
|
+
h("div", { className: "sm-moveList", role: "listbox", "aria-label": t("confirm.move.select") },
|
|
440
|
+
list.map((ws) => h("button", {
|
|
441
|
+
key: ws.id,
|
|
442
|
+
type: "button",
|
|
443
|
+
role: "option",
|
|
444
|
+
"aria-selected": selectedWorkspaceId === ws.id,
|
|
445
|
+
className: "sm-moveItem" + (selectedWorkspaceId === ws.id ? " sm-moveItemSelected" : ""),
|
|
446
|
+
disabled: busy,
|
|
447
|
+
onClick: () => setSelectedWorkspaceId(ws.id)
|
|
448
|
+
},
|
|
449
|
+
h("span", { className: "sm-moveItemName" }, String(ws.title || ws.name || ws.id)),
|
|
450
|
+
h("span", { className: "sm-moveItemMeta" }, ws.path ? String(ws.path) : "")
|
|
451
|
+
))
|
|
452
|
+
)
|
|
453
|
+
),
|
|
454
|
+
error ? h("div", { className: "sm-error", role: "alert" }, String(error)) : null
|
|
455
|
+
);
|
|
456
|
+
// Single cancel button (footer) + ESC handler. No "x" close button so the
|
|
457
|
+
// user only sees one cancel entry point.
|
|
458
|
+
const onKeyDown = (e) => {
|
|
459
|
+
if (e.key === "Escape" && !busy) {
|
|
460
|
+
e.preventDefault();
|
|
461
|
+
onCancel();
|
|
462
|
+
} else if (e.key === "Enter" && !busy && selectedWorkspaceId && list.length > 0) {
|
|
463
|
+
e.preventDefault();
|
|
464
|
+
onConfirm(selectedWorkspaceId);
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
return h("div", {
|
|
468
|
+
className: "sm-nativeDialogLayer sm-confirmDialog",
|
|
469
|
+
style: props.anchor && typeof props.anchor.top === "number"
|
|
470
|
+
? { position: "fixed", top: props.anchor.top + "px", left: props.anchor.left + "px", zIndex: 10001 }
|
|
471
|
+
: null,
|
|
472
|
+
role: "presentation",
|
|
473
|
+
onKeyDown,
|
|
474
|
+
tabIndex: -1
|
|
475
|
+
},
|
|
476
|
+
h("div", {
|
|
477
|
+
className: "sm-nativeDialogBackdrop",
|
|
478
|
+
onMouseDown: (e) => { if (e.target === e.currentTarget && !busy) onCancel(); }
|
|
479
|
+
}),
|
|
480
|
+
h("section", {
|
|
481
|
+
className: "sm-nativeDialog",
|
|
482
|
+
role: "dialog",
|
|
483
|
+
"aria-modal": "true",
|
|
484
|
+
"aria-labelledby": "sm-move-dialog-title",
|
|
485
|
+
ref: (el) => { if (el && typeof el.focus === "function") el.focus(); }
|
|
486
|
+
},
|
|
487
|
+
h("div", { className: "sm-nativeDialogHeader" },
|
|
488
|
+
h("h2", { id: "sm-move-dialog-title", className: "sm-nativeDialogTitle" }, title)
|
|
489
|
+
),
|
|
490
|
+
body,
|
|
491
|
+
h("div", { className: "sm-nativeDialogFooter" },
|
|
492
|
+
h("button", {
|
|
493
|
+
type: "button",
|
|
494
|
+
className: "sm-nativeDialogButton sm-nativeDialogCancel",
|
|
495
|
+
disabled: busy,
|
|
496
|
+
onClick: onCancel
|
|
497
|
+
}, cancelLabel),
|
|
498
|
+
h("button", {
|
|
499
|
+
type: "button",
|
|
500
|
+
className: "sm-nativeDialogButton sm-nativeDialogConfirm",
|
|
501
|
+
disabled: busy || !selectedWorkspaceId || list.length === 0,
|
|
502
|
+
onClick: () => { if (!busy && selectedWorkspaceId) onConfirm(selectedWorkspaceId); }
|
|
503
|
+
}, confirmLabel)
|
|
504
|
+
)
|
|
505
|
+
)
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function MigratePresetDialog(props) {
|
|
510
|
+
const t = typeof props.t === "function" ? props.t : (key) => key;
|
|
511
|
+
const open = props.open === true;
|
|
512
|
+
const onCancel = typeof props.onCancel === "function" ? props.onCancel : () => {};
|
|
513
|
+
const onRun = typeof props.onRun === "function" ? props.onRun : () => {};
|
|
514
|
+
const busy = props.busy === true;
|
|
515
|
+
const sessionId = props.sessionId || "";
|
|
516
|
+
const fallbackPreset = props.currentPreset || "";
|
|
517
|
+
const [scan, setScan] = useState(null);
|
|
518
|
+
const [target, setTarget] = useState("");
|
|
519
|
+
useEffect(() => {
|
|
520
|
+
if (!open || !sessionId) return;
|
|
521
|
+
let alive = true;
|
|
522
|
+
setScan(null);
|
|
523
|
+
setTarget("");
|
|
524
|
+
fetchApi("/preset-scan?sessionId=" + encodeURIComponent(sessionId)).then((d) => {
|
|
525
|
+
if (!alive) return;
|
|
526
|
+
if (d && d.ok) {
|
|
527
|
+
const result = d.result || {};
|
|
528
|
+
const rows = Array.isArray(result.rows)
|
|
529
|
+
? result.rows
|
|
530
|
+
: (result.rows && Array.isArray(result.rows.rows) ? result.rows.rows : []);
|
|
531
|
+
const availablePresets = Array.isArray(result.availablePresets)
|
|
532
|
+
? result.availablePresets
|
|
533
|
+
: (result.rows && Array.isArray(result.rows.availablePresets) ? result.rows.availablePresets : []);
|
|
534
|
+
const row = rows.find((item) => item && item.sessionId === sessionId) || null;
|
|
535
|
+
setScan({ ok: true, row, availablePresets });
|
|
536
|
+
} else {
|
|
537
|
+
setScan({ ok: false, error: (d && d.error) || "scan failed" });
|
|
538
|
+
}
|
|
539
|
+
}).catch((error) => {
|
|
540
|
+
if (alive) setScan({ ok: false, error: error instanceof Error ? error.message : String(error) });
|
|
541
|
+
});
|
|
542
|
+
return () => { alive = false; };
|
|
543
|
+
}, [open, sessionId]);
|
|
544
|
+
if (!open) return null;
|
|
545
|
+
|
|
546
|
+
const currentPreset = (scan && scan.ok && scan.row && (scan.row.finalPreset || scan.row.headerPreset)) || fallbackPreset || "—";
|
|
547
|
+
const presets = scan && scan.ok
|
|
548
|
+
? Array.from(new Set((scan.availablePresets || []).filter((preset) => typeof preset === "string" && preset.length > 0))).sort()
|
|
549
|
+
: [];
|
|
550
|
+
const targets = presets.filter((preset) => preset !== currentPreset);
|
|
551
|
+
const ready = target !== "" && target !== currentPreset;
|
|
552
|
+
const body = h("div", { className: "sm-migratePanel" },
|
|
553
|
+
scan === null
|
|
554
|
+
? h("div", { className: "sm-empty" }, t("busy.processing"))
|
|
555
|
+
: !scan.ok
|
|
556
|
+
? h("div", { className: "sm-error", role: "alert" }, scan.error || "scan failed")
|
|
557
|
+
: h(React.Fragment, null,
|
|
558
|
+
h("div", { className: "sm-migrateRow" },
|
|
559
|
+
h("label", null, t("migrate.current")),
|
|
560
|
+
h("div", { className: "sm-migrateCurrent" }, currentPreset)
|
|
561
|
+
),
|
|
562
|
+
h("div", { className: "sm-migrateRow" },
|
|
563
|
+
h("label", null, t("migrate.target")),
|
|
564
|
+
targets.length === 0
|
|
565
|
+
? h("div", { className: "sm-migrateEmpty" }, t("migrate.noPresets"))
|
|
566
|
+
: h("div", { className: "sm-migrateChips" }, targets.map((preset) => h("button", {
|
|
567
|
+
key: preset,
|
|
568
|
+
type: "button",
|
|
569
|
+
className: "sm-migrateChip" + (target === preset ? " sm-migrateChipOn" : ""),
|
|
570
|
+
disabled: busy,
|
|
571
|
+
onClick: () => setTarget(preset)
|
|
572
|
+
}, preset)))
|
|
573
|
+
),
|
|
574
|
+
target === currentPreset ? h("div", { className: "sm-migrateWarn" }, t("migrate.same")) : null
|
|
575
|
+
)
|
|
576
|
+
);
|
|
577
|
+
return h("div", {
|
|
578
|
+
className: "sm-nativeDialogLayer sm-confirmDialog sm-migrateConfirmLayer",
|
|
579
|
+
role: "presentation",
|
|
580
|
+
onKeyDown: (event) => { if (event.key === "Escape" && !busy) { event.preventDefault(); onCancel(); } }
|
|
581
|
+
},
|
|
582
|
+
h("div", {
|
|
583
|
+
className: "sm-nativeDialogBackdrop",
|
|
584
|
+
onMouseDown: (event) => { if (event.target === event.currentTarget && !busy) onCancel(); }
|
|
585
|
+
}),
|
|
586
|
+
h("section", {
|
|
587
|
+
className: "sm-nativeDialog sm-migrateDialog",
|
|
588
|
+
role: "dialog",
|
|
589
|
+
"aria-modal": "true",
|
|
590
|
+
"aria-labelledby": "sm-migrate-dialog-title"
|
|
591
|
+
},
|
|
592
|
+
h("div", { className: "sm-nativeDialogHeader" },
|
|
593
|
+
h("h2", { id: "sm-migrate-dialog-title", className: "sm-nativeDialogTitle" }, t("migrate.title"))
|
|
594
|
+
),
|
|
595
|
+
body,
|
|
596
|
+
h("div", { className: "sm-nativeDialogFooter" },
|
|
597
|
+
h("button", {
|
|
598
|
+
type: "button",
|
|
599
|
+
className: "sm-nativeDialogButton sm-nativeDialogCancel",
|
|
600
|
+
disabled: busy,
|
|
601
|
+
onClick: onCancel
|
|
602
|
+
}, t("confirm.cancel")),
|
|
603
|
+
h("button", {
|
|
604
|
+
type: "button",
|
|
605
|
+
className: "sm-nativeDialogButton sm-nativeDialogConfirm",
|
|
606
|
+
disabled: busy || !ready,
|
|
607
|
+
onClick: () => { if (ready) onRun(target); }
|
|
608
|
+
}, busy ? t("busy.processing") : t("migrate.confirm"))
|
|
609
|
+
)
|
|
610
|
+
)
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const panelStore = {
|
|
615
|
+
open: false,
|
|
616
|
+
listeners: new Set(),
|
|
617
|
+
getSnapshot() { return this.open; },
|
|
618
|
+
subscribe(cb) { this.listeners.add(cb); return () => this.listeners.delete(cb); },
|
|
619
|
+
set(v) { if (this.open === v) return; this.open = v; for (const fn of [...this.listeners]) fn(); },
|
|
620
|
+
// Pending action queued by the title-bar buttons (e.g. 移动至工作区).
|
|
621
|
+
// The SessionManagerPanel consumes this on mount so it can pre-open
|
|
622
|
+
// the matching inner dialog (move / delete / migrate) next to itself.
|
|
623
|
+
pendingMove: null,
|
|
624
|
+
pendingListeners: new Set(),
|
|
625
|
+
_emitPending() { for (const fn of [...this.pendingListeners]) fn(); },
|
|
626
|
+
subscribePending(cb) { this.pendingListeners.add(cb); return () => this.pendingListeners.delete(cb); },
|
|
627
|
+
setPendingMove(v) { this.pendingMove = v; this._emitPending(); },
|
|
628
|
+
consumePendingMove() { const v = this.pendingMove; this.pendingMove = null; return v; }
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
function SessionManagerPanel(props) {
|
|
632
|
+
const t = props.t;
|
|
633
|
+
const open = props.open;
|
|
634
|
+
const onClose = props.onClose;
|
|
635
|
+
const useSessions = props.useSessions;
|
|
636
|
+
const useWorkspaces = props.useWorkspaces;
|
|
637
|
+
const onOpen = props.onOpen;
|
|
638
|
+
const onArchive = props.onArchive;
|
|
639
|
+
const onUnarchive = props.onUnarchive;
|
|
640
|
+
const onRemove = props.onRemove;
|
|
641
|
+
const onMove = props.onMove;
|
|
642
|
+
const onMigrate = props.onMigrate;
|
|
643
|
+
const list = useSessions((s) => s);
|
|
644
|
+
const archivedIds = useWorkspaces((s) => s.archivedSessionIds);
|
|
645
|
+
const [filter, setFilter] = useState("all");
|
|
646
|
+
const [busyId, setBusyId] = useState(null);
|
|
647
|
+
const [error, setError] = useState(null);
|
|
648
|
+
const [now, setNow] = useState(() => Date.now());
|
|
649
|
+
const [moveFor, setMoveFor] = useState(null);
|
|
650
|
+
const [confirmFor, setConfirmFor] = useState(null);
|
|
651
|
+
const [workspaces, setWorkspaces] = useState([]);
|
|
652
|
+
const [migrating, setMigrating] = useState(false);
|
|
653
|
+
const [migrateFor, setMigrateFor] = useState(null);
|
|
654
|
+
useEffect(() => {
|
|
655
|
+
const timer = setInterval(() => setNow(Date.now()), 30000);
|
|
656
|
+
return () => clearInterval(timer);
|
|
657
|
+
}, []);
|
|
658
|
+
useEffect(() => {
|
|
659
|
+
if (!open) return;
|
|
660
|
+
let alive = true;
|
|
661
|
+
fetchApi("/workspaces").then((data) => {
|
|
662
|
+
if (alive && data.ok) setWorkspaces(data.result.workspaces || []);
|
|
663
|
+
});
|
|
664
|
+
return () => { alive = false; };
|
|
665
|
+
}, [open]);
|
|
666
|
+
// Drain any pending action queued by a header button. The panel is
|
|
667
|
+
// the single owner of every inner dialog, so opening it with a
|
|
668
|
+
// pending action pre-populates the corresponding dialog state.
|
|
669
|
+
useEffect(() => {
|
|
670
|
+
if (!open) return;
|
|
671
|
+
const pending = panelStore.consumePendingMove();
|
|
672
|
+
if (pending && pending.sessionId) {
|
|
673
|
+
setMoveFor({ id: pending.sessionId, displayTitle: pending.displayTitle || pending.sessionId, workspaceId: pending.workspaceId || "" });
|
|
674
|
+
}
|
|
675
|
+
}, [open]);
|
|
676
|
+
useEffect(() => {
|
|
677
|
+
if (moveFor) {
|
|
678
|
+
fetchApi("/workspaces").then((data) => {
|
|
679
|
+
if (data.ok) setWorkspaces(data.result.workspaces || []);
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
}, [moveFor]);
|
|
683
|
+
const archived = useMemo(() => new Set(archivedIds), [archivedIds]);
|
|
684
|
+
const rows = useMemo(() => {
|
|
685
|
+
const all = list.ids
|
|
686
|
+
.map((id) => list.byId[id])
|
|
687
|
+
.filter((s) => s !== undefined && s.origin !== "subagent" && (!s.blank || s.id === list.current));
|
|
688
|
+
if (filter === "active") return all.filter((s) => !archived.has(s.id));
|
|
689
|
+
if (filter === "archived") return all.filter((s) => archived.has(s.id));
|
|
690
|
+
return all;
|
|
691
|
+
}, [list, archived, filter]);
|
|
692
|
+
const runRow = async (sessionId, fn) => {
|
|
693
|
+
if (busyId !== null) return;
|
|
694
|
+
setBusyId(sessionId);
|
|
695
|
+
setError(null);
|
|
696
|
+
try {
|
|
697
|
+
await fn();
|
|
698
|
+
} catch (e) {
|
|
699
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
700
|
+
} finally {
|
|
701
|
+
setBusyId(null);
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
const filterBtn = (id, label) => h("button", {
|
|
705
|
+
type: "button",
|
|
706
|
+
key: id,
|
|
707
|
+
className: "sm-filterBtn" + (filter === id ? " sm-on" : ""),
|
|
708
|
+
onClick: () => setFilter(id)
|
|
709
|
+
}, label);
|
|
710
|
+
const renderRow = (s) => {
|
|
711
|
+
const isArchived = archived.has(s.id);
|
|
712
|
+
const isCurrent = s.id === list.current;
|
|
713
|
+
const busy = busyId === s.id;
|
|
714
|
+
return h("div", {
|
|
715
|
+
key: s.id,
|
|
716
|
+
className: "sm-row" + (isCurrent ? " sm-rowCurrent" : "")
|
|
717
|
+
},
|
|
718
|
+
h("div", { className: "sm-rowMain" },
|
|
719
|
+
h("div", { className: "sm-rowTitle" },
|
|
720
|
+
h("span", null, s.blank ? t("panel.session.new") : (s.displayTitle || s.id)),
|
|
721
|
+
isArchived ? h("span", { className: "sm-badge sm-badgeArchived" }, t("panel.archived")) : null,
|
|
722
|
+
s.running ? h("span", { className: "sm-badge sm-badgeRunning" }, t("panel.running")) : null,
|
|
723
|
+
isCurrent ? h("span", { className: "sm-badge sm-badgeCurrent" }, t("panel.current")) : null
|
|
724
|
+
),
|
|
725
|
+
h("div", { className: "sm-rowMeta" }, shortPath(s.cwd) + (s.cwd ? " · " : "") + formatRelative(s.updatedAt, now))
|
|
726
|
+
),
|
|
727
|
+
h("div", { className: "sm-rowActions" },
|
|
728
|
+
h("button", { type: "button", className: "sm-rowBtn", disabled: busy, onClick: () => onOpen(s.id) }, t("row.open")),
|
|
729
|
+
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")),
|
|
730
|
+
h("button", { type: "button", className: "sm-rowBtn", disabled: busy, onClick: () => setMoveFor(s) }, t("row.move")),
|
|
731
|
+
h("button", { type: "button", className: "sm-rowBtn", disabled: busy, onClick: () => setMigrateFor(s) }, t("row.migrate")),
|
|
732
|
+
h("button", { type: "button", className: "sm-rowBtn sm-rowBtnDanger", disabled: busy, onClick: () => setConfirmFor(s) }, t("row.delete"))
|
|
733
|
+
)
|
|
734
|
+
);
|
|
735
|
+
};
|
|
736
|
+
const rowsList = rows.length === 0
|
|
737
|
+
? h("div", { className: "sm-empty" }, filter === "archived" ? t("panel.empty.archived") : t("panel.empty"))
|
|
738
|
+
: h("div", { className: "sm-list" }, rows.map(renderRow));
|
|
739
|
+
const onPanelClose = () => {
|
|
740
|
+
if (busyId !== null) return;
|
|
741
|
+
setConfirmFor(null);
|
|
742
|
+
setMoveFor(null);
|
|
743
|
+
setMigrateFor(null);
|
|
744
|
+
onClose();
|
|
745
|
+
};
|
|
746
|
+
// Single cancel button in the panel footer (no native "x").
|
|
747
|
+
const onPanelKey = (e) => {
|
|
748
|
+
if (e.key === "Escape" && busyId === null) {
|
|
749
|
+
if (confirmFor || moveFor || migrateFor) return;
|
|
750
|
+
e.preventDefault();
|
|
751
|
+
onPanelClose();
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
const panelModal = h("div", {
|
|
755
|
+
className: "sm-nativeDialogLayer",
|
|
756
|
+
role: "presentation",
|
|
757
|
+
onKeyDown: onPanelKey,
|
|
758
|
+
tabIndex: -1
|
|
759
|
+
},
|
|
760
|
+
h("div", {
|
|
761
|
+
className: "sm-nativeDialogBackdrop",
|
|
762
|
+
onMouseDown: (e) => { if (e.target === e.currentTarget && busyId === null) onPanelClose(); }
|
|
763
|
+
}),
|
|
764
|
+
h("section", {
|
|
765
|
+
className: "sm-nativeDialog sm-panelDialog",
|
|
766
|
+
role: "dialog",
|
|
767
|
+
"aria-modal": "true",
|
|
768
|
+
"aria-labelledby": "sm-panel-dialog-title",
|
|
769
|
+
ref: (el) => { if (el && typeof el.focus === "function") el.focus(); }
|
|
770
|
+
},
|
|
771
|
+
h("div", { className: "sm-nativeDialogHeader" },
|
|
772
|
+
h("h2", { id: "sm-panel-dialog-title", className: "sm-nativeDialogTitle" }, t("panel.title")),
|
|
773
|
+
h("button", {
|
|
774
|
+
type: "button",
|
|
775
|
+
className: "sm-panelHeaderClose",
|
|
776
|
+
disabled: busyId !== null || migrating,
|
|
777
|
+
onClick: onPanelClose
|
|
778
|
+
}, t("panel.close"))
|
|
779
|
+
),
|
|
780
|
+
h("div", { className: "sm-panel" },
|
|
781
|
+
h("div", { className: "sm-filter" },
|
|
782
|
+
filterBtn("all", t("panel.filter.all")),
|
|
783
|
+
filterBtn("active", t("panel.filter.active")),
|
|
784
|
+
filterBtn("archived", t("panel.filter.archived"))
|
|
785
|
+
),
|
|
786
|
+
error ? h("div", { className: "sm-error", role: "alert" }, t("error.operation", { message: error })) : null,
|
|
787
|
+
rowsList
|
|
788
|
+
)
|
|
789
|
+
)
|
|
790
|
+
);
|
|
791
|
+
const deleteDialog = confirmFor === null ? null : h(ConfirmDialog, {
|
|
792
|
+
open: true,
|
|
793
|
+
onClose: () => { if (busyId === null) setConfirmFor(null); },
|
|
794
|
+
title: t("confirm.delete.title"),
|
|
795
|
+
description: t("confirm.delete.desc", { title: confirmFor.displayTitle || confirmFor.id }),
|
|
796
|
+
warning: confirmFor.running ? t("confirm.delete.running") : null,
|
|
797
|
+
confirmLabel: t("confirm.delete.confirm"),
|
|
798
|
+
cancelLabel: t("confirm.cancel"),
|
|
799
|
+
busy: busyId !== null,
|
|
800
|
+
onCancel: () => { if (busyId === null) setConfirmFor(null); },
|
|
801
|
+
onConfirm: () => {
|
|
802
|
+
const target = confirmFor;
|
|
803
|
+
setConfirmFor(null);
|
|
804
|
+
runRow(target.id, () => onRemove(target.id));
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
const moveDialog = moveFor === null ? null : h(MoveDialog, {
|
|
808
|
+
open: true,
|
|
809
|
+
onClose: () => { if (busyId === null) setMoveFor(null); },
|
|
810
|
+
title: t("confirm.move.title"),
|
|
811
|
+
description: t("confirm.move.desc", { title: moveFor.displayTitle || moveFor.id }),
|
|
812
|
+
sessionTitle: moveFor.displayTitle || moveFor.id,
|
|
813
|
+
currentWorkspaceId: moveFor.workspaceId,
|
|
814
|
+
workspaces: workspaces,
|
|
815
|
+
confirmLabel: t("confirm.move.confirm"),
|
|
816
|
+
cancelLabel: t("confirm.cancel"),
|
|
817
|
+
busy: busyId !== null,
|
|
818
|
+
onCancel: () => { if (busyId === null) setMoveFor(null); },
|
|
819
|
+
onConfirm: (targetWorkspaceId) => {
|
|
820
|
+
const target = moveFor;
|
|
821
|
+
setMoveFor(null);
|
|
822
|
+
runRow(target.id, () => onMove(target.id, targetWorkspaceId));
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
const migrateDialog = migrateFor === null ? null : h(MigratePresetDialog, {
|
|
826
|
+
open: true,
|
|
827
|
+
t: t,
|
|
828
|
+
sessionId: migrateFor.id,
|
|
829
|
+
currentPreset: migrateFor.agentPreset || "",
|
|
830
|
+
onCancel: () => { if (!migrating) setMigrateFor(null); },
|
|
831
|
+
busy: migrating,
|
|
832
|
+
onRun: async (targetPreset) => {
|
|
833
|
+
const target = migrateFor;
|
|
834
|
+
setMigrating(true);
|
|
835
|
+
setError(null);
|
|
836
|
+
try {
|
|
837
|
+
await onMigrate(target.id, targetPreset);
|
|
838
|
+
setMigrateFor(null);
|
|
839
|
+
try { window.alert(t("migrate.completed", { preset: targetPreset })); } catch (_) {}
|
|
840
|
+
} catch (error) {
|
|
841
|
+
setError(error instanceof Error ? error.message : String(error));
|
|
842
|
+
setMigrateFor(null);
|
|
843
|
+
} finally {
|
|
844
|
+
setMigrating(false);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
});
|
|
848
|
+
// Render the panel AND the inner dialog as siblings so React keeps
|
|
849
|
+
// both mounted. CSS z-index keeps the right ordering: the inner
|
|
850
|
+
// dialog (.sm-confirmDialog / .sm-moveDialog / .sm-migrateDialog)
|
|
851
|
+
// carries z-index 10000 and therefore sits ON TOP OF the panel
|
|
852
|
+
// (z-index 9999). Clicking inside the panel that the inner dialog
|
|
853
|
+
// blocks is not possible because the inner backdrop covers the
|
|
854
|
+
// panel area entirely (the inner modal is centered).
|
|
855
|
+
const primary = moveDialog || deleteDialog || migrateDialog;
|
|
856
|
+
return h(React.Fragment, null, panelModal, primary);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
function HeaderAction(props) {
|
|
860
|
+
const t = props.t;
|
|
861
|
+
const sessionId = props.sessionId;
|
|
862
|
+
// Slot framework spreads inject-factory return into top-level props,
|
|
863
|
+
// so the action callbacks live directly on props (not under .actions).
|
|
864
|
+
const onArchive = props.onArchive, onUnarchive = props.onUnarchive;
|
|
865
|
+
const onMove = props.onMove, onRemove = props.onRemove;
|
|
866
|
+
// Archive state lives in the workspace registry's archivedSessionIds
|
|
867
|
+
// (exposed through the global standard kit's useWorkspaces selector).
|
|
868
|
+
const useWorkspaces = props.useWorkspaces;
|
|
869
|
+
const archivedIds = (useWorkspaces ? useWorkspaces((s) => s.archivedSessionIds) : null) || [];
|
|
870
|
+
const isArchived = archivedIds.includes(sessionId);
|
|
871
|
+
|
|
872
|
+
const [confirmFor, setConfirmFor] = useState(null);
|
|
873
|
+
const [confirmAnchor, setConfirmAnchor] = useState(null);
|
|
874
|
+
const [moveFor, setMoveFor] = useState(null);
|
|
875
|
+
const [moveAnchor, setMoveAnchor] = useState(null);
|
|
876
|
+
const [workspaces, setWorkspaces] = useState([]);
|
|
877
|
+
const runWithAlert = (label, fn) => {
|
|
878
|
+
return Promise.resolve()
|
|
879
|
+
.then(() => fn())
|
|
880
|
+
.catch((e) => {
|
|
881
|
+
try {
|
|
882
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
883
|
+
window.alert(((t && t("error.operation")) || "操作失败:{message}").replace("{message}", msg));
|
|
884
|
+
} catch (_) {}
|
|
885
|
+
});
|
|
886
|
+
};
|
|
887
|
+
useEffect(() => {
|
|
888
|
+
if (!moveFor) { setWorkspaces([]); return; }
|
|
889
|
+
let alive = true;
|
|
890
|
+
fetchApi("/workspaces").then((d) => {
|
|
891
|
+
if (alive && d && d.ok) setWorkspaces(d.result.workspaces || []);
|
|
892
|
+
});
|
|
893
|
+
return () => { alive = false; };
|
|
894
|
+
}, [moveFor ? (moveFor.id || "x") : null]);
|
|
895
|
+
const anchorFor = (el) => {
|
|
896
|
+
if (!el || typeof el.getBoundingClientRect !== "function") return null;
|
|
897
|
+
const r = el.getBoundingClientRect();
|
|
898
|
+
return {
|
|
899
|
+
top: Math.round(r.top + r.height + 6),
|
|
900
|
+
left: Math.max(8, Math.round(r.right - 320))
|
|
901
|
+
};
|
|
902
|
+
};
|
|
903
|
+
const archiveLabelKey = isArchived ? "row.unarchive" : "row.archive";
|
|
904
|
+
return h(React.Fragment, null,
|
|
905
|
+
h("div", { className: "sm-header" },
|
|
906
|
+
h("button", {
|
|
907
|
+
type: "button",
|
|
908
|
+
className: "sm-headerBtn" + (isArchived ? " sm-headerBtnActive" : ""),
|
|
909
|
+
"aria-label": t ? t(archiveLabelKey) : (isArchived ? "移出归档" : "归档会话"),
|
|
910
|
+
onClick: () => runWithAlert("archive", () =>
|
|
911
|
+
isArchived ? onUnarchive(sessionId) : onArchive(sessionId)
|
|
912
|
+
)
|
|
913
|
+
}, isArchived ? (t ? t("row.unarchive") : "移出归档") : (t ? t("row.archive") : "归档")),
|
|
914
|
+
h("button", {
|
|
915
|
+
type: "button",
|
|
916
|
+
className: "sm-headerBtn" + (moveFor ? " sm-headerBtnActive" : ""),
|
|
917
|
+
"aria-label": t ? t("row.move") : "移动到工作区",
|
|
918
|
+
onClick: (e) => {
|
|
919
|
+
e.stopPropagation();
|
|
920
|
+
setConfirmFor(null);
|
|
921
|
+
setConfirmAnchor(null);
|
|
922
|
+
if (moveFor && moveFor.id === sessionId) {
|
|
923
|
+
setMoveFor(null);
|
|
924
|
+
setMoveAnchor(null);
|
|
925
|
+
} else {
|
|
926
|
+
setMoveAnchor(anchorFor(e.currentTarget));
|
|
927
|
+
setMoveFor({ id: sessionId, displayTitle: sessionId, workspaceId: "" });
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
}, t ? t("row.move") : "移动"),
|
|
931
|
+
h("button", {
|
|
932
|
+
type: "button",
|
|
933
|
+
className: "sm-headerBtn sm-headerBtnDanger" + (confirmFor ? " sm-headerBtnActive" : ""),
|
|
934
|
+
"aria-label": t ? t("row.delete") : "删除会话",
|
|
935
|
+
onClick: (e) => {
|
|
936
|
+
e.stopPropagation();
|
|
937
|
+
setMoveFor(null);
|
|
938
|
+
setMoveAnchor(null);
|
|
939
|
+
if (confirmFor && confirmFor.id === sessionId) {
|
|
940
|
+
setConfirmFor(null);
|
|
941
|
+
setConfirmAnchor(null);
|
|
942
|
+
} else {
|
|
943
|
+
setConfirmAnchor(anchorFor(e.currentTarget));
|
|
944
|
+
setConfirmFor({ id: sessionId, displayTitle: sessionId });
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
}, t ? t("row.delete") : "删除")
|
|
948
|
+
),
|
|
949
|
+
confirmFor === null ? null : h(ConfirmDialog, {
|
|
950
|
+
open: true,
|
|
951
|
+
anchor: confirmAnchor,
|
|
952
|
+
onClose: () => setConfirmFor(null),
|
|
953
|
+
onCancel: () => setConfirmFor(null),
|
|
954
|
+
onConfirm: () => {
|
|
955
|
+
const target = confirmFor;
|
|
956
|
+
setConfirmFor(null);
|
|
957
|
+
runWithAlert("delete", () => onRemove(target.id));
|
|
958
|
+
},
|
|
959
|
+
title: t ? t("confirm.delete.title") : "删除会话",
|
|
960
|
+
description: (confirmFor && (t ? t("confirm.delete.desc", { title: confirmFor.displayTitle || confirmFor.id }) : ("将删除 " + (confirmFor.displayTitle || confirmFor.id)))) || "",
|
|
961
|
+
confirmLabel: t ? t("confirm.delete.confirm") : "确认删除",
|
|
962
|
+
cancelLabel: t ? t("confirm.cancel") : "取消",
|
|
963
|
+
busy: false
|
|
964
|
+
}),
|
|
965
|
+
moveFor === null ? null : h(MoveDialog, {
|
|
966
|
+
open: true,
|
|
967
|
+
anchor: moveAnchor,
|
|
968
|
+
t: t,
|
|
969
|
+
workspaces: workspaces,
|
|
970
|
+
currentWorkspaceId: moveFor.workspaceId || "",
|
|
971
|
+
onClose: () => { setMoveFor(null); setMoveAnchor(null); },
|
|
972
|
+
onCancel: () => { setMoveFor(null); setMoveAnchor(null); },
|
|
973
|
+
title: t ? t("confirm.move.title") : "移动会话到工作区",
|
|
974
|
+
description: (moveFor && (t ? t("confirm.move.desc", { title: moveFor.displayTitle || moveFor.id }) : "")) || "",
|
|
975
|
+
sessionTitle: moveFor.displayTitle || moveFor.id,
|
|
976
|
+
confirmLabel: t ? t("confirm.move.confirm") : "确认移动",
|
|
977
|
+
cancelLabel: t ? t("confirm.cancel") : "取消",
|
|
978
|
+
busy: false,
|
|
979
|
+
onConfirm: (targetWorkspaceId) => {
|
|
980
|
+
const target = moveFor;
|
|
981
|
+
setMoveFor(null);
|
|
982
|
+
setMoveAnchor(null);
|
|
983
|
+
runWithAlert("move", () => onMove(target.id, targetWorkspaceId));
|
|
984
|
+
}
|
|
985
|
+
})
|
|
986
|
+
);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
class SafePanel extends React.Component {
|
|
990
|
+
constructor(p) { super(p); this.state = { failed: false }; }
|
|
991
|
+
getDerivedStateFromError() { return { failed: true }; }
|
|
992
|
+
componentDidCatch(error) { try { console.error("[dsh-session-manager] SessionManagerPanel crashed:", error); } catch (_) {} }
|
|
993
|
+
render() {
|
|
994
|
+
if (this.state.failed) return null;
|
|
995
|
+
return h(SessionManagerPanel, {
|
|
996
|
+
open: true,
|
|
997
|
+
onClose: () => { try { this.props.panelStore.set(false); } catch (_) {} },
|
|
998
|
+
useSessions: this.props.useSessions,
|
|
999
|
+
useWorkspaces: this.props.useWorkspaces,
|
|
1000
|
+
t: this.props.t,
|
|
1001
|
+
onOpen: (id) => { try { this.props.panelStore.set(false); this.props.props.onOpen(id); } catch (_) {} },
|
|
1002
|
+
onArchive: this.props.props.onArchive,
|
|
1003
|
+
onUnarchive: this.props.props.onUnarchive,
|
|
1004
|
+
onRemove: this.props.props.onRemove,
|
|
1005
|
+
onMove: this.props.props.onMove,
|
|
1006
|
+
onMigrate: this.props.props.onMigrate
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
function FooterAction(props) {
|
|
1012
|
+
const t = props.t;
|
|
1013
|
+
const onOpenPanel = props.onOpenPanel;
|
|
1014
|
+
const open = useSyncExternalStore(
|
|
1015
|
+
(cb) => panelStore.subscribe(cb),
|
|
1016
|
+
() => panelStore.getSnapshot(),
|
|
1017
|
+
() => panelStore.getSnapshot()
|
|
1018
|
+
);
|
|
1019
|
+
return h("div", { className: "sm-footer" },
|
|
1020
|
+
h("button", { type: "button", className: "sm-footerBtn", "aria-label": t("footer.aria"), onClick: () => onOpenPanel() },
|
|
1021
|
+
h(P.IconArchiveOutline20, { size: 14 }),
|
|
1022
|
+
h("span", null, t("footer.label"))
|
|
1023
|
+
),
|
|
1024
|
+
// The panel is mounted ONLY while the user has the modal open. When
|
|
1025
|
+
// The wrap guards against an in-panel crash by returning null on its
|
|
1026
|
+
// own error boundary rather than letting it bubble up to the slot.
|
|
1027
|
+
open ? h(SafePanel, {
|
|
1028
|
+
panelStore: panelStore,
|
|
1029
|
+
props: props,
|
|
1030
|
+
t: t,
|
|
1031
|
+
useSessions: props.useSessions,
|
|
1032
|
+
useWorkspaces: props.useWorkspaces,
|
|
1033
|
+
}) : null
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
function apply(ctx) {
|
|
1038
|
+
ctx.effect(() => ctx.locale.register(NS, { zh, en }), "session-manager: dictionaries");
|
|
1039
|
+
const actions = () => ({
|
|
1040
|
+
onOpen: (sessionId) => { ctx.sessions.open(sessionId); },
|
|
1041
|
+
onArchive: async (sessionId) => { await ctx.workspaces.archiveSession(sessionId); },
|
|
1042
|
+
onUnarchive: async (sessionId) => {
|
|
1043
|
+
const response = await callApi("/unarchive", { sessionId });
|
|
1044
|
+
if (!response.ok) throw new Error(response.error || "unarchive failed");
|
|
1045
|
+
},
|
|
1046
|
+
onRemove: async (sessionId) => {
|
|
1047
|
+
const response = await callApi("/delete", { sessionId });
|
|
1048
|
+
if (!response.ok) throw new Error(response.error || "delete failed");
|
|
1049
|
+
const current = ctx.sessions.list.getSnapshot().current;
|
|
1050
|
+
if (current === sessionId) ctx.sessions.clear();
|
|
1051
|
+
},
|
|
1052
|
+
onMove: async (sessionId, targetWorkspaceId) => {
|
|
1053
|
+
const wasCurrent = ctx.sessions.list.getSnapshot().current === sessionId;
|
|
1054
|
+
const response = await callApi("/move", { sessionId, targetWorkspaceId });
|
|
1055
|
+
if (!response.ok) throw new Error(response.error || "move failed");
|
|
1056
|
+
const refreshMovedSession = async () => {
|
|
1057
|
+
await Promise.allSettled([
|
|
1058
|
+
typeof ctx.workspaces?.refresh === "function" ? ctx.workspaces.refresh() : Promise.resolve(),
|
|
1059
|
+
ctx.sessions.refresh()
|
|
1060
|
+
]);
|
|
1061
|
+
if (wasCurrent && ctx.sessions.list.getSnapshot().byId[sessionId] !== undefined) {
|
|
1062
|
+
ctx.sessions.open(sessionId);
|
|
1063
|
+
}
|
|
1064
|
+
};
|
|
1065
|
+
// /move retires a live session, so host/session-removed can arrive on
|
|
1066
|
+
// the streaming channel slightly after the HTTP success response.
|
|
1067
|
+
// Pull both projections now and once more after that frame to make the
|
|
1068
|
+
// destination row appear without a browser refresh.
|
|
1069
|
+
await refreshMovedSession();
|
|
1070
|
+
setTimeout(() => { void refreshMovedSession(); }, 250);
|
|
1071
|
+
setTimeout(() => { void refreshMovedSession(); }, 900);
|
|
1072
|
+
},
|
|
1073
|
+
onMigrate: async (sessionId, toPreset) => {
|
|
1074
|
+
const wasCurrent = ctx.sessions.list.getSnapshot().current === sessionId;
|
|
1075
|
+
const response = await callApi("/preset-migrate", { sessionId, toPreset });
|
|
1076
|
+
if (!response.ok) throw new Error(response.error || "preset migration failed");
|
|
1077
|
+
ctx.sessions.noteAgentPreset(sessionId, toPreset);
|
|
1078
|
+
await ctx.sessions.refresh();
|
|
1079
|
+
if (wasCurrent) ctx.sessions.open(sessionId);
|
|
1080
|
+
// Host/session-removed and the HTTP response travel on different
|
|
1081
|
+
// streams. One delayed baseline closes that ordering race without a
|
|
1082
|
+
// page refresh and keeps the migrated conversation selectable.
|
|
1083
|
+
setTimeout(() => {
|
|
1084
|
+
void ctx.sessions.refresh().then(() => {
|
|
1085
|
+
ctx.sessions.noteAgentPreset(sessionId, toPreset);
|
|
1086
|
+
if (wasCurrent) ctx.sessions.open(sessionId);
|
|
1087
|
+
}).catch(() => {});
|
|
1088
|
+
}, 250);
|
|
1089
|
+
},
|
|
1090
|
+
onOpenPanel: () => panelStore.set(true)
|
|
1091
|
+
});
|
|
1092
|
+
ctx.slots.inject("conversation.session.header.actions", () => ctx.slots.register({
|
|
1093
|
+
name: "conversation.session.header.actions",
|
|
1094
|
+
id: "session-manager-header",
|
|
1095
|
+
order: 40,
|
|
1096
|
+
locale: NS,
|
|
1097
|
+
inject: () => actions()
|
|
1098
|
+
}, HeaderAction));
|
|
1099
|
+
ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
|
|
1100
|
+
name: "sidebar.footer.action",
|
|
1101
|
+
id: "session-manager-footer",
|
|
1102
|
+
order: 30,
|
|
1103
|
+
locale: NS,
|
|
1104
|
+
inject: () => actions()
|
|
1105
|
+
}, FooterAction));
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
const inject = ["slots", "sessions", "workspaces", "locale"];
|
|
1109
|
+
|
|
1110
|
+
exports.apply = apply;
|
|
1111
|
+
exports.inject = inject;
|
|
1112
|
+
return module.exports;
|
|
1113
|
+
}
|
|
1035
1114
|
});
|