dsh-theme-palette 1.3.0 → 1.3.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.
Files changed (3) hide show
  1. package/README.md +46 -0
  2. package/client.js +71 -5
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -7,6 +7,48 @@ DSH Web / Desktop 的主题配色插件:**4 套配色 × 明暗两模式**,
7
7
 
8
8
  当前版本 **0.8.2**(0.8.1 按深色专项审计重做深色侧,0.8.2 把强调色换成设计稿标准值;见 §修订记录)。
9
9
 
10
+ ## 为什么配色会自己跳回默认(v1.3.1)
11
+
12
+ 现象:设了暖砂,用了一阵(跑模型操作、切窗口、长时间放着),**突然变成黑白**,但设置页里
13
+ 「暖砂」还是选中的。原因在宿主的 override 生命周期:
14
+
15
+ ```js
16
+ // ThemeRuntime(宿主)
17
+ overrideTokens(source, tokens) {
18
+ const layer = { seq: this.overrideSeq++, tokens: validateOverrides(source, tokens) }
19
+ this.overrides.set(source, layer)
20
+ this.publish()
21
+ return () => { if (this.overrides.get(source) !== layer) return; this.overrides.delete(source); this.publish() }
22
+ }
23
+ ```
24
+
25
+ 而插件侧拿到的是被 runner 包装过的版本 —— **每一层的 disposer 都被挂在调用方的 fiber 上**:
26
+
27
+ ```js
28
+ const dispose = Reflect.apply(method, target, [id, tokens])
29
+ ctx.effect(() => dispose, 'cordis-client-runner: dynamic theme override layer')
30
+ ```
31
+
32
+ 也就是说:**这一层的生命周期 = 插件 fiber 的生命周期**。插件被卸载/重载、或 fiber 的
33
+ effect 被清理时,层就没了;`composeActive` 在 `overrides.size === 0` 时直接返回内置主题,
34
+ 于是页面**静默**变回默认色 —— localStorage 里的选择还在,设置页自然还显示暖砂。
35
+
36
+ 会触发 fiber 重载的典型情况:动态插件被宿主重载(改过插件文件/profile、宿主自我更新)、
37
+ 渲染进程恢复、以及某些"模型操作"引起的插件重载。
38
+
39
+ **v1.3.1 的处理**:加了一个自愈环。窗口重新可见 / 获焦 / `pageshow` / `theme/change` /
40
+ 每 4 秒,回读 `body` 上的 5 个关键 token;不在了就把最后一层重放:
41
+
42
+ ```
43
+ [dsh-theme-palette] 配色层不见了(visibility),已重放第 1 次
44
+ ```
45
+
46
+ 设置页顶部会显示"配色层被宿主丢过一次,已自动恢复 N 次";健康时不产生任何写入。
47
+
48
+ **另一种可能**(值得先分辨):如果是 **localStorage 被清**(换 profile、清缓存),设置页会回到
49
+ 「黑白」选中态 —— 那种情况自愈救不回来,因为连"你想用哪套"都丢了。判据就是**设置页里选中的
50
+ 是不是你原来那套**。
51
+
10
52
  ## 设置页重排(v1.3.0)
11
53
 
12
54
  原来的设置页是六行"工程参数"堆在一起、每行都是同一种小药丸。v1.3.0 重做成四段:
@@ -570,6 +612,10 @@ dsh plugin --profile desktop add "E:\Documents\deepseek-harness\default-workspac
570
612
  - 苔绿强调 `155°→165°` —— 与底色(h≈152)从 2° 拉开到 13°,链接不再只靠明度识别
571
613
  - 深色对比度:正文 14.40–14.98、三级 4.79 起、链接 4.65 起(0 处低于下限)
572
614
 
615
+ **1.3.1**
616
+ - 自愈:override 层被宿主丢掉时(插件 fiber 重载等)自动重放,设置页显示恢复次数
617
+ - 触发点:visibilitychange / focus / pageshow / theme/change / 4s 定时;健康时零写入
618
+
573
619
  **1.3.0**
