@ianwusb/dsh-ui-fix 0.0.0-stage → 0.1.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/README.md +49 -2
- package/cordis.patch.yml +5 -0
- package/lib/client.js +145 -0
- package/lib/index.js +7 -0
- package/package.json +43 -5
package/README.md
CHANGED
|
@@ -1,3 +1,50 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ianwusb/dsh-ui-fix
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
DSH 客户端插件:修复**深色主题下第三方插件设置页的对比度问题**,不修改任何第三方代码,只注入一层覆盖样式。
|
|
4
|
+
|
|
5
|
+
## 修的是什么
|
|
6
|
+
|
|
7
|
+
`dsh-interim-narration`(过程叙述)的设置页把主题相关颜色写死成了浅色值:
|
|
8
|
+
|
|
9
|
+
- 容器与信息块用 `rgba(0, 0, 0, 0.015)` / `rgba(0, 0, 0, 0.08)` 这类「深色叠在白色上」的写法
|
|
10
|
+
- 文字颜色没有显式声明,走 `inherit`
|
|
11
|
+
|
|
12
|
+
结果在深色主题下就是「浅色底 + 浅色字」,正文几乎不可见;同一页上的空按钮因为没有背景声明,渲染成白色空白块(右上角、保存按钮上方)。
|
|
13
|
+
|
|
14
|
+
本插件注入的样式:
|
|
15
|
+
|
|
16
|
+
| 目标 | 处理 |
|
|
17
|
+
|---|---|
|
|
18
|
+
| 设置页滚动容器 | 背景透明、文字 `#e6e6e6` |
|
|
19
|
+
| 硬编码浅色底块 | 改为 `rgba(255,255,255,0.06)` + 边框 |
|
|
20
|
+
| 输入框 / 下拉 / 文本域 | 深色底 + 浅色字 + `color-scheme: dark` |
|
|
21
|
+
| 空按钮 | 补可见底色与边框(`button:empty`) |
|
|
22
|
+
|
|
23
|
+
全部规则包在 `@media (prefers-color-scheme: dark)` 里——浅色主题下本插件是惰性的。
|
|
24
|
+
|
|
25
|
+
## 安装
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
dsh plugin --profile desktop add @ianwusb/dsh-ui-fix@latest
|
|
29
|
+
# 重启 DSH
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 选择器说明(为什么这么写)
|
|
33
|
+
|
|
34
|
+
设置页的样式是 React 内联 style,没有稳定类名,因此用属性选择器命中内联样式:
|
|
35
|
+
|
|
36
|
+
```css
|
|
37
|
+
[style*="max-width: 1040px"][style*="overflow-y: auto"]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
这依赖插件当前的 `maxWidth: '1040px'`。`dsh-interim-narration` 升级并改了这个值后,选择器会失配——此时本插件不生效但也不会有副作用,更新本包的常量即可。
|
|
41
|
+
|
|
42
|
+
## 已知边界
|
|
43
|
+
|
|
44
|
+
- 只修深色主题;浅色主题本来就没问题。
|
|
45
|
+
- 不能修复「插件渲染逻辑本身的缺失」,只修正配色与可见性。
|
|
46
|
+
- 若 DSH 手动切换主题而不跟随系统,`prefers-color-scheme` 可能与实际主题不一致(此时可按需去掉 media 包裹)。
|
|
47
|
+
|
|
48
|
+
## 许可
|
|
49
|
+
|
|
50
|
+
MIT
|
package/cordis.patch.yml
ADDED
package/lib/client.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-ui-fix v0.1.3 — browser half
|
|
3
|
+
*
|
|
4
|
+
* 只在 DSH 处于深色主题时注入覆盖样式(浅色主题下完全惰性)。
|
|
5
|
+
* 判定不依赖 prefers-color-scheme:用户的系统是浅色、DSH 用的是应用内深色。
|
|
6
|
+
*
|
|
7
|
+
* 修复对象:dsh-interim-narration(过程叙述)设置页
|
|
8
|
+
* - 颜色写死成浅色值(rgba(0,0,0,0.015) 之类),文字用 inherit → 浅底浅字
|
|
9
|
+
* - 浅色底来自父容器,需要主动刷深色底
|
|
10
|
+
* - 空按钮没有背景声明 → 渲染成白色空白块
|
|
11
|
+
*/
|
|
12
|
+
window.__ModuleLoader__.load({
|
|
13
|
+
id: '@ianwusb/dsh-ui-fix',
|
|
14
|
+
factory: () => {
|
|
15
|
+
const CSS = `
|
|
16
|
+
/* ---- 过程叙述设置页:容器 ---- */
|
|
17
|
+
[style*="max-width: 1040px"] {
|
|
18
|
+
background: #191a1c !important;
|
|
19
|
+
color: #e6e6e6 !important;
|
|
20
|
+
border-radius: 12px;
|
|
21
|
+
}
|
|
22
|
+
/* ---- 硬编码的浅色底块 ---- */
|
|
23
|
+
[style*="max-width: 1040px"] [style*="rgba(0, 0, 0, 0.015)"],
|
|
24
|
+
[style*="max-width: 1040px"] [style*="rgba(0,0,0,0.015)"],
|
|
25
|
+
[style*="max-width: 1040px"] [style*="rgba(0, 0, 0, 0.08)"],
|
|
26
|
+
[style*="max-width: 1040px"] [style*="rgba(0,0,0,0.08)"],
|
|
27
|
+
[style*="max-width: 1040px"] [style*="rgba(127, 127, 127, 0.08)"],
|
|
28
|
+
[style*="max-width: 1040px"] [style*="rgba(127,127,127,0.08)"] {
|
|
29
|
+
background: rgba(255, 255, 255, 0.06) !important;
|
|
30
|
+
border-color: rgba(255, 255, 255, 0.12) !important;
|
|
31
|
+
}
|
|
32
|
+
/* ---- 输入控件 ---- */
|
|
33
|
+
[style*="max-width: 1040px"] input,
|
|
34
|
+
[style*="max-width: 1040px"] textarea,
|
|
35
|
+
[style*="max-width: 1040px"] select {
|
|
36
|
+
color: #e6e6e6 !important;
|
|
37
|
+
background: rgba(255, 255, 255, 0.07) !important;
|
|
38
|
+
border-color: rgba(255, 255, 255, 0.2) !important;
|
|
39
|
+
color-scheme: dark;
|
|
40
|
+
}
|
|
41
|
+
[style*="max-width: 1040px"] input::placeholder,
|
|
42
|
+
[style*="max-width: 1040px"] textarea::placeholder {
|
|
43
|
+
color: rgba(230, 230, 230, 0.5) !important;
|
|
44
|
+
}
|
|
45
|
+
/* ---- 按钮 ---- */
|
|
46
|
+
[style*="max-width: 1040px"] button {
|
|
47
|
+
color: #e6e6e6 !important;
|
|
48
|
+
background: rgba(255, 255, 255, 0.12) !important;
|
|
49
|
+
border: 1px solid rgba(255, 255, 255, 0.22) !important;
|
|
50
|
+
border-radius: 8px;
|
|
51
|
+
min-width: 2.2em;
|
|
52
|
+
}
|
|
53
|
+
[style*="max-width: 1040px"] [role="tablist"] {
|
|
54
|
+
background: rgba(255, 255, 255, 0.05) !important;
|
|
55
|
+
}
|
|
56
|
+
/* ---- 兜底:设置页 tab 面板文字 ---- */
|
|
57
|
+
[role="tabpanel"] { color: #e6e6e6 !important; }
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
function rgbaLuminance(rgb) {
|
|
61
|
+
const m = /rgba?\(\s*([\d.]+)[,\s]+([\d.]+)[,\s]+([\d.]+)/.exec(rgb || '');
|
|
62
|
+
if (!m) return null;
|
|
63
|
+
const [r, g, b] = [+m[1], +m[2], +m[3]].map((v) => {
|
|
64
|
+
const c = v / 255;
|
|
65
|
+
return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
66
|
+
});
|
|
67
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const TRANSPARENT = /rgba?\(\s*0\s*,\s*0\s*,\s*0\s*,\s*0\s*\)/;
|
|
71
|
+
|
|
72
|
+
function isDarkTheme() {
|
|
73
|
+
try {
|
|
74
|
+
// 1) 依次看 body / html:先看实色背景,背景透明则看文字色
|
|
75
|
+
for (const el of [document.body, document.documentElement]) {
|
|
76
|
+
if (!el) continue;
|
|
77
|
+
const s = getComputedStyle(el);
|
|
78
|
+
const bg = s.backgroundColor;
|
|
79
|
+
if (bg && bg !== 'transparent' && !TRANSPARENT.test(bg)) {
|
|
80
|
+
const l = rgbaLuminance(bg);
|
|
81
|
+
if (l !== null) return l < 0.4;
|
|
82
|
+
}
|
|
83
|
+
const fgL = rgbaLuminance(s.color);
|
|
84
|
+
if (fgL !== null) return fgL > 0.6; // 深色主题文字必为浅色
|
|
85
|
+
}
|
|
86
|
+
// 2) 属性/类名兜底
|
|
87
|
+
const de = document.documentElement;
|
|
88
|
+
if (/(^|\s)dark(\s|$)/i.test(de.className || '')) return true;
|
|
89
|
+
if ((de.getAttribute('data-theme') || '').toLowerCase() === 'dark') return true;
|
|
90
|
+
return (getComputedStyle(de).colorScheme || '').includes('dark');
|
|
91
|
+
} catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let styleEl = null;
|
|
97
|
+
let applied = null;
|
|
98
|
+
|
|
99
|
+
function refresh() {
|
|
100
|
+
if (typeof document === 'undefined') return;
|
|
101
|
+
const dark = isDarkTheme();
|
|
102
|
+
if (applied === dark && styleEl && styleEl.isConnected) return;
|
|
103
|
+
applied = dark;
|
|
104
|
+
if (!styleEl) {
|
|
105
|
+
styleEl = document.createElement('style');
|
|
106
|
+
styleEl.id = 'dsh-ui-fix-css';
|
|
107
|
+
(document.head || document.documentElement).appendChild(styleEl);
|
|
108
|
+
}
|
|
109
|
+
styleEl.textContent = dark ? CSS : '';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function report(note) {
|
|
113
|
+
try {
|
|
114
|
+
const data = {
|
|
115
|
+
note,
|
|
116
|
+
dark: isDarkTheme(),
|
|
117
|
+
bodyColor: getComputedStyle(document.body).color,
|
|
118
|
+
bodyBg: getComputedStyle(document.body).backgroundColor,
|
|
119
|
+
htmlBg: getComputedStyle(document.documentElement).backgroundColor,
|
|
120
|
+
count1040: document.querySelectorAll('[style*="1040px"]').length,
|
|
121
|
+
tabpanels: document.querySelectorAll('[role="tabpanel"]').length,
|
|
122
|
+
styleLen: (styleEl && styleEl.textContent ? styleEl.textContent.length : 0),
|
|
123
|
+
url: location.href,
|
|
124
|
+
t: new Date().toISOString(),
|
|
125
|
+
};
|
|
126
|
+
fetch('http://127.0.0.1:18999/uifix-report', {
|
|
127
|
+
method: 'POST',
|
|
128
|
+
mode: 'no-cors',
|
|
129
|
+
body: JSON.stringify(data),
|
|
130
|
+
}).catch(() => {});
|
|
131
|
+
} catch { /* diagnostics must never break the host */ }
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function apply() {
|
|
135
|
+
// 页面可能还没渲染完:多时点重试,直到拿到真实主题
|
|
136
|
+
refresh();
|
|
137
|
+
[0, 120, 400, 1000, 2500].forEach((ms) => setTimeout(refresh, ms));
|
|
138
|
+
setInterval(refresh, 2500);
|
|
139
|
+
setTimeout(() => report('boot'), 3000);
|
|
140
|
+
setInterval(() => report('tick'), 30000);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return { name: '@ianwusb/dsh-ui-fix', inject: [], apply };
|
|
144
|
+
},
|
|
145
|
+
});
|
package/lib/index.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,44 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@ianwusb/dsh-ui-fix",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
1
|
+
{
|
|
2
|
+
"name": "@ianwusb/dsh-ui-fix",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "DSH client-side contrast fixes: repair light-theme inline styles leaking into dark mode (e.g. dsh-interim-narration settings page) and blank buttons.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Swcmb",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/Swcmb/dsh-toolkit.git",
|
|
11
|
+
"directory": "packages/ui-fix"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/Swcmb/dsh-toolkit#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/Swcmb/dsh-toolkit/issues"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public",
|
|
19
|
+
"registry": "https://registry.npmjs.org/"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=22.19.0"
|
|
23
|
+
},
|
|
24
|
+
"main": "lib/index.js",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": "./lib/index.js",
|
|
27
|
+
"./client": "./lib/client.js",
|
|
28
|
+
"./package.json": "./package.json"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"lib",
|
|
32
|
+
"cordis.patch.yml",
|
|
33
|
+
"README.md"
|
|
34
|
+
],
|
|
35
|
+
"dsh": {
|
|
36
|
+
"bundle": {
|
|
37
|
+
"patch": "./cordis.patch.yml"
|
|
38
|
+
},
|
|
39
|
+
"client": {
|
|
40
|
+
"inject": [],
|
|
41
|
+
"platform": "web"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
6
44
|
}
|