โ† Catalog

Edit Props (live)

config
config

Source

429 lines
import React from "react";
import {
  AbsoluteFill,
  useCurrentFrame,
  useVideoConfig,
  interpolate,
  spring,
  Easing,
} from "remotion";
import { loadFont as loadInter } from "@remotion/google-fonts/Inter";
import { loadFont as loadJetMono } from "@remotion/google-fonts/JetBrainsMono";

loadInter("normal", { weights: ["400", "500", "600", "700"] });
loadJetMono("normal", { weights: ["400", "500", "700"] });

const UI_FONT = "'Inter', sans-serif";
const CODE_FONT = "'JetBrains Mono', monospace";

// VS Code Dark+ palette โ€” accurate enough to feel like the real thing
const C = {
  bgChrome:  "#1f1f1f",
  bgSide:    "#181818",
  bgEdit:    "#1f1f1f",
  bgTerm:    "#181818",
  bgActBar:  "#181818",
  bgStatus:  "#0078d4",
  border:    "#2b2b2b",
  ink:       "#cccccc",
  mute:      "#9d9d9d",
  faint:     "#6e7681",
  accent:    "#3794ff",
  // Syntax
  comment:   "#6a9955",
  string:    "#ce9178",
  number:    "#b5cea8",
  keyword:   "#569cd6",
  type:      "#4ec9b0",
  fn:        "#dcdcaa",
  prop:      "#9cdcfe",
  punct:     "#d4d4d4",
};

interface VSCodeUIProps {
  backgroundColor?: string;
  // Show the full window chrome at this scale (1 = native 1080w)
  scale?: number;
}

// Token-coloured code line
const tok = (txt: string, color: string, key?: number) => (
  <span key={key} style={{ color }}>{txt}</span>
);

const SkillFile: React.FC<{ name: string; active?: boolean; opacity?: number }> = ({ name, active, opacity = 1 }) => (
  <div style={{
    display: "flex",
    alignItems: "center",
    gap: 8,
    padding: "3px 16px 3px 32px",
    background: active ? "#2a2d2e" : "transparent",
    borderLeft: active ? `2px solid ${C.accent}` : "2px solid transparent",
    color: active ? "#fff" : C.ink,
    fontFamily: UI_FONT,
    fontSize: 17,
    opacity,
  }}>
    <span style={{ color: "#519aba", fontSize: 14 }}>โ–ธ</span>
    <span>๐Ÿ“„</span>
    <span>{name}</span>
  </div>
);

