返回 DeepSeek-Reasonix
keys.go
根目录 / internal / browser / cdp / keys.go
1 package cdp
2
3 import (
4 "fmt"
5 "strings"
6 "unicode"
7 )
8
9 // Modifier bits as the DevTools input domain defines them.
10 const (
11 modAlt = 1
12 modCtrl = 2
13 modMeta = 4
14 modShift = 8
15 )
16
17 // namedKey is one key the model can name in a chord.
18 type namedKey struct {
19 key string
20 code string
21 vk int
22 text string
23 }
24
25 var namedKeys = map[string]namedKey{
26 "enter": {"Enter", "Enter", 13, "\r"},
27 "return": {"Enter", "Enter", 13, "\r"},
28 "tab": {"Tab", "Tab", 9, "\t"},
29 "escape": {"Escape", "Escape", 27, ""},
30 "esc": {"Escape", "Escape", 27, ""},
31 "space": {" ", "Space", 32, " "},
32 "backspace": {"Backspace", "Backspace", 8, ""},
33 "delete": {"Delete", "Delete", 46, ""},
34 "insert": {"Insert", "Insert", 45, ""},
35 "home": {"Home", "Home", 36, ""},
36 "end": {"End", "End", 35, ""},
37 "pageup": {"PageUp", "PageUp", 33, ""},
38 "pagedown": {"PageDown", "PageDown", 34, ""},
39 "arrowup": {"ArrowUp", "ArrowUp", 38, ""},
40 "arrowdown": {"ArrowDown", "ArrowDown", 40, ""},
41 "arrowleft": {"ArrowLeft", "ArrowLeft", 37, ""},
42 "arrowright": {"ArrowRight", "ArrowRight", 39, ""},
43 "up": {"ArrowUp", "ArrowUp", 38, ""},
44 "down": {"ArrowDown", "ArrowDown", 40, ""},
45 "left": {"ArrowLeft", "ArrowLeft", 37, ""},
46 "right": {"ArrowRight", "ArrowRight", 39, ""},
47 }
48
49 var modifierNames = map[string]int{
50 "alt": modAlt, "option": modAlt,
51 "ctrl": modCtrl, "control": modCtrl,
52 "meta": modMeta, "cmd": modMeta, "command": modMeta, "super": modMeta,
53 "shift": modShift,
54 }
55
56 // charEvents types one rune as a key press. Chrome turns a keyDown carrying
57 // text into keydown, keypress, and input, which is what a controlled React or
58 // Vue field listens for.
59 func charEvents(r rune) []map[string]any {
60 if r == '\n' || r == '\r' {
61 down, up, err := chordEvents("Enter")
62 if err != nil {
63 return nil
64 }
65 return []map[string]any{down, up}
66 }
67 text := string(r)
68 vk := 0
69 if r < unicode.MaxASCII {
70 vk = int(unicode.ToUpper(r))
71 }
72 return []map[string]any{
73 {"type": "keyDown", "text": text, "unmodifiedText": text, "key": text, "windowsVirtualKeyCode": vk, "nativeVirtualKeyCode": vk},
74 {"type": "keyUp", "key": text, "windowsVirtualKeyCode": vk, "nativeVirtualKeyCode": vk},
75 }
76 }
77
78 // chordEvents parses a key name or '+'-joined chord such as Control+a into the
79 // key-down and key-up events that reproduce it.
80 func chordEvents(spec string) (map[string]any, map[string]any, error) {
81 parts := strings.Split(spec, "+")
82 modifiers := 0
83 for i := range len(parts) - 1 {
84 name := strings.ToLower(strings.TrimSpace(parts[i]))
85 bit, ok := modifierNames[name]
86 if !ok {
87 return nil, nil, fmt.Errorf("unknown modifier %q in %q; use Alt, Control, Meta, or Shift", parts[i], spec)
88 }
89 modifiers |= bit
90 }
91 last := strings.TrimSpace(parts[len(parts)-1])
92 if last == "" {
93 return nil, nil, fmt.Errorf("keys %q names no key", spec)
94 }
95 key, err := resolveKey(last)
96 if err != nil {
97 return nil, nil, err
98 }
99 // A character is produced only when neither Control nor Meta is held; with
100 // them the event is a shortcut and must carry no text.
101 text := key.text
102 if modifiers&(modCtrl|modMeta) != 0 {
103 text = ""
104 }
105 if modifiers&modShift != 0 && len([]rune(key.key)) == 1 {
106 upper := strings.ToUpper(key.key)
107 key.key = upper
108 if text != "" {
109 text = upper
110 }
111 }
112 down := map[string]any{
113 "type": "keyDown", "key": key.key, "code": key.code, "modifiers": modifiers,
114 "windowsVirtualKeyCode": key.vk, "nativeVirtualKeyCode": key.vk,
115 }
116 if text != "" {
117 down["text"] = text
118 down["unmodifiedText"] = text
119 } else {
120 down["type"] = "rawKeyDown"
121 }
122 up := map[string]any{
123 "type": "keyUp", "key": key.key, "code": key.code, "modifiers": modifiers,
124 "windowsVirtualKeyCode": key.vk, "nativeVirtualKeyCode": key.vk,
125 }
126 return down, up, nil
127 }
128
129 // resolveKey maps a key name onto its event fields; a single character is its
130 // own key, and F1 to F24 follow their virtual key codes.
131 func resolveKey(name string) (namedKey, error) {
132 if k, ok := namedKeys[strings.ToLower(name)]; ok {
133 return k, nil
134 }
135 if n, ok := functionKey(name); ok {
136 return n, nil
137 }
138 runes := []rune(name)
139 if len(runes) != 1 {
140 return namedKey{}, fmt.Errorf("unknown key %q; use a single character, Enter, Tab, Escape, an arrow, or a function key", name)
141 }
142 r := runes[0]
143 return namedKey{key: name, code: charCode(r), vk: int(unicode.ToUpper(r)), text: name}, nil
144 }
145
146 func functionKey(name string) (namedKey, bool) {
147 upper := strings.ToUpper(name)
148 if len(upper) < 2 || upper[0] != 'F' {
149 return namedKey{}, false
150 }
151 n := 0
152 for _, r := range upper[1:] {
153 if r < '0' || r > '9' {
154 return namedKey{}, false
155 }
156 n = n*10 + int(r-'0')
157 }
158 if n < 1 || n > 24 {
159 return namedKey{}, false
160 }
161 return namedKey{key: upper, code: upper, vk: 111 + n}, true
162 }
163
164 // charCode names the physical key a character sits on, which controlled inputs
165 // read from KeyboardEvent.code.
166 func charCode(r rune) string {
167 switch {
168 case unicode.IsLetter(r) && r < unicode.MaxASCII:
169 return "Key" + strings.ToUpper(string(r))
170 case r >= '0' && r <= '9':
171 return "Digit" + string(r)
172 case r == ' ':
173 return "Space"
174 }
175 return ""
176 }
177
177 lines GO