@wenbin_wb/dsh-bridge 2.8.4 → 2.8.6

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
@@ -4,6 +4,34 @@
4
4
 
5
5
  ---
6
6
 
7
+ ## [v2.8.6] - 2026-08-30
8
+
9
+ ### 📱 移动端交互适配重构 & 隧道保活与传输优化
10
+ - **📱 移动端工作区体验重构与多途径秒级回切**:
11
+ - 修复移动端工作区面板(Workbench / `dsh-better-sidebar` / 任务管理)遮盖聊天对话与收起后白屏残留问题;
12
+ - 引入严密的显隐状态机(`dsh-workbench-open`),打开面板时在 Tab 栏常驻 **`✕ 返回对话`** 蓝色操作胶囊;
13
+ - 提供多途径极速回切:支持点击「返回对话」按钮、点击顶部 Header 会话标题、或在左侧抽屉切换会话秒级返回对话流;
14
+ - **🗂️ DSH 原生收起图标深度适配与双向开合**:
15
+ - 在移动端侧边栏抽屉顶部 Logo 栏完整集成并复用 DSH 原生自带的收起图标(`[|`);
16
+ - 支持点击主界面 `≡` 汉堡菜单流畅滑出抽屉、点击原生收起图标/点击会话/轻触遮罩丝滑收回抽屉;
17
+ - **🖥️ PC 桌面端严格隔离与零侵入**:
18
+ - 通过媒体查询与运行期断点双重守卫,确保 PC 桌面端(`> 768px`)100% 保持官方原生多列分栏布局与纯净体验,无任何样式污染与多余组件;
19
+ - **🛡️ Cloudflared 隧道守护进程自愈与自动重启**:
20
+ - 修复 Cloudflared 进程异常退出后无法自动拉起的问题,增加子进程退出监听与指数退避自动重连机制,确保长时间公网访问不断连;
21
+ - **⚡ 自建隧道 SSE 稳定性与数据传输优化**:
22
+ - 修复弱网环境下 SSE 断线重连问题,精简 Session 列表超大数据负载传输,显著降低移动端带宽占用与弱网延迟。
23
+
24
+ ---
25
+
26
+ ## [v2.8.5] - 2026-08-27
27
+
28
+ ### 🎯 产物文件推送改为 AI 指令驱动 & 修复 IM 平台断开连接问题
29
+ - **🎯 显式文件发送协议**:由 AI 通过 `[SEND_FILE: <路径>]` 显式驱动文件直传,杜绝修改代码时的文件误发风暴;
30
+ - **🧹 聊天正文自动净化**:自动剔除聊天窗口中的文件发送指令,保持聊天气泡清爽;
31
+ - **🔌 修复 IM 断开连接失效**:修复 QQ 等网关断开连接时 WebSocket 事件悬挂问题,断开后彻底阻断入站消息与出站回复。
32
+
33
+ ---
34
+
7
35
  ## [v2.8.4] - 2026-08-26
8
36
 
9
37
  ### 🗂️ 会话列表 Web 端 1:1 深度对齐与归档过滤修复
package/client/client.js CHANGED
@@ -3389,6 +3389,163 @@ function injectMobileStyles() {
3389
3389
  display: none !important;
3390
3390
  }
3391
3391
 
