
Ljg Infograph
- 6 installs
- 22 repo stars
- Updated February 26, 2026
- lijigang/ljg-infograph
Helps with ai & agent building tasks.
About
ljg-infograph is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- ljg-infograph
- AI & Agent Building
- AI-coding skill
Ljg Infograph by the numbers
- 6 all-time installs (skills.sh)
- Ranked #12,756 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/lijigang/ljg-infograph --skill ljg-infographAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 6 |
|---|---|
| repo stars | ★ 22 |
| Last updated | February 26, 2026 |
| Repository | lijigang/ljg-infograph ↗ |
What it does
Helps with ai & agent building tasks.
Files
约束
本 skill 输出为视觉文件(PNG),不适用 L0 中的 Org-mode、Denote 和 ASCII-only 规范。
Usage
<example> User: /ljg-infograph [一段文字或笔记内容] Assistant: [分析内容,生成信息图 PNG 到 ~/Downloads/] </example>
Instructions
步骤 1:读取资源
读取以下文件:
~/.claude/skills/ljg-infograph/assets/infograph_template.html- 确认截图脚本:
~/.claude/skills/ljg-infograph/assets/capture.js
步骤 2:内容分析与模块化
2.1 提取元信息
从用户输入中识别:
- 标题:文章/笔记标题,或从内容提炼(≤ 15 字)
- 副标题:一句话概括内容核心(≤ 30 字)
- 来源:作者或出处(用户提供则用,否则
李继刚)
2.2 选择主色调
整张图使用单一主色,根据内容主题选择:
| 主题 | accent | accent-light | accent-dark |
|---|---|---|---|
| 科技/AI | #2563EB | #EFF6FF | #1E3A5F |
| 方法论/教育 | #7C3AED | #F5F3FF | #3B1F6E |
| 商业/金融 | #0D9488 | #F0FDFA | #134E48 |
| 健康/生活 | #059669 | #ECFDF5 | #064E3B |
| 创意/设计 | #D97706 | #FFFBEB | #78350F |
| 通用默认 | #2563EB | #EFF6FF | #1E3A5F |
2.3 拆分为信息模块
将内容拆分为 5-8 个信息模块,每个模块包含:
- 模块标题:2-6 字,名词性短语
- 模块类型:从下表选择
- 模块内容:该模块的具体信息
| 类型 | 适用场景 | HTML 结构 |
|---|---|---|
(默认) | 要点列举 | ul.check-list > li |
mod-stat | 关键数字 | .stat-row > .stat > .stat-number + .stat-label |
mod-steps | 步骤流程 | .step-item > .step-num + .step-text |
mod-highlight | 核心洞察(全宽) | p.insight 居中大字 |
mod-dark | 结论/警告(全宽) | 深色底白字,内部可嵌任意元素 |
mod-table | 对比数据 | table.info-table > th + td |
mod-compare | A vs B | .compare-row > .compare-item |
禁止使用的模块:不要生成「关键词/标签」类模块(如 mod-tags)。论文关键词、书籍术语索引等属于信息密度极低的填充物,不应出现在信息图中。
2.4 布局规划
- 默认占 1 列,
mod-highlight/mod-dark设为span-2 - 模块不加
color-*class——全局只用一个 accent 色 - 相邻两列模块内容量尽量接近,保持视觉对齐
- 孤儿对齐:模板内置 JS 自动处理——若末尾单列模块落单,会自动展开为全宽。无需手动干预
步骤 3:生成 HTML
每个模块渲染为:
<div class="module {类型class} {span-2}">
<div class="module-title">模块标题</div>
<!-- 按类型填充 -->
</div>渲染模板,替换变量:
| 变量 | 内容 |
|---|---|
{{TITLE}} | 主标题 |
{{SUBTITLE}} | 副标题 |
{{ACCENT}} | 主色 |
{{ACCENT_LIGHT}} | 主色浅底 |
{{ACCENT_DARK}} | 主色深底 |
{{GRID_HTML}} | 所有模块 HTML |
{{SOURCE}} | 来源/署名 |
步骤 4:文件命名与截图
文件名从标题提取 {name}(中文直接用,去标点,≤ 20 字符)。
node ~/.claude/skills/ljg-infograph/assets/capture.js /tmp/ljg_infograph_{name}.html ~/Downloads/{name}.png 1080 800 fullpage步骤 5:交付
1. 报告文件路径 2. 执行 open ~/Downloads/
#!/usr/bin/env node
const path = require('path');
async function main() {
const args = process.argv.slice(2);
const htmlPath = args[0];
const outputPath = args[1];
const width = parseInt(args[2]) || 1200;
const height = parseInt(args[3]) || 1600;
const fullpage = args[4] === 'fullpage';
if (!htmlPath || !outputPath) {
console.error('Usage: node capture.js <html> <png> [width] [height] [fullpage]');
process.exit(1);
}
let chromium;
try {
chromium = require('playwright').chromium;
} catch {
console.error('Playwright not found. Run: npx playwright install chromium');
process.exit(1);
}
const browser = await chromium.launch();
const page = await browser.newPage();
await page.setViewportSize({ width, height: fullpage ? 800 : height });
const fileUrl = 'file://' + path.resolve(htmlPath);
await page.goto(fileUrl, { waitUntil: 'networkidle' });
await page.waitForTimeout(500);
if (fullpage) {
const bodyHeight = await page.evaluate(() => document.body.scrollHeight);
await page.setViewportSize({ width, height: bodyHeight });
await page.waitForTimeout(300);
await page.screenshot({
path: path.resolve(outputPath),
type: 'png',
clip: { x: 0, y: 0, width, height: bodyHeight }
});
} else {
await page.screenshot({
path: path.resolve(outputPath),
type: 'png',
clip: { x: 0, y: 0, width, height }
});
}
await browser.close();
console.log('OK: ' + path.resolve(outputPath));
}
main().catch(err => {
console.error(err.message);
process.exit(1);
});
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<style>
:root {
--accent: {{ACCENT}};
--accent-light: {{ACCENT_LIGHT}};
--accent-dark: {{ACCENT_DARK}};
--text: #111827;
--text-2: #4B5563;
--text-3: #9CA3AF;
--bg: #FFFFFF;
--card: #FFFFFF;
--border: #E5E7EB;
--canvas: #F3F4F6;
--font: -apple-system, 'PingFang SC', 'Hiragino Sans GB', system-ui, sans-serif;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: 1080px;
background: var(--canvas);
}
.infograph {
width: 1080px;
background: var(--canvas);
}
/* ── Header ── */
.header {
background: linear-gradient(135deg, var(--bg) 60%, var(--accent-light) 100%);
padding: 64px 72px 56px;
border-bottom: 3px solid var(--accent);
}
.header h1 {
font: 700 54px/1.25 var(--font);
color: var(--text);
letter-spacing: -0.02em;
margin-bottom: 14px;
}
.header .subtitle {
font: 400 22px/1.6 var(--font);
color: var(--text-2);
}
/* ── Grid ── */
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
padding: 24px 40px;
}
/* ── Module ── */
.module {
background: var(--card);
border-radius: 10px;
padding: 28px 28px 24px;
border: 1px solid var(--border);
border-left: 3px solid var(--accent);
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
}
.module.span-2 {
grid-column: span 2;
}
.module-title {
font: 600 15px/1 var(--font);
color: var(--accent);
text-transform: uppercase;
letter-spacing: 0.12em;
margin-bottom: 18px;
display: flex;
align-items: center;
gap: 10px;
}
.module-title::before {
content: '';
display: block;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--accent);
flex-shrink: 0;
}
/* ── Highlight Module ── */
.module.mod-highlight {
background: var(--accent-light);
border: 1px solid var(--accent-light);
border-left: 1px solid var(--accent-light);
text-align: center;
padding: 32px 48px;
}
.module.mod-highlight .insight {
font: 500 24px/1.65 var(--font);
color: var(--text);
}
/* ── Dark Module ── */
.module.mod-dark {
background: var(--accent-dark);
border-color: var(--accent-dark);
border-left: 3px solid var(--accent-dark);
}
.module.mod-dark .module-title {
color: rgba(255,255,255,0.5);
}
.module.mod-dark .module-title::before {
background: rgba(255,255,255,0.4);
}
.module.mod-dark p,
.module.mod-dark li,
.module.mod-dark .step-text,
.module.mod-dark .insight {
color: rgba(255,255,255,0.92);
}
.module.mod-dark strong {
color: #FFFFFF;
}
.module.mod-dark .step-num {
color: rgba(255,255,255,0.4);
}
.module.mod-dark .check-list li {
border-bottom-color: rgba(255,255,255,0.08);
}
.module.mod-dark .check-list li::before {
color: rgba(255,255,255,0.35);
}
/* ── Typography ── */
.module p {
font: 400 18px/1.7 var(--font);
color: var(--text-2);
margin-bottom: 8px;
}
.module p:last-child { margin-bottom: 0; }
.module strong {
font-weight: 600;
color: var(--text);
}
/* ── Stat ── */
.stat-row {
display: flex;
justify-content: space-around;
}
.stat {
display: flex;
flex-direction: column;
align-items: center;
padding: 8px 16px;
}
.stat-number {
font: 700 48px/1.1 var(--font);
color: var(--accent);
letter-spacing: -0.03em;
border-bottom: 3px solid var(--accent);
padding-bottom: 4px;
}
.stat-label {
font: 400 14px/1.5 var(--font);
color: var(--text-3);
margin-top: 6px;
}
/* ── Check List ── */
.check-list {
list-style: none;
}
.check-list li {
font: 400 18px/1.6 var(--font);
color: var(--text-2);
padding: 9px 0 9px 24px;
position: relative;
border-bottom: 1px solid var(--border);
}
.check-list li:last-child { border-bottom: none; }
.check-list li::before {
content: '—';
position: absolute;
left: 0;
color: var(--accent);
font-size: 14px;
}
.check-list li strong {
font-weight: 600;
color: var(--text);
}
/* ── Steps ── */
.step-item {
display: flex;
gap: 14px;
padding: 10px 0;
border-bottom: 1px solid var(--border);
align-items: flex-start;
}
.step-item:last-child { border-bottom: none; }
.step-num {
font: 600 13px/1 var(--font);
color: var(--accent);
min-width: 28px;
height: 28px;
border-radius: 50%;
background: var(--accent-light);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.step-text {
font: 400 18px/1.6 var(--font);
color: var(--text-2);
}
.step-text strong {
font-weight: 600;
color: var(--text);
}
/* ── Table ── */
.info-table {
width: 100%;
border-collapse: collapse;
font: 400 17px/1.5 var(--font);
}
.info-table th {
font-weight: 600;
color: var(--text);
text-align: left;
padding: 10px 12px;
border-bottom: 2px solid var(--text);
font-size: 15px;
}
.info-table td {
padding: 10px 12px;
color: var(--text-2);
border-bottom: 1px solid var(--border);
}
.info-table tr:last-child td { border-bottom: none; }
/* ── Tags ── */
.tag-row {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.tag {
display: inline-block;
font: 500 14px/1 var(--font);
padding: 7px 14px;
border-radius: 6px;
background: var(--canvas);
color: var(--text-2);
border: 1px solid var(--border);
}
/* ── Compare ── */
.compare-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.compare-item {
padding: 16px;
border-radius: 8px;
background: var(--canvas);
border-left: 3px solid var(--accent);
}
.compare-item .compare-label {
font: 600 16px/1.4 var(--font);
color: var(--text);
margin-bottom: 6px;
}
.compare-item p { font-size: 16px; }
/* ── Footer ── */
.footer {
display: flex;
align-items: center;
padding: 20px 40px 32px;
}
.footer .author {
display: flex;
align-items: center;
gap: 10px;
}
.footer .avatar {
width: 28px;
height: 28px;
border-radius: 50%;
object-fit: cover;
}
.footer .author-name {
font: 400 14px/1.5 var(--font);
color: var(--text-3);
}
</style>
</head>
<body>
<div class="infograph">
<div class="header">
<h1>{{TITLE}}</h1>
<p class="subtitle">{{SUBTITLE}}</p>
</div>
<div class="grid">
{{GRID_HTML}}
</div>
<div class="footer">
<div class="author">
<img class="avatar" src="file:///Users/lijigang/.claude/skills/ljg-infograph/assets/logo.png" alt="">
<span class="author-name">{{SOURCE}}</span>
</div>
</div>
</div>
<script>
(function(){
var grid = document.querySelector('.grid');
if (!grid) return;
var modules = Array.from(grid.children);
var col = 0;
modules.forEach(function(m){ col += m.classList.contains('span-2') ? 2 : 1; });
var last = modules[modules.length - 1];
if (col % 2 === 1 && last && !last.classList.contains('span-2')) {
last.classList.add('span-2');
}
})();
</script>
</body>
</html>