remotion-claude-agent-demo 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +160 -0
- package/apps/web/README.md +36 -0
- package/apps/web/env.example +20 -0
- package/apps/web/eslint.config.mjs +18 -0
- package/apps/web/next.config.ts +7 -0
- package/apps/web/package-lock.json +10348 -0
- package/apps/web/package.json +35 -0
- package/apps/web/postcss.config.mjs +7 -0
- package/apps/web/public/file.svg +1 -0
- package/apps/web/public/globe.svg +1 -0
- package/apps/web/public/next.svg +1 -0
- package/apps/web/public/vercel.svg +1 -0
- package/apps/web/public/window.svg +1 -0
- package/apps/web/src/app/.well-known/agent-card.json/route.ts +50 -0
- package/apps/web/src/app/background-tasks/[jobId]/cancel/route.ts +29 -0
- package/apps/web/src/app/events/stream/route.ts +58 -0
- package/apps/web/src/app/favicon.ico +0 -0
- package/apps/web/src/app/globals.css +174 -0
- package/apps/web/src/app/layout.tsx +34 -0
- package/apps/web/src/app/messages/answer/route.ts +57 -0
- package/apps/web/src/app/messages/stream/route.ts +381 -0
- package/apps/web/src/app/page.tsx +358 -0
- package/apps/web/src/app/tasks/[taskId]/cancel/route.ts +24 -0
- package/apps/web/src/app/tasks/[taskId]/route.ts +24 -0
- package/apps/web/src/app/tasks/route.ts +13 -0
- package/apps/web/src/components/chat/agent-blocks.tsx +111 -0
- package/apps/web/src/components/chat/ask-user-question-panel.tsx +172 -0
- package/apps/web/src/components/chat/session-sidebar.tsx +222 -0
- package/apps/web/src/components/chat/subagent-activity-sidebar.tsx +248 -0
- package/apps/web/src/components/chat/tool-blocks.tsx +550 -0
- package/apps/web/src/lib/a2a/activity-store.ts +150 -0
- package/apps/web/src/lib/a2a/client.ts +357 -0
- package/apps/web/src/lib/a2a/sse.ts +19 -0
- package/apps/web/src/lib/a2a/task-store.ts +111 -0
- package/apps/web/src/lib/a2a/types.ts +216 -0
- package/apps/web/src/lib/agent/answer-store.ts +109 -0
- package/apps/web/src/lib/agent/background-delivery.ts +343 -0
- package/apps/web/src/lib/agent/background-tool.ts +78 -0
- package/apps/web/src/lib/agent/background.ts +452 -0
- package/apps/web/src/lib/agent/chat.ts +543 -0
- package/apps/web/src/lib/agent/session-store.ts +26 -0
- package/apps/web/src/lib/chat/types.ts +44 -0
- package/apps/web/src/lib/env.ts +31 -0
- package/apps/web/src/lib/hooks/useA2AChat.ts +863 -0
- package/apps/web/src/lib/state/chat-atoms.ts +52 -0
- package/apps/web/src/lib/workspace.ts +9 -0
- package/apps/web/tsconfig.json +35 -0
- package/bin/remotion-agent.js +451 -0
- package/package.json +34 -0
- package/templates/.claude/CLAUDE.md +95 -0
- package/templates/.claude/README.md +129 -0
- package/templates/.claude/agents/composer-agent.md +188 -0
- package/templates/.claude/agents/crafter.md +181 -0
- package/templates/.claude/agents/creator.md +134 -0
- package/templates/.claude/agents/perceiver.md +92 -0
- package/templates/.claude/settings.json +36 -0
- package/templates/.claude/settings.local.json +39 -0
- package/templates/.claude/skills/agent-browser/SKILL.md +349 -0
- package/templates/.claude/skills/agent-browser/references/authentication.md +188 -0
- package/templates/.claude/skills/agent-browser/references/proxy-support.md +175 -0
- package/templates/.claude/skills/agent-browser/references/session-management.md +181 -0
- package/templates/.claude/skills/agent-browser/references/snapshot-refs.md +186 -0
- package/templates/.claude/skills/agent-browser/references/video-recording.md +162 -0
- package/templates/.claude/skills/agent-browser/templates/authenticated-session.sh +91 -0
- package/templates/.claude/skills/agent-browser/templates/capture-workflow.sh +68 -0
- package/templates/.claude/skills/agent-browser/templates/form-automation.sh +64 -0
- package/templates/.claude/skills/algorithmic-art/LICENSE.txt +202 -0
- package/templates/.claude/skills/algorithmic-art/SKILL.md +405 -0
- package/templates/.claude/skills/algorithmic-art/templates/generator_template.js +223 -0
- package/templates/.claude/skills/algorithmic-art/templates/viewer.html +599 -0
- package/templates/.claude/skills/asset-validator/SKILL.md +376 -0
- package/templates/.claude/skills/audio-video-sync/SKILL.md +219 -0
- package/templates/.claude/skills/bgm-manager/SKILL.md +334 -0
- package/templates/.claude/skills/remotion-best-practices/SKILL.md +45 -0
- package/templates/.claude/skills/remotion-best-practices/rules/3d.md +86 -0
- package/templates/.claude/skills/remotion-best-practices/rules/animations.md +29 -0
- package/templates/.claude/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +173 -0
- package/templates/.claude/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
- package/templates/.claude/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +108 -0
- package/templates/.claude/skills/remotion-best-practices/rules/assets.md +78 -0
- package/templates/.claude/skills/remotion-best-practices/rules/audio.md +172 -0
- package/templates/.claude/skills/remotion-best-practices/rules/calculate-metadata.md +104 -0
- package/templates/.claude/skills/remotion-best-practices/rules/can-decode.md +75 -0
- package/templates/.claude/skills/remotion-best-practices/rules/charts.md +58 -0
- package/templates/.claude/skills/remotion-best-practices/rules/compositions.md +141 -0
- package/templates/.claude/skills/remotion-best-practices/rules/display-captions.md +126 -0
- package/templates/.claude/skills/remotion-best-practices/rules/extract-frames.md +229 -0
- package/templates/.claude/skills/remotion-best-practices/rules/fonts.md +152 -0
- package/templates/.claude/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
- package/templates/.claude/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
- package/templates/.claude/skills/remotion-best-practices/rules/get-video-duration.md +58 -0
- package/templates/.claude/skills/remotion-best-practices/rules/gifs.md +138 -0
- package/templates/.claude/skills/remotion-best-practices/rules/images.md +130 -0
- package/templates/.claude/skills/remotion-best-practices/rules/import-srt-captions.md +67 -0
- package/templates/.claude/skills/remotion-best-practices/rules/lottie.md +68 -0
- package/templates/.claude/skills/remotion-best-practices/rules/maps.md +403 -0
- package/templates/.claude/skills/remotion-best-practices/rules/measuring-dom-nodes.md +35 -0
- package/templates/.claude/skills/remotion-best-practices/rules/measuring-text.md +143 -0
- package/templates/.claude/skills/remotion-best-practices/rules/parameters.md +98 -0
- package/templates/.claude/skills/remotion-best-practices/rules/sequencing.md +118 -0
- package/templates/.claude/skills/remotion-best-practices/rules/tailwind.md +11 -0
- package/templates/.claude/skills/remotion-best-practices/rules/text-animations.md +20 -0
- package/templates/.claude/skills/remotion-best-practices/rules/timing.md +179 -0
- package/templates/.claude/skills/remotion-best-practices/rules/transcribe-captions.md +19 -0
- package/templates/.claude/skills/remotion-best-practices/rules/transitions.md +122 -0
- package/templates/.claude/skills/remotion-best-practices/rules/trimming.md +53 -0
- package/templates/.claude/skills/remotion-best-practices/rules/videos.md +171 -0
- package/templates/.claude/skills/remotion-components/SKILL.md +453 -0
- package/templates/.claude/skills/render-config/SKILL.md +290 -0
- package/templates/.claude/skills/script-writer/SKILL.md +59 -0
- package/templates/.claude/skills/style-director/script-writer/SKILL.md +82 -0
- package/templates/.claude/skills/style-director/style-director/SKILL.md +287 -0
- package/templates/.claude/skills/style-director/style-director/references/audience-and-scenarios.md +43 -0
- package/templates/.claude/skills/style-director/style-director/references/interaction-innovation.md +26 -0
- package/templates/.claude/skills/style-director/style-director/references/motion-grammar.md +66 -0
- package/templates/.claude/skills/style-director/style-director/references/quality-checklist.md +29 -0
- package/templates/.claude/skills/style-director/style-director/references/scene-recipes.md +38 -0
- package/templates/.claude/skills/style-director/style-director/references/visual-style-system.md +148 -0
- package/templates/.claude/skills/subtitle-composer/SKILL.md +304 -0
- package/templates/.claude/skills/subtitle-processor/SKILL.md +308 -0
- package/templates/.claude/skills/timeline-generator/SKILL.md +253 -0
- package/templates/.claude/skills/video-preflight-check/SKILL.md +353 -0
- package/templates/.claude/skills/voice-synthesizer/SKILL.md +296 -0
- package/templates/.claude/skills/voice-synthesizer/scripts/synthesize_voice.py +315 -0
- package/templates/.claude/skills/voice-synthesizer/scripts/tts_cli.py +142 -0
- package/templates/.claude/skills/web-design-guidelines/SKILL.md +36 -0
- package/templates/.claude/skills/youtube-downloader/SKILL.md +99 -0
- package/templates/.claude/skills/youtube-downloader/scripts/download_video.py +145 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: remotion-components
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: Remotion组件库。提供标准化的动画背景、代码编辑器、终端、卡片等可复用组件。
|
|
5
|
+
triggers:
|
|
6
|
+
- remotion组件
|
|
7
|
+
- 动画组件
|
|
8
|
+
- 视频组件
|
|
9
|
+
- components
|
|
10
|
+
tools:
|
|
11
|
+
- Read
|
|
12
|
+
- Write
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Remotion 组件库 (Remotion Components)
|
|
16
|
+
|
|
17
|
+
提供一套标准化、可复用的Remotion视频组件,确保视频风格统一、动画流畅。
|
|
18
|
+
|
|
19
|
+
## 组件清单
|
|
20
|
+
|
|
21
|
+
1. **AnimatedBackground** - 动画背景(粒子、渐变、网格)
|
|
22
|
+
2. **CodeEditor** - 代码编辑器(打字效果、语法高亮)
|
|
23
|
+
3. **Terminal** - 终端命令行
|
|
24
|
+
4. **FeatureCard** - 特性卡片
|
|
25
|
+
5. **FlowDiagram** - 流程图
|
|
26
|
+
6. **Subtitles** - 字幕组件
|
|
27
|
+
7. **SceneTransition** - 场景过渡
|
|
28
|
+
|
|
29
|
+
## 1. AnimatedBackground 动画背景
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { AbsoluteFill, interpolate, useCurrentFrame } from "remotion";
|
|
33
|
+
|
|
34
|
+
interface AnimatedBackgroundProps {
|
|
35
|
+
variant?: "default" | "code" | "energetic";
|
|
36
|
+
particleCount?: number;
|
|
37
|
+
primaryColor?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const AnimatedBackground: React.FC<AnimatedBackgroundProps> = ({
|
|
41
|
+
variant = "default",
|
|
42
|
+
particleCount = 20,
|
|
43
|
+
primaryColor = "#D97706",
|
|
44
|
+
}) => {
|
|
45
|
+
const frame = useCurrentFrame();
|
|
46
|
+
|
|
47
|
+
// 渐变背景
|
|
48
|
+
const gradients = {
|
|
49
|
+
default: "linear-gradient(135deg, #0F172A 0%, #1E293B 100%)",
|
|
50
|
+
code: "linear-gradient(135deg, #0F172A 0%, #1a1a2e 50%, #16213e 100%)",
|
|
51
|
+
energetic: "linear-gradient(135deg, #0F172A 0%, #1E293B 50%, #0F172A 100%)",
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// 动态光晕
|
|
55
|
+
const glowX = 50 + Math.sin(frame * 0.02) * 20;
|
|
56
|
+
const glowY = 50 + Math.cos(frame * 0.015) * 20;
|
|
57
|
+
|
|
58
|
+
// 粒子生成
|
|
59
|
+
const particles = Array.from({ length: particleCount }, (_, i) => {
|
|
60
|
+
const seed = (42 * (i + 1) * 9301 + 49297) % 233280 / 233280;
|
|
61
|
+
return {
|
|
62
|
+
x: seed * 100,
|
|
63
|
+
y: (seed * (i + 1) * 7) % 100,
|
|
64
|
+
size: 2 + seed * 4,
|
|
65
|
+
speed: 0.5 + seed * 1.5,
|
|
66
|
+
delay: i * 10,
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<AbsoluteFill style={{ background: gradients[variant], overflow: "hidden" }}>
|
|
72
|
+
{/* 动态光晕 */}
|
|
73
|
+
<div
|
|
74
|
+
style={{
|
|
75
|
+
position: "absolute",
|
|
76
|
+
inset: 0,
|
|
77
|
+
background: `radial-gradient(circle at ${glowX}% ${glowY}%, ${primaryColor}20 0%, transparent 50%)`,
|
|
78
|
+
}}
|
|
79
|
+
/>
|
|
80
|
+
|
|
81
|
+
{/* 浮动粒子 */}
|
|
82
|
+
{particles.map((p, i) => {
|
|
83
|
+
const yOffset = ((frame + p.delay) * p.speed) % 120;
|
|
84
|
+
const opacity = interpolate(yOffset, [0, 20, 100, 120], [0, 0.6, 0.6, 0]);
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<div
|
|
88
|
+
key={i}
|
|
89
|
+
style={{
|
|
90
|
+
position: "absolute",
|
|
91
|
+
left: `${p.x}%`,
|
|
92
|
+
top: `${p.y - yOffset + 100}%`,
|
|
93
|
+
width: p.size,
|
|
94
|
+
height: p.size,
|
|
95
|
+
borderRadius: "50%",
|
|
96
|
+
backgroundColor: primaryColor,
|
|
97
|
+
opacity: opacity * 0.5,
|
|
98
|
+
boxShadow: `0 0 ${p.size * 2}px ${primaryColor}`,
|
|
99
|
+
}}
|
|
100
|
+
/>
|
|
101
|
+
);
|
|
102
|
+
})}
|
|
103
|
+
|
|
104
|
+
{/* 网格图案 */}
|
|
105
|
+
<div
|
|
106
|
+
style={{
|
|
107
|
+
position: "absolute",
|
|
108
|
+
inset: 0,
|
|
109
|
+
backgroundImage: `
|
|
110
|
+
linear-gradient(rgba(255,255,255,0.02) 1px, transparent 1px),
|
|
111
|
+
linear-gradient(90deg, rgba(255,255,255,0.02) 1px, transparent 1px)
|
|
112
|
+
`,
|
|
113
|
+
backgroundSize: "50px 50px",
|
|
114
|
+
opacity: 0.5,
|
|
115
|
+
}}
|
|
116
|
+
/>
|
|
117
|
+
</AbsoluteFill>
|
|
118
|
+
);
|
|
119
|
+
};
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## 2. CodeEditor 代码编辑器
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
import { AbsoluteFill, interpolate, useCurrentFrame, spring, useVideoConfig } from "remotion";
|
|
126
|
+
|
|
127
|
+
interface CodeLine {
|
|
128
|
+
text: string;
|
|
129
|
+
color?: string;
|
|
130
|
+
delay: number; // 开始显示的帧数
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
interface CodeEditorProps {
|
|
134
|
+
title?: string;
|
|
135
|
+
lines: CodeLine[];
|
|
136
|
+
showLineNumbers?: boolean;
|
|
137
|
+
typeEffect?: boolean;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export const CodeEditor: React.FC<CodeEditorProps> = ({
|
|
141
|
+
title = "code.py",
|
|
142
|
+
lines,
|
|
143
|
+
showLineNumbers = true,
|
|
144
|
+
typeEffect = true,
|
|
145
|
+
}) => {
|
|
146
|
+
const frame = useCurrentFrame();
|
|
147
|
+
const { fps } = useVideoConfig();
|
|
148
|
+
|
|
149
|
+
// 编辑器弹入动画
|
|
150
|
+
const editorSpring = spring({ frame, fps, config: { damping: 15, stiffness: 80 } });
|
|
151
|
+
|
|
152
|
+
// 光标闪烁
|
|
153
|
+
const showCursor = frame % 20 < 10;
|
|
154
|
+
|
|
155
|
+
// 当前正在打字的行
|
|
156
|
+
const currentLineIndex = lines.findIndex(
|
|
157
|
+
(line, i) => frame >= line.delay && (i === lines.length - 1 || frame < lines[i + 1].delay + 15)
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<div
|
|
162
|
+
style={{
|
|
163
|
+
opacity: editorSpring,
|
|
164
|
+
transform: `translateY(${interpolate(editorSpring, [0, 1], [30, 0])}px)`,
|
|
165
|
+
backgroundColor: "rgba(30, 41, 59, 0.95)",
|
|
166
|
+
borderRadius: 16,
|
|
167
|
+
overflow: "hidden",
|
|
168
|
+
boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.5)",
|
|
169
|
+
border: "1px solid rgba(71, 85, 105, 0.5)",
|
|
170
|
+
}}
|
|
171
|
+
>
|
|
172
|
+
{/* 标题栏 */}
|
|
173
|
+
<div
|
|
174
|
+
style={{
|
|
175
|
+
backgroundColor: "#0F172A",
|
|
176
|
+
padding: "12px 20px",
|
|
177
|
+
display: "flex",
|
|
178
|
+
alignItems: "center",
|
|
179
|
+
gap: 8,
|
|
180
|
+
}}
|
|
181
|
+
>
|
|
182
|
+
<div style={{ width: 12, height: 12, borderRadius: "50%", backgroundColor: "#EF4444" }} />
|
|
183
|
+
<div style={{ width: 12, height: 12, borderRadius: "50%", backgroundColor: "#F59E0B" }} />
|
|
184
|
+
<div style={{ width: 12, height: 12, borderRadius: "50%", backgroundColor: "#22C55E" }} />
|
|
185
|
+
<span style={{ color: "#64748B", marginLeft: 20, fontSize: 14 }}>{title}</span>
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
{/* 代码内容 */}
|
|
189
|
+
<div style={{ padding: 24, position: "relative" }}>
|
|
190
|
+
{/* 行号 */}
|
|
191
|
+
{showLineNumbers && (
|
|
192
|
+
<div
|
|
193
|
+
style={{
|
|
194
|
+
position: "absolute",
|
|
195
|
+
left: 24,
|
|
196
|
+
color: "#475569",
|
|
197
|
+
fontFamily: "JetBrains Mono, monospace",
|
|
198
|
+
fontSize: 18,
|
|
199
|
+
lineHeight: 1.8,
|
|
200
|
+
}}
|
|
201
|
+
>
|
|
202
|
+
{lines.map((_, i) => <div key={i}>{i + 1}</div>)}
|
|
203
|
+
</div>
|
|
204
|
+
)}
|
|
205
|
+
|
|
206
|
+
{/* 代码 */}
|
|
207
|
+
<pre
|
|
208
|
+
style={{
|
|
209
|
+
fontFamily: "JetBrains Mono, monospace",
|
|
210
|
+
fontSize: 18,
|
|
211
|
+
lineHeight: 1.8,
|
|
212
|
+
margin: 0,
|
|
213
|
+
paddingLeft: showLineNumbers ? 50 : 0,
|
|
214
|
+
}}
|
|
215
|
+
>
|
|
216
|
+
{lines.map((line, i) => {
|
|
217
|
+
const lineOpacity = interpolate(
|
|
218
|
+
frame,
|
|
219
|
+
[line.delay, line.delay + 12],
|
|
220
|
+
[0, 1],
|
|
221
|
+
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const isCurrentLine = i === currentLineIndex;
|
|
225
|
+
|
|
226
|
+
return (
|
|
227
|
+
<div
|
|
228
|
+
key={i}
|
|
229
|
+
style={{
|
|
230
|
+
opacity: lineOpacity,
|
|
231
|
+
color: line.color || "#F1F5F9",
|
|
232
|
+
backgroundColor: isCurrentLine ? "rgba(217, 119, 6, 0.1)" : "transparent",
|
|
233
|
+
marginLeft: -8,
|
|
234
|
+
paddingLeft: 8,
|
|
235
|
+
borderRadius: 4,
|
|
236
|
+
}}
|
|
237
|
+
>
|
|
238
|
+
{line.text || " "}
|
|
239
|
+
{isCurrentLine && showCursor && typeEffect && (
|
|
240
|
+
<span style={{ color: "#22D3EE" }}>|</span>
|
|
241
|
+
)}
|
|
242
|
+
</div>
|
|
243
|
+
);
|
|
244
|
+
})}
|
|
245
|
+
</pre>
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
);
|
|
249
|
+
};
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## 3. Terminal 终端组件
|
|
253
|
+
|
|
254
|
+
```tsx
|
|
255
|
+
interface TerminalLine {
|
|
256
|
+
text: string;
|
|
257
|
+
isCommand?: boolean;
|
|
258
|
+
color?: string;
|
|
259
|
+
delay: number;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
interface TerminalProps {
|
|
263
|
+
title?: string;
|
|
264
|
+
lines: TerminalLine[];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export const Terminal: React.FC<TerminalProps> = ({
|
|
268
|
+
title = "Terminal",
|
|
269
|
+
lines,
|
|
270
|
+
}) => {
|
|
271
|
+
const frame = useCurrentFrame();
|
|
272
|
+
const showCursor = frame % 20 < 10;
|
|
273
|
+
|
|
274
|
+
const currentLineIndex = lines.findIndex(
|
|
275
|
+
(line, i) => frame >= line.delay && (i === lines.length - 1 || frame < lines[i + 1].delay)
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
return (
|
|
279
|
+
<div
|
|
280
|
+
style={{
|
|
281
|
+
backgroundColor: "rgba(15, 23, 42, 0.95)",
|
|
282
|
+
borderRadius: 16,
|
|
283
|
+
overflow: "hidden",
|
|
284
|
+
boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.5)",
|
|
285
|
+
}}
|
|
286
|
+
>
|
|
287
|
+
{/* 标题栏 */}
|
|
288
|
+
<div
|
|
289
|
+
style={{
|
|
290
|
+
backgroundColor: "#1E293B",
|
|
291
|
+
padding: "12px 20px",
|
|
292
|
+
display: "flex",
|
|
293
|
+
alignItems: "center",
|
|
294
|
+
gap: 8,
|
|
295
|
+
}}
|
|
296
|
+
>
|
|
297
|
+
<div style={{ width: 12, height: 12, borderRadius: "50%", backgroundColor: "#EF4444" }} />
|
|
298
|
+
<div style={{ width: 12, height: 12, borderRadius: "50%", backgroundColor: "#F59E0B" }} />
|
|
299
|
+
<div style={{ width: 12, height: 12, borderRadius: "50%", backgroundColor: "#22C55E" }} />
|
|
300
|
+
<span style={{ color: "#64748B", marginLeft: 20, fontSize: 14 }}>{title}</span>
|
|
301
|
+
</div>
|
|
302
|
+
|
|
303
|
+
{/* 终端内容 */}
|
|
304
|
+
<div style={{ padding: 24 }}>
|
|
305
|
+
{lines.map((line, i) => {
|
|
306
|
+
const opacity = interpolate(
|
|
307
|
+
frame,
|
|
308
|
+
[line.delay, line.delay + 12],
|
|
309
|
+
[0, 1],
|
|
310
|
+
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
if (!line.text) return <div key={i} style={{ height: 12 }} />;
|
|
314
|
+
|
|
315
|
+
const isCurrentLine = i === currentLineIndex;
|
|
316
|
+
|
|
317
|
+
return (
|
|
318
|
+
<div
|
|
319
|
+
key={i}
|
|
320
|
+
style={{
|
|
321
|
+
opacity,
|
|
322
|
+
fontFamily: "JetBrains Mono, monospace",
|
|
323
|
+
fontSize: 20,
|
|
324
|
+
color: line.color || "#F1F5F9",
|
|
325
|
+
marginBottom: 8,
|
|
326
|
+
display: "flex",
|
|
327
|
+
alignItems: "center",
|
|
328
|
+
}}
|
|
329
|
+
>
|
|
330
|
+
{line.isCommand && (
|
|
331
|
+
<span style={{ color: "#22C55E", marginRight: 12, fontWeight: 700 }}>$</span>
|
|
332
|
+
)}
|
|
333
|
+
{line.text}
|
|
334
|
+
{isCurrentLine && showCursor && line.isCommand && (
|
|
335
|
+
<span style={{ color: "#22D3EE", marginLeft: 2 }}>|</span>
|
|
336
|
+
)}
|
|
337
|
+
</div>
|
|
338
|
+
);
|
|
339
|
+
})}
|
|
340
|
+
</div>
|
|
341
|
+
</div>
|
|
342
|
+
);
|
|
343
|
+
};
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
## 4. FeatureCard 特性卡片
|
|
347
|
+
|
|
348
|
+
```tsx
|
|
349
|
+
interface FeatureCardProps {
|
|
350
|
+
icon: string;
|
|
351
|
+
title: string;
|
|
352
|
+
description: string;
|
|
353
|
+
color: string;
|
|
354
|
+
delay: number;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export const FeatureCard: React.FC<FeatureCardProps> = ({
|
|
358
|
+
icon,
|
|
359
|
+
title,
|
|
360
|
+
description,
|
|
361
|
+
color,
|
|
362
|
+
delay,
|
|
363
|
+
}) => {
|
|
364
|
+
const frame = useCurrentFrame();
|
|
365
|
+
const { fps } = useVideoConfig();
|
|
366
|
+
|
|
367
|
+
const cardSpring = spring({
|
|
368
|
+
frame: frame - delay,
|
|
369
|
+
fps,
|
|
370
|
+
config: { damping: 12, stiffness: 100 },
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
const floatY = Math.sin(frame * 0.04) * 4;
|
|
374
|
+
|
|
375
|
+
return (
|
|
376
|
+
<div
|
|
377
|
+
style={{
|
|
378
|
+
opacity: cardSpring,
|
|
379
|
+
transform: `scale(${cardSpring}) translateY(${floatY}px)`,
|
|
380
|
+
backgroundColor: "rgba(30, 41, 59, 0.9)",
|
|
381
|
+
borderRadius: 16,
|
|
382
|
+
padding: 28,
|
|
383
|
+
border: `3px solid ${color}`,
|
|
384
|
+
boxShadow: `0 0 20px ${color}30`,
|
|
385
|
+
}}
|
|
386
|
+
>
|
|
387
|
+
<div style={{ display: "flex", alignItems: "center", gap: 15, marginBottom: 12 }}>
|
|
388
|
+
<span style={{ fontSize: 40 }}>{icon}</span>
|
|
389
|
+
<span style={{ fontSize: 28, fontWeight: 700, color }}>{title}</span>
|
|
390
|
+
</div>
|
|
391
|
+
<div style={{ fontSize: 18, color: "#94A3B8" }}>{description}</div>
|
|
392
|
+
</div>
|
|
393
|
+
);
|
|
394
|
+
};
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
## 5. 标准动画参数
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
// 推荐的 Spring 配置
|
|
401
|
+
export const SPRING_CONFIGS = {
|
|
402
|
+
// 快速弹入
|
|
403
|
+
snappy: { damping: 10, stiffness: 120, mass: 0.5 },
|
|
404
|
+
// 平滑过渡
|
|
405
|
+
smooth: { damping: 15, stiffness: 80, mass: 0.8 },
|
|
406
|
+
// 弹性效果
|
|
407
|
+
bouncy: { damping: 8, stiffness: 150, mass: 0.3 },
|
|
408
|
+
// 缓慢优雅
|
|
409
|
+
gentle: { damping: 20, stiffness: 60, mass: 1 },
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
// 标准色板
|
|
413
|
+
export const COLORS = {
|
|
414
|
+
primary: "#D97706", // 橙色 - 主色调
|
|
415
|
+
success: "#22C55E", // 绿色 - 成功
|
|
416
|
+
info: "#22D3EE", // 青色 - 信息
|
|
417
|
+
warning: "#F59E0B", // 黄色 - 警告
|
|
418
|
+
error: "#EF4444", // 红色 - 错误
|
|
419
|
+
purple: "#8B5CF6", // 紫色
|
|
420
|
+
pink: "#EC4899", // 粉色
|
|
421
|
+
blue: "#3B82F6", // 蓝色
|
|
422
|
+
|
|
423
|
+
text: "#F1F5F9", // 主文字
|
|
424
|
+
textMuted: "#94A3B8", // 次要文字
|
|
425
|
+
background: "#0F172A", // 背景
|
|
426
|
+
surface: "#1E293B", // 表面
|
|
427
|
+
border: "#334155", // 边框
|
|
428
|
+
};
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
## 使用示例
|
|
432
|
+
|
|
433
|
+
```tsx
|
|
434
|
+
import { AnimatedBackground, CodeEditor, Terminal, FeatureCard } from "./components";
|
|
435
|
+
import { SPRING_CONFIGS, COLORS } from "./constants";
|
|
436
|
+
|
|
437
|
+
export const MyScene: React.FC = () => {
|
|
438
|
+
return (
|
|
439
|
+
<AbsoluteFill>
|
|
440
|
+
<AnimatedBackground variant="code" primaryColor={COLORS.primary} />
|
|
441
|
+
|
|
442
|
+
<CodeEditor
|
|
443
|
+
title="agent.py"
|
|
444
|
+
lines={[
|
|
445
|
+
{ text: "from claude_agent_sdk import query", delay: 0, color: "#C792EA" },
|
|
446
|
+
{ text: "", delay: 10 },
|
|
447
|
+
{ text: "async for msg in query(...):", delay: 20, color: "#89DDFF" },
|
|
448
|
+
]}
|
|
449
|
+
/>
|
|
450
|
+
</AbsoluteFill>
|
|
451
|
+
);
|
|
452
|
+
};
|
|
453
|
+
```
|