@@ -148,15 +148,52 @@ const Options = () => {
148148 const [ portalUrlText , setPortalUrlText ] = useState ( '' ) ;
149149 const modelRef = createRef < HTMLInputElement > ( ) ;
150150 const [ modelText , setModelText ] = useState ( '' ) ;
151- const jupyterlabKeybindingAcceptRef = createRef < HTMLInputElement > ( ) ;
152151 const [ jupyterlabKeybindingAcceptText , setJupyterlabKeybindingAcceptText ] = useState ( '' ) ;
153- const jupyterlabKeybindingDismissRef = createRef < HTMLInputElement > ( ) ;
154152 const [ jupyterlabKeybindingDismissText , setJupyterlabKeybindingDismissText ] = useState ( '' ) ;
155- const jupyterNotebookKeybindingAcceptRef = createRef < HTMLInputElement > ( ) ;
156153 const [ jupyterNotebookKeybindingAcceptText , setJupyterNotebookKeybindingAcceptText ] =
157154 useState ( '' ) ;
158155 const [ jupyterDebounceMs , setJupyterDebounceMs ] = useState ( 0 ) ;
159156 const jupyterDebounceMsRef = createRef < HTMLInputElement > ( ) ;
157+ const [ currentKey , setCurrentKey ] = useState ( {
158+ key : '' ,
159+ ctrl : false ,
160+ alt : false ,
161+ shift : false ,
162+ meta : false ,
163+ } ) ;
164+ const [ jupyterlabAcceptInput , setJupyterlabAcceptInput ] = useState ( false ) ;
165+ const [ jupyterlabDismissInput , setJupyterlabDismissInput ] = useState ( false ) ;
166+ const [ notebookAcceptInput , setNotebookAcceptInput ] = useState ( false ) ;
167+
168+ const formatKeyCombination = ( key : any ) => {
169+ const modifiers = [ ] ;
170+ if ( key . ctrl ) modifiers . push ( 'Ctrl' ) ;
171+ if ( key . alt ) modifiers . push ( 'Alt' ) ;
172+ if ( key . shift ) modifiers . push ( 'Shift' ) ;
173+ if ( key . meta ) modifiers . push ( 'Meta' ) ;
174+ return [ ...modifiers , key . key . toUpperCase ( ) ] . join ( '+' ) ;
175+ } ;
176+
177+ const handleKeyDown = ( e : React . KeyboardEvent < HTMLInputElement > ) => {
178+ e . preventDefault ( ) ;
179+ const key = e . key ;
180+ if ( key !== 'Control' && key !== 'Alt' && key !== 'Shift' && key !== 'Meta' ) {
181+ const ctrl = e . ctrlKey ;
182+ const alt = e . altKey ;
183+ const shift = e . shiftKey ;
184+ const meta = e . metaKey ;
185+ setCurrentKey ( { key, ctrl, alt, shift, meta } ) ;
186+
187+ // Force blur using setTimeout to ensure it happens after state update
188+ setTimeout ( ( ) => {
189+ if ( e . currentTarget ) {
190+ e . currentTarget . blur ( ) ;
191+ // Also try to remove focus from the document
192+ ( document . activeElement as HTMLElement ) ?. blur ( ) ;
193+ }
194+ } , 0 ) ;
195+ }
196+ } ;
160197
161198 useEffect ( ( ) => {
162199 ( async ( ) => {
@@ -203,6 +240,7 @@ const Options = () => {
203240 }
204241 return PUBLIC_WEBSITE ;
205242 } , [ ] ) ;
243+
206244 return (
207245 < Box sx = { { width : '100%' , maxWidth : 400 , bgcolor : 'background.paper' } } >
208246 { ! CODEIUM_ENTERPRISE && (
@@ -328,95 +366,84 @@ const Options = () => {
328366 } }
329367 />
330368 < Box sx = { { my : 2 , mx : 2 } } >
331- < Typography variant = "h6" > Jupyterlab settings </ Typography >
369+ < Typography variant = "h6" > Jupyter Settings </ Typography >
332370 < Typography variant = "body2" >
333- A single keystroke is supported. The syntax is described{ ' ' }
334- < Link
335- href = "https://github.com/jupyterlab/lumino/blob/f85aad4903504c942fc202c57270e707f1ab87c1/packages/commands/src/index.ts#L1061-L1083"
336- target = "_blank"
337- >
338- here
339- < OpenInNewIcon
340- fontSize = "small"
341- sx = { {
342- verticalAlign : 'bottom' ,
343- } }
344- />
345- </ Link >
346- .
371+ Press the desired key combination in the input field. For example, press "Ctrl+Tab" for a
372+ Ctrl+Tab shortcut.
373+ </ Typography >
374+
375+ < Typography variant = "subtitle1" sx = { { mt : 2 , mb : 1 } } >
376+ JupyterLab
347377 </ Typography >
348378 < TextField
349379 id = "jupyterlabKeybindingAccept"
350- label = "Accept key binding "
380+ label = "Accept Shortcut "
351381 variant = "standard"
352382 fullWidth
353- inputRef = { jupyterlabKeybindingAcceptRef }
354- value = { jupyterlabKeybindingAcceptText }
355- onChange = { ( e ) => setJupyterlabKeybindingAcceptText ( e . target . value ) }
383+ value = { jupyterlabAcceptInput ? 'Press keys...' : jupyterlabKeybindingAcceptText || 'Tab' }
384+ onFocus = { ( ) => setJupyterlabAcceptInput ( true ) }
385+ onBlur = { async ( ) => {
386+ setJupyterlabAcceptInput ( false ) ;
387+ if ( currentKey . key ) {
388+ const formatted = formatKeyCombination ( currentKey ) ;
389+ setJupyterlabKeybindingAcceptText ( formatted ) ;
390+ await setStorageItem ( 'jupyterlabKeybindingAccept' , formatted ) ;
391+ setCurrentKey ( { key : '' , ctrl : false , alt : false , shift : false , meta : false } ) ;
392+ }
393+ } }
394+ onKeyDown = { handleKeyDown }
356395 />
357- < Box sx = { { display : 'flex' , justifyContent : 'flex-end' } } >
358- < Button
359- variant = "text"
360- onClick = { async ( ) => {
361- const keybinding = jupyterlabKeybindingAcceptRef . current ?. value ;
362- await setStorageItem ( 'jupyterlabKeybindingAccept' , keybinding ) ;
363- } }
364- sx = { { textTransform : 'none' } }
365- >
366- Enter Keybinding < LoginIcon />
367- </ Button >
368- </ Box >
369396 < TextField
370397 id = "jupyterlabKeybindingDismiss"
371- label = "Dismiss key binding "
398+ label = "Dismiss Shortcut "
372399 variant = "standard"
373400 fullWidth
374- inputRef = { jupyterlabKeybindingDismissRef }
375- value = { jupyterlabKeybindingDismissText }
376- onChange = { ( e ) => setJupyterlabKeybindingDismissText ( e . target . value ) }
401+ value = {
402+ jupyterlabDismissInput ? 'Press keys...' : jupyterlabKeybindingDismissText || 'Escape'
403+ }
404+ onFocus = { ( ) => setJupyterlabDismissInput ( true ) }
405+ onBlur = { async ( ) => {
406+ setJupyterlabDismissInput ( false ) ;
407+ if ( currentKey . key ) {
408+ const formatted = formatKeyCombination ( currentKey ) ;
409+ setJupyterlabKeybindingDismissText ( formatted ) ;
410+ await setStorageItem ( 'jupyterlabKeybindingDismiss' , formatted ) ;
411+ setCurrentKey ( { key : '' , ctrl : false , alt : false , shift : false , meta : false } ) ;
412+ }
413+ } }
414+ onKeyDown = { handleKeyDown }
377415 />
378- < Box sx = { { display : 'flex' , justifyContent : 'flex-end' } } >
379- < Button
380- variant = "text"
381- onClick = { async ( ) => {
382- const keybinding = jupyterlabKeybindingDismissRef . current ?. value ;
383- await setStorageItem ( 'jupyterlabKeybindingDismiss' , keybinding ) ;
384- } }
385- sx = { { textTransform : 'none' } }
386- >
387- Enter Keybinding < LoginIcon />
388- </ Button >
389- </ Box >
390- </ Box >
391- < Box sx = { { my : 2 , mx : 2 } } >
392- < Typography variant = "h6" > Jupyter Notebook settings </ Typography >
416+
417+ < Typography variant = "subtitle1" sx = { { mt : 2 , mb : 1 } } >
418+ Jupyter Notebook
419+ </ Typography >
393420 < TextField
394421 id = "jupyterNotebookKeybindingAccept"
395- label = "Accept key binding "
422+ label = "Accept Shortcut "
396423 variant = "standard"
397424 fullWidth
398- inputRef = { jupyterNotebookKeybindingAcceptRef }
399- value = { jupyterNotebookKeybindingAcceptText }
400- onChange = { ( e ) => setJupyterNotebookKeybindingAcceptText ( e . target . value ) }
425+ value = {
426+ notebookAcceptInput ? 'Press keys...' : jupyterNotebookKeybindingAcceptText || 'Tab'
427+ }
428+ onFocus = { ( ) => setNotebookAcceptInput ( true ) }
429+ onBlur = { async ( ) => {
430+ setNotebookAcceptInput ( false ) ;
431+ if ( currentKey . key ) {
432+ const formatted = formatKeyCombination ( currentKey ) ;
433+ setJupyterNotebookKeybindingAcceptText ( formatted ) ;
434+ await setStorageItem ( 'jupyterNotebookKeybindingAccept' , formatted ) ;
435+ setCurrentKey ( { key : '' , ctrl : false , alt : false , shift : false , meta : false } ) ;
436+ }
437+ } }
438+ onKeyDown = { handleKeyDown }
401439 />
402- < Box sx = { { display : 'flex' , justifyContent : 'flex-end' } } >
403- < Button
404- variant = "text"
405- onClick = { async ( ) => {
406- const keybinding = jupyterNotebookKeybindingAcceptRef . current ?. value ;
407- await setStorageItem ( 'jupyterNotebookKeybindingAccept' , keybinding ) ;
408- } }
409- sx = { { textTransform : 'none' } }
410- >
411- Enter Keybinding < LoginIcon />
412- </ Button >
413- </ Box >
414- </ Box >
415- < Box sx = { { my : 2 , mx : 2 } } >
416- < Typography variant = "h6" > Jupyter debounce time </ Typography >
440+
441+ < Typography variant = "subtitle1" sx = { { mt : 2 , mb : 1 } } >
442+ Performance
443+ </ Typography >
417444 < TextField
418445 id = "jupyterDebounceMs"
419- label = "Debounce time (ms)"
446+ label = "Debounce (ms)"
420447 variant = "standard"
421448 fullWidth
422449 type = "number"
@@ -428,8 +455,8 @@ const Options = () => {
428455 < Button
429456 variant = "text"
430457 onClick = { async ( ) => {
431- const debounceTime = Number ( jupyterDebounceMsRef . current ?. value ) ;
432- await setStorageItem ( 'jupyterDebounceMs' , debounceTime ) ;
458+ const debounceMs = parseInt ( jupyterDebounceMsRef . current ?. value ?? '0' ) ;
459+ await setStorageItem ( 'jupyterDebounceMs' , debounceMs ) ;
433460 } }
434461 sx = { { textTransform : 'none' } }
435462 >
0 commit comments