排版引擎大升级:CJK修复 + 外链脚注 + 暗黑模式 + 容器语法 + 16主题 + 画廊UI

converter.py 新增 6 项能力:
- CJK-Latin 自动加空格(中英混排更易读)
- 加粗标点外移(修复微信渲染 bug)
- ul/ol 转 section(微信原生列表不稳定)
- 外链→编号脚注 + 文末参考链接(微信屏蔽外链)
- data-darkmode-* 属性注入(适配微信暗黑模式)
- :::dialogue / :::timeline / :::callout / :::quote 容器语法

主题系统:
- 从 4 个扩充到 16 个(含字节/少数派/报纸/包豪斯/水墨/午夜等风格)
- 所有主题新增 darkmode 色值
- 新增 gallery 命令:浏览器内 16 主题并排预览 + 一键复制

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
wangzhuc 2026-03-28 22:53:28 +08:00
parent d4ca5ba9ef
commit 2fa0d7fa6d
18 changed files with 2909 additions and 0 deletions

View file

@ -142,6 +142,164 @@ def cmd_themes(args):
print(f" {name:24s} {theme.description}")
def cmd_gallery(args):
"""Render all themes side by side in a browser gallery."""
from concurrent.futures import ThreadPoolExecutor
# Use provided markdown or a built-in sample
if args.input:
md_text = Path(args.input).read_text(encoding="utf-8")
else:
md_text = _gallery_sample_markdown()
names = list_themes()
results = {}
def render_theme(name):
theme = load_theme(name)
converter = WeChatConverter(theme=theme)
result = converter.convert(md_text)
return name, theme.description, result.html
# Parallel rendering
with ThreadPoolExecutor(max_workers=8) as pool:
for name, desc, html in pool.map(lambda n: render_theme(n), names):
results[name] = (desc, html)
# Build gallery HTML
gallery_html = _build_gallery_html(results, names)
output = args.output or "/tmp/wewrite-gallery.html"
Path(output).write_text(gallery_html, encoding="utf-8")
print(f"Gallery: {output} ({len(names)} themes)")
if not args.no_open:
webbrowser.open(f"file://{Path(output).absolute()}")
def _gallery_sample_markdown():
return """# 示例文章标题
## 第一部分
这是一段正常的文章内容用来展示不同主题的排版效果WeWrite 支持多种排版主题每种都有独特的视觉风格
说实话选主题这件事看截图永远不如看实际渲染效果
## 关键数据
| 指标 | 数值 | 变化 |
|------|------|------|
| 阅读量 | 12,580 | +23% |
| 分享率 | 4.7% | +0.8% |
| 完读率 | 68% | -2% |
## 代码示例
```python
def hello():
print("Hello, WeWrite!")
```
> 好的排版不是让读者注意到设计而是让读者忘记设计只记住内容
## 列表展示
- 第一个要点简洁是设计的灵魂
- 第二个要点一致性比创意更重要
- 第三个要点移动端体验优先
**加粗文本***斜体文本*的样式也需要关注
最后这段用来展示文章结尾的留白和间距效果一篇好文章的结尾应该像一首好歌的最后一个音符恰到好处地收束
"""
def _join_newline(items):
"""Join items with comma + newline (workaround for f-string limitation)."""
return ",\n".join(items)
def _build_gallery_html(results, names):
cards = []
for name in names:
desc, html = results[name]
# Escape for embedding in JS
escaped_html = html.replace('\\', '\\\\').replace('`', '\\`').replace('$', '\\$')
cards.append(f"""
<div class="theme-card" onclick="selectTheme('{name}')">
<div class="theme-name">{name}</div>
<div class="theme-desc">{desc}</div>
<div class="phone-frame">
<div class="phone-content" id="preview-{name}">{html}</div>
</div>
<button class="copy-btn" onclick="event.stopPropagation(); copyHTML('{name}')">复制 HTML</button>
</div>""")
# Store HTML data for copy
data_entries = []
for name in names:
desc, html = results[name]
safe = html.replace('\\', '\\\\').replace("'", "\\'").replace('\n', '\\n')
data_entries.append(f" '{name}': '{safe}'")
return f"""<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WeWrite 主题画廊</title>
<style>
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
body {{ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: #0f0f0f; color: #fff; }}
.header {{ text-align: center; padding: 40px 20px 20px; }}
.header h1 {{ font-size: 28px; font-weight: 700; }}
.header p {{ color: #888; margin-top: 8px; font-size: 15px; }}
.grid {{ display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 24px; padding: 24px; max-width: 1440px; margin: 0 auto; }}
.theme-card {{ background: #1a1a1a; border-radius: 12px; padding: 16px; cursor: pointer; transition: transform 0.2s, box-shadow 0.2s; }}
.theme-card:hover {{ transform: translateY(-4px); box-shadow: 0 8px 24px rgba(0,0,0,0.4); }}
.theme-name {{ font-size: 16px; font-weight: 700; margin-bottom: 4px; }}
.theme-desc {{ font-size: 13px; color: #888; margin-bottom: 12px; }}
.phone-frame {{ background: #fff; border-radius: 8px; overflow: hidden; max-height: 480px; overflow-y: auto; }}
.phone-content {{ padding: 16px; font-size: 14px; transform: scale(0.85); transform-origin: top left; width: 118%; }}
.copy-btn {{ margin-top: 12px; width: 100%; padding: 8px; background: #333; color: #fff; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; }}
.copy-btn:hover {{ background: #555; }}
.toast {{ position: fixed; bottom: 40px; left: 50%; transform: translateX(-50%); background: #333; color: #fff; padding: 10px 24px; border-radius: 8px; font-size: 14px; display: none; z-index: 999; }}
</style>
</head>
<body>
<div class="header">
<h1>WeWrite 主题画廊</h1>
<p>{len(names)} 个主题 · 点击卡片查看大图 · 点击复制 HTML直接粘贴到公众号编辑器</p>
</div>
<div class="grid">
{''.join(cards)}
</div>
<div class="toast" id="toast">已复制到剪贴板</div>
<script>
const themeData = {{
{_join_newline(data_entries)}
}};
function copyHTML(name) {{
const html = themeData[name];
if (html) {{
navigator.clipboard.writeText(html).then(() => {{
const t = document.getElementById('toast');
t.style.display = 'block';
setTimeout(() => t.style.display = 'none', 1500);
}});
}}
}}
function selectTheme(name) {{
localStorage.setItem('wewrite-theme', name);
// Scroll to card for visual feedback
const el = document.getElementById('preview-' + name);
if (el) el.scrollIntoView({{ behavior: 'smooth', block: 'center' }});
}}
</script>
</body>
</html>"""
def main():
parser = argparse.ArgumentParser(
prog="wewrite",
@ -169,6 +327,12 @@ def main():
# themes
sub.add_parser("themes", help="List available themes")
# gallery
p_gallery = sub.add_parser("gallery", help="Open theme gallery in browser")
p_gallery.add_argument("input", nargs="?", default=None, help="Markdown file (optional, uses sample if omitted)")
p_gallery.add_argument("-o", "--output", help="Output HTML file path")
p_gallery.add_argument("--no-open", action="store_true", help="Don't open browser")
args = parser.parse_args()
try:
@ -178,6 +342,8 @@ def main():
cmd_publish(args)
elif args.command == "themes":
cmd_themes(args)
elif args.command == "gallery":
cmd_gallery(args)
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)

View file

