| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import React from "react"; |
| import { useCurrentFrame } from "remotion"; |
| import { useKit } from "./context"; |
| import { withAlpha } from "./theme"; |
| import { staggerEntrance } from "./motion"; |
|
|
| export interface CodeBlockProps { |
| |
| lines?: string[]; |
| |
| language?: string; |
| start?: number; |
| maxLines?: number; |
| style?: React.CSSProperties; |
| } |
|
|
| export const CodeBlock: React.FC<CodeBlockProps> = ({ |
| lines, |
| language, |
| start = 6, |
| maxLines = 12, |
| style, |
| }) => { |
| const frame = useCurrentFrame(); |
| const { palette, isPortrait } = useKit(); |
| const rows = (lines ?? []).filter((l) => typeof l === "string").slice(0, maxLines); |
| if (!rows.length) return null; |
|
|
| const mono = |
| "ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace"; |
| const fontSize = isPortrait ? 22 : 24; |
|
|
| return ( |
| <div |
| style={{ |
| background: palette.isDark ? withAlpha("#000000", 0.35) : withAlpha("#0B0B0F", 0.92), |
| border: `1px solid ${withAlpha(palette.accent, 0.35)}`, |
| borderRadius: 14, |
| overflow: "hidden", |
| boxShadow: "0 18px 50px rgba(0,0,0,0.35)", |
| maxWidth: isPortrait ? "92%" : 1100, |
| width: "100%", |
| ...style, |
| }} |
| > |
| {/* window chrome */} |
| <div |
| style={{ |
| display: "flex", |
| alignItems: "center", |
| gap: 8, |
| padding: "12px 16px", |
| background: withAlpha("#FFFFFF", 0.04), |
| borderBottom: `1px solid ${withAlpha("#FFFFFF", 0.08)}`, |
| }} |
| > |
| {["#FF5F56", "#FFBD2E", "#27C93F"].map((dot) => ( |
| <span key={dot} style={{ width: 11, height: 11, borderRadius: "50%", background: dot }} /> |
| ))} |
| {language && ( |
| <span |
| style={{ |
| marginLeft: 8, |
| fontFamily: mono, |
| fontSize: 14, |
| letterSpacing: "0.08em", |
| textTransform: "uppercase", |
| color: withAlpha("#FFFFFF", 0.5), |
| }} |
| > |
| {language} |
| </span> |
| )} |
| </div> |
| {/* lines */} |
| <div style={{ padding: "20px 22px", display: "flex", flexDirection: "column" }}> |
| {rows.map((line, i) => { |
| const enter = staggerEntrance(frame, i, { start, stagger: 4 }); |
| return ( |
| <div |
| key={i} |
| style={{ |
| display: "flex", |
| gap: 18, |
| opacity: enter.opacity, |
| transform: enter.transform, |
| fontFamily: mono, |
| fontSize, |
| lineHeight: 1.65, |
| whiteSpace: "pre", |
| }} |
| > |
| <span style={{ color: withAlpha("#FFFFFF", 0.28), userSelect: "none", minWidth: 22, textAlign: "right" }}> |
| {i + 1} |
| </span> |
| <span style={{ color: withAlpha("#FFFFFF", 0.92) }}>{line}</span> |
| </div> |
| ); |
| })} |
| </div> |
| </div> |
| ); |
| }; |
|
|