@@ -164,6 +164,7 @@ function Events0() { // app_builder
164164 task . code_editor = $ ( "#code-editor" ) ;
165165
166166 task . sys_code_editor . init_tabs ( task ) ;
167+ task . sys_code_editor . init_tab_shortcuts ( task ) ;
167168
168169 task . item_tree = task . sys_items . copy ( { handlers : false , details : false } ) ;
169170
@@ -3672,6 +3673,165 @@ function Events04() { // app_builder.catalogs.sys_code_editor
36723673 close_editor ( task , $ ( this ) . parent ( ) . attr ( 'id' ) ) ;
36733674 } ) ;
36743675 }
3676+ function init_tab_shortcuts ( task ) {
3677+ $ ( document ) . on ( 'keydown' , function ( e ) {
3678+ let current = $ ( '#task-tabs li.active button' ) . attr ( 'id' ) ;
3679+ let ids = Object . keys ( task . tabs ) ;
3680+ let idx = ids . indexOf ( current ) ;
3681+
3682+ // Ctrl+Alt+Right = next tab
3683+ if ( e . ctrlKey && e . altKey && e . key === "ArrowRight" ) {
3684+ e . preventDefault ( ) ;
3685+ if ( idx >= 0 ) {
3686+ let next = ids [ ( idx + 1 ) % ids . length ] ;
3687+ show_tab ( task , next ) ;
3688+ }
3689+ }
3690+
3691+ // Ctrl+Alt+Left = prev tab
3692+ if ( e . ctrlKey && e . altKey && e . key === "ArrowLeft" ) {
3693+ e . preventDefault ( ) ;
3694+ if ( idx >= 0 ) {
3695+ let prev = ids [ ( idx - 1 + ids . length ) % ids . length ] ;
3696+ show_tab ( task , prev ) ;
3697+ }
3698+ }
3699+
3700+ // Ctrl+Alt+Shift+W = close current tab
3701+ if ( e . ctrlKey && e . altKey && e . shiftKey && e . key . toLowerCase ( ) === "w" ) {
3702+ e . preventDefault ( ) ;
3703+ if ( current ) {
3704+ close_editor ( task , current ) ;
3705+ }
3706+ }
3707+ } ) ;
3708+ }
3709+ // call this after you create the Monaco editor (e.g. after task.editor is initialized)
3710+ function bindMonacoTabShortcuts ( editor , task ) {
3711+ if ( ! editor || ! window . monaco ) return ;
3712+
3713+ function changeTabByOffset ( offset ) {
3714+ // use DOM order so tab order matches what user expects
3715+ const ids = $ ( '#task-tabs li button' ) . map ( function ( ) { return $ ( this ) . attr ( 'id' ) ; } ) . get ( ) ;
3716+ const current = $ ( '#task-tabs li.active button' ) . attr ( 'id' ) ;
3717+ let idx = ids . indexOf ( current ) ;
3718+ if ( idx === - 1 ) idx = 0 ;
3719+ const next = ids [ ( idx + offset + ids . length ) % ids . length ] ;
3720+ if ( next ) show_tab ( task , next ) ;
3721+ }
3722+
3723+ // Ctrl/Cmd + Alt + Right
3724+ editor . addCommand (
3725+ monaco . KeyMod . CtrlCmd | monaco . KeyMod . Alt | monaco . KeyCode . RightArrow ,
3726+ ( ) => { changeTabByOffset ( 1 ) ; }
3727+ ) ;
3728+
3729+ // Ctrl/Cmd + Alt + Left
3730+ editor . addCommand (
3731+ monaco . KeyMod . CtrlCmd | monaco . KeyMod . Alt | monaco . KeyCode . LeftArrow ,
3732+ ( ) => { changeTabByOffset ( - 1 ) ; }
3733+ ) ;
3734+
3735+ // Ctrl/Cmd + Alt + Shift + W -> close tab
3736+ editor . addCommand (
3737+ monaco . KeyMod . CtrlCmd | monaco . KeyMod . Alt | monaco . KeyMod . Shift | monaco . KeyCode . KEY_W ,
3738+ ( ) => {
3739+ const current = $ ( '#task-tabs li.active button' ) . attr ( 'id' ) ;
3740+ if ( current ) close_editor ( task , current ) ;
3741+ }
3742+ ) ;
3743+ }
3744+ function init_global_tab_shortcuts ( task ) {
3745+ function changeTabByOffset ( offset ) {
3746+ const ids = $ ( '#task-tabs li button' ) . map ( function ( ) { return $ ( this ) . attr ( 'id' ) ; } ) . get ( ) ;
3747+ const current = $ ( '#task-tabs li.active button' ) . attr ( 'id' ) ;
3748+ let idx = ids . indexOf ( current ) ;
3749+ if ( idx === - 1 ) idx = 0 ;
3750+ const next = ids [ ( idx + offset + ids . length ) % ids . length ] ;
3751+ if ( next ) show_tab ( task , next ) ;
3752+ }
3753+
3754+ window . addEventListener ( 'keydown' , function ( e ) {
3755+ // debug tip: uncomment to inspect real events
3756+ console . log ( 'global keydown' , e . code , e . key , 'ctrl' , e . ctrlKey , 'alt' , e . altKey , 'meta' , e . metaKey , 'shift' , e . shiftKey ) ;
3757+
3758+ // ignore if user is typing in a normal input/textarea (but still allow if focus is Monaco)
3759+ const active = document . activeElement ;
3760+ const isPlainInput = active && ( active . tagName === 'INPUT' || active . tagName === 'TEXTAREA' ) ;
3761+ if ( isPlainInput && ! active . classList . contains ( 'monaco-editor' ) ) return ;
3762+
3763+ // Use e.code for layout-independence (ArrowLeft/Right, KeyW)
3764+ const ctrlOrCmd = e . ctrlKey || e . metaKey ;
3765+
3766+ // Ctrl/Cmd + Alt + Right
3767+ if ( ctrlOrCmd && e . altKey && e . code === 'ArrowRight' ) {
3768+ console . log ( "Navigating to tab id:" ) ;
3769+
3770+ e . preventDefault ( ) ;
3771+ changeTabByOffset ( 1 ) ;
3772+ return ;
3773+ }
3774+
3775+ // Ctrl/Cmd + Alt + Left
3776+ if ( ctrlOrCmd && e . altKey && e . code === 'ArrowLeft' ) {
3777+ console . log ( "Navigating to tab id:" ) ;
3778+
3779+ e . preventDefault ( ) ;
3780+ changeTabByOffset ( - 1 ) ;
3781+ return ;
3782+ }
3783+
3784+ // Ctrl/Cmd + Alt + Shift + W -> close
3785+ if ( ctrlOrCmd && e . altKey && e . shiftKey && e . code === 'KeyW' ) {
3786+ e . preventDefault ( ) ;
3787+ const current = $ ( '#task-tabs li.active button' ) . attr ( 'id' ) ;
3788+ if ( current ) close_editor ( task , current ) ;
3789+ return ;
3790+ }
3791+ } , true ) ; // <-- capture = true
3792+ }
3793+ window . addEventListener ( 'keydown' , function ( e ) {
3794+ if ( e . ctrlKey && e . altKey && e . shiftKey && e . code === 'KeyW' ) {
3795+ e . preventDefault ( ) ;
3796+ let current = $ ( '#task-tabs button.nav-link.active' ) . attr ( 'id' ) ; // ✅ fixed selector
3797+ // console.log("Closing tab id:", current);
3798+ if ( current ) {
3799+ close_editor ( task , current ) ;
3800+ } else {
3801+ console . warn ( "No active tab found!" ) ;
3802+ }
3803+ }
3804+ } , true ) ;
3805+
3806+
3807+ function switchTab ( offset ) {
3808+ let $buttons = $ ( '#task-tabs button.nav-link' ) ; // all tab buttons
3809+ let $active = $ ( '#task-tabs button.nav-link.active' ) ; // active tab button
3810+ let idx = $buttons . index ( $active ) ;
3811+ if ( idx === - 1 ) return ;
3812+
3813+ let nextIdx = ( idx + offset + $buttons . length ) % $buttons . length ;
3814+ let nextId = $buttons . eq ( nextIdx ) . attr ( 'id' ) ;
3815+ if ( nextId ) show_tab ( task , nextId ) ;
3816+ console . log ( "Navigating to tab id:" ) ;
3817+
3818+ }
3819+
3820+ window . addEventListener ( 'keydown' , function ( e ) {
3821+ const ctrlOrCmd = e . ctrlKey || e . metaKey ;
3822+
3823+ if ( ctrlOrCmd && e . altKey && e . code === 'ArrowRight' ) {
3824+ console . log ( "Navigating to tab id:" ) ;
3825+
3826+ e . preventDefault ( ) ;
3827+ switchTab ( 1 ) ;
3828+ }
3829+
3830+ if ( ctrlOrCmd && e . altKey && e . code === 'ArrowLeft' ) {
3831+ e . preventDefault ( ) ;
3832+ switchTab ( - 1 ) ;
3833+ }
3834+ } , true ) ;
36753835
36763836 function show_tab ( task , tag ) {
36773837 $ ( 'ul#task-tabs li button' ) . removeClass ( 'active' ) ;
@@ -3727,7 +3887,7 @@ function Events04() { // app_builder.catalogs.sys_code_editor
37273887 }
37283888
37293889 function close_query ( task , tag , callback ) {
3730- if ( get_modified ( task ) ) {
3890+ if ( ! get_modified ( task ) ) {
37313891 task . yes_no_cancel ( task . language . save_changes ,
37323892 function ( ) {
37333893 save_edit ( task , tag ) ;
@@ -4353,6 +4513,10 @@ function Events04() { // app_builder.catalogs.sys_code_editor
43534513 window . open ( encodeURI ( url ) ) ;
43544514 }
43554515 this . init_tabs = init_tabs ;
4516+ this . init_tab_shortcuts = init_tab_shortcuts ;
4517+ this . bindMonacoTabShortcuts = bindMonacoTabShortcuts ;
4518+ this . init_global_tab_shortcuts = init_global_tab_shortcuts ;
4519+ this . switchTab = switchTab ;
43564520 this . show_tab = show_tab ;
43574521 this . show_editor = show_editor ;
43584522 this . close_editor = close_editor ;
0 commit comments