dsh-in-hand 1.7.7 → 1.7.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/README.md CHANGED
@@ -266,6 +266,21 @@ PDF / ZIP / MP4)。这是**按内容识别**,不是"按内容放行" ——
266
266
 
267
267
  ![子代理入口图标](docs/screenshots/v1.7.7-subagents-icon.png)
268
268
 
269
+ ### v1.7.8:长会话不再整屏闪、附件信封不再露标签
270
+
271
+ 图片的骨架流光用 `inset:0` 绝对定位,却忘了给 `.md-imgwrap` 定位,包含块一路退到**视口**。
272
+ 长会话里总有还没进入视口的懒加载图,它们永远带着 `.loading`,于是整屏多出一条 1.35s
273
+ 无限循环的亮带(用户原话「反复闪屏」),`translateX(55%)` 还把页面从 390 撑到 601。现在
274
+ 流光被关进图片盒、不吃点击,离屏图也不再挂任何动画:
275
+
276
+ | 改前(整屏亮带,可横向滚到 601) | 改后(390,静止) |
277
+ | --- | --- |
278
+ | ![改前](docs/screenshots/v1.7.8-flash-before.png) | ![改后](docs/screenshots/v1.7.8-flash-after.png) |
279
+
280
+ 同一轮的第二个修复:DSH 给模型看的 `<path>…</path><type>image</type><content>…</content>`
281
+ 信封,以前在手机上原样显示成 XML(某会话第一条消息整条都是它),现在渲染成图片卡,
282
+ 拿不到图时仍是那条 36px 提示条。
283
+
269
284
  ### 页面为什么不做整页重建
270
285
 
271
286
  页面每 3 秒轮询一次,但**数据没变就完全不碰 DOM**,只刷新右上角时间戳。首版是
package/lib/page.html CHANGED
@@ -1083,7 +1083,7 @@ html,body{overflow-x:clip}
1083
1083
  ② min-height:0:没拿到 intrinsic size 之前**不占位撑高**,加载完再长出来,
1084
1084
  避免"先铺一块 44px 空气、再跳一下"的布局抖动;
1085
1085
  ③ 圆角与边框在图片盒子上,加载失败换成提示条时形状/位置不变。 */
