figrecipe 0.6.0__py3-none-any.whl → 0.7.4__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 (177) hide show
  1. figrecipe/__init__.py +106 -973
  2. figrecipe/_api/__init__.py +48 -0
  3. figrecipe/_api/_extract.py +108 -0
  4. figrecipe/_api/_notebook.py +61 -0
  5. figrecipe/_api/_panel.py +46 -0
  6. figrecipe/_api/_save.py +191 -0
  7. figrecipe/_api/_seaborn_proxy.py +34 -0
  8. figrecipe/_api/_style_manager.py +153 -0
  9. figrecipe/_api/_subplots.py +333 -0
  10. figrecipe/_api/_validate.py +82 -0
  11. figrecipe/_dev/__init__.py +2 -93
  12. figrecipe/_dev/_plotters.py +76 -0
  13. figrecipe/_dev/_run_demos.py +56 -0
  14. figrecipe/_dev/demo_plotters/__init__.py +35 -166
  15. figrecipe/_dev/demo_plotters/_categories.py +81 -0
  16. figrecipe/_dev/demo_plotters/_figure_creators.py +119 -0
  17. figrecipe/_dev/demo_plotters/_helpers.py +31 -0
  18. figrecipe/_dev/demo_plotters/_registry.py +50 -0
  19. figrecipe/_dev/demo_plotters/bar_categorical/__init__.py +4 -0
  20. figrecipe/_dev/demo_plotters/contour_surface/__init__.py +4 -0
  21. figrecipe/_dev/demo_plotters/distribution/__init__.py +4 -0
  22. figrecipe/_dev/demo_plotters/image_matrix/__init__.py +4 -0
  23. figrecipe/_dev/demo_plotters/line_curve/__init__.py +4 -0
  24. figrecipe/_dev/demo_plotters/{plot_plot.py → line_curve/plot_plot.py} +3 -2
  25. figrecipe/_dev/demo_plotters/scatter_points/__init__.py +4 -0
  26. figrecipe/_dev/demo_plotters/special/__init__.py +4 -0
  27. figrecipe/_dev/demo_plotters/{plot_pie.py → special/plot_pie.py} +5 -1
  28. figrecipe/_dev/demo_plotters/spectral_signal/__init__.py +4 -0
  29. figrecipe/_dev/demo_plotters/vector_flow/__init__.py +4 -0
  30. figrecipe/_editor/__init__.py +57 -9
  31. figrecipe/_editor/_bbox/__init__.py +43 -0
  32. figrecipe/_editor/_bbox/_collections.py +177 -0
  33. figrecipe/_editor/_bbox/_elements.py +159 -0
  34. figrecipe/_editor/_bbox/_extract.py +256 -0
  35. figrecipe/_editor/_bbox/_extract_axes.py +370 -0
  36. figrecipe/_editor/_bbox/_extract_text.py +342 -0
  37. figrecipe/_editor/_bbox/_lines.py +173 -0
  38. figrecipe/_editor/_bbox/_transforms.py +146 -0
  39. figrecipe/_editor/_flask_app.py +68 -1039
  40. figrecipe/_editor/_helpers.py +242 -0
  41. figrecipe/_editor/_hitmap/__init__.py +76 -0
  42. figrecipe/_editor/_hitmap/_artists/__init__.py +21 -0
  43. figrecipe/_editor/_hitmap/_artists/_collections.py +345 -0
  44. figrecipe/_editor/_hitmap/_artists/_images.py +68 -0
  45. figrecipe/_editor/_hitmap/_artists/_lines.py +107 -0
  46. figrecipe/_editor/_hitmap/_artists/_patches.py +163 -0
  47. figrecipe/_editor/_hitmap/_artists/_text.py +190 -0
  48. figrecipe/_editor/_hitmap/_colors.py +181 -0
  49. figrecipe/_editor/_hitmap/_detect.py +137 -0
  50. figrecipe/_editor/_hitmap/_restore.py +154 -0
  51. figrecipe/_editor/_hitmap_main.py +182 -0
  52. figrecipe/_editor/_preferences.py +135 -0
  53. figrecipe/_editor/_render_overrides.py +480 -0
  54. figrecipe/_editor/_renderer.py +35 -185
  55. figrecipe/_editor/_routes_axis.py +453 -0
  56. figrecipe/_editor/_routes_core.py +284 -0
  57. figrecipe/_editor/_routes_element.py +317 -0
  58. figrecipe/_editor/_routes_style.py +223 -0
  59. figrecipe/_editor/_templates/__init__.py +78 -1
  60. figrecipe/_editor/_templates/_html.py +109 -13
  61. figrecipe/_editor/_templates/_scripts/__init__.py +120 -0
  62. figrecipe/_editor/_templates/_scripts/_api.py +228 -0
  63. figrecipe/_editor/_templates/_scripts/_colors.py +485 -0
  64. figrecipe/_editor/_templates/_scripts/_core.py +436 -0
  65. figrecipe/_editor/_templates/_scripts/_debug_snapshot.py +186 -0
  66. figrecipe/_editor/_templates/_scripts/_element_editor.py +310 -0
  67. figrecipe/_editor/_templates/_scripts/_files.py +195 -0
  68. figrecipe/_editor/_templates/_scripts/_hitmap.py +509 -0
  69. figrecipe/_editor/_templates/_scripts/_inspector.py +315 -0
  70. figrecipe/_editor/_templates/_scripts/_labels.py +464 -0
  71. figrecipe/_editor/_templates/_scripts/_legend_drag.py +265 -0
  72. figrecipe/_editor/_templates/_scripts/_modals.py +226 -0
  73. figrecipe/_editor/_templates/_scripts/_overlays.py +292 -0
  74. figrecipe/_editor/_templates/_scripts/_panel_drag.py +334 -0
  75. figrecipe/_editor/_templates/_scripts/_panel_position.py +279 -0
  76. figrecipe/_editor/_templates/_scripts/_selection.py +237 -0
  77. figrecipe/_editor/_templates/_scripts/_tabs.py +89 -0
  78. figrecipe/_editor/_templates/_scripts/_view_mode.py +107 -0
  79. figrecipe/_editor/_templates/_scripts/_zoom.py +179 -0
  80. figrecipe/_editor/_templates/_styles/__init__.py +69 -0
  81. figrecipe/_editor/_templates/_styles/_base.py +64 -0
  82. figrecipe/_editor/_templates/_styles/_buttons.py +206 -0
  83. figrecipe/_editor/_templates/_styles/_color_input.py +123 -0
  84. figrecipe/_editor/_templates/_styles/_controls.py +265 -0
  85. figrecipe/_editor/_templates/_styles/_dynamic_props.py +144 -0
  86. figrecipe/_editor/_templates/_styles/_forms.py +126 -0
  87. figrecipe/_editor/_templates/_styles/_hitmap.py +184 -0
  88. figrecipe/_editor/_templates/_styles/_inspector.py +90 -0
  89. figrecipe/_editor/_templates/_styles/_labels.py +118 -0
  90. figrecipe/_editor/_templates/_styles/_modals.py +98 -0
  91. figrecipe/_editor/_templates/_styles/_overlays.py +130 -0
  92. figrecipe/_editor/_templates/_styles/_preview.py +225 -0
  93. figrecipe/_editor/_templates/_styles/_selection.py +73 -0
  94. figrecipe/_params/_DECORATION_METHODS.py +6 -0
  95. figrecipe/_recorder.py +35 -106
  96. figrecipe/_recorder_utils.py +124 -0
  97. figrecipe/_reproducer/__init__.py +18 -0
  98. figrecipe/_reproducer/_core.py +498 -0
  99. figrecipe/_reproducer/_custom_plots.py +279 -0
  100. figrecipe/_reproducer/_seaborn.py +100 -0
  101. figrecipe/_reproducer/_violin.py +186 -0
  102. figrecipe/_signatures/_kwargs.py +273 -0
  103. figrecipe/_signatures/_loader.py +21 -423
  104. figrecipe/_signatures/_parsing.py +147 -0
  105. figrecipe/_wrappers/_axes.py +119 -910
  106. figrecipe/_wrappers/_axes_helpers.py +136 -0
  107. figrecipe/_wrappers/_axes_plots.py +418 -0
  108. figrecipe/_wrappers/_axes_seaborn.py +157 -0
  109. figrecipe/_wrappers/_figure.py +162 -0
  110. figrecipe/_wrappers/_panel_labels.py +127 -0
  111. figrecipe/_wrappers/_plot_helpers.py +143 -0
  112. figrecipe/_wrappers/_violin_helpers.py +180 -0
  113. figrecipe/styles/__init__.py +8 -6
  114. figrecipe/styles/_dotdict.py +72 -0
  115. figrecipe/styles/_finalize.py +134 -0
  116. figrecipe/styles/_fonts.py +77 -0
  117. figrecipe/styles/_kwargs_converter.py +178 -0
  118. figrecipe/styles/_plot_styles.py +209 -0
  119. figrecipe/styles/_style_applier.py +32 -478
  120. figrecipe/styles/_style_loader.py +16 -192
  121. figrecipe/styles/_themes.py +151 -0
  122. figrecipe/styles/presets/MATPLOTLIB.yaml +2 -1
  123. figrecipe/styles/presets/SCITEX.yaml +29 -24
  124. {figrecipe-0.6.0.dist-info → figrecipe-0.7.4.dist-info}/METADATA +37 -2
  125. figrecipe-0.7.4.dist-info/RECORD +188 -0
  126. figrecipe/_editor/_bbox.py +0 -978
  127. figrecipe/_editor/_hitmap.py +0 -937
  128. figrecipe/_editor/_templates/_scripts.py +0 -2778
  129. figrecipe/_editor/_templates/_styles.py +0 -1326
  130. figrecipe/_reproducer.py +0 -975
  131. figrecipe-0.6.0.dist-info/RECORD +0 -90
  132. /figrecipe/_dev/demo_plotters/{plot_bar.py → bar_categorical/plot_bar.py} +0 -0
  133. /figrecipe/_dev/demo_plotters/{plot_barh.py → bar_categorical/plot_barh.py} +0 -0
  134. /figrecipe/_dev/demo_plotters/{plot_contour.py → contour_surface/plot_contour.py} +0 -0
  135. /figrecipe/_dev/demo_plotters/{plot_contourf.py → contour_surface/plot_contourf.py} +0 -0
  136. /figrecipe/_dev/demo_plotters/{plot_tricontour.py → contour_surface/plot_tricontour.py} +0 -0
  137. /figrecipe/_dev/demo_plotters/{plot_tricontourf.py → contour_surface/plot_tricontourf.py} +0 -0
  138. /figrecipe/_dev/demo_plotters/{plot_tripcolor.py → contour_surface/plot_tripcolor.py} +0 -0
  139. /figrecipe/_dev/demo_plotters/{plot_triplot.py → contour_surface/plot_triplot.py} +0 -0
  140. /figrecipe/_dev/demo_plotters/{plot_boxplot.py → distribution/plot_boxplot.py} +0 -0
  141. /figrecipe/_dev/demo_plotters/{plot_ecdf.py → distribution/plot_ecdf.py} +0 -0
  142. /figrecipe/_dev/demo_plotters/{plot_hist.py → distribution/plot_hist.py} +0 -0
  143. /figrecipe/_dev/demo_plotters/{plot_hist2d.py → distribution/plot_hist2d.py} +0 -0
  144. /figrecipe/_dev/demo_plotters/{plot_violinplot.py → distribution/plot_violinplot.py} +0 -0
  145. /figrecipe/_dev/demo_plotters/{plot_hexbin.py → image_matrix/plot_hexbin.py} +0 -0
  146. /figrecipe/_dev/demo_plotters/{plot_imshow.py → image_matrix/plot_imshow.py} +0 -0
  147. /figrecipe/_dev/demo_plotters/{plot_matshow.py → image_matrix/plot_matshow.py} +0 -0
  148. /figrecipe/_dev/demo_plotters/{plot_pcolor.py → image_matrix/plot_pcolor.py} +0 -0
  149. /figrecipe/_dev/demo_plotters/{plot_pcolormesh.py → image_matrix/plot_pcolormesh.py} +0 -0
  150. /figrecipe/_dev/demo_plotters/{plot_spy.py → image_matrix/plot_spy.py} +0 -0
  151. /figrecipe/_dev/demo_plotters/{plot_errorbar.py → line_curve/plot_errorbar.py} +0 -0
  152. /figrecipe/_dev/demo_plotters/{plot_fill.py → line_curve/plot_fill.py} +0 -0
  153. /figrecipe/_dev/demo_plotters/{plot_fill_between.py → line_curve/plot_fill_between.py} +0 -0
  154. /figrecipe/_dev/demo_plotters/{plot_fill_betweenx.py → line_curve/plot_fill_betweenx.py} +0 -0
  155. /figrecipe/_dev/demo_plotters/{plot_stackplot.py → line_curve/plot_stackplot.py} +0 -0
  156. /figrecipe/_dev/demo_plotters/{plot_stairs.py → line_curve/plot_stairs.py} +0 -0
  157. /figrecipe/_dev/demo_plotters/{plot_step.py → line_curve/plot_step.py} +0 -0
  158. /figrecipe/_dev/demo_plotters/{plot_scatter.py → scatter_points/plot_scatter.py} +0 -0
  159. /figrecipe/_dev/demo_plotters/{plot_eventplot.py → special/plot_eventplot.py} +0 -0
  160. /figrecipe/_dev/demo_plotters/{plot_loglog.py → special/plot_loglog.py} +0 -0
  161. /figrecipe/_dev/demo_plotters/{plot_semilogx.py → special/plot_semilogx.py} +0 -0
  162. /figrecipe/_dev/demo_plotters/{plot_semilogy.py → special/plot_semilogy.py} +0 -0
  163. /figrecipe/_dev/demo_plotters/{plot_stem.py → special/plot_stem.py} +0 -0
  164. /figrecipe/_dev/demo_plotters/{plot_acorr.py → spectral_signal/plot_acorr.py} +0 -0
  165. /figrecipe/_dev/demo_plotters/{plot_angle_spectrum.py → spectral_signal/plot_angle_spectrum.py} +0 -0
  166. /figrecipe/_dev/demo_plotters/{plot_cohere.py → spectral_signal/plot_cohere.py} +0 -0
  167. /figrecipe/_dev/demo_plotters/{plot_csd.py → spectral_signal/plot_csd.py} +0 -0
  168. /figrecipe/_dev/demo_plotters/{plot_magnitude_spectrum.py → spectral_signal/plot_magnitude_spectrum.py} +0 -0
  169. /figrecipe/_dev/demo_plotters/{plot_phase_spectrum.py → spectral_signal/plot_phase_spectrum.py} +0 -0
  170. /figrecipe/_dev/demo_plotters/{plot_psd.py → spectral_signal/plot_psd.py} +0 -0
  171. /figrecipe/_dev/demo_plotters/{plot_specgram.py → spectral_signal/plot_specgram.py} +0 -0
  172. /figrecipe/_dev/demo_plotters/{plot_xcorr.py → spectral_signal/plot_xcorr.py} +0 -0
  173. /figrecipe/_dev/demo_plotters/{plot_barbs.py → vector_flow/plot_barbs.py} +0 -0
  174. /figrecipe/_dev/demo_plotters/{plot_quiver.py → vector_flow/plot_quiver.py} +0 -0
  175. /figrecipe/_dev/demo_plotters/{plot_streamplot.py → vector_flow/plot_streamplot.py} +0 -0
  176. {figrecipe-0.6.0.dist-info → figrecipe-0.7.4.dist-info}/WHEEL +0 -0
  177. {figrecipe-0.6.0.dist-info → figrecipe-0.7.4.dist-info}/licenses/LICENSE +0 -0
@@ -10,10 +10,9 @@ applied, enabling real-time preview updates in the GUI editor.
10
10
  import io
11
11
  from typing import Any, Dict, Optional, Tuple
12
12
 
13
- from matplotlib.figure import Figure
14
-
15
13
  from .._wrappers import RecordingFigure
16
14
  from ._bbox import extract_bboxes
15
+ from ._render_overrides import apply_dark_mode, apply_overrides
17
16
 
18
17
 
19
18
  def render_preview(
@@ -48,13 +47,19 @@ def render_preview(
48
47
  # Get underlying matplotlib figure
49
48
  mpl_fig = fig.fig if hasattr(fig, "fig") else fig
50
49
 
50
+ # Get record for call_id grouping (if fig is a RecordingFigure)
51
+ record = fig.record if hasattr(fig, "record") else None
52
+
51
53
  # Apply style overrides
52
54
  if overrides:
53
- _apply_overrides(mpl_fig, overrides)
55
+ apply_overrides(mpl_fig, overrides, record)
54
56
 
55
57
  # Apply dark mode if requested
56
58
  if dark_mode:
57
- _apply_dark_mode(mpl_fig)
59
+ apply_dark_mode(mpl_fig)
60
+
61
+ # Finalize ticks and special plots (must be done after all plotting)
62
+ _finalize_figure(fig, mpl_fig)
58
63
 
59
64
  # Render to buffer first
60
65
  buf = io.BytesIO()
@@ -120,185 +125,6 @@ def render_to_base64(
120
125
  return base64_str, bboxes, img_size
121
126
 
122
127
 
123
- def _apply_overrides(fig: Figure, overrides: Dict[str, Any]) -> None:
124
- """
125
- Apply style overrides to figure.
126
-
127
- Parameters
128
- ----------
129
- fig : Figure
130
- Matplotlib figure.
131
- overrides : dict
132
- Style overrides with keys like:
133
- - axes_width_mm, axes_height_mm
134
- - fonts_axis_label_pt, fonts_tick_label_pt
135
- - lines_trace_mm
136
- - etc.
137
- """
138
- from ..styles._style_applier import apply_style_mm
139
-
140
- axes_list = fig.get_axes()
141
-
142
- for ax in axes_list:
143
- # Apply mm-based styling
144
- apply_style_mm(ax, overrides)
145
-
146
- # Apply specific overrides that aren't handled by apply_style_mm
147
- # YAML-compatible keys are canonical, legacy keys supported for backwards compatibility
148
-
149
- # Font sizes (YAML: fonts_axis_label_pt, legacy: axis_font_size_pt)
150
- axis_fs = overrides.get(
151
- "fonts_axis_label_pt", overrides.get("axis_font_size_pt")
152
- )
153
- if axis_fs is not None:
154
- ax.xaxis.label.set_fontsize(axis_fs)
155
- ax.yaxis.label.set_fontsize(axis_fs)
156
-
157
- tick_fs = overrides.get(
158
- "fonts_tick_label_pt", overrides.get("tick_font_size_pt")
159
- )
160
- if tick_fs is not None:
161
- ax.tick_params(labelsize=tick_fs)
162
-
163
- title_fs = overrides.get("fonts_title_pt", overrides.get("title_font_size_pt"))
164
- if title_fs is not None:
165
- ax.title.set_fontsize(title_fs)
166
-
167
- family = overrides.get("fonts_family", overrides.get("font_family"))
168
- if family is not None:
169
- ax.xaxis.label.set_fontfamily(family)
170
- ax.yaxis.label.set_fontfamily(family)
171
- ax.title.set_fontfamily(family)
172
- for label in ax.get_xticklabels() + ax.get_yticklabels():
173
- label.set_fontfamily(family)
174
-
175
- # Ticks (YAML: ticks_direction, legacy: tick_direction)
176
- tick_dir = overrides.get("ticks_direction", overrides.get("tick_direction"))
177
- if tick_dir is not None and tick_dir in ("in", "out", "inout"):
178
- ax.tick_params(direction=tick_dir)
179
-
180
- tick_len = overrides.get("ticks_length_mm", overrides.get("tick_length_mm"))
181
- if tick_len is not None:
182
- from .._utils._units import mm_to_pt
183
-
184
- length = mm_to_pt(tick_len)
185
- ax.tick_params(length=length)
186
-
187
- # Grid (YAML: behavior_grid, legacy: grid)
188
- grid_value = overrides.get("behavior_grid", overrides.get("grid"))
189
- if grid_value is not None:
190
- if grid_value:
191
- ax.grid(True, alpha=0.3)
192
- else:
193
- ax.grid(False)
194
-
195
- # Spines (YAML: behavior_hide_top_spine, legacy: hide_top_spine)
196
- hide_top = overrides.get(
197
- "behavior_hide_top_spine", overrides.get("hide_top_spine")
198
- )
199
- if hide_top is not None:
200
- ax.spines["top"].set_visible(not hide_top)
201
-
202
- hide_right = overrides.get(
203
- "behavior_hide_right_spine", overrides.get("hide_right_spine")
204
- )
205
- if hide_right is not None:
206
- ax.spines["right"].set_visible(not hide_right)
207
-
208
- # Legend
209
- legend = ax.get_legend()
210
- if legend is not None:
211
- if "legend_frameon" in overrides:
212
- legend.set_frame_on(overrides["legend_frameon"])
213
-
214
- if "legend_alpha" in overrides:
215
- frame = legend.get_frame()
216
- fc = frame.get_facecolor()
217
- frame.set_facecolor((*fc[:3], overrides["legend_alpha"]))
218
-
219
- # Line widths (YAML: lines_trace_mm, legacy: trace_thickness_mm)
220
- trace_mm = overrides.get("lines_trace_mm", overrides.get("trace_thickness_mm"))
221
- if trace_mm is not None:
222
- from .._utils._units import mm_to_pt
223
-
224
- lw = mm_to_pt(trace_mm)
225
- for line in ax.get_lines():
226
- line.set_linewidth(lw)
227
-
228
- # Marker sizes (YAML: markers_scatter_mm, legacy: marker_size_mm)
229
- # Only apply to PathCollection (scatter), not PolyCollection (violin/fill)
230
- scatter_mm = overrides.get(
231
- "markers_scatter_mm",
232
- overrides.get("markers_size_mm", overrides.get("marker_size_mm")),
233
- )
234
- if scatter_mm is not None:
235
- from matplotlib.collections import PathCollection
236
-
237
- from .._utils._units import mm_to_scatter_size
238
-
239
- size = mm_to_scatter_size(scatter_mm)
240
- for coll in ax.collections:
241
- # Only apply to scatter plots (PathCollection), not violin/fill (PolyCollection)
242
- if isinstance(coll, PathCollection):
243
- try:
244
- coll.set_sizes([size])
245
- except Exception:
246
- pass
247
-
248
-
249
- def _apply_dark_mode(fig: Figure) -> None:
250
- """
251
- Apply dark mode colors to figure.
252
-
253
- Parameters
254
- ----------
255
- fig : Figure
256
- Matplotlib figure.
257
- """
258
- # Dark theme colors
259
- bg_color = "#1a1a1a"
260
- text_color = "#e8e8e8"
261
-
262
- # Figure background
263
- fig.patch.set_facecolor(bg_color)
264
-
265
- # Figure-level text elements (suptitle, supxlabel, supylabel)
266
- if hasattr(fig, "_suptitle") and fig._suptitle is not None:
267
- fig._suptitle.set_color(text_color)
268
- if hasattr(fig, "_supxlabel") and fig._supxlabel is not None:
269
- fig._supxlabel.set_color(text_color)
270
- if hasattr(fig, "_supylabel") and fig._supylabel is not None:
271
- fig._supylabel.set_color(text_color)
272
-
273
- for ax in fig.get_axes():
274
- # Axes background
275
- ax.set_facecolor(bg_color)
276
-
277
- # Text colors
278
- ax.xaxis.label.set_color(text_color)
279
- ax.yaxis.label.set_color(text_color)
280
- ax.title.set_color(text_color)
281
-
282
- # Tick labels
283
- ax.tick_params(colors=text_color)
284
-
285
- # Spines
286
- for spine in ax.spines.values():
287
- spine.set_color(text_color)
288
-
289
- # Grid
290
- ax.tick_params(color=text_color)
291
-
292
- # Legend
293
- legend = ax.get_legend()
294
- if legend is not None:
295
- frame = legend.get_frame()
296
- frame.set_facecolor(bg_color)
297
- frame.set_edgecolor(text_color)
298
- for text in legend.get_texts():
299
- text.set_color(text_color)
300
-
301
-
302
128
  def render_download(
303
129
  fig: RecordingFigure,
304
130
  fmt: str = "png",
@@ -329,11 +155,17 @@ def render_download(
329
155
  """
330
156
  mpl_fig = fig.fig if hasattr(fig, "fig") else fig
331
157
 
158
+ # Get record for call_id grouping (if fig is a RecordingFigure)
159
+ record = fig.record if hasattr(fig, "record") else None
160
+
332
161
  if overrides:
333
- _apply_overrides(mpl_fig, overrides)
162
+ apply_overrides(mpl_fig, overrides, record)
334
163
 
335
164
  if dark_mode:
336
- _apply_dark_mode(mpl_fig)
165
+ apply_dark_mode(mpl_fig)
166
+
167
+ # Finalize ticks and special plots (must be done after all plotting)
168
+ _finalize_figure(fig, mpl_fig)
337
169
 
338
170
  buf = io.BytesIO()
339
171
  mpl_fig.savefig(buf, format=fmt, dpi=dpi, bbox_inches="tight")
@@ -342,8 +174,26 @@ def render_download(
342
174
  return buf.read()
343
175
 
344
176
 
177
+ def _finalize_figure(fig: RecordingFigure, mpl_fig: Any) -> None:
178
+ """Finalize ticks and special plots for all axes in the figure."""
179
+ from ..styles._style_applier import finalize_special_plots, finalize_ticks
180
+
181
+ # Get style dict for finalization
182
+ style_dict = {}
183
+ if hasattr(fig, "style") and fig.style:
184
+ from ..styles import get_style
185
+
186
+ style_dict = get_style(fig.style)
187
+
188
+ for ax in mpl_fig.get_axes():
189
+ finalize_ticks(ax)
190
+ finalize_special_plots(ax, style_dict)
191
+
192
+
345
193
  __all__ = [
346
194
  "render_preview",
347
195
  "render_to_base64",
348
196
  "render_download",
349
197
  ]
198
+
199
+ # EOF