@mangoc/dsh-sub2api-sync 0.2.8 → 0.2.9
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/README.md +62 -80
- package/README.zh.md +15 -3
- package/lib/client.cjs +228 -32
- package/lib/client.js +228 -32
- package/lib/config.d.ts +12 -2
- package/lib/dshgo.d.ts +35 -0
- package/lib/index.js +296 -27
- package/lib/types.d.ts +7 -0
- package/package.json +33 -2
package/lib/client.cjs
CHANGED
|
@@ -34,15 +34,17 @@ const PKG_NAME = __DSH_PLUGIN_NAME__;
|
|
|
34
34
|
const NS = "sub2api-sync";
|
|
35
35
|
const zh = {
|
|
36
36
|
nav: "Sub2API 同步",
|
|
37
|
-
localOnlyWarning: "
|
|
37
|
+
localOnlyWarning: "当前为跨设备连接:配置只读,仅本页生效",
|
|
38
38
|
configUnavailable: "配置暂不可用:宿主未连接或未暴露该设置条目",
|
|
39
|
+
routesUnreachableWarning: "动作路由不可达:当前连接可能经 DSH Remote 中继,或宿主半侧未就绪——登录、勾选与同步请回到电脑上的 DSH Web",
|
|
39
40
|
nameLine: "sub2api · {instance}",
|
|
40
41
|
nameLineSite: "{name} · {instance}",
|
|
41
42
|
balanceLabel: "余额 {amount}",
|
|
42
43
|
balanceTitle: "面板账号余额(同步结束自动刷新)",
|
|
43
|
-
signedOut: "
|
|
44
|
+
signedOut: "未连接面板",
|
|
44
45
|
signedInAs: "已登录:{email}",
|
|
45
46
|
signIn: "登录",
|
|
47
|
+
connectPanel: "连接面板",
|
|
46
48
|
signOut: "登出",
|
|
47
49
|
signOutDone: "已登出",
|
|
48
50
|
baseURLLabel: "实例地址",
|
|
@@ -51,8 +53,8 @@ const zh = {
|
|
|
51
53
|
savedNotice: "已保存",
|
|
52
54
|
writeRefused: "写入未被接受,已恢复为最新状态",
|
|
53
55
|
accountTitle: "面板账号",
|
|
54
|
-
accountSignedOutHint: "
|
|
55
|
-
signInTitle: "
|
|
56
|
+
accountSignedOutHint: "连接面板后自动发现分组并准备 key,勾选即可同步",
|
|
57
|
+
signInTitle: "连接 sub2api 面板",
|
|
56
58
|
channelLabel: "登录方式",
|
|
57
59
|
channelPassword: "密码登录",
|
|
58
60
|
channelOAuth: "网页授权",
|
|
@@ -66,7 +68,7 @@ const zh = {
|
|
|
66
68
|
signedInOAuthAccount: "{account} · 已通过网页授权登录",
|
|
67
69
|
emailPlaceholder: "you@example.com",
|
|
68
70
|
passwordLabel: "密码",
|
|
69
|
-
passwordHint: "
|
|
71
|
+
passwordHint: "留空则使用已保存的密码重新登录",
|
|
70
72
|
twoFactorPlaceholder: "6 位数字验证码",
|
|
71
73
|
twoFactorSubmit: "验证",
|
|
72
74
|
back: "返回",
|
|
@@ -94,7 +96,7 @@ const zh = {
|
|
|
94
96
|
actionFailed: "失败",
|
|
95
97
|
actionFailedLabel: "操作失败:{message}",
|
|
96
98
|
bundleTemplateLabel: "供应商名模板",
|
|
97
|
-
bundleTemplateHint: "
|
|
99
|
+
bundleTemplateHint: "供应商显示名模板,须包含 {keyName} 占位符",
|
|
98
100
|
bundleTemplateInvalid: "模板必须包含 {keyName} 占位符",
|
|
99
101
|
bundleKeyQuotaLabel: "新建 key 配额(USD)",
|
|
100
102
|
bundleKeyQuotaHint: "自动建 key 时应用;留空不设置",
|
|
@@ -108,7 +110,7 @@ const zh = {
|
|
|
108
110
|
bundleDeleteOrphanHint: "仅对记录过 keyId 的分组生效",
|
|
109
111
|
bundleEnrichLabel: "补全模型元数据",
|
|
110
112
|
bundleEnrichHint: "同步时按模型 id 从 models.dev 补全容量元数据;关闭则仅用内置快照(离线)",
|
|
111
|
-
bundleAllowRemoteLabel: "
|
|
113
|
+
bundleAllowRemoteLabel: "允许私网设备调用动作路由",
|
|
112
114
|
bundleAllowRemoteWarning: "开启后同源或私网页面可改写模型路由与凭据——仅在可信内网开启",
|
|
113
115
|
bundleAdvancedHint: "protocolOverrides / compatTemplate 高级字段请编辑 cordis.patch.yml",
|
|
114
116
|
badgeManagedTag: "Sub2API 同步",
|
|
@@ -122,7 +124,9 @@ const zh = {
|
|
|
122
124
|
detailBadgeSynced: "已同步 {count} 组",
|
|
123
125
|
detailBadgeSignedOut: "未连接面板",
|
|
124
126
|
detailSectionTitle: "最近同步",
|
|
125
|
-
detailSectionEmpty: "
|
|
127
|
+
detailSectionEmpty: "还没有同步过",
|
|
128
|
+
detailConnectTitle: "面板连接",
|
|
129
|
+
detailConnectHint: "连接面板后即可勾选 key 并同步,无需前往设置页",
|
|
126
130
|
activationTitle: "开始使用 Sub2API 同步",
|
|
127
131
|
activationStep1: "1. 在设置中连接面板(账号密码或网页授权)",
|
|
128
132
|
activationStep2: "2. 在 Keys 卡勾选要同步的 key(可跨分组)",
|
|
@@ -137,9 +141,9 @@ const zh = {
|
|
|
137
141
|
keyNameLabel: "Key 名称",
|
|
138
142
|
keyNameHint: "默认与分组同名",
|
|
139
143
|
keyGroupPlaceholder: "选择分组",
|
|
140
|
-
keyCreated: "
|
|
144
|
+
keyCreated: "已创建并勾选:{name}",
|
|
141
145
|
keysListEmpty: "还没有 key",
|
|
142
|
-
keysListLoading: "正在读取
|
|
146
|
+
keysListLoading: "正在读取 key…",
|
|
143
147
|
keysSearchPlaceholder: "搜索 key…",
|
|
144
148
|
keysSearchEmpty: "没有匹配的 key",
|
|
145
149
|
syncSelected: "同步所选({count})",
|
|
@@ -150,15 +154,17 @@ const zh = {
|
|
|
150
154
|
};
|
|
151
155
|
const en = {
|
|
152
156
|
nav: "Sub2API Sync",
|
|
153
|
-
localOnlyWarning: "
|
|
157
|
+
localOnlyWarning: "Cross-device connection: configuration is read-only and applies to this page only",
|
|
154
158
|
configUnavailable: "Configuration is unavailable: the host is not connected or does not expose this entry",
|
|
159
|
+
routesUnreachableWarning: "Action routes are unreachable: this connection may go through a DSH-Remote relay or the host half is not ready — sign in, select keys and sync from the desktop DSH Web",
|
|
155
160
|
nameLine: "sub2api · {instance}",
|
|
156
161
|
nameLineSite: "{name} · {instance}",
|
|
157
162
|
balanceLabel: "Balance {amount}",
|
|
158
163
|
balanceTitle: "Panel account balance (refreshed after each sync)",
|
|
159
|
-
signedOut: "
|
|
164
|
+
signedOut: "Not connected",
|
|
160
165
|
signedInAs: "Signed in as {email}",
|
|
161
166
|
signIn: "Sign in",
|
|
167
|
+
connectPanel: "Connect panel",
|
|
162
168
|
signOut: "Sign out",
|
|
163
169
|
signOutDone: "Signed out",
|
|
164
170
|
baseURLLabel: "Instance address",
|
|
@@ -167,8 +173,8 @@ const en = {
|
|
|
167
173
|
savedNotice: "Saved",
|
|
168
174
|
writeRefused: "The write was not accepted; the latest state was restored",
|
|
169
175
|
accountTitle: "Panel account",
|
|
170
|
-
accountSignedOutHint: "
|
|
171
|
-
signInTitle: "
|
|
176
|
+
accountSignedOutHint: "Connect the panel to discover its groups and prepare a key per group; tick the ones to sync",
|
|
177
|
+
signInTitle: "Connect to the sub2api panel",
|
|
172
178
|
channelLabel: "Sign-in channel",
|
|
173
179
|
channelPassword: "Password",
|
|
174
180
|
channelOAuth: "Web authorization",
|
|
@@ -182,7 +188,7 @@ const en = {
|
|
|
182
188
|
signedInOAuthAccount: "{account} · Signed in via web authorization",
|
|
183
189
|
emailPlaceholder: "you@example.com",
|
|
184
190
|
passwordLabel: "Password",
|
|
185
|
-
passwordHint: "Leave empty to
|
|
191
|
+
passwordHint: "Leave empty to sign in again with the stored password",
|
|
186
192
|
twoFactorPlaceholder: "6-digit code",
|
|
187
193
|
twoFactorSubmit: "Verify",
|
|
188
194
|
back: "Back",
|
|
@@ -210,7 +216,7 @@ const en = {
|
|
|
210
216
|
actionFailed: "failed",
|
|
211
217
|
actionFailedLabel: "Operation failed: {message}",
|
|
212
218
|
bundleTemplateLabel: "Provider name template",
|
|
213
|
-
bundleTemplateHint: "
|
|
219
|
+
bundleTemplateHint: "Provider display-name template; must contain the {keyName} placeholder",
|
|
214
220
|
bundleTemplateInvalid: "The template must contain the {keyName} placeholder",
|
|
215
221
|
bundleKeyQuotaLabel: "Created key quota (USD)",
|
|
216
222
|
bundleKeyQuotaHint: "Applied to auto-created keys; empty leaves it unset",
|
|
@@ -238,7 +244,9 @@ const en = {
|
|
|
238
244
|
detailBadgeSynced: "{count} group(s) synced",
|
|
239
245
|
detailBadgeSignedOut: "Panel not connected",
|
|
240
246
|
detailSectionTitle: "Last sync",
|
|
241
|
-
detailSectionEmpty: "Never synced
|
|
247
|
+
detailSectionEmpty: "Never synced",
|
|
248
|
+
detailConnectTitle: "Panel connection",
|
|
249
|
+
detailConnectHint: "Connect the panel to pick keys and sync — no need to visit the settings page",
|
|
242
250
|
activationTitle: "Get started with Sub2API Sync",
|
|
243
251
|
activationStep1: "1. Connect the panel in Settings (account password or web authorization)",
|
|
244
252
|
activationStep2: "2. Tick the keys to sync on the Keys card (cross-group)",
|
|
@@ -253,7 +261,7 @@ const en = {
|
|
|
253
261
|
keyNameLabel: "Key name",
|
|
254
262
|
keyNameHint: "Defaults to the group name",
|
|
255
263
|
keyGroupPlaceholder: "Pick a group",
|
|
256
|
-
keyCreated: "Created and
|
|
264
|
+
keyCreated: "Created and ticked: {name}",
|
|
257
265
|
keysListEmpty: "No keys yet",
|
|
258
266
|
keysListLoading: "Loading keys…",
|
|
259
267
|
keysSearchPlaceholder: "Search keys…",
|
|
@@ -433,9 +441,20 @@ function createPluginApi() {
|
|
|
433
441
|
//#endregion
|
|
434
442
|
//#region src/client/status.ts
|
|
435
443
|
const POLL_MS = 5e3;
|
|
444
|
+
/** Consecutive failed rounds before the card shows the unreachable banner. */
|
|
445
|
+
const DEGRADED_AFTER = 2;
|
|
436
446
|
let snapshot;
|
|
437
447
|
let timer;
|
|
438
448
|
const listeners = new Set();
|
|
449
|
+
/** Reachability of the plugin's action routes, as observed by the poller. */
|
|
450
|
+
let reachable = true;
|
|
451
|
+
let failures = 0;
|
|
452
|
+
const healthListeners = new Set();
|
|
453
|
+
function publishHealth(next) {
|
|
454
|
+
if (next === reachable) return;
|
|
455
|
+
reachable = next;
|
|
456
|
+
for (const listener of healthListeners) listener();
|
|
457
|
+
}
|
|
439
458
|
function publish(next) {
|
|
440
459
|
if (next === snapshot) return;
|
|
441
460
|
snapshot = next;
|
|
@@ -445,7 +464,12 @@ function publish(next) {
|
|
|
445
464
|
async function poll() {
|
|
446
465
|
try {
|
|
447
466
|
publish(await callJson(ROUTES.status));
|
|
448
|
-
|
|
467
|
+
failures = 0;
|
|
468
|
+
publishHealth(true);
|
|
469
|
+
} catch {
|
|
470
|
+
failures += 1;
|
|
471
|
+
if (failures >= DEGRADED_AFTER) publishHealth(false);
|
|
472
|
+
}
|
|
449
473
|
}
|
|
450
474
|
function subscribe(listener) {
|
|
451
475
|
listeners.add(listener);
|
|
@@ -470,6 +494,18 @@ const statusSource = {
|
|
|
470
494
|
function useStatus() {
|
|
471
495
|
return (0, react.useSyncExternalStore)(subscribe, getSnapshot);
|
|
472
496
|
}
|
|
497
|
+
const statusHealthSource = {
|
|
498
|
+
getSnapshot: () => reachable,
|
|
499
|
+
subscribe(fn) {
|
|
500
|
+
healthListeners.add(fn);
|
|
501
|
+
return () => {
|
|
502
|
+
healthListeners.delete(fn);
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
function useStatusHealth() {
|
|
507
|
+
return (0, react.useSyncExternalStore)(statusHealthSource.subscribe, statusHealthSource.getSnapshot);
|
|
508
|
+
}
|
|
473
509
|
function getSnapshot() {
|
|
474
510
|
return snapshot;
|
|
475
511
|
}
|
|
@@ -934,6 +970,88 @@ const CSS = `
|
|
|
934
970
|
font-size: 13px;
|
|
935
971
|
line-height: 22px;
|
|
936
972
|
}
|
|
973
|
+
|
|
974
|
+
/* Mobile (dsh-Go web island / narrow windows): stack the row layouts, keep
|
|
975
|
+
interactive rows at Android's 44dp touch floor, and let the anchored group
|
|
976
|
+
panel clear the soft keyboard. The desktop spec above is untouched — every
|
|
977
|
+
rule here is scoped to the narrow viewport. */
|
|
978
|
+
@media (max-width: 640px) {
|
|
979
|
+
.s2s-section {
|
|
980
|
+
gap: 12px;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
.s2s-row,
|
|
984
|
+
.s2s-inline {
|
|
985
|
+
flex-direction: column;
|
|
986
|
+
align-items: stretch;
|
|
987
|
+
gap: 8px;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
.s2s-actions {
|
|
991
|
+
justify-content: flex-start;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
.s2s-actions .s2s-feedback {
|
|
995
|
+
white-space: normal;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
.s2s-keyRow {
|
|
999
|
+
grid-template-columns: minmax(0, 1fr);
|
|
1000
|
+
min-height: 44px;
|
|
1001
|
+
padding: 8px 10px;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
.s2s-keyRow > :last-child {
|
|
1005
|
+
justify-self: start;
|
|
1006
|
+
text-align: left;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
.s2s-selectField,
|
|
1010
|
+
.s2s-groupOption {
|
|
1011
|
+
min-height: 44px;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
.s2s-input,
|
|
1015
|
+
.s2s-groupPicker {
|
|
1016
|
+
flex-basis: 100%;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
.s2s-searchInput {
|
|
1020
|
+
width: 100%;
|
|
1021
|
+
flex: 1 1 auto;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
.s2s-pager {
|
|
1025
|
+
justify-content: center;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
.s2s-footerStrip {
|
|
1029
|
+
flex-direction: column;
|
|
1030
|
+
align-items: flex-start;
|
|
1031
|
+
gap: 6px;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
.s2s-reportRemove {
|
|
1035
|
+
margin-left: 0;
|
|
1036
|
+
align-self: flex-end;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
/* Keyboard clearance: the fixed-position panel must not run under the
|
|
1040
|
+
soft keyboard; dvh tracks the visual viewport on Android WebViews. */
|
|
1041
|
+
.s2s-groupPanel {
|
|
1042
|
+
max-height: 60dvh;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
/* Notch and gesture-bar insets on standalone web islands. */
|
|
1047
|
+
@supports (padding: max(0px)) {
|
|
1048
|
+
@media (max-width: 640px) {
|
|
1049
|
+
.s2s-section {
|
|
1050
|
+
padding-left: max(8px, env(safe-area-inset-left));
|
|
1051
|
+
padding-right: max(8px, env(safe-area-inset-right));
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
937
1055
|
`;
|
|
938
1056
|
function ensureCss() {
|
|
939
1057
|
if (document.querySelector("style[data-plugin=\"dsh-sub2api-sync\"]") !== null) return;
|
|
@@ -1824,6 +1942,7 @@ function actionToneOf(action) {
|
|
|
1824
1942
|
}
|
|
1825
1943
|
function Sub2apiSyncSection({ t, api, config, useStatus: useStatus$1 }) {
|
|
1826
1944
|
const status = useStatus$1((value) => value);
|
|
1945
|
+
const routesReachable = useStatusHealth();
|
|
1827
1946
|
const snapshot$1 = useConfigForm(config);
|
|
1828
1947
|
const configValues = snapshot$1.value;
|
|
1829
1948
|
const writable = snapshot$1.writable;
|
|
@@ -2019,6 +2138,10 @@ function Sub2apiSyncSection({ t, api, config, useStatus: useStatus$1 }) {
|
|
|
2019
2138
|
className: "s2s-warn",
|
|
2020
2139
|
children: t("configUnavailable")
|
|
2021
2140
|
}),
|
|
2141
|
+
!routesReachable && (0, react_jsx_runtime.jsx)("p", {
|
|
2142
|
+
className: "s2s-warn",
|
|
2143
|
+
children: t("routesUnreachableWarning")
|
|
2144
|
+
}),
|
|
2022
2145
|
(0, react_jsx_runtime.jsx)("section", {
|
|
2023
2146
|
className: "s2s-card",
|
|
2024
2147
|
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -2072,7 +2195,7 @@ function Sub2apiSyncSection({ t, api, config, useStatus: useStatus$1 }) {
|
|
|
2072
2195
|
onClick: () => {
|
|
2073
2196
|
setSignInOpen(true);
|
|
2074
2197
|
},
|
|
2075
|
-
children: t("
|
|
2198
|
+
children: t("connectPanel")
|
|
2076
2199
|
})
|
|
2077
2200
|
]
|
|
2078
2201
|
})]
|
|
@@ -2248,22 +2371,32 @@ function parseNumber(text) {
|
|
|
2248
2371
|
function messageOf(error) {
|
|
2249
2372
|
return error instanceof Error ? error.message : String(error);
|
|
2250
2373
|
}
|
|
2251
|
-
function BundleConfigCard({ view,
|
|
2252
|
-
if (view !== "page"
|
|
2374
|
+
function BundleConfigCard({ view, t, config }) {
|
|
2375
|
+
if (view !== "page") return null;
|
|
2253
2376
|
return (0, react_jsx_runtime.jsx)(BundleConfigFields, {
|
|
2254
|
-
|
|
2377
|
+
config,
|
|
2255
2378
|
t
|
|
2256
2379
|
});
|
|
2257
2380
|
}
|
|
2258
|
-
function BundleConfigFields({
|
|
2259
|
-
const
|
|
2260
|
-
const
|
|
2381
|
+
function BundleConfigFields({ config, t }) {
|
|
2382
|
+
const snapshot$1 = useConfigForm(config);
|
|
2383
|
+
const values = snapshot$1.value ?? {};
|
|
2384
|
+
const writable = snapshot$1.writable;
|
|
2261
2385
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
2262
2386
|
const [feedback, setFeedback] = (0, react.useState)(undefined);
|
|
2263
2387
|
const run = (ops) => {
|
|
2264
2388
|
setBusy(true);
|
|
2265
2389
|
setFeedback(undefined);
|
|
2266
|
-
|
|
2390
|
+
const apply$1 = async () => {
|
|
2391
|
+
for (const op of ops) {
|
|
2392
|
+
const field = op.path[0];
|
|
2393
|
+
if (field === undefined || field === "") return false;
|
|
2394
|
+
const accepted = op.op === "set" ? await setField(config, field, op.value) : await unsetField(config, field);
|
|
2395
|
+
if (!accepted) return false;
|
|
2396
|
+
}
|
|
2397
|
+
return true;
|
|
2398
|
+
};
|
|
2399
|
+
apply$1().then((accepted) => {
|
|
2267
2400
|
setFeedback(accepted ? {
|
|
2268
2401
|
tone: "ok",
|
|
2269
2402
|
text: t("savedNotice")
|
|
@@ -2523,6 +2656,9 @@ function footerModel(status) {
|
|
|
2523
2656
|
...status?.lastReportAt !== undefined ? { lastReportAt: status.lastReportAt } : {}
|
|
2524
2657
|
};
|
|
2525
2658
|
}
|
|
2659
|
+
function detailCardModel(status) {
|
|
2660
|
+
return status?.auth?.signedIn === true ? "report" : "connect";
|
|
2661
|
+
}
|
|
2526
2662
|
function relativeMinutes(at, now) {
|
|
2527
2663
|
const minutes = Math.max(0, Math.floor((now - at) / 6e4));
|
|
2528
2664
|
if (minutes < 1) return { key: "footerJustNow" };
|
|
@@ -2608,11 +2744,64 @@ function DetailBadgeBody({ t }) {
|
|
|
2608
2744
|
});
|
|
2609
2745
|
}
|
|
2610
2746
|
function DetailSection(props) {
|
|
2611
|
-
const { subject, t } = props;
|
|
2747
|
+
const { subject, t, api, config } = props;
|
|
2612
2748
|
if (!isOwnBundle(subject)) return null;
|
|
2613
|
-
return (0, react_jsx_runtime.jsx)(DetailSectionBody, {
|
|
2749
|
+
return (0, react_jsx_runtime.jsx)(DetailSectionBody, {
|
|
2750
|
+
t,
|
|
2751
|
+
api,
|
|
2752
|
+
config
|
|
2753
|
+
});
|
|
2614
2754
|
}
|
|
2615
|
-
function DetailSectionBody({ t }) {
|
|
2755
|
+
function DetailSectionBody({ t, api, config }) {
|
|
2756
|
+
const status = useStatus();
|
|
2757
|
+
const reachable$1 = useStatusHealth();
|
|
2758
|
+
const snapshot$1 = useConfigForm(config);
|
|
2759
|
+
const [signInOpen, setSignInOpen] = (0, react.useState)(false);
|
|
2760
|
+
if (detailCardModel(status) === "connect") return (0, react_jsx_runtime.jsxs)("section", {
|
|
2761
|
+
className: "s2s-card",
|
|
2762
|
+
children: [
|
|
2763
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
2764
|
+
className: "s2s-name",
|
|
2765
|
+
children: t("detailConnectTitle")
|
|
2766
|
+
}),
|
|
2767
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
2768
|
+
className: "s2s-hint",
|
|
2769
|
+
children: t("detailConnectHint")
|
|
2770
|
+
}),
|
|
2771
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
2772
|
+
className: "s2s-actions",
|
|
2773
|
+
children: (0, react_jsx_runtime.jsx)(__deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
2774
|
+
variant: "primary",
|
|
2775
|
+
disabled: !reachable$1,
|
|
2776
|
+
onClick: () => {
|
|
2777
|
+
setSignInOpen(true);
|
|
2778
|
+
},
|
|
2779
|
+
children: t("connectPanel")
|
|
2780
|
+
})
|
|
2781
|
+
}),
|
|
2782
|
+
!reachable$1 && (0, react_jsx_runtime.jsx)("p", {
|
|
2783
|
+
className: "s2s-warn",
|
|
2784
|
+
children: t("routesUnreachableWarning")
|
|
2785
|
+
}),
|
|
2786
|
+
(0, react_jsx_runtime.jsx)(SignInDialog, {
|
|
2787
|
+
open: signInOpen,
|
|
2788
|
+
onClose: () => {
|
|
2789
|
+
setSignInOpen(false);
|
|
2790
|
+
},
|
|
2791
|
+
api,
|
|
2792
|
+
config,
|
|
2793
|
+
baseURL: snapshot$1.value?.baseURL,
|
|
2794
|
+
writable: snapshot$1.writable,
|
|
2795
|
+
onSignedIn: () => {
|
|
2796
|
+
void refreshStatus();
|
|
2797
|
+
},
|
|
2798
|
+
t
|
|
2799
|
+
})
|
|
2800
|
+
]
|
|
2801
|
+
});
|
|
2802
|
+
return (0, react_jsx_runtime.jsx)(DetailReportCard, { t });
|
|
2803
|
+
}
|
|
2804
|
+
function DetailReportCard({ t }) {
|
|
2616
2805
|
const status = useStatus();
|
|
2617
2806
|
const report = status?.lastReport;
|
|
2618
2807
|
return (0, react_jsx_runtime.jsxs)("section", {
|
|
@@ -2702,10 +2891,12 @@ function mount(ctx) {
|
|
|
2702
2891
|
locale: NS,
|
|
2703
2892
|
inject: () => sectionOps
|
|
2704
2893
|
}, Sub2apiSyncSection));
|
|
2894
|
+
const bundleConfigOps = { config };
|
|
2705
2895
|
ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
|
|
2706
2896
|
name: "plugins.bundle.config",
|
|
2707
2897
|
key: PKG_NAME,
|
|
2708
|
-
locale: NS
|
|
2898
|
+
locale: NS,
|
|
2899
|
+
inject: () => bundleConfigOps
|
|
2709
2900
|
}, BundleConfigCard));
|
|
2710
2901
|
ctx.slots.inject("settings.models.provider-card", () => ctx.slots.register({
|
|
2711
2902
|
name: "settings.models.provider-card",
|
|
@@ -2723,10 +2914,15 @@ function mount(ctx) {
|
|
|
2723
2914
|
id: "sub2api-sync",
|
|
2724
2915
|
locale: NS
|
|
2725
2916
|
}, DetailBadge));
|
|
2917
|
+
const detailOps = {
|
|
2918
|
+
api: sectionOps.api,
|
|
2919
|
+
config
|
|
2920
|
+
};
|
|
2726
2921
|
ctx.slots.inject("plugins.detail.section", () => ctx.slots.register({
|
|
2727
2922
|
name: "plugins.detail.section",
|
|
2728
2923
|
id: "sub2api-sync",
|
|
2729
|
-
locale: NS
|
|
2924
|
+
locale: NS,
|
|
2925
|
+
inject: () => detailOps
|
|
2730
2926
|
}, DetailSection));
|
|
2731
2927
|
const activationOps = { openBundleDetails: () => {
|
|
2732
2928
|
const nav = ctx.pluginNavigation;
|