dsh-remote-plugin 0.6.14 → 0.6.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apk/dsh-remote.apk +0 -0
- package/gateway.cjs +314 -30
- package/package.json +1 -1
- package/public/admin.html +46 -1
- package/public/admin.js +79 -9
- package/public/announcements.json +33 -0
- package/public/app.js +1103 -62
- package/public/desktop/desktop.css +22 -7
- package/public/desktop/desktop.html +24 -4
- package/public/desktop/desktop.js +275 -26
- package/public/index.html +120 -15
- package/public/morphicons-init.js +41 -0
- package/public/motion.js +469 -0
- package/public/plugin.html +4 -1
- package/public/plugin.js +1 -0
- package/public/styles.css +96 -6
- package/public/update.json +12 -12
- package/public/vendor/gsap/NOTICE.md +10 -0
- package/public/vendor/gsap/gsap.min.js +11 -0
- package/public/vendor/morphicons/LICENSE +21 -0
- package/public/vendor/morphicons/README.md +10 -0
- package/public/vendor/morphicons/controller-CXZuwJ_M.js +152 -0
- package/public/vendor/morphicons/dom.js +206 -0
- package/public/vendor/morphicons/element.js +261 -0
- package/public/vendor/morphicons/normalize-CYnN3Npw.js +540 -0
- package/public/vendor/morphicons/spring-CFHloqPP.js +623 -0
- package/public/version.json +1 -1
package/public/index.html
CHANGED
|
@@ -93,6 +93,8 @@
|
|
|
93
93
|
<div id="session-title" class="session-title" data-i18n="session.loading">加载中…</div>
|
|
94
94
|
<div id="session-sub" class="session-sub"></div>
|
|
95
95
|
</div>
|
|
96
|
+
<button id="btn-rename-session" class="icon-btn hidden" data-i18n-title="session.rename" data-i18n-aria="session.rename">✎</button>
|
|
97
|
+
<button id="btn-archive-session" class="mini-btn hidden" data-i18n="session.archive">归档</button>
|
|
96
98
|
<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>
|
|
97
99
|
<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>
|
|
98
100
|
<button id="btn-cancel" class="danger-btn hidden" data-i18n="session.stop">停止</button>
|
|
@@ -158,8 +160,7 @@
|
|
|
158
160
|
<div id="composer-input-wrap" class="composer-input-wrap">
|
|
159
161
|
<textarea id="composer-input" rows="1" data-i18n-placeholder="composer.placeholder" placeholder="给 DSH 发消息…"></textarea>
|
|
160
162
|
<button id="btn-fs-toggle" class="composer-fs-btn hidden" type="button" data-i18n-title="composer.fullscreen" data-i18n-aria="composer.fullscreen">
|
|
161
|
-
<
|
|
162
|
-
<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>
|
|
163
|
+
<morph-icon id="fs-ico" data-morph-state="closed" data-morph-closed="M8 3H3v5M16 3h5v5M8 21H3v-5M21 16v5h-5" data-morph-open="M9 3v6H3M15 3v6h6M9 21v-6H3M21 15h-6v6" aria-hidden="true"><svg 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></morph-icon>
|
|
163
164
|
</button>
|
|
164
165
|
</div>
|
|
165
166
|
<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>
|
|
@@ -243,6 +244,12 @@
|
|
|
243
244
|
<div class="setting-row" data-settings-group="general" style="cursor:pointer">
|
|
244
245
|
<div><div class="setting-name" data-i18n="settings.groupGeneral">通用</div><div class="setting-desc" data-i18n="settings.groupGeneralDesc">显示工具调用、预设提示词</div></div>
|
|
245
246
|
</div>
|
|
247
|
+
<div class="setting-row" data-settings-group="model" style="cursor:pointer">
|
|
248
|
+
<div><div class="setting-name" data-i18n="settings.groupModel">模型</div><div class="setting-desc" data-i18n="settings.groupModelDesc">提供方、API 密钥和模型目录</div></div>
|
|
249
|
+
</div>
|
|
250
|
+
<div class="setting-row" data-settings-group="tests" style="cursor:pointer">
|
|
251
|
+
<div><div class="setting-name" data-i18n="settings.groupTests">功能测试</div><div class="setting-desc" data-i18n="settings.groupTestsDesc">验证 Android 和 DSH 原生能力</div></div>
|
|
252
|
+
</div>
|
|
246
253
|
<div class="setting-row" data-settings-group="servers" style="cursor:pointer">
|
|
247
254
|
<div><div class="setting-name" data-i18n="settings.groupServers">服务器</div><div class="setting-desc" data-i18n="settings.groupServersDesc">服务器地址、扫码连接</div></div>
|
|
248
255
|
</div>
|
|
@@ -321,6 +328,58 @@
|
|
|
321
328
|
</div>
|
|
322
329
|
</div>
|
|
323
330
|
|
|
331
|
+
<div id="settings-page-model" class="settings-subpage hidden">
|
|
332
|
+
<div class="settings-group">
|
|
333
|
+
<div class="setting-row" data-settings-back style="cursor:pointer">
|
|
334
|
+
<button class="mini-btn" data-i18n="settings.back">‹ 返回</button>
|
|
335
|
+
<div class="setting-name" data-i18n="settings.groupModel">模型</div>
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
<div class="settings-group model-settings-shell">
|
|
339
|
+
<div class="model-settings-head">
|
|
340
|
+
<div>
|
|
341
|
+
<div class="setting-name" data-i18n="settings.modelTitle">模型</div>
|
|
342
|
+
<div class="setting-desc expanded" data-i18n="settings.modelIntro">填入各提供方的 API 密钥即可使用其模型。</div>
|
|
343
|
+
</div>
|
|
344
|
+
<div class="setting-actions">
|
|
345
|
+
<button id="btn-model-settings-open" class="mini-btn" type="button" data-i18n="settings.modelOpenConfig">打开配置文件</button>
|
|
346
|
+
<button id="btn-model-settings-refresh" class="mini-btn" type="button" data-i18n="settings.modelRefresh">刷新</button>
|
|
347
|
+
</div>
|
|
348
|
+
</div>
|
|
349
|
+
<div id="model-settings-status" class="model-settings-status muted" data-i18n="settings.modelLoading">加载模型配置…</div>
|
|
350
|
+
<div id="model-settings-list" class="model-settings-list"></div>
|
|
351
|
+
</div>
|
|
352
|
+
</div>
|
|
353
|
+
|
|
354
|
+
<div id="settings-page-tests" class="settings-subpage hidden">
|
|
355
|
+
<div class="settings-group">
|
|
356
|
+
<div class="setting-row" data-settings-back style="cursor:pointer">
|
|
357
|
+
<button class="mini-btn" data-i18n="settings.back">‹ 返回</button>
|
|
358
|
+
<div class="setting-name" data-i18n="settings.groupTests">功能测试</div>
|
|
359
|
+
</div>
|
|
360
|
+
</div>
|
|
361
|
+
<div class="settings-group feature-test-shell">
|
|
362
|
+
<div class="feature-test-head">
|
|
363
|
+
<div>
|
|
364
|
+
<div class="setting-name" data-i18n="settings.asrTestTitle">ASR 模型测试(小米手机)</div>
|
|
365
|
+
<div class="setting-desc expanded" data-i18n="settings.asrTestDesc">使用 Android 系统 SpeechRecognizer 验证小米/系统云端语音识别,不上传测试日志。</div>
|
|
366
|
+
</div>
|
|
367
|
+
</div>
|
|
368
|
+
<div class="feature-test-note" data-i18n="settings.asrTestInstruction">请保持页面在前台,朗读一段中文,包含停顿、数字和英文 API 名称。测试约 60 秒,会自动记录 partial、最终结果、重建 session 和错误。</div>
|
|
369
|
+
<div class="feature-test-actions">
|
|
370
|
+
<button id="btn-asr-test-start" class="mini-btn primary" type="button" data-i18n="settings.asrTestStart">开始测试</button>
|
|
371
|
+
<button id="btn-asr-test-stop" class="mini-btn" type="button" data-i18n="settings.asrTestStop" disabled>停止测试</button>
|
|
372
|
+
<button id="btn-asr-test-copy" class="mini-btn" type="button" data-i18n="settings.asrTestCopy" disabled>复制日志</button>
|
|
373
|
+
<button id="btn-asr-test-clear" class="mini-btn" type="button" data-i18n="settings.asrTestClear">清空日志</button>
|
|
374
|
+
<button id="btn-asr-test-permission" class="mini-btn hidden" type="button" data-i18n="settings.asrTestPermissionSettings">权限设置</button>
|
|
375
|
+
<button id="btn-asr-test-engine" class="mini-btn hidden" type="button" data-i18n="settings.asrTestEngineSettings">语音引擎设置</button>
|
|
376
|
+
</div>
|
|
377
|
+
<div id="asr-test-status" class="feature-test-status muted" role="status" data-i18n="settings.asrTestNativeOnly">仅 Android App 可用</div>
|
|
378
|
+
<div id="asr-test-summary" class="feature-test-summary"></div>
|
|
379
|
+
<pre id="asr-test-log" class="feature-test-log" data-i18n="settings.asrTestLogEmpty">暂无测试日志</pre>
|
|
380
|
+
</div>
|
|
381
|
+
</div>
|
|
382
|
+
|
|
324
383
|
<div id="settings-page-servers" class="settings-subpage hidden">
|
|
325
384
|
<div class="settings-group">
|
|
326
385
|
<div class="setting-row" data-settings-back style="cursor:pointer">
|
|
@@ -433,7 +492,7 @@
|
|
|
433
492
|
<button id="btn-host-describe" class="mini-btn" data-i18n="settings.probe">探测</button>
|
|
434
493
|
</div>
|
|
435
494
|
<div class="setting-row">
|
|
436
|
-
<div><div class="setting-name" data-i18n="settings.dshControlTitle">远程启动 DSH</div><div class="setting-desc expanded" id="dsh-control-desc" data-i18n="settings.dshControlProbing">检查本机服务状态…</div><div id="dsh-control-steps" class="dsh-control-steps hidden" aria-live="polite"></div></div>
|
|
495
|
+
<div><div class="setting-name"><span data-i18n="settings.dshControlTitle">远程启动 DSH</span><span class="dsh-experimental-badge" data-i18n="settings.dshExperimental">Windows 实验性</span></div><div class="setting-desc expanded" id="dsh-control-desc" data-i18n="settings.dshControlProbing">检查本机服务状态…</div><div id="dsh-control-steps" class="dsh-control-steps hidden" aria-live="polite"></div></div>
|
|
437
496
|
<div class="setting-actions">
|
|
438
497
|
<button id="btn-dsh-start" class="mini-btn" data-i18n="settings.dshStart">启动</button>
|
|
439
498
|
<button id="btn-dsh-restart" class="mini-btn" data-i18n="settings.dshRestart">重启</button>
|
|
@@ -653,6 +712,19 @@
|
|
|
653
712
|
</div>
|
|
654
713
|
</div>
|
|
655
714
|
|
|
715
|
+
<div id="modal-rename" class="modal hidden" role="dialog" aria-modal="true" aria-labelledby="rename-session-title">
|
|
716
|
+
<div class="modal-card">
|
|
717
|
+
<div id="rename-session-title" class="modal-title" data-i18n="session.renameTitle">重命名会话</div>
|
|
718
|
+
<div class="modal-body">
|
|
719
|
+
<input id="rename-session-input" class="text-input" maxlength="160" autocomplete="off" data-i18n-placeholder="session.renamePlaceholder" placeholder="输入新的会话名称">
|
|
720
|
+
</div>
|
|
721
|
+
<div class="modal-actions">
|
|
722
|
+
<button id="rename-cancel" class="btn subtle" data-i18n="session.renameCancel">取消</button>
|
|
723
|
+
<button id="rename-confirm" class="btn primary" data-i18n="session.renameConfirm">保存</button>
|
|
724
|
+
</div>
|
|
725
|
+
</div>
|
|
726
|
+
</div>
|
|
727
|
+
|
|
656
728
|
<!-- goal 模态 -->
|
|
657
729
|
<div id="modal-goal" class="modal hidden" role="dialog" aria-modal="true">
|
|
658
730
|
<div class="modal-card">
|
|
@@ -812,7 +884,7 @@
|
|
|
812
884
|
'statsPage.gatewayDown': '统计需要网关运行', 'statsPage.empty': '暂无统计,产生会话后自动聚合',
|
|
813
885
|
'statsPage.note': '注:本数据仅在使用 DeepSeek 官方 API 时估算;基于 token 计算,与官网账单可能有出入,一切以官网为准。统计自 2026-08-17 定价生效日起。',
|
|
814
886
|
'home.newSession': '+ 新会话', 'home.newWorkspace': '+ 工作区', 'home.empty': '暂无会话', 'home.createFailed': '新建会话失败', 'home.created': '会话已创建',
|
|
815
|
-
'wb.unbound': '未绑定工作台', 'wb.bound': '绑定 {title}', 'wb.noProjects': '暂无项目', 'wb.noSessions': '暂无会话', 'wb.newSession': '新会话', 'wb.flatHidden': '工作台会话已收起({n} 个)', 'wb.archivedHidden': '------隐藏已归档会话------', 'wb.archivedShown': '------显示已归档会话------',
|
|
887
|
+
'wb.unbound': '未绑定工作台', 'wb.bound': '绑定 {title}', 'wb.noProjects': '暂无项目', 'wb.noSessions': '暂无会话', 'wb.newSession': '新会话', 'wb.orderSaved': '顺序已保存到当前设备', 'wb.flatHidden': '工作台会话已收起({n} 个)', 'wb.archivedHidden': '------隐藏已归档会话------', 'wb.archivedShown': '------显示已归档会话------',
|
|
816
888
|
'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': '工作区已创建,但新会话未能打开',
|
|
817
889
|
'workspace.createTitle': '新建工作区', 'workspace.createDesc': '将在当前文件目录下创建文件夹,并自动打开新会话。', 'workspace.parent': '父目录', 'workspace.namePlaceholder': '例如:my-project', 'workspace.cancel': '取消', 'workspace.create': '创建并打开', 'workspace.nameRequired': '请输入工作区名称', 'workspace.created': '工作区已创建', 'workspace.sessionFailed': '目录已创建,但新会话打开失败:{msg}',
|
|
818
890
|
'workspace.current': '当前工作区', 'workspace.select': '选择工作区', 'workspace.all': '全部工作区', 'workspace.ungrouped': '未分组会话', 'workspace.files': '查看文件', 'workspace.allPath': '显示全部 DSH 工作区中的会话',
|
|
@@ -849,8 +921,8 @@
|
|
|
849
921
|
'groups.confirmDelete': '删除组「{group}」?组内服务器将归入默认组。', 'groups.deleted': '组已删除',
|
|
850
922
|
'stream.error': '事件流错误:{msg}',
|
|
851
923
|
'session.loading': '加载中…', 'session.unknown': '未知会话', 'session.running': '运行中', 'session.interrupted': '已中断',
|
|
852
|
-
'session.stop': '
|
|
853
|
-
'session.confirmStop': '
|
|
924
|
+
'session.stop': '停止本轮', 'session.history': '对话', 'session.errorMsg': '会话出错:{msg}', 'session.recovering': '恢复会话中…', 'session.recoveryReady': '会话已恢复', 'session.recoveryFailed': '会话恢复失败', 'session.recoveryCached': '正在查看缓存历史',
|
|
925
|
+
'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': '确认归档', 'session.rename': '重命名会话', 'session.renameTitle': '重命名会话', 'session.renamePlaceholder': '输入新的会话名称', 'session.renameCancel': '取消', 'session.renameConfirm': '保存', 'session.renameEmpty': '会话名称不能为空', 'session.renameFailed': '重命名失败', 'session.renamed': '会话名称已更新',
|
|
854
926
|
'notify.approvalTitle': '工具审批', 'notify.approvalBody': '{tool} 需要批准', 'notify.questionTitle': 'DSH 提问', 'notify.questionBody': '需要你回答',
|
|
855
927
|
'notify.permissionFailed': '通知权限申请失败:{msg}',
|
|
856
928
|
'history.loading': '加载历史…', 'history.cacheFallback': '网络不可用:显示本地缓存的历史', 'history.loadFailed': '加载历史失败:{msg}', 'history.retry': '重试',
|
|
@@ -930,13 +1002,28 @@
|
|
|
930
1002
|
'scan.paired': '配对成功,正在连接', 'scan.pairedLink': '已通过二维码配对', 'scan.notPair': '这不是 DSH Remote 的配对二维码',
|
|
931
1003
|
'scan.cancelled': '已取消扫码', 'scan.failed': '扫码失败:{msg}',
|
|
932
1004
|
'settings.groupGeneral': '通用', 'settings.groupGeneralDesc': '显示工具调用、预设提示词',
|
|
1005
|
+
'settings.groupModel': '模型', 'settings.groupModelDesc': '提供方、API 密钥和模型目录',
|
|
1006
|
+
'settings.groupTests': '功能测试', 'settings.groupTestsDesc': '验证 Android 和 DSH 原生能力',
|
|
933
1007
|
'settings.groupServers': '服务器', 'settings.groupServersDesc': '服务器地址、扫码连接',
|
|
934
1008
|
'settings.groupNotify': '通知', 'settings.groupNotifyDesc': '通知、提醒、后台轮询',
|
|
935
1009
|
'settings.groupTheme': '皮肤', 'settings.groupThemeDesc': '配色主题',
|
|
936
1010
|
'settings.groupAbout': '关于', 'settings.groupAboutDesc': 'DSH 状态、检查更新',
|
|
937
1011
|
'settings.donate': '赞赏支持', 'settings.donateDesc': '如果 dsh-remote 帮到了你,欢迎赞赏支持开发 ☕', 'settings.donateThanks': '感谢你的支持 ☕',
|
|
938
1012
|
'settings.back': '‹ 返回',
|
|
1013
|
+
'settings.modelTitle': '模型', 'settings.modelIntro': '填入各提供方的 API 密钥即可使用其模型。', 'settings.modelRefresh': '刷新', 'settings.modelOpenConfig': '打开配置文件',
|
|
1014
|
+
'settings.modelLoading': '加载模型配置…', 'settings.modelUnavailable': '模型配置暂不可用:{msg}', 'settings.modelEmpty': '当前 DSH 未声明可配置的提供方', 'settings.modelReadOnly': '当前配置只读',
|
|
1015
|
+
'settings.modelConfigured': '已配置', 'settings.modelMissing': '未配置', 'settings.modelEdit': '编辑', 'settings.modelSave': '保存', 'settings.modelSaving': '保存中…', 'settings.modelCancel': '取消',
|
|
1016
|
+
'settings.modelApiKey': 'API 密钥', 'settings.modelApiKeyPlaceholder': '输入 API 密钥(留空保持不变)', 'settings.modelApiKeyStored': '已保存的密钥,留空保持不变', 'settings.modelClearKey': '清除密钥',
|
|
1017
|
+
'settings.modelBaseUrl': 'API 地址', 'settings.modelBaseUrlPlaceholder': '提供方默认', 'settings.modelProtocol': '协议', 'settings.modelDisplayName': '显示名称',
|
|
1018
|
+
'settings.modelCatalog': '模型目录', 'settings.modelCatalogHint': '模型选择器使用以下模型;不修改则使用提供方默认目录', 'settings.modelFetch': '获取可用模型', 'settings.modelFetching': '获取中…',
|
|
1019
|
+
'settings.modelReasoning': '思考深度档位', 'settings.modelReasoningHint': '按模型配置可选档位;实际请求参数由 DSH 提供方适配器解释', 'settings.modelReasoningAdd': '添加档位', 'settings.modelReasoningClear': '清除档位', 'settings.modelReasoningRemove': '删除档位', 'settings.modelReasoningId': '档位值', 'settings.modelReasoningName': '显示名称', 'settings.modelReasoningDescription': '说明(可选)', 'settings.modelReasoningEmpty': '未配置档位,使用提供方默认行为', 'settings.modelReasoningDefault': '默认档位', 'settings.modelReasoningProviderDefault': '使用提供方默认', 'settings.modelReasoningIdRequired': '思考档位值不能为空', 'settings.modelReasoningInvalid': '思考档位值仅支持字母、数字、点、下划线、冒号和连字符', 'settings.modelReasoningDuplicate': '同一模型的思考档位值不能重复', 'settings.modelReasoningDefaultInvalid': '默认思考档位必须是已配置的档位', 'settings.modelReasoningLimit': '每个模型最多配置 {count} 个思考档位',
|
|
1020
|
+
'settings.modelFetchFailed': '获取模型失败:{msg}', 'settings.modelFetchEmpty': '提供方没有返回可用模型', 'settings.modelAdd': '添加模型', 'settings.modelId': '模型 ID', 'settings.modelIdRequired': '模型 ID 不能为空', 'settings.modelName': '名称(可选)', 'settings.modelRemove': '删除模型',
|
|
1021
|
+
'settings.modelNoModels': '暂无自定义模型,当前使用提供方默认目录', 'settings.modelCandidates': '发现的模型', 'settings.modelSelectAll': '全选', 'settings.modelSelectNone': '取消全选', 'settings.modelAddSelected': '加入选中模型',
|
|
1022
|
+
'settings.modelAdded': '已加入模型目录', 'settings.modelSaved': '模型配置已保存', 'settings.modelSaveFailed': '保存模型配置失败:{msg}', 'settings.modelOpenConfigFailed': '打开配置文件失败:{msg}', 'settings.modelOpenedConfig': '已请求打开 DSH 配置文件',
|
|
1023
|
+
'settings.modelLive': '保存后立即生效', 'settings.modelRestart': '保存后需重启 DSH 才生效',
|
|
1024
|
+
'settings.asrTestTitle': 'ASR 模型测试(小米手机)', 'settings.asrTestDesc': '使用 Android 系统 SpeechRecognizer 验证小米/系统云端语音识别,不上传测试日志。', 'settings.asrTestInstruction': '请保持页面在前台,朗读一段中文,包含停顿、数字和英文 API 名称。测试约 60 秒,会自动记录 partial、最终结果、重建 session 和错误。', 'settings.asrTestStart': '开始测试', 'settings.asrTestStop': '停止测试', 'settings.asrTestCopy': '复制日志', 'settings.asrTestClear': '清空日志', 'settings.asrTestPermissionSettings': '权限设置', 'settings.asrTestNativeOnly': '仅 Android App 可用', 'settings.asrTestWebUnsupported': '浏览器不支持原生 ASR 诊断,请在 Android App 中打开。', 'settings.asrTestConsent': '测试会使用麦克风,并可能将音频发送给 Android 系统默认语音识别服务。是否继续?', 'settings.asrTestStarted': 'ASR 测试已开始,请朗读测试内容', 'settings.asrTestStopped': 'ASR 测试已停止', 'settings.asrTestRestarting': '识别 session 重建中…', 'settings.asrTestPermission': '正在请求麦克风权限…', 'settings.asrTestPermissionDenied': '未授予麦克风权限,无法测试 ASR', 'settings.asrTestPermissionError': '系统语音识别服务未获麦克风授权,请打开权限设置后重试', 'settings.asrTestUnavailable': '当前设备没有可用的系统语音识别服务', 'settings.asrTestBusy': '已有 ASR 测试正在运行', 'settings.asrTestCopyOk': '测试日志已复制', 'settings.asrTestCopyFailed': '复制测试日志失败', 'settings.asrTestLogEmpty': '暂无测试日志', 'settings.asrTestMeta': '设备信息', 'settings.asrTestSummary': '测试摘要', 'settings.asrTestEvent': '事件日志', 'settings.asrTestCloud': '系统默认识别服务可能使用云端 ASR;本测试不上传日志', 'settings.asrTestStatus': '状态:{status}',
|
|
939
1025
|
'settings.servers': '服务器地址(可多个)', 'settings.speedTest': '测速', 'settings.add': '添加',
|
|
1026
|
+
'settings.asrTestEngineSettings': '语音引擎设置',
|
|
940
1027
|
'settings.scanTitle': '扫码连接', 'settings.scanDesc': '实时相机识别二维码,也可拍照/相册本地解码;无需谷歌服务', 'settings.scan': '扫码',
|
|
941
1028
|
'settings.notifyTitle': '通知', 'settings.notifyDesc': '收到审批/提问时推送', 'settings.announcementHistoryTitle': '历史公告', 'settings.announcementHistoryDesc': '查看以前收到的公告', 'settings.announcementHistory': '查看记录',
|
|
942
1029
|
'settings.testNotifyTitle': '测试通知', 'settings.testNotifyDesc': '立即排一条 3 秒后的本地通知,用于排查系统通知限制', 'settings.testNotify': '发送测试通知',
|
|
@@ -968,11 +1055,11 @@
|
|
|
968
1055
|
'theme.panelTitle': '选择配色', 'theme.close': '关闭',
|
|
969
1056
|
'settings.tokenTitle': '访问令牌', 'settings.change': '更换',
|
|
970
1057
|
'settings.hostTitle': 'DSH 状态', 'settings.hostProbing': '探测中…', 'settings.probe': '探测', 'settings.probeFailed': '探测失败',
|
|
971
|
-
'settings.dshControlTitle': '远程启动 DSH', 'settings.dshControlProbing': '正在向网关查询本机
|
|
1058
|
+
'settings.dshControlTitle': '远程启动 DSH', 'settings.dshExperimental': 'Windows 实验性', 'settings.dshControlProbing': '正在向网关查询本机 DSH 服务…', 'settings.dshRunning': '运行中', 'settings.dshStopped': '已停止', 'settings.dshUnsupported': '当前系统未配置可控的 DSH 服务', 'settings.dshStart': '启动', 'settings.dshRestart': '重启', 'settings.dshStarting': '正在{action} DSH…', 'settings.dshStarted': 'DSH 已{action}', 'settings.dshFailed': 'DSH 操作失败:{msg}',
|
|
972
1059
|
'settings.dshRunningDetail': '运行中 · {service} · PID {pid} · {state}', 'settings.dshStoppedDetail': '已停止 · {service} · {state}', 'settings.dshStatusFailed': '服务状态查询失败:{msg}',
|
|
973
|
-
'settings.dshStageQueued': '请求已到达网关,等待检查服务', 'settings.dshStageChecking': '正在检查
|
|
1060
|
+
'settings.dshStageQueued': '请求已到达网关,等待检查服务', 'settings.dshStageChecking': '正在检查 DSH 服务 {service}', 'settings.dshStageCommand': '服务已确认,正在提交{action}命令', 'settings.dshStageWaitingService': '命令已接受,等待服务进程运行({seconds} 秒)', 'settings.dshStageWaitingUpstream': '服务进程已运行,等待 DSH HTTP 接口恢复({seconds} 秒)', 'settings.dshStageWaitingEvents': 'DSH HTTP 已恢复,等待实时消息通道连接({seconds} 秒)',
|
|
974
1061
|
'settings.dshSuccessDetail': 'DSH {action}成功 · PID {pid} · HTTP {status} · 用时 {seconds} 秒', 'settings.dshAlreadyRunning': 'DSH 已在运行 · PID {pid}', 'settings.dshResultUnknown': '与网关的连接在操作中断开,最终结果未知;请重新进入此页查看服务状态',
|
|
975
|
-
'settings.dshErrorServiceNotFound': '未找到
|
|
1062
|
+
'settings.dshErrorServiceNotFound': '未找到 DSH 服务 {service},请先安装或配置 DSH_REMOTE_DSH_SERVICE', 'settings.dshErrorInvalidService': 'DSH 服务名配置不合法', 'settings.dshErrorSystemctlNotFound': '系统中找不到 systemctl', 'settings.dshErrorSystemdUnavailable': '无法连接当前用户的 systemd 会话', 'settings.dshErrorServiceControlNotFound': '系统中找不到 sc.exe', 'settings.dshErrorServiceDisabled': 'Windows DSH 服务已被禁用', 'settings.dshErrorServiceStopTimeout': 'Windows DSH 服务停止超时', 'settings.dshErrorStatusParseFailed': '无法解析 Windows DSH 服务状态', 'settings.dshErrorPermissionDenied': '当前用户无权控制 DSH 服务', 'settings.dshErrorCommandTimeout': '服务控制命令超时', 'settings.dshErrorCommandFailed': '服务控制命令失败', 'settings.dshErrorServiceFailed': 'DSH 服务启动后进入 failed 状态', 'settings.dshErrorServiceTimeout': 'DSH 服务未在限时内进入运行状态', 'settings.dshErrorUpstreamTimeout': 'DSH 进程已运行,但 HTTP 接口未在限时内恢复', 'settings.dshErrorEventsTimeout': 'DSH 服务和 HTTP 已恢复,但实时消息通道未在限时内连接', 'settings.dshErrorOperationNotFound': '网关找不到该操作,可能在期间重启过', 'settings.dshErrorOperationProgress': '已有一个 DSH 启停操作正在执行', 'settings.dshErrorGeneric': '{msg}', 'settings.dshErrorDetail': '详情:{detail}',
|
|
976
1063
|
'settings.hostDesc': 'DSH {version} · {cwd} · 附加会话 {n}',
|
|
977
1064
|
'settings.updateTitle': '检查更新', 'settings.updateLoading': '加载中…', 'settings.downloadUpdate': '下载并安装更新', 'settings.check': '检查',
|
|
978
1065
|
'settings.feedbackTitle': '反馈渠道', 'settings.feedbackDesc': 'GitHub / Gitee / B站:反馈 bug、提建议、唠嗑',
|
|
@@ -1024,7 +1111,7 @@
|
|
|
1024
1111
|
'statsPage.gatewayDown': 'Stats require the gateway', 'statsPage.empty': 'No stats yet — they aggregate as sessions happen',
|
|
1025
1112
|
'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.',
|
|
1026
1113
|
'home.newSession': '+ New session', 'home.newWorkspace': '+ Workspace', 'home.empty': 'No sessions yet', 'home.createFailed': 'Failed to create session', 'home.created': 'Session created',
|
|
1027
|
-
'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 ------',
|
|
1114
|
+
'wb.unbound': 'No workbench bound', 'wb.bound': 'Bound {title}', 'wb.noProjects': 'No projects', 'wb.noSessions': 'No sessions', 'wb.newSession': 'New session', 'wb.orderSaved': 'Order saved on this device', 'wb.flatHidden': 'Workbench sessions folded ({n})', 'wb.archivedHidden': '------ Hide archived ------', 'wb.archivedShown': '------ Show archived ------',
|
|
1028
1115
|
'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',
|
|
1029
1116
|
'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}',
|
|
1030
1117
|
'workspace.current': 'Current workspace', 'workspace.select': 'Select workspace', 'workspace.all': 'All workspaces', 'workspace.ungrouped': 'Ungrouped sessions', 'workspace.files': 'View files', 'workspace.allPath': 'Showing sessions from every DSH workspace',
|
|
@@ -1061,8 +1148,8 @@
|
|
|
1061
1148
|
'groups.confirmDelete': 'Delete group "{group}"? Its servers move to the default group.', 'groups.deleted': 'Group deleted',
|
|
1062
1149
|
'stream.error': 'Event stream error: {msg}',
|
|
1063
1150
|
'session.loading': 'Loading…', 'session.unknown': 'Unknown session', 'session.running': 'Running', 'session.interrupted': 'Interrupted',
|
|
1064
|
-
'session.stop': 'Stop', 'session.history': 'Conversation', 'session.errorMsg': 'Session error: {msg}',
|
|
1065
|
-
'session.confirmStop': 'Stop the
|
|
1151
|
+
'session.stop': 'Stop turn', 'session.history': 'Conversation', 'session.errorMsg': 'Session error: {msg}', 'session.recovering': 'Restoring session…', 'session.recoveryReady': 'Session ready', 'session.recoveryFailed': 'Session restore failed', 'session.recoveryCached': 'Viewing cached history',
|
|
1152
|
+
'session.confirmStop': 'Stop the current turn? Queued messages will be kept.', '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', 'session.rename': 'Rename session', 'session.renameTitle': 'Rename session', 'session.renamePlaceholder': 'Enter a new session name', 'session.renameCancel': 'Cancel', 'session.renameConfirm': 'Save', 'session.renameEmpty': 'Session name cannot be empty', 'session.renameFailed': 'Rename failed', 'session.renamed': 'Session name updated',
|
|
1066
1153
|
'notify.approvalTitle': 'Tool approval', 'notify.approvalBody': '{tool} needs approval', 'notify.questionTitle': 'DSH question', 'notify.questionBody': 'Needs your answer',
|
|
1067
1154
|
'notify.permissionFailed': 'Notification permission failed: {msg}',
|
|
1068
1155
|
'history.loading': 'Loading history…', 'history.cacheFallback': 'Network unavailable: showing cached history', 'history.loadFailed': 'Failed to load history: {msg}', 'history.retry': 'Retry',
|
|
@@ -1142,13 +1229,28 @@
|
|
|
1142
1229
|
'scan.paired': 'Paired, connecting', 'scan.pairedLink': 'Paired via QR code', 'scan.notPair': 'Not a DSH Remote pairing QR code',
|
|
1143
1230
|
'scan.cancelled': 'Scan cancelled', 'scan.failed': 'Scan failed: {msg}',
|
|
1144
1231
|
'settings.groupGeneral': 'General', 'settings.groupGeneralDesc': 'Tool calls, prompt presets',
|
|
1232
|
+
'settings.groupModel': 'Models', 'settings.groupModelDesc': 'Providers, API keys, and model catalogs',
|
|
1233
|
+
'settings.groupTests': 'Feature tests', 'settings.groupTestsDesc': 'Check Android and DSH native capabilities',
|
|
1145
1234
|
'settings.groupServers': 'Servers', 'settings.groupServersDesc': 'Server address, scan to pair',
|
|
1146
1235
|
'settings.groupNotify': 'Notifications', 'settings.groupNotifyDesc': 'Notifications, reminders, background polling',
|
|
1147
1236
|
'settings.groupTheme': 'Theme', 'settings.groupThemeDesc': 'Color themes',
|
|
1148
1237
|
'settings.groupAbout': 'About', 'settings.groupAboutDesc': 'DSH status, updates',
|
|
1149
1238
|
'settings.donate': 'Support', 'settings.donateDesc': 'If dsh-remote has been helpful to you, feel free to support development with a coffee ☕', 'settings.donateThanks': 'Thanks for your support ☕',
|
|
1150
1239
|
'settings.back': '‹ Back',
|
|
1240
|
+
'settings.modelTitle': 'Models', 'settings.modelIntro': 'Add a provider API key to use its models.', 'settings.modelRefresh': 'Refresh', 'settings.modelOpenConfig': 'Open config file',
|
|
1241
|
+
'settings.modelLoading': 'Loading model configuration…', 'settings.modelUnavailable': 'Model configuration unavailable: {msg}', 'settings.modelEmpty': 'DSH did not declare any configurable providers', 'settings.modelReadOnly': 'Configuration is read-only',
|
|
1242
|
+
'settings.modelConfigured': 'Configured', 'settings.modelMissing': 'Not configured', 'settings.modelEdit': 'Edit', 'settings.modelSave': 'Save', 'settings.modelSaving': 'Saving…', 'settings.modelCancel': 'Cancel',
|
|
1243
|
+
'settings.modelApiKey': 'API key', 'settings.modelApiKeyPlaceholder': 'Enter an API key (blank keeps the current key)', 'settings.modelApiKeyStored': 'A key is stored; blank keeps it', 'settings.modelClearKey': 'Clear key',
|
|
1244
|
+
'settings.modelBaseUrl': 'API URL', 'settings.modelBaseUrlPlaceholder': 'Provider default', 'settings.modelProtocol': 'Protocol', 'settings.modelDisplayName': 'Display name',
|
|
1245
|
+
'settings.modelCatalog': 'Model catalog', 'settings.modelCatalogHint': 'The model picker uses this list; leave it unchanged to use the provider catalog', 'settings.modelFetch': 'Fetch available models', 'settings.modelFetching': 'Fetching…',
|
|
1246
|
+
'settings.modelReasoning': 'Reasoning effort', 'settings.modelReasoningHint': 'Define selectable efforts per model; the DSH provider adapter interprets the request value', 'settings.modelReasoningAdd': 'Add effort', 'settings.modelReasoningClear': 'Clear efforts', 'settings.modelReasoningRemove': 'Remove effort', 'settings.modelReasoningId': 'Effort value', 'settings.modelReasoningName': 'Display name', 'settings.modelReasoningDescription': 'Description (optional)', 'settings.modelReasoningEmpty': 'No efforts configured; use the provider default behavior', 'settings.modelReasoningDefault': 'Default effort', 'settings.modelReasoningProviderDefault': 'Provider default', 'settings.modelReasoningIdRequired': 'Effort value cannot be empty', 'settings.modelReasoningInvalid': 'Effort values may contain only letters, numbers, dots, underscores, colons, and hyphens', 'settings.modelReasoningDuplicate': 'Effort values must be unique per model', 'settings.modelReasoningDefaultInvalid': 'The default effort must be one of the configured efforts', 'settings.modelReasoningLimit': 'Each model can have at most {count} reasoning efforts',
|
|
1247
|
+
'settings.modelFetchFailed': 'Could not fetch models: {msg}', 'settings.modelFetchEmpty': 'The provider returned no models', 'settings.modelAdd': 'Add model', 'settings.modelId': 'Model ID', 'settings.modelIdRequired': 'Model ID cannot be empty', 'settings.modelName': 'Name (optional)', 'settings.modelRemove': 'Remove model',
|
|
1248
|
+
'settings.modelNoModels': 'No custom models; using the provider default catalog', 'settings.modelCandidates': 'Discovered models', 'settings.modelSelectAll': 'Select all', 'settings.modelSelectNone': 'Clear selection', 'settings.modelAddSelected': 'Add selected models',
|
|
1249
|
+
'settings.modelAdded': 'Models added to the catalog', 'settings.modelSaved': 'Model configuration saved', 'settings.modelSaveFailed': 'Could not save model configuration: {msg}', 'settings.modelOpenConfigFailed': 'Could not open config file: {msg}', 'settings.modelOpenedConfig': 'Requested DSH config file',
|
|
1250
|
+
'settings.modelLive': 'Takes effect immediately', 'settings.modelRestart': 'Restart DSH to apply',
|
|
1251
|
+
'settings.asrTestTitle': 'ASR model test (Xiaomi)', 'settings.asrTestDesc': 'Use Android SpeechRecognizer to test Xiaomi/system cloud speech recognition. Test logs are not uploaded.', 'settings.asrTestInstruction': 'Keep this page in the foreground and read Chinese text containing pauses, numbers, and an English API name. The test runs for about 60 seconds and records partial/final results, session rebuilds, and errors.', 'settings.asrTestStart': 'Start test', 'settings.asrTestStop': 'Stop test', 'settings.asrTestRestarting': 'Rebuilding recognition session…', 'settings.asrTestCopy': 'Copy log', 'settings.asrTestClear': 'Clear log', 'settings.asrTestPermissionSettings': 'Permission settings', 'settings.asrTestNativeOnly': 'Available only in the Android app', 'settings.asrTestWebUnsupported': 'Native ASR diagnostics are unavailable in a browser. Open this page in the Android app.', 'settings.asrTestConsent': 'This test uses the microphone and may send audio to the Android system default speech recognition service. Continue?', 'settings.asrTestStarted': 'ASR test started; read the test content aloud', 'settings.asrTestStopped': 'ASR test stopped', 'settings.asrTestPermission': 'Requesting microphone permission…', 'settings.asrTestPermissionDenied': 'Microphone permission was not granted; ASR cannot be tested', 'settings.asrTestPermissionError': 'The system speech service lacks microphone authorization. Open permission settings and try again.', 'settings.asrTestUnavailable': 'No system speech recognition service is available on this device', 'settings.asrTestBusy': 'An ASR test is already running', 'settings.asrTestCopyOk': 'Test log copied', 'settings.asrTestCopyFailed': 'Could not copy the test log', 'settings.asrTestLogEmpty': 'No test log yet', 'settings.asrTestMeta': 'Device information', 'settings.asrTestSummary': 'Test summary', 'settings.asrTestEvent': 'Event log', 'settings.asrTestCloud': 'The system default recognition service may use cloud ASR; this test does not upload logs', 'settings.asrTestStatus': 'Status: {status}',
|
|
1151
1252
|
'settings.servers': 'Servers (one or more)', 'settings.speedTest': 'Test', 'settings.add': 'Add',
|
|
1253
|
+
'settings.asrTestEngineSettings': 'Speech engine settings',
|
|
1152
1254
|
'settings.scanTitle': 'Scan to pair', 'settings.scanDesc': 'Use the live camera or photo/gallery QR decoding; no Google services required', 'settings.scan': 'Scan',
|
|
1153
1255
|
'settings.notifyTitle': 'Notifications', 'settings.notifyDesc': 'Push on approvals / questions', 'settings.announcementHistoryTitle': 'Announcement history', 'settings.announcementHistoryDesc': 'Review announcements you received before', 'settings.announcementHistory': 'View history',
|
|
1154
1256
|
'settings.testNotifyTitle': 'Test notification', 'settings.testNotifyDesc': 'Schedule a local notification in 3 seconds to verify the notification pipeline', 'settings.testNotify': 'Send test notification',
|
|
@@ -1180,11 +1282,11 @@
|
|
|
1180
1282
|
'theme.panelTitle': 'Choose theme', 'theme.close': 'Close',
|
|
1181
1283
|
'settings.tokenTitle': 'Access token', 'settings.change': 'Change',
|
|
1182
1284
|
'settings.hostTitle': 'DSH status', 'settings.hostProbing': 'Probing…', 'settings.probe': 'Probe', 'settings.probeFailed': 'Probe failed',
|
|
1183
|
-
'settings.dshControlTitle': 'Remote DSH control', 'settings.dshControlProbing': 'Querying the gateway for the local
|
|
1285
|
+
'settings.dshControlTitle': 'Remote DSH control', 'settings.dshExperimental': 'Windows experimental', 'settings.dshControlProbing': 'Querying the gateway for the local DSH service…', 'settings.dshRunning': 'Running', 'settings.dshStopped': 'Stopped', 'settings.dshUnsupported': 'No controllable DSH service is configured', 'settings.dshStart': 'Start', 'settings.dshRestart': 'Restart', 'settings.dshStarting': '{action}ing DSH…', 'settings.dshStarted': 'DSH {action}ed', 'settings.dshFailed': 'DSH operation failed: {msg}',
|
|
1184
1286
|
'settings.dshRunningDetail': 'Running · {service} · PID {pid} · {state}', 'settings.dshStoppedDetail': 'Stopped · {service} · {state}', 'settings.dshStatusFailed': 'Service status query failed: {msg}',
|
|
1185
|
-
'settings.dshStageQueued': 'Request reached the gateway; waiting to inspect the service', 'settings.dshStageChecking': 'Checking
|
|
1287
|
+
'settings.dshStageQueued': 'Request reached the gateway; waiting to inspect the service', 'settings.dshStageChecking': 'Checking DSH service {service}', 'settings.dshStageCommand': 'Service confirmed; submitting the {action} command', 'settings.dshStageWaitingService': 'Command accepted; waiting for the service process ({seconds}s)', 'settings.dshStageWaitingUpstream': 'Service process is running; waiting for the DSH HTTP endpoint ({seconds}s)', 'settings.dshStageWaitingEvents': 'DSH HTTP is ready; waiting for the realtime message channels ({seconds}s)',
|
|
1186
1288
|
'settings.dshSuccessDetail': 'DSH {action} succeeded · PID {pid} · HTTP {status} · {seconds}s', 'settings.dshAlreadyRunning': 'DSH is already running · PID {pid}', 'settings.dshResultUnknown': 'The gateway connection was interrupted during the operation, so the final result is unknown. Reopen this page to check the service state.',
|
|
1187
|
-
'settings.dshErrorServiceNotFound': '
|
|
1289
|
+
'settings.dshErrorServiceNotFound': 'DSH service {service} was not found. Install it or configure DSH_REMOTE_DSH_SERVICE first.', 'settings.dshErrorInvalidService': 'The configured DSH service name is invalid', 'settings.dshErrorSystemctlNotFound': 'systemctl is not available on this host', 'settings.dshErrorSystemdUnavailable': 'Could not connect to the current user systemd session', 'settings.dshErrorServiceControlNotFound': 'sc.exe is not available on this host', 'settings.dshErrorServiceDisabled': 'The Windows DSH service is disabled', 'settings.dshErrorServiceStopTimeout': 'The Windows DSH service did not stop before timeout', 'settings.dshErrorStatusParseFailed': 'Could not parse the Windows DSH service status', 'settings.dshErrorPermissionDenied': 'The current user cannot control the DSH service', 'settings.dshErrorCommandTimeout': 'The service control command timed out', 'settings.dshErrorCommandFailed': 'The service control command failed', 'settings.dshErrorServiceFailed': 'The DSH service entered the failed state after startup', 'settings.dshErrorServiceTimeout': 'The DSH service did not enter the running state before timeout', 'settings.dshErrorUpstreamTimeout': 'The DSH process is running, but its HTTP endpoint did not recover before timeout', 'settings.dshErrorEventsTimeout': 'The DSH service and HTTP endpoint recovered, but the realtime message channels did not reconnect before timeout', 'settings.dshErrorOperationNotFound': 'The gateway no longer knows this operation; it may have restarted', 'settings.dshErrorOperationProgress': 'Another DSH start/restart operation is already running', 'settings.dshErrorGeneric': '{msg}', 'settings.dshErrorDetail': 'Detail: {detail}',
|
|
1188
1290
|
'settings.hostDesc': 'DSH {version} · {cwd} · {n} attached sessions',
|
|
1189
1291
|
'settings.updateTitle': 'Check for updates', 'settings.updateLoading': 'Loading…', 'settings.downloadUpdate': 'Download & install update', 'settings.check': 'Check',
|
|
1190
1292
|
'settings.feedbackTitle': 'Feedback', 'settings.feedbackDesc': 'GitHub / Gitee / Bilibili: report bugs, suggest features, or just chat',
|
|
@@ -1219,6 +1321,9 @@
|
|
|
1219
1321
|
<script src="i18n.js"></script>
|
|
1220
1322
|
<script src="theme.js"></script>
|
|
1221
1323
|
<script src="md.js"></script>
|
|
1324
|
+
<script src="vendor/gsap/gsap.min.js"></script>
|
|
1325
|
+
<script src="motion.js"></script>
|
|
1326
|
+
<script type="module" src="morphicons-init.js"></script>
|
|
1222
1327
|
<script src="app.js"></script>
|
|
1223
1328
|
</body>
|
|
1224
1329
|
</html>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Local morphicons bridge for the zero-build WebUI.
|
|
3
|
+
*
|
|
4
|
+
* The vendored element keeps a static SVG child as its first-paint fallback.
|
|
5
|
+
* UI code only changes data-morph-state; this bridge turns that state change
|
|
6
|
+
* into a morph without coupling the application code to the vendor API.
|
|
7
|
+
*/
|
|
8
|
+
import { defineMorphIcon } from './vendor/morphicons/element.js'
|
|
9
|
+
|
|
10
|
+
defineMorphIcon()
|
|
11
|
+
|
|
12
|
+
function syncMorphIcon(element) {
|
|
13
|
+
if (!element || !('icon' in element)) return
|
|
14
|
+
const state = element.dataset.morphState === 'open' ? 'open' : 'closed'
|
|
15
|
+
const next = state === 'open' ? element.dataset.morphOpen : element.dataset.morphClosed
|
|
16
|
+
if (!next || element.icon === next) return
|
|
17
|
+
element.icon = next
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function syncMorphIcons(root = document) {
|
|
21
|
+
if (root.matches?.('morph-icon[data-morph-closed][data-morph-open]')) syncMorphIcon(root)
|
|
22
|
+
root.querySelectorAll?.('morph-icon[data-morph-closed][data-morph-open]').forEach(syncMorphIcon)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function bootMorphIcons() {
|
|
26
|
+
syncMorphIcons()
|
|
27
|
+
const observer = new MutationObserver(records => {
|
|
28
|
+
for (const record of records) syncMorphIcon(record.target)
|
|
29
|
+
})
|
|
30
|
+
observer.observe(document.documentElement, {
|
|
31
|
+
subtree: true,
|
|
32
|
+
attributes: true,
|
|
33
|
+
attributeFilter: ['data-morph-state']
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (document.readyState === 'loading') {
|
|
38
|
+
document.addEventListener('DOMContentLoaded', bootMorphIcons, { once: true })
|
|
39
|
+
} else {
|
|
40
|
+
bootMorphIcons()
|
|
41
|
+
}
|