Files
hoelee-blog/scripts/og-gen/template-v2.1.html
T
hoelee a41448bb4a feat(og-gen): check in the v2 art pipeline and its batch renderer
The consultant-approved "one art, four targets" work existed only in the
working tree. Commit it so the 72-post rollout is reproducible from the repo
instead of from one session's scratch files.

- template-v2.html / v2.1 / v2.2: the iteration the consultant reviewed, plus
  the accepted frosted-bento layout (736x536 card, blueprint grid, full-width
  evidence bar, self-fitting headline script that reports "FIT <n>px").
- render_sample_suite{,_v2,_v3}.mjs + fit-title-probe.mjs: how the two sample
  posts were rendered for rounds 2-4 (kept for provenance).
- generate_prompts_catalog.py + prompts_catalog.json: the 72 per-post prompts
  and their visual metaphors, extracted from the posts themselves.
- render_from_art.mjs (new): the batch renderer. One zero-text base frame per
  post -> banner 1600x900 webp (+jpg fallback), social card 1200x630 (EN and
  zh-<slug>), thumbs 800x420 and 400x210. Cards are filled from frontmatter +
  art-meta.json; a missing evidence line or a headline that never settles is a
  hard failure, never a silently-degraded card.
- art-meta.json: the per-slug input contract. Two slugs wired; 70 still need
  base art (GPU) and a hand-written evidence line.
- README: documents the pipeline, the measured format choice (558 KB PNG ->
  84 KB mozjpeg q85) and the PostList thumb-helper trap when ogImage moves to
  .jpg.

Proof the renderer reproduces the accepted output: re-rendering the two sample
posts yields banner + 800px thumb files BYTE-IDENTICAL to the consultant's
delivery (sha256 16e6a7b0, f3b29f38, c4e28291, 8095ec6c), and all four cards
report FIT, 2-3 title lines, 2 description lines, 0 overflow in x and y.
2026-10-18 16:32:58 +08:00

341 lines
9.1 KiB
HTML

