mirror of
https://github.com/JimLiu/baoyu-skills.git
synced 2026-07-12 13:59:47 +08:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c9e57777b | |||
| 210905ef66 | |||
| bd4de7b995 | |||
| 832f06e86e | |||
| a7ba3d73db | |||
| e2a15aadcc | |||
| e3f00c103e | |||
| a501202ab6 | |||
| 166cb323dd | |||
| b7585ebf71 | |||
| 3205239067 | |||
| c5b3066962 | |||
| 84d7777246 | |||
| 8412392788 | |||
| 7cc2e640b0 | |||
| e2fa3065f7 | |||
| 8787cbe85b | |||
| 226d501e9e | |||
| 6796ec67bd | |||
| 856e8e5205 | |||
| a7d6b5c69b | |||
| 6e18725906 | |||
| 7133d54f04 | |||
| 208bb20e98 | |||
| 6edc1b7822 |
@@ -6,7 +6,7 @@
|
||||
},
|
||||
"metadata": {
|
||||
"description": "Skills shared by Baoyu for improving daily work efficiency",
|
||||
"version": "1.40.1"
|
||||
"version": "1.44.0"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
|
||||
@@ -2,6 +2,60 @@
|
||||
|
||||
English | [中文](./CHANGELOG.zh.md)
|
||||
|
||||
## 1.44.0 - 2026-03-05
|
||||
|
||||
### Features
|
||||
- `baoyu-url-to-markdown`: add `--download-media` flag to download images and videos to local directories, rewriting markdown links to local paths
|
||||
- `baoyu-url-to-markdown`: extract cover image from page meta (og:image) into YAML front matter `coverImage` field
|
||||
- `baoyu-url-to-markdown`: handle `data-src` lazy loading for WeChat and similar sites
|
||||
- `baoyu-url-to-markdown`: add EXTEND.md preferences with first-time setup for media download behavior
|
||||
|
||||
## 1.43.2 - 2026-03-05
|
||||
|
||||
### Refactor
|
||||
- `baoyu-url-to-markdown`: replace custom HTML extraction (linkedom + Readability + Turndown) with defuddle library for cleaner content extraction and markdown conversion
|
||||
|
||||
## 1.43.1 - 2026-03-02
|
||||
|
||||
### Features
|
||||
- `baoyu-post-to-x`: auto-detect WSL environment and resolve Chrome profile to Windows-native path for stable login persistence
|
||||
- `baoyu-post-to-wechat`: auto-detect WSL environment and resolve Chrome profile to Windows-native path for stable login persistence
|
||||
- `baoyu-danger-gemini-web`: WSL auto-detection for Chrome profile path; add `GEMINI_WEB_DEBUG_PORT` env var for fixed debug port
|
||||
- `baoyu-danger-x-to-markdown`: WSL auto-detection for Chrome profile path; add `X_DEBUG_PORT` env var for fixed debug port
|
||||
|
||||
## 1.43.0 - 2026-03-02
|
||||
|
||||
### Features
|
||||
- `baoyu-post-to-wechat`: support env var overrides for browser debug port (`WECHAT_BROWSER_DEBUG_PORT`) and profile directory (`WECHAT_BROWSER_PROFILE_DIR`)
|
||||
- `baoyu-post-to-x`: support env var overrides for browser debug port (`X_BROWSER_DEBUG_PORT`) and profile directory (`X_BROWSER_PROFILE_DIR`)
|
||||
|
||||
## 1.42.3 - 2026-03-02
|
||||
|
||||
### Fixes
|
||||
- `baoyu-image-gen`: use standard size presets for DashScope aspect ratio mapping instead of free-form calculation
|
||||
|
||||
## 1.42.2 - 2026-03-01
|
||||
|
||||
### Features
|
||||
- `baoyu-markdown-to-html`: inline rendering pipeline (no subprocess), fix CJK emphasis order, enhance modern theme with GFM alerts and improved typography
|
||||
- `baoyu-post-to-wechat`: internalize markdown conversion with modular renderer, add color support, simplify publishing workflow
|
||||
|
||||
## 1.42.1 - 2026-02-28
|
||||
|
||||
### Features
|
||||
- `baoyu-markdown-to-html`: modularize render.ts into cli, constants, extend-config, html-builder, renderer, themes, and types modules; bundle code highlighting themes locally
|
||||
|
||||
## 1.42.0 - 2026-02-28
|
||||
|
||||
### Features
|
||||
- `baoyu-markdown-to-html`: consolidate heritage and warm into single modern theme, add per-theme color defaults (default→blue, grace→purple, simple→green, modern→orange)
|
||||
- `baoyu-post-to-wechat`: add default color preference support in EXTEND.md, add modern theme option to first-time setup
|
||||
|
||||
## 1.41.0 - 2026-02-28
|
||||
|
||||
### Features
|
||||
- `baoyu-markdown-to-html`: rename themes (red→heritage, orange→warm), add 13 named color presets, serif-cjk font family, and per-theme style defaults
|
||||
|
||||
## 1.40.1 - 2026-02-28
|
||||
|
||||
### Features
|
||||
|
||||
@@ -2,6 +2,60 @@
|
||||
|
||||
[English](./CHANGELOG.md) | 中文
|
||||
|
||||
## 1.44.0 - 2026-03-05
|
||||
|
||||
### 新功能
|
||||
- `baoyu-url-to-markdown`:新增 `--download-media` 参数,支持下载图片和视频到本地目录,并将 Markdown 中的链接改写为本地路径
|
||||
- `baoyu-url-to-markdown`:从页面 meta 信息(og:image)提取封面图,写入 YAML front matter 的 `coverImage` 字段
|
||||
- `baoyu-url-to-markdown`:支持 `data-src` 懒加载图片提取(兼容微信公众号等站点)
|
||||
- `baoyu-url-to-markdown`:新增 EXTEND.md 偏好设置,支持首次使用引导配置媒体下载行为
|
||||
|
||||
## 1.43.2 - 2026-03-05
|
||||
|
||||
### 重构
|
||||
- `baoyu-url-to-markdown`:使用 defuddle 库替换自定义 HTML 提取逻辑(linkedom + Readability + Turndown),简化内容提取和 Markdown 转换
|
||||
|
||||
## 1.43.1 - 2026-03-02
|
||||
|
||||
### 新功能
|
||||
- `baoyu-post-to-x`:自动检测 WSL 环境,将 Chrome profile 路径解析为 Windows 本地路径,解决登录态丢失问题
|
||||
- `baoyu-post-to-wechat`:自动检测 WSL 环境,将 Chrome profile 路径解析为 Windows 本地路径,解决登录态丢失问题
|
||||
- `baoyu-danger-gemini-web`:WSL 自动检测 Chrome profile 路径;新增 `GEMINI_WEB_DEBUG_PORT` 环境变量支持固定调试端口
|
||||
- `baoyu-danger-x-to-markdown`:WSL 自动检测 Chrome profile 路径;新增 `X_DEBUG_PORT` 环境变量支持固定调试端口
|
||||
|
||||
## 1.43.0 - 2026-03-02
|
||||
|
||||
### 新功能
|
||||
- `baoyu-post-to-wechat`:支持通过环境变量覆盖浏览器调试端口(`WECHAT_BROWSER_DEBUG_PORT`)和配置目录(`WECHAT_BROWSER_PROFILE_DIR`)
|
||||
- `baoyu-post-to-x`:支持通过环境变量覆盖浏览器调试端口(`X_BROWSER_DEBUG_PORT`)和配置目录(`X_BROWSER_PROFILE_DIR`)
|
||||
|
||||
## 1.42.3 - 2026-03-02
|
||||
|
||||
### 修复
|
||||
- `baoyu-image-gen`:DashScope 宽高比映射改用标准预设尺寸匹配,避免自由计算产生无效分辨率
|
||||
|
||||
## 1.42.2 - 2026-03-01
|
||||
|
||||
### 新功能
|
||||
- `baoyu-markdown-to-html`:内联渲染管线(移除子进程),修复 CJK 强调符号处理顺序,增强 modern 主题(GFM 警告块、排版改进)
|
||||
- `baoyu-post-to-wechat`:内置 Markdown 转换模块化渲染器,新增颜色支持,简化发布流程
|
||||
|
||||
## 1.42.1 - 2026-02-28
|
||||
|
||||
### 新功能
|
||||
- `baoyu-markdown-to-html`:将 render.ts 拆分为 cli、constants、extend-config、html-builder、renderer、themes、types 模块;本地打包代码高亮主题
|
||||
|
||||
## 1.42.0 - 2026-02-28
|
||||
|
||||
### 新功能
|
||||
- `baoyu-markdown-to-html`:合并 heritage 和 warm 为 modern 主题,新增主题默认颜色(default→蓝、grace→紫、simple→绿、modern→橙)
|
||||
- `baoyu-post-to-wechat`:EXTEND.md 新增默认颜色配置,首次设置增加 modern 主题和颜色选择
|
||||
|
||||
## 1.41.0 - 2026-02-28
|
||||
|
||||
### 新功能
|
||||
- `baoyu-markdown-to-html`:重命名主题(red→heritage、orange→warm),新增 13 个颜色预设、serif-cjk 字体、主题级样式默认值
|
||||
|
||||
## 1.40.1 - 2026-02-28
|
||||
|
||||
### 新功能
|
||||
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
SRC="$REPO_ROOT/skills/baoyu-markdown-to-html/scripts/md/"
|
||||
DEST="$REPO_ROOT/skills/baoyu-post-to-wechat/scripts/md/"
|
||||
|
||||
echo "Syncing: $SRC → $DEST"
|
||||
|
||||
rsync -av --delete \
|
||||
--exclude 'node_modules/' \
|
||||
--exclude 'package-lock.json' \
|
||||
"$SRC" "$DEST"
|
||||
|
||||
echo "Installing dependencies..."
|
||||
cd "$DEST" && npm install
|
||||
|
||||
echo "Done."
|
||||
@@ -91,6 +91,8 @@ class CdpConnection {
|
||||
}
|
||||
|
||||
async function get_free_port(): Promise<number> {
|
||||
const fixed = parseInt(process.env.GEMINI_WEB_DEBUG_PORT || '', 10);
|
||||
if (fixed > 0) return fixed;
|
||||
return await new Promise((resolve, reject) => {
|
||||
const srv = net.createServer();
|
||||
srv.unref();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { execSync } from 'node:child_process';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
@@ -29,9 +30,22 @@ export function resolveGeminiWebCookiePath(): string {
|
||||
return path.join(resolveGeminiWebDataDir(), COOKIE_FILE_NAME);
|
||||
}
|
||||
|
||||
let _wslHome: string | null | undefined;
|
||||
function getWslWindowsHome(): string | null {
|
||||
if (_wslHome !== undefined) return _wslHome;
|
||||
if (!process.env.WSL_DISTRO_NAME) { _wslHome = null; return null; }
|
||||
try {
|
||||
const raw = execSync('cmd.exe /C "echo %USERPROFILE%"', { encoding: 'utf-8', timeout: 5000 }).trim().replace(/\r/g, '');
|
||||
_wslHome = execSync(`wslpath -u "${raw}"`, { encoding: 'utf-8', timeout: 5000 }).trim() || null;
|
||||
} catch { _wslHome = null; }
|
||||
return _wslHome;
|
||||
}
|
||||
|
||||
export function resolveGeminiWebChromeProfileDir(): string {
|
||||
const override = process.env.GEMINI_WEB_CHROME_PROFILE_DIR?.trim();
|
||||
if (override) return path.resolve(override);
|
||||
const wslHome = getWslWindowsHome();
|
||||
if (wslHome) return path.join(wslHome, '.local', 'share', APP_DATA_DIR, GEMINI_DATA_DIR, PROFILE_DIR_NAME);
|
||||
return path.join(resolveGeminiWebDataDir(), PROFILE_DIR_NAME);
|
||||
}
|
||||
|
||||
|
||||
@@ -117,6 +117,8 @@ class CdpConnection {
|
||||
}
|
||||
|
||||
async function getFreePort(): Promise<number> {
|
||||
const fixed = parseInt(process.env.X_DEBUG_PORT || "", 10);
|
||||
if (fixed > 0) return fixed;
|
||||
return await new Promise((resolve, reject) => {
|
||||
const srv = net.createServer();
|
||||
srv.unref();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { execSync } from "node:child_process";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
@@ -30,9 +31,22 @@ export function resolveXToMarkdownCookiePath(): string {
|
||||
return path.join(resolveXToMarkdownDataDir(), COOKIE_FILE_NAME);
|
||||
}
|
||||
|
||||
let _wslHome: string | null | undefined;
|
||||
function getWslWindowsHome(): string | null {
|
||||
if (_wslHome !== undefined) return _wslHome;
|
||||
if (!process.env.WSL_DISTRO_NAME) { _wslHome = null; return null; }
|
||||
try {
|
||||
const raw = execSync('cmd.exe /C "echo %USERPROFILE%"', { encoding: 'utf-8', timeout: 5000 }).trim().replace(/\r/g, '');
|
||||
_wslHome = execSync(`wslpath -u "${raw}"`, { encoding: 'utf-8', timeout: 5000 }).trim() || null;
|
||||
} catch { _wslHome = null; }
|
||||
return _wslHome;
|
||||
}
|
||||
|
||||
export function resolveXToMarkdownChromeProfileDir(): string {
|
||||
const override = process.env.X_CHROME_PROFILE_DIR?.trim();
|
||||
if (override) return path.resolve(override);
|
||||
const wslHome = getWslWindowsHome();
|
||||
if (wslHome) return path.join(wslHome, ".local", "share", APP_DATA_DIR, X_TO_MARKDOWN_DATA_DIR, PROFILE_DIR_NAME);
|
||||
return path.join(resolveXToMarkdownDataDir(), PROFILE_DIR_NAME);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,27 +22,53 @@ function parseAspectRatio(ar: string): { width: number; height: number } | null
|
||||
return { width: w, height: h };
|
||||
}
|
||||
|
||||
function getSizeFromAspectRatio(ar: string | null, quality: CliArgs["quality"]): string {
|
||||
const baseSize = quality === "2k" ? 1440 : 1024;
|
||||
const STANDARD_SIZES: [number, number][] = [
|
||||
[1024, 1024],
|
||||
[1280, 720],
|
||||
[720, 1280],
|
||||
[1024, 768],
|
||||
[768, 1024],
|
||||
[1536, 1024],
|
||||
[1024, 1536],
|
||||
[1536, 864],
|
||||
[864, 1536],
|
||||
];
|
||||
|
||||
if (!ar) return `${baseSize}*${baseSize}`;
|
||||
const STANDARD_SIZES_2K: [number, number][] = [
|
||||
[1536, 1536],
|
||||
[2048, 1152],
|
||||
[1152, 2048],
|
||||
[1536, 1024],
|
||||
[1024, 1536],
|
||||
[1536, 864],
|
||||
[864, 1536],
|
||||
[2048, 2048],
|
||||
];
|
||||
|
||||
function getSizeFromAspectRatio(ar: string | null, quality: CliArgs["quality"]): string {
|
||||
const is2k = quality === "2k";
|
||||
const defaultSize = is2k ? "1536*1536" : "1024*1024";
|
||||
|
||||
if (!ar) return defaultSize;
|
||||
|
||||
const parsed = parseAspectRatio(ar);
|
||||
if (!parsed) return `${baseSize}*${baseSize}`;
|
||||
if (!parsed) return defaultSize;
|
||||
|
||||
const ratio = parsed.width / parsed.height;
|
||||
const targetRatio = parsed.width / parsed.height;
|
||||
const sizes = is2k ? STANDARD_SIZES_2K : STANDARD_SIZES;
|
||||
|
||||
if (Math.abs(ratio - 1) < 0.1) {
|
||||
return `${baseSize}*${baseSize}`;
|
||||
let best = defaultSize;
|
||||
let bestDiff = Infinity;
|
||||
|
||||
for (const [w, h] of sizes) {
|
||||
const diff = Math.abs(w / h - targetRatio);
|
||||
if (diff < bestDiff) {
|
||||
bestDiff = diff;
|
||||
best = `${w}*${h}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (ratio > 1) {
|
||||
const w = Math.round(baseSize * ratio);
|
||||
return `${w}*${baseSize}`;
|
||||
}
|
||||
|
||||
const h = Math.round(baseSize / ratio);
|
||||
return `${baseSize}*${h}`;
|
||||
return best;
|
||||
}
|
||||
|
||||
function normalizeSize(size: string): string {
|
||||
|
||||
@@ -92,8 +92,7 @@ test -f "$HOME/.baoyu-skills/baoyu-post-to-wechat/EXTEND.md" && grep -o 'default
|
||||
| `default` (Recommended) | 经典主题 - 传统排版,标题居中带底边,二级标题白字彩底 |
|
||||
| `grace` | 优雅主题 - 文字阴影,圆角卡片,精致引用块 |
|
||||
| `simple` | 简洁主题 - 现代极简风,不对称圆角,清爽留白 |
|
||||
| `red` | 红色主题 - 红金配色,宋体排版,传统书法风格 |
|
||||
| `orange` | 橙色主题 - 暖色调现代风,宽松行距,圆角装饰 |
|
||||
| `modern` | 现代主题 - 大圆角、药丸形标题、宽松行距(搭配 `--color red` 还原传统红金风格) |
|
||||
|
||||
### Step 2: Convert
|
||||
|
||||
@@ -115,11 +114,32 @@ npx -y bun ${SKILL_DIR}/scripts/main.ts <markdown_file> [options]
|
||||
|
||||
| Option | Description | Default |
|
||||
|--------|-------------|---------|
|
||||
| `--theme <name>` | Theme name (default, grace, simple, red, orange) | default |
|
||||
| `--theme <name>` | Theme name (default, grace, simple, modern) | default |
|
||||
| `--color <name\|hex>` | Primary color: preset name or hex value | theme default |
|
||||
| `--font-family <name>` | Font: sans, serif, serif-cjk, mono, or CSS value | theme default |
|
||||
| `--font-size <N>` | Font size: 14px, 15px, 16px, 17px, 18px | 16px |
|
||||
| `--title <title>` | Override title from frontmatter | |
|
||||
| `--keep-title` | Keep the first heading in content | false (removed) |
|
||||
| `--help` | Show help | |
|
||||
|
||||
**Color Presets:**
|
||||
|
||||
| Name | Hex | Label |
|
||||
|------|-----|-------|
|
||||
| blue | #0F4C81 | 经典蓝 |
|
||||
| green | #009874 | 翡翠绿 |
|
||||
| vermilion | #FA5151 | 活力橘 |
|
||||
| yellow | #FECE00 | 柠檬黄 |
|
||||
| purple | #92617E | 薰衣紫 |
|
||||
| sky | #55C9EA | 天空蓝 |
|
||||
| rose | #B76E79 | 玫瑰金 |
|
||||
| olive | #556B2F | 橄榄绿 |
|
||||
| black | #333333 | 石墨黑 |
|
||||
| gray | #A9A9A9 | 雾烟灰 |
|
||||
| pink | #FFB7C5 | 樱花粉 |
|
||||
| red | #A93226 | 中国红 |
|
||||
| orange | #D97757 | 暖橘 (modern default) |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
@@ -129,6 +149,9 @@ npx -y bun ${SKILL_DIR}/scripts/main.ts article.md
|
||||
# With specific theme
|
||||
npx -y bun ${SKILL_DIR}/scripts/main.ts article.md --theme grace
|
||||
|
||||
# Theme with custom color
|
||||
npx -y bun ${SKILL_DIR}/scripts/main.ts article.md --theme modern --color red
|
||||
|
||||
# Keep the first heading in content
|
||||
npx -y bun ${SKILL_DIR}/scripts/main.ts article.md --keep-title
|
||||
|
||||
@@ -171,8 +194,7 @@ npx -y bun ${SKILL_DIR}/scripts/main.ts article.md --title "My Article"
|
||||
| `default` | 经典主题 - 传统排版,标题居中带底边,二级标题白字彩底 |
|
||||
| `grace` | 优雅主题 - 文字阴影,圆角卡片,精致引用块 (by @brzhang) |
|
||||
| `simple` | 简洁主题 - 现代极简风,不对称圆角,清爽留白 (by @okooo5km) |
|
||||
| `red` | 红色主题 - 红金配色,宋体排版,传统书法风格 |
|
||||
| `orange` | 橙色主题 - 暖色调现代风,宽松行距,圆角装饰 |
|
||||
| `modern` | 现代主题 - 大圆角、药丸形标题、宽松行距(搭配 `--color red` 还原传统红金风格) |
|
||||
|
||||
## Supported Markdown Features
|
||||
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { writeFile } from 'node:fs/promises';
|
||||
import os from 'node:os';
|
||||
import { createHash } from 'node:crypto';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import https from 'node:https';
|
||||
import http from 'node:http';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import process from 'node:process';
|
||||
import type { StyleConfig, HtmlDocumentMeta } from './md/types.js';
|
||||
import { DEFAULT_STYLE, THEME_STYLE_DEFAULTS } from './md/constants.js';
|
||||
import { loadThemeCss, normalizeThemeCss } from './md/themes.js';
|
||||
import { initRenderer, renderMarkdown, postProcessHtml } from './md/renderer.js';
|
||||
import {
|
||||
buildCss, loadCodeThemeCss, buildHtmlDocument,
|
||||
inlineCss, normalizeInlineCss, modifyHtmlStructure, removeFirstHeading,
|
||||
} from './md/html-builder.js';
|
||||
|
||||
interface ImageInfo {
|
||||
placeholder: string;
|
||||
@@ -187,33 +191,23 @@ export async function convertMarkdown(markdownPath: string, options?: { title?:
|
||||
|
||||
const modifiedMarkdown = `---\n${Object.entries(frontmatter).map(([k, v]) => `${k}: ${v}`).join('\n')}\n---\n${modifiedBody}`;
|
||||
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'markdown-to-html-'));
|
||||
const tempMdPath = path.join(tempDir, 'temp-article.md');
|
||||
await writeFile(tempMdPath, modifiedMarkdown, 'utf-8');
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const renderScript = path.join(__dirname, 'md', 'render.ts');
|
||||
|
||||
console.error(`[markdown-to-html] Rendering with theme: ${theme}, keepTitle: ${keepTitle}`);
|
||||
|
||||
const args = ['-y', 'bun', renderScript, tempMdPath, '--theme', theme];
|
||||
if (keepTitle) args.push('--keep-title');
|
||||
const themeDefaults = THEME_STYLE_DEFAULTS[theme] ?? {};
|
||||
const style: StyleConfig = { ...DEFAULT_STYLE, ...themeDefaults };
|
||||
const { baseCss, themeCss } = loadThemeCss(theme);
|
||||
const css = normalizeThemeCss(buildCss(baseCss, themeCss, style));
|
||||
const codeThemeCss = loadCodeThemeCss('github');
|
||||
|
||||
const result = spawnSync('npx', args, {
|
||||
stdio: ['inherit', 'pipe', 'pipe'],
|
||||
cwd: baseDir,
|
||||
});
|
||||
const renderer = initRenderer({});
|
||||
const { html: baseHtml, readingTime } = renderMarkdown(modifiedMarkdown, renderer);
|
||||
let htmlContent = postProcessHtml(baseHtml, readingTime, renderer);
|
||||
if (!keepTitle) htmlContent = removeFirstHeading(htmlContent);
|
||||
|
||||
if (result.status !== 0) {
|
||||
const stderr = result.stderr?.toString() || '';
|
||||
throw new Error(`Render failed: ${stderr}`);
|
||||
}
|
||||
|
||||
const tempHtmlPath = tempMdPath.replace(/\.md$/i, '.html');
|
||||
if (!fs.existsSync(tempHtmlPath)) {
|
||||
throw new Error(`HTML file not generated: ${tempHtmlPath}`);
|
||||
}
|
||||
const meta: HtmlDocumentMeta = { title, author, description: summary };
|
||||
const fullHtml = buildHtmlDocument(meta, css, htmlContent, codeThemeCss);
|
||||
const inlinedHtml = normalizeInlineCss(await inlineCss(fullHtml), style);
|
||||
const renderedHtml = modifyHtmlStructure(inlinedHtml);
|
||||
|
||||
const finalHtmlPath = markdownPath.replace(/\.md$/i, '.html');
|
||||
let backupPath: string | undefined;
|
||||
@@ -224,11 +218,16 @@ export async function convertMarkdown(markdownPath: string, options?: { title?:
|
||||
fs.renameSync(finalHtmlPath, backupPath);
|
||||
}
|
||||
|
||||
fs.copyFileSync(tempHtmlPath, finalHtmlPath);
|
||||
fs.writeFileSync(finalHtmlPath, renderedHtml, 'utf-8');
|
||||
|
||||
const contentImages: ImageInfo[] = [];
|
||||
let tempDir: string | undefined;
|
||||
for (const img of images) {
|
||||
const localPath = await resolveImagePath(img.src, baseDir, tempDir);
|
||||
if (!tempDir && (img.src.startsWith('http://') || img.src.startsWith('https://'))) {
|
||||
const os = await import('node:os');
|
||||
tempDir = fs.mkdtempSync(path.join(os.default.tmpdir(), 'markdown-to-html-'));
|
||||
}
|
||||
const localPath = await resolveImagePath(img.src, baseDir, tempDir ?? baseDir);
|
||||
contentImages.push({
|
||||
placeholder: img.placeholder,
|
||||
localPath,
|
||||
@@ -236,12 +235,12 @@ export async function convertMarkdown(markdownPath: string, options?: { title?:
|
||||
});
|
||||
}
|
||||
|
||||
let htmlContent = fs.readFileSync(finalHtmlPath, 'utf-8');
|
||||
let finalContent = fs.readFileSync(finalHtmlPath, 'utf-8');
|
||||
for (const img of contentImages) {
|
||||
const imgTag = `<img src="${img.placeholder}" data-local-path="${img.localPath}" style="display: block; width: 100%; margin: 1.5em auto;">`;
|
||||
htmlContent = htmlContent.replace(img.placeholder, imgTag);
|
||||
const imgTag = `<img src="${img.originalPath}" data-local-path="${img.localPath}" style="display: block; width: 100%; margin: 1.5em auto;">`;
|
||||
finalContent = finalContent.replace(img.placeholder, imgTag);
|
||||
}
|
||||
fs.writeFileSync(finalHtmlPath, htmlContent, 'utf-8');
|
||||
fs.writeFileSync(finalHtmlPath, finalContent, 'utf-8');
|
||||
|
||||
console.error(`[markdown-to-html] HTML saved to: ${finalHtmlPath}`);
|
||||
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
import type { CliOptions, ThemeName } from "./types.js";
|
||||
import {
|
||||
FONT_FAMILY_MAP,
|
||||
FONT_SIZE_OPTIONS,
|
||||
COLOR_PRESETS,
|
||||
CODE_BLOCK_THEMES,
|
||||
} from "./constants.js";
|
||||
import { THEME_NAMES } from "./themes.js";
|
||||
import { loadExtendConfig } from "./extend-config.js";
|
||||
|
||||
export function printUsage(): void {
|
||||
console.error(
|
||||
[
|
||||
"Usage:",
|
||||
" npx tsx src/md/render.ts <markdown_file> [options]",
|
||||
"",
|
||||
"Options:",
|
||||
` --theme <name> Theme (${THEME_NAMES.join(", ")})`,
|
||||
` --color <name|hex> Primary color: ${Object.keys(COLOR_PRESETS).join(", ")}, or hex`,
|
||||
` --font-family <name> Font: ${Object.keys(FONT_FAMILY_MAP).join(", ")}, or CSS value`,
|
||||
` --font-size <N> Font size: ${FONT_SIZE_OPTIONS.join(", ")} (default: 16px)`,
|
||||
` --code-theme <name> Code highlight theme (default: github)`,
|
||||
` --mac-code-block Show Mac-style code block header`,
|
||||
` --line-number Show line numbers in code blocks`,
|
||||
` --cite Enable footnote citations`,
|
||||
` --count Show reading time / word count`,
|
||||
` --legend <value> Image caption: title-alt, alt-title, title, alt, none`,
|
||||
` --keep-title Keep the first heading in output`,
|
||||
].join("\n")
|
||||
);
|
||||
}
|
||||
|
||||
function parseArgValue(argv: string[], i: number, flag: string): string | null {
|
||||
const arg = argv[i]!;
|
||||
if (arg.includes("=")) {
|
||||
return arg.slice(flag.length + 1);
|
||||
}
|
||||
const next = argv[i + 1];
|
||||
return next ?? null;
|
||||
}
|
||||
|
||||
function resolveFontFamily(value: string): string {
|
||||
return FONT_FAMILY_MAP[value] ?? value;
|
||||
}
|
||||
|
||||
function resolveColor(value: string): string {
|
||||
return COLOR_PRESETS[value] ?? value;
|
||||
}
|
||||
|
||||
export function parseArgs(argv: string[]): CliOptions | null {
|
||||
const ext = loadExtendConfig();
|
||||
|
||||
let inputPath = "";
|
||||
let theme: ThemeName = ext.default_theme ?? "default";
|
||||
let keepTitle = ext.keep_title ?? false;
|
||||
let primaryColor: string | undefined = ext.default_color ? resolveColor(ext.default_color) : undefined;
|
||||
let fontFamily: string | undefined = ext.default_font_family ? resolveFontFamily(ext.default_font_family) : undefined;
|
||||
let fontSize: string | undefined = ext.default_font_size ?? undefined;
|
||||
let codeTheme = ext.default_code_theme ?? "github";
|
||||
let isMacCodeBlock = ext.mac_code_block ?? true;
|
||||
let isShowLineNumber = ext.show_line_number ?? false;
|
||||
let citeStatus = ext.cite ?? false;
|
||||
let countStatus = ext.count ?? false;
|
||||
let legend = ext.legend ?? "alt";
|
||||
|
||||
for (let i = 0; i < argv.length; i += 1) {
|
||||
const arg = argv[i]!;
|
||||
|
||||
if (!arg.startsWith("--") && !inputPath) {
|
||||
inputPath = arg;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === "--help" || arg === "-h") {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (arg === "--keep-title") { keepTitle = true; continue; }
|
||||
if (arg === "--mac-code-block") { isMacCodeBlock = true; continue; }
|
||||
if (arg === "--no-mac-code-block") { isMacCodeBlock = false; continue; }
|
||||
if (arg === "--line-number") { isShowLineNumber = true; continue; }
|
||||
if (arg === "--cite") { citeStatus = true; continue; }
|
||||
if (arg === "--count") { countStatus = true; continue; }
|
||||
|
||||
if (arg === "--theme" || arg.startsWith("--theme=")) {
|
||||
const val = parseArgValue(argv, i, "--theme");
|
||||
if (!val) { console.error("Missing value for --theme"); return null; }
|
||||
theme = val as ThemeName;
|
||||
if (!arg.includes("=")) i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === "--color" || arg.startsWith("--color=")) {
|
||||
const val = parseArgValue(argv, i, "--color");
|
||||
if (!val) { console.error("Missing value for --color"); return null; }
|
||||
primaryColor = resolveColor(val);
|
||||
if (!arg.includes("=")) i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === "--font-family" || arg.startsWith("--font-family=")) {
|
||||
const val = parseArgValue(argv, i, "--font-family");
|
||||
if (!val) { console.error("Missing value for --font-family"); return null; }
|
||||
fontFamily = resolveFontFamily(val);
|
||||
if (!arg.includes("=")) i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === "--font-size" || arg.startsWith("--font-size=")) {
|
||||
const val = parseArgValue(argv, i, "--font-size");
|
||||
if (!val) { console.error("Missing value for --font-size"); return null; }
|
||||
fontSize = val.endsWith("px") ? val : `${val}px`;
|
||||
if (!FONT_SIZE_OPTIONS.includes(fontSize)) {
|
||||
console.error(`Invalid font size: ${fontSize}. Valid: ${FONT_SIZE_OPTIONS.join(", ")}`);
|
||||
return null;
|
||||
}
|
||||
if (!arg.includes("=")) i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === "--code-theme" || arg.startsWith("--code-theme=")) {
|
||||
const val = parseArgValue(argv, i, "--code-theme");
|
||||
if (!val) { console.error("Missing value for --code-theme"); return null; }
|
||||
codeTheme = val;
|
||||
if (!CODE_BLOCK_THEMES.includes(codeTheme)) {
|
||||
console.error(`Unknown code theme: ${codeTheme}`);
|
||||
return null;
|
||||
}
|
||||
if (!arg.includes("=")) i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === "--legend" || arg.startsWith("--legend=")) {
|
||||
const val = parseArgValue(argv, i, "--legend");
|
||||
if (!val) { console.error("Missing value for --legend"); return null; }
|
||||
const valid = ["title-alt", "alt-title", "title", "alt", "none"];
|
||||
if (!valid.includes(val)) {
|
||||
console.error(`Invalid legend: ${val}. Valid: ${valid.join(", ")}`);
|
||||
return null;
|
||||
}
|
||||
legend = val;
|
||||
if (!arg.includes("=")) i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
console.error(`Unknown argument: ${arg}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!inputPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!THEME_NAMES.includes(theme)) {
|
||||
console.error(`Unknown theme: ${theme}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
inputPath, theme, keepTitle, primaryColor, fontFamily, fontSize,
|
||||
codeTheme, isMacCodeBlock, isShowLineNumber, citeStatus, countStatus, legend,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
||||
Theme: 1c-light
|
||||
Description: Style IDE 1C:Enterprise 8
|
||||
Author: (c) Barilko Vitaliy <barilkovetal@gmail.com>
|
||||
Maintainer: @Diversus23
|
||||
Website: https://softonit.ru/
|
||||
License: see project LICENSE
|
||||
Touched: 2023
|
||||
*/.hljs{color:#00f;background:#fff}.hljs-comment{color:green}.hljs-tag{color:#444a}.hljs-tag .hljs-attr,.hljs-tag .hljs-name{color:#444}.hljs-attribute,.hljs-doctag,.hljs-function,.hljs-keyword,.hljs-name,.hljs-punctuation,.hljs-selector-tag{color:red}.hljs-params,.hljs-type{color:#00f}.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-symbol,.hljs-template-tag{color:#000}.hljs-section,.hljs-title{color:#00f}.hljs-link,.hljs-operator,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-template-variable,.hljs-variable{color:#ab5656}.hljs-literal{color:red}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-code{color:#00f}.hljs-meta,.hljs-meta .hljs-keyword,.hljs-meta .hljs-string{color:#963200}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1,7 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
||||
Theme: a11y-dark
|
||||
Author: @ericwbailey
|
||||
Maintainer: @ericwbailey
|
||||
|
||||
Based on the Tomorrow Night Eighties theme: https://github.com/isagalaev/highlight.js/blob/master/src/styles/tomorrow-night-eighties.css
|
||||
*/.hljs{background:#2b2b2b;color:#f8f8f2}.hljs-comment,.hljs-quote{color:#d4d0ab}.hljs-deletion,.hljs-name,.hljs-regexp,.hljs-selector-class,.hljs-selector-id,.hljs-tag,.hljs-template-variable,.hljs-variable{color:#ffa07a}.hljs-built_in,.hljs-link,.hljs-literal,.hljs-meta,.hljs-number,.hljs-params,.hljs-type{color:#f5ab35}.hljs-attribute{color:gold}.hljs-addition,.hljs-bullet,.hljs-string,.hljs-symbol{color:#abe338}.hljs-section,.hljs-title{color:#00e0e0}.hljs-keyword,.hljs-selector-tag{color:#dcc6e0}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}@media screen and (-ms-high-contrast:active){.hljs-addition,.hljs-attribute,.hljs-built_in,.hljs-bullet,.hljs-comment,.hljs-link,.hljs-literal,.hljs-meta,.hljs-number,.hljs-params,.hljs-quote,.hljs-string,.hljs-symbol,.hljs-type{color:highlight}.hljs-keyword,.hljs-selector-tag{font-weight:700}}
|
||||
@@ -0,0 +1,7 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
||||
Theme: a11y-light
|
||||
Author: @ericwbailey
|
||||
Maintainer: @ericwbailey
|
||||
|
||||
Based on the Tomorrow Night Eighties theme: https://github.com/isagalaev/highlight.js/blob/master/src/styles/tomorrow-night-eighties.css
|
||||
*/.hljs{background:#fefefe;color:#545454}.hljs-comment,.hljs-quote{color:#696969}.hljs-deletion,.hljs-name,.hljs-regexp,.hljs-selector-class,.hljs-selector-id,.hljs-tag,.hljs-template-variable,.hljs-variable{color:#d91e18}.hljs-attribute,.hljs-built_in,.hljs-link,.hljs-literal,.hljs-meta,.hljs-number,.hljs-params,.hljs-type{color:#aa5d00}.hljs-addition,.hljs-bullet,.hljs-string,.hljs-symbol{color:green}.hljs-section,.hljs-title{color:#007faa}.hljs-keyword,.hljs-selector-tag{color:#7928a1}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}@media screen and (-ms-high-contrast:active){.hljs-addition,.hljs-attribute,.hljs-built_in,.hljs-bullet,.hljs-comment,.hljs-link,.hljs-literal,.hljs-meta,.hljs-number,.hljs-params,.hljs-quote,.hljs-string,.hljs-symbol,.hljs-type{color:highlight}.hljs-keyword,.hljs-selector-tag{font-weight:700}}
|
||||
@@ -0,0 +1,20 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
||||
Theme: Agate
|
||||
Author: (c) Taufik Nurrohman <hi@taufik-nurrohman.com>
|
||||
Maintainer: @taufik-nurrohman
|
||||
Updated: 2021-04-24
|
||||
|
||||
#333
|
||||
#62c8f3
|
||||
#7bd694
|
||||
#888
|
||||
#a2fca2
|
||||
#ade5fc
|
||||
#b8d8a2
|
||||
#c6b4f0
|
||||
#d36363
|
||||
#fc9b9b
|
||||
#fcc28c
|
||||
#ffa
|
||||
#fff
|
||||
*/.hljs{background:#333;color:#fff}.hljs-doctag,.hljs-meta-keyword,.hljs-name,.hljs-strong{font-weight:700}.hljs-code,.hljs-emphasis{font-style:italic}.hljs-section,.hljs-tag{color:#62c8f3}.hljs-selector-class,.hljs-selector-id,.hljs-template-variable,.hljs-variable{color:#ade5fc}.hljs-meta-string,.hljs-string{color:#a2fca2}.hljs-attr,.hljs-quote,.hljs-selector-attr{color:#7bd694}.hljs-tag .hljs-attr{color:inherit}.hljs-attribute,.hljs-title,.hljs-type{color:#ffa}.hljs-number,.hljs-symbol{color:#d36363}.hljs-bullet,.hljs-template-tag{color:#b8d8a2}.hljs-built_in,.hljs-keyword,.hljs-literal,.hljs-selector-tag{color:#fcc28c}.hljs-code,.hljs-comment,.hljs-formula{color:#888}.hljs-link,.hljs-regexp,.hljs-selector-pseudo{color:#c6b4f0}.hljs-meta{color:#fc9b9b}.hljs-deletion{background:#fc9b9b;color:#333}.hljs-addition{background:#a2fca2;color:#333}.hljs-subst{color:#fff}.hljs a{color:inherit}.hljs a:focus,.hljs a:hover{color:inherit;text-decoration:underline}.hljs mark{background:#555;color:inherit}
|
||||
@@ -0,0 +1,9 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
||||
Theme: An Old Hope – Star Wars Syntax
|
||||
Author: (c) Gustavo Costa <gusbemacbe@gmail.com>
|
||||
Maintainer: @gusbemacbe
|
||||
|
||||
Original theme - Ocean Dark Theme – by https://github.com/gavsiu
|
||||
Based on Jesse Leite's Atom syntax theme 'An Old Hope'
|
||||
https://github.com/JesseLeite/an-old-hope-syntax-atom
|
||||
*/.hljs{background:#1c1d21;color:#c0c5ce}.hljs-comment,.hljs-quote{color:#b6b18b}.hljs-deletion,.hljs-name,.hljs-regexp,.hljs-selector-class,.hljs-selector-id,.hljs-tag,.hljs-template-variable,.hljs-variable{color:#eb3c54}.hljs-built_in,.hljs-link,.hljs-literal,.hljs-meta,.hljs-number,.hljs-params,.hljs-type{color:#e7ce56}.hljs-attribute{color:#ee7c2b}.hljs-addition,.hljs-bullet,.hljs-string,.hljs-symbol{color:#4fb4d7}.hljs-section,.hljs-title{color:#78bb65}.hljs-keyword,.hljs-selector-tag{color:#b45ea4}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#a9b7c6;background:#282b2e}.hljs-bullet,.hljs-literal,.hljs-number,.hljs-symbol{color:#6897bb}.hljs-deletion,.hljs-keyword,.hljs-selector-tag{color:#cc7832}.hljs-link,.hljs-template-variable,.hljs-variable{color:#629755}.hljs-comment,.hljs-quote{color:grey}.hljs-meta{color:#bbb529}.hljs-addition,.hljs-attribute,.hljs-string{color:#6a8759}.hljs-section,.hljs-title,.hljs-type{color:#ffc66d}.hljs-name,.hljs-selector-class,.hljs-selector-id{color:#e8bf6a}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#fff;color:#434f54}.hljs-subst{color:#434f54}.hljs-attribute,.hljs-doctag,.hljs-keyword,.hljs-name,.hljs-selector-tag{color:#00979d}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-code,.hljs-literal{color:#d35400}.hljs-link,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#00979d}.hljs-deletion,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-template-tag,.hljs-type{color:#005c5f}.hljs-comment{color:rgba(149,165,166,.8)}.hljs-meta .hljs-keyword{color:#728e00}.hljs-meta{color:#434f54}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-function{color:#728e00}.hljs-section,.hljs-title{color:#800;font-weight:700}.hljs-number{color:#8a7b52}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#222;color:#aaa}.hljs-subst{color:#aaa}.hljs-section{color:#fff}.hljs-comment,.hljs-meta,.hljs-quote{color:#444}.hljs-bullet,.hljs-regexp,.hljs-string,.hljs-symbol{color:#fc3}.hljs-addition,.hljs-number{color:#0c6}.hljs-attribute,.hljs-built_in,.hljs-link,.hljs-literal,.hljs-template-variable,.hljs-type{color:#32aaee}.hljs-keyword,.hljs-name,.hljs-selector-class,.hljs-selector-id,.hljs-selector-tag{color:#64a}.hljs-deletion,.hljs-template-tag,.hljs-title,.hljs-variable{color:#b16}.hljs-doctag,.hljs-section,.hljs-strong{font-weight:700}.hljs-emphasis{font-style:italic}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#fff;color:#000}.hljs-addition,.hljs-attribute,.hljs-bullet,.hljs-link,.hljs-section,.hljs-string,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#888}.hljs-comment,.hljs-deletion,.hljs-meta,.hljs-quote{color:#ccc}.hljs-keyword,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-strong,.hljs-type{font-weight:700}.hljs-emphasis{font-style:italic}
|
||||
+1
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}.hljs-keyword,.hljs-operator,.hljs-pattern-match{color:#f92672}.hljs-function,.hljs-pattern-match .hljs-constructor{color:#61aeee}.hljs-function .hljs-params{color:#a6e22e}.hljs-function .hljs-params .hljs-typing{color:#fd971f}.hljs-module-access .hljs-module{color:#7e57c2}.hljs-constructor{color:#e2b93d}.hljs-constructor .hljs-string{color:#9ccc65}.hljs-comment,.hljs-quote{color:#b18eb1;font-style:italic}.hljs-doctag,.hljs-formula{color:#c678dd}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#98c379}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#e6c07b}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#d19a66}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#61aeee}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#c678dd}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#98c379}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#d19a66}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#61aeee}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#e6c07b}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#383a42;background:#fafafa}.hljs-comment,.hljs-quote{color:#a0a1a7;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#a626a4}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e45649}.hljs-literal{color:#0184bb}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#50a14f}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#986801}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#4078f2}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#c18401}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#363c69;background:url(./brown-papersq.png) #b7a68e}.hljs-keyword,.hljs-literal,.hljs-selector-tag{color:#059}.hljs-addition,.hljs-attribute,.hljs-built_in,.hljs-bullet,.hljs-link,.hljs-name,.hljs-section,.hljs-string,.hljs-symbol,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-type,.hljs-variable{color:#2c009f}.hljs-comment,.hljs-deletion,.hljs-meta,.hljs-quote{color:#802022}.hljs-doctag,.hljs-keyword,.hljs-literal,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-strong,.hljs-title,.hljs-type{font-weight:700}.hljs-emphasis{font-style:italic}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#222;color:#fff}.hljs-comment,.hljs-quote{color:#777}.hljs-built_in,.hljs-bullet,.hljs-deletion,.hljs-link,.hljs-literal,.hljs-meta,.hljs-number,.hljs-params,.hljs-regexp,.hljs-symbol,.hljs-tag,.hljs-template-variable,.hljs-variable{color:#ab875d}.hljs-attribute,.hljs-name,.hljs-section,.hljs-selector-class,.hljs-selector-id,.hljs-title,.hljs-type{color:#9b869b}.hljs-addition,.hljs-keyword,.hljs-selector-tag,.hljs-string{color:#8f9c6c}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#000;background:#fff}.hljs-addition,.hljs-meta,.hljs-string,.hljs-symbol,.hljs-template-tag,.hljs-template-variable{color:#756bb1}.hljs-comment,.hljs-quote{color:#636363}.hljs-bullet,.hljs-link,.hljs-literal,.hljs-number,.hljs-regexp{color:#31a354}.hljs-deletion,.hljs-variable{color:#88f}.hljs-built_in,.hljs-doctag,.hljs-keyword,.hljs-name,.hljs-section,.hljs-selector-class,.hljs-selector-id,.hljs-selector-tag,.hljs-strong,.hljs-tag,.hljs-title,.hljs-type{color:#3182bd}.hljs-emphasis{font-style:italic}.hljs-attribute{color:#e6550d}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#ddd;background:#303030}.hljs-keyword,.hljs-link,.hljs-literal,.hljs-section,.hljs-selector-tag{color:#fff}.hljs-addition,.hljs-attribute,.hljs-built_in,.hljs-bullet,.hljs-name,.hljs-string,.hljs-symbol,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-type,.hljs-variable{color:#d88}.hljs-comment,.hljs-deletion,.hljs-meta,.hljs-quote{color:#979797}.hljs-doctag,.hljs-keyword,.hljs-literal,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-strong,.hljs-title,.hljs-type{font-weight:700}.hljs-emphasis{font-style:italic}
|
||||
@@ -0,0 +1,9 @@
|
||||
/*!
|
||||
Theme: Default
|
||||
Description: Original highlight.js style
|
||||
Author: (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
|
||||
Maintainer: @highlightjs/core-team
|
||||
Website: https://highlightjs.org/
|
||||
License: see project LICENSE
|
||||
Touched: 2021
|
||||
*/pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#f3f3f3;color:#444}.hljs-comment{color:#697070}.hljs-punctuation,.hljs-tag{color:#444a}.hljs-tag .hljs-attr,.hljs-tag .hljs-name{color:#444}.hljs-attribute,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-name,.hljs-selector-tag{font-weight:700}.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-template-tag,.hljs-type{color:#800}.hljs-section,.hljs-title{color:#800;font-weight:700}.hljs-link,.hljs-operator,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#ab5656}.hljs-literal{color:#695}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-code{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta .hljs-string{color:#38a}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1,7 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
||||
Theme: devibeans (dark)
|
||||
Author: @terminaldweller
|
||||
Maintainer: @terminaldweller
|
||||
|
||||
Inspired by vim's jellybeans theme (https://github.com/nanotech/jellybeans.vim)
|
||||
*/.hljs{background:#000;color:#a39e9b}.hljs-attr,.hljs-template-tag{color:#8787d7}.hljs-comment,.hljs-doctag,.hljs-quote{color:#396}.hljs-params{color:#a39e9b}.hljs-regexp{color:#d700ff}.hljs-literal,.hljs-number,.hljs-selector-id,.hljs-tag{color:#ef5350}.hljs-meta,.hljs-meta .hljs-keyword{color:#0087ff}.hljs-code,.hljs-formula,.hljs-keyword,.hljs-link,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-variable{color:#64b5f6}.hljs-built_in,.hljs-deletion,.hljs-title{color:#ff8700}.hljs-attribute,.hljs-function,.hljs-name,.hljs-property,.hljs-section,.hljs-type{color:#ffd75f}.hljs-addition,.hljs-bullet,.hljs-meta .hljs-string,.hljs-string,.hljs-subst,.hljs-symbol{color:#558b2f}.hljs-selector-tag{color:#96f}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#000;background:#f8f8ff}.hljs-comment,.hljs-quote{color:#408080;font-style:italic}.hljs-keyword,.hljs-literal,.hljs-selector-tag,.hljs-subst{color:#954121}.hljs-number{color:#40a070}.hljs-doctag,.hljs-string{color:#219161}.hljs-section,.hljs-selector-class,.hljs-selector-id,.hljs-type{color:#19469d}.hljs-params{color:#00f}.hljs-title{color:#458;font-weight:700}.hljs-attribute,.hljs-name,.hljs-tag{color:navy;font-weight:400}.hljs-template-variable,.hljs-variable{color:teal}.hljs-link,.hljs-regexp{color:#b68}.hljs-bullet,.hljs-symbol{color:#990073}.hljs-built_in{color:#0086b3}.hljs-meta{color:#999;font-weight:700}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#0ff;background:navy}.hljs-addition,.hljs-attribute,.hljs-built_in,.hljs-bullet,.hljs-string,.hljs-symbol,.hljs-template-tag,.hljs-template-variable{color:#ff0}.hljs-keyword,.hljs-name,.hljs-section,.hljs-selector-class,.hljs-selector-id,.hljs-selector-tag,.hljs-type,.hljs-variable{color:#fff}.hljs-comment,.hljs-deletion,.hljs-doctag,.hljs-quote{color:#888}.hljs-link,.hljs-literal,.hljs-number,.hljs-regexp{color:#0f0}.hljs-meta{color:teal}.hljs-keyword,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-strong,.hljs-title{font-weight:700}.hljs-emphasis{font-style:italic}
|
||||
@@ -0,0 +1,7 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
||||
* Theme: FelipeC
|
||||
* Author: (c) 2021 Felipe Contreras <felipe.contreras@gmail.com>
|
||||
* Website: https://github.com/felipec/vim-felipec
|
||||
*
|
||||
* Autogenerated with vim-felipec's generator.
|
||||
*/.hljs{color:#dedde4;background-color:#1d1c21}.hljs ::selection,.hljs::selection{color:#1d1c21;background-color:#ba9cef}.hljs-code,.hljs-comment,.hljs-quote{color:#9e9da4}.hljs-deletion,.hljs-literal,.hljs-number{color:#f09080}.hljs-doctag,.hljs-meta,.hljs-operator,.hljs-punctuation,.hljs-selector-attr,.hljs-subst,.hljs-template-variable{color:#ffbb7b}.hljs-type{color:#fddb7c}.hljs-selector-class,.hljs-selector-id,.hljs-tag,.hljs-title{color:#c4da7d}.hljs-addition,.hljs-regexp,.hljs-string{color:#93e4a4}.hljs-class,.hljs-property{color:#65e7d1}.hljs-name,.hljs-selector-tag{color:#30c2d8}.hljs-built_in,.hljs-keyword{color:#5fb8f2}.hljs-bullet,.hljs-section{color:#90aafa}.hljs-selector-pseudo{color:#ba9cef}.hljs-attr,.hljs-attribute,.hljs-params,.hljs-variable{color:#d991d2}.hljs-link,.hljs-symbol{color:#ec8dab}.hljs-literal,.hljs-strong,.hljs-title{font-weight:700}.hljs-emphasis{font-style:italic}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#eee;color:#000}.hljs-addition,.hljs-attribute,.hljs-emphasis,.hljs-link{color:#070}.hljs-emphasis{font-style:italic}.hljs-deletion,.hljs-string,.hljs-strong{color:#d14}.hljs-strong{font-weight:700}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-section,.hljs-title{color:#900}.hljs-class .hljs-title,.hljs-title.class_,.hljs-type{color:#458}.hljs-template-variable,.hljs-variable{color:#369}.hljs-bullet{color:#970}.hljs-meta{color:#34b}.hljs-code,.hljs-keyword,.hljs-literal,.hljs-number,.hljs-selector-tag{color:#099}.hljs-regexp{background-color:#fff0ff;color:#808}.hljs-symbol{color:#990073}.hljs-name,.hljs-selector-class,.hljs-selector-id,.hljs-tag{color:#070}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
||||
Theme: GitHub Dark Dimmed
|
||||
Description: Dark dimmed theme as seen on github.com
|
||||
Author: github.com
|
||||
Maintainer: @Hirse
|
||||
Updated: 2021-05-15
|
||||
|
||||
Colors taken from GitHub's CSS
|
||||
*/.hljs{color:#adbac7;background:#22272e}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#f47067}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#dcbdfb}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#6cb6ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#96d0ff}.hljs-built_in,.hljs-symbol{color:#f69d50}.hljs-code,.hljs-comment,.hljs-formula{color:#768390}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#8ddb8c}.hljs-subst{color:#adbac7}.hljs-section{color:#316dca;font-weight:700}.hljs-bullet{color:#eac55f}.hljs-emphasis{color:#adbac7;font-style:italic}.hljs-strong{color:#adbac7;font-weight:700}.hljs-addition{color:#b4f1b4;background-color:#1b4721}.hljs-deletion{color:#ffd8d3;background-color:#78191b}
|
||||
@@ -0,0 +1,10 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
||||
Theme: GitHub Dark
|
||||
Description: Dark theme as seen on github.com
|
||||
Author: github.com
|
||||
Maintainer: @Hirse
|
||||
Updated: 2021-05-15
|
||||
|
||||
Outdated base version: https://github.com/primer/github-syntax-dark
|
||||
Current colors taken from GitHub's CSS
|
||||
*/.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#79c0ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-code,.hljs-comment,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}
|
||||
@@ -0,0 +1,10 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
||||
Theme: GitHub
|
||||
Description: Light theme as seen on github.com
|
||||
Author: github.com
|
||||
Maintainer: @Hirse
|
||||
Updated: 2021-05-15
|
||||
|
||||
Outdated base version: https://github.com/primer/github-syntax-light
|
||||
Current colors taken from GitHub's CSS
|
||||
*/.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#005cc5}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-code,.hljs-comment,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#222;color:silver}.hljs-keyword{color:#ffb871;font-weight:700}.hljs-built_in{color:#ffb871}.hljs-literal{color:#ff8080}.hljs-symbol{color:#58e55a}.hljs-comment{color:#5b995b}.hljs-string{color:#ff0}.hljs-number{color:#ff8080}.hljs-addition,.hljs-attribute,.hljs-bullet,.hljs-code,.hljs-deletion,.hljs-doctag,.hljs-function,.hljs-link,.hljs-meta,.hljs-meta .hljs-keyword,.hljs-name,.hljs-quote,.hljs-regexp,.hljs-section,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-selector-tag,.hljs-subst,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-type,.hljs-variable{color:silver}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#fff;color:#000}.hljs-comment,.hljs-quote{color:#800}.hljs-keyword,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-title{color:#008}.hljs-template-variable,.hljs-variable{color:#660}.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-string{color:#080}.hljs-bullet,.hljs-link,.hljs-literal,.hljs-meta,.hljs-number,.hljs-symbol{color:#066}.hljs-attr,.hljs-built_in,.hljs-doctag,.hljs-params,.hljs-title,.hljs-type{color:#606}.hljs-attribute,.hljs-subst{color:#000}.hljs-formula{background-color:#eee;font-style:italic}.hljs-selector-class,.hljs-selector-id{color:#9b703f}.hljs-addition{background-color:#baeeba}.hljs-deletion{background-color:#ffc8bd}.hljs-doctag,.hljs-strong{font-weight:700}.hljs-emphasis{font-style:italic}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background-color:#652487;background-image:linear-gradient(160deg,#652487 0,#443ac3 35%,#0174b7 68%,#04988e 100%);color:#e7e4eb}.hljs-subtr{color:#e7e4eb}.hljs-comment,.hljs-doctag,.hljs-meta,.hljs-quote{color:#af8dd9}.hljs-attr,.hljs-regexp,.hljs-selector-id,.hljs-selector-tag,.hljs-tag,.hljs-template-tag{color:#aefbff}.hljs-bullet,.hljs-params,.hljs-selector-class{color:#f19fff}.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-section,.hljs-symbol,.hljs-type{color:#17fc95}.hljs-addition,.hljs-link,.hljs-number{color:#c5fe00}.hljs-string{color:#38c0ff}.hljs-addition,.hljs-attribute{color:#e7ff9f}.hljs-template-variable,.hljs-variable{color:#e447ff}.hljs-built_in,.hljs-class,.hljs-formula,.hljs-function,.hljs-name,.hljs-title{color:#ffc800}.hljs-deletion,.hljs-literal,.hljs-selector-pseudo{color:#ff9e44}.hljs-emphasis,.hljs-quote{font-style:italic}.hljs-keyword,.hljs-params,.hljs-section,.hljs-selector-class,.hljs-selector-id,.hljs-selector-tag,.hljs-strong,.hljs-template-tag{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background-color:#f9ccff;background-image:linear-gradient(295deg,#f9ccff 0,#e6bbf9 11%,#9ec6f9 32%,#55e6ee 60%,#91f5d1 74%,#f9ffbf 98%);color:#250482}.hljs-subtr{color:#01958b}.hljs-comment,.hljs-doctag,.hljs-meta,.hljs-quote{color:#cb7200}.hljs-attr,.hljs-regexp,.hljs-selector-id,.hljs-selector-tag,.hljs-tag,.hljs-template-tag{color:#07bd5f}.hljs-bullet,.hljs-params,.hljs-selector-class{color:#43449f}.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-section,.hljs-symbol,.hljs-type{color:#7d2801}.hljs-addition,.hljs-link,.hljs-number{color:#7f0096}.hljs-string{color:#2681ab}.hljs-addition,.hljs-attribute{color:#296562}.hljs-template-variable,.hljs-variable{color:#025c8f}.hljs-built_in,.hljs-class,.hljs-formula,.hljs-function,.hljs-name,.hljs-title{color:#529117}.hljs-deletion,.hljs-literal,.hljs-selector-pseudo{color:#ad13ff}.hljs-emphasis,.hljs-quote{font-style:italic}.hljs-keyword,.hljs-params,.hljs-section,.hljs-selector-class,.hljs-selector-id,.hljs-selector-tag,.hljs-strong,.hljs-template-tag{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#333;background:#fff}.hljs-comment,.hljs-quote{color:#777;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:700}.hljs-literal,.hljs-number{color:#777}.hljs-doctag,.hljs-formula,.hljs-string{color:#333;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAJ0lEQVQIW2O8e/fufwYGBgZBQUEQxcCIIfDu3Tuwivfv30NUoAsAALHpFMMLqZlPAAAAAElFTkSuQmCC)}.hljs-section,.hljs-selector-id,.hljs-title{color:#000;font-weight:700}.hljs-subst{font-weight:400}.hljs-class .hljs-title,.hljs-name,.hljs-title.class_,.hljs-type{color:#333;font-weight:700}.hljs-tag{color:#333}.hljs-regexp{color:#333;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAAPUlEQVQYV2NkQAN37979r6yszIgujiIAU4RNMVwhuiQ6H6wQl3XI4oy4FMHcCJPHcDS6J2A2EqUQpJhohQDexSef15DBCwAAAABJRU5ErkJggg==)}.hljs-bullet,.hljs-link,.hljs-symbol{color:#000;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAKElEQVQIW2NkQAO7d+/+z4gsBhJwdXVlhAvCBECKwIIwAbhKZBUwBQA6hBpm5efZsgAAAABJRU5ErkJggg==)}.hljs-built_in{color:#000;text-decoration:underline}.hljs-meta{color:#999;font-weight:700}.hljs-deletion{color:#fff;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAADCAYAAABS3WWCAAAAE0lEQVQIW2MMDQ39zzhz5kwIAQAyxweWgUHd1AAAAABJRU5ErkJggg==)}.hljs-addition{color:#000;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAALUlEQVQYV2N89+7dfwYk8P79ewZBQUFkIQZGOiu6e/cuiptQHAPl0NtNxAQBAM97Oejj3Dg7AAAAAElFTkSuQmCC)}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#1d1f21;color:#c5c8c6}.hljs span::selection,.hljs::selection{background:#373b41}.hljs span::-moz-selection,.hljs::-moz-selection{background:#373b41}.hljs-name,.hljs-title{color:#f0c674}.hljs-comment,.hljs-meta,.hljs-meta .hljs-keyword{color:#707880}.hljs-deletion,.hljs-link,.hljs-literal,.hljs-number,.hljs-symbol{color:#c66}.hljs-addition,.hljs-doctag,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-string{color:#b5bd68}.hljs-attribute,.hljs-code,.hljs-selector-id{color:#b294bb}.hljs-bullet,.hljs-keyword,.hljs-selector-tag,.hljs-tag{color:#81a2be}.hljs-subst,.hljs-template-tag,.hljs-template-variable,.hljs-variable{color:#8abeb7}.hljs-built_in,.hljs-quote,.hljs-section,.hljs-selector-class,.hljs-type{color:#de935f}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#000;background:#fff}.hljs-subst,.hljs-title{font-weight:400;color:#000}.hljs-comment,.hljs-quote{color:grey;font-style:italic}.hljs-meta{color:olive}.hljs-tag{background:#efefef}.hljs-keyword,.hljs-literal,.hljs-name,.hljs-section,.hljs-selector-class,.hljs-selector-id,.hljs-selector-tag,.hljs-type{font-weight:700;color:navy}.hljs-attribute,.hljs-link,.hljs-number,.hljs-regexp{font-weight:700;color:#00f}.hljs-link,.hljs-number,.hljs-regexp{font-weight:400}.hljs-string{color:green;font-weight:700}.hljs-bullet,.hljs-formula,.hljs-symbol{color:#000;background:#d0eded;font-style:italic}.hljs-doctag{text-decoration:underline}.hljs-template-variable,.hljs-variable{color:#660e7a}.hljs-addition{background:#baeeba}.hljs-deletion{background:#ffc8bd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#000;background:#fff}.hljs-subst,.hljs-title{font-weight:400;color:#000}.hljs-title.function_{color:#7a7a43}.hljs-code,.hljs-comment,.hljs-quote{color:#8c8c8c;font-style:italic}.hljs-meta{color:#9e880d}.hljs-section{color:#871094}.hljs-built_in,.hljs-keyword,.hljs-literal,.hljs-meta .hljs-keyword,.hljs-name,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-selector-tag,.hljs-symbol,.hljs-template-tag,.hljs-type,.hljs-variable.language_{color:#0033b3}.hljs-attr,.hljs-property{color:#871094}.hljs-attribute{color:#174ad4}.hljs-number{color:#1750eb}.hljs-regexp{color:#264eff}.hljs-link{text-decoration:underline;color:#006dcc}.hljs-meta .hljs-string,.hljs-string{color:#067d17}.hljs-char.escape_{color:#0037a6}.hljs-doctag{text-decoration:underline}.hljs-template-variable{color:#248f8f}.hljs-addition{background:#bee6be}.hljs-deletion{background:#d6d6d6}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#000;color:#f8f8f8}.hljs-comment,.hljs-meta,.hljs-quote{color:#7c7c7c}.hljs-keyword,.hljs-name,.hljs-selector-tag,.hljs-tag{color:#96cbfe}.hljs-attribute,.hljs-selector-id{color:#ffffb6}.hljs-addition,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-string{color:#a8ff60}.hljs-subst{color:#daefa3}.hljs-link,.hljs-regexp{color:#e9c062}.hljs-doctag,.hljs-section,.hljs-title,.hljs-type{color:#ffffb6}.hljs-bullet,.hljs-literal,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#c6c5fe}.hljs-deletion,.hljs-number{color:#ff73fd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#404040}.hljs,.hljs-subst{color:#f0f0f0}.hljs-comment{color:#b5b5b5;font-style:italic}.hljs-attribute,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-name,.hljs-selector-tag{color:#f0f0f0;font-weight:700}.hljs-string{color:#97bf0d}.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-template-tag,.hljs-type{color:#f0f0f0}.hljs-link,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#e2c696}.hljs-built_in,.hljs-literal{color:#97bf0d;font-weight:700}.hljs-addition,.hljs-bullet,.hljs-code{color:#397300}.hljs-class{color:#ce9d4d;font-weight:700}.hljs-section,.hljs-title{color:#df471e}.hljs-title>.hljs-built_in{color:#81bce9;font-weight:400}.hljs-meta{color:#1f7199}.hljs-meta .hljs-string{color:#4d99bf}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
+1
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#fff;color:#000}.hljs-subst{color:#000}.hljs-comment{color:#555;font-style:italic}.hljs-attribute,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-name,.hljs-selector-tag{color:#000;font-weight:700}.hljs-string{color:navy}.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-template-tag,.hljs-type{color:#000}.hljs-link,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#5e1700}.hljs-built_in,.hljs-literal{color:navy;font-weight:700}.hljs-addition,.hljs-bullet,.hljs-code{color:#397300}.hljs-class{color:#6f1c00;font-weight:700}.hljs-section,.hljs-title{color:#fb2c00}.hljs-title>.hljs-built_in{color:teal;font-weight:400}.hljs-meta{color:#1f7199}.hljs-meta .hljs-string{color:#4d99bf}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#221a0f;color:#d3af86}.hljs-comment,.hljs-quote{color:#d6baad}.hljs-meta,.hljs-name,.hljs-regexp,.hljs-selector-class,.hljs-selector-id,.hljs-tag,.hljs-template-variable,.hljs-variable{color:#dc3958}.hljs-built_in,.hljs-deletion,.hljs-link,.hljs-literal,.hljs-number,.hljs-params,.hljs-type{color:#f79a32}.hljs-addition,.hljs-bullet,.hljs-string,.hljs-symbol{color:#889b4a}.hljs-function,.hljs-keyword,.hljs-selector-tag{color:#98676a}.hljs-attribute,.hljs-section,.hljs-title{color:#f06431}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#fbebd4;color:#84613d}.hljs-comment,.hljs-quote{color:#a57a4c}.hljs-meta,.hljs-name,.hljs-regexp,.hljs-selector-class,.hljs-selector-id,.hljs-tag,.hljs-template-variable,.hljs-variable{color:#dc3958}.hljs-built_in,.hljs-deletion,.hljs-link,.hljs-literal,.hljs-number,.hljs-params,.hljs-type{color:#f79a32}.hljs-addition,.hljs-bullet,.hljs-string,.hljs-symbol{color:#889b4a}.hljs-function,.hljs-keyword,.hljs-selector-tag{color:#98676a}.hljs-attribute,.hljs-section,.hljs-title{color:#f06431}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#444;background:#fff}.hljs-name{color:#01a3a3}.hljs-meta,.hljs-tag{color:#789}.hljs-comment{color:#888}.hljs-attribute,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-name,.hljs-selector-tag{font-weight:700}.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-template-tag,.hljs-type{color:#4286f4}.hljs-section,.hljs-title{color:#4286f4;font-weight:700}.hljs-link,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#bc6060}.hljs-literal{color:#62bcbc}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-code{color:#25c6c6}.hljs-meta .hljs-string{color:#4d99bf}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#303030;color:#c5c8c6}.hljs-comment{color:#8d8d8d}.hljs-quote{color:#b3c7d8}.hljs-deletion,.hljs-name,.hljs-regexp,.hljs-selector-class,.hljs-selector-id,.hljs-tag,.hljs-template-variable,.hljs-variable{color:#c66}.hljs-built_in,.hljs-literal,.hljs-number,.hljs-subst .hljs-link,.hljs-type{color:#de935f}.hljs-attribute{color:#f0c674}.hljs-addition,.hljs-bullet,.hljs-params,.hljs-string{color:#b5bd68}.hljs-class,.hljs-function,.hljs-keyword,.hljs-selector-tag{color:#be94bb}.hljs-meta,.hljs-section,.hljs-title{color:#81a2be}.hljs-symbol{color:#dbc4d9}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background-color:#f4f4f4;color:#000}.hljs-subst{color:#000}.hljs-addition,.hljs-attribute,.hljs-bullet,.hljs-string,.hljs-symbol,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-variable{color:#050}.hljs-comment,.hljs-quote{color:#777}.hljs-link,.hljs-literal,.hljs-number,.hljs-regexp,.hljs-type{color:#800}.hljs-deletion,.hljs-meta{color:#00e}.hljs-built_in,.hljs-doctag,.hljs-keyword,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-tag,.hljs-title{font-weight:700;color:navy}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#eaeef3;color:#00193a}.hljs-doctag,.hljs-keyword,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-strong,.hljs-title{font-weight:700}.hljs-comment{color:#738191}.hljs-addition,.hljs-built_in,.hljs-literal,.hljs-name,.hljs-quote,.hljs-section,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-tag,.hljs-title,.hljs-type{color:#0048ab}.hljs-attribute,.hljs-bullet,.hljs-deletion,.hljs-link,.hljs-meta,.hljs-regexp,.hljs-subst,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#4c81c9}.hljs-emphasis{font-style:italic}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#23241f;color:#f8f8f2}.hljs-subst,.hljs-tag{color:#f8f8f2}.hljs-emphasis,.hljs-strong{color:#a8a8a2}.hljs-bullet,.hljs-link,.hljs-literal,.hljs-number,.hljs-quote,.hljs-regexp{color:#ae81ff}.hljs-code,.hljs-section,.hljs-selector-class,.hljs-title{color:#a6e22e}.hljs-strong{font-weight:700}.hljs-emphasis{font-style:italic}.hljs-attr,.hljs-keyword,.hljs-name,.hljs-selector-tag{color:#f92672}.hljs-attribute,.hljs-symbol{color:#66d9ef}.hljs-class .hljs-title,.hljs-params,.hljs-title.class_{color:#f8f8f2}.hljs-addition,.hljs-built_in,.hljs-selector-attr,.hljs-selector-id,.hljs-selector-pseudo,.hljs-string,.hljs-template-variable,.hljs-type,.hljs-variable{color:#e6db74}.hljs-comment,.hljs-deletion,.hljs-meta{color:#75715e}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#272822;color:#ddd}.hljs-keyword,.hljs-literal,.hljs-name,.hljs-number,.hljs-selector-tag,.hljs-strong,.hljs-tag{color:#f92672}.hljs-code{color:#66d9ef}.hljs-attr,.hljs-attribute,.hljs-link,.hljs-regexp,.hljs-symbol{color:#bf79db}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-emphasis,.hljs-section,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-string,.hljs-subst,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-type,.hljs-variable{color:#a6e22e}.hljs-class .hljs-title,.hljs-title.class_{color:#fff}.hljs-comment,.hljs-deletion,.hljs-meta,.hljs-quote{color:#75715e}.hljs-doctag,.hljs-keyword,.hljs-literal,.hljs-section,.hljs-selector-id,.hljs-selector-tag,.hljs-title,.hljs-type{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#011627;color:#d6deeb}.hljs-keyword{color:#c792ea;font-style:italic}.hljs-built_in{color:#addb67;font-style:italic}.hljs-type{color:#82aaff}.hljs-literal{color:#ff5874}.hljs-number{color:#f78c6c}.hljs-regexp{color:#5ca7e4}.hljs-string{color:#ecc48d}.hljs-subst{color:#d3423e}.hljs-symbol{color:#82aaff}.hljs-class{color:#ffcb8b}.hljs-function{color:#82aaff}.hljs-title{color:#dcdcaa;font-style:italic}.hljs-params{color:#7fdbca}.hljs-comment{color:#637777;font-style:italic}.hljs-doctag{color:#7fdbca}.hljs-meta,.hljs-meta .hljs-keyword{color:#82aaff}.hljs-meta .hljs-string{color:#ecc48d}.hljs-section{color:#82b1ff}.hljs-attr,.hljs-name,.hljs-tag{color:#7fdbca}.hljs-attribute{color:#80cbc4}.hljs-variable{color:#addb67}.hljs-bullet{color:#d9f5dd}.hljs-code{color:#80cbc4}.hljs-emphasis{color:#c792ea;font-style:italic}.hljs-strong{color:#addb67;font-weight:700}.hljs-formula{color:#c792ea}.hljs-link{color:#ff869a}.hljs-quote{color:#697098;font-style:italic}.hljs-selector-tag{color:#ff6363}.hljs-selector-id{color:#fad430}.hljs-selector-class{color:#addb67;font-style:italic}.hljs-selector-attr,.hljs-selector-pseudo{color:#c792ea;font-style:italic}.hljs-template-tag{color:#c792ea}.hljs-template-variable{color:#addb67}.hljs-addition{color:#addb67ff;font-style:italic}.hljs-deletion{color:#ef535090;font-style:italic}
|
||||
@@ -0,0 +1,10 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
||||
Theme: nnfx dark
|
||||
Description: a theme inspired by Netscape Navigator/Firefox
|
||||
Author: (c) 2020-2021 Jim Mason <jmason@ibinx.com>
|
||||
Maintainer: @RocketMan
|
||||
License: https://creativecommons.org/licenses/by-sa/4.0 CC BY-SA 4.0
|
||||
Updated: 2021-05-17
|
||||
|
||||
@version 1.1.0
|
||||
*/.hljs{background:#333;color:#fff}.language-xml .hljs-meta,.language-xml .hljs-meta-string{font-weight:700;font-style:italic;color:#69f}.hljs-comment,.hljs-quote{font-style:italic;color:#9c6}.hljs-built_in,.hljs-keyword,.hljs-name{color:#a7a}.hljs-attr,.hljs-name{font-weight:700}.hljs-string{font-weight:400}.hljs-code,.hljs-link,.hljs-meta .hljs-string,.hljs-number,.hljs-regexp,.hljs-string{color:#bce}.hljs-bullet,.hljs-symbol,.hljs-template-variable,.hljs-title,.hljs-variable{color:#d40}.hljs-class .hljs-title,.hljs-title.class_,.hljs-type{font-weight:700;color:#96c}.hljs-attr,.hljs-function .hljs-title,.hljs-subst,.hljs-tag,.hljs-title.function_{color:#fff}.hljs-formula{background-color:#eee;font-style:italic}.hljs-addition{background-color:#797}.hljs-deletion{background-color:#c99}.hljs-meta{color:#69f}.hljs-section,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-selector-tag{font-weight:700;color:#69f}.hljs-selector-pseudo{font-style:italic}.hljs-doctag,.hljs-strong{font-weight:700}.hljs-emphasis{font-style:italic}
|
||||
@@ -0,0 +1,10 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
||||
Theme: nnfx light
|
||||
Description: a theme inspired by Netscape Navigator/Firefox
|
||||
Author: (c) 2020-2021 Jim Mason <jmason@ibinx.com>
|
||||
Maintainer: @RocketMan
|
||||
License: https://creativecommons.org/licenses/by-sa/4.0 CC BY-SA 4.0
|
||||
Updated: 2021-05-17
|
||||
|
||||
@version 1.1.0
|
||||
*/.hljs{background:#fff;color:#000}.language-xml .hljs-meta,.language-xml .hljs-meta-string{font-weight:700;font-style:italic;color:#48b}.hljs-comment,.hljs-quote{font-style:italic;color:#070}.hljs-built_in,.hljs-keyword,.hljs-name{color:#808}.hljs-attr,.hljs-name{font-weight:700}.hljs-string{font-weight:400}.hljs-code,.hljs-link,.hljs-meta .hljs-string,.hljs-number,.hljs-regexp,.hljs-string{color:#00f}.hljs-bullet,.hljs-symbol,.hljs-template-variable,.hljs-title,.hljs-variable{color:#f40}.hljs-class .hljs-title,.hljs-title.class_,.hljs-type{font-weight:700;color:#639}.hljs-attr,.hljs-function .hljs-title,.hljs-subst,.hljs-tag,.hljs-title.function_{color:#000}.hljs-formula{background-color:#eee;font-style:italic}.hljs-addition{background-color:#beb}.hljs-deletion{background-color:#fbb}.hljs-meta{color:#269}.hljs-section,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-selector-tag{font-weight:700;color:#48b}.hljs-selector-pseudo{font-style:italic}.hljs-doctag,.hljs-strong{font-weight:700}.hljs-emphasis{font-style:italic}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#2e3440}.hljs,.hljs-subst{color:#d8dee9}.hljs-selector-tag{color:#81a1c1}.hljs-selector-id{color:#8fbcbb;font-weight:700}.hljs-selector-attr,.hljs-selector-class{color:#8fbcbb}.hljs-property,.hljs-selector-pseudo{color:#88c0d0}.hljs-addition{background-color:rgba(163,190,140,.5)}.hljs-deletion{background-color:rgba(191,97,106,.5)}.hljs-built_in,.hljs-class,.hljs-type{color:#8fbcbb}.hljs-function,.hljs-function>.hljs-title,.hljs-title.hljs-function{color:#88c0d0}.hljs-keyword,.hljs-literal,.hljs-symbol{color:#81a1c1}.hljs-number{color:#b48ead}.hljs-regexp{color:#ebcb8b}.hljs-string{color:#a3be8c}.hljs-title{color:#8fbcbb}.hljs-params{color:#d8dee9}.hljs-bullet{color:#81a1c1}.hljs-code{color:#8fbcbb}.hljs-emphasis{font-style:italic}.hljs-formula{color:#8fbcbb}.hljs-strong{font-weight:700}.hljs-link:hover{text-decoration:underline}.hljs-comment,.hljs-quote{color:#4c566a}.hljs-doctag{color:#8fbcbb}.hljs-meta,.hljs-meta .hljs-keyword{color:#5e81ac}.hljs-meta .hljs-string{color:#a3be8c}.hljs-attr{color:#8fbcbb}.hljs-attribute{color:#d8dee9}.hljs-name{color:#81a1c1}.hljs-section{color:#88c0d0}.hljs-tag{color:#81a1c1}.hljs-template-variable,.hljs-variable{color:#d8dee9}.hljs-template-tag{color:#5e81ac}.language-abnf .hljs-attribute{color:#88c0d0}.language-abnf .hljs-symbol{color:#ebcb8b}.language-apache .hljs-attribute{color:#88c0d0}.language-apache .hljs-section{color:#81a1c1}.language-arduino .hljs-built_in{color:#88c0d0}.language-aspectj .hljs-meta{color:#d08770}.language-aspectj>.hljs-title{color:#88c0d0}.language-bnf .hljs-attribute{color:#8fbcbb}.language-clojure .hljs-name{color:#88c0d0}.language-clojure .hljs-symbol{color:#ebcb8b}.language-coq .hljs-built_in{color:#88c0d0}.language-cpp .hljs-meta .hljs-string{color:#8fbcbb}.language-css .hljs-built_in{color:#88c0d0}.language-css .hljs-keyword{color:#d08770}.language-diff .hljs-meta,.language-ebnf .hljs-attribute{color:#8fbcbb}.language-glsl .hljs-built_in{color:#88c0d0}.language-groovy .hljs-meta:not(:first-child),.language-haxe .hljs-meta,.language-java .hljs-meta{color:#d08770}.language-ldif .hljs-attribute{color:#8fbcbb}.language-lisp .hljs-name,.language-lua .hljs-built_in,.language-moonscript .hljs-built_in,.language-nginx .hljs-attribute{color:#88c0d0}.language-nginx .hljs-section{color:#5e81ac}.language-pf .hljs-built_in,.language-processing .hljs-built_in{color:#88c0d0}.language-scss .hljs-keyword,.language-stylus .hljs-keyword{color:#81a1c1}.language-swift .hljs-meta{color:#d08770}.language-vim .hljs-built_in{color:#88c0d0;font-style:italic}.language-yaml .hljs-meta{color:#d08770}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#e0e2e4;background:#282b2e}.hljs-keyword,.hljs-literal,.hljs-selector-id,.hljs-selector-tag{color:#93c763}.hljs-number{color:#ffcd22}.hljs-attribute{color:#668bb0}.hljs-link,.hljs-regexp{color:#d39745}.hljs-meta{color:#557182}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-emphasis,.hljs-name,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-subst,.hljs-tag,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable{color:#8cbbad}.hljs-string,.hljs-symbol{color:#ec7600}.hljs-comment,.hljs-deletion,.hljs-quote{color:#818e96}.hljs-selector-class{color:#a082bd}.hljs-doctag,.hljs-keyword,.hljs-literal,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-strong,.hljs-title,.hljs-type{font-weight:700}.hljs-class .hljs-title,.hljs-code,.hljs-section,.hljs-title.class_{color:#fff}
|
||||
+1
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#e6e6e6;background:#2a2c2d}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}.hljs-comment,.hljs-quote{color:#bbb;font-style:italic}.hljs-params{color:#bbb}.hljs-attr,.hljs-punctuation{color:#e6e6e6}.hljs-meta,.hljs-name,.hljs-selector-tag{color:#ff4b82}.hljs-char.escape_,.hljs-operator{color:#b084eb}.hljs-deletion,.hljs-keyword{color:#ff75b5}.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-variable.language_{color:#ff9ac1}.hljs-code,.hljs-formula,.hljs-property,.hljs-section,.hljs-subst,.hljs-title.function_{color:#45a9f9}.hljs-addition,.hljs-bullet,.hljs-meta .hljs-string,.hljs-selector-class,.hljs-string,.hljs-symbol,.hljs-title.class_,.hljs-title.class_.inherited__{color:#19f9d8}.hljs-attribute,.hljs-built_in,.hljs-doctag,.hljs-link,.hljs-literal,.hljs-meta .hljs-keyword,.hljs-number,.hljs-punctuation,.hljs-selector-id,.hljs-tag,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-type,.hljs-variable{color:#ffb86c}
|
||||
+1
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#2a2c2d;background:#e6e6e6}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}.hljs-comment,.hljs-quote{color:#676b79;font-style:italic}.hljs-params{color:#676b79}.hljs-attr,.hljs-punctuation{color:#2a2c2d}.hljs-char.escape_,.hljs-meta,.hljs-name,.hljs-operator,.hljs-selector-tag{color:#c56200}.hljs-deletion,.hljs-keyword{color:#d92792}.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-variable.language_{color:#cc5e91}.hljs-code,.hljs-formula,.hljs-property,.hljs-section,.hljs-subst,.hljs-title.function_{color:#3787c7}.hljs-addition,.hljs-bullet,.hljs-meta .hljs-string,.hljs-selector-class,.hljs-string,.hljs-symbol,.hljs-title.class_,.hljs-title.class_.inherited__{color:#0d7d6c}.hljs-attribute,.hljs-built_in,.hljs-doctag,.hljs-link,.hljs-literal,.hljs-meta .hljs-keyword,.hljs-number,.hljs-selector-id,.hljs-tag,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-type,.hljs-variable{color:#7641bb}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#2f1e2e;color:#a39e9b}.hljs-comment,.hljs-quote{color:#8d8687}.hljs-link,.hljs-meta,.hljs-name,.hljs-regexp,.hljs-selector-class,.hljs-selector-id,.hljs-tag,.hljs-template-variable,.hljs-variable{color:#ef6155}.hljs-built_in,.hljs-deletion,.hljs-literal,.hljs-number,.hljs-params,.hljs-type{color:#f99b15}.hljs-attribute,.hljs-section,.hljs-title{color:#fec418}.hljs-addition,.hljs-bullet,.hljs-string,.hljs-symbol{color:#48b685}.hljs-keyword,.hljs-selector-tag{color:#815ba4}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#e7e9db;color:#4f424c}.hljs-comment,.hljs-quote{color:#776e71}.hljs-link,.hljs-meta,.hljs-name,.hljs-regexp,.hljs-selector-class,.hljs-selector-id,.hljs-tag,.hljs-template-variable,.hljs-variable{color:#ef6155}.hljs-built_in,.hljs-deletion,.hljs-literal,.hljs-number,.hljs-params,.hljs-type{color:#f99b15}.hljs-attribute,.hljs-section,.hljs-title{color:#fec418}.hljs-addition,.hljs-bullet,.hljs-string,.hljs-symbol{color:#48b685}.hljs-keyword,.hljs-selector-tag{color:#815ba4}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#dccf8f;background:url(./pojoaque.jpg) left top #181914}.hljs-comment,.hljs-quote{color:#586e75;font-style:italic}.hljs-addition,.hljs-keyword,.hljs-literal,.hljs-selector-tag{color:#b64926}.hljs-doctag,.hljs-number,.hljs-regexp,.hljs-string{color:#468966}.hljs-built_in,.hljs-name,.hljs-section,.hljs-title{color:#ffb03b}.hljs-class .hljs-title,.hljs-tag,.hljs-template-variable,.hljs-title.class_,.hljs-type,.hljs-variable{color:#b58900}.hljs-attribute{color:#b89859}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-subst,.hljs-symbol{color:#cb4b16}.hljs-deletion{color:#dc322f}.hljs-selector-class,.hljs-selector-id{color:#d3a60c}.hljs-formula{background:#073642}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#ffffdf}.hljs,.hljs-attr,.hljs-function,.hljs-name,.hljs-number,.hljs-params,.hljs-subst,.hljs-type{color:#000}.hljs-addition,.hljs-comment,.hljs-regexp,.hljs-section,.hljs-selector-pseudo{color:#0aa}.hljs-built_in,.hljs-class,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-selector-class{color:#066;font-weight:700}.hljs-code,.hljs-tag,.hljs-title,.hljs-variable{color:#066}.hljs-selector-attr,.hljs-string{color:#0080ff}.hljs-attribute,.hljs-deletion,.hljs-link,.hljs-symbol{color:#924b72}.hljs-literal,.hljs-meta,.hljs-selector-id{color:#924b72;font-weight:700}.hljs-name,.hljs-strong{font-weight:700}.hljs-emphasis{font-style:italic}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#aaa;background:#000}.hljs-emphasis,.hljs-strong{color:#a8a8a2}.hljs-bullet,.hljs-literal,.hljs-number,.hljs-quote,.hljs-regexp{color:#f5f}.hljs-code .hljs-selector-class{color:#aaf}.hljs-emphasis,.hljs-stronge,.hljs-type{font-style:italic}.hljs-function,.hljs-keyword,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-symbol{color:#ff5}.hljs-subst,.hljs-tag,.hljs-title{color:#aaa}.hljs-attribute{color:#f55}.hljs-class .hljs-title,.hljs-params,.hljs-title.class_,.hljs-variable{color:#88f}.hljs-addition,.hljs-built_in,.hljs-link,.hljs-selector-attr,.hljs-selector-id,.hljs-selector-pseudo,.hljs-string,.hljs-template-tag,.hljs-template-variable,.hljs-type{color:#f5f}.hljs-comment,.hljs-deletion,.hljs-meta{color:#5ff}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#000;background:#fff}.hljs-emphasis,.hljs-strong{color:#000}.hljs-bullet,.hljs-literal,.hljs-number,.hljs-quote,.hljs-regexp{color:navy}.hljs-code .hljs-selector-class{color:purple}.hljs-emphasis,.hljs-stronge,.hljs-type{font-style:italic}.hljs-function,.hljs-keyword,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-symbol{color:olive}.hljs-subst,.hljs-tag,.hljs-title{color:#000}.hljs-attribute{color:maroon}.hljs-class .hljs-title,.hljs-params,.hljs-title.class_,.hljs-variable{color:#0055af}.hljs-addition,.hljs-built_in,.hljs-comment,.hljs-deletion,.hljs-link,.hljs-meta,.hljs-selector-attr,.hljs-selector-id,.hljs-selector-pseudo,.hljs-string,.hljs-template-tag,.hljs-template-variable,.hljs-type{color:green}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#474949;color:#d1d9e1}.hljs-comment,.hljs-quote{color:#969896;font-style:italic}.hljs-addition,.hljs-keyword,.hljs-literal,.hljs-selector-tag,.hljs-type{color:#c9c}.hljs-number,.hljs-selector-attr,.hljs-selector-pseudo{color:#f99157}.hljs-doctag,.hljs-regexp,.hljs-string{color:#8abeb7}.hljs-built_in,.hljs-name,.hljs-section,.hljs-title{color:#b5bd68}.hljs-class .hljs-title,.hljs-selector-id,.hljs-template-variable,.hljs-title.class_,.hljs-variable{color:#fc6}.hljs-name,.hljs-section,.hljs-strong{font-weight:700}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-subst,.hljs-symbol{color:#f99157}.hljs-deletion{color:#dc322f}.hljs-formula{background:#eee8d5}.hljs-attr,.hljs-attribute{color:#81a2be}.hljs-emphasis{font-style:italic}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#444;background:#f0f0f0}.hljs-subst{color:#444}.hljs-comment{color:#888}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-name,.hljs-selector-tag{font-weight:700}.hljs-attribute{color:#0e9a00}.hljs-function{color:#99069a}.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-template-tag,.hljs-type{color:#800}.hljs-section,.hljs-title{color:#800;font-weight:700}.hljs-link,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#bc6060}.hljs-literal{color:#78a960}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-code{color:#0c9a9a}.hljs-meta{color:#1f7199}.hljs-meta .hljs-string{color:#4d99bf}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#3e5915;background:#f6f5b2}.hljs-keyword,.hljs-literal,.hljs-selector-tag{color:#059}.hljs-subst{color:#3e5915}.hljs-addition,.hljs-attribute,.hljs-built_in,.hljs-bullet,.hljs-link,.hljs-section,.hljs-string,.hljs-symbol,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-type,.hljs-variable{color:#2c009f}.hljs-comment,.hljs-deletion,.hljs-meta,.hljs-quote{color:#e60415}.hljs-doctag,.hljs-keyword,.hljs-literal,.hljs-name,.hljs-section,.hljs-selector-id,.hljs-selector-tag,.hljs-strong,.hljs-title,.hljs-type{font-weight:700}.hljs-emphasis{font-style:italic}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#2d2b57;color:#e3dfff;font-weight:400}.hljs-subst{color:#e3dfff}.hljs-title{color:#fad000;font-weight:400}.hljs-name{color:#a1feff}.hljs-tag{color:#fff}.hljs-attr{color:#f8d000;font-style:italic}.hljs-built_in,.hljs-keyword,.hljs-section,.hljs-selector-tag{color:#fb9e00}.hljs-addition,.hljs-attribute,.hljs-bullet,.hljs-code,.hljs-deletion,.hljs-quote,.hljs-regexp,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-string,.hljs-symbol,.hljs-template-tag{color:#4cd213}.hljs-meta,.hljs-meta .hljs-string{color:#fb9e00}.hljs-comment{color:#ac65ff}.hljs-keyword,.hljs-literal,.hljs-name,.hljs-selector-tag,.hljs-strong{font-weight:400}.hljs-literal,.hljs-number{color:#fa658d}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#1c1b19;color:#fce8c3}.hljs-literal,.hljs-quote,.hljs-subst{color:#fce8c3}.hljs-symbol,.hljs-type{color:#68a8e4}.hljs-deletion,.hljs-keyword{color:#ef2f27}.hljs-attribute,.hljs-function,.hljs-name,.hljs-section,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-title{color:#fbb829}.hljs-class,.hljs-code,.hljs-property,.hljs-template-variable,.hljs-variable{color:#0aaeb3}.hljs-addition,.hljs-bullet,.hljs-regexp,.hljs-string{color:#98bc37}.hljs-built_in,.hljs-params{color:#ff5c8f}.hljs-selector-tag,.hljs-template-tag{color:#2c78bf}.hljs-comment,.hljs-link,.hljs-meta,.hljs-number{color:#918175}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
||||
Theme: StackOverflow Dark
|
||||
Description: Dark theme as used on stackoverflow.com
|
||||
Author: stackoverflow.com
|
||||
Maintainer: @Hirse
|
||||
Website: https://github.com/StackExchange/Stacks
|
||||
License: MIT
|
||||
Updated: 2021-05-15
|
||||
|
||||
Updated for @stackoverflow/stacks v0.64.0
|
||||
Code Blocks: /blob/v0.64.0/lib/css/components/_stacks-code-blocks.less
|
||||
Colors: /blob/v0.64.0/lib/css/exports/_stacks-constants-colors.less
|
||||
*/.hljs{color:#fff;background:#1c1b1b}.hljs-subst{color:#fff}.hljs-comment{color:#999}.hljs-attr,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-section,.hljs-selector-tag{color:#88aece}.hljs-attribute{color:#c59bc1}.hljs-name,.hljs-number,.hljs-quote,.hljs-selector-id,.hljs-template-tag,.hljs-type{color:#f08d49}.hljs-selector-class{color:#88aece}.hljs-link,.hljs-regexp,.hljs-selector-attr,.hljs-string,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#b5bd68}.hljs-meta,.hljs-selector-pseudo{color:#88aece}.hljs-built_in,.hljs-literal,.hljs-title{color:#f08d49}.hljs-bullet,.hljs-code{color:#ccc}.hljs-meta .hljs-string{color:#b5bd68}.hljs-deletion{color:#de7176}.hljs-addition{color:#76c490}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
||||
Theme: StackOverflow Light
|
||||
Description: Light theme as used on stackoverflow.com
|
||||
Author: stackoverflow.com
|
||||
Maintainer: @Hirse
|
||||
Website: https://github.com/StackExchange/Stacks
|
||||
License: MIT
|
||||
Updated: 2021-05-15
|
||||
|
||||
Updated for @stackoverflow/stacks v0.64.0
|
||||
Code Blocks: /blob/v0.64.0/lib/css/components/_stacks-code-blocks.less
|
||||
Colors: /blob/v0.64.0/lib/css/exports/_stacks-constants-colors.less
|
||||
*/.hljs{color:#2f3337;background:#f6f6f6}.hljs-subst{color:#2f3337}.hljs-comment{color:#656e77}.hljs-attr,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-section,.hljs-selector-tag{color:#015692}.hljs-attribute{color:#803378}.hljs-name,.hljs-number,.hljs-quote,.hljs-selector-id,.hljs-template-tag,.hljs-type{color:#b75501}.hljs-selector-class{color:#015692}.hljs-link,.hljs-regexp,.hljs-selector-attr,.hljs-string,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#54790d}.hljs-meta,.hljs-selector-pseudo{color:#015692}.hljs-built_in,.hljs-literal,.hljs-title{color:#b75501}.hljs-bullet,.hljs-code{color:#535a60}.hljs-meta .hljs-string{color:#54790d}.hljs-deletion{color:#c02d2e}.hljs-addition{color:#2f6f44}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#000;color:#f8f8f8}.hljs-comment,.hljs-quote{color:#aeaeae;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-type{color:#e28964}.hljs-string{color:#65b042}.hljs-subst{color:#daefa3}.hljs-link,.hljs-regexp{color:#e9c062}.hljs-name,.hljs-section,.hljs-tag,.hljs-title{color:#89bdff}.hljs-class .hljs-title,.hljs-doctag,.hljs-title.class_{text-decoration:underline}.hljs-bullet,.hljs-number,.hljs-symbol{color:#3387cc}.hljs-params,.hljs-template-variable,.hljs-variable{color:#3e87e3}.hljs-attribute{color:#cda869}.hljs-meta{color:#8996a8}.hljs-formula{background-color:#0e2231;color:#f8f8f8;font-style:italic}.hljs-addition{background-color:#253b22;color:#f8f8f8}.hljs-deletion{background-color:#420e09;color:#f8f8f8}.hljs-selector-class{color:#9b703f}.hljs-selector-id{color:#8b98ab}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1,8 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
||||
Theme: Tokyo-night-Dark
|
||||
origin: https://github.com/enkia/tokyo-night-vscode-theme
|
||||
Description: Original highlight.js style
|
||||
Author: (c) Henri Vandersleyen <hvandersleyen@gmail.com>
|
||||
License: see project LICENSE
|
||||
Touched: 2022
|
||||
*/.hljs-comment,.hljs-meta{color:#565f89}.hljs-deletion,.hljs-doctag,.hljs-regexp,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-tag,.hljs-template-tag,.hljs-variable.language_{color:#f7768e}.hljs-link,.hljs-literal,.hljs-number,.hljs-params,.hljs-template-variable,.hljs-type,.hljs-variable{color:#ff9e64}.hljs-attribute,.hljs-built_in{color:#e0af68}.hljs-keyword,.hljs-property,.hljs-subst,.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#7dcfff}.hljs-selector-tag{color:#73daca}.hljs-addition,.hljs-bullet,.hljs-quote,.hljs-string,.hljs-symbol{color:#9ece6a}.hljs-code,.hljs-formula,.hljs-section{color:#7aa2f7}.hljs-attr,.hljs-char.escape_,.hljs-keyword,.hljs-name,.hljs-operator{color:#bb9af7}.hljs-punctuation{color:#c0caf5}.hljs{background:#1a1b26;color:#9aa5ce}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
||||
Theme: Tokyo-night-light
|
||||
origin: https://github.com/enkia/tokyo-night-vscode-theme
|
||||
Description: Original highlight.js style
|
||||
Author: (c) Henri Vandersleyen <hvandersleyen@gmail.com>
|
||||
License: see project LICENSE
|
||||
Touched: 2022
|
||||
*/.hljs-comment,.hljs-meta{color:#9699a3}.hljs-deletion,.hljs-doctag,.hljs-regexp,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-tag,.hljs-template-tag,.hljs-variable.language_{color:#8c4351}.hljs-link,.hljs-literal,.hljs-number,.hljs-params,.hljs-template-variable,.hljs-type,.hljs-variable{color:#965027}.hljs-attribute,.hljs-built_in{color:#8f5e15}.hljs-keyword,.hljs-property,.hljs-subst,.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#0f4b6e}.hljs-selector-tag{color:#33635c}.hljs-addition,.hljs-bullet,.hljs-quote,.hljs-string,.hljs-symbol{color:#485e30}.hljs-code,.hljs-formula,.hljs-section{color:#34548a}.hljs-attr,.hljs-char.escape_,.hljs-keyword,.hljs-name,.hljs-operator{color:#5a4a78}.hljs-punctuation{color:#343b58}.hljs{background:#d5d6db;color:#565a6e}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
+1
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs-comment,.hljs-quote{color:#7285b7}.hljs-deletion,.hljs-name,.hljs-regexp,.hljs-selector-class,.hljs-selector-id,.hljs-tag,.hljs-template-variable,.hljs-variable{color:#ff9da4}.hljs-built_in,.hljs-link,.hljs-literal,.hljs-meta,.hljs-number,.hljs-params,.hljs-type{color:#ffc58f}.hljs-attribute{color:#ffeead}.hljs-addition,.hljs-bullet,.hljs-string,.hljs-symbol{color:#d1f1a9}.hljs-section,.hljs-title{color:#bbdaff}.hljs-keyword,.hljs-selector-tag{color:#ebbbff}.hljs{background:#002451;color:#fff}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
+1
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs-comment,.hljs-quote{color:#969896}.hljs-deletion,.hljs-name,.hljs-regexp,.hljs-selector-class,.hljs-selector-id,.hljs-tag,.hljs-template-variable,.hljs-variable{color:#d54e53}.hljs-built_in,.hljs-link,.hljs-literal,.hljs-meta,.hljs-number,.hljs-params,.hljs-type{color:#e78c45}.hljs-attribute{color:#e7c547}.hljs-addition,.hljs-bullet,.hljs-string,.hljs-symbol{color:#b9ca4a}.hljs-section,.hljs-title{color:#7aa6da}.hljs-keyword,.hljs-selector-tag{color:#c397d8}.hljs{background:#000;color:#eaeaea}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#fff;color:#000}.hljs-comment,.hljs-quote,.hljs-variable{color:green}.hljs-built_in,.hljs-keyword,.hljs-name,.hljs-selector-tag,.hljs-tag{color:#00f}.hljs-addition,.hljs-attribute,.hljs-literal,.hljs-section,.hljs-string,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-type{color:#a31515}.hljs-deletion,.hljs-meta,.hljs-selector-attr,.hljs-selector-pseudo{color:#2b91af}.hljs-doctag{color:grey}.hljs-attr{color:red}.hljs-bullet,.hljs-link,.hljs-symbol{color:#00b0e8}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#1e1e1e;color:#dcdcdc}.hljs-keyword,.hljs-literal,.hljs-name,.hljs-symbol{color:#569cd6}.hljs-link{color:#569cd6;text-decoration:underline}.hljs-built_in,.hljs-type{color:#4ec9b0}.hljs-class,.hljs-number{color:#b8d7a3}.hljs-meta .hljs-string,.hljs-string{color:#d69d85}.hljs-regexp,.hljs-template-tag{color:#9a5334}.hljs-formula,.hljs-function,.hljs-params,.hljs-subst,.hljs-title{color:#dcdcdc}.hljs-comment,.hljs-quote{color:#57a64a;font-style:italic}.hljs-doctag{color:#608b4e}.hljs-meta,.hljs-meta .hljs-keyword,.hljs-tag{color:#9b9b9b}.hljs-template-variable,.hljs-variable{color:#bd63c5}.hljs-attr,.hljs-attribute{color:#9cdcfe}.hljs-section{color:gold}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-bullet,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-selector-tag{color:#d7ba7d}.hljs-addition{background-color:#144212;display:inline-block;width:100%}.hljs-deletion{background-color:#600;display:inline-block;width:100%}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#fff;color:#000}.xml .hljs-meta{color:silver}.hljs-comment,.hljs-quote{color:#007400}.hljs-attribute,.hljs-keyword,.hljs-literal,.hljs-name,.hljs-selector-tag,.hljs-tag{color:#aa0d91}.hljs-template-variable,.hljs-variable{color:#3f6e74}.hljs-code,.hljs-meta .hljs-string,.hljs-string{color:#c41a16}.hljs-link,.hljs-regexp{color:#0e0eff}.hljs-bullet,.hljs-number,.hljs-symbol,.hljs-title{color:#1c00cf}.hljs-meta,.hljs-section{color:#643820}.hljs-built_in,.hljs-class .hljs-title,.hljs-params,.hljs-title.class_,.hljs-type{color:#5c2699}.hljs-attr{color:#836c28}.hljs-subst{color:#000}.hljs-formula{background-color:#eee;font-style:italic}.hljs-addition{background-color:#baeeba}.hljs-deletion{background-color:#ffc8bd}.hljs-selector-class,.hljs-selector-id{color:#9b703f}.hljs-doctag,.hljs-strong{font-weight:700}.hljs-emphasis{font-style:italic}
|
||||
@@ -0,0 +1 @@
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#eaeaea;background:#000}.hljs-subst{color:#eaeaea}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-type{color:#eaeaea}.hljs-params{color:#da0000}.hljs-literal,.hljs-name,.hljs-number{color:red;font-weight:bolder}.hljs-comment{color:#969896}.hljs-quote,.hljs-selector-id{color:#0ff}.hljs-template-variable,.hljs-title,.hljs-variable{color:#0ff;font-weight:700}.hljs-keyword,.hljs-selector-class,.hljs-symbol{color:#fff000}.hljs-bullet,.hljs-string{color:#0f0}.hljs-section,.hljs-tag{color:#000fff}.hljs-selector-tag{color:#000fff;font-weight:700}.hljs-attribute,.hljs-built_in,.hljs-link,.hljs-regexp{color:#f0f}.hljs-meta{color:#fff;font-weight:bolder}
|
||||
@@ -0,0 +1,84 @@
|
||||
import type { StyleConfig } from "./types.js";
|
||||
|
||||
export const FONT_FAMILY_MAP: Record<string, string> = {
|
||||
sans: `-apple-system-font,BlinkMacSystemFont, Helvetica Neue, PingFang SC, Hiragino Sans GB , Microsoft YaHei UI , Microsoft YaHei ,Arial,sans-serif`,
|
||||
serif: `Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif`,
|
||||
"serif-cjk": `"Source Han Serif SC", "Noto Serif CJK SC", "Source Han Serif CN", STSong, SimSun, serif`,
|
||||
mono: `Menlo, Monaco, 'Courier New', monospace`,
|
||||
};
|
||||
|
||||
export const FONT_SIZE_OPTIONS = ["14px", "15px", "16px", "17px", "18px"];
|
||||
|
||||
export const COLOR_PRESETS: Record<string, string> = {
|
||||
blue: "#0F4C81",
|
||||
green: "#009874",
|
||||
vermilion: "#FA5151",
|
||||
yellow: "#FECE00",
|
||||
purple: "#92617E",
|
||||
sky: "#55C9EA",
|
||||
rose: "#B76E79",
|
||||
olive: "#556B2F",
|
||||
black: "#333333",
|
||||
gray: "#A9A9A9",
|
||||
pink: "#FFB7C5",
|
||||
red: "#A93226",
|
||||
orange: "#D97757",
|
||||
};
|
||||
|
||||
export const CODE_BLOCK_THEMES = [
|
||||
"1c-light", "a11y-dark", "a11y-light", "agate", "an-old-hope",
|
||||
"androidstudio", "arduino-light", "arta", "ascetic",
|
||||
"atom-one-dark-reasonable", "atom-one-dark", "atom-one-light",
|
||||
"brown-paper", "codepen-embed", "color-brewer", "dark", "default",
|
||||
"devibeans", "docco", "far", "felipec", "foundation",
|
||||
"github-dark-dimmed", "github-dark", "github", "gml", "googlecode",
|
||||
"gradient-dark", "gradient-light", "grayscale", "hybrid", "idea",
|
||||
"intellij-light", "ir-black", "isbl-editor-dark", "isbl-editor-light",
|
||||
"kimbie-dark", "kimbie-light", "lightfair", "lioshi", "magula",
|
||||
"mono-blue", "monokai-sublime", "monokai", "night-owl", "nnfx-dark",
|
||||
"nnfx-light", "nord", "obsidian", "panda-syntax-dark",
|
||||
"panda-syntax-light", "paraiso-dark", "paraiso-light", "pojoaque",
|
||||
"purebasic", "qtcreator-dark", "qtcreator-light", "rainbow", "routeros",
|
||||
"school-book", "shades-of-purple", "srcery", "stackoverflow-dark",
|
||||
"stackoverflow-light", "sunburst", "tokyo-night-dark", "tokyo-night-light",
|
||||
"tomorrow-night-blue", "tomorrow-night-bright", "vs", "vs2015", "xcode",
|
||||
"xt256",
|
||||
];
|
||||
|
||||
export const DEFAULT_STYLE: StyleConfig = {
|
||||
primaryColor: "#0F4C81",
|
||||
fontFamily: FONT_FAMILY_MAP.sans!,
|
||||
fontSize: "16px",
|
||||
foreground: "0 0% 3.9%",
|
||||
blockquoteBackground: "#f7f7f7",
|
||||
accentColor: "#6B7280",
|
||||
containerBg: "transparent",
|
||||
};
|
||||
|
||||
export const THEME_STYLE_DEFAULTS: Record<string, Partial<StyleConfig>> = {
|
||||
default: {
|
||||
primaryColor: COLOR_PRESETS.blue,
|
||||
},
|
||||
grace: {
|
||||
primaryColor: COLOR_PRESETS.purple,
|
||||
},
|
||||
simple: {
|
||||
primaryColor: COLOR_PRESETS.green,
|
||||
},
|
||||
modern: {
|
||||
primaryColor: COLOR_PRESETS.orange,
|
||||
accentColor: "#E4B1A0",
|
||||
containerBg: "rgba(250, 249, 245, 1)",
|
||||
fontFamily: FONT_FAMILY_MAP.sans,
|
||||
fontSize: "15px",
|
||||
blockquoteBackground: "rgba(255, 255, 255, 0.6)",
|
||||
},
|
||||
};
|
||||
|
||||
export const macCodeSvg = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" x="0px" y="0px" width="45px" height="13px" viewBox="0 0 450 130">
|
||||
<ellipse cx="50" cy="65" rx="50" ry="52" stroke="rgb(220,60,54)" stroke-width="2" fill="rgb(237,108,96)" />
|
||||
<ellipse cx="225" cy="65" rx="50" ry="52" stroke="rgb(218,151,33)" stroke-width="2" fill="rgb(247,193,81)" />
|
||||
<ellipse cx="400" cy="65" rx="50" ry="52" stroke="rgb(27,161,37)" stroke-width="2" fill="rgb(100,200,86)" />
|
||||
</svg>
|
||||
`.trim();
|
||||
@@ -0,0 +1,53 @@
|
||||
import fs from "node:fs";
|
||||
import { homedir } from "node:os";
|
||||
import path from "node:path";
|
||||
import type { ExtendConfig } from "./types.js";
|
||||
|
||||
function extractYamlFrontMatter(content: string): string | null {
|
||||
const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*$/m);
|
||||
return match ? match[1]! : null;
|
||||
}
|
||||
|
||||
function parseExtendYaml(yaml: string): Partial<ExtendConfig> {
|
||||
const config: Partial<ExtendConfig> = {};
|
||||
for (const line of yaml.split("\n")) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed || trimmed.startsWith("#")) continue;
|
||||
const colonIdx = trimmed.indexOf(":");
|
||||
if (colonIdx < 0) continue;
|
||||
const key = trimmed.slice(0, colonIdx).trim();
|
||||
let value = trimmed.slice(colonIdx + 1).trim().replace(/^['"]|['"]$/g, "");
|
||||
if (value === "null" || value === "") continue;
|
||||
|
||||
if (key === "default_theme") config.default_theme = value;
|
||||
else if (key === "default_color") config.default_color = value;
|
||||
else if (key === "default_font_family") config.default_font_family = value;
|
||||
else if (key === "default_font_size") config.default_font_size = value.endsWith("px") ? value : `${value}px`;
|
||||
else if (key === "default_code_theme") config.default_code_theme = value;
|
||||
else if (key === "mac_code_block") config.mac_code_block = value === "true";
|
||||
else if (key === "show_line_number") config.show_line_number = value === "true";
|
||||
else if (key === "cite") config.cite = value === "true";
|
||||
else if (key === "count") config.count = value === "true";
|
||||
else if (key === "legend") config.legend = value;
|
||||
else if (key === "keep_title") config.keep_title = value === "true";
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
export function loadExtendConfig(): Partial<ExtendConfig> {
|
||||
const paths = [
|
||||
path.join(process.cwd(), ".baoyu-skills", "baoyu-markdown-to-html", "EXTEND.md"),
|
||||
path.join(homedir(), ".baoyu-skills", "baoyu-markdown-to-html", "EXTEND.md"),
|
||||
];
|
||||
for (const p of paths) {
|
||||
try {
|
||||
const content = fs.readFileSync(p, "utf-8");
|
||||
const yaml = extractYamlFrontMatter(content);
|
||||
if (!yaml) continue;
|
||||
return parseExtendYaml(yaml);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import type { StyleConfig, HtmlDocumentMeta } from "./types.js";
|
||||
import { DEFAULT_STYLE } from "./constants.js";
|
||||
|
||||
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
||||
const CODE_THEMES_DIR = path.resolve(SCRIPT_DIR, "code-themes");
|
||||
|
||||
export function buildCss(baseCss: string, themeCss: string, style: StyleConfig = DEFAULT_STYLE): string {
|
||||
const variables = `
|
||||
:root {
|
||||
--md-primary-color: ${style.primaryColor};
|
||||
--md-font-family: ${style.fontFamily};
|
||||
--md-font-size: ${style.fontSize};
|
||||
--foreground: ${style.foreground};
|
||||
--blockquote-background: ${style.blockquoteBackground};
|
||||
--md-accent-color: ${style.accentColor};
|
||||
--md-container-bg: ${style.containerBg};
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 24px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
#output {
|
||||
max-width: 860px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
`.trim();
|
||||
|
||||
return [variables, baseCss, themeCss].join("\n\n");
|
||||
}
|
||||
|
||||
export function loadCodeThemeCss(themeName: string): string {
|
||||
const filePath = path.join(CODE_THEMES_DIR, `${themeName}.min.css`);
|
||||
try {
|
||||
return fs.readFileSync(filePath, "utf-8");
|
||||
} catch {
|
||||
console.error(`Code theme CSS not found: ${filePath}`);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export function buildHtmlDocument(meta: HtmlDocumentMeta, css: string, html: string, codeThemeCss?: string): string {
|
||||
const lines = [
|
||||
"<!doctype html>",
|
||||
"<html>",
|
||||
"<head>",
|
||||
' <meta charset="utf-8" />',
|
||||
' <meta name="viewport" content="width=device-width, initial-scale=1" />',
|
||||
` <title>${meta.title}</title>`,
|
||||
];
|
||||
if (meta.author) {
|
||||
lines.push(` <meta name="author" content="${meta.author}" />`);
|
||||
}
|
||||
if (meta.description) {
|
||||
lines.push(` <meta name="description" content="${meta.description}" />`);
|
||||
}
|
||||
lines.push(` <style>${css}</style>`);
|
||||
if (codeThemeCss) {
|
||||
lines.push(` <style>${codeThemeCss}</style>`);
|
||||
}
|
||||
lines.push(
|
||||
"</head>",
|
||||
"<body>",
|
||||
' <div id="output">',
|
||||
html,
|
||||
" </div>",
|
||||
"</body>",
|
||||
"</html>"
|
||||
);
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
export async function inlineCss(html: string): Promise<string> {
|
||||
try {
|
||||
const { default: juice } = await import("juice");
|
||||
return juice(html, {
|
||||
inlinePseudoElements: true,
|
||||
preserveImportant: true,
|
||||
resolveCSSVariables: false,
|
||||
});
|
||||
} catch (error) {
|
||||
const detail = error instanceof Error ? error.message : String(error);
|
||||
throw new Error(
|
||||
`Missing dependency "juice" for CSS inlining. Install it first (e.g. "bun add juice" or "npm add juice"). Original error: ${detail}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeCssText(cssText: string, style: StyleConfig = DEFAULT_STYLE): string {
|
||||
return cssText
|
||||
.replace(/var\(--md-primary-color\)/g, style.primaryColor)
|
||||
.replace(/var\(--md-font-family\)/g, style.fontFamily)
|
||||
.replace(/var\(--md-font-size\)/g, style.fontSize)
|
||||
.replace(/var\(--blockquote-background\)/g, style.blockquoteBackground)
|
||||
.replace(/var\(--md-accent-color\)/g, style.accentColor)
|
||||
.replace(/var\(--md-container-bg\)/g, style.containerBg)
|
||||
.replace(/hsl\(var\(--foreground\)\)/g, "#3f3f3f")
|
||||
.replace(/--md-primary-color:\s*[^;"']+;?/g, "")
|
||||
.replace(/--md-font-family:\s*[^;"']+;?/g, "")
|
||||
.replace(/--md-font-size:\s*[^;"']+;?/g, "")
|
||||
.replace(/--blockquote-background:\s*[^;"']+;?/g, "")
|
||||
.replace(/--md-accent-color:\s*[^;"']+;?/g, "")
|
||||
.replace(/--md-container-bg:\s*[^;"']+;?/g, "")
|
||||
.replace(/--foreground:\s*[^;"']+;?/g, "");
|
||||
}
|
||||
|
||||
export function normalizeInlineCss(html: string, style: StyleConfig = DEFAULT_STYLE): string {
|
||||
let output = html;
|
||||
output = output.replace(
|
||||
/<style([^>]*)>([\s\S]*?)<\/style>/gi,
|
||||
(_match, attrs: string, cssText: string) =>
|
||||
`<style${attrs}>${normalizeCssText(cssText, style)}</style>`
|
||||
);
|
||||
output = output.replace(
|
||||
/style="([^"]*)"/gi,
|
||||
(_match, cssText: string) => `style="${normalizeCssText(cssText, style)}"`
|
||||
);
|
||||
output = output.replace(
|
||||
/style='([^']*)'/gi,
|
||||
(_match, cssText: string) => `style='${normalizeCssText(cssText, style)}'`
|
||||
);
|
||||
return output;
|
||||
}
|
||||
|
||||
export function modifyHtmlStructure(htmlString: string): string {
|
||||
let output = htmlString;
|
||||
const pattern =
|
||||
/<li([^>]*)>([\s\S]*?)(<ul[\s\S]*?<\/ul>|<ol[\s\S]*?<\/ol>)<\/li>/i;
|
||||
while (pattern.test(output)) {
|
||||
output = output.replace(pattern, "<li$1>$2</li>$3");
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
export function removeFirstHeading(html: string): string {
|
||||
return html.replace(/<h[12][^>]*>[\s\S]*?<\/h[12]>/, "");
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,434 @@
|
||||
import frontMatter from "front-matter";
|
||||
import hljs from "highlight.js/lib/core";
|
||||
import { marked, type RendererObject, type Tokens } from "marked";
|
||||
import readingTime, { type ReadTimeResults } from "reading-time";
|
||||
import { unified } from "unified";
|
||||
import remarkParse from "remark-parse";
|
||||
import remarkCjkFriendly from "remark-cjk-friendly";
|
||||
import remarkStringify from "remark-stringify";
|
||||
|
||||
import {
|
||||
markedAlert,
|
||||
markedFootnotes,
|
||||
markedInfographic,
|
||||
markedMarkup,
|
||||
markedPlantUML,
|
||||
markedRuby,
|
||||
markedSlider,
|
||||
markedToc,
|
||||
MDKatex,
|
||||
} from "./extensions/index.js";
|
||||
import {
|
||||
COMMON_LANGUAGES,
|
||||
highlightAndFormatCode,
|
||||
} from "./utils/languages.js";
|
||||
import { macCodeSvg } from "./constants.js";
|
||||
import type { IOpts, ParseResult, RendererAPI } from "./types.js";
|
||||
|
||||
Object.entries(COMMON_LANGUAGES).forEach(([name, lang]) => {
|
||||
hljs.registerLanguage(name, lang);
|
||||
});
|
||||
|
||||
export { hljs };
|
||||
|
||||
marked.setOptions({
|
||||
breaks: true,
|
||||
});
|
||||
marked.use(markedSlider());
|
||||
|
||||
function escapeHtml(text: string): string {
|
||||
return text
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'")
|
||||
.replace(/`/g, "`");
|
||||
}
|
||||
|
||||
function buildAddition(): string {
|
||||
return `
|
||||
<style>
|
||||
.preview-wrapper pre::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
color: #ccc;
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
padding: 5px 10px 0;
|
||||
line-height: 15px;
|
||||
height: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
|
||||
function buildFootnoteArray(footnotes: [number, string, string][]): string {
|
||||
return footnotes
|
||||
.map(([index, title, link]) =>
|
||||
link === title
|
||||
? `<code style="font-size: 90%; opacity: 0.6;">[${index}]</code>: <i style="word-break: break-all">${title}</i><br/>`
|
||||
: `<code style="font-size: 90%; opacity: 0.6;">[${index}]</code> ${title}: <i style="word-break: break-all">${link}</i><br/>`
|
||||
)
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
function transform(legend: string, text: string | null, title: string | null): string {
|
||||
const options = legend.split("-");
|
||||
for (const option of options) {
|
||||
if (option === "alt" && text) {
|
||||
return text;
|
||||
}
|
||||
if (option === "title" && title) {
|
||||
return title;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function parseFrontMatterAndContent(markdownText: string): ParseResult {
|
||||
try {
|
||||
const parsed = frontMatter(markdownText);
|
||||
const yamlData = parsed.attributes;
|
||||
const markdownContent = parsed.body;
|
||||
const readingTimeResult = readingTime(markdownContent);
|
||||
return {
|
||||
yamlData: yamlData as Record<string, any>,
|
||||
markdownContent,
|
||||
readingTime: readingTimeResult,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error parsing front-matter:", error);
|
||||
return {
|
||||
yamlData: {},
|
||||
markdownContent: markdownText,
|
||||
readingTime: readingTime(markdownText),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function initRenderer(opts: IOpts = {}): RendererAPI {
|
||||
const footnotes: [number, string, string][] = [];
|
||||
let footnoteIndex = 0;
|
||||
let codeIndex = 0;
|
||||
const listOrderedStack: boolean[] = [];
|
||||
const listCounters: number[] = [];
|
||||
const isBrowser = typeof window !== "undefined";
|
||||
|
||||
function getOpts(): IOpts {
|
||||
return opts;
|
||||
}
|
||||
|
||||
function styledContent(styleLabel: string, content: string, tagName?: string): string {
|
||||
const tag = tagName ?? styleLabel;
|
||||
const className = `${styleLabel.replace(/_/g, "-")}`;
|
||||
const headingAttr = /^h\d$/.test(tag) ? " data-heading=\"true\"" : "";
|
||||
return `<${tag} class="${className}"${headingAttr}>${content}</${tag}>`;
|
||||
}
|
||||
|
||||
function addFootnote(title: string, link: string): number {
|
||||
const existingFootnote = footnotes.find(([, , existingLink]) => existingLink === link);
|
||||
if (existingFootnote) {
|
||||
return existingFootnote[0];
|
||||
}
|
||||
footnotes.push([++footnoteIndex, title, link]);
|
||||
return footnoteIndex;
|
||||
}
|
||||
|
||||
function reset(newOpts: Partial<IOpts>): void {
|
||||
footnotes.length = 0;
|
||||
footnoteIndex = 0;
|
||||
setOptions(newOpts);
|
||||
}
|
||||
|
||||
function setOptions(newOpts: Partial<IOpts>): void {
|
||||
opts = { ...opts, ...newOpts };
|
||||
marked.use(markedAlert());
|
||||
if (isBrowser) {
|
||||
marked.use(MDKatex({ nonStandard: true }, true));
|
||||
}
|
||||
marked.use(markedMarkup());
|
||||
marked.use(markedInfographic({ themeMode: opts.themeMode }));
|
||||
}
|
||||
|
||||
function buildReadingTime(readingTimeResult: ReadTimeResults): string {
|
||||
if (!opts.countStatus) {
|
||||
return "";
|
||||
}
|
||||
if (!readingTimeResult.words) {
|
||||
return "";
|
||||
}
|
||||
return `
|
||||
<blockquote class="md-blockquote">
|
||||
<p class="md-blockquote-p">字数 ${readingTimeResult?.words},阅读大约需 ${Math.ceil(readingTimeResult?.minutes)} 分钟</p>
|
||||
</blockquote>
|
||||
`;
|
||||
}
|
||||
|
||||
const buildFootnotes = () => {
|
||||
if (!footnotes.length) {
|
||||
return "";
|
||||
}
|
||||
return (
|
||||
styledContent("h4", "引用链接")
|
||||
+ styledContent("footnotes", buildFootnoteArray(footnotes), "p")
|
||||
);
|
||||
};
|
||||
|
||||
const renderer: RendererObject = {
|
||||
heading({ tokens, depth }: Tokens.Heading) {
|
||||
const text = this.parser.parseInline(tokens);
|
||||
const tag = `h${depth}`;
|
||||
return styledContent(tag, text);
|
||||
},
|
||||
|
||||
paragraph({ tokens }: Tokens.Paragraph): string {
|
||||
const text = this.parser.parseInline(tokens);
|
||||
const isFigureImage = text.includes("<figure") && text.includes("<img");
|
||||
const isEmpty = text.trim() === "";
|
||||
if (isFigureImage || isEmpty) {
|
||||
return text;
|
||||
}
|
||||
return styledContent("p", text);
|
||||
},
|
||||
|
||||
blockquote({ tokens }: Tokens.Blockquote): string {
|
||||
const text = this.parser.parse(tokens);
|
||||
return styledContent("blockquote", text);
|
||||
},
|
||||
|
||||
code({ text, lang = "" }: Tokens.Code): string {
|
||||
if (lang.startsWith("mermaid")) {
|
||||
if (isBrowser) {
|
||||
clearTimeout(codeIndex as any);
|
||||
codeIndex = setTimeout(async () => {
|
||||
const windowRef = typeof window !== "undefined" ? (window as any) : undefined;
|
||||
if (windowRef && windowRef.mermaid) {
|
||||
const mermaid = windowRef.mermaid;
|
||||
await mermaid.run();
|
||||
} else {
|
||||
const mermaid = await import("mermaid");
|
||||
await mermaid.default.run();
|
||||
}
|
||||
}, 0) as any as number;
|
||||
}
|
||||
return `<pre class="mermaid">${text}</pre>`;
|
||||
}
|
||||
const langText = lang.split(" ")[0];
|
||||
const isLanguageRegistered = hljs.getLanguage(langText);
|
||||
const language = isLanguageRegistered ? langText : "plaintext";
|
||||
|
||||
const highlighted = highlightAndFormatCode(
|
||||
text,
|
||||
language,
|
||||
hljs,
|
||||
!!opts.isShowLineNumber
|
||||
);
|
||||
|
||||
const span = `<span class="mac-sign" style="padding: 10px 14px 0;">${macCodeSvg}</span>`;
|
||||
let pendingAttr = "";
|
||||
if (!isLanguageRegistered && langText !== "plaintext") {
|
||||
const escapedText = text.replace(/"/g, """);
|
||||
pendingAttr = ` data-language-pending="${langText}" data-raw-code="${escapedText}" data-show-line-number="${opts.isShowLineNumber}"`;
|
||||
}
|
||||
const code = `<code class="language-${lang}"${pendingAttr}>${highlighted}</code>`;
|
||||
|
||||
return `<pre class="hljs code__pre">${span}${code}</pre>`;
|
||||
},
|
||||
|
||||
codespan({ text }: Tokens.Codespan): string {
|
||||
const escapedText = escapeHtml(text);
|
||||
return styledContent("codespan", escapedText, "code");
|
||||
},
|
||||
|
||||
list({ ordered, items, start = 1 }: Tokens.List) {
|
||||
listOrderedStack.push(ordered);
|
||||
listCounters.push(Number(start));
|
||||
const html = items.map((item) => this.listitem(item)).join("");
|
||||
listOrderedStack.pop();
|
||||
listCounters.pop();
|
||||
return styledContent(ordered ? "ol" : "ul", html);
|
||||
},
|
||||
|
||||
listitem(token: Tokens.ListItem) {
|
||||
const ordered = listOrderedStack[listOrderedStack.length - 1];
|
||||
const idx = listCounters[listCounters.length - 1]!;
|
||||
listCounters[listCounters.length - 1] = idx + 1;
|
||||
const prefix = ordered ? `${idx}. ` : "• ";
|
||||
let content: string;
|
||||
try {
|
||||
content = this.parser.parseInline(token.tokens);
|
||||
} catch {
|
||||
content = this.parser
|
||||
.parse(token.tokens)
|
||||
.replace(/^<p(?:\s[^>]*)?>([\s\S]*?)<\/p>/, "$1");
|
||||
}
|
||||
return styledContent("listitem", `${prefix}${content}`, "li");
|
||||
},
|
||||
|
||||
image({ href, title, text }: Tokens.Image): string {
|
||||
const newText = opts.legend ? transform(opts.legend, text, title) : "";
|
||||
const subText = newText ? styledContent("figcaption", newText) : "";
|
||||
const titleAttr = title ? ` title="${title}"` : "";
|
||||
return `<figure><img src="${href}"${titleAttr} alt="${text}"/>${subText}</figure>`;
|
||||
},
|
||||
|
||||
link({ href, title, text, tokens }: Tokens.Link): string {
|
||||
const parsedText = this.parser.parseInline(tokens);
|
||||
if (/^https?:\/\/mp\.weixin\.qq\.com/.test(href)) {
|
||||
return `<a href="${href}" title="${title || text}">${parsedText}</a>`;
|
||||
}
|
||||
if (href === text) {
|
||||
return parsedText;
|
||||
}
|
||||
if (opts.citeStatus) {
|
||||
const ref = addFootnote(title || text, href);
|
||||
return `<a href="${href}" title="${title || text}">${parsedText}<sup>[${ref}]</sup></a>`;
|
||||
}
|
||||
return `<a href="${href}" title="${title || text}">${parsedText}</a>`;
|
||||
},
|
||||
|
||||
strong({ tokens }: Tokens.Strong): string {
|
||||
return styledContent("strong", this.parser.parseInline(tokens));
|
||||
},
|
||||
|
||||
em({ tokens }: Tokens.Em): string {
|
||||
return styledContent("em", this.parser.parseInline(tokens));
|
||||
},
|
||||
|
||||
table({ header, rows }: Tokens.Table): string {
|
||||
const headerRow = header
|
||||
.map((cell) => {
|
||||
const text = this.parser.parseInline(cell.tokens);
|
||||
return styledContent("th", text);
|
||||
})
|
||||
.join("");
|
||||
const body = rows
|
||||
.map((row) => {
|
||||
const rowContent = row.map((cell) => this.tablecell(cell)).join("");
|
||||
return styledContent("tr", rowContent);
|
||||
})
|
||||
.join("");
|
||||
return `
|
||||
<section style="max-width: 100%; overflow: auto">
|
||||
<table class="preview-table">
|
||||
<thead>${headerRow}</thead>
|
||||
<tbody>${body}</tbody>
|
||||
</table>
|
||||
</section>
|
||||
`;
|
||||
},
|
||||
|
||||
tablecell(token: Tokens.TableCell): string {
|
||||
const text = this.parser.parseInline(token.tokens);
|
||||
return styledContent("td", text);
|
||||
},
|
||||
|
||||
hr(_: Tokens.Hr): string {
|
||||
return styledContent("hr", "");
|
||||
},
|
||||
};
|
||||
|
||||
marked.use({ renderer });
|
||||
marked.use(markedMarkup());
|
||||
marked.use(markedToc());
|
||||
marked.use(markedSlider());
|
||||
marked.use(markedAlert({}));
|
||||
if (isBrowser) {
|
||||
marked.use(MDKatex({ nonStandard: true }, true));
|
||||
}
|
||||
marked.use(markedFootnotes());
|
||||
marked.use(
|
||||
markedPlantUML({
|
||||
inlineSvg: isBrowser,
|
||||
})
|
||||
);
|
||||
marked.use(markedInfographic());
|
||||
marked.use(markedRuby());
|
||||
|
||||
return {
|
||||
buildAddition,
|
||||
buildFootnotes,
|
||||
setOptions,
|
||||
reset,
|
||||
parseFrontMatterAndContent,
|
||||
buildReadingTime,
|
||||
createContainer(content: string) {
|
||||
return styledContent("container", content, "section");
|
||||
},
|
||||
getOpts,
|
||||
};
|
||||
}
|
||||
|
||||
function preprocessCjkEmphasis(markdown: string): string {
|
||||
const processor = unified()
|
||||
.use(remarkParse)
|
||||
.use(remarkCjkFriendly);
|
||||
const tree = processor.parse(markdown);
|
||||
const extractText = (node: any): string => {
|
||||
if (node.type === "text") return node.value;
|
||||
if (node.children) return node.children.map(extractText).join("");
|
||||
return "";
|
||||
};
|
||||
const visit = (node: any, parent?: any, index?: number) => {
|
||||
if (node.children) {
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
visit(node.children[i], node, i);
|
||||
}
|
||||
}
|
||||
if (node.type === "strong" && parent && typeof index === "number") {
|
||||
const text = extractText(node);
|
||||
parent.children[index] = { type: "html", value: `<strong>${text}</strong>` };
|
||||
}
|
||||
if (node.type === "emphasis" && parent && typeof index === "number") {
|
||||
const text = extractText(node);
|
||||
parent.children[index] = { type: "html", value: `<em>${text}</em>` };
|
||||
}
|
||||
};
|
||||
visit(tree);
|
||||
const stringify = unified().use(remarkStringify);
|
||||
let result = stringify.stringify(tree);
|
||||
result = result.replace(/&#x([0-9A-Fa-f]+);/g, (_, hex) =>
|
||||
String.fromCodePoint(parseInt(hex, 16))
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
export function renderMarkdown(raw: string, renderer: RendererAPI): {
|
||||
html: string;
|
||||
readingTime: ReadTimeResults;
|
||||
} {
|
||||
const { markdownContent, readingTime: readingTimeResult } =
|
||||
renderer.parseFrontMatterAndContent(raw);
|
||||
const preprocessed = preprocessCjkEmphasis(markdownContent);
|
||||
const html = marked.parse(preprocessed) as string;
|
||||
return { html, readingTime: readingTimeResult };
|
||||
}
|
||||
|
||||
export function postProcessHtml(
|
||||
baseHtml: string,
|
||||
reading: ReadTimeResults,
|
||||
renderer: RendererAPI
|
||||
): string {
|
||||
let html = baseHtml;
|
||||
html = renderer.buildReadingTime(reading) + html;
|
||||
html += renderer.buildFootnotes();
|
||||
html += renderer.buildAddition();
|
||||
html += `
|
||||
<style>
|
||||
.hljs.code__pre > .mac-sign {
|
||||
display: ${renderer.getOpts().isMacCodeBlock ? "flex" : "none"};
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
html += `
|
||||
<style>
|
||||
h2 strong {
|
||||
color: inherit !important;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
return renderer.createContainer(html);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import type { ThemeName } from "./types.js";
|
||||
|
||||
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
||||
export const THEME_DIR = path.resolve(SCRIPT_DIR, "themes");
|
||||
const FALLBACK_THEMES: ThemeName[] = ["default", "grace", "simple"];
|
||||
|
||||
function stripOutputScope(cssContent: string): string {
|
||||
let css = cssContent;
|
||||
css = css.replace(/#output\s*\{/g, "body {");
|
||||
css = css.replace(/#output\s+/g, "");
|
||||
css = css.replace(/^#output\s*/gm, "");
|
||||
return css;
|
||||
}
|
||||
|
||||
function discoverThemesFromDir(dir: string): string[] {
|
||||
if (!fs.existsSync(dir)) {
|
||||
return [];
|
||||
}
|
||||
return fs
|
||||
.readdirSync(dir)
|
||||
.filter((name) => name.endsWith(".css"))
|
||||
.map((name) => name.replace(/\.css$/i, ""))
|
||||
.filter((name) => name.toLowerCase() !== "base");
|
||||
}
|
||||
|
||||
function resolveThemeNames(): ThemeName[] {
|
||||
const localThemes = discoverThemesFromDir(THEME_DIR);
|
||||
const resolved = localThemes.filter((name) =>
|
||||
fs.existsSync(path.join(THEME_DIR, `${name}.css`))
|
||||
);
|
||||
return resolved.length ? resolved : FALLBACK_THEMES;
|
||||
}
|
||||
|
||||
export const THEME_NAMES: ThemeName[] = resolveThemeNames();
|
||||
|
||||
export function loadThemeCss(theme: ThemeName): {
|
||||
baseCss: string;
|
||||
themeCss: string;
|
||||
} {
|
||||
const basePath = path.join(THEME_DIR, "base.css");
|
||||
const themePath = path.join(THEME_DIR, `${theme}.css`);
|
||||
|
||||
if (!fs.existsSync(basePath)) {
|
||||
throw new Error(`Missing base CSS: ${basePath}`);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(themePath)) {
|
||||
throw new Error(`Missing theme CSS for "${theme}": ${themePath}`);
|
||||
}
|
||||
|
||||
return {
|
||||
baseCss: fs.readFileSync(basePath, "utf-8"),
|
||||
themeCss: fs.readFileSync(themePath, "utf-8"),
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeThemeCss(css: string): string {
|
||||
return stripOutputScope(css);
|
||||
}
|
||||
@@ -0,0 +1,465 @@
|
||||
/**
|
||||
* MD 现代主题 (modern)
|
||||
* 大圆角、药丸形标题、宽松行距、现代感
|
||||
* 如需使用主题色,请使用 var(--md-primary-color) 代替颜色值
|
||||
*/
|
||||
|
||||
/* ==================== 容器样式覆盖 ==================== */
|
||||
section,
|
||||
container {
|
||||
font-family: var(--md-font-family);
|
||||
font-size: var(--md-font-size);
|
||||
line-height: 2;
|
||||
letter-spacing: 0px;
|
||||
font-weight: 400;
|
||||
background-color: var(--md-container-bg);
|
||||
border: 1px solid rgba(255, 255, 255, 0.01);
|
||||
border-radius: 25px;
|
||||
padding: 12px 12px;
|
||||
}
|
||||
|
||||
#output {
|
||||
font-family: var(--md-font-family);
|
||||
font-size: var(--md-font-size);
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
/* ==================== 一级标题 ==================== */
|
||||
h1 {
|
||||
display: table;
|
||||
padding: 0.3em 1em;
|
||||
margin: 20px auto;
|
||||
color: hsl(var(--foreground));
|
||||
background: var(--md-primary-color);
|
||||
border-radius: 15px;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ==================== 二级标题 ==================== */
|
||||
h2 {
|
||||
display: block;
|
||||
padding: 0.2em 0;
|
||||
padding-bottom: 0;
|
||||
margin: 0 auto 20px;
|
||||
width: 100%;
|
||||
color: var(--md-primary-color);
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.578px;
|
||||
line-height: 1.7;
|
||||
border-bottom: 2px solid var(--md-accent-color);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* ==================== 三级标题 ==================== */
|
||||
h3 {
|
||||
padding-left: 10px;
|
||||
border-left: 4px solid var(--md-primary-color);
|
||||
border-radius: 2px;
|
||||
margin: 0 8px 10px;
|
||||
color: hsl(var(--foreground));
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* ==================== 四级标题 ==================== */
|
||||
h4 {
|
||||
margin: 0 8px 10px;
|
||||
color: var(--md-primary-color);
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* ==================== 五级标题 ==================== */
|
||||
h5 {
|
||||
display: inline-block;
|
||||
margin: 0 8px 10px;
|
||||
padding: 4px 12px;
|
||||
color: hsl(var(--foreground));
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
border: 1px solid rgb(189, 224, 254);
|
||||
border-radius: 20px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* ==================== 六级标题 ==================== */
|
||||
h6 {
|
||||
margin: 0 8px 10px;
|
||||
color: var(--md-primary-color);
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* ==================== 段落 ==================== */
|
||||
p {
|
||||
margin: 20px 0;
|
||||
letter-spacing: 0.1em;
|
||||
color: hsl(var(--foreground));
|
||||
line-height: 2;
|
||||
letter-spacing: 0px;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* ==================== 引用块 ==================== */
|
||||
blockquote {
|
||||
font-style: normal;
|
||||
padding: 15px 0;
|
||||
margin: 12px 0;
|
||||
border-left: 7px solid var(--md-accent-color);
|
||||
border-radius: 10px;
|
||||
color: hsl(var(--foreground));
|
||||
background-color: var(--blockquote-background);
|
||||
}
|
||||
|
||||
blockquote > p {
|
||||
display: block;
|
||||
font-size: 1em;
|
||||
letter-spacing: 0.1em;
|
||||
color: hsl(var(--foreground));
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ==================== GFM 警告块 ==================== */
|
||||
.alert-title-note,
|
||||
.alert-title-tip,
|
||||
.alert-title-info,
|
||||
.alert-title-important,
|
||||
.alert-title-warning,
|
||||
.alert-title-caution,
|
||||
.alert-title-abstract,
|
||||
.alert-title-summary,
|
||||
.alert-title-tldr,
|
||||
.alert-title-todo,
|
||||
.alert-title-success,
|
||||
.alert-title-done,
|
||||
.alert-title-question,
|
||||
.alert-title-help,
|
||||
.alert-title-faq,
|
||||
.alert-title-failure,
|
||||
.alert-title-fail,
|
||||
.alert-title-missing,
|
||||
.alert-title-danger,
|
||||
.alert-title-error,
|
||||
.alert-title-bug,
|
||||
.alert-title-example,
|
||||
.alert-title-quote,
|
||||
.alert-title-cite {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.alert-title-note {
|
||||
color: #478be6;
|
||||
}
|
||||
|
||||
.alert-title-tip {
|
||||
color: #57ab5a;
|
||||
}
|
||||
|
||||
.alert-title-info {
|
||||
color: #93c5fd;
|
||||
}
|
||||
|
||||
.alert-title-important {
|
||||
color: #986ee2;
|
||||
}
|
||||
|
||||
.alert-title-warning {
|
||||
color: #c69026;
|
||||
}
|
||||
|
||||
.alert-title-caution {
|
||||
color: #e5534b;
|
||||
}
|
||||
|
||||
.alert-title-abstract,
|
||||
.alert-title-summary,
|
||||
.alert-title-tldr {
|
||||
color: #00bfff;
|
||||
}
|
||||
|
||||
.alert-title-todo {
|
||||
color: #478be6;
|
||||
}
|
||||
|
||||
.alert-title-success,
|
||||
.alert-title-done {
|
||||
color: #57ab5a;
|
||||
}
|
||||
|
||||
.alert-title-question,
|
||||
.alert-title-help,
|
||||
.alert-title-faq {
|
||||
color: #c69026;
|
||||
}
|
||||
|
||||
.alert-title-failure,
|
||||
.alert-title-fail,
|
||||
.alert-title-missing {
|
||||
color: #e5534b;
|
||||
}
|
||||
|
||||
.alert-title-danger,
|
||||
.alert-title-error {
|
||||
color: #e5534b;
|
||||
}
|
||||
|
||||
.alert-title-bug {
|
||||
color: #e5534b;
|
||||
}
|
||||
|
||||
.alert-title-example {
|
||||
color: #986ee2;
|
||||
}
|
||||
|
||||
.alert-title-quote,
|
||||
.alert-title-cite {
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
/* GFM Alert SVG 图标颜色 */
|
||||
.alert-icon-note {
|
||||
fill: #478be6;
|
||||
}
|
||||
|
||||
.alert-icon-tip {
|
||||
fill: #57ab5a;
|
||||
}
|
||||
|
||||
.alert-icon-info {
|
||||
fill: #93c5fd;
|
||||
}
|
||||
|
||||
.alert-icon-important {
|
||||
fill: #986ee2;
|
||||
}
|
||||
|
||||
.alert-icon-warning {
|
||||
fill: #c69026;
|
||||
}
|
||||
|
||||
.alert-icon-caution {
|
||||
fill: #e5534b;
|
||||
}
|
||||
|
||||
.alert-icon-abstract,
|
||||
.alert-icon-summary,
|
||||
.alert-icon-tldr {
|
||||
fill: #00bfff;
|
||||
}
|
||||
|
||||
.alert-icon-todo {
|
||||
fill: #478be6;
|
||||
}
|
||||
|
||||
.alert-icon-success,
|
||||
.alert-icon-done {
|
||||
fill: #57ab5a;
|
||||
}
|
||||
|
||||
.alert-icon-question,
|
||||
.alert-icon-help,
|
||||
.alert-icon-faq {
|
||||
fill: #c69026;
|
||||
}
|
||||
|
||||
.alert-icon-failure,
|
||||
.alert-icon-fail,
|
||||
.alert-icon-missing {
|
||||
fill: #e5534b;
|
||||
}
|
||||
|
||||
.alert-icon-danger,
|
||||
.alert-icon-error {
|
||||
fill: #e5534b;
|
||||
}
|
||||
|
||||
.alert-icon-bug {
|
||||
fill: #e5534b;
|
||||
}
|
||||
|
||||
.alert-icon-example {
|
||||
fill: #986ee2;
|
||||
}
|
||||
|
||||
.alert-icon-quote,
|
||||
.alert-icon-cite {
|
||||
fill: #9ca3af;
|
||||
}
|
||||
|
||||
/* ==================== 代码块 ==================== */
|
||||
pre.code__pre,
|
||||
.hljs.code__pre {
|
||||
font-size: 90%;
|
||||
overflow-x: auto;
|
||||
border-radius: 10px;
|
||||
padding: 0 !important;
|
||||
line-height: 1.5;
|
||||
margin: 10px 8px;
|
||||
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* ==================== 图片 ==================== */
|
||||
img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
margin: 0.1em auto 0.5em;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/* ==================== 列表 ==================== */
|
||||
ol {
|
||||
padding-left: 1em;
|
||||
margin-left: 0;
|
||||
color: hsl(var(--foreground));
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: circle;
|
||||
padding-left: 1em;
|
||||
margin-left: 0;
|
||||
color: hsl(var(--foreground));
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
li {
|
||||
display: block;
|
||||
margin: 0.2em 8px;
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
/* ==================== 脚注 ==================== */
|
||||
p.footnotes {
|
||||
margin: 0.5em 8px;
|
||||
font-size: 80%;
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
/* ==================== 图表 ==================== */
|
||||
figure {
|
||||
margin: 1.5em 8px;
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
figcaption,
|
||||
.md-figcaption {
|
||||
text-align: center;
|
||||
color: #888;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
/* ==================== 分隔线 ==================== */
|
||||
hr {
|
||||
border-style: solid;
|
||||
border-width: 1px 0 0;
|
||||
border-color: var(--md-accent-color);
|
||||
margin: 1.5em 0;
|
||||
}
|
||||
|
||||
/* ==================== 行内代码 ==================== */
|
||||
code {
|
||||
font-size: 90%;
|
||||
color: #d14;
|
||||
background: rgba(27, 31, 35, 0.05);
|
||||
padding: 3px 5px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* 代码块内的 code 标签需要特殊处理(覆盖行内 code 样式) */
|
||||
pre.code__pre > code,
|
||||
.hljs.code__pre > code {
|
||||
display: -webkit-box;
|
||||
padding: 0.5em 1em 1em;
|
||||
overflow-x: auto;
|
||||
text-indent: 0;
|
||||
color: inherit;
|
||||
background: none;
|
||||
white-space: nowrap;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ==================== 强调 ==================== */
|
||||
em {
|
||||
font-style: italic;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
/* ==================== 链接 ==================== */
|
||||
a {
|
||||
color: var(--md-primary-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* ==================== 粗体 ==================== */
|
||||
strong {
|
||||
color: var(--md-primary-color);
|
||||
font-weight: bold;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
/* ==================== 表格 ==================== */
|
||||
table {
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
thead {
|
||||
font-weight: bold;
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
th {
|
||||
border: 1px solid #dfdfdf;
|
||||
padding: 0.25em 0.5em;
|
||||
color: hsl(var(--foreground));
|
||||
word-break: keep-all;
|
||||
background: color-mix(in srgb, var(--md-primary-color) 10%, transparent);
|
||||
}
|
||||
|
||||
td {
|
||||
border: 1px solid #dfdfdf;
|
||||
padding: 0.25em 0.5em;
|
||||
color: hsl(var(--foreground));
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
/* ==================== KaTeX 公式 ==================== */
|
||||
.katex-inline {
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.katex-block {
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
padding: 0.5em 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ==================== 标记高亮 ==================== */
|
||||
.markup-highlight {
|
||||
background-color: var(--md-primary-color);
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.markup-underline {
|
||||
text-decoration: underline;
|
||||
text-decoration-color: var(--md-primary-color);
|
||||
}
|
||||
|
||||
.markup-wavyline {
|
||||
text-decoration: underline wavy;
|
||||
text-decoration-color: var(--md-primary-color);
|
||||
text-decoration-thickness: 2px;
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
/**
|
||||
* MD 橙色主题
|
||||
* 暖色调现代风,橙色主色,宽松行距
|
||||
*/
|
||||
|
||||
/* ==================== 容器样式覆盖 ==================== */
|
||||
section,
|
||||
container {
|
||||
font-family: PingFang SC, system-ui, -apple-system, BlinkMacSystemFont, Helvetica Neue, Hiragino Sans GB, Microsoft YaHei UI, Microsoft YaHei, Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
line-height: 2;
|
||||
background-color: rgba(250, 249, 245, 1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.01);
|
||||
border-radius: 25px;
|
||||
padding: 12px 12px;
|
||||
}
|
||||
|
||||
#output {
|
||||
font-family: PingFang SC, system-ui, -apple-system, BlinkMacSystemFont, Helvetica Neue, Hiragino Sans GB, Microsoft YaHei UI, Microsoft YaHei, Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
/* ==================== 一级标题 ==================== */
|
||||
h1 {
|
||||
display: table;
|
||||
padding: 0.3em 1em;
|
||||
margin: 20px auto;
|
||||
color: #3B3B38;
|
||||
background: #D97757;
|
||||
border-radius: 15px;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ==================== 二级标题 ==================== */
|
||||
h2 {
|
||||
display: block;
|
||||
padding: 0.2em 0;
|
||||
padding-bottom: 0;
|
||||
margin: 0 auto 20px;
|
||||
width: 100%;
|
||||
color: #D97757;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
border-bottom: 2px solid #E4B1A0;
|
||||
}
|
||||
|
||||
/* ==================== 三级标题 ==================== */
|
||||
h3 {
|
||||
margin: 0 8px 10px;
|
||||
color: #3B3B38;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* ==================== 四级标题 ==================== */
|
||||
h4 {
|
||||
margin: 0 8px 10px;
|
||||
color: #D97757;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* ==================== 五级标题 ==================== */
|
||||
h5 {
|
||||
display: inline-block;
|
||||
margin: 0 8px 10px;
|
||||
padding: 4px 10px;
|
||||
color: #3B3B38;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
border: 1px solid rgb(189, 224, 254);
|
||||
border-radius: 20px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* ==================== 六级标题 ==================== */
|
||||
h6 {
|
||||
margin: 0 8px 10px;
|
||||
color: #D97757;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* ==================== 段落 ==================== */
|
||||
p {
|
||||
margin: 20px 0;
|
||||
color: #3B3B38;
|
||||
line-height: 2;
|
||||
letter-spacing: 0px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
/* ==================== 引用块 ==================== */
|
||||
blockquote {
|
||||
font-style: normal;
|
||||
padding: 15px 12px;
|
||||
border-left: 7px solid rgba(228, 177, 160, 1);
|
||||
border-radius: 10px;
|
||||
color: #3B3B38;
|
||||
background-color: rgba(255, 255, 255, 0.6);
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
blockquote > p {
|
||||
color: #3B3B38;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ==================== 代码块 ==================== */
|
||||
pre.code__pre,
|
||||
.hljs.code__pre {
|
||||
border-radius: 10px;
|
||||
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* ==================== 图片 ==================== */
|
||||
img {
|
||||
border-radius: 10px;
|
||||
margin: 5px auto;
|
||||
}
|
||||
|
||||
/* ==================== 列表 ==================== */
|
||||
ol {
|
||||
padding-left: 1em;
|
||||
margin: 15px 0;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
margin: 15px 0;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 0.2em 0;
|
||||
color: #3B3B38;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
/* ==================== 分隔线 ==================== */
|
||||
hr {
|
||||
border-style: solid;
|
||||
border-width: 1px 0 0;
|
||||
border-color: #D97757;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ==================== 强调 ==================== */
|
||||
strong {
|
||||
color: #3B3B38;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* ==================== 标记高亮 ==================== */
|
||||
.markup-highlight {
|
||||
background-color: #3B3B3B;
|
||||
padding: 10px;
|
||||
color: #FAF9F5;
|
||||
}
|
||||
|
||||
.markup-underline {
|
||||
text-decoration: underline;
|
||||
text-decoration-color: #E4B1A0;
|
||||
}
|
||||
|
||||
/* ==================== 链接 ==================== */
|
||||
a {
|
||||
color: #D97757;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* ==================== 表格 ==================== */
|
||||
th {
|
||||
background: rgba(217, 119, 87, 0.1);
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
/**
|
||||
* MD 红色主题
|
||||
* 传统书法风格,红金配色,宋体排版
|
||||
*/
|
||||
|
||||
/* ==================== 容器字体覆盖 ==================== */
|
||||
section,
|
||||
container {
|
||||
font-family: "Source Han Serif SC", "Noto Serif CJK SC", "Source Han Serif CN", STSong, SimSun, serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.8;
|
||||
background-color: rgba(255, 251, 240, 1);
|
||||
border: 4px solid rgba(169, 50, 38, 1);
|
||||
border-radius: 12px;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
#output {
|
||||
font-family: "Source Han Serif SC", "Noto Serif CJK SC", "Source Han Serif CN", STSong, SimSun, serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
/* ==================== 一级标题 ==================== */
|
||||
h1 {
|
||||
display: table;
|
||||
padding: 0 1em;
|
||||
border-bottom: 2px solid #A93226;
|
||||
margin: 30px auto 20px;
|
||||
color: #A93226;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ==================== 二级标题 ==================== */
|
||||
h2 {
|
||||
display: block;
|
||||
width: fit-content;
|
||||
padding: 6px 20px;
|
||||
margin: 30px auto 20px;
|
||||
color: #FFFFFF;
|
||||
background-color: #A93226;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
border-radius: 8px;
|
||||
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 6px;
|
||||
line-height: 1.6em;
|
||||
}
|
||||
|
||||
/* ==================== 三级标题 ==================== */
|
||||
h3 {
|
||||
padding-left: 8px;
|
||||
border-left: 3px solid #A93226;
|
||||
margin: 20px 8px 10px 0;
|
||||
color: #A93226;
|
||||
font-size: 17px;
|
||||
font-weight: normal;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* ==================== 四级标题 ==================== */
|
||||
h4 {
|
||||
margin: 10px 8px;
|
||||
color: #D4AC0D;
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* ==================== 五级标题 ==================== */
|
||||
h5 {
|
||||
margin: 10px 8px;
|
||||
color: #797D7F;
|
||||
font-size: 15px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* ==================== 六级标题 ==================== */
|
||||
h6 {
|
||||
margin: 10px 8px;
|
||||
color: #797D7F;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* ==================== 段落 ==================== */
|
||||
p {
|
||||
margin: 16px 0;
|
||||
letter-spacing: 0.5px;
|
||||
color: #2C2C2C;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
/* ==================== 引用块 ==================== */
|
||||
blockquote {
|
||||
font-style: normal;
|
||||
padding: 15px 12px;
|
||||
border-left: 2px solid rgba(169, 50, 38, 0.8);
|
||||
border-radius: 4px;
|
||||
color: #5D4037;
|
||||
background-color: rgba(253, 237, 236, 1);
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
blockquote > p {
|
||||
color: #5D4037;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ==================== 代码块 ==================== */
|
||||
pre.code__pre,
|
||||
.hljs.code__pre {
|
||||
font-size: 13px;
|
||||
border: 1px solid #D4AC0D;
|
||||
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* ==================== 图片 ==================== */
|
||||
img {
|
||||
border-radius: 4px;
|
||||
border: 1px solid #D4AC0D;
|
||||
margin: 20px auto;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
/* ==================== 列表 ==================== */
|
||||
ol {
|
||||
padding-left: 1em;
|
||||
margin: 15px 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
margin: 15px 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 0.2em 0;
|
||||
color: #2C2C2C;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* ==================== 分隔线 ==================== */
|
||||
hr {
|
||||
border-style: solid;
|
||||
border-width: 2px 0 0;
|
||||
border-color: #D4AC0D;
|
||||
margin: 40px 0;
|
||||
}
|
||||
|
||||
/* ==================== 强调 ==================== */
|
||||
strong {
|
||||
color: #A93226;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* ==================== 标记高亮 ==================== */
|
||||
.markup-highlight {
|
||||
background-color: #FADBD8;
|
||||
padding: 5px;
|
||||
color: #A93226;
|
||||
}
|
||||
|
||||
.markup-underline {
|
||||
text-decoration: underline;
|
||||
text-decoration-color: #D4AC0D;
|
||||
}
|
||||
|
||||
/* ==================== 链接 ==================== */
|
||||
a {
|
||||
color: #A93226;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* ==================== 表格 ==================== */
|
||||
th {
|
||||
background: rgba(169, 50, 38, 0.1);
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import type { ReadTimeResults } from "reading-time";
|
||||
|
||||
export type ThemeName = string;
|
||||
|
||||
export interface StyleConfig {
|
||||
primaryColor: string;
|
||||
fontFamily: string;
|
||||
fontSize: string;
|
||||
foreground: string;
|
||||
blockquoteBackground: string;
|
||||
accentColor: string;
|
||||
containerBg: string;
|
||||
}
|
||||
|
||||
export interface IOpts {
|
||||
legend?: string;
|
||||
citeStatus?: boolean;
|
||||
countStatus?: boolean;
|
||||
isMacCodeBlock?: boolean;
|
||||
isShowLineNumber?: boolean;
|
||||
themeMode?: "light" | "dark";
|
||||
}
|
||||
|
||||
export interface RendererAPI {
|
||||
reset: (newOpts: Partial<IOpts>) => void;
|
||||
setOptions: (newOpts: Partial<IOpts>) => void;
|
||||
getOpts: () => IOpts;
|
||||
parseFrontMatterAndContent: (markdown: string) => {
|
||||
yamlData: Record<string, any>;
|
||||
markdownContent: string;
|
||||
readingTime: ReadTimeResults;
|
||||
};
|
||||
buildReadingTime: (reading: ReadTimeResults) => string;
|
||||
buildFootnotes: () => string;
|
||||
buildAddition: () => string;
|
||||
createContainer: (html: string) => string;
|
||||
}
|
||||
|
||||
export interface ParseResult {
|
||||
yamlData: Record<string, any>;
|
||||
markdownContent: string;
|
||||
readingTime: ReadTimeResults;
|
||||
}
|
||||
|
||||
export interface CliOptions {
|
||||
inputPath: string;
|
||||
theme: ThemeName;
|
||||
keepTitle: boolean;
|
||||
primaryColor?: string;
|
||||
fontFamily?: string;
|
||||
fontSize?: string;
|
||||
codeTheme: string;
|
||||
isMacCodeBlock: boolean;
|
||||
isShowLineNumber: boolean;
|
||||
citeStatus: boolean;
|
||||
countStatus: boolean;
|
||||
legend: string;
|
||||
}
|
||||
|
||||
export interface ExtendConfig {
|
||||
default_theme: string | null;
|
||||
default_color: string | null;
|
||||
default_font_family: string | null;
|
||||
default_font_size: string | null;
|
||||
default_code_theme: string | null;
|
||||
mac_code_block: boolean | null;
|
||||
show_line_number: boolean | null;
|
||||
cite: boolean | null;
|
||||
count: boolean | null;
|
||||
legend: string | null;
|
||||
keep_title: boolean | null;
|
||||
}
|
||||
|
||||
export interface HtmlDocumentMeta {
|
||||
title: string;
|
||||
author?: string;
|
||||
description?: string;
|
||||
}
|
||||
@@ -18,6 +18,7 @@ description: Posts content to WeChat Official Account (微信公众号) via API
|
||||
| `scripts/wechat-browser.ts` | Image-text posts (图文) |
|
||||
| `scripts/wechat-article.ts` | Article posting via browser (文章) |
|
||||
| `scripts/wechat-api.ts` | Article posting via API (文章) |
|
||||
| `scripts/md-to-wechat.ts` | Markdown → WeChat-ready HTML with image placeholders |
|
||||
| `scripts/check-permissions.ts` | Verify environment & permissions |
|
||||
|
||||
## Preferences (EXTEND.md)
|
||||
@@ -48,7 +49,7 @@ test -f "$HOME/.baoyu-skills/baoyu-post-to-wechat/EXTEND.md" && echo "user"
|
||||
│ Not found │ Run first-time setup ([references/config/first-time-setup.md](references/config/first-time-setup.md)) → Save → Continue │
|
||||
└───────────┴───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
**EXTEND.md Supports**: Default theme | Default publishing method (api/browser) | Default author | Default open-comment switch | Default fans-only-comment switch | Chrome profile path
|
||||
**EXTEND.md Supports**: Default theme | Default color | Default publishing method (api/browser) | Default author | Default open-comment switch | Default fans-only-comment switch | Chrome profile path
|
||||
|
||||
First-time setup: [references/config/first-time-setup.md](references/config/first-time-setup.md)
|
||||
|
||||
@@ -64,6 +65,7 @@ First-time setup: [references/config/first-time-setup.md](references/config/firs
|
||||
|
||||
```md
|
||||
default_theme: default
|
||||
default_color: blue
|
||||
default_publish_method: api
|
||||
default_author: 宝玉
|
||||
need_open_comment: 1
|
||||
@@ -71,6 +73,10 @@ only_fans_can_comment: 0
|
||||
chrome_profile_path: /path/to/chrome/profile
|
||||
```
|
||||
|
||||
**Theme options**: default, grace, simple, modern
|
||||
|
||||
**Color presets**: blue, green, vermilion, yellow, purple, sky, rose, olive, black, gray, pink, red, orange (or hex value)
|
||||
|
||||
**Value priority**:
|
||||
1. CLI arguments
|
||||
2. Frontmatter
|
||||
@@ -98,7 +104,7 @@ Checks: Chrome, profile isolation, Bun, Accessibility, clipboard, paste keystrok
|
||||
| Clipboard copy | Ensure Swift/AppKit available (macOS Xcode CLI tools: `xcode-select --install`) |
|
||||
| Paste keystroke (macOS) | Same as Accessibility fix above |
|
||||
| Paste keystroke (Linux) | Install `xdotool` (X11) or `ydotool` (Wayland) |
|
||||
| API credentials | Follow guided setup in Step 5, or manually set in `.baoyu-skills/.env` |
|
||||
| API credentials | Follow guided setup in Step 2, or manually set in `.baoyu-skills/.env` |
|
||||
|
||||
## Image-Text Posting (图文)
|
||||
|
||||
@@ -119,12 +125,10 @@ Copy this checklist and check off items as you complete them:
|
||||
Publishing Progress:
|
||||
- [ ] Step 0: Load preferences (EXTEND.md)
|
||||
- [ ] Step 1: Determine input type
|
||||
- [ ] Step 2: Check markdown-to-html skill
|
||||
- [ ] Step 3: Convert to HTML
|
||||
- [ ] Step 4: Validate metadata (title, summary, cover)
|
||||
- [ ] Step 5: Select method and configure credentials
|
||||
- [ ] Step 6: Publish to WeChat
|
||||
- [ ] Step 7: Report completion
|
||||
- [ ] Step 2: Select method and configure credentials
|
||||
- [ ] Step 3: Resolve theme/color and validate metadata
|
||||
- [ ] Step 4: Publish to WeChat
|
||||
- [ ] Step 5: Report completion
|
||||
```
|
||||
|
||||
### Step 0: Load Preferences
|
||||
@@ -134,6 +138,8 @@ Check and load EXTEND.md settings (see Preferences section above).
|
||||
**CRITICAL**: If not found, complete first-time setup BEFORE any other steps or questions.
|
||||
|
||||
Resolve and store these defaults for later steps:
|
||||
- `default_theme` (default `default`)
|
||||
- `default_color` (omit if not set — theme default applies)
|
||||
- `default_author`
|
||||
- `need_open_comment` (default `1`)
|
||||
- `only_fans_can_comment` (default `0`)
|
||||
@@ -142,9 +148,9 @@ Resolve and store these defaults for later steps:
|
||||
|
||||
| Input Type | Detection | Action |
|
||||
|------------|-----------|--------|
|
||||
| HTML file | Path ends with `.html`, file exists | Skip to Step 4 |
|
||||
| HTML file | Path ends with `.html`, file exists | Skip to Step 3 |
|
||||
| Markdown file | Path ends with `.md`, file exists | Continue to Step 2 |
|
||||
| Plain text | Not a file path, or file doesn't exist | Save to markdown, then Step 2 |
|
||||
| Plain text | Not a file path, or file doesn't exist | Save to markdown, continue to Step 2 |
|
||||
|
||||
**Plain Text Handling**:
|
||||
|
||||
@@ -162,77 +168,7 @@ mkdir -p "$(pwd)/post-to-wechat/$(date +%Y-%m-%d)"
|
||||
- "Understanding AI Models" → `understanding-ai-models`
|
||||
- "人工智能的未来" → `ai-future` (translate to English for slug)
|
||||
|
||||
### Step 2: Check Markdown-to-HTML Skill
|
||||
|
||||
**Skip if**: Input is `.html` file
|
||||
|
||||
**Skill Discovery**:
|
||||
|
||||
```bash
|
||||
# Check if baoyu-markdown-to-html exists
|
||||
test -f skills/baoyu-markdown-to-html/SKILL.md && echo "found"
|
||||
```
|
||||
|
||||
| Result | Action |
|
||||
|--------|--------|
|
||||
| Found | Read its SKILL.md, continue to Step 3 |
|
||||
| Multiple skills | AskUserQuestion to choose |
|
||||
| Not found | Show installation suggestion |
|
||||
|
||||
**When Not Found**:
|
||||
|
||||
```
|
||||
No markdown-to-html skill found.
|
||||
|
||||
Suggested installation:
|
||||
https://github.com/JimLiu/baoyu-skills/blob/main/skills/baoyu-markdown-to-html/SKILL.md
|
||||
|
||||
Options:
|
||||
A) Cancel - install the skill first
|
||||
B) Continue - provide HTML file manually
|
||||
```
|
||||
|
||||
### Step 3: Convert Markdown to HTML
|
||||
|
||||
**Skip if**: Input is `.html` file
|
||||
|
||||
1. **Resolve theme** (first match wins, do NOT ask user if resolved):
|
||||
- CLI `--theme` argument
|
||||
- EXTEND.md `default_theme` (loaded in Step 0)
|
||||
- Fallback: `default`
|
||||
|
||||
2. **Execute conversion** (using the discovered skill), **always pass `--theme`**:
|
||||
|
||||
```bash
|
||||
npx -y bun ${MD_TO_HTML_SKILL_DIR}/scripts/main.ts <markdown_file> --theme <theme>
|
||||
```
|
||||
|
||||
**CRITICAL**: Always include `--theme` parameter. Never omit it, even if using `default`.
|
||||
|
||||
3. **Parse JSON output** to get: `htmlPath`, `title`, `author`, `summary`, `contentImages`
|
||||
|
||||
### Step 4: Validate Metadata
|
||||
|
||||
Check extracted metadata from Step 3 (or HTML meta tags if direct HTML input).
|
||||
|
||||
| Field | If Missing |
|
||||
|-------|------------|
|
||||
| Title | Prompt: "Enter title, or press Enter to auto-generate from content" |
|
||||
| Summary | Prompt: "Enter summary, or press Enter to auto-generate (recommended for SEO)" |
|
||||
| Author | Use fallback chain: CLI `--author` → frontmatter `author` → EXTEND.md `default_author` |
|
||||
|
||||
**Auto-Generation Logic**:
|
||||
- **Title**: First H1/H2 heading, or first sentence
|
||||
- **Summary**: First paragraph, truncated to 120 characters
|
||||
|
||||
**Cover Image Check** (required for `article_type=news`):
|
||||
1. Use CLI `--cover` if provided.
|
||||
2. Else use frontmatter (`coverImage`, `featureImage`, `cover`, `image`).
|
||||
3. Else check article directory default path: `imgs/cover.png`.
|
||||
4. Else fallback to first inline content image.
|
||||
5. If still missing, stop and request a cover image before publishing.
|
||||
|
||||
### Step 5: Select Publishing Method and Configure
|
||||
### Step 2: Select Publishing Method and Configure
|
||||
|
||||
**Ask publishing method** (unless specified in EXTEND.md or CLI):
|
||||
|
||||
@@ -273,14 +209,49 @@ WECHAT_APP_ID=<user_input>
|
||||
WECHAT_APP_SECRET=<user_input>
|
||||
```
|
||||
|
||||
### Step 6: Publish to WeChat
|
||||
### Step 3: Resolve Theme/Color and Validate Metadata
|
||||
|
||||
**API method**:
|
||||
1. **Resolve theme** (first match wins, do NOT ask user if resolved):
|
||||
- CLI `--theme` argument
|
||||
- EXTEND.md `default_theme` (loaded in Step 0)
|
||||
- Fallback: `default`
|
||||
|
||||
2. **Resolve color** (first match wins):
|
||||
- CLI `--color` argument
|
||||
- EXTEND.md `default_color` (loaded in Step 0)
|
||||
- Omit if not set (theme default applies)
|
||||
|
||||
3. **Validate metadata** from frontmatter (markdown) or HTML meta tags (HTML input):
|
||||
|
||||
| Field | If Missing |
|
||||
|-------|------------|
|
||||
| Title | Prompt: "Enter title, or press Enter to auto-generate from content" |
|
||||
| Summary | Prompt: "Enter summary, or press Enter to auto-generate (recommended for SEO)" |
|
||||
| Author | Use fallback chain: CLI `--author` → frontmatter `author` → EXTEND.md `default_author` |
|
||||
|
||||
**Auto-Generation Logic**:
|
||||
- **Title**: First H1/H2 heading, or first sentence
|
||||
- **Summary**: First paragraph, truncated to 120 characters
|
||||
|
||||
4. **Cover Image Check** (required for API `article_type=news`):
|
||||
1. Use CLI `--cover` if provided.
|
||||
2. Else use frontmatter (`coverImage`, `featureImage`, `cover`, `image`).
|
||||
3. Else check article directory default path: `imgs/cover.png`.
|
||||
4. Else fallback to first inline content image.
|
||||
5. If still missing, stop and request a cover image before publishing.
|
||||
|
||||
### Step 4: Publish to WeChat
|
||||
|
||||
**CRITICAL**: Publishing scripts handle markdown conversion internally. Do NOT pre-convert markdown to HTML — pass the original markdown file directly. This ensures the API method renders images as `<img>` tags (for API upload) while the browser method uses placeholders (for paste-and-replace workflow).
|
||||
|
||||
**API method** (accepts `.md` or `.html`):
|
||||
|
||||
```bash
|
||||
npx -y bun ${SKILL_DIR}/scripts/wechat-api.ts <html_file> [--title <title>] [--summary <summary>] [--author <author>] [--cover <cover_path>]
|
||||
npx -y bun ${SKILL_DIR}/scripts/wechat-api.ts <file> --theme <theme> [--color <color>] [--title <title>] [--summary <summary>] [--author <author>] [--cover <cover_path>]
|
||||
```
|
||||
|
||||
**CRITICAL**: Always include `--theme` parameter. Never omit it, even if using `default`. Only include `--color` if explicitly set by user or EXTEND.md.
|
||||
|
||||
**`draft/add` payload rules**:
|
||||
- Use endpoint: `POST https://api.weixin.qq.com/cgi-bin/draft/add?access_token=ACCESS_TOKEN`
|
||||
- `article_type`: `news` (default) or `newspic`
|
||||
@@ -292,13 +263,14 @@ npx -y bun ${SKILL_DIR}/scripts/wechat-api.ts <html_file> [--title <title>] [--s
|
||||
|
||||
If script parameters do not expose the two comment fields, still ensure final API request body includes resolved values.
|
||||
|
||||
**Browser method**:
|
||||
**Browser method** (accepts `--markdown` or `--html`):
|
||||
|
||||
```bash
|
||||
npx -y bun ${SKILL_DIR}/scripts/wechat-article.ts --markdown <markdown_file> --theme <theme> [--color <color>]
|
||||
npx -y bun ${SKILL_DIR}/scripts/wechat-article.ts --html <html_file>
|
||||
```
|
||||
|
||||
### Step 7: Completion Report
|
||||
### Step 5: Completion Report
|
||||
|
||||
**For API method**, include draft management link:
|
||||
|
||||
@@ -307,7 +279,7 @@ WeChat Publishing Complete!
|
||||
|
||||
Input: [type] - [path]
|
||||
Method: API
|
||||
Theme: [theme name]
|
||||
Theme: [theme name] [color if set]
|
||||
|
||||
Article:
|
||||
• Title: [title]
|
||||
@@ -334,7 +306,7 @@ WeChat Publishing Complete!
|
||||
|
||||
Input: [type] - [path]
|
||||
Method: Browser
|
||||
Theme: [theme name]
|
||||
Theme: [theme name] [color if set]
|
||||
|
||||
Article:
|
||||
• Title: [title]
|
||||
@@ -362,7 +334,7 @@ Files created:
|
||||
|---------|------------|---------------|-------------------|
|
||||
| Plain text input | ✗ | ✓ | ✓ |
|
||||
| HTML input | ✗ | ✓ | ✓ |
|
||||
| Markdown input | Title/content | ✓ (via skill) | ✓ (via skill) |
|
||||
| Markdown input | Title/content | ✓ | ✓ |
|
||||
| Multiple images | ✓ (up to 9) | ✓ (inline) | ✓ (inline) |
|
||||
| Themes | ✗ | ✓ | ✓ |
|
||||
| Auto-generate metadata | ✗ | ✓ | ✓ |
|
||||
@@ -376,16 +348,12 @@ Files created:
|
||||
|
||||
**For API method**:
|
||||
- WeChat Official Account API credentials
|
||||
- Guided setup in Step 5, or manually set in `.baoyu-skills/.env`
|
||||
- Guided setup in Step 2, or manually set in `.baoyu-skills/.env`
|
||||
|
||||
**For Browser method**:
|
||||
- Google Chrome
|
||||
- First run: log in to WeChat Official Account (session preserved)
|
||||
|
||||
**For Markdown conversion**:
|
||||
- A markdown-to-html skill (e.g., `baoyu-markdown-to-html`)
|
||||
- If not installed, the workflow will suggest installation
|
||||
|
||||
**Config File Locations** (priority order):
|
||||
1. Environment variables
|
||||
2. `<cwd>/.baoyu-skills/.env`
|
||||
@@ -395,8 +363,7 @@ Files created:
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| No markdown-to-html skill | Install `baoyu-markdown-to-html` from suggested URL |
|
||||
| Missing API credentials | Follow guided setup in Step 5 |
|
||||
| Missing API credentials | Follow guided setup in Step 2 |
|
||||
| Access token error | Check if API credentials are valid and not expired |
|
||||
| Not logged in (browser) | First run opens browser - scan QR to log in |
|
||||
| Chrome not found | Set `WECHAT_BROWSER_CHROME_PATH` env var |
|
||||
|
||||
@@ -20,9 +20,9 @@ npx -y bun ./scripts/wechat-article.ts --markdown article.md --author "作者名
|
||||
| Parameter | Description |
|
||||
|-----------|-------------|
|
||||
| `--markdown <path>` | Markdown file to convert and post |
|
||||
| `--theme <name>` | Theme: default, grace, or simple |
|
||||
| `--theme <name>` | Theme: default, grace, simple, modern |
|
||||
| `--title <text>` | Override title (auto-extracted from markdown) |
|
||||
| `--author <name>` | Author name (default: 宝玉) |
|
||||
| `--author <name>` | Author name |
|
||||
| `--summary <text>` | Article summary |
|
||||
| `--html <path>` | Pre-rendered HTML file (alternative to markdown) |
|
||||
| `--profile <dir>` | Chrome profile directory |
|
||||
|
||||
@@ -49,14 +49,34 @@ header: "Theme"
|
||||
question: "Default theme for article conversion?"
|
||||
options:
|
||||
- label: "default (Recommended)"
|
||||
description: "Classic layout - centered title with border, white-on-color H2"
|
||||
description: "Classic layout - centered title with border, white-on-color H2 (default: blue)"
|
||||
- label: "grace"
|
||||
description: "Elegant - text shadows, rounded cards, refined blockquotes"
|
||||
description: "Elegant - text shadows, rounded cards, refined blockquotes (default: purple)"
|
||||
- label: "simple"
|
||||
description: "Minimal modern - asymmetric rounded corners, clean whitespace"
|
||||
description: "Minimal modern - asymmetric rounded corners, clean whitespace (default: green)"
|
||||
- label: "modern"
|
||||
description: "Large rounded corners, pill headings, spacious (default: orange)"
|
||||
```
|
||||
|
||||
### Question 2: Default Publishing Method
|
||||
### Question 2: Default Color
|
||||
|
||||
```yaml
|
||||
header: "Color"
|
||||
question: "Default color preset? (theme default if not set)"
|
||||
options:
|
||||
- label: "Theme default (Recommended)"
|
||||
description: "Use the theme's built-in default color"
|
||||
- label: "blue"
|
||||
description: "#0F4C81 经典蓝"
|
||||
- label: "red"
|
||||
description: "#A93226 中国红"
|
||||
- label: "green"
|
||||
description: "#009874 翡翠绿"
|
||||
```
|
||||
|
||||
Note: User can choose "Other" to type any preset name (vermilion, yellow, purple, sky, rose, olive, black, gray, pink, orange) or hex value.
|
||||
|
||||
### Question 3: Default Publishing Method
|
||||
|
||||
```yaml
|
||||
header: "Method"
|
||||
@@ -68,7 +88,7 @@ options:
|
||||
description: "Slow, requires Chrome and login session"
|
||||
```
|
||||
|
||||
### Question 3: Default Author
|
||||
### Question 4: Default Author
|
||||
|
||||
```yaml
|
||||
header: "Author"
|
||||
@@ -80,7 +100,7 @@ options:
|
||||
|
||||
Note: User will likely choose "Other" to type their author name.
|
||||
|
||||
### Question 4: Open Comments
|
||||
### Question 5: Open Comments
|
||||
|
||||
```yaml
|
||||
header: "Comments"
|
||||
@@ -92,7 +112,7 @@ options:
|
||||
description: "Disable comments by default"
|
||||
```
|
||||
|
||||
### Question 5: Fans-Only Comments
|
||||
### Question 6: Fans-Only Comments
|
||||
|
||||
```yaml
|
||||
header: "Fans only"
|
||||
@@ -104,7 +124,7 @@ options:
|
||||
description: "Only followers can comment"
|
||||
```
|
||||
|
||||
### Question 6: Save Location
|
||||
### Question 7: Save Location
|
||||
|
||||
```yaml
|
||||
header: "Save"
|
||||
@@ -133,7 +153,8 @@ options:
|
||||
## EXTEND.md Template
|
||||
|
||||
```md
|
||||
default_theme: [default/grace/simple]
|
||||
default_theme: [default/grace/simple/modern]
|
||||
default_color: [preset name, hex, or empty for theme default]
|
||||
default_publish_method: [api/browser]
|
||||
default_author: [author name or empty]
|
||||
need_open_comment: [1/0]
|
||||
|
||||
@@ -112,7 +112,7 @@ function parseFrontmatter(content: string): { frontmatter: Record<string, string
|
||||
return { frontmatter, body: match[2]! };
|
||||
}
|
||||
|
||||
export async function convertMarkdown(markdownPath: string, options?: { title?: string; theme?: string }): Promise<ParsedResult> {
|
||||
export async function convertMarkdown(markdownPath: string, options?: { title?: string; theme?: string; color?: string }): Promise<ParsedResult> {
|
||||
const baseDir = path.dirname(markdownPath);
|
||||
const content = fs.readFileSync(markdownPath, 'utf-8');
|
||||
const theme = options?.theme ?? 'default';
|
||||
@@ -183,9 +183,12 @@ export async function convertMarkdown(markdownPath: string, options?: { title?:
|
||||
const __dirname = path.dirname(__filename);
|
||||
const renderScript = path.join(__dirname, 'md', 'render.ts');
|
||||
|
||||
console.error(`[md-to-wechat] Rendering markdown with theme: ${theme}`);
|
||||
const renderArgs = ['-y', 'bun', renderScript, tempMdPath, '--theme', theme];
|
||||
if (options?.color) renderArgs.push('--color', options.color);
|
||||
|
||||
const result = spawnSync('npx', ['-y', 'bun', renderScript, tempMdPath, '--theme', theme], {
|
||||
console.error(`[md-to-wechat] Rendering markdown with theme: ${theme}${options?.color ? `, color: ${options.color}` : ''}`);
|
||||
|
||||
const result = spawnSync('npx', renderArgs, {
|
||||
stdio: ['inherit', 'pipe', 'pipe'],
|
||||
cwd: baseDir,
|
||||
});
|
||||
@@ -227,7 +230,8 @@ Usage:
|
||||
|
||||
Options:
|
||||
--title <title> Override title
|
||||
--theme <name> Theme name (default, grace, simple)
|
||||
--theme <name> Theme name (default, grace, simple, modern)
|
||||
--color <name|hex> Primary color (blue, green, vermilion, etc. or hex)
|
||||
--help Show this help
|
||||
|
||||
Output JSON format:
|
||||
@@ -246,6 +250,7 @@ Output JSON format:
|
||||
Example:
|
||||
npx -y bun md-to-wechat.ts article.md
|
||||
npx -y bun md-to-wechat.ts article.md --theme grace
|
||||
npx -y bun md-to-wechat.ts article.md --theme modern --color blue
|
||||
`);
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -259,6 +264,7 @@ async function main(): Promise<void> {
|
||||
let markdownPath: string | undefined;
|
||||
let title: string | undefined;
|
||||
let theme: string | undefined;
|
||||
let color: string | undefined;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i]!;
|
||||
@@ -266,6 +272,8 @@ async function main(): Promise<void> {
|
||||
title = args[++i];
|
||||
} else if (arg === '--theme' && args[i + 1]) {
|
||||
theme = args[++i];
|
||||
} else if (arg === '--color' && args[i + 1]) {
|
||||
color = args[++i];
|
||||
} else if (!arg.startsWith('-')) {
|
||||
markdownPath = arg;
|
||||
}
|
||||
@@ -281,7 +289,7 @@ async function main(): Promise<void> {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const result = await convertMarkdown(markdownPath, { title, theme });
|
||||
const result = await convertMarkdown(markdownPath, { title, theme, color });
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
import type { CliOptions, ThemeName } from "./types.js";
|
||||
import {
|
||||
FONT_FAMILY_MAP,
|
||||
FONT_SIZE_OPTIONS,
|
||||
COLOR_PRESETS,
|
||||
CODE_BLOCK_THEMES,
|
||||
} from "./constants.js";
|
||||
import { THEME_NAMES } from "./themes.js";
|
||||
import { loadExtendConfig } from "./extend-config.js";
|
||||
|
||||
export function printUsage(): void {
|
||||
console.error(
|
||||
[
|
||||
"Usage:",
|
||||
" npx tsx src/md/render.ts <markdown_file> [options]",
|
||||
"",
|
||||
"Options:",
|
||||
` --theme <name> Theme (${THEME_NAMES.join(", ")})`,
|
||||
` --color <name|hex> Primary color: ${Object.keys(COLOR_PRESETS).join(", ")}, or hex`,
|
||||
` --font-family <name> Font: ${Object.keys(FONT_FAMILY_MAP).join(", ")}, or CSS value`,
|
||||
` --font-size <N> Font size: ${FONT_SIZE_OPTIONS.join(", ")} (default: 16px)`,
|
||||
` --code-theme <name> Code highlight theme (default: github)`,
|
||||
` --mac-code-block Show Mac-style code block header`,
|
||||
` --line-number Show line numbers in code blocks`,
|
||||
` --cite Enable footnote citations`,
|
||||
` --count Show reading time / word count`,
|
||||
` --legend <value> Image caption: title-alt, alt-title, title, alt, none`,
|
||||
` --keep-title Keep the first heading in output`,
|
||||
].join("\n")
|
||||
);
|
||||
}
|
||||
|
||||
function parseArgValue(argv: string[], i: number, flag: string): string | null {
|
||||
const arg = argv[i]!;
|
||||
if (arg.includes("=")) {
|
||||
return arg.slice(flag.length + 1);
|
||||
}
|
||||
const next = argv[i + 1];
|
||||
return next ?? null;
|
||||
}
|
||||
|
||||
function resolveFontFamily(value: string): string {
|
||||
return FONT_FAMILY_MAP[value] ?? value;
|
||||
}
|
||||
|
||||
function resolveColor(value: string): string {
|
||||
return COLOR_PRESETS[value] ?? value;
|
||||
}
|
||||
|
||||
export function parseArgs(argv: string[]): CliOptions | null {
|
||||
const ext = loadExtendConfig();
|
||||
|
||||
let inputPath = "";
|
||||
let theme: ThemeName = ext.default_theme ?? "default";
|
||||
let keepTitle = ext.keep_title ?? false;
|
||||
let primaryColor: string | undefined = ext.default_color ? resolveColor(ext.default_color) : undefined;
|
||||
let fontFamily: string | undefined = ext.default_font_family ? resolveFontFamily(ext.default_font_family) : undefined;
|
||||
let fontSize: string | undefined = ext.default_font_size ?? undefined;
|
||||
let codeTheme = ext.default_code_theme ?? "github";
|
||||
let isMacCodeBlock = ext.mac_code_block ?? true;
|
||||
let isShowLineNumber = ext.show_line_number ?? false;
|
||||
let citeStatus = ext.cite ?? false;
|
||||
let countStatus = ext.count ?? false;
|
||||
let legend = ext.legend ?? "alt";
|
||||
|
||||
for (let i = 0; i < argv.length; i += 1) {
|
||||
const arg = argv[i]!;
|
||||
|
||||
if (!arg.startsWith("--") && !inputPath) {
|
||||
inputPath = arg;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === "--help" || arg === "-h") {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (arg === "--keep-title") { keepTitle = true; continue; }
|
||||
if (arg === "--mac-code-block") { isMacCodeBlock = true; continue; }
|
||||
if (arg === "--no-mac-code-block") { isMacCodeBlock = false; continue; }
|
||||
if (arg === "--line-number") { isShowLineNumber = true; continue; }
|
||||
if (arg === "--cite") { citeStatus = true; continue; }
|
||||
if (arg === "--count") { countStatus = true; continue; }
|
||||
|
||||
if (arg === "--theme" || arg.startsWith("--theme=")) {
|
||||
const val = parseArgValue(argv, i, "--theme");
|
||||
if (!val) { console.error("Missing value for --theme"); return null; }
|
||||
theme = val as ThemeName;
|
||||
if (!arg.includes("=")) i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === "--color" || arg.startsWith("--color=")) {
|
||||
const val = parseArgValue(argv, i, "--color");
|
||||
if (!val) { console.error("Missing value for --color"); return null; }
|
||||
primaryColor = resolveColor(val);
|
||||
if (!arg.includes("=")) i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === "--font-family" || arg.startsWith("--font-family=")) {
|
||||
const val = parseArgValue(argv, i, "--font-family");
|
||||
if (!val) { console.error("Missing value for --font-family"); return null; }
|
||||
fontFamily = resolveFontFamily(val);
|
||||
if (!arg.includes("=")) i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === "--font-size" || arg.startsWith("--font-size=")) {
|
||||
const val = parseArgValue(argv, i, "--font-size");
|
||||
if (!val) { console.error("Missing value for --font-size"); return null; }
|
||||
fontSize = val.endsWith("px") ? val : `${val}px`;
|
||||
if (!FONT_SIZE_OPTIONS.includes(fontSize)) {
|
||||
console.error(`Invalid font size: ${fontSize}. Valid: ${FONT_SIZE_OPTIONS.join(", ")}`);
|
||||
return null;
|
||||
}
|
||||
if (!arg.includes("=")) i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === "--code-theme" || arg.startsWith("--code-theme=")) {
|
||||
const val = parseArgValue(argv, i, "--code-theme");
|
||||
if (!val) { console.error("Missing value for --code-theme"); return null; }
|
||||
codeTheme = val;
|
||||
if (!CODE_BLOCK_THEMES.includes(codeTheme)) {
|
||||
console.error(`Unknown code theme: ${codeTheme}`);
|
||||
return null;
|
||||
}
|
||||
if (!arg.includes("=")) i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === "--legend" || arg.startsWith("--legend=")) {
|
||||
const val = parseArgValue(argv, i, "--legend");
|
||||
if (!val) { console.error("Missing value for --legend"); return null; }
|
||||
const valid = ["title-alt", "alt-title", "title", "alt", "none"];
|
||||
if (!valid.includes(val)) {
|
||||
console.error(`Invalid legend: ${val}. Valid: ${valid.join(", ")}`);
|
||||
return null;
|
||||
}
|
||||
legend = val;
|
||||
if (!arg.includes("=")) i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
console.error(`Unknown argument: ${arg}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!inputPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!THEME_NAMES.includes(theme)) {
|
||||
console.error(`Unknown theme: ${theme}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
inputPath, theme, keepTitle, primaryColor, fontFamily, fontSize,
|
||||
codeTheme, isMacCodeBlock, isShowLineNumber, citeStatus, countStatus, legend,
|
||||
};
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user