
Xhs Note Creator
- 1.1k installs
- 2.1k repo stars
- Updated April 1, 2026
- comeonzhj/auto-redbook-skills
xhs-note-creator is an agent skill for Xiaohongshu note content and image card creation.
About
The xhs-note-creator skill creates Xiaohongshu note assets including titles, body copy, cover images, and content cards from user-provided materials or briefs. It supports eight layout themes and four smart pagination modes for visually consistent social posts. Workflows cover drafting note copy, generating image cards, and optionally publishing to the Xiaohongshu platform. Agents help creators localize tone for Chinese social audiences while maintaining brand-aligned visuals across multi-card note formats. Xiaohongshu note drafting with title and body copy. Image card generation with cover and content cards. Eight layout themes and four smart pagination modes. Optional publishing workflow to Xiaohongshu. Turns raw materials into polished social note assets. Create Xiaohongshu note content, image cards, and optional publishing from user materials.
- Xiaohongshu note drafting with title and body copy.
- Image card generation with cover and content cards.
- Eight layout themes and four smart pagination modes.
- Optional publishing workflow to Xiaohongshu.
- Turns raw materials into polished social note assets.
Xhs Note Creator by the numbers
- 1,149 all-time installs (skills.sh)
- +49 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #423 of 1,879 Marketing & SEO skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
What xhs-note-creator says it does
支持 8 种精美排版主题和 4 种智能分页模式。
npx skills add https://github.com/comeonzhj/auto-redbook-skills --skill xhs-note-creatorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.1k |
|---|---|
| repo stars | ★ 2.1k |
| Security audit | 2 / 3 scanners passed |
| Last updated | April 1, 2026 |
| Repository | comeonzhj/auto-redbook-skills ↗ |
How do I create a Xiaohongshu note with image cards from my materials?
Create Xiaohongshu note content, image cards, and optional publishing from user materials.
Who is it for?
Creators and marketers publishing on Xiaohongshu (Little Red Book).
Skip if: Skip for non-Chinese social platforms or text-only posts without cards.
When should I use this skill?
User creates Xiaohongshu notes, 小红书 content, or red book image cards.
What you get
Drafted note copy with themed image cards ready for Xiaohongshu publishing.
- Note title and body
- Image-card Markdown deck
By the numbers
- 8 layout themes and 4 smart pagination modes
- Titles ≤20 characters with 5–10 SEO tags per note
Files
小红书笔记创作技能
根据用户提供的资料或需求,创作小红书笔记内容、生成精美图片卡片,并可选择发布到小红书。
详细参数文档见 references/params.md---
工作流程
第一步:撰写小红书笔记内容
根据用户需求和资料,创作符合小红书风格的内容:
标题:不超过 20 字,吸引眼球,可用数字/疑问句/感叹号增强吸引力。
正文:段落清晰,点缀少量 Emoji(每段 1-2 个),短句短段,结尾附 5-10 个 SEO 标签。
---
第二步:生成渲染用 Markdown 文档
注意:此 Markdown 专为图片渲染设计,禁止直接使用上一步的笔记正文。
文档结构:
---
emoji: "🚀"
title: "封面大标题(≤15字)"
subtitle: "封面副标题(≤15字)"
---
# 正文内容...
---
# 第二张卡片内容...(使用 --- 手动分隔时)分页策略选择:
- 内容需精确切分 → 用
---手动分隔,配合-m separator - 内容长短不稳定 → 生成普通 Markdown,使用
-m auto-split
---
第三步:渲染图片卡片
python scripts/render_xhs.py <markdown_file> [options]默认主题:sketch(手绘素描风格) 默认分页:separator(按 --- 分隔)
常用示例:
# 默认(sketch 主题 + 手动分页)
python scripts/render_xhs.py content.md
# 自动分页(推荐内容长短不定时)
python scripts/render_xhs.py content.md -m auto-split
# 切换主题
python scripts/render_xhs.py content.md -t playful-geometric -m auto-split
# 固定尺寸自动缩放
python scripts/render_xhs.py content.md -m auto-fit生成结果:cover.png(封面)+ card_1.png、card_2.png...(正文卡片)
可用主题(-t):sketch、default、playful-geometric、neo-brutalism、botanical、professional、retro、terminal
分页模式(-m):separator、auto-fit、auto-split、dynamic
完整参数说明见 references/params.md---
第四步:发布小红书笔记(可选)
前置条件:配置好 .env 文件中的 XHS_COOKIE(详见 references/params.md)
# 默认仅自己可见(推荐先预览确认)
python scripts/publish_xhs.py --title "笔记标题" --desc "笔记描述" \
--images cover.png card_1.png card_2.png
# 确认无误后公开发布
python scripts/publish_xhs.py --title "笔记标题" --desc "笔记描述" \
--images cover.png card_1.png card_2.png --public默认以「仅自己可见」发布,加 --public 参数才会公开。---
技能资源
脚本
scripts/render_xhs.py— 渲染脚本(主推,8 主题 + 4 分页模式)scripts/render_xhs_v2.py— 渲染脚本 V2(备用,7 种渐变色彩风格)scripts/publish_xhs.py— 发布脚本
模板与样式
assets/cover.html— 封面 HTML 模板assets/card.html— 正文卡片 HTML 模板assets/styles.css— 公共容器样式assets/themes/— 各主题 CSS 文件
参考文档
references/params.md— 完整参数参考(主题/模式/发布参数)
{
"$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
"name": "comeonzhj-Auto-Redbook-Skills",
"owner": {
"name": "ZhangJia"
},
"description": "小红书笔记自动创作插件:撰写内容、生成图片卡片、一键发布",
"plugins": [
{
"name": "auto-redbook-skills",
"source": "./",
"description": "小红书笔记自动创作技能。支持 8 种排版主题和 4 种智能分页模式。",
"category": "content",
"keywords": [
"xiaohongshu",
"小红书",
"redbook",
"social-media",
"content-creation"
]
}
]
}
{
"name": "auto-redbook-skills",
"description": "小红书笔记自动创作技能:撰写内容、生成精美封面与正文卡片、可选一键发布。支持 8 种排版主题(sketch / neo-brutalism / botanical 等)和 4 种智能分页模式。",
"version": "1.0.0",
"category": "content",
"keywords": [
"xiaohongshu",
"小红书",
"redbook",
"social-media",
"content-creation",
"image-generation"
],
"homepage": "https://github.com/comeonzhj/Auto-Redbook-Skills",
"author": {
"name": "ZhangJia"
}
}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1080">
<title>小红书卡片</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700;900&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Noto Sans SC', 'Source Han Sans CN', 'PingFang SC', 'Microsoft YaHei', sans-serif;
width: 1080px;
min-height: 1440px;
overflow: hidden;
background: transparent;
}
.card-container {
width: 1080px;
min-height: 1440px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
position: relative;
padding: 50px;
overflow: hidden;
}
.card-inner {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 60px;
min-height: calc(1440px - 100px);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px);
}
/* Markdown 内容样式 */
.card-content {
color: #475569;
font-size: 42px;
line-height: 1.7;
}
.card-content h1 {
font-size: 72px;
font-weight: 700;
color: #1e293b;
margin-bottom: 40px;
line-height: 1.3;
}
.card-content h2 {
font-size: 56px;
font-weight: 600;
color: #334155;
margin: 50px 0 25px 0;
line-height: 1.4;
}
.card-content h3 {
font-size: 48px;
font-weight: 600;
color: #475569;
margin: 40px 0 20px 0;
}
.card-content p {
margin-bottom: 35px;
}
.card-content strong {
font-weight: 700;
color: #1e293b;
}
.card-content em {
font-style: italic;
color: #6366f1;
}
.card-content a {
color: #6366f1;
text-decoration: none;
border-bottom: 2px solid #6366f1;
}
.card-content ul,
.card-content ol {
margin: 30px 0;
padding-left: 60px;
}
.card-content li {
margin-bottom: 20px;
line-height: 1.6;
}
.card-content blockquote {
border-left: 8px solid #6366f1;
padding-left: 40px;
background: #f1f5f9;
padding-top: 25px;
padding-bottom: 25px;
padding-right: 30px;
margin: 35px 0;
color: #64748b;
font-style: italic;
border-radius: 0 12px 12px 0;
}
.card-content blockquote p {
margin: 0;
}
.card-content code {
background: #f1f5f9;
padding: 6px 16px;
border-radius: 8px;
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
font-size: 38px;
color: #6366f1;
}
.card-content pre {
background: #1e293b;
color: #e2e8f0;
padding: 40px;
border-radius: 16px;
margin: 35px 0;
overflow-x: visible;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-all;
white-space: pre-wrap;
font-size: 36px;
line-height: 1.5;
}
.card-content pre code {
background: transparent;
color: inherit;
padding: 0;
font-size: inherit;
}
.card-content img {
max-width: 100%;
height: auto;
border-radius: 16px;
margin: 35px auto;
display: block;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
}
.card-content hr {
border: none;
height: 2px;
background: #e2e8f0;
margin: 50px 0;
}
/* Tags 标签样式 */
.tags-container {
margin-top: 50px;
padding-top: 30px;
border-top: 2px solid #e2e8f0;
}
.tag {
display: inline-block;
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
color: white;
padding: 12px 28px;
border-radius: 30px;
font-size: 34px;
margin: 10px 15px 10px 0;
font-weight: 500;
}
/* 信息卡片样式 */
.info-card {
margin: 40px 0;
padding: 40px 50px;
background: linear-gradient(135deg, rgba(99, 102, 241, 0.12) 0%, rgba(139, 92, 246, 0.12) 100%);
border: 2px solid rgba(99, 102, 241, 0.2);
border-radius: 20px;
box-shadow: 0 4px 20px rgba(99, 102, 241, 0.1);
}
/* 页码样式 */
.page-number {
position: absolute;
bottom: 80px;
right: 80px;
font-size: 36px;
color: rgba(255, 255, 255, 0.8);
font-weight: 500;
}
</style>
</head>
<body>
<div class="card-container">
<div class="card-inner">
<div class="card-content">
{{CONTENT}}
</div>
</div>
<div class="page-number">{{PAGE_NUMBER}}</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1080, height=1440">
<title>小红书封面</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700;900&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Noto Sans SC', 'Source Han Sans CN', 'PingFang SC', 'Microsoft YaHei', sans-serif;
width: 1080px;
height: 1440px;
overflow: hidden;
}
.cover-container {
width: 1080px;
height: 1440px;
background: linear-gradient(180deg, #f3f3f3 0%, #f9f9f9 100%);
position: relative;
overflow: hidden;
}
.cover-inner {
position: absolute;
width: 950px;
height: 1310px;
left: 65px;
top: 65px;
background: #F3F3F3;
border-radius: 25px;
display: flex;
flex-direction: column;
padding: 80px 85px;
}
.cover-emoji {
font-size: 180px;
line-height: 1.2;
margin-bottom: 50px;
}
.cover-title {
font-weight: 900;
font-size: 130px;
line-height: 1.4;
background: linear-gradient(180deg, #111827 0%, #4B5563 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
flex: 1;
display: flex;
align-items: flex-start;
word-break: normal;
overflow-wrap: break-word;
}
.cover-subtitle {
font-weight: 350;
font-size: 72px;
line-height: 1.4;
color: #000000;
margin-top: auto;
}
</style>
</head>
<body>
<div class="cover-container">
<div class="cover-inner">
<div class="cover-emoji">{{EMOJI}}</div>
<div class="cover-title">{{TITLE}}</div>
<div class="cover-subtitle">{{SUBTITLE}}</div>
</div>
</div>
</body>
</html>
活泼几何风格 💜
这是 Playful Geometric 风格的示例,灵感来自 Memphis 设计。
糖果般的色彩搭配,硬边框与圆角的对比,让内容更有活力!
特点
- 紫色、粉色、黄色的俏皮配色
- 硬阴影贴纸效果
- 非对称圆角设计
---
新粗野主义风格 ⚡
Neo-Brutalism 风格:RAW. LOUD. UNAPOLOGETIC.
厚重的黑色边框,高饱和度色块,这就是反设计美学!
视觉特点
- 5px 以上的粗边框
- 8-10px 的硬阴影
- 荧光黄与电光青的撞色
---
植物园风格 🌿
Botanical 风格带来自然清新的感觉。
森林绿与米白的搭配,仿佛置身于植物园中
设计理念
- 柔和的绿色系配色
- 自然有机的圆角
- 温暖的米色背景
---
更多风格选择
还有更多精美风格等你探索:
1. Professional - 专业商务风格 2. Retro - 复古怀旧风格 3. Terminal - 终端命令行风格 4. Sketch - 手绘素描风格
使用命令:
python render_xhs.py example.md -t playful-geometric#小红书模板 #排版设计 #内容创作
/* 小红书卡片样式 */
/* CSS 变量定义 */
:root {
--primary-color: #ff2442;
--primary-light: #ff6b81;
--text-primary: #1e293b;
--text-secondary: #334155;
--text-muted: #64748b;
--text-light: #475569;
--background-light: #f8fafc;
--background-gray: #f1f5f9;
--border-color: #e2e8f0;
--border-radius: 25px;
--card-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
--font-family: 'Source Han Sans CN', 'PingFang SC', 'Microsoft YaHei', -apple-system, sans-serif;
}
/* 全局样式重置 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(--font-family);
background: transparent;
color: var(--text-secondary);
line-height: 1.6;
}
/* ========================
封面卡片样式
======================== */
.cover-container {
width: 1080px;
height: 1440px;
background: linear-gradient(180deg, #f3f3f3 0%, #f9f9f9 100%);
position: relative;
overflow: hidden;
}
.cover-inner {
position: absolute;
width: 950px;
height: 1310px;
left: 65px;
top: 65px;
background: #F3F3F3;
border-radius: var(--border-radius);
display: flex;
flex-direction: column;
padding: 60px 85px;
}
.cover-emoji {
font-size: 180px;
line-height: 1.2;
margin-bottom: 40px;
}
.cover-title {
font-weight: 900;
font-size: 130px;
line-height: 1.35;
background: linear-gradient(180deg, #111827 0%, #4B5563 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: auto;
word-break: normal;
overflow-wrap: break-word;
}
.cover-subtitle {
font-weight: 350;
font-size: 72px;
line-height: 1.4;
color: #000000;
margin-top: auto;
padding-bottom: 20px;
}
/* ========================
正文卡片样式
======================== */
.card-container {
width: 1080px;
min-height: 1440px;
background: linear-gradient(180deg, #f3f3f3 0%, #f9f9f9 100%);
position: relative;
padding: 50px;
overflow: hidden;
}
.card-inner {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 60px;
min-height: calc(1440px - 100px);
box-shadow: var(--card-shadow);
backdrop-filter: blur(10px);
}
/* Markdown 内容样式 */
.card-content {
color: var(--text-light);
font-size: 42px;
line-height: 1.7;
}
.card-content h1 {
font-size: 72px;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 40px;
line-height: 1.3;
}
.card-content h2 {
font-size: 56px;
font-weight: 600;
color: var(--text-secondary);
margin: 50px 0 25px 0;
line-height: 1.4;
}
.card-content h3 {
font-size: 48px;
font-weight: 600;
color: var(--text-light);
margin: 40px 0 20px 0;
}
.card-content p {
margin-bottom: 35px;
}
.card-content strong {
font-weight: 700;
color: var(--text-primary);
}
.card-content em {
font-style: italic;
color: var(--primary-color);
}
.card-content a {
color: var(--primary-color);
text-decoration: none;
border-bottom: 2px solid var(--primary-color);
}
.card-content ul,
.card-content ol {
margin: 30px 0;
padding-left: 60px;
}
.card-content li {
margin-bottom: 20px;
line-height: 1.6;
}
.card-content blockquote {
border-left: 8px solid var(--primary-color);
padding-left: 40px;
background: var(--background-gray);
padding-top: 25px;
padding-bottom: 25px;
margin: 35px 0;
color: var(--text-muted);
font-style: italic;
border-radius: 0 12px 12px 0;
}
.card-content blockquote p {
margin: 0;
}
.card-content code {
background: var(--background-gray);
padding: 6px 16px;
border-radius: 8px;
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
font-size: 38px;
color: var(--primary-color);
}
.card-content pre {
background: var(--text-primary);
color: #e2e8f0;
padding: 40px;
border-radius: 16px;
margin: 35px 0;
overflow-x: visible;
overflow-wrap: break-word;
word-wrap: break-word;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: normal;
white-space: pre-wrap;
font-size: 36px;
line-height: 1.5;
}
.card-content pre code {
background: transparent;
color: inherit;
padding: 0;
font-size: inherit;
}
.card-content img {
max-width: 100%;
height: auto;
border-radius: 16px;
margin: 35px auto;
display: block;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
}
.card-content hr {
border: none;
height: 2px;
background: var(--border-color);
margin: 50px 0;
}
/* Tags 标签样式 */
.card-content .tags {
margin-top: 50px;
padding-top: 30px;
border-top: 2px solid var(--border-color);
}
.card-content .tag {
display: inline-block;
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-light) 100%);
color: white;
padding: 12px 28px;
border-radius: 30px;
font-size: 34px;
margin: 10px 15px 10px 0;
font-weight: 500;
}
/* 信息卡片样式 */
.info-card {
margin: 40px 0;
padding: 40px 50px;
background: linear-gradient(135deg, rgba(99, 102, 241, 0.12) 0%, rgba(139, 92, 246, 0.12) 100%);
border: 2px solid rgba(99, 102, 241, 0.2);
border-radius: 20px;
box-shadow: 0 4px 20px rgba(99, 102, 241, 0.1);
}
.info-card.success {
background: linear-gradient(135deg, rgba(16, 185, 129, 0.12) 0%, rgba(5, 150, 105, 0.12) 100%);
border-color: rgba(16, 185, 129, 0.2);
}
.info-card.warning {
background: linear-gradient(135deg, rgba(245, 158, 11, 0.12) 0%, rgba(217, 119, 6, 0.12) 100%);
border-color: rgba(245, 158, 11, 0.2);
}
.info-card.error {
background: linear-gradient(135deg, rgba(239, 68, 68, 0.12) 0%, rgba(220, 38, 38, 0.12) 100%);
border-color: rgba(239, 68, 68, 0.2);
}
/* 页码样式 */
.page-number {
position: absolute;
bottom: 80px;
right: 80px;
font-size: 36px;
color: rgba(255, 255, 255, 0.8);
font-weight: 500;
}
/* ========================
渐变背景预设
======================== */
.bg-gradient-1 {
background: linear-gradient(180deg, #3450E4 0%, #D266DA 100%);
}
.bg-gradient-2 {
background: linear-gradient(180deg, #f3f3f3 0%, #f9f9f9 100%);
}
.bg-gradient-3 {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.bg-gradient-4 {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
.bg-gradient-5 {
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
}
.bg-gradient-6 {
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
}
.bg-gradient-7 {
background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%);
}
.bg-gradient-8 {
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
}/* ============================================
Botanical - 植物园风格
自然柔和,清新淡雅
适配小红书卡片渲染
============================================ */
/* 配色定义
背景色: #F9FAF6 (淡绿白)
前景色: #2D3B36 (深绿灰)
主绿色: #4A7C59 (森林绿)
浅绿: #8FBC8F (淡绿)
棕色: #8B7355 (木质棕)
米色: #E8E4DC (暖米白)
*/
.card-inner {
background-color: #f9faf6;
}
.card-content {
color: #2d3b36;
font-size: 42px;
line-height: 1.8;
}
/* 标题样式 */
.card-content h1 {
font-size: 72px;
font-weight: 700;
color: #4a7c59;
margin-bottom: 40px;
line-height: 1.3;
padding-bottom: 20px;
border-bottom: 4px solid #8fbc8f;
}
.card-content h2 {
font-size: 56px;
font-weight: 600;
color: #3d5a48;
margin: 50px 0 25px 0;
line-height: 1.4;
padding-left: 20px;
border-left: 6px solid #4a7c59;
}
.card-content h3 {
font-size: 48px;
font-weight: 600;
color: #4a7c59;
margin: 40px 0 20px 0;
}
/* 段落 */
.card-content p {
margin-bottom: 35px;
}
/* 粗体 */
.card-content strong,
.card-content b {
font-weight: 700;
color: #2d3b36;
background-color: rgba(143, 188, 143, 0.3);
padding: 0.1em 0.3em;
border-radius: 4px;
}
/* 斜体 */
.card-content em,
.card-content i {
font-style: italic;
color: #4a7c59;
}
/* 链接 */
.card-content a {
color: #4a7c59;
text-decoration: none;
border-bottom: 2px solid #8fbc8f;
transition: all 0.2s;
}
/* 列表 */
.card-content ul,
.card-content ol {
margin: 30px 0;
padding-left: 60px;
}
.card-content li {
margin-bottom: 20px;
line-height: 1.6;
}
.card-content li::marker {
color: #4a7c59;
}
/* 引用块 */
.card-content blockquote {
margin: 35px 0;
padding: 30px 40px;
background-color: #e8e4dc;
color: #3d5a48;
border-left: 6px solid #4a7c59;
border-radius: 0 12px 12px 0;
}
.card-content blockquote p {
margin: 0;
font-style: italic;
}
/* 行内代码 */
.card-content code {
font-family: 'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace;
font-size: 38px;
background-color: #e8e4dc;
color: #8b7355;
padding: 6px 16px;
border-radius: 6px;
}
/* 代码块 */
.card-content pre {
margin: 35px 0;
padding: 40px;
background-color: #2d3b36;
color: #e8e4dc;
border-radius: 12px;
overflow-x: visible;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-all;
white-space: pre-wrap;
}
.card-content pre code {
background-color: transparent;
color: inherit;
padding: 0;
font-size: 36px;
line-height: 1.5;
}
/* 分割线 */
.card-content hr {
margin: 50px 0;
border: none;
height: 3px;
background: linear-gradient(90deg, transparent, #8fbc8f, transparent);
}
/* 图片 */
.card-content img {
display: block;
max-width: 100%;
height: auto;
border-radius: 12px;
margin: 35px auto;
box-shadow: 0 4px 20px rgba(45, 59, 54, 0.1);
}
/* Tags 标签样式 */
.tags-container {
margin-top: 50px;
padding-top: 30px;
border-top: 2px solid #e8e4dc;
}
.tag {
display: inline-block;
background-color: #4a7c59;
color: white;
padding: 12px 28px;
border-radius: 30px;
font-size: 34px;
margin: 10px 15px 10px 0;
font-weight: 500;
}
/* ============================================
Default - 默认风格
小红书原生风格,渐变紫色背景
适配小红书卡片渲染
============================================ */
/* 配色定义
背景色: #FFFFFF (白色卡片)
前景色: #475569 (中性灰)
主色: #6366f1 (靛蓝紫)
浅紫: #8b5cf6 (紫罗兰)
灰色: #64748b (石板灰)
边框: #e2e8f0 (浅灰边框)
*/
.card-content {
color: #475569;
background-color: #ffffff;
font-size: 42px;
line-height: 1.7;
}
/* 标题样式 */
.card-content h1 {
font-size: 72px;
font-weight: 700;
color: #1e293b;
margin-bottom: 40px;
line-height: 1.3;
}
.card-content h2 {
font-size: 56px;
font-weight: 600;
color: #334155;
margin: 50px 0 25px 0;
line-height: 1.4;
}
.card-content h3 {
font-size: 48px;
font-weight: 600;
color: #475569;
margin: 40px 0 20px 0;
}
/* 段落 */
.card-content p {
margin-bottom: 35px;
}
/* 粗体 */
.card-content strong,
.card-content b {
font-weight: 700;
color: #1e293b;
}
/* 斜体 */
.card-content em,
.card-content i {
font-style: italic;
color: #6366f1;
}
/* 链接 */
.card-content a {
color: #6366f1;
text-decoration: none;
border-bottom: 2px solid #6366f1;
}
/* 列表 */
.card-content ul,
.card-content ol {
margin: 30px 0;
padding-left: 60px;
}
.card-content li {
margin-bottom: 20px;
line-height: 1.6;
}
/* 引用块 */
.card-content blockquote {
border-left: 8px solid #6366f1;
padding-left: 40px;
background: #f1f5f9;
padding-top: 25px;
padding-bottom: 25px;
padding-right: 30px;
margin: 35px 0;
color: #64748b;
font-style: italic;
border-radius: 0 12px 12px 0;
}
.card-content blockquote p {
margin: 0;
}
/* 行内代码 */
.card-content code {
background: #f1f5f9;
padding: 6px 16px;
border-radius: 8px;
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
font-size: 38px;
color: #6366f1;
}
/* 代码块 */
.card-content pre {
background: #1e293b;
color: #e2e8f0;
padding: 40px;
border-radius: 16px;
margin: 35px 0;
overflow-x: visible;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-all;
white-space: pre-wrap;
font-size: 36px;
line-height: 1.5;
}
.card-content pre code {
background: transparent;
color: inherit;
padding: 0;
font-size: inherit;
}
/* 图片 */
.card-content img {
max-width: 100%;
height: auto;
border-radius: 16px;
margin: 35px auto;
display: block;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
}
/* 分割线 */
.card-content hr {
border: none;
height: 2px;
background: #e2e8f0;
margin: 50px 0;
}
/* Tags 标签样式 */
.tags-container {
margin-top: 50px;
padding-top: 30px;
border-top: 2px solid #e2e8f0;
}
.tag {
display: inline-block;
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
color: white;
padding: 12px 28px;
border-radius: 30px;
font-size: 34px;
margin: 10px 15px 10px 0;
font-weight: 500;
}
/* ============================================
Neo-Brutalism - 新粗野主义风格
RAW. LOUD. UNAPOLOGETIC.
厚重黑色边框 + 硬阴影 + 高饱和色块
适配小红书卡片渲染
============================================ */
/* 配色定义
背景色: #FFFDF5 (奶油白)
主黑色: #000000 (纯黑)
强调红: #FF4757 (热辣红)
强调黄: #FECA57 (荧光黄)
强调青: #00D2D3 (电光青)
强调紫: #A29BFE (柔和紫)
*/
.card-inner {
background-color: #fffdf5;
/* 纸张纹理感 */
background-image: repeating-linear-gradient(
0deg,
transparent,
transparent 1px,
rgba(0, 0, 0, 0.008) 1px,
rgba(0, 0, 0, 0.008) 2px
);
}
.card-content {
color: #000000;
font-size: 42px;
line-height: 1.7;
}
/* 标题样式 */
.card-content h1 {
font-size: 72px;
font-weight: 900;
padding: 0.4em 0.6em;
background-color: #feca57;
color: #000000;
border: 5px solid #000000;
box-shadow: 8px 8px 0 #000000;
text-transform: uppercase;
margin-bottom: 40px;
line-height: 1.15;
}
.card-content h2 {
font-size: 56px;
font-weight: 900;
padding: 0.35em 0.6em;
background-color: #ffffff;
color: #000000;
border: 4px solid #000000;
border-left: 10px solid #ff4757;
box-shadow: 6px 6px 0 #000000;
margin: 50px 0 25px 0;
line-height: 1.15;
}
.card-content h3 {
font-size: 48px;
font-weight: 900;
padding: 0.25em 0.5em;
color: #000000;
background-color: #00d2d3;
border: 4px solid #000000;
box-shadow: 4px 4px 0 #000000;
display: inline-block;
margin: 40px 0 20px 0;
line-height: 1.15;
}
/* 段落 */
.card-content p {
margin-bottom: 35px;
}
/* 加粗 - 黄色高亮块 */
.card-content strong,
.card-content b {
font-weight: 900;
color: #000000;
background-color: #feca57;
padding: 0.08em 0.25em;
border: 2px solid #000000;
}
/* 斜体 - 红色下划线 */
.card-content em,
.card-content i {
font-style: italic;
color: #000000;
border-bottom: 3px solid #ff4757;
}
/* 链接 - 青色底纹+粗下划线 */
.card-content a {
color: #000000;
text-decoration: none;
background-color: #00d2d3;
padding: 0.08em 0.2em;
border-bottom: 4px solid #000000;
font-weight: 600;
}
/* 列表 */
.card-content ul,
.card-content ol {
margin: 30px 0;
padding-left: 60px;
}
.card-content ul {
list-style-type: square;
}
.card-content li {
margin-bottom: 20px;
line-height: 1.6;
}
.card-content li::marker {
color: #ff4757;
font-weight: 900;
}
/* 引用块 - 紫色色块+硬阴影 */
.card-content blockquote {
margin: 35px 0;
padding: 0.85em 1.1em;
background-color: #a29bfe;
color: #000000;
border: 5px solid #000000;
border-left: 12px solid #ff4757;
box-shadow: 8px 8px 0 #000000;
}
.card-content blockquote p {
margin: 0;
font-style: normal;
}
/* 行内代码 - 黄色背景 */
.card-content code {
font-family: 'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace;
font-size: 38px;
background-color: #feca57;
color: #000000;
padding: 0.18em 0.45em;
border: 3px solid #000000;
font-weight: 600;
}
/* 代码块 - 黑色背景+红色阴影 */
.card-content pre {
margin: 35px 0;
padding: 40px;
background-color: #000000;
border: 5px solid #000000;
box-shadow: 10px 10px 0 #ff4757;
overflow-x: visible;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-all;
white-space: pre-wrap;
}
.card-content pre code {
background-color: transparent;
color: #ffffff;
padding: 0;
border: none;
font-size: 36px;
line-height: 1.55;
font-weight: 400;
}
/* 分割线 - 粗犷几何 */
.card-content hr {
margin: 50px 0;
border: none;
height: 8px;
background-color: #000000;
box-shadow: 5px 5px 0 #ff4757;
}
/* 图片 - 厚边框框架 */
.card-content img {
display: block;
max-width: 100%;
height: auto;
border: 5px solid #000000;
box-shadow: 8px 8px 0 #000000;
margin: 35px auto;
}
/* Tags 标签样式 */
.tags-container {
margin-top: 50px;
padding-top: 30px;
border-top: 5px solid #000000;
}
.tag {
display: inline-block;
background-color: #ff4757;
color: white;
padding: 12px 28px;
font-size: 34px;
margin: 10px 15px 10px 0;
font-weight: 800;
border: 3px solid #000000;
box-shadow: 4px 4px 0 #000000;
text-transform: uppercase;
}
/* ============================================
Playful Geometric - 活泼几何风格
Memphis 设计与现代波普艺术的融合
适配小红书卡片渲染
============================================ */
/* 配色定义
背景色: #FFFDF5 (温暖奶油白 - 纸质感)
前景色: #1E293B (石板色 800)
主紫色: #8B5CF6 (Vivid Violet - 品牌主色)
粉色: #F472B6 (Hot Pink - 俏皮活力)
黄色: #FBBF24 (Amber/Yellow - 乐观积极)
薄荷绿: #34D399 (Emerald/Mint - 清新感)
*/
.card-inner {
background-color: #fffdf5;
/* 几何点阵背景装饰 */
background-image: radial-gradient(circle, #e2e8f0 1.5px, transparent 1.5px);
background-size: 24px 24px;
}
.card-content {
color: #1e293b;
font-size: 42px;
line-height: 1.7;
}
/* 标题样式 */
.card-content h1 {
font-size: 72px;
font-weight: 800;
padding: 0.4em 0.65em;
background-color: #8b5cf6;
color: #ffffff;
border: 3px solid #1e293b;
border-radius: 0 28px 0 28px;
box-shadow: 6px 6px 0 #1e293b;
margin-bottom: 40px;
line-height: 1.25;
}
.card-content h2 {
font-size: 56px;
font-weight: 800;
padding: 0.3em 0.6em;
background-color: #fffdf5;
color: #7c3aed;
border: 3px solid #1e293b;
border-left: 10px solid #8b5cf6;
border-radius: 0 20px 20px 0;
box-shadow: 5px 5px 0 #f472b6;
margin: 50px 0 25px 0;
line-height: 1.25;
}
.card-content h3 {
font-size: 48px;
font-weight: 800;
padding: 0.25em 0.65em;
color: #1e293b;
background-color: #fbbf24;
border: 2px solid #1e293b;
border-radius: 9999px;
display: inline-block;
box-shadow: 4px 4px 0 #1e293b;
margin: 40px 0 20px 0;
line-height: 1.25;
}
/* 段落 */
.card-content p {
margin-bottom: 35px;
}
/* 粗体 - 黄色高亮贴纸效果 */
.card-content strong,
.card-content b {
font-weight: 700;
color: #1e293b;
background-color: #fbbf24;
padding: 0.1em 0.3em;
border-radius: 6px;
box-shadow: 2px 2px 0 rgba(30, 41, 59, 0.2);
}
/* 斜体 - 紫色强调 */
.card-content em,
.card-content i {
font-style: italic;
color: #8b5cf6;
font-weight: 500;
}
/* 链接 - 活泼几何风格 */
.card-content a {
color: #8b5cf6;
text-decoration: none;
font-weight: 500;
background-color: rgba(139, 92, 246, 0.1);
padding: 0.08em 0.25em;
border-radius: 5px;
border-bottom: 2px solid #8b5cf6;
}
/* 列表 */
.card-content ul,
.card-content ol {
margin: 30px 0;
padding-left: 60px;
}
.card-content li {
margin-bottom: 20px;
line-height: 1.6;
}
.card-content li::marker {
color: #8b5cf6;
font-weight: 700;
}
/* 引用块 - 气泡贴纸风格 */
.card-content blockquote {
margin: 35px 0;
padding: 0.8em 1.1em 0.8em 1.6em;
background-color: #f472b6;
color: #1e293b;
border: 3px solid #1e293b;
border-left: 10px solid #fbbf24;
border-radius: 0 28px 28px 0;
box-shadow: 6px 6px 0 #1e293b;
}
.card-content blockquote p {
margin: 0;
font-style: normal;
}
/* 行内代码 - 黄色贴纸 */
.card-content code {
font-family: 'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace;
font-size: 38px;
background-color: #fbbf24;
color: #1e293b;
padding: 0.15em 0.5em;
border: 2px solid #1e293b;
border-radius: 8px;
}
/* 代码块 - 深色背景配紫色硬阴影 */
.card-content pre {
margin: 35px 0;
padding: 40px;
background-color: #1e293b;
border: 3px solid #1e293b;
border-radius: 20px;
box-shadow: 8px 8px 0 #8b5cf6;
overflow-x: visible;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-all;
white-space: pre-wrap;
}
.card-content pre code {
background-color: transparent;
color: #ffffff;
padding: 0;
border: none;
border-radius: 0;
font-size: 36px;
line-height: 1.55;
}
/* 分割线 - 彩虹几何装饰条 */
.card-content hr {
margin: 50px 0;
border: none;
height: 10px;
background: repeating-linear-gradient(
90deg,
#8b5cf6 0px,
#8b5cf6 24px,
#f472b6 24px,
#f472b6 48px,
#fbbf24 48px,
#fbbf24 72px,
#34d399 72px,
#34d399 96px
);
border-radius: 9999px;
}
/* 图片 - 贴纸框架风格 */
.card-content img {
display: block;
max-width: 100%;
height: auto;
border: 4px solid #1e293b;
border-radius: 20px;
box-shadow: 8px 8px 0 #1e293b;
margin: 35px auto;
}
/* Tags 标签样式 */
.tags-container {
margin-top: 50px;
padding-top: 30px;
border-top: 5px solid #fbbf24;
}
.tag {
display: inline-block;
background-color: #8b5cf6;
color: white;
padding: 12px 28px;
border-radius: 9999px;
font-size: 34px;
margin: 10px 15px 10px 0;
font-weight: 600;
border: 2px solid #1e293b;
box-shadow: 3px 3px 0 #1e293b;
}
/* ============================================
Professional - 专业商务风格
简洁、稳重、可读性强
适配小红书卡片渲染
============================================ */
/* 配色定义
背景色: #FFFFFF (纯白)
前景色: #1A202C (深灰黑)
主蓝色: #2563EB (专业蓝)
浅蓝: #DBEAFE (淡蓝背景)
灰色: #64748B (中性灰)
边框: #E2E8F0 (浅灰边框)
*/
.card-inner {
background-color: #ffffff;
}
.card-content {
color: #1a202c;
font-size: 42px;
line-height: 1.75;
}
/* 标题样式 */
.card-content h1 {
font-size: 72px;
font-weight: 700;
color: #1a202c;
margin-bottom: 40px;
line-height: 1.3;
padding-bottom: 20px;
border-bottom: 3px solid #2563eb;
}
.card-content h2 {
font-size: 56px;
font-weight: 600;
color: #1e40af;
margin: 50px 0 25px 0;
line-height: 1.4;
}
.card-content h3 {
font-size: 48px;
font-weight: 600;
color: #374151;
margin: 40px 0 20px 0;
}
/* 段落 */
.card-content p {
margin-bottom: 35px;
color: #374151;
}
/* 粗体 */
.card-content strong,
.card-content b {
font-weight: 700;
color: #1a202c;
}
/* 斜体 */
.card-content em,
.card-content i {
font-style: italic;
color: #2563eb;
}
/* 链接 */
.card-content a {
color: #2563eb;
text-decoration: none;
border-bottom: 2px solid #93c5fd;
}
/* 列表 */
.card-content ul,
.card-content ol {
margin: 30px 0;
padding-left: 60px;
}
.card-content li {
margin-bottom: 20px;
line-height: 1.6;
color: #374151;
}
.card-content li::marker {
color: #2563eb;
}
/* 引用块 */
.card-content blockquote {
margin: 35px 0;
padding: 30px 40px;
background-color: #dbeafe;
color: #1e40af;
border-left: 5px solid #2563eb;
border-radius: 0 8px 8px 0;
}
.card-content blockquote p {
margin: 0;
}
/* 行内代码 */
.card-content code {
font-family: 'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace;
font-size: 38px;
background-color: #f1f5f9;
color: #0f172a;
padding: 6px 16px;
border-radius: 6px;
}
/* 代码块 */
.card-content pre {
margin: 35px 0;
padding: 40px;
background-color: #0f172a;
color: #e2e8f0;
border-radius: 12px;
overflow-x: visible;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-all;
white-space: pre-wrap;
}
.card-content pre code {
background-color: transparent;
color: inherit;
padding: 0;
font-size: 36px;
line-height: 1.5;
}
/* 分割线 */
.card-content hr {
margin: 50px 0;
border: none;
height: 2px;
background-color: #e2e8f0;
}
/* 图片 */
.card-content img {
display: block;
max-width: 100%;
height: auto;
border-radius: 8px;
margin: 35px auto;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
}
/* Tags 标签样式 */
.tags-container {
margin-top: 50px;
padding-top: 30px;
border-top: 2px solid #e2e8f0;
}
.tag {
display: inline-block;
background-color: #2563eb;
color: white;
padding: 12px 28px;
border-radius: 6px;
font-size: 34px;
margin: 10px 15px 10px 0;
font-weight: 500;
}
/* ============================================
Retro - 复古怀旧风格
温暖的米色调和复古排版
适配小红书卡片渲染
============================================ */
/* 配色定义
背景色: #FDF6E3 (复古米黄)
前景色: #5C4033 (棕褐色)
主色: #D35400 (复古橙)
浅橙: #F39C12 (暖黄)
深棕: #8B4513 (马鞍棕)
米色: #F5DEB3 (小麦色)
*/
.card-inner {
background-color: #fdf6e3;
}
.card-content {
color: #5c4033;
font-size: 42px;
line-height: 1.8;
}
/* 标题样式 */
.card-content h1 {
font-size: 72px;
font-weight: 700;
color: #d35400;
margin-bottom: 40px;
line-height: 1.3;
border-bottom: 4px double #d35400;
padding-bottom: 15px;
}
.card-content h2 {
font-size: 56px;
font-weight: 600;
color: #8b4513;
margin: 50px 0 25px 0;
line-height: 1.4;
}
.card-content h3 {
font-size: 48px;
font-weight: 600;
color: #a0522d;
margin: 40px 0 20px 0;
font-style: italic;
}
/* 段落 */
.card-content p {
margin-bottom: 35px;
}
/* 粗体 */
.card-content strong,
.card-content b {
font-weight: 700;
color: #8b4513;
}
/* 斜体 */
.card-content em,
.card-content i {
font-style: italic;
color: #d35400;
}
/* 链接 */
.card-content a {
color: #d35400;
text-decoration: underline;
text-decoration-style: wavy;
text-underline-offset: 4px;
}
/* 列表 */
.card-content ul,
.card-content ol {
margin: 30px 0;
padding-left: 60px;
}
.card-content li {
margin-bottom: 20px;
line-height: 1.6;
}
.card-content li::marker {
color: #d35400;
}
/* 引用块 */
.card-content blockquote {
margin: 35px 0;
padding: 30px 40px;
background-color: #f5deb3;
color: #5c4033;
border-left: 6px solid #d35400;
font-style: italic;
}
.card-content blockquote p {
margin: 0;
}
/* 行内代码 */
.card-content code {
font-family: 'Courier New', Courier, monospace;
font-size: 38px;
background-color: #f5deb3;
color: #8b4513;
padding: 6px 16px;
border-radius: 4px;
}
/* 代码块 */
.card-content pre {
margin: 35px 0;
padding: 40px;
background-color: #5c4033;
color: #fdf6e3;
border: 3px solid #8b4513;
overflow-x: visible;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-all;
white-space: pre-wrap;
}
.card-content pre code {
background-color: transparent;
color: inherit;
padding: 0;
font-size: 36px;
line-height: 1.5;
}
/* 分割线 */
.card-content hr {
margin: 50px 0;
border: none;
height: 3px;
background: repeating-linear-gradient(
90deg,
#d35400,
#d35400 10px,
transparent 10px,
transparent 20px
);
}
/* 图片 */
.card-content img {
display: block;
max-width: 100%;
height: auto;
margin: 35px auto;
border: 4px solid #8b4513;
box-shadow: 4px 4px 0 #d35400;
}
/* Tags 标签样式 */
.tags-container {
margin-top: 50px;
padding-top: 30px;
border-top: 3px double #d35400;
}
.tag {
display: inline-block;
background-color: #d35400;
color: #fdf6e3;
padding: 12px 28px;
border-radius: 4px;
font-size: 34px;
margin: 10px 15px 10px 0;
font-weight: 600;
border: 2px solid #8b4513;
}
/* ============================================
Sketch - 手绘素描风格
纸张质感、手写字体效果
适配小红书卡片渲染
============================================ */
/* 配色定义
背景色: #FFFEF9 (米白纸张)
前景色: #333333 (炭笔黑)
主色: #555555 (铅笔灰)
强调: #E74C3C (红色标记笔)
蓝色: #3498DB (蓝色圆珠笔)
黄色: #F1C40F (荧光笔)
*/
.card-inner {
background-color: #fffef9;
/* 纸张网格背景 */
background-image:
linear-gradient(#e0e0e0 1px, transparent 1px),
linear-gradient(90deg, #e0e0e0 1px, transparent 1px);
background-size: 30px 30px;
}
.card-content {
color: #333333;
font-size: 42px;
line-height: 1.8;
}
/* 标题样式 */
.card-content h1 {
font-size: 72px;
font-weight: 700;
color: #333333;
margin-bottom: 40px;
line-height: 1.3;
text-decoration: underline;
text-decoration-style: wavy;
text-decoration-color: #e74c3c;
text-underline-offset: 10px;
}
.card-content h2 {
font-size: 56px;
font-weight: 600;
color: #555555;
margin: 50px 0 25px 0;
line-height: 1.4;
border-bottom: 3px dashed #999999;
padding-bottom: 10px;
}
.card-content h3 {
font-size: 48px;
font-weight: 600;
color: #666666;
margin: 40px 0 20px 0;
}
/* 段落 */
.card-content p {
margin-bottom: 35px;
}
/* 粗体 - 加粗圈标 */
.card-content strong,
.card-content b {
font-weight: 700;
color: #333333;
background-color: #f1c40f;
padding: 0.1em 0.3em;
border-radius: 4px;
}
/* 斜体 */
.card-content em,
.card-content i {
font-style: italic;
color: #3498db;
}
/* 链接 */
.card-content a {
color: #3498db;
text-decoration: underline;
text-decoration-style: dashed;
}
/* 列表 */
.card-content ul,
.card-content ol {
margin: 30px 0;
padding-left: 60px;
}
.card-content li {
margin-bottom: 20px;
line-height: 1.6;
}
.card-content li::marker {
color: #e74c3c;
}
/* 引用块 - 便签纸风格 */
.card-content blockquote {
margin: 35px 0;
padding: 30px 40px;
background-color: #fff9c4;
color: #333333;
border: none;
box-shadow: 3px 3px 0 #ddd;
transform: rotate(-0.5deg);
}
.card-content blockquote p {
margin: 0;
font-style: italic;
}
/* 行内代码 */
.card-content code {
font-family: 'Courier New', Courier, monospace;
font-size: 38px;
background-color: #f0f0f0;
color: #555555;
padding: 6px 16px;
border: 2px dashed #999999;
border-radius: 4px;
}
/* 代码块 */
.card-content pre {
margin: 35px 0;
padding: 40px;
background-color: #f5f5f5;
color: #333333;
border: 2px solid #333333;
overflow-x: visible;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-all;
white-space: pre-wrap;
}
.card-content pre code {
background-color: transparent;
color: inherit;
padding: 0;
border: none;
font-size: 36px;
line-height: 1.5;
}
/* 分割线 */
.card-content hr {
margin: 50px 0;
border: none;
height: 3px;
background: repeating-linear-gradient(
90deg,
#333333,
#333333 5px,
transparent 5px,
transparent 10px
);
}
/* 图片 */
.card-content img {
display: block;
max-width: 100%;
height: auto;
margin: 35px auto;
border: 3px solid #333333;
box-shadow: 5px 5px 0 #ddd;
transform: rotate(0.5deg);
}
/* Tags 标签样式 */
.tags-container {
margin-top: 50px;
padding-top: 30px;
border-top: 2px dashed #999999;
}
.tag {
display: inline-block;
background-color: #fff;
color: #333333;
padding: 12px 28px;
border: 2px solid #333333;
font-size: 34px;
margin: 10px 15px 10px 0;
font-weight: 600;
transform: rotate(-1deg);
}
/* ============================================
Terminal - 终端/命令行风格
黑客美学,极简科技感
适配小红书卡片渲染
============================================ */
/* 配色定义
背景色: #0D1117 (深黑)
前景色: #C9D1D9 (淡灰白)
主绿: #39D353 (终端绿)
黄色: #F0E68C (警告黄)
青色: #58A6FF (链接蓝)
紫色: #A371F7 (高亮紫)
*/
.card-inner {
background-color: #0d1117;
}
.card-content {
color: #c9d1d9;
font-family: 'SF Mono', 'Monaco', 'Consolas', 'Liberation Mono', monospace;
font-size: 40px;
line-height: 1.6;
}
/* 标题样式 */
.card-content h1 {
font-size: 68px;
font-weight: 700;
color: #39d353;
margin-bottom: 40px;
line-height: 1.3;
}
.card-content h1::before {
content: '# ';
color: #58a6ff;
}
.card-content h2 {
font-size: 54px;
font-weight: 600;
color: #58a6ff;
margin: 50px 0 25px 0;
line-height: 1.4;
}
.card-content h2::before {
content: '## ';
color: #a371f7;
}
.card-content h3 {
font-size: 46px;
font-weight: 600;
color: #a371f7;
margin: 40px 0 20px 0;
}
.card-content h3::before {
content: '### ';
color: #39d353;
}
/* 段落 */
.card-content p {
margin-bottom: 35px;
}
/* 粗体 */
.card-content strong,
.card-content b {
font-weight: 700;
color: #39d353;
}
/* 斜体 */
.card-content em,
.card-content i {
font-style: italic;
color: #f0e68c;
}
/* 链接 */
.card-content a {
color: #58a6ff;
text-decoration: underline;
text-underline-offset: 4px;
}
/* 列表 */
.card-content ul,
.card-content ol {
margin: 30px 0;
padding-left: 60px;
}
.card-content li {
margin-bottom: 20px;
line-height: 1.6;
}
.card-content li::marker {
color: #39d353;
}
/* 引用块 */
.card-content blockquote {
margin: 35px 0;
padding: 30px 40px;
background-color: #161b22;
color: #8b949e;
border-left: 4px solid #39d353;
}
.card-content blockquote::before {
content: '> ';
color: #39d353;
}
.card-content blockquote p {
margin: 0;
}
/* 行内代码 */
.card-content code {
font-family: inherit;
font-size: 38px;
background-color: #21262d;
color: #f0e68c;
padding: 6px 16px;
border-radius: 6px;
}
/* 代码块 */
.card-content pre {
margin: 35px 0;
padding: 40px;
background-color: #161b22;
color: #c9d1d9;
border: 1px solid #30363d;
border-radius: 8px;
overflow-x: visible;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-all;
white-space: pre-wrap;
}
.card-content pre code {
background-color: transparent;
color: inherit;
padding: 0;
font-size: 36px;
line-height: 1.5;
}
/* 分割线 */
.card-content hr {
margin: 50px 0;
border: none;
height: 2px;
background-color: #30363d;
}
/* 图片 */
.card-content img {
display: block;
max-width: 100%;
height: auto;
margin: 35px auto;
border: 1px solid #30363d;
border-radius: 8px;
}
/* Tags 标签样式 */
.tags-container {
margin-top: 50px;
padding-top: 30px;
border-top: 1px solid #30363d;
}
.tag {
display: inline-block;
background-color: #21262d;
color: #39d353;
padding: 12px 28px;
border-radius: 30px;
font-size: 32px;
margin: 10px 15px 10px 0;
font-weight: 500;
border: 1px solid #39d353;
}
Bud100� %trodsclboretrodsclboolb�bplist00�]Sh @� @� @� @retrolg1ScompretrolsvCblobBbplist00�
UVWXZXiconSize_showIconPreviewWcolumns_calculateAllSizes_scrollPositionYXtextSize_scrollPositionXZsortColumn_useRelativeDates_viewOptionsVersion#@0 �!%*/49>BGKO�ZidentifierYascendingUwidthWvisibleTname �Xubiquity#�\dateModified� �"[dateCreated�&(Tsizea �+-Tkind s �02Ulabel d�57Wversion K�:<Xcomments ,�?^dateLastOpened�CEZshareOwner��HE_shareLastEditor�LYdateAdded�QS�_invitationStatus##@*#�c@\dateModified &8@Tfo��������������"/023<HIJSXY[\ejkmnw}~�������������������������12;DMZ[[lretrolsvpblob�bplist00�
GHIJLXiconSize_showIconPreviewWcolumns_calculateAllSizes_scrollPositionYXtextSize_scrollPositionXZsortColumn_useRelativeDates_viewOptionsVersion#@0 � %*/48=BXcommentsUlabelWversion[dateCreatedTsize\dateModifiedTkindTname^dateLastOpened�WvisibleYascendingUwidthUindex ,�#$ d�() K�,.��23 a�,7 �;< s�@A �,E##@*#�c@\dateModified &8@Tfo������������(0:@FGHKMVWXZ\efgiktuwxz�����������������������������M�retromoDDblobE������AretromodDblobE������Aretroph1ScompretrovSrnlongSketchdsclboolSketchlg1ScompSketchmoDDblob�
�F���ASketchmodDblob�
�F���ASketchph1Scompterminalbwspblob�bplist00�]ShowStatusBar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar _{{833, 336}, {920, 436}} #/;R_klmno�
�terminaldsclboolterminallg1ScompterminallsvCblobBbplist00�
UVWXZ_viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizes_scrollPositionYXtextSize_scrollPositionXZsortColumn_useRelativeDatesXiconSize �!%*/49>BGKO�ZidentifierYascendingUwidthWvisibleTname �Xubiquity#�\dateModified� �"[dateCreated�&(Tsizea �+-Tkind s �02Ulabel d�57Wversion K�:<Xcomments ,�?^dateLastOpened�CEZshareOwner��HE_shareLastEditor�LYdateAdded�QS�_invitationStatus##@*#�c�\dateModified #@02DL`r{�����������
!*78:;DPQR[`acdmrsuv������������������������
"#%&9:CLUbc[lterminallsvpblob�bplist00�
GHIJL_viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizes_scrollPositionYXtextSize_scrollPositionXZsortColumn_useRelativeDatesXiconSize � %*/48=BXcommentsUlabelWversion[dateCreatedTsize\dateModifiedTkindTname^dateLastOpened�WvisibleYascendingUwidthUindex ,�#$ d�() K�,.��23 a�,7 �;< s�@A �,E##@*#�c�\dateModified #@02DL`r{�����������'08BHNOPSU^_`bdmnoqs|}�������������������������������M�terminalmoDDblob�������AterminalmodDblob�������Aterminalph1ScompterminalvSrnlongauto-fitbwspblob�bplist00�]ShowStatusBar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar _{{833, 336}, {920, 436}} #/;R_klmno�
�auto-fitdsclboolauto-fitlg1Scompauto-fitlsvCblobBbplist00�
UVWXZXiconSize_showIconPreviewWcolumns_calculateAllSizes_scrollPositionYXtextSize_scrollPositionXZsortColumn_useRelativeDates_viewOptionsVersion#@0 �!%*/49>BGKO�ZidentifierYascendingUwidthWvisibleTname �Xubiquity#�\dateModified� �"[dateCreated�&(Tsizea �+-Tkind s �02Ulabel d�57Wversion K�:<Xcomments ,�?^dateLastOpened�CEZshareOwner��HE_shareLastEditor�LYdateAdded�QS�_invitationStatus##@*#�c@\dateModified &8@Tfo��������������"/023<HIJSXY[\ejkmnw}~�������������������������12;DMZ[[lauto-fitlsvpblob�bplist00�
GHIJLXiconSize_showIconPreviewWcolumns_calculateAllSizes_scrollPositionYXtextSize_scrollPositionXZsortColumn_useRelativeDates_viewOptionsVersion#@0 � %*/48=BXcommentsUlabelWversion[dateCreatedTsize\dateModifiedTkindTname^dateLastOpened�WvisibleYascendingUwidthUindex ,�#$ d�() K�,.��23 a�,7 �;< s�@A �,E##@*#�c@\dateModified &8@Tfo������������(0:@FGHKMVWXZ\efgiktuwxz�����������������������������M�auto-fitmoDDblob��^���Aauto-fitmodDblob��^���Aauto-fitph1Scompauto-fitvSrnlongplayful-geometricdsclboolplayful-geometriclg1Scompplayful-geometricmoDDblob�|���Aplayful-geometricmodDblob�|���Aplayful-geometricph1Scompretrobwspblob�bplist00�]ShowStatusBar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar _{{833, 336}, {920, 436}} #/;R_klmno�
�\dateModified� �"[dateCreated�&(Tsizea �+-Tkind s �02Ulabel d�57Wversion K�:<Xcomments ,�?^dateLastOpened�CEZshareOwner��HE_shareLastEditor�LYdateAdded�QS�_invitationStatus##@*#�c�\dateModified #@02DL`r{�����������
!*78:;DPQR[`acdmrsuv������������������������
"#%&9:CLUbc[lterminallsvpblob�bplist00�
GHIJL_viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizes_scrollPositionYXtextSize_scrollPositionXZsortColumn_useRelativeDatesXiconSize � %*/48=BXcommentsUlabelWversion[dateCreatedTsize\dateModifiedTkindTname^dateLastOpened�WvisibleYascendingUwidthUindex ,�#$ d�() K�,.��23 a�,7 �;< s�@A �,E##@*#�c�\dateModified #@02DL`r{�����������'08BHNOPSU^_`bdmnoqs|}�������������������������������M�terminalmoDDblob�������AterminalmodDblob�������Aterminalph1ScompterminalvSrnlong0E� DSDB `�8@� @� @scrollPositionYXtextSize_scrollPositionXZsortColumn_useRelativeDates_viewOptionsVersion#@0 � %*/48=BXcommentsUlabelWversion[dateCreatedTsize\dateModifiedTkindTname^dateLastOpened�WvisibleYascendingUwidthUindex ,�#$ d�() K�,.��23 a�,7 �;< s�@A �,E##@*#�c@\dateModified &8@Tfo������������(0:@FGHKMVWXZ\efgiktuwxz�����������������������������M�auto-fitmoDDblob��^���Aauto-fitmodDblob��^���Aauto-fitph1Scompauto-fitvSrnlongplayful神器一:Notion 📝
全能型笔记工具,支持数据库、看板、日历等多种视图。
一个工具替代十个 App,笔记、任务、项目管理全搞定!
核心功能:
- 📊 灵活的数据库视图
- 🔗 双向链接
- 🎨 丰富的模板库
- 👥 团队协作
神器二:Raycast ⚡
Mac 上的效率启动器,比 Spotlight 强大 100 倍!
# 快捷命令示例
raycast://extensions/raycast/clipboard/clipboard-history必装插件推荐:
- 剪贴板历史
- 窗口管理
- 快捷短语
- API 调试工具
总结 🎯
效率提升不在于工具多少,而在于是否真正用起来。
选择 2-3 个适合自己的工具,持续使用,形成习惯,你就能:
✅ 节省 50% 的时间 ✅ 减少 80% 的焦虑 ✅ 提升 100% 的专注力
神器一:Notion 📝
全能型笔记工具,支持数据库、看板、日历等多种视图。
一个工具替代十个 App,笔记、任务、项目管理全搞定!
核心功能:
- 📊 灵活的数据库视图
- 🔗 双向链接
- 🎨 丰富的模板库
- 👥 团队协作
---
神器二:Raycast ⚡
Mac 上的效率启动器,比 Spotlight 强大 100 倍!
# 快捷命令示例
raycast://extensions/raycast/clipboard/clipboard-history必装插件推荐:
- 剪贴板历史
- 窗口管理
- 快捷短语
- API 调试工具
---
神器三:Arc 浏览器 🌈
全新理念的浏览器体验:
- 侧边栏标签管理
- 空间分组功能
- 内置笔记和画板
- 极简无干扰模式
---
神器四:Warp 终端 🖥️
基于 Rust 的现代化终端:
# 支持 AI 智能补全
def example():
print("Hello Warp!")- ⚡ 极速性能
- 🤖 AI 智能提示
- 📋 自动补全
- 🎯 分组工作区
---
神器五:Fig 自动补全 🔮
终端命令自动补全神器:
- 数百种 CLI 工具支持
- 可视化参数提示
- 团队协作分享
# 小红书 Cookie 配置
# 将此文件复制为 .env 并填入真实的 Cookie
#
# 获取方式:
# 1. 在浏览器中登录小红书 (https://www.xiaohongshu.com)
# 2. 打开开发者工具 (F12)
# 3. 在 Network 标签中,访问任意页面
# 4. 查看请求头中的 Cookie,复制完整的 cookie 字符串
XHS_COOKIE=your_cookie_string_here
# 注意事项:
# - Cookie 有有效期,过期后需要重新获取
# - 不要分享你的 Cookie,它包含登录凭证
{
"name": "md2redbook",
"version": "2.0.0",
"description": "小红书笔记素材创作工具 - 支持多种排版样式和智能分页",
"main": "scripts/render_xhs_v2.js",
"scripts": {
"render": "node scripts/render_xhs_v2.js",
"install-browsers": "npx playwright install chromium"
},
"keywords": [
"xiaohongshu",
"markdown",
"image-generation",
"social-media"
],
"author": "",
"license": "MIT",
"dependencies": {
"js-yaml": "^4.1.0",
"marked": "^11.0.0",
"playwright": "^1.58.0"
}
}📕 Auto-Redbook-Skills(已重构版)
⚠️ 使用此工具前情确保已悉知官方 3 月 10 日发布的公告
公告地址:关于打击AI托管运营账号的治理公告
方式一:Claude Code Plugin 安装(推荐)
# 添加本仓库为 marketplace
/plugin marketplace add comeonzhj/Auto-Redbook-Skills
# 安装插件
/plugin install auto-redbook-skills@comeonzhj-Auto-Redbook-Skills安装后运行 /reload-plugins 即可使用。
方式二:一句话安装
跟你的 Agent 说:
拉取下面的项目,安装其中的技能:https://github.com/comeonzhj/Auto-Redbook-Skills
方式三:手动安装
详见下方使用方式总览。
---
自动撰写小红书笔记、生成多主题卡片、可选自动发布的 Skills
当前版本对渲染脚本和样式系统做了一次完整重构,感谢 Cursor 的辅助开发 🙌
---
✨ 本次重构亮点
- 🎨 8 套主题皮肤:默认简约灰 + Playful Geometric / Neo-Brutalism / Botanical / Professional / Retro / Terminal / Sketch
- 📐 4 种分页模式:
separator:按---分隔手动分页auto-fit:固定尺寸,自动整体缩放内容,避免溢出/大面积留白auto-split:根据渲染后高度自动拆分为多张卡片dynamic:根据内容动态调整图片高度- 🧱 统一卡片结构:外层浅灰背景(
card-container)+ 内层主题背景(card-inner)+ 纯排版层(card-content) - 🧠 封面与正文一体化:封面背景、标题渐变和正文卡片背景都按主题自动匹配
---
🖼 主题效果示例
所有示例均为 1080×1440px,小红书推荐 3:4 比例
更多示例去 demo 中查看
| !Playful Geometric | !Retro |
| !Sketch | !Terminal |
Auto-fit 模式示例(自动缩放)
!Auto Fit
---
🚀 使用方式总览
1. 克隆项目
git clone https://github.com/comeonzhj/Auto-Redbook-Skills.git
cd Auto-Redbook-Skills可以将本项目放到支持 Skills 的客户端目录,例如:
- Claude:
~/.claude/skills/ - Alma:
~/.config/Alma/skills/ - TRAE:
/your-path/.trae/skills/
2. 安装依赖
Python:
pip install -r requirements.txt
playwright install chromiumNode.js:
npm install
npx playwright install chromium---
🎨 渲染图片(Python)
核心脚本:scripts/render_xhs.py
# 最简单用法(默认主题 + 手动分页)
python scripts/render_xhs.py demos/content.md
# 使用自动分页(推荐:内容长短难控)
python scripts/render_xhs.py demos/content.md -m auto-split
# 使用固定尺寸自动缩放(auto-fit)
python scripts/render_xhs.py demos/content_auto_fit.md -m auto-fit
# 切换主题(例如 Playful Geometric)
python scripts/render_xhs.py demos/content.md -t playful-geometric -m auto-split
# 自定义尺寸和像素比
python scripts/render_xhs.py demos/content.md -t retro -m dynamic --width 1080 --height 1440 --max-height 2160 --dpr 2主要参数:
| 参数 | 简写 | 说明 |
|---|---|---|
--theme | -t | 主题:default、playful-geometric、neo-brutalism、botanical、professional、retro、terminal、sketch |
--mode | -m | 分页模式:separator / auto-fit / auto-split / dynamic |
--width | -w | 图片宽度(默认 1080) |
--height | 图片高度(默认 1440,dynamic 为最小高度) | |
--max-height | dynamic 模式最大高度(默认 2160) | |
--dpr | 设备像素比,控制清晰度(默认 2) |
生成结果会包含:封面cover.png+ 正文卡片card_1.png、card_2.png...
---
🎨 渲染图片(Node.js)
脚本:scripts/render_xhs.js,参数与 Python 基本一致:
# 默认主题 + 手动分页
node scripts/render_xhs.js demos/content.md
# 指定主题 + 自动分页
node scripts/render_xhs.js demos/content.md -t terminal -m auto-split---
📤 发布到小红书
1. 配置 Cookie
cp env.example.txt .env编辑 .env:
XHS_COOKIE=your_cookie_string_here获取方式:浏览器登录小红书 → F12 → Network → 任意请求的 Cookie 头,复制整串。
2. 手动发布(可选)
python scripts/publish_xhs.py \
--title "笔记标题" \
--desc "笔记描述内容" \
--images cover.png card_1.png card_2.png可选参数:
| 参数 | 说明 |
|---|---|
--public | 公开发布(默认仅自己可见) |
--post-time "2024-01-01 12:00:00" | 定时发布 |
--api-mode | 通过 xhs-api 服务发布 |
--dry-run | 仅验证,不实际发布 |
---
📁 项目结构(重构后)
Auto-Redbook-Skills/
├── SKILL.md # 技能描述(Agent 使用说明)
├── README.md # 项目文档(你现在看到的)
├── requirements.txt # Python 依赖
├── package.json # Node.js 依赖
├── env.example.txt # Cookie 配置示例
├── references/ # 技能参考文档
│ └── params.md # 完整参数参考(主题/模式/发布参数)
├── assets/
│ ├── cover.html # 封面 HTML 模板
│ ├── card.html # 正文卡片 HTML 模板
│ ├── styles.css # 共用容器样式(cover-inner / card-inner 等)
│ └── example.md # 示例 Markdown
├── assets/themes/ # 主题样式(只控制排版 & 内层背景)
│ ├── default.css
│ ├── playful-geometric.css
│ ├── neo-brutalism.css
│ ├── botanical.css
│ ├── professional.css
│ ├── retro.css
│ ├── terminal.css
│ └── sketch.css
├── demos/ # 各主题示例渲染结果
│ ├── content.md
│ ├── content_auto_fit.md
│ ├── auto-fit/
│ ├── playful-geometric/
│ ├── retro/
│ ├── Sketch/
│ └── terminal/
└── scripts/
├── render_xhs.py # Python 渲染脚本(8 主题 + 4 分页模式)
├── render_xhs_v2.py # Python 渲染脚本 V2(7 渐变色彩风格)
├── render_xhs.js # Node.js 渲染脚本
└── publish_xhs.py # 小红书发布脚本(默认仅自己可见)---
⚠️ 注意事项
1. Cookie 安全:不要把 .env 提交到 Git 或共享出去。 2. Cookie 有效期:过期后发布失败是正常现象,重新抓一次 Cookie 即可。 3. 发布频率:避免短时间内高频发布,以免触发平台风控。 4. 图片尺寸:默认 1080×1440px,符合小红书推荐比例。
---
🙏 致谢
- Playwright - 浏览器自动化渲染
- Marked - Markdown 解析
- xhs - 小红书 API 客户端
- Cursor - 本次重构过程中提供了极大帮助 ❤️
---
📄 License
MIT License © 2026
参数参考文档
渲染脚本(render_xhs.py)
python scripts/render_xhs.py <markdown_file> [options]参数列表
| 参数 | 简写 | 说明 | 默认值 |
|---|---|---|---|
--output-dir | -o | 输出目录 | 当前工作目录 |
--theme | -t | 排版主题 | sketch |
--mode | -m | 分页模式 | separator |
--width | -w | 图片宽度(px) | 1080 |
--height | 图片高度(dynamic 下为最小高度) | 1440 | |
--max-height | dynamic 模式下的最大高度 | 4320 | |
--dpr | 设备像素比(清晰度) | 2 |
排版主题(--theme)
| 值 | 名称 | 说明 |
|---|---|---|
sketch | 手绘素描 | 手绘风格,默认 |
default | 默认简约 | 浅灰渐变背景(#f3f3f3 → #f9f9f9) |
playful-geometric | 活泼几何 | Memphis 设计风格 |
neo-brutalism | 新粗野主义 | 粗框线条、强对比 |
botanical | 植物园自然 | 自然绿植风格 |
professional | 专业商务 | 简洁商务蓝 |
retro | 复古怀旧 | 暖色复古感 |
terminal | 终端命令行 | 深色代码终端风格 |
分页模式(--mode)
| 值 | 说明 | 适用场景 |
|---|---|---|
separator | 按 --- 分隔符分页 | 内容已手动控量,需要精确分页 |
auto-fit | 固定尺寸,自动整体缩放内容 | 封面 + 单张图,尺寸固定不溢出 |
auto-split | 根据渲染后高度自动切分 | 内容长短不稳定,推荐通用选择 |
dynamic | 根据内容动态调整图片高度 | 允许不同高度卡片,字数 ≤550 |
常用命令示例
# 默认:sketch 主题 + 手动分隔分页
python scripts/render_xhs.py content.md
# 自动分页(推荐内容不稳定时使用)
python scripts/render_xhs.py content.md -m auto-split
# 固定尺寸自动缩放
python scripts/render_xhs.py content.md -m auto-fit
# 切换主题
python scripts/render_xhs.py content.md -t playful-geometric -m auto-split
# 自定义尺寸
python scripts/render_xhs.py content.md -t retro -m dynamic --width 1080 --height 1440 --dpr 2---
发布脚本(publish_xhs.py)
python scripts/publish_xhs.py --title "标题" --desc "描述" --images img1.png img2.png参数列表
| 参数 | 简写 | 说明 | 默认值 |
|---|---|---|---|
--title | -t | 笔记标题(不超过 20 字) | 必填 |
--desc | -d | 笔记描述/正文内容 | "" |
--images | -i | 图片文件路径(可多个) | 必填 |
--public | 公开发布(默认仅自己可见) | False | |
--post-time | 定时发布(格式:2024-01-01 12:00:00) | 立即发布 | |
--api-mode | 通过 xhs-api 服务发布 | 本地模式 | |
--api-url | API 服务地址 | http://localhost:5005 | |
--dry-run | 仅验证,不实际发布 | False |
注意:默认以「仅自己可见」发布,确认内容无误后再用 --public 公开。常用命令示例
# 默认(仅自己可见,用于预览确认)
python scripts/publish_xhs.py --title "标题" --desc "描述" --images cover.png card_1.png card_2.png
# 公开发布
python scripts/publish_xhs.py --title "标题" --desc "描述" --images cover.png card_1.png --public
# 定时发布
python scripts/publish_xhs.py --title "标题" --desc "描述" --images *.png --post-time "2024-12-01 10:00:00" --public
# API 模式
python scripts/publish_xhs.py --title "标题" --desc "描述" --images *.png --api-mode
# 仅验证不发布
python scripts/publish_xhs.py --title "标题" --desc "描述" --images *.png --dry-run环境变量配置(.env)
cp env.example.txt .env编辑 .env:
# 必需:小红书 Cookie
XHS_COOKIE=your_cookie_string_here
# 可选:API 模式服务地址
XHS_API_URL=http://localhost:5005Cookie 获取方式:浏览器登录小红书 → F12 → Network → 任意请求的 Cookie 头,复制完整字符串。
---
Markdown 文档格式
YAML 头部元数据
---
emoji: "🚀" # 封面装饰 Emoji
title: "大标题" # 封面大标题(不超过 15 字)
subtitle: "副标题文案" # 封面副标题(不超过 15 字)
---分页分隔符
使用 --- 手动分割卡片(配合 -m separator 使用):
---
emoji: "💡"
title: "工具推荐"
subtitle: "提升效率的 5 个神器"
---
# 神器一:Notion
> 全能笔记工具...
---
# 神器二:Raycast
快捷启动工具...# 小红书笔记创作技能依赖
# Markdown 处理
markdown>=3.4.0
PyYAML>=6.0
# 浏览器自动化(渲染图片)
playwright>=1.40.0
# 小红书发布
xhs>=0.4.0
# 环境变量管理
python-dotenv>=1.0.0
# HTTP 请求(API 模式)
requests>=2.28.0
#!/usr/bin/env python3
"""
小红书笔记发布脚本 - 增强版
支持直接发布(本地签名)和通过 API 服务发布两种方式
使用方法:
# 直接发布(使用本地签名)
python publish_xhs.py --title "标题" --desc "描述" --images cover.png card_1.png
# 通过 API 服务发布
python publish_xhs.py --title "标题" --desc "描述" --images cover.png card_1.png --api-mode
环境变量:
在同目录或项目根目录下创建 .env 文件,配置:
# 必需:小红书 Cookie
XHS_COOKIE=your_cookie_string_here
# 可选:API 服务地址(使用 --api-mode 时需要)
XHS_API_URL=http://localhost:5005
依赖安装:
pip install xhs python-dotenv requests
"""
import argparse
import os
import sys
import json
import re
from pathlib import Path
from typing import List, Optional, Dict, Any
try:
from dotenv import load_dotenv
import requests
except ImportError as e:
print(f"缺少依赖: {e}")
print("请运行: pip install python-dotenv requests")
sys.exit(1)
def load_cookie() -> str:
"""从 .env 文件加载 Cookie"""
# 尝试从多个位置加载 .env
env_paths = [
Path.cwd() / '.env',
Path(__file__).parent.parent / '.env',
Path(__file__).parent.parent.parent / '.env',
]
for env_path in env_paths:
if env_path.exists():
load_dotenv(env_path)
break
cookie = os.getenv('XHS_COOKIE')
if not cookie:
print("❌ 错误: 未找到 XHS_COOKIE 环境变量")
print("请创建 .env 文件,添加以下内容:")
print("XHS_COOKIE=your_cookie_string_here")
print("\nCookie 获取方式:")
print("1. 在浏览器中登录小红书(https://www.xiaohongshu.com)")
print("2. 打开开发者工具(F12)")
print("3. 在 Network 标签中查看任意请求的 Cookie 头")
print("4. 复制完整的 cookie 字符串")
sys.exit(1)
return cookie
def parse_cookie(cookie_string: str) -> Dict[str, str]:
"""解析 Cookie 字符串为字典"""
cookies = {}
for item in cookie_string.split(';'):
item = item.strip()
if '=' in item:
key, value = item.split('=', 1)
cookies[key.strip()] = value.strip()
return cookies
def validate_cookie(cookie_string: str) -> bool:
"""验证 Cookie 是否包含必要的字段"""
cookies = parse_cookie(cookie_string)
# 检查必需的 cookie 字段
required_fields = ['a1', 'web_session']
missing = [f for f in required_fields if f not in cookies]
if missing:
print(f"⚠️ Cookie 可能不完整,缺少字段: {', '.join(missing)}")
print("这可能导致签名失败,请确保 Cookie 包含 a1 和 web_session 字段")
return False
return True
def get_api_url() -> str:
"""获取 API 服务地址"""
return os.getenv('XHS_API_URL', 'http://localhost:5005')
def validate_images(image_paths: List[str]) -> List[str]:
"""验证图片文件是否存在"""
valid_images = []
for path in image_paths:
if os.path.exists(path):
valid_images.append(os.path.abspath(path))
else:
print(f"⚠️ 警告: 图片不存在 - {path}")
if not valid_images:
print("❌ 错误: 没有有效的图片文件")
sys.exit(1)
return valid_images
class LocalPublisher:
"""本地发布模式:直接使用 xhs 库"""
def __init__(self, cookie: str):
self.cookie = cookie
self.client = None
def init_client(self):
"""初始化 xhs 客户端"""
try:
from xhs import XhsClient
from xhs.help import sign as local_sign
except ImportError:
print("❌ 错误: 缺少 xhs 库")
print("请运行: pip install xhs")
sys.exit(1)
# 解析 a1 值
cookies = parse_cookie(self.cookie)
a1 = cookies.get('a1', '')
def sign_func(uri, data=None, a1_param="", web_session=""):
# 使用 cookie 中的 a1 值
return local_sign(uri, data, a1=a1 or a1_param)
self.client = XhsClient(cookie=self.cookie, sign=sign_func)
def get_user_info(self) -> Optional[Dict[str, Any]]:
"""获取当前登录用户信息"""
try:
info = self.client.get_self_info()
print(f"👤 当前用户: {info.get('nickname', '未知')}")
return info
except Exception as e:
print(f"⚠️ 无法获取用户信息: {e}")
return None
def publish(self, title: str, desc: str, images: List[str],
is_private: bool = True, post_time: str = None) -> Dict[str, Any]:
"""发布图文笔记"""
print(f"\n🚀 准备发布笔记(本地模式)...")
print(f" 📌 标题: {title}")
print(f" 📝 描述: {desc[:50]}..." if len(desc) > 50 else f" 📝 描述: {desc}")
print(f" 🖼️ 图片数量: {len(images)}")
try:
result = self.client.create_image_note(
title=title,
desc=desc,
files=images,
is_private=is_private,
post_time=post_time
)
print("\n✨ 笔记发布成功!")
if isinstance(result, dict):
note_id = result.get('note_id') or result.get('id')
if note_id:
print(f" 📎 笔记ID: {note_id}")
print(f" 🔗 链接: https://www.xiaohongshu.com/explore/{note_id}")
return result
except Exception as e:
error_msg = str(e)
print(f"\n❌ 发布失败: {error_msg}")
# 提供具体的错误排查建议
if 'sign' in error_msg.lower() or 'signature' in error_msg.lower():
print("\n💡 签名错误排查建议:")
print("1. 确保 Cookie 包含有效的 a1 和 web_session 字段")
print("2. Cookie 可能已过期,请重新获取")
print("3. 尝试使用 --api-mode 通过 API 服务发布")
elif 'cookie' in error_msg.lower():
print("\n💡 Cookie 错误排查建议:")
print("1. 确保 Cookie 格式正确")
print("2. Cookie 可能已过期,请重新获取")
print("3. 确保 Cookie 来自已登录的小红书网页版")
raise
class ApiPublisher:
"""API 发布模式:通过 xhs-api 服务发布"""
def __init__(self, cookie: str, api_url: str = None):
self.cookie = cookie
self.api_url = api_url or get_api_url()
self.session_id = 'md2redbook_session'
def init_client(self):
"""初始化 API 客户端"""
print(f"📡 连接 API 服务: {self.api_url}")
# 健康检查
try:
resp = requests.get(f"{self.api_url}/health", timeout=5)
if resp.status_code != 200:
raise Exception("API 服务不可用")
except requests.exceptions.RequestException as e:
print(f"❌ 无法连接到 API 服务: {e}")
print(f"\n💡 请确保 xhs-api 服务已启动:")
print(f" cd xhs-api && python app_full.py")
sys.exit(1)
# 初始化 session
try:
resp = requests.post(
f"{self.api_url}/init",
json={
"session_id": self.session_id,
"cookie": self.cookie
},
timeout=30
)
result = resp.json()
if resp.status_code == 200 and result.get('status') == 'success':
print(f"✅ API 初始化成功")
user_info = result.get('user_info', {})
if user_info:
print(f"👤 当前用户: {user_info.get('nickname', '未知')}")
elif result.get('status') == 'warning':
print(f"⚠️ {result.get('message')}")
else:
raise Exception(result.get('error', '初始化失败'))
except Exception as e:
print(f"❌ API 初始化失败: {e}")
sys.exit(1)
def get_user_info(self) -> Optional[Dict[str, Any]]:
"""获取当前登录用户信息"""
try:
resp = requests.post(
f"{self.api_url}/user/info",
json={"session_id": self.session_id},
timeout=10
)
if resp.status_code == 200:
result = resp.json()
if result.get('status') == 'success':
info = result.get('user_info', {})
print(f"👤 当前用户: {info.get('nickname', '未知')}")
return info
return None
except Exception as e:
print(f"⚠️ 无法获取用户信息: {e}")
return None
def publish(self, title: str, desc: str, images: List[str],
is_private: bool = True, post_time: str = None) -> Dict[str, Any]:
"""发布图文笔记"""
print(f"\n🚀 准备发布笔记(API 模式)...")
print(f" 📌 标题: {title}")
print(f" 📝 描述: {desc[:50]}..." if len(desc) > 50 else f" 📝 描述: {desc}")
print(f" 🖼️ 图片数量: {len(images)}")
try:
payload = {
"session_id": self.session_id,
"title": title,
"desc": desc,
"files": images,
"is_private": is_private
}
if post_time:
payload["post_time"] = post_time
resp = requests.post(
f"{self.api_url}/publish/image",
json=payload,
timeout=120
)
result = resp.json()
if resp.status_code == 200 and result.get('status') == 'success':
print("\n✨ 笔记发布成功!")
publish_result = result.get('result', {})
if isinstance(publish_result, dict):
note_id = publish_result.get('note_id') or publish_result.get('id')
if note_id:
print(f" 📎 笔记ID: {note_id}")
print(f" 🔗 链接: https://www.xiaohongshu.com/explore/{note_id}")
return publish_result
else:
raise Exception(result.get('error', '发布失败'))
except Exception as e:
error_msg = str(e)
print(f"\n❌ 发布失败: {error_msg}")
raise
def main():
parser = argparse.ArgumentParser(
description='将图片发布为小红书笔记',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog='''
示例:
# 基本用法(默认仅自己可见)
python publish_xhs.py -t "我的标题" -d "正文内容" -i cover.png card_1.png card_2.png
# 公开发布
python publish_xhs.py -t "我的标题" -d "正文内容" -i *.png --public
# 使用 API 模式
python publish_xhs.py -t "我的标题" -d "正文内容" -i *.png --api-mode
# 定时发布
python publish_xhs.py -t "我的标题" -d "正文内容" -i *.png --post-time "2024-12-01 10:00:00"
'''
)
parser.add_argument(
'--title', '-t',
required=True,
help='笔记标题(不超过20字)'
)
parser.add_argument(
'--desc', '-d',
default='',
help='笔记描述/正文内容'
)
parser.add_argument(
'--images', '-i',
nargs='+',
required=True,
help='图片文件路径(可以多个)'
)
parser.add_argument(
'--public',
action='store_true',
help='公开发布(默认为仅自己可见)'
)
parser.add_argument(
'--post-time',
default=None,
help='定时发布时间(格式:2024-01-01 12:00:00)'
)
parser.add_argument(
'--api-mode',
action='store_true',
help='使用 API 模式发布(需要 xhs-api 服务运行)'
)
parser.add_argument(
'--api-url',
default=None,
help='API 服务地址(默认: http://localhost:5005)'
)
parser.add_argument(
'--dry-run',
action='store_true',
help='仅验证,不实际发布'
)
args = parser.parse_args()
# 验证标题长度
if len(args.title) > 20:
print(f"⚠️ 警告: 标题超过20字,将被截断")
args.title = args.title[:20]
# 加载 Cookie
cookie = load_cookie()
# 验证 Cookie 格式
validate_cookie(cookie)
# 验证图片
valid_images = validate_images(args.images)
if args.dry_run:
print("\n🔍 验证模式 - 不会实际发布")
print(f" 📌 标题: {args.title}")
print(f" 📝 描述: {args.desc}")
print(f" 🖼️ 图片: {valid_images}")
print(f" 🔒 私密: {not args.public}")
print(f" ⏰ 定时: {args.post_time or '立即发布'}")
print(f" 📡 模式: {'API' if args.api_mode else '本地'}")
print("\n✅ 验证通过,可以发布")
return
# 选择发布方式
if args.api_mode:
publisher = ApiPublisher(cookie, args.api_url)
else:
publisher = LocalPublisher(cookie)
# 初始化客户端
publisher.init_client()
# 发布笔记
try:
publisher.publish(
title=args.title,
desc=args.desc,
images=valid_images,
is_private=not args.public,
post_time=args.post_time
)
except Exception as e:
sys.exit(1)
if __name__ == '__main__':
main()
#!/usr/bin/env node
/**
* 小红书卡片渲染脚本 V2 - Node.js 智能分页版
* 将 Markdown 文件渲染为小红书风格的图片卡片
*
* 新特性:
* 1. 智能分页:自动检测内容高度,超出时自动拆分到多张卡片
* 2. 多种样式:支持多种预设样式主题
*
* 使用方法:
* node render_xhs_v2.js <markdown_file> [options]
*
* 依赖安装:
* npm install marked js-yaml playwright
* npx playwright install chromium
*/
const fs = require('fs');
const path = require('path');
const { chromium } = require('playwright');
const { marked } = require('marked');
const yaml = require('js-yaml');
// 获取脚本所在目录
const SCRIPT_DIR = path.dirname(__dirname);
const ASSETS_DIR = path.join(SCRIPT_DIR, 'assets');
// 卡片尺寸配置 (3:4 比例)
const CARD_WIDTH = 1080;
const CARD_HEIGHT = 1440;
// 内容区域安全高度
const SAFE_HEIGHT = CARD_HEIGHT - 120 - 100 - 80 - 40; // ~1100px
// 样式配置
const STYLES = {
purple: {
name: "紫韵",
cover_bg: "linear-gradient(180deg, #3450E4 0%, #D266DA 100%)",
card_bg: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
accent_color: "#6366f1",
},
xiaohongshu: {
name: "小红书红",
cover_bg: "linear-gradient(180deg, #FF2442 0%, #FF6B81 100%)",
card_bg: "linear-gradient(135deg, #FF2442 0%, #FF6B81 100%)",
accent_color: "#FF2442",
},
mint: {
name: "清新薄荷",
cover_bg: "linear-gradient(180deg, #43e97b 0%, #38f9d7 100%)",
card_bg: "linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)",
accent_color: "#43e97b",
},
sunset: {
name: "日落橙",
cover_bg: "linear-gradient(180deg, #fa709a 0%, #fee140 100%)",
card_bg: "linear-gradient(135deg, #fa709a 0%, #fee140 100%)",
accent_color: "#fa709a",
},
ocean: {
name: "深海蓝",
cover_bg: "linear-gradient(180deg, #4facfe 0%, #00f2fe 100%)",
card_bg: "linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)",
accent_color: "#4facfe",
},
elegant: {
name: "优雅白",
cover_bg: "linear-gradient(180deg, #f5f5f5 0%, #e0e0e0 100%)",
card_bg: "linear-gradient(135deg, #f5f5f5 0%, #e8e8e8 100%)",
accent_color: "#333333",
},
dark: {
name: "暗黑模式",
cover_bg: "linear-gradient(180deg, #1a1a2e 0%, #16213e 100%)",
card_bg: "linear-gradient(135deg, #1a1a2e 0%, #16213e 100%)",
accent_color: "#e94560",
},
};
/**
* 解析 Markdown 文件,提取 YAML 头部和正文内容
*/
function parseMarkdownFile(filePath) {
const content = fs.readFileSync(filePath, 'utf-8');
const yamlPattern = /^---\s*\n([\s\S]*?)\n---\s*\n/;
const yamlMatch = content.match(yamlPattern);
let metadata = {};
let body = content;
if (yamlMatch) {
try {
metadata = yaml.load(yamlMatch[1]) || {};
} catch (e) {
metadata = {};
}
body = content.slice(yamlMatch[0].length);
}
return { metadata, body: body.trim() };
}
/**
* 按照 --- 分隔符拆分正文为多张卡片内容
*/
function splitContentBySeparator(body) {
const parts = body.split(/\n---+\n/);
return parts.filter(part => part.trim()).map(part => part.trim());
}
/**
* 预估内容高度
*/
function estimateContentHeight(content) {
const lines = content.split('\n');
let totalHeight = 0;
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed) {
totalHeight += 20;
continue;
}
if (trimmed.startsWith('# ')) {
totalHeight += 130;
} else if (trimmed.startsWith('## ')) {
totalHeight += 110;
} else if (trimmed.startsWith('### ')) {
totalHeight += 90;
} else if (trimmed.startsWith('```')) {
totalHeight += 80;
} else if (trimmed.match(/^[-*+]\s/)) {
totalHeight += 85;
} else if (trimmed.startsWith('>')) {
totalHeight += 100;
} else if (trimmed.startsWith('![')) {
totalHeight += 300;
} else {
const charCount = trimmed.length;
const linesNeeded = Math.max(1, charCount / 28);
totalHeight += Math.floor(linesNeeded * 42 * 1.7) + 35;
}
}
return totalHeight;
}
/**
* 智能拆分内容
*/
function smartSplitContent(content, maxHeight = SAFE_HEIGHT) {
const blocks = [];
let currentBlock = [];
const lines = content.split('\n');
for (const line of lines) {
if (line.trim().startsWith('#') && currentBlock.length > 0) {
blocks.push(currentBlock.join('\n'));
currentBlock = [line];
} else if (line.trim() === '---') {
if (currentBlock.length > 0) {
blocks.push(currentBlock.join('\n'));
currentBlock = [];
}
} else {
currentBlock.push(line);
}
}
if (currentBlock.length > 0) {
blocks.push(currentBlock.join('\n'));
}
if (blocks.length <= 1) {
const paragraphs = content.split('\n\n').filter(b => b.trim());
blocks.length = 0;
blocks.push(...paragraphs);
}
const cards = [];
let currentCard = [];
let currentHeight = 0;
for (const block of blocks) {
const blockHeight = estimateContentHeight(block);
if (blockHeight > maxHeight) {
if (currentCard.length > 0) {
cards.push(currentCard.join('\n\n'));
currentCard = [];
currentHeight = 0;
}
const blockLines = block.split('\n');
let subBlock = [];
let subHeight = 0;
for (const line of blockLines) {
const lineHeight = estimateContentHeight(line);
if (subHeight + lineHeight > maxHeight && subBlock.length > 0) {
cards.push(subBlock.join('\n'));
subBlock = [line];
subHeight = lineHeight;
} else {
subBlock.push(line);
subHeight += lineHeight;
}
}
if (subBlock.length > 0) {
cards.push(subBlock.join('\n'));
}
} else if (currentHeight + blockHeight > maxHeight && currentCard.length > 0) {
cards.push(currentCard.join('\n\n'));
currentCard = [block];
currentHeight = blockHeight;
} else {
currentCard.push(block);
currentHeight += blockHeight;
}
}
if (currentCard.length > 0) {
cards.push(currentCard.join('\n\n'));
}
return cards.length > 0 ? cards : [content];
}
/**
* 将 Markdown 转换为 HTML
*/
function convertMarkdownToHtml(mdContent, style = STYLES.purple) {
const tagsPattern = /((?:#[\w\u4e00-\u9fa5]+\s*)+)$/m;
const tagsMatch = mdContent.match(tagsPattern);
let tagsHtml = "";
if (tagsMatch) {
const tagsStr = tagsMatch[1];
mdContent = mdContent.slice(0, tagsMatch.index).trim();
const tags = tagsStr.match(/#([\w\u4e00-\u9fa5]+)/g);
if (tags) {
const accent = style.accent_color;
tagsHtml = '<div class="tags-container">';
for (const tag of tags) {
tagsHtml += `<span class="tag" style="background: ${accent};">${tag}</span>`;
}
tagsHtml += '</div>';
}
}
const html = marked.parse(mdContent, { breaks: true, gfm: true });
return html + tagsHtml;
}
/**
* 生成封面 HTML
*/
function generateCoverHtml(metadata, styleKey = 'purple') {
const style = STYLES[styleKey] || STYLES.purple;
const emoji = metadata.emoji || '📝';
let title = metadata.title || '标题';
let subtitle = metadata.subtitle || '';
if (title.length > 15) title = title.slice(0, 15);
if (subtitle.length > 15) subtitle = subtitle.slice(0, 15);
const isDark = styleKey === 'dark';
const textColor = isDark ? '#ffffff' : '#000000';
const titleGradient = isDark
? 'linear-gradient(180deg, #ffffff 0%, #cccccc 100%)'
: 'linear-gradient(180deg, #2E67B1 0%, #4C4C4C 100%)';
const innerBg = isDark ? '#1a1a2e' : '#F3F3F3';
return `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1080, height=1440">
<title>小红书封面</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700;900&display=swap');
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Noto Sans SC', 'Source Han Sans CN', 'PingFang SC', 'Microsoft YaHei', sans-serif;
width: 1080px; height: 1440px; overflow: hidden;
}
.cover-container {
width: 1080px; height: 1440px;
background: ${style.cover_bg};
position: relative; overflow: hidden;
}
.cover-inner {
position: absolute; width: 950px; height: 1310px;
left: 65px; top: 65px;
background: ${innerBg};
border-radius: 25px;
display: flex; flex-direction: column;
padding: 80px 85px;
}
.cover-emoji { font-size: 180px; line-height: 1.2; margin-bottom: 50px; }
.cover-title {
font-weight: 900; font-size: 130px; line-height: 1.4;
background: ${titleGradient};
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
flex: 1;
display: flex; align-items: flex-start;
word-break: break-all;
}
.cover-subtitle {
font-weight: 350; font-size: 72px; line-height: 1.4;
color: ${textColor};
margin-top: auto;
}
</style>
</head>
<body>
<div class="cover-container">
<div class="cover-inner">
<div class="cover-emoji">${emoji}</div>
<div class="cover-title">${title}</div>
<div class="cover-subtitle">${subtitle}</div>
</div>
</div>
</body>
</html>`;
}
/**
* 生成正文卡片 HTML
*/
function generateCardHtml(content, pageNumber = 1, totalPages = 1, styleKey = 'purple') {
const style = STYLES[styleKey] || STYLES.purple;
const htmlContent = convertMarkdownToHtml(content, style);
const pageText = totalPages > 1 ? `${pageNumber}/${totalPages}` : '';
const isDark = styleKey === 'dark';
const cardBg = isDark ? 'rgba(30, 30, 46, 0.95)' : 'rgba(255, 255, 255, 0.95)';
const textColor = isDark ? '#e0e0e0' : '#475569';
const headingColor = isDark ? '#ffffff' : '#1e293b';
const h2Color = isDark ? '#e0e0e0' : '#334155';
const h3Color = isDark ? '#c0c0c0' : '#475569';
const codeBg = isDark ? '#252540' : '#f1f5f9';
const preBg = isDark ? '#0f0f23' : '#1e293b';
const blockquoteBg = isDark ? '#252540' : '#f1f5f9';
const blockquoteColor = isDark ? '#a0a0a0' : '#64748b';
const hrBg = isDark ? '#333355' : '#e2e8f0';
return `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1080">
<title>小红书卡片</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700;900&display=swap');
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Noto Sans SC', 'Source Han Sans CN', 'PingFang SC', 'Microsoft YaHei', sans-serif;
width: 1080px; min-height: 1440px; overflow: hidden; background: transparent;
}
.card-container {
width: 1080px; min-height: 1440px;
background: ${style.card_bg};
position: relative; padding: 50px; overflow: hidden;
}
.card-inner {
background: ${cardBg};
border-radius: 20px;
padding: 60px;
min-height: calc(1440px - 100px);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px);
}
.card-content {
color: ${textColor};
font-size: 42px;
line-height: 1.7;
}
.card-content h1 {
font-size: 72px; font-weight: 700; color: ${headingColor};
margin-bottom: 40px; line-height: 1.3;
}
.card-content h2 {
font-size: 56px; font-weight: 600; color: ${h2Color};
margin: 50px 0 25px 0; line-height: 1.4;
}
.card-content h3 {
font-size: 48px; font-weight: 600; color: ${h3Color};
margin: 40px 0 20px 0;
}
.card-content p { margin-bottom: 35px; }
.card-content strong { font-weight: 700; color: ${headingColor}; }
.card-content em { font-style: italic; color: ${style.accent_color}; }
.card-content a {
color: ${style.accent_color}; text-decoration: none;
border-bottom: 2px solid ${style.accent_color};
}
.card-content ul, .card-content ol {
margin: 30px 0; padding-left: 60px;
}
.card-content li { margin-bottom: 20px; line-height: 1.6; }
.card-content blockquote {
border-left: 8px solid ${style.accent_color};
padding-left: 40px;
background: ${blockquoteBg};
padding-top: 25px; padding-bottom: 25px; padding-right: 30px;
margin: 35px 0;
color: ${blockquoteColor};
font-style: italic;
border-radius: 0 12px 12px 0;
}
.card-content blockquote p { margin: 0; }
.card-content code {
background: ${codeBg};
padding: 6px 16px; border-radius: 8px;
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
font-size: 38px;
color: ${style.accent_color};
}
.card-content pre {
background: ${preBg};
color: ${isDark ? '#e0e0e0' : '#e2e8f0'};
padding: 40px; border-radius: 16px;
margin: 35px 0;
overflow-x: visible;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-all;
white-space: pre-wrap;
font-size: 36px; line-height: 1.5;
}
.card-content pre code {
background: transparent; color: inherit; padding: 0; font-size: inherit;
}
.card-content img {
max-width: 100%; height: auto; border-radius: 16px;
margin: 35px auto; display: block;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
}
.card-content hr {
border: none; height: 2px;
background: ${hrBg};
margin: 50px 0;
}
.tags-container {
margin-top: 50px; padding-top: 30px;
border-top: 2px solid ${hrBg};
}
.tag {
display: inline-block;
background: ${style.accent_color};
color: white;
padding: 12px 28px; border-radius: 30px;
font-size: 34px;
margin: 10px 15px 10px 0;
font-weight: 500;
}
.page-number {
position: absolute;
bottom: 80px; right: 80px;
font-size: 36px;
color: rgba(255, 255, 255, 0.8);
font-weight: 500;
}
</style>
</head>
<body>
<div class="card-container">
<div class="card-inner">
<div class="card-content">
${htmlContent}
</div>
</div>
<div class="page-number">${pageText}</div>
</div>
</body>
</html>`;
}
/**
* 测量内容高度
*/
async function measureContentHeight(page, htmlContent) {
await page.setContent(htmlContent, { waitUntil: 'networkidle' });
await page.waitForTimeout(300);
return await page.evaluate(() => {
const inner = document.querySelector('.card-inner');
if (inner) return inner.scrollHeight;
const container = document.querySelector('.card-container');
return container ? container.scrollHeight : document.body.scrollHeight;
});
}
/**
* 处理和渲染卡片
*/
async function processAndRenderCards(cardContents, outputDir, styleKey) {
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: CARD_WIDTH, height: CARD_HEIGHT } });
const allCards = [];
try {
for (const content of cardContents) {
const estimatedHeight = estimateContentHeight(content);
let splitContents;
if (estimatedHeight > SAFE_HEIGHT) {
splitContents = smartSplitContent(content, SAFE_HEIGHT);
} else {
splitContents = [content];
}
for (const splitContent of splitContents) {
const tempHtml = generateCardHtml(splitContent, 1, 1, styleKey);
const actualHeight = await measureContentHeight(page, tempHtml);
if (actualHeight > CARD_HEIGHT - 100) {
const lines = splitContent.split('\n');
const subContents = [];
let subLines = [];
for (const line of lines) {
const testLines = [...subLines, line];
const testHtml = generateCardHtml(testLines.join('\n'), 1, 1, styleKey);
const testHeight = await measureContentHeight(page, testHtml);
if (testHeight > CARD_HEIGHT - 100 && subLines.length > 0) {
subContents.push(subLines.join('\n'));
subLines = [line];
} else {
subLines = testLines;
}
}
if (subLines.length > 0) {
subContents.push(subLines.join('\n'));
}
allCards.push(...subContents);
} else {
allCards.push(splitContent);
}
}
}
} finally {
await browser.close();
}
return allCards;
}
/**
* 渲染 HTML 到图片
*/
async function renderHtmlToImage(page, htmlContent, outputPath) {
await page.setContent(htmlContent, { waitUntil: 'networkidle' });
await page.waitForTimeout(300);
await page.screenshot({
path: outputPath,
clip: { x: 0, y: 0, width: CARD_WIDTH, height: CARD_HEIGHT },
type: 'png'
});
console.log(` ✅ 已生成: ${outputPath}`);
}
/**
* 主渲染函数
*/
async function renderMarkdownToCards(mdFile, outputDir, styleKey = 'purple') {
console.log(`\n🎨 开始渲染: ${mdFile}`);
console.log(`🎨 使用样式: ${STYLES[styleKey].name}`);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
const data = parseMarkdownFile(mdFile);
const { metadata, body } = data;
const cardContents = splitContentBySeparator(body);
console.log(` 📄 检测到 ${cardContents.length} 个内容块`);
console.log(' 🔍 分析内容高度并智能分页...');
const processedCards = await processAndRenderCards(cardContents, outputDir, styleKey);
const totalCards = processedCards.length;
console.log(` 📄 将生成 ${totalCards} 张卡片`);
if (metadata.emoji || metadata.title) {
console.log(' 📷 生成封面...');
const coverHtml = generateCoverHtml(metadata, styleKey);
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: CARD_WIDTH, height: CARD_HEIGHT } });
try {
await renderHtmlToImage(page, coverHtml, path.join(outputDir, 'cover.png'));
} finally {
await browser.close();
}
}
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: CARD_WIDTH, height: CARD_HEIGHT } });
try {
for (let i = 0; i < processedCards.length; i++) {
const pageNum = i + 1;
console.log(` 📷 生成卡片 ${pageNum}/${totalCards}...`);
const cardHtml = generateCardHtml(processedCards[i], pageNum, totalCards, styleKey);
const cardPath = path.join(outputDir, `card_${pageNum}.png`);
await renderHtmlToImage(page, cardHtml, cardPath);
}
} finally {
await browser.close();
}
console.log(`\n✨ 渲染完成!共生成 ${totalCards} 张卡片,保存到: ${outputDir}`);
return totalCards;
}
/**
* 列出所有样式
*/
function listStyles() {
console.log('\n📋 可用样式列表:');
console.log('-'.repeat(40));
for (const [key, style] of Object.entries(STYLES)) {
console.log(` ${key.padEnd(12)} - ${style.name}`);
}
console.log('-'.repeat(40));
}
/**
* 解析命令行参数
*/
function parseArgs() {
const args = process.argv.slice(2);
if (args.length === 0 || args.includes('--help')) {
console.log(`
使用方法: node render_xhs_v2.js <markdown_file> [options]
选项:
-o, --output-dir <dir> 输出目录(默认为当前工作目录)
-s, --style <style> 样式主题(默认: purple)
--list-styles 列出所有可用样式
--help 显示帮助信息
可用样式:
purple, xiaohongshu, mint, sunset, ocean, elegant, dark
示例:
node render_xhs_v2.js note.md
node render_xhs_v2.js note.md -o ./output --style xiaohongshu
`);
process.exit(0);
}
if (args.includes('--list-styles')) {
listStyles();
process.exit(0);
}
let markdownFile = null;
let outputDir = process.cwd();
let style = 'purple';
for (let i = 0; i < args.length; i++) {
if (args[i] === '--output-dir' || args[i] === '-o') {
outputDir = args[i + 1];
i++;
} else if (args[i] === '--style' || args[i] === '-s') {
if (STYLES[args[i + 1]]) {
style = args[i + 1];
} else {
console.error(`❌ 无效样式: ${args[i + 1]}`);
console.log('可用样式:', Object.keys(STYLES).join(', '));
process.exit(1);
}
i++;
} else if (!args[i].startsWith('-')) {
markdownFile = args[i];
}
}
if (!markdownFile) {
console.error('❌ 错误: 请指定 Markdown 文件');
process.exit(1);
}
if (!fs.existsSync(markdownFile)) {
console.error(`❌ 错误: 文件不存在 - ${markdownFile}`);
process.exit(1);
}
return { markdownFile, outputDir, style };
}
// 主函数
async function main() {
const { markdownFile, outputDir, style } = parseArgs();
await renderMarkdownToCards(markdownFile, outputDir, style);
}
main().catch(error => {
console.error('❌ 渲染失败:', error.message);
process.exit(1);
});
#!/usr/bin/env node
/**
* 小红书卡片渲染脚本 - Node.js 增强版
* 支持多种排版样式和智能分页策略
*
* 使用方法:
* node render_xhs.js <markdown_file> [options]
*
* 选项:
* --output-dir, -o 输出目录(默认为当前工作目录)
* --theme, -t 排版主题:default, playful-geometric, neo-brutalism, 等
* --mode, -m 分页模式:separator, auto-fit, auto-split, dynamic
* --width, -w 图片宽度(默认 1080)
* --height, -h 图片高度(默认 1440)
* --dpr 设备像素比(默认 2)
*
* 依赖安装:
* npm install marked yaml playwright
* npx playwright install chromium
*/
const fs = require('fs');
const path = require('path');
const { marked } = require('marked');
const yaml = require('yaml');
const { chromium } = require('playwright');
// 获取脚本所在目录
const SCRIPT_DIR = path.dirname(__dirname);
const ASSETS_DIR = path.join(SCRIPT_DIR, 'assets');
const THEMES_DIR = path.join(ASSETS_DIR, 'themes');
// 默认卡片尺寸配置 (3:4 比例)
const DEFAULT_WIDTH = 1080;
const DEFAULT_HEIGHT = 1440;
const MAX_HEIGHT = 2160;
// 可用主题列表
const AVAILABLE_THEMES = [
'default',
'playful-geometric',
'neo-brutalism',
'botanical',
'professional',
'retro',
'terminal',
'sketch'
];
// 分页模式
const PAGING_MODES = ['separator', 'auto-fit', 'auto-split', 'dynamic'];
// 主题背景色
const THEME_BACKGROUNDS = {
'default': 'linear-gradient(180deg, #f3f3f3 0%, #f9f9f9 100%)',
'playful-geometric': 'linear-gradient(135deg, #8B5CF6 0%, #F472B6 100%)',
'neo-brutalism': 'linear-gradient(135deg, #FF4757 0%, #FECA57 100%)',
'botanical': 'linear-gradient(135deg, #4A7C59 0%, #8FBC8F 100%)',
'professional': 'linear-gradient(135deg, #2563EB 0%, #3B82F6 100%)',
'retro': 'linear-gradient(135deg, #D35400 0%, #F39C12 100%)',
'terminal': 'linear-gradient(135deg, #0D1117 0%, #161B22 100%)',
'sketch': 'linear-gradient(135deg, #555555 0%, #888888 100%)'
};
// 封面标题文字渐变(随主题变化)
const THEME_TITLE_GRADIENTS = {
'default': 'linear-gradient(180deg, #111827 0%, #4B5563 100%)',
'playful-geometric': 'linear-gradient(180deg, #7C3AED 0%, #F472B6 100%)',
'neo-brutalism': 'linear-gradient(180deg, #000000 0%, #FF4757 100%)',
'botanical': 'linear-gradient(180deg, #1F2937 0%, #4A7C59 100%)',
'professional': 'linear-gradient(180deg, #1E3A8A 0%, #2563EB 100%)',
'retro': 'linear-gradient(180deg, #8B4513 0%, #D35400 100%)',
'terminal': 'linear-gradient(180deg, #39D353 0%, #58A6FF 100%)',
'sketch': 'linear-gradient(180deg, #111827 0%, #6B7280 100%)',
};
/**
* 解析命令行参数
*/
function parseArgs() {
const args = process.argv.slice(2);
const options = {
markdownFile: null,
outputDir: process.cwd(),
theme: 'default',
mode: 'separator',
width: DEFAULT_WIDTH,
height: DEFAULT_HEIGHT,
maxHeight: MAX_HEIGHT,
dpr: 2
};
for (let i = 0; i < args.length; i++) {
const arg = args[i];
const nextArg = args[i + 1];
switch (arg) {
case '--output-dir':
case '-o':
options.outputDir = nextArg;
i++;
break;
case '--theme':
case '-t':
options.theme = nextArg;
i++;
break;
case '--mode':
case '-m':
options.mode = nextArg;
i++;
break;
case '--width':
case '-w':
options.width = parseInt(nextArg);
i++;
break;
case '--height':
options.height = parseInt(nextArg);
i++;
break;
case '--max-height':
options.maxHeight = parseInt(nextArg);
i++;
break;
case '--dpr':
options.dpr = parseInt(nextArg);
i++;
break;
case '--help':
printHelp();
process.exit(0);
default:
if (!arg.startsWith('-')) {
options.markdownFile = arg;
}
}
}
return options;
}
/**
* 打印帮助信息
*/
function printHelp() {
console.log(`
小红书卡片渲染脚本 - Node.js 版本
使用方法:
node render_xhs.js <markdown_file> [options]
选项:
--output-dir, -o 输出目录(默认为当前工作目录)
--theme, -t 排版主题
--mode, -m 分页模式
--width, -w 图片宽度(默认 1080)
--height 图片高度(默认 1440)
--max-height 最大高度(默认 2160)
--dpr 设备像素比(默认 2)
可用主题: ${AVAILABLE_THEMES.join(', ')}
分页模式: ${PAGING_MODES.join(', ')}
`);
}
/**
* 解析 Markdown 文件
*/
function parseMarkdownFile(filePath) {
const content = fs.readFileSync(filePath, 'utf-8');
// 解析 YAML 头部
const yamlMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n/);
let metadata = {};
let body = content;
if (yamlMatch) {
try {
metadata = yaml.parse(yamlMatch[1]) || {};
} catch (e) {
metadata = {};
}
body = content.slice(yamlMatch[0].length);
}
return { metadata, body: body.trim() };
}
/**
* 按分隔符拆分内容
*/
function splitContentBySeparator(body) {
const parts = body.split(/\n---+\n/);
return parts.map(p => p.trim()).filter(p => p);
}
/**
* 加载主题 CSS
*/
function loadThemeCss(theme) {
const themeFile = path.join(THEMES_DIR, `${theme}.css`);
if (fs.existsSync(themeFile)) {
return fs.readFileSync(themeFile, 'utf-8');
}
const defaultFile = path.join(THEMES_DIR, 'default.css');
if (fs.existsSync(defaultFile)) {
return fs.readFileSync(defaultFile, 'utf-8');
}
return '';
}
/**
* 生成封面 HTML
*/
function generateCoverHtml(metadata, theme, width, height) {
const emoji = metadata.emoji || '📝';
let title = metadata.title || '标题';
let subtitle = metadata.subtitle || '';
if (title.length > 15) title = title.slice(0, 15);
if (subtitle.length > 15) subtitle = subtitle.slice(0, 15);
const bg = THEME_BACKGROUNDS[theme] || THEME_BACKGROUNDS['default'];
const titleBg = THEME_TITLE_GRADIENTS[theme] || THEME_TITLE_GRADIENTS['default'];
return `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=${width}, height=${height}">
<title>小红书封面</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700;900&display=swap');
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Noto Sans SC', 'Source Han Sans CN', 'PingFang SC', 'Microsoft YaHei', sans-serif;
width: ${width}px;
height: ${height}px;
overflow: hidden;
}
.cover-container {
width: ${width}px;
height: ${height}px;
background: ${bg};
position: relative;
overflow: hidden;
}
.cover-inner {
position: absolute;
width: ${Math.floor(width * 0.88)}px;
height: ${Math.floor(height * 0.91)}px;
left: ${Math.floor(width * 0.06)}px;
top: ${Math.floor(height * 0.045)}px;
background: #F3F3F3;
border-radius: 25px;
display: flex;
flex-direction: column;
padding: ${Math.floor(width * 0.074)}px ${Math.floor(width * 0.079)}px;
}
.cover-emoji {
font-size: ${Math.floor(width * 0.167)}px;
line-height: 1.2;
margin-bottom: ${Math.floor(height * 0.035)}px;
}
.cover-title {
font-weight: 900;
font-size: ${Math.floor(width * 0.12)}px;
line-height: 1.4;
background: ${titleBg};
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
flex: 1;
display: flex;
align-items: flex-start;
word-break: break-all;
}
.cover-subtitle {
font-weight: 350;
font-size: ${Math.floor(width * 0.067)}px;
line-height: 1.4;
color: #000000;
margin-top: auto;
}
</style>
</head>
<body>
<div class="cover-container">
<div class="cover-inner">
<div class="cover-emoji">${emoji}</div>
<div class="cover-title">${title}</div>
<div class="cover-subtitle">${subtitle}</div>
</div>
</div>
</body>
</html>`;
}
/**
* 生成正文卡片 HTML
*/
function generateCardHtml(content, theme, pageNumber, totalPages, width, height, mode) {
const htmlContent = marked.parse(content);
const themeCss = loadThemeCss(theme);
const pageText = totalPages > 1 ? `${pageNumber}/${totalPages}` : '';
const bg = THEME_BACKGROUNDS[theme] || THEME_BACKGROUNDS['default'];
let containerStyle, innerStyle, contentStyle;
if (mode === 'auto-fit') {
containerStyle = `
width: ${width}px;
height: ${height}px;
background: ${bg};
position: relative;
padding: 50px;
overflow: hidden;
`;
innerStyle = `
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 60px;
height: calc(${height}px - 100px);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px);
overflow: hidden;
display: flex;
flex-direction: column;
`;
contentStyle = 'flex: 1; overflow: hidden;';
} else if (mode === 'dynamic') {
containerStyle = `
width: ${width}px;
min-height: ${height}px;
background: ${bg};
position: relative;
padding: 50px;
`;
innerStyle = `
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 60px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px);
`;
contentStyle = '';
} else {
containerStyle = `
width: ${width}px;
min-height: ${height}px;
background: ${bg};
position: relative;
padding: 50px;
overflow: hidden;
`;
innerStyle = `
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 60px;
min-height: calc(${height}px - 100px);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px);
`;
contentStyle = '';
}
return `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=${width}">
<title>小红书卡片</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700;900&display=swap');
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Noto Sans SC', 'Source Han Sans CN', 'PingFang SC', 'Microsoft YaHei', sans-serif;
width: ${width}px;
overflow: hidden;
background: transparent;
}
.card-container { ${containerStyle} }
.card-inner { ${innerStyle} }
.card-content { line-height: 1.7; ${contentStyle} }
/* auto-fit 用:对整个内容块做 transform 缩放 */
.card-content-scale { transform-origin: top left; will-change: transform; }
${themeCss}
.page-number {
position: absolute;
bottom: 80px;
right: 80px;
font-size: 36px;
color: rgba(255, 255, 255, 0.8);
font-weight: 500;
}
</style>
</head>
<body>
<div class="card-container">
<div class="card-inner">
<div class="card-content">
<div class="card-content-scale">
${htmlContent}
</div>
</div>
</div>
<div class="page-number">${pageText}</div>
</div>
</body>
</html>`;
}
/**
* 渲染 HTML 为图片
*/
async function renderHtmlToImage(htmlContent, outputPath, width, height, mode, maxHeight, dpr) {
const browser = await chromium.launch();
const viewportHeight = mode !== 'dynamic' ? height : maxHeight;
const page = await browser.newPage({
viewport: { width, height: viewportHeight },
deviceScaleFactor: dpr
});
await page.setContent(htmlContent);
await page.waitForLoadState('networkidle');
await page.waitForTimeout(500);
let actualHeight;
if (mode === 'auto-fit') {
await page.evaluate(() => {
const viewportContent = document.querySelector('.card-content');
const scaleEl = document.querySelector('.card-content-scale');
if (!viewportContent || !scaleEl) return;
// reset
scaleEl.style.transform = 'none';
scaleEl.style.width = '';
scaleEl.style.height = '';
const availableWidth = viewportContent.clientWidth;
const availableHeight = viewportContent.clientHeight;
const rect = scaleEl.getBoundingClientRect();
const contentWidth = Math.max(scaleEl.scrollWidth, rect.width);
const contentHeight = Math.max(scaleEl.scrollHeight, rect.height);
if (!contentWidth || !contentHeight || !availableWidth || !availableHeight) return;
const scale = Math.min(1, availableWidth / contentWidth, availableHeight / contentHeight);
// expand layout box to avoid clip
scaleEl.style.width = (availableWidth / scale) + 'px';
scaleEl.style.transformOrigin = 'top left';
scaleEl.style.transform = `translate(0px, 0px) scale(${scale})`;
});
await page.waitForTimeout(100);
actualHeight = height;
} else if (mode === 'dynamic') {
const contentHeight = await page.evaluate(() => {
const container = document.querySelector('.card-container');
return container ? container.scrollHeight : document.body.scrollHeight;
});
actualHeight = Math.max(height, Math.min(contentHeight, maxHeight));
} else {
const contentHeight = await page.evaluate(() => {
const container = document.querySelector('.card-container');
return container ? container.scrollHeight : document.body.scrollHeight;
});
actualHeight = Math.max(height, contentHeight);
}
await page.screenshot({
path: outputPath,
clip: { x: 0, y: 0, width, height: actualHeight },
type: 'png'
});
await browser.close();
console.log(` ✅ 已生成: ${outputPath} (${width}x${actualHeight})`);
return actualHeight;
}
/**
* 主渲染函数
*/
async function renderMarkdownToCards(options) {
const { markdownFile, outputDir, theme, mode, width, height, maxHeight, dpr } = options;
console.log(`\n🎨 开始渲染: ${markdownFile}`);
console.log(` 📐 主题: ${theme}`);
console.log(` 📏 模式: ${mode}`);
console.log(` 📐 尺寸: ${width}x${height}`);
// 确保输出目录存在
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
// 解析 Markdown 文件
const { metadata, body } = parseMarkdownFile(markdownFile);
// 分割内容
const cardContents = splitContentBySeparator(body);
const totalCards = cardContents.length;
console.log(` 📄 检测到 ${totalCards} 张正文卡片`);
// 生成封面
if (metadata.emoji || metadata.title) {
console.log(' 📷 生成封面...');
const coverHtml = generateCoverHtml(metadata, theme, width, height);
const coverPath = path.join(outputDir, 'cover.png');
await renderHtmlToImage(coverHtml, coverPath, width, height, 'separator', maxHeight, dpr);
}
// 生成正文卡片
for (let i = 0; i < cardContents.length; i++) {
const content = cardContents[i];
console.log(` 📷 生成卡片 ${i + 1}/${totalCards}...`);
const cardHtml = generateCardHtml(content, theme, i + 1, totalCards, width, height, mode);
const cardPath = path.join(outputDir, `card_${i + 1}.png`);
await renderHtmlToImage(cardHtml, cardPath, width, height, mode, maxHeight, dpr);
}
console.log(`\n✨ 渲染完成!图片已保存到: ${outputDir}`);
}
/**
* 主函数
*/
async function main() {
const options = parseArgs();
if (!options.markdownFile) {
console.error('❌ 错误: 请提供 Markdown 文件路径');
printHelp();
process.exit(1);
}
if (!fs.existsSync(options.markdownFile)) {
console.error(`❌ 错误: 文件不存在 - ${options.markdownFile}`);
process.exit(1);
}
if (!AVAILABLE_THEMES.includes(options.theme)) {
console.error(`❌ 错误: 不支持的主题 - ${options.theme}`);
console.error(`可用主题: ${AVAILABLE_THEMES.join(', ')}`);
process.exit(1);
}
if (!PAGING_MODES.includes(options.mode)) {
console.error(`❌ 错误: 不支持的分页模式 - ${options.mode}`);
console.error(`可用模式: ${PAGING_MODES.join(', ')}`);
process.exit(1);
}
await renderMarkdownToCards(options);
}
main().catch(console.error);
Related skills
How it compares
Use xhs-note-creator for Xiaohongshu-specific card pipelines; generic social skills lack Chinese SEO tag and pagination rules.
FAQ
What outputs are generated?
Note title and body plus cover and content image cards.
How many layout themes?
Eight layout themes with four smart pagination modes.
Can it publish?
Yes; optional publishing to Xiaohongshu is supported in the workflow.
Is Xhs Note Creator safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.