Skip to content

Commit 2b096f9

Browse files
author
platipusica
committed
7.0.72
1 parent a8ad722 commit 2b096f9

File tree

11 files changed

+497
-5
lines changed

11 files changed

+497
-5
lines changed

builder/admin.sqlite

0 Bytes
Binary file not shown.

builder/builder.sqlite

0 Bytes
Binary file not shown.

builder/jam_files/js/admin.js

Lines changed: 165 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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;

builder/js/app_builder.js

Lines changed: 165 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ function Events1() { // 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 Events14() { // 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 Events14() { // 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 Events14() { // 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;

demo/admin.sqlite

0 Bytes
Binary file not shown.

demo/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<title>Jam.py demo</title>
77
<meta name="viewport" content="width=device-width, initial-scale=1.0">
88
<link rel="icon" href="static/img/j.png" type="image/png">
9-
<link href="jam/css/bs5/bootstrap.css" rel="stylesheet">
9+
<link href="jam/css/bs5/bootstrap-darkly.css " rel="stylesheet">
1010
<link href="jam/css/bs5/bootstrap-icons.css" rel="stylesheet">
1111
<!--<link href="jam/css/datepicker.css" rel="stylesheet">-->
1212
<link href="jam/css/zebra_datepicker/bootstrap/zebra_datepicker.min.css" rel="stylesheet">

demo/langs.sqlite

0 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)