3392
+ /* 3.0 \u5DE5\u4F5C\u533A Workbench / \u4EFB\u52A1\u7BA1\u7406 / \u591A Tab \u680F\u79FB\u52A8\u7AEF\u81EA\u9002\u5E94\u9002\u914D */
3393
+ body:not(.dsh-workbench-open) div[class*="nArs4W_panel"],
3394
+ body:not(.dsh-workbench-open) div[class*="workbench_panel"],
3395
+ body:not(.dsh-workbench-open) div[class*="workbenchPanel"],
3396
+ div[class*="nArs4W_panel"][class*="panelHidden"],
3397
+ div[class*="workbench_panel"][class*="panelHidden"],
3398
+ div[class*="workbenchPanel"][class*="panelHidden"],
3399
+ div[class*="panelHidden"] {
3400
+ display: none !important;
3401
+ visibility: hidden !important;
3402
+ pointer-events: none !important;
3403
+ width: 0 !important;
3404
+ height: 0 !important;
3405
+ max-height: 0 !important;
3406
+ z-index: -1 !important;
3407
+ opacity: 0 !important;
3408
+ transform: translateX(105%) !important;
3409
+ }
3410
+
3411
+ body.dsh-workbench-open div[class*="nArs4W_panel"]:not([class*="panelHidden"]),
3412
+ body.dsh-workbench-open div[class*="workbench_panel"]:not([class*="panelHidden"]),
3413
+ body.dsh-workbench-open div[class*="workbenchPanel"]:not([class*="panelHidden"]) {
3414
+ display: flex !important;
3415
+ visibility: visible !important;
3416
+ pointer-events: auto !important;
3417
+ top: var(--dsh-mobile-header-h, 52px) !important;
3418
+ height: calc(100dvh - var(--dsh-mobile-header-h, 52px)) !important;
3419
+ max-height: calc(100dvh - var(--dsh-mobile-header-h, 52px)) !important;
3420
+ z-index: 50 !important;
3421
+ box-sizing: border-box !important;
3422
+ background: var(--dsw-alias-bg-layer-1, #ffffff) !important;
3423
+ transform: none !important;
3424
+ opacity: 1 !important;
3425
+ }
3426
+
3427
+ /* Tab \u680F\uFF1A\u6A2A\u5411\u6ED1\u52A8\u624B\u52BF + \u5E72\u51C0\u7684\u5E95\u90E8\u8FB9\u6846\uFF0C\u675C\u7EDD\u4E0E\u9876\u90E8\u79FB\u52A8\u7AEF Header \u91CD\u53E0 */
3428
+ div[class*="nArs4W_tabBar"],
3429
+ div[class*="workbench_tabBar"],
3430
+ div[class*="tabBar"] {
3431
+ min-height: 40px !important;
3432
+ height: 40px !important;
3433
+ background: var(--dsw-alias-bg-layer-1, #ffffff) !important;
3434
+ border-bottom: 1px solid rgba(0, 0, 0, 0.08) !important;
3435
+ display: flex !important;
3436
+ align-items: center !important;
3437
+ justify-content: space-between !important;
3438
+ padding: 0 8px !important;
3439
+ gap: 6px !important;
3440
+ overflow: visible !important;
3441
+ box-sizing: border-box !important;
3442
+ }
3443
+
3444
+ div[class*="nArs4W_tabList"],
3445
+ div[class*="tabList"] {
3446
+ display: flex !important;
3447
+ align-items: center !important;
3448
+ gap: 6px !important;
3449
+ flex: 1 1 auto !important;
3450
+ min-width: 0 !important;
3451
+ overflow-x: auto !important;
3452
+ scrollbar-width: none !important;
3453
+ -webkit-overflow-scrolling: touch !important;
3454
+ }
3455
+ div[class*="nArs4W_tabList"]::-webkit-scrollbar,
3456
+ div[class*="tabList"]::-webkit-scrollbar {
3457
+ display: none !important;
3458
+ }
3459
+
3460
+ /* \u5355\u4E2A Tab \u80F6\u56CA\u5316\uFF0C\u6587\u5B57\u8D85\u957F\u81EA\u52A8\u6253\u70B9\uFF0C\u9632\u6B62 Tab \u4E92\u76F8\u6324\u538B */
3461
+ div[class*="nArs4W_tab"],
3462
+ div[class*="workbench_tab"] {
3463
+ flex: 0 0 auto !important;
3464
+ max-width: 170px !important;
3465
+ min-width: 70px !important;
3466
+ height: 30px !important;
3467
+ padding: 0 8px 0 10px !important;
3468
+ border-radius: 6px !important;
3469
+ font-size: 12.5px !important;
3470
+ display: inline-flex !important;
3471
+ align-items: center !important;
3472
+ justify-content: space-between !important;
3473
+ gap: 6px !important;
3474
+ background: var(--dsw-alias-bg-layer-2, #f3f4f6) !important;
3475
+ color: var(--dsw-alias-label-secondary, #6b7280) !important;
3476
+ cursor: pointer !important;
3477
+ user-select: none !important;
3478
+ box-sizing: border-box !important;
3479
+ }
3480
+
3481
+ div[class*="nArs4W_tabActive"],
3482
+ div[class*="workbench_tabActive"] {
3483
+ background: var(--dsw-alias-bg-layer-1, #ffffff) !important;
3484
+ color: var(--dsw-alias-label-primary, #111827) !important;
3485
+ font-weight: 600 !important;
3486
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1) !important;
3487
+ }
3488
+
3489
+ span[class*="nArs4W_tabTitle"],
3490
+ span[class*="tabTitle"] {
3491
+ overflow: hidden !important;
3492
+ text-overflow: ellipsis !important;
3493
+ white-space: nowrap !important;
3494
+ flex: 1 1 auto !important;
3495
+ }
3496
+
3497
+ button[class*="nArs4W_tabClose"],
3498
+ button[class*="tabClose"] {
3499
+ width: 18px !important;
3500
+ height: 18px !important;
3501
+ border-radius: 50% !important;
3502
+ display: inline-flex !important;
3503
+ align-items: center !important;
3504
+ justify-content: center !important;
3505
+ flex-shrink: 0 !important;
3506
+ opacity: 0.6 !important;
3507
+ padding: 0 !important;
3508
+ }
3509
+
3510
+ button[class*="nArs4W_tabBarPlus"],
3511
+ button[class*="tabBarPlus"] {
3512
+ width: 28px !important;
3513
+ height: 28px !important;
3514
+ border-radius: 50% !important;
3515
+ display: inline-flex !important;
3516
+ align-items: center !important;
3517
+ justify-content: center !important;
3518
+ flex-shrink: 0 !important;
3519
+ }
3520
+
3521
+ /* \u79FB\u52A8\u7AEF\u9762\u677F\u53F3\u4E0A\u89D2\u201C\u8FD4\u56DE\u5BF9\u8BDD / \u2715 \u6536\u8D77\u201D\u6309\u94AE\uFF1A\u5E38\u9A7B\u53F3\u4FA7\uFF0C\u9192\u76EE\u4E14\u6613\u89E6\u8FBE */
3522
+ .dsh-mobile-panel-close-btn {
3523
+ margin-left: 8px !important;
3524
+ flex: 0 0 auto !important;
3525
+ height: 28px !important;
3526
+ padding: 0 10px !important;
3527
+ border-radius: 14px !important;
3528
+ font-size: 12px !important;
3529
+ font-weight: 600 !important;
3530
+ background: #2563eb !important;
3531
+ color: #ffffff !important;
3532
+ border: none !important;
3533
+ display: inline-flex !important;
3534
+ align-items: center !important;
3535
+ justify-content: center !important;
3536
+ gap: 4px !important;
3537
+ cursor: pointer !important;
3538
+ user-select: none !important;
3539
+ box-shadow: 0 2px 6px rgba(37, 99, 235, 0.28) !important;
3540
+ white-space: nowrap !important;
3541
+ transition: transform 0.1s, opacity 0.15s !important;
3542
+ z-index: 10 !important;
3543
+ }
3544
+ .dsh-mobile-panel-close-btn:active {
3545
+ transform: scale(0.95) !important;
3546
+ opacity: 0.85 !important;
3547
+ }
3548
+
3392
3549
  /* 3.1 \u4F1A\u8BDD\u5BF9\u8BDD\u5934\u90E8\u9876\u680F\uFF1A\u79FB\u52A8\u7AEF\u9632\u6324\u538B\u4E0E\u7A7A\u95F4\u91CA\u653E\u4F18\u5316\uFF08\u4E25\u683C\u6392\u9664 .dsh-mobile-app-header\uFF09 */
3393
3550
  div[class*="_centerCol"] header,
3394
3551
  header[class*="wSkVaW_header"] {
@@ -3583,19 +3740,53 @@ function injectMobileStyles() {
3583
3740
  transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1);
3584
3741
  overflow-y: auto !important;
3585
3742
  border-right: 1px solid rgba(0, 0, 0, 0.06) !important;
3743
+ pointer-events: auto !important;
3586
3744
  }
3587
3745
  body.dsh-drawer-open div[class*="_sidebarCol"] {
3588
3746
  transform: translateX(0) !important;
3589
3747
  box-shadow: 4px 0 28px rgba(0, 0, 0, 0.25) !important;
3748
+ pointer-events: auto !important;
3590
3749
  }
3591
3750
 
3592
- /* \u62BD\u5C49\u5185\u90E8\uFF1A\u5F3A\u5236 100% \u5BBD\u5EA6\uFF0C\u9690\u85CF\u5197\u4F59\u6298\u53E0\u6309\u94AE */
3751
+ /* \u62BD\u5C49\u5185\u90E8\uFF1A\u5F3A\u5236 100% \u5BBD\u5EA6\uFF0C\u65E0\u8BBA\u5185\u90E8\u72B6\u6001\u5982\u4F55\u5747\u6B63\u5E38\u5C55\u5F00\u5E76\u5C55\u793A DSH \u81EA\u5E26\u7684\u9876\u90E8\u6536\u8D77\u4FA7\u8FB9\u680F\u56FE\u6807 */
3593
3752
  body.dsh-drawer-open div[class*="hHd-Xa_root"] {
3594
3753
  width: 100% !important;
3595
3754
  max-width: 100% !important;
3755
+ min-width: 100% !important;
3756
+ display: flex !important;
3757
+ flex-direction: column !important;
3758
+ }
3759
+ body.dsh-drawer-open div[class*="hHd-Xa_collapsed"] div[class*="hHd-Xa_regionArea"],
3760
+ body.dsh-drawer-open div[class*="hHd-Xa_collapsed"] button[class*="hHd-Xa_newSession"],
3761
+ body.dsh-drawer-open div[class*="hHd-Xa_collapsed"] div[class*="qDHVXG_root"] {
3762
+ display: flex !important;
3763
+ visibility: visible !important;
3764
+ }
3765
+ div[class*="hHd-Xa_logoRow"] {
3766
+ display: flex !important;
3767
+ align-items: center !important;
3768
+ justify-content: space-between !important;
3769
+ width: 100% !important;
3770
+ padding: 10px 14px 6px 14px !important;
3771
+ box-sizing: border-box !important;
3596
3772
  }
3597
3773
  div[class*="hHd-Xa_logoRow"] button[class*="hHd-Xa_toggle"] {
3598
- display: none !important;
3774
+ display: inline-flex !important;
3775
+ align-items: center !important;
3776
+ justify-content: center !important;
3777
+ width: 32px !important;
3778
+ height: 32px !important;
3779
+ border-radius: 8px !important;
3780
+ color: var(--dsw-alias-label-secondary, #6b7280) !important;
3781
+ background: transparent !important;
3782
+ border: none !important;
3783
+ cursor: pointer !important;
3784
+ margin-left: auto !important;
3785
+ transition: background 0.15s, color 0.15s !important;
3786
+ }
3787
+ div[class*="hHd-Xa_logoRow"] button[class*="hHd-Xa_toggle"]:active {
3788
+ background: var(--dsw-alias-bg-layer-2, rgba(0, 0, 0, 0.06)) !important;
3789
+ color: var(--dsw-alias-label-primary, #111827) !important;
3599
3790
  }
3600
3791
 
3601
3792
  /* \u8BBE\u7F6E\u5F39\u7A97\u6253\u5F00\u65F6\u89E3\u9664\u62BD\u5C49\u9690\u85CF\u9650\u5236 */
@@ -3613,8 +3804,6 @@ function injectMobileStyles() {
3613
3804
  position: fixed;
3614
3805
  inset: 0;
3615
3806
  background: rgba(0, 0, 0, 0.4);
3616
- backdrop-filter: blur(4px);
3617
- -webkit-backdrop-filter: blur(4px);
3618
3807
  z-index: 9999;
3619
3808
  display: none !important;
3620
3809
  }
@@ -3866,7 +4055,8 @@ function injectMobileStyles() {
3866
4055
 
3867
4056
  @media (min-width: 769px) {
3868
4057
  .dsh-mobile-app-header,
3869
- .dsh-mobile-backdrop {
4058
+ .dsh-mobile-backdrop,
4059
+ .dsh-mobile-panel-close-btn {
3870
4060
  display: none !important;
3871
4061
  }
3872
4062
  }
@@ -3890,16 +4080,21 @@ function setupMobileExperience(rpcCall, ctx) {
3890
4080
  <line x1="3" y1="15" x2="14" y2="15"></line>
3891
4081
  </svg>
3892
4082
  `;
3893
- leftBtn.onclick = () => {
4083
+ leftBtn.onclick = (e) => {
4084
+ e.stopPropagation();
3894
4085
  const isOpen = document.body.classList.toggle("dsh-drawer-open");
3895
4086
  if (isOpen) {
3896
- const collapsedToggle = document.querySelector('div[class*="hHd-Xa_collapsed"] button[class*="hHd-Xa_toggle"]');
3897
- if (collapsedToggle) collapsedToggle.click();
4087
+ const expand = document.querySelector('button[aria-label*="\u6253\u5F00\u4FA7\u8FB9\u680F"], button[title*="\u6253\u5F00\u4FA7\u8FB9\u680F"]');
4088
+ if (expand) expand.click();
3898
4089
  }
3899
4090
  };
3900
4091
  titleEl = document.createElement("div");
3901
4092
  titleEl.className = "dsh-mobile-header-title";
3902
4093
  titleEl.innerText = "\u65B0\u4F1A\u8BDD";
4094
+ titleEl.onclick = () => {
4095
+ const openPanels = document.querySelectorAll('div[class*="nArs4W_panel"]:not([class*="panelHidden"]), div[class*="workbench_panel"]:not([class*="panelHidden"])');
4096
+ openPanels.forEach((p) => p.classList.add("nArs4W_panelHidden"));
4097
+ };
3903
4098
  const rightBtn = document.createElement("button");
3904
4099
  rightBtn.className = "dsh-header-new-btn";
3905
4100
  rightBtn.title = "\u65B0\u5EFA\u4F1A\u8BDD";
@@ -3911,6 +4106,8 @@ function setupMobileExperience(rpcCall, ctx) {
3911
4106
  </svg>
3912
4107
  `;
3913
4108
  rightBtn.onclick = () => {
4109
+ const openPanels = document.querySelectorAll('div[class*="nArs4W_panel"]:not([class*="panelHidden"]), div[class*="workbench_panel"]:not([class*="panelHidden"])');
4110
+ openPanels.forEach((p) => p.classList.add("nArs4W_panelHidden"));
3914
4111
  const dshNewBtn = document.querySelector('button[aria-label="\u65B0\u5EFA\u4F1A\u8BDD"]');
3915
4112
  if (dshNewBtn) dshNewBtn.click();
3916
4113
  };
@@ -3935,6 +4132,59 @@ function setupMobileExperience(rpcCall, ctx) {
3935
4132
  if (typeof ctx?.sessions?.list?.subscribe === "function") {
3936
4133
  ctx.sessions.list.subscribe(syncMobileTitle);
3937
4134
  }
4135
+ if (typeof ctx?.sessions?.active?.subscribe === "function") {
4136
+ ctx.sessions.active.subscribe(() => {
4137
+ if (document.body.classList.contains("dsh-drawer-open")) {
4138
+ document.body.classList.remove("dsh-drawer-open");
4139
+ }
4140
+ if (typeof window !== "undefined" && window.innerWidth <= 768) {
4141
+ document.body.classList.remove("dsh-workbench-open");
4142
+ const openPanels = document.querySelectorAll('div[class*="nArs4W_panel"]:not([class*="panelHidden"]), div[class*="workbench_panel"]:not([class*="panelHidden"])');
4143
+ openPanels.forEach((p) => p.classList.add("nArs4W_panelHidden"));
4144
+ }
4145
+ });
4146
+ }
4147
+ const ensurePanelCloseButton = () => {
4148
+ if (typeof window === "undefined") return;
4149
+ if (window.innerWidth > 768) {
4150
+ document.querySelectorAll(".dsh-mobile-panel-close-btn").forEach((btn) => btn.remove());
4151
+ return;
4152
+ }
4153
+ const panels = document.querySelectorAll('div[class*="nArs4W_panel"]:not([class*="panelHidden"]), div[class*="workbench_panel"]:not([class*="panelHidden"])');
4154
+ panels.forEach((p) => {
4155
+ const bar = p.querySelector('div[class*="tabBar"], div[class*="nArs4W_tabBar"]');
4156
+ if (bar && !bar.querySelector(".dsh-mobile-panel-close-btn")) {
4157
+ const btn = document.createElement("button");
4158
+ btn.className = "dsh-mobile-panel-close-btn";
4159
+ btn.innerHTML = `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg><span>\u8FD4\u56DE\u5BF9\u8BDD</span>`;
4160
+ btn.onclick = (e) => {
4161
+ e.stopPropagation();
4162
+ document.body.classList.remove("dsh-workbench-open");
4163
+ p.classList.add("nArs4W_panelHidden");
4164
+ const collapseBtn = document.querySelector('button[class*="toggleButton"][aria-label*="\u6536\u8D77"]');
4165
+ if (collapseBtn) collapseBtn.click();
4166
+ };
4167
+ bar.appendChild(btn);
4168
+ }
4169
+ });
4170
+ };
4171
+ const panelObserver = new MutationObserver(ensurePanelCloseButton);
4172
+ panelObserver.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ["class"] });
4173
+ window.addEventListener("resize", ensurePanelCloseButton);
4174
+ document.addEventListener("click", (e) => {
4175
+ if (typeof window === "undefined" || window.innerWidth > 768) return;
4176
+ const trigger = e.target.closest('button[aria-label*="\u9762\u677F"], button[aria-label*="\u5DE5\u4F5C\u533A"], div[class*="toggleCluster"] button, button[class*="subagent"], div[class*="headerActions"] button, div[class*="titleRow"] button');
4177
+ if (trigger && !trigger.classList.contains("dsh-mobile-panel-close-btn") && !trigger.classList.contains("dsh-header-menu-btn") && !trigger.classList.contains("dsh-header-new-btn")) {
4178
+ document.body.classList.add("dsh-workbench-open");
4179
+ }
4180
+ }, true);
4181
+ document.addEventListener("click", (e) => {
4182
+ if (typeof window === "undefined" || window.innerWidth > 768) return;
4183
+ const toggle = e.target.closest('button[aria-label*="\u6536\u8D77\u4FA7\u8FB9\u680F"], button[title*="\u6536\u8D77\u4FA7\u8FB9\u680F"]');
4184
+ if (toggle) {
4185
+ document.body.classList.remove("dsh-drawer-open");
4186
+ }
4187
+ }, true);
3938
4188
  let backdrop = document.querySelector(".dsh-mobile-backdrop");
3939
4189
  if (!backdrop) {
3940
4190
  backdrop = document.createElement("div");
@@ -3971,10 +4221,16 @@ function setupMobileExperience(rpcCall, ctx) {
3971
4221
  return;
3972
4222
  }
3973
4223
  }
3974
- const sessionRow = e.target.closest('div[class*="sessionRow"], div[role="treeitem"]');
4224
+ const sessionRow = e.target.closest('a, div[class*="sessionRow"], div[role="treeitem"]');
3975
4225
  if (sessionRow) {
3976
- document.body.classList.remove("dsh-drawer-open");
4226
+ setTimeout(() => {
4227
+ if (document.body.classList.contains("dsh-drawer-open")) {
4228
+ document.body.classList.remove("dsh-drawer-open");
4229
+ }
4230
+ }, 80);
3977
4231
  }
4232
+ } else if (e.target.classList?.contains("dsh-mobile-backdrop")) {
4233
+ document.body.classList.remove("dsh-drawer-open");
3978
4234
  }
3979
4235
  }, true);
3980
4236
  let longPressTimer = null;
@@ -4570,28 +4826,7 @@ function RemoteDirectoryFlow(props) {
4570
4826
  const outcome = React.useRef(props);
4571
4827
  outcome.current = props;
4572
4828
  const armed = React.useRef(false);
4573
- React.useEffect(() => {
4574
- if (!open) {
4575
- armed.current = false;
4576
- return;
4577
- }
4578
- if (armed.current) return;
4579
- armed.current = true;
4580
- if (isLocalEnvironment()) {
4581
- const pickFn = typeof pick === "function" ? pick : typeof window.__dshClientCtx?.workspaces?.pickDirectory === "function" ? () => window.__dshClientCtx.workspaces.pickDirectory() : null;
4582
- if (pickFn) {
4583
- pickFn().then((chosenPath) => {
4584
- if (chosenPath === null) {
4585
- if (outcome.current?.onCancel) outcome.current.onCancel();
4586
- } else if (chosenPath) {
4587
- if (outcome.current?.onPicked) outcome.current.onPicked(chosenPath);
4588
- }
4589
- }, (err) => {
4590
- if (outcome.current?.onError) outcome.current.onError(err instanceof Error ? err.message : String(err));
4591
- });
4592
- return;
4593
- }
4594
- }
4829
+ const openRemoteModal = () => {
4595
4830
  if (typeof window.__dshOpenRemoteWorkspaceModal === "function") {
4596
4831
  window.__dshOpenRemoteWorkspaceModal(
4597
4832
  (res) => {
@@ -4610,7 +4845,42 @@ function RemoteDirectoryFlow(props) {
4610
4845
  }
4611
4846
  }
4612
4847
  );
4848
+ } else if (outcome.current?.onCancel) {
4849
+ outcome.current.onCancel();
4850
+ }
4851
+ };
4852
+ React.useEffect(() => {
4853
+ if (!open) {
4854
+ armed.current = false;
4855
+ return;
4856
+ }
4857
+ if (armed.current) return;
4858
+ armed.current = true;
4859
+ const isNativeHost = typeof window !== "undefined" && (window.electron || window.__DSH_NATIVE_HOST__);
4860
+ if (isNativeHost || isLocalEnvironment()) {
4861
+ const pickFn = typeof pick === "function" ? pick : typeof window.__dshClientCtx?.workspaces?.pickDirectory === "function" ? () => window.__dshClientCtx.workspaces.pickDirectory() : null;
4862
+ if (pickFn) {
4863
+ try {
4864
+ const promise = pickFn();
4865
+ if (promise && typeof promise.then === "function") {
4866
+ promise.then((chosenPath) => {
4867
+ if (chosenPath === null) {
4868
+ if (outcome.current?.onCancel) outcome.current.onCancel();
4869
+ } else if (chosenPath) {
4870
+ if (outcome.current?.onPicked) outcome.current.onPicked(chosenPath);
4871
+ }
4872
+ }).catch(() => {
4873
+ openRemoteModal();
4874
+ });
4875
+ return;
4876
+ }
4877
+ } catch {
4878
+ openRemoteModal();
4879
+ return;
4880
+ }
4881
+ }
4613
4882
  }
4883
+ openRemoteModal();
4614
4884
  }, [open, pick]);
4615
4885
  return null;
4616
4886
  }