ivoryos 1.0.9__py3-none-any.whl → 1.2.0b1__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.
Potentially problematic release.
This version of ivoryos might be problematic. Click here for more details.
- ivoryos/__init__.py +26 -7
- ivoryos/routes/api/api.py +56 -0
- ivoryos/routes/auth/auth.py +5 -5
- ivoryos/routes/control/control.py +77 -372
- ivoryos/routes/control/control_file.py +36 -0
- ivoryos/routes/control/control_new_device.py +142 -0
- ivoryos/routes/control/templates/controllers.html +166 -0
- ivoryos/routes/control/templates/controllers_new.html +112 -0
- ivoryos/routes/control/utils.py +38 -0
- ivoryos/routes/data/data.py +129 -0
- ivoryos/routes/data/templates/components/step_card.html +13 -0
- ivoryos/routes/{database/templates/database → data/templates}/workflow_database.html +14 -8
- ivoryos/routes/{database/templates/database → data/templates}/workflow_view.html +3 -3
- ivoryos/routes/design/__init__.py +4 -0
- ivoryos/routes/design/design.py +298 -656
- ivoryos/routes/design/design_file.py +68 -0
- ivoryos/routes/design/design_step.py +145 -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 +34 -0
- ivoryos/routes/design/templates/components/deck_selector.html +10 -0
- ivoryos/routes/design/templates/components/edit_action_form.html +38 -0
- ivoryos/routes/design/templates/components/instruments_panel.html +66 -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 +39 -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 +41 -0
- ivoryos/routes/execute/__init__.py +0 -0
- ivoryos/routes/execute/execute.py +317 -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 +31 -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 +17 -0
- ivoryos/routes/execute/templates/components/tab_bayesian.html +399 -0
- ivoryos/routes/execute/templates/components/tab_configuration.html +98 -0
- ivoryos/routes/execute/templates/components/tab_repeat.html +14 -0
- ivoryos/routes/execute/templates/experiment_run.html +294 -0
- ivoryos/routes/library/__init__.py +0 -0
- ivoryos/routes/library/library.py +159 -0
- ivoryos/routes/{database/templates/database/scripts_database.html → library/templates/library.html} +30 -22
- ivoryos/routes/main/main.py +1 -1
- ivoryos/routes/main/templates/{main/home.html → home.html} +4 -4
- ivoryos/socket_handlers.py +52 -0
- ivoryos/static/js/action_handlers.js +213 -0
- ivoryos/static/js/db_delete.js +23 -0
- ivoryos/static/js/script_metadata.js +39 -0
- ivoryos/static/js/sortable_design.js +89 -56
- ivoryos/static/js/ui_state.js +113 -0
- ivoryos/templates/base.html +4 -4
- ivoryos/utils/bo_campaign.py +179 -3
- ivoryos/utils/db_models.py +14 -5
- ivoryos/utils/form.py +5 -9
- ivoryos/utils/global_config.py +13 -1
- ivoryos/utils/py_to_json.py +225 -0
- ivoryos/utils/script_runner.py +49 -7
- ivoryos/utils/serilize.py +203 -0
- ivoryos/utils/task_runner.py +4 -1
- ivoryos/version.py +1 -1
- {ivoryos-1.0.9.dist-info → ivoryos-1.2.0b1.dist-info}/METADATA +5 -8
- ivoryos-1.2.0b1.dist-info/RECORD +105 -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/design/templates/design/experiment_builder.html +0 -521
- ivoryos/routes/design/templates/design/experiment_run.html +0 -558
- 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.2.0b1.dist-info}/LICENSE +0 -0
- {ivoryos-1.0.9.dist-info → ivoryos-1.2.0b1.dist-info}/WHEEL +0 -0
- {ivoryos-1.0.9.dist-info → ivoryos-1.2.0b1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
function saveWorkflow(link) {
|
|
6
|
+
const url = link.dataset.postUrl;
|
|
7
|
+
|
|
8
|
+
fetch(url, {
|
|
9
|
+
method: 'POST',
|
|
10
|
+
headers: {
|
|
11
|
+
'Content-Type': 'application/json'
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
.then(res => res.json())
|
|
15
|
+
.then(data => {
|
|
16
|
+
if (data.success) {
|
|
17
|
+
// flash a success message
|
|
18
|
+
flash("Workflow saved successfully", "success");
|
|
19
|
+
window.location.reload(); // or update the UI dynamically
|
|
20
|
+
} else {
|
|
21
|
+
alert("Failed to save workflow: " + data.error);
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
.catch(err => {
|
|
25
|
+
console.error("Save error:", err);
|
|
26
|
+
alert("Something went wrong.");
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
function updateInstrumentPanel(link) {
|
|
32
|
+
const url = link.dataset.getUrl;
|
|
33
|
+
fetch(url)
|
|
34
|
+
.then(res => res.json())
|
|
35
|
+
.then(data => {
|
|
36
|
+
if (data.html) {
|
|
37
|
+
document.getElementById("sidebar-wrapper").innerHTML = data.html;
|
|
38
|
+
initializeDragHandlers()
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function addMethodToDesign(event, form) {
|
|
44
|
+
event.preventDefault(); // Prevent default form submission
|
|
45
|
+
|
|
46
|
+
const formData = new FormData(form);
|
|
47
|
+
|
|
48
|
+
fetch(form.action, {
|
|
49
|
+
method: 'POST',
|
|
50
|
+
body: formData
|
|
51
|
+
})
|
|
52
|
+
.then(response => response.json())
|
|
53
|
+
.then(data => {
|
|
54
|
+
if (data.success) {
|
|
55
|
+
updateActionCanvas(data.html);
|
|
56
|
+
hideModal();
|
|
57
|
+
} else {
|
|
58
|
+
alert("Failed to add method: " + data.error);
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
.catch(error => console.error('Error:', error));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
function updateActionCanvas(html) {
|
|
66
|
+
document.getElementById("canvas-action-wrapper").innerHTML = html;
|
|
67
|
+
initializeCanvas(); // Reinitialize canvas functionality
|
|
68
|
+
document.querySelectorAll('#pythonCodeOverlay pre code').forEach((block) => {
|
|
69
|
+
hljs.highlightElement(block);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
let lastFocusedElement = null;
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
function hideModal() {
|
|
78
|
+
if (document.activeElement) {
|
|
79
|
+
document.activeElement.blur();
|
|
80
|
+
}
|
|
81
|
+
$('#dropModal').modal('hide');
|
|
82
|
+
if (lastFocusedElement) {
|
|
83
|
+
lastFocusedElement.focus(); // Return focus to the triggering element
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function submitEditForm(event) {
|
|
88
|
+
event.preventDefault();
|
|
89
|
+
const form = event.target;
|
|
90
|
+
const formData = new FormData(form);
|
|
91
|
+
|
|
92
|
+
fetch(form.action, {
|
|
93
|
+
method: 'POST',
|
|
94
|
+
body: formData
|
|
95
|
+
})
|
|
96
|
+
.then(response => response.text())
|
|
97
|
+
.then(html => {
|
|
98
|
+
if (html) {
|
|
99
|
+
// Update only the action list
|
|
100
|
+
updateActionCanvas(html);
|
|
101
|
+
|
|
102
|
+
if (previousHtmlState) {
|
|
103
|
+
document.getElementById('instrument-panel').innerHTML = previousHtmlState;
|
|
104
|
+
previousHtmlState = null; // Clear the stored state
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
.catch(error => {
|
|
109
|
+
console.error('Error:', error);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function clearDraft() {
|
|
114
|
+
fetch(scriptDeleteUrl, {
|
|
115
|
+
method: "DELETE",
|
|
116
|
+
headers: {
|
|
117
|
+
"Content-Type": "application/json",
|
|
118
|
+
},
|
|
119
|
+
})
|
|
120
|
+
.then(res => res.json())
|
|
121
|
+
.then(data => {
|
|
122
|
+
if (data.success) {
|
|
123
|
+
window.location.reload();
|
|
124
|
+
} else {
|
|
125
|
+
alert("Failed to clear draft");
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
.catch(error => console.error("Failed to clear draft", error));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
let previousHtmlState = null; // Store the previous state
|
|
135
|
+
|
|
136
|
+
function duplicateAction(uuid) {
|
|
137
|
+
if (!uuid) {
|
|
138
|
+
console.error('Invalid UUID');
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
fetch(scriptStepDupUrl.replace('0', uuid), {
|
|
143
|
+
method: 'POST',
|
|
144
|
+
headers: {
|
|
145
|
+
'Content-Type': 'application/json'
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
.then(response => response.text())
|
|
150
|
+
.then(html => {
|
|
151
|
+
updateActionCanvas(html);
|
|
152
|
+
})
|
|
153
|
+
.catch(error => console.error('Error:', error));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function editAction(uuid) {
|
|
157
|
+
if (!uuid) {
|
|
158
|
+
console.error('Invalid UUID');
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Save current state before fetching new content
|
|
163
|
+
previousHtmlState = document.getElementById('instrument-panel').innerHTML;
|
|
164
|
+
|
|
165
|
+
fetch(scriptStepUrl.replace('0', uuid), {
|
|
166
|
+
method: 'GET',
|
|
167
|
+
headers: {
|
|
168
|
+
'Content-Type': 'application/json'
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
.then(response => response.text())
|
|
172
|
+
.then(html => {
|
|
173
|
+
document.getElementById('instrument-panel').innerHTML = html;
|
|
174
|
+
|
|
175
|
+
// Add click handler for back button
|
|
176
|
+
document.getElementById('back').addEventListener('click', function(e) {
|
|
177
|
+
e.preventDefault();
|
|
178
|
+
if (previousHtmlState) {
|
|
179
|
+
document.getElementById('instrument-panel').innerHTML = previousHtmlState;
|
|
180
|
+
previousHtmlState = null; // Clear the stored state
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
})
|
|
184
|
+
.catch(error => console.error('Error:', error));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
function deleteAction(uuid) {
|
|
190
|
+
if (!uuid) {
|
|
191
|
+
console.error('Invalid UUID');
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
fetch(scriptStepUrl.replace('0', uuid), {
|
|
196
|
+
method: 'DELETE',
|
|
197
|
+
headers: {
|
|
198
|
+
'Content-Type': 'application/json'
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
.then(response => response.text())
|
|
202
|
+
.then(html => {
|
|
203
|
+
// Find the first list element's content and replace it
|
|
204
|
+
updateActionCanvas(html);
|
|
205
|
+
})
|
|
206
|
+
.catch(error => console.error('Error:', error));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
@@ -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,16 +1,56 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// Move triggerModal to global scope
|
|
2
|
+
function triggerModal(formHtml, actionName, actionId, dropTargetId) {
|
|
3
|
+
if (formHtml && formHtml.trim() !== "") {
|
|
4
|
+
var $form = $("<div>").html(formHtml);
|
|
5
|
+
|
|
6
|
+
var $hiddenInput = $("<input>")
|
|
7
|
+
.attr("type", "hidden")
|
|
8
|
+
.attr("name", "drop_target_id")
|
|
9
|
+
.attr("id", "dropTargetInput")
|
|
10
|
+
.val(dropTargetId);
|
|
11
|
+
|
|
12
|
+
$form.find("button[type='submit']").before($hiddenInput);
|
|
13
|
+
|
|
14
|
+
$("#modalFormFields").empty().append($form.children());
|
|
15
|
+
|
|
16
|
+
const $modal = $("#dropModal");
|
|
17
|
+
|
|
18
|
+
setTimeout(() => {
|
|
19
|
+
showModal($modal);
|
|
20
|
+
}, 0);
|
|
21
|
+
|
|
22
|
+
$("#modalDropTarget").text(dropTargetId || "N/A");
|
|
23
|
+
$("#modalFormFields")
|
|
24
|
+
.data("action-id", actionId)
|
|
25
|
+
.data("action-name", actionName)
|
|
26
|
+
.data("drop-target-id", dropTargetId);
|
|
27
|
+
} else {
|
|
28
|
+
console.error("Form HTML is undefined or empty!");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function showModal($modal) {
|
|
33
|
+
$modal.modal({
|
|
34
|
+
backdrop: 'static',
|
|
35
|
+
keyboard: true,
|
|
36
|
+
focus: true
|
|
37
|
+
}).modal('show');
|
|
38
|
+
}
|
|
3
39
|
|
|
40
|
+
const state = {
|
|
41
|
+
dropTargetId: ""
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function initializeCanvas() {
|
|
4
45
|
$("#list ul").sortable({
|
|
5
46
|
cancel: ".unsortable",
|
|
6
47
|
opacity: 0.8,
|
|
7
48
|
cursor: "move",
|
|
8
49
|
placeholder: "drop-placeholder",
|
|
9
50
|
update: function () {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
});
|
|
51
|
+
const item_order = $("ul.reorder li").map(function () {
|
|
52
|
+
return this.id;
|
|
53
|
+
}).get();
|
|
14
54
|
var order_string = "order=" + item_order.join(",");
|
|
15
55
|
|
|
16
56
|
$.ajax({
|
|
@@ -19,87 +59,80 @@ $(document).ready(function () {
|
|
|
19
59
|
data: order_string,
|
|
20
60
|
cache: false,
|
|
21
61
|
success: function (data) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
window.location.href = window.location.href;
|
|
62
|
+
// Update the canvas content with the new HTML
|
|
63
|
+
updateActionCanvas(data)
|
|
25
64
|
}
|
|
65
|
+
}).fail(function (jqXHR, textStatus, errorThrown) {
|
|
66
|
+
console.error("Failed to update order:", textStatus, errorThrown);
|
|
26
67
|
});
|
|
27
68
|
}
|
|
28
69
|
});
|
|
29
70
|
|
|
30
71
|
// Make Entire Accordion Item Draggable
|
|
31
|
-
$(".accordion-item").on("dragstart", function (event) {
|
|
32
|
-
let formHtml = $(this).find(".accordion-body").html();
|
|
33
|
-
event.originalEvent.dataTransfer.setData("form", formHtml || "");
|
|
72
|
+
$(".accordion-item").off("dragstart").on("dragstart", function (event) {
|
|
73
|
+
let formHtml = $(this).find(".accordion-body").html();
|
|
74
|
+
event.originalEvent.dataTransfer.setData("form", formHtml || "");
|
|
34
75
|
event.originalEvent.dataTransfer.setData("action", $(this).find(".draggable-action").data("action"));
|
|
35
76
|
event.originalEvent.dataTransfer.setData("id", $(this).find(".draggable-action").attr("id"));
|
|
36
|
-
|
|
37
77
|
$(this).addClass("dragging");
|
|
38
78
|
});
|
|
39
79
|
|
|
40
|
-
|
|
41
|
-
$("#list ul, .canvas").on("dragover", function (event) {
|
|
80
|
+
$("#list ul, .canvas").off("dragover").on("dragover", function (event) {
|
|
42
81
|
event.preventDefault();
|
|
43
82
|
let $target = $(event.target).closest("li");
|
|
44
83
|
|
|
45
|
-
// If we're over a valid <li> element in the list
|
|
46
84
|
if ($target.length) {
|
|
47
|
-
dropTargetId = $target.attr("id") || "";
|
|
48
|
-
|
|
49
|
-
$(".drop-placeholder").remove(); // Remove existing placeholders
|
|
50
|
-
$("<li class='drop-placeholder'></li>").insertBefore($target); // Insert before the target element
|
|
85
|
+
state.dropTargetId = $target.attr("id") || "";
|
|
86
|
+
insertDropPlaceholder($target);
|
|
51
87
|
} else if (!$("#list ul").children().length && $(this).hasClass("canvas")) {
|
|
52
|
-
$(".drop-placeholder").remove();
|
|
53
|
-
// $("#list ul").append("<li class='drop-placeholder'></li>"); // Append placeholder to canvas
|
|
88
|
+
$(".drop-placeholder").remove();
|
|
54
89
|
} else {
|
|
55
|
-
dropTargetId = "";
|
|
90
|
+
state.dropTargetId = "";
|
|
56
91
|
}
|
|
57
92
|
});
|
|
58
93
|
|
|
59
|
-
$("#list ul, .canvas").on("dragleave", function () {
|
|
60
|
-
$(".drop-placeholder").remove();
|
|
94
|
+
$("#list ul, .canvas").off("dragleave").on("dragleave", function () {
|
|
95
|
+
$(".drop-placeholder").remove();
|
|
61
96
|
});
|
|
62
97
|
|
|
63
|
-
$("#list ul, .canvas").on("drop", function (event) {
|
|
98
|
+
$("#list ul, .canvas").off("drop").on("drop", function (event) {
|
|
64
99
|
event.preventDefault();
|
|
65
|
-
|
|
66
100
|
var actionName = event.originalEvent.dataTransfer.getData("action");
|
|
67
101
|
var actionId = event.originalEvent.dataTransfer.getData("id");
|
|
68
|
-
var formHtml = event.originalEvent.dataTransfer.getData("form");
|
|
102
|
+
var formHtml = event.originalEvent.dataTransfer.getData("form");
|
|
69
103
|
let listLength = $("ul.reorder li").length;
|
|
70
|
-
dropTargetId = dropTargetId || listLength + 1;
|
|
104
|
+
state.dropTargetId = state.dropTargetId || listLength + 1;
|
|
71
105
|
$(".drop-placeholder").remove();
|
|
72
|
-
|
|
73
|
-
triggerModal(formHtml, actionName, actionId, dropTargetId);
|
|
74
|
-
|
|
106
|
+
document.activeElement?.blur();
|
|
107
|
+
triggerModal(formHtml, actionName, actionId, state.dropTargetId);
|
|
75
108
|
});
|
|
109
|
+
}
|
|
76
110
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
111
|
+
function insertDropPlaceholder($target) {
|
|
112
|
+
$(".drop-placeholder").remove();
|
|
113
|
+
$("<li class='drop-placeholder'></li>").insertBefore($target);
|
|
114
|
+
}
|
|
81
115
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
.attr("id", "dropTargetInput")
|
|
87
|
-
.val(dropTargetId);
|
|
116
|
+
// Add this function to sortable_design.js
|
|
117
|
+
function initializeDragHandlers() {
|
|
118
|
+
$(".accordion-item").off("dragstart").on("dragstart", function (event) {
|
|
119
|
+
let formHtml = $(this).find(".accordion-body form").prop('outerHTML');
|
|
88
120
|
|
|
89
|
-
|
|
90
|
-
|
|
121
|
+
if (!formHtml) {
|
|
122
|
+
console.error("Form not found in accordion-body");
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
91
125
|
|
|
92
|
-
|
|
93
|
-
|
|
126
|
+
event.originalEvent.dataTransfer.setData("form", formHtml);
|
|
127
|
+
event.originalEvent.dataTransfer.setData("action", $(this).find(".draggable-action").data("action"));
|
|
128
|
+
event.originalEvent.dataTransfer.setData("id", $(this).find(".draggable-action").attr("id"));
|
|
94
129
|
|
|
95
|
-
|
|
96
|
-
|
|
130
|
+
$(this).addClass("dragging");
|
|
131
|
+
});
|
|
132
|
+
}
|
|
97
133
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
});
|
|
134
|
+
// Make sure it's called in the document ready function
|
|
135
|
+
$(document).ready(function () {
|
|
136
|
+
initializeCanvas();
|
|
137
|
+
initializeDragHandlers(); // Add this line
|
|
138
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Toggle visibility of line numbers
|
|
2
|
+
function toggleLineNumbers(save = true) {
|
|
3
|
+
const show = document.getElementById('toggleLineNumbers').checked;
|
|
4
|
+
document.querySelectorAll('.line-number').forEach(el => {
|
|
5
|
+
el.classList.toggle('d-none', !show);
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
if (save) {
|
|
9
|
+
localStorage.setItem('showLineNumbers', show ? 'true' : 'false');
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Toggle visibility of Python code overlay
|
|
14
|
+
function toggleCodeOverlay(state = null) {
|
|
15
|
+
const overlay = document.getElementById("pythonCodeOverlay");
|
|
16
|
+
const checkbox = document.getElementById("showPythonCodeSwitch");
|
|
17
|
+
|
|
18
|
+
const isVisible = overlay.classList.contains("show");
|
|
19
|
+
const newState = state !== null ? state : !isVisible;
|
|
20
|
+
|
|
21
|
+
if (newState) {
|
|
22
|
+
overlay.classList.add("show");
|
|
23
|
+
checkbox.checked = true;
|
|
24
|
+
} else {
|
|
25
|
+
overlay.classList.remove("show");
|
|
26
|
+
checkbox.checked = false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Save state to session via PATCH
|
|
30
|
+
fetch(scriptUIStateUrl, {
|
|
31
|
+
method: "PATCH",
|
|
32
|
+
headers: { "Content-Type": "application/json" },
|
|
33
|
+
body: JSON.stringify({ show_code: newState })
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
function setScriptPhase(stype) {
|
|
39
|
+
fetch(scriptUIStateUrl, {
|
|
40
|
+
method: "PATCH",
|
|
41
|
+
headers: {
|
|
42
|
+
"Content-Type": "application/json",
|
|
43
|
+
},
|
|
44
|
+
body: JSON.stringify({ editing_type: stype })
|
|
45
|
+
})
|
|
46
|
+
.then(res => res.json())
|
|
47
|
+
.then(data => {
|
|
48
|
+
if (data.html) {
|
|
49
|
+
document.getElementById("canvas-wrapper").innerHTML = data.html;
|
|
50
|
+
initializeCanvas(); // Reinitialize the canvas functionality
|
|
51
|
+
document.querySelectorAll('#pythonCodeOverlay pre code').forEach((block) => {
|
|
52
|
+
hljs.highlightElement(block);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
.catch(error => console.error("Failed to update editing type", error));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
function changeDeck(deck) {
|
|
62
|
+
fetch(scriptUIStateUrl, {
|
|
63
|
+
method: "PATCH",
|
|
64
|
+
headers: {
|
|
65
|
+
"Content-Type": "application/json",
|
|
66
|
+
},
|
|
67
|
+
body: JSON.stringify({ deck_name: deck })
|
|
68
|
+
})
|
|
69
|
+
.then(res => res.json())
|
|
70
|
+
.then(data => {
|
|
71
|
+
if (data.html) {
|
|
72
|
+
document.getElementById("sidebar-wrapper").innerHTML = data.html;
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
.catch(error => console.error("Failed to change deck", error));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
function toggleAutoFill() {
|
|
81
|
+
const instrumentValue = document.querySelector('.form-check.form-switch').dataset.instrument;
|
|
82
|
+
|
|
83
|
+
fetch(scriptUIStateUrl, {
|
|
84
|
+
method: "PATCH",
|
|
85
|
+
headers: {
|
|
86
|
+
"Content-Type": "application/json",
|
|
87
|
+
},
|
|
88
|
+
body: JSON.stringify({
|
|
89
|
+
autofill: document.getElementById("autoFillCheck").checked,
|
|
90
|
+
instrument: instrumentValue
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
.then(res => res.json())
|
|
94
|
+
.then(data => {
|
|
95
|
+
if (data.html) {
|
|
96
|
+
document.getElementById("instrument-panel").innerHTML = data.html;
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
// Restore state on page load
|
|
101
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
102
|
+
const savedState = localStorage.getItem('showLineNumbers');
|
|
103
|
+
const checkbox = document.getElementById('toggleLineNumbers');
|
|
104
|
+
|
|
105
|
+
if (savedState === 'true') {
|
|
106
|
+
checkbox.checked = true;
|
|
107
|
+
}
|
|
108
|
+
if (checkbox) {
|
|
109
|
+
toggleLineNumbers(false); // don't overwrite localStorage on load
|
|
110
|
+
checkbox.addEventListener('change', () => toggleLineNumbers());
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
});
|
ivoryos/templates/base.html
CHANGED
|
@@ -38,16 +38,16 @@
|
|
|
38
38
|
</li>
|
|
39
39
|
{% if enable_design %}
|
|
40
40
|
<li class="nav-item">
|
|
41
|
-
<a class="nav-link" href="{{ url_for('
|
|
41
|
+
<a class="nav-link" href="{{ url_for('library.load_from_database') }}" aria-current="page">Library</a>
|
|
42
42
|
</li>
|
|
43
43
|
<li class="nav-item">
|
|
44
44
|
<a class="nav-link" href="{{ url_for('design.experiment_builder') }}">Design</a>
|
|
45
45
|
</li>
|
|
46
46
|
<li class="nav-item">
|
|
47
|
-
<a class="nav-link" href="{{ url_for('
|
|
47
|
+
<a class="nav-link" href="{{ url_for('execute.experiment_run') }}">Compile/Run</a>
|
|
48
48
|
</li>
|
|
49
49
|
<li class="nav-item">
|
|
50
|
-
<a class="nav-link" href="{{ url_for('
|
|
50
|
+
<a class="nav-link" href="{{ url_for('data.list_workflows') }}">Data</a>
|
|
51
51
|
</li>
|
|
52
52
|
{% endif %}
|
|
53
53
|
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
<h1 class="modal-title fs-5" id="importModal">Import deck by file path</h1>
|
|
117
117
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
118
118
|
</div>
|
|
119
|
-
<form method="POST" action="{{ url_for('control.import_deck') }}" enctype="multipart/form-data">
|
|
119
|
+
<form method="POST" action="{{ url_for('control.temp.import_deck') }}" enctype="multipart/form-data">
|
|
120
120
|
<div class="modal-body">
|
|
121
121
|
<h5>from connection history</h5>
|
|
122
122
|
<div class="form-group">
|