wip
This commit is contained in:
parent
8542e3d7d5
commit
19417964de
15 changed files with 3017 additions and 9 deletions
8
packages/video/.gitignore
vendored
Normal file
8
packages/video/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Rendered video outputs
|
||||||
|
out/
|
||||||
|
*.mp4
|
||||||
|
*.webm
|
||||||
|
*.mov
|
||||||
|
|
||||||
|
# Remotion cache
|
||||||
|
.remotion/
|
||||||
|
|
@ -5,7 +5,8 @@
|
||||||
"description": "Marketing videos for TPMJS",
|
"description": "Marketing videos for TPMJS",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "remotion studio",
|
"dev": "remotion studio",
|
||||||
"render": "remotion render MarketingVideo out/marketing-video.mp4",
|
"render": "remotion render TPMJSExplainer out/explainer-video.mp4",
|
||||||
|
"render:features": "remotion render TPMJSFeatures out/features-video.mp4",
|
||||||
"upgrade": "remotion upgrade"
|
"upgrade": "remotion upgrade"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
||||||
55
packages/video/src/FeaturesVideo.tsx
Normal file
55
packages/video/src/FeaturesVideo.tsx
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
import { AbsoluteFill, Sequence, useVideoConfig } from 'remotion';
|
||||||
|
import { colors } from './design-tokens';
|
||||||
|
|
||||||
|
// Scene imports
|
||||||
|
import { FeaturesOpeningScene } from './scenes/features/FeaturesOpeningScene';
|
||||||
|
import { ToolRegistryScene } from './scenes/features/ToolRegistryScene';
|
||||||
|
import { OmegaAgentScene } from './scenes/features/OmegaAgentScene';
|
||||||
|
import { CollectionsFeatureScene } from './scenes/features/CollectionsFeatureScene';
|
||||||
|
import { CustomAgentsScene } from './scenes/features/CustomAgentsScene';
|
||||||
|
import { MCPProtocolScene } from './scenes/features/MCPProtocolScene';
|
||||||
|
import { SecureExecutionScene } from './scenes/features/SecureExecutionScene';
|
||||||
|
import { TestScenariosScene } from './scenes/features/TestScenariosScene';
|
||||||
|
import { LivingSkillsScene } from './scenes/features/LivingSkillsScene';
|
||||||
|
import { DeveloperSDKScene } from './scenes/features/DeveloperSDKScene';
|
||||||
|
import { FeaturesClosingScene } from './scenes/features/FeaturesClosingScene';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TPMJS Features Showcase Video
|
||||||
|
* Total: 2700 frames @ 30fps = 90 seconds
|
||||||
|
*
|
||||||
|
* A punchy showcase of all 9 platform features
|
||||||
|
*/
|
||||||
|
export const FeaturesVideo = () => {
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
// Scene timing (in seconds)
|
||||||
|
const scenes = [
|
||||||
|
{ component: FeaturesOpeningScene, start: 0, duration: 6 },
|
||||||
|
{ component: ToolRegistryScene, start: 6, duration: 9 },
|
||||||
|
{ component: OmegaAgentScene, start: 15, duration: 9 },
|
||||||
|
{ component: CollectionsFeatureScene, start: 24, duration: 8 },
|
||||||
|
{ component: CustomAgentsScene, start: 32, duration: 8 },
|
||||||
|
{ component: MCPProtocolScene, start: 40, duration: 9 },
|
||||||
|
{ component: SecureExecutionScene, start: 49, duration: 8 },
|
||||||
|
{ component: TestScenariosScene, start: 57, duration: 8 },
|
||||||
|
{ component: LivingSkillsScene, start: 65, duration: 8 },
|
||||||
|
{ component: DeveloperSDKScene, start: 73, duration: 9 },
|
||||||
|
{ component: FeaturesClosingScene, start: 82, duration: 8 },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AbsoluteFill style={{ backgroundColor: colors.bg.base }}>
|
||||||
|
{scenes.map(({ component: Component, start, duration }, index) => (
|
||||||
|
<Sequence
|
||||||
|
key={index}
|
||||||
|
from={start * fps}
|
||||||
|
durationInFrames={duration * fps}
|
||||||
|
premountFor={fps}
|
||||||
|
>
|
||||||
|
<Component />
|
||||||
|
</Sequence>
|
||||||
|
))}
|
||||||
|
</AbsoluteFill>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1,15 +1,26 @@
|
||||||
import { Composition } from 'remotion';
|
import { Composition } from 'remotion';
|
||||||
import { ExplainerVideo } from './ExplainerVideo';
|
import { ExplainerVideo } from './ExplainerVideo';
|
||||||
|
import { FeaturesVideo } from './FeaturesVideo';
|
||||||
|
|
||||||
export const RemotionRoot = () => {
|
export const RemotionRoot = () => {
|
||||||
return (
|
return (
|
||||||
<Composition
|
<>
|
||||||
id="TPMJSExplainer"
|
<Composition
|
||||||
component={ExplainerVideo}
|
id="TPMJSExplainer"
|
||||||
durationInFrames={9000}
|
component={ExplainerVideo}
|
||||||
fps={30}
|
durationInFrames={9000}
|
||||||
width={1920}
|
fps={30}
|
||||||
height={1080}
|
width={1920}
|
||||||
/>
|
height={1080}
|
||||||
|
/>
|
||||||
|
<Composition
|
||||||
|
id="TPMJSFeatures"
|
||||||
|
component={FeaturesVideo}
|
||||||
|
durationInFrames={2700}
|
||||||
|
fps={30}
|
||||||
|
width={1920}
|
||||||
|
height={1080}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
271
packages/video/src/scenes/features/CollectionsFeatureScene.tsx
Normal file
271
packages/video/src/scenes/features/CollectionsFeatureScene.tsx
Normal file
|
|
@ -0,0 +1,271 @@
|
||||||
|
import {
|
||||||
|
AbsoluteFill,
|
||||||
|
interpolate,
|
||||||
|
spring,
|
||||||
|
useCurrentFrame,
|
||||||
|
useVideoConfig,
|
||||||
|
} from 'remotion';
|
||||||
|
import { colors, typography, springConfigs } from '../../design-tokens';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feature 3: Collections (0:24 - 0:32)
|
||||||
|
* Curate tool sets for specific use cases
|
||||||
|
*/
|
||||||
|
|
||||||
|
const CollectionCard = ({
|
||||||
|
name,
|
||||||
|
toolCount,
|
||||||
|
category,
|
||||||
|
delay,
|
||||||
|
isActive = false,
|
||||||
|
}: {
|
||||||
|
name: string;
|
||||||
|
toolCount: number;
|
||||||
|
category: string;
|
||||||
|
delay: number;
|
||||||
|
isActive?: boolean;
|
||||||
|
}) => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
const progress = spring({
|
||||||
|
frame: frame - delay,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.snappy,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: 20,
|
||||||
|
backgroundColor: isActive ? colors.bg.surface2 : colors.bg.surface,
|
||||||
|
border: `1px ${isActive ? 'solid' : 'dashed'} ${isActive ? colors.copper.default : colors.border.default}`,
|
||||||
|
marginBottom: 12,
|
||||||
|
opacity: progress,
|
||||||
|
transform: `translateY(${interpolate(progress, [0, 1], [20, 0])}px) scale(${isActive ? 1.02 : 1})`,
|
||||||
|
boxShadow: isActive ? `0 0 20px ${colors.copper.glow}` : 'none',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
marginBottom: 8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.lg,
|
||||||
|
fontWeight: typography.fontWeight.semibold,
|
||||||
|
color: isActive ? colors.copper.default : colors.text.primary,
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: '4px 10px',
|
||||||
|
backgroundColor: colors.bg.surface2,
|
||||||
|
border: `1px dashed ${colors.border.default}`,
|
||||||
|
fontSize: typography.fontSize.xs,
|
||||||
|
color: colors.text.muted,
|
||||||
|
textTransform: 'lowercase',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{category}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: 4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Array.from({ length: Math.min(toolCount, 5) }).map((_, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
style={{
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
backgroundColor: colors.copper.muted,
|
||||||
|
opacity: 0.6 + i * 0.1,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{toolCount > 5 && (
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.xs,
|
||||||
|
color: colors.text.muted,
|
||||||
|
marginLeft: 4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
+{toolCount - 5}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.text.secondary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{toolCount} tools
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CollectionsFeatureScene = () => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
const headerProgress = spring({
|
||||||
|
frame,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.smooth,
|
||||||
|
});
|
||||||
|
|
||||||
|
const collections = [
|
||||||
|
{ name: 'data-science-kit', toolCount: 12, category: 'analytics' },
|
||||||
|
{ name: 'content-creation', toolCount: 8, category: 'marketing' },
|
||||||
|
{ name: 'devops-automation', toolCount: 15, category: 'infra' },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AbsoluteFill
|
||||||
|
style={{
|
||||||
|
backgroundColor: colors.bg.base,
|
||||||
|
fontFamily: typography.fontFamily.sans,
|
||||||
|
padding: 80,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Feature badge */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 60,
|
||||||
|
left: 80,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 12,
|
||||||
|
opacity: headerProgress,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
backgroundColor: colors.copper.default,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
fontSize: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
📁
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.copper.default,
|
||||||
|
letterSpacing: typography.letterSpacing.widest,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Feature 03
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize['2xl'],
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
color: colors.text.primary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Collections
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginLeft: 16,
|
||||||
|
padding: '4px 12px',
|
||||||
|
backgroundColor: colors.bg.surface2,
|
||||||
|
border: `1px dashed ${colors.border.default}`,
|
||||||
|
fontSize: typography.fontSize.xs,
|
||||||
|
color: colors.text.secondary,
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
shareable
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main content */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: 80,
|
||||||
|
marginTop: 160,
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Left - Description */}
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize['3xl'],
|
||||||
|
fontWeight: typography.fontWeight.semibold,
|
||||||
|
color: colors.text.primary,
|
||||||
|
marginBottom: 24,
|
||||||
|
lineHeight: typography.lineHeight.tight,
|
||||||
|
opacity: headerProgress,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Curate tool sets
|
||||||
|
<br />
|
||||||
|
for specific{' '}
|
||||||
|
<span style={{ color: colors.copper.default }}>use cases</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.lg,
|
||||||
|
color: colors.text.secondary,
|
||||||
|
lineHeight: typography.lineHeight.relaxed,
|
||||||
|
opacity: interpolate(frame, [fps * 1, fps * 2], [0, 1], {
|
||||||
|
extrapolateLeft: 'clamp',
|
||||||
|
extrapolateRight: 'clamp',
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add test scenarios to validate behavior.
|
||||||
|
<br />
|
||||||
|
Generate living documentation automatically.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right - Collection cards */}
|
||||||
|
<div style={{ flex: 1, maxWidth: 400 }}>
|
||||||
|
{collections.map((collection, i) => (
|
||||||
|
<CollectionCard
|
||||||
|
key={collection.name}
|
||||||
|
{...collection}
|
||||||
|
delay={fps * 1 + i * 12}
|
||||||
|
isActive={i === 0}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AbsoluteFill>
|
||||||
|
);
|
||||||
|
};
|
||||||
268
packages/video/src/scenes/features/CustomAgentsScene.tsx
Normal file
268
packages/video/src/scenes/features/CustomAgentsScene.tsx
Normal file
|
|
@ -0,0 +1,268 @@
|
||||||
|
import {
|
||||||
|
AbsoluteFill,
|
||||||
|
interpolate,
|
||||||
|
spring,
|
||||||
|
useCurrentFrame,
|
||||||
|
useVideoConfig,
|
||||||
|
} from 'remotion';
|
||||||
|
import { colors, typography, springConfigs } from '../../design-tokens';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feature 4: Custom Agents (0:32 - 0:40)
|
||||||
|
* Build AI agents with your choice of LLM
|
||||||
|
*/
|
||||||
|
|
||||||
|
const AgentCard = ({
|
||||||
|
name,
|
||||||
|
model,
|
||||||
|
tools,
|
||||||
|
status,
|
||||||
|
delay,
|
||||||
|
}: {
|
||||||
|
name: string;
|
||||||
|
model: string;
|
||||||
|
tools: number;
|
||||||
|
status: 'public' | 'private';
|
||||||
|
delay: number;
|
||||||
|
}) => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
const progress = spring({
|
||||||
|
frame: frame - delay,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.snappy,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: 20,
|
||||||
|
backgroundColor: colors.bg.surface,
|
||||||
|
border: `1px dashed ${colors.border.default}`,
|
||||||
|
width: 280,
|
||||||
|
opacity: progress,
|
||||||
|
transform: `scale(${interpolate(progress, [0, 1], [0.9, 1])})`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 12,
|
||||||
|
marginBottom: 16,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
backgroundColor: colors.copper.default,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
color: colors.white,
|
||||||
|
fontSize: typography.fontSize.lg,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{name[0].toUpperCase()}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.base,
|
||||||
|
fontWeight: typography.fontWeight.semibold,
|
||||||
|
color: colors.text.primary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.xs,
|
||||||
|
color: colors.text.muted,
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{model}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.text.secondary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tools} tools configured
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: '4px 10px',
|
||||||
|
backgroundColor:
|
||||||
|
status === 'public' ? colors.status.successMuted : colors.bg.surface2,
|
||||||
|
border: `1px solid ${status === 'public' ? colors.status.success : colors.border.default}`,
|
||||||
|
fontSize: typography.fontSize.xs,
|
||||||
|
color: status === 'public' ? colors.status.success : colors.text.muted,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{status}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CustomAgentsScene = () => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
const headerProgress = spring({
|
||||||
|
frame,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.smooth,
|
||||||
|
});
|
||||||
|
|
||||||
|
const agents = [
|
||||||
|
{ name: 'Research Bot', model: 'claude-opus-4', tools: 8, status: 'public' as const },
|
||||||
|
{ name: 'Code Review', model: 'gpt-4o', tools: 5, status: 'private' as const },
|
||||||
|
{ name: 'Data Analyst', model: 'claude-sonnet-4', tools: 12, status: 'public' as const },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AbsoluteFill
|
||||||
|
style={{
|
||||||
|
backgroundColor: colors.bg.base,
|
||||||
|
fontFamily: typography.fontFamily.sans,
|
||||||
|
padding: 80,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Feature badge */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 60,
|
||||||
|
left: 80,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 12,
|
||||||
|
opacity: headerProgress,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
backgroundColor: colors.copper.default,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
fontSize: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
👤
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.copper.default,
|
||||||
|
letterSpacing: typography.letterSpacing.widest,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Feature 04
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize['2xl'],
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
color: colors.text.primary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Custom Agents
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginLeft: 16,
|
||||||
|
padding: '4px 12px',
|
||||||
|
backgroundColor: colors.bg.surface2,
|
||||||
|
border: `1px dashed ${colors.border.default}`,
|
||||||
|
fontSize: typography.fontSize.xs,
|
||||||
|
color: colors.text.secondary,
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
unlimited
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Tagline */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: 180,
|
||||||
|
marginBottom: 48,
|
||||||
|
opacity: headerProgress,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize['3xl'],
|
||||||
|
fontWeight: typography.fontWeight.semibold,
|
||||||
|
color: colors.text.primary,
|
||||||
|
lineHeight: typography.lineHeight.tight,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Your LLM. Your prompts.
|
||||||
|
<br />
|
||||||
|
<span style={{ color: colors.copper.default }}>Your tools.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Agent cards */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: 24,
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{agents.map((agent, i) => (
|
||||||
|
<AgentCard key={agent.name} {...agent} delay={fps * 1.5 + i * 10} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Bottom text */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 80,
|
||||||
|
left: 80,
|
||||||
|
opacity: interpolate(frame, [fps * 4, fps * 5], [0, 1], {
|
||||||
|
extrapolateLeft: 'clamp',
|
||||||
|
extrapolateRight: 'clamp',
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.lg,
|
||||||
|
color: colors.text.muted,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Share publicly or keep private — your choice
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</AbsoluteFill>
|
||||||
|
);
|
||||||
|
};
|
||||||
321
packages/video/src/scenes/features/DeveloperSDKScene.tsx
Normal file
321
packages/video/src/scenes/features/DeveloperSDKScene.tsx
Normal file
|
|
@ -0,0 +1,321 @@
|
||||||
|
import {
|
||||||
|
AbsoluteFill,
|
||||||
|
interpolate,
|
||||||
|
spring,
|
||||||
|
useCurrentFrame,
|
||||||
|
useVideoConfig,
|
||||||
|
} from 'remotion';
|
||||||
|
import { colors, typography, springConfigs } from '../../design-tokens';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feature 9: Developer SDK (1:13 - 1:22)
|
||||||
|
* Publish tools with one keyword, TypeScript support
|
||||||
|
*/
|
||||||
|
|
||||||
|
const CodeLine = ({
|
||||||
|
content,
|
||||||
|
indent = 0,
|
||||||
|
delay,
|
||||||
|
highlight = false,
|
||||||
|
}: {
|
||||||
|
content: string;
|
||||||
|
indent?: number;
|
||||||
|
delay: number;
|
||||||
|
highlight?: boolean;
|
||||||
|
}) => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
const progress = spring({
|
||||||
|
frame: frame - delay,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.snappy,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
fontSize: typography.fontSize.base,
|
||||||
|
color: highlight ? colors.copper.default : colors.text.primary,
|
||||||
|
paddingLeft: indent * 24,
|
||||||
|
opacity: progress,
|
||||||
|
transform: `translateX(${interpolate(progress, [0, 1], [-10, 0])}px)`,
|
||||||
|
lineHeight: typography.lineHeight.relaxed,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DeveloperSDKScene = () => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
const headerProgress = spring({
|
||||||
|
frame,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.smooth,
|
||||||
|
});
|
||||||
|
|
||||||
|
const codeLines = [
|
||||||
|
{ content: '// package.json', indent: 0, highlight: false },
|
||||||
|
{ content: '{', indent: 0, highlight: false },
|
||||||
|
{ content: '"name": "my-awesome-tool",', indent: 1, highlight: false },
|
||||||
|
{ content: '"keywords": ["tpmjs"],', indent: 1, highlight: true },
|
||||||
|
{ content: '"tpmjs": {', indent: 1, highlight: true },
|
||||||
|
{ content: '"tools": ["./src/tools.ts"]', indent: 2, highlight: true },
|
||||||
|
{ content: '}', indent: 1, highlight: false },
|
||||||
|
{ content: '}', indent: 0, highlight: false },
|
||||||
|
];
|
||||||
|
|
||||||
|
const features = [
|
||||||
|
'TypeScript support',
|
||||||
|
'Vercel AI SDK integration',
|
||||||
|
'Auto schema extraction',
|
||||||
|
'Zero config publish',
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AbsoluteFill
|
||||||
|
style={{
|
||||||
|
backgroundColor: colors.bg.base,
|
||||||
|
fontFamily: typography.fontFamily.sans,
|
||||||
|
padding: 80,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Feature badge */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 60,
|
||||||
|
left: 80,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 12,
|
||||||
|
opacity: headerProgress,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
backgroundColor: colors.copper.default,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
fontSize: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{'</>'}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.copper.default,
|
||||||
|
letterSpacing: typography.letterSpacing.widest,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Feature 09
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize['2xl'],
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
color: colors.text.primary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Developer SDK
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginLeft: 16,
|
||||||
|
padding: '4px 12px',
|
||||||
|
backgroundColor: colors.status.errorMuted,
|
||||||
|
border: `1px solid ${colors.status.error}`,
|
||||||
|
fontSize: typography.fontSize.xs,
|
||||||
|
color: colors.status.error,
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
npm
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main content */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: 80,
|
||||||
|
marginTop: 160,
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Left - Code block */}
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundColor: colors.bg.surface,
|
||||||
|
border: `1px solid ${colors.border.default}`,
|
||||||
|
padding: 24,
|
||||||
|
maxWidth: 500,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: 8,
|
||||||
|
marginBottom: 16,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
borderRadius: '50%',
|
||||||
|
backgroundColor: colors.status.error,
|
||||||
|
opacity: 0.5,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
borderRadius: '50%',
|
||||||
|
backgroundColor: colors.status.warning,
|
||||||
|
opacity: 0.5,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
borderRadius: '50%',
|
||||||
|
backgroundColor: colors.status.success,
|
||||||
|
opacity: 0.5,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{codeLines.map((line, i) => (
|
||||||
|
<CodeLine
|
||||||
|
key={i}
|
||||||
|
{...line}
|
||||||
|
delay={fps * 0.5 + i * 5}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* npm publish command */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: 24,
|
||||||
|
padding: '16px 24px',
|
||||||
|
backgroundColor: colors.bg.surface2,
|
||||||
|
border: `1px dashed ${colors.border.default}`,
|
||||||
|
display: 'inline-flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 12,
|
||||||
|
opacity: interpolate(frame, [fps * 3, fps * 4], [0, 1], {
|
||||||
|
extrapolateLeft: 'clamp',
|
||||||
|
extrapolateRight: 'clamp',
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span style={{ color: colors.status.success }}>$</span>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
fontSize: typography.fontSize.lg,
|
||||||
|
color: colors.text.primary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
npm publish
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.text.muted,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
→ auto-discovered in minutes
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right - Features */}
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize['3xl'],
|
||||||
|
fontWeight: typography.fontWeight.semibold,
|
||||||
|
color: colors.text.primary,
|
||||||
|
marginBottom: 32,
|
||||||
|
opacity: headerProgress,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
One keyword.
|
||||||
|
<br />
|
||||||
|
<span style={{ color: colors.copper.default }}>Instant discovery.</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: 16,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{features.map((feature, i) => {
|
||||||
|
const featureProgress = spring({
|
||||||
|
frame: frame - fps * 2 - i * 8,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.snappy,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={feature}
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 16,
|
||||||
|
opacity: featureProgress,
|
||||||
|
transform: `translateX(${interpolate(featureProgress, [0, 1], [20, 0])}px)`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
backgroundColor: colors.status.successMuted,
|
||||||
|
border: `1px solid ${colors.status.success}`,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.status.success,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
✓
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.lg,
|
||||||
|
color: colors.text.secondary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{feature}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AbsoluteFill>
|
||||||
|
);
|
||||||
|
};
|
||||||
208
packages/video/src/scenes/features/FeaturesClosingScene.tsx
Normal file
208
packages/video/src/scenes/features/FeaturesClosingScene.tsx
Normal file
|
|
@ -0,0 +1,208 @@
|
||||||
|
import {
|
||||||
|
AbsoluteFill,
|
||||||
|
interpolate,
|
||||||
|
spring,
|
||||||
|
useCurrentFrame,
|
||||||
|
useVideoConfig,
|
||||||
|
Easing,
|
||||||
|
} from 'remotion';
|
||||||
|
import { colors, typography, springConfigs } from '../../design-tokens';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Features Closing Scene (1:22 - 1:30)
|
||||||
|
* Call to action with tpmjs.com
|
||||||
|
*/
|
||||||
|
export const FeaturesClosingScene = () => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
// All features revealed
|
||||||
|
const gridProgress = spring({
|
||||||
|
frame,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.smooth,
|
||||||
|
});
|
||||||
|
|
||||||
|
// URL animation
|
||||||
|
const urlProgress = spring({
|
||||||
|
frame: frame - fps * 1.5,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.bouncy,
|
||||||
|
});
|
||||||
|
|
||||||
|
// CTA animation
|
||||||
|
const ctaProgress = spring({
|
||||||
|
frame: frame - fps * 3,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.snappy,
|
||||||
|
});
|
||||||
|
|
||||||
|
const features = [
|
||||||
|
'Tool Registry',
|
||||||
|
'Omega Agent',
|
||||||
|
'Collections',
|
||||||
|
'Custom Agents',
|
||||||
|
'MCP Protocol',
|
||||||
|
'Secure Execution',
|
||||||
|
'Test Scenarios',
|
||||||
|
'Living Skills',
|
||||||
|
'Developer SDK',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Pulsing glow
|
||||||
|
const glowIntensity = Math.sin(frame * 0.08) * 0.3 + 0.7;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AbsoluteFill
|
||||||
|
style={{
|
||||||
|
backgroundColor: colors.bg.base,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
fontFamily: typography.fontFamily.sans,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Radial gradient background */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
inset: 0,
|
||||||
|
background: `radial-gradient(ellipse at center, ${colors.copper.glow} 0%, transparent 60%)`,
|
||||||
|
opacity: 0.2 * glowIntensity,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Features grid (small) */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 80,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: 12,
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
padding: '0 200px',
|
||||||
|
opacity: gridProgress,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{features.map((feature, i) => (
|
||||||
|
<div
|
||||||
|
key={feature}
|
||||||
|
style={{
|
||||||
|
padding: '8px 16px',
|
||||||
|
backgroundColor: colors.bg.surface,
|
||||||
|
border: `1px dashed ${colors.border.default}`,
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.text.secondary,
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
textTransform: 'lowercase',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{feature}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main CTA */}
|
||||||
|
<div style={{ textAlign: 'center' }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize['4xl'],
|
||||||
|
fontWeight: typography.fontWeight.semibold,
|
||||||
|
color: colors.text.primary,
|
||||||
|
marginBottom: 32,
|
||||||
|
opacity: urlProgress,
|
||||||
|
transform: `translateY(${interpolate(urlProgress, [0, 1], [30, 0])}px)`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Start building with AI tools
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* URL box */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'inline-block',
|
||||||
|
padding: '24px 48px',
|
||||||
|
backgroundColor: colors.bg.surface,
|
||||||
|
border: `2px solid ${colors.copper.default}`,
|
||||||
|
marginBottom: 32,
|
||||||
|
opacity: urlProgress,
|
||||||
|
transform: `scale(${interpolate(urlProgress, [0, 1], [0.9, 1])})`,
|
||||||
|
boxShadow: `0 0 ${40 * glowIntensity}px ${colors.copper.glow}`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
fontSize: typography.fontSize['5xl'],
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
color: colors.copper.default,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
tpmjs.com
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Action buttons */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: 24,
|
||||||
|
justifyContent: 'center',
|
||||||
|
opacity: ctaProgress,
|
||||||
|
transform: `translateY(${interpolate(ctaProgress, [0, 1], [20, 0])}px)`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: '16px 32px',
|
||||||
|
backgroundColor: colors.copper.default,
|
||||||
|
fontSize: typography.fontSize.lg,
|
||||||
|
fontWeight: typography.fontWeight.semibold,
|
||||||
|
color: colors.white,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Try Omega Agent
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: '16px 32px',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
border: `1px solid ${colors.border.default}`,
|
||||||
|
fontSize: typography.fontSize.lg,
|
||||||
|
color: colors.text.secondary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Read the Docs
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Bottom tagline */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 60,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
textAlign: 'center',
|
||||||
|
opacity: interpolate(frame, [fps * 5, fps * 6], [0, 1], {
|
||||||
|
extrapolateLeft: 'clamp',
|
||||||
|
extrapolateRight: 'clamp',
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.lg,
|
||||||
|
color: colors.text.muted,
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
The Package Manager for AI Tools
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</AbsoluteFill>
|
||||||
|
);
|
||||||
|
};
|
||||||
160
packages/video/src/scenes/features/FeaturesOpeningScene.tsx
Normal file
160
packages/video/src/scenes/features/FeaturesOpeningScene.tsx
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
import {
|
||||||
|
AbsoluteFill,
|
||||||
|
interpolate,
|
||||||
|
spring,
|
||||||
|
useCurrentFrame,
|
||||||
|
useVideoConfig,
|
||||||
|
Easing,
|
||||||
|
} from 'remotion';
|
||||||
|
import { colors, typography, springConfigs } from '../../design-tokens';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Features Opening Scene (0:00 - 0:06)
|
||||||
|
* Bold intro with TPMJS branding and tagline
|
||||||
|
*/
|
||||||
|
export const FeaturesOpeningScene = () => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
// Logo animation
|
||||||
|
const logoScale = spring({
|
||||||
|
frame,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.bouncy,
|
||||||
|
});
|
||||||
|
|
||||||
|
const logoRotate = interpolate(frame, [0, fps * 0.5], [-10, 0], {
|
||||||
|
extrapolateLeft: 'clamp',
|
||||||
|
extrapolateRight: 'clamp',
|
||||||
|
easing: Easing.out(Easing.back(2)),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Tagline animation
|
||||||
|
const taglineProgress = spring({
|
||||||
|
frame: frame - fps * 1,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.smooth,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Feature count animation
|
||||||
|
const countProgress = spring({
|
||||||
|
frame: frame - fps * 2,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.snappy,
|
||||||
|
});
|
||||||
|
|
||||||
|
const featureCount = Math.floor(
|
||||||
|
interpolate(frame, [fps * 2, fps * 3.5], [0, 9], {
|
||||||
|
extrapolateLeft: 'clamp',
|
||||||
|
extrapolateRight: 'clamp',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Copper line sweep
|
||||||
|
const lineWidth = interpolate(frame, [fps * 3, fps * 5], [0, 100], {
|
||||||
|
extrapolateLeft: 'clamp',
|
||||||
|
extrapolateRight: 'clamp',
|
||||||
|
easing: Easing.out(Easing.cubic),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AbsoluteFill
|
||||||
|
style={{
|
||||||
|
backgroundColor: colors.bg.base,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
fontFamily: typography.fontFamily.sans,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Gradient overlay */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
inset: 0,
|
||||||
|
background: `radial-gradient(ellipse at center, ${colors.copper.glow} 0%, transparent 70%)`,
|
||||||
|
opacity: 0.15,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Main content */}
|
||||||
|
<div style={{ textAlign: 'center' }}>
|
||||||
|
{/* Logo/Brand */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize['8xl'],
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
color: colors.text.primary,
|
||||||
|
letterSpacing: typography.letterSpacing.tight,
|
||||||
|
transform: `scale(${logoScale}) rotate(${logoRotate}deg)`,
|
||||||
|
marginBottom: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span style={{ color: colors.copper.default }}>tpm</span>js
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Tagline */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize['2xl'],
|
||||||
|
color: colors.text.secondary,
|
||||||
|
opacity: taglineProgress,
|
||||||
|
transform: `translateY(${interpolate(taglineProgress, [0, 1], [20, 0])}px)`,
|
||||||
|
marginBottom: 48,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
The Package Manager for AI Tools
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Feature count */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'inline-flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 16,
|
||||||
|
padding: '16px 32px',
|
||||||
|
backgroundColor: colors.bg.surface,
|
||||||
|
border: `2px solid ${colors.copper.default}`,
|
||||||
|
opacity: countProgress,
|
||||||
|
transform: `scale(${interpolate(countProgress, [0, 1], [0.9, 1])})`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize['4xl'],
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
color: colors.copper.default,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{featureCount}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.xl,
|
||||||
|
color: colors.text.primary,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: typography.letterSpacing.widest,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Core Features
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Bottom copper line */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 100,
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translateX(-50%)',
|
||||||
|
width: `${lineWidth}%`,
|
||||||
|
height: 3,
|
||||||
|
backgroundColor: colors.copper.default,
|
||||||
|
boxShadow: `0 0 30px ${colors.copper.glow}`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</AbsoluteFill>
|
||||||
|
);
|
||||||
|
};
|
||||||
311
packages/video/src/scenes/features/LivingSkillsScene.tsx
Normal file
311
packages/video/src/scenes/features/LivingSkillsScene.tsx
Normal file
|
|
@ -0,0 +1,311 @@
|
||||||
|
import {
|
||||||
|
AbsoluteFill,
|
||||||
|
interpolate,
|
||||||
|
spring,
|
||||||
|
useCurrentFrame,
|
||||||
|
useVideoConfig,
|
||||||
|
} from 'remotion';
|
||||||
|
import { colors, typography, springConfigs } from '../../design-tokens';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feature 8: Living Skills (1:05 - 1:13)
|
||||||
|
* Documentation that evolves from real usage
|
||||||
|
*/
|
||||||
|
|
||||||
|
const SkillNode = ({
|
||||||
|
label,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
delay,
|
||||||
|
size = 'md',
|
||||||
|
}: {
|
||||||
|
label: string;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
delay: number;
|
||||||
|
size?: 'sm' | 'md' | 'lg';
|
||||||
|
}) => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
const progress = spring({
|
||||||
|
frame: frame - delay,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.bouncy,
|
||||||
|
});
|
||||||
|
|
||||||
|
const sizes = {
|
||||||
|
sm: { w: 100, h: 40, font: typography.fontSize.xs },
|
||||||
|
md: { w: 140, h: 50, font: typography.fontSize.sm },
|
||||||
|
lg: { w: 180, h: 60, font: typography.fontSize.base },
|
||||||
|
};
|
||||||
|
|
||||||
|
const s = sizes[size];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
left: x,
|
||||||
|
top: y,
|
||||||
|
width: s.w,
|
||||||
|
height: s.h,
|
||||||
|
backgroundColor: size === 'lg' ? colors.copper.default : colors.bg.surface2,
|
||||||
|
border: `1px ${size === 'lg' ? 'solid' : 'dashed'} ${size === 'lg' ? colors.copper.default : colors.border.default}`,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
opacity: progress,
|
||||||
|
transform: `scale(${interpolate(progress, [0, 1], [0.5, 1])})`,
|
||||||
|
boxShadow: size === 'lg' ? `0 0 30px ${colors.copper.glow}` : 'none',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
fontSize: s.font,
|
||||||
|
color: size === 'lg' ? colors.white : colors.text.primary,
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LivingSkillsScene = () => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
const headerProgress = spring({
|
||||||
|
frame,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.smooth,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Skill nodes with positions
|
||||||
|
const skills = [
|
||||||
|
{ label: 'API Design', x: 800, y: 300, size: 'lg' as const, delay: fps * 1 },
|
||||||
|
{ label: 'Error Handling', x: 600, y: 200, size: 'md' as const, delay: fps * 1.5 },
|
||||||
|
{ label: 'Rate Limits', x: 1000, y: 200, size: 'md' as const, delay: fps * 1.8 },
|
||||||
|
{ label: 'Caching', x: 550, y: 400, size: 'sm' as const, delay: fps * 2.2 },
|
||||||
|
{ label: 'Auth', x: 700, y: 450, size: 'sm' as const, delay: fps * 2.5 },
|
||||||
|
{ label: 'Webhooks', x: 950, y: 420, size: 'sm' as const, delay: fps * 2.8 },
|
||||||
|
{ label: 'Pagination', x: 1100, y: 350, size: 'sm' as const, delay: fps * 3 },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Connection lines progress
|
||||||
|
const linesProgress = interpolate(frame, [fps * 3, fps * 5], [0, 1], {
|
||||||
|
extrapolateLeft: 'clamp',
|
||||||
|
extrapolateRight: 'clamp',
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AbsoluteFill
|
||||||
|
style={{
|
||||||
|
backgroundColor: colors.bg.base,
|
||||||
|
fontFamily: typography.fontFamily.sans,
|
||||||
|
padding: 80,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Feature badge */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 60,
|
||||||
|
left: 80,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 12,
|
||||||
|
opacity: headerProgress,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
backgroundColor: colors.copper.default,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
fontSize: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
💬
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.copper.default,
|
||||||
|
letterSpacing: typography.letterSpacing.widest,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Feature 08
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize['2xl'],
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
color: colors.text.primary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Living Skills
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginLeft: 16,
|
||||||
|
padding: '4px 12px',
|
||||||
|
backgroundColor: colors.status.infoMuted,
|
||||||
|
border: `1px solid ${colors.status.info}`,
|
||||||
|
fontSize: typography.fontSize.xs,
|
||||||
|
color: colors.status.info,
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
NEW
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Left description */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
left: 80,
|
||||||
|
top: 200,
|
||||||
|
maxWidth: 400,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize['3xl'],
|
||||||
|
fontWeight: typography.fontWeight.semibold,
|
||||||
|
color: colors.text.primary,
|
||||||
|
lineHeight: typography.lineHeight.tight,
|
||||||
|
marginBottom: 24,
|
||||||
|
opacity: headerProgress,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Documentation that{' '}
|
||||||
|
<span style={{ color: colors.copper.default }}>evolves</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.lg,
|
||||||
|
color: colors.text.secondary,
|
||||||
|
lineHeight: typography.lineHeight.relaxed,
|
||||||
|
opacity: interpolate(frame, [fps * 1, fps * 2], [0, 1], {
|
||||||
|
extrapolateLeft: 'clamp',
|
||||||
|
extrapolateRight: 'clamp',
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Skills emerge from question patterns
|
||||||
|
<br />
|
||||||
|
and proven behaviors in real usage.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Skill graph */}
|
||||||
|
<svg
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
inset: 0,
|
||||||
|
pointerEvents: 'none',
|
||||||
|
}}
|
||||||
|
viewBox="0 0 1920 1080"
|
||||||
|
>
|
||||||
|
{/* Connection lines */}
|
||||||
|
<g opacity={linesProgress}>
|
||||||
|
<line
|
||||||
|
x1="870"
|
||||||
|
y1="330"
|
||||||
|
x2="670"
|
||||||
|
y2="225"
|
||||||
|
stroke={colors.border.default}
|
||||||
|
strokeWidth="1"
|
||||||
|
strokeDasharray="4 4"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="940"
|
||||||
|
y1="330"
|
||||||
|
x2="1070"
|
||||||
|
y2="225"
|
||||||
|
stroke={colors.border.default}
|
||||||
|
strokeWidth="1"
|
||||||
|
strokeDasharray="4 4"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="840"
|
||||||
|
y1="360"
|
||||||
|
x2="610"
|
||||||
|
y2="420"
|
||||||
|
stroke={colors.border.default}
|
||||||
|
strokeWidth="1"
|
||||||
|
strokeDasharray="4 4"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="870"
|
||||||
|
y1="360"
|
||||||
|
x2="750"
|
||||||
|
y2="470"
|
||||||
|
stroke={colors.border.default}
|
||||||
|
strokeWidth="1"
|
||||||
|
strokeDasharray="4 4"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="940"
|
||||||
|
y1="360"
|
||||||
|
x2="1000"
|
||||||
|
y2="440"
|
||||||
|
stroke={colors.border.default}
|
||||||
|
strokeWidth="1"
|
||||||
|
strokeDasharray="4 4"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="970"
|
||||||
|
y1="340"
|
||||||
|
x2="1150"
|
||||||
|
y2="370"
|
||||||
|
stroke={colors.border.default}
|
||||||
|
strokeWidth="1"
|
||||||
|
strokeDasharray="4 4"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
{/* Skill nodes */}
|
||||||
|
{skills.map((skill) => (
|
||||||
|
<SkillNode key={skill.label} {...skill} />
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* Bottom quote */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 80,
|
||||||
|
left: 80,
|
||||||
|
right: 80,
|
||||||
|
textAlign: 'center',
|
||||||
|
opacity: interpolate(frame, [fps * 5, fps * 6], [0, 1], {
|
||||||
|
extrapolateLeft: 'clamp',
|
||||||
|
extrapolateRight: 'clamp',
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.lg,
|
||||||
|
color: colors.text.muted,
|
||||||
|
fontStyle: 'italic',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
"Skills, proven in the wild — not declared on paper"
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</AbsoluteFill>
|
||||||
|
);
|
||||||
|
};
|
||||||
298
packages/video/src/scenes/features/MCPProtocolScene.tsx
Normal file
298
packages/video/src/scenes/features/MCPProtocolScene.tsx
Normal file
|
|
@ -0,0 +1,298 @@
|
||||||
|
import {
|
||||||
|
AbsoluteFill,
|
||||||
|
interpolate,
|
||||||
|
spring,
|
||||||
|
useCurrentFrame,
|
||||||
|
useVideoConfig,
|
||||||
|
} from 'remotion';
|
||||||
|
import { colors, typography, springConfigs } from '../../design-tokens';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feature 5: MCP Protocol (0:40 - 0:49)
|
||||||
|
* Works with Claude Desktop, Cursor, Windsurf, and any MCP client
|
||||||
|
*/
|
||||||
|
|
||||||
|
const ClientLogo = ({
|
||||||
|
name,
|
||||||
|
delay,
|
||||||
|
connected,
|
||||||
|
}: {
|
||||||
|
name: string;
|
||||||
|
delay: number;
|
||||||
|
connected: boolean;
|
||||||
|
}) => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
const progress = spring({
|
||||||
|
frame: frame - delay,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.bouncy,
|
||||||
|
});
|
||||||
|
|
||||||
|
const connectionProgress = spring({
|
||||||
|
frame: frame - delay - fps * 0.5,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.snappy,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 12,
|
||||||
|
opacity: progress,
|
||||||
|
transform: `translateY(${interpolate(progress, [0, 1], [30, 0])}px)`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 80,
|
||||||
|
height: 80,
|
||||||
|
backgroundColor: colors.bg.surface2,
|
||||||
|
border: `2px ${connected ? 'solid' : 'dashed'} ${connected ? colors.status.success : colors.border.default}`,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
fontSize: typography.fontSize['2xl'],
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
color: connected ? colors.status.success : colors.text.secondary,
|
||||||
|
boxShadow: connected ? `0 0 20px ${colors.status.successMuted}` : 'none',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{name[0]}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.text.secondary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
|
{connected && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.xs,
|
||||||
|
color: colors.status.success,
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
opacity: connectionProgress,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
connected
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MCPProtocolScene = () => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
const headerProgress = spring({
|
||||||
|
frame,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.smooth,
|
||||||
|
});
|
||||||
|
|
||||||
|
const urlProgress = spring({
|
||||||
|
frame: frame - fps * 2,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.snappy,
|
||||||
|
});
|
||||||
|
|
||||||
|
const clients = [
|
||||||
|
{ name: 'Claude', connected: true },
|
||||||
|
{ name: 'Cursor', connected: true },
|
||||||
|
{ name: 'Windsurf', connected: true },
|
||||||
|
{ name: 'Any MCP', connected: false },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Connection line animation
|
||||||
|
const lineProgress = interpolate(frame, [fps * 3, fps * 5], [0, 1], {
|
||||||
|
extrapolateLeft: 'clamp',
|
||||||
|
extrapolateRight: 'clamp',
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AbsoluteFill
|
||||||
|
style={{
|
||||||
|
backgroundColor: colors.bg.base,
|
||||||
|
fontFamily: typography.fontFamily.sans,
|
||||||
|
padding: 80,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Feature badge */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 60,
|
||||||
|
left: 80,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 12,
|
||||||
|
opacity: headerProgress,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
backgroundColor: colors.copper.default,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
fontSize: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
🔗
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.copper.default,
|
||||||
|
letterSpacing: typography.letterSpacing.widest,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Feature 05
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize['2xl'],
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
color: colors.text.primary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
MCP Protocol
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginLeft: 16,
|
||||||
|
padding: '4px 12px',
|
||||||
|
backgroundColor: colors.bg.surface2,
|
||||||
|
border: `1px dashed ${colors.border.default}`,
|
||||||
|
fontSize: typography.fontSize.xs,
|
||||||
|
color: colors.text.secondary,
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
universal
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Center URL box */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
left: '50%',
|
||||||
|
top: '45%',
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: '20px 40px',
|
||||||
|
backgroundColor: colors.bg.surface,
|
||||||
|
border: `2px solid ${colors.copper.default}`,
|
||||||
|
marginBottom: 24,
|
||||||
|
opacity: urlProgress,
|
||||||
|
transform: `scale(${interpolate(urlProgress, [0, 1], [0.9, 1])})`,
|
||||||
|
boxShadow: `0 0 30px ${colors.copper.glow}`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.xs,
|
||||||
|
color: colors.text.muted,
|
||||||
|
marginBottom: 8,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: typography.letterSpacing.widest,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
MCP Server URL
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
fontSize: typography.fontSize.xl,
|
||||||
|
color: colors.copper.default,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
tpmjs.com/mcp
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.lg,
|
||||||
|
color: colors.text.secondary,
|
||||||
|
opacity: urlProgress,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
One URL. Instant access to all tools.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Client logos */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 120,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: 60,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{clients.map((client, i) => (
|
||||||
|
<ClientLogo
|
||||||
|
key={client.name}
|
||||||
|
{...client}
|
||||||
|
delay={fps * 1 + i * 8}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Connection lines */}
|
||||||
|
<svg
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
inset: 0,
|
||||||
|
pointerEvents: 'none',
|
||||||
|
}}
|
||||||
|
viewBox="0 0 1920 1080"
|
||||||
|
>
|
||||||
|
{clients.slice(0, 3).map((_, i) => {
|
||||||
|
const startX = 480 + i * 320;
|
||||||
|
const startY = 820;
|
||||||
|
const endX = 960;
|
||||||
|
const endY = 520;
|
||||||
|
|
||||||
|
const path = `M ${startX} ${startY} Q ${startX} ${(startY + endY) / 2} ${endX} ${endY}`;
|
||||||
|
const pathLength = 400; // Approximate
|
||||||
|
|
||||||
|
return (
|
||||||
|
<path
|
||||||
|
key={i}
|
||||||
|
d={path}
|
||||||
|
fill="none"
|
||||||
|
stroke={colors.copper.default}
|
||||||
|
strokeWidth={2}
|
||||||
|
strokeDasharray={pathLength}
|
||||||
|
strokeDashoffset={pathLength * (1 - lineProgress)}
|
||||||
|
opacity={0.5}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</svg>
|
||||||
|
</AbsoluteFill>
|
||||||
|
);
|
||||||
|
};
|
||||||
265
packages/video/src/scenes/features/OmegaAgentScene.tsx
Normal file
265
packages/video/src/scenes/features/OmegaAgentScene.tsx
Normal file
|
|
@ -0,0 +1,265 @@
|
||||||
|
import {
|
||||||
|
AbsoluteFill,
|
||||||
|
interpolate,
|
||||||
|
spring,
|
||||||
|
useCurrentFrame,
|
||||||
|
useVideoConfig,
|
||||||
|
} from 'remotion';
|
||||||
|
import { colors, typography, springConfigs } from '../../design-tokens';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feature 2: Omega Agent (0:15 - 0:24)
|
||||||
|
* AI that dynamically discovers and executes tools
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Message = ({
|
||||||
|
role,
|
||||||
|
content,
|
||||||
|
delay,
|
||||||
|
}: {
|
||||||
|
role: 'user' | 'assistant';
|
||||||
|
content: string;
|
||||||
|
delay: number;
|
||||||
|
}) => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
const progress = spring({
|
||||||
|
frame: frame - delay,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.snappy,
|
||||||
|
});
|
||||||
|
|
||||||
|
const isUser = role === 'user';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: isUser ? 'flex-end' : 'flex-start',
|
||||||
|
marginBottom: 16,
|
||||||
|
opacity: progress,
|
||||||
|
transform: `translateY(${interpolate(progress, [0, 1], [15, 0])}px)`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
maxWidth: 420,
|
||||||
|
padding: '14px 18px',
|
||||||
|
backgroundColor: isUser ? colors.copper.default : colors.bg.surface2,
|
||||||
|
border: isUser ? 'none' : `1px dashed ${colors.border.default}`,
|
||||||
|
color: isUser ? colors.white : colors.text.primary,
|
||||||
|
fontSize: typography.fontSize.base,
|
||||||
|
lineHeight: typography.lineHeight.relaxed,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ToolPill = ({ name, delay }: { name: string; delay: number }) => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
const progress = spring({
|
||||||
|
frame: frame - delay,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.bouncy,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'inline-flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 6,
|
||||||
|
padding: '6px 12px',
|
||||||
|
backgroundColor: colors.bg.surface,
|
||||||
|
border: `1px solid ${colors.status.success}`,
|
||||||
|
marginRight: 8,
|
||||||
|
opacity: progress,
|
||||||
|
transform: `scale(${interpolate(progress, [0, 1], [0.7, 1])})`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 6,
|
||||||
|
height: 6,
|
||||||
|
borderRadius: '50%',
|
||||||
|
backgroundColor: colors.status.success,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
fontSize: typography.fontSize.xs,
|
||||||
|
color: colors.status.success,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OmegaAgentScene = () => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
const headerProgress = spring({
|
||||||
|
frame,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.smooth,
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectedTools = ['web-scraper', 'sentiment', 'chart-gen'];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AbsoluteFill
|
||||||
|
style={{
|
||||||
|
backgroundColor: colors.bg.base,
|
||||||
|
fontFamily: typography.fontFamily.sans,
|
||||||
|
padding: 80,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Feature badge */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 60,
|
||||||
|
left: 80,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 12,
|
||||||
|
opacity: headerProgress,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
backgroundColor: colors.copper.default,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
fontSize: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
🧩
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.copper.default,
|
||||||
|
letterSpacing: typography.letterSpacing.widest,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Feature 02
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize['2xl'],
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
color: colors.text.primary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Omega Agent
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginLeft: 16,
|
||||||
|
padding: '4px 12px',
|
||||||
|
backgroundColor: colors.status.successMuted,
|
||||||
|
border: `1px solid ${colors.status.success}`,
|
||||||
|
fontSize: typography.fontSize.xs,
|
||||||
|
color: colors.status.success,
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
LIVE
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Chat interface */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: 160,
|
||||||
|
maxWidth: 700,
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Message
|
||||||
|
role="user"
|
||||||
|
content="Scrape competitor pricing pages and create a comparison chart"
|
||||||
|
delay={fps * 0.5}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Tool selection */}
|
||||||
|
{frame > fps * 1.5 && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginBottom: 16,
|
||||||
|
marginLeft: 8,
|
||||||
|
opacity: interpolate(frame, [fps * 1.5, fps * 2], [0, 1], {
|
||||||
|
extrapolateLeft: 'clamp',
|
||||||
|
extrapolateRight: 'clamp',
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.text.muted,
|
||||||
|
marginBottom: 8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Auto-selected tools:
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{selectedTools.map((tool, i) => (
|
||||||
|
<ToolPill key={tool} name={tool} delay={fps * 2 + i * 6} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Message
|
||||||
|
role="assistant"
|
||||||
|
content="I've scraped 5 competitor pages and generated a comparison chart. Found 23% average price difference..."
|
||||||
|
delay={fps * 3.5}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Bottom highlight */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 80,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
textAlign: 'center',
|
||||||
|
opacity: interpolate(frame, [fps * 5, fps * 6], [0, 1], {
|
||||||
|
extrapolateLeft: 'clamp',
|
||||||
|
extrapolateRight: 'clamp',
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.xl,
|
||||||
|
color: colors.text.secondary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
No configuration needed.{' '}
|
||||||
|
<span style={{ color: colors.copper.default }}>Just ask.</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</AbsoluteFill>
|
||||||
|
);
|
||||||
|
};
|
||||||
261
packages/video/src/scenes/features/SecureExecutionScene.tsx
Normal file
261
packages/video/src/scenes/features/SecureExecutionScene.tsx
Normal file
|
|
@ -0,0 +1,261 @@
|
||||||
|
import {
|
||||||
|
AbsoluteFill,
|
||||||
|
interpolate,
|
||||||
|
spring,
|
||||||
|
useCurrentFrame,
|
||||||
|
useVideoConfig,
|
||||||
|
} from 'remotion';
|
||||||
|
import { colors, typography, springConfigs } from '../../design-tokens';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feature 6: Secure Execution (0:49 - 0:57)
|
||||||
|
* Isolated sandboxes, rate limiting, encrypted credentials
|
||||||
|
*/
|
||||||
|
|
||||||
|
const SecurityLayer = ({
|
||||||
|
label,
|
||||||
|
icon,
|
||||||
|
delay,
|
||||||
|
index,
|
||||||
|
}: {
|
||||||
|
label: string;
|
||||||
|
icon: string;
|
||||||
|
delay: number;
|
||||||
|
index: number;
|
||||||
|
}) => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
const progress = spring({
|
||||||
|
frame: frame - delay,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.snappy,
|
||||||
|
});
|
||||||
|
|
||||||
|
const width = 300 - index * 40;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width,
|
||||||
|
padding: '16px 24px',
|
||||||
|
backgroundColor: colors.bg.surface,
|
||||||
|
border: `1px solid ${colors.copper.default}`,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 16,
|
||||||
|
marginBottom: 16,
|
||||||
|
opacity: progress,
|
||||||
|
transform: `translateY(${interpolate(progress, [0, 1], [-20, 0])}px)`,
|
||||||
|
boxShadow: `0 ${4 + index * 2}px ${10 + index * 5}px rgba(0,0,0,0.3)`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span style={{ fontSize: 20 }}>{icon}</span>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.text.primary,
|
||||||
|
textTransform: 'lowercase',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SecureExecutionScene = () => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
const headerProgress = spring({
|
||||||
|
frame,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.smooth,
|
||||||
|
});
|
||||||
|
|
||||||
|
const shieldPulse = Math.sin(frame * 0.1) * 0.1 + 1;
|
||||||
|
|
||||||
|
const layers = [
|
||||||
|
{ label: 'isolated sandbox', icon: '📦' },
|
||||||
|
{ label: 'rate limiting', icon: '⏱️' },
|
||||||
|
{ label: 'timeout handling', icon: '⚡' },
|
||||||
|
{ label: 'encrypted at rest', icon: '🔐' },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AbsoluteFill
|
||||||
|
style={{
|
||||||
|
backgroundColor: colors.bg.base,
|
||||||
|
fontFamily: typography.fontFamily.sans,
|
||||||
|
padding: 80,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Feature badge */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 60,
|
||||||
|
left: 80,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 12,
|
||||||
|
opacity: headerProgress,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
backgroundColor: colors.copper.default,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
fontSize: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
🔑
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.copper.default,
|
||||||
|
letterSpacing: typography.letterSpacing.widest,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Feature 06
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize['2xl'],
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
color: colors.text.primary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Secure Execution
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginLeft: 16,
|
||||||
|
padding: '4px 12px',
|
||||||
|
backgroundColor: colors.status.successMuted,
|
||||||
|
border: `1px solid ${colors.status.success}`,
|
||||||
|
fontSize: typography.fontSize.xs,
|
||||||
|
color: colors.status.success,
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
sandboxed
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main content */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: 120,
|
||||||
|
marginTop: 180,
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Left - Shield visualization */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 200,
|
||||||
|
height: 240,
|
||||||
|
position: 'relative',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
transform: `scale(${shieldPulse})`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Shield shape using CSS */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 180,
|
||||||
|
height: 200,
|
||||||
|
backgroundColor: colors.copper.default,
|
||||||
|
clipPath: 'polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%)',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
boxShadow: `0 0 60px ${colors.copper.glow}`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 160,
|
||||||
|
height: 180,
|
||||||
|
backgroundColor: colors.bg.surface,
|
||||||
|
clipPath: 'polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%)',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span style={{ fontSize: 64 }}>🛡️</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right - Security layers */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{layers.map((layer, i) => (
|
||||||
|
<SecurityLayer
|
||||||
|
key={layer.label}
|
||||||
|
{...layer}
|
||||||
|
index={i}
|
||||||
|
delay={fps * 1 + i * 10}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Bottom text */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 80,
|
||||||
|
left: 80,
|
||||||
|
right: 80,
|
||||||
|
textAlign: 'center',
|
||||||
|
opacity: interpolate(frame, [fps * 5, fps * 6], [0, 1], {
|
||||||
|
extrapolateLeft: 'clamp',
|
||||||
|
extrapolateRight: 'clamp',
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.xl,
|
||||||
|
color: colors.text.secondary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Every tool runs in{' '}
|
||||||
|
<span style={{ color: colors.copper.default }}>complete isolation</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</AbsoluteFill>
|
||||||
|
);
|
||||||
|
};
|
||||||
284
packages/video/src/scenes/features/TestScenariosScene.tsx
Normal file
284
packages/video/src/scenes/features/TestScenariosScene.tsx
Normal file
|
|
@ -0,0 +1,284 @@
|
||||||
|
import {
|
||||||
|
AbsoluteFill,
|
||||||
|
interpolate,
|
||||||
|
spring,
|
||||||
|
useCurrentFrame,
|
||||||
|
useVideoConfig,
|
||||||
|
} from 'remotion';
|
||||||
|
import { colors, typography, springConfigs } from '../../design-tokens';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feature 7: Test Scenarios (0:57 - 1:05)
|
||||||
|
* AI-generated test scenarios, pass rates, execution times
|
||||||
|
*/
|
||||||
|
|
||||||
|
const TestResult = ({
|
||||||
|
name,
|
||||||
|
status,
|
||||||
|
time,
|
||||||
|
delay,
|
||||||
|
}: {
|
||||||
|
name: string;
|
||||||
|
status: 'pass' | 'fail' | 'running';
|
||||||
|
time?: string;
|
||||||
|
delay: number;
|
||||||
|
}) => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
const progress = spring({
|
||||||
|
frame: frame - delay,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.snappy,
|
||||||
|
});
|
||||||
|
|
||||||
|
const statusColors = {
|
||||||
|
pass: colors.status.success,
|
||||||
|
fail: colors.status.error,
|
||||||
|
running: colors.status.warning,
|
||||||
|
};
|
||||||
|
|
||||||
|
const statusIcons = {
|
||||||
|
pass: '✓',
|
||||||
|
fail: '✕',
|
||||||
|
running: '○',
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 16,
|
||||||
|
padding: '12px 16px',
|
||||||
|
backgroundColor: colors.bg.surface,
|
||||||
|
borderLeft: `3px solid ${statusColors[status]}`,
|
||||||
|
marginBottom: 8,
|
||||||
|
opacity: progress,
|
||||||
|
transform: `translateX(${interpolate(progress, [0, 1], [-20, 0])}px)`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
fontSize: typography.fontSize.lg,
|
||||||
|
color: statusColors[status],
|
||||||
|
width: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{statusIcons[status]}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.text.primary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</span>
|
||||||
|
{time && (
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.xs,
|
||||||
|
color: colors.text.muted,
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{time}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TestScenariosScene = () => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
const headerProgress = spring({
|
||||||
|
frame,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.smooth,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Animated progress bar
|
||||||
|
const passRate = Math.min(
|
||||||
|
94,
|
||||||
|
interpolate(frame, [fps * 2, fps * 4], [0, 94], {
|
||||||
|
extrapolateLeft: 'clamp',
|
||||||
|
extrapolateRight: 'clamp',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const tests = [
|
||||||
|
{ name: 'search_basic_query', status: 'pass' as const, time: '124ms' },
|
||||||
|
{ name: 'search_with_filters', status: 'pass' as const, time: '89ms' },
|
||||||
|
{ name: 'handle_empty_results', status: 'pass' as const, time: '45ms' },
|
||||||
|
{ name: 'validate_api_key', status: 'fail' as const, time: '2.1s' },
|
||||||
|
{ name: 'rate_limit_handling', status: 'pass' as const, time: '312ms' },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AbsoluteFill
|
||||||
|
style={{
|
||||||
|
backgroundColor: colors.bg.base,
|
||||||
|
fontFamily: typography.fontFamily.sans,
|
||||||
|
padding: 80,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Feature badge */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 60,
|
||||||
|
left: 80,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 12,
|
||||||
|
opacity: headerProgress,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
backgroundColor: colors.copper.default,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
fontSize: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
✓
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.copper.default,
|
||||||
|
letterSpacing: typography.letterSpacing.widest,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Feature 07
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize['2xl'],
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
color: colors.text.primary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Test Scenarios
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginLeft: 16,
|
||||||
|
padding: '4px 12px',
|
||||||
|
backgroundColor: colors.bg.surface2,
|
||||||
|
border: `1px dashed ${colors.border.default}`,
|
||||||
|
fontSize: typography.fontSize.xs,
|
||||||
|
color: colors.text.secondary,
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
automated
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main content */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: 80,
|
||||||
|
marginTop: 160,
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Left - Pass rate */}
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.text.muted,
|
||||||
|
marginBottom: 8,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: typography.letterSpacing.widest,
|
||||||
|
opacity: headerProgress,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Pass Rate
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize['7xl'],
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
color: colors.status.success,
|
||||||
|
marginBottom: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Math.floor(passRate)}%
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Progress bar */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: 400,
|
||||||
|
height: 12,
|
||||||
|
backgroundColor: colors.bg.surface2,
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: `${passRate}%`,
|
||||||
|
height: '100%',
|
||||||
|
backgroundColor: colors.status.success,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: 32,
|
||||||
|
fontSize: typography.fontSize.lg,
|
||||||
|
color: colors.text.secondary,
|
||||||
|
lineHeight: typography.lineHeight.relaxed,
|
||||||
|
opacity: interpolate(frame, [fps * 2, fps * 3], [0, 1], {
|
||||||
|
extrapolateLeft: 'clamp',
|
||||||
|
extrapolateRight: 'clamp',
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
AI-generated tests validate
|
||||||
|
<br />
|
||||||
|
tool behavior automatically
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right - Test results */}
|
||||||
|
<div style={{ flex: 1, maxWidth: 500 }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.text.muted,
|
||||||
|
marginBottom: 16,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: typography.letterSpacing.widest,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Recent Tests
|
||||||
|
</div>
|
||||||
|
{tests.map((test, i) => (
|
||||||
|
<TestResult key={test.name} {...test} delay={fps * 1 + i * 8} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AbsoluteFill>
|
||||||
|
);
|
||||||
|
};
|
||||||
286
packages/video/src/scenes/features/ToolRegistryScene.tsx
Normal file
286
packages/video/src/scenes/features/ToolRegistryScene.tsx
Normal file
|
|
@ -0,0 +1,286 @@
|
||||||
|
import {
|
||||||
|
AbsoluteFill,
|
||||||
|
interpolate,
|
||||||
|
spring,
|
||||||
|
useCurrentFrame,
|
||||||
|
useVideoConfig,
|
||||||
|
} from 'remotion';
|
||||||
|
import { colors, typography, springConfigs } from '../../design-tokens';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feature 1: Tool Registry (0:06 - 0:15)
|
||||||
|
* Browse 1M+ AI tools from npm
|
||||||
|
*/
|
||||||
|
|
||||||
|
const ToolCard = ({
|
||||||
|
name,
|
||||||
|
downloads,
|
||||||
|
score,
|
||||||
|
delay,
|
||||||
|
}: {
|
||||||
|
name: string;
|
||||||
|
downloads: string;
|
||||||
|
score: number;
|
||||||
|
delay: number;
|
||||||
|
}) => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
const progress = spring({
|
||||||
|
frame: frame - delay,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.snappy,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: '16px 20px',
|
||||||
|
backgroundColor: colors.bg.surface2,
|
||||||
|
border: `1px dashed ${colors.border.default}`,
|
||||||
|
marginBottom: 12,
|
||||||
|
opacity: progress,
|
||||||
|
transform: `translateX(${interpolate(progress, [0, 1], [-30, 0])}px)`,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
fontSize: typography.fontSize.base,
|
||||||
|
color: colors.text.primary,
|
||||||
|
marginBottom: 4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.text.muted,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{downloads} downloads/mo
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 60,
|
||||||
|
height: 6,
|
||||||
|
backgroundColor: colors.bg.surface,
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: `${score}%`,
|
||||||
|
height: '100%',
|
||||||
|
backgroundColor: colors.copper.default,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.copper.default,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{score}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ToolRegistryScene = () => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
const headerProgress = spring({
|
||||||
|
frame,
|
||||||
|
fps,
|
||||||
|
config: springConfigs.smooth,
|
||||||
|
});
|
||||||
|
|
||||||
|
const counterValue = Math.floor(
|
||||||
|
interpolate(frame, [fps * 0.5, fps * 3], [0, 1847293], {
|
||||||
|
extrapolateLeft: 'clamp',
|
||||||
|
extrapolateRight: 'clamp',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const tools = [
|
||||||
|
{ name: '@anthropic/claude-sdk', downloads: '2.1M', score: 98 },
|
||||||
|
{ name: 'firecrawl-aisdk', downloads: '847K', score: 95 },
|
||||||
|
{ name: 'openai-tool-kit', downloads: '1.2M', score: 92 },
|
||||||
|
{ name: 'langchain-tools', downloads: '654K', score: 89 },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AbsoluteFill
|
||||||
|
style={{
|
||||||
|
backgroundColor: colors.bg.base,
|
||||||
|
fontFamily: typography.fontFamily.sans,
|
||||||
|
padding: 80,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Feature badge */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 60,
|
||||||
|
left: 80,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 12,
|
||||||
|
opacity: headerProgress,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
backgroundColor: colors.copper.default,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
fontSize: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
🔍
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.copper.default,
|
||||||
|
letterSpacing: typography.letterSpacing.widest,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Feature 01
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize['2xl'],
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
color: colors.text.primary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Tool Registry
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main content */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: 80,
|
||||||
|
marginTop: 160,
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Left - Stats */}
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize['7xl'],
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
color: colors.copper.default,
|
||||||
|
marginBottom: 16,
|
||||||
|
opacity: headerProgress,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{counterValue.toLocaleString()}+
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.xl,
|
||||||
|
color: colors.text.secondary,
|
||||||
|
marginBottom: 32,
|
||||||
|
opacity: headerProgress,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
AI tools auto-discovered from npm
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Badges */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: 12,
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
opacity: interpolate(frame, [fps * 2, fps * 3], [0, 1], {
|
||||||
|
extrapolateLeft: 'clamp',
|
||||||
|
extrapolateRight: 'clamp',
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{['auto-sync', 'quality scores', 'health monitoring'].map((badge) => (
|
||||||
|
<div
|
||||||
|
key={badge}
|
||||||
|
style={{
|
||||||
|
padding: '8px 16px',
|
||||||
|
border: `1px dashed ${colors.border.default}`,
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.text.secondary,
|
||||||
|
textTransform: 'lowercase',
|
||||||
|
fontFamily: typography.fontFamily.mono,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{badge}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right - Tool list */}
|
||||||
|
<div style={{ flex: 1, maxWidth: 500 }}>
|
||||||
|
{tools.map((tool, i) => (
|
||||||
|
<ToolCard
|
||||||
|
key={tool.name}
|
||||||
|
{...tool}
|
||||||
|
delay={fps * 1 + i * 10}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Bottom tagline */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 80,
|
||||||
|
left: 80,
|
||||||
|
right: 80,
|
||||||
|
opacity: interpolate(frame, [fps * 5, fps * 6], [0, 1], {
|
||||||
|
extrapolateLeft: 'clamp',
|
||||||
|
extrapolateRight: 'clamp',
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: typography.fontSize.lg,
|
||||||
|
color: colors.text.muted,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Updated within minutes of npm publication
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</AbsoluteFill>
|
||||||
|
);
|
||||||
|
};
|
||||||
Loading…
Add table
Add a link
Reference in a new issue