dsh-long-plugins 2.3.0 → 2.4.1
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 +215 -82
- package/dsh.plugin.json +1 -1
- package/lib/index.js +62 -2
- package/package.json +1 -1
- package/patches/dsh-client-connection-heartbeat.sh +8 -3
- package/skill/dsh-long-plugins-install/SKILL.md +11 -0
- package/skill/dsh-upgrade/SKILL.md +13 -29
package/client/client.js
CHANGED
|
@@ -529,17 +529,27 @@ window.__ModuleLoader__.load({
|
|
|
529
529
|
}
|
|
530
530
|
}
|
|
531
531
|
}
|
|
532
|
-
window.
|
|
533
|
-
window.
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
532
|
+
const dragOn = !!(window.__dshLongMod && window.__dshLongMod('uploadDragDrop'))
|
|
533
|
+
const pasteOn = !!(window.__dshLongMod && window.__dshLongMod('uploadPaste'))
|
|
534
|
+
if (dragOn) {
|
|
535
|
+
window.addEventListener('dragenter', onDragEnter, true)
|
|
536
|
+
window.addEventListener('dragover', onDragOver, true)
|
|
537
|
+
window.addEventListener('dragleave', onDragLeave, true)
|
|
538
|
+
window.addEventListener('drop', onDrop, true)
|
|
539
|
+
}
|
|
540
|
+
if (pasteOn) {
|
|
541
|
+
window.addEventListener('paste', onPaste, true)
|
|
542
|
+
}
|
|
537
543
|
return () => {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
544
|
+
if (dragOn) {
|
|
545
|
+
window.removeEventListener('dragenter', onDragEnter, true)
|
|
546
|
+
window.removeEventListener('dragover', onDragOver, true)
|
|
547
|
+
window.removeEventListener('dragleave', onDragLeave, true)
|
|
548
|
+
window.removeEventListener('drop', onDrop, true)
|
|
549
|
+
}
|
|
550
|
+
if (pasteOn) {
|
|
551
|
+
window.removeEventListener('paste', onPaste, true)
|
|
552
|
+
}
|
|
543
553
|
}
|
|
544
554
|
}, [controller, sessionId])
|
|
545
555
|
|
|
@@ -1367,25 +1377,52 @@ window.__ModuleLoader__.load({
|
|
|
1367
1377
|
}
|
|
1368
1378
|
|
|
1369
1379
|
function apply(ctx) {
|
|
1370
|
-
const
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1380
|
+
const mod = window.__dshLongMod || (() => true)
|
|
1381
|
+
const anyUpload = mod('uploadAttach') || mod('uploadDragDrop') || mod('uploadPaste')
|
|
1382
|
+
if (anyUpload) {
|
|
1383
|
+
const controller = new FileDraftController(ctx)
|
|
1384
|
+
ctx.effect(() => () => controller.dispose(), 'dsh-upload-manager: draft-file state')
|
|
1385
|
+
ctx.effect(() => ctx.inputTriggers.registerSource({
|
|
1386
|
+
trigger: '@',
|
|
1387
|
+
name: SOURCE,
|
|
1388
|
+
order: 10_000,
|
|
1389
|
+
candidates: async () => [],
|
|
1390
|
+
onPick: () => undefined,
|
|
1391
|
+
codec: {
|
|
1392
|
+
clipboardText: () => '',
|
|
1393
|
+
serialize: async (ref) => {
|
|
1394
|
+
const file = controller.fileForRef(ref)
|
|
1395
|
+
if (!file) throw new Error('待发送文件已失效,请重新上传')
|
|
1396
|
+
controller.markSerializing(ref)
|
|
1397
|
+
return serializedFile(file)
|
|
1398
|
+
},
|
|
1385
1399
|
},
|
|
1386
|
-
},
|
|
1387
|
-
|
|
1388
|
-
|
|
1400
|
+
}), 'dsh-upload-manager: hidden file reference codec')
|
|
1401
|
+
// 输入框附件上传(回形针) + 待发送文件栏:由 uploadAttach 控制
|
|
1402
|
+
if (mod('uploadAttach')) {
|
|
1403
|
+
ctx.slots.inject('conversation.input.left', () => ctx.slots.register({
|
|
1404
|
+
name: 'conversation.input.left',
|
|
1405
|
+
id: 'local-file-upload',
|
|
1406
|
+
order: -20,
|
|
1407
|
+
label: '上传文件',
|
|
1408
|
+
}, (props) => React.createElement(UploadControl, { ...props, controller })))
|
|
1409
|
+
ctx.slots.inject('conversation.input.dock', () => ctx.slots.register({
|
|
1410
|
+
name: 'conversation.input.dock',
|
|
1411
|
+
id: 'local-file-upload-rail',
|
|
1412
|
+
order: 80,
|
|
1413
|
+
label: '待发送文件',
|
|
1414
|
+
}, (props) => React.createElement(PendingFileRail, { ...props, controller })))
|
|
1415
|
+
}
|
|
1416
|
+
// 拖放上传(遮罩+window 监听) / 粘贴上传:由各自开关控制
|
|
1417
|
+
if (mod('uploadDragDrop') || mod('uploadPaste')) {
|
|
1418
|
+
ctx.slots.inject('conversation.input.dock', () => ctx.slots.register({
|
|
1419
|
+
name: 'conversation.input.dock',
|
|
1420
|
+
id: 'local-file-upload-dropzone',
|
|
1421
|
+
order: 90,
|
|
1422
|
+
label: '拖放/粘贴上传',
|
|
1423
|
+
}, (props) => React.createElement(DragDropOverlay, { ...props, controller })))
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1389
1426
|
ctx.effect(() => {
|
|
1390
1427
|
const style = document.createElement('style')
|
|
1391
1428
|
style.dataset.plugin = 'dsh-file-uploads'
|
|
@@ -1393,41 +1430,24 @@ window.__ModuleLoader__.load({
|
|
|
1393
1430
|
document.head.appendChild(style)
|
|
1394
1431
|
return () => style.remove()
|
|
1395
1432
|
}, 'dsh-upload-manager: styles')
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
order: 90,
|
|
1415
|
-
label: '拖放上传',
|
|
1416
|
-
}, (props) => React.createElement(DragDropOverlay, { ...props, controller })))
|
|
1417
|
-
|
|
1418
|
-
ctx.slots.inject('settings.section', () => ctx.slots.register({
|
|
1419
|
-
name: 'settings.section',
|
|
1420
|
-
id: 'uploaded-files',
|
|
1421
|
-
order: 30,
|
|
1422
|
-
label: '上传文件',
|
|
1423
|
-
}, UploadSettingsSection))
|
|
1424
|
-
|
|
1425
|
-
ctx.slots.inject('settings.section', () => ctx.slots.register({
|
|
1426
|
-
name: 'settings.section',
|
|
1427
|
-
id: 'output-files',
|
|
1428
|
-
order: 35,
|
|
1429
|
-
label: '输出文件',
|
|
1430
|
-
}, WorkspaceFilesSection))
|
|
1433
|
+
// 上传文件预览/管理设置区:由 uploadPreview 控制
|
|
1434
|
+
if (mod('uploadPreview')) {
|
|
1435
|
+
ctx.slots.inject('settings.section', () => ctx.slots.register({
|
|
1436
|
+
name: 'settings.section',
|
|
1437
|
+
id: 'uploaded-files',
|
|
1438
|
+
order: 30,
|
|
1439
|
+
label: '上传文件',
|
|
1440
|
+
}, UploadSettingsSection))
|
|
1441
|
+
}
|
|
1442
|
+
// 输出文件(工作区)设置区:由 workspace 控制
|
|
1443
|
+
if (mod('workspace')) {
|
|
1444
|
+
ctx.slots.inject('settings.section', () => ctx.slots.register({
|
|
1445
|
+
name: 'settings.section',
|
|
1446
|
+
id: 'output-files',
|
|
1447
|
+
order: 35,
|
|
1448
|
+
label: '输出文件',
|
|
1449
|
+
}, WorkspaceFilesSection))
|
|
1450
|
+
}
|
|
1431
1451
|
}
|
|
1432
1452
|
return { apply, inject }
|
|
1433
1453
|
})()
|
|
@@ -2062,18 +2082,21 @@ window.__ModuleLoader__.load({
|
|
|
2062
2082
|
const inject = ["slots", "locale"];
|
|
2063
2083
|
/** Register the token-usage card into the sidebar footer. */
|
|
2064
2084
|
function apply(ctx) {
|
|
2065
|
-
injectMobileCss();
|
|
2085
|
+
injectMobileCss(); // 移动端布局 CSS 始终注入(与余额开关解耦)
|
|
2066
2086
|
ctx.effect(() => ctx.locale.register(NS, {
|
|
2067
2087
|
zh,
|
|
2068
2088
|
en
|
|
2069
2089
|
}), "dsh-token-usage: dictionaries");
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2090
|
+
// 余额 chip:仅当"余额"模块开启时注册(禁用余额时保留移动布局)
|
|
2091
|
+
if (!(window.__dshLongModules && window.__dshLongModules.balance === false)) {
|
|
2092
|
+
ctx.slots.inject("conversation.composer.dock", () => ctx.slots.register({
|
|
2093
|
+
name: "conversation.composer.dock",
|
|
2094
|
+
id: "token-balance",
|
|
2095
|
+
order: -10,
|
|
2096
|
+
locale: NS,
|
|
2097
|
+
inject: () => ({})
|
|
2098
|
+
}, BalanceChip));
|
|
2099
|
+
}
|
|
2077
2100
|
}
|
|
2078
2101
|
return { apply, inject }
|
|
2079
2102
|
})()
|
|
@@ -2437,6 +2460,11 @@ window.__ModuleLoader__.load({
|
|
|
2437
2460
|
.dsh-turn-preview{width:min(360px,88vw);height:min(70vh,520px);top:50%!important;left:50%!important;transform:translate(-50%,-50%)!important}
|
|
2438
2461
|
|
|
2439
2462
|
}
|
|
2463
|
+
/* 会话导航开关(off 时隐藏轮次刻尺/手机把手):与 DSH 原生回合导航避免冲突 */
|
|
2464
|
+
html[data-dsh-turnruler="off"] .dsh-turn-ruler,
|
|
2465
|
+
html[data-dsh-turnruler="off"] .dsh-turn-phone-tab {
|
|
2466
|
+
display: none !important;
|
|
2467
|
+
}
|
|
2440
2468
|
`
|
|
2441
2469
|
const turnRulerPlugin = {
|
|
2442
2470
|
inject: [],
|
|
@@ -2467,6 +2495,11 @@ window.__ModuleLoader__.load({
|
|
|
2467
2495
|
let scrollAnim = 0
|
|
2468
2496
|
let scrollTarget = -1
|
|
2469
2497
|
let scrollFrom = 0
|
|
2498
|
+
// 会话导航开关:从 glass 配置读取 turnRuler;false=off 隐藏刻尺(避免与 DSH 原生回合导航冲突)
|
|
2499
|
+
;(() => {
|
|
2500
|
+
const setVis = (en) => { try { document.documentElement.setAttribute('data-dsh-turnruler', en ? 'on' : 'off') } catch (_) {} }
|
|
2501
|
+
fetch('/api/dsh-uploads/glass-config', { cache: 'no-store' }).then((r) => r.json()).then((b) => { if (b && b.cfg) setVis((b.cfg.modules && b.cfg.modules.turnRuler) !== false) }).catch(() => {})
|
|
2502
|
+
})()
|
|
2470
2503
|
|
|
2471
2504
|
const ensureRuler = () => {
|
|
2472
2505
|
if (ruler && ruler.isConnected) return ruler
|
|
@@ -3157,10 +3190,10 @@ window.__ModuleLoader__.load({
|
|
|
3157
3190
|
backdrop-filter: blur(var(--dsh-glass-blur, 16px)) !important;
|
|
3158
3191
|
-webkit-backdrop-filter: blur(var(--dsh-glass-blur, 16px)) !important;
|
|
3159
3192
|
}
|
|
3160
|
-
/* 任务/todo 面板(lXshSW_root, 在 conversation.input.dock 内)
|
|
3193
|
+
/* 任务/todo 面板(lXshSW_root, 在 conversation.input.dock 内):半透明磨砂底+可读文字 */
|
|
3161
3194
|
html[data-dsh-glass="on"] [class*="lXshSW_root"],
|
|
3162
3195
|
html[data-dsh-glass="on"] [class*="lXshSW_body"] {
|
|
3163
|
-
background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3)
|
|
3196
|
+
background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 62%, transparent) !important;
|
|
3164
3197
|
color: var(--dsw-alias-label-primary) !important;
|
|
3165
3198
|
backdrop-filter: blur(var(--dsh-glass-blur, 16px)) !important;
|
|
3166
3199
|
-webkit-backdrop-filter: blur(var(--dsh-glass-blur, 16px)) !important;
|
|
@@ -3173,7 +3206,7 @@ window.__ModuleLoader__.load({
|
|
|
3173
3206
|
/* 排队消息/等待任务面板(_7yHdaG_dock/panel, 在 conversation.input.dock 内):不透明/磨砂底+可读文字 */
|
|
3174
3207
|
html[data-dsh-glass="on"] [class*="_7yHdaG_panel"],
|
|
3175
3208
|
html[data-dsh-glass="on"] [class*="_7yHdaG_dock"] {
|
|
3176
|
-
background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3)
|
|
3209
|
+
background-color: color-mix(in srgb, var(--dsw-specific-input-major, #e8edf3) 62%, transparent) !important;
|
|
3177
3210
|
color: var(--dsw-alias-label-primary) !important;
|
|
3178
3211
|
backdrop-filter: blur(var(--dsh-glass-blur, 16px)) !important;
|
|
3179
3212
|
-webkit-backdrop-filter: blur(var(--dsh-glass-blur, 16px)) !important;
|
|
@@ -3272,6 +3305,8 @@ window.__ModuleLoader__.load({
|
|
|
3272
3305
|
--dsw-alias-label-tertiary: #50545b !important;
|
|
3273
3306
|
}
|
|
3274
3307
|
}
|
|
3308
|
+
`
|
|
3309
|
+
const GLASS_LAYOUT_CSS = `
|
|
3275
3310
|
.dsh-glass-section{display:flex;flex-direction:column;gap:16px;min-width:0;padding:4px 2px 24px;color:var(--dsw-alias-label-primary)}
|
|
3276
3311
|
.dsh-glass-head{display:flex;align-items:center;justify-content:space-between;gap:16px}
|
|
3277
3312
|
.dsh-glass-head h2{margin:0;font-size:20px;line-height:28px}
|
|
@@ -3346,6 +3381,80 @@ window.__ModuleLoader__.load({
|
|
|
3346
3381
|
),
|
|
3347
3382
|
)
|
|
3348
3383
|
}
|
|
3384
|
+
// 「dsh-long」设置区:各模块开关(独立、移动端干净),避免把开关塞进 RA-Span
|
|
3385
|
+
const DSH_LONG_MODULES = [
|
|
3386
|
+
['glass', 'RA-Span'],
|
|
3387
|
+
['turnRuler', '会话导航(轮次刻尺)'],
|
|
3388
|
+
['uploadAttach', '附件上传(回形针)'],
|
|
3389
|
+
['uploadDragDrop', '附件拖放上传'],
|
|
3390
|
+
['uploadPaste', '附件粘贴上传'],
|
|
3391
|
+
['uploadPreview', '上传文件预览/管理'],
|
|
3392
|
+
['skillDocs', '技能文档'],
|
|
3393
|
+
['balance', '账户余额'],
|
|
3394
|
+
['workspace', '输出文件预览/管理'],
|
|
3395
|
+
['mobile', '移动端布局'],
|
|
3396
|
+
]
|
|
3397
|
+
const DshLongSettingsSection = () => {
|
|
3398
|
+
const [state, setState] = React.useState({ loading: true, saving: false, error: '', modules: null, patches: null })
|
|
3399
|
+
const load = React.useCallback(async () => {
|
|
3400
|
+
setState((s) => ({ ...s, loading: true, error: '', patches: null }))
|
|
3401
|
+
try {
|
|
3402
|
+
const [r, pr] = await Promise.all([
|
|
3403
|
+
fetch('/api/dsh-uploads/glass-config', { cache: 'no-store' }),
|
|
3404
|
+
fetch('/api/dsh-uploads/patch-status', { cache: 'no-store' }),
|
|
3405
|
+
])
|
|
3406
|
+
const b = await r.json()
|
|
3407
|
+
if (!r.ok) throw new Error(b.error || ('HTTP ' + r.status))
|
|
3408
|
+
let patches = null
|
|
3409
|
+
try { const pb = await pr.json(); patches = pb.patches } catch (_) {}
|
|
3410
|
+
setState((s) => ({ ...s, loading: false, modules: b.cfg.modules || {}, patches }))
|
|
3411
|
+
} catch (e) { setState((s) => ({ ...s, loading: false, error: String((e && e.message) || e) })) }
|
|
3412
|
+
}, [])
|
|
3413
|
+
React.useEffect(() => { load(); }, [load])
|
|
3414
|
+
const setMod = React.useCallback((k, v) => setState((s) => ({ ...s, modules: { ...(s.modules || {}), [k]: v } })), [])
|
|
3415
|
+
const save = React.useCallback(async () => {
|
|
3416
|
+
setState((s) => ({ ...s, saving: true, error: '' }))
|
|
3417
|
+
try {
|
|
3418
|
+
const r = await fetch('/api/dsh-uploads/glass-config', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ modules: state.modules }) })
|
|
3419
|
+
const b = await r.json()
|
|
3420
|
+
if (!r.ok) throw new Error(b.error || ('HTTP ' + r.status))
|
|
3421
|
+
window.__dshLongModules = state.modules
|
|
3422
|
+
try { localStorage.setItem('dsh-long:modules', JSON.stringify(state.modules)) } catch (e) {}
|
|
3423
|
+
setState((s) => ({ ...s, saving: false, error: '' }))
|
|
3424
|
+
} catch (e) { setState((s) => ({ ...s, saving: false, error: String((e && e.message) || e) })) }
|
|
3425
|
+
}, [state.modules])
|
|
3426
|
+
const PATCH_LABELS = { trustedHosts: 'privileged 信任 trustedHosts', loopback: '反代域名视为 loopback', heartbeat: 'WebSocket 心跳' }
|
|
3427
|
+
const pstat = (st) => st === '已打' ? { text: '已打', cls: 'ok' } : (st === '原生无需' ? { text: '原生无需', cls: 'na' } : { text: '未打', cls: 'warn' })
|
|
3428
|
+
if (state.loading) return React.createElement('div', { className: 'dsh-glass-section' }, '加载中…')
|
|
3429
|
+
return React.createElement('div', { className: 'dsh-glass-section' },
|
|
3430
|
+
React.createElement('div', { className: 'dsh-glass-head' },
|
|
3431
|
+
React.createElement('div', null, React.createElement('h2', null, 'dsh-long'), React.createElement('p', null, '各模块开关:关掉某模块即禁用其功能(若新版 DSH 自带某项,可关掉插件对应功能避免冲突)。')),
|
|
3432
|
+
),
|
|
3433
|
+
state.error ? React.createElement('div', { className: 'dsh-glass-error' }, state.error) : null,
|
|
3434
|
+
React.createElement('div', { className: 'dsh-glass-card' },
|
|
3435
|
+
DSH_LONG_MODULES.map(([k, label]) => React.createElement('div', { key: k, className: 'dsh-glass-row' },
|
|
3436
|
+
React.createElement('label', null, label),
|
|
3437
|
+
React.createElement('label', { className: 'dsh-glass-switch' }, React.createElement('input', { type: 'checkbox', checked: (state.modules ? state.modules[k] !== false : true), onChange: (e) => setMod(k, e.target.checked) }), (state.modules ? state.modules[k] !== false : true) ? '已开启' : '已关闭'),
|
|
3438
|
+
)),
|
|
3439
|
+
React.createElement('div', { className: 'dsh-glass-actions' },
|
|
3440
|
+
React.createElement('button', { type: 'button', onClick: load }, '重置'),
|
|
3441
|
+
React.createElement('button', { type: 'button', className: 'primary', disabled: state.saving, onClick: save }, state.saving ? '保存中…' : '保存生效'),
|
|
3442
|
+
),
|
|
3443
|
+
),
|
|
3444
|
+
state.patches ? React.createElement('div', { className: 'dsh-glass-card' },
|
|
3445
|
+
React.createElement('div', { className: 'dsh-glass-zone-title' }, '补丁状态(只读)'),
|
|
3446
|
+
typeof state.patches === 'string' ? React.createElement('p', { className: 'dsh-glass-note' }, state.patches)
|
|
3447
|
+
: Object.keys(PATCH_LABELS).map((k) => {
|
|
3448
|
+
const st = state.patches[k]
|
|
3449
|
+
const info = pstat(st)
|
|
3450
|
+
return React.createElement('div', { key: k, className: 'dsh-glass-row' },
|
|
3451
|
+
React.createElement('label', null, PATCH_LABELS[k]),
|
|
3452
|
+
React.createElement('span', { className: 'dsh-glass-val' + (info.cls === 'ok' ? '' : (info.cls === 'na' ? ' dsh-glass-na' : ' dsh-glass-warn')) }, info.text),
|
|
3453
|
+
)
|
|
3454
|
+
})
|
|
3455
|
+
) : null,
|
|
3456
|
+
)
|
|
3457
|
+
}
|
|
3349
3458
|
const GlassSettingsSection = () => {
|
|
3350
3459
|
const [state, setState] = React.useState({ loading: true, saving: false, error: '', cfg: null, bgImage: '', bgList: [] })
|
|
3351
3460
|
const load = React.useCallback(async () => {
|
|
@@ -3501,7 +3610,7 @@ window.__ModuleLoader__.load({
|
|
|
3501
3610
|
const style = document.createElement('style')
|
|
3502
3611
|
style.dataset.plugin = 'dsh-long-plugins'
|
|
3503
3612
|
style.dataset.pluginCss = 'dsh-long-plugins/glass'
|
|
3504
|
-
style.textContent = GLASS_CSS
|
|
3613
|
+
style.textContent = GLASS_CSS // 玻璃视觉规则(仅 RA-Span 开启时注入)
|
|
3505
3614
|
document.head.appendChild(style)
|
|
3506
3615
|
return () => style.remove()
|
|
3507
3616
|
}, 'dsh-long-plugins: glass styles')
|
|
@@ -3765,6 +3874,9 @@ window.__ModuleLoader__.load({
|
|
|
3765
3874
|
try {
|
|
3766
3875
|
const tk = dshDark() ? 'dark' : 'light'
|
|
3767
3876
|
root.setAttribute('data-dsh-theme', tk)
|
|
3877
|
+
root.setAttribute('data-dsh-turnruler', ((c.modules && c.modules.turnRuler) !== false) ? 'on' : 'off')
|
|
3878
|
+
window.__dshLongModules = c.modules || {}
|
|
3879
|
+
try { localStorage.setItem('dsh-long:modules', JSON.stringify(window.__dshLongModules)) } catch (e) {}
|
|
3768
3880
|
const bt = bgTint(tk)
|
|
3769
3881
|
const v = `rgba(${hexToRgb(bt.color)},${bt.mask})`
|
|
3770
3882
|
root.style.setProperty('--dsh-glass-bg-zone', v)
|
|
@@ -3867,7 +3979,17 @@ window.__ModuleLoader__.load({
|
|
|
3867
3979
|
...glassPlugin.inject,
|
|
3868
3980
|
]))
|
|
3869
3981
|
|
|
3982
|
+
// 模块开关缓存:页面加载时同步读取(让 main apply 的 modEnabled 门控生效);
|
|
3983
|
+
// 配置加载(applyTheme)/保存(dsh-long 保存)后更新。开关在下次刷新生效。
|
|
3984
|
+
try {
|
|
3985
|
+
const _m = localStorage.getItem('dsh-long:modules')
|
|
3986
|
+
if (_m) window.__dshLongModules = JSON.parse(_m)
|
|
3987
|
+
} catch (_) {}
|
|
3988
|
+
// 全局模块开关查询(供各组件/apply 内部判断)
|
|
3989
|
+
window.__dshLongMod = (n) => (window.__dshLongModules ? window.__dshLongModules[n] !== false : true)
|
|
3990
|
+
|
|
3870
3991
|
function apply(ctx) {
|
|
3992
|
+
const modEnabled = (n) => (window.__dshLongModules ? window.__dshLongModules[n] !== false : true)
|
|
3871
3993
|
const safeApply = (label, fn) => {
|
|
3872
3994
|
try {
|
|
3873
3995
|
fn(ctx)
|
|
@@ -3875,13 +3997,24 @@ window.__ModuleLoader__.load({
|
|
|
3875
3997
|
console.error('[dsh-long-plugins] ' + label + ' apply failed:', error)
|
|
3876
3998
|
}
|
|
3877
3999
|
}
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
4000
|
+
// 「dsh-long」设置区(模块开关+补丁状态)始终注册——独立于任何模块开关, 否则禁用 RA-Span 会连带它一起消失
|
|
4001
|
+
ctx.slots.inject('settings.section', () => ctx.slots.register({ name: 'settings.section', id: 'dsh-long', order: 60, label: 'dsh-long' }, DshLongSettingsSection))
|
|
4002
|
+
// .dsh-glass-* 布局类(设置区排版)的 CSS 始终注入(不随 glass 开关丢失; 体积小)
|
|
4003
|
+
ctx.effect(() => {
|
|
4004
|
+
const style = document.createElement('style')
|
|
4005
|
+
style.dataset.plugin = 'dsh-long-plugins'
|
|
4006
|
+
style.dataset.pluginCss = 'dsh-long-plugins/glass-layout'
|
|
4007
|
+
style.textContent = GLASS_LAYOUT_CSS
|
|
4008
|
+
document.head.appendChild(style)
|
|
4009
|
+
return () => style.remove()
|
|
4010
|
+
}, 'dsh-long-plugins: glass layout css (always)')
|
|
4011
|
+
safeApply('upload', (c) => uploadPlugin.apply(c)) // 上传子功能(预览/附件/拖放/粘贴)在插件内部按模块开关
|
|
4012
|
+
if (modEnabled('skillDocs')) safeApply('skill-docs', (c) => skillDocsPlugin.apply(c))
|
|
4013
|
+
safeApply('token-usage', (c) => tokenUsagePlugin.apply(c)) // 余额 chip 开关在其内部; MOBILE_CSS 始终注入
|
|
4014
|
+
if (modEnabled('mobile')) safeApply('mobile-hamburger', (c) => mobilePlugin.apply(c))
|
|
4015
|
+
if (modEnabled('workspace')) safeApply('workspace-files', (c) => workspaceFilesPlugin.apply(c))
|
|
4016
|
+
if (modEnabled('turnRuler')) safeApply('turn-ruler', (c) => turnRulerPlugin.apply(c))
|
|
4017
|
+
if (modEnabled('glass')) safeApply('glass', (c) => glassPlugin.apply(c))
|
|
3885
4018
|
}
|
|
3886
4019
|
|
|
3887
4020
|
exports.apply = apply
|
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.4.1",
|
|
5
5
|
"entry": {
|
|
6
6
|
"name": "dsh-long-plugins",
|
|
7
7
|
"inject": [
|
package/lib/index.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* (which honours `config.trustedHosts`); file paths are resolved inside
|
|
13
13
|
* their configured root so traversal is impossible.
|
|
14
14
|
*/
|
|
15
|
-
import { createReadStream } from "node:fs";
|
|
15
|
+
import { createReadStream, readFileSync } from "node:fs";
|
|
16
16
|
import { readFile, writeFile } from "node:fs/promises";
|
|
17
17
|
import { link, lstat, mkdir, open, readdir, rename, stat, unlink } from "node:fs/promises";
|
|
18
18
|
import { homedir } from "node:os";
|
|
@@ -28,6 +28,8 @@ import ExcelJS from "exceljs";
|
|
|
28
28
|
// 插件根下的 client/vendor 目录(存放 docx-preview / jszip 前端库,供 docx 预览端点读取)。
|
|
29
29
|
// 用 module 级定位,让 createHandlers / 各 handler 都能访问(不依赖 apply 内的 PACKAGE_DIR)。
|
|
30
30
|
const VENDOR_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "..", "client", "vendor");
|
|
31
|
+
// 插件当前版本(读 package.json),用于「插件升级时」的配置迁移(如默认关闭回合导航/RA-Span)
|
|
32
|
+
const PLUGIN_VERSION = (() => { try { return JSON.parse(readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf8")).version || ""; } catch { return ""; } })();
|
|
31
33
|
|
|
32
34
|
export const name = "dsh-long-plugins";
|
|
33
35
|
|
|
@@ -1628,6 +1630,8 @@ window.addEventListener('resize',function(){ try{ if(fitMode) zoomFit(); else ap
|
|
|
1628
1630
|
bgTint: { light: { color: "#1a2332", mask: 0.28 }, dark: { color: "#1a2332", mask: 0.28 } },
|
|
1629
1631
|
inputBox: { light: { color: "", opacity: 1 }, dark: { color: "", opacity: 1 } },
|
|
1630
1632
|
zone: { session: { color: "#1a2332", mask: 0.45, opacity: 0.5 }, input: { color: "#1a2332", mask: 0.6, opacity: 0.55 } },
|
|
1633
|
+
// 各模块开关(「dsh-long」设置区):false=禁用该模块功能(避免与 DSH 新版自带功能冲突/按需关闭)
|
|
1634
|
+
modules: { glass: false, uploadAttach: true, uploadDragDrop: true, uploadPaste: true, uploadPreview: true, skillDocs: true, balance: true, mobile: true, workspace: true, turnRuler: false },
|
|
1631
1635
|
};
|
|
1632
1636
|
const glassDir = resolve(homedir(), ".dsh-long-plugins");
|
|
1633
1637
|
const glassFile = join(glassDir, "glass.json");
|
|
@@ -1659,6 +1663,16 @@ window.addEventListener('resize',function(){ try{ if(fitMode) zoomFit(); else ap
|
|
|
1659
1663
|
await writeFile(glassFile, JSON.stringify(cfg, null, 2), "utf8");
|
|
1660
1664
|
} catch (_) {}
|
|
1661
1665
|
}
|
|
1666
|
+
// 插件升级时:版本号变化 → 默认关闭「会话导航」「RA-Span」(避免与 DSH 新版自带功能冲突)
|
|
1667
|
+
if (PLUGIN_VERSION && cfg._pluginVersion !== PLUGIN_VERSION) {
|
|
1668
|
+
try {
|
|
1669
|
+
cfg.modules = { glass: false, uploadAttach: true, uploadDragDrop: true, uploadPaste: true, uploadPreview: true, skillDocs: true, balance: true, mobile: true, workspace: true, turnRuler: false, ...(cfg.modules || {}) };
|
|
1670
|
+
cfg.modules.glass = false;
|
|
1671
|
+
cfg.modules.turnRuler = false;
|
|
1672
|
+
cfg._pluginVersion = PLUGIN_VERSION;
|
|
1673
|
+
await writeFile(glassFile, JSON.stringify(cfg, null, 2), "utf8");
|
|
1674
|
+
} catch (_) {}
|
|
1675
|
+
}
|
|
1662
1676
|
return cfg;
|
|
1663
1677
|
} catch { return { ...GLASS_DEFAULTS }; }
|
|
1664
1678
|
}
|
|
@@ -1684,6 +1698,8 @@ window.addEventListener('resize',function(){ try{ if(fitMode) zoomFit(); else ap
|
|
|
1684
1698
|
bgTint: cfg.bgTint || { light: { color: "#1a2332", mask: 0.28 }, dark: { color: "#1a2332", mask: 0.28 } },
|
|
1685
1699
|
bgColor: cfg.bgColor || "#1a2332", bgMask: cfg.bgMask ?? 0.28,
|
|
1686
1700
|
bgBlur: cfg.bgBlur ?? 0, bgOpacity: cfg.bgOpacity ?? 1,
|
|
1701
|
+
turnRuler: (cfg.modules && cfg.modules.turnRuler) !== false,
|
|
1702
|
+
modules: { glass: false, uploadAttach: true, uploadDragDrop: true, uploadPaste: true, uploadPreview: true, skillDocs: true, balance: true, mobile: true, workspace: true, turnRuler: false, ...(cfg.modules || {}) },
|
|
1687
1703
|
bgImage: imgMeta,
|
|
1688
1704
|
},
|
|
1689
1705
|
bgImage: imgMeta,
|
|
@@ -1695,6 +1711,12 @@ window.addEventListener('resize',function(){ try{ if(fitMode) zoomFit(); else ap
|
|
|
1695
1711
|
const body = await readJsonBody(req, 4 * 1024 * 1024);
|
|
1696
1712
|
const current = await readGlassJSON();
|
|
1697
1713
|
const next = { ...current, ...sanitizeGlass(typeof body === "object" && body !== null ? body : {}) };
|
|
1714
|
+
// 各模块开关表:body.modules 里有的键按布尔合并
|
|
1715
|
+
if (body && typeof body.modules === "object" && body.modules !== null) {
|
|
1716
|
+
const m = { glass: true, uploadAttach: true, uploadDragDrop: true, uploadPaste: true, uploadPreview: true, skillDocs: true, balance: true, mobile: true, workspace: true, turnRuler: true, ...(current.modules || {}) };
|
|
1717
|
+
for (const k of Object.keys(body.modules)) m[k] = body.modules[k] !== false;
|
|
1718
|
+
next.modules = m;
|
|
1719
|
+
}
|
|
1698
1720
|
// 背景图改为"文件 + 引用": 用户上传的 data-URI 存入 current.uri, 配置只存 "current" 引用
|
|
1699
1721
|
if (typeof next.bgImage === "string" && next.bgImage.startsWith("data:image/")) {
|
|
1700
1722
|
try {
|
|
@@ -1754,8 +1776,40 @@ window.addEventListener('resize',function(){ try{ if(fitMode) zoomFit(); else ap
|
|
|
1754
1776
|
sendError(res, error, onError);
|
|
1755
1777
|
}
|
|
1756
1778
|
};
|
|
1779
|
+
// 补丁状态:只读检测 DSH 核心各补丁的「已打 / 未打 / 原生无需」,供 dsh-long 设置区显示
|
|
1780
|
+
const patchStatus = async (req, res) => {
|
|
1781
|
+
try {
|
|
1782
|
+
requireTrusted(req);
|
|
1783
|
+
// 定位 DSH 核心 dsh-client-connection 目录(探测常见路径)
|
|
1784
|
+
let DIR = "";
|
|
1785
|
+
const candidates = [
|
|
1786
|
+
"/volume1/npm/global/lib/node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai/dsh-client-connection/lib",
|
|
1787
|
+
"/usr/local/lib/node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai/dsh-client-connection/lib",
|
|
1788
|
+
"/volume1/dsh/.dsh/node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai/dsh-client-connection/lib",
|
|
1789
|
+
"/usr/lib/node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai/dsh-client-connection/lib",
|
|
1790
|
+
];
|
|
1791
|
+
for (const c of candidates) { try { await stat(join(c, "index.js")); DIR = c; break; } catch (_) {} }
|
|
1792
|
+
if (!DIR) { sendJson(res, 200, { ok: true, patches: null, error: "未找到 DSH 核心 dsh-client-connection 目录" }); return; }
|
|
1793
|
+
const IDX = join(DIR, "index.js");
|
|
1794
|
+
const CLI = join(DIR, "client.js");
|
|
1795
|
+
const idx = await readFile(IDX, "utf8");
|
|
1796
|
+
let cli = "";
|
|
1797
|
+
try { cli = await readFile(CLI, "utf8"); } catch (_) {}
|
|
1798
|
+
const mark1 = "PRIVILEGED_METHODS.has(method) && !isTrustedApiRequest(request, trustedHosts)";
|
|
1799
|
+
const mark2 = "isLoopback: pageLocation === void 0 || isLoopbackHostname(pageLocation.hostname) || [";
|
|
1800
|
+
const mark3 = "WEBSOCKET_HEARTBEAT_MS";
|
|
1801
|
+
const patches = {
|
|
1802
|
+
trustedHosts: idx.includes(mark1) ? "已打" : "未打",
|
|
1803
|
+
loopback: cli.includes(mark2) ? "已打" : "未打",
|
|
1804
|
+
heartbeat: idx.includes(mark3) ? "已打" : (idx.includes("socket.ping()") ? "原生无需" : "未打"),
|
|
1805
|
+
};
|
|
1806
|
+
sendJson(res, 200, { ok: true, patches, dir: DIR });
|
|
1807
|
+
} catch (error) {
|
|
1808
|
+
sendError(res, error, onError);
|
|
1809
|
+
}
|
|
1810
|
+
};
|
|
1757
1811
|
|
|
1758
|
-
return { root, maxFileBytes, totalMaxBytes, api, download, preview, workspaceList, workspaceFile, workspacePreview, workspaceBrowse, workspaceDelete, workspaceRename, workspaceSave, docxPreviewPage, docxPreviewAsset, pptxPreviewPage, xlsxPreviewPage, xlsxPreviewAsset, glassConfig, glassBackgrounds, glassBackground };
|
|
1812
|
+
return { root, maxFileBytes, totalMaxBytes, api, download, preview, workspaceList, workspaceFile, workspacePreview, workspaceBrowse, workspaceDelete, workspaceRename, workspaceSave, docxPreviewPage, docxPreviewAsset, pptxPreviewPage, xlsxPreviewPage, xlsxPreviewAsset, glassConfig, glassBackgrounds, glassBackground, patchStatus };
|
|
1759
1813
|
}
|
|
1760
1814
|
|
|
1761
1815
|
/** 人类可读文件大小。 */
|
|
@@ -2672,6 +2726,12 @@ export async function apply(ctx, config = {}) {
|
|
|
2672
2726
|
handler: handlers.glassBackground,
|
|
2673
2727
|
}), "dsh-long-plugins: glass-ui background image route");
|
|
2674
2728
|
|
|
2729
|
+
ctx.effect(() => ctx.webServer.register({
|
|
2730
|
+
kind: "exact",
|
|
2731
|
+
path: "/api/dsh-uploads/patch-status",
|
|
2732
|
+
handler: handlers.patchStatus,
|
|
2733
|
+
}), "dsh-long-plugins: patch status route");
|
|
2734
|
+
|
|
2675
2735
|
ctx.effect(() => ctx.webServer.register({
|
|
2676
2736
|
kind: "exact",
|
|
2677
2737
|
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.4.1",
|
|
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",
|
|
@@ -42,11 +42,16 @@ if grep -q 'WEBSOCKET_HEARTBEAT_MS' "$IDX"; then
|
|
|
42
42
|
echo " 已打过,跳过 ✓"
|
|
43
43
|
exit 0
|
|
44
44
|
fi
|
|
45
|
+
# DSH 已原生心跳(如 v0.1.2-alpha 网关定期 ping):检测到已是 ping 心跳即跳过,避免重复/冲突
|
|
46
|
+
if grep -q 'socket\.ping()' "$IDX"; then
|
|
47
|
+
echo " 检测到 DSH 已有 WebSocket ping 心跳(原生或已打过),跳过 ✓"
|
|
48
|
+
exit 0
|
|
49
|
+
fi
|
|
45
50
|
|
|
46
|
-
# 待替换的原始代码未在 ->
|
|
51
|
+
# 待替换的原始代码未在 -> 可能是 DSH 已改逻辑(如 v0.1.2-alpha 会话视图/网关重构):温和跳过而不是硬失败
|
|
47
52
|
if ! grep -q 'var WebSocketDownlinks = class {' "$IDX"; then
|
|
48
|
-
echo "
|
|
49
|
-
exit
|
|
53
|
+
echo " 提示:未找到 WebSocketDownlinks 类,DSH 可能已改逻辑(如新版原生心跳),跳过本补丁(不再硬失败)" >&2
|
|
54
|
+
exit 0
|
|
50
55
|
fi
|
|
51
56
|
|
|
52
57
|
cp "$IDX" "$IDX.bak-heartbeat-$(date +%Y%m%d-%H%M)"
|
|
@@ -119,6 +119,16 @@ pnpm install # 或用探测到的 pnpm 绝对路径
|
|
|
119
119
|
# md2docxScript: /your/path/md2docx.py # 可选,默认用包内 lib/md2docx.py
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
+
### 4.5 设置插件默认:回合导航 + RA-Span 关闭(避免与 DSH 新版自带功能冲突 / 默认更简洁)
|
|
123
|
+
> DSH ≥ v0.1.2-alpha 已自带「紧凑回合导航」。新机默认把插件 `modules.turnRuler` 置 false(避免轮次刻尺冲突),`modules.glass`(RA-Span)也默认 false(要玻璃桌面可在「dsh-long」设置区手动开)。如目标 DSH 无原生回合导航,可在「dsh-long」手动开回。
|
|
124
|
+
```sh
|
|
125
|
+
GJ="${HOME:-/root}/.dsh-long-plugins/glass.json"
|
|
126
|
+
mkdir -p "$(dirname "$GJ")"
|
|
127
|
+
[ -f "$GJ" ] || echo '{"enabled":false,"blur":20,"bgImage":"","bgColor":"#1a2332","bgMask":0.28,"bgTint":{"light":{"color":"#1a2332","mask":0.28},"dark":{"color":"#1a2332","mask":0.28}},"inputBox":{"light":{"color":"","opacity":1},"dark":{"color":"","opacity":1}},"zone":{"session":{"color":"#1a2332","mask":0.45,"opacity":0.5},"input":{"color":"#1a2332","mask":0.6,"opacity":0.55}},"modules":{"glass":false,"uploadAttach":true,"uploadDragDrop":true,"uploadPaste":true,"uploadPreview":true,"skillDocs":true,"balance":true,"mobile":true,"workspace":true,"turnRuler":false}}' > "$GJ"
|
|
128
|
+
node -e "const fs=require('fs');const p=JSON.parse(fs.readFileSync(process.argv[1],'utf8'));p.modules=p.modules||{};p.modules.glass=false;p.modules.turnRuler=false;fs.writeFileSync(process.argv[1],JSON.stringify(p,null,2)+'\n')" "$GJ" \
|
|
129
|
+
&& echo " ✅ 已默认关闭插件 RA-Span(modules.glass=false) 与回合导航(modules.turnRuler=false)"
|
|
130
|
+
```
|
|
131
|
+
|
|
122
132
|
## 工作目录 / 上传目录匹配(重要决策点)
|
|
123
133
|
|
|
124
134
|
上传/工作区根**不是写死的**,靠环境变量推导:
|
|
@@ -176,6 +186,7 @@ setsid nohup <node绝对路径> --expose-internals --max-old-space-size=8192 \
|
|
|
176
186
|
> **md2docx 工具需新建会话后 agent 才加载**(会话工具集创建时固定),验证时开新会话。
|
|
177
187
|
6. 窄屏(手机)下预览窗按钮自动换行不截断。
|
|
178
188
|
7. 用 `dsh --profile <p> --dump-config` 核对组合树正确。
|
|
189
|
+
8. **「dsh-long」设置区**:设置面板出现「dsh-long」节,含**各模块开关**(RA-Span/会话导航/附件上传/拖放/粘贴/上传预览/技能文档/余额/移动端/输出文件)与**「补丁状态(只读)」**——显示 3 个 DSH 核心补丁的已打/未打/原生无需。模块开关存于 `~/.dsh-long-plugins/glass.json` 的 `modules` 字段,保存后下次刷新生效。
|
|
179
190
|
|
|
180
191
|
## 硬性安全边界(必须遵守)
|
|
181
192
|
- **不主动 `git push` / 打 tag / 发 Release**;需要发版本时停下用 `ask_user_question` 等用户确认。
|
|
@@ -60,6 +60,19 @@ sh <插件目录>/patches/dsh-client-connection-heartbeat.sh
|
|
|
60
60
|
cd "$DSH_HOME/profiles/web" && pnpm install
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
### 5.5 升级后自动禁用插件「回合导航」(避免与 DSH 新版自带回合导航冲突)
|
|
64
|
+
> DSH ≥ v0.1.2-alpha 已自带「紧凑回合导航」。若插件 turn-ruler 仍开启,两者同时操纵会话 DOM 可能冲突→前端崩。升级流程里**自动把插件 `modules.turnRuler` 置为 false**;如目标版无原生回合导航,可在「dsh-long」设置区手动开回来。
|
|
65
|
+
```sh
|
|
66
|
+
GJ="${HOME:-/root}/.dsh-long-plugins/glass.json"
|
|
67
|
+
if [ -f "$GJ" ]; then
|
|
68
|
+
node -e "const fs=require('fs');const p=JSON.parse(fs.readFileSync(process.argv[1],'utf8'));p.modules=p.modules||{};p.modules.turnRuler=false;fs.writeFileSync(process.argv[1],JSON.stringify(p,null,2)+'\n')" "$GJ" \
|
|
69
|
+
&& echo " ✅ 已自动关闭插件回合导航 (modules.turnRuler=false,可在 dsh-long 手动开回)" \
|
|
70
|
+
|| echo " ⚠️ 未能更新插件配置(可手动在 dsh-long 关掉「会话导航」)"
|
|
71
|
+
else
|
|
72
|
+
echo " (未找到插件配置, 跳过; 可手动在 dsh-long 关掉「会话导航」)"
|
|
73
|
+
fi
|
|
74
|
+
```
|
|
75
|
+
|
|
63
76
|
### 6. 重启 dsh + 强刷浏览器
|
|
64
77
|
```sh
|
|
65
78
|
/volume1/dsh/start.sh # 或按当前环境的重启方式
|
|
@@ -73,34 +86,6 @@ cd "$DSH_HOME/profiles/web" && pnpm install
|
|
|
73
86
|
4. 提问/审批窗口:弹出一个问题,等 60s+ 不再自己消失(心跳补丁生效)。
|
|
74
87
|
5. 插件设置面板「上传文件」「输出文件」「技能文档」都在。
|
|
75
88
|
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` | 白底回来 |
|
|
104
89
|
|
|
105
90
|
## 硬性安全边界(必须遵守)
|
|
106
91
|
- **不主动 push / 打 tag / 发 Release**;需要发布版本时停下用 ask_user_question 等确认。
|
|
@@ -114,5 +99,4 @@ dsh-long-plugins 的「RA-Span(统一桌面/玻璃)」**大量依赖 DSH 内
|
|
|
114
99
|
| 升级后设置页"未暴露命名空间" | 补丁2 被覆盖 | 重跑 apply-dsh-patches.sh |
|
|
115
100
|
| 升级后提问窗口自己消失 | 心跳补丁(补丁3)被覆盖 | 重跑 apply-dsh-patches.sh 或装插件补丁脚本 |
|
|
116
101
|
| 升级后插件不显示 | 插件安装副本未同步 | 步骤 5 `pnpm install` |
|
|
117
|
-
| 升级后 RA-Span 观感/配色不对(输入框重影、标签不变色、弹窗透明、统计字淡等) | DSH 混淆类名变了 | 见 RA-Span 校准节:强刷 + 逐项检查,让用户右键给出新类名,更新 `client/client.js` 对应选择器 |
|
|
118
102
|
| 按钮全没了/界面异常 | 浏览器缓存了旧前端 | 强刷 Ctrl/Cmd+Shift+R |
|