fix: 深色模式入口移至外观分组并移除侧边栏按钮(1.0.6)
- 设置入口 group 改为官方 interface(外观)分组 - 删除侧边栏 ThemeToggle 注入器及组件,设置页成为唯一入口 - verify-toggle.py 改为直接驱动主题状态并校验翻转 - 同步 README 与脚本文档,版本升级 1.0.6
This commit is contained in:
@@ -1,64 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import IconSunLine from '~icons/ri/sun-line'
|
||||
import IconMoonLine from '~icons/ri/moon-line'
|
||||
import { useDarkMode } from '../composables/useDarkMode'
|
||||
|
||||
const { isDark, toggle } = useDarkMode()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
type="button"
|
||||
class="theme-toggle"
|
||||
:aria-pressed="isDark"
|
||||
:aria-label="isDark ? '切换到浅色模式' : '切换到深色模式'"
|
||||
@click="toggle"
|
||||
>
|
||||
<IconSunLine v-if="isDark" class="theme-toggle__icon" />
|
||||
<IconMoonLine v-else class="theme-toggle__icon" />
|
||||
<span class="theme-toggle__label">
|
||||
{{ isDark ? '浅色模式' : '深色模式' }}
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.theme-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: var(--halo-text-secondary);
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
border-radius: 0.25rem;
|
||||
transition:
|
||||
background-color 0.15s,
|
||||
color 0.15s;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.theme-toggle:hover {
|
||||
background-color: var(--halo-bg-hover);
|
||||
color: var(--halo-text-primary);
|
||||
}
|
||||
|
||||
.theme-toggle:focus-visible {
|
||||
outline: 2px solid var(--halo-accent-primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.theme-toggle__icon {
|
||||
width: 1.125rem;
|
||||
height: 1.125rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.theme-toggle__label {
|
||||
font-size: 0.8125rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
+2
-4
@@ -3,10 +3,7 @@ import { IconPalette } from '@halo-dev/components'
|
||||
import { markRaw } from 'vue'
|
||||
import './styles/index.css'
|
||||
import { initDarkReaderEngine } from './darkreader-engine'
|
||||
import { injectThemeToggle } from './injector'
|
||||
|
||||
// 在插件加载后将切换器注入到侧边栏,暗色转换完全交给 Dark Reader
|
||||
injectThemeToggle()
|
||||
initDarkReaderEngine()
|
||||
|
||||
export default definePlugin({
|
||||
@@ -23,7 +20,8 @@ export default definePlugin({
|
||||
searchable: true,
|
||||
menu: {
|
||||
name: '深色模式',
|
||||
group: '偏好设置',
|
||||
// Halo 官方「外观」分组
|
||||
group: 'interface',
|
||||
icon: markRaw(IconPalette),
|
||||
priority: 50,
|
||||
},
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import { createVNode, render } from 'vue'
|
||||
import ThemeToggle from './components/ThemeToggle.vue'
|
||||
|
||||
const CONTAINER_CLASS = 'plugin-dark-mode-toggle'
|
||||
const TARGET_SELECTOR = '.sidebar__profile'
|
||||
|
||||
let mounted = false
|
||||
|
||||
/**
|
||||
* 将 ThemeToggle 注入到侧边栏中 UserProfileBanner 上方。
|
||||
* 使用 MutationObserver 等待侧边栏 DOM 渲染完成。
|
||||
*/
|
||||
export function injectThemeToggle(): void {
|
||||
if (mounted) return
|
||||
|
||||
// 先尝试直接查找(侧边栏可能已经渲染)
|
||||
tryMount()
|
||||
|
||||
// 如果还没渲染,等待 DOM 变化
|
||||
if (!mounted) {
|
||||
const observer = new MutationObserver(() => {
|
||||
tryMount()
|
||||
if (mounted) observer.disconnect()
|
||||
})
|
||||
observer.observe(document.body, { childList: true, subtree: true })
|
||||
}
|
||||
}
|
||||
|
||||
function tryMount(): void {
|
||||
// 避免重复挂载
|
||||
if (document.querySelector(`.${CONTAINER_CLASS}`)) {
|
||||
mounted = true
|
||||
return
|
||||
}
|
||||
|
||||
const profileEl = document.querySelector(TARGET_SELECTOR)
|
||||
if (!profileEl?.parentNode) return
|
||||
|
||||
const container = document.createElement('div')
|
||||
container.className = CONTAINER_CLASS
|
||||
profileEl.parentNode.insertBefore(container, profileEl)
|
||||
|
||||
const vnode = createVNode(ThemeToggle)
|
||||
render(vnode, container)
|
||||
mounted = true
|
||||
}
|
||||
@@ -20,7 +20,10 @@ const modeOptions: { value: ThemeMode; label: string; description: string }[] =
|
||||
|
||||
const optionEls = ref<HTMLButtonElement[]>([])
|
||||
const activeIndex = computed(() =>
|
||||
Math.max(0, modeOptions.findIndex((option) => option.value === theme.value)),
|
||||
Math.max(
|
||||
0,
|
||||
modeOptions.findIndex((option) => option.value === theme.value),
|
||||
),
|
||||
)
|
||||
|
||||
function setOptionRef(el: unknown, index: number): void {
|
||||
@@ -79,7 +82,11 @@ function onKeydown(event: KeyboardEvent): void {
|
||||
:class="{ 'is-active': theme === option.value }"
|
||||
:aria-checked="theme === option.value"
|
||||
:tabindex="theme === option.value ? 0 : -1"
|
||||
:ref="(el: unknown) => { setOptionRef(el, index) }"
|
||||
:ref="
|
||||
(el: unknown) => {
|
||||
setOptionRef(el, index)
|
||||
}
|
||||
"
|
||||
@click="setTheme(option.value)"
|
||||
>
|
||||
<div class="dark-mode-settings__option-label">{{ option.label }}</div>
|
||||
@@ -177,4 +184,4 @@ function onKeydown(event: KeyboardEvent): void {
|
||||
font-size: 0.8125rem;
|
||||
color: var(--halo-text-tertiary);
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user