@ -49,6 +49,12 @@ class WeChatConverter:
title = self._extract_title(markdown_text)
markdown_text = self._strip_h1(markdown_text)
# Pre-process container blocks (:::dialogue, :::timeline, etc.)
markdown_text = self._preprocess_containers(markdown_text)
# CJK fix: auto-space between CJK and Latin characters
markdown_text = self._fix_cjk_spacing(markdown_text)
# Parse Markdown → HTML
html = self._markdown_to_html(markdown_text)
@ -58,12 +64,24 @@ class WeChatConverter:
# Process images (ensure responsive styling)
html, images = self._process_images(html)
# CJK fix: move punctuation outside bold tags
html = self._fix_cjk_bold_punctuation(html)
# CJK fix: convert ul/ol to section-based lists (WeChat renders native lists unreliably)
html = self._convert_lists_to_sections(html)
# Convert external links to footnotes (WeChat blocks external links)
html = self._convert_links_to_footnotes(html)
# Apply inline CSS from theme
html = self._apply_inline_styles(html)
# Apply WeChat compatibility fixes
html = self._apply_wechat_fixes(html)
# Inject dark mode attributes
html = self._inject_darkmode(html)
# Generate digest from plain text
digest = self._generate_digest(html)
@ -201,6 +219,294 @@ class WeChatConverter:
return str(soup)
# -- CJK compatibility fixes --
def _fix_cjk_spacing(self, text: str) -> str:
"""Auto-insert thin space between CJK and Latin/digit characters.
WeChat renders CJK-Latin without spacing, making mixed text hard to read.
This inserts a thin space (U+200A) at CJKLatin boundaries.
Runs on raw Markdown before parsing, skipping code blocks and links.
"""
# CJK unicode ranges
cjk = r'[\u4e00-\u9fff\u3400-\u4dbf\u3000-\u303f\uff00-\uffef]'
latin = r'[A-Za-z0-9]'
lines = text.split('\n')
result = []
in_code_block = False
for line in lines:
if line.strip().startswith('```'):
in_code_block = not in_code_block
result.append(line)
continue
if in_code_block:
result.append(line)
continue
# CJK followed by Latin
line = re.sub(f'({cjk})({latin})', r'\1 \2', line)
# Latin followed by CJK
line = re.sub(f'({latin})({cjk})', r'\1 \2', line)
result.append(line)
return '\n'.join(result)
def _fix_cjk_bold_punctuation(self, html: str) -> str:
"""Move Chinese punctuation outside bold/strong tags.
WeChat renders bold CJK punctuation with ugly spacing.
Move trailing punctuation () outside </strong>.
"""
# Match: <strong>内容+中文标点</strong> → <strong>内容</strong>标点
pattern = r'(<strong>)(.*?)([,。!?;:、]+)(</strong>)'
return re.sub(pattern, r'\1\2\4\3', html)
def _convert_lists_to_sections(self, html: str) -> str:
"""Convert <ul>/<ol> to styled <section> elements.
WeChat's native list rendering is unreliable (inconsistent bullet
style, broken indentation on some devices). Using section+span
for bullets/numbers gives full control over appearance.
"""
soup = BeautifulSoup(html, "html.parser")
text_color = self._theme.colors.get("text", "#333333")
primary = self._theme.colors.get("primary", "#2563eb")
for ul in soup.find_all("ul"):
section = soup.new_tag("section")
for li in ul.find_all("li", recursive=False):
item = soup.new_tag("section", style=f"display: flex; align-items: flex-start; margin-bottom: 8px; color: {text_color}")
bullet = soup.new_tag("span", style=f"color: {primary}; margin-right: 8px; flex-shrink: 0; font-size: 18px; line-height: 1.6")
bullet.string = ""
content = soup.new_tag("span", style="flex: 1")
for child in list(li.children):
content.append(child.extract() if hasattr(child, 'extract') else child)
item.append(bullet)
item.append(content)
section.append(item)
ul.replace_with(section)
for idx, ol in enumerate(soup.find_all("ol")):
section = soup.new_tag("section")
for num, li in enumerate(ol.find_all("li", recursive=False), 1):
item = soup.new_tag("section", style=f"display: flex; align-items: flex-start; margin-bottom: 8px; color: {text_color}")
number = soup.new_tag("span", style=f"color: {primary}; margin-right: 8px; flex-shrink: 0; font-weight: 700; line-height: 1.8")
number.string = f"{num}."
content = soup.new_tag("span", style="flex: 1")
for child in list(li.children):
content.append(child.extract() if hasattr(child, 'extract') else child)
item.append(number)
item.append(content)
section.append(item)
ol.replace_with(section)
return str(soup)
# -- External link → footnote conversion --
def _convert_links_to_footnotes(self, html: str) -> str:
"""Convert external <a> links to superscript footnote numbers.
WeChat blocks external links readers see dead text. This converts
each external link to a superscript number with the URL collected
into a reference list appended at the end.
"""
soup = BeautifulSoup(html, "html.parser")
footnotes = []
counter = 0
primary = self._theme.colors.get("primary", "#2563eb")
for a in soup.find_all("a"):
href = a.get("href", "")
if not href or href.startswith("#"):
continue # skip anchors
counter += 1
text = a.get_text()
footnotes.append((counter, text, href))
# Replace <a> with text + superscript number
sup = soup.new_tag("sup")
sup_link = soup.new_tag("span", style=f"color: {primary}; font-size: 12px")
sup_link.string = f"[{counter}]"
sup.append(sup_link)
a.replace_with(text, sup)
if footnotes:
# Append reference section
hr = soup.new_tag("hr", style="border: none; border-top: 1px solid #e5e5e5; margin: 32px 0 16px")
soup.append(hr)
ref_title = soup.new_tag("p", style="font-size: 13px; color: #999999; margin-bottom: 8px; font-weight: 700")
ref_title.string = "参考链接"
soup.append(ref_title)
for num, text, href in footnotes:
ref = soup.new_tag("p", style="font-size: 12px; color: #999999; margin: 2px 0; word-break: break-all")
ref.string = f"[{num}] {text}: {href}"
soup.append(ref)
return str(soup)
# -- Dark mode --
def _inject_darkmode(self, html: str) -> str:
"""Inject data-darkmode-* attributes for WeChat dark mode.
WeChat auto-inverts colors in dark mode, which often breaks
designed color schemes. Explicit darkmode attributes tell WeChat
exactly what colors to use instead of guessing.
"""
darkmode = self._theme.colors.get("darkmode", {})
if not darkmode:
return html
soup = BeautifulSoup(html, "html.parser")
dm_text = darkmode.get("text", "#c8c8c8")
dm_bg = darkmode.get("background", "#1e1e1e")
dm_primary = darkmode.get("primary", "#6aadff")
# Body-level elements (p, li, section, span)
for tag_name in ("p", "span", "section"):
for elem in soup.find_all(tag_name):
style = elem.get("style", "")
# Only set if element has a color
if "color" in style:
elem["data-darkmode-color"] = dm_text
elem["data-darkmode-bgcolor"] = "transparent"
# Headings
dm_heading = darkmode.get("text", "#e0e0e0")
for tag_name in ("h1", "h2", "h3", "h4"):
for elem in soup.find_all(tag_name):
elem["data-darkmode-color"] = dm_heading
elem["data-darkmode-bgcolor"] = "transparent"
# Code blocks
dm_code_bg = darkmode.get("code_bg", "#2d2d2d")
dm_code_color = darkmode.get("code_color", "#d4d4d4")
for pre in soup.find_all("pre"):
pre["data-darkmode-bgcolor"] = dm_code_bg
pre["data-darkmode-color"] = dm_code_color
for code in soup.find_all("code"):
code["data-darkmode-color"] = dm_code_color
# Blockquotes
dm_quote_bg = darkmode.get("quote_bg", "#2a2a2a")
for bq in soup.find_all("blockquote"):
bq["data-darkmode-bgcolor"] = dm_quote_bg
bq["data-darkmode-color"] = dm_text
# Strong/em with primary color
for strong in soup.find_all("strong"):
strong["data-darkmode-color"] = dm_primary
return str(soup)
# -- Container block syntax --
def _preprocess_containers(self, text: str) -> str:
"""Pre-process :::container blocks into styled HTML before Markdown parsing.
Supports:
:::dialogue chat bubble layout
:::timeline vertical timeline with dots
:::callout Obsidian-style callout (tip/warning/info/danger)
:::quote styled pull quote
"""
text = self._process_dialogue(text)
text = self._process_timeline(text)
text = self._process_callout(text)
text = self._process_quote_block(text)
return text
def _process_dialogue(self, text: str) -> str:
"""Convert :::dialogue blocks to chat bubble HTML."""
primary = self._theme.colors.get("primary", "#2563eb")
def replace_dialogue(match):
content = match.group(1).strip()
bubbles = []
for line in content.split('\n'):
line = line.strip()
if not line:
continue
if line.startswith('> '):
# Right-aligned (reply) bubble
msg = line[2:].strip()
bubbles.append(f'<section style="display: flex; justify-content: flex-end; margin-bottom: 12px">'
f'<section style="background: {primary}; color: white; padding: 10px 14px; border-radius: 12px 12px 2px 12px; max-width: 80%; font-size: 15px; line-height: 1.6">{msg}</section></section>')
else:
# Left-aligned bubble
bubbles.append(f'<section style="display: flex; justify-content: flex-start; margin-bottom: 12px">'
f'<section style="background: #f3f4f6; color: #333; padding: 10px 14px; border-radius: 12px 12px 12px 2px; max-width: 80%; font-size: 15px; line-height: 1.6">{line}</section></section>')
return '\n'.join(bubbles)
return re.sub(r':::dialogue\n(.*?)\n:::', replace_dialogue, text, flags=re.DOTALL)
def _process_timeline(self, text: str) -> str:
"""Convert :::timeline blocks to vertical timeline HTML."""
primary = self._theme.colors.get("primary", "#2563eb")
def replace_timeline(match):
content = match.group(1).strip()
items = []
for line in content.split('\n'):
line = line.strip()
if not line:
continue
# Format: "**title** description" or just "description"
items.append(
f'<section style="display: flex; margin-bottom: 16px">'
f'<section style="flex-shrink: 0; width: 12px; display: flex; flex-direction: column; align-items: center">'
f'<section style="width: 10px; height: 10px; border-radius: 50%; background: {primary}; margin-top: 6px"></section>'
f'<section style="width: 2px; flex: 1; background: #e5e7eb; margin-top: 4px"></section>'
f'</section>'
f'<section style="flex: 1; padding-left: 12px; padding-bottom: 8px; font-size: 15px; line-height: 1.7">{line}</section>'
f'</section>'
)
return '\n'.join(items)
return re.sub(r':::timeline\n(.*?)\n:::', replace_timeline, text, flags=re.DOTALL)
def _process_callout(self, text: str) -> str:
"""Convert :::callout blocks to styled callout boxes.
Syntax: :::callout tip/warning/info/danger
"""
colors_map = {
"tip": ("#059669", "#ecfdf5", "💡"),
"warning": ("#d97706", "#fffbeb", "⚠️"),
"info": ("#2563eb", "#eff6ff", ""),
"danger": ("#dc2626", "#fef2f2", "🚨"),
}
def replace_callout(match):
ctype = match.group(1).strip().lower()
content = match.group(2).strip()
color, bg, icon = colors_map.get(ctype, colors_map["info"])
return (f'<section style="background: {bg}; border-left: 4px solid {color}; '
f'padding: 14px 16px; border-radius: 4px; margin: 16px 0; font-size: 15px; line-height: 1.7">'
f'<section style="font-weight: 700; color: {color}; margin-bottom: 6px">{icon} {ctype.upper()}</section>'
f'{content}</section>')
return re.sub(r':::callout\s+(\w+)\n(.*?)\n:::', replace_callout, text, flags=re.DOTALL)
def _process_quote_block(self, text: str) -> str:
"""Convert :::quote blocks to styled pull quotes."""
primary = self._theme.colors.get("primary", "#2563eb")
def replace_quote(match):
content = match.group(1).strip()
return (f'<section style="margin: 24px 0; padding: 20px 24px; border-left: 4px solid {primary}; '
f'background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%); border-radius: 0 8px 8px 0">'
f'<section style="font-size: 18px; line-height: 1.8; color: #333; font-style: italic">'
f'"{content}"</section></section>')
return re.sub(r':::quote\n(.*?)\n:::', replace_quote, text, flags=re.DOTALL)
# -- Digest generation --
def _generate_digest(self, html: str, max_bytes: int = 120) -> str:
"""Generate a digest that fits within WeChat's byte limit (120 bytes UTF-8)."""
soup = BeautifulSoup(html, "html.parser")

