21 Commits

Author SHA1 Message Date
felixzsh f3cb662498 fix(ui): correct key visualizer QML syntax 2026-08-28 09:54:26 -05:00
felixzsh 48a8020910 fix(ui): clear stale combo when capture stops updating 2026-08-26 18:47:29 -05:00
felixzsh a7357e3840 fix(ui): declare debugFont and debugFontMetrics before use
debugFont was referenced by debugText and debugFontMetrics before its
declaration, causing "ReferenceError: debugFont is not defined" on panel
load. Move both the debugFont property and the debugFontMetrics FontMetrics
block above the debug overlay so all references resolve in declaration
order; qualify the last use as root.debugFont.
2026-08-26 17:06:57 -05:00
felixzsh 89cd794708 fix(security): fail-closed XDG_RUNTIME_DIR and harden state file writes
Remove predictable /tmp/omarchy-key-visualizer.json fallback that exposed
captured key combinations and allowed symlink redirection when
XDG_RUNTIME_DIR is absent. Introduce fail-closed behaviour: disable
capture if XDG_RUNTIME_DIR is missing, equals /tmp, is not absolute,
not owned or not 0700.

Add secure_write with O_NOFOLLOW mitigation (test ! -L), atomic
tmp+rename and chmod 600 to prevent world-readable leaks and TOCTOU
races. Apply same guard to QML statePath/superPath and Panel nudge
writes (umask 077, chmod 600, symlink check).

Fixes security review at 790b07c0c9:
key-visualizer.lua:15-17,141-145
2026-08-24 14:00:12 -05:00
felixzsh d586678d1d refactor: shared re-position mechanism
complement
2026-08-20 10:39:32 -05:00
felixzsh aa11f5f7a2 feat: drag and drop keyvisualizer 2026-08-20 10:39:32 -05:00
felixzsh deec43c0ff feat: add D-pad fine-tune nudges below the position selector
Add unit-by-unit manual position control via a four-button D-pad
(left, up/down together, right) under the position dropdown. Nudges
persist as offsetX/offsetY in the shared config and are silently
clamped to the screen edges, so a move that would cross a border is
ignored. Selecting a preset position resets the manual offset. While
the display is hidden, a nudge flashes a directional arrow preview so
the move direction is visible even with no keys on screen.

Co-Authored-By: opencode <opencode@anthropic.com>
2026-08-20 10:39:27 -05:00
felixzsh 2ee5f88933 fix: persists config after reboots 2026-08-17 17:56:49 -05:00
felixzsh 82e60ba360 fix: drop modifier-only chords from the history
Releasing a chord made only of modifiers (Super, Super Ctrl Shift...) is
'mods of nothing': it scores nothing in combo mode and now no longer
lingers or occupies a history row either. The mods still show live while
held, which is the useful feedback; on release the entry is removed
instantly instead of entering its linger window.
2026-08-12 11:31:43 -05:00
felixzsh f705ccb37f fix: keep only complete combos in the history
The Lua emits on every key-down, so a chord pressed key-by-key without
releasing arrives as growing states (Super, Super Ctrl, Super Ctrl
Shift, Super Ctrl Shift 1). The history treated each one as a separate
combo, filling the stack with partial noise. Now a growing state while
the chord is still held (a strict superset of the current entry) updates
the entry in place instead of pushing a history row; only the complete
combo at release ever enters the history.
2026-08-12 10:59:09 -05:00
felixzsh 0a4aa59ee1 feat: add combo mode with combo counter, score and escalating effects
A 'Combo mode' toggle turns the display into a game-style counter.
Chords containing a modifier (Super/Ctrl/Alt/Shift/Menu/AltGr) are
combos: they build a combo counter, apply a multiplier (x2 at 5, x3 at
10 ... x8 at 50) and escalate the effects. Plain characters and shifted
chars typed alone are hits: +10 score only, they never touch the combo
counter or its window. Scoring: hit = 10, combo = (20 + 15*(mods-1) +
5*(keys-1)) * multiplier.

Counted once per physical chord on the Lua's empty payload (release),
so the intermediate growing emits of a chord never double-count. The
banner (COMBO 12 x3 - 3,450) sits below the history for bottom
positions and above it for top positions, with a floating +N pop per
press. Effects scale with modifiers and chain length: banner pulse,
hue shift, screen shake (up to 5px), continuous hue cycling at 10+
combos and a constant subtle vibration at 20+. The combo resets after
comboWindowMs (2s) without a new combo; the score accumulates for the
shell session.

Also fixes the README Behavior section: the history bullet was never
applied (a failed multi-edit call), now restored alongside the combo
mode docs. Manifest bumped to 1.7.0.
2026-08-12 10:51:01 -05:00
felixzsh d6c608374c feat: add stacking history with fading older entries
The last historyCount combos (1-5, default 1) stack on screen instead
of vanishing: each new combo pushes the previous one down the stack and
every older entry fades step by step (entryOpacity gradient). The stack
direction follows the position by default — top positions grow downward
(newest on top), bottom positions grow upward (newest at the bottom
edge) — and is not configurable. The panel gains a History NumberField
for the count.

Middle positions (center-left/center-right/center-center) were removed
from the dropdown and any leftover config value is folded into the
bottom row. With lingerMs 0 entries never expire, so the stack keeps
the last few combos on screen until newer ones push them out.

Manifest bumped to 1.6.0.
2026-08-12 10:06:40 -05:00
felixzsh 21f9f3fb57 Measure each chip with FontMetrics.advanceWidth
The shared TextMetrics had its text set imperatively inside the width
bindings, which went stale from the third chip on — every container
collapsed to single-char width, so Super/Space/Backspace were illegible
in combos of 3+. FontMetrics.advanceWidth(text) is a stateless method,
so every chip gets its own correct width.
2026-08-11 17:31:36 -05:00
felixzsh a5c31aaef7 lingerMs 0 keeps the last combo on screen until the next key
The hide timer only starts (and only clears) when lingerMs > 0, so 0
means the last full combo stays visible until the next key replaces it
— the keyviz-video look, as an explicit opt-in rather than the previous
behavior where 0 hid instantly.
2026-08-11 16:23:00 -05:00
felixzsh 34646dec28 Fix pause toggle: never delete the flag file
The FileView watcher fires on content changes but not on deletion, so
resuming (rm of the flag) left the panel and the menu stuck paused.
Rewrite the flag with 0 or 1 instead, matching the state-file pattern
that already works.
2026-08-11 14:03:12 -05:00
felixzsh 5670aca888 Move config out of the plugin dir so edits don't reload the plugin
The shell watches every file under ~/.config/omarchy/plugins/ with
inotify (no extension filter) and reloads all plugin code on any change,
so config.json there made every edit tear down and rebuild the panel and
bar widget. Config now lives at ~/.config/omarchy/key-visualizer.json, which
is not watched: edits update the display live. The panel migrates the
old plugin-dir config (values carried over, file removed) on first load
after the update.
2026-08-11 13:42:24 -05:00
felixzsh 1f532510a5 Make display options configurable via config.json
The panel reads config.json from its plugin folder (seeded with defaults
on first run, hot-reloaded on save): mode (all | bindings — the bindings
mode only shows combos with a modifier, ideal for keybinding tutorials),
position (keyviz-style alignment, bottom-center by default) and margin,
plus the existing lingerMs. Replaces the hardcoded bottom anchors with
x/y computed from position and margin.
2026-08-11 11:07:47 -05:00
felixzsh bd20dd8a9b Add bar widget to pause and resume the key display
A keyboard glyph in the bar (WidgetButton, right section by default)
toggles a pause flag at $XDG_RUNTIME_DIR/omarchy-key-visualizer.paused that
the panel and every bar widget instance watch, so a click on any monitor
updates all of them. While paused the panel ignores key state and hides;
the button dims. Also exposes pause/resume/toggle over the key-visualizer IPC
target.
2026-08-11 11:06:37 -05:00
felixzsh 7ab4d2f18d Linger on screen after release, keyviz-style
Previously the chips cleared the instant the keys were released, so the
120ms hide delay was invisible. Keep the last combo rendered for the
whole linger window and only clear it when the timer fires, so a quick
tap stays readable (keyviz keeps keys for lingerDurationMs, 5s by
default). Default 1000ms, tunable via lingerMs.
2026-08-11 10:52:54 -05:00
felixzsh 4955c33767 Auto-inject the Hyprland capture hook on plugin load
The capture script must run inside Hyprland's Lua config, but a plugin
cannot register itself in the user-owned hyprland.lua. On first load the
panel appends a small guarded block that dofiles key-visualizer.lua; Hyprland
auto-reloads its config on save, so install is now just add + enable.

The block is idempotent (marker comment), upgrades older plain dofile
lines, and no-ops when the plugin folder is removed so uninstalling never
breaks the config.
2026-08-11 10:11:35 -05:00
felixzsh d0a36e79dd Add key-visualizer panel UI 2026-08-11 09:53:48 -05:00