dsh-remote-plugin 0.6.6 → 0.6.8

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/public/index.html CHANGED
@@ -2,7 +2,7 @@
2
2
  <html lang="zh-CN">
3
3
  <head>
4
4
  <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, user-scalable=no">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
6
6
  <meta name="theme-color" content="#05348B">
7
7
  <meta name="mobile-web-app-capable" content="yes">
8
8
  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
@@ -20,9 +20,9 @@
20
20
  <span class="brand-name">DSH Remote</span>
21
21
  </div>
22
22
  <div class="topbar-right">
23
- <button id="btn-donate" class="topbar-btn icon-btn" data-i18n-title="settings.donate" data-i18n-aria="settings.donate">🎁</button>
24
- <button id="btn-feedback" class="topbar-btn icon-btn" data-i18n-title="feedback.title" data-i18n-aria="feedback.title" aria-haspopup="menu" aria-expanded="false">💬</button>
25
- <button id="btn-refresh" class="topbar-btn icon-btn" data-i18n-title="a11y.refresh" data-i18n-aria="a11y.refresh">⟳</button>
23
+ <button id="btn-donate" class="topbar-btn icon-btn" data-i18n-title="settings.donate" data-i18n-aria="settings.donate"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 8h11v6a4 4 0 0 1-4 4H10a4 4 0 0 1-4-4Z"/><path d="M17 10h1.5a2.5 2.5 0 0 1 0 5H17M9 4c0 1 1 1.2 1 2M13 4c0 1 1 1.2 1 2M5 20h13"/></svg></button>
24
+ <button id="btn-feedback" class="topbar-btn icon-btn" data-i18n-title="feedback.title" data-i18n-aria="feedback.title" aria-haspopup="menu" aria-expanded="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 11.5a7.5 7.5 0 0 1-7.5 7.5H7l-3 2 .8-3.1A7.5 7.5 0 1 1 20 11.5Z"/><path d="M8 11.5h.01M12 11.5h.01M16 11.5h.01"/></svg></button>
25
+ <button id="btn-refresh" class="topbar-btn icon-btn" data-i18n-title="a11y.refresh" data-i18n-aria="a11y.refresh"><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="M20 11a8 8 0 1 0 1 4"/><path d="M20 5v6h-6"/></svg></button>
26
26
  <span id="conn-badge" class="topbar-btn conn-badge off" data-i18n="conn.off">未连接</span>
27
27
  </div>
28
28
  </header>
@@ -31,6 +31,13 @@
31
31
  <!-- 会话列表 -->
32
32
  <section id="view-home" class="view">
33
33
  <div class="stat-strip" id="stat-strip"></div>
34
+ <div id="workbench-bar" class="workbench-bar unbound">
35
+ <button id="wb-toggle" class="wb-toggle" type="button" aria-expanded="false">
36
+ <span id="wb-label" class="wb-label" data-i18n="wb.unbound">未绑定工作台</span>
37
+ <span class="wb-chevron" aria-hidden="true">▾</span>
38
+ </button>
39
+ <div id="wb-panel" class="wb-panel hidden"></div>
40
+ </div>
34
41
  <div class="section-head">
35
42
  <span data-i18n="nav.sessions">会话</span>
36
43
  <div class="section-actions">
@@ -53,7 +60,7 @@
53
60
  <div id="fs-path" class="fs-path" data-i18n="fs.loading">加载中…</div>
54
61
  <button id="fs-new-workspace" class="mini-btn" data-i18n="fs.newWorkspace">新建工作区</button>
55
62
  <button id="fs-upload-btn" class="mini-btn" data-i18n="fs.upload">上传</button>
56
- <button id="fs-refresh" class="icon-btn" data-i18n-title="a11y.refresh" data-i18n-aria="a11y.refresh">⟳</button>
63
+ <button id="fs-refresh" class="icon-btn" data-i18n-title="a11y.refresh" data-i18n-aria="a11y.refresh"><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="M20 11a8 8 0 1 0 1 4"/><path d="M20 5v6h-6"/></svg></button>
57
64
  </div>
58
65
  <div id="fs-progress" class="fs-progress hidden">
59
66
  <div class="fs-progress-track"><div id="fs-progress-bar" class="fs-progress-bar"></div></div>
@@ -71,12 +78,13 @@
71
78
  <!-- 会话详情 -->
72
79
  <section id="view-session" class="view hidden">
73
80
  <div id="session-head" class="session-head">
74
- <button id="btn-back" class="icon-btn">‹</button>
81
+ <button id="btn-back" class="icon-btn" data-i18n-title="settings.back" data-i18n-aria="settings.back">‹</button>
75
82
  <div class="session-head-meta">
76
83
  <div id="session-title" class="session-title" data-i18n="session.loading">加载中…</div>
77
84
  <div id="session-sub" class="session-sub"></div>
78
85
  </div>
79
- <button id="btn-stats" class="icon-btn" data-i18n-title="stats.title">📊</button>
86
+ <button id="btn-stats" class="icon-btn" data-i18n-title="stats.title" data-i18n-aria="stats.title"><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>
87
+ <button id="btn-fs-send" class="session-send-btn hidden" type="button" data-i18n="composer.send" data-i18n-title="composer.send" data-i18n-aria="composer.send">发送</button>
80
88
  <button id="btn-cancel" class="danger-btn hidden" data-i18n="session.stop">停止</button>
81
89
  </div>
82
90
 
@@ -95,7 +103,7 @@
95
103
  </div>
96
104
  </div>
97
105
 
98
- <div class="composer-wrap">
106
+ <div id="composer-wrap" class="composer-wrap">
99
107
  <div id="composer-menu" class="composer-menu hidden">
100
108
  <div class="menu-group">
101
109
  <div class="menu-title" data-i18n="menu.commandsTitle">输入指令</div>
@@ -107,6 +115,11 @@
107
115
  <button class="menu-chip" data-cmd="/permission" data-i18n="menu.permission">/permission 切换权限预设</button>
108
116
  <button class="menu-chip" data-cmd="/plan" data-i18n="menu.plan">/plan 进入/退出计划模式</button>
109
117
  </div>
118
+ <div class="menu-chips hidden" id="permission-submenu">
119
+ <button class="menu-chip perm-opt" data-perm="read-only" data-i18n="perm.readOnly">read-only</button>
120
+ <button class="menu-chip perm-opt" data-perm="workspace-write" data-i18n="perm.workspaceWrite">workspace-write</button>
121
+ <button class="menu-chip perm-opt" data-perm="danger-full-access" data-i18n="perm.dangerFullAccess">danger-full-access</button>
122
+ </div>
110
123
  </div>
111
124
  <div class="menu-group hidden" id="preset-menu-group">
112
125
  <div class="menu-title" data-i18n="presets.menuTitle">预设</div>
@@ -121,20 +134,73 @@
121
134
  <div id="menu-efforts" class="menu-chips"></div>
122
135
  </div>
123
136
  </div>