207
toolkit/themes/bauhaus.yaml Normal file
View file

@ -0,0 +1,207 @@
name: "bauhaus"
description: "包豪斯设计风格:纯白底黑色为主,红蓝黄色块点缀,几何感强烈"
colors:
primary: "#e63226"
secondary: "#004592"
text: "#1a1a1a"
text_light: "#555555"
background: "#ffffff"
code_bg: "#f0f0f0"
code_color: "#004592"
quote_border: "#e63226"
quote_bg: "#fff5f5"
border_radius: "0px"
darkmode:
background: "#111111"
text: "#e8e8e8"
text_light: "#a0a0a0"
primary: "#f04438"
code_bg: "#222222"
code_color: "#5b9bd5"
quote_bg: "#1e1212"
quote_border: "#f04438"
base_css: |
body {
font-family: "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
font-size: 16px;
line-height: 1.75;
color: #1a1a1a;
background: #ffffff;
max-width: 720px;
margin: 0 auto;
padding: 20px;
word-wrap: break-word;
}
h1 {
font-size: 30px;
font-weight: 900;
color: #1a1a1a;
margin: 36px 0 18px 0;
padding: 12px 16px;
background: #e63226;
color: #ffffff;
line-height: 1.3;
text-transform: uppercase;
letter-spacing: 0.04em;
}
h2 {
font-size: 22px;
font-weight: 800;
color: #004592;
margin: 32px 0 14px 0;
padding: 8px 0;
border-bottom: 4px solid #1a1a1a;
line-height: 1.4;
}
h3 {
font-size: 18px;
font-weight: 700;
color: #1a1a1a;
margin: 24px 0 12px 0;
padding-left: 12px;
border-left: 6px solid #f5b700;
line-height: 1.4;
}
h4 {
font-size: 16px;
font-weight: 700;
color: #1a1a1a;
margin: 20px 0 10px 0;
line-height: 1.4;
}
p {
font-size: 16px;
line-height: 1.75;
color: #1a1a1a;
margin: 12px 0;
}
strong {
font-weight: 800;
color: #e63226;
}
em {
font-style: italic;
color: #555555;
}
code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: #f0f0f0;
color: #004592;
padding: 2px 6px;
border-radius: 0px;
border: 1px solid #ddd;
}
pre {
background: #1a1a1a;
color: #f0f0f0;
padding: 16px;
border-radius: 0px;
overflow-x: auto;
margin: 16px 0;
line-height: 1.6;
border-left: 6px solid #e63226;
}
pre code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: none;
color: #f0f0f0;
padding: 0;
border-radius: 0;
border: none;
}
blockquote {
border-left: 6px solid #e63226;
background: #fff5f5;
margin: 16px 0;
padding: 12px 16px;
border-radius: 0;
color: #1a1a1a;
}
blockquote p {
margin: 8px 0;
color: #1a1a1a;
}
ul {
padding-left: 24px;
margin: 12px 0;
}
ol {
padding-left: 24px;
margin: 12px 0;
}
li {
font-size: 16px;
line-height: 1.75;
color: #1a1a1a;
margin: 6px 0;
}
table {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
font-size: 15px;
}
thead {
background: #004592;
}
th {
background: #004592;
color: #ffffff;
font-weight: 700;
padding: 10px 14px;
text-align: left;
border: 2px solid #1a1a1a;
}
td {
padding: 10px 14px;
border: 2px solid #1a1a1a;
color: #1a1a1a;
}
tr {
background: #ffffff;
}
img {
max-width: 100%;
height: auto;
display: block;
margin: 24px auto;
border-radius: 0px;
border: 2px solid #1a1a1a;
}
a {
color: #004592;
text-decoration: none;
font-weight: 700;
border-bottom: 2px solid #f5b700;
}
hr {
border: none;
height: 4px;
background: #1a1a1a;
margin: 28px 0;
}

View file

