import React from "react"; import { useCurrentFrame, useVideoConfig, AbsoluteFill, Img, staticFile, interpolate, } from "remotion"; import type { RemotionScene } from "../Root"; import type { RenderSegmentExtension } from "@pipeline/shared"; import { GITHUB_DAILY_PICK_PALETTE } from "../base/theme/colors"; import { SubtitleBar } from "../base/components/subtitle-bar"; import { Watermark } from "../base/components/watermark"; import { formatCount } from "@pipeline/shared"; import { ColorDot } from "../github-trending/index"; interface GithubDailyPickSceneProps { scene: RemotionScene; sceneIndex: number; totalScenes: number; totalFrames: number; title: string; channelName?: string; } /** Dark-stage tag chip — the deep-dive board keeps the dark look, so it can't * reuse github-trending's (now light) Tag. Dark glass chip, light accent. */ const Tag: React.FC<{ accent: string; children: React.ReactNode; size?: "default" | "large" }> = ({ accent, children, size = "default" }) => (
{children}
); /** * github-daily-pick content scene — one ACT chapter of the day's six-act * recommendation (钩子/痛点/亮相/亮点/上手/总结). Unlike the board templates (headline = repo fullName), here the scene * title is the act ("快速上手"), the repo identity moves to a * compact sub-line, and the body is the scene's keyframe cards plus the repo's * social preview. A top-right progress badge (03 / 07) reinforces the * deep-dive chapter structure. */ const GithubDailyPickScene: React.FC = ({ scene, sceneIndex, totalScenes, channelName, }) => { const frame = useCurrentFrame(); const { width, height, durationInFrames } = useVideoConfig(); const isPortrait = height > width; const palette = GITHUB_DAILY_PICK_PALETTE[sceneIndex % GITHUB_DAILY_PICK_PALETTE.length]; const github: RenderSegmentExtension | undefined = scene.extension?.type === "github-daily-pick" ? scene.extension : undefined; const sceneDur = durationInFrames; const fadeOpacity = interpolate( frame, [0, 8, sceneDur - 8, sceneDur], [0, 1, 1, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" } ); const socialPreview = github?.images?.socialPreview; const starHistory = github?.images?.starHistory; const pad = isPortrait ? 48 : 80; const footerReserved = isPortrait ? 500 : 140; const repo = github?.repo; const language = repo?.language || ""; const languageColor = repo?.languageColor || palette.accent; const starsLabel = repo?.stars != null ? `${formatCount(repo.stars)} stars` : ""; const license = repo?.license || ""; const fullName = repo?.fullName || ""; if (!github) { return (
{scene.title || scene.captionOrigin}
{(scene.caption?.length ?? 0) > 0 && }
); } // The scene's cards, ordered as produced. Card kinds are free-form strings // (功能简介 / 快速上手 / 对比 / 安装 ...); shown as titled sections. const cards = (scene.cardList ?? []).filter((c) => (c.desc ?? "").trim()); const badge = `${String(sceneIndex + 1).padStart(2, "0")} / ${String(totalScenes).padStart(2, "0")}`; const renderHeader = (fontSize: number, tagSize: "default" | "large", dotSize: number) => (
{/* Aspect title — the chapter headline (from displayText) */}
{scene.title || fullName}
{/* Repo identity — compact sub-line under the act title */}
{fullName} {language && ( {language} )} {starsLabel && {starsLabel}} {license && {license}}
); const renderBadge = (scale: number) => (
{String(sceneIndex + 1).padStart(2, "0")} / {String(totalScenes).padStart(2, "0")}
); const cardsRow = (size: "default" | "large") => (
{cards.slice(0, size === "large" ? 2 : 3).map((card, i) => ( ))}
); return ( {/* Subtle grid overlay (matches other templates' aesthetic) */}
{/* Top accent line */}
{isPortrait ? (
{renderHeader(84, "large", 16)}
{renderBadge(0.9)}
{socialPreview && ( )} {cardsRow("large")}
) : ( <>
{renderHeader(64, "default", 14)}
{renderBadge(1)}
{socialPreview && (
{starHistory && ( )}
)} {cardsRow("default")}
)} {(scene.caption?.length ?? 0) > 0 && ( )} ); }; const ImageFrame: React.FC<{ children: React.ReactNode }> = ({ children }) => (
{children}
); const AspectSection: React.FC<{ title: string; accent: string; body: string; size?: "default" | "large"; }> = ({ title, accent, body, size = "default" }) => { const isLarge = size === "large"; const cleanBody = (body ?? "").replace(/[。,、,.;;::\s]+$/, ""); return (
{title}
{cleanBody}
); }; export default GithubDailyPickScene;