dsh-remote-plugin 0.6.13 → 0.6.14

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/md.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * 用法: mdToHtml(text) -> HTML 字符串
3
3
  * 安全: 先 HTML 转义再转标记; 链接仅允许 http/https, 其余保留为纯文本。
4
4
  * 支持: 代码块 / 行内代码 / #~### 标题 / **粗体** / *斜体* / - 无序列表 /
5
- * 1. 有序列表 / > 引用 / [text](url) 链接 / 换行。
5
+ * 1. 有序列表 / > 引用 / GFM 表格 / [text](url) 链接 / 换行。
6
6
  * 同时支持浏览器全局 window.mdToHtml 与 Node CommonJS module.exports。
7
7
  */
8
8
  (function (root, factory) {
@@ -35,6 +35,70 @@
35
35
  return s
36
36
  }
37
37
 
38
+ function tableCells(line) {
39
+ var s = String(line || '').trim()
40
+ if (s.charAt(0) === '|') s = s.slice(1)
41
+ if (s.charAt(s.length - 1) === '|') s = s.slice(0, -1)
42
+ var cells = []
43
+ var cell = ''
44
+ var escaped = false
45
+ for (var i = 0; i < s.length; i++) {
46
+ var c = s.charAt(i)
47
+ if (escaped) { cell += c; escaped = false; continue }
48
+ if (c === '\\') { escaped = true; continue }
49
+ if (c === '|') { cells.push(cell.trim()); cell = ''; continue }
50
+ cell += c
51
+ }
52
+ if (escaped) cell += '\\'
53
+ cells.push(cell.trim())
54
+ return cells
55
+ }
56
+
57
+ function tableAlignment(cell) {
58
+ var s = String(cell || '').trim()
59
+ if (!/^:?-{3,}:?$/.test(s)) return null
60
+ if (s.charAt(0) === ':' && s.charAt(s.length - 1) === ':') return 'center'
61
+ if (s.charAt(0) === ':') return 'left'
62
+ if (s.charAt(s.length - 1) === ':') return 'right'
63
+ return null
64
+ }
65
+
66
+ function tableAt(lines, start) {
67
+ if (start + 1 >= lines.length || !/\|/.test(lines[start]) || !/\|/.test(lines[start + 1])) return null
68
+ var headers = tableCells(lines[start])
69
+ var separators = tableCells(lines[start + 1])
70
+ if (headers.length < 2 || separators.length !== headers.length) return null
71
+ if (!separators.every(function (cell) { return /^:?-{3,}:?$/.test(String(cell || '').trim()) })) return null
72
+ var alignments = separators.map(tableAlignment)
73
+ var rows = []
74
+ var i = start + 2
75
+ while (i < lines.length && lines[i].trim() && /\|/.test(lines[i])) {
76
+ var cells = tableCells(lines[i])
77
+ if (cells.length !== headers.length) break
78
+ rows.push(cells)
79
+ i++
80
+ }
81
+ return { headers: headers, alignments: alignments, rows: rows, next: i }
82
+ }
83
+
84
+ function tableHtml(table) {
85
+ function cellTag(tag, value, align) {
86
+ var style = align ? ' style="text-align:' + align + '"' : ''
87
+ return '<' + tag + style + '>' + inline(value) + '</' + tag + '>'
88
+ }
89
+ var head = '<thead><tr>' + table.headers.map(function (value, i) {
90
+ return cellTag('th', value, table.alignments[i])
91
+ }).join('') + '</tr></thead>'
92
+ var body = table.rows.length
93
+ ? '<tbody>' + table.rows.map(function (row) {
94
+ return '<tr>' + row.map(function (value, i) {
95
+ return cellTag('td', value, table.alignments[i])
96
+ }).join('') + '</tr>'
97
+ }).join('') + '</tbody>'
98
+ : ''
99
+ return '<div class="md-table-wrap"><table>' + head + body + '</table></div>'
100
+ }
101
+
38
102
  function renderLines(lines) {
39
103
  var html = ''
40
104
  var i = 0
@@ -49,6 +113,12 @@
49
113
  i++
50
114
  continue
51
115
  }
116
+ var table = tableAt(lines, i)
117
+ if (table) {
118
+ html += tableHtml(table)
119
+ i = table.next
120
+ continue
121
+ }
52
122
  var q = line.match(/^&gt;\s?(.*)$/)
53
123
  if (q) {
54
124
  html += '<blockquote>' + inline(q[1]) + '</blockquote>'
package/public/styles.css CHANGED
@@ -329,6 +329,23 @@ body.in-session .view {
329
329
  .card-row { display: flex; justify-content: space-between; gap: 10px; padding: 3px 0; font-size: 13px; }
330
330
  .card-row .k { color: var(--dsr-muted); }
331
331
  .card-row .v { text-align: right; word-break: break-all; }
332
+ .subagent-card { padding: 0; overflow: hidden; }
333
+ .subagent-toggle { width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 12px; border: 0; background: transparent; color: inherit; text-align: left; cursor: pointer; }
334
+ .subagent-toggle .card-title { margin: 0; }
335
+ .subagent-toggle-icon { color: var(--dsr-muted); font-size: 16px; line-height: 1; }
336
+ .subagent-list { padding: 0 12px 10px; border-top: 1px solid var(--dsr-line); }
337
+ .queue-dock { margin: 0 0 10px; border: 1px solid var(--dsr-line); border-radius: 12px; background: var(--dsr-surface); overflow: hidden; }
338
+ .queue-dock-head { display: flex; align-items: center; gap: 8px; padding: 9px 12px; color: var(--dsr-muted); font-size: 12px; font-weight: 700; border-bottom: 1px solid var(--dsr-line); }
339
+ .queue-dock-list { max-height: 220px; overflow-y: auto; }
340
+ .queue-dock-item { display: flex; align-items: center; gap: 8px; padding: 8px 10px 8px 12px; border-bottom: 1px solid var(--dsr-line); }
341
+ .queue-dock-item:last-child { border-bottom: 0; }
342
+ .queue-dock-preview { flex: 1; min-width: 0; color: var(--dsr-text); font-size: 13px; line-height: 1.4; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
343
+ .queue-dock-action { flex: 0 0 auto; }
344
+ .md-table-wrap { max-width: 100%; overflow-x: auto; margin: 8px 0; }
345
+ .md-table-wrap table { width: max-content; min-width: 100%; border-collapse: collapse; font-size: .94em; }
346
+ .md-table-wrap th, .md-table-wrap td { padding: 6px 9px; border: 1px solid var(--dsr-line); text-align: left; white-space: nowrap; }
347
+ .md-table-wrap th { background: var(--dsr-bg-2); font-weight: 700; }
348
+ .md-table-wrap tbody tr:nth-child(even) { background: color-mix(in srgb, var(--dsr-bg-2) 45%, transparent); }
332
349
  .goal-obj { font-size: 13px; line-height: 1.55; color: var(--dsr-text); }
333
350
  .goal-phase { font-size: 12px; color: var(--dsr-info); margin-top: 3px; }
334
351
  .goal-actions { display: flex; gap: 8px; margin-top: 9px; flex-wrap: wrap; }
@@ -498,6 +515,16 @@ body.composer-fullscreen { overflow: hidden; }
498
515
  background: var(--dsr-accent); color: var(--dsr-on-accent); font-weight: 700; font-size: 14px;
499
516
  }
500
517
  .send-btn:disabled { opacity: .45; }
518
+ .composer-status { display: flex; align-items: center; gap: 7px; padding: 0 3px; color: var(--dsr-accent-strong); font-size: 12px; font-weight: 700; }
519
+ .composer-status-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; box-shadow: 0 0 0 0 currentColor; animation: dsr-running-pulse 1.3s ease-out infinite; }
520
+ @keyframes dsr-running-pulse { 0% { box-shadow: 0 0 0 0 currentColor; opacity: 1; } 70% { box-shadow: 0 0 0 6px transparent; opacity: .65; } 100% { box-shadow: 0 0 0 0 transparent; opacity: 1; } }
521
+ .scan-live-card { width: min(92vw, 420px); }
522
+ .scan-live-preview { position: relative; width: 100%; aspect-ratio: 1 / 1; overflow: hidden; border-radius: 14px; background: #05070d; }
523
+ .scan-live-preview video { display: block; width: 100%; height: 100%; object-fit: cover; }
524
+ .scan-live-frame { position: absolute; inset: 18%; border: 2px solid rgba(255,255,255,.9); border-radius: 18px; box-shadow: 0 0 0 999px rgba(0,0,0,.28); pointer-events: none; }
525
+ .scan-live-frame::after { content: ''; position: absolute; left: 8%; right: 8%; top: 8%; height: 2px; background: var(--dsr-accent-strong); box-shadow: 0 0 12px var(--dsr-accent-strong); animation: scan-live-line 1.8s ease-in-out infinite; }
526
+ @keyframes scan-live-line { 0%, 100% { transform: translateY(0); opacity: .55; } 50% { transform: translateY(220px); opacity: 1; } }
527
+ .scan-live-status { padding: 10px 2px 0; color: var(--dsr-muted); font-size: 13px; text-align: center; }
501
528
  .composer-menu {
502
529
  max-height: min(46vh, 340px); overflow-y: auto;
503
530
  padding: 10px 12px; display: flex; flex-direction: column; gap: 12px;
@@ -0,0 +1,74 @@
1
+ /* DSH Remote Prompt 转写核心模块(dev 分支实验功能)
2
+ * 纯逻辑、零依赖:密钥掩码、固定整理提示词、状态码文案和 SSE 增量解析。
3
+ * 浏览器通过 window.TranscribeCore 使用,Node 测试通过 CommonJS 使用。
4
+ * 当前仅在 dev 分支保留,不接入稳定版主界面。
5
+ */
6
+ (function (root, factory) {
7
+ if (typeof module === 'object' && module.exports) module.exports = factory()
8
+ else root.TranscribeCore = factory()
9
+ })(typeof self !== 'undefined' ? self : this, function () {
10
+ 'use strict'
11
+
12
+ const TRANSCRIBE_SYSTEM_PROMPT = [
13
+ '你是文本整理助手。请把用户发来的原始文字改写成一条可直接使用的提示词,要求:',
14
+ '1. 分条分点:把内容按要点拆成编号列表,层次清晰;',
15
+ '2. 逻辑清晰:按「目标—背景—要求—输出」的顺序整理,删除冗余重复;',
16
+ '3. 修正语句与错别字:修正病句、错别字、标点与大小写问题;',
17
+ '4. 删除无意义口语语气词:去掉「那个、就是说、嗯、啊、然后」等口头禅;',
18
+ '5. 保留原意:不增删核心信息,不擅自补充额外要求;',
19
+ '6. 直接输出改写结果,不解释、不客套。'
20
+ ].join('\n')
21
+
22
+ function maskApiKey(key) {
23
+ const value = String(key || '')
24
+ if (!value) return ''
25
+ if (value.length <= 8) return '****' + value.slice(-4)
26
+ return value.slice(0, 4) + '****' + value.slice(-4)
27
+ }
28
+
29
+ function statusMessage(status) {
30
+ if (status === 400) return '请求参数错误:请检查 API 地址、模型名或输入内容(400)'
31
+ if (status === 401 || status === 403) return '认证失败:API 密钥无效或无权限(' + status + ')'
32
+ if (status === 404) return '接口不存在:请检查 API 地址是否以 /v1 结尾(404)'
33
+ if (status === 429) return '请求过于频繁或额度不足(429)'
34
+ if (status >= 500) return '服务端错误(' + status + ')'
35
+ return '请求失败(HTTP ' + status + ')'
36
+ }
37
+
38
+ function parseSseData(line) {
39
+ const data = String(line || '').trim().replace(/^data:\s*/, '').trim()
40
+ if (data === '[DONE]') return { type: 'done' }
41
+ let value
42
+ try { value = JSON.parse(data) } catch { return { type: 'skip' } }
43
+ if (value.error) return { type: 'error', error: String(value.error.message || statusMessage(Number(value.error.code) || 0)) }
44
+ const piece = value.choices?.[0]?.delta?.content
45
+ return typeof piece === 'string' ? { type: 'delta', text: piece } : { type: 'skip' }
46
+ }
47
+
48
+ async function consumeSse(reader, decoder, onDelta, options = {}) {
49
+ let buffer = ''
50
+ let full = ''
51
+ while (true) {
52
+ const { done, value } = await reader.read()
53
+ if (done) break
54
+ buffer += decoder.decode(value, { stream: true })
55
+ options.onChunk?.()
56
+ let nl
57
+ while ((nl = buffer.indexOf('\n')) !== -1) {
58
+ const line = buffer.slice(0, nl).trim()
59
+ buffer = buffer.slice(nl + 1)
60
+ if (!line.startsWith('data:')) continue
61
+ const parsed = parseSseData(line)
62
+ if (parsed.type === 'done') { await reader.cancel?.(); return full }
63
+ if (parsed.type === 'error') throw new Error(parsed.error)
64
+ if (parsed.type === 'delta') {
65
+ full += parsed.text
66
+ onDelta?.(parsed.text)
67
+ }
68
+ }
69
+ }
70
+ return full
71
+ }
72
+
73
+ return { TRANSCRIBE_SYSTEM_PROMPT, maskApiKey, statusMessage, parseSseData, consumeSse }
74
+ })
@@ -1,10 +1,14 @@
1
1
  {
2
- "version": "0.6.13",
2
+ "version": "0.6.14",
3
3
  "apkUrl": "dsh-remote.apk",
4
- "sha256": "6cee7bfb1425fb26bcfbcd5c5f932b8ff26ca9d5026ac199b5393a00d0c343b1",
5
- "releasedAt": "2026-08-24T05:49:23.534Z",
6
- "notes": "0.6.13 正式版:新增首次连接 Doctor,集中检查 DSH 服务、远程网关、局域网地址、防火墙、终端配对和实时消息通道;网关控制台新增可选的独立设备密钥,支持设备备注、最近 IP、二维码、令牌轮换、复制和退出,共享令牌在启用后仅保留管理权限;手机端和桌面端支持实时思考内容,并为未声明推理档位的模型提供 low、high、max 三档选择;普通会话列表、工作区树和主页统计不再混入子代理内部会话;Android App 落后于网关版本时显示明确更新提醒;新增网关协议与能力协商并兼容旧网关,同时补充设备隔离、重启持久化、推理显示、会话过滤和版本差异回归测试。",
4
+ "sha256": "4adbea85b1cf3f1768609a14c70adf3876033d892f7fab3dfa0d7f40959e643c",
5
+ "releasedAt": "2026-08-25T06:48:09.119Z",
6
+ "notes": "0.6.14-rc.7:将 Android 后台轮询服务接入与前台 App 相同的 client ID,修复 Dalvik 后台请求被显示为第二台未知设备;保留旧记录的顺序无关迁移;补齐文件上传请求的持久设备 ID;实时扫码继续使用 Worker 解码并降低取样负载;修复非用户来源的 DSH user/message 被误显示为用户输入;新增子代理折叠、排队消息插话、运行中提示和按轮次稳定排序;Markdown 支持 GFM 表格。",
7
7
  "history": [
8
+ {
9
+ "version": "0.6.14",
10
+ "notes": "0.6.14-rc.7:将 Android 后台轮询服务接入与前台 App 相同的 client ID,修复 Dalvik 后台请求被显示为第二台未知设备;保留旧记录的顺序无关迁移;补齐文件上传请求的持久设备 ID;实时扫码继续使用 Worker 解码并降低取样负载;修复非用户来源的 DSH user/message 被误显示为用户输入;新增子代理折叠、排队消息插话、运行中提示和按轮次稳定排序;Markdown 支持 GFM 表格。"
11
+ },
8
12
  {
9
13
  "version": "0.6.13",
10
14
  "notes": "0.6.13 正式版:新增首次连接 Doctor,集中检查 DSH 服务、远程网关、局域网地址、防火墙、终端配对和实时消息通道;网关控制台新增可选的独立设备密钥,支持设备备注、最近 IP、二维码、令牌轮换、复制和退出,共享令牌在启用后仅保留管理权限;手机端和桌面端支持实时思考内容,并为未声明推理档位的模型提供 low、high、max 三档选择;普通会话列表、工作区树和主页统计不再混入子代理内部会话;Android App 落后于网关版本时显示明确更新提醒;新增网关协议与能力协商并兼容旧网关,同时补充设备隔离、重启持久化、推理显示、会话过滤和版本差异回归测试。"
@@ -40,10 +44,6 @@
40
44
  {
41
45
  "version": "0.6.5",
42
46
  "notes": "连接稳定性全面优化:25 秒心跳保活(WiFi 切换 / NAT 超时不再假活);60 秒无消息自动重连,指数退避重连(1s → 30s 上限 + 抖动);网关死连接自动清理(60 秒无数据双向销毁);断网自动检测与恢复(离线 / 重连中倒计时 / 连接失败状态可见);降级轮询恢复优化。"
43
- },
44
- {
45
- "version": "0.6.4",
46
- "notes": "扫码配对改为显式双按钮(拍照/相册),修复小米等 ROM 选择器错乱;会话历史加载超时放宽至 45 秒,失败可重试;网关端口自定义(插件管理页可改,环境变量仍优先)+ 启动前端口占用检测;峰谷计费提醒改前台服务驱动,后台按时必达;消息 Markdown 渲染(标题/代码块/列表/链接);修复 DSH 重启后网关上游端口不刷新(Issue #1);新增测试通知按钮。"
47
47
  }
48
48
  ]
49
49
  }
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "0.6.13"
2
+ "version": "0.6.14"
3
3
  }