<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Angel L. Valdés</title>
    <description>The latest articles on DEV Community by Angel L. Valdés (@alvaldesdev).</description>
    <link>https://dev.to/alvaldesdev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4213667%2F9074e796-51d7-4aa3-8b3c-b5467f243124.jpg</url>
      <title>DEV Community: Angel L. Valdés</title>
      <link>https://dev.to/alvaldesdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alvaldesdev"/>
    <language>en</language>
    <item>
      <title>JUMP: keyboard-first, even outside the browser</title>
      <dc:creator>Angel L. Valdés</dc:creator>
      <pubDate>Wed, 28 Oct 2026 18:22:25 +0000</pubDate>
      <link>https://dev.to/alvaldesdev/jump-keyboard-first-even-outside-the-browser-22km</link>
      <guid>https://dev.to/alvaldesdev/jump-keyboard-first-even-outside-the-browser-22km</guid>
      <description>&lt;p&gt;In the browser I barely touch the mouse. I use &lt;a href="https://github.com/brookhong/Surfingkeys" rel="noopener noreferrer"&gt;Surfingkeys&lt;/a&gt;, an extension that puts a label on every link and button: I type the label and that's the click. Over the years I built my whole setup around the keyboard, and Vim's &lt;code&gt;h j k l&lt;/code&gt; keys are muscle memory by now.&lt;/p&gt;

&lt;p&gt;The trouble started when I left the browser. To click a single button in Finder, System Settings or any other app, I had to let go of the keyboard, reach for the mouse and come back. A few milliseconds, sure. But it happens many times a day and it adds up, and I'm a productivity nerd: those milliseconds matter to me.&lt;/p&gt;

&lt;p&gt;That's where &lt;a href="https://alvaldes.github.io/jump/" rel="noopener noreferrer"&gt;JUMP&lt;/a&gt; came from.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;JUMP is a macOS menu bar app. Press a shortcut and every clickable element in the focused window gets a short label. Type the label and JUMP clicks it.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
⌘ ⇧ Space
&lt;/td&gt;
&lt;td&gt;Hints&lt;/td&gt;
&lt;td&gt;Labels every clickable element. Type a label to click it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
⌘ ⇧ J
&lt;/td&gt;
&lt;td&gt;Scroll&lt;/td&gt;
&lt;td&gt;Scrolls the window Vim-style: &lt;code&gt;h j k l&lt;/code&gt;, &lt;code&gt;d u&lt;/code&gt;, &lt;code&gt;gg G&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
⌘ ⇧ F
&lt;/td&gt;
&lt;td&gt;Search&lt;/td&gt;
&lt;td&gt;Finds visible text, ignoring case and accents.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Hold a modifier while typing the label to pick the action: ⇧ right-clicks, ⌘ double-clicks and ⌥ only moves the pointer. All three shortcuts and the modifiers can be changed in Settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsexl1xpn049brwc4ihcl.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsexl1xpn049brwc4ihcl.webp" alt="JUMP's settings: how the labels look and how clicks behave" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I didn't use something that already existed
&lt;/h2&gt;

&lt;p&gt;The first thing I did was look around. I found two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/nchudleigh/vimac" rel="noopener noreferrer"&gt;Vimac&lt;/a&gt;, open source, but untouched since December 2022.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.homerow.app" rel="noopener noreferrer"&gt;Homerow&lt;/a&gt;, its commercial successor: it works well, but it's paid and closed source.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neither felt right, so I thought: why not build my own tool, the way I like it, free and open source? I'm an open-source fan. I like the community that grows around a project and the idea of sharing what you learn. JUMP starts from Vimac's code, which is GPL-3.0, modernizes it for macOS 26 and ships under the same license.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works under the hood
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The Accessibility API.&lt;/strong&gt; macOS exposes every app's interface as a tree of elements: buttons, fields, links. JUMP walks that tree in the focused window, keeps what can be acted on and draws the labels on top. That's why it needs the Accessibility permission.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OCR as a fallback.&lt;/strong&gt; Some windows don't expose their text through Accessibility, like terminals or PDFs. For those, JUMP can recognize the text on screen with the Vision framework. It's off by default, asks for the Screen Recording permission only when you turn it on, and the captures never leave memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No Mac App Store.&lt;/strong&gt; Store apps run in a sandbox, and an app that reads and drives other apps through Accessibility can't work from inside one. JUMP ships through GitHub Releases instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Engineering decisions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The core is its own package.&lt;/strong&gt; The logic lives in &lt;code&gt;JumpKit&lt;/code&gt;, a Swift package that's tested without opening the app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;swift &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--package-path&lt;/span&gt; Packages/JumpKit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Testing something that depends on other windows and system permissions is hard. Keeping the core apart from the UI makes it testable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The permission that vanished on every build.&lt;/strong&gt; macOS ties the Accessibility permission to the app's code signature. With the default ad-hoc signature, the signature changes on every build and the permission is gone. The fix was signing with my own development certificate, and the release script refuses to publish an ad-hoc signed app: users would lose the permission on every update.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A release you can't get wrong.&lt;/strong&gt; The same script also rejects uncommitted changes and untagged commits. I'd rather it fail than ship something I can't reproduce.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;JUMP needs macOS 26 or later. Download it from &lt;a href="https://alvaldes.github.io/jump/" rel="noopener noreferrer"&gt;its website&lt;/a&gt; or from &lt;a href="https://github.com/alvaldes/jump" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It isn't notarized by Apple yet, so macOS blocks it the first time. The &lt;a href="https://github.com/alvaldes/jump#install" rel="noopener noreferrer"&gt;README&lt;/a&gt; walks you through opening it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you use it and something breaks, or you have an idea to make it better, &lt;a href="https://github.com/alvaldes/jump/issues" rel="noopener noreferrer"&gt;open an issue&lt;/a&gt; in the repository. That's what open source is for.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://www.alvaldes.dev/en/blog/introducing-jump/" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>macos</category>
      <category>opensource</category>
      <category>swift</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
