Skip to main content
Glama
template.tsx.j212.9 kB
{/* chuk-motion/src/chuk_motion/components/code/CodeBlock/template.tsx.j2 */} import React from 'react'; import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig } from 'remotion'; import { Highlight, themes } from 'prism-react-renderer'; interface CodeBlockProps { code: string; language?: string; title?: string; startFrame: number; durationInFrames: number; variant?: string; animation?: string; show_line_numbers?: boolean; highlight_lines?: number[]; } export const CodeBlock: React.FC<CodeBlockProps> = ({ code, language = 'javascript', title, startFrame, durationInFrames, variant = 'editor', animation = 'fade_in', show_line_numbers = true, highlight_lines = [] }) => { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); const relativeFrame = frame - startFrame; // Don't render if outside the time range // If durationInFrames is 0, this is a nested component and should always render if (durationInFrames > 0 && (frame < startFrame || frame >= startFrame + durationInFrames)) { return null; } // Animation logic let opacity = 1; let scale = 1; let translateY = 0; let blur = 0; if (animation === 'fade_in') { const progress = spring({ frame: relativeFrame, fps, config: { damping: [[ motion.default_spring.config.damping ]], mass: [[ motion.default_spring.config.mass ]], stiffness: [[ motion.default_spring.config.stiffness ]] } }); opacity = progress; } else if (animation === 'slide_up') { const progress = spring({ frame: relativeFrame, fps, config: { damping: [[ motion.default_spring.config.damping ]], mass: [[ motion.default_spring.config.mass ]], stiffness: [[ motion.default_spring.config.stiffness ]] } }); opacity = progress; translateY = interpolate(progress, [0, 1], [50, 0]); } else if (animation === 'scale_in') { const progress = spring({ frame: relativeFrame, fps, config: { damping: [[ motion.default_spring.config.damping ]], mass: [[ motion.default_spring.config.mass ]], stiffness: [[ motion.default_spring.config.stiffness ]] } }); opacity = progress; scale = interpolate(progress, [0, 1], [0.9, 1]); } else if (animation === 'blur_in') { const progress = spring({ frame: relativeFrame, fps, config: { damping: [[ motion.default_spring.config.damping ]], mass: [[ motion.default_spring.config.mass ]], stiffness: [[ motion.default_spring.config.stiffness ]] } }); opacity = progress; blur = interpolate(progress, [0, 1], [10, 0]); } else { // Default: fade in opacity = interpolate(relativeFrame, [0, 20], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }); } // Exit animation (skip for nested components with durationInFrames=0) let finalOpacity = opacity; if (durationInFrames > 0) { const exitDuration = 20; const exitProgress = interpolate( relativeFrame, [durationInFrames - exitDuration, durationInFrames], [1, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } ); finalOpacity = opacity * exitProgress; } // Variant styles const variants = { minimal: { background: '[[ colors.background.dark ]]', borderRadius: parseInt('[[ spacing.border_radius.lg ]]'), border: 'none', padding: parseInt('[[ spacing.spacing.lg ]]'), boxShadow: `0 10px 40px [[ colors.shadow.dark ]]` }, terminal: { background: '[[ colors.background.darker ]]', borderRadius: parseInt('[[ spacing.border_radius.md ]]'), border: '1px solid [[ colors.border.subtle ]]', padding: parseInt('[[ spacing.spacing.xl ]]'), boxShadow: '0 10px 40px [[ colors.background.dark ]]' }, editor: { background: '[[ colors.background.dark ]]', borderRadius: parseInt('[[ spacing.border_radius.lg ]]'), border: '1px solid [[ colors.border.subtle ]]', padding: 0, boxShadow: `0 20px 60px [[ colors.shadow.dark ]]` }, glass: { background: '[[ colors.background.glass ]]', backdropFilter: 'blur(20px)', borderRadius: parseInt('[[ spacing.spacing.md ]]'), border: '1px solid [[ colors.border.subtle ]]', padding: parseInt('[[ spacing.spacing.xl ]]'), boxShadow: `0 20px 60px [[ colors.shadow.dark ]]` } }; const variantStyle = variants[variant as keyof typeof variants] || variants.editor; return ( <div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', transform: `scale(${scale}) translateY(${translateY}px)`, opacity: finalOpacity, filter: `blur(${blur}px)`, fontFamily: "'Fira Code', 'Monaco', 'Consolas', 'monospace'", overflow: 'hidden' }} > <Highlight theme={themes.vsDark} code={code} language={language as any} > {({ className, style, tokens, getLineProps, getTokenProps }) => ( <div style={variantStyle}> {title && variant === 'editor' ? ( <> <div style={{ background: '[[ colors.background.overlay ]]', padding: `[[ spacing.spacing.sm ]] [[ spacing.spacing.lg ]]`, borderTopLeftRadius: parseInt('[[ spacing.border_radius.lg ]]'), borderTopRightRadius: parseInt('[[ spacing.border_radius.lg ]]'), borderBottom: '1px solid [[ colors.border.light ]]', display: 'flex', alignItems: 'center', gap: parseInt('[[ spacing.spacing.xs ]]') }} > <div style={{ display: 'flex', gap: parseInt('[[ spacing.spacing.xs ]]') }}> <div style={{ width: parseInt('[[ spacing.spacing.sm ]]'), height: parseInt('[[ spacing.spacing.sm ]]'), borderRadius: '50%', background: '[[ colors.semantic.error ]]' }} /> <div style={{ width: parseInt('[[ spacing.spacing.sm ]]'), height: parseInt('[[ spacing.spacing.sm ]]'), borderRadius: '50%', background: '[[ colors.semantic.warning ]]' }} /> <div style={{ width: parseInt('[[ spacing.spacing.sm ]]'), height: parseInt('[[ spacing.spacing.sm ]]'), borderRadius: '50%', background: '[[ colors.semantic.success ]]' }} /> </div> <div style={{ fontSize: parseInt('[[ typography.font_sizes[typography.default_resolution].xs ]]'), color: '[[ colors.text.muted ]]', marginLeft: parseInt('[[ spacing.spacing.sm ]]'), fontFamily: "'[[ "', '".join(typography.primary_font.fonts) ]]'" }} > {title} </div> </div> <div style={{ padding: parseInt('[[ spacing.spacing.xl ]]') }}> <div style={{ display: 'flex', gap: parseInt('[[ spacing.spacing.lg ]]') }}> {show_line_numbers && ( <div style={{ color: '[[ colors.text.dimmed ]]', fontSize: parseInt('[[ typography.font_sizes[typography.default_resolution].xs ]]'), lineHeight: parseFloat('[[ typography.line_heights.relaxed ]]'), textAlign: 'right', userSelect: 'none', minWidth: parseInt('[[ spacing.spacing["3xl"] ]]') }} > {tokens.map((_, idx) => ( <div key={idx}>{idx + 1}</div> ))} </div> )} <div style={{ flex: 1, fontSize: parseInt('[[ typography.font_sizes[typography.default_resolution].xs ]]'), lineHeight: parseFloat('[[ typography.line_heights.relaxed ]]'), overflow: 'auto', maxHeight: '100%', whiteSpace: 'pre' }} > {tokens.map((line, i) => { const isHighlighted = highlight_lines.includes(i + 1); return ( <div key={i} {...getLineProps({ line })} style={{ ...getLineProps({ line }).style, backgroundColor: isHighlighted ? '[[ colors.highlight.line ]]' : 'transparent', paddingLeft: parseInt('[[ spacing.spacing.xs ]]'), paddingRight: parseInt('[[ spacing.spacing.xs ]]'), marginLeft: -parseInt('[[ spacing.spacing.xs ]]'), marginRight: -parseInt('[[ spacing.spacing.xs ]]') }} > {line.map((token, key) => ( <span key={key} {...getTokenProps({ token })} /> ))} </div> ); })} </div> </div> </div> </> ) : ( <div style={{ padding: variant === 'editor' ? parseInt('[[ spacing.spacing.xl ]]') : 0 }}> {title && variant !== 'editor' && ( <div style={{ fontSize: parseInt('[[ typography.font_sizes[typography.default_resolution].sm ]]'), fontWeight: parseInt('[[ typography.font_weights.semibold ]]'), color: '[[ colors.text.on_dark ]]', marginBottom: parseInt('[[ spacing.spacing.lg ]]'), fontFamily: "'[[ "', '".join(typography.primary_font.fonts) ]]'" }} > {title} </div> )} <div style={{ display: 'flex', gap: parseInt('[[ spacing.spacing.lg ]]') }}> {show_line_numbers && ( <div style={{ color: '[[ colors.text.dimmed ]]', fontSize: parseInt('[[ typography.font_sizes[typography.default_resolution].xs ]]'), lineHeight: parseFloat('[[ typography.line_heights.relaxed ]]'), textAlign: 'right', userSelect: 'none', minWidth: 50 }} > {tokens.map((_, idx) => ( <div key={idx}>{idx + 1}</div> ))} </div> )} <div style={{ flex: 1, fontSize: parseInt('[[ typography.font_sizes[typography.default_resolution].xs ]]'), lineHeight: parseFloat('[[ typography.line_heights.relaxed ]]'), overflow: 'auto', maxHeight: '100%', whiteSpace: 'pre' }} > {tokens.map((line, i) => { const isHighlighted = highlight_lines.includes(i + 1); return ( <div key={i} {...getLineProps({ line })} style={{ ...getLineProps({ line }).style, backgroundColor: isHighlighted ? '[[ colors.highlight.line ]]' : 'transparent', paddingLeft: parseInt('[[ spacing.spacing.xs ]]'), paddingRight: parseInt('[[ spacing.spacing.xs ]]'), marginLeft: -parseInt('[[ spacing.spacing.xs ]]'), marginRight: -parseInt('[[ spacing.spacing.xs ]]') }} > {line.map((token, key) => ( <span key={key} {...getTokenProps({ token })} /> ))} </div> ); })} </div> </div> </div> )} </div> )} </Highlight> </div> ); };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/chrishayuk/chuk-mcp-remotion'

If you have feedback or need assistance with the MCP directory API, please join our Discord server