{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "press-stat",
  "title": "Press Stat",
  "description": "A mono caption presses out and a huge green 73% lands beside 'Edit time down'.",
  "dependencies": ["remotion", "@remotion/google-fonts"],
  "files": [
    {
      "path": "registry/remocn/press-stat/index.tsx",
      "content": "\"use client\";\n\nimport { loadFont as loadPlexMono } from \"@remotion/google-fonts/IBMPlexMono\";\nimport type React from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport {\n  AbsoluteFill,\n  continueRender,\n  delayRender,\n  Easing,\n  interpolate,\n  spring,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\n\nconst plex = loadPlexMono(\"normal\", {\n  weights: [\"400\", \"500\"],\n  subsets: [\"latin\"],\n});\n\nexport interface PressStatProps {\n  before?: string;\n  value?: number;\n  from?: number;\n  prefix?: string;\n  suffix?: string;\n  accent?: string;\n  background?: string;\n  speed?: number;\n  theme?: \"dark\" | \"light\";\n}\n\nconst GREEN = { dark: \"#00C805\", light: \"#00873C\" } as const;\n\nconst THEMES = {\n  dark: { bg: \"#000000\", ink: \"#FFFFFF\" },\n  light: { bg: \"#FFFFFF\", ink: \"#000000\" },\n} as const;\n\nconst RISE_LEN = 14;\nconst RISE_GAP = 4;\nconst PRESS_AT = 30;\nconst COUNT_LEN = 42;\n\nconst EASE_OUT = Easing.bezier(0.22, 1, 0.36, 1);\nconst EASE_COUNT = Easing.bezier(0.45, 0, 0.2, 1);\nconst CLAMP = {\n  extrapolateLeft: \"clamp\",\n  extrapolateRight: \"clamp\",\n} as const;\nconst INFLATE = { damping: 16, stiffness: 70, mass: 1 };\n\ninterface Metrics {\n  adv: number;\n  cap: number;\n  base: number;\n}\n\nfunction measure(): Metrics {\n  const ctx = document.createElement(\"canvas\").getContext(\"2d\");\n  if (!ctx) throw new Error(\"press-stat: no 2d context\");\n  ctx.font = `500 100px \"${plex.fontFamily}\"`;\n  const m = ctx.measureText(\"0000000000\");\n  const asc = m.fontBoundingBoxAscent / 100;\n  const desc = m.fontBoundingBoxDescent / 100;\n  return {\n    adv: m.width / 1000,\n    cap: m.actualBoundingBoxAscent / 100,\n    base: (1 - (asc + desc)) / 2 + asc,\n  };\n}\n\nfunction useMetrics() {\n  const [m, setM] = useState<Metrics | null>(null);\n  const [handle] = useState(() => delayRender(\"press-stat: measuring\"));\n  const released = useRef(false);\n  useEffect(() => {\n    let live = true;\n    Promise.all([\n      plex.waitUntilDone(),\n      document.fonts.load(`500 100px \"${plex.fontFamily}\"`),\n      document.fonts.load(`400 100px \"${plex.fontFamily}\"`),\n    ])\n      .catch(() => undefined)\n      .then(() => {\n        if (!live) return;\n        setM(measure());\n        if (!released.current) {\n          released.current = true;\n          continueRender(handle);\n        }\n      });\n    return () => {\n      live = false;\n    };\n  }, [handle]);\n  return m;\n}\n\nconst fmt = (n: number) => Math.max(0, Math.floor(n)).toLocaleString(\"en-US\");\n\nexport function PressStat({\n  before = \"Edit time down\",\n  value = 73,\n  from = 0,\n  prefix = \"\",\n  suffix = \"%\",\n  accent,\n  background,\n  speed = 1,\n  theme = \"dark\",\n}: PressStatProps) {\n  const t = useCurrentFrame() * speed;\n  const { width: W, height: H, fps } = useVideoConfig();\n  const c = THEMES[theme];\n  const green = accent ?? GREEN[theme];\n  const m = useMetrics();\n\n  const transparent = background === \"transparent\";\n  const stage = transparent ? \"transparent\" : (background ?? c.bg);\n\n  if (!m) {\n    return (\n      <div style={{ position: \"absolute\", inset: 0, background: stage }} />\n    );\n  }\n\n  const portrait = H > W * 1.1;\n  const margin = Math.min(W, H) * (portrait ? 0.07 : 0.075);\n  const M = W - 2 * margin;\n\n  const target = Math.max(0, Math.floor(value));\n  const start = Math.min(target, Math.max(0, Math.floor(from)));\n  const pCount = interpolate(t, [PRESS_AT, PRESS_AT + COUNT_LEN], [0, 1], {\n    ...CLAMP,\n    easing: EASE_COUNT,\n  });\n  const shown = Math.round(start + (target - start) * pCount);\n  const numText = `${prefix}${fmt(shown)}${suffix}`;\n  const finalText = `${prefix}${fmt(target)}${suffix}`;\n  const startText = `${prefix}${fmt(start)}${suffix}`;\n  const len = (s: string) => Array.from(s).length;\n\n  const words = before.split(\" \").filter(Boolean);\n  const wordsEm = len(before) * m.adv;\n  const gapEm = m.adv;\n\n  const Fw = Math.min(\n    H * (portrait ? 0.045 : 0.085),\n    (M * 0.82) /\n      (wordsEm + (words.length ? gapEm : 0) + len(startText) * m.adv),\n  );\n  const Fh = Math.min(\n    (H * (portrait ? 0.3 : 0.62)) / m.cap,\n    (M * 0.58) / (len(finalText) * m.adv),\n  );\n\n  const g = spring({ frame: t - PRESS_AT, fps, config: INFLATE });\n  const gc = Math.min(1, Math.max(0, g));\n  const Fn = Fw + (Fh - Fw) * g;\n\n  const numW = len(numText) * m.adv * Fn;\n  const line0 =\n    (wordsEm + (words.length ? gapEm : 0) + len(startText) * m.adv) * Fw;\n  const left0 = (W - line0) / 2;\n  const numRight0 = left0 + line0;\n  const numRight = numRight0 + (W - margin - numRight0) * gc;\n  const numLeft = numRight - numW;\n\n  const wordsLeft = left0 + (margin - left0) * gc;\n  const room = numLeft - gapEm * Fw - wordsLeft;\n  const Fwords = words.length\n    ? Math.max(Fw * 0.3, Math.min(Fw, room / Math.max(0.01, wordsEm)))\n    : Fw;\n\n  const base0 = (H + m.cap * Fw) / 2;\n  const base1 = (H + m.cap * Fh) / 2;\n  const baseY = base0 + (base1 - base0) * gc;\n\n  const rise = (i: number) =>\n    interpolate(t, [i * RISE_GAP, i * RISE_GAP + RISE_LEN], [0, 1], {\n      ...CLAMP,\n      easing: EASE_OUT,\n    });\n\n  const masked = (\n    key: string,\n    x: number,\n    F: number,\n    r: number,\n    text: string,\n    color: string,\n    weight: number,\n  ) => {\n    const h = F * 1.25;\n    return (\n      <div\n        key={key}\n        style={{\n          position: \"absolute\",\n          left: x - F * 0.05,\n          top: baseY - m.cap * F - F * 0.2,\n          width: len(text) * m.adv * F + F * 0.1,\n          height: h,\n          overflow: \"hidden\",\n        }}\n      >\n        <div\n          style={{\n            position: \"absolute\",\n            left: F * 0.05,\n            top: F * 0.2 + m.cap * F - m.base * F + (1 - r) * h,\n            height: F,\n            lineHeight: `${F}px`,\n            fontFamily: plex.fontFamily,\n            fontWeight: weight,\n            fontSize: F,\n            whiteSpace: \"pre\",\n            color,\n          }}\n        >\n          {text}\n        </div>\n      </div>\n    );\n  };\n\n  const nodes: React.ReactNode[] = [];\n  let x = wordsLeft;\n  words.forEach((w, i) => {\n    nodes.push(masked(`w${i}`, x, Fwords, rise(i), w, c.ink, 400));\n    x += (len(w) + 1) * m.adv * Fwords;\n  });\n  nodes.push(masked(\"n\", numLeft, Fn, rise(words.length), numText, green, 500));\n\n  return (\n    <AbsoluteFill style={{ background: stage, overflow: \"hidden\" }}>\n      {nodes}\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/press-stat.tsx"
    }
  ],
  "type": "registry:component"
}
