dsh-remote-plugin 0.6.18 → 0.6.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apk/dsh-remote.apk +0 -0
- package/package.json +1 -1
- package/public/admin.html +16 -17
- package/public/admin.js +6 -5
- package/public/app.js +52 -14
- package/public/desktop/desktop.css +48 -7
- package/public/desktop/desktop.html +23 -20
- package/public/desktop/desktop.js +72 -16
- package/public/index.html +31 -29
- package/public/motion.js +105 -17
- package/public/plugin.html +6 -5
- package/public/styles.css +56 -24
- package/public/theme-vars.css +244 -167
- package/public/theme.js +2 -2
- package/public/update.json +8 -8
- package/public/version.json +1 -1
|
@@ -27,10 +27,11 @@ const CAP = window.Capacitor || null
|
|
|
27
27
|
|
|
28
28
|
/* ---------------- 皮肤 ---------------- */
|
|
29
29
|
const THEME_META = [
|
|
30
|
-
{ id: 'default', sw: ['#
|
|
31
|
-
{ id: 'dark', sw: ['#
|
|
32
|
-
{ id: 'light', sw: ['#
|
|
33
|
-
{ id: 'neutral', sw: ['#
|
|
30
|
+
{ id: 'default', sw: ['#0D1117', '#21262D', '#58A6FF'] },
|
|
31
|
+
{ id: 'dark', sw: ['#161316', '#302323', '#FFB86B'] },
|
|
32
|
+
{ id: 'light', sw: ['#F5F7F8', '#FFFFFF', '#176B87'] },
|
|
33
|
+
{ id: 'neutral', sw: ['#EEF1E8', '#FAFBF6', '#47643C'] },
|
|
34
|
+
{ id: 'mono', sw: ['#050505', '#333333', '#F5F5F5'] }
|
|
34
35
|
]
|
|
35
36
|
function themeGet() {
|
|
36
37
|
let v = LS.get('dshTheme', '')
|
|
@@ -1381,6 +1382,24 @@ function goalOf(s) {
|
|
|
1381
1382
|
if (!p) return null
|
|
1382
1383
|
return p.goal && typeof p.goal === 'object' ? p.goal : p
|
|
1383
1384
|
}
|
|
1385
|
+
const COLLAPSED_GOALS_KEY = 'dshCollapsedGoalsV1'
|
|
1386
|
+
function collapsedGoals() {
|
|
1387
|
+
try {
|
|
1388
|
+
const value = JSON.parse(LS.get(COLLAPSED_GOALS_KEY, '[]'))
|
|
1389
|
+
return Array.isArray(value) ? value.filter(item => item && typeof item.sessionId === 'string' && typeof item.goalId === 'string') : []
|
|
1390
|
+
} catch { return [] }
|
|
1391
|
+
}
|
|
1392
|
+
function goalDisplayId(goal) { return String(goal?.id || '__current__') }
|
|
1393
|
+
function isGoalCollapsed(sessionId, goal) {
|
|
1394
|
+
const goalId = goalDisplayId(goal)
|
|
1395
|
+
return collapsedGoals().some(item => item.sessionId === sessionId && item.goalId === goalId)
|
|
1396
|
+
}
|
|
1397
|
+
function setGoalCollapsed(sessionId, goal, collapsed) {
|
|
1398
|
+
const goalId = goalDisplayId(goal)
|
|
1399
|
+
const next = collapsedGoals().filter(item => item.sessionId !== sessionId)
|
|
1400
|
+
if (collapsed) next.push({ sessionId, goalId })
|
|
1401
|
+
LS.set(COLLAPSED_GOALS_KEY, JSON.stringify(next.slice(-100)))
|
|
1402
|
+
}
|
|
1384
1403
|
function onSessionEvent(sessionId, event) {
|
|
1385
1404
|
if (event?.type === 'turn/start' || event?.type === 'turn/end') {
|
|
1386
1405
|
noteSessionTurnTime(sessionId, event)
|
|
@@ -1738,15 +1757,23 @@ async function renderSessionCards() {
|
|
|
1738
1757
|
const todos = proj(s, 'todos')
|
|
1739
1758
|
let html = ''
|
|
1740
1759
|
if (goal && !isGoalTerminal(goal)) {
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
<div class="ds-goal-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
<
|
|
1748
|
-
<
|
|
1749
|
-
|
|
1760
|
+
const collapsed = isGoalCollapsed(sessionId, goal)
|
|
1761
|
+
html += `<div class="ds-goal-disclosure${collapsed ? ' is-collapsed' : ''}">
|
|
1762
|
+
<div id="goal-panel-desktop" class="ds-card ds-goal-card${collapsed ? ' hidden' : ''}">
|
|
1763
|
+
<div class="ds-goal-card-head"><div class="ds-card-title">${t('goal.title')}</div>
|
|
1764
|
+
<button type="button" class="ds-goal-collapse-btn" data-goal-collapse="1" aria-expanded="true" aria-controls="goal-panel-desktop" title="${esc(t('goal.collapse'))}"><span aria-hidden="true">›</span>${t('goal.collapseShort')}</button>
|
|
1765
|
+
</div>
|
|
1766
|
+
<div class="ds-goal-obj">${esc(goal.objective || '')}</div>
|
|
1767
|
+
<div class="ds-goal-phase">phase: ${esc(goal.phase || '?')} · revision ${goal.revision ?? '?'}</div>
|
|
1768
|
+
<div class="ds-goal-actions">
|
|
1769
|
+
${goal.phase === 'active' ? `<button class="ds-mini-btn" data-goal="pause">${t('goal.pause')}</button>` : `<button class="ds-mini-btn" data-goal="resume">${t('goal.resume')}</button>`}
|
|
1770
|
+
<button class="ds-mini-btn" data-goal="complete">${t('goal.complete')}</button>
|
|
1771
|
+
<button class="ds-mini-btn" data-goal="edit">${t('goal.edit')}</button>
|
|
1772
|
+
<button class="ds-mini-btn" data-goal="clear">${t('goal.clear')}</button>
|
|
1773
|
+
</div>
|
|
1774
|
+
</div>
|
|
1775
|
+
<button type="button" class="ds-goal-side-tab${collapsed ? '' : ' hidden'}" data-goal-collapse="0" aria-expanded="false" aria-controls="goal-panel-desktop" aria-label="${esc(t('goal.expand'))}" title="${esc(t('goal.expand'))}"><span aria-hidden="true">‹</span><span>${t('goal.title')}</span><small>${t('goal.collapsedHint')}</small></button>
|
|
1776
|
+
</div>`
|
|
1750
1777
|
}
|
|
1751
1778
|
if (todos?.items?.length) {
|
|
1752
1779
|
html += `<div class="ds-card"><div class="ds-card-title">${t('todos.title')}</div>${todos.items.map(item =>
|
|
@@ -1756,6 +1783,15 @@ async function renderSessionCards() {
|
|
|
1756
1783
|
box.innerHTML = html
|
|
1757
1784
|
box.querySelectorAll('[data-goal]').forEach(btn =>
|
|
1758
1785
|
btn.addEventListener('click', () => goalAction(btn.dataset.goal)))
|
|
1786
|
+
box.querySelectorAll('[data-goal-collapse]').forEach(btn =>
|
|
1787
|
+
btn.addEventListener('click', () => {
|
|
1788
|
+
const collapse = btn.dataset.goalCollapse === '1'
|
|
1789
|
+
const currentGoal = goalOf(state.byId.get(state.current))
|
|
1790
|
+
if (!currentGoal) return
|
|
1791
|
+
setGoalCollapsed(state.current, currentGoal, collapse)
|
|
1792
|
+
void renderSessionCards()
|
|
1793
|
+
requestAnimationFrame(() => box.querySelector(`[data-goal-collapse="${collapse ? '0' : '1'}"]`)?.focus())
|
|
1794
|
+
}))
|
|
1759
1795
|
|
|
1760
1796
|
const sub = await safeRpc('subagent.list', { parentSessionId: sessionId }, '')
|
|
1761
1797
|
if (renderGeneration !== sessionCardsRenderGeneration || state.current !== sessionId) return
|
|
@@ -2719,10 +2755,25 @@ function renderOverviewDesktop() {
|
|
|
2719
2755
|
$('ds-overview-session-list').querySelectorAll('[data-ds-overview-session]').forEach(btn => btn.addEventListener('click', () => openSession(btn.dataset.dsOverviewSession)))
|
|
2720
2756
|
}
|
|
2721
2757
|
|
|
2758
|
+
const narrowSidebarQuery = window.matchMedia('(max-width: 1023px)')
|
|
2759
|
+
function setMobileSidebar(open) {
|
|
2760
|
+
const sidebar = $('ds-sidebar')
|
|
2761
|
+
const backdrop = $('ds-sidebar-backdrop')
|
|
2762
|
+
const trigger = $('btn-mobile-nav')
|
|
2763
|
+
if (!sidebar || !backdrop || !trigger) return
|
|
2764
|
+
const visible = !!open && narrowSidebarQuery.matches
|
|
2765
|
+
sidebar.classList.toggle('mobile-open', visible)
|
|
2766
|
+
backdrop.classList.toggle('hidden', !visible)
|
|
2767
|
+
trigger.setAttribute('aria-expanded', String(visible))
|
|
2768
|
+
sidebar.inert = narrowSidebarQuery.matches && !visible
|
|
2769
|
+
$('ds-app')?.classList.toggle('mobile-nav-open', visible)
|
|
2770
|
+
}
|
|
2771
|
+
|
|
2722
2772
|
function showView(id) {
|
|
2723
2773
|
state.view = id
|
|
2724
2774
|
for (const v of ['view-overview', 'view-sessions', 'view-chat', 'view-files', 'view-settings']) $(v).classList.toggle('hidden', v !== id)
|
|
2725
2775
|
document.querySelectorAll('.ds-nav-item').forEach(b => b.classList.toggle('active', b.dataset.view === id))
|
|
2776
|
+
if (narrowSidebarQuery.matches) setMobileSidebar(false)
|
|
2726
2777
|
window.DshMotion?.view($(id))
|
|
2727
2778
|
const titles = { 'view-overview': 'ds.overview', 'view-sessions': 'ds.sessions', 'view-chat': 'ds.sessions', 'view-files': 'ds.files', 'view-settings': 'ds.settings' }
|
|
2728
2779
|
if (id === 'view-chat') { const s = state.byId.get(state.current); $('ds-title').textContent = s ? titleOf(s) : t('ds.sessions') }
|
|
@@ -2825,9 +2876,14 @@ function bindUi() {
|
|
|
2825
2876
|
LS.set('sessionSort', state.sessionSort)
|
|
2826
2877
|
renderSessions()
|
|
2827
2878
|
})
|
|
2828
|
-
$('btn-mobile-nav').addEventListener('click', () =>
|
|
2829
|
-
|
|
2830
|
-
|
|
2879
|
+
$('btn-mobile-nav').addEventListener('click', () => setMobileSidebar(!$('ds-sidebar').classList.contains('mobile-open')))
|
|
2880
|
+
$('ds-sidebar-backdrop').addEventListener('click', () => setMobileSidebar(false))
|
|
2881
|
+
narrowSidebarQuery.addEventListener?.('change', () => setMobileSidebar(false))
|
|
2882
|
+
document.addEventListener('keydown', (event) => {
|
|
2883
|
+
if (event.key === 'Escape' && $('ds-sidebar').classList.contains('mobile-open')) {
|
|
2884
|
+
setMobileSidebar(false)
|
|
2885
|
+
$('btn-mobile-nav').focus()
|
|
2886
|
+
}
|
|
2831
2887
|
})
|
|
2832
2888
|
document.querySelectorAll('.ds-nav-item').forEach(b => b.addEventListener('click', () => showView(b.dataset.view)))
|
|
2833
2889
|
$('ds-overview-refresh').addEventListener('click', async () => {
|
package/public/index.html
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
6
|
-
<meta name="theme-color" content="#
|
|
6
|
+
<meta name="theme-color" content="#0D1117">
|
|
7
7
|
<meta name="mobile-web-app-capable" content="yes">
|
|
8
8
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
9
9
|
<title>DSH Remote</title>
|
|
10
10
|
<script>/* 统一入口: 桌面浏览器(宽屏且 UA 非移动)访问根地址自动切换到桌面端, 手机/窄窗口继续 App 界面 */ (function(){try{var proto=location.protocol;var ua=navigator.userAgent||'';var mobile=/Android|iPhone|iPad|iPod|Mobile|Windows Phone/i.test(ua);if((proto==='http:'||proto==='https:')&&!mobile&&window.innerWidth>=1024){var p=location.pathname;if(p==='/'||/\/index\.html$/.test(p)){location.replace('desktop/desktop.html'+location.search)}}}catch(e){}})()</script>
|
|
11
|
-
<script>/* 首帧前应用皮肤, 避免闪白/闪黑 */ (function(){try{var t=localStorage.getItem('dshTheme');if(t!=='default'&&t!=='dark'&&t!=='light'&&t!=='neutral'){t=window.matchMedia('(prefers-color-scheme: light)').matches?'light':'default'}document.documentElement.setAttribute('data-theme',t)}catch(e){}})()</script>
|
|
11
|
+
<script>/* 首帧前应用皮肤, 避免闪白/闪黑 */ (function(){try{var t=localStorage.getItem('dshTheme');if(t!=='default'&&t!=='dark'&&t!=='light'&&t!=='neutral'&&t!=='mono'){t=window.matchMedia('(prefers-color-scheme: light)').matches?'light':'default'}document.documentElement.setAttribute('data-theme',t)}catch(e){}})()</script>
|
|
12
12
|
<link rel="manifest" href="manifest.webmanifest">
|
|
13
13
|
<link rel="icon" href="icon.svg" type="image/svg+xml">
|
|
14
14
|
<link rel="stylesheet" href="styles.css">
|
|
@@ -276,27 +276,27 @@
|
|
|
276
276
|
<section id="view-settings" class="view hidden">
|
|
277
277
|
<div id="settings-home">
|
|
278
278
|
<div class="settings-group">
|
|
279
|
-
<
|
|
279
|
+
<button type="button" class="setting-row setting-link" data-settings-group="general">
|
|
280
280
|
<div><div class="setting-name" data-i18n="settings.groupGeneral">通用</div><div class="setting-desc" data-i18n="settings.groupGeneralDesc">显示工具调用、预设提示词</div></div>
|
|
281
|
-
</
|
|
282
|
-
<
|
|
281
|
+
</button>
|
|
282
|
+
<button type="button" class="setting-row setting-link" data-settings-group="model">
|
|
283
283
|
<div><div class="setting-name" data-i18n="settings.groupModel">模型</div><div class="setting-desc" data-i18n="settings.groupModelDesc">提供方、API 密钥和模型目录</div></div>
|
|
284
|
-
</
|
|
285
|
-
<
|
|
284
|
+
</button>
|
|
285
|
+
<button type="button" class="setting-row setting-link" data-settings-group="tests">
|
|
286
286
|
<div><div class="setting-name" data-i18n="settings.groupTests">功能测试</div><div class="setting-desc" data-i18n="settings.groupTestsDesc">验证 Android 和 DSH 原生能力</div></div>
|
|
287
|
-
</
|
|
288
|
-
<
|
|
287
|
+
</button>
|
|
288
|
+
<button type="button" class="setting-row setting-link" data-settings-group="servers">
|
|
289
289
|
<div><div class="setting-name" data-i18n="settings.groupServers">服务器</div><div class="setting-desc" data-i18n="settings.groupServersDesc">服务器地址、扫码连接</div></div>
|
|
290
|
-
</
|
|
291
|
-
<
|
|
290
|
+
</button>
|
|
291
|
+
<button type="button" class="setting-row setting-link" data-settings-group="notify">
|
|
292
292
|
<div><div class="setting-name" data-i18n="settings.groupNotify">通知</div><div class="setting-desc" data-i18n="settings.groupNotifyDesc">通知、提醒、后台轮询</div></div>
|
|
293
|
-
</
|
|
294
|
-
<
|
|
293
|
+
</button>
|
|
294
|
+
<button type="button" class="setting-row setting-link" data-settings-group="theme">
|
|
295
295
|
<div><div class="setting-name" data-i18n="settings.groupTheme">皮肤</div><div class="setting-desc" data-i18n="settings.groupThemeDesc">配色主题</div></div>
|
|
296
|
-
</
|
|
297
|
-
<
|
|
296
|
+
</button>
|
|
297
|
+
<button type="button" class="setting-row setting-link" data-settings-group="about">
|
|
298
298
|
<div><div class="setting-name" data-i18n="settings.groupAbout">关于</div><div class="setting-desc" data-i18n="settings.groupAboutDesc">DSH 状态、检查更新</div></div>
|
|
299
|
-
</
|
|
299
|
+
</button>
|
|
300
300
|
</div>
|
|
301
301
|
<div class="settings-group">
|
|
302
302
|
<div class="setting-row">
|
|
@@ -336,7 +336,7 @@
|
|
|
336
336
|
<p class="about" data-i18n="settings.about">DSH Remote · 局域网 / Tailscale 均可访问<br>仅作为 DSH 本机 3080 端口的带认证代理</p>
|
|
337
337
|
</div>
|
|
338
338
|
|
|
339
|
-
<div id="settings-page-general" class="settings-subpage hidden">
|
|
339
|
+
<div id="settings-page-general" class="settings-subpage hidden" data-motion-panel>
|
|
340
340
|
<div class="settings-group">
|
|
341
341
|
<div class="setting-row" data-settings-back style="cursor:pointer">
|
|
342
342
|
<button class="mini-btn" data-i18n="settings.back">‹ 返回</button>
|
|
@@ -363,7 +363,7 @@
|
|
|
363
363
|
</div>
|
|
364
364
|
</div>
|
|
365
365
|
|
|
366
|
-
<div id="settings-page-model" class="settings-subpage hidden">
|
|
366
|
+
<div id="settings-page-model" class="settings-subpage hidden" data-motion-panel>
|
|
367
367
|
<div class="settings-group">
|
|
368
368
|
<div class="setting-row" data-settings-back style="cursor:pointer">
|
|
369
369
|
<button class="mini-btn" data-i18n="settings.back">‹ 返回</button>
|
|
@@ -386,7 +386,7 @@
|
|
|
386
386
|
</div>
|
|
387
387
|
</div>
|
|
388
388
|
|
|
389
|
-
<div id="settings-page-tests" class="settings-subpage hidden">
|
|
389
|
+
<div id="settings-page-tests" class="settings-subpage hidden" data-motion-panel>
|
|
390
390
|
<div class="settings-group">
|
|
391
391
|
<div class="setting-row" data-settings-back style="cursor:pointer">
|
|
392
392
|
<button class="mini-btn" data-i18n="settings.back">‹ 返回</button>
|
|
@@ -415,7 +415,7 @@
|
|
|
415
415
|
</div>
|
|
416
416
|
</div>
|
|
417
417
|
|
|
418
|
-
<div id="settings-page-servers" class="settings-subpage hidden">
|
|
418
|
+
<div id="settings-page-servers" class="settings-subpage hidden" data-motion-panel>
|
|
419
419
|
<div class="settings-group">
|
|
420
420
|
<div class="setting-row" data-settings-back style="cursor:pointer">
|
|
421
421
|
<button class="mini-btn" data-i18n="settings.back">‹ 返回</button>
|
|
@@ -452,7 +452,7 @@
|
|
|
452
452
|
</div>
|
|
453
453
|
</div>
|
|
454
454
|
|
|
455
|
-
<div id="settings-page-notify" class="settings-subpage hidden">
|
|
455
|
+
<div id="settings-page-notify" class="settings-subpage hidden" data-motion-panel>
|
|
456
456
|
<div class="settings-group">
|
|
457
457
|
<div class="setting-row" data-settings-back style="cursor:pointer">
|
|
458
458
|
<button class="mini-btn" data-i18n="settings.back">‹ 返回</button>
|
|
@@ -496,7 +496,7 @@
|
|
|
496
496
|
</div>
|
|
497
497
|
</div>
|
|
498
498
|
|
|
499
|
-
<div id="settings-page-theme" class="settings-subpage hidden">
|
|
499
|
+
<div id="settings-page-theme" class="settings-subpage hidden" data-motion-panel>
|
|
500
500
|
<div class="settings-group">
|
|
501
501
|
<div class="setting-row" data-settings-back style="cursor:pointer">
|
|
502
502
|
<button class="mini-btn" data-i18n="settings.back">‹ 返回</button>
|
|
@@ -505,13 +505,13 @@
|
|
|
505
505
|
</div>
|
|
506
506
|
<div class="settings-group">
|
|
507
507
|
<div class="setting-row">
|
|
508
|
-
<div><div class="setting-name" data-i18n="settings.themeTitle">皮肤</div><div class="setting-desc" data-i18n="settings.themeDesc">默认深空 / 落日 / 易北爱乐厅 /
|
|
508
|
+
<div><div class="setting-name" data-i18n="settings.themeTitle">皮肤</div><div class="setting-desc" data-i18n="settings.themeDesc">默认深空 / 落日 / 易北爱乐厅 / 草原孤塔 / 黑曜白</div></div>
|
|
509
509
|
<button id="btn-theme" class="mini-btn" style="min-width:104px">默认深空</button>
|
|
510
510
|
</div>
|
|
511
511
|
</div>
|
|
512
512
|
</div>
|
|
513
513
|
|
|
514
|
-
<div id="settings-page-about" class="settings-subpage hidden">
|
|
514
|
+
<div id="settings-page-about" class="settings-subpage hidden" data-motion-panel>
|
|
515
515
|
<div class="settings-group">
|
|
516
516
|
<div class="setting-row" data-settings-back style="cursor:pointer">
|
|
517
517
|
<button class="mini-btn" data-i18n="settings.back">‹ 返回</button>
|
|
@@ -607,7 +607,7 @@
|
|
|
607
607
|
<span data-i18n="nav.stats">统计</span>
|
|
608
608
|
</button>
|
|
609
609
|
<button data-view="view-settings" class="nav-btn">
|
|
610
|
-
<span class="nav-ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><
|
|
610
|
+
<span class="nav-ico"><svg class="settings-gear" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.09a2 2 0 0 1 1 1.73v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.38a2 2 0 0 0-.73-2.73l-.15-.09a2 2 0 0 1-1-1.74v-.51a2 2 0 0 1 1-1.73l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2Z"/><circle cx="12" cy="12" r="3"/></svg></span>
|
|
611
611
|
<span data-i18n="nav.settings">设置</span>
|
|
612
612
|
</button>
|
|
613
613
|
</nav>
|
|
@@ -975,6 +975,7 @@
|
|
|
975
975
|
'stats.llmTime': '模型耗时', 'stats.minutes': ' 分钟', 'stats.outputCache': '输出 / 缓存读',
|
|
976
976
|
'stats.ctxPressure': '上下文压力', 'stats.permission': '权限', 'stats.empty': '暂无统计数据',
|
|
977
977
|
'goal.title': '目标', 'goal.pause': '暂停', 'goal.resume': '继续', 'goal.complete': '完成', 'goal.edit': '改目标', 'goal.clear': '清除',
|
|
978
|
+
'goal.collapse': '收起目标到侧边', 'goal.collapseShort': '收起', 'goal.expand': '展开目标', 'goal.collapsedHint': '仍在运行',
|
|
978
979
|
'goal.none': '当前会话没有目标', 'goal.confirmClear': '清除当前目标?(不会删除会话)', 'goal.confirmComplete': '将目标标记为完成?',
|
|
979
980
|
'goal.actionFailed': '目标操作失败', 'goal.actionSubmitted': '目标操作已提交',
|
|
980
981
|
'goal.cannotEmpty': '目标不能为空', 'goal.updateFailed': '更新失败', 'goal.updated': '目标已更新',
|
|
@@ -1087,8 +1088,8 @@
|
|
|
1087
1088
|
'settings.toolsTitle': '显示工具调用', 'settings.toolsDesc': '关闭后隐藏 🔧/📦 工具调用与结果',
|
|
1088
1089
|
'settings.mobileEnterTitle': '手机回车行为', 'settings.mobileEnterDesc': '选择手机端按 Enter 时换行或直接发送', 'settings.mobileEnterNewline': '回车换行', 'settings.mobileEnterSend': '回车发送',
|
|
1089
1090
|
'settings.langTitle': '语言 / Language', 'settings.langDesc': '界面语言,跟随系统或手动切换',
|
|
1090
|
-
'settings.themeTitle': '皮肤', 'settings.themeDesc': '默认深空 / 落日 / 易北爱乐厅 / 草原孤塔',
|
|
1091
|
-
'theme.default': '默认深空', 'theme.dark': '落日', 'theme.light': '易北爱乐厅', 'theme.neutral': '草原孤塔',
|
|
1091
|
+
'settings.themeTitle': '皮肤', 'settings.themeDesc': '默认深空 / 落日 / 易北爱乐厅 / 草原孤塔 / 黑曜白',
|
|
1092
|
+
'theme.default': '默认深空', 'theme.dark': '落日', 'theme.light': '易北爱乐厅', 'theme.neutral': '草原孤塔', 'theme.mono': '黑曜白',
|
|
1092
1093
|
'theme.panelTitle': '选择配色', 'theme.close': '关闭',
|
|
1093
1094
|
'settings.tokenTitle': '访问令牌', 'settings.change': '更换',
|
|
1094
1095
|
'settings.hostTitle': 'DSH 状态', 'settings.hostProbing': '探测中…', 'settings.probe': '探测', 'settings.probeFailed': '探测失败',
|
|
@@ -1204,6 +1205,7 @@
|
|
|
1204
1205
|
'stats.llmTime': 'LLM time', 'stats.minutes': ' min', 'stats.outputCache': 'Output / cache read',
|
|
1205
1206
|
'stats.ctxPressure': 'Context pressure', 'stats.permission': 'Permission', 'stats.empty': 'No stats yet',
|
|
1206
1207
|
'goal.title': 'Goal', 'goal.pause': 'Pause', 'goal.resume': 'Resume', 'goal.complete': 'Complete', 'goal.edit': 'Edit goal', 'goal.clear': 'Clear',
|
|
1208
|
+
'goal.collapse': 'Collapse goal to the side', 'goal.collapseShort': 'Collapse', 'goal.expand': 'Expand goal', 'goal.collapsedHint': 'Still running',
|
|
1207
1209
|
'goal.none': 'This session has no goal', 'goal.confirmClear': 'Clear the current goal? (the session stays)', 'goal.confirmComplete': 'Mark the goal as completed?',
|
|
1208
1210
|
'goal.actionFailed': 'Goal action failed', 'goal.actionSubmitted': 'Goal action submitted',
|
|
1209
1211
|
'goal.cannotEmpty': 'Goal cannot be empty', 'goal.updateFailed': 'Update failed', 'goal.updated': 'Goal updated',
|
|
@@ -1316,8 +1318,8 @@
|
|
|
1316
1318
|
'settings.toolsTitle': 'Show tool calls', 'settings.toolsDesc': 'Hide 🔧/📦 tool calls and results when off',
|
|
1317
1319
|
'settings.mobileEnterTitle': 'Mobile Enter behavior', 'settings.mobileEnterDesc': 'Choose whether Enter inserts a newline or sends', 'settings.mobileEnterNewline': 'Enter = newline', 'settings.mobileEnterSend': 'Enter = send',
|
|
1318
1320
|
'settings.langTitle': 'Language / 语言', 'settings.langDesc': 'UI language, follows system or switch manually',
|
|
1319
|
-
'settings.themeTitle': 'Theme', 'settings.themeDesc': '
|
|
1320
|
-
'theme.default': 'Default', 'theme.dark': 'Sunset', 'theme.light': 'Elbphilharmonie', 'theme.neutral': 'Prairie Tower',
|
|
1321
|
+
'settings.themeTitle': 'Theme', 'settings.themeDesc': 'Deep Space / Sunset / Elbphilharmonie / Prairie Tower / Monochrome',
|
|
1322
|
+
'theme.default': 'Default', 'theme.dark': 'Sunset', 'theme.light': 'Elbphilharmonie', 'theme.neutral': 'Prairie Tower', 'theme.mono': 'Monochrome',
|
|
1321
1323
|
'theme.panelTitle': 'Choose theme', 'theme.close': 'Close',
|
|
1322
1324
|
'settings.tokenTitle': 'Access token', 'settings.change': 'Change',
|
|
1323
1325
|
'settings.hostTitle': 'DSH status', 'settings.hostProbing': 'Probing…', 'settings.probe': 'Probe', 'settings.probeFailed': 'Probe failed',
|
package/public/motion.js
CHANGED
|
@@ -7,8 +7,21 @@
|
|
|
7
7
|
|
|
8
8
|
const reduceQuery = window.matchMedia?.('(prefers-reduced-motion: reduce)')
|
|
9
9
|
let reduced = !!reduceQuery?.matches
|
|
10
|
-
|
|
11
|
-
const
|
|
10
|
+
const activePulses = new Set()
|
|
11
|
+
const recentRuns = new WeakMap()
|
|
12
|
+
|
|
13
|
+
reduceQuery?.addEventListener?.('change', event => {
|
|
14
|
+
reduced = !!event.matches
|
|
15
|
+
if (!reduced) return
|
|
16
|
+
const animated = document.querySelectorAll('.view, .ds-view, [data-motion-view], .modal, .ds-modal, .sheet, .ds-drawer, [data-motion-panel]')
|
|
17
|
+
gsap.killTweensOf(animated)
|
|
18
|
+
clear(animated)
|
|
19
|
+
for (const node of activePulses) {
|
|
20
|
+
gsap.killTweensOf(node)
|
|
21
|
+
clear(node)
|
|
22
|
+
}
|
|
23
|
+
activePulses.clear()
|
|
24
|
+
})
|
|
12
25
|
|
|
13
26
|
function clear(targets) {
|
|
14
27
|
gsap.set(targets, { clearProps: 'opacity,visibility,transform,willChange' })
|
|
@@ -22,17 +35,32 @@
|
|
|
22
35
|
return items.map(motionKey).join('|')
|
|
23
36
|
}
|
|
24
37
|
|
|
38
|
+
function startedRecently(node, threshold = 48) {
|
|
39
|
+
const now = window.performance?.now?.() || Date.now()
|
|
40
|
+
const previous = recentRuns.get(node) || 0
|
|
41
|
+
recentRuns.set(node, now)
|
|
42
|
+
return previous > 0 && now - previous < threshold
|
|
43
|
+
}
|
|
44
|
+
|
|
25
45
|
function view(view) {
|
|
26
|
-
if (!view) return
|
|
27
|
-
const children = [...view.children].filter(child => !child.classList.contains('hidden')).slice(0,
|
|
28
|
-
|
|
46
|
+
if (!view || startedRecently(view)) return
|
|
47
|
+
const children = [...view.children].filter(child => !child.classList.contains('hidden')).slice(0, 8)
|
|
48
|
+
const targets = children.length ? children : [view]
|
|
49
|
+
gsap.killTweensOf([view, ...targets])
|
|
50
|
+
gsap.set(view, { autoAlpha: 1, clearProps: 'transform' })
|
|
29
51
|
if (reduced) {
|
|
30
|
-
clear([view, ...
|
|
52
|
+
clear([view, ...targets])
|
|
31
53
|
return
|
|
32
54
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
55
|
+
gsap.fromTo(targets, { opacity: 0.72, y: 8 }, {
|
|
56
|
+
opacity: 1,
|
|
57
|
+
y: 0,
|
|
58
|
+
duration: 0.24,
|
|
59
|
+
ease: 'power2.out',
|
|
60
|
+
stagger: { each: 0.035, from: 'start' },
|
|
61
|
+
overwrite: 'auto',
|
|
62
|
+
clearProps: 'opacity,visibility,transform,willChange'
|
|
63
|
+
})
|
|
36
64
|
}
|
|
37
65
|
|
|
38
66
|
function list(container, selector) {
|
|
@@ -420,8 +448,57 @@
|
|
|
420
448
|
registry.set(selector, state)
|
|
421
449
|
}
|
|
422
450
|
|
|
451
|
+
function panel(node) {
|
|
452
|
+
if (!node || startedRecently(node)) return
|
|
453
|
+
gsap.killTweensOf(node)
|
|
454
|
+
if (reduced) return clear(node)
|
|
455
|
+
gsap.fromTo(node, { autoAlpha: 0, y: 8 }, {
|
|
456
|
+
autoAlpha: 1,
|
|
457
|
+
y: 0,
|
|
458
|
+
duration: 0.2,
|
|
459
|
+
ease: 'power2.out',
|
|
460
|
+
overwrite: 'auto',
|
|
461
|
+
clearProps: 'opacity,visibility,transform,willChange'
|
|
462
|
+
})
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function popover(node) {
|
|
466
|
+
if (!node || startedRecently(node)) return
|
|
467
|
+
const opensUpward = !!(node.closest('.composer-wrap, .ds-composer') || node.style.bottom)
|
|
468
|
+
gsap.killTweensOf(node)
|
|
469
|
+
if (reduced) return clear(node)
|
|
470
|
+
gsap.fromTo(node, {
|
|
471
|
+
autoAlpha: 0,
|
|
472
|
+
y: opensUpward ? 7 : -7,
|
|
473
|
+
scale: 0.985,
|
|
474
|
+
transformOrigin: opensUpward ? 'bottom left' : 'top right'
|
|
475
|
+
}, {
|
|
476
|
+
autoAlpha: 1,
|
|
477
|
+
y: 0,
|
|
478
|
+
scale: 1,
|
|
479
|
+
duration: 0.18,
|
|
480
|
+
ease: 'power2.out',
|
|
481
|
+
overwrite: 'auto',
|
|
482
|
+
clearProps: 'opacity,visibility,transform,transformOrigin,willChange'
|
|
483
|
+
})
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function activate(node) {
|
|
487
|
+
if (!node || reduced || startedRecently(node, 90)) return
|
|
488
|
+
const target = node.querySelector('.nav-home-orb, .nav-ico, .ds-nav-ico') || node
|
|
489
|
+
gsap.killTweensOf(target)
|
|
490
|
+
gsap.fromTo(target, { scale: 0.9, y: 2 }, {
|
|
491
|
+
scale: 1,
|
|
492
|
+
y: 0,
|
|
493
|
+
duration: 0.22,
|
|
494
|
+
ease: 'power2.out',
|
|
495
|
+
overwrite: 'auto',
|
|
496
|
+
clearProps: 'transform,willChange'
|
|
497
|
+
})
|
|
498
|
+
}
|
|
499
|
+
|
|
423
500
|
function overlay(node) {
|
|
424
|
-
if (!node) return
|
|
501
|
+
if (!node || startedRecently(node)) return
|
|
425
502
|
const card = node.querySelector('.modal-card, .ds-modal-card, .sheet, .ds-drawer') || node
|
|
426
503
|
gsap.killTweensOf([node, card])
|
|
427
504
|
if (reduced) {
|
|
@@ -430,11 +507,18 @@
|
|
|
430
507
|
}
|
|
431
508
|
const isSideDrawer = card.classList.contains('ds-drawer')
|
|
432
509
|
const isSheet = card.classList.contains('sheet')
|
|
510
|
+
if (card === node) {
|
|
511
|
+
gsap.fromTo(card,
|
|
512
|
+
{ autoAlpha: 0, x: isSideDrawer ? 28 : 0, y: isSheet ? 24 : 8, scale: isSideDrawer || isSheet ? 1 : 0.985 },
|
|
513
|
+
{ autoAlpha: 1, x: 0, y: 0, scale: 1, duration: isSheet ? 0.24 : 0.2, ease: 'power3.out', overwrite: 'auto', clearProps: 'opacity,visibility,transform,willChange' }
|
|
514
|
+
)
|
|
515
|
+
return
|
|
516
|
+
}
|
|
433
517
|
const tl = gsap.timeline({ defaults: { ease: 'power3.out' } })
|
|
434
|
-
tl.fromTo(node, { autoAlpha: 0 }, { autoAlpha: 1, duration: 0.
|
|
518
|
+
tl.fromTo(node, { autoAlpha: 0 }, { autoAlpha: 1, duration: 0.14, overwrite: 'auto', clearProps: 'opacity,visibility' })
|
|
435
519
|
.fromTo(card,
|
|
436
|
-
{
|
|
437
|
-
{
|
|
520
|
+
{ x: isSideDrawer ? 28 : 0, y: isSheet ? 24 : 8, scale: isSideDrawer || isSheet ? 1 : 0.985 },
|
|
521
|
+
{ x: 0, y: 0, scale: 1, duration: isSheet ? 0.24 : 0.2, overwrite: 'auto', clearProps: 'transform,willChange' },
|
|
438
522
|
'<'
|
|
439
523
|
)
|
|
440
524
|
}
|
|
@@ -449,10 +533,10 @@
|
|
|
449
533
|
}
|
|
450
534
|
}
|
|
451
535
|
|
|
452
|
-
window.DshMotion = { gsap, reduced: () => reduced, view, list, relayout, bindLongPressReorder, overlay, pulse }
|
|
536
|
+
window.DshMotion = { gsap, reduced: () => reduced, view, list, relayout, bindLongPressReorder, overlay, panel, popover, activate, pulse }
|
|
453
537
|
|
|
454
538
|
document.addEventListener('DOMContentLoaded', () => {
|
|
455
|
-
document.querySelectorAll('.view:not(.hidden), .ds-view:not(.hidden)').forEach(node => view(node))
|
|
539
|
+
document.querySelectorAll('.view:not(.hidden), .ds-view:not(.hidden), [data-motion-view]:not(.hidden)').forEach(node => view(node))
|
|
456
540
|
}, { once: true })
|
|
457
541
|
|
|
458
542
|
const observer = new MutationObserver(records => {
|
|
@@ -460,9 +544,13 @@
|
|
|
460
544
|
const node = record.target
|
|
461
545
|
if (!(node instanceof HTMLElement)) continue
|
|
462
546
|
const becameVisible = record.oldValue?.split(/\s+/).includes('hidden') && !node.classList.contains('hidden')
|
|
547
|
+
const becameActive = !record.oldValue?.split(/\s+/).includes('active') && node.classList.contains('active')
|
|
548
|
+
if (becameActive && node.matches('.nav-btn, .ds-nav-item')) activate(node)
|
|
463
549
|
if (!becameVisible) continue
|
|
464
|
-
if (node.matches('.view, .ds-view')) view(node)
|
|
465
|
-
else if (node.matches('.modal, .ds-modal, .sheet, .ds-drawer')) overlay(node)
|
|
550
|
+
if (node.matches('.view, .ds-view, [data-motion-view]')) view(node)
|
|
551
|
+
else if (node.matches('.modal, .ds-modal, .sheet-backdrop, .sheet, .ds-drawer')) overlay(node)
|
|
552
|
+
else if (node.matches('.composer-menu, .ds-group-menu, .ds-feedback-menu, .fb-menu')) popover(node)
|
|
553
|
+
else if (node.matches('[data-motion-panel], .wb-panel, .ds-wb-panel, .doctor-body, .settings-subpage')) panel(node)
|
|
466
554
|
}
|
|
467
555
|
})
|
|
468
556
|
observer.observe(document.documentElement, { subtree: true, attributes: true, attributeFilter: ['class'], attributeOldValue: true })
|
package/public/plugin.html
CHANGED
|
@@ -4,19 +4,19 @@
|
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
6
6
|
<title>DSH Remote</title>
|
|
7
|
-
<script>/* 插件抽屉首帧沿用当前主题 */ (function(){try{var t=localStorage.getItem('dshTheme');if(t!=='default'&&t!=='dark'&&t!=='light'&&t!=='neutral'){t=window.matchMedia('(prefers-color-scheme: light)').matches?'light':'default'}document.documentElement.setAttribute('data-theme',t)}catch(e){}})()</script>
|
|
7
|
+
<script>/* 插件抽屉首帧沿用当前主题 */ (function(){try{var t=localStorage.getItem('dshTheme');if(t!=='default'&&t!=='dark'&&t!=='light'&&t!=='neutral'&&t!=='mono'){t=window.matchMedia('(prefers-color-scheme: light)').matches?'light':'default'}document.documentElement.setAttribute('data-theme',t)}catch(e){}})()</script>
|
|
8
8
|
<link rel="stylesheet" href="theme-vars.css">
|
|
9
9
|
<style>
|
|
10
10
|
:root { color-scheme: dark; }
|
|
11
11
|
html[data-theme="light"], html[data-theme="neutral"] { color-scheme: light; }
|
|
12
|
-
* { box-sizing: border-box; }
|
|
12
|
+
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
|
|
13
13
|
body {
|
|
14
14
|
margin: 0; min-width: 280px; color: var(--dsr-text); background:
|
|
15
15
|
radial-gradient(420px 240px at 100% 0, var(--dsr-accent-soft), transparent 72%),
|
|
16
16
|
var(--dsr-bg); font: 13px/1.45 system-ui, -apple-system, "PingFang SC", "Noto Sans CJK SC", sans-serif;
|
|
17
17
|
}
|
|
18
18
|
button, a { font: inherit; }
|
|
19
|
-
button { cursor: pointer; }
|
|
19
|
+
button { cursor: pointer; touch-action: manipulation; }
|
|
20
20
|
.plugin-shell { min-height: 100vh; padding: 18px 16px 16px; display: flex; flex-direction: column; gap: 14px; }
|
|
21
21
|
.plugin-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
|
|
22
22
|
.plugin-brand { display: flex; align-items: center; gap: 9px; min-width: 0; }
|
|
@@ -41,8 +41,9 @@
|
|
|
41
41
|
.plugin-primary:hover, .plugin-primary:focus-visible { filter: brightness(1.08); transform: translateY(-1px); }
|
|
42
42
|
.plugin-primary:disabled { cursor: wait; opacity: .65; transform: none; }
|
|
43
43
|
.plugin-actions { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
|
|
44
|
-
.plugin-action { min-height: 42px; padding: 7px 5px; border: 1px solid var(--dsr-line); border-radius: 12px; background: var(--dsr-panel); color: var(--dsr-text); font-size: 11px; }
|
|
44
|
+
.plugin-action { min-height: 42px; padding: 7px 5px; border: 1px solid var(--dsr-line); border-radius: 12px; background: var(--dsr-panel); color: var(--dsr-text); font-size: 11px; transition: transform .18s ease, background-color .18s ease, border-color .18s ease; }
|
|
45
45
|
.plugin-action:hover, .plugin-action:focus-visible { border-color: var(--dsr-accent-line); background: var(--dsr-accent-soft); }
|
|
46
|
+
.plugin-action:active { transform: scale(.98); }
|
|
46
47
|
.plugin-action svg { display: block; width: 17px; height: 17px; margin: 0 auto 3px; color: var(--dsr-accent-strong); }
|
|
47
48
|
.plugin-usage { padding: 14px; border: 1px solid var(--dsr-line); border-radius: 16px; background: linear-gradient(145deg, var(--dsr-panel), var(--dsr-bg-2)); }
|
|
48
49
|
.plugin-section-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; color: var(--dsr-text); font-size: 12px; font-weight: 750; }
|
|
@@ -87,7 +88,7 @@
|
|
|
87
88
|
</style>
|
|
88
89
|
</head>
|
|
89
90
|
<body>
|
|
90
|
-
<main class="plugin-shell">
|
|
91
|
+
<main class="plugin-shell" data-motion-view>
|
|
91
92
|
<header class="plugin-head">
|
|
92
93
|
<div class="plugin-brand">
|
|
93
94
|
<span class="plugin-brand-mark" aria-hidden="true"></span>
|