This file keeps the old name, but the trie-based chord subsystem is gone. The current feature is a
9-key magic expansion system driven by the Magic Keys table in README.md.
Each physical magic key checks the previously emitted key and dispatches from there.
- Runtime entry point: generated
case MAGIC_*blocks inqmk/generated.c - Dispatch source:
switch (get_last_keycode()) - Source of truth:
README.mdMagic Keystable - Follow-up suffix logic:
process_suffix()in qmk/keymap.c
The basic pattern is:
- Type a normal key.
- Press one of the nine magic keys.
- Firmware looks at the previous key and emits the cell content for that
(prev, magic)pair.
Examples from the current table:
bthenmagic_d->becausetthenmagic_d->tionnthenmagic_d->qu,thenmagic_b->and
Rows are preceding keys. Columns are the nine physical magic keys.
Single-character cells tap that keycode directly.
a+magic_cwith celle->aet+magic_hwith cell,->t,
These do not enable suffix chaining. Pressing the same magic again replays just the emitted letter or symbol, without repeating any setup backspace from the original expansion.
Bare multi-character cells are treated as word expansions.
- Generator emits the text plus a trailing space.
- Firmware enables the suffix state machine with the word's final character.
Examples:
b+magic_dwith cellbecauseemitsecausebecause the previousbis strippedj+magic_dwith celljustemitsustspc+magic_dwith celltheemitsthe
Quoted strings are literal output. They do not get an automatic trailing space and they do not enable suffix state.
Rules:
- Previous key is a letter and the string starts with that letter: strip the prefix
- Previous key is a letter and the string does not start with that letter: send backspace, then the full string
- Previous key is not a letter: append the string as-is
Examples:
n+magic_dwith cell"qu"-> backspace +qu,+magic_bwith cell" and "-> appendand.+magic_dwith cell"./"-> append./
Bracketed names call a named handler instead of sending raw text.
Current handler:
[dotSpc]-> backspace, send., enable one-shot shift, clear suffix state
When a bare-word magic fires, qmk/keymap.c enters suffix mode. The next magic press can rewrite
the trailing space into a suffix or punctuation.
Supported suffix follow-ups:
magic_g->ed, except after a word ending inewhere it becomesdmagic_e->lymagic_b->smagic_d->n'tingkey ->ing, with a second backspace when the prior letter is a vowelmagic_i->.and one-shot shiftmagic_h->,
Examples:
b+magic_d->because, thenmagic_b->becausesj+magic_d->just, thenmagic_g->justedspc+magic_d->the, thenmagic_i->the.with shift armedu+magic_i->update, thenmagic_g->updated
Suffix mode exits on any non-suffix key.
Magic keys also become the new remembered key. That means a magic can dispatch from a previous magic, not only from letters or punctuation.
Bare-word and literal-string magics still set the remembered key to the magic trigger:
set_last_keycode(MAGIC_X);That allows a dedicated magic row in the table for cross-magic expansions and punctuation flows.
The generator logic lives in src/main/kotlin/generate.kt.
Relevant rules:
addMagic()reads theMagictable fromREADME.mdaddMagicEntry()decides whether a cell becomes tap,SEND_STRING, backspace+string, or a named handler- Bare words get
set_suffix_state(...) - Quoted strings stay literal
Generated output lands in qmk/generated.c. Do not edit that file directly.
To change magic behavior:
- Edit the
Magic Keystable inREADME.md. - Regenerate with
mise run generate. - Flash with
mise run flashif needed.
The table in README.md is the source of truth.