dsh-remote-plugin 0.6.19 → 0.6.21
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/client.js +9 -2
- package/gateway.cjs +610 -33
- package/index.mjs +79 -5
- package/package.json +1 -1
- package/public/app.js +129 -2
- package/public/desktop/desktop.css +42 -6
- package/public/desktop/desktop.html +21 -1
- package/public/desktop/desktop.js +123 -2
- package/public/index.html +24 -3
- package/public/styles.css +50 -4
- package/public/update.json +12 -12
- package/public/version.json +1 -1
|
@@ -840,10 +840,79 @@ function renderGroupSelect() {
|
|
|
840
840
|
function toggleGroupMenu() { $('group-select-menu').classList.toggle('hidden') }
|
|
841
841
|
function closeGroupMenu() { $('group-select-menu').classList.add('hidden') }
|
|
842
842
|
|
|
843
|
+
let desktopGroupDrawerReturnFocus = null
|
|
844
|
+
|
|
845
|
+
function desktopGroupDrawerOpen() {
|
|
846
|
+
return !$('ds-group-drawer')?.classList.contains('hidden')
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function renderDesktopGroupDrawer() {
|
|
850
|
+
const trigger = $('ds-group-drawer-trigger')
|
|
851
|
+
const brandGroup = $('ds-brand-group-name')
|
|
852
|
+
const current = $('ds-group-drawer-current')
|
|
853
|
+
const list = $('ds-group-drawer-list')
|
|
854
|
+
if (brandGroup) brandGroup.textContent = state.activeGroup
|
|
855
|
+
if (trigger) trigger.setAttribute('aria-label', t('ds.groupsDrawerOpen') + ':' + state.activeGroup)
|
|
856
|
+
if (current) current.textContent = state.activeGroup
|
|
857
|
+
if (!list) return
|
|
858
|
+
list.innerHTML = state.groups.map(group => {
|
|
859
|
+
const servers = groupServers(group)
|
|
860
|
+
const selected = group === state.activeGroup
|
|
861
|
+
return `<button type="button" class="ds-group-drawer-option ${selected ? 'current' : ''}" data-ds-quick-group="${esc(group)}" ${selected ? 'aria-current="true"' : ''}>
|
|
862
|
+
<span class="ds-group-drawer-mark" aria-hidden="true"></span>
|
|
863
|
+
<span class="ds-group-drawer-copy"><span class="ds-group-drawer-name">${esc(group)}</span><span class="ds-group-drawer-meta">${t('ds.groupsServerCount', { count: servers.length })}</span></span>
|
|
864
|
+
<span class="ds-group-drawer-check" aria-hidden="true">${selected ? '✓' : ''}</span>
|
|
865
|
+
</button>`
|
|
866
|
+
}).join('')
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
function openDesktopGroupDrawer() {
|
|
870
|
+
const drawer = $('ds-group-drawer')
|
|
871
|
+
const backdrop = $('ds-group-drawer-backdrop')
|
|
872
|
+
const trigger = $('ds-group-drawer-trigger')
|
|
873
|
+
if (!drawer || !backdrop || !trigger) return
|
|
874
|
+
renderDesktopGroupDrawer()
|
|
875
|
+
desktopGroupDrawerReturnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : trigger
|
|
876
|
+
drawer.classList.remove('hidden')
|
|
877
|
+
backdrop.classList.remove('hidden')
|
|
878
|
+
trigger.setAttribute('aria-expanded', 'true')
|
|
879
|
+
requestAnimationFrame(() => (drawer.querySelector('[data-ds-quick-group].current') || drawer.querySelector('[data-ds-quick-group]') || drawer).focus({ preventScroll: true }))
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
function closeDesktopGroupDrawer({ restoreFocus = true } = {}) {
|
|
883
|
+
const drawer = $('ds-group-drawer')
|
|
884
|
+
const backdrop = $('ds-group-drawer-backdrop')
|
|
885
|
+
const trigger = $('ds-group-drawer-trigger')
|
|
886
|
+
if (!drawer || !backdrop || drawer.classList.contains('hidden')) return
|
|
887
|
+
drawer.classList.add('hidden')
|
|
888
|
+
backdrop.classList.add('hidden')
|
|
889
|
+
trigger?.setAttribute('aria-expanded', 'false')
|
|
890
|
+
if (restoreFocus) (desktopGroupDrawerReturnFocus || trigger)?.focus({ preventScroll: true })
|
|
891
|
+
desktopGroupDrawerReturnFocus = null
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
function keepDesktopGroupDrawerFocus(event) {
|
|
895
|
+
if (!desktopGroupDrawerOpen()) return
|
|
896
|
+
if (event.key === 'Escape') {
|
|
897
|
+
event.preventDefault()
|
|
898
|
+
closeDesktopGroupDrawer()
|
|
899
|
+
return
|
|
900
|
+
}
|
|
901
|
+
if (event.key !== 'Tab') return
|
|
902
|
+
const drawer = $('ds-group-drawer')
|
|
903
|
+
const targets = [...drawer.querySelectorAll('button:not([disabled]), [href], [tabindex]:not([tabindex="-1"])')]
|
|
904
|
+
if (!targets.length) return
|
|
905
|
+
const first = targets[0]
|
|
906
|
+
const last = targets[targets.length - 1]
|
|
907
|
+
if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus() }
|
|
908
|
+
else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus() }
|
|
909
|
+
}
|
|
910
|
+
|
|
843
911
|
function renderServers() {
|
|
844
912
|
const box = $('server-list')
|
|
845
913
|
if (!box) return
|
|
846
914
|
renderGroupSelect()
|
|
915
|
+
renderDesktopGroupDrawer()
|
|
847
916
|
box.innerHTML = state.groups.map(g => {
|
|
848
917
|
const list = groupServers(g)
|
|
849
918
|
const auto = state.autoSelect[g] !== false
|
|
@@ -1566,6 +1635,7 @@ async function openSession(id) {
|
|
|
1566
1635
|
renderSessions()
|
|
1567
1636
|
renderSessionCards()
|
|
1568
1637
|
renderQueue()
|
|
1638
|
+
renderSessionPendingDesktop()
|
|
1569
1639
|
updateComposerStatus()
|
|
1570
1640
|
await loadHistory()
|
|
1571
1641
|
}
|
|
@@ -1576,6 +1646,7 @@ function closeSession() {
|
|
|
1576
1646
|
const cards = $('session-cards')
|
|
1577
1647
|
if (cards) cards.innerHTML = ''
|
|
1578
1648
|
renderQueue()
|
|
1649
|
+
renderSessionPendingDesktop()
|
|
1579
1650
|
updateComposerStatus()
|
|
1580
1651
|
updateSessionActions()
|
|
1581
1652
|
showView('view-sessions')
|
|
@@ -2010,6 +2081,39 @@ function serverLabel() {
|
|
|
2010
2081
|
const cur = currentServerEntry()
|
|
2011
2082
|
return cur ? (cur.note || cur.url) : (state.server || location.host)
|
|
2012
2083
|
}
|
|
2084
|
+
function desktopApprovalDetail(a) {
|
|
2085
|
+
const lines = []
|
|
2086
|
+
if (a?.reason) lines.push(String(a.reason))
|
|
2087
|
+
if (a?.arguments !== undefined) lines.push(safeJson(a.arguments))
|
|
2088
|
+
if (a?.callId) lines.push(`callId: ${a.callId}`)
|
|
2089
|
+
return lines.join('\n') || t('ds.approvalReason', { reason: '' })
|
|
2090
|
+
}
|
|
2091
|
+
function renderSessionPendingDesktop() {
|
|
2092
|
+
const box = $('session-pending')
|
|
2093
|
+
if (!box) return
|
|
2094
|
+
const sessionId = state.current
|
|
2095
|
+
const items = [
|
|
2096
|
+
...state.approvals.filter(a => a.sessionId === sessionId).map(a => ({ kind: 'approval', item: a })),
|
|
2097
|
+
...state.questions.filter(q => q.sessionId === sessionId).map(q => ({ kind: 'question', item: q }))
|
|
2098
|
+
]
|
|
2099
|
+
box.classList.toggle('hidden', !sessionId || !items.length)
|
|
2100
|
+
if (!sessionId || !items.length) { box.innerHTML = ''; return }
|
|
2101
|
+
box.innerHTML = `<div class="ds-session-pending-head"><span>${esc(t('ds.attention'))}</span><span>${items.length}</span></div><div class="ds-session-pending-list">${items.map(({ kind, item }) => {
|
|
2102
|
+
if (kind === 'approval') return `<article class="ds-notif-card" data-session-approval="${esc(item.approvalId)}">
|
|
2103
|
+
<div class="ds-notif-head">🔐 ${t('ds.approvalTitle')}</div>
|
|
2104
|
+
<div class="ds-notif-title">${esc(item.toolName || t('ds.toolDefault'))}</div>
|
|
2105
|
+
<div class="ds-notif-body">${esc(desktopApprovalDetail(item))}</div>
|
|
2106
|
+
<div class="ds-notif-actions"><button type="button" class="ds-btn allow" data-session-approve="1">${t('ds.allow')}</button><button type="button" class="ds-btn reject" data-session-approve="0">${t('ds.reject')}</button></div>
|
|
2107
|
+
</article>`
|
|
2108
|
+
return `<article class="ds-notif-card question" data-session-question="${esc(item.rpcId)}">
|
|
2109
|
+
<div class="ds-notif-head">❓ ${t('ds.questionNotify')}</div>
|
|
2110
|
+
<div class="ds-notif-title">${esc((item.questions || []).map(question => question.question).filter(Boolean).join(' / '))}</div>
|
|
2111
|
+
<div class="ds-notif-actions"><button type="button" class="ds-btn" data-session-answer>${t('ds.submit')}</button></div>
|
|
2112
|
+
</article>`
|
|
2113
|
+
}).join('')}</div>`
|
|
2114
|
+
box.querySelectorAll('[data-session-approve]').forEach(button => button.addEventListener('click', () => approveApproval(button.closest('[data-session-approval]')?.dataset.sessionApproval || '', button.dataset.sessionApprove === '1')))
|
|
2115
|
+
box.querySelectorAll('[data-session-answer]').forEach(button => button.addEventListener('click', () => openQuestionModal(state.questions.find(q => q.rpcId === button.closest('[data-session-question]')?.dataset.sessionQuestion))))
|
|
2116
|
+
}
|
|
2013
2117
|
function renderNotifStack() {
|
|
2014
2118
|
const stack = $('notif-stack')
|
|
2015
2119
|
const items = [
|
|
@@ -2019,7 +2123,7 @@ function renderNotifStack() {
|
|
|
2019
2123
|
stack.innerHTML = items.map(it => {
|
|
2020
2124
|
if (it.kind === 'approval') {
|
|
2021
2125
|
const a = it.a
|
|
2022
|
-
const reason =
|
|
2126
|
+
const reason = desktopApprovalDetail(a)
|
|
2023
2127
|
return `<div class="ds-notif-card" data-approval="${esc(a.approvalId)}" tabindex="0">
|
|
2024
2128
|
<div class="ds-notif-head">🔐 ${t('ds.approvalTitle')} · ${esc(serverLabel())} · ${fmtTime(a.time || Date.now())}</div>
|
|
2025
2129
|
<div class="ds-notif-title">${esc(a.toolName || t('ds.toolDefault'))}</div>
|
|
@@ -2050,6 +2154,7 @@ function renderNotifStack() {
|
|
|
2050
2154
|
stack.querySelectorAll('.ds-notif-card').forEach(card => card.addEventListener('keydown', (e) => {
|
|
2051
2155
|
if (e.key === 'Escape') toast(t('ds.ignored'), 'ok')
|
|
2052
2156
|
}))
|
|
2157
|
+
renderSessionPendingDesktop()
|
|
2053
2158
|
renderOverviewDesktop()
|
|
2054
2159
|
}
|
|
2055
2160
|
async function approveApproval(id, allow) {
|
|
@@ -2708,7 +2813,7 @@ function renderOverviewDesktop() {
|
|
|
2708
2813
|
$('ds-overview-attention-list').innerHTML = pending.length ? pending.slice(0, 4).map(({ kind, item }) => {
|
|
2709
2814
|
const title = titleOf(state.byId.get(item.sessionId))
|
|
2710
2815
|
if (kind === 'approval') return `<div class="ds-overview-attention-item" data-ds-overview-approval="${esc(item.approvalId)}">
|
|
2711
|
-
<span class="ds-overview-mark">⌁</span><span class="ds-overview-copy"><span class="ds-overview-item-title">${esc(item.toolName || t('ds.toolDefault'))}</span><span class="ds-overview-item-desc">${esc(item
|
|
2816
|
+
<span class="ds-overview-mark">⌁</span><span class="ds-overview-copy"><span class="ds-overview-item-title">${esc(item.toolName || t('ds.toolDefault'))}</span><span class="ds-overview-item-desc">${esc(desktopApprovalDetail(item))} · ${esc(title)}</span></span>
|
|
2712
2817
|
<span class="ds-overview-actions"><button class="ds-btn allow" data-ds-overview-approve="1">${t('ds.allow')}</button><button class="ds-btn reject" data-ds-overview-approve="0">${t('ds.reject')}</button></span>
|
|
2713
2818
|
</div>`
|
|
2714
2819
|
return `<button type="button" class="ds-overview-attention-item question" data-ds-overview-question="${esc(item.rpcId)}">
|
|
@@ -3041,6 +3146,22 @@ function bindUi() {
|
|
|
3041
3146
|
$('btn-server-add').addEventListener('click', addServer)
|
|
3042
3147
|
$('btn-group-add').addEventListener('click', addGroup)
|
|
3043
3148
|
$('group-select-btn').addEventListener('click', (e) => { e.stopPropagation(); toggleGroupMenu() })
|
|
3149
|
+
$('ds-group-drawer-trigger').addEventListener('click', () => { desktopGroupDrawerOpen() ? closeDesktopGroupDrawer() : openDesktopGroupDrawer() })
|
|
3150
|
+
$('ds-group-drawer-close').addEventListener('click', () => closeDesktopGroupDrawer())
|
|
3151
|
+
$('ds-group-drawer-backdrop').addEventListener('click', () => closeDesktopGroupDrawer())
|
|
3152
|
+
$('ds-group-drawer-list').addEventListener('click', (e) => {
|
|
3153
|
+
const option = e.target.closest('[data-ds-quick-group]')
|
|
3154
|
+
if (!option) return
|
|
3155
|
+
const group = option.dataset.dsQuickGroup
|
|
3156
|
+
closeDesktopGroupDrawer({ restoreFocus: false })
|
|
3157
|
+
if (group !== state.activeGroup) switchGroup(group)
|
|
3158
|
+
})
|
|
3159
|
+
$('ds-group-drawer-manage').addEventListener('click', () => {
|
|
3160
|
+
closeDesktopGroupDrawer({ restoreFocus: false })
|
|
3161
|
+
showView('view-settings')
|
|
3162
|
+
showSettingsPage('servers')
|
|
3163
|
+
})
|
|
3164
|
+
document.addEventListener('keydown', keepDesktopGroupDrawerFocus)
|
|
3044
3165
|
document.addEventListener('click', (e) => { if (!e.target.closest('#group-select')) closeGroupMenu() })
|
|
3045
3166
|
$('server-input').addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.isComposing) { e.preventDefault(); addServer() } })
|
|
3046
3167
|
|
package/public/index.html
CHANGED
|
@@ -15,10 +15,11 @@
|
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
17
17
|
<header class="topbar">
|
|
18
|
-
<
|
|
18
|
+
<button id="group-drawer-trigger" class="brand brand-trigger" type="button" aria-haspopup="dialog" aria-controls="group-drawer" aria-expanded="false" data-i18n-aria="groups.drawerOpen">
|
|
19
19
|
<span class="brand-dot"></span>
|
|
20
|
-
<span class="brand-name">DSH Remote</span>
|
|
21
|
-
|
|
20
|
+
<span class="brand-copy"><span class="brand-name">DSH Remote</span><span id="brand-group-name" class="brand-group-name">默认</span></span>
|
|
21
|
+
<svg class="brand-caret" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m7 10 5 5 5-5"/></svg>
|
|
22
|
+
</button>
|
|
22
23
|
<div class="topbar-right">
|
|
23
24
|
<button id="btn-donate" class="topbar-btn icon-btn" data-i18n-title="settings.donate" data-i18n-aria="settings.donate"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 8h11v6a4 4 0 0 1-4 4H10a4 4 0 0 1-4-4Z"/><path d="M17 10h1.5a2.5 2.5 0 0 1 0 5H17M9 4c0 1 1 1.2 1 2M13 4c0 1 1 1.2 1 2M5 20h13"/></svg></button>
|
|
24
25
|
<button id="btn-feedback" class="topbar-btn icon-btn" data-i18n-title="feedback.title" data-i18n-aria="feedback.title" aria-haspopup="menu" aria-expanded="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 11.5a7.5 7.5 0 0 1-7.5 7.5H7l-3 2 .8-3.1A7.5 7.5 0 1 1 20 11.5Z"/><path d="M8 11.5h.01M12 11.5h.01M16 11.5h.01"/></svg></button>
|
|
@@ -27,6 +28,20 @@
|
|
|
27
28
|
</div>
|
|
28
29
|
</header>
|
|
29
30
|
|
|
31
|
+
<button id="group-drawer-backdrop" class="group-drawer-backdrop hidden" type="button" tabindex="-1" aria-hidden="true"></button>
|
|
32
|
+
<aside id="group-drawer" class="group-drawer hidden" role="dialog" aria-modal="true" aria-labelledby="group-drawer-title" tabindex="-1">
|
|
33
|
+
<div class="group-drawer-head">
|
|
34
|
+
<div><div class="group-drawer-eyebrow" data-i18n="groups.drawerEyebrow">QUICK SWITCH</div><h2 id="group-drawer-title" data-i18n="groups.drawerTitle">切换服务器组</h2></div>
|
|
35
|
+
<button id="group-drawer-close" class="group-drawer-close" type="button" data-i18n-aria="groups.drawerClose" data-i18n-title="groups.drawerClose">×</button>
|
|
36
|
+
</div>
|
|
37
|
+
<div class="group-drawer-current"><span data-i18n="groups.drawerCurrent">当前连接组</span><strong id="group-drawer-current">默认</strong></div>
|
|
38
|
+
<div id="group-drawer-list" class="group-drawer-list" role="group" data-i18n-aria="groups.drawerTitle"></div>
|
|
39
|
+
<div class="group-drawer-foot">
|
|
40
|
+
<button id="group-drawer-manage" class="group-drawer-manage" type="button"><span data-i18n="groups.drawerManage">管理服务器与组</span><span aria-hidden="true">›</span></button>
|
|
41
|
+
<p data-i18n="groups.drawerManageDesc">编辑、测速和调整组</p>
|
|
42
|
+
</div>
|
|
43
|
+
</aside>
|
|
44
|
+
|
|
30
45
|
<main id="main" class="main">
|
|
31
46
|
<!-- 会话列表 -->
|
|
32
47
|
<section id="view-home" class="view">
|
|
@@ -104,6 +119,8 @@
|
|
|
104
119
|
|
|
105
120
|
<div id="queue-dock" class="queue-dock hidden" aria-live="polite"></div>
|
|
106
121
|
|
|
122
|
+
<section id="session-pending" class="session-pending hidden" aria-live="polite"></section>
|
|
123
|
+
|
|
107
124
|
<div class="section-head small">
|
|
108
125
|
<span data-i18n="session.history">对话</span>
|
|
109
126
|
<span id="history-hint" class="muted"></span>
|
|
@@ -956,6 +973,8 @@
|
|
|
956
973
|
'groups.added': '已创建组:{group}', 'groups.promptRename': '重命名组「{group}」:',
|
|
957
974
|
'groups.renamed': '组已重命名', 'groups.cannotDeleteDefault': '默认组不可删除',
|
|
958
975
|
'groups.confirmDelete': '删除组「{group}」?组内服务器将归入默认组。', 'groups.deleted': '组已删除',
|
|
976
|
+
'groups.drawerOpen': '切换服务器组', 'groups.drawerClose': '关闭组列表', 'groups.drawerEyebrow': '快速切换', 'groups.drawerTitle': '切换服务器组', 'groups.drawerCurrent': '当前连接组',
|
|
977
|
+
'groups.drawerManage': '管理服务器与组', 'groups.drawerManageDesc': '编辑、测速和调整组', 'groups.serverCount': '{count} 台服务器',
|
|
959
978
|
'stream.error': '事件流错误:{msg}',
|
|
960
979
|
'session.loading': '加载中…', 'session.unknown': '未知会话', 'session.running': '运行中', 'session.interrupted': '已中断',
|
|
961
980
|
'session.stop': '停止本轮', 'session.history': '对话', 'session.errorMsg': '会话出错:{msg}', 'session.recovering': '恢复会话中…', 'session.recoveryReady': '会话已恢复', 'session.recoveryFailed': '会话恢复失败', 'session.recoveryCached': '正在查看缓存历史',
|
|
@@ -1186,6 +1205,8 @@
|
|
|
1186
1205
|
'groups.added': 'Created group: {group}', 'groups.promptRename': 'Rename group "{group}":',
|
|
1187
1206
|
'groups.renamed': 'Group renamed', 'groups.cannotDeleteDefault': 'The default group cannot be deleted',
|
|
1188
1207
|
'groups.confirmDelete': 'Delete group "{group}"? Its servers move to the default group.', 'groups.deleted': 'Group deleted',
|
|
1208
|
+
'groups.drawerOpen': 'Switch server group', 'groups.drawerClose': 'Close group list', 'groups.drawerEyebrow': 'QUICK SWITCH', 'groups.drawerTitle': 'Switch server group', 'groups.drawerCurrent': 'Current connection group',
|
|
1209
|
+
'groups.drawerManage': 'Manage servers and groups', 'groups.drawerManageDesc': 'Edit, test, and organize groups', 'groups.serverCount': '{count} servers',
|
|
1189
1210
|
'stream.error': 'Event stream error: {msg}',
|
|
1190
1211
|
'session.loading': 'Loading…', 'session.unknown': 'Unknown session', 'session.running': 'Running', 'session.interrupted': 'Interrupted',
|
|
1191
1212
|
'session.stop': 'Stop turn', 'session.history': 'Conversation', 'session.errorMsg': 'Session error: {msg}', 'session.recovering': 'Restoring session…', 'session.recoveryReady': 'Session ready', 'session.recoveryFailed': 'Session restore failed', 'session.recoveryCached': 'Viewing cached history',
|
package/public/styles.css
CHANGED
|
@@ -40,7 +40,15 @@ body { padding-bottom: calc(92px + var(--dsr-safe-b)); }
|
|
|
40
40
|
background: var(--dsr-bg);
|
|
41
41
|
border-bottom: 1px solid var(--dsr-line);
|
|
42
42
|
}
|
|
43
|
-
.brand { display: flex; align-items: center; gap: 8px; font-weight: 700; letter-spacing: .5px; }
|
|
43
|
+
.brand { display: flex; align-items: center; gap: 8px; min-width: 0; font-weight: 700; letter-spacing: .5px; }
|
|
44
|
+
.brand-trigger { padding: 4px 5px 4px 2px; border-radius: 11px; text-align: left; color: var(--dsr-text); }
|
|
45
|
+
.brand-trigger:active { background: var(--dsr-accent-soft); }
|
|
46
|
+
.brand-trigger:focus-visible { outline: 2px solid var(--dsr-accent); outline-offset: 2px; }
|
|
47
|
+
.brand-copy { min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 0; line-height: 1.14; }
|
|
48
|
+
.brand-name { white-space: nowrap; }
|
|
49
|
+
.brand-group-name { max-width: 16ch; overflow: hidden; color: var(--dsr-muted); font-size: 10.5px; font-weight: 600; letter-spacing: .15px; text-overflow: ellipsis; white-space: nowrap; }
|
|
50
|
+
.brand-caret { width: 14px; height: 14px; flex: none; color: var(--dsr-muted); transition: transform .18s ease, color .18s ease; }
|
|
51
|
+
.brand-trigger[aria-expanded="true"] .brand-caret { color: var(--dsr-accent-strong); transform: rotate(180deg); }
|
|
44
52
|
.brand-dot {
|
|
45
53
|
width: 10px; height: 10px; border-radius: 50%;
|
|
46
54
|
background: linear-gradient(135deg, var(--dsr-brand-a), var(--dsr-brand-b));
|
|
@@ -77,6 +85,34 @@ button {
|
|
|
77
85
|
.topbar-right #btn-feedback { color: var(--dsr-info); }
|
|
78
86
|
.topbar-right #btn-refresh { color: var(--dsr-success); }
|
|
79
87
|
.topbar-right .topbar-btn.conn-badge { padding: 0 14px; border-radius: 999px; font-size: 12.5px; }
|
|
88
|
+
|
|
89
|
+
/* ---------- 顶栏组切换抽屉 ---------- */
|
|
90
|
+
.group-drawer-backdrop { position: fixed; inset: 0; z-index: 70; border: 0; background: var(--dsr-overlay); }
|
|
91
|
+
.group-drawer { position: fixed; inset: 0 auto 0 0; z-index: 71; display: flex; flex-direction: column; width: min(360px, calc(100vw - 18px)); background: var(--dsr-panel); border-right: 1px solid var(--dsr-line); box-shadow: 18px 0 48px var(--dsr-shadow); animation: group-drawer-in .22s cubic-bezier(.2,.8,.2,1); }
|
|
92
|
+
.group-drawer.hidden, .group-drawer-backdrop.hidden { display: none; }
|
|
93
|
+
@keyframes group-drawer-in { from { opacity: .01; transform: translateX(-16px); } to { opacity: 1; transform: translateX(0); } }
|
|
94
|
+
body.group-drawer-open { overflow: hidden; }
|
|
95
|
+
.group-drawer-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; padding: calc(var(--dsr-safe-top) + 18px) 18px 15px; border-bottom: 1px solid var(--dsr-line); }
|
|
96
|
+
.group-drawer-eyebrow { margin-bottom: 3px; color: var(--dsr-accent-strong); font-size: 10px; font-weight: 750; letter-spacing: 1.5px; }
|
|
97
|
+
.group-drawer-head h2 { margin: 0; color: var(--dsr-text); font-size: 20px; line-height: 1.2; }
|
|
98
|
+
.group-drawer-close { width: 38px; height: 38px; display: grid; flex: none; place-items: center; border: 1px solid var(--dsr-line); border-radius: 12px; background: var(--dsr-bg-2); color: var(--dsr-muted); font-size: 24px; line-height: 1; }
|
|
99
|
+
.group-drawer-close:active { background: var(--dsr-accent-soft); color: var(--dsr-accent-strong); }
|
|
100
|
+
.group-drawer-current { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 14px 14px 8px; padding: 11px 12px; border: 1px solid var(--dsr-accent-line); border-radius: 12px; background: var(--dsr-accent-soft); color: var(--dsr-muted); font-size: 12px; }
|
|
101
|
+
.group-drawer-current strong { min-width: 0; overflow: hidden; color: var(--dsr-accent-strong); font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
|
|
102
|
+
.group-drawer-list { flex: 1; min-height: 0; overflow-y: auto; padding: 4px 10px 14px; display: flex; flex-direction: column; gap: 4px; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: var(--dsr-line) transparent; }
|
|
103
|
+
.group-drawer-option { width: 100%; min-height: 58px; display: flex; align-items: center; gap: 11px; padding: 9px 11px; border: 1px solid transparent; border-radius: 13px; color: var(--dsr-text); text-align: left; }
|
|
104
|
+
.group-drawer-option:active { background: var(--dsr-bg-2); }
|
|
105
|
+
.group-drawer-option.current { border-color: var(--dsr-accent-line); background: linear-gradient(100deg, var(--dsr-accent-soft), transparent); }
|
|
106
|
+
.group-drawer-option-mark { width: 10px; height: 10px; flex: none; border: 2px solid var(--dsr-line); border-radius: 50%; }
|
|
107
|
+
.group-drawer-option.current .group-drawer-option-mark { border-color: var(--dsr-accent); background: var(--dsr-accent); box-shadow: 0 0 0 4px var(--dsr-accent-soft); }
|
|
108
|
+
.group-drawer-option-copy { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 2px; }
|
|
109
|
+
.group-drawer-option-name { overflow: hidden; font-size: 14px; font-weight: 700; text-overflow: ellipsis; white-space: nowrap; }
|
|
110
|
+
.group-drawer-option-meta { overflow: hidden; color: var(--dsr-muted); font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
|
|
111
|
+
.group-drawer-option-check { flex: none; color: var(--dsr-accent-strong); font-size: 18px; font-weight: 750; }
|
|
112
|
+
.group-drawer-foot { flex: none; padding: 12px 14px calc(12px + var(--dsr-safe-b)); border-top: 1px solid var(--dsr-line); background: var(--dsr-bg-2); }
|
|
113
|
+
.group-drawer-manage { width: 100%; min-height: 44px; display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 0 12px; border: 1px solid var(--dsr-line); border-radius: 11px; background: var(--dsr-panel); color: var(--dsr-accent-strong); font-weight: 700; text-align: left; }
|
|
114
|
+
.group-drawer-manage:active { background: var(--dsr-accent-soft); }
|
|
115
|
+
.group-drawer-foot p { margin: 7px 2px 0; color: var(--dsr-muted); font-size: 11px; }
|
|
80
116
|
.mini-btn {
|
|
81
117
|
padding: 5px 11px; border-radius: 9px; font-size: 13px;
|
|
82
118
|
background: var(--dsr-panel); border: 1px solid var(--dsr-line); color: var(--dsr-accent-strong);
|
|
@@ -424,6 +460,13 @@ body.in-session .view {
|
|
|
424
460
|
.queue-dock-item:last-child { border-bottom: 0; }
|
|
425
461
|
.queue-dock-preview { flex: 1; min-width: 0; color: var(--dsr-text); font-size: 13px; line-height: 1.4; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
426
462
|
.queue-dock-action { flex: 0 0 auto; }
|
|
463
|
+
.session-pending { flex:0 0 auto; margin:0 0 10px; padding:10px; border:1px solid var(--dsr-warning-line); border-radius:13px; background:var(--dsr-warning-soft); }
|
|
464
|
+
.session-pending-head { display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:8px; color:var(--dsr-text); font-size:12px; font-weight:750; }
|
|
465
|
+
.session-pending-list { display:flex; flex-direction:column; gap:8px; }
|
|
466
|
+
.session-pending .pending-card { min-width:0; background:var(--dsr-panel); }
|
|
467
|
+
.session-pending .pc-title { line-height:1.45; overflow-wrap:anywhere; word-break:break-word; }
|
|
468
|
+
.session-pending .pc-desc { line-height:1.5; white-space:pre-wrap; overflow-wrap:anywhere; }
|
|
469
|
+
.session-pending .goal-actions { justify-content:flex-end; }
|
|
427
470
|
.md-table-wrap { max-width: 100%; overflow-x: auto; margin: 8px 0; }
|
|
428
471
|
.md-table-wrap table { width: max-content; min-width: 100%; border-collapse: collapse; font-size: .94em; }
|
|
429
472
|
.md-table-wrap th, .md-table-wrap td { padding: 6px 9px; border: 1px solid var(--dsr-line); text-align: left; white-space: nowrap; }
|
|
@@ -711,16 +754,17 @@ body.in-session .main { padding-bottom: 0; }
|
|
|
711
754
|
.overview-section-meta { color:var(--dsr-muted); font-size:11px; font-weight:500; }
|
|
712
755
|
.overview-attention-list, .overview-session-list { display:flex; flex-direction:column; gap:7px; }
|
|
713
756
|
.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; }
|
|
757
|
+
.overview-attention-item { align-items:flex-start; }
|
|
714
758
|
button.overview-attention-item, button.overview-session-item { cursor:pointer; }
|
|
715
759
|
.overview-attention-item { border-left:3px solid var(--dsr-warning); }
|
|
716
760
|
.overview-attention-item.question { border-left-color:var(--dsr-accent-2); }
|
|
717
761
|
.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; }
|
|
718
762
|
.overview-attention-item.question .overview-item-mark { background:var(--dsr-accent-2-soft); color:var(--dsr-accent-2); }
|
|
719
763
|
.overview-item-copy { min-width:0; flex:1; }
|
|
720
|
-
.overview-item-title {
|
|
721
|
-
.overview-item-desc {
|
|
764
|
+
.overview-item-title { font-size:12.5px; font-weight:650; line-height:1.45; overflow-wrap:anywhere; word-break:break-word; }
|
|
765
|
+
.overview-item-desc { margin-top:3px; color:var(--dsr-muted); font-size:11px; line-height:1.45; white-space:normal; overflow-wrap:anywhere; }
|
|
722
766
|
.overview-item-arrow { color:var(--dsr-muted); font-size:18px; }
|
|
723
|
-
.overview-item-actions { flex:0 0 auto; display:flex; gap:4px; }
|
|
767
|
+
.overview-item-actions { flex:0 0 auto; display:flex; flex-wrap:wrap; justify-content:flex-end; gap:4px; }
|
|
724
768
|
.overview-item-actions .mini-btn { padding:4px 7px; font-size:10px; }
|
|
725
769
|
.overview-metric-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:7px; }
|
|
726
770
|
.overview-metric { min-width:0; padding:11px 12px; border:1px solid var(--dsr-line); border-radius:13px; background:var(--dsr-panel); }
|
|
@@ -1207,6 +1251,8 @@ button.overview-attention-item, button.overview-session-item { cursor:pointer; }
|
|
|
1207
1251
|
.section-actions .session-sort { max-width: 98px; }
|
|
1208
1252
|
.section-actions .mini-btn { max-width: 92px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1209
1253
|
.topbar { padding-left: 10px; padding-right: 10px; }
|
|
1254
|
+
.brand { gap: 6px; }
|
|
1255
|
+
.brand-group-name { max-width: 10ch; }
|
|
1210
1256
|
.topbar-right { gap: 4px; min-width: 0; }
|
|
1211
1257
|
.topbar-right .topbar-btn.icon-btn { width: 38px; height: 38px; }
|
|
1212
1258
|
.topbar-right .topbar-btn.conn-badge { max-width: 72px; padding-left: 8px; padding-right: 8px; overflow: hidden; text-overflow: ellipsis; }
|
package/public/update.json
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.6.
|
|
2
|
+
"version": "0.6.21",
|
|
3
3
|
"apkUrl": "dsh-remote.apk",
|
|
4
|
-
"sha256": "
|
|
5
|
-
"releasedAt": "2026-08-
|
|
6
|
-
"notes": "0.6.
|
|
4
|
+
"sha256": "46fc5409dbe59300e352ae6dddfbc001856a349a85b81be025e958316a1c5dc0",
|
|
5
|
+
"releasedAt": "2026-08-31T04:10:09.396Z",
|
|
6
|
+
"notes": "0.6.21:左上角 DSH Remote 名称可展开服务器组快捷切换抽屉,当前组、组内服务器数量和管理入口一目了然,手机与桌面端均可一键切换;修复部分新版 DSH 命令执行成功返回 void 时被误判为未执行、继而把 /命令作为普通文本发送的问题,同时兼容旧版三参数、新版四参数及默认参数签名。",
|
|
7
7
|
"history": [
|
|
8
|
+
{
|
|
9
|
+
"version": "0.6.21",
|
|
10
|
+
"notes": "0.6.21:左上角 DSH Remote 名称可展开服务器组快捷切换抽屉,当前组、组内服务器数量和管理入口一目了然,手机与桌面端均可一键切换;修复部分新版 DSH 命令执行成功返回 void 时被误判为未执行、继而把 /命令作为普通文本发送的问题,同时兼容旧版三参数、新版四参数及默认参数签名。"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"version": "0.6.20",
|
|
14
|
+
"notes": "0.6.20:适配 DSH 0.1.2-alpha.1 的新版客户端模块、认证、斜杠 RPC 与 remote.mux 实时协议,同时保留旧版 DSH 的自动回退;修复斜杠命令因 commands.execute 参数变化触发 aborted 报错,以及新版历史、工作区、模型、目标和子代理等接口不兼容问题;审批与用户提问现在会在所属对话内完整显示并可直接处理,主页仍保留全局待办入口,长工具参数、原因和调用标识不再被截断。"
|
|
15
|
+
},
|
|
8
16
|
{
|
|
9
17
|
"version": "0.6.19",
|
|
10
18
|
"notes": "0.6.19:全面优化手机端、桌面端、管理页和插件页的布局、动效与响应式体验,并支持减少动态效果偏好;重做蓝色、草原、音乐厅等主题的明暗层级和文字图标对比度,新增利落的黑白配色;统一主页图标配色并修正设置齿轮中心孔偏移;目标面板支持收起为始终可见的侧边入口,保留运行状态、可随时恢复,并按会话与目标在本地记忆。"
|
|
@@ -36,14 +44,6 @@
|
|
|
36
44
|
{
|
|
37
45
|
"version": "0.6.12",
|
|
38
46
|
"notes": "0.6.12:修复远程启动和重启 DSH 时统一报 HTTP 502 且无法确认结果的问题;改为异步追踪 systemd 服务检查、命令提交、进程启动、DSH HTTP 恢复和 mux/host 实时通道重连阶段,成功时显示 PID、HTTP 状态和用时,失败时区分服务不存在、systemd 不可用、权限不足、命令失败、服务失败、启动超时、HTTP 恢复超时和实时通道恢复超时;优化工作区会话筛选、文件预览和应用内选择器;新增中央投票公告与反馈成功确认;主页公告栏常驻并在无未读公告时显示空状态,同时修正设置页异常右箭头、主页刷新图标居中和周末谷时提醒。"
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
"version": "0.6.11",
|
|
42
|
-
"notes": "0.6.11 正式版:新增按 DSH 工作区筛选、切换和创建会话,文件页支持切换工作区并直接预览常见文本与 Markdown;修复工作区筛选未正确收敛会话、工作区位于默认文件根外时无法查看,以及新会话工作区名称与完整路径显示异常;新增投票公告及隐私化汇总;统一手机端应用内选择抽屉;周末全天按谷时并修复重复峰谷提醒;插件侧栏图标采用亮色背景并随插件自身四套皮肤切换配色;加强网关安装、连接与故障排查引导。"
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
"version": "0.6.10",
|
|
46
|
-
"notes": "0.6.10 正式版:修复 DSH/网关重启后事件通道不自动恢复、启动阶段网卡读取异常、同源状态误报、双通道总览卡在 3/4、图片后实时回复不可见及子代理重复;新增完整用户链路与重启生命周期测试;公网反馈切换到安全的 HTTPS 临时入口并加强来源 IP 防伪造、节流与隐私保护。"
|
|
47
47
|
}
|
|
48
48
|
]
|
|
49
49
|
}
|
package/public/version.json
CHANGED