repotrailer 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/.codex-plugin/plugin.json +22 -0
- package/LICENSE +21 -0
- package/README.md +225 -0
- package/action.yml +38 -0
- package/assets/cover.svg +16 -0
- package/assets/demo-preview.png +0 -0
- package/bin/repotrailer.js +9 -0
- package/llms.txt +54 -0
- package/package.json +61 -0
- package/scripts/growth-check.mjs +337 -0
- package/scripts/publish-readiness.mjs +104 -0
- package/skills/repotrailer/SKILL.md +52 -0
- package/src/analyze.js +405 -0
- package/src/cli.js +156 -0
- package/src/hyperframes.js +588 -0
- package/src/index.js +7 -0
- package/src/launch-kit.js +217 -0
- package/src/storyboard.js +71 -0
- package/src/utils.js +73 -0
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
import { copyFile, mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
import { escapeHtml, run } from "./utils.js";
|
|
6
|
+
|
|
7
|
+
function titleClass(value) {
|
|
8
|
+
const length = String(value).length;
|
|
9
|
+
if (length > 54) {
|
|
10
|
+
return "title-long";
|
|
11
|
+
}
|
|
12
|
+
if (length > 32) {
|
|
13
|
+
return "title-medium";
|
|
14
|
+
}
|
|
15
|
+
return "title-short";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function sceneBody(scene, index) {
|
|
19
|
+
const metrics = scene.metrics
|
|
20
|
+
? `<div class="metrics entrance entrance-metrics">
|
|
21
|
+
${scene.metrics.map((metric) => `<div class="metric">
|
|
22
|
+
<strong>${escapeHtml(metric.value)}</strong>
|
|
23
|
+
<span>${escapeHtml(metric.label)}</span>
|
|
24
|
+
</div>`).join("")}
|
|
25
|
+
</div>`
|
|
26
|
+
: "";
|
|
27
|
+
|
|
28
|
+
const terminal = scene.kind === "install"
|
|
29
|
+
? `<div class="terminal entrance entrance-terminal">
|
|
30
|
+
<span class="prompt">$</span>
|
|
31
|
+
<code>${escapeHtml(scene.title)}</code>
|
|
32
|
+
<span class="cursor"></span>
|
|
33
|
+
</div>`
|
|
34
|
+
: "";
|
|
35
|
+
|
|
36
|
+
const title = scene.kind === "install"
|
|
37
|
+
? "One command."
|
|
38
|
+
: escapeHtml(scene.title);
|
|
39
|
+
|
|
40
|
+
return `<section
|
|
41
|
+
class="hf-scene scene-${escapeHtml(scene.kind)}"
|
|
42
|
+
id="scene-${index}"
|
|
43
|
+
data-scene-index="${index}"
|
|
44
|
+
data-layout-allow-overflow
|
|
45
|
+
>
|
|
46
|
+
<div class="grid" data-layout-ignore></div>
|
|
47
|
+
<div class="orb orb-hot entrance entrance-orb" data-layout-allow-overflow></div>
|
|
48
|
+
<div class="orb orb-accent entrance entrance-orb" data-layout-allow-overflow></div>
|
|
49
|
+
<div class="index-mark entrance entrance-index">${String(index + 1).padStart(2, "0")}</div>
|
|
50
|
+
<div class="scene-content">
|
|
51
|
+
<p class="eyebrow entrance entrance-eyebrow">${escapeHtml(scene.eyebrow)}</p>
|
|
52
|
+
<h2 class="headline ${titleClass(title)} entrance entrance-title">${title}</h2>
|
|
53
|
+
${terminal}
|
|
54
|
+
<p class="copy entrance entrance-copy">${escapeHtml(scene.body)}</p>
|
|
55
|
+
${metrics}
|
|
56
|
+
</div>
|
|
57
|
+
<div class="rail entrance entrance-rail">
|
|
58
|
+
<span>REPOTRAILER</span>
|
|
59
|
+
<span>${String(scene.start).padStart(4, "0")}S</span>
|
|
60
|
+
</div>
|
|
61
|
+
</section>`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function animationScript(scenes) {
|
|
65
|
+
const entrances = scenes.map((scene, index) => {
|
|
66
|
+
const root = `#scene-${index}`;
|
|
67
|
+
const direction = index % 2 ? -1 : 1;
|
|
68
|
+
const ambientStart = Number((scene.start + 0.84).toFixed(2));
|
|
69
|
+
const repeats = Math.max(0, Math.ceil(scene.duration / 1.8) - 2);
|
|
70
|
+
const terminalEntrance = scene.kind === "install"
|
|
71
|
+
? `
|
|
72
|
+
tl.from("${root} .entrance-terminal", {
|
|
73
|
+
x: -90, scale: .96, opacity: 0, duration: .5, ease: "back.out(1.7)"
|
|
74
|
+
}, ${Number((scene.start + 0.3).toFixed(2))});`
|
|
75
|
+
: "";
|
|
76
|
+
const metricsEntrance = scene.metrics
|
|
77
|
+
? `
|
|
78
|
+
tl.from("${root} .entrance-metrics .metric", {
|
|
79
|
+
y: 54, opacity: 0, duration: .4, stagger: .09, ease: "back.out(1.5)"
|
|
80
|
+
}, ${Number((scene.start + 0.34).toFixed(2))});`
|
|
81
|
+
: "";
|
|
82
|
+
|
|
83
|
+
return ` tl.from("${root} .entrance-eyebrow", {
|
|
84
|
+
y: 46, opacity: 0, duration: .46, ease: "expo.out"
|
|
85
|
+
}, ${Number((scene.start + 0.08).toFixed(2))});
|
|
86
|
+
tl.from("${root} .entrance-title", {
|
|
87
|
+
y: 78, rotation: ${index % 2 ? 1.8 : -1.4}, opacity: 0,
|
|
88
|
+
duration: .58, ease: "power4.out"
|
|
89
|
+
}, ${Number((scene.start + 0.16).toFixed(2))});${terminalEntrance}
|
|
90
|
+
tl.from("${root} .entrance-copy", {
|
|
91
|
+
x: ${52 * direction}, opacity: 0, duration: .44, ease: "power3.out"
|
|
92
|
+
}, ${Number((scene.start + 0.36).toFixed(2))});${metricsEntrance}
|
|
93
|
+
tl.from("${root} .entrance-index", {
|
|
94
|
+
scale: .72, rotation: -5, opacity: 0, duration: .62, ease: "expo.out"
|
|
95
|
+
}, ${Number((scene.start + 0.12).toFixed(2))});
|
|
96
|
+
tl.from("${root} .entrance-rail", {
|
|
97
|
+
y: -42, opacity: 0, duration: .38, ease: "power2.out"
|
|
98
|
+
}, ${Number((scene.start + 0.48).toFixed(2))});
|
|
99
|
+
tl.from("${root} .entrance-orb", {
|
|
100
|
+
scale: .64, opacity: 0, duration: .72, stagger: .07, ease: "circ.out"
|
|
101
|
+
}, ${Number((scene.start + 0.04).toFixed(2))});
|
|
102
|
+
tl.to("${root} .orb-accent", {
|
|
103
|
+
scale: 1.035, x: ${16 * direction}, duration: .9,
|
|
104
|
+
repeat: ${repeats}, yoyo: true, ease: "sine.inOut"
|
|
105
|
+
}, ${ambientStart});`;
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const transitions = scenes.slice(1).map((scene, offset) => {
|
|
109
|
+
const index = offset + 1;
|
|
110
|
+
const previous = index - 1;
|
|
111
|
+
const transitionStart = Number((scene.start - 0.34).toFixed(2));
|
|
112
|
+
const direction = index % 2 === 0 ? 1 : -1;
|
|
113
|
+
const flash = scene.kind === "proof"
|
|
114
|
+
? `
|
|
115
|
+
tl.fromTo(".transition-flash", {
|
|
116
|
+
opacity: 0
|
|
117
|
+
}, {
|
|
118
|
+
opacity: .82,
|
|
119
|
+
duration: .11,
|
|
120
|
+
ease: "expo.in",
|
|
121
|
+
immediateRender: false
|
|
122
|
+
}, ${transitionStart});
|
|
123
|
+
tl.to(".transition-flash", {
|
|
124
|
+
opacity: 0,
|
|
125
|
+
duration: .17,
|
|
126
|
+
ease: "expo.out"
|
|
127
|
+
}, ${Number((transitionStart + 0.12).toFixed(2))});`
|
|
128
|
+
: "";
|
|
129
|
+
|
|
130
|
+
return ` tl.to("#scene-${previous}", {
|
|
131
|
+
xPercent: ${-100 * direction},
|
|
132
|
+
opacity: 0,
|
|
133
|
+
duration: .34,
|
|
134
|
+
ease: "power4.inOut"
|
|
135
|
+
}, ${transitionStart});
|
|
136
|
+
tl.fromTo("#scene-${index}", {
|
|
137
|
+
xPercent: ${100 * direction},
|
|
138
|
+
opacity: 1
|
|
139
|
+
}, {
|
|
140
|
+
xPercent: 0,
|
|
141
|
+
opacity: 1,
|
|
142
|
+
duration: .34,
|
|
143
|
+
ease: "power4.inOut",
|
|
144
|
+
immediateRender: false
|
|
145
|
+
}, ${transitionStart});${flash}`;
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const last = scenes.length - 1;
|
|
149
|
+
const finalStart = Number(
|
|
150
|
+
(scenes[last].start + scenes[last].duration - 0.68).toFixed(2),
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
return `${transitions.join("\n\n")}
|
|
154
|
+
|
|
155
|
+
${entrances.join("\n\n")}
|
|
156
|
+
|
|
157
|
+
tl.to("#scene-${last} .entrance", {
|
|
158
|
+
y: -18,
|
|
159
|
+
opacity: 0,
|
|
160
|
+
duration: .48,
|
|
161
|
+
stagger: .025,
|
|
162
|
+
ease: "power2.in"
|
|
163
|
+
}, ${finalStart});`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function compositionHtml(repo, scenes, palette) {
|
|
167
|
+
const duration = Number(
|
|
168
|
+
(scenes.at(-1).start + scenes.at(-1).duration).toFixed(2),
|
|
169
|
+
);
|
|
170
|
+
return `<!doctype html>
|
|
171
|
+
<html lang="en">
|
|
172
|
+
<head>
|
|
173
|
+
<meta charset="UTF-8">
|
|
174
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
175
|
+
<title>${escapeHtml(repo.title)} · RepoTrailer</title>
|
|
176
|
+
<script src="./gsap.min.js"></script>
|
|
177
|
+
<style>
|
|
178
|
+
html, body {
|
|
179
|
+
margin: 0;
|
|
180
|
+
width: 1920px;
|
|
181
|
+
height: 1080px;
|
|
182
|
+
overflow: hidden;
|
|
183
|
+
background-color: ${palette.ink};
|
|
184
|
+
color: ${palette.paper};
|
|
185
|
+
font-family: Inter, "Helvetica Neue", Arial, sans-serif;
|
|
186
|
+
}
|
|
187
|
+
* { box-sizing: border-box; }
|
|
188
|
+
#repotrailer {
|
|
189
|
+
position: relative;
|
|
190
|
+
width: 1920px;
|
|
191
|
+
height: 1080px;
|
|
192
|
+
overflow: hidden;
|
|
193
|
+
background-color: ${palette.ink};
|
|
194
|
+
}
|
|
195
|
+
.hf-scene {
|
|
196
|
+
position: absolute;
|
|
197
|
+
inset: 0;
|
|
198
|
+
width: 100%;
|
|
199
|
+
height: 100%;
|
|
200
|
+
overflow: hidden;
|
|
201
|
+
background-color: ${palette.ink};
|
|
202
|
+
opacity: 0;
|
|
203
|
+
will-change: transform;
|
|
204
|
+
}
|
|
205
|
+
.hf-scene:first-child { opacity: 1; }
|
|
206
|
+
.grid {
|
|
207
|
+
position: absolute;
|
|
208
|
+
inset: 0;
|
|
209
|
+
background-image:
|
|
210
|
+
linear-gradient(rgba(199, 255, 69, .055) 1px, rgba(199, 255, 69, 0) 1px),
|
|
211
|
+
linear-gradient(90deg, rgba(199, 255, 69, .055) 1px, rgba(199, 255, 69, 0) 1px);
|
|
212
|
+
background-size: 120px 120px;
|
|
213
|
+
}
|
|
214
|
+
.grid::after {
|
|
215
|
+
content: "";
|
|
216
|
+
position: absolute;
|
|
217
|
+
inset: 0;
|
|
218
|
+
background-image: repeating-linear-gradient(
|
|
219
|
+
0deg,
|
|
220
|
+
rgba(245, 242, 233, .025) 0,
|
|
221
|
+
rgba(245, 242, 233, .025) 1px,
|
|
222
|
+
rgba(245, 242, 233, 0) 1px,
|
|
223
|
+
rgba(245, 242, 233, 0) 7px
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
.scene-content {
|
|
227
|
+
position: relative;
|
|
228
|
+
z-index: 4;
|
|
229
|
+
display: flex;
|
|
230
|
+
flex-direction: column;
|
|
231
|
+
justify-content: center;
|
|
232
|
+
width: 100%;
|
|
233
|
+
height: 100%;
|
|
234
|
+
padding: 120px 150px 150px;
|
|
235
|
+
gap: 28px;
|
|
236
|
+
}
|
|
237
|
+
.eyebrow {
|
|
238
|
+
margin: 0;
|
|
239
|
+
color: ${palette.accent};
|
|
240
|
+
font-family: ui-monospace, Menlo, Monaco, Consolas, monospace;
|
|
241
|
+
font-size: 25px;
|
|
242
|
+
font-weight: 800;
|
|
243
|
+
letter-spacing: .22em;
|
|
244
|
+
text-transform: uppercase;
|
|
245
|
+
}
|
|
246
|
+
.headline {
|
|
247
|
+
max-width: 1480px;
|
|
248
|
+
margin: 0;
|
|
249
|
+
color: ${palette.paper};
|
|
250
|
+
font-weight: 900;
|
|
251
|
+
letter-spacing: -.065em;
|
|
252
|
+
line-height: .88;
|
|
253
|
+
text-wrap: balance;
|
|
254
|
+
}
|
|
255
|
+
.title-short { font-size: 168px; }
|
|
256
|
+
.title-medium { font-size: 126px; }
|
|
257
|
+
.title-long { font-size: 94px; line-height: .96; }
|
|
258
|
+
.copy {
|
|
259
|
+
max-width: 1120px;
|
|
260
|
+
margin: 4px 0 0;
|
|
261
|
+
color: ${palette.muted};
|
|
262
|
+
font-size: 39px;
|
|
263
|
+
font-weight: 450;
|
|
264
|
+
line-height: 1.3;
|
|
265
|
+
text-wrap: balance;
|
|
266
|
+
}
|
|
267
|
+
.orb {
|
|
268
|
+
position: absolute;
|
|
269
|
+
z-index: 2;
|
|
270
|
+
border-radius: 999px;
|
|
271
|
+
will-change: transform, opacity;
|
|
272
|
+
}
|
|
273
|
+
.orb-hot {
|
|
274
|
+
top: -340px;
|
|
275
|
+
right: -210px;
|
|
276
|
+
width: 780px;
|
|
277
|
+
height: 780px;
|
|
278
|
+
background-color: ${palette.hot};
|
|
279
|
+
}
|
|
280
|
+
.orb-accent {
|
|
281
|
+
top: -230px;
|
|
282
|
+
right: -100px;
|
|
283
|
+
width: 500px;
|
|
284
|
+
height: 500px;
|
|
285
|
+
background-color: ${palette.accent};
|
|
286
|
+
}
|
|
287
|
+
.index-mark {
|
|
288
|
+
position: absolute;
|
|
289
|
+
z-index: 3;
|
|
290
|
+
right: 110px;
|
|
291
|
+
bottom: 58px;
|
|
292
|
+
color: rgba(245, 242, 233, .08);
|
|
293
|
+
font-size: 280px;
|
|
294
|
+
font-weight: 900;
|
|
295
|
+
letter-spacing: -.08em;
|
|
296
|
+
}
|
|
297
|
+
.rail {
|
|
298
|
+
position: absolute;
|
|
299
|
+
z-index: 5;
|
|
300
|
+
left: 0;
|
|
301
|
+
right: 0;
|
|
302
|
+
bottom: 0;
|
|
303
|
+
display: flex;
|
|
304
|
+
justify-content: space-between;
|
|
305
|
+
padding: 28px 150px 34px;
|
|
306
|
+
border-top: 2px solid rgba(245, 242, 233, .18);
|
|
307
|
+
color: ${palette.paper};
|
|
308
|
+
font-family: ui-monospace, Menlo, Monaco, Consolas, monospace;
|
|
309
|
+
font-size: 18px;
|
|
310
|
+
font-weight: 800;
|
|
311
|
+
letter-spacing: .16em;
|
|
312
|
+
}
|
|
313
|
+
.metrics {
|
|
314
|
+
display: flex;
|
|
315
|
+
gap: 34px;
|
|
316
|
+
width: min(1240px, 100%);
|
|
317
|
+
margin-top: 18px;
|
|
318
|
+
}
|
|
319
|
+
.metric {
|
|
320
|
+
display: flex;
|
|
321
|
+
flex-direction: column;
|
|
322
|
+
min-width: 260px;
|
|
323
|
+
padding: 26px 30px 28px;
|
|
324
|
+
border: 2px solid rgba(245, 242, 233, .2);
|
|
325
|
+
background-color: rgba(17, 19, 25, .9);
|
|
326
|
+
}
|
|
327
|
+
.metric strong {
|
|
328
|
+
color: ${palette.accent};
|
|
329
|
+
font-family: ui-monospace, Menlo, Monaco, Consolas, monospace;
|
|
330
|
+
font-size: 86px;
|
|
331
|
+
font-variant-numeric: tabular-nums;
|
|
332
|
+
line-height: .95;
|
|
333
|
+
}
|
|
334
|
+
.metric span {
|
|
335
|
+
margin-top: 14px;
|
|
336
|
+
color: ${palette.muted};
|
|
337
|
+
font-family: ui-monospace, Menlo, Monaco, Consolas, monospace;
|
|
338
|
+
font-size: 18px;
|
|
339
|
+
font-weight: 800;
|
|
340
|
+
letter-spacing: .14em;
|
|
341
|
+
text-transform: uppercase;
|
|
342
|
+
}
|
|
343
|
+
.terminal {
|
|
344
|
+
display: flex;
|
|
345
|
+
align-items: center;
|
|
346
|
+
width: min(1500px, 100%);
|
|
347
|
+
min-height: 138px;
|
|
348
|
+
padding: 0 42px;
|
|
349
|
+
border: 3px solid ${palette.accent};
|
|
350
|
+
background-color: #111319;
|
|
351
|
+
box-shadow: 18px 18px 0 ${palette.hot};
|
|
352
|
+
font-family: ui-monospace, Menlo, Monaco, Consolas, monospace;
|
|
353
|
+
font-size: 47px;
|
|
354
|
+
overflow: hidden;
|
|
355
|
+
}
|
|
356
|
+
.terminal .prompt { color: ${palette.hot}; margin-right: 24px; }
|
|
357
|
+
.terminal code {
|
|
358
|
+
color: ${palette.paper};
|
|
359
|
+
white-space: nowrap;
|
|
360
|
+
overflow: hidden;
|
|
361
|
+
text-overflow: ellipsis;
|
|
362
|
+
}
|
|
363
|
+
.cursor {
|
|
364
|
+
flex: 0 0 auto;
|
|
365
|
+
width: 24px;
|
|
366
|
+
height: 58px;
|
|
367
|
+
margin-left: 16px;
|
|
368
|
+
background-color: ${palette.accent};
|
|
369
|
+
}
|
|
370
|
+
.scene-feature:nth-of-type(even) .scene-content {
|
|
371
|
+
align-items: flex-end;
|
|
372
|
+
text-align: right;
|
|
373
|
+
}
|
|
374
|
+
.scene-feature:nth-of-type(even) .eyebrow,
|
|
375
|
+
.scene-feature:nth-of-type(even) .headline,
|
|
376
|
+
.scene-feature:nth-of-type(even) .copy {
|
|
377
|
+
max-width: 1320px;
|
|
378
|
+
}
|
|
379
|
+
.scene-feature:nth-of-type(even) .orb-hot {
|
|
380
|
+
right: auto;
|
|
381
|
+
left: -260px;
|
|
382
|
+
top: 650px;
|
|
383
|
+
}
|
|
384
|
+
.scene-feature:nth-of-type(even) .orb-accent {
|
|
385
|
+
right: auto;
|
|
386
|
+
left: -40px;
|
|
387
|
+
top: 740px;
|
|
388
|
+
}
|
|
389
|
+
.scene-proof .headline { max-width: 1560px; }
|
|
390
|
+
.scene-proof .orb-hot { top: -180px; right: -120px; }
|
|
391
|
+
.scene-proof .orb-accent { top: -80px; right: 60px; }
|
|
392
|
+
.scene-outro .scene-content { align-items: center; text-align: center; }
|
|
393
|
+
.scene-outro .headline { font-size: 188px; }
|
|
394
|
+
.scene-outro .copy { font-family: ui-monospace, Menlo, Monaco, Consolas, monospace; font-size: 28px; }
|
|
395
|
+
.transition-flash {
|
|
396
|
+
position: absolute;
|
|
397
|
+
z-index: 20;
|
|
398
|
+
inset: 0;
|
|
399
|
+
pointer-events: none;
|
|
400
|
+
background-color: ${palette.hot};
|
|
401
|
+
opacity: 0;
|
|
402
|
+
}
|
|
403
|
+
</style>
|
|
404
|
+
</head>
|
|
405
|
+
<body>
|
|
406
|
+
<main
|
|
407
|
+
id="repotrailer"
|
|
408
|
+
data-composition-id="repotrailer"
|
|
409
|
+
data-width="1920"
|
|
410
|
+
data-height="1080"
|
|
411
|
+
data-start="0"
|
|
412
|
+
data-duration="${duration}"
|
|
413
|
+
>
|
|
414
|
+
${scenes.map(sceneBody).join("")}
|
|
415
|
+
<div class="transition-flash" data-layout-ignore></div>
|
|
416
|
+
<script>
|
|
417
|
+
window.__timelines = window.__timelines || {};
|
|
418
|
+
const tl = gsap.timeline({ paused: true });
|
|
419
|
+
${animationScript(scenes)}
|
|
420
|
+
|
|
421
|
+
window.__timelines["repotrailer"] = tl;
|
|
422
|
+
</script>
|
|
423
|
+
</main>
|
|
424
|
+
</body>
|
|
425
|
+
</html>`;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function designMarkdown(palette) {
|
|
429
|
+
return `# RepoTrailer Visual Identity
|
|
430
|
+
|
|
431
|
+
## Style Prompt
|
|
432
|
+
|
|
433
|
+
Swiss Pulse precision with Maximalist Type launch energy. Grid-locked developer
|
|
434
|
+
tool graphics on a near-black canvas, oversized heavy typography, and one toxic
|
|
435
|
+
lime accent balanced by a coral transition hit. Motion is fast, directional,
|
|
436
|
+
and decisive: elements snap into place with no floating or decorative softness.
|
|
437
|
+
|
|
438
|
+
## Colors
|
|
439
|
+
|
|
440
|
+
- Canvas: \`${palette.ink}\`
|
|
441
|
+
- Primary text: \`${palette.paper}\`
|
|
442
|
+
- Accent: \`${palette.accent}\`
|
|
443
|
+
- Transition hit: \`${palette.hot}\`
|
|
444
|
+
- Secondary text: \`${palette.muted}\`
|
|
445
|
+
|
|
446
|
+
## Typography
|
|
447
|
+
|
|
448
|
+
- Headlines: Inter Black, 900
|
|
449
|
+
- Labels and commands: Menlo / system monospace
|
|
450
|
+
|
|
451
|
+
## Motion
|
|
452
|
+
|
|
453
|
+
- Primary transition: 0.34-second push slide with \`power4.inOut\`
|
|
454
|
+
- Entrance signature: \`expo.out\`, \`power4.out\`, and restrained back easing
|
|
455
|
+
- Proof scene accent: one coral overexposure flash
|
|
456
|
+
- Final scene: simple fade to black
|
|
457
|
+
|
|
458
|
+
## What NOT to Do
|
|
459
|
+
|
|
460
|
+
- No gradients or gradient text
|
|
461
|
+
- No blue-purple AI palette
|
|
462
|
+
- No generic equal-sized card grid
|
|
463
|
+
- No floating glassmorphism
|
|
464
|
+
- No invented metrics or decorative charts
|
|
465
|
+
`;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export async function writeHyperframesProject(
|
|
469
|
+
repo,
|
|
470
|
+
scenes,
|
|
471
|
+
outputDirectory,
|
|
472
|
+
palette,
|
|
473
|
+
) {
|
|
474
|
+
const project = path.join(path.resolve(outputDirectory), "hyperframes");
|
|
475
|
+
await mkdir(project, { recursive: true });
|
|
476
|
+
const files = {
|
|
477
|
+
project,
|
|
478
|
+
index: path.join(project, "index.html"),
|
|
479
|
+
design: path.join(project, "DESIGN.md"),
|
|
480
|
+
config: path.join(project, "hyperframes.json"),
|
|
481
|
+
package: path.join(project, "package.json"),
|
|
482
|
+
gsap: path.join(project, "gsap.min.js"),
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
await Promise.all([
|
|
486
|
+
writeFile(files.index, compositionHtml(repo, scenes, palette)),
|
|
487
|
+
writeFile(files.design, designMarkdown(palette)),
|
|
488
|
+
copyFile(
|
|
489
|
+
fileURLToPath(import.meta.resolve("gsap/dist/gsap.min.js")),
|
|
490
|
+
files.gsap,
|
|
491
|
+
),
|
|
492
|
+
writeFile(
|
|
493
|
+
files.config,
|
|
494
|
+
`${JSON.stringify({
|
|
495
|
+
$schema: "https://hyperframes.heygen.com/schema/hyperframes.json",
|
|
496
|
+
registry: "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry",
|
|
497
|
+
paths: {
|
|
498
|
+
blocks: "compositions",
|
|
499
|
+
components: "compositions/components",
|
|
500
|
+
assets: "assets",
|
|
501
|
+
},
|
|
502
|
+
}, null, 2)}\n`,
|
|
503
|
+
),
|
|
504
|
+
writeFile(
|
|
505
|
+
files.package,
|
|
506
|
+
`${JSON.stringify({
|
|
507
|
+
name: "repotrailer-render",
|
|
508
|
+
private: true,
|
|
509
|
+
scripts: {
|
|
510
|
+
lint: "npx hyperframes lint",
|
|
511
|
+
inspect: "npx hyperframes inspect --samples 18",
|
|
512
|
+
render: "npx hyperframes render --output ../trailer.mp4 --workers 1 --quality standard",
|
|
513
|
+
},
|
|
514
|
+
}, null, 2)}\n`,
|
|
515
|
+
),
|
|
516
|
+
]);
|
|
517
|
+
|
|
518
|
+
return files;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
export async function renderHyperframesProject(
|
|
522
|
+
projectDirectory,
|
|
523
|
+
trailerPath,
|
|
524
|
+
options = {},
|
|
525
|
+
) {
|
|
526
|
+
const npxArgs = ["--yes", "hyperframes"];
|
|
527
|
+
const lint = await run("npx", [...npxArgs, "lint"], {
|
|
528
|
+
cwd: projectDirectory,
|
|
529
|
+
timeout: 120_000,
|
|
530
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
531
|
+
});
|
|
532
|
+
if (!lint.ok) {
|
|
533
|
+
throw new Error(`HyperFrames lint failed:\n${lint.stdout}\n${lint.stderr}`);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const inspect = await run(
|
|
537
|
+
"npx",
|
|
538
|
+
[...npxArgs, "inspect", "--samples", "18", "--strict"],
|
|
539
|
+
{
|
|
540
|
+
cwd: projectDirectory,
|
|
541
|
+
timeout: 180_000,
|
|
542
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
543
|
+
},
|
|
544
|
+
);
|
|
545
|
+
if (!inspect.ok) {
|
|
546
|
+
throw new Error(
|
|
547
|
+
`HyperFrames layout inspection failed:\n${inspect.stdout}\n${inspect.stderr}`,
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
const render = await run(
|
|
552
|
+
"npx",
|
|
553
|
+
[
|
|
554
|
+
...npxArgs,
|
|
555
|
+
"render",
|
|
556
|
+
"--output",
|
|
557
|
+
trailerPath,
|
|
558
|
+
"--workers",
|
|
559
|
+
String(options.workers ?? 1),
|
|
560
|
+
"--quality",
|
|
561
|
+
options.quality ?? "standard",
|
|
562
|
+
"--strict",
|
|
563
|
+
],
|
|
564
|
+
{
|
|
565
|
+
cwd: projectDirectory,
|
|
566
|
+
timeout: options.timeout ?? 900_000,
|
|
567
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
568
|
+
},
|
|
569
|
+
);
|
|
570
|
+
if (!render.ok) {
|
|
571
|
+
throw new Error(
|
|
572
|
+
`HyperFrames render failed:\n${render.stdout}\n${render.stderr}`,
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
return {
|
|
577
|
+
trailer: trailerPath,
|
|
578
|
+
lint: lint.stdout,
|
|
579
|
+
inspect: inspect.stdout,
|
|
580
|
+
render: render.stdout,
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
export const __test = {
|
|
585
|
+
compositionHtml,
|
|
586
|
+
designMarkdown,
|
|
587
|
+
titleClass,
|
|
588
|
+
};
|
package/src/index.js
ADDED