574
620
  - 设置页重排:2×2 缩略图卡片(随明暗切换)+ 段落式设置行 + 分段控件 + 版本徽标
575
621
  - 样式从内联改为独立注入的 `settings.css`(支持 hover / 窄屏 / `body[data-ds-dark-theme]`)
package/client.js CHANGED
@@ -27,7 +27,7 @@ window.__ModuleLoader__.load({
27
27
  const React = require('react')
28
28
  const h = React.createElement
29
29
 
30
- const PLUGIN_VERSION = '1.3.0'
30
+ const PLUGIN_VERSION = '1.3.1'
31
31
  const STORE_KEY = 'dsh-theme-palette.v1'
32
32
  const OVERRIDE_SOURCE = 'dsh-theme-palette'
33
33
 
@@ -1119,6 +1119,11 @@ body[data-ds-dark-theme] .dsh-tp-check{background:var(--t-accent-d);color:var(--
1119
1119
  row(txt.sidebar, txt.sidebarSub, seg(SIDEBAR_MODES, sidebarMode, props.setSidebarMode)),
1120
1120
  row(txt.rightbar, txt.rightbarSub, seg(RIGHTBAR_MODES, rightbarMode, props.setRightbarMode)),
1121
1121
  health && !health.ok && h('p', { className: 'dsh-tp-status is-bad' }, txt.bad),
1122
+ health && health.ok && health.reapplies > 0 && h(
1123
+ 'p',
1124
+ { className: 'dsh-tp-status' },
1125
+ (isZh() ? '配色层被宿主丢过一次,已自动恢复 ' : 'Palette layer was dropped by the host; restored ') + health.reapplies + (isZh() ? ' 次' : 'x'),
1126
+ ),
1122
1127
  h('button', {
1123
1128
  type: 'button',
1124
1129
  className: 'dsh-tp-more',
@@ -1210,7 +1215,13 @@ body[data-ds-dark-theme] .dsh-tp-check{background:var(--t-accent-d);color:var(--
1210
1215
 
1211
1216
  let disposeLayer = () => {}
1212
1217
  /** 按当前配色 + 侧栏档位重建覆盖层(换配色与换侧栏档位走同一条路)。 */
1213
- const applyAll = () => {
1218
+ // 最近一次成功应用的 token 表与原因:自愈时据此重放,也用来解释"为什么又变回去了"。
1219
+ let lastTokens = null
1220
+ let lastAppliedAt = 0
1221
+ let lastReason = 'init'
1222
+ let reapplyCount = 0
1223
+
1224
+ const applyAll = (reason) => {
1214
1225
  const scheme = SCHEMES.find((s) => s.id === current) || SCHEMES[0]
1215
1226
  const tokens = buildTokens(scheme, sidebarMode, textMode, rightbarMode, scrollMode, controlMode, mdMode)
1216
1227
  let next = () => {}
@@ -1226,8 +1237,38 @@ body[data-ds-dark-theme] .dsh-tp-check{background:var(--t-accent-d);color:var(--
1226
1237
  const prev = disposeLayer
1227
1238
  disposeLayer = next
1228
1239
  prev()
1240
+ if (reason !== undefined) lastReason = reason
1241
+ lastTokens = tokens
1242
+ lastAppliedAt = Date.now()
1229
1243
  scheduleProbe(tokens)
1230
1244
  }
1245
+ /**
1246
+ * 自愈。
1247
+ *
1248
+ * 宿主的 theme.overrideTokens 把每一层的 disposer 挂在**调用方 fiber** 上
1249
+ * (`ctx.effect(() => dispose, ...)`):插件被卸载/重载、或 fiber 的 effect 被清理时,
1250
+ * 这一层就没了 —— 表现正是"配色自己跳回默认",而 localStorage 里的选择还在。
1251
+ * 这里定期(以及窗口重新可见/获焦时)回读 body:token 不在了就把最后一层重放。
1252
+ */
1253
+ const ensureApplied = (reason) => {
1254
+ if (lastTokens === null) return
1255
+ if (Date.now() - lastAppliedAt < 900) return
1256
+ let missing = false
1257
+ try {
1258
+ const style = getComputedStyle(document.body)
1259
+ for (const name of TOKEN_PROBE) {
1260
+ if (!(name in lastTokens)) continue
1261
+ if (style.getPropertyValue(name).trim() === '') { missing = true; break }
1262
+ }
1263
+ } catch (err) {
1264
+ missing = true
1265
+ }
1266
+ if (!missing) return
1267
+ reapplyCount += 1
1268
+ console.info('[dsh-theme-palette] 配色层不见了(' + reason + '),已重放第 ' + reapplyCount + ' 次')
1269
+ applyAll('self-heal:' + reason)
1270
+ }
1271
+
1231
1272
  let health = null
1232
1273
  const healthListeners = new Set()
1233
1274
  const getHealth = () => health
@@ -1243,7 +1284,7 @@ body[data-ds-dark-theme] .dsh-tp-check{background:var(--t-accent-d);color:var(--
1243
1284
  const next = probeApplied(tokens)
1244
1285
  if (next === null) return
1245
1286
  if (health !== null && health.ok === next.ok && health.checked === next.checked) return
1246
- health = next
1287
+ health = { ...next, reapplies: reapplyCount }
1247
1288
  if (!next.ok) console.warn('[dsh-theme-palette] 有 token 没写进 body,宿主可能改名了')
1248
1289
  emitHealth()
1249
1290
  }
@@ -1321,11 +1362,36 @@ body[data-ds-dark-theme] .dsh-tp-check{background:var(--t-accent-d);color:var(--
1321
1362
  console.warn('[dsh-theme-palette] dev hook attach 失败:', err)
1322
1363
  }
1323
1364
 
1365
+ // 自愈的触发点:窗口回前台、获焦、页面恢复、主题事件、以及一个低频定时器。
1366
+ const heal = (why) => () => { try { ensureApplied(why) } catch (err) { /* 自愈失败不影响主流程 */ } }
1367
+ const healOnVisible = heal('visibility')
1368
+ const healOnFocus = heal('focus')
1369
+ const healOnShow = heal('pageshow')
1370
+ const healOnTheme = heal('theme-change')
1371
+ let healTimer = null
1372
+ try {
1373
+ document.addEventListener('visibilitychange', healOnVisible)
1374
+ window.addEventListener('focus', healOnFocus)
1375
+ window.addEventListener('pageshow', healOnShow)
1376
+ } catch (err) { /* 无 DOM 时跳过 */ }
1377
+ try { ctx.on('theme/change', healOnTheme) } catch (err) { /* 事件名变了就只靠定时器 */ }
1378
+ try { healTimer = setInterval(() => ensureApplied('interval'), 4000) } catch (err) { /* 无定时器环境 */ }
1379
+
1324
1380
  ctx.effect(() => {
1325
- applyAll()
1381
+ applyAll('boot')
1326
1382
  const removeStyle = installRightbarStyle()
1327
1383
  const removeSettings = installSettingsStyle()
1328
- return () => { disposeLayer(); removeStyle(); removeSettings() }
1384
+ return () => {
1385
+ disposeLayer()
1386
+ removeStyle()
1387
+ removeSettings()
1388
+ try {
1389
+ document.removeEventListener('visibilitychange', healOnVisible)
1390
+ window.removeEventListener('focus', healOnFocus)
1391
+ window.removeEventListener('pageshow', healOnShow)
1392
+ } catch (err) { /* 同上 */ }
1393
+ if (healTimer !== null) { clearInterval(healTimer); healTimer = null }
1394
+ }
1329
1395
  }, 'dsh-theme-palette: token layer + 右栏样式')
1330
1396
 
1331
1397
  ctx.slots.inject('settings.section', () =>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-theme-palette",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "DSH 主题配色:4 套配色 × 明暗两模式,OKLCH 色域安全重建 + 真实承载面对比度求解,可调强度与面板表面。",
5
5
  "type": "module",
6
6
  "main": "index.js",