Repository navigation
Keystroke 1.4.0
Released September 11, 2026.
Extensions now live inside Keystroke, off until you turn them on, and the
first two are in the box: Timer, which now rings and counts down in the bar,
and Translate. Every typed command explains itself as you type it. And there
is a usage guide, one page with a screenshot for every feature, reachable from
Settings → Learn Keystroke.
New
- Extensions ship with Keystroke. Third-party extensions are one folder
each under extensions/, contributed through pull
requests the way the Raycast extensions repository works, and reach every
user with the next update. The marketplace index, the hosted catalog and
the detached install, update and remove jobs are gone. Every extension
is off until you turn it on, and one that is off is never compiled:
installing or updating Keystroke runs no third-party code. Turning one on
asks for confirmation and says what was checked, that it runs in your
shell with your permissions, and that reading its code first is
recommended; confirming loads it at once, turning it off destroys its
service. One that fails to load is listed with the reason under Needs
attention. A folder in~/.local/share/keystroke/extensions/is picked
up on the next open, for an extension you are writing. A declared setup
script runs only from a Run setup row, in a visible terminal.
bin/keystroke check-extensionsis the review gate and runs on pull
requests through GitHub Actions. - Commands explain themselves. A provider declares its typed triggers
(prefix, arguments, examples) inextension.jsonor on the provider
object, and the palette does the rest for all of them. Typetr,timer
or:and the line under the search field names the action and the
argument you are on (Translate · to: a language code or name), the
arguments still to type appear after the caret as[to] <text>and vanish
as you fill them, and a sheen sweeps over a recognised prefix.Tabtypes
a command's prefix from its name (transl→tr) and, while typing,
moves to the next argument./lists every command with its usage;
/trfilters it. Each extension's screen starts with its usage and
examples you can run with↵, and turning an extension on says what to
type. Every prefix can be renamed in the provider's settings (tmfor
timer). A typed command is exclusive: only its owner answers, which
generalises the old~special case. Emoji, Files, Timer and Translate
declare theirs. - Translate. Google Translate without an account, ported from the
Raycast google-translate extension:tr bonjourdetects the language and
translates into your first target,tr fr good morningpicks a target by
code or name,bonjour to englishneeds no prefix. The translation is the
answer row (↵copies,Ctrl+↵pastes into the app you came from), the
reverse translation follows as a check, then every other target you
configured. An editor view shows all of them for longer text, with
dictation; the selection, when there is one as the palette opens, is
offered at the root; a picker on the Translate screen chooses the target
languages. Requests go out 350 ms after you stop typing, are cached for the
session, and back off for a minute on HTTP 429. Off until you turn it on. - Timer rings and counts down in the bar. When a timer ends a sound
plays (chime by default; drop, alarm, a file of your own, or off), and the
soonest countdown sits in the bar right after the Keystroke button from the
moment it starts, with+1for every timer behind it; pressing it opens
the Timers screen. Both are settings. This is the answer to the request
for "a proper alarm and countdown, not a lame notification" (#6). Under
the hood the host gainedhost.setBarItemandhost.providerSettings, so
any extension can put an item next to the menu button. - Learn Keystroke. Settings gains a Learn Keystroke row (type
learnorguideanywhere) that opens the new usage guide,
evindor.github.io/keystroke/guide:
every feature on one page with a screenshot, a few lines on how it works
and one thing to try, ending with commands and extensions.
Improvements
- The bar button goes first on the left when installed from a checkout
(bin/keystroke installpassesleft --index 0);omarchy plugin add --enablestill asks for a section, andomarchy bar move evindor.keystroke left --index 0moves it afterwards. Tabis a space while typing a command: aftertr frit moves to the
text, aftertimer 10mto the name, and does nothing on the last argument.- The website has a usage guide with 35 fresh screenshots, all rendered
offscreen from the real palette in the current theme by
tools/showcase/offscreen.py, without touching the desktop.
Fixes
- Direct routes into a disabled provider (
omarchy menu toggle appswith
Applications off) return to the root with a status line instead of an
empty palette (#3). - The bar widget had no size. It anchored its row to its own height
while taking its height from the row, so the bar lost the menu button and
never showed the countdown. It is sized from its buttons now, and the
extensions check asserts it against the realBarWidget.qml.
Update
omarchy plugin update evindor.keystroke
omarchy restart shellThe restart is needed because Quickshell 0.3.1 keeps cached QML until the
shell restarts. Requires Omarchy 4.0.2 or later.
If you installed the Timer or Calpad extension the old way (as a separate
Omarchy plugin), it is no longer picked up from ~/.config/omarchy/plugins.
Timer ships with Keystroke: open Extensions → Timer and turn it on; its
old settings under the reverse-DNS id were deliberately not migrated. Calpad
still ships from its own repository until it is ported; a symlink to its
checkout in ~/.local/share/keystroke/extensions/calpad keeps it working
meanwhile. omarchy plugin remove <id> removes the old plugin copies.
For extension authors
manifest.json becomes extension.json in a folder under extensions/
whose name is the id; the injected property is extension, not manifest.
Declare your prefixes under commands and read ctx.command.rest instead of
parsing the prefix yourself. CONTRIBUTING.md
and docs/providers.md are current; extensions/timer
is the reference.
Validation: 162 QML tests, 48 time-zone cases, the application, file,
catalog, matching worker, engine and session checks, the palette matching,
shortcut, route, motion, dictation, extensions (now against the real bar
widget) and commands checks, the Translate check with a fake curl, the
extension review checks for Timer and Translate, the voice, clipboard, Codex
and hotkey checks, plugin validation, site checks and qmllint passed
offscreen. See the verification record.