dsh-multipane 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -10
- package/lib/client.js +58 -47
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# dsh-
|
|
1
|
+
# dsh-multipane(多栏视图)
|
|
2
2
|
|
|
3
3
|
Hermes 式多栏工作区,接管 DSH 主区:每栏一条自己的标签条、拖拽分栏、可关闭,最多四栏。
|
|
4
4
|
|
|
@@ -6,18 +6,22 @@ Hermes 式多栏工作区,接管 DSH 主区:每栏一条自己的标签条
|
|
|
6
6
|
|
|
7
7
|
需要 DSH `0.2.1-rc.5` 及以上。装完**重启 DSH** 才会生效。
|
|
8
8
|
|
|
9
|
+
> **名字必须三处一致**:`package.json` 的 `name`、`cordis.patch.yml` 的 `name`、`lib/client.js` 里的模块 `id`
|
|
10
|
+
> 三者要完全相同,否则 DSH 会静默不加载(控制台报 `import failed` / `duplicate factory registration`)。
|
|
11
|
+
> 所以本项目**不做发布期改名**,本地开发名和 npm 包名就是同一个 `dsh-multipane`。
|
|
12
|
+
|
|
9
13
|
### 方式一(推荐):npm
|
|
10
14
|
|
|
11
15
|
在 DSH「设置 → 添加插件」里粘贴包名:
|
|
12
16
|
|
|
13
17
|
```
|
|
14
|
-
|
|
18
|
+
dsh-multipane
|
|
15
19
|
```
|
|
16
20
|
|
|
17
21
|
或者命令行:
|
|
18
22
|
|
|
19
23
|
```sh
|
|
20
|
-
npx @deepseek-ai/dsh plugin --profile desktop add
|
|
24
|
+
npx @deepseek-ai/dsh plugin --profile desktop add dsh-multipane
|
|
21
25
|
```
|
|
22
26
|
|
|
23
27
|
npm 走公共 registry,有国内镜像可用,比 GitHub 稳。
|
|
@@ -30,24 +34,31 @@ npm 走公共 registry,有国内镜像可用,比 GitHub 稳。
|
|
|
30
34
|
npx @deepseek-ai/dsh plugin --profile desktop add github:weiaisuyan/dsh-split-view
|
|
31
35
|
```
|
|
32
36
|
|
|
33
|
-
|
|
37
|
+
仓库名仍是 `dsh-split-view`,但包名取自 `package.json`,装下来同样叫 `dsh-multipane`。
|
|
38
|
+
|
|
39
|
+
### ⚠️ 别和作者 luckybilly 的 `dsh-split-view` 同时启用
|
|
34
40
|
|
|
35
|
-
npm
|
|
41
|
+
npm 上另有一个插件 `dsh-split-view`(作者 luckybilly),也是给 DSH 做多会话分屏,实现路线完全不同(每栏跑一个 iframe 客户端)。
|
|
36
42
|
|
|
37
43
|
| 来源 | 安装名 |
|
|
38
44
|
| --- | --- |
|
|
39
|
-
| 本插件(npm
|
|
40
|
-
| 本插件(GitHub 直装) | `dsh-split-view`(取自仓库 package.json) |
|
|
45
|
+
| 本插件(npm / GitHub 装法都一样) | `dsh-multipane` |
|
|
41
46
|
| 另一个作者的插件 | `dsh-split-view` |
|
|
42
47
|
|
|
43
|
-
|
|
48
|
+
两者**名字不同、不会互相覆盖**,但都在抢主区,同时启用会互相打架(栏结构错乱、设置页串台)。只留一个,装前先确认:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
npx @deepseek-ai/dsh plugin list | grep split-view
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
如果你装过本插件的旧版本(`@weiaisuyansjd/dsh-split-view`,或 GitHub 直装的 `dsh-split-view`),先按下面的命令卸掉再装新版。
|
|
44
55
|
|
|
45
56
|
### 卸载
|
|
46
57
|
|
|
47
58
|
```sh
|
|
48
|
-
|
|
59
|
+
npx @deepseek-ai/dsh plugin --profile desktop remove dsh-multipane
|
|
60
|
+
# 旧版本(如果装过)
|
|
49
61
|
npx @deepseek-ai/dsh plugin --profile desktop remove @weiaisuyansjd/dsh-split-view
|
|
50
|
-
# GitHub 装的
|
|
51
62
|
npx @deepseek-ai/dsh plugin --profile desktop remove dsh-split-view
|
|
52
63
|
```
|
|
53
64
|
|
|
@@ -58,6 +69,8 @@ npx @deepseek-ai/dsh plugin --profile desktop remove dsh-split-view
|
|
|
58
69
|
- **npm 装**:重跑一次 `add`(或先 `remove` 再 `add`)
|
|
59
70
|
- **GitHub 装**:必须 `remove` 后重新 `add`,否则不会拉到新提交
|
|
60
71
|
|
|
72
|
+
升级/换装都不会丢布局:栏、标签、栏宽存在 `localStorage`(键名固定为 `dsh-split-view:*`,与包名无关)。
|
|
73
|
+
|
|
61
74
|
## 交互
|
|
62
75
|
|
|
63
76
|
| 动作 | 结果 |
|
package/lib/client.js
CHANGED
|
@@ -2602,6 +2602,14 @@ window.__ModuleLoader__.load({
|
|
|
2602
2602
|
actions.setHover(null);
|
|
2603
2603
|
},
|
|
2604
2604
|
onDrop: (event) => {
|
|
2605
|
+
/* 外部文件拖拽(我们自己的标签/会话都不在拖):**原样放行**。
|
|
2606
|
+
宿主的 @deepseek-ai/dsh-client-ui-attachment 在 document 上挂了
|
|
2607
|
+
dragenter/drop 监听,靠 dragDepth 计数器控制「拖到此处添加」遮罩;
|
|
2608
|
+
drop 是外部拖拽唯一能把它归零的时刻(window 的 dragend 只对页面内
|
|
2609
|
+
发起的拖拽触发)。这里一旦 stopPropagation,宿主收不到 drop →
|
|
2610
|
+
计数器永久卡住 → 遮罩常驻且界面没有任何退出入口。
|
|
2611
|
+
标签/会话的正常拖拽照旧走下面的 preventDefault + stopPropagation。 */
|
|
2612
|
+
if (drag.sessionId === null && drag.tab === null) return;
|
|
2605
2613
|
event.preventDefault();
|
|
2606
2614
|
event.stopPropagation();
|
|
2607
2615
|
if (drag.tab !== null && drag.tab.paneId === pane.id) {
|
|
@@ -2869,6 +2877,9 @@ window.__ModuleLoader__.load({
|
|
|
2869
2877
|
actions.setHover(null);
|
|
2870
2878
|
},
|
|
2871
2879
|
onDrop: (event) => {
|
|
2880
|
+
/* 同 PaneHeader:外部文件拖拽一律放行,让宿主的 document 级 drop
|
|
2881
|
+
收到并重置 dragDepth(否则「拖到此处添加」遮罩永久常驻)。 */
|
|
2882
|
+
if (drag.sessionId === null && drag.tab === null) return;
|
|
2872
2883
|
event.preventDefault();
|
|
2873
2884
|
event.stopPropagation();
|
|
2874
2885
|
if (event.target instanceof Element && event.target.closest(".dsv-paneheader") !== null) return;
|
|
@@ -6135,53 +6146,53 @@ window.__ModuleLoader__.load({
|
|
|
6135
6146
|
看到的是同一套项:置顶 / 重命名 / 分叉 / 归档 / 复制会话 ID…(全部由宿主注册表提供,
|
|
6136
6147
|
官方以后新增项自动出现,我们只加「锁定标签」这一条)。
|
|
6137
6148
|
resolve 在 React 之外被调用 → 只能通过 LIVE_STATE / LIVE_APPLY 读状态、写状态。 */
|
|
6138
|
-
ctx.effect(() => {
|
|
6139
|
-
let sub = null;
|
|
6140
|
-
try {
|
|
6141
|
-
/* Cordis 运行时依赖声明:回调里的 inner ctx 才有权访问 contextMenus。
|
|
6142
|
-
顺手把服务引用存 MENUS,供标签右键直接 open 宿主菜单。 */
|
|
6143
|
-
sub = ctx.inject(["contextMenus"], (inner) => {
|
|
6144
|
-
MENUS = inner.contextMenus;
|
|
6145
|
-
try {
|
|
6146
|
-
inner.contextMenus.register({
|
|
6147
|
-
id: "session.dsv-tab-lock",
|
|
6148
|
-
target: "session",
|
|
6149
|
-
order: 900,
|
|
6150
|
-
resolve: (value) => {
|
|
6151
|
-
const sessionId = value !== null && value !== void 0 ? value.sessionId : void 0;
|
|
6152
|
-
const state = LIVE_STATE;
|
|
6153
|
-
if (typeof sessionId !== "string" || state === null) return [];
|
|
6154
|
-
const hit = findSession(state, sessionId);
|
|
6155
|
-
if (hit === void 0) return [];
|
|
6156
|
-
const locked = hit.tab.locked === true;
|
|
6157
|
-
return [
|
|
6158
|
-
{
|
|
6159
|
-
id: "dsv-tab-lock",
|
|
6160
|
-
label: locked ? "解锁标签" : "锁定标签",
|
|
6161
|
-
separatorBefore: true,
|
|
6162
|
-
run: () => {
|
|
6163
|
-
if (typeof LIVE_APPLY !== "function") return;
|
|
6164
|
-
LIVE_APPLY((prev) => toggleLockTab(prev, hit.pane.id, hit.tab.id));
|
|
6165
|
-
}
|
|
6166
|
-
}
|
|
6167
|
-
];
|
|
6168
|
-
}
|
|
6169
|
-
});
|
|
6170
|
-
} catch (error) {
|
|
6171
|
-
console.warn("[split-view] 注册会话菜单项失败:", error);
|
|
6172
|
-
}
|
|
6173
|
-
});
|
|
6174
|
-
} catch (error) {
|
|
6175
|
-
console.warn("[split-view] contextMenus 注入失败:", error);
|
|
6176
|
-
}
|
|
6177
|
-
return () => {
|
|
6178
|
-
MENUS = null;
|
|
6179
|
-
try {
|
|
6180
|
-
if (sub !== null && typeof sub.dispose === "function") sub.dispose();
|
|
6181
|
-
} catch {
|
|
6182
|
-
/* 忽略 */
|
|
6183
|
-
}
|
|
6184
|
-
};
|
|
6149
|
+
ctx.effect(() => {
|
|
6150
|
+
let sub = null;
|
|
6151
|
+
try {
|
|
6152
|
+
/* Cordis 运行时依赖声明:回调里的 inner ctx 才有权访问 contextMenus。
|
|
6153
|
+
顺手把服务引用存 MENUS,供标签右键直接 open 宿主菜单。 */
|
|
6154
|
+
sub = ctx.inject(["contextMenus"], (inner) => {
|
|
6155
|
+
MENUS = inner.contextMenus;
|
|
6156
|
+
try {
|
|
6157
|
+
inner.contextMenus.register({
|
|
6158
|
+
id: "session.dsv-tab-lock",
|
|
6159
|
+
target: "session",
|
|
6160
|
+
order: 900,
|
|
6161
|
+
resolve: (value) => {
|
|
6162
|
+
const sessionId = value !== null && value !== void 0 ? value.sessionId : void 0;
|
|
6163
|
+
const state = LIVE_STATE;
|
|
6164
|
+
if (typeof sessionId !== "string" || state === null) return [];
|
|
6165
|
+
const hit = findSession(state, sessionId);
|
|
6166
|
+
if (hit === void 0) return [];
|
|
6167
|
+
const locked = hit.tab.locked === true;
|
|
6168
|
+
return [
|
|
6169
|
+
{
|
|
6170
|
+
id: "dsv-tab-lock",
|
|
6171
|
+
label: locked ? "解锁标签" : "锁定标签",
|
|
6172
|
+
separatorBefore: true,
|
|
6173
|
+
run: () => {
|
|
6174
|
+
if (typeof LIVE_APPLY !== "function") return;
|
|
6175
|
+
LIVE_APPLY((prev) => toggleLockTab(prev, hit.pane.id, hit.tab.id));
|
|
6176
|
+
}
|
|
6177
|
+
}
|
|
6178
|
+
];
|
|
6179
|
+
}
|
|
6180
|
+
});
|
|
6181
|
+
} catch (error) {
|
|
6182
|
+
console.warn("[split-view] 注册会话菜单项失败:", error);
|
|
6183
|
+
}
|
|
6184
|
+
});
|
|
6185
|
+
} catch (error) {
|
|
6186
|
+
console.warn("[split-view] contextMenus 注入失败:", error);
|
|
6187
|
+
}
|
|
6188
|
+
return () => {
|
|
6189
|
+
MENUS = null;
|
|
6190
|
+
try {
|
|
6191
|
+
if (sub !== null && typeof sub.dispose === "function") sub.dispose();
|
|
6192
|
+
} catch {
|
|
6193
|
+
/* 忽略 */
|
|
6194
|
+
}
|
|
6195
|
+
};
|
|
6185
6196
|
}, "dsh-split-view: tab lock session menu");
|
|
6186
6197
|
SERVICES.sessions = service("sessions");
|
|
6187
6198
|
SERVICES.layout = service("layout");
|
package/package.json
CHANGED