hyperframes 0.1.14 → 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/dist/cli.js CHANGED
@@ -422,7 +422,7 @@ var VERSION;
422
422
  var init_version = __esm({
423
423
  "src/version.ts"() {
424
424
  "use strict";
425
- VERSION = true ? "0.1.14" : "0.0.0-dev";
425
+ VERSION = true ? "0.1.15" : "0.0.0-dev";
426
426
  }
427
427
  });
428
428
 
@@ -193,7 +193,7 @@ Group size affects pacing. Fast content needs fast caption turnover.
193
193
  - **Conversational:** 3-5 words per group. Natural phrase length.
194
194
  - **Measured/calm:** 4-6 words per group. Longer groups match slower pace.
195
195
 
196
- Break groups on sentence boundaries (`.` `?` `!`), pauses (>150ms gap), or max word count — whichever comes first.
196
+ Break groups on sentence boundaries (period, question mark, exclamation), pauses (150ms+ gap), or max word count — whichever comes first.
197
197
 
198
198
  ## Positioning
199
199
 
@@ -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
+ ];