dsh-remote-plugin 0.6.17 → 0.6.19
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/apk/dsh-remote.apk +0 -0
- package/gateway-stats.cjs +3 -2
- package/package.json +1 -1
- package/public/admin.html +16 -17
- package/public/admin.js +6 -5
- package/public/app.js +293 -75
- package/public/desktop/desktop.css +52 -7
- package/public/desktop/desktop.html +39 -32
- package/public/desktop/desktop.js +186 -48
- package/public/index.html +89 -48
- package/public/motion.js +105 -17
- package/public/plugin.html +6 -5
- package/public/styles.css +143 -43
- package/public/theme-vars.css +244 -167
- package/public/theme.js +2 -2
- package/public/update.json +12 -12
- package/public/version.json +1 -1
package/apk/dsh-remote.apk
CHANGED
|
Binary file
|
package/gateway-stats.cjs
CHANGED
|
@@ -186,10 +186,11 @@ function tokenKeyName(key) {
|
|
|
186
186
|
|
|
187
187
|
/** 统计存储: 单文件按天 + 游标。写操作由网关单进程调用, 内部用同步队列串行化。 */
|
|
188
188
|
class StatsStore {
|
|
189
|
-
constructor(dir) {
|
|
189
|
+
constructor(dir, options = {}) {
|
|
190
190
|
this.dir = dir || path.join(os.homedir(), '.dsh-remote', 'stats')
|
|
191
191
|
this.daysDir = path.join(this.dir, DAYS_DIR)
|
|
192
192
|
this.cursorsFile = path.join(this.dir, CURSORS_FILE)
|
|
193
|
+
this.spawn = typeof options.spawn === 'function' ? options.spawn : spawn
|
|
193
194
|
this.cursors = null
|
|
194
195
|
this.queue = Promise.resolve()
|
|
195
196
|
fs.mkdirSync(this.daysDir, { recursive: true })
|
|
@@ -313,7 +314,7 @@ class StatsStore {
|
|
|
313
314
|
if (lastSeq >= 0) this._setCursor(sessionId, lastSeq)
|
|
314
315
|
}
|
|
315
316
|
|
|
316
|
-
const zstd = spawn('zstd', ['-dc', file], { stdio: ['ignore', 'pipe', 'ignore'] })
|
|
317
|
+
const zstd = this.spawn('zstd', ['-dc', file], { windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'] })
|
|
317
318
|
const rl = readline.createInterface({ input: zstd.stdout })
|
|
318
319
|
|
|
319
320
|
zstd.on('error', (err) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-remote-plugin",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.19",
|
|
4
4
|
"description": "DSH Remote 官方 bundle 插件:DSH 左侧原生边栏入口 + 右侧抽屉内嵌管理控制台;内置网关随 DSH 自动启停(systemd 独立单元),抽屉直显令牌与设备监控;网关提供 /fs/* 文件传输端点(列表/断点下载/上传),配合 Android App 远程操控会话/审批/提问/goal 与文件互传(多服务器测速切换、聊天记录离线缓存)。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.mjs",
|
package/public/admin.html
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
6
|
-
<meta name="theme-color" content="#
|
|
6
|
+
<meta name="theme-color" content="#0D1117">
|
|
7
7
|
<title>DSH Remote · 管理</title>
|
|
8
|
-
<script>/* 首帧前应用皮肤 */ (function(){try{var t=localStorage.getItem('dshTheme');if(t!=='default'&&t!=='dark'&&t!=='light'&&t!=='neutral'){t=window.matchMedia('(prefers-color-scheme: light)').matches?'light':'default'}document.documentElement.setAttribute('data-theme',t)}catch(e){}})()</script>
|
|
8
|
+
<script>/* 首帧前应用皮肤 */ (function(){try{var t=localStorage.getItem('dshTheme');if(t!=='default'&&t!=='dark'&&t!=='light'&&t!=='neutral'&&t!=='mono'){t=window.matchMedia('(prefers-color-scheme: light)').matches?'light':'default'}document.documentElement.setAttribute('data-theme',t)}catch(e){}})()</script>
|
|
9
9
|
<link rel="stylesheet" href="../styles.css">
|
|
10
10
|
<style>
|
|
11
11
|
.admin-wrap { max-width: 1180px; margin: 0 auto; padding: calc(env(safe-area-inset-top, 0px) + 14px) 14px 40px; }
|
|
@@ -87,8 +87,8 @@
|
|
|
87
87
|
.device-key-toggle-actions { display:flex; align-items:center; gap:8px; flex:none; }
|
|
88
88
|
.device-key-switch { position:relative; width:42px; height:24px; flex:none; }
|
|
89
89
|
.device-key-switch input { position:absolute; opacity:0; pointer-events:none; }
|
|
90
|
-
.device-key-switch span { position:absolute; inset:0; border:1px solid var(--dsr-line); border-radius:999px; background:var(--dsr-bg-2); cursor:pointer; transition
|
|
91
|
-
.device-key-switch span::after { content:''; position:absolute; width:18px; height:18px; left:2px; top:2px; border-radius:50%; background:var(--dsr-muted); transition
|
|
90
|
+
.device-key-switch span { position:absolute; inset:0; border:1px solid var(--dsr-line); border-radius:999px; background:var(--dsr-bg-2); cursor:pointer; transition:background-color .18s ease,border-color .18s ease; }
|
|
91
|
+
.device-key-switch span::after { content:''; position:absolute; width:18px; height:18px; left:2px; top:2px; border-radius:50%; background:var(--dsr-muted); transition:transform .18s ease,background-color .18s ease; }
|
|
92
92
|
.device-key-switch input:checked + span { border-color:var(--dsr-accent-line); background:var(--dsr-accent-soft); }
|
|
93
93
|
.device-key-switch input:checked + span::after { transform:translateX(18px); background:var(--dsr-accent-strong); }
|
|
94
94
|
.device-key-switch input:focus-visible + span { outline:2px solid var(--dsr-accent-strong); outline-offset:2px; }
|
|
@@ -126,8 +126,8 @@
|
|
|
126
126
|
.host-ip-use { color: var(--dsr-muted); }
|
|
127
127
|
.host-ip-switch { position: relative; display: inline-block; width: 38px; height: 22px; vertical-align: middle; }
|
|
128
128
|
.host-ip-switch input { position: absolute; width: 1px; height: 1px; opacity: 0; }
|
|
129
|
-
.host-ip-switch span { position: absolute; inset: 0; border: 1px solid var(--dsr-line); border-radius: 999px; background: var(--dsr-bg-2); cursor: pointer; transition: .18s ease; }
|
|
130
|
-
.host-ip-switch span::after { content: ''; position: absolute; width: 16px; height: 16px; left: 2px; top: 2px; border-radius: 50%; background: var(--dsr-muted); transition: .18s ease; }
|
|
129
|
+
.host-ip-switch span { position: absolute; inset: 0; border: 1px solid var(--dsr-line); border-radius: 999px; background: var(--dsr-bg-2); cursor: pointer; transition: background-color .18s ease, border-color .18s ease; }
|
|
130
|
+
.host-ip-switch span::after { content: ''; position: absolute; width: 16px; height: 16px; left: 2px; top: 2px; border-radius: 50%; background: var(--dsr-muted); transition: transform .18s ease, background-color .18s ease; }
|
|
131
131
|
.host-ip-switch input:checked + span { border-color: var(--dsr-accent-line); background: var(--dsr-accent-soft); }
|
|
132
132
|
.host-ip-switch input:checked + span::after { transform: translateX(16px); background: var(--dsr-accent-strong); }
|
|
133
133
|
.host-ip-switch input:focus-visible + span { outline: 2px solid var(--dsr-accent-strong); outline-offset: 2px; }
|
|
@@ -186,7 +186,6 @@
|
|
|
186
186
|
width: 248px; background: var(--dsr-bg-2); border: 1px solid var(--dsr-line); border-radius: 14px;
|
|
187
187
|
box-shadow: 0 12px 34px var(--dsr-shadow); padding: 6px;
|
|
188
188
|
display: flex; flex-direction: column; gap: 2px;
|
|
189
|
-
animation: fb-in .18s ease;
|
|
190
189
|
}
|
|
191
190
|
.fb-item {
|
|
192
191
|
min-height: 44px; display: flex; align-items: center; gap: 10px;
|
|
@@ -204,7 +203,6 @@
|
|
|
204
203
|
.fb-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
|
|
205
204
|
.fb-name { font-weight: 600; }
|
|
206
205
|
.fb-desc { font-size: 11px; color: var(--dsr-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
207
|
-
@keyframes fb-in { from { opacity: 0; transform: translateY(-6px) } to { opacity: 1; transform: translateY(0) } }
|
|
208
206
|
/* 窄屏压缩顶栏: 图标化文字按钮, 保证全部按钮完整可见 */
|
|
209
207
|
@media (max-width: 900px) {
|
|
210
208
|
.gh-label, .t-label { display: none; }
|
|
@@ -331,8 +329,7 @@
|
|
|
331
329
|
@media (max-width: 900px) {
|
|
332
330
|
.admin-title { display: grid; grid-template-columns: minmax(0, 1fr) auto; row-gap: 10px; }
|
|
333
331
|
.admin-title .left { min-width: 0; }
|
|
334
|
-
.admin-title .right { grid-column: 1 / -1; justify-content: flex-start; min-width: 0;
|
|
335
|
-
.admin-title .right::-webkit-scrollbar { display: none; }
|
|
332
|
+
.admin-title .right { grid-column: 1 / -1; justify-content: flex-start; min-width: 0; flex-wrap: wrap; overflow: visible; }
|
|
336
333
|
}
|
|
337
334
|
@media (max-width: 440px) {
|
|
338
335
|
.admin-title h1 { display: block; min-width: 0; overflow: visible; font-size: 18px; }
|
|
@@ -381,12 +378,12 @@
|
|
|
381
378
|
</div>
|
|
382
379
|
</div>
|
|
383
380
|
|
|
384
|
-
<div id="login-view" class="login-card hidden">
|
|
385
|
-
<input id="token-input" type="password" placeholder="输入网关访问令牌" autocomplete="off" data-i18n-placeholder="tokenPlaceholder">
|
|
381
|
+
<div id="login-view" class="login-card hidden" data-motion-view>
|
|
382
|
+
<input id="token-input" name="gateway-token" type="password" placeholder="输入网关访问令牌" autocomplete="off" aria-label="网关访问令牌" data-i18n-placeholder="tokenPlaceholder" data-i18n-aria="tokenPlaceholder">
|
|
386
383
|
<button id="btn-login" class="mini-btn" data-i18n="enter">进入</button>
|
|
387
384
|
</div>
|
|
388
385
|
|
|
389
|
-
<div id="main-view" class="hidden">
|
|
386
|
+
<div id="main-view" class="hidden" data-motion-view>
|
|
390
387
|
<section id="admin-hero" class="admin-hero" aria-live="polite">
|
|
391
388
|
<div class="admin-hero-copy">
|
|
392
389
|
<div class="admin-eyebrow" data-i18n="hero.eyebrow">GATEWAY CONTROL</div>
|
|
@@ -455,8 +452,8 @@
|
|
|
455
452
|
</div>
|
|
456
453
|
|
|
457
454
|
<div id="gateway-port-row" class="gateway-port-row hidden">
|
|
458
|
-
<label data-i18n="gatewayPort">网关端口</label>
|
|
459
|
-
<input id="gateway-port-input" type="number" min="1" max="65535" inputmode="numeric" placeholder="8787">
|
|
455
|
+
<label for="gateway-port-input" data-i18n="gatewayPort">网关端口</label>
|
|
456
|
+
<input id="gateway-port-input" name="gateway-port" type="number" min="1" max="65535" inputmode="numeric" autocomplete="off" placeholder="8787">
|
|
460
457
|
<span id="gateway-port-current" class="muted">—</span>
|
|
461
458
|
<button id="btn-save-port" class="mini-btn" data-i18n="savePort">保存</button>
|
|
462
459
|
</div>
|
|
@@ -560,7 +557,7 @@
|
|
|
560
557
|
'doctor.realtime': '实时消息通道', 'doctor.realtimePass': 'mux 与 host 通道均已连接', 'doctor.realtimeFail': '实时通道未全部建立;可先使用轮询,检查 DSH 后会自动恢复',
|
|
561
558
|
'doctor.start': '启动', 'doctor.showQr': '显示二维码', 'doctor.copyAddress': '复制地址', 'doctor.copyCommand': '复制命令',
|
|
562
559
|
'doctor.reportCopied': '诊断报告已复制', 'doctor.commandCopied': '防火墙命令已复制,请确认网络范围后手动执行', 'doctor.addressCopied': '服务器地址已复制',
|
|
563
|
-
'theme.default': '默认深空', 'theme.dark': '落日', 'theme.light': '易北爱乐厅', 'theme.neutral': '草原孤塔',
|
|
560
|
+
'theme.default': '默认深空', 'theme.dark': '落日', 'theme.light': '易北爱乐厅', 'theme.neutral': '草原孤塔', 'theme.mono': '黑曜白',
|
|
564
561
|
'theme.panelTitle': '选择配色', 'theme.close': '关闭',
|
|
565
562
|
'repo': '仓库',
|
|
566
563
|
'donateTitle': '赞赏支持', 'donateThanks': '感谢你的支持 ☕',
|
|
@@ -670,7 +667,7 @@
|
|
|
670
667
|
'doctor.realtime': 'Realtime channels', 'doctor.realtimePass': 'Both mux and host channels are connected', 'doctor.realtimeFail': 'Realtime channels are incomplete. Polling remains available and recovery is automatic.',
|
|
671
668
|
'doctor.start': 'Start', 'doctor.showQr': 'Show QR', 'doctor.copyAddress': 'Copy address', 'doctor.copyCommand': 'Copy command',
|
|
672
669
|
'doctor.reportCopied': 'Diagnostic report copied', 'doctor.commandCopied': 'Firewall command copied. Review the network scope before running it.', 'doctor.addressCopied': 'Server address copied',
|
|
673
|
-
'theme.default': 'Default', 'theme.dark': 'Sunset', 'theme.light': 'Elbphilharmonie', 'theme.neutral': 'Prairie Tower',
|
|
670
|
+
'theme.default': 'Default', 'theme.dark': 'Sunset', 'theme.light': 'Elbphilharmonie', 'theme.neutral': 'Prairie Tower', 'theme.mono': 'Monochrome',
|
|
674
671
|
'theme.panelTitle': 'Choose theme', 'theme.close': 'Close',
|
|
675
672
|
'repo': 'Repo',
|
|
676
673
|
'donateTitle': 'Support', 'donateThanks': 'Thanks for your support ☕',
|
|
@@ -767,6 +764,8 @@
|
|
|
767
764
|
<script src="../qrcode.min.js"></script>
|
|
768
765
|
<script src="../i18n.js"></script>
|
|
769
766
|
<script src="../theme.js"></script>
|
|
767
|
+
<script src="../vendor/gsap/gsap.min.js"></script>
|
|
768
|
+
<script src="../motion.js"></script>
|
|
770
769
|
<script src="../admin.js"></script>
|
|
771
770
|
</body>
|
|
772
771
|
</html>
|
package/public/admin.js
CHANGED
|
@@ -935,10 +935,11 @@ function renderLangBtn() {
|
|
|
935
935
|
}
|
|
936
936
|
|
|
937
937
|
const THEME_META = [
|
|
938
|
-
{ id: 'default', sw: ['#
|
|
939
|
-
{ id: 'dark', sw: ['#
|
|
940
|
-
{ id: 'light', sw: ['#
|
|
941
|
-
{ id: 'neutral', sw: ['#
|
|
938
|
+
{ id: 'default', sw: ['#0D1117', '#21262D', '#58A6FF'] },
|
|
939
|
+
{ id: 'dark', sw: ['#161316', '#302323', '#FFB86B'] },
|
|
940
|
+
{ id: 'light', sw: ['#F5F7F8', '#FFFFFF', '#176B87'] },
|
|
941
|
+
{ id: 'neutral', sw: ['#EEF1E8', '#FAFBF6', '#47643C'] },
|
|
942
|
+
{ id: 'mono', sw: ['#050505', '#333333', '#F5F5F5'] }
|
|
942
943
|
]
|
|
943
944
|
|
|
944
945
|
function renderThemeBtn() {
|
|
@@ -957,7 +958,7 @@ function renderThemeOptions() {
|
|
|
957
958
|
if (!box) return
|
|
958
959
|
const cur = window.DSHTheme.get()
|
|
959
960
|
box.innerHTML = THEME_META.map(m => `
|
|
960
|
-
<button class="theme-option ${m.id === cur ? 'current' : ''}" data-theme="${m.id}" title="${t('theme.' + m.id)}">
|
|
961
|
+
<button type="button" class="theme-option ${m.id === cur ? 'current' : ''}" data-theme="${m.id}" aria-pressed="${m.id === cur}" title="${t('theme.' + m.id)}">
|
|
961
962
|
<span class="theme-swatches">${m.sw.map(c => `<i style="background:${c}"></i>`).join('')}</span>
|
|
962
963
|
<span class="theme-name">${t('theme.' + m.id)}</span>
|
|
963
964
|
<span class="theme-check">${m.id === cur ? '✓' : ''}</span>
|