Add WeRead year-in-review HyperFrames template (#2131)

Co-authored-by: Tuola Ge <gexingli@refly.ai>
This commit is contained in:
Tuola-waj 2026-05-19 15:11:41 +08:00 committed by GitHub
parent a38e09f931
commit 5254559cb1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 913 additions and 0 deletions

View file

@ -788,6 +788,11 @@ export const FR_PROMPT_TEMPLATE_COPY: Record<string, Partial<Pick<PromptTemplate
summary:
'Clip hype vertical HyperFrames 1080×1920 de 6 secondes — compteur style Apple de $0 à $10,000 avec flash vert, particules money-burst, icône cash stack et kicker headline. Bâti sur le bloc HyperFrames `apple-money-count`.',
},
'weread-year-in-review-video-template': {
title: 'Template vidéo WeRead Year in Review',
summary:
'Template vidéo HyperFrames 9:16 pour rapports annuels de lecture façon WeRead : papier chaud, typographie chinoise éditoriale, transitions de pages, statistiques de lecture, traces de notes, mots-clés dintérêt et carte finale de persona lecteur.',
},
'hyperframes-product-reveal-minimal': {
title: 'HyperFrames : product reveal minimal de 5 secondes',
summary:

View file

@ -788,6 +788,11 @@ export const RU_PROMPT_TEMPLATE_COPY: Record<string, Partial<Pick<PromptTemplate
summary:
'6-секундный вертикальный HyperFrames-клип 1080x1920 — счетчик в духе Apple от $0 до $10 000 с зеленой вспышкой, денежными частицами, иконкой пачки наличных и заголовком-kicker. Основано на каталожном блоке HyperFrames `apple-money-count`.',
},
'weread-year-in-review-video-template': {
title: 'Видео-шаблон WeRead Year in Review',
summary:
'Вертикальный 9:16 HyperFrames-шаблон для годовых отчетов о чтении в стиле WeRead: теплая бумажная фактура, редакционная китайская типографика, переходы страниц, статистика чтения, следы заметок, ключевые интересы и финальная карточка читательского persona.',
},
'hyperframes-product-reveal-minimal': {
title: 'HyperFrames: 5-секундный минималистичный product reveal',
summary:

View file

@ -826,6 +826,11 @@ const DE_PROMPT_TEMPLATE_COPY: Record<string, LocalizedPromptTemplateCopy> = {
summary:
'Ein 6-sekündiger vertikaler 1080×1920-HyperFrames-Hype-Clip Apple-artiger $0 → $10.000-Counter mit grünem Flash, Money-Burst-Partikeln, Cash-Stack-Icon, Kicker-Headline. Aufgebaut auf dem HyperFrames-`apple-money-count`-Catalog-Block.',
},
'weread-year-in-review-video-template': {
title: 'WeRead Year in Review Video Template',
summary:
'A 9:16 HyperFrames video template for WeRead-style annual reading reports: warm paper texture, editorial Chinese typography, book-page transitions, reading stats, note traces, interest keywords, and a final reading persona card.',
},
'hyperframes-product-reveal-minimal': {
title: 'HyperFrames: 5-Sekunden minimaler Product Reveal',
summary:

View file

@ -0,0 +1,93 @@
---
name: weread-year-in-review-video-template
description: |
WeRead-inspired HyperFrames video template for vertical annual reading reports,
personal reading dashboards, book-note recaps, and shareable year-in-review
stories. Use when users want a 9:16 HTML-to-MP4 reading report with warm paper
texture, editorial Chinese typography, book-page metaphors, data highlights,
and deterministic motion.
triggers:
- "WeRead year in review"
- "WeRead annual report"
- "reading year in review video"
- "annual reading report template"
- "微信读书年度报告"
- "读书年度总结视频"
- "阅读年报 HyperFrames"
od:
mode: template
surface: video
type: hyperframes
platform: mobile
preview:
type: html
entry: example.html
reload: debounce-100
design_system:
requires: false
outputs:
primary: index.html
secondary:
- template.html
- example.html
example_prompt: "Create a WeRead-style 9:16 HyperFrames annual reading report video with 12 scenes, warm paper texture, book-page transitions, reading stats, notes, keywords, and a final reading persona card."
capabilities_required:
- file_write
---
# WeRead Year in Review Video Template
Create a vertical HyperFrames composition for annual reading reports: WeRead,
Goodreads, Readwise, Notion reading logs, book clubs, or personal learning
recaps. The template turns reading time, active days, bookshelf assets, notes,
keywords, and a reading persona into a shareable 9:16 video.
## Resource Map
```text
weread-year-in-review-video-template/
├── SKILL.md
├── assets/
│ ├── template.html
│ └── default-showcase.mp4
├── references/
│ └── checklist.md
└── example.html
```
## Workflow
1. Copy `assets/template.html` to `index.html`.
2. Replace the default report data in the `REPORT` object:
- owner/title
- reading hours and active days
- bookshelf and completion stats
- note composition
- interest keywords
- reading persona and share line
3. Preserve the 12-scene timeline unless the user asks for a shorter cut.
4. Keep the WeRead-inspired visual language:
- warm paper background
- ink-blue typography
- restrained WeRead green accents
- book pages, bookmarks, highlights, note cards, and shelf metaphors
5. Motion should feel like flipping through a reading journal. Avoid techy
slide transitions, bouncy UI effects, and dashboard-loading motion.
6. Keep the composition deterministic:
- direct `data-start`, `data-duration`, and `data-track-index` attributes
- no unseeded randomness
- no infinite loops or `repeat: -1`
- no dependency on scroll, hover, localStorage, or runtime class discovery
7. Validate against `references/checklist.md` before emitting.
## Output Contract
Emit one short orientation sentence, then a single HTML artifact:
```xml
<artifact identifier="weread-year-in-review-video-template" type="text/html" title="WeRead Year in Review Video Template">
<!doctype html>
<html>...</html>
</artifact>
```

View file

@ -0,0 +1,714 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1080, height=1920" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<title>WeRead Year in Review Video Template</title>
<style>
:root {
--paper: #fbfaf5;
--paper-warm: #f4f1e8;
--ink: #27394c;
--ink-soft: #526477;
--muted: #788174;
--line: rgba(39, 57, 76, 0.16);
--weread: #65b782;
--gold: #c4aa73;
--sage: #8fa58c;
--brick: #b97861;
--font-display: "Songti SC", "Noto Serif CJK SC", "Source Han Serif SC", "SimSun", serif;
--font-body: -apple-system, BlinkMacSystemFont, "PingFang SC", "Noto Sans CJK SC", "Microsoft YaHei", sans-serif;
--font-mono: "SFMono-Regular", ui-monospace, monospace;
}
* { box-sizing: border-box; }
html, body {
margin: 0;
width: 1080px;
height: 1920px;
overflow: hidden;
background:
radial-gradient(circle at 18% 8%, rgba(101, 183, 130, 0.12), transparent 30%),
radial-gradient(circle at 82% 12%, rgba(196, 170, 115, 0.11), transparent 28%),
linear-gradient(180deg, #f1f2eb, #e9ebe2);
color: var(--ink);
font-family: var(--font-body);
}
#root {
position: relative;
width: 1080px;
height: 1920px;
overflow: hidden;
}
.scene {
--accent: var(--weread);
position: absolute;
inset: 0;
display: grid;
grid-template-rows: 184px minmax(0, 1fr) 230px;
padding: 78px 76px 82px;
background:
linear-gradient(180deg, rgba(255,255,255,0.38), transparent 36%),
repeating-linear-gradient(0deg, transparent 0 39px, rgba(39,57,76,0.026) 40px),
linear-gradient(90deg, transparent 0 62px, color-mix(in srgb, var(--accent), transparent 88%) 63px, transparent 64px),
radial-gradient(circle at 82% 8%, color-mix(in srgb, var(--accent), transparent 90%), transparent 28%),
var(--paper);
border-radius: 34px;
border: 1px solid rgba(39, 57, 76, 0.08);
overflow: hidden;
opacity: 0;
visibility: hidden;
}
.scene:first-of-type {
opacity: 1;
visibility: visible;
}
.scene::before {
content: "";
position: absolute;
inset: 36px;
border: 1px solid rgba(39, 57, 76, 0.06);
border-radius: 24px;
pointer-events: none;
}
.scene::after {
content: attr(data-label);
position: absolute;
right: 74px;
top: 66px;
font: 700 18px/1 var(--font-mono);
color: color-mix(in srgb, var(--accent), var(--ink) 45%);
letter-spacing: 0.18em;
}
.scene.gold { --accent: var(--gold); }
.scene.sage { --accent: var(--sage); }
.scene.brick { --accent: var(--brick); }
.header { align-self: start; position: relative; z-index: 2; }
.kicker {
display: inline-flex;
align-items: center;
gap: 16px;
color: color-mix(in srgb, var(--ink), var(--accent) 22%);
font: 800 22px/1 var(--font-mono);
letter-spacing: 0.14em;
}
.kicker::before {
content: "";
width: 48px;
height: 1px;
background: currentColor;
opacity: 0.46;
}
h1, h2, p { margin: 0; }
h1, h2, .serif, .mega, .persona {
font-family: var(--font-display);
font-weight: 500;
letter-spacing: 0;
}
h1 {
margin-top: 50px;
font-size: 116px;
line-height: 1.06;
max-width: 820px;
}
h2 {
margin-top: 42px;
font-size: 64px;
line-height: 1.18;
max-width: 820px;
}
.visual {
position: relative;
z-index: 1;
min-height: 0;
display: grid;
place-items: center;
}
.caption {
align-self: end;
position: relative;
z-index: 3;
padding-top: 30px;
border-top: 1px solid color-mix(in srgb, var(--accent), transparent 70%);
color: var(--muted);
font-size: 30px;
line-height: 1.62;
font-weight: 650;
}
.caption strong { color: var(--ink); }
.cover-stack {
width: 560px;
height: 360px;
border: 1px solid color-mix(in srgb, var(--weread), transparent 62%);
border-radius: 8px 34px 34px 8px;
background:
linear-gradient(90deg, rgba(101,183,130,0.22) 0 18px, transparent 18px),
linear-gradient(180deg, rgba(255,255,255,0.56), rgba(255,255,255,0.08)),
#f8fbf4;
box-shadow: -30px 26px 0 rgba(101,183,130,0.10), 24px 22px 0 rgba(101,183,130,0.06);
}
.cover-stack i {
display: block;
height: 1px;
margin: 96px 88px 0;
background: rgba(39,57,76,0.13);
}
.cover-stack i + i { width: 54%; margin-top: 38px; }
.cover-number {
margin-top: 88px;
text-align: center;
color: rgba(39,57,76,0.34);
font: 500 68px/1 var(--font-display);
}
.cover-number small {
display: block;
margin-top: 16px;
font: 700 22px/1 var(--font-body);
letter-spacing: 0.1em;
}
.badge {
position: absolute;
left: 0;
bottom: 88px;
color: var(--weread);
font: 800 24px/1 var(--font-mono);
letter-spacing: 0.12em;
}
.mega {
color: var(--ink);
font-size: 220px;
line-height: 0.88;
text-align: center;
}
.mega .unit {
display: block;
margin-top: 34px;
color: var(--ink-soft);
font: 800 44px/1.25 var(--font-body);
letter-spacing: 0.04em;
}
.orbit {
position: absolute;
width: 620px;
height: 620px;
border-radius: 50%;
border: 2px solid rgba(101,183,130,0.24);
}
.orbit::before,
.orbit::after {
content: "";
position: absolute;
inset: 78px;
border: 2px solid rgba(196,170,115,0.24);
border-radius: inherit;
}
.orbit::after { inset: 160px; border-color: rgba(39,57,76,0.12); }
.path-card {
width: 620px;
height: 620px;
position: relative;
}
.path-card svg { width: 100%; height: 100%; overflow: visible; }
.path-card .mega {
position: absolute;
inset: 0;
display: grid;
place-items: center;
font-size: 190px;
}
.kpi-grid {
width: 850px;
display: grid;
grid-template-columns: 1.2fr 1fr 1fr;
gap: 32px;
align-items: end;
}
.kpi-main {
grid-row: span 2;
padding-right: 30px;
border-right: 1px solid var(--line);
}
.kpi-main span,
.kpi em {
display: block;
color: var(--muted);
font-style: normal;
font-size: 24px;
line-height: 1;
}
.kpi-main b {
display: block;
margin-top: 34px;
color: var(--ink);
font: 500 92px/0.96 var(--font-display);
}
.kpi {
min-height: 142px;
padding-bottom: 22px;
border-bottom: 1px solid var(--line);
}
.kpi strong {
display: block;
margin-top: 18px;
color: var(--ink);
font: 500 62px/1 var(--font-display);
}
.rhythm {
width: 820px;
display: grid;
gap: 28px;
position: relative;
padding-left: 72px;
}
.month {
display: grid;
grid-template-columns: 44px 1fr;
align-items: center;
gap: 28px;
color: var(--muted);
font: 800 22px/1 var(--font-mono);
}
.month i {
display: block;
width: calc(var(--v) * 1%);
height: 8px;
border-radius: 999px;
background: var(--c);
transform-origin: left center;
}
.curve {
position: absolute;
left: 110px;
right: 20px;
top: 210px;
height: 280px;
pointer-events: none;
}
.book-card {
width: 640px;
height: 430px;
position: relative;
border: 1px solid rgba(196,170,115,0.52);
border-radius: 8px 34px 34px 8px;
background: linear-gradient(90deg, rgba(185,120,97,0.18) 0 22px, rgba(255,255,255,0.26) 22px);
}
.book-card::before {
content: "《定位》";
position: absolute;
left: 82px;
top: 132px;
color: var(--ink);
font: 500 86px/1 var(--font-display);
letter-spacing: 0.06em;
}
.book-card::after {
content: "";
position: absolute;
left: 82px;
right: 110px;
bottom: 110px;
height: 12px;
border-radius: 999px;
background: linear-gradient(90deg, var(--gold) 0 78%, rgba(196,170,115,0.18) 78% 100%);
transform-origin: left center;
}
.bookmark {
position: absolute;
right: 74px;
top: 0;
width: 56px;
height: 170px;
background: var(--weread);
clip-path: polygon(0 0,100% 0,100% 100%,50% 78%,0 100%);
}
.map {
width: 780px;
height: 600px;
position: relative;
background:
linear-gradient(var(--line) 1px, transparent 1px),
linear-gradient(90deg, var(--line) 1px, transparent 1px);
background-size: 86px 86px;
mask-image: radial-gradient(circle, #000 0 58%, transparent 78%);
}
.topic {
position: absolute;
left: var(--x);
top: var(--y);
transform: translate(-50%, -50%);
color: var(--c);
font: 500 var(--s)/1 var(--font-display);
white-space: nowrap;
}
.topic::before {
content: "";
position: absolute;
left: 50%;
top: 50%;
width: var(--d);
height: var(--d);
transform: translate(-50%, -50%);
border-radius: 50%;
border: 1px solid color-mix(in srgb, currentColor, transparent 60%);
z-index: -1;
}
.margin-notes {
width: 760px;
height: 620px;
position: relative;
}
.margin-notes::before {
content: "";
position: absolute;
left: 50%;
top: 20px;
bottom: 20px;
width: 1px;
background: var(--line);
}
.note {
position: absolute;
left: var(--x);
top: var(--y);
width: var(--w);
padding-top: 18px;
border-top: 2px solid color-mix(in srgb, var(--c), transparent 30%);
color: var(--c);
font: 800 26px/1.35 var(--font-body);
}
.note b {
display: block;
margin-top: 12px;
font: 500 54px/1 var(--font-display);
}
.shelves {
width: 800px;
display: grid;
gap: 70px;
}
.asset-number {
position: absolute;
right: 106px;
top: 260px;
text-align: right;
color: var(--ink);
font: 500 120px/0.9 var(--font-display);
}
.asset-number small {
display: block;
margin-top: 24px;
color: var(--muted);
font: 800 24px/1 var(--font-body);
}
.shelf-row {
position: relative;
min-height: 118px;
padding-top: 38px;
border-bottom: 1px solid var(--line);
}
.shelf-row i {
display: block;
width: max(var(--w), var(--min, 0px));
height: var(--h);
border-radius: 999px;
background: var(--c);
opacity: var(--o, 0.78);
}
.shelf-row span {
position: absolute;
left: 0;
top: 0;
color: var(--muted);
font-size: 26px;
font-weight: 800;
}
.shelf-row b {
position: absolute;
right: 0;
top: 0;
color: var(--ink);
font: 500 48px/1 var(--font-display);
}
.pages {
width: 690px;
height: 650px;
position: relative;
}
.page-stack i {
position: absolute;
left: calc(var(--n) * 24px);
top: calc(var(--n) * 16px);
width: 430px;
height: 560px;
border-radius: 8px 34px 34px 8px;
border: 1px solid color-mix(in srgb, var(--c), transparent 32%);
background: color-mix(in srgb, var(--c), white 88%);
}
.progress {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 14px;
border-radius: 999px;
background: rgba(39,57,76,0.12);
}
.progress i {
display: block;
width: 70.52%;
height: 100%;
border-radius: inherit;
background: var(--weread);
transform-origin: left center;
}
.legend {
position: absolute;
right: 0;
top: 88px;
display: grid;
gap: 30px;
color: var(--muted);
font-size: 26px;
font-weight: 800;
}
.legend span::before {
content: "";
display: inline-block;
width: 16px;
height: 16px;
margin-right: 14px;
border-radius: 50%;
background: var(--c);
}
.notes-scene {
display: grid;
grid-template-rows: auto 1fr auto;
gap: 54px;
width: 860px;
height: 880px;
}
.notes-scene .mega { font-size: 190px; }
.word-cloud {
position: relative;
min-height: 360px;
border-top: 1px solid var(--line);
border-bottom: 1px solid var(--line);
}
.word-cloud span {
position: absolute;
left: var(--x);
top: var(--y);
transform: translate(-50%, -50%) rotate(var(--r));
color: var(--c);
font-family: var(--font-display);
font-size: var(--s);
white-space: nowrap;
}
.annotation {
display: grid;
gap: 16px;
border-left: 2px solid rgba(185,120,97,0.46);
padding-left: 22px;
}
.annotation div {
display: grid;
grid-template-columns: 150px 1fr;
align-items: center;
min-height: 70px;
padding: 0 22px;
border: 1px solid color-mix(in srgb, var(--c), transparent 48%);
border-radius: 14px;
color: var(--muted);
font-size: 24px;
font-weight: 750;
}
.annotation b {
color: var(--c);
font: 500 44px/1 var(--font-display);
}
.persona-card {
width: 760px;
min-height: 650px;
display: grid;
place-items: center;
text-align: center;
border: 1px solid rgba(101,183,130,0.52);
border-radius: 90px 90px 34px 34px;
background: radial-gradient(circle at 50% 18%, rgba(101,183,130,0.11), transparent 36%);
}
.persona {
font-size: 126px;
line-height: 1.08;
}
.chips {
margin: 56px auto 0;
max-width: 660px;
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 18px;
}
.chips span {
border: 1px solid rgba(101,183,130,0.50);
border-radius: 999px;
padding: 14px 22px;
color: var(--ink-soft);
font-size: 24px;
font-weight: 800;
background: rgba(255,255,255,0.24);
}
.share {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 18px;
margin-top: 34px;
color: var(--ink);
font-size: 22px;
text-align: center;
}
.share b {
display: block;
font: 500 40px/1 var(--font-display);
}
@keyframes gentleFloat {
from { transform: translateY(0); }
to { transform: translateY(-8px); }
}
</style>
</head>
<body>
<main id="root" data-composition-id="main" data-start="0" data-duration="40.8" data-width="1080" data-height="1920">
<section class="scene clip" id="scene-01" data-label="01" data-start="0" data-duration="3.8" data-track-index="10">
<div class="header"><div class="kicker">年度开场</div><h1>我的微信读书<br />年度报告</h1></div>
<div class="visual"><div class="cover-stack"><i></i><i></i><div class="cover-number">1693<small>小时 30 分钟</small></div></div><div class="badge">2024-2026 读书报告</div></div>
<p class="caption">过去两年,我在书里停留了 <strong>1693 小时 30 分钟</strong></p>
</section>
<section class="scene clip" id="scene-02" data-label="02" data-start="3.8" data-duration="3.3" data-track-index="10">
<div class="header"><div class="kicker">停留时间</div></div>
<div class="visual"><div class="orbit"></div><div class="mega">1693<span class="unit">小时 30 分钟</span></div></div>
<p class="caption">有些时间没有被消耗,它们被一页一页地存了下来。</p>
</section>
<section class="scene clip" id="scene-03" data-label="03" data-start="7.1" data-duration="3.3" data-track-index="10">
<div class="header"><div class="kicker">持续回到书里</div><h2>439 天,<br />我一次次回到书里</h2></div>
<div class="visual"><div class="path-card"><svg viewBox="0 0 620 620" aria-hidden="true"><path d="M308 48 C148 164 150 270 292 320 C468 382 472 496 322 558" fill="none" stroke="rgba(101,183,130,.45)" stroke-width="4"/><g fill="var(--paper)" stroke="var(--ink-soft)" stroke-width="3"><circle cx="308" cy="48" r="9"/><circle cx="220" cy="212" r="9"/><circle cx="292" cy="320" r="9"/><circle cx="448" cy="420" r="9"/><circle cx="322" cy="558" r="9"/></g></svg><div class="mega">439<span class="unit"></span></div></div></div>
<p class="caption">不是每天都读很多,但每一次打开,都把生活往内心推近一点。</p>
</section>
<section class="scene clip gold" id="scene-04" data-label="04" data-start="10.4" data-duration="3.3" data-track-index="10">
<div class="header"><div class="kicker">年度总览</div><h2>这一年,阅读变成了<br />可以翻看的积累。</h2></div>
<div class="visual"><div class="kpi-grid"><div class="kpi-main"><span>阅读时长</span><b>1693小时<br />30分钟</b></div><div class="kpi"><em>有效阅读日</em><strong>439</strong></div><div class="kpi"><em>书架条目</em><strong>771</strong></div><div class="kpi"><em>笔记总量</em><strong>10950</strong></div><div class="kpi"><em>有笔记书籍</em><strong>42</strong></div><div class="kpi"><em>语义文本</em><strong>9900</strong></div></div></div>
<p class="caption">数字不是结论,它们只是提醒我:很多思考已经悄悄留下。</p>
</section>
<section class="scene clip gold" id="scene-05" data-label="05" data-start="13.7" data-duration="3.2" data-track-index="10">
<div class="header"><div class="kicker">阅读节律</div><h2>我的阅读节律</h2></div>
<div class="visual"><div class="rhythm"><div class="month"><span>01</span><i style="--v:38;--c:var(--sage)"></i></div><div class="month"><span>02</span><i style="--v:72;--c:var(--gold)"></i></div><div class="month"><span>03</span><i style="--v:66;--c:var(--ink)"></i></div><div class="month"><span>04</span><i style="--v:78;--c:var(--weread)"></i></div><div class="month"><span>05</span><i style="--v:70;--c:var(--sage)"></i></div><div class="month"><span>06</span><i style="--v:68;--c:var(--ink)"></i></div><div class="month"><span>07</span><i style="--v:58;--c:var(--gold)"></i></div><div class="month"><span>08</span><i style="--v:74;--c:var(--weread)"></i></div><div class="month"><span>09</span><i style="--v:80;--c:var(--ink)"></i></div><div class="month"><span>10</span><i style="--v:84;--c:var(--gold)"></i></div><div class="month"><span>11</span><i style="--v:70;--c:var(--sage)"></i></div><div class="month"><span>12</span><i style="--v:46;--c:var(--weread)"></i></div><svg class="curve" viewBox="0 0 700 280"><path d="M10 210 C72 92 134 160 190 118 S315 74 368 148 S500 220 558 92 S646 120 690 58" fill="none" stroke="var(--ink)" stroke-width="4"/></svg></div></div>
<p class="caption">高峰和低谷都在,但书页始终没有离开我的生活。</p>
</section>
<section class="scene clip gold" id="scene-06" data-label="06" data-start="16.9" data-duration="3.1" data-track-index="10">
<div class="header"><div class="kicker">读得最久</div><h2>我读得最久的一本书</h2></div>
<div class="visual"><div class="book-card"><div class="bookmark"></div></div></div>
<p class="caption">《定位》停留得最久,也让“如何被记住”这件事反复出现。</p>
</section>
<section class="scene clip sage" id="scene-07" data-label="07" data-start="20" data-duration="3.3" data-track-index="10">
<div class="header"><div class="kicker">兴趣地图</div><h2>我的阅读兴趣地图</h2></div>
<div class="visual"><div class="map"><span class="topic" style="--x:50%;--y:43%;--s:72px;--d:230px;--c:var(--ink)">AI技术</span><span class="topic" style="--x:24%;--y:66%;--s:48px;--d:140px;--c:var(--gold)">创业</span><span class="topic" style="--x:76%;--y:66%;--s:44px;--d:160px;--c:var(--weread)">产品增长</span><span class="topic" style="--x:58%;--y:78%;--s:38px;--d:112px;--c:var(--brick)">商业化</span><span class="topic" style="--x:76%;--y:28%;--s:36px;--d:106px;--c:var(--sage)">系统思维</span><span class="topic" style="--x:26%;--y:28%;--s:34px;--d:100px;--c:var(--muted)">组织管理</span></div></div>
<p class="caption">我更多在技术、商业和系统之间寻找答案。</p>
</section>
<section class="scene clip sage" id="scene-08" data-label="08" data-start="23.3" data-duration="3.2" data-track-index="10">
<div class="header"><div class="kicker">阅读偏好</div><h2>我的阅读偏好</h2></div>
<div class="visual"><div class="margin-notes"><div class="note" style="--x:0%;--y:40px;--w:350px;--c:var(--ink)">最高峰<b>AI 技术</b></div><div class="note" style="--x:54%;--y:145px;--w:330px;--c:var(--gold)">另一条主线<b>创业管理</b></div><div class="note" style="--x:10%;--y:320px;--w:320px;--c:var(--weread)">持续展开<b>产品增长</b></div><div class="note" style="--x:56%;--y:440px;--w:330px;--c:var(--sage)">底层方法<b>系统思维</b></div></div></div>
<p class="caption">偏好不是标签,而是我反复追问同一类问题的路径。</p>
</section>
<section class="scene clip sage" id="scene-09" data-label="09" data-start="26.5" data-duration="3.2" data-track-index="10">
<div class="header"><div class="kicker">书架资产</div><h2>我的书架资产</h2></div>
<div class="visual"><div class="asset-number">771<small>书架条目</small></div><div class="shelves"><div class="shelf-row"><span>电子书</span><b>94.03%</b><i style="--h:54px;--w:94.03%;--c:var(--ink)"></i></div><div class="shelf-row"><span>有声 / 专辑</span><b>5.84%</b><i style="--h:24px;--w:5.84%;--min:42px;--c:var(--gold);--o:.72"></i></div><div class="shelf-row"><span>文章收藏</span><b>0.13%</b><i style="--h:10px;--w:.13%;--min:12px;--c:var(--weread);--o:.9"></i></div></div></div>
<p class="caption">书架不是仓库,它更像一组长期问题的索引。</p>
</section>
<section class="scene clip sage" id="scene-10" data-label="10" data-start="29.7" data-duration="3.2" data-track-index="10">
<div class="header"><div class="kicker">读完进度</div><h2>我和这些书的进度</h2></div>
<div class="visual"><div class="pages"><div class="page-stack"><i style="--n:0;--c:var(--weread)"></i><i style="--n:1;--c:var(--gold)"></i><i style="--n:2;--c:var(--sage)"></i><i style="--n:3;--c:var(--ink)"></i><i style="--n:4;--c:var(--brick)"></i></div><div class="legend"><span style="--c:var(--weread)">已读完 70.52%</span><span style="--c:var(--ink)">未读完 23.64%</span><span style="--c:var(--sage)">连载 1.95%</span><span style="--c:var(--gold)">已完结专辑 3.89%</span></div><div class="progress"><i></i></div></div></div>
<p class="caption">有些书已经合上,有些仍把书签夹在下一次打开的位置。</p>
</section>
<section class="scene clip brick" id="scene-11" data-label="11" data-start="32.9" data-duration="3.7" data-track-index="10">
<div class="header"><div class="kicker">思考痕迹</div></div>
<div class="visual"><div class="notes-scene"><div class="mega">10950<span class="unit">条笔记</span></div><div class="word-cloud"><span style="--x:50%;--y:46%;--r:-4deg;--s:62px;--c:var(--ink)">智能体</span><span style="--x:22%;--y:34%;--r:7deg;--s:40px;--c:var(--gold)">商业化</span><span style="--x:76%;--y:34%;--r:-7deg;--s:38px;--c:var(--weread)">系统思维</span><span style="--x:36%;--y:76%;--r:5deg;--s:34px;--c:var(--brick)">产品增长</span><span style="--x:74%;--y:76%;--r:4deg;--s:31px;--c:var(--muted)">创业</span></div><div class="annotation"><div style="--c:var(--ink)"><b>68.08%</b><span>划线,把重要句子留下</span></div><div style="--c:var(--brick)"><b>22.33%</b><span>想法,和作者短暂停顿</span></div><div style="--c:var(--weread)"><b>9.59%</b><span>书签,留给下一次打开</span></div></div></div></div>
<p class="caption">真正留下来的,不只是读过,而是那些被我停下来标记的瞬间。</p>
</section>
<section class="scene clip brick" id="scene-12" data-label="12" data-start="36.6" data-duration="4.2" data-track-index="10">
<div class="header"><div class="kicker">阅读人格</div><h2>我的阅读人格</h2></div>
<div class="visual"><div class="persona-card"><div><div class="persona">技术商业<br />建造者</div><div class="chips"><span>AI技术</span><span>创业</span><span>产品增长</span><span>商业化</span><span>系统思维</span><span>智能体</span></div></div></div></div>
<div><p class="caption">我习惯从技术、产品和商业系统里寻找答案。</p><div class="share"><span><b>1693小时</b>阅读</span><span><b>10950条</b>笔记</span><span><b>42本</b>深度标记书籍</span></div></div>
</section>
</main>
<script>
window.__timelines = window.__timelines || {};
var scenes = Array.from(document.querySelectorAll(".scene"));
function showScene(index) {
scenes.forEach(function (scene, i) {
scene.style.visibility = i === index ? "visible" : "hidden";
scene.style.opacity = i === index ? "1" : "0";
});
}
var tl = gsap.timeline({ paused: true });
scenes.forEach(function (scene, index) {
var start = Number(scene.dataset.start);
var duration = Number(scene.dataset.duration);
tl.set(scene, { autoAlpha: 1 }, start);
tl.fromTo(scene, { y: 26 }, { y: 0, duration: 0.55, ease: "power3.out", immediateRender: false }, start);
tl.from(scene.querySelectorAll(".kicker, h1, h2"), { autoAlpha: 0, y: 28, duration: 0.6, stagger: 0.08, ease: "power3.out", immediateRender: false }, start + 0.08);
tl.from(scene.querySelectorAll(".visual > *"), { autoAlpha: 0, y: 34, duration: 0.72, stagger: 0.08, ease: "power3.out", immediateRender: false }, start + 0.35);
tl.from(scene.querySelectorAll(".mega, .persona"), { autoAlpha: 0, y: 18, duration: 0.65, ease: "power3.out", immediateRender: false }, start + 0.45);
tl.from(scene.querySelectorAll(".month i, .progress i, .shelf-row i"), { scaleX: 0, duration: 0.85, stagger: 0.04, ease: "power3.out", immediateRender: false }, start + 0.75);
tl.from(scene.querySelectorAll(".topic, .note, .word-cloud span, .annotation div, .chips span, .kpi, .legend span"), { autoAlpha: 0, y: 14, duration: 0.48, stagger: 0.07, ease: "power2.out", immediateRender: false }, start + 0.68);
tl.from(scene.querySelectorAll(".caption, .share, .badge"), { autoAlpha: 0, y: 16, duration: 0.55, ease: "power2.out", immediateRender: false }, start + Math.min(1.2, duration - 1.2));
tl.to(scene, { y: -18, autoAlpha: 0, duration: 0.45, ease: "power2.inOut" }, start + duration - 0.46);
tl.set(scene, { autoAlpha: 0 }, start + duration);
});
window.__timelines.main = tl;
showScene(0);
var jumpTimes = [0, 3.8, 7.1, 10.4, 13.7, 16.9, 20, 23.3, 26.5, 29.7, 32.9, 36.6];
window.addEventListener("keydown", function (event) {
var keys = ["1","2","3","4","5","6","7","8","9","0","-","="];
var index = keys.indexOf(event.key);
if (index >= 0) {
showScene(index);
tl.pause(jumpTimes[index] + 0.8);
}
if (event.key.toLowerCase() === "r") {
showScene(0);
tl.restart();
}
});
</script>
</body>
</html>

View file

@ -0,0 +1,64 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>WeRead Year in Review Video Template</title>
<style>
html, body {
margin: 0;
height: 100%;
background: #eef0e8;
color: #243649;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
.top {
height: 52px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
border-bottom: 1px solid rgba(36, 54, 73, 0.14);
background: rgba(250, 249, 244, 0.92);
letter-spacing: 0.03em;
font-size: 12px;
}
.top b { color: #243649; }
.top span { color: #6b766f; }
.stage {
height: calc(100% - 53px);
display: grid;
grid-template-columns: 1fr 1fr;
min-height: 0;
}
.pane {
min-width: 0;
border-right: 1px solid rgba(36, 54, 73, 0.14);
background: #f6f5ef;
}
.pane:last-child { border-right: 0; background: #111; }
iframe, video {
width: 100%;
height: 100%;
border: 0;
display: block;
}
video { object-fit: contain; background: #000; }
</style>
</head>
<body>
<div class="top">
<b>WEREAD YEAR IN REVIEW · HYPERFRAMES VIDEO TEMPLATE</b>
<span>Left: editable HTML template · Right: rendered MP4 showcase</span>
</div>
<div class="stage">
<div class="pane">
<iframe src="./assets/template.html" title="WeRead Year in Review template preview"></iframe>
</div>
<div class="pane">
<video controls preload="metadata" src="./assets/default-showcase.mp4"></video>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,27 @@
# Checklist
## P0
- `assets/template.html` exists and opens directly in a browser.
- `example.html` previews both the editable template and `assets/default-showcase.mp4`.
- Skill frontmatter uses `od.mode: template`, `od.surface: video`, and `od.type: hyperframes`.
- Composition is vertical 9:16, 1080x1920, with exactly 12 scenes.
- Every scene has direct `class="scene clip"` plus explicit `data-start`, `data-duration`, and `data-track-index` attributes.
- Rendered MP4 is not blank: contact sheet must show visible content in all 12 scenes.
- No scene has text/component overlap at 1080x1920.
- Timeline is deterministic: no unseeded randomness and no infinite repeats.
- Template avoids sandbox-hostile APIs (`localStorage`, `sessionStorage`, `alert`, `confirm`, `prompt`).
## P1
- Motion feels like flipping through a reading journal: calm page reveals, highlight strokes, note cards, and bookmark accents.
- Key stats remain legible after video compression on mobile social feeds.
- Scene holds stay between 2.8 and 4.2 seconds by default.
- The final persona card is strong enough to serve as a social sharing frame.
## P2
- Keyboard preview controls (`1`-`9`, `0`, `-`, `=`, `r`) jump to scenes locally.
- The template can be adapted to non-WeRead reading data without changing the visual system.
- Static fallback remains readable if GSAP fails to load.