Add integrated dark mode (#5272)

This commit is contained in:
the1812 2025-07-15 09:18:48 +08:00
parent 32dbb93758
commit 94fdf85395
7 changed files with 197 additions and 44 deletions

View File

@ -74,7 +74,7 @@
## 最低配置
- 操作系统: Windows 8.1 / macOS Big Sur
- 分辨率: 1920x1080, 缩放 125%
- 浏览器: Chrome 105+ / Firefox 127+ / Safari 15.4+ (仅理论上能运行, 没测试过)
- 浏览器: Chrome 105+ / Firefox 140+ / Safari 18.4+ (仅理论上能运行, 没测试过)
- 处理器: Intel Core i5-10500 / AMD Ryzen 5 3600
- 内存: 8GB
- 脚本管理器: Tampermonkey 5.0 / Violentmonkey 2.18
@ -84,7 +84,7 @@
## 推荐配置
- 操作系统: Windows 10 / macOS Sonoma
- 分辨率: 3840x2160, 缩放 200%
- 浏览器: Chrome 120+ / Firefox 127+
- 浏览器: Chrome 120+ / Firefox 140+
- 处理器: Intel Core i7-11700 / AMD Ryzen 7 5800
- 内存: 32GB
- 脚本管理器: Tampermonkey 5.0 / Violentmonkey 2.18

View File

@ -59,7 +59,7 @@
"terser-webpack-plugin": "^5.0.1",
"to-string-loader": "^1.2.0",
"ts-node": "^10.7.0",
"typescript": "^4.9.5",
"typescript": "5.8.3",
"vue-eslint-parser": "^9.4.3",
"vue-loader": "^15.10.1",
"webpack": "^5.31.2",

82
pnpm-lock.yaml generated
View File

@ -99,10 +99,10 @@ importers:
version: 8.5.14
'@typescript-eslint/eslint-plugin':
specifier: ^5.50.0
version: 5.62.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@4.9.5))(eslint@7.32.0)(typescript@4.9.5)
version: 5.62.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3)
'@typescript-eslint/parser':
specifier: ^5.50.0
version: 5.62.0(eslint@7.32.0)(typescript@4.9.5)
version: 5.62.0(eslint@7.32.0)(typescript@5.8.3)
async-exit-hook:
specifier: ^2.0.1
version: 2.0.1
@ -123,13 +123,13 @@ importers:
version: 7.32.0
eslint-config-airbnb-base:
specifier: ^14.1.0
version: 14.2.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@4.9.5))(eslint@7.32.0))(eslint@7.32.0)
version: 14.2.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0))(eslint@7.32.0)
eslint-config-prettier:
specifier: ^8.6.0
version: 8.10.0(eslint@7.32.0)
eslint-plugin-import:
specifier: ^2.20.1
version: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@4.9.5))(eslint@7.32.0)
version: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)
eslint-plugin-prettier:
specifier: ^4.2.1
version: 4.2.1(eslint-config-prettier@8.10.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8)
@ -177,10 +177,10 @@ importers:
version: 1.2.0
ts-node:
specifier: ^10.7.0
version: 10.9.2(@types/node@17.0.45)(typescript@4.9.5)
version: 10.9.2(@types/node@17.0.45)(typescript@5.8.3)
typescript:
specifier: ^4.9.5
version: 4.9.5
specifier: 5.8.3
version: 5.8.3
vue-eslint-parser:
specifier: ^9.4.3
version: 9.4.3(eslint@7.32.0)
@ -3540,9 +3540,9 @@ packages:
resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
engines: {node: '>= 0.4'}
typescript@4.9.5:
resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
engines: {node: '>=4.2.0'}
typescript@5.8.3:
resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
engines: {node: '>=14.17'}
hasBin: true
unbox-primitive@1.1.0:
@ -4803,34 +4803,34 @@ snapshots:
dependencies:
'@types/node': 17.0.45
'@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@4.9.5))(eslint@7.32.0)(typescript@4.9.5)':
'@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3)':
dependencies:
'@eslint-community/regexpp': 4.12.1
'@typescript-eslint/parser': 5.62.0(eslint@7.32.0)(typescript@4.9.5)
'@typescript-eslint/parser': 5.62.0(eslint@7.32.0)(typescript@5.8.3)
'@typescript-eslint/scope-manager': 5.62.0
'@typescript-eslint/type-utils': 5.62.0(eslint@7.32.0)(typescript@4.9.5)
'@typescript-eslint/utils': 5.62.0(eslint@7.32.0)(typescript@4.9.5)
'@typescript-eslint/type-utils': 5.62.0(eslint@7.32.0)(typescript@5.8.3)
'@typescript-eslint/utils': 5.62.0(eslint@7.32.0)(typescript@5.8.3)
debug: 4.4.0
eslint: 7.32.0
graphemer: 1.4.0
ignore: 5.3.2
natural-compare-lite: 1.4.0
semver: 7.7.1
tsutils: 3.21.0(typescript@4.9.5)
tsutils: 3.21.0(typescript@5.8.3)
optionalDependencies:
typescript: 4.9.5
typescript: 5.8.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@4.9.5)':
'@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3)':
dependencies:
'@typescript-eslint/scope-manager': 5.62.0
'@typescript-eslint/types': 5.62.0
'@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5)
'@typescript-eslint/typescript-estree': 5.62.0(typescript@5.8.3)
debug: 4.4.0
eslint: 7.32.0
optionalDependencies:
typescript: 4.9.5
typescript: 5.8.3
transitivePeerDependencies:
- supports-color
@ -4839,21 +4839,21 @@ snapshots:
'@typescript-eslint/types': 5.62.0
'@typescript-eslint/visitor-keys': 5.62.0
'@typescript-eslint/type-utils@5.62.0(eslint@7.32.0)(typescript@4.9.5)':
'@typescript-eslint/type-utils@5.62.0(eslint@7.32.0)(typescript@5.8.3)':
dependencies:
'@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5)
'@typescript-eslint/utils': 5.62.0(eslint@7.32.0)(typescript@4.9.5)
'@typescript-eslint/typescript-estree': 5.62.0(typescript@5.8.3)
'@typescript-eslint/utils': 5.62.0(eslint@7.32.0)(typescript@5.8.3)
debug: 4.4.0
eslint: 7.32.0
tsutils: 3.21.0(typescript@4.9.5)
tsutils: 3.21.0(typescript@5.8.3)
optionalDependencies:
typescript: 4.9.5
typescript: 5.8.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/types@5.62.0': {}
'@typescript-eslint/typescript-estree@5.62.0(typescript@4.9.5)':
'@typescript-eslint/typescript-estree@5.62.0(typescript@5.8.3)':
dependencies:
'@typescript-eslint/types': 5.62.0
'@typescript-eslint/visitor-keys': 5.62.0
@ -4861,20 +4861,20 @@ snapshots:
globby: 11.1.0
is-glob: 4.0.3
semver: 7.7.1
tsutils: 3.21.0(typescript@4.9.5)
tsutils: 3.21.0(typescript@5.8.3)
optionalDependencies:
typescript: 4.9.5
typescript: 5.8.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@5.62.0(eslint@7.32.0)(typescript@4.9.5)':
'@typescript-eslint/utils@5.62.0(eslint@7.32.0)(typescript@5.8.3)':
dependencies:
'@eslint-community/eslint-utils': 4.4.1(eslint@7.32.0)
'@types/json-schema': 7.0.15
'@types/semver': 7.5.8
'@typescript-eslint/scope-manager': 5.62.0
'@typescript-eslint/types': 5.62.0
'@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5)
'@typescript-eslint/typescript-estree': 5.62.0(typescript@5.8.3)
eslint: 7.32.0
eslint-scope: 5.1.1
semver: 7.7.1
@ -5683,11 +5683,11 @@ snapshots:
escape-string-regexp@4.0.0: {}
eslint-config-airbnb-base@14.2.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@4.9.5))(eslint@7.32.0))(eslint@7.32.0):
eslint-config-airbnb-base@14.2.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0))(eslint@7.32.0):
dependencies:
confusing-browser-globals: 1.0.11
eslint: 7.32.0
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@4.9.5))(eslint@7.32.0)
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)
object.assign: 4.1.7
object.entries: 1.1.8
@ -5703,17 +5703,17 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.12.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint@7.32.0):
eslint-module-utils@2.12.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@7.32.0):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 5.62.0(eslint@7.32.0)(typescript@4.9.5)
'@typescript-eslint/parser': 5.62.0(eslint@7.32.0)(typescript@5.8.3)
eslint: 7.32.0
eslint-import-resolver-node: 0.3.9
transitivePeerDependencies:
- supports-color
eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@4.9.5))(eslint@7.32.0):
eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.8
@ -5724,7 +5724,7 @@ snapshots:
doctrine: 2.1.0
eslint: 7.32.0
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint@7.32.0)
eslint-module-utils: 2.12.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@7.32.0)
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@ -5736,7 +5736,7 @@ snapshots:
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
optionalDependencies:
'@typescript-eslint/parser': 5.62.0(eslint@7.32.0)(typescript@4.9.5)
'@typescript-eslint/parser': 5.62.0(eslint@7.32.0)(typescript@5.8.3)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
@ -7382,7 +7382,7 @@ snapshots:
totalist@3.0.1: {}
ts-node@10.9.2(@types/node@17.0.45)(typescript@4.9.5):
ts-node@10.9.2(@types/node@17.0.45)(typescript@5.8.3):
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.11
@ -7396,7 +7396,7 @@ snapshots:
create-require: 1.1.1
diff: 4.0.2
make-error: 1.3.6
typescript: 4.9.5
typescript: 5.8.3
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
@ -7409,10 +7409,10 @@ snapshots:
tslib@1.14.1: {}
tsutils@3.21.0(typescript@4.9.5):
tsutils@3.21.0(typescript@5.8.3):
dependencies:
tslib: 1.14.1
typescript: 4.9.5
typescript: 5.8.3
type-check@0.4.0:
dependencies:
@ -7453,7 +7453,7 @@ snapshots:
possible-typed-array-names: 1.1.0
reflect.getprototypeof: 1.0.10
typescript@4.9.5: {}
typescript@5.8.3: {}
unbox-primitive@1.1.0:
dependencies:

