Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
521 changes: 512 additions & 9 deletions bun.lock

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions packages/video/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules
dist
.DS_Store
.env
build/
# Ignore the output video from Git but not videos you import into src/.
out
40 changes: 40 additions & 0 deletions packages/video/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Sideffect Promo Video

A Remotion composition for a short `npm i sideffect` promo. The video introduces the public API with a typed workflow snippet, then shows how the Vite adapter turns workflow layers into Cloudflare workflow bindings.

## Commands

**Install Dependencies**

```console
bun install
```

**Start Preview**

```console
bun run dev
```

**Render video**

```console
bun run render
```

**Render a preview frame**

```console
bun run still
```

## Composition

- `SideffectPromo`: 1920x1080, 30fps, 17 seconds.

## Storyline

- Install: `npm i sideffect`.
- API: `Schema`, `Step.make`, `Workflow.make`, and `workflow.toLayer`.
- Build path: source workflow layer to Vite discovery to Cloudflare binding.
- Outro: `sideffect` and the install command.
40 changes: 40 additions & 0 deletions packages/video/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "video",
"version": "1.0.0",
"private": true,
"description": "Remotion promo video for sideffect",
"license": "Apache-2.0",
"repository": {},
"sideEffects": [
"*.css"
],
"scripts": {
"dev": "remotionb studio",
"build": "remotionb bundle",
"render": "remotionb render src/index.ts SideffectPromo out/sideffect-promo.mp4",
"still": "remotionb still src/index.ts SideffectPromo out/sideffect-promo.png --frame=260",
"upgrade": "remotionb upgrade",
"check": "vp check"
},
"dependencies": {
"@remotion/cli": "4.0.477",
"@remotion/tailwind-v4": "4.0.477",
"@remotion/zod-types": "4.0.477",
"postprocessing": "^6.39.1",
"react": "19.2.3",
"react-dom": "19.2.3",
"remotion": "4.0.477",
"tailwindcss": "4.0.0",
"zod": "4.3.6"
},
"devDependencies": {
"@remotion/google-fonts": "4.0.477",
"@types/node": "^25.6.2",
"@types/react": "19.2.7",
"@types/web": "0.0.166",
"@typescript/native-preview": "catalog:",
"bumpp": "^11.1.0",
"typescript": "catalog:",
"vite-plus": "catalog:"
}
}
Binary file added packages/video/public/background.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions packages/video/remotion.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// See all configuration options: https://remotion.dev/docs/config
// Each option also is available as a CLI flag: https://remotion.dev/docs/cli

// Note: When using the Node.JS APIs, the config file doesn't apply. Instead, pass options directly to the APIs

import { Config } from "@remotion/cli/config";

Config.setVideoImageFormat("jpeg");
Config.setOverwriteOutput(true);
Config.setChromiumOpenGlRenderer("angle");
Config.setConcurrency(1);
16 changes: 16 additions & 0 deletions packages/video/src/Root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import "./index.css";
import { Composition } from "remotion";
import { SideffectPromo, SIDEFFECT_PROMO_DURATION } from "./sideffect-promo/composition";

export { SideffectPromo, SIDEFFECT_PROMO_DURATION };

export const RemotionRoot: React.FC = () => (
<Composition
component={SideffectPromo}
durationInFrames={SIDEFFECT_PROMO_DURATION}
fps={30}
height={1080}
id="SideffectPromo"
width={1920}
/>
);
7 changes: 7 additions & 0 deletions packages/video/src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
* {
box-sizing: border-box;
}

body {
margin: 0;
}
7 changes: 7 additions & 0 deletions packages/video/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// This is your entry file! Refer to it when you render:
// npx remotion render <entry-file> HelloWorld out/video.mp4

import { registerRoot } from "remotion";
import { RemotionRoot } from "./Root";

registerRoot(RemotionRoot);
21 changes: 21 additions & 0 deletions packages/video/src/sideffect-promo/components/Background.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { AbsoluteFill, Img, staticFile } from "remotion";

export const Background = () => (
<AbsoluteFill style={{ background: "#08111f", overflow: "hidden" }}>
<Img
src={staticFile("background.png")}
style={{
height: "100%",
objectFit: "cover",
objectPosition: "center",
width: "100%",
}}
/>
<AbsoluteFill
style={{
background:
"radial-gradient(circle at 50% 48%, rgba(255, 255, 255, 0.18) 0%, rgba(255, 255, 255, 0) 44%), linear-gradient(180deg, rgba(8, 17, 31, 0.08) 0%, rgba(8, 17, 31, 0.34) 100%)",
}}
/>
</AbsoluteFill>
);
231 changes: 231 additions & 0 deletions packages/video/src/sideffect-promo/components/CodeWindow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
import type { CSSProperties } from "react";
import { useCurrentFrame } from "remotion";

import {
CODE_BODY_PADDING_BOTTOM,
CODE_BODY_PADDING_TOP,
CODE_BODY_PADDING_X,
CODE_HEADER_HEIGHT,
CODE_LINE_HEIGHT,
CODE_ROW_PADDING_LEFT,
CODE_ROW_PADDING_RIGHT,
CODE_VERTICAL_PADDING,
CONTENT_WIDTH,
} from "../constants";
import { ds } from "../design";
import { syntaxColors, tokenizeLine } from "../syntax";
import { totalChars } from "../timings";

