Lost Inputs

Posted

A UI shouldn’t lose your inputs.

This may seem obvious, but I often see applications that behave incorrectly if your inputs come too fast. One common situation where I see interfaces falter is keyboard shortcuts that bring you to a text input. Keypresses after the shortcut should always land in that input.

Examples

App Launchers

A common example is an app launcher. On GNOME I tap Super and type to search my apps. If I type Superfoo it should search for “foo” every time. GNOME works correctly here. No matter how overloaded and laggy my system is, and no matter how fast I type, the search is accurately captured. KDE’s KRunner does not pass this test. Oftentimes the first few characters of my search will land in the previously-focused application. I think this is because it is implemented as a separate process, and it can take a moment to start up and capture keyboard focus. macOS’s Spotlight also fails, although the failure window seems much smaller than KDE’s and required significant system load to hit.

Clipboard

Ghostty handles paste events asynchronously. Typing Ctrl+Shift+vEnter can trigger the Enter first, then the pasted text lands on the next line.

Web Apps

This occurs on web apps too. I’ll pick on Linear as they appear to have thought about this to some degree. Some shortcuts like a1 will reliably self-assign the ticket no matter how fast I type or how locked up my system is. Even complex interactions like mbCtrl+VEnter will mark the current issue as blocked by the issue in my clipboard. That is how it should work!

However, they aren’t perfect. /foo can have various behaviours depending on how fast you type. It should search for “foo” every time, but I have seen a few failures:

Mouse Events

This can happen for mouse events too. For example Firefox has an excellent feature where after clicking the close button on a tab, the next tab’s close button will always land in the exact same spot. Even if the tabs were previously compressed because too many are open, they will wait until you move your mouse away to expand again. This way you can just keep clicking in the same location to close many tabs with ease.

But the tab closing animation ruins this! There is a gap in time where the old tab hasn’t gone away and clicks just disappear. So you can’t simply click 4 times to close 4 tabs, it may close fewer than you intended. You instead need to wait until the previous click is fully processed before performing the next click. This isn’t the worst failure mode but can make workflows where you close lots of tabs unnecessarily slow and annoying. Ctrl+W doesn’t appear to suffer this issue and always closes the right number of tabs.

This reminds me of the classic moving click target UX failure. This is the opposite of that: instead of the target jumping away as you try to click, the predictable target is instead not ready yet.

Difficulty

The main difficulty in implementing this correctly is that you need some sort of global input routing that buffers events until the application is ready for them. This can be quite difficult in browser applications as there is no way to “defer” input events other than completely blocking the main thread. So if follow-up keypresses are required you need to synchronously adjust focus, which may be difficult in your framework. This typically also precludes any form of lazy loading. Alternatively you would need to implement your own focus and input event handling logic, which is almost certainly a bad idea.

Asynchronous work is a complicated case in general, because buffering inputs for an extended period is confusing to the user. What if that work can fail? Do you drop the buffered input events? Or feed it to the previously focused input control? Do you need some sort of timeout? Luckily I find that this case is very rare. (Although the clipboard example above is one of these cases.)

If you have any sort of UI automation, this is easy to test: feed the keys in as a single batch and be sure that your app handles them properly!

Why?

Typing speed shouldn’t affect functionality! Keyboard shortcuts are designed for people to go fast, nothing will harm your productivity more than familiar keyboard shortcuts doing something unexpected! (Bonus points if those inputs aren’t lost, but trigger a completely different action like “delete”.) It is easy to say that it is a 2 millisecond gap and no one will hit a window that small, but that gap may be much larger on a low-end machine swapping like crazy. Or maybe my local LLM is hogging my GPU for a second or two. Don’t let fingers race your UI, just close the gap.