feat: 1.0.3 引入 Dark Reader 通用暗色引擎
This commit is contained in:
@@ -10,8 +10,16 @@ build/
|
|||||||
*.iml
|
*.iml
|
||||||
.DS_Store
|
.DS_Store
|
||||||
node_modules/
|
node_modules/
|
||||||
|
.pnpm-store/
|
||||||
dist/
|
dist/
|
||||||
# Playwright 测试工具 — 只跟踪脚本和配置,忽略输出/截图/缓存
|
# Playwright 测试工具 — 只跟踪脚本和配置,忽略输出/截图/缓存
|
||||||
workplace/*
|
workplace/*
|
||||||
!workplace/*.py
|
!workplace/*.py
|
||||||
!workplace/*.yaml
|
!workplace/*.yaml
|
||||||
|
|
||||||
|
# 暗色扫描输出与会话
|
||||||
|
scripts/output/
|
||||||
|
scripts/.browser-profile/
|
||||||
|
|
||||||
|
# Dark Reader 源码下载目录(构建依赖已 vendor 到 ui/vendor/darkreader)
|
||||||
|
docs/darkreader/
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ halo-dark-mode-plugin/
|
|||||||
│
|
│
|
||||||
├── 前端 (Vue 3 / TypeScript) ← 核心实现
|
├── 前端 (Vue 3 / TypeScript) ← 核心实现
|
||||||
│ ├── index.ts ← definePlugin() 入口,注册路由+组件
|
│ ├── index.ts ← definePlugin() 入口,注册路由+组件
|
||||||
|
│ ├── darkreader-engine.ts ← Dark Reader 通用暗色引擎
|
||||||
│ ├── injector.ts ← ThemeToggle 侧边栏注入器(MutationObserver 方案)
|
│ ├── injector.ts ← ThemeToggle 侧边栏注入器(MutationObserver 方案)
|
||||||
│ ├── composables/
|
│ ├── composables/
|
||||||
│ │ ├── useDarkMode.ts ← 单例状态管理(light/dark/auto + localStorage 持久化)
|
│ │ ├── useDarkMode.ts ← 单例状态管理(light/dark/auto + localStorage 持久化)
|
||||||
@@ -80,18 +81,23 @@ halo-dark-mode-plugin/
|
|||||||
### 主题切换机制
|
### 主题切换机制
|
||||||
|
|
||||||
- **触发方式**:`document.documentElement` 上设置/移除 `data-halo-theme="dark"` 属性
|
- **触发方式**:`document.documentElement` 上设置/移除 `data-halo-theme="dark"` 属性
|
||||||
|
- **主引擎**:Dark Reader(本地 vendored 于 `ui/vendor/darkreader`,API 包由
|
||||||
|
`npm run api` 构建),监听 `useDarkMode().isDark`,深色时 `enable()`,浅色时
|
||||||
|
`disable()`,可自动分析 Halo 核心与第三方插件的动态 DOM
|
||||||
- **CSS 变量体系**:所有颜色通过 `--halo-*` 前缀的 CSS 自定义属性控制,一个语义变量对应一个视觉属性
|
- **CSS 变量体系**:所有颜色通过 `--halo-*` 前缀的 CSS 自定义属性控制,一个语义变量对应一个视觉属性
|
||||||
- **颜色空间**:全部使用 OKLCH(感知均匀,暗色模式天然适配)
|
- **颜色空间**:全部使用 OKLCH(感知均匀,暗色模式天然适配)
|
||||||
- **暗色配色策略**:用亮度层次区分背景(越"高"的层越亮),中性色含微量蓝色调,强调色略降饱和
|
- **暗色配色策略**:用亮度层次区分背景(越"高"的层越亮),中性色含微量蓝色调,强调色略降饱和
|
||||||
|
- **CSS 覆盖定位**:现有 `overrides/` 仅作为 Dark Reader 的兼容层与兜底,
|
||||||
|
不再逐页新增手工转换规则;新页面残留统一交给 Dark Reader 处理
|
||||||
|
|
||||||
### ⚠️ 最关键的教训:Halo 2.25 用 UnoCSS,不是 Tailwind
|
### ⚠️ 最关键的教训:Halo 2.25 用 UnoCSS,不是 Tailwind
|
||||||
|
|
||||||
Halo 2.25 的 Console 实际使用 **UnoCSS**(hash 类如 `uno-*`)加 **BEM 语义类**。**不要写 `.bg-white`、`.text-gray-900`、`.v-card` 这类 Tailwind/Vuetify 原子选择器**——它们在真实 DOM 中不存在,覆盖会静默失效。
|
Halo 2.25 的 Console 实际使用 **UnoCSS**(hash 类如 `uno-*`)加 **BEM 语义aliyun "curl -sI --max-time 5 http://localhost/ 2>&1 |类**。**不要写 `.bg-white`、`.text-gray-900`、`.v-card` 这类 Tailwind/Vuetify 原子选择器**——它们在真实 DOM 中不存在,覆盖会静默失效。
|
||||||
|
|
||||||
真实 DOM 里的容器类名(已被 `halo-core.css` 覆盖):
|
真实 DOM 里的容器类名(已被 `halo-core.css` 覆盖):
|
||||||
|
|
||||||
| 语义 | 真实类名 |
|
| 语义 | 真实类名 |
|
||||||
|------|---------|
|
| ------------------- | --------------------------------------------------------------------------------------------- |
|
||||||
| 页面顶栏 | `.page-header` / `.page-header__title-text` |
|
| 页面顶栏 | `.page-header` / `.page-header__title-text` |
|
||||||
| 列表卡片容器 | `.card-wrapper` |
|
| 列表卡片容器 | `.card-wrapper` |
|
||||||
| 文章/用户列表项标题 | `.entity-field-title` / `.entity-field-title-body` |
|
| 文章/用户列表项标题 | `.entity-field-title` / `.entity-field-title-body` |
|
||||||
@@ -107,6 +113,7 @@ Halo 2.25 的 Console 实际使用 **UnoCSS**(hash 类如 `uno-*`)加 **BEM
|
|||||||
### 覆盖策略(重写后)
|
### 覆盖策略(重写后)
|
||||||
|
|
||||||
`halo-core.css` 是主要覆盖文件,按语义类精准覆盖。它包含三类规则:
|
`halo-core.css` 是主要覆盖文件,按语义类精准覆盖。它包含三类规则:
|
||||||
|
|
||||||
1. **BEM 语义类**(如 `.card-wrapper`)— 直接 `[data-halo-theme="dark"] .card-wrapper { background-color: var(--halo-bg-card) }`
|
1. **BEM 语义类**(如 `.card-wrapper`)— 直接 `[data-halo-theme="dark"] .card-wrapper { background-color: var(--halo-bg-card) }`
|
||||||
2. **通用 UnoCSS 工具类**(如 `.bg-gray-50`、`.hover:text-gray-600`)— 批量覆盖文字/背景/边框
|
2. **通用 UnoCSS 工具类**(如 `.bg-gray-50`、`.hover:text-gray-600`)— 批量覆盖文字/背景/边框
|
||||||
3. **根背景兜底** — `html, body { background-color: var(--halo-bg-body) !important }`(body 不设暗色会在溢出时露白)
|
3. **根背景兜底** — `html, body { background-color: var(--halo-bg-body) !important }`(body 不设暗色会在溢出时露白)
|
||||||
@@ -134,6 +141,15 @@ Halo 扩展点系统**没有侧边栏插槽**。`injector.ts` 用 `MutationObser
|
|||||||
|
|
||||||
后端极简 — `DarkModePlugin extends BasePlugin` 仅含 `start()`/`stop()` 生命周期钩子。所有核心逻辑在前端。插件不依赖后端 Setting API。
|
后端极简 — `DarkModePlugin extends BasePlugin` 仅含 `start()`/`stop()` 生命周期钩子。所有核心逻辑在前端。插件不依赖后端 Setting API。
|
||||||
|
|
||||||
|
## 打包约定(必须)
|
||||||
|
|
||||||
|
每次执行 `./gradlew build` 打 JAR 前,**必须先递增版本标签**:
|
||||||
|
|
||||||
|
1. 修改 `gradle.properties` 中的 `version`(如 `1.0.2` → `1.0.3`)
|
||||||
|
2. 同步更新 `src/main/java/run/halo/darkmode/DarkModePlugin.java` 的 `@since`
|
||||||
|
3. 构建完成后核对 `build/libs/plugin-dark-mode-<新版本>.jar` 存在,且 JAR 内的
|
||||||
|
`ui/style.css`、`ui/main.js` 包含本次改动(用关键字检查,例如新增类名)
|
||||||
|
4. 向用户交付时明确给出新 JAR 路径和版本号,避免线上仍加载旧 bundle 的混淆
|
||||||
## 验证工作流(必须)
|
## 验证工作流(必须)
|
||||||
|
|
||||||
对 CSS 覆盖的任何改动,都要在**真实 Halo 环境**验证,不能只靠 `vite build` 通过:
|
对 CSS 覆盖的任何改动,都要在**真实 Halo 环境**验证,不能只靠 `vite build` 通过:
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ Halo 2.25 暗色模式插件 — 为 Halo 后台管理面板提供深色/浅色
|
|||||||
- ⚡ **瞬间切换**:CSS 变量瞬时生效,无可见闪烁
|
- ⚡ **瞬间切换**:CSS 变量瞬时生效,无可见闪烁
|
||||||
- 🧩 **侧边栏注入**:切换按钮自动出现在侧边栏底部(UserProfileBanner 上方)
|
- 🧩 **侧边栏注入**:切换按钮自动出现在侧边栏底部(UserProfileBanner 上方)
|
||||||
- ⚙️ **设置页面**:提供详细的模式选择界面(菜单 → 偏好设置 → 深色模式)
|
- ⚙️ **设置页面**:提供详细的模式选择界面(菜单 → 偏好设置 → 深色模式)
|
||||||
|
- 🔍 **Dark Reader 引擎**:内置成熟的开源暗色转换引擎,自动分析页面 CSS 与 DOM,
|
||||||
|
第三方插件页面的黑字/白底也能自动转换,不再依赖逐页手工覆盖
|
||||||
- 🎨 **OKLCH 色彩空间**:感知均匀,暗色模式天然适配,WCAG AA 对比度保证
|
- 🎨 **OKLCH 色彩空间**:感知均匀,暗色模式天然适配,WCAG AA 对比度保证
|
||||||
- 📦 **零后端依赖**:纯前端实现,不需要后端 API
|
- 📦 **零后端依赖**:纯前端实现,不需要后端 API
|
||||||
- ✅ **覆盖已验证**:仪表盘、内容管理(文章/页面/评论)、链接、订阅、瞬间、用户、主题、设置、编辑器、模态框等页面已针对 Halo 2.25 真实 DOM 逐一验证
|
- ✅ **覆盖已验证**:仪表盘、内容管理(文章/页面/评论)、链接、订阅、瞬间、用户、主题、设置、编辑器、模态框等页面已针对 Halo 2.25 真实 DOM 逐一验证
|
||||||
@@ -69,6 +71,7 @@ pnpm test:unit # 单元测试
|
|||||||
└── ui/
|
└── ui/
|
||||||
└── src/
|
└── src/
|
||||||
├── index.ts # definePlugin 入口
|
├── index.ts # definePlugin 入口
|
||||||
|
├── darkreader-engine.ts # Dark Reader 通用暗色引擎
|
||||||
├── injector.ts # ThemeToggle 侧边栏注入器
|
├── injector.ts # ThemeToggle 侧边栏注入器
|
||||||
├── composables/
|
├── composables/
|
||||||
│ ├── useDarkMode.ts # 核心状态管理(模块级单例)
|
│ ├── useDarkMode.ts # 核心状态管理(模块级单例)
|
||||||
|
|||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
version=1.0.1
|
version=1.0.3
|
||||||
org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8
|
org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8
|
||||||
org.gradle.java.home=D:/settings/Language/Java/jdk-25.0.1
|
org.gradle.java.home=D:/settings/Language/Java/jdk-25.0.1
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# 暗色模式回归扫描工具
|
||||||
|
|
||||||
|
用 Playwright 驱动 Edge 对 Halo 后台做全路由深浅色残留扫描,验证每次 CSS 改动。
|
||||||
|
|
||||||
|
## 依赖
|
||||||
|
|
||||||
|
- Python 3.13 环境(本机:`D:\settings\settings\uv\my_uv_env`)
|
||||||
|
- Playwright(已安装):`pip install playwright` 或 `uv pip install playwright`
|
||||||
|
- 本机 Edge(Playwright 通过 `channel="msedge"` 复用,无需下载浏览器内核)
|
||||||
|
|
||||||
|
## 首次使用:登录
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
D:\settings\settings\uv\my_uv_env\Scripts\python.exe scripts\login-wait.py
|
||||||
|
```
|
||||||
|
|
||||||
|
会弹出 Edge 窗口,登录 `https://blog.liuhangyv.top/console` 后脚本自动退出,
|
||||||
|
会话保存在 `scripts/.browser-profile/`(已被 gitignore,不会入库)。
|
||||||
|
|
||||||
|
已有登录态想复用,可显式指定 profile:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
D:\settings\settings\uv\my_uv_env\Scripts\python.exe scripts\scan-dark.py --profile workplace\pw-profile
|
||||||
|
```
|
||||||
|
|
||||||
|
## 常用命令
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# 全路由深色扫描,注入本地构建产物做预部署验证,存在残留时返回非 0
|
||||||
|
D:\settings\settings\uv\my_uv_env\Scripts\python.exe scripts\scan-dark.py --local-css ui\build\dist\style.css --assert-zero
|
||||||
|
|
||||||
|
# 只扫指定页面
|
||||||
|
D:\settings\settings\uv\my_uv_env\Scripts\python.exe scripts\scan-dark.py --pages /console/posts,/console/users
|
||||||
|
|
||||||
|
# 浅色模式回归抽查
|
||||||
|
D:\settings\settings\uv\my_uv_env\Scripts\python.exe scripts\scan-dark.py --mode light --pages /console/dashboard,/console/posts
|
||||||
|
```
|
||||||
|
|
||||||
|
## 输出
|
||||||
|
|
||||||
|
- `scripts/output/scan-results.json`:按路由分组的结果
|
||||||
|
- `scripts/output/shots/*.png`:每页截图
|
||||||
|
- 均已被 gitignore,不入库。
|
||||||
|
|
||||||
|
## 切换行为验证
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
D:\settings\settings\uv\my_uv_env\Scripts\python.exe scripts\verify-toggle.py
|
||||||
|
```
|
||||||
|
|
||||||
|
部署新版插件后运行,会检查侧边栏按钮切换、`data-halo-theme` 属性、
|
||||||
|
localStorage 持久化以及 Monaco 日志查看器主题是否同步。
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
- 扫描会忽略 `img/canvas/video/svg/iframe`,避免把文章图片、编辑器画布等内容性
|
||||||
|
元素误判为界面残留。
|
||||||
|
- 路由从 Vue Router 动态发现,Halo 或插件升级新增页面后无需维护清单。
|
||||||
|
- `--local-css` 用 `add_style_tag` 追加本地样式(不替换线上 bundle),可安全验证
|
||||||
|
CSS 修复;JS 改动(如 Monaco 主题同步)需要重新构建并部署后再跑 `verify-toggle.py`。
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
"""打开带持久化配置的 Edge 窗口,等待用户在 Halo 后台完成登录。
|
||||||
|
|
||||||
|
登录成功后会话保存在 profile 目录,供 scan-dark.py 复用。
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import pathlib
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
|
||||||
|
DEFAULT_PROFILE = pathlib.Path(__file__).parent / ".browser-profile"
|
||||||
|
LOGIN_URL = "https://blog.liuhangyv.top/console/login"
|
||||||
|
TIMEOUT_S = 280
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description="登录 Halo 后台并保存会话")
|
||||||
|
parser.add_argument("--profile", default=str(DEFAULT_PROFILE))
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
with sync_playwright() as p:
|
||||||
|
ctx = p.chromium.launch_persistent_context(
|
||||||
|
args.profile,
|
||||||
|
channel="msedge",
|
||||||
|
headless=False,
|
||||||
|
viewport={"width": 1600, "height": 950},
|
||||||
|
)
|
||||||
|
page = ctx.pages[0] if ctx.pages else ctx.new_page()
|
||||||
|
page.goto(LOGIN_URL)
|
||||||
|
print("浏览器窗口已打开,请在其中登录 Halo 后台...", flush=True)
|
||||||
|
|
||||||
|
deadline = time.time() + TIMEOUT_S
|
||||||
|
while time.time() < deadline:
|
||||||
|
try:
|
||||||
|
url = page.url
|
||||||
|
except Exception:
|
||||||
|
print("检测到窗口被关闭", flush=True)
|
||||||
|
return 2
|
||||||
|
if "/console" in url and "/login" not in url:
|
||||||
|
time.sleep(3)
|
||||||
|
print(f"检测到登录成功: {url}", flush=True)
|
||||||
|
ctx.close()
|
||||||
|
return 0
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
print("等待超时,未检测到登录", flush=True)
|
||||||
|
ctx.close()
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
"""Halo 后台暗色模式残留扫描器(正式回归工具)。
|
||||||
|
|
||||||
|
用法示例:
|
||||||
|
python scripts/scan-dark.py --local-css ui/build/dist/style.css --assert-zero
|
||||||
|
python scripts/scan-dark.py --mode light --pages /console/posts,/console/users
|
||||||
|
python scripts/scan-dark.py --profile workplace/pw-profile
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import pathlib
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
|
||||||
|
try:
|
||||||
|
sys.stdout.reconfigure(encoding="utf-8")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
BASE = "https://blog.liuhangyv.top"
|
||||||
|
DEFAULT_PROFILE = pathlib.Path(__file__).parent / ".browser-profile"
|
||||||
|
DEFAULT_OUTPUT = pathlib.Path(__file__).parent / "output"
|
||||||
|
|
||||||
|
SCAN_JS = r"""
|
||||||
|
() => {
|
||||||
|
const EXCLUDED_TAGS = new Set(['IMG', 'CANVAS', 'VIDEO', 'SVG', 'IFRAME']);
|
||||||
|
const results = [];
|
||||||
|
const seen = new Set();
|
||||||
|
const isVisible = (el) => {
|
||||||
|
const cs = getComputedStyle(el);
|
||||||
|
return cs.display !== 'none' && cs.visibility !== 'hidden' && +cs.opacity > 0.05;
|
||||||
|
};
|
||||||
|
const shortPath = (el) => {
|
||||||
|
const parts = [];
|
||||||
|
let cur = el;
|
||||||
|
for (let i = 0; i < 5 && cur && cur !== document.body; i++) {
|
||||||
|
let p = cur.tagName.toLowerCase();
|
||||||
|
if (cur.id) p += '#' + cur.id;
|
||||||
|
else if (typeof cur.className === 'string' && cur.className.trim()) {
|
||||||
|
p += '.' + cur.className.trim().split(/\s+/).slice(0, 2).join('.');
|
||||||
|
}
|
||||||
|
parts.unshift(p);
|
||||||
|
cur = cur.parentElement;
|
||||||
|
}
|
||||||
|
return parts.join(' > ');
|
||||||
|
};
|
||||||
|
document.querySelectorAll('body *').forEach(el => {
|
||||||
|
if (EXCLUDED_TAGS.has(el.tagName)) return;
|
||||||
|
if (!isVisible(el)) return;
|
||||||
|
const r = el.getBoundingClientRect();
|
||||||
|
if (r.width < 50 || r.height < 20) return;
|
||||||
|
const cs = getComputedStyle(el);
|
||||||
|
const issues = [];
|
||||||
|
const bg = cs.backgroundColor.match(/rgba?\(([\d.]+),\s*([\d.]+),\s*([\d.]+)(?:,\s*([\d.]+))?\)/);
|
||||||
|
if (bg && bg[4] !== '0' && +bg[1] > 235 && +bg[2] > 235 && +bg[3] > 235) {
|
||||||
|
issues.push('light-bg ' + cs.backgroundColor);
|
||||||
|
}
|
||||||
|
const hasText = [...el.childNodes].some(n => n.nodeType === 3 && n.textContent.trim());
|
||||||
|
const c = cs.color.match(/rgba?\(([\d.]+),\s*([\d.]+),\s*([\d.]+)(?:,\s*([\d.]+))?\)/);
|
||||||
|
if (hasText && c && (+c[4] ?? 1) > 0.05 && (+c[1] + +c[2] + +c[3]) / 3 < 95) {
|
||||||
|
issues.push('dark-text ' + cs.color);
|
||||||
|
}
|
||||||
|
if (!issues.length) return;
|
||||||
|
const cls = (typeof el.className === 'string' ? el.className : '').trim().replace(/\s+/g, ' ').slice(0, 150);
|
||||||
|
const key = el.tagName + '|' + cls + '|' + issues.join(',');
|
||||||
|
if (seen.has(key)) return;
|
||||||
|
seen.add(key);
|
||||||
|
results.push({
|
||||||
|
tag: el.tagName.toLowerCase(),
|
||||||
|
cls,
|
||||||
|
path: shortPath(el),
|
||||||
|
issues,
|
||||||
|
size: Math.round(r.width) + 'x' + Math.round(r.height),
|
||||||
|
text: (el.textContent || '').trim().slice(0, 40),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def slug(route: str) -> str:
|
||||||
|
return re.sub(r"[^a-z0-9]+", "-", route.lower()).strip("-") or "root"
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description="Halo 后台暗色模式残留扫描")
|
||||||
|
parser.add_argument("--base", default=BASE)
|
||||||
|
parser.add_argument("--profile", default=str(DEFAULT_PROFILE))
|
||||||
|
parser.add_argument("--output-dir", default=str(DEFAULT_OUTPUT))
|
||||||
|
parser.add_argument("--mode", choices=["dark", "light"], default="dark")
|
||||||
|
parser.add_argument("--pages", default="", help="逗号分隔的路由子集,默认扫描全部")
|
||||||
|
parser.add_argument("--local-css", default="", help="注入本地构建的 style.css 做预部署验证")
|
||||||
|
parser.add_argument("--assert-zero", action="store_true", help="存在残留时以退出码 1 结束")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
output = pathlib.Path(args.output_dir)
|
||||||
|
shots = output / "shots"
|
||||||
|
shots.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
if args.mode == "dark":
|
||||||
|
init_js = """
|
||||||
|
try { localStorage.setItem('halo-dark-mode-theme', 'dark'); } catch (e) {}
|
||||||
|
document.documentElement.setAttribute('data-halo-theme', 'dark');
|
||||||
|
"""
|
||||||
|
else:
|
||||||
|
init_js = """
|
||||||
|
try { localStorage.setItem('halo-dark-mode-theme', 'light'); } catch (e) {}
|
||||||
|
document.documentElement.removeAttribute('data-halo-theme');
|
||||||
|
"""
|
||||||
|
|
||||||
|
with sync_playwright() as p:
|
||||||
|
ctx = p.chromium.launch_persistent_context(
|
||||||
|
args.profile,
|
||||||
|
channel="msedge",
|
||||||
|
headless=True,
|
||||||
|
viewport={"width": 1600, "height": 950},
|
||||||
|
)
|
||||||
|
ctx.add_init_script(init_js)
|
||||||
|
page = ctx.pages[0] if ctx.pages else ctx.new_page()
|
||||||
|
page.goto(args.base + "/console/dashboard", wait_until="domcontentloaded")
|
||||||
|
try:
|
||||||
|
page.wait_for_load_state("networkidle", timeout=10000)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
page.wait_for_selector("#app > *, .sidebar, .main-content", timeout=20000)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
time.sleep(2)
|
||||||
|
if "/login" in page.url:
|
||||||
|
print("SESSION_EXPIRED 登录态失效,请先运行 scripts/login-wait.py 登录")
|
||||||
|
ctx.close()
|
||||||
|
return 3
|
||||||
|
|
||||||
|
routes = []
|
||||||
|
try:
|
||||||
|
routes = page.evaluate(
|
||||||
|
"""() => {
|
||||||
|
const app = document.querySelector('#app').__vue_app__;
|
||||||
|
const router = app.config.globalProperties.$router;
|
||||||
|
return router.getRoutes().map(r => r.path);
|
||||||
|
}"""
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
routes = page.evaluate(
|
||||||
|
"""() => [...new Set([...document.querySelectorAll('a[href]')]
|
||||||
|
.map(a => a.getAttribute('href'))
|
||||||
|
.filter(h => h && h.startsWith('/console')))]"""
|
||||||
|
)
|
||||||
|
routes = sorted(
|
||||||
|
{
|
||||||
|
r if r.startswith("/console") else "/console" + r
|
||||||
|
for r in routes
|
||||||
|
if r.startswith("/") and ":" not in r and r not in ("/", "/console")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
editor = [r for r in routes if "editor" in r]
|
||||||
|
routes = [r for r in routes if "editor" not in r] + editor[:1]
|
||||||
|
if args.pages:
|
||||||
|
wanted = [r.strip() for r in args.pages.split(",") if r.strip()]
|
||||||
|
routes = [r for r in wanted if r.startswith("/console")]
|
||||||
|
|
||||||
|
print(f"模式={args.mode} 待扫描路由数={len(routes)}", flush=True)
|
||||||
|
all_results = {}
|
||||||
|
for route in routes:
|
||||||
|
try:
|
||||||
|
last_error = None
|
||||||
|
for attempt in range(3):
|
||||||
|
try:
|
||||||
|
page.goto(args.base + route, wait_until="domcontentloaded")
|
||||||
|
last_error = None
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
last_error = e
|
||||||
|
print(f"{route}: 第 {attempt + 1} 次导航失败,稍后重试", flush=True)
|
||||||
|
time.sleep(8)
|
||||||
|
if last_error:
|
||||||
|
raise last_error
|
||||||
|
|
||||||
|
try:
|
||||||
|
page.wait_for_load_state("networkidle", timeout=8000)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
# 限流时 Halo bundle 可能加载较慢,确保应用渲染完成再扫描
|
||||||
|
page.wait_for_selector("#app > *, .sidebar, .main-content", timeout=20000)
|
||||||
|
if args.local_css:
|
||||||
|
page.add_style_tag(path=args.local_css)
|
||||||
|
time.sleep(2)
|
||||||
|
if args.mode == "dark":
|
||||||
|
page.evaluate("document.documentElement.setAttribute('data-halo-theme','dark')")
|
||||||
|
else:
|
||||||
|
page.evaluate("document.documentElement.removeAttribute('data-halo-theme')")
|
||||||
|
time.sleep(0.3)
|
||||||
|
items = page.evaluate(SCAN_JS)
|
||||||
|
all_results[route] = items
|
||||||
|
page.screenshot(path=str(shots / (slug(route) + ".png")))
|
||||||
|
print(f"{route}: {len(items)} 处疑似残留", flush=True)
|
||||||
|
except Exception as e:
|
||||||
|
all_results[route] = {"error": str(e)[:200]}
|
||||||
|
print(f"{route}: 扫描失败 {e}", flush=True)
|
||||||
|
|
||||||
|
out_json = output / "scan-results.json"
|
||||||
|
out_json.write_text(json.dumps(all_results, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||||
|
ctx.close()
|
||||||
|
|
||||||
|
total = sum(
|
||||||
|
len(v) for v in all_results.values() if isinstance(v, list)
|
||||||
|
)
|
||||||
|
dirty = sum(1 for v in all_results.values() if isinstance(v, list) and v)
|
||||||
|
print(f"DONE 残留条目={total} 残留路由={dirty} 结果文件={out_json}", flush=True)
|
||||||
|
if args.assert_zero and dirty:
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
"""验证侧边栏主题切换:点击后检查 data-halo-theme、localStorage 与 Monaco 主题同步。
|
||||||
|
|
||||||
|
部署新版插件后运行可获得完整结果;预部署阶段 Monaco 断言会提示跳过。
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import pathlib
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
|
||||||
|
DEFAULT_PROFILE = pathlib.Path(__file__).parent / ".browser-profile"
|
||||||
|
BASE = "https://blog.liuhangyv.top"
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description="验证主题切换运行时行为")
|
||||||
|
parser.add_argument("--base", default=BASE)
|
||||||
|
parser.add_argument("--profile", default=str(DEFAULT_PROFILE))
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
with sync_playwright() as p:
|
||||||
|
ctx = p.chromium.launch_persistent_context(
|
||||||
|
args.profile, channel="msedge", headless=True,
|
||||||
|
viewport={"width": 1600, "height": 950},
|
||||||
|
)
|
||||||
|
page = ctx.pages[0] if ctx.pages else ctx.new_page()
|
||||||
|
page.goto(args.base + "/console/dashboard", wait_until="domcontentloaded")
|
||||||
|
try:
|
||||||
|
page.wait_for_load_state("networkidle", timeout=10000)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
time.sleep(3)
|
||||||
|
if "/login" in page.url:
|
||||||
|
print("SESSION_EXPIRED 请先运行 scripts/login-wait.py")
|
||||||
|
ctx.close()
|
||||||
|
return 3
|
||||||
|
|
||||||
|
toggle = page.locator(".theme-toggle")
|
||||||
|
toggle.wait_for(state="visible", timeout=15000)
|
||||||
|
before = page.evaluate("() => ({ attr: document.documentElement.getAttribute('data-halo-theme'), stored: localStorage.getItem('halo-dark-mode-theme') })")
|
||||||
|
toggle.click()
|
||||||
|
time.sleep(0.5)
|
||||||
|
after = page.evaluate("() => ({ attr: document.documentElement.getAttribute('data-halo-theme'), stored: localStorage.getItem('halo-dark-mode-theme') })")
|
||||||
|
toggle.click()
|
||||||
|
time.sleep(0.5)
|
||||||
|
restored = page.evaluate("() => ({ attr: document.documentElement.getAttribute('data-halo-theme'), stored: localStorage.getItem('halo-dark-mode-theme') })")
|
||||||
|
|
||||||
|
attr_flipped = before["attr"] != after["attr"] and before["attr"] == restored["attr"]
|
||||||
|
stored_flipped = before["stored"] != after["stored"] and before["stored"] == restored["stored"]
|
||||||
|
print(f"初始: {before}")
|
||||||
|
print(f"切换: {after}")
|
||||||
|
print(f"还原: {restored}")
|
||||||
|
print(f"属性翻转: {'PASS' if attr_flipped else 'FAIL'} 存储翻转: {'PASS' if stored_flipped else 'FAIL'}")
|
||||||
|
|
||||||
|
page.goto(args.base + "/console/log-viewer", wait_until="domcontentloaded")
|
||||||
|
try:
|
||||||
|
page.wait_for_load_state("networkidle", timeout=6000)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
time.sleep(2)
|
||||||
|
has_monaco = page.locator(".monaco-editor").count() > 0
|
||||||
|
if has_monaco:
|
||||||
|
dark_attr = page.evaluate("document.documentElement.getAttribute('data-halo-theme')") == "dark"
|
||||||
|
theme_cls = page.evaluate("() => { const el = document.querySelector('.monaco-editor'); return el ? el.className : '' }")
|
||||||
|
monaco_dark = "vs-dark" in theme_cls
|
||||||
|
print(f"Monaco 实例: 存在 | 属性dark={dark_attr} | 主题类含vs-dark={monaco_dark} | class={theme_cls[:60]}")
|
||||||
|
print(f"Monaco 主题同步: {'PASS' if (dark_attr and monaco_dark) or (not dark_attr and not monaco_dark) else 'FAIL(可能部署的还是旧版 JS)'}")
|
||||||
|
else:
|
||||||
|
print("Monaco 实例: 未检测到,跳过主题断言(日志页面可能未加载或路由不同)")
|
||||||
|
ctx.close()
|
||||||
|
return 0 if attr_flipped and stored_flipped else 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -10,7 +10,7 @@ import run.halo.app.plugin.PluginContext;
|
|||||||
* <p>Only one main class extending {@link BasePlugin} is allowed per plugin.</p>
|
* <p>Only one main class extending {@link BasePlugin} is allowed per plugin.</p>
|
||||||
*
|
*
|
||||||
* @author LHY
|
* @author LHY
|
||||||
* @since 1.0.1
|
* @since 1.0.3
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class DarkModePlugin extends BasePlugin {
|
public class DarkModePlugin extends BasePlugin {
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ plugins {
|
|||||||
|
|
||||||
group 'run.halo.darkmode.ui'
|
group 'run.halo.darkmode.ui'
|
||||||
|
|
||||||
|
node {
|
||||||
|
// 使用系统安装的 Node.js,避免 Gradle 下载的本地 Node 目录缺失时失败
|
||||||
|
download = false
|
||||||
|
}
|
||||||
|
|
||||||
tasks.register('pnpmBuild', PnpmTask) {
|
tasks.register('pnpmBuild', PnpmTask) {
|
||||||
group = 'build'
|
group = 'build'
|
||||||
description = 'Build the UI project using pnpm'
|
description = 'Build the UI project using pnpm'
|
||||||
|
|||||||
+3
-1
@@ -23,10 +23,10 @@
|
|||||||
"@halo-dev/ui-shared": "^2.25.1",
|
"@halo-dev/ui-shared": "^2.25.1",
|
||||||
"axios": "^1.13.5",
|
"axios": "^1.13.5",
|
||||||
"canvas-confetti": "^1.9.3",
|
"canvas-confetti": "^1.9.3",
|
||||||
|
"darkreader": "file:./vendor/darkreader",
|
||||||
"vue": "^3.5.28"
|
"vue": "^3.5.28"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^8.0.16",
|
|
||||||
"@halo-dev/ui-plugin-bundler-kit": "^2.25.1",
|
"@halo-dev/ui-plugin-bundler-kit": "^2.25.1",
|
||||||
"@iconify-json/ri": "^1.2.10",
|
"@iconify-json/ri": "^1.2.10",
|
||||||
"@tsconfig/node20": "^20.1.6",
|
"@tsconfig/node20": "^20.1.6",
|
||||||
@@ -41,6 +41,7 @@
|
|||||||
"eslint": "^9.29.0",
|
"eslint": "^9.29.0",
|
||||||
"eslint-plugin-oxlint": "^0.16.12",
|
"eslint-plugin-oxlint": "^0.16.12",
|
||||||
"eslint-plugin-vue": "~10.0.1",
|
"eslint-plugin-vue": "~10.0.1",
|
||||||
|
"jiti": "^2.7.0",
|
||||||
"jsdom": "^26.1.0",
|
"jsdom": "^26.1.0",
|
||||||
"npm-run-all2": "^7.0.2",
|
"npm-run-all2": "^7.0.2",
|
||||||
"oxlint": "^0.16.12",
|
"oxlint": "^0.16.12",
|
||||||
@@ -48,6 +49,7 @@
|
|||||||
"sass": "^1.89.2",
|
"sass": "^1.89.2",
|
||||||
"typescript": "~5.8.3",
|
"typescript": "~5.8.3",
|
||||||
"unplugin-icons": "^23.0.1",
|
"unplugin-icons": "^23.0.1",
|
||||||
|
"vite": "^8.0.16",
|
||||||
"vitest": "^4.1.0",
|
"vitest": "^4.1.0",
|
||||||
"vue-tsc": "^3.3.3"
|
"vue-tsc": "^3.3.3"
|
||||||
},
|
},
|
||||||
|
|||||||
Generated
+98
-78
@@ -13,23 +13,26 @@ importers:
|
|||||||
version: 2.25.2(axios@1.19.0)
|
version: 2.25.2(axios@1.19.0)
|
||||||
'@halo-dev/components':
|
'@halo-dev/components':
|
||||||
specifier: ^2.25.1
|
specifier: ^2.25.1
|
||||||
version: 2.25.2(vue-router@5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(vue@3.5.41(typescript@5.8.3))
|
version: 2.25.2(vue-router@5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(vue@3.5.41(typescript@5.8.3))
|
||||||
'@halo-dev/ui-shared':
|
'@halo-dev/ui-shared':
|
||||||
specifier: ^2.25.1
|
specifier: ^2.25.1
|
||||||
version: 2.25.2(@tiptap/pm@3.29.2)(axios@1.19.0)(vue-router@5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(vue@3.5.41(typescript@5.8.3))
|
version: 2.25.2(@tiptap/pm@3.29.2)(axios@1.19.0)(vue-router@5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(vue@3.5.41(typescript@5.8.3))
|
||||||
axios:
|
axios:
|
||||||
specifier: ^1.13.5
|
specifier: ^1.13.5
|
||||||
version: 1.19.0
|
version: 1.19.0
|
||||||
canvas-confetti:
|
canvas-confetti:
|
||||||
specifier: ^1.9.3
|
specifier: ^1.9.3
|
||||||
version: 1.9.4
|
version: 1.9.4
|
||||||
|
darkreader:
|
||||||
|
specifier: file:./vendor/darkreader
|
||||||
|
version: file:vendor/darkreader
|
||||||
vue:
|
vue:
|
||||||
specifier: ^3.5.28
|
specifier: ^3.5.28
|
||||||
version: 3.5.41(typescript@5.8.3)
|
version: 3.5.41(typescript@5.8.3)
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@halo-dev/ui-plugin-bundler-kit':
|
'@halo-dev/ui-plugin-bundler-kit':
|
||||||
specifier: ^2.25.1
|
specifier: ^2.25.1
|
||||||
version: 2.25.2(@rsbuild/core@2.1.10)(@rsbuild/plugin-vue@2.0.1(@rsbuild/core@2.1.10)(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(vue@3.5.41(typescript@5.8.3)))(@vitejs/plugin-vue@6.0.8(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(axios@1.19.0)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))
|
version: 2.25.2(@rsbuild/core@2.1.10)(@rsbuild/plugin-vue@2.0.1(@rsbuild/core@2.1.10)(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(vue@3.5.41(typescript@5.8.3)))(@vitejs/plugin-vue@6.0.8(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(axios@1.19.0)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))
|
||||||
'@iconify-json/ri':
|
'@iconify-json/ri':
|
||||||
specifier: ^1.2.10
|
specifier: ^1.2.10
|
||||||
version: 1.2.10
|
version: 1.2.10
|
||||||
@@ -47,13 +50,13 @@ importers:
|
|||||||
version: 24.13.3
|
version: 24.13.3
|
||||||
'@vitest/eslint-plugin':
|
'@vitest/eslint-plugin':
|
||||||
specifier: ^1.2.7
|
specifier: ^1.2.7
|
||||||
version: 1.6.26(@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5)(typescript@5.8.3))(eslint@9.39.5)(typescript@5.8.3))(eslint@9.39.5)(typescript@5.8.3)(vitest@4.1.10(@types/node@24.13.3)(jsdom@26.1.0)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0)))
|
version: 1.6.26(@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)(vitest@4.1.10(@types/node@24.13.3)(jsdom@26.1.0)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0)))
|
||||||
'@vue/eslint-config-prettier':
|
'@vue/eslint-config-prettier':
|
||||||
specifier: ^10.2.0
|
specifier: ^10.2.0
|
||||||
version: 10.2.0(eslint@9.39.5)(prettier@3.9.6)
|
version: 10.2.0(eslint@9.39.5(jiti@2.7.0))(prettier@3.9.6)
|
||||||
'@vue/eslint-config-typescript':
|
'@vue/eslint-config-typescript':
|
||||||
specifier: ^14.5.1
|
specifier: ^14.5.1
|
||||||
version: 14.9.0(eslint-plugin-vue@10.0.1(eslint@9.39.5)(vue-eslint-parser@10.4.1(eslint@9.39.5)))(eslint@9.39.5)(typescript@5.8.3)
|
version: 14.9.0(eslint-plugin-vue@10.0.1(eslint@9.39.5(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@9.39.5(jiti@2.7.0))))(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)
|
||||||
'@vue/test-utils':
|
'@vue/test-utils':
|
||||||
specifier: ^2.4.6
|
specifier: ^2.4.6
|
||||||
version: 2.4.11(@vue/compiler-dom@3.5.41)(@vue/server-renderer@3.5.41)(vue@3.5.41(typescript@5.8.3))
|
version: 2.4.11(@vue/compiler-dom@3.5.41)(@vue/server-renderer@3.5.41)(vue@3.5.41(typescript@5.8.3))
|
||||||
@@ -62,13 +65,16 @@ importers:
|
|||||||
version: 0.7.0(typescript@5.8.3)(vue@3.5.41(typescript@5.8.3))
|
version: 0.7.0(typescript@5.8.3)(vue@3.5.41(typescript@5.8.3))
|
||||||
eslint:
|
eslint:
|
||||||
specifier: ^9.29.0
|
specifier: ^9.29.0
|
||||||
version: 9.39.5
|
version: 9.39.5(jiti@2.7.0)
|
||||||
eslint-plugin-oxlint:
|
eslint-plugin-oxlint:
|
||||||
specifier: ^0.16.12
|
specifier: ^0.16.12
|
||||||
version: 0.16.12
|
version: 0.16.12
|
||||||
eslint-plugin-vue:
|
eslint-plugin-vue:
|
||||||
specifier: ~10.0.1
|
specifier: ~10.0.1
|
||||||
version: 10.0.1(eslint@9.39.5)(vue-eslint-parser@10.4.1(eslint@9.39.5))
|
version: 10.0.1(eslint@9.39.5(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@9.39.5(jiti@2.7.0)))
|
||||||
|
jiti:
|
||||||
|
specifier: ^2.7.0
|
||||||
|
version: 2.7.0
|
||||||
jsdom:
|
jsdom:
|
||||||
specifier: ^26.1.0
|
specifier: ^26.1.0
|
||||||
version: 26.1.0
|
version: 26.1.0
|
||||||
@@ -92,10 +98,10 @@ importers:
|
|||||||
version: 23.0.1(@vue/compiler-sfc@3.5.41)
|
version: 23.0.1(@vue/compiler-sfc@3.5.41)
|
||||||
vite:
|
vite:
|
||||||
specifier: ^8.0.16
|
specifier: ^8.0.16
|
||||||
version: 8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0)
|
version: 8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0)
|
||||||
vitest:
|
vitest:
|
||||||
specifier: ^4.1.0
|
specifier: ^4.1.0
|
||||||
version: 4.1.10(@types/node@24.13.3)(jsdom@26.1.0)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))
|
version: 4.1.10(@types/node@24.13.3)(jsdom@26.1.0)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))
|
||||||
vue-tsc:
|
vue-tsc:
|
||||||
specifier: ^3.3.3
|
specifier: ^3.3.3
|
||||||
version: 3.3.9(typescript@5.8.3)
|
version: 3.3.9(typescript@5.8.3)
|
||||||
@@ -1079,6 +1085,9 @@ packages:
|
|||||||
csstype@3.2.3:
|
csstype@3.2.3:
|
||||||
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
||||||
|
|
||||||
|
darkreader@file:vendor/darkreader:
|
||||||
|
resolution: {directory: vendor/darkreader, type: directory}
|
||||||
|
|
||||||
data-urls@5.0.0:
|
data-urls@5.0.0:
|
||||||
resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
|
resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -1454,6 +1463,10 @@ packages:
|
|||||||
jackspeak@3.4.3:
|
jackspeak@3.4.3:
|
||||||
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
|
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
|
||||||
|
|
||||||
|
jiti@2.7.0:
|
||||||
|
resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
js-beautify@1.15.4:
|
js-beautify@1.15.4:
|
||||||
resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==}
|
resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
@@ -2427,9 +2440,9 @@ snapshots:
|
|||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@eslint-community/eslint-utils@4.10.1(eslint@9.39.5)':
|
'@eslint-community/eslint-utils@4.10.1(eslint@9.39.5(jiti@2.7.0))':
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 9.39.5
|
eslint: 9.39.5(jiti@2.7.0)
|
||||||
eslint-visitor-keys: 3.4.3
|
eslint-visitor-keys: 3.4.3
|
||||||
|
|
||||||
'@eslint-community/regexpp@4.12.2': {}
|
'@eslint-community/regexpp@4.12.2': {}
|
||||||
@@ -2488,33 +2501,33 @@ snapshots:
|
|||||||
axios: 1.19.0
|
axios: 1.19.0
|
||||||
qs: 6.15.3
|
qs: 6.15.3
|
||||||
|
|
||||||
'@halo-dev/components@2.25.2(vue-router@5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(vue@3.5.41(typescript@5.8.3))':
|
'@halo-dev/components@2.25.2(vue-router@5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(vue@3.5.41(typescript@5.8.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
floating-vue: 5.2.2(vue@3.5.41(typescript@5.8.3))
|
floating-vue: 5.2.2(vue@3.5.41(typescript@5.8.3))
|
||||||
vue: 3.5.41(typescript@5.8.3)
|
vue: 3.5.41(typescript@5.8.3)
|
||||||
vue-router: 5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3))
|
vue-router: 5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3))
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@nuxt/kit'
|
- '@nuxt/kit'
|
||||||
|
|
||||||
'@halo-dev/ui-plugin-bundler-kit@2.25.2(@rsbuild/core@2.1.10)(@rsbuild/plugin-vue@2.0.1(@rsbuild/core@2.1.10)(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(vue@3.5.41(typescript@5.8.3)))(@vitejs/plugin-vue@6.0.8(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(axios@1.19.0)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))':
|
'@halo-dev/ui-plugin-bundler-kit@2.25.2(@rsbuild/core@2.1.10)(@rsbuild/plugin-vue@2.0.1(@rsbuild/core@2.1.10)(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(vue@3.5.41(typescript@5.8.3)))(@vitejs/plugin-vue@6.0.8(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(axios@1.19.0)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@halo-dev/api-client': 2.25.2(axios@1.19.0)
|
'@halo-dev/api-client': 2.25.2(axios@1.19.0)
|
||||||
'@rsbuild/core': 2.1.10
|
'@rsbuild/core': 2.1.10
|
||||||
'@rsbuild/plugin-vue': 2.0.1(@rsbuild/core@2.1.10)(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(vue@3.5.41(typescript@5.8.3))
|
'@rsbuild/plugin-vue': 2.0.1(@rsbuild/core@2.1.10)(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(vue@3.5.41(typescript@5.8.3))
|
||||||
'@vitejs/plugin-vue': 6.0.8(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3))
|
'@vitejs/plugin-vue': 6.0.8(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3))
|
||||||
js-yaml: 4.3.1
|
js-yaml: 4.3.1
|
||||||
semver: 7.8.5
|
semver: 7.8.5
|
||||||
vite: 8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0)
|
vite: 8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- axios
|
- axios
|
||||||
|
|
||||||
'@halo-dev/ui-shared@2.25.2(@tiptap/pm@3.29.2)(axios@1.19.0)(vue-router@5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(vue@3.5.41(typescript@5.8.3))':
|
'@halo-dev/ui-shared@2.25.2(@tiptap/pm@3.29.2)(axios@1.19.0)(vue-router@5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(vue@3.5.41(typescript@5.8.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@halo-dev/api-client': 2.25.2(axios@1.19.0)
|
'@halo-dev/api-client': 2.25.2(axios@1.19.0)
|
||||||
'@tiptap/core': 3.29.2(@tiptap/pm@3.29.2)
|
'@tiptap/core': 3.29.2(@tiptap/pm@3.29.2)
|
||||||
mitt: 3.0.1
|
mitt: 3.0.1
|
||||||
vue: 3.5.41(typescript@5.8.3)
|
vue: 3.5.41(typescript@5.8.3)
|
||||||
vue-router: 5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3))
|
vue-router: 5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3))
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@tiptap/pm'
|
- '@tiptap/pm'
|
||||||
- axios
|
- axios
|
||||||
@@ -2870,15 +2883,15 @@ snapshots:
|
|||||||
|
|
||||||
'@types/tough-cookie@4.0.5': {}
|
'@types/tough-cookie@4.0.5': {}
|
||||||
|
|
||||||
'@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5)(typescript@5.8.3))(eslint@9.39.5)(typescript@5.8.3)':
|
'@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/regexpp': 4.12.2
|
'@eslint-community/regexpp': 4.12.2
|
||||||
'@typescript-eslint/parser': 8.66.0(eslint@9.39.5)(typescript@5.8.3)
|
'@typescript-eslint/parser': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)
|
||||||
'@typescript-eslint/scope-manager': 8.66.0
|
'@typescript-eslint/scope-manager': 8.66.0
|
||||||
'@typescript-eslint/type-utils': 8.66.0(eslint@9.39.5)(typescript@5.8.3)
|
'@typescript-eslint/type-utils': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)
|
||||||
'@typescript-eslint/utils': 8.66.0(eslint@9.39.5)(typescript@5.8.3)
|
'@typescript-eslint/utils': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)
|
||||||
'@typescript-eslint/visitor-keys': 8.66.0
|
'@typescript-eslint/visitor-keys': 8.66.0
|
||||||
eslint: 9.39.5
|
eslint: 9.39.5(jiti@2.7.0)
|
||||||
ignore: 7.0.6
|
ignore: 7.0.6
|
||||||
natural-compare: 1.4.0
|
natural-compare: 1.4.0
|
||||||
ts-api-utils: 2.5.0(typescript@5.8.3)
|
ts-api-utils: 2.5.0(typescript@5.8.3)
|
||||||
@@ -2886,14 +2899,14 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@typescript-eslint/parser@8.66.0(eslint@9.39.5)(typescript@5.8.3)':
|
'@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/scope-manager': 8.66.0
|
'@typescript-eslint/scope-manager': 8.66.0
|
||||||
'@typescript-eslint/types': 8.66.0
|
'@typescript-eslint/types': 8.66.0
|
||||||
'@typescript-eslint/typescript-estree': 8.66.0(typescript@5.8.3)
|
'@typescript-eslint/typescript-estree': 8.66.0(typescript@5.8.3)
|
||||||
'@typescript-eslint/visitor-keys': 8.66.0
|
'@typescript-eslint/visitor-keys': 8.66.0
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
eslint: 9.39.5
|
eslint: 9.39.5(jiti@2.7.0)
|
||||||
typescript: 5.8.3
|
typescript: 5.8.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -2916,13 +2929,13 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
typescript: 5.8.3
|
typescript: 5.8.3
|
||||||
|
|
||||||
'@typescript-eslint/type-utils@8.66.0(eslint@9.39.5)(typescript@5.8.3)':
|
'@typescript-eslint/type-utils@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/types': 8.66.0
|
'@typescript-eslint/types': 8.66.0
|
||||||
'@typescript-eslint/typescript-estree': 8.66.0(typescript@5.8.3)
|
'@typescript-eslint/typescript-estree': 8.66.0(typescript@5.8.3)
|
||||||
'@typescript-eslint/utils': 8.66.0(eslint@9.39.5)(typescript@5.8.3)
|
'@typescript-eslint/utils': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
eslint: 9.39.5
|
eslint: 9.39.5(jiti@2.7.0)
|
||||||
ts-api-utils: 2.5.0(typescript@5.8.3)
|
ts-api-utils: 2.5.0(typescript@5.8.3)
|
||||||
typescript: 5.8.3
|
typescript: 5.8.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -2945,13 +2958,13 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@typescript-eslint/utils@8.66.0(eslint@9.39.5)(typescript@5.8.3)':
|
'@typescript-eslint/utils@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5)
|
'@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(jiti@2.7.0))
|
||||||
'@typescript-eslint/scope-manager': 8.66.0
|
'@typescript-eslint/scope-manager': 8.66.0
|
||||||
'@typescript-eslint/types': 8.66.0
|
'@typescript-eslint/types': 8.66.0
|
||||||
'@typescript-eslint/typescript-estree': 8.66.0(typescript@5.8.3)
|
'@typescript-eslint/typescript-estree': 8.66.0(typescript@5.8.3)
|
||||||
eslint: 9.39.5
|
eslint: 9.39.5(jiti@2.7.0)
|
||||||
typescript: 5.8.3
|
typescript: 5.8.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -2961,21 +2974,21 @@ snapshots:
|
|||||||
'@typescript-eslint/types': 8.66.0
|
'@typescript-eslint/types': 8.66.0
|
||||||
eslint-visitor-keys: 5.0.1
|
eslint-visitor-keys: 5.0.1
|
||||||
|
|
||||||
'@vitejs/plugin-vue@6.0.8(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3))':
|
'@vitejs/plugin-vue@6.0.8(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@rolldown/pluginutils': 1.0.1
|
'@rolldown/pluginutils': 1.0.1
|
||||||
vite: 8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0)
|
vite: 8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0)
|
||||||
vue: 3.5.41(typescript@5.8.3)
|
vue: 3.5.41(typescript@5.8.3)
|
||||||
|
|
||||||
'@vitest/eslint-plugin@1.6.26(@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5)(typescript@5.8.3))(eslint@9.39.5)(typescript@5.8.3))(eslint@9.39.5)(typescript@5.8.3)(vitest@4.1.10(@types/node@24.13.3)(jsdom@26.1.0)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0)))':
|
'@vitest/eslint-plugin@1.6.26(@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)(vitest@4.1.10(@types/node@24.13.3)(jsdom@26.1.0)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0)))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/scope-manager': 8.66.0
|
'@typescript-eslint/scope-manager': 8.66.0
|
||||||
'@typescript-eslint/utils': 8.66.0(eslint@9.39.5)(typescript@5.8.3)
|
'@typescript-eslint/utils': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)
|
||||||
eslint: 9.39.5
|
eslint: 9.39.5(jiti@2.7.0)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@typescript-eslint/eslint-plugin': 8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5)(typescript@5.8.3))(eslint@9.39.5)(typescript@5.8.3)
|
'@typescript-eslint/eslint-plugin': 8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)
|
||||||
typescript: 5.8.3
|
typescript: 5.8.3
|
||||||
vitest: 4.1.10(@types/node@24.13.3)(jsdom@26.1.0)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))
|
vitest: 4.1.10(@types/node@24.13.3)(jsdom@26.1.0)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -2988,13 +3001,13 @@ snapshots:
|
|||||||
chai: 6.2.2
|
chai: 6.2.2
|
||||||
tinyrainbow: 3.1.1
|
tinyrainbow: 3.1.1
|
||||||
|
|
||||||
'@vitest/mocker@4.1.10(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))':
|
'@vitest/mocker@4.1.10(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vitest/spy': 4.1.10
|
'@vitest/spy': 4.1.10
|
||||||
estree-walker: 3.0.3
|
estree-walker: 3.0.3
|
||||||
magic-string: 0.30.21
|
magic-string: 0.30.21
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
vite: 8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0)
|
vite: 8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0)
|
||||||
|
|
||||||
'@vitest/pretty-format@4.1.10':
|
'@vitest/pretty-format@4.1.10':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3085,23 +3098,23 @@ snapshots:
|
|||||||
|
|
||||||
'@vue/devtools-shared@8.2.1': {}
|
'@vue/devtools-shared@8.2.1': {}
|
||||||
|
|
||||||
'@vue/eslint-config-prettier@10.2.0(eslint@9.39.5)(prettier@3.9.6)':
|
'@vue/eslint-config-prettier@10.2.0(eslint@9.39.5(jiti@2.7.0))(prettier@3.9.6)':
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 9.39.5
|
eslint: 9.39.5(jiti@2.7.0)
|
||||||
eslint-config-prettier: 10.1.8(eslint@9.39.5)
|
eslint-config-prettier: 10.1.8(eslint@9.39.5(jiti@2.7.0))
|
||||||
eslint-plugin-prettier: 5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5))(eslint@9.39.5)(prettier@3.9.6)
|
eslint-plugin-prettier: 5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0))(prettier@3.9.6)
|
||||||
prettier: 3.9.6
|
prettier: 3.9.6
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/eslint'
|
- '@types/eslint'
|
||||||
|
|
||||||
'@vue/eslint-config-typescript@14.9.0(eslint-plugin-vue@10.0.1(eslint@9.39.5)(vue-eslint-parser@10.4.1(eslint@9.39.5)))(eslint@9.39.5)(typescript@5.8.3)':
|
'@vue/eslint-config-typescript@14.9.0(eslint-plugin-vue@10.0.1(eslint@9.39.5(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@9.39.5(jiti@2.7.0))))(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/utils': 8.66.0(eslint@9.39.5)(typescript@5.8.3)
|
'@typescript-eslint/utils': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)
|
||||||
eslint: 9.39.5
|
eslint: 9.39.5(jiti@2.7.0)
|
||||||
eslint-plugin-vue: 10.0.1(eslint@9.39.5)(vue-eslint-parser@10.4.1(eslint@9.39.5))
|
eslint-plugin-vue: 10.0.1(eslint@9.39.5(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@9.39.5(jiti@2.7.0)))
|
||||||
fast-glob: 3.3.3
|
fast-glob: 3.3.3
|
||||||
typescript-eslint: 8.66.0(eslint@9.39.5)(typescript@5.8.3)
|
typescript-eslint: 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)
|
||||||
vue-eslint-parser: 10.4.1(eslint@9.39.5)
|
vue-eslint-parser: 10.4.1(eslint@9.39.5(jiti@2.7.0))
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.8.3
|
typescript: 5.8.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -3307,6 +3320,8 @@ snapshots:
|
|||||||
|
|
||||||
csstype@3.2.3: {}
|
csstype@3.2.3: {}
|
||||||
|
|
||||||
|
darkreader@file:vendor/darkreader: {}
|
||||||
|
|
||||||
data-urls@5.0.0:
|
data-urls@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
whatwg-mimetype: 4.0.0
|
whatwg-mimetype: 4.0.0
|
||||||
@@ -3366,32 +3381,32 @@ snapshots:
|
|||||||
|
|
||||||
escape-string-regexp@4.0.0: {}
|
escape-string-regexp@4.0.0: {}
|
||||||
|
|
||||||
eslint-config-prettier@10.1.8(eslint@9.39.5):
|
eslint-config-prettier@10.1.8(eslint@9.39.5(jiti@2.7.0)):
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 9.39.5
|
eslint: 9.39.5(jiti@2.7.0)
|
||||||
|
|
||||||
eslint-plugin-oxlint@0.16.12:
|
eslint-plugin-oxlint@0.16.12:
|
||||||
dependencies:
|
dependencies:
|
||||||
jsonc-parser: 3.3.1
|
jsonc-parser: 3.3.1
|
||||||
|
|
||||||
eslint-plugin-prettier@5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5))(eslint@9.39.5)(prettier@3.9.6):
|
eslint-plugin-prettier@5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0))(prettier@3.9.6):
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint: 9.39.5
|
eslint: 9.39.5(jiti@2.7.0)
|
||||||
prettier: 3.9.6
|
prettier: 3.9.6
|
||||||
prettier-linter-helpers: 1.0.1
|
prettier-linter-helpers: 1.0.1
|
||||||
synckit: 0.11.13
|
synckit: 0.11.13
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
eslint-config-prettier: 10.1.8(eslint@9.39.5)
|
eslint-config-prettier: 10.1.8(eslint@9.39.5(jiti@2.7.0))
|
||||||
|
|
||||||
eslint-plugin-vue@10.0.1(eslint@9.39.5)(vue-eslint-parser@10.4.1(eslint@9.39.5)):
|
eslint-plugin-vue@10.0.1(eslint@9.39.5(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@9.39.5(jiti@2.7.0))):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5)
|
'@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(jiti@2.7.0))
|
||||||
eslint: 9.39.5
|
eslint: 9.39.5(jiti@2.7.0)
|
||||||
natural-compare: 1.4.0
|
natural-compare: 1.4.0
|
||||||
nth-check: 2.1.1
|
nth-check: 2.1.1
|
||||||
postcss-selector-parser: 6.1.4
|
postcss-selector-parser: 6.1.4
|
||||||
semver: 7.8.5
|
semver: 7.8.5
|
||||||
vue-eslint-parser: 10.4.1(eslint@9.39.5)
|
vue-eslint-parser: 10.4.1(eslint@9.39.5(jiti@2.7.0))
|
||||||
xml-name-validator: 4.0.0
|
xml-name-validator: 4.0.0
|
||||||
|
|
||||||
eslint-scope@8.4.0:
|
eslint-scope@8.4.0:
|
||||||
@@ -3412,9 +3427,9 @@ snapshots:
|
|||||||
|
|
||||||
eslint-visitor-keys@5.0.1: {}
|
eslint-visitor-keys@5.0.1: {}
|
||||||
|
|
||||||
eslint@9.39.5:
|
eslint@9.39.5(jiti@2.7.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5)
|
'@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(jiti@2.7.0))
|
||||||
'@eslint-community/regexpp': 4.12.2
|
'@eslint-community/regexpp': 4.12.2
|
||||||
'@eslint/config-array': 0.21.2
|
'@eslint/config-array': 0.21.2
|
||||||
'@eslint/config-helpers': 0.4.2
|
'@eslint/config-helpers': 0.4.2
|
||||||
@@ -3448,6 +3463,8 @@ snapshots:
|
|||||||
minimatch: 3.1.5
|
minimatch: 3.1.5
|
||||||
natural-compare: 1.4.0
|
natural-compare: 1.4.0
|
||||||
optionator: 0.9.4
|
optionator: 0.9.4
|
||||||
|
optionalDependencies:
|
||||||
|
jiti: 2.7.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -3676,6 +3693,8 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@pkgjs/parseargs': 0.11.0
|
'@pkgjs/parseargs': 0.11.0
|
||||||
|
|
||||||
|
jiti@2.7.0: {}
|
||||||
|
|
||||||
js-beautify@1.15.4:
|
js-beautify@1.15.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
config-chain: 1.1.13
|
config-chain: 1.1.13
|
||||||
@@ -4259,13 +4278,13 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
prelude-ls: 1.2.1
|
prelude-ls: 1.2.1
|
||||||
|
|
||||||
typescript-eslint@8.66.0(eslint@9.39.5)(typescript@5.8.3):
|
typescript-eslint@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/eslint-plugin': 8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5)(typescript@5.8.3))(eslint@9.39.5)(typescript@5.8.3)
|
'@typescript-eslint/eslint-plugin': 8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)
|
||||||
'@typescript-eslint/parser': 8.66.0(eslint@9.39.5)(typescript@5.8.3)
|
'@typescript-eslint/parser': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)
|
||||||
'@typescript-eslint/typescript-estree': 8.66.0(typescript@5.8.3)
|
'@typescript-eslint/typescript-estree': 8.66.0(typescript@5.8.3)
|
||||||
'@typescript-eslint/utils': 8.66.0(eslint@9.39.5)(typescript@5.8.3)
|
'@typescript-eslint/utils': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)
|
||||||
eslint: 9.39.5
|
eslint: 9.39.5(jiti@2.7.0)
|
||||||
typescript: 5.8.3
|
typescript: 5.8.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
@@ -4298,7 +4317,7 @@ snapshots:
|
|||||||
picomatch: 4.0.5
|
picomatch: 4.0.5
|
||||||
webpack-virtual-modules: 0.6.2
|
webpack-virtual-modules: 0.6.2
|
||||||
|
|
||||||
unplugin@3.3.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0)):
|
unplugin@3.3.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/remapping': 2.3.5
|
'@jridgewell/remapping': 2.3.5
|
||||||
picomatch: 4.0.5
|
picomatch: 4.0.5
|
||||||
@@ -4306,7 +4325,7 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@rspack/core': 2.1.8(@swc/helpers@0.5.23)
|
'@rspack/core': 2.1.8(@swc/helpers@0.5.23)
|
||||||
rolldown: 1.2.3
|
rolldown: 1.2.3
|
||||||
vite: 8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0)
|
vite: 8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0)
|
||||||
|
|
||||||
uri-js@4.4.1:
|
uri-js@4.4.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -4314,7 +4333,7 @@ snapshots:
|
|||||||
|
|
||||||
util-deprecate@1.0.2: {}
|
util-deprecate@1.0.2: {}
|
||||||
|
|
||||||
vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0):
|
vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
lightningcss: 1.33.0
|
lightningcss: 1.33.0
|
||||||
picomatch: 4.0.5
|
picomatch: 4.0.5
|
||||||
@@ -4324,13 +4343,14 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/node': 24.13.3
|
'@types/node': 24.13.3
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
|
jiti: 2.7.0
|
||||||
sass: 1.102.0
|
sass: 1.102.0
|
||||||
yaml: 2.9.0
|
yaml: 2.9.0
|
||||||
|
|
||||||
vitest@4.1.10(@types/node@24.13.3)(jsdom@26.1.0)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0)):
|
vitest@4.1.10(@types/node@24.13.3)(jsdom@26.1.0)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vitest/expect': 4.1.10
|
'@vitest/expect': 4.1.10
|
||||||
'@vitest/mocker': 4.1.10(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))
|
'@vitest/mocker': 4.1.10(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))
|
||||||
'@vitest/pretty-format': 4.1.10
|
'@vitest/pretty-format': 4.1.10
|
||||||
'@vitest/runner': 4.1.10
|
'@vitest/runner': 4.1.10
|
||||||
'@vitest/snapshot': 4.1.10
|
'@vitest/snapshot': 4.1.10
|
||||||
@@ -4347,7 +4367,7 @@ snapshots:
|
|||||||
tinyexec: 1.3.0
|
tinyexec: 1.3.0
|
||||||
tinyglobby: 0.2.17
|
tinyglobby: 0.2.17
|
||||||
tinyrainbow: 3.1.1
|
tinyrainbow: 3.1.1
|
||||||
vite: 8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0)
|
vite: 8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0)
|
||||||
why-is-node-running: 2.3.0
|
why-is-node-running: 2.3.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/node': 24.13.3
|
'@types/node': 24.13.3
|
||||||
@@ -4359,10 +4379,10 @@ snapshots:
|
|||||||
|
|
||||||
vue-component-type-helpers@3.3.9: {}
|
vue-component-type-helpers@3.3.9: {}
|
||||||
|
|
||||||
vue-eslint-parser@10.4.1(eslint@9.39.5):
|
vue-eslint-parser@10.4.1(eslint@9.39.5(jiti@2.7.0)):
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
eslint: 9.39.5
|
eslint: 9.39.5(jiti@2.7.0)
|
||||||
eslint-scope: 9.1.2
|
eslint-scope: 9.1.2
|
||||||
eslint-visitor-keys: 5.0.1
|
eslint-visitor-keys: 5.0.1
|
||||||
espree: 11.2.0
|
espree: 11.2.0
|
||||||
@@ -4375,7 +4395,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
vue: 3.5.41(typescript@5.8.3)
|
vue: 3.5.41(typescript@5.8.3)
|
||||||
|
|
||||||
vue-router@5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)):
|
vue-router@5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/generator': 8.0.0
|
'@babel/generator': 8.0.0
|
||||||
'@vue-macros/common': 3.1.4(vue@3.5.41(typescript@5.8.3))
|
'@vue-macros/common': 3.1.4(vue@3.5.41(typescript@5.8.3))
|
||||||
@@ -4392,13 +4412,13 @@ snapshots:
|
|||||||
picomatch: 4.0.5
|
picomatch: 4.0.5
|
||||||
scule: 1.3.0
|
scule: 1.3.0
|
||||||
tinyglobby: 0.2.17
|
tinyglobby: 0.2.17
|
||||||
unplugin: 3.3.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))
|
unplugin: 3.3.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))
|
||||||
unplugin-utils: 0.3.2
|
unplugin-utils: 0.3.2
|
||||||
vue: 3.5.41(typescript@5.8.3)
|
vue: 3.5.41(typescript@5.8.3)
|
||||||
yaml: 2.9.0
|
yaml: 2.9.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@vue/compiler-sfc': 3.5.41
|
'@vue/compiler-sfc': 3.5.41
|
||||||
vite: 8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0)
|
vite: 8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@farmfe/core'
|
- '@farmfe/core'
|
||||||
- '@rspack/core'
|
- '@rspack/core'
|
||||||
|
|||||||
@@ -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
@@ -2,10 +2,14 @@ import { definePlugin } from '@halo-dev/ui-shared'
|
|||||||
import { IconPalette } from '@halo-dev/components'
|
import { IconPalette } from '@halo-dev/components'
|
||||||
import { markRaw } from 'vue'
|
import { markRaw } from 'vue'
|
||||||
import './styles/index.css'
|
import './styles/index.css'
|
||||||
|
import { initDarkReaderEngine } from './darkreader-engine'
|
||||||
import { injectThemeToggle } from './injector'
|
import { injectThemeToggle } from './injector'
|
||||||
|
import { initMonacoThemeSync } from './monaco-theme'
|
||||||
|
|
||||||
// 在插件加载后将切换器注入到侧边栏
|
// 在插件加载后将切换器注入到侧边栏,并让 Monaco 跟随主题
|
||||||
injectThemeToggle()
|
injectThemeToggle()
|
||||||
|
initMonacoThemeSync()
|
||||||
|
initDarkReaderEngine()
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
components: {},
|
components: {},
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* 让 Monaco Editor(日志查看器)跟随 Halo 暗色模式。
|
||||||
|
* 优先调用 Monaco 官方主题 API,CSS 兜底见 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 })
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
@import './variables.css';
|
@import './variables.css';
|
||||||
@import './overrides/halo-core.css';
|
@import './overrides/halo-core.css';
|
||||||
@import './overrides/plugin-pages.css';
|
@import './overrides/plugin-pages.css';
|
||||||
|
@import './overrides/uno-fallback.css';
|
||||||
@import './overrides/utilities.css';
|
@import './overrides/utilities.css';
|
||||||
@import './overrides/layout.css';
|
@import './overrides/layout.css';
|
||||||
@import './overrides/components.css';
|
@import './overrides/components.css';
|
||||||
|
|||||||
@@ -1,73 +1,21 @@
|
|||||||
/* ============================================================
|
/* ============================================================
|
||||||
Halo Dark Mode — @halo-dev/components 组件库覆盖
|
Halo Dark Mode — Halo 组件库覆盖(已清理)
|
||||||
覆盖 Halo 组件库中的 VCard, VModal, VDropdown, VTag 等
|
已删除 Halo 2.25 中不存在的 Vuetify 类(.v-card 等),
|
||||||
|
真实 DOM 类覆盖请优先维护 halo-core.css。
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
[data-halo-theme="dark"] {
|
[data-halo-theme="dark"] {
|
||||||
/* ===== 卡片 VCard ===== */
|
/* ===== 模态框(具体类 + !important,防被核心 scoped 样式覆盖) ===== */
|
||||||
.v-card,
|
.modal-content,
|
||||||
[class*="v-card"] {
|
.modal-header,
|
||||||
background-color: var(--halo-bg-card);
|
.modal-body,
|
||||||
border-color: var(--halo-border-base);
|
.modal-footer {
|
||||||
color: var(--halo-text-primary);
|
background-color: var(--halo-bg-card) !important;
|
||||||
}
|
color: var(--halo-text-primary) !important;
|
||||||
|
|
||||||
/* ===== 模态框 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 ===== */
|
/* ===== 表格 VTable ===== */
|
||||||
table,
|
table {
|
||||||
.v-table,
|
|
||||||
[class*="table"] {
|
|
||||||
background-color: var(--halo-bg-card);
|
background-color: var(--halo-bg-card);
|
||||||
color: var(--halo-text-primary);
|
color: var(--halo-text-primary);
|
||||||
}
|
}
|
||||||
@@ -94,49 +42,29 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ===== 分页 ===== */
|
/* ===== 分页 ===== */
|
||||||
.pagination,
|
.pagination {
|
||||||
.v-pagination {
|
|
||||||
color: var(--halo-text-secondary);
|
color: var(--halo-text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination .active,
|
.pagination .active {
|
||||||
.v-pagination .active {
|
|
||||||
background-color: var(--halo-accent-primary);
|
background-color: var(--halo-accent-primary);
|
||||||
color: var(--halo-accent-primary-text);
|
color: var(--halo-accent-primary-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== 面包屑 ===== */
|
/* ===== 面包屑 ===== */
|
||||||
.breadcrumb,
|
.breadcrumb {
|
||||||
.v-breadcrumb {
|
|
||||||
color: var(--halo-text-secondary);
|
color: var(--halo-text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumb a,
|
.breadcrumb a {
|
||||||
.v-breadcrumb a {
|
|
||||||
color: var(--halo-text-link);
|
color: var(--halo-text-link);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== Toast / 通知 ===== */
|
/* ===== Toast / 通知 ===== */
|
||||||
.v-toast,
|
|
||||||
.toast-notification,
|
.toast-notification,
|
||||||
[class*="toast"] {
|
[class*="toast"] {
|
||||||
background-color: var(--halo-bg-card);
|
background-color: var(--halo-bg-card);
|
||||||
color: var(--halo-text-primary);
|
color: var(--halo-text-primary);
|
||||||
border-color: var(--halo-border-base);
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -232,3 +232,32 @@
|
|||||||
background-color: oklch(30% 0.04 160 / 50%);
|
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;
|
||||||
|
}
|
||||||
@@ -87,3 +87,16 @@
|
|||||||
border-color: var(--halo-border-base);
|
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;
|
||||||
|
}
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
|
/* ===== 根元素背景(html 自身携带属性,需独立选择器) ===== */
|
||||||
|
html[data-halo-theme="dark"] {
|
||||||
|
background-color: var(--halo-bg-body) !important;
|
||||||
|
}
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
Halo Dark Mode — Halo 核心 BEM 类覆盖
|
Halo Dark Mode — Halo 核心 BEM 类覆盖
|
||||||
基于 Halo 2.25 真实 DOM 扫描(demo.halocms.site)得出的选择器
|
基于 Halo 2.25 真实 DOM 扫描(demo.halocms.site)得出的选择器
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
[data-halo-theme="dark"] {
|
[data-halo-theme="dark"] {
|
||||||
/* ===== 页面根背景(body 溢出时兜底) ===== */
|
/* ===== 页面根背景(body 溢出时兜底,html 见文件顶部独立规则) ===== */
|
||||||
html,
|
|
||||||
body {
|
body {
|
||||||
background-color: var(--halo-bg-body) !important;
|
background-color: var(--halo-bg-body) !important;
|
||||||
color: var(--halo-text-primary);
|
color: var(--halo-text-primary);
|
||||||
@@ -100,14 +103,6 @@
|
|||||||
.entity-field-description {
|
.entity-field-description {
|
||||||
color: var(--halo-text-secondary);
|
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 {
|
.pagination {
|
||||||
@@ -131,8 +126,7 @@
|
|||||||
/* ===== 标签/徽章(BEM) ===== */
|
/* ===== 标签/徽章(BEM) ===== */
|
||||||
.tag-wrapper,
|
.tag-wrapper,
|
||||||
.tag-default,
|
.tag-default,
|
||||||
.tag-pill,
|
.tag-pill {
|
||||||
[class*="tag-"] {
|
|
||||||
background-color: var(--halo-bg-hover);
|
background-color: var(--halo-bg-hover);
|
||||||
color: var(--halo-text-secondary);
|
color: var(--halo-text-secondary);
|
||||||
}
|
}
|
||||||
@@ -206,10 +200,10 @@
|
|||||||
border-color: var(--halo-border-light);
|
border-color: var(--halo-border-light);
|
||||||
}
|
}
|
||||||
.description-item__label {
|
.description-item__label {
|
||||||
color: var(--halo-text-secondary);
|
color: var(--halo-text-secondary) !important;
|
||||||
}
|
}
|
||||||
.description-item__content {
|
.description-item__content {
|
||||||
color: var(--halo-text-primary);
|
color: var(--halo-text-primary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== 已选指示器 ===== */
|
/* ===== 已选指示器 ===== */
|
||||||
@@ -265,8 +259,50 @@
|
|||||||
border-color: var(--halo-border-base);
|
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;
|
||||||
}
|
}
|
||||||
@@ -55,8 +55,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ===== 页面标题 ===== */
|
/* ===== 页面标题 ===== */
|
||||||
.page-title,
|
.page-title {
|
||||||
.v-card-title {
|
|
||||||
color: var(--halo-text-primary);
|
color: var(--halo-text-primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -247,3 +247,137 @@
|
|||||||
color: var(--halo-text-primary) !important;
|
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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -62,10 +62,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ===== 链接 ===== */
|
/* ===== 链接 ===== */
|
||||||
a:not([class*="text-"]),
|
html[data-halo-theme="dark"] a:not([class*="text-"]) {
|
||||||
.hover\:text-gray-900:hover,
|
|
||||||
.hover\:text-gray-800:hover,
|
|
||||||
.hover\:text-gray-700:hover {
|
|
||||||
color: var(--halo-text-link);
|
color: var(--halo-text-link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+23
@@ -0,0 +1,23 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Dark Reader Ltd.
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
Vendored
+9366
File diff suppressed because it is too large
Load Diff
Vendored
+9048
File diff suppressed because it is too large
Load Diff
Vendored
+173
@@ -0,0 +1,173 @@
|
|||||||
|
declare namespace DarkReader {
|
||||||
|
/**
|
||||||
|
* Enables dark mode for current web page.
|
||||||
|
* @param theme Theme options.
|
||||||
|
* @param fixes Fixes for the generated theme.
|
||||||
|
*/
|
||||||
|
function enable(theme: Partial<Theme>, fixes?: DynamicThemeFix): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables dark mode for current web page.
|
||||||
|
*/
|
||||||
|
function disable(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables dark mode when system color scheme is dark.
|
||||||
|
* @param theme Theme options.
|
||||||
|
* @param fixes Fixes for the generated theme.
|
||||||
|
*/
|
||||||
|
function auto(theme: Partial<Theme> | false, fixes?: DynamicThemeFix): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops watching for system color scheme.
|
||||||
|
* @param isEnabled Boolean `false` value.
|
||||||
|
*/
|
||||||
|
function auto(isEnabled: false): void;
|
||||||
|
/**
|
||||||
|
* Returns if darkreader is enabled.
|
||||||
|
*/
|
||||||
|
function isEnabled(): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a function for making CORS requests.
|
||||||
|
* @param fetch A fetch function.
|
||||||
|
*/
|
||||||
|
function setFetchMethod(fetch: (url: string) => Promise<Response>): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the generated CSS by Dark Reader as a string.
|
||||||
|
*/
|
||||||
|
function exportGeneratedCSS(): Promise<string>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Theme options.
|
||||||
|
*/
|
||||||
|
interface Theme {
|
||||||
|
/**
|
||||||
|
* 1 - dark mode, 0 - dimmed mode.
|
||||||
|
* Default 1.
|
||||||
|
*/
|
||||||
|
mode: 0 | 1;
|
||||||
|
/**
|
||||||
|
* Brightness (0 - 100+).
|
||||||
|
* Default 100.
|
||||||
|
*/
|
||||||
|
brightness: number;
|
||||||
|
/**
|
||||||
|
* Contrast (0 - 100+).
|
||||||
|
* Default 100.
|
||||||
|
*/
|
||||||
|
contrast: number;
|
||||||
|
/**
|
||||||
|
* Grayscale (0 - 100).
|
||||||
|
* Default 0.
|
||||||
|
*/
|
||||||
|
grayscale: number;
|
||||||
|
/**
|
||||||
|
* Sepia (0 - 100).
|
||||||
|
* Default 0.
|
||||||
|
*/
|
||||||
|
sepia: number;
|
||||||
|
/**
|
||||||
|
* Specifies if custom font should be used.
|
||||||
|
* Default false.
|
||||||
|
*/
|
||||||
|
useFont: boolean;
|
||||||
|
/**
|
||||||
|
* Font family to use.
|
||||||
|
*/
|
||||||
|
fontFamily: string;
|
||||||
|
/**
|
||||||
|
* Makes text look bolder (0 - 1px).
|
||||||
|
* Default 0.
|
||||||
|
*/
|
||||||
|
textStroke: number;
|
||||||
|
/**
|
||||||
|
* Background color to use for dark mode.
|
||||||
|
* Default #181a1b
|
||||||
|
*/
|
||||||
|
darkSchemeBackgroundColor: string;
|
||||||
|
/**
|
||||||
|
* Text color to use for dark mode.
|
||||||
|
* Default #e8e6e3
|
||||||
|
*/
|
||||||
|
darkSchemeTextColor: string;
|
||||||
|
/**
|
||||||
|
* Background color to use for light mode.
|
||||||
|
* Default #dcdad7
|
||||||
|
*/
|
||||||
|
lightSchemeBackgroundColor: string;
|
||||||
|
/**
|
||||||
|
* Text color to use for light mode.
|
||||||
|
* Default #181a1b
|
||||||
|
*/
|
||||||
|
lightSchemeTextColor: string;
|
||||||
|
/**
|
||||||
|
* Scrollbar color
|
||||||
|
* Default auto
|
||||||
|
*/
|
||||||
|
scrollbarColor: string;
|
||||||
|
/**
|
||||||
|
* Selection color
|
||||||
|
* Default auto
|
||||||
|
*/
|
||||||
|
selectionColor: string;
|
||||||
|
/**
|
||||||
|
* Specifies if it has to style system controls
|
||||||
|
* Default true
|
||||||
|
*/
|
||||||
|
styleSystemControls: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains fixes for the generated theme.
|
||||||
|
*/
|
||||||
|
interface DynamicThemeFix {
|
||||||
|
/**
|
||||||
|
* List of CSS selectors that should be inverted.
|
||||||
|
* Usually icons that are contained in sprites.
|
||||||
|
*/
|
||||||
|
invert: string[];
|
||||||
|
/**
|
||||||
|
* Additional CSS.
|
||||||
|
* ${color} template should be used to apply theme options to a color.
|
||||||
|
* Example:
|
||||||
|
* ```
|
||||||
|
* body {
|
||||||
|
* background-color: ${white} !important;
|
||||||
|
* background-image: none !important;
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
css: string;
|
||||||
|
/**
|
||||||
|
* List of CSS selectors where it's inline style should not be analyzed
|
||||||
|
* Mostly used for color pickers
|
||||||
|
*/
|
||||||
|
ignoreInlineStyle: string[];
|
||||||
|
/**
|
||||||
|
* List of CSS selectors where it's image should not be analyzed
|
||||||
|
* Mostly used for wrongly inverted background-images
|
||||||
|
*/
|
||||||
|
ignoreImageAnalysis: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A toggle to disable the proxying of `document.styleSheets`.
|
||||||
|
* This is a API-Exclusive option, as it can break legitimate websites,
|
||||||
|
* who are using the Dark Reader API.
|
||||||
|
*/
|
||||||
|
disableStyleSheetsProxy: boolean;
|
||||||
|
/**
|
||||||
|
* List of stylesheet URL patterns to ignore.
|
||||||
|
* Stylesheets matching these patterns will not be processed by Dark Reader.
|
||||||
|
* - Simple string: matches if URL contains the string (e.g., "crisp")
|
||||||
|
* - Prefix with ^: matches if URL starts with pattern (e.g., "^https://client.crisp")
|
||||||
|
* - Suffix with $: matches if URL ends with pattern (e.g., ".css$")
|
||||||
|
*/
|
||||||
|
ignoreCSSUrl: string[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'darkreader' {
|
||||||
|
export = DarkReader;
|
||||||
|
}
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "darkreader",
|
||||||
|
"version": "4.9.129",
|
||||||
|
"description": "Dark mode for every website",
|
||||||
|
"main": "darkreader.js",
|
||||||
|
"module": "darkreader.mjs",
|
||||||
|
"types": "index.d.ts",
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user