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.
@@ -645,3 +645,104 @@ input:focus-visible, select:focus-visible, textarea:focus-visible {
645
645
  .ds-overview-metrics { grid-template-columns:repeat(2,minmax(0,1fr)); }
646
646
  .ds-overview-actions .ds-btn { padding-left:5px; padding-right:5px; }
647
647
  }
648
+
649
+ /* 关于页贡献者致谢:随页面提供名单,离线可见,按贡献提交数排序。 */
650
+ .contributors-thanks { margin: 6px 0 12px; color: var(--dsr-muted); font-size: 12px; line-height: 1.6; }
651
+ .contributors-list { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; margin: 0 0 10px; padding: 0; }
652
+ .contributors-list li { min-width: 0; max-width: 100%; }
653
+ .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; }
654
+ .contributors-list a:hover { border-color: var(--dsr-accent-line); color: var(--dsr-accent-strong); }
655
+ .contributors-all { display: inline-flex; align-items: center; min-height: 44px; color: var(--dsr-accent-strong); font-size: 12px; text-underline-offset: 3px; }
656
+ .contributors-section a:focus-visible { outline: 2px solid var(--dsr-accent-strong); outline-offset: 3px; }
657
+
658
+ /* 会话操作统一入口;标题与工作区保持独立阅读空间。 */
659
+ .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); }
660
+ .session-actions-dialog::backdrop,.session-info-dialog::backdrop { background:var(--dsr-overlay); }
661
+ .session-dialog-head { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:12px; }
662
+ .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; }
663
+ .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; }
664
+ .session-actions-dialog>button:hover { background:var(--dsr-accent-soft); }
665
+ .session-actions-dialog>button svg { display:none; }
666
+ .session-actions-dialog #btn-archive-session { color:var(--dsr-warning); border-top:1px solid var(--dsr-line); margin-top:12px; }
667
+ .session-info-dialog dl { margin:0 0 16px; }
668
+ .session-info-dialog dt { margin-top:14px; color:var(--dsr-muted); font-size:12px; }
669
+ .session-info-dialog dd { margin:4px 0 0; white-space:pre-wrap; overflow-wrap:anywhere; line-height:1.6; }
670
+ .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; }
671
+ .session-actions-dialog button:focus-visible,.session-info-dialog button:focus-visible { outline:2px solid var(--dsr-accent); outline-offset:2px; }
672
+
673
+ .ds-topbar { height:auto; min-height:64px; padding-top:8px; padding-bottom:8px; }
674
+ .ds-title-block { flex:1; min-width:0; }
675
+ .ds-title { display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; width:100%; padding:0; border:0; background:transparent; color:inherit; font:inherit; font-weight:700; text-align:left; white-space:normal; overflow-wrap:anywhere; cursor:pointer; }
676
+ .ds-workspace { display:block; max-width:100%; padding:3px 0 0; border:0; background:transparent; color:var(--dsr-muted); font:inherit; font-size:11px; text-align:left; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; cursor:pointer; }
677
+ @media(max-width:720px) { .ds-top-right .ds-server { display:none; } .ds-title { font-size:14px; } }
678
+
679
+ .session-info-dialog>button:disabled { opacity:.45; cursor:default; }
680
+
681
+ /* 有界历史视图:保留消息节点,窗口外记录不参与布局。 */
682
+ .history-entry { flex:0 0 auto; min-width:0; overflow-anchor:none; display:flow-root; }
683
+ .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; }
684
+ .history-page-control[hidden],.history-live:empty { display:none; }
685
+ .history-empty { padding:24px 8px; text-align:center; color:var(--dsr-muted); }
686
+ .history-source { color:var(--dsr-muted); font-size:11px; font-weight:400; letter-spacing:0; }
687
+ .history-meta { display:flex; align-items:center; gap:8px; min-width:0; flex-wrap:wrap; justify-content:flex-end; }
688
+ .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; }
689
+
690
+ .ds-history-wrap { flex:1; min-height:0; position:relative; display:flex; }
691
+ .ds-history { overflow-anchor:none; }
692
+ #history-source { flex:none; padding:5px 22px; }
693
+
694
+ .crash-preview {margin:10px 0;font-size:13px;}
695
+ .crash-preview summary {cursor:pointer;min-height:44px;display:flex;align-items:center;}
696
+ .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;}
697
+ /* Keep feedback fields readable and actions visible when log details expand. */
698
+ #modal-feedback .ds-modal-card { display:flex; flex-direction:column; max-height:calc(100dvh - 48px); overflow:hidden; }
699
+ #modal-feedback .ds-modal-title,#modal-feedback .ds-modal-actions { flex-shrink:0; }
700
+ #modal-feedback .ds-modal-body { min-height:0; max-height:none; overflow-y:auto; overscroll-behavior:contain; }
701
+ #modal-feedback .ds-modal-body>* { flex-shrink:0; }
702
+ #modal-feedback .ds-modal-actions .ds-btn { min-height:44px; }
703
+
704
+ /* Link instrument: persistent geometry, compositor sweep and interruptible state transitions. */
705
+ .ds-overview-pulse-card { --pulse-ease:cubic-bezier(0.23,1,0.32,1); transition:border-color 240ms ease; }
706
+ .ds-overview-pulse-copy,.ds-overview-link { position:relative; }
707
+ .ds-overview-status { line-height:1.25; min-block-size:1.25em; }
708
+ .ds-overview-status-desc { line-height:1.4; min-block-size:2.8em; }
709
+ .ds-overview-pulse-card.status-checking { border-color:var(--dsr-accent-line); }
710
+ .ds-overview-pulse-card.status-checking .ds-overview-primary-action { border-color:var(--dsr-accent-strong); background:var(--dsr-accent-strong); color:var(--dsr-on-accent); }
711
+ .ds-overview-primary-action { transition:transform 160ms var(--pulse-ease),background-color 240ms ease,border-color 240ms ease,color 240ms ease; }
712
+ .ds-system-pulse-ring { width:136px; height:136px; padding:0; background:none; box-shadow:none; isolation:isolate; flex:none; }
713
+ .ds-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)); }
714
+ .ds-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; }
715
+ .ds-overview-pulse-card.status-checking .ds-system-pulse-ring::after { opacity:.55; transform:scale(1); }
716
+ .ds-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; }
717
+ .ds-system-pulse-ring .pulse-dial path { opacity:.15; transition:opacity 240ms var(--pulse-ease),stroke 240ms ease; }
718
+ .ds-system-pulse-ring .pulse-dial [data-confirmed="true"] { opacity:1; }
719
+ .ds-overview-pulse-card.status-nominal .ds-system-pulse-ring .pulse-dial [data-confirmed="true"] { stroke:var(--dsr-success); }
720
+ .ds-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; }
721
+ .ds-overview-pulse-card[data-pulse-running="true"] .pulse-sweep { animation-play-state:running; }
722
+ .ds-overview-pulse-card.status-checking .pulse-sweep { opacity:1; }
723
+ .ds-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; }
724
+ .ds-system-pulse-ring .pulse-reticle::before,.ds-system-pulse-ring .pulse-reticle::after { content:""; position:absolute; width:1px; height:5px; left:50%; background:var(--dsr-accent); opacity:.65; }
725
+ .ds-system-pulse-ring .pulse-reticle::before { top:-3px; }
726
+ .ds-system-pulse-ring .pulse-reticle::after { bottom:-3px; }
727
+ .ds-system-pulse-core { position:absolute; inset:24%; width:auto; height:auto; background:var(--dsr-bg-2); z-index:1; }
728
+ .ds-system-pulse-core strong { font-size:15px; letter-spacing:.6px; transition:color 240ms ease; }
729
+ .ds-system-pulse-core span { font-family:ui-monospace,SFMono-Regular,Consolas,monospace; font-variant-numeric:tabular-nums; font-size:9px; letter-spacing:.5px; }
730
+ .ds-overview-pulse-card.status-nominal .ds-system-pulse-core strong { color:var(--dsr-success); }
731
+ .ds-overview-link i { transition:background-color 240ms ease,box-shadow 240ms ease; }
732
+ .ds-overview-link.pending i { background:var(--dsr-accent); animation:overview-detect 2.5s ease-in-out infinite; }
733
+ .ds-overview-link:nth-child(2) i { animation-delay:-.15s; }
734
+ .ds-overview-link:nth-child(3) i { animation-delay:-.3s; }
735
+ .ds-overview-link:nth-child(4) i { animation-delay:-.45s; }
736
+ .ds-overview-link.pending b { color:var(--dsr-muted); }
737
+ .ds-overview-refresh:disabled { cursor:progress; opacity:.6; }
738
+ .pulse-text-echo { position:absolute!important; pointer-events:none; user-select:none; z-index:2; }
739
+ @keyframes overview-scan { to { transform:rotate(360deg); } }
740
+ @keyframes overview-detect { 50% { opacity:.4; } }
741
+ @media(prefers-reduced-motion:reduce) {
742
+ .ds-overview-pulse-card,.ds-overview-pulse-card .pulse-dial path,.ds-overview-pulse-card .pulse-reticle,.ds-overview-pulse-card .pulse-sweep,.ds-overview-pulse-card .ds-system-pulse-ring::after,.ds-overview-pulse-card .ds-overview-link i,.ds-overview-pulse-card .ds-system-pulse-core strong,.ds-overview-pulse-card .ds-overview-primary-action { transition:none; }
743
+ .ds-overview-pulse-card .pulse-sweep,.ds-overview-pulse-card .ds-overview-link.pending i { animation:none; }
744
+ .ds-overview-pulse-card.status-checking .pulse-sweep { opacity:.4; }
745
+ .ds-overview-pulse-card .pulse-text-echo { display:none; }
746
+ }
747
+
748
+ @media(max-width:600px) { .ds-system-pulse-ring { width:108px; height:108px; } .ds-overview-status { font-size:20px; } html[lang="en"] .ds-overview-status { min-block-size:2.5em; } }
@@ -54,6 +54,7 @@
54
54
  </div>
