Decompose customize dialog, fix reset conflict race, and auto-close popup

Extract key handler and conflict resolution logic from the ~390-line
customize-shortcut-dialog-inner into testable top-level defn- functions:
- customize-key-handler: key event state machine (~70 lines)
- compute-override-plan: pure conflict resolution for reassign
- compute-reset-plan: pure conflict resolution for reset-to-default
- matches-default-binding?: canonical default-binding comparison

Add persist-user-shortcuts-batch! to core.cljs to atomically persist
multiple binding changes in a single config read-modify-write cycle,
fixing a race condition where sequential persist-user-shortcut! calls
clobbered each other (config-handler/set-config! updates state async
via save-file! promise chain, so the second call reads stale data).

Fix reset-fn! to strip conflicting bindings before restoring defaults,
preventing error notifications from goog.ui.KeyboardShortcutHandler
duplicate key registration. Auto-close the customize popup 300ms after
reset so it doesn't float over a now-empty filtered list.

Clear user overrides (persist nil) when conflict stripping leaves a
binding that matches the action's default, so actions correctly
disappear from the "Custom" filter tab.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
scheinriese
2026-03-11 13:32:24 +01:00
committed by Tienson Qin
parent 8088b6f3f8
commit 33dcefaa06
2 changed files with 257 additions and 111 deletions

View File

@@ -309,3 +309,29 @@
:shortcuts (into-shortcuts (:shortcuts (state/get-global-config))))
;; web browser platform
(storage/set :ls-shortcuts (into-shortcuts (storage/get :ls-shortcuts)))))))
(defn persist-user-shortcuts-batch!
"Persist multiple shortcut binding changes atomically.
changes is a seq of [id binding] pairs where binding is a string, vector,
boolean, or nil (nil means remove/reset to default).
Reads each config source once, applies all changes, and writes once per source
to avoid read-modify-write races between sequential persist-user-shortcut! calls."
[changes]
(let [apply-changes
(fn [shortcuts]
(reduce (fn [m [id binding]]
(if (nil? binding)
(dissoc m id)
(if (or (string? binding)
(vector? binding)
(boolean? binding))
(assoc m id binding)
m)))
(or shortcuts {})
changes))]
(config-handler/set-config!
:shortcuts (apply-changes (:shortcuts (state/get-graph-config))))
(if (util/electron?)
(global-config-handler/set-global-config-kv!
:shortcuts (apply-changes (:shortcuts (state/get-global-config))))
(storage/set :ls-shortcuts (apply-changes (storage/get :ls-shortcuts))))))