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 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-color: var(--dsh-glass-session-bg, rgba(26,35,50,0.45)) !important;
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-color: var(--dsh-glass-input-bg, rgba(26,35,50,0.6)) !important;
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('p', { className: 'dsh-glass-note' }, '提示:背景图铺满整页;仅消息滚动区做磨砂玻璃卡片;输入卡片与其余区域透出背景。'),
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
- // ... 后续 applier 逻辑在下方
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
- el.style.backgroundImage = `url('${c.bgImage}')`
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.3.9",
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 > 1024 * 1024) {
200
- rejectPromise(new Error("body too large"));
201
- request.destroy();
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.0.0",
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",