dsh-remote-web 0.7.2-beta.2 → 0.7.3-beta.1
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-remote-web",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3-beta.1",
|
|
4
4
|
"description": "公网远程控制 DeepSeek Harness(dsh web):安装即得专属加密地址,人在外面也能用手机访问电脑上的 dsh——无需同一局域网/WiFi、无需公网 IP 与内网穿透,全程加密;手机端 100% 还原电脑体验(对话/工具/审批/设置)。技术用户可选自建服务,流量走自己的服务器。Remote control DeepSeek Harness (dsh web) from anywhere over the public internet — install, get an encrypted URL, use it from your phone on any network (dual-half cordis plugin: Settings panel + same-origin /dsh-remote routes). (2026-09 由 dsh-remote-ui 更名 / renamed from dsh-remote-ui; dsh-remote 的 dsh web 插件半,不是纯 UI/皮肤插件)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -554,6 +554,40 @@ div.dsh-ma-menu .dsh-ma-load-warn[hidden] { display: none !important; }
|
|
|
554
554
|
div.dsh-ma-menu .dsh-ma-load-warn .ico { flex: none; }
|
|
555
555
|
div.dsh-ma-menu .dsh-ma-load-warn .txt { min-width: 0; overflow-wrap: anywhere; }
|
|
556
556
|
|
|
557
|
+
/* ===== 旧浏览器兼容提示条(2026-10-18 真机事故)=====
|
|
558
|
+
事故:iOS 16.1 / 17.3 的 Safari 打不开镜像页(缺 Promise.withResolvers / AbortSignal.any /
|
|
559
|
+
甚至不支持新语法)。补不了的 API 已在注入体最前面尽量补齐;补不了的(语法级)用这条**顶部轻量条**
|
|
560
|
+
明确告诉用户"升级系统或换浏览器",而不是让他干等。
|
|
561
|
+
安全边界:只占顶部一条、限高可滚、可关闭、不覆盖整屏、z-index 低于 FAB(400)——
|
|
562
|
+
和「加载卡住」底部条同一套安全方向:判断错最坏只是多一条可关的提示,绝不遮挡主界面。 */
|
|
563
|
+
div.dsh-ma-compatbar {
|
|
564
|
+
position: fixed; top: 0; left: 0; right: 0; z-index: 398;
|
|
565
|
+
box-sizing: border-box;
|
|
566
|
+
padding: calc(10px + env(safe-area-inset-top, 0px)) 14px 10px;
|
|
567
|
+
max-height: min(42vh, 300px); overflow: auto;
|
|
568
|
+
background: #fff8e6; color: #6b4a00;
|
|
569
|
+
border-bottom: 1px solid rgba(154, 103, 0, .35);
|
|
570
|
+
box-shadow: 0 2px 10px rgba(0, 0, 0, .12);
|
|
571
|
+
font: 400 12.5px/1.65 -apple-system, system-ui, sans-serif;
|
|
572
|
+
text-align: left;
|
|
573
|
+
}
|
|
574
|
+
div.dsh-ma-compatbar[hidden] { display: none !important; }
|
|
575
|
+
div.dsh-ma-compatbar .dsh-ma-compatbar-title { font-weight: 700; font-size: 13.5px; margin-bottom: 4px; }
|
|
576
|
+
div.dsh-ma-compatbar .dsh-ma-compatbar-detail { color: #7a5a10; word-break: break-word; }
|
|
577
|
+
div.dsh-ma-compatbar .dsh-ma-compatbar-hint { margin-top: 4px; color: #7a5a10; }
|
|
578
|
+
div.dsh-ma-compatbar .dsh-ma-compatbar-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
|
|
579
|
+
div.dsh-ma-compatbar button {
|
|
580
|
+
min-height: 40px; padding: 8px 12px; border-radius: 8px;
|
|
581
|
+
border: .5px solid rgba(154, 103, 0, .45); background: #fffdf5; color: #6b4a00;
|
|
582
|
+
font: 600 12.5px/1 -apple-system, system-ui, sans-serif; cursor: pointer;
|
|
583
|
+
}
|
|
584
|
+
div.dsh-ma-compatbar button.primary { background: #9a6700; border-color: #9a6700; color: #fff; }
|
|
585
|
+
div.dsh-ma-compatbar button.dsh-ma-compatbar-x {
|
|
586
|
+
position: absolute; top: calc(6px + env(safe-area-inset-top, 0px)); right: 8px;
|
|
587
|
+
min-height: 32px; min-width: 36px; padding: 4px 8px; border: none; background: transparent;
|
|
588
|
+
color: #7a5a10; font-size: 14px;
|
|
589
|
+
}
|
|
590
|
+
|
|
557
591
|
/* ===== 加载疑似卡住时的**可操作**底部提示条(2026-10-17 事故新增) =====
|
|
558
592
|
事故现场:用户本机 ~/.dsh-remote(bridge 运行时 + 账号配对信息/设备密钥)被误删 → bridge 起不来、
|
|
559
593
|
手机端镜像页第一次加载**干等 47 秒**才报错;页面上只有悬浮菜单里一行"已等 N 秒…复制给客服",
|
|
@@ -663,6 +697,112 @@ const FAB_PHONE_SVG =
|
|
|
663
697
|
const SCRIPT = `(() => {
|
|
664
698
|
"use strict";
|
|
665
699
|
|
|
700
|
+
/* ===DSH-MA-COMPAT-START===
|
|
701
|
+
旧浏览器兼容层(2026-10-18 真机事故)。
|
|
702
|
+
事故:iPhone iOS 16.1 / 17.3 的 Safari 打开镜像页永远卡住、出不来对话界面 ——
|
|
703
|
+
诊断上报抓到真实错误(mirror_diag):
|
|
704
|
+
· TypeError: Promise.withResolvers is not a function (Safari 17.4+ 才有)
|
|
705
|
+
· AbortSignal.any is not a function (Safari 17.4+ 才有)
|
|
706
|
+
· SyntaxError: Unexpected token '{'(iOS 16.1 连新语法都解析不了 → 只能提示升级)
|
|
707
|
+
DSH web 前端按现代浏览器构建;镜像页在**它的模块执行之前**补齐缺失的运行时 API,
|
|
708
|
+
能让 iOS 17.x / 老 Safari 直接可用;补不了的(语法级不支持)明确提示用户升级/换浏览器。
|
|
709
|
+
注入体纪律:本段不得出现反引号;只用 ES5 语法(老 Safari 也要能解析这段)。 */
|
|
710
|
+
|
|
711
|
+
// ① 运行时 API 补齐(全部有 typeof 守卫,绝不覆盖已有实现)
|
|
712
|
+
(function () {
|
|
713
|
+
try {
|
|
714
|
+
if (typeof Promise !== "undefined" && typeof Promise.withResolvers !== "function") {
|
|
715
|
+
Object.defineProperty(Promise, "withResolvers", {
|
|
716
|
+
configurable: true, writable: true,
|
|
717
|
+
value: function () {
|
|
718
|
+
var resolve, reject;
|
|
719
|
+
var promise = new Promise(function (res, rej) { resolve = res; reject = rej; });
|
|
720
|
+
return { promise: promise, resolve: resolve, reject: reject };
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
} catch (e) { /* 补齐失败不影响页面 */ }
|
|
725
|
+
try {
|
|
726
|
+
if (typeof AbortSignal !== "undefined" && typeof AbortSignal.any !== "function") {
|
|
727
|
+
AbortSignal.any = function (signals) {
|
|
728
|
+
var ctrl = new AbortController();
|
|
729
|
+
var list = [];
|
|
730
|
+
try { list = Array.prototype.slice.call(signals || []); } catch (e2) { list = []; }
|
|
731
|
+
for (var i = 0; i < list.length; i++) {
|
|
732
|
+
try { if (list[i] && list[i].aborted) { ctrl.abort(list[i].reason); return ctrl.signal; } } catch (e3) { /* 忽略 */ }
|
|
733
|
+
}
|
|
734
|
+
function onAbort(e) {
|
|
735
|
+
var sig = e && e.target;
|
|
736
|
+
try { ctrl.abort(sig && sig.reason); } catch (e4) { try { ctrl.abort(); } catch (e5) { /* 忽略 */ } }
|
|
737
|
+
}
|
|
738
|
+
for (var j = 0; j < list.length; j++) {
|
|
739
|
+
try {
|
|
740
|
+
if (list[j] && typeof list[j].addEventListener === "function") {
|
|
741
|
+
list[j].addEventListener("abort", onAbort, { once: true });
|
|
742
|
+
}
|
|
743
|
+
} catch (e6) { /* 忽略 */ }
|
|
744
|
+
}
|
|
745
|
+
return ctrl.signal;
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
} catch (e) { /* 同上 */ }
|
|
749
|
+
try {
|
|
750
|
+
if (typeof AbortSignal !== "undefined" && typeof AbortSignal.timeout !== "function") {
|
|
751
|
+
AbortSignal.timeout = function (ms) {
|
|
752
|
+
var ctrl = new AbortController();
|
|
753
|
+
setTimeout(function () {
|
|
754
|
+
try { ctrl.abort(new Error("TimeoutError")); } catch (e2) { try { ctrl.abort(); } catch (e3) { /* 忽略 */ } }
|
|
755
|
+
}, Number(ms) || 0);
|
|
756
|
+
return ctrl.signal;
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
} catch (e) { /* 同上 */ }
|
|
760
|
+
try {
|
|
761
|
+
if (typeof Array.prototype.findLast !== "function") {
|
|
762
|
+
Array.prototype.findLast = function (fn, thisArg) {
|
|
763
|
+
for (var i = this.length - 1; i >= 0; i--) {
|
|
764
|
+
if (fn.call(thisArg, this[i], i, this)) return this[i];
|
|
765
|
+
}
|
|
766
|
+
return undefined;
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
if (typeof Array.prototype.findLastIndex !== "function") {
|
|
770
|
+
Array.prototype.findLastIndex = function (fn, thisArg) {
|
|
771
|
+
for (var i = this.length - 1; i >= 0; i--) {
|
|
772
|
+
if (fn.call(thisArg, this[i], i, this)) return i;
|
|
773
|
+
}
|
|
774
|
+
return -1;
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
if (typeof Array.prototype.toSorted !== "function") {
|
|
778
|
+
Array.prototype.toSorted = function (cmp) { return this.slice().sort(cmp); };
|
|
779
|
+
}
|
|
780
|
+
if (typeof Array.prototype.toReversed !== "function") {
|
|
781
|
+
Array.prototype.toReversed = function () { return this.slice().reverse(); };
|
|
782
|
+
}
|
|
783
|
+
if (typeof Object.hasOwn !== "function") {
|
|
784
|
+
Object.hasOwn = function (o, k) { return Object.prototype.hasOwnProperty.call(o, k); };
|
|
785
|
+
}
|
|
786
|
+
} catch (e) { /* 同上 */ }
|
|
787
|
+
})();
|
|
788
|
+
|
|
789
|
+
// ② 探测"补不了的":语法级不支持(老 Safari 解析不到新版构建)→ 只能提示升级/换浏览器
|
|
790
|
+
var maCompat = (function () {
|
|
791
|
+
var why = "";
|
|
792
|
+
try {
|
|
793
|
+
// 静态块(Safari 16.4+)。CSP 禁 eval 时抛的是 EvalError 而非 SyntaxError —— 不能误判
|
|
794
|
+
new Function("class __DshCompatProbe { static { this.ok = 1; } }");
|
|
795
|
+
} catch (e) {
|
|
796
|
+
if (e && e.name === "SyntaxError") why = "系统版本过旧(不支持现代 JavaScript 语法)";
|
|
797
|
+
}
|
|
798
|
+
var missing = [];
|
|
799
|
+
try { if (typeof Promise.withResolvers !== "function") missing.push("Promise.withResolvers"); } catch (e2) { /* 忽略 */ }
|
|
800
|
+
try { if (typeof AbortSignal !== "undefined" && typeof AbortSignal.any !== "function") missing.push("AbortSignal.any"); } catch (e3) { /* 忽略 */ }
|
|
801
|
+
if (!why && missing.length) why = "缺少浏览器能力:" + missing.join(" / ");
|
|
802
|
+
return { ok: !why, why: why };
|
|
803
|
+
})();
|
|
804
|
+
/* ===DSH-MA-COMPAT-END=== */
|
|
805
|
+
|
|
666
806
|
/* ===== 【0.6.16 已删除】首屏加载提示层(0.6.14 引入,0.6.15 仍在)=====
|
|
667
807
|
为什么删掉(用户实测,2026-09-25):「界面都已经加载完了,十几秒就把插件的接口加载好了,
|
|
668
808
|
但这个遮罩一直不消失」—— 截图里官方 UI 就在下面透出来,秒表走到 187 秒。
|
|
@@ -738,10 +878,121 @@ const SCRIPT = `(() => {
|
|
|
738
878
|
⚠️ 12 秒是"开始提示"的阈值,不是"判定失败":限速放开后正常首屏 1~3 秒,
|
|
739
879
|
12 秒既能及时帮到用户,也不至于刚打开就吓人(宁可晚一点,也别误报)。
|
|
740
880
|
⚠️ 注入体是模板字符串:本段注释/文案**一律用「」**,不得出现反引号。 */
|
|
881
|
+
try {
|
|
882
|
+
maWireCompatErrors();
|
|
883
|
+
if (maCompat && maCompat.ok === false) {
|
|
884
|
+
var maCompatKick = function () { setTimeout(function () { maShowCompatBar(); }, 800); };
|
|
885
|
+
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", maCompatKick);
|
|
886
|
+
else maCompatKick();
|
|
887
|
+
}
|
|
888
|
+
} catch (e) { /* 兼容提示失败绝不影响主功能 */ }
|
|
889
|
+
|
|
741
890
|
var maLoadWatch = { startedAt: Date.now(), slow: false, wired: false, bound: false, warnEl: null, warnTxt: null,
|
|
742
891
|
reported: false, reportMs: 0,
|
|
743
892
|
// 2026-10-17:底部可操作提示条(barEl = 懒创建的节点;barTimer = 秒数刷新)
|
|
744
893
|
barEl: null, barTxt: null, barTimer: 0, dismissed: false };
|
|
894
|
+
/* ===== 旧浏览器兼容提示条(2026-10-18)=====
|
|
895
|
+
只有两种触发:① 注入体最前面的 maCompat 探测到"补不了的"(语法级不支持);
|
|
896
|
+
② 页面脚本报出 SyntaxError / is not a function 这类典型兼容错误且界面还没就绪。
|
|
897
|
+
只显示一条、可关闭、不遮挡整屏;提供「返回设备列表」与「复制诊断信息」两个出口。 */
|
|
898
|
+
var maCompatBar = { el: null, txt: null, detail: null, dismissed: false, wired: false };
|
|
899
|
+
function maCompatReason(msg) {
|
|
900
|
+
var why = "";
|
|
901
|
+
try { why = String(msg ? msg : (maCompat && maCompat.why) || "").slice(0, 200); } catch (e) { why = ""; }
|
|
902
|
+
return why;
|
|
903
|
+
}
|
|
904
|
+
function maMountCompatBar(reason) {
|
|
905
|
+
try {
|
|
906
|
+
if (maCompatBar.dismissed) return;
|
|
907
|
+
if (maCompatBar.el && maCompatBar.el.parentNode) { maCompatRender(reason); return; }
|
|
908
|
+
if (!document.body) return;
|
|
909
|
+
var bar = document.createElement("div");
|
|
910
|
+
bar.className = "dsh-ma-compatbar";
|
|
911
|
+
bar.setAttribute("role", "alert");
|
|
912
|
+
|
|
913
|
+
var title = document.createElement("div");
|
|
914
|
+
title.className = "dsh-ma-compatbar-title";
|
|
915
|
+
title.textContent = "浏览器版本过旧,远程界面可能无法打开";
|
|
916
|
+
|
|
917
|
+
var detail = document.createElement("div");
|
|
918
|
+
detail.className = "dsh-ma-compatbar-detail";
|
|
919
|
+
|
|
920
|
+
var hint = document.createElement("div");
|
|
921
|
+
hint.className = "dsh-ma-compatbar-hint";
|
|
922
|
+
hint.textContent = "建议:把系统/浏览器升级到最新(iOS 17.4 及以上),或改用 Chrome、电脑端访问。";
|
|
923
|
+
|
|
924
|
+
var row = document.createElement("div");
|
|
925
|
+
row.className = "dsh-ma-compatbar-row";
|
|
926
|
+
var back = document.createElement("button");
|
|
927
|
+
back.className = "primary";
|
|
928
|
+
back.textContent = "返回设备列表";
|
|
929
|
+
back.addEventListener("click", function () {
|
|
930
|
+
try { location.href = "/app/"; } catch (e) { try { location.assign("/app/"); } catch (e2) { /* 忽略 */ } }
|
|
931
|
+
});
|
|
932
|
+
var copy = document.createElement("button");
|
|
933
|
+
copy.textContent = "复制诊断信息";
|
|
934
|
+
copy.addEventListener("click", function () {
|
|
935
|
+
var line = "";
|
|
936
|
+
try {
|
|
937
|
+
var ua = String((navigator && navigator.userAgent) || "").slice(0, 120);
|
|
938
|
+
line = "dsh-remote 浏览器兼容问题:缺少能力 = " + maCompatReason(reason) + ";UA=" + ua + ";页面=" + String(location.href || "").slice(0, 120);
|
|
939
|
+
} catch (e) { line = "dsh-remote 浏览器兼容问题(无法读取 UA)"; }
|
|
940
|
+
try {
|
|
941
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
942
|
+
navigator.clipboard.writeText(line).then(function () { hint.textContent = "已复制诊断信息,可直接发给客服。"; }, function () { hint.textContent = line; });
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
} catch (e2) { /* 落到下面 */ }
|
|
946
|
+
hint.textContent = line;
|
|
947
|
+
});
|
|
948
|
+
var x = document.createElement("button");
|
|
949
|
+
x.className = "dsh-ma-compatbar-x";
|
|
950
|
+
x.setAttribute("aria-label", "关闭提示");
|
|
951
|
+
x.textContent = "✕";
|
|
952
|
+
x.addEventListener("click", function () { maCompatBar.dismissed = true; try { bar.hidden = true; } catch (e) { /* 忽略 */ } });
|
|
953
|
+
|
|
954
|
+
row.appendChild(back);
|
|
955
|
+
row.appendChild(copy);
|
|
956
|
+
bar.appendChild(x);
|
|
957
|
+
bar.appendChild(title);
|
|
958
|
+
bar.appendChild(detail);
|
|
959
|
+
bar.appendChild(hint);
|
|
960
|
+
bar.appendChild(row);
|
|
961
|
+
document.body.appendChild(bar);
|
|
962
|
+
maCompatBar.el = bar; maCompatBar.txt = title; maCompatBar.detail = detail;
|
|
963
|
+
maCompatRender(reason);
|
|
964
|
+
} catch (e) { /* 提示失败绝不影响主功能 */ }
|
|
965
|
+
}
|
|
966
|
+
function maCompatRender(reason) {
|
|
967
|
+
try {
|
|
968
|
+
if (!maCompatBar.el) return;
|
|
969
|
+
var why = maCompatReason(reason);
|
|
970
|
+
if (why && maCompatBar.detail) maCompatBar.detail.textContent = "检测到:" + why;
|
|
971
|
+
maCompatBar.el.hidden = !!maCompatBar.dismissed;
|
|
972
|
+
} catch (e) { /* 忽略 */ }
|
|
973
|
+
}
|
|
974
|
+
/** 显示兼容提示(带就绪保护:界面已经能用了就不再打扰)。 */
|
|
975
|
+
function maShowCompatBar(reason) {
|
|
976
|
+
try {
|
|
977
|
+
if (maCompatBar.dismissed) return;
|
|
978
|
+
if (maLoadReadyNow()) return; // 页面其实正常 → 不吓人
|
|
979
|
+
maMountCompatBar(reason);
|
|
980
|
+
} catch (e) { /* 忽略 */ }
|
|
981
|
+
}
|
|
982
|
+
/** 页面脚本报出"典型兼容错误"时也要提示(有些内联报错早于我们的探测)。 */
|
|
983
|
+
function maWireCompatErrors() {
|
|
984
|
+
try {
|
|
985
|
+
window.addEventListener("error", function (e) {
|
|
986
|
+
try {
|
|
987
|
+
var msg = String((e && e.message) || "");
|
|
988
|
+
if (!/SyntaxError|Unexpected token|is not a function|Cannot read propert/i.test(msg)) return;
|
|
989
|
+
if (!/(withResolvers|AbortSignal|Promise|assets\\/|plugins\\/)/i.test(msg + " " + String((e.target && (e.target.src || e.target.href)) || ""))) return;
|
|
990
|
+
maShowCompatBar(msg.slice(0, 200));
|
|
991
|
+
} catch (e2) { /* 忽略 */ }
|
|
992
|
+
}, true);
|
|
993
|
+
} catch (e) { /* 忽略 */ }
|
|
994
|
+
}
|
|
995
|
+
|
|
745
996
|
/** 看门狗阈值(毫秒):12 秒仍认不出官方界面就开始提示。测试按常量断言,不要散落魔法数。 */
|
|
746
997
|
var MA_LOAD_WATCH_MS = 12 * 1000;
|
|
747
998
|
/** 官方界面是否已经可用(只看最强信号;只用于决定"要不要多显示一行字")。 */
|