1086
- .md-imgwrap{display:block;width:100%;max-width:100%;min-height:0;padding:0;margin:var(--sp-2) 0;border:0;
1086
+ .md-imgwrap{display:block;position:relative;width:100%;max-width:100%;min-height:0;padding:0;margin:var(--sp-2) 0;border:0;
1087
1087
  background:none;text-align:left;border-radius:var(--dsw-radius-md);overflow:hidden}
1088
1088
  .md-img{display:block;width:100%;max-width:100%;height:auto;min-height:0;border:1px solid var(--line);
1089
1089
  border-radius:var(--dsw-radius-md);background:var(--panel2)}
@@ -1240,12 +1240,17 @@ html,body{overflow-x:clip}
1240
1240
  @keyframes nudge{0%,100%{transform:translateX(0)}30%{transform:translateX(-2px)}70%{transform:translateX(2px)}}
1241
1241
 
1242
1242
  /* 图片:先落一块纸纹,显影时轻轻放大一点。注意 .loading 是 JS **之后**才加的 ——
1243
- JS 不跑时 .md-img 完全可见,绝不留隐形图。 */
1243
+ JS 不跑时 .md-img 完全可见,绝不留隐形图。
1244
+ 2026-10-22(v1.7.8):.md-imgwrap 必须 position:relative —— 否则下面这条
1245
+ `inset:0` 的绝对定位流光会以**视口**为包含块:长会话里只要有一张懒加载图片
1246
+ 还没进入视口,它就一直带着 .loading,于是整屏出现一条 1.35s 无限循环的亮带
1247
+ (用户报的"反复闪屏"),还把它自己的 translateX(55%) 算进文档宽度,
1248
+ 把 390 的页面撑到 601(横向可滚)。同时流光绝不能吃点击。 */
1244
1249
  .md-img{transition:opacity var(--mo-slow) var(--mo-ease),transform var(--mo-slow) var(--mo-ease)}
1245
1250
  .md-imgwrap.loading .md-img{opacity:0;transform:scale(1.012)}
1246
1251
  .md-imgwrap.done .md-img{opacity:1;transform:none}
1247
1252
  .md-imgwrap.loading::after{
1248
- content:'';position:absolute;inset:0;
1253
+ content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;
1249
1254
  background:linear-gradient(100deg,transparent 18%,var(--mo-ink) 42%,transparent 62%);
1250
1255
  animation:sheen 1.35s var(--mo-ease) infinite;
1251
1256
  }
@@ -2436,12 +2441,54 @@ function blocksInto(el, text){
2436
2441
  return el
2437
2442
  }
2438
2443
 
2444
+ /* DSH 把「用户贴了一个附件」序列化成一段**信封文本**给模型看:
2445
+ <path>C:\…\a.png</path><type>image</type><content>image/png image, 1398x1542 px, 437517 bytes</content>
2446
+ 这段协议在手机上也原样显示过 —— 用户看到的是 XML 标签(2026-10-22 的
2447
+ 「Token Router 说明书与印刷 PDF」会话,第一条消息整条都是它)。v1.7.8 解析成
2448
+ 真正的图片 / 文件卡(同样走 imgNode 的失败兜底),文本里不再留标签。 */
2449
+ var ENVELOPE_RE = /<path>([\s\S]*?)<\/path>\s*<type>\s*(image|file)\s*<\/type>\s*<content>([\s\S]*?)<\/content>/g
2450
+ function splitAttachmentEnvelopes(text){
2451
+ var images = [], files = []
2452
+ var clean = String(text == null ? '' : text).replace(ENVELOPE_RE, function(all, p, kind, meta){
2453
+ var path = String(p || '').trim()
2454
+ if (!path) return ''
2455
+ var info = { path:path, meta:String(meta || '').replace(/\s+/g, ' ').trim() }
2456
+ if (kind === 'image') images.push(info)
2457
+ else files.push(info)
2458
+ return ''
2459
+ })
2460
+ return {
2461
+ text: clean.replace(/\n{3,}/g, '\n\n').replace(/^\s+|\s+$/g, ''),
2462
+ images: images,
2463
+ files: files
2464
+ }
2465
+ }
2466
+ function envelopeAttachmentsNode(env){
2467
+ var wrap = document.createElement('div'); wrap.className = 'message-attachments'
2468
+ var i
2469
+ for (i = 0; i < env.images.length; i++) {
2470
+ var a = env.images[i]
2471
+ var node = imgNode(fileUrlFor(a.path), baseName(a.path))
2472
+ if (a.meta) node.title = (node.title ? node.title + ' · ' : '') + a.meta
2473
+ wrap.appendChild(node)
2474
+ }
2475
+ for (i = 0; i < env.files.length; i++) {
2476
+ var f = env.files[i]
2477
+ wrap.appendChild(fileCard(fileUrlFor(f.path), baseName(f.path), '文件'))
2478
+ }
2479
+ return wrap.children.length ? wrap : null
2480
+ }
2481
+
2439
2482
  function bubNode(text, opts){
2440
2483
  var o = opts || {}
2441
2484
  var wrap = document.createElement('div'); wrap.className = 'bubwrap'
2442
2485
  var b = document.createElement('div')
2443
2486
  b.className = 'bub'
2444
2487
  var s = String(text == null ? '' : text)
2488
+ var env = splitAttachmentEnvelopes(s)
2489
+ if (env.images.length || env.files.length) s = env.text
2490
+ var attach = envelopeAttachmentsNode(env)
2491
+ if (attach) b.appendChild(attach)
2445
2492
  var parts = s.split('```')
2446
2493
  if (parts.length > 1) {
2447
2494
  for (var i = 0; i < parts.length; i++) {
@@ -5280,8 +5327,11 @@ window.fetch = function (u, o) {
5280
5327
  }
5281
5328
 
5282
5329
  /* 4c. 图片:没加载完的才标 .loading(骨架流光),加载完 .done 显影。
5283
- 注意 .loading 是这里**加上**的 —— JS 不跑时图片本来就直接可见。 */
5330
+ 注意 .loading 是这里**加上**的 —— JS 不跑时图片本来就直接可见。
5331
+ 2026-10-22(v1.7.8):只给**贴着视口**的图挂流光。长会话里几十张懒加载图
5332
+ 永远在视口外,以前它们会一直挂着 .loading,流光动画也就永远在跑(闪屏)。 */
5284
5333
  var wraps = doc.querySelectorAll('.md-imgwrap')
5334
+ var vh = window.innerHeight || 800
5285
5335
  for (var j = 0; j < wraps.length; j++) {
5286
5336
  var w = wraps[j]
5287
5337
  if (w.classList.contains('done')) continue
@@ -5291,7 +5341,10 @@ window.fetch = function (u, o) {
5291
5341
  w.classList.remove('loading')
5292
5342
  w.classList.add('done')
5293
5343
  } else {
5294
- w.classList.add('loading')
5344
+ var wr = w.getBoundingClientRect()
5345
+ var near = wr.bottom > -vh && wr.top < vh * 2
5346
+ if (near) w.classList.add('loading')
5347
+ else w.classList.remove('loading')
5295
5348
  }
5296
5349
  }
5297
5350
 
package/lib/sw.js CHANGED
@@ -46,7 +46,8 @@ const SHELL = ['/taskwatch', '/taskwatch/icon-192.png', '/taskwatch/icon-512.png
46
46
  // 又足以把整站打死。别再加回来。
47
47
  // v17(v1.7.7):页面换了正文图片的兜底渲染(坏图 → 44px 以内提示条)与抽屉图标
48
48
  // (字符字形 → 内联 SVG)。外壳里的 icon-192 也是预缓存的,一起升号清掉旧壳。
49
- const CACHE = 'taskwatch-v17'
49
+ // v18(v1.7.8):图片流光的包含块修好(长会话不再整屏闪)+ 附件信封改成图片卡。
50
+ const CACHE = 'taskwatch-v18'
50
51
 
51
52
  const JSON_HEADERS = { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' }
52
53
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-in-hand",
3
- "version": "1.7.7",
3
+ "version": "1.7.8",
4
4
  "description": "掌上 DSH:DeepSeek Harness 的手机端,不用装 App——会话、实时对话、交付文件、后台任务与待批准。 / DSH in hand: the phone client for DeepSeek Harness with no app to install.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",