dsh-long-plugins 2.0.0 → 2.1.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/README.md +12 -0
- package/client/client.js +103 -13
- package/dsh.plugin.json +1 -1
- package/lib/index.js +18 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -124,6 +124,18 @@ MIT
|
|
|
124
124
|
|
|
125
125
|
## Changelog / 更新记录
|
|
126
126
|
|
|
127
|
+
### v2.1.0
|
|
128
|
+
- 增强:毛玻璃界面(玻璃拟态)进一步打磨。
|
|
129
|
+
- 主题感知磨砂:磨砂卡片颜色跟随系统深色/浅色主题自动反转(浅色=白磨砂深字 / 深色=深磨砂浅字)。
|
|
130
|
+
- 会话区内容反底色全透明化:去掉代码块/提示/气泡等反底色小块,统一融入磨砂卡片。
|
|
131
|
+
- 会话区「启用玻璃」开关:可单独禁用会话区磨砂(还原原生)。
|
|
132
|
+
- 会话区/输入区各自独立「罩色 + 罩强度 + 透明度」调节。
|
|
133
|
+
- 新增「背景图罩」:背景图上叠一层半透明罩色,深度可调。
|
|
134
|
+
- 手机端/窄屏(≤768px)自动禁用毛玻璃,还原 DSH 原生。
|
|
135
|
+
- 输入卡片/底部白色层透明化,更透背景。
|
|
136
|
+
- 修复:输入框下拉/悬浮菜单(`/` 命令、`@` 提及)背景透明,改为不透明随主题色。
|
|
137
|
+
- 修复:玻璃界面自定义背景图上传超大文件报 `Unexpected token '<'` 错误——上传上限提升到 4MiB。
|
|
138
|
+
|
|
127
139
|
### v2.0.0
|
|
128
140
|
- 新增:毛玻璃界面(玻璃拟态)。为 DSH 网页叠加自定义毛玻璃效果,将界面打磨成"玻璃"质感。
|
|
129
141
|
- **自定义背景图**:上传一张背景图铺满整个页面(含左边栏、顶部、会话区),实现桌面壁纸式的背景;左边栏 / 顶部半透明透出背景图,界面更通透。
|
package/client/client.js
CHANGED
|
@@ -3060,7 +3060,7 @@ window.__ModuleLoader__.load({
|
|
|
3060
3060
|
}
|
|
3061
3061
|
/* 会话滚动区:磨砂玻璃悬浮卡片(居中收窄,不占满宽度,四周透背景图) */
|
|
3062
3062
|
html[data-dsh-glass="on"] #root [class*="wSkVaW_scrollBody"] {
|
|
3063
|
-
background
|
|
3063
|
+
background: linear-gradient(var(--dsh-glass-session-white, rgba(255,255,255,0.5)), var(--dsh-glass-session-white, rgba(255,255,255,0.5))) var(--dsh-glass-session-bg, rgba(26,35,50,0.45)) !important;
|
|
3064
3064
|
backdrop-filter: blur(var(--dsh-glass-blur, 16px));
|
|
3065
3065
|
-webkit-backdrop-filter: blur(var(--dsh-glass-blur, 16px));
|
|
3066
3066
|
border-radius: 16px;
|
|
@@ -3074,9 +3074,18 @@ window.__ModuleLoader__.load({
|
|
|
3074
3074
|
html[data-dsh-glass="on"] #root [class*="wSkVaW_scrollBody"]::-webkit-scrollbar-track { background: transparent; }
|
|
3075
3075
|
html[data-dsh-glass="on"] #root [class*="wSkVaW_scrollBody"]::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.35); border-radius: 8px; }
|
|
3076
3076
|
html[data-dsh-glass="on"] #root [class*="wSkVaW_scrollBody"]::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.5); }
|
|
3077
|
+
/* 会话区禁用玻璃:scrollBody 还原为透明、无磨砂、无圆角/阴影(保持 DSH 原生会话区外观) */
|
|
3078
|
+
html[data-dsh-glass="on"][data-dsh-session-glass="off"] #root [class*="wSkVaW_scrollBody"] {
|
|
3079
|
+
background-color: transparent !important;
|
|
3080
|
+
background-image: none !important;
|
|
3081
|
+
backdrop-filter: none !important;
|
|
3082
|
+
-webkit-backdrop-filter: none !important;
|
|
3083
|
+
border-radius: 0 !important;
|
|
3084
|
+
box-shadow: none !important;
|
|
3085
|
+
}
|
|
3077
3086
|
/* 底部输入卡片:深色半透明磨砂(参考图:深色玻璃卡片 + 白字,与背景区分开) */
|
|
3078
3087
|
html[data-dsh-glass="on"] #root [class*="uV2eYG_card"] {
|
|
3079
|
-
background
|
|
3088
|
+
background: linear-gradient(var(--dsh-glass-input-white, rgba(255,255,255,0.3)), var(--dsh-glass-input-white, rgba(255,255,255,0.3))) var(--dsh-glass-input-bg, rgba(26,35,50,0.6)) !important;
|
|
3080
3089
|
backdrop-filter: blur(var(--dsh-glass-blur, 16px));
|
|
3081
3090
|
-webkit-backdrop-filter: blur(var(--dsh-glass-blur, 16px));
|
|
3082
3091
|
}
|
|
@@ -3087,6 +3096,47 @@ window.__ModuleLoader__.load({
|
|
|
3087
3096
|
html[data-dsh-glass="off"] #root [class*="wSkVaW_root"],
|
|
3088
3097
|
html[data-dsh-glass="off"] #root [class*="wSkVaW_scrollBody"],
|
|
3089
3098
|
html[data-dsh-glass="off"] #root [class*="uV2eYG_card"] { background-color: transparent !important; backdrop-filter: none; }
|
|
3099
|
+
/* 用户消息气泡:浅色半透明底(随主题),与 AI 消息(透明/磨砂)区分 */
|
|
3100
|
+
html[data-dsh-glass="on"] #root [class*="ngdEzaw_bubble"]:not([class*="button"]) {
|
|
3101
|
+
background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 62%, transparent) !important;
|
|
3102
|
+
}
|
|
3103
|
+
/* 会话区所有内容元素背景透明化:去掉"反底色"小块衬底,统一融入磨砂卡片 */
|
|
3104
|
+
html[data-dsh-glass="on"] #root [class*="wSkVaW_scrollBody"] *:not([class*="button"]):not([class*="bubble"]):not([class*="_menu"]) {
|
|
3105
|
+
background-color: transparent !important;
|
|
3106
|
+
}
|
|
3107
|
+
/* 会话区内容衬底(代码块/提示/气泡/引用等)玻璃开启时透明化,融入磨砂卡片,去掉浅色小块衬底 */
|
|
3108
|
+
html[data-dsh-glass="on"] #root [class*="wSkVaW_scrollBody"] [class*="n_block"],
|
|
3109
|
+
html[data-dsh-glass="on"] #root [class*="wSkVaW_scrollBody"] [class*="n_bannerWrap"],
|
|
3110
|
+
|
|
3111
|
+
html[data-dsh-glass="on"] #root [class*="wSkVaW_scrollBody"] [class*="n_plain"],
|
|
3112
|
+
html[data-dsh-glass="on"] #root [class*="wSkVaW_scrollBody"] [class*="n_copyButton"],
|
|
3113
|
+
html[data-dsh-glass="on"] #root [class*="wSkVaW_scrollBody"] [class*="md-code-block"] {
|
|
3114
|
+
background-color: transparent !important;
|
|
3115
|
+
}
|
|
3116
|
+
/* uV2eYG_add: composer 里随内容变化的白色衬底,玻璃开启时透明化 */
|
|
3117
|
+
html[data-dsh-glass="on"] #root [class*="uV2eYG_add"] {
|
|
3118
|
+
background-color: transparent !important;
|
|
3119
|
+
}
|
|
3120
|
+
/* 输入框下拉/悬浮菜单(如 / 命令、@ 提及):背景给不透明随主题色,避免叠在会话磨砂卡上重叠看不清 */
|
|
3121
|
+
html[data-dsh-glass="on"] #root [class*="_menu"][class*="_menu"][class*="_menu"][class*="_menu"] {
|
|
3122
|
+
background-color: var(--dsw-specific-input-major, #e8edf3) !important;
|
|
3123
|
+
background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 97%, transparent) !important;
|
|
3124
|
+
backdrop-filter: none !important;
|
|
3125
|
+
-webkit-backdrop-filter: none !important;
|
|
3126
|
+
}
|
|
3127
|
+
/* 输入框旁权限/功能选择下拉(盾牌 Full access 等 Radix 风格菜单):与 / 命令菜单同样处理 */
|
|
3128
|
+
html[data-dsh-glass="on"] #root [class*="uV2eYG_card"] [class*="_root_"]:has([class*="_list_"]) {
|
|
3129
|
+
background-color: var(--dsw-specific-input-major, #e8edf3) !important;
|
|
3130
|
+
background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 97%, transparent) !important;
|
|
3131
|
+
backdrop-filter: none !important;
|
|
3132
|
+
-webkit-backdrop-filter: none !important;
|
|
3133
|
+
}
|
|
3134
|
+
html[data-dsh-glass="on"] #root [class*="uV2eYG_card"] [class*="_list_"] {
|
|
3135
|
+
background-color: var(--dsw-specific-input-major, #e8edf3) !important;
|
|
3136
|
+
background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 97%, transparent) !important;
|
|
3137
|
+
backdrop-filter: none !important;
|
|
3138
|
+
-webkit-backdrop-filter: none !important;
|
|
3139
|
+
}
|
|
3090
3140
|
.dsh-glass-section{display:flex;flex-direction:column;gap:16px;min-width:0;padding:4px 2px 24px;color:var(--dsw-alias-label-primary)}
|
|
3091
3141
|
.dsh-glass-head{display:flex;align-items:center;justify-content:space-between;gap:16px}
|
|
3092
3142
|
.dsh-glass-head h2{margin:0;font-size:20px;line-height:28px}
|
|
@@ -3131,11 +3181,14 @@ window.__ModuleLoader__.load({
|
|
|
3131
3181
|
)
|
|
3132
3182
|
}
|
|
3133
3183
|
// 会话区/输入区共用设置卡:罩色 + 罩强度
|
|
3134
|
-
const GlassZoneCard = ({ title, zone, onChange }) => {
|
|
3135
|
-
const z = zone || { color: '#1a2332', mask: 0.45 }
|
|
3184
|
+
const GlassZoneCard = ({ title, zone, onChange, showGlassToggle }) => {
|
|
3185
|
+
const z = zone || { color: '#1a2332', mask: 0.45, enabled: true }
|
|
3136
3186
|
const setPatch = (p) => onChange({ ...z, ...p })
|
|
3137
3187
|
return React.createElement('div', { className: 'dsh-glass-card' },
|
|
3138
3188
|
React.createElement('div', { className: 'dsh-glass-zone-title' }, title),
|
|
3189
|
+
showGlassToggle ? React.createElement('div', { className: 'dsh-glass-row' },
|
|
3190
|
+
React.createElement('label', { className: 'dsh-glass-switch' }, React.createElement('input', { type: 'checkbox', checked: z.enabled !== false, onChange: (e) => setPatch({ enabled: e.target.checked }) }), title + ' 启用玻璃'),
|
|
3191
|
+
) : null,
|
|
3139
3192
|
React.createElement('div', { className: 'dsh-glass-row' },
|
|
3140
3193
|
React.createElement('label', null, title + ' 罩色'),
|
|
3141
3194
|
React.createElement('div', { className: 'dsh-glass-swatches' },
|
|
@@ -3148,6 +3201,11 @@ window.__ModuleLoader__.load({
|
|
|
3148
3201
|
React.createElement('input', { type: 'range', className: 'dsh-glass-range', min: 0, max: 0.95, step: 0.01, value: z.mask ?? 0.45, onChange: (e) => setPatch({ mask: Number(e.target.value) }) }),
|
|
3149
3202
|
React.createElement('span', { className: 'dsh-glass-val' }, `${Math.round((Number(z.mask ?? 0.45)) * 100)}%`),
|
|
3150
3203
|
),
|
|
3204
|
+
React.createElement('div', { className: 'dsh-glass-row' },
|
|
3205
|
+
React.createElement('label', null, title + ' 透明度'),
|
|
3206
|
+
React.createElement('input', { type: 'range', className: 'dsh-glass-range', min: 0, max: 1, step: 0.01, value: z.opacity ?? 0.5, onChange: (e) => setPatch({ opacity: Number(e.target.value) }) }),
|
|
3207
|
+
React.createElement('span', { className: 'dsh-glass-val' }, `${Math.round((Number(z.opacity ?? 0.5)) * 100)}%`),
|
|
3208
|
+
),
|
|
3151
3209
|
)
|
|
3152
3210
|
}
|
|
3153
3211
|
const GlassSettingsSection = () => {
|
|
@@ -3170,10 +3228,10 @@ window.__ModuleLoader__.load({
|
|
|
3170
3228
|
const cfg = state.cfg || {}
|
|
3171
3229
|
const payload = {
|
|
3172
3230
|
enabled: !!cfg.enabled, blur: Number(cfg.blur) || 0,
|
|
3173
|
-
bgImage: state.bgImage || cfg.bgImage || '',
|
|
3231
|
+
bgImage: state.bgImage || cfg.bgImage || '', bgMask: Number(cfg.bgMask) || 0.28, bgColor: cfg.bgColor || '#1a2332',
|
|
3174
3232
|
zone: {
|
|
3175
|
-
session: { color: (cfg.session && cfg.session.color) || '#1a2332', mask: Number((cfg.session && cfg.session.mask)) || 0.45 },
|
|
3176
|
-
input: { color: (cfg.input && cfg.input.color) || '#1a2332', mask: Number((cfg.input && cfg.input.mask)) || 0.6 },
|
|
3233
|
+
session: { enabled: cfg.session && cfg.session.enabled !== false, color: (cfg.session && cfg.session.color) || '#1a2332', mask: Number((cfg.session && cfg.session.mask)) || 0.45, opacity: Number((cfg.session && cfg.session.opacity)) || 0.5 },
|
|
3234
|
+
input: { color: (cfg.input && cfg.input.color) || '#1a2332', mask: Number((cfg.input && cfg.input.mask)) || 0.6, opacity: Number((cfg.input && cfg.input.opacity)) || 0.55 },
|
|
3177
3235
|
},
|
|
3178
3236
|
}
|
|
3179
3237
|
const r = await fetch('/api/dsh-uploads/glass-config', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload) })
|
|
@@ -3197,13 +3255,18 @@ window.__ModuleLoader__.load({
|
|
|
3197
3255
|
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) }) }),
|
|
3198
3256
|
React.createElement('span', { className: 'dsh-glass-val' }, `${Number(cfg.blur ?? 20)} px`),
|
|
3199
3257
|
),
|
|
3200
|
-
React.createElement(GlassZoneCard, { title: '会话区', zone: cfg.session, onChange: (p) => patchZone('session', p) }),
|
|
3258
|
+
React.createElement(GlassZoneCard, { title: '会话区', zone: cfg.session, onChange: (p) => patchZone('session', p), showGlassToggle: true }),
|
|
3201
3259
|
React.createElement(GlassZoneCard, { title: '输入区', zone: cfg.input, onChange: (p) => patchZone('input', p) }),
|
|
3202
3260
|
React.createElement('div', { className: 'dsh-glass-row' },
|
|
3203
3261
|
React.createElement('label', null, '共用背景图'),
|
|
3204
3262
|
React.createElement('div', { className: 'dsh-glass-bg' }, glassImageInput(state.bgImage, (v) => setState((s) => ({ ...s, bgImage: v })))),
|
|
3205
3263
|
),
|
|
3206
|
-
React.createElement('
|
|
3264
|
+
React.createElement('div', { className: 'dsh-glass-row' },
|
|
3265
|
+
React.createElement('label', null, '背景图罩'),
|
|
3266
|
+
React.createElement('input', { type: 'range', className: 'dsh-glass-range', min: 0, max: 0.95, step: 0.01, value: cfg.bgMask ?? 0.28, onChange: (e) => patch({ bgMask: Number(e.target.value) }) }),
|
|
3267
|
+
React.createElement('span', { className: 'dsh-glass-val' }, `${Math.round((Number(cfg.bgMask ?? 0.28)) * 100)}%`),
|
|
3268
|
+
),
|
|
3269
|
+
React.createElement('p', { className: 'dsh-glass-note' }, '提示:背景图铺满整页(可加背景图罩);会话区/输入区各自使用自己的罩色、罩强度与透明度;左栏/顶部透出背景图。'),
|
|
3207
3270
|
React.createElement('div', { className: 'dsh-glass-actions' },
|
|
3208
3271
|
React.createElement('button', { type: 'button', onClick: load }, '重置'),
|
|
3209
3272
|
React.createElement('button', { type: 'button', className: 'primary', disabled: state.saving, onClick: save }, state.saving ? '保存中…' : '保存生效'),
|
|
@@ -3243,21 +3306,46 @@ window.__ModuleLoader__.load({
|
|
|
3243
3306
|
|
|
3244
3307
|
window.__dshGlassApply = (cfg) => {
|
|
3245
3308
|
const c = cfg || {}
|
|
3309
|
+
// 手机端/窄屏(<=768px):不开启毛玻璃,还原 DSH 原生样式(清背景图/罩变量/磨砂)。
|
|
3310
|
+
if (window.innerWidth <= 768) {
|
|
3311
|
+
root.setAttribute('data-dsh-glass', 'off')
|
|
3312
|
+
root.setAttribute('data-dsh-session-glass', 'off')
|
|
3313
|
+
root.style.setProperty('--dsh-glass-blur', '')
|
|
3314
|
+
root.style.setProperty('--dsh-glass-session-bg', '')
|
|
3315
|
+
root.style.setProperty('--dsh-glass-input-bg', '')
|
|
3316
|
+
root.style.setProperty('--dsh-glass-bg-zone', '')
|
|
3317
|
+
const b = document.body
|
|
3318
|
+
b.style.backgroundImage = 'none'; b.style.backgroundSize = ''; b.style.backgroundPosition = ''; b.style.backgroundRepeat = ''
|
|
3319
|
+
const fr = document.querySelector('#root > div > div') || document.querySelector('#root > div')
|
|
3320
|
+
if (fr) { fr.style.backgroundColor = ''; fr.style.backgroundImage = 'none'; fr.style.backgroundSize = ''; fr.style.backgroundPosition = ''; fr.style.backgroundRepeat = '' }
|
|
3321
|
+
return
|
|
3322
|
+
}
|
|
3246
3323
|
root.setAttribute('data-dsh-glass', c.enabled ? 'on' : 'off')
|
|
3247
|
-
|
|
3324
|
+
const sessOn = !(c.zone && c.zone.session && c.zone.session.enabled === false)
|
|
3325
|
+
root.setAttribute('data-dsh-session-glass', sessOn ? 'on' : 'off')
|
|
3248
3326
|
const blur = Number(c.blur) || 16
|
|
3249
3327
|
root.style.setProperty('--dsh-glass-blur', blur + 'px')
|
|
3250
|
-
const zs = (c.zone && c.zone.session) || { color: '#1a2332', mask: 0.45 }
|
|
3251
|
-
const zi = (c.zone && c.zone.input) || { color: '#1a2332', mask: 0.6 }
|
|
3328
|
+
const zs = (c.zone && c.zone.session) || { color: '#1a2332', mask: 0.45, opacity: 0.5 }
|
|
3329
|
+
const zi = (c.zone && c.zone.input) || { color: '#1a2332', mask: 0.6, opacity: 0.55 }
|
|
3252
3330
|
root.style.setProperty('--dsh-glass-session-bg', `rgba(${hexToRgb(zs.color)},${Math.min(0.95, Math.max(0, Number(zs.mask) || 0.45))})`)
|
|
3253
3331
|
root.style.setProperty('--dsh-glass-input-bg', `rgba(${hexToRgb(zi.color)},${Math.min(0.95, Math.max(0, Number(zi.mask) || 0.6))})`)
|
|
3332
|
+
const sa = Math.round(Math.min(1, Math.max(0, Number(zs.opacity) || 0.5)) * 100)
|
|
3333
|
+
root.style.setProperty('--dsh-glass-session-white', `color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) ${sa}%, transparent)`)
|
|
3334
|
+
const ia = Math.round(Math.min(1, Math.max(0, Number(zi.opacity) === 0 ? 0 : (Number(zi.opacity) || 0.3))) * 100)
|
|
3335
|
+
root.style.setProperty('--dsh-glass-input-white', `color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) ${ia}%, transparent)`)
|
|
3336
|
+
// 背景图罩:整页背景图上叠一层半透明罩色
|
|
3337
|
+
const bgc = c.bgColor || '#1a2332'
|
|
3338
|
+
const bgm = Math.min(0.95, Math.max(0, Number(c.bgMask) || 0.28))
|
|
3339
|
+
root.style.setProperty('--dsh-glass-bg-zone', `rgba(${hexToRgb(bgc)},${bgm})`)
|
|
3254
3340
|
|
|
3255
3341
|
const body = document.body
|
|
3256
3342
|
const frame = document.querySelector('#root > div > div') || document.querySelector('#root > div')
|
|
3257
3343
|
const applyBg = (el) => {
|
|
3258
3344
|
if (!el) return
|
|
3259
3345
|
if (c.enabled && c.bgImage) {
|
|
3260
|
-
|
|
3346
|
+
// 背景图 + 罩色:先叠加一层背景罩(linear-gradient),再铺背景图,实现"背景图上有罩"。
|
|
3347
|
+
const gz = root.style.getPropertyValue('--dsh-glass-bg-zone') || 'rgba(26,35,50,0.28)'
|
|
3348
|
+
el.style.backgroundImage = `linear-gradient(${gz},${gz}), url('${c.bgImage}')`
|
|
3261
3349
|
el.style.backgroundSize = 'cover'
|
|
3262
3350
|
el.style.backgroundPosition = 'center'
|
|
3263
3351
|
el.style.backgroundRepeat = 'no-repeat'
|
|
@@ -3268,6 +3356,8 @@ window.__ModuleLoader__.load({
|
|
|
3268
3356
|
el.style.backgroundRepeat = ''
|
|
3269
3357
|
}
|
|
3270
3358
|
}
|
|
3359
|
+
// body 背景色也设透明,避免 body 白色露底形成"白色遮盖层"(背景图 cover 铺底即可)。
|
|
3360
|
+
if (c.enabled && c.bgImage) { body.style.backgroundColor = 'transparent' } else { body.style.backgroundColor = '' }
|
|
3271
3361
|
applyBg(body)
|
|
3272
3362
|
if (frame) { frame.style.backgroundColor = 'transparent'; applyBg(frame) }
|
|
3273
3363
|
centerScroll()
|
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": "1.
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"entry": {
|
|
6
6
|
"name": "dsh-long-plugins",
|
|
7
7
|
"inject": [
|
package/lib/index.js
CHANGED
|
@@ -190,20 +190,24 @@ export function isTrustedUploadRequest(req, trustedHosts = []) {
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
/** Read a JSON request body (bounded; 1 MiB to fit large edited documents). */
|
|
193
|
-
function readJsonBody(request) {
|
|
193
|
+
function readJsonBody(request, maxBytes = 1024 * 1024) {
|
|
194
194
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
195
195
|
let size = 0;
|
|
196
|
+
let tooBig = false;
|
|
196
197
|
const chunks = [];
|
|
197
198
|
request.on("data", (chunk) => {
|
|
199
|
+
if (tooBig) return;
|
|
198
200
|
size += chunk.length;
|
|
199
|
-
if (size >
|
|
200
|
-
|
|
201
|
-
|
|
201
|
+
if (size > maxBytes) {
|
|
202
|
+
tooBig = true;
|
|
203
|
+
chunks.length = 0;
|
|
204
|
+
rejectPromise(new HttpError(413, "body too large"));
|
|
202
205
|
return;
|
|
203
206
|
}
|
|
204
207
|
chunks.push(chunk);
|
|
205
208
|
});
|
|
206
209
|
request.on("end", () => {
|
|
210
|
+
if (tooBig) return;
|
|
207
211
|
try {
|
|
208
212
|
resolvePromise(JSON.parse(Buffer.concat(chunks).toString("utf8")));
|
|
209
213
|
} catch (error) {
|
|
@@ -675,6 +679,8 @@ function sanitizeGlass(input) {
|
|
|
675
679
|
if ("color" in input) out.color = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(String(input.color)) ? String(input.color) : undefined;
|
|
676
680
|
const sanitizeUri = (v) => { const s = String(v ?? ""); return (/^data:image\/(png|jpe?g|webp|gif);base64,/.test(s) && s.length <= 2 * 1024 * 1024 + 256) ? s : undefined; };
|
|
677
681
|
if ("bgImage" in input) out.bgImage = sanitizeUri(input.bgImage);
|
|
682
|
+
if ("bgColor" in input) out.bgColor = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(String(input.bgColor)) ? String(input.bgColor) : undefined;
|
|
683
|
+
if ("bgMask" in input) { const n = Number(input.bgMask); out.bgMask = Number.isFinite(n) ? Math.max(0, Math.min(0.95, n)) : undefined; }
|
|
678
684
|
// 会话区/输入区各自独立罩色+罩强度
|
|
679
685
|
if (input.zone && typeof input.zone === "object") {
|
|
680
686
|
const zo = {};
|
|
@@ -682,8 +688,10 @@ function sanitizeGlass(input) {
|
|
|
682
688
|
const z = input.zone[key];
|
|
683
689
|
if (z && typeof z === "object") {
|
|
684
690
|
const o = {};
|
|
691
|
+
if ("enabled" in z) o.enabled = z.enabled === true;
|
|
685
692
|
if ("color" in z) o.color = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(String(z.color)) ? String(z.color) : undefined;
|
|
686
693
|
if ("mask" in z) { const n = Number(z.mask); o.mask = Number.isFinite(n) ? Math.max(0, Math.min(0.95, n)) : undefined; }
|
|
694
|
+
if ("opacity" in z) { const n = Number(z.opacity); o.opacity = Number.isFinite(n) ? Math.max(0, Math.min(1, n)) : undefined; }
|
|
687
695
|
zo[key] = Object.fromEntries(Object.entries(o).filter(([, v]) => v !== undefined));
|
|
688
696
|
}
|
|
689
697
|
}
|
|
@@ -1584,8 +1592,8 @@ window.addEventListener('resize',function(){ try{ if(fitMode) zoomFit(); else ap
|
|
|
1584
1592
|
// 背景图以 data-URI 存进配置,随配置持久化。enabled=总开关;blur=磨砂模糊;mask=整页罩强度;
|
|
1585
1593
|
// color=叠加/罩色;bgImage=共用背景图。顶栏/左栏的"罩"与整页共用 mask+color(不再单独存,简化)。
|
|
1586
1594
|
const GLASS_DEFAULTS = {
|
|
1587
|
-
enabled: false, blur: 20, bgImage: "",
|
|
1588
|
-
zone: { session: { color: "#1a2332", mask: 0.45 }, input: { color: "#1a2332", mask: 0.6 } },
|
|
1595
|
+
enabled: false, blur: 20, bgImage: "", bgColor: "#1a2332", bgMask: 0.28,
|
|
1596
|
+
zone: { session: { color: "#1a2332", mask: 0.45, opacity: 0.5 }, input: { color: "#1a2332", mask: 0.6, opacity: 0.55 } },
|
|
1589
1597
|
};
|
|
1590
1598
|
const glassDir = resolve(homedir(), ".dsh-long-plugins");
|
|
1591
1599
|
const glassFile = join(glassDir, "glass.json");
|
|
@@ -1602,8 +1610,9 @@ window.addEventListener('resize',function(){ try{ if(fitMode) zoomFit(); else ap
|
|
|
1602
1610
|
found: true,
|
|
1603
1611
|
cfg: {
|
|
1604
1612
|
enabled: cfg.enabled, blur: cfg.blur,
|
|
1605
|
-
session: cfg.zone?.session || { color: "#1a2332", mask: 0.45 },
|
|
1606
|
-
input: cfg.zone?.input || { color: "#1a2332", mask: 0.6 },
|
|
1613
|
+
session: cfg.zone?.session || { enabled: true, color: "#1a2332", mask: 0.45, opacity: 0.5 },
|
|
1614
|
+
input: cfg.zone?.input || { color: "#1a2332", mask: 0.6, opacity: 0.55 },
|
|
1615
|
+
bgColor: cfg.bgColor || "#1a2332", bgMask: cfg.bgMask ?? 0.28,
|
|
1607
1616
|
bgImage: cfg.bgImage ? { present: true, bytes: Math.round((cfg.bgImage.length * 3) / 4) } : { present: false },
|
|
1608
1617
|
},
|
|
1609
1618
|
bgImage: cfg.bgImage,
|
|
@@ -1611,7 +1620,7 @@ window.addEventListener('resize',function(){ try{ if(fitMode) zoomFit(); else ap
|
|
|
1611
1620
|
return;
|
|
1612
1621
|
}
|
|
1613
1622
|
if (req.method === "POST") {
|
|
1614
|
-
const body = await readJsonBody(req);
|
|
1623
|
+
const body = await readJsonBody(req, 4 * 1024 * 1024);
|
|
1615
1624
|
const current = await readGlassJSON();
|
|
1616
1625
|
const next = { ...current, ...sanitizeGlass(typeof body === "object" && body !== null ? body : {}) };
|
|
1617
1626
|
await mkdir(glassDir, { recursive: true, mode: 0o700 });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-long-plugins",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.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",
|