@dshfly/remote-connector 0.2.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/LICENSE +21 -0
- package/README.md +44 -0
- package/config.js +69 -0
- package/cordis.patch.yml +6 -0
- package/core/cloud.js +55 -0
- package/core/connector-core.js +910 -0
- package/core/dsh-web.js +556 -0
- package/core/keys.js +68 -0
- package/core/path-guard.js +60 -0
- package/core/pending-approvals.js +80 -0
- package/core/push-notifier.js +79 -0
- package/core/session-titles.js +85 -0
- package/core/tunnel.js +607 -0
- package/dist/client.js +556 -0
- package/dist/client.js.map +7 -0
- package/http-api.js +531 -0
- package/index.js +173 -0
- package/mobile-bridge/README.md +33 -0
- package/mobile-bridge/core/bridge-core.js +367 -0
- package/mobile-bridge/core/enumerate.js +44 -0
- package/mobile-bridge/core/files.js +431 -0
- package/mobile-bridge/core/roots.js +50 -0
- package/mobile-bridge/index.js +59 -0
- package/package.json +87 -0
package/dist/client.js
ADDED
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({ id: "@dshfly/remote-connector", factory: (require) => { var module = { exports: {} }; var exports = module.exports;
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/client/index.jsx
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
apply: () => apply,
|
|
34
|
+
inject: () => inject
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
|
|
38
|
+
// src/client/locales.js
|
|
39
|
+
var zh = {
|
|
40
|
+
nav: "\u8FDC\u7A0B\u8FDE\u63A5",
|
|
41
|
+
statusConnected: "\u5DF2\u8FDE\u63A5",
|
|
42
|
+
statusConnecting: "\u8FDE\u63A5\u4E2D",
|
|
43
|
+
statusDisconnected: "\u672A\u8FDE\u63A5",
|
|
44
|
+
relayServer: "\u4E2D\u7EE7\u670D\u52A1\u5668",
|
|
45
|
+
version: "\u7248\u672C",
|
|
46
|
+
checkUpdate: "\u68C0\u67E5\u66F4\u65B0",
|
|
47
|
+
updateAvailable: "\u53D1\u73B0\u65B0\u7248\u672C",
|
|
48
|
+
upgrade: "\u4E00\u952E\u5347\u7EA7",
|
|
49
|
+
upgrading: "\u5347\u7EA7\u4E2D\u2026",
|
|
50
|
+
upgradeDone: "\u5347\u7EA7\u5B8C\u6210\uFF0C\u8BF7\u91CD\u542F dsh web \u751F\u6548",
|
|
51
|
+
upgradeFailed: "\u5347\u7EA7\u5931\u8D25",
|
|
52
|
+
upToDate: "\u5DF2\u662F\u6700\u65B0\u7248\u672C",
|
|
53
|
+
checkFailed: "\u68C0\u67E5\u66F4\u65B0\u5931\u8D25",
|
|
54
|
+
accountName: "\u8D26\u53F7",
|
|
55
|
+
deviceId: "\u8BBE\u5907 ID",
|
|
56
|
+
hostname: "\u8BA1\u7B97\u673A\u540D",
|
|
57
|
+
pairedDevices: "\u5DF2\u914D\u5BF9\u8BBE\u5907",
|
|
58
|
+
noDevices: "\u6682\u65E0\u5DF2\u914D\u5BF9\u8BBE\u5907",
|
|
59
|
+
lastUsed: "\u6700\u8FD1\u4F7F\u7528",
|
|
60
|
+
never: "\u4ECE\u672A",
|
|
61
|
+
unpair: "\u89E3\u9664\u914D\u5BF9",
|
|
62
|
+
deviceOnline: "\u5728\u7EBF",
|
|
63
|
+
deviceOffline: "\u79BB\u7EBF",
|
|
64
|
+
startPairing: "\u65B0\u5EFA\u914D\u5BF9",
|
|
65
|
+
pairingTitle: "\u914D\u5BF9\u4E8C\u7EF4\u7801",
|
|
66
|
+
waitingScan: "\u7B49\u5F85\u624B\u673A\u626B\u7801\u2026\uFF0810 \u5206\u949F\u6709\u6548\uFF09",
|
|
67
|
+
recommendedTitle: "\u63A8\u8350\u63D2\u4EF6",
|
|
68
|
+
recommendedHint: "\u88C5\u5230\u672C\u673A dsh web \u540E\uFF0C\u624B\u673A App \u7684\u63D2\u4EF6 Tab \u4F1A\u81EA\u52A8\u51FA\u73B0\uFF1A",
|
|
69
|
+
install: "\u4E00\u952E\u5B89\u88C5",
|
|
70
|
+
installing: "\u5B89\u88C5\u4E2D\u2026",
|
|
71
|
+
installed: "\u5DF2\u5B89\u88C5",
|
|
72
|
+
installDone: "\u5B89\u88C5\u5B8C\u6210\uFF0C\u65E0\u9700\u91CD\u542F",
|
|
73
|
+
installReboot: "\u5DF2\u5B89\u88C5\uFF0C\u9700\u91CD\u542F dsh web \u540E\u751F\u6548",
|
|
74
|
+
installFailed: "\u5B89\u88C5\u5931\u8D25",
|
|
75
|
+
awaitingConfirm: "\u6709\u4E00\u53F0\u624B\u673A\u8BF7\u6C42\u4E0E\u672C\u673A\u914D\u5BF9",
|
|
76
|
+
confirmPairing: "\u914D\u5BF9\u8BF7\u6C42",
|
|
77
|
+
approve: "\u786E\u8BA4\u901A\u8FC7",
|
|
78
|
+
reject: "\u62D2\u7EDD",
|
|
79
|
+
pairingDone: "\u914D\u5BF9\u5B8C\u6210",
|
|
80
|
+
pairingRejected: "\u5DF2\u62D2\u7EDD\u8BE5\u914D\u5BF9\u8BF7\u6C42",
|
|
81
|
+
copyLink: "\u590D\u5236\u94FE\u63A5",
|
|
82
|
+
copied: "\u5DF2\u590D\u5236",
|
|
83
|
+
refresh: "\u5237\u65B0",
|
|
84
|
+
operationFailed: "\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5",
|
|
85
|
+
loadFailed: "\u52A0\u8F7D\u72B6\u6001\u5931\u8D25",
|
|
86
|
+
confirmUnpair: "\u786E\u8BA4\u89E3\u9664\u8FD9\u53F0\u8BBE\u5907\u7684\u914D\u5BF9\uFF1F",
|
|
87
|
+
fullDisk: "\u5141\u8BB8\u6D4F\u89C8\u5B8C\u6574\u78C1\u76D8",
|
|
88
|
+
fullDiskHint: "\u5F00\u542F\u540E\u624B\u673A\u53EF\u6D4F\u89C8\u6574\u4E2A\u6587\u4EF6\u7CFB\u7EDF\u5E76\u65B0\u5EFA\u4EFB\u610F\u4F4D\u7F6E\u7684\u5DE5\u4F5C\u533A\uFF1B\u5173\u95ED\u5219\u4EC5\u9650\u5DF2\u767B\u8BB0\u5DE5\u4F5C\u533A\u4E0E dsh web \u4E3B\u76EE\u5F55",
|
|
89
|
+
permissionChanged: "\u6743\u9650\u5DF2\u66F4\u65B0",
|
|
90
|
+
intro: "\u901A\u8FC7\u624B\u673A App \u968F\u65F6\u968F\u5730\u8FDE\u63A5 DeepSeek Harness\u2014\u2014\u7AEF\u5230\u7AEF\u52A0\u5BC6\u3001\u5B89\u5168\u4E2D\u7EE7\uFF0C\u590D\u6742\u7F51\u7EDC\u73AF\u5883\u4E5F\u80FD\u7A33\u5B9A\u8FDE\u63A5\u3002",
|
|
91
|
+
confirm: "\u786E\u5B9A",
|
|
92
|
+
cancel: "\u53D6\u6D88",
|
|
93
|
+
scanHint: "\u5728\u624B\u673A\u4E0A\u4F7F\u7528 DSH Fly \u626B\u7801",
|
|
94
|
+
shortCodeHint: "\u8BF7\u6838\u5BF9\u624B\u673A\u5C4F\u5E55\u4E0A\u7684\u914D\u5BF9\u7801",
|
|
95
|
+
shortCodeCompare: "\u4E0E\u624B\u673A\u300C\u914D\u5BF9\u5B8C\u6210\u300D\u9875\u663E\u793A\u7684 8 \u4F4D\u7801\u4E00\u81F4\u624D\u53EF\u786E\u8BA4\u2014\u2014\u7801\u4E0D\u540C\u8BF4\u660E\u4E2D\u7EE7\u88AB\u7BE1\u6539",
|
|
96
|
+
knownDevice: "\u8FD9\u662F\u5DF2\u914D\u5BF9\u8FC7\u7684\u8BBE\u5907\uFF0C\u786E\u8BA4\u540E\u514D\u7801\u91CD\u8FDE",
|
|
97
|
+
tokenRevoked: "\u8BBE\u5907\u5DF2\u64A4\u9500",
|
|
98
|
+
tokenRevokedTip: "\u672C\u673A\u5DF2\u88AB\u4E2D\u7EE7\u64A4\u9500\uFF0C\u9700\u91CD\u65B0\u521D\u59CB\u5316\u540E\u518D\u914D\u5BF9",
|
|
99
|
+
tokenExpired: "\u51ED\u8BC1\u5DF2\u8FC7\u671F",
|
|
100
|
+
tokenExpiredTip: "\u8BBE\u5907\u51ED\u8BC1\u5DF2\u8FC7\u671F\uFF0C\u6B63\u5728\u81EA\u52A8\u5237\u65B0\uFF08\u4E00\u822C\u51E0\u79D2\u5185\u6062\u590D\uFF09",
|
|
101
|
+
tokenExpiring: "\u51ED\u8BC1\u5373\u5C06\u8FC7\u671F",
|
|
102
|
+
tokenExpiringTip: "\u8BBE\u5907\u51ED\u8BC1\u5C06\u5728 12 \u5C0F\u65F6\u5185\u8FC7\u671F\uFF0C\u4F1A\u81EA\u52A8\u5237\u65B0",
|
|
103
|
+
tokenUnregistered: "\u672A\u767B\u8BB0",
|
|
104
|
+
tokenUnregisteredTip: "\u672C\u673A\u5C1A\u672A\u5728\u4E2D\u7EE7\u767B\u8BB0\uFF0C\u65B0\u5EFA\u914D\u5BF9\u4F1A\u81EA\u52A8\u767B\u8BB0"
|
|
105
|
+
};
|
|
106
|
+
var en = {
|
|
107
|
+
nav: "Remote Connector",
|
|
108
|
+
statusConnected: "Connected",
|
|
109
|
+
statusConnecting: "Connecting",
|
|
110
|
+
statusDisconnected: "Disconnected",
|
|
111
|
+
relayServer: "Relay server",
|
|
112
|
+
version: "Version",
|
|
113
|
+
checkUpdate: "Check update",
|
|
114
|
+
updateAvailable: "New version available",
|
|
115
|
+
upgrade: "Upgrade",
|
|
116
|
+
upgrading: "Upgrading\u2026",
|
|
117
|
+
upgradeDone: "Upgrade done, restart dsh web to apply",
|
|
118
|
+
upgradeFailed: "Upgrade failed",
|
|
119
|
+
upToDate: "Up to date",
|
|
120
|
+
checkFailed: "Update check failed",
|
|
121
|
+
accountName: "Account",
|
|
122
|
+
deviceId: "Device ID",
|
|
123
|
+
hostname: "Hostname",
|
|
124
|
+
pairedDevices: "Paired devices",
|
|
125
|
+
noDevices: "No paired devices",
|
|
126
|
+
lastUsed: "Last used",
|
|
127
|
+
never: "never",
|
|
128
|
+
unpair: "Unpair",
|
|
129
|
+
deviceOnline: "Online",
|
|
130
|
+
deviceOffline: "Offline",
|
|
131
|
+
startPairing: "New pairing",
|
|
132
|
+
pairingTitle: "Pairing QR",
|
|
133
|
+
waitingScan: "Waiting for phone scan\u2026 (10 min)",
|
|
134
|
+
awaitingConfirm: "A phone is requesting to pair with this machine",
|
|
135
|
+
confirmPairing: "Pairing request",
|
|
136
|
+
approve: "Approve",
|
|
137
|
+
reject: "Reject",
|
|
138
|
+
pairingDone: "Paired",
|
|
139
|
+
pairingRejected: "Pairing request rejected",
|
|
140
|
+
copyLink: "Copy link",
|
|
141
|
+
copied: "Copied",
|
|
142
|
+
refresh: "Refresh",
|
|
143
|
+
operationFailed: "Operation failed, retry",
|
|
144
|
+
loadFailed: "Failed to load status",
|
|
145
|
+
confirmUnpair: "Unpair this device?",
|
|
146
|
+
fullDisk: "Allow full disk browsing",
|
|
147
|
+
fullDiskHint: "When on, the phone can browse the whole filesystem and create workspaces anywhere; when off, it is limited to registered workspaces and the dsh web home directory",
|
|
148
|
+
permissionChanged: "Permission updated",
|
|
149
|
+
intro: "Connect to DeepSeek Harness from your phone anytime, anywhere \u2014 end-to-end encrypted, secure relay, stable under any network conditions.",
|
|
150
|
+
confirm: "OK",
|
|
151
|
+
cancel: "Cancel",
|
|
152
|
+
scanHint: "Scan with the DSH Fly app on your phone",
|
|
153
|
+
shortCodeHint: "Compare with the pairing code on the phone screen",
|
|
154
|
+
shortCodeCompare: `Only confirm if it matches the 8-char code on the phone's "paired" screen \u2014 a mismatch means the relay was tampered with`,
|
|
155
|
+
knownDevice: "This device was paired before; confirm to reconnect without a code",
|
|
156
|
+
tokenRevoked: "Device revoked",
|
|
157
|
+
tokenRevokedTip: "This machine was revoked by the relay; reinitialize before pairing",
|
|
158
|
+
tokenExpired: "Credential expired",
|
|
159
|
+
tokenExpiredTip: "Device credential expired, auto-refreshing (usually recovers in seconds)",
|
|
160
|
+
tokenExpiring: "Credential expiring",
|
|
161
|
+
tokenExpiringTip: "Device credential expires within 12h, auto-refreshed",
|
|
162
|
+
tokenUnregistered: "Unregistered",
|
|
163
|
+
tokenUnregisteredTip: "Not registered on the relay; a new pairing registers it automatically",
|
|
164
|
+
recommendedTitle: "Recommended plugins",
|
|
165
|
+
recommendedHint: "Installed into this dsh web, they appear in the app plugin tab automatically:",
|
|
166
|
+
install: "Install",
|
|
167
|
+
installing: "Installing\u2026",
|
|
168
|
+
installed: "Installed",
|
|
169
|
+
installDone: "Installed, no restart needed",
|
|
170
|
+
installReboot: "Installed, restart dsh web to apply",
|
|
171
|
+
installFailed: "Install failed"
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// src/client/SettingsSection.jsx
|
|
175
|
+
var import_react = __toESM(require("react"), 1);
|
|
176
|
+
|
|
177
|
+
// src/client/api.js
|
|
178
|
+
async function request(path, init) {
|
|
179
|
+
const headers = new Headers(init?.headers);
|
|
180
|
+
if (init?.body !== void 0 && !headers.has("content-type")) {
|
|
181
|
+
headers.set("content-type", "application/json");
|
|
182
|
+
}
|
|
183
|
+
const response = await fetch(path, { ...init, headers, cache: "no-store" });
|
|
184
|
+
let payload = null;
|
|
185
|
+
try {
|
|
186
|
+
payload = await response.json();
|
|
187
|
+
} catch {
|
|
188
|
+
}
|
|
189
|
+
if (!response.ok) {
|
|
190
|
+
throw new Error(payload && payload.error || `HTTP ${response.status}`);
|
|
191
|
+
}
|
|
192
|
+
return payload;
|
|
193
|
+
}
|
|
194
|
+
var getStatus = () => request("/dshfly/status");
|
|
195
|
+
var startPairing = () => request("/dshfly/pair", { method: "POST" });
|
|
196
|
+
var getPairing = (id) => request(`/dshfly/pair/${encodeURIComponent(id)}`);
|
|
197
|
+
var confirmPairing = (id, fullDisk = false) => request(`/dshfly/pair/${encodeURIComponent(id)}/confirm`, { method: "POST", body: JSON.stringify({ fullDisk }) });
|
|
198
|
+
var rejectPairing = (id) => request(`/dshfly/pair/${encodeURIComponent(id)}/reject`, { method: "POST" });
|
|
199
|
+
var unpair = (phonePk) => request("/dshfly/unpair", { method: "POST", body: JSON.stringify({ phonePk }) });
|
|
200
|
+
var setPermission = (phonePk, fullDisk) => request("/dshfly/permissions", { method: "POST", body: JSON.stringify({ phonePk, fullDisk }) });
|
|
201
|
+
var getRecommendedPlugins = () => request("/dshfly/plugins/recommended");
|
|
202
|
+
var installPlugin = (id) => request("/dshfly/plugins/install", { method: "POST", body: JSON.stringify({ id }) });
|
|
203
|
+
var checkUpdate = () => request("/dshfly/plugins/checkUpdate");
|
|
204
|
+
var upgrade = () => request("/dshfly/plugins/upgrade", { method: "POST" });
|
|
205
|
+
|
|
206
|
+
// src/client/SettingsSection.jsx
|
|
207
|
+
var sectionStyle = {
|
|
208
|
+
display: "grid",
|
|
209
|
+
gap: 20,
|
|
210
|
+
maxWidth: 760,
|
|
211
|
+
// 对齐 DSH 内置设置页 section(.pbvGtq_section max-width:760px)
|
|
212
|
+
padding: "8px 0 32px"
|
|
213
|
+
};
|
|
214
|
+
var cardStyle = {
|
|
215
|
+
border: "1px solid var(--dsh-border, rgba(127, 127, 127, .28))",
|
|
216
|
+
borderRadius: 12,
|
|
217
|
+
padding: 16,
|
|
218
|
+
display: "grid",
|
|
219
|
+
gap: 12
|
|
220
|
+
};
|
|
221
|
+
var rowStyle = {
|
|
222
|
+
display: "flex",
|
|
223
|
+
alignItems: "center",
|
|
224
|
+
justifyContent: "space-between",
|
|
225
|
+
gap: 12,
|
|
226
|
+
flexWrap: "wrap"
|
|
227
|
+
};
|
|
228
|
+
var labelStyle = { fontSize: 12, opacity: 0.65 };
|
|
229
|
+
var badgeStyle = (color) => ({
|
|
230
|
+
display: "inline-flex",
|
|
231
|
+
alignItems: "center",
|
|
232
|
+
gap: 6,
|
|
233
|
+
fontSize: 13,
|
|
234
|
+
padding: "3px 10px",
|
|
235
|
+
borderRadius: 999,
|
|
236
|
+
border: "1px solid currentColor",
|
|
237
|
+
color
|
|
238
|
+
});
|
|
239
|
+
var btnStyle = {
|
|
240
|
+
border: "1px solid var(--dsh-border, rgba(127, 127, 127, .35))",
|
|
241
|
+
borderRadius: 8,
|
|
242
|
+
background: "transparent",
|
|
243
|
+
color: "inherit",
|
|
244
|
+
padding: "8px 14px",
|
|
245
|
+
cursor: "pointer",
|
|
246
|
+
fontSize: 13
|
|
247
|
+
};
|
|
248
|
+
var deviceStatusStyle = (online) => ({
|
|
249
|
+
display: "inline-flex",
|
|
250
|
+
alignItems: "center",
|
|
251
|
+
gap: 5,
|
|
252
|
+
fontSize: 12,
|
|
253
|
+
padding: "2px 8px",
|
|
254
|
+
borderRadius: 999,
|
|
255
|
+
border: "1px solid currentColor",
|
|
256
|
+
color: online ? "#2f9e44" : "#868e96",
|
|
257
|
+
whiteSpace: "nowrap"
|
|
258
|
+
});
|
|
259
|
+
var switchStyle = { display: "flex", alignItems: "center", gap: 8, fontSize: 13 };
|
|
260
|
+
var modalBackdropStyle = {
|
|
261
|
+
position: "fixed",
|
|
262
|
+
inset: 0,
|
|
263
|
+
zIndex: 9999,
|
|
264
|
+
background: "rgba(0, 0, 0, 0.55)",
|
|
265
|
+
display: "flex",
|
|
266
|
+
alignItems: "center",
|
|
267
|
+
justifyContent: "center",
|
|
268
|
+
padding: 24
|
|
269
|
+
};
|
|
270
|
+
var modalCardStyle = {
|
|
271
|
+
background: "var(--dsh-card-bg, #fff)",
|
|
272
|
+
color: "var(--dsh-card-fg, #1a1d26)",
|
|
273
|
+
borderRadius: 16,
|
|
274
|
+
padding: 24,
|
|
275
|
+
maxWidth: 380,
|
|
276
|
+
width: "100%",
|
|
277
|
+
boxShadow: "0 12px 40px rgba(0,0,0,.35)"
|
|
278
|
+
};
|
|
279
|
+
var modalTitleStyle = { fontSize: 17, fontWeight: "700", marginBottom: 10 };
|
|
280
|
+
function statusBadge(connection, t, relayUrl) {
|
|
281
|
+
const tip = relayUrl ? `${t("relayServer")}: ${relayUrl}` : void 0;
|
|
282
|
+
if (connection === "connected") return /* @__PURE__ */ import_react.default.createElement("span", { style: badgeStyle("#2f9e44"), title: tip }, "\u25CF ", t("statusConnected"));
|
|
283
|
+
if (connection === "connecting") return /* @__PURE__ */ import_react.default.createElement("span", { style: badgeStyle("#e8890c"), title: tip }, "\u25D0 ", t("statusConnecting"));
|
|
284
|
+
return /* @__PURE__ */ import_react.default.createElement("span", { style: badgeStyle("#c92a2a"), title: tip }, "\u25CB ", t("statusDisconnected"));
|
|
285
|
+
}
|
|
286
|
+
function tokenBadge(tokenState, t) {
|
|
287
|
+
if (tokenState === "revoked") return /* @__PURE__ */ import_react.default.createElement("span", { style: badgeStyle("#c92a2a"), title: t("tokenRevokedTip") }, "\u26A0 ", t("tokenRevoked"));
|
|
288
|
+
if (tokenState === "expired") return /* @__PURE__ */ import_react.default.createElement("span", { style: badgeStyle("#c92a2a"), title: t("tokenExpiredTip") }, "\u26A0 ", t("tokenExpired"));
|
|
289
|
+
if (tokenState === "expiring") return /* @__PURE__ */ import_react.default.createElement("span", { style: badgeStyle("#e8890c"), title: t("tokenExpiringTip") }, "\u25D0 ", t("tokenExpiring"));
|
|
290
|
+
if (tokenState === "unregistered") return /* @__PURE__ */ import_react.default.createElement("span", { style: badgeStyle("#868e96"), title: t("tokenUnregisteredTip") }, "\u25CB ", t("tokenUnregistered"));
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
function fmtTime(ts, t) {
|
|
294
|
+
if (!ts) return t("never");
|
|
295
|
+
const d = new Date(ts);
|
|
296
|
+
return `${d.getMonth() + 1}/${d.getDate()} ${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
|
|
297
|
+
}
|
|
298
|
+
function SettingsSection({ t }) {
|
|
299
|
+
const [status, setStatus] = (0, import_react.useState)(null);
|
|
300
|
+
const [pairing, setPairing] = (0, import_react.useState)(null);
|
|
301
|
+
const [confirmFullDisk, setConfirmFullDisk] = (0, import_react.useState)(false);
|
|
302
|
+
const [busy, setBusy] = (0, import_react.useState)(null);
|
|
303
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
304
|
+
const [notice, setNotice] = (0, import_react.useState)(null);
|
|
305
|
+
const [recommended, setRecommended] = (0, import_react.useState)(null);
|
|
306
|
+
const [installBusy, setInstallBusy] = (0, import_react.useState)(null);
|
|
307
|
+
const [installNotice, setInstallNotice] = (0, import_react.useState)(null);
|
|
308
|
+
const [updateInfo, setUpdateInfo] = (0, import_react.useState)(null);
|
|
309
|
+
const [updateBusy, setUpdateBusy] = (0, import_react.useState)(false);
|
|
310
|
+
const [toast, setToast] = (0, import_react.useState)(null);
|
|
311
|
+
const toastTimer = (0, import_react.useRef)(null);
|
|
312
|
+
const showToast = (0, import_react.useCallback)((text, kind = "ok") => {
|
|
313
|
+
setToast({ text, kind });
|
|
314
|
+
if (toastTimer.current) clearTimeout(toastTimer.current);
|
|
315
|
+
toastTimer.current = setTimeout(() => setToast(null), 3e3);
|
|
316
|
+
}, []);
|
|
317
|
+
const loadUpdate = (0, import_react.useCallback)(async (manual = false) => {
|
|
318
|
+
try {
|
|
319
|
+
const r = await checkUpdate();
|
|
320
|
+
setUpdateInfo(r);
|
|
321
|
+
if (manual) {
|
|
322
|
+
if (r.updateAvailable) showToast(t("updateAvailable") + ` v${r.latest}`, "ok");
|
|
323
|
+
else if (r.error) showToast(`${t("checkFailed")}\uFF1A${r.error}`, "err");
|
|
324
|
+
else showToast(t("upToDate"), "ok");
|
|
325
|
+
}
|
|
326
|
+
} catch (e) {
|
|
327
|
+
setUpdateInfo({ error: e.message });
|
|
328
|
+
if (manual) showToast(`${t("checkFailed")}\uFF1A${e.message}`, "err");
|
|
329
|
+
}
|
|
330
|
+
}, [t, showToast]);
|
|
331
|
+
(0, import_react.useEffect)(() => {
|
|
332
|
+
void loadUpdate(false);
|
|
333
|
+
}, [loadUpdate]);
|
|
334
|
+
const onUpgrade = (0, import_react.useCallback)(async () => {
|
|
335
|
+
setUpdateBusy(true);
|
|
336
|
+
try {
|
|
337
|
+
await upgrade();
|
|
338
|
+
showToast(t("upgradeDone"), "ok");
|
|
339
|
+
} catch (e) {
|
|
340
|
+
showToast(`${t("upgradeFailed")}\uFF1A${e.message}`, "err");
|
|
341
|
+
} finally {
|
|
342
|
+
setUpdateBusy(false);
|
|
343
|
+
}
|
|
344
|
+
}, [t, showToast]);
|
|
345
|
+
const loadRecommended = (0, import_react.useCallback)(async () => {
|
|
346
|
+
try {
|
|
347
|
+
const r = await getRecommendedPlugins();
|
|
348
|
+
setRecommended(r.plugins || []);
|
|
349
|
+
} catch (e) {
|
|
350
|
+
setRecommended([]);
|
|
351
|
+
}
|
|
352
|
+
}, []);
|
|
353
|
+
(0, import_react.useEffect)(() => {
|
|
354
|
+
void loadRecommended();
|
|
355
|
+
}, [loadRecommended]);
|
|
356
|
+
const onInstallPlugin = (0, import_react.useCallback)(async (id) => {
|
|
357
|
+
setInstallBusy(id);
|
|
358
|
+
setInstallNotice(null);
|
|
359
|
+
try {
|
|
360
|
+
const r = await installPlugin(id);
|
|
361
|
+
setInstallNotice({ id, text: r.rebootNeeded ? t("installReboot") : t("installDone"), kind: "ok" });
|
|
362
|
+
void loadRecommended();
|
|
363
|
+
} catch (e) {
|
|
364
|
+
setInstallNotice({ id, text: `${t("installFailed")}\uFF1A${e.message}`, kind: "err" });
|
|
365
|
+
} finally {
|
|
366
|
+
setInstallBusy(null);
|
|
367
|
+
}
|
|
368
|
+
}, [t, loadRecommended]);
|
|
369
|
+
const refresh = (0, import_react.useCallback)(async () => {
|
|
370
|
+
try {
|
|
371
|
+
setStatus(await getStatus());
|
|
372
|
+
} catch (e) {
|
|
373
|
+
setError(e.message);
|
|
374
|
+
}
|
|
375
|
+
}, []);
|
|
376
|
+
(0, import_react.useEffect)(() => {
|
|
377
|
+
void refresh();
|
|
378
|
+
const timer = window.setInterval(() => void refresh(), 2e3);
|
|
379
|
+
return () => window.clearInterval(timer);
|
|
380
|
+
}, [refresh]);
|
|
381
|
+
(0, import_react.useEffect)(() => {
|
|
382
|
+
if (!pairing || pairing.phase === "done" || pairing.phase === "awaiting-confirm") return;
|
|
383
|
+
let alive = true;
|
|
384
|
+
const timer = window.setInterval(async () => {
|
|
385
|
+
try {
|
|
386
|
+
const st = await getPairing(pairing.pairingId);
|
|
387
|
+
if (st.status === "completed" && alive) {
|
|
388
|
+
setPairing((p) => ({
|
|
389
|
+
...p,
|
|
390
|
+
phase: "awaiting-confirm",
|
|
391
|
+
phonePk: st.phone?.pk_identity || p.phonePk,
|
|
392
|
+
phoneName: st.phone?.name || p.phoneName,
|
|
393
|
+
shortCode: st.shortCode || null,
|
|
394
|
+
// A0:首次配对短码(connector 本地计算)
|
|
395
|
+
known: !!st.known
|
|
396
|
+
// A0:已 pin 设备 → 免码直接确认
|
|
397
|
+
}));
|
|
398
|
+
} else if (st.status === "done" && alive) {
|
|
399
|
+
setPairing((p) => ({ ...p, phase: "done" }));
|
|
400
|
+
void refresh();
|
|
401
|
+
}
|
|
402
|
+
} catch {
|
|
403
|
+
}
|
|
404
|
+
}, 1500);
|
|
405
|
+
return () => {
|
|
406
|
+
alive = false;
|
|
407
|
+
window.clearInterval(timer);
|
|
408
|
+
};
|
|
409
|
+
}, [pairing, refresh]);
|
|
410
|
+
async function run(label, action) {
|
|
411
|
+
try {
|
|
412
|
+
setBusy(label);
|
|
413
|
+
setError(null);
|
|
414
|
+
setNotice(null);
|
|
415
|
+
await action();
|
|
416
|
+
} catch (e) {
|
|
417
|
+
setError(e.message || t("operationFailed"));
|
|
418
|
+
} finally {
|
|
419
|
+
setBusy(null);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
async function onStartPairing() {
|
|
423
|
+
await run("pair", async () => {
|
|
424
|
+
const p = await startPairing();
|
|
425
|
+
setPairing({ ...p, phase: "waiting-scan" });
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
async function onConfirm() {
|
|
429
|
+
if (!pairing) return;
|
|
430
|
+
await run("confirm", async () => {
|
|
431
|
+
await confirmPairing(pairing.pairingId, confirmFullDisk);
|
|
432
|
+
setPairing((p) => ({ ...p, phase: "done" }));
|
|
433
|
+
setConfirmFullDisk(false);
|
|
434
|
+
await refresh();
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
async function onReject() {
|
|
438
|
+
if (!pairing) return;
|
|
439
|
+
await run("reject", async () => {
|
|
440
|
+
await rejectPairing(pairing.pairingId);
|
|
441
|
+
setPairing(null);
|
|
442
|
+
setNotice(t("pairingRejected"));
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
async function onUnpair(phonePk) {
|
|
446
|
+
if (!window.confirm(t("confirmUnpair"))) return;
|
|
447
|
+
await run("unpair", async () => {
|
|
448
|
+
await unpair(phonePk);
|
|
449
|
+
await refresh();
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
async function onTogglePermission(phonePk, fullDisk) {
|
|
453
|
+
await run("perm", async () => {
|
|
454
|
+
await setPermission(phonePk, fullDisk);
|
|
455
|
+
await refresh();
|
|
456
|
+
setNotice(t("permissionChanged"));
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
async function onCopyLink() {
|
|
460
|
+
if (!pairing?.deepLink) return;
|
|
461
|
+
try {
|
|
462
|
+
await navigator.clipboard.writeText(pairing.deepLink);
|
|
463
|
+
setNotice(t("copied"));
|
|
464
|
+
} catch {
|
|
465
|
+
setError(t("operationFailed"));
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
const connected = status?.connection === "connected";
|
|
469
|
+
const devices = status?.devices || [];
|
|
470
|
+
const recommendedPending = (recommended || []).filter((p) => !p.installed);
|
|
471
|
+
return /* @__PURE__ */ import_react.default.createElement("div", { style: sectionStyle }, /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 16 } }, /* @__PURE__ */ import_react.default.createElement("h2", { style: { margin: 0, fontSize: 18, fontWeight: 600, color: "var(--dsw-alias-label-primary, inherit)" } }, t("nav")), /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, flexShrink: 0 } }, /* @__PURE__ */ import_react.default.createElement("code", { style: { fontSize: 13, opacity: 0.75 } }, status?.version ? `v${status.version}` : "\u2026"), /* @__PURE__ */ import_react.default.createElement(
|
|
472
|
+
"button",
|
|
473
|
+
{
|
|
474
|
+
title: t("checkUpdate"),
|
|
475
|
+
"aria-label": t("checkUpdate"),
|
|
476
|
+
onClick: () => loadUpdate(true),
|
|
477
|
+
disabled: updateBusy,
|
|
478
|
+
style: {
|
|
479
|
+
border: "none",
|
|
480
|
+
background: "transparent",
|
|
481
|
+
cursor: updateBusy ? "default" : "pointer",
|
|
482
|
+
padding: 6,
|
|
483
|
+
borderRadius: 6,
|
|
484
|
+
color: "inherit",
|
|
485
|
+
opacity: updateBusy ? 0.4 : 0.7,
|
|
486
|
+
lineHeight: 0
|
|
487
|
+
}
|
|
488
|
+
},
|
|
489
|
+
/* @__PURE__ */ import_react.default.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M21 12a9 9 0 1 1-2.64-6.36" }), /* @__PURE__ */ import_react.default.createElement("polyline", { points: "21 3 21 9 15 9" }))
|
|
490
|
+
))), /* @__PURE__ */ import_react.default.createElement("p", { style: { margin: 0, fontSize: 13, color: "var(--dsw-alias-label-tertiary, inherit)", lineHeight: 1.6, maxWidth: 560 } }, t("intro")), updateInfo?.updateAvailable && /* @__PURE__ */ import_react.default.createElement("div", { style: { ...cardStyle, borderColor: "#e8890c", padding: "10px 16px" } }, /* @__PURE__ */ import_react.default.createElement("div", { style: { ...rowStyle, gap: 10 } }, /* @__PURE__ */ import_react.default.createElement("span", { style: { fontSize: 13, color: "#e8890c" } }, t("updateAvailable"), " v", updateInfo.latest), /* @__PURE__ */ import_react.default.createElement("span", { style: { display: "flex", alignItems: "center", gap: 8 } }, /* @__PURE__ */ import_react.default.createElement("button", { style: btnStyle, onClick: onUpgrade, disabled: updateBusy }, updateBusy ? t("upgrading") : t("upgrade"))))), /* @__PURE__ */ import_react.default.createElement("div", { style: cardStyle }, /* @__PURE__ */ import_react.default.createElement("div", { style: rowStyle }, /* @__PURE__ */ import_react.default.createElement("span", { style: labelStyle }, t("relayServer")), /* @__PURE__ */ import_react.default.createElement("span", { style: { display: "flex", alignItems: "center", gap: 8 } }, statusBadge(status?.connection, t, status?.relayUrl), tokenBadge(status?.tokenState, t))), /* @__PURE__ */ import_react.default.createElement("div", { style: rowStyle }, /* @__PURE__ */ import_react.default.createElement("span", { style: labelStyle }, t("accountName")), /* @__PURE__ */ import_react.default.createElement("code", null, status?.accountName || "\u2026")), /* @__PURE__ */ import_react.default.createElement("div", { style: rowStyle }, /* @__PURE__ */ import_react.default.createElement("span", { style: labelStyle }, t("deviceId")), /* @__PURE__ */ import_react.default.createElement("code", null, status?.deviceId ? status.deviceId.slice(0, 12) + "\u2026" : "\u2026")), /* @__PURE__ */ import_react.default.createElement("div", { style: rowStyle }, /* @__PURE__ */ import_react.default.createElement("span", { style: labelStyle }, t("hostname")), /* @__PURE__ */ import_react.default.createElement("code", null, status?.hostname || "\u2026"))), recommendedPending.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { style: cardStyle }, /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 15, fontWeight: "600" } }, t("recommendedTitle")), /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 13, opacity: 0.7, lineHeight: 1.5 } }, t("recommendedHint")), recommendedPending.map((p) => /* @__PURE__ */ import_react.default.createElement("div", { key: p.id, style: rowStyle }, /* @__PURE__ */ import_react.default.createElement("span", { style: { display: "flex", alignItems: "center", gap: 8, fontSize: 13 } }, /* @__PURE__ */ import_react.default.createElement("span", null, p.icon ? /* @__PURE__ */ import_react.default.createElement("span", { style: { fontSize: 16 } }, "\u{1F4E6}") : null), /* @__PURE__ */ import_react.default.createElement("span", null, /* @__PURE__ */ import_react.default.createElement("strong", null, p.title), " \u2014 ", p.description)), /* @__PURE__ */ import_react.default.createElement(
|
|
491
|
+
"button",
|
|
492
|
+
{
|
|
493
|
+
style: btnStyle,
|
|
494
|
+
onClick: () => onInstallPlugin(p.id),
|
|
495
|
+
disabled: installBusy !== null
|
|
496
|
+
},
|
|
497
|
+
installBusy === p.id ? t("installing") : t("install")
|
|
498
|
+
), installNotice?.id === p.id && /* @__PURE__ */ import_react.default.createElement("span", { style: { fontSize: 12, color: installNotice.kind === "err" ? "#c92a2a" : "#2f9e44" } }, installNotice.text)))), /* @__PURE__ */ import_react.default.createElement("div", { style: cardStyle }, /* @__PURE__ */ import_react.default.createElement("div", { style: rowStyle }, /* @__PURE__ */ import_react.default.createElement("strong", null, t("pairingTitle")), !pairing && /* @__PURE__ */ import_react.default.createElement("button", { style: btnStyle, onClick: onStartPairing, disabled: busy !== null || !connected }, t("startPairing"))), pairing && pairing.phase === "waiting-scan" && pairing.qrDataUrl && /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "grid", justifyItems: "center", gap: 10 } }, /* @__PURE__ */ import_react.default.createElement("img", { src: pairing.qrDataUrl, alt: "QR", style: { width: 220, height: 220, borderRadius: 8 } }), /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 13, opacity: 0.75 } }, t("scanHint")), /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 12, opacity: 0.6 } }, pairing.pairingId, " \xB7 ", pairing.fp.slice(0, 12), "\u2026"), /* @__PURE__ */ import_react.default.createElement("button", { style: btnStyle, onClick: onCopyLink }, t("copyLink"))), pairing && pairing.phase === "done" && /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "grid", gap: 10 } }, /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 14, color: "#2f9e44" } }, t("pairingDone")), /* @__PURE__ */ import_react.default.createElement("button", { style: btnStyle, onClick: () => setPairing(null), disabled: busy !== null }, t("startPairing")))), pairing && pairing.phase === "awaiting-confirm" && /* @__PURE__ */ import_react.default.createElement("div", { style: modalBackdropStyle }, /* @__PURE__ */ import_react.default.createElement("div", { style: modalCardStyle }, /* @__PURE__ */ import_react.default.createElement("div", { style: modalTitleStyle }, t("confirmPairing")), /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 14, opacity: 0.8, marginBottom: 6 } }, t("awaitingConfirm")), /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 13, opacity: 0.65, marginBottom: 14 } }, pairing.phoneName ? `${t("deviceId")}: ${pairing.phoneName}` : `${pairing.pairingId.slice(0, 8)}\u2026 \xB7 ${t("deviceId")}: ${pairing.phonePk ? pairing.phonePk.slice(0, 10) + "\u2026" : "\u2014"}`), pairing.shortCode && /* @__PURE__ */ import_react.default.createElement("div", { style: { background: "var(--dsh-border, rgba(127,127,127,.08))", borderRadius: 8, padding: "12px 14px", marginBottom: 12, textAlign: "center" } }, /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 12, opacity: 0.7, marginBottom: 6 } }, t("shortCodeHint")), /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 28, fontWeight: 800, letterSpacing: 4, fontFamily: "monospace", color: "var(--dsh-fg, #1a1d26)" } }, pairing.shortCode), /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 12, opacity: 0.6, marginTop: 6, lineHeight: 1.5 } }, t("shortCodeCompare"))), pairing.known && /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 13, opacity: 0.7, marginBottom: 12 } }, t("knownDevice")), /* @__PURE__ */ import_react.default.createElement("label", { style: { ...switchStyle, marginBottom: 16, cursor: "pointer" } }, /* @__PURE__ */ import_react.default.createElement(
|
|
499
|
+
"input",
|
|
500
|
+
{
|
|
501
|
+
type: "checkbox",
|
|
502
|
+
checked: confirmFullDisk,
|
|
503
|
+
onChange: (e) => setConfirmFullDisk(e.target.checked),
|
|
504
|
+
disabled: busy !== null
|
|
505
|
+
}
|
|
506
|
+
), /* @__PURE__ */ import_react.default.createElement("span", null, t("fullDisk"))), /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 12, opacity: 0.6, marginBottom: 16, lineHeight: 1.5 } }, t("fullDiskHint")), /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", gap: 10 } }, /* @__PURE__ */ import_react.default.createElement("button", { style: { ...btnStyle, flex: 1, background: "#fff", borderColor: "#c92a2a", color: "#c92a2a", fontWeight: "600", padding: "12px 16px" }, onClick: onReject, disabled: busy !== null }, t("reject")), /* @__PURE__ */ import_react.default.createElement("button", { style: { ...btnStyle, flex: 2, background: "#2f9e44", borderColor: "#2f9e44", color: "#fff", fontWeight: "700", padding: "12px 16px" }, onClick: onConfirm, disabled: busy !== null }, t("approve"))))), /* @__PURE__ */ import_react.default.createElement("div", { style: cardStyle }, /* @__PURE__ */ import_react.default.createElement("strong", null, t("pairedDevices")), devices.length === 0 && /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 13, opacity: 0.6 } }, t("noDevices")), devices.map((d) => /* @__PURE__ */ import_react.default.createElement("div", { key: d.phonePk, style: { ...rowStyle, padding: "6px 0", borderTop: "1px solid var(--dsh-border, rgba(127,127,127,.15))" } }, /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "grid", gap: 4 } }, /* @__PURE__ */ import_react.default.createElement("code", { style: { fontSize: 13, fontWeight: d.name ? 600 : 400 } }, d.name || d.shortId + "\u2026"), /* @__PURE__ */ import_react.default.createElement("span", { style: { fontSize: 12, opacity: 0.6 } }, t("lastUsed"), ": ", fmtTime(d.lastUsedAt, t)), /* @__PURE__ */ import_react.default.createElement("label", { style: { ...switchStyle, cursor: "pointer", opacity: 0.9 } }, /* @__PURE__ */ import_react.default.createElement(
|
|
507
|
+
"input",
|
|
508
|
+
{
|
|
509
|
+
type: "checkbox",
|
|
510
|
+
checked: !!(d.permission && d.permission.fullDisk),
|
|
511
|
+
onChange: (e) => onTogglePermission(d.phonePk, e.target.checked),
|
|
512
|
+
disabled: busy !== null
|
|
513
|
+
}
|
|
514
|
+
), /* @__PURE__ */ import_react.default.createElement("span", null, t("fullDisk")))), /* @__PURE__ */ import_react.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10 } }, /* @__PURE__ */ import_react.default.createElement("span", { style: deviceStatusStyle(d.online !== false) }, d.online === false ? `\u25CB ${t("deviceOffline")}` : `\u25CF ${t("deviceOnline")}`), /* @__PURE__ */ import_react.default.createElement("button", { style: btnStyle, onClick: () => onUnpair(d.phonePk), disabled: busy !== null }, t("unpair")))))), notice && /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 13, color: "#2f9e44" } }, notice), error && /* @__PURE__ */ import_react.default.createElement("div", { style: { fontSize: 13, color: "#c92a2a" } }, error), toast && /* @__PURE__ */ import_react.default.createElement("div", { style: {
|
|
515
|
+
position: "fixed",
|
|
516
|
+
top: 24,
|
|
517
|
+
right: 24,
|
|
518
|
+
zIndex: 1e4,
|
|
519
|
+
background: toast.kind === "err" ? "#c92a2a" : "#2f9e44",
|
|
520
|
+
color: "#fff",
|
|
521
|
+
borderRadius: 8,
|
|
522
|
+
padding: "10px 16px",
|
|
523
|
+
fontSize: 13,
|
|
524
|
+
boxShadow: "0 8px 24px rgba(0,0,0,.25)",
|
|
525
|
+
maxWidth: 320,
|
|
526
|
+
lineHeight: 1.5
|
|
527
|
+
} }, toast.text));
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// src/client/index.jsx
|
|
531
|
+
var namespace = "dshfly";
|
|
532
|
+
var inject = ["slots", "locale"];
|
|
533
|
+
function apply(context) {
|
|
534
|
+
context.effect(
|
|
535
|
+
() => context.locale.register(namespace, { zh, en }),
|
|
536
|
+
"dshfly-remote-connector: dictionaries"
|
|
537
|
+
);
|
|
538
|
+
const translate = context.locale.bind(namespace);
|
|
539
|
+
const injected = () => ({ t: translate });
|
|
540
|
+
context.slots.inject(
|
|
541
|
+
"settings.section",
|
|
542
|
+
() => context.slots.register(
|
|
543
|
+
{
|
|
544
|
+
name: "settings.section",
|
|
545
|
+
id: "dshfly-remote-connector",
|
|
546
|
+
order: 30,
|
|
547
|
+
// 位于内置 models(10) 等之后
|
|
548
|
+
label: () => translate("nav"),
|
|
549
|
+
inject: injected
|
|
550
|
+
},
|
|
551
|
+
SettingsSection
|
|
552
|
+
)
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
return module.exports; } });
|
|
556
|
+
//# sourceMappingURL=client.js.map
|