dsh-bridge-gateway 0.1.2 → 0.1.4

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,16 @@
2
2
 
3
3
  本项目 `dsh-bridge-gateway` 是基于 [dsh-bridge](https://github.com/wenbin-wb/dsh-bridge) 的移植增强分支,在保留原版能力之外,重点新增「公网直连网关」。
4
4
 
5
+ ## [0.1.4] - 待发布
6
+
7
+ ### 修复
8
+ - 修复「远程访问」面板在宿主连接未建立时无限卡在「加载中」的问题:为状态拉取增加兜底超时(15s),超时后释放 in-flight 锁并给出可用的「🔄 重试」按钮,避免面板永远无法自行恢复。
9
+
10
+ ## [0.1.3] - 待发布
11
+
12
+ ### 修复
13
+ - 修复移动端对话时底部输入框横向超出屏幕右缘的问题:约束输入卡片与编辑器最大宽度不超视口,长内容自动折行。
14
+
5
15
  ## [0.1.2] - 待发布
6
16
 
7
17
  ### 新增
package/client/client.js CHANGED
@@ -2920,18 +2920,29 @@ function BridgePanel({ rpcCall }) {
2920
2920
  }, [rpcCall, adminToken]);
2921
2921
  const loadInFlightRef = React.useRef(false);
2922
2922
  const loadSeqRef = React.useRef(0);