@ -0,0 +1,198 @@
name: "bold-green"
description: "大胆森林绿风格:白底绿色主色,清新自然,适合环保健康和可持续发展内容"
colors:
primary: "#16a34a"
secondary: "#22c55e"
text: "#1a2e1a"
text_light: "#4a6a4a"
background: "#ffffff"
code_bg: "#f0fdf4"
code_color: "#15803d"
quote_border: "#16a34a"
quote_bg: "#f0fdf4"
border_radius: "8px"
darkmode:
background: "#0f1a0f"
text: "#d8e8d8"
text_light: "#8aaa8a"
primary: "#4ade80"
code_bg: "#162816"
code_color: "#6ee7a0"
quote_bg: "#142014"
quote_border: "#4ade80"
base_css: |
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
font-size: 16px;
line-height: 1.8;
color: #1a2e1a;
background: #ffffff;
max-width: 720px;
margin: 0 auto;
padding: 20px;
word-wrap: break-word;
}
h1 {
font-size: 26px;
font-weight: 700;
color: #ffffff;
margin: 32px 0 16px 0;
padding: 12px 16px;
background: #16a34a;
border-radius: 8px;
line-height: 1.4;
}
h2 {
font-size: 22px;
font-weight: 700;
color: #16a34a;
margin: 28px 0 14px 0;
padding: 8px 0 8px 12px;
border-left: 4px solid #16a34a;
line-height: 1.4;
}
h3 {
font-size: 18px;
font-weight: 600;
color: #15803d;
margin: 24px 0 12px 0;
line-height: 1.4;
}
h4 {
font-size: 16px;
font-weight: 600;
color: #1a2e1a;
margin: 20px 0 10px 0;
line-height: 1.4;
}
p {
font-size: 16px;
line-height: 1.8;
color: #1a2e1a;
margin: 12px 0;
}
strong {
font-weight: 700;
color: #16a34a;
}
em {
font-style: italic;
color: #4a6a4a;
}
code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: #f0fdf4;
color: #15803d;
padding: 2px 6px;
border-radius: 4px;
}
pre {
background: #f0fdf4;
color: #1a2e1a;
padding: 16px;
border-radius: 8px;
overflow-x: auto;
margin: 16px 0;
line-height: 1.6;
border: 1px solid #bbf7d0;
}
pre code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: none;
color: #1a2e1a;
padding: 0;
border-radius: 0;
}
blockquote {
border-left: 4px solid #16a34a;
background: #f0fdf4;
margin: 16px 0;
padding: 12px 16px;
border-radius: 0 8px 8px 0;
color: #1a2e1a;
}
blockquote p {
margin: 8px 0;
color: #1a2e1a;
}
ul {
padding-left: 24px;
margin: 12px 0;
}
ol {
padding-left: 24px;
margin: 12px 0;
}
li {
font-size: 16px;
line-height: 1.8;
color: #1a2e1a;
margin: 6px 0;
}
table {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
font-size: 15px;
}
thead {
background: #16a34a;
}
th {
background: #16a34a;
color: #ffffff;
font-weight: 600;
padding: 10px 14px;
text-align: left;
border: 1px solid #16a34a;
}
td {
padding: 10px 14px;
border: 1px solid #d1fae5;
color: #1a2e1a;
}
tr {
background: #ffffff;
}
img {
max-width: 100%;
height: auto;
display: block;
margin: 24px auto;
border-radius: 8px;
}
a {
color: #16a34a;
text-decoration: none;
font-weight: 500;
}
hr {
border: none;
border-top: 2px solid #d1fae5;
margin: 24px 0;
}

View file

@ -0,0 +1,197 @@
name: "bold-navy"
description: "大胆藏青风格:白底藏青主色,稳重专业,适合金融商务和行业分析内容"
colors:
primary: "#1e3a5f"
secondary: "#2c5282"
text: "#1a1a2e"
text_light: "#4a5568"
background: "#ffffff"
code_bg: "#f0f4f8"
code_color: "#1e3a5f"
quote_border: "#1e3a5f"
quote_bg: "#f0f4f8"
border_radius: "6px"
darkmode:
background: "#0a0f1a"
text: "#d8dce8"
text_light: "#8890a0"
primary: "#5b8cc8"
code_bg: "#141a28"
code_color: "#7ca8e0"
quote_bg: "#101828"
quote_border: "#5b8cc8"
base_css: |
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
font-size: 16px;
line-height: 1.8;
color: #1a1a2e;
background: #ffffff;
max-width: 720px;
margin: 0 auto;
padding: 20px;
word-wrap: break-word;
}
h1 {
font-size: 26px;
font-weight: 700;
color: #1e3a5f;
margin: 32px 0 16px 0;
padding-bottom: 12px;
border-bottom: 3px solid #1e3a5f;
line-height: 1.4;
}
h2 {
font-size: 22px;
font-weight: 700;
color: #1e3a5f;
margin: 28px 0 14px 0;
padding: 8px 0 8px 12px;
border-left: 4px solid #1e3a5f;
border-bottom: 1px solid #e2e8f0;
line-height: 1.4;
}
h3 {
font-size: 18px;
font-weight: 600;
color: #2c5282;
margin: 24px 0 12px 0;
line-height: 1.4;
}
h4 {
font-size: 16px;
font-weight: 600;
color: #1a1a2e;
margin: 20px 0 10px 0;
line-height: 1.4;
}
p {
font-size: 16px;
line-height: 1.8;
color: #1a1a2e;
margin: 12px 0;
}
strong {
font-weight: 700;
color: #1e3a5f;
}
em {
font-style: italic;
color: #4a5568;
}
code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: #f0f4f8;
color: #1e3a5f;
padding: 2px 6px;
border-radius: 4px;
}
pre {
background: #1e3a5f;
color: #e2e8f0;
padding: 16px;
border-radius: 6px;
overflow-x: auto;
margin: 16px 0;
line-height: 1.6;
}
pre code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: none;
color: #e2e8f0;
padding: 0;
border-radius: 0;
}
blockquote {
border-left: 4px solid #1e3a5f;
background: #f0f4f8;
margin: 16px 0;
padding: 12px 16px;
border-radius: 0 6px 6px 0;
color: #2c5282;
}
blockquote p {
margin: 8px 0;
color: #2c5282;
}
ul {
padding-left: 24px;
margin: 12px 0;
}
ol {
padding-left: 24px;
margin: 12px 0;
}
li {
font-size: 16px;
line-height: 1.8;
color: #1a1a2e;
margin: 6px 0;
}
table {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
font-size: 15px;
}
thead {
background: #1e3a5f;
}
th {
background: #1e3a5f;
color: #ffffff;
font-weight: 600;
padding: 10px 14px;
text-align: left;
border: 1px solid #1e3a5f;
}
td {
padding: 10px 14px;
border: 1px solid #e2e8f0;
color: #1a1a2e;
}
tr {
background: #ffffff;
}
img {
max-width: 100%;
height: auto;
display: block;
margin: 24px auto;
border-radius: 6px;
}
a {
color: #2c5282;
text-decoration: none;
font-weight: 500;
}
hr {
border: none;
border-top: 2px solid #e2e8f0;
margin: 24px 0;
}

View file

