Skip to content

Commit f0b47a3

Browse files
Merge pull request #2460 from sensei-hacker/fix-javascript-programming-issues
Fix JavaScript Programming tab issues (#2453)
2 parents c23ffd1 + 6681f4e commit f0b47a3

7 files changed

Lines changed: 170 additions & 27 deletions

File tree

js/configurator_main.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,21 @@ $(function() {
138138
return;
139139
}
140140

141+
// Check for unsaved changes in current tab before switching
142+
if (GUI.active_tab === 'javascript_programming' &&
143+
TABS.javascript_programming &&
144+
TABS.javascript_programming.isDirty) {
145+
console.log('[Tab Switch] Checking for unsaved changes in JavaScript Programming tab');
146+
const confirmMsg = i18n.getMessage('unsavedChanges') ||
147+
'You have unsaved changes. Leave anyway?';
148+
149+
if (!confirm(confirmMsg)) {
150+
console.log('[Tab Switch] User cancelled tab switch');
151+
return; // Cancel tab switch
152+
}
153+
console.log('[Tab Switch] User confirmed tab switch');
154+
}
155+
141156
GUI.tab_switch_in_progress = true;
142157

143158
GUI.tab_switch_cleanup(function () {

js/serial_backend.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,28 @@ var SerialBackend = (function () {
203203
CONFIGURATOR.connection.connect(selected_port, {bitrate: selected_baud}, privateScope.onOpen);
204204
}
205205
} else {
206+
// Check for unsaved changes in JavaScript Programming tab
207+
if (GUI.active_tab === 'javascript_programming' &&
208+
TABS.javascript_programming &&
209+
TABS.javascript_programming.isDirty) {
210+
console.log('[Disconnect] Checking for unsaved changes in JavaScript Programming tab');
211+
const confirmMsg = i18n.getMessage('unsavedChanges') ||
212+
'You have unsaved changes. Leave anyway?';
213+
214+
if (!confirm(confirmMsg)) {
215+
console.log('[Disconnect] User cancelled disconnect due to unsaved changes');
216+
return; // Cancel disconnect
217+
}
218+
console.log('[Disconnect] User confirmed, proceeding with disconnect');
219+
// Clear isDirty flag so tab switch during disconnect doesn't show warning again
220+
TABS.javascript_programming.isDirty = false;
221+
}
222+
206223
if (this.isDemoRunning) {
207224
SITLProcess.stop();
208225
this.isDemoRunning = false;
209226
}
210-
227+
211228
var wasConnected = CONFIGURATOR.connectionValid;
212229

213230
timeout.killAll();
Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/**
22
* INAV API Definitions - Main Export
3-
*
3+
*
44
* Location: js/transpiler/api/definitions/index.js
5-
*
5+
*
66
* Exports all API definitions as a single object.
77
* This is the SINGLE SOURCE OF TRUTH for INAV JavaScript API.
88
*/
@@ -15,23 +15,14 @@ import waypoint from './waypoint.js';
1515
import pid from './pid.js';
1616
import helpers from './helpers.js';
1717
import events from './events.js';
18+
import override from './override.js';
1819

1920
export default {
20-
// Read-only telemetry and state
2121
flight,
22-
23-
// RC receiver channels
2422
rc,
25-
26-
// Waypoint navigation
2723
waypoint,
28-
29-
// Programming PID controllers
3024
pid,
31-
32-
// Helper functions (min, max, abs, sin, cos, etc.)
3325
helpers,
34-
35-
// Event handlers (on, sticky, etc.)
36-
events
26+
events,
27+
override
3728
};
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/**
2+
* INAV Override API Definition
3+
*
4+
* Location: js/transpiler/api/definitions/override.js
5+
*
6+
* Writable override operations for flight control.
7+
* Source: src/main/programming/logic_condition.h
8+
*/
9+
10+
'use strict';
11+
12+
import { OPERATION } from '../../transpiler/inav_constants.js';
13+
14+
export default {
15+
// Throttle Control
16+
throttleScale: {
17+
type: 'number',
18+
unit: '%',
19+
desc: 'Scale throttle output (0-100%)',
20+
readonly: false,
21+
range: [0, 100],
22+
inavOperation: OPERATION.OVERRIDE_THROTTLE_SCALE
23+
},
24+
25+
throttle: {
26+
type: 'number',
27+
unit: 'us',
28+
desc: 'Direct throttle override in microseconds',
29+
readonly: false,
30+
range: [1000, 2000],
31+
inavOperation: OPERATION.OVERRIDE_THROTTLE
32+
},
33+
34+
// VTX Control (nested object)
35+
vtx: {
36+
type: 'object',
37+
desc: 'Video transmitter control',
38+
properties: {
39+
power: {
40+
type: 'number',
41+
desc: 'VTX power level (0-4)',
42+
readonly: false,
43+
range: [0, 4],
44+
inavOperation: OPERATION.SET_VTX_POWER_LEVEL
45+
},
46+
47+
band: {
48+
type: 'number',
49+
desc: 'VTX frequency band (0-5)',
50+
readonly: false,
51+
range: [0, 5],
52+
inavOperation: OPERATION.SET_VTX_BAND
53+
},
54+
55+
channel: {
56+
type: 'number',
57+
desc: 'VTX channel (1-8)',
58+
readonly: false,
59+
range: [1, 8],
60+
inavOperation: OPERATION.SET_VTX_CHANNEL
61+
}
62+
}
63+
},
64+
65+
// Arming Control
66+
armSafety: {
67+
type: 'boolean',
68+
desc: 'Override arm safety switch',
69+
readonly: false,
70+
inavOperation: OPERATION.OVERRIDE_ARMING_SAFETY
71+
},
72+
73+
// OSD Override
74+
osdLayout: {
75+
type: 'number',
76+
desc: 'Set OSD layout (0-3)',
77+
readonly: false,
78+
range: [0, 3],
79+
inavOperation: OPERATION.SET_OSD_LAYOUT
80+
},
81+
82+
// RC Channel Override
83+
rcChannel: {
84+
type: 'function',
85+
desc: 'Override RC channel value. Usage: override.rcChannel(channel, value)',
86+
readonly: false,
87+
inavOperation: OPERATION.RC_CHANNEL_OVERRIDE
88+
// Note: This requires special handling in codegen as it takes channel number as operandA
89+
},
90+
91+
// Loiter Override
92+
loiterRadius: {
93+
type: 'number',
94+
unit: 'cm',
95+
desc: 'Override loiter radius in centimeters',
96+
readonly: false,
97+
range: [0, 100000],
98+
inavOperation: OPERATION.LOITER_OVERRIDE
99+
},
100+
101+
// Min Ground Speed Override
102+
minGroundSpeed: {
103+
type: 'number',
104+
unit: 'm/s',
105+
desc: 'Override minimum ground speed',
106+
readonly: false,
107+
range: [0, 150],
108+
inavOperation: OPERATION.OVERRIDE_MIN_GROUND_SPEED
109+
}
110+
111+
// Note: Flight axis angle/rate overrides (operations 45, 46) would need
112+
// special syntax since they require specifying the axis (0=roll, 1=pitch, 2=yaw)
113+
// These should probably be exposed as:
114+
// override.flightAxis.angle(axis, degrees)
115+
// override.flightAxis.rate(axis, degreesPerSecond)
116+
};

js/transpiler/api/types.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ declare namespace inav {
4242
dts += generateInterfaceFromDefinition('flight', apiDefinitions.flight);
4343
dts += generateInterfaceFromDefinition('rc', apiDefinitions.rc);
4444
dts += generateInterfaceFromDefinition('waypoint', apiDefinitions.waypoint);
45+
dts += generateInterfaceFromDefinition('override', apiDefinitions.override);
4546

4647
// Add special types
4748
dts += `

js/transpiler/editor/monaco_loader.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ function initializeMonacoEditor(monaco, containerId, options = {}) {
6060
noEmit: true,
6161
esModuleInterop: true,
6262
allowJs: true,
63-
checkJs: false
63+
checkJs: false,
64+
lib: ['es2020'] // Only ES2020 core library (excludes DOM/browser APIs like navigator)
6465
});
6566

6667
console.log('Monaco Editor initialized');

tabs/javascript_programming.js

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ TABS.javascript_programming = {
7171

7272
self.loadFromFC(function() {
7373
self.isDirty = false;
74+
75+
// Set up dirty tracking AFTER initial load to avoid marking as dirty during decompilation
76+
self.editor.onDidChangeModelContent(function() {
77+
if (!self.isDirty) {
78+
console.log('[JavaScript Programming] Editor marked as dirty (unsaved changes)');
79+
}
80+
self.isDirty = true;
81+
});
82+
7483
GUI.content_ready(callback);
7584
});
7685
} catch (error) {
@@ -724,19 +733,12 @@ if (flight.homeDistance > 100) {
724733
},
725734

726735
cleanup: function (callback) {
727-
const self = this;
728-
729-
// Check for unsaved changes
730-
if (this.isDirty) {
731-
const confirmMsg = i18n.getMessage('unsavedChanges') ||
732-
'You have unsaved changes. Leave anyway?';
733-
if (!confirm(confirmMsg)) {
734-
// Cancel navigation
735-
return;
736-
}
737-
}
736+
console.log('[JavaScript Programming] cleanup() - disposing editor');
738737

739738
// Dispose Monaco editor
739+
// Note: Unsaved changes are checked BEFORE cleanup() is called:
740+
// - For disconnect: in serial_backend.js
741+
// - For tab switch: in configurator_main.js
740742
if (this.editor && this.editor.dispose) {
741743
this.editor.dispose();
742744
this.editor = null;

0 commit comments

Comments
 (0)