No description
Find a file
2026-06-24 16:43:35 +08:00
dicts Phase 3: dictionary loading pipeline + binary embedding 2026-06-22 21:26:23 +08:00
dts Implement Phase 2: real platform shims + behavior driver + steno key defines 2026-06-22 21:02:14 +08:00
include Cleanup: remove dead files from rapid iteration 2026-06-23 00:29:06 +08:00
src Fix linker: disable ortho cache, stub CancelAllStenoKeys, stub flash, fix C linkage for raise_zmk_keycode_state_changed 2026-06-23 00:06:57 +08:00
tools/dict_compiler Phase 3b: working dict compiler + dict download script 2026-06-22 21:52:57 +08:00
zephyr Initial skeleton: ZMK module structure + Javelin submodule + stub shims 2026-06-22 20:49:01 +08:00
.gitignore Auto-download + compile Plover dict during build 2026-06-23 00:47:15 +08:00
CMakeLists.txt Fix dict embedding: scope source property to app target directory 2026-06-24 15:43:00 +08:00
Kconfig Fix: Plover + Lapwing are additive, not exclusive 2026-06-23 00:58:17 +08:00
README.md Add recommendations for 1MB flash boards 2026-06-24 16:43:35 +08:00

zmk-javelin-steno

ZMK module that integrates the Javelin steno engine for on-board stenography on ZMK keyboards.

Status

Dropped — my hardware (nice_nano_v2, 1MB flash) can't fit any dictionary. Build passes on boards with sufficient flash but has not been hardware tested. Contributions welcome.

Flash Requirements

Total flash usage (firmware + engine + dict) on a corne split (central half):

Dictionary Total flash
Plover + Lapwing 3.78 MB
Plover only 2.53 MB
Lapwing only 2.08 MB
No dict (engine only) 534 KB

Making It Work on 1MB Flash

If you want to use this on boards with limited flash (nice_nano_v2, etc.), some options:

  • Optimize dictionary format — Javelin uses JSC4 hash maps (~2-3MB compiled). A trie/DAWG-based format could reduce this significantly.
  • Reduce dictionary size — Trim to most common entries. ~120K entries fits ~458KB, ~100K entries fits ~384KB.
  • Add external QSPI flash — Boards with external flash (W25Q128, etc.) can store the full dictionary. The engine already supports XIP reads via XipPointer.

Setup

Add to your west.yml:

manifest:
  remotes:
    - name: afiqzudinhadi
      url-base: https://github.com/afiqzudinhadi
    - name: jthlim
      url-base: https://github.com/jthlim
  projects:
    - name: zmk-javelin-steno
      remote: afiqzudinhadi
      revision: main
    - name: javelin-steno
      remote: jthlim
      revision: main
      path: zmk-javelin-steno/javelin

Add to your .conf:

CONFIG_ZMK_JAVELIN_STENO=y
CONFIG_ZMK_JAVELIN_STENO_DICT_PLOVER=y
CONFIG_ZMK_JAVELIN_STENO_DICT_LAPWING=y
CONFIG_CPLUSPLUS=y
CONFIG_LIB_CPLUSPLUS=y
CONFIG_HEAP_MEM_POOL_SIZE=32768

Set CONFIG_ZMK_JAVELIN_STENO_DICT_PLOVER and CONFIG_ZMK_JAVELIN_STENO_DICT_LAPWING to choose which dictionaries to include. Both can be enabled simultaneously.

Add to your .keymap (see steno_keys.h for all available key bindings):

#include <dt-bindings/zmk/javelin_steno.h>
#include <behaviors/javelin_steno.dtsi>

steno_layer {
    bindings = <
        &javsteno STENO_S1  &javsteno STENO_TL  &javsteno STENO_PL ...
    >;
};

License

This module is a bridge layer. The Javelin steno engine itself is licensed under PolyForm Noncommercial 1.0.0.