dsh-theme-palette 1.8.2 → 1.8.3
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/CHANGELOG.md +16 -0
- package/client.js +83 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,22 @@
|
|
|
4
4
|
|
|
5
5
|
## 版本一览
|
|
6
6
|
|
|
7
|
+
### 1.8.3
|
|
8
|
+
补齐 token 覆盖缺口(按宿主 118 个 alias/specific 做传递闭包统计:76% 跟随;余下 28 个逐项判断该不该跟):
|
|
9
|
+
|
|
10
|
+
- **深色菜单**:`--dsw-specific-menu` 宿主带字面量兜底(深 `#303136f0`),只改 `menu-surface-fill` 不够,
|
|
11
|
+
深色菜单仍是冷灰 → 现在按配色给(浅 50/深 850,alpha 0.94)
|
|
12
|
+
- **品牌新色**:`--dsw-alias-brand-primary-new-…` 浅色写死 `#4176e6` 宿主蓝 → 两模式都归到强调色
|
|
13
|
+
- **文件 diff 温度调和**:6 个 `file-diff-*` 保留绿/红语义,色相往配色拉 16–22°、压低饱和;
|
|
14
|
+
断言「新增仍是绿色系、删除仍是红色系」+ 正文对 diff 底 ≥4.5
|
|
15
|
+
- **对比度矩阵加菜单面**:半透明面按画布合成后给出参考值,标 `※`(不计硬门),
|
|
16
|
+
UI 与 `palette-dev` 共用同一份 `CONTRAST_SURFACES`(含 token 面)
|
|
17
|
+
- **诊断导出**:矩阵区新增「复制诊断」,输出 settings + 对比度矩阵 JSON,便于提 issue
|
|
18
|
+
- **forced-colors**:核实我们改的都是 UA 强制色属性,系统高对比模式会自动接管,
|
|
19
|
+
因此**不加** `forced-color-adjust: none` 对抗(代码内已注明理由)
|
|
20
|
+
- 契约测试补 `--dsw-specific-menu` / `--dsw-alias-brand-primary-new-…` / `--dsw-alias-file-diff-added-bg`
|
|
21
|
+
- 明确不跟:`bg-mask-*`(遮罩)、`border-inverted*`(反色边框)、onboarding(一次性引导)——刻意保留宿主原值
|
|
22
|
+
|
|
7
23
|
### 1.8.2
|
|
8
24
|
**暖砂字体体系修正**(用户反馈“和其他两套不一样,尤其字体颜色”):
|
|
9
25
|
|
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.8.
|
|
30
|
+
const PLUGIN_VERSION = '1.8.3'
|
|
31
31
|
const STORE_KEY = 'dsh-theme-palette.v1'
|
|
32
32
|
const OVERRIDE_SOURCE = 'dsh-theme-palette'
|
|
33
33
|
|
|
@@ -240,6 +240,10 @@ window.__ModuleLoader__.load({
|
|
|
240
240
|
{ id: '875', mode: 'dark', scope: true },
|
|
241
241
|
{ id: '850', mode: 'dark', scope: true },
|
|
242
242
|
{ id: '800', mode: 'dark', scope: true },
|
|
243
|
+
// 菜单是半透明面,实际颜色取决于 backdrop;这里按"合成在画布上"给出参考值,
|
|
244
|
+
// 标 scope:false(不计硬门),并在 UI 用 ※ 注明依赖 backdrop
|
|
245
|
+
{ id: 'menuL', label: 'menu', mode: 'light', scope: false, token: '--dsw-specific-menu' },
|
|
246
|
+
{ id: 'menuD', label: 'menu', mode: 'dark', scope: false, token: '--dsw-specific-menu' },
|
|
243
247
|
]
|
|
244
248
|
const CONTRAST_ROLES = [
|
|
245
249
|
{ id: 'main', step: { light: '1000', dark: '50' } },
|
|
@@ -437,6 +441,26 @@ window.__ModuleLoader__.load({
|
|
|
437
441
|
return hex + Math.round(clamp(alpha, 0, 1) * 255).toString(16).padStart(2, '0')
|
|
438
442
|
}
|
|
439
443
|
|
|
444
|
+
/** 8 位 hex(带 alpha)按「覆盖在 bg 上」合成为不透明色;不带 alpha 时原样返回。 */
|
|
445
|
+
function flatten(hex, bgHex) {
|
|
446
|
+
if (typeof hex !== 'string' || hex.length !== 9) return hex
|
|
447
|
+
const fg = hexToRgb(hex.slice(0, 7))
|
|
448
|
+
const bg = hexToRgb(bgHex)
|
|
449
|
+
const a = parseInt(hex.slice(7, 9), 16) / 255
|
|
450
|
+
return rgbToHex(fg.map((v, i) => v * a + bg[i] * (1 - a)))
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* 语义色温度调和:保留色相为主(绿仍是绿、红仍是红),只把色相往配色的暖/冷方向
|
|
455
|
+
* 拉一小段、并压低饱和度。用于 file diff 这种大面积色块——不能整块换成主题色,
|
|
456
|
+
* 那会让「新增/删除」失去语义。
|
|
457
|
+
*/
|
|
458
|
+
function harmonize(hex, targetHue, pull, chromaScale) {
|
|
459
|
+
const [L, C, h] = toOklch(hex)
|
|
460
|
+
const dh = ((targetHue - h + 540) % 360) - 180
|
|
461
|
+
return fromOklch(L, C * chromaScale, h + dh * pull)
|
|
462
|
+
}
|
|
463
|
+
|
|
440
464
|
/**
|
|
441
465
|
* 区块底色微调:在 OKLab 里给亮度加 delta,同时按需削减色度。
|
|
442
466
|
*
|
|
@@ -716,6 +740,17 @@ window.__ModuleLoader__.load({
|
|
|
716
740
|
light: withAlpha(r.bluish['50'], 0.58),
|
|
717
741
|
dark: withAlpha(r.bluish['750'], 0.45),
|
|
718
742
|
},
|
|
743
|
+
// 宿主 `--dsw-specific-menu` 自带字面量兜底(浅 #f8f9faf0 / 深 #303136f0),
|
|
744
|
+
// 只改 menu-surface-fill 不够:深色菜单实际用的是它,会留着冷灰。这里一并对齐。
|
|
745
|
+
'--dsw-specific-menu': {
|
|
746
|
+
light: withAlpha(r.bluish['50'], 0.94),
|
|
747
|
+
dark: withAlpha(r.bluish['850'], 0.94),
|
|
748
|
+
},
|
|
749
|
+
// 浅色写死 #4176e6(宿主蓝),深色已引用 deepseek-450;两模式都归到强调色
|
|
750
|
+
'--dsw-alias-brand-primary-new-colorprimary-new-color': {
|
|
751
|
+
light: r.deepseek['500'],
|
|
752
|
+
dark: r.deepseek['400'],
|
|
753
|
+
},
|
|
719
754
|
'--dsw-alias-menu-group-header-fill': {
|
|
720
755
|
// 用 60 而不是 50:50 现在是「深色模式正文」的色度,拿去当浅色菜单头会偏艳
|
|
721
756
|
light: withAlpha(r.bluish['60'], 0.94),
|
|
@@ -936,6 +971,19 @@ window.__ModuleLoader__.load({
|
|
|
936
971
|
light: mixHex(ramps.deepseek['500'], ramps.bluish['50'], 0.05),
|
|
937
972
|
dark: mixHex(ramps.deepseek['400'], ramps.bluish['900'], 0.07),
|
|
938
973
|
}
|
|
974
|
+
/* ---- 文件 diff:宿主是固定冷绿/冷红的大面积色块,暖砂/苔绿下最显眼。
|
|
975
|
+
* 只做「温度调和」:保留绿=新增、红=删除的语义色相,往配色色相拉 16–22° 并压低饱和。 */
|
|
976
|
+
if (params.tinted) {
|
|
977
|
+
const diffHue = params.hue
|
|
978
|
+
const area = (hex) => harmonize(hex, diffHue, 0.16, 0.90)
|
|
979
|
+
const mark = (hex) => harmonize(hex, diffHue, 0.22, 0.95)
|
|
980
|
+
pairs['--dsw-alias-file-diff-added-bg'] = { light: area('#e6f4e7'), dark: area('#1f3124') }
|
|
981
|
+
pairs['--dsw-alias-file-diff-added-gutter'] = { light: area('#edf7ed'), dark: area('#132016') }
|
|
982
|
+
pairs['--dsw-alias-file-diff-added-marker'] = { light: mark('#01a241'), dark: mark('#41c977') }
|
|
983
|
+
pairs['--dsw-alias-file-diff-deleted-bg'] = { light: area('#fce6e2'), dark: area('#3c1f1b') }
|
|
984
|
+
pairs['--dsw-alias-file-diff-deleted-gutter'] = { light: area('#fdece9'), dark: area('#28130e') }
|
|
985
|
+
pairs['--dsw-alias-file-diff-deleted-marker'] = { light: mark('#ba2723'), dark: mark('#fa423e') }
|
|
986
|
+
}
|
|
939
987
|
/* ---- 语法高亮(1.8.2)----
|
|
940
988
|
* 宿主渲染器用 Shiki 的 `css-variables` 模式(className="shiki css-variables",
|
|
941
989
|
* 样式走 var(--shiki-*)),所以覆盖这些变量有效,不需要 !important。
|
|
@@ -1028,6 +1076,8 @@ window.__ModuleLoader__.load({
|
|
|
1028
1076
|
* 命中 ui-layout 的 `data-rightbar-col`(稳定语义钩子,不是哈希类名)。
|
|
1029
1077
|
* 全屏右栏时退回画布色 —— 那时右栏就是整块内容区,染成侧栏色会像只剩一条侧栏。
|
|
1030
1078
|
*/
|
|
1079
|
+
// 说明:本插件不额外处理 forced-colors —— 我们改的 color/background-color/border-color
|
|
1080
|
+
// 都在 UA 强制色列表内,系统高对比模式下会被自动接管,无需(也不应)用 forced-color-adjust 对抗。
|
|
1031
1081
|
const RIGHTBAR_CSS = [
|
|
1032
1082
|
'[data-rightbar-col]{background:var(--dshtp-rightbar-fill,var(--dsw-alias-bg-base))}',
|
|
1033
1083
|
'[data-rightbar-fullscreen] [data-rightbar-col]{background:var(--dsw-alias-bg-base)}',
|
|
@@ -1474,6 +1524,7 @@ body[data-ds-dark-theme] .dsh-tp-check{background:var(--t-accent-d);color:var(--
|
|
|
1474
1524
|
explainLink: '链接已为可读性压暗:{ratio}:1(目标 {target}:1)',
|
|
1475
1525
|
preview: '预览',
|
|
1476
1526
|
sample: '试试示例',
|
|
1527
|
+
copyDiag: '复制诊断',
|
|
1477
1528
|
contrast: '对比度',
|
|
1478
1529
|
contrastSub: '实测 / 目标(按承载面;※ 不计硬门·△ 表面受限)',
|
|
1479
1530
|
cMain: '正文',
|
|
@@ -1527,6 +1578,7 @@ body[data-ds-dark-theme] .dsh-tp-check{background:var(--t-accent-d);color:var(--
|
|
|
1527
1578
|
explainLink: '連結已為可讀性壓暗:{ratio}:1(目標 {target}:1)',
|
|
1528
1579
|
preview: '預覽',
|
|
1529
1580
|
sample: '試試示例',
|
|
1581
|
+
copyDiag: '複製診斷',
|
|
1530
1582
|
contrast: '對比度',
|
|
1531
1583
|
contrastSub: '實測 / 目標(依承載面;※ 不計硬門·△ 表面受限)',
|
|
1532
1584
|
cMain: '正文',
|
|
@@ -1580,6 +1632,7 @@ body[data-ds-dark-theme] .dsh-tp-check{background:var(--t-accent-d);color:var(--
|
|
|
1580
1632
|
explainLink: 'Link darkened for readability: {ratio}:1 (target {target}:1)',
|
|
1581
1633
|
preview: 'Preview',
|
|
1582
1634
|
sample: 'Try a sample',
|
|
1635
|
+
copyDiag: 'Copy diagnostics',
|
|
1583
1636
|
contrast: 'Contrast',
|
|
1584
1637
|
contrastSub: 'measured / target per surface (※ not gated · △ surface-limited)',
|
|
1585
1638
|
cMain: 'Body',
|
|
@@ -1633,6 +1686,7 @@ body[data-ds-dark-theme] .dsh-tp-check{background:var(--t-accent-d);color:var(--
|
|
|
1633
1686
|
explainLink: 'リンクを可読性のため暗くしました:{ratio}:1(目標 {target}:1)',
|
|
1634
1687
|
preview: 'プレビュー',
|
|
1635
1688
|
sample: 'サンプルを試す',
|
|
1689
|
+
copyDiag: '診断をコピー',
|
|
1636
1690
|
contrast: 'コントラスト',
|
|
1637
1691
|
contrastSub: '実測 / 目標(面ごと;※ 判定外・△ 面の上限)',
|
|
1638
1692
|
cMain: '本文',
|
|
@@ -1735,6 +1789,7 @@ body[data-ds-dark-theme] .dsh-tp-check{background:var(--t-accent-d);color:var(--
|
|
|
1735
1789
|
const [copyState, setCopyState] = React.useState(null)
|
|
1736
1790
|
const [advanced, setAdvanced] = React.useState(false)
|
|
1737
1791
|
const [matrixOpen, setMatrixOpen] = React.useState(false)
|
|
1792
|
+
const [diagState, setDiagState] = React.useState(null)
|
|
1738
1793
|
// 升级提示按版本只出现一次
|
|
1739
1794
|
const noticePending = React.useSyncExternalStore(props.subscribeNotice, props.getNoticePending)
|
|
1740
1795
|
// HEX 输入需要「草稿」态:非法值不能写进主题,但用户还在输入时也不该被立刻回滚。
|
|
@@ -1907,6 +1962,20 @@ body[data-ds-dark-theme] .dsh-tp-check{background:var(--t-accent-d);color:var(--
|
|
|
1907
1962
|
onClick: () => setMatrixOpen(!matrixOpen),
|
|
1908
1963
|
}, (matrixOpen ? '▾ ' : '▸ ') + txt.contrast),
|
|
1909
1964
|
h('span', { className: 'dsh-tp-matrix-sub' }, txt.contrastSub),
|
|
1965
|
+
h('div', { className: 'dsh-tp-field-row' },
|
|
1966
|
+
h('button', {
|
|
1967
|
+
type: 'button',
|
|
1968
|
+
className: 'dsh-tp-mini',
|
|
1969
|
+
onClick: async () => {
|
|
1970
|
+
try {
|
|
1971
|
+
if (!navigator.clipboard || typeof navigator.clipboard.writeText !== 'function') throw new Error('no clipboard')
|
|
1972
|
+
await navigator.clipboard.writeText(JSON.stringify(props.getDiagnostics(), null, 2))
|
|
1973
|
+
setDiagState('copied')
|
|
1974
|
+
} catch (err) { setDiagState('fail') }
|
|
1975
|
+
},
|
|
1976
|
+
}, txt.copyDiag),
|
|
1977
|
+
diagState && h('span', { className: 'dsh-tp-note', role: 'status', 'aria-live': 'polite' },
|
|
1978
|
+
diagState === 'copied' ? txt.copied : txt.copyFail)),
|
|
1910
1979
|
matrixOpen && (() => {
|
|
1911
1980
|
const report = props.getContrastReport()
|
|
1912
1981
|
return h(
|
|
@@ -1914,7 +1983,7 @@ body[data-ds-dark-theme] .dsh-tp-check{background:var(--t-accent-d);color:var(--
|
|
|
1914
1983
|
{ id: 'dsh-tp-matrix-body' },
|
|
1915
1984
|
h('thead', null, h('tr', null,
|
|
1916
1985
|
h('th', { scope: 'col' }, ''),
|
|
1917
|
-
CONTRAST_SURFACES.map((sf) => h('th', { key: sf.id, scope: 'col' }, sf.id + (sf.scope === false ? '※' : ''))))),
|
|
1986
|
+
CONTRAST_SURFACES.map((sf) => h('th', { key: sf.id, scope: 'col' }, (sf.label || sf.id) + (sf.scope === false ? '※' : ''))))),
|
|
1918
1987
|
h('tbody', null, report.rows.map((row) => h('tr', { key: row.role },
|
|
1919
1988
|
h('th', { scope: 'row' }, roleLabel[row.role] || row.role),
|
|
1920
1989
|
row.cells.map((cell) => {
|
|
@@ -2123,7 +2192,7 @@ body[data-ds-dark-theme] .dsh-tp-check{background:var(--t-accent-d);color:var(--
|
|
|
2123
2192
|
|
|
2124
2193
|
// 开发期接口:palette-dev.mjs 通过它拿到同一套引擎(避免「报表好看、上线不一样」)
|
|
2125
2194
|
if (DEV_OPT_IN && typeof window !== 'undefined' && typeof window.__DSH_THEME_PALETTE_DEV__ === 'function') {
|
|
2126
|
-
window.__DSH_THEME_PALETTE_DEV__({ buildTokens, SCHEMES, SIDEBAR_MODES, TEXT_MODES, RIGHTBAR_MODES, SCROLL_MODES, CONTROL_MODES, MD_MODES, HC_MODES, contrast, toOklch, fromOklch, rampsFor, floors, encodeConfig, decodeConfig, customScheme, readCustom, writeCustom, CONTRAST_SURFACES, CONTRAST_ROLES, settingsCss: SETTINGS_CSS, cardColors })
|
|
2195
|
+
window.__DSH_THEME_PALETTE_DEV__({ buildTokens, SCHEMES, SIDEBAR_MODES, TEXT_MODES, RIGHTBAR_MODES, SCROLL_MODES, CONTROL_MODES, MD_MODES, HC_MODES, contrast, toOklch, fromOklch, flatten, rampsFor, floors, encodeConfig, decodeConfig, customScheme, readCustom, writeCustom, CONTRAST_SURFACES, CONTRAST_ROLES, settingsCss: SETTINGS_CSS, cardColors })
|
|
2127
2196
|
}
|
|
2128
2197
|
|
|
2129
2198
|
return {
|
|
@@ -2429,6 +2498,13 @@ body[data-ds-dark-theme] .dsh-tp-check{background:var(--t-accent-d);color:var(--
|
|
|
2429
2498
|
linkTarget: floors(hc).link,
|
|
2430
2499
|
}
|
|
2431
2500
|
}
|
|
2501
|
+
/** 诊断导出:把当前配色与对比度矩阵打成可粘贴的 JSON(提 issue 用)。 */
|
|
2502
|
+
const getDiagnostics = () => ({
|
|
2503
|
+
plugin: 'dsh-theme-palette',
|
|
2504
|
+
version: PLUGIN_VERSION,
|
|
2505
|
+
settings: { palette: current, sidebar: sidebarMode, text: textMode, rightbar: rightbarMode, scroll: scrollMode, control: controlMode, md: mdMode, hc: hcMode, lang: langPref },
|
|
2506
|
+
report: getContrastReport(),
|
|
2507
|
+
})
|
|
2432
2508
|
const previewConfigCode = (code) => {
|
|
2433
2509
|
const cfg = decodeConfig(code)
|
|
2434
2510
|
return cfg ? { ok: true, config: cfg } : { ok: false }
|
|
@@ -2452,7 +2528,9 @@ body[data-ds-dark-theme] .dsh-tp-check{background:var(--t-accent-d);color:var(--
|
|
|
2452
2528
|
const fgHex = role.id === 'link'
|
|
2453
2529
|
? t['--dsw-alias-link'][sf.mode]
|
|
2454
2530
|
: t['--dsw-static-neutral-bluish-' + role.step[sf.mode]][sf.mode]
|
|
2455
|
-
const bg =
|
|
2531
|
+
const bg = sf.token
|
|
2532
|
+
? flatten(t[sf.token][sf.mode], t['--dsw-static-neutral-bluish-' + (sf.mode === 'light' ? '00' : '950')][sf.mode])
|
|
2533
|
+
: t['--dsw-static-neutral-bluish-' + sf.id][sf.mode]
|
|
2456
2534
|
const ratio = contrast(fgHex, bg)
|
|
2457
2535
|
const claim = sf.mode === 'light' ? F[role.id] : (role.id === 'link' ? F.linkDark : F[role.id])
|
|
2458
2536
|
const ceiling = contrast(sf.mode === 'light' ? '#000000' : '#ffffff', bg)
|
|
@@ -2627,6 +2705,7 @@ body[data-ds-dark-theme] .dsh-tp-check{background:var(--t-accent-d);color:var(--
|
|
|
2627
2705
|
previewConfigCode,
|
|
2628
2706
|
getSampleCode,
|
|
2629
2707
|
getContrastReport,
|
|
2708
|
+
getDiagnostics,
|
|
2630
2709
|
subscribeNotice,
|
|
2631
2710
|
getNoticePending,
|
|
2632
2711
|
dismissNotice,
|