Files
hoelee-blog/scripts/og-gen/template-v2.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

298 lines
7.6 KiB
HTML

<!DOCTYPE html>
<html lang="{{LANG}}">
<head>
<meta charset="UTF-8">
<style>
:root {
--bg-base: #0b0f19;
--primary: #4d82ff;
--primary-bright: #6d8dff;
--accent-green: #3fb950;
--accent-red: #ff7b72;
--accent-amber: #fbbf24;
--text-main: #f3f4f6;
--text-muted: #9ca3af;
--text-dim: #6b7c99;
--border-glass: rgba(109, 141, 255, 0.22);
}
* { 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;
flex-direction: column;
justify-content: space-between;
padding: 52px 64px;
}
/* 1. 底层:Ideogram 4 生成的单篇专属背景底图 */
.bg-artwork {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
object-position: right center;
z-index: 1;
}
/* 2. 中层:强制暗色渐变遮罩 (Programmatic Scrim) + 蓝图网格装饰 */
.scrim-overlay {
position: absolute;
inset: 0;
z-index: 2;
background:
radial-gradient(circle at 82% 50%, rgba(11, 15, 25, 0.05) 0%, rgba(11, 15, 25, 0.65) 55%, rgba(11, 15, 25, 0.96) 78%),
linear-gradient(90deg, #0b0f19 0%, #0b0f19 38%, rgba(11, 15, 25, 0.94) 54%, rgba(11, 15, 25, 0.35) 74%, rgba(11, 15, 25, 0.08) 100%);
}
/* 延续 sample-new-default-og.jpg 的微弱蓝图几何线与十字准星 */
.blueprint-accents {
position: absolute;
inset: 24px;
border: 1px solid rgba(255, 255, 255, 0.04);
border-radius: 12px;
pointer-events: none;
z-index: 3;
}
.crosshair {
position: absolute;
width: 15px;
height: 15px;
opacity: 0.35;
}
.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: 28px; right: 32px; }
.ch-br { bottom: 28px; right: 32px; }
/* 3. 顶层:左侧 65% 安全区三段式排版容器 */
.safe-zone {
position: relative;
z-index: 4;
width: 740px; /* 严格控制在左侧 ~62% 安全区内 */
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
/* --- 第一段 (Top):分类徽章 + 核心技术标签 --- */
.top-row {
display: flex;
align-items: center;
gap: 14px;
flex-wrap: nowrap;
}
.cat-badge {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 14px;
border-radius: 999px;
background: rgba(77, 130, 255, 0.16);
border: 1px solid rgba(109, 141, 255, 0.45);
color: #93b4ff;
font-family: "JetBrains Mono", "Fira Code", monospace;
font-size: 14px;
font-weight: 600;
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: 10px;
font-family: "JetBrains Mono", "Fira Code", monospace;
font-size: 14px;
color: var(--text-dim);
}
.top-tags span {
background: rgba(28, 36, 51, 0.75);
border: 1px solid rgba(42, 54, 74, 0.8);
padding: 4px 10px;
border-radius: 6px;
color: var(--text-muted);
}
/* --- 第二段 (Center):主标题 + 硬核证据摘要条 --- */
.center-block {
display: flex;
flex-direction: column;
gap: 20px;
margin: auto 0;
padding: 12px 0;
}
.title {
font-weight: 800;
font-size: {{TITLE_SIZE}}px;
line-height: 1.18;
letter-spacing: -0.02em;
color: #ffffff;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-shadow: 0 4px 24px rgba(0, 0, 0, 0.6);
}
.title .accent {
color: var(--primary-bright);
}
.brand-divider {
width: 56px;
height: 4px;
border-radius: 2px;
background: var(--primary-bright);
}
/* 浓缩版证据胶囊:保留旧版 Terminal 的硬核感,但更紧凑现代 */
.evidence-strip {
display: inline-flex;
align-items: center;
gap: 12px;
width: fit-content;
max-width: 720px;
padding: 12px 18px;
border-radius: 8px;
background: rgba(21, 27, 38, 0.82);
border: 1px solid rgba(45, 58, 80, 0.9);
backdrop-filter: blur(8px);
font-family: "JetBrains Mono", "Fira Code", monospace;
font-size: 16px;
line-height: 1.4;
color: #d1d5db;
}
.evidence-strip .prompt {
color: var(--primary-bright);
font-weight: 700;
}
.evidence-strip .text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* --- 第三段 (Bottom):个人品牌锁定 (与 sample-new-default-og.jpg 呼应) --- */
.bottom-row {
display: flex;
align-items: center;
justify-content: space-between;
padding-top: 18px;
border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.author-lockup {
display: flex;
align-items: center;
gap: 14px;
}
.author-avatar {
width: 44px;
height: 44px;
border-radius: 50%;
object-fit: cover;
border: 2px solid rgba(109, 141, 255, 0.6);
}
.author-meta {
display: flex;
flex-direction: column;
gap: 2px;
}
.author-name {
font-size: 18px;
font-weight: 700;
color: #ffffff;
letter-spacing: 0.2px;
}
.author-name .handle {
font-weight: 500;
color: var(--text-muted);
font-size: 15px;
margin-left: 6px;
}
.author-role {
font-family: "JetBrains Mono", monospace;
font-size: 13px;
color: var(--primary-bright);
}
.domain-pill {
font-family: "JetBrains Mono", monospace;
font-size: 15px;
color: var(--text-muted);
background: rgba(15, 20, 28, 0.75);
border: 1px solid rgba(255, 255, 255, 0.1);
padding: 6px 14px;
border-radius: 6px;
}
</style>
</head>
<body>
<div class="canvas">
<!-- 1. Ideogram 4 背景图 -->
<img class="bg-artwork" src="{{BG_IMAGE_SRC}}" alt="">
<!-- 2. 渐变遮罩与蓝图准星 -->
<div class="scrim-overlay"></div>
<div class="blueprint-accents">
<div class="crosshair ch-tr"></div>
<div class="crosshair ch-br"></div>
</div>
<!-- 3. 左侧 65% 三段式黄金排版区 -->
<div class="safe-zone">
<!-- Top -->
<div class="top-row">
<div class="cat-badge"><span class="dot"></span>{{CATEGORY}}</div>
<div class="top-tags">{{TAGS_HTML}}</div>
</div>
<!-- Center -->
<div class="center-block">
<h1 class="title">{{TITLE_HTML}}</h1>
<div class="brand-divider"></div>
<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>