dsh-plugin-tether 0.1.23 → 0.1.25

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/CHANGELOG.md CHANGED
@@ -2,6 +2,50 @@
2
2
 
3
3
  本文件记录面向用户的变化。每个版本的这一节会原样作为该版本 Release 的说明。
4
4
 
5
+ ## 0.1.25
6
+
7
+ ### 修复
8
+
9
+ - **窄屏下侧栏卡在 56px:标签文字渲染出来却被裁掉,侧栏开关也找不到**([#22](https://github.com/zexadev/dsh-tether/issues/22))。0.1.24 的窄屏样式用「主框架下有没有拖拽把手」判断侧栏是否展开,这个判据要靠 `:has()`。有的厂商 WebView 会把带 `:has()` 的规则整条丢掉(报告机型:华为 Mate 50 / HarmonyOS 4.2),于是「展开时侧栏 300px」那条不生效,而 dsh 自己处在展开态——标签照常渲染,被 56px 的盒子裁成每行一两个字;开关只在收起态渲染,页面上也找不到,界面就卡在这个混态。现在改读 dsh 写在主框架上的 `data-sidebar-collapsed`(收起为 `"true"`,展开时属性不存在),不再用 `:has()`;插件声明支持的五段 dsh 范围各取一版查过,这个属性每版都有。拖拽把手那条规则一并删掉,手机上它本来也拖不动。实测 480×1056:收起侧栏 57px、无遮罩、「主机」标签隐藏;按开关 301px、遮罩出现、标签显示;点内容区收回。
10
+ - **dsh 桌面端里「我的电脑」按钮点了没有任何反应**([#23](https://github.com/zexadev/dsh-tether/issues/23))。按钮的文案和行为原本按 `window.parent !== window` 判断「是不是在 tether 手机 App 里」,而 dsh 桌面端的插件界面同样跑在宿主的内嵌 frame 里,于是桌面端被当成手机:按钮显示「我的电脑」,点击只向父窗口发一条 `dsh-tether:open-hosts` 消息——桌面宿主没有这条消息的监听者,按钮因此是死的,也没有别的出路。现在改成 App 建窗口时往所有 frame 注入 `window.__DSH_TETHER_APP__`,谁是 App 谁挂标记;拿不到标记一律按浏览器处理,当场出示配对串。桌面端因此也能拿到配对串(换手机、加设备时用),不必再手动开浏览器访问本机地址。这条要配新版 App 才完整:老 App 没有这个标记,装上新插件后按钮会显示「连接手机」并出示配对串。
11
+ - **窄屏下附件缩略图被挤变形**(修 #22 时发现)。窄屏那几条规则用 `[class*="_frame"]` 选主框架,但 dsh 的会话区、附件缩略图、子代理、文档预览的局部类名也带 `_frame`,而且都长在主框架里面。附件缩略图自己是个单格网格,被 `grid-template-columns: 56px minmax(0,1fr) 0px` 改成三列之后,图片挤进 56px 的第一列;侧栏遮罩那条同理会糊在每个嵌套元素上。这几条现在限定成「带 `_frame` 且不在任何 `_frame` 里面」。
12
+
13
+ ### 变更
14
+
15
+ - 声明支持 dsh `0.2.1-rc.5` 与 `0.2.2-alpha.2`。dsh 从 0.1.7 起会拒装声明范围没覆盖自己的插件,所以 0.2.2-alpha 线上原本装不上。两个版本都实跑验证过:布局包的类名与 `data-sidebar-collapsed` 和 0.2.1-rc.3 逐字相同,抽屉、设置弹窗、侧栏按钮行为一致。APK 内置的 dsh 仍是 `0.2.1-rc.2`,本次不动。
16
+
17
+ ### English
18
+
19
+ On a narrow screen the sidebar could get stuck at 56px with its labels rendered but clipped and no sidebar toggle in sight ([#22](https://github.com/zexadev/dsh-tether/issues/22)). The narrow-screen styling in 0.1.24 decided whether the sidebar was expanded by looking for the drag handle under the frame, which needs `:has()`. Some vendor WebViews drop every rule containing `:has()` (reported on a Huawei Mate 50 running HarmonyOS 4.2), so the "sidebar is 300px when expanded" rule never applied while dsh itself was in the expanded state: the labels rendered and were clipped to one or two characters by the 56px box, and the toggle is only rendered while collapsed, so there was no way out of that mixed state. The expanded state is now read from `data-sidebar-collapsed`, which dsh writes on the frame itself (`"true"` when collapsed, absent when expanded), with no `:has()` involved; the attribute was checked in one version from each of the five dsh ranges the plugin declares. The drag-handle rule is gone as well — it was never draggable on a phone. Measured at 480×1056: collapsed sidebar 57px, no veil, hosts label hidden; after the toggle 301px with the veil and the label; tapping the content column collapses it again.
20
+
21
+ In the dsh desktop app the "My computers" button did nothing when clicked ([#23](https://github.com/zexadev/dsh-tether/issues/23)). The label and the behaviour were chosen by `window.parent !== window`, meant to detect "running inside the tether phone app", but the plugin UI in the dsh desktop shell also lives in an embedded frame, so the desktop was treated as a phone: the button read "My computers" and a click only posted `dsh-tether:open-hosts` to the parent window — which the desktop shell does not listen for, leaving the button dead with nothing else to fall back on. The app now injects `window.__DSH_TETHER_APP__` into every frame when it creates the window, so whoever is the app says so; without that marker the plugin treats the page as a browser and shows the pairing string. Desktop users can therefore get the pairing string (for a new phone or an extra device) without opening a browser by hand. This one needs the new app build to be complete: an older app does not set the marker, so with the new plugin its button reads "Connect a phone" and shows the pairing string.
22
+
23
+ Attachment thumbnails were squeezed out of shape on narrow screens (found while fixing #22). Those narrow-screen rules selected the frame with `[class*="_frame"]`, but the conversation area, attachment thumbnails, subagent and document-preview components all carry local class names containing `_frame` and all live inside the frame. An attachment thumbnail is a single-cell grid, so `grid-template-columns: 56px minmax(0,1fr) 0px` turned it into three columns and pushed the image into the 56px first column, and the sidebar veil would likewise be painted over every nested element. Those rules are now restricted to a `_frame` that is not inside another `_frame`.
24
+
25
+ Support is now declared for dsh `0.2.1-rc.5` and `0.2.2-alpha.2`. Since 0.1.7 dsh refuses to install a plugin whose declared range does not cover it, so the 0.2.2-alpha line could not install this plugin at all. Both versions were verified by running them: the layout package class names and `data-sidebar-collapsed` are identical to 0.2.1-rc.3, and the drawer, settings dialog and sidebar button all behave the same. The dsh bundled inside the APK stays at `0.2.1-rc.2`.
26
+
27
+ ## 0.1.24
28
+
29
+ ### 修复
30
+
31
+ - **老 WebView 上 dsh 界面整个起不来:停在「正在加载工作区…」,或点「选择工作区」时报 `AbortSignal.any is not a function`**([#20](https://github.com/zexadev/dsh-tether/issues/20))。dsh 0.2.1-rc 的前端用到 `Promise.withResolvers`(Chrome 119 起有)和 `AbortSignal.any`(Chrome 116 起有),而华为等厂商随系统发的 WebView 还停在 Chromium 114,两个都没有。前者让启动脚本当场抛错,后者让读取远端数据那一步次次失败,界面于是无限转圈重连。现在 App 在建窗口时就补上这两个 API,并且补进页面里内嵌的 dsh 界面那一层——它和外壳不同源,只补外壳够不着。补之前先探测,WebView 自带的就不动,新机器上等于没有这段代码。真机对照:华为 TET-AN50 / Android 12 / WebView 114.0.5.302,修前必现,修后 dsh 正常启动、目录列表正常、脚本报错为零;本机模式与终端不受影响。
32
+ - **放一段时间后界面卡在「重新连接中」,等多久都不恢复,只能强停 App 重开**([#19](https://github.com/zexadev/dsh-tether/issues/19))。四处缺陷叠在一起:
33
+ - 链路死了也不报断开。读写两侧用「都结束才算完」来等,而写侧平时一直阻塞在待发队列上、只有读侧会报错,这个条件于是永远不成立。实测杀掉电脑侧 sidecar 七分钟,手机顶栏一直显示「已连接」,一条断开事件都没发出来。改成任一侧结束即收尾,现在约半分钟内就会报「连接已断开」并给出「重新连接」。
34
+ - 本地代理的监听口既不随连接关闭,换连接时也不让出。按主机派生的那个固定端口因此被废弃的监听长期占着,新连接只能退回随机端口。更要命的是废监听照样接受 TCP 连接、但每个请求都被丢掉——页面看着是连上的,实际一个请求也出不去,这正是「显示正常却一直重连」的来由。
35
+ - 端口一换,对 dsh 来说就是换了个来源,按来源存的状态(当前会话、界面偏好)全是空的,于是每次重连都像第一次打开。
36
+ - 旧连接收尾时不看自己还是不是当前那条,一律广播断开并清空发送端。刚建好的新连接会被它一起判死:顶栏在连上的同一毫秒跳回「未连接」,审批决定也发不出去。
37
+
38
+ 真机验证:杀掉电脑侧进程后 37 秒内报断开;重连回到同一个固定端口;连按三次重连,手机上始终只有那一个监听口,也不再出现多余的断开事件。
39
+ - **会话里工具调用那几行被切掉大半,只剩文字最上面一道横杠**([#18](https://github.com/zexadev/dsh-tether/issues/18))。这是 0.1.0 起给窄屏注入的样式写宽了:那条规则本来只给设置弹窗的「说明 + 控件」行用,判据却只看类名里带不带 `row`,把会话里的工具调用行一并命中了。那种行高度写死、超出即裁,被强行允许换行之后图标占满第一行、标题掉到第二行,露在外面的只剩第二行最上面几个像素。文件路径、命令、报错这类长标题全中招,短标题排得下反而没事,所以一问一答看不出来、任务一跑长就满屏横杠。判据改成「这一行里确实有说明列」:设置弹窗照旧,会话里的行恢复单行加省略号。顺带说明,这跟手机型号和 WebView 版本都没关系——把桌面浏览器窗口收到同样宽度,一样会出现。
40
+
41
+ ### English
42
+
43
+ The whole dsh interface failed to start on older WebViews: it sat at "Loading workspace…", or choosing a workspace reported `AbortSignal.any is not a function` ([#20](https://github.com/zexadev/dsh-tether/issues/20)). The dsh 0.2.1-rc frontend uses `Promise.withResolvers` (Chrome 119 and later) and `AbortSignal.any` (Chrome 116 and later), and the system WebView shipped by Huawei and others is still on Chromium 114, which has neither. The first made the boot script throw outright; the second made every read of remote data fail, so the client looped on "connection lost, retry". The app now supplies both APIs when it creates the window, including inside the embedded dsh interface — that frame has a different origin, so patching the shell alone never reaches it. Each API is feature-detected first and left alone when the WebView already has it, so on a current device this code does nothing. Checked on a real device, Huawei TET-AN50 / Android 12 / WebView 114.0.5.302: the failure was reproducible before and gone after, with dsh starting, the directory list rendering, and no script errors. Local mode and the terminal are unaffected.
44
+
45
+ After sitting idle the interface got stuck on "Reconnecting", never recovered however long you waited, and only a force stop brought it back ([#19](https://github.com/zexadev/dsh-tether/issues/19)). Four defects stacked up. First, a dead link was never reported: the connection waited for both its reader and its writer to finish, and the writer normally blocks forever on the outgoing queue while only the reader errors out, so that condition never came true — with the computer-side sidecar killed, the phone kept showing "Connected" for seven minutes without emitting a single disconnect. Either side finishing now ends the connection, so the disconnect and the Reconnect button appear within about half a minute. Second, the local proxy listener was neither closed when its connection ended nor released when a new connection replaced it, so the port derived from the computer stayed occupied by a dead listener and every later connection fell back to a random one — and the dead listener still accepted TCP while dropping every request, which is exactly how the page can look connected while nothing gets through. Third, once the port changes dsh sees a different origin, so everything stored per origin (the current session, interface preferences) comes up empty and every reconnect looks like a first run. Fourth, a connection tearing down did not check whether it was still the current one, and broadcast a disconnect and cleared the send channel regardless, killing a connection that had just been established: the status flipped back to "Not connected" in the same millisecond it connected, and approval decisions could no longer be sent. Verified on a real device: the disconnect now arrives 37 seconds after the computer-side process is killed, a reconnect returns to the same derived port, and three reconnects in a row leave exactly one listener on the phone with no spurious disconnects.
46
+
47
+ Tool-call rows in a conversation were sliced down to a sliver of their top edge ([#18](https://github.com/zexadev/dsh-tether/issues/18)). The narrow-screen styling injected since 0.1.0 was written too broadly: the rule was meant for the "description plus control" rows of the settings dialog, but it matched on nothing more than the word `row` in a class name, so it also caught the tool-call rows in the conversation. Those rows have a fixed height and clip their overflow, so once wrapping was forced on them the icon filled the first line, the title dropped to a second line, and only a few pixels of that second line stayed visible. Long titles — file paths, commands, error messages — all hit this, while short ones still fitted on one line, which is why a single question and answer looked fine and a longer task filled the screen with slivers. The rule now requires the row to actually contain a description column: the settings dialog is unchanged, and conversation rows are back to one line with an ellipsis. For the record, this had nothing to do with the phone model or the WebView version — narrowing a desktop browser window to the same width reproduces it.
48
+
5
49
  ## 0.1.23
6
50
 
7
51
  ### 修复
package/README.md CHANGED
@@ -17,7 +17,7 @@
17
17
  <a href="../../releases"><img src="https://img.shields.io/github/downloads/zexadev/dsh-tether/total?style=flat&label=downloads&color=4D6BFE" alt="Downloads"></a>
18
18
  <a href="../../stargazers"><img src="https://img.shields.io/github/stars/zexadev/dsh-tether?style=flat&label=%E2%98%85&color=08C" alt="Stars"></a>
19
19
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-2EA44F?style=flat" alt="MIT"></a>
20
- <img src="https://img.shields.io/badge/dsh-0.1.0--rc.7%20%7C%20rc.8%20%7C%200.1.2%20%7C%200.1.5%20%7C%200.1.6%20%7C%200.1.7%20%7C%200.2.0%20%7C%200.2.1-4D6BFE?style=flat" alt="dsh 0.1.0-rc.7 | rc.8 | 0.1.2 | 0.1.5 | 0.1.6 | 0.1.7 | 0.2.0 | 0.2.1">
20
+ <img src="https://img.shields.io/badge/dsh-0.1.0--rc.7%20%7C%20rc.8%20%7C%200.1.2%20%7C%200.1.5%20%7C%200.1.6%20%7C%200.1.7%20%7C%200.2.0%20%7C%200.2.1%20%7C%200.2.2-4D6BFE?style=flat" alt="dsh 0.1.0-rc.7 | rc.8 | 0.1.2 | 0.1.5 | 0.1.6 | 0.1.7 | 0.2.0 | 0.2.1 | 0.2.2">
21
21
  <img src="https://img.shields.io/badge/Android-4493F8?style=flat" alt="Android">
22
22
  <img src="https://img.shields.io/badge/iOS-beta-8E8E93?style=flat" alt="iOS beta">
23
23
  <a href="https://www.dsh.so/artifact/dsh-tether"><img src="https://www.dsh.so/badge/dsh-tether.svg" alt="dsh.so security scan"></a>
@@ -137,7 +137,7 @@ The Termux route still works if you prefer it: install dsh and this plugin insid
137
137
  - The interface on the phone is DSH's own; the narrow-screen fit comes from minimal injected styles, so a dsh layout change may need a follow-up here.
138
138
  - English and Chinese: the app follows your phone's system language, terminal output follows the computer's, and the part injected into the dsh interface follows dsh's own language setting. There is no separate language switch.
139
139
  - Local mode exists only in the `arm64` build, never on iOS; the phone lacks bash and a full coreutils, so shell-dependent tools are incomplete. The `arm64` APK is about 83 MB because of the bundled runtime; the others are about 32 MB.
140
- - Verified against dsh **`0.1.0-rc.7`**, **`0.1.0-rc.8`**, **`0.1.2`** (alpha and rc.1), **`0.1.5-alpha.1`**, **`0.1.5-rc.2`**, **`0.1.6-alpha.2`**, **`0.1.7-rc.2`**, **`0.2.0-rc.2`**, **`0.2.1-alpha.1`**, **`0.2.1-alpha.2`** and **`0.2.1-rc.2`**. dsh is in developer preview — check this line before assuming a newer dsh works. From 0.1.7 dsh refuses to install a plugin whose declared range does not cover it, so a version outside this line is a refusal at install time rather than a silent risk. The browser authentication dsh introduced in 0.1.2-alpha is handled entirely on the machine side; the phone app needs no update for it.
140
+ - Verified against dsh **`0.1.0-rc.7`**, **`0.1.0-rc.8`**, **`0.1.2`** (alpha and rc.1), **`0.1.5-alpha.1`**, **`0.1.5-rc.2`**, **`0.1.6-alpha.2`**, **`0.1.7-rc.2`**, **`0.2.0-rc.2`**, **`0.2.1-alpha.1`**, **`0.2.1-alpha.2`**, **`0.2.1-rc.2`**, **`0.2.1-rc.3`**, **`0.2.1-rc.5`** and **`0.2.2-alpha.2`**. dsh is in developer preview — check this line before assuming a newer dsh works. From 0.1.7 dsh refuses to install a plugin whose declared range does not cover it, so a version outside this line is a refusal at install time rather than a silent risk. The browser authentication dsh introduced in 0.1.2-alpha is handled entirely on the machine side; the phone app needs no update for it.
141
141
 
142
142
  ## What has been verified
143
143
 
package/README.zh.md CHANGED
@@ -17,7 +17,7 @@
17
17
  <a href="../../releases"><img src="https://img.shields.io/github/downloads/zexadev/dsh-tether/total?style=flat&label=downloads&color=4D6BFE" alt="下载量"></a>
18
18
  <a href="../../stargazers"><img src="https://img.shields.io/github/stars/zexadev/dsh-tether?style=flat&label=%E2%98%85&color=08C" alt="Stars"></a>
19
19
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-2EA44F?style=flat" alt="MIT"></a>
20
- <img src="https://img.shields.io/badge/dsh-0.1.0--rc.7%20%7C%20rc.8%20%7C%200.1.2%20%7C%200.1.5%20%7C%200.1.6%20%7C%200.1.7%20%7C%200.2.0%20%7C%200.2.1-4D6BFE?style=flat" alt="dsh 0.1.0-rc.7 | rc.8 | 0.1.2 | 0.1.5 | 0.1.6 | 0.1.7 | 0.2.0 | 0.2.1">
20
+ <img src="https://img.shields.io/badge/dsh-0.1.0--rc.7%20%7C%20rc.8%20%7C%200.1.2%20%7C%200.1.5%20%7C%200.1.6%20%7C%200.1.7%20%7C%200.2.0%20%7C%200.2.1%20%7C%200.2.2-4D6BFE?style=flat" alt="dsh 0.1.0-rc.7 | rc.8 | 0.1.2 | 0.1.5 | 0.1.6 | 0.1.7 | 0.2.0 | 0.2.1 | 0.2.2">
21
21
  <img src="https://img.shields.io/badge/Android-4493F8?style=flat" alt="Android">
22
22
  <img src="https://img.shields.io/badge/iOS-%E6%B5%8B%E8%AF%95%E7%89%88-8E8E93?style=flat" alt="iOS 测试版">
23
23
  <a href="https://www.dsh.so/artifact/dsh-tether"><img src="https://www.dsh.so/badge/dsh-tether.svg" alt="dsh.so 安全扫描"></a>
@@ -137,7 +137,7 @@ dsh plugin --profile web add .
137
137
  - 手机上的界面是 DSH 自己的,窄屏适配靠注入的最小样式完成;dsh 改版式时可能需要跟进。
138
138
  - 中英双语:App 界面跟随手机的系统语言;电脑终端上的输出跟随电脑的系统语言;注入 dsh 界面的那部分跟随 dsh 自己的语言设置。没有单独的语言开关。
139
139
  - 本地模式只有 `arm64` 包有,iOS 没有;手机上没有 bash 与完整 coreutils,依赖 shell 的工具用不全。arm64 包因内置运行时约 83 MB,其余包约 32 MB。
140
- - 已针对 dsh **`0.1.0-rc.7`**、**`0.1.0-rc.8`**、**`0.1.2`**(alpha 与 rc.1)、**`0.1.5-alpha.1`**、**`0.1.5-rc.2`**、**`0.1.6-alpha.2`**、**`0.1.7-rc.2`**、**`0.2.0-rc.2`**、**`0.2.1-alpha.1`**、**`0.2.1-alpha.2`** 与 **`0.2.1-rc.2`** 验证。dsh 处于 developer preview,换更新的 dsh 之前先看这一行。0.1.7 起 dsh 会拒绝安装声明范围没覆盖自己的插件,所以这一行之外的版本是装不上,而不是装上了埋雷。dsh 0.1.2-alpha 引入的浏览器认证完全在电脑侧处理,手机 App 无需更新。
140
+ - 已针对 dsh **`0.1.0-rc.7`**、**`0.1.0-rc.8`**、**`0.1.2`**(alpha 与 rc.1)、**`0.1.5-alpha.1`**、**`0.1.5-rc.2`**、**`0.1.6-alpha.2`**、**`0.1.7-rc.2`**、**`0.2.0-rc.2`**、**`0.2.1-alpha.1`**、**`0.2.1-alpha.2`**、**`0.2.1-rc.2`**、**`0.2.1-rc.3`**、**`0.2.1-rc.5`** 与 **`0.2.2-alpha.2`** 验证。dsh 处于 developer preview,换更新的 dsh 之前先看这一行。0.1.7 起 dsh 会拒绝安装声明范围没覆盖自己的插件,所以这一行之外的版本是装不上,而不是装上了埋雷。dsh 0.1.2-alpha 引入的浏览器认证完全在电脑侧处理,手机 App 无需更新。
141
141
 
142
142
  ## 验证过什么
143
143
 
package/index.js CHANGED
@@ -455,9 +455,11 @@ const tetherCss = `
455
455
  /* 设置行是「说明文字 + 控件」一行排开,说明列 min-width:0,窄屏被控件挤到
456
456
  2px 宽,中文于是一字一行。让说明占满一行、控件换到下一行。
457
457
  选择器咬的是 CSS Module 的本地名(row/rowText),只有哈希前缀会随构建变;
458
- dsh 若重命名,规则失效退回现状,不会更糟。 */
459
- [class*="_row"]:not([class*="rowText"]) { flex-wrap: wrap !important; }
460
- [class*="rowText"] { flex: 1 1 100% !important; }
458
+ dsh 若重命名,规则失效退回现状,不会更糟。
459
+ 必须要求带 rowText 直接子元素:光凭名字里有 row 还会命中会话里的工具调用
460
+ 行,那种行高度写死 24px 且 overflow:hidden,一换行标题就被切掉半截。 */
461
+ [class*="_row"]:has(> [class*="rowText"]) { flex-wrap: wrap !important; }
462
+ [class*="_row"] > [class*="rowText"] { flex: 1 1 100% !important; }
461
463
 
462
464
  /* 弹窗是「左导航 + 右内容」横向排列,导航固定 188px——在 412px 的手机上
463
465
  占掉 45%,内容只剩两百出头。改成导航横排在顶、内容占满整宽。
@@ -488,13 +490,17 @@ const tetherCss = `
488
490
  }
489
491
  /* 侧栏展开时主框架把网格从「56px 内容」改成「280px 内容」,对话区被压到
490
492
  140px。改成抽屉:网格保持收起时的列宽,侧栏浮到内容之上盖住一部分。
491
- 展开态没有专门的类名,但展开才会出现拖拽把手,用它作状态信号。
492
- 把手本身在手机上没用(没法拖),隐掉。 */
493
+ 展开态读 dsh 自己写在 frame 上的 data-sidebar-collapsed(收起为 "true",
494
+ 展开时该属性不在),0.1.0-rc.7 起每个版本都带。把手本身在手机上没用
495
+ (没法拖),隐掉。 */
493
496
  /* 网格恒定为收起时的列宽,侧栏恒定绝对定位、只让宽度随展开态变化——
494
497
  position 不可过渡,只有始终 absolute 才能做出滑入滑出的动画。
495
498
  侧栏脱离文档流后剩下的子元素会整体上移一列(对话区掉进 56px 那列,
496
499
  反而更窄),所以显式钉住列号。 */
497
- [class*="_frame"] {
500
+ /* 只认最外层那个 frame。dsh 里好几个组件的局部类名也带 _frame(会话区、
501
+ 附件缩略图、子代理、文档预览),它们都长在它里面:附件缩略图自己是个
502
+ 单格网格,被这条列模板改掉后图片会挤进 56px 的第一列。 */
503
+ [class*="_frame"]:not([class*="_frame"] *) {
498
504
  grid-template-columns: 56px minmax(0, 1fr) 0px !important;
499
505
  }
500
506
  [class*="_frame"] > [class*="centerCol"] { grid-column: 2 !important; }
@@ -510,14 +516,13 @@ const tetherCss = `
510
516
  z-index: 30 !important;
511
517
  transition: width 220ms ease, box-shadow 220ms ease;
512
518
  }
513
- /* 展开态没有专门的类名,但展开才会出现拖拽把手,用它作状态信号 */
514
- [class*="_frame"]:has(> [class*="_handle"]) > [class*="sidebarCol"] {
519
+ [class*="_frame"]:not([data-sidebar-collapsed="true"]) > [class*="sidebarCol"] {
515
520
  width: min(300px, 84vw) !important;
516
521
  box-shadow: 0 10px 36px rgba(0, 0, 0, 0.24);
517
522
  }
518
523
  /* 抽屉下的内容压暗。指针不拦——收起由注入的脚本接管,拦了反而会在脚本
519
524
  未生效时把整页点死。 */
520
- [class*="_frame"]::after {
525
+ [class*="_frame"]:not([class*="_frame"] *)::after {
521
526
  content: '';
522
527
  position: absolute;
523
528
  inset: 0;
@@ -527,12 +532,12 @@ const tetherCss = `
527
532
  pointer-events: none;
528
533
  transition: opacity 220ms ease;
529
534
  }
530
- [class*="_frame"]:has(> [class*="_handle"])::after { opacity: 1; }
535
+ [class*="_frame"]:not([class*="_frame"] *):not([data-sidebar-collapsed="true"])::after { opacity: 1; }
531
536
  [class*="_frame"] > [class*="_handle"] { display: none !important; }
532
537
 
533
538
  /* 侧栏收起时是图标条,「主机」按钮的文字跟着藏起来,和 dsh 自己的按钮一致 */
534
539
  [data-dsh-tether="hosts-label"] { display: none; }
535
- [class*="_frame"]:has(> [class*="_handle"]) [data-dsh-tether="hosts-label"] { display: inline; }
540
+ [class*="_frame"]:not([data-sidebar-collapsed="true"]) [data-dsh-tether="hosts-label"] { display: inline; }
536
541
  @media (prefers-reduced-motion: reduce) {
537
542
  [class*="_frame"] > [class*="sidebarCol"],
538
543
  [class*="_frame"]::after { transition: none; }
@@ -568,7 +573,8 @@ const tetherScript = `
568
573
  document.addEventListener('pointerdown', function (event) {
569
574
  if (!narrow.matches) return
570
575
  var frame = document.querySelector('[class*="_frame"]')
571
- if (!frame || !frame.querySelector(':scope > [class*="_handle"]')) return
576
+ // 抽屉开着才有「点外面收起来」可言;判据和样式那边同一个
577
+ if (!frame || frame.getAttribute('data-sidebar-collapsed') === 'true') return
572
578
  var drawer = frame.querySelector(':scope > [class*="sidebarCol"]')
573
579
  if (!drawer || drawer.contains(event.target)) return
574
580
  var toggle = document.querySelector('button[aria-label*="侧边栏"], button[aria-label*="sidebar" i]')
@@ -647,7 +653,13 @@ const tetherScript = `
647
653
  // 这里放「主机」按钮,点了让外层的手机客户端打开它自己的主机页。
648
654
  // 同一个按钮两种归宿:手机 App 里打开它自己的主机页;电脑浏览器里出示配对串,
649
655
  // 好让用户换手机、加设备时有地方拿新配对码(否则只能去删 paired.json)。
650
- var framed = window.parent !== window
656
+ // 判据是「App 自己挂的标记」,不是「我在不在 iframe 里」:dsh 桌面端的插件界面
657
+ // 同样跑在宿主的内嵌 frame 里,按 window.parent !== window 判会被当成手机 App,
658
+ // 按钮变「我的电脑」、点了只往父窗口发一条消息——桌面端没有这条消息的监听者,
659
+ // 按钮就是死的。标记拿不到一律按浏览器处理,出示配对串,至少点得动;
660
+ // 0.1.24 及更早的 App 没有这个标记,在 App 里也会出示配对串(它自己的顶栏有
661
+ // 「主机」入口,功能不丢)。
662
+ var inApp = !!window.__DSH_TETHER_APP__
651
663
 
652
664
  // 已配对手机列表。白名单每条都持有访问权,而手机重装 App 会换身份密钥,
653
665
  // 旧条目就此变成没人认识的常驻凭证——这里是撤销它们的地方。
@@ -801,7 +813,7 @@ const tetherScript = `
801
813
  var button = document.createElement('button')
802
814
  button.type = 'button'
803
815
  button.setAttribute('data-dsh-tether', 'hosts-trigger')
804
- var label = framed ? tx('我的电脑', 'My computers') : tx('连接手机', 'Connect a phone')
816
+ var label = inApp ? tx('我的电脑', 'My computers') : tx('连接手机', 'Connect a phone')
805
817
  button.setAttribute('aria-label', label)
806
818
  // 浏览器侧沿用原来那个 18px 图标,一字不改;抄不到「设置」时按参照行的尺寸画
807
819
  var icon = reference === null || reference.browser
@@ -809,7 +821,7 @@ const tetherScript = `
809
821
  : phoneIcon(referenceIconWidth(reference.trigger))
810
822
  button.innerHTML = icon + '<span data-dsh-tether="hosts-label">' + label + '</span>'
811
823
  button.addEventListener('click', function () {
812
- if (framed) window.parent.postMessage({ type: 'dsh-tether:open-hosts' }, '*')
824
+ if (inApp) window.parent.postMessage({ type: 'dsh-tether:open-hosts' }, '*')
813
825
  else showPairing()
814
826
  })
815
827
  row.append(button)
@@ -851,7 +863,7 @@ const tetherScript = `
851
863
  }
852
864
  // dsh 的语言能在设置里当场切,这个标签跟着换
853
865
  new MutationObserver(function () {
854
- var next = framed ? tx('我的电脑', 'My computers') : tx('连接手机', 'Connect a phone')
866
+ var next = inApp ? tx('我的电脑', 'My computers') : tx('连接手机', 'Connect a phone')
855
867
  button.setAttribute('aria-label', next)
856
868
  var span = button.querySelector('[data-dsh-tether="hosts-label"]')
857
869
  if (span) span.textContent = next
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-tether",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
4
4
  "description": "Mobile client for the DeepSeek Harness — use the dsh on your dev machine from your Android or iOS phone, remotely across networks, peer-to-peer over iroh with no server to set up (falls back to a relay that only sees ciphertext)",
5
5
  "keywords": [
6
6
  "dsh",
@@ -48,19 +48,19 @@
48
48
  }
49
49
  },
50
50
  "peerDependencies": {
51
- "@deepseek-ai/dsh-host-webserver": ">=0.1.0-rc.7 <0.1.1-0 || >=0.1.2-alpha.0 <0.1.3-0 || >=0.1.5-alpha.0 <0.1.8-0 || >=0.2.0-rc.2 <0.2.1-0 || >=0.2.1-alpha.1 <0.2.2-0",
52
- "@deepseek-ai/dsh-user-approval": ">=0.1.0-rc.7 <0.1.1-0 || >=0.1.2-alpha.0 <0.1.3-0 || >=0.1.5-alpha.0 <0.1.8-0 || >=0.2.0-rc.2 <0.2.1-0 || >=0.2.1-alpha.1 <0.2.2-0"
51
+ "@deepseek-ai/dsh-host-webserver": ">=0.1.0-rc.7 <0.1.1-0 || >=0.1.2-alpha.0 <0.1.3-0 || >=0.1.5-alpha.0 <0.1.8-0 || >=0.2.0-rc.2 <0.2.1-0 || >=0.2.1-alpha.1 <0.2.2-0 || >=0.2.2-alpha.2 <0.2.3-0",
52
+ "@deepseek-ai/dsh-user-approval": ">=0.1.0-rc.7 <0.1.1-0 || >=0.1.2-alpha.0 <0.1.3-0 || >=0.1.5-alpha.0 <0.1.8-0 || >=0.2.0-rc.2 <0.2.1-0 || >=0.2.1-alpha.1 <0.2.2-0 || >=0.2.2-alpha.2 <0.2.3-0"
53
53
  },
54
54
  "peerDependenciesMeta": {
55
55
  "@deepseek-ai/dsh-host-webserver": { "optional": true },
56
56
  "@deepseek-ai/dsh-user-approval": { "optional": true }
57
57
  },
58
58
  "optionalDependencies": {
59
- "dsh-tether-host-android-arm64": "0.1.23",
60
- "dsh-tether-host-darwin-arm64": "0.1.23",
61
- "dsh-tether-host-darwin-x64": "0.1.23",
62
- "dsh-tether-host-linux-arm64": "0.1.23",
63
- "dsh-tether-host-linux-x64": "0.1.23",
64
- "dsh-tether-host-win32-x64": "0.1.23"
59
+ "dsh-tether-host-android-arm64": "0.1.25",
60
+ "dsh-tether-host-darwin-arm64": "0.1.25",
61
+ "dsh-tether-host-darwin-x64": "0.1.25",
62
+ "dsh-tether-host-linux-arm64": "0.1.25",
63
+ "dsh-tether-host-linux-x64": "0.1.25",
64
+ "dsh-tether-host-win32-x64": "0.1.25"
65
65
  }
66
66
  }