dsh-long-plugins 2.2.2 → 2.3.0
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 +116 -57
- package/dsh.plugin.json +1 -1
- package/lib/index.js +102 -5
- package/package.json +1 -1
package/client/client.js
CHANGED
|
@@ -1961,7 +1961,9 @@ window.__ModuleLoader__.load({
|
|
|
1961
1961
|
if (!cancelled) setBalance(null);
|
|
1962
1962
|
}
|
|
1963
1963
|
};
|
|
1964
|
-
|
|
1964
|
+
// 首次拉取延迟到浏览器空闲(减轻首屏网络风暴), 60s 轮询不变
|
|
1965
|
+
const schedule = () => { try { if (window.requestIdleCallback) window.requestIdleCallback(() => load(), { timeout: 300 }); else setTimeout(load, 300) } catch (_) { load() } };
|
|
1966
|
+
schedule();
|
|
1965
1967
|
const interval = setInterval(load, 60000);
|
|
1966
1968
|
return () => {
|
|
1967
1969
|
cancelled = true;
|
|
@@ -2003,7 +2005,9 @@ window.__ModuleLoader__.load({
|
|
|
2003
2005
|
if (!cancelled) setCost(null);
|
|
2004
2006
|
}
|
|
2005
2007
|
};
|
|
2006
|
-
|
|
2008
|
+
// 首次拉取延迟到空闲(减轻首屏网络风暴), 60s 轮询不变
|
|
2009
|
+
const schedule = () => { try { if (window.requestIdleCallback) window.requestIdleCallback(() => load(), { timeout: 300 }); else setTimeout(load, 300) } catch (_) { load() } };
|
|
2010
|
+
schedule();
|
|
2007
2011
|
const interval = setInterval(load, 60000);
|
|
2008
2012
|
return () => {
|
|
2009
2013
|
cancelled = true;
|
|
@@ -3133,24 +3137,51 @@ window.__ModuleLoader__.load({
|
|
|
3133
3137
|
color: #ffffff !important;
|
|
3134
3138
|
text-shadow: 0 1px 1px rgba(0,0,0,.5);
|
|
3135
3139
|
}
|
|
3140
|
+
/* (移除 color/text-shadow 过渡: 会在收尾重渲染时短暂贴默认色导致"闪黑") */
|
|
3136
3141
|
/* DSH 弹窗/浮层(提问/确认/重命名等):给不透明底+主题文字, 杜绝透明与会话内容重叠 */
|
|
3137
3142
|
html[data-dsh-glass="on"] [class*="Radix"] [class*="Content"],
|
|
3138
3143
|
html[data-dsh-glass="on"] [class*="DialogContent"],
|
|
3139
3144
|
html[data-dsh-glass="on"] [class*="ModalContent"],
|
|
3140
3145
|
html[data-dsh-glass="on"] [class*="overlay"] [class*="content"],
|
|
3141
3146
|
html[data-dsh-glass="on"] [class*="_content"][class*="dialog"] {
|
|
3142
|
-
background-color: var(--dsw-specific-input-major, #e8edf3) !important;
|
|
3147
|
+
background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 76%, transparent) !important;
|
|
3143
3148
|
color: var(--dsw-alias-label-primary) !important;
|
|
3144
|
-
backdrop-filter: blur(
|
|
3145
|
-
-webkit-backdrop-filter: blur(
|
|
3149
|
+
backdrop-filter: blur(var(--dsh-glass-blur, 16px)) !important;
|
|
3150
|
+
-webkit-backdrop-filter: blur(var(--dsh-glass-blur, 16px)) !important;
|
|
3146
3151
|
}
|
|
3147
|
-
/* DSH 提问/弹窗卡(M8wy4a_card
|
|
3148
|
-
|
|
3149
|
-
html[data-dsh-glass="on"] [
|
|
3150
|
-
background-color: var(--dsw-specific-input-major, #e8edf3) !important;
|
|
3152
|
+
/* DSH 提问/弹窗卡(M8wy4a_card):不透明底+原生文字, 避免透明重叠、白字。
|
|
3153
|
+
注意: 不能再用 [data-chain-overlay-fallback] [class*=card](会误匹配输入框 uV2eYG_card 加 blur 造成重影) */
|
|
3154
|
+
html[data-dsh-glass="on"] [class*="M8wy4a_card"] {
|
|
3155
|
+
background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 76%, transparent) !important;
|
|
3156
|
+
color: var(--dsw-alias-label-primary) !important;
|
|
3157
|
+
backdrop-filter: blur(var(--dsh-glass-blur, 16px)) !important;
|
|
3158
|
+
-webkit-backdrop-filter: blur(var(--dsh-glass-blur, 16px)) !important;
|
|
3159
|
+
}
|
|
3160
|
+
/* 任务/todo 面板(lXshSW_root, 在 conversation.input.dock 内):不透明底+可读文字, 不透明与会话重叠 */
|
|
3161
|
+
html[data-dsh-glass="on"] [class*="lXshSW_root"],
|
|
3162
|
+
html[data-dsh-glass="on"] [class*="lXshSW_body"] {
|
|
3163
|
+
background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 76%, transparent) !important;
|
|
3164
|
+
color: var(--dsw-alias-label-primary) !important;
|
|
3165
|
+
backdrop-filter: blur(var(--dsh-glass-blur, 16px)) !important;
|
|
3166
|
+
-webkit-backdrop-filter: blur(var(--dsh-glass-blur, 16px)) !important;
|
|
3167
|
+
}
|
|
3168
|
+
html[data-dsh-glass="on"] [class*="lXshSW_header"] [class*="lXshSW_title"],
|
|
3169
|
+
html[data-dsh-glass="on"] [class*="lXshSW_header"] [class*="lXshSW_progress"],
|
|
3170
|
+
html[data-dsh-glass="on"] [class*="lXshSW_item"] {
|
|
3171
|
+
color: var(--dsw-alias-label-primary) !important;
|
|
3172
|
+
}
|
|
3173
|
+
/* 排队消息/等待任务面板(_7yHdaG_dock/panel, 在 conversation.input.dock 内):不透明/磨砂底+可读文字 */
|
|
3174
|
+
html[data-dsh-glass="on"] [class*="_7yHdaG_panel"],
|
|
3175
|
+
html[data-dsh-glass="on"] [class*="_7yHdaG_dock"] {
|
|
3176
|
+
background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 88%, transparent) !important;
|
|
3177
|
+
color: var(--dsw-alias-label-primary) !important;
|
|
3178
|
+
backdrop-filter: blur(var(--dsh-glass-blur, 16px)) !important;
|
|
3179
|
+
-webkit-backdrop-filter: blur(var(--dsh-glass-blur, 16px)) !important;
|
|
3180
|
+
}
|
|
3181
|
+
html[data-dsh-glass="on"] [class*="_7yHdaG_header"],
|
|
3182
|
+
html[data-dsh-glass="on"] [class*="_7yHdaG_preview"],
|
|
3183
|
+
html[data-dsh-glass="on"] [class*="_7yHdaG_row"] {
|
|
3151
3184
|
color: var(--dsw-alias-label-primary) !important;
|
|
3152
|
-
backdrop-filter: blur(8px) !important;
|
|
3153
|
-
-webkit-backdrop-filter: blur(8px) !important;
|
|
3154
3185
|
}
|
|
3155
3186
|
/* 轮次/历史提问预览:默认隐藏(点开才显示),并去掉头部底色,避免底部露出白条 */
|
|
3156
3187
|
.dsh-turn-preview:not(.open){display:none !important;}
|
|
@@ -3206,23 +3237,32 @@ window.__ModuleLoader__.load({
|
|
|
3206
3237
|
}
|
|
3207
3238
|
/* 输入框下拉/悬浮菜单(如 / 命令、@ 提及):背景给不透明随主题色,避免叠在会话磨砂卡上重叠看不清 */
|
|
3208
3239
|
html[data-dsh-glass="on"] #root [class*="_menu"][class*="_menu"][class*="_menu"][class*="_menu"] {
|
|
3209
|
-
background-color: var(--dsw-specific-input-major, #e8edf3) !important;
|
|
3240
|
+
background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 76%, transparent) !important;
|
|
3210
3241
|
background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 97%, transparent) !important;
|
|
3211
3242
|
backdrop-filter: none !important;
|
|
3212
3243
|
-webkit-backdrop-filter: none !important;
|
|
3213
3244
|
}
|
|
3214
|
-
/* 输入框旁权限/功能选择下拉(盾牌 Full access 等
|
|
3215
|
-
html[data-dsh-glass="on"] #root [class*="uV2eYG_card"] [class*="_root_"]
|
|
3245
|
+
/* 输入框旁权限/功能选择下拉(盾牌 Full access 等):给不透明底+可读文字(高优先级, 压过 DSH 透明覆盖) */
|
|
3246
|
+
html[data-dsh-glass="on"] #root [class*="uV2eYG_card"] [class*="_root_"] [class*="_list_"],
|
|
3247
|
+
html[data-dsh-glass="on"] #root [class*="uV2eYG_card"] [class*="_root_"] [class*="_list_"] [class*="_viewport_"],
|
|
3248
|
+
html[data-dsh-glass="on"] #root [class*="uV2eYG_card"] [class*="_list_"] [class*="_viewport_"] [class*="_itemWrap_"] {
|
|
3216
3249
|
background-color: var(--dsw-specific-input-major, #e8edf3) !important;
|
|
3217
|
-
background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 97%, transparent) !important;
|
|
3218
3250
|
backdrop-filter: none !important;
|
|
3219
3251
|
-webkit-backdrop-filter: none !important;
|
|
3220
3252
|
}
|
|
3221
|
-
html[data-dsh-glass="on"] #root [class*="uV2eYG_card"] [class*="
|
|
3253
|
+
html[data-dsh-glass="on"] #root [class*="uV2eYG_card"] [class*="_item_"],
|
|
3254
|
+
html[data-dsh-glass="on"] #root [class*="uV2eYG_card"] [class*="_itemLabel_"] {
|
|
3255
|
+
color: var(--dsw-alias-label-primary) !important;
|
|
3256
|
+
}
|
|
3257
|
+
/* 兜底: Radix 风格菜单/下拉(可能被渲染到 body 顶层) */
|
|
3258
|
+
html[data-dsh-glass="on"] [class*="Radix"] [class*="Menu"],
|
|
3259
|
+
html[data-dsh-glass="on"] [class*="radix-menu"],
|
|
3260
|
+
html[data-dsh-glass="on"] [class*="menuitem"],
|
|
3261
|
+
html[data-dsh-glass="on"] [role="menu"] {
|
|
3222
3262
|
background-color: var(--dsw-specific-input-major, #e8edf3) !important;
|
|
3223
|
-
background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 97%, transparent) !important;
|
|
3224
3263
|
backdrop-filter: none !important;
|
|
3225
3264
|
-webkit-backdrop-filter: none !important;
|
|
3265
|
+
color: var(--dsw-alias-label-primary) !important;
|
|
3226
3266
|
}
|
|
3227
3267
|
/* 玻璃开启 + 浅色系统主题:加深弱化标签/元信息文字,避免浅灰字叠在浅磨砂卡上看不清 */
|
|
3228
3268
|
@media (prefers-color-scheme: light) {
|
|
@@ -3307,14 +3347,16 @@ window.__ModuleLoader__.load({
|
|
|
3307
3347
|
)
|
|
3308
3348
|
}
|
|
3309
3349
|
const GlassSettingsSection = () => {
|
|
3310
|
-
const [state, setState] = React.useState({ loading: true, saving: false, error: '', cfg: null, bgImage: '' })
|
|
3350
|
+
const [state, setState] = React.useState({ loading: true, saving: false, error: '', cfg: null, bgImage: '', bgList: [] })
|
|
3311
3351
|
const load = React.useCallback(async () => {
|
|
3312
3352
|
setState((s) => ({ ...s, loading: true, error: '' }))
|
|
3313
3353
|
try {
|
|
3314
3354
|
const r = await fetch('/api/dsh-uploads/glass-config', { cache: 'no-store' })
|
|
3315
3355
|
const b = await r.json()
|
|
3316
3356
|
if (!r.ok) throw new Error(b.error || ('HTTP ' + r.status))
|
|
3317
|
-
|
|
3357
|
+
let list = []
|
|
3358
|
+
try { const rb = await fetch('/api/dsh-uploads/glass-backgrounds', { cache: 'no-store' }); const jb = await rb.json(); list = jb.list || [] } catch (_) {}
|
|
3359
|
+
setState((s) => ({ ...s, loading: false, cfg: b.cfg, bgImage: b.bgImageUrl || '', bgList: list }))
|
|
3318
3360
|
} catch (e) { setState((s) => ({ ...s, loading: false, error: glassText(e) })) }
|
|
3319
3361
|
}, [])
|
|
3320
3362
|
React.useEffect(() => { load(); }, [load])
|
|
@@ -3337,9 +3379,16 @@ window.__ModuleLoader__.load({
|
|
|
3337
3379
|
try {
|
|
3338
3380
|
const cfg = state.cfg || {}
|
|
3339
3381
|
const num = (v, fb) => ((typeof v === 'number' && Number.isFinite(v) && v >= 0) ? v : fb)
|
|
3382
|
+
// 背景图: 仅当用户上传了新图(data-URI)才传; 未改(URL)省略以保持引用; 清空传 '' 清除
|
|
3383
|
+
const sBg = state.bgImage
|
|
3384
|
+
let bgImageOut
|
|
3385
|
+
if (sBg === '') bgImageOut = ''
|
|
3386
|
+
else if (typeof sBg === 'string' && sBg.startsWith('data:image/')) bgImageOut = sBg
|
|
3387
|
+
else bgImageOut = undefined
|
|
3340
3388
|
const payload = {
|
|
3341
3389
|
enabled: !!cfg.enabled, blur: num(cfg.blur, 0),
|
|
3342
|
-
|
|
3390
|
+
bgMask: num(cfg.bgMask, 0.28), bgColor: cfg.bgColor || '#1a2332', bgBlur: num(cfg.bgBlur, 0), bgOpacity: num(cfg.bgOpacity, 1),
|
|
3391
|
+
...(bgImageOut !== undefined ? { bgImage: bgImageOut } : {}),
|
|
3343
3392
|
bgTint: {
|
|
3344
3393
|
light: { color: (cfg.bgTint?.light?.color) || '#1a2332', mask: num(cfg.bgTint?.light?.mask, 0.28) },
|
|
3345
3394
|
dark: { color: (cfg.bgTint?.dark?.color) || '#1a2332', mask: num(cfg.bgTint?.dark?.mask, 0.28) },
|
|
@@ -3356,8 +3405,9 @@ window.__ModuleLoader__.load({
|
|
|
3356
3405
|
const r = await fetch('/api/dsh-uploads/glass-config', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload) })
|
|
3357
3406
|
const b = await r.json()
|
|
3358
3407
|
if (!r.ok) throw new Error(b.error || ('HTTP ' + r.status))
|
|
3359
|
-
|
|
3360
|
-
|
|
3408
|
+
const applyBg = (payload.bgImage && payload.bgImage.startsWith('data:')) ? payload.bgImage : ((state.bgImage && state.bgImage.startsWith('/')) ? state.bgImage : '')
|
|
3409
|
+
window.__dshGlassApply && window.__dshGlassApply({ ...payload, bgImage: applyBg })
|
|
3410
|
+
setState((s) => ({ ...s, saving: false, cfg: { ...(s.cfg || {}), ...payload }, bgImage: applyBg, error: '' }))
|
|
3361
3411
|
} catch (e) { setState((s) => ({ ...s, saving: false, error: glassText(e) })) }
|
|
3362
3412
|
}, [state])
|
|
3363
3413
|
const cfg = state.cfg || {}
|
|
@@ -3370,13 +3420,12 @@ window.__ModuleLoader__.load({
|
|
|
3370
3420
|
state.error ? React.createElement('div', { className: 'dsh-glass-error' }, state.error) : null,
|
|
3371
3421
|
React.createElement('div', { className: 'dsh-glass-card' },
|
|
3372
3422
|
React.createElement('div', { className: 'dsh-glass-row' },
|
|
3373
|
-
React.createElement('label', null, '
|
|
3374
|
-
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) }) }),
|
|
3375
|
-
React.createElement('span', { className: 'dsh-glass-val' }, `${Number(cfg.blur ?? 20)} px`),
|
|
3376
|
-
),
|
|
3377
|
-
React.createElement('div', { className: 'dsh-glass-row' },
|
|
3378
|
-
React.createElement('label', null, '共用背景图'),
|
|
3423
|
+
React.createElement('label', null, '背景图'),
|
|
3379
3424
|
React.createElement('div', { className: 'dsh-glass-bg' }, glassImageInput(state.bgImage, (v) => setState((s) => ({ ...s, bgImage: v })))),
|
|
3425
|
+
state.bgList && state.bgList.length ? React.createElement('select', { className: 'dsh-glass-file', value: '', style: { marginLeft: 8 }, onChange: async (e) => {
|
|
3426
|
+
const name = e.target.value; if (!name) return
|
|
3427
|
+
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 (_) {}
|
|
3428
|
+
} }, React.createElement('option', { value: '' }, '选择旧图…'), state.bgList.map((n, i) => React.createElement('option', { key: n, value: n }, '背景图 ' + (i + 1))) ) : null,
|
|
3380
3429
|
),
|
|
3381
3430
|
React.createElement('div', { className: 'dsh-glass-card' },
|
|
3382
3431
|
React.createElement('div', { className: 'dsh-glass-zone-title' }, '背景罩'),
|
|
@@ -3403,9 +3452,18 @@ window.__ModuleLoader__.load({
|
|
|
3403
3452
|
React.createElement('span', { className: 'dsh-glass-val' }, `${Number(cfg.bgBlur ?? 0)} px`),
|
|
3404
3453
|
),
|
|
3405
3454
|
React.createElement('div', { className: 'dsh-glass-row' },
|
|
3406
|
-
React.createElement('label', null, '
|
|
3407
|
-
React.createElement('input', { type: 'range', className: 'dsh-glass-range', min: 0, max:
|
|
3408
|
-
React.createElement('span', { className: 'dsh-glass-val' }, `${Math.round((Number(cfg.bgOpacity ?? 1)) * 100)}%`),
|
|
3455
|
+
React.createElement('label', null, '背景透明度'),
|
|
3456
|
+
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 }) }),
|
|
3457
|
+
React.createElement('span', { className: 'dsh-glass-val' }, `${Math.round((1 - (Number(cfg.bgOpacity ?? 1))) * 100)}%`),
|
|
3458
|
+
),
|
|
3459
|
+
React.createElement('div', { className: 'dsh-glass-card' },
|
|
3460
|
+
React.createElement('div', { className: 'dsh-glass-zone-title' }, '面板'),
|
|
3461
|
+
React.createElement('div', { className: 'dsh-glass-row' },
|
|
3462
|
+
React.createElement('label', null, '面板磨砂'),
|
|
3463
|
+
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) }) }),
|
|
3464
|
+
React.createElement('span', { className: 'dsh-glass-val' }, `${Number(cfg.blur ?? 20)} px`),
|
|
3465
|
+
),
|
|
3466
|
+
React.createElement('p', { className: 'dsh-glass-note' }, '作用于弹窗/任务卡等面板的磨砂玻璃强度(0 = 关闭磨砂)。'),
|
|
3409
3467
|
),
|
|
3410
3468
|
React.createElement('div', { className: 'dsh-glass-card' },
|
|
3411
3469
|
React.createElement('div', { className: 'dsh-glass-zone-title' }, '输入框'),
|
|
@@ -3419,13 +3477,13 @@ window.__ModuleLoader__.load({
|
|
|
3419
3477
|
React.createElement('button', { type: 'button', style: { marginLeft: 8, fontSize: 12 }, onClick: () => patchInput(t, { color: '', opacity: 1 }) }, '原生'),
|
|
3420
3478
|
),
|
|
3421
3479
|
React.createElement('div', { className: 'dsh-glass-row' },
|
|
3422
|
-
React.createElement('label', null, '
|
|
3423
|
-
React.createElement('input', { type: 'range', className: 'dsh-glass-range', min: 0, max:
|
|
3424
|
-
React.createElement('span', { className: 'dsh-glass-val' }, `${Math.round((typeof tb.opacity === 'number' ? tb.opacity : 1) * 100)}%`),
|
|
3480
|
+
React.createElement('label', null, '透明度'),
|
|
3481
|
+
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 }) }),
|
|
3482
|
+
React.createElement('span', { className: 'dsh-glass-val' }, `${Math.round((1 - (typeof tb.opacity === 'number' ? tb.opacity : 1)) * 100)}%`),
|
|
3425
3483
|
),
|
|
3426
3484
|
)
|
|
3427
3485
|
}),
|
|
3428
|
-
React.createElement('p', { className: 'dsh-glass-note' }, '浅色主题与深色主题可分别设置。100% =
|
|
3486
|
+
React.createElement('p', { className: 'dsh-glass-note' }, '浅色主题与深色主题可分别设置。0% = 全不透明,100% = 全透明;"原生" = 跟随该主题的 DSH 原生色。文字颜色按背景亮度自动配深/浅,保证可读。'),
|
|
3429
3487
|
),
|
|
3430
3488
|
React.createElement('p', { className: 'dsh-glass-note' }, '提示:背景图铺满整页(可加背景图罩);背景图模糊/透明度单独可调;会话区使用自己的罩色、罩强度与透明度;左栏/顶部透出背景图;输入框颜色/不透明度可单独设置。'),
|
|
3431
3489
|
React.createElement('div', { className: 'dsh-glass-actions' },
|
|
@@ -3563,18 +3621,15 @@ window.__ModuleLoader__.load({
|
|
|
3563
3621
|
if (isInSkip(root)) return
|
|
3564
3622
|
root.querySelectorAll('div,span,button,a,label,p,li,h1,h2,h3,h4').forEach((el) => {
|
|
3565
3623
|
if (isInSkip(el)) return
|
|
3566
|
-
|
|
3567
|
-
const hasText = el.textContent && el.textContent.trim()
|
|
3568
|
-
if (!hasText) return
|
|
3624
|
+
if (el.dataset && el.dataset.dshgl) return // 已处理过, 跳过
|
|
3569
3625
|
const ownText = Array.from(el.childNodes).some((n) => n.nodeType === 3 && n.textContent.trim())
|
|
3570
|
-
const textLike = /^(BUTTON|A|SPAN|LABEL|LI|P|H1|H2|H3|H4)$/.test(
|
|
3626
|
+
const textLike = /^(BUTTON|A|SPAN|LABEL|LI|P|H1|H2|H3|H4)$/.test(el.tagName)
|
|
3571
3627
|
if (!textLike && !ownText) return
|
|
3572
|
-
// 跳过有盒子背景的元素(
|
|
3573
|
-
|
|
3574
|
-
const bgbg = getComputedStyle(el).backgroundColor
|
|
3575
|
-
if (bgbg && bgbg !== 'rgba(0, 0, 0, 0)' && bgbg !== 'transparent') return
|
|
3628
|
+
// 跳过有盒子背景的元素(用类名判断, 不再 getComputedStyle 以免每元素强制样式重算导致卡顿)
|
|
3629
|
+
if (/bubble|badge|chip|tag|pill|code-block|md-code|n_block|n_banner|n_plain|button/i.test(el.className)) return
|
|
3576
3630
|
el.style.setProperty('color', '#ffffff', 'important')
|
|
3577
3631
|
el.style.setProperty('text-shadow', '0 1px 1px rgba(0,0,0,.5)')
|
|
3632
|
+
if (el.dataset) el.dataset.dshgl = '1'
|
|
3578
3633
|
})
|
|
3579
3634
|
// 图标也变白(随 currentColor),用于工作区文件夹等单色图标;保留显式品牌色
|
|
3580
3635
|
root.querySelectorAll('svg, [class*="icon"], [class*="Icon"]').forEach((el) => {
|
|
@@ -3599,15 +3654,14 @@ window.__ModuleLoader__.load({
|
|
|
3599
3654
|
if (!scroll) return
|
|
3600
3655
|
scroll.querySelectorAll('div,span,p,li,pre,code,button,a,label,h1,h2,h3,h4').forEach((el) => {
|
|
3601
3656
|
if (isInSkip(el)) return
|
|
3602
|
-
|
|
3603
|
-
if (!hasText) return
|
|
3657
|
+
if (el.dataset && el.dataset.dshgl) return // 已处理过, 跳过(只处理新内容)
|
|
3604
3658
|
const ownText = Array.from(el.childNodes).some((n) => n.nodeType === 3 && n.textContent.trim())
|
|
3605
3659
|
const textLike = /^(BUTTON|A|SPAN|LABEL|LI|P|H1|H2|H3|H4|PRE|CODE)$/.test(el.tagName)
|
|
3606
3660
|
if (!textLike && !ownText) return
|
|
3607
|
-
|
|
3608
|
-
if (bgbg && bgbg !== 'rgba(0, 0, 0, 0)' && bgbg !== 'transparent') return
|
|
3661
|
+
if (/bubble|badge|chip|tag|pill|code-block|md-code|n_block|n_banner|n_plain/i.test(el.className)) return
|
|
3609
3662
|
el.style.setProperty('color', '#ffffff', 'important')
|
|
3610
3663
|
el.style.setProperty('text-shadow', '0 1px 1px rgba(0,0,0,.5)')
|
|
3664
|
+
if (el.dataset) el.dataset.dshgl = '1'
|
|
3611
3665
|
})
|
|
3612
3666
|
// 在白字底上, 给过程内容配色区分, Error 用红色。
|
|
3613
3667
|
// 文字常在标签的子元素里(白字已把它设白), 故连同文本子元素一起上色。
|
|
@@ -3695,13 +3749,16 @@ window.__ModuleLoader__.load({
|
|
|
3695
3749
|
const fg = dark ? '#f2f6fa' : '#1a2332'
|
|
3696
3750
|
// 只匹配提问/弹窗卡(M8wy4a_card),避免误匹配输入框 uV2eYG_card(含"card")导致 blur 重影
|
|
3697
3751
|
document.querySelectorAll('[class*="M8wy4a_card"]').forEach((el) => {
|
|
3698
|
-
|
|
3752
|
+
const rgb = bg
|
|
3753
|
+
el.style.setProperty('background-color', `color-mix(in srgb, ${rgb} 76%, transparent)`, 'important')
|
|
3699
3754
|
el.style.setProperty('color', fg, 'important')
|
|
3700
|
-
el.style.setProperty('backdrop-filter', 'blur(
|
|
3755
|
+
el.style.setProperty('backdrop-filter', 'blur(var(--dsh-glass-blur, 20px))')
|
|
3701
3756
|
})
|
|
3702
3757
|
}
|
|
3703
3758
|
// 主题相关的东西统一重算:背景罩 + 输入框。切主题时由 observer/mq 触发。
|
|
3704
3759
|
// 写入前先断开观察器,写完再重连,避免自触发造成死循环。
|
|
3760
|
+
// 把非关键的"内容配色/白字"延迟到浏览器空闲时执行, 让页面先渲染出来(优化刷新加载时间)
|
|
3761
|
+
const defer = (fn) => { try { if (window.requestIdleCallback) window.requestIdleCallback(fn, { timeout: 120 }); else setTimeout(fn, 0) } catch (_) { fn() } }
|
|
3705
3762
|
const applyTheme = () => {
|
|
3706
3763
|
const obs = window.__dshGlassStyleObs
|
|
3707
3764
|
if (obs) obs.disconnect()
|
|
@@ -3712,9 +3769,7 @@ window.__ModuleLoader__.load({
|
|
|
3712
3769
|
const v = `rgba(${hexToRgb(bt.color)},${bt.mask})`
|
|
3713
3770
|
root.style.setProperty('--dsh-glass-bg-zone', v)
|
|
3714
3771
|
applyInputCard()
|
|
3715
|
-
applyChromeText()
|
|
3716
|
-
applySessionText()
|
|
3717
|
-
fixDialogs()
|
|
3772
|
+
defer(() => { applyChromeText(); applySessionText(); fixDialogs() })
|
|
3718
3773
|
} finally {
|
|
3719
3774
|
if (obs) obs.observe(document.documentElement, { attributes: true, attributeFilter: ['style', 'class'] })
|
|
3720
3775
|
}
|
|
@@ -3733,6 +3788,8 @@ window.__ModuleLoader__.load({
|
|
|
3733
3788
|
bgimg.style.backgroundSize = 'cover'
|
|
3734
3789
|
bgimg.style.backgroundPosition = 'center'
|
|
3735
3790
|
bgimg.style.backgroundRepeat = 'no-repeat'
|
|
3791
|
+
// 背景图模糊 = 背景图模糊(bgBlur) + 磨砂模糊强度(blur),两者叠加让背景图一起磨砂
|
|
3792
|
+
// 背景图模糊 = 仅背景图模糊(bgBlur);面板磨砂(blur)作用于弹窗/任务面板的 backdrop-filter
|
|
3736
3793
|
const bblur = Math.max(0, Math.min(80, Number.isFinite(Number(c.bgBlur)) ? Number(c.bgBlur) : 0))
|
|
3737
3794
|
bgimg.style.filter = bblur > 0 ? `blur(${bblur}px)` : 'none'
|
|
3738
3795
|
const bop = Math.max(0, Math.min(1, Number.isFinite(Number(c.bgOpacity)) ? Number(c.bgOpacity) : 1))
|
|
@@ -3752,7 +3809,7 @@ window.__ModuleLoader__.load({
|
|
|
3752
3809
|
// 侧边栏/顶栏 DOM 变化(会话列表更新等)时重算左栏/顶栏文字,防抖 120ms
|
|
3753
3810
|
let chromeTimer = null
|
|
3754
3811
|
const chromeRoots = [...document.querySelectorAll('#root [class*="sidebarCol"],#root [class*="wSkVaW_header"]')]
|
|
3755
|
-
const chromeObs = new MutationObserver(() => { if (chromeTimer) clearTimeout(chromeTimer); chromeTimer = setTimeout(applyChromeText,
|
|
3812
|
+
const chromeObs = new MutationObserver(() => { if (chromeTimer) clearTimeout(chromeTimer); chromeTimer = setTimeout(applyChromeText, 30) })
|
|
3756
3813
|
chromeRoots.forEach((r) => chromeObs.observe(r, { childList: true, subtree: true, characterData: true }))
|
|
3757
3814
|
// 输入框卡片(uV2eYG_card/root)可能在启动后才挂载(会话/输入区加载晚),
|
|
3758
3815
|
// 启动那一刻 applyInputCard 常找不到框 → 需刷新才生效。监听新增卡片节点时重算,防抖 100ms。
|
|
@@ -3770,7 +3827,7 @@ window.__ModuleLoader__.load({
|
|
|
3770
3827
|
const sessionObs = new MutationObserver((muts) => {
|
|
3771
3828
|
let hit = false
|
|
3772
3829
|
for (const m of muts) { for (const node of m.addedNodes) { if (node.nodeType === 1 && inScroll(node)) { hit = true; break } } if (hit) break }
|
|
3773
|
-
if (hit) { if (sessionTimer) clearTimeout(sessionTimer); sessionTimer = setTimeout(() => { applySessionText(); fixDialogs() },
|
|
3830
|
+
if (hit) { if (sessionTimer) clearTimeout(sessionTimer); sessionTimer = setTimeout(() => { applySessionText(); fixDialogs() }, 20) }
|
|
3774
3831
|
})
|
|
3775
3832
|
sessionObs.observe(document.body, { childList: true, subtree: true })
|
|
3776
3833
|
}
|
|
@@ -3791,9 +3848,11 @@ window.__ModuleLoader__.load({
|
|
|
3791
3848
|
}, 'dsh-long-plugins: glass applier')
|
|
3792
3849
|
ctx.slots.inject('settings.section', () => ctx.slots.register({ name: 'settings.section', id: 'glass-ui', order: 40, label: 'RA-Span' }, GlassSettingsSection))
|
|
3793
3850
|
ctx.effect(() => {
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3851
|
+
// 玻璃配置/背景图历史拉取延迟到空闲, 减轻首屏网络风暴(背景没用玻璃时几乎零成本)
|
|
3852
|
+
const loadGlass = () => { fetch('/api/dsh-uploads/glass-config', { cache: 'no-store' }).then((r) => r.json()).then((b) => {
|
|
3853
|
+
if (b && b.cfg) { window.__dshGlassApply && window.__dshGlassApply({ ...b.cfg, bgImage: b.bgImageUrl || '' }) }
|
|
3854
|
+
}).catch(() => {}) }
|
|
3855
|
+
try { if (window.requestIdleCallback) window.requestIdleCallback(loadGlass, { timeout: 500 }); else setTimeout(loadGlass, 500) } catch (_) { loadGlass() }
|
|
3797
3856
|
}, 'dsh-long-plugins: glass boot')
|
|
3798
3857
|
},
|
|
3799
3858
|
}
|
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.
|
|
4
|
+
"version": "2.3.0",
|
|
5
5
|
"entry": {
|
|
6
6
|
"name": "dsh-long-plugins",
|
|
7
7
|
"inject": [
|
package/lib/index.js
CHANGED
|
@@ -1631,15 +1631,49 @@ 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");
|
|
1635
|
+
const currentBgFile = join(bgDir, "current.uri");
|
|
1636
|
+
// 背景图文件引用: 配置里 bgImage 为 "current"(存 bgDir/current.uri) 或 ""(无) ;不再内嵌大图 data-URI
|
|
1637
|
+
const decodeDataUri = (s) => { const m = /^data:(image\/[a-zA-Z0-9.+-]+);base64,([A-Za-z0-9+/=]+)$/.exec(String(s || "")); return m ? { mime: m[1], base64: m[2] } : null; };
|
|
1638
|
+
const bgImageUrl = (cfg) => (cfg.bgImage === "current") ? "/api/dsh-uploads/glass-background" : "";
|
|
1639
|
+
// 只保留当前背景图(current.uri), 删除其它历史图片文件(旧图清理)
|
|
1640
|
+
async function pruneBackgrounds() {
|
|
1641
|
+
try {
|
|
1642
|
+
const names = await readdir(bgDir);
|
|
1643
|
+
for (const n of names) {
|
|
1644
|
+
if (n === "current.uri" || !n.endsWith(".uri")) continue;
|
|
1645
|
+
try { await unlink(join(bgDir, n)); } catch (_) {}
|
|
1646
|
+
}
|
|
1647
|
+
} catch (_) {}
|
|
1648
|
+
}
|
|
1634
1649
|
async function readGlassJSON() {
|
|
1635
|
-
try {
|
|
1636
|
-
|
|
1650
|
+
try {
|
|
1651
|
+
const raw = JSON.parse(await readFile(glassFile, "utf8"));
|
|
1652
|
+
const cfg = { ...GLASS_DEFAULTS, ...(raw && typeof raw === "object" ? raw : {}) };
|
|
1653
|
+
// 迁移旧版: bgImage 是内嵌 data-URI(大图) → 存成文件 + 改引用
|
|
1654
|
+
if (typeof cfg.bgImage === "string" && cfg.bgImage.startsWith("data:image/")) {
|
|
1655
|
+
try {
|
|
1656
|
+
await mkdir(bgDir, { recursive: true, mode: 0o700 });
|
|
1657
|
+
await writeFile(currentBgFile, cfg.bgImage, "utf8");
|
|
1658
|
+
cfg.bgImage = "current";
|
|
1659
|
+
await writeFile(glassFile, JSON.stringify(cfg, null, 2), "utf8");
|
|
1660
|
+
} catch (_) {}
|
|
1661
|
+
}
|
|
1662
|
+
return cfg;
|
|
1663
|
+
} catch { return { ...GLASS_DEFAULTS }; }
|
|
1637
1664
|
}
|
|
1638
1665
|
const glassConfig = async (req, res) => {
|
|
1639
1666
|
try {
|
|
1640
1667
|
requireTrusted(req);
|
|
1641
1668
|
if (req.method === "GET" || req.method === "HEAD") {
|
|
1642
1669
|
const cfg = await readGlassJSON();
|
|
1670
|
+
// 背景图只返回"是否存在 + 图片 URL", 不再返回内嵌大图 data-URI(减小响应)
|
|
1671
|
+
let imgMeta = { present: false, bytes: 0 };
|
|
1672
|
+
let imgUrl = "";
|
|
1673
|
+
if (cfg.bgImage === "current") {
|
|
1674
|
+
imgUrl = "/api/dsh-uploads/glass-background";
|
|
1675
|
+
try { imgMeta = { present: true, bytes: (await stat(currentBgFile)).size || 0 }; } catch (_) { imgMeta = { present: true, bytes: 0 }; }
|
|
1676
|
+
}
|
|
1643
1677
|
sendJson(res, 200, {
|
|
1644
1678
|
found: true,
|
|
1645
1679
|
cfg: {
|
|
@@ -1649,9 +1683,11 @@ window.addEventListener('resize',function(){ try{ if(fitMode) zoomFit(); else ap
|
|
|
1649
1683
|
inputBox: cfg.inputBox || { light: { color: "", opacity: 1 }, dark: { color: "", opacity: 1 } },
|
|
1650
1684
|
bgTint: cfg.bgTint || { light: { color: "#1a2332", mask: 0.28 }, dark: { color: "#1a2332", mask: 0.28 } },
|
|
1651
1685
|
bgColor: cfg.bgColor || "#1a2332", bgMask: cfg.bgMask ?? 0.28,
|
|
1652
|
-
|
|
1686
|
+
bgBlur: cfg.bgBlur ?? 0, bgOpacity: cfg.bgOpacity ?? 1,
|
|
1687
|
+
bgImage: imgMeta,
|
|
1653
1688
|
},
|
|
1654
|
-
bgImage:
|
|
1689
|
+
bgImage: imgMeta,
|
|
1690
|
+
bgImageUrl: imgUrl,
|
|
1655
1691
|
});
|
|
1656
1692
|
return;
|
|
1657
1693
|
}
|
|
@@ -1659,6 +1695,19 @@ window.addEventListener('resize',function(){ try{ if(fitMode) zoomFit(); else ap
|
|
|
1659
1695
|
const body = await readJsonBody(req, 4 * 1024 * 1024);
|
|
1660
1696
|
const current = await readGlassJSON();
|
|
1661
1697
|
const next = { ...current, ...sanitizeGlass(typeof body === "object" && body !== null ? body : {}) };
|
|
1698
|
+
// 背景图改为"文件 + 引用": 用户上传的 data-URI 存入 current.uri, 配置只存 "current" 引用
|
|
1699
|
+
if (typeof next.bgImage === "string" && next.bgImage.startsWith("data:image/")) {
|
|
1700
|
+
try {
|
|
1701
|
+
await mkdir(bgDir, { recursive: true, mode: 0o700 });
|
|
1702
|
+
await writeFile(currentBgFile, next.bgImage, "utf8");
|
|
1703
|
+
next.bgImage = "current";
|
|
1704
|
+
} catch (_) { next.bgImage = ""; }
|
|
1705
|
+
} else if ("bgImage" in body && body.bgImage === "") {
|
|
1706
|
+
next.bgImage = ""; // 显式清除
|
|
1707
|
+
try { await unlink(currentBgFile); } catch (_) {}
|
|
1708
|
+
}
|
|
1709
|
+
// 只保留当前背景图, 删除历史旧图
|
|
1710
|
+
await pruneBackgrounds();
|
|
1662
1711
|
await mkdir(glassDir, { recursive: true, mode: 0o700 });
|
|
1663
1712
|
await writeFile(glassFile, JSON.stringify(next, null, 2), "utf8");
|
|
1664
1713
|
sendJson(res, 200, { ok: true, saved: true });
|
|
@@ -1669,8 +1718,44 @@ window.addEventListener('resize',function(){ try{ if(fitMode) zoomFit(); else ap
|
|
|
1669
1718
|
sendError(res, error, onError);
|
|
1670
1719
|
}
|
|
1671
1720
|
};
|
|
1721
|
+
// 背景图历史(供设置面板"选择旧图"):GET 列出,GET ?name= 返回对应 data-URI
|
|
1722
|
+
const glassBackgrounds = async (req, res) => {
|
|
1723
|
+
try {
|
|
1724
|
+
requireTrusted(req);
|
|
1725
|
+
const url = new URL(req.url, "http://x");
|
|
1726
|
+
const name = url.searchParams.get("name");
|
|
1727
|
+
if (name) {
|
|
1728
|
+
if (!/^[\w.:-]+\.uri$/.test(name)) { sendError(res, new Error("bad name"), onError); return; }
|
|
1729
|
+
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; }
|
|
1730
|
+
catch { sendJson(res, 404, { error: "not found" }); return; }
|
|
1731
|
+
}
|
|
1732
|
+
let list = [];
|
|
1733
|
+
// 只保留当前背景图, 删除历史旧图; 列表排除当前图
|
|
1734
|
+
try { await pruneBackgrounds(); } catch (_) {}
|
|
1735
|
+
try { list = (await readdir(bgDir)).filter((n) => n.endsWith(".uri") && n !== "current.uri").sort().reverse().slice(0, 30); } catch (_) {}
|
|
1736
|
+
sendJson(res, 200, { list });
|
|
1737
|
+
} catch (error) {
|
|
1738
|
+
sendError(res, error, onError);
|
|
1739
|
+
}
|
|
1740
|
+
};
|
|
1741
|
+
// 返回当前背景图(文件存的 data-URI 解码为真实图片)
|
|
1742
|
+
const glassBackground = async (req, res) => {
|
|
1743
|
+
try {
|
|
1744
|
+
requireTrusted(req);
|
|
1745
|
+
const cfg = await readGlassJSON();
|
|
1746
|
+
if (cfg.bgImage !== "current") { sendJson(res, 404, { error: "no background" }); return; }
|
|
1747
|
+
const buf = await readFile(currentBgFile, "utf8");
|
|
1748
|
+
const d = decodeDataUri(buf);
|
|
1749
|
+
if (!d) { sendJson(res, 404, { error: "bad image" }); return; }
|
|
1750
|
+
const bytes = Buffer.from(d.base64, "base64");
|
|
1751
|
+
res.writeHead(200, { "content-type": d.mime, "cache-control": "no-store", "x-content-type-options": "nosniff" });
|
|
1752
|
+
res.end(bytes);
|
|
1753
|
+
} catch (error) {
|
|
1754
|
+
sendError(res, error, onError);
|
|
1755
|
+
}
|
|
1756
|
+
};
|
|
1672
1757
|
|
|
1673
|
-
return { root, maxFileBytes, totalMaxBytes, api, download, preview, workspaceList, workspaceFile, workspacePreview, workspaceBrowse, workspaceDelete, workspaceRename, workspaceSave, docxPreviewPage, docxPreviewAsset, pptxPreviewPage, xlsxPreviewPage, xlsxPreviewAsset, glassConfig };
|
|
1758
|
+
return { root, maxFileBytes, totalMaxBytes, api, download, preview, workspaceList, workspaceFile, workspacePreview, workspaceBrowse, workspaceDelete, workspaceRename, workspaceSave, docxPreviewPage, docxPreviewAsset, pptxPreviewPage, xlsxPreviewPage, xlsxPreviewAsset, glassConfig, glassBackgrounds, glassBackground };
|
|
1674
1759
|
}
|
|
1675
1760
|
|
|
1676
1761
|
/** 人类可读文件大小。 */
|
|
@@ -2575,6 +2660,18 @@ export async function apply(ctx, config = {}) {
|
|
|
2575
2660
|
handler: handlers.glassConfig,
|
|
2576
2661
|
}), "dsh-long-plugins: glass-ui config route");
|
|
2577
2662
|
|
|
2663
|
+
ctx.effect(() => ctx.webServer.register({
|
|
2664
|
+
kind: "exact",
|
|
2665
|
+
path: "/api/dsh-uploads/glass-backgrounds",
|
|
2666
|
+
handler: handlers.glassBackgrounds,
|
|
2667
|
+
}), "dsh-long-plugins: glass-ui backgrounds route");
|
|
2668
|
+
|
|
2669
|
+
ctx.effect(() => ctx.webServer.register({
|
|
2670
|
+
kind: "exact",
|
|
2671
|
+
path: "/api/dsh-uploads/glass-background",
|
|
2672
|
+
handler: handlers.glassBackground,
|
|
2673
|
+
}), "dsh-long-plugins: glass-ui background image route");
|
|
2674
|
+
|
|
2578
2675
|
ctx.effect(() => ctx.webServer.register({
|
|
2579
2676
|
kind: "exact",
|
|
2580
2677
|
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.
|
|
3
|
+
"version": "2.3.0",
|
|
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",
|