dsh-deeppilot 0.9.1 → 0.9.2
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/docs/DSH_RELEASE_MEMORY.md +176 -0
- package/lib/client.js +488 -209
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +219 -85
- package/lib/index.js +6905 -6012
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -3667,7 +3667,330 @@ window.__ModuleLoader__.load({
|
|
|
3667
3667
|
return typeof value === "number" && Number.isInteger(value) && value >= 1024 && value <= 65535 ? value : DEFAULT_LOCAL_PORT;
|
|
3668
3668
|
}
|
|
3669
3669
|
//#endregion
|
|
3670
|
+
//#region src/client/settings-props.ts
|
|
3671
|
+
const MISSING_REPORT_HOOK = () => ({
|
|
3672
|
+
status: "loading",
|
|
3673
|
+
report: null,
|
|
3674
|
+
message: ""
|
|
3675
|
+
});
|
|
3676
|
+
const MISSING_ENABLED_HOOK = () => ({
|
|
3677
|
+
status: "unavailable",
|
|
3678
|
+
enabled: true
|
|
3679
|
+
});
|
|
3680
|
+
const MISSING_LOCAL_ENABLED_HOOK = () => ({
|
|
3681
|
+
status: "unavailable",
|
|
3682
|
+
enabled: true
|
|
3683
|
+
});
|
|
3684
|
+
const MISSING_LOCAL_PORT_HOOK = () => ({
|
|
3685
|
+
status: "unavailable",
|
|
3686
|
+
value: DEFAULT_LOCAL_PORT
|
|
3687
|
+
});
|
|
3688
|
+
const MISSING_REMOTE_ENABLED_HOOK = () => ({
|
|
3689
|
+
status: "unavailable",
|
|
3690
|
+
enabled: false
|
|
3691
|
+
});
|
|
3692
|
+
const MISSING_REMOTE_LIMIT_HOOK = () => ({
|
|
3693
|
+
status: "unavailable",
|
|
3694
|
+
value: 8
|
|
3695
|
+
});
|
|
3696
|
+
const MISSING_DEBUG_HOOK = () => ({
|
|
3697
|
+
status: "unavailable",
|
|
3698
|
+
enabled: false
|
|
3699
|
+
});
|
|
3700
|
+
/** 诊断行的 key,集中在此便于对账(tests/settings-props.test.ts 逐条断言)。 */
|
|
3701
|
+
const DIAG_KEY = {
|
|
3702
|
+
reportHook: "diag.missingReportHook",
|
|
3703
|
+
enabledHook: "diag.missingEnabledHook",
|
|
3704
|
+
localEnabledHook: "diag.missingLocalEnabledHook",
|
|
3705
|
+
localPortHook: "diag.missingLocalPortHook",
|
|
3706
|
+
remoteEnabledHook: "diag.missingRemoteEnabledHook",
|
|
3707
|
+
remoteLimitHook: "diag.missingRemoteLimitHook",
|
|
3708
|
+
debugHook: "diag.missingDebugHook",
|
|
3709
|
+
refresh: "diag.missingRefresh",
|
|
3710
|
+
reveal: "diag.missingReveal",
|
|
3711
|
+
rotate: "diag.missingRotate",
|
|
3712
|
+
rename: "diag.missingRename",
|
|
3713
|
+
testRelay: "diag.missingTestRelay",
|
|
3714
|
+
testPush: "diag.missingTestPush",
|
|
3715
|
+
setEnabled: "diag.missingSetEnabled",
|
|
3716
|
+
setLocal: "diag.missingSetLocal",
|
|
3717
|
+
setLocalPort: "diag.missingSetLocalPort",
|
|
3718
|
+
setRemote: "diag.missingSetRemote",
|
|
3719
|
+
setRemoteLimit: "diag.missingSetRemoteLimit",
|
|
3720
|
+
setDebug: "diag.missingSetDebug",
|
|
3721
|
+
settingsUnavailable: "diag.settingsUnavailable"
|
|
3722
|
+
};
|
|
3723
|
+
const CAPABILITY_BY_PROP = [
|
|
3724
|
+
["refresh", "refresh"],
|
|
3725
|
+
["beginPairing", "beginPairing"],
|
|
3726
|
+
["revokeDevice", "revokeDevice"],
|
|
3727
|
+
["setDeviceName", "setDeviceName"],
|
|
3728
|
+
["testRelay", "testRelay"],
|
|
3729
|
+
["testPush", "testPush"],
|
|
3730
|
+
["setEnabled", "setDeepPilotEnabled"],
|
|
3731
|
+
["setLocalEnabled", "setDeepPilotLocalEnabled"],
|
|
3732
|
+
["setLocalPort", "setDeepPilotLocalPort"],
|
|
3733
|
+
["setRemoteEnabled", "setDeepPilotRemoteEnabled"],
|
|
3734
|
+
["setRemoteLimit", "setDeepPilotRemoteConnectionLimit"],
|
|
3735
|
+
["setDebug", "setDeepPilotDebug"]
|
|
3736
|
+
];
|
|
3737
|
+
const isFn = (value) => typeof value === "function";
|
|
3738
|
+
/**
|
|
3739
|
+
* 把宿主注入的原始对象归一化成页面可消费的面。
|
|
3740
|
+
*
|
|
3741
|
+
* 逐项复刻迁移前页面内的行为:hook 缺失换 fallback 并记诊断;函数缺失则该
|
|
3742
|
+
* 能力不进 `can` 并记诊断;诊断顺序与原先的 `diag[]` 完全一致(宿主过期时
|
|
3743
|
+
* 用户看到的技术诊断因此一字不差)。
|
|
3744
|
+
*/
|
|
3745
|
+
function normalizeSettingsPageProps(raw) {
|
|
3746
|
+
const source = raw ?? {};
|
|
3747
|
+
const t = typeof source.t === "function" ? source.t : ((key) => key);
|
|
3748
|
+
const diagnostics = [];
|
|
3749
|
+
const can = /* @__PURE__ */ new Set();
|
|
3750
|
+
const useReport = isFn(source.useDeepPilotReport) ? source.useDeepPilotReport : MISSING_REPORT_HOOK;
|
|
3751
|
+
const useEnabled = isFn(source.useDeepPilotEnabled) ? source.useDeepPilotEnabled : MISSING_ENABLED_HOOK;
|
|
3752
|
+
const useLocalEnabled = isFn(source.useDeepPilotLocalEnabled) ? source.useDeepPilotLocalEnabled : MISSING_LOCAL_ENABLED_HOOK;
|
|
3753
|
+
const useLocalPort = isFn(source.useDeepPilotLocalPort) ? source.useDeepPilotLocalPort : MISSING_LOCAL_PORT_HOOK;
|
|
3754
|
+
const useRemoteEnabled = isFn(source.useDeepPilotRemoteEnabled) ? source.useDeepPilotRemoteEnabled : MISSING_REMOTE_ENABLED_HOOK;
|
|
3755
|
+
const useRemoteLimit = isFn(source.useDeepPilotRemoteConnectionLimit) ? source.useDeepPilotRemoteConnectionLimit : MISSING_REMOTE_LIMIT_HOOK;
|
|
3756
|
+
const useDebug = isFn(source.useDeepPilotDebug) ? source.useDeepPilotDebug : MISSING_DEBUG_HOOK;
|
|
3757
|
+
if (!isFn(source.useDeepPilotReport)) diagnostics.push(t(DIAG_KEY.reportHook));
|
|
3758
|
+
if (!isFn(source.useDeepPilotEnabled)) diagnostics.push(t(DIAG_KEY.enabledHook));
|
|
3759
|
+
if (!isFn(source.refresh)) diagnostics.push(t(DIAG_KEY.refresh));
|
|
3760
|
+
if (!isFn(source.beginPairing)) diagnostics.push(t(DIAG_KEY.reveal));
|
|
3761
|
+
if (!isFn(source.revokeDevice)) diagnostics.push(t(DIAG_KEY.rotate));
|
|
3762
|
+
if (!isFn(source.setDeviceName)) diagnostics.push(t(DIAG_KEY.rename));
|
|
3763
|
+
if (!isFn(source.testRelay)) diagnostics.push(t(DIAG_KEY.testRelay));
|
|
3764
|
+
if (!isFn(source.testPush)) diagnostics.push(t(DIAG_KEY.testPush));
|
|
3765
|
+
if (!isFn(source.setDeepPilotEnabled)) diagnostics.push(t(DIAG_KEY.setEnabled));
|
|
3766
|
+
if (!isFn(source.useDeepPilotLocalEnabled)) diagnostics.push(t(DIAG_KEY.localEnabledHook));
|
|
3767
|
+
if (!isFn(source.useDeepPilotLocalPort)) diagnostics.push(t(DIAG_KEY.localPortHook));
|
|
3768
|
+
if (!isFn(source.setDeepPilotLocalEnabled)) diagnostics.push(t(DIAG_KEY.setLocal));
|
|
3769
|
+
if (!isFn(source.setDeepPilotLocalPort)) diagnostics.push(t(DIAG_KEY.setLocalPort));
|
|
3770
|
+
if (!isFn(source.useDeepPilotRemoteEnabled)) diagnostics.push(t(DIAG_KEY.remoteEnabledHook));
|
|
3771
|
+
if (!isFn(source.setDeepPilotRemoteEnabled)) diagnostics.push(t(DIAG_KEY.setRemote));
|
|
3772
|
+
if (!isFn(source.useDeepPilotRemoteConnectionLimit)) diagnostics.push(t(DIAG_KEY.remoteLimitHook));
|
|
3773
|
+
if (!isFn(source.setDeepPilotRemoteConnectionLimit)) diagnostics.push(t(DIAG_KEY.setRemoteLimit));
|
|
3774
|
+
if (!isFn(source.useDeepPilotDebug)) diagnostics.push(t(DIAG_KEY.debugHook));
|
|
3775
|
+
if (!isFn(source.setDeepPilotDebug)) diagnostics.push(t(DIAG_KEY.setDebug));
|
|
3776
|
+
for (const [capability, prop] of CAPABILITY_BY_PROP) if (isFn(source[prop])) can.add(capability);
|
|
3777
|
+
return {
|
|
3778
|
+
props: {
|
|
3779
|
+
useDeepPilotReport: useReport,
|
|
3780
|
+
useDeepPilotEnabled: useEnabled,
|
|
3781
|
+
useDeepPilotLocalEnabled: useLocalEnabled,
|
|
3782
|
+
useDeepPilotLocalPort: useLocalPort,
|
|
3783
|
+
useDeepPilotRemoteEnabled: useRemoteEnabled,
|
|
3784
|
+
useDeepPilotRemoteConnectionLimit: useRemoteLimit,
|
|
3785
|
+
useDeepPilotDebug: useDebug,
|
|
3786
|
+
t,
|
|
3787
|
+
refresh: isFn(source.refresh) ? source.refresh : void 0,
|
|
3788
|
+
beginPairing: isFn(source.beginPairing) ? source.beginPairing : void 0,
|
|
3789
|
+
revokeDevice: isFn(source.revokeDevice) ? source.revokeDevice : void 0,
|
|
3790
|
+
setDeviceName: isFn(source.setDeviceName) ? source.setDeviceName : void 0,
|
|
3791
|
+
testRelay: isFn(source.testRelay) ? source.testRelay : void 0,
|
|
3792
|
+
testPush: isFn(source.testPush) ? source.testPush : void 0,
|
|
3793
|
+
setDeepPilotEnabled: isFn(source.setDeepPilotEnabled) ? source.setDeepPilotEnabled : void 0,
|
|
3794
|
+
setDeepPilotLocalEnabled: isFn(source.setDeepPilotLocalEnabled) ? source.setDeepPilotLocalEnabled : void 0,
|
|
3795
|
+
setDeepPilotLocalPort: isFn(source.setDeepPilotLocalPort) ? source.setDeepPilotLocalPort : void 0,
|
|
3796
|
+
setDeepPilotRemoteEnabled: isFn(source.setDeepPilotRemoteEnabled) ? source.setDeepPilotRemoteEnabled : void 0,
|
|
3797
|
+
setDeepPilotRemoteConnectionLimit: isFn(source.setDeepPilotRemoteConnectionLimit) ? source.setDeepPilotRemoteConnectionLimit : void 0,
|
|
3798
|
+
setDeepPilotDebug: isFn(source.setDeepPilotDebug) ? source.setDeepPilotDebug : void 0
|
|
3799
|
+
},
|
|
3800
|
+
can,
|
|
3801
|
+
diagnostics,
|
|
3802
|
+
reportHookMissing: !isFn(source.useDeepPilotReport)
|
|
3803
|
+
};
|
|
3804
|
+
}
|
|
3805
|
+
//#endregion
|
|
3806
|
+
//#region src/client/settings-view-models.ts
|
|
3807
|
+
/**
|
|
3808
|
+
* 设置页的 view model:把原先住在渲染函数里的三块逻辑变成可单测的纯模块。
|
|
3809
|
+
*
|
|
3810
|
+
* 迁移前 `settings-page.ts` 是一个 1059 行的组件,funnel phase → {dot, label}、
|
|
3811
|
+
端口/连接数草稿的编辑与合法性、serviceStatus 派生全在渲染函数里,没有任何
|
|
3812
|
+
直接测试。这里按 pairing-panel.ts 的形状把它们拆成 state / action / reduce /
|
|
3813
|
+
view 四件套——shape 与该先例一致,但**不继承它唯一的泄漏**:view model 只返回
|
|
3814
|
+
语义值,i18n key 的映射留在页面(措辞归页面,测试不跟着措辞漂)。
|
|
3815
|
+
*/
|
|
3816
|
+
const REMOTE_PHASE_META = {
|
|
3817
|
+
disabled: {
|
|
3818
|
+
dot: "",
|
|
3819
|
+
label: "disabled"
|
|
3820
|
+
},
|
|
3821
|
+
starting: {
|
|
3822
|
+
dot: " pbb-dotWarn",
|
|
3823
|
+
label: "starting"
|
|
3824
|
+
},
|
|
3825
|
+
login_required: {
|
|
3826
|
+
dot: " pbb-dotWarn",
|
|
3827
|
+
label: "login_required"
|
|
3828
|
+
},
|
|
3829
|
+
online: {
|
|
3830
|
+
dot: " pbb-dotOk",
|
|
3831
|
+
label: "online"
|
|
3832
|
+
},
|
|
3833
|
+
error: {
|
|
3834
|
+
dot: " pbb-dotBad",
|
|
3835
|
+
label: "error"
|
|
3836
|
+
},
|
|
3837
|
+
unavailable: {
|
|
3838
|
+
dot: " pbb-dotBad",
|
|
3839
|
+
label: "unavailable"
|
|
3840
|
+
},
|
|
3841
|
+
stopped: {
|
|
3842
|
+
dot: "",
|
|
3843
|
+
label: "stopped"
|
|
3844
|
+
}
|
|
3845
|
+
};
|
|
3846
|
+
const LOCAL_PHASE_META = {
|
|
3847
|
+
disabled: {
|
|
3848
|
+
dot: "",
|
|
3849
|
+
label: "disabled"
|
|
3850
|
+
},
|
|
3851
|
+
starting: {
|
|
3852
|
+
dot: " pbb-dotWarn",
|
|
3853
|
+
label: "starting"
|
|
3854
|
+
},
|
|
3855
|
+
online: {
|
|
3856
|
+
dot: " pbb-dotOk",
|
|
3857
|
+
label: "online"
|
|
3858
|
+
},
|
|
3859
|
+
error: {
|
|
3860
|
+
dot: " pbb-dotBad",
|
|
3861
|
+
label: "error"
|
|
3862
|
+
},
|
|
3863
|
+
stopped: {
|
|
3864
|
+
dot: "",
|
|
3865
|
+
label: "stopped"
|
|
3866
|
+
}
|
|
3867
|
+
};
|
|
3868
|
+
/** 从点位反推「需要注意 / 已在线」——原先页面用字符串 includes 猜类名。 */
|
|
3869
|
+
function dotState(dot) {
|
|
3870
|
+
return {
|
|
3871
|
+
attention: dot?.includes("pbb-dotBad") === true,
|
|
3872
|
+
online: dot?.includes("pbb-dotOk") === true
|
|
3873
|
+
};
|
|
3874
|
+
}
|
|
3875
|
+
function serviceStatus(input) {
|
|
3876
|
+
const local = dotState(input.localDot);
|
|
3877
|
+
const remote = dotState(input.remoteDot);
|
|
3878
|
+
const attention = local.attention || remote.attention;
|
|
3879
|
+
const online = local.online || remote.online;
|
|
3880
|
+
return {
|
|
3881
|
+
kind: !input.switchReady ? "loading" : !input.enabled ? "off" : attention ? "attention" : online ? "ready" : "loading",
|
|
3882
|
+
dot: !input.enabled ? "" : attention ? " pbb-dotBad" : online ? " pbb-dotOk" : " pbb-dotWarn"
|
|
3883
|
+
};
|
|
3884
|
+
}
|
|
3885
|
+
const LOCAL_PORT_RANGE = {
|
|
3886
|
+
min: MIN_LOCAL_PORT,
|
|
3887
|
+
max: MAX_LOCAL_PORT,
|
|
3888
|
+
fallback: DEFAULT_LOCAL_PORT
|
|
3889
|
+
};
|
|
3890
|
+
const REMOTE_LIMIT_RANGE = {
|
|
3891
|
+
min: 1,
|
|
3892
|
+
max: 16,
|
|
3893
|
+
fallback: 8
|
|
3894
|
+
};
|
|
3895
|
+
function initialNumericDraft(range) {
|
|
3896
|
+
return {
|
|
3897
|
+
draft: String(range.fallback),
|
|
3898
|
+
message: "",
|
|
3899
|
+
failed: false
|
|
3900
|
+
};
|
|
3901
|
+
}
|
|
3902
|
+
function numericDraftReduce(state, action, range) {
|
|
3903
|
+
switch (action.type) {
|
|
3904
|
+
case "edit": return {
|
|
3905
|
+
...state,
|
|
3906
|
+
draft: action.draft
|
|
3907
|
+
};
|
|
3908
|
+
case "sync": return {
|
|
3909
|
+
...state,
|
|
3910
|
+
draft: String(action.value),
|
|
3911
|
+
message: "",
|
|
3912
|
+
failed: false
|
|
3913
|
+
};
|
|
3914
|
+
case "applied": return {
|
|
3915
|
+
...state,
|
|
3916
|
+
message: "",
|
|
3917
|
+
failed: false
|
|
3918
|
+
};
|
|
3919
|
+
case "succeeded": return {
|
|
3920
|
+
...state,
|
|
3921
|
+
message: action.message,
|
|
3922
|
+
failed: false
|
|
3923
|
+
};
|
|
3924
|
+
case "failed": return {
|
|
3925
|
+
...state,
|
|
3926
|
+
message: action.message,
|
|
3927
|
+
failed: true
|
|
3928
|
+
};
|
|
3929
|
+
}
|
|
3930
|
+
}
|
|
3931
|
+
function numericDraftView(state, current, range) {
|
|
3932
|
+
const value = Number(state.draft);
|
|
3933
|
+
const valid = Number.isInteger(value) && value >= range.min && value <= range.max;
|
|
3934
|
+
return {
|
|
3935
|
+
value,
|
|
3936
|
+
valid,
|
|
3937
|
+
unchanged: valid && value === current
|
|
3938
|
+
};
|
|
3939
|
+
}
|
|
3940
|
+
/** 已撤销的设备不展示。 */
|
|
3941
|
+
function visibleDevices(report) {
|
|
3942
|
+
return (report?.devices ?? []).filter((device) => device.revokedAt === void 0);
|
|
3943
|
+
}
|
|
3944
|
+
/** 设备展示名:自定义名优先,都没有则空串(兜底文案归页面)。 */
|
|
3945
|
+
function deviceLabel(device) {
|
|
3946
|
+
return (device.customName ?? device.deviceName).trim();
|
|
3947
|
+
}
|
|
3948
|
+
/** 渲染一块连接磁贴。纯数据函数:九参进,元素树出。 */
|
|
3949
|
+
function renderConnectionTile(key, title, description, checked, ready, dot, statusLabel, onToggle, extra = null) {
|
|
3950
|
+
return (0, react.createElement)("div", {
|
|
3951
|
+
className: "pbb-connectionTile",
|
|
3952
|
+
key
|
|
3953
|
+
}, (0, react.createElement)("div", { className: "pbb-connectionTileHeader" }, (0, react.createElement)("span", { className: "pbb-switchTitle pbb-dotRow" }, (0, react.createElement)("span", {
|
|
3954
|
+
className: "pbb-dot" + dot,
|
|
3955
|
+
role: "img",
|
|
3956
|
+
"aria-label": statusLabel,
|
|
3957
|
+
title: statusLabel
|
|
3958
|
+
}), title), (0, react.createElement)("button", {
|
|
3959
|
+
type: "button",
|
|
3960
|
+
role: "switch",
|
|
3961
|
+
"aria-checked": checked,
|
|
3962
|
+
"aria-label": title,
|
|
3963
|
+
disabled: !ready,
|
|
3964
|
+
className: "pbb-switch" + (checked ? " pbb-switchOn" : ""),
|
|
3965
|
+
onClick: onToggle
|
|
3966
|
+
})), (0, react.createElement)("span", { className: "pbb-connectionState" }, statusLabel), (0, react.createElement)("p", { className: "pbb-connectionDescription" }, description), extra);
|
|
3967
|
+
}
|
|
3968
|
+
//#endregion
|
|
3670
3969
|
//#region src/client/settings-page.ts
|
|
3970
|
+
/** phase 语义标签 → i18n key。措辞归页面,view model 只认语义。 */
|
|
3971
|
+
const REMOTE_LABEL_KEY = {
|
|
3972
|
+
disabled: "phase.disabled",
|
|
3973
|
+
starting: "phase.starting",
|
|
3974
|
+
login_required: "phase.login_required",
|
|
3975
|
+
online: "phase.online",
|
|
3976
|
+
error: "phase.error",
|
|
3977
|
+
unavailable: "phase.unavailable",
|
|
3978
|
+
stopped: "phase.stopped"
|
|
3979
|
+
};
|
|
3980
|
+
const LOCAL_LABEL_KEY = {
|
|
3981
|
+
disabled: "local.phaseDisabled",
|
|
3982
|
+
starting: "local.phaseStarting",
|
|
3983
|
+
online: "local.phaseOnline",
|
|
3984
|
+
error: "local.phaseError",
|
|
3985
|
+
stopped: "local.phaseStopped"
|
|
3986
|
+
};
|
|
3987
|
+
/** 服务状态 → i18n key。 */
|
|
3988
|
+
const SERVICE_STATUS_KEY = {
|
|
3989
|
+
loading: "status.loading",
|
|
3990
|
+
off: "status.off",
|
|
3991
|
+
attention: "status.attention",
|
|
3992
|
+
ready: "status.ready"
|
|
3993
|
+
};
|
|
3671
3994
|
/** Inline trash icon for the compact destructive row action. Kept as a tiny
|
|
3672
3995
|
* element (no icon dependency) so the client bundle stays dependency-free
|
|
3673
3996
|
* apart from React. */
|
|
@@ -3721,68 +4044,19 @@ window.__ModuleLoader__.load({
|
|
|
3721
4044
|
textarea.remove();
|
|
3722
4045
|
if (!copied) throw new Error(t("clipboard.rejected"));
|
|
3723
4046
|
}
|
|
3724
|
-
/**
|
|
3725
|
-
*
|
|
3726
|
-
*
|
|
3727
|
-
*
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
login_required: {
|
|
3738
|
-
dot: " pbb-dotWarn",
|
|
3739
|
-
labelKey: "phase.login_required"
|
|
3740
|
-
},
|
|
3741
|
-
online: {
|
|
3742
|
-
dot: " pbb-dotOk",
|
|
3743
|
-
labelKey: "phase.online"
|
|
3744
|
-
},
|
|
3745
|
-
error: {
|
|
3746
|
-
dot: " pbb-dotBad",
|
|
3747
|
-
labelKey: "phase.error"
|
|
3748
|
-
},
|
|
3749
|
-
unavailable: {
|
|
3750
|
-
dot: " pbb-dotBad",
|
|
3751
|
-
labelKey: "phase.unavailable"
|
|
3752
|
-
},
|
|
3753
|
-
stopped: {
|
|
3754
|
-
dot: "",
|
|
3755
|
-
labelKey: "phase.stopped"
|
|
3756
|
-
}
|
|
3757
|
-
};
|
|
3758
|
-
const LOCAL_PHASE_META = {
|
|
3759
|
-
disabled: {
|
|
3760
|
-
dot: "",
|
|
3761
|
-
labelKey: "local.phaseDisabled"
|
|
3762
|
-
},
|
|
3763
|
-
starting: {
|
|
3764
|
-
dot: " pbb-dotWarn",
|
|
3765
|
-
labelKey: "local.phaseStarting"
|
|
3766
|
-
},
|
|
3767
|
-
online: {
|
|
3768
|
-
dot: " pbb-dotOk",
|
|
3769
|
-
labelKey: "local.phaseOnline"
|
|
3770
|
-
},
|
|
3771
|
-
error: {
|
|
3772
|
-
dot: " pbb-dotBad",
|
|
3773
|
-
labelKey: "local.phaseError"
|
|
3774
|
-
},
|
|
3775
|
-
stopped: {
|
|
3776
|
-
dot: "",
|
|
3777
|
-
labelKey: "local.phaseStopped"
|
|
3778
|
-
}
|
|
3779
|
-
};
|
|
3780
|
-
/** Slot component: hooks come from the slot renderer, named use<Key>. */
|
|
3781
|
-
function DeepPilotSettingsPage(props) {
|
|
3782
|
-
const [remoteLimitDraft, setRemoteLimitDraft] = (0, react.useState)(String(8));
|
|
3783
|
-
const [remoteLimitMessage, setRemoteLimitMessage] = (0, react.useState)("");
|
|
3784
|
-
const [localPortDraft, setLocalPortDraft] = (0, react.useState)(String(DEFAULT_LOCAL_PORT));
|
|
3785
|
-
const [localPortMessage, setLocalPortMessage] = (0, react.useState)("");
|
|
4047
|
+
/**
|
|
4048
|
+
* Slot component: hooks come from the slot renderer, named use<Key>.
|
|
4049
|
+
*
|
|
4050
|
+
* 组件的接口是 `SettingsPageProps`(见 settings-props.ts):宿主注入面与页面
|
|
4051
|
+
* 共享同一份类型。这里只做一次归一化,之后全程消费类型化对象——页面里不再
|
|
4052
|
+
* 出现任何 typeof 守卫,也不再自己拼诊断行。
|
|
4053
|
+
*/
|
|
4054
|
+
function DeepPilotSettingsPage(rawProps) {
|
|
4055
|
+
const surface = normalizeSettingsPageProps(rawProps);
|
|
4056
|
+
const props = surface.props;
|
|
4057
|
+
const can = (capability) => surface.can.has(capability);
|
|
4058
|
+
const [remoteLimitDraft, dispatchRemoteLimitDraft] = (0, react.useReducer)((state, action) => numericDraftReduce(state, action, REMOTE_LIMIT_RANGE), initialNumericDraft(REMOTE_LIMIT_RANGE));
|
|
4059
|
+
const [localPortDraft, dispatchLocalPortDraft] = (0, react.useReducer)((state, action) => numericDraftReduce(state, action, LOCAL_PORT_RANGE), initialNumericDraft(LOCAL_PORT_RANGE));
|
|
3786
4060
|
const [selectedPairingHost, setSelectedPairingHost] = (0, react.useState)(null);
|
|
3787
4061
|
const [pairingPanel, dispatchPairingPanel] = (0, react.useReducer)(pairingPanelReduce, initialPairingPanelState);
|
|
3788
4062
|
const qrRequestId = (0, react.useRef)(0);
|
|
@@ -3799,14 +4073,15 @@ window.__ModuleLoader__.load({
|
|
|
3799
4073
|
const [editingDeviceId, setEditingDeviceId] = (0, react.useState)(null);
|
|
3800
4074
|
const [deviceNameDraft, setDeviceNameDraft] = (0, react.useState)("");
|
|
3801
4075
|
(0, react.useEffect)(() => {
|
|
3802
|
-
if (
|
|
3803
|
-
props.refresh
|
|
3804
|
-
|
|
4076
|
+
if (!can("refresh")) return;
|
|
4077
|
+
const refresh = props.refresh;
|
|
4078
|
+
refresh();
|
|
4079
|
+
const timer = globalThis.setInterval(() => refresh(), 3e3);
|
|
3805
4080
|
return () => globalThis.clearInterval(timer);
|
|
3806
4081
|
}, [props.refresh]);
|
|
3807
4082
|
const sendPushTest = () => {
|
|
3808
4083
|
if (pushTestBusy) return;
|
|
3809
|
-
if (
|
|
4084
|
+
if (!can("testPush")) {
|
|
3810
4085
|
setPushTestError(translateWith(props.t, "push.staleHost"));
|
|
3811
4086
|
return;
|
|
3812
4087
|
}
|
|
@@ -3823,7 +4098,7 @@ window.__ModuleLoader__.load({
|
|
|
3823
4098
|
};
|
|
3824
4099
|
const runRelayTest = () => {
|
|
3825
4100
|
if (relayTestBusy) return;
|
|
3826
|
-
if (
|
|
4101
|
+
if (!can("testRelay")) {
|
|
3827
4102
|
setRelayTestError(translateWith(props.t, "push.staleHostRelay"));
|
|
3828
4103
|
return;
|
|
3829
4104
|
}
|
|
@@ -3839,15 +4114,15 @@ window.__ModuleLoader__.load({
|
|
|
3839
4114
|
});
|
|
3840
4115
|
};
|
|
3841
4116
|
(0, react.useEffect)(() => {
|
|
3842
|
-
if (
|
|
3843
|
-
const timer = globalThis.setTimeout(() =>
|
|
4117
|
+
if (remoteLimitDraft.message === "") return;
|
|
4118
|
+
const timer = globalThis.setTimeout(() => dispatchRemoteLimitDraft({ type: "applied" }), 4e3);
|
|
3844
4119
|
return () => globalThis.clearTimeout(timer);
|
|
3845
|
-
}, [
|
|
4120
|
+
}, [remoteLimitDraft.message]);
|
|
3846
4121
|
(0, react.useEffect)(() => {
|
|
3847
|
-
if (
|
|
3848
|
-
const timer = globalThis.setTimeout(() =>
|
|
4122
|
+
if (localPortDraft.message === "") return;
|
|
4123
|
+
const timer = globalThis.setTimeout(() => dispatchLocalPortDraft({ type: "applied" }), 4e3);
|
|
3849
4124
|
return () => globalThis.clearTimeout(timer);
|
|
3850
|
-
}, [
|
|
4125
|
+
}, [localPortDraft.message]);
|
|
3851
4126
|
(0, react.useEffect)(() => {
|
|
3852
4127
|
if (pairingPanel.grant === null) return;
|
|
3853
4128
|
const remaining = Math.max(0, pairingPanel.grant.expiresAt - Date.now());
|
|
@@ -3864,112 +4139,123 @@ window.__ModuleLoader__.load({
|
|
|
3864
4139
|
const timer = globalThis.setTimeout(() => dispatchPairingPanel({ type: "dismissNotice" }), 2500);
|
|
3865
4140
|
return () => globalThis.clearTimeout(timer);
|
|
3866
4141
|
}, [pairingPanel.notice]);
|
|
3867
|
-
const diag =
|
|
3868
|
-
let
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
let localPortReady = false;
|
|
3875
|
-
let remoteEnabled = false;
|
|
3876
|
-
let remoteSwitchReady = false;
|
|
3877
|
-
let remoteConnectionLimit = 8;
|
|
3878
|
-
let remoteConnectionLimitReady = false;
|
|
3879
|
-
let debugEnabled = false;
|
|
3880
|
-
let debugSwitchReady = false;
|
|
3881
|
-
let failed = false;
|
|
3882
|
-
try {
|
|
3883
|
-
if (typeof props.useDeepPilotReport !== "function") {
|
|
3884
|
-
diag.push(translateWith(props.t, "diag.missingReportHook"));
|
|
4142
|
+
const diag = surface.diagnostics;
|
|
4143
|
+
let failed = surface.reportHookMissing;
|
|
4144
|
+
const readHooks = () => {
|
|
4145
|
+
const reportState = props.useDeepPilotReport((s) => s);
|
|
4146
|
+
hooks.report = reportState.report;
|
|
4147
|
+
if (reportState.status === "error" && reportState.message) {
|
|
4148
|
+
diag.push(reportState.message);
|
|
3885
4149
|
failed = true;
|
|
3886
|
-
} else {
|
|
3887
|
-
const state = props.useDeepPilotReport((s) => s);
|
|
3888
|
-
report = state.report;
|
|
3889
|
-
if (state.status === "error" && state.message) {
|
|
3890
|
-
diag.push(state.message);
|
|
3891
|
-
failed = true;
|
|
3892
|
-
}
|
|
3893
4150
|
}
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
if (typeof props.useDeepPilotDebug === "function") {
|
|
3932
|
-
const state = props.useDeepPilotDebug((s) => s);
|
|
3933
|
-
debugEnabled = state.enabled;
|
|
3934
|
-
debugSwitchReady = state.status === "ready";
|
|
3935
|
-
} else diag.push(translateWith(props.t, "diag.missingDebugHook"));
|
|
3936
|
-
if (typeof props.setDeepPilotDebug !== "function") diag.push(translateWith(props.t, "diag.missingSetDebug"));
|
|
4151
|
+
const enabledState = props.useDeepPilotEnabled((s) => s);
|
|
4152
|
+
hooks.enabled = enabledState.enabled;
|
|
4153
|
+
hooks.switchReady = enabledState.status === "ready";
|
|
4154
|
+
if (enabledState.status === "unavailable") diag.push(translateWith(props.t, "diag.settingsUnavailable"));
|
|
4155
|
+
const localEnabledState = props.useDeepPilotLocalEnabled((s) => s);
|
|
4156
|
+
hooks.localEnabled = localEnabledState.enabled;
|
|
4157
|
+
hooks.localSwitchReady = localEnabledState.status === "ready";
|
|
4158
|
+
const localPortState = props.useDeepPilotLocalPort((s) => s);
|
|
4159
|
+
hooks.localPort = localPortState.value;
|
|
4160
|
+
hooks.localPortReady = localPortState.status === "ready";
|
|
4161
|
+
const remoteEnabledState = props.useDeepPilotRemoteEnabled((s) => s);
|
|
4162
|
+
hooks.remoteEnabled = remoteEnabledState.enabled;
|
|
4163
|
+
hooks.remoteSwitchReady = remoteEnabledState.status === "ready";
|
|
4164
|
+
const remoteLimitState = props.useDeepPilotRemoteConnectionLimit((s) => s);
|
|
4165
|
+
hooks.remoteConnectionLimit = remoteLimitState.value;
|
|
4166
|
+
hooks.remoteConnectionLimitReady = remoteLimitState.status === "ready";
|
|
4167
|
+
const debugState = props.useDeepPilotDebug((s) => s);
|
|
4168
|
+
hooks.debugEnabled = debugState.enabled;
|
|
4169
|
+
hooks.debugSwitchReady = debugState.status === "ready";
|
|
4170
|
+
};
|
|
4171
|
+
const hooks = {
|
|
4172
|
+
report: null,
|
|
4173
|
+
enabled: true,
|
|
4174
|
+
switchReady: false,
|
|
4175
|
+
localEnabled: true,
|
|
4176
|
+
localSwitchReady: false,
|
|
4177
|
+
localPort: DEFAULT_LOCAL_PORT,
|
|
4178
|
+
localPortReady: false,
|
|
4179
|
+
remoteEnabled: false,
|
|
4180
|
+
remoteSwitchReady: false,
|
|
4181
|
+
remoteConnectionLimit: 8,
|
|
4182
|
+
remoteConnectionLimitReady: false,
|
|
4183
|
+
debugEnabled: false,
|
|
4184
|
+
debugSwitchReady: false
|
|
4185
|
+
};
|
|
4186
|
+
try {
|
|
4187
|
+
readHooks();
|
|
3937
4188
|
} catch (error) {
|
|
3938
4189
|
diag.push(translateWith(props.t, "diag.renderError") + (error instanceof Error ? error.message : String(error)));
|
|
3939
4190
|
failed = true;
|
|
3940
4191
|
}
|
|
4192
|
+
const report = hooks.report;
|
|
4193
|
+
const enabled = hooks.enabled;
|
|
4194
|
+
const switchReady = hooks.switchReady;
|
|
4195
|
+
const localEnabled = hooks.localEnabled;
|
|
4196
|
+
const localSwitchReady = hooks.localSwitchReady;
|
|
4197
|
+
const localPort = hooks.localPort;
|
|
4198
|
+
const localPortReady = hooks.localPortReady;
|
|
4199
|
+
const remoteEnabled = hooks.remoteEnabled;
|
|
4200
|
+
const remoteSwitchReady = hooks.remoteSwitchReady;
|
|
4201
|
+
const remoteConnectionLimit = hooks.remoteConnectionLimit;
|
|
4202
|
+
const remoteConnectionLimitReady = hooks.remoteConnectionLimitReady;
|
|
4203
|
+
const debugEnabled = hooks.debugEnabled;
|
|
4204
|
+
const debugSwitchReady = hooks.debugSwitchReady;
|
|
3941
4205
|
(0, react.useEffect)(() => {
|
|
3942
|
-
|
|
4206
|
+
dispatchRemoteLimitDraft({
|
|
4207
|
+
type: "sync",
|
|
4208
|
+
value: remoteConnectionLimit
|
|
4209
|
+
});
|
|
3943
4210
|
}, [remoteConnectionLimit]);
|
|
3944
4211
|
(0, react.useEffect)(() => {
|
|
3945
|
-
|
|
4212
|
+
dispatchLocalPortDraft({
|
|
4213
|
+
type: "sync",
|
|
4214
|
+
value: localPort
|
|
4215
|
+
});
|
|
3946
4216
|
}, [localPort]);
|
|
3947
|
-
const
|
|
3948
|
-
const
|
|
4217
|
+
const localPortView = numericDraftView(localPortDraft, localPort, LOCAL_PORT_RANGE);
|
|
4218
|
+
const remoteLimitView = numericDraftView(remoteLimitDraft, remoteConnectionLimit, REMOTE_LIMIT_RANGE);
|
|
3949
4219
|
const applyLocalPort = () => {
|
|
3950
|
-
if (!
|
|
3951
|
-
|
|
4220
|
+
if (!localPortView.valid || !can("setLocalPort")) {
|
|
4221
|
+
dispatchLocalPortDraft({
|
|
4222
|
+
type: "failed",
|
|
4223
|
+
message: translateWith(props.t, "local.portInvalid")
|
|
4224
|
+
});
|
|
3952
4225
|
return;
|
|
3953
4226
|
}
|
|
3954
|
-
|
|
3955
|
-
props.setDeepPilotLocalPort(
|
|
3956
|
-
|
|
4227
|
+
dispatchLocalPortDraft({ type: "applied" });
|
|
4228
|
+
Promise.resolve(props.setDeepPilotLocalPort(localPortView.value)).then(() => {
|
|
4229
|
+
dispatchLocalPortDraft({
|
|
4230
|
+
type: "succeeded",
|
|
4231
|
+
message: translateWith(props.t, "local.portApplied")
|
|
4232
|
+
});
|
|
3957
4233
|
}, (error) => {
|
|
3958
|
-
|
|
4234
|
+
dispatchLocalPortDraft({
|
|
4235
|
+
type: "failed",
|
|
4236
|
+
message: translateWith(props.t, "local.portFailed") + (error instanceof Error ? error.message : String(error))
|
|
4237
|
+
});
|
|
3959
4238
|
});
|
|
3960
4239
|
};
|
|
3961
|
-
const parsedRemoteLimit = Number(remoteLimitDraft);
|
|
3962
|
-
const remoteLimitValid = Number.isInteger(parsedRemoteLimit) && parsedRemoteLimit >= 1 && parsedRemoteLimit <= 16;
|
|
3963
4240
|
const applyRemoteLimit = () => {
|
|
3964
|
-
if (!
|
|
3965
|
-
|
|
4241
|
+
if (!remoteLimitView.valid || !can("setRemoteLimit")) {
|
|
4242
|
+
dispatchRemoteLimitDraft({
|
|
4243
|
+
type: "failed",
|
|
4244
|
+
message: translateWith(props.t, "remote.limitInvalid")
|
|
4245
|
+
});
|
|
3966
4246
|
return;
|
|
3967
4247
|
}
|
|
3968
|
-
|
|
3969
|
-
props.setDeepPilotRemoteConnectionLimit(
|
|
3970
|
-
|
|
4248
|
+
dispatchRemoteLimitDraft({ type: "applied" });
|
|
4249
|
+
Promise.resolve(props.setDeepPilotRemoteConnectionLimit(remoteLimitView.value)).then(() => {
|
|
4250
|
+
dispatchRemoteLimitDraft({
|
|
4251
|
+
type: "succeeded",
|
|
4252
|
+
message: translateWith(props.t, "remote.limitApplied")
|
|
4253
|
+
});
|
|
3971
4254
|
}, (error) => {
|
|
3972
|
-
|
|
4255
|
+
dispatchRemoteLimitDraft({
|
|
4256
|
+
type: "failed",
|
|
4257
|
+
message: translateWith(props.t, "remote.limitFailed") + (error instanceof Error ? error.message : String(error))
|
|
4258
|
+
});
|
|
3973
4259
|
});
|
|
3974
4260
|
};
|
|
3975
4261
|
const pairingTargets = report === null ? [] : selectPairingTargets(report.local, report.remote);
|
|
@@ -3984,7 +4270,7 @@ window.__ModuleLoader__.load({
|
|
|
3984
4270
|
* switching between LAN and public always mints a fresh one.
|
|
3985
4271
|
*/
|
|
3986
4272
|
const showPairingQR = (target) => {
|
|
3987
|
-
if (
|
|
4273
|
+
if (!can("beginPairing")) return;
|
|
3988
4274
|
const requestId = qrRequestId.current + 1;
|
|
3989
4275
|
qrRequestId.current = requestId;
|
|
3990
4276
|
dispatchPairingPanel({
|
|
@@ -4074,7 +4360,7 @@ window.__ModuleLoader__.load({
|
|
|
4074
4360
|
}, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "advanced.bufferMax")), (0, react.createElement)("span", { className: "pbb-value" }, String(report.historyBufferMax) + translateWith(props.t, "advanced.frames")))));
|
|
4075
4361
|
}
|
|
4076
4362
|
const revokeDevice = (deviceId, name) => {
|
|
4077
|
-
if (
|
|
4363
|
+
if (!can("revokeDevice")) return;
|
|
4078
4364
|
if (typeof window !== "undefined" && !window.confirm(translateWith(props.t, "devices.revokeConfirm", { name }))) return;
|
|
4079
4365
|
setDeviceBusy(deviceId);
|
|
4080
4366
|
setDeviceBusyAction("revoke");
|
|
@@ -4088,7 +4374,7 @@ window.__ModuleLoader__.load({
|
|
|
4088
4374
|
setDeviceBusyAction(null);
|
|
4089
4375
|
});
|
|
4090
4376
|
};
|
|
4091
|
-
const deviceLabel = (device) => (device
|
|
4377
|
+
const deviceLabel$1 = (device) => deviceLabel(device) || translateWith(props.t, "devices.unnamed");
|
|
4092
4378
|
const beginDeviceRename = (device) => {
|
|
4093
4379
|
setEditingDeviceId(device.deviceId);
|
|
4094
4380
|
setDeviceNameDraft(device.customName ?? device.deviceName);
|
|
@@ -4100,7 +4386,7 @@ window.__ModuleLoader__.load({
|
|
|
4100
4386
|
setDeviceMessage("");
|
|
4101
4387
|
};
|
|
4102
4388
|
const saveDeviceName = (deviceId) => {
|
|
4103
|
-
if (
|
|
4389
|
+
if (!can("setDeviceName")) return;
|
|
4104
4390
|
const normalized = deviceNameDraft.trim();
|
|
4105
4391
|
if (normalized.length > 64) {
|
|
4106
4392
|
setDeviceMessage(translateWith(props.t, "devices.nameInvalid"));
|
|
@@ -4120,11 +4406,11 @@ window.__ModuleLoader__.load({
|
|
|
4120
4406
|
setDeviceBusyAction(null);
|
|
4121
4407
|
});
|
|
4122
4408
|
};
|
|
4123
|
-
const visibleDevices = report
|
|
4124
|
-
const deviceTable = visibleDevices.length > 0 ? (0, react.createElement)("div", { className: "pbb-tableWrap" }, (0, react.createElement)("table", { className: "pbb-table" }, (0, react.createElement)("thead", null, (0, react.createElement)("tr", null, (0, react.createElement)("th", null, translateWith(props.t, "devices.col.name")), (0, react.createElement)("th", null, translateWith(props.t, "devices.col.lastSeen")), (0, react.createElement)("th", { className: "pbb-tableActionHead" }, translateWith(props.t, "devices.col.actions")))), (0, react.createElement)("tbody", null, visibleDevices.map((d) => {
|
|
4409
|
+
const visibleDevices$1 = visibleDevices(report);
|
|
4410
|
+
const deviceTable = visibleDevices$1.length > 0 ? (0, react.createElement)("div", { className: "pbb-tableWrap" }, (0, react.createElement)("table", { className: "pbb-table" }, (0, react.createElement)("thead", null, (0, react.createElement)("tr", null, (0, react.createElement)("th", null, translateWith(props.t, "devices.col.name")), (0, react.createElement)("th", null, translateWith(props.t, "devices.col.lastSeen")), (0, react.createElement)("th", { className: "pbb-tableActionHead" }, translateWith(props.t, "devices.col.actions")))), (0, react.createElement)("tbody", null, visibleDevices$1.map((d) => {
|
|
4125
4411
|
const editing = editingDeviceId === d.deviceId;
|
|
4126
4412
|
const busy = deviceBusy === d.deviceId;
|
|
4127
|
-
const label = deviceLabel(d);
|
|
4413
|
+
const label = deviceLabel$1(d);
|
|
4128
4414
|
return (0, react.createElement)("tr", {
|
|
4129
4415
|
key: d.deviceId,
|
|
4130
4416
|
"aria-busy": busy
|
|
@@ -4169,10 +4455,10 @@ window.__ModuleLoader__.load({
|
|
|
4169
4455
|
title: translateWith(props.t, "devices.nameCancel"),
|
|
4170
4456
|
"aria-label": translateWith(props.t, "devices.nameCancel"),
|
|
4171
4457
|
onClick: cancelDeviceRename
|
|
4172
|
-
}, CloseIcon()))), deviceNameDraft.length > 64 ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "devices.nameInvalid")) : null) : (0, react.createElement)("div", null, (0, react.createElement)("div", { className: "pbb-deviceNameRow" }, (0, react.createElement)("span", { className: "pbb-deviceName" }, label), d.customName ? (0, react.createElement)("span", { className: "pbb-badge" }, translateWith(props.t, "devices.customBadge")) : null), (0, react.createElement)("span", { className: "pbb-deviceMeta" }, d.appVersion, " · ", (0, react.createElement)("code", null, d.fingerprint.slice(0, 12))))), (0, react.createElement)("td", { className: "pbb-lastSeen" }, new Date(d.lastSeenTs).toLocaleString()), (0, react.createElement)("td", { className: "pbb-tableActionCell" }, !editing ? (0, react.createElement)("button", {
|
|
4458
|
+
}, CloseIcon()))), deviceNameDraft.length > 64 ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "devices.nameInvalid")) : null) : (0, react.createElement)("div", null, (0, react.createElement)("div", { className: "pbb-deviceNameRow" }, (0, react.createElement)("span", { className: "pbb-deviceName" }, label), d.customName ? (0, react.createElement)("span", { className: "pbb-badge" }, translateWith(props.t, "devices.customBadge")) : null), (0, react.createElement)("span", { className: "pbb-deviceMeta" }, d.appVersion, " · ", (0, react.createElement)("code", null, (d.fingerprint ?? "").slice(0, 12))))), (0, react.createElement)("td", { className: "pbb-lastSeen" }, new Date(d.lastSeenTs ?? 0).toLocaleString()), (0, react.createElement)("td", { className: "pbb-tableActionCell" }, !editing ? (0, react.createElement)("button", {
|
|
4173
4459
|
type: "button",
|
|
4174
4460
|
className: "pbb-action",
|
|
4175
|
-
disabled: busy || editingDeviceId !== null ||
|
|
4461
|
+
disabled: busy || editingDeviceId !== null || !can("setDeviceName"),
|
|
4176
4462
|
"aria-label": translateWith(props.t, "devices.renameAria", { name: label }),
|
|
4177
4463
|
onClick: () => beginDeviceRename(d)
|
|
4178
4464
|
}, translateWith(props.t, "devices.rename")) : null, (0, react.createElement)("button", {
|
|
@@ -4187,34 +4473,21 @@ window.__ModuleLoader__.load({
|
|
|
4187
4473
|
const switchDesc = switchReady ? enabled ? translateWith(props.t, "master.on") : translateWith(props.t, "master.off") : translateWith(props.t, "master.loading");
|
|
4188
4474
|
const localMeta = report === null ? null : LOCAL_PHASE_META[report.local.phase];
|
|
4189
4475
|
const remoteMeta = report === null ? null : REMOTE_PHASE_META[report.remote.phase];
|
|
4190
|
-
const
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
className: "pbb-dot" + dot,
|
|
4199
|
-
role: "img",
|
|
4200
|
-
"aria-label": statusLabel,
|
|
4201
|
-
title: statusLabel
|
|
4202
|
-
}), title), (0, react.createElement)("button", {
|
|
4203
|
-
type: "button",
|
|
4204
|
-
role: "switch",
|
|
4205
|
-
"aria-checked": checked,
|
|
4206
|
-
"aria-label": title,
|
|
4207
|
-
disabled: !ready,
|
|
4208
|
-
className: "pbb-switch" + (checked ? " pbb-switchOn" : ""),
|
|
4209
|
-
onClick: onToggle
|
|
4210
|
-
})), (0, react.createElement)("span", { className: "pbb-connectionState" }, statusLabel), (0, react.createElement)("p", { className: "pbb-connectionDescription" }, description), extra);
|
|
4476
|
+
const service = serviceStatus({
|
|
4477
|
+
enabled,
|
|
4478
|
+
switchReady,
|
|
4479
|
+
localDot: localMeta?.dot,
|
|
4480
|
+
remoteDot: remoteMeta?.dot
|
|
4481
|
+
});
|
|
4482
|
+
const serviceStatusText = translateWith(props.t, SERVICE_STATUS_KEY[service.kind]);
|
|
4483
|
+
const serviceDot = service.dot;
|
|
4211
4484
|
return (0, react.createElement)("div", { className: "pbb-section" }, (0, react.createElement)("header", { className: "pbb-pageHeader" }, (0, react.createElement)("div", null, (0, react.createElement)("h2", { className: "pbb-title" }, "DeepPilot"), (0, react.createElement)("p", { className: "pbb-intro" }, translateWith(props.t, "meta.intro"))), (0, react.createElement)("button", {
|
|
4212
4485
|
type: "button",
|
|
4213
4486
|
className: "pbb-refresh",
|
|
4214
4487
|
onClick: () => {
|
|
4215
|
-
if (
|
|
4488
|
+
if (can("refresh")) props.refresh();
|
|
4216
4489
|
}
|
|
4217
|
-
}, translateWith(props.t, "meta.refresh"))), (0, react.createElement)("div", { className: "pbb-card pbb-primaryCard" }, (0, react.createElement)("div", { className: "pbb-cardHeader" }, (0, react.createElement)("div", null, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "connection.title")), (0, react.createElement)("p", { className: "pbb-cardDescription" }, translateWith(props.t, "connection.description"))), (0, react.createElement)("span", { className: "pbb-statusBadge" }, (0, react.createElement)("span", { className: "pbb-dot" + serviceDot }),
|
|
4490
|
+
}, translateWith(props.t, "meta.refresh"))), (0, react.createElement)("div", { className: "pbb-card pbb-primaryCard" }, (0, react.createElement)("div", { className: "pbb-cardHeader" }, (0, react.createElement)("div", null, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "connection.title")), (0, react.createElement)("p", { className: "pbb-cardDescription" }, translateWith(props.t, "connection.description"))), (0, react.createElement)("span", { className: "pbb-statusBadge" }, (0, react.createElement)("span", { className: "pbb-dot" + serviceDot }), serviceStatusText)), (0, react.createElement)("div", { className: "pbb-masterRow" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-switchTitle" }, switchTitle), (0, react.createElement)("span", { className: "pbb-switchDesc" }, switchDesc)), (0, react.createElement)("button", {
|
|
4218
4491
|
type: "button",
|
|
4219
4492
|
role: "switch",
|
|
4220
4493
|
"aria-checked": enabled,
|
|
@@ -4222,12 +4495,12 @@ window.__ModuleLoader__.load({
|
|
|
4222
4495
|
disabled: !switchReady,
|
|
4223
4496
|
className: "pbb-switch" + (enabled ? " pbb-switchOn" : ""),
|
|
4224
4497
|
onClick: () => {
|
|
4225
|
-
if (
|
|
4498
|
+
if (can("setEnabled")) props.setDeepPilotEnabled(!enabled);
|
|
4226
4499
|
}
|
|
4227
|
-
})), (0, react.createElement)("div", { className: "pbb-connectionGrid" }, renderConnectionTile("local", translateWith(props.t, "local.title"), localSwitchReady ? localEnabled ? translateWith(props.t, "local.on", { port: localPort }) : translateWith(props.t, "local.off") : translateWith(props.t, "master.loading"), localEnabled, localSwitchReady, localMeta?.dot ?? "", localMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, localMeta.
|
|
4228
|
-
if (
|
|
4229
|
-
}, (0, react.createElement)(react.Fragment, null, report !== null && report.local.message && report.local.phase === "error" ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, report.local.message) : null, report !== null && report.local.tlsIdentityRegenerated === true ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "local.tlsRegenerated")) : null)), renderConnectionTile("remote", translateWith(props.t, "remote.title"), remoteSwitchReady ? remoteEnabled ? translateWith(props.t, "remote.on") : translateWith(props.t, "remote.off") : translateWith(props.t, "master.loading"), remoteEnabled, remoteSwitchReady, remoteMeta?.dot ?? "", remoteMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, remoteMeta.
|
|
4230
|
-
if (
|
|
4500
|
+
})), (0, react.createElement)("div", { className: "pbb-connectionGrid" }, renderConnectionTile("local", translateWith(props.t, "local.title"), localSwitchReady ? localEnabled ? translateWith(props.t, "local.on", { port: localPort }) : translateWith(props.t, "local.off") : translateWith(props.t, "master.loading"), localEnabled, localSwitchReady, localMeta?.dot ?? "", localMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, LOCAL_LABEL_KEY[localMeta.label]), () => {
|
|
4501
|
+
if (can("setLocalEnabled")) props.setDeepPilotLocalEnabled(!localEnabled);
|
|
4502
|
+
}, (0, react.createElement)(react.Fragment, null, report !== null && report.local.message && report.local.phase === "error" ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, report.local.message) : null, report !== null && report.local.tlsIdentityRegenerated === true ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "local.tlsRegenerated")) : null)), renderConnectionTile("remote", translateWith(props.t, "remote.title"), remoteSwitchReady ? remoteEnabled ? translateWith(props.t, "remote.on") : translateWith(props.t, "remote.off") : translateWith(props.t, "master.loading"), remoteEnabled, remoteSwitchReady, remoteMeta?.dot ?? "", remoteMeta === null ? translateWith(props.t, "phase.unknown") : translateWith(props.t, REMOTE_LABEL_KEY[remoteMeta.label]), () => {
|
|
4503
|
+
if (can("setRemoteEnabled")) props.setDeepPilotRemoteEnabled(!remoteEnabled);
|
|
4231
4504
|
}, (0, react.createElement)(react.Fragment, null, report !== null && report.remote.phase === "login_required" && typeof report.remote.authURL === "string" && report.remote.authURL.startsWith("https://") ? (0, react.createElement)("div", { className: "pbb-rowAction" }, (0, react.createElement)("a", {
|
|
4232
4505
|
className: "pbb-action pbb-buttonPrimary",
|
|
4233
4506
|
href: report.remote.authURL,
|
|
@@ -4236,18 +4509,21 @@ window.__ModuleLoader__.load({
|
|
|
4236
4509
|
}, translateWith(props.t, "remote.openAuth"))) : null, report !== null && report.remote.message && (report.remote.phase === "error" || report.remote.phase === "unavailable") ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, report.remote.message) : null))), report === null ? null : (0, react.createElement)("div", { className: "pbb-statGrid" }, primaryRows), (0, react.createElement)("details", { className: "pbb-inlineDetails" }, (0, react.createElement)("summary", null, translateWith(props.t, "connection.optionsSummary")), (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-limitRow pbb-limitNested" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("label", {
|
|
4237
4510
|
className: "pbb-switchTitle",
|
|
4238
4511
|
htmlFor: "deeppilot-local-port"
|
|
4239
|
-
}, translateWith(props.t, "local.portTitle")), (0, react.createElement)("span", { className: "pbb-switchDesc" }, translateWith(props.t, "local.portDescription")), !
|
|
4512
|
+
}, translateWith(props.t, "local.portTitle")), (0, react.createElement)("span", { className: "pbb-switchDesc" }, translateWith(props.t, "local.portDescription")), !localPortView.valid ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "local.portInvalid")) : localPortDraft.message ? (0, react.createElement)("p", { className: "pbb-diag" + (localPortDraft.failed ? " pbb-diagBad" : "") }, localPortDraft.message) : null), (0, react.createElement)("div", { className: "pbb-limitControl" }, (0, react.createElement)("input", {
|
|
4240
4513
|
id: "deeppilot-local-port",
|
|
4241
4514
|
className: "pbb-numberInput",
|
|
4242
4515
|
type: "number",
|
|
4243
|
-
min:
|
|
4244
|
-
max:
|
|
4516
|
+
min: LOCAL_PORT_RANGE.min,
|
|
4517
|
+
max: LOCAL_PORT_RANGE.max,
|
|
4245
4518
|
step: 1,
|
|
4246
4519
|
inputMode: "numeric",
|
|
4247
|
-
value: localPortDraft,
|
|
4520
|
+
value: localPortDraft.draft,
|
|
4248
4521
|
disabled: !localPortReady,
|
|
4249
4522
|
"aria-label": translateWith(props.t, "local.portTitle"),
|
|
4250
|
-
onChange: (event) =>
|
|
4523
|
+
onChange: (event) => dispatchLocalPortDraft({
|
|
4524
|
+
type: "edit",
|
|
4525
|
+
draft: event.currentTarget.value
|
|
4526
|
+
}),
|
|
4251
4527
|
onKeyDown: (event) => {
|
|
4252
4528
|
if (event.key === "Enter") {
|
|
4253
4529
|
event.preventDefault();
|
|
@@ -4257,23 +4533,26 @@ window.__ModuleLoader__.load({
|
|
|
4257
4533
|
}), (0, react.createElement)("button", {
|
|
4258
4534
|
type: "button",
|
|
4259
4535
|
className: "pbb-action",
|
|
4260
|
-
disabled: !localPortReady || !
|
|
4536
|
+
disabled: !localPortReady || !localPortView.valid || localPortView.unchanged,
|
|
4261
4537
|
onClick: applyLocalPort
|
|
4262
4538
|
}, translateWith(props.t, "remote.limitApply")))), (0, react.createElement)("div", { className: "pbb-limitRow pbb-limitNested" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("label", {
|
|
4263
4539
|
className: "pbb-switchTitle",
|
|
4264
4540
|
htmlFor: "deeppilot-funnel-source-limit"
|
|
4265
|
-
}, translateWith(props.t, "remote.limitTitle")), (0, react.createElement)("span", { className: "pbb-switchDesc" }, translateWith(props.t, "remote.limitDescription")), !
|
|
4541
|
+
}, translateWith(props.t, "remote.limitTitle")), (0, react.createElement)("span", { className: "pbb-switchDesc" }, translateWith(props.t, "remote.limitDescription")), !remoteLimitView.valid ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, translateWith(props.t, "remote.limitInvalid")) : remoteLimitDraft.message ? (0, react.createElement)("p", { className: "pbb-diag" + (remoteLimitDraft.failed ? " pbb-diagBad" : "") }, remoteLimitDraft.message) : null), (0, react.createElement)("div", { className: "pbb-limitControl" }, (0, react.createElement)("input", {
|
|
4266
4542
|
id: "deeppilot-funnel-source-limit",
|
|
4267
4543
|
className: "pbb-numberInput",
|
|
4268
4544
|
type: "number",
|
|
4269
4545
|
min: 1,
|
|
4270
|
-
max:
|
|
4546
|
+
max: REMOTE_LIMIT_RANGE.max,
|
|
4271
4547
|
step: 1,
|
|
4272
4548
|
inputMode: "numeric",
|
|
4273
|
-
value: remoteLimitDraft,
|
|
4549
|
+
value: remoteLimitDraft.draft,
|
|
4274
4550
|
disabled: !remoteConnectionLimitReady,
|
|
4275
4551
|
"aria-label": translateWith(props.t, "remote.limitTitle"),
|
|
4276
|
-
onChange: (event) =>
|
|
4552
|
+
onChange: (event) => dispatchRemoteLimitDraft({
|
|
4553
|
+
type: "edit",
|
|
4554
|
+
draft: event.currentTarget.value
|
|
4555
|
+
}),
|
|
4277
4556
|
onKeyDown: (event) => {
|
|
4278
4557
|
if (event.key === "Enter") {
|
|
4279
4558
|
event.preventDefault();
|
|
@@ -4283,7 +4562,7 @@ window.__ModuleLoader__.load({
|
|
|
4283
4562
|
}), (0, react.createElement)("button", {
|
|
4284
4563
|
type: "button",
|
|
4285
4564
|
className: "pbb-action",
|
|
4286
|
-
disabled: !remoteConnectionLimitReady || !
|
|
4565
|
+
disabled: !remoteConnectionLimitReady || !remoteLimitView.valid || remoteLimitView.unchanged,
|
|
4287
4566
|
onClick: applyRemoteLimit
|
|
4288
4567
|
}, translateWith(props.t, "remote.limitApply")))))), (0, react.createElement)("details", { className: "pbb-help" }, (0, react.createElement)("summary", null, translateWith(props.t, "help.remoteTitle")), (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.recommended")), (0, react.createElement)("ol", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.step1")), (0, react.createElement)("li", null, translateWith(props.t, "help.step2")), (0, react.createElement)("li", null, translateWith(props.t, "help.step3")), (0, react.createElement)("li", null, translateWith(props.t, "help.step4"))), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.funnelHint"))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.httpsTitle")), (0, react.createElement)("ol", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.httpsStep1")), (0, react.createElement)("li", null, translateWith(props.t, "help.httpsStep2")), (0, react.createElement)("li", null, translateWith(props.t, "help.httpsStep3"))), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.httpsHint"))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.allowTitle")), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.allowBody")), (0, react.createElement)("code", { className: "pbb-helpCode" }, "\"nodeAttrs\": [\n {\n \"target\": [\"autogroup:member\"],\n \"attr\": [\"funnel\"],\n },\n],"), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.allowHint"))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.faqTitle")), (0, react.createElement)("ul", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.faq1")), (0, react.createElement)("li", null, translateWith(props.t, "help.faq2")), (0, react.createElement)("li", null, translateWith(props.t, "help.faq3")), (0, react.createElement)("li", null, translateWith(props.t, "help.faq4")))), (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-helpHeading" }, translateWith(props.t, "help.securityTitle")), (0, react.createElement)("ul", { className: "pbb-helpList" }, (0, react.createElement)("li", null, translateWith(props.t, "help.security1")), (0, react.createElement)("li", null, translateWith(props.t, "help.security2")), (0, react.createElement)("li", null, translateWith(props.t, "help.security3")), (0, react.createElement)("li", null, translateWith(props.t, "help.security4")), (0, react.createElement)("li", null, translateWith(props.t, "help.security5"))), (0, react.createElement)("p", { className: "pbb-helpText" }, translateWith(props.t, "help.docsPrefix"), (0, react.createElement)("a", {
|
|
4289
4568
|
className: "pbb-helpLink",
|
|
@@ -4322,7 +4601,7 @@ window.__ModuleLoader__.load({
|
|
|
4322
4601
|
className: "pbb-action pbb-buttonPrimary",
|
|
4323
4602
|
onClick: copyPairingInfo,
|
|
4324
4603
|
"data-testid": "pairingInfoCopy"
|
|
4325
|
-
}, translateWith(props.t, "panel.tokenAction.copy")))), (0, react.createElement)("p", { className: "pbb-qrHint" }, translateWith(props.t, "pair.qrHint", { kind: panel.target?.kind === "public" ? translateWith(props.t, "pair.kind.public") : translateWith(props.t, "pair.kind.lan") }))) : null)), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-cardHeader" }, (0, react.createElement)("div", null, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "devices.title")), (0, react.createElement)("p", { className: "pbb-cardDescription" }, translateWith(props.t, "devices.description"))), (0, react.createElement)("span", { className: "pbb-countBadge" }, String(visibleDevices.length))), (0, react.createElement)("div", { className: "pbb-cardBody" }, deviceMessage ? (0, react.createElement)("p", { className: "pbb-diag" + (deviceMessage.startsWith(translateWith(props.t, "devices.renameFailed")) || deviceMessage.startsWith(translateWith(props.t, "devices.revokeFailed")) ? " pbb-diagBad" : "") }, deviceMessage) : null, deviceTable)), (0, react.createElement)("details", {
|
|
4604
|
+
}, translateWith(props.t, "panel.tokenAction.copy")))), (0, react.createElement)("p", { className: "pbb-qrHint" }, translateWith(props.t, "pair.qrHint", { kind: panel.target?.kind === "public" ? translateWith(props.t, "pair.kind.public") : translateWith(props.t, "pair.kind.lan") }))) : null)), (0, react.createElement)("div", { className: "pbb-card" }, (0, react.createElement)("div", { className: "pbb-cardHeader" }, (0, react.createElement)("div", null, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "devices.title")), (0, react.createElement)("p", { className: "pbb-cardDescription" }, translateWith(props.t, "devices.description"))), (0, react.createElement)("span", { className: "pbb-countBadge" }, String(visibleDevices$1.length))), (0, react.createElement)("div", { className: "pbb-cardBody" }, deviceMessage ? (0, react.createElement)("p", { className: "pbb-diag" + (deviceMessage.startsWith(translateWith(props.t, "devices.renameFailed")) || deviceMessage.startsWith(translateWith(props.t, "devices.revokeFailed")) ? " pbb-diagBad" : "") }, deviceMessage) : null, deviceTable)), (0, react.createElement)("details", {
|
|
4326
4605
|
className: "pbb-card pbb-disclosure",
|
|
4327
4606
|
open: troubleshootingOpen,
|
|
4328
4607
|
onToggle: (event) => setTroubleshootingOpen(event.currentTarget.open)
|
|
@@ -4344,7 +4623,7 @@ window.__ModuleLoader__.load({
|
|
|
4344
4623
|
disabled: !debugSwitchReady,
|
|
4345
4624
|
className: "pbb-switch" + (debugEnabled ? " pbb-switchOn" : ""),
|
|
4346
4625
|
onClick: () => {
|
|
4347
|
-
if (
|
|
4626
|
+
if (can("setDebug")) props.setDeepPilotDebug(!debugEnabled);
|
|
4348
4627
|
}
|
|
4349
4628
|
}))), relayTestError ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, relayTestError) : null, relayTestResult === null ? (0, react.createElement)("p", { className: "pbb-diag" }, translateWith(props.t, "push.relayDefault")) : (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("span", { className: "pbb-label" }, relayTestResult.url || translateWith(props.t, "push.relayUrlEmpty")), (0, react.createElement)("code", { className: "pbb-token " + (relayTestResult.overall === "ok" ? "pbb-ok" : "pbb-bad") }, relayTestResult.overall === "ok" ? translateWith(props.t, "push.relayOk") : translateWith(props.t, "push.relayBad"))), relayTestResult.steps.map((step, index) => (0, react.createElement)("p", {
|
|
4350
4629
|
className: "pbb-diag",
|