Skip to content

RGGH/type-sim

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

type-sim

Rust

Reads a text file and "types" its contents into whatever window currently has focus — nvim in a terminal, VSCode, anything — using real simulated keystrokes (X11 XTEST extension). Built for recording clean coding-demo footage without having to actually type on camera.

How it works

It does not paste text or fake a terminal. It sends real key events system-wide via X11, the same way xdotool type does. That means:

  • It works with any focused window (nvim, VSCode, a browser, whatever).
  • You must manually click/focus the target window before it starts typing — there's a countdown for that.
  • It only works on X11, not Wayland. Linux Mint Cinnamon uses X11 by default, so you should be fine. Run echo $XDG_SESSION_TYPE to confirm — it should print x11.

Setup (Linux Mint)

Install a Rust toolchain (skip if you already have one):

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"

Install the X11 dev headers enigo links against:

sudo apt update
sudo apt install libxdo-dev

Build:

cd type-sim
cargo build --release

The binary will be at target/release/type-sim. Optionally install it somewhere on your $PATH:

cp target/release/type-sim ~/.local/bin/

Usage

type-sim path/to/script.txt

You'll get a 5-second countdown — click into nvim (make sure you're in INSERT mode) or VSCode before it ends. Then it types.

Options

type-sim <FILE> [OPTIONS]

  --min-delay <ms>        min pause between keystrokes   (default 25)
  --max-delay <ms>        max pause between keystrokes   (default 90)
  --newline-pause <ms>    extra pause after each Enter    (default 250)
  --start-delay <secs>    countdown before typing begins  (default 5)
  --hesitate-chance <0-1> chance of a random long pause    (default 0.02)
  --hesitate-ms <ms>      length of that long pause        (default 600)

Example — faster, twitchier typing with no hesitation:

type-sim demo.rs --min-delay 10 --max-delay 40 --hesitate-chance 0

Tips for nvim

  • Open nvim, press i to enter INSERT mode, then run type-sim and switch focus back during the countdown.
  • If your file has leading whitespace/indentation you want nvim to reproduce literally rather than re-indent, run nvim with :set noautoindent noexpandtab first, or the auto-indent will double up whitespace as it retypes each \n.

Tips for VSCode

  • Click into the editor pane during the countdown.
  • Turn off VSCode's "Format on Type" / auto-closing brackets if you want an exact character-for-character replay, since VSCode will otherwise insert matching brackets/quotes itself as it "sees" you type them.

Notes

  • This sends genuine input events, so anything with focus receives them — don't let the countdown end while some other window (like a password field) is focused.
  • Unicode characters are supported via Key::Unicode.

would this code work on M$ windo$e and Mac O$

Short answer: yes, the code itself should work as-is on both — enigo is cross-platform and picks the right backend automatically based on target OS. You don't need to change main.rs or Cargo.toml. What changes is the setup step, not the code.

Windows

# install Rust: https://rustup.rs (just run the installer)
cd type-sim
cargo build --release
.\target\release\type-sim.exe demo.rs
  • No libxdo-dev needed — that was Linux/X11-only. On Windows, enigo uses the native SendInput API.
  • Should work out of the box with VSCode. For a Windows terminal + nvim, same deal — focus it during the countdown.
  • Watch out for elevated (Run as Administrator) target apps — synthetic input from a non-elevated process generally can't reach an elevated window (Windows' UIPI blocks it). Run your terminal and VSCode/nvim at the same privilege level.

macOS

# install Rust: https://rustup.rs
cd type-sim
cargo build --release
./target/release/type-sim demo.rs
  • No libxdo-dev equivalent needed — enigo uses Core Graphics events (CGEvent) on macOS.
  • You will need to grant Accessibility permission the first time: System Settings → Privacy & Security → Accessibility → enable it for your terminal app (Terminal.app, iTerm2, etc.) or the compiled binary itself. Without this, keystrokes silently won't send.
  • Some sandboxed/hardened apps ignore synthetic events even with permission granted — VSCode and terminal-based nvim should be fine.

What doesn't change

Cargo.toml, main.rs, all the CLI flags, the whole workflow (countdown → focus window → it types) — identical on every platform. The only platform-specific bit is the one-time OS permission/dependency dance above.

messes up the /// and indents in Neovim:

This isn't actually a keybinding problem — it's an editor autoindent/autocomment problem. Here's what's happening:

The cause:

type-sim sends real, individual keystrokes (Key::Return, Key::Unicode(ch), etc.) — not a single paste event. Your target editor (looks like Neovim, based on the NORMAL/libversion statusline) doesn't know this is "pasted" text. So every feature that normally helps a human typing kicks in and fights your file's own formatting:

  1. Autoindent: when you press Enter, Neovim auto-inserts indentation matching the previous line. Then type-sim also types the leading spaces/tabs that are already in your source file. Those two sources of indentation stack, so each line gets progressively more indented than the last — exactly the staircase effect you're seeing between lines 6→5→4→3→2→1 in image 2.

  2. Comment continuation (formatoptions containing r/o): after a line starting with /// or //!, Neovim auto-inserts a new //! (or /// ) prefix on the next line for you. Then type-sim types the file's own //!//// prefix right after that — giving you the doubled/mangled comment markers and words getting jammed together (the//! decision-making).

The fix — put Neovim in "dumb" mode before the demo runs:

In Neovim, run this before starting type-sim (or add it to a scratch config you source just for demos):

:set paste

paste mode disables autoindent, smartindent, cindent, and the comment-continuation formatting all at once — so raw keystrokes land exactly as sent, no side effects. Turn it back off after with :set nopaste.

If you want it scriptable rather than manual, you could have countdown() in type-sim also send the necessary keys to toggle paste mode itself, e.g. type :set paste<CR>i before typing the body and <Esc>:set nopaste<CR> after — but doing it manually once per demo is simpler and less fragile.

If you also demo in VSCode, the equivalent culprits are editor.autoIndent and editor.autoClosingBrackets/editor.formatOnType — worth turning those off for the same reason.

About

Reads a text file and "types" its contents into whatever window

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages