fix(capture): restore key overlay updates

This commit is contained in:
felixzsh
2026-08-28 09:42:46 -05:00
4 changed files with 40 additions and 23 deletions
+29 -12
View File
@@ -29,6 +29,11 @@ Item {
// it. The history tick prunes entries whose linger window passed. With
// historyCount 1 this is exactly "the current combo, lingering".
property var entries: []
// Epoch (seconds) of the last state payload we successfully parsed. Used
// to detect a dead capture: if no new payload arrives within maxStateAgeMs
// and the top combo was never released, we treat it as released so the
// panel self-heals instead of freezing on a stale combo forever.
property real lastStateT: 0
// How many combos stay on screen (1..5, default 1). Older entries fade
// out via the entryOpacity() gradient; a count of 1 is the classic
// current-combo-only display.
@@ -467,9 +472,10 @@ Item {
if (!root.paused) {
try {
var parsed = JSON.parse(stateFile.text())
if (parsed && Array.isArray(parsed.keys)) {
if parsed && Array.isArray(parsed.keys) {
var age = Math.floor(Date.now() / 1000) - (parsed.t || 0)
if (age <= Math.ceil(root.maxStateAgeMs / 1000)) next = parsed.keys
if ((parsed.t || 0) > 0) root.lastStateT = parsed.t
}
} catch (e) {}
} if (next.length > 0 && root.mode === "bindings") {
@@ -529,6 +535,16 @@ Item {
repeat: true
running: root.entries.length > 0
onTriggered: {
// If the capture stopped updating (crashed, plugin unloaded, or a
// config reload disabled it), the top combo may be stuck with
// releasedAt === 0 forever because no empty "all keys up" payload
// ever arrives. Treat a stale state file as a release so the combo
// lingers normally and then clears, instead of freezing on screen.
if (root.entries.length > 0 && root.entries[0].releasedAt === 0 &&
root.lastStateT > 0 &&
Math.floor(Date.now() / 1000) - root.lastStateT > Math.ceil(root.maxStateAgeMs / 1000)) {
root.entries[0] = { keys: root.entries[0].keys, releasedAt: Date.now() }
}
var now = Date.now()
var kept = []
for (var i = 0; i < root.entries.length; i++) {
@@ -995,6 +1011,17 @@ Item {
// Debug overlay: live readout of the card's position/dimensions and the
// movement state, shown next to the card while moving and after release.
// Toggle with: omarchy-shell key-visualizer debug
readonly property var debugFont: Qt.font({
family: Style.font.family,
pixelSize: Style.font.bodySmall,
bold: false
})
FontMetrics {
id: debugFontMetrics
font: debugFont
}
BorderSurface {
id: debugOverlay
visible: root.debugOverlay
@@ -1012,7 +1039,7 @@ Item {
anchors.fill: parent
anchors.margins: root.cardPad
verticalAlignment: Text.AlignVCenter
font: debugFont
font: root.debugFont
color: Color.popups.text
text: {
var lb = "\n"
@@ -1026,16 +1053,6 @@ Item {
}
readonly property bool debugDragging: dragArea.dragging
FontMetrics {
id: debugFontMetrics
font: debugFont
}
readonly property var debugFont: Qt.font({
family: Style.font.family,
pixelSize: Style.font.bodySmall,
bold: false
})
// Combo mode banner — a separate visual stacked against the history
// card (below it for bottom positions, above it for top positions).
// Shows the combo counter, multiplier and running score; hue and
+5 -1
View File
@@ -225,7 +225,10 @@ Panel {
owner: root
bar: root.bar
open: root.opened
contentWidth: Style.space(250)
contentWidth: panel.fittedContentWidth(
Math.max(Style.space(250),
filterLabel.implicitWidth + Style.spacing.xl + modeButtons.implicitWidth
+ panel.padding * 2 + Border.left(panel.borderSpec) + Border.right(panel.borderSpec)))
contentHeight: menuColumn.implicitHeight + panel.padding * 2
Column {
@@ -295,6 +298,7 @@ Panel {
height: modeButtons.height
Text {
id: filterLabel
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
text: "Filter"
+5 -9
View File
@@ -32,9 +32,8 @@ local function is_runtime_secure(r)
if not r or r == "" then return false end
if r == "/tmp" then return false end
if r:sub(1, 1) ~= "/" then return false end
-- Shell ownership probes fail from Hyprland's embedded Lua environment.
-- /run/user is root-controlled, so accepting only this process's systemd
-- runtime path still excludes shared or caller-supplied directories.
-- runtime path avoids shell probes that fail in Hyprland's Lua runtime.
local uid = effective_uid()
return uid ~= nil and r == "/run/user/" .. uid
end
@@ -53,10 +52,8 @@ local SUPER_FLAG = runtime and (runtime .. "/omarchy-key-visualizer-super") or n
local function secure_write(path, content)
if not runtime or (path ~= STATE_FILE and path ~= SUPER_FLAG) then return false end
-- FileView watches the existing inode, so replacing the path on every key
-- leaves Quickshell attached to an unlinked file. Updating in place keeps
-- the watcher live. The containing runtime directory is private to the
-- effective user and the two accepted paths are fixed above.
-- FileView watches the existing inode. These are fixed paths inside the
-- current user's private runtime directory, so update them in place.
local f = io.open(path, "w")
if not f then return false end
f:write(content)
@@ -206,9 +203,8 @@ local function emit_super()
secure_write(SUPER_FLAG, down and "1" or "0")
end
-- Create both files when the hook loads. This clears stale state after a
-- compositor restart and gives FileView stable paths to watch before the
-- first keyboard event arrives.
-- Create stable files before Quickshell starts watching them and clear stale
-- state left by a compositor restart.
emit()
emit_super()
+1 -1
View File
@@ -2,7 +2,7 @@
"schemaVersion": 1,
"id": "felixzsh.key-visualizer",
"name": "Key Visualizer",
"version": "1.8.1",
"version": "1.8.3",
"author": "felixzsh",
"description": "Shows the keys you press on screen. Great for keybinding tutorials, demos, and screencasts.",
"kinds": [