View File

@ -0,0 +1 @@
简化版的夜间模式,跟随 b 站官方的深色主题。

View File

@ -0,0 +1,105 @@
import { defineComponentMetadata } from '@/components/component'
import { getCookieValue } from '@/core/utils'
const name = 'integratedDarkMode'
const darkModeClass = 'dark'
const integratedDarkModeClass = 'integrated-dark'
const darkMetaColor = '#111'
const isOfficialDarkModeEnabled = () => {
return getCookieValue('theme_style') === 'dark'
}
const enableDarkMode = () => {
document.body.classList.add(darkModeClass, integratedDarkModeClass)
const themeColorMeta = dq('meta[name="theme-color"]') as HTMLMetaElement
if (!themeColorMeta) {
document.head.insertAdjacentHTML(
'beforeend',
`<meta name="theme-color" content="${darkMetaColor}">`,
)
} else {
themeColorMeta.dataset.light = themeColorMeta.content
themeColorMeta.content = darkMetaColor
}
const colorSchemeMeta = dq('meta[name="color-scheme"]') as HTMLMetaElement
if (!colorSchemeMeta) {
document.head.insertAdjacentHTML('beforeend', `<meta name="color-scheme" content="dark">`)
} else {
colorSchemeMeta.content = 'dark'
}
}
const disableDarkMode = () => {
document.body.classList.remove(darkModeClass, integratedDarkModeClass)
const themeColorMeta = dq('meta[name="theme-color"]') as HTMLMetaElement
if (themeColorMeta?.dataset.light) {
themeColorMeta.content = themeColorMeta.dataset.light
} else {
themeColorMeta?.remove()
}
const colorSchemeMeta = dq('meta[name="color-scheme"]') as HTMLMetaElement
if (colorSchemeMeta) {
colorSchemeMeta.content = 'light'
}
}
const toggleDarkModeByCookie = () => {
if (isOfficialDarkModeEnabled()) {
enableDarkMode()
} else {
disableDarkMode()
}
}
const cookieChangeHandler = (e: CookieChangedEvent) => {
if (e.changed.some(cookie => cookie.name === 'theme_style')) {
toggleDarkModeByCookie()
}
}
export const component = defineComponentMetadata({
name,
displayName: '深色模式',
entry: () => {
cookieStore.addEventListener('change', cookieChangeHandler)
},
reload: () => {
cookieStore.addEventListener('change', cookieChangeHandler)
toggleDarkModeByCookie()
},
unload: () => {
cookieStore.removeEventListener('change', cookieChangeHandler)
document.body.classList.remove(darkModeClass, integratedDarkModeClass)
},
tags: [componentsTags.style, componentsTags.general],
instantStyles: [
{
name,
style: () => import('./integrated-variables.scss'),
},
],
plugin: {
displayName: '深色模式 - 提前注入',
description: {
'zh-CN': '提前注入深色模式的 .dark class 以减少一些组件首屏仍然是白色的问题.',
},
async setup() {
if (!isOfficialDarkModeEnabled()) {
return
}
const { contentLoaded } = await import('@/core/life-cycle')
const { isComponentEnabled } = await import('@/core/settings')
contentLoaded(() => {
if (isComponentEnabled(name)) {
cookieStore.addEventListener('change', cookieChangeHandler)
toggleDarkModeByCookie()
}
})
},
},
})

