dsh-remote-plugin 0.6.7 → 0.6.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +31 -25
- package/README.md +30 -24
- package/apk/dsh-remote.apk +0 -0
- package/client.js +11 -9
- package/gateway-stats.cjs +26 -11
- package/gateway.cjs +494 -87
- package/index.mjs +12 -2
- package/package.json +1 -1
- package/public/admin.html +103 -7
- package/public/admin.js +24 -0
- package/public/announcements.json +12 -0
- package/public/app.js +592 -91
- package/public/desktop/desktop.css +126 -19
- package/public/desktop/desktop.html +63 -14
- package/public/desktop/desktop.js +295 -77
- package/public/index.html +145 -41
- package/public/plugin.html +151 -0
- package/public/plugin.js +179 -0
- package/public/styles.css +176 -15
- package/public/theme-vars.css +9 -0
- package/public/update.json +12 -4
- package/public/version.json +1 -1
package/public/plugin.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/* DSH Remote 插件端快速状态面板 · 零依赖 */
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
const $ = (id) => document.getElementById(id)
|
|
5
|
+
const API = '/remote/admin/api'
|
|
6
|
+
let latest = null
|
|
7
|
+
let busy = false
|
|
8
|
+
let usageBusy = false
|
|
9
|
+
let usageLoadedAt = 0
|
|
10
|
+
|
|
11
|
+
function text(id, value) { const el = $(id); if (el) el.textContent = value == null ? '—' : String(value) }
|
|
12
|
+
function escapeText(value) { return String(value ?? '').replace(/[<>&"]/g, c => ({ '<': '<', '>': '>', '&': '&', '"': '"' }[c])) }
|
|
13
|
+
function openConsole() { window.open('/remote/admin/', '_blank', 'noopener') }
|
|
14
|
+
function closePanel() { window.parent.postMessage({ source: 'dsh-remote-plugin', type: 'close' }, location.origin) }
|
|
15
|
+
function fmtTokens(value) {
|
|
16
|
+
const n = Number(value) || 0
|
|
17
|
+
if (n >= 1e6) return (n / 1e6).toFixed(n >= 1e7 ? 0 : 1) + 'M'
|
|
18
|
+
if (n >= 1e3) return (n / 1e3).toFixed(n >= 1e5 ? 0 : 1) + 'K'
|
|
19
|
+
return String(Math.round(n))
|
|
20
|
+
}
|
|
21
|
+
function fmtCost(value) { return '¥' + (Number(value) || 0).toFixed(2) }
|
|
22
|
+
function renderUsage(days) {
|
|
23
|
+
const section = $('plugin-usage')
|
|
24
|
+
const empty = $('plugin-usage-empty')
|
|
25
|
+
if (!section) return
|
|
26
|
+
if (!Array.isArray(days) || !days.length) {
|
|
27
|
+
section.classList.add('empty')
|
|
28
|
+
empty?.classList.remove('hidden')
|
|
29
|
+
text('plugin-usage-date', '—')
|
|
30
|
+
text('plugin-token-total', '—')
|
|
31
|
+
text('plugin-peak-share', '0%')
|
|
32
|
+
text('plugin-cost', '—')
|
|
33
|
+
text('plugin-cost-split', '等待网关统计')
|
|
34
|
+
if ($('plugin-token-track')) $('plugin-token-track').innerHTML = ''
|
|
35
|
+
$('plugin-usage-ring')?.style.setProperty('--plugin-peak', '0%')
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
const today = days[days.length - 1] || {}
|
|
39
|
+
const total = today.total || {}
|
|
40
|
+
const input = Number(total.input) || 0
|
|
41
|
+
const cache = (Number(total.cacheRead) || 0) + (Number(total.cacheWrite) || 0)
|
|
42
|
+
const output = Number(total.output) || 0
|
|
43
|
+
const totalTokens = input + cache + output
|
|
44
|
+
const peakCost = Number(today.peak?.cost) || 0
|
|
45
|
+
const offCost = Number(today.off?.cost) || 0
|
|
46
|
+
const totalCost = peakCost + offCost
|
|
47
|
+
const peakShare = totalCost > 0 ? Math.round(peakCost / totalCost * 100) : 0
|
|
48
|
+
const share = (n) => totalTokens ? Math.max(0, n / totalTokens * 100) : 0
|
|
49
|
+
section.classList.remove('empty')
|
|
50
|
+
empty?.classList.add('hidden')
|
|
51
|
+
text('plugin-usage-date', today.date || '—')
|
|
52
|
+
text('plugin-token-total', fmtTokens(totalTokens))
|
|
53
|
+
text('plugin-peak-share', peakShare + '%')
|
|
54
|
+
text('plugin-cost', fmtCost(totalCost))
|
|
55
|
+
text('plugin-cost-split', `高峰 ${fmtCost(peakCost)} · 空闲 ${fmtCost(offCost)}`)
|
|
56
|
+
const track = $('plugin-token-track')
|
|
57
|
+
if (track) track.innerHTML = `<i class="input" style="width:${share(input)}%"></i><i class="cache" style="width:${share(cache)}%"></i><i class="output" style="width:${share(output)}%"></i>`
|
|
58
|
+
$('plugin-usage-ring')?.style.setProperty('--plugin-peak', peakShare + '%')
|
|
59
|
+
}
|
|
60
|
+
async function loadUsage() {
|
|
61
|
+
if (usageBusy) return
|
|
62
|
+
usageBusy = true
|
|
63
|
+
try {
|
|
64
|
+
const res = await fetch(`${API}/stats/summary?days=7`, { headers: { 'x-dsh-remote-client': 'plugin' } })
|
|
65
|
+
if (!res.ok) throw new Error('HTTP ' + res.status)
|
|
66
|
+
const out = await res.json()
|
|
67
|
+
renderUsage(out.days || [])
|
|
68
|
+
} catch {
|
|
69
|
+
renderUsage([])
|
|
70
|
+
} finally {
|
|
71
|
+
usageLoadedAt = Date.now()
|
|
72
|
+
usageBusy = false
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function setBusy(value) {
|
|
77
|
+
busy = value
|
|
78
|
+
$('plugin-primary').disabled = value
|
|
79
|
+
$('plugin-toggle').disabled = value
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function render(st) {
|
|
83
|
+
latest = st
|
|
84
|
+
const gateway = st.mode === 'gateway'
|
|
85
|
+
const healthy = gateway && st.upstream?.reachable !== false
|
|
86
|
+
const installed = st.gatewayInstalled !== false
|
|
87
|
+
const hero = $('plugin-hero')
|
|
88
|
+
hero.classList.toggle('ok', healthy)
|
|
89
|
+
hero.classList.toggle('warn', gateway && !healthy)
|
|
90
|
+
hero.classList.toggle('off', !gateway)
|
|
91
|
+
|
|
92
|
+
text('plugin-status', healthy ? '系统运行正常' : gateway ? '网关需要关注' : installed ? '网关待启动' : 'DSH 已连接')
|
|
93
|
+
text('plugin-status-desc', healthy
|
|
94
|
+
? `${st.onlineCount || 0} 台设备在线 · 最近请求 ${st.totalRequests || 0} 次`
|
|
95
|
+
: gateway ? 'DSH 上游暂时不可达,请打开控制台诊断' : installed ? '本地网关尚未运行,启动后即可远程连接' : '插件已连接 DSH,但未检测到网关程序')
|
|
96
|
+
text('plugin-status-meta', healthy ? '本地服务可用' : gateway ? '需要查看诊断' : installed ? '本地服务未启动' : '仅 DSH 内嵌模式')
|
|
97
|
+
|
|
98
|
+
const primary = $('plugin-primary')
|
|
99
|
+
primary.textContent = healthy ? '打开控制台' : installed ? '启动网关' : '查看控制台'
|
|
100
|
+
primary.dataset.action = healthy ? 'console' : installed ? 'start' : 'console'
|
|
101
|
+
text('plugin-toggle-label', gateway ? '停止网关' : '启动网关')
|
|
102
|
+
$('plugin-toggle').classList.toggle('hidden', !installed)
|
|
103
|
+
|
|
104
|
+
text('plugin-version', st.version ? 'v' + st.version : '—')
|
|
105
|
+
text('plugin-gateway-version', gateway ? (st.version ? 'v' + st.version : '运行中') : '未运行')
|
|
106
|
+
text('plugin-devices', `${st.onlineCount || 0} / ${st.deviceCount || 0}`)
|
|
107
|
+
text('plugin-host', (st.lanIPs || []).find(x => x && x !== '127.0.0.1') || st.host || '127.0.0.1')
|
|
108
|
+
if (!gateway) {
|
|
109
|
+
usageLoadedAt = Date.now()
|
|
110
|
+
renderUsage([])
|
|
111
|
+
} else if (Date.now() - usageLoadedAt > 30000) {
|
|
112
|
+
loadUsage()
|
|
113
|
+
}
|
|
114
|
+
$('plugin-error').classList.add('hidden')
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function load() {
|
|
118
|
+
try {
|
|
119
|
+
const res = await fetch(`${API}/state`, { headers: { 'x-dsh-remote-client': 'plugin' } })
|
|
120
|
+
if (!res.ok) throw new Error('HTTP ' + res.status)
|
|
121
|
+
render(await res.json())
|
|
122
|
+
} catch (e) {
|
|
123
|
+
$('plugin-hero').classList.remove('ok', 'warn')
|
|
124
|
+
$('plugin-hero').classList.add('off')
|
|
125
|
+
text('plugin-status', '无法读取状态')
|
|
126
|
+
text('plugin-status-desc', 'DSH Remote 插件暂时无法访问本地服务')
|
|
127
|
+
text('plugin-status-meta', '请稍后重试')
|
|
128
|
+
$('plugin-error').textContent = '状态读取失败:' + escapeText(e.message || e)
|
|
129
|
+
$('plugin-error').classList.remove('hidden')
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function toggleGateway() {
|
|
134
|
+
if (busy) return
|
|
135
|
+
setBusy(true)
|
|
136
|
+
const action = latest?.mode === 'gateway' ? 'stop' : 'start'
|
|
137
|
+
try {
|
|
138
|
+
const res = await fetch(`${API}/gateway`, {
|
|
139
|
+
method: 'POST',
|
|
140
|
+
headers: { 'content-type': 'application/json', 'x-dsh-remote-client': 'plugin' },
|
|
141
|
+
body: JSON.stringify({ action }),
|
|
142
|
+
})
|
|
143
|
+
if (!res.ok) throw new Error('HTTP ' + res.status)
|
|
144
|
+
await new Promise(resolve => setTimeout(resolve, 650))
|
|
145
|
+
await load()
|
|
146
|
+
} catch (e) {
|
|
147
|
+
$('plugin-error').textContent = '网关操作失败:' + (e.message || e)
|
|
148
|
+
$('plugin-error').classList.remove('hidden')
|
|
149
|
+
} finally { setBusy(false) }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function copyToken() {
|
|
153
|
+
const token = latest?.token || ''
|
|
154
|
+
if (!token) return
|
|
155
|
+
try {
|
|
156
|
+
await navigator.clipboard.writeText(token)
|
|
157
|
+
const button = $('plugin-copy')
|
|
158
|
+
const old = button.lastChild
|
|
159
|
+
if (old) old.textContent = '已复制'
|
|
160
|
+
setTimeout(() => { if (old) old.textContent = '复制令牌' }, 1400)
|
|
161
|
+
} catch {
|
|
162
|
+
$('plugin-error').textContent = '复制失败,请从控制台手动复制'
|
|
163
|
+
$('plugin-error').classList.remove('hidden')
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
$('plugin-close').addEventListener('click', closePanel)
|
|
168
|
+
$('plugin-console').addEventListener('click', openConsole)
|
|
169
|
+
$('plugin-toggle').addEventListener('click', toggleGateway)
|
|
170
|
+
$('plugin-copy').addEventListener('click', copyToken)
|
|
171
|
+
$('plugin-primary').addEventListener('click', () => {
|
|
172
|
+
if ($('plugin-primary').dataset.action === 'start') toggleGateway()
|
|
173
|
+
else openConsole()
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
load()
|
|
177
|
+
loadUsage()
|
|
178
|
+
setInterval(load, 5000)
|
|
179
|
+
setInterval(loadUsage, 30000)
|
package/public/styles.css
CHANGED
|
@@ -20,7 +20,7 @@ html, body {
|
|
|
20
20
|
overscroll-behavior-y: none;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
body { padding-bottom: calc(
|
|
23
|
+
body { padding-bottom: calc(92px + var(--dsr-safe-b)); }
|
|
24
24
|
|
|
25
25
|
.hidden { display: none !important; }
|
|
26
26
|
.muted { color: var(--dsr-muted); font-size: 12px; }
|
|
@@ -55,9 +55,10 @@ button {
|
|
|
55
55
|
}
|
|
56
56
|
.icon-btn {
|
|
57
57
|
width: 34px; height: 34px; border-radius: 10px;
|
|
58
|
-
background: var(--dsr-panel); border: 1px solid var(--dsr-line);
|
|
58
|
+
background: var(--dsr-panel); border: 1px solid var(--dsr-line); color: var(--dsr-accent-strong);
|
|
59
59
|
font-size: 17px; display: grid; place-items: center;
|
|
60
60
|
}
|
|
61
|
+
.icon-btn svg { width: 19px; height: 19px; display: block; }
|
|
61
62
|
.icon-btn:active { background: var(--dsr-panel-2); }
|
|
62
63
|
/* 顶栏按钮统一: 反馈/刷新/连接徽章同高同基线 */
|
|
63
64
|
.topbar-btn {
|
|
@@ -66,6 +67,9 @@ button {
|
|
|
66
67
|
line-height: 1; vertical-align: middle; flex-shrink: 0;
|
|
67
68
|
}
|
|
68
69
|
.topbar-right .topbar-btn.icon-btn { width: 44px; padding: 0; font-size: 19px; border-radius: 12px; }
|
|
70
|
+
.topbar-right #btn-donate { color: var(--dsr-accent-2); }
|
|
71
|
+
.topbar-right #btn-feedback { color: var(--dsr-info); }
|
|
72
|
+
.topbar-right #btn-refresh { color: var(--dsr-success); }
|
|
69
73
|
.topbar-right .topbar-btn.conn-badge { padding: 0 14px; border-radius: 999px; font-size: 12.5px; }
|
|
70
74
|
.mini-btn {
|
|
71
75
|
padding: 5px 11px; border-radius: 9px; font-size: 13px;
|
|
@@ -199,6 +203,9 @@ a.mini-btn { text-decoration: none; display: inline-flex; align-items: center; }
|
|
|
199
203
|
.session-swipe { position: relative; overflow: hidden; border-radius: var(--dsr-radius); background: var(--dsr-error); touch-action: pan-y; }
|
|
200
204
|
.session-swipe .session-card { position: relative; z-index: 1; transform: translateX(var(--swipe-x, 0)); transition: transform .18s ease; }
|
|
201
205
|
.session-swipe.revealed .session-card { transform: translateX(-92px); }
|
|
206
|
+
.session-swipe .wb-session { position: relative; z-index: 1; transform: translateX(var(--swipe-x, 0)); transition: transform .18s ease; }
|
|
207
|
+
.session-swipe.revealed .wb-session { transform: translateX(-92px); }
|
|
208
|
+
.wb-sessions .session-swipe { border-radius: 8px; }
|
|
202
209
|
.sc-archive-btn { position: absolute; inset: 0 0 0 auto; z-index: 0; width: 92px; display: grid; place-items: center; padding: 0; background: var(--dsr-error); color: #fff; font-size: 13px; font-weight: 700; }
|
|
203
210
|
.sc-archive-btn:active { filter: brightness(.9); }
|
|
204
211
|
.empty { text-align: center; color: var(--dsr-muted); padding: 34px 0; }
|
|
@@ -236,8 +243,9 @@ body.in-session .main {
|
|
|
236
243
|
display: flex;
|
|
237
244
|
flex-direction: column;
|
|
238
245
|
overflow: hidden;
|
|
239
|
-
padding: 12px 12px
|
|
246
|
+
padding: 12px 12px 0;
|
|
240
247
|
}
|
|
248
|
+
body.in-session .bottom-nav { display: none; }
|
|
241
249
|
body.in-session .view {
|
|
242
250
|
flex: 1;
|
|
243
251
|
min-height: 0;
|
|
@@ -273,6 +281,8 @@ body.in-session .view {
|
|
|
273
281
|
}
|
|
274
282
|
.session-head .session-title { flex-shrink: 1; }
|
|
275
283
|
.session-head-meta { flex: 1; min-width: 0; }
|
|
284
|
+
.session-send-btn { flex: 0 0 auto; min-height: 38px; padding: 0 13px; border-radius: 12px; border: 1px solid var(--dsr-accent-line); background: var(--dsr-accent-soft); color: var(--dsr-accent-strong); font: inherit; font-size: 13px; font-weight: 700; }
|
|
285
|
+
.session-send-btn:active { filter: brightness(.92); }
|
|
276
286
|
.session-title {
|
|
277
287
|
font-size: 16px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
278
288
|
}
|
|
@@ -314,6 +324,8 @@ body.in-session .view {
|
|
|
314
324
|
padding-right: 16px;
|
|
315
325
|
scrollbar-width: none;
|
|
316
326
|
}
|
|
327
|
+
body.in-session .history { padding-bottom: calc(76px + var(--dsr-safe-b)); }
|
|
328
|
+
body.in-session.has-composer-images .history { padding-bottom: calc(146px + var(--dsr-safe-b)); }
|
|
317
329
|
.history::-webkit-scrollbar { display: none; }
|
|
318
330
|
.history-more { text-align: center; margin: 4px 0; }
|
|
319
331
|
|
|
@@ -363,6 +375,16 @@ body.in-session .view {
|
|
|
363
375
|
padding: 5px 12px; border-radius: 999px; max-width: 92%;
|
|
364
376
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
365
377
|
}
|
|
378
|
+
.event.event-detail {
|
|
379
|
+
align-self: stretch; max-width: 96%; white-space: normal; overflow: visible;
|
|
380
|
+
border-radius: 12px; padding: 8px 10px; line-height: 1.45;
|
|
381
|
+
}
|
|
382
|
+
.event.event-detail summary { white-space: normal; overflow-wrap: anywhere; cursor: pointer; }
|
|
383
|
+
.event.event-detail pre {
|
|
384
|
+
margin: 8px 0 0; max-height: 280px; overflow: auto; white-space: pre-wrap;
|
|
385
|
+
overflow-wrap: anywhere; word-break: break-word; font: 12px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
386
|
+
background: var(--dsr-code-bg); border-radius: 8px; padding: 9px 10px;
|
|
387
|
+
}
|
|
366
388
|
.tool {
|
|
367
389
|
align-self: flex-start; max-width: 96%; background: var(--dsr-panel);
|
|
368
390
|
border: 1px solid var(--dsr-line); border-left: 3px solid var(--dsr-accent-2);
|
|
@@ -391,10 +413,26 @@ body.in-session .view {
|
|
|
391
413
|
background: var(--dsr-composer-bg); backdrop-filter: blur(10px);
|
|
392
414
|
border-top: 1px solid var(--dsr-line);
|
|
393
415
|
}
|
|
416
|
+
.composer-attachments {
|
|
417
|
+
display: flex; gap: 8px; overflow-x: auto; padding: 8px 12px 0;
|
|
418
|
+
scrollbar-width: none;
|
|
419
|
+
}
|
|
420
|
+
.composer-attachments::-webkit-scrollbar { display: none; }
|
|
421
|
+
.composer-attachment { position: relative; flex: 0 0 58px; width: 58px; height: 58px; border-radius: 11px; overflow: hidden; border: 1px solid var(--dsr-line); background: var(--dsr-panel); }
|
|
422
|
+
.composer-attachment img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
|
423
|
+
.composer-attachment-remove { position: absolute; top: 3px; right: 3px; width: 19px; height: 19px; border-radius: 50%; border: 1px solid rgba(0,0,0,.18); background: rgba(0,0,0,.62); color: #fff; font-size: 14px; line-height: 16px; padding: 0; }
|
|
424
|
+
.composer-image-btn { flex: 0 0 42px; width: 42px; height: 42px; display: grid; place-items: center; border-radius: 14px; color: var(--dsr-info); background: var(--dsr-panel); border: 1px solid var(--dsr-line); }
|
|
425
|
+
.composer-image-btn svg { width: 20px; height: 20px; }
|
|
426
|
+
.composer-image-btn:active, .composer-image-btn.active { color: var(--dsr-accent-strong); background: var(--dsr-accent-soft); border-color: var(--dsr-accent-line); }
|
|
427
|
+
.composer-image-menu { position: absolute; right: 12px; bottom: 58px; z-index: 4; display: flex; gap: 7px; padding: 7px; border: 1px solid var(--dsr-line); border-radius: 14px; background: var(--dsr-bg-2); box-shadow: 0 12px 30px var(--dsr-shadow); }
|
|
428
|
+
.composer-image-option { display: inline-flex; align-items: center; gap: 5px; min-height: 34px; padding: 6px 10px; border: 1px solid var(--dsr-line); border-radius: 10px; background: var(--dsr-panel); color: var(--dsr-text); font: inherit; font-size: 12px; }
|
|
429
|
+
.composer-image-option svg { width: 16px; height: 16px; color: var(--dsr-info); }
|
|
394
430
|
.composer {
|
|
395
431
|
display: flex; gap: 8px; align-items: flex-end;
|
|
396
432
|
padding: 8px 12px calc(8px + var(--dsr-safe-b));
|
|
397
433
|
}
|
|
434
|
+
.composer-input-wrap { flex: 1; min-width: 0; position: relative; display: flex; align-items: flex-end; }
|
|
435
|
+
.composer-input-wrap textarea { width: 100%; min-width: 0; }
|
|
398
436
|
.composer textarea {
|
|
399
437
|
flex: 1; resize: none; background: var(--dsr-panel); color: var(--dsr-text);
|
|
400
438
|
border: 1px solid var(--dsr-line); border-radius: 16px;
|
|
@@ -402,6 +440,23 @@ body.in-session .view {
|
|
|
402
440
|
max-height: 120px; outline: none;
|
|
403
441
|
}
|
|
404
442
|
.composer textarea:focus { border-color: var(--dsr-accent-line); }
|
|
443
|
+
.composer-fs-btn { position: absolute; top: 5px; right: 6px; z-index: 2; width: 27px; height: 27px; display: grid; place-items: center; border-radius: 8px; background: var(--dsr-bg-2); border: 1px solid var(--dsr-line); color: var(--dsr-muted); }
|
|
444
|
+
.composer-fs-btn:active { color: var(--dsr-accent-strong); border-color: var(--dsr-accent-line); }
|
|
445
|
+
.composer-fs-btn svg { width: 15px; height: 15px; }
|
|
446
|
+
.composer-input-wrap.has-fs-btn textarea { padding-right: 38px; }
|
|
447
|
+
.composer-fs-handle { display: none; }
|
|
448
|
+
.composer-wrap.fs { position: fixed; top: calc(54px + var(--dsr-safe-top)); left: 0; right: 0; bottom: 0; max-width: none; height: auto; z-index: 35; display: flex; flex-direction: column; background: var(--dsr-composer-bg); }
|
|
449
|
+
.composer-wrap.fs .composer-menu { display: none; }
|
|
450
|
+
.composer-wrap.fs .composer-fs-handle { display: flex; align-items: center; justify-content: center; flex: none; height: 28px; touch-action: pan-y; }
|
|
451
|
+
.composer-fs-handle span { width: 38px; height: 4px; border-radius: 999px; background: var(--dsr-line); }
|
|
452
|
+
.composer-wrap.fs .composer { flex: 1; align-items: stretch; padding-bottom: calc(8px + var(--dsr-safe-b)); }
|
|
453
|
+
.composer-wrap.fs .composer-input-wrap { align-items: stretch; }
|
|
454
|
+
.composer-wrap.fs .composer-input-wrap textarea { height: 100%; max-height: none; resize: none; }
|
|
455
|
+
.composer-wrap.fs .composer-fs-btn { top: 7px; right: 7px; width: 30px; height: 30px; }
|
|
456
|
+
.composer-wrap.fs .plus-btn { display: none; }
|
|
457
|
+
.composer-wrap.fs .composer-image-btn, .composer-wrap.fs .composer-image-menu, .composer-wrap.fs .send-btn { display: none; }
|
|
458
|
+
.composer-wrap.fs .composer-image-menu { bottom: 58px; }
|
|
459
|
+
body.composer-fullscreen { overflow: hidden; }
|
|
405
460
|
.plus-btn {
|
|
406
461
|
flex-shrink: 0; width: 42px; height: 42px; border-radius: 14px;
|
|
407
462
|
background: var(--dsr-panel); color: var(--dsr-info); border: 1px solid var(--dsr-line);
|
|
@@ -439,7 +494,65 @@ body.in-session .view {
|
|
|
439
494
|
}
|
|
440
495
|
.model-chip.current { color: var(--dsr-info); border-color: var(--dsr-info-line); background: var(--dsr-info-soft); }
|
|
441
496
|
.model-provider { font-size: 11px; color: var(--dsr-muted); margin: 7px 0 5px; }
|
|
442
|
-
body.in-session .main { padding-bottom:
|
|
497
|
+
body.in-session .main { padding-bottom: 0; }
|
|
498
|
+
|
|
499
|
+
/* ---------- 系统总览 · A 方案 ---------- */
|
|
500
|
+
.overview-head { display:flex; align-items:flex-start; justify-content:space-between; gap:12px; margin:2px 0 16px; }
|
|
501
|
+
.overview-eyebrow { color:var(--dsr-accent-strong); font-size:10px; font-weight:800; letter-spacing:1.7px; line-height:1.3; }
|
|
502
|
+
.overview-title { margin:3px 0 2px; font-size:25px; line-height:1.18; letter-spacing:-.4px; }
|
|
503
|
+
.overview-subtitle { margin:0; color:var(--dsr-muted); font-size:12px; }
|
|
504
|
+
.overview-refresh { width:34px; height:34px; padding:0; border-radius:11px; font-size:18px; line-height:1; }
|
|
505
|
+
.overview-refresh { color: var(--dsr-accent-strong); }
|
|
506
|
+
.overview-refresh svg { width:17px; height:17px; display:block; }
|
|
507
|
+
.overview-pulse-card { position:relative; overflow:hidden; display:grid; grid-template-columns:minmax(0,1fr) auto; gap:8px 12px; align-items:center; padding:14px; margin-bottom:14px; border:1px solid var(--dsr-accent-line); border-radius:16px; background:linear-gradient(135deg,var(--dsr-panel),var(--dsr-bg-2)); box-shadow:0 10px 26px var(--dsr-shadow); }
|
|
508
|
+
.overview-pulse-card::after { content:""; position:absolute; width:110px; height:110px; right:-45px; top:-55px; border-radius:50%; background:var(--dsr-accent-soft); filter:blur(3px); pointer-events:none; }
|
|
509
|
+
.overview-pulse-copy { min-width:0; align-self:start; }
|
|
510
|
+
.overview-status { margin-top:6px; font-size:20px; font-weight:780; letter-spacing:-.3px; }
|
|
511
|
+
.overview-status-desc { margin-top:4px; color:var(--dsr-muted); font-size:11px; }
|
|
512
|
+
.overview-primary-action { display:inline-flex; align-items:center; justify-content:center; min-height:34px; margin-top:13px; padding:7px 13px; border:1px solid var(--dsr-accent-strong); border-radius:10px; background:var(--dsr-accent-strong); color:var(--dsr-on-accent); font:inherit; font-size:12px; font-weight:750; cursor:pointer; transition:transform .18s ease, filter .18s ease; }
|
|
513
|
+
.overview-primary-action:hover, .overview-primary-action:focus-visible { filter:brightness(1.08); transform:translateY(-1px); }
|
|
514
|
+
.overview-primary-action:disabled { opacity:.65; cursor:wait; transform:none; }
|
|
515
|
+
.overview-pulse-card.status-degraded { border-color:var(--dsr-warning-line); }
|
|
516
|
+
.overview-pulse-card.status-degraded .overview-primary-action { border-color:var(--dsr-warning); background:var(--dsr-warning); color:var(--dsr-on-warning); }
|
|
517
|
+
.overview-pulse-card.status-offline { border-color:var(--dsr-error-line); }
|
|
518
|
+
.overview-pulse-card.status-offline .overview-primary-action { border-color:var(--dsr-error); background:var(--dsr-error); color:var(--dsr-neutral-9); }
|
|
519
|
+
.system-pulse-ring { --pulse-pct:0%; position:relative; z-index:1; width:92px; height:92px; padding:7px; border-radius:50%; background:conic-gradient(var(--dsr-accent) 0 var(--pulse-pct),var(--dsr-line) var(--pulse-pct) 100%); box-shadow:0 0 20px var(--dsr-glow); }
|
|
520
|
+
.system-pulse-ring::before { content:""; position:absolute; inset:6px; border-radius:50%; border:1px solid var(--dsr-accent-line); opacity:.7; }
|
|
521
|
+
.system-pulse-core { position:relative; display:flex; flex-direction:column; align-items:center; justify-content:center; width:100%; height:100%; border-radius:50%; background:var(--dsr-bg-2); color:var(--dsr-muted); }
|
|
522
|
+
.system-pulse-core strong { max-width:100%; color:var(--dsr-text); font-size:14px; line-height:1.05; letter-spacing:.2px; text-align:center; white-space:nowrap; }
|
|
523
|
+
.system-pulse-core span { max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; margin-top:4px; font-size:8px; text-align:center; }
|
|
524
|
+
.overview-links { grid-column:1 / -1; display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:6px; padding-top:8px; border-top:1px solid var(--dsr-line); }
|
|
525
|
+
.overview-link { min-width:0; display:grid; grid-template-columns:auto 1fr; align-items:center; gap:4px 6px; padding-top:9px; color:var(--dsr-muted); font-size:10px; }
|
|
526
|
+
.overview-link i { width:6px; height:6px; border-radius:50%; background:var(--dsr-muted); box-shadow:0 0 0 3px var(--dsr-line); }
|
|
527
|
+
.overview-link b { grid-column:2; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--dsr-text); font-size:10px; font-weight:650; }
|
|
528
|
+
.overview-link.ok i { background:var(--dsr-success); box-shadow:0 0 0 3px var(--dsr-success-soft); }
|
|
529
|
+
.overview-link.warn i { background:var(--dsr-warning); box-shadow:0 0 0 3px var(--dsr-warning-soft); }
|
|
530
|
+
.overview-link.off i { background:var(--dsr-error); box-shadow:0 0 0 3px var(--dsr-error-soft); }
|
|
531
|
+
.overview-section-head { display:flex; align-items:center; justify-content:space-between; gap:10px; margin:17px 1px 8px; font-size:12px; font-weight:750; letter-spacing:.3px; }
|
|
532
|
+
.overview-section-meta { color:var(--dsr-muted); font-size:11px; font-weight:500; }
|
|
533
|
+
.overview-attention-list, .overview-session-list { display:flex; flex-direction:column; gap:7px; }
|
|
534
|
+
.overview-attention-item, .overview-session-item { width:100%; display:flex; align-items:center; gap:10px; min-width:0; padding:11px 12px; border:1px solid var(--dsr-line); border-radius:13px; background:var(--dsr-panel); color:var(--dsr-text); text-align:left; }
|
|
535
|
+
button.overview-attention-item, button.overview-session-item { cursor:pointer; }
|
|
536
|
+
.overview-attention-item { border-left:3px solid var(--dsr-warning); }
|
|
537
|
+
.overview-attention-item.question { border-left-color:var(--dsr-accent-2); }
|
|
538
|
+
.overview-item-mark { flex:0 0 auto; width:25px; height:25px; display:grid; place-items:center; border-radius:8px; background:var(--dsr-warning-soft); color:var(--dsr-warning); font-size:12px; }
|
|
539
|
+
.overview-attention-item.question .overview-item-mark { background:var(--dsr-accent-2-soft); color:var(--dsr-accent-2); }
|
|
540
|
+
.overview-item-copy { min-width:0; flex:1; }
|
|
541
|
+
.overview-item-title { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:12.5px; font-weight:650; }
|
|
542
|
+
.overview-item-desc { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; margin-top:2px; color:var(--dsr-muted); font-size:11px; }
|
|
543
|
+
.overview-item-arrow { color:var(--dsr-muted); font-size:18px; }
|
|
544
|
+
.overview-item-actions { flex:0 0 auto; display:flex; gap:4px; }
|
|
545
|
+
.overview-item-actions .mini-btn { padding:4px 7px; font-size:10px; }
|
|
546
|
+
.overview-metric-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:7px; }
|
|
547
|
+
.overview-metric { min-width:0; padding:11px 12px; border:1px solid var(--dsr-line); border-radius:13px; background:var(--dsr-panel); }
|
|
548
|
+
.overview-metric span { display:block; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--dsr-muted); font-size:10.5px; }
|
|
549
|
+
.overview-metric strong { display:block; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; margin-top:5px; color:var(--dsr-text); font-size:15px; font-weight:700; }
|
|
550
|
+
.overview-session-item { cursor:pointer; }
|
|
551
|
+
.overview-session-item .overview-item-mark { background:var(--dsr-info-soft); color:var(--dsr-info); }
|
|
552
|
+
.overview-session-item.running .overview-item-mark { background:var(--dsr-success-soft); color:var(--dsr-success); }
|
|
553
|
+
.overview-session-item .overview-item-desc { font-variant-numeric:tabular-nums; }
|
|
554
|
+
.overview-empty { padding:10px 12px; border:1px dashed var(--dsr-line); border-radius:13px; color:var(--dsr-muted); font-size:12px; text-align:center; }
|
|
555
|
+
.overview-legacy { display:none; }
|
|
443
556
|
|
|
444
557
|
/* ---------- 待办 ---------- */
|
|
445
558
|
.pending-list, .jobs-list { display: flex; flex-direction: column; gap: 8px; }
|
|
@@ -489,7 +602,8 @@ body.in-session .main { padding-bottom: 84px; }
|
|
|
489
602
|
padding: 11px 13px; min-width: 0;
|
|
490
603
|
}
|
|
491
604
|
.fs-row:active { background: var(--dsr-panel-2); }
|
|
492
|
-
.fs-ico { flex-shrink: 0;
|
|
605
|
+
.fs-ico { flex-shrink: 0; width: 22px; height: 22px; display:grid; place-items:center; color:var(--dsr-info); }
|
|
606
|
+
.fs-ico svg { width:20px; height:20px; display:block; }
|
|
493
607
|
.fs-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; }
|
|
494
608
|
.fs-name {
|
|
495
609
|
font-size: 14.5px; font-weight: 600; white-space: nowrap;
|
|
@@ -586,18 +700,41 @@ body.in-session .main { padding-bottom: 84px; }
|
|
|
586
700
|
/* ---------- 底部导航 ---------- */
|
|
587
701
|
.bottom-nav {
|
|
588
702
|
position: fixed; left: 0; right: 0; bottom: 0; z-index: 25;
|
|
589
|
-
display: flex;
|
|
590
|
-
|
|
591
|
-
padding-bottom: var(--dsr-safe-b);
|
|
703
|
+
display: flex; align-items: flex-end; min-height: 88px; gap: 3px; isolation: isolate;
|
|
704
|
+
padding: 0 8px var(--dsr-safe-b); background: transparent; border-top: none;
|
|
592
705
|
}
|
|
593
|
-
.nav-
|
|
594
|
-
|
|
595
|
-
|
|
706
|
+
.nav-wave { position: absolute; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; filter: drop-shadow(0 -8px 20px var(--dsr-shadow)); }
|
|
707
|
+
.nav-wave svg { width: 100%; height: 100%; display: block; overflow: visible; }
|
|
708
|
+
.nav-wave-fill { fill: var(--dsr-nav-bg); }
|
|
709
|
+
.nav-wave-line { fill: none; stroke: var(--dsr-accent-line); stroke-width: .55; vector-effect: non-scaling-stroke; }
|
|
710
|
+
.nav-wave::after { content: ''; position: absolute; left: 10%; right: 10%; top: 28%; height: 1px; background: linear-gradient(90deg, transparent, var(--dsr-accent-2-line), var(--dsr-accent-line), transparent); opacity: .45;
|
|
596
711
|
}
|
|
597
|
-
.nav-btn
|
|
598
|
-
|
|
712
|
+
.nav-btn {
|
|
713
|
+
flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 4px;
|
|
714
|
+
min-width: 0; min-height: 69px; padding: 7px 2px 5px; color: var(--dsr-nav-muted); font-size: 11px; position: relative;
|
|
715
|
+
border-radius: 14px 14px 0 0; touch-action: manipulation;
|
|
716
|
+
}
|
|
717
|
+
.nav-btn .nav-ico { width: 22px; height: 22px; display: grid; place-items: center; line-height: 1; transition: transform .18s ease, color .18s ease; }
|
|
718
|
+
.nav-btn .nav-ico svg { width: 21px; height: 21px; display: block; }
|
|
719
|
+
.nav-btn:nth-of-type(1) { --nav-tint: var(--dsr-info); }
|
|
720
|
+
.nav-btn:nth-of-type(2) { --nav-tint: var(--dsr-success); }
|
|
721
|
+
.nav-btn:nth-of-type(4) { --nav-tint: var(--dsr-warning); }
|
|
722
|
+
.nav-btn:nth-of-type(5) { --nav-tint: var(--dsr-accent-2); }
|
|
723
|
+
.nav-btn:not(.nav-home) .nav-ico { color: var(--nav-tint, var(--dsr-nav-muted)); opacity: .82; }
|
|
724
|
+
.nav-btn:not(.nav-home) > span:last-of-type { color: var(--dsr-nav-muted); }
|
|
725
|
+
.nav-btn.active:not(.nav-home) { color: var(--dsr-nav-active); }
|
|
726
|
+
.nav-btn.active:not(.nav-home) .nav-ico { transform: translateY(-1px); opacity: 1; }
|
|
727
|
+
.nav-btn.active:not(.nav-home)::after { content: ''; position: absolute; bottom: 3px; width: 4px; height: 4px; border-radius: 50%; background: var(--dsr-nav-active); box-shadow: 0 0 10px var(--dsr-glow); }
|
|
728
|
+
.nav-home { z-index: 2; transform: translateY(-13px); color: var(--dsr-nav-muted); }
|
|
729
|
+
.nav-home-orb { width: 56px; height: 56px; display: grid; place-items: center; border-radius: 50%; background: var(--dsr-bg-2); border: 1px solid var(--dsr-accent-line); box-shadow: 0 5px 18px var(--dsr-shadow); transition: transform .18s ease, background .18s ease, box-shadow .18s ease; }
|
|
730
|
+
.nav-home .nav-ico { width: 30px; height: 30px; color: var(--dsr-accent-strong); }
|
|
731
|
+
.nav-home .nav-ico svg { width: 29px; height: 29px; }
|
|
732
|
+
.nav-home.active { color: var(--dsr-nav-active); }
|
|
733
|
+
.nav-home.active .nav-home-orb { background: radial-gradient(circle at 34% 27%, var(--dsr-info), var(--dsr-accent) 42%, var(--dsr-accent-2)); border-color: var(--dsr-accent); color: var(--dsr-on-accent); box-shadow: 0 7px 24px var(--dsr-glow), inset 0 0 0 1px rgba(255,255,255,.32); }
|
|
734
|
+
.nav-home.active .nav-ico { color: var(--dsr-on-accent); transform: none; }
|
|
735
|
+
.nav-home:active .nav-home-orb { transform: scale(.96); }
|
|
599
736
|
.nav-badge {
|
|
600
|
-
position: absolute; top:
|
|
737
|
+
position: absolute; top: 1px; left: calc(50% + 19px); z-index: 3;
|
|
601
738
|
background: var(--dsr-nav-badge-bg); color: var(--dsr-nav-badge-text); border-radius: 999px;
|
|
602
739
|
font-size: 10px; font-weight: 800; padding: 0 6px; line-height: 16px;
|
|
603
740
|
}
|
|
@@ -623,7 +760,7 @@ body.in-session .main { padding-bottom: 84px; }
|
|
|
623
760
|
padding: 9px 10px; border-radius: 13px; border: none; background: transparent; color: var(--dsr-text);
|
|
624
761
|
font: inherit; text-align: left; cursor: pointer; text-decoration: none;
|
|
625
762
|
}
|
|
626
|
-
.sheet-item:hover, .sheet-item:focus-visible { background: var(--dsr-bg);
|
|
763
|
+
.sheet-item:hover, .sheet-item:focus-visible { background: var(--dsr-bg); }
|
|
627
764
|
.sheet-item.primary { background: var(--dsr-accent-soft); border: 1px solid var(--dsr-accent-line); }
|
|
628
765
|
.sheet-item.primary:hover { background: var(--dsr-accent-soft); filter: brightness(1.03); }
|
|
629
766
|
.sheet-ico {
|
|
@@ -692,6 +829,13 @@ body.in-session .main { padding-bottom: 84px; }
|
|
|
692
829
|
.announcement-content { line-height: 1.7; overflow-wrap: anywhere; word-break: break-word; }
|
|
693
830
|
.announcement-action { display: inline-block; margin-top: 12px; color: var(--dsr-accent-strong); font-size: 13px; font-weight: 600; text-decoration: none; }
|
|
694
831
|
.announcement-action:hover { text-decoration: underline; }
|
|
832
|
+
.announcement-history-card { max-width: 520px; }
|
|
833
|
+
.announcement-history-list { display: flex; flex-direction: column; gap: 8px; }
|
|
834
|
+
.announcement-history-item { border: 1px solid var(--dsr-line); border-radius: 12px; background: var(--dsr-panel); padding: 10px 12px; }
|
|
835
|
+
.announcement-history-item summary { cursor: pointer; display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-weight: 700; overflow-wrap: anywhere; }
|
|
836
|
+
.announcement-history-item summary small { flex: 0 0 auto; color: var(--dsr-muted); font-size: 11px; font-weight: 400; }
|
|
837
|
+
.announcement-history-item .announcement-history-content { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--dsr-line); line-height: 1.65; overflow-wrap: anywhere; word-break: break-word; }
|
|
838
|
+
.announcement-history-item .announcement-action { margin-top: 8px; }
|
|
695
839
|
.kv { display: flex; justify-content: space-between; gap: 10px; font-size: 13px; padding: 3px 0; }
|
|
696
840
|
.kv .k { color: var(--dsr-muted); } .kv .v { word-break: break-all; text-align: right; }
|
|
697
841
|
|
|
@@ -744,7 +888,24 @@ body.in-session .main { padding-bottom: 84px; }
|
|
|
744
888
|
.toast.err { border-color: var(--dsr-error-line); color: var(--dsr-error); }
|
|
745
889
|
.toast.ok { border-color: var(--dsr-success-line); color: var(--dsr-success); }
|
|
746
890
|
|
|
891
|
+
/* 键盘焦点:保证深浅皮肤和移动端外接键盘都有清晰反馈 */
|
|
892
|
+
button:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible {
|
|
893
|
+
outline: 2px solid var(--dsr-accent);
|
|
894
|
+
outline-offset: 2px;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
@media (prefers-reduced-motion: reduce) {
|
|
898
|
+
*, *::before, *::after {
|
|
899
|
+
animation-duration: .01ms !important;
|
|
900
|
+
animation-iteration-count: 1 !important;
|
|
901
|
+
transition-duration: .01ms !important;
|
|
902
|
+
scroll-behavior: auto !important;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
|
|
747
906
|
@media (min-width: 560px) {
|
|
748
907
|
.stat-strip { grid-template-columns: repeat(3, 1fr); }
|
|
749
908
|
.msg { max-width: 78%; }
|
|
909
|
+
.overview-pulse-card { grid-template-columns:1fr auto; }
|
|
910
|
+
.overview-metric-grid { grid-template-columns:repeat(4,minmax(0,1fr)); }
|
|
750
911
|
}
|
package/public/theme-vars.css
CHANGED
|
@@ -9,6 +9,15 @@
|
|
|
9
9
|
--dsr-safe-b: max(env(safe-area-inset-bottom, 0px), var(--native-bottom, 0px));
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
html,
|
|
13
|
+
html[data-theme="default"],
|
|
14
|
+
html[data-theme="dark"] { color-scheme: dark; }
|
|
15
|
+
html[data-theme="light"],
|
|
16
|
+
html[data-theme="neutral"] { color-scheme: light; }
|
|
17
|
+
@media (prefers-color-scheme: light) {
|
|
18
|
+
html:not([data-theme]) { color-scheme: light; }
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
/* ============ 深空「默认」: 经典深蓝紫(保留原配色) ============ */
|
|
13
22
|
:root,
|
|
14
23
|
html[data-theme="default"] {
|
package/public/update.json
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.6.
|
|
2
|
+
"version": "0.6.9",
|
|
3
3
|
"apkUrl": "dsh-remote.apk",
|
|
4
|
-
"sha256": "
|
|
5
|
-
"releasedAt": "2026-08-
|
|
6
|
-
"notes": "
|
|
4
|
+
"sha256": "b8d869a6a6840261bc511005e4c86e3a2853df15f29ee33406e5c9e1f21f5796",
|
|
5
|
+
"releasedAt": "2026-08-21T19:36:51.824Z",
|
|
6
|
+
"notes": "0.6.9 正式版:全面优化网络连接稳定性,重构网关实时链路与连接管理;修复 downlink-only 心跳断联、网关重启风暴和 WebSocket 状态竞态;新增 VPN 友好 Ping/Pong、集中 mux/host collector、短时 WebSocket ticket、CORS 收紧与统计旁路优化。",
|
|
7
7
|
"history": [
|
|
8
|
+
{
|
|
9
|
+
"version": "0.6.9",
|
|
10
|
+
"notes": "0.6.9 正式版:全面优化网络连接稳定性,重构网关实时链路与连接管理;修复 downlink-only 心跳断联、网关重启风暴和 WebSocket 状态竞态;新增 VPN 友好 Ping/Pong、集中 mux/host collector、短时 WebSocket ticket、CORS 收紧与统计旁路优化。"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"version": "0.6.8",
|
|
14
|
+
"notes": "0.6.8 正式版:重构手机端、桌面端和插件管理界面;新增工作台、图片附件、历史公告和全屏输入;修复会话布局、系统提示裁切、主题图标对比度与全屏输入高度问题。"
|
|
15
|
+
},
|
|
8
16
|
{
|
|
9
17
|
"version": "0.6.7",
|
|
10
18
|
"notes": "修复手机端左滑归档松手后按钮不固定;新增云端公告弹窗,支持按 App 版本和有效期定向发布;新增工作台绑定与项目级会话;新增手机端左滑归档和二次确认;新增手机回车行为设置;新增归档会话折叠和 /permission 参数选择。"
|
package/public/version.json
CHANGED