ivoryos 1.0.9__py3-none-any.whl → 1.4.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- docs/source/conf.py +84 -0
- ivoryos/__init__.py +17 -207
- ivoryos/app.py +154 -0
- ivoryos/config.py +1 -0
- ivoryos/optimizer/ax_optimizer.py +191 -0
- ivoryos/optimizer/base_optimizer.py +84 -0
- ivoryos/optimizer/baybe_optimizer.py +193 -0
- ivoryos/optimizer/nimo_optimizer.py +173 -0
- ivoryos/optimizer/registry.py +11 -0
- ivoryos/routes/auth/auth.py +43 -14
- ivoryos/routes/auth/templates/change_password.html +32 -0
- ivoryos/routes/control/control.py +101 -366
- ivoryos/routes/control/control_file.py +33 -0
- ivoryos/routes/control/control_new_device.py +152 -0
- ivoryos/routes/control/templates/controllers.html +193 -0
- ivoryos/routes/control/templates/controllers_new.html +112 -0
- ivoryos/routes/control/utils.py +40 -0
- ivoryos/routes/data/data.py +197 -0
- ivoryos/routes/data/templates/components/step_card.html +78 -0
- ivoryos/routes/{database/templates/database → data/templates}/workflow_database.html +14 -8
- ivoryos/routes/data/templates/workflow_view.html +360 -0
- ivoryos/routes/design/__init__.py +4 -0
- ivoryos/routes/design/design.py +348 -657
- ivoryos/routes/design/design_file.py +68 -0
- ivoryos/routes/design/design_step.py +171 -0
- ivoryos/routes/design/templates/components/action_form.html +53 -0
- ivoryos/routes/design/templates/components/actions_panel.html +25 -0
- ivoryos/routes/design/templates/components/autofill_toggle.html +10 -0
- ivoryos/routes/design/templates/components/canvas.html +5 -0
- ivoryos/routes/design/templates/components/canvas_footer.html +9 -0
- ivoryos/routes/design/templates/components/canvas_header.html +75 -0
- ivoryos/routes/design/templates/components/canvas_main.html +39 -0
- ivoryos/routes/design/templates/components/deck_selector.html +10 -0
- ivoryos/routes/design/templates/components/edit_action_form.html +53 -0
- ivoryos/routes/design/templates/components/info_modal.html +318 -0
- ivoryos/routes/design/templates/components/instruments_panel.html +88 -0
- ivoryos/routes/design/templates/components/modals/drop_modal.html +17 -0
- ivoryos/routes/design/templates/components/modals/json_modal.html +22 -0
- ivoryos/routes/design/templates/components/modals/new_script_modal.html +17 -0
- ivoryos/routes/design/templates/components/modals/rename_modal.html +23 -0
- ivoryos/routes/design/templates/components/modals/saveas_modal.html +27 -0
- ivoryos/routes/design/templates/components/modals.html +6 -0
- ivoryos/routes/design/templates/components/python_code_overlay.html +56 -0
- ivoryos/routes/design/templates/components/sidebar.html +15 -0
- ivoryos/routes/design/templates/components/text_to_code_panel.html +20 -0
- ivoryos/routes/design/templates/experiment_builder.html +44 -0
- ivoryos/routes/execute/__init__.py +0 -0
- ivoryos/routes/execute/execute.py +377 -0
- ivoryos/routes/execute/execute_file.py +78 -0
- ivoryos/routes/execute/templates/components/error_modal.html +20 -0
- ivoryos/routes/execute/templates/components/logging_panel.html +56 -0
- ivoryos/routes/execute/templates/components/progress_panel.html +27 -0
- ivoryos/routes/execute/templates/components/run_panel.html +9 -0
- ivoryos/routes/execute/templates/components/run_tabs.html +60 -0
- ivoryos/routes/execute/templates/components/tab_bayesian.html +520 -0
- ivoryos/routes/execute/templates/components/tab_configuration.html +383 -0
- ivoryos/routes/execute/templates/components/tab_repeat.html +18 -0
- ivoryos/routes/execute/templates/experiment_run.html +30 -0
- ivoryos/routes/library/__init__.py +0 -0
- ivoryos/routes/library/library.py +157 -0
- ivoryos/routes/{database/templates/database/scripts_database.html → library/templates/library.html} +32 -23
- ivoryos/routes/main/main.py +31 -3
- ivoryos/routes/main/templates/{main/home.html → home.html} +4 -4
- ivoryos/server.py +180 -0
- ivoryos/socket_handlers.py +52 -0
- ivoryos/static/ivoryos_logo.png +0 -0
- ivoryos/static/js/action_handlers.js +384 -0
- ivoryos/static/js/db_delete.js +23 -0
- ivoryos/static/js/script_metadata.js +39 -0
- ivoryos/static/js/socket_handler.js +40 -5
- ivoryos/static/js/sortable_design.js +107 -56
- ivoryos/static/js/ui_state.js +114 -0
- ivoryos/templates/base.html +67 -8
- ivoryos/utils/bo_campaign.py +180 -3
- ivoryos/utils/client_proxy.py +267 -36
- ivoryos/utils/db_models.py +300 -65
- ivoryos/utils/decorators.py +34 -0
- ivoryos/utils/form.py +63 -29
- ivoryos/utils/global_config.py +34 -1
- ivoryos/utils/nest_script.py +314 -0
- ivoryos/utils/py_to_json.py +295 -0
- ivoryos/utils/script_runner.py +599 -165
- ivoryos/utils/serilize.py +201 -0
- ivoryos/utils/task_runner.py +71 -21
- ivoryos/utils/utils.py +50 -6
- ivoryos/version.py +1 -1
- ivoryos-1.4.4.dist-info/METADATA +263 -0
- ivoryos-1.4.4.dist-info/RECORD +119 -0
- {ivoryos-1.0.9.dist-info → ivoryos-1.4.4.dist-info}/WHEEL +1 -1
- {ivoryos-1.0.9.dist-info → ivoryos-1.4.4.dist-info}/top_level.txt +1 -0
- tests/unit/test_type_conversion.py +42 -0
- tests/unit/test_util.py +3 -0
- ivoryos/routes/control/templates/control/controllers.html +0 -78
- ivoryos/routes/control/templates/control/controllers_home.html +0 -55
- ivoryos/routes/control/templates/control/controllers_new.html +0 -89
- ivoryos/routes/database/database.py +0 -306
- ivoryos/routes/database/templates/database/step_card.html +0 -7
- ivoryos/routes/database/templates/database/workflow_view.html +0 -130
- ivoryos/routes/design/templates/design/experiment_builder.html +0 -521
- ivoryos/routes/design/templates/design/experiment_run.html +0 -558
- ivoryos-1.0.9.dist-info/METADATA +0 -218
- ivoryos-1.0.9.dist-info/RECORD +0 -61
- /ivoryos/routes/auth/templates/{auth/login.html → login.html} +0 -0
- /ivoryos/routes/auth/templates/{auth/signup.html → signup.html} +0 -0
- /ivoryos/routes/{database → data}/__init__.py +0 -0
- /ivoryos/routes/main/templates/{main/help.html → help.html} +0 -0
- {ivoryos-1.0.9.dist-info → ivoryos-1.4.4.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// STATE MANAGEMENT
|
|
3
|
+
// ============================================================================
|
|
4
|
+
|
|
5
|
+
let previousHtmlState = null; // Store previous instrument panel state
|
|
6
|
+
let lastFocusedElement = null; // Track focus for modal management
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// MODE & BATCH MANAGEMENT
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
function getMode() {
|
|
13
|
+
return sessionStorage.getItem("mode") || "single";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function setMode(mode, triggerUpdate = true) {
|
|
17
|
+
sessionStorage.setItem("mode", mode);
|
|
18
|
+
|
|
19
|
+
const modeButtons = document.querySelectorAll(".mode-toggle");
|
|
20
|
+
const batchOptions = document.getElementById("batch-options");
|
|
21
|
+
|
|
22
|
+
modeButtons.forEach(b => b.classList.toggle("active", b.dataset.mode === mode));
|
|
23
|
+
|
|
24
|
+
if (batchOptions) {
|
|
25
|
+
batchOptions.style.display = (mode === "batch") ? "inline-flex" : "none";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (triggerUpdate) updateCode();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getBatch() {
|
|
32
|
+
return sessionStorage.getItem("batch") || "sample";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function setBatch(batch, triggerUpdate = true) {
|
|
36
|
+
sessionStorage.setItem("batch", batch);
|
|
37
|
+
|
|
38
|
+
const batchButtons = document.querySelectorAll(".batch-toggle");
|
|
39
|
+
batchButtons.forEach(b => b.classList.toggle("active", b.dataset.batch === batch));
|
|
40
|
+
|
|
41
|
+
if (triggerUpdate) updateCode();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ============================================================================
|
|
45
|
+
// CODE OVERLAY MANAGEMENT
|
|
46
|
+
// ============================================================================
|
|
47
|
+
|
|
48
|
+
async function updateCode() {
|
|
49
|
+
try {
|
|
50
|
+
const params = new URLSearchParams({ mode: getMode(), batch: getBatch() });
|
|
51
|
+
const res = await fetch(scriptCompileUrl + "?" + params.toString());
|
|
52
|
+
if (!res.ok) return;
|
|
53
|
+
|
|
54
|
+
const data = await res.json();
|
|
55
|
+
const codeElem = document.getElementById("python-code");
|
|
56
|
+
|
|
57
|
+
codeElem.removeAttribute("data-highlighted"); // Reset highlight.js flag
|
|
58
|
+
codeElem.textContent = data.code['script'] || "# No code found";
|
|
59
|
+
|
|
60
|
+
if (window.hljs) {
|
|
61
|
+
hljs.highlightElement(codeElem);
|
|
62
|
+
}
|
|
63
|
+
} catch (err) {
|
|
64
|
+
console.error("Error updating code:", err);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function initializeCodeOverlay() {
|
|
69
|
+
const codeElem = document.getElementById("python-code");
|
|
70
|
+
const copyBtn = document.getElementById("copy-code");
|
|
71
|
+
const downloadBtn = document.getElementById("download-code");
|
|
72
|
+
|
|
73
|
+
if (!copyBtn || !downloadBtn) return; // Elements don't exist
|
|
74
|
+
|
|
75
|
+
// Remove old listeners by cloning (prevents duplicate bindings)
|
|
76
|
+
const newCopyBtn = copyBtn.cloneNode(true);
|
|
77
|
+
const newDownloadBtn = downloadBtn.cloneNode(true);
|
|
78
|
+
copyBtn.parentNode.replaceChild(newCopyBtn, copyBtn);
|
|
79
|
+
downloadBtn.parentNode.replaceChild(newDownloadBtn, downloadBtn);
|
|
80
|
+
|
|
81
|
+
// Copy to clipboard
|
|
82
|
+
newCopyBtn.addEventListener("click", () => {
|
|
83
|
+
navigator.clipboard.writeText(codeElem.textContent)
|
|
84
|
+
.then(() => alert("Code copied!"))
|
|
85
|
+
.catch(err => console.error("Failed to copy", err));
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Download current code
|
|
89
|
+
newDownloadBtn.addEventListener("click", () => {
|
|
90
|
+
const blob = new Blob([codeElem.textContent], { type: "text/plain" });
|
|
91
|
+
const url = URL.createObjectURL(blob);
|
|
92
|
+
const a = document.createElement("a");
|
|
93
|
+
a.href = url;
|
|
94
|
+
a.download = "script.py";
|
|
95
|
+
a.click();
|
|
96
|
+
URL.revokeObjectURL(url);
|
|
97
|
+
});
|
|
98
|
+
updateCode();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ============================================================================
|
|
102
|
+
// UI UPDATE FUNCTIONS
|
|
103
|
+
// ============================================================================
|
|
104
|
+
|
|
105
|
+
function updateActionCanvas(html) {
|
|
106
|
+
document.getElementById("canvas-action-wrapper").innerHTML = html;
|
|
107
|
+
initializeCanvas();
|
|
108
|
+
|
|
109
|
+
const mode = getMode();
|
|
110
|
+
const batch = getBatch();
|
|
111
|
+
|
|
112
|
+
// Rebind event handlers for mode/batch toggles
|
|
113
|
+
document.querySelectorAll(".mode-toggle").forEach(btn => {
|
|
114
|
+
btn.addEventListener("click", () => setMode(btn.dataset.mode));
|
|
115
|
+
});
|
|
116
|
+
document.querySelectorAll(".batch-toggle").forEach(btn => {
|
|
117
|
+
btn.addEventListener("click", () => setBatch(btn.dataset.batch));
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Restore toggle UI state (without triggering updates)
|
|
121
|
+
setMode(mode, false);
|
|
122
|
+
setBatch(batch, false);
|
|
123
|
+
|
|
124
|
+
// Reinitialize code overlay buttons
|
|
125
|
+
initializeCodeOverlay();
|
|
126
|
+
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function updateInstrumentPanel(link) {
|
|
130
|
+
const url = link.dataset.getUrl;
|
|
131
|
+
|
|
132
|
+
fetch(url)
|
|
133
|
+
.then(res => res.json())
|
|
134
|
+
.then(data => {
|
|
135
|
+
if (data.html) {
|
|
136
|
+
document.getElementById("sidebar-wrapper").innerHTML = data.html;
|
|
137
|
+
initializeDragHandlers();
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
.catch(err => console.error("Error updating instrument panel:", err));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ============================================================================
|
|
144
|
+
// WORKFLOW MANAGEMENT
|
|
145
|
+
// ============================================================================
|
|
146
|
+
|
|
147
|
+
function saveWorkflow(link) {
|
|
148
|
+
const url = link.dataset.postUrl;
|
|
149
|
+
|
|
150
|
+
fetch(url, {
|
|
151
|
+
method: 'POST',
|
|
152
|
+
headers: {
|
|
153
|
+
'Content-Type': 'application/json'
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
.then(res => res.json())
|
|
157
|
+
.then(data => {
|
|
158
|
+
if (data.success) {
|
|
159
|
+
window.location.reload();
|
|
160
|
+
} else {
|
|
161
|
+
alert("Failed to save workflow: " + data.error);
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
.catch(err => {
|
|
165
|
+
console.error("Save error:", err);
|
|
166
|
+
alert("Something went wrong.");
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function clearDraft() {
|
|
171
|
+
fetch(scriptDeleteUrl, {
|
|
172
|
+
method: "DELETE",
|
|
173
|
+
headers: {
|
|
174
|
+
"Content-Type": "application/json",
|
|
175
|
+
},
|
|
176
|
+
})
|
|
177
|
+
.then(res => res.json())
|
|
178
|
+
.then(data => {
|
|
179
|
+
if (data.success) {
|
|
180
|
+
window.location.reload();
|
|
181
|
+
} else {
|
|
182
|
+
alert("Failed to clear draft");
|
|
183
|
+
}
|
|
184
|
+
})
|
|
185
|
+
.catch(error => console.error("Failed to clear draft", error));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ============================================================================
|
|
189
|
+
// ACTION MANAGEMENT (CRUD Operations)
|
|
190
|
+
// ============================================================================
|
|
191
|
+
|
|
192
|
+
function addMethodToDesign(event, form) {
|
|
193
|
+
event.preventDefault();
|
|
194
|
+
|
|
195
|
+
const formData = new FormData(form);
|
|
196
|
+
|
|
197
|
+
fetch(form.action, {
|
|
198
|
+
method: 'POST',
|
|
199
|
+
body: formData
|
|
200
|
+
})
|
|
201
|
+
.then(response => response.json())
|
|
202
|
+
.then(data => {
|
|
203
|
+
if (data.success) {
|
|
204
|
+
updateActionCanvas(data.html);
|
|
205
|
+
hideModal();
|
|
206
|
+
} else {
|
|
207
|
+
alert("Failed to add method: " + data.error);
|
|
208
|
+
}
|
|
209
|
+
})
|
|
210
|
+
.catch(error => console.error('Error:', error));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function editAction(uuid) {
|
|
214
|
+
if (!uuid) {
|
|
215
|
+
console.error('Invalid UUID');
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Store current state for rollback
|
|
220
|
+
previousHtmlState = document.getElementById('instrument-panel').innerHTML;
|
|
221
|
+
|
|
222
|
+
fetch(scriptStepUrl.replace('0', uuid), {
|
|
223
|
+
method: 'GET',
|
|
224
|
+
headers: {
|
|
225
|
+
'Content-Type': 'application/json'
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
.then(response => {
|
|
229
|
+
if (!response.ok) {
|
|
230
|
+
return response.json().then(err => {
|
|
231
|
+
if (err.warning) {
|
|
232
|
+
alert(err.warning);
|
|
233
|
+
}
|
|
234
|
+
// Restore panel so user isn't stuck
|
|
235
|
+
if (previousHtmlState) {
|
|
236
|
+
document.getElementById('instrument-panel').innerHTML = previousHtmlState;
|
|
237
|
+
previousHtmlState = null;
|
|
238
|
+
}
|
|
239
|
+
throw new Error("Step fetch failed: " + response.status);
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
return response.text();
|
|
243
|
+
})
|
|
244
|
+
.then(html => {
|
|
245
|
+
document.getElementById('instrument-panel').innerHTML = html;
|
|
246
|
+
|
|
247
|
+
// Set up back button
|
|
248
|
+
const backButton = document.getElementById('back');
|
|
249
|
+
if (backButton) {
|
|
250
|
+
backButton.addEventListener('click', function(e) {
|
|
251
|
+
e.preventDefault();
|
|
252
|
+
if (previousHtmlState) {
|
|
253
|
+
document.getElementById('instrument-panel').innerHTML = previousHtmlState;
|
|
254
|
+
previousHtmlState = null;
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
})
|
|
259
|
+
.catch(error => console.error('Error:', error));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function submitEditForm(event) {
|
|
263
|
+
event.preventDefault();
|
|
264
|
+
|
|
265
|
+
const form = event.target;
|
|
266
|
+
const formData = new FormData(form);
|
|
267
|
+
|
|
268
|
+
fetch(form.action, {
|
|
269
|
+
method: 'POST',
|
|
270
|
+
body: formData
|
|
271
|
+
})
|
|
272
|
+
.then(response => response.text())
|
|
273
|
+
.then(html => {
|
|
274
|
+
if (html) {
|
|
275
|
+
updateActionCanvas(html);
|
|
276
|
+
|
|
277
|
+
// Restore previous instrument panel state
|
|
278
|
+
if (previousHtmlState) {
|
|
279
|
+
document.getElementById('instrument-panel').innerHTML = previousHtmlState;
|
|
280
|
+
previousHtmlState = null;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Check for warnings
|
|
284
|
+
showWarningIfExists(html);
|
|
285
|
+
}
|
|
286
|
+
})
|
|
287
|
+
.catch(error => console.error('Error:', error));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function duplicateAction(uuid) {
|
|
291
|
+
if (!uuid) {
|
|
292
|
+
console.error('Invalid UUID');
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
fetch(scriptStepDupUrl.replace('0', uuid), {
|
|
297
|
+
method: 'POST',
|
|
298
|
+
headers: {
|
|
299
|
+
'Content-Type': 'application/json'
|
|
300
|
+
}
|
|
301
|
+
})
|
|
302
|
+
.then(response => response.text())
|
|
303
|
+
.then(html => {
|
|
304
|
+
updateActionCanvas(html);
|
|
305
|
+
showWarningIfExists(html);
|
|
306
|
+
})
|
|
307
|
+
.catch(error => console.error('Error:', error));
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function deleteAction(uuid) {
|
|
311
|
+
if (!uuid) {
|
|
312
|
+
console.error('Invalid UUID');
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
fetch(scriptStepUrl.replace('0', uuid), {
|
|
317
|
+
method: 'DELETE',
|
|
318
|
+
headers: {
|
|
319
|
+
'Content-Type': 'application/json'
|
|
320
|
+
}
|
|
321
|
+
})
|
|
322
|
+
.then(response => response.text())
|
|
323
|
+
.then(html => {
|
|
324
|
+
updateActionCanvas(html);
|
|
325
|
+
showWarningIfExists(html);
|
|
326
|
+
})
|
|
327
|
+
.catch(error => console.error('Error:', error));
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ============================================================================
|
|
331
|
+
// MODAL MANAGEMENT
|
|
332
|
+
// ============================================================================
|
|
333
|
+
|
|
334
|
+
function hideModal() {
|
|
335
|
+
if (document.activeElement) {
|
|
336
|
+
document.activeElement.blur();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
$('#dropModal').modal('hide');
|
|
340
|
+
|
|
341
|
+
if (lastFocusedElement) {
|
|
342
|
+
lastFocusedElement.focus();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// ============================================================================
|
|
347
|
+
// UTILITY FUNCTIONS
|
|
348
|
+
// ============================================================================
|
|
349
|
+
|
|
350
|
+
function showWarningIfExists(html) {
|
|
351
|
+
const parser = new DOMParser();
|
|
352
|
+
const doc = parser.parseFromString(html, 'text/html');
|
|
353
|
+
const warningDiv = doc.querySelector('#warning');
|
|
354
|
+
|
|
355
|
+
if (warningDiv && warningDiv.textContent.trim()) {
|
|
356
|
+
alert(warningDiv.textContent.trim());
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// ============================================================================
|
|
361
|
+
// INITIALIZATION
|
|
362
|
+
// ============================================================================
|
|
363
|
+
|
|
364
|
+
document.addEventListener("DOMContentLoaded", function() {
|
|
365
|
+
const mode = getMode();
|
|
366
|
+
const batch = getBatch();
|
|
367
|
+
|
|
368
|
+
// Set up mode/batch toggle listeners
|
|
369
|
+
document.querySelectorAll(".mode-toggle").forEach(btn => {
|
|
370
|
+
btn.addEventListener("click", () => setMode(btn.dataset.mode));
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
document.querySelectorAll(".batch-toggle").forEach(btn => {
|
|
374
|
+
btn.addEventListener("click", () => setBatch(btn.dataset.batch));
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
// Restore UI state (without triggering updates)
|
|
378
|
+
setMode(mode, false);
|
|
379
|
+
setBatch(batch, false);
|
|
380
|
+
|
|
381
|
+
// Initialize code overlay
|
|
382
|
+
initializeCodeOverlay();
|
|
383
|
+
|
|
384
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
|
|
2
|
+
function deleteWorkflow(link) {
|
|
3
|
+
const url = link.dataset.deleteUrl;
|
|
4
|
+
|
|
5
|
+
fetch(url, {
|
|
6
|
+
method: 'DELETE',
|
|
7
|
+
headers: {
|
|
8
|
+
'Content-Type': 'application/json'
|
|
9
|
+
}
|
|
10
|
+
})
|
|
11
|
+
.then(res => res.json())
|
|
12
|
+
.then(data => {
|
|
13
|
+
if (data.success) {
|
|
14
|
+
window.location.reload(); // or remove the row dynamically
|
|
15
|
+
} else {
|
|
16
|
+
alert("Failed to delete workflow: " + data.error);
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
.catch(err => {
|
|
20
|
+
console.error("Delete error:", err);
|
|
21
|
+
alert("Something went wrong.");
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
function editScriptName(event) {
|
|
2
|
+
event.preventDefault(); // Prevent full form submission
|
|
3
|
+
const newName = document.getElementById("new-name").value;
|
|
4
|
+
fetch(scriptMetaUrl, {
|
|
5
|
+
method: "PATCH",
|
|
6
|
+
headers: {
|
|
7
|
+
"Content-Type": "application/json",
|
|
8
|
+
},
|
|
9
|
+
body: JSON.stringify({name: newName})
|
|
10
|
+
})
|
|
11
|
+
.then(res => res.json())
|
|
12
|
+
.then(data => {
|
|
13
|
+
if (data.success) {
|
|
14
|
+
window.location.reload(); // or update the title on page directly
|
|
15
|
+
} else {
|
|
16
|
+
alert("Failed to rename script");
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
.catch(error => console.error("Failed to rename script", error));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function lockScriptEditing() {
|
|
23
|
+
fetch(scriptMetaUrl, {
|
|
24
|
+
method: "PATCH",
|
|
25
|
+
headers: {
|
|
26
|
+
"Content-Type": "application/json",
|
|
27
|
+
},
|
|
28
|
+
body: JSON.stringify({ status: "finalized" })
|
|
29
|
+
})
|
|
30
|
+
.then(res => res.json())
|
|
31
|
+
.then(data => {
|
|
32
|
+
if (data.success) {
|
|
33
|
+
window.location.reload();
|
|
34
|
+
} else {
|
|
35
|
+
alert("Failed to update script status");
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
.catch(error => console.error("Failed to update script status", error));
|
|
39
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
document.addEventListener("DOMContentLoaded", function() {
|
|
2
|
-
var socket = io
|
|
2
|
+
var socket = io();
|
|
3
3
|
socket.on('connect', function() {
|
|
4
4
|
console.log('Connected');
|
|
5
5
|
});
|
|
@@ -37,13 +37,43 @@ document.addEventListener("DOMContentLoaded", function() {
|
|
|
37
37
|
console.error("Error received:", errorData);
|
|
38
38
|
var progressBar = document.getElementById('progress-bar-inner');
|
|
39
39
|
|
|
40
|
-
progressBar.classList.remove('bg-success');
|
|
41
|
-
progressBar.classList.add('bg-danger');
|
|
42
|
-
|
|
40
|
+
progressBar.classList.remove('bg-success', 'bg-warning');
|
|
41
|
+
progressBar.classList.add('bg-danger');
|
|
42
|
+
|
|
43
43
|
var errorModal = new bootstrap.Modal(document.getElementById('error-modal'));
|
|
44
|
-
document.getElementById('
|
|
44
|
+
document.getElementById('errorModalLabel').innerText = "Error Detected";
|
|
45
|
+
document.getElementById('error-message').innerText =
|
|
46
|
+
"An error occurred: " + errorData.message;
|
|
47
|
+
|
|
48
|
+
// Show all buttons again
|
|
49
|
+
document.getElementById('retry-btn').style.display = "inline-block";
|
|
50
|
+
document.getElementById('continue-btn').style.display = "inline-block";
|
|
51
|
+
document.getElementById('stop-btn').style.display = "inline-block";
|
|
52
|
+
|
|
45
53
|
errorModal.show();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
socket.on('human_intervention', function(data) {
|
|
58
|
+
console.warn("Human intervention required:", data);
|
|
59
|
+
var progressBar = document.getElementById('progress-bar-inner');
|
|
46
60
|
|
|
61
|
+
// Set progress bar to yellow
|
|
62
|
+
progressBar.classList.remove('bg-success', 'bg-danger');
|
|
63
|
+
progressBar.classList.add('bg-warning');
|
|
64
|
+
|
|
65
|
+
// Reuse error modal but update content
|
|
66
|
+
var errorModal = new bootstrap.Modal(document.getElementById('error-modal'));
|
|
67
|
+
document.getElementById('errorModalLabel').innerText = "Human Intervention Required";
|
|
68
|
+
document.getElementById('error-message').innerText =
|
|
69
|
+
"Workflow paused: " + (data.message || "Please check and manually resume.");
|
|
70
|
+
|
|
71
|
+
// Optionally: hide retry button, since it may not apply
|
|
72
|
+
document.getElementById('retry-btn').style.display = "none";
|
|
73
|
+
document.getElementById('continue-btn').style.display = "inline-block";
|
|
74
|
+
document.getElementById('stop-btn').style.display = "inline-block";
|
|
75
|
+
|
|
76
|
+
errorModal.show();
|
|
47
77
|
});
|
|
48
78
|
|
|
49
79
|
// Handle Pause/Resume Button
|
|
@@ -71,6 +101,11 @@ document.addEventListener("DOMContentLoaded", function() {
|
|
|
71
101
|
document.getElementById('continue-btn').addEventListener('click', function() {
|
|
72
102
|
socket.emit('pause'); // Resume execution
|
|
73
103
|
console.log("Execution resumed.");
|
|
104
|
+
|
|
105
|
+
// Reset progress bar color to running (blue)
|
|
106
|
+
var progressBar = document.getElementById('progress-bar-inner');
|
|
107
|
+
progressBar.classList.remove('bg-danger', 'bg-warning');
|
|
108
|
+
progressBar.classList.add('bg-primary');
|
|
74
109
|
});
|
|
75
110
|
|
|
76
111
|
document.getElementById('retry-btn').addEventListener('click', function() {
|