fix(lock): harden lock screen input paths
CI / typecheck + test + build (windows) (push) Canceled after 0s
CI / typecheck + test + build (windows) (push) Canceled after 0s
- remove the application menu while locked (lockMenu.ts): Alt reveals the default menu and its mouse-clickable Reload/DevTools/Zoom items bypass before-input-event entirely; menu is rebuilt from the default template on unlock, startup-restored locks covered from initLockController - extract the keyboard classification into pure lockShortcuts.ts (isLockBlockedShortcut/isPanicLockChord) with a table-driven test (lock-shortcuts.mjs, 29 cases) — the v1.0.17 lockout escaped CI because this decision lived inline in createWindow() - reserve Ctrl+L in the global show/hide shortcut recorder: a global registration intercepts the chord at OS level and silently disables the panic lock - skip auto-repeat keydowns in the panic-lock branch (held Ctrl+L on an unconfigured app re-read lock.json + settings.json per repeat) - LockScreen: re-sync the cooldown clock on visibilitychange/focus/pageshow so a suspended renderer timer cannot leave the password input disabled past the real deadline
This commit is contained in:
1 parent
dd08f8054a
commit
86f51df2e6
11 files changed
+229
-44
No files matched your search
@@ -68,6 +68,23 @@ export function LockScreen({ state, onStateChange }: LockScreenProps): React.JSX
|
||||
return () => window.clearInterval(id)
|
||||
}, [cooldownUntil])
|
||||
|
||||
// The countdown is renderer-driven, and Chromium may suspend or coalesce
|
||||
// timers while the window is hidden, minimized or occluded — a suspended
|
||||
// interval would leave the input disabled (and the "retry in N s" text
|
||||
// frozen) long past the real deadline. Re-sync the clock the moment the
|
||||
// page becomes visible or focused again, so recovery is immediate.
|
||||
useEffect(() => {
|
||||
const sync = (): void => setNow(Date.now())
|
||||
document.addEventListener('visibilitychange', sync)
|
||||
window.addEventListener('focus', sync)
|
||||
window.addEventListener('pageshow', sync)
|
||||
return () => {
|
||||
document.removeEventListener('visibilitychange', sync)
|
||||
window.removeEventListener('focus', sync)
|
||||
window.removeEventListener('pageshow', sync)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const remainingMs = Math.max(0, cooldownUntil - now)
|
||||
const cooling = remainingMs > 0
|
||||
|
||||
|
||||
@@ -500,8 +500,17 @@ function acceleratorFromEvent(e: React.KeyboardEvent<HTMLInputElement>): string
|
||||
*/
|
||||
const RESERVED_CONTROL_KEYS = new Set(['=', '-', '0', 'PageUp', 'PageDown'])
|
||||
|
||||
/**
|
||||
* Whole chords the app owns outright, matched exactly (modifier set included).
|
||||
* Ctrl+L is the panic lock, captured in main's before-input-event: a global
|
||||
* registration intercepts the key at the OS level even while this window is
|
||||
* focused, so binding it here would silently disable the lock shortcut.
|
||||
*/
|
||||
const RESERVED_EXACT_ACCELERATORS = new Set(['Control+L'])
|
||||
|
||||
/** true when `accel` (e.g. "Control+Shift+=") collides with an in-app shortcut */
|
||||
function isReservedAccelerator(accel: string): boolean {
|
||||
if (RESERVED_EXACT_ACCELERATORS.has(accel)) return true
|
||||
const parts = accel.split('+')
|
||||
return parts.includes('Control') && RESERVED_CONTROL_KEYS.has(parts[parts.length - 1])
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user