55
55
  <button class="ds-nav-item" data-view="view-overview"><span class="ds-nav-ico">◌</span><span data-i18n="ds.overview">主页</span></button>
56
56
  <button class="ds-nav-item" data-view="view-files"><span class="ds-nav-ico">⇅</span><span data-i18n="ds.files">文件传输</span></button>
57
+ <button class="ds-nav-item" data-view="view-plugins"><span class="ds-nav-ico" aria-hidden="true">▦</span><span data-i18n="ds.plugins">插件</span></button>
57
58
  <button class="ds-nav-item" data-view="view-settings"><span class="ds-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><span data-i18n="ds.settings">设置</span></button>
58
59
  <div class="ds-feedback">
59
60
  <button id="btn-feedback" class="ds-feedback-btn" aria-haspopup="menu" aria-expanded="false" data-i18n-title="ds.feedback" data-i18n-aria="ds.feedback">
@@ -103,16 +104,13 @@
103
104
  <div class="ds-main">
104
105
  <header class="ds-topbar">
105
106
  <button id="btn-mobile-nav" class="ds-icon-btn" aria-controls="ds-sidebar" aria-expanded="false" data-i18n-aria="ds.menu" data-i18n-title="ds.menu"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h16"/></svg></button>
106
- <div class="ds-title" id="ds-title" data-i18n="ds.sessions">会话</div>
107
+ <div class="ds-title-block"><button class="ds-title" id="ds-title" type="button" aria-haspopup="dialog" aria-controls="session-info" data-i18n="ds.sessions">会话</button><button id="ds-workspace" class="ds-workspace hidden" type="button" aria-haspopup="dialog" aria-controls="session-info"></button></div>
107
108
  <span id="ds-session-status" class="ds-session-status hidden"></span>