@ -0,0 +1,199 @@
name: "bytedance"
description: "字节跳动风:白底品牌蓝,现代无衬线,大间距,适合科技产品内容"
colors:
primary: "#1966FF"
secondary: "#4e8fff"
text: "#1f2329"
text_light: "#646a73"
background: "#ffffff"
code_bg: "#f5f6f7"
code_color: "#1966FF"
quote_border: "#1966FF"
quote_bg: "#f0f5ff"
border_radius: "8px"
darkmode:
background: "#1a1a1a"
text: "#e8e8e8"
text_light: "#a0a0a0"
primary: "#4e8fff"
code_bg: "#2a2a2a"
code_color: "#6ea8fe"
quote_bg: "#1e2a3a"
quote_border: "#4e8fff"
base_css: |
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
font-size: 16px;
line-height: 2;
color: #1f2329;
background: #ffffff;
max-width: 720px;
margin: 0 auto;
padding: 24px;
word-wrap: break-word;
letter-spacing: 0.02em;
}
h1 {
font-size: 28px;
font-weight: 800;
color: #1f2329;
margin: 40px 0 20px 0;
padding-bottom: 14px;
border-bottom: 3px solid #1966FF;
line-height: 1.4;
letter-spacing: -0.01em;
}
h2 {
font-size: 22px;
font-weight: 700;
color: #1f2329;
margin: 36px 0 16px 0;
padding: 10px 0 10px 14px;
border-left: 4px solid #1966FF;
line-height: 1.4;
}
h3 {
font-size: 18px;
font-weight: 600;
color: #1966FF;
margin: 28px 0 14px 0;
line-height: 1.4;
}
h4 {
font-size: 16px;
font-weight: 600;
color: #1f2329;
margin: 24px 0 12px 0;
line-height: 1.4;
}
p {
font-size: 16px;
line-height: 2;
color: #1f2329;
margin: 16px 0;
}
strong {
font-weight: 700;
color: #1966FF;
}
em {
font-style: italic;
color: #646a73;
}
code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: #f5f6f7;
color: #1966FF;
padding: 2px 8px;
border-radius: 4px;
}
pre {
background: #f5f6f7;
color: #1f2329;
padding: 20px;
border-radius: 8px;
overflow-x: auto;
margin: 20px 0;
line-height: 1.6;
border: 1px solid #e5e6e8;
}
pre code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: none;
color: #1f2329;
padding: 0;
border-radius: 0;
}
blockquote {
border-left: 4px solid #1966FF;
background: #f0f5ff;
margin: 20px 0;
padding: 16px 20px;
border-radius: 0 8px 8px 0;
color: #1f2329;
}
blockquote p {
margin: 8px 0;
color: #1f2329;
}
ul {
padding-left: 28px;
margin: 16px 0;
}
ol {
padding-left: 28px;
margin: 16px 0;
}
li {
font-size: 16px;
line-height: 2;
color: #1f2329;
margin: 8px 0;
}
table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
font-size: 15px;
}
thead {
background: #1966FF;
}
th {
background: #1966FF;
color: #ffffff;
font-weight: 600;
padding: 12px 16px;
text-align: left;
border: 1px solid #1966FF;
}
td {
padding: 12px 16px;
border: 1px solid #e5e6e8;
color: #1f2329;
}
tr {
background: #ffffff;
}
img {
max-width: 100%;
height: auto;
display: block;
margin: 28px auto;
border-radius: 8px;
}
a {
color: #1966FF;
text-decoration: none;
font-weight: 500;
}
hr {
border: none;
border-top: 1px solid #e5e6e8;
margin: 32px 0;
}

View file

@ -0,0 +1,198 @@
name: "elegant-rose"
description: "优雅玫瑰风格:浅粉底玫瑰色点缀,温柔精致,适合女性生活和时尚内容"
colors:
primary: "#be185d"
secondary: "#db2777"
text: "#3d1f2e"
text_light: "#7a5068"
background: "#fdf2f8"
code_bg: "#fce7f3"
code_color: "#be185d"
quote_border: "#be185d"
quote_bg: "#fce7f3"
border_radius: "12px"
darkmode:
background: "#1a0f14"
text: "#e8d0dc"
text_light: "#a07888"
primary: "#f472b6"
code_bg: "#2a1520"
code_color: "#f9a8d4"
quote_bg: "#221018"
quote_border: "#f472b6"
base_css: |
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
font-size: 16px;
line-height: 1.85;
color: #3d1f2e;
background: #fdf2f8;
max-width: 720px;
margin: 0 auto;
padding: 20px;
word-wrap: break-word;
}
h1 {
font-size: 26px;
font-weight: 700;
color: #be185d;
margin: 32px 0 16px 0;
text-align: center;
padding-bottom: 12px;
border-bottom: 2px solid #f9a8d4;
line-height: 1.4;
}
h2 {
font-size: 22px;
font-weight: 700;
color: #be185d;
margin: 28px 0 14px 0;
padding: 8px 0 8px 12px;
border-left: 4px solid #be185d;
line-height: 1.4;
}
h3 {
font-size: 18px;
font-weight: 600;
color: #db2777;
margin: 24px 0 12px 0;
line-height: 1.4;
}
h4 {
font-size: 16px;
font-weight: 600;
color: #3d1f2e;
margin: 20px 0 10px 0;
line-height: 1.4;
}
p {
font-size: 16px;
line-height: 1.85;
color: #3d1f2e;
margin: 12px 0;
}
strong {
font-weight: 700;
color: #be185d;
}
em {
font-style: italic;
color: #7a5068;
}
code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: #fce7f3;
color: #be185d;
padding: 2px 6px;
border-radius: 6px;
}
pre {
background: #fce7f3;
color: #3d1f2e;
padding: 16px;
border-radius: 12px;
overflow-x: auto;
margin: 16px 0;
line-height: 1.6;
border: 1px solid #fbcfe8;
}
pre code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: none;
color: #3d1f2e;
padding: 0;
border-radius: 0;
}
blockquote {
border-left: 4px solid #be185d;
background: #fce7f3;
margin: 16px 0;
padding: 12px 16px;
border-radius: 0 12px 12px 0;
color: #7a5068;
}
blockquote p {
margin: 8px 0;
color: #7a5068;
}
ul {
padding-left: 24px;
margin: 12px 0;
}
ol {
padding-left: 24px;
margin: 12px 0;
}
li {
font-size: 16px;
line-height: 1.85;
color: #3d1f2e;
margin: 6px 0;
}
table {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
font-size: 15px;
}
thead {
background: #be185d;
}
th {
background: #be185d;
color: #ffffff;
font-weight: 600;
padding: 10px 14px;
text-align: left;
border: 1px solid #be185d;
}
td {
padding: 10px 14px;
border: 1px solid #fbcfe8;
color: #3d1f2e;
}
tr {
background: #fdf2f8;
}
img {
max-width: 100%;
height: auto;
display: block;
margin: 24px auto;
border-radius: 12px;
}
a {
color: #be185d;
text-decoration: none;
font-weight: 500;
}
hr {
border: none;
border-top: 1px solid #fbcfe8;
margin: 24px 0;
}

View file

@ -0,0 +1,197 @@
name: "focus-red"
description: "聚焦红风格:白底中国红标题和引用边框,醒目有力,适合新闻评论和观点输出"
colors:
primary: "#dc2626"
secondary: "#ef4444"
text: "#1a1a1a"
text_light: "#555555"
background: "#ffffff"
code_bg: "#fef2f2"
code_color: "#b91c1c"
quote_border: "#dc2626"
quote_bg: "#fef2f2"
border_radius: "6px"
darkmode:
background: "#1a0f0f"
text: "#e8d8d8"
text_light: "#a08888"
primary: "#f87171"
code_bg: "#2a1515"
code_color: "#fca5a5"
quote_bg: "#221010"
quote_border: "#f87171"
base_css: |
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
font-size: 16px;
line-height: 1.8;
color: #1a1a1a;
background: #ffffff;
max-width: 720px;
margin: 0 auto;
padding: 20px;
word-wrap: break-word;
}
h1 {
font-size: 26px;
font-weight: 800;
color: #dc2626;
margin: 32px 0 16px 0;
padding-bottom: 12px;
border-bottom: 3px solid #dc2626;
line-height: 1.4;
}
h2 {
font-size: 22px;
font-weight: 700;
color: #dc2626;
margin: 28px 0 14px 0;
padding: 8px 0 8px 12px;
border-left: 4px solid #dc2626;
line-height: 1.4;
}
h3 {
font-size: 18px;
font-weight: 600;
color: #1a1a1a;
margin: 24px 0 12px 0;
line-height: 1.4;
}
h4 {
font-size: 16px;
font-weight: 600;
color: #1a1a1a;
margin: 20px 0 10px 0;
line-height: 1.4;
}
p {
font-size: 16px;
line-height: 1.8;
color: #1a1a1a;
margin: 12px 0;
}
strong {
font-weight: 700;
color: #dc2626;
}
em {
font-style: italic;
color: #555555;
}
code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: #fef2f2;
color: #b91c1c;
padding: 2px 6px;
border-radius: 4px;
}
pre {
background: #fef2f2;
color: #1a1a1a;
padding: 16px;
border-radius: 6px;
overflow-x: auto;
margin: 16px 0;
line-height: 1.6;
border: 1px solid #fecaca;
}
pre code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: none;
color: #1a1a1a;
padding: 0;
border-radius: 0;
}
blockquote {
border-left: 4px solid #dc2626;
background: #fef2f2;
margin: 16px 0;
padding: 12px 16px;
border-radius: 0 6px 6px 0;
color: #333333;
}
blockquote p {
margin: 8px 0;
color: #333333;
}
ul {
padding-left: 24px;
margin: 12px 0;
}
ol {
padding-left: 24px;
margin: 12px 0;
}
li {
font-size: 16px;
line-height: 1.8;
color: #1a1a1a;
margin: 6px 0;
}
table {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
font-size: 15px;
}
thead {
background: #dc2626;
}
th {
background: #dc2626;
color: #ffffff;
font-weight: 700;
padding: 10px 14px;
text-align: left;
border: 1px solid #dc2626;
}
td {
padding: 10px 14px;
border: 1px solid #e5e7eb;
color: #1a1a1a;
}
tr {
background: #ffffff;
}
img {
max-width: 100%;
height: auto;
display: block;
margin: 24px auto;
border-radius: 6px;
}
a {
color: #dc2626;
text-decoration: none;
font-weight: 500;
}
hr {
border: none;
border-top: 2px solid #fecaca;
margin: 24px 0;
}

