@@ -10,11 +10,11 @@ import { SettingsPageHeader } from "@components/SettingsPageheader";
10
10
import { useJsonRpc } from "@/hooks/useJsonRpc" ;
11
11
import { useHidStore , useRTCStore , useUiStore , useSettingsStore } from "@/hooks/stores" ;
12
12
import { keys , modifiers } from "@/keyboardMappings" ;
13
- import { layouts , chars } from "@/keyboardLayouts" ;
13
+ import { KeyStroke , KeyboardLayout , selectedKeyboard } from "@/keyboardLayouts" ;
14
14
import notifications from "@/notifications" ;
15
15
16
- const hidKeyboardPayload = ( keys : number [ ] , modifier : number ) => {
17
- return { keys , modifier } ;
16
+ const hidKeyboardPayload = ( modifier : number , keys : number [ ] ) => {
17
+ return { modifier , keys } ;
18
18
} ;
19
19
20
20
const modifierCode = ( shift ?: boolean , altRight ?: boolean ) => {
@@ -62,49 +62,56 @@ export default function PasteModal() {
62
62
const onConfirmPaste = useCallback ( async ( ) => {
63
63
setPasteMode ( false ) ;
64
64
setDisableVideoFocusTrap ( false ) ;
65
+
65
66
if ( rpcDataChannel ?. readyState !== "open" || ! TextAreaRef . current ) return ;
66
- if ( ! safeKeyboardLayout ) return ;
67
- if ( ! chars [ safeKeyboardLayout ] ) return ;
67
+ const keyboard : KeyboardLayout = selectedKeyboard ( safeKeyboardLayout ) ;
68
+ if ( ! keyboard ) return ;
69
+
68
70
const text = TextAreaRef . current . value ;
69
71
70
72
try {
71
73
for ( const char of text ) {
72
- const { key , shift , altRight , deadKey , accentKey } = chars [ safeKeyboardLayout ] [ char ]
73
- if ( ! key ) continue ;
74
+ const keyprops = keyboard . chars [ char ] ;
75
+ if ( ! keyprops ) continue ;
74
76
75
- const keyz = [ keys [ key ] ] ;
76
- const modz = [ modifierCode ( shift , altRight ) ] ;
77
+ const { key , shift , altRight , deadKey , accentKey } = keyprops ;
78
+ if ( ! key ) continue ;
77
79
78
- if ( deadKey ) {
79
- keyz . push ( keys [ "Space" ] ) ;
80
- modz . push ( noModifier ) ;
81
- }
80
+ // if this is an accented character, we need to send that accent FIRST
82
81
if ( accentKey ) {
83
- keyz . unshift ( keys [ accentKey . key ] )
84
- modz . unshift ( modifierCode ( accentKey . shift , accentKey . altRight ) )
82
+ await sendKeystroke ( { modifier : modifierCode ( accentKey . shift , accentKey . altRight ) , keys : [ keys [ accentKey . key ] ] } )
85
83
}
86
84
87
- for ( const [ index , kei ] of keyz . entries ( ) ) {
88
- await new Promise < void > ( ( resolve , reject ) => {
89
- send (
90
- "keyboardReport" ,
91
- hidKeyboardPayload ( [ kei ] , modz [ index ] ) ,
92
- params => {
93
- if ( "error" in params ) return reject ( params . error ) ;
94
- send ( "keyboardReport" , hidKeyboardPayload ( [ ] , 0 ) , params => {
95
- if ( "error" in params ) return reject ( params . error ) ;
96
- resolve ( ) ;
97
- } ) ;
98
- } ,
99
- ) ;
100
- } ) ;
85
+ // now send the actual key
86
+ await sendKeystroke ( { modifier : modifierCode ( shift , altRight ) , keys : [ keys [ key ] ] } ) ;
87
+
88
+ // if what was requested was a dead key, we need to send an unmodified space to emit
89
+ // just the accent character
90
+ if ( deadKey ) {
91
+ await sendKeystroke ( { modifier : noModifier , keys : [ keys [ "Space" ] ] } ) ;
101
92
}
93
+
94
+ // now send a message with no keys down to "release" the keys
95
+ await sendKeystroke ( { modifier : 0 , keys : [ ] } ) ;
102
96
}
103
97
} catch ( error ) {
104
- console . error ( error ) ;
98
+ console . error ( "Failed to paste text:" , error ) ;
105
99
notifications . error ( "Failed to paste text" ) ;
106
100
}
107
- } , [ rpcDataChannel ?. readyState , send , setDisableVideoFocusTrap , setPasteMode , safeKeyboardLayout ] ) ;
101
+
102
+ async function sendKeystroke ( stroke : KeyStroke ) {
103
+ await new Promise < void > ( ( resolve , reject ) => {
104
+ send (
105
+ "keyboardReport" ,
106
+ hidKeyboardPayload ( stroke . modifier , stroke . keys ) ,
107
+ params => {
108
+ if ( "error" in params ) return reject ( params . error ) ;
109
+ resolve ( ) ;
110
+ }
111
+ ) ;
112
+ } ) ;
113
+ }
114
+ } , [ rpcDataChannel ?. readyState , safeKeyboardLayout , send , setDisableVideoFocusTrap , setPasteMode ] ) ;
108
115
109
116
useEffect ( ( ) => {
110
117
if ( TextAreaRef . current ) {
@@ -154,7 +161,7 @@ export default function PasteModal() {
154
161
// @ts -expect-error TS doesn't recognize Intl.Segmenter in some environments
155
162
[ ...new Intl . Segmenter ( ) . segment ( value ) ]
156
163
. map ( x => x . segment )
157
- . filter ( char => ! chars [ safeKeyboardLayout ] [ char ] ) ,
164
+ . filter ( char => ! selectedKeyboard ( safeKeyboardLayout ) . chars [ char ] ) ,
158
165
) ,
159
166
] ;
160
167
@@ -175,7 +182,7 @@ export default function PasteModal() {
175
182
</ div >
176
183
< div className = "space-y-4" >
177
184
< p className = "text-xs text-slate-600 dark:text-slate-400" >
178
- Sending text using keyboard layout: { layouts [ safeKeyboardLayout ] }
185
+ Sending text using keyboard layout: { selectedKeyboard ( safeKeyboardLayout ) . name }
179
186
</ p >
180
187
</ div >
181
188
</ div >
0 commit comments