feat(article): 添加 GPT-5.4 深度解析文章及配套图表
- 新增 GPT-5.4 深度解析文章,涵盖六大核心能力详解 - 添加 SVG 格式的 GPT-5.4 能力全景图 - 添加 Mermaid 格式的模型家族关系图 - 添加 GPT-5.4 六大能力思维导图 - 添加 Computer Use 工作流程图 - 添加 OSWorld 桌面操作基准测试图表 - 添加 上下文窗口演进对比图 - 添加 上下文压缩原理图 - 添加 Tool Search 机制对比图 - 添加 可配置推理深度图 - 添加 GDPval 对比图表 - 添加 三方模型对比图 - 添加 API 定价对比图 - 添加 Mermaid 配置文件和样式文件 - 添加模型选择指南 SVG 图片
This commit is contained in:
@@ -0,0 +1,227 @@
|
||||
/**
|
||||
* 用 Playwright 渲染 Mermaid .mmd 文件为高质量 PNG
|
||||
* 用法: node render_mermaid.js <input.mmd> <output.png> [width]
|
||||
*/
|
||||
|
||||
const { chromium } = require('/Users/bing/node_modules/.pnpm/playwright@1.58.2/node_modules/playwright');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const mmdFile = process.argv[2];
|
||||
const outFile = process.argv[3];
|
||||
const width = parseInt(process.argv[4] || '2400', 10);
|
||||
|
||||
if (!mmdFile || !outFile) {
|
||||
console.error('Usage: node render_mermaid.js <input.mmd> <output.png> [width]');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const mmdContent = fs.readFileSync(mmdFile, 'utf-8');
|
||||
|
||||
// 检测图表类型
|
||||
const isMindmap = mmdContent.trim().startsWith('mindmap');
|
||||
const isXYChart = mmdContent.includes('xychart-beta');
|
||||
|
||||
const html = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 40px;
|
||||
background: #080c18;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
#diagram {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* ===== 全局文字 ===== */
|
||||
.mermaid text, .mermaid tspan, .mermaid span, .mermaid p {
|
||||
font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif !important;
|
||||
}
|
||||
|
||||
/* ===== 流程图节点文字强制白色 ===== */
|
||||
.mermaid .node .label,
|
||||
.mermaid .node .label span,
|
||||
.mermaid .node .label p,
|
||||
.mermaid .nodeLabel,
|
||||
.mermaid .edgeLabel span,
|
||||
.mermaid .edgeLabel p,
|
||||
.mermaid .cluster-label span,
|
||||
.mermaid .cluster-label p {
|
||||
color: #e0f7fa !important;
|
||||
}
|
||||
.mermaid .node text, .mermaid .node tspan {
|
||||
fill: #e0f7fa !important;
|
||||
}
|
||||
|
||||
/* ===== 流程图节点发光 ===== */
|
||||
.mermaid .node rect, .mermaid .node polygon, .mermaid .node circle {
|
||||
filter: drop-shadow(0 0 6px rgba(0, 229, 255, 0.4));
|
||||
rx: 8;
|
||||
ry: 8;
|
||||
}
|
||||
|
||||
/* ===== 流程图边线 ===== */
|
||||
.mermaid .edge-pattern-solid, .mermaid .flowchart-link {
|
||||
stroke: #00b8d4 !important;
|
||||
stroke-width: 2px !important;
|
||||
}
|
||||
.mermaid marker path {
|
||||
fill: #00e5ff !important;
|
||||
}
|
||||
|
||||
/* ===== Cluster/subgraph ===== */
|
||||
.mermaid .cluster rect {
|
||||
stroke: #1a6a8a !important;
|
||||
stroke-width: 2px !important;
|
||||
stroke-dasharray: 6 3 !important;
|
||||
fill: rgba(10, 26, 46, 0.7) !important;
|
||||
rx: 12 !important;
|
||||
ry: 12 !important;
|
||||
}
|
||||
.mermaid .cluster-label text, .mermaid .cluster-label tspan {
|
||||
fill: #4dd0e1 !important;
|
||||
}
|
||||
.mermaid .cluster-label span, .mermaid .cluster-label p {
|
||||
color: #4dd0e1 !important;
|
||||
}
|
||||
|
||||
/* ===== Edge labels ===== */
|
||||
.mermaid .edgeLabel rect {
|
||||
fill: #0a1628 !important;
|
||||
opacity: 0.9 !important;
|
||||
}
|
||||
.mermaid .edgeLabel text, .mermaid .edgeLabel tspan {
|
||||
fill: #80deea !important;
|
||||
}
|
||||
|
||||
/* ===== Mindmap 修复黑块 ===== */
|
||||
.mermaid .mindmap-node rect,
|
||||
.mermaid .mindmap-node polygon,
|
||||
.mermaid .mindmap-node circle,
|
||||
.mermaid .mindmap-node ellipse,
|
||||
.mermaid .mindmap-node path {
|
||||
stroke: #00b8d4 !important;
|
||||
stroke-width: 2px !important;
|
||||
filter: drop-shadow(0 0 6px rgba(0, 229, 255, 0.35)) !important;
|
||||
}
|
||||
.mermaid .mindmap-node text, .mermaid .mindmap-node tspan {
|
||||
fill: #e0f7fa !important;
|
||||
}
|
||||
/* Mindmap 分区颜色 */
|
||||
.mermaid .section-0 rect, .mermaid .section-0 path { fill: #0d3b66 !important; stroke: #00e5ff !important; }
|
||||
.mermaid .section-1 rect, .mermaid .section-1 path { fill: #1a3a4a !important; stroke: #26c6da !important; }
|
||||
.mermaid .section-2 rect, .mermaid .section-2 path { fill: #1b3044 !important; stroke: #4dd0e1 !important; }
|
||||
.mermaid .section-3 rect, .mermaid .section-3 path { fill: #14293d !important; stroke: #00bcd4 !important; }
|
||||
.mermaid .section-4 rect, .mermaid .section-4 path { fill: #0f2b3d !important; stroke: #80deea !important; }
|
||||
.mermaid .section-5 rect, .mermaid .section-5 path { fill: #0d3352 !important; stroke: #4fc3f7 !important; }
|
||||
.mermaid .section-6 rect, .mermaid .section-6 path { fill: #102a40 !important; stroke: #29b6f6 !important; }
|
||||
.mermaid .section-7 rect, .mermaid .section-7 path { fill: #0e2d44 !important; stroke: #81d4fa !important; }
|
||||
/* 根节点 */
|
||||
.mermaid .mindmap-node:first-of-type circle {
|
||||
fill: #0d2137 !important;
|
||||
stroke: #00e5ff !important;
|
||||
stroke-width: 3px !important;
|
||||
filter: drop-shadow(0 0 12px rgba(0, 229, 255, 0.6)) !important;
|
||||
}
|
||||
/* 黑色回退 */
|
||||
.mermaid rect[fill="#000"], .mermaid rect[fill="#000000"], .mermaid rect[fill="black"],
|
||||
.mermaid path[fill="#000"], .mermaid path[fill="#000000"], .mermaid path[fill="black"] {
|
||||
fill: #0d3b66 !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="diagram">
|
||||
<pre class="mermaid">
|
||||
${mmdContent}
|
||||
</pre>
|
||||
</div>
|
||||
<script>
|
||||
mermaid.initialize({
|
||||
startOnLoad: true,
|
||||
theme: 'base',
|
||||
themeVariables: {
|
||||
primaryColor: '#0d2137',
|
||||
primaryTextColor: '#e0f7fa',
|
||||
primaryBorderColor: '#00e5ff',
|
||||
lineColor: '#00b8d4',
|
||||
secondaryColor: '#1a3a4a',
|
||||
secondaryTextColor: '#e0f7fa',
|
||||
secondaryBorderColor: '#00bcd4',
|
||||
tertiaryColor: '#112240',
|
||||
tertiaryTextColor: '#e0f7fa',
|
||||
tertiaryBorderColor: '#26c6da',
|
||||
noteBkgColor: '#0d2137',
|
||||
noteTextColor: '#e0f7fa',
|
||||
noteBorderColor: '#00e5ff',
|
||||
edgeLabelBackground: '#0a1628',
|
||||
clusterBkg: '#0a1a2e',
|
||||
clusterBorder: '#1a5276',
|
||||
titleColor: '#00e5ff',
|
||||
background: '#080c18',
|
||||
mainBkg: '#0d2137',
|
||||
nodeBorder: '#00e5ff',
|
||||
nodeTextColor: '#e0f7fa',
|
||||
fontFamily: 'SF Pro Display, -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica Neue, Arial, sans-serif',
|
||||
fontSize: '16px',
|
||||
xyChart: {
|
||||
backgroundColor: '#080c18',
|
||||
titleColor: '#00e5ff',
|
||||
xAxisLabelColor: '#e0f7fa',
|
||||
yAxisLabelColor: '#e0f7fa',
|
||||
xAxisTitleColor: '#00e5ff',
|
||||
yAxisTitleColor: '#00e5ff',
|
||||
xAxisTickColor: '#00b8d4',
|
||||
yAxisTickColor: '#00b8d4',
|
||||
xAxisLineColor: '#00b8d4',
|
||||
yAxisLineColor: '#00b8d4',
|
||||
plotColorPalette: '#00e5ff,#4dd0e1,#26c6da,#00bcd4,#80deea,#b2ebf2'
|
||||
}
|
||||
},
|
||||
mindmap: { useMaxWidth: false },
|
||||
flowchart: { useMaxWidth: false, htmlLabels: true }
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch();
|
||||
const page = await browser.newPage({
|
||||
viewport: { width: width, height: 1600 },
|
||||
deviceScaleFactor: 2
|
||||
});
|
||||
|
||||
await page.setContent(html, { waitUntil: 'networkidle' });
|
||||
await page.waitForSelector('.mermaid svg', { timeout: 15000 });
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
const box = await page.locator('#diagram').boundingBox();
|
||||
if (!box) {
|
||||
console.error('Failed to locate diagram');
|
||||
await browser.close();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const padding = 40;
|
||||
await page.screenshot({
|
||||
path: outFile,
|
||||
clip: {
|
||||
x: Math.max(0, box.x - padding),
|
||||
y: Math.max(0, box.y - padding),
|
||||
width: box.width + padding * 2,
|
||||
height: box.height + padding * 2
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`OK: ${outFile} (${Math.round(box.width)}x${Math.round(box.height)})`);
|
||||
await browser.close();
|
||||
})();
|
||||
@@ -0,0 +1,53 @@
|
||||
"""上传文章 003 的所有图片到七牛云 OSS"""
|
||||
|
||||
import os
|
||||
import glob
|
||||
from qiniu import Auth, put_file
|
||||
|
||||
ACCESS_KEY = 't1PIPGcvBY9lJVXFZFb48maTQsGGhvLsR5QQlNq0'
|
||||
SECRET_KEY = 'KGooFdF5eCLdCIMCOD6x5ofMzu4vYE17T5Mvp9qC'
|
||||
BUCKET_NAME = 'union-saas'
|
||||
CDN_DOMAIN = 'https://cdn.union.jxyunge.com'
|
||||
UPLOAD_PREFIX = 'self-media/003/'
|
||||
|
||||
|
||||
def upload_file(local_path, key):
|
||||
"""上传单个文件到七牛"""
|
||||
q = Auth(ACCESS_KEY, SECRET_KEY)
|
||||
token = q.upload_token(BUCKET_NAME, key, 3600)
|
||||
ret, info = put_file(token, key, local_path, version='v2')
|
||||
if info.status_code == 200:
|
||||
url = f'{CDN_DOMAIN}/{key}'
|
||||
print(f' OK {os.path.basename(local_path)} -> {url}')
|
||||
return url
|
||||
else:
|
||||
print(f' FAIL {os.path.basename(local_path)}: {info}')
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
img_dir = os.path.join(os.path.dirname(__file__), '..', 'articles', '003')
|
||||
|
||||
# 收集所有图片文件(PNG + SVG)
|
||||
files = sorted(
|
||||
glob.glob(os.path.join(img_dir, '*.png'))
|
||||
+ glob.glob(os.path.join(img_dir, '*.svg'))
|
||||
)
|
||||
|
||||
results = {}
|
||||
for f in files:
|
||||
name = os.path.basename(f)
|
||||
key = UPLOAD_PREFIX + name
|
||||
url = upload_file(f, key)
|
||||
if url:
|
||||
results[name] = url
|
||||
|
||||
print(f'\n===== 上传完成: {len(results)}/{len(files)} =====')
|
||||
for name, url in results.items():
|
||||
print(f'{name}: {url}')
|
||||
|
||||
return results
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user