ivoryos 1.2.2__py3-none-any.whl → 1.2.3__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.

Files changed (60) hide show
  1. ivoryos/routes/auth/templates/login.html +25 -0
  2. ivoryos/routes/auth/templates/signup.html +32 -0
  3. ivoryos/routes/control/templates/controllers.html +166 -0
  4. ivoryos/routes/control/templates/controllers_new.html +112 -0
  5. ivoryos/routes/data/templates/components/step_card.html +13 -0
  6. ivoryos/routes/data/templates/workflow_database.html +109 -0
  7. ivoryos/routes/data/templates/workflow_view.html +130 -0
  8. ivoryos/routes/design/templates/components/action_form.html +53 -0
  9. ivoryos/routes/design/templates/components/actions_panel.html +25 -0
  10. ivoryos/routes/design/templates/components/autofill_toggle.html +10 -0
  11. ivoryos/routes/design/templates/components/canvas.html +5 -0
  12. ivoryos/routes/design/templates/components/canvas_footer.html +9 -0
  13. ivoryos/routes/design/templates/components/canvas_header.html +75 -0
  14. ivoryos/routes/design/templates/components/canvas_main.html +34 -0
  15. ivoryos/routes/design/templates/components/deck_selector.html +10 -0
  16. ivoryos/routes/design/templates/components/edit_action_form.html +38 -0
  17. ivoryos/routes/design/templates/components/instruments_panel.html +66 -0
  18. ivoryos/routes/design/templates/components/modals/drop_modal.html +17 -0
  19. ivoryos/routes/design/templates/components/modals/json_modal.html +22 -0
  20. ivoryos/routes/design/templates/components/modals/new_script_modal.html +17 -0
  21. ivoryos/routes/design/templates/components/modals/rename_modal.html +23 -0
  22. ivoryos/routes/design/templates/components/modals/saveas_modal.html +27 -0
  23. ivoryos/routes/design/templates/components/modals.html +6 -0
  24. ivoryos/routes/design/templates/components/python_code_overlay.html +39 -0
  25. ivoryos/routes/design/templates/components/sidebar.html +15 -0
  26. ivoryos/routes/design/templates/components/text_to_code_panel.html +20 -0
  27. ivoryos/routes/design/templates/experiment_builder.html +41 -0
  28. ivoryos/routes/execute/templates/components/error_modal.html +20 -0
  29. ivoryos/routes/execute/templates/components/logging_panel.html +31 -0
  30. ivoryos/routes/execute/templates/components/progress_panel.html +27 -0
  31. ivoryos/routes/execute/templates/components/run_panel.html +9 -0
  32. ivoryos/routes/execute/templates/components/run_tabs.html +17 -0
  33. ivoryos/routes/execute/templates/components/tab_bayesian.html +398 -0
  34. ivoryos/routes/execute/templates/components/tab_configuration.html +98 -0
  35. ivoryos/routes/execute/templates/components/tab_repeat.html +14 -0
  36. ivoryos/routes/execute/templates/experiment_run.html +294 -0
  37. ivoryos/routes/library/templates/library.html +92 -0
  38. ivoryos/routes/main/templates/help.html +141 -0
  39. ivoryos/routes/main/templates/home.html +103 -0
  40. ivoryos/static/favicon.ico +0 -0
  41. ivoryos/static/gui_annotation/Slide1.png +0 -0
  42. ivoryos/static/gui_annotation/Slide2.PNG +0 -0
  43. ivoryos/static/js/action_handlers.js +213 -0
  44. ivoryos/static/js/db_delete.js +23 -0
  45. ivoryos/static/js/overlay.js +12 -0
  46. ivoryos/static/js/script_metadata.js +39 -0
  47. ivoryos/static/js/socket_handler.js +125 -0
  48. ivoryos/static/js/sortable_card.js +24 -0
  49. ivoryos/static/js/sortable_design.js +138 -0
  50. ivoryos/static/js/ui_state.js +113 -0
  51. ivoryos/static/logo.webp +0 -0
  52. ivoryos/static/style.css +211 -0
  53. ivoryos/templates/base.html +157 -0
  54. ivoryos/version.py +1 -1
  55. {ivoryos-1.2.2.dist-info → ivoryos-1.2.3.dist-info}/METADATA +1 -1
  56. ivoryos-1.2.3.dist-info/RECORD +100 -0
  57. ivoryos-1.2.2.dist-info/RECORD +0 -47
  58. {ivoryos-1.2.2.dist-info → ivoryos-1.2.3.dist-info}/WHEEL +0 -0
  59. {ivoryos-1.2.2.dist-info → ivoryos-1.2.3.dist-info}/licenses/LICENSE +0 -0
  60. {ivoryos-1.2.2.dist-info → ivoryos-1.2.3.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,12 @@
1
+ function addOverlayToButtons(buttonIds) {
2
+ buttonIds.forEach(function(buttonId) {
3
+ document.getElementById(buttonId).addEventListener('submit', function() {
4
+ // Display the overlay
5
+ document.getElementById('overlay').style.display = 'block';
6
+ document.getElementById('overlay-text').innerText = `Processing ${buttonId}...`;
7
+ });
8
+ });
9
+ }
10
+
11
+ // buttonIds should be set dynamically in your HTML template
12
+ addOverlayToButtons(buttonIds);
@@ -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
+ }
@@ -0,0 +1,125 @@
1
+ document.addEventListener("DOMContentLoaded", function() {
2
+ var socket = io.connect('http://' + document.domain + ':' + location.port);
3
+ socket.on('connect', function() {
4
+ console.log('Connected');
5
+ });
6
+ socket.on('progress', function(data) {
7
+ var progress = data.progress;
8
+ console.log(progress);
9
+ // Update the progress bar's width and appearance
10
+ var progressBar = document.getElementById('progress-bar-inner');
11
+ progressBar.style.width = progress + '%';
12
+ progressBar.setAttribute('aria-valuenow', progress);
13
+ const runPanel = document.getElementById("run-panel");
14
+ const codePanel = document.getElementById("code-panel");
15
+ if (progress === 1) {
16
+ if (runPanel) runPanel.style.display = "none";
17
+ if (codePanel) {
18
+ codePanel.style.display = "block";
19
+ codePanel.scrollIntoView({ behavior: "smooth" });
20
+ }
21
+ progressBar.classList.remove('bg-success');
22
+ progressBar.classList.remove('bg-danger');
23
+ progressBar.classList.add('progress-bar-animated');
24
+ }
25
+ if (progress === 100) {
26
+ // Remove animation and set green color when 100% is reached
27
+ progressBar.classList.remove('progress-bar-animated');
28
+ progressBar.classList.add('bg-success'); // Bootstrap class for green color
29
+ setTimeout(() => {
30
+ if (runPanel) runPanel.style.display = "block";
31
+ if (codePanel) codePanel.style.display = "none";
32
+ }, 1000); // Small delay to let users see the completion
33
+ }
34
+ });
35
+
36
+ socket.on('error', function(errorData) {
37
+ console.error("Error received:", errorData);
38
+ var progressBar = document.getElementById('progress-bar-inner');
39
+
40
+ progressBar.classList.remove('bg-success');
41
+ progressBar.classList.add('bg-danger'); // Red color for error
42
+ // Show error modal
43
+ var errorModal = new bootstrap.Modal(document.getElementById('error-modal'));
44
+ document.getElementById('error-message').innerText = "An error occurred: " + errorData.message;
45
+ errorModal.show();
46
+
47
+ });
48
+
49
+ // Handle Pause/Resume Button
50
+ document.getElementById('pause-resume').addEventListener('click', function() {
51
+ socket.emit('pause');
52
+ console.log('Pause/Resume is toggled.');
53
+ var button = this;
54
+ var icon = button.querySelector("i");
55
+
56
+ // Toggle Pause and Resume
57
+ if (icon.classList.contains("bi-pause-circle")) {
58
+ icon.classList.remove("bi-pause-circle");
59
+ icon.classList.add("bi-play-circle");
60
+ button.innerHTML = '<i class="bi bi-play-circle"></i>';
61
+ button.setAttribute("title", "Resume execution");
62
+ } else {
63
+ icon.classList.remove("bi-play-circle");
64
+ icon.classList.add("bi-pause-circle");
65
+ button.innerHTML = '<i class="bi bi-pause-circle"></i>';
66
+ button.setAttribute("title", "Pause execution");
67
+ }
68
+ });
69
+
70
+ // Handle Modal Buttons
71
+ document.getElementById('continue-btn').addEventListener('click', function() {
72
+ socket.emit('pause'); // Resume execution
73
+ console.log("Execution resumed.");
74
+ });
75
+
76
+ document.getElementById('retry-btn').addEventListener('click', function() {
77
+ socket.emit('retry'); // Resume execution
78
+ console.log("Execution resumed, retrying.");
79
+ });
80
+
81
+ document.getElementById('stop-btn').addEventListener('click', function() {
82
+ socket.emit('pause'); // Resume execution
83
+ socket.emit('abort_current'); // Stop execution
84
+ console.log("Execution stopped.");
85
+
86
+ // Reset UI back to initial state
87
+ document.getElementById("code-panel").style.display = "none";
88
+ document.getElementById("run-panel").style.display = "block";
89
+ });
90
+
91
+ socket.on('log', function(data) {
92
+ var logMessage = data.message;
93
+ console.log(logMessage);
94
+ $('#logging-panel').append(logMessage + "<br>");
95
+ $('#logging-panel').scrollTop($('#logging-panel')[0].scrollHeight);
96
+ });
97
+
98
+ document.getElementById('abort-pending').addEventListener('click', function() {
99
+ var confirmation = confirm("Are you sure you want to stop after this iteration?");
100
+ if (confirmation) {
101
+ socket.emit('abort_pending');
102
+ console.log('Abort action sent to server.');
103
+ }
104
+ });
105
+ document.getElementById('abort-current').addEventListener('click', function() {
106
+ var confirmation = confirm("Are you sure you want to stop after this step?");
107
+ if (confirmation) {
108
+ socket.emit('abort_current');
109
+ console.log('Stop action sent to server.');
110
+ }
111
+ });
112
+
113
+ socket.on('execution', function(data) {
114
+ // Remove highlighting from all lines
115
+ document.querySelectorAll('pre code').forEach(el => el.style.backgroundColor = '');
116
+
117
+ // Get the currently executing line and highlight it
118
+ let executingLine = document.getElementById(data.section);
119
+ if (executingLine) {
120
+ executingLine.style.backgroundColor = '#cce5ff'; // Highlight
121
+ executingLine.style.transition = 'background-color 0.3s ease-in-out';
122
+
123
+ }
124
+ });
125
+ });
@@ -0,0 +1,24 @@
1
+ $(function() {
2
+ $("#sortable-grid").sortable({
3
+ items: ".card",
4
+ cursor: "move",
5
+ opacity: 0.7,
6
+ revert: true,
7
+ placeholder: "ui-state-highlight",
8
+ start: function(event, ui) {
9
+ ui.placeholder.height(ui.item.height());
10
+ },
11
+ update: function() {
12
+ const newOrder = $("#sortable-grid").sortable("toArray");
13
+ $.ajax({
14
+ url: saveOrderUrl, // saveOrderUrl should be set dynamically in your HTML template
15
+ method: 'POST',
16
+ contentType: 'application/json',
17
+ data: JSON.stringify({ order: newOrder }),
18
+ success: function() {
19
+ console.log('Order saved');
20
+ }
21
+ });
22
+ }
23
+ }).disableSelection();
24
+ });
@@ -0,0 +1,138 @@
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
+ }
39
+
40
+ const state = {
41
+ dropTargetId: ""
42
+ };
43
+
44
+ function initializeCanvas() {
45
+ $("#list ul").sortable({
46
+ cancel: ".unsortable",
47
+ opacity: 0.8,
48
+ cursor: "move",
49
+ placeholder: "drop-placeholder",
50
+ update: function () {
51
+ const item_order = $("ul.reorder li").map(function () {
52
+ return this.id;
53
+ }).get();
54
+ var order_string = "order=" + item_order.join(",");
55
+
56
+ $.ajax({
57
+ method: "POST",
58
+ url: updateListUrl,
59
+ data: order_string,
60
+ cache: false,
61
+ success: function (data) {
62
+ // Update the canvas content with the new HTML
63
+ updateActionCanvas(data)
64
+ }
65
+ }).fail(function (jqXHR, textStatus, errorThrown) {
66
+ console.error("Failed to update order:", textStatus, errorThrown);
67
+ });
68
+ }
69
+ });
70
+
71
+ // Make Entire Accordion Item Draggable
72
+ $(".accordion-item").off("dragstart").on("dragstart", function (event) {
73
+ let formHtml = $(this).find(".accordion-body").html();
74
+ event.originalEvent.dataTransfer.setData("form", formHtml || "");
75
+ event.originalEvent.dataTransfer.setData("action", $(this).find(".draggable-action").data("action"));
76
+ event.originalEvent.dataTransfer.setData("id", $(this).find(".draggable-action").attr("id"));
77
+ $(this).addClass("dragging");
78
+ });
79
+
80
+ $("#list ul, .canvas").off("dragover").on("dragover", function (event) {
81
+ event.preventDefault();
82
+ let $target = $(event.target).closest("li");
83
+
84
+ if ($target.length) {
85
+ state.dropTargetId = $target.attr("id") || "";
86
+ insertDropPlaceholder($target);
87
+ } else if (!$("#list ul").children().length && $(this).hasClass("canvas")) {
88
+ $(".drop-placeholder").remove();
89
+ } else {
90
+ state.dropTargetId = "";
91
+ }
92
+ });
93
+
94
+ $("#list ul, .canvas").off("dragleave").on("dragleave", function () {
95
+ $(".drop-placeholder").remove();
96
+ });
97
+
98
+ $("#list ul, .canvas").off("drop").on("drop", function (event) {
99
+ event.preventDefault();
100
+ var actionName = event.originalEvent.dataTransfer.getData("action");
101
+ var actionId = event.originalEvent.dataTransfer.getData("id");
102
+ var formHtml = event.originalEvent.dataTransfer.getData("form");
103
+ let listLength = $("ul.reorder li").length;
104
+ state.dropTargetId = state.dropTargetId || listLength + 1;
105
+ $(".drop-placeholder").remove();
106
+ document.activeElement?.blur();
107
+ triggerModal(formHtml, actionName, actionId, state.dropTargetId);
108
+ });
109
+ }
110
+
111
+ function insertDropPlaceholder($target) {
112
+ $(".drop-placeholder").remove();
113
+ $("<li class='drop-placeholder'></li>").insertBefore($target);
114
+ }
115
+
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');
120
+
121
+ if (!formHtml) {
122
+ console.error("Form not found in accordion-body");
123
+ return false;
124
+ }
125
+
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"));
129
+
130
+ $(this).addClass("dragging");
131
+ });
132
+ }
133
+
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
+ });
Binary file