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.
Files changed (3) hide show
  1. package/README.md +23 -10
  2. package/lib/client.js +58 -47
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # dsh-split-view
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
- @weiaisuyansjd/dsh-split-view
18
+ dsh-multipane
15
19
  ```
16
20
 
17
21
  或者命令行:
18
22
 
19
23
  ```sh
20
- npx @deepseek-ai/dsh plugin --profile desktop add @weiaisuyansjd/dsh-split-view
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
- ### ⚠️ 不要和 npm 上的 `dsh-split-view` 混装
37
+ 仓库名仍是 `dsh-split-view`,但包名取自 `package.json`,装下来同样叫 `dsh-multipane`。
38
+
39
+ ### ⚠️ 别和作者 luckybilly 的 `dsh-split-view` 同时启用
34
40
 
35
- npm 上有一个**同名不同源**的插件 `dsh-split-view`(作者 luckybilly),它也是给 DSH 做多会话分屏的,但实现路线完全不同(每栏跑一个 iframe 客户端)。两者装名会撞车:
41
+ npm 上另有一个插件 `dsh-split-view`(作者 luckybilly),也是给 DSH 做多会话分屏,实现路线完全不同(每栏跑一个 iframe 客户端)。
36
42
 
37
43
  | 来源 | 安装名 |
38
44
  | --- | --- |
39
- | 本插件(npm) | `@weiaisuyansjd/dsh-split-view` |
40
- | 本插件(GitHub 直装) | `dsh-split-view`(取自仓库 package.json) |
45
+ | 本插件(npm / GitHub 装法都一样) | `dsh-multipane` |
41
46
  | 另一个作者的插件 | `dsh-split-view` |
42
47
 
43
- 也就是说 **GitHub 直装的版本和 luckybilly 那个包同名**。两个都装会互相覆盖,表现出来是「插件时有时无、改了没反应」。装之前先确认机器上没有另一个 `dsh-split-view`。
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
- # npm 装的
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-multipane",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Hermes-style tabbed workspace for the DSH main area: drag sessions in, split into up to four columns, close tabs.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",