dsh-sessions-manager 3.5.0 → 3.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +12 -4
- package/README.md +12 -4
- package/lib/client.js +130 -92
- package/lib/client.js.map +2 -2
- package/lib/index.js +904 -241
- package/lib/index.js.map +4 -4
- package/package.json +1 -1
- package/src/client/index.jsx +154 -90
- package/src/client/logic.js +13 -1
- package/src/compat/capabilities.js +64 -8
- package/src/compat/persistence.js +137 -18
- package/src/handle-era-ops.js +185 -0
- package/src/handle-era-paths.js +126 -0
- package/src/index.js +373 -96
- package/src/markdown.js +122 -71
- package/src/path-guard.js +59 -0
- package/src/session-meta-cache.js +40 -13
- package/src/title-persist-index.js +6 -1
- package/src/zstd-frame.js +68 -34
package/README.en.md
CHANGED
|
@@ -12,9 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
> DSH session manager: archive, move, restore, and inspect sessions from **Settings → 会话管理**; mark unread, move, and delete sessions directly from the **main sidebar**. Deleted sessions go to the recycle bin first and can be restored or permanently purged.
|
|
14
14
|
|
|
15
|
-
A persistent DSH plugin (host + browser halves)
|
|
16
|
-
|
|
17
|
-
v3.5.0 adds a two-generation DSH persistence adapter. It understands both legacy header lists/`readFrom` and the new snapshot/`SessionHandle` API, and reports action-level capabilities to the UI. When a runtime has no verified safe purge or migration path, the corresponding controls explain why they are disabled and the Host rejects direct calls, preventing false success messages.
|
|
15
|
+
A persistent DSH plugin (host + browser halves) covering both the **settings panel** and the **main sidebar**, so frequent session actions don't require opening Settings. It understands both legacy header lists/`readFrom` and the new snapshot/`SessionHandle` API, enabling actions according to the current Runtime's verified capabilities. Unverified purge or migration paths are disabled in both UI and Host to prevent false success.
|
|
18
16
|
|
|
19
17
|
## Features
|
|
20
18
|
|
|
@@ -45,7 +43,7 @@ A small dot is rendered to the left of each session row. Its color is driven by
|
|
|
45
43
|
| Color | State | Description |
|
|
46
44
|
|---|---|---|
|
|
47
45
|
| 🔵 Blue | Manually marked unread | Toggle via the ⋯ menu or by clicking the dot; auto-cleared when the session is opened |
|
|
48
|
-
| 🟡 Yellow | Working | The session is currently running (`running`) |
|
|
46
|
+
| 🟡 Yellow | Working | The session is currently running (`ongoing`, with legacy `running` compatibility) |
|
|
49
47
|
| 🟠 Amber | Awaiting feedback | The session has a follow-up question and is waiting for your input or confirmation (`warning`) |
|
|
50
48
|
| 🟢 Green | Completed but unread | The session is done (`done`) but you haven't reopened it yet; disappears after you view it |
|
|
51
49
|
| 🔴 Red | Error / needs attention | The session hit an error (`error`) |
|
|
@@ -164,6 +162,16 @@ lib/client.js pre-built client (ModuleLoader CJS handshake)
|
|
|
164
162
|
- `0.1.3-alpha.1`: snapshot lists and read-only `SessionHandle` flows are supported. Permanent purge and cross-workspace move are disabled when no verified safe path is available; other management functions continue to work. The panel shows the effective capabilities.
|
|
165
163
|
- Unverified future runtimes expose only capabilities the plugin can safely identify; method presence alone is not presented as behavioral compatibility.
|
|
166
164
|
|
|
165
|
+
### Behavior differences and degradations under `0.1.3-alpha.1`
|
|
166
|
+
|
|
167
|
+
- **Recycle-bin restore is verified**: before restoring, the plugin confirms the underlying stored session still exists (live / `stat` / listing, in that order). It returns accurate errors when the session is gone (`DSM_SESSION_MISSING`), was permanently purged (`DSM_SESSION_PURGED`), or the index still lists it but the log file has vanished (`DSM_SESSION_LOG_MISSING`); when the log location cannot be verified the entry is honestly reported as `unverified` instead of silently passing. Restoring a session whose workspace was deleted succeeds and notes that it now lives under "未分组" (ungrouped).
|
|
168
|
+
- **Permanent purge / empty recycle bin / automatic physical cleanup**: the official public contract offers no delete API, so the plugin keeps the legacy-era semi-official approach — a **three-layer guarded path derivation** starting from the backend instance's storage-root field (root → session directory structure → session-id ownership check), followed by a whole-directory removal verified with the official `stat`. Sessions with an active writer are refused (409). When the derivation fails (e.g. the storage root is unavailable), the actions degrade to disabled with an explicit reason instead of deleting blindly.
|
|
169
|
+
- **Cross-workspace move**: the primary path replays events through the official `create` + `append` (writer-ownership probe refuses active sessions, before/after `revision` checks guard against concurrent writes, backup + rollback guarantees no half-moved state on failure); when the backend holds a ghost record for the same id it falls back to relocating the log with a frame0 cwd rewrite (frame count and content verified byte-for-byte). Paths come from the same guarded derivation; the workspace grouping index is rebuilt automatically after the move — no restart needed.
|
|
170
|
+
- **Purge tombstones never suppress new sessions**: if a session with the same id is recreated later, the tombstone yields automatically and the new session appears normally in lists and the sidebar.
|
|
171
|
+
- **Metadata-cache change tokens**: on the new runtime the plugin prefers the official `stat`/`list` snapshot `revision` (comparable only within one service instance and one session id) to detect changes; legacy runtimes keep the `(mtime, size)` file fingerprint. Revisions are never persisted into the cross-process title index. List metadata comes straight from `snapshot.header` and titles from the batch projection — a missing title does **not** trigger a full-log decode, avoiding amplification of the known historical-session loading regression in alpha.
|
|
172
|
+
- **Auto-archive**: the new runtime exposes no reliable "last activity" timestamp; the sweep skips (`no-activity-data`) when idleness cannot be proven, and never archives on a guess.
|
|
173
|
+
- **Sidebar injection**: DOM / React fiber recognition is centralized in a versioned adapter; when the upstream sidebar shape is no longer recognized, all injections degrade safely and the official sidebar is left untouched. While recognition works, updates are driven incrementally by a MutationObserver with only a low-frequency fallback check.
|
|
174
|
+
|
|
167
175
|
## License
|
|
168
176
|
|
|
169
177
|
[MIT](./LICENSE) © TOBYCAI
|
package/README.md
CHANGED
|
@@ -12,9 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
> DSH 会话管理器:在**设置 → 会话管理**里统一归档、移动、恢复、查看详情;在**主页侧边栏**直接标记未读、移动、删除会话。删除先进入回收站,可恢复或彻底清理。
|
|
14
14
|
|
|
15
|
-
一个 DSH 持久化插件(host + browser
|
|
16
|
-
|
|
17
|
-
v3.5.0 新增 DSH 双代持久化适配:同时识别旧式 header 列表/`readFrom` 与新版 snapshot/`SessionHandle`,并向界面报告每项操作的实际能力。当前 Runtime 没有经过验证的安全删除或迁移路径时,相关按钮会显示禁用原因,Host 也会拒绝调用,避免出现“提示成功但日志没有删除/移动”的假成功。
|
|
15
|
+
一个 DSH 持久化插件(host + browser 双半),同时覆盖「设置面板」与「主页侧边栏」两个入口,无需打开设置即可完成高频会话操作。插件同时识别旧式 header 列表/`readFrom` 与新版 snapshot/`SessionHandle`,并按当前 Runtime 的实际能力启用安全操作;尚未验证的删除或迁移路径会在界面与 Host 端同步禁用,避免假成功。
|
|
18
16
|
|
|
19
17
|
## 功能
|
|
20
18
|
|
|
@@ -45,7 +43,7 @@ v3.5.0 新增 DSH 双代持久化适配:同时识别旧式 header 列表/`read
|
|
|
45
43
|
| 颜色 | 状态 | 说明 |
|
|
46
44
|
|---|---|---|
|
|
47
45
|
| 🔵 蓝 | 手动标记未读 | 通过 ⋯ 菜单或点击圆点手动标记;进入会话后自动清除 |
|
|
48
|
-
| 🟡 黄 | 工作中 |
|
|
46
|
+
| 🟡 黄 | 工作中 | 会话正在运行(新版 `ongoing`,兼容旧版 `running`) |
|
|
49
47
|
| 🟠 琥珀 | 等待反馈 | 会话有追问,需要用户输入或确认(`warning`) |
|
|
50
48
|
| 🟢 绿 | 完成后未读 | 会话已完成(`done`)但你还没重新打开看过;看过一次后不再显示 |
|
|
51
49
|
| 🔴 红 | 出错 / 需关注 | 会话遇到错误(`error`) |
|
|
@@ -164,6 +162,16 @@ lib/client.js 预构建 client(ModuleLoader CJS handshake)
|
|
|
164
162
|
- `0.1.3-alpha.1`:支持 snapshot 列表和 `SessionHandle` 只读流程;永久删除与跨工作区移动在缺少已验证安全路径时自动禁用,其余管理能力继续工作。能力以面板实际提示为准。
|
|
165
163
|
- 未经验证的未来 Runtime 默认只开放能够识别的安全能力;插件不会用方法存在与否冒充行为兼容。
|
|
166
164
|
|
|
165
|
+
### `0.1.3-alpha.1` 下的行为差异与降级说明
|
|
166
|
+
|
|
167
|
+
- **回收站恢复需要校验**:恢复前会确认底层会话仍存在(live / `stat` / 列表三级判定)。底层会话已不存在(`DSM_SESSION_MISSING`)、已被彻底删除(`DSM_SESSION_PURGED`)或索引仍在但日志文件消失(`DSM_SESSION_LOG_MISSING`)时返回准确错误;无法核验日志位置时如实标注 `unverified`,不会假装校验通过。恢复到「工作区已删除」的会话会成功并提示其暂归「未分组」。
|
|
168
|
+
- **彻底删除 / 清空回收站 / 自动物理清理**:官方公共契约未提供删除 API,插件沿用 legacy 时代的半官方路线——从后端实例的存储根目录字段出发做**三层守卫式路径推导**(根目录 → 会话目录结构 → 会话 ID 归属校验),通过后整目录删除并以官方 `stat` 复核;存在活跃写入的会话会被拒绝(409)。推导失败(如无法确认存储根目录)时自动禁用并说明原因,绝不盲删。
|
|
169
|
+
- **跨工作区移动**:主路径为官方 `create` + `append` 事件重放(写所有权探测拒绝活跃会话、revision 前后校验防并发写入、备份回滚保证失败不留半移动状态);后端存在同 ID 幽灵记录时回退到 frame0 cwd 改写搬运(帧数与内容逐位校验)。路径同样来自守卫式推导;移动后自动重建工作区分组索引,无需重启。
|
|
170
|
+
- **彻底删除墓碑不压制新会话**:若同 ID 会话被重新创建,墓碑自动让位,新会话正常出现在列表与侧栏。
|
|
171
|
+
- **元数据缓存的变更令牌**:新 Runtime 优先使用官方 `stat`/`list` 快照的 `revision`(仅同一 service 实例、同一会话内可比较)判断会话是否变化,旧 Runtime 继续使用 `(mtime, size)` 文件指纹;`revision` 绝不写入跨进程持久索引。列表元数据直接取自 `snapshot.header`,标题走批量投影——没有标题时**不会**自动解码整本日志,避免放大 alpha 已知的历史会话加载性能回退。
|
|
172
|
+
- **自动归档**:新 Runtime 不提供可靠的「最后活跃时间」,无法证明会话闲置时检查会跳过(`no-activity-data`),绝不基于猜测归档。
|
|
173
|
+
- **侧栏注入**:DOM / React fiber 识别收敛为可版本化的 adapter;上游侧栏结构不被识别时整体安全停用,不影响官方侧栏本身。识别正常时以 MutationObserver 增量驱动为主,仅保留低频兜底检查。
|
|
174
|
+
|
|
167
175
|
## License
|
|
168
176
|
|
|
169
177
|
[MIT](./LICENSE) © TOBYCAI
|
package/lib/client.js
CHANGED
|
@@ -40,9 +40,12 @@ var import_react = __toESM(require("react"), 1);
|
|
|
40
40
|
function dotStateFor({ manualUnread = false, dataState = "", isActive = false } = {}) {
|
|
41
41
|
if (manualUnread) return "manual";
|
|
42
42
|
switch (dataState) {
|
|
43
|
+
case "ongoing":
|
|
43
44
|
case "running":
|
|
44
45
|
return "running";
|
|
45
46
|
case "warning":
|
|
47
|
+
case "waiting":
|
|
48
|
+
case "needs-attention":
|
|
46
49
|
return "feedback";
|
|
47
50
|
case "error":
|
|
48
51
|
return "error";
|
|
@@ -52,6 +55,10 @@ function dotStateFor({ manualUnread = false, dataState = "", isActive = false }
|
|
|
52
55
|
return null;
|
|
53
56
|
}
|
|
54
57
|
}
|
|
58
|
+
function authoritativeTitleForFirstPaint({ firstPaint = false, rendered = "", authoritative = "" } = {}) {
|
|
59
|
+
if (!firstPaint || !authoritative || rendered === authoritative) return null;
|
|
60
|
+
return authoritative;
|
|
61
|
+
}
|
|
55
62
|
function canDropOnWorkspace(item, target) {
|
|
56
63
|
if (!item || !target) return false;
|
|
57
64
|
if (item.workspacePath && target.path === item.workspacePath) return false;
|
|
@@ -91,12 +98,12 @@ function loadPanelPrefs() {
|
|
|
91
98
|
}
|
|
92
99
|
}
|
|
93
100
|
var CSS = `
|
|
94
|
-
.archv{--dsm-radius-tag:4px;display:flex;flex-direction:column;gap:4px;max-width:800px;padding:8px 2px 28px}
|
|
101
|
+
.archv{--dsm-radius-tag:4px;--dsm-radius-ctl:9px;--dsm-radius-sheet:10px;--dsm-radius-card:12px;display:flex;flex-direction:column;gap:4px;max-width:800px;padding:8px 2px 28px}
|
|
95
102
|
.archv-head{display:flex;align-items:center;gap:10px;margin:0 0 2px}
|
|
96
103
|
.archv-title{font-size:16px;font-weight:650;color:var(--dsw-alias-label-primary);letter-spacing:-0.01em;margin:0}
|
|
97
104
|
.archv-count{font-size:11px;font-weight:600;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-fill-subtle);border:1px solid var(--dsw-alias-border-l2);border-radius:999px;padding:1px 8px;flex:none}
|
|
98
105
|
.archv-sub{font-size:12px;line-height:1.55;color:var(--dsw-alias-label-tertiary);margin:0 0 12px;max-width:64ch}
|
|
99
|
-
.archv-err{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:9px 12px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-error-primary) 40%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 8%,transparent);border-radius:
|
|
106
|
+
.archv-err{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:9px 12px;border:1px solid color-mix(in srgb,var(--dsw-alias-state-error-primary) 40%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 8%,transparent);border-radius:var(--dsm-radius-sheet);color:var(--dsw-alias-state-error-primary);font-size:12px;margin-bottom:10px}
|
|
100
107
|
.archv-errretry{appearance:none;border:1px solid color-mix(in srgb,var(--dsw-alias-state-error-primary) 45%,transparent);background:transparent;color:inherit;border-radius:8px;padding:4px 10px;font-size:11px;cursor:pointer;flex:none}
|
|
101
108
|
.sess-filter{display:flex;align-items:center;gap:6px;margin:0 0 12px}
|
|
102
109
|
.sess-fbtn{appearance:none;min-height:30px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-fill-subtle);color:var(--dsw-alias-label-secondary);border-radius:999px;font-size:12px;font-weight:500;cursor:pointer}
|
|
@@ -105,13 +112,13 @@ var CSS = `
|
|
|
105
112
|
.sess-tools{display:grid;grid-template-columns:minmax(180px,1fr) minmax(140px,.7fr) minmax(130px,.55fr);gap:8px;margin:0 0 8px}
|
|
106
113
|
.sess-field{display:flex;flex-direction:column;gap:5px;min-width:0}
|
|
107
114
|
.sess-field label{font-size:11px;font-weight:600;color:var(--dsw-alias-label-secondary)}
|
|
108
|
-
.sess-field input,.sess-field select{box-sizing:border-box;width:100%;min-height:36px;padding:0 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:
|
|
115
|
+
.sess-field input,.sess-field select{box-sizing:border-box;width:100%;min-height:36px;padding:0 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsm-radius-ctl);background:var(--dsw-alias-fill-elevated);color:var(--dsw-alias-label-primary);font:inherit;font-size:12px}
|
|
109
116
|
.sess-results{font-size:11px;color:var(--dsw-alias-label-tertiary);margin:0 0 4px}
|
|
110
117
|
.archv button:focus-visible,.archv input:focus-visible,.archv select:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}
|
|
111
118
|
.sess-batch{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:8px 2px 4px;margin-bottom:4px}
|
|
112
119
|
.sess-btntext{font-size:12px;color:var(--dsw-alias-label-tertiary);flex:none}
|
|
113
120
|
.archv-list{display:flex;flex-direction:column;gap:8px}
|
|
114
|
-
.archv-card{display:flex;flex-direction:column;align-items:stretch;gap:0;padding:12px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:
|
|
121
|
+
.archv-card{display:flex;flex-direction:column;align-items:stretch;gap:0;padding:12px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsm-radius-card);background:var(--dsw-alias-fill-elevated);transition:border-color .15s ease,background-color .15s ease}
|
|
115
122
|
.archv-card:hover{border-color:var(--dsw-alias-border-l4)}
|
|
116
123
|
.archv-card-exp{border-color:var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-1)}
|
|
117
124
|
.archv-row{display:flex;align-items:center;gap:14px;width:100%;min-width:0}
|
|
@@ -132,16 +139,16 @@ var CSS = `
|
|
|
132
139
|
.archv-star-on svg{fill:currentColor}
|
|
133
140
|
.archv-body{flex:1;min-width:0;display:flex;align-items:center;gap:12px}
|
|
134
141
|
.archv-actions{display:flex;gap:8px;flex:none;flex-wrap:nowrap;justify-content:flex-end}
|
|
135
|
-
.archv-btn{appearance:none;min-height:32px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-fill-subtle);color:var(--dsw-alias-label-secondary);border-radius:
|
|
142
|
+
.archv-btn{appearance:none;min-height:32px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-fill-subtle);color:var(--dsw-alias-label-secondary);border-radius:var(--dsm-radius-ctl);font-size:12px;font-weight:500;cursor:pointer;white-space:nowrap;display:inline-flex;align-items:center;justify-content:center;gap:6px;text-align:center;transition:background-color .15s ease,border-color .15s ease,color .15s ease}
|
|
136
143
|
.archv-btn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
|
|
137
144
|
.archv-btn:disabled{opacity:.5;cursor:default}
|
|
138
145
|
.archv-del{color:var(--dsw-alias-state-error-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 45%,transparent)}
|
|
139
146
|
.archv-del:hover:not(:disabled){background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent);color:var(--dsw-alias-state-error-primary)}
|
|
140
147
|
.archv-go{color:var(--dsw-alias-state-business-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-business-primary) 45%,transparent)}
|
|
141
148
|
.archv-go:hover:not(:disabled){background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 10%,transparent);color:var(--dsw-alias-state-business-primary)}
|
|
142
|
-
.archv-empty{display:flex;align-items:center;gap:10px;padding:20px 14px;border:1px dashed var(--dsw-alias-border-l3);border-radius:
|
|
149
|
+
.archv-empty{display:flex;align-items:center;gap:10px;padding:20px 14px;border:1px dashed var(--dsw-alias-border-l3);border-radius:var(--dsm-radius-card);color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}
|
|
143
150
|
.archv-skel{display:flex;flex-direction:column;gap:8px}
|
|
144
|
-
.archv-skel-card{height:58px;border-radius:
|
|
151
|
+
.archv-skel-card{height:58px;border-radius:var(--dsm-radius-card);background:var(--dsw-alias-fill-subtle);position:relative;overflow:hidden}
|
|
145
152
|
.archv-skel-card::after{content:'';position:absolute;inset:0;transform:translateX(-100%);background:linear-gradient(90deg,transparent,color-mix(in srgb,var(--dsw-alias-fill-elevated) 75%,transparent),transparent);animation:archv-shimmer 1.4s infinite}
|
|
146
153
|
.archv-status{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);z-index:60;background:var(--dsw-alias-fill-elevated);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);padding:9px 16px;border-radius:999px;font-size:12px;box-shadow:0 8px 24px rgb(0 0 0/.18);display:flex;align-items:center;gap:8px;animation:archv-pop .18s ease-out;max-width:min(90vw,420px)}
|
|
147
154
|
.archv-spin{width:12px;height:12px;border:2px solid color-mix(in srgb,var(--dsw-alias-label-secondary) 35%,transparent);border-top-color:var(--dsw-alias-label-secondary);border-radius:50%;animation:archv-rot .8s linear infinite;flex:none}
|
|
@@ -150,24 +157,24 @@ var CSS = `
|
|
|
150
157
|
@keyframes archv-pop{from{opacity:0;transform:translateX(-50%) translateY(10px)}}
|
|
151
158
|
@media (prefers-reduced-motion:reduce){.archv-skel-card::after{animation:none}.archv-card,.archv-btn{transition:none}.archv-status,.archv-spin{animation:none}}
|
|
152
159
|
@media (max-width:640px){.archv-card{flex-direction:column;align-items:stretch;gap:10px}.archv-actions{justify-content:flex-end}.sess-tools{grid-template-columns:1fr}.sess-fbtn,.archv-btn{min-height:40px}}
|
|
153
|
-
.mv-sheet{width:100%;box-sizing:border-box;display:flex;flex-direction:column;gap:12px;margin-top:12px;padding:14px;border:1px solid var(--dsw-alias-border-l2);border-radius:
|
|
160
|
+
.mv-sheet{width:100%;box-sizing:border-box;display:flex;flex-direction:column;gap:12px;margin-top:12px;padding:14px;border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsm-radius-sheet);background:var(--dsw-alias-fill-subtle)}
|
|
154
161
|
.mv-sheet-head{display:flex;align-items:center;justify-content:space-between;gap:10px}
|
|
155
162
|
.mv-sheet-title{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);margin:0}
|
|
156
163
|
.mv-sheet-close{appearance:none;width:26px;height:26px;color:var(--dsw-alias-label-secondary);background:0 0;border:none;border-radius:7px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:16px;line-height:1}
|
|
157
164
|
.mv-sheet-close:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
|
|
158
|
-
.mv-seg{display:flex;gap:2px;padding:2px;background:var(--dsw-alias-fill-elevated);border:1px solid var(--dsw-alias-border-l2);border-radius:
|
|
165
|
+
.mv-seg{display:flex;gap:2px;padding:2px;background:var(--dsw-alias-fill-elevated);border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsm-radius-sheet);width:100%}
|
|
159
166
|
.mv-segbtn{appearance:none;flex:1;min-height:30px;padding:0 12px;border:none;background:transparent;color:var(--dsw-alias-label-secondary);border-radius:8px;font-size:12px;font-weight:500;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:6px}
|
|
160
167
|
.mv-segbtn:hover:not(:disabled){color:var(--dsw-alias-label-primary)}
|
|
161
168
|
.mv-segbtn-on{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);box-shadow:0 1px 2px rgb(0 0 0/.08)}
|
|
162
169
|
.mv-field{display:flex;flex-direction:column;gap:6px}
|
|
163
170
|
.mv-field label.mv-field-label{font-size:12px;font-weight:500;color:var(--dsw-alias-label-secondary)}
|
|
164
|
-
.mv-field select,.mv-field input[type=text]{box-sizing:border-box;appearance:none;width:100%;min-height:34px;padding:0 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:
|
|
171
|
+
.mv-field select,.mv-field input[type=text]{box-sizing:border-box;appearance:none;width:100%;min-height:34px;padding:0 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsm-radius-ctl);background:var(--dsw-alias-fill-elevated);color:var(--dsw-alias-label-primary);font-size:12px;font-family:inherit}
|
|
165
172
|
.mv-field select:focus-visible,.mv-field input[type=text]:focus-visible,.mv-sheet-close:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px}
|
|
166
173
|
.mv-browse-row{display:flex;align-items:center;gap:8px}
|
|
167
174
|
.mv-browse-row input[type=text]{flex:1;min-width:0}
|
|
168
175
|
.mv-foot{display:flex;justify-content:flex-end;align-items:center;gap:8px;margin-top:2px}
|
|
169
176
|
@media (max-width:640px){.archv-row{flex-wrap:wrap}.mv-sheet{padding:12px}}
|
|
170
|
-
.dtl-sheet{margin-top:12px;padding:14px;border:1px solid var(--dsw-alias-border-l2);border-radius:
|
|
177
|
+
.dtl-sheet{margin-top:12px;padding:14px;border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsm-radius-sheet);background:var(--dsw-alias-fill-subtle)}
|
|
171
178
|
.dtl-sheet-head{display:flex;align-items:center;justify-content:space-between;gap:10px}
|
|
172
179
|
.dtl-sheet-title{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);margin:0}
|
|
173
180
|
.dtl-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:8px;margin-top:10px}
|
|
@@ -188,7 +195,7 @@ var CSS = `
|
|
|
188
195
|
.more-wrap{position:relative;flex:none}
|
|
189
196
|
.more-btn{appearance:none;width:28px;height:28px;border:none;border-radius:8px;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;line-height:1}
|
|
190
197
|
.more-btn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
|
|
191
|
-
.more-menu{position:absolute;top:calc(100% + 4px);right:0;z-index:60;min-width:160px;padding:5px;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l3);border-radius:
|
|
198
|
+
.more-menu{position:absolute;top:calc(100% + 4px);right:0;z-index:60;min-width:160px;padding:5px;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l3);border-radius:var(--dsm-radius-sheet);box-shadow:0 10px 32px rgb(0 0 0/.24);display:flex;flex-direction:column;gap:1px}
|
|
192
199
|
.more-item{appearance:none;display:flex;align-items:center;gap:8px;width:100%;padding:7px 10px;border:none;background:transparent;color:var(--dsw-alias-label-primary);border-radius:7px;font-size:12.5px;cursor:pointer;white-space:nowrap;text-align:left}
|
|
193
200
|
.more-item:hover{background:var(--dsw-alias-interactive-bg-hover)}
|
|
194
201
|
.more-item-danger{color:var(--dsw-alias-state-error-primary)}
|
|
@@ -431,9 +438,13 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
431
438
|
const w = workspaces.find((x) => x.workspaceId === id);
|
|
432
439
|
return w ? w.path : "";
|
|
433
440
|
};
|
|
434
|
-
const actionCapability = (name) =>
|
|
435
|
-
|
|
436
|
-
|
|
441
|
+
const actionCapability = (name, legacy) => {
|
|
442
|
+
const source = capabilities && capabilities.actions;
|
|
443
|
+
return source && (source[name] || legacy && source[legacy]) || { available: false, reason: "\u6B63\u5728\u68C0\u67E5\u5F53\u524D DSH \u7684\u517C\u5BB9\u80FD\u529B\u2026" };
|
|
444
|
+
};
|
|
445
|
+
const canPurge = actionCapability("physicalPurge", "purge");
|
|
446
|
+
const canMove = actionCapability("relocateSession", "move");
|
|
447
|
+
const canRestoreTrash = actionCapability("restoreIndexedSession", "restoreTrash");
|
|
437
448
|
const archivedList = sessions ? sessions.filter((x) => x.archived) : [];
|
|
438
449
|
const activeList = sessions ? sessions.filter((x) => !x.archived) : [];
|
|
439
450
|
const starredList = starredOf(sessions);
|
|
@@ -574,9 +585,9 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
574
585
|
const restoreTrash = (sid) => {
|
|
575
586
|
if (trashBusy) return;
|
|
576
587
|
setTrashBusy(sid);
|
|
577
|
-
postJSON("/archived-sessions/trash/restore", { sessionId: sid }).then(() => {
|
|
588
|
+
postJSON("/archived-sessions/trash/restore", { sessionId: sid }).then((r) => {
|
|
578
589
|
setTrashBusy(null);
|
|
579
|
-
showToast("\u5DF2\u6062\u590D\u4F1A\u8BDD");
|
|
590
|
+
showToast(r && r.workspaceGone ? "\u5DF2\u6062\u590D\u4F1A\u8BDD\uFF08\u539F\u5DE5\u4F5C\u533A\u5DF2\u5220\u9664\uFF0C\u4F1A\u8BDD\u6682\u5F52\u300C\u672A\u5206\u7EC4\u300D\uFF09" : "\u5DF2\u6062\u590D\u4F1A\u8BDD");
|
|
580
591
|
loadTrash();
|
|
581
592
|
refresh();
|
|
582
593
|
}).catch((e) => {
|
|
@@ -1212,10 +1223,16 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
1212
1223
|
] }),
|
|
1213
1224
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sess-field", children: [
|
|
1214
1225
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u6C38\u4E45\u6E05\u7406" }),
|
|
1215
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn archv-del", disabled: trashBusy !== null || !trash.length || !canPurge.available, title: !canPurge.available ? canPurge.reason :
|
|
1226
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn archv-del", disabled: trashBusy !== null || !trash.length || !canPurge.available, "aria-disabled": !canPurge.available, title: !canPurge.available ? canPurge.reason : "\u7269\u7406\u5220\u9664\u8FD9\u4E9B\u65E5\u5FD7\uFF0C\u91CA\u653E\u78C1\u76D8\u7A7A\u95F4", onClick: () => setPurgeTarget("__all"), children: "\u6E05\u7A7A\u56DE\u6536\u7AD9" })
|
|
1216
1227
|
] })
|
|
1217
1228
|
] }),
|
|
1218
|
-
!canPurge.available && /* @__PURE__ */ (0, import_jsx_runtime.
|
|
1229
|
+
!canPurge.available && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "archv-empty", role: "status", children: [
|
|
1230
|
+
canPurge.reason,
|
|
1231
|
+
"\u3002\u56DE\u6536\u7AD9\u6761\u76EE\u4F1A\u4E00\u76F4\u4FDD\u7559\uFF0C\u53EF\u968F\u65F6\u6062\u590D\uFF1B\u5F53\u524D DSH \u7248\u672C\u4E0B\u79FB\u5165\u56DE\u6536\u7AD9",
|
|
1232
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "\u4E0D\u4F1A\u91CA\u653E\u78C1\u76D8\u7A7A\u95F4" }),
|
|
1233
|
+
"\uFF0C\u4F1A\u8BDD\u65E5\u5FD7\u4ECD\u5B8C\u6574\u4FDD\u7559\u5728\u539F\u5DE5\u4F5C\u533A\u76EE\u5F55\u3002"
|
|
1234
|
+
] }),
|
|
1235
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtl-note", children: "\u56DE\u6536\u7AD9\u662F\u8F6F\u5220\u9664\uFF1A\u65E5\u5FD7\u4ECD\u7559\u5728\u539F\u5DE5\u4F5C\u533A\u76EE\u5F55\uFF0C\u300C\u5F7B\u5E95\u5220\u9664\u300D\u624D\u662F\u771F\u6B63\u91CA\u653E\u78C1\u76D8\u7A7A\u95F4\u7684\u4E00\u6B65\uFF08\u5F53\u524D\u7248\u672C\u672A\u652F\u6301\u65F6\u4F1A\u4FDD\u6301\u7981\u7528\uFF09\u3002" }),
|
|
1219
1236
|
trashCheck && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: trashCheck.missing ? "archv-err" : "archv-empty", role: "status", children: [
|
|
1220
1237
|
"\u6821\u9A8C\u5B8C\u6210\uFF1A",
|
|
1221
1238
|
trashCheck.healthy,
|
|
@@ -1229,7 +1246,7 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
1229
1246
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-trash-name", title: t.sessionId, children: t.title || t.sessionId }),
|
|
1230
1247
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsm-trash-date", children: fmtDate(t.deletedAt) }),
|
|
1231
1248
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsm-trash-actions", children: [
|
|
1232
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", disabled: trashBusy !== null, onClick: () => restoreTrash(t.sessionId), children: "\u6062\u590D" }),
|
|
1249
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", disabled: trashBusy !== null || !canRestoreTrash.available, title: !canRestoreTrash.available ? canRestoreTrash.reason : "\u6062\u590D\u5230\u5220\u9664\u524D\u7684\u4F4D\u7F6E\u4E0E\u5F52\u6863\u72B6\u6001", onClick: () => restoreTrash(t.sessionId), children: "\u6062\u590D" }),
|
|
1233
1250
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn archv-del", disabled: trashBusy !== null || !canPurge.available, title: !canPurge.available ? canPurge.reason : void 0, onClick: () => setPurgeTarget(t.sessionId), children: "\u5F7B\u5E95\u5220\u9664" })
|
|
1234
1251
|
] })
|
|
1235
1252
|
] }, t.sessionId)) })
|
|
@@ -1240,7 +1257,7 @@ function SessionPanel({ workspacesSvc }) {
|
|
|
1240
1257
|
purgeTarget === "__all" ? "\u5168\u90E8\u56DE\u6536\u7AD9\u4F1A\u8BDD" : "\u8FD9\u4E2A\u4F1A\u8BDD",
|
|
1241
1258
|
"\uFF1F"
|
|
1242
1259
|
] }),
|
|
1243
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dlg-text", children: "\u6B64\u64CD\u4F5C\u4F1A\u7269\u7406\u5220\u9664\u65E5\u5FD7\uFF0C\u65E0\u6CD5\u6062\u590D\u3002\u5F52\u6863\u3001\u7B5B\u9009\u548C\u91CD\u65B0\u5B89\u88C5\u63D2\u4EF6\u90FD\u4E0D\u80FD\u627E\u56DE\u8FD9\u4E9B\u6570\u636E\u3002" }),
|
|
1260
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dlg-text", children: "\u6B64\u64CD\u4F5C\u4F1A\u7269\u7406\u5220\u9664\u65E5\u5FD7\u5E76\u91CA\u653E\u78C1\u76D8\u7A7A\u95F4\uFF0C\u65E0\u6CD5\u6062\u590D\u3002\u5F52\u6863\u3001\u7B5B\u9009\u548C\u91CD\u65B0\u5B89\u88C5\u63D2\u4EF6\u90FD\u4E0D\u80FD\u627E\u56DE\u8FD9\u4E9B\u6570\u636E\u3002" }),
|
|
1244
1261
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dlg-actions", children: [
|
|
1245
1262
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn", onClick: () => setPurgeTarget(null), children: "\u53D6\u6D88" }),
|
|
1246
1263
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "archv-btn archv-del", onClick: () => {
|
|
@@ -1282,7 +1299,7 @@ var SIDEBAR_AUG_CSS = `
|
|
|
1282
1299
|
.dsm-sub-cur{font-size:11px;color:var(--dsw-alias-state-business-primary);flex:none;margin-left:8px}
|
|
1283
1300
|
.dsm-dot{position:absolute;left:6px;top:50%;transform:translateY(-50%);width:8px;height:8px;border-radius:50%;box-sizing:border-box;cursor:pointer;pointer-events:auto;z-index:1}
|
|
1284
1301
|
.dsm-dot-unread-manual{background:var(--dsw-alias-state-business-primary)}
|
|
1285
|
-
.dsm-dot-waiting{background:var(--dsw-alias-state-
|
|
1302
|
+
.dsm-dot-waiting{background:var(--dsw-alias-state-warn-primary,#F59E0B)}
|
|
1286
1303
|
.dsm-dot-unread{background:var(--dsw-alias-state-success-primary)}
|
|
1287
1304
|
.dsm-drag-source{opacity:.48}
|
|
1288
1305
|
.dsm-drop-target{position:relative;background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 10%,transparent)!important;outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px;border-radius:8px}
|
|
@@ -1351,57 +1368,83 @@ async function dsmLoadCapabilities() {
|
|
|
1351
1368
|
}
|
|
1352
1369
|
return dsmCapabilities;
|
|
1353
1370
|
}
|
|
1354
|
-
function dsmActionCapability(name) {
|
|
1355
|
-
|
|
1371
|
+
function dsmActionCapability(name, legacy) {
|
|
1372
|
+
const source = dsmCapabilities && dsmCapabilities.actions;
|
|
1373
|
+
return source ? source[name] || legacy && source[legacy] || null : null;
|
|
1356
1374
|
}
|
|
1357
1375
|
async function dsmLoadTrashIds() {
|
|
1358
1376
|
try {
|
|
1359
1377
|
const r = await postJSON("/archived-sessions/sidebar-state", {});
|
|
1360
1378
|
dsmTrashIds = new Set((r && r.trashedSessionIds || []).map(String));
|
|
1361
1379
|
dsmServerPurgedIds = new Set((r && r.purgedSessionIds || []).map(String));
|
|
1380
|
+
for (const id of [...dsmPendingPurged]) {
|
|
1381
|
+
if (dsmServerPurgedIds.has(id)) dsmPendingPurged.delete(id);
|
|
1382
|
+
}
|
|
1362
1383
|
dsmAuthoritativeTitles = new Map(Object.entries(r && r.titles || {}).map(([id, title]) => [String(id), String(title)]));
|
|
1363
1384
|
if (dsmRepaintDots) dsmRepaintDots();
|
|
1364
1385
|
} catch (e) {
|
|
1365
1386
|
}
|
|
1366
1387
|
return dsmTrashIds;
|
|
1367
1388
|
}
|
|
1368
|
-
var
|
|
1369
|
-
var dsmPurgedIds = null;
|
|
1389
|
+
var dsmPendingPurged = /* @__PURE__ */ new Set();
|
|
1370
1390
|
function dsmLoadPurged() {
|
|
1371
|
-
|
|
1372
|
-
try {
|
|
1373
|
-
dsmPurgedIds = new Set(JSON.parse(localStorage.getItem(DSM_KEY_PURGED) || "[]"));
|
|
1374
|
-
} catch (e) {
|
|
1375
|
-
dsmPurgedIds = /* @__PURE__ */ new Set();
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
dsmServerPurgedIds.forEach((id) => dsmPurgedIds.add(id));
|
|
1379
|
-
return dsmPurgedIds;
|
|
1380
|
-
}
|
|
1381
|
-
function dsmSavePurged() {
|
|
1382
|
-
try {
|
|
1383
|
-
localStorage.setItem(DSM_KEY_PURGED, JSON.stringify([...dsmLoadPurged()]));
|
|
1384
|
-
} catch (e) {
|
|
1385
|
-
}
|
|
1391
|
+
return /* @__PURE__ */ new Set([...dsmServerPurgedIds, ...dsmPendingPurged]);
|
|
1386
1392
|
}
|
|
1387
1393
|
function dsmMarkPurged(ids) {
|
|
1388
|
-
|
|
1389
|
-
ids.forEach((id) => s.add(String(id)));
|
|
1390
|
-
dsmSavePurged();
|
|
1394
|
+
ids.forEach((id) => dsmPendingPurged.add(String(id)));
|
|
1391
1395
|
if (dsmRepaintDots) dsmRepaintDots();
|
|
1392
1396
|
}
|
|
1397
|
+
var SIDEBAR_ADAPTER_VERSION = 3;
|
|
1398
|
+
var SIDEBAR_ADAPTER_MAX_MISSES = 200;
|
|
1399
|
+
function createSidebarAdapter() {
|
|
1400
|
+
let disabled = false;
|
|
1401
|
+
let misses = 0;
|
|
1402
|
+
const findFiber = (el) => {
|
|
1403
|
+
const k = Object.keys(el).find((kk) => kk.startsWith("__reactFiber") || kk.startsWith("__reactInternalInstance"));
|
|
1404
|
+
return k ? el[k] : null;
|
|
1405
|
+
};
|
|
1406
|
+
const recognize = (row) => {
|
|
1407
|
+
const out = { node: null, group: null };
|
|
1408
|
+
if (disabled || !row || typeof row !== "object") return out;
|
|
1409
|
+
let f = findFiber(row);
|
|
1410
|
+
let guard = 0;
|
|
1411
|
+
while (f && guard++ < 300) {
|
|
1412
|
+
const props = f.memoizedProps;
|
|
1413
|
+
if (props && props.node && typeof props.node === "object") out.node = props.node;
|
|
1414
|
+
if (props && props.group && typeof props.group === "object") out.group = props.group;
|
|
1415
|
+
f = f.return;
|
|
1416
|
+
}
|
|
1417
|
+
if (!out.node && !out.group) {
|
|
1418
|
+
if (++misses >= SIDEBAR_ADAPTER_MAX_MISSES) {
|
|
1419
|
+
disabled = true;
|
|
1420
|
+
try {
|
|
1421
|
+
console.warn("[dsh-sessions-manager] \u4FA7\u680F\u6CE8\u5165 adapter v" + SIDEBAR_ADAPTER_VERSION + " \u5DF2\u505C\u7528\uFF1A\u672A\u8BC6\u522B\u5230\u5DF2\u77E5\u7684 DSH \u4FA7\u680F\u8282\u70B9\u7ED3\u6784\uFF08\u4E0A\u6E38\u53EF\u80FD\u5DF2\u6539\u7248\uFF09");
|
|
1422
|
+
} catch (e) {
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
} else if (misses > 0) {
|
|
1426
|
+
misses = 0;
|
|
1427
|
+
}
|
|
1428
|
+
return out;
|
|
1429
|
+
};
|
|
1430
|
+
return {
|
|
1431
|
+
version: SIDEBAR_ADAPTER_VERSION,
|
|
1432
|
+
get disabled() {
|
|
1433
|
+
return disabled;
|
|
1434
|
+
},
|
|
1435
|
+
findFiber,
|
|
1436
|
+
recognize
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
var sidebarAdapter = createSidebarAdapter();
|
|
1393
1440
|
function installSidebarSessionMenuAug() {
|
|
1394
1441
|
if (typeof document === "undefined") return;
|
|
1395
1442
|
const AUG = "data-dsm-aug";
|
|
1396
1443
|
let styleInjected = false;
|
|
1397
1444
|
let activeSubClose = null;
|
|
1398
1445
|
let hoverTimer = null;
|
|
1399
|
-
const findFiber = (el) => {
|
|
1400
|
-
const k = Object.keys(el).find((kk) => kk.startsWith("__reactFiber") || kk.startsWith("__reactInternalInstance"));
|
|
1401
|
-
return k ? el[k] : null;
|
|
1402
|
-
};
|
|
1403
1446
|
const sessionInfoFromMenu = (menuEl) => {
|
|
1404
|
-
let f = findFiber(menuEl);
|
|
1447
|
+
let f = sidebarAdapter.findFiber(menuEl);
|
|
1405
1448
|
let guard = 0;
|
|
1406
1449
|
while (f && guard++ < 300) {
|
|
1407
1450
|
const p = f.memoizedProps;
|
|
@@ -1584,7 +1627,7 @@ function installSidebarSessionMenuAug() {
|
|
|
1584
1627
|
const move = mk("\u79FB\u52A8\u4F1A\u8BDD", ICON_MOVE, false);
|
|
1585
1628
|
const del = mk("\u5220\u9664\u4F1A\u8BDD", ICON_DEL, true);
|
|
1586
1629
|
const mark = mk(dsmLoadManual().has(info.id) ? "\u6807\u8BB0\u5DF2\u8BFB" : "\u6807\u8BB0\u672A\u8BFB", ICON_UNREAD, false);
|
|
1587
|
-
const moveCapability = dsmActionCapability("move");
|
|
1630
|
+
const moveCapability = dsmActionCapability("relocateSession") || dsmActionCapability("move");
|
|
1588
1631
|
if (!moveCapability || !moveCapability.available) {
|
|
1589
1632
|
move.btn.disabled = true;
|
|
1590
1633
|
move.btn.title = moveCapability && moveCapability.reason || "\u6B63\u5728\u68C0\u67E5\u5F53\u524D\u7248\u672C\u7684\u79FB\u52A8\u80FD\u529B";
|
|
@@ -1623,6 +1666,7 @@ function installSidebarSessionMenuAug() {
|
|
|
1623
1666
|
};
|
|
1624
1667
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
1625
1668
|
const obs = new MutationObserver(() => {
|
|
1669
|
+
if (sidebarAdapter.disabled) return;
|
|
1626
1670
|
const menus = document.querySelectorAll('body > [role="menu"]');
|
|
1627
1671
|
menus.forEach((menuEl) => {
|
|
1628
1672
|
if (seen.has(menuEl)) return;
|
|
@@ -1646,21 +1690,7 @@ function installSidebarStatusDots() {
|
|
|
1646
1690
|
s.textContent = SIDEBAR_AUG_CSS;
|
|
1647
1691
|
document.head.appendChild(s);
|
|
1648
1692
|
}
|
|
1649
|
-
const
|
|
1650
|
-
const k = Object.keys(el).find((kk) => kk.startsWith("__reactFiber") || kk.startsWith("__reactInternalInstance"));
|
|
1651
|
-
return k ? el[k] : null;
|
|
1652
|
-
};
|
|
1653
|
-
const fiberProp = (el, pred) => {
|
|
1654
|
-
let f = findFiber(el);
|
|
1655
|
-
let g = 0;
|
|
1656
|
-
while (f && g++ < 300) {
|
|
1657
|
-
const p = f.memoizedProps;
|
|
1658
|
-
if (p && pred(p)) return pred(p);
|
|
1659
|
-
f = f.return;
|
|
1660
|
-
}
|
|
1661
|
-
return null;
|
|
1662
|
-
};
|
|
1663
|
-
const rowNode = (row) => fiberProp(row, (p) => p.node && typeof p.node.id === "string" && p.node.id ? p.node : null);
|
|
1693
|
+
const rowNode = (row) => sidebarAdapter.recognize(row).node;
|
|
1664
1694
|
const rowId = (row) => {
|
|
1665
1695
|
const node = rowNode(row);
|
|
1666
1696
|
return node ? node.id : null;
|
|
@@ -1675,7 +1705,7 @@ function installSidebarStatusDots() {
|
|
|
1675
1705
|
// 蓝 手动标记未读
|
|
1676
1706
|
running: "#EAB308",
|
|
1677
1707
|
// 黄 工作中 (DSH running)
|
|
1678
|
-
feedback: "var(--dsw-alias-state-
|
|
1708
|
+
feedback: "var(--dsw-alias-state-warn-primary, #F59E0B)",
|
|
1679
1709
|
// 琥珀 需用户反馈 (DSH warning)
|
|
1680
1710
|
done: "var(--dsw-alias-state-success-primary)",
|
|
1681
1711
|
// 绿 完成后未读 (DSH done)
|
|
@@ -1683,7 +1713,9 @@ function installSidebarStatusDots() {
|
|
|
1683
1713
|
// 红 出错/需关注 (DSH error)
|
|
1684
1714
|
};
|
|
1685
1715
|
const manualUnread = dsmLoadManual();
|
|
1716
|
+
const titleInitializedRows = /* @__PURE__ */ new WeakSet();
|
|
1686
1717
|
const paint = () => {
|
|
1718
|
+
if (sidebarAdapter.disabled) return;
|
|
1687
1719
|
const activeId = activeRowId();
|
|
1688
1720
|
if (activeId !== curActive && activeId && manualUnread.has(activeId)) {
|
|
1689
1721
|
manualUnread.delete(activeId);
|
|
@@ -1703,12 +1735,20 @@ function installSidebarStatusDots() {
|
|
|
1703
1735
|
}
|
|
1704
1736
|
if (row.style.display === "none") row.style.display = "";
|
|
1705
1737
|
const authoritativeTitle = dsmAuthoritativeTitles.get(id);
|
|
1706
|
-
if (
|
|
1738
|
+
if (!titleInitializedRows.has(row)) {
|
|
1707
1739
|
const node = rowNode(row) || {};
|
|
1708
1740
|
const expected = new Set([node.title, node.displayTitle, node.name].filter((value) => typeof value === "string"));
|
|
1709
1741
|
const spans = [...row.children].filter((el) => el.tagName === "SPAN" && !el.querySelector("[data-state]") && (el.textContent || "").trim());
|
|
1710
1742
|
const titleEl = spans.find((el) => expected.has((el.textContent || "").trim())) || spans[0];
|
|
1711
|
-
if (titleEl
|
|
1743
|
+
if (titleEl) {
|
|
1744
|
+
const correction = authoritativeTitleForFirstPaint({
|
|
1745
|
+
firstPaint: true,
|
|
1746
|
+
rendered: titleEl.textContent || "",
|
|
1747
|
+
authoritative: authoritativeTitle || ""
|
|
1748
|
+
});
|
|
1749
|
+
if (correction) titleEl.textContent = correction;
|
|
1750
|
+
titleInitializedRows.add(row);
|
|
1751
|
+
}
|
|
1712
1752
|
}
|
|
1713
1753
|
const sd = row.querySelector("[data-state]");
|
|
1714
1754
|
if (sd) sd.style.display = "none";
|
|
@@ -1738,10 +1778,6 @@ function installSidebarStatusDots() {
|
|
|
1738
1778
|
});
|
|
1739
1779
|
};
|
|
1740
1780
|
dsmRepaintDots = paint;
|
|
1741
|
-
const tick = () => {
|
|
1742
|
-
if (++dsmTrashTick % 8 === 0) dsmLoadTrashIds();
|
|
1743
|
-
paint();
|
|
1744
|
-
};
|
|
1745
1781
|
let raf = 0;
|
|
1746
1782
|
const schedulePaint = () => {
|
|
1747
1783
|
if (raf) return;
|
|
@@ -1750,38 +1786,38 @@ function installSidebarStatusDots() {
|
|
|
1750
1786
|
paint();
|
|
1751
1787
|
});
|
|
1752
1788
|
};
|
|
1789
|
+
const FALLBACK_TICK_MS = 4e3;
|
|
1790
|
+
const tick = () => {
|
|
1791
|
+
if (typeof document !== "undefined" && document.hidden) return;
|
|
1792
|
+
if (++dsmTrashTick % 8 === 0) dsmLoadTrashIds();
|
|
1793
|
+
paint();
|
|
1794
|
+
};
|
|
1753
1795
|
tick();
|
|
1754
1796
|
dsmLoadTrashIds();
|
|
1755
1797
|
paint();
|
|
1756
1798
|
const obs = new MutationObserver(schedulePaint);
|
|
1757
|
-
obs.observe(document.body, {
|
|
1758
|
-
|
|
1799
|
+
obs.observe(document.body, {
|
|
1800
|
+
childList: true,
|
|
1801
|
+
subtree: true,
|
|
1802
|
+
attributes: true,
|
|
1803
|
+
attributeFilter: ["data-state", "aria-selected", "class", "style"],
|
|
1804
|
+
characterData: true
|
|
1805
|
+
});
|
|
1806
|
+
setInterval(tick, FALLBACK_TICK_MS);
|
|
1759
1807
|
}
|
|
1760
1808
|
function installSidebarWorkspaceDrag() {
|
|
1761
1809
|
if (typeof document === "undefined") return;
|
|
1762
1810
|
let sessions = /* @__PURE__ */ new Map();
|
|
1763
1811
|
let dragging = null;
|
|
1764
1812
|
let moving = false;
|
|
1765
|
-
const
|
|
1766
|
-
const
|
|
1767
|
-
return
|
|
1768
|
-
};
|
|
1769
|
-
const fiberNodes = (el) => {
|
|
1770
|
-
const out = [];
|
|
1771
|
-
let fiber = findFiber(el);
|
|
1772
|
-
let guard = 0;
|
|
1773
|
-
while (fiber && guard++ < 300) {
|
|
1774
|
-
const props = fiber.memoizedProps;
|
|
1775
|
-
if (props && props.node && typeof props.node === "object") out.push(props.node);
|
|
1776
|
-
if (props && props.group && typeof props.group === "object") out.push(props.group);
|
|
1777
|
-
fiber = fiber.return;
|
|
1778
|
-
}
|
|
1779
|
-
return out;
|
|
1813
|
+
const sessionForRow = (row) => {
|
|
1814
|
+
const { node, group } = sidebarAdapter.recognize(row);
|
|
1815
|
+
return sessionForNodes(group ? [node, group].filter(Boolean) : [node], sessions);
|
|
1780
1816
|
};
|
|
1781
|
-
const sessionForRow = (row) => sessionForNodes(fiberNodes(row), sessions);
|
|
1782
1817
|
const workspaceForRow = (row) => {
|
|
1783
1818
|
if (sessionForRow(row)) return null;
|
|
1784
|
-
|
|
1819
|
+
const { group } = sidebarAdapter.recognize(row);
|
|
1820
|
+
return group ? workspaceForNodes([group]) : null;
|
|
1785
1821
|
};
|
|
1786
1822
|
const eventRow = (event) => {
|
|
1787
1823
|
for (const item of event.composedPath ? event.composedPath() : []) {
|
|
@@ -1801,6 +1837,7 @@ function installSidebarWorkspaceDrag() {
|
|
|
1801
1837
|
document.querySelectorAll(".dsm-drag-source,.dsm-drop-target").forEach((el) => el.classList.remove("dsm-drag-source", "dsm-drop-target"));
|
|
1802
1838
|
};
|
|
1803
1839
|
const decorateRows = () => {
|
|
1840
|
+
if (sidebarAdapter.disabled) return;
|
|
1804
1841
|
document.querySelectorAll('[role="treeitem"]').forEach((row) => {
|
|
1805
1842
|
const session = sessionForRow(row);
|
|
1806
1843
|
const ws = workspaceForRow(row);
|
|
@@ -1825,7 +1862,8 @@ function installSidebarWorkspaceDrag() {
|
|
|
1825
1862
|
maybeRefresh();
|
|
1826
1863
|
}, true);
|
|
1827
1864
|
document.addEventListener("dragstart", (event) => {
|
|
1828
|
-
|
|
1865
|
+
if (sidebarAdapter.disabled) return;
|
|
1866
|
+
const moveCapability = dsmActionCapability("relocateSession") || dsmActionCapability("move");
|
|
1829
1867
|
if (!moveCapability || !moveCapability.available) return;
|
|
1830
1868
|
const row = eventRow(event);
|
|
1831
1869
|
const item = row && sessionForRow(row);
|