137
+ <div id="composer-fs-handle" class="composer-fs-handle" aria-hidden="true"><span></span></div>
138
+ <div id="composer-image-menu" class="composer-image-menu hidden" role="menu">
139
+ <button type="button" class="composer-image-option" data-image-source="CAMERA" role="menuitem"><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="M4 8.5h3l1.5-2h7L17 8.5h3v10H4z"/><circle cx="12" cy="13.5" r="3.2"/></svg><span data-i18n="composer.takePhoto">拍照</span></button>
140
+ <button type="button" class="composer-image-option" data-image-source="PHOTOS" role="menuitem"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3.5" y="4" width="17" height="16" rx="2"/><circle cx="8.5" cy="9" r="1.2"/><path d="m5 17 4.5-4.5 3 3 2-2L19 17"/></svg><span data-i18n="composer.choosePhoto">相册</span></button>
141
+ </div>
142
+ <div id="composer-attachments" class="composer-attachments hidden"></div>
124
143
  <div class="composer">
125
144
  <button id="btn-plus" class="plus-btn" data-i18n-aria="a11y.more" data-i18n-title="a11y.moreTitle">+</button>
126
- <textarea id="composer-input" rows="1" data-i18n-placeholder="composer.placeholder" placeholder="给 DSH 发消息…"></textarea>
145
+ <div id="composer-input-wrap" class="composer-input-wrap">
146
+ <textarea id="composer-input" rows="1" data-i18n-placeholder="composer.placeholder" placeholder="给 DSH 发消息…"></textarea>
147
+ <button id="btn-fs-toggle" class="composer-fs-btn hidden" type="button" data-i18n-title="composer.fullscreen" data-i18n-aria="composer.fullscreen">
148
+ <svg id="fs-ico-expand" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><path d="M8 3H3v5M16 3h5v5M8 21H3v-5M21 16v5h-5"/></svg>
149
+ <svg id="fs-ico-collapse" class="hidden" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><path d="M9 3v6H3M15 3v6h6M9 21v-6H3M21 15h-6v6"/></svg>
150
+ </button>
151
+ </div>
152
+ <button id="btn-image" class="composer-image-btn" type="button" data-i18n-title="composer.addImage" data-i18n-aria="composer.addImage"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3.5" y="4" width="17" height="16" rx="2"/><circle cx="8.5" cy="9" r="1.2"/><path d="m5 17 4.5-4.5 3 3 2-2L19 17"/></svg></button>
127
153
  <button id="btn-send" class="send-btn" data-i18n="composer.send">发送</button>
128
154
  </div>
155
+ <input id="composer-camera-input" class="hidden" type="file" accept="image/*" capture="environment">
156
+ <input id="composer-gallery-input" class="hidden" type="file" accept="image/*" multiple>
129
157
  </div>
130
158
  </section>
131
159
 
132
- <!-- 待办 -->
160
+ <!-- 系统总览 -->
133
161
  <section id="view-activity" class="view hidden">
134
- <div class="section-head"><span data-i18n="pending.title">待处理</span><span id="pending-count" class="muted"></span></div>
135
- <div id="pending-list" class="pending-list"></div>
136
- <div class="section-head"><span data-i18n="jobs.title">后台任务</span></div>
137
- <div id="jobs-list" class="jobs-list"></div>
162
+ <div class="overview-head">
163
+ <div>
164
+ <div class="overview-eyebrow" data-i18n="overview.eyebrow">SYSTEM OVERVIEW</div>
165
+ <h1 class="overview-title" data-i18n="overview.title">主页</h1>
166
+ <p class="overview-subtitle" data-i18n="overview.subtitle">实时状态与需要你处理的事项</p>
167
+ </div>
168
+ <button id="overview-refresh" class="mini-btn overview-refresh" data-i18n-title="overview.refresh" data-i18n-aria="overview.refresh"><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="M20 11a8 8 0 1 0 1 4"/><path d="M20 5v6h-6"/></svg></button>
169
+ </div>
170
+
171
+ <div class="overview-pulse-card">
172
+ <div class="overview-pulse-copy">
173
+ <div class="overview-eyebrow" data-i18n="overview.systemPulse">系统链路</div>
174
+ <div id="overview-status" class="overview-status" data-i18n="overview.checking">检查中…</div>
175
+ <div id="overview-status-desc" class="overview-status-desc">—</div>
176
+ <button id="overview-primary-action" class="overview-primary-action" type="button" data-i18n="overview.action.openSession">打开最近会话</button>
177
+ </div>
178
+ <div id="overview-pulse-ring" class="system-pulse-ring" style="--pulse-pct:0%" aria-label="System health">
179
+ <div class="system-pulse-core"><strong id="overview-health">—</strong><span id="overview-health-caption">—</span></div>
180
+ </div>
181
+ <div id="overview-links" class="overview-links" aria-label="System components">
182
+ <div class="overview-link" data-overview-link="gateway"><i></i><span data-i18n="overview.gateway">网关</span><b>—</b></div>
183
+ <div class="overview-link" data-overview-link="dsh"><i></i><span data-i18n="overview.dsh">DSH 上游</span><b>—</b></div>
184
+ <div class="overview-link" data-overview-link="mux"><i></i><span data-i18n="overview.mux">实时 mux</span><b>—</b></div>
185
+ <div class="overview-link" data-overview-link="host"><i></i><span data-i18n="overview.host">实时 host</span><b>—</b></div>
186
+ </div>
187
+ </div>
188
+
189
+ <div class="overview-section-head"><span data-i18n="overview.attention">需要你处理</span><span id="overview-attention-count" class="overview-section-meta"></span></div>
190
+ <div id="overview-attention-list" class="overview-attention-list"></div>
191
+
192
+ <div class="overview-section-head"><span data-i18n="overview.metrics">运行指标</span></div>
193
+ <div class="overview-metric-grid">
194
+ <div class="overview-metric"><span data-i18n="overview.dshVersion">DSH 版本</span><strong id="overview-dsh-version">—</strong></div>
195
+ <div class="overview-metric"><span data-i18n="overview.gatewayVersion">网关状态</span><strong id="overview-gateway-version">—</strong></div>
196
+ <div class="overview-metric"><span data-i18n="overview.activeSessions">活跃会话</span><strong id="overview-active-sessions">0</strong></div>
197
+ <div class="overview-metric"><span data-i18n="overview.connectionMode">连接模式</span><strong id="overview-connection-mode">—</strong></div>
198
+ </div>
199
+
200
+ <div class="overview-section-head"><span data-i18n="overview.activity">近期活动</span><span id="overview-active-count" class="overview-section-meta"></span></div>
201
+ <div id="overview-session-list" class="overview-session-list"></div>
202
+
203
+ <div class="overview-legacy hidden" aria-hidden="true"><div id="pending-list"></div><div id="jobs-list"></div><span id="pending-count"></span></div>
138
204
  </section>
139
205
 
140
206
  <!-- Token 统计 -->