198
toolkit/themes/github.yaml Normal file
View file

@ -0,0 +1,198 @@
name: "github"
description: "GitHub风格白底蓝色链接等宽代码块简洁清晰适合技术文档和开发者内容"
colors:
primary: "#0969da"
secondary: "#0550ae"
text: "#1f2328"
text_light: "#656d76"
background: "#ffffff"
code_bg: "#f6f8fa"
code_color: "#0550ae"
quote_border: "#d0d7de"
quote_bg: "#f6f8fa"
border_radius: "6px"
darkmode:
background: "#0d1117"
text: "#e6edf3"
text_light: "#8b949e"
primary: "#58a6ff"
code_bg: "#161b22"
code_color: "#79c0ff"
quote_bg: "#161b22"
quote_border: "#30363d"
base_css: |
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
font-size: 16px;
line-height: 1.75;
color: #1f2328;
background: #ffffff;
max-width: 720px;
margin: 0 auto;
padding: 20px;
word-wrap: break-word;
}
h1 {
font-size: 26px;
font-weight: 600;
color: #1f2328;
margin: 32px 0 16px 0;
padding-bottom: 10px;
border-bottom: 1px solid #d1d9e0;
line-height: 1.4;
}
h2 {
font-size: 22px;
font-weight: 600;
color: #1f2328;
margin: 28px 0 14px 0;
padding-bottom: 8px;
border-bottom: 1px solid #d1d9e0;
line-height: 1.4;
}
h3 {
font-size: 18px;
font-weight: 600;
color: #1f2328;
margin: 24px 0 12px 0;
line-height: 1.4;
}
h4 {
font-size: 16px;
font-weight: 600;
color: #1f2328;
margin: 20px 0 10px 0;
line-height: 1.4;
}
p {
font-size: 16px;
line-height: 1.75;
color: #1f2328;
margin: 12px 0;
}
strong {
font-weight: 600;
color: #1f2328;
}
em {
font-style: italic;
color: #1f2328;
}
code {
font-family: ui-monospace, "SFMono-Regular", "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
font-size: 13.6px;
background: rgba(175,184,193,0.2);
color: #1f2328;
padding: 3px 6px;
border-radius: 6px;
}
pre {
background: #f6f8fa;
color: #1f2328;
padding: 16px;
border-radius: 6px;
overflow-x: auto;
margin: 16px 0;
line-height: 1.5;
border: 1px solid #d1d9e0;
}
pre code {
font-family: ui-monospace, "SFMono-Regular", "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
font-size: 13.6px;
background: none;
color: #1f2328;
padding: 0;
border-radius: 0;
}
blockquote {
border-left: 4px solid #d0d7de;
background: transparent;
margin: 16px 0;
padding: 4px 16px;
border-radius: 0;
color: #656d76;
}
blockquote p {
margin: 8px 0;
color: #656d76;
}
ul {
padding-left: 28px;
margin: 12px 0;
}
ol {
padding-left: 28px;
margin: 12px 0;
}
li {
font-size: 16px;
line-height: 1.75;
color: #1f2328;
margin: 4px 0;
}
table {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
font-size: 15px;
}
thead {
background: #f6f8fa;
}
th {
background: #f6f8fa;
color: #1f2328;
font-weight: 600;
padding: 8px 14px;
text-align: left;
border: 1px solid #d1d9e0;
}
td {
padding: 8px 14px;
border: 1px solid #d1d9e0;
color: #1f2328;
}
tr {
background: #ffffff;
}
img {
max-width: 100%;
height: auto;
display: block;
margin: 24px auto;
border-radius: 6px;
}
a {
color: #0969da;
text-decoration: none;
font-weight: 400;
}
hr {
border: none;
height: 2px;
background: #d1d9e0;
margin: 24px 0;
}

204
toolkit/themes/ink.yaml Normal file
View file

@ -0,0 +1,204 @@
name: "ink"
description: "水墨中国风:宣纸底墨色文字,中文衬线字体,留白疏朗,适合文化和人文内容"
colors:
primary: "#4a4a4a"
secondary: "#6b6b6b"
text: "#1a1a1a"
text_light: "#666666"
background: "#f8f5f0"
code_bg: "#f0ebe3"
code_color: "#555555"
quote_border: "#999999"
quote_bg: "#f4f0e8"
border_radius: "2px"
darkmode:
background: "#1a1816"
text: "#d8d2c8"
text_light: "#9a9488"
primary: "#b0a898"
code_bg: "#252220"
code_color: "#c0b8a8"
quote_bg: "#222018"
quote_border: "#706858"
base_css: |
body {
font-family: "Songti SC", "SimSun", "Noto Serif SC", Georgia, serif;
font-size: 16px;
line-height: 2;
color: #1a1a1a;
background: #f8f5f0;
max-width: 680px;
margin: 0 auto;
padding: 28px;
word-wrap: break-word;
}
h1 {
font-size: 28px;
font-weight: 700;
color: #1a1a1a;
margin: 48px 0 24px 0;
text-align: center;
line-height: 1.4;
letter-spacing: 0.1em;
}
h2 {
font-size: 22px;
font-weight: 700;
color: #1a1a1a;
margin: 40px 0 16px 0;
text-align: center;
padding-bottom: 12px;
line-height: 1.4;
letter-spacing: 0.05em;
}
h3 {
font-size: 18px;
font-weight: 600;
color: #333333;
margin: 32px 0 14px 0;
line-height: 1.4;
letter-spacing: 0.03em;
}
h4 {
font-size: 16px;
font-weight: 600;
color: #333333;
margin: 24px 0 12px 0;
line-height: 1.4;
}
p {
font-size: 16px;
line-height: 2;
color: #1a1a1a;
margin: 16px 0;
text-indent: 2em;
}
strong {
font-weight: 700;
color: #1a1a1a;
}
em {
font-style: italic;
color: #666666;
}
code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: #f0ebe3;
color: #555555;
padding: 2px 6px;
border-radius: 2px;
}
pre {
background: #f0ebe3;
color: #1a1a1a;
padding: 16px;
border-radius: 2px;
overflow-x: auto;
margin: 20px 0;
line-height: 1.6;
border: 1px solid #ddd5c8;
}
pre code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: none;
color: #1a1a1a;
padding: 0;
border-radius: 0;
}
blockquote {
border-left: 3px solid #999999;
background: #f4f0e8;
margin: 20px 0;
padding: 14px 18px;
border-radius: 0 2px 2px 0;
color: #555555;
font-style: italic;
}
blockquote p {
margin: 8px 0;
color: #555555;
text-indent: 0;
}
ul {
padding-left: 24px;
margin: 16px 0;
}
ol {
padding-left: 24px;
margin: 16px 0;
}
li {
font-size: 16px;
line-height: 2;
color: #1a1a1a;
margin: 8px 0;
}
table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
font-size: 15px;
}
thead {
background: #4a4a4a;
}
th {
background: #4a4a4a;
color: #f8f5f0;
font-weight: 600;
padding: 10px 14px;
text-align: left;
border: 1px solid #4a4a4a;
}
td {
padding: 10px 14px;
border: 1px solid #ddd5c8;
color: #1a1a1a;
}
tr {
background: #f8f5f0;
}
img {
max-width: 100%;
height: auto;
display: block;
margin: 32px auto;
border-radius: 2px;
}
a {
color: #555555;
text-decoration: underline;
}
hr {
border: none;
text-align: center;
margin: 36px 0;
height: 20px;
background: transparent;
border-bottom: 1px solid #ccc5b8;
}

