@wenbin_wb/dsh-bridge 2.10.5 → 2.10.6

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/client/index.js CHANGED
@@ -213,14 +213,14 @@ function StatusTag({ running, status }) {
213
213
  bg = 'var(--dsw-alias-state-success-bg,#ecfdf5)';
214
214
  color = 'var(--dsw-alias-state-success-primary,#059669)';
215
215
  text = '已连接';
216
- } else if (status === 'starting') {
216
+ } else if (status === 'starting' || status === 'connecting' || status === 'downloading') {
217
217
  bg = 'var(--dsw-alias-state-info-bg,#eff6ff)';
218
218
  color = 'var(--dsw-alias-state-info-primary,#3b82f6)';
219
- text = '连接中…';
219
+ text = status === 'downloading' ? '下载中…' : '连接中…';
220
220
  } else if (status === 'reconnecting') {
221
221
  bg = 'var(--dsw-alias-state-warn-bg,#fffbeb)';
222
222
  color = 'var(--dsw-alias-state-warn-primary,#d97706)';
223
- text = '重连中…';
223
+ text = '自动重连中…';
224
224
  } else if (status === 'paused') {
225
225
  bg = 'var(--dsw-alias-state-warn-bg,#fffbeb)';
226
226
  color = 'var(--dsw-alias-state-warn-primary,#d97706)';
@@ -416,6 +416,146 @@ const CustomTunnelGuide = React.memo(function CustomTunnelGuide() {
416
416
  );
417
417
  });
418
418
 