2923
+ const LOAD_TIMEOUT_MS = 15e3;
2923
2924
  const load = React.useCallback(async (quiet = false) => {
2924
2925
  if (loadInFlightRef.current) return;
2925
2926
  loadInFlightRef.current = true;
2926
2927
  const currentSeq = ++loadSeqRef.current;
2928
+ const timedOut = { done: false };
2927
2929
  try {
2928
- const r = await authRpcCall(BRIDGE_ENDPOINTS.getStatus, {});
2930
+ const result = await Promise.race([
2931
+ authRpcCall(BRIDGE_ENDPOINTS.getStatus, {}),
2932
+ new Promise((resolve) => {
2933
+ setTimeout(() => {
2934
+ timedOut.done = true;
2935
+ resolve(null);
2936
+ }, LOAD_TIMEOUT_MS);
2937
+ })
2938
+ ]);
2939
+ if (timedOut.done) throw new Error("\u52A0\u8F7D\u8D85\u65F6\uFF08\u5BBF\u4E3B\u8FDE\u63A5\u53EF\u80FD\u5728\u5207\u6362\u6216\u672A\u5EFA\u7ACB\uFF09\u3002\u8BF7\u70B9\u51FB\u300C\u{1F504} \u91CD\u8BD5\u300D\u5237\u65B0\u3002");
2929
2940
  if (currentSeq !== loadSeqRef.current) return;
2930
- if (!r?.ok) throw new Error(r?.error?.message ?? "RPC failed");
2931
- setStatus(r.value);
2941
+ if (!result?.ok) throw new Error(result?.error?.message ?? "RPC failed");
2942
+ setStatus(result.value);
2932
2943
  if (!quiet) setErr(null);
2933
2944
  } catch (e) {
2934
- if (currentSeq === loadSeqRef.current) setErr(e.message);
2945
+ if (currentSeq === loadSeqRef.current) setErr(e.message || "\u52A0\u8F7D\u5931\u8D25");
2935
2946
  } finally {
2936
2947
  loadInFlightRef.current = false;
2937
2948
  }
@@ -3345,6 +3356,11 @@ function BridgePanel({ rpcCall }) {
3345
3356
  }
3346
3357
  },
3347
3358
  React.createElement("span", { style: { flex: "1 1 auto" } }, err),
3359
+ !isInterceptionErr && React.createElement("button", {
3360
+ type: "button",
3361
+ style: { ...s.btnGhost, height: 26, fontSize: 12, padding: "0 10px", flexShrink: 0 },
3362
+ onClick: () => load()
3363
+ }, "\u{1F504} \u91CD\u8BD5"),
3348
3364
  isInterceptionErr && React.createElement("button", {
3349
3365
  type: "button",
3350
3366
  style: { ...s.btnPri, background: "#dc2626", color: "#ffffff", height: 26, fontSize: 12, padding: "0 10px", flexShrink: 0 },
@@ -3902,6 +3918,27 @@ function injectMobileStyles() {
3902
3918
  background: var(--dsw-alias-bg-layer-2, #f4f4f7) !important;
3903
3919
  }
3904
3920
 
3921
+ /* \u8F93\u5165\u6846\u6A2A\u5411\u4E0D\u6EA2\u51FA\uFF1A\u5361\u7247\u53CA\u5176\u7F29\u5BB9\u5668\u6700\u5927\u5BBD\u5EA6\u4E0D\u8D85\u8FC7\u89C6\u53E3\uFF0C\u5185\u90E8\u7F16\u8F91\u5668\u53EF\u6536\u7F29\u3001\u957F\u5185\u5BB9\u81EA\u52A8\u6298\u884C */
3922
+ div[class*="uV2eYG_scrollBody"] {
3923
+ max-width: 100vw !important;
3924
+ overflow-x: hidden !important;
3925
+ }
3926
+ div[class*="uV2eYG_card"] {
3927
+ width: 100% !important;
3928
+ max-width: 100% !important;
3929
+ min-width: 0 !important;
3930
+ box-sizing: border-box !important;
3931
+ }
3932
+ div[class*="uV2eYG_card"] div,
3933
+ div[class*="uV2eYG_card"] textarea,
3934
+ div[class*="uV2eYG_card"] [contenteditable="true"] {
3935
+ max-width: 100% !important;
3936
+ min-width: 0 !important;
3937
+ overflow-wrap: anywhere !important;
3938
+ word-break: break-word !important;
3939
+ box-sizing: border-box !important;
3940
+ }
3941
+
3905
3942
  /* \u8F93\u5165\u6846\u5E95\u90E8\u5DE5\u5177\u680F\uFF1A\u5F39\u6027\u81EA\u9002\u5E94\uFF0C\u5F7B\u5E95\u675C\u7EDD\u6743\u9650\u9009\u62E9\u5668(Full access)\u4E0E\u6A21\u578B\u9009\u62E9\u5668\u91CD\u53E0\u78B0\u649E */
3906
3943
  div[class*="uV2eYG_row"] {
3907
3944
  display: flex !important;
package/client/index.js CHANGED
@@ -2597,18 +2597,28 @@ function BridgePanel({ rpcCall }) {
2597
2597
 
2598
2598
  const loadInFlightRef = React.useRef(false);
2599
2599
  const loadSeqRef = React.useRef(0);
2600
+ // 兜底超时:RPC 若一直不返回(连接打不开/宿主未响应),强制结束「加载中」,
2601
+ // 释放 in-flight 锁并提示重试,避免面板无限卡在加载态
2602
+ const LOAD_TIMEOUT_MS = 15000;
2600
2603
  const load = React.useCallback(async (quiet = false) => {
2601
2604
  if (loadInFlightRef.current) return;
2602
2605
  loadInFlightRef.current = true;
2603
2606
  const currentSeq = ++loadSeqRef.current;
2607
+ const timedOut = { done: false };
2604
2608
  try {
2605
- const r = await authRpcCall(BRIDGE_ENDPOINTS.getStatus, {});
2609
+ const result = await Promise.race([
2610
+ authRpcCall(BRIDGE_ENDPOINTS.getStatus, {}),
2611
+ new Promise((resolve) => {
2612
+ setTimeout(() => { timedOut.done = true; resolve(null); }, LOAD_TIMEOUT_MS);
2613
+ }),
2614
+ ]);
2615
+ if (timedOut.done) throw new Error('加载超时(宿主连接可能在切换或未建立)。请点击「🔄 重试」刷新。');
2606
2616
  if (currentSeq !== loadSeqRef.current) return;
2607
- if (!r?.ok) throw new Error(r?.error?.message ?? 'RPC failed');
2608
- setStatus(r.value);
2617
+ if (!result?.ok) throw new Error(result?.error?.message ?? 'RPC failed');
2618
+ setStatus(result.value);
2609
2619
  if (!quiet) setErr(null);
2610
2620
  } catch (e) {
2611
- if (currentSeq === loadSeqRef.current) setErr(e.message);
2621
+ if (currentSeq === loadSeqRef.current) setErr(e.message || '加载失败');
2612
2622
  } finally {
2613
2623
  loadInFlightRef.current = false;
2614
2624
  }
@@ -2977,6 +2987,11 @@ function BridgePanel({ rpcCall }) {
2977
2987
  },
2978
2988
  },
2979
2989
  React.createElement('span', { style: { flex: '1 1 auto' } }, err),
2990
+ !isInterceptionErr && React.createElement('button', {
2991
+ type: 'button',
2992
+ style: { ...s.btnGhost, height: 26, fontSize: 12, padding: '0 10px', flexShrink: 0 },
2993
+ onClick: () => load(),
2994
+ }, '🔄 重试'),
2980
2995
  isInterceptionErr && React.createElement('button', {
2981
2996
  type: 'button',
2982
2997
  style: { ...s.btnPri, background: '#dc2626', color: '#ffffff', height: 26, fontSize: 12, padding: '0 10px', flexShrink: 0 },
@@ -3500,6 +3515,27 @@ function injectMobileStyles() {
3500
3515
  background: var(--dsw-alias-bg-layer-2, #f4f4f7) !important;
3501
3516
  }
3502
3517
 
3518
+ /* 输入框横向不溢出:卡片及其缩容器最大宽度不超过视口,内部编辑器可收缩、长内容自动折行 */
3519
+ div[class*="uV2eYG_scrollBody"] {
3520
+ max-width: 100vw !important;
3521
+ overflow-x: hidden !important;
3522
+ }
3523
+ div[class*="uV2eYG_card"] {
3524
+ width: 100% !important;
3525
+ max-width: 100% !important;
3526
+ min-width: 0 !important;
3527
+ box-sizing: border-box !important;
3528
+ }
3529
+ div[class*="uV2eYG_card"] div,
3530
+ div[class*="uV2eYG_card"] textarea,
3531
+ div[class*="uV2eYG_card"] [contenteditable="true"] {
3532
+ max-width: 100% !important;
3533
+ min-width: 0 !important;
3534
+ overflow-wrap: anywhere !important;
3535
+ word-break: break-word !important;
3536
+ box-sizing: border-box !important;
3537
+ }
3538
+
3503
3539
  /* 输入框底部工具栏:弹性自适应,彻底杜绝权限选择器(Full access)与模型选择器重叠碰撞 */
3504
3540
  div[class*="uV2eYG_row"] {
3505
3541
  display: flex !important;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-bridge-gateway",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "以 dsh-bridge 为根基、融合 remote-gateway 能力的 DSH 远程访问网关:局域网 / Cloudflare 隧道 / 自建隧道之外,新增「公网直连网关」(0.0.0.0 监听 + 自带 HTTPS 自签证书 + 强制登录门禁),解决移动端外网直连操控电脑。",
5
5
  "releaseNotes": "【dsh-bridge-gateway】以 dsh-bridge 为根基、融合 remote-gateway 能力的 DSH 远程访问网关\n• ➕ 公网访问区:隧道 / 直连网关 二选一,网关全部配置项可在界面配置\n• ➕ 直连网关:0.0.0.0:<端口> 自带 HTTPS(自签证书) + 强制 public_only 门禁,复用 AuthManager,链路打通到 DSH loopback\n• 📱 修复移动端选择模型时界面溢出屏幕的问题\n• 默认不启用,安装后原有局域网/隧道/IM 行为完全不变",
6
6
  "type": "module",