export const VSCodeUI: React.FC<VSCodeUIProps> = ({ backgroundColor = "#0a0b10", scale = 1 }) => {
  const frame = useCurrentFrame();
  const { fps, width, height } = useVideoConfig();
  const t = frame / fps;

  // Window enters with a tiny scale-up + fade
  const winSp = spring({ frame, fps, config: { damping: 14, stiffness: 100, mass: 0.6 } });
  const winScale = interpolate(winSp, [0, 1], [0.96, 1]);
  const winOp = interpolate(frame, [0, 12], [0, 1], { extrapolateRight: "clamp" });

  // Sidebar fades in first (0..0.4s)
  const sideOp = interpolate(t, [0.05, 0.35], [0, 1], { extrapolateRight: "clamp", extrapolateLeft: "clamp" });

  // === Cursor choreography ===
  // Single move: from top-right โ†’ terminal panel โ†’ click โ†’ zoom + typing.
  // Sidebar files stay visible the whole time (no folder-collapse step).
  const startX = 0.82, startY = 0.18;
  const termX  = 0.55, termY = 0.71;  // 0.71 = inside terminal area (top ~0.63, bottom ~0.74)

  const moveT = interpolate(t, [0.40, 1.95], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp", easing: Easing.inOut(Easing.cubic) });
  const cx = startX + (termX - startX) * moveT;
  const cy = startY + (termY - startY) * moveT;
  const cursorVisible = t < 2.30;

  // Code reveals line-by-line during the cursor's slow approach
  const codeReveal = interpolate(t, [0.40, 1.85], [0, 1], {
    extrapolateLeft: "clamp", extrapolateRight: "clamp",
  });

  // Terminal types after the click
  const termStart = 2.10;
  const termCmd = "$ claude /skill pdf";
  const termTypedChars = Math.max(0, Math.floor((t - termStart) * 26));
  const termTyped = termCmd.slice(0, Math.min(termTypedChars, termCmd.length));
  const termResult = t > termStart + 1.2
    ? "โœ“ pdf skill loaded โ€” ready"
    : "";

  // Active editor cursor blink in terminal
  const blink = Math.floor((frame / fps) * 2) % 2 === 0;

  // Window dims โ€” fits the 1080ร—1920 frame with margins
  const winW = Math.floor(1080 * 0.94);
  const winH = Math.floor(1920 * 0.66);
  const winX = Math.floor((1080 - winW) / 2);
  const winY = Math.floor(1920 * 0.10);

  // Sidebar, editor, terminal split inside the window
  const actBarW = 50;
  const sideW = 240;
  const termH = 220;
  const tabH = 42;
  const titleH = 40;
  const statusH = 34;
  const editorH = winH - titleH - tabH - termH - statusH;

  // === Zoom into terminal after the click ===
  // Pin the terminal's top-left text origin to a comfortable left margin so
  // the prompt ("$ claude...") is never cropped at any zoom level.
  // Terminal text starts at: editor col left + 18px padding.
  const termTextLeftInWin = actBarW + sideW + 18;
  const termTextTopInWin  = winH - statusH - termH + 40; // command line y
  const termOriginXAbs = winX + termTextLeftInWin;
  const termOriginYAbs = winY + termTextTopInWin;
  const zoomT = interpolate(t, [2.00, 2.60], [0, 1], {
    extrapolateLeft: "clamp", extrapolateRight: "clamp",
    easing: Easing.inOut(Easing.cubic),
  });
  const zoom = 1 + 0.55 * zoomT; // 1.55x โ€” enough impact, terminal stays in view
  // Land the text origin at frame (90, 760) โ€” left margin, slightly above center
  const dx = (90  - termOriginXAbs) * zoomT;
  const dy = (760 - termOriginYAbs) * zoomT;

  // Skills list rendered inside sidebar
  const skills = [
    "pdf.md",
    "docx.md",
    "xlsx.md",
    "pptx.md",
    "canvas-design.md",
    "frontend-design.md",
    "mcp-builder.md",
    "skill-creator.md",
    "webapp-testing.md",
    "claude-api.md",
  ];

  // PDF.md content (token-coloured)
  const codeLines: React.ReactNode[][] = [
    [tok("# pdf โ€” Extract text + tables from PDFs", C.comment)],
    [],
    [tok("name", C.prop), tok(": ", C.punct), tok("pdf", C.string)],
    [tok("description", C.prop), tok(": ", C.punct), tok("PDF extraction skill โ€” text, tables, metadata", C.string)],
    [tok("inputs", C.prop), tok(":", C.punct)],
    [tok("  - ", C.punct), tok("file", C.prop), tok(": ", C.punct), tok("path", C.type)],
    [tok("  - ", C.punct), tok("pages", C.prop), tok(": ", C.punct), tok("optional[range]", C.type)],
    [],
    [tok("steps", C.prop), tok(":", C.punct)],
    [tok("  1. ", C.number), tok("open ", C.keyword), tok("pdfplumber", C.fn), tok(".load(file)", C.punct)],
    [tok("  2. ", C.number), tok("for ", C.keyword), tok("page ", C.prop), tok("in ", C.keyword), tok("doc", C.prop), tok(": extract_text()", C.punct)],
    [tok("  3. ", C.number), tok("detect_tables", C.fn), tok("(page) โ†’ write_csv()", C.punct)],
    [tok("  4. ", C.number), tok("return ", C.keyword), tok("structured_json", C.type)],
    [],
    [tok("examples", C.prop), tok(":", C.punct)],
    [tok("  - ", C.punct), tok('"extract invoice line items from receipt.pdf"', C.string)],
    [tok("  - ", C.punct), tok('"summarize 200-page Q3 report"', C.string)],
  ];
  // Reveal full lines at a time โ€” feels like fast paste/load, not slow type
  const linesToShow = Math.floor(codeReveal * codeLines.length + 0.001);
  const renderedLines = codeLines.map((line, li) => li < linesToShow ? line : []);

  return (
    <AbsoluteFill style={{
      background: `radial-gradient(ellipse at 50% 35%, #14161e 0%, #06070b 75%)`,
      overflow: "hidden",
    }}>
    {/* Zoom wrapper โ€” scales the entire UI (window + cursor) into terminal */}
    <div style={{
      position: "absolute",
      inset: 0,
      transform: `translate(${dx}px, ${dy}px) scale(${zoom})`,
      transformOrigin: `${termOriginXAbs}px ${termOriginYAbs}px`,
    }}>
      {/* Decorative blurred light */}
      <div style={{
        position: "absolute",
        top: "8%", left: "20%",
        width: 600, height: 600,
        background: "radial-gradient(circle, rgba(55,148,255,0.10) 0%, transparent 60%)",
        filter: "blur(40px)",
        pointerEvents: "none",
      }} />

      {/* Window */}
      <div style={{
        position: "absolute",
        left: winX, top: winY,
        width: winW, height: winH,
        background: C.bgEdit,
        borderRadius: 14,
        boxShadow: "0 30px 80px rgba(0,0,0,0.65), 0 0 0 1px rgba(255,255,255,0.04)",
        overflow: "hidden",
        opacity: winOp,
        transform: `scale(${winScale})`,
        transformOrigin: "center center",
        display: "flex",
        flexDirection: "column",
      }}>

        {/* Title bar */}
        <div style={{
          height: titleH,
          background: C.bgChrome,
          borderBottom: `1px solid ${C.border}`,
          display: "flex",
          alignItems: "center",
          padding: "0 16px",
          gap: 8,
          flexShrink: 0,
        }}>
          {/* mac traffic lights */}
          <div style={{ width: 12, height: 12, borderRadius: "50%", background: "#ff5f57" }} />
          <div style={{ width: 12, height: 12, borderRadius: "50%", background: "#febc2e" }} />
          <div style={{ width: 12, height: 12, borderRadius: "50%", background: "#28c840" }} />
          <div style={{ flex: 1, textAlign: "center", color: C.mute, fontFamily: UI_FONT, fontSize: 15 }}>
            anthropics/skills โ€” Visual Studio Code
          </div>
          <div style={{ width: 36 }} />
        </div>

        {/* Body row: activity bar | sidebar | editor */}
        <div style={{ display: "flex", flex: 1, minHeight: 0 }}>
          {/* Activity bar */}
          <div style={{
            width: actBarW, background: C.bgActBar,
            borderRight: `1px solid ${C.border}`,
            display: "flex", flexDirection: "column", alignItems: "center",
            padding: "12px 0", gap: 18,
          }}>
            {["๐Ÿ“","๐Ÿ”","๐Ÿ”€","๐Ÿ›","๐Ÿงฉ"].map((ic, i) => (
              <div key={i} style={{
                fontSize: 22,
                opacity: i === 0 ? 1 : 0.45,
                borderLeft: i === 0 ? `2px solid #fff` : "2px solid transparent",
                paddingLeft: 4,
                width: "100%", textAlign: "center",
              }}>{ic}</div>
            ))}
          </div>

          {/* Sidebar */}
          <div style={{
            width: sideW, background: C.bgSide,
            borderRight: `1px solid ${C.border}`,
            opacity: sideOp,
            overflow: "hidden",
          }}>
            <div style={{
              padding: "10px 16px", color: C.mute,
              fontFamily: UI_FONT, fontSize: 13, letterSpacing: 1.2,
              textTransform: "uppercase",
            }}>
              Explorer
            </div>
            <div style={{
              padding: "4px 16px",
              fontFamily: UI_FONT, fontSize: 15,
              color: "#fff", display: "flex", alignItems: "center", gap: 6,
            }}>
              <span style={{ color: C.mute }}>โ–พ</span>
              <span style={{ fontWeight: 600 }}>SKILLS</span>
            </div>
            {skills.map((s, i) => (
              <SkillFile key={s} name={s} opacity={interpolate(t, [0.10 + i * 0.025, 0.25 + i * 0.025], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" })} />
            ))}
          </div>

          {/* Editor column (tabs + content + terminal) */}
          <div style={{ flex: 1, display: "flex", flexDirection: "column", minWidth: 0 }}>
            {/* Tab strip */}
            <div style={{
              height: tabH,
              background: C.bgChrome,
              borderBottom: `1px solid ${C.border}`,
              display: "flex", alignItems: "stretch",
              flexShrink: 0,
            }}>
              <div style={{
                background: C.bgEdit,
                borderRight: `1px solid ${C.border}`,
                padding: "0 18px",
                display: "flex", alignItems: "center", gap: 8,
                color: "#fff", fontFamily: UI_FONT, fontSize: 15,
                borderTop: `2px solid ${C.accent}`,
              }}>
                <span style={{ color: "#519aba" }}>โ–ธ</span>
                pdf.md
                <span style={{ color: C.mute, marginLeft: 6, fontSize: 18 }}>ร—</span>
              </div>
            </div>

            {/* Code area */}
            <div style={{
              flex: 1,
              background: C.bgEdit,
              padding: "16px 0 16px 0",
              overflow: "hidden",
              fontFamily: CODE_FONT,
              fontSize: 18,
              lineHeight: 1.55,
              color: C.ink,
              position: "relative",
            }}>
              {renderedLines.map((line, i) => (
                <div key={i} style={{ display: "flex", paddingLeft: 16, paddingRight: 16 }}>
                  <span style={{ width: 36, color: C.faint, textAlign: "right", marginRight: 16, userSelect: "none" }}>
                    {i + 1}
                  </span>
                  <span style={{ flex: 1 }}>
                    {line.length === 0 ? "\u00A0" : line}
                    {/* Typewriter caret on the last visible line */}
                    {i === renderedLines.findIndex((l, ix) => ix > 0 && renderedLines[ix].length === 0 && (renderedLines[ix - 1].length > 0)) - 1 && codeReveal < 1 && (
                      <span style={{ background: C.ink, opacity: blink ? 1 : 0.2, marginLeft: 1 }}>&nbsp;</span>
                    )}
                  </span>
                </div>
              ))}
            </div>

            {/* Terminal panel */}
            <div style={{
              height: termH,
              background: C.bgTerm,
              borderTop: `1px solid ${C.border}`,
              padding: "10px 18px",
              flexShrink: 0,
              fontFamily: CODE_FONT,
            }}>
              {/* Terminal tabs */}
              <div style={{ display: "flex", gap: 18, marginBottom: 10, color: C.mute, fontFamily: UI_FONT, fontSize: 13, letterSpacing: 1.2 }}>
                <span style={{ color: "#fff", borderBottom: `1px solid ${C.accent}`, paddingBottom: 4 }}>TERMINAL</span>
                <span style={{ paddingBottom: 4, opacity: 0.65 }}>PROBLEMS</span>
                <span style={{ paddingBottom: 4, opacity: 0.65 }}>OUTPUT</span>
                <span style={{ paddingBottom: 4, opacity: 0.65 }}>DEBUG CONSOLE</span>
              </div>
              <div style={{ color: C.ink, fontSize: 17, lineHeight: 1.5 }}>
                <span>{termTyped}</span>
                {termTypedChars < termCmd.length && (
                  <span style={{ background: C.ink, opacity: blink ? 1 : 0.2, marginLeft: 1 }}>&nbsp;</span>
                )}
              </div>
              {termResult && (
                <div style={{ color: "#73c991", fontSize: 17, marginTop: 6 }}>
                  {termResult}
                </div>
              )}
            </div>
          </div>
        </div>

        {/* Status bar */}
        <div style={{
          height: statusH,
          background: C.bgStatus,
          color: "#fff",
          fontFamily: UI_FONT,
          fontSize: 13,
          display: "flex",
          alignItems: "center",
          padding: "0 14px",
          gap: 18,
          flexShrink: 0,
        }}>
          <span>โއ main</span>
          <span>โ†ป 0 โš  0</span>
          <span style={{ flex: 1 }} />
          <span>YAML</span>
          <span>UTF-8</span>
          <span>LF</span>
          <span>Ln {Math.min(17, Math.floor(codeReveal * 17) + 1)}, Col 1</span>
        </div>
      </div>

      {/* Cursor */}
      {cursorVisible && (
        <svg width="32" height="42" viewBox="0 0 34 44" style={{
          position: "absolute",
          left: `${cx * 100}%`,
          top: `${cy * 100}%`,
          transform: "translate(-4px, -2px)",
          filter: "drop-shadow(0 3px 5px rgba(0,0,0,0.6))",
          zIndex: 10,
        }}>
          <path d="M4 4 L4 32 L11 26 L16 38 L20 36 L15 24 L24 24 Z" fill="#fff" stroke="#000" strokeWidth="1.5" />
        </svg>
      )}

      {/* Click ripple โ€” terminal */}
      {t > 1.95 && t < 2.45 && (
        <div style={{
          position: "absolute",
          left: `${termX * 100}%`,
          top: `${termY * 100}%`,
          width: 100, height: 100,
          marginLeft: -50, marginTop: -50,
          borderRadius: "50%",
          border: `3px solid ${C.accent}`,
          opacity: interpolate(t, [1.95, 2.45], [0.95, 0]),
          transform: `scale(${interpolate(t, [1.95, 2.45], [0.4, 2.0])})`,
          pointerEvents: "none",
        }} />
      )}
    </div>
    </AbsoluteFill>
  );
};

Schema (Zod)

// Auto-extracted from Props interface โ€” see source for authoritative types.
// Component: VSCodeUI
// 2 props detected ยท 0 port(s)

VSCode UI

vs_code_ui

VSCode UI โ€” Andy's hand-crafted Remotion scene. 2 props. no ports detected โ€” source-only preview.

Workflow Inputs (0)

This block has no workflow-connected inputs.

Config Fields (2)

  • scale
  • backgroundColor

Meta

Updated
4/21/2026