@@ -221,6 +287,13 @@
221
287
  <div><div class="setting-name" data-i18n="settings.toolsTitle">显示工具调用</div><div class="setting-desc" data-i18n="settings.toolsDesc">关闭后隐藏 🔧/📦 工具调用与结果</div></div>
222
288
  <label class="switch"><input type="checkbox" id="opt-tools"><span class="slider"></span></label>
223
289
  </div>
290
+ <div class="setting-row">
291
+ <div><div class="setting-name" data-i18n="settings.mobileEnterTitle">手机回车行为</div><div class="setting-desc" data-i18n="settings.mobileEnterDesc">选择手机端按 Enter 时换行或直接发送</div></div>
292
+ <select id="mobile-enter-action" class="setting-select">
293
+ <option value="newline" data-i18n="settings.mobileEnterNewline">回车换行</option>
294
+ <option value="send" data-i18n="settings.mobileEnterSend">回车发送</option>
295
+ </select>
296
+ </div>
224
297
  <div class="setting-row">
225
298
  <div><div class="setting-name" data-i18n="presets.title">预设提示词</div><div class="setting-desc" data-i18n="presets.desc">常用提示词,对话界面一键插入(最多 20 条)</div></div>
226
299
  <button id="btn-preset-add" class="mini-btn" data-i18n="presets.add">新增</button>
@@ -303,6 +376,10 @@
303
376
  <div><div class="setting-name" data-i18n="settings.taskDoneTitle">任务结束通知</div><div class="setting-desc" data-i18n="settings.taskDoneDesc">任务/目标完成时推送“✅ 任务已完成”</div></div>
304
377
  <label class="switch"><input type="checkbox" id="opt-task-done"><span class="slider"></span></label>
305
378
  </div>
379
+ <div class="setting-row">
380
+ <div><div class="setting-name" data-i18n="settings.announcementHistoryTitle">历史公告</div><div class="setting-desc" data-i18n="settings.announcementHistoryDesc">查看以前收到的公告</div></div>
381
+ <button id="btn-announcement-history" class="mini-btn" data-i18n="settings.announcementHistory">查看记录</button>
382
+ </div>
306
383
  </div>
307
384
  </div>
308
385
 
@@ -381,16 +458,37 @@
381
458
  </button>
382
459
  </div>
383
460
 
384
- <nav class="bottom-nav">
385
- <button data-view="view-home" class="nav-btn active"><span class="nav-ico">▤</span><span data-i18n="nav.sessions">会话</span></button>
386
- <button data-view="view-files" class="nav-btn"><span class="nav-ico">⇅</span><span data-i18n="nav.files">文件</span></button>
387
- <button data-view="view-activity" class="nav-btn"><span class="nav-ico">◷</span><span data-i18n="nav.pending">待办</span><b id="nav-pending" class="nav-badge hidden"></b></button>
388
- <button data-view="view-stats" class="nav-btn"><span class="nav-ico">▦</span><span data-i18n="nav.stats">统计</span></button>
389
- <button data-view="view-settings" class="nav-btn"><span class="nav-ico">⚙</span><span data-i18n="nav.settings">设置</span></button>
461
+ <nav class="bottom-nav" aria-label="主导航">
462
+ <div class="nav-wave" aria-hidden="true">
463
+ <svg viewBox="0 0 100 100" preserveAspectRatio="none">
464
+ <path class="nav-wave-fill" d="M0 39 C16 39 28 39 37 31 C44 25 46 10 50 10 C54 10 56 25 63 31 C72 39 84 39 100 39 V100 H0Z"/>
465
+ <path class="nav-wave-line" d="M0 39 C16 39 28 39 37 31 C44 25 46 10 50 10 C54 10 56 25 63 31 C72 39 84 39 100 39"/>
466
+ </svg>
467
+ </div>
468
+ <button data-view="view-home" class="nav-btn">
469
+ <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"><rect x="4" y="4" width="6" height="6" rx="1"/><rect x="14" y="4" width="6" height="6" rx="1"/><rect x="4" y="14" width="6" height="6" rx="1"/><rect x="14" y="14" width="6" height="6" rx="1"/></svg></span>
470
+ <span data-i18n="nav.sessions">会话</span>
471
+ </button>
472
+ <button data-view="view-files" class="nav-btn">
473
+ <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"><path d="M8 4v12M5 7l3-3 3 3M16 20V8m-3 9 3 3 3-3"/><path d="M4 20h7M13 4h7" opacity=".55"/></svg></span>
474
+ <span data-i18n="nav.files">文件</span>
475
+ </button>
476
+ <button data-view="view-activity" class="nav-btn nav-home">
477
+ <span class="nav-home-orb"><span class="nav-ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="7.5"/><circle cx="12" cy="12" r="2.2" fill="currentColor" stroke="none"/><path d="M12 2.5v3M12 18.5v3M2.5 12h3M18.5 12h3" opacity=".75"/></svg></span></span>
478
+ <span data-i18n="nav.home">主页</span><b id="nav-pending" class="nav-badge hidden"></b>
479
+ </button>
480
+ <button data-view="view-stats" class="nav-btn">
481
+ <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"><path d="M5 19V9M12 19V5M19 19v-7"/><path d="M3 19h18" opacity=".55"/></svg></span>
482
+ <span data-i18n="nav.stats">统计</span>
483
+ </button>
484
+ <button data-view="view-settings" class="nav-btn">
485
+ <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"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1-1.8 1.8-.1-.1a1.7 1.7 0 0 0-1.9-.3 1.7 1.7 0 0 0-1 1.5v.2h-2.5v-.2a1.7 1.7 0 0 0-1-1.5 1.7 1.7 0 0 0-1.9.3l-.1.1-1.8-1.8.1-.1A1.7 1.7 0 0 0 8.1 15a1.7 1.7 0 0 0-1.5-1H6v-2.5h.2a1.7 1.7 0 0 0 1.5-1 1.7 1.7 0 0 0-.3-1.9l-.1-.1 1.8-1.8.1.1a1.7 1.7 0 0 0 1.9.3 1.7 1.7 0 0 0 1-1.5v-.2h2.5v.2a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.9-.3l.1-.1 1.8 1.8-.1.1a1.7 1.7 0 0 0-.3 1.9 1.7 1.7 0 0 0 1.5 1h.2V14h-.2a1.7 1.7 0 0 0-1.5 1Z"/></svg></span>
486
+ <span data-i18n="nav.settings">设置</span>
487
+ </button>
390
488
  </nav>
391
489
 
392
490
  <!-- 审批模态 -->
393
- <div id="modal-approval" class="modal hidden">
491
+ <div id="modal-approval" class="modal hidden" role="dialog" aria-modal="true">
394
492
  <div class="modal-card">
395
493
  <div class="modal-title" data-i18n="modal.approvalTitle">工具审批</div>
396
494
  <div id="approval-body" class="modal-body"></div>
@@ -402,7 +500,7 @@
402
500
  </div>
403
501
 
