dsh-terminal-panel 1.0.0
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/LICENSE +21 -0
- package/README.md +117 -0
- package/README_EN.md +117 -0
- package/assets/banner.svg +32 -0
- package/client.js +1315 -0
- package/cordis.patch.yml +3 -0
- package/host.js +92 -0
- package/impl.js +484 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BaiZhi967
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/banner.svg" width="900" alt="dsh-plugin-terminal-panel banner"/>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# dsh-plugin-terminal-panel
|
|
6
|
+
|
|
7
|
+
[English](README_EN.md) | 中文
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/dsh-terminal-panel)
|
|
10
|
+
[](LICENSE)
|
|
11
|
+
[](https://github.com/BaiZhi967/dsh-plugin-terminal-panel)
|
|
12
|
+
|
|
13
|
+
**在 DSH 网页里开终端**:侧栏点一下,主区域就多出一个和「对话」同级的终端面板,里面是**真 PTY**(node-pty / ConPTY),不是假的输出框,也不弹系统窗口。
|
|
14
|
+
|
|
15
|
+
- 终端跑在 **DSH 宿主进程**里,输出通过 SSE 实时推给页面;
|
|
16
|
+
- 关掉页面、刷新页面都不掉线,PTY 存活并**回放最近 256 KB 输出**,回来画面还在;
|
|
17
|
+
- 面板宽度变化会自动 `resize` PTY,shell 的重绘跟着走。
|
|
18
|
+
|
|
19
|
+
## ✨ 功能
|
|
20
|
+
|
|
21
|
+
| 能力 | 说明 |
|
|
22
|
+
|---|---|
|
|
23
|
+
| **侧栏入口** | 左栏面板图标区多一个终端图标,与「插件」图标同级;点它主区域切到终端面板,机制与「对话」面板完全一致 |
|
|
24
|
+
| **活跃计数** | 图标右上角实时显示运行中的终端数量,为 0 时自动隐藏;悬停提示同步带上数量 |
|
|
25
|
+
| **多标签** | 一个面板里开多个终端(上限 12 个),每个一个标签,带状态点和关闭按钮 |
|
|
26
|
+
| **重命名** | 双击标签名,或点标签上的 ✎;`Enter` 提交、`Esc` 取消、失焦自动提交(中文 / emoji 正常) |
|
|
27
|
+
| **自绘终端** | 内置一个小型 VT 引擎:ANSI 颜色(16 / 256 / 真彩)、光标定位、清行清屏、备用屏幕、滚动区域、宽字符 |
|
|
28
|
+
| **跟随主题** | 亮 / 暗两套 ANSI 调色板:亮色主题下 `37`/`93` 映射为深灰 / 深黄,白底上也能看清(PowerShell 的输入高亮不再"隐形") |
|
|
29
|
+
| **文本选择** | 标签栏 ⧉ 一键切到「选择文本」模式(暂停键盘输入),可直接框选复制输出 |
|
|
30
|
+
| **中文输入** | 独立输入通道,IME 组合输入、中文、emoji 都能正确送进 PTY |
|
|
31
|
+
| **热重载** | 改 `impl.js` 后 `POST /__reload` 即可生效,不用重启 DSH;客户端代码改动由 DSH 的模块热更新自动送达页面 |
|
|
32
|
+
|
|
33
|
+
## 📦 安装
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
dsh plugin --profile web add dsh-terminal-panel
|
|
37
|
+
dsh --profile web
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
装好后刷新一次页面(或直接看侧栏),左栏面板图标区就会出现终端图标。
|
|
41
|
+
|
|
42
|
+
> 若 `dsh plugin add` 拉不到包(刚发布、或公司/国内镜像尚未同步),用下面的源码方式安装。
|
|
43
|
+
|
|
44
|
+
从源码安装(本地开发、二次修改):
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
git clone https://github.com/BaiZhi967/dsh-plugin-terminal-panel.git
|
|
48
|
+
cd dsh-plugin-terminal-panel
|
|
49
|
+
|
|
50
|
+
# 在插件目录的「父目录」执行,dsh 会把相对路径锚定到调用目录
|
|
51
|
+
dsh plugin --profile web add ../dsh-plugin-terminal-panel
|
|
52
|
+
dsh --profile web
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
也可以直接写进 profile 的 `cordis.patch.yml`:
|
|
56
|
+
|
|
57
|
+
```yaml
|
|
58
|
+
- insert:
|
|
59
|
+
- id: terminal-panel
|
|
60
|
+
name: dsh-terminal-panel
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
> 环境要求:DSH `>= 0.1.6-alpha.2`、Node `^22.19.0 || >=24.0.0`;宿主进程需要有可用 PTY(本插件用 `subprocess.spawnTerminal`,即宿主自带的 node-pty)。
|
|
64
|
+
|
|
65
|
+
## 🚀 用法
|
|
66
|
+
|
|
67
|
+
1. 点左栏的**终端图标** → 主区域打开终端面板;
|
|
68
|
+
2. 点 **+ 新建终端** → 立即分配一个 PTY,shell 自动选 `pwsh` → `powershell` → 宿主默认 shell,工作目录默认取最新工作区;
|
|
69
|
+
3. 直接在面板里敲命令(点击面板即聚焦输入);
|
|
70
|
+
4. 双击标签名改名;✎ 也是改名;✕ 关闭(进程树会被终止);
|
|
71
|
+
5. 面板随窗口变化自动 `resize`。
|
|
72
|
+
|
|
73
|
+
## 🧱 结构
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
dsh-plugin-terminal-panel/
|
|
77
|
+
├── host.js # 宿主入口:只承载路由 + 热重载外壳
|
|
78
|
+
├── impl.js # 宿主实现:PTY 生命周期、SSE 推送、write/resize/close/rename
|
|
79
|
+
├── client.js # 客户端:终端面板、侧栏图标、VT 引擎、输入处理
|
|
80
|
+
├── cordis.patch.yml # bundle 补丁:向 profile 插入插件行
|
|
81
|
+
└── package.json # dsh.bundle.patch + dsh.client 声明
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
两侧如何协作:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
侧栏图标 / 终端面板 (client.js)
|
|
88
|
+
│ 同源 HTTP(回环地址)
|
|
89
|
+
▼
|
|
90
|
+
GET /system-terminals/api/stream?id=… ← SSE:先回放缓冲,再推实时输出
|
|
91
|
+
POST /system-terminals/api/{list,create,write,resize,close,rename,remove}
|
|
92
|
+
▼
|
|
93
|
+
impl.js ── subprocess.spawnTerminal() ──► 真 PTY(node-pty / ConPTY)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
设计上有意分成两个文件:**加载过的 ESM 模块会在宿主进程里被永久缓存**,所以 `host.js` 保持极简、把逻辑放进 `impl.js`,由 `host.js` 用带缓存参数的动态 `import()` 拉取 —— 这样替换 `impl.js` 后一次 `POST /__reload` 就能生效,无需重启 DSH。
|
|
97
|
+
|
|
98
|
+
### 本地 API
|
|
99
|
+
|
|
100
|
+
路由前缀 `/system-terminals/api`,仅监听 DSH 自己的回环地址,无需登录态(与生态内其他插件的私有路由一致):
|
|
101
|
+
|
|
102
|
+
| 方法 | 作用 |
|
|
103
|
+
|---|---|
|
|
104
|
+
| `GET /health` | 版本、平台、shell、PTY 可用性 |
|
|
105
|
+
| `GET /list` | 终端列表(id、标题、cwd、pid、行列、状态、是否可 resize) |
|
|
106
|
+
| `POST /create` | `{cwd?, cols?, rows?}` → 新建终端 |
|
|
107
|
+
| `POST /write` | `{id, data}` → 写入 PTY(原始字节,UTF-8) |
|
|
108
|
+
| `POST /resize` | `{id, cols, rows}` |
|
|
109
|
+
| `POST /close` | `{id}` → 终止进程 |
|
|
110
|
+
| `POST /rename` | `{id, title}` → 改名(去空白、上限 60 字) |
|
|
111
|
+
| `POST /remove` | `{id}` → 从列表移除已退出的终端 |
|
|
112
|
+
| `GET /stream?id=` | SSE:`history` / `data` / `status` / `exit` 事件,base64 负载 |
|
|
113
|
+
| `POST /__reload` | 开发用:重新加载 `impl.js` |
|
|
114
|
+
|
|
115
|
+
## 📄 License
|
|
116
|
+
|
|
117
|
+
[MIT](LICENSE) © 2026 BaiZhi967
|
package/README_EN.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/banner.svg" width="900" alt="dsh-plugin-terminal-panel banner"/>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# dsh-plugin-terminal-panel
|
|
6
|
+
|
|
7
|
+
English | [中文](README.md)
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/dsh-terminal-panel)
|
|
10
|
+
[](LICENSE)
|
|
11
|
+
[](https://github.com/BaiZhi967/dsh-plugin-terminal-panel)
|
|
12
|
+
|
|
13
|
+
**Terminals inside the DSH web UI.** Click the sidebar icon and a terminal panel opens in the main column, as a peer of the Conversation panel. It is a **real PTY** (node-pty / ConPTY) — not a fake output box, and never an OS window.
|
|
14
|
+
|
|
15
|
+
- Terminals live in the **DSH host process**; output is streamed to the page over SSE.
|
|
16
|
+
- Refreshing or closing the page does not kill them: the PTY survives and **replays the last 256 KB**, so the screen comes back.
|
|
17
|
+
- Resizing the panel resizes the PTY, so the shell redraws at the new width.
|
|
18
|
+
|
|
19
|
+
## ✨ Features
|
|
20
|
+
|
|
21
|
+
| Feature | Detail |
|
|
22
|
+
|---|---|
|
|
23
|
+
| **Sidebar entry** | One terminal icon in the sidebar's panel-icon row, next to the built-in Plugins icon. Clicking it switches the main column to the terminal panel — the same mechanism the Conversation panel uses |
|
|
24
|
+
| **Active count** | A live badge on the icon shows how many terminals are running (hidden at zero); the tooltip carries the count too |
|
|
25
|
+
| **Tabs** | Several terminals in one panel (up to 12), each with a status dot and a close button |
|
|
26
|
+
| **Rename** | Double-click a tab, or use its ✎ button; `Enter` commits, `Esc` cancels, blur commits. CJK and emoji round-trip correctly |
|
|
27
|
+
| **Own VT engine** | A small terminal renderer written for this plugin: ANSI colour (16 / 256 / truecolor), cursor addressing, erase, alternate screen, scroll regions, wide characters |
|
|
28
|
+
| **Theme aware** | Separate light and dark ANSI palettes: on a light theme `37` / `93` map to dark grey / dark yellow, so PowerShell's input highlighting stays readable |
|
|
29
|
+
| **Text selection** | The ⧉ button in the tab bar pauses keyboard input so you can select and copy output |
|
|
30
|
+
| **IME input** | A dedicated input channel: composed Chinese, CJK and emoji reach the PTY correctly |
|
|
31
|
+
| **Live reload** | Edit `impl.js` and `POST /__reload` — no DSH restart. Client-side edits are hot-updated by DSH's module table |
|
|
32
|
+
|
|
33
|
+
## 📦 Install
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
dsh plugin --profile web add dsh-terminal-panel
|
|
37
|
+
dsh --profile web
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Reload the page once and the terminal icon appears in the sidebar.
|
|
41
|
+
|
|
42
|
+
> If `dsh plugin add` cannot resolve the package yet (a fresh publish, or a mirror that has not synced), install it from source instead.
|
|
43
|
+
|
|
44
|
+
From source (local development, or your own fork):
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
git clone https://github.com/BaiZhi967/dsh-plugin-terminal-panel.git
|
|
48
|
+
cd dsh-plugin-terminal-panel
|
|
49
|
+
|
|
50
|
+
# run from the plugin's PARENT directory: a relative path is anchored there
|
|
51
|
+
dsh plugin --profile web add ../dsh-plugin-terminal-panel
|
|
52
|
+
dsh --profile web
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Or mount it by hand in the profile's `cordis.patch.yml`:
|
|
56
|
+
|
|
57
|
+
```yaml
|
|
58
|
+
- insert:
|
|
59
|
+
- id: terminal-panel
|
|
60
|
+
name: dsh-terminal-panel
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
> Requirements: DSH `>= 0.1.6-alpha.2`, Node `^22.19.0 || >=24.0.0`, and a host with a working PTY (this plugin uses `subprocess.spawnTerminal`, i.e. the bundled node-pty).
|
|
64
|
+
|
|
65
|
+
## 🚀 Usage
|
|
66
|
+
|
|
67
|
+
1. Click the **terminal icon** in the sidebar → the terminal panel opens in the main column.
|
|
68
|
+
2. Click **+ New Terminal** → a PTY is allocated immediately. The shell is picked as `pwsh` → `powershell` → the host default, and the working directory defaults to the newest workspace.
|
|
69
|
+
3. Type directly in the panel (clicking it focuses the input).
|
|
70
|
+
4. Double-click a tab (or use ✎) to rename; ✕ closes the terminal and terminates its process tree.
|
|
71
|
+
5. The panel resizes the PTY automatically as the window changes.
|
|
72
|
+
|
|
73
|
+
## 🧱 Layout
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
dsh-plugin-terminal-panel/
|
|
77
|
+
├── host.js # host entry: the route carrier + reload shell only
|
|
78
|
+
├── impl.js # host implementation: PTY lifecycle, SSE, write/resize/close/rename
|
|
79
|
+
├── client.js # client: terminal panel, sidebar icon, VT engine, input handling
|
|
80
|
+
├── cordis.patch.yml # bundle patch: inserts the plugin row into a profile
|
|
81
|
+
└── package.json # dsh.bundle.patch + dsh.client declarations
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
How the halves talk:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
sidebar icon / terminal panel (client.js)
|
|
88
|
+
│ same-origin HTTP on the loopback listener
|
|
89
|
+
▼
|
|
90
|
+
GET /system-terminals/api/stream?id=… ← SSE: replay buffer first, then live output
|
|
91
|
+
POST /system-terminals/api/{list,create,write,resize,close,rename,remove}
|
|
92
|
+
▼
|
|
93
|
+
impl.js ── subprocess.spawnTerminal() ──► real PTY (node-pty / ConPTY)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The two-file host split is deliberate: **a loaded ES module stays cached in the host process for its lifetime**, so `host.js` stays minimal and pulls the implementation in through a cache-busted dynamic `import()`. Replacing `impl.js` then takes effect with one `POST /__reload`, no DSH restart.
|
|
97
|
+
|
|
98
|
+
### Local API
|
|
99
|
+
|
|
100
|
+
Prefix `/system-terminals/api`, bound to DSH's own loopback listener, no session auth — the same shape other plugins in this ecosystem use for their private routes:
|
|
101
|
+
|
|
102
|
+
| Method | Purpose |
|
|
103
|
+
|---|---|
|
|
104
|
+
| `GET /health` | Version, platform, shell, PTY availability |
|
|
105
|
+
| `GET /list` | Terminals: id, title, cwd, pid, cols/rows, status, resizable |
|
|
106
|
+
| `POST /create` | `{cwd?, cols?, rows?}` → open a terminal |
|
|
107
|
+
| `POST /write` | `{id, data}` → write raw UTF-8 bytes to the PTY |
|
|
108
|
+
| `POST /resize` | `{id, cols, rows}` |
|
|
109
|
+
| `POST /close` | `{id}` → terminate the process |
|
|
110
|
+
| `POST /rename` | `{id, title}` → rename (whitespace collapsed, 60 chars max) |
|
|
111
|
+
| `POST /remove` | `{id}` → drop an exited terminal from the list |
|
|
112
|
+
| `GET /stream?id=` | SSE: `history` / `data` / `status` / `exit` events, base64 payloads |
|
|
113
|
+
| `POST /__reload` | Development: reload `impl.js` |
|
|
114
|
+
|
|
115
|
+
## 📄 License
|
|
116
|
+
|
|
117
|
+
[MIT](LICENSE) © 2026 BaiZhi967
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="900" height="220" viewBox="0 0 900 220" role="img" aria-label="dsh-plugin-terminal-panel">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
|
4
|
+
<stop offset="0" stop-color="#0b1220"/>
|
|
5
|
+
<stop offset="1" stop-color="#131f36"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
<rect width="900" height="220" rx="18" fill="url(#bg)"/>
|
|
9
|
+
|
|
10
|
+
<!-- left rail: the sidebar panel icon with its active-terminal badge -->
|
|
11
|
+
<rect x="26" y="30" width="52" height="160" rx="12" fill="#0e1a2e"/>
|
|
12
|
+
<rect x="38" y="52" width="28" height="24" rx="6" fill="none" stroke="#4d6bfe" stroke-width="2"/>
|
|
13
|
+
<path d="M45 60l5 4-5 4" fill="none" stroke="#4d6bfe" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
14
|
+
<path d="M54 70h7" stroke="#4d6bfe" stroke-width="2" stroke-linecap="round"/>
|
|
15
|
+
<circle cx="68" cy="50" r="9" fill="#4d6bfe"/>
|
|
16
|
+
<text x="68" y="54" font-family="Segoe UI,Helvetica,Arial,sans-serif" font-size="11" font-weight="700" fill="#ffffff" text-anchor="middle">2</text>
|
|
17
|
+
|
|
18
|
+
<!-- main column: the terminal panel -->
|
|
19
|
+
<rect x="94" y="30" width="780" height="160" rx="14" fill="#080f1c" stroke="#22304a"/>
|
|
20
|
+
<rect x="94.5" y="30.5" width="779" height="34" rx="13" fill="#101c30" stroke="#22304a"/>
|
|
21
|
+
<rect x="108" y="40" width="146" height="20" rx="6" fill="#17253d"/>
|
|
22
|
+
<circle cx="121" cy="50" r="4" fill="#16a34a"/>
|
|
23
|
+
<text x="131" y="54" font-family="Segoe UI,Helvetica,Arial,sans-serif" font-size="12" fill="#dbe6f5">Terminal 1</text>
|
|
24
|
+
<text x="266" y="54" font-family="Segoe UI,Helvetica,Arial,sans-serif" font-size="12" fill="#7d8fae">+ New Terminal</text>
|
|
25
|
+
|
|
26
|
+
<!-- screen -->
|
|
27
|
+
<text x="116" y="96" font-family="Cascadia Mono,Consolas,Menlo,monospace" font-size="13.5" fill="#dbe6f5">PS E:\DSH> npm run dev</text>
|
|
28
|
+
<text x="116" y="122" font-family="Cascadia Mono,Consolas,Menlo,monospace" font-size="13.5" fill="#b5ba00">> vite --port 5173</text>
|
|
29
|
+
<text x="116" y="148" font-family="Cascadia Mono,Consolas,Menlo,monospace" font-size="13.5" fill="#7d8fae"> ready in 412 ms</text>
|
|
30
|
+
<rect x="116" y="158" width="8" height="15" fill="#4d6bfe" opacity="0.85"/>
|
|
31
|
+
<text x="136" y="171" font-family="Cascadia Mono,Consolas,Menlo,monospace" font-size="13.5" fill="#dbe6f5">PS E:\DSH></text>
|
|
32
|
+
</svg>
|