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.
@@ -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
+ }