404
502
  <!-- 提问模态 -->
405
- <div id="modal-question" class="modal hidden">
503
+ <div id="modal-question" class="modal hidden" role="dialog" aria-modal="true">
406
504
  <div class="modal-card">
407
505
  <div class="modal-title" data-i18n="modal.questionTitle">DSH 需要你回答</div>
408
506
  <div id="question-body" class="modal-body"></div>
@@ -414,7 +512,7 @@
414
512
  </div>
415
513
 
416
514
  <!-- 写反馈模态 -->
417
- <div id="modal-feedback" class="modal hidden">
515
+ <div id="modal-feedback" class="modal hidden" role="dialog" aria-modal="true">
418
516
  <div class="modal-card">
419
517
  <div class="modal-title" data-i18n="feedback.modalTitle">写反馈</div>
420
518
  <div class="modal-body">
@@ -434,7 +532,7 @@
434
532
  </div>
435
533
 
436
534
  <!-- 新建工作区模态 -->
437
- <div id="modal-workspace" class="modal hidden">
535
+ <div id="modal-workspace" class="modal hidden" role="dialog" aria-modal="true">
438
536
  <div class="modal-card">
439
537
  <div class="modal-title" data-i18n="workspace.createTitle">新建工作区</div>
440
538
  <div class="modal-body">
@@ -449,8 +547,23 @@
449
547
  </div>
450
548
  </div>
451
549
 
550
+ <div id="modal-archive" class="modal hidden" role="dialog" aria-modal="true">
551
+ <div class="modal-card archive-modal-card">
552
+ <div class="modal-title" data-i18n="session.archiveTitle">确认归档会话</div>
553
+ <p class="archive-modal-desc" data-i18n="session.archiveDesc">归档后会话将从普通列表隐藏,但不会删除对话记录。</p>
554
+ <div class="archive-summary">
555
+ <div class="archive-summary-row"><span data-i18n="session.archiveConversation">对话</span><strong id="archive-session-title"></strong></div>
556
+ <div class="archive-summary-row"><span data-i18n="session.archiveWorkspace">工作区</span><code id="archive-session-workspace"></code></div>
557
+ </div>
558
+ <div class="modal-actions">
559
+ <button id="archive-cancel" class="btn subtle" data-i18n="session.archiveCancel">取消</button>
560
+ <button id="archive-confirm" class="btn archive-confirm-btn" data-i18n="session.archiveConfirmAction">确认归档</button>
561
+ </div>
562
+ </div>
563
+ </div>
564
+
452
565
  <!-- goal 模态 -->
453
- <div id="modal-goal" class="modal hidden">
566
+ <div id="modal-goal" class="modal hidden" role="dialog" aria-modal="true">
454
567
  <div class="modal-card">
455
568
  <div class="modal-title" data-i18n="modal.goalTitle">目标控制</div>
456
569
  <div id="goal-body" class="modal-body"></div>
@@ -462,7 +575,7 @@
462
575
  </div>
463
576
 
464
577
  <!-- 统计模态 -->
465
- <div id="modal-stats" class="modal hidden">
578
+ <div id="modal-stats" class="modal hidden" role="dialog" aria-modal="true">
466
579
  <div class="modal-card">
467
580
  <div class="modal-title" data-i18n="modal.statsTitle">本轮统计</div>
468
581
  <div id="stats-body" class="modal-body"></div>
@@ -473,7 +586,7 @@
473
586
  </div>
474
587
 
475
588
  <!-- 皮肤选择模态 -->
476
- <div id="modal-theme" class="modal hidden">
589
+ <div id="modal-theme" class="modal hidden" role="dialog" aria-modal="true">
477
590
  <div class="modal-card">
478
591
  <div class="modal-title" data-i18n="theme.panelTitle">选择配色</div>
479
592
  <div id="theme-options" class="theme-panel-list"></div>
@@ -484,7 +597,7 @@
484
597
  </div>
485
598
 
486
599
  <!-- 赞赏支持模态 -->
487
- <div id="modal-donate" class="modal hidden">
600
+ <div id="modal-donate" class="modal hidden" role="dialog" aria-modal="true">
488
601
  <div class="modal-card">
489
602
  <div class="modal-title" data-i18n="settings.donate">赞赏支持</div>
490
603
  <div class="modal-body donate-body">
@@ -498,7 +611,7 @@
498
611
  </div>
499
612
 
500
613
  <!-- 更新内容模态 -->
501
- <div id="modal-notes" class="modal hidden">
614
+ <div id="modal-notes" class="modal hidden" role="dialog" aria-modal="true">
502
615
  <div class="modal-card">
503
616
  <div class="modal-title"><span id="notes-version" class="muted"></span> <span data-i18n="notes.title">更新内容</span></div>
504
617
  <div class="modal-body notes-body">
@@ -515,7 +628,32 @@
515
628
  </div>
516
629
  </div>
517
630
 
518
- <div id="toast" class="toast hidden"></div>
631
+ <!-- 远程公告模态 -->
632
+ <div id="modal-announcement" class="modal hidden" role="dialog" aria-modal="true">
633
+ <div class="modal-card announcement-card">
634
+ <div class="announcement-kicker" data-i18n="announcement.label">公告</div>
635
+ <div id="announcement-title" class="modal-title"></div>
636
+ <div id="announcement-content" class="modal-body announcement-content"></div>
637
+ <a id="announcement-action" class="announcement-action hidden" target="_blank" rel="noopener"></a>
638
+ <div class="modal-actions">
639
+ <button id="announcement-later" class="btn subtle" data-i18n="announcement.later">稍后再看</button>
640
+ <button id="announcement-confirm" class="btn primary" data-i18n="announcement.gotIt">知道了</button>
641
+ </div>
642
+ </div>
643
+ </div>
644
+
645
+ <div id="modal-announcement-history" class="modal hidden" role="dialog" aria-modal="true">
646
+ <div class="modal-card announcement-history-card">
647
+ <div class="announcement-kicker" data-i18n="announcement.historyLabel">公告记录</div>
648
+ <div class="modal-title" data-i18n="announcement.historyTitle">历史公告</div>
649
+ <div id="announcement-history-list" class="announcement-history-list"></div>
650
+ <div class="modal-actions">
651
+ <button id="announcement-history-close" class="btn subtle" data-i18n="announcement.close">关闭</button>
652
+ </div>
653
+ </div>
654
+ </div>
655
+
656
+ <div id="toast" class="toast hidden" role="status" aria-live="polite"></div>
519
657
 
520
658
  <script>