108
- <button id="btn-rename-session" class="ds-icon-btn hidden" data-i18n-title="ds.sessionRename" data-i18n-aria="ds.sessionRename">✎</button>
109
- <button id="btn-archive-session" class="ds-btn hidden" data-i18n="ds.sessionArchive">归档</button>
109
+
110
110
  <div class="ds-top-right">
111
- <button id="btn-insights" class="ds-icon-btn hidden" data-i18n-title="ds.insights" data-i18n-aria="ds.insights"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><circle cx="12" cy="12" r="8"/><path d="M12 4v8l6 5M12 12H4"/></svg></button>
111
+ <button id="btn-session-more" class="ds-icon-btn" type="button" aria-haspopup="dialog" aria-controls="session-actions" data-i18n-aria="ds.sessionMore" aria-label="更多操作">⋯</button>
112
112
  <span class="ds-conn" id="conn-badge" title="">●</span>
113
113
  <span class="ds-server" id="server-badge"></span>
114
- <button id="btn-donate" class="ds-icon-btn" data-i18n-aria="ds.donate" data-i18n-title="ds.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>
115
- <button id="btn-stats-top" class="ds-icon-btn" data-i18n-aria="ds.stats" data-i18n-title="ds.stats"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 19V9M12 19V5M19 19v-7M3 19h18"/></svg></button>
116
114
  </div>
117
115
  </header>
118
116
 
@@ -131,11 +129,11 @@
131
129
  <div class="ds-overview-pulse-card">
132
130
  <div class="ds-overview-pulse-copy">
133
131
  <div class="ds-overview-eyebrow" data-i18n="ds.systemPulse">系统链路</div>
134
- <div id="ds-overview-status" class="ds-overview-status" data-i18n="ds.checking">检查中…</div>
132
+ <div id="ds-overview-status" aria-live="polite" class="ds-overview-status" data-i18n="ds.checking">检查中…</div>
135
133
  <div id="ds-overview-status-desc" class="ds-overview-status-desc">—</div>
136
134
  <button id="ds-overview-primary-action" class="ds-overview-primary-action" type="button" data-i18n="ds.action.openSession">打开最近会话</button>
137
135
  </div>
138
- <div id="ds-overview-pulse-ring" class="ds-system-pulse-ring" style="--pulse-pct:0%"><div class="ds-system-pulse-core"><strong id="ds-overview-health">—</strong><span id="ds-overview-health-caption">—</span></div></div>
136
+ <div id="ds-overview-pulse-ring" class="ds-system-pulse-ring"><svg class="pulse-dial" viewBox="0 0 120 120" aria-hidden="true" focusable="false"><path data-pulse-link="gateway" data-confirmed="false" d="M 66.093 10.373 A 50 50 0 0 1 109.627 53.907"/><path data-pulse-link="dsh" data-confirmed="false" d="M 109.627 66.093 A 50 50 0 0 1 66.093 109.627"/><path data-pulse-link="mux" data-confirmed="false" d="M 53.907 109.627 A 50 50 0 0 1 10.373 66.093"/><path data-pulse-link="host" data-confirmed="false" d="M 10.373 53.907 A 50 50 0 0 1 53.907 10.373"/></svg><span class="pulse-sweep" aria-hidden="true"></span><span class="pulse-reticle" aria-hidden="true"></span><div class="ds-system-pulse-core"><strong id="ds-overview-health">—</strong><span id="ds-overview-health-caption">—</span></div></div>
139
137
  <div class="ds-overview-links">
140
138
  <div class="ds-overview-link" data-ds-overview-link="gateway"><i></i><span data-i18n="ds.gateway">网关</span><b>—</b></div>
141
139
  <div class="ds-overview-link" data-ds-overview-link="dsh"><i></i><span data-i18n="ds.dshUpstream">DSH 上游</span><b>—</b></div>
@@ -170,7 +168,8 @@
170
168
  <div id="session-cards" class="ds-session-cards"></div>
171
169
  <div id="queue-dock" class="ds-queue-dock hidden" aria-live="polite"></div>
172
170
  <section id="session-pending" class="ds-session-pending hidden" aria-live="polite"></section>
173
- <div id="history" class="ds-history" aria-live="polite"></div>
171
+ <div id="history-source" class="history-source" role="status" aria-live="polite"></div>
172
+ <div class="ds-history-wrap"><div id="history" class="ds-history"></div><button id="btn-history-latest" class="history-latest hidden" type="button" data-i18n="ds.historyLatest">回到最新</button></div>
174
173
  <div class="ds-composer">
175
174
  <div id="composer-status" class="ds-composer-status hidden" role="status" aria-live="polite"><span class="ds-composer-status-dot" aria-hidden="true"></span><span id="composer-status-text" data-i18n="ds.composerRunning">运行中…</span></div>
176
175
  <textarea id="composer" rows="1" data-i18n-placeholder="ds.composerPlaceholder" placeholder="输入消息…"></textarea>
@@ -218,10 +217,10 @@
218
217
  </section>
219
218
 
220
219
  <!-- 设置 -->
220
+ <section id="view-plugins" class="ds-view hidden"><div id="desktop-plugin-page"></div></section>
221
221
  <section id="view-settings" class="ds-view hidden">
222
222
  <div class="ds-section-label" data-i18n="ds.settings">设置</div>
223
223
  <div id="settings-home">
