scitex 2.7.3__py3-none-any.whl → 2.8.1__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.
Files changed (160) hide show
  1. scitex/__version__.py +1 -1
  2. scitex/dev/plt/__init__.py +0 -0
  3. scitex/dev/plt/plot_mpl_axhline.py +0 -0
  4. scitex/dev/plt/plot_mpl_axhspan.py +0 -0
  5. scitex/dev/plt/plot_mpl_axvline.py +0 -0
  6. scitex/dev/plt/plot_mpl_axvspan.py +0 -0
  7. scitex/dev/plt/plot_mpl_bar.py +0 -0
  8. scitex/dev/plt/plot_mpl_barh.py +0 -0
  9. scitex/dev/plt/plot_mpl_boxplot.py +0 -0
  10. scitex/dev/plt/plot_mpl_contour.py +0 -0
  11. scitex/dev/plt/plot_mpl_contourf.py +0 -0
  12. scitex/dev/plt/plot_mpl_errorbar.py +0 -0
  13. scitex/dev/plt/plot_mpl_eventplot.py +0 -0
  14. scitex/dev/plt/plot_mpl_fill.py +0 -0
  15. scitex/dev/plt/plot_mpl_fill_between.py +0 -0
  16. scitex/dev/plt/plot_mpl_hexbin.py +0 -0
  17. scitex/dev/plt/plot_mpl_hist.py +0 -0
  18. scitex/dev/plt/plot_mpl_hist2d.py +0 -0
  19. scitex/dev/plt/plot_mpl_imshow.py +0 -0
  20. scitex/dev/plt/plot_mpl_pcolormesh.py +0 -0
  21. scitex/dev/plt/plot_mpl_pie.py +0 -0
  22. scitex/dev/plt/plot_mpl_plot.py +0 -0
  23. scitex/dev/plt/plot_mpl_quiver.py +0 -0
  24. scitex/dev/plt/plot_mpl_scatter.py +0 -0
  25. scitex/dev/plt/plot_mpl_stackplot.py +0 -0
  26. scitex/dev/plt/plot_mpl_stem.py +0 -0
  27. scitex/dev/plt/plot_mpl_step.py +0 -0
  28. scitex/dev/plt/plot_mpl_violinplot.py +0 -0
  29. scitex/dev/plt/plot_sns_barplot.py +0 -0
  30. scitex/dev/plt/plot_sns_boxplot.py +0 -0
  31. scitex/dev/plt/plot_sns_heatmap.py +0 -0
  32. scitex/dev/plt/plot_sns_histplot.py +0 -0
  33. scitex/dev/plt/plot_sns_kdeplot.py +0 -0
  34. scitex/dev/plt/plot_sns_lineplot.py +0 -0
  35. scitex/dev/plt/plot_sns_scatterplot.py +0 -0
  36. scitex/dev/plt/plot_sns_stripplot.py +0 -0
  37. scitex/dev/plt/plot_sns_swarmplot.py +0 -0
  38. scitex/dev/plt/plot_sns_violinplot.py +0 -0
  39. scitex/dev/plt/plot_stx_bar.py +0 -0
  40. scitex/dev/plt/plot_stx_barh.py +0 -0
  41. scitex/dev/plt/plot_stx_box.py +0 -0
  42. scitex/dev/plt/plot_stx_boxplot.py +0 -0
  43. scitex/dev/plt/plot_stx_conf_mat.py +0 -0
  44. scitex/dev/plt/plot_stx_contour.py +0 -0
  45. scitex/dev/plt/plot_stx_ecdf.py +0 -0
  46. scitex/dev/plt/plot_stx_errorbar.py +0 -0
  47. scitex/dev/plt/plot_stx_fill_between.py +0 -0
  48. scitex/dev/plt/plot_stx_fillv.py +0 -0
  49. scitex/dev/plt/plot_stx_heatmap.py +0 -0
  50. scitex/dev/plt/plot_stx_image.py +0 -0
  51. scitex/dev/plt/plot_stx_imshow.py +0 -0
  52. scitex/dev/plt/plot_stx_joyplot.py +0 -0
  53. scitex/dev/plt/plot_stx_kde.py +0 -0
  54. scitex/dev/plt/plot_stx_line.py +0 -0
  55. scitex/dev/plt/plot_stx_mean_ci.py +0 -0
  56. scitex/dev/plt/plot_stx_mean_std.py +0 -0
  57. scitex/dev/plt/plot_stx_median_iqr.py +0 -0
  58. scitex/dev/plt/plot_stx_raster.py +0 -0
  59. scitex/dev/plt/plot_stx_rectangle.py +0 -0
  60. scitex/dev/plt/plot_stx_scatter.py +0 -0
  61. scitex/dev/plt/plot_stx_shaded_line.py +0 -0
  62. scitex/dev/plt/plot_stx_violin.py +0 -0
  63. scitex/dev/plt/plot_stx_violinplot.py +0 -0
  64. scitex/diagram/README.md +197 -0
  65. scitex/diagram/__init__.py +48 -0
  66. scitex/diagram/_compile.py +312 -0
  67. scitex/diagram/_diagram.py +355 -0
  68. scitex/diagram/_presets.py +173 -0
  69. scitex/diagram/_schema.py +182 -0
  70. scitex/diagram/_split.py +278 -0
  71. scitex/fig/editor/__init__.py +5 -2
  72. scitex/fig/editor/_dearpygui_editor.py +1 -1
  73. scitex/fig/editor/_mpl_editor.py +1 -1
  74. scitex/fig/editor/_qt_editor.py +1 -1
  75. scitex/fig/editor/_tkinter_editor.py +1 -1
  76. scitex/fig/editor/edit/__init__.py +50 -0
  77. scitex/fig/editor/edit/backend_detector.py +109 -0
  78. scitex/fig/editor/edit/bundle_resolver.py +240 -0
  79. scitex/fig/editor/edit/editor_launcher.py +239 -0
  80. scitex/fig/editor/edit/manual_handler.py +53 -0
  81. scitex/fig/editor/edit/panel_loader.py +232 -0
  82. scitex/fig/editor/edit/path_resolver.py +67 -0
  83. scitex/fig/editor/flask_editor/_bbox.py +23 -0
  84. scitex/fig/editor/flask_editor/_core.py +908 -103
  85. scitex/fig/editor/flask_editor/_renderer.py +74 -0
  86. scitex/fig/editor/flask_editor/static/css/base/reset.css +41 -0
  87. scitex/fig/editor/flask_editor/static/css/base/typography.css +16 -0
  88. scitex/fig/editor/flask_editor/static/css/base/variables.css +85 -0
  89. scitex/fig/editor/flask_editor/static/css/components/buttons.css +217 -0
  90. scitex/fig/editor/flask_editor/static/css/components/context-menu.css +93 -0
  91. scitex/fig/editor/flask_editor/static/css/components/dropdown.css +57 -0
  92. scitex/fig/editor/flask_editor/static/css/components/forms.css +112 -0
  93. scitex/fig/editor/flask_editor/static/css/components/modal.css +59 -0
  94. scitex/fig/editor/flask_editor/static/css/components/sections.css +212 -0
  95. scitex/fig/editor/flask_editor/static/css/features/canvas.css +176 -0
  96. scitex/fig/editor/flask_editor/static/css/features/element-inspector.css +190 -0
  97. scitex/fig/editor/flask_editor/static/css/features/loading.css +59 -0
  98. scitex/fig/editor/flask_editor/static/css/features/overlay.css +45 -0
  99. scitex/fig/editor/flask_editor/static/css/features/panel-grid.css +95 -0
  100. scitex/fig/editor/flask_editor/static/css/features/selection.css +101 -0
  101. scitex/fig/editor/flask_editor/static/css/features/statistics.css +138 -0
  102. scitex/fig/editor/flask_editor/static/css/index.css +31 -0
  103. scitex/fig/editor/flask_editor/static/css/layout/container.css +7 -0
  104. scitex/fig/editor/flask_editor/static/css/layout/controls.css +56 -0
  105. scitex/fig/editor/flask_editor/static/css/layout/preview.css +78 -0
  106. scitex/fig/editor/flask_editor/static/js/alignment/axis.js +314 -0
  107. scitex/fig/editor/flask_editor/static/js/alignment/basic.js +107 -0
  108. scitex/fig/editor/flask_editor/static/js/alignment/distribute.js +54 -0
  109. scitex/fig/editor/flask_editor/static/js/canvas/canvas.js +172 -0
  110. scitex/fig/editor/flask_editor/static/js/canvas/dragging.js +258 -0
  111. scitex/fig/editor/flask_editor/static/js/canvas/resize.js +48 -0
  112. scitex/fig/editor/flask_editor/static/js/canvas/selection.js +71 -0
  113. scitex/fig/editor/flask_editor/static/js/core/api.js +288 -0
  114. scitex/fig/editor/flask_editor/static/js/core/state.js +143 -0
  115. scitex/fig/editor/flask_editor/static/js/core/utils.js +245 -0
  116. scitex/fig/editor/flask_editor/static/js/dev/element-inspector.js +992 -0
  117. scitex/fig/editor/flask_editor/static/js/editor/bbox.js +339 -0
  118. scitex/fig/editor/flask_editor/static/js/editor/element-drag.js +286 -0
  119. scitex/fig/editor/flask_editor/static/js/editor/overlay.js +371 -0
  120. scitex/fig/editor/flask_editor/static/js/editor/preview.js +293 -0
  121. scitex/fig/editor/flask_editor/static/js/main.js +426 -0
  122. scitex/fig/editor/flask_editor/static/js/shortcuts/context-menu.js +152 -0
  123. scitex/fig/editor/flask_editor/static/js/shortcuts/keyboard.js +265 -0
  124. scitex/fig/editor/flask_editor/static/js/ui/controls.js +184 -0
  125. scitex/fig/editor/flask_editor/static/js/ui/download.js +57 -0
  126. scitex/fig/editor/flask_editor/static/js/ui/help.js +100 -0
  127. scitex/fig/editor/flask_editor/static/js/ui/theme.js +34 -0
  128. scitex/fig/editor/flask_editor/templates/__init__.py +95 -5
  129. scitex/fig/editor/flask_editor/templates/_html.py +27 -9
  130. scitex/fig/editor/flask_editor/templates/_scripts.py +1928 -131
  131. scitex/fig/editor/flask_editor/templates/_styles.py +363 -51
  132. scitex/fig/io/_bundle.py +97 -12
  133. scitex/io/__init__.py +12 -0
  134. scitex/io/_bundle.py +69 -10
  135. scitex/io/_zip_bundle.py +439 -0
  136. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/__init__.py +0 -0
  137. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_labels.py +0 -0
  138. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_metadata.py +0 -0
  139. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_visual.py +0 -0
  140. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +0 -0
  141. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_base.py +0 -0
  142. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_scientific.py +0 -0
  143. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_statistical.py +0 -0
  144. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_stx_aliases.py +0 -0
  145. scitex/plt/_subplots/_AxisWrapperMixins/_RawMatplotlibMixin.py +0 -0
  146. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/__init__.py +0 -0
  147. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_base.py +0 -0
  148. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +0 -0
  149. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_bar.py +0 -0
  150. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_barh.py +0 -0
  151. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_errorbar.py +0 -0
  152. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter.py +0 -0
  153. scitex/plt/io/_layered_bundle.py +0 -0
  154. scitex/schema/_plot.py +0 -0
  155. {scitex-2.7.3.dist-info → scitex-2.8.1.dist-info}/METADATA +1 -1
  156. {scitex-2.7.3.dist-info → scitex-2.8.1.dist-info}/RECORD +78 -22
  157. scitex/fig/editor/_edit.py +0 -751
  158. {scitex-2.7.3.dist-info → scitex-2.8.1.dist-info}/WHEEL +0 -0
  159. {scitex-2.7.3.dist-info → scitex-2.8.1.dist-info}/entry_points.txt +0 -0
  160. {scitex-2.7.3.dist-info → scitex-2.8.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,265 @@
1
+ /**
2
+ * Keyboard Shortcuts
3
+ * Handles all keyboard shortcuts for the editor
4
+ */
5
+
6
+ // ============================================================================
7
+ // Main Keyboard Event Handler
8
+ // ============================================================================
9
+ document.addEventListener('keydown', (e) => {
10
+ const key = e.key.toLowerCase();
11
+ const isCtrl = e.ctrlKey || e.metaKey;
12
+ const isShift = e.shiftKey;
13
+ const isAlt = e.altKey;
14
+
15
+ // Don't capture shortcuts when typing in inputs
16
+ if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.tagName === 'SELECT') {
17
+ return;
18
+ }
19
+
20
+ // =========================================================================
21
+ // Multi-key shortcut mode (Alt+A → alignment, Alt+Shift+A → axis alignment)
22
+ // =========================================================================
23
+ if (shortcutMode === 'align') {
24
+ e.preventDefault();
25
+ handleAlignShortcut(key, isShift);
26
+ shortcutMode = null;
27
+ return;
28
+ }
29
+
30
+ if (shortcutMode === 'alignByAxis') {
31
+ e.preventDefault();
32
+ handleAlignByAxisShortcut(key);
33
+ shortcutMode = null;
34
+ return;
35
+ }
36
+
37
+ // =========================================================================
38
+ // Basic Operations
39
+ // =========================================================================
40
+
41
+ // Ctrl+S: Save
42
+ if (isCtrl && key === 's') {
43
+ e.preventDefault();
44
+ saveToBundle();
45
+ return;
46
+ }
47
+
48
+ // Ctrl+Z: Undo
49
+ if (isCtrl && !isShift && key === 'z') {
50
+ e.preventDefault();
51
+ undoLastChange();
52
+ return;
53
+ }
54
+
55
+ // Ctrl+Y or Ctrl+Shift+Z: Redo
56
+ if ((isCtrl && key === 'y') || (isCtrl && isShift && key === 'z')) {
57
+ e.preventDefault();
58
+ redoLastChange();
59
+ return;
60
+ }
61
+
62
+ // Delete: Remove selected element override
63
+ if (key === 'delete' || key === 'backspace') {
64
+ if (selectedElement && !isCtrl) {
65
+ e.preventDefault();
66
+ deleteSelectedOverride();
67
+ return;
68
+ }
69
+ }
70
+
71
+ // =========================================================================
72
+ // Panel/Element Movement (Arrow keys)
73
+ // =========================================================================
74
+
75
+ // Arrow keys: Move selected panel by 1mm (or 5mm with Shift)
76
+ if (['arrowup', 'arrowdown', 'arrowleft', 'arrowright'].includes(key)) {
77
+ e.preventDefault();
78
+ const amount = isShift ? 5 : 1; // 5mm or 1mm
79
+ moveSelectedPanel(key.replace('arrow', ''), amount);
80
+ return;
81
+ }
82
+
83
+ // =========================================================================
84
+ // View Controls
85
+ // =========================================================================
86
+
87
+ // + or =: Zoom in
88
+ if ((key === '+' || key === '=') && !isCtrl) {
89
+ e.preventDefault();
90
+ zoomCanvas(1.1);
91
+ return;
92
+ }
93
+
94
+ // -: Zoom out
95
+ if (key === '-' && !isCtrl) {
96
+ e.preventDefault();
97
+ zoomCanvas(0.9);
98
+ return;
99
+ }
100
+
101
+ // 0: Fit to window
102
+ if (key === '0' && !isCtrl) {
103
+ e.preventDefault();
104
+ fitCanvasToWindow();
105
+ return;
106
+ }
107
+
108
+ // Ctrl++ : Increase canvas size
109
+ if (isCtrl && (key === '+' || key === '=')) {
110
+ e.preventDefault();
111
+ resizeCanvas(1.1);
112
+ return;
113
+ }
114
+
115
+ // Ctrl+- : Decrease canvas size
116
+ if (isCtrl && key === '-') {
117
+ e.preventDefault();
118
+ resizeCanvas(0.9);
119
+ return;
120
+ }
121
+
122
+ // =========================================================================
123
+ // Alignment Modes (Alt+A → basic, Alt+Shift+A → by axis)
124
+ // =========================================================================
125
+ if (isAlt && isShift && key === 'a') {
126
+ // Alt+Shift+A: Align by Axis (scientific alignment based on plot axes)
127
+ e.preventDefault();
128
+ shortcutMode = 'alignByAxis';
129
+ setStatus('Align by Axis: L=Y-Axis(left) R=Right T=Top B=X-Axis(bottom) C=Center-H M=Center-V S=Stack', false);
130
+ setTimeout(() => {
131
+ if (shortcutMode === 'alignByAxis') {
132
+ shortcutMode = null;
133
+ setStatus('Ready', false);
134
+ }
135
+ }, 3000);
136
+ return;
137
+ }
138
+ if (isAlt && !isShift && key === 'a') {
139
+ // Alt+A: Basic alignment (by bounding box)
140
+ e.preventDefault();
141
+ shortcutMode = 'align';
142
+ setStatus('Alignment mode: L=Left R=Right T=Top B=Bottom C=Center H=DistH V=DistV', false);
143
+ setTimeout(() => {
144
+ if (shortcutMode === 'align') {
145
+ shortcutMode = null;
146
+ setStatus('Ready', false);
147
+ }
148
+ }, 3000);
149
+ return;
150
+ }
151
+
152
+ // =========================================================================
153
+ // Arrange (Alt+F, Alt+B)
154
+ // =========================================================================
155
+ if (isAlt && key === 'f') {
156
+ e.preventDefault();
157
+ bringPanelToFront();
158
+ return;
159
+ }
160
+
161
+ if (isAlt && key === 'b') {
162
+ e.preventDefault();
163
+ sendPanelToBack();
164
+ return;
165
+ }
166
+
167
+ // =========================================================================
168
+ // Escape: Deselect/Cancel mode
169
+ // =========================================================================
170
+ if (key === 'escape') {
171
+ e.preventDefault();
172
+ deselectAllPanels();
173
+ shortcutMode = null;
174
+ return;
175
+ }
176
+
177
+ // =========================================================================
178
+ // G: Toggle grid visibility
179
+ // =========================================================================
180
+ if (key === 'g' && !isCtrl && !isAlt) {
181
+ e.preventDefault();
182
+ toggleGridVisibility();
183
+ return;
184
+ }
185
+
186
+ // =========================================================================
187
+ // Ctrl+A: Select all panels
188
+ // =========================================================================
189
+ if (isCtrl && key === 'a') {
190
+ e.preventDefault();
191
+ selectAllPanels();
192
+ return;
193
+ }
194
+
195
+ // =========================================================================
196
+ // Help (? or F1)
197
+ // =========================================================================
198
+ if (key === '?' || key === 'f1') {
199
+ e.preventDefault();
200
+ showShortcutHelp();
201
+ return;
202
+ }
203
+ });
204
+
205
+ // ============================================================================
206
+ // Undo/Redo Functions
207
+ // ============================================================================
208
+ function undoLastChange() {
209
+ if (undoStack.length === 0) {
210
+ setStatus('Nothing to undo', true);
211
+ return;
212
+ }
213
+
214
+ const prevState = undoStack.pop();
215
+ redoStack.push({...overrides});
216
+ overrides = prevState;
217
+ updatePreview();
218
+ setStatus('Undone');
219
+ }
220
+
221
+ function redoLastChange() {
222
+ if (redoStack.length === 0) {
223
+ setStatus('Nothing to redo', true);
224
+ return;
225
+ }
226
+
227
+ const nextState = redoStack.pop();
228
+ undoStack.push({...overrides});
229
+ overrides = nextState;
230
+ updatePreview();
231
+ setStatus('Redone');
232
+ }
233
+
234
+ function saveUndoState() {
235
+ undoStack.push({...overrides});
236
+ redoStack = []; // Clear redo stack on new change
237
+ }
238
+
239
+ // ============================================================================
240
+ // Delete Override
241
+ // ============================================================================
242
+ function deleteSelectedOverride() {
243
+ if (!selectedElement) return;
244
+
245
+ // Remove from element_overrides
246
+ if (overrides.element_overrides && overrides.element_overrides[selectedElement]) {
247
+ delete overrides.element_overrides[selectedElement];
248
+ setStatus(`Deleted override for ${selectedElement}`);
249
+ updatePreview();
250
+ }
251
+ }
252
+
253
+ // ============================================================================
254
+ // Grid Visibility Toggle
255
+ // ============================================================================
256
+ function toggleGridVisibility() {
257
+ gridVisible = !gridVisible;
258
+ const canvas = document.getElementById('panel-canvas');
259
+ if (canvas) {
260
+ canvas.style.backgroundImage = gridVisible ?
261
+ 'linear-gradient(rgba(128,128,128,0.1) 1px, transparent 1px), linear-gradient(90deg, rgba(128,128,128,0.1) 1px, transparent 1px)' :
262
+ 'none';
263
+ }
264
+ setStatus(`Grid ${gridVisible ? 'visible' : 'hidden'}`);
265
+ }
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Controls Panel
3
+ * Handles all form controls and data collection
4
+ */
5
+
6
+ // ============================================================================
7
+ // Collect Overrides from Form
8
+ // ============================================================================
9
+ function collectOverrides() {
10
+ const o = {};
11
+
12
+ // Labels - Title
13
+ const title = document.getElementById('title')?.value;
14
+ if (title) o.title = title;
15
+ o.show_title = document.getElementById('show_title')?.checked;
16
+ o.title_fontsize = parseInt(document.getElementById('title_fontsize')?.value) || 8;
17
+
18
+ // Labels - Caption
19
+ const caption = document.getElementById('caption')?.value;
20
+ if (caption) o.caption = caption;
21
+ o.show_caption = document.getElementById('show_caption')?.checked;
22
+ o.caption_fontsize = parseInt(document.getElementById('caption_fontsize')?.value) || 7;
23
+
24
+ // Labels - Axis
25
+ const xlabel = document.getElementById('xlabel')?.value;
26
+ const ylabel = document.getElementById('ylabel')?.value;
27
+ if (xlabel) o.xlabel = xlabel;
28
+ if (ylabel) o.ylabel = ylabel;
29
+
30
+ // Axis limits
31
+ const xmin = document.getElementById('xmin')?.value;
32
+ const xmax = document.getElementById('xmax')?.value;
33
+ if (xmin !== '' && xmax !== '') o.xlim = [parseFloat(xmin), parseFloat(xmax)];
34
+
35
+ const ymin = document.getElementById('ymin')?.value;
36
+ const ymax = document.getElementById('ymax')?.value;
37
+ if (ymin !== '' && ymax !== '') o.ylim = [parseFloat(ymin), parseFloat(ymax)];
38
+
39
+ // Traces
40
+ o.traces = traces;
41
+
42
+ // Legend
43
+ o.legend_visible = document.getElementById('legend_visible')?.checked;
44
+ o.legend_loc = document.getElementById('legend_loc')?.value;
45
+ o.legend_frameon = document.getElementById('legend_frameon')?.checked;
46
+ o.legend_fontsize = parseInt(document.getElementById('legend_fontsize')?.value) || 6;
47
+ o.legend_ncols = parseInt(document.getElementById('legend_ncols')?.value) || 1;
48
+ o.legend_x = parseFloat(document.getElementById('legend_x')?.value) || 0.95;
49
+ o.legend_y = parseFloat(document.getElementById('legend_y')?.value) || 0.95;
50
+
51
+ // Axis and Ticks - X Axis (Bottom)
52
+ o.x_n_ticks = parseInt(document.getElementById('x_n_ticks')?.value) || 4;
53
+ o.hide_x_ticks = document.getElementById('hide_x_ticks')?.checked;
54
+ o.x_tick_fontsize = parseInt(document.getElementById('x_tick_fontsize')?.value) || 7;
55
+ o.x_tick_direction = document.getElementById('x_tick_direction')?.value;
56
+ o.x_tick_length = parseFloat(document.getElementById('x_tick_length')?.value) || 0.8;
57
+ o.x_tick_width = parseFloat(document.getElementById('x_tick_width')?.value) || 0.2;
58
+
59
+ // X Axis (Top)
60
+ o.show_x_top = document.getElementById('show_x_top')?.checked;
61
+ o.x_top_mirror = document.getElementById('x_top_mirror')?.checked;
62
+
63
+ // Y Axis (Left)
64
+ o.y_n_ticks = parseInt(document.getElementById('y_n_ticks')?.value) || 4;
65
+ o.hide_y_ticks = document.getElementById('hide_y_ticks')?.checked;
66
+ o.y_tick_fontsize = parseInt(document.getElementById('y_tick_fontsize')?.value) || 7;
67
+ o.y_tick_direction = document.getElementById('y_tick_direction')?.value;
68
+ o.y_tick_length = parseFloat(document.getElementById('y_tick_length')?.value) || 0.8;
69
+ o.y_tick_width = parseFloat(document.getElementById('y_tick_width')?.value) || 0.2;
70
+
71
+ // Y Axis (Right)
72
+ o.show_y_right = document.getElementById('show_y_right')?.checked;
73
+ o.y_right_mirror = document.getElementById('y_right_mirror')?.checked;
74
+
75
+ // Spines
76
+ o.hide_bottom_spine = document.getElementById('hide_bottom_spine')?.checked;
77
+ o.hide_left_spine = document.getElementById('hide_left_spine')?.checked;
78
+
79
+ // Z Axis (3D)
80
+ o.hide_z_ticks = document.getElementById('hide_z_ticks')?.checked;
81
+ o.z_n_ticks = parseInt(document.getElementById('z_n_ticks')?.value) || 4;
82
+ o.z_tick_fontsize = parseInt(document.getElementById('z_tick_fontsize')?.value) || 7;
83
+ o.z_tick_direction = document.getElementById('z_tick_direction')?.value;
84
+
85
+ // Style
86
+ o.grid = document.getElementById('grid')?.checked;
87
+ o.hide_top_spine = document.getElementById('hide_top_spine')?.checked;
88
+ o.hide_right_spine = document.getElementById('hide_right_spine')?.checked;
89
+ o.axis_width = parseFloat(document.getElementById('axis_width')?.value) || 0.2;
90
+ o.axis_fontsize = parseInt(document.getElementById('axis_fontsize')?.value) || 7;
91
+ o.facecolor = document.getElementById('facecolor')?.value;
92
+ o.transparent = document.getElementById('transparent')?.value === 'true';
93
+
94
+ // Dimensions (always in inches for matplotlib)
95
+ o.fig_size = getFigSizeInches();
96
+ o.dpi = parseInt(document.getElementById('dpi')?.value) || 300;
97
+
98
+ // Annotations
99
+ o.annotations = overrides.annotations || [];
100
+
101
+ // Element-specific overrides (per-element styles)
102
+ if (overrides.element_overrides) {
103
+ o.element_overrides = overrides.element_overrides;
104
+ }
105
+
106
+ return o;
107
+ }
108
+
109
+ // ============================================================================
110
+ // Update Controls from Overrides
111
+ // ============================================================================
112
+ function updateControlsFromOverrides() {
113
+ // This function updates the form fields from the overrides object
114
+ // (Would be populated from the server data)
115
+ }
116
+
117
+ // ============================================================================
118
+ // Axis Tab Switching
119
+ // ============================================================================
120
+ function switchAxisTab(axis) {
121
+ // Update tab buttons
122
+ document.querySelectorAll('.axis-tab-btn').forEach(btn => {
123
+ btn.classList.toggle('active', btn.dataset.axis === axis);
124
+ });
125
+
126
+ // Update panels
127
+ document.querySelectorAll('.axis-panel').forEach(panel => {
128
+ panel.style.display = panel.dataset.axis === axis ? 'block' : 'none';
129
+ });
130
+ }
131
+
132
+ // ============================================================================
133
+ // Custom Legend Position Toggle
134
+ // ============================================================================
135
+ function toggleCustomLegendPosition() {
136
+ const customPos = document.getElementById('legend-custom-position');
137
+ const legendLoc = document.getElementById('legend_loc');
138
+ if (customPos && legendLoc) {
139
+ customPos.style.display = legendLoc.value === 'custom' ? 'block' : 'none';
140
+ }
141
+ }
142
+
143
+ // ============================================================================
144
+ // Background Type Toggle
145
+ // ============================================================================
146
+ function setBackgroundType(type) {
147
+ backgroundType = type;
148
+
149
+ // Update hidden inputs for collectOverrides
150
+ const transparentInput = document.getElementById('transparent');
151
+ const facecolorInput = document.getElementById('facecolor');
152
+
153
+ if (type === 'white') {
154
+ if (transparentInput) transparentInput.value = 'false';
155
+ if (facecolorInput) facecolorInput.value = 'white';
156
+ } else if (type === 'black') {
157
+ if (transparentInput) transparentInput.value = 'false';
158
+ if (facecolorInput) facecolorInput.value = 'black';
159
+ } else {
160
+ // transparent
161
+ if (transparentInput) transparentInput.value = 'true';
162
+ if (facecolorInput) facecolorInput.value = 'none';
163
+ }
164
+
165
+ // Update button states
166
+ document.querySelectorAll('.background-btn').forEach(btn => {
167
+ btn.classList.toggle('active', btn.dataset.type === type);
168
+ });
169
+
170
+ // Trigger update only after initialization
171
+ if (!initializingBackground) {
172
+ scheduleUpdate();
173
+ }
174
+ }
175
+
176
+ // ============================================================================
177
+ // Reset Overrides
178
+ // ============================================================================
179
+ function resetOverrides() {
180
+ if (confirm('Reset all settings to defaults?')) {
181
+ overrides = {};
182
+ updatePreview();
183
+ }
184
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Download Menu
3
+ * Handles file download functionality
4
+ */
5
+
6
+ // ============================================================================
7
+ // Toggle Download Menu
8
+ // ============================================================================
9
+ function toggleDownloadMenu() {
10
+ const menu = document.getElementById('download-menu');
11
+ if (menu.style.display === 'block') {
12
+ menu.style.display = 'none';
13
+ document.removeEventListener('click', closeDownloadMenuOnClickOutside);
14
+ } else {
15
+ menu.style.display = 'block';
16
+ // Close when clicking outside
17
+ setTimeout(() => {
18
+ document.addEventListener('click', closeDownloadMenuOnClickOutside);
19
+ }, 0);
20
+ }
21
+ }
22
+
23
+ function closeDownloadMenuOnClickOutside(e) {
24
+ const menu = document.getElementById('download-menu');
25
+ const btn = document.getElementById('download-btn');
26
+ if (menu && btn && !menu.contains(e.target) && !btn.contains(e.target)) {
27
+ menu.style.display = 'none';
28
+ document.removeEventListener('click', closeDownloadMenuOnClickOutside);
29
+ }
30
+ }
31
+
32
+ // ============================================================================
33
+ // Download Handlers
34
+ // ============================================================================
35
+ function downloadAs(format) {
36
+ saveToBundle().then(() => {
37
+ // First export to bundle
38
+ const data = collectOverrides();
39
+ data.format = format;
40
+
41
+ fetch('/export', {
42
+ method: 'POST',
43
+ headers: {'Content-Type': 'application/json'},
44
+ body: JSON.stringify(data)
45
+ })
46
+ .then(r => r.json())
47
+ .then(result => {
48
+ // Then trigger download
49
+ const filename = result.path.split('/').pop();
50
+ window.location.href = '/download/' + filename;
51
+ toggleDownloadMenu();
52
+ })
53
+ .catch(err => {
54
+ setStatus('Error exporting: ' + err, true);
55
+ });
56
+ });
57
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Help System
3
+ * Keyboard shortcut help dialog
4
+ */
5
+
6
+ // ============================================================================
7
+ // Show Shortcut Help
8
+ // ============================================================================
9
+ function showShortcutHelp() {
10
+ const helpHtml = `
11
+ <div id="shortcut-help-overlay" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); z-index: 10000; display: flex; justify-content: center; align-items: center;" onclick="this.remove();">
12
+ <div style="background: var(--bg-primary); padding: 30px; border-radius: 8px; max-width: 600px; max-height: 80vh; overflow-y: auto;" onclick="event.stopPropagation();">
13
+ <h2 style="margin-top: 0;">Keyboard Shortcuts</h2>
14
+
15
+ <h3>Basic Operations</h3>
16
+ <table style="width: 100%; margin-bottom: 20px;">
17
+ <tr><td><kbd>Ctrl</kbd>+<kbd>S</kbd></td><td>Save changes</td></tr>
18
+ <tr><td><kbd>Ctrl</kbd>+<kbd>Z</kbd></td><td>Undo</td></tr>
19
+ <tr><td><kbd>Ctrl</kbd>+<kbd>Y</kbd></td><td>Redo</td></tr>
20
+ <tr><td><kbd>Delete</kbd></td><td>Remove selected override</td></tr>
21
+ <tr><td><kbd>Esc</kbd></td><td>Deselect all</td></tr>
22
+ </table>
23
+
24
+ <h3>Panel Movement</h3>
25
+ <table style="width: 100%; margin-bottom: 20px;">
26
+ <tr><td><kbd>Arrow Keys</kbd></td><td>Move panel by 1mm</td></tr>
27
+ <tr><td><kbd>Shift</kbd>+<kbd>Arrows</kbd></td><td>Move panel by 5mm</td></tr>
28
+ </table>
29
+
30
+ <h3>View Controls</h3>
31
+ <table style="width: 100%; margin-bottom: 20px;">
32
+ <tr><td><kbd>+</kbd> / <kbd>=</kbd></td><td>Zoom in</td></tr>
33
+ <tr><td><kbd>-</kbd></td><td>Zoom out</td></tr>
34
+ <tr><td><kbd>0</kbd></td><td>Fit to window</td></tr>
35
+ <tr><td><kbd>G</kbd></td><td>Toggle grid</td></tr>
36
+ </table>
37
+
38
+ <h3>Alignment (Basic)</h3>
39
+ <table style="width: 100%; margin-bottom: 20px;">
40
+ <tr><td><kbd>Alt</kbd>+<kbd>A</kbd> then <kbd>L</kbd></td><td>Align left</td></tr>
41
+ <tr><td><kbd>Alt</kbd>+<kbd>A</kbd> then <kbd>R</kbd></td><td>Align right</td></tr>
42
+ <tr><td><kbd>Alt</kbd>+<kbd>A</kbd> then <kbd>T</kbd></td><td>Align top</td></tr>
43
+ <tr><td><kbd>Alt</kbd>+<kbd>A</kbd> then <kbd>B</kbd></td><td>Align bottom</td></tr>
44
+ <tr><td><kbd>Alt</kbd>+<kbd>A</kbd> then <kbd>C</kbd></td><td>Center horizontally</td></tr>
45
+ <tr><td><kbd>Alt</kbd>+<kbd>A</kbd> then <kbd>M</kbd></td><td>Center vertically</td></tr>
46
+ </table>
47
+
48
+ <h3>Alignment (By Axis)</h3>
49
+ <table style="width: 100%; margin-bottom: 20px;">
50
+ <tr><td><kbd>Alt</kbd>+<kbd>Shift</kbd>+<kbd>A</kbd> then <kbd>L</kbd></td><td>Align by Y-axis (left)</td></tr>
51
+ <tr><td><kbd>Alt</kbd>+<kbd>Shift</kbd>+<kbd>A</kbd> then <kbd>B</kbd></td><td>Align by X-axis (bottom)</td></tr>
52
+ <tr><td><kbd>Alt</kbd>+<kbd>Shift</kbd>+<kbd>A</kbd> then <kbd>S</kbd></td><td>Stack vertically</td></tr>
53
+ </table>
54
+
55
+ <h3>Selection</h3>
56
+ <table style="width: 100%; margin-bottom: 20px;">
57
+ <tr><td><kbd>Ctrl</kbd>+<kbd>A</kbd></td><td>Select all panels</td></tr>
58
+ <tr><td><kbd>Ctrl</kbd>+<kbd>Click</kbd></td><td>Toggle panel selection</td></tr>
59
+ </table>
60
+
61
+ <h3>Arrange</h3>
62
+ <table style="width: 100%; margin-bottom: 20px;">
63
+ <tr><td><kbd>Alt</kbd>+<kbd>F</kbd></td><td>Bring to front</td></tr>
64
+ <tr><td><kbd>Alt</kbd>+<kbd>B</kbd></td><td>Send to back</td></tr>
65
+ </table>
66
+
67
+ <h3>Developer Tools</h3>
68
+ <table style="width: 100%; margin-bottom: 20px;">
69
+ <tr><td><kbd>Alt</kbd>+<kbd>I</kbd></td><td>Toggle element inspector</td></tr>
70
+ <tr><td><kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>I</kbd></td><td>Rectangle selection mode</td></tr>
71
+ <tr><td><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>I</kbd></td><td>Copy console logs</td></tr>
72
+ </table>
73
+
74
+ <button onclick="this.closest('#shortcut-help-overlay').remove()" style="margin-top: 20px;">Close</button>
75
+ </div>
76
+ </div>
77
+ `;
78
+
79
+ // Remove existing help if any
80
+ const existing = document.getElementById('shortcut-help-overlay');
81
+ if (existing) existing.remove();
82
+
83
+ // Add to DOM
84
+ document.body.insertAdjacentHTML('beforeend', helpHtml);
85
+ }
86
+
87
+ // Add kbd styling
88
+ const kbdStyle = document.createElement('style');
89
+ kbdStyle.textContent = `
90
+ kbd {
91
+ background: var(--bg-tertiary, #333);
92
+ border: 1px solid var(--border-color, #555);
93
+ border-radius: 3px;
94
+ padding: 2px 6px;
95
+ font-family: monospace;
96
+ font-size: 0.85em;
97
+ margin-right: 8px;
98
+ }
99
+ `;
100
+ document.head.appendChild(kbdStyle);
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Theme Management
3
+ * Dark/light theme toggle
4
+ */
5
+
6
+ // ============================================================================
7
+ // Theme Toggle
8
+ // ============================================================================
9
+ function toggleTheme() {
10
+ document.body.classList.toggle('dark-theme');
11
+ const isDark = document.body.classList.contains('dark-theme');
12
+
13
+ // Save theme preference
14
+ localStorage.setItem('theme', isDark ? 'dark' : 'light');
15
+
16
+ // Re-render single panel preview with dark/light mode colors (if visible)
17
+ if (!showingPanelGrid) {
18
+ updatePreview();
19
+ }
20
+ }
21
+
22
+ // ============================================================================
23
+ // Initialize Theme
24
+ // ============================================================================
25
+ function initializeTheme() {
26
+ // Load saved theme
27
+ const savedTheme = localStorage.getItem('theme');
28
+ if (savedTheme === 'dark') {
29
+ document.body.classList.add('dark-theme');
30
+ }
31
+ }
32
+
33
+ // Initialize on load
34
+ document.addEventListener('DOMContentLoaded', initializeTheme);