syd 1.0.2__py3-none-any.whl → 1.2.0__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.
- syd/__init__.py +3 -10
- syd/flask_deployment/deployer.py +119 -26
- syd/flask_deployment/static/css/styles.css +100 -67
- syd/flask_deployment/static/css/viewer.css +48 -0
- syd/flask_deployment/static/js/modules/api.js +89 -0
- syd/flask_deployment/static/js/modules/config.js +22 -0
- syd/flask_deployment/static/js/modules/plot.js +75 -0
- syd/flask_deployment/static/js/modules/state.js +89 -0
- syd/flask_deployment/static/js/modules/system_controls.js +191 -0
- syd/flask_deployment/static/js/modules/ui_controls.js +812 -0
- syd/flask_deployment/static/js/modules/utils.js +49 -0
- syd/flask_deployment/static/js/old_viewer.js +1195 -0
- syd/flask_deployment/static/js/viewer.js +53 -826
- syd/flask_deployment/templates/index.html +1 -1
- syd/notebook_deployment/deployer.py +1 -3
- syd/notebook_deployment/widgets.py +45 -27
- syd/support.py +25 -0
- syd/viewer.py +35 -4
- {syd-1.0.2.dist-info → syd-1.2.0.dist-info}/METADATA +24 -10
- syd-1.2.0.dist-info/RECORD +28 -0
- syd-1.0.2.dist-info/RECORD +0 -19
- {syd-1.0.2.dist-info → syd-1.2.0.dist-info}/WHEEL +0 -0
- {syd-1.0.2.dist-info → syd-1.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { state } from './state.js';
|
|
2
|
+
import { updateThreshold } from './config.js';
|
|
3
|
+
import { updateStatus, createSlowLoadingImage } from './utils.js';
|
|
4
|
+
|
|
5
|
+
let loadingTimeout = null; // Timeout for showing loading state
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Update the plot with current state
|
|
9
|
+
*/
|
|
10
|
+
export function updatePlot() {
|
|
11
|
+
const plotImage = document.getElementById('plot-image');
|
|
12
|
+
if (!plotImage) {
|
|
13
|
+
console.warn("Plot image element not found");
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Clear any existing loading timeout
|
|
18
|
+
if (loadingTimeout) {
|
|
19
|
+
clearTimeout(loadingTimeout);
|
|
20
|
+
loadingTimeout = null; // Reset timeout variable
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Show loading state after threshold
|
|
24
|
+
loadingTimeout = setTimeout(() => {
|
|
25
|
+
const slowLoadingDataURL = createSlowLoadingImage(); // Get cached or create new
|
|
26
|
+
plotImage.src = slowLoadingDataURL;
|
|
27
|
+
plotImage.style.opacity = '0.5';
|
|
28
|
+
updateStatus('Generating plot...'); // Update status during loading
|
|
29
|
+
}, updateThreshold * 1000);
|
|
30
|
+
|
|
31
|
+
// Build query string from state
|
|
32
|
+
const queryParams = new URLSearchParams();
|
|
33
|
+
for (const [name, value] of Object.entries(state)) {
|
|
34
|
+
if (Array.isArray(value) || typeof value === 'object') {
|
|
35
|
+
// Ensure complex objects/arrays are properly stringified for URL
|
|
36
|
+
queryParams.append(name, JSON.stringify(value));
|
|
37
|
+
} else {
|
|
38
|
+
queryParams.append(name, value);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Set the image source to the plot endpoint with parameters
|
|
43
|
+
const url = `/plot?${queryParams.toString()}`;
|
|
44
|
+
|
|
45
|
+
// Use an Image object to preload and handle load/error events
|
|
46
|
+
const newImage = new Image();
|
|
47
|
+
|
|
48
|
+
newImage.onload = function() {
|
|
49
|
+
// Clear loading timeout if it hasn't fired yet
|
|
50
|
+
if (loadingTimeout) {
|
|
51
|
+
clearTimeout(loadingTimeout);
|
|
52
|
+
loadingTimeout = null;
|
|
53
|
+
}
|
|
54
|
+
// Update the actual image source and reset opacity
|
|
55
|
+
plotImage.src = url;
|
|
56
|
+
plotImage.style.opacity = 1;
|
|
57
|
+
// Don't necessarily set status to Ready here, as state updates might happen
|
|
58
|
+
// Let the calling function (updateParameter or initial load) handle final status
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
newImage.onerror = function() {
|
|
62
|
+
// Clear loading timeout
|
|
63
|
+
if (loadingTimeout) {
|
|
64
|
+
clearTimeout(loadingTimeout);
|
|
65
|
+
loadingTimeout = null;
|
|
66
|
+
}
|
|
67
|
+
updateStatus('Error loading plot');
|
|
68
|
+
plotImage.style.opacity = 1; // Reset opacity even on error
|
|
69
|
+
// Optionally display an error image/message
|
|
70
|
+
// plotImage.src = 'path/to/error/image.png';
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Start loading the new image
|
|
74
|
+
newImage.src = url;
|
|
75
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { updateControlValue } from './ui_controls.js';
|
|
2
|
+
import { updatePlot } from './plot.js';
|
|
3
|
+
import { updateParameterOnServer } from './api.js';
|
|
4
|
+
import { updateStatus } from './utils.js';
|
|
5
|
+
|
|
6
|
+
export let state = {};
|
|
7
|
+
export let paramInfo = {};
|
|
8
|
+
export let paramOrder = [];
|
|
9
|
+
let isUpdating = false;
|
|
10
|
+
|
|
11
|
+
// Function to initialize state (called after fetching initial data)
|
|
12
|
+
export function initializeState(initialData) {
|
|
13
|
+
paramInfo = initialData.params;
|
|
14
|
+
paramOrder = initialData.param_order;
|
|
15
|
+
// Initialize state from parameter info
|
|
16
|
+
for (const [name, param] of Object.entries(paramInfo)) {
|
|
17
|
+
state[name] = param.value;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Update a parameter value and send to server
|
|
23
|
+
*/
|
|
24
|
+
export function updateParameter(name, value) {
|
|
25
|
+
// Prevent recursive updates
|
|
26
|
+
if (isUpdating) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
// Indicate status update
|
|
30
|
+
updateStatus('Updating ' + name + '...');
|
|
31
|
+
|
|
32
|
+
// Update local state
|
|
33
|
+
state[name] = value;
|
|
34
|
+
|
|
35
|
+
// Send update to server (from api.js)
|
|
36
|
+
updateParameterOnServer(name, value)
|
|
37
|
+
.then(data => {
|
|
38
|
+
if (data.error) {
|
|
39
|
+
console.error('Error:', data.error);
|
|
40
|
+
} else {
|
|
41
|
+
// Update state with any changes from callbacks
|
|
42
|
+
updateStateFromServer(data.state, data.params);
|
|
43
|
+
// Update plot
|
|
44
|
+
updatePlot();
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
.catch(error => {
|
|
48
|
+
console.error('Error:', error);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Indicate status update
|
|
52
|
+
updateStatus('Ready!');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Update local state from server response
|
|
57
|
+
*/
|
|
58
|
+
export function updateStateFromServer(serverState, serverParamInfo) {
|
|
59
|
+
// Set updating flag to prevent recursive updates
|
|
60
|
+
isUpdating = true;
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
// Update global paramInfo first if it changed
|
|
64
|
+
if (serverParamInfo) {
|
|
65
|
+
// Basic check: update if stringified versions differ. Might need deeper comparison.
|
|
66
|
+
if(JSON.stringify(paramInfo) !== JSON.stringify(serverParamInfo)) {
|
|
67
|
+
paramInfo = serverParamInfo;
|
|
68
|
+
// TODO: Potentially re-create controls if param info structure changed significantly?
|
|
69
|
+
// For now, we only update values below.
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Update any parameters that changed due to callbacks
|
|
74
|
+
for (const [name, value] of Object.entries(serverState)) {
|
|
75
|
+
// Check if state value changed OR if paramInfo for this specific param changed
|
|
76
|
+
const currentParamInfoStr = paramInfo[name] ? JSON.stringify(paramInfo[name]) : undefined;
|
|
77
|
+
const serverParamInfoStr = serverParamInfo && serverParamInfo[name] ? JSON.stringify(serverParamInfo[name]) : undefined;
|
|
78
|
+
|
|
79
|
+
if (JSON.stringify(state[name]) !== JSON.stringify(value) || currentParamInfoStr !== serverParamInfoStr) {
|
|
80
|
+
state[name] = value;
|
|
81
|
+
// Pass the potentially updated paramInfo for this specific control
|
|
82
|
+
updateControlValue(name, value, serverParamInfo ? serverParamInfo[name] : paramInfo[name]);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} finally {
|
|
86
|
+
// Clear updating flag
|
|
87
|
+
isUpdating = false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { config, updateThreshold, setUpdateThreshold, setConfigValue } from './config.js';
|
|
2
|
+
// Import createFloatController - we assume it will be exported from ui_controls.js
|
|
3
|
+
import { createFloatController } from './ui_controls.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Create system controls (width, threshold, margin)
|
|
7
|
+
* @param {HTMLElement} container - The DOM element to append controls to.
|
|
8
|
+
*/
|
|
9
|
+
export function createSystemControls(container) {
|
|
10
|
+
// Create controls width slider
|
|
11
|
+
const widthControl = createFloatController('controls_width', {
|
|
12
|
+
type: 'float',
|
|
13
|
+
value: config.controlsWidthPercent,
|
|
14
|
+
min: 10,
|
|
15
|
+
max: 50,
|
|
16
|
+
step: 1
|
|
17
|
+
});
|
|
18
|
+
widthControl.className = 'numeric-control system-control';
|
|
19
|
+
|
|
20
|
+
// Add label for width control
|
|
21
|
+
const widthLabel = document.createElement('span');
|
|
22
|
+
widthLabel.className = 'control-label';
|
|
23
|
+
widthLabel.textContent = 'Controls Width %';
|
|
24
|
+
|
|
25
|
+
const widthGroup = document.createElement('div');
|
|
26
|
+
widthGroup.className = 'control-group';
|
|
27
|
+
widthGroup.appendChild(widthLabel);
|
|
28
|
+
widthGroup.appendChild(widthControl);
|
|
29
|
+
|
|
30
|
+
// Create update threshold slider
|
|
31
|
+
const thresholdControl = createFloatController('update_threshold', {
|
|
32
|
+
type: 'float',
|
|
33
|
+
value: updateThreshold,
|
|
34
|
+
min: 0.1,
|
|
35
|
+
max: 10.0,
|
|
36
|
+
step: 0.1
|
|
37
|
+
});
|
|
38
|
+
thresholdControl.className = 'numeric-control system-control';
|
|
39
|
+
|
|
40
|
+
// Add label for threshold control
|
|
41
|
+
const thresholdLabel = document.createElement('span');
|
|
42
|
+
thresholdLabel.className = 'control-label';
|
|
43
|
+
thresholdLabel.textContent = 'Update Threshold';
|
|
44
|
+
|
|
45
|
+
const thresholdGroup = document.createElement('div');
|
|
46
|
+
thresholdGroup.className = 'control-group';
|
|
47
|
+
thresholdGroup.appendChild(thresholdLabel);
|
|
48
|
+
thresholdGroup.appendChild(thresholdControl);
|
|
49
|
+
|
|
50
|
+
// Create plot margin slider
|
|
51
|
+
const plotMarginControl = createFloatController('plot_margin', {
|
|
52
|
+
type: 'float',
|
|
53
|
+
value: config.plotMarginPercent,
|
|
54
|
+
min: 0,
|
|
55
|
+
max: 50,
|
|
56
|
+
step: 1
|
|
57
|
+
});
|
|
58
|
+
plotMarginControl.className = 'numeric-control system-control';
|
|
59
|
+
|
|
60
|
+
// Add label for margin control
|
|
61
|
+
const marginLabel = document.createElement('span');
|
|
62
|
+
marginLabel.className = 'control-label';
|
|
63
|
+
marginLabel.textContent = 'Plot Margin %';
|
|
64
|
+
|
|
65
|
+
const marginGroup = document.createElement('div');
|
|
66
|
+
marginGroup.className = 'control-group';
|
|
67
|
+
marginGroup.appendChild(marginLabel);
|
|
68
|
+
marginGroup.appendChild(plotMarginControl);
|
|
69
|
+
|
|
70
|
+
// Add custom event listeners
|
|
71
|
+
// Width Control Listeners
|
|
72
|
+
function updateControlsWidth(width) {
|
|
73
|
+
setConfigValue('controlsWidthPercent', width); // Use setter from config.js
|
|
74
|
+
|
|
75
|
+
// Update the root containers using querySelector for classes
|
|
76
|
+
const rootContainer = document.querySelector('.viewer-container');
|
|
77
|
+
const controlsContainer = document.querySelector('.controls-container'); // Select the outer div by class
|
|
78
|
+
const plotContainer = document.querySelector('.plot-container');
|
|
79
|
+
|
|
80
|
+
if (rootContainer && controlsContainer && plotContainer) {
|
|
81
|
+
if (config.controlsPosition === 'left' || config.controlsPosition === 'right') {
|
|
82
|
+
controlsContainer.style.width = `${width}%`;
|
|
83
|
+
plotContainer.style.width = `${100 - width}%`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Ensure slider/input values match
|
|
88
|
+
widthSlider.value = width;
|
|
89
|
+
widthInput.value = width;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const widthSlider = widthControl.querySelector('input[type="range"]');
|
|
93
|
+
const widthInput = widthControl.querySelector('input[type="number"]');
|
|
94
|
+
|
|
95
|
+
widthSlider.addEventListener('input', function() { // Real-time update for number input
|
|
96
|
+
updateControlsWidth(this.value);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
widthInput.addEventListener('change', function() {
|
|
100
|
+
updateControlsWidth(this.value);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Threshold Control Listeners
|
|
104
|
+
const thresholdSlider = thresholdControl.querySelector('input[type="range"]');
|
|
105
|
+
const thresholdInput = thresholdControl.querySelector('input[type="number"]');
|
|
106
|
+
|
|
107
|
+
thresholdSlider.addEventListener('input', function() { // Real-time update for number input
|
|
108
|
+
thresholdInput.value = this.value;
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
thresholdSlider.addEventListener('change', function() {
|
|
112
|
+
const newThreshold = parseFloat(this.value);
|
|
113
|
+
setUpdateThreshold(newThreshold); // Use setter from config.js
|
|
114
|
+
thresholdInput.value = newThreshold; // Ensure input matches final value
|
|
115
|
+
thresholdSlider.value = newThreshold; // Ensure slider matches final value
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Plot Margin Control Listeners
|
|
119
|
+
const marginSlider = plotMarginControl.querySelector('input[type="range"]');
|
|
120
|
+
const marginInput = plotMarginControl.querySelector('input[type="number"]');
|
|
121
|
+
const plotContainer = document.querySelector('.plot-container');
|
|
122
|
+
|
|
123
|
+
// Function to apply margin and adjust size of the plot image
|
|
124
|
+
function applyPlotMargin(marginPercent) {
|
|
125
|
+
const plotImage = document.getElementById('plot-image'); // Get the image element
|
|
126
|
+
if (plotImage) {
|
|
127
|
+
const effectiveMargin = parseFloat(marginPercent); // Ensure it's a number
|
|
128
|
+
// Apply margin to the image
|
|
129
|
+
plotImage.style.margin = `${effectiveMargin}%`;
|
|
130
|
+
// Adjust width and height to account for the margin
|
|
131
|
+
plotImage.style.width = `calc(100% - ${2 * effectiveMargin}%)`;
|
|
132
|
+
plotImage.style.height = `calc(100% - ${2 * effectiveMargin}%)`;
|
|
133
|
+
// Reset container padding just in case
|
|
134
|
+
if (plotContainer) {
|
|
135
|
+
plotContainer.style.padding = '0';
|
|
136
|
+
}
|
|
137
|
+
setConfigValue('plotMarginPercent', effectiveMargin); // Update config using setter
|
|
138
|
+
} else {
|
|
139
|
+
console.warn('Plot image element not found when applying margin.');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
marginSlider.addEventListener('input', function() { // Real-time update for number input
|
|
144
|
+
marginInput.value = this.value;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
marginSlider.addEventListener('change', function() {
|
|
148
|
+
const margin = parseFloat(this.value);
|
|
149
|
+
marginInput.value = margin; // Ensure input matches final value
|
|
150
|
+
marginSlider.value = margin; // Ensure slider matches final value
|
|
151
|
+
applyPlotMargin(margin);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Add wheel event listener to plot container for margin control
|
|
155
|
+
if (plotContainer) {
|
|
156
|
+
plotContainer.addEventListener('wheel', function(event) {
|
|
157
|
+
event.preventDefault(); // Prevent page scrolling
|
|
158
|
+
|
|
159
|
+
const currentValue = parseFloat(marginSlider.value);
|
|
160
|
+
const step = parseFloat(marginSlider.step) || 1;
|
|
161
|
+
const min = parseFloat(marginSlider.min);
|
|
162
|
+
const max = parseFloat(marginSlider.max);
|
|
163
|
+
|
|
164
|
+
let newValue;
|
|
165
|
+
if (event.deltaY < 0) {
|
|
166
|
+
// Scrolling up (or zoom in) -> decrease margin
|
|
167
|
+
newValue = currentValue - step;
|
|
168
|
+
} else {
|
|
169
|
+
// Scrolling down (or zoom out) -> increase margin
|
|
170
|
+
newValue = currentValue + step;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Clamp the value within min/max bounds
|
|
174
|
+
newValue = Math.max(min, Math.min(max, newValue));
|
|
175
|
+
|
|
176
|
+
// Only update if the value actually changed
|
|
177
|
+
if (newValue !== currentValue) {
|
|
178
|
+
marginSlider.value = newValue;
|
|
179
|
+
marginInput.value = newValue;
|
|
180
|
+
applyPlotMargin(newValue);
|
|
181
|
+
}
|
|
182
|
+
}, { passive: false }); // Need passive: false to call preventDefault()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Apply initial margin
|
|
186
|
+
applyPlotMargin(config.plotMarginPercent);
|
|
187
|
+
|
|
188
|
+
container.appendChild(widthGroup);
|
|
189
|
+
container.appendChild(thresholdGroup);
|
|
190
|
+
container.appendChild(marginGroup);
|
|
191
|
+
}
|