hyper-animator-codex 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -0
- package/bin/hyper-animator-codex.mjs +74 -0
- package/lib/install-skill.mjs +69 -0
- package/package.json +39 -0
- package/skills/hyper-animator-codex/SKILL.md +87 -0
- package/skills/hyper-animator-codex/agents/openai.yaml +4 -0
- package/skills/hyper-animator-codex/references/examples/request-code_demo.json +18 -0
- package/skills/hyper-animator-codex/references/examples/request-podcast_caption.json +22 -0
- package/skills/hyper-animator-codex/references/examples/request-product_launch.json +23 -0
- package/skills/hyper-animator-codex/references/hyperframes-agent-pseudocode.ts +266 -0
- package/skills/hyper-animator-codex/references/hyperframes-catalog-map.json +13402 -0
- package/skills/hyper-animator-codex/references/hyperframes-intent-workflow.md +191 -0
- package/skills/hyper-animator-codex/scripts/validate_hyperframes_html.py +88 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# HyperFrames Intent Workflow
|
|
2
|
+
|
|
3
|
+
目标:把自然语言视频/动画需求稳定地转成 HyperFrames HTML composition,并在关键节点用 `AskUserQuestion` 澄清。
|
|
4
|
+
|
|
5
|
+
本 workflow 采用双模式架构:
|
|
6
|
+
|
|
7
|
+
- `assemble_existing_catalog_items`:复用已安装的 HyperFrames catalog block/component,适合快速组装和调用现有视觉资产。
|
|
8
|
+
- `generate_new_hyperframes_html`:根据 catalog 候选作为参考,按 `HyperFrames-AI-Generation-Patterns-codex.md` 生成新的完整 HyperFrames HTML,适合用户要求“写 HTML 动画”或现有 catalog 不完全匹配的场景。
|
|
9
|
+
|
|
10
|
+
## Pipeline
|
|
11
|
+
|
|
12
|
+
1. 接收自然语言需求。
|
|
13
|
+
2. 抽取初始 `intent_profile`:用途、平台、画幅、内容类型、风格、动效、时长、是否需要字幕/转场/片尾。
|
|
14
|
+
3. 若用途或画幅缺失,调用 `AskUserQuestion` 第一轮澄清。
|
|
15
|
+
4. 用 `hyperframes-catalog-map.json` 对 catalog items 打分。
|
|
16
|
+
5. 选出候选 plan:主场景 Block、可选 Component、可选 Transition、可选 Outro。
|
|
17
|
+
6. 判断 generation mode:组装现有 catalog,还是生成新 HTML。
|
|
18
|
+
7. 调用 `AskUserQuestion` 第二轮澄清样式、动效、候选取舍,以及 generation mode。
|
|
19
|
+
8. 若选择 `assemble_existing_catalog_items`,生成 wrapper composition,并解析 component paste 指令为真实 snippet。
|
|
20
|
+
9. 若选择 `generate_new_hyperframes_html`,按 patterns 文档生成完整 block/component HTML。
|
|
21
|
+
10. 运行 pre-render quality gates。
|
|
22
|
+
11. 展示方案摘要和 HTML preview,让用户验证/确认。
|
|
23
|
+
12. 根据反馈修订。
|
|
24
|
+
13. 渲染视频。
|
|
25
|
+
|
|
26
|
+
## Intent Profile
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"rawRequest": "",
|
|
31
|
+
"purpose": "product_launch",
|
|
32
|
+
"format": "landscape_16_9",
|
|
33
|
+
"generationMode": "generate_new_hyperframes_html",
|
|
34
|
+
"styleTags": ["apple_like", "premium"],
|
|
35
|
+
"motionTags": ["reveal", "zoom"],
|
|
36
|
+
"neededRoles": ["main_scene", "device_showcase", "caption", "outro"],
|
|
37
|
+
"durationTarget": 30,
|
|
38
|
+
"contentInputs": {
|
|
39
|
+
"productName": "",
|
|
40
|
+
"script": "",
|
|
41
|
+
"code": "",
|
|
42
|
+
"data": []
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## AskUserQuestion Round 1: 用途和场景
|
|
48
|
+
|
|
49
|
+
当 `purpose` 或 `format` 不明确时先问,不要直接选 block。
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"purpose": "clarify_intent_and_format",
|
|
54
|
+
"questions": [
|
|
55
|
+
{
|
|
56
|
+
"id": "purpose",
|
|
57
|
+
"question": "你这次视频/动画主要用于什么场景?",
|
|
58
|
+
"choices": ["产品发布/功能展示", "技术教程/代码演示", "数据报告/市场复盘", "播客/采访/字幕包装", "社交媒体短视频"]
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"id": "format",
|
|
62
|
+
"question": "目标画幅是什么?",
|
|
63
|
+
"choices": ["横屏 16:9", "竖屏 9:16", "方形", "还不确定"]
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Catalog Scoring
|
|
70
|
+
|
|
71
|
+
```text
|
|
72
|
+
score =
|
|
73
|
+
keywordMatch * 0.30
|
|
74
|
+
+ intentDomainMatch * 0.25
|
|
75
|
+
+ formatMatch * 0.15
|
|
76
|
+
+ assetRoleMatch * 0.10
|
|
77
|
+
+ styleMatch * 0.10
|
|
78
|
+
+ motionMatch * 0.05
|
|
79
|
+
+ constraintsMatch * 0.05
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
硬约束优先于分数:
|
|
83
|
+
|
|
84
|
+
- 竖屏需求优先 portrait 条目,如 `instagram-follow`, `tiktok-follow`, `spotify-card`, `flowchart-vertical`。
|
|
85
|
+
- 需要叠加字幕时优先 component:`caption-*`。
|
|
86
|
+
- 需要完整镜头时优先 block。
|
|
87
|
+
- `code-morph` 在 failedItems 中,默认不可选。
|
|
88
|
+
|
|
89
|
+
## Generation Mode Decision
|
|
90
|
+
|
|
91
|
+
默认规则:
|
|
92
|
+
|
|
93
|
+
- 用户说“用现有 catalog / 快速拼一个 / 调用 block / 组合这些条目”:选择 `assemble_existing_catalog_items`。
|
|
94
|
+
- 用户说“写 HTML 动画 / 生成一个新效果 / 自定义样式 / 按我的品牌做”:选择 `generate_new_hyperframes_html`。
|
|
95
|
+
- 如果 component 的 `install.includeSnippet` 只是 `<!-- paste from ... -->` 注释,不能直接作为最终 HTML;必须读取/粘贴真实 snippet,或者切到 `generate_new_hyperframes_html`。
|
|
96
|
+
- 如果需要原创 caption、shader、WebGL、3D 或复杂时间线,优先 `generate_new_hyperframes_html` 并遵循 patterns 文档。
|
|
97
|
+
|
|
98
|
+
必要时追问:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"purpose": "clarify_generation_mode",
|
|
103
|
+
"question": "你希望这次是快速组合现有 HyperFrames catalog 条目,还是生成一个新的完整 HTML 动画?",
|
|
104
|
+
"choices": ["快速组合现有条目", "生成新的完整 HTML 动画"]
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## AskUserQuestion Round 2: 样式和动效
|
|
109
|
+
|
|
110
|
+
在候选 items 出来后再问,问题要带候选上下文。
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"purpose": "clarify_style_motion",
|
|
115
|
+
"candidateContext": ["app-showcase", "vfx-iphone-device", "logo-outro"],
|
|
116
|
+
"questions": [
|
|
117
|
+
{
|
|
118
|
+
"id": "style",
|
|
119
|
+
"question": "这次更想要哪种视觉方向?",
|
|
120
|
+
"choices": ["Apple 风高级产品感", "电影感/暗调科技", "极简清爽", "社媒高能/动感"]
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"id": "motion",
|
|
124
|
+
"question": "动效更偏哪种节奏?",
|
|
125
|
+
"choices": ["稳重高级", "快节奏冲击", "柔和流动", "故障/赛博"]
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## HTML Generation Plan
|
|
132
|
+
|
|
133
|
+
生成 HTML 前输出 plan:
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"generationMode": "generate_new_hyperframes_html",
|
|
138
|
+
"selectedItems": [
|
|
139
|
+
{"id": "app-showcase", "role": "visual_reference"},
|
|
140
|
+
{"id": "caption-pill-karaoke", "role": "caption_reference"},
|
|
141
|
+
{"id": "logo-outro", "role": "outro_reference"}
|
|
142
|
+
],
|
|
143
|
+
"patternsRequired": true,
|
|
144
|
+
"assumptions": ["用户未提供 logo,先使用文字 logo 占位"],
|
|
145
|
+
"nextValidation": "请确认风格、文案、时长和画幅。"
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## HTML Generation Rules
|
|
150
|
+
|
|
151
|
+
### assemble_existing_catalog_items
|
|
152
|
+
|
|
153
|
+
- Block 可用 `data-composition-src` include。
|
|
154
|
+
- Component 的 paste comment 不是最终视觉;必须解析为真实 snippet 后粘贴。
|
|
155
|
+
- Wrapper composition 仍应有固定画幅和总时长。
|
|
156
|
+
|
|
157
|
+
### generate_new_hyperframes_html
|
|
158
|
+
|
|
159
|
+
必须遵循 `HyperFrames-AI-Generation-Patterns-codex.md`:
|
|
160
|
+
|
|
161
|
+
- 完整 block 使用 `data-composition-id`, `data-width`, `data-height`, `data-duration`。
|
|
162
|
+
- 主动画使用 `gsap.timeline({ paused: true })`。
|
|
163
|
+
- 注册到 `window.__timelines[id]`。
|
|
164
|
+
- CSS scoped 到 `[data-composition-id="..."]` 或唯一 component id。
|
|
165
|
+
- timeline 覆盖 declared duration,并 padding 到完整 duration。
|
|
166
|
+
- 字体、图片、GLTF、WebGL 等 async 资源 ready 后再注册 timeline。
|
|
167
|
+
- WebGL/Three/shader 必须有 fallback timeline。
|
|
168
|
+
|
|
169
|
+
## Pre-Render Quality Gates
|
|
170
|
+
|
|
171
|
+
渲染前必须检查:
|
|
172
|
+
|
|
173
|
+
- HTML 有固定 width/height/duration。
|
|
174
|
+
- Block 有 `data-composition-id`。
|
|
175
|
+
- 有 paused GSAP timeline 并注册 `window.__timelines[id]`。
|
|
176
|
+
- Timeline 覆盖 declared duration。
|
|
177
|
+
- 没有用 `Date.now()`、hover 或无限 RAF 驱动主进度。
|
|
178
|
+
- Async 资源处理不会导致空白 render。
|
|
179
|
+
- Component 是真实 snippet 或 attach 函数,不是 paste 注释。
|
|
180
|
+
- CSS selector scoped 或唯一。
|
|
181
|
+
- 文本在目标视频尺寸可读。
|
|
182
|
+
|
|
183
|
+
## User Validation
|
|
184
|
+
|
|
185
|
+
渲染前必须让用户确认:
|
|
186
|
+
|
|
187
|
+
- generation mode 是否符合预期。
|
|
188
|
+
- 选用/参考的 block/component 是否符合需求。
|
|
189
|
+
- 画幅和时长是否正确。
|
|
190
|
+
- 文案、代码、数据、logo 是否正确。
|
|
191
|
+
- 是否需要替换风格或动效。
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Static pre-render checks for HyperFrames HTML compositions."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import re
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def has_attr(html: str, name: str) -> bool:
|
|
13
|
+
return re.search(rf"\b{name}\s*=\s*['\"]?[^'\"\s>]+", html, re.IGNORECASE) is not None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def check_html(html: str, *, component: bool = False) -> tuple[list[str], list[str]]:
|
|
17
|
+
failures: list[str] = []
|
|
18
|
+
warnings: list[str] = []
|
|
19
|
+
|
|
20
|
+
if not component:
|
|
21
|
+
for attr in ("data-width", "data-height", "data-duration", "data-composition-id"):
|
|
22
|
+
if not has_attr(html, attr):
|
|
23
|
+
failures.append(f"missing {attr}")
|
|
24
|
+
|
|
25
|
+
if "<!-- paste from " in html:
|
|
26
|
+
failures.append("unresolved component paste placeholder")
|
|
27
|
+
|
|
28
|
+
paused_timeline = re.search(
|
|
29
|
+
r"gsap\s*\.\s*timeline\s*\(\s*\{[^}]*paused\s*:\s*true",
|
|
30
|
+
html,
|
|
31
|
+
re.IGNORECASE | re.DOTALL,
|
|
32
|
+
)
|
|
33
|
+
if not paused_timeline:
|
|
34
|
+
failures.append("missing gsap.timeline({ paused: true })")
|
|
35
|
+
|
|
36
|
+
if "window.__timelines" not in html:
|
|
37
|
+
failures.append("missing window.__timelines registration")
|
|
38
|
+
|
|
39
|
+
if re.search(r"\bDate\s*\.\s*now\s*\(", html):
|
|
40
|
+
failures.append("uses Date.now(); render progress must be timeline-driven")
|
|
41
|
+
|
|
42
|
+
if re.search(r"\bsetInterval\s*\(", html):
|
|
43
|
+
failures.append("uses setInterval(); primary render progress must be timeline-driven")
|
|
44
|
+
|
|
45
|
+
if re.search(r"\brequestAnimationFrame\s*\(", html) and "fallback" not in html.lower():
|
|
46
|
+
warnings.append("requestAnimationFrame found; ensure it is not the primary render clock and has render fallback")
|
|
47
|
+
|
|
48
|
+
if "<style" in html.lower() and "[data-composition-id" not in html:
|
|
49
|
+
warnings.append("style block found without [data-composition-id] scoped selector")
|
|
50
|
+
|
|
51
|
+
if len(re.findall(r"data-duration\s*=\s*['\"]?(\d+(?:\.\d+)?)", html)) > 1:
|
|
52
|
+
warnings.append("multiple data-duration values found; verify the intended render duration")
|
|
53
|
+
|
|
54
|
+
return failures, warnings
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def main() -> int:
|
|
58
|
+
parser = argparse.ArgumentParser(description="Validate HyperFrames HTML before render.")
|
|
59
|
+
parser.add_argument("html_file", help="Path to a HyperFrames HTML file")
|
|
60
|
+
parser.add_argument(
|
|
61
|
+
"--component",
|
|
62
|
+
action="store_true",
|
|
63
|
+
help="Validate a component snippet instead of a full block composition",
|
|
64
|
+
)
|
|
65
|
+
args = parser.parse_args()
|
|
66
|
+
|
|
67
|
+
path = Path(args.html_file)
|
|
68
|
+
if not path.exists():
|
|
69
|
+
print(f"FAIL: file does not exist: {path}", file=sys.stderr)
|
|
70
|
+
return 2
|
|
71
|
+
|
|
72
|
+
html = path.read_text(encoding="utf-8")
|
|
73
|
+
failures, warnings = check_html(html, component=args.component)
|
|
74
|
+
|
|
75
|
+
for warning in warnings:
|
|
76
|
+
print(f"WARN: {warning}", file=sys.stderr)
|
|
77
|
+
|
|
78
|
+
if failures:
|
|
79
|
+
for failure in failures:
|
|
80
|
+
print(f"FAIL: {failure}", file=sys.stderr)
|
|
81
|
+
return 1
|
|
82
|
+
|
|
83
|
+
print("OK: HyperFrames HTML passed static pre-render checks")
|
|
84
|
+
return 0
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
if __name__ == "__main__":
|
|
88
|
+
raise SystemExit(main())
|