feat: 1.0.3 引入 Dark Reader 通用暗色引擎

This commit is contained in:
2026-08-08 17:21:22 +08:00
parent b36ad30d7a
commit 202ab537d1
29 changed files with 19555 additions and 207 deletions
+48
View File
@@ -0,0 +1,48 @@
import { disable, enable } from 'darkreader'
import { watch } from 'vue'
import { useDarkMode } from './composables/useDarkMode'
/**
* Dark Reader 引擎参数。
* brightness/contrast 保持默认观感,背景色与现有 Halo 暗色变量接近,
* 让 Dark Reader 补全第三方插件页面时不会显得突兀。
*/
const DARK_READER_THEME = {
brightness: 100,
contrast: 90,
grayscale: 0,
sepia: 0,
darkSchemeBackgroundColor: '#181b20',
darkSchemeTextColor: '#e8e6e3',
scrollbarColor: '#3a3f4a',
selectionColor: '#2f6f7a',
styleSystemControls: true,
} as const
let initialized = false
/**
* 初始化 Dark Reader 通用暗色引擎。
* 监听 useDarkMode 的 isDark 状态,深色时启用,浅色时关闭。
*/
export function initDarkReaderEngine(): void {
if (initialized) return
initialized = true
const { isDark } = useDarkMode()
watch(
isDark,
(dark) => {
try {
if (dark) {
enable(DARK_READER_THEME)
} else {
disable()
}
} catch (error) {
console.error('[dark-mode] Dark Reader 引擎异常', error)
}
},
{ immediate: true },
)
}
+5 -1
View File
@@ -2,10 +2,14 @@ import { definePlugin } from '@halo-dev/ui-shared'
import { IconPalette } from '@halo-dev/components'
import { markRaw } from 'vue'
import './styles/index.css'
import { initDarkReaderEngine } from './darkreader-engine'
import { injectThemeToggle } from './injector'
import { initMonacoThemeSync } from './monaco-theme'
// 在插件加载后将切换器注入到侧边栏
// 在插件加载后将切换器注入到侧边栏,并让 Monaco 跟随主题
injectThemeToggle()
initMonacoThemeSync()
initDarkReaderEngine()
export default definePlugin({
components: {},
+43
View File
@@ -0,0 +1,43 @@
/**
* 让 Monaco Editor(日志查看器)跟随 Halo 暗色模式。
* 优先调用 Monaco 官方主题 APICSS 兜底见 styles/overrides/editor.css。
*/
type MonacoLike = {
editor?: {
setTheme: (name: string) => void
}
}
const DARK_THEME = 'vs-dark'
const LIGHT_THEME = 'vs'
function getMonaco(): MonacoLike | undefined {
return (window as unknown as { monaco?: MonacoLike }).monaco
}
function applyMonacoTheme(): void {
const monaco = getMonaco()
if (!monaco?.editor?.setTheme) return
const isDark = document.documentElement.getAttribute('data-halo-theme') === 'dark'
monaco.editor.setTheme(isDark ? DARK_THEME : LIGHT_THEME)
}
/** 初始化 Monaco 主题同步:立即应用,并监听属性变化与实例懒加载。 */
export function initMonacoThemeSync(): void {
applyMonacoTheme()
const attributeObserver = new MutationObserver(() => applyMonacoTheme())
attributeObserver.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-halo-theme'],
})
// 日志页面可能懒加载 Monaco,实例渲染后再补一次
const instanceObserver = new MutationObserver(() => {
if (document.querySelector('.monaco-editor')) {
applyMonacoTheme()
}
})
instanceObserver.observe(document.body, { childList: true, subtree: true })
}
+1
View File
@@ -6,6 +6,7 @@
@import './variables.css';
@import './overrides/halo-core.css';
@import './overrides/plugin-pages.css';
@import './overrides/uno-fallback.css';
@import './overrides/utilities.css';
@import './overrides/layout.css';
@import './overrides/components.css';
+16 -88
View File
@@ -1,73 +1,21 @@
/* ============================================================
Halo Dark Mode — @halo-dev/components 组件库覆盖
覆盖 Halo 组件库中的 VCard, VModal, VDropdown, VTag 等
Halo Dark Mode — Halo 组件库覆盖(已清理)
已删除 Halo 2.25 中不存在的 Vuetify 类(.v-card 等),
真实 DOM 类覆盖请优先维护 halo-core.css。
============================================================ */
[data-halo-theme="dark"] {
/* ===== 卡片 VCard ===== */
.v-card,
[class*="v-card"] {
background-color: var(--halo-bg-card);
border-color: var(--halo-border-base);
color: var(--halo-text-primary);
}
/* ===== 模态框 VModal ===== */
.v-modal,
.modal-container,
[class*="modal"] {
background-color: var(--halo-bg-card);
color: var(--halo-text-primary);
}
/* ===== 下拉菜单 VDropdown ===== */
.v-dropdown,
.dropdown-menu,
[class*="dropdown"] {
background-color: var(--halo-bg-dropdown);
border-color: var(--halo-border-base);
color: var(--halo-text-primary);
}
.v-dropdown-item:hover,
.dropdown-item:hover {
background-color: var(--halo-bg-hover);
}
/* ===== 提示框 VTooltip ===== */
.v-tooltip,
[class*="tooltip"] {
background-color: var(--halo-bg-tooltip);
color: var(--halo-text-inverse);
}
/* ===== 标签/徽章 VTag, VBadge ===== */
.v-tag,
[class*="tag"],
.v-badge,
[class*="badge"] {
background-color: var(--halo-tag-bg);
color: var(--halo-tag-text);
}
/* ===== 按钮 ===== */
.btn-default,
.btn-secondary,
button:not([class*="btn-primary"]):not([class*="btn-danger"]) {
background-color: var(--halo-bg-card);
color: var(--halo-text-primary);
border-color: var(--halo-border-base);
}
.btn-default:hover,
.btn-secondary:hover {
background-color: var(--halo-bg-hover);
/* ===== 模态框(具体类 + !important,防被核心 scoped 样式覆盖) ===== */
.modal-content,
.modal-header,
.modal-body,
.modal-footer {
background-color: var(--halo-bg-card) !important;
color: var(--halo-text-primary) !important;
}
/* ===== 表格 VTable ===== */
table,
.v-table,
[class*="table"] {
table {
background-color: var(--halo-bg-card);
color: var(--halo-text-primary);
}
@@ -94,49 +42,29 @@
}
/* ===== 分页 ===== */
.pagination,
.v-pagination {
.pagination {
color: var(--halo-text-secondary);
}
.pagination .active,
.v-pagination .active {
.pagination .active {
background-color: var(--halo-accent-primary);
color: var(--halo-accent-primary-text);
}
/* ===== 面包屑 ===== */
.breadcrumb,
.v-breadcrumb {
.breadcrumb {
color: var(--halo-text-secondary);
}
.breadcrumb a,
.v-breadcrumb a {
.breadcrumb a {
color: var(--halo-text-link);
}
/* ===== Toast / 通知 ===== */
.v-toast,
.toast-notification,
[class*="toast"] {
background-color: var(--halo-bg-card);
color: var(--halo-text-primary);
border-color: var(--halo-border-base);
}
/* ===== 步骤条 ===== */
.v-steps,
[class*="steps"] {
color: var(--halo-text-secondary);
}
/* ===== 开关 VSwitch ===== */
.v-switch-track {
background-color: var(--halo-bg-disabled);
}
.v-switch-track[aria-checked="true"] {
background-color: var(--halo-accent-primary);
}
}
}
+29
View File
@@ -232,3 +232,32 @@
background-color: oklch(30% 0.04 160 / 50%);
}
}
/* ===== Monaco Editor(日志查看器,JS 主题优先,此处仅兜底) ===== */
html[data-halo-theme="dark"] .monaco-editor.vs,
html[data-halo-theme="dark"] .monaco-editor.vs .margin,
html[data-halo-theme="dark"] .monaco-editor.vs .lines-content,
html[data-halo-theme="dark"] .monaco-editor.vs .monaco-scrollable-element {
background-color: var(--halo-bg-input) !important;
color: var(--halo-text-primary) !important;
}
html[data-halo-theme="dark"] .monaco-editor.vs .line-numbers {
color: var(--halo-text-tertiary) !important;
}
html[data-halo-theme="dark"] .monaco-editor.vs .cursor {
border-color: var(--halo-text-primary) !important;
}
html[data-halo-theme="dark"] .monaco-editor.vs .selected-text,
html[data-halo-theme="dark"] .monaco-editor.vs .view-overlays .selected-text {
background-color: oklch(30% 0.04 160 / 50%) !important;
}
html[data-halo-theme="dark"] .monaco-editor.vs .find-widget,
html[data-halo-theme="dark"] .monaco-editor.vs .monaco-hover {
background-color: var(--halo-bg-dropdown) !important;
color: var(--halo-text-primary) !important;
}
/* ===== 编辑器链接(覆盖通用链接色规则) ===== */
html[data-halo-theme="dark"] .ProseMirror a,
html[data-halo-theme="dark"] .tiptap a {
color: var(--halo-text-link) !important;
}
+13
View File
@@ -87,3 +87,16 @@
border-color: var(--halo-border-base);
}
}
/* ===== 强表单覆盖(scoped 哈希类特异性更高,需 html 前缀 + !important ===== */
html[data-halo-theme="dark"] input:not([type="checkbox"]):not([type="radio"]):not([type="color"]),
html[data-halo-theme="dark"] textarea,
html[data-halo-theme="dark"] select {
background-color: var(--halo-bg-input) !important;
color: var(--halo-text-primary) !important;
border-color: var(--halo-border-input) !important;
}
html[data-halo-theme="dark"] input:not([type="checkbox"]):not([type="radio"]):not([type="color"])::placeholder,
html[data-halo-theme="dark"] textarea::placeholder,
html[data-halo-theme="dark"] select::placeholder {
color: var(--halo-text-tertiary) !important;
}
+54 -18
View File
@@ -1,11 +1,14 @@
/* ===== 根元素背景(html 自身携带属性,需独立选择器) ===== */
html[data-halo-theme="dark"] {
background-color: var(--halo-bg-body) !important;
}
/* ============================================================
Halo Dark Mode — Halo 核心 BEM 类覆盖
基于 Halo 2.25 真实 DOM 扫描(demo.halocms.site)得出的选择器
============================================================ */
[data-halo-theme="dark"] {
/* ===== 页面根背景(body 溢出时兜底) ===== */
html,
/* ===== 页面根背景(body 溢出时兜底html 见文件顶部独立规则 ===== */
body {
background-color: var(--halo-bg-body) !important;
color: var(--halo-text-primary);
@@ -100,14 +103,6 @@
.entity-field-description {
color: var(--halo-text-secondary);
}
.entity-field-title a,
.entity-field-title-body a {
color: var(--halo-text-primary);
}
.entity-field-title a:hover,
.entity-field-title-body a:hover {
color: var(--halo-text-link);
}
/* ===== 分页 ===== */
.pagination {
@@ -131,8 +126,7 @@
/* ===== 标签/徽章(BEM ===== */
.tag-wrapper,
.tag-default,
.tag-pill,
[class*="tag-"] {
.tag-pill {
background-color: var(--halo-bg-hover);
color: var(--halo-text-secondary);
}
@@ -206,10 +200,10 @@
border-color: var(--halo-border-light);
}
.description-item__label {
color: var(--halo-text-secondary);
color: var(--halo-text-secondary) !important;
}
.description-item__content {
color: var(--halo-text-primary);
color: var(--halo-text-primary) !important;
}
/* ===== 已选指示器 ===== */
@@ -265,8 +259,50 @@
border-color: var(--halo-border-base);
}
/* ===== 通用链接 ===== */
a:not([class]) {
color: var(--halo-text-link);
}
}
/* ===== 侧边栏激活菜单(Halo 核心 .menu-item-title.active ===== */
html[data-halo-theme="dark"] .menu-item-title.active,
html[data-halo-theme="dark"] .menu-item-title.active:hover {
background-color: var(--halo-menu-item-active) !important;
color: var(--halo-text-primary) !important;
}
html[data-halo-theme="dark"] .menu-item-title.active::after {
background: var(--halo-accent-primary) !important;
}
/* ===== 空状态标题 ===== */
html[data-halo-theme="dark"] .empty-wrapper .empty-title {
color: var(--halo-text-secondary) !important;
}
/* ===== 提示条(Alert ===== */
html[data-halo-theme="dark"] .alert-wrapper {
background-color: var(--halo-bg-card) !important;
border-color: var(--halo-border-base) !important;
color: var(--halo-text-primary) !important;
}
html[data-halo-theme="dark"] .alert-wrapper .alert-title,
html[data-halo-theme="dark"] .alert-wrapper .alert-description,
html[data-halo-theme="dark"] .alert-wrapper .alert-icon {
color: var(--halo-text-secondary) !important;
}
html[data-halo-theme="dark"] .alert-wrapper.alert-warning {
border-color: oklch(40% 0.12 85) !important;
}
html[data-halo-theme="dark"] .alert-wrapper.alert-error {
border-color: oklch(40% 0.17 25) !important;
}
html[data-halo-theme="dark"] .alert-wrapper.alert-success {
border-color: oklch(40% 0.14 150) !important;
}
/* ===== 实体列表标题链接(覆盖通用链接色,hover 变链接色) ===== */
html[data-halo-theme="dark"] .entity-field-title a,
html[data-halo-theme="dark"] .entity-field-title-body a {
color: var(--halo-text-primary) !important;
}
html[data-halo-theme="dark"] .entity-field-title a:hover,
html[data-halo-theme="dark"] .entity-field-title-body a:hover {
color: var(--halo-text-link) !important;
}
+1 -2
View File
@@ -55,8 +55,7 @@
}
/* ===== 页面标题 ===== */
.page-title,
.v-card-title {
.page-title {
color: var(--halo-text-primary);
}
}
+134
View File
@@ -247,3 +247,137 @@
color: var(--halo-text-primary) !important;
}
}
/* ===== AstraHub / 心愿便签(共用 ah-* UI ===== */
html[data-halo-theme="dark"] .ah-card,
html[data-halo-theme="dark"] .ah-topbar,
html[data-halo-theme="dark"] .ah-topbar-search,
html[data-halo-theme="dark"] .ah-float-nav {
background-color: var(--halo-bg-card) !important;
border-color: var(--halo-border-base) !important;
color: var(--halo-text-primary) !important;
}
html[data-halo-theme="dark"] .ah-topbar-search {
background-color: var(--halo-bg-input) !important;
}
html[data-halo-theme="dark"] .ah-topbar-brand,
html[data-halo-theme="dark"] .planet-hero-title {
color: var(--halo-text-primary) !important;
}
html[data-halo-theme="dark"] .ah-topbar-search input {
background-color: transparent !important;
color: var(--halo-text-primary) !important;
}
html[data-halo-theme="dark"] .planet-links-more {
background-color: var(--halo-bg-card) !important;
color: var(--halo-text-secondary) !important;
}
/* ===== RSS 订阅(links/rss ===== */
html[data-halo-theme="dark"] .subscription-panel,
html[data-halo-theme="dark"] .feed-main,
html[data-halo-theme="dark"] .feed-toolbar,
html[data-halo-theme="dark"] .feed-stream,
html[data-halo-theme="dark"] .feed-stream__footer {
background-color: var(--halo-bg-card) !important;
border-color: var(--halo-border-base) !important;
color: var(--halo-text-primary) !important;
}
html[data-halo-theme="dark"] .feed-status-tabs {
background-color: var(--halo-bg-hover) !important;
color: var(--halo-text-secondary) !important;
}
html[data-halo-theme="dark"] .feed-brief__title,
html[data-halo-theme="dark"] .feed-stream p,
html[data-halo-theme="dark"] .feed-stream a,
html[data-halo-theme="dark"] .subscription-panel a,
html[data-halo-theme="dark"] .subscription-panel span {
color: var(--halo-text-primary) !important;
}
/* ===== 私密文章插件 ===== */
html[data-halo-theme="dark"] .focus-card,
html[data-halo-theme="dark"] .overview-card,
html[data-halo-theme="dark"] .list-card,
html[data-halo-theme="dark"] .empty-state {
background-color: var(--halo-bg-card) !important;
border-color: var(--halo-border-base) !important;
color: var(--halo-text-primary) !important;
}
html[data-halo-theme="dark"] .overview-card h2,
html[data-halo-theme="dark"] .page-shell h2,
html[data-halo-theme="dark"] .overview-stats dd,
html[data-halo-theme="dark"] .overview-stats dt {
color: var(--halo-text-primary) !important;
}
/* ===== AI 评论自动处理插件 ===== */
html[data-halo-theme="dark"] .settings-container,
html[data-halo-theme="dark"] .setting-panel,
html[data-halo-theme="dark"] .tabs-wrap,
html[data-halo-theme="dark"] .sidebar-card,
html[data-halo-theme="dark"] .list-empty,
html[data-halo-theme="dark"] .reply-card {
background-color: var(--halo-bg-card) !important;
border-color: var(--halo-border-base) !important;
color: var(--halo-text-primary) !important;
}
html[data-halo-theme="dark"] .form-row,
html[data-halo-theme="dark"] .list-col,
html[data-halo-theme="dark"] .panel-header,
html[data-halo-theme="dark"] .card-footer {
background-color: var(--halo-bg-hover) !important;
color: var(--halo-text-primary) !important;
}
html[data-halo-theme="dark"] .section-header__text h3,
html[data-halo-theme="dark"] .sidebar-header h4,
html[data-halo-theme="dark"] .panel-header h3,
html[data-halo-theme="dark"] .reply-card h3 {
color: var(--halo-text-primary) !important;
}
html[data-halo-theme="dark"] .wake-word-tag {
background-color: var(--halo-bg-hover) !important;
color: var(--halo-text-secondary) !important;
}
/* ===== 数据工坊 / 代码注入器等工具页语义容器 ===== */
html[data-halo-theme="dark"] .data-studio-card-body,
html[data-halo-theme="dark"] .injector-view-card-body,
html[data-halo-theme="dark"] .injector-editor-container {
background-color: var(--halo-bg-card) !important;
border-color: var(--halo-border-base) !important;
color: var(--halo-text-primary) !important;
}
/* ===== 示例插件页 ===== */
html[data-halo-theme="dark"] main section#plugin-starter {
background-color: var(--halo-bg-body) !important;
color: var(--halo-text-primary) !important;
}
/* ===== 友链卡片徽章 ===== */
html[data-halo-theme="dark"] .link-badge {
background-color: var(--halo-bg-hover) !important;
color: var(--halo-text-primary) !important;
border-color: var(--halo-border-base) !important;
}
/* ===== 日程日历插件(time-column 布局) ===== */
html[data-halo-theme="dark"] .time-column__header,
html[data-halo-theme="dark"] .time-column__body,
html[data-halo-theme="dark"] [class*="time-column"] {
background-color: var(--halo-bg-card) !important;
color: var(--halo-text-primary) !important;
border-color: var(--halo-border-base) !important;
}
html[data-halo-theme="dark"] .entry-card-header__title {
color: var(--halo-text-primary) !important;
}
/* ===== 存储工具箱补充类 ===== */
html[data-halo-theme="dark"] .stat-value,
html[data-halo-theme="dark"] .stat-card .card-title,
html[data-halo-theme="dark"] .panel-card .card-title {
color: var(--halo-text-primary) !important;
}
html[data-halo-theme="dark"] .bar-track {
background-color: var(--halo-bg-disabled) !important;
}
+34
View File
@@ -0,0 +1,34 @@
/* ============================================================
Halo Dark Mode — UnoCSS 哈希类兜底
Halo 2.25 中 UnoCSS 同时生成 uno-* / i-* 哈希类,
这些类随构建变化,只能按结构定位兜底,不写死哈希值。
注意:本文件规则刻意不加 !important,便于后续语义规则覆盖。
============================================================ */
/* 文字兜底:只修正文本元素,豁免表单控件 */
html[data-halo-theme="dark"] main [class*="uno-"]:not(input):not(select):not(textarea) {
color: var(--halo-text-primary);
}
/* 块级容器背景兜底:豁免交互元素与表单控件 */
html[data-halo-theme="dark"] main [class*="uno-"]:not(button):not(a):not(input):not(select):not(textarea):not(label) {
background-color: var(--halo-bg-card);
border-color: var(--halo-border-base);
}
/* 页面级容器使用内容区背景 */
html[data-halo-theme="dark"] main > [class*="uno-"] {
background-color: var(--halo-bg-content);
}
/* 卡片头/卡片体内的 uno-* 区块使用 hover 级背景 */
html[data-halo-theme="dark"] .card-header [class*="uno-"],
html[data-halo-theme="dark"] .card-body [class*="uno-"] {
background-color: var(--halo-bg-hover);
border-color: var(--halo-border-light);
}
/* 旧版 i-* 哈希:只做文字兜底,避免给图标类设置背景 */
html[data-halo-theme="dark"] main [class*="i-"]:not(input):not(select):not(textarea) {
color: var(--halo-text-primary);
}
+1 -4
View File
@@ -62,10 +62,7 @@
}
/* ===== 链接 ===== */
a:not([class*="text-"]),
.hover\:text-gray-900:hover,
.hover\:text-gray-800:hover,
.hover\:text-gray-700:hover {
html[data-halo-theme="dark"] a:not([class*="text-"]) {
color: var(--halo-text-link);
}