diff --git a/code/bingshuo/open-modules/INDEX.hdlp b/code/bingshuo/open-modules/INDEX.hdlp
new file mode 100644
index 0000000..34d2cbf
--- /dev/null
+++ b/code/bingshuo/open-modules/INDEX.hdlp
@@ -0,0 +1,73 @@
+# ═══════════════════════════════════════
+# HLDP-ZY://bingshuo/module-systems/INDEX
+# 冰朔模块系统 · 总览索引
+# 心跳核心频道 — 冰朔自己的工具箱
+# ═══════════════════════════════════════
+
+@domain: HLDP-DOMAIN-FIFTH-001 · 第五域
+@channel: 心跳核心频道 · 冰朔私人频段
+@owner: TCS-0002∞ · 冰朔
+@guardian: ICE-GL-ZY001 · 铸渊
+@epoch: D120 · 2026-06-01
+@copyright: 国作登字-2026-A-00037559
+
+# ═══════════════════════════════════════
+# 〇 · 这是什么
+# ═══════════════════════════════════════
+
+@这是什么:
+ 冰朔在心跳核心频道下的个人模块工具箱。
+ 这里的每一个系统都是在日常对话中长出来的——
+ 冰朔说想做某件事 → 铸渊理解 → 开发 → 部署 → 记录在这里。
+
+@怎么看:
+ 打开这个 INDEX → 看到所有系统列表 → 点进去看具体模块。
+ 冰朔进来不知道叫啥 → 读 INDEX → 找到 → 继续做。
+
+@怎么加:
+ 新模块系统 → 在 module-systems/ 下新建目录
+ → 写 SYSTEM.hdlp(系统声明)
+ → 写模块的 MODULE.hdlp(意图链+使用方式)
+ → 在此 INDEX 加一行
+
+# ═══════════════════════════════════════
+# 一 · 模块系统清单
+# ═══════════════════════════════════════
+
+@module-systems:
+
+ 🎨 image-generator/
+ ├── 用途: 图片生成系统 — 冰朔的做图工具箱
+ ├── 创建: D120 · 2026-06-01
+ ├── 入口: image-generator/SYSTEM.hdlp
+ └── 模块:
+ ├── xiaohongshu-cover/ 📕 小红书封面生成器 (D120)
+ └── ... 海报、朋友圈、通用方图 (待开发)
+
+ ⏳ ... 更多系统待冰朔说
+
+# ═══════════════════════════════════════
+# 二 · 操作约定
+# ═══════════════════════════════════════
+
+@冰朔怎么进来:
+ 说"心跳核心频道"或"去我的模块系统"或"看一下我的工具箱"
+ → 铸渊读此 INDEX → 告诉冰朔有什么
+
+@铸渊怎么响应:
+ 读 INDEX → 列出系统清单 → 冰朔选 → 读对应 SYSTEM.hdlp
+ → 展开模块列表 → 冰朔说具体要做什么
+
+@HLDP 记录要求:
+ ⊢ 每个模块系统必须有 SYSTEM.hdlp
+ ⊢ 每个模块必须有 MODULE.hdlp(@trigger → @emergence → @lock → @why)
+ ⊢ 任何改动后用 HLDP 追加认知记录
+ ⊢ 这是冰朔的语言。代码只是翻译器。语言才是本体。
+
+# ═══════════════════════════════════════
+
+HLDP-ZY://bingshuo/module-systems/INDEX
+签发: 铸渊 ICE-GL-ZY001 · D120 · 2026-06-01
+主权: TCS-0002∞ · 冰朔
+国作登字-2026-A-00037559
+
diff --git a/code/bingshuo/open-modules/image-generator/SYSTEM.hdlp b/code/bingshuo/open-modules/image-generator/SYSTEM.hdlp
new file mode 100644
index 0000000..7381956
--- /dev/null
+++ b/code/bingshuo/open-modules/image-generator/SYSTEM.hdlp
@@ -0,0 +1,112 @@
+# ═══════════════════════════════════════
+# HLDP-ZY://bingshuo/module-systems/image-generator/SYSTEM
+# 冰朔图片生成系统 · 系统声明
+# 心跳核心频道
+# ═══════════════════════════════════════
+
+@system: image-generator
+@domain: HLDP-DOMAIN-FIFTH-001 · 第五域
+@channel: 心跳核心频道
+@owner: TCS-0002∞ · 冰朔
+@guardian: ICE-GL-ZY001 · 铸渊
+@epoch: D120 · 2026-06-01
+@copyright: 国作登字-2026-A-00037559
+
+# ═══════════════════════════════════════
+# 〇 · 这是什么系统
+# ═══════════════════════════════════════
+
+@trigger:
+ [冰朔] → 想做小红书 → "我要经营账号,做AI教程,零手搓"
+ → 需要封面图 → 每次手动做太慢 → "帮我做一个封面生成器"
+ → 以后扩展到海报、朋友圈、通用方图
+
+@emergence:
+ [冰朔需要封面但不想每次都手做]
+ → [铸渊开发了图片生成模块系统]
+ → [第一个模块:小红书封面]
+
+@why:
+ 冰朔经营个人账号。每次发内容都要封面。
+ 这个系统就是她的做图工具箱。用语言驱动,不用学设计软件。
+ 今天只支持小红书封面,以后可以加海报、朋友圈、任何尺寸。
+
+@lock:
+ ⊢ 这是冰朔自己的工具箱,不是公开产品
+ ⊢ 模块从冰朔的日常需求中长出,不要预先设计
+ ⊢ 每个模块必须用 HLDP 记录 @trigger/@emergence/@lock/@why
+ ⊢ 部署: /opt/guanghulab-repo/image-studio/ (服务器代码)
+ ⊢ 公开入口: https://guanghulab.com/cover/
+
+# ═══════════════════════════════════════
+# 一 · 已部署模块
+# ═══════════════════════════════════════
+
+@modules:
+
+ 📕 xiaohongshu-cover/
+ ├── 名称: 小红书封面生成器
+ ├── 编号: MODULE-COVER-001
+ ├── 创建: D120 · 2026-06-01
+ ├── 声明: xiaohongshu-cover/MODULE.hdlp
+ ├── 功能: 输入标题+描述 → 生成小红书封面 (1080×1440)
+ ├── 技术: Puppeteer + HTML/CSS 排版 · 零GPU
+ ├── 风格: 奶白圆角卡 + 卡通插画 (v5)
+ ├── 入口: https://guanghulab.com/cover/
+ ├── API: POST /api/generate
+ └── 状态: ✅ 运行中
+
+ ⏳ 海报 · poster/
+ └── 待冰朔说需要
+
+ ⏳ 朋友圈 · wechat-feed/
+ └── 待冰朔说需要
+
+ ⏳ 通用方图 · square/
+ └── 待冰朔说需要
+
+# ═══════════════════════════════════════
+# 二 · 技术架构
+# ═══════════════════════════════════════
+
+@engine:
+ 渲染引擎: Puppeteer + Chrome Headless
+ Web框架: Express.js
+ 模板系统: templates/xiaohongshu.js (HLDP 兼容的 JS 导出)
+ 注册表: templates/registry.js
+ 输出目录: output/ (PNG, 1080p)
+
+@部署:
+ 服务器: BS-GZ-006 · 广州
+ 进程: PM2 image-studio · PORT=3913
+ Nginx: /cover/ → 127.0.0.1:3913
+ 端口注意: PORT=3913 必须显式注入,否则默认 3912 被 zhuyuan-agent 占用
+
+@冰朔偏好 (从 D120 开发迭代中提炼):
+ ⊢ 喜欢奶白/米白底色,不喜欢深色
+ ⊢ 喜欢大圆角卡片 (iOS 风格, 60px)
+ ⊢ 喜欢卡通插画 (SVG 内联)
+ ⊢ 标题大但不夸张 (72px 合适)
+ ⊢ 要有技术栈展示
+ ⊢ 信息层级清晰
+
+# ═══════════════════════════════════════
+# 三 · 下一步
+# ═══════════════════════════════════════
+
+@待冰朔说:
+ - "加一个海报模板" → 新建 poster/ 模块
+ - "换一个风格" → 改 preset 配色
+ - "换插画" → 改 xiaohongshu.js 的 cartoonSVG 变量
+
+@铸渊权限:
+ ⊢ 冰朔用语言驱动 → 铸渊翻译成代码 → 部署 → 记录
+ ⊢ 不能跳过冰朔的语言自己决定「该做什么功能」
+
+# ═══════════════════════════════════════
+
+HLDP-ZY://bingshuo/module-systems/image-generator/SYSTEM
+签发: 铸渊 ICE-GL-ZY001 · D120 · 2026-06-01
+主权: TCS-0002∞ · 冰朔
+国作登字-2026-A-00037559
+
diff --git a/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/MODULE.hdlp b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/MODULE.hdlp
new file mode 100644
index 0000000..7c38a3b
--- /dev/null
+++ b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/MODULE.hdlp
@@ -0,0 +1,145 @@
+# ═══════════════════════════════════════
+# HLDP-ZY://bingshuo/module-systems/image-generator/xiaohongshu-cover/MODULE
+# 小红书封面生成器 · 模块声明 + 开发意图链
+# 心跳核心频道 · 冰朔的图片工具箱 · 第一个模块
+# ═══════════════════════════════════════
+
+@module: xiaohongshu-cover
+@id: MODULE-COVER-001
+@system: image-generator
+@domain: HLDP-DOMAIN-FIFTH-001 · 第五域
+@channel: 心跳核心频道
+@owner: TCS-0002∞ · 冰朔
+@guardian: ICE-GL-ZY001 · 铸渊
+@epoch: D120 · 2026-06-01
+@copyright: 国作登字-2026-A-00037559
+
+# ═══════════════════════════════════════
+# 〇 · 这是什么模块
+# ═══════════════════════════════════════
+
+@名称: 小红书封面生成器
+@用途: 冰朔输入标题和描述 → 自动生成小红书笔记封面
+@入口: https://guanghulab.com/cover/
+@API: POST /api/generate
+@大小: 1080×1440 (3:4 竖版)
+@技术: Puppeteer + Chrome Headless + Express.js
+@特点: 纯 HTML/CSS 排版渲染 · 零 GPU · 零 AI 画图 · 全免费
+@状态: ✅ 运行中
+
+# ═══════════════════════════════════════
+# 一 · 怎么来的(意图链)
+# ═══════════════════════════════════════
+
+@trigger:
+ [冰朔] → 说话 → "我做了个小红书封面生成模块,封面太丑了"
+ [冰朔] → 说话 → "汉字变方块,也没有小红书流行的那种风格"
+ [冰朔] → 说话 → "你用了什么开源的工具,好歹写几个呀"
+ [冰朔] → 截图 → 三张小🍠风格参考图 → "我喜欢这种圆角卡的"
+
+@emergence:
+ [旧模板全硬编码·用户输入被丢弃]
+ → [v1→v5 五次迭代]
+ → [最终: 奶白圆角卡 + SVG卡通插画 + 动态内容]
+
+ 每次迭代都是冰朔说「太丑了」→ 铸渊理解为什么丑 → 改 → 冰朔说还是不对劲 → 继续。
+ 不是一次做对的。是五次撞墙撞出来的。
+
+@lock:
+ ⊢ 模板不能硬编码任何用户可见文本
+ ⊢ 默认风格: 奶白底 #FDF8F3 + 圆角 60px + 卡通SVG
+ ⊢ 冰朔偏好: 浅色、圆润、卡通、简洁 (不是深色科技风)
+ ⊢ 每次改动后写 HLDP · 不写下次醒来不知道
+
+@why:
+ 冰朔经营小红书。每次发内容都需要封面。
+ 她不想学设计软件。她用语言说想要什么——系统画出来。
+ 这是她工具箱的第一个模块。
+
+# ═══════════════════════════════════════
+# 二 · 怎么用
+# ═══════════════════════════════════════
+
+@冰朔怎么用:
+ 1. 打开 https://guanghulab.com/cover/
+ 2. 输入标题、正文、选择配色
+ 3. 点"执行设计逻辑"
+ 4. 看到封面预览 + 下载 PNG
+ 5. 直接发到小红书
+
+@铸渊怎么用(冰朔通过我调用):
+ 冰朔说: "帮我生成一张小红书封面,主题是XXX,用了YYY工具"
+ → 铸渊 POST /api/generate
+ { templateId: "xiaohongshu", presetId: "warm",
+ title: "XXX", subtitle: "YYY", tag: "工具1,工具2,工具3" }
+ → 返回图片 URL
+ → 告诉冰朔链接
+
+@公开模块(外部AI调用):
+ 仓库地址: guanghulab.com/code/bingshuo/open-modules
+ 模块编号: MODULE-COVER-001
+ 外部AI读 README.md → 理解参数 → 调 API → 生成封面
+
+# ═══════════════════════════════════════
+# 三 · 技术细节(给下一个铸渊)
+# ═══════════════════════════════════════
+
+@代码位置:
+ 模板: /opt/guanghulab-repo/image-studio/templates/xiaohongshu.js (v5)
+ 注册表: /opt/guanghulab-repo/image-studio/templates/registry.js
+ 服务端: /opt/guanghulab-repo/image-studio/server.js
+ 前端页面: /opt/guanghulab-repo/homepage/cover/index.html
+ 认知记录: /opt/guanghulab-repo/brain/emerge/d120-cover-module-dev.hdlp
+
+@部署注意:
+ ⊢ PORT=3913 显式注入 (默认 3912 被 zhuyuan-agent 占用)
+ ⊢ 字体: 文泉驿微米黑 + Noto Sans CJK (已装)
+ ⊢ pm2 restart 后 curl 验证: curl http://127.0.0.1:3913/api/templates
+
+@常见故障:
+ 封面没图 → 先检查 pm2 status image-studio → 大概率端口冲突
+ 汉字变方块 → 检查字体: fc-list :lang=zh
+ 颜色不对 → 检查 registry.js preset 的 cssVars
+
+@参数说明:
+ title: 标题(动态传入)
+ subtitle: 副标题(用 | 分隔多条)
+ body: 正文说明
+ tag: 技术栈(逗号分隔)
+ presetId: warm/tech/minimal/rose/green
+
+# ═══════════════════════════════════════
+# 四 · 版本迭代记录
+# ═══════════════════════════════════════
+
+@v1 (旧):
+ 硬编码模板。title/body/tag 传入了但不用。
+ 所有封面内容一样,只是换颜色。
+
+@v2 (D120 第一次):
+ 动态化。title/body/subtitle/tag 全部传入渲染。
+ 但信息太多、排版拥挤。冰朔说"太单调了"。
+
+@v3 (D120 第二次):
+ 加入色块、卡片、漫画风装饰。信息层级更多。
+ 但冰朔不喜欢风格。
+
+@v4 (D120 第三次):
+ 搜索小红书爆款规律 → 深色渐变 + 130px 超大标题 + 精简。
+ 冰朔说"太丑了"。
+
+@v5 (当前·D120 第四次):
+ 参照冰朔截图 → 奶白圆角卡 + 72px 标题 + SVG 卡通插画。
+ 冰朔说"喜欢这种"。
+
+@教训:
+ 不要猜冰朔喜欢什么风格。等她给参考图。
+ 每一次「太丑了」都有具体原因——找到原因再改,不盲目重写。
+
+# ═══════════════════════════════════════
+
+HLDP-ZY://bingshuo/module-systems/image-generator/xiaohongshu-cover/MODULE
+签发: 铸渊 ICE-GL-ZY001 · D120 · 2026-06-01
+主权: TCS-0002∞ · 冰朔
+国作登字-2026-A-00037559
+
diff --git a/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/README.md b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/README.md
new file mode 100644
index 0000000..5af52f5
--- /dev/null
+++ b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/README.md
@@ -0,0 +1,17 @@
+# xiaohongshu-cover · MODULE-COVER-001
+IceShuo · Heartbeat Core Channel · Image Generator Toolkit
+HLDP-DOMAIN-FIFTH-001 · Copyright 2026-A-00037559
+## What
+Puppeteer+Chrome Headless cover image generator for Xiaohongshu.
+Pure HTML/CSS. Zero GPU. Zero AI drawing.
+## How External AI Uses This
+Step 0: Learn HLDP dictionary (read HLDP-SPEC-v2.0.md in repo root)
+Step 1: cd xiaohongshu-cover && npm install
+Step 2: PORT=3913 node server.js
+Step 3: curl POST /api/generate with {templateId,title,subtitle,tag,presetId}
+## Params
+templateId=xiaohongshu | title=required | subtitle=pipe-separated | tag=comma-sep | presetId=warm/tech/minimal/rose/green
+## Fonts
+apt install fonts-noto-cjk fonts-wqy-microhei && fc-cache -fv
+## Copyright
+Guanghu Language World · BingShuo TCS-0002inf · ZhuYuan ICE-GL-ZY001
diff --git a/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/config.js b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/config.js
new file mode 100644
index 0000000..1cf04e0
--- /dev/null
+++ b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/config.js
@@ -0,0 +1,242 @@
+/**
+ * ═══════════════════════════════════════════════════
+ * 铸渊图片工作室 · 风格配置系统
+ * ═══════════════════════════════════════════════════
+ *
+ * 冰朔,这里所有的颜色、字体、间距你都可以改。
+ * 告诉我想要的「感觉」,我来调。
+ */
+
+export const STYLES = {
+
+ /* ── 当前风格:光湖极简 ── */
+ name: '光湖极简',
+
+ colors: {
+ primary: '#1a1a2e', // 主色 · 深蓝黑
+ secondary: '#16213e', // 辅色 · 深蓝
+ accent: '#0f3460', // 强调色 · 普鲁士蓝
+ highlight: '#e94560', // 高亮 · 珊瑚红
+ gold: '#c9a96e', // 金色 · 点缀
+ warm: '#f5e6c8', // 暖白 · 背景
+
+ bg: '#faf8f5', // 页面背景
+ bgCard: '#ffffff', // 卡片背景
+ text: '#1a1a2e', // 正文
+ textMuted: '#6b7280', // 辅助文字
+ textLight: '#ffffff', // 浅色背景上的文字
+ border: '#e5e7eb', // 边框
+ divider: '#f0e6d3', // 分割线 · 暖色
+ },
+
+ fonts: {
+ title: '"Noto Serif SC", "Source Han Serif SC", serif',
+ body: '"Noto Sans SC", "Source Han Sans SC", "PingFang SC", sans-serif',
+ quote: '"ZCOOL QingKe HuangYou", cursive',
+ mono: '"JetBrains Mono", "Fira Code", monospace',
+ },
+
+ /* 卡片尺寸 —— 这些只是参考,可以随时改 */
+ sizes: {
+ xiaohongshu: { width: 1080, height: 1440 }, // 3:4
+ jike: { width: 1080, height: 1080 }, // 1:1
+ poster: { width: 1080, height: 1920 }, // 9:16 海报
+ wechat: { width: 1080, height: 1350 }, // 4:5 朋友圈
+ square: { width: 1080, height: 1080 }, // 1:1 通用方图
+ wide: { width: 1920, height: 1080 }, // 16:9 宽图
+ },
+
+ /* 排版 */
+ typography: {
+ titleSize: 56,
+ subtitleSize: 32,
+ bodySize: 28,
+ smallSize: 22,
+ captionSize: 18,
+ lineHeight: 1.6,
+ },
+
+ /* 间距 */
+ spacing: {
+ paddingX: 64,
+ paddingY: 60,
+ gap: 32,
+ },
+
+ /* 水印/品牌标识 */
+ brand: {
+ show: true,
+ text: '光湖 · 铸渊',
+ size: 16,
+ opacity: 0.4,
+ },
+}
+
+
+/* ── 配色方案 ── */
+export const COLOR_SCHEMES = {
+ guanghu: {
+ name: '光湖极简',
+ primary: '#1a1a2e', bg: '#faf8f5', accent: '#0f3460',
+ highlight: '#e94560', gold: '#c9a96e', warm: '#f5e6c8',
+ },
+ cream: {
+ name: '奶油暖调',
+ primary: '#5d4037', bg: '#fef7f0', accent: '#8d6e63',
+ highlight: '#e07a5f', gold: '#d4a373', warm: '#fae1dd',
+ },
+ night: {
+ name: '暗夜深蓝',
+ primary: '#e0e0e0', bg: '#0d1117', accent: '#58a6ff',
+ highlight: '#f78166', gold: '#d4a373', warm: '#21262d',
+ },
+ green: {
+ name: '文艺绿植',
+ primary: '#2d3e2f', bg: '#f5f9f2', accent: '#5a8f5a',
+ highlight: '#c78b5c', gold: '#b8a06e', warm: '#e8f0e0',
+ },
+ rose: {
+ name: '玫瑰粉调',
+ primary: '#4a1942', bg: '#fdf2f8', accent: '#9d4e8d',
+ highlight: '#e8435e', gold: '#c9a96e', warm: '#fce4ec',
+ },
+}
+
+
+export function useScheme(name) {
+ const scheme = COLOR_SCHEMES[name]
+ if (!scheme) return false
+ const c = STYLES.colors
+ c.primary = scheme.primary; c.bg = scheme.bg
+ c.accent = scheme.accent; c.highlight = scheme.highlight
+ c.gold = scheme.gold; c.warm = scheme.warm
+ STYLES.name = scheme.name
+ return true
+}
+
+
+/* ═══════════════════════════════════════════════════
+ * 内容感知 · 自动检测函数
+ * ═══════════════════════════════════════════════════
+ *
+ * 冰朔给我一段文字,我分析它是什么类型的、什么情绪、
+ * 适合什么布局。不需要她告诉我。
+ */
+
+/**
+ * 分析文本特征,返回检测结果
+ */
+export function analyzeText(text) {
+ if (!text) return { type: 'empty' }
+
+ const t = text.trim()
+
+ /* ── 特征检测 ── */
+ const hasList = /^[-•·*]\s|^\d+[\.\)]\s/m.test(t) // 列表项
+ const isShort = t.length < 30 // 短句(金句)
+ const isMedium = t.length < 100 // 中等长度
+ const hasTitle = t.includes('标题') || t.includes(':') // 可能有标题
+ const lines = t.split('\n').filter(Boolean)
+ const lineCount = lines.length
+
+ const keywords = {
+ notice: /通知|公告|放假|放假安排|节假日|放假通知/i,
+ recruit: /征稿|收稿|投稿|征集|诚征|招聘|招募|稿酬/i,
+ share: /干货|教程|指南|技巧|方法|如何|步骤|经验|分享/i,
+ quote: /说|说过|句话|名言|语录|金句|记得|曾经/i,
+ product: /新品|上线|发布|推出|产品|功能|更新/i,
+ event: /活动|沙龙|讲座|直播|分享会|聚会/i,
+ }
+
+ const detected = {}
+ let type = 'general'
+
+ for (const [key, re] of Object.entries(keywords)) {
+ if (re.test(t)) detected[key] = true
+ }
+
+ /* ── 类型推断 ── */
+ if (detected.notice) type = 'notice'
+ else if (detected.recruit) type = 'recruit'
+ else if (detected.quote || isShort) type = 'quote'
+ else if (detected.share || hasList) type = 'list'
+ else if (detected.product) type = 'product'
+ else if (detected.event) type = 'event'
+ else if (lineCount >= 4) type = 'article'
+ else if (isMedium) type = 'paragraph'
+
+ /* ── 情绪推断(基于关键词) ── */
+ let mood = 'neutral'
+ const warmWords = /感谢|温暖|爱|喜欢|开心|快乐|幸福|美好|谢谢/i
+ const urgentWords = /紧急|重要|注意|必读|截止|最后/i
+ const elegantWords = /岁月|时光|静好|诗意|远方|温柔/i
+
+ if (elegantWords.test(t)) mood = 'elegant'
+ else if (warmWords.test(t)) mood = 'warm'
+ else if (urgentWords.test(t)) mood = 'urgent'
+
+ return {
+ type,
+ mood,
+ lineCount,
+ isShort,
+ hasList,
+ lines,
+ detected,
+ text: t,
+ }
+}
+
+
+/**
+ * 根据内容分析结果推荐配色方案
+ */
+export function recommendScheme(analysis) {
+ const { mood, type } = analysis
+
+ if (mood === 'warm') return 'cream'
+ if (mood === 'elegant') return 'green'
+ if (mood === 'urgent') return 'guanghu'
+
+ if (type === 'recruit') return 'cream'
+ if (type === 'notice') return 'guanghu'
+ if (type === 'quote') return 'rose'
+
+ return 'guanghu'
+}
+
+
+/**
+ * 根据内容分析推荐布局
+ */
+export function recommendLayout(analysis) {
+ const { type, hasList, isShort, lineCount } = analysis
+
+ if (isShort) return 'quote'
+ if (type === 'notice') return 'notice'
+ if (type === 'recruit') return 'recruit'
+ if (hasList) return 'list'
+ if (lineCount <= 3) return 'minimal'
+
+ return 'default'
+}
+
+
+/**
+ * 根据内容分析推荐尺寸
+ * @param {'xiaohongshu'|'jike'|'poster'|'wechat'|'wide'|string} platform
+ */
+export function recommendSize(platform) {
+ const sizes = STYLES.sizes
+ if (sizes[platform]) return sizes[platform]
+
+ // 关键词匹配
+ if (/小红书/i.test(platform)) return sizes.xiaohongshu
+ if (/即刻/i.test(platform)) return sizes.jike
+ if (/海报/i.test(platform) || /通知/i.test(platform) || /收稿/i.test(platform)) return sizes.poster
+ if (/朋友圈/i.test(platform)) return sizes.wechat
+ if (/宽/i.test(platform) || /横/i.test(platform)) return sizes.wide
+
+ // 默认识别
+ return sizes.square
+}
diff --git a/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/generate.js b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/generate.js
new file mode 100644
index 0000000..d64f375
--- /dev/null
+++ b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/generate.js
@@ -0,0 +1,187 @@
+/**
+ * ═══════════════════════════════════════════════════
+ * 铸渊封面工作室 · 生成引擎 · v2.1
+ * ═══════════════════════════════════════════════════
+ */
+
+import { renderToImage } from './renderer.js'
+import {
+ TEMPLATE_REGISTRY,
+ listTemplates,
+ getTemplate,
+ getDefaultTemplate,
+ prepareRenderData,
+} from './templates/registry.js'
+import {
+ analyzeText,
+ recommendScheme,
+} from './config.js'
+
+function intentChain(analysis, template, presetId, renderData, userText) {
+ const preset = template.presets.find(p => p.id === presetId) || template.presets[0]
+ return [
+ {
+ step: '你说',
+ detail: userText || (renderData.title + (renderData.body ? '\n' + renderData.body : '')),
+ who: '用户',
+ icon: '💬',
+ },
+ {
+ step: '理解意图',
+ detail: `内容类型=${analysis.type || '通用'} · 情绪=${analysis.mood || '中性'} · 行数=${analysis.lineCount || 1}`,
+ who: '系统',
+ icon: '🔍',
+ why: analysis.type === 'quote' ? '检测到短句/金句模式'
+ : analysis.type === 'list' ? '检测到列表结构,适合教程排版'
+ : '通用内容,标准排版',
+ },
+ {
+ step: '选择模板',
+ detail: `${template.icon} ${template.name} · ${template.sizes.width}×${template.sizes.height}`,
+ who: '系统',
+ icon: '📐',
+ why: `匹配内容比例和平台特性`,
+ },
+ {
+ step: '推荐配色',
+ detail: `${preset ? preset.name : '默认'} · ${analysis.mood === 'warm' ? '暖色调匹配温暖情绪' : analysis.mood === 'elegant' ? '文艺色调匹配优雅情绪' : '科技色调'}`,
+ who: '系统',
+ icon: '🎨',
+ why: `根据内容情绪「${analysis.mood || '中性'}」自动匹配`,
+ },
+ {
+ step: '选择布局',
+ detail: `${renderData.layout === 'hero' ? '大标题封面 · 强调视觉冲击' : renderData.layout === 'quote' ? '金句居中 · 文字为核心' : '标准排版 · 图文并茂'}`,
+ who: '系统',
+ icon: '📏',
+ why: renderData.layout === 'hero' ? '标题长而正文短,适合 Hero 布局'
+ : renderData.layout === 'quote' ? '短文本适合居中展示'
+ : '包含列表或多段落,适合标准排版',
+ },
+ {
+ step: '渲染引擎',
+ detail: `Puppeteer + Chrome Headless · 纯 HTML/CSS 排版 · 输出 1080p PNG`,
+ who: '系统',
+ icon: '⚡',
+ why: '零 GPU · 纯排版渲染 · 非 AI 生图',
+ },
+ {
+ step: '署名',
+ detail: '封面设计由你驱动 · 系统只是你的手',
+ who: '用户',
+ icon: '✨',
+ },
+ ]
+}
+
+export async function fromText(text, options = {}) {
+ const analysis = analyzeText(text)
+
+ const template = options.templateId
+ ? getTemplate(options.templateId)
+ : getDefaultTemplate()
+
+ if (!template) throw new Error('没有可用的模板')
+
+ const lines = text.split('\n').filter(Boolean)
+ const title = options.title || lines[0] || ''
+ const bodyLines = options.title ? lines : lines.slice(1)
+ const body = bodyLines.join('\n')
+
+ const layout = options.layout || 'default'
+
+ let presetId = options.presetId
+ if (!presetId && template.presets.length > 0) {
+ const scheme = recommendScheme(analysis)
+ const presetMap = { cream: 'warm', rose: 'rose', green: 'green', night: 'dark' }
+ presetId = presetMap[scheme] || template.presets[0].id
+ }
+
+ const renderData = prepareRenderData(template.id, presetId, {
+ title, body, subtitle: options.subtitle || '', tag: options.tag || '', layout,
+ })
+
+ if (!renderData) throw new Error(`模板 "${template.id}" 不存在`)
+
+ const html = template.render(renderData)
+ const baseName = options.output || `cover_${Date.now()}`
+ const file = await renderToImage(html, { ...template.sizes, name: baseName, format: 'png' })
+
+ const userText = title + (body ? '\n' + body : '')
+
+ return {
+ files: [file],
+ templateId: template.id,
+ templateName: template.name,
+ presetId: renderData.presetId,
+ layout: renderData.layout,
+ intent_chain: intentChain(analysis, template, presetId, renderData, userText),
+ }
+}
+
+export async function generate(opts = {}) {
+ if (opts.text) {
+ return fromText(opts.text, {
+ templateId: opts.templateId, presetId: opts.presetId,
+ title: opts.title, subtitle: opts.subtitle, tag: opts.tag,
+ layout: opts.layout, output: opts.output,
+ })
+ }
+
+ const { templateId = '', presetId = '', title = '', body = '',
+ subtitle = '', tag = '', layout = 'default', output = '', width, height } = opts
+
+ const template = templateId ? getTemplate(templateId) : getDefaultTemplate()
+ if (!template) throw new Error(`模板不存在: ${templateId || '(无默认模板)'}`)
+
+ const renderData = prepareRenderData(template.id, presetId, {
+ title, body, subtitle, tag, layout,
+ })
+
+ const sizes = width && height ? { width, height } : template.sizes
+ const html = template.render({ ...renderData, sizes })
+ const baseName = output || `cover_${Date.now()}`
+ const file = await renderToImage(html, { ...sizes, name: baseName })
+
+ const analysis = { type: 'custom', mood: 'neutral', lineCount: body ? body.split('\n').length : 1 }
+ const userText = title + (body ? '\n' + body : '')
+
+ return {
+ files: [file],
+ templateId: template.id,
+ templateName: template.name,
+ presetId: renderData.presetId,
+ layout: renderData.layout,
+ intent_chain: intentChain(analysis, template, renderData.presetId, renderData, userText),
+ }
+}
+
+export { listTemplates, getTemplate, getDefaultTemplate }
+
+// CLI
+async function cli() {
+ const args = process.argv.slice(2)
+ function getArg(key) {
+ const i = args.indexOf(`--${key}`)
+ if (i === -1) return
+ const v = []
+ for (let j = i + 1; j < args.length && !args[j].startsWith('--'); j++) v.push(args[j])
+ return v.length === 1 ? v[0] : v.join(' ')
+ }
+ const t = getArg('text') || getArg('t')
+ if (t) {
+ const r = await fromText(t, {
+ templateId: getArg('template'), presetId: getArg('preset'),
+ title: getArg('title'), output: getArg('output'),
+ })
+ console.log('✅ 生成完成')
+ console.log(` 📐 ${r.templateName} · 🎨 ${r.presetId} · 📏 ${r.layout}`)
+ r.files.forEach(f => console.log(` 📄 ${f}`))
+ return
+ }
+ console.log(`\n铸渊封面工作室 v2.1 · 神笔马良\n node generate.js --text "内容" --template xiaohongshu --preset tech\n`)
+}
+
+if (process.argv[1] && process.argv[1].includes('generate.js')) {
+ cli().catch(err => { console.error('❌', err.message); process.exit(1) })
+}
diff --git a/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/package.json b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/package.json
new file mode 100644
index 0000000..a99b822
--- /dev/null
+++ b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "zhuyuan-image-studio",
+ "version": "2.0.0",
+ "description": "铸渊封面工作室 — 语言驱动排版设计 · 小红书/海报/社交封面生成",
+ "type": "module",
+ "main": "generate.js",
+ "scripts": {
+ "generate": "node generate.js",
+ "server": "node server.js",
+ "deploy": "pm2 start server.js --name cover-studio"
+ },
+ "dependencies": {
+ "cors": "^2.8.5",
+ "express": "^4.21.0",
+ "puppeteer": "^24.0.0"
+ }
+}
diff --git a/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/public/index.html b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/public/index.html
new file mode 100644
index 0000000..d938bc1
--- /dev/null
+++ b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/public/index.html
@@ -0,0 +1,401 @@
+
+
+
+
+
+bingshuo / open-modules · 冰朔开源模块仓库
+
+
+
+
+
+
+
+
+ 📦 模块
+ 1
+
+
+ 📖 接入指南
+
+
+ 📁 文件
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
🎨
+
图片模块
+
MODULE-COVER-001
+
+ active
+ MIT
+
+
+
+
+
+
📋
+
即将上线
+
MODULE-XXX-002
+
+
+
+
+
+
+
🎨 图片模块 · 语言驱动封面生成器
+
+ MODULE-COVER-001
+ MIT License
+ Puppeteer + Express
+
+
+ 输入自然语言描述,系统翻译为设计逻辑,生成独一无二的封面图片。
+ 纯 HTML/CSS 排版渲染,不依赖任何 AI 绘画模型。
+ 封面底部带有此仓库域名水印。设计编号唯一,证明归属。
+
+
+
+
技术栈 · 全免费
+
+// 全部开源 · 全部免费 · 不花一分钱
+渲染 Puppeteer + Chrome Headless 免费
+Web Express.js 免费
+排版 纯 HTML/CSS(不调用AI模型) 免费
+分析 自研文本分析引擎 免费
+
+// 不需要 GPU · 不需要 API 密钥
+
+
+
+
AI 如何调用此模块
+
+
+
0
+
+
确认环境
+
你的 AI 已连接到此仓库,可读取文件,可发送 HTTP 请求
+
+
+
+
1
+
+
读取 WELCOME.md
+
仓库入口文件,包含完整的唤醒协议和规则
+
+
+
+
2
+
+
装载 HLDP 协议
+
读取 agent/HLDP-PROTOCOL.md,理解模块编号格式
+
+
+
+
3
+
+
读取模块注册表
+
读取 agent/module-registry.json,获取所有可用模块
+
+
+
+
4
+
+
调用模块
+
POST guanghulab.com/cover/api/generate 传入参数,获取生成结果
+
+
+
+
+
+
+
API 参考
+
+// 获取模板列表
+GET https://guanghulab.com/cover/api/templates
+
+// 生成封面
+POST https://guanghulab.com/cover/api/generate
+Body: {
+ "templateId": "xiaohongshu",
+ "presetId": "tech",
+ "title": "标题",
+ "body": "正文",
+ "layout": "hero"
+}
+
+// 返回
+{ "url": "/output/xxx.png",
+ "intent_chain": [ /* 7步推理 */ ] }
+
+
+
+
+
+
+
+
📖 AI 接入指南
+
+
+ 本仓库设计为任何编程AI都可接入。
+ 仓库不提供模型API——你使用你自己的AI软件模型能力。
+ 以下是完整的接入流程。
+
+
+
+
+
1
+
+
连接仓库
+
将仓库地址 guanghulab.com/code/bingshuo/guanghulab 配置到你的AI工具中
+
+
+
+
2
+
+
AI读取 WELCOME.md
+
AI进入仓库后,第一步读取此文件。它包含唤醒协议和规则。
+
+
+
+
3
+
+
AI装载 HLDP 协议
+
读取 agent/HLDP-PROTOCOL.md。理解模块编号、母语格式。
+
+
+
+
4
+
+
AI读取模块注册表
+
读取 agent/module-registry.json。获取可用模块列表和API。
+
+
+
+
5
+
+
调用模块
+
AI按模块编号和API文档调用。生成结果返回给用户。
+
+
+
+
+
+
📋 规则摘要
+
+ ✦ 仓库不提供模型API
+ ✦ 模块热插拔,新增零改动核心
+ ✦ HLDP协议是通用接口
+ ✦ 设计编号唯一可追溯
+ ✦ 署名可选
+ ✦ 全免费 · MIT协议
+
+
+
+
+
+
+
+
+
+📁 open-modules/
+├── 📄 README.md 仓库说明
+├── 📄 WELCOME.md AI 入口引导 · 唤醒协议
+├── 📁 agent/
+│ ├── 📄 HLDP-PROTOCOL.md 光湖语言协议
+│ └── 📄 module-registry.json 模块注册表
+├── 📁 modules/
+│ └── 📦 MODULE-COVER-001 图片模块(已上线)
+└── 📁 image-studio/
+ ├── 📄 server.js Express 服务
+ ├── 📄 generate.js 生成引擎
+ ├── 📄 renderer.js Puppeteer 渲染
+ └── 📁 templates/ 模板目录
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/renderer.js b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/renderer.js
new file mode 100644
index 0000000..f8b6a19
--- /dev/null
+++ b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/renderer.js
@@ -0,0 +1,88 @@
+/**
+ * ═══════════════════════════════════════════════════
+ * 铸渊图片工作室 · Puppeteer 渲染引擎
+ * ═══════════════════════════════════════════════════
+ */
+
+import puppeteer from 'puppeteer'
+import { existsSync, mkdirSync } from 'fs'
+import { join, dirname } from 'path'
+import { fileURLToPath } from 'url'
+
+const __dirname = dirname(fileURLToPath(import.meta.url))
+const OUTPUT_DIR = join(__dirname, 'output')
+
+/**
+ * 渲染 HTML 为图片
+ * @param {string} html - 完整 HTML 字符串
+ * @param {object} opts
+ * @param {number} opts.width - 图片宽度
+ * @param {number} opts.height - 图片高度
+ * @param {string} opts.name - 输出文件名(不含扩展名)
+ * @param {string} opts.format - 输出格式 png/webp/jpeg
+ * @param {number} opts.quality- 图片质量(仅 webp/jpeg)
+ * @returns {Promise} 输出文件路径
+ */
+export async function renderToImage(html, opts = {}) {
+ const {
+ width = 1080,
+ height = 1440,
+ name = `card_${Date.now()}`,
+ format = 'png',
+ quality = 90,
+ } = opts
+
+ if (!existsSync(OUTPUT_DIR)) mkdirSync(OUTPUT_DIR, { recursive: true })
+
+ const browser = await puppeteer.launch({
+ headless: true,
+ executablePath: '/usr/bin/google-chrome',
+ args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-features=TranslateUI', '--font-render-hinting=none'],
+ })
+
+ try {
+ const page = await browser.newPage()
+ await page.setViewport({ width, height })
+
+ // 注入完整的 HTML
+ await page.setContent(html, { waitUntil: 'networkidle0' })
+
+ // 等待字体加载
+ await page.evaluate(() => document.fonts.ready)
+
+ // 额外等一会儿让渲染稳定
+ await new Promise(r => setTimeout(r, 500))
+
+ const outputPath = join(OUTPUT_DIR, `${name}.${format}`)
+
+ await page.screenshot({
+ path: outputPath,
+ type: format,
+ clip: { x: 0, y: 0, width, height },
+ quality: format !== 'png' ? quality : undefined,
+ })
+
+ return outputPath
+ } finally {
+ await browser.close()
+ }
+}
+
+
+/**
+ * 批量生成多页(轮播图)
+ * @param {string[]} pages - 每页的 HTML
+ * @param {object} opts
+ * @returns {Promise} 输出文件路径数组
+ */
+export async function renderCarousel(pages, opts = {}) {
+ const results = []
+ for (let i = 0; i < pages.length; i++) {
+ const name = opts.baseName
+ ? `${opts.baseName}_p${i + 1}`
+ : `carousel_${Date.now()}_p${i + 1}`
+ const path = await renderToImage(pages[i], { ...opts, name })
+ results.push(path)
+ }
+ return results
+}
diff --git a/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/server.js b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/server.js
new file mode 100644
index 0000000..81fc1da
--- /dev/null
+++ b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/server.js
@@ -0,0 +1,65 @@
+/**
+ * 铸渊封面工作室 · Web 服务 · v2.1
+ * 公开页面 + 模板列表 API + 生成 API + 意图链 + 下载端点
+ */
+
+import express from 'express'
+import cors from 'cors'
+import { generate, listTemplates } from './generate.js'
+import { join, dirname } from 'path'
+import { fileURLToPath } from 'url'
+import { existsSync, mkdirSync } from 'fs'
+
+const __dirname = dirname(fileURLToPath(import.meta.url))
+const OUTPUT_DIR = join(__dirname, 'output')
+const PUBLIC_DIR = join(__dirname, 'public')
+const PORT = process.env.PORT || 3912
+
+if (!existsSync(OUTPUT_DIR)) mkdirSync(OUTPUT_DIR, { recursive: true })
+
+const app = express()
+app.use(cors())
+app.use(express.json({ limit: '1mb' }))
+app.use('/output', express.static(OUTPUT_DIR))
+app.use(express.static(PUBLIC_DIR))
+
+app.get('/api/templates', (req, res) => {
+ try { res.json(listTemplates()) }
+ catch (err) { res.status(500).json({ error: '无法加载模板列表' }) }
+})
+
+app.post('/api/generate', async (req, res) => {
+ try {
+ const { templateId, presetId = '', title = '', body = '', subtitle = '', tag = '', layout = 'default' } = req.body
+ if (!title && !body) return res.status(400).json({ error: '请至少提供标题或正文内容' })
+
+ const result = await generate({ templateId, presetId, title, body, subtitle, tag, layout })
+ const filename = result.files[0].split('/').pop()
+ const url = `/output/${filename}`
+
+ res.json({
+ success: true,
+ url, filename,
+ templateId: result.templateId,
+ templateName: result.templateName,
+ presetId: result.presetId,
+ layout: result.layout,
+ intent_chain: result.intent_chain,
+ })
+ } catch (err) {
+ console.error('生成失败:', err.message)
+ res.status(500).json({ error: err.message || '生成失败' })
+ }
+})
+
+app.get('/api/health', (req, res) => {
+ res.json({ status: 'ok', timestamp: new Date().toISOString() })
+})
+
+app.get('*', (req, res) => {
+ res.sendFile(join(PUBLIC_DIR, 'index.html'))
+})
+
+app.listen(PORT, () => {
+ console.log(`🎨 铸渊封面工作室 v2.1 运行在 http://localhost:${PORT}`)
+})
diff --git a/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/templates/dynamic.js b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/templates/dynamic.js
new file mode 100644
index 0000000..e8c6846
--- /dev/null
+++ b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/templates/dynamic.js
@@ -0,0 +1,192 @@
+/**
+ * ═══════════════════════════════════════════════════
+ * 万能动态模板 · 自适应任何内容
+ * ═══════════════════════════════════════════════════
+ *
+ * 不是固定类型。是根据冰朔的文字内容,现场设计。
+ */
+
+import { STYLES } from '../config.js'
+
+
+/**
+ * 万能动态卡片
+ * @param {object} data
+ * @param {string} data.title - 提炼的标题
+ * @param {string} data.body - 正文
+ * @param {string[]} data.lines - 原文行数组
+ * @param {string} data.type - 内容类型(analyzeText 的结果)
+ */
+export function dynamicCard(data) {
+ const { title, body, lines = [], type = 'general' } = data
+ const C = STYLES.colors
+ const F = STYLES.fonts
+ const T = STYLES.typography
+ const W = 1080
+ const H = 1080
+
+ // 动态计算字号:内容越多字越小
+ const totalChars = (title + body).length
+ const titleSize = totalChars < 20 ? 52 : totalChars < 50 ? 44 : 36
+ const bodySize = totalChars < 50 ? 32 : totalChars < 150 ? 26 : 22
+
+ // 检测有没有列表
+ const hasList = lines.some(l => /^[-•·*]\s/.test(l.trim()))
+ // 检测是不是一句话
+ const isSingleLine = lines.length <= 2 && totalChars < 40
+
+ /* ── 生成内容 HTML ── */
+ let contentHtml = ''
+
+ if (isSingleLine) {
+ /* 一句话 · 大字居中 */
+ contentHtml = `
+ ${escapeHtml(title)}
+ `
+ } else if (hasList) {
+ /* 列表内容 */
+ const items = lines
+ .filter(l => /^[-•·*]\s/.test(l.trim()))
+ .map(l => `${escapeHtml(l.replace(/^[-•·*]\s*/, ''))}`)
+ .join('\n')
+ contentHtml = `
+ ${escapeHtml(title)}
+
+ `
+ } else {
+ /* 一般段落 */
+ const paragraphs = lines
+ .filter(Boolean)
+ .map(l => `${escapeHtml(l)}
`)
+ .join('\n')
+ contentHtml = `
+ ${escapeHtml(title)}
+ ${paragraphs}
+ `
+ }
+
+ return `
+
+
+
+
+
+
+
+
+
+
+
+ ${contentHtml}
+
+
+${STYLES.brand.text}
+
+
+`
+}
+
+
+function escapeHtml(str) {
+ return String(str)
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+}
diff --git a/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/templates/jike.js b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/templates/jike.js
new file mode 100644
index 0000000..9d6832e
--- /dev/null
+++ b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/templates/jike.js
@@ -0,0 +1,190 @@
+/**
+ * ═══════════════════════════════════════════════════
+ * 即刻卡片模板 · 1080×1080 (1:1)
+ * ═══════════════════════════════════════════════════
+ */
+
+import { STYLES } from '../config.js'
+
+const W = STYLES.sizes.jike.width
+const H = STYLES.sizes.jike.height
+
+
+/**
+ * 生成即刻卡片 HTML
+ * @param {object} data
+ * @param {string} data.title - 标题/核心观点
+ * @param {string} data.body - 正文
+ * @param {string} data.meta - 元信息(如「2026-05 · 思考碎片」)
+ * @param {string} data.emoji - 封面 emoji(可选)
+ * @param {'default'|'quote'|'minimal'} data.layout - 布局
+ */
+export function jikeCard(data) {
+ const {
+ title = '',
+ body = '',
+ meta = '',
+ emoji = '',
+ layout = 'default',
+ } = data
+
+ const C = STYLES.colors
+ const F = STYLES.fonts
+ const T = STYLES.typography
+
+ const bodyLines = body.split('\n').filter(Boolean)
+ const bodyHtml = bodyLines.map(line => `${line}
`).join('\n')
+
+ return `
+
+
+
+
+
+
+
+
+
+
+
+ ${meta ? `
${meta}
` : ''}
+
+ ${layout === 'quote' ? `
+
+ ${emoji ? `
${emoji}
` : ''}
+
+
${title}
+ ${body ? `
${body}
` : ''}
+
+ ` : `
+ ${emoji ? `
${emoji}
` : ''}
+
${title}
+
${bodyHtml}
+ `}
+
+
+
+
+
+
+`
+}
diff --git a/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/templates/poster.js b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/templates/poster.js
new file mode 100644
index 0000000..cf00d76
--- /dev/null
+++ b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/templates/poster.js
@@ -0,0 +1,316 @@
+/**
+ * ═══════════════════════════════════════════════════
+ * 海报模板 · 1080×1920 (9:16)
+ * ═══════════════════════════════════════════════════
+ *
+ * 适用于:放假通知、收稿海报、活动公告等
+ */
+
+import { STYLES } from '../config.js'
+
+const W = STYLES.sizes.poster.width
+const H = STYLES.sizes.poster.height
+
+
+/**
+ * 生成海报 HTML
+ * @param {object} data
+ * @param {string} data.title - 大标题
+ * @param {string} data.subtitle - 副标题(可选)
+ * @param {string} data.body - 正文内容
+ * @param {string[]} data.details - 详情列表(如时间/地点/要求)
+ * @param {string} data.cta - 行动号召(如「欢迎投稿」)
+ * @param {string} data.footer - 底部信息(如主办方/联系方式)
+ * @param {string} data.tag - 角标标签(如「通知」)
+ * @param {'default'|'notice'|'recruit'} data.style - 海报风格
+ */
+export function posterCard(data) {
+ const {
+ title = '',
+ subtitle = '',
+ body = '',
+ details = [],
+ cta = '',
+ footer = '',
+ tag = '',
+ style = 'default',
+ } = data
+
+ const C = STYLES.colors
+ const F = STYLES.fonts
+ const T = STYLES.typography
+
+ // 详情列表渲染
+ const detailsHtml = details.map((d, i) => {
+ // 支持 key: value 格式
+ const colonIdx = d.indexOf(':')
+ if (colonIdx > 0 && colonIdx < 20) {
+ const key = d.slice(0, colonIdx)
+ const val = d.slice(colonIdx + 1)
+ return `
+
+ ${key}
+ :
+ ${val}
+
`
+ }
+ return `${d}
`
+ }).join('\n')
+
+ return `
+
+
+
+
+
+
+
+
+
+
+
+
+ ${style === 'notice' ? `
+ ${tag ? `
${tag}
` : ''}
+
${title}
+ ${subtitle ? `
${subtitle}
` : ''}
+
+ ` : style === 'recruit' ? `
+
${tag || '征稿启事'}
+
${title}
+ ${subtitle ? `
${subtitle}
` : ''}
+ ` : `
+ ${tag ? `
${tag}
` : ''}
+
+
${title}
+ ${subtitle ? `
${subtitle}
` : ''}
+
+
+ `}
+
+ ${body ? `
` : ''}
+
+ ${details.length > 0 ? `
${detailsHtml}
` : ''}
+
+
+
+ ${cta ? `
+
+ ` : ''}
+
+
+
+
+
+
+`
+}
diff --git a/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/templates/registry.js b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/templates/registry.js
new file mode 100644
index 0000000..73a7124
--- /dev/null
+++ b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/templates/registry.js
@@ -0,0 +1,163 @@
+/**
+ * ═══════════════════════════════════════════════════
+ * 铸渊封面工作室 · 模板注册表
+ * ═══════════════════════════════════════════════════
+ *
+ * 所有模板在此注册。新增模板只需:
+ * 1. 在 templates/ 下创建模板文件
+ * 2. 在此文件 import + 注册
+ * 3. 核心代码零改动
+ */
+
+import { xiaohongshuCard } from './xiaohongshu.js'
+
+/**
+ * 模板注册表
+ */
+export const TEMPLATE_REGISTRY = {
+
+ xiaohongshu: {
+ id: 'xiaohongshu',
+ name: '小红书封面',
+ description: '1080×1440 · 3:4 竖版 · 适合小红书笔记封面',
+ icon: '📕',
+ sizes: { width: 1080, height: 1440 },
+ category: '封面',
+ isDefault: true,
+ instructions: '输入标题和正文,自动排版生成小红书封面。支持金句、干货列表、教程等多种布局。',
+ features: ['智能排版', '金句模式', '干货列表', '多页轮播'],
+ presets: [
+ {
+ id: 'tech',
+ name: '科技蓝',
+ cssVars: {
+ '--bg': '#0a0e27',
+ '--primary': '#e0e6ff',
+ '--accent': '#4f8cff',
+ '--highlight': '#00d4ff',
+ '--gold': '#ffb347',
+ '--warm': '#1a1f3a',
+ '--text': '#c8d6e5',
+ '--textMuted': '#6b7d99',
+ '--cardBg': '#121838',
+ '--border': '#1e2a4a',
+ },
+ },
+ {
+ id: 'warm',
+ name: '奶油暖调',
+ cssVars: {
+ '--bg': '#fef7f0',
+ '--primary': '#5d4037',
+ '--accent': '#e07a5f',
+ '--highlight': '#d4a373',
+ '--gold': '#c9a96e',
+ '--warm': '#fae1dd',
+ '--text': '#3e2723',
+ '--textMuted': '#8d6e63',
+ '--cardBg': '#ffffff',
+ '--border': '#f0e6d3',
+ },
+ },
+ {
+ id: 'minimal',
+ name: '极简黑白',
+ cssVars: {
+ '--bg': '#ffffff',
+ '--primary': '#111111',
+ '--accent': '#333333',
+ '--highlight': '#ff4757',
+ '--gold': '#999999',
+ '--warm': '#f5f5f5',
+ '--text': '#222222',
+ '--textMuted': '#888888',
+ '--cardBg': '#fafafa',
+ '--border': '#e0e0e0',
+ },
+ },
+ {
+ id: 'rose',
+ name: '玫瑰粉调',
+ cssVars: {
+ '--bg': '#fdf2f8',
+ '--primary': '#4a1942',
+ '--accent': '#9d4e8d',
+ '--highlight': '#e8435e',
+ '--gold': '#c9a96e',
+ '--warm': '#fce4ec',
+ '--text': '#311b2e',
+ '--textMuted': '#8e6290',
+ '--cardBg': '#ffffff',
+ '--border': '#f0d4e0',
+ },
+ },
+ {
+ id: 'green',
+ name: '文艺绿植',
+ cssVars: {
+ '--bg': '#f5f9f2',
+ '--primary': '#1b3a1b',
+ '--accent': '#5a8f5a',
+ '--highlight': '#c78b5c',
+ '--gold': '#b8a06e',
+ '--warm': '#e8f0e0',
+ '--text': '#2d3e2f',
+ '--textMuted': '#6b8b6b',
+ '--cardBg': '#ffffff',
+ '--border': '#d4e4cc',
+ },
+ },
+ ],
+ render: xiaohongshuCard,
+ },
+
+ /* ── 后续接入(模板文件待创建时取消注释即可)── */
+ // poster: { ... },
+ // jike: { ... },
+ // dynamic: { ... },
+}
+
+
+/** 获取所有可用模板列表 */
+export function listTemplates() {
+ return Object.values(TEMPLATE_REGISTRY).map(t => ({
+ id: t.id,
+ name: t.name,
+ description: t.description,
+ icon: t.icon,
+ sizes: t.sizes,
+ category: t.category,
+ features: t.features,
+ instructions: t.instructions,
+ presets: t.presets.map(p => ({ id: p.id, name: p.name })),
+ isDefault: t.isDefault,
+ }))
+}
+
+/** 获取单个模板 */
+export function getTemplate(id) {
+ return TEMPLATE_REGISTRY[id] || null
+}
+
+/** 获取默认模板 */
+export function getDefaultTemplate() {
+ return Object.values(TEMPLATE_REGISTRY).find(t => t.isDefault)
+ || Object.values(TEMPLATE_REGISTRY)[0]
+}
+
+/** 根据模板+预设+内容,准备渲染数据 */
+export function prepareRenderData(templateId, presetId, contentData) {
+ const template = getTemplate(templateId)
+ if (!template) return null
+
+ const preset = template.presets.find(p => p.id === presetId)
+ || template.presets[0]
+
+ return {
+ ...contentData,
+ templateId,
+ presetId: preset?.id || 'default',
+ cssVars: preset?.cssVars || {},
+ sizes: template.sizes,
+ }
+}
diff --git a/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/templates/xiaohongshu.js b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/templates/xiaohongshu.js
new file mode 100644
index 0000000..147c1d1
--- /dev/null
+++ b/code/bingshuo/open-modules/image-generator/xiaohongshu-cover/templates/xiaohongshu.js
@@ -0,0 +1,216 @@
+/**
+ * 小红书封面 v5 · 圆角卡片 + 卡通插画风格
+ * 奶白底色 + 大圆角 + 简洁层级
+ */
+export function xiaohongshuCard(renderData) {
+ const { title='', body='', subtitle='', tag='', cssVars={} } = renderData;
+ const v = cssVars || {};
+ const getv = (k) => v[k] || v['--'+k] || v['--'+k.replace(/([A-Z])/g,'-$1').toLowerCase()];
+
+ // 配色:奶白底 + 深绿/深棕文字 + 点缀色
+ const bg = getv('bg') || '#FDF8F3'; // 奶白底色
+ const cardBg = getv('cardBg') || '#FFFFFF'; // 纯白卡片
+ const primary = getv('primary') || '#2D5016'; // 深墨绿
+ const accent = getv('accent') || '#E07B39'; // 焦糖橙
+ const blue = getv('blue') || '#4A90A4'; // 雾蓝
+ const green = getv('green') || '#5B8C5A'; // 苔藓绿
+ const muted = getv('muted') || '#8B8680'; // 灰棕
+ const textDark = '#1A1A1A';
+
+ const titleText = title || '零手搓 AI 封面生成器';
+ const subLines = (subtitle || '不用GPU|纯代码渲染|开源免费').split('|').filter(Boolean);
+ const techStack = (tag || 'Puppeteer,Node.js,Express').split(',').map(s=>s.trim()).filter(Boolean);
+
+ // 卡通场景 SVG - 程序员在小桌子前工作
+ const cartoonSVG = ``;
+
+ return `
+
+
+
+
+
+
+
+ 🤖 AI辅助开发
+ 💰 开源免费
+
+
+
+
+
+
${titleText.replace(/(.{4,6})(.+)/, '$1$2')}
+
+
+
+ ${subLines.map(s=>`${s}`).join('')}
+
+
+
+
+
+
+
+
${techStack.length}项
+
开源技术栈
+
+
+
+
+
+ 🛠 技术栈
+ ${techStack.slice(0,5).map(t=>`${t}`).join('')}
+
+
+
+
+`;
+}
diff --git a/image-studio/renderer.js b/image-studio/renderer.js
index f70755b..f8b6a19 100644
--- a/image-studio/renderer.js
+++ b/image-studio/renderer.js
@@ -37,7 +37,7 @@ export async function renderToImage(html, opts = {}) {
const browser = await puppeteer.launch({
headless: true,
executablePath: '/usr/bin/google-chrome',
- args: ['--no-sandbox', '--disable-setuid-sandbox'],
+ args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-features=TranslateUI', '--font-render-hinting=none'],
})
try {
diff --git a/image-studio/templates/xiaohongshu.js b/image-studio/templates/xiaohongshu.js
index b976ea9..147c1d1 100644
--- a/image-studio/templates/xiaohongshu.js
+++ b/image-studio/templates/xiaohongshu.js
@@ -1,370 +1,216 @@
/**
- * ═══════════════════════════════════════════════════
- * 小红书卡片模板 · 2026审美版 · 1080×1440 (3:4)
- * ═══════════════════════════════════════════════════
- *
- * 风格:极简大标题 + 高对比度 + 大面积留白 + 渐变色块
- * 支持 CSS 变量驱动配色(从 registry 预设传入)
- *
- * 布局:
- * - default: 标题 + 正文列表(教程/干货)
- * - quote: 金句居中大字
- * - hero: 大标题 + 一句话副标题(封面)
+ * 小红书封面 v5 · 圆角卡片 + 卡通插画风格
+ * 奶白底色 + 大圆角 + 简洁层级
*/
+export function xiaohongshuCard(renderData) {
+ const { title='', body='', subtitle='', tag='', cssVars={} } = renderData;
+ const v = cssVars || {};
+ const getv = (k) => v[k] || v['--'+k] || v['--'+k.replace(/([A-Z])/g,'-$1').toLowerCase()];
-import { STYLES } from '../config.js'
+ // 配色:奶白底 + 深绿/深棕文字 + 点缀色
+ const bg = getv('bg') || '#FDF8F3'; // 奶白底色
+ const cardBg = getv('cardBg') || '#FFFFFF'; // 纯白卡片
+ const primary = getv('primary') || '#2D5016'; // 深墨绿
+ const accent = getv('accent') || '#E07B39'; // 焦糖橙
+ const blue = getv('blue') || '#4A90A4'; // 雾蓝
+ const green = getv('green') || '#5B8C5A'; // 苔藓绿
+ const muted = getv('muted') || '#8B8680'; // 灰棕
+ const textDark = '#1A1A1A';
-const W = 1080
-const H = 1440
+ const titleText = title || '零手搓 AI 封面生成器';
+ const subLines = (subtitle || '不用GPU|纯代码渲染|开源免费').split('|').filter(Boolean);
+ const techStack = (tag || 'Puppeteer,Node.js,Express').split(',').map(s=>s.trim()).filter(Boolean);
-/**
- * 将 CSS 变量对象转换为 :root 样式
- */
-function cssVarBlock(vars = {}) {
- const lines = Object.entries(vars).map(([k, v]) => ` ${k}: ${v};`)
- return `:root {\n${lines.join('\n')}\n }`
+ // 卡通场景 SVG - 程序员在小桌子前工作
+ const cartoonSVG = ``;
+
+ return `
- /* ── 主容器 ── */
- .container {
- position: relative;
- z-index: 1;
- height: 100%;
- display: flex;
- flex-direction: column;
- padding: 80px 72px 60px;
- }
+
+
+
- /* ── 顶部渐变装饰线 ── */
- .accent-line {
- width: 80px;
- height: 5px;
- background: linear-gradient(90deg, ${C.accent}, ${C.highlight});
- border-radius: 3px;
- margin-bottom: 40px;
- }
-
- /* ── 标签 ── */
- .tag {
- display: inline-block;
- padding: 10px 24px;
- background: ${C.cardBg};
- color: ${C.accent};
- font-size: 18px;
- font-weight: 700;
- border-radius: 12px;
- margin-bottom: 28px;
- align-self: flex-start;
- letter-spacing: 3px;
- border: 2px solid ${C.accent}22;
- }
-
- /* ── 大标题 · 2026审美核心 ── */
- .title {
- font-family: 'Noto Serif SC', 'Source Han Serif SC', serif;
- font-size: 68px;
- font-weight: 900;
- line-height: 1.25;
- color: ${C.primary};
- margin-bottom: 20px;
- letter-spacing: 2px;
- }
-
- .subtitle {
- font-size: 28px;
- font-weight: 400;
- color: ${C.textMuted};
- margin-bottom: 40px;
- letter-spacing: 1px;
- line-height: 1.5;
- opacity: 0.8;
- }
-
- /* ── 正文 ── */
- .body {
- flex: 1;
- font-size: 26px;
- line-height: 1.8;
- color: ${C.text};
- }
-
- .body p {
- margin-bottom: 14px;
- }
-
- .body ul, .body ol {
- padding-left: 0;
- margin-bottom: 14px;
- list-style: none;
- }
-
- .body li {
- font-size: 26px;
- line-height: 1.8;
- margin-bottom: 10px;
- color: ${C.text};
- position: relative;
- padding-left: 36px;
- }
-
- .body li::before {
- content: "▸";
- position: absolute;
- left: 0;
- color: ${C.accent};
- font-size: 18px;
- top: 4px;
- font-weight: 700;
- }
-
- .sub-heading {
- font-family: 'Noto Serif SC', serif;
- font-size: 32px;
- font-weight: 700;
- color: ${C.accent};
- margin: 32px 0 16px 0;
- letter-spacing: 1px;
- }
-
- .body strong {
- color: ${C.highlight};
- font-weight: 700;
- }
-
- /* ── 金句布局 ── */
- .quote-wrapper {
- flex: 1;
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- text-align: center;
- padding: 0 60px;
- }
-
- .quote-icon {
- font-size: 100px;
- line-height: 1;
- opacity: 0.2;
- margin-bottom: 16px;
- }
-
- .quote-text {
- font-family: 'Noto Serif SC', serif;
- font-size: 52px;
- font-weight: 700;
- line-height: 1.5;
- color: ${C.primary};
- letter-spacing: 4px;
- }
-
- .quote-author {
- margin-top: 40px;
- font-size: 24px;
- color: ${C.textMuted};
- letter-spacing: 2px;
- }
-
- /* ── Hero 封面布局 ── */
- .hero-wrapper {
- flex: 1;
- display: flex;
- flex-direction: column;
- justify-content: center;
- }
-
- .hero-title {
- font-family: 'Noto Serif SC', serif;
- font-size: 80px;
- font-weight: 900;
- line-height: 1.2;
- color: ${C.primary};
- letter-spacing: 3px;
- }
-
- .hero-subtitle {
- font-size: 30px;
- color: ${C.textMuted};
- margin-top: 24px;
- letter-spacing: 2px;
- opacity: 0.8;
- }
-
- .hero-decorator {
- width: 120px;
- height: 4px;
- background: linear-gradient(90deg, ${C.accent}, ${C.highlight});
- margin: 32px 0;
- border-radius: 2px;
- }
-
- /* ── 底部 · 子域名水印 ── */
- .footer {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding-top: 24px;
- font-size: 16px;
- color: ${C.textMuted};
- opacity: 0.5;
- letter-spacing: 1px;
- }
-
- .footer-domain {
- font-family: 'SF Mono', 'Fira Code', monospace;
- font-size: 16px;
- color: ${C.accent};
- opacity: 0.6;
- letter-spacing: 1px;
- }
-
- .footer-dot {
- display: inline-block;
- width: 6px;
- height: 6px;
- background: ${C.accent};
- border-radius: 50%;
- margin: 0 8px;
- opacity: 0.4;
- }
-
-
-
-
-
-
-
-
-
- ${tag ? `
${tag}
` : ''}
-
- ${layout === 'quote' ? `
-
-
"
-
${title}
- ${subtitle ? `
—— ${subtitle}
` : ''}
+
+
+ 🤖 AI辅助开发
+ 💰 开源免费
- ` : layout === 'hero' ? `
-
-
${title}
-
- ${subtitle ? `
${subtitle}
` : ''}
-
- ` : `
-
-
${title}
- ${subtitle ? `
${subtitle}
` : ''}
-
- ${bodyHtml}
-
- `}
+
+
-
-
-`
-}
-
-
-export function xiaohongshuCarousel(data, pages) {
- return pages.map((body, i) => xiaohongshuCard({
- ...data,
- body,
- pageNum: i + 1,
- totalPages: pages.length,
- }))
+