Communitygithub.com

AmanVarshney01/tcut

Grave vídeos reproduzíveis de terminal (MP4/GIF/SVG/HTML) a partir de scripts TypeScript ou sessões ao vivo com a CLI tcut. Use quando o usuário quiser uma demo de terminal, screencast de CLI, gravação de TUI ou GIF animado de terminal para README, site ou postagem social.

O que é tcut?

tcut é uma ferramenta de gravação de terminal que permite criar vídeos determinísticos e reproduzíveis a partir de scripts TypeScript ou sessões ao vivo. Diferente de gravadores de tela tradicionais, o tcut captura a saída real de um shell (usando um PTY com o terminal Ghostty headless) e a renderiza em formatos como MP4, GIF, SVG, WebM e HTML. Para desenvolvedores e agentes de IA, o modo script é o mais útil: você escreve um arquivo `demo.video.ts` com a API `defineVideo`, que simula digitação, pressionamento de teclas, espera por padrões na tela e muito mais — tudo sem depender de temporizadores imprecisos. O tcut suporta múltiplos temas (mais de 600 temas Ghostty), overlays de teclas, legendas Markdown, cards de transição, zoom em regiões, capítulos MP4, e até um painel de navegador para demos de front-end. Ele também funciona em CI, permitindo testes rápidos (`tcut test`), diffs entre gravações (`tcut diff`) e cortes/ junções sem regravar (`tcut cut`, `tcut concat`). A ferramenta é instalada via `bun add -g termcut` e requer ffmpeg apenas para formatos de vídeo comuns; SVG e HTML são nativos.

Funciona com✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/AmanVarshney01/tcut/tree/HEAD/skills/tcut

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

tcut — sessões de terminal para vídeo

tcut grava um shell real (PTY + terminal Ghostty headless) e renderiza vídeos determinísticos. Scripts são TypeScript puro — sem DSL. Mesma gravação, mesmos pixels, sempre.

Configuração

bun add -g termcut   # Bun >= 1.4, instala o comando `tcut`

MP4/GIF/WebM precisam do ffmpeg no PATH. Os players SVG e HTML não precisam de nada mais. Tudo suporta --json para resultados legíveis por máquina; nunca solicita confirmação.

Duas formas de gravar

Com script (preferido para agentes) — escreva demo.video.ts, execute tcut demo.video.ts:

import { defineVideo } from "tcut";

export default defineVideo(
  { output: ["demo.mp4", "demo.gif"], preset: "x", theme: "Catppuccin Mocha", keys: true, maxPause: "1.5s" },
  async (t) => {
    await t.run("bun --version");        // digita, pressiona Enter, espera o prompt retornar
    await t.expect(/1\.\d+/);            // afirma o que aparece na tela renderizada (falha o vídeo se ausente)
    await t.sleep("1.5s");
  },
);

Ao vivo — tcut rec -o demo.gif abre um shell, grava até exit, e escreve tanto a gravação exata (demo.cast) quanto um script editável (demo.video.ts) do que foi digitado. tcut rec -o demo.mp4 -- npm create vite grava um único comando.

A API t (dentro do defineVideo)

  • t.run(cmd, { wait? }) — digita + Enter + espera pelo prompt do shell (baseado na tela, não em temporizador)
  • t.type(text) / t.paste(text) / t.enter() / t.escape() / t.tab() — controla TUIs tecla por tecla
  • t.up/down/left/right/pageUp/pageDown/home/end(), t.ctrl("c"), t.alt("b"), t.shift("tab"), t.key("f5"), t.scrollUp/scrollDown()
  • t.wait(regex?, { scope: "line" | "screen" }) — espera até que a tela mostre; t.expect(re) — afirma
  • t.sleep("800ms") — durações aceitam "500ms", "1.5s", números (ms)
  • t.hide(async () => { ... }) — executa configuração fora da câmera (o estado persiste; não mate jobs em background aqui)
  • t.print(markdown) / t.title(text) — renderiza legendas Markdown no vídeo sem digitar
  • t.zoom({ rows: [0, 5], cols: [0, 60], duration: "500ms" }) — amplia uma região; t.zoom(null) redefine
  • t.slide("Implantar na nuvem", { eyebrow: "3", subtitle: "…", during }) — cartão de transição de quadro inteiro entre demos de funcionalidades: título central grande em tipografia real, desbotado, grava um capítulo com o mesmo nome; during: async () => … executa cd/clear/configuração de servidor invisivelmente atrás dele
  • t.chapter("Instalar") — metadados reais de capítulo MP4 e um ponto de corte: --chapters Instalar / --split-chapters no momento da renderização
  • t.expect(/…/, { scope: "scrollback" }) vê a saída que rolou para fora; t.scrollback() retorna a transcrição completa; -o demo.log escreve
  • Setas são enviadas como SS3 quando o programa ativou o modo cursor de aplicação; t.paste() é suportado entre colchetes quando o programa solicitou — editores se comportam como com um terminal real; print("[texto](url)") cria um link clicável em SVG/HTML
  • title: "auto" segue títulos OSC; tcut doctor demo.cast explica o que uma gravação usou e o que o tcut não pode mostrar (imagens inline)
  • t.timelapse(async () => { await t.run("bun install") }, { speed: 8 }) — tudo dentro executa 8× mais rápido (maxPause apenas remove silêncio; isso comprime a saída)
  • t.snapshot("shot.png") / t.snapshot("hero.svg") — instantâneo daquele momento exato em cada renderização (screenshot = alias), t.clear(), t.resize(cols, rows), t.screen() / t.line() para ler a tela
  • Painel do navegador: browser: { position: "right" | "overlay", width } na configuração, então t.browser.goto(url), t.browser.waitFor(/text/), t.browser.click(sel), t.focus("browser" | "terminal") — grava um WebView real ao lado/sobre o terminal (demos de servidor de desenvolvimento)

