发布 1.0.2 工程质量与样式优化

This commit is contained in:
Darkings 2026-06-22 17:16:12 +08:00
parent f963a19690
commit 4bc66db15a
41 changed files with 2158 additions and 400 deletions

View file

@ -19,14 +19,5 @@ jobs:
- run: npm ci
- name: Stylelint
run: npm run lint:css
- name: Build
run: npm run build
- name: CSS Audit
run: npm run audit:css
- name: Contrast Check
run: npm run check:contrast
- name: Verify
run: npm run verify

View file

@ -9,6 +9,8 @@ on:
jobs:
release:
runs-on: ubuntu-latest
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
permissions:
contents: write
steps:

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
node_modules/
docs/
ERRORS.md
tmp/

81
AGENTS.md Normal file
View file

@ -0,0 +1,81 @@
# AGENTS.md
本文件为 Codex 在此仓库中工作时提供指导。
## 项目规范
- 本项目所有文档、注释、提交信息均使用中文书写(专业术语如 SCSS、CSS、Vite、Obsidian 等除外)。
- 长期项目文档、研究文档、路线图、QA 文档与问题汇总优先维护在 Obsidian 项目目录:
```text
C:\Users\Jie\iCloudDrive\iCloud~md~obsidian\SecondBrain\project\Monokai Syntax
```
- 当前仓库仅保留开发、构建、协作、发布与源码旁注所必需的文档。
- 将仓库文档迁移到上述 Obsidian 项目目录前,需先确认该文档不会被构建脚本、发布流程、协作规则或源码注释直接依赖。
- 已迁移到 Obsidian 项目目录的文档,不应在仓库中继续维护重复副本;如仓库流程需要引用,应在仓库文档中写明 Obsidian 目录中的目标位置。
- `docs/` 目录仅作为临时整理区使用。稳定的说明文档、QA 清单、路线图和研究报告应迁移到 Obsidian 项目目录。
## 构建命令
```
npm run dev # 监听模式SCSS 变更时重新构建 theme.css
npm run build # 生产构建 → dist/theme.css
npm run lint:css # Stylelint 检查 src/scss/**/*.scss
npm run check:contrast # WCAG 对比度审计
npm run audit:css # CSS 质量审计
npm run verify:icons # 验证图标字体覆盖
npm run verify:graph # 验证图谱 CSS 变量
npm run generate:icons # 从图标字体重新生成 file-icons.generated.scss
npm run release:pack # 完整发布前检查verify + lint + build + audit
npm run build:vault # 构建并同步到默认 Obsidian vault可追加自定义路径
npm run build:vault -- D:/你的/Vault # 构建并同步到指定 Vault
```
`generate:icons` 脚本通过 `prebuild`/`prelint:css` 钩子在 `build``lint:css` 之前自动运行。
## 架构
这是一个 **Obsidian 主题**,将 VS Code Monokai Pro 调色板映射到 Obsidian 约 400 个 CSS 自定义属性上。构建工具链使用 **Vite**,配有一个自定义 `obsidianThemeBundle` 插件,将 `license.css` + 编译后的 SCSS + Style Settings YAML 配置拼接为单个 `dist/theme.css`
### SCSS 模块层级(全部位于 `src/scss/`
| 文件 | 职责 |
|---|---|
| `_variables.scss` | 设计 token40+ 颜色原语(深色 + 浅色)、字体栈、间距、圆角 |
| `_mixins.scss` | `focus-ring`、`compact-density`、`reduced-motion` 等 mixin |
| `_base.scss` | 将 SCSS 变量映射到 Obsidian CSS 自定义属性(`:root`、`.theme-dark`、`.theme-light`),同时包含全局 UI 规则(导航、元数据、搜索、标签) |
| `components/_editor.scss` | Markdown 渲染与 CodeMirror 6 语法高亮(`.cm-s-obsidian` token 颜色) |
| `components/_ribbon.scss` | 左侧边栏 Ribbon类似 VS Code 活动栏) |
| `components/_tabs.scss` | 工作区标签页头部,激活态强调下划线 |
| `components/_modals.scss` | 命令面板、提示框、建议弹窗 |
| `components/_file-icons.scss` | 自定义图标字体(`monokai-pro-icons`),用于文件树,支持单色切换 |
| `plugins/_graph.scss` | 关系图谱:节点/连线颜色、面板控件、聚焦环动画 |
| `plugins/_canvas.scss` | Canvas 卡片颜色映射到 Monokai 六色光谱 |
| `plugins/_style-settings.scss` | 动态开关5 种滤镜主题Machine/Octagon/Ristretto/Spectrum、紧凑模式、单色图标、通过 `body` 类实现用户可覆盖的强调色/链接色/代码色 |
入口点为 `src/main.js`,仅导入 `src/scss/index.scss`。该文件按顺序转发 `variables → mixins → base → components → plugins`
### Style Settings 配置(`src/css/style-settings/`
这些 `*.css.md` 文件包含由 `obsidian-style-settings` 社区插件解析的 YAML 元数据块。Vite 插件按字母顺序排列并拼接到 `theme.css` 末尾。文件:`00-overview`、`10-palette`、`20-density`、`30-icons`、`40-typography`、`50-accents`。
### 关键 Obsidian CSS 变量
主题通过设置 Obsidian 文档化的 CSS 自定义属性来工作。最重要的分组:
- `--background-primary/secondary` — 主编辑区 vs 侧边栏背景
- `--text-normal/muted/faint` — 文本层级
- `--text-accent`、`--interactive-accent` — 行动号召洋红色
- `--h1-color``--h6-color` — 标题颜色级联(洋红 → 橙 → 黄 → 绿 → 青 → 紫)
- `--link-color`、`--link-external-color` — 青色链接
- `--blockquote-border-color` — 绿色边框
- `--code-*` — 代码语法 token 颜色
- `--nav-item-*`、`--ribbon-background` — 导航 chrome
- 自定义 `--monokai-*` 变量 — 滤镜和用户覆盖的内部桥接变量
## 约束
- **禁止外部资源的 `@import`** — Obsidian 审核流程拒绝在运行时获取远程字体/资源的主题。所有资源必须内联(图标字体以 Base64 编码在 `_file-icons.generated.scss` 中)。
- **禁止 `!important`** — Obsidian 的 CSS 级联依赖特异性;`!important` 会破坏用户 CSS 片段。
- **禁止 ID 选择器** — 由 Stylelint 强制执行;只允许使用 class。
- 主题目标为 Obsidian 1.0.0+manifest.json 中的 `minAppVersion`),该版本引入了 CSS 变量系统。

View file

@ -1,5 +1,16 @@
# 更新日志 / Changelog
## 1.0.22026-06-22
- 系统优化阅读模式与 Live Preview 的视觉一致性,专项验证扩展到 30 项。
- 修正文件树同层级文件夹与 Markdown 文件对齐问题。
- 调整根层文件夹折叠图标尺寸与线宽,降低视觉重量。
- 优化 H1-H6 标题下边距,按层级从大到小递减。
- 更新深色模式代码 token 色彩,使其更贴近 Monokai Pro。
- 补齐段落、表格源码、Callout、块引用、代码块、附件嵌入、标签、链接、脚注与扩展任务状态的阅读/编辑一致性规则。
- 增加版本一致性、生成文件一致性、样式 polish、Vault QA 等发布前验证。
- 更新发布版 README补充安装、验证与 GitHub Release 流程。
## 1.0.12026-05-15
- 完善文档树文档图标README、TODO、CHANGELOG、API、PROMPTS、CURSOR、CLAUDE、AGENTS 与 `Bug_*.md`

160
README.md
View file

@ -1,78 +1,152 @@
# Monokai Syntax
[中文](https://github.com/Darkings/Obsidian-MonokaiSyntax/blob/master/README.zh.md) | [Development](https://github.com/Darkings/Obsidian-MonokaiSyntax/blob/master/DEVELOPMENT.md) | [GitHub](https://github.com/Darkings/Obsidian-MonokaiSyntax)
Monokai Syntax is an Obsidian community theme inspired by the Monokai Pro visual language, tuned for writing, code notes, navigation, graph view, and long-running knowledge bases.
Monokai Syntax 是一个面向 Obsidian 的 Monokai Pro 风格主题重点优化写作、代码笔记、文件树导航、阅读模式、Live Preview、关系图谱和 Canvas 的长期使用体验。
![Monokai Syntax](./MonokaiSyntax.png)
## Highlights
## 版本
- Monokai-inspired dark and light palettes for Obsidian.
- Markdown reading view and Live Preview styling.
- CodeMirror 6 syntax colors mapped to the Monokai spectrum.
- File explorer, ribbon, tabs, modals, graph view, and canvas polish.
- Custom file icons with optional monochrome mode.
- Style Settings support for palette filters, density, icons, typography, and accents.
- No remote runtime assets in the final theme CSS.
当前版本:`1.0.2`
## 1.0.1 Update
最低 Obsidian 版本:`1.0.0`
- Expanded document icons for README, TODO, CHANGELOG, API, PROMPTS, CURSOR, CLAUDE, AGENTS, and `Bug_*.md` notes.
- Reworked callout icons with glyphs from `icons/icons.woff`, with larger icons and Monokai-style semantic backgrounds and left borders.
- Updated the file explorer so second-level and deeper folder collapse icons use `folder-dimmed2`, while root-level folders keep Obsidian's native collapse icon.
- Added a Style Settings toggle to hide file tree icons.
- Improved compact mode density for the file explorer.
## 主要特性
## Installation
- Monokai Pro 风格深色与浅色调色板。
- CodeMirror 6 语法高亮映射到 Monokai 六色光谱。
- 阅读模式与 Live Preview 使用共享语义变量收敛视觉差异。
- 文件树图标覆盖常见文档、配置、代码与项目文件。
- 文件树同层级文件夹与 Markdown 文件对齐,根层折叠图标使用轻量尺寸。
- Callout、块引用、任务列表、表格、代码块、链接、标签、脚注、图片与附件嵌入统一样式层级。
- 关系图谱、Canvas、Ribbon、标签页、弹窗和搜索界面适配主题色。
- 支持 Style Settings调色板滤镜、紧凑模式、图标模式、字体、强调色、链接色与代码色。
- 构建产物不加载远程运行时资源,图标字体以内联 woff 形式打包。
### Community Theme
## 1.0.2 更新重点
Monokai Syntax is available from Obsidian's community theme store.
- 系统收敛阅读模式与 Live Preview 的视觉差异,专项验证覆盖 30 项样式一致性检查。
- 优化 H1-H6 标题下边距,按标题层级递减。
- 修正文件树同层级文件夹与 Markdown 文件的对齐问题。
- 调整根层文件夹折叠图标尺寸与线宽,避免视觉过重。
- 更新代码 token 颜色,使深色模式更贴近 Monokai Pro。
- 增强代码块、表格源码、块引用、Callout、标签、链接、脚注、附件嵌入和扩展任务状态的阅读/编辑一致性。
- 增加发布前验证:版本一致性、生成文件一致性、样式 polish、Vault QA 和 CSS 审计。
Open Obsidian, go to `Settings` -> `Appearance` -> `Themes` -> `Manage`, search for `Monokai Syntax`, then install and enable it.
## 安装
### Manual Installation
### 手动安装
Create this folder in your Obsidian vault:
在你的 Obsidian vault 中创建目录:
```text
Your Vault/.obsidian/themes/Monokai Syntax/
你的 Vault/.obsidian/themes/Monokai Syntax/
```
Copy these files into it:
复制以下文件到该目录:
```text
theme.css
manifest.json
```
Then enable `Monokai Syntax` from Obsidian's Appearance settings.
然后在 Obsidian 中进入 `设置` -> `外观` -> `主题`,选择并启用 `Monokai Syntax`
### 从 GitHub Release 安装
发布 GitHub Release 后,下载对应版本中的:
```text
theme.css
manifest.json
```
放入你的 vault 主题目录即可。
### 提交 Obsidian 社区主题
社区主题上架需要向 `obsidianmd/obsidian-releases` 提交主题信息 PR并等待审核通过。审核通过后用户才能在 Obsidian 的社区主题商店中搜索安装。
## Style Settings
For the full configuration experience, install the community plugin `Style Settings`.
建议安装 Obsidian 社区插件 `Style Settings`,以获得完整配置体验。
Available controls include:
当前支持:
- Palette filter: Classic, Machine, Octagon, Ristretto, Spectrum.
- Compact density.
- Colorful or monochrome file icons.
- Font and typography options.
- Accent, link, and code color overrides.
- 调色板滤镜Classic、Machine、Octagon、Ristretto、Spectrum。
- 紧凑模式。
- 彩色或单色文件树图标。
- 文件树图标开关。
- 字体与排版选项。
- 强调色、链接色与代码色覆盖。
## Roadmap
## 本地开发
- Expand file icon coverage.
- Refine Markdown reading view and Live Preview details.
- Improve Style Settings coverage.
- Polish graph view, canvas, and plugin compatibility.
- Track Obsidian UI changes and keep the published community theme up to date.
安装依赖:
## Development
```powershell
npm install
```
Build, lint, audit, and vault sync instructions live in [DEVELOPMENT.md](https://github.com/Darkings/Obsidian-MonokaiSyntax/blob/master/DEVELOPMENT.md).
构建主题:
## License
```powershell
npm run build
```
MIT. See [LICENSE.md](https://github.com/Darkings/Obsidian-MonokaiSyntax/blob/master/LICENSE.md).
完整验证:
```powershell
npm run verify
```
构建并同步到默认 vault
```powershell
npm run build:vault
```
发布前检查:
```powershell
npm run release:pack
```
## 发布流程
1. 确认 `package.json`、`manifest.json`、`versions.json` 版本一致。
2. 运行 `npm run release:pack`
3. 确认根目录存在发布产物:
```text
theme.css
manifest.json
```
4. 创建并推送版本标签,例如:
```powershell
git tag v1.0.2
git push origin v1.0.2
```
5. GitHub Actions 会在标签推送后运行发布检查并创建 Release附件包含 `theme.css``manifest.json`
## 验证覆盖
当前发布前验证包含:
- 版本一致性检查。
- 图标字体生成与覆盖验证。
- 阅读模式与 Live Preview 样式一致性验证。
- Active 视觉覆盖验证。
- 关系图谱变量验证。
- Node 测试。
- Stylelint。
- WCAG 对比度检查。
- 构建产物一致性检查。
- CSS 审计:禁止远程资源、`!important` 与 ID 选择器。
- Vault 侧 QA 验证。
## 许可证
MIT。详见 [LICENSE.md](./LICENSE.md)。

View file

@ -1,78 +1,19 @@
# Monokai Syntax
# Monokai Syntax 中文说明
[English](https://github.com/Darkings/Obsidian-MonokaiSyntax/blob/master/README.md) | [开发说明](https://github.com/Darkings/Obsidian-MonokaiSyntax/blob/master/DEVELOPMENT.zh.md) | [GitHub](https://github.com/Darkings/Obsidian-MonokaiSyntax)
中文说明已合并到主文档:[README.md](./README.md)。
Monokai Syntax 是一个受 Monokai Pro 视觉语言启发的 Obsidian 社区主题,面向写作、代码笔记、知识库导航、关系图谱与长期知识管理场景优化。
常用发布前命令:
![Monokai Syntax](./MonokaiSyntax.png)
## 亮点
- 面向 Obsidian 的 Monokai 风格深色与浅色调色板。
- Markdown 阅读视图与 Live Preview 样式优化。
- CodeMirror 6 语法高亮映射到 Monokai 六色光谱。
- 文件树、Ribbon、标签页、弹窗、关系图谱与 Canvas 细节打磨。
- 自定义文件图标,支持彩色与单色模式。
- 支持 Style Settings 调整调色板滤镜、密度、图标、字体与强调色。
- 最终主题 CSS 不加载远程运行时资源。
## 1.0.1 更新
- 扩展文档图标覆盖README、TODO、CHANGELOG、API、PROMPTS、CURSOR、CLAUDE、AGENTS 与 `Bug_*.md`
- Callout 图标改用 `icons/icons.woff` 内置 glyph并放大图标尺寸补齐 Monokai 风格语义背景色与左边框。
- 文件树第二层及以后文件夹折叠图标改用 `folder-dimmed2`,根目录第一层保留 Obsidian 原生折叠图标。
- Style Settings 新增关闭文档树图标开关。
- 优化文件树紧凑模式的字号、行高与图标间距。
## 安装
### 社区主题
Monokai Syntax 已发布到 Obsidian 社区主题商店。
打开 Obsidian进入 `设置` -> `外观` -> `主题` -> `管理`,搜索 `Monokai Syntax`,然后安装并启用。
### 手动安装
在你的 Obsidian vault 中创建目录:
```text
你的 Vault/.obsidian/themes/Monokai Syntax/
```powershell
npm run verify
npm run build:vault
npm run qa:verify
npm run release:pack
```
复制以下文件到该目录
发布产物:
```text
theme.css
manifest.json
```
然后在 Obsidian 的外观设置中启用 `Monokai Syntax`
## Style Settings
建议安装 Obsidian 社区插件 `Style Settings`,以获得完整配置体验。
当前支持:
- 调色板滤镜Classic、Machine、Octagon、Ristretto、Spectrum。
- 紧凑密度。
- 彩色或单色文件图标。
- 字体与排版选项。
- 强调色、链接色与代码色覆盖。
## 未来规划
- 扩展文件图标覆盖。
- 继续打磨 Markdown 阅读视图与 Live Preview 细节。
- 增强 Style Settings 覆盖范围。
- 优化关系图谱、Canvas 与插件兼容性。
- 跟进 Obsidian UI 变化,持续维护已发布的社区主题版本。
## 开发
构建、检查、审计与 Vault 同步说明见 [DEVELOPMENT.zh.md](https://github.com/Darkings/Obsidian-MonokaiSyntax/blob/master/DEVELOPMENT.zh.md)。
## 许可证
MIT。详见 [LICENSE.md](https://github.com/Darkings/Obsidian-MonokaiSyntax/blob/master/LICENSE.md)。

448
dist/theme.css vendored
View file

@ -60,16 +60,16 @@
--list-marker-color: #ae81ff;
--code-normal: #f8f8f2;
--code-background: #1e1f1c;
--code-comment: #8f908a;
--code-function: #a6e22e;
--code-important: #f92672;
--code-keyword: #f92672;
--code-operator: #ff6188;
--code-property: #66d9ef;
--code-comment: #72736f;
--code-function: #a9dc76;
--code-important: #ff6188;
--code-keyword: #ff6188;
--code-operator: #fc9867;
--code-property: #78dce8;
--code-punctuation: #c1c1bd;
--code-string: #e6db74;
--code-tag: #f92672;
--code-value: #ae81ff;
--code-string: #ffd866;
--code-tag: #ff6188;
--code-value: #ab9df2;
--ribbon-background: #1e1f1c;
--nav-item-background-hover: #3e3d32;
--nav-item-background-active: #414339;
@ -253,10 +253,10 @@ input[type=checkbox]:checked {
}
.tag {
color: var(--link-color);
background-color: var(--background-secondary);
border: 1px solid var(--background-modifier-border);
border-radius: 999px;
color: var(--monokai-tag-color, var(--link-color));
background-color: var(--monokai-tag-background, var(--background-secondary));
border: 1px solid var(--monokai-tag-border-color, var(--background-modifier-border));
border-radius: var(--monokai-tag-radius, var(--radius-s));
}
.tag:hover {
@ -270,12 +270,48 @@ input[type=checkbox]:checked {
--bold-weight: 900;
--italic-color: var(--text-normal);
--heading-spacing: 1rem;
--monokai-heading-gap-h1: 1.25rem;
--monokai-heading-gap-h2: 1.1rem;
--monokai-heading-gap-h3: 0.95rem;
--monokai-heading-gap-h4: 0.8rem;
--monokai-heading-gap-h5: 0.7rem;
--monokai-heading-gap-h6: 0.6rem;
--monokai-heading-weight: 600;
--monokai-inline-code-color: #a6e22e;
--monokai-inline-code-background: var(--code-background);
--monokai-inline-code-padding: 0.1em 0.3em;
--monokai-formatting-marker-color: var(--text-faint);
--monokai-strikethrough-color: var(--text-muted);
--monokai-highlight-radius: var(--radius-s);
--monokai-highlight-padding-inline: 0.12em;
--monokai-hr-color: var(--background-modifier-border);
--monokai-hr-thickness: 1px;
--monokai-hr-spacing: 1.25rem;
--monokai-footnote-color: var(--link-color);
--monokai-footnote-marker-color: var(--text-faint);
--monokai-footnote-font-size: 0.85em;
--monokai-media-radius: var(--radius-m);
--monokai-media-border-color: var(--background-modifier-border);
--monokai-media-background: var(--background-secondary);
--monokai-embed-padding: 0.75rem;
--monokai-tag-color: var(--link-color);
--monokai-tag-background: var(--background-secondary);
--monokai-tag-border-color: var(--background-modifier-border);
--monokai-tag-radius: var(--radius-s);
--monokai-link-decoration-color: var(--link-color);
--monokai-link-decoration-thickness: 1px;
--monokai-paragraph-line-height: var(--line-height-normal);
--monokai-paragraph-spacing: 0.75rem;
--monokai-table-source-background: var(--background-secondary);
--monokai-table-source-font-family: var(--font-monospace-theme);
--monokai-table-source-separator-color: var(--text-faint);
--monokai-codeblock-border-color: var(--background-modifier-border);
--monokai-codeblock-radius: var(--radius-m);
--monokai-codeblock-padding: 1rem;
--monokai-codeblock-padding-inline: 1rem;
--callout-blend-mode: normal;
--callout-icon-size: 1.25em;
--p-spacing: 0.75rem;
--p-spacing: var(--monokai-paragraph-spacing);
-webkit-font-smoothing: antialiased;
text-rendering: optimizelegibility;
}
@ -313,7 +349,13 @@ body.theme-dark {
--monokai-inline-code-color: #a6e22e;
--monokai-inline-code-background: var(--code-background);
--monokai-table-border: rgb(248 248 242 / 18%);
--monokai-table-header-background: rgb(248 248 242 / 6%);
--monokai-task-done-color: rgb(248 248 242 / 68%);
--monokai-task-forward-color: var(--text-accent);
--monokai-task-cancelled-color: var(--text-faint);
--monokai-task-question-color: var(--link-color);
--monokai-modal-shadow: 0 18px 50px rgb(0 0 0 / 35%);
--monokai-selection-ring: rgb(249 38 114 / 28%);
}
body.theme-light {
@ -349,7 +391,13 @@ body.theme-light {
--monokai-inline-code-color: #269d69;
--monokai-inline-code-background: var(--code-background);
--monokai-table-border: rgb(61 61 61 / 18%);
--monokai-table-header-background: rgb(61 61 61 / 6%);
--monokai-task-done-color: rgb(61 61 61 / 66%);
--monokai-task-forward-color: var(--text-accent);
--monokai-task-cancelled-color: var(--text-faint);
--monokai-task-question-color: var(--link-color);
--monokai-modal-shadow: 0 16px 36px rgb(61 61 61 / 16%);
--monokai-selection-ring: rgb(200 63 103 / 22%);
}
.markdown-source-view.mod-cm6 .inline-title {
@ -358,10 +406,38 @@ body.theme-light {
.markdown-rendered h1,
.markdown-rendered h2,
.markdown-rendered h3 {
.markdown-rendered h3,
.markdown-rendered h4,
.markdown-rendered h5,
.markdown-rendered h6 {
font-weight: var(--monokai-heading-weight);
letter-spacing: 0;
margin-block: calc(var(--heading-spacing) * 1.5) var(--spacing-4);
margin-block-start: calc(var(--heading-spacing) * 1.5);
}
.markdown-rendered h1 {
margin-block-end: var(--monokai-heading-gap-h1);
}
.markdown-rendered h2 {
margin-block-end: var(--monokai-heading-gap-h2);
}
.markdown-rendered h3 {
margin-block-end: var(--monokai-heading-gap-h3);
}
.markdown-rendered h4 {
color: var(--h4-color);
margin-block-end: var(--monokai-heading-gap-h4);
}
.markdown-rendered h5 {
color: var(--h5-color);
margin-block-end: var(--monokai-heading-gap-h5);
}
.markdown-rendered h6 {
color: var(--h6-color);
margin-block-end: var(--monokai-heading-gap-h6);
}
.markdown-rendered p {
line-height: var(--monokai-paragraph-line-height);
margin-block: 0 var(--monokai-paragraph-spacing);
}
.markdown-rendered strong {
color: var(--bold-color);
@ -370,11 +446,16 @@ body.theme-light {
.markdown-rendered em {
color: var(--italic-color);
}
.markdown-rendered del,
.markdown-rendered s {
color: var(--monokai-strikethrough-color);
text-decoration-color: var(--monokai-strikethrough-color);
}
.markdown-rendered mark {
color: var(--monokai-highlight-text);
background-color: var(--text-highlight-bg);
border-radius: var(--radius-s);
padding-inline: 0.12em;
border-radius: var(--monokai-highlight-radius);
padding-inline: var(--monokai-highlight-padding-inline);
}
.markdown-rendered ul > li::marker,
.markdown-rendered ol > li::marker {
@ -386,6 +467,16 @@ body.theme-light {
.markdown-rendered .task-list-item.is-checked > p {
color: var(--monokai-task-done-color);
}
.markdown-rendered .task-list-item[data-task=">"] {
color: var(--monokai-task-forward-color);
}
.markdown-rendered .task-list-item[data-task="-"] {
color: var(--monokai-task-cancelled-color);
text-decoration-line: line-through;
}
.markdown-rendered .task-list-item[data-task="?"] {
color: var(--monokai-task-question-color);
}
.markdown-rendered table {
border: 1px solid var(--monokai-table-border);
}
@ -393,20 +484,25 @@ body.theme-light {
.markdown-rendered td {
border: 1px solid var(--monokai-table-border);
}
.markdown-rendered th {
color: var(--text-normal);
background-color: var(--monokai-table-header-background);
font-weight: 700;
}
.markdown-rendered code {
color: var(--monokai-inline-code-color);
background-color: var(--monokai-inline-code-background);
border: 0;
border-radius: var(--radius-s);
box-shadow: none;
padding: 0.1em 0.3em;
padding: var(--monokai-inline-code-padding);
}
.markdown-rendered pre {
background-color: var(--code-background);
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-m);
border: 1px solid var(--monokai-codeblock-border-color);
border-radius: var(--monokai-codeblock-radius);
font-family: var(--font-monospace-theme);
padding: 1rem;
padding: var(--monokai-codeblock-padding);
}
.markdown-rendered pre code {
background-color: transparent;
@ -414,7 +510,91 @@ body.theme-light {
box-shadow: none;
padding: 0;
}
.markdown-rendered hr {
border: 0;
border-top: var(--monokai-hr-thickness) solid var(--monokai-hr-color);
margin-block: var(--monokai-hr-spacing);
}
.markdown-rendered .footnote-ref,
.markdown-rendered .footnote-link {
color: var(--monokai-footnote-color);
font-size: var(--monokai-footnote-font-size);
}
.markdown-rendered .footnote-backref {
color: var(--monokai-footnote-marker-color);
}
.markdown-rendered img,
.markdown-rendered .image-embed {
max-width: 100%;
background-color: var(--monokai-media-background);
border: 1px solid var(--monokai-media-border-color);
border-radius: var(--monokai-media-radius);
}
.markdown-rendered .file-embed,
.markdown-rendered .pdf-embed,
.markdown-rendered .audio-embed,
.markdown-rendered .video-embed,
.markdown-rendered .internal-embed {
background-color: var(--monokai-media-background);
border: 1px solid var(--monokai-media-border-color);
border-radius: var(--monokai-media-radius);
padding: var(--monokai-embed-padding);
}
.markdown-source-view.mod-cm6 {
/* stylelint-disable selector-class-pattern -- Obsidian/CodeMirror 内部类名。 */
}
.markdown-source-view.mod-cm6 .HyperMD-header-1 {
padding-block-end: var(--monokai-heading-gap-h1);
}
.markdown-source-view.mod-cm6 .HyperMD-header-2 {
padding-block-end: var(--monokai-heading-gap-h2);
}
.markdown-source-view.mod-cm6 .HyperMD-header-3 {
padding-block-end: var(--monokai-heading-gap-h3);
}
.markdown-source-view.mod-cm6 .HyperMD-header-4 {
padding-block-end: var(--monokai-heading-gap-h4);
}
.markdown-source-view.mod-cm6 .HyperMD-header-5 {
padding-block-end: var(--monokai-heading-gap-h5);
}
.markdown-source-view.mod-cm6 .HyperMD-header-6 {
padding-block-end: var(--monokai-heading-gap-h6);
}
.markdown-source-view.mod-cm6 .HyperMD-task-line[data-task=x],
.markdown-source-view.mod-cm6 .HyperMD-task-line[data-task=X] {
color: var(--monokai-task-done-color);
}
.markdown-source-view.mod-cm6 .HyperMD-task-line[data-task=">"] {
color: var(--monokai-task-forward-color);
}
.markdown-source-view.mod-cm6 .HyperMD-task-line[data-task="-"] {
color: var(--monokai-task-cancelled-color);
text-decoration-line: line-through;
}
.markdown-source-view.mod-cm6 .HyperMD-task-line[data-task="?"] {
color: var(--monokai-task-question-color);
}
.markdown-source-view.mod-cm6 .HyperMD-codeblock {
background-color: var(--code-background);
font-family: var(--font-monospace-theme);
padding-inline: var(--monokai-codeblock-padding-inline);
}
.markdown-source-view.mod-cm6 .HyperMD-hr {
color: var(--monokai-formatting-marker-color);
padding-block: calc(var(--monokai-hr-spacing) / 2);
}
.markdown-source-view.mod-cm6 .HyperMD-paragraph {
padding-block-end: calc(var(--monokai-paragraph-spacing) / 3);
}
.markdown-source-view.mod-cm6 .HyperMD-table-row {
font-family: var(--monokai-table-source-font-family);
background-color: var(--monokai-table-source-background);
}
.markdown-source-view.mod-cm6 {
/* stylelint-enable selector-class-pattern */
}
.markdown-source-view.mod-cm6 .cm-header-1 {
color: var(--h1-color);
font-weight: var(--monokai-heading-weight);
@ -427,6 +607,18 @@ body.theme-light {
color: var(--h3-color);
font-weight: var(--monokai-heading-weight);
}
.markdown-source-view.mod-cm6 .cm-header-4 {
color: var(--h4-color);
font-weight: var(--monokai-heading-weight);
}
.markdown-source-view.mod-cm6 .cm-header-5 {
color: var(--h5-color);
font-weight: var(--monokai-heading-weight);
}
.markdown-source-view.mod-cm6 .cm-header-6 {
color: var(--h6-color);
font-weight: var(--monokai-heading-weight);
}
.markdown-source-view.mod-cm6 .cm-strong {
color: var(--bold-color);
font-weight: var(--bold-weight);
@ -434,16 +626,83 @@ body.theme-light {
.markdown-source-view.mod-cm6 .cm-em {
color: var(--italic-color);
}
.markdown-source-view.mod-cm6 .cm-strikethrough {
color: var(--monokai-strikethrough-color);
text-decoration-color: var(--monokai-strikethrough-color);
}
.markdown-source-view.mod-cm6 .cm-link,
.markdown-source-view.mod-cm6 .cm-url {
color: var(--link-color);
}
.markdown-source-view.mod-cm6 .cm-formatting-link {
color: var(--text-faint);
color: var(--monokai-formatting-marker-color);
}
.markdown-source-view.mod-cm6 .cm-hmd-table-sep,
.markdown-source-view.mod-cm6 .cm-hmd-table-align,
.markdown-source-view.mod-cm6 .cm-formatting-table {
color: var(--monokai-table-source-separator-color);
}
.markdown-source-view.mod-cm6 .cm-formatting-code-block,
.markdown-source-view.mod-cm6 .cm-hmd-codeblock-begin,
.markdown-source-view.mod-cm6 .cm-hmd-codeblock-end {
color: var(--monokai-formatting-marker-color);
}
.markdown-source-view.mod-cm6 .cm-table {
color: var(--text-normal);
}
.markdown-source-view.mod-cm6 .cm-formatting-list {
color: var(--list-marker-color);
}
.markdown-source-view.mod-cm6 .cm-formatting-strong,
.markdown-source-view.mod-cm6 .cm-formatting-em,
.markdown-source-view.mod-cm6 .cm-formatting-strikethrough,
.markdown-source-view.mod-cm6 .cm-formatting-code,
.markdown-source-view.mod-cm6 .cm-formatting-highlight {
color: var(--monokai-formatting-marker-color);
}
.markdown-source-view.mod-cm6 .cm-highlight {
color: var(--monokai-highlight-text);
background-color: var(--text-highlight-bg);
border-radius: var(--monokai-highlight-radius);
padding-inline: var(--monokai-highlight-padding-inline);
}
.markdown-source-view.mod-cm6 .cm-hr {
color: var(--monokai-hr-color);
}
.markdown-source-view.mod-cm6 .cm-footref {
color: var(--monokai-footnote-color);
font-size: var(--monokai-footnote-font-size);
}
.markdown-source-view.mod-cm6 .cm-formatting-footref {
color: var(--monokai-footnote-marker-color);
}
.markdown-source-view.mod-cm6 .image-embed,
.markdown-source-view.mod-cm6 .cm-embed-block {
max-width: 100%;
background-color: var(--monokai-media-background);
border: 1px solid var(--monokai-media-border-color);
border-radius: var(--monokai-media-radius);
}
.markdown-source-view.mod-cm6 .file-embed,
.markdown-source-view.mod-cm6 .pdf-embed,
.markdown-source-view.mod-cm6 .audio-embed,
.markdown-source-view.mod-cm6 .video-embed,
.markdown-source-view.mod-cm6 .internal-embed,
.markdown-source-view.mod-cm6 .cm-embed-block {
background-color: var(--monokai-media-background);
border: 1px solid var(--monokai-media-border-color);
border-radius: var(--monokai-media-radius);
padding: var(--monokai-embed-padding);
}
.markdown-source-view.mod-cm6 .cm-hashtag {
color: var(--monokai-tag-color);
background-color: var(--monokai-tag-background);
border: 1px solid var(--monokai-tag-border-color);
border-radius: var(--monokai-tag-radius);
padding-inline: 0.25em;
}
.markdown-source-view.mod-cm6 .cm-line {
line-height: var(--monokai-paragraph-line-height);
}
.markdown-source-view.mod-cm6 .cm-inline-code {
color: var(--monokai-inline-code-color);
@ -451,7 +710,7 @@ body.theme-light {
border: 0;
border-radius: var(--radius-s);
box-shadow: none;
padding: 0.05em 0.25em;
padding: var(--monokai-inline-code-padding);
}
.cm-s-obsidian span.cm-comment {
@ -480,8 +739,12 @@ body.theme-light {
a,
.markdown-rendered a,
.cm-hmd-internal-link {
.cm-hmd-internal-link,
.cm-link,
.cm-url {
color: var(--link-color);
text-decoration-color: var(--monokai-link-decoration-color);
text-decoration-thickness: var(--monokai-link-decoration-thickness);
}
a:hover,
@ -490,6 +753,14 @@ a:hover,
color: var(--link-color-hover);
}
a:focus-visible,
.markdown-rendered a:focus-visible,
.cm-hmd-internal-link:focus-visible {
outline: 2px solid var(--interactive-accent);
outline-offset: 2px;
border-radius: var(--radius-s);
}
/* stylelint-disable */
/*
* 此文件由 `npm run generate:icons` 自动生成
@ -503,19 +774,24 @@ a:hover,
font-weight: 400;
}
.nav-file-title::before {
display: inline-block;
flex: 0 0 1rem;
width: 1rem;
margin-inline-end: 0.35rem;
display: inline-flex;
align-items: center;
justify-content: center;
width: var(--monokai-file-tree-slot-size);
min-width: var(--monokai-file-tree-slot-size);
height: var(--monokai-file-tree-glyph-size);
margin-inline-end: calc(-1 * var(--monokai-file-tree-slot-size));
transform: translateX(calc(-1 * (var(--monokai-file-tree-slot-size) + var(--monokai-file-tree-icon-gap))));
--monokai-file-icon-color: #ffd866;
color: var(--monokai-file-icon-color, var(--icon-color));
content: "\e658";
font-family: "monokai-pro-icons";
font-size: 1rem;
font-size: var(--monokai-file-tree-glyph-size);
font-style: normal;
font-variant: normal;
font-weight: 400;
line-height: 1;
pointer-events: none;
speak: never;
text-align: center;
text-rendering: auto;
@ -884,14 +1160,40 @@ a:hover,
content: "\e8e8";
}
.nav-folder-title[data-path*="/"] .nav-folder-collapse-indicator,
.nav-folder-title[data-path*="/"] .collapse-icon,
.nav-folder[data-path*="/"] > .nav-folder-title .nav-folder-collapse-indicator,
.nav-folder[data-path*="/"] > .nav-folder-title .collapse-icon,
.nav-folder-children .nav-folder-children .nav-folder-title .nav-folder-collapse-indicator,
.nav-folder-children .nav-folder-children .nav-folder-title .collapse-icon {
width: 1rem;
.nav-file-title,
.nav-folder-title {
--monokai-file-tree-slot-size: 1rem;
--monokai-file-tree-icon-gap: 0.35rem;
--monokai-file-tree-glyph-size: 0.8125rem;
--monokai-file-tree-toggle-size: 9px;
--monokai-file-tree-toggle-stroke-width: 1.75;
overflow: visible;
position: relative;
}
.nav-folder-title .nav-folder-collapse-indicator,
.nav-folder-title .collapse-icon {
flex: 0 0 var(--monokai-file-tree-slot-size);
width: var(--monokai-file-tree-slot-size);
min-width: var(--monokai-file-tree-slot-size);
margin-inline-end: var(--monokai-file-tree-icon-gap);
}
.nav-folder-title .nav-folder-collapse-indicator svg,
.nav-folder-title .collapse-icon svg {
width: var(--monokai-file-tree-toggle-size);
height: var(--monokai-file-tree-toggle-size);
font-size: var(--monokai-file-tree-toggle-size);
stroke-width: var(--monokai-file-tree-toggle-stroke-width);
}
.nav-folder-title .nav-folder-collapse-indicator,
.nav-folder-title .collapse-icon {
display: inline-flex;
align-items: center;
justify-content: center;
}
.nav-folder-title[data-path*="/"] .nav-folder-collapse-indicator::before,
.nav-folder-title[data-path*="/"] .collapse-icon::before,
.nav-folder[data-path*="/"] > .nav-folder-title .nav-folder-collapse-indicator::before,
@ -902,7 +1204,7 @@ a:hover,
display: inline-block;
color: var(--icon-color);
font-family: monokai-pro-icons, monospace;
font-size: 1rem;
font-size: var(--monokai-file-tree-glyph-size);
font-style: normal;
font-weight: 400;
line-height: 1;
@ -985,6 +1287,7 @@ body.is-mobile .workspace-ribbon {
color: var(--text-muted);
background-color: transparent;
border-radius: var(--radius-s) var(--radius-s) 0 0;
transition: color 140ms ease, background-color 140ms ease, box-shadow 140ms ease;
}
.workspace-tab-header:hover {
@ -1011,7 +1314,7 @@ body.is-mobile .workspace-ribbon {
background-color: var(--background-secondary);
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-m);
box-shadow: 0 18px 50px rgba(0, 0, 0, 0.35);
box-shadow: var(--monokai-modal-shadow);
}
.modal-title,
@ -1027,6 +1330,8 @@ body.is-mobile .workspace-ribbon {
background-color: var(--background-primary);
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-s);
padding: 0.45rem 0.6rem;
transition: border-color 140ms ease, box-shadow 140ms ease;
}
.prompt-input:focus,
@ -1237,7 +1542,7 @@ body.is-mobile .workspace-ribbon {
.canvas-node.is-focused,
.canvas-node.is-selected {
border-color: var(--interactive-accent);
box-shadow: 0 0 0 2px rgba(249, 38, 114, 0.28);
box-shadow: 0 0 0 2px var(--monokai-selection-ring);
}
.canvas-node-label {
@ -1306,12 +1611,18 @@ body.monokai-syntax-compact {
--nav-item-size: 22px;
--ribbon-item-size: 32px;
--input-height: 28px;
--callout-padding: 0.9rem;
--callout-content-padding: var(--spacing-3) 0 0 0;
}
body.monokai-syntax-compact .nav-files-container {
font-size: var(--monokai-nav-font-size);
}
body.monokai-syntax-compact .nav-file-title,
body.monokai-syntax-compact .nav-folder-title {
--monokai-file-tree-slot-size: 13px;
--monokai-file-tree-icon-gap: 4px;
--monokai-file-tree-glyph-size: 12px;
--monokai-file-tree-toggle-size: 8px;
min-height: var(--nav-item-size);
line-height: var(--nav-item-size);
padding: 0 6px;
@ -1321,24 +1632,6 @@ body.monokai-syntax-compact .nav-folder-title-content {
font-size: var(--monokai-nav-font-size);
line-height: var(--nav-item-size);
}
body.monokai-syntax-compact .nav-file-title::before {
width: 13px;
flex: 0 0 13px;
margin-inline-end: 4px;
font-size: 13px;
}
body.monokai-syntax-compact .nav-folder-collapse-indicator {
width: 14px;
margin-inline-end: 1px;
}
body.monokai-syntax-compact .nav-folder-title[data-path*="/"] .nav-folder-collapse-indicator::before,
body.monokai-syntax-compact .nav-folder-title[data-path*="/"] .collapse-icon::before,
body.monokai-syntax-compact .nav-folder[data-path*="/"] > .nav-folder-title .nav-folder-collapse-indicator::before,
body.monokai-syntax-compact .nav-folder[data-path*="/"] > .nav-folder-title .collapse-icon::before,
body.monokai-syntax-compact .nav-folder-children .nav-folder-children .nav-folder-title .nav-folder-collapse-indicator::before,
body.monokai-syntax-compact .nav-folder-children .nav-folder-children .nav-folder-title .collapse-icon::before {
font-size: 13px;
}
body.monokai-syntax-compact .side-dock-ribbon-action {
height: var(--ribbon-item-size);
padding: 4px;
@ -1346,6 +1639,10 @@ body.monokai-syntax-compact .side-dock-ribbon-action {
body.monokai-syntax-compact .markdown-rendered pre {
padding: 0.75rem;
}
body.monokai-syntax-compact .markdown-rendered th,
body.monokai-syntax-compact .markdown-rendered td {
padding: 0.35rem 0.5rem;
}
body.monokai-syntax-monochrome-icons {
--icon-color: var(--text-muted);
@ -1360,6 +1657,7 @@ body {
--link-color: var(--monokai-link-color, var(--monokai-default-link));
--link-external-color: var(--monokai-link-color, var(--monokai-default-link));
--blockquote-border-color: var(--monokai-success-color, var(--monokai-default-success));
--monokai-blockquote-border: var(--monokai-success-color, var(--monokai-default-success));
--checkbox-color-checked: var(--monokai-success-color, var(--monokai-default-success));
--text-highlight-bg: var(--monokai-warning-color, var(--monokai-default-warning));
--code-background: var(--monokai-code-background, var(--monokai-default-code-background));
@ -1373,6 +1671,10 @@ body:is(.theme-dark, .theme-light) {
--callout-content-padding: var(--spacing-4) 0 0 0;
--callout-border-width: 0;
--callout-radius: var(--radius-s);
--monokai-blockquote-content-gap: 1.25rem;
--monokai-blockquote-nested-gap: 0.75rem;
--monokai-callout-title-gap: var(--spacing-2);
--monokai-callout-source-marker-color: var(--text-faint);
}
/* ===== 块引用(阅读 + 编辑统一) ===== */
@ -1382,7 +1684,11 @@ body .markdown-rendered blockquote {
border-inline-start: 2px solid var(--monokai-blockquote-border);
border-radius: 0 var(--radius-s) var(--radius-s) 0;
color: var(--text-normal);
padding: 0.75rem 0.75rem 0.75rem 1.25rem;
padding: 0.75rem 0.75rem 0.75rem var(--monokai-blockquote-content-gap);
}
body .markdown-rendered blockquote blockquote {
margin-block: var(--monokai-blockquote-nested-gap);
}
body .markdown-source-view.mod-cm6 .cm-line.HyperMD-quote,
@ -1392,14 +1698,25 @@ body .markdown-source-view.mod-cm6 .HyperMD-quote {
border-radius: 0 var(--radius-s) var(--radius-s) 0;
}
body .markdown-source-view.mod-cm6 .cm-line.HyperMD-quote + .cm-line.HyperMD-quote,
body .markdown-source-view.mod-cm6 .HyperMD-quote + .HyperMD-quote {
border-start-start-radius: 0;
border-end-start-radius: 0;
}
body .markdown-source-view.mod-cm6 .cm-line.HyperMD-quote .cm-quote,
body .markdown-source-view.mod-cm6 .HyperMD-quote .cm-quote {
color: var(--text-normal);
border-inline-start: 0;
margin-left: 0.6rem;
margin-left: var(--monokai-blockquote-content-gap);
padding-inline-start: 0;
}
body .markdown-source-view.mod-cm6 .cm-quote-2,
body .markdown-source-view.mod-cm6 .cm-quote-3 {
color: var(--text-normal);
}
body .markdown-source-view.mod-cm6 .cm-line.HyperMD-quote .cm-formatting-quote,
body .markdown-source-view.mod-cm6 .HyperMD-quote .cm-formatting-quote {
display: none;
@ -1430,7 +1747,7 @@ body .markdown-source-view.mod-cm6 .callout-title,
body .markdown-source-view.mod-cm6 .cm-callout-title {
color: var(--callout-border, var(--monokai-callout-note-border));
font-weight: 700;
gap: var(--spacing-2);
gap: var(--monokai-callout-title-gap);
padding-inline-start: 0;
}
@ -1445,6 +1762,15 @@ body .markdown-source-view.mod-cm6 .cm-callout-content {
padding-inline-start: 0;
}
body .markdown-source-view.mod-cm6 .cm-callout-content {
color: var(--text-normal);
}
body .markdown-source-view.mod-cm6 .cm-formatting-callout,
body .markdown-source-view.mod-cm6 .cm-callout .cm-formatting {
color: var(--monokai-callout-source-marker-color);
}
/* ===== Callout 语义色分组映射 ===== */
body .markdown-preview-view.markdown-rendered .callout[data-callout=note],
body .markdown-rendered .callout[data-callout=note],

View file

@ -1,6 +1,6 @@
{
"name": "Monokai Syntax",
"version": "1.0.1",
"version": "1.0.2",
"minAppVersion": "1.0.0",
"author": "lat3ncy"
}

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "monokai-syntax",
"version": "1.0.1",
"version": "1.0.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "monokai-syntax",
"version": "1.0.1",
"version": "1.0.2",
"license": "MIT",
"devDependencies": {
"sass": "^1.99.0",

View file

@ -1,6 +1,6 @@
{
{
"name": "monokai-syntax",
"version": "1.0.1",
"version": "1.0.2",
"description": "面向 Obsidian 的 Monokai Syntax 主题。",
"author": "lat3ncy",
"license": "MIT",
@ -12,19 +12,27 @@
"dev": "vite build --watch",
"generate:icons": "node scripts/generate-icon-theme.js",
"build": "vite build",
"test": "node --test",
"lint:css": "stylelint \"src/scss/**/*.scss\"",
"check:contrast": "node scripts/check-contrast.js",
"audit:css": "node scripts/audit-css.js",
"verify": "npm run verify:versions && npm run verify:pip-editor && npm run verify:icons && npm run verify:graph && npm run verify:active-visual && npm run verify:style-polish && npm test && npm run lint:css && npm run check:contrast && npm run build && npm run verify:generated && npm run audit:css",
"verify:versions": "node scripts/verify-versions.js",
"verify:generated": "node scripts/verify-generated.js",
"verify:pip-editor": "node scripts/verify-pip-editor.js",
"verify:icons": "node scripts/verify-icons.js",
"verify:graph": "node scripts/verify-graph.js",
"verify:active-visual": "node scripts/verify-active-visual.js",
"verify:style-polish": "node scripts/verify-style-polish.js",
"qa:create": "node scripts/create-qa-content.js",
"qa:verify": "node scripts/verify-qa.js",
"build:dev": "vite build --mode development",
"sync:vault": "node scripts/sync-vault-theme.js",
"build:vault": "npm run build && node scripts/sync-vault-theme.js",
"release:pack": "npm run verify:pip-editor && npm run verify:icons && npm run verify:graph && npm run verify:active-visual && npm run lint:css && npm run check:contrast && npm run build && npm run audit:css"
"release:pack": "npm run verify"
},
"engines": {
"node": ">=22"
},
"devDependencies": {
"sass": "^1.99.0",

View file

@ -1,31 +1,41 @@
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { pathToFileURL } from "node:url";
const themeCssPath = resolve(import.meta.dirname, "../theme.css");
const themeCss = readFileSync(themeCssPath, "utf8");
export function auditCssContent(themeCss) {
const urlMatches = [...themeCss.matchAll(/url\((["']?)(.*?)\1\)/gi)].map((match) => match[2]);
const forbiddenRuntimeUrls = urlMatches.filter(
(url) => !url.startsWith("data:font/woff;base64,"),
);
const checks = [
["外部 @import", /@import/i.test(themeCss)],
["远程 URL", /https?:\/\//i.test(themeCss)],
["非本地内联字体 url() 资源", forbiddenRuntimeUrls.length > 0],
["!important 声明", /!important/i.test(themeCss)],
["ID 选择器", /(^|[{};])\s*#[A-Za-z][\w-]*/m.test(themeCss)],
];
const failures = checks.filter(([, matched]) => matched).map(([label]) => label);
const urlMatches = [...themeCss.matchAll(/url\((["']?)(.*?)\1\)/gi)].map((match) => match[2]);
const forbiddenRuntimeUrls = urlMatches.filter(
(url) => !url.startsWith("data:font/woff;base64,"),
);
return {
passed: failures.length === 0,
failures,
checks,
};
}
const checks = [
["外部 @import", /@import/i.test(themeCss)],
["远程 URL", /https?:\/\//i.test(themeCss)],
["非本地内联字体 url() 资源", forbiddenRuntimeUrls.length > 0],
["!important 声明", /!important/i.test(themeCss)],
];
function runCli() {
const themeCssPath = resolve(import.meta.dirname, "../theme.css");
const result = auditCssContent(readFileSync(themeCssPath, "utf8"));
let hasFailure = false;
for (const [label, matched] of result.checks) {
console.log(`${label}: ${matched ? "发现" : "未发现"}`);
}
for (const [label, matched] of checks) {
console.log(`${label}: ${matched ? "发现" : "未发现"}`);
if (matched) {
hasFailure = true;
if (!result.passed) {
process.exitCode = 1;
}
}
if (hasFailure) {
process.exitCode = 1;
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
runCli();
}

View file

@ -1,4 +1,6 @@
const pairs = [
import { pathToFileURL } from "node:url";
export const pairs = [
["深色正文", "#f8f8f2", "#272822", 4.5],
["深色链接", "#66d9ef", "#272822", 4.5],
["深色图标", "#c1c1bd", "#1e1f1c", 3],
@ -9,7 +11,7 @@ const pairs = [
["浅色高亮文本", "#3d3d3d", "#f7e7b3", 4.5],
];
function toRgb(hex) {
export function toRgb(hex) {
const value = hex.replace("#", "");
return [
Number.parseInt(value.slice(0, 2), 16),
@ -18,19 +20,19 @@ function toRgb(hex) {
];
}
function channel(value) {
export function channel(value) {
const normalized = value / 255;
return normalized <= 0.03928
? normalized / 12.92
: ((normalized + 0.055) / 1.055) ** 2.4;
}
function luminance(hex) {
export function luminance(hex) {
const [red, green, blue] = toRgb(hex).map(channel);
return 0.2126 * red + 0.7152 * green + 0.0722 * blue;
}
function contrast(foreground, background) {
export function contrast(foreground, background) {
const foregroundLuminance = luminance(foreground);
const backgroundLuminance = luminance(background);
const lighter = Math.max(foregroundLuminance, backgroundLuminance);
@ -38,19 +40,39 @@ function contrast(foreground, background) {
return (lighter + 0.05) / (darker + 0.05);
}
let hasFailure = false;
export function checkContrastPairs(contrastPairs = pairs) {
return contrastPairs.map(([label, foreground, background, minimum]) => {
const ratio = contrast(foreground, background);
for (const [label, foreground, background, minimum] of pairs) {
const ratio = contrast(foreground, background);
const passed = ratio >= minimum;
const formattedRatio = ratio.toFixed(2);
console.log(`${label}: ${formattedRatio}:1要求 >= ${minimum}:1`);
return {
label,
foreground,
background,
minimum,
ratio,
passed: ratio >= minimum,
};
});
}
if (!passed) {
hasFailure = true;
function runCli() {
const results = checkContrastPairs();
let hasFailure = false;
for (const result of results) {
const formattedRatio = result.ratio.toFixed(2);
console.log(`${result.label}: ${formattedRatio}:1要求 >= ${result.minimum}:1`);
if (!result.passed) {
hasFailure = true;
}
}
if (hasFailure) {
process.exitCode = 1;
}
}
if (hasFailure) {
process.exitCode = 1;
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
runCli();
}

View file

@ -1,9 +1,9 @@
import { mkdirSync, writeFileSync } from "node:fs";
import { resolve } from "node:path";
const qaDir = resolve(
"C:/Users/Jie/iCloudDrive/iCloud~md~obsidian/SecondBrain/project/Monokai Syntax/QA",
);
import { resolveQaPaths, resolveVaultRoot } from "./qa-paths.js";
const { qaDir } = resolveQaPaths(resolveVaultRoot());
mkdirSync(qaDir, { recursive: true });

View file

@ -171,19 +171,24 @@ const lines = [
"}",
"",
".nav-file-title::before {",
' display: inline-block;',
' flex: 0 0 1rem;',
' width: 1rem;',
' margin-inline-end: 0.35rem;',
" display: inline-flex;",
" align-items: center;",
" justify-content: center;",
" width: var(--monokai-file-tree-slot-size);",
" min-width: var(--monokai-file-tree-slot-size);",
" height: var(--monokai-file-tree-glyph-size);",
" margin-inline-end: calc(-1 * var(--monokai-file-tree-slot-size));",
" transform: translateX(calc(-1 * (var(--monokai-file-tree-slot-size) + var(--monokai-file-tree-icon-gap))));",
` --monokai-file-icon-color: ${defaultFileIcon.fontColor};`,
' color: var(--monokai-file-icon-color, var(--icon-color));',
` content: "${cssContent(defaultFileIcon.fontCharacter)}";`,
' font-family: "monokai-pro-icons";',
" font-size: 1rem;",
" font-size: var(--monokai-file-tree-glyph-size);",
" font-style: normal;",
" font-variant: normal;",
" font-weight: 400;",
" line-height: 1;",
" pointer-events: none;",
" speak: never;",
" text-align: center;",
" text-rendering: auto;",

22
scripts/qa-paths.js Normal file
View file

@ -0,0 +1,22 @@
import { resolve } from "node:path";
export const DEFAULT_VAULT = "C:/Users/Jie/iCloudDrive/iCloud~md~obsidian/SecondBrain";
export function resolveVaultRoot({ argv = process.argv.slice(2), env = process.env } = {}) {
return argv[0] ?? env.OBSIDIAN_VAULT ?? DEFAULT_VAULT;
}
export function resolveQaPaths(vaultRoot) {
const vaultDir = resolve(vaultRoot);
const qaDir = resolve(vaultDir, "project/Monokai Syntax/QA");
const themeDir = resolve(vaultDir, ".obsidian/themes/Monokai Syntax");
return {
vaultDir,
qaDir,
themeDir,
appearancePath: resolve(vaultDir, ".obsidian/appearance.json"),
themeCssPath: resolve(themeDir, "theme.css"),
manifestPath: resolve(themeDir, "manifest.json"),
};
}

View file

@ -1,16 +1,10 @@
import { copyFileSync, mkdirSync } from "node:fs";
import { resolve } from "node:path";
import { resolveQaPaths, resolveVaultRoot } from "./qa-paths.js";
const rootDir = resolve(import.meta.dirname, "..");
const vaultRoot = process.argv[2] ?? process.env.OBSIDIAN_VAULT;
if (!vaultRoot) {
console.error("请设置 OBSIDIAN_VAULT 环境变量或通过命令行参数指定 Vault 路径");
console.error("示例npm run sync:vault -- D:/你的/Obsidian/Vault");
process.exit(1);
}
const vaultThemeDir = resolve(vaultRoot, ".obsidian/themes/Monokai Syntax");
const { themeDir: vaultThemeDir } = resolveQaPaths(resolveVaultRoot());
mkdirSync(vaultThemeDir, { recursive: true });

View file

@ -24,7 +24,11 @@ const packageJson = JSON.parse(files.packageJson);
const checks = [
[
"Active 视觉验证脚本已接入 release:pack",
packageJson.scripts?.["release:pack"]?.includes("npm run verify:active-visual"),
packageJson.scripts?.verify?.includes("npm run verify:active-visual")
&& (
packageJson.scripts?.["release:pack"]?.includes("npm run verify:active-visual")
|| packageJson.scripts?.["release:pack"]?.includes("npm run verify")
),
],
[
"Active 视觉覆盖层位于插件样式之后",

View file

@ -0,0 +1,74 @@
import { createHash } from "node:crypto";
import { existsSync, readFileSync } from "node:fs";
import { resolve } from "node:path";
import { pathToFileURL } from "node:url";
function sha256(content) {
return createHash("sha256").update(content).digest("hex");
}
export function compareThemeCss({ rootThemeCss, distThemeCss }) {
const rootHash = sha256(rootThemeCss);
const distHash = sha256(distThemeCss);
return {
passed: rootHash === distHash,
rootHash,
distHash,
};
}
export function verifyGeneratedOutputs(rootDir = resolve(import.meta.dirname, "..")) {
const paths = {
rootTheme: resolve(rootDir, "theme.css"),
distTheme: resolve(rootDir, "dist/theme.css"),
generatedIcons: resolve(rootDir, "src/scss/components/_file-icons.generated.scss"),
};
const checks = [];
function add(label, passed) {
checks.push({ label, passed });
}
add("根目录 theme.css 存在", existsSync(paths.rootTheme));
add("dist/theme.css 存在", existsSync(paths.distTheme));
add("图标生成 SCSS 存在", existsSync(paths.generatedIcons));
if (existsSync(paths.rootTheme) && existsSync(paths.distTheme)) {
const comparison = compareThemeCss({
rootThemeCss: readFileSync(paths.rootTheme, "utf8"),
distThemeCss: readFileSync(paths.distTheme, "utf8"),
});
add("根目录 theme.css 与 dist/theme.css 内容一致", comparison.passed);
}
if (existsSync(paths.generatedIcons)) {
const generatedIcons = readFileSync(paths.generatedIcons, "utf8");
add("图标 SCSS 包含内联字体", /data:font\/woff;base64,/.test(generatedIcons));
add("图标 SCSS 包含 AGENTS.md 专属规则", /\[data-path="AGENTS\.md"\]::before/.test(generatedIcons));
}
const failures = checks.filter((check) => !check.passed).map((check) => check.label);
return {
passed: failures.length === 0,
failures,
checks,
};
}
function runCli() {
const result = verifyGeneratedOutputs();
for (const check of result.checks) {
console.log(`${check.label}: ${check.passed ? "通过" : "失败"}`);
}
if (!result.passed) {
process.exitCode = 1;
}
}
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
runCli();
}

View file

@ -31,6 +31,7 @@ const checks = [
["图标 content 使用 CSS Unicode 转义", /content: "[\\][0-9a-f]{4}";/i.test(files.generated)],
["图标 content 未被双重转义", !/content: "[\\][\\][0-9a-f]{4}";/i.test(files.generated)],
["默认文件图标规则", /\.nav-file-title::before/.test(files.generated)],
["文件图标以零净宽度进入 gutter 且不推开文件名", /\.nav-file-title::before[\s\S]*?display:\s*inline-flex;[\s\S]*?width:\s*var\(--monokai-file-tree-slot-size\);[\s\S]*?margin-inline-end:\s*calc\(-1 \* var\(--monokai-file-tree-slot-size\)\);[\s\S]*?transform:\s*translateX\(calc\(-1 \* \(var\(--monokai-file-tree-slot-size\) \+ var\(--monokai-file-tree-icon-gap\)\)\)\);[\s\S]*?font-size:\s*var\(--monokai-file-tree-glyph-size\);/.test(files.generated) && !/\.nav-file-title::before[\s\S]*?position:\s*absolute;/.test(files.generated)],
["Markdown 扩展名规则", /\[data-path\$="\.md"\]::before/.test(files.generated)],
["package.json 文件名规则", /\[data-path\$="\/package\.json"\]::before/.test(files.generated)],
["README.md/readme.md 文件名规则", /\[data-path\$="\/README\.md"\]::before/.test(files.generated) && /\[data-path\$="\/readme\.md"\]::before/.test(files.generated)],
@ -50,6 +51,11 @@ const checks = [
["所有默认 Callout 类型都有背景与边框映射", ["note", "abstract", "info", "todo", "tip", "question", "warning", "failure", "bug", "success", "example", "quote"].every((name) => new RegExp(`--monokai-callout-${name}-bg`).test(files.activeVisual) || new RegExp(`--monokai-callout-${name}-bg`).test(readFileSync(resolve(rootDir, "src/scss/components/_editor.scss"), "utf8")))],
["Callout 图标尺寸放大", /--callout-icon-size:\s*1\.25em/.test(readFileSync(resolve(rootDir, "src/scss/components/_editor.scss"), "utf8"))],
["文件树扩展名颜色多样", /--monokai-file-icon-color:\s*#ff6188/.test(files.generated) && /--monokai-file-icon-color:\s*#fc9867/.test(files.generated) && /--monokai-file-icon-color:\s*#ffd866/.test(files.generated) && /--monokai-file-icon-color:\s*#a9dc76/.test(files.generated) && /--monokai-file-icon-color:\s*#78dce8/.test(files.generated) && /--monokai-file-icon-color:\s*#ab9df2/.test(files.generated)],
["文件夹内容不额外生成第二个图标", !/\.nav-folder-title-content::before/.test(files.wrapper) && !/\.nav-folder-title::before/.test(files.wrapper)],
["文件行不通过移动标题盒修正对齐", !/\.nav-file-title\s*\{[\s\S]*?margin-inline-start:/.test(files.wrapper) && !/\.nav-file-title-content\s*\{[\s\S]*?margin-inline-start:/.test(files.wrapper)],
["文件树拆分布局槽与真实图标尺寸", /--monokai-file-tree-slot-size:\s*1rem/.test(files.wrapper) && /--monokai-file-tree-glyph-size:\s*0\.8125rem/.test(files.wrapper) && /--monokai-file-tree-toggle-size:\s*9px/.test(files.wrapper) && /--monokai-file-tree-toggle-stroke-width:\s*1\.75/.test(files.wrapper) && /\.nav-folder-title\s+\.nav-folder-collapse-indicator,\s*[\r\n]+\.nav-folder-title\s+\.collapse-icon[\s\S]*?flex:\s*0 0 var\(--monokai-file-tree-slot-size\);[\s\S]*?width:\s*var\(--monokai-file-tree-slot-size\);[\s\S]*?margin-inline-end:\s*var\(--monokai-file-tree-icon-gap\);/.test(files.wrapper)],
["根层文件夹折叠 SVG 使用独立轻量尺寸与线宽", /\.nav-folder-title\s+\.nav-folder-collapse-indicator svg,\s*[\r\n]+\.nav-folder-title\s+\.collapse-icon svg[\s\S]*?width:\s*var\(--monokai-file-tree-toggle-size\);[\s\S]*?height:\s*var\(--monokai-file-tree-toggle-size\);[\s\S]*?stroke-width:\s*var\(--monokai-file-tree-toggle-stroke-width\);/.test(files.wrapper)],
["紧凑模式仍共享同一图标槽变量", /body\.monokai-syntax-compact[\s\S]*?\.nav-file-title,\s*[\r\n]+\s*\.nav-folder-title[\s\S]*?--monokai-file-tree-slot-size:\s*13px;[\s\S]*?--monokai-file-tree-icon-gap:\s*4px;[\s\S]*?--monokai-file-tree-glyph-size:\s*12px;[\s\S]*?--monokai-file-tree-toggle-size:\s*8px;/.test(readFileSync(resolve(rootDir, "src/scss/plugins/_style-settings.scss"), "utf8"))],
["保留 Obsidian 默认文件夹折叠指示器", /\.nav-folder-collapse-indicator/.test(readFileSync(resolve(rootDir, "src/scss/_base.scss"), "utf8"))],
["单色图标模式规则", /body\.monokai-syntax-monochrome-icons/.test(files.wrapper)],
["保留彩色图标模式", /--monokai-file-icon-color/.test(files.generated)],

View file

@ -8,20 +8,21 @@ const activeVisualScss = readFileSync(activeVisualPath, "utf8");
const checks = [
["行内代码使用强调背景变量", /--monokai-inline-code-background:/],
["行内代码编辑与阅读模式使用同一 padding 变量", /--monokai-inline-code-padding:\s*0\.1em 0\.3em;[\s\S]*\.markdown-rendered[\s\S]*?code[\s\S]*?padding:\s*var\(--monokai-inline-code-padding\);[\s\S]*\.cm-inline-code[\s\S]*?padding:\s*var\(--monokai-inline-code-padding\);/],
["行内代码不使用着色边框变量", !/--monokai-inline-code-border:/.test(editorScss)],
["阅读视图行内代码不使用边框阴影", /\.markdown-rendered[\s\S]*?code[\s\S]*?box-shadow:\s*none;/],
["阅读视图块引用背景保持透明", /\.markdown-rendered[\s\S]*?blockquote[\s\S]*?background-color:\s*transparent;/],
["Obsidian 块引用原生变量同步透明背景", /--blockquote-background-color:\s*transparent;/],
["Obsidian 块引用原生变量同步细边框", /--blockquote-border-thickness:\s*2px;/],
["阅读视图块引用使用左边框", /\.markdown-rendered[\s\S]*?blockquote[\s\S]*?border-inline-start:\s*2px solid var\(--monokai-blockquote-border\);/],
["阅读视图块引用增加左侧呼吸感", /\.markdown-rendered[\s\S]*?blockquote[\s\S]*?padding:\s*#\{\$spacing-3\} #\{\$spacing-3\} #\{\$spacing-3\} 1\.25rem;/],
["阅读视图块引用增加左侧呼吸感", /\.markdown-rendered[\s\S]*?blockquote[\s\S]*?padding:\s*#\{\$spacing-3\} #\{\$spacing-3\} #\{\$spacing-3\} var\(--monokai-blockquote-content-gap\);/],
["深色块引用边框使用 Monokai 黄色", /body\.theme-dark[\s\S]*?--monokai-blockquote-border:\s*#\{\$color-dark-blockquote-border\};/],
["浅色块引用边框使用 Monokai 黄色", /body\.theme-light[\s\S]*?--monokai-blockquote-border:\s*#\{\$color-light-blockquote-border\};/],
["Live Preview 块引用取消默认格式符号显示", /\.cm-formatting-quote[\s\S]*?display:\s*none;/],
["Live Preview 块引用标记不重复绘制边框", /\.cm-quote[\s\S]*?border-inline-start:\s*0;/],
["Live Preview 块引用背景保持透明", /\.HyperMD-quote[\s\S]*?background-color:\s*transparent;/],
["Live Preview 块引用使用左边框", /\.HyperMD-quote[\s\S]*?border-inline-start:\s*2px solid var\(--monokai-blockquote-border\);/],
["Live Preview 块引用文本自身保留间距", /\.HyperMD-quote[\s\S]*?\.cm-quote[\s\S]*?margin-left:\s*0\.6rem;/],
["Live Preview 块引用文本自身保留间距", /\.HyperMD-quote[\s\S]*?\.cm-quote[\s\S]*?margin-left:\s*var\(--monokai-blockquote-content-gap\);/],
["浅色模式 H1-H3 字重为 700", /body\.theme-light[\s\S]*?--monokai-heading-weight:\s*700;/],
["深色模式 H1-H3 字重为 600", /body\.theme-dark[\s\S]*?--monokai-heading-weight:\s*600;/],
["标题样式使用 PIP 字重变量", /font-weight:\s*var\(--monokai-heading-weight\)/],

View file

@ -1,18 +1,8 @@
import { existsSync, readFileSync } from "node:fs";
import { resolve } from "node:path";
import { pathToFileURL } from "node:url";
const rootDir = resolve(import.meta.dirname, "..");
const vaultDir = resolve("C:/Users/Jie/iCloudDrive/iCloud~md~obsidian/SecondBrain");
const qaDir = resolve(vaultDir, "project/Monokai Syntax/QA");
const themeDir = resolve(vaultDir, ".obsidian/themes/Monokai Syntax");
const appearancePath = resolve(vaultDir, ".obsidian/appearance.json");
const themeCssPath = resolve(themeDir, "theme.css");
const checks = [];
function addCheck(label, passed) {
checks.push([label, passed]);
console.log(`${label}: ${passed ? "通过" : "失败"}`);
}
import { resolveQaPaths, resolveVaultRoot } from "./qa-paths.js";
const qaFiles = [
"Monokai Syntax QA 总览.md",
@ -22,20 +12,9 @@ const qaFiles = [
"Monokai Syntax QA.canvas",
];
for (const fileName of qaFiles) {
addCheck(`QA 文件存在:${fileName}`, existsSync(resolve(qaDir, fileName)));
}
addCheck("Vault 主题 CSS 存在", existsSync(themeCssPath));
addCheck("Vault manifest 存在", existsSync(resolve(themeDir, "manifest.json")));
const appearance = JSON.parse(readFileSync(appearancePath, "utf8"));
addCheck("Obsidian 已选择 Monokai Syntax 主题", appearance.cssTheme === "Monokai Syntax");
const themeCss = readFileSync(themeCssPath, "utf8");
const requiredPatterns = [
["深色模式变量", /\.theme-dark\{/],
["浅色模式变量", /\.theme-light\{/],
["深色模式变量", /\.theme-dark\s*\{/],
["浅色模式变量", /\.theme-light\s*\{/],
["编辑器样式", /\.markdown-rendered/],
["关系图谱样式", /\.graph-view/],
["Canvas 样式", /\.canvas-wrapper/],
@ -51,16 +30,73 @@ const requiredPatterns = [
["强调色变量", /monokai-accent-color/],
];
for (const [label, pattern] of requiredPatterns) {
addCheck(label, pattern.test(themeCss));
function readJsonResult(path, label) {
if (!existsSync(path)) {
return { ok: false, error: `${label} 不存在` };
}
try {
return { ok: true, value: JSON.parse(readFileSync(path, "utf8")) };
} catch {
return { ok: false, error: `${label} 不是有效 JSON` };
}
}
const canvas = JSON.parse(readFileSync(resolve(qaDir, "Monokai Syntax QA.canvas"), "utf8"));
addCheck("Canvas 包含节点", Array.isArray(canvas.nodes) && canvas.nodes.length >= 3);
addCheck("Canvas 包含边线", Array.isArray(canvas.edges) && canvas.edges.length >= 2);
const failed = checks.filter(([, passed]) => !passed);
if (failed.length > 0) {
process.exitCode = 1;
function addCheck(checks, label, passed) {
checks.push({ label, passed });
}
export function verifyQaVault(vaultRoot) {
const paths = resolveQaPaths(vaultRoot);
const checks = [];
for (const fileName of qaFiles) {
addCheck(checks, `QA 文件存在:${fileName}`, existsSync(resolve(paths.qaDir, fileName)));
}
addCheck(checks, "Vault 主题 CSS 存在", existsSync(paths.themeCssPath));
addCheck(checks, "Vault manifest 存在", existsSync(paths.manifestPath));
const appearance = readJsonResult(paths.appearancePath, "appearance.json");
addCheck(checks, appearance.error ?? "appearance.json 可读取", appearance.ok);
addCheck(
checks,
"Obsidian 已选择 Monokai Syntax 主题",
appearance.ok && appearance.value.cssTheme === "Monokai Syntax",
);
const themeCss = existsSync(paths.themeCssPath) ? readFileSync(paths.themeCssPath, "utf8") : "";
for (const [label, pattern] of requiredPatterns) {
addCheck(checks, label, pattern.test(themeCss));
}
const canvas = readJsonResult(resolve(paths.qaDir, "Monokai Syntax QA.canvas"), "Monokai Syntax QA.canvas");
addCheck(checks, canvas.error ?? "Canvas JSON 可读取", canvas.ok);
addCheck(checks, "Canvas 包含节点", canvas.ok && Array.isArray(canvas.value.nodes) && canvas.value.nodes.length >= 3);
addCheck(checks, "Canvas 包含边线", canvas.ok && Array.isArray(canvas.value.edges) && canvas.value.edges.length >= 2);
const failures = checks.filter((check) => !check.passed).map((check) => check.label);
return {
passed: failures.length === 0,
failures,
checks,
paths,
};
}
function runCli() {
const result = verifyQaVault(resolveVaultRoot());
for (const check of result.checks) {
console.log(`${check.label}: ${check.passed ? "通过" : "失败"}`);
}
if (!result.passed) {
process.exitCode = 1;
}
}
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
runCli();
}

View file

@ -0,0 +1,241 @@
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
const rootDir = resolve(import.meta.dirname, "..");
const files = {
editor: readFileSync(resolve(rootDir, "src/scss/components/_editor.scss"), "utf8"),
activeVisual: readFileSync(resolve(rootDir, "src/scss/_active-visual-overrides.scss"), "utf8"),
base: readFileSync(resolve(rootDir, "src/scss/_base.scss"), "utf8"),
modals: readFileSync(resolve(rootDir, "src/scss/components/_modals.scss"), "utf8"),
tabs: readFileSync(resolve(rootDir, "src/scss/components/_tabs.scss"), "utf8"),
canvas: readFileSync(resolve(rootDir, "src/scss/plugins/_canvas.scss"), "utf8"),
styleSettings: readFileSync(resolve(rootDir, "src/scss/plugins/_style-settings.scss"), "utf8"),
};
const checks = [
[
"H4-H6 在阅读模式与编辑模式都有颜色和字重覆盖",
/\.markdown-rendered[\s\S]*?h4,[\s\S]*?h5,[\s\S]*?h6[\s\S]*?font-weight:\s*var\(--monokai-heading-weight\);/.test(files.editor)
&& /\.cm-header-4[\s\S]*?color:\s*var\(--h4-color\);[\s\S]*?font-weight:\s*var\(--monokai-heading-weight\);/.test(files.editor)
&& /\.cm-header-5[\s\S]*?color:\s*var\(--h5-color\);[\s\S]*?font-weight:\s*var\(--monokai-heading-weight\);/.test(files.editor)
&& /\.cm-header-6[\s\S]*?color:\s*var\(--h6-color\);[\s\S]*?font-weight:\s*var\(--monokai-heading-weight\);/.test(files.editor),
],
[
"H1-H6 下边距按层级递减并同步编辑模式",
/--monokai-heading-gap-h1:\s*1\.25rem;[\s\S]*?--monokai-heading-gap-h2:\s*1\.1rem;[\s\S]*?--monokai-heading-gap-h3:\s*0\.95rem;[\s\S]*?--monokai-heading-gap-h4:\s*0\.8rem;[\s\S]*?--monokai-heading-gap-h5:\s*0\.7rem;[\s\S]*?--monokai-heading-gap-h6:\s*0\.6rem;/.test(files.editor)
&& /\.markdown-rendered[\s\S]*?h1\s*\{[\s\S]*?margin-block-end:\s*var\(--monokai-heading-gap-h1\);/.test(files.editor)
&& /\.markdown-rendered[\s\S]*?h6\s*\{[\s\S]*?margin-block-end:\s*var\(--monokai-heading-gap-h6\);/.test(files.editor)
&& /\.HyperMD-header-1[\s\S]*?padding-block-end:\s*var\(--monokai-heading-gap-h1\);/.test(files.editor)
&& /\.HyperMD-header-6[\s\S]*?padding-block-end:\s*var\(--monokai-heading-gap-h6\);/.test(files.editor),
],
[
"块引用阅读模式与编辑模式共享内容间距变量",
/--monokai-blockquote-content-gap:\s*1\.25rem;/.test(files.activeVisual)
&& /blockquote[\s\S]*?padding:\s*#\{\$spacing-3\} #\{\$spacing-3\} #\{\$spacing-3\} var\(--monokai-blockquote-content-gap\);/.test(files.activeVisual)
&& /\.cm-quote[\s\S]*?margin-left:\s*var\(--monokai-blockquote-content-gap\);/.test(files.activeVisual),
],
[
"Style Settings 成功色同步影响块引用边框",
/--monokai-blockquote-border:\s*var\(--monokai-success-color, var\(--monokai-default-success\)\);/.test(files.styleSettings),
],
[
"Modal 阴影使用深浅模式变量",
/body\.theme-dark[\s\S]*?--monokai-modal-shadow:\s*0 18px 50px rgb\(0 0 0 \/ 35%\);/.test(files.editor)
&& /body\.theme-light[\s\S]*?--monokai-modal-shadow:\s*0 16px 36px rgb\(61 61 61 \/ 16%\);/.test(files.editor)
&& /box-shadow:\s*var\(--monokai-modal-shadow\);/.test(files.modals),
],
[
"Canvas 选中光环跟随强调色变量",
/--monokai-selection-ring:\s*rgb\(249 38 114 \/ 28%\);/.test(files.editor)
&& /--monokai-selection-ring:\s*rgb\(200 63 103 \/ 22%\);/.test(files.editor)
&& /\.canvas-node\.is-focused[\s\S]*?box-shadow:\s*0 0 0 2px var\(--monokai-selection-ring\);/.test(files.canvas),
],
[
"紧凑模式同步压缩 Callout 与表格",
/body\.monokai-syntax-compact[\s\S]*?--callout-padding:\s*0\.9rem;/.test(files.styleSettings)
&& /body\.monokai-syntax-compact[\s\S]*?--callout-content-padding:\s*var\(--spacing-3\) 0 0 0;/.test(files.styleSettings)
&& /body\.monokai-syntax-compact[\s\S]*?\.markdown-rendered th,[\s\S]*?\.markdown-rendered td[\s\S]*?padding:\s*0\.35rem 0\.5rem;/.test(files.styleSettings),
],
[
"标签圆角使用主题半径系统",
/\.tag\s*\{[\s\S]*?border-radius:\s*var\(--monokai-tag-radius, var\(--radius-s\)\);/.test(files.base)
&& !/border-radius:\s*999px;/.test(files.base),
],
[
"链接具有键盘焦点可见状态",
/a:focus-visible,[\s\S]*?\.markdown-rendered a:focus-visible,[\s\S]*?\.cm-hmd-internal-link:focus-visible[\s\S]*?outline:\s*2px solid var\(--interactive-accent\);/.test(files.editor)
&& /outline-offset:\s*2px;/.test(files.editor),
],
[
"标签页和 Modal 输入具备克制动效与舒适内边距",
/\.workspace-tab-header\s*\{[\s\S]*?transition:[\s\S]*?color 140ms ease,[\s\S]*?background-color 140ms ease,[\s\S]*?box-shadow 140ms ease;/.test(files.tabs)
&& /\.prompt-input,[\s\S]*?\.modal textarea\s*\{[\s\S]*?padding:\s*0\.45rem 0\.6rem;[\s\S]*?transition:[\s\S]*?border-color 140ms ease,[\s\S]*?box-shadow 140ms ease;/.test(files.modals),
],
[
"表格表头具备独立背景与文字层级",
/--monokai-table-header-background:\s*rgb\(248 248 242 \/ 6%\);/.test(files.editor)
&& /--monokai-table-header-background:\s*rgb\(61 61 61 \/ 6%\);/.test(files.editor)
&& /\.markdown-rendered[\s\S]*?th\s*\{(?=[\s\S]*?color:\s*var\(--text-normal\);)(?=[\s\S]*?background-color:\s*var\(--monokai-table-header-background\);)[\s\S]*?\}/.test(files.editor),
],
[
"深色代码 token 使用 Monokai Pro palette",
/\.theme-dark[\s\S]*?--code-keyword:\s*#\{\$color-dark-red-soft\};/.test(files.base)
&& /\.theme-dark[\s\S]*?--code-operator:\s*#\{\$color-dark-orange-soft\};/.test(files.base)
&& /\.theme-dark[\s\S]*?--code-string:\s*#\{\$color-dark-yellow-soft\};/.test(files.base)
&& /\.theme-dark[\s\S]*?--code-function:\s*#\{\$color-dark-green-soft\};/.test(files.base)
&& /\.theme-dark[\s\S]*?--code-property:\s*#\{\$color-dark-cyan-soft\};/.test(files.base)
&& /\.theme-dark[\s\S]*?--code-value:\s*#\{\$color-dark-purple-soft\};/.test(files.base),
],
[
"列表符号阅读模式与编辑模式共享颜色变量",
/\.markdown-rendered[\s\S]*?ul > li::marker,[\s\S]*?ol > li::marker[\s\S]*?color:\s*var\(--list-marker-color\);/.test(files.editor)
&& /\.cm-formatting-list[\s\S]*?color:\s*var\(--list-marker-color\);/.test(files.editor),
],
[
"已完成任务阅读模式与编辑模式共享弱化文本变量",
/\.markdown-rendered[\s\S]*?\.task-list-item\.is-checked[\s\S]*?color:\s*var\(--monokai-task-done-color\);/.test(files.editor)
&& /\.HyperMD-task-line\[data-task="x"\][\s\S]*?color:\s*var\(--monokai-task-done-color\);/.test(files.editor)
&& /\.HyperMD-task-line\[data-task="X"\][\s\S]*?color:\s*var\(--monokai-task-done-color\);/.test(files.editor),
],
[
"编辑模式代码块使用阅读模式代码块背景和等宽字体",
/\.markdown-rendered[\s\S]*?pre\s*\{[\s\S]*?background-color:\s*var\(--code-background\);[\s\S]*?font-family:\s*var\(--font-monospace-theme\);/.test(files.editor)
&& /\.HyperMD-codeblock[\s\S]*?background-color:\s*var\(--code-background\);[\s\S]*?font-family:\s*var\(--font-monospace-theme\);/.test(files.editor),
],
[
"删除线阅读模式与编辑模式共享弱化颜色",
/--monokai-strikethrough-color:\s*var\(--text-muted\);/.test(files.editor)
&& /\.markdown-rendered[\s\S]*?del,[\s\S]*?s\s*\{[\s\S]*?color:\s*var\(--monokai-strikethrough-color\);[\s\S]*?text-decoration-color:\s*var\(--monokai-strikethrough-color\);/.test(files.editor)
&& /\.cm-strikethrough[\s\S]*?color:\s*var\(--monokai-strikethrough-color\);[\s\S]*?text-decoration-color:\s*var\(--monokai-strikethrough-color\);/.test(files.editor),
],
[
"编辑模式 Markdown 格式符号统一弱化",
/--monokai-formatting-marker-color:\s*var\(--text-faint\);/.test(files.editor)
&& /\.cm-formatting-strong,[\s\S]*?\.cm-formatting-em,[\s\S]*?\.cm-formatting-strikethrough,[\s\S]*?\.cm-formatting-code,[\s\S]*?\.cm-formatting-highlight[\s\S]*?color:\s*var\(--monokai-formatting-marker-color\);/.test(files.editor),
],
[
"高亮阅读模式与编辑模式共享圆角和横向内边距",
/--monokai-highlight-radius:\s*var\(--radius-s\);/.test(files.editor)
&& /--monokai-highlight-padding-inline:\s*0\.12em;/.test(files.editor)
&& /\.markdown-rendered[\s\S]*?mark\s*\{[\s\S]*?border-radius:\s*var\(--monokai-highlight-radius\);[\s\S]*?padding-inline:\s*var\(--monokai-highlight-padding-inline\);/.test(files.editor)
&& /\.cm-highlight[\s\S]*?border-radius:\s*var\(--monokai-highlight-radius\);[\s\S]*?padding-inline:\s*var\(--monokai-highlight-padding-inline\);/.test(files.editor),
],
[
"水平分割线阅读模式与编辑模式共享颜色和间距变量",
/--monokai-hr-color:\s*var\(--background-modifier-border\);/.test(files.editor)
&& /--monokai-hr-thickness:\s*1px;/.test(files.editor)
&& /--monokai-hr-spacing:\s*#\{\$spacing-5\};/.test(files.editor)
&& /\.markdown-rendered[\s\S]*?hr\s*\{[\s\S]*?border-top:\s*var\(--monokai-hr-thickness\) solid var\(--monokai-hr-color\);[\s\S]*?margin-block:\s*var\(--monokai-hr-spacing\);/.test(files.editor)
&& /\.HyperMD-hr[\s\S]*?color:\s*var\(--monokai-formatting-marker-color\);[\s\S]*?padding-block:\s*calc\(var\(--monokai-hr-spacing\) \/ 2\);/.test(files.editor)
&& /\.cm-hr[\s\S]*?color:\s*var\(--monokai-hr-color\);/.test(files.editor),
],
[
"脚注引用阅读模式与编辑模式共享链接层级",
/--monokai-footnote-color:\s*var\(--link-color\);/.test(files.editor)
&& /--monokai-footnote-marker-color:\s*var\(--text-faint\);/.test(files.editor)
&& /--monokai-footnote-font-size:\s*0\.85em;/.test(files.editor)
&& /\.markdown-rendered[\s\S]*?\.footnote-ref,[\s\S]*?\.footnote-link[\s\S]*?color:\s*var\(--monokai-footnote-color\);[\s\S]*?font-size:\s*var\(--monokai-footnote-font-size\);/.test(files.editor)
&& /\.markdown-rendered[\s\S]*?\.footnote-backref[\s\S]*?color:\s*var\(--monokai-footnote-marker-color\);/.test(files.editor)
&& /\.cm-footref[\s\S]*?color:\s*var\(--monokai-footnote-color\);[\s\S]*?font-size:\s*var\(--monokai-footnote-font-size\);/.test(files.editor)
&& /\.cm-formatting-footref[\s\S]*?color:\s*var\(--monokai-footnote-marker-color\);/.test(files.editor),
],
[
"图片与编辑模式嵌入共享边框圆角背景",
/--monokai-media-radius:\s*var\(--radius-m\);/.test(files.editor)
&& /--monokai-media-border-color:\s*var\(--background-modifier-border\);/.test(files.editor)
&& /--monokai-media-background:\s*var\(--background-secondary\);/.test(files.editor)
&& /\.markdown-rendered[\s\S]*?img,[\s\S]*?\.image-embed\s*\{[\s\S]*?background-color:\s*var\(--monokai-media-background\);[\s\S]*?border:\s*1px solid var\(--monokai-media-border-color\);[\s\S]*?border-radius:\s*var\(--monokai-media-radius\);/.test(files.editor)
&& /\.markdown-source-view\.mod-cm6[\s\S]*?\.image-embed,[\s\S]*?\.cm-embed-block\s*\{[\s\S]*?background-color:\s*var\(--monokai-media-background\);[\s\S]*?border:\s*1px solid var\(--monokai-media-border-color\);[\s\S]*?border-radius:\s*var\(--monokai-media-radius\);/.test(files.editor),
],
[
"标签阅读模式与编辑模式共享胶囊样式变量",
/--monokai-tag-color:\s*var\(--link-color\);/.test(files.editor)
&& /--monokai-tag-background:\s*var\(--background-secondary\);/.test(files.editor)
&& /--monokai-tag-border-color:\s*var\(--background-modifier-border\);/.test(files.editor)
&& /--monokai-tag-radius:\s*var\(--radius-s\);/.test(files.editor)
&& /\.tag\s*\{[\s\S]*?color:\s*var\(--monokai-tag-color, var\(--link-color\)\);[\s\S]*?background-color:\s*var\(--monokai-tag-background, var\(--background-secondary\)\);[\s\S]*?border:\s*1px solid var\(--monokai-tag-border-color, var\(--background-modifier-border\)\);[\s\S]*?border-radius:\s*var\(--monokai-tag-radius, var\(--radius-s\)\);/.test(files.base)
&& /\.cm-hashtag[\s\S]*?color:\s*var\(--monokai-tag-color\);[\s\S]*?background-color:\s*var\(--monokai-tag-background\);[\s\S]*?border:\s*1px solid var\(--monokai-tag-border-color\);[\s\S]*?border-radius:\s*var\(--monokai-tag-radius\);/.test(files.editor),
],
[
"链接阅读模式与编辑模式共享装饰和格式符号变量",
/--monokai-link-decoration-color:\s*var\(--link-color\);/.test(files.editor)
&& /--monokai-link-decoration-thickness:\s*1px;/.test(files.editor)
&& /a,[\s\S]*?\.markdown-rendered a,[\s\S]*?\.cm-hmd-internal-link,[\s\S]*?\.cm-link,[\s\S]*?\.cm-url[\s\S]*?color:\s*var\(--link-color\);[\s\S]*?text-decoration-color:\s*var\(--monokai-link-decoration-color\);[\s\S]*?text-decoration-thickness:\s*var\(--monokai-link-decoration-thickness\);/.test(files.editor)
&& /\.cm-formatting-link[\s\S]*?color:\s*var\(--monokai-formatting-marker-color\);/.test(files.editor),
],
[
"段落阅读模式与编辑模式共享行高和段落节奏变量",
/--monokai-paragraph-line-height:\s*var\(--line-height-normal\);/.test(files.editor)
&& /--monokai-paragraph-spacing:\s*#\{\$spacing-3\};/.test(files.editor)
&& /--p-spacing:\s*var\(--monokai-paragraph-spacing\);/.test(files.editor)
&& /\.markdown-rendered[\s\S]*?p\s*\{[\s\S]*?line-height:\s*var\(--monokai-paragraph-line-height\);[\s\S]*?margin-block:\s*0 var\(--monokai-paragraph-spacing\);/.test(files.editor)
&& /\.markdown-source-view\.mod-cm6[\s\S]*?\.cm-line\s*\{[\s\S]*?line-height:\s*var\(--monokai-paragraph-line-height\);/.test(files.editor)
&& /\.HyperMD-paragraph[\s\S]*?padding-block-end:\s*calc\(var\(--monokai-paragraph-spacing\) \/ 3\);/.test(files.editor),
],
[
"Markdown 表格源码在编辑模式具备等宽和分隔符层级",
/--monokai-table-source-background:\s*var\(--background-secondary\);/.test(files.editor)
&& /--monokai-table-source-font-family:\s*var\(--font-monospace-theme\);/.test(files.editor)
&& /--monokai-table-source-separator-color:\s*var\(--text-faint\);/.test(files.editor)
&& /\.HyperMD-table-row[\s\S]*?font-family:\s*var\(--monokai-table-source-font-family\);[\s\S]*?background-color:\s*var\(--monokai-table-source-background\);/.test(files.editor)
&& /\.cm-hmd-table-sep,[\s\S]*?\.cm-hmd-table-align,[\s\S]*?\.cm-formatting-table[\s\S]*?color:\s*var\(--monokai-table-source-separator-color\);/.test(files.editor)
&& /\.cm-table[\s\S]*?color:\s*var\(--text-normal\);/.test(files.editor),
],
[
"Callout 编辑态标题和源码标记贴近阅读模式层级",
/--monokai-callout-title-gap:\s*var\(--spacing-2\);/.test(files.activeVisual)
&& /--monokai-callout-source-marker-color:\s*var\(--text-faint\);/.test(files.activeVisual)
&& /\.callout-title,[\s\S]*?\.cm-callout-title[\s\S]*?gap:\s*var\(--monokai-callout-title-gap\);/.test(files.activeVisual)
&& /body \.markdown-source-view\.mod-cm6 \.cm-callout-content[\s\S]*?color:\s*var\(--text-normal\);/.test(files.activeVisual)
&& /body \.markdown-source-view\.mod-cm6 \.cm-formatting-callout,[\s\S]*?body \.markdown-source-view\.mod-cm6 \.cm-callout \.cm-formatting[\s\S]*?color:\s*var\(--monokai-callout-source-marker-color\);/.test(files.activeVisual),
],
[
"块引用多行和嵌套层级在阅读模式与编辑模式收敛",
/--monokai-blockquote-nested-gap:\s*#\{\$spacing-3\};/.test(files.activeVisual)
&& /body \.markdown-rendered blockquote blockquote[\s\S]*?margin-block:\s*var\(--monokai-blockquote-nested-gap\);/.test(files.activeVisual)
&& /body \.markdown-source-view\.mod-cm6 \.cm-line\.HyperMD-quote \+ \.cm-line\.HyperMD-quote,[\s\S]*?body \.markdown-source-view\.mod-cm6 \.HyperMD-quote \+ \.HyperMD-quote[\s\S]*?border-start-start-radius:\s*0;[\s\S]*?border-end-start-radius:\s*0;/.test(files.activeVisual)
&& /\.cm-quote-2,[\s\S]*?\.cm-quote-3[\s\S]*?color:\s*var\(--text-normal\);/.test(files.activeVisual),
],
[
"代码块阅读模式与编辑模式共享边框圆角并弱化 fence token",
/--monokai-codeblock-border-color:\s*var\(--background-modifier-border\);/.test(files.editor)
&& /--monokai-codeblock-radius:\s*var\(--radius-m\);/.test(files.editor)
&& /--monokai-codeblock-padding:\s*#\{\$spacing-4\};/.test(files.editor)
&& /--monokai-codeblock-padding-inline:\s*#\{\$spacing-4\};/.test(files.editor)
&& /\.markdown-rendered[\s\S]*?pre\s*\{[\s\S]*?border:\s*1px solid var\(--monokai-codeblock-border-color\);[\s\S]*?border-radius:\s*var\(--monokai-codeblock-radius\);[\s\S]*?padding:\s*var\(--monokai-codeblock-padding\);/.test(files.editor)
&& /\.HyperMD-codeblock[\s\S]*?background-color:\s*var\(--code-background\);[\s\S]*?font-family:\s*var\(--font-monospace-theme\);[\s\S]*?padding-inline:\s*var\(--monokai-codeblock-padding-inline\);/.test(files.editor)
&& /\.cm-formatting-code-block,[\s\S]*?\.cm-hmd-codeblock-begin,[\s\S]*?\.cm-hmd-codeblock-end[\s\S]*?color:\s*var\(--monokai-formatting-marker-color\);/.test(files.editor),
],
[
"附件与内部嵌入扩展共享媒体容器变量",
/--monokai-embed-padding:\s*#\{\$spacing-3\};/.test(files.editor)
&& /\.markdown-rendered[\s\S]*?\.file-embed,[\s\S]*?\.pdf-embed,[\s\S]*?\.audio-embed,[\s\S]*?\.video-embed,[\s\S]*?\.internal-embed\s*\{[\s\S]*?background-color:\s*var\(--monokai-media-background\);[\s\S]*?border:\s*1px solid var\(--monokai-media-border-color\);[\s\S]*?border-radius:\s*var\(--monokai-media-radius\);[\s\S]*?padding:\s*var\(--monokai-embed-padding\);/.test(files.editor)
&& /\.markdown-source-view\.mod-cm6[\s\S]*?\.file-embed,[\s\S]*?\.pdf-embed,[\s\S]*?\.audio-embed,[\s\S]*?\.video-embed,[\s\S]*?\.internal-embed,[\s\S]*?\.cm-embed-block\s*\{[\s\S]*?background-color:\s*var\(--monokai-media-background\);[\s\S]*?border:\s*1px solid var\(--monokai-media-border-color\);[\s\S]*?border-radius:\s*var\(--monokai-media-radius\);[\s\S]*?padding:\s*var\(--monokai-embed-padding\);/.test(files.editor),
],
[
"扩展任务状态阅读模式与编辑模式共享语义颜色",
/--monokai-task-forward-color:\s*var\(--text-accent\);/.test(files.editor)
&& /--monokai-task-cancelled-color:\s*var\(--text-faint\);/.test(files.editor)
&& /--monokai-task-question-color:\s*var\(--link-color\);/.test(files.editor)
&& /\.markdown-rendered[\s\S]*?\.task-list-item\[data-task=">"\][\s\S]*?color:\s*var\(--monokai-task-forward-color\);/.test(files.editor)
&& /\.markdown-rendered[\s\S]*?\.task-list-item\[data-task="-"\][\s\S]*?color:\s*var\(--monokai-task-cancelled-color\);[\s\S]*?text-decoration-line:\s*line-through;/.test(files.editor)
&& /\.markdown-rendered[\s\S]*?\.task-list-item\[data-task="\?"\][\s\S]*?color:\s*var\(--monokai-task-question-color\);/.test(files.editor)
&& /\.HyperMD-task-line\[data-task=">"\][\s\S]*?color:\s*var\(--monokai-task-forward-color\);/.test(files.editor)
&& /\.HyperMD-task-line\[data-task="-"\][\s\S]*?color:\s*var\(--monokai-task-cancelled-color\);[\s\S]*?text-decoration-line:\s*line-through;/.test(files.editor)
&& /\.HyperMD-task-line\[data-task="\?"\][\s\S]*?color:\s*var\(--monokai-task-question-color\);/.test(files.editor),
],
];
let hasFailure = false;
for (const [label, passed] of checks) {
console.log(`${label}: ${passed ? "通过" : "失败"}`);
if (!passed) {
hasFailure = true;
}
}
if (hasFailure) {
process.exitCode = 1;
}

View file

@ -0,0 +1,78 @@
import { existsSync, readFileSync } from "node:fs";
import { resolve } from "node:path";
import { pathToFileURL } from "node:url";
export function readJsonFile(path) {
return JSON.parse(readFileSync(path, "utf8"));
}
export function normalizeReleaseTag(tag) {
return tag?.replace(/^refs\/tags\//, "").replace(/^v/, "");
}
export function checkVersionConsistency({
packageJson,
manifestJson,
versionsJson,
releaseTag = process.env.GITHUB_REF_NAME,
requireReleaseTag = Boolean(process.env.GITHUB_ACTIONS && process.env.GITHUB_REF_TYPE === "tag"),
}) {
const failures = [];
const packageVersion = packageJson.version;
const manifestVersion = manifestJson.version;
const minAppVersion = manifestJson.minAppVersion;
const tagVersion = normalizeReleaseTag(releaseTag);
if (!packageVersion) {
failures.push("package.json 缺少 version 字段");
}
if (manifestVersion !== packageVersion) {
failures.push(`manifest.json 版本 ${manifestVersion ?? "缺失"} 与 package.json 版本 ${packageVersion ?? "缺失"} 不一致`);
}
if (!versionsJson[packageVersion]) {
failures.push(`versions.json 缺少当前版本 ${packageVersion} 的映射`);
}
if (versionsJson[packageVersion] !== minAppVersion) {
failures.push(`versions.json 中 ${packageVersion} 的最低 Obsidian 版本应为 ${minAppVersion}`);
}
if (requireReleaseTag && !tagVersion) {
failures.push("发布环境缺少版本标签");
}
if (tagVersion && tagVersion !== packageVersion) {
failures.push(`发布标签 ${releaseTag} 与 package.json 版本 ${packageVersion} 不一致`);
}
return {
passed: failures.length === 0,
failures,
};
}
function runCli() {
const rootDir = resolve(import.meta.dirname, "..");
const result = checkVersionConsistency({
packageJson: readJsonFile(resolve(rootDir, "package.json")),
manifestJson: readJsonFile(resolve(rootDir, "manifest.json")),
versionsJson: readJsonFile(resolve(rootDir, "versions.json")),
});
if (result.passed) {
console.log("版本一致性检查: 通过");
return;
}
for (const failure of result.failures) {
console.error(`版本一致性检查: ${failure}`);
}
process.exitCode = 1;
}
if (existsSync(process.argv[1] ?? "") && import.meta.url === pathToFileURL(process.argv[1]).href) {
runCli();
}

View file

@ -9,6 +9,10 @@ body:is(.theme-dark, .theme-light) {
--callout-content-padding: var(--spacing-4) 0 0 0;
--callout-border-width: 0;
--callout-radius: var(--radius-s);
--monokai-blockquote-content-gap: 1.25rem;
--monokai-blockquote-nested-gap: #{$spacing-3};
--monokai-callout-title-gap: var(--spacing-2);
--monokai-callout-source-marker-color: var(--text-faint);
}
/* ===== 块引用(阅读 + 编辑统一) ===== */
@ -19,7 +23,11 @@ body .markdown-rendered blockquote {
border-inline-start: 2px solid var(--monokai-blockquote-border);
border-radius: 0 var(--radius-s) var(--radius-s) 0;
color: var(--text-normal);
padding: #{$spacing-3} #{$spacing-3} #{$spacing-3} 1.25rem;
padding: #{$spacing-3} #{$spacing-3} #{$spacing-3} var(--monokai-blockquote-content-gap);
}
body .markdown-rendered blockquote blockquote {
margin-block: var(--monokai-blockquote-nested-gap);
}
body .markdown-source-view.mod-cm6 .cm-line.HyperMD-quote,
@ -29,14 +37,25 @@ body .markdown-source-view.mod-cm6 .HyperMD-quote {
border-radius: 0 var(--radius-s) var(--radius-s) 0;
}
body .markdown-source-view.mod-cm6 .cm-line.HyperMD-quote + .cm-line.HyperMD-quote,
body .markdown-source-view.mod-cm6 .HyperMD-quote + .HyperMD-quote {
border-start-start-radius: 0;
border-end-start-radius: 0;
}
body .markdown-source-view.mod-cm6 .cm-line.HyperMD-quote .cm-quote,
body .markdown-source-view.mod-cm6 .HyperMD-quote .cm-quote {
color: var(--text-normal);
border-inline-start: 0;
margin-left: 0.6rem;
margin-left: var(--monokai-blockquote-content-gap);
padding-inline-start: 0;
}
body .markdown-source-view.mod-cm6 .cm-quote-2,
body .markdown-source-view.mod-cm6 .cm-quote-3 {
color: var(--text-normal);
}
body .markdown-source-view.mod-cm6 .cm-line.HyperMD-quote .cm-formatting-quote,
body .markdown-source-view.mod-cm6 .HyperMD-quote .cm-formatting-quote {
display: none;
@ -68,7 +87,7 @@ body .markdown-source-view.mod-cm6 .callout-title,
body .markdown-source-view.mod-cm6 .cm-callout-title {
color: var(--callout-border, var(--monokai-callout-note-border));
font-weight: 700;
gap: var(--spacing-2);
gap: var(--monokai-callout-title-gap);
padding-inline-start: 0;
}
@ -83,6 +102,15 @@ body .markdown-source-view.mod-cm6 .cm-callout-content {
padding-inline-start: 0;
}
body .markdown-source-view.mod-cm6 .cm-callout-content {
color: var(--text-normal);
}
body .markdown-source-view.mod-cm6 .cm-formatting-callout,
body .markdown-source-view.mod-cm6 .cm-callout .cm-formatting {
color: var(--monokai-callout-source-marker-color);
}
/* ===== Callout 语义色分组映射 ===== */
body .markdown-preview-view.markdown-rendered .callout[data-callout="note"],

View file

@ -55,16 +55,16 @@
--list-marker-color: #{$color-dark-purple};
--code-normal: #{$color-dark-text-normal};
--code-background: #{$color-dark-background-secondary};
--code-comment: #{$color-dark-text-faint};
--code-function: #{$color-dark-green};
--code-important: #{$color-dark-magenta};
--code-keyword: #{$color-dark-magenta};
--code-operator: #{$color-dark-red-soft};
--code-property: #{$color-dark-cyan};
--code-comment: #72736f;
--code-function: #{$color-dark-green-soft};
--code-important: #{$color-dark-red-soft};
--code-keyword: #{$color-dark-red-soft};
--code-operator: #{$color-dark-orange-soft};
--code-property: #{$color-dark-cyan-soft};
--code-punctuation: #{$color-dark-text-muted};
--code-string: #{$color-dark-yellow};
--code-tag: #{$color-dark-magenta};
--code-value: #{$color-dark-purple};
--code-string: #{$color-dark-yellow-soft};
--code-tag: #{$color-dark-red-soft};
--code-value: #{$color-dark-purple-soft};
--ribbon-background: #{$color-dark-background-secondary};
--nav-item-background-hover: #{$color-dark-hover};
--nav-item-background-active: #{$color-dark-active};
@ -248,10 +248,10 @@ input[type="checkbox"]:checked {
}
.tag {
color: var(--link-color);
background-color: var(--background-secondary);
border: 1px solid var(--background-modifier-border);
border-radius: 999px;
color: var(--monokai-tag-color, var(--link-color));
background-color: var(--monokai-tag-background, var(--background-secondary));
border: 1px solid var(--monokai-tag-border-color, var(--background-modifier-border));
border-radius: var(--monokai-tag-radius, var(--radius-s));
}
.tag:hover {

View file

@ -6,12 +6,48 @@
--bold-weight: 900;
--italic-color: var(--text-normal);
--heading-spacing: #{$spacing-4};
--monokai-heading-gap-h1: 1.25rem;
--monokai-heading-gap-h2: 1.1rem;
--monokai-heading-gap-h3: 0.95rem;
--monokai-heading-gap-h4: 0.8rem;
--monokai-heading-gap-h5: 0.7rem;
--monokai-heading-gap-h6: 0.6rem;
--monokai-heading-weight: 600;
--monokai-inline-code-color: #a6e22e;
--monokai-inline-code-background: var(--code-background);
--monokai-inline-code-padding: 0.1em 0.3em;
--monokai-formatting-marker-color: var(--text-faint);
--monokai-strikethrough-color: var(--text-muted);
--monokai-highlight-radius: var(--radius-s);
--monokai-highlight-padding-inline: 0.12em;
--monokai-hr-color: var(--background-modifier-border);
--monokai-hr-thickness: 1px;
--monokai-hr-spacing: #{$spacing-5};
--monokai-footnote-color: var(--link-color);
--monokai-footnote-marker-color: var(--text-faint);
--monokai-footnote-font-size: 0.85em;
--monokai-media-radius: var(--radius-m);
--monokai-media-border-color: var(--background-modifier-border);
--monokai-media-background: var(--background-secondary);
--monokai-embed-padding: #{$spacing-3};
--monokai-tag-color: var(--link-color);
--monokai-tag-background: var(--background-secondary);
--monokai-tag-border-color: var(--background-modifier-border);
--monokai-tag-radius: var(--radius-s);
--monokai-link-decoration-color: var(--link-color);
--monokai-link-decoration-thickness: 1px;
--monokai-paragraph-line-height: var(--line-height-normal);
--monokai-paragraph-spacing: #{$spacing-3};
--monokai-table-source-background: var(--background-secondary);
--monokai-table-source-font-family: var(--font-monospace-theme);
--monokai-table-source-separator-color: var(--text-faint);
--monokai-codeblock-border-color: var(--background-modifier-border);
--monokai-codeblock-radius: var(--radius-m);
--monokai-codeblock-padding: #{$spacing-4};
--monokai-codeblock-padding-inline: #{$spacing-4};
--callout-blend-mode: normal;
--callout-icon-size: 1.25em;
--p-spacing: #{$spacing-3};
--p-spacing: var(--monokai-paragraph-spacing);
-webkit-font-smoothing: antialiased;
text-rendering: optimizelegibility;
@ -50,7 +86,13 @@ body.theme-dark {
--monokai-inline-code-color: #a6e22e;
--monokai-inline-code-background: var(--code-background);
--monokai-table-border: rgb(248 248 242 / 18%);
--monokai-table-header-background: rgb(248 248 242 / 6%);
--monokai-task-done-color: rgb(248 248 242 / 68%);
--monokai-task-forward-color: var(--text-accent);
--monokai-task-cancelled-color: var(--text-faint);
--monokai-task-question-color: var(--link-color);
--monokai-modal-shadow: 0 18px 50px rgb(0 0 0 / 35%);
--monokai-selection-ring: rgb(249 38 114 / 28%);
}
body.theme-light {
@ -86,7 +128,13 @@ body.theme-light {
--monokai-inline-code-color: #269d69;
--monokai-inline-code-background: var(--code-background);
--monokai-table-border: rgb(61 61 61 / 18%);
--monokai-table-header-background: rgb(61 61 61 / 6%);
--monokai-task-done-color: rgb(61 61 61 / 66%);
--monokai-task-forward-color: var(--text-accent);
--monokai-task-cancelled-color: var(--text-faint);
--monokai-task-question-color: var(--link-color);
--monokai-modal-shadow: 0 16px 36px rgb(61 61 61 / 16%);
--monokai-selection-ring: rgb(200 63 103 / 22%);
}
.markdown-source-view.mod-cm6 .inline-title {
@ -96,10 +144,45 @@ body.theme-light {
.markdown-rendered {
h1,
h2,
h3 {
h3,
h4,
h5,
h6 {
font-weight: var(--monokai-heading-weight);
letter-spacing: 0;
margin-block: calc(var(--heading-spacing) * 1.5) var(--spacing-4);
margin-block-start: calc(var(--heading-spacing) * 1.5);
}
h1 {
margin-block-end: var(--monokai-heading-gap-h1);
}
h2 {
margin-block-end: var(--monokai-heading-gap-h2);
}
h3 {
margin-block-end: var(--monokai-heading-gap-h3);
}
h4 {
color: var(--h4-color);
margin-block-end: var(--monokai-heading-gap-h4);
}
h5 {
color: var(--h5-color);
margin-block-end: var(--monokai-heading-gap-h5);
}
h6 {
color: var(--h6-color);
margin-block-end: var(--monokai-heading-gap-h6);
}
p {
line-height: var(--monokai-paragraph-line-height);
margin-block: 0 var(--monokai-paragraph-spacing);
}
strong {
@ -111,11 +194,17 @@ body.theme-light {
color: var(--italic-color);
}
del,
s {
color: var(--monokai-strikethrough-color);
text-decoration-color: var(--monokai-strikethrough-color);
}
mark {
color: var(--monokai-highlight-text);
background-color: var(--text-highlight-bg);
border-radius: var(--radius-s);
padding-inline: 0.12em;
border-radius: var(--monokai-highlight-radius);
padding-inline: var(--monokai-highlight-padding-inline);
}
ul > li::marker,
@ -131,6 +220,19 @@ body.theme-light {
color: var(--monokai-task-done-color);
}
.task-list-item[data-task=">"] {
color: var(--monokai-task-forward-color);
}
.task-list-item[data-task="-"] {
color: var(--monokai-task-cancelled-color);
text-decoration-line: line-through;
}
.task-list-item[data-task="?"] {
color: var(--monokai-task-question-color);
}
table {
border: 1px solid var(--monokai-table-border);
}
@ -140,21 +242,27 @@ body.theme-light {
border: 1px solid var(--monokai-table-border);
}
th {
color: var(--text-normal);
background-color: var(--monokai-table-header-background);
font-weight: 700;
}
code {
color: var(--monokai-inline-code-color);
background-color: var(--monokai-inline-code-background);
border: 0;
border-radius: var(--radius-s);
box-shadow: none;
padding: 0.1em 0.3em;
padding: var(--monokai-inline-code-padding);
}
pre {
background-color: var(--code-background);
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-m);
border: 1px solid var(--monokai-codeblock-border-color);
border-radius: var(--monokai-codeblock-radius);
font-family: var(--font-monospace-theme);
padding: #{$spacing-4};
padding: var(--monokai-codeblock-padding);
code {
background-color: transparent;
@ -163,9 +271,109 @@ body.theme-light {
padding: 0;
}
}
hr {
border: 0;
border-top: var(--monokai-hr-thickness) solid var(--monokai-hr-color);
margin-block: var(--monokai-hr-spacing);
}
.footnote-ref,
.footnote-link {
color: var(--monokai-footnote-color);
font-size: var(--monokai-footnote-font-size);
}
.footnote-backref {
color: var(--monokai-footnote-marker-color);
}
img,
.image-embed {
max-width: 100%;
background-color: var(--monokai-media-background);
border: 1px solid var(--monokai-media-border-color);
border-radius: var(--monokai-media-radius);
}
.file-embed,
.pdf-embed,
.audio-embed,
.video-embed,
.internal-embed {
background-color: var(--monokai-media-background);
border: 1px solid var(--monokai-media-border-color);
border-radius: var(--monokai-media-radius);
padding: var(--monokai-embed-padding);
}
}
.markdown-source-view.mod-cm6 {
/* stylelint-disable selector-class-pattern -- Obsidian/CodeMirror 内部类名。 */
.HyperMD-header-1 {
padding-block-end: var(--monokai-heading-gap-h1);
}
.HyperMD-header-2 {
padding-block-end: var(--monokai-heading-gap-h2);
}
.HyperMD-header-3 {
padding-block-end: var(--monokai-heading-gap-h3);
}
.HyperMD-header-4 {
padding-block-end: var(--monokai-heading-gap-h4);
}
.HyperMD-header-5 {
padding-block-end: var(--monokai-heading-gap-h5);
}
.HyperMD-header-6 {
padding-block-end: var(--monokai-heading-gap-h6);
}
.HyperMD-task-line[data-task="x"],
.HyperMD-task-line[data-task="X"] {
color: var(--monokai-task-done-color);
}
.HyperMD-task-line[data-task=">"] {
color: var(--monokai-task-forward-color);
}
.HyperMD-task-line[data-task="-"] {
color: var(--monokai-task-cancelled-color);
text-decoration-line: line-through;
}
.HyperMD-task-line[data-task="?"] {
color: var(--monokai-task-question-color);
}
.HyperMD-codeblock {
background-color: var(--code-background);
font-family: var(--font-monospace-theme);
padding-inline: var(--monokai-codeblock-padding-inline);
}
.HyperMD-hr {
color: var(--monokai-formatting-marker-color);
padding-block: calc(var(--monokai-hr-spacing) / 2);
}
.HyperMD-paragraph {
padding-block-end: calc(var(--monokai-paragraph-spacing) / 3);
}
.HyperMD-table-row {
font-family: var(--monokai-table-source-font-family);
background-color: var(--monokai-table-source-background);
}
/* stylelint-enable selector-class-pattern */
.cm-header-1 {
color: var(--h1-color);
font-weight: var(--monokai-heading-weight);
@ -181,6 +389,21 @@ body.theme-light {
font-weight: var(--monokai-heading-weight);
}
.cm-header-4 {
color: var(--h4-color);
font-weight: var(--monokai-heading-weight);
}
.cm-header-5 {
color: var(--h5-color);
font-weight: var(--monokai-heading-weight);
}
.cm-header-6 {
color: var(--h6-color);
font-weight: var(--monokai-heading-weight);
}
.cm-strong {
color: var(--bold-color);
font-weight: var(--bold-weight);
@ -190,18 +413,98 @@ body.theme-light {
color: var(--italic-color);
}
.cm-strikethrough {
color: var(--monokai-strikethrough-color);
text-decoration-color: var(--monokai-strikethrough-color);
}
.cm-link,
.cm-url {
color: var(--link-color);
}
.cm-formatting-link {
color: var(--text-faint);
color: var(--monokai-formatting-marker-color);
}
.cm-hmd-table-sep,
.cm-hmd-table-align,
.cm-formatting-table {
color: var(--monokai-table-source-separator-color);
}
.cm-formatting-code-block,
.cm-hmd-codeblock-begin,
.cm-hmd-codeblock-end {
color: var(--monokai-formatting-marker-color);
}
.cm-table {
color: var(--text-normal);
}
.cm-formatting-list {
color: var(--list-marker-color);
}
.cm-formatting-strong,
.cm-formatting-em,
.cm-formatting-strikethrough,
.cm-formatting-code,
.cm-formatting-highlight {
color: var(--monokai-formatting-marker-color);
}
.cm-highlight {
color: var(--monokai-highlight-text);
background-color: var(--text-highlight-bg);
border-radius: var(--monokai-highlight-radius);
padding-inline: var(--monokai-highlight-padding-inline);
}
.cm-hr {
color: var(--monokai-hr-color);
}
.cm-footref {
color: var(--monokai-footnote-color);
font-size: var(--monokai-footnote-font-size);
}
.cm-formatting-footref {
color: var(--monokai-footnote-marker-color);
}
.image-embed,
.cm-embed-block {
max-width: 100%;
background-color: var(--monokai-media-background);
border: 1px solid var(--monokai-media-border-color);
border-radius: var(--monokai-media-radius);
}
.file-embed,
.pdf-embed,
.audio-embed,
.video-embed,
.internal-embed,
.cm-embed-block {
background-color: var(--monokai-media-background);
border: 1px solid var(--monokai-media-border-color);
border-radius: var(--monokai-media-radius);
padding: var(--monokai-embed-padding);
}
.cm-hashtag {
color: var(--monokai-tag-color);
background-color: var(--monokai-tag-background);
border: 1px solid var(--monokai-tag-border-color);
border-radius: var(--monokai-tag-radius);
padding-inline: 0.25em;
}
.cm-line {
line-height: var(--monokai-paragraph-line-height);
}
.cm-inline-code {
@ -210,7 +513,7 @@ body.theme-light {
border: 0;
border-radius: var(--radius-s);
box-shadow: none;
padding: 0.05em 0.25em;
padding: var(--monokai-inline-code-padding);
}
}
@ -248,8 +551,12 @@ body.theme-light {
a,
.markdown-rendered a,
.cm-hmd-internal-link {
.cm-hmd-internal-link,
.cm-link,
.cm-url {
color: var(--link-color);
text-decoration-color: var(--monokai-link-decoration-color);
text-decoration-thickness: var(--monokai-link-decoration-thickness);
}
a:hover,
@ -257,3 +564,11 @@ a:hover,
.cm-hmd-internal-link:hover {
color: var(--link-color-hover);
}
a:focus-visible,
.markdown-rendered a:focus-visible,
.cm-hmd-internal-link:focus-visible {
outline: 2px solid var(--interactive-accent);
outline-offset: 2px;
border-radius: var(--radius-s);
}

View file

@ -13,19 +13,24 @@
}
.nav-file-title::before {
display: inline-block;
flex: 0 0 1rem;
width: 1rem;
margin-inline-end: 0.35rem;
display: inline-flex;
align-items: center;
justify-content: center;
width: var(--monokai-file-tree-slot-size);
min-width: var(--monokai-file-tree-slot-size);
height: var(--monokai-file-tree-glyph-size);
margin-inline-end: calc(-1 * var(--monokai-file-tree-slot-size));
transform: translateX(calc(-1 * (var(--monokai-file-tree-slot-size) + var(--monokai-file-tree-icon-gap))));
--monokai-file-icon-color: #ffd866;
color: var(--monokai-file-icon-color, var(--icon-color));
content: "\e658";
font-family: "monokai-pro-icons";
font-size: 1rem;
font-size: var(--monokai-file-tree-glyph-size);
font-style: normal;
font-variant: normal;
font-weight: 400;
line-height: 1;
pointer-events: none;
speak: never;
text-align: center;
text-rendering: auto;

View file

@ -1,19 +1,52 @@
@forward "file-icons.generated";
.nav-file-title,
.nav-folder-title {
--monokai-file-tree-slot-size: 1rem;
--monokai-file-tree-icon-gap: 0.35rem;
--monokai-file-tree-glyph-size: 0.8125rem;
--monokai-file-tree-toggle-size: 9px;
--monokai-file-tree-toggle-stroke-width: 1.75;
overflow: visible;
position: relative;
}
.nav-folder-title .nav-folder-collapse-indicator,
.nav-folder-title .collapse-icon {
flex: 0 0 var(--monokai-file-tree-slot-size);
width: var(--monokai-file-tree-slot-size);
min-width: var(--monokai-file-tree-slot-size);
margin-inline-end: var(--monokai-file-tree-icon-gap);
}
.nav-folder-title .nav-folder-collapse-indicator svg,
.nav-folder-title .collapse-icon svg {
width: var(--monokai-file-tree-toggle-size);
height: var(--monokai-file-tree-toggle-size);
font-size: var(--monokai-file-tree-toggle-size);
stroke-width: var(--monokai-file-tree-toggle-stroke-width);
}
.nav-folder-title .nav-folder-collapse-indicator,
.nav-folder-title .collapse-icon {
display: inline-flex;
align-items: center;
justify-content: center;
}
.nav-folder-title[data-path*="/"] .nav-folder-collapse-indicator,
.nav-folder-title[data-path*="/"] .collapse-icon,
.nav-folder[data-path*="/"] > .nav-folder-title .nav-folder-collapse-indicator,
.nav-folder[data-path*="/"] > .nav-folder-title .collapse-icon,
.nav-folder-children .nav-folder-children .nav-folder-title .nav-folder-collapse-indicator,
.nav-folder-children .nav-folder-children .nav-folder-title .collapse-icon {
width: 1rem;
&::before {
content: "\e64d";
display: inline-block;
color: var(--icon-color);
font-family: monokai-pro-icons, monospace;
font-size: 1rem;
font-size: var(--monokai-file-tree-glyph-size);
font-style: normal;
font-weight: 400;
line-height: 1;

View file

@ -7,7 +7,7 @@
background-color: var(--background-secondary);
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-m);
box-shadow: 0 18px 50px rgb(0 0 0 / 35%);
box-shadow: var(--monokai-modal-shadow);
}
.modal-title,
@ -23,6 +23,10 @@
background-color: var(--background-primary);
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-s);
padding: 0.45rem 0.6rem;
transition:
border-color 140ms ease,
box-shadow 140ms ease;
}
.prompt-input:focus,

View file

@ -9,6 +9,10 @@
color: var(--text-muted);
background-color: transparent;
border-radius: var(--radius-s) var(--radius-s) 0 0;
transition:
color 140ms ease,
background-color 140ms ease,
box-shadow 140ms ease;
}
.workspace-tab-header:hover {

View file

@ -25,7 +25,7 @@
.canvas-node.is-focused,
.canvas-node.is-selected {
border-color: var(--interactive-accent);
box-shadow: 0 0 0 2px rgb(249 38 114 / 28%);
box-shadow: 0 0 0 2px var(--monokai-selection-ring);
}
.canvas-node-label {

View file

@ -42,12 +42,20 @@ body.monokai-syntax-filter-spectrum {
body.monokai-syntax-compact {
@include compact-density;
--callout-padding: 0.9rem;
--callout-content-padding: var(--spacing-3) 0 0 0;
.nav-files-container {
font-size: var(--monokai-nav-font-size);
}
.nav-file-title,
.nav-folder-title {
--monokai-file-tree-slot-size: 13px;
--monokai-file-tree-icon-gap: 4px;
--monokai-file-tree-glyph-size: 12px;
--monokai-file-tree-toggle-size: 8px;
min-height: var(--nav-item-size);
line-height: var(--nav-item-size);
padding: 0 6px;
@ -59,27 +67,6 @@ body.monokai-syntax-compact {
line-height: var(--nav-item-size);
}
.nav-file-title::before {
width: 13px;
flex: 0 0 13px;
margin-inline-end: 4px;
font-size: 13px;
}
.nav-folder-collapse-indicator {
width: 14px;
margin-inline-end: 1px;
}
.nav-folder-title[data-path*="/"] .nav-folder-collapse-indicator::before,
.nav-folder-title[data-path*="/"] .collapse-icon::before,
.nav-folder[data-path*="/"] > .nav-folder-title .nav-folder-collapse-indicator::before,
.nav-folder[data-path*="/"] > .nav-folder-title .collapse-icon::before,
.nav-folder-children .nav-folder-children .nav-folder-title .nav-folder-collapse-indicator::before,
.nav-folder-children .nav-folder-children .nav-folder-title .collapse-icon::before {
font-size: 13px;
}
.side-dock-ribbon-action {
height: var(--ribbon-item-size);
padding: 4px;
@ -88,6 +75,11 @@ body.monokai-syntax-compact {
.markdown-rendered pre {
padding: 0.75rem;
}
.markdown-rendered th,
.markdown-rendered td {
padding: 0.35rem 0.5rem;
}
}
body.monokai-syntax-monochrome-icons {
@ -103,6 +95,7 @@ body {
--link-color: var(--monokai-link-color, var(--monokai-default-link));
--link-external-color: var(--monokai-link-color, var(--monokai-default-link));
--blockquote-border-color: var(--monokai-success-color, var(--monokai-default-success));
--monokai-blockquote-border: var(--monokai-success-color, var(--monokai-default-success));
--checkbox-color-checked: var(--monokai-success-color, var(--monokai-default-success));
--text-highlight-bg: var(--monokai-warning-color, var(--monokai-default-warning));
--code-background: var(--monokai-code-background, var(--monokai-default-code-background));

18
test/audit-css.test.js Normal file
View file

@ -0,0 +1,18 @@
import assert from "node:assert/strict";
import test from "node:test";
import { auditCssContent } from "../scripts/audit-css.js";
test("CSS 审计允许本地内联 woff 字体", () => {
const result = auditCssContent("@font-face { src: url(data:font/woff;base64,AAAA); }");
assert.equal(result.passed, true);
});
test("CSS 审计报告远程资源和 ID 选择器", () => {
const result = auditCssContent("#app { background: url(https://example.com/a.png); }");
assert.equal(result.passed, false);
assert.match(result.failures.join("\n"), /远程 URL/);
assert.match(result.failures.join("\n"), /ID 选择器/);
});

View file

@ -0,0 +1,12 @@
import assert from "node:assert/strict";
import test from "node:test";
import { contrast, toRgb } from "../scripts/check-contrast.js";
test("十六进制颜色转换为 RGB", () => {
assert.deepEqual(toRgb("#66d9ef"), [102, 217, 239]);
});
test("对比度计算符合 WCAG 公式", () => {
assert.equal(contrast("#000000", "#ffffff").toFixed(2), "21.00");
});

16
test/qa-paths.test.js Normal file
View file

@ -0,0 +1,16 @@
import assert from "node:assert/strict";
import test from "node:test";
import { DEFAULT_VAULT, resolveVaultRoot } from "../scripts/qa-paths.js";
test("Vault 路径优先使用命令行参数", () => {
assert.equal(resolveVaultRoot({ argv: ["D:/Vault"], env: { OBSIDIAN_VAULT: "E:/Vault" } }), "D:/Vault");
});
test("Vault 路径其次使用 OBSIDIAN_VAULT", () => {
assert.equal(resolveVaultRoot({ argv: [], env: { OBSIDIAN_VAULT: "E:/Vault" } }), "E:/Vault");
});
test("Vault 路径没有输入时回退到本机默认值", () => {
assert.equal(resolveVaultRoot({ argv: [], env: {} }), DEFAULT_VAULT);
});

View file

@ -0,0 +1,16 @@
import assert from "node:assert/strict";
import test from "node:test";
import { compareThemeCss } from "../scripts/verify-generated.js";
test("主题 CSS 内容一致时哈希相同", () => {
const result = compareThemeCss({ rootThemeCss: "body{}", distThemeCss: "body{}" });
assert.equal(result.passed, true);
});
test("主题 CSS 内容不一致时报告失败", () => {
const result = compareThemeCss({ rootThemeCss: "body{}", distThemeCss: ".theme-dark{}" });
assert.equal(result.passed, false);
});

33
test/verify-qa.test.js Normal file
View file

@ -0,0 +1,33 @@
import assert from "node:assert/strict";
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { resolve } from "node:path";
import test from "node:test";
import { verifyQaVault } from "../scripts/verify-qa.js";
test("QA 验证将缺失文件记录为失败项", () => {
const vaultDir = mkdtempSync(resolve(tmpdir(), "monokai-qa-"));
mkdirSync(resolve(vaultDir, ".obsidian/themes/Monokai Syntax"), { recursive: true });
writeFileSync(resolve(vaultDir, ".obsidian/appearance.json"), "{}");
writeFileSync(resolve(vaultDir, ".obsidian/themes/Monokai Syntax/theme.css"), ".theme-dark{}");
writeFileSync(resolve(vaultDir, ".obsidian/themes/Monokai Syntax/manifest.json"), "{}");
const result = verifyQaVault(vaultDir);
assert.equal(result.passed, false);
assert.match(result.checks.map((check) => check.label).join("\n"), /QA 文件存在/);
});
test("QA 验证对无效 JSON 给出失败项而不抛出异常", () => {
const vaultDir = mkdtempSync(resolve(tmpdir(), "monokai-qa-"));
mkdirSync(resolve(vaultDir, ".obsidian/themes/Monokai Syntax"), { recursive: true });
writeFileSync(resolve(vaultDir, ".obsidian/appearance.json"), "{");
writeFileSync(resolve(vaultDir, ".obsidian/themes/Monokai Syntax/theme.css"), "");
writeFileSync(resolve(vaultDir, ".obsidian/themes/Monokai Syntax/manifest.json"), "{}");
const result = verifyQaVault(vaultDir);
assert.equal(result.passed, false);
assert.match(result.failures.join("\n"), /appearance\.json 不是有效 JSON/);
});

View file

@ -0,0 +1,26 @@
import assert from "node:assert/strict";
import test from "node:test";
import { checkVersionConsistency } from "../scripts/verify-versions.js";
test("版本检查要求 package、manifest、versions 保持一致", () => {
const result = checkVersionConsistency({
packageJson: { version: "1.2.3" },
manifestJson: { version: "1.2.3", minAppVersion: "1.0.0" },
versionsJson: { "1.2.3": "1.0.0" },
});
assert.equal(result.passed, true);
assert.deepEqual(result.failures, []);
});
test("版本检查报告不一致的中文错误", () => {
const result = checkVersionConsistency({
packageJson: { version: "1.2.3" },
manifestJson: { version: "1.2.4", minAppVersion: "1.0.0" },
versionsJson: { "1.2.3": "1.0.0" },
});
assert.equal(result.passed, false);
assert.match(result.failures.join("\n"), /manifest\.json 版本/);
});

448
theme.css
View file

@ -60,16 +60,16 @@
--list-marker-color: #ae81ff;
--code-normal: #f8f8f2;
--code-background: #1e1f1c;
--code-comment: #8f908a;
--code-function: #a6e22e;
--code-important: #f92672;
--code-keyword: #f92672;
--code-operator: #ff6188;
--code-property: #66d9ef;
--code-comment: #72736f;
--code-function: #a9dc76;
--code-important: #ff6188;
--code-keyword: #ff6188;
--code-operator: #fc9867;
--code-property: #78dce8;
--code-punctuation: #c1c1bd;
--code-string: #e6db74;
--code-tag: #f92672;
--code-value: #ae81ff;
--code-string: #ffd866;
--code-tag: #ff6188;
--code-value: #ab9df2;
--ribbon-background: #1e1f1c;
--nav-item-background-hover: #3e3d32;
--nav-item-background-active: #414339;
@ -253,10 +253,10 @@ input[type=checkbox]:checked {
}
.tag {
color: var(--link-color);
background-color: var(--background-secondary);
border: 1px solid var(--background-modifier-border);
border-radius: 999px;
color: var(--monokai-tag-color, var(--link-color));
background-color: var(--monokai-tag-background, var(--background-secondary));
border: 1px solid var(--monokai-tag-border-color, var(--background-modifier-border));
border-radius: var(--monokai-tag-radius, var(--radius-s));
}
.tag:hover {
@ -270,12 +270,48 @@ input[type=checkbox]:checked {
--bold-weight: 900;
--italic-color: var(--text-normal);
--heading-spacing: 1rem;
--monokai-heading-gap-h1: 1.25rem;
--monokai-heading-gap-h2: 1.1rem;
--monokai-heading-gap-h3: 0.95rem;
--monokai-heading-gap-h4: 0.8rem;
--monokai-heading-gap-h5: 0.7rem;
--monokai-heading-gap-h6: 0.6rem;
--monokai-heading-weight: 600;
--monokai-inline-code-color: #a6e22e;
--monokai-inline-code-background: var(--code-background);
--monokai-inline-code-padding: 0.1em 0.3em;
--monokai-formatting-marker-color: var(--text-faint);
--monokai-strikethrough-color: var(--text-muted);
--monokai-highlight-radius: var(--radius-s);
--monokai-highlight-padding-inline: 0.12em;
--monokai-hr-color: var(--background-modifier-border);
--monokai-hr-thickness: 1px;
--monokai-hr-spacing: 1.25rem;
--monokai-footnote-color: var(--link-color);
--monokai-footnote-marker-color: var(--text-faint);
--monokai-footnote-font-size: 0.85em;
--monokai-media-radius: var(--radius-m);
--monokai-media-border-color: var(--background-modifier-border);
--monokai-media-background: var(--background-secondary);
--monokai-embed-padding: 0.75rem;
--monokai-tag-color: var(--link-color);
--monokai-tag-background: var(--background-secondary);
--monokai-tag-border-color: var(--background-modifier-border);
--monokai-tag-radius: var(--radius-s);
--monokai-link-decoration-color: var(--link-color);
--monokai-link-decoration-thickness: 1px;
--monokai-paragraph-line-height: var(--line-height-normal);
--monokai-paragraph-spacing: 0.75rem;
--monokai-table-source-background: var(--background-secondary);
--monokai-table-source-font-family: var(--font-monospace-theme);
--monokai-table-source-separator-color: var(--text-faint);
--monokai-codeblock-border-color: var(--background-modifier-border);
--monokai-codeblock-radius: var(--radius-m);
--monokai-codeblock-padding: 1rem;
--monokai-codeblock-padding-inline: 1rem;
--callout-blend-mode: normal;
--callout-icon-size: 1.25em;
--p-spacing: 0.75rem;
--p-spacing: var(--monokai-paragraph-spacing);
-webkit-font-smoothing: antialiased;
text-rendering: optimizelegibility;
}
@ -313,7 +349,13 @@ body.theme-dark {
--monokai-inline-code-color: #a6e22e;
--monokai-inline-code-background: var(--code-background);
--monokai-table-border: rgb(248 248 242 / 18%);
--monokai-table-header-background: rgb(248 248 242 / 6%);
--monokai-task-done-color: rgb(248 248 242 / 68%);
--monokai-task-forward-color: var(--text-accent);
--monokai-task-cancelled-color: var(--text-faint);
--monokai-task-question-color: var(--link-color);
--monokai-modal-shadow: 0 18px 50px rgb(0 0 0 / 35%);
--monokai-selection-ring: rgb(249 38 114 / 28%);
}
body.theme-light {
@ -349,7 +391,13 @@ body.theme-light {
--monokai-inline-code-color: #269d69;
--monokai-inline-code-background: var(--code-background);
--monokai-table-border: rgb(61 61 61 / 18%);
--monokai-table-header-background: rgb(61 61 61 / 6%);
--monokai-task-done-color: rgb(61 61 61 / 66%);
--monokai-task-forward-color: var(--text-accent);
--monokai-task-cancelled-color: var(--text-faint);
--monokai-task-question-color: var(--link-color);
--monokai-modal-shadow: 0 16px 36px rgb(61 61 61 / 16%);
--monokai-selection-ring: rgb(200 63 103 / 22%);
}
.markdown-source-view.mod-cm6 .inline-title {
@ -358,10 +406,38 @@ body.theme-light {
.markdown-rendered h1,
.markdown-rendered h2,
.markdown-rendered h3 {
.markdown-rendered h3,
.markdown-rendered h4,
.markdown-rendered h5,
.markdown-rendered h6 {
font-weight: var(--monokai-heading-weight);
letter-spacing: 0;
margin-block: calc(var(--heading-spacing) * 1.5) var(--spacing-4);
margin-block-start: calc(var(--heading-spacing) * 1.5);
}
.markdown-rendered h1 {
margin-block-end: var(--monokai-heading-gap-h1);
}
.markdown-rendered h2 {
margin-block-end: var(--monokai-heading-gap-h2);
}
.markdown-rendered h3 {
margin-block-end: var(--monokai-heading-gap-h3);
}
.markdown-rendered h4 {
color: var(--h4-color);
margin-block-end: var(--monokai-heading-gap-h4);
}
.markdown-rendered h5 {
color: var(--h5-color);
margin-block-end: var(--monokai-heading-gap-h5);
}
.markdown-rendered h6 {
color: var(--h6-color);
margin-block-end: var(--monokai-heading-gap-h6);
}
.markdown-rendered p {
line-height: var(--monokai-paragraph-line-height);
margin-block: 0 var(--monokai-paragraph-spacing);
}
.markdown-rendered strong {
color: var(--bold-color);
@ -370,11 +446,16 @@ body.theme-light {
.markdown-rendered em {
color: var(--italic-color);
}
.markdown-rendered del,
.markdown-rendered s {
color: var(--monokai-strikethrough-color);
text-decoration-color: var(--monokai-strikethrough-color);
}
.markdown-rendered mark {
color: var(--monokai-highlight-text);
background-color: var(--text-highlight-bg);
border-radius: var(--radius-s);
padding-inline: 0.12em;
border-radius: var(--monokai-highlight-radius);
padding-inline: var(--monokai-highlight-padding-inline);
}
.markdown-rendered ul > li::marker,
.markdown-rendered ol > li::marker {
@ -386,6 +467,16 @@ body.theme-light {
.markdown-rendered .task-list-item.is-checked > p {
color: var(--monokai-task-done-color);
}
.markdown-rendered .task-list-item[data-task=">"] {
color: var(--monokai-task-forward-color);
}
.markdown-rendered .task-list-item[data-task="-"] {
color: var(--monokai-task-cancelled-color);
text-decoration-line: line-through;
}
.markdown-rendered .task-list-item[data-task="?"] {
color: var(--monokai-task-question-color);
}
.markdown-rendered table {
border: 1px solid var(--monokai-table-border);
}
@ -393,20 +484,25 @@ body.theme-light {
.markdown-rendered td {
border: 1px solid var(--monokai-table-border);
}
.markdown-rendered th {
color: var(--text-normal);
background-color: var(--monokai-table-header-background);
font-weight: 700;
}
.markdown-rendered code {
color: var(--monokai-inline-code-color);
background-color: var(--monokai-inline-code-background);
border: 0;
border-radius: var(--radius-s);
box-shadow: none;
padding: 0.1em 0.3em;
padding: var(--monokai-inline-code-padding);
}
.markdown-rendered pre {
background-color: var(--code-background);
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-m);
border: 1px solid var(--monokai-codeblock-border-color);
border-radius: var(--monokai-codeblock-radius);
font-family: var(--font-monospace-theme);
padding: 1rem;
padding: var(--monokai-codeblock-padding);
}
.markdown-rendered pre code {
background-color: transparent;
@ -414,7 +510,91 @@ body.theme-light {
box-shadow: none;
padding: 0;
}
.markdown-rendered hr {
border: 0;
border-top: var(--monokai-hr-thickness) solid var(--monokai-hr-color);
margin-block: var(--monokai-hr-spacing);
}
.markdown-rendered .footnote-ref,
.markdown-rendered .footnote-link {
color: var(--monokai-footnote-color);
font-size: var(--monokai-footnote-font-size);
}
.markdown-rendered .footnote-backref {
color: var(--monokai-footnote-marker-color);
}
.markdown-rendered img,
.markdown-rendered .image-embed {
max-width: 100%;
background-color: var(--monokai-media-background);
border: 1px solid var(--monokai-media-border-color);
border-radius: var(--monokai-media-radius);
}
.markdown-rendered .file-embed,
.markdown-rendered .pdf-embed,
.markdown-rendered .audio-embed,
.markdown-rendered .video-embed,
.markdown-rendered .internal-embed {
background-color: var(--monokai-media-background);
border: 1px solid var(--monokai-media-border-color);
border-radius: var(--monokai-media-radius);
padding: var(--monokai-embed-padding);
}
.markdown-source-view.mod-cm6 {
/* stylelint-disable selector-class-pattern -- Obsidian/CodeMirror 内部类名。 */
}
.markdown-source-view.mod-cm6 .HyperMD-header-1 {
padding-block-end: var(--monokai-heading-gap-h1);
}
.markdown-source-view.mod-cm6 .HyperMD-header-2 {
padding-block-end: var(--monokai-heading-gap-h2);
}
.markdown-source-view.mod-cm6 .HyperMD-header-3 {
padding-block-end: var(--monokai-heading-gap-h3);
}
.markdown-source-view.mod-cm6 .HyperMD-header-4 {
padding-block-end: var(--monokai-heading-gap-h4);
}
.markdown-source-view.mod-cm6 .HyperMD-header-5 {
padding-block-end: var(--monokai-heading-gap-h5);
}
.markdown-source-view.mod-cm6 .HyperMD-header-6 {
padding-block-end: var(--monokai-heading-gap-h6);
}
.markdown-source-view.mod-cm6 .HyperMD-task-line[data-task=x],
.markdown-source-view.mod-cm6 .HyperMD-task-line[data-task=X] {
color: var(--monokai-task-done-color);
}
.markdown-source-view.mod-cm6 .HyperMD-task-line[data-task=">"] {
color: var(--monokai-task-forward-color);
}
.markdown-source-view.mod-cm6 .HyperMD-task-line[data-task="-"] {
color: var(--monokai-task-cancelled-color);
text-decoration-line: line-through;
}
.markdown-source-view.mod-cm6 .HyperMD-task-line[data-task="?"] {
color: var(--monokai-task-question-color);
}
.markdown-source-view.mod-cm6 .HyperMD-codeblock {
background-color: var(--code-background);
font-family: var(--font-monospace-theme);
padding-inline: var(--monokai-codeblock-padding-inline);
}
.markdown-source-view.mod-cm6 .HyperMD-hr {
color: var(--monokai-formatting-marker-color);
padding-block: calc(var(--monokai-hr-spacing) / 2);
}
.markdown-source-view.mod-cm6 .HyperMD-paragraph {
padding-block-end: calc(var(--monokai-paragraph-spacing) / 3);
}
.markdown-source-view.mod-cm6 .HyperMD-table-row {
font-family: var(--monokai-table-source-font-family);
background-color: var(--monokai-table-source-background);
}
.markdown-source-view.mod-cm6 {
/* stylelint-enable selector-class-pattern */
}
.markdown-source-view.mod-cm6 .cm-header-1 {
color: var(--h1-color);
font-weight: var(--monokai-heading-weight);
@ -427,6 +607,18 @@ body.theme-light {
color: var(--h3-color);
font-weight: var(--monokai-heading-weight);
}
.markdown-source-view.mod-cm6 .cm-header-4 {
color: var(--h4-color);
font-weight: var(--monokai-heading-weight);
}
.markdown-source-view.mod-cm6 .cm-header-5 {
color: var(--h5-color);
font-weight: var(--monokai-heading-weight);
}
.markdown-source-view.mod-cm6 .cm-header-6 {
color: var(--h6-color);
font-weight: var(--monokai-heading-weight);
}
.markdown-source-view.mod-cm6 .cm-strong {
color: var(--bold-color);
font-weight: var(--bold-weight);
@ -434,16 +626,83 @@ body.theme-light {
.markdown-source-view.mod-cm6 .cm-em {
color: var(--italic-color);
}
.markdown-source-view.mod-cm6 .cm-strikethrough {
color: var(--monokai-strikethrough-color);
text-decoration-color: var(--monokai-strikethrough-color);
}
.markdown-source-view.mod-cm6 .cm-link,
.markdown-source-view.mod-cm6 .cm-url {
color: var(--link-color);
}
.markdown-source-view.mod-cm6 .cm-formatting-link {
color: var(--text-faint);
color: var(--monokai-formatting-marker-color);
}
.markdown-source-view.mod-cm6 .cm-hmd-table-sep,
.markdown-source-view.mod-cm6 .cm-hmd-table-align,
.markdown-source-view.mod-cm6 .cm-formatting-table {
color: var(--monokai-table-source-separator-color);
}
.markdown-source-view.mod-cm6 .cm-formatting-code-block,
.markdown-source-view.mod-cm6 .cm-hmd-codeblock-begin,
.markdown-source-view.mod-cm6 .cm-hmd-codeblock-end {
color: var(--monokai-formatting-marker-color);
}
.markdown-source-view.mod-cm6 .cm-table {
color: var(--text-normal);
}
.markdown-source-view.mod-cm6 .cm-formatting-list {
color: var(--list-marker-color);
}
.markdown-source-view.mod-cm6 .cm-formatting-strong,
.markdown-source-view.mod-cm6 .cm-formatting-em,
.markdown-source-view.mod-cm6 .cm-formatting-strikethrough,
.markdown-source-view.mod-cm6 .cm-formatting-code,
.markdown-source-view.mod-cm6 .cm-formatting-highlight {
color: var(--monokai-formatting-marker-color);
}
.markdown-source-view.mod-cm6 .cm-highlight {
color: var(--monokai-highlight-text);
background-color: var(--text-highlight-bg);
border-radius: var(--monokai-highlight-radius);
padding-inline: var(--monokai-highlight-padding-inline);
}
.markdown-source-view.mod-cm6 .cm-hr {
color: var(--monokai-hr-color);
}
.markdown-source-view.mod-cm6 .cm-footref {
color: var(--monokai-footnote-color);
font-size: var(--monokai-footnote-font-size);
}
.markdown-source-view.mod-cm6 .cm-formatting-footref {
color: var(--monokai-footnote-marker-color);
}
.markdown-source-view.mod-cm6 .image-embed,
.markdown-source-view.mod-cm6 .cm-embed-block {
max-width: 100%;
background-color: var(--monokai-media-background);
border: 1px solid var(--monokai-media-border-color);
border-radius: var(--monokai-media-radius);
}
.markdown-source-view.mod-cm6 .file-embed,
.markdown-source-view.mod-cm6 .pdf-embed,
.markdown-source-view.mod-cm6 .audio-embed,
.markdown-source-view.mod-cm6 .video-embed,
.markdown-source-view.mod-cm6 .internal-embed,
.markdown-source-view.mod-cm6 .cm-embed-block {
background-color: var(--monokai-media-background);
border: 1px solid var(--monokai-media-border-color);
border-radius: var(--monokai-media-radius);
padding: var(--monokai-embed-padding);
}
.markdown-source-view.mod-cm6 .cm-hashtag {
color: var(--monokai-tag-color);
background-color: var(--monokai-tag-background);
border: 1px solid var(--monokai-tag-border-color);
border-radius: var(--monokai-tag-radius);
padding-inline: 0.25em;
}
.markdown-source-view.mod-cm6 .cm-line {
line-height: var(--monokai-paragraph-line-height);
}
.markdown-source-view.mod-cm6 .cm-inline-code {
color: var(--monokai-inline-code-color);
@ -451,7 +710,7 @@ body.theme-light {
border: 0;
border-radius: var(--radius-s);
box-shadow: none;
padding: 0.05em 0.25em;
padding: var(--monokai-inline-code-padding);
}
.cm-s-obsidian span.cm-comment {
@ -480,8 +739,12 @@ body.theme-light {
a,
.markdown-rendered a,
.cm-hmd-internal-link {
.cm-hmd-internal-link,
.cm-link,
.cm-url {
color: var(--link-color);
text-decoration-color: var(--monokai-link-decoration-color);
text-decoration-thickness: var(--monokai-link-decoration-thickness);
}
a:hover,
@ -490,6 +753,14 @@ a:hover,
color: var(--link-color-hover);
}
a:focus-visible,
.markdown-rendered a:focus-visible,
.cm-hmd-internal-link:focus-visible {
outline: 2px solid var(--interactive-accent);
outline-offset: 2px;
border-radius: var(--radius-s);
}
/* stylelint-disable */
/*
* 此文件由 `npm run generate:icons` 自动生成
@ -503,19 +774,24 @@ a:hover,
font-weight: 400;
}
.nav-file-title::before {
display: inline-block;
flex: 0 0 1rem;
width: 1rem;
margin-inline-end: 0.35rem;
display: inline-flex;
align-items: center;
justify-content: center;
width: var(--monokai-file-tree-slot-size);
min-width: var(--monokai-file-tree-slot-size);
height: var(--monokai-file-tree-glyph-size);
margin-inline-end: calc(-1 * var(--monokai-file-tree-slot-size));
transform: translateX(calc(-1 * (var(--monokai-file-tree-slot-size) + var(--monokai-file-tree-icon-gap))));
--monokai-file-icon-color: #ffd866;
color: var(--monokai-file-icon-color, var(--icon-color));
content: "\e658";
font-family: "monokai-pro-icons";
font-size: 1rem;
font-size: var(--monokai-file-tree-glyph-size);
font-style: normal;
font-variant: normal;
font-weight: 400;
line-height: 1;
pointer-events: none;
speak: never;
text-align: center;
text-rendering: auto;
@ -884,14 +1160,40 @@ a:hover,
content: "\e8e8";
}
.nav-folder-title[data-path*="/"] .nav-folder-collapse-indicator,
.nav-folder-title[data-path*="/"] .collapse-icon,
.nav-folder[data-path*="/"] > .nav-folder-title .nav-folder-collapse-indicator,
.nav-folder[data-path*="/"] > .nav-folder-title .collapse-icon,
.nav-folder-children .nav-folder-children .nav-folder-title .nav-folder-collapse-indicator,
.nav-folder-children .nav-folder-children .nav-folder-title .collapse-icon {
width: 1rem;
.nav-file-title,
.nav-folder-title {
--monokai-file-tree-slot-size: 1rem;
--monokai-file-tree-icon-gap: 0.35rem;
--monokai-file-tree-glyph-size: 0.8125rem;
--monokai-file-tree-toggle-size: 9px;
--monokai-file-tree-toggle-stroke-width: 1.75;
overflow: visible;
position: relative;
}
.nav-folder-title .nav-folder-collapse-indicator,
.nav-folder-title .collapse-icon {
flex: 0 0 var(--monokai-file-tree-slot-size);
width: var(--monokai-file-tree-slot-size);
min-width: var(--monokai-file-tree-slot-size);
margin-inline-end: var(--monokai-file-tree-icon-gap);
}
.nav-folder-title .nav-folder-collapse-indicator svg,
.nav-folder-title .collapse-icon svg {
width: var(--monokai-file-tree-toggle-size);
height: var(--monokai-file-tree-toggle-size);
font-size: var(--monokai-file-tree-toggle-size);
stroke-width: var(--monokai-file-tree-toggle-stroke-width);
}
.nav-folder-title .nav-folder-collapse-indicator,
.nav-folder-title .collapse-icon {
display: inline-flex;
align-items: center;
justify-content: center;
}
.nav-folder-title[data-path*="/"] .nav-folder-collapse-indicator::before,
.nav-folder-title[data-path*="/"] .collapse-icon::before,
.nav-folder[data-path*="/"] > .nav-folder-title .nav-folder-collapse-indicator::before,
@ -902,7 +1204,7 @@ a:hover,
display: inline-block;
color: var(--icon-color);
font-family: monokai-pro-icons, monospace;
font-size: 1rem;
font-size: var(--monokai-file-tree-glyph-size);
font-style: normal;
font-weight: 400;
line-height: 1;
@ -985,6 +1287,7 @@ body.is-mobile .workspace-ribbon {
color: var(--text-muted);
background-color: transparent;
border-radius: var(--radius-s) var(--radius-s) 0 0;
transition: color 140ms ease, background-color 140ms ease, box-shadow 140ms ease;
}
.workspace-tab-header:hover {
@ -1011,7 +1314,7 @@ body.is-mobile .workspace-ribbon {
background-color: var(--background-secondary);
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-m);
box-shadow: 0 18px 50px rgba(0, 0, 0, 0.35);
box-shadow: var(--monokai-modal-shadow);
}
.modal-title,
@ -1027,6 +1330,8 @@ body.is-mobile .workspace-ribbon {
background-color: var(--background-primary);
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-s);
padding: 0.45rem 0.6rem;
transition: border-color 140ms ease, box-shadow 140ms ease;
}
.prompt-input:focus,
@ -1237,7 +1542,7 @@ body.is-mobile .workspace-ribbon {
.canvas-node.is-focused,
.canvas-node.is-selected {
border-color: var(--interactive-accent);
box-shadow: 0 0 0 2px rgba(249, 38, 114, 0.28);
box-shadow: 0 0 0 2px var(--monokai-selection-ring);
}
.canvas-node-label {
@ -1306,12 +1611,18 @@ body.monokai-syntax-compact {
--nav-item-size: 22px;
--ribbon-item-size: 32px;
--input-height: 28px;
--callout-padding: 0.9rem;
--callout-content-padding: var(--spacing-3) 0 0 0;
}
body.monokai-syntax-compact .nav-files-container {
font-size: var(--monokai-nav-font-size);
}
body.monokai-syntax-compact .nav-file-title,
body.monokai-syntax-compact .nav-folder-title {
--monokai-file-tree-slot-size: 13px;
--monokai-file-tree-icon-gap: 4px;
--monokai-file-tree-glyph-size: 12px;
--monokai-file-tree-toggle-size: 8px;
min-height: var(--nav-item-size);
line-height: var(--nav-item-size);
padding: 0 6px;
@ -1321,24 +1632,6 @@ body.monokai-syntax-compact .nav-folder-title-content {
font-size: var(--monokai-nav-font-size);
line-height: var(--nav-item-size);
}
body.monokai-syntax-compact .nav-file-title::before {
width: 13px;
flex: 0 0 13px;
margin-inline-end: 4px;
font-size: 13px;
}
body.monokai-syntax-compact .nav-folder-collapse-indicator {
width: 14px;
margin-inline-end: 1px;
}
body.monokai-syntax-compact .nav-folder-title[data-path*="/"] .nav-folder-collapse-indicator::before,
body.monokai-syntax-compact .nav-folder-title[data-path*="/"] .collapse-icon::before,
body.monokai-syntax-compact .nav-folder[data-path*="/"] > .nav-folder-title .nav-folder-collapse-indicator::before,
body.monokai-syntax-compact .nav-folder[data-path*="/"] > .nav-folder-title .collapse-icon::before,
body.monokai-syntax-compact .nav-folder-children .nav-folder-children .nav-folder-title .nav-folder-collapse-indicator::before,
body.monokai-syntax-compact .nav-folder-children .nav-folder-children .nav-folder-title .collapse-icon::before {
font-size: 13px;
}
body.monokai-syntax-compact .side-dock-ribbon-action {
height: var(--ribbon-item-size);
padding: 4px;
@ -1346,6 +1639,10 @@ body.monokai-syntax-compact .side-dock-ribbon-action {
body.monokai-syntax-compact .markdown-rendered pre {
padding: 0.75rem;
}
body.monokai-syntax-compact .markdown-rendered th,
body.monokai-syntax-compact .markdown-rendered td {
padding: 0.35rem 0.5rem;
}
body.monokai-syntax-monochrome-icons {
--icon-color: var(--text-muted);
@ -1360,6 +1657,7 @@ body {
--link-color: var(--monokai-link-color, var(--monokai-default-link));
--link-external-color: var(--monokai-link-color, var(--monokai-default-link));
--blockquote-border-color: var(--monokai-success-color, var(--monokai-default-success));
--monokai-blockquote-border: var(--monokai-success-color, var(--monokai-default-success));
--checkbox-color-checked: var(--monokai-success-color, var(--monokai-default-success));
--text-highlight-bg: var(--monokai-warning-color, var(--monokai-default-warning));
--code-background: var(--monokai-code-background, var(--monokai-default-code-background));
@ -1373,6 +1671,10 @@ body:is(.theme-dark, .theme-light) {
--callout-content-padding: var(--spacing-4) 0 0 0;
--callout-border-width: 0;
--callout-radius: var(--radius-s);
--monokai-blockquote-content-gap: 1.25rem;
--monokai-blockquote-nested-gap: 0.75rem;
--monokai-callout-title-gap: var(--spacing-2);
--monokai-callout-source-marker-color: var(--text-faint);
}
/* ===== 块引用(阅读 + 编辑统一) ===== */
@ -1382,7 +1684,11 @@ body .markdown-rendered blockquote {
border-inline-start: 2px solid var(--monokai-blockquote-border);
border-radius: 0 var(--radius-s) var(--radius-s) 0;
color: var(--text-normal);
padding: 0.75rem 0.75rem 0.75rem 1.25rem;
padding: 0.75rem 0.75rem 0.75rem var(--monokai-blockquote-content-gap);
}
body .markdown-rendered blockquote blockquote {
margin-block: var(--monokai-blockquote-nested-gap);
}
body .markdown-source-view.mod-cm6 .cm-line.HyperMD-quote,
@ -1392,14 +1698,25 @@ body .markdown-source-view.mod-cm6 .HyperMD-quote {
border-radius: 0 var(--radius-s) var(--radius-s) 0;
}
body .markdown-source-view.mod-cm6 .cm-line.HyperMD-quote + .cm-line.HyperMD-quote,
body .markdown-source-view.mod-cm6 .HyperMD-quote + .HyperMD-quote {
border-start-start-radius: 0;
border-end-start-radius: 0;
}
body .markdown-source-view.mod-cm6 .cm-line.HyperMD-quote .cm-quote,
body .markdown-source-view.mod-cm6 .HyperMD-quote .cm-quote {
color: var(--text-normal);
border-inline-start: 0;
margin-left: 0.6rem;
margin-left: var(--monokai-blockquote-content-gap);
padding-inline-start: 0;
}
body .markdown-source-view.mod-cm6 .cm-quote-2,
body .markdown-source-view.mod-cm6 .cm-quote-3 {
color: var(--text-normal);
}
body .markdown-source-view.mod-cm6 .cm-line.HyperMD-quote .cm-formatting-quote,
body .markdown-source-view.mod-cm6 .HyperMD-quote .cm-formatting-quote {
display: none;
@ -1430,7 +1747,7 @@ body .markdown-source-view.mod-cm6 .callout-title,
body .markdown-source-view.mod-cm6 .cm-callout-title {
color: var(--callout-border, var(--monokai-callout-note-border));
font-weight: 700;
gap: var(--spacing-2);
gap: var(--monokai-callout-title-gap);
padding-inline-start: 0;
}
@ -1445,6 +1762,15 @@ body .markdown-source-view.mod-cm6 .cm-callout-content {
padding-inline-start: 0;
}
body .markdown-source-view.mod-cm6 .cm-callout-content {
color: var(--text-normal);
}
body .markdown-source-view.mod-cm6 .cm-formatting-callout,
body .markdown-source-view.mod-cm6 .cm-callout .cm-formatting {
color: var(--monokai-callout-source-marker-color);
}
/* ===== Callout 语义色分组映射 ===== */
body .markdown-preview-view.markdown-rendered .callout[data-callout=note],
body .markdown-rendered .callout[data-callout=note],

View file

@ -1,4 +1,5 @@
{
"1.0.0": "1.0.0",
"1.0.1": "1.0.0"
"1.0.1": "1.0.0",
"1.0.2": "1.0.0"
}