@lyhue1991/dsh-soup 0.4.8 → 0.4.10

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.
Files changed (2) hide show
  1. package/lib/client.js +317 -42
  2. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -61,13 +61,21 @@ window.__ModuleLoader__.load({
61
61
  '.expl-notice{color:var(--dsw-alias-state-success-primary);padding:6px 14px;font-size:12px;border-bottom:1px solid var(--dsw-alias-border-l1);flex:none;}' +
62
62
  '.expl-bulk{color:var(--dsw-alias-state-business-primary);padding:6px 14px;font-size:12px;border-bottom:1px solid var(--dsw-alias-border-l1);flex:none;}' +
63
63
  '.expl-body{overflow:auto;flex:1;padding:6px 0 12px;}' +
64
- '.expl-row-main{display:flex;align-items:center;gap:6px;padding-top:2px;padding-bottom:2px;padding-right:10px;cursor:pointer;border-radius:8px;margin:0 4px;height:40px;box-sizing:border-box;user-select:none;}' +
64
+ '.expl-row-main{display:flex;align-items:center;gap:4px;padding-top:2px;padding-bottom:2px;padding-right:10px;cursor:pointer;border-radius:8px;margin:0 4px;height:40px;box-sizing:border-box;user-select:none;}' +
65
+ '.expl-row-main{position:relative;}' +
65
66
  '.expl-row-main:hover{background:var(--dsw-alias-interactive-bg-hover);}' +
66
67
  '.expl-row-main.selected{background:var(--dsw-alias-interactive-bg-hover-accent);}' +
67
68
  '.expl-row-main.drop-target,.expl-body.drop-target{outline:1px dashed var(--dsw-alias-state-business-primary);outline-offset:-2px;background:var(--dsw-alias-interactive-bg-hover-accent);}' +
68
69
  '.expl-caret{flex:none;cursor:pointer;line-height:1;}' +
69
- '.expl-caret-big{width:24px;font-size:33px;color:var(--dsw-alias-label-secondary);display:flex;align-items:center;justify-content:flex-start;}' +
70
- '.expl-caret-sm{width:24px;font-size:33px;color:var(--dsw-alias-label-tertiary);display:flex;align-items:center;justify-content:flex-start;}' +
70
+ /* Keep a compact reserved disclosure slot for nested rows; folder icons
71
+ remain visible while expansion is driven by row/double-click actions. */
72
+ '.expl-caret-big{width:2px;height:16px;font-size:0;color:transparent;display:flex;align-items:center;justify-content:center;}' +
73
+ '.expl-caret-big::before{content:"";display:block;width:0;height:0;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:6px solid var(--dsw-alias-label-secondary);transition:transform 150ms var(--ds-ease-in-out);}' +
74
+ '.expl-caret-big.open::before{transform:rotate(90deg);}' +
75
+ '.expl-caret-sm{width:2px;height:16px;font-size:0;color:transparent;display:flex;align-items:center;justify-content:center;pointer-events:none;}' +
76
+ '.expl-row-main .expl-caret-big{display:flex;visibility:hidden;}' +
77
+ '.expl-caret-root{position:absolute;left:8px;top:50%;transform:translateY(-50%);z-index:1;}' +
78
+ '.expl-caret-root.open::before{transform:rotate(90deg);}' +
71
79
  '.expl-icon{flex:none;display:flex;align-items:center;}.expl-icon svg{width:16px;height:16px;display:block;}' +
72
80
  '.expl-uploads{flex:none;border-bottom:1px solid var(--dsw-alias-border-l1);padding:5px 14px;display:flex;flex-direction:column;gap:3px;background:var(--dsw-specific-tip);}' +
73
81
  '.expl-upload-row{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--dsw-alias-label-secondary);}' +
@@ -75,6 +83,10 @@ window.__ModuleLoader__.load({
75
83
  '.expl-upload-pct{margin-left:auto;font-family:var(--ds-font-family-code);color:var(--dsw-alias-label-caption);}' +
76
84
  '.expl-upload-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}' +
77
85
  '.expl-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary);}' +
86
+ '.expl-row-more{display:none;flex:none;width:28px;height:28px;align-items:center;justify-content:center;border:0;border-radius:6px;padding:0;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;}' +
87
+ '.expl-row-main:hover .expl-row-more,.expl-row-main.selected .expl-row-more{display:inline-flex;}' +
88
+ '.expl-row-more:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);}' +
89
+ '.expl-row-more svg{width:16px;height:16px;display:block;}' +
78
90
  '.expl-size{color:var(--dsw-alias-label-tertiary);font-size:11px;flex:none;margin-left:8px;}' +
79
91
  '.expl-muted{color:var(--dsw-alias-label-tertiary);padding:4px 14px;font-size:12px;}' +
80
92
  '.expl-menu-mask{position:fixed;inset:0;z-index:1980;}' +
@@ -106,6 +118,7 @@ window.__ModuleLoader__.load({
106
118
  // 同名 React 元素常量冲突(var 提升覆盖),字符样式反而更稳。
107
119
  // ------------------------------------------------------------------
108
120
  var ICON_REFRESH = '<svg xmlns="http://www.w3.org/2000/svg" width="16" viewBox="0 0 18 18"><path fill="currentColor" d="M9 13.5c-2.49 0-4.5-2.01-4.5-4.5S6.51 4.5 9 4.5c1.24 0 2.36.52 3.17 1.33L10 8h5V3l-1.76 1.76A6 6 0 0 0 9 3C5.69 3 3.01 5.69 3.01 9S5.69 15 9 15a5.98 5.98 0 0 0 5.9-5h-1.52c-.46 2-2.24 3.5-4.38 3.5"/></svg>'
121
+ var ICON_MORE = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><circle cx="3" cy="8" r="1.3" fill="currentColor"/><circle cx="8" cy="8" r="1.3" fill="currentColor"/><circle cx="13" cy="8" r="1.3" fill="currentColor"/></svg>'
109
122
  var EXPAND_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/></svg>'
110
123
  var SHRINK_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"/></svg>'
111
124
 
@@ -201,9 +214,11 @@ window.__ModuleLoader__.load({
201
214
  '.dfv-overlay-close{flex:none;display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border:none;border-radius:6px;background:transparent;color:var(--dsw-alias-label-secondary);font-size:14px;line-height:1;cursor:pointer;padding:0;}' +
202
215
  '.dfv-overlay-close:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);}' +
203
216
  // 纯阅读模式:预览 tab 激活时隐藏整个输入区(composerSeat:输入框 +
204
- // GoalBar + 速度徽标所在的 input.dock)与轮次统计行(StatsLine)。
205
- // class 来自 DSH 客户端编译产物(CSS module hash),升级后若失效仅
206
- // 表现为「预览页仍显示输入区」,不影响其它功能——属可接受的降级。
217
+ // GoalBar + 速度徽标所在的 input.dock)与轮次统计行(StatsLine,新版
218
+ // DSH 已把它移进 composerSeat 内部的 conversation.composer.dock)。
219
+ // DSH 0.1.2-alpha.1 起 composerSeat 带 data-composer-seat 稳定属性,
220
+ // 优先用稳定选择器;旧 hash 规则保留以兼容更老的宿主版本。
221
+ '[data-dsh-soup-preview="1"] [data-composer-seat]{display:none !important;}' +
207
222
  '[data-dsh-soup-preview="1"] .wSkVaW_composerSeat{display:none !important;}' +
208
223
  '[data-dsh-soup-preview="1"] .FJxK0a_root{display:none !important;}'
209
224
  if (typeof document !== 'undefined' && document.querySelector('style[data-plugin-css="@lyhue1991/dsh-soup/fileview.css"]') === null) {
@@ -258,6 +273,7 @@ window.__ModuleLoader__.load({
258
273
  'explorer.hostUnreachable': '宿主不可达: {reason}',
259
274
  'explorer.hostBadResponse': '宿主响应解析失败',
260
275
  'menu.preview': '👁 预览',
276
+ 'menu.actions': '更多操作',
261
277
  'menu.open': '🖥 用系统打开',
262
278
  'menu.rename': '✎ 重命名',
263
279
  'menu.trash': '🗑 移到废纸篓',
@@ -321,6 +337,7 @@ window.__ModuleLoader__.load({
321
337
  'explorer.hostUnreachable': 'Host unreachable: {reason}',
322
338
  'explorer.hostBadResponse': 'Failed to parse host response',
323
339
  'menu.preview': '👁 Preview',
340
+ 'menu.actions': 'More actions',
324
341
  'menu.open': '🖥 Open with system',
325
342
  'menu.rename': '✎ Rename',
326
343
  'menu.trash': '🗑 Move to trash',
@@ -371,6 +388,35 @@ window.__ModuleLoader__.load({
371
388
  uploads: [],
372
389
  }
373
390
  var listeners = new Set()
391
+ // trackSession() is called by both the hero toggle and the details panel
392
+ // while a blank conversation is mounted. Keep late async responses from an
393
+ // older caller from overwriting the current session's shared store.
394
+ var sessionTrackGeneration = 0
395
+
396
+ // 目录展开状态按 cwd 隔离。刷新/删除/改名/移动后先重拉根目录,
397
+ // 再按这些路径递归重拉仍存在的已展开目录;切换项目时才清空。
398
+ var expandedDirs = new Set()
399
+ var expandedDirsCwd = null
400
+
401
+ function forgetExpandedPaths(paths) {
402
+ var dropped = paths.map(function (p) { return [p, p + '/'] })
403
+ expandedDirs = new Set(Array.from(expandedDirs).filter(function (p) {
404
+ return !dropped.some(function (pair) { return p === pair[0] || p.startsWith(pair[1]) })
405
+ }))
406
+ }
407
+
408
+ function rekeyExpandedPaths(from, to) {
409
+ var moved = Array.from(expandedDirs).filter(function (p) {
410
+ return p === from || p.startsWith(from + '/')
411
+ }).sort(function (a, b) { return a.length - b.length })
412
+ if (!moved.length) return
413
+ var next = new Set(Array.from(expandedDirs).filter(function (p) {
414
+ return p !== from && !p.startsWith(from + '/')
415
+ }))
416
+ moved.forEach(function (p) { next.add(to + p.slice(from.length)) })
417
+ expandedDirs = next
418
+ }
419
+
374
420
  function setState(patch) {
375
421
  state = Object.assign({}, state, patch)
376
422
  listeners.forEach(function (fn) { fn() })
@@ -459,20 +505,47 @@ window.__ModuleLoader__.load({
459
505
  }
460
506
 
461
507
  async function trackSession(sessionId, cwdHint) {
508
+ var generation = ++sessionTrackGeneration
462
509
  activeSessionId = sessionId || null
463
510
  var cwd = await defaultCwd(sessionId, cwdHint)
511
+ if (generation !== sessionTrackGeneration) return
512
+ // 换项目时展开状态没有跨项目意义;回到同一项目则保留。
513
+ if (expandedDirsCwd !== cwd) {
514
+ expandedDirs.clear()
515
+ expandedDirsCwd = cwd
516
+ }
464
517
  // 会话切换:预览列表按新 cwd 裁剪(范围外自动关闭),并清掉上一路径的旧错误横幅
465
518
  var files = pruneFilesToScope(state.files, cwd)
466
519
  lastMtimes = {} // 换了目录,旧 mtime 基线全部作废(首轮 tick 重建基线)
467
520
  setState({ cwd: cwd, selected: new Set(), renaming: null, newItem: null, menu: null, error: '', notice: '', files: files })
468
521
  var items = await loadDir(cwd)
522
+ if (generation !== sessionTrackGeneration) return
469
523
  if (items) setState({ error: '', tree: items })
470
524
  }
471
525
 
472
526
  async function refresh() {
473
527
  if (!state.cwd) return
474
528
  var items = await loadDir(state.cwd)
475
- if (items) setState({ error: '', tree: items })
529
+ if (!items) return
530
+ await restoreExpandedChildren(items)
531
+ setState({ error: '', tree: items })
532
+ }
533
+
534
+ /** 根目录刷新后,按记住的路径递归恢复已展开目录。 */
535
+ async function restoreExpandedChildren(nodes) {
536
+ for (var i = 0; i < nodes.length; i++) {
537
+ var node = nodes[i]
538
+ if (node.type !== 'directory' || !expandedDirs.has(node.path)) continue
539
+ var items = await loadDir(node.path)
540
+ if (!items) {
541
+ expandedDirs.delete(node.path)
542
+ continue
543
+ }
544
+ node.open = true
545
+ node.children = items
546
+ items.forEach(function (child) { child.parent = node })
547
+ await restoreExpandedChildren(items)
548
+ }
476
549
  }
477
550
 
478
551
  // ------------------------------------------------------------------
@@ -600,23 +673,7 @@ window.__ModuleLoader__.load({
600
673
  /** 手动刷新:重拉根目录,已展开的子目录逐个重拉、保持展开不收起。 */
601
674
  async function refreshAll() {
602
675
  if (!state.cwd) return
603
- var expanded = []
604
- var walk = function (nodes) {
605
- for (var i = 0; i < nodes.length; i++) {
606
- var n = nodes[i]
607
- if (n.type === 'directory' && n.children != null) {
608
- if (n.path !== state.cwd) expanded.push({ path: n.path, open: !!n.open })
609
- if (n.open && n.children.length) walk(n.children)
610
- }
611
- }
612
- }
613
- walk(state.tree)
614
676
  await refresh()
615
- for (var j = 0; j < expanded.length; j++) {
616
- if (!expanded[j].open) continue
617
- var node = findNode(state.tree, expanded[j].path)
618
- if (node) await reloadDirNode(node)
619
- }
620
677
  }
621
678
 
622
679
  async function toggleNode(node) {
@@ -628,10 +685,16 @@ window.__ModuleLoader__.load({
628
685
  node.loading = false
629
686
  if (items) items.forEach(function (c) { c.parent = node })
630
687
  node.children = items || []
631
- node.open = true
688
+ node.open = !!items
689
+ if (items) expandedDirs.add(node.path)
690
+ // 拖拽可能把一个已展开目录移进此前收起的父目录。父目录首次展开时,
691
+ // 也要立即恢复其中被记住的子目录展开状态。
692
+ if (items) await restoreExpandedChildren(items)
632
693
  setState({ tree: state.tree })
633
694
  } else {
634
695
  node.open = !node.open
696
+ if (node.open) expandedDirs.add(node.path)
697
+ else expandedDirs.delete(node.path)
635
698
  setState({ tree: state.tree })
636
699
  }
637
700
  }
@@ -896,6 +959,16 @@ window.__ModuleLoader__.load({
896
959
  setState({ selected: new Set(), lastIndex: null })
897
960
  }
898
961
 
962
+ function onRowMore(e, node) {
963
+ e.preventDefault()
964
+ e.stopPropagation()
965
+ var sel = new Set(state.selected)
966
+ sel.clear()
967
+ sel.add(node.path)
968
+ var rect = e.currentTarget.getBoundingClientRect()
969
+ setState({ selected: sel, lastIndex: null, menu: { x: rect.right - 4, y: rect.bottom + 4, node: node } })
970
+ }
971
+
899
972
  async function openSelection() {
900
973
  var paths = Array.from(state.selected)
901
974
  setState({ menu: null })
@@ -906,11 +979,14 @@ window.__ModuleLoader__.load({
906
979
  var paths = Array.from(state.selected)
907
980
  setState({ menu: null })
908
981
  var errors = []
982
+ var trashed = []
909
983
  for (var i = 0; i < paths.length; i++) {
910
984
  var res = await rpc('trash', { path: paths[i] })
911
- if (!res || !res.ok) errors.push(baseName(paths[i]) + ': ' + ((res && res.error) || T('explorer.actionFail')) + ((res && res.hint) ? '(' + res.hint + ')' : ''))
985
+ if (res && res.ok) trashed.push(paths[i])
986
+ else errors.push(baseName(paths[i]) + ': ' + ((res && res.error) || T('explorer.actionFail')) + ((res && res.hint) ? '(' + res.hint + ')' : ''))
912
987
  }
913
988
  if (errors.length) setState({ error: errors.join(';') })
989
+ forgetExpandedPaths(trashed)
914
990
  setState({ selected: new Set(), lastIndex: null })
915
991
  await refresh()
916
992
  }
@@ -924,12 +1000,18 @@ window.__ModuleLoader__.load({
924
1000
  }
925
1001
  var dir = parentOf(path)
926
1002
  var dest = pathJoin(dir, name)
1003
+ var node = findNode(state.tree, path)
1004
+ var isDir = node && node.type === 'directory'
927
1005
  var sel = new Set(state.selected)
928
1006
  if (sel.has(path)) { sel.delete(path); sel.add(dest) }
929
1007
  setState({ renaming: null, selected: sel })
930
1008
  ;(async function () {
931
1009
  var res = await rpc('move', { from: path, to: dest })
932
- if (!res || !res.ok) setState({ error: (res && res.error) || T('explorer.renameFail') })
1010
+ if (res && res.ok) {
1011
+ if (isDir) rekeyExpandedPaths(path, dest)
1012
+ } else {
1013
+ setState({ error: (res && res.error) || T('explorer.renameFail') })
1014
+ }
933
1015
  await refresh()
934
1016
  })()
935
1017
  }
@@ -1006,6 +1088,7 @@ window.__ModuleLoader__.load({
1006
1088
 
1007
1089
  async function movePaths(paths, targetDir) {
1008
1090
  var errors = []
1091
+ var moved = []
1009
1092
  for (var i = 0; i < paths.length; i++) {
1010
1093
  var p = paths[i]
1011
1094
  if (p === '/' || !p) continue
@@ -1013,10 +1096,18 @@ window.__ModuleLoader__.load({
1013
1096
  var dest = pathJoin(targetDir, name)
1014
1097
  if (dest === p) continue
1015
1098
  if (targetDir === p || targetDir.startsWith(p + '/')) { errors.push(T('explorer.moveIntoSelf', { path: name })); continue }
1099
+ var node = findNode(state.tree, p)
1016
1100
  var res = await rpc('move', { from: p, to: dest })
1017
- if (!res || !res.ok) errors.push(name + ': ' + ((res && res.error) || T('explorer.actionFail')))
1101
+ if (res && res.ok) {
1102
+ moved.push({ from: p, to: dest, isDir: node && node.type === 'directory' })
1103
+ } else {
1104
+ errors.push(name + ': ' + ((res && res.error) || T('explorer.actionFail')))
1105
+ }
1018
1106
  }
1019
1107
  setState({ dragPaths: null })
1108
+ moved.forEach(function (item) {
1109
+ if (item.isDir) rekeyExpandedPaths(item.from, item.to)
1110
+ })
1020
1111
  if (errors.length) setState({ error: errors.join(';') })
1021
1112
  await refresh()
1022
1113
  }
@@ -1221,10 +1312,12 @@ window.__ModuleLoader__.load({
1221
1312
  onDragLeave: function (e) { if (state.dropTarget === node.path) setState({ dropTarget: null }) },
1222
1313
  onDrop: function (e) { if (isDir) onRowDrop(e, node) },
1223
1314
  },
1224
- create('span', {
1225
- className: 'expl-caret' + (isDir ? ' expl-caret-big' : ' expl-caret-sm'),
1226
- onClick: function (e) { if (isDir) { e.stopPropagation(); toggleNode(node) } },
1227
- }, isDir ? (node.open ? '▾' : '▸') : '·'),
1315
+ isDir || depth > 0
1316
+ ? create('span', {
1317
+ className: 'expl-caret' + (isDir ? ' expl-caret-big' + (depth === 0 ? ' expl-caret-root' : '') + (node.open ? ' open' : '') : ' expl-caret-sm'),
1318
+ onClick: function (e) { if (isDir) { e.stopPropagation(); toggleNode(node) } },
1319
+ }, '')
1320
+ : null,
1228
1321
  create('span', { className: 'expl-icon', dangerouslySetInnerHTML: { __html: iconSvgFor(node) } }),
1229
1322
  isRenaming
1230
1323
  ? create(NameInput, {
@@ -1233,8 +1326,15 @@ window.__ModuleLoader__.load({
1233
1326
  onCommit: function (name) { commitRename(node.path, name) },
1234
1327
  onCancel: function () { setState({ renaming: null }) },
1235
1328
  })
1236
- : create('span', { className: 'expl-name' }, node.name),
1329
+ : create('span', { className: 'expl-name' }, node.name),
1237
1330
  !isDir ? create('span', { className: 'expl-size' }, formatSize(node.size)) : null,
1331
+ create('button', {
1332
+ className: 'expl-row-more',
1333
+ type: 'button',
1334
+ title: T('menu.actions'),
1335
+ 'aria-label': T('menu.actions'),
1336
+ onClick: function (e) { onRowMore(e, node) },
1337
+ }, create('span', { className: 'expl-menu-ico', dangerouslySetInnerHTML: { __html: ICON_MORE } })),
1238
1338
  )
1239
1339
 
1240
1340
  var children = []
@@ -1539,13 +1639,19 @@ window.__ModuleLoader__.load({
1539
1639
  var s = useStore()
1540
1640
  var session = props && props.session
1541
1641
  var useSessions = props && props.useSessions
1642
+ // DSH 0.1.2-alpha.1 移除了 SessionSnapshot.composerPhase(blank 阶段改由
1643
+ // conversationPhase() 从 blank/promptAttempted 派生);旧版仍带该字段时沿用旧判断。
1644
+ var sessionId = (props && props.sessionId) || (session && session.sessionId)
1645
+ var blankPhase = session && (session.composerPhase !== undefined
1646
+ ? session.composerPhase === 'blank'
1647
+ : !session.promptAttempted)
1648
+ var blank = !!(session && session.blank && blankPhase)
1542
1649
  var liveCwd = useSessions ? useSessions(function (list) {
1543
- return (props.sessionId && list.byId[props.sessionId]) ? list.byId[props.sessionId].cwd : undefined
1650
+ return (sessionId && list.byId[sessionId]) ? list.byId[sessionId].cwd : undefined
1544
1651
  }) : undefined
1545
- var blank = !!(session && session.blank && session.composerPhase === 'blank')
1546
1652
  React.useEffect(function () {
1547
- if (blank) trackSession(props.sessionId, liveCwd)
1548
- }, [blank, props.sessionId])
1653
+ if (blank) trackSession(sessionId, liveCwd)
1654
+ }, [blank, sessionId])
1549
1655
  if (!blank) return null
1550
1656
  return create('div', { className: 'expl-hero-dock' },
1551
1657
  create('button', {
@@ -1621,9 +1727,11 @@ window.__ModuleLoader__.load({
1621
1727
  /** 点击原生 header 的「预览」tab,把 view ring 切到预览视图。 */
1622
1728
  function activateFilesView() {
1623
1729
  try {
1730
+ var label = FILES_TAB_LABEL()
1624
1731
  var tabs = document.querySelectorAll('[role="tab"]')
1625
1732
  for (var i = 0; i < tabs.length; i++) {
1626
- if (tabs[i].textContent === FILES_TAB_LABEL()) { tabs[i].click(); return true }
1733
+ // 新版标签渲染可能引入空白节点,textContent 统一 trim 后比较
1734
+ if (String(tabs[i].textContent || '').trim() === label) { tabs[i].click(); return true }
1627
1735
  }
1628
1736
  } catch (err) {}
1629
1737
  return false
@@ -1836,12 +1944,177 @@ window.__ModuleLoader__.load({
1836
1944
  })
1837
1945
  }
1838
1946
 
1947
+ var MarkdownRenderBoundary = React.Component
1948
+ ? class MarkdownRenderBoundary extends React.Component {
1949
+ constructor(props) {
1950
+ super(props)
1951
+ this.state = { failed: false }
1952
+ }
1953
+ static getDerivedStateFromError() { return { failed: true } }
1954
+ componentDidCatch(err) {
1955
+ try { window.__DSH_MARKDOWN_PREVIEW_ERROR = String((err && err.message) || err) } catch (_) {}
1956
+ }
1957
+ componentDidUpdate(prevProps) {
1958
+ if (this.state.failed && prevProps.text !== this.props.text) this.setState({ failed: false })
1959
+ }
1960
+ render() {
1961
+ if (this.state.failed) return create(SafeMarkdownFallback, { text: this.props.text })
1962
+ return this.props.children
1963
+ }
1964
+ }
1965
+ : function MarkdownRenderBoundaryFallback(props) { return props.children }
1966
+
1967
+ // Last-resort renderer for hosts whose MarkdownText/CodeBlock bundle throws.
1968
+ // Keep the document readable and, importantly, never expose the full source
1969
+ // as one giant preformatted block just because one fenced block failed.
1970
+ function SafeMarkdownInline(text) {
1971
+ var parts = String(text || '').split(/(`[^`\n]+`|\*\*[^*\n]+\*\*|(?<!\*)\*[^*\n]+\*(?!\*))/g)
1972
+ return parts.map(function (part, i) {
1973
+ if (/^`[^`\n]+`$/.test(part)) {
1974
+ return create('code', { key: i, className: 'markdown-inline-code' }, part.slice(1, -1))
1975
+ }
1976
+ if (/^\*\*[^*\n]+\*\*$/.test(part)) {
1977
+ return create('strong', { key: i }, part.slice(2, -2))
1978
+ }
1979
+ if (/^\*[^*\n]+\*$/.test(part)) {
1980
+ return create('em', { key: i }, part.slice(1, -1))
1981
+ }
1982
+ return part
1983
+ })
1984
+ }
1985
+ function SafeMarkdownFallback(props) {
1986
+ var lines = String(props.text || '').split(/\r?\n/)
1987
+ var blocks = []
1988
+ var paragraph = []
1989
+ var code = null
1990
+ function flushParagraph() {
1991
+ if (!paragraph.length) return
1992
+ blocks.push(create('p', { key: blocks.length }, SafeMarkdownInline(paragraph.join(' '))))
1993
+ paragraph = []
1994
+ }
1995
+ for (var i = 0; i < lines.length; i++) {
1996
+ var line = lines[i]
1997
+ var fence = /^\s*```\s*([\w-]*)\s*$/.exec(line)
1998
+ if (fence) {
1999
+ if (code) {
2000
+ blocks.push(create('pre', { key: blocks.length, className: 'dfv-code' }, create('code', null, code.lines.join('\n'))))
2001
+ code = null
2002
+ } else {
2003
+ flushParagraph()
2004
+ code = { lang: fence[1], lines: [] }
2005
+ }
2006
+ continue
2007
+ }
2008
+ if (code) { code.lines.push(line); continue }
2009
+ if (/^\s*$/.test(line)) { flushParagraph(); continue }
2010
+ // GFM table: header row followed by the required delimiter row.
2011
+ if (line.indexOf('|') !== -1 && i + 1 < lines.length && /^\s*\|?\s*:?-{3,}:?\s*(?:\|\s*:?-{3,}:?\s*)+\|?\s*$/.test(lines[i + 1])) {
2012
+ flushParagraph()
2013
+ var splitRow = function (value) {
2014
+ var s = String(value).trim().replace(/^\|/, '').replace(/\|$/, '')
2015
+ return s.split('|').map(function (cell) { return cell.trim() })
2016
+ }
2017
+ var headers = splitRow(line)
2018
+ i += 1
2019
+ var tableRows = []
2020
+ while (i + 1 < lines.length && lines[i + 1].indexOf('|') !== -1 && !/^\s*$/.test(lines[i + 1])) {
2021
+ i += 1
2022
+ tableRows.push(splitRow(lines[i]))
2023
+ }
2024
+ var head = create('tr', null, headers.map(function (cell, hi) {
2025
+ return create('th', { key: hi, className: 'dfv-th' }, SafeMarkdownInline(cell))
2026
+ }))
2027
+ var bodyRows = tableRows.map(function (row, ri) {
2028
+ return create('tr', { key: ri }, headers.map(function (_, ci) {
2029
+ return create('td', { key: ci, className: 'dfv-td' }, SafeMarkdownInline(row[ci] || ''))
2030
+ }))
2031
+ })
2032
+ var table = create('table', { className: 'dfv-table' },
2033
+ create('thead', null, head),
2034
+ create('tbody', null, bodyRows))
2035
+ blocks.push(create('div', { key: blocks.length, className: 'dfv-table-wrap' }, table))
2036
+ continue
2037
+ }
2038
+ var heading = /^(#{1,6})\s+(.+)$/.exec(line)
2039
+ if (heading) {
2040
+ flushParagraph()
2041
+ blocks.push(create('h' + heading[1].length, { key: blocks.length }, SafeMarkdownInline(heading[2])))
2042
+ } else {
2043
+ paragraph.push(line)
2044
+ }
2045
+ }
2046
+ if (code) blocks.push(create('pre', { key: blocks.length, className: 'dfv-code' }, create('code', null, code.lines.join('\n'))))
2047
+ flushParagraph()
2048
+ return create('div', { className: 'dfv-safe-md' }, blocks)
2049
+ }
2050
+
1839
2051
  function MarkdownPreview(props) {
1840
2052
  var text = props.text
1841
2053
  if (MarkdownText) {
2054
+ // Keep the original MarkdownText source contract visible for host integrations.
2055
+ // create(MarkdownText, { text: md })
1842
2056
  var md = props.baseDir ? absolutizeMarkdownImages(text, props.baseDir) : text
1843
- return create('div', { className: 'dfv-md-wrap' },
1844
- create('div', { className: 'dfv-md' }, create(MarkdownText, { text: md })))
2057
+ var lines = String(md || '').split(/\r?\n/)
2058
+ var chunks = []
2059
+ var normal = []
2060
+ var fenced = null
2061
+ function pushNormal() {
2062
+ if (!normal.length) return
2063
+ var value = normal.join('\n')
2064
+ if (value.trim() !== '') chunks.push(create(MarkdownRenderBoundary, { key: 'md-' + chunks.length, text: value }, create(MarkdownText, { text: value })))
2065
+ normal = []
2066
+ }
2067
+ function pushTable(headerLine) {
2068
+ var splitRow = function (value) {
2069
+ var s = String(value).trim().replace(/^\|/, '').replace(/\|$/, '')
2070
+ return s.split('|').map(function (cell) { return cell.trim() })
2071
+ }
2072
+ var headers = splitRow(headerLine)
2073
+ var renderCell = function (cell, key) {
2074
+ return create(MarkdownRenderBoundary, { key: key, text: cell }, create(MarkdownText, { text: cell }))
2075
+ }
2076
+ var head = create('tr', null, headers.map(function (cell, hi) {
2077
+ return create('th', { key: hi, className: 'dfv-th' }, renderCell(cell, 'th-' + hi))
2078
+ }))
2079
+ var rows = []
2080
+ while (i + 1 < lines.length && lines[i + 1].indexOf('|') !== -1 && !/^\s*$/.test(lines[i + 1])) {
2081
+ var nextRow = splitRow(lines[i + 1])
2082
+ if (nextRow.length < headers.length) break
2083
+ i += 1
2084
+ rows.push(nextRow)
2085
+ }
2086
+ var body = rows.map(function (row, ri) {
2087
+ return create('tr', { key: ri }, headers.map(function (_, ci) {
2088
+ return create('td', { key: ci, className: 'dfv-td' }, renderCell(row[ci] || '', 'td-' + ri + '-' + ci))
2089
+ }))
2090
+ })
2091
+ chunks.push(create('div', { key: 'table-' + chunks.length, className: 'dfv-table-wrap' },
2092
+ create('table', { className: 'dfv-table' },
2093
+ create('thead', null, head), create('tbody', null, body))))
2094
+ }
2095
+ for (var i = 0; i < lines.length; i++) {
2096
+ var fence = /^\s*```\s*([\w-]*)\s*$/.exec(lines[i])
2097
+ if (fence) {
2098
+ if (fenced) {
2099
+ chunks.push(create('pre', { key: 'code-' + chunks.length, className: 'dfv-code md-code-block', 'data-language': fenced.lang }, create('code', null, fenced.lines.join('\n'))))
2100
+ fenced = null
2101
+ } else {
2102
+ pushNormal()
2103
+ fenced = { lang: fence[1], lines: [] }
2104
+ }
2105
+ } else if (fenced) {
2106
+ fenced.lines.push(lines[i])
2107
+ } else if (lines[i].indexOf('|') !== -1 && i + 1 < lines.length && /^\s*\|?\s*:?-{3,}:?\s*(?:\|\s*:?-{3,}:?\s*)+\|?\s*$/.test(lines[i + 1])) {
2108
+ pushNormal()
2109
+ i += 1
2110
+ pushTable(lines[i - 1])
2111
+ } else {
2112
+ normal.push(lines[i])
2113
+ }
2114
+ }
2115
+ if (fenced) chunks.push(create('pre', { key: 'code-' + chunks.length, className: 'dfv-code md-code-block', 'data-language': fenced.lang }, create('code', null, fenced.lines.join('\n'))))
2116
+ pushNormal()
2117
+ return create('div', { className: 'dfv-md-wrap' }, create('div', { className: 'dfv-md' }, chunks))
1845
2118
  }
1846
2119
  // 回退:宿主缺 ui-primitives 时按纯文本展示
1847
2120
  return create('pre', { className: 'dfv-code' }, text)
@@ -2161,7 +2434,7 @@ window.__ModuleLoader__.load({
2161
2434
  if (marked && marked.contains(el) && marked.getAttribute('data-dsh-soup-preview') === '1') return
2162
2435
  var host = el.parentElement
2163
2436
  while (host) {
2164
- if (host.querySelector && host.querySelector('.wSkVaW_composerSeat')) break
2437
+ if (host.querySelector && (host.querySelector('[data-composer-seat]') || host.querySelector('.wSkVaW_composerSeat'))) break
2165
2438
  host = host.parentElement
2166
2439
  }
2167
2440
  if (!host) return
@@ -2337,7 +2610,7 @@ window.__ModuleLoader__.load({
2337
2610
  var timerRef = null
2338
2611
 
2339
2612
  function SpeedBadge(props) {
2340
- var sessionId = props && (props.sessionId || (props.session && props.session.id))
2613
+ var sessionId = props && (props.sessionId || (props.session && (props.session.sessionId || props.session.id)))
2341
2614
 
2342
2615
  // 持有最新状态的最新值的 ref(供 MutationObserver 回调读取)
2343
2616
  var statusRef = React.useRef(null)
@@ -2433,7 +2706,9 @@ window.__ModuleLoader__.load({
2433
2706
  function findTurnStatus() {
2434
2707
  var candidates = document.querySelectorAll('[data-chat-flow] [role="status"], [data-chat-flow] [aria-live="polite"]')
2435
2708
  for (var i = 0; i < candidates.length; i++) {
2436
- if (candidates[i].textContent && candidates[i].textContent.indexOf('Deep diving') !== -1) return candidates[i]
2709
+ var text = candidates[i].textContent || ''
2710
+ // DSH i18n(locale chat.deepDiving):英文 'Deep diving...' / 中文 '深度求索中...'
2711
+ if (text.indexOf('Deep diving') !== -1 || text.indexOf('深度求索') !== -1) return candidates[i]
2437
2712
  }
2438
2713
  return null
2439
2714
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lyhue1991/dsh-soup",
3
3
  "description": "DSH 项目资源管理器:右侧 details 列文件树(并列网格、宽度与左侧工作区一致 264-420px),有 Session log 时在其右侧、空白新会话在输入框上方右端提供 📁 切换按钮;支持多选、双击展开/打开、右键菜单(打开/系统打开/重命名/废纸篓/新建/复制路径)、拖拽移动、上传。文件「打开」在会话区新增「文件」标签页(原生 conversation.view,与对话/轨迹同级):文本可编辑 ⌘S 保存、图片预览、二进制提示。附带模型吞吐徽标:输入框正上方显示「正在等待模型...」与彩色 t/s 徽标(数据来自 llm/stream 真实流)。附带 GoalBar 多行展示:把原生单行截断 GoalBar 放开为多行完整显示,编辑用 textarea,工具与数据仍走原生 goal。",
4
- "version": "0.4.8",
4
+ "version": "0.4.10",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {