feat: 初始化 Halo 暗色模式插件

- Halo Plugin 后端(Java/Gradle),含 DarkModePlugin 主类和测试
- Vue 3 + TypeScript 前端 UI,包含主题切换组件和设置页面
- 暗色模式 CSS 变量和覆盖样式(布局/编辑器/表单/滚动条等)
- 设计文档和调查文档
- Halo 插件/主题开发 Agent Skills

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-06 21:03:00 +08:00
commit edbf78f236
115 changed files with 14745 additions and 0 deletions
+142
View File
@@ -0,0 +1,142 @@
/* ============================================================
Halo Dark Mode — @halo-dev/components 组件库覆盖
覆盖 Halo 组件库中的 VCard, VModal, VDropdown, VTag 等
============================================================ */
[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);
}
/* ===== 表格 VTable ===== */
table,
.v-table,
[class*="table"] {
background-color: var(--halo-bg-card);
color: var(--halo-text-primary);
}
thead,
.table-header {
background-color: var(--halo-table-header-bg);
}
thead th,
.table-header th {
color: var(--halo-text-secondary);
border-color: var(--halo-table-border);
}
tbody td,
.table-body td {
border-color: var(--halo-table-border);
}
tbody tr:hover,
.table-row:hover {
background-color: var(--halo-table-row-hover);
}
/* ===== 分页 ===== */
.pagination,
.v-pagination {
color: var(--halo-text-secondary);
}
.pagination .active,
.v-pagination .active {
background-color: var(--halo-accent-primary);
color: var(--halo-accent-primary-text);
}
/* ===== 面包屑 ===== */
.breadcrumb,
.v-breadcrumb {
color: var(--halo-text-secondary);
}
.breadcrumb a,
.v-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);
}
}
+71
View File
@@ -0,0 +1,71 @@
/* ============================================================
Halo Dark Mode — 富文本编辑器覆盖
@halo-dev/richtext-editor + TipTap / ProseMirror
============================================================ */
[data-halo-theme="dark"] {
/* ===== ProseMirror / TipTap 编辑器主体 ===== */
.ProseMirror,
.tiptap,
[class*="editor-content"] {
color: var(--halo-text-primary);
background-color: var(--halo-bg-input);
}
/* ===== 编辑器菜单栏 ===== */
.editor-toolbar,
.editor-menubar,
[class*="editor-toolbar"],
[class*="editor-menubar"] {
background-color: var(--halo-bg-card);
border-color: var(--halo-border-base);
}
.editor-toolbar button,
.editor-menubar button {
color: var(--halo-text-secondary);
}
.editor-toolbar button:hover,
.editor-menubar button:hover,
.editor-toolbar button.is-active,
.editor-menubar button.is-active {
background-color: var(--halo-bg-hover);
color: var(--halo-text-primary);
}
/* ===== 代码块 ===== */
.ProseMirror pre,
.ProseMirror code,
.tiptap pre,
.tiptap code {
background-color: oklch(20% 0.015 250);
color: oklch(85% 0.03 160);
}
/* ===== Bubble menu / floating menu ===== */
.tippy-box,
.tiptap-bubble-menu,
.floating-menu {
background-color: var(--halo-bg-dropdown);
border-color: var(--halo-border-base);
color: var(--halo-text-primary);
}
/* ===== 编辑器占位符 ===== */
.ProseMirror p.is-editor-empty:first-child::before {
color: var(--halo-text-tertiary);
}
/* ===== 链接 ===== */
.ProseMirror a,
.tiptap a {
color: var(--halo-text-link);
}
/* ===== 选中文字 ===== */
.ProseMirror ::selection,
.tiptap ::selection {
background-color: oklch(30% 0.04 160 / 50%);
}
}
+89
View File
@@ -0,0 +1,89 @@
/* ============================================================
Halo Dark Mode — FormKit 表单组件覆盖
覆盖输入框、选择器、开关、复选框等表单元素
============================================================ */
[data-halo-theme="dark"] {
/* ===== 输入框 ===== */
input[type="text"],
input[type="password"],
input[type="email"],
input[type="number"],
input[type="search"],
input[type="url"],
input[type="tel"],
textarea,
select,
.formkit-input,
[data-formkit-input] {
background-color: var(--halo-bg-input);
color: var(--halo-text-primary);
border-color: var(--halo-border-input);
}
input:focus,
textarea:focus,
select:focus,
.formkit-input:focus {
border-color: var(--halo-border-focus);
outline-color: var(--halo-border-focus);
}
input::placeholder,
textarea::placeholder,
.formkit-input::placeholder {
color: var(--halo-text-tertiary);
}
input:disabled,
textarea:disabled,
select:disabled {
background-color: var(--halo-bg-disabled);
color: var(--halo-text-tertiary);
}
/* ===== 复选框/单选框 ===== */
input[type="checkbox"],
input[type="radio"],
.formkit-checkbox,
.formkit-radio {
accent-color: var(--halo-accent-primary);
}
/* ===== FormKit 标签和帮助文本 ===== */
.formkit-label,
.formkit-legend {
color: var(--halo-text-secondary);
}
.formkit-help,
.formkit-message {
color: var(--halo-text-tertiary);
}
/* ===== FormKit 外层 ===== */
.formkit-outer {
color: var(--halo-text-primary);
}
/* ===== FormKit 前缀/后缀 ===== */
.formkit-prefix,
.formkit-suffix {
background-color: var(--halo-bg-hover);
color: var(--halo-text-secondary);
border-color: var(--halo-border-input);
}
/* ===== 选择器下拉 ===== */
select option {
background-color: var(--halo-bg-dropdown);
color: var(--halo-text-primary);
}
/* ===== 代码块 in forms ===== */
.formkit-code {
background-color: var(--halo-bg-hover);
color: var(--halo-text-primary);
border-color: var(--halo-border-base);
}
}
+62
View File
@@ -0,0 +1,62 @@
/* ============================================================
Halo Dark Mode — 核心布局覆盖
覆盖 BasicLayout.vue 的硬编码颜色
============================================================ */
[data-halo-theme="dark"] {
/* ===== 页面整体 ===== */
.layout {
background-color: var(--halo-bg-body);
}
/* ===== 侧边栏 ===== */
.sidebar {
background-color: var(--halo-bg-sidebar);
box-shadow: var(--halo-shadow-sm);
}
.sidebar__search {
background-color: var(--halo-search-bg);
color: var(--halo-search-placeholder);
}
.sidebar__search:hover {
color: var(--halo-search-text);
}
.sidebar__search-icon {
color: inherit;
}
.sidebar__search-shortcut {
color: var(--halo-text-tertiary);
}
.sidebar__logo {
filter: brightness(0.9) invert(0);
}
.sidebar__profile {
border-top-color: var(--halo-border-light);
}
/* ===== 内容区 ===== */
.main-content {
background-color: var(--halo-bg-content);
color: var(--halo-text-primary);
}
.main-content__footer-text {
color: var(--halo-text-tertiary);
}
.main-content__footer-link {
color: var(--halo-text-link);
}
/* ===== 页面标题 ===== */
.page-title,
.v-card-title {
color: var(--halo-text-primary);
}
}
+48
View File
@@ -0,0 +1,48 @@
/* ============================================================
Halo Dark Mode — 滚动条覆盖
OverlayScrollbars + 原生滚动条
============================================================ */
[data-halo-theme="dark"] {
/* ===== OverlayScrollbars ===== */
.os-scrollbar .os-scrollbar-handle {
background-color: var(--halo-scrollbar-thumb);
}
.os-scrollbar .os-scrollbar-track {
background-color: var(--halo-scrollbar-track);
}
.os-scrollbar .os-scrollbar-handle:hover {
background-color: oklch(45% 0.03 250);
}
/* ===== 原生滚动条(Firefox ===== */
* {
scrollbar-color: var(--halo-scrollbar-thumb) var(--halo-scrollbar-track);
scrollbar-width: thin;
}
/* ===== 原生滚动条(WebkitChrome/Edge/Safari ===== */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--halo-scrollbar-track);
}
::-webkit-scrollbar-thumb {
background: var(--halo-scrollbar-thumb);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: oklch(45% 0.03 250);
}
::-webkit-scrollbar-corner {
background: var(--halo-scrollbar-track);
}
}
+77
View File
@@ -0,0 +1,77 @@
/* ============================================================
Halo Dark Mode — Tailwind 工具类覆盖
覆盖 Tailwind 的颜色类和常见 UI 原子类
============================================================ */
[data-halo-theme="dark"] {
/* ===== 背景色 ===== */
.bg-white {
background-color: var(--halo-bg-card);
}
.bg-gray-50,
.bg-gray-100 {
background-color: var(--halo-bg-content);
}
.bg-gray-200 {
background-color: var(--halo-bg-hover);
}
/* ===== 文字色 ===== */
.text-gray-900,
.text-gray-800 {
color: var(--halo-text-primary);
}
.text-gray-700,
.text-gray-600,
.text-gray-500 {
color: var(--halo-text-secondary);
}
.text-gray-400,
.text-gray-300 {
color: var(--halo-text-tertiary);
}
/* ===== 边框 ===== */
.border,
.border-gray-200,
.border-gray-300 {
border-color: var(--halo-border-base);
}
.divide-y > :not([hidden]) ~ :not([hidden]),
.divide-x > :not([hidden]) ~ :not([hidden]) {
border-color: var(--halo-border-light);
}
/* ===== 阴影 ===== */
.shadow {
box-shadow: var(--halo-shadow-base);
}
.shadow-sm {
box-shadow: var(--halo-shadow-sm);
}
.shadow-lg,
.shadow-xl {
box-shadow: var(--halo-shadow-lg);
}
/* ===== 链接 ===== */
a:not([class*="text-"]),
.hover\:text-gray-900:hover,
.hover\:text-gray-800:hover,
.hover\:text-gray-700:hover {
color: var(--halo-text-link);
}
/* ===== 通用悬停 ===== */
.hover\:bg-gray-50:hover,
.hover\:bg-gray-100:hover {
background-color: var(--halo-bg-hover);
}
}