Giter Club home page Giter Club logo

vhs's Introduction

VHS


Latest Release Go Docs Build Status

Write terminal GIFs as code for integration testing and demoing your CLI tools.

Welcome to VHS

The above example was generated with VHS (view source).

Tutorial

To get started, install VHS and create a new .tape file.

vhs new demo.tape

Open the .tape file with your favorite $EDITOR.

vim demo.tape

Tape files consist of a series of commands. The commands are instructions for VHS to perform on its virtual terminal. For a list of all possible commands see the command reference.

# Where should we write the GIF?
Output demo.gif

# Set up a 1200x600 terminal with 46px font.
Set FontSize 46
Set Width 1200
Set Height 600

# Type a command in the terminal.
Type "echo 'Welcome to VHS!'"

# Pause for dramatic effect...
Sleep 500ms

# Run the command by pressing enter.
Enter

# Admire the output for a bit.
Sleep 5s

Once you've finished, save the file and feed it into VHS.

vhs demo.tape

All done! You should see a new file called demo.gif (or whatever you named the Output) in the directory.

A GIF produced by the VHS code above

For more examples see the examples/ directory.

Installation

Note

VHS requires ttyd and ffmpeg to be installed and available on your PATH.

Use a package manager:

# macOS or Linux
brew install vhs

# macOS (via MacPorts)
sudo port install vhs

# Arch Linux (btw)
pacman -S vhs

# Nix
nix-env -iA nixpkgs.vhs

# Debian/Ubuntu
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://repo.charm.sh/apt/gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/charm.gpg
echo "deb [signed-by=/etc/apt/keyrings/charm.gpg] https://repo.charm.sh/apt/ * *" | sudo tee /etc/apt/sources.list.d/charm.list
# Install ttyd from https://github.com/tsl0922/ttyd/releases
sudo apt update && sudo apt install vhs ffmpeg

# Fedora/RHEL
echo '[charm]
name=Charm
baseurl=https://repo.charm.sh/yum/
enabled=1
gpgcheck=1
gpgkey=https://repo.charm.sh/yum/gpg.key' | sudo tee /etc/yum.repos.d/charm.repo
# Install ttyd from https://github.com/tsl0922/ttyd/releases
sudo yum install vhs ffmpeg

# Void Linux
sudo xbps-install vhs

# Windows using winget
# Note: Winget will install all the needed dependencies, which're FFmpeg and ttyd.
#       No need to do any prerequisites to install vhs using this method.
winget install charmbracelet.vhs

# Windows using scoop
scoop install vhs

Or, use Docker to run VHS directly, dependencies included:

docker run --rm -v $PWD:/vhs ghcr.io/charmbracelet/vhs <cassette>.tape

Or, download it:

  • Packages are available in Debian and RPM formats
  • Binaries are available for Linux, macOS, and Windows

Or, just install it with go:

go install github.com/charmbracelet/vhs@latest

Record Tapes

VHS has the ability to generate tape files from your terminal actions!

To record to a tape file, run:

vhs record > cassette.tape

Perform any actions you want and then exit the terminal session to stop recording. You may want to manually edit the generated .tape file to add settings or modify actions. Then, you can generate the GIF:

vhs cassette.tape

Publish Tapes

VHS allows you to publish your GIFs to our servers for easy sharing with your friends and colleagues. Specify which file you want to share, then use the publish sub-command to host it on vhs.charm.sh. The output will provide you with links to share your GIF via browser, HTML, and Markdown.

vhs publish demo.gif

The VHS Server

VHS has an SSH server built in! When you self-host VHS you can access it as though it were installed locally. VHS will have access to commands and applications on the host, so you don't need to install them on your machine.

To start the server run:

vhs serve
Configuration Options
  • VHS_PORT: The port to listen on (1976)
  • VHS_HOST: The host to listen on (localhost)
  • VHS_GID: The Group ID to run the server as (current user's GID)
  • VHS_UID: The User ID to run the server as (current user's UID)
  • VHS_KEY_PATH: The path to the SSH key to use (.ssh/vhs_ed25519)
  • VHS_AUTHORIZED_KEYS_PATH: The path to the authorized keys file (empty, publicly accessible)

Then, simply access VHS from a different machine via ssh:

ssh vhs.example.com < demo.tape > demo.gif

VHS Command Reference

Note

You can view all VHS documentation on the command line with vhs manual.

There are a few basic types of VHS commands:

Output

The Output command allows you to specify the location and file format of the render. You can specify more than one output in a tape file which will render them to the respective locations.

Output out.gif
Output out.mp4
Output out.webm
Output frames/ # a directory of frames as a PNG sequence

Require

The Require command allows you to specify dependencies for your tape file. These are useful to fail early if a required program is missing from the $PATH, and it is certain that the VHS execution will not work as expected.

Require commands must be defined at the top of a tape file, before any non- setting or non-output command.

# A tape file that requires gum and glow to be in the $PATH
Require gum
Require glow

Settings

The Set command allows you to change global aspects of the terminal, such as the font settings, window dimensions, and GIF output location.

Setting must be administered at the top of the tape file. Any setting (except TypingSpeed) applied after a non-setting or non-output command will be ignored.

Set Shell

Set the shell with the Set Shell <shell> command

Set Shell fish

Set Font Size

Set the font size with the Set FontSize <number> command.

Set FontSize 10
Set FontSize 20
Set FontSize 40
Example of setting the font size to 10 pixels Example of setting the font size to 20 pixels Example of setting the font size to 40 pixels

Set Font Family

Set the font family with the Set FontFamily "<font>" command

Set FontFamily "Monoflow"
Example of changing the font family to Monoflow

Set Width

Set the width of the terminal with the Set Width command.

Set Width 300
Example of changing the width of the terminal

Set Height

Set the height of the terminal with the Set Height command.

Set Height 1000
Example of changing the height of the terminal

Set Letter Spacing

Set the spacing between letters (tracking) with the Set LetterSpacing Command.

Set LetterSpacing 20
Example of changing the letter spacing to 20 pixels between characters

Set Line Height

Set the spacing between lines with the Set LineHeight Command.

Set LineHeight 1.8
Example of changing the line height to 1.8

Set Typing Speed

Set TypingSpeed 500ms # 500ms
Set TypingSpeed 1s    # 1s

Set the typing speed of seconds per key press. For example, a typing speed of 0.1 would result in a 0.1s (100ms) delay between each character being typed.

This setting can also be overwritten per command with the @<time> syntax.

Set TypingSpeed 0.1
Type "100ms delay per character"
Type@500ms "500ms delay per character"
Example of using the Type command in VHS

Set Theme

Set the theme of the terminal with the Set Theme command. The theme value should be a JSON string with the base 16 colors and foreground + background.

Set Theme { "name": "Whimsy", "black": "#535178", "red": "#ef6487", "green": "#5eca89", "yellow": "#fdd877", "blue": "#65aef7", "magenta": "#aa7ff0", "cyan": "#43c1be", "white": "#ffffff", "brightBlack": "#535178", "brightRed": "#ef6487", "brightGreen": "#5eca89", "brightYellow": "#fdd877", "brightBlue": "#65aef7", "brightMagenta": "#aa7ff0", "brightCyan": "#43c1be", "brightWhite": "#ffffff", "background": "#29283b", "foreground": "#b3b0d6", "selection": "#3d3c58", "cursor": "#b3b0d6" }

Example of changing the theme to Whimsy

You can also set themes by name:

Set Theme "Catppuccin Frappe"

See the full list by running vhs themes, or in THEMES.md.

Set Padding

Set the padding (in pixels) of the terminal frame with the Set Padding command.

Set Padding 0
Example of setting the padding

Set Margin

Set the margin (in pixels) of the video with the Set Margin command.

Set Margin 60
Set MarginFill "#6B50FF"
Example of setting the margin

Set Window Bar

Set the type of window bar (Colorful, ColorfulRight, Rings, RingsRight) on the terminal window with the Set WindowBar command.

Set WindowBar Colorful
Example of setting the margin

Set Border Radius

Set the border radius (in pixels) of the terminal window with the Set BorderRadius command.

# You'll likely want to add a Margin + MarginFill if you use BorderRadius.
Set Margin 20
Set MarginFill "#674EFF"
Set BorderRadius 10
Example of setting the margin

Set Framerate

Set the rate at which VHS captures frames with the Set Framerate command.

Set Framerate 60

Set Playback Speed

Set the playback speed of the final render.

Set PlaybackSpeed 0.5 # Make output 2 times slower
Set PlaybackSpeed 1.0 # Keep output at normal speed (default)
Set PlaybackSpeed 2.0 # Make output 2 times faster

Set Loop Offset

Set the offset for when the GIF loop should begin. This allows you to make the first frame of the GIF (generally used for previews) more interesting.

Set LoopOffset 5 # Start the GIF at the 5th frame
Set LoopOffset 50% # Start the GIF halfway through

Set Cursor Blink

Set whether the cursor should blink. Enabled by default.

Set CursorBlink false
Example of setting the cursor blink.

Type

Use Type to emulate key presses. That is, you can use Type to script typing in a terminal. Type is handy for both entering commands and interacting with prompts and TUIs in the terminal. The command takes a string argument of the characters to type.

You can set the standard typing speed with Set TypingSpeed and override it in places with a @time argument.

# Type something
Type "Whatever you want"

# Type something really slowly!
Type@500ms "Slow down there, partner."

Escape single and double quotes with backticks.

Type `VAR="Escaped"`
Example of using the Type command in VHS

Keys

Key commands take an optional @time and optional repeat count for repeating the key press every interval of <time>.

Key[@<time>] [count]

Backspace

Press the backspace key with the Backspace command.

Backspace 18
Example of pressing the Backspace key 18 times

Ctrl

You can access the control modifier and send control sequences with the Ctrl command.

Ctrl+R
Example of pressing the Ctrl+R key to reverse search

Enter

Press the enter key with the Enter command.

Enter 2
Example of pressing the Enter key twice

Arrow Keys

Press any of the arrow keys with the Up, Down, Left, Right commands.

Up 2
Down 2
Left
Right
Left
Right
Type "B"
Type "A"
Example of pressing the arrow keys to navigate text

Tab

Enter a tab with the Tab command.

Tab@500ms 2
Example of pressing the tab key twice for autocomplete

Space

Press the space bar with the Space command.

Space 10
Example of pressing the space key

Page Up / Down

Press the Page Up / Down keys with the PageUp or PageDown commands.

PageUp 3
PageDown 5

Sleep

The Sleep command allows you to continue capturing frames without interacting with the terminal. This is useful when you need to wait on something to complete while including it in the recording like a spinner or loading state. The command takes a number argument in seconds.

Sleep 0.5   # 500ms
Sleep 2     # 2s
Sleep 100ms # 100ms
Sleep 1s    # 1s

Hide

The Hide command instructs VHS to stop capturing frames. It's useful to pause a recording to perform hidden commands.

Hide

This command is helpful for performing any setup and cleanup required to record a GIF, such as building the latest version of a binary and removing the binary once the demo is recorded.

Output example.gif

# Setup
Hide
Type "go build -o example . && clear"
Enter
Show

# Recording...
Type 'Running ./example'
...
Enter

# Cleanup
Hide
Type 'rm example'

Show

The Show command instructs VHS to begin capturing frames, again. It's useful after a Hide command to resume frame recording for the output.

Hide
Type "You won't see this being typed."
Show
Type "You will see this being typed."
Example of typing something while hidden

Screenshot

The Screenshot command captures the current frame (png format).

# At any point...
Screenshot examples/screenshot.png

Copy / Paste

The Copy and Paste copy and paste the string from clipboard.

Copy "https://github.com/charmbracelet"
Type "open "
Sleep 500ms
Paste

Source

The source command allows you to execute commands from another tape.

Source config.tape

Continuous Integration

You can hook up VHS to your CI pipeline to keep your GIFs up-to-date with the official VHS GitHub Action:

โš™๏ธ charmbracelet/vhs-action

VHS can also be used for integration testing. Use the .txt or .ascii output to generate golden files. Store these files in a git repository to ensure there are no diffs between runs of the tape file.

Output golden.ascii

Syntax Highlighting

Thereโ€™s a tree-sitter grammar for .tape files available for editors that support syntax highlighting with tree-sitter:

๐ŸŒณ charmbracelet/tree-sitter-vhs

It works great with Neovim, Emacs, and so on!

Feedback

Weโ€™d love to hear your thoughts on this project. Feel free to drop us a note!

License

MIT


Part of Charm.

The Charm logo

Charm็ƒญ็ˆฑๅผ€ๆบ โ€ข Charm loves open source

vhs's People

Contributors

abhijit-hota avatar abhinav avatar alexwforsythe avatar aymanbagabas avatar bashbunni avatar braheezy avatar brumhard avatar caarlos0 avatar delta456 avatar dependabot[bot] avatar elmarsan avatar gaskeo avatar griimick avatar johnstonmatt avatar jolheiser avatar lucapette avatar maaslalani avatar meowgorithm avatar mikelorant avatar muesli avatar og-mrk avatar rcw5 avatar rebzzel avatar rm-dr avatar roland-5 avatar t-chab avatar taigrr avatar tomgroenwoldt avatar tr3mor avatar tranzystorekk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

vhs's Issues

Optimize the way to simulate human typing

The SlowMotion of rod, it's not designed for production usage, it has a lot of side effects:

vhs/vhs.go

Line 71 in 1508f49

browser := rod.New().ControlURL(u).MustConnect().SlowMotion(opts.TypingSpeed)

Better to control the typing speed on vhs itself.

Make output relative to tape file

# in directory called demo
mkdir -p subdir
cat <<EOF > tape.tape
Output ./subdir/tape.gif
Type "Hello, World!"
Enter
Sleep 5s
EOF
cd ..
vhs ./demo/tape.tape

Currently this usage would write to demo/../subdir/tape.gif instead of demo/subdir/tape.gif, because it seems that the output is currently relative to the current directory, not the tape's directory.

I thought this would be useful because I noticed the output of vhs new demo.tape doesn't work "out of the box", because the examples/ directory must exist. If the output could be relative to the tape file, then examples/demo.tape could use ./demo.gif, and the file would also work "out of the box" as the embedded output of vhs new. Making outputs relative to the tape would make results easier to reproducible.

Asciicast Output

This is probably out of scope but would be nice to have asciicast output (Output: out.cast).
I initally wanted gif for the readme and asciicast for the mdbook documentation but then decided against gif due to it's size.
Managed to record it by simply spawning an asciinema recording within tmux.
Keeping size in sync with the other outputs could be challenge but there are also gif, svg,.. generators available for asciicast.
It might even provide an alternative to the chrome dependency (which pushes my cpu to the limit).

Hide
Type "tmux"
Enter
Type "tmux resize-window -x 108 -y 24"
Enter
Type "asciinema rec --overwrite out.cast"
Enter
Sleep 1s
Show

# ...

Hide
Type "exit"
Enter
Show

Example

Source: https://github.com/rsteube/carapace-bin/blob/18ca32ea6093b965e4949a13ccce85f9108ca65c/carapace-bin.tape

Asciicast

asciicast

Gif

GIF output with agg (ignore the missing symbols, didn't get the font right)

agg --font-family "RobotoMono Nerd Font Mono,Noto Color Emoji"  carapace-bin.cast out.gif

out

Svg

SVG output with svg-term-cli (nice output but high load for playing it)

cat carapace-bin.cast | svg-term --out out.svg

out

related #105

Require not working?

I am not sure if i am doing something wrong, but i can not run any binary in vhs context.

Here a simple example:

โฏ vhs --version
vhs version v0.1.1 (b023c66)

โฏ cat demo.tape 
   1   โ”‚ Output ./demo.gif
   2   โ”‚ 
   3   โ”‚ Require deno
   4   โ”‚ 
   5   โ”‚ Type "deno --version" Enter

โฏ vhs < demo.tape 
  3 โ”‚ Require deno
      ^^^^^^^ Invalid command: Require

  3 โ”‚ Require deno
              ^^^^ Invalid command: deno

Error: parse error
Usage:
  vhs <file> [flags]
  vhs [command]

Available Commands:
  help        Help about any command
  new         Create a new tape file with example tape file contents and documentation
  serve       Start the VHS SSH server
  validate    Validate a glob file path and parses all the files to ensure they are valid without running them.

Flags:
  -h, --help      help for vhs
  -v, --version   version for vhs

Use "vhs [command] --help" for more information about a command.

parse error

Is including a built in SSH server a good idea

I apologise beforehand, I know this is a discussion topic and probably belongs on the discord server, but I am currently too busy to follow this up, so I am creating an issue in the hope is starts a discussion among the kind people who do dedicate their time. I noticed this was a relatively new project, and felt it better to raise this now, than keep quiet.

I only came across this project 30min ago, and liked the idea a lot. However the inclusion of a built in SSH server seems very strange to me, and likely to result in all sorts of security vulnerabilities, for example: #52

If I were to guess why this was added it was because of the following two scenarios:

  1. You want to create GIFS on a remote computer where things were setup already for what you wanted to record.
  2. You want to setup VHS on only one computer rather than install it locally, but getting the output files locally.

My alternative solution for scenario 1 is adding the ability to stop/start recording. This way, someone can use ssh to connect to the remote at the beginning of the tabe file, start recording, then stop recording before disconnecting. The advantage here is that VHS does not need to be installed on the remote at all. Another idea is introducing some kind of pre/post hooks concept.

For scenario 2, we already have tools like scp, that should be enough to cover most use cases.

Referencing this famous quote:

Write programs that do one thing and do it well. Write programs to work together. Write programs to handle text streams, because that is a universal interface.

I think VHS looks really cool. But I do not think it should try and do something that other programs already do well, especially things that potentially compromise security. If users need to allow remote SSH access to a machine, they should use something proper developed for that purpose.

I am sorry for being a bit of downer on what probably seemed like a good idea at the time, I just think this particular idea is trying to solve a problem that could be handled in a more simple way.

automatically output alt text

It would be super awesome for normalizing accessibility if vhs was able to automatically generate plaintext descriptions of what's happening in a recording file from the .tape file.

Leakless causes panic on Windows

While trying to test out #55, I ran into an issue where it seems like Defender is finding a dependency and marking it as a virus.

panic: fork/exec C:\Users\MIKEY~1\AppData\Local\Temp\leakless-0c3354cd58f0813bb5b34ddf3a7c16ed\leakless.exe: Operation did not complete successfully because the file contains a virus or potentially unwanted software.

goroutine 1 [running]:
github.com/go-rod/rod/lib/utils.glob..func2({0x1928c60?, 0xc0001126c0?})
        C:/Users/mlombardi/go/pkg/mod/github.com/go-rod/[email protected]/lib/utils/utils.go:60 +0x25
github.com/go-rod/rod/lib/utils.E(...)
        C:/Users/mlombardi/go/pkg/mod/github.com/go-rod/[email protected]/lib/utils/utils.go:66
github.com/go-rod/rod/lib/launcher.(*Launcher).MustLaunch(0xc00039c380?)
        C:/Users/mlombardi/go/pkg/mod/github.com/go-rod/[email protected]/lib/launcher/launcher.go:351 +0x67
main.New()
        C:/code/contrib/vhs/vhs.go:70 +0x27b
main.Evaluate({0xc0007ba6c0, 0x8b}, {0x1ea4ac0, 0xc000006018}, {0x0, 0x0, 0x0?})
        C:/code/contrib/vhs/evaluator.go:33 +0x1c5
main.glob..func1(0x2453ea0?, {0xc000497940, 0x1, 0x1?})
        C:/code/contrib/vhs/main.go:60 +0x16d
github.com/spf13/cobra.(*Command).execute(0x2453ea0, {0xc00005a3d0, 0x1, 0x1})
        C:/Users/mlombardi/go/pkg/mod/github.com/spf13/[email protected]/command.go:916 +0x862
github.com/spf13/cobra.(*Command).ExecuteC(0x2453ea0)
        C:/Users/mlombardi/go/pkg/mod/github.com/spf13/[email protected]/command.go:1040 +0x3b4
github.com/spf13/cobra.(*Command).Execute(...)
        C:/Users/mlombardi/go/pkg/mod/github.com/spf13/[email protected]/command.go:968
main.main()
        C:/code/contrib/vhs/main.go:135 +0x25

Which seems like there's not a good fix (except maybe getting it signed/etc), but there is a workaround:

You can disable leakless, the browser process auto-kill won't work anymore, but you can add a defer on your main function to close the browser manually:

u := launcher.New().Leakless(false).MustLaunch()

browser := rod.New().ControlURL(u).MustConnect()
defer browser.MustClose()

Originally posted by @ysmood in go-rod/rod#739 (comment)

Windows: Bash just throw back if WSL not installed

Hey! Nice tool.
But I found one painful bug on Windows.
This bug is not entirely obvious, because the build is successful:

File: demo.tape
Output .gif demo.gif
Set FontSize 46
Set Width 1200
Set Height 600
Type echo 'Welcome to VHS!'
Sleep 500ms
Enter 1
Sleep 5s
Creating GIF...

But at the output gif we see this:
demo

I found that this is due to the fact that WSL is not installed on my machine, and bash command just throws me back out:
bash

The solution, I think, is obvious to you: run cmd or powershell instead of bash on Windows machines.

vhs/tty.go

Line 34 in cf3919b

"bash", "--login",

I know that you are preparing a feature (Set-Shell) but keep in mind that if the Set-Shell was not explicitly specified in the file, it should be selected automatically depending on the OS.

Thank you!
Have a nice day!

Syntax Highlighting

Apache

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms
Ctrl+C
Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

TSX

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms
Ctrl+C
Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

COBOL

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms
Ctrl+C
Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

Stylus

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms
Ctrl+C
Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

Turtle

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms
Ctrl+C
Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

TCL

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms
Ctrl+C
Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

Verilog

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms
Ctrl+C
Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

JavaScript

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms
Ctrl+C
Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

TypeScript

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms
Ctrl+C
Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

Sass

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms
Ctrl+C
Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

Scss

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms
Ctrl+C
Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

Swift

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms
Ctrl+C
Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

Perl

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms
Ctrl+C
Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

OCaml

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms
Ctrl+C
Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

Pascal

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms
Ctrl+C
Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

Mathematica

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms
Ctrl+C
Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

MySQL

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms
Ctrl+C
Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

NGINX

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms
Ctrl+C
Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms
Ctrl+C
Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

Lisp

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms Ctrl+C Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms Ctrl+C Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms Ctrl+C Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

Haskell

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms Ctrl+C Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms Ctrl+C Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms Ctrl+C Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

C

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms Ctrl+C Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms Ctrl+C Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms Ctrl+C Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

C++

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms Ctrl+C Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms Ctrl+C Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms Ctrl+C Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

ELM

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms Ctrl+C Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms Ctrl+C Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms Ctrl+C Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

CSS

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms Ctrl+C Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms Ctrl+C Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms Ctrl+C Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

Elixir

Output ./renders/demo.gif
Set FontSize 42
Set Height 600
Set Width 1200

Type "Welcome to VHS!"
Sleep 500ms Ctrl+C Sleep 500ms
Type "VHS let's you write terminal GIFs as code."
Sleep 500ms Ctrl+C Sleep 500ms
Type "Let's take it for a spin."
Sleep 500ms Ctrl+C Sleep 500ms

Type "gum spin -s line -- sleep 3" Enter
Sleep 5s

Terminal graphics support

It would be nice if vhs supported terminal graphics (i.e. sixel / kitty / iterm2 graphics protocols).

Package not found on AUR

Hello, thanks for this project!

The project is currently not present in the AUR if I'm not mistaken? The README.md says to install the package vhs from AUR but it is not found. But Carlos seems to have added vhs-bin but its dependencies are not added properly since it depends on ttyd and ffmpeg.

Strange prompt output at the start of the gif

I attempted to put together a gif to demo my crate, woodo: https://github.com/knpwrs/woodo

Here is my script:

Output woodo.gif

Set FontSize 32
Set Width 800
Set Height 1200

Type woodo
Sleep 500ms
Enter
Sleep 500ms
Type sudo woodo
Sleep 500ms
Enter
Type lolnope
Enter
Sleep 5s

Here is the output gif:

woodo

The gif starts out with 11 frames, ending with:

image

Before transitioning to:

image

Allow for multiple files/types in vhs serve

Currently vhs serve forces and returns a gif regardless of Output.
It would be interesting to allow for multiple (or at least choose an alternative), which would then be streamed back as a single archive of files.

From a glance at the code I think it should be possible to check if there are multiple outputs and, if so, create an archive to return instead of the bytes of a single file.


I would be interested in getting my feet a little wet if you find this proposal worth pursuing. ๐Ÿ™‚

Insert blank line

I'd love to be able to insert a blank line between commands but right now if I hit Enter multiple times I get extra > symbols even if I use Hide and Show.

Output demo.gif

Set FontSize 32
Set Width 1200
Set Height 600

Type "echo 'First!'"

Sleep 500ms

Enter

Hide
Enter
Show

Type "echo 'Second!'"

Sleep 500ms

Enter

Sleep 5s

demo

Add select/highlight to shown text

This request might be out of scope for the project overall, and if that's the case please ignore it.

For the same illustrative purposes mentioned in #65, I think it would be nice to have some construct to highlight a specific portion of the text shown at a specific point in the demo. Maybe some regex could be specified and used for this?

Also for highlighting it does not need to be like the usual text highlighting, since its a video maybe the highlighted word could glamorously glow for instance, or blink, or get underlined, or have an aura around it, etc.

It might be something as follows for example to highlight the word "VHS" that was previously typed to the screen:

# Where should we write the GIF?
Output demo.gif

...

# Type a command in the terminal.
Type "echo 'Welcome to VHS!'"

# Pause for dramatic effect...
Sleep 500ms

# Remember the name!
Highlight "VHS"

...

vhs as a GitHub Action

We should think about providing a Github Action that runs vhs as a workflow, spawning a docker image with the required dependencies like ttyd.

Xcode is too outdated

Howdy,

Mac users who have updated to the latest version of mac osx may get the below issue

Error: Your Xcode (14.0.1) is too outdated

I've put together a page for this, you're welcome to include it:

Documentation

It's just a simple case of

sudo rm -rf /Library/Developer/CommandLineTools
sudo xcode-select --install

Thought it would be worth while letting you know about this in case you get some issues around that :)

Example outputs at double speed

When I run the demo tape:

# Where should we write the GIF?
Output demo.gif

# Set up a 1200x600 terminal with 46px font.
Set FontSize 46
Set Width 1200
Set Height 600

# Type a command in the terminal.
Type "echo 'Welcome to VHS!'"

# Pause for dramatic effect...
Sleep 500ms

# Run the command by pressing enter.
Enter

# Admire the output for a bit.
Sleep 5s

I get the following, which seems to be at 2x speed compared to the example in the README:
demo

Wait for command to finish before continuing

Is it possible to wait for a command to finish before you continue typing.

I know I can use Sleep to wait but I might not know how long the command will take and it could be different every time.

demo

ffmpeg error: Thread message queue blocking

When I started trying a script that is a little longer than the "Hello world" scripts, I am getting an ffmpeg failure.

This script:

Output demo.gif

Type "git --help |most"
Enter
Sleep 300ms

Down@300ms 14
Type "q"
Enter
Sleep 5s

fails with this stdout/stderr:

File: demo.tape
Output .gif demo.gif
Type git --help |most
Enter 1
Sleep 300ms
Down 300ms 14
Type q
Enter 1
Sleep 5s
Creating GIF...
ffmpeg version 4.2.7-0ubuntu0.1 Copyright (c) 2000-2022 the FFmpeg developers
  built with gcc 9 (Ubuntu 9.4.0-1ubuntu1~20.04.1)
  configuration: --prefix=/usr --extra-version=0ubuntu0.1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --arch=amd64 --enable-gpl --disable-stripping --enable-avresample --disable-filter=resample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librsvg --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opencl --enable-opengl --enable-sdl2 --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-nvenc --enable-chromaprint --enable-frei0r --enable-libx264 --enable-shared
  libavutil      56. 31.100 / 56. 31.100
  libavcodec     58. 54.100 / 58. 54.100
  libavformat    58. 29.100 / 58. 29.100
  libavdevice    58.  8.100 / 58.  8.100
  libavfilter     7. 57.100 /  7. 57.100
  libavresample   4.  0.  0 /  4.  0.  0
  libswscale      5.  5.100 /  5.  5.100
  libswresample   3.  5.100 /  3.  5.100
  libpostproc    55.  5.100 / 55.  5.100
Input #0, image2, from '/tmp/vhs2543560270/frame-text-%05d.png':
  Duration: 00:00:20.04, start: 0.000000, bitrate: N/A
    Stream #0:0: Video: png, rgba(pc), 1027x425, 25 fps, 25 tbr, 25 tbn, 25 tbc
Input #1, image2, from '/tmp/vhs2543560270/frame-cursor-%05d.png':
  Duration: 00:00:20.04, start: 0.000000, bitrate: N/A
    Stream #1:0: Video: png, rgba(pc), 1027x425, 25 fps, 25 tbr, 25 tbn, 25 tbc
Stream mapping:
  Stream #0:0 (png) -> overlay:main
  Stream #1:0 (png) -> overlay:overlay
  paletteuse -> Stream #0:0 (gif)
Press [q] to stop, [?] for help
[image2 @ 0x55bd9520da80] Thread message queue blocking; consider raising the thread_queue_size option (current value: 8)
[image2 @ 0x55bd9521e4c0] Thread message queue blocking; consider raising the thread_queue_size option (current value: 8)
Output #0, gif, to 'demo.gif':
  Metadata:
    encoder         : Lavf58.29.100
    Stream #0:0: Video: gif, pal8, 1200x600, q=2-31, 200 kb/s, 50 fps, 100 tbn, 50 tbc (default)
    Metadata:
      encoder         : Lavc58.54.100 gif
frame=    0 fps=0.0 q=0.0 size=       0kB time=00:00:00.00 bitrate=N/A speed=   0x    
frame=    0 fps=0.0 q=0.0 size=       0kB time=00:00:00.00 bitrate=N/A speed=   0x    
frame=    0 fps=0.0 q=0.0 size=       0kB time=00:00:00.00 bitrate=N/A speed=   0x    
frame=    0 fps=0.0 q=0.0 size=       0kB time=00:00:00.00 bitrate=N/A speed=   0x    
frame=    0 fps=0.0 q=0.0 size=       0kB time=00:00:00.00 bitrate=N/A speed=   0x    
frame=    0 fps=0.0 q=0.0 size=       0kB time=00:00:00.00 bitrate=N/A speed=   0x    
frame=    0 fps=0.0 q=0.0 size=       0kB time=00:00:00.00 bitrate=N/A speed=   0x    
frame=    0 fps=0.0 q=0.0 size=       0kB time=00:00:00.00 bitrate=N/A speed=   0x    
frame=    0 fps=0.0 q=0.0 size=       0kB time=00:00:00.00 bitrate=N/A speed=   0x    
frame=    0 fps=0.0 q=0.0 size=       0kB time=00:00:00.00 bitrate=N/A speed=   0x    
frame=    0 fps=0.0 q=0.0 size=       0kB time=00:00:00.00 bitrate=N/A speed=   0x    

Note that the same script works if you remove the last Sleep, and also this very same script occasionally works for me.
The little information I could find hints that it might be something to do with an attempt to render real time where it is not necessary.

Also, reducing framerate to 20 helps, but then it only delays the error which occurs again when continuing to create a longer script.

Dependencies not automatically installed

I'm trying to run vhs on Ubuntu 22.04.1 in an LXC container. Image coming from the LXC image repos.

$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 22.04.1 LTS
Release:	22.04
Codename:	jammy

I create a non-root user and install vhs following the instructions in the vhs readme. When I run vhs < demo.tape it starts to process, but then fails with an error saying I'm missing a library. I end up having to install several libraries.

sudo apt install libnss3
sudo apt install libatk1.0-0
sudo apt install libatk-bridge2.0-0
sudo apt install libcups2
sudo apt install libxcomposite1
sudo apt install libxdamage1

docs: include READMEs for the examples

It would be nice to be able to see what an example aims to accomplish when you click into it. I think READMEs would be great for this. Low priority of course, but a nice to have.

Add ability to speed up GIFs

Generally, demos should be faster than normal so we should allow users to set the speed at which their GIFs playback. Implementation wise, we will use ffmpeg's setpts filter.

We have a few options for how to do this on the DSL side.

  1. Use the filename.
Output out.gif      # normal speed
Output out@2x.gif   # double speed
Output out@0.5x.gif # half speed
  1. Set an option.
Set PlaybackSpeed 1.0 # default
Set PlaybackSpeed 2.0 # double speed
Set PlaybackSpeed 0.5 # half speed

Interactive Tape Writing

VHS should have an interactive mode for writing tape files which would look something like:

vhs record

Which would open up a browser terminal and listen to your browser events and write to a tape file. This would make it much easier to write tape files but still keep them easily editable for modifications and tweaks for the final render.

feat: set theme

Discussed in Discord

i think you should be able to do Set Theme TokyoNight

  • include supported themes in the README

issue: `vhs` thinking that I am running on `WSL` when it is not the case

I have downloaded vhs from GitHub Release. I am trying to make some GIF for my examples of the project box-cli-maker with the following code:

# Where should we write the GIF?
Output demo.gif

# Set up a 1200x600 terminal with 46px font.
Set FontSize 46
Set Width 1200
Set Height 600

# Type a command in the terminal.
Type "go run path\git\box-cli-maker\examples\unicode.go"

# Pause for dramatic effect...
Sleep 500ms

# Run the command by pressing enter.
Enter

# Admire the output for a bit.
Sleep 5s

The GIF successfully produces but it throws me an error as it expects go to be installed on Ubuntu when I am not running vhs on WSL.

demo

It seems weird to me how this happened ๐Ÿค”

Add `Set WorkingDirectory` setting

VHS should have the ability to set the working directory of the the tape file so that it may be run from anywhere and produce the same result.

Proposed syntax:

Set WorkingDirectory $HOME/src/vhs/examples/glow

panic: [launcher] Failed to launch the browser

Does this require a browser to function?...

Running the below commands on Ubuntu 20 (headless), ttyd 1.7.2, I get the error below.

$ vhs new demo.tape
$ vhs < demo.tape

Output

panic: [launcher] Failed to launch the browser, the doc might help https://go-rod.github.io/#/compatibility?id=os: /home/vagrant/.cache/rod/browser/chromium-1033860/chrome-linux/chrome: error while loading shared libraries: libgbm.so.1: cannot open shared object file: No such file or directory


goroutine 1 [running]:
github.com/go-rod/rod/lib/utils.glob..func2({0xd784e0?, 0xc00008d8a0?})
	/home/runner/go/pkg/mod/github.com/go-rod/[email protected]/lib/utils/utils.go:60 +0x25
github.com/go-rod/rod/lib/utils.E(...)
	/home/runner/go/pkg/mod/github.com/go-rod/[email protected]/lib/utils/utils.go:66
github.com/go-rod/rod/lib/launcher.(*Launcher).MustLaunch(0xc000189730?)
	/home/runner/go/pkg/mod/github.com/go-rod/[email protected]/lib/launcher/launcher.go:351 +0x67
main.New()
	/home/runner/go/pkg/mod/github.com/charmbracelet/[email protected]/vhs.go:65 +0x22a
main.Evaluate({0xc000490900, 0x85b}, {0x1449ac0, 0xc000012018}, {0x0, 0x0})
	/home/runner/go/pkg/mod/github.com/charmbracelet/[email protected]/evaluator.go:30 +0x1c5
main.glob..func1(0x1a0c340?, {0x1a60598, 0x0, 0x0?})
	/home/runner/go/pkg/mod/github.com/charmbracelet/[email protected]/main.go:61 +0x16a
github.com/spf13/cobra.(*Command).execute(0x1a0c340, {0xc000022070, 0x0, 0x0})
	/home/runner/go/pkg/mod/github.com/spf13/[email protected]/command.go:916 +0x862
github.com/spf13/cobra.(*Command).ExecuteC(0x1a0c340)
	/home/runner/go/pkg/mod/github.com/spf13/[email protected]/command.go:1040 +0x3bd
github.com/spf13/cobra.(*Command).Execute(...)
	/home/runner/go/pkg/mod/github.com/spf13/[email protected]/command.go:968
main.main()
	/home/runner/go/pkg/mod/github.com/charmbracelet/[email protected]/main.go:136 +0x25

Validate `ttyd` version (1.7.2)

Instead of simply checking whether ttyd is present on the $PATH we need to ensure it is using the latest version (1.7.2).

We should keep track of a minimum version for ttyd that we can bump when it has breaking changes.

Add support for playing audio in video outputs

This request might be out of scope for the project overall, and if that's the case please ignore it.

I was wondering if its possible to add a feature where one would be able to start playing a specific audio file after a specific point in the script (for example after displaying a specific command). This might be helpful in cases where one would like to make some educational material or even for documentation purposes, or onboardings.

Constructs to play the audio and to concurrently play or await for the audio once played might be useful to serve the above purposes. "ID" constructs might be added to also micro control the awaiting, to specify for which audio should the player wait if multiple ones where playing at the same time.

Adding such feature might be used as follows if one was to edit them into the sample in the readme:


# Where should we write the GIF?
Output demo.mp4

...

# Pause for dramatic effect...
Sleep 500ms


## plays the specified audio file
PlayAudio two-Inception-style-BRAAMS-followed-in-succession.wav

## awaits for the played audio to finish before continuing with the demo
AwaitAudio 

# Run the command by pressing enter.
Enter

...

Is this feature making the project into a declarative sort of term video editing tool? Yeah kinda, but I was thinking maybe its simple enough that it could serve a specific niche, as using full blown video editors for simple tool demos or documentation purposes can be really frustrating and an overkill.

Wrong number of lines in case of CRLF files

The isNewLine function below checks for \r and \n separately.

vhs/lexer.go

Lines 204 to 207 in ab65901

// isNewLine returns whether a character is a newline.
func isNewLine(ch byte) bool {
return ch == '\n' || ch == '\r'
}

The CRLF line ending is \r\n and hence, the line count doubles as they are counted as two different newlines here:

vhs/lexer.go

Lines 151 to 162 in ab65901

// skipWhitespace skips whitespace characters.
// If it encounters a newline, it increments the line counter to keep track
// of the token's line number.
func (l *Lexer) skipWhitespace() {
for isWhitespace(l.ch) {
if isNewLine(l.ch) {
l.line++
l.column = 0
}
l.readChar()
}
}

This messes up error logs and sometimes gives out of range errors due to this code here:

vhs/main.go

Line 116 in 1a35296

fmt.Println(lines[err.Token.Line-1])

Support multi-line commands

I'd love to see support long commands and choose where the line breaks happen.

$ mamba install -y python=3.11 \
    foo bar baz 

It seems VHS doesn't support this, I tried the following.

# This includes the `\\ \n` characters in the command

Type "mamba install -y python=3.11 \\ \n foo bar baz"
# This gives a 'Error: parse error'

Type "mamba install -y python=3.11 \
foo bar baz"

Add WebP output support

WebP offers the same quality and also animations, but needs way less data. (One GIF of mine was 171 KB and the same as WebP was just 13 KB)

Hidden minimum values for sizes

Using version 0.1.1.

The set Width and Height are positive, but probably not enough for the app to create a gif. The error is misleading.

โžœ ./vhs shrug.tape
File: shrug.tape
Output .gif shrug.gif
Set FontSize 40
Set FontFamily Comic Mono
Set Width 300
Set Height 100
panic: {-32602 Width and height values must be positive, not greater than 10000000 }

goroutine 1 [running]:
github.com/go-rod/rod/lib/utils.glob..func2({0xd9ade0?, 0xc0002c6690?})
        /home/runner/go/pkg/mod/github.com/go-rod/[email protected]/lib/utils/utils.go:60 +0x25
github.com/go-rod/rod.genE.func1({0xc00011eda0?, 0x52fb0d?, 0x530b05?})
        /home/runner/go/pkg/mod/github.com/go-rod/[email protected]/must.go:36 +0x5a
github.com/go-rod/rod.(*Page).MustSetViewport(0xc000160370, 0x1?, 0x1?, 0x2?, 0x0?)
        /home/runner/go/pkg/mod/github.com/go-rod/[email protected]/must.go:307 +0xd8
main.(*VHS).Setup(0xc0004645c0)
        /home/runner/go/pkg/mod/github.com/charmbracelet/[email protected]/vhs.go:91 +0x66
main.Evaluate({0xc0005a8000, 0x863}, {0x1454480, 0xc000132008}, {0x0, 0x0, 0x0?})
        /home/runner/go/pkg/mod/github.com/charmbracelet/[email protected]/evaluator.go:48 +0x3fd
main.glob..func1(0x1a1b320?, {0xc0007356f0, 0x1, 0x1?})
        /home/runner/go/pkg/mod/github.com/charmbracelet/[email protected]/main.go:60 +0x16d
github.com/spf13/cobra.(*Command).execute(0x1a1b320, {0xc000138010, 0x1, 0x1})
        /home/runner/go/pkg/mod/github.com/spf13/[email protected]/command.go:916 +0x862
github.com/spf13/cobra.(*Command).ExecuteC(0x1a1b320)
        /home/runner/go/pkg/mod/github.com/spf13/[email protected]/command.go:1040 +0x3bd
github.com/spf13/cobra.(*Command).Execute(...)
        /home/runner/go/pkg/mod/github.com/spf13/[email protected]/command.go:968
main.main()
        /home/runner/go/pkg/mod/github.com/charmbracelet/[email protected]/main.go:135 +0x25

Working with wider sizes.

โžœ ./vhs shrug.tape
File: shrug.tape
Output .gif shrug.gif
Set FontSize 40
Set FontFamily Comic Mono
Set Width 600
Set Height 200
Type ยฏ\_(ใƒ„)_/ยฏ
Space 10
Sleep 5s
Creating GIF...

`Hide` is not working

I just noticed that Hide is not working on my machine using docker. The README looks like it too:

image

The animation of the command "typing" is not there, but the command itself is. This makes it hard to use for setup, as the whole setup process is visible to the user.

Support additional keys

As discussed in #64 - we need the ability to send more keys. Here is a list borrowed from the AutoHotkey list

  • F1 - F12
  • Enter
  • Escape + Esc
  • Space
  • Tab
  • Backspace
  • Delete + Del
  • Insert + Ins
  • Up / Down / Left / Right
  • End
  • PgUp / PgDn
  • CapsLock
  • ScrollLock
  • NumLock
  • Control + Ctrl **
  • LControl + LCtrl **
  • RControl + RCtrl **
  • Alt **
  • LAlt **
  • RAlt **
  • Shift **
  • LShift **
  • RShift **
  • LWin **
  • RWin **
  • AppsKey **
  • Numpad0 - Numpad9
  • NumpadDot
  • NumpadEnter
  • NumpadMult
  • NumpadDiv
  • NumpadAdd
  • NumpadSub
  • NumpadDel
  • NumpadIns
  • NumpadClear
  • NumpadUp
  • NumpadDown
  • NumpadLeft
  • NumpadRight
  • NumpadHome
  • NumpadEnd
  • NumpadPgUp
  • NumpadPgDn
  • PrintScreen
  • Pause

** These keys may not be needed in vhs context

Unauthorized to pull Docker image

Hey, I wanted to give vhs a spin via Docker, but I am getting the following error when trying to pull the image:

Error response from daemon: Head "https://ghcr.io/v2/charmbracelet/vhs/manifests/latest": unauthorized.

Allow numeric filenames as output (`1.gif`)

Using Output 1.gif in a .tape file breaks with a friendly error.

File: test.tape
  1 โ”‚ Output 1.gif
      ^^^^^^ Expected file path after output

  1 โ”‚ Output 1.gif
             ^^ Invalid command: 1.

  1 โ”‚ Output 1.gif
               ^^^ Invalid command: gif

Error: parse error

Perhaps it could be allowed?
(Of course, adding an alpha character to the path works.)

Support rendering offset

Support starting the rendering a few seconds into the recording, then looping around from the end to the beginning.

The idea behind this is that it generates a more reasonable thumbnail when sharing the gif on various platforms. The viewer won't typically notice this if the output is running as a continuous loop.

Set GIF quality

All the GIFs I'm getting seem a little blurry. Is there any way to set the quality?

Output doc/source/images/operator-install.gif

Set FontSize 18
Set Width 1200
Set Height 200
Set Padding 20
Set TypingSpeed 25ms

Type "helm install --wait --repo https://helm.dask.org --create-namespace -n dask-operator --generate-name dask-kubernetes-operator"

Sleep 500ms

Enter

Sleep 5s

Enter
Type "kubectl get daskclusters"

Sleep 500ms

Enter

Sleep 2s

Enter
Type "kubectl get pods -A -l app.kubernetes.io/name=dask-kubernetes-operator"

Sleep 500ms

Enter

Sleep 5s

operator-install

VHS Logomarks

Heya! I create templates for the the popular IDE Replit and saw VHS in my feed and thought, "Wow, this stuff is really cool!" so I went ahead and made a template for VHS on Replit.

I was wondering if you had (or could make) a square logo for VHS when you get a chance, it would help with making the template icon neater and would be a nice addition to VHS' branding overall :).

Fail early when missing required programs

VHS depends on the host computer to execute terminal commands (including installed software).
It is possible for a tape that runs on one computer (with a program installed) will generate incorrectly on a different computer (with the program missing).

For example, the following tape:

Output glow.gif

Type "glow"
Enter
Sleep 5

requires that glow is installed on the machine that VHS is running on.
On a machine that doesn't have glow installed the recording will still succeed but the output will show "bash: command not found: glow".

There are a few different solutions we can employ:

  1. Introduce a Require command

The Require command would act as a pre-check to ensure the computer running VHS has the correct dependencies for the .tape file in the path. For example:

Output glow.gif

Require glow,foo,bar

Type "glow"
Enter
Sleep 5

Now, when running VHS the running instance will exit as soon as it realizes that glow is not installed on the machine. More importantly, it will not override the existing glow.gif if it exists. The downside is that the tape writer must enumerate the dependencies at the top of the file manually (which might not be a big deal).

  1. Introduce a set -e-like option

The set -e option instructs bash to immediately exit if any command [1] has a non-zero exit status.

We can introduce a command / setting that tells VHS to stop recording if any of the commands has a non-zero exit code, as would be in the case of a program not being installed.

Output glow.gif

Set Exit ... # OnFailure

Type "glow"
Enter
Sleep 5

This tape would exit after Type "glow" + Enter commands, when it realizes that the command failed. The downside of this is that sometimes demos may involve exiting with non-zero status codes to be part of the GIF.

  1. Introduce an Exec command
Output glow.gif

Exec "glow"

Sleep 5

This Exec command would act as a Require <program> + Type <program"> + Enter. The downside of this approach is that it is harder to modify aspects of the typing speed and delay between typing the last letter and pressing enter, though these can be solved with something such as Exec@200ms "glow" 1s (but maybe that is a bit complicated syntax)

`Alt` + `Shift` modifiers

VHS should support Alt and Shift modifiers for keypress:

Ctrl+Shift+O
Ctrl+Alt+L
Ctrl+Alt+Shift+P
Ctrl+Shift+Alt+C

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.