Kaomarchy
Input cute Japanese Emoticons (just like a real JK) with a simple menu or hotkey.
Kaomarchy is an Omarchy Quattro shell plugin: a searchable, keyboard-driven kaomoji (顔文字) picker that inserts the chosen emoticon directly into the focused application — the same type-into-field behavior as the built-in Emoji menu.
- 1,600+ single-line kaomoji, bundled offline (kaomoji-data, MIT)
- Search by the kaomoji itself, category, Japanese tag, or romaji reading — try
cat,cute,ねこ,love - Full keyboard navigation: type to filter, arrows / PgUp / PgDn to move, Enter to insert, Esc to clear/close
- Mouse support: hover to select, click to insert
- No network access at runtime; no dependencies beyond
wl-clipboard,wtype,hyprctlandjq(already required by the built-in emoji menu / Hyprland)
Install
omarchy plugin add https://github.com/Signal-Six/Kaomarchy.git
This clones the plugin, validates the manifest, and enables it (the shell
rescans ~/.config/omarchy/plugins/ automatically).
Menu entry (Super+Space)
The menu row lives in your user menu extension so the plugin stays a pure
overlay. Add one line to ~/.config/omarchy/extensions/omarchy-menu.jsonc
inside the { … } object, next to the existing trigger.emoji row:
"trigger.kaomoji": {
"icon": "",
"label": "Kaomoji",
"aliases": ["kaomoji", "emoticon", "emoticons", "japanese emoticons", "顔文字"],
"description": "Japanese emoticon picker",
"action": "omarchy-shell shell toggle signal-six.kaomarchy"
},
The menu is watched — it appears in Super+Space immediately, searchable as
kaomoji or emoticon. omarchy menu summon kaomoji works too.
Optional: global hotkey
Pick any key combo you like in Hyprland (~/.config/hypr/bindings.lua):
bind = SUPER, BRACKETR, exec, omarchy-shell shell toggle signal-six.kaomarchy
(SUPER+] is suggested because it's unassigned in the stock config, but
anything free works — the plugin deliberately doesn't presume a key.)
Update / remove
omarchy plugin update # fast-forward pull of installed plugins
omarchy plugin remove signal-six.kaomarchy
Dataset provenance
kaomoji.json is committed, so the plugin works offline forever and its
content is reviewable as part of this repository. Its exact upstream
provenance is recorded in build/dataset-source.json:
- Repository:
kaomojikan/kaomoji-data(MIT, see LICENSE-UPSTREAM) - Full 40-character upstream commit (no mutable branches or tags)
- Commit date, and the SHA-256 + git blob object id of every upstream file
The builder (build/build_kaomoji_dataset.py) is fail-closed: it accepts
only a full commit SHA, verifies fetched content against GitHub's tree for
that exact commit, and cross-checks SHA-256 hashes against the provenance
record. Any mismatch aborts the build. See CONTRIBUTING.md
for the pinned refresh procedure.
Refreshing the dataset
To rebuild the dataset from upstream, follow the pinned-commit procedure in
CONTRIBUTING.md. The short version: an upstream refresh is
a new full commit SHA → rebuild → new kaomoji.json + new
build/dataset-source.json → new plugin commit; the builder refuses to
process any other upstream content.
The builder drops multi-line ASCII-art faces (they don't fit fixed-height cells) and merges duplicate texts, keeping the richest keyword set.
How insertion works
On selection, Kaomarchy runs kaomoji-insert.sh, which first decides
deterministically whether there is a real window to paste into, then acts:
- Paste into a focused field — if a visible, mapped, input-accepting
window is on the active workspace, it puts the kaomoji on the Wayland
clipboard as sensitive content, synthesizes
Shift+Insertwithwtypeso the focused app pastes it, then kills the clipboard process so the sensitive entry is reaped. - Clipboard fallback — if the active workspace is blank (nothing to paste into), it leaves the kaomoji on the regular clipboard so you can paste it wherever you go next. The first-party emoji menu does nothing in this case; Kaomarchy deliberately keeps the clip.
The routing is deliberate: Hyprland leaves windows on other workspaces
marked visible and input-accepting, so a plain "is something focused?" check
misfires on a blank workspace (it sees a window that is actually parked
elsewhere) and would drop the kaomoji. Kaomarchy therefore checks
hyprctl clients -j against the active workspace id (hyprctl activeworkspace -j) with jq. The overlay itself is a layer-shell surface
and never appears in that list, so it can't pollute the decision. All three
calls are read-only and only run at selection time — no daemon, no lingering
process. jq + hyprctl are used for routing; without either, it falls back
to the simpler activewindow heuristic (no fallback clip guarantee).
Files
| File | Purpose |
|---|---|
manifest.json |
Omarchy plugin manifest (signal-six.kaomarchy, overlay, keepLoaded) |
Kaomoji.qml |
Searchable overlay UI |
KaomojiSearch.js |
Parse + filter logic (QML-importable, Node-testable) |
kaomoji.json |
Bundled dataset: {"k": "<kaomoji>", "s": "<search text>"} |
kaomoji-insert.sh |
Clipboard + synthesized-paste inserter |
build/build_kaomoji_dataset.py |
Dataset builder from kaomoji-data (pinned, fail-closed) |
build/dataset-source.json |
Upstream provenance: pinned commit, dates, SHA-256 hashes |
build/test_search.js |
Node smoke tests for the filter logic |
License
MIT — see LICENSE. Dataset credits and license in LICENSE-UPSTREAM.