type CodeWindowProps = {
readonly charBudget: number;
readonly filename: string;
readonly fontSize?: number;
readonly highlightLines?: readonly number[];
readonly lineHeight?: number;
readonly lines: readonly string[];
readonly style?: CSSProperties;
readonly width?: number;
};

const codeBodyHeight = (lineCount: number, lineHeight = CODE_LINE_HEIGHT) =>
CODE_VERTICAL_PADDING + lineCount * lineHeight;

const codeWindowHeight = (lineCount: number, lineHeight = CODE_LINE_HEIGHT) =>
CODE_HEADER_HEIGHT + codeBodyHeight(lineCount, lineHeight);

const currentTypedLine = (lines: readonly string[], charBudget: number) => {
let consumed = 0;

for (let index = 0; index < lines.length; index += 1) {
consumed += lines[index].length + 1;
if (charBudget < consumed) {
return index + 1;
}
}

return lines.length;
};

const lineHasTyped = (lines: readonly string[], lineNumber: number, charBudget: number) => {
const charsBeforeLine = lines
.slice(0, lineNumber - 1)
.reduce((sum, line) => sum + line.length + 1, 0);

return charBudget > charsBeforeLine;
};

export const CodeWindow = ({
charBudget,
filename,
fontSize = 24,
highlightLines = [],
lineHeight = CODE_LINE_HEIGHT,
lines,
style,
width = CONTENT_WIDTH,
}: CodeWindowProps) => {
let remaining = charBudget;
const frame = useCurrentFrame();
const typedLine = currentTypedLine(lines, charBudget);
const cursorOn = Math.floor(frame / 15) % 2 === 0;
const bodyHeight = codeBodyHeight(lines.length, lineHeight);
const windowHeight = codeWindowHeight(lines.length, lineHeight);
const fullBudget = totalChars(lines);

return (
<div
style={{
background: ds.codeBg,
border: `1px solid ${ds.border}`,
borderRadius: 8,
boxShadow: "0 28px 90px rgba(31, 45, 62, 0.22)",
height: windowHeight,
overflow: "hidden",
width,
...style,
}}
>
<div
style={{
alignItems: "center",
background: ds.codeHeader,
borderBottom: `1px solid ${ds.borderStrong}`,
color: ds.codeMeta,
display: "flex",
fontFamily: ds.fontMono,
fontSize: 20,
fontWeight: 700,
height: CODE_HEADER_HEIGHT,
padding: "0 26px",
}}
>
<span>{filename}</span>
</div>
<div
style={{
boxSizing: "border-box",
fontFamily: ds.fontMono,
fontSize,
fontVariantLigatures: "none",
height: bodyHeight,
lineHeight: 1,
padding: `${CODE_BODY_PADDING_TOP}px ${CODE_BODY_PADDING_X}px ${CODE_BODY_PADDING_BOTTOM}px`,
position: "relative",
}}
>
{highlightLines.map((lineNumber) => {
const hasTyped = lineHasTyped(lines, lineNumber, charBudget);

return (
<div
key={`${filename}-highlight-${lineNumber}`}
style={{
background: ds.codeHighlight,
boxSizing: "border-box",
height: lineHeight,
left: 0,
opacity: hasTyped ? 1 : 0,
pointerEvents: "none",
position: "absolute",
right: 0,
top: CODE_BODY_PADDING_TOP + (lineNumber - 1) * lineHeight,
}}
/>
);
})}
{lines.map((line, index) => {
const visible = line.slice(0, Math.max(0, remaining));
const lineNumber = index + 1;
const hasTyped = visible.length > 0 || remaining > 0;
const isCurrentLine = typedLine === lineNumber && charBudget < fullBudget;
remaining -= line.length + 1;

return (
<div
key={`${filename}-${lineNumber}`}
style={{
alignItems: "center",
boxSizing: "border-box",
display: "flex",
height: lineHeight,
opacity: hasTyped ? 1 : 0.18,
overflow: "hidden",
paddingLeft: CODE_ROW_PADDING_LEFT,
paddingRight: CODE_ROW_PADDING_RIGHT,
position: "relative",
whiteSpace: "pre",
}}
>
<span
style={{
alignItems: "center",
color: ds.codeLine,
display: "flex",
flex: "0 0 54px",
height: "100%",
justifyContent: "flex-end",
lineHeight: 1,
marginRight: 28,
textAlign: "right",
}}
>
{lineNumber.toString().padStart(2, "0")}
</span>
<span
style={{
alignItems: "center",
display: "flex",
height: "100%",
lineHeight: 1,
minWidth: 0,
position: "relative",
whiteSpace: "pre",
}}
>
{line.startsWith(" ") ? (
<span
style={{
background: ds.codeIndent,
display: "inline-block",
height: lineHeight,
left: 0,
position: "absolute",
top: 0,
width: 1,
}}
/>
) : null}
{tokenizeLine(visible).map(([text, kind], tokenIndex) => (
<span
key={`${lineNumber}-${tokenIndex}`}
style={{
color: syntaxColors[kind],
display: "inline-block",
lineHeight: 1,
whiteSpace: "pre",
}}
>
{text}
</span>
))}
{isCurrentLine ? (
<span
style={{
alignSelf: "center",
background: cursorOn ? ds.accent : "transparent",
display: "inline-block",
flex: "0 0 auto",
height: Math.min(lineHeight - 8, fontSize + 7),
marginLeft: 3,
width: 9,
}}
/>
) : null}
</span>
</div>
);
})}
</div>
</div>
);
};
Loading
Loading