tcut × Remotion — 使用真实终端画面制作发布视频
tcut 生成像素级的终端片段;Remotion 使用 React 的弹簧动画、排版和布局来编排它们。两者结合:一部完整的动态设计发布视频,其中的终端画面是真实的、可复现的、可重新渲染的。
首先安装两个技能:npx skills add AmanVarshney01/tcut remotion-dev/skills。
管线
-
使用 tcut 录制画面(参见
tcut技能)。对于用于 Remotion 的片段:fps: 30以匹配 Remotion 合成;margin: 0, borderRadius: 0,让 React 绘制卡片(圆角、阴影)。保留windowBar: "colorful"—— 交通灯信号看起来像“真正的终端”- 对于 1920×1080 的合成,使用
--font-size 30录制(80 列宽约 1480 像素),确保片段从不放大 maxPause: "1s"保持片段紧凑;仅在观众需要看到按键时才添加keys: true- 输出 MP4 (H.264) —— Remotion 的
<OffthreadVideo>能帧精确读取 - 在每个步骤前添加
t.chapter("name"):章节信息嵌入 MP4 中,使用ffprobe -show_chapters -print_format json clip.mp4可获得精确时间戳,可用于同步覆盖层(例如高亮正在执行的脚本行——最强的“代码到视频”镜头) - 用多种主题重渲染一次录制:
tcut render hero.cast --theme nord -o themes/nord.mp4,然后按帧切换<OffthreadVideo src>:相同的时间线,主题实时切换
-
搭建 Remotion 项目:
bun create video(选择空白模板),或在一个包含registerRoot的项目中添加remotion、@remotion/cli、react、react-dom。 -
合成:将 tcut 输出的 MP4 文件复制到
public/目录,然后通过以下方式编排:
import { AbsoluteFill, OffthreadVideo, staticFile, spring, useCurrentFrame, useVideoConfig } from "remotion";
const Clip: React.FC<{ src: string; caption: string }> = ({ src, caption }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const enter = spring({ frame, fps, config: { damping: 200 } });
return (
<AbsoluteFill style={{ background: "#11111b", justifyContent: "center", alignItems: "center" }}>
<div style={{ transform: `scale(${0.92 + 0.08 * enter})`, borderRadius: 16, overflow: "hidden", boxShadow: "0 30px 80px rgba(0,0,0,.6)" }}>
<OffthreadVideo src={staticFile(src)} style={{ width: 1060 }} muted />
</div>
<h2 style={{ color: "#cdd6f4", fontFamily: "ui-monospace, monospace", fontSize: 38, marginTop: 26, opacity: enter }}>{caption}</h2>
</AbsoluteFill>
);
};
使用 <Sequence from={...} durationInFrames={...}> 编排场景;通过 ffprobe -show_entries format=duration 获取每个片段的实际时长来匹配场景长度。使用 startFrom / endAt(帧为单位)进行裁剪。
对于已经包含留白 margin 的片段,可以通过在 overflow: hidden 的卡片内将视频缩放至 videoW / (videoW - 2 * margin) 来裁剪。
-
渲染:执行
bunx remotion render <CompositionId> out/promo.mp4 --crf 15 --image-format png获得高质量母版(PNG 中间帧,接近无损 H.264)。确保合成 fps 与 tcut 片段匹配以避免重采样;先使用bunx remotion still <Id> still.png --frame N为每个场景检查单帧布局。 -
同时发布为网页版(可选):相同的合成可以通过
@remotion/player在浏览器中实时运行 ——<Player component={Promo} durationInFrames fps compositionWidth={1920} compositionHeight={1080} controls autoPlay loop initiallyMuted acknowledgeRemotionLicense style={{ width: "100%" }} />。Player 中<OffthreadVideo>会自动回退到<Video>。将片段 URL 统一通过一个辅助函数管理(staticFile(ASSET_BASE + path)),使项目和网站可以指向不同的文件夹;将片段复制到网站的public/目录,并加载相同的字体(例如@fontsource-variable/jetbrains-mono)。标题变成真实的 DOM 文本,整个视频可拖动浏览;tcut.amanv.dev/promo 是一个示例。
让效果看起来专业的设计要点
- 终端片段文字清晰:永远不要放大到 100% 以上;改为在带框的卡片内稍微缩小(约 85-95%)
- 使用终端主题调色板中的纯色平整背景(例如
#0b0b12,前景#e8e8f2);让窗口卡片通过阴影来体现深度。避免渐变、发光和点阵网格——它们会与清晰的终端文字冲突 - 标题卡片:逐字打出的等宽字体标题(按帧切分字符串)看起来有“终端原生”的感觉
- 结尾卡片:将安装命令放在类似复制粘贴的代码块中,比一个 Logo 更有效
- 一个场景一个想法,搭配大号等宽字体标题和执行该想法的单行 tcut API;在画面中出现某个元素的瞬间切换。功能展示视频可长约 70 秒;社交短视频应控制在 30 秒以内
为什么选择这种组合
终端画面是构建产物,而非屏幕录制:修改 tcut 脚本或主题,重新运行,然后使用 bunx remotion render 重新渲染推广视频——一条完全可复现的、端到端基于 TypeScript 的发布视频生产管线。