Arslan1997 commited on
Commit
d326c2d
·
1 Parent(s): aeea10c

LETS GOOOOOOOOOOOOOOOOOOOOOOOOO

Browse files
frontend/src/pages/Landing.tsx CHANGED
@@ -19,7 +19,7 @@ import {
19
 
20
  export const PENDING_SOURCE_KEY = "ns_pending_source";
21
  // Bump after re-rendering public/demo.mp4 so browsers and the CDN fetch the new file.
22
- const DEMO_VERSION = 3;
23
 
24
  const FEATURES = [
25
  {
 
19
 
20
  export const PENDING_SOURCE_KEY = "ns_pending_source";
21
  // Bump after re-rendering public/demo.mp4 so browsers and the CDN fetch the new file.
22
+ const DEMO_VERSION = 4;
23
 
24
  const FEATURES = [
25
  {
renderer/scripts/make_demo_vo.py CHANGED
@@ -2,7 +2,7 @@
2
 
3
  The screens show the product; the voice never explains it, it only says how it feels: exploring,
4
  excited, mesmerized. Also writes src/compositions/demoVo.ts; scenes stretch to fit their lines.
5
- Then run make_demo_music.py, which reads the same file to size the score.
6
 
7
  cd backend && PYTHONPATH=. .venv/Scripts/python ../renderer/scripts/make_demo_vo.py
8
  """
 
2
 
3
  The screens show the product; the voice never explains it, it only says how it feels: exploring,
4
  excited, mesmerized. Also writes src/compositions/demoVo.ts; scenes stretch to fit their lines.
5
+ make_demo_music.py writes a 47.5 s score; raise its TOTAL and rerun it if the demo grows past that.
6
 
7
  cd backend && PYTHONPATH=. .venv/Scripts/python ../renderer/scripts/make_demo_vo.py
8
  """
renderer/src/compositions/LandingDemo.tsx CHANGED
@@ -1,6 +1,8 @@
1
  import type { CSSProperties, ReactNode } from "react";
2
- import { AbsoluteFill, Audio as SoundTrack, Easing, Img, interpolate, random, Sequence, spring, staticFile, useCurrentFrame, useVideoConfig } from "remotion";
3
  import { theme } from "../theme";
 
 
4
 
5
  /** Landing page product demo: a trip through the Notestack galaxy. 1920x1080, 30 fps, ~32 s.
6
  * Scenes: warp in, paste URL, posts enter orbit, cited research, audio overview, Launch Kit,
@@ -9,24 +11,25 @@ import { theme } from "../theme";
9
  export const DEMO_FPS = 30;
10
  const S = (seconds: number) => Math.round(seconds * DEMO_FPS);
11
 
12
- // Scene lengths fit the narration (public/demo-vo/*.mp3, voiced by "Nora Vale, Mission Control"):
13
- // each line starts `vo` seconds into its scene and ends with room to breathe before the crossfade.
14
- const SCENES = [
 
15
  { id: "warp", len: 5.2, vo: 0.6 },
16
  { id: "paste", len: 9.4, vo: 0.4 },
17
  { id: "orbit", len: 7.6, vo: 0.4 },
18
  { id: "research", len: 5.6, vo: 0.4 },
19
- { id: "audio", len: 5.8, vo: 0.4 },
20
  { id: "launchkit", len: 7.8, vo: 0.4 },
21
  { id: "launchpad", len: 4.6, vo: 0.4 },
22
  { id: "outro", len: 4.2, vo: 0.5 },
23
  ] as const;
24
  const XFADE = 0.5; // seconds of overlap between scenes
 
25
 
26
- // Narration clip lengths (public/demo-vo/durations.json) so the music can duck under the voice.
27
- const VO_SECONDS: Record<(typeof SCENES)[number]["id"], number> = {
28
- warp: 3.69, paste: 8.34, orbit: 5.96, research: 4.39, audio: 4.57, launchkit: 5.83, launchpad: 3.14, outro: 2.56,
29
- };
30
 
31
  /** Frame ranges where the narrator is speaking, in composition time. */
32
  const VO_WINDOWS: [number, number][] = (() => {
@@ -192,9 +195,9 @@ function Warp() {
192
  <br />
193
  <span style={{ color: theme.white, textShadow: `0 0 30px ${BLUE}, 0 0 80px ${BLUE}` }}>in orbit.</span>
194
  </h1>
195
- <p style={{ marginTop: 40, fontFamily: theme.mono, fontSize: 30, letterSpacing: 4, color: "rgba(255,255,255,0.75)", opacity: sub }}>
196
- NOTESTACK
197
- </p>
198
  </AbsoluteFill>
199
  <Flare at={50} duration={30} y={46} />
200
  </AbsoluteFill>
@@ -214,7 +217,7 @@ function Paste() {
214
  return (
215
  <AbsoluteFill>
216
  <WarpField speed={1.2} />
217
- <Caption eyebrow="Step 01" title="Point it at your writing" />
218
  <AbsoluteFill style={{ justifyContent: "center", alignItems: "center", flexDirection: "column", gap: 44, paddingTop: 120 }}>
219
  <div style={{ ...panel, display: "flex", alignItems: "center", gap: 18, padding: 14, width: 1100, borderRadius: 999 }}>
220
  <div style={{ flex: 1, fontFamily: theme.body, fontSize: 40, color: url ? theme.white : "rgba(255,255,255,0.4)", paddingLeft: 30 }}>
@@ -306,7 +309,7 @@ function Orbit() {
306
  return (
307
  <AbsoluteFill>
308
  <WarpField speed={0.8} />
309
- <Caption eyebrow="Step 02" title="Watch your posts come into orbit" />
310
  <svg width={width} height={height} style={{ position: "absolute" }}>
311
  <defs>
312
  <radialGradient id="planet" cx="40%" cy="35%" r="70%">
@@ -373,7 +376,7 @@ function Research() {
373
  return (
374
  <AbsoluteFill>
375
  <WarpField speed={0.6} count={260} />
376
- <Caption eyebrow="Research" title="Ask your archive. Every answer cites you." />
377
  <div style={{ position: "absolute", left: 140, right: 140, top: 360, display: "grid", gridTemplateColumns: "1.25fr 1fr", gap: 40 }}>
378
  <div style={{ ...panel, padding: 36, display: "grid", gap: 22, alignContent: "start" }}>
379
  <div style={{ justifySelf: "end", padding: "14px 22px", borderRadius: "18px 18px 4px 18px", background: `${BLUE}22`, border: `1px solid ${BLUE}66`, fontFamily: theme.body, fontSize: 30, color: theme.white }}>
@@ -426,7 +429,7 @@ function Audio() {
426
  return (
427
  <AbsoluteFill>
428
  <WarpField speed={0.7} count={260} />
429
- <Caption eyebrow="Audio overview" title="Two hosts. Your ideas. Grounded." />
430
  <div style={{ position: "absolute", left: 140, right: 140, top: 380, display: "grid", gap: 40 }}>
431
  <div style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: 10, height: 220 }}>
432
  {Array.from({ length: 64 }).map((_, i) => {
@@ -466,7 +469,7 @@ function LaunchKit() {
466
  return (
467
  <AbsoluteFill>
468
  <WarpField speed={0.9} count={260} />
469
- <Caption eyebrow="Launch Kit" title="One post. Every platform. Your voice." />
470
  <div style={{ position: "absolute", left: 140, right: 140, top: 380, display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 28 }}>
471
  {cards.map((c, i) => {
472
  const s = spring({ frame: frame - 10 - i * 7, fps, config: { damping: 14 } });
@@ -498,7 +501,7 @@ function Launchpad() {
498
  return (
499
  <AbsoluteFill>
500
  <WarpField speed={0.8} count={260} />
501
- <Caption eyebrow="Launchpad" title="Schedule it. Watch it lift off." />
502
  <div style={{ position: "absolute", left: 140, top: 360, display: "grid", gridTemplateColumns: "repeat(7, 120px)", gap: 12 }}>
503
  {Array.from({ length: 28 }).map((_, i) => {
504
  const on = lit.includes(i) && frame > 8 + lit.indexOf(i) * 6;
@@ -564,8 +567,9 @@ function Outro() {
564
  <WarpField speed={interpolate(frame, [0, 60, 114], [0.5, 2, 12])} streak={interpolate(frame, [70, 114], [0, 1], { extrapolateLeft: "clamp" })} />
565
  <AbsoluteFill style={{ justifyContent: "center", alignItems: "center", textAlign: "center", gap: 30 }}>
566
  {/* The navbar mark (frontend/public/logo.svg, copied to public/logo.svg). */}
567
- <Img src={staticFile("logo.svg")} width={128} height={128} style={{ borderRadius: 30, opacity: s, transform: `scale(${s})`, boxShadow: `0 0 80px ${BLUE}` }} />
568
- <h1 style={{ margin: 0, fontFamily: theme.display, fontSize: 120, color: theme.white, opacity: s, textShadow: `0 0 40px ${BLUE}` }}>Notestack</h1>
 
569
  <p style={{ margin: 0, fontFamily: theme.body, fontSize: 40, color: "rgba(255,255,255,0.8)", opacity: cta }}>Your knowledge, in orbit.</p>
570
  <p style={{ margin: 0, padding: "18px 40px", borderRadius: 999, background: BLUE, color: theme.white, fontFamily: theme.body, fontWeight: 600, fontSize: 32, opacity: cta, boxShadow: `0 0 40px ${BLUE}` }}>
571
  Start free at notestack.ai
 
1
  import type { CSSProperties, ReactNode } from "react";
2
+ import { AbsoluteFill, Audio as SoundTrack, Easing, interpolate, random, Sequence, spring, staticFile, useCurrentFrame, useVideoConfig } from "remotion";
3
  import { theme } from "../theme";
4
+ import { DEMO_VO_SECONDS } from "./demoVo";
5
+ import { Lockup } from "./Lockup";
6
 
7
  /** Landing page product demo: a trip through the Notestack galaxy. 1920x1080, 30 fps, ~32 s.
8
  * Scenes: warp in, paste URL, posts enter orbit, cited research, audio overview, Launch Kit,
 
11
  export const DEMO_FPS = 30;
12
  const S = (seconds: number) => Math.round(seconds * DEMO_FPS);
13
 
14
+ // Narration (public/demo-vo/*.mp3, voiced by "Nora Vale, Mission Control"): each line starts `vo`
15
+ // seconds into its scene. Scenes keep their minimum length for the animation and stretch when a line
16
+ // needs more room, so it always ends with a breath before the next scene's line.
17
+ const BASE_SCENES = [
18
  { id: "warp", len: 5.2, vo: 0.6 },
19
  { id: "paste", len: 9.4, vo: 0.4 },
20
  { id: "orbit", len: 7.6, vo: 0.4 },
21
  { id: "research", len: 5.6, vo: 0.4 },
22
+ { id: "audio", len: 5.1, vo: 0.4 },
23
  { id: "launchkit", len: 7.8, vo: 0.4 },
24
  { id: "launchpad", len: 4.6, vo: 0.4 },
25
  { id: "outro", len: 4.2, vo: 0.5 },
26
  ] as const;
27
  const XFADE = 0.5; // seconds of overlap between scenes
28
+ const BREATH = 0.8; // gap between the end of one line and the next scene's line (includes the crossfade)
29
 
30
+ // Narration clip lengths, written by scripts/make_demo_vo.py; the music ducks under them.
31
+ const VO_SECONDS: Record<(typeof BASE_SCENES)[number]["id"], number> = DEMO_VO_SECONDS;
32
+ const SCENES = BASE_SCENES.map((s) => ({ ...s, len: Math.max(s.len, s.vo + VO_SECONDS[s.id] + BREATH) }));
 
33
 
34
  /** Frame ranges where the narrator is speaking, in composition time. */
35
  const VO_WINDOWS: [number, number][] = (() => {
 
195
  <br />
196
  <span style={{ color: theme.white, textShadow: `0 0 30px ${BLUE}, 0 0 80px ${BLUE}` }}>in orbit.</span>
197
  </h1>
198
+ <div style={{ marginTop: 44, opacity: sub }}>
199
+ <Lockup size={60} glow={0.7} />
200
+ </div>
201
  </AbsoluteFill>
202
  <Flare at={50} duration={30} y={46} />
203
  </AbsoluteFill>
 
217
  return (
218
  <AbsoluteFill>
219
  <WarpField speed={1.2} />
220
+ <Caption eyebrow="Step 01" title="Feel the pull of something new" />
221
  <AbsoluteFill style={{ justifyContent: "center", alignItems: "center", flexDirection: "column", gap: 44, paddingTop: 120 }}>
222
  <div style={{ ...panel, display: "flex", alignItems: "center", gap: 18, padding: 14, width: 1100, borderRadius: 999 }}>
223
  <div style={{ flex: 1, fontFamily: theme.body, fontSize: 40, color: url ? theme.white : "rgba(255,255,255,0.4)", paddingLeft: 30 }}>
 
309
  return (
310
  <AbsoluteFill>
311
  <WarpField speed={0.8} />
312
+ <Caption eyebrow="Step 02" title="Shimmering. Impossible to look away." />
313
  <svg width={width} height={height} style={{ position: "absolute" }}>
314
  <defs>
315
  <radialGradient id="planet" cx="40%" cy="35%" r="70%">
 
376
  return (
377
  <AbsoluteFill>
378
  <WarpField speed={0.6} count={260} />
379
+ <Caption eyebrow="Research" title="A spark. Suddenly, wide awake." />
380
  <div style={{ position: "absolute", left: 140, right: 140, top: 360, display: "grid", gridTemplateColumns: "1.25fr 1fr", gap: 40 }}>
381
  <div style={{ ...panel, padding: 36, display: "grid", gap: 22, alignContent: "start" }}>
382
  <div style={{ justifySelf: "end", padding: "14px 22px", borderRadius: "18px 18px 4px 18px", background: `${BLUE}22`, border: `1px solid ${BLUE}66`, fontFamily: theme.body, fontSize: 30, color: theme.white }}>
 
429
  return (
430
  <AbsoluteFill>
431
  <WarpField speed={0.7} count={260} />
432
+ <Caption eyebrow="Audio overview" title="Lean back. Let it wash over you." />
433
  <div style={{ position: "absolute", left: 140, right: 140, top: 380, display: "grid", gap: 40 }}>
434
  <div style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: 10, height: 220 }}>
435
  {Array.from({ length: 64 }).map((_, i) => {
 
469
  return (
470
  <AbsoluteFill>
471
  <WarpField speed={0.9} count={260} />
472
+ <Caption eyebrow="Launch Kit" title="Your pulse quickens." />
473
  <div style={{ position: "absolute", left: 140, right: 140, top: 380, display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 28 }}>
474
  {cards.map((c, i) => {
475
  const s = spring({ frame: frame - 10 - i * 7, fps, config: { damping: 14 } });
 
501
  return (
502
  <AbsoluteFill>
503
  <WarpField speed={0.8} count={260} />
504
+ <Caption eyebrow="Launchpad" title="Three, two, one. Flying." />
505
  <div style={{ position: "absolute", left: 140, top: 360, display: "grid", gridTemplateColumns: "repeat(7, 120px)", gap: 12 }}>
506
  {Array.from({ length: 28 }).map((_, i) => {
507
  const on = lit.includes(i) && frame > 8 + lit.indexOf(i) * 6;
 
567
  <WarpField speed={interpolate(frame, [0, 60, 114], [0.5, 2, 12])} streak={interpolate(frame, [70, 114], [0, 1], { extrapolateLeft: "clamp" })} />
568
  <AbsoluteFill style={{ justifyContent: "center", alignItems: "center", textAlign: "center", gap: 30 }}>
569
  {/* The navbar mark (frontend/public/logo.svg, copied to public/logo.svg). */}
570
+ <div style={{ opacity: Math.min(1, s), transform: `scale(${0.85 + 0.15 * s})` }}>
571
+ <Lockup size={150} />
572
+ </div>
573
  <p style={{ margin: 0, fontFamily: theme.body, fontSize: 40, color: "rgba(255,255,255,0.8)", opacity: cta }}>Your knowledge, in orbit.</p>
574
  <p style={{ margin: 0, padding: "18px 40px", borderRadius: 999, background: BLUE, color: theme.white, fontFamily: theme.body, fontWeight: 600, fontSize: 32, opacity: cta, boxShadow: `0 0 40px ${BLUE}` }}>
575
  Start free at notestack.ai
renderer/src/compositions/LaunchTrailer.tsx CHANGED
@@ -259,12 +259,16 @@ function Planet({ cx, cy, r }: { cx: number; cy: number; r: number }) {
259
  <stop offset="90%" stopColor={BLUE} stopOpacity={0.55} />
260
  <stop offset="100%" stopColor={BLUE} stopOpacity={0} />
261
  </radialGradient>
 
 
 
 
262
  <clipPath id="planet-clip">
263
  <circle r={r} />
264
  </clipPath>
265
  </defs>
266
  <g transform={`translate(${cx} ${cy})`}>
267
- <circle r={r * 2.2} fill={BLUE} opacity={0.12 * shimmer} />
268
  <g transform={`rotate(${tilt})`}>{ring("back")}</g>
269
  <circle r={r} fill="url(#planet-body)" />
270
  {/* Soft cloud bands, clipped to the sphere, drifting slowly. */}
 
259
  <stop offset="90%" stopColor={BLUE} stopOpacity={0.55} />
260
  <stop offset="100%" stopColor={BLUE} stopOpacity={0} />
261
  </radialGradient>
262
+ <radialGradient id="planet-halo" cx="50%" cy="50%" r="50%">
263
+ <stop offset="30%" stopColor={BLUE} stopOpacity={0.3} />
264
+ <stop offset="100%" stopColor={BLUE} stopOpacity={0} />
265
+ </radialGradient>
266
  <clipPath id="planet-clip">
267
  <circle r={r} />
268
  </clipPath>
269
  </defs>
270
  <g transform={`translate(${cx} ${cy})`}>
271
+ <circle r={r * 2.4} fill="url(#planet-halo)" opacity={shimmer} />
272
  <g transform={`rotate(${tilt})`}>{ring("back")}</g>
273
  <circle r={r} fill="url(#planet-body)" />
274
  {/* Soft cloud bands, clipped to the sphere, drifting slowly. */}