Note: This site is currently "Under construction". I'm migrating to a new version of my site building software. Lots of things are in a state of disrepair as a result (for example, footnote links aren't working). It's all part of the process of building in public. Most things should still be readable though.

Exmaples Of Sending Simulated Keystrokes To Mac Apps With OSA/JXA JavaScript

One of my favorite little hacks is being able to send key presses to mac apps from apple's javascript functions. I used it to put together a script to automate text for making gifs.

Here's a basic example:

Code

// File: hello-world.js

  const sys = new Application('System Events')
  const app = new Application('Stickies')

  app.activate()
  sys.keystroke('hello, world')

That script opens the built-in Stickeis app and types 'hello, world' when you run it with:

Code

osascript -l JavaScript hello-world.js

Sending letters with a modifiers (i.e. shift, command, option, control, or function) is done like this:

Code

// File: upper-case.js

const sys = new Application('System Events')
const app = new Application('Stickies')

app.activate()
sys.keystroke('this is upper case', { using: ['shift down'] })

Code

osascript -l JavaScript upper-case.js

Add multiple modifiers to the array if you need more than one. For example, `Command` + `Shift` + `c` opens the colors pallet in the Stickes app. The scripting approach to that is:

Code

// File: color-pallet.js

  const sys = new Application('System Events')
  const app = new Application('Stickies')

  app.activate()

  sys.keystroke('t', { using: [
      'command down',
      'option down'
  ] })

Code

osascript -l JavaScript color-pallet.js

(TKTKTKT - Test examples of option, control, and the function key to verify they work as expected.)

There are some keys (like the arrow keys) that don't work with the `keystroke()` method. The way to get to them is to use the internal code for the key with the `keyCode()` method.

For example, if you still have the test sticky note from above open, this will move the cursor back one chracters.

Code

// File: arrow-press.js

  const sys = new Application('System Events')
  const app = new Application('Stickies')

  app.activate()
  sys.keyCode(123)

Code

osascript -l JavaScript arrow-press.js

For example, this mimics pressing the F3/Mission Control key.

Code

const sys = new Application('System Events')
  sys.keyCode(160)

** NOTES

- TKTKTKTK Note the difference between Enter and Return (and how that doesn't matter most of the time)

- TKTKTK Note the differences between the function keys that have two sets of codes and check to see if the other F keys do as well.

- TKTKTKT Note the media keys that do and don't work.

- TKTKTK Do examples with "command down" type stuff on keyCode() calls to check functionality.

- Some things don't work the way you'd expect (or don't work at all). For example, I can't do the equivelent of `Command` + `Shift` + `3` to take a screenshot. Seems like that should work, but it doesn't. You'll likely run into other things like that were you need to find another approach to the automation.

- These examples are a bit contrived. I don't know why you'd want to open the color pallet in the Stickies app, but that's not the point. The goal is to show how the tools work.

- While these examples are all JavaScript you can also use AppleScript to do the same thing.

- TODO: Put in AppleScript examples or make a version of this page with ApplieScript

- Sometimes the speed of the keystrokes comes into play and goes too fast if you're swithing things around. Adding a delay can help alleviate that.

Code

The code blocks below are only for testing the
  code in the main body of the post.

Code

cat <<- "EOC" > /private/tmp/emacs_tmp_1.txt
  <<source_hello_world>>
  EOC
  osascript -l JavaScript /private/tmp/emacs_tmp_1.txt
            
  osascript -l JavaScript -e "Application('Emacs').activate()"

Code

cat <<- "EOC" > /private/tmp/emacs_tmp_2.txt
  <<source_upper_case>>
  EOC
  osascript -l JavaScript /private/tmp/emacs_tmp_2.txt
            
  osascript -l JavaScript -e "Application('Emacs').activate()"

Code

cat <<- "EOC" > /private/tmp/emacs_tmp_3.txt
  <<source_color_pallet>>
  EOC
  osascript -l JavaScript /private/tmp/emacs_tmp_3.txt
  sleep 0.2
  osascript -l JavaScript -e "Application('Emacs').activate()"

Code

cat <<- "EOC" > /private/tmp/emacs_tmp.txt
  <<source_arrow_press>>
  EOC
  osascript -l JavaScript /private/tmp/emacs_tmp.txt
  sleep 0.2
  osascript -l JavaScript -e "Application('Emacs').activate()"