dsh-theme-palette 1.10.0 → 1.11.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/CHANGELOG.md +15 -0
- package/README-es.md +1 -1
- package/README-hi.md +1 -1
- package/README-pt.md +1 -1
- package/README-zh.md +1 -1
- package/README.md +1 -1
- package/assets/screenshot-settings-custom.png +0 -0
- package/assets/screenshot-settings-dark.png +0 -0
- package/assets/screenshot-settings-hc.png +0 -0
- package/assets/screenshot-settings-light.png +0 -0
- package/assets/screenshots.meta.json +81 -0
- package/client.js +69 -27
- package/docs/DESIGN.md +7 -2
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@
|
|
|
4
4
|
|
|
5
5
|
## 版本一览
|
|
6
6
|
|
|
7
|
+
### 1.11.0
|
|
8
|
+
2026-10-27 全量审计(评分 89/100,报告见 [`AUDIT-2026-10-27.md`](AUDIT-2026-10-27.md))的 12 条问题全部修订。两条 P1 都是门面级小修;审计里有一处**自我修正**也如实落地:取色拖动的性能问题经实测降级为防御性优化。
|
|
9
|
+
|
|
10
|
+
- **npm 包内 README 截图修复(P1-1)**:`files` 此前不含 `assets/`,npmjs.com 上 README 的 4 张截图全部 404。现已入包(包体 140KB → 436KB)。选「打进包」而非「绝对 GitHub URL」:自包含、不依赖仓库地址、npm 与镜像双兼容。
|
|
11
|
+
- **界面语言回退改 en(P1-2)**:宿主语言不在 zh/ja/en 里(fr/de/es/pt/…)时,设置页从整页中文改为回落英文。`pickLocale` 进 dev 钩子导出,`check-audit` 第 21 组断言 fr → en。
|
|
12
|
+
- **深色注释(label-caption)过 Lc 闸(P2-3)**:原先深色侧只按浅色下限求解,实测 Lc 47.9–51.7,低于本插件给文字定的 Lc 60 线(三级同面 62–66、链接有 Lc 闸,唯独注释没有)。现标准档 Lc 60 / HC 档 70——HC 定 70 而非照抄链接的 75:HC 三级 Lc 落在 72–77,套 75 会反超三级破坏层级(实测暖砂 HC 原值 71.7 已达标故不变,靛蓝 HC 66.6 → 72.0)。抬完标准档注释 WCAG 5.73–6.19,仍低于三级的 6.4(层级断言钉住)。`check-audit` 第 4 组扩为 WCAG + Lc + 层级三重断言;DESIGN.md 口径表补「注释层」与「装饰层」两行(后者即 P3-12:label-dimmed/document-preview 按 3.0 装饰下限、有意不进承诺模型)。
|
|
13
|
+
- **取色拖动按帧合并(P2-4,防御性优化)**:取色器 `input` 在拖动中每帧多次触发,此前每次都全量重放覆盖层(宿主侧重写约 200 个 token 触发全页样式重算)。现在同帧多次取色合并到下一帧只重放一次、以最后一次为准;重置/派生/导入等离散动作保持同步。审计修正:插件侧构建实测仅约 0.05ms/次(60 次共 3ms),瓶颈在宿主侧重排——所以这是防御性优化而非实测缺陷修复。`check-selfheal` 新增第 6 场景(假宿主计数:帧内 +0、合并后 +1、末次生效),其沙箱的 localStorage 桩顺带从「恒返 null」改为 Map 实现(原桩让自定义色永远读回默认值,与真实宿主不符)。
|
|
14
|
+
- **CI(P2-5)**:新增 `.github/workflows/ci.yml`,push/PR 在 Node 22/24 跑 `npm run check` + `npm run report`;此前全部质量门只在本地/发版时跑。脚本只依赖 node 内建,无需 install;宿主契约在无 asar 的 CI 上自动 SKIP。
|
|
15
|
+
- **dev 钩子补导出(P2-6)**:`apcaContrast` / `raiseToLc` / `solveForeground` / `pickLocale` / `hostLocale` / `floorsDark` —— 外部工具复算 Lc 不必再自带一份 APCA 实现。
|
|
16
|
+
- **组合数口径统一为 1080(P3-10)**:五语 README 与 DESIGN.md 原写「2160 组(5 × 216 × 两档)」,公式把高对比算了两次(216 已含 hc 档),实际扫描 1080 组(1.9.10 撤右栏档前才是 2160)。
|
|
17
|
+
- **notice 文案版本同步断言(P3-11)**:`check-ui` 新增——四个语种的 `notice` 恰好 4 条且都含当前 `PLUGIN_VERSION`,升级漏改不会绿。
|
|
18
|
+
- **代码卫生(P3-7/8/9)**:短码编解码从废弃的 `escape`/`unescape` 换 `TextEncoder`/`TextDecoder`(产出逐字节相同,新旧短码互解码兼容,第 2 组旧码断言守着);`TEXT_TARGETS` 常量表从 `buildTokensRaw` 体内上提到模块级(紧随 `SURFACE_TARGETS`);`langPref` 声明先于引用它的 `getConfigCode` 闭包。
|
|
19
|
+
- **发版联动**:版本号 bump 至 1.11.0、四语 notice 换新、截图用 `npm run shots` 重拍(无头 Chrome/Edge + CDP 截 preview HTML,不需要 DSH 宿主——审计时「重拍需要宿主 GUI」的判断有误,发版时更正),`screenshots.meta.json` 的 `pluginVersion` 随之对齐。
|
|
20
|
+
- 复核:`npm run check` 六件套全绿(含新增断言);两档报表 exit 0,1080 组配置「未达标 0 / 低于 AA 4.5 = 0 / 硬失败 0」;`npm pack --dry-run` 确认 assets 入包(20 文件 / 435.6KB)。
|
|
21
|
+
|
|
7
22
|
### 1.10.0
|
|
8
23
|
按审计报告的改进清单做了六件事:色彩数学单测、高对比跟随系统、运行时覆盖报告、色觉缺陷守门、单色派生,另外把 display-P3 的可行性量清楚后决定**本期不做**。其中「色觉缺陷守门」第一次跑就抓到一处真问题(浅色链接在模拟下低于 AA),下限因此微调。
|
|
9
24
|
|
package/README-es.md
CHANGED
|
@@ -72,7 +72,7 @@ OKLCH(L, C, tono) ──► ajuste al gamut (búsqueda binaria en C) ──► h
|
|
|
72
72
|
- los mínimos de texto se exigen en tiempo de construcción: cuerpo ≥ 12:1 en claro y ≥ 10:1 en oscuro (alto contraste: 15:1), secundario ≥ 6.2 claro / 8.0 oscuro (HC 9 / 9.6), terciario/enlace ≥ 4.85 / 4.95 claro / 6.4 oscuro (HC 7 / 7.6), enlace con APCA Lc ≥ 60 oscuro (HC 75), texto en barra lateral ≥ 7, texto en burbuja ≥ 7;
|
|
73
73
|
- las superficies prometidas son 00/60 en claro y 950/875 en oscuro; las oscuras 800 (superficie de módulo) y 850 (sub-superficie) **no** respaldan el texto del cuerpo: el informe las cuenta aparte como «fuera de política» (ni cuentan como aprobado ni como fallo), y toda superficie sigue teniendo que superar el suelo AA de 4.5;
|
|
74
74
|
- el informe separa tres cosas: el objetivo declarado, el techo físico (lo que el blanco o el negro alcanzan en esa superficie) y la exención de política; 0.93 es solo margen del solucionador, para decidir si un objetivo queda por encima del techo;
|
|
75
|
-
- `node palette-dev.mjs` comprueba
|
|
75
|
+
- `node palette-dev.mjs` comprueba 1080 combinaciones (5 paletas × 216 ajustes, ambos niveles de contraste incluidos).
|
|
76
76
|
|
|
77
77
|
Las notas de diseño, el criterio por componente y las tablas de contraste están en [docs/DESIGN.md](docs/DESIGN.md). Historial: [CHANGELOG.md](CHANGELOG.md).
|
|
78
78
|
|
package/README-hi.md
CHANGED
|
@@ -72,7 +72,7 @@ OKLCH(L, C, ह्यू) ──► गैमट क्लैंप (C पर
|
|
|
72
72
|
- टेक्स्ट की न्यूनतम सीमा बनाते समय लागू: मुख्य लाइट में ≥ 12:1 और डार्क में ≥ 10:1 (उच्च-कंट्रास्ट: 15:1), द्वितीयक ≥ 6.2 लाइट / 8.0 डार्क (HC 9 / 9.6), तृतीयक/लिंक ≥ 4.85 / 4.95 लाइट / 6.4 डार्क (HC 7 / 7.6), लिंक APCA Lc ≥ 60 डार्क (HC 75), साइडबार में ≥ 7, बबल में ≥ 7;
|
|
73
73
|
- वादे की गई सतहें लाइट में 00/60 और डार्क में 950/875 हैं; डार्क 800 (मॉड्यूल सतह) और 850 (उप-सतह) मुख्य टेक्स्ट को नहीं सहनाते — रिपोर्ट उन्हें अलग «नीति से बाहर» के रूप में गिनती है (न तो पास, न ही फेल), और हर सतह को AA की न्यूनतम सीमा 4.5 से गुज़रना ही होता है;
|
|
74
74
|
- रिपोर्ट तीन बातें अलग रखती है: घोषित लक्ष्य, भौतिक सीमा (उस सतह पर सफ़ेद या काला कितना हासिल कर सकता है) और नीति छूट; 0.93 केवल सॉल्वर का मार्जिन है, इससे तय होता है कि लक्ष्य सीमा से ऊपर है या नहीं;
|
|
75
|
-
- `node palette-dev.mjs`
|
|
75
|
+
- `node palette-dev.mjs` 1080 संयोजन जाँचता है (5 पैलेट × 216 सेटिंग, दोनों कंट्रास्ट स्तर सहित)।
|
|
76
76
|
|
|
77
77
|
डिज़ाइन नोट्स, घटक-वार तर्क और कंट्रास्ट तालिकाएँ [docs/DESIGN.md](docs/DESIGN.md) में। इतिहास: [CHANGELOG.md](CHANGELOG.md)।
|
|
78
78
|
|
package/README-pt.md
CHANGED
|
@@ -72,7 +72,7 @@ OKLCH(L, C, matiz) ──► limite de gamut (busca binária em C) ──► hex
|
|
|
72
72
|
- os pisos de texto são exigidos na construção: corpo ≥ 12:1 no claro e ≥ 10:1 no escuro (alto contraste: 15:1), secundário ≥ 6.2 claro / 8.0 escuro (HC 9 / 9.6), terciário/link ≥ 4.85 / 4.95 claro / 6.4 escuro (HC 7 / 7.6), link com APCA Lc ≥ 60 escuro (HC 75), texto na barra lateral ≥ 7, texto no balão ≥ 7;
|
|
73
73
|
- as superfícies prometidas são 00/60 no claro e 950/875 no escuro; as escuras 800 (superfície de módulo) e 850 (sub-superfície) **não** sustentam o texto do corpo: o relatório as conta à parte como «fora de política» (nem contam como aprovado nem como falha), e toda superfície ainda precisa passar pelo piso AA de 4.5;
|
|
74
74
|
- o relatório separa três coisas: a meta declarada, o teto físico (o que o branco ou o preto alcançam nessa superfície) e a isenção de política; 0.93 é apenas margem do solucionador, usada para decidir se uma meta fica acima do teto;
|
|
75
|
-
- `node palette-dev.mjs` verifica
|
|
75
|
+
- `node palette-dev.mjs` verifica 1080 combinações (5 paletas × 216 ajustes, ambos os níveis de contraste incluídos).
|
|
76
76
|
|
|
77
77
|
Notas de design, critério por componente e tabelas de contraste em [docs/DESIGN.md](docs/DESIGN.md). Histórico: [CHANGELOG.md](CHANGELOG.md).
|
|
78
78
|
|
package/README-zh.md
CHANGED
|
@@ -73,7 +73,7 @@ OKLCH(L, C, hue) ──► 色域钳位(对 C 二分) ──► sRGB hex
|
|
|
73
73
|
- 文字下限在构建期强制:正文浅色 ≥ 12:1、深色 ≥ 10:1(高对比 15:1),次级 ≥ 6.2 浅 / 8.0 深(HC 9 / 9.6),三级/链接 ≥ 4.85 / 4.95 浅 / 6.4 深(HC 7 / 7.6),链接另过 APCA Lc ≥ 60 深 / 75 HC,侧栏内 ≥ 7,气泡内 ≥ 7;
|
|
74
74
|
- 承诺面是浅色 00/60 与深色 950/875;深色 800(模块面)与 850(次级深面)**不为正文兜底**,报表单列为「政策外」(既不算达标也不算失败),且每一档承载面仍须过 AA 底线 4.5;
|
|
75
75
|
- 报表把三件事分开:声明目标、物理上限(该承载面上黑或白前景能达到的对比度)、政策豁免;0.93 只是求解余量,仅用于判断目标是否高于上限;
|
|
76
|
-
- `node palette-dev.mjs` 扫全部
|
|
76
|
+
- `node palette-dev.mjs` 扫全部 1080 组配置(5 套配色 × 216 组设置,已含标准/高对比两档)并打印承载面报表,`--hc` 把主报表切到高对比档;硬失败时退出码 1。
|
|
77
77
|
|
|
78
78
|
逐组件设计取舍与对比度表见 [docs/DESIGN.md](docs/DESIGN.md),版本记录见 [CHANGELOG.md](CHANGELOG.md)。
|
|
79
79
|
|
package/README.md
CHANGED
|
@@ -73,7 +73,7 @@ OKLCH(L, C, hue) ──► gamut clamp (binary search on C) ──► sRGB hex
|
|
|
73
73
|
- text floors are enforced at build time: body ≥ 12:1 in light and ≥ 10:1 in dark (high contrast: 15:1), secondary ≥ 6.2 in light / 8.0 in dark (HC 9 / 9.6), tertiary/link ≥ 4.85 / 4.95 in light / 6.4 in dark (HC 7 / 7.6), link APCA Lc ≥ 60 in dark (HC 75), sidebar text ≥ 7, bubble text ≥ 7;
|
|
74
74
|
- the promised surfaces are light 00/60 and dark 950/875; dark 800 (module surface) and 850 (sub-surface) do **not** back body text — the report counts them apart as "out of policy" (neither pass nor fail), and every surface still has to clear the AA floor of 4.5;
|
|
75
75
|
- the report separates three things: the declared target, the physical ceiling (what black or white can reach on that surface) and the policy exemption; 0.93 is only a solver margin, used to decide whether a target sits above the ceiling;
|
|
76
|
-
-
|
|
76
|
+
- 1080 configuration combos (5 palettes × 216 settings, both contrast tiers included) are checked by `node palette-dev.mjs`.
|
|
77
77
|
|
|
78
78
|
Design notes, per-component rationale and the contrast tables live in [docs/DESIGN.md](docs/DESIGN.md). Release history: [CHANGELOG.md](CHANGELOG.md).
|
|
79
79
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"generatedAt": "2026-10-27T03:52:58.491Z",
|
|
3
|
+
"pluginVersion": "1.11.0",
|
|
4
|
+
"chrome": "Chrome/156.0.8078.48",
|
|
5
|
+
"width": 620,
|
|
6
|
+
"scale": 1,
|
|
7
|
+
"images": [
|
|
8
|
+
{
|
|
9
|
+
"file": "assets/screenshot-settings-light.png",
|
|
10
|
+
"html": "tools/preview-light.html",
|
|
11
|
+
"preview": [
|
|
12
|
+
"--palette",
|
|
13
|
+
"warm",
|
|
14
|
+
"--adv",
|
|
15
|
+
"0"
|
|
16
|
+
],
|
|
17
|
+
"htmlSha256": "0cebb53312c0c39018705b81ece89e2d62ab14e7073214535d2330b4a019bb60",
|
|
18
|
+
"cssWidth": 620,
|
|
19
|
+
"cssHeight": 888,
|
|
20
|
+
"width": 620,
|
|
21
|
+
"height": 888,
|
|
22
|
+
"bytes": 47503,
|
|
23
|
+
"sha256": "6378abcf6c4fc42f0a9f2d3f32ac2a9a1679534debe126a2d9e0dd8c9999495f"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"file": "assets/screenshot-settings-dark.png",
|
|
27
|
+
"html": "tools/preview-dark.html",
|
|
28
|
+
"preview": [
|
|
29
|
+
"--palette",
|
|
30
|
+
"warm",
|
|
31
|
+
"--adv",
|
|
32
|
+
"0"
|
|
33
|
+
],
|
|
34
|
+
"htmlSha256": "c1dcd522dc7af24d3b916b4e92001f47287839d9e507689d07df959da9ca8103",
|
|
35
|
+
"cssWidth": 620,
|
|
36
|
+
"cssHeight": 888,
|
|
37
|
+
"width": 620,
|
|
38
|
+
"height": 888,
|
|
39
|
+
"bytes": 48041,
|
|
40
|
+
"sha256": "688476198bab7c192eb9ead70a96afccb761b721f5cc5df3309879caddb21b07"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"file": "assets/screenshot-settings-custom.png",
|
|
44
|
+
"html": "tools/preview-light-customadv.html",
|
|
45
|
+
"preview": [
|
|
46
|
+
"--palette",
|
|
47
|
+
"custom",
|
|
48
|
+
"--adv",
|
|
49
|
+
"1",
|
|
50
|
+
"--out",
|
|
51
|
+
"customadv"
|
|
52
|
+
],
|
|
53
|
+
"htmlSha256": "e29b936a12a75f1b2e8047c46872cb3f192a9e3a32bddccb0226d74bc8ed18a2",
|
|
54
|
+
"cssWidth": 620,
|
|
55
|
+
"cssHeight": 1753,
|
|
56
|
+
"width": 620,
|
|
57
|
+
"height": 1753,
|
|
58
|
+
"bytes": 116461,
|
|
59
|
+
"sha256": "89a0b0d346e2b94bc6bbd6a43b1fba5a198e44628159a3770fafd00de3d0c054"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"file": "assets/screenshot-settings-hc.png",
|
|
63
|
+
"html": "tools/preview-light-hcadv.html",
|
|
64
|
+
"preview": [
|
|
65
|
+
"--hc",
|
|
66
|
+
"1",
|
|
67
|
+
"--adv",
|
|
68
|
+
"1",
|
|
69
|
+
"--out",
|
|
70
|
+
"hcadv"
|
|
71
|
+
],
|
|
72
|
+
"htmlSha256": "e737c6c6bcb4ca5edad80cdd04c49eee9c30bcbca396b779320731a6860639ec",
|
|
73
|
+
"cssWidth": 620,
|
|
74
|
+
"cssHeight": 1463,
|
|
75
|
+
"width": 620,
|
|
76
|
+
"height": 1463,
|
|
77
|
+
"bytes": 94432,
|
|
78
|
+
"sha256": "c4312f097c283a44ece12257710455a676ff2c0a054c59d1efc47b9c991d81a7"
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
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.
|
|
30
|
+
const PLUGIN_VERSION = '1.11.0'
|
|
31
31
|
const STORE_KEY = 'dsh-theme-palette.v1'
|
|
32
32
|
const OVERRIDE_SOURCE = 'dsh-theme-palette'
|
|
33
33
|
|
|
@@ -146,6 +146,19 @@ window.__ModuleLoader__.load({
|
|
|
146
146
|
moss: { '00': '#f9fbf8', '60': '#f5f7f2', '150': '#e0e7dd', '950': '#131b16', '875': '#1a241d', '850': '#1d2a22', '800': '#2f3f34' },
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
/* ---- 参考实现的 ink / link 精确对齐(1.9.0)----
|
|
150
|
+
* 参考的 ink 比求解结果更浅、更中性;这里直接取参考值,
|
|
151
|
+
* 但仍过一遍对比度下限:达不到的目标文字沿远离背景方向微调(只动明度,不动色相)。
|
|
152
|
+
* 三级文字在控件面上若参考本身不达 AA,会略微变深 —— 这是唯一允许偏离参考的地方。
|
|
153
|
+
* (审计 2026-10-27 P3-8:从 buildTokensRaw 体内上提 —— 它是不变的常量表,
|
|
154
|
+
* 放在函数里每次未命中缓存都要重新分配,且与 SURFACE_TARGETS 的组织不一致。)
|
|
155
|
+
*/
|
|
156
|
+
const TEXT_TARGETS = {
|
|
157
|
+
warm: { light: { 1000: '#2b2622', 700: '#6b625a', 600: '#8c8177', link: '#8a5233' }, dark: { 50: '#f5efe8', 300: '#c4b8ac', 400: '#9a8d80', link: '#e6c4a0' } },
|
|
158
|
+
indigo: { light: { 1000: '#1a2330', 700: '#516074', 600: '#74839a', link: '#345286' }, dark: { 50: '#eef3fa', 300: '#b7c5d8', 400: '#8a9bb2', link: '#a6bfe3' } },
|
|
159
|
+
moss: { light: { 1000: '#1f2a22', 700: '#55645a', 600: '#7a8a7e', link: '#406236' }, dark: { 50: '#eef5ef', 300: '#b8c8bc', 400: '#8ba091', link: '#aed0a2' } },
|
|
160
|
+
}
|
|
161
|
+
|
|
149
162
|
/* 对比度下限:主文本 16 / 次文本 6 / 三级文本 4.6(AA)/ 链接 4.6 */
|
|
150
163
|
/* 对比度下限:按**真实承载面**定(卡层/模块层比画布更难达标),不是按画布定。
|
|
151
164
|
正文 15.5 / 次级 6.2 / 三级 4.62(AA)/ 链接 4.62(AA) */
|
|
@@ -1257,12 +1270,23 @@ window.__ModuleLoader__.load({
|
|
|
1257
1270
|
// 注释/时间戳:既要跟随主题(色度由 midC 给),也要在画布/卡层上可读。
|
|
1258
1271
|
// 直接拿 400 号色在浅色下只有约 2.4:1,所以这里对 00/60 求解到 AA。
|
|
1259
1272
|
light: solveForeground(r.bluish, '400', [r.bluish['00'], r.bluish['60']], floors(hc).tertiary, 0.62),
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1273
|
+
/* 深色(审计 2026-10-27 P2-3):原先只按浅色下限(4.85)对 800/850 求解,
|
|
1274
|
+
* 实测 Lc 只有 48–52 —— 本插件给文字定的可读线是 Lc 60,三级文字同面有 62–66。
|
|
1275
|
+
* 深色侧的「WCAG 过线 ≠ 看得清」论证(见 MIN_SECONDARY_DARK)同样适用于注释。
|
|
1276
|
+
* 这里在 WCAG 求解之后再过一道 Lc 闸:
|
|
1277
|
+
* 标准档 60(与链接同线);HC 档 70 而不是照抄链接的 75 ——
|
|
1278
|
+
* HC 三级文字 Lc 落在 72–77,注释套 75 会反超三级(靛蓝 75 > 72),
|
|
1279
|
+
* 破坏「注释弱于三级」的层级。抬完后标准档注释 WCAG ≈ 5.6–6.0,仍低于三级的 6.4。 */
|
|
1280
|
+
dark: raiseToLc(
|
|
1281
|
+
solveForeground(
|
|
1282
|
+
r.bluish,
|
|
1283
|
+
'500',
|
|
1284
|
+
[r.bluish['800'], r.bluish['850']],
|
|
1285
|
+
Math.min(floors(hc).tertiary, contrast('#ffffff', r.bluish['800']) * 0.995),
|
|
1286
|
+
0.30,
|
|
1287
|
+
),
|
|
1263
1288
|
[r.bluish['800'], r.bluish['850']],
|
|
1264
|
-
|
|
1265
|
-
0.30,
|
|
1289
|
+
hc ? 70 : 60,
|
|
1266
1290
|
),
|
|
1267
1291
|
},
|
|
1268
1292
|
// 主人气泡(`--dsw-specific-bubble`)宿主指向 deepseek-50:那是「品牌色的极浅底」,
|
|
@@ -1684,16 +1708,8 @@ window.__ModuleLoader__.load({
|
|
|
1684
1708
|
if (!params.tinted && sidebarMode === 'light') {
|
|
1685
1709
|
pairs['--dsw-static-neutral-bluish-00'] = { light: '#fafafa', dark: BASE.bluish['00'] }
|
|
1686
1710
|
}
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
* 但仍过一遍对比度下限:达不到的目标文字沿远离背景方向微调(只动明度,不动色相)。
|
|
1690
|
-
* 三级文字在控件面上若参考本身不达 AA,会略微变深 —— 这是唯一允许偏离参考的地方。
|
|
1691
|
-
*/
|
|
1692
|
-
const TEXT_TARGETS = {
|
|
1693
|
-
warm: { light: { 1000: '#2b2622', 700: '#6b625a', 600: '#8c8177', link: '#8a5233' }, dark: { 50: '#f5efe8', 300: '#c4b8ac', 400: '#9a8d80', link: '#e6c4a0' } },
|
|
1694
|
-
indigo: { light: { 1000: '#1a2330', 700: '#516074', 600: '#74839a', link: '#345286' }, dark: { 50: '#eef3fa', 300: '#b7c5d8', 400: '#8a9bb2', link: '#a6bfe3' } },
|
|
1695
|
-
moss: { light: { 1000: '#1f2a22', 700: '#55645a', 600: '#7a8a7e', link: '#406236' }, dark: { 50: '#eef5ef', 300: '#b8c8bc', 400: '#8ba091', link: '#aed0a2' } },
|
|
1696
|
-
}
|
|
1711
|
+
// 参考墨色表在模块级(TEXT_TARGETS,紧随 SURFACE_TARGETS);自定义没有参考表,
|
|
1712
|
+
// 走下面的通用求解路径。
|
|
1697
1713
|
const TT = TEXT_TARGETS[scheme.id]
|
|
1698
1714
|
// 三套预置走参考目标;自定义没有参考表,但同样要拿到跟随主题的注释/文字
|
|
1699
1715
|
if (scheme.tinted) {
|
|
@@ -2184,7 +2200,12 @@ body[data-ds-dark-theme] .dsh-tp{color-scheme:dark}
|
|
|
2184
2200
|
x: { bg: custom.bg, text: custom.text, accent: custom.accent },
|
|
2185
2201
|
}
|
|
2186
2202
|
try {
|
|
2187
|
-
|
|
2203
|
+
// TextEncoder 产出与旧版 escape/encodeURIComponent 链逐字节相同的 UTF-8(审计 2026-10-27 P3-7):
|
|
2204
|
+
// 新旧版本的短码可以互相解码,check-audit 第 2 组的旧码断言守着这一点。
|
|
2205
|
+
const bytes = new TextEncoder().encode(JSON.stringify(raw))
|
|
2206
|
+
let binary = ''
|
|
2207
|
+
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i])
|
|
2208
|
+
const b64 = btoa(binary)
|
|
2188
2209
|
return CONFIG_PREFIX + b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
|
|
2189
2210
|
} catch (err) { return '' }
|
|
2190
2211
|
}
|
|
@@ -2198,7 +2219,10 @@ body[data-ds-dark-theme] .dsh-tp{color-scheme:dark}
|
|
|
2198
2219
|
try {
|
|
2199
2220
|
let b64 = text.slice(CONFIG_PREFIX.length).replace(/-/g, '+').replace(/_/g, '/')
|
|
2200
2221
|
while (b64.length % 4 !== 0) b64 += '='
|
|
2201
|
-
const
|
|
2222
|
+
const bin = atob(b64)
|
|
2223
|
+
const bytes = new Uint8Array(bin.length)
|
|
2224
|
+
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i)
|
|
2225
|
+
const raw = JSON.parse(new TextDecoder().decode(bytes))
|
|
2202
2226
|
if (!raw || raw.v !== 1) return null
|
|
2203
2227
|
const next = {
|
|
2204
2228
|
palette: SCHEMES.some((sc) => sc.id === raw.p) ? raw.p : null,
|
|
@@ -2261,7 +2285,9 @@ body[data-ds-dark-theme] .dsh-tp{color-scheme:dark}
|
|
|
2261
2285
|
|
|
2262
2286
|
function pickLocale(pref) {
|
|
2263
2287
|
if (pref !== 'auto') return pref
|
|
2264
|
-
|
|
2288
|
+
// 兜底必须是 en 而不是 zh-CN(审计 2026-10-27 P1-2):宿主语言不在
|
|
2289
|
+
// zh/ja/en 里(fr/de/es/pt/…)时给整页中文,对国际用户是最差的选择。
|
|
2290
|
+
return hostLocale() || 'en'
|
|
2265
2291
|
}
|
|
2266
2292
|
|
|
2267
2293
|
const TEXT = {
|
|
@@ -2291,7 +2317,7 @@ body[data-ds-dark-theme] .dsh-tp{color-scheme:dark}
|
|
|
2291
2317
|
derive: '由主色派生',
|
|
2292
2318
|
deriveHint: '用当前强调色的色相与色度推导底色与文字(明暗跟随当前模式)',
|
|
2293
2319
|
deriveNote: '「由主色派生」以强调色为唯一输入,自动算出底色与文字 —— 会覆盖你手动设置的底色与文字;派生之后仍可逐项手改。',
|
|
2294
|
-
notice: '1.
|
|
2320
|
+
notice: '1.11.0:深色模式下注释与时间戳更清晰(按感知对比度 Lc 加了一道闸);宿主语言不是中/英/日时界面改为回落英文;取色拖动按帧合并更流畅。',
|
|
2295
2321
|
dismiss: '知道了',
|
|
2296
2322
|
explainClamp: '强调色已归一化:{from} → {to}(保证按钮与勾选可读)',
|
|
2297
2323
|
explainLink: '链接已为可读性压暗:{ratio}:1(目标 {target}:1)',
|
|
@@ -2359,7 +2385,7 @@ body[data-ds-dark-theme] .dsh-tp{color-scheme:dark}
|
|
|
2359
2385
|
derive: '由主色派生',
|
|
2360
2386
|
deriveHint: '用目前強調色的色相與彩度推導底色與文字(明暗跟隨目前模式)',
|
|
2361
2387
|
deriveNote: '「由主色派生」以強調色為唯一輸入,自動算出底色與文字 —— 會覆蓋你手動設置的底色與文字;派生之後仍可逐項手改。',
|
|
2362
|
-
notice: '1.
|
|
2388
|
+
notice: '1.11.0:深色模式下註釋與時間戳更清晰(按感知對比度 Lc 加了一道閘);宿主語言不是中/英/日時介面改為回落英文;取色拖動按幀合併更流暢。',
|
|
2363
2389
|
dismiss: '知道了',
|
|
2364
2390
|
explainClamp: '強調色已正規化:{from} → {to}(確保按鈕與勾選可讀)',
|
|
2365
2391
|
explainLink: '連結已為可讀性壓暗:{ratio}:1(目標 {target}:1)',
|
|
@@ -2427,7 +2453,7 @@ body[data-ds-dark-theme] .dsh-tp{color-scheme:dark}
|
|
|
2427
2453
|
derive: 'Derive from accent',
|
|
2428
2454
|
deriveHint: 'Derive background and text from the current accent hue and chroma (follows the current light/dark mode)',
|
|
2429
2455
|
deriveNote: '"Derive from accent" takes the accent as its only input and computes background and text from it — this overwrites the background and text you set by hand; you can still edit each of them afterwards.',
|
|
2430
|
-
notice: '1.
|
|
2456
|
+
notice: '1.11.0: captions and timestamps are clearer in dark mode (a perceptual-contrast Lc gate was added); the UI now falls back to English when the host language is not Chinese, English or Japanese; colour-picker drags are smoother (frame-coalesced).',
|
|
2431
2457
|
dismiss: 'Got it',
|
|
2432
2458
|
explainClamp: 'Accent normalised: {from} → {to} (keeps buttons and checks readable)',
|
|
2433
2459
|
explainLink: 'Link darkened for readability: {ratio}:1 (target {target}:1)',
|
|
@@ -2495,7 +2521,7 @@ body[data-ds-dark-theme] .dsh-tp{color-scheme:dark}
|
|
|
2495
2521
|
derive: 'メイン色から生成',
|
|
2496
2522
|
deriveHint: '現在のアクセント色の色相と彩度から背景色と文字色を生成します(明暗は現在のモードに従います)',
|
|
2497
2523
|
deriveNote: '「メイン色から生成」はアクセント色だけを入力として背景色と文字色を自動計算します。手動で設定した背景色と文字色は上書きされます(生成後に個別に変更できます)。',
|
|
2498
|
-
notice: '1.
|
|
2524
|
+
notice: '1.11.0:ダークモードのキャプション・タイムスタンプがより明瞭になりました(知覚コントラスト Lc のゲートを追加)。ホスト言語が中/英/日以外のとき UI は英語にフォールバックします。カラーピッカーのドラッグがフレーム統合でより滑らかになりました。',
|
|
2499
2525
|
dismiss: '了解',
|
|
2500
2526
|
explainClamp: 'アクセント色を正規化しました:{from} → {to}(ボタンとチェックの可読性のため)',
|
|
2501
2527
|
explainLink: 'リンクを可読性のため暗くしました:{ratio}:1(目標 {target}:1)',
|
|
@@ -3122,7 +3148,7 @@ body[data-ds-dark-theme] .dsh-tp{color-scheme:dark}
|
|
|
3122
3148
|
|
|
3123
3149
|
// 开发期接口:palette-dev.mjs 通过它拿到同一套引擎(避免「报表好看、上线不一样」)
|
|
3124
3150
|
if (DEV_OPT_IN && typeof window !== 'undefined' && typeof window.__DSH_THEME_PALETTE_DEV__ === 'function') {
|
|
3125
|
-
window.__DSH_THEME_PALETTE_DEV__({ buildTokens, SCHEMES, SIDEBAR_MODES, TEXT_MODES, SCROLL_MODES, CONTROL_MODES, MD_MODES, HC_MODES, contrast, toOklch, fromOklch, flatten, luminance, withAlpha, mixHex, harmonize, deltaE, simulateCvd, rampsFor, floors, encodeConfig, decodeConfig, customScheme, deriveCustomFromAccent, readCustom, writeCustom, CONTRAST_SURFACES, CONTRAST_ROLES, buildContrastReport, devContrastReport, CONTRAST_TARGETS, CONTRAST_EXEMPT, CONTRAST_FLOOR, CONTRAST_SOLVER_MARGIN, LAYER_MARK, settingsCss: SETTINGS_CSS, markdownCss: MARKDOWN_CSS, cardColors })
|
|
3151
|
+
window.__DSH_THEME_PALETTE_DEV__({ buildTokens, SCHEMES, SIDEBAR_MODES, TEXT_MODES, SCROLL_MODES, CONTROL_MODES, MD_MODES, HC_MODES, contrast, toOklch, fromOklch, flatten, luminance, withAlpha, mixHex, harmonize, deltaE, simulateCvd, rampsFor, floors, floorsDark, apcaContrast, raiseToLc, solveForeground, pickLocale, hostLocale, encodeConfig, decodeConfig, customScheme, deriveCustomFromAccent, readCustom, writeCustom, CONTRAST_SURFACES, CONTRAST_ROLES, buildContrastReport, devContrastReport, CONTRAST_TARGETS, CONTRAST_EXEMPT, CONTRAST_FLOOR, CONTRAST_SOLVER_MARGIN, LAYER_MARK, settingsCss: SETTINGS_CSS, markdownCss: MARKDOWN_CSS, cardColors })
|
|
3126
3152
|
}
|
|
3127
3153
|
|
|
3128
3154
|
return {
|
|
@@ -3143,8 +3169,9 @@ body[data-ds-dark-theme] .dsh-tp{color-scheme:dark}
|
|
|
3143
3169
|
let controlMode = readControlStored()
|
|
3144
3170
|
let mdMode = readMdStored()
|
|
3145
3171
|
let hcMode = readHcStored()
|
|
3146
|
-
|
|
3172
|
+
// langPref 必须先于引用它的闭包声明(审计 2026-10-27 P3-9:原顺序运行时无害,但极易误读成 TDZ)
|
|
3147
3173
|
let langPref = readLangPref()
|
|
3174
|
+
const getConfigCode = () => encodeConfig({ palette: current, sidebar: sidebarMode, text: textMode, scroll: scrollMode, control: controlMode, md: mdMode, hc: hcMode, lang: langPref })
|
|
3148
3175
|
const localeListeners = new Set()
|
|
3149
3176
|
const currentLocale = () => pickLocale(langPref)
|
|
3150
3177
|
const emitLocale = () => { for (const fn of localeListeners) { try { fn() } catch (err) { /* 忽略 */ } } }
|
|
@@ -3558,13 +3585,28 @@ body[data-ds-dark-theme] .dsh-tp{color-scheme:dark}
|
|
|
3558
3585
|
applyAll('custom:reset')
|
|
3559
3586
|
emitCustom()
|
|
3560
3587
|
}
|
|
3588
|
+
/* 取色拖动的帧合并(审计 2026-10-27 P2-4):取色器 input 事件在拖动中每帧来多次,
|
|
3589
|
+
* 每次都重放覆盖层会让宿主连续做全页样式重写(约 200 个 token)+ 样式回读。
|
|
3590
|
+
* 同帧多次取色合并到下一帧只应用一次;离散动作(重置/派生/导入)不走这里,保持同步。 */
|
|
3591
|
+
let customFrameQueued = false
|
|
3592
|
+
const scheduleCustomApply = (reason) => {
|
|
3593
|
+
if (customFrameQueued) return
|
|
3594
|
+
customFrameQueued = true
|
|
3595
|
+
const run = () => {
|
|
3596
|
+
customFrameQueued = false
|
|
3597
|
+
applyAll(reason)
|
|
3598
|
+
emitCustom()
|
|
3599
|
+
}
|
|
3600
|
+
try { requestAnimationFrame(run) } catch (err) {
|
|
3601
|
+
try { setTimeout(run, 0) } catch (err2) { run() }
|
|
3602
|
+
}
|
|
3603
|
+
}
|
|
3561
3604
|
const setCustomColor = (field, hex) => {
|
|
3562
3605
|
if (CUSTOM_FIELDS.indexOf(field) === -1) return false
|
|
3563
3606
|
if (typeof hex !== 'string' || !HEX_RE.test(hex)) return false
|
|
3564
3607
|
const next = Object.assign({}, readCustom(), { [field]: hex.toLowerCase() })
|
|
3565
3608
|
writeCustom(next)
|
|
3566
|
-
|
|
3567
|
-
emitCustom()
|
|
3609
|
+
scheduleCustomApply('custom:' + field)
|
|
3568
3610
|
return true
|
|
3569
3611
|
}
|
|
3570
3612
|
/** 以当前主色为唯一输入,重算底色与文字色(1.10.0,审计建议 4)。 */
|
package/docs/DESIGN.md
CHANGED
|
@@ -127,6 +127,11 @@
|
|
|
127
127
|
- 物理上限只由计算得出。求解器里的 **0.93** 只是**求解余量**,仅用于判断「目标是否高于上限」,不混进上限本身。
|
|
128
128
|
- **底线是硬的**:政策外的面也不许掉到 AA 4.5 以下;深色侧还要同时满足 APCA 的 Lc 下限
|
|
129
129
|
(正文 60 / 次级 60 / 三级 45,实测已在 61.9–68.5)。
|
|
130
|
+
- **注释层(`label-caption`,1.11.0)**:不在承诺模型的角色表里,但深色侧同样过 Lc 闸 ——
|
|
131
|
+
标准档 **Lc 60**(与链接同线)、HC 档 **70**(HC 三级文字 Lc 落在 72–77,注释套链接的 75 会反超三级、
|
|
132
|
+
破坏「注释弱于三级」的层级)。抬完标准档注释 WCAG 约 5.6–6.0,仍低于三级的 6.4;`tools/check-audit.mjs` 第 4 组守着。
|
|
133
|
+
- **装饰层(`label-dimmed` / `label-document-preview`)**:按「装饰性文字」解到 **3.0**,刻意低于所有可读承诺 ——
|
|
134
|
+
宿主把它们用于占位/预览这类辅助场景;不在 `CONTRAST_ROLES` 承诺模型内,也不进报表(有意的政策外)。
|
|
130
135
|
- 三个出口共用同一份数据:设置页 UI、报表 CLI(`palette-dev.mjs`)、审计断言(`tools/check-audit.mjs`)。
|
|
131
136
|
单元格公开 `measured` / `declaredTarget` / `physicalCeiling` / `policyApplicable` / `limitedByPhysics` /
|
|
132
137
|
`meetsFloor` / `apca` / `pass`;政策外的 `pass` 为 `null`(不算达标也不算失败)。
|
|
@@ -323,8 +328,8 @@ sRGB 之外还有 P3 的色度余量,问题是「同一个 L/h 下 P3 能多
|
|
|
323
328
|
黑白配色不覆盖这些别名 → 全部回落色阶步进(本来就没有可测读数,报表整格记「未测量」)。
|
|
324
329
|
|
|
325
330
|
**为什么必须这么量**:按步进值报,自定义配色下三级文字会被报成 **4.21**(低于 AA 4.5 的假警报),
|
|
326
|
-
而实际渲染的覆盖值已被求解器兜到 ≥ **4.62**。1.9.8
|
|
327
|
-
|
|
331
|
+
而实际渲染的覆盖值已被求解器兜到 ≥ **4.62**。1.9.8 改完之后,全部组合配置
|
|
332
|
+
(5 套配色 × 216 组设置 = **1080** 组,216 已含标准/高对比两档;1.9.10 撤右栏档前是 2160 组)里,
|
|
328
333
|
低于 AA 4.5 底线的格子 = **0**,硬失败(pass=false)也是 **0**。
|
|
329
334
|
|
|
330
335
|
**深色压暗面的修正**:只按 `950`/`875` 压暗时,暖砂标准档的三级文字在 `800`(模块面)上只有 **3.79**、
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-theme-palette",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "DSH 主题配色:4 套配色 × 明暗两模式,OKLCH 色域安全重建 + 真实承载面对比度求解,可调强度与面板表面。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"files": [
|
|
18
18
|
"index.js",
|
|
19
19
|
"client.js",
|
|
20
|
+
"assets",
|
|
20
21
|
"cordis.patch.yml",
|
|
21
22
|
"icon.svg",
|
|
22
23
|
"README.md",
|