Configurações essenciais

output (array = múltiplos formatos), preset: "readme" | "x" | "youtube" | "square", theme (~600 temas Ghostty, tcut themes), cols/rows ou width/height (px), fps, scale: 2 (mesmo layout, 2× os pixels — use para Retina/HiDPI ou qualquer coisa que um player vai escalar), typingSpeed/typingJitter, keys: true (overlay de pressionamento de tecla, um chip por vez; { limit, font, color, background, radius, position }), maxPause: "1.5s" (compressão de inatividade), requires: ["bun", "eza"] (falhe rapidamente antes de gravar se uma ferramenta estiver faltando — também em tcut test), windowBar: "none" + margin: 0 + borderRadius: 0 para um terminal simples, title, marginFill ("transparent" = alfa real em png/webp/gif/webm/svg/html; mp4 usa fallback), shadow: true (sombra suave; margem padrão de 40), watermark: "© você" ou { image: "logo.png", position: "top-left", opacity, size }.

Cortar e juntar (sem regravar)

Tudo na linha do tempo visível do cast, então todo formato funciona e o resultado ainda é um .cast:

tcut render demo.cast --from 2s --to 10s -o clip.gif      # janela de tempo
tcut render demo.cast --chapters Zoom,Intro -o clip.mp4   # capítulos, nessa ordem
tcut render demo.cast --split-chapters -o demo.mp4         # demo-01-intro.mp4, demo-02-zoom.mp4 … (biblioteca de clipes ideal para Remotion)
tcut cut demo.cast --from 2s --to 10s                      # escreve demo-cut.cast
tcut concat intro.cast demo.cast --gap 500ms -o launch.mp4 # mesmas cols×rows necessárias; tela reseta em cada emenda

Renderizar novamente sem reexecutar

Gravação (.cast) e renderização são separadas:

tcut render demo.cast --theme "Gruvbox Dark" -o demo.svg
tcut render demo.cast --width 1280 --height 720 --speed 1.5 -o demo.mp4

CI / testes

  • tcut test demo.video.ts — executa o script como teste (rápido, sem codificação de vídeo)
  • tcut diff a.cast b.cast [--images dir] — compara texto da tela de duas gravações, sai com código 1 se houver diferença

Armadilhas

  • Servidores de desenvolvimento em background no PTY: bun run dev </dev/null >/tmp/dev.log 2>&1 & (leitores de stdin recebem SIGTTIN; logs perdidos repintam sobre TUIs)
  • Espere na tela, não no tempo: prefira t.run / t.wait(/Local:/, { scope: "screen" }) em vez de longos sleeps
  • t.hide mantém o estado — um kill oculto no final vaza para o último quadro
  • Para TUIs (nvim, lazygit, claude), inicie com t.run(cmd, { wait: /algo-na-tela/ }), depois controle com teclas

Referência completa: https://tcut.amanv.dev/llms.txt

Usar tcut como biblioteca

import { defineVideo, renderCast } from "termcut" (apenas Bun). defineVideo(config, script) retorna um Video: await video.run({ force, log }) grava e renderiza e retorna { outputs, screenshots, durationSeconds, recording }; video.record() e video.render(recording, { overrides, clip }) separam as etapas; renderCast(file, overrides) re-renderiza qualquer cast sem um shell. Também exportados: recordLive, cutRecording, concatRecordings, selectChapters, buildSvg, buildHtml, replayFrames, diffCasts, diagnoseCast, generateScript, runScriptTests, publishFiles. Em CI, passe theme/font explícitos ("auto" lê o terminal em execução) e renderize um vídeo por vez.

Individual skills in this repo

This repo contains 7 individual skills — each has its own dedicated page.

Habilidades Relacionadas