import { Group, Meta, Transcript, TweetExtra } from "common-utils"; import { AbsoluteFill } from "remotion"; import { RenderUtils } from "../RenderUtils"; function formatTweetDate(date: Date) { const hours = date.getHours(); const minutes = date.getMinutes(); const ampm = hours >= 12 ? "PM" : "AM"; const hour12 = hours % 12 === 0 ? 12 : hours % 12; const minStr = minutes.toString().padStart(2, "0"); const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; const month = months[date.getMonth()]; const day = date.getDate(); const year = date.getFullYear(); return `${hour12}:${minStr} ${ampm} · ${month} ${day}, ${year}`; } export default function TweetPoster({ transcript, meta, }: { transcript: Transcript; meta: Meta; }) { const textParts = transcript.audioCaption?.words; if (!textParts) { return (

Empty tweet text!

); } const { width, height } = meta.resolution || { width: 1080, height: 1920 }; // Scale solely from WIDTH (as requested) const W = Math.max(320, width); // guard for tiny canvases // Outer padding around the card (kept modest so card fills width but not flush) const outerPad = Math.max(Math.round(W * 0.04), 16); // 4% of width, min 16px // Card internal padding const boxPad = Math.max(Math.round(W * 0.03), 14); // 3% of width, min 14px // Avatar + typography strictly from width const avatarSize = Math.max(Math.round(W * 0.08), 56); // 8% of width const fontNamePx = Math.round(W * 0.035); // display name const fontUserPx = Math.round(W * 0.028); // @username const fontMainPx = Math.round(W * 0.042); // tweet text const fontDatePx = Math.round(W * 0.026); // footer date // Reasonable clamps to avoid extremes on very large/small canvases const clamp = (v: number, min: number, max: number) => Math.min(max, Math.max(min, v)); const fontName = `${clamp(fontNamePx, 16, 40)}px`; const fontUsername = `${clamp(fontUserPx, 14, 32)}px`; const fontMain = `${clamp(fontMainPx, 18, 48)}px`; const fontDate = `${clamp(fontDatePx, 12, 28)}px`; let bgColor = (transcript.extras as TweetExtra)?.textBgColor || "#ffffff"; const fullName = textParts[0]?.word; const fullNameStyle = textParts[0]?.textStyle; const username = textParts[1]?.word; const usernameStyle = textParts[1]?.textStyle; const tweetLine1 = textParts[2]?.word; const tweetLine1Style = textParts[2]?.textStyle; const tweetLine2 = textParts[3]?.word; const tweetLine2Style = textParts[3]?.textStyle; const avatar = RenderUtils.tryStaticFile(transcript.mediaAbsPaths?.[0]?.path); return ( {/* Outer pad keeps some breathing room; box fills remaining width */}
{/* Tweet box now fully fits parent width with the outerPad margin */}
{/* Header */}
avatar
{fullName}
{username}
{/* Tweet text */}
{tweetLine1} {tweetLine2 && ( <> {"\n"} {tweetLine2} )}
{/* Footer */}
{formatTweetDate(new Date())} · Follow
); }