224
- <button type="button" id="btn-plugin-center" class="ds-setting-row ds-setting-link"><span>插件中心 · 已安装 / 发现插件</span><span>›</span></button>
225
224
  <div class="ds-settings">
226
225
  <button type="button" class="ds-setting-row ds-setting-link" data-settings-group="general">
227
226
  <div><div class="ds-setting-name" data-i18n="ds.groupGeneral">通用</div><div class="ds-setting-desc" data-i18n="ds.groupGeneralDesc">工具调用、预设提示词</div></div>
@@ -307,6 +306,20 @@
307
306
  <div><div class="ds-setting-name" data-i18n="ds.donate">赞赏支持</div><div class="ds-setting-desc" data-i18n="ds.donateDesc">如果 dsh-remote 帮到了你,欢迎赞赏支持开发 ☕</div></div>
308
307
  <span class="ds-setting-arrow" aria-hidden="true">›</span>
309
308
  </div>
309
+ <section class="ds-setting-row contributors-section" aria-labelledby="contributors-title">
310
+ <div>
311
+ <div id="contributors-title" class="ds-setting-name" data-i18n="ds.contributors">贡献者</div>
312
+ <p class="contributors-thanks" data-i18n="ds.contributorsThanks">感谢每一位让 DSH Remote 变得更好的贡献者。</p>
313
+ <!-- GitHub contributions 降序;同数量按用户名排序。更新名单时核对仓库 contributors API。 -->
314
+ <ul class="contributors-list">
315
+ <li><a href="https://github.com/Blank-not-black" target="_blank" rel="noopener noreferrer">Blank-not-black</a></li>
316
+ <li><a href="https://github.com/wikkd" target="_blank" rel="noopener noreferrer">wikkd</a></li>
317
+ <li><a href="https://github.com/anupamme" target="_blank" rel="noopener noreferrer">anupamme</a></li>
318
+ <li><a href="https://github.com/liuchang-t" target="_blank" rel="noopener noreferrer">liuchang-t</a></li>
319
+ </ul>
320
+ <a class="contributors-all" href="https://github.com/Blank-not-black/dsh-Remote/graphs/contributors" target="_blank" rel="noopener noreferrer" data-i18n="ds.contributorsAll">查看完整贡献记录 ↗</a>
321
+ </div>
322
+ </section>
310
323
  </div>
311
324
  </section>
312
325
 
@@ -347,6 +360,24 @@
347
360
  </div>
348
361
  </div>
349
362
 
363
+
364
+ <dialog id="session-actions" class="session-actions-dialog" aria-labelledby="session-actions-title">
365
+ <div class="session-dialog-head"><strong id="session-actions-title" data-i18n="ds.sessionMore">更多操作</strong><button type="button" data-session-dialog-close aria-label="关闭">×</button></div>
366
+ <button id="btn-rename-session" class="ds-icon-btn hidden" data-i18n-title="ds.sessionRename" data-i18n-aria="ds.sessionRename" data-i18n="ds.sessionRename">✎</button>
367
+ <button id="btn-insights" class="ds-icon-btn hidden" data-i18n-title="ds.insights" data-i18n-aria="ds.insights" data-i18n="ds.insights">上下文与费用</button>
368
+
369
+ <button id="btn-donate" class="ds-icon-btn" data-i18n-aria="ds.donate" data-i18n-title="ds.donate" data-i18n="ds.donate">赞赏支持</button>
370
+
371
+ <button id="btn-stats-top" class="ds-icon-btn" data-i18n-aria="ds.stats" data-i18n-title="ds.stats" data-i18n="ds.stats">统计</button>
372
+
373
+ <button id="btn-session-info" type="button" data-i18n="ds.sessionDetails">会话详情</button>
374
+ <button id="btn-archive-session" class="ds-btn hidden" data-i18n="ds.sessionArchive">归档</button>
375
+ </dialog>
376
+ <dialog id="session-info" class="session-info-dialog" aria-labelledby="session-info-title">
377
+ <div class="session-dialog-head"><strong id="session-info-title" data-i18n="ds.sessionDetails">会话详情</strong><button type="button" data-session-dialog-close aria-label="关闭">×</button></div>
378
+ <dl><dt data-i18n="ds.fullTitle">完整标题</dt><dd id="session-info-name"></dd><dt data-i18n="ds.workspace">工作区</dt><dd id="session-info-path"></dd><dt data-i18n="ds.sessionId">会话 ID</dt><dd id="session-info-id"></dd></dl>
379
+ <button id="btn-copy-session-workspace" type="button" data-i18n="ds.copyWorkspace">复制工作区路径</button>
380
+ </dialog>
350
381
  <div id="modal-rename" class="ds-modal hidden" role="dialog" aria-modal="true" aria-labelledby="rename-session-title">
351
382
  <div class="ds-modal-card">
352
383
  <div id="rename-session-title" class="ds-modal-title" data-i18n="ds.sessionRenameTitle">重命名会话</div>
@@ -373,6 +404,8 @@
373
404
  <textarea id="fb-msg" class="ds-fb-textarea" rows="5" maxlength="2000" data-i18n-placeholder="ds.feedbackMessagePlaceholder" placeholder="请描述遇到的问题或建议(必填,≤2000 字)"></textarea>
374
405
  <input id="fb-contact" class="ds-fb-input" maxlength="200" data-i18n-placeholder="ds.feedbackContactPlaceholder" placeholder="联系方式(可选):邮箱 / 微信 / B站 ID">
375
406
  <label class="ds-fb-diagnostics"><input id="fb-include-diagnostics" type="checkbox"> <span data-i18n="ds.feedbackIncludeDiagnostics">附带兼容性诊断日志(可选)</span><small data-i18n="ds.feedbackDiagnosticsPrivacy">仅上传网关/协议版本、接口失败摘要和实时链路状态;不包含令牌、Cookie、对话内容或文件路径。</small></label>
