dsh-long-plugins 2.2.1 → 2.2.3

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/client/client.js CHANGED
@@ -3053,7 +3053,7 @@ window.__ModuleLoader__.load({
3053
3053
  backdrop-filter: none;
3054
3054
  -webkit-backdrop-filter: none;
3055
3055
  }
3056
- /* 左栏顶部「新会话」按钮:去掉白/黑实底与边框,透出背景图;文字跟侧边栏一致(白+描边) */
3056
+ /* 左栏顶部「新会话」按钮:去掉白/黑实底与边框,透出背景图;文字颜色交由下方统一的侧边栏/顶栏可读文字规则决定 */
3057
3057
  html[data-dsh-glass="on"] #root [class*="newSession"] {
3058
3058
  background-color: transparent !important;
3059
3059
  background-image: none !important;
@@ -3062,9 +3062,9 @@ window.__ModuleLoader__.load({
3062
3062
  box-shadow: none !important;
3063
3063
  backdrop-filter: none !important;
3064
3064
  -webkit-backdrop-filter: none !important;
3065
- color: #ffffff !important;
3066
- text-shadow: 0 1px 3px rgba(0,0,0,.85);
3067
3065
  }
3066
+ /* 左栏/顶栏文字可读性改用 JS 处理(见 glassPlugin.apply 的 applyChromeText),
3067
+ 只作用于主界面左栏/顶栏、并跳过设置/弹窗/浮层,避免波及设置面板 */
3068
3068
  /* 左栏内部根 / 底部容器也用透明(这些是 DSH 当前版本哈希类名;背景图要透到左栏与底部) */
3069
3069
  html[data-dsh-glass="on"] #root [class*="hHd-Xa_root"],
3070
3070
  html[data-dsh-glass="on"] #root [class*="FJxK0a_root"] {
@@ -3109,7 +3109,7 @@ window.__ModuleLoader__.load({
3109
3109
  -webkit-backdrop-filter: none !important;
3110
3110
  }
3111
3111
  /* 输入框盒子(uV2eYG_card):实底(浅色=白/深色=深)并在背景图之上,文字随主题,保证可读。
3112
- 加 #root 提高优先级,压过 DSH 自身把 card 背景设透明的规则 */
3112
+ 加 #root 提高优先级,压过 DSH 自身把 card 背景设透明的规则(与 v2.2.1 一致) */
3113
3113
  html[data-dsh-glass="on"] #root [class*="uV2eYG_card"],
3114
3114
  html[data-dsh-glass="on"] [class*="uV2eYG_card"] {
3115
3115
  position: relative !important;
@@ -3125,6 +3125,46 @@ window.__ModuleLoader__.load({
3125
3125
  html[data-dsh-glass="on"] #root [class*="uV2eYG_card"],
3126
3126
  html[data-dsh-glass="on"] [class*="uV2eYG_card"] { background: #1c2530 !important; color: #f5f7fa !important; }
3127
3127
  }
3128
+ /* 会话语流"耗时统计"(turn-tail: 12:38/用时/首token/tok/s):白字+深投影,避免淡字叠在背景图上看不见 */
3129
+ html[data-dsh-glass="on"] [class*="osXY9a_root"],
3130
+ html[data-dsh-glass="on"] [class*="osXY9a_root"] span,
3131
+ html[data-dsh-glass="on"] [class*="p-xYUq_timeEnd"],
3132
+ html[data-dsh-glass="on"] [class*="p-xYUq_"] span {
3133
+ color: #ffffff !important;
3134
+ text-shadow: 0 1px 1px rgba(0,0,0,.5);
3135
+ }
3136
+ /* DSH 弹窗/浮层(提问/确认/重命名等):给不透明底+主题文字, 杜绝透明与会话内容重叠 */
3137
+ html[data-dsh-glass="on"] [class*="Radix"] [class*="Content"],
3138
+ html[data-dsh-glass="on"] [class*="DialogContent"],
3139
+ html[data-dsh-glass="on"] [class*="ModalContent"],
3140
+ html[data-dsh-glass="on"] [class*="overlay"] [class*="content"],
3141
+ html[data-dsh-glass="on"] [class*="_content"][class*="dialog"] {
3142
+ background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 76%, transparent) !important;
3143
+ color: var(--dsw-alias-label-primary) !important;
3144
+ backdrop-filter: blur(var(--dsh-glass-blur, 16px)) !important;
3145
+ -webkit-backdrop-filter: blur(var(--dsh-glass-blur, 16px)) !important;
3146
+ }
3147
+ /* DSH 提问/弹窗卡(M8wy4a_card):不透明底+原生文字, 避免透明重叠、白字。
3148
+ 注意: 不能再用 [data-chain-overlay-fallback] [class*=card](会误匹配输入框 uV2eYG_card 加 blur 造成重影) */
3149
+ html[data-dsh-glass="on"] [class*="M8wy4a_card"] {
3150
+ background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 76%, transparent) !important;
3151
+ color: var(--dsw-alias-label-primary) !important;
3152
+ backdrop-filter: blur(var(--dsh-glass-blur, 16px)) !important;
3153
+ -webkit-backdrop-filter: blur(var(--dsh-glass-blur, 16px)) !important;
3154
+ }
3155
+ /* 任务/todo 面板(lXshSW_root, 在 conversation.input.dock 内):不透明底+可读文字, 不透明与会话重叠 */
3156
+ html[data-dsh-glass="on"] [class*="lXshSW_root"],
3157
+ html[data-dsh-glass="on"] [class*="lXshSW_body"] {
3158
+ background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 76%, transparent) !important;
3159
+ color: var(--dsw-alias-label-primary) !important;
3160
+ backdrop-filter: blur(var(--dsh-glass-blur, 16px)) !important;
3161
+ -webkit-backdrop-filter: blur(var(--dsh-glass-blur, 16px)) !important;
3162
+ }
3163
+ html[data-dsh-glass="on"] [class*="lXshSW_header"] [class*="lXshSW_title"],
3164
+ html[data-dsh-glass="on"] [class*="lXshSW_header"] [class*="lXshSW_progress"],
3165
+ html[data-dsh-glass="on"] [class*="lXshSW_item"] {
3166
+ color: var(--dsw-alias-label-primary) !important;
3167
+ }
3128
3168
  /* 轮次/历史提问预览:默认隐藏(点开才显示),并去掉头部底色,避免底部露出白条 */
3129
3169
  .dsh-turn-preview:not(.open){display:none !important;}
3130
3170
  .dsh-turn-preview-head{background:transparent !important;}
@@ -3148,17 +3188,21 @@ window.__ModuleLoader__.load({
3148
3188
  html[data-dsh-glass="on"][data-dsh-theme="light"] #root [class*="QWLzLg_title"] {
3149
3189
  color: #2f3d4a !important;
3150
3190
  }
3151
- /* 输入框下方状态/统计行(余额+步数/时长/速度等)都在 composer.dock 这个稳定 data 槽里:
3152
- 混淆类名会变, 改用 data 属性锁定, 按主题给高对比色+描边, 保证在背景图上可读 */
3153
- html[data-dsh-glass="on"][data-dsh-theme="light"] [data-slot-conversation="conversation.composer.dock"],
3154
- html[data-dsh-glass="on"][data-dsh-theme="light"] [data-slot-conversation="conversation.composer.dock"] * {
3155
- color: #243240 !important;
3156
- text-shadow: 0 1px 2px rgba(255,255,255,.5);
3157
- }
3158
- html[data-dsh-glass="on"][data-dsh-theme="dark"] [data-slot-conversation="conversation.composer.dock"],
3159
- html[data-dsh-glass="on"][data-dsh-theme="dark"] [data-slot-conversation="conversation.composer.dock"] * {
3160
- color: #e8edf3 !important;
3161
- text-shadow: 0 1px 2px rgba(0,0,0,.6);
3191
+ /* 输入框下方状态/统计行(余额+步数/时长/速度等)都在 composer.dock 槽里:
3192
+ 背景是会变的背景图, 单色难保证可读;给整块加半透明深色衬底+亮字, 任意背景都可读。
3193
+ 槽位属性可能是 data-slot-conversation data-slot(两种都锁) */
3194
+ html[data-dsh-glass="on"] [data-slot-conversation="conversation.composer.dock"],
3195
+ html[data-dsh-glass="on"] [data-slot="conversation.composer.dock"] {
3196
+ background-color: rgba(12,20,30,.55) !important;
3197
+ border-radius: 8px !important;
3198
+ padding: 1px 10px !important;
3199
+ backdrop-filter: blur(2px);
3200
+ -webkit-backdrop-filter: blur(2px);
3201
+ }
3202
+ html[data-dsh-glass="on"] [data-slot-conversation="conversation.composer.dock"] *,
3203
+ html[data-dsh-glass="on"] [data-slot="conversation.composer.dock"] * {
3204
+ color: #f0f4f9 !important;
3205
+ text-shadow: 0 1px 1px rgba(0,0,0,.5);
3162
3206
  }
3163
3207
  /* 会话区所有内容元素背景透明化:去掉"反底色"小块衬底,统一融入磨砂卡片 */
3164
3208
  html[data-dsh-glass="on"] #root [class*="wSkVaW_scrollBody"] *:not([class*="button"]):not([class*="bubble"]):not([class*="_menu"]) {
@@ -3175,20 +3219,20 @@ window.__ModuleLoader__.load({
3175
3219
  }
3176
3220
  /* 输入框下拉/悬浮菜单(如 / 命令、@ 提及):背景给不透明随主题色,避免叠在会话磨砂卡上重叠看不清 */
3177
3221
  html[data-dsh-glass="on"] #root [class*="_menu"][class*="_menu"][class*="_menu"][class*="_menu"] {
3178
- background-color: var(--dsw-specific-input-major, #e8edf3) !important;
3222
+ background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 76%, transparent) !important;
3179
3223
  background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 97%, transparent) !important;
3180
3224
  backdrop-filter: none !important;
3181
3225
  -webkit-backdrop-filter: none !important;
3182
3226
  }
3183
3227
  /* 输入框旁权限/功能选择下拉(盾牌 Full access 等 Radix 风格菜单):与 / 命令菜单同样处理 */
3184
3228
  html[data-dsh-glass="on"] #root [class*="uV2eYG_card"] [class*="_root_"]:has([class*="_list_"]) {
3185
- background-color: var(--dsw-specific-input-major, #e8edf3) !important;
3229
+ background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 76%, transparent) !important;
3186
3230
  background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 97%, transparent) !important;
3187
3231
  backdrop-filter: none !important;
3188
3232
  -webkit-backdrop-filter: none !important;
3189
3233
  }
3190
3234
  html[data-dsh-glass="on"] #root [class*="uV2eYG_card"] [class*="_list_"] {
3191
- background-color: var(--dsw-specific-input-major, #e8edf3) !important;
3235
+ background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 76%, transparent) !important;
3192
3236
  background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 97%, transparent) !important;
3193
3237
  backdrop-filter: none !important;
3194
3238
  -webkit-backdrop-filter: none !important;
@@ -3276,14 +3320,16 @@ window.__ModuleLoader__.load({
3276
3320
  )
3277
3321
  }
3278
3322
  const GlassSettingsSection = () => {
3279
- const [state, setState] = React.useState({ loading: true, saving: false, error: '', cfg: null, bgImage: '' })
3323
+ const [state, setState] = React.useState({ loading: true, saving: false, error: '', cfg: null, bgImage: '', bgList: [] })
3280
3324
  const load = React.useCallback(async () => {
3281
3325
  setState((s) => ({ ...s, loading: true, error: '' }))
3282
3326
  try {
3283
3327
  const r = await fetch('/api/dsh-uploads/glass-config', { cache: 'no-store' })
3284
3328
  const b = await r.json()
3285
3329
  if (!r.ok) throw new Error(b.error || ('HTTP ' + r.status))
3286
- setState((s) => ({ ...s, loading: false, cfg: b.cfg, bgImage: b.bgImage || '' }))
3330
+ let list = []
3331
+ try { const rb = await fetch('/api/dsh-uploads/glass-backgrounds', { cache: 'no-store' }); const jb = await rb.json(); list = jb.list || [] } catch (_) {}
3332
+ setState((s) => ({ ...s, loading: false, cfg: b.cfg, bgImage: b.bgImage || '', bgList: list }))
3287
3333
  } catch (e) { setState((s) => ({ ...s, loading: false, error: glassText(e) })) }
3288
3334
  }, [])
3289
3335
  React.useEffect(() => { load(); }, [load])
@@ -3339,13 +3385,12 @@ window.__ModuleLoader__.load({
3339
3385
  state.error ? React.createElement('div', { className: 'dsh-glass-error' }, state.error) : null,
3340
3386
  React.createElement('div', { className: 'dsh-glass-card' },
3341
3387
  React.createElement('div', { className: 'dsh-glass-row' },
3342
- React.createElement('label', null, '磨砂模糊强度'),
3343
- React.createElement('input', { type: 'range', className: 'dsh-glass-range', min: 0, max: 80, value: cfg.blur ?? 20, onChange: (e) => patch({ blur: Number(e.target.value) }) }),
3344
- React.createElement('span', { className: 'dsh-glass-val' }, `${Number(cfg.blur ?? 20)} px`),
3345
- ),
3346
- React.createElement('div', { className: 'dsh-glass-row' },
3347
- React.createElement('label', null, '共用背景图'),
3388
+ React.createElement('label', null, '背景图'),
3348
3389
  React.createElement('div', { className: 'dsh-glass-bg' }, glassImageInput(state.bgImage, (v) => setState((s) => ({ ...s, bgImage: v })))),
3390
+ state.bgList && state.bgList.length ? React.createElement('select', { className: 'dsh-glass-file', value: '', style: { marginLeft: 8 }, onChange: async (e) => {
3391
+ const name = e.target.value; if (!name) return
3392
+ try { const rb = await fetch('/api/dsh-uploads/glass-backgrounds?name=' + encodeURIComponent(name), { cache: 'no-store' }); const uri = await rb.text(); if (uri && uri.startsWith('data:image/')) setState((s) => ({ ...s, bgImage: uri })) } catch (_) {}
3393
+ } }, React.createElement('option', { value: '' }, '选择旧图…'), state.bgList.map((n, i) => React.createElement('option', { key: n, value: n }, '背景图 ' + (i + 1))) ) : null,
3349
3394
  ),
3350
3395
  React.createElement('div', { className: 'dsh-glass-card' },
3351
3396
  React.createElement('div', { className: 'dsh-glass-zone-title' }, '背景罩'),
@@ -3372,9 +3417,18 @@ window.__ModuleLoader__.load({
3372
3417
  React.createElement('span', { className: 'dsh-glass-val' }, `${Number(cfg.bgBlur ?? 0)} px`),
3373
3418
  ),
3374
3419
  React.createElement('div', { className: 'dsh-glass-row' },
3375
- React.createElement('label', null, '背景图透明度'),
3376
- React.createElement('input', { type: 'range', className: 'dsh-glass-range', min: 0, max: 1, step: 0.01, value: cfg.bgOpacity ?? 1, onChange: (e) => patch({ bgOpacity: Number(e.target.value) }) }),
3377
- React.createElement('span', { className: 'dsh-glass-val' }, `${Math.round((Number(cfg.bgOpacity ?? 1)) * 100)}%`),
3420
+ React.createElement('label', null, '背景透明度'),
3421
+ React.createElement('input', { type: 'range', className: 'dsh-glass-range', min: 0, max: 100, value: Math.round((1 - (Number(cfg.bgOpacity ?? 1))) * 100), onChange: (e) => patch({ bgOpacity: 1 - Number(e.target.value) / 100 }) }),
3422
+ React.createElement('span', { className: 'dsh-glass-val' }, `${Math.round((1 - (Number(cfg.bgOpacity ?? 1))) * 100)}%`),
3423
+ ),
3424
+ React.createElement('div', { className: 'dsh-glass-card' },
3425
+ React.createElement('div', { className: 'dsh-glass-zone-title' }, '面板'),
3426
+ React.createElement('div', { className: 'dsh-glass-row' },
3427
+ React.createElement('label', null, '面板磨砂'),
3428
+ React.createElement('input', { type: 'range', className: 'dsh-glass-range', min: 0, max: 80, value: cfg.blur ?? 20, onChange: (e) => patch({ blur: Number(e.target.value) }) }),
3429
+ React.createElement('span', { className: 'dsh-glass-val' }, `${Number(cfg.blur ?? 20)} px`),
3430
+ ),
3431
+ React.createElement('p', { className: 'dsh-glass-note' }, '作用于弹窗/任务卡等面板的磨砂玻璃强度(0 = 关闭磨砂)。'),
3378
3432
  ),
3379
3433
  React.createElement('div', { className: 'dsh-glass-card' },
3380
3434
  React.createElement('div', { className: 'dsh-glass-zone-title' }, '输入框'),
@@ -3388,13 +3442,13 @@ window.__ModuleLoader__.load({
3388
3442
  React.createElement('button', { type: 'button', style: { marginLeft: 8, fontSize: 12 }, onClick: () => patchInput(t, { color: '', opacity: 1 }) }, '原生'),
3389
3443
  ),
3390
3444
  React.createElement('div', { className: 'dsh-glass-row' },
3391
- React.createElement('label', null, '不透明度'),
3392
- React.createElement('input', { type: 'range', className: 'dsh-glass-range', min: 0, max: 1, step: 0.01, value: (typeof tb.opacity === 'number') ? tb.opacity : 1, onChange: (e) => patchInput(t, { opacity: Number(e.target.value) }) }),
3393
- React.createElement('span', { className: 'dsh-glass-val' }, `${Math.round((typeof tb.opacity === 'number' ? tb.opacity : 1) * 100)}%`),
3445
+ React.createElement('label', null, '透明度'),
3446
+ React.createElement('input', { type: 'range', className: 'dsh-glass-range', min: 0, max: 100, value: Math.round((1 - (typeof tb.opacity === 'number' ? tb.opacity : 1)) * 100), onChange: (e) => patchInput(t, { opacity: 1 - Number(e.target.value) / 100 }) }),
3447
+ React.createElement('span', { className: 'dsh-glass-val' }, `${Math.round((1 - (typeof tb.opacity === 'number' ? tb.opacity : 1)) * 100)}%`),
3394
3448
  ),
3395
3449
  )
3396
3450
  }),
3397
- React.createElement('p', { className: 'dsh-glass-note' }, '浅色主题与深色主题可分别设置。100% = 实底不透明,越低越透出背景图;"原生" = 跟随该主题的 DSH 原生色。文字颜色按背景亮度自动配深/浅,保证可读。'),
3451
+ React.createElement('p', { className: 'dsh-glass-note' }, '浅色主题与深色主题可分别设置。0% = 全不透明,100% = 全透明;"原生" = 跟随该主题的 DSH 原生色。文字颜色按背景亮度自动配深/浅,保证可读。'),
3398
3452
  ),
3399
3453
  React.createElement('p', { className: 'dsh-glass-note' }, '提示:背景图铺满整页(可加背景图罩);背景图模糊/透明度单独可调;会话区使用自己的罩色、罩强度与透明度;左栏/顶部透出背景图;输入框颜色/不透明度可单独设置。'),
3400
3454
  React.createElement('div', { className: 'dsh-glass-actions' },
@@ -3509,6 +3563,7 @@ window.__ModuleLoader__.load({
3509
3563
  const bgHex = hasColor ? tb.color : defBg
3510
3564
  const alpha = (tb && typeof tb.opacity === 'number' && Number.isFinite(tb.opacity)) ? Math.min(1, Math.max(0, tb.opacity)) : 1
3511
3565
  const bg = `rgba(${hexToRgb(bgHex)},${alpha})`
3566
+ // 背景/文字同步:按背景底色亮度决定文字色(亮底=深字/暗底=浅字),确保可读;与 v2.2.1 一致
3512
3567
  const fg = relLum(bgHex) > 0.5 ? '#1a2332' : '#f5f7fa'
3513
3568
  inputCard.style.setProperty('background-color', bg, 'important')
3514
3569
  inputCard.style.setProperty('color', fg, 'important')
@@ -3517,6 +3572,158 @@ window.__ModuleLoader__.load({
3517
3572
  el.style.setProperty('color', fg, 'important')
3518
3573
  })
3519
3574
  }
3575
+ // 主界面左栏/顶栏可见文字:白字+单层轻投影(Windows 桌面图标风格, 不加粗)。
3576
+ // 显式跳过设置/弹窗/浮层(modal/overlay/dialog/settings/panel/drawer/Radix/menu), 避免波及设置面板/弹出菜单。
3577
+ const SKIP_SEL = ':is([class*="settings"],[class*="Dialog"],[class*="dialog"],[class*="modal"],[class*="overlay"],[class*="_panel"],[class*="panel"],[class*="drawer"],[class*="Radix"],[class*="_menu"],[class*="brand"],[data-chain-overlay-fallback],[class*="M8wy4a_"])'
3578
+ const isInSkip = (el) => { let p = el; while (p && p !== document.documentElement) { if (p.matches && p.matches(SKIP_SEL)) return true; p = p.parentElement } return false }
3579
+ const applyChromeText = () => {
3580
+ if (!c.enabled) return
3581
+ // 只处理主界面左栏/顶栏内带文字的元素;跳过图标/纯容器。
3582
+ // 文字检测放宽:button/a/span/li/p/标题等直接算,纯 div 容器需自身带文本才算,
3583
+ // 这样底部「设置/已归档」等包在子元素里的按钮也能命中。
3584
+ const roots = [...document.querySelectorAll('#root [class*="sidebarCol"],#root [class*="wSkVaW_header"]')]
3585
+ roots.forEach((root) => {
3586
+ if (isInSkip(root)) return
3587
+ root.querySelectorAll('div,span,button,a,label,p,li,h1,h2,h3,h4').forEach((el) => {
3588
+ if (isInSkip(el)) return
3589
+ const tag = el.tagName
3590
+ const hasText = el.textContent && el.textContent.trim()
3591
+ if (!hasText) return
3592
+ const ownText = Array.from(el.childNodes).some((n) => n.nodeType === 3 && n.textContent.trim())
3593
+ const textLike = /^(BUTTON|A|SPAN|LABEL|LI|P|H1|H2|H3|H4)$/.test(tag)
3594
+ if (!textLike && !ownText) return
3595
+ // 跳过有盒子背景的元素(badge/徽章/药丸等, 如 deepseek 的 HARNESS 徽章):
3596
+ // 这类文字颜色应随其盒子背景走主题, 不强制白字(否则白底徽章+白字不可见)
3597
+ const bgbg = getComputedStyle(el).backgroundColor
3598
+ if (bgbg && bgbg !== 'rgba(0, 0, 0, 0)' && bgbg !== 'transparent') return
3599
+ el.style.setProperty('color', '#ffffff', 'important')
3600
+ el.style.setProperty('text-shadow', '0 1px 1px rgba(0,0,0,.5)')
3601
+ })
3602
+ // 图标也变白(随 currentColor),用于工作区文件夹等单色图标;保留显式品牌色
3603
+ root.querySelectorAll('svg, [class*="icon"], [class*="Icon"]').forEach((el) => {
3604
+ if (isInSkip(el)) return
3605
+ el.style.setProperty('color', '#ffffff', 'important')
3606
+ el.querySelectorAll('path,use,rect,circle,line,polyline,ellipse').forEach((p) => {
3607
+ const f = (p.getAttribute('fill') || '').toLowerCase()
3608
+ if (f && f !== 'none' && f !== 'currentcolor') return
3609
+ p.style.setProperty('fill', 'currentColor', 'important')
3610
+ const s = (p.getAttribute('stroke') || '').toLowerCase()
3611
+ if (s && s !== 'none' && s !== 'currentcolor') return
3612
+ p.style.setProperty('stroke', 'currentColor', 'important')
3613
+ })
3614
+ })
3615
+ })
3616
+ }
3617
+ // 会话区内容(尤其 AI 过程内容:Bash/Think 标签、思考、工具输出等浅色灰字)
3618
+ // 在透明背景图上看不清;用同侧边栏「白字+深投影」处理,跳过有盒子背景的元素(用户气泡/代码块衬底等)。
3619
+ const applySessionText = () => {
3620
+ if (!c.enabled) return
3621
+ const scroll = document.querySelector('#root [class*="wSkVaW_scrollBody"]')
3622
+ if (!scroll) return
3623
+ scroll.querySelectorAll('div,span,p,li,pre,code,button,a,label,h1,h2,h3,h4').forEach((el) => {
3624
+ if (isInSkip(el)) return
3625
+ const hasText = el.textContent && el.textContent.trim()
3626
+ if (!hasText) return
3627
+ const ownText = Array.from(el.childNodes).some((n) => n.nodeType === 3 && n.textContent.trim())
3628
+ const textLike = /^(BUTTON|A|SPAN|LABEL|LI|P|H1|H2|H3|H4|PRE|CODE)$/.test(el.tagName)
3629
+ if (!textLike && !ownText) return
3630
+ const bgbg = getComputedStyle(el).backgroundColor
3631
+ if (bgbg && bgbg !== 'rgba(0, 0, 0, 0)' && bgbg !== 'transparent') return
3632
+ el.style.setProperty('color', '#ffffff', 'important')
3633
+ el.style.setProperty('text-shadow', '0 1px 1px rgba(0,0,0,.5)')
3634
+ })
3635
+ // 在白字底上, 给过程内容配色区分, Error 用红色。
3636
+ // 文字常在标签的子元素里(白字已把它设白), 故连同文本子元素一起上色。
3637
+ const paint = (el, color) => {
3638
+ if (!el) return
3639
+ const set = (d) => { if (d.textContent && d.textContent.trim()) d.style.setProperty('color', color, 'important') }
3640
+ set(el)
3641
+ el.querySelectorAll('span,div,p,li,a').forEach(set)
3642
+ }
3643
+ // 错误/失败单行文本 → 红(只判短行、以 Error/错误/失败/异常 开头, 避免整段误标红)
3644
+ const isErrText = (d) => {
3645
+ const t = (d.textContent || '').trim()
3646
+ if (!t || t.length > 120) return false
3647
+ return /^(error|错误|失败|异常|fail)\b/i.test(t) || /^(error|错误|失败|异常)\s*[::]/i.test(t) || /^File not found/i.test(t)
3648
+ }
3649
+ scroll.querySelectorAll('div,span,p,li,pre,code').forEach((el) => {
3650
+ if (!isErrText(el)) return
3651
+ const ownText = Array.from(el.childNodes).some((n) => n.nodeType === 3 && n.textContent.trim())
3652
+ if (ownText) el.style.setProperty('color', '#ff6b6b', 'important')
3653
+ })
3654
+ // 工具名标签(o3BgMG_title):按工具类型配色(Bash/Shell=绿, Read=蓝, Edit=橙, Write=紫, 默认蓝)
3655
+ const toolColor = (text) => {
3656
+ const t = (text || '').toLowerCase()
3657
+ if (/bash|shell|zsh|terminal|powershell|cmd|sh\b/i.test(t)) return '#5ec26b'
3658
+ if (/edit|modify/i.test(t)) return '#f0a860'
3659
+ if (/write|create/i.test(t)) return '#c9a0ff'
3660
+ return '#8ccfff'
3661
+ }
3662
+ scroll.querySelectorAll('[class*="o3BgMG_title"],[class*="CY-8Ka_title"]').forEach((title) => {
3663
+ paint(title, toolColor(title.textContent))
3664
+ // 从标签向上找最近一个"含链接/代码/更多文本"的容器作为工具行,找不到就用父级
3665
+ let box = title.parentElement
3666
+ for (let i = 0; i < 4 && box; i++) {
3667
+ if (box.querySelector && box.querySelector('a, code, [class*="desc"], [class*="path"], [class*="cmd"], [class*="arg"]')) break
3668
+ box = box.parentElement
3669
+ }
3670
+ if (!box) box = title.parentElement
3671
+ box.querySelectorAll('div,span,a,p,li,pre,code').forEach((el) => {
3672
+ if (el === title || el.querySelector('[class*="o3BgMG_title"]')) return
3673
+ const ownText = Array.from(el.childNodes).some((n) => n.nodeType === 3 && n.textContent.trim())
3674
+ if (!ownText || isErrText(el)) return
3675
+ paint(el, '#6fb89a')
3676
+ })
3677
+ })
3678
+ // 工具输出/路径(o3BgMG_summary):成功 → 水绿;errorSummary 保持红
3679
+ scroll.querySelectorAll('[class*="o3BgMG_summary"]').forEach((el) => {
3680
+ if (/errorSummary/i.test(String(el.className))) return
3681
+ paint(el, '#6fb89a')
3682
+ })
3683
+ // 路径/文件链接(o3BgMG_fileLink) → 水绿(与白字标签区分)
3684
+ scroll.querySelectorAll('[class*="o3BgMG_fileLink"]').forEach((el) => paint(el, '#6fb89a'))
3685
+ // 思考正文(think 容器) → 暖琥珀;再覆盖 Think 标签为琥珀。
3686
+ // 全文档查找,不受 scrollBody 位置/加载时序影响(仍跳过设置/菜单/浮层)
3687
+ const paintThinkBody = (root) => {
3688
+ if (!root) return
3689
+ root.querySelectorAll('div,span,p,li').forEach((el) => {
3690
+ const ownText = Array.from(el.childNodes).some((n) => n.nodeType === 3 && n.textContent.trim())
3691
+ if (ownText && !isErrText(el) && !isInSkip(el)) paint(el, '#e6c583')
3692
+ })
3693
+ }
3694
+ const thinkRoots = [...document.querySelectorAll('[class*="QWLzLg_root"]')].filter((r) => !isInSkip(r))
3695
+ thinkRoots.forEach(paintThinkBody)
3696
+ document.querySelectorAll('[class*="QWLzLg_title"]').forEach((el) => {
3697
+ if (!isInSkip(el)) paint(el, '#f2c566')
3698
+ })
3699
+ // 会话区工具调用等图标:改为白色清晰显示(单色图标随 currentColor 变白, 保留品牌色)
3700
+ scroll.querySelectorAll('svg, [class*="icon"], [class*="Icon"]').forEach((el) => {
3701
+ if (isInSkip(el)) return
3702
+ el.style.setProperty('color', '#ffffff', 'important')
3703
+ el.querySelectorAll('path,use,rect,circle,line,polyline,ellipse').forEach((p) => {
3704
+ const f = (p.getAttribute('fill') || '').toLowerCase()
3705
+ if (f && f !== 'none' && f !== 'currentcolor') return
3706
+ p.style.setProperty('fill', 'currentColor', 'important')
3707
+ const s = (p.getAttribute('stroke') || '').toLowerCase()
3708
+ if (s && s !== 'none' && s !== 'currentcolor') return
3709
+ p.style.setProperty('stroke', 'currentColor', 'important')
3710
+ })
3711
+ })
3712
+ }
3713
+ // DSH 提问/弹窗卡(M8wy4a_card 等):内联强制不透明底+原生文字色, 压过 DSH 重渲染/透明与白字
3714
+ const fixDialogs = () => {
3715
+ if (!c.enabled) return
3716
+ const dark = dshDark()
3717
+ const bg = (getComputedStyle(document.documentElement).getPropertyValue('--dsw-specific-input-major') || '').trim() || '#e8edf3'
3718
+ const fg = dark ? '#f2f6fa' : '#1a2332'
3719
+ // 只匹配提问/弹窗卡(M8wy4a_card),避免误匹配输入框 uV2eYG_card(含"card")导致 blur 重影
3720
+ document.querySelectorAll('[class*="M8wy4a_card"]').forEach((el) => {
3721
+ const rgb = bg
3722
+ el.style.setProperty('background-color', `color-mix(in srgb, ${rgb} 76%, transparent)`, 'important')
3723
+ el.style.setProperty('color', fg, 'important')
3724
+ el.style.setProperty('backdrop-filter', 'blur(var(--dsh-glass-blur, 20px))')
3725
+ })
3726
+ }
3520
3727
  // 主题相关的东西统一重算:背景罩 + 输入框。切主题时由 observer/mq 触发。
3521
3728
  // 写入前先断开观察器,写完再重连,避免自触发造成死循环。
3522
3729
  const applyTheme = () => {
@@ -3529,6 +3736,9 @@ window.__ModuleLoader__.load({
3529
3736
  const v = `rgba(${hexToRgb(bt.color)},${bt.mask})`
3530
3737
  root.style.setProperty('--dsh-glass-bg-zone', v)
3531
3738
  applyInputCard()
3739
+ applyChromeText()
3740
+ applySessionText()
3741
+ fixDialogs()
3532
3742
  } finally {
3533
3743
  if (obs) obs.observe(document.documentElement, { attributes: true, attributeFilter: ['style', 'class'] })
3534
3744
  }
@@ -3547,6 +3757,8 @@ window.__ModuleLoader__.load({
3547
3757
  bgimg.style.backgroundSize = 'cover'
3548
3758
  bgimg.style.backgroundPosition = 'center'
3549
3759
  bgimg.style.backgroundRepeat = 'no-repeat'
3760
+ // 背景图模糊 = 背景图模糊(bgBlur) + 磨砂模糊强度(blur),两者叠加让背景图一起磨砂
3761
+ // 背景图模糊 = 仅背景图模糊(bgBlur);面板磨砂(blur)作用于弹窗/任务面板的 backdrop-filter
3550
3762
  const bblur = Math.max(0, Math.min(80, Number.isFinite(Number(c.bgBlur)) ? Number(c.bgBlur) : 0))
3551
3763
  bgimg.style.filter = bblur > 0 ? `blur(${bblur}px)` : 'none'
3552
3764
  const bop = Math.max(0, Math.min(1, Number.isFinite(Number(c.bgOpacity)) ? Number(c.bgOpacity) : 1))
@@ -3563,6 +3775,30 @@ window.__ModuleLoader__.load({
3563
3775
  // 记录当前主题应用函数,供唯一观察器/mq 调用
3564
3776
  window.__dshGlassApplyTheme = applyTheme
3565
3777
  centerScroll()
3778
+ // 侧边栏/顶栏 DOM 变化(会话列表更新等)时重算左栏/顶栏文字,防抖 120ms
3779
+ let chromeTimer = null
3780
+ const chromeRoots = [...document.querySelectorAll('#root [class*="sidebarCol"],#root [class*="wSkVaW_header"]')]
3781
+ const chromeObs = new MutationObserver(() => { if (chromeTimer) clearTimeout(chromeTimer); chromeTimer = setTimeout(applyChromeText, 120) })
3782
+ chromeRoots.forEach((r) => chromeObs.observe(r, { childList: true, subtree: true, characterData: true }))
3783
+ // 输入框卡片(uV2eYG_card/root)可能在启动后才挂载(会话/输入区加载晚),
3784
+ // 启动那一刻 applyInputCard 常找不到框 → 需刷新才生效。监听新增卡片节点时重算,防抖 100ms。
3785
+ let cardTimer = null
3786
+ const hasInputNode = (node) => (node.querySelector && (node.querySelector('[class*="uV2eYG_card"]') || node.querySelector('[class*="uV2eYG_root"]'))) || (node.matches && (node.matches('[class*="uV2eYG_card"]') || node.matches('[class*="uV2eYG_root"]')))
3787
+ const cardObs = new MutationObserver((muts) => {
3788
+ let hit = false
3789
+ for (const m of muts) { for (const node of m.addedNodes) { if (node && node.nodeType === 1 && hasInputNode(node)) { hit = true; break } } if (hit) break }
3790
+ if (hit) { if (cardTimer) clearTimeout(cardTimer); cardTimer = setTimeout(() => applyTheme(), 100) }
3791
+ })
3792
+ cardObs.observe(document.body, { childList: true, subtree: true })
3793
+ // 会话区内容随流式更新会持续变,重算会话区文字(防抖 150ms, 只监听新增/移除节点)
3794
+ let sessionTimer = null
3795
+ const inScroll = (node) => { let p = node; while (p && p !== document.documentElement) { if (p.matches && p.matches('[class*="wSkVaW_scrollBody"]')) return true; p = p.parentElement } return false }
3796
+ const sessionObs = new MutationObserver((muts) => {
3797
+ let hit = false
3798
+ for (const m of muts) { for (const node of m.addedNodes) { if (node.nodeType === 1 && inScroll(node)) { hit = true; break } } if (hit) break }
3799
+ if (hit) { if (sessionTimer) clearTimeout(sessionTimer); sessionTimer = setTimeout(() => { applySessionText(); fixDialogs() }, 150) }
3800
+ })
3801
+ sessionObs.observe(document.body, { childList: true, subtree: true })
3566
3802
  }
3567
3803
  return () => {
3568
3804
  window.__dshGlassApply = undefined
@@ -3571,6 +3807,12 @@ window.__ModuleLoader__.load({
3571
3807
  if (window.__dshGlassStyleObs) { window.__dshGlassStyleObs.disconnect(); window.__dshGlassStyleObs = null }
3572
3808
  if (themeMq) themeMq.removeEventListener('change', themeMqHandler)
3573
3809
  window.__dshGlassApplyTheme = undefined
3810
+ if (chromeObs) chromeObs.disconnect()
3811
+ if (chromeTimer) clearTimeout(chromeTimer)
3812
+ if (cardObs) cardObs.disconnect()
3813
+ if (cardTimer) clearTimeout(cardTimer)
3814
+ if (sessionObs) sessionObs.disconnect()
3815
+ if (sessionTimer) clearTimeout(sessionTimer)
3574
3816
  }
3575
3817
  }, 'dsh-long-plugins: glass applier')
3576
3818
  ctx.slots.inject('settings.section', () => ctx.slots.register({ name: 'settings.section', id: 'glass-ui', order: 40, label: 'RA-Span' }, GlassSettingsSection))
package/dsh.plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-long-plugins",
3
3
  "description": "Merged DSH web plugins: upload manager (drag-and-drop attach any file to the message), workspace output files, skill docs, account balance, Markdown-to-Word (md2docx), and polished file preview (Word & PowerPoint real preview with zoom/pan, rendered Markdown), plus an auto-repair install for DSH core patches (reverse-proxy WebSocket heartbeat, upgrade relink). | 一个插件整合 DSH Web 的常用增强:上传管理(拖放上传:拖任意文件到会话框直接附加)、工作区「输出文件」面板、技能文档浏览、账户余额显示、Markdown 转 Word(md2docx)、Word/PowerPoint 真实预览(可缩放、翻页);并自带修复安装,自动重连 DSH 核心补丁(反代 WebSocket 心跳、升级重连)。",
4
- "version": "2.2.1",
4
+ "version": "2.2.3",
5
5
  "entry": {
6
6
  "name": "dsh-long-plugins",
7
7
  "inject": [
package/lib/index.js CHANGED
@@ -1631,6 +1631,7 @@ window.addEventListener('resize',function(){ try{ if(fitMode) zoomFit(); else ap
1631
1631
  };
1632
1632
  const glassDir = resolve(homedir(), ".dsh-long-plugins");
1633
1633
  const glassFile = join(glassDir, "glass.json");
1634
+ const bgDir = join(glassDir, "backgrounds");
1634
1635
  async function readGlassJSON() {
1635
1636
  try { const raw = JSON.parse(await readFile(glassFile, "utf8")); return { ...GLASS_DEFAULTS, ...(raw && typeof raw === "object" ? raw : {}) }; }
1636
1637
  catch { return { ...GLASS_DEFAULTS }; }
@@ -1649,6 +1650,7 @@ window.addEventListener('resize',function(){ try{ if(fitMode) zoomFit(); else ap
1649
1650
  inputBox: cfg.inputBox || { light: { color: "", opacity: 1 }, dark: { color: "", opacity: 1 } },
1650
1651
  bgTint: cfg.bgTint || { light: { color: "#1a2332", mask: 0.28 }, dark: { color: "#1a2332", mask: 0.28 } },
1651
1652
  bgColor: cfg.bgColor || "#1a2332", bgMask: cfg.bgMask ?? 0.28,
1653
+ bgBlur: cfg.bgBlur ?? 0, bgOpacity: cfg.bgOpacity ?? 1,
1652
1654
  bgImage: cfg.bgImage ? { present: true, bytes: Math.round((cfg.bgImage.length * 3) / 4) } : { present: false },
1653
1655
  },
1654
1656
  bgImage: cfg.bgImage,
@@ -1661,6 +1663,14 @@ window.addEventListener('resize',function(){ try{ if(fitMode) zoomFit(); else ap
1661
1663
  const next = { ...current, ...sanitizeGlass(typeof body === "object" && body !== null ? body : {}) };
1662
1664
  await mkdir(glassDir, { recursive: true, mode: 0o700 });
1663
1665
  await writeFile(glassFile, JSON.stringify(next, null, 2), "utf8");
1666
+ // 背景图若有变更且是 data-URI, 存入 backgrounds/ 供"选择旧图"使用
1667
+ if (next.bgImage && current.bgImage !== next.bgImage && /^data:image\//.test(next.bgImage)) {
1668
+ try {
1669
+ await mkdir(bgDir, { recursive: true, mode: 0o700 });
1670
+ const name = Date.now() + ":" + Math.random().toString(36).slice(2, 8) + ".uri";
1671
+ await writeFile(join(bgDir, name), next.bgImage, "utf8");
1672
+ } catch (_) {}
1673
+ }
1664
1674
  sendJson(res, 200, { ok: true, saved: true });
1665
1675
  return;
1666
1676
  }
@@ -1669,8 +1679,26 @@ window.addEventListener('resize',function(){ try{ if(fitMode) zoomFit(); else ap
1669
1679
  sendError(res, error, onError);
1670
1680
  }
1671
1681
  };
1682
+ // 背景图历史(供设置面板"选择旧图"):GET 列出,GET ?name= 返回对应 data-URI
1683
+ const glassBackgrounds = async (req, res) => {
1684
+ try {
1685
+ requireTrusted(req);
1686
+ const url = new URL(req.url, "http://x");
1687
+ const name = url.searchParams.get("name");
1688
+ if (name) {
1689
+ if (!/^[\w.:-]+\.uri$/.test(name)) { sendError(res, new Error("bad name"), onError); return; }
1690
+ try { const buf = await readFile(join(bgDir, name)); res.writeHead(200, { "content-type": "text/plain; charset=utf-8", "cache-control": "no-store" }); res.end(buf); return; }
1691
+ catch { sendJson(res, 404, { error: "not found" }); return; }
1692
+ }
1693
+ let list = [];
1694
+ try { list = (await readdir(bgDir)).filter((n) => n.endsWith(".uri")).sort().reverse().slice(0, 30); } catch (_) {}
1695
+ sendJson(res, 200, { list });
1696
+ } catch (error) {
1697
+ sendError(res, error, onError);
1698
+ }
1699
+ };
1672
1700
 
1673
- return { root, maxFileBytes, totalMaxBytes, api, download, preview, workspaceList, workspaceFile, workspacePreview, workspaceBrowse, workspaceDelete, workspaceRename, workspaceSave, docxPreviewPage, docxPreviewAsset, pptxPreviewPage, xlsxPreviewPage, xlsxPreviewAsset, glassConfig };
1701
+ return { root, maxFileBytes, totalMaxBytes, api, download, preview, workspaceList, workspaceFile, workspacePreview, workspaceBrowse, workspaceDelete, workspaceRename, workspaceSave, docxPreviewPage, docxPreviewAsset, pptxPreviewPage, xlsxPreviewPage, xlsxPreviewAsset, glassConfig, glassBackgrounds };
1674
1702
  }
1675
1703
 
1676
1704
  /** 人类可读文件大小。 */
@@ -2575,6 +2603,12 @@ export async function apply(ctx, config = {}) {
2575
2603
  handler: handlers.glassConfig,
2576
2604
  }), "dsh-long-plugins: glass-ui config route");
2577
2605
 
2606
+ ctx.effect(() => ctx.webServer.register({
2607
+ kind: "exact",
2608
+ path: "/api/dsh-uploads/glass-backgrounds",
2609
+ handler: handlers.glassBackgrounds,
2610
+ }), "dsh-long-plugins: glass-ui backgrounds route");
2611
+
2578
2612
  ctx.effect(() => ctx.webServer.register({
2579
2613
  kind: "exact",
2580
2614
  path: "/api/dsh-uploads/workspace-browse",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-long-plugins",
3
- "version": "2.2.1",
3
+ "version": "2.2.3",
4
4
  "description": "Merged DSH web plugins: upload manager (drag-and-drop attach any file to the message), workspace output files, skill docs, account balance, Markdown-to-Word (md2docx), and polished file preview (Word & PowerPoint real preview with zoom/pan, rendered Markdown), plus an auto-repair install for DSH core patches (reverse-proxy WebSocket heartbeat, upgrade relink). | 一个插件整合 DSH Web 的常用增强:上传管理(拖放上传:拖任意文件到会话框直接附加)、工作区「输出文件」面板、技能文档浏览、账户余额显示、Markdown 转 Word(md2docx)、Word/PowerPoint 真实预览(可缩放、翻页);并自带修复安装,自动重连 DSH 核心补丁(反代 WebSocket 心跳、升级重连)。",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -73,6 +73,34 @@ cd "$DSH_HOME/profiles/web" && pnpm install
73
73
  4. 提问/审批窗口:弹出一个问题,等 60s+ 不再自己消失(心跳补丁生效)。
74
74
  5. 插件设置面板「上传文件」「输出文件」「技能文档」都在。
75
75
  6. `grep -q WEBSOCKET_HEARTBEAT_MS <核心解压路径>/dsh-client-connection/lib/index.js` → 心跳在位。
76
+ 7. **RA-Span 玻璃观感**:左栏/顶栏文字图标、会话区过程内容配色、输入框、提问卡、耗时统计等是否仍正常(见下文"RA-Span 升级后需重新校准")。
77
+
78
+ ## RA-Span(统一桌面/玻璃)升级后需重新校准 ⚠️
79
+
80
+ dsh-long-plugins 的「RA-Span(统一桌面/玻璃)」**大量依赖 DSH 内部混淆类名**(`o3BgMG_*`/`CY-8Ka_*`/`QWLzLg_*`/`M8wy4a_*`/`uV2eYG_*`/`wSkVaW_*`/`osXY9a_*`/`p-xYUq_*` 等)与 DSH 内部 DOM 结构。DSH 升级重打包后**这些类名前缀会变**,导致插件里靠类名压样式的规则/JS **静默失效**(不报错,只是观感/配色不对)。
81
+
82
+ 升级流程会**同步插件代码(逻辑保住)**,但**不会自动重抓新的类名**,所以升级后必须**人工复核**玻璃观感。
83
+
84
+ ### 升级后重点复核 + 处理
85
+ 1. 浏览器强刷(Ctrl/Cmd+Shift+R)。
86
+ 2. 逐项看 RA-Span 是否仍正常:
87
+ - 左栏/顶栏文字与图标(白字+深影、品牌区不被白化)
88
+ - 会话区过程内容配色(Bash 绿 / Read 蓝 / Edit 橙 / Write 紫 / Think 琥珀、错误红、工具输出水绿)
89
+ - 状态/统计行余额(走 `data-slot-conversation="composer.dock"` 的相对稳)
90
+ - 输入框(无重影、背景/文字随主题、可调色)
91
+ - 「新会话」按钮去白底
92
+ 3. 若某项观感/配色不对 → **新类名变了**。让用户在对应元素「右键 → 检查」,把高亮元素及父级的 class 发我,把 `client/client.js` 里对应的 `[class*="..."]` 前缀更新成新类名(只改选择器,不动逻辑)。
93
+ 4. 稳定项无需动:用 `data-slot-*` 的(状态栏)、注入 `<head>` 的 CSS、`data-dsh-theme` 属性。
94
+
95
+ ### 各依赖类名的功能清单(升级后最可能失效)
96
+ | 功能 | 依赖类名 | 失效表现 |
97
+ |---|---|---|
98
+ | 工具名配色(Bash/Read/Edit/Write) | `o3BgMG_title` / `CY-8Ka_title` | 标签不变色 |
99
+ | Think 配色 | `QWLzLg_*` | Think 不琥珀 |
100
+ | 提问/弹窗不透明 | `M8wy4a_card` | 弹窗透明重叠 |
101
+ | 输入框 | `uV2eYG_*` | 重影/背景不随主题 |
102
+ | 耗时统计 | `osXY9a_*` / `p-xYUq_*` | 统计字淡/重影 |
103
+ | 左栏「新会话」 | `newSession` | 白底回来 |
76
104
 
77
105
  ## 硬性安全边界(必须遵守)
78
106
  - **不主动 push / 打 tag / 发 Release**;需要发布版本时停下用 ask_user_question 等确认。
@@ -86,4 +114,5 @@ cd "$DSH_HOME/profiles/web" && pnpm install
86
114
  | 升级后设置页"未暴露命名空间" | 补丁2 被覆盖 | 重跑 apply-dsh-patches.sh |
87
115
  | 升级后提问窗口自己消失 | 心跳补丁(补丁3)被覆盖 | 重跑 apply-dsh-patches.sh 或装插件补丁脚本 |
88
116
  | 升级后插件不显示 | 插件安装副本未同步 | 步骤 5 `pnpm install` |
117
+ | 升级后 RA-Span 观感/配色不对(输入框重影、标签不变色、弹窗透明、统计字淡等) | DSH 混淆类名变了 | 见 RA-Span 校准节:强刷 + 逐项检查,让用户右键给出新类名,更新 `client/client.js` 对应选择器 |
89
118
  | 按钮全没了/界面异常 | 浏览器缓存了旧前端 | 强刷 Ctrl/Cmd+Shift+R |