figrecipe 0.7.4__py3-none-any.whl → 0.9.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.
Files changed (143) hide show
  1. figrecipe/__init__.py +74 -76
  2. figrecipe/__main__.py +12 -0
  3. figrecipe/_api/_panel.py +67 -0
  4. figrecipe/_api/_save.py +100 -4
  5. figrecipe/_cli/__init__.py +7 -0
  6. figrecipe/_cli/_compose.py +87 -0
  7. figrecipe/_cli/_convert.py +117 -0
  8. figrecipe/_cli/_crop.py +82 -0
  9. figrecipe/_cli/_edit.py +70 -0
  10. figrecipe/_cli/_extract.py +128 -0
  11. figrecipe/_cli/_fonts.py +47 -0
  12. figrecipe/_cli/_info.py +67 -0
  13. figrecipe/_cli/_main.py +58 -0
  14. figrecipe/_cli/_reproduce.py +79 -0
  15. figrecipe/_cli/_style.py +77 -0
  16. figrecipe/_cli/_validate.py +66 -0
  17. figrecipe/_cli/_version.py +50 -0
  18. figrecipe/_composition/__init__.py +32 -0
  19. figrecipe/_composition/_alignment.py +452 -0
  20. figrecipe/_composition/_compose.py +179 -0
  21. figrecipe/_composition/_import_axes.py +127 -0
  22. figrecipe/_composition/_visibility.py +125 -0
  23. figrecipe/_dev/__init__.py +2 -0
  24. figrecipe/_dev/browser/__init__.py +69 -0
  25. figrecipe/_dev/browser/_audio.py +240 -0
  26. figrecipe/_dev/browser/_caption.py +356 -0
  27. figrecipe/_dev/browser/_click_effect.py +146 -0
  28. figrecipe/_dev/browser/_cursor.py +196 -0
  29. figrecipe/_dev/browser/_highlight.py +105 -0
  30. figrecipe/_dev/browser/_narration.py +237 -0
  31. figrecipe/_dev/browser/_recorder.py +446 -0
  32. figrecipe/_dev/browser/_utils.py +178 -0
  33. figrecipe/_dev/browser/_video_trim/__init__.py +152 -0
  34. figrecipe/_dev/browser/_video_trim/_detection.py +223 -0
  35. figrecipe/_dev/browser/_video_trim/_markers.py +140 -0
  36. figrecipe/_editor/__init__.py +36 -36
  37. figrecipe/_editor/_bbox/_extract.py +155 -9
  38. figrecipe/_editor/_bbox/_extract_text.py +124 -0
  39. figrecipe/_editor/_call_overrides.py +183 -0
  40. figrecipe/_editor/_datatable_plot_handlers.py +249 -0
  41. figrecipe/_editor/_figure_layout.py +211 -0
  42. figrecipe/_editor/_flask_app.py +157 -16
  43. figrecipe/_editor/_helpers.py +17 -8
  44. figrecipe/_editor/_hitmap/_detect.py +89 -32
  45. figrecipe/_editor/_hitmap_main.py +4 -4
  46. figrecipe/_editor/_overrides.py +4 -1
  47. figrecipe/_editor/_plot_types_registry.py +190 -0
  48. figrecipe/_editor/_render_overrides.py +38 -11
  49. figrecipe/_editor/_renderer.py +46 -1
  50. figrecipe/_editor/_routes_annotation.py +114 -0
  51. figrecipe/_editor/_routes_axis.py +35 -6
  52. figrecipe/_editor/_routes_captions.py +130 -0
  53. figrecipe/_editor/_routes_composition.py +270 -0
  54. figrecipe/_editor/_routes_core.py +15 -173
  55. figrecipe/_editor/_routes_datatable.py +364 -0
  56. figrecipe/_editor/_routes_element.py +37 -19
  57. figrecipe/_editor/_routes_files.py +443 -0
  58. figrecipe/_editor/_routes_image.py +200 -0
  59. figrecipe/_editor/_routes_snapshot.py +94 -0
  60. figrecipe/_editor/_routes_style.py +28 -8
  61. figrecipe/_editor/_templates/__init__.py +40 -2
  62. figrecipe/_editor/_templates/_html.py +97 -103
  63. figrecipe/_editor/_templates/_html_components/__init__.py +13 -0
  64. figrecipe/_editor/_templates/_html_components/_composition_toolbar.py +79 -0
  65. figrecipe/_editor/_templates/_html_components/_file_browser.py +41 -0
  66. figrecipe/_editor/_templates/_html_datatable.py +92 -0
  67. figrecipe/_editor/_templates/_scripts/__init__.py +58 -0
  68. figrecipe/_editor/_templates/_scripts/_accordion.py +328 -0
  69. figrecipe/_editor/_templates/_scripts/_annotation_drag.py +504 -0
  70. figrecipe/_editor/_templates/_scripts/_api.py +1 -1
  71. figrecipe/_editor/_templates/_scripts/_canvas_context_menu.py +182 -0
  72. figrecipe/_editor/_templates/_scripts/_captions.py +231 -0
  73. figrecipe/_editor/_templates/_scripts/_composition.py +283 -0
  74. figrecipe/_editor/_templates/_scripts/_core.py +94 -37
  75. figrecipe/_editor/_templates/_scripts/_datatable/__init__.py +59 -0
  76. figrecipe/_editor/_templates/_scripts/_datatable/_cell_edit.py +97 -0
  77. figrecipe/_editor/_templates/_scripts/_datatable/_clipboard.py +164 -0
  78. figrecipe/_editor/_templates/_scripts/_datatable/_context_menu.py +221 -0
  79. figrecipe/_editor/_templates/_scripts/_datatable/_core.py +150 -0
  80. figrecipe/_editor/_templates/_scripts/_datatable/_editable.py +511 -0
  81. figrecipe/_editor/_templates/_scripts/_datatable/_import.py +161 -0
  82. figrecipe/_editor/_templates/_scripts/_datatable/_plot.py +261 -0
  83. figrecipe/_editor/_templates/_scripts/_datatable/_selection.py +438 -0
  84. figrecipe/_editor/_templates/_scripts/_datatable/_table.py +256 -0
  85. figrecipe/_editor/_templates/_scripts/_datatable/_tabs.py +354 -0
  86. figrecipe/_editor/_templates/_scripts/_element_editor.py +17 -2
  87. figrecipe/_editor/_templates/_scripts/_files.py +274 -40
  88. figrecipe/_editor/_templates/_scripts/_files_context_menu.py +240 -0
  89. figrecipe/_editor/_templates/_scripts/_hitmap.py +87 -84
  90. figrecipe/_editor/_templates/_scripts/_image_drop.py +428 -0
  91. figrecipe/_editor/_templates/_scripts/_legend_drag.py +5 -0
  92. figrecipe/_editor/_templates/_scripts/_multi_select.py +198 -0
  93. figrecipe/_editor/_templates/_scripts/_panel_drag.py +219 -48
  94. figrecipe/_editor/_templates/_scripts/_panel_drag_snapshot.py +33 -0
  95. figrecipe/_editor/_templates/_scripts/_panel_position.py +238 -54
  96. figrecipe/_editor/_templates/_scripts/_panel_resize.py +230 -0
  97. figrecipe/_editor/_templates/_scripts/_panel_snap.py +307 -0
  98. figrecipe/_editor/_templates/_scripts/_region_select.py +255 -0
  99. figrecipe/_editor/_templates/_scripts/_selection.py +8 -1
  100. figrecipe/_editor/_templates/_scripts/_sync.py +242 -0
  101. figrecipe/_editor/_templates/_scripts/_undo_redo.py +348 -0
  102. figrecipe/_editor/_templates/_scripts/_zoom.py +52 -19
  103. figrecipe/_editor/_templates/_styles/__init__.py +9 -0
  104. figrecipe/_editor/_templates/_styles/_base.py +47 -0
  105. figrecipe/_editor/_templates/_styles/_buttons.py +127 -6
  106. figrecipe/_editor/_templates/_styles/_composition.py +87 -0
  107. figrecipe/_editor/_templates/_styles/_controls.py +168 -3
  108. figrecipe/_editor/_templates/_styles/_datatable/__init__.py +40 -0
  109. figrecipe/_editor/_templates/_styles/_datatable/_editable.py +203 -0
  110. figrecipe/_editor/_templates/_styles/_datatable/_panel.py +268 -0
  111. figrecipe/_editor/_templates/_styles/_datatable/_table.py +479 -0
  112. figrecipe/_editor/_templates/_styles/_datatable/_toolbar.py +384 -0
  113. figrecipe/_editor/_templates/_styles/_datatable/_vars.py +123 -0
  114. figrecipe/_editor/_templates/_styles/_dynamic_props.py +5 -5
  115. figrecipe/_editor/_templates/_styles/_file_browser.py +466 -0
  116. figrecipe/_editor/_templates/_styles/_forms.py +98 -0
  117. figrecipe/_editor/_templates/_styles/_hitmap.py +7 -0
  118. figrecipe/_editor/_templates/_styles/_modals.py +29 -0
  119. figrecipe/_editor/_templates/_styles/_overlays.py +5 -5
  120. figrecipe/_editor/_templates/_styles/_preview.py +213 -8
  121. figrecipe/_editor/_templates/_styles/_spinner.py +117 -0
  122. figrecipe/_editor/static/audio/click.mp3 +0 -0
  123. figrecipe/_editor/static/click.mp3 +0 -0
  124. figrecipe/_editor/static/icons/favicon.ico +0 -0
  125. figrecipe/_integrations/__init__.py +17 -0
  126. figrecipe/_integrations/_scitex_stats.py +298 -0
  127. figrecipe/_params/_DECORATION_METHODS.py +2 -0
  128. figrecipe/_recorder.py +28 -3
  129. figrecipe/_reproducer/_core.py +60 -49
  130. figrecipe/_utils/__init__.py +3 -0
  131. figrecipe/_utils/_bundle.py +205 -0
  132. figrecipe/_wrappers/_axes.py +150 -2
  133. figrecipe/_wrappers/_caption_generator.py +218 -0
  134. figrecipe/_wrappers/_figure.py +26 -1
  135. figrecipe/_wrappers/_stat_annotation.py +274 -0
  136. figrecipe/styles/_style_applier.py +10 -2
  137. figrecipe/styles/presets/SCITEX.yaml +11 -4
  138. {figrecipe-0.7.4.dist-info → figrecipe-0.9.0.dist-info}/METADATA +144 -146
  139. figrecipe-0.9.0.dist-info/RECORD +277 -0
  140. figrecipe-0.9.0.dist-info/entry_points.txt +2 -0
  141. figrecipe-0.7.4.dist-info/RECORD +0 -188
  142. {figrecipe-0.7.4.dist-info → figrecipe-0.9.0.dist-info}/WHEEL +0 -0
  143. {figrecipe-0.7.4.dist-info → figrecipe-0.9.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: figrecipe
3
- Version: 0.7.4
3
+ Version: 0.9.0
4
4
  Summary: Reproducible matplotlib wrapper with mm-precision layouts
5
5
  Project-URL: Homepage, https://github.com/ywatanabe1989/figrecipe
6
6
  Project-URL: Documentation, https://github.com/ywatanabe1989/figrecipe#readme
@@ -21,6 +21,7 @@ Classifier: Programming Language :: Python :: 3.11
21
21
  Classifier: Programming Language :: Python :: 3.12
22
22
  Classifier: Topic :: Scientific/Engineering :: Visualization
23
23
  Requires-Python: >=3.9
24
+ Requires-Dist: click>=8.0.0
24
25
  Requires-Dist: matplotlib>=3.5.0
25
26
  Requires-Dist: numpy>=1.20.0
26
27
  Requires-Dist: ruamel-yaml>=0.17.0
@@ -29,12 +30,26 @@ Provides-Extra: all
29
30
  Requires-Dist: flask>=2.0.0; extra == 'all'
30
31
  Requires-Dist: pandas>=1.3.0; extra == 'all'
31
32
  Requires-Dist: pillow>=9.0.0; extra == 'all'
33
+ Requires-Dist: playwright>=1.40.0; extra == 'all'
34
+ Requires-Dist: pytesseract>=0.3.0; extra == 'all'
32
35
  Requires-Dist: pytest-cov>=4.0.0; extra == 'all'
33
36
  Requires-Dist: pytest>=7.0.0; extra == 'all'
37
+ Requires-Dist: pywebview>=4.0.0; extra == 'all'
34
38
  Requires-Dist: seaborn>=0.12.0; extra == 'all'
39
+ Provides-Extra: demo
40
+ Requires-Dist: playwright>=1.40.0; extra == 'demo'
41
+ Requires-Dist: pytesseract>=0.3.0; extra == 'demo'
42
+ Provides-Extra: desktop
43
+ Requires-Dist: flask>=2.0.0; extra == 'desktop'
44
+ Requires-Dist: pillow>=9.0.0; extra == 'desktop'
45
+ Requires-Dist: pyqt6-webengine>=6.0.0; extra == 'desktop'
46
+ Requires-Dist: pyqt6>=6.0.0; extra == 'desktop'
47
+ Requires-Dist: pywebview>=4.0.0; extra == 'desktop'
48
+ Requires-Dist: qtpy>=2.0.0; extra == 'desktop'
35
49
  Provides-Extra: dev
36
50
  Requires-Dist: pre-commit>=3.5.0; extra == 'dev'
37
51
  Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
52
+ Requires-Dist: pytest-xdist>=3.0.0; extra == 'dev'
38
53
  Requires-Dist: pytest>=7.0.0; extra == 'dev'
39
54
  Provides-Extra: editor
40
55
  Requires-Dist: flask>=2.0.0; extra == 'editor'
@@ -47,7 +62,7 @@ Requires-Dist: seaborn>=0.12.0; extra == 'seaborn'
47
62
  Description-Content-Type: text/markdown
48
63
 
49
64
  <!-- ---
50
- !-- Timestamp: 2025-12-22 14:49:10
65
+ !-- Timestamp: 2025-12-27 02:15:07
51
66
  !-- Author: ywatanabe
52
67
  !-- File: /home/ywatanabe/proj/figrecipe/README.md
53
68
  !-- --- -->
@@ -58,51 +73,29 @@ Description-Content-Type: text/markdown
58
73
  </a>
59
74
  </p>
60
75
 
61
- # FigRecipe
76
+ # FigRecipe — **Reproducible matplotlib figures with mm-precision layouts.**
62
77
 
63
- **Reproducible matplotlib figures with mm-precision layouts.**
64
- FigRecipe is a lightweight recording & reproduction layer for matplotlib,
65
- designed for scientific figures that must remain **editable, inspectable,
66
- and reproducible**.
78
+ FigRecipe separates **what** is plotted (data) from **how** it is styled, storing both in a structured format. This enables reproducible figures with GUI editing while preserving scientific integrity, and allows AI integration in a scientifically rigorous manner. Ultimately, FigRecipe will bundle: (1) reproducible, style-editable figures, and (2) metadata such as statistical values—creating atomic, portable, and traceable scientific figure objects essential for automated research in the AI era.
67
79
 
68
- Part of **SciTeX™ – Research OS for reproducible science**
69
- https://scitex.ai
80
+ FigRecipe is part of [**SciTeX™ (pending) – Research OS for reproducible science**](https://scitex.ai)
70
81
 
71
82
  [![PyPI version](https://badge.fury.io/py/figrecipe.svg)](https://badge.fury.io/py/figrecipe)
72
83
  [![Tests](https://github.com/ywatanabe1989/figrecipe/actions/workflows/test.yml/badge.svg)](https://github.com/ywatanabe1989/figrecipe/actions/workflows/test.yml)
73
84
  [![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
74
85
 
75
- ---
76
-
77
- ## Why FigRecipe?
78
-
79
- In scientific workflows, figures are often:
80
- - hard to reproduce once scripts change,
81
- - resized manually in pixels or inches,
82
- - impossible to partially reuse or inspect later.
83
-
84
- **FigRecipe solves this by recording plotting calls as a structured “recipe”**,
85
- allowing figures to be:
86
- - faithfully reproduced,
87
- - partially re-rendered,
88
- - inspected for underlying data,
89
- - laid out in **exact millimeters** for publication.
90
86
 
91
87
  ---
92
88
 
93
89
  ## Key Features
94
90
 
95
- - ✅ Drop-in replacement for `matplotlib.pyplot` (use `figrecipe.pyplot` to enable recording)
96
- - ✅ Automatic recording of plotting calls
97
- - ✅ Reproduce figures from a YAML recipe
98
- - ✅ Extract plotted data programmatically
99
- - ✅ Selective reproduction of specific plots
100
- - ✅ Millimeter-based layout (journal-ready)
91
+ - ✅ Drop-in replacement for `matplotlib.pyplot`—minimal migration and learning cost
92
+ - ✅ All intermediate files use familiar formats (PNG/SVG/PDF/YAML/JSON)
93
+ - ✅ Import/Export to integrate with your existing workflow
94
+ - ✅ Millimeter-based layout (journal-ready)—difficult to achieve manually with matplotlib
101
95
  - ✅ Publication-quality style presets
102
- - ✅ Dark theme support (data colors preserved)
103
- - ✅ Seamless seaborn integration
104
- - ✅ Crop figures to content with mm margins
105
- - ✅ **Interactive GUI editor** with live preview
96
+ - ✅ Dark theme support (data colors preserved), with light theme export for journal compliance
97
+ - ✅ Research-focused features: automatic cropping, axis alignment, panel labels, and caption embedding
98
+ - ✅ Interactive GUI editor for manual adjustments
106
99
 
107
100
  ---
108
101
 
@@ -110,20 +103,29 @@ allowing figures to be:
110
103
 
111
104
  📓 **[View Demo Notebook on nbviewer](https://nbviewer.org/github/ywatanabe1989/figrecipe/blob/main/examples/figrecipe_demo.ipynb)** (recommended)
112
105
 
113
- The notebook includes side-by-side comparisons of original and reproduced figures.
106
+ <details>
107
+ <summary><b>Demo Videos</b> — GUI Editor in action</summary>
114
108
 
115
- Source: [examples/figrecipe_demo.ipynb](examples/figrecipe_demo.ipynb)
109
+ | Dark Mode | Change Color | Drag Panel |
110
+ |:---:|:---:|:---:|
111
+ | ![Dark Mode](examples/demo_movie/outputs/01_enable_dark_mode.gif) | ![Change Color](examples/demo_movie/outputs/02_change_element_color.gif) | ![Drag Panel](examples/demo_movie/outputs/03_drag_panel_position.gif) |
116
112
 
117
- ### Supported Plot Types
113
+ | Move Legend | Undo/Redo | Toggle Theme |
114
+ |:---:|:---:|:---:|
115
+ | ![Move Legend](examples/demo_movie/outputs/04_move_legend.gif) | ![Undo Redo](examples/demo_movie/outputs/05_undo_and_redo.gif) | ![Toggle Theme](examples/demo_movie/outputs/06_toggle_theme.gif) |
118
116
 
119
- FigRecipe supports 46 matplotlib plot types, organized into 9 categories:
117
+ | Hover Feedback | Edit Labels | Zoom Controls |
118
+ |:---:|:---:|:---:|
119
+ | ![Hover](examples/demo_movie/outputs/07_hover_feedback.gif) | ![Edit Labels](examples/demo_movie/outputs/08_edit_labels.gif) | ![Zoom](examples/demo_movie/outputs/09_zoom_controls.gif) |
120
+
121
+ </details>
120
122
 
121
123
  <details>
122
- <summary><b>All Plot Types</b> (click to expand)</summary>
124
+ <summary><b>Supported Plot Types</b> 46 matplotlib plot types in 9 categories</summary>
125
+
123
126
  <p align="center">
124
127
  <img src="docs/images/plot_types/all_plot_types.png" alt="All Plot Types" width="100%"/>
125
128
  </p>
126
- </details>
127
129
 
128
130
  | Line & Curve | Scatter | Distribution |
129
131
  |:---:|:---:|:---:|
@@ -138,6 +140,7 @@ FigRecipe supports 46 matplotlib plot types, organized into 9 categories:
138
140
  | ![Vector](docs/images/plot_types/category_vector_flow.png) | ![Spectral](docs/images/plot_types/category_spectral_signal.png) | ![Special](docs/images/plot_types/category_special.png) |
139
141
 
140
142
  Generate all plots: `python examples/demo_plot_all.py`
143
+ </details>
141
144
 
142
145
  ## Installation
143
146
 
@@ -178,12 +181,53 @@ img_path, yaml_path, result = fr.save(fig, 'figure.png')
178
181
  # → creates: figure.png + figure.yaml
179
182
  ```
180
183
 
184
+ <details>
185
+ <summary><b>Supported I/O Formats</b> — Save and load are fully symmetric</summary>
186
+
187
+ **Save** creates both image and recipe. **Load** finds recipe from any format:
188
+
189
+ ```python
190
+ # Save examples
191
+ fr.save(fig, 'figure.png') # → figure.png + figure.yaml
192
+ fr.save(fig, 'figure.yaml') # → figure.yaml + figure.png
193
+ fr.save(fig, 'figure_bundle/') # → directory with recipe.yaml + figure.png
194
+ fr.save(fig, 'figure.zip') # → ZIP containing recipe.yaml + figure.png
195
+
196
+ # Load examples (symmetric with save)
197
+ fig, ax = fr.load('figure.png') # ← finds figure.yaml
198
+ fig, ax = fr.load('figure.yaml') # ← direct
199
+ fig, ax = fr.load('figure_bundle/') # ← finds recipe.yaml inside
200
+ fig, ax = fr.load('figure.zip') # ← extracts recipe.yaml
201
+
202
+ # Edit also supports all formats
203
+ fr.edit('figure.png') # Opens editor, finds figure.yaml
204
+
205
+ # Note: fr.reproduce() is an alias for fr.load()
206
+ ```
207
+
208
+ | Format | Save | Load | Notes |
209
+ |--------|:----:|:----:|-------|
210
+ | `.png` / `.jpg` / `.jpeg` | ✓ | ✓ | Creates/finds `.yaml` alongside |
211
+ | `.pdf` / `.svg` | ✓ | ✓ | Creates/finds `.yaml` alongside |
212
+ | `.tif` / `.tiff` | ✓ | ✓ | Creates/finds `.yaml` alongside |
213
+ | `.yaml` / `.yml` | ✓ | ✓ | Creates/finds image alongside |
214
+ | Directory (`path/`) | ✓ | ✓ | Bundle with `recipe.yaml` + image |
215
+ | `.zip` | ✓ | ✓ | ZIP bundle with `recipe.yaml` + image |
216
+
217
+ **Alternative save method:**
218
+ ```python
219
+ fig.savefig('figure.png') # Same as fr.save()
220
+ fig.savefig('figure.png', save_recipe=False) # Image only, no recipe
221
+ ```
222
+
223
+ </details>
224
+
181
225
  ### Reproducing a Figure
182
226
 
183
227
  ``` python
184
228
  import figrecipe as fr
185
229
 
186
- fig, ax = fr.reproduce('figure.yaml')
230
+ fig, ax = fr.reproduce('figure.yaml') # Or .png, .pdf, directory/, .zip
187
231
  ```
188
232
 
189
233
  ### Extracting Plotted Data
@@ -195,18 +239,6 @@ data = fr.extract_data('figure.yaml')
195
239
  # {'sine_wave': {'x': array([...]), 'y': array([...])}}
196
240
  ```
197
241
 
198
- ### Millimeter-Based Layout (Publication-Ready)
199
-
200
- ``` python
201
- fig, ax = fr.subplots(
202
- axes_width_mm=60,
203
- axes_height_mm=40,
204
- margin_left_mm=15,
205
- margin_bottom_mm=12,
206
- )
207
- ```
208
- This guarantees consistent sizing across editors, exports, and journals.
209
-
210
242
  ### Style Presets
211
243
 
212
244
  ``` python
@@ -227,6 +259,47 @@ fr.load_style('/path/to/my_style.yaml')
227
259
 
228
260
  See [src/figrecipe/styles/presets/](src/figrecipe/styles/presets/) for full examples.
229
261
 
262
+ ### Millimeter-Based Layout (Publication-Ready)
263
+
264
+ ``` python
265
+ fig, ax = fr.subplots(
266
+ axes_width_mm=60,
267
+ axes_height_mm=40,
268
+ margin_left_mm=15,
269
+ margin_bottom_mm=12,
270
+ )
271
+ ```
272
+ This guarantees consistent sizing across editors, exports, and journals.
273
+
274
+ ### Figure Legends (Scientific Captions)
275
+
276
+ Store publication-ready figure legends as metadata (not rendered on the figure):
277
+
278
+ ``` python
279
+ fig, axes = fr.subplots(1, 2)
280
+
281
+ # Figure legend (main description)
282
+ fig.set_caption(
283
+ "Comparison of treatment effects on neural activity. "
284
+ "Data represent mean ± SEM."
285
+ )
286
+
287
+ # Panel legends (A, B, C, ...)
288
+ axes[0].set_caption("Representative traces from control condition")
289
+ axes[1].set_caption("Quantification across subjects (n=12)")
290
+
291
+ # Access captions
292
+ print(fig.caption) # Figure legend
293
+ print(axes[0].caption) # Panel A legend
294
+
295
+ # Auto-generate with statistics
296
+ fig.set_stats({"comparisons": [{"name": "A vs B", "p_value": 0.003}]})
297
+ full_legend = fig.generate_caption(style="publication")
298
+ # → "Comparison of... (A) Representative traces... (B) Quantification... A vs B (p=0.003)."
299
+ ```
300
+
301
+ Captions are saved in the recipe YAML and displayed in the GUI editor's caption pane below the canvas—ready for copy-paste into manuscripts.
302
+
230
303
  ### Interactive GUI Editor
231
304
 
232
305
  ``` python
@@ -259,16 +332,11 @@ The editor provides:
259
332
  |:---:|:---:|:---:|
260
333
  | ![Axis Panel](docs/images/editor_axis_panel.png) | ![Download](docs/images/editor_download_options.png) | ![Dark Mode](docs/images/editor_dark_mode.png) |
261
334
 
262
- ### Style Format (YAML)
335
+ <details>
336
+ <summary><b>Style Format (YAML)</b> — Full preset example</summary>
263
337
 
264
338
  ``` yaml
265
- # Timestamp: "2025-12-22 12:40:36 (ywatanabe)"
266
- # File: ./src/figrecipe/styles/presets/SCITEX.yaml
267
- # FIGRECIPE Style Preset
268
- # ======================
269
- # Publication-quality settings for scientific figures.
270
- # Optimized for scientific journals with Arial font.
271
-
339
+ # FIGRECIPE Style Preset (SCITEX)
272
340
  axes:
273
341
  width_mm: 40
274
342
  height_mm: 28
@@ -280,113 +348,46 @@ margins:
280
348
  bottom_mm: 1
281
349
  top_mm: 1
282
350
 
283
- spacing:
284
- horizontal_mm: 8
285
- vertical_mm: 10
286
-
287
351
  fonts:
288
352
  family: "Arial"
289
353
  axis_label_pt: 7
290
354
  tick_label_pt: 7
291
355
  title_pt: 8
292
- suptitle_pt: 8
293
- legend_pt: 6
294
- annotation_pt: 6
295
-
296
- padding:
297
- label_pt: 2.0
298
- tick_pt: 2.0
299
- title_pt: 4.0
300
356
 
301
357
  lines:
302
358
  trace_mm: 0.2
303
359
  errorbar_mm: 0.2
304
- errorbar_cap_mm: 0.8
305
360
 
306
361
  ticks:
307
362
  length_mm: 0.8
308
363
  thickness_mm: 0.2
309
364
  direction: "out"
310
- n_ticks: 4
311
-
312
- markers:
313
- size_mm: 0.8
314
- scatter_mm: 0.8
315
- edge_width_mm: null # None = no border (cleaner than 0)
316
-
317
- legend:
318
- frameon: false # No frame for clean look
319
- bg: null # Background (null = use theme.legend_bg)
320
- edgecolor: null # Frame edge color
321
- alpha: 1.0 # Transparency
322
- loc: "best"
323
365
 
324
366
  output:
325
367
  dpi: 300
326
368
  transparent: true
327
369
  format: "pdf"
328
370
 
329
- behavior:
330
- auto_scale_axes: true
331
- hide_top_spine: true
332
- hide_right_spine: true
333
- grid: false
334
-
335
371
  theme:
336
372
  mode: "light"
337
373
  dark:
338
- figure_bg: "transparent"
339
- axes_bg: "transparent"
340
- legend_bg: "transparent"
341
374
  text: "#d4d4d4"
342
375
  spine: "#d4d4d4"
343
- tick: "#d4d4d4"
344
- grid: "#3a3a3a"
345
376
  light:
346
- figure_bg: "transparent"
347
- axes_bg: "transparent"
348
- legend_bg: "transparent"
349
377
  text: "black"
350
378
  spine: "black"
351
- tick: "black"
352
- grid: "#cccccc"
353
379
 
354
- # SciTeX Color Palette (RGB format)
355
380
  colors:
356
381
  palette:
357
382
  - [0, 128, 192] # blue
358
383
  - [255, 70, 50] # red
359
384
  - [20, 180, 20] # green
360
- - [230, 160, 20] # yellow
361
- - [200, 50, 255] # purple
362
- - [20, 200, 200] # lightblue
363
- - [228, 94, 50] # orange
364
- - [255, 150, 200] # pink
365
-
366
- # Named colors
367
- white: [255, 255, 255]
368
- black: [0, 0, 0]
369
- blue: [0, 128, 192]
370
- red: [255, 70, 50]
371
- pink: [255, 150, 200]
372
- green: [20, 180, 20]
373
- yellow: [230, 160, 20]
374
- gray: [128, 128, 128]
375
- grey: [128, 128, 128]
376
- purple: [200, 50, 255]
377
- lightblue: [20, 200, 200]
378
- brown: [128, 0, 0]
379
- navy: [0, 0, 100]
380
- orange: [228, 94, 50]
381
-
382
- # Semantic
383
- primary: [0, 128, 192]
384
- secondary: [255, 70, 50]
385
- accent: [20, 180, 20]
386
-
387
385
  # EOF
388
386
  ```
389
387
 
388
+ See [src/figrecipe/styles/presets/](src/figrecipe/styles/presets/) for complete examples.
389
+ </details>
390
+
390
391
 
391
392
  ### API Overview
392
393
 
@@ -394,7 +395,7 @@ colors:
394
395
  |----------------------------------|---------------------------------------------------|
395
396
  | `import figrecipe.pyplot as plt` | Drop-in replacement of `matplotlib.pyplot as plt` |
396
397
  | `import figrecipe as fr` | Import figrecipe package |
397
- |----------------------------------|---------------------------------------------------|
398
+
398
399
  | Function | |
399
400
  |----------------------------------|---------------------------------------------------|
400
401
  | `fr.subplots()` | Create a recording-enabled figure |
@@ -406,24 +407,21 @@ colors:
406
407
  | `fr.load_style()` | Load style preset (global) |
407
408
  | `fr.list_presets()` | List available presets |
408
409
  | `fr.crop('fig.png')` | Crop to content with mm margin |
409
-
410
- ## Positioning
411
-
412
- FigRecipe focuses on *how* figures are constructed, not *what* they represent.
413
-
414
- FigRecipe provides recording, reproduction, layout fidelity, and interactive editing.
415
-
416
- Higher-level workflows (figures–tables–statistics bundle, integration with manuscript writing) are handled in:
417
-
418
- FTS (Figure-Table-Statistics Bundle) in SciTeX Ecosystem:
419
- https://github.com/ywatanabe1989/scitex-code
420
- https://scitex.ai/vis/
421
- https://scitex.ai/writer/
410
+ | `fig.set_caption(text)` | Set figure legend (metadata) |
411
+ | `ax.set_caption(text)` | Set panel legend (metadata) |
422
412
 
423
413
 
424
414
  ## License
425
415
 
426
416
  AGPL-3.0 See [LICENSE](LICENSE)
427
- .
417
+
418
+ ## Contact
419
+ Yusuke Watanabe (ywatanabe@scitex.ai)
420
+
421
+ <p align="center">
422
+ <a href="https://scitex.ai" target="_blank">
423
+ <img src="docs/scitex-icon-navy-inverted.png" alt="SciTeX" width="80"/>
424
+ </a>
425
+ </p>
428
426
 
429
427
  <!-- EOF -->