View file

@ -0,0 +1,197 @@
name: "midnight"
description: "午夜深色主题:深蓝黑底白色文字,蓝色高亮,适合技术和深夜阅读内容"
colors:
primary: "#60a5fa"
secondary: "#93c5fd"
text: "#e2e8f0"
text_light: "#94a3b8"
background: "#0f172a"
code_bg: "#1e293b"
code_color: "#7dd3fc"
quote_border: "#60a5fa"
quote_bg: "#172040"
border_radius: "8px"
darkmode:
background: "#0f172a"
text: "#e2e8f0"
text_light: "#94a3b8"
primary: "#60a5fa"
code_bg: "#1e293b"
code_color: "#7dd3fc"
quote_bg: "#172040"
quote_border: "#60a5fa"
base_css: |
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
font-size: 16px;
line-height: 1.8;
color: #e2e8f0;
background: #0f172a;
max-width: 720px;
margin: 0 auto;
padding: 20px;
word-wrap: break-word;
}
h1 {
font-size: 26px;
font-weight: 700;
color: #f1f5f9;
margin: 32px 0 16px 0;
padding-bottom: 12px;
border-bottom: 2px solid #60a5fa;
line-height: 1.4;
}
h2 {
font-size: 22px;
font-weight: 700;
color: #60a5fa;
margin: 28px 0 14px 0;
padding: 8px 0 8px 12px;
border-left: 4px solid #60a5fa;
line-height: 1.4;
}
h3 {
font-size: 18px;
font-weight: 600;
color: #93c5fd;
margin: 24px 0 12px 0;
line-height: 1.4;
}
h4 {
font-size: 16px;
font-weight: 600;
color: #cbd5e1;
margin: 20px 0 10px 0;
line-height: 1.4;
}
p {
font-size: 16px;
line-height: 1.8;
color: #e2e8f0;
margin: 12px 0;
}
strong {
font-weight: 700;
color: #60a5fa;
}
em {
font-style: italic;
color: #94a3b8;
}
code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: #1e293b;
color: #7dd3fc;
padding: 2px 6px;
border-radius: 4px;
}
pre {
background: #1e293b;
color: #e2e8f0;
padding: 16px;
border-radius: 8px;
overflow-x: auto;
margin: 16px 0;
line-height: 1.6;
border: 1px solid #334155;
}
pre code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: none;
color: #e2e8f0;
padding: 0;
border-radius: 0;
}
blockquote {
border-left: 4px solid #60a5fa;
background: #172040;
margin: 16px 0;
padding: 12px 16px;
border-radius: 0 8px 8px 0;
color: #cbd5e1;
}
blockquote p {
margin: 8px 0;
color: #cbd5e1;
}
ul {
padding-left: 24px;
margin: 12px 0;
}
ol {
padding-left: 24px;
margin: 12px 0;
}
li {
font-size: 16px;
line-height: 1.8;
color: #e2e8f0;
margin: 6px 0;
}
table {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
font-size: 15px;
}
thead {
background: #1e3a5f;
}
th {
background: #1e3a5f;
color: #f1f5f9;
font-weight: 600;
padding: 10px 14px;
text-align: left;
border: 1px solid #334155;
}
td {
padding: 10px 14px;
border: 1px solid #334155;
color: #e2e8f0;
}
tr {
background: #0f172a;
}
img {
max-width: 100%;
height: auto;
display: block;
margin: 24px auto;
border-radius: 8px;
}
a {
color: #60a5fa;
text-decoration: none;
font-weight: 500;
}
hr {
border: none;
border-top: 1px solid #334155;
margin: 24px 0;
}

View file

@ -0,0 +1,202 @@
name: "minimal-gold"
description: "极简金色风格:白底金色细线点缀,奢华但克制,适合高端品牌和精品内容"
colors:
primary: "#b8860b"
secondary: "#d4a843"
text: "#2a2a2a"
text_light: "#6b6b6b"
background: "#ffffff"
code_bg: "#faf8f3"
code_color: "#8b6914"
quote_border: "#b8860b"
quote_bg: "#fdfbf5"
border_radius: "4px"
darkmode:
background: "#141210"
text: "#e0dcd0"
text_light: "#9a9488"
primary: "#d4a843"
code_bg: "#1e1c18"
code_color: "#e0c060"
quote_bg: "#1a1810"
quote_border: "#d4a843"
base_css: |
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
font-size: 16px;
line-height: 1.8;
color: #2a2a2a;
background: #ffffff;
max-width: 720px;
margin: 0 auto;
padding: 20px;
word-wrap: break-word;
letter-spacing: 0.01em;
}
h1 {
font-size: 26px;
font-weight: 600;
color: #2a2a2a;
margin: 36px 0 18px 0;
text-align: center;
padding-bottom: 14px;
border-bottom: 1px solid #b8860b;
line-height: 1.4;
letter-spacing: 0.05em;
}
h2 {
font-size: 21px;
font-weight: 600;
color: #2a2a2a;
margin: 30px 0 14px 0;
padding-bottom: 8px;
border-bottom: 1px solid #d4c59a;
line-height: 1.4;
}
h3 {
font-size: 18px;
font-weight: 600;
color: #b8860b;
margin: 24px 0 12px 0;
line-height: 1.4;
}
h4 {
font-size: 16px;
font-weight: 600;
color: #2a2a2a;
margin: 20px 0 10px 0;
line-height: 1.4;
}
p {
font-size: 16px;
line-height: 1.8;
color: #2a2a2a;
margin: 12px 0;
}
strong {
font-weight: 700;
color: #b8860b;
}
em {
font-style: italic;
color: #6b6b6b;
}
code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: #faf8f3;
color: #8b6914;
padding: 2px 6px;
border-radius: 4px;
}
pre {
background: #faf8f3;
color: #2a2a2a;
padding: 16px;
border-radius: 4px;
overflow-x: auto;
margin: 16px 0;
line-height: 1.6;
border: 1px solid #e8e0c8;
}
pre code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: none;
color: #2a2a2a;
padding: 0;
border-radius: 0;
}
blockquote {
border-left: 2px solid #b8860b;
background: #fdfbf5;
margin: 16px 0;
padding: 12px 16px;
border-radius: 0 4px 4px 0;
color: #6b6b6b;
}
blockquote p {
margin: 8px 0;
color: #6b6b6b;
}
ul {
padding-left: 24px;
margin: 12px 0;
}
ol {
padding-left: 24px;
margin: 12px 0;
}
li {
font-size: 16px;
line-height: 1.8;
color: #2a2a2a;
margin: 6px 0;
}
table {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
font-size: 15px;
}
thead {
background: #2a2a2a;
}
th {
background: #2a2a2a;
color: #d4a843;
font-weight: 600;
padding: 10px 14px;
text-align: left;
border: 1px solid #2a2a2a;
letter-spacing: 0.03em;
}
td {
padding: 10px 14px;
border: 1px solid #e8e0c8;
color: #2a2a2a;
}
tr {
background: #ffffff;
}
img {
max-width: 100%;
height: auto;
display: block;
margin: 24px auto;
border-radius: 4px;
}
a {
color: #b8860b;
text-decoration: none;
font-weight: 500;
}
hr {
border: none;
border-top: 1px solid #d4c59a;
margin: 28px auto;
width: 40%;
}

View file

@ -11,6 +11,15 @@ colors:
quote_border: "#cccccc"
quote_bg: "#f9f9f9"
border_radius: "4px"
darkmode:
background: "#1a1a1a"
text: "#c0c0c0"
text_light: "#888888"
primary: "#e0e0e0"
code_bg: "#252525"
code_color: "#c0c0c0"
quote_bg: "#222222"
quote_border: "#555555"
base_css: |
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;

View file