View File

@ -0,0 +1,11 @@
body.integrated-dark {
--be-text-title-color: var(--text1);
--be-text-content-color: var(--text1);
--be-card-background-color: var(--bg1);
--be-card-border-color: var(--line_regular);
--be-tag-background-color: rgba(var(--graph_medium_rgb), 0.1);
--be-tag-hover-background-color: rgba(var(--graph_medium_rgb), 0.2);
--be-tag-active-background-color: rgba(var(--graph_medium_rgb), 0.3);
--be-avatar-border-color: var(--line_light);
--be-hover-highlight-background-color: rgba(var(--graph_medium_rgb), 0.1);
}

36
src/cookie-store.d.ts vendored Normal file
View File

@ -0,0 +1,36 @@
interface Cookie {
domain: string
expires: number
name: string
partitioned: boolean
path: string
sameSite: 'Strict' | 'Lax' | 'None'
secure: boolean
value: string
}
interface CookieChangedEvent extends Event {
changed: Readonly<Array<Cookie>>
deleted: Readonly<Array<Cookie>>
}
interface CookieStore {
get(name: string): Promise<Cookie | null>
get(options: { name: string; url?: string }): Promise<Cookie | null>
getAll(): Promise<Cookie[]>
getAll(name: string): Promise<Cookie[]>
getAll(options: { name?: string; url?: string }): Promise<Cookie[]>
set(name: string, value: string): Promise<void>
set(options: Partial<Cookie> & { name: string; value: string }): Promise<void>
delete(name: string): Promise<void>
delete(options: { name: string; url?: string }): Promise<void>
addEventListener(eventName: 'change', handler: (event: CookieChangedEvent) => void): void
removeEventListener(eventName: 'change', handler: (event: CookieChangedEvent) => void): void
onchange: null | ((event: CookieChangedEvent) => void)
}
declare const cookieStore: CookieStore