MoreRSS

site iconJonas HietalaModify

A writer, developer and wannabe code monkey.
Please copy the RSS to your reader, or quickly subscribe to:

Inoreader Feedly Follow Feedbin Local Reader

Rss preview of Blog of Jonas Hietala

I'll give up Neovim when you pry it from my cold, dead hands

2025-02-18 08:00:00

I recently came upon a horror story where a developer was forced to switch editor from Neovim to Cursor and I felt I had to write a little to cleanse myself of the disgust I felt.

Two different ways of approaching an editor

I think that there’s two opposing ways of thinking about the tool that is an editor:

  1. Refuse to personalize anything and only use the basic features

    An editor is a simple tool I use to get the job done.

  2. Get stuck in configuration hell and spend tons of time tweaking minor things

    An editor is a highly personalized tool that works the way I want.

These are the extreme ends of the spectrum to make a point and most developers will fall somewhere in between.

It’s not a static proposition; I’ve had periods in my life where I’ve used the same Vim configuration for years and other times I’ve spent more time rewriting my Neovim config than doing useful things.

Freedom of choice is important

Freedom of choice is more to be treasured than any possession earth can give.

David O. McKay

Some developers want zero configuration while others want to configure their editor so it’s just right. Either way is fine and I’ve met excellent developers from both sides.

But removing the power of choice is a horrible idea as you’re forcing developers to work in a way they’re not comfortable with, not productive with, or simply don’t like. You’re bound to make some of the developers miserable or see them leave (usually the best ones who can easily find another job).

To explain how important an editor might be to some people, I give you this story about Stephen Hendry—one of the most successful Snooker players ever—and how important his cue was to him:

In all the years I’ve been playing I’ve never considered changing my cue. It was the first cue I ever bought, aged 13, picked from a cabinet in a Dunfermline snooker centre just because I liked the Rex Williams signature on it.

I saved £40 to buy it. It’s a cheap bit of wood and it’s been the butt of other players’ jokes for ages. Alex Higgins said it was ‘only good for holdin’ up f*g tomatoes!’

But I insist on sticking with it. And I’ve won a lot of silverware, including seven World Championship trophies, with it. It’s a one-piece which I carry in a wooden, leather-bound case that’s much more expensive than the cue it houses.

But in 2003, at Glasgow airport after a flight from Bangkok, it emerges through the rubber flaps on the carousel and even at twenty yards I can see that both case and cue are broken. Snapped almost clean in two, the whole thing now resembling some form of shepherd’s crook. The cue comes to where I’m standing, and I pick it up, the broken end dangling down forlornly.

I could weep. Instead, I laugh.

‘Well,’ I say to my stunned-looking friend John, ‘that’s my career over.’

Small improvements leads to large long-term gains

Kaizen isn’t about massive overhauls or overnight success. Instead, it focuses on small, continuous improvements that add up to significant long-term gains.

I firmly believe that even small improvements are worth it as they add up over time (also see compound interest and how it relates to financial investments).

An editor is a great example where even small improvements may have a big effect for the simple reason that you spend so much time in your editor. I’ve spent hours almost every day inside (neo)vim since I started using it 15+ years ago.

