scitex 2.4.3__py3-none-any.whl → 2.5.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.
- scitex/__version__.py +1 -1
- scitex/io/_load.py +5 -0
- scitex/io/_load_modules/_canvas.py +171 -0
- scitex/io/_save.py +8 -0
- scitex/io/_save_modules/_canvas.py +356 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot.py +77 -22
- scitex/plt/docs/FIGURE_ARCHITECTURE.md +257 -0
- scitex/plt/utils/__init__.py +10 -0
- scitex/plt/utils/_collect_figure_metadata.py +14 -12
- scitex/plt/utils/_csv_column_naming.py +237 -0
- scitex/session/_decorator.py +13 -1
- scitex/vis/README.md +246 -615
- scitex/vis/__init__.py +138 -78
- scitex/vis/canvas.py +423 -0
- scitex/vis/docs/CANVAS_ARCHITECTURE.md +307 -0
- scitex/vis/editor/__init__.py +1 -1
- scitex/vis/editor/_dearpygui_editor.py +1830 -0
- scitex/vis/editor/_defaults.py +40 -1
- scitex/vis/editor/_edit.py +54 -18
- scitex/vis/editor/_flask_editor.py +37 -0
- scitex/vis/editor/_qt_editor.py +865 -0
- scitex/vis/editor/flask_editor/__init__.py +21 -0
- scitex/vis/editor/flask_editor/bbox.py +216 -0
- scitex/vis/editor/flask_editor/core.py +152 -0
- scitex/vis/editor/flask_editor/plotter.py +130 -0
- scitex/vis/editor/flask_editor/renderer.py +184 -0
- scitex/vis/editor/flask_editor/templates/__init__.py +33 -0
- scitex/vis/editor/flask_editor/templates/html.py +295 -0
- scitex/vis/editor/flask_editor/templates/scripts.py +614 -0
- scitex/vis/editor/flask_editor/templates/styles.py +549 -0
- scitex/vis/editor/flask_editor/utils.py +81 -0
- scitex/vis/io/__init__.py +84 -21
- scitex/vis/io/canvas.py +226 -0
- scitex/vis/io/data.py +204 -0
- scitex/vis/io/directory.py +202 -0
- scitex/vis/io/export.py +460 -0
- scitex/vis/io/panel.py +424 -0
- {scitex-2.4.3.dist-info → scitex-2.5.0.dist-info}/METADATA +9 -2
- {scitex-2.4.3.dist-info → scitex-2.5.0.dist-info}/RECORD +42 -21
- scitex/vis/DJANGO_INTEGRATION.md +0 -677
- scitex/vis/editor/_web_editor.py +0 -1440
- scitex/vis/tmp.txt +0 -239
- {scitex-2.4.3.dist-info → scitex-2.5.0.dist-info}/WHEEL +0 -0
- {scitex-2.4.3.dist-info → scitex-2.5.0.dist-info}/entry_points.txt +0 -0
- {scitex-2.4.3.dist-info → scitex-2.5.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# File: ./src/scitex/vis/editor/flask_editor/renderer.py
|
|
4
|
+
"""Figure rendering for Flask editor."""
|
|
5
|
+
|
|
6
|
+
from typing import Dict, Any, Tuple
|
|
7
|
+
import base64
|
|
8
|
+
import io
|
|
9
|
+
|
|
10
|
+
import matplotlib
|
|
11
|
+
matplotlib.use('Agg')
|
|
12
|
+
import matplotlib.pyplot as plt
|
|
13
|
+
from matplotlib.ticker import MaxNLocator
|
|
14
|
+
from PIL import Image
|
|
15
|
+
|
|
16
|
+
from .plotter import plot_from_csv
|
|
17
|
+
from .bbox import extract_bboxes
|
|
18
|
+
|
|
19
|
+
# mm to pt conversion factor
|
|
20
|
+
MM_TO_PT = 2.83465
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def render_preview_with_bboxes(csv_data, overrides: Dict[str, Any], axis_fontsize: int = 7
|
|
24
|
+
) -> Tuple[str, Dict[str, Any], Dict[str, int]]:
|
|
25
|
+
"""Render figure and return base64 PNG along with element bounding boxes.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
csv_data: DataFrame containing CSV data
|
|
29
|
+
overrides: Dictionary with override settings
|
|
30
|
+
axis_fontsize: Default font size for axis labels
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
tuple: (base64_image_data, bboxes_dict, image_size)
|
|
34
|
+
"""
|
|
35
|
+
o = overrides
|
|
36
|
+
|
|
37
|
+
# Dimensions
|
|
38
|
+
dpi = o.get('dpi', 300)
|
|
39
|
+
fig_size = o.get('fig_size', [3.15, 2.68])
|
|
40
|
+
|
|
41
|
+
# Font sizes
|
|
42
|
+
axis_fontsize = o.get('axis_fontsize', 7)
|
|
43
|
+
tick_fontsize = o.get('tick_fontsize', 7)
|
|
44
|
+
title_fontsize = o.get('title_fontsize', 8)
|
|
45
|
+
|
|
46
|
+
# Line/axis thickness
|
|
47
|
+
linewidth_pt = o.get('linewidth', 0.57)
|
|
48
|
+
axis_width_pt = o.get('axis_width', 0.2) * MM_TO_PT
|
|
49
|
+
tick_length_pt = o.get('tick_length', 0.8) * MM_TO_PT
|
|
50
|
+
tick_width_pt = o.get('tick_width', 0.2) * MM_TO_PT
|
|
51
|
+
tick_direction = o.get('tick_direction', 'out')
|
|
52
|
+
x_n_ticks = o.get('x_n_ticks', o.get('n_ticks', 4))
|
|
53
|
+
y_n_ticks = o.get('y_n_ticks', o.get('n_ticks', 4))
|
|
54
|
+
hide_x_ticks = o.get('hide_x_ticks', False)
|
|
55
|
+
hide_y_ticks = o.get('hide_y_ticks', False)
|
|
56
|
+
|
|
57
|
+
transparent = o.get('transparent', True)
|
|
58
|
+
|
|
59
|
+
# Create figure
|
|
60
|
+
fig, ax = plt.subplots(figsize=fig_size, dpi=dpi)
|
|
61
|
+
_apply_background(fig, ax, o, transparent)
|
|
62
|
+
|
|
63
|
+
# Plot from CSV data
|
|
64
|
+
if csv_data is not None:
|
|
65
|
+
plot_from_csv(ax, csv_data, overrides, linewidth=linewidth_pt)
|
|
66
|
+
else:
|
|
67
|
+
ax.text(0.5, 0.5, "No plot data available\n(CSV not found)",
|
|
68
|
+
ha='center', va='center', transform=ax.transAxes,
|
|
69
|
+
fontsize=axis_fontsize)
|
|
70
|
+
|
|
71
|
+
# Apply labels
|
|
72
|
+
_apply_labels(ax, o, title_fontsize, axis_fontsize)
|
|
73
|
+
|
|
74
|
+
# Tick styling
|
|
75
|
+
_apply_tick_styling(ax, tick_fontsize, tick_length_pt, tick_width_pt, tick_direction,
|
|
76
|
+
x_n_ticks, y_n_ticks, hide_x_ticks, hide_y_ticks)
|
|
77
|
+
|
|
78
|
+
# Apply grid, limits, spines
|
|
79
|
+
_apply_style(ax, o, axis_width_pt)
|
|
80
|
+
|
|
81
|
+
# Apply annotations
|
|
82
|
+
_apply_annotations(ax, o, axis_fontsize)
|
|
83
|
+
|
|
84
|
+
fig.tight_layout()
|
|
85
|
+
|
|
86
|
+
# Get element bounding boxes BEFORE saving (need renderer)
|
|
87
|
+
fig.canvas.draw()
|
|
88
|
+
renderer = fig.canvas.get_renderer()
|
|
89
|
+
|
|
90
|
+
# Save to buffer first to get actual image size
|
|
91
|
+
buf = io.BytesIO()
|
|
92
|
+
fig.savefig(buf, format='png', dpi=dpi, bbox_inches='tight', transparent=transparent)
|
|
93
|
+
buf.seek(0)
|
|
94
|
+
|
|
95
|
+
# Get actual saved image dimensions
|
|
96
|
+
img = Image.open(buf)
|
|
97
|
+
img_width, img_height = img.size
|
|
98
|
+
buf.seek(0)
|
|
99
|
+
|
|
100
|
+
# Get bboxes
|
|
101
|
+
bboxes = extract_bboxes(fig, ax, renderer, img_width, img_height)
|
|
102
|
+
|
|
103
|
+
img_data = base64.b64encode(buf.read()).decode('utf-8')
|
|
104
|
+
plt.close(fig)
|
|
105
|
+
|
|
106
|
+
return img_data, bboxes, {'width': img_width, 'height': img_height}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _apply_background(fig, ax, o, transparent):
|
|
110
|
+
"""Apply background settings to figure."""
|
|
111
|
+
if transparent:
|
|
112
|
+
fig.patch.set_facecolor('none')
|
|
113
|
+
ax.patch.set_facecolor('none')
|
|
114
|
+
elif o.get('facecolor'):
|
|
115
|
+
fig.patch.set_facecolor(o['facecolor'])
|
|
116
|
+
ax.patch.set_facecolor(o['facecolor'])
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _apply_labels(ax, o, title_fontsize, axis_fontsize):
|
|
120
|
+
"""Apply title and axis labels."""
|
|
121
|
+
if o.get('title'):
|
|
122
|
+
ax.set_title(o['title'], fontsize=title_fontsize)
|
|
123
|
+
if o.get('xlabel'):
|
|
124
|
+
ax.set_xlabel(o['xlabel'], fontsize=axis_fontsize)
|
|
125
|
+
if o.get('ylabel'):
|
|
126
|
+
ax.set_ylabel(o['ylabel'], fontsize=axis_fontsize)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _apply_tick_styling(ax, tick_fontsize, tick_length_pt, tick_width_pt, tick_direction,
|
|
130
|
+
x_n_ticks, y_n_ticks, hide_x_ticks, hide_y_ticks):
|
|
131
|
+
"""Apply tick styling to axes."""
|
|
132
|
+
ax.tick_params(
|
|
133
|
+
axis='both',
|
|
134
|
+
labelsize=tick_fontsize,
|
|
135
|
+
length=tick_length_pt,
|
|
136
|
+
width=tick_width_pt,
|
|
137
|
+
direction=tick_direction,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
if hide_x_ticks:
|
|
141
|
+
ax.xaxis.set_ticks([])
|
|
142
|
+
ax.xaxis.set_ticklabels([])
|
|
143
|
+
else:
|
|
144
|
+
ax.xaxis.set_major_locator(MaxNLocator(nbins=x_n_ticks))
|
|
145
|
+
if hide_y_ticks:
|
|
146
|
+
ax.yaxis.set_ticks([])
|
|
147
|
+
ax.yaxis.set_ticklabels([])
|
|
148
|
+
else:
|
|
149
|
+
ax.yaxis.set_major_locator(MaxNLocator(nbins=y_n_ticks))
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _apply_style(ax, o, axis_width_pt):
|
|
153
|
+
"""Apply grid, axis limits, and spine settings."""
|
|
154
|
+
if o.get('grid'):
|
|
155
|
+
ax.grid(True, linewidth=axis_width_pt, alpha=0.3)
|
|
156
|
+
|
|
157
|
+
if o.get('xlim'):
|
|
158
|
+
ax.set_xlim(o['xlim'])
|
|
159
|
+
if o.get('ylim'):
|
|
160
|
+
ax.set_ylim(o['ylim'])
|
|
161
|
+
|
|
162
|
+
if o.get('hide_top_spine', True):
|
|
163
|
+
ax.spines['top'].set_visible(False)
|
|
164
|
+
if o.get('hide_right_spine', True):
|
|
165
|
+
ax.spines['right'].set_visible(False)
|
|
166
|
+
|
|
167
|
+
for spine in ax.spines.values():
|
|
168
|
+
spine.set_linewidth(axis_width_pt)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _apply_annotations(ax, o, axis_fontsize):
|
|
172
|
+
"""Apply text annotations to figure."""
|
|
173
|
+
for annot in o.get('annotations', []):
|
|
174
|
+
if annot.get('type') == 'text':
|
|
175
|
+
ax.text(
|
|
176
|
+
annot.get('x', 0.5),
|
|
177
|
+
annot.get('y', 0.5),
|
|
178
|
+
annot.get('text', ''),
|
|
179
|
+
transform=ax.transAxes,
|
|
180
|
+
fontsize=annot.get('fontsize', axis_fontsize),
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# EOF
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# File: ./src/scitex/vis/editor/flask_editor/templates/__init__.py
|
|
4
|
+
"""Template components for Flask editor."""
|
|
5
|
+
|
|
6
|
+
from .styles import CSS_STYLES
|
|
7
|
+
from .scripts import JS_SCRIPTS
|
|
8
|
+
from .html import HTML_BODY
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def build_html_template() -> str:
|
|
12
|
+
"""Build the complete HTML template from components."""
|
|
13
|
+
return f'''<!DOCTYPE html>
|
|
14
|
+
<html lang="en" data-theme="dark">
|
|
15
|
+
<head>
|
|
16
|
+
<meta charset="UTF-8">
|
|
17
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
18
|
+
<title>SciTeX Editor - {{{{ filename }}}}</title>
|
|
19
|
+
<style>
|
|
20
|
+
{CSS_STYLES}
|
|
21
|
+
</style>
|
|
22
|
+
</head>
|
|
23
|
+
<body>
|
|
24
|
+
{HTML_BODY}
|
|
25
|
+
<script>
|
|
26
|
+
{JS_SCRIPTS}
|
|
27
|
+
</script>
|
|
28
|
+
</body>
|
|
29
|
+
</html>
|
|
30
|
+
'''
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# EOF
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# File: ./src/scitex/vis/editor/flask_editor/templates/html.py
|
|
4
|
+
"""HTML structure for the Flask editor UI."""
|
|
5
|
+
|
|
6
|
+
HTML_BODY = '''
|
|
7
|
+
<div class="container">
|
|
8
|
+
<div class="preview">
|
|
9
|
+
<div class="preview-wrapper">
|
|
10
|
+
<div class="preview-container" id="preview-container">
|
|
11
|
+
<img id="preview-img" src="" alt="Figure Preview">
|
|
12
|
+
<svg id="hover-overlay" class="hover-overlay"></svg>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="controls">
|
|
17
|
+
<div class="controls-header">
|
|
18
|
+
<div>
|
|
19
|
+
<h1>SciTeX Editor</h1>
|
|
20
|
+
<div class="filename">{{ filename }}</div>
|
|
21
|
+
</div>
|
|
22
|
+
<button class="theme-toggle" onclick="toggleTheme()" title="Toggle light/dark mode">
|
|
23
|
+
<span id="theme-icon">☾</span>
|
|
24
|
+
</button>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div class="controls-body">
|
|
28
|
+
<!-- Labels Section -->
|
|
29
|
+
<div class="section" id="section-labels">
|
|
30
|
+
<div class="section-header section-toggle collapsed" onclick="toggleSection(this)">Labels</div>
|
|
31
|
+
<div class="section-content collapsed">
|
|
32
|
+
<div class="field">
|
|
33
|
+
<label>Title</label>
|
|
34
|
+
<input type="text" id="title" placeholder="Figure title">
|
|
35
|
+
</div>
|
|
36
|
+
<div class="field-row">
|
|
37
|
+
<div class="field">
|
|
38
|
+
<label>X Label (Left)</label>
|
|
39
|
+
<input type="text" id="xlabel" placeholder="X axis label">
|
|
40
|
+
</div>
|
|
41
|
+
<div class="field">
|
|
42
|
+
<label>Y Label (Right)</label>
|
|
43
|
+
<input type="text" id="ylabel" placeholder="Y axis label">
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<!-- Axis Limits Section -->
|
|
50
|
+
<div class="section" id="section-axis-limits">
|
|
51
|
+
<div class="section-header section-toggle collapsed" onclick="toggleSection(this)">Axis Limits</div>
|
|
52
|
+
<div class="section-content collapsed">
|
|
53
|
+
<div class="field-row">
|
|
54
|
+
<div class="field">
|
|
55
|
+
<label>X (Left)</label>
|
|
56
|
+
<div class="field-row" style="gap: 4px; margin-top: 4px;">
|
|
57
|
+
<input type="number" id="xmin" step="any" placeholder="Min">
|
|
58
|
+
<input type="number" id="xmax" step="any" placeholder="Max">
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="field">
|
|
62
|
+
<label>Y (Right)</label>
|
|
63
|
+
<div class="field-row" style="gap: 4px; margin-top: 4px;">
|
|
64
|
+
<input type="number" id="ymin" step="any" placeholder="Min">
|
|
65
|
+
<input type="number" id="ymax" step="any" placeholder="Max">
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<!-- Traces Section -->
|
|
73
|
+
<div class="section" id="section-traces">
|
|
74
|
+
<div class="section-header section-toggle collapsed" onclick="toggleSection(this)">Traces</div>
|
|
75
|
+
<div class="section-content collapsed">
|
|
76
|
+
<div class="traces-list" id="traces-list">
|
|
77
|
+
<!-- Dynamically populated -->
|
|
78
|
+
</div>
|
|
79
|
+
<div class="field" style="margin-top: 10px;">
|
|
80
|
+
<label>Default Line Width (pt)</label>
|
|
81
|
+
<input type="number" id="linewidth" value="1.0" min="0.1" max="5" step="0.1">
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<!-- Legend Section -->
|
|
87
|
+
<div class="section" id="section-legend">
|
|
88
|
+
<div class="section-header section-toggle collapsed" onclick="toggleSection(this)">Legend</div>
|
|
89
|
+
<div class="section-content collapsed">
|
|
90
|
+
<label class="checkbox-field">
|
|
91
|
+
<input type="checkbox" id="legend_visible" checked>
|
|
92
|
+
<span>Show Legend</span>
|
|
93
|
+
</label>
|
|
94
|
+
<div class="field">
|
|
95
|
+
<label>Position</label>
|
|
96
|
+
<select id="legend_loc" onchange="toggleCustomLegendPosition()">
|
|
97
|
+
<option value="best">Best</option>
|
|
98
|
+
<option value="upper right">Upper Right</option>
|
|
99
|
+
<option value="upper left">Upper Left</option>
|
|
100
|
+
<option value="lower right">Lower Right</option>
|
|
101
|
+
<option value="lower left">Lower Left</option>
|
|
102
|
+
<option value="center right">Center Right</option>
|
|
103
|
+
<option value="center left">Center Left</option>
|
|
104
|
+
<option value="upper center">Upper Center</option>
|
|
105
|
+
<option value="lower center">Lower Center</option>
|
|
106
|
+
<option value="center">Center</option>
|
|
107
|
+
<option value="custom">Custom (Drag)</option>
|
|
108
|
+
</select>
|
|
109
|
+
</div>
|
|
110
|
+
<div id="custom-legend-coords" class="field-row" style="display: none;">
|
|
111
|
+
<div class="field">
|
|
112
|
+
<label>X (0-1)</label>
|
|
113
|
+
<input type="number" id="legend_x" value="0.5" min="0" max="1" step="0.01">
|
|
114
|
+
</div>
|
|
115
|
+
<div class="field">
|
|
116
|
+
<label>Y (0-1)</label>
|
|
117
|
+
<input type="number" id="legend_y" value="0.5" min="0" max="1" step="0.01">
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
<label class="checkbox-field">
|
|
121
|
+
<input type="checkbox" id="legend_frameon">
|
|
122
|
+
<span>Show Frame</span>
|
|
123
|
+
</label>
|
|
124
|
+
<div class="field">
|
|
125
|
+
<label>Font Size (pt)</label>
|
|
126
|
+
<input type="number" id="legend_fontsize" value="6" min="4" max="16" step="1">
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<!-- Ticks Section -->
|
|
132
|
+
<div class="section" id="section-ticks">
|
|
133
|
+
<div class="section-header section-toggle collapsed" onclick="toggleSection(this)">Ticks</div>
|
|
134
|
+
<div class="section-content collapsed">
|
|
135
|
+
<div class="field-row">
|
|
136
|
+
<div class="field">
|
|
137
|
+
<label>X (Left)</label>
|
|
138
|
+
<label class="checkbox-field" style="margin-top: 4px;">
|
|
139
|
+
<input type="checkbox" id="hide_x_ticks">
|
|
140
|
+
<span>Hide</span>
|
|
141
|
+
</label>
|
|
142
|
+
<input type="number" id="x_n_ticks" value="4" min="2" max="10" step="1" placeholder="N Ticks" title="Number of ticks">
|
|
143
|
+
</div>
|
|
144
|
+
<div class="field">
|
|
145
|
+
<label>Y (Right)</label>
|
|
146
|
+
<label class="checkbox-field" style="margin-top: 4px;">
|
|
147
|
+
<input type="checkbox" id="hide_y_ticks">
|
|
148
|
+
<span>Hide</span>
|
|
149
|
+
</label>
|
|
150
|
+
<input type="number" id="y_n_ticks" value="4" min="2" max="10" step="1" placeholder="N Ticks" title="Number of ticks">
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
<div class="field-row">
|
|
154
|
+
<div class="field">
|
|
155
|
+
<label>Font Size (pt)</label>
|
|
156
|
+
<input type="number" id="tick_fontsize" value="7" min="4" max="16" step="1">
|
|
157
|
+
</div>
|
|
158
|
+
<div class="field">
|
|
159
|
+
<label>Direction</label>
|
|
160
|
+
<select id="tick_direction">
|
|
161
|
+
<option value="out">Out</option>
|
|
162
|
+
<option value="in">In</option>
|
|
163
|
+
<option value="inout">Both</option>
|
|
164
|
+
</select>
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
<div class="field-row">
|
|
168
|
+
<div class="field">
|
|
169
|
+
<label>Tick Length (mm)</label>
|
|
170
|
+
<input type="number" id="tick_length" value="0.8" min="0.1" max="3" step="0.1">
|
|
171
|
+
</div>
|
|
172
|
+
<div class="field">
|
|
173
|
+
<label>Tick Width (mm)</label>
|
|
174
|
+
<input type="number" id="tick_width" value="0.2" min="0.05" max="1" step="0.05">
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<!-- Style Section -->
|
|
181
|
+
<div class="section" id="section-style">
|
|
182
|
+
<div class="section-header section-toggle collapsed" onclick="toggleSection(this)">Style</div>
|
|
183
|
+
<div class="section-content collapsed">
|
|
184
|
+
<label class="checkbox-field">
|
|
185
|
+
<input type="checkbox" id="grid">
|
|
186
|
+
<span>Show Grid</span>
|
|
187
|
+
</label>
|
|
188
|
+
<label class="checkbox-field">
|
|
189
|
+
<input type="checkbox" id="hide_top_spine" checked>
|
|
190
|
+
<span>Hide Top Spine</span>
|
|
191
|
+
</label>
|
|
192
|
+
<label class="checkbox-field">
|
|
193
|
+
<input type="checkbox" id="hide_right_spine" checked>
|
|
194
|
+
<span>Hide Right Spine</span>
|
|
195
|
+
</label>
|
|
196
|
+
<div class="field-row">
|
|
197
|
+
<div class="field">
|
|
198
|
+
<label>Axis Width (mm)</label>
|
|
199
|
+
<input type="number" id="axis_width" value="0.2" min="0.05" max="1" step="0.05">
|
|
200
|
+
</div>
|
|
201
|
+
<div class="field">
|
|
202
|
+
<label>Label Size (pt)</label>
|
|
203
|
+
<input type="number" id="axis_fontsize" value="7" min="4" max="16" step="1">
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
<div class="field">
|
|
207
|
+
<label>Background Color</label>
|
|
208
|
+
<div class="color-field">
|
|
209
|
+
<input type="color" id="facecolor" value="#ffffff">
|
|
210
|
+
<input type="text" id="facecolor_text" value="#ffffff" placeholder="#ffffff">
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
<label class="checkbox-field">
|
|
214
|
+
<input type="checkbox" id="transparent" checked>
|
|
215
|
+
<span>Transparent Background</span>
|
|
216
|
+
</label>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
<!-- Dimensions Section -->
|
|
221
|
+
<div class="section" id="section-dimensions">
|
|
222
|
+
<div class="section-header section-toggle collapsed" onclick="toggleSection(this)">Dimensions</div>
|
|
223
|
+
<div class="section-content collapsed">
|
|
224
|
+
<div class="field-row">
|
|
225
|
+
<div class="field">
|
|
226
|
+
<label>Width (inch)</label>
|
|
227
|
+
<input type="number" id="fig_width" value="3.15" min="1" max="12" step="0.1">
|
|
228
|
+
</div>
|
|
229
|
+
<div class="field">
|
|
230
|
+
<label>Height (inch)</label>
|
|
231
|
+
<input type="number" id="fig_height" value="2.68" min="1" max="12" step="0.1">
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
<div class="field">
|
|
235
|
+
<label>DPI</label>
|
|
236
|
+
<input type="number" id="dpi" value="300" min="72" max="600" step="1">
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
|
|
241
|
+
<!-- Annotations Section -->
|
|
242
|
+
<div class="section" id="section-annotations">
|
|
243
|
+
<div class="section-header section-toggle collapsed" onclick="toggleSection(this)">Annotations</div>
|
|
244
|
+
<div class="section-content collapsed">
|
|
245
|
+
<div class="field">
|
|
246
|
+
<label>Text</label>
|
|
247
|
+
<input type="text" id="annot-text" placeholder="Annotation text">
|
|
248
|
+
</div>
|
|
249
|
+
<div class="field-row">
|
|
250
|
+
<div class="field">
|
|
251
|
+
<label>X (0-1)</label>
|
|
252
|
+
<input type="number" id="annot-x" value="0.5" min="0" max="1" step="0.05">
|
|
253
|
+
</div>
|
|
254
|
+
<div class="field">
|
|
255
|
+
<label>Y (0-1)</label>
|
|
256
|
+
<input type="number" id="annot-y" value="0.5" min="0" max="1" step="0.05">
|
|
257
|
+
</div>
|
|
258
|
+
<div class="field">
|
|
259
|
+
<label>Size</label>
|
|
260
|
+
<input type="number" id="annot-size" value="8" min="4" max="24" step="1">
|
|
261
|
+
</div>
|
|
262
|
+
</div>
|
|
263
|
+
<button class="btn btn-secondary" onclick="addAnnotation()">Add Annotation</button>
|
|
264
|
+
<div class="annotations-list" id="annotations-list"></div>
|
|
265
|
+
</div>
|
|
266
|
+
</div>
|
|
267
|
+
|
|
268
|
+
<!-- Actions Section -->
|
|
269
|
+
<div class="section">
|
|
270
|
+
<div class="section-header">Actions</div>
|
|
271
|
+
<div class="field-row" style="align-items: center; margin-bottom: 8px;">
|
|
272
|
+
<div class="field" style="flex: 1;">
|
|
273
|
+
<label>Auto-Update</label>
|
|
274
|
+
<select id="auto_update_interval" onchange="setAutoUpdateInterval()">
|
|
275
|
+
<option value="0">Off</option>
|
|
276
|
+
<option value="500">Hot (0.5s)</option>
|
|
277
|
+
<option value="1000">Fast (1s)</option>
|
|
278
|
+
<option value="2000" selected>Normal (2s)</option>
|
|
279
|
+
<option value="5000">Slow (5s)</option>
|
|
280
|
+
</select>
|
|
281
|
+
</div>
|
|
282
|
+
<button class="btn btn-cta" onclick="updatePreview()" style="flex: 0; margin-left: 8px;">Update Now</button>
|
|
283
|
+
</div>
|
|
284
|
+
<button class="btn btn-primary" onclick="saveManual()" title="Ctrl+S">Save to .manual.json</button>
|
|
285
|
+
<button class="btn btn-secondary" onclick="resetOverrides()">Reset to Original</button>
|
|
286
|
+
</div>
|
|
287
|
+
|
|
288
|
+
<div class="status-bar" id="status">Ready</div>
|
|
289
|
+
</div>
|
|
290
|
+
</div>
|
|
291
|
+
</div>
|
|
292
|
+
'''
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
# EOF
|