hyperframes 0.1.13 → 0.1.15
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 +5 -1
- package/dist/cli.js +940 -380
- package/dist/hyperframe-runtime.js +8 -4
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +8 -4
- package/dist/skills/hyperframes-captions/SKILL.md +195 -29
- package/dist/skills/hyperframes-compose/SKILL.md +1 -0
- package/dist/skills/hyperframes-compose/house-style.md +2 -2
- package/dist/studio/assets/index-CLmYRLY-.css +1 -0
- package/dist/studio/assets/index-CRvFpc0E.js +84 -0
- package/dist/studio/index.html +2 -2
- package/dist/templates/_shared/CLAUDE.md +14 -2
- package/dist/templates/blank/compositions/captions.html +3 -0
- package/dist/templates/blank/index.html +1 -1
- package/dist/templates/decision-tree/compositions/decision_tree.html +420 -0
- package/dist/templates/decision-tree/index.html +56 -0
- package/dist/templates/generators.ts +32 -0
- package/dist/templates/kinetic-type/compositions/main-graphics.html +588 -0
- package/dist/templates/kinetic-type/index.html +61 -0
- package/dist/templates/nyt-graph/compositions/nyt-chart.html +450 -0
- package/dist/templates/nyt-graph/index.html +51 -0
- package/dist/templates/product-promo/assets/figma-cursors.svg +30 -0
- package/dist/templates/product-promo/assets/figma-logo-pieces.svg +15 -0
- package/dist/templates/product-promo/assets/figma-logo-pills.svg +6 -0
- package/dist/templates/product-promo/compositions/scene1-logo-intro.html +189 -0
- package/dist/templates/product-promo/compositions/scene2-4-canvas.html +570 -0
- package/dist/templates/product-promo/compositions/scene5-logo-outro.html +226 -0
- package/dist/templates/product-promo/index.html +158 -0
- package/package.json +2 -2
- package/dist/studio/assets/index-2uBPlHR_.css +0 -1
- package/dist/studio/assets/index-uQ8cgxb3.js +0 -92
package/dist/studio/index.html
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>HyperFrames Studio</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-CRvFpc0E.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CLmYRLY-.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
| Skill | Command | When to use |
|
|
8
8
|
| ------------------------ | ----------------------- | ------------------------------------------------------------------------------------------------------ |
|
|
9
9
|
| **hyperframes-compose** | `/hyperframes-compose` | Creating or editing ANY HTML composition — videos, animations, title cards, overlays, sub-compositions |
|
|
10
|
-
| **hyperframes-captions** | `/hyperframes-captions` |
|
|
10
|
+
| **hyperframes-captions** | `/hyperframes-captions` | Any text synced to audio: captions, subtitles, lyrics, karaoke. Also covers transcription strategy. |
|
|
11
11
|
| **gsap-core** | `/gsap-core` | GSAP tweens: `gsap.to()`, `from()`, `fromTo()`, easing, stagger, defaults |
|
|
12
12
|
| **gsap-timeline** | `/gsap-timeline` | Timeline sequencing, position parameter, labels, nesting |
|
|
13
13
|
| **gsap-performance** | `/gsap-performance` | Animation performance — transforms over layout props, will-change, batching |
|
|
@@ -21,7 +21,9 @@
|
|
|
21
21
|
```bash
|
|
22
22
|
npx hyperframes dev # preview in browser (studio editor)
|
|
23
23
|
npx hyperframes render # render to MP4
|
|
24
|
-
npx hyperframes lint # validate compositions
|
|
24
|
+
npx hyperframes lint # validate compositions (errors + warnings)
|
|
25
|
+
npx hyperframes lint --verbose # include info-level findings
|
|
26
|
+
npx hyperframes lint --json # machine-readable output for CI
|
|
25
27
|
npx hyperframes docs <topic> # reference docs in terminal
|
|
26
28
|
```
|
|
27
29
|
|
|
@@ -48,6 +50,16 @@ https://hyperframes.heygen.com/llms.txt
|
|
|
48
50
|
- `meta.json` — project metadata (id, name)
|
|
49
51
|
- `transcript.json` — whisper word-level transcript (if generated)
|
|
50
52
|
|
|
53
|
+
## Linting — ALWAYS RUN AFTER CHANGES
|
|
54
|
+
|
|
55
|
+
After creating or editing any `.html` composition, **always** run the linter before considering the task complete:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npx hyperframes lint
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Fix all errors before presenting the result. Warnings are informational and usually safe to ignore.
|
|
62
|
+
|
|
51
63
|
## Key Rules
|
|
52
64
|
|
|
53
65
|
1. Every timed element needs `data-start`, `data-duration`, and `data-track-index`
|
|
@@ -37,6 +37,8 @@
|
|
|
37
37
|
0 2px 8px rgba(0, 0, 0, 0.8),
|
|
38
38
|
0 0 2px rgba(0, 0, 0, 0.9);
|
|
39
39
|
white-space: nowrap;
|
|
40
|
+
max-width: 1600px;
|
|
41
|
+
overflow: hidden;
|
|
40
42
|
}
|
|
41
43
|
</style>
|
|
42
44
|
|
|
@@ -83,6 +85,7 @@
|
|
|
83
85
|
index < lines.length - 1 ? Math.min(line.end, lines[index + 1].start) : line.end;
|
|
84
86
|
|
|
85
87
|
tl.to(el, { opacity: 0, y: -10, duration: 0.25, ease: "power2.in" }, hideTime - 0.25);
|
|
88
|
+
tl.set(el, { opacity: 0, visibility: "hidden" }, hideTime);
|
|
86
89
|
});
|
|
87
90
|
|
|
88
91
|
window.__timelines["captions"] = tl;
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
>
|
|
32
32
|
<video
|
|
33
33
|
id="a-roll"
|
|
34
|
+
class="clip"
|
|
34
35
|
src="__VIDEO_SRC__"
|
|
35
36
|
muted
|
|
36
37
|
playsinline
|
|
37
|
-
data-start="0"
|
|
38
38
|
data-duration="__VIDEO_DURATION__"
|
|
39
39
|
data-track-index="0"
|
|
40
40
|
style="position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover"
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
<template id="decision-tree-template">
|
|
2
|
+
<div data-composition-id="decision-tree" data-width="1920" data-height="1080" data-duration="12">
|
|
3
|
+
<div class="canvas">
|
|
4
|
+
<svg class="connectors" width="1920" height="1080" viewBox="0 0 1920 1080">
|
|
5
|
+
<!-- Level 1 to 2 -->
|
|
6
|
+
<path
|
|
7
|
+
id="path-1-L"
|
|
8
|
+
class="connector"
|
|
9
|
+
d="M 960 130 L 960 200 L 600 200 L 600 270"
|
|
10
|
+
fill="none"
|
|
11
|
+
stroke="#333"
|
|
12
|
+
stroke-width="4"
|
|
13
|
+
/>
|
|
14
|
+
<path
|
|
15
|
+
id="path-1-R"
|
|
16
|
+
class="connector"
|
|
17
|
+
d="M 960 130 L 960 200 L 1320 200 L 1320 270"
|
|
18
|
+
fill="none"
|
|
19
|
+
stroke="#333"
|
|
20
|
+
stroke-width="4"
|
|
21
|
+
/>
|
|
22
|
+
|
|
23
|
+
<!-- Level 2 to 3 (Left side) -->
|
|
24
|
+
<path
|
|
25
|
+
id="path-2-LL"
|
|
26
|
+
class="connector"
|
|
27
|
+
d="M 600 330 L 600 400 L 400 400 L 400 470"
|
|
28
|
+
fill="none"
|
|
29
|
+
stroke="#333"
|
|
30
|
+
stroke-width="4"
|
|
31
|
+
/>
|
|
32
|
+
<path
|
|
33
|
+
id="path-2-LR"
|
|
34
|
+
class="connector"
|
|
35
|
+
d="M 600 330 L 600 400 L 800 400 L 800 470"
|
|
36
|
+
fill="none"
|
|
37
|
+
stroke="#333"
|
|
38
|
+
stroke-width="4"
|
|
39
|
+
/>
|
|
40
|
+
|
|
41
|
+
<!-- Level 2 to 3 (Right side) -->
|
|
42
|
+
<path
|
|
43
|
+
id="path-2-RL"
|
|
44
|
+
class="connector"
|
|
45
|
+
d="M 1320 330 L 1320 400 L 1120 400 L 1120 470"
|
|
46
|
+
fill="none"
|
|
47
|
+
stroke="#333"
|
|
48
|
+
stroke-width="4"
|
|
49
|
+
/>
|
|
50
|
+
<path
|
|
51
|
+
id="path-2-RR"
|
|
52
|
+
class="connector"
|
|
53
|
+
d="M 1320 330 L 1320 400 L 1520 400 L 1520 470"
|
|
54
|
+
fill="none"
|
|
55
|
+
stroke="#333"
|
|
56
|
+
stroke-width="4"
|
|
57
|
+
/>
|
|
58
|
+
</svg>
|
|
59
|
+
|
|
60
|
+
<div id="label-yes" class="connector-label">Yes</div>
|
|
61
|
+
<div id="label-not-sure" class="connector-label">Not sure</div>
|
|
62
|
+
|
|
63
|
+
<!-- Level 1 -->
|
|
64
|
+
<div id="node-root" class="node sticky yellow" style="left: 960px; top: 100px">
|
|
65
|
+
Should I learn to code?
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<!-- Level 2 -->
|
|
69
|
+
<div id="node-yes" class="node sticky green" style="left: 600px; top: 300px">Yes</div>
|
|
70
|
+
<div id="node-not-sure" class="node sticky peach" style="left: 1320px; top: 300px">
|
|
71
|
+
Not sure
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<!-- Level 3 -->
|
|
75
|
+
<div id="node-python" class="node sticky lavender" style="left: 400px; top: 500px">
|
|
76
|
+
<div style="position: relative; display: inline-block">
|
|
77
|
+
<span id="python-text">Start with Pythom</span>
|
|
78
|
+
<svg class="squiggle-container" width="64" height="6" viewBox="0 0 64 6">
|
|
79
|
+
<path
|
|
80
|
+
id="squiggle"
|
|
81
|
+
d="M 0 3 Q 2 0 4 3 T 8 3 T 12 3 T 16 3 T 20 3 T 24 3 T 28 3 T 32 3 T 36 3 T 40 3 T 44 3 T 48 3 T 52 3 T 56 3 T 60 3 T 64 3"
|
|
82
|
+
fill="none"
|
|
83
|
+
stroke="#E53935"
|
|
84
|
+
stroke-width="2"
|
|
85
|
+
/>
|
|
86
|
+
</svg>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
<div id="node-nocode" class="node sticky blue" style="left: 800px; top: 500px">
|
|
90
|
+
Try no-code first
|
|
91
|
+
</div>
|
|
92
|
+
<div id="node-website" class="node sticky pink" style="left: 1120px; top: 500px">
|
|
93
|
+
Build a personal website
|
|
94
|
+
</div>
|
|
95
|
+
<div id="node-course" class="node sticky yellow" style="left: 1520px; top: 500px">
|
|
96
|
+
Take a free intro course
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<!-- Cursor -->
|
|
100
|
+
<div id="cursor" class="cursor-container">
|
|
101
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
|
|
102
|
+
<path
|
|
103
|
+
d="M5.65376 12.3673H5.46026L5.31717 12.4976L0.500002 16.8829L0.500002 1.19841L11.7841 12.3673H5.65376Z"
|
|
104
|
+
fill="white"
|
|
105
|
+
stroke="black"
|
|
106
|
+
/>
|
|
107
|
+
</svg>
|
|
108
|
+
<div class="cursor-tag">You</div>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<!-- Emoji -->
|
|
112
|
+
<div id="emoji-thumb" class="emoji">👍</div>
|
|
113
|
+
|
|
114
|
+
<!-- Overlay -->
|
|
115
|
+
<div id="fade-overlay"></div>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<style>
|
|
119
|
+
[data-composition-id="decision-tree"] {
|
|
120
|
+
width: 1920px;
|
|
121
|
+
height: 1080px;
|
|
122
|
+
background-color: #ffffff;
|
|
123
|
+
background-image: radial-gradient(#e5e5e5 1px, transparent 1px);
|
|
124
|
+
background-size: 20px 20px;
|
|
125
|
+
position: absolute;
|
|
126
|
+
overflow: hidden;
|
|
127
|
+
font-family: "Inter", sans-serif;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
[data-composition-id="decision-tree"] .canvas {
|
|
131
|
+
width: 100%;
|
|
132
|
+
height: 100%;
|
|
133
|
+
position: absolute;
|
|
134
|
+
transform: scale(1.2);
|
|
135
|
+
transform-origin: 960px 300px;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
[data-composition-id="decision-tree"] .node {
|
|
139
|
+
position: absolute;
|
|
140
|
+
transform: translate(-50%, -50%) scale(0);
|
|
141
|
+
padding: 16px 24px;
|
|
142
|
+
border-radius: 12px;
|
|
143
|
+
font-size: 18px;
|
|
144
|
+
font-weight: 500;
|
|
145
|
+
color: #333;
|
|
146
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
147
|
+
white-space: nowrap;
|
|
148
|
+
display: flex;
|
|
149
|
+
flex-direction: column;
|
|
150
|
+
align-items: center;
|
|
151
|
+
justify-content: center;
|
|
152
|
+
z-index: 10;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
[data-composition-id="decision-tree"] .node.yellow {
|
|
156
|
+
background-color: #e8d44d;
|
|
157
|
+
}
|
|
158
|
+
[data-composition-id="decision-tree"] .node.green {
|
|
159
|
+
background-color: #c2e8a0;
|
|
160
|
+
}
|
|
161
|
+
[data-composition-id="decision-tree"] .node.peach {
|
|
162
|
+
background-color: #f5c5a3;
|
|
163
|
+
}
|
|
164
|
+
[data-composition-id="decision-tree"] .node.lavender {
|
|
165
|
+
background-color: #d4c5f9;
|
|
166
|
+
}
|
|
167
|
+
[data-composition-id="decision-tree"] .node.blue {
|
|
168
|
+
background-color: #a8d8f0;
|
|
169
|
+
}
|
|
170
|
+
[data-composition-id="decision-tree"] .node.pink {
|
|
171
|
+
background-color: #f8b4c8;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
[data-composition-id="decision-tree"] .connectors {
|
|
175
|
+
position: absolute;
|
|
176
|
+
top: 0;
|
|
177
|
+
left: 0;
|
|
178
|
+
z-index: 5;
|
|
179
|
+
pointer-events: none;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
[data-composition-id="decision-tree"] .connector {
|
|
183
|
+
stroke-dasharray: 1000;
|
|
184
|
+
stroke-dashoffset: 1000;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
[data-composition-id="decision-tree"] .connector-label {
|
|
188
|
+
position: absolute;
|
|
189
|
+
font-size: 14px;
|
|
190
|
+
font-weight: 600;
|
|
191
|
+
color: #333;
|
|
192
|
+
background: white;
|
|
193
|
+
padding: 2px 6px;
|
|
194
|
+
border-radius: 4px;
|
|
195
|
+
opacity: 0;
|
|
196
|
+
z-index: 6;
|
|
197
|
+
transform: translate(-50%, -50%);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
#label-yes {
|
|
201
|
+
left: 780px;
|
|
202
|
+
top: 230px;
|
|
203
|
+
}
|
|
204
|
+
#label-not-sure {
|
|
205
|
+
left: 1140px;
|
|
206
|
+
top: 230px;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
[data-composition-id="decision-tree"] .cursor-container {
|
|
210
|
+
position: absolute;
|
|
211
|
+
left: 1920px;
|
|
212
|
+
top: 1080px;
|
|
213
|
+
z-index: 100;
|
|
214
|
+
pointer-events: none;
|
|
215
|
+
display: flex;
|
|
216
|
+
align-items: flex-start;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
[data-composition-id="decision-tree"] .cursor-tag {
|
|
220
|
+
background-color: #9747ff;
|
|
221
|
+
color: white;
|
|
222
|
+
padding: 2px 8px;
|
|
223
|
+
border-radius: 4px;
|
|
224
|
+
font-size: 12px;
|
|
225
|
+
font-weight: 600;
|
|
226
|
+
margin-left: 4px;
|
|
227
|
+
margin-top: 12px;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
[data-composition-id="decision-tree"] .squiggle-container {
|
|
231
|
+
position: absolute;
|
|
232
|
+
right: 0;
|
|
233
|
+
bottom: -4px;
|
|
234
|
+
opacity: 0;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
[data-composition-id="decision-tree"] .emoji {
|
|
238
|
+
position: absolute;
|
|
239
|
+
font-size: 32px;
|
|
240
|
+
left: 510px;
|
|
241
|
+
top: 500px;
|
|
242
|
+
transform: translate(-50%, -50%) scale(0);
|
|
243
|
+
z-index: 20;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
[data-composition-id="decision-tree"] #fade-overlay {
|
|
247
|
+
position: absolute;
|
|
248
|
+
top: 0;
|
|
249
|
+
left: 0;
|
|
250
|
+
width: 100%;
|
|
251
|
+
height: 100%;
|
|
252
|
+
background-color: white;
|
|
253
|
+
opacity: 0;
|
|
254
|
+
z-index: 1000;
|
|
255
|
+
pointer-events: none;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.selection-border {
|
|
259
|
+
position: absolute;
|
|
260
|
+
top: -4px;
|
|
261
|
+
left: -4px;
|
|
262
|
+
right: -4px;
|
|
263
|
+
bottom: -4px;
|
|
264
|
+
border: 2px solid #0b84f3;
|
|
265
|
+
border-radius: 16px;
|
|
266
|
+
pointer-events: none;
|
|
267
|
+
opacity: 0;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.text-highlight {
|
|
271
|
+
background-color: #d0e4ff;
|
|
272
|
+
}
|
|
273
|
+
</style>
|
|
274
|
+
|
|
275
|
+
<script>
|
|
276
|
+
(function () {
|
|
277
|
+
const tl = gsap.timeline({ paused: true });
|
|
278
|
+
|
|
279
|
+
// 1. Root node scales from 0→100% over 0.4s
|
|
280
|
+
tl.to("#node-root", { scale: 1, duration: 0.4, ease: "power2.out" });
|
|
281
|
+
|
|
282
|
+
// 2. Hold 0.6s
|
|
283
|
+
tl.addLabel("hold1", "+=0.6");
|
|
284
|
+
|
|
285
|
+
// 3. Two elbow connectors draw downward-left and downward-right simultaneously (0.5s)
|
|
286
|
+
tl.to(
|
|
287
|
+
".connector#path-1-L, .connector#path-1-R",
|
|
288
|
+
{
|
|
289
|
+
strokeDashoffset: 0,
|
|
290
|
+
duration: 0.5,
|
|
291
|
+
ease: "none",
|
|
292
|
+
},
|
|
293
|
+
"hold1",
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
// Labels fade in at midpoint
|
|
297
|
+
tl.to(
|
|
298
|
+
"#label-yes, #label-not-sure",
|
|
299
|
+
{
|
|
300
|
+
opacity: 1,
|
|
301
|
+
duration: 0.2,
|
|
302
|
+
},
|
|
303
|
+
"hold1+=0.25",
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
// 4. Level 2 nodes scale in at endpoints with 0.2s stagger
|
|
307
|
+
tl.to("#node-yes", { scale: 1, duration: 0.4, ease: "back.out(1.7)" }, "hold1+=0.4");
|
|
308
|
+
tl.to("#node-not-sure", { scale: 1, duration: 0.4, ease: "back.out(1.7)" }, "hold1+=0.6");
|
|
309
|
+
|
|
310
|
+
// 5. Hold 0.5s
|
|
311
|
+
tl.addLabel("hold2", "+=0.5");
|
|
312
|
+
|
|
313
|
+
// 6. Four connectors draw down from level 2 (0.4s)
|
|
314
|
+
tl.to(
|
|
315
|
+
".connector#path-2-LL, .connector#path-2-LR, .connector#path-2-RL, .connector#path-2-RR",
|
|
316
|
+
{
|
|
317
|
+
strokeDashoffset: 0,
|
|
318
|
+
duration: 0.4,
|
|
319
|
+
ease: "none",
|
|
320
|
+
},
|
|
321
|
+
"hold2",
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
// 7. Four leaf nodes scale in with 0.15s stagger
|
|
325
|
+
const leafNodes = ["#node-python", "#node-nocode", "#node-website", "#node-course"];
|
|
326
|
+
leafNodes.forEach((id, i) => {
|
|
327
|
+
tl.to(id, { scale: 1, duration: 0.4, ease: "back.out(1.7)" }, `hold2+=${0.3 + i * 0.15}`);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Red wavy underline appears with text
|
|
331
|
+
tl.to(".squiggle-container", { opacity: 1, duration: 0.1 }, "hold2+=0.3");
|
|
332
|
+
|
|
333
|
+
// 8. Hold 0.8s
|
|
334
|
+
tl.addLabel("hold3", "+=0.8");
|
|
335
|
+
|
|
336
|
+
// 9. Cursor drifts in from bottom-right (1s) to lavender sticky
|
|
337
|
+
tl.to(
|
|
338
|
+
"#cursor",
|
|
339
|
+
{
|
|
340
|
+
left: 450,
|
|
341
|
+
top: 540,
|
|
342
|
+
duration: 1,
|
|
343
|
+
ease: "power1.inOut",
|
|
344
|
+
},
|
|
345
|
+
"hold3",
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
// 10. Cursor clicks. Selection border appears.
|
|
349
|
+
tl.add(() => {
|
|
350
|
+
const node = document.querySelector("#node-python");
|
|
351
|
+
if (!node) return;
|
|
352
|
+
if (!node.querySelector(".selection-border")) {
|
|
353
|
+
const border = document.createElement("div");
|
|
354
|
+
border.className = "selection-border";
|
|
355
|
+
node.appendChild(border);
|
|
356
|
+
}
|
|
357
|
+
gsap.set(".selection-border", { opacity: 1 });
|
|
358
|
+
}, "hold3+=1");
|
|
359
|
+
|
|
360
|
+
// Visual click feedback
|
|
361
|
+
tl.to("#cursor", { scale: 0.8, duration: 0.05, yoyo: true, repeat: 1 }, "hold3+=1");
|
|
362
|
+
|
|
363
|
+
// 11. Hold 0.3s
|
|
364
|
+
tl.addLabel("hold4", "+=0.3");
|
|
365
|
+
|
|
366
|
+
// 12. Cursor double-clicks "Pythom" — highlight in blue
|
|
367
|
+
tl.to("#cursor", { scale: 0.8, duration: 0.05, yoyo: true, repeat: 3 }, "hold4");
|
|
368
|
+
tl.add(() => {
|
|
369
|
+
const textEl = document.querySelector("#python-text");
|
|
370
|
+
if (!textEl) return;
|
|
371
|
+
textEl.innerHTML = 'Start with <span class="text-highlight">Pythom</span>';
|
|
372
|
+
}, "hold4+=0.1");
|
|
373
|
+
|
|
374
|
+
// 13. Hold 0.2s
|
|
375
|
+
tl.addLabel("hold5", "+=0.2");
|
|
376
|
+
|
|
377
|
+
// 14. Highlighted text deletes and "Python" types in (fast)
|
|
378
|
+
// Squiggle disappears when last char lands
|
|
379
|
+
const typingStart = tl.labels["hold5"];
|
|
380
|
+
const words = ["P", "Py", "Pyt", "Pyth", "Pytho", "Python"];
|
|
381
|
+
words.forEach((word, i) => {
|
|
382
|
+
tl.add(
|
|
383
|
+
() => {
|
|
384
|
+
const el = document.querySelector("#python-text");
|
|
385
|
+
if (el) el.innerHTML = `Start with <span class="text-highlight">${word}</span>`;
|
|
386
|
+
},
|
|
387
|
+
typingStart + i * 0.05,
|
|
388
|
+
);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// Remove highlight and squiggle at the end of typing
|
|
392
|
+
const typingEnd = typingStart + words.length * 0.05;
|
|
393
|
+
tl.add(() => {
|
|
394
|
+
const pyEl = document.querySelector("#python-text");
|
|
395
|
+
if (pyEl) pyEl.innerHTML = "Start with Python";
|
|
396
|
+
gsap.set(".squiggle-container", { opacity: 0 });
|
|
397
|
+
}, typingEnd);
|
|
398
|
+
|
|
399
|
+
// 15. Hold 0.5s
|
|
400
|
+
tl.addLabel("hold6", typingEnd + 0.5);
|
|
401
|
+
|
|
402
|
+
// 16. Cursor moves slightly and clicks away to deselect
|
|
403
|
+
tl.to("#cursor", { left: 500, top: 580, duration: 0.3 }, "hold6");
|
|
404
|
+
tl.to("#cursor", { scale: 0.8, duration: 0.05, yoyo: true, repeat: 1 }, "hold6+=0.3");
|
|
405
|
+
tl.to(".selection-border", { opacity: 0, duration: 0.1 }, "hold6+=0.3");
|
|
406
|
+
|
|
407
|
+
// 17. 👍 emoji pops in
|
|
408
|
+
tl.to("#emoji-thumb", { scale: 1, duration: 0.15, ease: "back.out(2)" }, "hold6+=0.4");
|
|
409
|
+
|
|
410
|
+
// 18. Hold 2s
|
|
411
|
+
tl.addLabel("hold7", "+=2");
|
|
412
|
+
|
|
413
|
+
// 19. Fade entire screen to white over 0.5s
|
|
414
|
+
tl.to("#fade-overlay", { opacity: 1, duration: 0.5 }, "hold7");
|
|
415
|
+
|
|
416
|
+
window.__timelines["decision-tree"] = tl;
|
|
417
|
+
})();
|
|
418
|
+
</script>
|
|
419
|
+
</div>
|
|
420
|
+
</template>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=1920, height=1080" />
|
|
6
|
+
<title>Decision Tree</title>
|
|
7
|
+
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
|
8
|
+
<style>
|
|
9
|
+
* {
|
|
10
|
+
margin: 0;
|
|
11
|
+
padding: 0;
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
}
|
|
14
|
+
html,
|
|
15
|
+
body {
|
|
16
|
+
margin: 0;
|
|
17
|
+
width: 1920px;
|
|
18
|
+
height: 1080px;
|
|
19
|
+
overflow: hidden;
|
|
20
|
+
background: #ffffff;
|
|
21
|
+
font-family: "Inter", sans-serif;
|
|
22
|
+
}
|
|
23
|
+
</style>
|
|
24
|
+
<link
|
|
25
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=block"
|
|
26
|
+
rel="stylesheet"
|
|
27
|
+
/>
|
|
28
|
+
</head>
|
|
29
|
+
<body>
|
|
30
|
+
<div
|
|
31
|
+
id="root"
|
|
32
|
+
data-composition-id="main"
|
|
33
|
+
data-start="0"
|
|
34
|
+
data-duration="15"
|
|
35
|
+
data-width="1920"
|
|
36
|
+
data-height="1080"
|
|
37
|
+
>
|
|
38
|
+
<div
|
|
39
|
+
id="decision-tree-comp"
|
|
40
|
+
data-composition-id="decision-tree"
|
|
41
|
+
data-composition-src="compositions/decision_tree.html"
|
|
42
|
+
data-start="0"
|
|
43
|
+
data-duration="15"
|
|
44
|
+
data-track-index="0"
|
|
45
|
+
data-width="1920"
|
|
46
|
+
data-height="1080"
|
|
47
|
+
></div>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<script>
|
|
51
|
+
window.__timelines = window.__timelines || {};
|
|
52
|
+
const tl = gsap.timeline({ paused: true });
|
|
53
|
+
window.__timelines["main"] = tl;
|
|
54
|
+
</script>
|
|
55
|
+
</body>
|
|
56
|
+
</html>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type TemplateId =
|
|
2
|
+
| "blank"
|
|
3
|
+
| "warm-grain"
|
|
4
|
+
| "play-mode"
|
|
5
|
+
| "swiss-grid"
|
|
6
|
+
| "vignelli"
|
|
7
|
+
| "decision-tree"
|
|
8
|
+
| "kinetic-type"
|
|
9
|
+
| "product-promo"
|
|
10
|
+
| "nyt-graph";
|
|
11
|
+
|
|
12
|
+
export interface TemplateOption {
|
|
13
|
+
id: TemplateId;
|
|
14
|
+
label: string;
|
|
15
|
+
hint: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const TEMPLATES: TemplateOption[] = [
|
|
19
|
+
{ id: "blank", label: "Blank", hint: "Empty composition — just the scaffolding" },
|
|
20
|
+
{ id: "warm-grain", label: "Warm Grain", hint: "Cream aesthetic with grain texture" },
|
|
21
|
+
{ id: "play-mode", label: "Play Mode", hint: "Playful elastic animations" },
|
|
22
|
+
{ id: "swiss-grid", label: "Swiss Grid", hint: "Structured grid layout" },
|
|
23
|
+
{ id: "vignelli", label: "Vignelli", hint: "Bold typography with red accents" },
|
|
24
|
+
{ id: "decision-tree", label: "Decision Tree", hint: "Animated flowchart with branching paths" },
|
|
25
|
+
{ id: "kinetic-type", label: "Kinetic Type", hint: "Bold kinetic typography promo" },
|
|
26
|
+
{
|
|
27
|
+
id: "product-promo",
|
|
28
|
+
label: "Product Promo",
|
|
29
|
+
hint: "Multi-scene product showcase with SVG assets",
|
|
30
|
+
},
|
|
31
|
+
{ id: "nyt-graph", label: "NYT Graph", hint: "Animated data chart in print editorial style" },
|
|
32
|
+
];
|