Repository navigation
Add named switches with normal and ordered hold actions - #731
Merged
Merged
Conversation
enaboapps
marked this pull request as ready for review
September 12, 2026 13:51
Greptile SummaryAdds named keyboard switches, ordered hold actions, local Windows/macOS input adapters, switch configuration UI, persistence, and shared scanning gesture dispatch.
Confidence Score: 5/5Safe to merge. The prior draft-focus issue is fixed in the current implementation. enaboapps manually resolved the automatic-scan cycle-counting thread without explanation; the current implementation counts a wrap when scanning leaves the final or initial grid slot, including an exact-length step. Reviews (7): Last reviewed commit: "Count exact-length passes and keep focus..." | Re-trigger Greptile |
enaboapps
marked this pull request as draft
September 12, 2026 16:03
enaboapps
marked this pull request as ready for review
September 12, 2026 16:11
Each switch is now a collapsed row showing name, key badge and an action summary, with Edit and Remove as the only controls. Expanding a row opens a stacked editor: name, key with Change key, press action, and hold actions as numbered rows with icon reorder buttons and a live timing preview. Adding a switch starts learning immediately and Save stays disabled until both name and key exist. Learning shows a highlighted capture panel inside the affected row, and key errors sit under the key field. Guidance moves into the shared note and disclosure style, save state and errors use the existing note and error band, and the empty list uses the standard empty state. Hold timing gets five presets, an exact-interval select, and the emergency hold duration alongside it. Fixes the unstyled fieldset border around the interval group. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The hook records a capture refusal as both the general error and the capture error, so the Switches tab showed the same message twice when the window was not focused. The error band now carries save errors only. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
On Windows the reserved press never reaches the page but its release does, and a Space release on a focused button clicks it, while an unreserved key would scroll Settings. A separate window is not an option because losing main-window focus cancels the capture. While learning, a dialog now holds focus on itself rather than a control and swallows every key event at the document capture phase. Escape still reaches the backend through its hotkey; Cancel capture is mouse-only. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
tao tracks focus with a flag flipped by WM_SETFOCUS and WM_KILLFOCUS on the top-level window, but keyboard focus lives in the WebView2 child. After the first click inside the page is_focused stays false while the app is plainly in front, so begin_switch_capture refused every mouse user with "Focus Switchify PC before learning a switch." Compare the window against GetForegroundWindow instead; macOS keeps is_focused. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A capture that ended without a key left its target set, so a later view carrying the backend's remembered key could assign it to the refused row or raise a false duplicate. The target is now released whenever a capture ends keyless, and the error is recorded against the row that was learning rather than shown under whichever row is open next. Done, Save switch, Cancel and Remove unmounted the clicked button and dropped focus to the page; focus now returns to the row's Edit button or to Add switch. Edit gains aria-controls, the capture dialog links its body text and says Cancel is mouse-only, and an unavailable key is announced in the collapsed row instead of shown by colour alone. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
An unattended automatic scan swept forever. The technique now reports when automatic movement has wrapped the current phase MAX_SCAN_CYCLES times and the session resets to idle, exactly as Stop would. Manual Next and Back steps never count, and each Select starts a fresh count for the phase it enters. The limit is a constant of three for now. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Tauri rewrites the autogenerated permission files on every dev build, and with core.autocrlf on Windows git then reported them as modified despite identical content. A .gitattributes rule fixes their EOL. The docs and a test name also used a word the identity check rejects. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A step of exactly the region length wrapped back to the same edge, so the before/after comparison missed the pass and small grid cells at the top speed could scan past the three-pass limit. The wrap flag now comes from the overflow itself, and cyclic indexes wrap on leaving their last slot. Removing a switch while a new one is being drafted sent focus to the hidden Add button; it now lands on the draft's name. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add Settings → Switches for named keyboard switches with normal release actions and ordered hold actions. Switchify now owns local capture through platform adapters; USAHP is no longer a dependency.
Settings UI
Each switch is a collapsed row showing its name, key badge and an action summary, with Edit and Remove as the only controls. Edit expands an inline editor: name, key with Change key, press action, and hold actions as numbered rows with reorder and remove icons plus a live preview of when each hold action is offered. Add switch starts learning immediately and Save stays disabled until both name and key exist. Hold timing shows five presets, an exact-interval select and the resulting emergency hold duration. Guidance, save status and errors use the shared Settings note and error styles.
While learning, a modal dialog holds keyboard focus on itself and swallows every key event, because on Windows a reserved key's release still reaches the WebView and would click a focused button or scroll Settings. Escape cancels through the backend hotkey; Cancel capture is mouse-only. Key errors are shown under the row that was learning, focus returns to the row after collapsing, and an unavailable key is announced as well as coloured.
Platform behaviour
Windows reserves plain assigned keys with global hotkeys and receives releases through Raw Input. This addresses the observed setup where hardware Space never reached the low-level keyboard hook. Registration conflicts prevent enabling and roll back reservations. Cancellation discards pending actions and drains held keys. macOS retains event-tap capture. Existing settings, scanning techniques and gesture behavior remain compatible.
On Windows the learning command checks that the main window is the foreground window rather than tao's focus flag. That flag turns false once keyboard focus moves into the WebView2 child, which refused every capture started after the first click in the page.
The shared scanner dispatches Select, Next, Previous, Reverse, Stop and Pause/resume. Holding freezes movement and displays a native nonactivating release prompt. Settings migrate old point-scan bindings once and save before scanning starts.
Closes #730.
Validation
At 2cabfb1: tsc, 130 frontend tests plus 5 Node tests, production build, cargo fmt, Clippy all targets with warnings denied, 300 Rust unit tests and 7 integration tests passed on Windows. Tests use fake input and do not send desktop input. A review of 39de3a1 found two frontend state bugs and several accessibility gaps; all are fixed in 2cabfb1 with tests.
Manual evidence: in the dev build with the window focused, Add switch opened the capture modal against the real backend, and Cancel capture returned it to idle; no key events were sent by the agent. A standalone probe earlier received hardware Space through Raw Input and global hotkeys while the low-level hook received no events. Hold timing, final click behavior, a real switch press through the modal and broader foreground-app compatibility remain manual checks.
Known Windows limitations are shown in Settings and documented: key releases may reach other apps; modified combinations are not reserved; F12 is unavailable. This adapter does not claim complete key suppression. macOS interaction needs validation on macOS.
Open question: focus-loss cancellation still relies on tao's Focused(false) event, which may not fire on Windows once the WebView2 child owns keyboard focus, so learning may not end when another app comes to the front. Needs a manual check and possibly a foreground poll in the capture watchdog.