From e3187f1740b809826eea86c34e640b12358b3b65 Mon Sep 17 00:00:00 2001 From: SnapOtter Date: Fri, 8 May 2026 16:37:16 +0800 Subject: [PATCH] feat(videos): add Terminal component with typing + output animation --- apps/videos/src/components/Terminal.tsx | 94 +++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 apps/videos/src/components/Terminal.tsx diff --git a/apps/videos/src/components/Terminal.tsx b/apps/videos/src/components/Terminal.tsx new file mode 100644 index 00000000..95348002 --- /dev/null +++ b/apps/videos/src/components/Terminal.tsx @@ -0,0 +1,94 @@ +import type React from "react"; +import { interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion"; +import { AppWindow } from "@/components/AppWindow"; +import { TypeWriter } from "@/components/TypeWriter"; +import { SPRING } from "@/lib/motion"; + +interface OutputLine { + text: string; + color: string; + delay: number; +} + +export const Terminal: React.FC<{ + command: { text: string; color: string }[]; + commandStartFrame: number; + commandSpeed?: number; + outputLines?: OutputLine[]; + outputStartFrame: number; + outputStagger?: number; + width?: number; + height?: number; + style?: React.CSSProperties; + enterFrame?: number; +}> = ({ + command, + commandStartFrame, + commandSpeed = 2, + outputLines = [], + outputStartFrame, + outputStagger = 3, + width = 800, + height = 450, + style, + enterFrame = 0, +}) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const enterProgress = spring({ + frame: frame - enterFrame, + fps, + config: SPRING.snappy, + }); + + const translateY = interpolate(enterProgress, [0, 1], [height + 50, 0]); + const opacity = interpolate(enterProgress, [0, 1], [0, 1]); + + return ( +
+ +
+
+ $ + +
+ + {outputLines.map((line, i) => { + const lineFrame = outputStartFrame + i * outputStagger; + const lineOpacity = interpolate(frame, [lineFrame, lineFrame + 6], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + + return ( +
+ {line.text} +
+ ); + })} +
+
+
+ ); +};