419
+ // ── 公网入口总览卡:把"当前生效的访问地址"提到页面主角位置 ──────────────
420
+ const TunnelEntryCard = React.memo(function TunnelEntryCard({
421
+ entry, onCopy, copied, autoStart, onToggleAutoStart, onStart, onStop, onReset,
422
+ }) {
423
+ const [showQr, setShowQr] = React.useState(false);
424
+ const hasUrl = Boolean(entry && entry.url);
425
+ const active = Boolean(entry && entry.running);
426
+
427
+ return React.createElement('div', {
428
+ style: {
429
+ ...s.card,
430
+ borderColor: active ? 'var(--dsw-alias-state-success-border,#a7f3d0)' : undefined,
431
+ background: active ? 'linear-gradient(180deg, var(--dsw-alias-bg-layer-2,#f9fafb), var(--dsw-alias-bg-layer-1,#ffffff))' : undefined,
432
+ },
433
+ },
434
+ React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 10 } },
435
+ React.createElement('div', { style: { flex: '1 1 auto', minWidth: 0 } },
436
+ React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
437
+ React.createElement('span', { style: { fontSize: 16 } }, '🌐'),
438
+ React.createElement('div', { style: s.label }, '公网访问入口'),
439
+ ),
440
+ React.createElement('div', { style: { ...s.muted, marginTop: 4 } },
441
+ entry ? (entry.title + ' · ' + entry.desc) : '尚未配置任何公网隧道,可在下方开启 Cloudflare 隧道或配置自建隧道'
442
+ ),
443
+ ),
444
+ React.createElement(StatusTag, {
445
+ running: active,
446
+ status: (entry && entry.phase && entry.phase !== 'ready') ? entry.phase : undefined,
447
+ }),
448
+ ),
449
+
450
+ React.createElement('div', { style: { ...s.block, display: 'flex', flexDirection: 'column', gap: 10 } },
451
+ // 有地址:大号 URL + 复制
452
+ hasUrl && React.createElement('div', {
453
+ style: {
454
+ padding: '10px 12px',
455
+ background: 'var(--dsw-alias-bg-layer-1,#ffffff)',
456
+ border: '1px solid var(--dsw-alias-border-l2,#e5e7eb)',
457
+ borderRadius: 10,
458
+ display: 'flex', alignItems: 'center', gap: 10,
459
+ },
460
+ },
461
+ React.createElement('code', {
462
+ style: { ...s.code, flex: '1 1 auto', fontSize: 13.5, wordBreak: 'break-all', lineHeight: 1.5 },
463
+ }, entry.url),
464
+ React.createElement('button', {
465
+ style: { ...s.btnGhost, flexShrink: 0, height: 28, padding: '0 12px', fontSize: 12 },
466
+ onClick: () => onCopy && onCopy(entry.url),
467
+ }, copied ? '✓ 已复制' : '复制'),
468
+ ),
469
+ // 状态细节(重连/错误/连接中)——无 URL 时格外重要,让用户知道隧道在自愈而非消失
470
+ entry && entry.stateDetail && React.createElement('div', {
471
+ style: {
472
+ fontSize: 12, lineHeight: 1.5,
473
+ color: entry.phase === 'error' ? 'var(--dsw-alias-state-error-primary,#dc2626)'
474
+ : entry.phase === 'reconnecting' ? 'var(--dsw-alias-state-warn-primary,#d97706)'
475
+ : 'var(--dsw-alias-label-secondary,#6b7280)',
476
+ },
477
+ }, entry.stateDetail),
478
+ // 操作行:running → 关闭/停止重连(+ 重置);!running → 开启
479
+ active && onStop && React.createElement('div', { style: { display: 'flex', gap: 8, flexWrap: 'wrap' } },
480
+ React.createElement('button', {
481
+ style: s.btnGhost,
482
+ onClick: onStop,
483
+ }, entry && entry.phase === 'reconnecting' ? '停止重连' : '关闭'),
484
+ onReset && React.createElement('button', {
485
+ style: { ...s.btnGhost, height: 28, padding: '0 12px', fontSize: 12 },
486
+ onClick: onReset,
487
+ title: '关闭并重新开启,更换临时地址',
488
+ }, '🔄 重置链接'),
489
+ ),
490
+ // running 但无 onStop 的入口(如外部登记):只给重置/二维码辅助,无开关
491
+ active && !onStop && onReset && React.createElement('div', { style: { display: 'flex', gap: 8, flexWrap: 'wrap' } },
492
+ React.createElement('button', {
493
+ style: { ...s.btnGhost, height: 28, padding: '0 12px', fontSize: 12 },
494
+ onClick: onReset,
495
+ title: '关闭并重新开启,更换临时地址',
496
+ }, '🔄 重置链接'),
497
+ ),
498
+ // 未运行:引导开启(连接中/下载中禁用)
499
+ !active && onStart && React.createElement('button', {
500
+ style: {
501
+ ...s.btnPri, alignSelf: 'flex-start',
502
+ opacity: (entry && entry.configured === false) ? 0.4 : 1,
503
+ background: (entry && entry.phase === 'connecting') ? 'var(--dsw-alias-state-info-primary,#3b82f6)' : undefined,
504
+ },
505
+ onClick: onStart,
506
+ disabled: Boolean((entry && entry.configured === false) || (entry && (entry.phase === 'connecting' || entry.phase === 'downloading'))),
507
+ title: (entry && entry.configured === false) ? '请先在「隧道配置」中保存服务器配置' : '',
508
+ }, (entry && entry.phase === 'connecting') ? '连接中…' : (entry && entry.phase === 'downloading') ? '下载中…' : '开启公网隧道'),
509
+ // 二维码辅助按钮:有地址未运行时也可查看(外部登记等)
510
+ hasUrl && entry.qr && React.createElement('div', { style: { display: 'flex', gap: 8, flexWrap: 'wrap' } },
511
+ React.createElement('button', {
512
+ style: { ...s.btnGhost, height: 28, padding: '0 12px', fontSize: 12 },
513
+ onClick: () => setShowQr((v) => !v),
514
+ }, showQr ? '隐藏二维码' : '显示二维码'),
515
+ ),
516
+ showQr && hasUrl && entry.qr && React.createElement('div', { style: { display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 6 } },
517
+ React.createElement('img', { src: entry.qr, alt: 'QR', style: { ...s.qr, margin: 0 } }),
518
+ React.createElement('div', { style: { ...s.muted, fontSize: 11 } }, '请在私密环境下扫码使用'),
519
+ ),
520
+ ),
521
+
522
+ onToggleAutoStart && React.createElement('label', {
523
+ style: {
524
+ display: 'flex', alignItems: 'center', gap: 6, marginTop: 12, paddingTop: 10,
525
+ borderTop: '1px solid var(--dsw-alias-border-l2,#e5e7eb)',
526
+ fontSize: 12, color: 'var(--dsw-alias-label-secondary,#6b7280)', cursor: 'pointer', userSelect: 'none',
527
+ },
528
+ title: 'DSH 启动时自动恢复该隧道的运行状态',
529
+ },
530
+ React.createElement('input', {
531
+ type: 'checkbox',
532
+ checked: Boolean(autoStart),
533
+ onChange: (e) => onToggleAutoStart(e.target.checked),
534
+ }),
535
+ React.createElement('span', null, '随 DSH 启动自动开启'),
536
+ ),
537
+ );
538
+ });
539
+
540
+ // 隧道配置折叠分组:把 CF / 自建 / 外部 三套"不常用"配置收进一处
541
+ const TunnelConfigGroup = React.memo(function TunnelConfigGroup({ children }) {
542
+ const [open, setOpen] = React.useState(false);
543
+ return React.createElement('div', { style: s.card },
544
+ React.createElement('div', {
545
+ style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', cursor: 'pointer', userSelect: 'none', gap: 8 },
546
+ onClick: () => setOpen((v) => !v),
547
+ },
548
+ React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
549
+ React.createElement('span', { style: { fontSize: 14 } }, '⚙️'),
550
+ React.createElement('div', { style: s.label }, '隧道配置'),
551
+ React.createElement('span', { style: { ...s.muted, fontSize: 11 } }, 'Token · 固定域名 · 自建服务器 · 外部登记'),
552
+ ),
553
+ React.createElement('span', { style: { fontSize: 12, color: 'var(--dsw-alias-label-tertiary,#9ca3af)', flexShrink: 0 } }, open ? '收起 ▴' : '展开 ▾'),
554
+ ),
555
+ open && React.createElement('div', { style: { marginTop: 4 } }, children),
556
+ );
557
+ });
558
+
419
559
  const CustomTunnelConfigForm = React.memo(function CustomTunnelConfigForm({ serverUrl: initUrl, accessToken: initToken, onSave }) {
420
560
  const [serverUrl, setServerUrl] = React.useState(initUrl ?? '');
421
561
  const [accessToken, setAccessToken] = React.useState(initToken ?? '');
@@ -511,7 +651,7 @@ const TunnelCard = React.memo(function TunnelCard({
511
651
  React.createElement('div', { style: { ...s.muted, marginTop: 2 } }, desc),
512
652
  ),
513
653
  React.createElement('div', { style: { display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 6 } },
514
- React.createElement(StatusTag, { running }),
654
+ React.createElement(StatusTag, { running, status: phase === 'ready' ? undefined : phase }),
515
655
  onToggleAutoStart && React.createElement('label', {
516
656
  style: {
517
657
  display: 'flex',
@@ -537,7 +677,9 @@ const TunnelCard = React.memo(function TunnelCard({
537
677
  phase !== 'idle' && phase !== 'ready' && React.createElement('div', {
538
678
  style: {
539
679
  ...s.block, fontSize: 12,
540
- color: phase === 'error' ? 'var(--dsw-alias-state-error-primary,#dc2626)' : 'var(--dsw-alias-label-secondary,#6b7280)',
680
+ color: phase === 'error' ? 'var(--dsw-alias-state-error-primary,#dc2626)'
681
+ : phase === 'reconnecting' ? 'var(--dsw-alias-state-warn-primary,#d97706)'
682
+ : 'var(--dsw-alias-label-secondary,#6b7280)',
541
683
  },
542
684
  }, state?.detail ?? phase),
543
685
  url && React.createElement(QrBlock, { url, qr, onReset, auth, onNavigateSecurity }),
@@ -550,7 +692,10 @@ const TunnelCard = React.memo(function TunnelCard({
550
692
  disabled: configured === false || phase === 'connecting' || phase === 'downloading',
551
693
  title: configured === false ? '请先保存服务器配置' : '',
552
694
  }, phase === 'connecting' ? '连接中…' : phase === 'downloading' ? '下载中…' : '开启'),
553
- running && onStop && React.createElement('button', { style: s.btnGhost, onClick: onStop }, '关闭'),
695
+ running && onStop && React.createElement('button', {
696
+ style: s.btnGhost,
697
+ onClick: onStop,
698
+ }, phase === 'reconnecting' ? '停止重连' : '关闭'),
554
699
  ),
555
700
  );
556
701
  });
@@ -2595,6 +2740,20 @@ function BridgePanel({ rpcCall }) {
2595
2740
  const [platforms, setPlatforms] = React.useState(null);
2596
2741
  const [selectedPlatform, setSelectedPlatform] = React.useState('wechat');
2597
2742
 
2743
+ // 隧道页"主入口"地址复制反馈
2744
+ const [copiedUrl, setCopiedUrl] = React.useState('');
2745
+ const copyEntryUrl = React.useCallback((url) => {
2746
+ const done = () => {
2747
+ setCopiedUrl(url);
2748
+ setTimeout(() => setCopiedUrl(''), 2000);
2749
+ };
2750
+ if (navigator.clipboard?.writeText) {
2751
+ navigator.clipboard.writeText(url).then(done).catch(() => done());
2752
+ } else {
2753
+ done();
2754
+ }
2755
+ }, []);
2756
+
2598
2757
  // 远程设备管理权限解锁状态
2599
2758
  const isLocalhost = typeof window === 'undefined' || (
2600
2759
  !window.location.hostname ||
@@ -2851,58 +3010,103 @@ function BridgePanel({ rpcCall }) {
2851
3010
  );
2852
3011
  } else if (activeTab === 'tunnel') {
2853
3012
  const ext = status?.externalTunnel;
2854
- tabContent = React.createElement(React.Fragment, null,
2855
- React.createElement(TunnelCard, {
2856
- title: 'Cloudflare 隧道',
2857
- desc: status?.cloudflared?.tokenConfigured
2858
- ? '固定域名模式(Token 运行 · 重启 URL 保持不变)'
2859
- : '一键获取公网地址(免登录临时随机域名)',
2860
- data: {
2861
- running: status?.cloudflared?.running,
2862
- url: status?.cloudflared?.url,
2863
- qr: status?.cloudflared?.qr,
2864
- state: status?.cloudflared?.state,
2865
- },
2866
- autoStart: status?.cloudflared?.autoStart,
2867
- onToggleAutoStart: onToggleCloudflaredAutoStart,
2868
- auth: status?.auth,
2869
- onNavigateSecurity: navSecurity,
2870
- onStart: onStartCloudflared,
2871
- onStop: onStopCloudflared,
2872
- onReset: status?.cloudflared?.running ? onResetCloudflared : null,
3013
+ const cf = status?.cloudflared;
3014
+
3015
+ // 计算"当前主入口":优先自建隧道(固定地址)> Cloudflare > 外部登记。
3016
+ // running(含重连中)即入列——重连时 url 可能暂时为空,但应让用户看到状态。
3017
+ const cfDesc = cf?.tokenConfigured ? '固定域名模式' : '免登录临时域名';
3018
+ const entries = [
3019
+ ct && ct.running && {
3020
+ key: 'custom', title: '自建隧道', desc: 'VPS 自建 · 固定地址',
3021
+ url: ct.url || null, qr: ct.qr, running: true,
3022
+ phase: ct.state && ct.state.phase, stateDetail: ct.state && ct.state.detail,
3023
+ autoStart: ct.autoStart, onToggleAutoStart: onToggleCustomAutoStart,
3024
+ onStart: onStartCustom, onStop: onStopCustom,
2873
3025
  },
2874
- React.createElement(CloudflareConfigForm, {
2875
- token: status?.cloudflared?.token ?? '',
2876
- hostname: status?.cloudflared?.hostname ?? '',
2877
- onSave: saveCloudflaredConfig,
2878
- }),
2879
- ),
2880
- React.createElement(ExternalTunnelCard, {
2881
- ext,
2882
- onSave: saveExternalTunnel,
3026
+ cf && cf.running && {
3027
+ key: 'cloudflared', title: 'Cloudflare 隧道', desc: cfDesc,
3028
+ url: cf.url || null, qr: cf.qr, running: true,
3029
+ phase: cf.state && cf.state.phase, stateDetail: cf.state && cf.state.detail,
3030
+ autoStart: cf.autoStart, onToggleAutoStart: onToggleCloudflaredAutoStart,
3031
+ onStart: onStartCloudflared, onStop: onStopCloudflared,
3032
+ onReset: onResetCloudflared,
3033
+ },
3034
+ ext && ext.configured && ext.url && {
3035
+ key: 'external', title: '外部已部署隧道', desc: '自行部署登记',
3036
+ url: ext.url, qr: ext.qr, running: true,
3037
+ },
3038
+ ].filter(Boolean);
3039
+ const primary = entries[0] || null;
3040
+ const otherCount = primary ? entries.length - 1 : entries.length;
3041
+
3042
+ tabContent = React.createElement(React.Fragment, null,
3043
+ React.createElement(TunnelEntryCard, {
3044
+ entry: primary,
3045
+ onCopy: copyEntryUrl,
3046
+ copied: Boolean(copiedUrl && primary && copiedUrl === primary.url),
3047
+ autoStart: primary ? primary.autoStart : undefined,
3048
+ onToggleAutoStart: primary ? primary.onToggleAutoStart : undefined,
3049
+ onStart: primary ? primary.onStart : (cf ? onStartCloudflared : null),
3050
+ onStop: primary ? primary.onStop : undefined,
3051
+ onReset: primary ? primary.onReset : undefined,
2883
3052
  }),
2884
- React.createElement(TunnelCard, {
2885
- title: '自建隧道',
2886
- desc: '连接自己部署的隧道服务器,获得固定域名',
2887
- data: {
2888
- configured: ct?.configured,
2889
- running: ct?.running,
2890
- url: ct?.url,
2891
- qr: ct?.qr,
2892
- state: ct?.state,
3053
+ otherCount > 0 && React.createElement('div', {
3054
+ style: { ...s.muted, fontSize: 11, marginBottom: 8, textAlign: 'center' },
3055
+ }, '另有 ' + otherCount + ' 个隧道入口在运行,可在下方「隧道配置」中查看与管理'),
3056
+
3057
+ React.createElement(TunnelConfigGroup, null,
3058
+ React.createElement(TunnelCard, {
3059
+ title: 'Cloudflare 隧道',
3060
+ desc: cf && cf.tokenConfigured
3061
+ ? '固定域名模式(Token 运行 · 重启 URL 保持不变)'
3062
+ : '一键获取公网地址(免登录临时随机域名)',
3063
+ data: {
3064
+ running: cf && cf.running,
3065
+ url: cf && cf.url,
3066
+ qr: cf && cf.qr,
3067
+ state: cf && cf.state,
3068
+ },
3069
+ autoStart: cf && cf.autoStart,
3070
+ onToggleAutoStart: onToggleCloudflaredAutoStart,
3071
+ auth: status && status.auth,
3072
+ onNavigateSecurity: navSecurity,
3073
+ onStart: onStartCloudflared,
3074
+ onStop: onStopCloudflared,
3075
+ onReset: (cf && cf.running) ? onResetCloudflared : null,
2893
3076
  },
2894
- autoStart: ct?.autoStart,
2895
- onToggleAutoStart: onToggleCustomAutoStart,
2896
- auth: status?.auth,
2897
- onNavigateSecurity: navSecurity,
2898
- onStart: onStartCustom,
2899
- onStop: onStopCustom,
2900
- },
2901
- React.createElement(CustomTunnelGuide),
2902
- React.createElement(CustomTunnelConfigForm, {
2903
- serverUrl: ct?.serverUrl ?? '',
2904
- accessToken: ct?.accessToken ?? '',
2905
- onSave: saveConfig,
3077
+ React.createElement(CloudflareConfigForm, {
3078
+ token: (cf && cf.token) || '',
3079
+ hostname: (cf && cf.hostname) || '',
3080
+ onSave: saveCloudflaredConfig,
3081
+ }),
3082
+ ),
3083
+ React.createElement(TunnelCard, {
3084
+ title: '自建隧道',
3085
+ desc: '连接自己部署的隧道服务器,获得固定域名',
3086
+ data: {
3087
+ configured: ct && ct.configured,
3088
+ running: ct && ct.running,
3089
+ url: ct && ct.url,
3090
+ qr: ct && ct.qr,
3091
+ state: ct && ct.state,
3092
+ },
3093
+ autoStart: ct && ct.autoStart,
3094
+ onToggleAutoStart: onToggleCustomAutoStart,
3095
+ auth: status && status.auth,
3096
+ onNavigateSecurity: navSecurity,
3097
+ onStart: onStartCustom,
3098
+ onStop: onStopCustom,
3099
+ },
3100
+ React.createElement(CustomTunnelGuide),
3101
+ React.createElement(CustomTunnelConfigForm, {
3102
+ serverUrl: (ct && ct.serverUrl) || '',
3103
+ accessToken: (ct && ct.accessToken) || '',
3104
+ onSave: saveConfig,
3105
+ }),
3106
+ ),
3107
+ React.createElement(ExternalTunnelCard, {
3108
+ ext,
3109
+ onSave: saveExternalTunnel,
2906
3110
  }),
2907
3111
  ),
2908
3112
  );
@@ -2991,6 +3195,14 @@ function BridgePanel({ rpcCall }) {
2991
3195
  const policy = auth?.adminPolicy ?? 'password_unlock';
2992
3196
  // 系统是否已配置任何密码(访客访问密码 或 独立管理密码)。
2993
3197
  const hasAnyPassword = !!(auth?.hasPassword || auth?.hasAdminPassword);
3198
+ // 解锁密码类型:有独立管理密码 → 用管理密码;否则(只有访问密码/都没有)
3199
+ // 服务端以 adminPasswordHash || passwordHash 兜底,实际校验的是访问密码。
3200
+ // UI 需如实告诉用户该输哪个密码,避免"哪来的管理密码"的困惑。
3201
+ const unlockUsesAdmin = !!auth?.hasAdminPassword;
3202
+ const unlockPwdKind = unlockUsesAdmin ? '管理密码' : '访问密码';
3203
+ const unlockPwdHint = unlockUsesAdmin
3204
+ ? '请输入后台管理密码解锁管理权限。'
3205
+ : '当前未设置独立管理密码,输入您的访问密码即可解锁。';
2994
3206
  // 锁屏条件:远程 + 管理保护开启(adminProtection)+ 未解锁 + 非宽松策略。
2995
3207
  // 不依赖 auth.enabled:即使访问认证关闭,管理保护仍独立生效,上锁后必须显示锁屏。
2996
3208
  // local_only 也锁定(显示"仅限本机管理"专属锁屏)。
@@ -3040,8 +3252,32 @@ function BridgePanel({ rpcCall }) {
3040
3252
  React.createElement('div', { style: { textAlign: 'center', marginBottom: 20 } },
3041
3253
  React.createElement('div', { style: { fontSize: 40, marginBottom: 10 } }, '🔒'),
3042
3254
  React.createElement('div', { style: { ...s.label, fontSize: 16, fontWeight: 600 } }, '管理控制台已锁定'),
3043
- React.createElement('div', { style: { ...s.muted, fontSize: 12, marginTop: 6, lineHeight: 1.5 } },
3044
- '当前设备为远程访问。为保护您的网络与平台配置安全,请输入管理员密码解锁管理权限。'
3255
+ // 醒目提示该输入哪种密码
3256
+ React.createElement('div', { style: { marginTop: 10, display: 'flex', justifyContent: 'center' } },
3257
+ unlockUsesAdmin ? (
3258
+ React.createElement('span', {
3259
+ style: {
3260
+ display: 'inline-flex', alignItems: 'center', gap: 5,
3261
+ padding: '4px 12px', borderRadius: 999, fontSize: 12, fontWeight: 600,
3262
+ background: 'var(--dsw-alias-state-info-bg,#eff6ff)',
3263
+ color: 'var(--dsw-alias-state-info-primary,#2563eb)',
3264
+ border: '1px solid var(--dsw-alias-state-info-border,#bfdbfe)',
3265
+ },
3266
+ }, '🔑 使用管理密码解锁')
3267
+ ) : (
3268
+ React.createElement('span', {
3269
+ style: {
3270
+ display: 'inline-flex', alignItems: 'center', gap: 5,
3271
+ padding: '4px 12px', borderRadius: 999, fontSize: 12, fontWeight: 600,
3272
+ background: 'var(--dsw-alias-state-success-bg,#ecfdf5)',
3273
+ color: 'var(--dsw-alias-state-success-primary,#059669)',
3274
+ border: '1px solid var(--dsw-alias-state-success-border,#a7f3d0)',
3275
+ },
3276
+ }, '🔐 使用访问密码解锁')
3277
+ ),
3278
+ ),
3279
+ React.createElement('div', { style: { ...s.muted, fontSize: 12, marginTop: 10, lineHeight: 1.6 } },
3280
+ '当前设备为远程访问。为保护您的网络与平台配置安全,' + unlockPwdHint
3045
3281
  ),
3046
3282
  ),
3047
3283
  React.createElement('form', {
@@ -3051,7 +3287,7 @@ function BridgePanel({ rpcCall }) {
3051
3287
  React.createElement('input', {
3052
3288
  type: 'password',
3053
3289
  style: s.input,
3054
- placeholder: '输入后台管理密码',
3290
+ placeholder: '输入' + unlockPwdKind,
3055
3291
  value: unlockPassword,
3056
3292
  onChange: (e) => setUnlockPassword(e.target.value),
3057
3293
  autoFocus: true,
@@ -3070,7 +3306,7 @@ function BridgePanel({ rpcCall }) {
3070
3306
  type: 'button',
3071
3307
  style: { ...s.btnLink, fontSize: 12, color: 'var(--dsw-alias-label-secondary,#6b7280)' },
3072
3308
  onClick: () => setShowForgotGuide(v => !v),
3073
- }, '❓ 忘记后台管理密码?'),
3309
+ }, '❓ 忘记' + unlockPwdKind + '?'),
3074
3310
  ),
3075
3311
  showForgotGuide && React.createElement('div', {
3076
3312
  style: {
@@ -3079,8 +3315,8 @@ function BridgePanel({ rpcCall }) {
3079
3315
  color: 'var(--dsw-alias-label-secondary,#4b5563)', textAlign: 'left',
3080
3316
  },
3081
3317
  },
3082
- React.createElement('div', { style: { fontWeight: 600, color: 'var(--dsw-alias-label-primary,currentColor)', marginBottom: 4 } }, '🛟 找回与重置密码指引:'),
3083
- React.createElement('div', null, '1. ', React.createElement('strong', null, '电脑本机直连修改'), ':直接在运行本程序的电脑本机打开本控制台(127.0.0.1 享有物理免锁特权),可随时修改管理密码。'),
3318
+ React.createElement('div', { style: { fontWeight: 600, color: 'var(--dsw-alias-label-primary,currentColor)', marginBottom: 4 } }, '🛟 找回与重置' + unlockPwdKind + '指引:'),
3319
+ React.createElement('div', null, '1. ', React.createElement('strong', null, '电脑本机直连修改'), ':直接在运行本程序的电脑本机打开本控制台(127.0.0.1 享有物理免锁特权),可随时修改或清除密码。'),
3084
3320
  React.createElement('div', { style: { marginTop: 4 } }, '2. ', React.createElement('strong', null, '服务器 / 无头环境'), ':救急重置步骤参见 GitHub README 的「三重容灾保命体系」章节。'),
3085
3321
  ),
3086
3322
  )
@@ -3114,7 +3350,7 @@ function BridgePanel({ rpcCall }) {
3114
3350
  setUnlockErr(err);
3115
3351
  setShowUnlockModal(true);
3116
3352
  },
3117
- }, '🔑 立即输入管理密码解锁'),
3353
+ }, '🔑 立即输入' + unlockPwdKind + '解锁'),
3118
3354
  ),
3119
3355
 
3120
3356
  // 管理员解锁状态提示条
@@ -3190,7 +3426,9 @@ function BridgePanel({ rpcCall }) {
3190
3426
  }, '✕'),
3191
3427
  ),
3192
3428
  React.createElement('div', { style: { fontSize: 13, color: 'var(--dsw-alias-label-secondary,#4b5563)', marginBottom: 16, lineHeight: 1.5 } },
3193
- '当前操作需要后台管理员权限。为保护您的网络配置与机器人平台安全,请输入管理密码解锁:'
3429
+ unlockUsesAdmin
3430
+ ? '当前操作需要后台管理员权限。为保护您的网络配置与机器人平台安全,请输入管理密码解锁:'
3431
+ : '当前操作需要后台管理权限。未设置独立管理密码,输入您的访问密码即可解锁:'
3194
3432
  ),
3195
3433
  React.createElement('form', {
3196
3434
  onSubmit: handleUnlockAdmin,
@@ -3199,7 +3437,7 @@ function BridgePanel({ rpcCall }) {
3199
3437
  React.createElement('input', {
3200
3438
  type: 'password',
3201
3439
  style: s.input,
3202
- placeholder: '请输入后台管理密码',
3440
+ placeholder: '输入' + unlockPwdKind,
3203
3441
  value: unlockPassword,
3204
3442
  onChange: (e) => setUnlockPassword(e.target.value),
3205
3443
  autoFocus: true,