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",
|
||||
"scripts": {
|
||||
"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"
|
||||
},
|
||||
"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 { ExplainerVideo } from './ExplainerVideo';
|
||||
import { FeaturesVideo } from './FeaturesVideo';
|
||||
|
||||
export const RemotionRoot = () => {
|
||||
return (
|
||||
<Composition
|
||||
id="TPMJSExplainer"
|
||||
component={ExplainerVideo}
|
||||
durationInFrames={9000}
|
||||
fps={30}
|
||||
width={1920}
|
||||
height={1080}
|
||||
/>
|
||||
<>
|
||||
<Composition
|
||||
id="TPMJSExplainer"
|
||||
component={ExplainerVideo}
|
||||
durationInFrames={9000}
|
||||
fps={30}
|
||||
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