Even simple things like quickly changing text inside brackets (ci[) instead of selecting text with your mouse might save hundreds of hours during a programming career—and that’s just one example.

Naturally, as a developer you can find small but worthwhile improvements in other areas too, for instance:

  • Learning the programming languages and libraries you use a little better

  • Customizing your keyboard and keyboard layout

    This is more for comfort and health than speed but that makes it even more important, not less.

  • Increasing your typing speed

    Some people dismiss typing speed as they say they’re limited by their thinking, not typing. But the benefit of typing faster (and more fluidly) isn’t really the overall time spent typing vs thinking; it’s so you can continue thinking with as little interruption as possible.

    On some level you want to reduce the time typing in this chain:

    think… edit, think… edit, think…

    It’s also why the Vim way of editing is so good—it’s based on making small edits and to return quickly to normal (thinking) mode.

Some people ask how can you afford to spend time practicing Vim commands or to configure your editor as it takes away time from work?

But I ask you: with a programming career of several decades and tens of thousands of hours to spend in front of your computer, how can you afford not to?

Neovim is versatile

During the years I’ve done different things:

  • Studied at the University and then started working as a developer.
  • Used many different programming languages and paradigms.
  • Changed domains many times (games, grammars & language design, graphics programming, web development, …)
  • Switched keyboard and keyboard layout multiple times.
  • Been blogging and wrote a book.

The one constant through all of this has been Neovim. Neovim may not have the best language specific integrations but it does everything well and the benefit of having the same setup for everything you do is not to be underestimated. It pairs nicely with the idea of adding up small improvements over time; every small improvement that I add to my Neovim workflow will stay with me no matter what I work with.

Neovim is easily extensible

Neovim: a Personalized Development Environment

I’ve always felt that Vimscript is the worst part of Vim. Maybe that’s a weird statement as the scriptability of Vim is one if it’s strengths; and to be fair, simple things are very nice:

nnoremap j gj
set expandtab

But writing complex things in Vimscript is simply not a great experience.

One of the major benefits of Neovim is the addition of Lua as a first-class scripting language. Yes, Lua isn’t perfect and it’s often too verbose but it’s so much better than Vimscript. Lua is the main reason that the Neovim plugin ecosystem is currently a lot more vibrant than in Vim.

Making it easier to write plugins is of course a boon, but the real benefit is in how it makes it even easier to make more complex customization for yourself. Just plop down some Lua in the configuration files you already have and you’re done. (Emacs worked this out to an even greater extent decades ago.)

One way I use this customizability is to help me when I’m blogging:

  • A custom picker to search and display blog posts
  • Autocomplete tags, post urls, and more
  • LSP-like functionality to display warnings and jump between posts
  • Use treesitter to convert links, autoformat lists, and jump around

Maybe you don’t need to create something this big but even small things such as disabling autoformat for certain file types in specific folders can be incredibly useful.

Approachability should not be underestimated.

Neovim won’t go anywhere

The old is expected to stay longer than the young in proportion to their age.

Nassim Nicholas Taleb, “Antifragile”

The last big benefit with Neovim I’ll highlight—and why I feel fine with investing even more time into Neovim—is that Neovim will most likely continue to exist and thrive for years if not decades to come.

While Vim has—after an impressive 30 years of development—recently entered maintenance mode, activity in Neovim has steadily increased since the fork from Vim more than a decade ago. The amount of high quality plugins, interest in Google trends, and GitHub activity have all been trending upwards. Neovim was also the most desired editor according to the latest Stackoverflow developer survey and the overall buzz and excitement in the community is at an all-time high.

With the self-reinforced behavior and benefits of investing into a versatile and flexible editor with a huge plugin ecosystem such as Neovim I see no reason for the trend to taper off anytime soon.

Neovim will probably never be as popular as something like VSCode but as an open source project backed by excited developers, Neovim will probably be around long after VSCode has been discontinued for The Next Big Thing.

Securing my partner's digital life

2025-01-20 08:00:00

I’ve been with Veronica for over a decade now and I think I’m starting to know her fairly well. Yet she still manages to surprise me. For instance, a couple of weeks ago she came and asked me about email security:

I worry that my email password is too weak. Can you help me change email address and make it secure?

It was completely unexpected—but I’m all for it.

The action plan

All heroic journeys needs a plan; here’s mine:

  1. Buy her a domain name (her .com surname was available).
  2. Migrate her email to Fastmail.
  3. Setup Bitwarden as a password manager.
  4. Use a YubiKey to secure the important services.

Why a domain?

If you ever want (or need) to change email providers it’s very nice to have your own domain. For instance, Veronica has a hotmail.com address but she can’t bring that with her if she moves to Fastmail.

Worse, what if she gets locked out of her Outlook account for some reason? It might happen if you forget your password, someone breaks into your account, or even by accident.

In almost all cases, your email is your key to the rest of your digital life. The email address is your username and to reset your password you use your email. If you lose access to your email you lose everything.

When you control your domain, you can point the domain to a new email provider and continue with your life.

Why pay for email?

One of the first things Veronica told me when I proposed that she’d change providers was that she didn’t want to pay. It’s a common sentiment online that email must be cheap (or even free).

I don’t think that email is the area where cost should be the most significant factor. As I argued for in why you should own your email’s domain, your email is your most important digital asset. If email is so important, why try to be cheap about it? You should spend your money on the important things and shouldn’t spend money on the unimportant things.

Paying for email gives you a couple of nice things:

  • Human support.

    It’s all too easy to get shafted by algorithms where you might get banned because you triggered some edge case (such as resetting your password outside your usual IP address).

  • Ability to use your own domain.

    Having a custom domain is a paid feature at most email providers.

  • A long-term viable business.

    How do you run an email company if you don’t charge for it?
    (You sell out your users or you close your business.)

Why a password manager?

The best thing you can do security wise is to adopt a password manager. Then you don’t have to try to remember dozens of passwords (leading to easy-to-remember and duplicate passwords) and can focus on remembering a single (stronger) password, confident that the password manager will remember all the rest.

“Putting all your passwords in one basket” is a concern of course but I think the pros outweigh the cons.

Why a YubiKey?

To take digital security to the next level you should use two-factor authentication (2FA). 2FA is an extra “thing” in addition to your password you need to be able to login. It could be a code sent to your phone over SMS (insecure), to your email (slightly better), a code from a 2FA app on your phone such as Aegis Authenticator (good), or from a hardware token (most secure).

It’s easy to think that I went with a YubiKey because it’s the most secure option; but the biggest reason is that a YubiKey is more convenient than a 2FA app.

With a 2FA app you have to whip out your phone, open the 2FA app, locate the correct site, and then copy the TOTP code into the website (quickly, before the code changes). It’s honestly not that convenient, even for someone like me who’s used this setup for years.

With a YubiKey you plug it into a USB port and press it when it flashes. Or on the phone you can use NFC. NFC is slightly more annoying compared to plugging it in as you need to move/hold it in a specific spot, yet it’s still preferable to having to jump between apps on the phone.

There are hardware keys other than YubiKey of course. I’ve used YubiKey for years and have a good experience. Don’t fix what isn’t broken.

The setup

Here’s a few quick notes on how I setup her new accounts:

Password management with Bitwarden

The first thing we did was setup Bitwarden as the password manager for her. I chose the family plan so I can handle the billing.

To give her access I installed Bitwarden as:

  1. An app on her phone
  2. A browser plugin on her computer

I gave her a YubiKey and registered it with Bitwarden for additional security. As a backup I also registered my own YubiKeys on her account; if she loses her key we still have others she can use.

Although it was a bit confusing for her I think she appreciates not having to remember a dozen different passwords and can simply remember one (stronger) password. We can also share passwords easily via Bitwarden (for news papers, Spotify, etc).

The YubiKey itself is very user friendly and she hasn’t run into any usability issues.

Email on Fastmail

With the core security up and running the next step was to change her email:

  1. Gave her an email address on Fastmail with her own domain (<firstname>@<lastname>.com).

    She has a basic account that I manage (there’s a Duo plan that I couldn’t migrate to at this time).

  2. I secured the account with our YubiKeys and a generated password stored in Bitwarden.

  3. We bolstered the security of her old Hotmail account by generating a new password and registering our YubiKeys.

  4. Forward all email from her old Hotmail address to her new address.

With this done she has a secure email account with an email address that she owns.

As is proper she’s been changing her contact information and changing email address in her other services. It’s a slow process but I can’t be too critical—I still have a few services that use my old Gmail address even though I migrated to my own domain more than a decade ago.

Notes on recovery and redundancy

It’s great to worry about weak phishing, weak passwords, and getting hacked. But for most people the much bigger risk is to forget your password or lose your second factor auth, and get locked out that way.

To reduce the risk of losing access to her accounts we have:

  • Multiple YubiKeys for all accounts.
  • The recovery codes for all accounts are written down and secured.
  • My own accounts can recover her Bitwarden and Fastmail accounts via their built-in recovery functionality.

Perfect is the enemy of good

Some go further than we’ve done here, others do less, and I think that’s fine. It’s important to not compare yourself with others too much; even small security measures makes a big difference in practice.

Not doing anything at all because you feel overwhelmed is worse than doing something, even something simple as making sure you’re using a strong password for your email account.

First impressions of Ghostty

2025-01-06 08:00:00

There are two conflicting forces in play in setting up your computer environment:

  1. It’s worthwhile to pursue small improvements in frequently used tools.
  2. It’s not productive to get stuck tweaking your setup endlessly.

It’s common to find people get stuck at the extreme ends of the spectrum; some programmers refuse to configure or learn their tools at all, while others get stuck re-configuring their setups constantly without any productivity gains to show for it.

Finding a balance can be tricky. With regards to terminals I’ve been using alacritty for many years. It gets the job done but I don’t know if I’m missing out on anything? I’ve been meaning to look at alternatives like wezterm and kitty but I never got far enough to try them out.

On one hand it’s just a terminal, what difference could it make?
On the other hand, I spend countless of hours every day inside a terminal so even a small improvement should pay off in the long run.

Enter Ghostty, a terminal so hyped up it made me drop any useful things I was working on and see what the fuzz was about. I don’t quite get why people hype up a terminal of all things but here we are.

Ghostty didn’t revolutionize my setup or anything but I admit that Ghostty is quite nice and it has replaced alacritty as my terminal.

I just want a blank canvas without any decorations

One of the big selling points of Ghostty is its native platform integration. It’s supposed to integrate well with your window manager so it looks the same and gives you some extra functionality… But I don’t know why I should care—I just want a big square without decorations of any kind.

You’re supposed to to be able to simply turn off any window decorations:

window-decoration = false

At the moment there’s a bug that requires you set some weird GTK settings to fully remove the borders:

gtk-titlebar = false
gtk-adwaita = false

It’s unfortunate as I haven’t done any GKT configuration on my machine (I use XMonad as my window manager and I don’t have any window decorations anywhere).

Cursor invert

cursor-invert-fg-bg = true

In alacritty I’ve had the cursor invert the background and foreground and you can do that in Ghostty too.

I ran into an issue where it interferes with indent-blankline.nvim making the cursor very hard to spot in indents (taking the color of the indent guides, which is by design low contrast with the background).

Annoying but it gave me the shove I needed to try out different plugins to see if the problem persisted. I ended up with (an even nicer) setup using snacks.nvim that doesn’t hide the cursor:

Left: indent-blankline.nvim (cursor barely visible)
Right: snacks.nvim (cursor visible and it highlights scope).

Minimum contrast

Unreadable ls output is a staple of the excellent Linux UX. It might look like this:

Super annoying.

You can of course configure the ls output colors but that’s just for one program and it won’t automatically follow when you ssh to another server.

Ghostty’s minimum-contrast option ensures that the text and background always has enough contrast to be visible:

minimum-contrast = 1.05

Most excellent.

Hide cursor while typing

mouse-hide-while-typing = true

A small quality-of-life feature is the ability to hide the cursor when typing. I didn’t know I needed this in my life.

Consistent font sizing between desktop and laptop

With alacritty I have an annoying problem where I need to use a very different font size on my laptop and my desktop (8 and 12). This wasn’t always the case and I think something may have changed in alacritty but I’m not sure.

Ghostty doesn’t have this problem and I can now use the same font settings across my machines ( font-size = 16 ).

Ligature support

The issue for adding ligatures to alacritty was closed eight years ago and even though I wanted to try ligatures I couldn’t be bothered to “run a low quality fork”.

Ghostty seems like the opposite of “low quality” and it renders Iosevka’s ligatures very well:

My configured ligatures of Iosevka, rendered in Ghostty.

Overall I feel that the font rendering in Ghostty is a little better than in alacritty, although that might be recency bias. I’m still undecided on ligatures but I love that I don’t have to feel limited by the terminal.

I use a custom Iosevka build with these Ghostty settings:

font-family = IosevkaTreeLig Nerd Font
font-style = Medium
font-style-bold = Bold
font-style-italic = Medium Italic
font-style-bold-italic = Bold Italic
font-size = 16

Colorscheme

While Ghostty has an absolutely excellent theme selector with a bunch of included themes (ghostty +list-themes) melange-nvim wasn’t included, so I had to configure the colorscheme myself. It was fairly straightforward even though the palette = 0= syntax was a bit surprising:

# The dark variant of melange
background = #292522
foreground = #ECE1D7
palette = 0=#867462
palette = 1=#D47766
palette = 2=#85B695
palette = 3=#EBC06D
palette = 4=#A3A9CE
palette = 5=#CF9BC2
palette = 6=#89B3B6
palette = 7=#ECE1D7
palette = 8=#34302C
palette = 9=#BD8183
palette = 10=#78997A
palette = 11=#E49B5D
palette = 12=#7F91B2
palette = 13=#B380B0
palette = 14=#7B9695
palette = 15=#C1A78E
# I think it's nice to colorize the selection too
selection-background = #403a36
selection-foreground = #c1a78e

I’m happy with Ghostty

In the end Ghostty has improved my setup and I’m happy I took time to try it out. It took a little more time than “just launch it” but it absolutely wasn’t a big deal. The reward was a few pleasant improvements that have improved my life a little.

And perhaps most important of all: I’m now an alpha Nerd that uses a terminal written in Zig.

2024 in review

2025-01-05 08:00:00

It’s time for my 15th yearly review.

Nerdy things I enjoyed

  • I read a lot of fantasy books this year!

    My favorite new series were The Kingkiller Chronicle, Gentlemen Bastards series, and The Stormlight Archive.

  • Customizing Neovim was fun and rewarding.

    It’s amazing I got anything productive done this year…

  • I really enjoy working with Rust in my own hobby projects.

  • Types are coming to Elixir and I’m loving it.

    (I recently migrated some small projects to v1.18 and found a bunch of errors.)

  • The Gleam programming language shows a lot of promise.

    My one gripe is the pain of manually encoding/decoding JSON (even with the various libraries). Compared to for example Elixir dynamic encoding or Rust’s #[derive(Deserialize)] it just feels so bad that I’ve avoided Gleam for some projects. Shame on me?

  • CSS is alive and better than ever.

Things I accomplished

Tentative plans/goals/wishes for 2025

  • For some reason the idea of writing a fantasy novel got stuck in my head.

    I had a stint where I listened to dozens of hours of advice for aspiring writers and started planning a series. The excitement tapered off a bit during the Christmas holidays and I don’t know if this was just a temporary sidetrack or if it’s something I’ll actually end up doing.

  • Design a one-handed keyboard layout.

    Again, this was just something my brain got stuck thinking about and I’m not sure if it’s just a fleeting idea or something I need to do so I can stop thinking about it. (Sometimes just a little planning plus solving the most difficult problems are enough—I don’t have to finish all the crazy/dumb ideas I get for my mind to consider them “done”.)

  • Complete my second 3D printer.

    There’s no point in having a single printer; what if I break it and I can no longer print replacement parts for it?

  • Develop my home automation system more.

    I’ve got a ton of things I’d like to improve (or play around with). For instance, yesterday I received the Home Assistant Voice Preview Edition that I hope works as well as advertised.

A type checking error in Elixir 1.18

2024-12-30 08:00:00

Although I’m a big Elixir fan, the lack of static typing has always been my biggest annoyance (and why I think Gleam is so cool). I think static typing helps catch bugs earlier and in an automated way, leading to less buggy software and saves time in the long run.

To my great joy Elixir is working on a new type system that will hopefully give us the early type checking errors I’ve been craving for. The system has been rolled out in steps since v1.17 and when I migrated to v1.18 I found my first type checking warning that I wanted to highlight.

Comparison with structs

This is the offending code with the corresponding warning:

def get_surrounding_events_as_dt(events, now = %DateTime{}) do
time = DateTime.to_time(now)
next_i = Enum.find_index(events, fn {_, event_time} -> time < event_time end) || 0
warning: comparison with structs found:
time
given types:
dynamic(%Time{})
where "event_time" was given the type:
# type: dynamic()
# from: lib/haex/sun.ex
{_, event_time}
where "time" was given the type:
# type: dynamic(%Time{})
# from: lib/haex/sun.ex:88:10
time = DateTime.to_time(now)
Comparison operators (>, =,
Comparing with a struct won't give meaningful results.
Structs that can be compared typically define a compare/2 function within their modules that
can be used for semantic comparison.
typing violation found at:
│ next_i = Enum.find_index(events, fn {_, event_time} -> time
│ ~

(The type checker cannot yet resolve event_time to the Time struct, leaving it as dynamic() in the text above.)

The issue here as that < isn’t overloaded for the Time struct (like it would be in for instance Rust) and will instead perform structural comparison.

You should use Time.before? instead of < (and DateTime.before for DateTime etc).

My hope for the future

I’ve always disliked matching against atoms in Elixir as it’s so easy to make a mistake, for example like this:

case Supervisor.start_child(supervisor, child_spec) do
{:error, {:already_stated, pid}} ->
Logger.info("Got pid: #{inspect(pid)}")

(There’s a missing r in :already_stated.)

At the moment this doesn’t produce an error but I really hope we’ll reach this point sooner rather than later as I make these kinds of mistakes all the time. I think I catch most of these with tests but I’m sure some slip through.

I hope this isn’t that far away as the v1.18 type checker manages to catch a simpler case like this:

def num_to_descr(num) do
case num do
1 -> :one
2 -> :two
_ -> :many
end
end
def print(num) do
case num_to_descr(num) do
:zero -> IO.puts("zero")
x -> IO.puts("Other: #{x}")
end
end
warning: the following clause will never match:
:zero
because it attempts to match on the result of:
num_to_descr(num)
which has type:
dynamic(:many or :one or :two)
typing violation found at:
41 │ :zero -> IO.puts("zero")
│ ~~~~~~~~~~~~~~~~~~~~~~~~

The current Cybershard layout

2024-11-26 08:00:00

This is the keyboard layout I’m using for my custom keyboard that I generated, printed, and hand-wired. It’s a minimalistic keyboard of 35 keys and features an integrated trackball on the right-hand side.

The keyboard layout started out as a direct copy of the T-34 keyboard layout, with some small modifications from the 34-key keyboard T-34 was designed for:

  • An extra two keys on the left-hand side, in the ring and middle finger columns.
  • Removed the second thumb key on the right-hand side.
  • Accommodation for mouse keys and trackball handling.

While the layout has diverged since then, the design philosophy from original T-34 post still holds true and I recommend it as it may explain why the layout looks like it does.

Legend

I use quite a number of special features for the keys and I’ve tried to color code according to the above legend.

Layers

Layers are super important for smaller keyboards and I use them a ton.

Base

  • The repeat key is used to output the last pressed key.
  • I shift keys using auto shift (see long press) and CAPSWORD.
  • The letters Z and Q, together with a bunch of other keys, are on combos.
  • The keys for F2, F12 and FUN are just extras and aren’t in a comfortable enough position to warrant anything more common.

Swedish overlay

Swedish overlay.

When I want to write Swedish I activate this layer that replaces ()_ with åäö, or I use combos from any layer.

Symbols

Symbols layer.

I typically use combos to output symbols (following the same layout pattern as the symbols layer). The symbols layer is mostly used to roll symbol pairs like {} or #[.

Some common symbol sequences (like ->, != , or ```) exists as combos and others as long press.

Numbers

Numbers layer. The browner keys (like J) turn off NUMWORD.

While I can activate the number layer persistently (using leader sequences) I typically use combos for single digits (like 0), or NUMWORD for larger numbers (like 1984).

NUMWORD makes the number layer smart, so it will deactivate when certain keys are pressed. It’s used to type numbers in text or code and for relative movement in Neovim, where 17J would move 17 lines down and then turn off the number layer. Jumping directly to a line in Neovim with 12G is also made convenient.

If I want to enter the layer without it turning off I can either use leader sequences to activate it persistently or hold the NUMWORD combo (hold both thumbs). The layer won’t release until both thumb keys are released, so Space can be tapped with the left thumb without leaving the number layer.

@u is there to easily activate macros in Neovim. For example 7@u in the number layer would run the u macro 7 times and then turn off NUMWORD.

Function keys

Function keys.
  • The function keys follow the same layout as the numbers layer.
  • The trackball DPI can be lowered and raised at runtime.

Navigation layers

Navigation layer.
  • Keys with Gui are used in xmonad.

    • Gui + W, Gui + E and Gui + R are used to switch between monitor.
    • Gui + J/Gui + K to switch windows.
    • Gui + ,/Gui + . to add/remove windows from the “master” area.
  • This layer is commonly used in conjunction with the trackball, giving access to mouse keys.

    For instance, Shift + Left Mouse can be used to drag, Ctrl + A to select, and Ctrl + C to copy.

  • Tabbing is for switching browser tabs and the Back / Fwd mouse buttons goes backwards and forwards in history.

  • Ctrl + arrow is used to switch windows in Neovim.

  • Ctrl + D and Ctrl + U are used in Neovim to scroll half a screen (compared to PgUp/PgDn that scrolls an entire screen).

  • The workspace layer is triggered by first holding Space, and then holding the right thumb key (WNAV).

Arrows on the left-hand side.

This layer exists for the rare occasions I want to navigate using only the left hand. The keys on the right side is there just because and are rarely, if ever, used.

Workspace layer

Workspace layer. All keys have an implicit Gui modifier.

This is used for all window and workspace management in xmonad. Some common operations are also on the navigation layer. Auto shift works and can used to send a window to another workspace (Gui + Shift + 2).

Windows layer

Windows layer. All keys have an implicit Alt modifier.

This is purely to enable window switching using Alt-Tab and Ctrl-Alt-Tab, without releasing Alt.

Mouse layer

The mouse layer for right-hand only usage.

I generally prefer to use the navigation layer, where the mouse buttons are on the left side, but in some cases I’d like to only use my right hand.

Special characters

Specials layer.

The dead keys add diacritic to any letter. For example, to get é you can use the dead key ´ then e, and the operating system will merge them together. (É also exists as a combo.)

Modifiers

Modifiers.

I typically use long press for shift and combos for other modifiers, this layer is a fallback for when those aren’t enough (the layer is mostly used for Right Alt).

Gaming layer

A simple gaming layer.

I’m not a heavy gamer by any means but this layer allows me to play Core Keeper with my kid. By necessity this layer disables auto shift.

Combos

Combos is another fantastic tool that I (ab)use a lot. Simply put it allows you to press multiple keys at once and acts as another key—very useful for smaller layouts.

Neighbour combos

These combos are made by keys next to each other, either horizontally (pressed with two fingers) or vertically (pressed with one finger in the middle of two keys).

2-key neighboring combos.
  • Some combos have a separate hold behaviour; for instance holding Escape activates the symbols layer, allowing me to output [] easily.
  • vsplit splits a window vertically in Neovim and hsplit splits it horizontally, and Close Window closes a window in Neovim (<C-w>q).
  • Clear resets all states; sets the base layer, releases modifiers, stops CAPSWORD and NUMWORD, and clears other persistent states.
  • Ctrl + Shift + M is the shortcut to mute/unmute in Teams.
3-key horizontal combos.
  • SWE activates the Swedish layer, and if prefixed with ()_ it will replace that with åäö and vice versa. So for example if I typed hall( I would press SWE to get hallå, with the Swedish layer activated.
  • Ctrl W is used to close tabs in Firefox.
A 4-key horizontal combo.
  • Save Neovim is a 4-key combo that saves the buffer in Neovim.

Split combos

These split combos uses the ring and index finger.

Combos for the quotes " and '.
Combos don’t have to be on the same row, these angled combos are fairly comfortable.
One-shot Alt on the left combo and holding the right combo activates the windows layer (with implicit Alt).
Leader key on the bottom row.

Combos over keyboard halfs

  • Tapping T + A once activates CAPSWORD, tapping again makes it persistent (CAPS LOCK), and a third tap to deactivate CAPS LOCK.
  • Tapping Space + E activates NUMWORD and tapping them again activates the number layer persistently.

The repeat key works with the above, making them easier to double-tap.

  • Swaps _ and -.
  • Access the specials layer with the ring fingers.

Thumb key combos

I have a bunch of 2-key thumb + key combos:

Combos using Space and another key.
Combos using E and another key.

The logic here is that same-side thumb + key = symbol and opposite-side thumb + key = digit, following the placements of the numbers, symbols and swedish layers. They’re used if I want to just type a single character, without having to activate a layer first.

Combos using FUN and another key.

I have similar combos for the function keys.

QMK boot

The keycode QMK_BOOT enters boot mode for the microcontroller connected via USB, making it easy to update the keymap on the keyboard. These two 5-key combos (one for each half) are almost impossible to trigger accidentally while being easily accessible.

Additional features

While layers and combos are the two main features I use, QMK has a lot of other nifty features (and you roll your own implementation of them too).

Long press

Most keys have a different behaviour when tapped compared to a long press. Most commonly I use this to produce shifted keys (called auto shift). So tapping the A key will output a as normal and if it it A will appear instead.

There are a bunch of special cases as well (many on top of combos):

Tap Long press
_ < > / \ # Double, e.g __
" ' = ` 0 . Triple, e.g """
| & = Double with spaces, e.g  || 
!  !=  (with spaces)
? {:?}
# {:#?}
% %{}
( [ { Close and move cursor between
@ @u (paired with qu combo for Neovim macro execution)

Leader sequences

I use the combo l + ) as the leader key. This will wait for a sequence of key presses (in contrast to combos where keys must be pressed at the same time). I use this with mnemonics for rarely used outputs:

Leader sequence Action
l + ), c Caps lock
l + ), s Swedish input in Linux (mapped in xmonad)
l + ), t, n Toggle Number layer
l + ), t, s Toggle Symbols layer
l + ), t, f Toggle Function layer
l + ), t, c Toggle Caps lock escape swap
l + ), Esc Ctrl Shift Escape

CAPSWORD

CAPSWORD is a “smart caps lock”. It works like a regular caps lock, except it automatically turns off after certain keys are typed (most commonly space).

It will not turn off on letters, numbers, _ - Backspace and the Repeat keys.

NUMWORD

NUMWORD is a “smart layer”. It’s similar to CAPSWORD, except it activates and then turns off the numbers layer instead of caps lock.

It will not turn off on these keys: 0-9 % / + * - _ . , : = x Backspace Enter and the Repeat keys.

Repeat key

The repeat key simply repeats the previous key. So to type fall I can type f a l Repeat, using four different fingers instead of pressing l twice. It can also repeat things like Ctrl-c or Delete, and unlike regular keys that use auto shift the Repeat key can be held.

Trackball

The trackball is normally configured to move the mouse as a regular trackball.

There are different modes that alters the behavior of the trackball:

  1. Lower the DPI when Space or _ are held (the mouse moves slower when the navigation layer or the mouse layer are active).
  2. Raise the DPI when the MOD combo is held (the mouse moves faster).
  3. Scroll instead of moving the mouse when the SYM combo is held.

More info

  • Read the T-34 series for the design process and motivations of my other keyboard layout (it’s the same layout with minor refinements and additions).

  • See the post Building my ultimate keyboard for how I designed and built the keyboard I’m using this layout with.

  • For implementation details and the most up-to-date reference check out the layout’s QMK source code.


  • Copied the T-34 layout and adapted it for the new keyboard by adding a mouse layer, remove the shortcut layer, and changed the activation of the specials layer.

  • Moved - to an angled combo, moving the WIN key to the top row, and move % to the home-row and ! to the bottom row.

  • Reworked the mouse layer and use a more advanced triggering mechanism to be more explicit about when the layer is turned on and off.

  • Added double-tap functionality to NUMWORD and CAPSWORD combos.

    1. Moved - back to it’s original position and placed % on the angled combo.
    2. Experimental functionality to swap - and _ for the languages that use kebab-case.
    3. Removed the mouse layer and placed mouse buttons on the navigation layer.
  • Reworked the navigation layer to keep the original positions for PgUp, PgDn, and Tabs. To allow this I moved the mouse click to index finger and demoted the up/down to the top row.

    1. Reworked the navigation layer to be more focused on two hands on the board. The idea is to use better placed keys on the right-hand side instead of cramming in too much on the left. Moved PgUp and Home to the right side and it’s enough to use the arrow keys on the right. (The secondary navigation layer exists to enable arrow keys on the left side.)

    2. Added mouse button combos on the right-hand side so in a pinch I can operate the mouse with one hand.

    1. Removed mouse button combos from the right-side and place them on a separate layer instead (to keep the shift combo in the regular place).

    2. Add Gui + ,/. for xmonad master window management to the navigation layer, moving Ctrl + A to the home-row once again.

  • Added the gaming layer.