dsh-remote-plugin 0.6.20 → 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/index.mjs +29 -2
- package/package.json +1 -1
- package/public/app.js +87 -0
- package/public/desktop/desktop.css +33 -2
- package/public/desktop/desktop.html +20 -1
- package/public/desktop/desktop.js +85 -0
- package/public/index.html +22 -3
- package/public/styles.css +39 -1
- package/public/update.json +8 -8
- package/public/version.json +1 -1
package/apk/dsh-remote.apk
CHANGED
|
Binary file
|
package/index.mjs
CHANGED
|
@@ -513,6 +513,23 @@ async function resolveAgent(ctx, sessionId) {
|
|
|
513
513
|
}
|
|
514
514
|
}
|
|
515
515
|
|
|
516
|
+
function commandHead(line) {
|
|
517
|
+
const match = /^\/+(\S+)/.exec(String(line || '').trim())
|
|
518
|
+
return match ? match[1].replace(/^\/+/, '') : ''
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
function commandEntryName(entry) {
|
|
522
|
+
const value = typeof entry === 'string' ? entry : entry?.name
|
|
523
|
+
return String(value || '').trim().replace(/^\/+/, '')
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function commandWasExecuted(result) {
|
|
527
|
+
// DSH 0.1.x 的 commands.execute() 成功时可以返回 void。只要 Promise 正常
|
|
528
|
+
// 完成,就不应把同一条 /command 再降级为 session.prompt 的普通文本;只有
|
|
529
|
+
// 执行器明确返回 false / { executed: false } 才表示未处理。
|
|
530
|
+
return result !== false && result?.executed !== false
|
|
531
|
+
}
|
|
532
|
+
|
|
516
533
|
async function resolveFile(pathname) {
|
|
517
534
|
let abs = targetPath(pathname)
|
|
518
535
|
if (abs === null) return null
|
|
@@ -771,17 +788,27 @@ async function serveStatic(req, res, ctx) {
|
|
|
771
788
|
sendJson(res, 200, { ok: false, message: 'agent not found', debug: { resolvePath, commandNames: [] } })
|
|
772
789
|
return
|
|
773
790
|
}
|
|
791
|
+
let commandEntries = null
|
|
774
792
|
let commandNames = []
|
|
775
793
|
try {
|
|
776
|
-
|
|
794
|
+
const listed = await ctx.commands.list(agent)
|
|
795
|
+
commandEntries = Array.isArray(listed) ? listed : null
|
|
796
|
+
commandNames = commandEntries ? commandEntries.map(commandEntryName).filter(Boolean) : []
|
|
777
797
|
} catch (e) {
|
|
778
798
|
commandNames = ['list-error: ' + (e?.message || String(e))]
|
|
779
799
|
}
|
|
800
|
+
const name = commandHead(line)
|
|
801
|
+
if (commandEntries && (!name || !commandEntries.some(entry => commandEntryName(entry) === name))) {
|
|
802
|
+
// 仅把 DSH 已登记的 slash command 截留到命令服务。未知 /xxx 仍让客户端
|
|
803
|
+
// 按普通文本发送,保持 DSH 自己处理自定义提示词的兼容行为。
|
|
804
|
+
sendJson(res, 200, { ok: true, executed: false, debug: { resolvePath, commandNames, reason: 'unknown-command' } })
|
|
805
|
+
return
|
|
806
|
+
}
|
|
780
807
|
const signal = AbortSignal.timeout(30000)
|
|
781
808
|
const result = ctx.commands.execute.length === 3
|
|
782
809
|
? await ctx.commands.execute(agent, line, signal)
|
|
783
810
|
: await ctx.commands.execute(agent, line, [], signal)
|
|
784
|
-
sendJson(res, 200, { ok: true, executed: result
|
|
811
|
+
sendJson(res, 200, { ok: true, executed: commandWasExecuted(result), debug: { resolvePath, commandNames } })
|
|
785
812
|
} catch (e) {
|
|
786
813
|
sendJson(res, 200, { ok: false, message: e?.message || String(e) })
|
|
787
814
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-remote-plugin",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.21",
|
|
4
4
|
"description": "DSH Remote 官方 bundle 插件:DSH 左侧原生边栏入口 + 右侧抽屉内嵌管理控制台;内置网关随 DSH 自动启停(systemd 独立单元),抽屉直显令牌与设备监控;网关提供 /fs/* 文件传输端点(列表/断点下载/上传),配合 Android App 远程操控会话/审批/提问/goal 与文件互传(多服务器测速切换、聊天记录离线缓存)。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.mjs",
|
package/public/app.js
CHANGED
|
@@ -938,10 +938,81 @@ function closeGroupMenu() {
|
|
|
938
938
|
if (menu) menu.classList.add('hidden')
|
|
939
939
|
}
|
|
940
940
|
|
|
941
|
+
let groupDrawerReturnFocus = null
|
|
942
|
+
|
|
943
|
+
function groupDrawerOpen() {
|
|
944
|
+
return !$('group-drawer')?.classList.contains('hidden')
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
function renderGroupDrawer() {
|
|
948
|
+
const trigger = $('group-drawer-trigger')
|
|
949
|
+
const brandGroup = $('brand-group-name')
|
|
950
|
+
const current = $('group-drawer-current')
|
|
951
|
+
const list = $('group-drawer-list')
|
|
952
|
+
if (brandGroup) brandGroup.textContent = state.activeGroup
|
|
953
|
+
if (trigger) trigger.setAttribute('aria-label', t('groups.drawerOpen') + ':' + state.activeGroup)
|
|
954
|
+
if (current) current.textContent = state.activeGroup
|
|
955
|
+
if (!list) return
|
|
956
|
+
list.innerHTML = state.groups.map(group => {
|
|
957
|
+
const servers = groupServers(group)
|
|
958
|
+
const selected = group === state.activeGroup
|
|
959
|
+
return `<button type="button" class="group-drawer-option ${selected ? 'current' : ''}" data-quick-group="${esc(group)}" ${selected ? 'aria-current="true"' : ''}>
|
|
960
|
+
<span class="group-drawer-option-mark" aria-hidden="true"></span>
|
|
961
|
+
<span class="group-drawer-option-copy"><span class="group-drawer-option-name">${esc(group)}</span><span class="group-drawer-option-meta">${t('groups.serverCount', { count: servers.length })}</span></span>
|
|
962
|
+
<span class="group-drawer-option-check" aria-hidden="true">${selected ? '✓' : ''}</span>
|
|
963
|
+
</button>`
|
|
964
|
+
}).join('')
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
function openGroupDrawer() {
|
|
968
|
+
const drawer = $('group-drawer')
|
|
969
|
+
const backdrop = $('group-drawer-backdrop')
|
|
970
|
+
const trigger = $('group-drawer-trigger')
|
|
971
|
+
if (!drawer || !backdrop || !trigger) return
|
|
972
|
+
renderGroupDrawer()
|
|
973
|
+
groupDrawerReturnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : trigger
|
|
974
|
+
drawer.classList.remove('hidden')
|
|
975
|
+
backdrop.classList.remove('hidden')
|
|
976
|
+
document.body.classList.add('group-drawer-open')
|
|
977
|
+
trigger.setAttribute('aria-expanded', 'true')
|
|
978
|
+
requestAnimationFrame(() => (drawer.querySelector('[data-quick-group].current') || drawer.querySelector('[data-quick-group]') || drawer).focus({ preventScroll: true }))
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
function closeGroupDrawer({ restoreFocus = true } = {}) {
|
|
982
|
+
const drawer = $('group-drawer')
|
|
983
|
+
const backdrop = $('group-drawer-backdrop')
|
|
984
|
+
const trigger = $('group-drawer-trigger')
|
|
985
|
+
if (!drawer || !backdrop || drawer.classList.contains('hidden')) return
|
|
986
|
+
drawer.classList.add('hidden')
|
|
987
|
+
backdrop.classList.add('hidden')
|
|
988
|
+
document.body.classList.remove('group-drawer-open')
|
|
989
|
+
trigger?.setAttribute('aria-expanded', 'false')
|
|
990
|
+
if (restoreFocus) (groupDrawerReturnFocus || trigger)?.focus({ preventScroll: true })
|
|
991
|
+
groupDrawerReturnFocus = null
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
function keepGroupDrawerFocus(event) {
|
|
995
|
+
if (!groupDrawerOpen()) return
|
|
996
|
+
if (event.key === 'Escape') {
|
|
997
|
+
event.preventDefault()
|
|
998
|
+
closeGroupDrawer()
|
|
999
|
+
return
|
|
1000
|
+
}
|
|
1001
|
+
if (event.key !== 'Tab') return
|
|
1002
|
+
const drawer = $('group-drawer')
|
|
1003
|
+
const targets = [...drawer.querySelectorAll('button:not([disabled]), [href], [tabindex]:not([tabindex="-1"])')]
|
|
1004
|
+
if (!targets.length) return
|
|
1005
|
+
const first = targets[0]
|
|
1006
|
+
const last = targets[targets.length - 1]
|
|
1007
|
+
if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus() }
|
|
1008
|
+
else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus() }
|
|
1009
|
+
}
|
|
1010
|
+
|
|
941
1011
|
function renderServers() {
|
|
942
1012
|
const box = $('server-list')
|
|
943
1013
|
if (!box) return
|
|
944
1014
|
renderGroupSelect()
|
|
1015
|
+
renderGroupDrawer()
|
|
945
1016
|
const groupsHtml = state.groups.map(g => {
|
|
946
1017
|
const list = groupServers(g)
|
|
947
1018
|
const auto = state.autoSelect[g] !== false
|
|
@@ -6879,6 +6950,22 @@ function bindUi() {
|
|
|
6879
6950
|
$('btn-server-add').addEventListener('click', addServer)
|
|
6880
6951
|
$('btn-group-add').addEventListener('click', addGroup)
|
|
6881
6952
|
$('group-select-btn').addEventListener('click', (e) => { e.stopPropagation(); toggleGroupMenu() })
|
|
6953
|
+
$('group-drawer-trigger').addEventListener('click', () => { groupDrawerOpen() ? closeGroupDrawer() : openGroupDrawer() })
|
|
6954
|
+
$('group-drawer-close').addEventListener('click', () => closeGroupDrawer())
|
|
6955
|
+
$('group-drawer-backdrop').addEventListener('click', () => closeGroupDrawer())
|
|
6956
|
+
$('group-drawer-list').addEventListener('click', (e) => {
|
|
6957
|
+
const option = e.target.closest('[data-quick-group]')
|
|
6958
|
+
if (!option) return
|
|
6959
|
+
const group = option.dataset.quickGroup
|
|
6960
|
+
closeGroupDrawer({ restoreFocus: false })
|
|
6961
|
+
if (group !== state.activeGroup) switchGroup(group)
|
|
6962
|
+
})
|
|
6963
|
+
$('group-drawer-manage').addEventListener('click', () => {
|
|
6964
|
+
closeGroupDrawer({ restoreFocus: false })
|
|
6965
|
+
showView('view-settings')
|
|
6966
|
+
showSettingsPage('servers')
|
|
6967
|
+
})
|
|
6968
|
+
document.addEventListener('keydown', keepGroupDrawerFocus)
|
|
6882
6969
|
document.addEventListener('click', (e) => {
|
|
6883
6970
|
if (!e.target.closest('#group-select')) closeGroupMenu()
|
|
6884
6971
|
})
|
|
@@ -9,9 +9,16 @@ html, body {
|
|
|
9
9
|
.ds-app { position: fixed; inset: 0; display: flex; overflow: hidden; }
|
|
10
10
|
.ds-sidebar { width: 280px; flex: none; display: flex; flex-direction: column; border-right: 1px solid var(--dsr-line); background: linear-gradient(180deg, var(--dsr-panel), var(--dsr-bg)); box-shadow: 12px 0 34px var(--dsr-shadow); overflow: hidden; }
|
|
11
11
|
.ds-sidebar-backdrop { display: none; }
|
|
12
|
-
.ds-brand { display: flex; align-items: center; gap:
|
|
12
|
+
.ds-brand { display: flex; align-items: center; gap: 6px; padding: 12px 12px 8px 18px; font-weight: 750; font-size: 15px; letter-spacing: .1px; }
|
|
13
|
+
.ds-brand-trigger { min-width: 0; flex: 1; display: flex; align-items: center; gap: 8px; padding: 5px 6px 5px 0; border: 0; border-radius: 10px; background: transparent; color: var(--dsr-text); font: inherit; text-align: left; cursor: pointer; }
|
|
14
|
+
.ds-brand-trigger:hover { background: var(--dsr-accent-soft); }
|
|
15
|
+
.ds-brand-trigger:focus-visible { outline: 2px solid var(--dsr-accent); outline-offset: 2px; }
|
|
13
16
|
.ds-logo { color: var(--dsr-accent-strong); }
|
|
14
|
-
.ds-brand-
|
|
17
|
+
.ds-brand-copy { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 0; line-height: 1.14; }
|
|
18
|
+
.ds-brand-name { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
19
|
+
.ds-brand-group-name { min-width: 0; overflow: hidden; color: var(--dsr-muted); font-size: 10px; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
|
|
20
|
+
.ds-brand-caret { flex: none; color: var(--dsr-muted); font-size: 15px; line-height: 1; transition: transform .18s ease, color .18s ease; }
|
|
21
|
+
.ds-brand-trigger[aria-expanded="true"] .ds-brand-caret { color: var(--dsr-accent-strong); transform: rotate(180deg); }
|
|
15
22
|
.ds-gh { color: var(--dsr-accent-2); display: inline-flex; }
|
|
16
23
|
.ds-gh svg { width: 17px; height: 17px; fill: currentColor; }
|
|
17
24
|
.ds-new-session { padding: 0 12px 8px; }
|
|
@@ -470,6 +477,30 @@ button.ds-overview-attention-item, button.ds-overview-session-item { cursor:poin
|
|
|
470
477
|
.ds-drawer-body::-webkit-scrollbar-thumb { background: var(--dsr-line); border-radius: 999px; }
|
|
471
478
|
.ds-drawer-body::-webkit-scrollbar-thumb:hover { background: var(--dsr-muted); }
|
|
472
479
|
|
|
480
|
+
/* 左上角应用名展开的组快捷切换抽屉 */
|
|
481
|
+
.ds-group-drawer-backdrop { position: fixed; inset: 0; z-index: 108; border: 0; background: var(--dsr-overlay); }
|
|
482
|
+
.ds-group-drawer { position: fixed; inset: 0 auto 0 0; z-index: 109; width: min(360px, calc(100vw - 18px)); display: flex; flex-direction: column; background: var(--dsr-panel); border-right: 1px solid var(--dsr-line); box-shadow: 18px 0 48px var(--dsr-shadow); animation: ds-group-drawer-in .22s cubic-bezier(.2,.8,.2,1); }
|
|
483
|
+
.ds-group-drawer.hidden, .ds-group-drawer-backdrop.hidden { display: none; }
|
|
484
|
+
@keyframes ds-group-drawer-in { from { opacity: .01; transform: translateX(-16px); } to { opacity: 1; transform: translateX(0); } }
|
|
485
|
+
.ds-group-drawer-head { flex: none; display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; padding: 19px 16px 15px; border-bottom: 1px solid var(--dsr-line); }
|
|
486
|
+
.ds-group-drawer-eyebrow { margin-bottom: 3px; color: var(--dsr-accent-strong); font-size: 10px; font-weight: 750; letter-spacing: 1.5px; }
|
|
487
|
+
.ds-group-drawer-head h2 { margin: 0; color: var(--dsr-text); font-size: 19px; line-height: 1.2; }
|
|
488
|
+
.ds-group-drawer-current { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 14px 14px 8px; padding: 10px 12px; border: 1px solid var(--dsr-accent-line); border-radius: 12px; background: var(--dsr-accent-soft); color: var(--dsr-muted); font-size: 12px; }
|
|
489
|
+
.ds-group-drawer-current strong { min-width: 0; overflow: hidden; color: var(--dsr-accent-strong); font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
|
|
490
|
+
.ds-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; }
|
|
491
|
+
.ds-group-drawer-option { width: 100%; min-height: 56px; display: flex; align-items: center; gap: 11px; padding: 9px 11px; border: 1px solid transparent; border-radius: 13px; background: transparent; color: var(--dsr-text); font: inherit; text-align: left; cursor: pointer; }
|
|
492
|
+
.ds-group-drawer-option:hover { background: var(--dsr-bg-2); }
|
|
493
|
+
.ds-group-drawer-option.current { border-color: var(--dsr-accent-line); background: linear-gradient(100deg, var(--dsr-accent-soft), transparent); }
|
|
494
|
+
.ds-group-drawer-mark { width: 10px; height: 10px; flex: none; border: 2px solid var(--dsr-line); border-radius: 50%; }
|
|
495
|
+
.ds-group-drawer-option.current .ds-group-drawer-mark { border-color: var(--dsr-accent); background: var(--dsr-accent); box-shadow: 0 0 0 4px var(--dsr-accent-soft); }
|
|
496
|
+
.ds-group-drawer-copy { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 2px; }
|
|
497
|
+
.ds-group-drawer-name { overflow: hidden; font-size: 14px; font-weight: 700; text-overflow: ellipsis; white-space: nowrap; }
|
|
498
|
+
.ds-group-drawer-meta { overflow: hidden; color: var(--dsr-muted); font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
|
|
499
|
+
.ds-group-drawer-check { flex: none; color: var(--dsr-accent-strong); font-size: 18px; font-weight: 750; }
|
|
500
|
+
.ds-group-drawer-foot { flex: none; padding: 12px 14px; border-top: 1px solid var(--dsr-line); background: var(--dsr-bg-2); }
|
|
501
|
+
.ds-group-drawer-foot .ds-btn { width: 100%; justify-content: space-between; }
|
|
502
|
+
.ds-group-drawer-foot p { margin: 7px 2px 0; color: var(--dsr-muted); font-size: 11px; }
|
|
503
|
+
|
|
473
504
|
/* 通知卡片栈(右下角) */
|
|
474
505
|
.ds-toast-stack { position: fixed; right: 16px; bottom: 16px; z-index: 100; display: flex; flex-direction: column; gap: 10px; width: min(380px, calc(100vw - 32px)); }
|
|
475
506
|
.ds-notif-card { background: var(--dsr-panel); border: 1px solid var(--dsr-line); border-left: 3px solid var(--dsr-accent-strong); border-radius: 12px; box-shadow: 0 10px 28px rgba(0,0,0,.3); padding: 11px 13px; animation: dsSlideIn .22s ease; }
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
<div class="ds-app" id="ds-app">
|
|
13
13
|
<aside class="ds-sidebar" id="ds-sidebar">
|
|
14
14
|
<div class="ds-brand">
|
|
15
|
-
<
|
|
15
|
+
<button id="ds-group-drawer-trigger" class="ds-brand-trigger" type="button" aria-haspopup="dialog" aria-controls="ds-group-drawer" aria-expanded="false" data-i18n-aria="ds.groupsDrawerOpen">
|
|
16
|
+
<span class="ds-logo">◆</span><span class="ds-brand-copy"><span class="ds-brand-name">DSH Remote</span><span id="ds-brand-group-name" class="ds-brand-group-name">默认</span></span><span class="ds-brand-caret" aria-hidden="true">⌄</span>
|
|
17
|
+
</button>
|
|
16
18
|
<a class="ds-gh" href="https://github.com/Blank-not-black/dsh-Remote" target="_blank" rel="noopener" data-i18n-title="ds.repo" data-i18n-aria="ds.repo">
|
|
17
19
|
<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8a8.01 8.01 0 0 0 5.47 7.59c.4.08.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z"/></svg>
|
|
18
20
|
</a>
|
|
@@ -82,6 +84,19 @@
|
|
|
82
84
|
</div>
|
|
83
85
|
</aside>
|
|
84
86
|
<button id="ds-sidebar-backdrop" class="ds-sidebar-backdrop hidden" type="button" data-i18n-aria="ds.close" aria-label="关闭菜单"></button>
|
|
87
|
+
<button id="ds-group-drawer-backdrop" class="ds-group-drawer-backdrop hidden" type="button" tabindex="-1" aria-hidden="true"></button>
|
|
88
|
+
<aside id="ds-group-drawer" class="ds-group-drawer hidden" role="dialog" aria-modal="true" aria-labelledby="ds-group-drawer-title" tabindex="-1">
|
|
89
|
+
<div class="ds-group-drawer-head">
|
|
90
|
+
<div><div class="ds-group-drawer-eyebrow" data-i18n="ds.groupsDrawerEyebrow">QUICK SWITCH</div><h2 id="ds-group-drawer-title" data-i18n="ds.groupsDrawerTitle">切换服务器组</h2></div>
|
|
91
|
+
<button id="ds-group-drawer-close" class="ds-icon-btn" type="button" data-i18n-aria="ds.groupsDrawerClose" data-i18n-title="ds.groupsDrawerClose">×</button>
|
|
92
|
+
</div>
|
|
93
|
+
<div class="ds-group-drawer-current"><span data-i18n="ds.groupsDrawerCurrent">当前连接组</span><strong id="ds-group-drawer-current">默认</strong></div>
|
|
94
|
+
<div id="ds-group-drawer-list" class="ds-group-drawer-list" role="group" data-i18n-aria="ds.groupsDrawerTitle"></div>
|
|
95
|
+
<div class="ds-group-drawer-foot">
|
|
96
|
+
<button id="ds-group-drawer-manage" class="ds-btn" type="button"><span data-i18n="ds.groupsDrawerManage">管理服务器与组</span><span aria-hidden="true">›</span></button>
|
|
97
|
+
<p data-i18n="ds.groupsDrawerManageDesc">编辑、测速和调整组</p>
|
|
98
|
+
</div>
|
|
99
|
+
</aside>
|
|
85
100
|
|
|
86
101
|
<div class="ds-main">
|
|
87
102
|
<header class="ds-topbar">
|
|
@@ -528,6 +543,8 @@
|
|
|
528
543
|
'ds.groupsPromptRename': '重命名组「{group}」:', 'ds.groupsRenamed': '组已重命名',
|
|
529
544
|
'ds.groupsCannotDeleteDefault': '默认组不可删除',
|
|
530
545
|
'ds.groupsConfirmDelete': '删除组「{group}」?组内服务器将归入默认组。', 'ds.groupsDeleted': '组已删除',
|
|
546
|
+
'ds.groupsDrawerOpen': '切换服务器组', 'ds.groupsDrawerClose': '关闭组列表', 'ds.groupsDrawerEyebrow': '快速切换', 'ds.groupsDrawerTitle': '切换服务器组', 'ds.groupsDrawerCurrent': '当前连接组',
|
|
547
|
+
'ds.groupsDrawerManage': '管理服务器与组', 'ds.groupsDrawerManageDesc': '编辑、测速和调整组', 'ds.groupsServerCount': '{count} 台服务器',
|
|
531
548
|
'ds.theme.default': '默认深空', 'ds.theme.dark': '落日', 'ds.theme.light': '易北爱乐厅', 'ds.theme.neutral': '草原孤塔', 'ds.theme.mono': '黑曜白',
|
|
532
549
|
'ds.role.me': '我', 'ds.role.dsh': 'DSH', 'ds.toolDefault': '工具',
|
|
533
550
|
'ds.eventSystemReminder': '系统提示',
|
|
@@ -631,6 +648,8 @@
|
|
|
631
648
|
'ds.groupsPromptRename': 'Rename group "{group}":', 'ds.groupsRenamed': 'Group renamed',
|
|
632
649
|
'ds.groupsCannotDeleteDefault': 'The default group cannot be deleted',
|
|
633
650
|
'ds.groupsConfirmDelete': 'Delete group "{group}"? Its servers move to the default group.', 'ds.groupsDeleted': 'Group deleted',
|
|
651
|
+
'ds.groupsDrawerOpen': 'Switch server group', 'ds.groupsDrawerClose': 'Close group list', 'ds.groupsDrawerEyebrow': 'QUICK SWITCH', 'ds.groupsDrawerTitle': 'Switch server group', 'ds.groupsDrawerCurrent': 'Current connection group',
|
|
652
|
+
'ds.groupsDrawerManage': 'Manage servers and groups', 'ds.groupsDrawerManageDesc': 'Edit, test, and organize groups', 'ds.groupsServerCount': '{count} servers',
|
|
634
653
|
'ds.theme.default': 'Deep Space', 'ds.theme.dark': 'Sunset', 'ds.theme.light': 'Elbphilharmonie', 'ds.theme.neutral': 'Prairie Tower', 'ds.theme.mono': 'Monochrome',
|
|
635
654
|
'ds.role.me': 'Me', 'ds.role.dsh': 'DSH', 'ds.toolDefault': 'tool',
|
|
636
655
|
'ds.eventSystemReminder': 'System reminder',
|
|
@@ -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
|
|
@@ -3077,6 +3146,22 @@ function bindUi() {
|
|
|
3077
3146
|
$('btn-server-add').addEventListener('click', addServer)
|
|
3078
3147
|
$('btn-group-add').addEventListener('click', addGroup)
|
|
3079
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)
|
|
3080
3165
|
document.addEventListener('click', (e) => { if (!e.target.closest('#group-select')) closeGroupMenu() })
|
|
3081
3166
|
$('server-input').addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.isComposing) { e.preventDefault(); addServer() } })
|
|
3082
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">
|
|
@@ -958,6 +973,8 @@
|
|
|
958
973
|
'groups.added': '已创建组:{group}', 'groups.promptRename': '重命名组「{group}」:',
|
|
959
974
|
'groups.renamed': '组已重命名', 'groups.cannotDeleteDefault': '默认组不可删除',
|
|
960
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} 台服务器',
|
|
961
978
|
'stream.error': '事件流错误:{msg}',
|
|
962
979
|
'session.loading': '加载中…', 'session.unknown': '未知会话', 'session.running': '运行中', 'session.interrupted': '已中断',
|
|
963
980
|
'session.stop': '停止本轮', 'session.history': '对话', 'session.errorMsg': '会话出错:{msg}', 'session.recovering': '恢复会话中…', 'session.recoveryReady': '会话已恢复', 'session.recoveryFailed': '会话恢复失败', 'session.recoveryCached': '正在查看缓存历史',
|
|
@@ -1188,6 +1205,8 @@
|
|
|
1188
1205
|
'groups.added': 'Created group: {group}', 'groups.promptRename': 'Rename group "{group}":',
|
|
1189
1206
|
'groups.renamed': 'Group renamed', 'groups.cannotDeleteDefault': 'The default group cannot be deleted',
|
|
1190
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',
|
|
1191
1210
|
'stream.error': 'Event stream error: {msg}',
|
|
1192
1211
|
'session.loading': 'Loading…', 'session.unknown': 'Unknown session', 'session.running': 'Running', 'session.interrupted': 'Interrupted',
|
|
1193
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);
|
|
@@ -1215,6 +1251,8 @@ button.overview-attention-item, button.overview-session-item { cursor:pointer; }
|
|
|
1215
1251
|
.section-actions .session-sort { max-width: 98px; }
|
|
1216
1252
|
.section-actions .mini-btn { max-width: 92px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1217
1253
|
.topbar { padding-left: 10px; padding-right: 10px; }
|
|
1254
|
+
.brand { gap: 6px; }
|
|
1255
|
+
.brand-group-name { max-width: 10ch; }
|
|
1218
1256
|
.topbar-right { gap: 4px; min-width: 0; }
|
|
1219
1257
|
.topbar-right .topbar-btn.icon-btn { width: 38px; height: 38px; }
|
|
1220
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,14 @@
|
|
|
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
|
+
},
|
|
8
12
|
{
|
|
9
13
|
"version": "0.6.20",
|
|
10
14
|
"notes": "0.6.20:适配 DSH 0.1.2-alpha.1 的新版客户端模块、认证、斜杠 RPC 与 remote.mux 实时协议,同时保留旧版 DSH 的自动回退;修复斜杠命令因 commands.execute 参数变化触发 aborted 报错,以及新版历史、工作区、模型、目标和子代理等接口不兼容问题;审批与用户提问现在会在所属对话内完整显示并可直接处理,主页仍保留全局待办入口,长工具参数、原因和调用标识不再被截断。"
|
|
@@ -40,10 +44,6 @@
|
|
|
40
44
|
{
|
|
41
45
|
"version": "0.6.12",
|
|
42
46
|
"notes": "0.6.12:修复远程启动和重启 DSH 时统一报 HTTP 502 且无法确认结果的问题;改为异步追踪 systemd 服务检查、命令提交、进程启动、DSH HTTP 恢复和 mux/host 实时通道重连阶段,成功时显示 PID、HTTP 状态和用时,失败时区分服务不存在、systemd 不可用、权限不足、命令失败、服务失败、启动超时、HTTP 恢复超时和实时通道恢复超时;优化工作区会话筛选、文件预览和应用内选择器;新增中央投票公告与反馈成功确认;主页公告栏常驻并在无未读公告时显示空状态,同时修正设置页异常右箭头、主页刷新图标居中和周末谷时提醒。"
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
"version": "0.6.11",
|
|
46
|
-
"notes": "0.6.11 正式版:新增按 DSH 工作区筛选、切换和创建会话,文件页支持切换工作区并直接预览常见文本与 Markdown;修复工作区筛选未正确收敛会话、工作区位于默认文件根外时无法查看,以及新会话工作区名称与完整路径显示异常;新增投票公告及隐私化汇总;统一手机端应用内选择抽屉;周末全天按谷时并修复重复峰谷提醒;插件侧栏图标采用亮色背景并随插件自身四套皮肤切换配色;加强网关安装、连接与故障排查引导。"
|
|
47
47
|
}
|
|
48
48
|
]
|
|
49
49
|
}
|
package/public/version.json
CHANGED