@wenbin_wb/dsh-bridge 2.10.6 → 2.10.8
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 +27 -0
- package/README.md +1 -1
- package/client/client.js +395 -5
- package/client/index.js +334 -3
- package/client/mobile-styles.js +68 -0
- package/docs/cloudflare-fixed-domain.md +158 -0
- package/lib/auth/manager.js +54 -4
- package/lib/bridge-rpc-constants.js +1 -0
- package/lib/bridge-rpc.js +8 -0
- package/lib/cloudflared-manager.mjs +31 -4
- package/lib/index.js +158 -2
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,33 @@
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [v2.10.8] - 2026-09-08
|
|
8
|
+
|
|
9
|
+
### ✨ 新功能
|
|
10
|
+
|
|
11
|
+
- **登录 Session 持久化**(issue #36,PR #37):宿主(dsh web)进程重启后,已登录设备不再需要重输访问密码——手机远程访问的登录态不再随每次 DSH 重启丢失。改密码 / 切换模式 / 重新生成 Token 仍会同步吊销全部旧会话(安全语义保持);文件损坏/写盘失败安全降级。
|
|
12
|
+
- **设置页检测 DSH 宿主更新并一键升级**:版本横幅同步检测 `@deepseek-ai/dsh` 线上最新版,发现新版时黄色提示;检测到 npm 全局安装形态时提供「一键升级 DSH」按钮(升级后引导重启),非 npm 安装(源码 / 打包加壳等)仅提醒并给官方升级方式,避免误导。
|
|
13
|
+
- **移动端输入框可折叠**:聊天头部工具栏(Session 下载钮旁)新增折叠按钮,收起底部输入区后消息阅读区最大化(约 +20% 可视高度);折叠时底部保留「✏️ 点击输入消息」细条一键唤回;折叠偏好跨会话记忆。
|
|
14
|
+
|
|
15
|
+
### 🐞 修复
|
|
16
|
+
|
|
17
|
+
- **iOS Safari 键盘弹起时聊天输入框上下跳动**:输入区为 sticky 吸底布局,键盘弹起时随 visual viewport 收缩反复重排抖动——现键盘弹起时固定输入框所属滚动容器高度,键盘收起后还原,仅 iOS 生效、不依赖宿主类名。
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## [v2.10.7] - 2026-09-08
|
|
22
|
+
|
|
23
|
+
### 🐞 修复
|
|
24
|
+
|
|
25
|
+
- **修复 Cloudflare 固定域名(Token 模式)隧道秒退**(issue #35):v2.10.6 引入的 `--no-autoupdate` 参数位置错误(写在 `run` 之后)导致 cloudflared 报 `Incorrect Usage` 秒退、固定域名隧道永远起不来(免费临时隧道不受影响)。已改为放在 `run` 之前,与临时隧道分支保持一致。
|
|
26
|
+
- **确定性配置错误直接提示**:`Incorrect Usage` / 无效 Token 等配置错误不再误判为"意外退出"退避重连 12 次,而是直接置 error 显示配置错误原因,便于定位问题。
|
|
27
|
+
|
|
28
|
+
### ✨ 优化
|
|
29
|
+
|
|
30
|
+
- **Cloudflare 固定域名配置区新增教程入口**:「查看 Cloudflare 固定域名申请与配置教程」链接,覆盖账号注册 → 域名接入 → 创建固定隧道 → 复制 Token → 绑定子域名(Public Hostname)→ 填回面板的完整图文步骤,中英文菜单对照。
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
7
34
|
## [v2.10.6] - 2026-09-07
|
|
8
35
|
|
|
9
36
|
### 🐞 修复
|
package/README.md
CHANGED
|
@@ -139,7 +139,7 @@ dsh plugin --profile web add @wenbin_wb/dsh-bridge@latest
|
|
|
139
139
|
3. 几秒内自动生成公网 URL 和二维码,点「重置链接」可随时换新。
|
|
140
140
|
|
|
141
141
|
- **模式 2:Cloudflare Token 固定域名(永久不变 · 免费)**
|
|
142
|
-
1. 在 [Cloudflare Zero Trust 控制台](https://one.dash.cloudflare.com/) 免费创建 Tunnel 并绑定域名(如 `dsh.yourdomain.com
|
|
142
|
+
1. 在 [Cloudflare Zero Trust 控制台](https://one.dash.cloudflare.com/) 免费创建 Tunnel 并绑定域名(如 `dsh.yourdomain.com`)——**[📖 从零申请/配置完整教程](docs/cloudflare-fixed-domain.md)**(注册账号 → 接入域名 → 建隧道 → 取 Token → 绑定子域名 → 填回面板);
|
|
143
143
|
2. 展开卡片底部的 **「⚙️ 高级配置:固定域名 (Cloudflare Token)」**,填入自定义域名与 Tunnel Token 并保存;
|
|
144
144
|
3. 勾选 **「随 DSH 启动自动开启」**,每次 DSH 重启即可自动恢复隧道,**URL 永久固定不变**!
|
|
145
145
|
|
package/client/client.js
CHANGED
|
@@ -425,6 +425,74 @@ var MOBILE_STYLES_CSS = `
|
|
|
425
425
|
padding-bottom: max(16px, env(safe-area-inset-bottom)) !important;
|
|
426
426
|
}
|
|
427
427
|
|
|
428
|
+
/* \u2500\u2500 \u79FB\u52A8\u7AEF\u8F93\u5165\u6846\u6298\u53E0\u6A21\u5F0F\uFF08body.dsh-composer-collapsed\uFF09\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
429
|
+
\u6298\u53E0\u540E\u9690\u85CF\u5438\u5E95\u8F93\u5165\u533A\uFF0C\u8BA9\u6D88\u606F viewArea \u81EA\u52A8\u4F38\u5C55\u5230\u5168\u9AD8\uFF0C\u6700\u5927\u5316\u9605\u8BFB\u533A\u3002
|
|
430
|
+
\u5165\u53E3\uFF1A\u804A\u5929\u5934\u90E8\u5DE5\u5177\u680F\uFF08Session \u4E0B\u8F7D\u94AE\u65C1\uFF09\u7684\u6298\u53E0\u6309\u94AE\uFF0C\u6216\u6298\u53E0\u6001\u5E95\u90E8\u7EC6\u8F93\u5165\u6761\u70B9\u5B83\u5524\u56DE\u3002
|
|
431
|
+
\u72B6\u6001\u6301\u4E45\u5316\u5230 localStorage\u3002 */
|
|
432
|
+
body.dsh-composer-collapsed div[class*="wSkVaW_composerSeat"] {
|
|
433
|
+
display: none !important;
|
|
434
|
+
}
|
|
435
|
+
body.dsh-composer-collapsed div[class*="wSkVaW_viewArea"] {
|
|
436
|
+
flex: 1 1 auto !important;
|
|
437
|
+
height: auto !important;
|
|
438
|
+
min-height: 0 !important;
|
|
439
|
+
}
|
|
440
|
+
/* \u6298\u53E0\u65F6\u6EDA\u52A8\u533A\u5E95\u90E8\u5BF9\u9F50 safe-area\uFF0C\u907F\u514D\u5185\u5BB9\u88AB iPhone \u5E95\u90E8\u6A2A\u6761\u906E\u6321 */
|
|
441
|
+
body.dsh-composer-collapsed div[class*="wSkVaW_scrollBody"] {
|
|
442
|
+
padding-bottom: max(16px, env(safe-area-inset-bottom)) !important;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/* \u6298\u53E0\u8F93\u5165\u6846\u6309\u94AE\uFF1A\u6CE8\u5165\u5728\u804A\u5929\u5934\u90E8\u5DE5\u5177\u680F\uFF08Session \u4E0B\u8F7D\u94AE\u65C1\uFF09\uFF0C
|
|
446
|
+
\u4E0E DSH sessionLogButton \u540C\u89C4\u683C\uFF0828px \u5706\u5F62\u56FE\u6807\u94AE\uFF09\uFF0C\u89C6\u89C9\u4E0E\u539F\u751F\u4E00\u81F4 */
|
|
447
|
+
.dsh-header-fold-btn {
|
|
448
|
+
min-width: 28px !important;
|
|
449
|
+
width: 28px !important;
|
|
450
|
+
height: 28px !important;
|
|
451
|
+
padding: 0 !important;
|
|
452
|
+
border-radius: 50% !important;
|
|
453
|
+
display: inline-flex !important;
|
|
454
|
+
align-items: center !important;
|
|
455
|
+
justify-content: center !important;
|
|
456
|
+
flex-shrink: 0 !important;
|
|
457
|
+
border: 1px solid var(--dsw-alias-border-l2, rgba(0, 0, 0, 0.1)) !important;
|
|
458
|
+
background: var(--dsw-alias-bg-layer-2, rgba(0, 0, 0, 0.03)) !important;
|
|
459
|
+
color: var(--dsw-alias-label-secondary, #6b7280) !important;
|
|
460
|
+
cursor: pointer !important;
|
|
461
|
+
transition: opacity 0.15s !important;
|
|
462
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03) !important;
|
|
463
|
+
-webkit-tap-highlight-color: transparent !important;
|
|
464
|
+
}
|
|
465
|
+
.dsh-header-fold-btn:active {
|
|
466
|
+
opacity: 0.6 !important;
|
|
467
|
+
}
|
|
468
|
+
.dsh-header-fold-btn svg {
|
|
469
|
+
width: 15px !important;
|
|
470
|
+
height: 15px !important;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/* \u6298\u53E0\u6001\u5E95\u90E8\u7EC6\u8F93\u5165\u6761\uFF1A\u70B9\u51FB\u5524\u8D77\u8F93\u5165\u6846\uFF08\u4F4D\u4E8E\u539F\u8F93\u5165\u533A\u4F4D\u7F6E\uFF0Csticky \u5E95\u90E8\uFF09 */
|
|
474
|
+
.dsh-composer-collapsed-bar {
|
|
475
|
+
display: none !important;
|
|
476
|
+
position: sticky !important;
|
|
477
|
+
bottom: 0 !important;
|
|
478
|
+
margin: 8px 12px max(8px, env(safe-area-inset-bottom, 0px)) 12px !important;
|
|
479
|
+
padding: 11px 16px !important;
|
|
480
|
+
border-radius: 22px !important;
|
|
481
|
+
background: var(--dsw-alias-bg-layer-2, #f4f4f7) !important;
|
|
482
|
+
border: 1px solid rgba(0, 0, 0, 0.07) !important;
|
|
483
|
+
color: var(--dsw-alias-label-tertiary, #8b93a1) !important;
|
|
484
|
+
font-size: 13.5px !important;
|
|
485
|
+
line-height: 1.4 !important;
|
|
486
|
+
cursor: pointer !important;
|
|
487
|
+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.04) !important;
|
|
488
|
+
-webkit-tap-highlight-color: transparent !important;
|
|
489
|
+
z-index: 40 !important;
|
|
490
|
+
box-sizing: border-box !important;
|
|
491
|
+
}
|
|
492
|
+
body.dsh-composer-collapsed .dsh-composer-collapsed-bar {
|
|
493
|
+
display: block !important;
|
|
494
|
+
}
|
|
495
|
+
|
|
428
496
|
/* \u8F93\u5165\u5361\u7247\uFF1ADeepSeek App \u5706\u89D2\u5927\u80F6\u56CA\u9020\u578B */
|
|
429
497
|
div[class*="uV2eYG_card"] {
|
|
430
498
|
border-radius: 26px !important;
|
|
@@ -999,6 +1067,7 @@ var BRIDGE_ENDPOINTS = {
|
|
|
999
1067
|
setLanIp: "setLanIp",
|
|
1000
1068
|
checkVersion: "checkVersion",
|
|
1001
1069
|
upgradePlugin: "upgradePlugin",
|
|
1070
|
+
upgradeDsh: "upgradeDsh",
|
|
1002
1071
|
restartDsh: "restartDsh",
|
|
1003
1072
|
exportBackup: "exportBackup",
|
|
1004
1073
|
importBackup: "importBackup",
|
|
@@ -1085,6 +1154,7 @@ var GITHUB_URL = "https://github.com/wenbin-wb/dsh-bridge";
|
|
|
1085
1154
|
var RELEASES_URL = "https://github.com/wenbin-wb/dsh-bridge/releases";
|
|
1086
1155
|
var ISSUES_URL = "https://github.com/wenbin-wb/dsh-bridge/issues/new";
|
|
1087
1156
|
var TUNNEL_DOCS_URL = "https://github.com/wenbin-wb/dsh-bridge/blob/main/docs/custom-tunnel.md";
|
|
1157
|
+
var CLOUDFLARE_TUTORIAL_URL = "https://github.com/wenbin-wb/dsh-bridge/blob/main/docs/cloudflare-fixed-domain.md";
|
|
1088
1158
|
function upgradeCommands(latest) {
|
|
1089
1159
|
const spec = `@wenbin_wb/dsh-bridge@${latest}`;
|
|
1090
1160
|
return [
|
|
@@ -1849,6 +1919,12 @@ var CloudflareConfigForm = React.memo(function CloudflareConfigForm2({ token, ho
|
|
|
1849
1919
|
{ style: { fontSize: 12, color: "var(--dsw-alias-label-secondary, #6b7280)", marginBottom: 8, lineHeight: 1.5 } },
|
|
1850
1920
|
"\u5728 Cloudflare Zero Trust \u63A7\u5236\u53F0\u521B\u5EFA Tunnel \u5373\u53EF\u83B7\u53D6\u4E13\u5C5E Token \u5E76\u7ED1\u5B9A\u81EA\u5DF1\u7684\u57DF\u540D\uFF08\u5982 dsh.yourname.com\uFF09\uFF0C\u6BCF\u6B21\u91CD\u542F URL \u6C38\u4E0D\u53D8\u66F4\u3002\u4E0D\u586B\u5219\u4F7F\u7528\u9ED8\u8BA4\u514D\u767B\u5F55\u4E34\u65F6\u968F\u673A\u57DF\u540D\u3002"
|
|
1851
1921
|
),
|
|
1922
|
+
React.createElement("a", {
|
|
1923
|
+
href: CLOUDFLARE_TUTORIAL_URL,
|
|
1924
|
+
target: "_blank",
|
|
1925
|
+
rel: "noreferrer",
|
|
1926
|
+
style: { ...s.btnLink, marginBottom: 10 }
|
|
1927
|
+
}, "\u{1F4D6} \u67E5\u770B\u300CCloudflare \u56FA\u5B9A\u57DF\u540D\u300D\u7533\u8BF7\u4E0E\u914D\u7F6E\u6559\u7A0B"),
|
|
1852
1928
|
React.createElement(
|
|
1853
1929
|
"div",
|
|
1854
1930
|
{ style: { marginBottom: 8 } },
|
|
@@ -3683,6 +3759,7 @@ function VersionBanner({ rpcCall }) {
|
|
|
3683
3759
|
}, [check]);
|
|
3684
3760
|
const hasUpdate = info?.latest && info?.current && !info.error && semverGt(info.latest, info.current);
|
|
3685
3761
|
const isLatest = info?.latest && info?.current && !info.error && !semverGt(info.latest, info.current);
|
|
3762
|
+
const dshHasUpdate = !!(info?.dshLatest && info?.dshVersion && !info.error && semverGt(info.dshLatest, info.dshVersion));
|
|
3686
3763
|
const handleUpgrade = React.useCallback(async () => {
|
|
3687
3764
|
if (!info?.latest || upgrading) return;
|
|
3688
3765
|
setUpgrading(true);
|
|
@@ -3704,6 +3781,28 @@ function VersionBanner({ rpcCall }) {
|
|
|
3704
3781
|
setUpgrading(false);
|
|
3705
3782
|
}
|
|
3706
3783
|
}, [info?.latest, upgrading, rpcCall]);
|
|
3784
|
+
const [dshUpgrading, setDshUpgrading] = React.useState(false);
|
|
3785
|
+
const [dshUpgradeResult, setDshUpgradeResult] = React.useState(null);
|
|
3786
|
+
const handleUpgradeDsh = React.useCallback(async () => {
|
|
3787
|
+
if (!info?.dshLatest || dshUpgrading) return;
|
|
3788
|
+
setDshUpgrading(true);
|
|
3789
|
+
setDshUpgradeResult(null);
|
|
3790
|
+
setDismissRestart(false);
|
|
3791
|
+
resetRestartStatus();
|
|
3792
|
+
try {
|
|
3793
|
+
const r = await rpcCall(BRIDGE_ENDPOINTS.upgradeDsh, { version: info.dshLatest });
|
|
3794
|
+
if (r?.ok && r.value?.ok) {
|
|
3795
|
+
setDshUpgradeResult({ ok: true, message: `DSH \u5DF2\u6210\u529F\u5347\u7EA7\u5230 v${info.dshLatest}\uFF01` });
|
|
3796
|
+
} else {
|
|
3797
|
+
const msg = r?.value?.error || r?.error?.message || "\u5347\u7EA7\u5931\u8D25";
|
|
3798
|
+
setDshUpgradeResult({ ok: false, message: msg, manual: true });
|
|
3799
|
+
}
|
|
3800
|
+
} catch (e) {
|
|
3801
|
+
setDshUpgradeResult({ ok: false, message: e.message || "\u5347\u7EA7\u8BF7\u6C42\u5931\u8D25", manual: true });
|
|
3802
|
+
} finally {
|
|
3803
|
+
setDshUpgrading(false);
|
|
3804
|
+
}
|
|
3805
|
+
}, [info?.dshLatest, dshUpgrading, rpcCall]);
|
|
3707
3806
|
const links = React.createElement(
|
|
3708
3807
|
"div",
|
|
3709
3808
|
{ style: { display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap" } },
|
|
@@ -3768,24 +3867,26 @@ function VersionBanner({ rpcCall }) {
|
|
|
3768
3867
|
hasUpdate && React.createElement("span", { style: { fontWeight: 600, fontSize: 11 } }, `\u2794 v${info.latest}`),
|
|
3769
3868
|
info?.error && React.createElement("span", { style: { color: "var(--dsw-alias-state-warn-primary,#d97706)", fontSize: 11 } }, "(\u7F51\u7EDC\u8D85\u65F6)")
|
|
3770
3869
|
),
|
|
3771
|
-
// DSH
|
|
3870
|
+
// DSH 宿主版本标签(有新版时黄色高亮)
|
|
3772
3871
|
info?.dshVersion && React.createElement(
|
|
3773
3872
|
"span",
|
|
3774
3873
|
{
|
|
3775
3874
|
style: {
|
|
3776
3875
|
...s.tag,
|
|
3777
|
-
background: "var(--dsw-alias-bg-layer-2,#f3f4f6)",
|
|
3778
|
-
color: "var(--dsw-alias-label-tertiary,#6b7280)",
|
|
3876
|
+
background: dshHasUpdate ? "var(--dsw-alias-state-warn-bg,#fffbeb)" : "var(--dsw-alias-bg-layer-2,#f3f4f6)",
|
|
3877
|
+
color: dshHasUpdate ? "var(--dsw-alias-state-warn-primary,#d97706)" : "var(--dsw-alias-label-tertiary,#6b7280)",
|
|
3779
3878
|
padding: "3px 10px",
|
|
3780
3879
|
fontSize: 12,
|
|
3781
3880
|
fontWeight: 500,
|
|
3782
3881
|
display: "inline-flex",
|
|
3783
3882
|
alignItems: "center",
|
|
3784
3883
|
gap: 5
|
|
3785
|
-
}
|
|
3884
|
+
},
|
|
3885
|
+
title: dshHasUpdate ? `\u53D1\u73B0 DSH \u65B0\u7248\u672C v${info.dshLatest}` : void 0
|
|
3786
3886
|
},
|
|
3787
3887
|
React.createElement("span", { style: { opacity: 0.75, fontSize: 11, fontWeight: 400 } }, "DSH"),
|
|
3788
|
-
`v${info.dshVersion}
|
|
3888
|
+
`v${info.dshVersion}`,
|
|
3889
|
+
dshHasUpdate && React.createElement("span", { style: { fontWeight: 600, fontSize: 11 } }, `\u2794 v${info.dshLatest}`)
|
|
3789
3890
|
),
|
|
3790
3891
|
// 刷新检查按钮
|
|
3791
3892
|
React.createElement(
|
|
@@ -3975,6 +4076,109 @@ function VersionBanner({ rpcCall }) {
|
|
|
3975
4076
|
)
|
|
3976
4077
|
)
|
|
3977
4078
|
)
|
|
4079
|
+
),
|
|
4080
|
+
// ── DSH 宿主有新版本时的提示 / 一键升级卡片 ──
|
|
4081
|
+
dshHasUpdate && React.createElement(
|
|
4082
|
+
"div",
|
|
4083
|
+
{
|
|
4084
|
+
style: {
|
|
4085
|
+
...s.card,
|
|
4086
|
+
background: "var(--dsw-alias-state-warn-bg,#fffbeb)",
|
|
4087
|
+
border: "1px solid var(--dsw-alias-state-warn-border,#fde68a)",
|
|
4088
|
+
padding: "14px 16px",
|
|
4089
|
+
marginTop: 10,
|
|
4090
|
+
marginBottom: 0
|
|
4091
|
+
}
|
|
4092
|
+
},
|
|
4093
|
+
React.createElement(
|
|
4094
|
+
"div",
|
|
4095
|
+
{ style: { display: "flex", alignItems: "flex-start", gap: 12 } },
|
|
4096
|
+
React.createElement("span", { style: { fontSize: 22 } }, "\u{1F6E0}\uFE0F"),
|
|
4097
|
+
React.createElement(
|
|
4098
|
+
"div",
|
|
4099
|
+
{ style: { flex: 1, minWidth: 0 } },
|
|
4100
|
+
React.createElement(
|
|
4101
|
+
"div",
|
|
4102
|
+
{ style: { display: "flex", alignItems: "center", justifyContent: "space-between", flexWrap: "wrap", gap: 8, marginBottom: 8 } },
|
|
4103
|
+
React.createElement("div", {
|
|
4104
|
+
style: { fontSize: 13, fontWeight: 600, color: "var(--dsw-alias-state-warn-primary,#92400e)" }
|
|
4105
|
+
}, `DSH \u6709\u65B0\u7248\u672C v${info.dshLatest}\uFF08\u5F53\u524D v${info.dshVersion}\uFF09`),
|
|
4106
|
+
info?.dshUpgradable ? React.createElement(
|
|
4107
|
+
"button",
|
|
4108
|
+
{
|
|
4109
|
+
style: {
|
|
4110
|
+
...s.btnPri,
|
|
4111
|
+
height: 28,
|
|
4112
|
+
fontSize: 12,
|
|
4113
|
+
padding: "0 14px",
|
|
4114
|
+
background: dshUpgradeResult?.ok ? "var(--dsw-alias-state-success-primary,#059669)" : "var(--dsw-alias-brand-primary,#4f6ef7)",
|
|
4115
|
+
opacity: dshUpgrading || restarting ? 0.6 : 1
|
|
4116
|
+
},
|
|
4117
|
+
onClick: handleUpgradeDsh,
|
|
4118
|
+
disabled: dshUpgrading || restarting || dshUpgradeResult?.ok
|
|
4119
|
+
},
|
|
4120
|
+
dshUpgrading ? React.createElement(
|
|
4121
|
+
"span",
|
|
4122
|
+
{ style: { display: "inline-flex", alignItems: "center", gap: 6 } },
|
|
4123
|
+
React.createElement("span", { style: { animation: "spin 1s linear infinite", display: "inline-flex" } }, React.createElement(Icons.refresh)),
|
|
4124
|
+
"\u6B63\u5728\u5347\u7EA7 DSH\u2026"
|
|
4125
|
+
) : dshUpgradeResult?.ok ? "\u2713 DSH \u5347\u7EA7\u5B8C\u6210" : `\u4E00\u952E\u5347\u7EA7 DSH \u5230 v${info.dshLatest}`
|
|
4126
|
+
) : React.createElement("a", {
|
|
4127
|
+
href: GITHUB_URL,
|
|
4128
|
+
target: "_blank",
|
|
4129
|
+
rel: "noreferrer",
|
|
4130
|
+
style: { ...s.btnLink, fontSize: 12, fontWeight: 600 }
|
|
4131
|
+
}, "\u67E5\u770B\u5B98\u65B9\u5347\u7EA7\u65B9\u5F0F \u2197")
|
|
4132
|
+
),
|
|
4133
|
+
React.createElement(
|
|
4134
|
+
"div",
|
|
4135
|
+
{
|
|
4136
|
+
style: { fontSize: 12, color: "var(--dsw-alias-label-secondary,#6b7280)", lineHeight: 1.6 }
|
|
4137
|
+
},
|
|
4138
|
+
info?.dshUpgradable ? "\u5347\u7EA7 DSH \u547D\u4EE4\u884C\u5DE5\u5177\u540E\u9700\u91CD\u542F DSH \u670D\u52A1\u751F\u6548\u3002" : info?.dshUpgradeReason || "\u5F53\u524D DSH \u975E npm \u5168\u5C40\u5B89\u88C5\uFF0C\u65E0\u6CD5\u4E00\u952E\u81EA\u52A8\u5347\u7EA7\uFF0C\u8BF7\u6309\u5B98\u65B9\u6E20\u9053\u624B\u52A8\u66F4\u65B0\u3002"
|
|
4139
|
+
),
|
|
4140
|
+
dshUpgradeResult && React.createElement("div", {
|
|
4141
|
+
style: {
|
|
4142
|
+
marginTop: 10,
|
|
4143
|
+
fontSize: 12,
|
|
4144
|
+
lineHeight: 1.6,
|
|
4145
|
+
color: dshUpgradeResult.ok ? "var(--dsw-alias-state-success-primary,#059669)" : "var(--dsw-alias-state-error-primary,#dc2626)"
|
|
4146
|
+
}
|
|
4147
|
+
}, dshUpgradeResult.message),
|
|
4148
|
+
dshUpgradeResult?.ok && !dismissRestart && React.createElement(
|
|
4149
|
+
"div",
|
|
4150
|
+
{
|
|
4151
|
+
style: { display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap", marginTop: 10 }
|
|
4152
|
+
},
|
|
4153
|
+
React.createElement("button", {
|
|
4154
|
+
style: { ...s.btnPri, height: 30, fontSize: 12, padding: "0 14px", background: "var(--dsw-alias-state-success-primary,#059669)" },
|
|
4155
|
+
onClick: handleRestart
|
|
4156
|
+
}, "\u{1F504} \u7ACB\u5373\u91CD\u542F DSH \u670D\u52A1"),
|
|
4157
|
+
React.createElement("button", {
|
|
4158
|
+
style: { ...s.btnGhost, height: 30, fontSize: 12, padding: "0 12px" },
|
|
4159
|
+
onClick: () => setDismissRestart(true)
|
|
4160
|
+
}, "\u7A0D\u540E\u624B\u52A8\u91CD\u542F")
|
|
4161
|
+
),
|
|
4162
|
+
(restarting || restartStatus) && React.createElement(
|
|
4163
|
+
"div",
|
|
4164
|
+
{
|
|
4165
|
+
style: {
|
|
4166
|
+
display: "flex",
|
|
4167
|
+
alignItems: "center",
|
|
4168
|
+
gap: 8,
|
|
4169
|
+
fontSize: 12,
|
|
4170
|
+
marginTop: 8,
|
|
4171
|
+
color: restartStatus?.phase === "success" ? "var(--dsw-alias-state-success-primary,#059669)" : restartStatus?.phase === "timeout" ? "var(--dsw-alias-state-error-primary,#dc2626)" : "var(--dsw-alias-state-info-primary,#2563eb)",
|
|
4172
|
+
fontWeight: 500
|
|
4173
|
+
}
|
|
4174
|
+
},
|
|
4175
|
+
restartStatus?.phase !== "success" && restartStatus?.phase !== "timeout" && React.createElement("span", {
|
|
4176
|
+
style: { animation: "spin 1s linear infinite", display: "inline-flex" }
|
|
4177
|
+
}, React.createElement(Icons.refresh)),
|
|
4178
|
+
restartStatus?.text || "\u6B63\u5728\u5904\u7406\u2026"
|
|
4179
|
+
)
|
|
4180
|
+
)
|
|
4181
|
+
)
|
|
3978
4182
|
)
|
|
3979
4183
|
);
|
|
3980
4184
|
}
|
|
@@ -5749,10 +5953,196 @@ function RemoteDirectoryFlow(props) {
|
|
|
5749
5953
|
}, [open, pick]);
|
|
5750
5954
|
return null;
|
|
5751
5955
|
}
|
|
5956
|
+
function setupIosKeyboardAdapter() {
|
|
5957
|
+
if (typeof window === "undefined" || !window.visualViewport) return;
|
|
5958
|
+
if (!/iPhone|iPad|iPod/.test(navigator.userAgent || "")) return;
|
|
5959
|
+
const vv = window.visualViewport;
|
|
5960
|
+
const isEditable = (el) => el && (el.isContentEditable || el.tagName === "TEXTAREA" || el.tagName === "INPUT");
|
|
5961
|
+
let keyboardOpen = false;
|
|
5962
|
+
let pinnedEl = null;
|
|
5963
|
+
let pinnedH = null;
|
|
5964
|
+
let pinnedInline = null;
|
|
5965
|
+
const findScrollContainer = (el) => {
|
|
5966
|
+
let n = el;
|
|
5967
|
+
while (n && n !== document.body) {
|
|
5968
|
+
const cs = getComputedStyle(n);
|
|
5969
|
+
if ((cs.overflowY === "auto" || cs.overflowY === "scroll") && n.scrollHeight > n.clientHeight) {
|
|
5970
|
+
return n;
|
|
5971
|
+
}
|
|
5972
|
+
n = n.parentElement;
|
|
5973
|
+
}
|
|
5974
|
+
return null;
|
|
5975
|
+
};
|
|
5976
|
+
vv.addEventListener("resize", () => {
|
|
5977
|
+
const ratio = vv.height / window.innerHeight;
|
|
5978
|
+
const nowOpen = ratio < 0.75;
|
|
5979
|
+
if (nowOpen === keyboardOpen) return;
|
|
5980
|
+
keyboardOpen = nowOpen;
|
|
5981
|
+
const el = document.activeElement;
|
|
5982
|
+
if (nowOpen) {
|
|
5983
|
+
const scroller = isEditable(el) ? findScrollContainer(el) : null;
|
|
5984
|
+
if (scroller) {
|
|
5985
|
+
pinnedEl = scroller;
|
|
5986
|
+
pinnedInline = scroller.style.height || "";
|
|
5987
|
+
pinnedH = scroller.getBoundingClientRect().height;
|
|
5988
|
+
scroller.style.height = `${Math.round(pinnedH)}px`;
|
|
5989
|
+
}
|
|
5990
|
+
requestAnimationFrame(() => {
|
|
5991
|
+
if (isEditable(el)) {
|
|
5992
|
+
try {
|
|
5993
|
+
el.scrollIntoView({ block: "nearest" });
|
|
5994
|
+
} catch {
|
|
5995
|
+
}
|
|
5996
|
+
}
|
|
5997
|
+
});
|
|
5998
|
+
} else {
|
|
5999
|
+
if (pinnedEl) {
|
|
6000
|
+
pinnedEl.style.height = pinnedInline;
|
|
6001
|
+
pinnedEl = null;
|
|
6002
|
+
pinnedH = null;
|
|
6003
|
+
pinnedInline = null;
|
|
6004
|
+
}
|
|
6005
|
+
}
|
|
6006
|
+
});
|
|
6007
|
+
}
|
|
6008
|
+
function setupComposerCollapse() {
|
|
6009
|
+
if (typeof window === "undefined" || typeof document === "undefined") return;
|
|
6010
|
+
if (window.innerWidth > 768) return;
|
|
6011
|
+
const LS_KEY = "dsh-composer-fold";
|
|
6012
|
+
let bar = null;
|
|
6013
|
+
let busy = false;
|
|
6014
|
+
let lastHadSeat = null;
|
|
6015
|
+
const getFoldBtn = () => {
|
|
6016
|
+
const existing = document.querySelector(".dsh-header-fold-btn");
|
|
6017
|
+
if (existing) return existing;
|
|
6018
|
+
const utils = document.querySelector('div[class*="wSkVaW_headerUtilities"], div[class*="headerUtilities"]');
|
|
6019
|
+
const logBtn = document.querySelector('button[class*="sessionLogButton"], button[class*="nL4_yW_sessionLogButton"]');
|
|
6020
|
+
if (!utils) return null;
|
|
6021
|
+
const btn = document.createElement("button");
|
|
6022
|
+
btn.className = "dsh-header-fold-btn";
|
|
6023
|
+
btn.setAttribute("aria-label", "\u6536\u8D77/\u5C55\u5F00\u8F93\u5165\u6846");
|
|
6024
|
+
btn.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="3" y1="18" x2="21" y2="18"></line><polyline points="6 9 12 15 18 9"></polyline></svg>';
|
|
6025
|
+
if (logBtn && logBtn.parentElement === utils) utils.insertBefore(btn, logBtn);
|
|
6026
|
+
else utils.appendChild(btn);
|
|
6027
|
+
return btn;
|
|
6028
|
+
};
|
|
6029
|
+
const isCollapsed = () => document.body.classList.contains("dsh-composer-collapsed");
|
|
6030
|
+
const readPref = () => {
|
|
6031
|
+
try {
|
|
6032
|
+
return localStorage.getItem(LS_KEY) === "1";
|
|
6033
|
+
} catch {
|
|
6034
|
+
return false;
|
|
6035
|
+
}
|
|
6036
|
+
};
|
|
6037
|
+
const ensureBar = () => {
|
|
6038
|
+
if (bar && document.body.contains(bar)) return bar;
|
|
6039
|
+
const seat = document.querySelector('div[class*="composerSeat"]');
|
|
6040
|
+
const scrollBody = seat ? seat.parentElement : null;
|
|
6041
|
+
if (!scrollBody) return null;
|
|
6042
|
+
bar = document.createElement("div");
|
|
6043
|
+
bar.className = "dsh-composer-collapsed-bar";
|
|
6044
|
+
bar.textContent = "\u270F\uFE0F \u70B9\u51FB\u8F93\u5165\u6D88\u606F\u2026";
|
|
6045
|
+
scrollBody.insertBefore(bar, seat);
|
|
6046
|
+
bar.addEventListener("click", () => setCollapsed(false));
|
|
6047
|
+
return bar;
|
|
6048
|
+
};
|
|
6049
|
+
const removeBar = () => {
|
|
6050
|
+
if (bar) {
|
|
6051
|
+
try {
|
|
6052
|
+
bar.remove();
|
|
6053
|
+
} catch {
|
|
6054
|
+
}
|
|
6055
|
+
bar = null;
|
|
6056
|
+
}
|
|
6057
|
+
};
|
|
6058
|
+
const setCollapsed = (collapsed) => {
|
|
6059
|
+
if (busy) return;
|
|
6060
|
+
busy = true;
|
|
6061
|
+
try {
|
|
6062
|
+
if (isCollapsed() !== collapsed) {
|
|
6063
|
+
document.body.classList.toggle("dsh-composer-collapsed", collapsed);
|
|
6064
|
+
try {
|
|
6065
|
+
localStorage.setItem(LS_KEY, collapsed ? "1" : "0");
|
|
6066
|
+
} catch {
|
|
6067
|
+
}
|
|
6068
|
+
updateButton(collapsed);
|
|
6069
|
+
}
|
|
6070
|
+
if (collapsed) ensureBar();
|
|
6071
|
+
else removeBar();
|
|
6072
|
+
} finally {
|
|
6073
|
+
busy = false;
|
|
6074
|
+
}
|
|
6075
|
+
};
|
|
6076
|
+
const updateButton = (collapsed) => {
|
|
6077
|
+
const btn = getFoldBtn();
|
|
6078
|
+
if (!btn) return;
|
|
6079
|
+
btn.title = collapsed ? "\u5C55\u5F00\u8F93\u5165\u6846" : "\u6536\u8D77\u8F93\u5165\u6846\uFF0C\u6700\u5927\u5316\u5BF9\u8BDD\u9605\u8BFB\u533A";
|
|
6080
|
+
const icon = collapsed ? '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="6" width="20" height="12" rx="2"></rect><line x1="6" y1="10" x2="6.01" y2="10"></line><line x1="10" y1="10" x2="10.01" y2="10"></line><line x1="14" y1="10" x2="14.01" y2="10"></line><line x1="6" y1="14" x2="10" y2="14"></line><line x1="14" y1="14" x2="18" y2="14"></line></svg>' : '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="3" y1="18" x2="21" y2="18"></line><polyline points="6 9 12 15 18 9"></polyline></svg>';
|
|
6081
|
+
const existing = btn.querySelector("svg");
|
|
6082
|
+
if (!existing || existing.outerHTML !== icon) btn.innerHTML = icon;
|
|
6083
|
+
};
|
|
6084
|
+
const handleSeatPresent = () => {
|
|
6085
|
+
const btn = getFoldBtn();
|
|
6086
|
+
if (!btn) return false;
|
|
6087
|
+
btn.style.display = "inline-flex";
|
|
6088
|
+
if (!btn._dshFoldWired) {
|
|
6089
|
+
btn._dshFoldWired = true;
|
|
6090
|
+
btn.onclick = (e) => {
|
|
6091
|
+
e.stopPropagation();
|
|
6092
|
+
setCollapsed(!isCollapsed());
|
|
6093
|
+
};
|
|
6094
|
+
}
|
|
6095
|
+
document.body.classList.add("dsh-composer-active");
|
|
6096
|
+
const want = readPref();
|
|
6097
|
+
if (want !== isCollapsed()) {
|
|
6098
|
+
setCollapsed(want);
|
|
6099
|
+
} else {
|
|
6100
|
+
updateButton(isCollapsed());
|
|
6101
|
+
}
|
|
6102
|
+
return true;
|
|
6103
|
+
};
|
|
6104
|
+
const handleSeatAbsent = () => {
|
|
6105
|
+
const btn = getFoldBtn();
|
|
6106
|
+
if (btn) {
|
|
6107
|
+
btn.style.display = "none";
|
|
6108
|
+
}
|
|
6109
|
+
document.body.classList.remove("dsh-composer-active");
|
|
6110
|
+
removeBar();
|
|
6111
|
+
};
|
|
6112
|
+
const onComposerChange = () => {
|
|
6113
|
+
const hasSeat = !!document.querySelector('div[class*="composerSeat"]');
|
|
6114
|
+
const hasBtn = !!document.querySelector(".dsh-header-fold-btn");
|
|
6115
|
+
if (busy) return;
|
|
6116
|
+
if (hasSeat === lastHadSeat && hasSeat === hasBtn) return;
|
|
6117
|
+
busy = true;
|
|
6118
|
+
try {
|
|
6119
|
+
if (hasSeat) {
|
|
6120
|
+
lastHadSeat = handleSeatPresent();
|
|
6121
|
+
} else {
|
|
6122
|
+
handleSeatAbsent();
|
|
6123
|
+
lastHadSeat = false;
|
|
6124
|
+
}
|
|
6125
|
+
} finally {
|
|
6126
|
+
busy = false;
|
|
6127
|
+
}
|
|
6128
|
+
};
|
|
6129
|
+
const observer = new MutationObserver(() => {
|
|
6130
|
+
observer.disconnect();
|
|
6131
|
+
try {
|
|
6132
|
+
onComposerChange();
|
|
6133
|
+
} finally {
|
|
6134
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
6135
|
+
}
|
|
6136
|
+
});
|
|
6137
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
6138
|
+
onComposerChange();
|
|
6139
|
+
}
|
|
5752
6140
|
function apply(ctx) {
|
|
5753
6141
|
window.__dshClientCtx = ctx;
|
|
5754
6142
|
const rpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(BRIDGE_RPC_CHANNEL, endpoint, payload, signal);
|
|
5755
6143
|
window.__dshOpenRemoteWorkspaceModal = (onAdded, onPickDirect, onCancel) => showRemoteWorkspaceDialog(rpcCall, onAdded, ctx, onPickDirect, onCancel);
|
|
6144
|
+
setupIosKeyboardAdapter();
|
|
6145
|
+
setupComposerCollapse();
|
|
5756
6146
|
setupMobileExperience(rpcCall, ctx);
|
|
5757
6147
|
const injected = () => ({ pick: () => ctx.workspaces?.pickDirectory?.() });
|
|
5758
6148
|
ctx.slots.inject(
|