dsh-remote-plugin 0.7.2 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apk/dsh-remote.apk +0 -0
- package/gateway.cjs +313 -18
- package/index.mjs +31 -9
- package/package.json +1 -1
- package/public/app.js +310 -303
- package/public/desktop/desktop.css +101 -0
- package/public/desktop/desktop.html +59 -21
- package/public/desktop/desktop.js +260 -101
- package/public/history.js +160 -0
- package/public/index.html +61 -22
- package/public/link-check.js +109 -0
- package/public/plugin-center.css +11 -0
- package/public/plugin-center.js +92 -39
- package/public/styles.css +107 -2
- package/public/update.json +8 -8
- package/public/version.json +1 -1
package/public/plugin-center.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
window.DshPluginCenter = (() => {
|
|
4
4
|
let dialog, connection, snapshot, timer, generation = 0, pending = false, tab = 'installed', offset = 0, searchSequence = 0, detailSequence = 0
|
|
5
|
+
const expandedDetails = new Map()
|
|
5
6
|
let renderedInstalled = '', renderedJobs = '', filter = '', statusFilter = 'all', refreshSequence = 0
|
|
6
7
|
const labels = { install: '安装', update: '更新', remove: '卸载', enable: '启用', disable: '停用', queued: '等待执行', running: '执行中', complete: '已完成', failed: '失败', interrupted: '已中断', active: '运行中', pending: '等待依赖', loading: '加载中', disposed: '未运行', unloading: '卸载中', inactive: '未运行' }
|
|
7
8
|
function node(tag, text, className) {
|
|
@@ -43,7 +44,7 @@ window.DshPluginCenter = (() => {
|
|
|
43
44
|
return el
|
|
44
45
|
}
|
|
45
46
|
function renderInstalled() {
|
|
46
|
-
const list = dialog.querySelector('.pc-list'); list.replaceChildren()
|
|
47
|
+
const list = dialog.querySelector('.pc-list'), restore = preserveListPosition(list); list.replaceChildren()
|
|
47
48
|
if (!snapshot) { list.append(node('p', '尚未读取插件状态,请点击刷新。')); return }
|
|
48
49
|
const rows = snapshot.items.filter(item => tab === 'builtin' ? !item.managed : item.managed)
|
|
49
50
|
.filter(item => [item.name, item.displayName, item.description].join(' ').toLowerCase().includes(filter.toLowerCase()))
|
|
@@ -60,10 +61,11 @@ window.DshPluginCenter = (() => {
|
|
|
60
61
|
else if (runtime.length) meta.append(node('span', `${runtime.filter(entry => entry.phase === 'active').length}/${runtime.length} 组件运行中`, 'pc-badge'))
|
|
61
62
|
el.append(meta)
|
|
62
63
|
const actions = node('div', null, 'pc-card-actions')
|
|
63
|
-
const
|
|
64
|
+
const area = inlineArea(item.name)
|
|
65
|
+
const detail = button('查看详情', () => installedDetails(item, area, detail)); detail.className = 'pc-detail-button'
|
|
64
66
|
actions.append(detail)
|
|
65
67
|
if (item.managed && snapshot.writable) {
|
|
66
|
-
actions.append(button('查看更新', () => showDetails(item.name)))
|
|
68
|
+
actions.append(button('查看更新', () => showDetails(item.name, '', area, detail, true)))
|
|
67
69
|
if (item.bundle) {
|
|
68
70
|
const toggle = button(item.enabled ? '已启用' : '已停用', () => mutate(item.enabled ? 'disable' : 'enable', item.name))
|
|
69
71
|
toggle.className = 'pc-toggle'; toggle.disabled = pending || snapshot.busy
|
|
@@ -74,7 +76,10 @@ window.DshPluginCenter = (() => {
|
|
|
74
76
|
const remove = button('卸载', () => mutate('remove', item.name)); remove.className = 'pc-danger-button'; actions.append(remove)
|
|
75
77
|
for (const action of actions.children) if (action !== detail) action.disabled = pending || snapshot.busy
|
|
76
78
|
} else actions.append(node('small', '主机维护 · 只读'))
|
|
77
|
-
el.
|
|
79
|
+
el.dataset.plugin = item.name
|
|
80
|
+
detail.setAttribute('aria-controls', area.id)
|
|
81
|
+
renderInline(area, detail, item)
|
|
82
|
+
el.append(actions, area)
|
|
78
83
|
list.append(el)
|
|
79
84
|
}
|
|
80
85
|
const runtime = node('details', null, 'pc-runtime')
|
|
@@ -82,19 +87,66 @@ window.DshPluginCenter = (() => {
|
|
|
82
87
|
if (!snapshot.runtimeAvailable) runtime.append(node('p', '此 DSH 版本未提供加载状态'))
|
|
83
88
|
for (const entry of snapshot.runtime) runtime.append(node('p', entry.name + ' · ' + entry.phase + (entry.enabled ? '' : ' · disabled')))
|
|
84
89
|
list.append(runtime)
|
|
90
|
+
restore()
|
|
85
91
|
}
|
|
86
|
-
function
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
function preserveListPosition(list) {
|
|
93
|
+
const anchor = [...list.children].find(el => el.dataset.plugin && el.getBoundingClientRect?.().bottom > 0)
|
|
94
|
+
if (!anchor?.getBoundingClientRect) return () => {}
|
|
95
|
+
const top = anchor.getBoundingClientRect().top, name = anchor.dataset.plugin
|
|
96
|
+
let parent = list.parentElement
|
|
97
|
+
while (parent && !/(auto|scroll)/.test(getComputedStyle(parent).overflowY)) parent = parent.parentElement
|
|
98
|
+
return () => {
|
|
99
|
+
const current = [...list.children].find(el => el.dataset.plugin === name)
|
|
100
|
+
if (!current) return
|
|
101
|
+
const delta = current.getBoundingClientRect().top - top
|
|
102
|
+
if (parent) parent.scrollTop += delta
|
|
103
|
+
else window.scrollBy(0, delta)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function inlineArea(name) {
|
|
107
|
+
const area = node('section', null, 'pc-inline-detail')
|
|
108
|
+
area.dataset.key = tab + ':' + name
|
|
109
|
+
area.id = 'pc-detail-' + encodeURIComponent(area.dataset.key)
|
|
110
|
+
return area
|
|
111
|
+
}
|
|
112
|
+
function currentArea(key) { return [...dialog.querySelectorAll('.pc-inline-detail')].find(el => el.dataset.key === key) }
|
|
113
|
+
function renderInline(area, control, installed) {
|
|
114
|
+
const entry = expandedDetails.get(area.dataset.key)
|
|
115
|
+
control.textContent = entry ? '收起详情' : '查看详情'
|
|
116
|
+
control.setAttribute('aria-expanded', String(!!entry))
|
|
117
|
+
area.replaceChildren(); area.hidden = !entry
|
|
118
|
+
if (!entry) return
|
|
119
|
+
if (entry.loading) { area.append(node('p', '正在加载详情…', 'pc-detail-loading')); return }
|
|
120
|
+
if (entry.error) { area.append(node('p', entry.error, 'pc-detail-error'), button('重试', () => showDetails(entry.name, entry.version, area, control, true))); return }
|
|
121
|
+
if (entry.kind === 'installed') {
|
|
122
|
+
const item = installed || entry.item
|
|
123
|
+
if (item.description) area.append(node('p', item.description))
|
|
124
|
+
area.append(node('p', '配置:' + (item.enabled ? '启用' : '停用')), node('h4', '组件运行状态'))
|
|
125
|
+
const entries = item.runtime || []
|
|
126
|
+
if (!entries.length) area.append(node('p', '未获得该包的组件归属。可在列表底部查看当前进程全部加载状态。'))
|
|
127
|
+
for (const component of entries) area.append(node('p', component.name + ' · ' + (labels[component.phase] || component.phase)))
|
|
128
|
+
if (!item.managed) area.append(node('p', '随 DSH 提供的核心插件由主机维护。'))
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
const item = entry.item
|
|
132
|
+
area.append(node('h4', item.name + ' @ ' + item.version), node('p', item.description || ''), node('p', '许可证:' + (item.license || '未声明') + ' · ' + (item.bundle ? 'DSH bundle' : '未声明 DSH bundle')))
|
|
133
|
+
area.append(node('p', '运行要求:' + JSON.stringify(item.engines) + ';依赖要求:' + JSON.stringify(item.peers)))
|
|
134
|
+
area.append(node('p', '插件代码将在 DSH 主机运行。安装脚本默认禁用;需要额外配置的插件须在主机完成配置。'))
|
|
135
|
+
if (/^https:\/\//.test(item.homepage)) {
|
|
136
|
+
const link = node('a', '项目主页'); link.href = item.homepage; link.target = '_blank'; link.rel = 'noopener noreferrer'; area.append(link)
|
|
137
|
+
}
|
|
138
|
+
const input = node('input'); input.value = entry.versionDraft ?? item.version; input.setAttribute('aria-label', '插件版本'); input.placeholder = '指定版本,如 1.2.3'
|
|
139
|
+
input.addEventListener('input', () => { entry.versionDraft = input.value })
|
|
140
|
+
area.append(input, button('查看此版本', () => showDetails(entry.name, input.value.trim(), area, control, true)))
|
|
141
|
+
const row = snapshot?.items.find(row => row.name === item.name), action = row ? 'update' : 'install'
|
|
142
|
+
const install = button(labels[action] + ' ' + item.version, () => mutate(action, item.name, item.version))
|
|
143
|
+
install.disabled = !item.bundle || item.protected || !snapshot?.writable || snapshot.busy || pending || row?.version === item.version
|
|
144
|
+
area.append(install)
|
|
145
|
+
}
|
|
146
|
+
function installedDetails(item, area, control) {
|
|
147
|
+
if (expandedDetails.has(area.dataset.key)) expandedDetails.delete(area.dataset.key)
|
|
148
|
+
else expandedDetails.set(area.dataset.key, { kind: 'installed', item })
|
|
149
|
+
renderInline(area, control, item)
|
|
98
150
|
}
|
|
99
151
|
function renderTabs() {
|
|
100
152
|
for (const button of dialog.querySelectorAll('[data-pc-tab]')) {
|
|
@@ -158,33 +210,34 @@ window.DshPluginCenter = (() => {
|
|
|
158
210
|
const el = card(item.name, item.description)
|
|
159
211
|
const meta = node('div', null, 'pc-meta'); meta.append(node('span', item.version ? 'v' + item.version : '版本未声明', 'pc-version'))
|
|
160
212
|
const actions = node('div', null, 'pc-card-actions')
|
|
161
|
-
const
|
|
162
|
-
|
|
213
|
+
const area = inlineArea(item.name)
|
|
214
|
+
const detail = button('查看详情', () => showDetails(item.name, '', area, detail)); detail.className = 'pc-detail-button'; detail.setAttribute('aria-controls', area.id); actions.append(detail)
|
|
215
|
+
el.dataset.plugin = item.name
|
|
216
|
+
renderInline(area, detail)
|
|
217
|
+
el.append(meta, actions, area)
|
|
163
218
|
list.append(el)
|
|
164
219
|
}
|
|
165
220
|
if (offset > 0) list.append(button('上一页', () => { offset -= 20; return search() }))
|
|
166
221
|
if (offset + 20 < data.total) list.append(button('下一页', () => { offset += 20; return search() }))
|
|
167
222
|
}
|
|
168
|
-
async function showDetails(name, version) {
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
223
|
+
async function showDetails(name, version, area, control, force = false) {
|
|
224
|
+
const key = area.dataset.key
|
|
225
|
+
if (!force && expandedDetails.has(key)) { expandedDetails.delete(key); renderInline(area, control); return }
|
|
226
|
+
const current = generation, entry = { name, version, kind: 'market', loading: true }
|
|
227
|
+
expandedDetails.set(key, entry); renderInline(area, control)
|
|
228
|
+
try {
|
|
229
|
+
const { item } = await api('/details?name=' + encodeURIComponent(name) + (version ? '&version=' + encodeURIComponent(version) : ''))
|
|
230
|
+
if (current !== generation || expandedDetails.get(key) !== entry) return
|
|
231
|
+
entry.item = item; entry.loading = false
|
|
232
|
+
} catch (error) {
|
|
233
|
+
if (current !== generation || expandedDetails.get(key) !== entry) return
|
|
234
|
+
entry.loading = false; entry.error = error.message
|
|
235
|
+
}
|
|
236
|
+
const target = currentArea(key)
|
|
237
|
+
if (target) {
|
|
238
|
+
const button = (target.parentElement || target.parent).querySelector('.pc-detail-button')
|
|
239
|
+
renderInline(target, button)
|
|
179
240
|
}
|
|
180
|
-
const input = node('input'); input.value = item.version; input.setAttribute('aria-label', '插件版本'); input.placeholder = '指定版本,如 1.2.3'
|
|
181
|
-
el.append(input, button('查看此版本', () => showDetails(name, input.value.trim())))
|
|
182
|
-
const installed = snapshot?.items.find(row => row.name === name)
|
|
183
|
-
const action = installed ? 'update' : 'install'
|
|
184
|
-
const install = button((labels[action]) + ' ' + item.version, () => mutate(action, name, item.version))
|
|
185
|
-
install.disabled = !item.bundle || item.protected || !snapshot?.writable || snapshot.busy || pending || installed?.version === item.version
|
|
186
|
-
el.append(install, button('关闭详情', () => area.replaceChildren()))
|
|
187
|
-
area.append(el); area.scrollIntoView({ block: 'nearest' })
|
|
188
241
|
}
|
|
189
242
|
async function mutate(action, name, version) {
|
|
190
243
|
if (pending) return
|
|
@@ -215,7 +268,7 @@ window.DshPluginCenter = (() => {
|
|
|
215
268
|
} finally { if (current === generation) { pending = false; await refresh() } }
|
|
216
269
|
}
|
|
217
270
|
function close() {
|
|
218
|
-
generation++; searchSequence++; detailSequence++; clearTimeout(timer)
|
|
271
|
+
generation++; searchSequence++; detailSequence++; clearTimeout(timer); expandedDetails.clear()
|
|
219
272
|
if (!dialog) return
|
|
220
273
|
if (dialog.tagName === 'DIALOG') dialog.close()
|
|
221
274
|
else { dialog.open = false; dialog.remove() }
|
|
@@ -232,7 +285,7 @@ window.DshPluginCenter = (() => {
|
|
|
232
285
|
const toolbar = node('div', null, 'pc-tabs')
|
|
233
286
|
for (const [value, title] of [['installed', '已安装'], ['market', '添加插件'], ['builtin', '内置插件']]) {
|
|
234
287
|
const control = button(title, () => {
|
|
235
|
-
tab = value; detailSequence++; renderedInstalled = ''; dialog.querySelector('.pc-detail').replaceChildren()
|
|
288
|
+
tab = value; detailSequence++; expandedDetails.clear(); renderedInstalled = ''; dialog.querySelector('.pc-detail').replaceChildren()
|
|
236
289
|
renderTabs(); message('')
|
|
237
290
|
dialog.querySelector('.pc-market-tools').hidden = value !== 'market'; dialog.querySelector('.pc-filters').hidden = value === 'market'
|
|
238
291
|
if (value === 'market') { offset = 0; return search() }
|
package/public/styles.css
CHANGED
|
@@ -287,7 +287,9 @@ html.reorder-scroll-lock body { overscroll-behavior: none; }
|
|
|
287
287
|
.archived-toggle { width: 100%; display: block; text-align: center; padding: 9px 0; margin-top: 2px; border-radius: 9px; background: var(--dsr-panel); border: 1px dashed var(--dsr-line); color: var(--dsr-muted); font-size: 12px; }
|
|
288
288
|
|
|
289
289
|
/* ---------- 会话详情 ---------- */
|
|
290
|
-
|
|
290
|
+
/* 会话页不显示底部导航,不保留导航的页面占位或输入栏偏移。
|
|
291
|
+
* 系统安全区仍由 .composer 的底部 padding 处理。 */
|
|
292
|
+
body.in-session { overflow: hidden; height: 100vh; height: 100dvh; padding-bottom: 0; }
|
|
291
293
|
body.in-session .topbar { display: none; }
|
|
292
294
|
body.in-session .main {
|
|
293
295
|
height: 100%;
|
|
@@ -297,6 +299,7 @@ body.in-session .main {
|
|
|
297
299
|
padding: 12px 12px 0;
|
|
298
300
|
}
|
|
299
301
|
body.in-session .bottom-nav { display: none; }
|
|
302
|
+
body.in-session .composer-wrap { bottom: 0; }
|
|
300
303
|
body.in-session .view {
|
|
301
304
|
flex: 1;
|
|
302
305
|
min-height: 0;
|
|
@@ -548,7 +551,7 @@ body.in-session.has-composer-images .history { padding-bottom: calc(146px + var(
|
|
|
548
551
|
.composer-fs-btn svg { width: 15px; height: 15px; }
|
|
549
552
|
.composer-input-wrap.has-fs-btn textarea { padding-right: 38px; }
|
|
550
553
|
.composer-fs-handle { display: none; }
|
|
551
|
-
.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); }
|
|
554
|
+
.composer-wrap.fs { position: fixed; top: var(--composer-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); }
|
|
552
555
|
.composer-wrap.fs .composer-menu { display: none; }
|
|
553
556
|
.composer-wrap.fs .composer-fs-handle { display: flex; align-items: center; justify-content: center; flex: none; height: 28px; touch-action: pan-y; }
|
|
554
557
|
.composer-fs-handle span { width: 38px; height: 4px; border-radius: 999px; background: var(--dsr-line); }
|
|
@@ -1232,3 +1235,105 @@ button:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible
|
|
|
1232
1235
|
#btn-send, #btn-fs-send { touch-action: none; user-select: none; -webkit-touch-callout: none; }
|
|
1233
1236
|
#btn-send.steer-mode, #btn-fs-send.steer-mode { outline: 2px solid currentColor; outline-offset: 2px; }
|
|
1234
1237
|
.send-hold-ready { box-shadow: 0 0 0 3px var(--dsr-accent-strong); }
|
|
1238
|
+
|
|
1239
|
+
/* 关于页贡献者致谢:随页面提供名单,离线可见,按贡献提交数排序。 */
|
|
1240
|
+
.contributors-thanks { margin: 6px 0 12px; color: var(--dsr-muted); font-size: 12px; line-height: 1.6; }
|
|
1241
|
+
.contributors-list { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; margin: 0 0 10px; padding: 0; }
|
|
1242
|
+
.contributors-list li { min-width: 0; max-width: 100%; }
|
|
1243
|
+
.contributors-list a { display: flex; align-items: center; min-height: 44px; box-sizing: border-box; padding: 8px 12px; border: 1px solid var(--dsr-line); border-radius: 10px; background: var(--dsr-bg-2); color: var(--dsr-text); font-size: 13px; overflow-wrap: anywhere; text-decoration: none; }
|
|
1244
|
+
.contributors-list a:hover { border-color: var(--dsr-accent-line); color: var(--dsr-accent-strong); }
|
|
1245
|
+
.contributors-all { display: inline-flex; align-items: center; min-height: 44px; color: var(--dsr-accent-strong); font-size: 12px; text-underline-offset: 3px; }
|
|
1246
|
+
.contributors-section a:focus-visible { outline: 2px solid var(--dsr-accent-strong); outline-offset: 3px; }
|
|
1247
|
+
|
|
1248
|
+
/* 会话操作统一入口;标题与工作区保持独立阅读空间。 */
|
|
1249
|
+
.session-actions-dialog,.session-info-dialog { width:min(420px,calc(100vw - 24px)); max-height:calc(100dvh - 48px); padding:16px; border:1px solid var(--dsr-line); border-radius:18px; background:var(--dsr-bg-2); color:var(--dsr-text); overflow:auto; box-shadow:0 16px 48px var(--dsr-shadow); }
|
|
1250
|
+
.session-actions-dialog::backdrop,.session-info-dialog::backdrop { background:var(--dsr-overlay); }
|
|
1251
|
+
.session-dialog-head { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:12px; }
|
|
1252
|
+
.session-dialog-head button { min-width:44px; min-height:44px; color:var(--dsr-muted); font:inherit; font-size:24px; border:0; background:transparent; cursor:pointer; }
|
|
1253
|
+
.session-actions-dialog>button { display:block; width:100%; min-height:48px; padding:12px; margin:4px 0; border:0; border-radius:10px; background:var(--dsr-panel); color:var(--dsr-text); font:inherit; text-align:left; cursor:pointer; }
|
|
1254
|
+
.session-actions-dialog>button:hover { background:var(--dsr-accent-soft); }
|
|
1255
|
+
.session-actions-dialog>button svg { display:none; }
|
|
1256
|
+
.session-actions-dialog #btn-archive-session { color:var(--dsr-warning); border-top:1px solid var(--dsr-line); margin-top:12px; }
|
|
1257
|
+
.session-info-dialog dl { margin:0 0 16px; }
|
|
1258
|
+
.session-info-dialog dt { margin-top:14px; color:var(--dsr-muted); font-size:12px; }
|
|
1259
|
+
.session-info-dialog dd { margin:4px 0 0; white-space:pre-wrap; overflow-wrap:anywhere; line-height:1.6; }
|
|
1260
|
+
.session-info-dialog>button { min-height:44px; padding:8px 14px; border:1px solid var(--dsr-line); border-radius:10px; background:var(--dsr-panel); color:var(--dsr-text); font:inherit; cursor:pointer; }
|
|
1261
|
+
.session-actions-dialog button:focus-visible,.session-info-dialog button:focus-visible { outline:2px solid var(--dsr-accent); outline-offset:2px; }
|
|
1262
|
+
|
|
1263
|
+
body.in-session .session-head { gap:10px; margin-bottom:0; }
|
|
1264
|
+
.session-head .icon-btn { width:44px; height:44px; flex:none; }
|
|
1265
|
+
.session-title { display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; width:100%; padding:0; text-align:left; white-space:normal; overflow-wrap:anywhere; line-height:1.4; }
|
|
1266
|
+
#session-sub { display:block; flex:none; min-height:36px; width:100%; padding:7px 2px; margin:0; text-align:left; }
|
|
1267
|
+
.composer-status { padding:4px 12px 0; min-height:36px; }
|
|
1268
|
+
.composer-status #btn-cancel { margin-left:auto; min-height:36px; font-size:12px; padding:5px 10px; }
|
|
1269
|
+
body.in-session .history { padding-bottom:calc(var(--composer-height, calc(96px + var(--dsr-safe-b))) + 12px); }
|
|
1270
|
+
body.in-session.has-composer-images .history { padding-bottom:calc(var(--composer-height, calc(166px + var(--dsr-safe-b))) + 12px); }
|
|
1271
|
+
|
|
1272
|
+
.session-info-dialog>button:disabled { opacity:.45; cursor:default; }
|
|
1273
|
+
|
|
1274
|
+
/* 有界历史视图:保留消息节点,窗口外记录不参与布局。 */
|
|
1275
|
+
.history-entry { flex:0 0 auto; min-width:0; overflow-anchor:none; display:flow-root; }
|
|
1276
|
+
.history-page-control { flex:none; align-self:center; min-height:44px; padding:8px 14px; border:1px solid var(--dsr-line); border-radius:10px; background:var(--dsr-panel); color:var(--dsr-muted); font:inherit; cursor:pointer; }
|
|
1277
|
+
.history-page-control[hidden],.history-live:empty { display:none; }
|
|
1278
|
+
.history-empty { padding:24px 8px; text-align:center; color:var(--dsr-muted); }
|
|
1279
|
+
.history-source { color:var(--dsr-muted); font-size:11px; font-weight:400; letter-spacing:0; }
|
|
1280
|
+
.history-meta { display:flex; align-items:center; gap:8px; min-width:0; flex-wrap:wrap; justify-content:flex-end; }
|
|
1281
|
+
.history-latest { position:absolute; right:22px; bottom:20px; z-index:8; min-height:44px; padding:8px 12px; border:1px solid var(--dsr-accent-line); border-radius:999px; color:var(--dsr-accent-strong); background:var(--dsr-bg-2); box-shadow:0 4px 16px var(--dsr-shadow); font:inherit; cursor:pointer; }
|
|
1282
|
+
|
|
1283
|
+
#history { overflow-anchor:none; }
|
|
1284
|
+
#history-wrap>.history-latest { bottom:calc(var(--composer-height, 96px) + 16px); }
|
|
1285
|
+
.session-title { min-height:44px; }
|
|
1286
|
+
|
|
1287
|
+
.crash-preview {margin:10px 0;font-size:13px;}
|
|
1288
|
+
.crash-preview summary {cursor:pointer;min-height:44px;display:flex;align-items:center;}
|
|
1289
|
+
.crash-preview pre {max-height:200px;overflow:auto;white-space:pre-wrap;overflow-wrap:anywhere;font-size:12px;padding:10px;background:var(--dsr-panel);border-radius:8px;}
|
|
1290
|
+
/* Keep feedback actions visible while reading expanded crash summaries. */
|
|
1291
|
+
#modal-feedback .modal-card { display:flex; flex-direction:column; max-height:calc(100dvh - 48px - var(--dsr-safe-t) - var(--dsr-safe-b)); overflow:hidden; }
|
|
1292
|
+
#modal-feedback .modal-title,#modal-feedback .modal-actions { flex-shrink:0; }
|
|
1293
|
+
#modal-feedback .modal-body { min-height:0; overflow-y:auto; overscroll-behavior:contain; }
|
|
1294
|
+
#modal-feedback .modal-actions .btn { min-height:44px; }
|
|
1295
|
+
|
|
1296
|
+
/* Link instrument: persistent geometry, compositor sweep and interruptible state transitions. */
|
|
1297
|
+
.overview-pulse-card { --pulse-ease:cubic-bezier(0.23,1,0.32,1); transition:border-color 240ms ease; }
|
|
1298
|
+
.overview-pulse-copy,.overview-link { position:relative; }
|
|
1299
|
+
.overview-status { line-height:1.25; min-block-size:1.25em; }
|
|
1300
|
+
html[lang="en"] .overview-status { min-block-size:2.5em; }
|
|
1301
|
+
.overview-status-desc { line-height:1.4; min-block-size:2.8em; }
|
|
1302
|
+
.overview-pulse-card.status-checking { border-color:var(--dsr-accent-line); }
|
|
1303
|
+
.overview-pulse-card.status-checking .overview-primary-action { border-color:var(--dsr-accent-strong); background:var(--dsr-accent-strong); color:var(--dsr-on-accent); }
|
|
1304
|
+
.overview-primary-action { transition:transform 160ms var(--pulse-ease),background-color 240ms ease,border-color 240ms ease,color 240ms ease; }
|
|
1305
|
+
.system-pulse-ring { width:108px; height:108px; padding:0; background:none; box-shadow:none; isolation:isolate; flex:none; }
|
|
1306
|
+
.system-pulse-ring::before { inset:0; border:0; opacity:.7; background:repeating-conic-gradient(var(--dsr-accent-line) 0deg 1deg,transparent 1deg 15deg); -webkit-mask:radial-gradient(farthest-side,transparent calc(100% - 2px),#000 calc(100% - 1px)); mask:radial-gradient(farthest-side,transparent calc(100% - 2px),#000 calc(100% - 1px)); }
|
|
1307
|
+
.system-pulse-ring::after { content:""; position:absolute; inset:12%; border-radius:50%; box-shadow:0 0 24px var(--dsr-accent-line); opacity:.12; transform:scale(.96); transition:opacity 240ms var(--pulse-ease),transform 240ms var(--pulse-ease); pointer-events:none; }
|
|
1308
|
+
.overview-pulse-card.status-checking .system-pulse-ring::after { opacity:.55; transform:scale(1); }
|
|
1309
|
+
.system-pulse-ring .pulse-dial { position:absolute; inset:0; width:100%; height:100%; overflow:visible; fill:none; stroke:var(--dsr-accent); stroke-width:2; stroke-linecap:round; }
|
|
1310
|
+
.system-pulse-ring .pulse-dial path { opacity:.15; transition:opacity 240ms var(--pulse-ease),stroke 240ms ease; }
|
|
1311
|
+
.system-pulse-ring .pulse-dial [data-confirmed="true"] { opacity:1; }
|
|
1312
|
+
.overview-pulse-card.status-nominal .system-pulse-ring .pulse-dial [data-confirmed="true"] { stroke:var(--dsr-success); }
|
|
1313
|
+
.system-pulse-ring .pulse-sweep { position:absolute; inset:3%; border-radius:50%; background:conic-gradient(from 0deg,transparent 0deg 278deg,var(--dsr-accent-soft) 306deg,var(--dsr-accent) 350deg,var(--dsr-accent-strong) 358deg,transparent 360deg); -webkit-mask:radial-gradient(farthest-side,transparent calc(100% - 3px),#000 calc(100% - 2px)); mask:radial-gradient(farthest-side,transparent calc(100% - 3px),#000 calc(100% - 2px)); opacity:0; animation:overview-scan 3s linear infinite; animation-play-state:paused; transition:opacity 240ms var(--pulse-ease); pointer-events:none; }
|
|
1314
|
+
.overview-pulse-card[data-pulse-running="true"] .pulse-sweep { animation-play-state:running; }
|
|
1315
|
+
.overview-pulse-card.status-checking .pulse-sweep { opacity:1; }
|
|
1316
|
+
.system-pulse-ring .pulse-reticle { position:absolute; inset:22%; border:1px solid var(--dsr-accent-line); border-radius:50%; opacity:.6; pointer-events:none; transition:border-color 240ms ease; }
|
|
1317
|
+
.system-pulse-ring .pulse-reticle::before,.system-pulse-ring .pulse-reticle::after { content:""; position:absolute; width:1px; height:5px; left:50%; background:var(--dsr-accent); opacity:.65; }
|
|
1318
|
+
.system-pulse-ring .pulse-reticle::before { top:-3px; }
|
|
1319
|
+
.system-pulse-ring .pulse-reticle::after { bottom:-3px; }
|
|
1320
|
+
.system-pulse-core { position:absolute; inset:24%; width:auto; height:auto; background:var(--dsr-bg-2); z-index:1; }
|
|
1321
|
+
.system-pulse-core strong { font-size:12px; letter-spacing:.6px; transition:color 240ms ease; }
|
|
1322
|
+
.system-pulse-core span { font-family:ui-monospace,SFMono-Regular,Consolas,monospace; font-variant-numeric:tabular-nums; font-size:9px; letter-spacing:.5px; }
|
|
1323
|
+
.overview-pulse-card.status-nominal .system-pulse-core strong { color:var(--dsr-success); }
|
|
1324
|
+
.overview-link i { transition:background-color 240ms ease,box-shadow 240ms ease; }
|
|
1325
|
+
.overview-link.pending i { background:var(--dsr-accent); animation:overview-detect 2.5s ease-in-out infinite; }
|
|
1326
|
+
.overview-link:nth-child(2) i { animation-delay:-.15s; }
|
|
1327
|
+
.overview-link:nth-child(3) i { animation-delay:-.3s; }
|
|
1328
|
+
.overview-link:nth-child(4) i { animation-delay:-.45s; }
|
|
1329
|
+
.overview-link.pending b { color:var(--dsr-muted); }
|
|
1330
|
+
.overview-refresh:disabled { cursor:progress; opacity:.6; }
|
|
1331
|
+
.pulse-text-echo { position:absolute!important; pointer-events:none; user-select:none; z-index:2; }
|
|
1332
|
+
@keyframes overview-scan { to { transform:rotate(360deg); } }
|
|
1333
|
+
@keyframes overview-detect { 50% { opacity:.4; } }
|
|
1334
|
+
@media(prefers-reduced-motion:reduce) {
|
|
1335
|
+
.overview-pulse-card,.overview-pulse-card .pulse-dial path,.overview-pulse-card .pulse-reticle,.overview-pulse-card .pulse-sweep,.overview-pulse-card .system-pulse-ring::after,.overview-pulse-card .overview-link i,.overview-pulse-card .system-pulse-core strong,.overview-pulse-card .overview-primary-action { transition:none; }
|
|
1336
|
+
.overview-pulse-card .pulse-sweep,.overview-pulse-card .overview-link.pending i { animation:none; }
|
|
1337
|
+
.overview-pulse-card.status-checking .pulse-sweep { opacity:.4; }
|
|
1338
|
+
.overview-pulse-card .pulse-text-echo { display:none; }
|
|
1339
|
+
}
|
package/public/update.json
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.7.
|
|
2
|
+
"version": "0.7.3",
|
|
3
3
|
"apkUrl": "dsh-remote.apk",
|
|
4
|
-
"sha256": "
|
|
5
|
-
"releasedAt": "2026-10-
|
|
6
|
-
"notes": "
|
|
4
|
+
"sha256": "e2f186e022797c0c94110728cdc9ad5abc828c9989ee66ca66b49396a2c8e79f",
|
|
5
|
+
"releasedAt": "2026-10-10T05:27:42.179Z",
|
|
6
|
+
"notes": "修复手机对话页输入栏下方空白,标题与工作区独立显示,操作集中到更多菜单。主页系统链路自动检测和重试,新增分段仪表、拖尾扫描与平滑状态切换。对话历史先显示本地缓存,再后台核对云端内容;一致时保留消息与阅读位置,修复持续上翻导致的卡顿,隐藏无意义的标题事件。插件详情原地展开,清理重复入口,完善 DSH Desktop 插件管理。新增 Windows Desktop 远程启动与重启(实验性),以及网关、受控 DSH 与 Desktop 的限次崩溃恢复;本机轮转记录诊断日志,用户可预览并自行选择通过现有反馈通道上传摘要。加强敏感目录访问、设备权限、配对与系统备份保护,新增离线贡献者致谢,更新现有 Capacitor 至 8.5.3。Android 已完成构建与浏览器验收,尚未手机真机验收;本次不提供 HarmonyOS HAP,新增 WebUI 功能尚未同步鸿蒙。",
|
|
7
7
|
"history": [
|
|
8
|
+
{
|
|
9
|
+
"version": "0.7.3",
|
|
10
|
+
"notes": "修复手机对话页输入栏下方空白,标题与工作区独立显示,操作集中到更多菜单。主页系统链路自动检测和重试,新增分段仪表、拖尾扫描与平滑状态切换。对话历史先显示本地缓存,再后台核对云端内容;一致时保留消息与阅读位置,修复持续上翻导致的卡顿,隐藏无意义的标题事件。插件详情原地展开,清理重复入口,完善 DSH Desktop 插件管理。新增 Windows Desktop 远程启动与重启(实验性),以及网关、受控 DSH 与 Desktop 的限次崩溃恢复;本机轮转记录诊断日志,用户可预览并自行选择通过现有反馈通道上传摘要。加强敏感目录访问、设备权限、配对与系统备份保护,新增离线贡献者致谢,更新现有 Capacitor 至 8.5.3。Android 已完成构建与浏览器验收,尚未手机真机验收;本次不提供 HarmonyOS HAP,新增 WebUI 功能尚未同步鸿蒙。"
|
|
11
|
+
},
|
|
8
12
|
{
|
|
9
13
|
"version": "0.7.2",
|
|
10
14
|
"notes": "补齐 DSH Desktop 的插件管理接入,使用桌面版自带管理入口,支持安装、更新、卸载及配置启停,修复后台任务启动和 pnpm 11 卸载参数兼容。新增手机与桌面的上下文、费用和额度面板,适配 dsh-context 与 dsh-cost-meter;需要主机安装并启用对应插件。修复设备密钥访问插件管理接口的越权、切换连接竞态、插件任务残留锁和 Desktop 打开控制台无响应。提供 Android 本地测试安装包(未做手机真机安装验证);鸿蒙本批未同步上下文与费用面板。"
|
|
@@ -40,10 +44,6 @@
|
|
|
40
44
|
{
|
|
41
45
|
"version": "0.6.21",
|
|
42
46
|
"notes": "0.6.21:左上角 DSH Remote 名称可展开服务器组快捷切换抽屉,当前组、组内服务器数量和管理入口一目了然,手机与桌面端均可一键切换;修复部分新版 DSH 命令执行成功返回 void 时被误判为未执行、继而把 /命令作为普通文本发送的问题,同时兼容旧版三参数、新版四参数及默认参数签名。"
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
"version": "0.6.20",
|
|
46
|
-
"notes": "0.6.20:适配 DSH 0.1.2-alpha.1 的新版客户端模块、认证、斜杠 RPC 与 remote.mux 实时协议,同时保留旧版 DSH 的自动回退;修复斜杠命令因 commands.execute 参数变化触发 aborted 报错,以及新版历史、工作区、模型、目标和子代理等接口不兼容问题;审批与用户提问现在会在所属对话内完整显示并可直接处理,主页仍保留全局待办入口,长工具参数、原因和调用标识不再被截断。"
|
|
47
47
|
}
|
|
48
48
|
]
|
|
49
49
|
}
|
package/public/version.json
CHANGED