-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcore.cljc
More file actions
124 lines (98 loc) · 4.48 KB
/
Copy pathcore.cljc
File metadata and controls
124 lines (98 loc) · 4.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
(ns keymap.core
"Central resolver for shell-owned keyboard shortcuts.
Block-local contenteditable keys are intentionally owned by components.block
and are absent from this table. See docs/KEYBOARD_OWNERSHIP.md.
Design:
- One declarative table, zero divergence
- Context-aware (editing vs non-editing mode)
- Hot-reloadable
- Extensible via plugin registration"
(:require [kernel.query :as q]))
;; ── Keymap Registry ───────────────────────────────────────────────────────────
(defonce !keymap-registry
(atom {}))
(defn register!
"Register key bindings for a context.
Args:
context - :global, :editing, :non-editing
bindings - Vector of [key-spec intent-type] pairs
Key spec format:
{:key \"Enter\"} - plain key
{:key \"ArrowDown\" :shift true} - with modifiers
{:key \"Tab\" :mod true} - cmd/ctrl
{:key \"Backspace\" :mod true :shift true} - multiple modifiers
Example:
(register! :non-editing
[[{:key \"Enter\"} :create-and-enter-edit]
[{:key \"ArrowDown\"} :select-next-sibling]
[{:key \"Tab\" :shift true} :outdent-selected]])"
[context bindings]
(swap! !keymap-registry update context
(fn [existing]
(into (or existing []) bindings))))
(defn clear-bindings!
"Clear all bindings for a context (useful for hot reload)."
[context]
(swap! !keymap-registry dissoc context))
(defn reset-all!
"Reset entire keymap registry (useful for tests)."
[]
(reset! !keymap-registry {}))
;; ── Key Matching ──────────────────────────────────────────────────────────────
(defn- modifier-match?
"Check if event modifiers match key-spec modifiers."
[event-mods spec-mods]
(and (= (:mod event-mods false) (:mod spec-mods false))
(= (:shift event-mods false) (:shift spec-mods false))
(= (:alt event-mods false) (:alt spec-mods false))))
(defn- key-matches?
"Check if keyboard event matches key specification."
[event key-spec]
(and (= (:key event) (:key key-spec))
(modifier-match? event key-spec)))
(defn- find-binding
"Find matching binding in context bindings."
[bindings event]
(some (fn [[key-spec intent-type]]
(when (key-matches? event key-spec)
intent-type))
bindings))
;; ── Context Resolution ────────────────────────────────────────────────────────
(defn- resolve-context
"Determine current input context from session state.
IMPORTANT: Session state (not db) contains :ui :editing-block-id.
The db only contains document data, not ephemeral UI state."
[session]
(if (q/editing? session) :editing :non-editing))
;; ── Resolver ──────────────────────────────────────────────────────────────────
(defn resolve-intent-type
"Resolve keyboard event to intent type based on current context.
Args:
event - Event map with :key, :mod, :shift, :alt
session - Current session state (contains :ui :editing-block-id)
Returns:
- intent-type keyword if binding found
- nil if no binding matches
Priority order:
1. Context-specific bindings (:editing or :non-editing)
2. Global bindings (:global)"
[event session]
(let [context (resolve-context session)
registry @!keymap-registry
context-bindings (get registry context)
global-bindings (get registry :global)]
(or (find-binding context-bindings event)
(find-binding global-bindings event))))
;; ── Event Parsing ─────────────────────────────────────────────────────────────
#_{:clj-kondo/ignore [:unused-binding]} ; e used in CLJS reader conditional
(defn parse-dom-event
"Parse DOM KeyboardEvent to event map.
Args:
e - DOM KeyboardEvent
Returns:
{:key \"Enter\" :mod true :shift false :alt false}"
[e]
{:key #?(:clj nil :cljs (.-key e))
:mod #?(:clj false :cljs (or (.-metaKey e) (.-ctrlKey e)))
:shift #?(:clj false :cljs (.-shiftKey e))
:alt #?(:clj false :cljs (.-altKey e))})