521
659
  window.APP_STR = {
@@ -526,7 +664,14 @@
526
664
  'conn.poll': '轮询', 'conn.pollTitle': '当前网络不支持实时推送,已降级为轮询(延迟数秒)',
527
665
  'conn.titleGroup': '{group} · {url}({ms})',
528
666
  'common.refreshing': '刷新中…',
529
- 'nav.sessions': '会话', 'nav.files': '文件', 'nav.pending': '待办', 'nav.stats': '统计', 'nav.settings': '设置',
667
+ 'nav.sessions': '会话', 'nav.files': '文件', 'nav.pending': '待办', 'nav.overview': '总览', 'nav.home': '主页', 'nav.stats': '统计', 'nav.settings': '设置',
668
+ 'overview.eyebrow': 'SYSTEM OVERVIEW', 'overview.title': '主页', 'overview.subtitle': '实时状态与需要你处理的事项',
669
+ 'overview.systemPulse': '系统链路', 'overview.nominal': '系统链路正常', 'overview.degraded': '部分链路需要关注', 'overview.offline': '连接已断开', 'overview.checking': '检查中…', 'overview.components': '{n}/4 个模块在线', 'overview.live': 'LIVE', 'overview.allLinked': '全部链路', 'overview.offlineCore': 'OFFLINE',
670
+ 'overview.gateway': '网关', 'overview.dsh': 'DSH 上游', 'overview.mux': '实时 mux', 'overview.host': '实时 host', 'overview.online': '在线', 'overview.offlineShort': '离线',
671
+ 'overview.attention': '需要你处理', 'overview.metrics': '运行指标', 'overview.activity': '近期活动', 'overview.dshVersion': 'DSH 版本', 'overview.gatewayVersion': '网关状态',
672
+ 'overview.activeSessions': '活跃会话', 'overview.connectionMode': '连接模式', 'overview.ws': '实时 WS', 'overview.poll': '降级轮询', 'overview.noActivity': '暂无后台活动', 'overview.noSession': '暂无会话',
673
+ 'overview.refresh': '刷新总览', 'overview.none': '暂无事项', 'overview.pendingCount': '{n} 项待处理', 'overview.activeCount': '{n} 个活跃会话',
674
+ 'overview.action.openSession': '打开最近会话', 'overview.action.newSession': '新建会话', 'overview.action.attention': '处理待办', 'overview.action.connect': '去设置连接', 'overview.action.refresh': '重新检查连接',
530
675
  'statsPage.title': 'Token 统计',
531
676
  'statsPage.todayTokens': '今日 Token', 'statsPage.todayCost': '今日费用', 'statsPage.peakShare': '高峰占比',
532
677
  'statsPage.input': '未缓存输入', 'statsPage.cacheRead': '缓存命中', 'statsPage.cacheWrite': '缓存写入', 'statsPage.output': '输出',
@@ -534,6 +679,7 @@
534
679
  'statsPage.gatewayDown': '统计需要网关运行', 'statsPage.empty': '暂无统计,产生会话后自动聚合',
535
680
  'statsPage.note': '注:本数据仅在使用 DeepSeek 官方 API 时估算;基于 token 计算,与官网账单可能有出入,一切以官网为准。统计自 2026-08-17 定价生效日起。',
536
681
  'home.newSession': '+ 新会话', 'home.newWorkspace': '+ 工作区', 'home.empty': '暂无会话', 'home.createFailed': '新建会话失败', 'home.created': '会话已创建',
682
+ 'wb.unbound': '未绑定工作台', 'wb.bound': '绑定 {title}', 'wb.noProjects': '暂无项目', 'wb.noSessions': '暂无会话', 'wb.newSession': '新会话', 'wb.flatHidden': '工作台会话已收起({n} 个)', 'wb.archivedHidden': '------隐藏已归档会话------', 'wb.archivedShown': '------显示已归档会话------',
537
683
  'workspace.createTitle': '新建工作区', 'workspace.createDesc': '将在当前文件目录下创建文件夹,并自动打开新会话。', 'workspace.parent': '父目录', 'workspace.namePlaceholder': '例如:my-project', 'workspace.cancel': '取消', 'workspace.create': '创建并打开', 'workspace.nameRequired': '请输入工作区名称', 'workspace.exists': '该目录已存在', 'workspace.invalidName': '名称不能包含路径分隔符', 'workspace.createFailed': '创建工作区失败:{msg}', 'workspace.created': '工作区已创建', 'workspace.createdNoSession': '工作区已创建,但新会话未能打开',
538
684
  'workspace.createTitle': '新建工作区', 'workspace.createDesc': '将在当前文件目录下创建文件夹,并自动打开新会话。', 'workspace.parent': '父目录', 'workspace.namePlaceholder': '例如:my-project', 'workspace.cancel': '取消', 'workspace.create': '创建并打开', 'workspace.nameRequired': '请输入工作区名称', 'workspace.created': '工作区已创建', 'workspace.sessionFailed': '目录已创建,但新会话打开失败:{msg}',
539
685
  'sessions.running': '运行中', 'sessions.statRunning': '运行中', 'sessions.statPending': '待处理', 'sessions.statTotal': '会话总数',
@@ -569,7 +715,7 @@
569
715
  'stream.error': '事件流错误:{msg}',
570
716
  'session.loading': '加载中…', 'session.unknown': '未知会话', 'session.running': '运行中', 'session.interrupted': '已中断',
571
717
  'session.stop': '停止', 'session.history': '对话', 'session.errorMsg': '会话出错:{msg}',
572
- 'session.confirmStop': '停止当前会话正在运行的任务?', 'session.stopFailed': '停止失败', 'session.stopRequested': '已请求停止',
718
+ 'session.confirmStop': '停止当前会话正在运行的任务?', 'session.stopFailed': '停止失败', 'session.stopRequested': '已请求停止', 'session.archive': '归档', 'session.archiveConfirm': '归档这个会话?归档后可在“显示已归档会话”中打开。', 'session.archiveFailed': '归档失败:{msg}', 'session.archived': '会话已归档', 'session.archiveTitle': '确认归档会话', 'session.archiveDesc': '归档后会话将从普通列表隐藏,但不会删除对话记录。', 'session.archiveConversation': '对话', 'session.archiveWorkspace': '工作区', 'session.archiveCancel': '取消', 'session.archiveConfirmAction': '确认归档',
573
719
  'notify.approvalTitle': '工具审批', 'notify.approvalBody': '{tool} 需要批准', 'notify.questionTitle': 'DSH 提问', 'notify.questionBody': '需要你回答',
574
720
  'notify.permissionFailed': '通知权限申请失败:{msg}',
575
721
  'history.loading': '加载历史…', 'history.cacheFallback': '网络不可用:显示本地缓存的历史', 'history.loadFailed': '加载历史失败:{msg}', 'history.retry': '重试',
@@ -591,7 +737,7 @@
591
737
  'todos.title': '任务清单',
592
738
  'subagent.title': '子代理', 'subagent.diagnostic': '诊断项', 'subagent.running': '· 运行中', 'subagent.interrupt': '中断',
593
739
  'subagent.confirmInterrupt': '中断这个子代理当前回合?', 'subagent.interruptFailed': '中断失败', 'subagent.interruptSubmitted': '中断请求已提交',
594
- 'send.failed': '发送失败', 'send.commandSent': '指令已发送', 'send.sent': '已发送', 'send.commandExecuted': '命令已执行',
740
+ 'send.failed': '发送失败', 'send.commandSent': '指令已发送', 'send.sent': '已发送', 'send.imageSent': '图片已发送', 'send.commandExecuted': '命令已执行',
595
741
  'models.loading': '模型加载中…', 'models.loadFailed': '模型列表加载失败:{msg}', 'models.unavailable': '不可用', 'models.none': '没有可用模型',
596
742
  'models.switchFailed': '切换模型失败', 'models.switched': '已切换模型:{model}',
597
743
  'models.effortFailed': '切换思考深度失败', 'models.effortSwitched': '思考深度:{effort}',
@@ -599,8 +745,9 @@
599
745
  'menu.compact': '/compact 压缩对话历史', 'menu.export': '/export 导出会话日志 ZIP',
600
746
  'menu.feedback': '/feedback 反馈当前会话', 'menu.goal': '/goal 设置/查看任务目标',
601
747
  'menu.permission': '/permission 切换权限预设', 'menu.plan': '/plan 进入/退出计划模式',
748
+ 'perm.readOnly': 'read-only', 'perm.workspaceWrite': 'workspace-write', 'perm.dangerFullAccess': 'danger-full-access',
602
749
  'menu.modelTitle': '模型切换', 'menu.refresh': '刷新', 'menu.modelAuto': '点开时自动加载', 'menu.effortTitle': '思考深度',
603
- 'composer.placeholder': '给 DSH 发消息…', 'composer.send': '发送',
750
+ 'composer.placeholder': '给 DSH 发消息…', 'composer.send': '发送', 'composer.fullscreen': '全屏编辑', 'composer.exitFullscreen': '退出全屏编辑', 'composer.addImage': '添加图片', 'composer.takePhoto': '拍照', 'composer.choosePhoto': '相册', 'composer.imageAdded': '已添加图片', 'composer.imageRemoved': '已移除图片', 'composer.removeImage': '移除图片', 'composer.imageUnsupported': '仅支持 PNG、JPEG、WebP 或 GIF 图片', 'composer.imageTooLarge': '单张图片不能超过 {size}', 'composer.imageLimit': '最多添加 {count} 张图片', 'composer.imageSlashUnsupported': '带图片时不能发送斜杠命令', 'composer.imageReadFailed': '图片读取失败:{msg}',
604
751
  'pending.title': '待处理', 'pending.count': '{n} 项', 'pending.approvalTitle': '🔧 {tool} 请求批准', 'pending.noReason': '无说明',
605
752
  'pending.allow': '允许', 'pending.reject': '拒绝', 'pending.question': '❓ {question}',
606
753
  'pending.questionCount': '共 {n} 个问题', 'pending.answer': '去回答', 'pending.empty': '暂无待处理事项',
@@ -651,7 +798,7 @@
651
798
  'settings.back': '‹ 返回',
652
799
  'settings.servers': '服务器地址(可多个)', 'settings.speedTest': '测速', 'settings.add': '添加',
653
800
  'settings.scanTitle': '扫码连接', 'settings.scanDesc': '拍照/相册本地解码(无谷歌服务依赖);也可系统相机扫码或手动粘贴', 'settings.scan': '扫码',
654
- 'settings.notifyTitle': '通知', 'settings.notifyDesc': '收到审批/提问时推送',
801
+ 'settings.notifyTitle': '通知', 'settings.notifyDesc': '收到审批/提问时推送', 'settings.announcementHistoryTitle': '历史公告', 'settings.announcementHistoryDesc': '查看以前收到的公告', 'settings.announcementHistory': '查看记录',
655
802
  'settings.testNotifyTitle': '测试通知', 'settings.testNotifyDesc': '立即排一条 3 秒后的本地通知,用于排查系统通知限制', 'settings.testNotify': '发送测试通知',
656
803
  'settings.testNotifySent': '测试通知已排定,3 秒后到达', 'settings.testNotifyFailed': '发送测试通知失败:{msg}', 'settings.testNotifyUnavailable': '测试通知仅在 App 内可用',
657
804
  'settings.peakRemindTitle': '峰谷计费提醒', 'settings.peakRemindDesc': '每天 9:00 / 12:00 / 14:00 / 18:00 推送峰谷切换(App 内有效)',
@@ -674,6 +821,7 @@
674
821
  'presets.nameEmpty': '名称不能为空', 'presets.nameTooLong': '名称不能超过 20 字', 'presets.textTooLong': '内容不能超过 2000 字', 'presets.limit': '最多 20 条预设', 'presets.added': '预设已添加',
675
822
  'presets.confirmDelete': '删除预设「{name}」?', 'presets.saved': '预设已保存', 'presets.deleted': '预设已删除',
676
823
  'settings.toolsTitle': '显示工具调用', 'settings.toolsDesc': '关闭后隐藏 🔧/📦 工具调用与结果',
824
+ 'settings.mobileEnterTitle': '手机回车行为', 'settings.mobileEnterDesc': '选择手机端按 Enter 时换行或直接发送', 'settings.mobileEnterNewline': '回车换行', 'settings.mobileEnterSend': '回车发送',
677
825
  'settings.langTitle': '语言 / Language', 'settings.langDesc': '界面语言,跟随系统或手动切换',
678
826
  'settings.themeTitle': '皮肤', 'settings.themeDesc': '默认深空 / 落日 / 易北爱乐厅 / 草原孤塔',
679
827
  'theme.default': '默认深空', 'theme.dark': '落日', 'theme.light': '易北爱乐厅', 'theme.neutral': '草原孤塔',
@@ -702,6 +850,7 @@
702
850
  'modal.approvalTitle': '工具审批', 'modal.reject': '拒绝', 'modal.allowOnce': '允许一次',
703
851
  'modal.questionTitle': 'DSH 需要你回答', 'modal.later': '稍后', 'modal.submit': '提交',
704
852
  'modal.goalTitle': '目标控制', 'modal.close': '关闭', 'modal.updateGoal': '更新目标',
853
+ 'announcement.label': '公告', 'announcement.later': '稍后再看', 'announcement.gotIt': '知道了', 'announcement.open': '查看详情', 'announcement.historyLabel': '公告记录', 'announcement.historyTitle': '历史公告', 'announcement.historyEmpty': '暂无历史公告', 'announcement.close': '关闭', 'announcement.noDate': '未标注日期',
705
854
  'modal.statsTitle': '本轮统计',
706
855
  'time.justNow': '刚刚', 'time.minAgo': ' 分钟前', 'time.hourAgo': ' 小时前'
707
856
  },
