home
NOTE: Under Construction - I'm in the middle of upgrading my site and lots of stuff is kinda broken. Please forgive the mess.

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.

October 2023

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()"
═══ § ═══