@ -0,0 +1,206 @@
name: "newspaper"
description: "经典报纸风格:米黄底深棕文字,衬线字体质感,适合深度报道和评论"
colors:
primary: "#8b4513"
secondary: "#a0522d"
text: "#2c2416"
text_light: "#5c4a3a"
background: "#f5f0e8"
code_bg: "#ede7db"
code_color: "#8b4513"
quote_border: "#8b4513"
quote_bg: "#f0eade"
border_radius: "2px"
darkmode:
background: "#1e1a14"
text: "#ddd5c8"
text_light: "#a09580"
primary: "#c8915a"
code_bg: "#2a2418"
code_color: "#d4a574"
quote_bg: "#28221a"
quote_border: "#c8915a"
base_css: |
body {
font-family: Georgia, "Songti SC", "SimSun", "Noto Serif SC", serif;
font-size: 16px;
line-height: 1.9;
color: #2c2416;
background: #f5f0e8;
max-width: 720px;
margin: 0 auto;
padding: 20px;
word-wrap: break-word;
}
h1 {
font-size: 28px;
font-weight: 700;
color: #2c2416;
margin: 36px 0 16px 0;
text-align: center;
padding-bottom: 12px;
border-bottom: 3px double #8b4513;
line-height: 1.3;
letter-spacing: 0.03em;
}
h2 {
font-size: 22px;
font-weight: 700;
color: #2c2416;
margin: 30px 0 14px 0;
padding-bottom: 8px;
border-bottom: 1px solid #8b4513;
line-height: 1.4;
}
h3 {
font-size: 18px;
font-weight: 700;
color: #5c4a3a;
margin: 24px 0 12px 0;
line-height: 1.4;
text-transform: uppercase;
letter-spacing: 0.05em;
font-size: 16px;
}
h4 {
font-size: 16px;
font-weight: 700;
color: #5c4a3a;
margin: 20px 0 10px 0;
line-height: 1.4;
}
p {
font-size: 16px;
line-height: 1.9;
color: #2c2416;
margin: 14px 0;
text-align: justify;
}
strong {
font-weight: 700;
color: #2c2416;
}
em {
font-style: italic;
color: #5c4a3a;
}
code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: #ede7db;
color: #8b4513;
padding: 2px 6px;
border-radius: 2px;
}
pre {
background: #ede7db;
color: #2c2416;
padding: 16px;
border-radius: 2px;
overflow-x: auto;
margin: 16px 0;
line-height: 1.6;
border: 1px solid #d4cbb8;
}
pre code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: none;
color: #2c2416;
padding: 0;
border-radius: 0;
}
blockquote {
border-left: 3px solid #8b4513;
background: #f0eade;
margin: 16px 0;
padding: 12px 16px;
border-radius: 0 2px 2px 0;
color: #5c4a3a;
font-style: italic;
}
blockquote p {
margin: 8px 0;
color: #5c4a3a;
}
ul {
padding-left: 24px;
margin: 14px 0;
}
ol {
padding-left: 24px;
margin: 14px 0;
}
li {
font-size: 16px;
line-height: 1.9;
color: #2c2416;
margin: 6px 0;
}
table {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
font-size: 15px;
}
thead {
background: #8b4513;
}
th {
background: #8b4513;
color: #f5f0e8;
font-weight: 700;
padding: 10px 14px;
text-align: left;
border: 1px solid #8b4513;
}
td {
padding: 10px 14px;
border: 1px solid #d4cbb8;
color: #2c2416;
}
tr {
background: #f5f0e8;
}
img {
max-width: 100%;
height: auto;
display: block;
margin: 24px auto;
border-radius: 2px;
border: 1px solid #d4cbb8;
}
a {
color: #8b4513;
text-decoration: underline;
font-weight: 500;
}
hr {
border: none;
border-top: 1px solid #d4cbb8;
margin: 28px auto;
width: 60%;
}

View file

@ -11,6 +11,15 @@ colors:
quote_border: "#2563eb"
quote_bg: "#eff6ff"
border_radius: "8px"
darkmode:
background: "#1e1e1e"
text: "#c8c8c8"
text_light: "#999999"
primary: "#6aadff"
code_bg: "#2d2d2d"
code_color: "#d4d4d4"
quote_bg: "#252525"
quote_border: "#4a90d9"
base_css: |
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;

198
toolkit/themes/sspai.yaml Normal file
View file

@ -0,0 +1,198 @@
name: "sspai"
description: "少数派风格:暖白底红色点缀,清爽文艺,适合数码生活和效率工具内容"
colors:
primary: "#c7372f"
secondary: "#d4524b"
text: "#333333"
text_light: "#888888"
background: "#fafaf7"
code_bg: "#f5f2ed"
code_color: "#c7372f"
quote_border: "#c7372f"
quote_bg: "#fdf5f4"
border_radius: "6px"
darkmode:
background: "#1c1c1a"
text: "#e0ddd8"
text_light: "#9a9790"
primary: "#e05a52"
code_bg: "#2a2825"
code_color: "#e87c76"
quote_bg: "#2a2220"
quote_border: "#e05a52"
base_css: |
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
font-size: 16px;
line-height: 1.85;
color: #333333;
background: #fafaf7;
max-width: 720px;
margin: 0 auto;
padding: 20px;
word-wrap: break-word;
}
h1 {
font-size: 26px;
font-weight: 700;
color: #1a1a1a;
margin: 36px 0 18px 0;
text-align: center;
line-height: 1.4;
}
h2 {
font-size: 21px;
font-weight: 700;
color: #c7372f;
margin: 30px 0 14px 0;
padding-bottom: 8px;
border-bottom: 2px solid #c7372f;
line-height: 1.4;
}
h3 {
font-size: 18px;
font-weight: 600;
color: #333333;
margin: 24px 0 12px 0;
padding-left: 10px;
border-left: 3px solid #c7372f;
line-height: 1.4;
}
h4 {
font-size: 16px;
font-weight: 600;
color: #333333;
margin: 20px 0 10px 0;
line-height: 1.4;
}
p {
font-size: 16px;
line-height: 1.85;
color: #333333;
margin: 14px 0;
}
strong {
font-weight: 700;
color: #c7372f;
}
em {
font-style: italic;
color: #666666;
}
code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: #f5f2ed;
color: #c7372f;
padding: 2px 6px;
border-radius: 4px;
}
pre {
background: #f5f2ed;
color: #333333;
padding: 16px;
border-radius: 6px;
overflow-x: auto;
margin: 16px 0;
line-height: 1.6;
border: 1px solid #e8e4dc;
}
pre code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 14px;
background: none;
color: #333333;
padding: 0;
border-radius: 0;
}
blockquote {
border-left: 4px solid #c7372f;
background: #fdf5f4;
margin: 16px 0;
padding: 12px 16px;
border-radius: 0 6px 6px 0;
color: #555555;
}
blockquote p {
margin: 8px 0;
color: #555555;
}
ul {
padding-left: 24px;
margin: 14px 0;
}
ol {
padding-left: 24px;
margin: 14px 0;
}
li {
font-size: 16px;
line-height: 1.85;
color: #333333;
margin: 6px 0;
}
table {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
font-size: 15px;
}
thead {
background: #c7372f;
}
th {
background: #c7372f;
color: #ffffff;
font-weight: 600;
padding: 10px 14px;
text-align: left;
border: 1px solid #c7372f;
}
td {
padding: 10px 14px;
border: 1px solid #e8e4dc;
color: #333333;
}
tr {
background: #fafaf7;
}
img {
max-width: 100%;
height: auto;
display: block;
margin: 24px auto;
border-radius: 6px;
}
a {
color: #c7372f;
text-decoration: none;
font-weight: 500;
}
hr {
border: none;
border-top: 1px solid #e8e4dc;
margin: 28px 0;
}

View file

@ -11,6 +11,15 @@ colors:
quote_border: "#7c3aed"
quote_bg: "#f8f5ff"
border_radius: "8px"
darkmode:
background: "#1a1a2e"
text: "#c8c8c8"
text_light: "#999999"
primary: "#a78bfa"
code_bg: "#1e1e2e"
code_color: "#c8cad8"
quote_bg: "#232340"
quote_border: "#7c3aed"
base_css: |
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;

View file

@ -11,6 +11,15 @@ colors:
quote_border: "#d97706"
quote_bg: "#fffbeb"
border_radius: "8px"
darkmode:
background: "#1e1e1e"
text: "#d4c8b8"
text_light: "#a09080"
primary: "#f0a830"
code_bg: "#2a2520"
code_color: "#d4b896"
quote_bg: "#2a2418"
quote_border: "#d97706"
base_css: |
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;