@@ -712,7 +861,14 @@
712
861
  'conn.poll': 'Polling', 'conn.pollTitle': 'Realtime push is unavailable on this network; degraded to polling (a few seconds delay)',
713
862
  'conn.titleGroup': '{group} · {url} ({ms})',
714
863
  'common.refreshing': 'Refreshing…',
715
- 'nav.sessions': 'Sessions', 'nav.files': 'Files', 'nav.pending': 'Inbox', 'nav.stats': 'Stats', 'nav.settings': 'Settings',
864
+ 'nav.sessions': 'Sessions', 'nav.files': 'Files', 'nav.pending': 'Inbox', 'nav.overview': 'Overview', 'nav.home': 'Home', 'nav.stats': 'Stats', 'nav.settings': 'Settings',
865
+ 'overview.eyebrow': 'SYSTEM OVERVIEW', 'overview.title': 'Home', 'overview.subtitle': 'Live status and items needing your attention',
866
+ 'overview.systemPulse': 'System pulse', 'overview.nominal': 'System nominal', 'overview.degraded': 'Some links need attention', 'overview.offline': 'Connection offline', 'overview.checking': 'Checking…', 'overview.components': '{n}/4 components online', 'overview.live': 'LIVE', 'overview.allLinked': 'ALL LINKED', 'overview.offlineCore': 'OFFLINE',
867
+ 'overview.gateway': 'Gateway', 'overview.dsh': 'DSH upstream', 'overview.mux': 'Live mux', 'overview.host': 'Live host', 'overview.online': 'Online', 'overview.offlineShort': 'Offline',
868
+ 'overview.attention': 'Needs attention', 'overview.metrics': 'Runtime metrics', 'overview.activity': 'Recent activity', 'overview.dshVersion': 'DSH version', 'overview.gatewayVersion': 'Gateway status',
869
+ 'overview.activeSessions': 'Active sessions', 'overview.connectionMode': 'Connection mode', 'overview.ws': 'Live WS', 'overview.poll': 'Degraded polling', 'overview.noActivity': 'No background activity', 'overview.noSession': 'No sessions yet',
870
+ 'overview.refresh': 'Refresh overview', 'overview.none': 'Nothing pending', 'overview.pendingCount': '{n} pending', 'overview.activeCount': '{n} active sessions',
871
+ 'overview.action.openSession': 'Open recent session', 'overview.action.newSession': 'New session', 'overview.action.attention': 'Review pending', 'overview.action.connect': 'Set up connection', 'overview.action.refresh': 'Check connection',
716
872
  'statsPage.title': 'Token stats',
717
873
  'statsPage.todayTokens': 'Tokens today', 'statsPage.todayCost': 'Cost today', 'statsPage.peakShare': 'Peak share',
718
874
  'statsPage.input': 'Uncached input', 'statsPage.cacheRead': 'Cache read', 'statsPage.cacheWrite': 'Cache write', 'statsPage.output': 'Output',
@@ -720,6 +876,7 @@
720
876
  'statsPage.gatewayDown': 'Stats require the gateway', 'statsPage.empty': 'No stats yet — they aggregate as sessions happen',
721
877
  'statsPage.note': 'Note: estimates assume the official DeepSeek API. Token-based calculation may differ from the official bill; always defer to deepseek.com. Stats start from the 2026-08-17 pricing date.',
722
878
  'home.newSession': '+ New session', 'home.newWorkspace': '+ Workspace', 'home.empty': 'No sessions yet', 'home.createFailed': 'Failed to create session', 'home.created': 'Session created',
879
+ 'wb.unbound': 'No workbench bound', 'wb.bound': 'Bound {title}', 'wb.noProjects': 'No projects', 'wb.noSessions': 'No sessions', 'wb.newSession': 'New session', 'wb.flatHidden': 'Workbench sessions folded ({n})', 'wb.archivedHidden': '------ Hide archived ------', 'wb.archivedShown': '------ Show archived ------',
723
880
  'workspace.createTitle': 'New workspace', 'workspace.createDesc': 'Create a folder in the current file directory and open a new session there.', 'workspace.parent': 'Parent folder', 'workspace.namePlaceholder': 'For example: my-project', 'workspace.cancel': 'Cancel', 'workspace.create': 'Create & open', 'workspace.nameRequired': 'Enter a workspace name', 'workspace.exists': 'That folder already exists', 'workspace.invalidName': 'The name cannot contain path separators', 'workspace.createFailed': 'Could not create workspace: {msg}', 'workspace.created': 'Workspace created', 'workspace.createdNoSession': 'Workspace created, but the new session could not be opened',
724
881
  'workspace.createTitle': 'New workspace', 'workspace.createDesc': 'Create a folder in the current file directory and open a new session there.', 'workspace.parent': 'Parent folder', 'workspace.namePlaceholder': 'For example: my-project', 'workspace.cancel': 'Cancel', 'workspace.create': 'Create & open', 'workspace.nameRequired': 'Enter a workspace name', 'workspace.created': 'Workspace created', 'workspace.sessionFailed': 'Folder created, but the new session could not be opened: {msg}',
725
882
  'sessions.running': 'Running', 'sessions.statRunning': 'Running', 'sessions.statPending': 'Pending', 'sessions.statTotal': 'Sessions',
@@ -755,7 +912,7 @@
755
912
  'stream.error': 'Event stream error: {msg}',
756
913
  'session.loading': 'Loading…', 'session.unknown': 'Unknown session', 'session.running': 'Running', 'session.interrupted': 'Interrupted',
757
914
  'session.stop': 'Stop', 'session.history': 'Conversation', 'session.errorMsg': 'Session error: {msg}',
758
- 'session.confirmStop': 'Stop the currently running task in this session?', 'session.stopFailed': 'Stop failed', 'session.stopRequested': 'Stop requested',
915
+ 'session.confirmStop': 'Stop the currently running task in this session?', 'session.stopFailed': 'Stop failed', 'session.stopRequested': 'Stop requested', 'session.archive': 'Archive', 'session.archiveConfirm': 'Archive this session? You can open it from “Show archived”.', 'session.archiveFailed': 'Archive failed: {msg}', 'session.archived': 'Session archived', 'session.archiveTitle': 'Confirm archive', 'session.archiveDesc': 'The session will be hidden from the normal list, but its conversation will not be deleted.', 'session.archiveConversation': 'Conversation', 'session.archiveWorkspace': 'Workspace', 'session.archiveCancel': 'Cancel', 'session.archiveConfirmAction': 'Archive session',
759
916
  'notify.approvalTitle': 'Tool approval', 'notify.approvalBody': '{tool} needs approval', 'notify.questionTitle': 'DSH question', 'notify.questionBody': 'Needs your answer',
760
917
  'notify.permissionFailed': 'Notification permission failed: {msg}',
761
918
  'history.loading': 'Loading history…', 'history.cacheFallback': 'Network unavailable: showing cached history', 'history.loadFailed': 'Failed to load history: {msg}', 'history.retry': 'Retry',
@@ -777,7 +934,7 @@
777
934
  'todos.title': 'Todos',
778
935
  'subagent.title': 'Subagents', 'subagent.diagnostic': 'Diagnostic', 'subagent.running': '· running', 'subagent.interrupt': 'Interrupt',
779
936
  'subagent.confirmInterrupt': 'Interrupt this subagent\'s current turn?', 'subagent.interruptFailed': 'Interrupt failed', 'subagent.interruptSubmitted': 'Interrupt requested',
780
- 'send.failed': 'Send failed', 'send.commandSent': 'Command sent', 'send.sent': 'Sent', 'send.commandExecuted': 'Command executed',
937
+ 'send.failed': 'Send failed', 'send.commandSent': 'Command sent', 'send.sent': 'Sent', 'send.imageSent': 'Image sent', 'send.commandExecuted': 'Command executed',
781
938
  'models.loading': 'Loading models…', 'models.loadFailed': 'Failed to load models: {msg}', 'models.unavailable': 'unavailable', 'models.none': 'No models available',
782
939
  'models.switchFailed': 'Model switch failed', 'models.switched': 'Switched model: {model}',
783
940
  'models.effortFailed': 'Failed to switch reasoning effort', 'models.effortSwitched': 'Reasoning effort: {effort}',
@@ -785,8 +942,9 @@
785
942
  'menu.compact': '/compact Compress conversation history', 'menu.export': '/export Export session log ZIP',
786
943
  'menu.feedback': '/feedback Feedback current session', 'menu.goal': '/goal Set/view task goal',
787
944
  'menu.permission': '/permission Switch permission preset', 'menu.plan': '/plan Enter/exit plan mode',
945
+ 'perm.readOnly': 'read-only', 'perm.workspaceWrite': 'workspace-write', 'perm.dangerFullAccess': 'danger-full-access',
788
946
  'menu.modelTitle': 'Model', 'menu.refresh': 'Refresh', 'menu.modelAuto': 'Auto-loads when opened', 'menu.effortTitle': 'Reasoning effort',
789
- 'composer.placeholder': 'Message DSH…', 'composer.send': 'Send',
947
+ 'composer.placeholder': 'Message DSH…', 'composer.send': 'Send', 'composer.fullscreen': 'Fullscreen editor', 'composer.exitFullscreen': 'Exit fullscreen editor', 'composer.addImage': 'Add image', 'composer.takePhoto': 'Camera', 'composer.choosePhoto': 'Gallery', 'composer.imageAdded': 'Image added', 'composer.imageRemoved': 'Image removed', 'composer.removeImage': 'Remove image', 'composer.imageUnsupported': 'Only PNG, JPEG, WebP or GIF images are supported', 'composer.imageTooLarge': 'Each image must be under {size}', 'composer.imageLimit': 'You can add up to {count} images', 'composer.imageSlashUnsupported': 'Slash commands cannot include images', 'composer.imageReadFailed': 'Could not read image: {msg}',
790
948
  'pending.title': 'Inbox', 'pending.count': '{n} items', 'pending.approvalTitle': '🔧 {tool} requests approval', 'pending.noReason': 'No reason given',
791
949
  'pending.allow': 'Allow', 'pending.reject': 'Reject', 'pending.question': '❓ {question}',
792
950
  'pending.questionCount': '{n} questions', 'pending.answer': 'Answer', 'pending.empty': 'Nothing pending',
@@ -837,7 +995,7 @@
837
995
  'settings.back': '‹ Back',
838
996
  'settings.servers': 'Servers (one or more)', 'settings.speedTest': 'Test', 'settings.add': 'Add',
839
997
  'settings.scanTitle': 'Scan to pair', 'settings.scanDesc': 'Photo/gallery QR decoding on device (no Google services); system camera or manual paste also work', 'settings.scan': 'Scan',
840
- 'settings.notifyTitle': 'Notifications', 'settings.notifyDesc': 'Push on approvals / questions',
998
+ 'settings.notifyTitle': 'Notifications', 'settings.notifyDesc': 'Push on approvals / questions', 'settings.announcementHistoryTitle': 'Announcement history', 'settings.announcementHistoryDesc': 'Review announcements you received before', 'settings.announcementHistory': 'View history',
841
999
  'settings.testNotifyTitle': 'Test notification', 'settings.testNotifyDesc': 'Schedule a local notification in 3 seconds to verify the notification pipeline', 'settings.testNotify': 'Send test notification',
842
1000
  'settings.testNotifySent': 'Test notification scheduled, arriving in 3s', 'settings.testNotifyFailed': 'Failed to send test notification: {msg}', 'settings.testNotifyUnavailable': 'Test notification is only available in the app',
843
1001
  'settings.peakRemindTitle': 'Peak/off-peak reminders', 'settings.peakRemindDesc': 'Push at 9:00 / 12:00 / 14:00 / 18:00 daily (in-app only)',
@@ -860,6 +1018,7 @@
860
1018
  'presets.nameEmpty': 'Name cannot be empty', 'presets.nameTooLong': 'Name must be ≤20 characters', 'presets.textTooLong': 'Content must be ≤2000 characters', 'presets.limit': 'At most 20 presets', 'presets.added': 'Preset added',
861
1019
  'presets.confirmDelete': 'Delete preset "{name}"?', 'presets.saved': 'Preset saved', 'presets.deleted': 'Preset deleted',
862
1020
  'settings.toolsTitle': 'Show tool calls', 'settings.toolsDesc': 'Hide 🔧/📦 tool calls and results when off',
1021
+ 'settings.mobileEnterTitle': 'Mobile Enter behavior', 'settings.mobileEnterDesc': 'Choose whether Enter inserts a newline or sends', 'settings.mobileEnterNewline': 'Enter = newline', 'settings.mobileEnterSend': 'Enter = send',
863
1022
  'settings.langTitle': 'Language / 语言', 'settings.langDesc': 'UI language, follows system or switch manually',
864
1023
  'settings.themeTitle': 'Theme', 'settings.themeDesc': 'Default deep space / Sunset / Elbphilharmonie / Prairie Tower',
865
1024
  'theme.default': 'Default', 'theme.dark': 'Sunset', 'theme.light': 'Elbphilharmonie', 'theme.neutral': 'Prairie Tower',
@@ -888,6 +1047,7 @@
888
1047
  'modal.approvalTitle': 'Tool approval', 'modal.reject': 'Reject', 'modal.allowOnce': 'Allow once',
889
1048
  'modal.questionTitle': 'DSH needs your answer', 'modal.later': 'Later', 'modal.submit': 'Submit',
890
1049
  'modal.goalTitle': 'Goal control', 'modal.close': 'Close', 'modal.updateGoal': 'Update goal',
1050
+ 'announcement.label': 'Announcement', 'announcement.later': 'Later', 'announcement.gotIt': 'Got it', 'announcement.open': 'View details', 'announcement.historyLabel': 'Announcement archive', 'announcement.historyTitle': 'Announcement history', 'announcement.historyEmpty': 'No announcement history', 'announcement.close': 'Close', 'announcement.noDate': 'No date',
891
1051
  'modal.statsTitle': 'This round',
892
1052
  'time.justNow': 'just now', 'time.minAgo': ' min ago', 'time.hourAgo': ' hr ago'
893
1053
  }