{
  "name": "opencode",
  "type": "registry:block",
  "description": "OpenCode TUI composer with agent and model status",
  "dependencies": [
    "remotion",
    "@remotion/google-fonts"
  ],
  "registryDependencies": [
    "ai-composer-utils"
  ],
  "files": [
    {
      "path": "registry/bases/default/scenes/opencode/index.tsx",
      "type": "registry:block",
      "content": "import { loadFont } from \"@remotion/google-fonts/JetBrainsMono\";\nimport { AbsoluteFill, useCurrentFrame, useVideoConfig } from \"remotion\";\nimport {\n  AI_TYPING_START_TUI,\n  Caret,\n  fadeUpAt,\n  introBounceIn,\n  stageScale,\n  useTypewriter,\n} from \"@/remotion/lib/ai-composer-utils\";\n\nconst { fontFamily } = loadFont(\"normal\", {\n  weights: [\"400\", \"600\", \"700\"],\n  subsets: [\"latin\"],\n});\n\nexport type OpencodeProps = {\n  placeholder?: string;\n  query?: string;\n  agentName?: string;\n  modelName?: string;\n  provider?: string;\n  accentColor?: string;\n  theme?: \"light\" | \"dark\";\n  speed?: number;\n};\n\ntype Theme = {\n  page: string;\n  boxBg: string;\n  fg: string;\n  fgMuted: string;\n  fgQuery: string;\n};\n\nexport const THEMES: Record<\"light\" | \"dark\", Theme> = {\n  light: {\n    page: \"#FFFFFF\",\n    boxBg: \"#F2F2F2\",\n    fg: \"#111111\",\n    fgMuted: \"#888888\",\n    fgQuery: \"#555555\",\n  },\n  dark: {\n    page: \"#000000\",\n    boxBg: \"#161616\",\n    fg: \"#EDEDED\",\n    fgMuted: \"#6B6B6B\",\n    fgQuery: \"#9A9A9A\",\n  },\n};\n\nconst TYPING_CPS = 20;\n\nfunction Wordmark() {\n  return (\n    <svg width={468} height={84} viewBox=\"0 0 234 42\" fill=\"none\">\n      <title>opencode</title>\n      <clipPath id=\"opencode-wordmark-clip\">\n        <rect width={234} height={42} fill=\"#ffffff\" />\n      </clipPath>\n      <g clipPath=\"url(#opencode-wordmark-clip)\">\n        <path d=\"M18 30H6V18H18V30Z\" fill=\"#4B4646\" />\n        <path d=\"M18 12H6V30H18V12ZM24 36H0V6H24V36Z\" fill=\"#B7B1B1\" />\n        <path d=\"M48 30H36V18H48V30Z\" fill=\"#4B4646\" />\n        <path d=\"M36 30H48V12H36V30ZM54 36H36V42H30V6H54V36Z\" fill=\"#B7B1B1\" />\n        <path d=\"M84 24V30H66V24H84Z\" fill=\"#4B4646\" />\n        <path\n          d=\"M84 24H66V30H84V36H60V6H84V24ZM66 18H78V12H66V18Z\"\n          fill=\"#B7B1B1\"\n        />\n        <path d=\"M108 36H96V18H108V36Z\" fill=\"#4B4646\" />\n        <path\n          d=\"M108 12H96V36H90V6H108V12ZM114 36H108V12H114V36Z\"\n          fill=\"#B7B1B1\"\n        />\n        <path d=\"M144 30H126V18H144V30Z\" fill=\"#4B4646\" />\n        <path d=\"M144 12H126V30H144V36H120V6H144V12Z\" fill=\"#F1ECEC\" />\n        <path d=\"M168 30H156V18H168V30Z\" fill=\"#4B4646\" />\n        <path d=\"M168 12H156V30H168V12ZM174 36H150V6H174V36Z\" fill=\"#F1ECEC\" />\n        <path d=\"M198 30H186V18H198V30Z\" fill=\"#4B4646\" />\n        <path\n          d=\"M198 12H186V30H198V12ZM204 36H180V6H198V0H204V36Z\"\n          fill=\"#F1ECEC\"\n        />\n        <path d=\"M234 24V30H216V24H234Z\" fill=\"#4B4646\" />\n        <path\n          d=\"M216 12V18H228V12H216ZM234 24H216V30H234V36H210V6H234V24Z\"\n          fill=\"#F1ECEC\"\n        />\n      </g>\n    </svg>\n  );\n}\n\nexport const Opencode: React.FC<OpencodeProps> = ({\n  placeholder = \"Ask anything... \",\n  query = '\"What is the tech stack of this project?\"',\n  agentName = \"Build\",\n  modelName = \"Kimi K2.5\",\n  provider = \"Moonshot AI\",\n  accentColor = \"#2B7FFF\",\n  theme = \"dark\",\n  speed = 1,\n}) => {\n  const frame = useCurrentFrame();\n  const { width, height, fps } = useVideoConfig();\n  const t = THEMES[theme];\n  const scale = stageScale(width, height);\n\n  const tw = useTypewriter(query, {\n    cps: TYPING_CPS,\n    speed,\n    startFrame: AI_TYPING_START_TUI,\n  });\n\n  const intro = introBounceIn(frame * speed, fps);\n  const logoFade = fadeUpAt(frame * speed, [4, 22]);\n  const boxFade = fadeUpAt(frame * speed, [12, 30]);\n  const hintsFade = fadeUpAt(frame * speed, [20, 38]);\n\n  const boxWidth = 1000;\n\n  return (\n    <AbsoluteFill style={{ background: t.page, fontFamily }}>\n      <div\n        style={{\n          position: \"absolute\",\n          left: \"50%\",\n          top: \"50%\",\n          width: 1280,\n          height: 720,\n          transform: `translate(-50%, -50%) scale(${scale})`,\n          display: \"flex\",\n          flexDirection: \"column\",\n          alignItems: \"center\",\n          justifyContent: \"center\",\n        }}\n      >\n        <div\n          style={{\n            lineHeight: 0,\n            opacity: logoFade.opacity,\n            transform: `translateY(${logoFade.translateY + intro.translateY * 0.4}px)`,\n          }}\n        >\n          <Wordmark />\n        </div>\n\n        <div\n          style={{\n            width: boxWidth,\n            marginTop: 40,\n            minHeight: 150,\n            background: t.boxBg,\n            borderLeft: `4px solid ${accentColor}`,\n            padding: \"26px 28px\",\n            boxSizing: \"border-box\",\n            display: \"flex\",\n            flexDirection: \"column\",\n            justifyContent: \"center\",\n            gap: 26,\n            opacity: boxFade.opacity,\n            transform: `translateY(${boxFade.translateY + intro.translateY * 0.6}px) scale(${intro.scale})`,\n            transformOrigin: \"center top\",\n          }}\n        >\n          <div\n            style={{\n              display: \"inline-flex\",\n              alignItems: \"center\",\n              fontSize: 22,\n            }}\n          >\n            <span style={{ color: t.fgMuted }}>{placeholder}</span>\n            {tw.count > 0 ? (\n              <span style={{ color: t.fgQuery }}>{tw.text}</span>\n            ) : null}\n            <span style={{ opacity: 0.6, display: \"inline-flex\" }}>\n              <Caret\n                width={12}\n                height={24}\n                color={t.fg}\n                blink={!tw.typing}\n                speed={speed}\n                marginLeft={2}\n              />\n            </span>\n          </div>\n\n          <div\n            style={{\n              display: \"flex\",\n              flexDirection: \"row\",\n              alignItems: \"center\",\n              gap: 16,\n              fontSize: 20,\n            }}\n          >\n            <span style={{ color: accentColor, fontWeight: 600 }}>\n              {agentName}\n            </span>\n            <span style={{ color: t.fg }}>{modelName}</span>\n            <span style={{ color: t.fgMuted }}>{provider}</span>\n          </div>\n        </div>\n\n        <div\n          style={{\n            width: boxWidth,\n            marginTop: 18,\n            display: \"flex\",\n            justifyContent: \"flex-end\",\n            gap: 22,\n            fontSize: 18,\n            opacity: hintsFade.opacity,\n            transform: `translateY(${hintsFade.translateY}px)`,\n          }}\n        >\n          <span style={{ display: \"inline-flex\", gap: 8 }}>\n            <span style={{ color: t.fg, fontWeight: 700 }}>tab</span>\n            <span style={{ color: t.fgMuted }}>agents</span>\n          </span>\n          <span style={{ display: \"inline-flex\", gap: 8 }}>\n            <span style={{ color: t.fg, fontWeight: 700 }}>ctrl+p</span>\n            <span style={{ color: t.fgMuted }}>commands</span>\n          </span>\n        </div>\n      </div>\n    </AbsoluteFill>\n  );\n};\n"
    }
  ],
  "atlas": {
    "lane": "blocks",
    "drive": "time",
    "tier": "core",
    "tags": [
      "ai"
    ]
  }
}