407
+ <label class="ds-fb-diagnostics"><input id="fb-include-crashes" type="checkbox"> <span data-i18n="ds.feedbackIncludeCrashes">附带崩溃日志摘要(可选)</span><small data-i18n="ds.feedbackCrashPrivacy">默认仅在本机记录。勾选后上传最近 3 条退出、重启和错误位置摘要,不含对话、密钥或完整路径。</small></label>
408
+ <details id="fb-crash-details" class="crash-preview"><summary data-i18n="ds.feedbackCrashPreview">查看拟上传的日志摘要</summary><pre id="fb-crash-preview"></pre></details>
376
409
  </div>
377
410
  <div class="ds-modal-actions">
378
411
  <button id="fb-cancel" class="ds-btn" data-i18n="ds.feedbackCancel">取消</button>
@@ -495,13 +528,13 @@
495
528
  <script>
496
529
  window.DESKTOP_STR = {
497
530
  zh: {
498
- 'ds.repo': 'GitHub 仓库', 'ds.newSession': '+ 新会话', 'ds.sessionUntitled': '新会话', 'ds.newWorkspace': '新建工作区', 'ds.sessions': '会话', 'ds.overview': '主页', 'ds.overviewEyebrow': 'SYSTEM OVERVIEW', 'ds.overviewSubtitle': '实时状态与需要你处理的事项',
499
- 'ds.systemPulse': '系统链路', 'ds.systemNominal': '系统链路正常', 'ds.systemDegraded': '部分链路需要关注', 'ds.systemOffline': '连接已断开', 'ds.checking': '检查中…', 'ds.components': '{n}/4 个模块在线', 'ds.live': 'LIVE', 'ds.allLinked': '全部链路', 'ds.offlineCore': 'OFFLINE',
531
+ 'ds.repo': 'GitHub 仓库', 'ds.newSession': '+ 新会话', 'ds.sessionUntitled': '新会话', 'ds.newWorkspace': '新建工作区', 'ds.sessionMore': '更多操作', 'ds.sessionDetails': '会话详情', 'ds.workspace': '工作区', 'ds.noWorkspace': '未关联工作区', 'ds.copyWorkspace': '复制工作区路径', 'ds.sessionId': '会话 ID', 'ds.fullTitle': '完整标题', 'ds.copied': '已复制', 'ds.sessions': '会话', 'ds.overview': '主页', 'ds.overviewEyebrow': 'SYSTEM OVERVIEW', 'ds.overviewSubtitle': '实时状态与需要你处理的事项',
532
+ 'ds.systemPulse': '系统链路', 'ds.systemNominal': '系统链路正常', 'ds.systemDegraded': '部分链路需要关注', 'ds.systemOffline': '连接已断开', 'ds.checking': '检查中…', 'ds.confirmedShort': '已确认', 'ds.checkingShort': '检测中', 'ds.autoChecking': '正在自动检测,已确认 {n}/4 项', 'ds.autoRetry': '{n}/4 项在线,将自动复查未连接的链路', 'ds.components': '{n}/4 个模块在线', 'ds.live': 'LIVE', 'ds.allLinked': '全部链路', 'ds.offlineCore': 'OFFLINE',
500
533
  'ds.gateway': '网关', 'ds.dshUpstream': 'DSH 上游', 'ds.mux': '实时 mux', 'ds.host': '实时 host', 'ds.online': '在线', 'ds.offlineShort': '离线',
501
534
  'ds.attention': '需要你处理', 'ds.runtime': '运行指标', 'ds.recentActivity': '近期活动', 'ds.dshVersion': 'DSH 版本', 'ds.gatewayStatus': '网关状态', 'ds.activeSessions': '活跃会话', 'ds.connectionMode': '连接模式', 'ds.liveWs': '实时 WS', 'ds.poll': '降级轮询', 'ds.running': '运行中', 'ds.noReason': '无说明', 'ds.noActivity': '暂无后台活动', 'ds.noSessions': '暂无会话', 'ds.refreshOverview': '刷新总览', 'ds.pendingCount': '{n} 项待处理', 'ds.activeCount': '{n} 个活跃会话', 'ds.nothingPending': '暂无事项', 'ds.action.openSession': '打开最近会话', 'ds.action.newSession': '新建会话', 'ds.action.attention': '处理待办', 'ds.action.connect': '去设置连接', 'ds.action.refresh': '重新检查连接',
502
535
  'ds.sortLabel': '会话排序', 'ds.sortTime': '按时间', 'ds.sortWorkspace': '按工作区', 'ds.workspace': '工作区', 'ds.workspaceUnknown': '未指定工作区', 'ds.wbOrderSaved': '顺序已保存到当前设备',
503
536
  'ds.insights': '上下文与费用',
504
- 'ds.files': '文件传输', 'ds.settings': '设置', 'ds.stats': '统计', 'ds.menu': '导航',
537
+ 'ds.files': '文件传输', 'ds.plugins': '插件', 'ds.settings': '设置', 'ds.stats': '统计', 'ds.menu': '导航',
505
538
  'ds.send': '发送', 'ds.composerPlaceholder': '输入消息,Enter 发送…', 'ds.composerRunning': '运行中…', 'ds.presets': '预设', 'ds.sessionRename': '重命名会话', 'ds.sessionRenameTitle': '重命名会话', 'ds.sessionRenamePlaceholder': '输入新的会话名称', 'ds.sessionRenameConfirm': '保存', 'ds.sessionRenameFailed': '重命名失败', 'ds.sessionRenameEmpty': '会话名称不能为空', 'ds.sessionRenamed': '会话名称已更新', 'ds.sessionArchive': '归档', 'ds.sessionArchiveConfirm': '归档这个会话?归档后可在“显示已归档”中打开。', 'ds.sessionArchived': '会话已归档', 'ds.sessionStop': '停止本轮', 'ds.sessionStopConfirm': '停止当前回合?排队中的消息不会被删除。', 'ds.sessionStopFailed': '停止失败', 'ds.sessionStopRequested': '已请求停止本轮', 'ds.sessionRecovering': '恢复会话中…', 'ds.sessionReady': '会话已恢复', 'ds.sessionRecoveryFailed': '会话恢复失败',
506
539
  'ds.commands': '指令',
507
540
  'ds.cmdCompact': '/compact 压缩对话历史', 'ds.cmdExport': '/export 导出会话日志 ZIP', 'ds.commandTimedOut': '命令执行超时,未作为普通消息发送', 'ds.exportStarted': '会话日志已开始下载,请在浏览器下载中查看', 'ds.exportFailed': '无法导出会话日志:{msg}', 'ds.compacting': '正在压缩对话 · 已用时 {elapsed}', 'ds.compactComplete': '对话压缩完成', 'ds.compactFailed': '对话压缩未完成:{msg}', 'ds.commandRunning': '正在执行 /{command} · 已用时 {elapsed}', 'ds.commandComplete': '/{command} 已完成', 'ds.commandFailed': '/{command} 未完成:{msg}',
@@ -513,7 +546,8 @@
513
546
  'ds.groupServers': '服务器', 'ds.groupServersDesc': '服务器地址与令牌',
514
547
  'ds.groupNotify': '通知', 'ds.groupNotifyDesc': '通知与提醒',
515
548
  'ds.groupTheme': '皮肤', 'ds.groupThemeDesc': '配色主题',
516
- 'ds.groupAbout': '关于', 'ds.groupAboutDesc': '版本信息',
549
+ 'ds.contributors': '贡献者', 'ds.contributorsThanks': '感谢每一位让 DSH Remote 变得更好的贡献者。', 'ds.contributorsAll': '查看完整贡献记录 ↗',
550
+ 'ds.groupAbout': '关于', 'ds.groupAboutDesc': '版本信息、贡献者致谢',
517
551
  'ds.donate': '赞赏支持', 'ds.donateDesc': '如果 dsh-remote 帮到了你,欢迎赞赏支持开发 ☕', 'ds.donateThanks': '感谢你的支持 ☕', 'ds.donateClose': '关闭',
518
552
  'ds.notesTitle': '更新内容', 'ds.notesClose': '关闭', 'ds.notesPage': '版本号 {current}/{total}',
519
553
  'ds.back': '‹ 返回', 'ds.emptyGroup': '暂无设置项',
@@ -524,7 +558,7 @@
524
558
  'ds.feedback': '反馈', 'ds.feedbackGithubDesc': '反馈 bug / 提建议', 'ds.feedbackGiteeDesc': '国内镜像,无需代理',
525
559
  'ds.feedbackBiliDesc': 'UP 动态页交流', 'ds.feedbackCopyLink': '复制项目链接', 'ds.feedbackCopyDesc': '手动分享给朋友',
526
560
  'ds.feedbackCopied': '项目链接已复制', 'ds.feedbackCopyFailed': '复制失败,请手动复制',
527
- 'ds.feedbackWrite': '写反馈', 'ds.feedbackWriteDesc': 'App 内直接提交', 'ds.feedbackModalTitle': '写反馈', 'ds.feedbackIncludeDiagnostics': '附带兼容性诊断日志(可选)', 'ds.feedbackDiagnosticsPrivacy': '仅上传网关/协议版本、接口失败摘要和实时链路状态;不包含令牌、Cookie、对话内容或文件路径。',
561
+ 'ds.feedbackWrite': '写反馈', 'ds.feedbackWriteDesc': 'App 内直接提交', 'ds.feedbackModalTitle': '写反馈', 'ds.feedbackIncludeDiagnostics': '附带兼容性诊断日志(可选)', 'ds.feedbackDiagnosticsPrivacy': '仅上传网关/协议版本、接口失败摘要和实时链路状态;不包含令牌、Cookie、对话内容或文件路径。', 'ds.feedbackIncludeCrashes': '附带崩溃日志摘要(可选)', 'ds.feedbackCrashPrivacy': '默认仅在本机记录。勾选后上传最近 3 条退出、重启和错误位置摘要,不含对话、密钥或完整路径。', 'ds.feedbackCrashPreview': '查看拟上传的日志摘要', 'ds.feedbackCrashLoading': '读取本机日志…', 'ds.feedbackCrashUnavailable': '当前网关暂时无法读取日志,请更新网关或稍后重试。',
528
562
  'ds.feedbackTypeBug': 'Bug', 'ds.feedbackTypeSuggestion': '建议', 'ds.feedbackTypeOther': '其他',
529
563
  'ds.feedbackMessagePlaceholder': '请描述遇到的问题或建议(必填,≤2000 字)',
530
564
  'ds.feedbackContactPlaceholder': '联系方式(可选):邮箱 / 微信 / B站 ID',
@@ -540,7 +574,7 @@
540
574
  'ds.toastSent': '已发送', 'ds.toastCopied': '令牌已复制', 'ds.toastAuth': '令牌无效',
541
575
  'ds.toastConnFailed': '连接失败', 'ds.toastOpFailed': '操作失败',
542
576
  'ds.sessionsEmpty': '暂无会话', 'ds.loading': '加载中…',
543
- 'ds.historyLoading': '加载历史…', 'ds.historyEmpty': '暂无消息',
577
+ 'ds.historyCached': '本地缓存', 'ds.historySyncing': '本地缓存 · 正在同步', 'ds.historySynced': '最新记录已同步', 'ds.historySaved': '最新记录 · 已缓存', 'ds.historyOffline': '本地记录 · 网络不可用', 'ds.historyEarlier': '加载更早的记录', 'ds.historyLoadingEarlier': '正在加载更早的记录…', 'ds.historyLatest': '回到最新', 'ds.historyCacheFailed': '已同步 · 本地缓存未保存', 'ds.historyLoading': '加载历史…', 'ds.historyEmpty': '暂无消息', 'ds.historyRetry': '重试',
544
578
  'ds.allow': '允许', 'ds.reject': '拒绝',
545
579
  'ds.approvalTitle': '工具审批', 'ds.questionNotify': '需要回答',
546
580
  'ds.approvalBody': '{tool} · {server}', 'ds.approvalReason': '原因:{reason}',
@@ -601,13 +635,13 @@
601
635
  'menu.modelTitle': '模型切换', 'menu.effortTitle': '思考深度',
602
636
  },
603
637
  en: {
604
- 'ds.repo': 'GitHub repo', 'ds.newSession': '+ New session', 'ds.sessionUntitled': 'New session', 'ds.newWorkspace': 'New workspace', 'ds.sessions': 'Sessions', 'ds.overview': 'Home', 'ds.overviewEyebrow': 'SYSTEM OVERVIEW', 'ds.overviewSubtitle': 'Live status and items needing your attention',
605
- 'ds.systemPulse': 'System pulse', 'ds.systemNominal': 'System nominal', 'ds.systemDegraded': 'Some links need attention', 'ds.systemOffline': 'Connection offline', 'ds.checking': 'Checking…', 'ds.components': '{n}/4 components online', 'ds.live': 'LIVE', 'ds.allLinked': 'ALL LINKED', 'ds.offlineCore': 'OFFLINE',
638
+ 'ds.repo': 'GitHub repo', 'ds.newSession': '+ New session', 'ds.sessionUntitled': 'New session', 'ds.newWorkspace': 'New workspace', 'ds.sessionMore': 'More actions', 'ds.sessionDetails': 'Session details', 'ds.workspace': 'Workspace', 'ds.noWorkspace': 'No workspace', 'ds.copyWorkspace': 'Copy workspace path', 'ds.sessionId': 'Session ID', 'ds.fullTitle': 'Full title', 'ds.copied': 'Copied', 'ds.sessions': 'Sessions', 'ds.overview': 'Home', 'ds.overviewEyebrow': 'SYSTEM OVERVIEW', 'ds.overviewSubtitle': 'Live status and items needing your attention',
639
+ 'ds.systemPulse': 'System pulse', 'ds.systemNominal': 'System nominal', 'ds.systemDegraded': 'Some links need attention', 'ds.systemOffline': 'Connection offline', 'ds.checking': 'Checking…', 'ds.confirmedShort': 'CONFIRMED', 'ds.checkingShort': 'Checking', 'ds.autoChecking': 'Checking automatically · {n}/4 confirmed', 'ds.autoRetry': '{n}/4 online · Automatic retry scheduled', 'ds.components': '{n}/4 components online', 'ds.live': 'LIVE', 'ds.allLinked': 'ALL LINKED', 'ds.offlineCore': 'OFFLINE',
606
640
  'ds.gateway': 'Gateway', 'ds.dshUpstream': 'DSH upstream', 'ds.mux': 'Live mux', 'ds.host': 'Live host', 'ds.online': 'Online', 'ds.offlineShort': 'Offline',
607
641
  'ds.attention': 'Needs attention', 'ds.runtime': 'Runtime metrics', 'ds.recentActivity': 'Recent activity', 'ds.dshVersion': 'DSH version', 'ds.gatewayStatus': 'Gateway status', 'ds.activeSessions': 'Active sessions', 'ds.connectionMode': 'Connection mode', 'ds.liveWs': 'Live WS', 'ds.poll': 'Degraded polling', 'ds.running': 'Running', 'ds.noReason': 'No reason given', 'ds.noActivity': 'No background activity', 'ds.noSessions': 'No sessions yet', 'ds.refreshOverview': 'Refresh overview', 'ds.pendingCount': '{n} pending', 'ds.activeCount': '{n} active sessions', 'ds.nothingPending': 'Nothing pending', 'ds.action.openSession': 'Open recent session', 'ds.action.newSession': 'New session', 'ds.action.attention': 'Review pending', 'ds.action.connect': 'Set up connection', 'ds.action.refresh': 'Check connection',
608
642
  'ds.sortLabel': 'Session sort', 'ds.sortTime': 'By time', 'ds.sortWorkspace': 'By workspace', 'ds.workspace': 'Workspace', 'ds.workspaceUnknown': 'No workspace', 'ds.wbOrderSaved': 'Order saved on this device',
609
643
  'ds.insights': 'Context & costs',
610
- 'ds.files': 'Files', 'ds.settings': 'Settings', 'ds.stats': 'Stats', 'ds.menu': 'Menu',
644
+ 'ds.files': 'Files', 'ds.plugins': 'Plugins', 'ds.settings': 'Settings', 'ds.stats': 'Stats', 'ds.menu': 'Menu',
611
645
  'ds.send': 'Send', 'ds.composerPlaceholder': 'Type a message, Enter to send…', 'ds.composerRunning': 'Running…', 'ds.presets': 'Presets', 'ds.sessionRename': 'Rename session', 'ds.sessionRenameTitle': 'Rename session', 'ds.sessionRenamePlaceholder': 'Enter a new session name', 'ds.sessionRenameConfirm': 'Save', 'ds.sessionRenameFailed': 'Rename failed', 'ds.sessionRenameEmpty': 'Session name cannot be empty', 'ds.sessionRenamed': 'Session name updated', 'ds.sessionArchive': 'Archive', 'ds.sessionArchiveConfirm': 'Archive this session? You can open it from “Show archived”.', 'ds.sessionArchived': 'Session archived', 'ds.sessionStop': 'Stop turn', 'ds.sessionStopConfirm': 'Stop the current turn? Queued messages will be kept.', 'ds.sessionStopFailed': 'Stop failed', 'ds.sessionStopRequested': 'Stop requested', 'ds.sessionRecovering': 'Restoring session…', 'ds.sessionReady': 'Session ready', 'ds.sessionRecoveryFailed': 'Session restore failed',
612
646
  'ds.commands': 'Commands',
613
647
  'ds.cmdCompact': '/compact Compress conversation history', 'ds.cmdExport': '/export Export session log ZIP', 'ds.commandTimedOut': 'Command timed out and was not sent as a chat message', 'ds.exportStarted': 'Session log download started; check your browser downloads', 'ds.exportFailed': 'Could not export session log: {msg}', 'ds.compacting': 'Compressing conversation · {elapsed} elapsed', 'ds.compactComplete': 'Conversation compression complete', 'ds.compactFailed': 'Conversation compression did not finish: {msg}', 'ds.commandRunning': 'Running /{command} · {elapsed} elapsed', 'ds.commandComplete': '/{command} complete', 'ds.commandFailed': '/{command} did not finish: {msg}',
@@ -630,7 +664,8 @@
630
664
  'ds.feedback': 'Feedback', 'ds.feedbackGithubDesc': 'Report bugs · suggest features', 'ds.feedbackGiteeDesc': 'Mirror in China, no proxy needed',
631
665
  'ds.feedbackBiliDesc': 'Chat on the UP\'s Bilibili page', 'ds.feedbackCopyLink': 'Copy project link', 'ds.feedbackCopyDesc': 'Share it manually',
632
666
  'ds.feedbackCopied': 'Project link copied', 'ds.feedbackCopyFailed': 'Copy failed, copy manually',
633
- 'ds.feedbackWrite': 'Write feedback', 'ds.feedbackWriteDesc': 'Submit from the app', 'ds.feedbackModalTitle': 'Write feedback', 'ds.feedbackIncludeDiagnostics': 'Include compatibility diagnostics (optional)', 'ds.feedbackDiagnosticsPrivacy': 'Uploads only gateway/protocol versions, interface failure summaries, and realtime status; no token, cookie, conversation content, or file path.',
667
+ 'ds.contributors': 'Contributors', 'ds.contributorsThanks': 'Thank you to everyone who helps make DSH Remote better.', 'ds.contributorsAll': 'View all contributions ↗',
668
+ 'ds.feedbackWrite': 'Write feedback', 'ds.feedbackWriteDesc': 'Submit from the app', 'ds.feedbackModalTitle': 'Write feedback', 'ds.feedbackIncludeDiagnostics': 'Include compatibility diagnostics (optional)', 'ds.feedbackDiagnosticsPrivacy': 'Uploads only gateway/protocol versions, interface failure summaries, and realtime status; no token, cookie, conversation content, or file path.', 'ds.feedbackIncludeCrashes': 'Include crash summary (optional)', 'ds.feedbackCrashPrivacy': 'Recorded locally by default. Opt in to upload up to 3 recent exit, restart and error-location records. No conversations, credentials or full paths.', 'ds.feedbackCrashPreview': 'Preview the log summary to upload', 'ds.feedbackCrashLoading': 'Reading local logs…', 'ds.feedbackCrashUnavailable': 'Logs are unavailable. Update the gateway or try again later.',
634
669
  'ds.feedbackTypeBug': 'Bug', 'ds.feedbackTypeSuggestion': 'Suggestion', 'ds.feedbackTypeOther': 'Other',
635
670
  'ds.feedbackMessagePlaceholder': 'Describe the bug or suggestion (required, ≤2000 chars)',
636
671
  'ds.feedbackContactPlaceholder': 'Contact (optional): email / WeChat / Bilibili ID',
@@ -646,7 +681,7 @@
646
681
  'ds.toastSent': 'Sent', 'ds.toastCopied': 'Token copied', 'ds.toastAuth': 'Invalid token',
647
682
  'ds.toastConnFailed': 'Connection failed', 'ds.toastOpFailed': 'Operation failed',
648
683
  'ds.sessionsEmpty': 'No sessions', 'ds.loading': 'Loading…',
649
- 'ds.historyLoading': 'Loading history…', 'ds.historyEmpty': 'No messages yet',
684
+ 'ds.historyCached': 'Local cache', 'ds.historySyncing': 'Local cache · Syncing', 'ds.historySynced': 'Latest history synced', 'ds.historySaved': 'Latest history · Cached', 'ds.historyOffline': 'Local history · Offline', 'ds.historyEarlier': 'Load earlier history', 'ds.historyLoadingEarlier': 'Loading earlier history…', 'ds.historyLatest': 'Jump to latest', 'ds.historyCacheFailed': 'Synced · Cache could not be saved', 'ds.historyLoading': 'Loading history…', 'ds.historyEmpty': 'No messages yet', 'ds.historyRetry': 'Retry',
650
685
  'ds.allow': 'Allow', 'ds.reject': 'Reject',
651
686
  'ds.approvalTitle': 'Tool approval', 'ds.questionNotify': 'Question',
652
687
  'ds.approvalBody': '{tool} · {server}', 'ds.approvalReason': 'Reason: {reason}',
@@ -717,6 +752,9 @@
717
752
  <script type="module" src="../morphicons-init.js"></script>
718
753
  <script src="../plugin-center.js"></script>
719
754
  <script src="../insights.js"></script>
755
+ <script src="../sha256.js"></script>
756
+ <script src="../history.js"></script>
757
+ <script src="../link-check.js"></script>
720
758
  <script src="desktop.js"></script>
721
759
  </body>
722
760
  </html>