@wenbin_wb/dsh-bridge 2.8.6 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +295 -235
- package/README.en.md +408 -572
- package/README.md +430 -570
- package/client/client.js +3654 -3487
- package/client/index.js +220 -877
- package/client/mobile-styles.js +802 -0
- package/docs/fix-plan-202608.md +108 -0
- package/lib/auth/login-template.js +381 -381
- package/lib/auth/manager.js +531 -469
- package/lib/bridge-rpc-constants.js +1 -0
- package/lib/bridge-rpc.js +436 -502
- package/lib/cloudflared-manager.mjs +361 -345
- package/lib/compat.js +129 -0
- package/lib/feishu/index.js +225 -222
- package/lib/feishu/node.js +433 -409
- package/lib/index.js +1765 -1739
- package/lib/platform/base.js +147 -156
- package/lib/platform/commands.js +221 -0
- package/lib/platform/conversation-bridge.js +816 -1570
- package/lib/platform/dsh-storage.js +117 -0
- package/lib/platform/index.js +10 -10
- package/lib/platform/message-split.js +191 -0
- package/lib/platform/session-catalog.js +372 -0
- package/lib/platform/stream-slices.js +21 -0
- package/lib/qq/index.js +312 -309
- package/lib/qq/node.js +532 -533
- package/lib/telegram/index.js +215 -212
- package/lib/telegram/node.js +348 -350
- package/lib/tunnel-client.mjs +39 -15
- package/lib/wechat/gateway.js +973 -960
- package/lib/wechat/index.js +244 -241
- package/lib/wechat/media.js +285 -281
- package/lib/wechat/node.js +352 -350
- package/package.json +106 -102
package/client/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MOBILE_STYLES_CSS } from './mobile-styles.js'
|
|
1
2
|
// dsh-bridge 客户端插件:设置页「远程访问」面板
|
|
2
3
|
|
|
3
4
|
// 兼容非 HTTPS 环境(如手机局域网 HTTP 访问):为非安全上下文补齐 crypto.randomUUID
|
|
@@ -111,6 +112,7 @@ const s = {
|
|
|
111
112
|
tag: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '3px 10px', borderRadius: 999, fontSize: 12, fontWeight: 500, whiteSpace: 'nowrap', flexShrink: 0, minWidth: 'max-content', lineHeight: 1.4 },
|
|
112
113
|
input: { width: '100%', font: 'inherit', fontSize: 13, padding: '7px 10px', borderRadius: 8, border: '1px solid var(--dsw-alias-border-l2,#d1d5db)', background: 'var(--dsw-alias-bg-layer-2,#f9fafb)', color: 'var(--dsw-alias-label-primary,currentColor)', outline: 'none', boxSizing: 'border-box' },
|
|
113
114
|
warn: { background: 'var(--dsw-alias-state-warn-bg,#fffbeb)', border: '1px solid var(--dsw-alias-state-warn-border,#fde68a)', borderRadius: 8, padding: '10px 14px', fontSize: 12, color: 'var(--dsw-alias-state-warn-primary,#92400e)', lineHeight: 1.6 },
|
|
115
|
+
err: { background: 'var(--dsw-alias-state-error-bg,#fef2f2)', border: '1px solid var(--dsw-alias-state-error-border,#fecaca)', borderRadius: 8, padding: '10px 14px', fontSize: 12, color: 'var(--dsw-alias-state-error-primary,#991b1b)', lineHeight: 1.6 },
|
|
114
116
|
tip: { background: 'var(--dsw-alias-bg-layer-2,#f9fafb)', borderRadius: 8, padding: '10px 14px', fontSize: 12, color: 'var(--dsw-alias-label-secondary,#6b7280)', lineHeight: 1.6 },
|
|
115
117
|
};
|
|
116
118
|
|
|
@@ -317,6 +319,78 @@ function QrBlock({ url, qr, onReset, auth, onNavigateSecurity }) {
|
|
|
317
319
|
);
|
|
318
320
|
}
|
|
319
321
|
|
|
322
|
+
const LanNetworkSelector = React.memo(function LanNetworkSelector({ lan, onSelectIp }) {
|
|
323
|
+
const interfaces = lan?.interfaces || [];
|
|
324
|
+
const selectedIp = lan?.selectedIp || '';
|
|
325
|
+
const currentIp = lan?.ip || '';
|
|
326
|
+
const [switching, setSwitching] = React.useState(false);
|
|
327
|
+
|
|
328
|
+
if (!interfaces || interfaces.length <= 1) return null;
|
|
329
|
+
|
|
330
|
+
const handleChange = async (e) => {
|
|
331
|
+
const val = e.target.value;
|
|
332
|
+
setSwitching(true);
|
|
333
|
+
try {
|
|
334
|
+
await onSelectIp(val || null);
|
|
335
|
+
} finally {
|
|
336
|
+
setSwitching(false);
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
return React.createElement('div', {
|
|
341
|
+
style: {
|
|
342
|
+
...s.block,
|
|
343
|
+
background: 'var(--dsw-alias-bg-layer-2, rgba(243, 244, 246, 0.6))',
|
|
344
|
+
padding: '10px 12px',
|
|
345
|
+
borderRadius: 8,
|
|
346
|
+
border: '1px solid var(--dsw-alias-border-l2, #e5e7eb)',
|
|
347
|
+
marginTop: 8,
|
|
348
|
+
marginBottom: 6,
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
React.createElement('div', {
|
|
352
|
+
style: {
|
|
353
|
+
display: 'flex',
|
|
354
|
+
alignItems: 'center',
|
|
355
|
+
justifyContent: 'space-between',
|
|
356
|
+
marginBottom: 6,
|
|
357
|
+
fontSize: 12,
|
|
358
|
+
fontWeight: 500,
|
|
359
|
+
color: 'var(--dsw-alias-label-primary, currentColor)',
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 5 } },
|
|
363
|
+
'🛜 局域网网卡 / IP 选择'
|
|
364
|
+
),
|
|
365
|
+
switching && React.createElement('span', {
|
|
366
|
+
style: { fontSize: 11, color: 'var(--dsw-alias-brand-primary, #4f6ef7)' },
|
|
367
|
+
}, '切换中…')
|
|
368
|
+
),
|
|
369
|
+
React.createElement('div', { style: { ...s.muted, fontSize: 11, marginBottom: 6 } },
|
|
370
|
+
'检测到主机存在多张网卡(如物理 Wi-Fi、以太网、WSL 或虚拟机)。若默认 IP 无法被移动端访问,可手动切换:'
|
|
371
|
+
),
|
|
372
|
+
React.createElement('select', {
|
|
373
|
+
style: {
|
|
374
|
+
...s.input,
|
|
375
|
+
height: 32,
|
|
376
|
+
fontSize: 12,
|
|
377
|
+
padding: '0 8px',
|
|
378
|
+
background: 'var(--dsw-alias-bg-layer-1, #ffffff)',
|
|
379
|
+
cursor: 'pointer',
|
|
380
|
+
},
|
|
381
|
+
value: selectedIp,
|
|
382
|
+
onChange: handleChange,
|
|
383
|
+
disabled: switching,
|
|
384
|
+
},
|
|
385
|
+
React.createElement('option', { value: '' }, `⚡ 自动推荐 (${interfaces[0]?.address || currentIp} · ${interfaces[0]?.label || interfaces[0]?.name || ''})`),
|
|
386
|
+
interfaces.map((iface) => React.createElement('option', {
|
|
387
|
+
key: `${iface.name}-${iface.address}`,
|
|
388
|
+
value: iface.address,
|
|
389
|
+
}, `${iface.address} · ${iface.label || iface.name}${iface.isVirtual ? ' [虚拟/WSL]' : ''}`)),
|
|
390
|
+
),
|
|
391
|
+
);
|
|
392
|
+
});
|
|
393
|
+
|
|
320
394
|
const CustomTunnelGuide = React.memo(function CustomTunnelGuide() {
|
|
321
395
|
return React.createElement('div', { style: s.block },
|
|
322
396
|
React.createElement('a', {
|
|
@@ -485,7 +559,11 @@ const CloudflareConfigForm = React.memo(function CloudflareConfigForm({ token, h
|
|
|
485
559
|
setSaving(true);
|
|
486
560
|
setMsg(null);
|
|
487
561
|
try {
|
|
488
|
-
|
|
562
|
+
// 只回传被修改的字段:掩码 '******' 与未变更的 token 都不上传,服务端保留现值
|
|
563
|
+
const patch = {};
|
|
564
|
+
if (hostnameVal !== (hostname || '')) patch.hostname = hostnameVal;
|
|
565
|
+
if (tokenVal !== (token || '')) patch.token = tokenVal;
|
|
566
|
+
if (Object.keys(patch).length > 0) await onSave(patch);
|
|
489
567
|
setMsg({ ok: true, text: '✓ 固定域名配置已保存' });
|
|
490
568
|
} catch (err) {
|
|
491
569
|
setMsg({ ok: false, text: err.message || '保存失败' });
|
|
@@ -987,7 +1065,7 @@ const AccessAuthCard = React.memo(function AccessAuthCard({ auth, rpcCall, onUpd
|
|
|
987
1065
|
|
|
988
1066
|
// ---- 通用 IM 平台卡片 ----
|
|
989
1067
|
|
|
990
|
-
function PlatformCard({ platformId, platformName, platformDesc, rpcCall
|
|
1068
|
+
function PlatformCard({ platformId, platformName, platformDesc, rpcCall }) {
|
|
991
1069
|
const [platform, setPlatform] = React.useState(null);
|
|
992
1070
|
const [err, setErr] = React.useState(null);
|
|
993
1071
|
const [busy, setBusy] = React.useState(false);
|
|
@@ -1003,6 +1081,7 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
1003
1081
|
approvalTimeoutSec: String(platform.config.approvalTimeoutSec ?? 600),
|
|
1004
1082
|
maxMessageChars: String((platform.config.maxMessageChars >= 500 ? platform.config.maxMessageChars : null) ?? (platformId === 'telegram' ? 4096 : 2000)),
|
|
1005
1083
|
sendChunkDelayMs: String(platform.config.sendChunkDelayMs ?? 1500),
|
|
1084
|
+
groupAutoApprove: Boolean(platform.config.groupAutoApprove),
|
|
1006
1085
|
appId: platform.config.appId ?? '',
|
|
1007
1086
|
// Secret 不由后端回传;空值表示沿用已保存密钥
|
|
1008
1087
|
clientSecret: '',
|
|
@@ -1014,12 +1093,6 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
1014
1093
|
}
|
|
1015
1094
|
}, [platform?.config, platformId]);
|
|
1016
1095
|
|
|
1017
|
-
// 向上传递连接状态(供平台列表卡片绿点使用)
|
|
1018
|
-
React.useEffect(() => {
|
|
1019
|
-
const connected = platform?.status === 'connected' || platform?.status === 'starting' || platform?.status === 'reconnecting';
|
|
1020
|
-
onStatusChange?.(connected);
|
|
1021
|
-
}, [platform?.status, onStatusChange]);
|
|
1022
|
-
|
|
1023
1096
|
const loadInFlightRef = React.useRef(false);
|
|
1024
1097
|
const seqRef = React.useRef(0);
|
|
1025
1098
|
const load = React.useCallback(async (quiet = false) => {
|
|
@@ -1067,6 +1140,12 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
1067
1140
|
|
|
1068
1141
|
const onLogin = React.useCallback(() => act(BRIDGE_ENDPOINTS.platformLogin, {}), [act]);
|
|
1069
1142
|
const onStop = React.useCallback(() => act(BRIDGE_ENDPOINTS.platformStop, {}), [act]);
|
|
1143
|
+
// 断开后重连:凭证仍在(configured)时直接用已存凭证重启网关,无需重新扫码;
|
|
1144
|
+
// 只有解绑后(无凭证)才走扫码/绑定流程
|
|
1145
|
+
const onReconnect = React.useCallback(() => {
|
|
1146
|
+
if (platform?.configured) return act(BRIDGE_ENDPOINTS.platformStart, {});
|
|
1147
|
+
return act(BRIDGE_ENDPOINTS.platformLogin, {});
|
|
1148
|
+
}, [act, platform?.configured]);
|
|
1070
1149
|
|
|
1071
1150
|
// 白名单管理
|
|
1072
1151
|
const [newId, setNewId] = React.useState('');
|
|
@@ -1111,11 +1190,16 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
1111
1190
|
// 高级设置保存
|
|
1112
1191
|
const saveConfig = React.useCallback(async () => {
|
|
1113
1192
|
if (!cfgDraft) return;
|
|
1193
|
+
// 非法数字输入回落到当前配置值/默认值,避免 NaN 发到服务端且使 cfgDirty 永真
|
|
1194
|
+
const num = (v, fallback) => {
|
|
1195
|
+
const n = Number(v);
|
|
1196
|
+
return Number.isFinite(n) ? n : fallback;
|
|
1197
|
+
};
|
|
1114
1198
|
const payload = {
|
|
1115
|
-
digestIntervalSec:
|
|
1116
|
-
approvalTimeoutSec:
|
|
1117
|
-
maxMessageChars:
|
|
1118
|
-
sendChunkDelayMs:
|
|
1199
|
+
digestIntervalSec: num(cfgDraft.digestIntervalSec, platform?.config?.digestIntervalSec ?? 300),
|
|
1200
|
+
approvalTimeoutSec: num(cfgDraft.approvalTimeoutSec, platform?.config?.approvalTimeoutSec ?? 600),
|
|
1201
|
+
maxMessageChars: num(cfgDraft.maxMessageChars, platform?.config?.maxMessageChars ?? 2000),
|
|
1202
|
+
sendChunkDelayMs: num(cfgDraft.sendChunkDelayMs, platform?.config?.sendChunkDelayMs ?? 1500),
|
|
1119
1203
|
};
|
|
1120
1204
|
// QQ / 飞书 / Telegram 平台额外携带凭证
|
|
1121
1205
|
if (platformId === 'qq') {
|
|
@@ -1329,9 +1413,9 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
1329
1413
|
),
|
|
1330
1414
|
React.createElement('div', { style: { display: 'flex', gap: 8, marginTop: 10, flexWrap: 'wrap', alignItems: 'center' } },
|
|
1331
1415
|
platform.status !== 'connected' && platform.status !== 'starting' &&
|
|
1332
|
-
React.createElement('button', { style: s.btnPri, onClick:
|
|
1416
|
+
React.createElement('button', { style: s.btnPri, onClick: onReconnect, disabled: busy, title: platform?.configured ? '使用已保存的凭证直接恢复连接,无需重新扫码' : '首次使用需扫码绑定' }, platform?.configured ? '重新连接' : '连接'),
|
|
1333
1417
|
(platform.status === 'connected' || platform.status === 'starting') &&
|
|
1334
|
-
React.createElement('button', { style: s.btnGhost, onClick: onStop, disabled: busy }, '断开'),
|
|
1418
|
+
React.createElement('button', { style: s.btnGhost, onClick: onStop, disabled: busy, title: '停止接收消息;凭证保留,可随时"重新连接"恢复' }, '断开'),
|
|
1335
1419
|
React.createElement('button', {
|
|
1336
1420
|
style: { ...s.btnGhost, color: 'var(--dsw-alias-state-error-primary,#dc2626)', borderColor: 'var(--dsw-alias-state-error-primary,#dc2626)', opacity: busy ? 0.5 : 1 },
|
|
1337
1421
|
disabled: busy,
|
|
@@ -1339,6 +1423,23 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
1339
1423
|
title: '清除登录凭证,下次需重新配置',
|
|
1340
1424
|
}, '解绑账号'),
|
|
1341
1425
|
),
|
|
1426
|
+
// 群聊自动授权开关(仅支持群聊的平台):关闭时新群首次 @机器人 不会自动加白
|
|
1427
|
+
platformId !== 'wechat' && React.createElement('label', {
|
|
1428
|
+
style: { display: 'flex', alignItems: 'center', gap: 6, marginTop: 10, fontSize: 12, cursor: 'pointer', width: 'fit-content', color: 'var(--dsw-alias-label-secondary,#6b7280)' },
|
|
1429
|
+
title: '开启后,白名单之外的新群首次 @机器人 即自动授权入白名单;关闭(默认)时新群必须先在此手动添加',
|
|
1430
|
+
},
|
|
1431
|
+
React.createElement('input', {
|
|
1432
|
+
type: 'checkbox',
|
|
1433
|
+
checked: Boolean(cfgDraft?.groupAutoApprove),
|
|
1434
|
+
disabled: busy,
|
|
1435
|
+
onChange: (e) => {
|
|
1436
|
+
const checked = e.target.checked;
|
|
1437
|
+
setCfgDraft(d => ({ ...(d ?? {}), groupAutoApprove: checked }));
|
|
1438
|
+
act(BRIDGE_ENDPOINTS.platformSetConfig, { groupAutoApprove: checked });
|
|
1439
|
+
},
|
|
1440
|
+
}),
|
|
1441
|
+
React.createElement('span', null, '群聊自动授权(新群首次 @机器人 自动加入白名单,默认关闭)'),
|
|
1442
|
+
),
|
|
1342
1443
|
// 飞书 / Telegram 扫码直达对话引导卡片
|
|
1343
1444
|
(platformId === 'feishu' || platformId === 'telegram') && platform.botQr && React.createElement('div', {
|
|
1344
1445
|
style: {
|
|
@@ -1774,41 +1875,71 @@ function BackupRestoreWidget({ rpcCall, onUpdate }) {
|
|
|
1774
1875
|
}
|
|
1775
1876
|
|
|
1776
1877
|
// 运维 Tab 内的手动重启 DSH 服务小卡片
|
|
1777
|
-
|
|
1878
|
+
// 重启 + 健康轮询公共逻辑:定时器保存在 ref 中,组件卸载即清理。
|
|
1879
|
+
// 修复点:原先两处复制粘贴的 setInterval 不随组件卸载清理,用户切走 Tab 后
|
|
1880
|
+
// 轮询仍会继续运行,并在服务恢复时无条件执行 window.location.reload()。
|
|
1881
|
+
function useDshRestart({ rpcCall, maxAttempts = 30, texts = {} } = {}) {
|
|
1882
|
+
const T = {
|
|
1883
|
+
restarting: '正在向 DSH 服务发送重启指令…',
|
|
1884
|
+
reconnecting: 'DSH 服务正在重启中,正在自动重新连接…',
|
|
1885
|
+
success: '🎉 重启成功!已重新建立连接,正在刷新页面…',
|
|
1886
|
+
timeout: '重连等待超时,请手动刷新页面。',
|
|
1887
|
+
...texts,
|
|
1888
|
+
};
|
|
1778
1889
|
const [restarting, setRestarting] = React.useState(false);
|
|
1779
1890
|
const [status, setStatus] = React.useState(null);
|
|
1891
|
+
const pollRef = React.useRef(null);
|
|
1892
|
+
const reloadRef = React.useRef(null);
|
|
1893
|
+
|
|
1894
|
+
React.useEffect(() => () => {
|
|
1895
|
+
if (pollRef.current) clearInterval(pollRef.current);
|
|
1896
|
+
if (reloadRef.current) clearTimeout(reloadRef.current);
|
|
1897
|
+
}, []);
|
|
1780
1898
|
|
|
1781
|
-
const
|
|
1899
|
+
const startRestart = React.useCallback(async () => {
|
|
1782
1900
|
setRestarting(true);
|
|
1783
|
-
setStatus({ phase: 'restarting', text:
|
|
1901
|
+
setStatus({ phase: 'restarting', text: T.restarting });
|
|
1784
1902
|
try {
|
|
1785
1903
|
await rpcCall(BRIDGE_ENDPOINTS.restartDsh, {});
|
|
1786
|
-
} catch {
|
|
1904
|
+
} catch {
|
|
1905
|
+
// 服务可能瞬间关闭导致连接断开,忽略
|
|
1906
|
+
}
|
|
1787
1907
|
|
|
1788
|
-
setStatus({ phase: 'reconnecting', text:
|
|
1908
|
+
setStatus({ phase: 'reconnecting', text: T.reconnecting });
|
|
1789
1909
|
await new Promise(r => setTimeout(r, 2000));
|
|
1790
1910
|
|
|
1791
1911
|
let attempts = 0;
|
|
1792
|
-
|
|
1793
|
-
const pollHealth = setInterval(async () => {
|
|
1912
|
+
pollRef.current = setInterval(async () => {
|
|
1794
1913
|
attempts++;
|
|
1795
1914
|
try {
|
|
1796
1915
|
const r = await rpcCall(BRIDGE_ENDPOINTS.checkVersion, {});
|
|
1797
1916
|
if (r?.ok) {
|
|
1798
|
-
clearInterval(
|
|
1799
|
-
|
|
1800
|
-
|
|
1917
|
+
clearInterval(pollRef.current);
|
|
1918
|
+
pollRef.current = null;
|
|
1919
|
+
setStatus({ phase: 'success', text: T.success });
|
|
1920
|
+
reloadRef.current = setTimeout(() => { window.location.reload(); }, 1000);
|
|
1801
1921
|
return;
|
|
1802
1922
|
}
|
|
1803
|
-
} catch {
|
|
1923
|
+
} catch {
|
|
1924
|
+
// 仍在启动中,继续等待
|
|
1925
|
+
}
|
|
1804
1926
|
|
|
1805
1927
|
if (attempts >= maxAttempts) {
|
|
1806
|
-
clearInterval(
|
|
1807
|
-
|
|
1928
|
+
clearInterval(pollRef.current);
|
|
1929
|
+
pollRef.current = null;
|
|
1930
|
+
setStatus({ phase: 'timeout', text: T.timeout });
|
|
1808
1931
|
setRestarting(false);
|
|
1809
1932
|
}
|
|
1810
1933
|
}, 1000);
|
|
1811
|
-
};
|
|
1934
|
+
}, [rpcCall, maxAttempts]);
|
|
1935
|
+
|
|
1936
|
+
const resetStatus = React.useCallback(() => setStatus(null), []);
|
|
1937
|
+
|
|
1938
|
+
return { restarting, status, startRestart, resetStatus };
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
function RestartDshCard({ rpcCall }) {
|
|
1942
|
+
const { restarting, status, startRestart: handleRestart } = useDshRestart({ rpcCall });
|
|
1812
1943
|
|
|
1813
1944
|
return React.createElement('div', { style: { ...s.card, marginBottom: 16 } },
|
|
1814
1945
|
React.createElement('div', { style: { marginBottom: 10 } },
|
|
@@ -1882,8 +2013,6 @@ function RemoteWorkspaceCard({ rpcCall }) {
|
|
|
1882
2013
|
onClick: () => {
|
|
1883
2014
|
if (typeof window.__dshOpenRemoteWorkspaceModal === 'function') {
|
|
1884
2015
|
window.__dshOpenRemoteWorkspaceModal();
|
|
1885
|
-
} else if (typeof showRemoteWorkspaceDialog === 'function') {
|
|
1886
|
-
showRemoteWorkspaceDialog(rpcCall, () => load());
|
|
1887
2016
|
}
|
|
1888
2017
|
},
|
|
1889
2018
|
}, '+ 远程添加工作区'),
|
|
@@ -1915,6 +2044,20 @@ function RemoteWorkspaceCard({ rpcCall }) {
|
|
|
1915
2044
|
);
|
|
1916
2045
|
}
|
|
1917
2046
|
|
|
2047
|
+
// 手动升级命令行:展示命令 + 一键复制
|
|
2048
|
+
function UpgradeCommandRow({ cmd }) {
|
|
2049
|
+
const [copied, copy] = useCopy();
|
|
2050
|
+
return React.createElement('div', { style: { display: 'flex', gap: 6, alignItems: 'center' } },
|
|
2051
|
+
React.createElement('code', {
|
|
2052
|
+
style: { ...s.code, flex: '1 1 auto', fontSize: 12, lineHeight: 1.5, wordBreak: 'break-all' },
|
|
2053
|
+
}, cmd),
|
|
2054
|
+
React.createElement('button', {
|
|
2055
|
+
style: { ...s.btnGhost, height: 26, padding: '0 10px', fontSize: 11, flex: '0 0 auto' },
|
|
2056
|
+
onClick: () => copy(cmd),
|
|
2057
|
+
}, copied ? '✓ 已复制' : '复制'),
|
|
2058
|
+
);
|
|
2059
|
+
}
|
|
2060
|
+
|
|
1918
2061
|
// 版本检查 + 一键升级 + GitHub/反馈入口
|
|
1919
2062
|
function VersionBanner({ rpcCall }) {
|
|
1920
2063
|
const [info, setInfo] = React.useState(null);
|
|
@@ -1922,15 +2065,22 @@ function VersionBanner({ rpcCall }) {
|
|
|
1922
2065
|
const [upgrading, setUpgrading] = React.useState(false);
|
|
1923
2066
|
const [upgradeResult, setUpgradeResult] = React.useState(null);
|
|
1924
2067
|
const [showManual, setShowManual] = React.useState(false);
|
|
1925
|
-
const [restarting, setRestarting] = React.useState(false);
|
|
1926
|
-
const [restartStatus, setRestartStatus] = React.useState(null);
|
|
1927
2068
|
const [dismissRestart, setDismissRestart] = React.useState(false);
|
|
2069
|
+
const { restarting, status: restartStatus, startRestart: handleRestart, resetStatus: resetRestartStatus } = useDshRestart({
|
|
2070
|
+
rpcCall,
|
|
2071
|
+
texts: {
|
|
2072
|
+
restarting: '正在调度 DSH 服务重启…',
|
|
2073
|
+
success: '🎉 重启成功!已自动加载最新版本。正在刷新页面…',
|
|
2074
|
+
},
|
|
2075
|
+
});
|
|
1928
2076
|
|
|
1929
2077
|
const check = React.useCallback(async () => {
|
|
1930
2078
|
setLoading(true);
|
|
1931
2079
|
try {
|
|
1932
2080
|
const r = await rpcCall(BRIDGE_ENDPOINTS.checkVersion, {});
|
|
1933
2081
|
if (r?.ok) setInfo(r.value);
|
|
2082
|
+
} catch {
|
|
2083
|
+
// 版本检查失败:保留现有 info,不打断面板
|
|
1934
2084
|
} finally {
|
|
1935
2085
|
setLoading(false);
|
|
1936
2086
|
}
|
|
@@ -1946,7 +2096,7 @@ function VersionBanner({ rpcCall }) {
|
|
|
1946
2096
|
setUpgrading(true);
|
|
1947
2097
|
setUpgradeResult(null);
|
|
1948
2098
|
setDismissRestart(false);
|
|
1949
|
-
|
|
2099
|
+
resetRestartStatus();
|
|
1950
2100
|
try {
|
|
1951
2101
|
const r = await rpcCall(BRIDGE_ENDPOINTS.upgradePlugin, { version: info.latest });
|
|
1952
2102
|
if (r?.ok && r.value?.ok) {
|
|
@@ -1963,46 +2113,6 @@ function VersionBanner({ rpcCall }) {
|
|
|
1963
2113
|
}
|
|
1964
2114
|
}, [info?.latest, upgrading, rpcCall]);
|
|
1965
2115
|
|
|
1966
|
-
const handleRestart = React.useCallback(async () => {
|
|
1967
|
-
setRestarting(true);
|
|
1968
|
-
setRestartStatus({ phase: 'restarting', text: '正在调度 DSH 服务重启…' });
|
|
1969
|
-
try {
|
|
1970
|
-
await rpcCall(BRIDGE_ENDPOINTS.restartDsh, {});
|
|
1971
|
-
} catch {
|
|
1972
|
-
// 忽略 RPC 错误(因为服务可能瞬间关闭导致网络连接断开)
|
|
1973
|
-
}
|
|
1974
|
-
|
|
1975
|
-
setRestartStatus({ phase: 'reconnecting', text: 'DSH 服务正在重启中,正在自动重新连接…' });
|
|
1976
|
-
|
|
1977
|
-
// 等待 2 秒后开始健康检查轮询
|
|
1978
|
-
await new Promise(r => setTimeout(r, 2000));
|
|
1979
|
-
|
|
1980
|
-
let attempts = 0;
|
|
1981
|
-
const maxAttempts = 30; // 最多探测 30 次(约 30 秒)
|
|
1982
|
-
const pollHealth = setInterval(async () => {
|
|
1983
|
-
attempts++;
|
|
1984
|
-
try {
|
|
1985
|
-
const r = await rpcCall(BRIDGE_ENDPOINTS.checkVersion, {});
|
|
1986
|
-
if (r?.ok) {
|
|
1987
|
-
clearInterval(pollHealth);
|
|
1988
|
-
setRestartStatus({ phase: 'success', text: '🎉 重启成功!已自动加载最新版本。正在刷新页面…' });
|
|
1989
|
-
setTimeout(() => {
|
|
1990
|
-
window.location.reload();
|
|
1991
|
-
}, 1000);
|
|
1992
|
-
return;
|
|
1993
|
-
}
|
|
1994
|
-
} catch {
|
|
1995
|
-
// 仍在启动中,继续等待
|
|
1996
|
-
}
|
|
1997
|
-
|
|
1998
|
-
if (attempts >= maxAttempts) {
|
|
1999
|
-
clearInterval(pollHealth);
|
|
2000
|
-
setRestartStatus({ phase: 'timeout', text: '重连等待超时,请手动刷新页面。' });
|
|
2001
|
-
setRestarting(false);
|
|
2002
|
-
}
|
|
2003
|
-
}, 1000);
|
|
2004
|
-
}, [rpcCall]);
|
|
2005
|
-
|
|
2006
2116
|
const links = React.createElement('div', { style: { display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap' } },
|
|
2007
2117
|
React.createElement('a', {
|
|
2008
2118
|
href: GITHUB_URL, target: '_blank', rel: 'noreferrer', style: s.btnLink,
|
|
@@ -2336,7 +2446,6 @@ function BridgePanel({ rpcCall }) {
|
|
|
2336
2446
|
// 本机物理访问自动静默获取 adminToken,免输密码直通管理(支持 3080 原生端口与 3082 代理端口)
|
|
2337
2447
|
const fetchLoopbackToken = React.useCallback(async () => {
|
|
2338
2448
|
if (!isLocalhost) return null;
|
|
2339
|
-
const currentPort = typeof window !== 'undefined' ? (window.location.port || (window.location.protocol === 'https:' ? '443' : '80')) : '3082';
|
|
2340
2449
|
const proxyPort = status?.proxy?.port || 3082;
|
|
2341
2450
|
const candidateUrls = [
|
|
2342
2451
|
'/__dsh_bridge__/loopback-token',
|
|
@@ -2369,6 +2478,9 @@ function BridgePanel({ rpcCall }) {
|
|
|
2369
2478
|
}
|
|
2370
2479
|
}, [isLocalhost, adminUnlocked, fetchLoopbackToken]);
|
|
2371
2480
|
|
|
2481
|
+
// 被管理员鉴权拦截的操作在此暂存:解锁成功后自动续办,用户无需再点一次
|
|
2482
|
+
const pendingRetryRef = React.useRef(null);
|
|
2483
|
+
|
|
2372
2484
|
const authRpcCall = React.useCallback(async (endpoint, payload = {}, signal) => {
|
|
2373
2485
|
let token = adminToken || getGlobalAdminToken();
|
|
2374
2486
|
if (isLocalhost && !token) {
|
|
@@ -2383,6 +2495,7 @@ function BridgePanel({ rpcCall }) {
|
|
|
2383
2495
|
if (res?.ok === false) {
|
|
2384
2496
|
const msg = res?.error?.message || '';
|
|
2385
2497
|
if (msg.includes('管理员权限') || msg.includes('管理密码解锁')) {
|
|
2498
|
+
pendingRetryRef.current = { endpoint, payload: enriched };
|
|
2386
2499
|
setUnlockErr(msg);
|
|
2387
2500
|
setShowUnlockModal(true);
|
|
2388
2501
|
}
|
|
@@ -2474,6 +2587,28 @@ function BridgePanel({ rpcCall }) {
|
|
|
2474
2587
|
return () => clearInterval(t);
|
|
2475
2588
|
}, [load]);
|
|
2476
2589
|
|
|
2590
|
+
// 解锁成功后自动续办被拦截的操作:用户不再需要重新点击一次
|
|
2591
|
+
React.useEffect(() => {
|
|
2592
|
+
if (!adminUnlocked) return;
|
|
2593
|
+
const pending = pendingRetryRef.current;
|
|
2594
|
+
if (!pending) return;
|
|
2595
|
+
pendingRetryRef.current = null;
|
|
2596
|
+
const token = adminToken || getGlobalAdminToken();
|
|
2597
|
+
(async () => {
|
|
2598
|
+
try {
|
|
2599
|
+
const r = await rpcCall(pending.endpoint, { ...pending.payload, adminToken: token });
|
|
2600
|
+
if (r?.ok) {
|
|
2601
|
+
if (r.value) setStatus(r.value);
|
|
2602
|
+
setErr(null);
|
|
2603
|
+
} else {
|
|
2604
|
+
setErr(r?.error?.message || '刚才的操作重试失败,请手动重试');
|
|
2605
|
+
}
|
|
2606
|
+
} catch (e) {
|
|
2607
|
+
setErr(e.message || '刚才的操作重试失败,请手动重试');
|
|
2608
|
+
}
|
|
2609
|
+
})();
|
|
2610
|
+
}, [adminUnlocked, adminToken, rpcCall]);
|
|
2611
|
+
|
|
2477
2612
|
const act = React.useCallback(async (endpoint, payload) => {
|
|
2478
2613
|
try {
|
|
2479
2614
|
const r = await authRpcCall(endpoint, payload ?? {});
|
|
@@ -2497,6 +2632,8 @@ function BridgePanel({ rpcCall }) {
|
|
|
2497
2632
|
act(BRIDGE_ENDPOINTS.saveCloudflaredConfig, { token, hostname })
|
|
2498
2633
|
, [act]);
|
|
2499
2634
|
|
|
2635
|
+
const onSelectLanIp = React.useCallback((ip) => act(BRIDGE_ENDPOINTS.setLanIp, { ip }), [act]);
|
|
2636
|
+
|
|
2500
2637
|
const onStartCustom = React.useCallback(() => act(BRIDGE_ENDPOINTS.startCustomTunnel), [act]);
|
|
2501
2638
|
const onStopCustom = React.useCallback(() => act(BRIDGE_ENDPOINTS.stopCustomTunnel), [act]);
|
|
2502
2639
|
const onToggleCustomAutoStart = React.useCallback((autoStart) =>
|
|
@@ -2536,7 +2673,12 @@ function BridgePanel({ rpcCall }) {
|
|
|
2536
2673
|
data: { running: status?.proxy?.running, url: status?.lan?.url, qr: status?.lan?.qr },
|
|
2537
2674
|
auth: status?.auth,
|
|
2538
2675
|
onNavigateSecurity: navSecurity,
|
|
2539
|
-
}
|
|
2676
|
+
},
|
|
2677
|
+
React.createElement(LanNetworkSelector, {
|
|
2678
|
+
lan: status?.lan,
|
|
2679
|
+
onSelectIp: onSelectLanIp,
|
|
2680
|
+
})
|
|
2681
|
+
);
|
|
2540
2682
|
} else if (activeTab === 'tunnel') {
|
|
2541
2683
|
tabContent = React.createElement(React.Fragment, null,
|
|
2542
2684
|
React.createElement(TunnelCard, {
|
|
@@ -2666,7 +2808,6 @@ function BridgePanel({ rpcCall }) {
|
|
|
2666
2808
|
platformName: IM_PLATFORMS.find(p => p.id === selectedPlatform)?.label ?? selectedPlatform,
|
|
2667
2809
|
platformDesc: IM_PLATFORMS.find(p => p.id === selectedPlatform)?.desc ?? '',
|
|
2668
2810
|
rpcCall: authRpcCall,
|
|
2669
|
-
onStatusChange: () => {}, // 状态变化已由 listPlatforms 轮询处理,不需要回调
|
|
2670
2811
|
}),
|
|
2671
2812
|
);
|
|
2672
2813
|
}
|
|
@@ -2703,7 +2844,7 @@ function BridgePanel({ rpcCall }) {
|
|
|
2703
2844
|
},
|
|
2704
2845
|
React.createElement('div', { style: { fontWeight: 600, color: 'var(--dsw-alias-label-primary,currentColor)', marginBottom: 4 } }, '🛟 救急解除锁定指引:'),
|
|
2705
2846
|
React.createElement('div', null, '1. ', React.createElement('strong', null, '电脑本机直连修改'), ':直接在运行本程序的电脑本机打开本控制台(127.0.0.1 享有物理免锁特权),可随时修改策略或清除密码。'),
|
|
2706
|
-
React.createElement('div', { style: { marginTop: 4 } }, '2. ', React.createElement('strong', null, '
|
|
2847
|
+
React.createElement('div', { style: { marginTop: 4 } }, '2. ', React.createElement('strong', null, '服务器 / 无头环境'), ':救急重置步骤参见 GitHub README 的「三重容灾保命体系」章节。'),
|
|
2707
2848
|
),
|
|
2708
2849
|
)
|
|
2709
2850
|
) : (
|
|
@@ -2754,7 +2895,7 @@ function BridgePanel({ rpcCall }) {
|
|
|
2754
2895
|
},
|
|
2755
2896
|
React.createElement('div', { style: { fontWeight: 600, color: 'var(--dsw-alias-label-primary,currentColor)', marginBottom: 4 } }, '🛟 找回与重置密码指引:'),
|
|
2756
2897
|
React.createElement('div', null, '1. ', React.createElement('strong', null, '电脑本机直连修改'), ':直接在运行本程序的电脑本机打开本控制台(127.0.0.1 享有物理免锁特权),可随时修改管理密码。'),
|
|
2757
|
-
React.createElement('div', { style: { marginTop: 4 } }, '2. ', React.createElement('strong', null, '
|
|
2898
|
+
React.createElement('div', { style: { marginTop: 4 } }, '2. ', React.createElement('strong', null, '服务器 / 无头环境'), ':救急重置步骤参见 GitHub README 的「三重容灾保命体系」章节。'),
|
|
2758
2899
|
),
|
|
2759
2900
|
)
|
|
2760
2901
|
)
|
|
@@ -2901,805 +3042,7 @@ function injectMobileStyles() {
|
|
|
2901
3042
|
|
|
2902
3043
|
const style = document.createElement('style');
|
|
2903
3044
|
style.id = 'dsh-bridge-mobile-styles';
|
|
2904
|
-
style.textContent =
|
|
2905
|
-
/* DSH Bridge 隐藏 Tab 栏原生滚动条并保持平滑滑动 */
|
|
2906
|
-
.dsh-tabbar-container {
|
|
2907
|
-
scrollbar-width: none !important;
|
|
2908
|
-
-ms-overflow-style: none !important;
|
|
2909
|
-
}
|
|
2910
|
-
.dsh-tabbar-container::-webkit-scrollbar {
|
|
2911
|
-
display: none !important;
|
|
2912
|
-
width: 0 !important;
|
|
2913
|
-
height: 0 !important;
|
|
2914
|
-
}
|
|
2915
|
-
|
|
2916
|
-
/* DSH Bridge 移动端自适应与触控交互增强样式 */
|
|
2917
|
-
:root {
|
|
2918
|
-
--dsh-mobile-header-h: 52px;
|
|
2919
|
-
--dsh-mobile-safe-top: env(safe-area-inset-top, 0px);
|
|
2920
|
-
--dsh-mobile-safe-bottom: env(safe-area-inset-bottom, 0px);
|
|
2921
|
-
}
|
|
2922
|
-
|
|
2923
|
-
@media (max-width: 768px) {
|
|
2924
|
-
/* 1. 主框架为 Header 腾出顶部空间 */
|
|
2925
|
-
div[class*="_frame"] {
|
|
2926
|
-
display: flex !important;
|
|
2927
|
-
flex-direction: column !important;
|
|
2928
|
-
width: 100vw !important;
|
|
2929
|
-
height: 100dvh !important;
|
|
2930
|
-
margin: 0 !important;
|
|
2931
|
-
padding-top: var(--dsh-mobile-header-h) !important;
|
|
2932
|
-
position: relative !important;
|
|
2933
|
-
grid-template-columns: 1fr !important;
|
|
2934
|
-
overflow: hidden !important;
|
|
2935
|
-
box-sizing: border-box !important;
|
|
2936
|
-
}
|
|
2937
|
-
|
|
2938
|
-
/* 2. 顶部原生导航条:100% 还原 DeepSeek App (左侧双横线,右侧(+),中间留白,无多余设置按钮) */
|
|
2939
|
-
.dsh-mobile-app-header {
|
|
2940
|
-
position: fixed !important;
|
|
2941
|
-
top: 0 !important;
|
|
2942
|
-
left: 0 !important;
|
|
2943
|
-
right: 0 !important;
|
|
2944
|
-
height: var(--dsh-mobile-header-h) !important;
|
|
2945
|
-
padding-top: var(--dsh-mobile-safe-top) !important;
|
|
2946
|
-
background: transparent !important;
|
|
2947
|
-
display: flex !important;
|
|
2948
|
-
align-items: center !important;
|
|
2949
|
-
justify-content: space-between !important;
|
|
2950
|
-
padding-left: 16px !important;
|
|
2951
|
-
padding-right: 16px !important;
|
|
2952
|
-
z-index: 9998 !important;
|
|
2953
|
-
box-sizing: border-box !important;
|
|
2954
|
-
user-select: none !important;
|
|
2955
|
-
pointer-events: none !important;
|
|
2956
|
-
}
|
|
2957
|
-
|
|
2958
|
-
/* 左侧双横线按钮 (DeepSeek App 原生图标) */
|
|
2959
|
-
.dsh-header-menu-btn {
|
|
2960
|
-
width: 40px;
|
|
2961
|
-
height: 40px;
|
|
2962
|
-
border-radius: 50%;
|
|
2963
|
-
border: none;
|
|
2964
|
-
background: transparent;
|
|
2965
|
-
color: var(--dsw-alias-label-primary, #111827);
|
|
2966
|
-
display: inline-flex;
|
|
2967
|
-
align-items: center;
|
|
2968
|
-
justify-content: flex-start;
|
|
2969
|
-
cursor: pointer;
|
|
2970
|
-
padding: 0;
|
|
2971
|
-
transition: opacity 0.15s;
|
|
2972
|
-
pointer-events: auto !important;
|
|
2973
|
-
}
|
|
2974
|
-
.dsh-header-menu-btn:active {
|
|
2975
|
-
opacity: 0.6;
|
|
2976
|
-
}
|
|
2977
|
-
|
|
2978
|
-
/* 右侧 (+) 新建会话按钮 (DeepSeek App 原生图标) */
|
|
2979
|
-
.dsh-header-new-btn {
|
|
2980
|
-
width: 40px;
|
|
2981
|
-
height: 40px;
|
|
2982
|
-
border-radius: 50%;
|
|
2983
|
-
border: none;
|
|
2984
|
-
background: transparent;
|
|
2985
|
-
color: var(--dsw-alias-label-primary, #111827);
|
|
2986
|
-
display: inline-flex;
|
|
2987
|
-
align-items: center;
|
|
2988
|
-
justify-content: flex-end;
|
|
2989
|
-
cursor: pointer;
|
|
2990
|
-
padding: 0;
|
|
2991
|
-
transition: opacity 0.15s;
|
|
2992
|
-
pointer-events: auto !important;
|
|
2993
|
-
}
|
|
2994
|
-
.dsh-header-new-btn:active {
|
|
2995
|
-
opacity: 0.6;
|
|
2996
|
-
}
|
|
2997
|
-
|
|
2998
|
-
/* 中间动态会话标题 (单行居中打点截断,100% 还原原生 App 导航体验) */
|
|
2999
|
-
.dsh-mobile-header-title {
|
|
3000
|
-
flex: 1 1 auto !important;
|
|
3001
|
-
min-width: 0 !important;
|
|
3002
|
-
text-align: center !important;
|
|
3003
|
-
font-size: 15px !important;
|
|
3004
|
-
font-weight: 600 !important;
|
|
3005
|
-
color: var(--dsw-alias-label-primary, #111827) !important;
|
|
3006
|
-
overflow: hidden !important;
|
|
3007
|
-
text-overflow: ellipsis !important;
|
|
3008
|
-
white-space: nowrap !important;
|
|
3009
|
-
padding: 0 10px !important;
|
|
3010
|
-
user-select: none !important;
|
|
3011
|
-
pointer-events: none !important;
|
|
3012
|
-
letter-spacing: -0.2px !important;
|
|
3013
|
-
}
|
|
3014
|
-
|
|
3015
|
-
/* 3. 中间主内容区与输入框 */
|
|
3016
|
-
div[class*="_centerCol"] {
|
|
3017
|
-
flex: 1 1 100% !important;
|
|
3018
|
-
width: 100% !important;
|
|
3019
|
-
min-width: 0 !important;
|
|
3020
|
-
max-width: 100% !important;
|
|
3021
|
-
display: flex !important;
|
|
3022
|
-
height: 100% !important;
|
|
3023
|
-
}
|
|
3024
|
-
|
|
3025
|
-
div[class*="_detailsCol"],
|
|
3026
|
-
div[class*="toggleCluster"],
|
|
3027
|
-
div[class*="W-zNGW_toggleCluster"] {
|
|
3028
|
-
display: none !important;
|
|
3029
|
-
}
|
|
3030
|
-
|
|
3031
|
-
/* 3.0 工作区 Workbench / 任务管理 / 多 Tab 栏移动端自适应适配 */
|
|
3032
|
-
body:not(.dsh-workbench-open) div[class*="nArs4W_panel"],
|
|
3033
|
-
body:not(.dsh-workbench-open) div[class*="workbench_panel"],
|
|
3034
|
-
body:not(.dsh-workbench-open) div[class*="workbenchPanel"],
|
|
3035
|
-
div[class*="nArs4W_panel"][class*="panelHidden"],
|
|
3036
|
-
div[class*="workbench_panel"][class*="panelHidden"],
|
|
3037
|
-
div[class*="workbenchPanel"][class*="panelHidden"],
|
|
3038
|
-
div[class*="panelHidden"] {
|
|
3039
|
-
display: none !important;
|
|
3040
|
-
visibility: hidden !important;
|
|
3041
|
-
pointer-events: none !important;
|
|
3042
|
-
width: 0 !important;
|
|
3043
|
-
height: 0 !important;
|
|
3044
|
-
max-height: 0 !important;
|
|
3045
|
-
z-index: -1 !important;
|
|
3046
|
-
opacity: 0 !important;
|
|
3047
|
-
transform: translateX(105%) !important;
|
|
3048
|
-
}
|
|
3049
|
-
|
|
3050
|
-
body.dsh-workbench-open div[class*="nArs4W_panel"]:not([class*="panelHidden"]),
|
|
3051
|
-
body.dsh-workbench-open div[class*="workbench_panel"]:not([class*="panelHidden"]),
|
|
3052
|
-
body.dsh-workbench-open div[class*="workbenchPanel"]:not([class*="panelHidden"]) {
|
|
3053
|
-
display: flex !important;
|
|
3054
|
-
visibility: visible !important;
|
|
3055
|
-
pointer-events: auto !important;
|
|
3056
|
-
top: var(--dsh-mobile-header-h, 52px) !important;
|
|
3057
|
-
height: calc(100dvh - var(--dsh-mobile-header-h, 52px)) !important;
|
|
3058
|
-
max-height: calc(100dvh - var(--dsh-mobile-header-h, 52px)) !important;
|
|
3059
|
-
z-index: 50 !important;
|
|
3060
|
-
box-sizing: border-box !important;
|
|
3061
|
-
background: var(--dsw-alias-bg-layer-1, #ffffff) !important;
|
|
3062
|
-
transform: none !important;
|
|
3063
|
-
opacity: 1 !important;
|
|
3064
|
-
}
|
|
3065
|
-
|
|
3066
|
-
/* Tab 栏:横向滑动手势 + 干净的底部边框,杜绝与顶部移动端 Header 重叠 */
|
|
3067
|
-
div[class*="nArs4W_tabBar"],
|
|
3068
|
-
div[class*="workbench_tabBar"],
|
|
3069
|
-
div[class*="tabBar"] {
|
|
3070
|
-
min-height: 40px !important;
|
|
3071
|
-
height: 40px !important;
|
|
3072
|
-
background: var(--dsw-alias-bg-layer-1, #ffffff) !important;
|
|
3073
|
-
border-bottom: 1px solid rgba(0, 0, 0, 0.08) !important;
|
|
3074
|
-
display: flex !important;
|
|
3075
|
-
align-items: center !important;
|
|
3076
|
-
justify-content: space-between !important;
|
|
3077
|
-
padding: 0 8px !important;
|
|
3078
|
-
gap: 6px !important;
|
|
3079
|
-
overflow: visible !important;
|
|
3080
|
-
box-sizing: border-box !important;
|
|
3081
|
-
}
|
|
3082
|
-
|
|
3083
|
-
div[class*="nArs4W_tabList"],
|
|
3084
|
-
div[class*="tabList"] {
|
|
3085
|
-
display: flex !important;
|
|
3086
|
-
align-items: center !important;
|
|
3087
|
-
gap: 6px !important;
|
|
3088
|
-
flex: 1 1 auto !important;
|
|
3089
|
-
min-width: 0 !important;
|
|
3090
|
-
overflow-x: auto !important;
|
|
3091
|
-
scrollbar-width: none !important;
|
|
3092
|
-
-webkit-overflow-scrolling: touch !important;
|
|
3093
|
-
}
|
|
3094
|
-
div[class*="nArs4W_tabList"]::-webkit-scrollbar,
|
|
3095
|
-
div[class*="tabList"]::-webkit-scrollbar {
|
|
3096
|
-
display: none !important;
|
|
3097
|
-
}
|
|
3098
|
-
|
|
3099
|
-
/* 单个 Tab 胶囊化,文字超长自动打点,防止 Tab 互相挤压 */
|
|
3100
|
-
div[class*="nArs4W_tab"],
|
|
3101
|
-
div[class*="workbench_tab"] {
|
|
3102
|
-
flex: 0 0 auto !important;
|
|
3103
|
-
max-width: 170px !important;
|
|
3104
|
-
min-width: 70px !important;
|
|
3105
|
-
height: 30px !important;
|
|
3106
|
-
padding: 0 8px 0 10px !important;
|
|
3107
|
-
border-radius: 6px !important;
|
|
3108
|
-
font-size: 12.5px !important;
|
|
3109
|
-
display: inline-flex !important;
|
|
3110
|
-
align-items: center !important;
|
|
3111
|
-
justify-content: space-between !important;
|
|
3112
|
-
gap: 6px !important;
|
|
3113
|
-
background: var(--dsw-alias-bg-layer-2, #f3f4f6) !important;
|
|
3114
|
-
color: var(--dsw-alias-label-secondary, #6b7280) !important;
|
|
3115
|
-
cursor: pointer !important;
|
|
3116
|
-
user-select: none !important;
|
|
3117
|
-
box-sizing: border-box !important;
|
|
3118
|
-
}
|
|
3119
|
-
|
|
3120
|
-
div[class*="nArs4W_tabActive"],
|
|
3121
|
-
div[class*="workbench_tabActive"] {
|
|
3122
|
-
background: var(--dsw-alias-bg-layer-1, #ffffff) !important;
|
|
3123
|
-
color: var(--dsw-alias-label-primary, #111827) !important;
|
|
3124
|
-
font-weight: 600 !important;
|
|
3125
|
-
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1) !important;
|
|
3126
|
-
}
|
|
3127
|
-
|
|
3128
|
-
span[class*="nArs4W_tabTitle"],
|
|
3129
|
-
span[class*="tabTitle"] {
|
|
3130
|
-
overflow: hidden !important;
|
|
3131
|
-
text-overflow: ellipsis !important;
|
|
3132
|
-
white-space: nowrap !important;
|
|
3133
|
-
flex: 1 1 auto !important;
|
|
3134
|
-
}
|
|
3135
|
-
|
|
3136
|
-
button[class*="nArs4W_tabClose"],
|
|
3137
|
-
button[class*="tabClose"] {
|
|
3138
|
-
width: 18px !important;
|
|
3139
|
-
height: 18px !important;
|
|
3140
|
-
border-radius: 50% !important;
|
|
3141
|
-
display: inline-flex !important;
|
|
3142
|
-
align-items: center !important;
|
|
3143
|
-
justify-content: center !important;
|
|
3144
|
-
flex-shrink: 0 !important;
|
|
3145
|
-
opacity: 0.6 !important;
|
|
3146
|
-
padding: 0 !important;
|
|
3147
|
-
}
|
|
3148
|
-
|
|
3149
|
-
button[class*="nArs4W_tabBarPlus"],
|
|
3150
|
-
button[class*="tabBarPlus"] {
|
|
3151
|
-
width: 28px !important;
|
|
3152
|
-
height: 28px !important;
|
|
3153
|
-
border-radius: 50% !important;
|
|
3154
|
-
display: inline-flex !important;
|
|
3155
|
-
align-items: center !important;
|
|
3156
|
-
justify-content: center !important;
|
|
3157
|
-
flex-shrink: 0 !important;
|
|
3158
|
-
}
|
|
3159
|
-
|
|
3160
|
-
/* 移动端面板右上角“返回对话 / ✕ 收起”按钮:常驻右侧,醒目且易触达 */
|
|
3161
|
-
.dsh-mobile-panel-close-btn {
|
|
3162
|
-
margin-left: 8px !important;
|
|
3163
|
-
flex: 0 0 auto !important;
|
|
3164
|
-
height: 28px !important;
|
|
3165
|
-
padding: 0 10px !important;
|
|
3166
|
-
border-radius: 14px !important;
|
|
3167
|
-
font-size: 12px !important;
|
|
3168
|
-
font-weight: 600 !important;
|
|
3169
|
-
background: #2563eb !important;
|
|
3170
|
-
color: #ffffff !important;
|
|
3171
|
-
border: none !important;
|
|
3172
|
-
display: inline-flex !important;
|
|
3173
|
-
align-items: center !important;
|
|
3174
|
-
justify-content: center !important;
|
|
3175
|
-
gap: 4px !important;
|
|
3176
|
-
cursor: pointer !important;
|
|
3177
|
-
user-select: none !important;
|
|
3178
|
-
box-shadow: 0 2px 6px rgba(37, 99, 235, 0.28) !important;
|
|
3179
|
-
white-space: nowrap !important;
|
|
3180
|
-
transition: transform 0.1s, opacity 0.15s !important;
|
|
3181
|
-
z-index: 10 !important;
|
|
3182
|
-
}
|
|
3183
|
-
.dsh-mobile-panel-close-btn:active {
|
|
3184
|
-
transform: scale(0.95) !important;
|
|
3185
|
-
opacity: 0.85 !important;
|
|
3186
|
-
}
|
|
3187
|
-
|
|
3188
|
-
/* 3.1 会话对话头部顶栏:移动端防挤压与空间释放优化(严格排除 .dsh-mobile-app-header) */
|
|
3189
|
-
div[class*="_centerCol"] header,
|
|
3190
|
-
header[class*="wSkVaW_header"] {
|
|
3191
|
-
padding: 4px 16px 2px 16px !important;
|
|
3192
|
-
position: relative !important;
|
|
3193
|
-
overflow: visible !important;
|
|
3194
|
-
}
|
|
3195
|
-
|
|
3196
|
-
div[class*="wSkVaW_titleRow"],
|
|
3197
|
-
div[class*="titleRow"] {
|
|
3198
|
-
display: flex !important;
|
|
3199
|
-
flex-direction: row !important;
|
|
3200
|
-
align-items: center !important;
|
|
3201
|
-
justify-content: space-between !important;
|
|
3202
|
-
gap: 8px !important;
|
|
3203
|
-
min-height: 32px !important;
|
|
3204
|
-
width: 100% !important;
|
|
3205
|
-
box-sizing: border-box !important;
|
|
3206
|
-
}
|
|
3207
|
-
|
|
3208
|
-
/* 移动端将原有嵌入在内容区的长面包屑标题隐藏(已统一提升至顶部导航栏正中),彻底释放第二行空间 */
|
|
3209
|
-
nav[class*="wSkVaW_crumbs"],
|
|
3210
|
-
nav[class*="crumbs"],
|
|
3211
|
-
div[class*="wSkVaW_crumbs"],
|
|
3212
|
-
div[class*="crumbs"],
|
|
3213
|
-
[class*="wSkVaW_crumbs"] {
|
|
3214
|
-
display: none !important;
|
|
3215
|
-
}
|
|
3216
|
-
|
|
3217
|
-
/* 子代理/智能体模式胶囊 (Actions):紧凑圆角胶囊 */
|
|
3218
|
-
div[class*="wSkVaW_headerActions"],
|
|
3219
|
-
div[class*="headerActions"] {
|
|
3220
|
-
flex: 0 0 auto !important;
|
|
3221
|
-
display: inline-flex !important;
|
|
3222
|
-
align-items: center !important;
|
|
3223
|
-
gap: 4px !important;
|
|
3224
|
-
margin-left: 0 !important;
|
|
3225
|
-
}
|
|
3226
|
-
|
|
3227
|
-
button[class*="h8S2Va_trigger"],
|
|
3228
|
-
button[class*="subagent"] {
|
|
3229
|
-
min-height: 26px !important;
|
|
3230
|
-
height: 26px !important;
|
|
3231
|
-
padding: 2px 8px !important;
|
|
3232
|
-
font-size: 11.5px !important;
|
|
3233
|
-
line-height: 16px !important;
|
|
3234
|
-
border-radius: 13px !important;
|
|
3235
|
-
background: var(--dsw-alias-bg-layer-2, rgba(0, 0, 0, 0.04)) !important;
|
|
3236
|
-
white-space: nowrap !important;
|
|
3237
|
-
flex-shrink: 0 !important;
|
|
3238
|
-
}
|
|
3239
|
-
|
|
3240
|
-
/* Session Log 导出下载按钮 (Utilities):在移动端极简为 28px 圆形纯图标按钮,隐藏长文本,极大释放顶部空间 */
|
|
3241
|
-
div[class*="wSkVaW_headerUtilities"],
|
|
3242
|
-
div[class*="headerUtilities"] {
|
|
3243
|
-
flex: 0 0 auto !important;
|
|
3244
|
-
margin-left: 4px !important;
|
|
3245
|
-
display: inline-flex !important;
|
|
3246
|
-
align-items: center !important;
|
|
3247
|
-
}
|
|
3248
|
-
|
|
3249
|
-
button[class*="nL4_yW_sessionLogButton"],
|
|
3250
|
-
button[class*="sessionLogButton"] {
|
|
3251
|
-
min-width: 28px !important;
|
|
3252
|
-
width: 28px !important;
|
|
3253
|
-
height: 28px !important;
|
|
3254
|
-
padding: 0 !important;
|
|
3255
|
-
border-radius: 50% !important;
|
|
3256
|
-
display: inline-flex !important;
|
|
3257
|
-
align-items: center !important;
|
|
3258
|
-
justify-content: center !important;
|
|
3259
|
-
flex-shrink: 0 !important;
|
|
3260
|
-
border: 1px solid var(--dsw-alias-border-l2, rgba(0, 0, 0, 0.1)) !important;
|
|
3261
|
-
background: var(--dsw-alias-bg-layer-2, rgba(0, 0, 0, 0.03)) !important;
|
|
3262
|
-
color: var(--dsw-alias-label-secondary, #6b7280) !important;
|
|
3263
|
-
margin-left: 0 !important;
|
|
3264
|
-
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03) !important;
|
|
3265
|
-
}
|
|
3266
|
-
|
|
3267
|
-
button[class*="nL4_yW_sessionLogButton"]:hover:not(:disabled),
|
|
3268
|
-
button[class*="sessionLogButton"]:hover:not(:disabled) {
|
|
3269
|
-
background: var(--dsw-alias-interactive-bg-hover, rgba(0, 0, 0, 0.06)) !important;
|
|
3270
|
-
color: var(--dsw-alias-label-primary, #111827) !important;
|
|
3271
|
-
}
|
|
3272
|
-
|
|
3273
|
-
button[class*="nL4_yW_sessionLogButton"] span,
|
|
3274
|
-
button[class*="sessionLogButton"] span {
|
|
3275
|
-
display: none !important;
|
|
3276
|
-
}
|
|
3277
|
-
|
|
3278
|
-
button[class*="nL4_yW_sessionLogButton"] svg,
|
|
3279
|
-
button[class*="sessionLogButton"] svg {
|
|
3280
|
-
width: 13px !important;
|
|
3281
|
-
height: 13px !important;
|
|
3282
|
-
margin: 0 !important;
|
|
3283
|
-
}
|
|
3284
|
-
|
|
3285
|
-
/* 子代理展开菜单在移动端右对齐与宽度自适应 */
|
|
3286
|
-
div[class*="h8S2Va_menu"] {
|
|
3287
|
-
max-width: calc(100vw - 32px) !important;
|
|
3288
|
-
left: auto !important;
|
|
3289
|
-
right: 0 !important;
|
|
3290
|
-
}
|
|
3291
|
-
|
|
3292
|
-
/* 输入框底座:DeepSeek App 居中及底部固定 */
|
|
3293
|
-
div[class*="wSkVaW_scrollBody"] {
|
|
3294
|
-
padding-bottom: max(16px, env(safe-area-inset-bottom)) !important;
|
|
3295
|
-
}
|
|
3296
|
-
|
|
3297
|
-
/* 输入卡片:DeepSeek App 圆角大胶囊造型 */
|
|
3298
|
-
div[class*="uV2eYG_card"] {
|
|
3299
|
-
border-radius: 26px !important;
|
|
3300
|
-
padding: 14px 16px 12px !important;
|
|
3301
|
-
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05) !important;
|
|
3302
|
-
border: 1px solid rgba(0, 0, 0, 0.07) !important;
|
|
3303
|
-
background: var(--dsw-alias-bg-layer-2, #f4f4f7) !important;
|
|
3304
|
-
}
|
|
3305
|
-
|
|
3306
|
-
/* 输入框底部工具栏:弹性自适应,彻底杜绝权限选择器(Full access)与模型选择器重叠碰撞 */
|
|
3307
|
-
div[class*="uV2eYG_row"] {
|
|
3308
|
-
display: flex !important;
|
|
3309
|
-
align-items: center !important;
|
|
3310
|
-
justify-content: space-between !important;
|
|
3311
|
-
gap: 6px !important;
|
|
3312
|
-
width: 100% !important;
|
|
3313
|
-
padding: 2px 2px 4px !important;
|
|
3314
|
-
box-sizing: border-box !important;
|
|
3315
|
-
}
|
|
3316
|
-
|
|
3317
|
-
div[class*="uV2eYG_tools"] {
|
|
3318
|
-
display: flex !important;
|
|
3319
|
-
align-items: center !important;
|
|
3320
|
-
gap: 6px !important;
|
|
3321
|
-
flex: 0 0 auto !important;
|
|
3322
|
-
min-width: 0 !important;
|
|
3323
|
-
}
|
|
3324
|
-
|
|
3325
|
-
div[class*="uV2eYG_modes"] {
|
|
3326
|
-
display: flex !important;
|
|
3327
|
-
align-items: center !important;
|
|
3328
|
-
gap: 4px !important;
|
|
3329
|
-
flex: 0 0 auto !important;
|
|
3330
|
-
min-width: 0 !important;
|
|
3331
|
-
}
|
|
3332
|
-
|
|
3333
|
-
button[class*="Sh0Q9G_trigger"] {
|
|
3334
|
-
flex: 0 0 auto !important;
|
|
3335
|
-
min-width: 0 !important;
|
|
3336
|
-
}
|
|
3337
|
-
|
|
3338
|
-
div[class*="uV2eYG_trailing"] {
|
|
3339
|
-
display: flex !important;
|
|
3340
|
-
align-items: center !important;
|
|
3341
|
-
justify-content: flex-end !important;
|
|
3342
|
-
gap: 6px !important;
|
|
3343
|
-
flex: 1 1 auto !important;
|
|
3344
|
-
min-width: 0 !important;
|
|
3345
|
-
}
|
|
3346
|
-
|
|
3347
|
-
div[class*="_7KE1Ra_root"] {
|
|
3348
|
-
flex: 0 1 auto !important;
|
|
3349
|
-
min-width: 0 !important;
|
|
3350
|
-
max-width: 180px !important;
|
|
3351
|
-
}
|
|
3352
|
-
|
|
3353
|
-
button[class*="_7KE1Ra_trigger"] {
|
|
3354
|
-
max-width: 100% !important;
|
|
3355
|
-
min-width: 0 !important;
|
|
3356
|
-
flex: 1 1 auto !important;
|
|
3357
|
-
padding: 0 4px 0 6px !important;
|
|
3358
|
-
}
|
|
3359
|
-
|
|
3360
|
-
span[class*="_7KE1Ra_triggerLabel"] {
|
|
3361
|
-
overflow: hidden !important;
|
|
3362
|
-
text-overflow: ellipsis !important;
|
|
3363
|
-
white-space: nowrap !important;
|
|
3364
|
-
min-width: 0 !important;
|
|
3365
|
-
}
|
|
3366
|
-
|
|
3367
|
-
/* 4. 原生侧边栏抽屉化 (Drawer) */
|
|
3368
|
-
div[class*="_sidebarCol"] {
|
|
3369
|
-
position: fixed !important;
|
|
3370
|
-
left: 0 !important;
|
|
3371
|
-
top: 0 !important;
|
|
3372
|
-
bottom: 0 !important;
|
|
3373
|
-
height: 100dvh !important;
|
|
3374
|
-
width: 290px !important;
|
|
3375
|
-
max-width: 82vw !important;
|
|
3376
|
-
z-index: 10000 !important;
|
|
3377
|
-
background: var(--dsw-alias-bg-layer-1, #ffffff) !important;
|
|
3378
|
-
transform: translateX(-105%);
|
|
3379
|
-
transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1);
|
|
3380
|
-
overflow-y: auto !important;
|
|
3381
|
-
border-right: 1px solid rgba(0, 0, 0, 0.06) !important;
|
|
3382
|
-
pointer-events: auto !important;
|
|
3383
|
-
}
|
|
3384
|
-
body.dsh-drawer-open div[class*="_sidebarCol"] {
|
|
3385
|
-
transform: translateX(0) !important;
|
|
3386
|
-
box-shadow: 4px 0 28px rgba(0, 0, 0, 0.25) !important;
|
|
3387
|
-
pointer-events: auto !important;
|
|
3388
|
-
}
|
|
3389
|
-
|
|
3390
|
-
/* 抽屉内部:强制 100% 宽度,无论内部状态如何均正常展开并展示 DSH 自带的顶部收起侧边栏图标 */
|
|
3391
|
-
body.dsh-drawer-open div[class*="hHd-Xa_root"] {
|
|
3392
|
-
width: 100% !important;
|
|
3393
|
-
max-width: 100% !important;
|
|
3394
|
-
min-width: 100% !important;
|
|
3395
|
-
display: flex !important;
|
|
3396
|
-
flex-direction: column !important;
|
|
3397
|
-
}
|
|
3398
|
-
body.dsh-drawer-open div[class*="hHd-Xa_collapsed"] div[class*="hHd-Xa_regionArea"],
|
|
3399
|
-
body.dsh-drawer-open div[class*="hHd-Xa_collapsed"] button[class*="hHd-Xa_newSession"],
|
|
3400
|
-
body.dsh-drawer-open div[class*="hHd-Xa_collapsed"] div[class*="qDHVXG_root"] {
|
|
3401
|
-
display: flex !important;
|
|
3402
|
-
visibility: visible !important;
|
|
3403
|
-
}
|
|
3404
|
-
div[class*="hHd-Xa_logoRow"] {
|
|
3405
|
-
display: flex !important;
|
|
3406
|
-
align-items: center !important;
|
|
3407
|
-
justify-content: space-between !important;
|
|
3408
|
-
width: 100% !important;
|
|
3409
|
-
padding: 10px 14px 6px 14px !important;
|
|
3410
|
-
box-sizing: border-box !important;
|
|
3411
|
-
}
|
|
3412
|
-
div[class*="hHd-Xa_logoRow"] button[class*="hHd-Xa_toggle"] {
|
|
3413
|
-
display: inline-flex !important;
|
|
3414
|
-
align-items: center !important;
|
|
3415
|
-
justify-content: center !important;
|
|
3416
|
-
width: 32px !important;
|
|
3417
|
-
height: 32px !important;
|
|
3418
|
-
border-radius: 8px !important;
|
|
3419
|
-
color: var(--dsw-alias-label-secondary, #6b7280) !important;
|
|
3420
|
-
background: transparent !important;
|
|
3421
|
-
border: none !important;
|
|
3422
|
-
cursor: pointer !important;
|
|
3423
|
-
margin-left: auto !important;
|
|
3424
|
-
transition: background 0.15s, color 0.15s !important;
|
|
3425
|
-
}
|
|
3426
|
-
div[class*="hHd-Xa_logoRow"] button[class*="hHd-Xa_toggle"]:active {
|
|
3427
|
-
background: var(--dsw-alias-bg-layer-2, rgba(0, 0, 0, 0.06)) !important;
|
|
3428
|
-
color: var(--dsw-alias-label-primary, #111827) !important;
|
|
3429
|
-
}
|
|
3430
|
-
|
|
3431
|
-
/* 设置弹窗打开时解除抽屉隐藏限制 */
|
|
3432
|
-
div[class*="_sidebarCol"]:has(div[class*="VOzbGW_overlay"]) {
|
|
3433
|
-
transform: none !important;
|
|
3434
|
-
width: 100vw !important;
|
|
3435
|
-
max-width: 100vw !important;
|
|
3436
|
-
background: transparent !important;
|
|
3437
|
-
box-shadow: none !important;
|
|
3438
|
-
pointer-events: none !important;
|
|
3439
|
-
}
|
|
3440
|
-
|
|
3441
|
-
/* 5. 半透明背景遮罩 */
|
|
3442
|
-
.dsh-mobile-backdrop {
|
|
3443
|
-
position: fixed;
|
|
3444
|
-
inset: 0;
|
|
3445
|
-
background: rgba(0, 0, 0, 0.4);
|
|
3446
|
-
z-index: 9999;
|
|
3447
|
-
display: none !important;
|
|
3448
|
-
}
|
|
3449
|
-
body.dsh-drawer-open .dsh-mobile-backdrop {
|
|
3450
|
-
display: block !important;
|
|
3451
|
-
pointer-events: auto !important;
|
|
3452
|
-
}
|
|
3453
|
-
|
|
3454
|
-
/* 6. 设置中心全自适应适配 */
|
|
3455
|
-
div[class*="VOzbGW_overlay"] {
|
|
3456
|
-
position: fixed !important;
|
|
3457
|
-
inset: 0 !important;
|
|
3458
|
-
width: 100vw !important;
|
|
3459
|
-
height: 100dvh !important;
|
|
3460
|
-
display: flex !important;
|
|
3461
|
-
align-items: center !important;
|
|
3462
|
-
justify-content: center !important;
|
|
3463
|
-
background: rgba(0, 0, 0, 0.45) !important;
|
|
3464
|
-
backdrop-filter: blur(4px) !important;
|
|
3465
|
-
-webkit-backdrop-filter: blur(4px) !important;
|
|
3466
|
-
z-index: 10002 !important;
|
|
3467
|
-
padding: 10px !important;
|
|
3468
|
-
box-sizing: border-box !important;
|
|
3469
|
-
pointer-events: auto !important;
|
|
3470
|
-
}
|
|
3471
|
-
div[class*="VOzbGW_panel"] {
|
|
3472
|
-
width: 100% !important;
|
|
3473
|
-
max-width: 100% !important;
|
|
3474
|
-
height: 92dvh !important;
|
|
3475
|
-
max-height: 92dvh !important;
|
|
3476
|
-
display: flex !important;
|
|
3477
|
-
flex-direction: row !important;
|
|
3478
|
-
border-radius: 18px !important;
|
|
3479
|
-
overflow: hidden !important;
|
|
3480
|
-
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.25) !important;
|
|
3481
|
-
background: var(--dsw-alias-bg-layer-1, #ffffff) !important;
|
|
3482
|
-
}
|
|
3483
|
-
nav[class*="VOzbGW_nav"] {
|
|
3484
|
-
width: 78px !important;
|
|
3485
|
-
min-width: 78px !important;
|
|
3486
|
-
max-width: 78px !important;
|
|
3487
|
-
padding: 10px 4px !important;
|
|
3488
|
-
box-sizing: border-box !important;
|
|
3489
|
-
border-right: 1px solid var(--dsw-alias-border-l2, #e5e7eb) !important;
|
|
3490
|
-
display: flex !important;
|
|
3491
|
-
flex-direction: column !important;
|
|
3492
|
-
gap: 6px !important;
|
|
3493
|
-
overflow-y: auto !important;
|
|
3494
|
-
}
|
|
3495
|
-
nav[class*="VOzbGW_nav"] button[class*="VOzbGW_navCell"],
|
|
3496
|
-
button[class*="VOzbGW_navCell"] {
|
|
3497
|
-
padding: 8px 2px !important;
|
|
3498
|
-
display: flex !important;
|
|
3499
|
-
flex-direction: column !important;
|
|
3500
|
-
align-items: center !important;
|
|
3501
|
-
justify-content: center !important;
|
|
3502
|
-
text-align: center !important;
|
|
3503
|
-
height: auto !important;
|
|
3504
|
-
min-height: 48px !important;
|
|
3505
|
-
gap: 4px !important;
|
|
3506
|
-
border-radius: 10px !important;
|
|
3507
|
-
}
|
|
3508
|
-
span[class*="VOzbGW_navLabel"] {
|
|
3509
|
-
font-size: 10.5px !important;
|
|
3510
|
-
line-height: 1.2 !important;
|
|
3511
|
-
white-space: normal !important;
|
|
3512
|
-
word-break: break-all !important;
|
|
3513
|
-
text-align: center !important;
|
|
3514
|
-
}
|
|
3515
|
-
div[class*="VOzbGW_content"] {
|
|
3516
|
-
flex: 1 1 auto !important;
|
|
3517
|
-
min-width: 0 !important;
|
|
3518
|
-
width: calc(100% - 78px) !important;
|
|
3519
|
-
max-width: calc(100% - 78px) !important;
|
|
3520
|
-
display: flex !important;
|
|
3521
|
-
flex-direction: column !important;
|
|
3522
|
-
overflow: hidden !important;
|
|
3523
|
-
}
|
|
3524
|
-
div[class*="VOzbGW_options"] {
|
|
3525
|
-
flex: 1 1 auto !important;
|
|
3526
|
-
width: 100% !important;
|
|
3527
|
-
max-width: 100% !important;
|
|
3528
|
-
box-sizing: border-box !important;
|
|
3529
|
-
padding: 0 14px 20px !important;
|
|
3530
|
-
overflow-x: hidden !important;
|
|
3531
|
-
overflow-y: auto !important;
|
|
3532
|
-
-webkit-overflow-scrolling: touch !important;
|
|
3533
|
-
}
|
|
3534
|
-
|
|
3535
|
-
/* 设置中心选项行手机自适应(垂直流式,防文字单字折行) */
|
|
3536
|
-
div[class*="VOzbGW_options"] div[class*="_row"] {
|
|
3537
|
-
display: flex !important;
|
|
3538
|
-
flex-direction: column !important;
|
|
3539
|
-
align-items: stretch !important;
|
|
3540
|
-
gap: 8px !important;
|
|
3541
|
-
width: 100% !important;
|
|
3542
|
-
padding: 12px 0 !important;
|
|
3543
|
-
box-sizing: border-box !important;
|
|
3544
|
-
}
|
|
3545
|
-
div[class*="VOzbGW_options"] div[class*="_rowText"] {
|
|
3546
|
-
width: 100% !important;
|
|
3547
|
-
max-width: 100% !important;
|
|
3548
|
-
}
|
|
3549
|
-
div[class*="VOzbGW_options"] button[class*="_selector"],
|
|
3550
|
-
div[class*="VOzbGW_options"] select,
|
|
3551
|
-
div[class*="VOzbGW_options"] input {
|
|
3552
|
-
width: 100% !important;
|
|
3553
|
-
max-width: 100% !important;
|
|
3554
|
-
box-sizing: border-box !important;
|
|
3555
|
-
}
|
|
3556
|
-
|
|
3557
|
-
/* 7. 代码块、表格与徽标自适应 */
|
|
3558
|
-
pre, code, pre > code, table {
|
|
3559
|
-
max-width: 100% !important;
|
|
3560
|
-
overflow-x: auto !important;
|
|
3561
|
-
-webkit-overflow-scrolling: touch !important;
|
|
3562
|
-
font-size: 12.5px !important;
|
|
3563
|
-
}
|
|
3564
|
-
|
|
3565
|
-
/* 状态徽标与药丸按钮永不折字 */
|
|
3566
|
-
span[style*="border-radius: 999"],
|
|
3567
|
-
span[style*="border-radius:999"] {
|
|
3568
|
-
white-space: nowrap !important;
|
|
3569
|
-
flex-shrink: 0 !important;
|
|
3570
|
-
min-width: max-content !important;
|
|
3571
|
-
}
|
|
3572
|
-
|
|
3573
|
-
/* 二维码与图片移动端弹性缩放 */
|
|
3574
|
-
img[alt="QR"], img[src^="data:image"] {
|
|
3575
|
-
max-width: 100% !important;
|
|
3576
|
-
box-sizing: border-box !important;
|
|
3577
|
-
}
|
|
3578
|
-
|
|
3579
|
-
/* 8. 确保所有 Popover 弹出菜单、操作气泡、下拉框位于抽屉之上且支持触控交互 */
|
|
3580
|
-
div[class*="_portal"],
|
|
3581
|
-
div[class*="portal"],
|
|
3582
|
-
div[class*="popup"],
|
|
3583
|
-
div[class*="dropdown"],
|
|
3584
|
-
div[class*="menu"],
|
|
3585
|
-
div[role="menu"],
|
|
3586
|
-
div[role="dialog"] {
|
|
3587
|
-
z-index: 10005 !important;
|
|
3588
|
-
pointer-events: auto !important;
|
|
3589
|
-
}
|
|
3590
|
-
|
|
3591
|
-
/* 9. 移动端侧边栏:会话与工作区三点操作按钮始终清晰可见且易于点击 */
|
|
3592
|
-
div[class*="sessionRow"] span[class*="rowActions"],
|
|
3593
|
-
div[class*="sessionRow"] button[class*="iconButton"],
|
|
3594
|
-
div[class*="treeBody"] button[class*="iconButton"] {
|
|
3595
|
-
opacity: 0.8 !important;
|
|
3596
|
-
display: inline-flex !important;
|
|
3597
|
-
visibility: visible !important;
|
|
3598
|
-
pointer-events: auto !important;
|
|
3599
|
-
}
|
|
3600
|
-
div[class*="sessionRow"]:active {
|
|
3601
|
-
background: var(--dsw-alias-bg-layer-2, #f3f4f6) !important;
|
|
3602
|
-
}
|
|
3603
|
-
|
|
3604
|
-
/* 全局 overlayLayer 绝不被染黑 */
|
|
3605
|
-
div[class*="overlayLayer"],
|
|
3606
|
-
div[class*="uV2eYG_overlayAnchor"] {
|
|
3607
|
-
background: transparent !important;
|
|
3608
|
-
pointer-events: none !important;
|
|
3609
|
-
}
|
|
3610
|
-
div[class*="overlayLayer"] > * {
|
|
3611
|
-
pointer-events: auto !important;
|
|
3612
|
-
}
|
|
3613
|
-
}
|
|
3614
|
-
|
|
3615
|
-
/* 远程工作区选择弹窗移动端/桌面端自适应样式 */
|
|
3616
|
-
#dsh-remote-workspace-modal {
|
|
3617
|
-
position: fixed !important;
|
|
3618
|
-
inset: 0 !important;
|
|
3619
|
-
z-index: 100000 !important;
|
|
3620
|
-
background: rgba(0, 0, 0, 0.65) !important;
|
|
3621
|
-
backdrop-filter: blur(5px) !important;
|
|
3622
|
-
display: flex !important;
|
|
3623
|
-
align-items: center !important;
|
|
3624
|
-
justify-content: center !important;
|
|
3625
|
-
padding: 16px !important;
|
|
3626
|
-
box-sizing: border-box !important;
|
|
3627
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
|
|
3628
|
-
color: var(--dsw-alias-label-primary, #111827) !important;
|
|
3629
|
-
}
|
|
3630
|
-
|
|
3631
|
-
.dsh-ws-dialog-card {
|
|
3632
|
-
background: var(--dsw-alias-bg-layer-1, #ffffff) !important;
|
|
3633
|
-
border: 1px solid var(--dsw-alias-border-l1, #e5e7eb) !important;
|
|
3634
|
-
border-radius: 16px !important;
|
|
3635
|
-
width: 100% !important;
|
|
3636
|
-
max-width: 620px !important;
|
|
3637
|
-
max-height: 88vh !important;
|
|
3638
|
-
display: flex !important;
|
|
3639
|
-
flex-direction: column !important;
|
|
3640
|
-
box-shadow: 0 25px 35px -5px rgba(0,0,0,0.3), 0 12px 16px -5px rgba(0,0,0,0.2) !important;
|
|
3641
|
-
overflow: hidden !important;
|
|
3642
|
-
animation: dshModalFadeIn 0.2s ease-out !important;
|
|
3643
|
-
}
|
|
3644
|
-
|
|
3645
|
-
.dsh-ws-chips-scroll {
|
|
3646
|
-
display: flex !important;
|
|
3647
|
-
align-items: center !important;
|
|
3648
|
-
gap: 6px !important;
|
|
3649
|
-
overflow-x: auto !important;
|
|
3650
|
-
white-space: nowrap !important;
|
|
3651
|
-
scrollbar-width: none !important;
|
|
3652
|
-
-ms-overflow-style: none !important;
|
|
3653
|
-
-webkit-overflow-scrolling: touch !important;
|
|
3654
|
-
padding: 2px 0 !important;
|
|
3655
|
-
}
|
|
3656
|
-
.dsh-ws-chips-scroll::-webkit-scrollbar {
|
|
3657
|
-
display: none !important;
|
|
3658
|
-
}
|
|
3659
|
-
|
|
3660
|
-
@media (max-width: 640px) {
|
|
3661
|
-
#dsh-remote-workspace-modal {
|
|
3662
|
-
align-items: flex-end !important;
|
|
3663
|
-
padding: 0 !important;
|
|
3664
|
-
}
|
|
3665
|
-
|
|
3666
|
-
.dsh-ws-dialog-card {
|
|
3667
|
-
max-height: 92dvh !important;
|
|
3668
|
-
height: 92dvh !important;
|
|
3669
|
-
border-bottom-left-radius: 0 !important;
|
|
3670
|
-
border-bottom-right-radius: 0 !important;
|
|
3671
|
-
border-left: none !important;
|
|
3672
|
-
border-right: none !important;
|
|
3673
|
-
border-bottom: none !important;
|
|
3674
|
-
max-width: 100vw !important;
|
|
3675
|
-
width: 100vw !important;
|
|
3676
|
-
margin: 0 !important;
|
|
3677
|
-
animation: dshBottomSheetUp 0.25s cubic-bezier(0.16, 1, 0.3, 1) !important;
|
|
3678
|
-
}
|
|
3679
|
-
|
|
3680
|
-
.dsh-ws-drag-handle {
|
|
3681
|
-
display: block !important;
|
|
3682
|
-
}
|
|
3683
|
-
}
|
|
3684
|
-
|
|
3685
|
-
@keyframes dshModalFadeIn {
|
|
3686
|
-
from { opacity: 0; transform: scale(0.96); }
|
|
3687
|
-
to { opacity: 1; transform: scale(1); }
|
|
3688
|
-
}
|
|
3689
|
-
|
|
3690
|
-
@keyframes dshBottomSheetUp {
|
|
3691
|
-
from { transform: translateY(100%); }
|
|
3692
|
-
to { transform: translateY(0); }
|
|
3693
|
-
}
|
|
3694
|
-
|
|
3695
|
-
@media (min-width: 769px) {
|
|
3696
|
-
.dsh-mobile-app-header,
|
|
3697
|
-
.dsh-mobile-backdrop,
|
|
3698
|
-
.dsh-mobile-panel-close-btn {
|
|
3699
|
-
display: none !important;
|
|
3700
|
-
}
|
|
3701
|
-
}
|
|
3702
|
-
`;
|
|
3045
|
+
style.textContent = MOBILE_STYLES_CSS;
|
|
3703
3046
|
document.head.appendChild(style);
|
|
3704
3047
|
}
|
|
3705
3048
|
|