@hyzyn/dsh-tty 0.18.2 → 0.18.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.en.md +46 -13
- package/README.md +37 -11
- package/client.js +43 -36
- package/lib/index.js +6 -6
- package/lib/index.js.map +1 -1
- package/lib/probe.js +1 -1
- package/lib/probe.js.map +1 -1
- package/lib/ssh.js +1 -1
- package/lib/ssh.js.map +1 -1
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -31,7 +31,10 @@ After installing, restart `dsh web`; a “Terminal” entry appears in the sideb
|
|
|
31
31
|
a tab; **double-clicking a tab renames it** (the name is persisted with the tab and survives a reconnect);
|
|
32
32
|
each tab is an independent session (local PTY or SSH channel);
|
|
33
33
|
- **The working directory follows the current DSH session**: new tabs open in the current session’s
|
|
34
|
-
working directory (the host `cwd` configuration is the fallback)
|
|
34
|
+
working directory (the host `cwd` configuration is the fallback). Since 0.1.6 the session list
|
|
35
|
+
snapshot no longer carries `current` (view selection moved to the workspace domain), so the client
|
|
36
|
+
reads `retainedBy.mainView > 0` to find the current session — trusting only the legacy field leaves
|
|
37
|
+
the cwd empty and new tabs fall back to the host’s start directory;
|
|
35
38
|
- Supports TUIs such as vim / htop / less (TERM is injected as `xterm-256color`);
|
|
36
39
|
- Panel size changes are resized automatically (xterm fit → native PTY resize);
|
|
37
40
|
- **Ctrl+F searches inside the terminal** (Enter next / Shift+Enter previous / Esc closes only the search
|
|
@@ -241,17 +244,25 @@ slot (`ssh2`’s sftp subsystem, host half in `src/sftp.ts`):
|
|
|
241
244
|
|
|
242
245
|

|
|
243
246
|
|
|
244
|
-
- **Placement (0.16.0)**: when the terminal panel is open and
|
|
247
|
+
- **Placement (0.16.0)**: when the terminal panel is open and **this tab’s** mount slot is free, the File Browser
|
|
245
248
|
**docks below the terminal** — the path bar / list / transfer progress take the full width (a file list is
|
|
246
249
|
a wide table, so full width below beats a narrow column on the right, and the terminal also keeps its width
|
|
247
250
|
without wrapping; a dual pane side by side needs that width even more), and the terminal stays visible and
|
|
248
251
|
usable. The height is draggable and can collapse into a single title bar (collapsing neither closes the
|
|
249
|
-
panel nor interrupts browsing); when
|
|
250
|
-
|
|
251
|
-
|
|
252
|
+
panel nor interrupts browsing); when another panel (such as the containers panel) already holds the slot on
|
|
253
|
+
the same tab, or the panel is not open, it falls back to the original centered dialog, **without pushing
|
|
254
|
+
anyone else’s panel out**. The title / collapse / ✕ come from tty’s mount slot;
|
|
255
|
+
- **Follows the tab (0.18.4)**: the File Browser talks to the host of the tab that opened it, so it belongs to
|
|
256
|
+
that tab: switching away hides it (in-flight transfers keep running and the scene is restored when you come
|
|
257
|
+
back), and closing the tab tears it down. **Every entry follows the same rule** — the connection bar’s
|
|
258
|
+
“SFTP”, the 📂 on a connection-book entry and “File Browser” in the SSH dialog / settings card all become
|
|
259
|
+
owned by whatever tab was active when they were opened; only “panel open with no tabs at all” counts as
|
|
260
|
+
owned by no tab (always visible). Before this, the pane stayed put across a tab switch — title reading host
|
|
261
|
+
A while the active tab was B, worst case uploading to the wrong host;
|
|
252
262
|
- **Entries**: ① connection-book entries in the tab bar “+” menu carry a 📂 (open the File Browser for that
|
|
253
263
|
entry); ② fill in host/authentication in the SSH connection dialog and click “File Browser” (you can
|
|
254
|
-
browse without saving to the connection book);
|
|
264
|
+
browse without saving to the connection book); ③ the “SFTP” button in an SSH tab’s connection bar (owned by
|
|
265
|
+
that tab, see the previous bullet);
|
|
255
266
|
- **Operations**: directory browsing (Enter in the path box to jump, `.. (parent directory)`, a single click
|
|
256
267
|
on a file downloads it), **upload** (multi-select files, XHR streaming + percentage progress; since 0.8.0
|
|
257
268
|
**drag & drop** is supported — files and folders can be dropped straight into the dialog, folders are
|
|
@@ -589,8 +600,8 @@ ctx.inject(['ttyTerminal'], (c) => {
|
|
|
589
600
|
|
|
590
601
|
### In-panel mount slots (client service `ttyPanel`, 0.16.0)
|
|
591
602
|
|
|
592
|
-
> tty’s own SFTP File Browser goes through this channel too (0.16.0): when the mount slot is free it docks
|
|
593
|
-
>
|
|
603
|
+
> tty’s own SFTP File Browser goes through this channel too (0.16.0): when the mount slot is free it docks
|
|
604
|
+
> below, and when another panel on the same tab has taken it, it falls back to the dialog.
|
|
594
605
|
|
|
595
606
|
`mount` solves “a consumer gives the host and tty puts a terminal in it”; `ttyPanel` is its **mirror** — tty
|
|
596
607
|
gives consumers a slot inside the terminal panel to mount their own UI into. A typical case: dsh-docker opens
|
|
@@ -598,6 +609,16 @@ gives consumers a slot inside the terminal panel to mount their own UI into. A t
|
|
|
598
609
|
which stays visible, clickable and typable instead of being covered by a full-screen modal (which was exactly
|
|
599
610
|
the pain before 0.15).
|
|
600
611
|
|
|
612
|
+
A mount slot is **connection-scoped**: its credentials / target come from the terminal tab that opened it.
|
|
613
|
+
So since 0.18.4 every pane records its **owner tab** (`options.ownerSid`, defaulting to the active tab at
|
|
614
|
+
mount time): switching to another tab **hides** the pane (`data-dock-hidden`; its DOM and your rendered tree
|
|
615
|
+
survive, in-flight transfers keep running) and switching back restores the scene; closing the owner tab tears
|
|
616
|
+
the pane down. Without this, the pane stayed put across a tab switch — its title read `SFTP · cdc-test-161`
|
|
617
|
+
while the active tab was `192.168.80.248`, leaving **another host**’s file listing on screen (worst case:
|
|
618
|
+
uploading to the wrong host). Passing `ownerSid: null` means “owned by no tab” (always visible); that is what
|
|
619
|
+
happens automatically when the panel is open with no tabs at all, and a consumer can use it to declare “this
|
|
620
|
+
pane has nothing to do with tabs, do not hide it on a switch”.
|
|
621
|
+
|
|
601
622
|
```js
|
|
602
623
|
ctx.inject(['ttyPanel'], (c) => {
|
|
603
624
|
// Use your own modal when the panel is not open (or tty < 0.16)
|
|
@@ -608,6 +629,7 @@ ctx.inject(['ttyPanel'], (c) => {
|
|
|
608
629
|
side: 'right', // 'right' (default, vertical list / list+detail) | 'bottom' (wide horizontal table)
|
|
609
630
|
size: 520, // initial size in px: right = width (default 460), bottom = height (default 320)
|
|
610
631
|
min: 360, // minimum size in px (optional, default 280 / 160)
|
|
632
|
+
ownerSid: tab.sid, // owner tab (optional): omitted = current active tab, null = owned by no tab
|
|
611
633
|
onClose: () => { /* called when tty tears the panel down: unmount your React root here */ },
|
|
612
634
|
})
|
|
613
635
|
createRoot(pane.element).render(<MyPanel />)
|
|
@@ -618,7 +640,7 @@ ctx.inject(['ttyPanel'], (c) => {
|
|
|
618
640
|
| Member | Description |
|
|
619
641
|
| --- | --- |
|
|
620
642
|
| `isOpen()` | Whether the terminal panel is currently open (minimized does not count). Consumers use it to decide “mount in here” or “use my own modal” |
|
|
621
|
-
| `mountPane(options)` | Mounts a slot on the right / at the bottom of the panel and returns a handle; **only one at a time**, and a later `mountPane` first tears the previous one down (calling its `onClose`). With `side:'bottom'` it spans the full width and is sized by height (drag the top edge), collapsing into a title bar |
|
|
643
|
+
| `mountPane(options)` | Mounts a slot on the right / at the bottom of the panel and returns a handle; **only one at a time**, and a later `mountPane` first tears the previous one down (calling its `onClose`). With `side:'bottom'` it spans the full width and is sized by height (drag the top edge), collapsing into a title bar. `ownerSid` records the owner tab (default: the active one); panes owned by another tab are **hidden** on tab switch (DOM and your React tree survive, restored when you switch back — see “connection-scoped” above) |
|
|
622
644
|
| `handle.element` | The host the consumer renders into (flex column, already `overflow:hidden`, filling the body area) |
|
|
623
645
|
| `handle.setTitle(text)` / `setHint(text)` | Change the title / the grey hint |
|
|
624
646
|
| `handle.expand() / collapse() / toggle() / isCollapsed()` | Collapse into a 32px strip (vertical title + expand/close buttons), and the terminal immediately gets its width back |
|
|
@@ -698,7 +720,7 @@ client-side changes.
|
|
|
698
720
|
|
|
699
721
|
Styles should not be changed by “refresh the page and take a look”: the script loads `client.js` into a pure
|
|
700
722
|
static fixture page (`scripts/preview/harness.html` + a fake DSH host from `mock-host.js`: module
|
|
701
|
-
loader / fetch / WebSocket) and renders
|
|
723
|
+
loader / fetch / WebSocket) and renders 29 UI states one by one with headless Chrome, screenshotting them to
|
|
702
724
|
`packages/tty/.preview/shots/`:
|
|
703
725
|
|
|
704
726
|
```bash
|
|
@@ -708,9 +730,20 @@ node scripts/preview.mjs --list # list scenes
|
|
|
708
730
|
node scripts/preview.mjs --theme=light # light theme
|
|
709
731
|
```
|
|
710
732
|
|
|
711
|
-
Coverage: local terminal / multi-tab + SSH connection bar / the “+” menu / SSH dialog (new, edit)/
|
|
712
|
-
settings card / SFTP (single pane, dual pane
|
|
713
|
-
|
|
733
|
+
Coverage: local terminal / multi-tab + SSH connection bar / the “+” menu / SSH dialog (new, edit, probe)/
|
|
734
|
+
settings card (also side by side with docker)/ SFTP (single pane, dual pane, placement fallback)/
|
|
735
|
+
**mount slot follows the tab** (`dock-pane-tab`, the 0.18.4 regression)/ minimized badge / exit and error
|
|
736
|
+
overlays / tunnel popover / search box / toast / embedded terminals (alone and alongside the panel)/
|
|
737
|
+
docker panel and “containers → terminal drawer”.
|
|
738
|
+
|
|
739
|
+
A scene may attach a **function-shaped** assertion to `window.__previewAssert` (returning `null` means pass,
|
|
740
|
+
a string / array means fail); the script runs it and folds the result into `✓/✗`. An assertion that only
|
|
741
|
+
lives in the fixture, seen by nobody unless someone pulls `diag` by hand, is a regression that is not really
|
|
742
|
+
pinned — which is exactly what bit the 0.18.4 “panel does not follow the tab” fix: the assertion was written
|
|
743
|
+
already, but because it was mixed into a `diag` object containing a function, the whole evaluation failed
|
|
744
|
+
silently and everything reported ✓.
|
|
745
|
+
|
|
746
|
+
The fixture also renders the `--dsw-*` skin variables together with the real UI, so it can
|
|
714
747
|
verify things like “is there still a white panel after switching light/dark themes”. The output directory
|
|
715
748
|
`.preview/` is gitignored.
|
|
716
749
|
|
package/README.md
CHANGED
|
@@ -32,7 +32,9 @@ dsh plugin --profile web add link:$(pwd)/packages/tty # 仓库开发调试
|
|
|
32
32
|
**双击标签可重命名**(重命名随标签持久化,断线恢复后保留);每个标签
|
|
33
33
|
独立会话(本地 PTY 或 SSH channel);
|
|
34
34
|
- **工作目录跟随当前 DSH 会话**:新标签默认在当前会话工作目录打开
|
|
35
|
-
(宿主配置 `cwd`
|
|
35
|
+
(宿主配置 `cwd` 作兜底)。0.1.6 起会话列表快照不再带 `current`(视图选中项搬到了
|
|
36
|
+
workspace 域),客户端改看 `retainedBy.mainView > 0` 认当前会话——只认老字段会让
|
|
37
|
+
cwd 恒为空、新标签回落宿主启动目录;
|
|
36
38
|
- 支持 vim / htop / less 等 TUI(TERM 已注入为 `xterm-256color`);
|
|
37
39
|
- 面板大小变化自动 resize(xterm fit → PTY 原生 resize);
|
|
38
40
|
- **Ctrl+F 终端内搜索**(Enter 下一个 / Shift+Enter 上一个 / Esc 只关搜索框),
|
|
@@ -220,14 +222,20 @@ subsystem,宿主半体 `src/sftp.ts`):
|
|
|
220
222
|
|
|
221
223
|

|
|
222
224
|
|
|
223
|
-
- **落点(0.16.0
|
|
225
|
+
- **落点(0.16.0)**:终端面板开着且**本标签**的挂载位空着时,文件浏览**挂在终端下方**——路径栏 /
|
|
224
226
|
列表 / 传输进度占满整宽(文件列表是横向宽表,下方全宽比右侧窄栏好用,终端也保住
|
|
225
227
|
宽度不会折行;双栏两栏并排更需要这个宽度),终端继续可见可用。高度可拖、可折叠成
|
|
226
|
-
|
|
227
|
-
|
|
228
|
+
一条标题栏(折叠不关面板、不中断浏览);同一标签的挂载位已被别的面板(如容器面板)
|
|
229
|
+
占用、或面板没开时,退回原来的居中对话框,**不会把别人的面板挤掉**。标题 / 折叠 / ✕ 由
|
|
228
230
|
tty 的挂载位提供;
|
|
231
|
+
- **跟着标签切(0.18.4)**:文件浏览连的是**打开它的那个标签**那台主机,所以它归属该标签:
|
|
232
|
+
切到别的标签时整块收起(在途传输照跑,切回来还在原地),标签关掉时一并收掉。**所有入口
|
|
233
|
+
用同一条规则**——连接栏「SFTP」、连接簿条目的 📂、SSH 对话框 / 设置卡片的「文件浏览」都
|
|
234
|
+
归属打开那一刻的活动标签;只有「面板开着但一个标签都没有」才算不隶属任何标签(永远可见)。
|
|
235
|
+
此前不认归属,切完标签面板还停在原处——标题写着 A 主机、底下活动标签是 B,最坏会往错主机上传;
|
|
229
236
|
- **入口**:① 标签栏「+」菜单的连接簿条目带 📂(按该条目打开文件浏览);
|
|
230
237
|
② SSH 连接对话框填好主机/认证后点「文件浏览」(不落连接簿也能浏览);
|
|
238
|
+
③ SSH 标签的连接栏「SFTP」按钮(归属该标签,见上一条);
|
|
231
239
|
- **操作**:目录浏览(路径框回车跳转、`..(上级目录)`、单击文件即下载)、
|
|
232
240
|
**上传**(多选文件,XHR 流式 + 进度百分比;0.8.0 起支持**拖拽**——文件与
|
|
233
241
|
文件夹直接拖入对话框,文件夹经 `webkitGetAsEntry` 递归展开逐个上传,目录
|
|
@@ -548,14 +556,23 @@ ctx.inject(['ttyTerminal'], (c) => {
|
|
|
548
556
|
|
|
549
557
|
### 面板内挂载位(客户端服务 `ttyPanel`,0.16.0)
|
|
550
558
|
|
|
551
|
-
> tty 自己的 SFTP 文件浏览也走这条通道(0.16.0
|
|
552
|
-
>
|
|
559
|
+
> tty 自己的 SFTP 文件浏览也走这条通道(0.16.0):挂载位空着时挂在下方,
|
|
560
|
+
> 被同标签的别的面板占用时退回对话框。
|
|
553
561
|
|
|
554
562
|
`mount` 解决的是「消费方给宿主,tty 往里塞终端」;`ttyPanel` 是它的**镜像**——tty 在
|
|
555
563
|
终端面板里给消费方一块位置,让消费方把自己的界面挂进来。典型场景:dsh-docker 从 SSH
|
|
556
564
|
连接栏点「容器」,容器面板挂在终端**右侧**,终端继续可见、可点、可输入,而不是被整屏
|
|
557
565
|
弹窗盖住(0.15 之前那正是用户的痛点)。
|
|
558
566
|
|
|
567
|
+
挂载位是**连接级**的:它的凭证 / 目标来自**打开它的那个终端标签**。所以 0.18.4 起每块
|
|
568
|
+
pane 记一个「归属标签」(`options.ownerSid`,默认 = 挂载那一刻的活动标签):切到别的
|
|
569
|
+
标签时整块**收起**(`data-dock-hidden`,DOM 与你 render 的树都保活,在途传输继续跑),
|
|
570
|
+
切回来恢复现场;归属标签被关掉时 pane 一并收掉。不这么做的话,切完标签面板还停在原处
|
|
571
|
+
——标题写着 `SFTP · cdc-test-161`、底下活动标签却是 `192.168.80.248`,面板里躺着**另一台
|
|
572
|
+
主机**的文件列表(最坏的情况是往错主机上传)。显式传 `ownerSid: null` 表示「不隶属任何
|
|
573
|
+
标签」(一直可见)——面板开着但一个标签都没有时自动落进这一档;消费方也可以用它声明
|
|
574
|
+
「这块与标签无关,别跟着切」。
|
|
575
|
+
|
|
559
576
|
```js
|
|
560
577
|
ctx.inject(['ttyPanel'], (c) => {
|
|
561
578
|
// 面板没开(或 tty < 0.16)时走自己的弹窗
|
|
@@ -566,6 +583,7 @@ ctx.inject(['ttyPanel'], (c) => {
|
|
|
566
583
|
side: 'right', // 'right'(默认,竖向列表 / 列表+详情)| 'bottom'(横向宽表)
|
|
567
584
|
size: 520, // 初始尺寸 px:right = 宽度(默认 460)、bottom = 高度(默认 320)
|
|
568
585
|
min: 360, // 最小尺寸 px(可选,默认 280 / 160)
|
|
586
|
+
ownerSid: tab.sid, // 归属标签(可选):省略 = 当前活动标签,null = 不隶属任何标签
|
|
569
587
|
onClose: () => { /* tty 收掉面板时回调:在这里 unmount 自己的 React root */ },
|
|
570
588
|
})
|
|
571
589
|
createRoot(pane.element).render(<MyPanel />)
|
|
@@ -576,7 +594,7 @@ ctx.inject(['ttyPanel'], (c) => {
|
|
|
576
594
|
| 成员 | 说明 |
|
|
577
595
|
| --- | --- |
|
|
578
596
|
| `isOpen()` | 终端面板是否正开着(最小化不算)。消费方据此决定「挂进来」还是「走自己的弹窗」 |
|
|
579
|
-
| `mountPane(options)` | 在面板右侧 / 下方挂一块位置并返回 handle;**同时只挂一个**,后来的 `mountPane` 会先收掉前一个(并回调它的 `onClose`)。`side:'bottom'`
|
|
597
|
+
| `mountPane(options)` | 在面板右侧 / 下方挂一块位置并返回 handle;**同时只挂一个**,后来的 `mountPane` 会先收掉前一个(并回调它的 `onClose`)。`side:'bottom'` 时占满宽度、由高度定尺寸(拖上边缘),折叠成一条标题栏。`ownerSid` 记归属标签(默认当前活动标签),切换标签时归属别的标签的 pane 会被**收起**(DOM 与你的 React 树保活,切回来恢复;见上文「连接级」那段) |
|
|
580
598
|
| `handle.element` | 消费方 render 的宿主(flex 纵向、已 `overflow:hidden`,撑满正文区) |
|
|
581
599
|
| `handle.setTitle(text)` / `setHint(text)` | 改标题 / 灰字 |
|
|
582
600
|
| `handle.expand() / collapse() / toggle() / isCollapsed()` | 折叠成 32px 窄条(竖排标题 + 展开/关闭按钮),终端立刻拿回宽度 |
|
|
@@ -665,7 +683,7 @@ esbuild 的 text loader 内联进 `client.js`)。构建产物 `client.js`(
|
|
|
665
683
|
|
|
666
684
|
改样式不该靠「刷新页面看一眼」:脚本把 `client.js` 装进一个纯静态夹具页
|
|
667
685
|
(`scripts/preview/harness.html` + `mock-host.js` 伪造的 DSH 宿主:module
|
|
668
|
-
loader / fetch / WebSocket),用 headless Chrome 把
|
|
686
|
+
loader / fetch / WebSocket),用 headless Chrome 把 29 个界面状态逐个渲染并
|
|
669
687
|
截图到 `packages/tty/.preview/shots/`:
|
|
670
688
|
|
|
671
689
|
```bash
|
|
@@ -675,9 +693,17 @@ node scripts/preview.mjs --list # 列出场景
|
|
|
675
693
|
node scripts/preview.mjs --theme=light # 浅色主题
|
|
676
694
|
```
|
|
677
695
|
|
|
678
|
-
覆盖:本地终端 / 多标签 + SSH 连接栏 / 「+」菜单 / SSH
|
|
679
|
-
|
|
680
|
-
|
|
696
|
+
覆盖:本地终端 / 多标签 + SSH 连接栏 / 「+」菜单 / SSH 对话框(新建、编辑、试连)/
|
|
697
|
+
设置卡片(含与 docker 并排对照)/ SFTP(单窗体、双栏、落点回退)/
|
|
698
|
+
**挂载位跟着标签切**(`dock-pane-tab`,0.18.4 的回归)/ 最小化徽标 / 退出与错误遮罩 /
|
|
699
|
+
隧道弹层 / 搜索框 / toast / 嵌入式终端(单独与面板共存)/ docker 面板与「容器 → 终端抽屉」。
|
|
700
|
+
|
|
701
|
+
场景可以把**函数形态**的断言挂到 `window.__previewAssert`(返回 `null` = 通过,返回
|
|
702
|
+
字符串 / 数组 = 失败),脚本会跑掉它并把结果计入 `✓/✗`。断言光挂在夹具里、只有手工
|
|
703
|
+
取 `diag` 时才有人看,回归等于没钉——0.18.4 修「面板不跟标签切」时就吃到这个亏:断言
|
|
704
|
+
早写好了,但因为混在带函数的 `diag` 对象里,整条求值静默失败,一路都是 ✓。
|
|
705
|
+
|
|
706
|
+
夹具还会把 `--dsw-*` 皮肤变量与真实界面一并渲染,因此能验
|
|
681
707
|
「明暗主题切换后是否还有白色面板」这类问题。产物目录 `.preview/` 已 gitignore。
|
|
682
708
|
|
|
683
709
|
> 夹具里的 `ctx.inject` 与真实 cordis 同语义:**依赖里有一个服务不存在就不触发回调**,
|