<!DOCTYPE html>
<html lang="{{LANG}}">
<head>
<meta charset="UTF-8">
<style>
:root {
--bg-base: #090d16;
--primary: #4d82ff;
--primary-bright: #6d8dff;
--accent-green: #3fb950;
--accent-red: #ff7b72;
--text-main: #f8fafc;
--text-sub: #cbd5e1;
--text-muted: #94a3b8;
--text-dim: #64748b;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: 1200px;
height: 630px;
overflow: hidden;
background: var(--bg-base);
font-family: "Space Grotesk", "Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
color: var(--text-main);
-webkit-font-smoothing: antialiased;
}
.canvas {
position: relative;
width: 1200px;
height: 630px;
display: flex;
align-items: center;
padding: 44px 52px;
overflow: hidden;
}
/* 1. 底层:将 AI 3D 底图适度放大 18% 并向中轴线微移,消除中间与右上角空洞 */
.bg-artwork {
position: absolute;
top: -9%;
right: -4%;
width: 116%;
height: 118%;
object-fit: cover;
object-position: right center;
z-index: 1;
}
/* 2. 中层 A:柔和过渡遮罩(告别死纯黑,保留底层微光与空间感) */
.scrim-overlay {
position: absolute;
inset: 0;
z-index: 2;
background:
radial-gradient(circle at 18% 22%, rgba(77, 130, 255, 0.14) 0%, transparent 48%),
radial-gradient(circle at 80% 50%, rgba(9, 13, 22, 0.0) 0%, rgba(9, 13, 22, 0.55) 58%, rgba(9, 13, 22, 0.90) 82%),
linear-gradient(90deg, rgba(9, 13, 22, 0.84) 0%, rgba(9, 13, 22, 0.78) 42%, rgba(9, 13, 22, 0.28) 68%, rgba(9, 13, 22, 0.05) 100%);
}
/* 3. 中层 B:CSS 编程级蓝图坐标网格(确保左侧绝不死黑单调) */
.blueprint-grid {
position: absolute;
inset: 0;
z-index: 2;
background-image:
linear-gradient(to right, rgba(109, 141, 255, 0.05) 1px, transparent 1px),
linear-gradient(to bottom, rgba(109, 141, 255, 0.05) 1px, transparent 1px);
background-size: 36px 36px;
mask-image: linear-gradient(90deg, rgba(0,0,0,0.9) 0%, rgba(0,0,0,0.5) 55%, transparent 85%);
-webkit-mask-image: linear-gradient(90deg, rgba(0,0,0,0.9) 0%, rgba(0,0,0,0.5) 55%, transparent 85%);
}
/* 外框蓝图装饰线与十字准星 */
.blueprint-accents {
position: absolute;
inset: 20px;
border: 1px solid rgba(109, 141, 255, 0.10);
border-radius: 16px;
pointer-events: none;
z-index: 3;
}
.crosshair {
position: absolute;
width: 15px;
height: 15px;
opacity: 0.45;
}
.crosshair::before, .crosshair::after {
content: "";
position: absolute;
background: var(--primary-bright);
}
.crosshair::before { top: 7px; left: 0; width: 15px; height: 1px; }
.crosshair::after { left: 7px; top: 0; width: 1px; height: 15px; }
.ch-tr { top: 22px; right: 26px; }
.ch-br { bottom: 22px; right: 26px; }
/* 4. 顶层:左侧半透明磨砂亚克力 Bento 容器卡 */
.bento-card {
position: relative;
z-index: 4;
width: 736px;
height: 536px;
padding: 38px 44px;
border-radius: 18px;
background: linear-gradient(145deg, rgba(21, 29, 46, 0.82) 0%, rgba(13, 18, 30, 0.74) 100%);
border: 1px solid rgba(109, 141, 255, 0.24);
box-shadow:
0 24px 48px rgba(0, 0, 0, 0.55),
inset 0 1px 0 rgba(255, 255, 255, 0.12);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
display: flex;
flex-direction: column;
justify-content: space-between;
}
/* Bento 卡片顶部蓝色微光装饰条 */
.bento-card::before {
content: "";
position: absolute;
top: -1px;
left: 44px;
width: 96px;
height: 2px;
background: linear-gradient(90deg, var(--primary-bright), transparent);
}
/* --- 第一段 (Top):分类徽章 + 核心技术标签 --- */
.top-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.badge-group {
display: flex;
align-items: center;
gap: 12px;
}
.cat-badge {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 14px;
border-radius: 999px;
background: rgba(77, 130, 255, 0.2);
border: 1px solid rgba(109, 141, 255, 0.5);
color: #a5c0ff;
font-family: "JetBrains Mono", "Fira Code", monospace;
font-size: 13px;
font-weight: 700;
letter-spacing: 1px;
text-transform: uppercase;
}
.cat-badge .dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--accent-green);
box-shadow: 0 0 8px var(--accent-green);
}
.top-tags {
display: flex;
align-items: center;
gap: 8px;
font-family: "JetBrains Mono", "Fira Code", monospace;
font-size: 13px;
}
.top-tags span {
background: rgba(15, 21, 35, 0.75);
border: 1px solid rgba(56, 72, 99, 0.7);
padding: 4px 10px;
border-radius: 6px;
color: var(--text-muted);
}
/* --- 第二段 (Center):大标题 + 2行摘要 + 100%等宽终端证据条 --- */
.center-block {
display: flex;
flex-direction: column;
gap: 16px;
margin: auto 0;
}
.title {
font-weight: 800;
font-size: {{TITLE_SIZE}}px;
line-height: 1.22;
letter-spacing: -0.02em;
color: #ffffff;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-shadow: 0 2px 16px rgba(0, 0, 0, 0.5);
}
/* 新增:2 行文章核心摘要,彻底消除纵向空洞 */
.desc {
font-size: 18px;
line-height: 1.5;
color: var(--text-sub);
font-weight: 400;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
/* 100% 撑满 Bento 容器的终端证据栏,消除右侧阶梯空缺 */
.evidence-strip {
display: flex;
align-items: center;
gap: 12px;
width: 100%;
margin-top: 4px;
padding: 13px 18px;
border-radius: 10px;
background: rgba(9, 13, 22, 0.85);
border: 1px solid rgba(77, 130, 255, 0.28);
border-left: 3px solid var(--primary-bright);
font-family: "JetBrains Mono", "Fira Code", monospace;
font-size: 15.5px;
line-height: 1.4;
color: #e2e8f0;
}
.evidence-strip .prompt {
color: var(--accent-green);
font-weight: 700;
}
.evidence-strip .text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* --- 第三段 (Bottom):个人品牌锁定 --- */
.bottom-row {
display: flex;
align-items: center;
justify-content: space-between;
padding-top: 18px;
border-top: 1px solid rgba(255, 255, 255, 0.09);
}
.author-lockup {
display: flex;
align-items: center;
gap: 14px;
}
.author-avatar {
width: 44px;
height: 44px;
border-radius: 50%;
object-fit: cover;
border: 2px solid var(--primary-bright);
}
.author-meta {
display: flex;
flex-direction: column;
gap: 2px;
}
.author-name {
font-size: 17px;
font-weight: 700;
color: #ffffff;
}
.author-name .handle {
font-weight: 500;
color: var(--text-muted);
font-size: 14px;
margin-left: 6px;
}
.author-role {
font-family: "JetBrains Mono", monospace;
font-size: 12px;
letter-spacing: 0.5px;
color: var(--primary-bright);
}
.domain-pill {
font-family: "JetBrains Mono", monospace;
font-size: 14px;
font-weight: 600;
color: #cbd5e1;
background: rgba(77, 130, 255, 0.12);
border: 1px solid rgba(109, 141, 255, 0.3);
padding: 6px 14px;
border-radius: 8px;
}
</style>
</head>
<body>
<div class="canvas">
<!-- 1. Ideogram 4 背景图(自动放大 18% 并居中偏右) -->
<img class="bg-artwork" src="{{BG_IMAGE_SRC}}" alt="">
<!-- 2. 柔和渐变遮罩 + 编程级蓝图网格 + 准星 -->
<div class="scrim-overlay"></div>
<div class="blueprint-grid"></div>
<div class="blueprint-accents">
<div class="crosshair ch-tr"></div>
<div class="crosshair ch-br"></div>
</div>
<!-- 3. 左侧半透明磨砂 Bento 容器 -->
<div class="bento-card">
<!-- Top -->
<div class="top-row">
<div class="badge-group">
<div class="cat-badge"><span class="dot"></span>{{CATEGORY}}</div>
<div class="top-tags">{{TAGS_HTML}}</div>
</div>
</div>
<!-- Center -->
<div class="center-block">
<h1 class="title">{{TITLE_HTML}}</h1>
<p class="desc">{{DESCRIPTION}}</p>
<div class="evidence-strip">
<span class="prompt">$</span>
<span class="text">{{EVIDENCE_SUMMARY}}</span>
</div>
</div>
<!-- Bottom -->
<div class="bottom-row">
<div class="author-lockup">
<img class="author-avatar" src="{{AVATAR_PATH}}" alt="Lee Teong Hoe">
<div class="author-meta">
<div class="author-name">Lee Teong Hoe <span class="handle">· Mr Hoelee</span></div>
<div class="author-role">BUILD · OPERATE · DOCUMENT</div>
</div>
</div>
<div class="domain-pill">blog.hoelee.com</div>
</div>
</div>
</div>
</body>
</html>