scitex 2.7.0__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 (355) hide show
  1. scitex/__init__.py +6 -2
  2. scitex/__version__.py +1 -1
  3. scitex/audio/README.md +52 -0
  4. scitex/audio/__init__.py +384 -0
  5. scitex/audio/__main__.py +129 -0
  6. scitex/audio/_tts.py +334 -0
  7. scitex/audio/engines/__init__.py +44 -0
  8. scitex/audio/engines/base.py +275 -0
  9. scitex/audio/engines/elevenlabs_engine.py +143 -0
  10. scitex/audio/engines/gtts_engine.py +162 -0
  11. scitex/audio/engines/pyttsx3_engine.py +131 -0
  12. scitex/audio/mcp_server.py +757 -0
  13. scitex/bridge/_helpers.py +1 -1
  14. scitex/bridge/_plt_vis.py +1 -1
  15. scitex/bridge/_stats_vis.py +1 -1
  16. scitex/dev/plt/__init__.py +272 -0
  17. scitex/dev/plt/plot_mpl_axhline.py +28 -0
  18. scitex/dev/plt/plot_mpl_axhspan.py +28 -0
  19. scitex/dev/plt/plot_mpl_axvline.py +28 -0
  20. scitex/dev/plt/plot_mpl_axvspan.py +28 -0
  21. scitex/dev/plt/plot_mpl_bar.py +29 -0
  22. scitex/dev/plt/plot_mpl_barh.py +29 -0
  23. scitex/dev/plt/plot_mpl_boxplot.py +28 -0
  24. scitex/dev/plt/plot_mpl_contour.py +31 -0
  25. scitex/dev/plt/plot_mpl_contourf.py +31 -0
  26. scitex/dev/plt/plot_mpl_errorbar.py +30 -0
  27. scitex/dev/plt/plot_mpl_eventplot.py +28 -0
  28. scitex/dev/plt/plot_mpl_fill.py +30 -0
  29. scitex/dev/plt/plot_mpl_fill_between.py +31 -0
  30. scitex/dev/plt/plot_mpl_hexbin.py +28 -0
  31. scitex/dev/plt/plot_mpl_hist.py +28 -0
  32. scitex/dev/plt/plot_mpl_hist2d.py +28 -0
  33. scitex/dev/plt/plot_mpl_imshow.py +29 -0
  34. scitex/dev/plt/plot_mpl_pcolormesh.py +31 -0
  35. scitex/dev/plt/plot_mpl_pie.py +29 -0
  36. scitex/dev/plt/plot_mpl_plot.py +29 -0
  37. scitex/dev/plt/plot_mpl_quiver.py +31 -0
  38. scitex/dev/plt/plot_mpl_scatter.py +28 -0
  39. scitex/dev/plt/plot_mpl_stackplot.py +31 -0
  40. scitex/dev/plt/plot_mpl_stem.py +29 -0
  41. scitex/dev/plt/plot_mpl_step.py +29 -0
  42. scitex/dev/plt/plot_mpl_violinplot.py +28 -0
  43. scitex/dev/plt/plot_sns_barplot.py +29 -0
  44. scitex/dev/plt/plot_sns_boxplot.py +29 -0
  45. scitex/dev/plt/plot_sns_heatmap.py +28 -0
  46. scitex/dev/plt/plot_sns_histplot.py +29 -0
  47. scitex/dev/plt/plot_sns_kdeplot.py +29 -0
  48. scitex/dev/plt/plot_sns_lineplot.py +31 -0
  49. scitex/dev/plt/plot_sns_scatterplot.py +29 -0
  50. scitex/dev/plt/plot_sns_stripplot.py +29 -0
  51. scitex/dev/plt/plot_sns_swarmplot.py +29 -0
  52. scitex/dev/plt/plot_sns_violinplot.py +29 -0
  53. scitex/dev/plt/plot_stx_bar.py +29 -0
  54. scitex/dev/plt/plot_stx_barh.py +29 -0
  55. scitex/dev/plt/plot_stx_box.py +28 -0
  56. scitex/dev/plt/plot_stx_boxplot.py +28 -0
  57. scitex/dev/plt/plot_stx_conf_mat.py +28 -0
  58. scitex/dev/plt/plot_stx_contour.py +31 -0
  59. scitex/dev/plt/plot_stx_ecdf.py +28 -0
  60. scitex/dev/plt/plot_stx_errorbar.py +30 -0
  61. scitex/dev/plt/plot_stx_fill_between.py +31 -0
  62. scitex/dev/plt/plot_stx_fillv.py +28 -0
  63. scitex/dev/plt/plot_stx_heatmap.py +28 -0
  64. scitex/dev/plt/plot_stx_image.py +28 -0
  65. scitex/dev/plt/plot_stx_imshow.py +28 -0
  66. scitex/dev/plt/plot_stx_joyplot.py +28 -0
  67. scitex/dev/plt/plot_stx_kde.py +28 -0
  68. scitex/dev/plt/plot_stx_line.py +28 -0
  69. scitex/dev/plt/plot_stx_mean_ci.py +28 -0
  70. scitex/dev/plt/plot_stx_mean_std.py +28 -0
  71. scitex/dev/plt/plot_stx_median_iqr.py +28 -0
  72. scitex/dev/plt/plot_stx_raster.py +28 -0
  73. scitex/dev/plt/plot_stx_rectangle.py +28 -0
  74. scitex/dev/plt/plot_stx_scatter.py +29 -0
  75. scitex/dev/plt/plot_stx_shaded_line.py +29 -0
  76. scitex/dev/plt/plot_stx_violin.py +28 -0
  77. scitex/dev/plt/plot_stx_violinplot.py +28 -0
  78. scitex/diagram/README.md +197 -0
  79. scitex/diagram/__init__.py +48 -0
  80. scitex/diagram/_compile.py +312 -0
  81. scitex/diagram/_diagram.py +355 -0
  82. scitex/diagram/_presets.py +173 -0
  83. scitex/diagram/_schema.py +182 -0
  84. scitex/diagram/_split.py +278 -0
  85. scitex/fig/__init__.py +352 -0
  86. scitex/{vis → fig}/backend/_parser.py +1 -1
  87. scitex/{vis → fig}/canvas.py +1 -1
  88. scitex/{vis → fig}/editor/__init__.py +5 -2
  89. scitex/{vis → fig}/editor/_dearpygui_editor.py +1 -1
  90. scitex/{vis → fig}/editor/_defaults.py +70 -5
  91. scitex/{vis → fig}/editor/_mpl_editor.py +1 -1
  92. scitex/{vis → fig}/editor/_qt_editor.py +182 -2
  93. scitex/{vis → fig}/editor/_tkinter_editor.py +1 -1
  94. scitex/fig/editor/edit/__init__.py +50 -0
  95. scitex/fig/editor/edit/backend_detector.py +109 -0
  96. scitex/fig/editor/edit/bundle_resolver.py +240 -0
  97. scitex/fig/editor/edit/editor_launcher.py +239 -0
  98. scitex/fig/editor/edit/manual_handler.py +53 -0
  99. scitex/fig/editor/edit/panel_loader.py +232 -0
  100. scitex/fig/editor/edit/path_resolver.py +67 -0
  101. scitex/fig/editor/flask_editor/_bbox.py +1299 -0
  102. scitex/fig/editor/flask_editor/_core.py +1429 -0
  103. scitex/{vis → fig}/editor/flask_editor/_plotter.py +38 -4
  104. scitex/fig/editor/flask_editor/_renderer.py +813 -0
  105. scitex/fig/editor/flask_editor/static/css/base/reset.css +41 -0
  106. scitex/fig/editor/flask_editor/static/css/base/typography.css +16 -0
  107. scitex/fig/editor/flask_editor/static/css/base/variables.css +85 -0
  108. scitex/fig/editor/flask_editor/static/css/components/buttons.css +217 -0
  109. scitex/fig/editor/flask_editor/static/css/components/context-menu.css +93 -0
  110. scitex/fig/editor/flask_editor/static/css/components/dropdown.css +57 -0
  111. scitex/fig/editor/flask_editor/static/css/components/forms.css +112 -0
  112. scitex/fig/editor/flask_editor/static/css/components/modal.css +59 -0
  113. scitex/fig/editor/flask_editor/static/css/components/sections.css +212 -0
  114. scitex/fig/editor/flask_editor/static/css/features/canvas.css +176 -0
  115. scitex/fig/editor/flask_editor/static/css/features/element-inspector.css +190 -0
  116. scitex/fig/editor/flask_editor/static/css/features/loading.css +59 -0
  117. scitex/fig/editor/flask_editor/static/css/features/overlay.css +45 -0
  118. scitex/fig/editor/flask_editor/static/css/features/panel-grid.css +95 -0
  119. scitex/fig/editor/flask_editor/static/css/features/selection.css +101 -0
  120. scitex/fig/editor/flask_editor/static/css/features/statistics.css +138 -0
  121. scitex/fig/editor/flask_editor/static/css/index.css +31 -0
  122. scitex/fig/editor/flask_editor/static/css/layout/container.css +7 -0
  123. scitex/fig/editor/flask_editor/static/css/layout/controls.css +56 -0
  124. scitex/fig/editor/flask_editor/static/css/layout/preview.css +78 -0
  125. scitex/fig/editor/flask_editor/static/js/alignment/axis.js +314 -0
  126. scitex/fig/editor/flask_editor/static/js/alignment/basic.js +107 -0
  127. scitex/fig/editor/flask_editor/static/js/alignment/distribute.js +54 -0
  128. scitex/fig/editor/flask_editor/static/js/canvas/canvas.js +172 -0
  129. scitex/fig/editor/flask_editor/static/js/canvas/dragging.js +258 -0
  130. scitex/fig/editor/flask_editor/static/js/canvas/resize.js +48 -0
  131. scitex/fig/editor/flask_editor/static/js/canvas/selection.js +71 -0
  132. scitex/fig/editor/flask_editor/static/js/core/api.js +288 -0
  133. scitex/fig/editor/flask_editor/static/js/core/state.js +143 -0
  134. scitex/fig/editor/flask_editor/static/js/core/utils.js +245 -0
  135. scitex/fig/editor/flask_editor/static/js/dev/element-inspector.js +992 -0
  136. scitex/fig/editor/flask_editor/static/js/editor/bbox.js +339 -0
  137. scitex/fig/editor/flask_editor/static/js/editor/element-drag.js +286 -0
  138. scitex/fig/editor/flask_editor/static/js/editor/overlay.js +371 -0
  139. scitex/fig/editor/flask_editor/static/js/editor/preview.js +293 -0
  140. scitex/fig/editor/flask_editor/static/js/main.js +426 -0
  141. scitex/fig/editor/flask_editor/static/js/shortcuts/context-menu.js +152 -0
  142. scitex/fig/editor/flask_editor/static/js/shortcuts/keyboard.js +265 -0
  143. scitex/fig/editor/flask_editor/static/js/ui/controls.js +184 -0
  144. scitex/fig/editor/flask_editor/static/js/ui/download.js +57 -0
  145. scitex/fig/editor/flask_editor/static/js/ui/help.js +100 -0
  146. scitex/fig/editor/flask_editor/static/js/ui/theme.js +34 -0
  147. scitex/fig/editor/flask_editor/templates/__init__.py +123 -0
  148. scitex/fig/editor/flask_editor/templates/_html.py +852 -0
  149. scitex/fig/editor/flask_editor/templates/_scripts.py +4933 -0
  150. scitex/fig/editor/flask_editor/templates/_styles.py +1658 -0
  151. scitex/{vis → fig}/io/__init__.py +13 -1
  152. scitex/fig/io/_bundle.py +1058 -0
  153. scitex/{vis → fig}/io/_canvas.py +1 -1
  154. scitex/{vis → fig}/io/_data.py +1 -1
  155. scitex/{vis → fig}/io/_export.py +1 -1
  156. scitex/{vis → fig}/io/_load.py +1 -1
  157. scitex/{vis → fig}/io/_panel.py +1 -1
  158. scitex/{vis → fig}/io/_save.py +1 -1
  159. scitex/{vis → fig}/model/__init__.py +1 -1
  160. scitex/{vis → fig}/model/_annotations.py +1 -1
  161. scitex/{vis → fig}/model/_axes.py +1 -1
  162. scitex/{vis → fig}/model/_figure.py +1 -1
  163. scitex/{vis → fig}/model/_guides.py +1 -1
  164. scitex/{vis → fig}/model/_plot.py +1 -1
  165. scitex/{vis → fig}/model/_styles.py +1 -1
  166. scitex/{vis → fig}/utils/__init__.py +1 -1
  167. scitex/io/__init__.py +22 -26
  168. scitex/io/_bundle.py +493 -0
  169. scitex/io/_flush.py +5 -2
  170. scitex/io/_load.py +98 -0
  171. scitex/io/_load_modules/_H5Explorer.py +5 -2
  172. scitex/io/_load_modules/_canvas.py +2 -2
  173. scitex/io/_load_modules/_image.py +3 -4
  174. scitex/io/_load_modules/_txt.py +4 -2
  175. scitex/io/_metadata.py +34 -324
  176. scitex/io/_metadata_modules/__init__.py +46 -0
  177. scitex/io/_metadata_modules/_embed.py +70 -0
  178. scitex/io/_metadata_modules/_read.py +64 -0
  179. scitex/io/_metadata_modules/_utils.py +79 -0
  180. scitex/io/_metadata_modules/embed_metadata_jpeg.py +74 -0
  181. scitex/io/_metadata_modules/embed_metadata_pdf.py +53 -0
  182. scitex/io/_metadata_modules/embed_metadata_png.py +26 -0
  183. scitex/io/_metadata_modules/embed_metadata_svg.py +62 -0
  184. scitex/io/_metadata_modules/read_metadata_jpeg.py +57 -0
  185. scitex/io/_metadata_modules/read_metadata_pdf.py +51 -0
  186. scitex/io/_metadata_modules/read_metadata_png.py +39 -0
  187. scitex/io/_metadata_modules/read_metadata_svg.py +44 -0
  188. scitex/io/_qr_utils.py +5 -3
  189. scitex/io/_save.py +548 -30
  190. scitex/io/_save_modules/_canvas.py +3 -3
  191. scitex/io/_save_modules/_image.py +5 -9
  192. scitex/io/_save_modules/_tex.py +7 -4
  193. scitex/io/_zip_bundle.py +439 -0
  194. scitex/io/utils/h5_to_zarr.py +11 -9
  195. scitex/msword/__init__.py +255 -0
  196. scitex/msword/profiles.py +357 -0
  197. scitex/msword/reader.py +753 -0
  198. scitex/msword/utils.py +289 -0
  199. scitex/msword/writer.py +362 -0
  200. scitex/plt/__init__.py +5 -2
  201. scitex/plt/_subplots/_AxesWrapper.py +6 -6
  202. scitex/plt/_subplots/_AxisWrapper.py +15 -9
  203. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/__init__.py +36 -0
  204. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_labels.py +264 -0
  205. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_metadata.py +213 -0
  206. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_visual.py +128 -0
  207. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +59 -0
  208. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_base.py +34 -0
  209. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_scientific.py +593 -0
  210. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_statistical.py +654 -0
  211. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_stx_aliases.py +527 -0
  212. scitex/plt/_subplots/_AxisWrapperMixins/_RawMatplotlibMixin.py +321 -0
  213. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/__init__.py +33 -0
  214. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_base.py +152 -0
  215. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +600 -0
  216. scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +79 -5
  217. scitex/plt/_subplots/_FigWrapper.py +6 -6
  218. scitex/plt/_subplots/_SubplotsWrapper.py +28 -18
  219. scitex/plt/_subplots/_export_as_csv.py +35 -5
  220. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +8 -0
  221. scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +10 -21
  222. scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +18 -7
  223. scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +28 -12
  224. scitex/plt/_subplots/_export_as_csv_formatters/_format_matshow.py +10 -4
  225. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_imshow.py +13 -1
  226. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +12 -2
  227. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +10 -3
  228. scitex/plt/_subplots/_export_as_csv_formatters/_format_quiver.py +10 -4
  229. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +18 -3
  230. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +44 -36
  231. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +14 -2
  232. scitex/plt/_subplots/_export_as_csv_formatters/_format_streamplot.py +11 -5
  233. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_bar.py +84 -0
  234. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_barh.py +85 -0
  235. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_conf_mat.py +14 -3
  236. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_contour.py +54 -0
  237. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_ecdf.py +14 -2
  238. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_errorbar.py +120 -0
  239. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_heatmap.py +16 -6
  240. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_image.py +29 -19
  241. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_imshow.py +63 -0
  242. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_joyplot.py +22 -5
  243. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_ci.py +18 -14
  244. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_std.py +18 -14
  245. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_median_iqr.py +18 -14
  246. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_raster.py +10 -2
  247. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter.py +51 -0
  248. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter_hist.py +18 -9
  249. scitex/plt/ax/_plot/_stx_ecdf.py +4 -2
  250. scitex/plt/gallery/_generate.py +421 -14
  251. scitex/plt/io/__init__.py +53 -0
  252. scitex/plt/io/_bundle.py +490 -0
  253. scitex/plt/io/_layered_bundle.py +1343 -0
  254. scitex/plt/styles/SCITEX_STYLE.yaml +26 -0
  255. scitex/plt/styles/__init__.py +14 -0
  256. scitex/plt/styles/presets.py +78 -0
  257. scitex/plt/utils/__init__.py +13 -1
  258. scitex/plt/utils/_collect_figure_metadata.py +10 -14
  259. scitex/plt/utils/_configure_mpl.py +6 -18
  260. scitex/plt/utils/_crop.py +32 -14
  261. scitex/plt/utils/_csv_column_naming.py +54 -0
  262. scitex/plt/utils/_figure_mm.py +116 -1
  263. scitex/plt/utils/_hitmap.py +1643 -0
  264. scitex/plt/utils/metadata/__init__.py +25 -0
  265. scitex/plt/utils/metadata/_core.py +9 -10
  266. scitex/plt/utils/metadata/_dimensions.py +6 -3
  267. scitex/plt/utils/metadata/_editable_export.py +405 -0
  268. scitex/plt/utils/metadata/_geometry_extraction.py +570 -0
  269. scitex/schema/__init__.py +109 -16
  270. scitex/schema/_canvas.py +1 -1
  271. scitex/schema/_plot.py +1015 -0
  272. scitex/schema/_stats.py +2 -2
  273. scitex/stats/__init__.py +117 -0
  274. scitex/stats/io/__init__.py +29 -0
  275. scitex/stats/io/_bundle.py +156 -0
  276. scitex/tex/__init__.py +4 -0
  277. scitex/tex/_export.py +890 -0
  278. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/METADATA +11 -1
  279. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/RECORD +294 -170
  280. scitex/io/memo.md +0 -2827
  281. scitex/plt/REQUESTS.md +0 -191
  282. scitex/plt/_subplots/TODO.md +0 -53
  283. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +0 -559
  284. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +0 -1609
  285. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +0 -447
  286. scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_between.json +0 -110
  287. scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_betweenx.json +0 -88
  288. scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fill_between.json +0 -103
  289. scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fillv.json +0 -106
  290. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/bar.json +0 -92
  291. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/barh.json +0 -92
  292. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/boxplot.json +0 -92
  293. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_bar.json +0 -84
  294. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_barh.json +0 -84
  295. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_box.json +0 -83
  296. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_boxplot.json +0 -93
  297. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violin.json +0 -91
  298. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violinplot.json +0 -91
  299. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/violinplot.json +0 -91
  300. scitex/plt/templates/research-master/scitex/vis/gallery/contour/contour.json +0 -97
  301. scitex/plt/templates/research-master/scitex/vis/gallery/contour/contourf.json +0 -98
  302. scitex/plt/templates/research-master/scitex/vis/gallery/contour/stx_contour.json +0 -84
  303. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist.json +0 -101
  304. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist2d.json +0 -96
  305. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_ecdf.json +0 -95
  306. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_joyplot.json +0 -95
  307. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_kde.json +0 -93
  308. scitex/plt/templates/research-master/scitex/vis/gallery/grid/imshow.json +0 -95
  309. scitex/plt/templates/research-master/scitex/vis/gallery/grid/matshow.json +0 -95
  310. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_conf_mat.json +0 -83
  311. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_heatmap.json +0 -92
  312. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_image.json +0 -121
  313. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_imshow.json +0 -84
  314. scitex/plt/templates/research-master/scitex/vis/gallery/line/plot.json +0 -110
  315. scitex/plt/templates/research-master/scitex/vis/gallery/line/step.json +0 -92
  316. scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_line.json +0 -95
  317. scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_shaded_line.json +0 -96
  318. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/hexbin.json +0 -95
  319. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/scatter.json +0 -95
  320. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stem.json +0 -92
  321. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stx_scatter.json +0 -84
  322. scitex/plt/templates/research-master/scitex/vis/gallery/special/pie.json +0 -94
  323. scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_raster.json +0 -109
  324. scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_rectangle.json +0 -108
  325. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/errorbar.json +0 -93
  326. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_errorbar.json +0 -84
  327. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_ci.json +0 -96
  328. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_std.json +0 -96
  329. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_median_iqr.json +0 -96
  330. scitex/plt/templates/research-master/scitex/vis/gallery/vector/quiver.json +0 -99
  331. scitex/plt/templates/research-master/scitex/vis/gallery/vector/streamplot.json +0 -100
  332. scitex/vis/__init__.py +0 -177
  333. scitex/vis/editor/_edit.py +0 -390
  334. scitex/vis/editor/flask_editor/_bbox.py +0 -529
  335. scitex/vis/editor/flask_editor/_core.py +0 -168
  336. scitex/vis/editor/flask_editor/_renderer.py +0 -393
  337. scitex/vis/editor/flask_editor/templates/__init__.py +0 -33
  338. scitex/vis/editor/flask_editor/templates/_html.py +0 -513
  339. scitex/vis/editor/flask_editor/templates/_scripts.py +0 -1261
  340. scitex/vis/editor/flask_editor/templates/_styles.py +0 -739
  341. /scitex/{vis → fig}/README.md +0 -0
  342. /scitex/{vis → fig}/backend/__init__.py +0 -0
  343. /scitex/{vis → fig}/backend/_export.py +0 -0
  344. /scitex/{vis → fig}/backend/_render.py +0 -0
  345. /scitex/{vis → fig}/docs/CANVAS_ARCHITECTURE.md +0 -0
  346. /scitex/{vis → fig}/editor/_flask_editor.py +0 -0
  347. /scitex/{vis → fig}/editor/flask_editor/__init__.py +0 -0
  348. /scitex/{vis → fig}/editor/flask_editor/_utils.py +0 -0
  349. /scitex/{vis → fig}/io/_directory.py +0 -0
  350. /scitex/{vis → fig}/model/_plot_types.py +0 -0
  351. /scitex/{vis → fig}/utils/_defaults.py +0 -0
  352. /scitex/{vis → fig}/utils/_validate.py +0 -0
  353. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/WHEEL +0 -0
  354. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/entry_points.txt +0 -0
  355. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,1261 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # File: ./src/scitex/vis/editor/flask_editor/templates/scripts.py
4
- """JavaScript for the Flask editor UI."""
5
-
6
- JS_SCRIPTS = """
7
- let overrides = {{ overrides|safe }};
8
- let traces = overrides.traces || [];
9
- let elementBboxes = {};
10
- let imgSize = {width: 0, height: 0};
11
- let hoveredElement = null;
12
- let selectedElement = null;
13
-
14
- // Cycle selection state for overlapping elements
15
- let elementsAtCursor = []; // All elements at current cursor position
16
- let currentCycleIndex = 0; // Current index in cycle
17
-
18
- // Unit system state (default: mm)
19
- let dimensionUnit = 'mm';
20
- const MM_TO_INCH = 1 / 25.4;
21
- const INCH_TO_MM = 25.4;
22
-
23
- // Hover system - client-side hit testing
24
- function initHoverSystem() {
25
- const container = document.getElementById('preview-container');
26
- const img = document.getElementById('preview-img');
27
-
28
- img.addEventListener('mousemove', (e) => {
29
- if (imgSize.width === 0 || imgSize.height === 0) return;
30
-
31
- const rect = img.getBoundingClientRect();
32
- const x = e.clientX - rect.left;
33
- const y = e.clientY - rect.top;
34
-
35
- const scaleX = imgSize.width / rect.width;
36
- const scaleY = imgSize.height / rect.height;
37
- const imgX = x * scaleX;
38
- const imgY = y * scaleY;
39
-
40
- const element = findElementAt(imgX, imgY);
41
- if (element !== hoveredElement) {
42
- hoveredElement = element;
43
- updateOverlay();
44
- }
45
- });
46
-
47
- img.addEventListener('mouseleave', () => {
48
- hoveredElement = null;
49
- updateOverlay();
50
- });
51
-
52
- img.addEventListener('click', (e) => {
53
- const rect = img.getBoundingClientRect();
54
- const x = e.clientX - rect.left;
55
- const y = e.clientY - rect.top;
56
- const scaleX = imgSize.width / rect.width;
57
- const scaleY = imgSize.height / rect.height;
58
- const imgX = x * scaleX;
59
- const imgY = y * scaleY;
60
-
61
- // Alt+click or find all overlapping elements
62
- if (e.altKey) {
63
- // Cycle through overlapping elements
64
- const allElements = findAllElementsAt(imgX, imgY);
65
- if (allElements.length > 0) {
66
- // If cursor moved to different location, reset cycle
67
- if (JSON.stringify(allElements) !== JSON.stringify(elementsAtCursor)) {
68
- elementsAtCursor = allElements;
69
- currentCycleIndex = 0;
70
- } else {
71
- // Cycle to next element
72
- currentCycleIndex = (currentCycleIndex + 1) % elementsAtCursor.length;
73
- }
74
- selectedElement = elementsAtCursor[currentCycleIndex];
75
- updateOverlay();
76
- scrollToSection(selectedElement);
77
-
78
- // Show cycle indicator in status
79
- const total = elementsAtCursor.length;
80
- const current = currentCycleIndex + 1;
81
- console.log(`Cycle selection: ${current}/${total} - ${selectedElement}`);
82
- }
83
- } else if (hoveredElement) {
84
- // Normal click - select hovered element
85
- selectedElement = hoveredElement;
86
- elementsAtCursor = []; // Reset cycle
87
- currentCycleIndex = 0;
88
- updateOverlay();
89
- scrollToSection(selectedElement);
90
- }
91
- });
92
-
93
- // Right-click for cycle selection menu
94
- img.addEventListener('contextmenu', (e) => {
95
- e.preventDefault();
96
-
97
- const rect = img.getBoundingClientRect();
98
- const x = e.clientX - rect.left;
99
- const y = e.clientY - rect.top;
100
- const scaleX = imgSize.width / rect.width;
101
- const scaleY = imgSize.height / rect.height;
102
- const imgX = x * scaleX;
103
- const imgY = y * scaleY;
104
-
105
- const allElements = findAllElementsAt(imgX, imgY);
106
- if (allElements.length > 1) {
107
- // Cycle to next element
108
- if (JSON.stringify(allElements) !== JSON.stringify(elementsAtCursor)) {
109
- elementsAtCursor = allElements;
110
- currentCycleIndex = 0;
111
- } else {
112
- currentCycleIndex = (currentCycleIndex + 1) % elementsAtCursor.length;
113
- }
114
- selectedElement = elementsAtCursor[currentCycleIndex];
115
- updateOverlay();
116
- scrollToSection(selectedElement);
117
-
118
- const total = elementsAtCursor.length;
119
- const current = currentCycleIndex + 1;
120
- console.log(`Right-click cycle: ${current}/${total} - ${selectedElement}`);
121
- } else if (allElements.length === 1) {
122
- selectedElement = allElements[0];
123
- updateOverlay();
124
- scrollToSection(selectedElement);
125
- }
126
- });
127
-
128
- img.addEventListener('load', () => {
129
- updateOverlay();
130
- });
131
- }
132
-
133
- function findElementAt(x, y) {
134
- // Multi-panel aware hit detection with specificity hierarchy:
135
- // 1. Data elements with points (lines, scatter) - proximity detection
136
- // 2. Small elements (labels, ticks, legends, bars, fills)
137
- // 3. Panel bboxes - lowest priority (fallback)
138
-
139
- const PROXIMITY_THRESHOLD = 15;
140
- const SCATTER_THRESHOLD = 20; // Larger threshold for scatter points
141
-
142
- // First: Check for data elements with points (lines, scatter)
143
- let closestDataElement = null;
144
- let minDistance = Infinity;
145
-
146
- for (const [name, bbox] of Object.entries(elementBboxes)) {
147
- if (bbox.points && bbox.points.length > 0) {
148
- // Check if cursor is within general bbox area first
149
- if (x >= bbox.x0 - SCATTER_THRESHOLD && x <= bbox.x1 + SCATTER_THRESHOLD &&
150
- y >= bbox.y0 - SCATTER_THRESHOLD && y <= bbox.y1 + SCATTER_THRESHOLD) {
151
-
152
- const elementType = bbox.element_type || 'line';
153
- let dist;
154
-
155
- if (elementType === 'scatter') {
156
- // For scatter, find distance to nearest point
157
- dist = distanceToNearestPoint(x, y, bbox.points);
158
- } else {
159
- // For lines, find distance to line segments
160
- dist = distanceToLine(x, y, bbox.points);
161
- }
162
-
163
- if (dist < minDistance) {
164
- minDistance = dist;
165
- closestDataElement = name;
166
- }
167
- }
168
- }
169
- }
170
-
171
- // Use appropriate threshold based on element type
172
- if (closestDataElement) {
173
- const bbox = elementBboxes[closestDataElement];
174
- const threshold = (bbox.element_type === 'scatter') ? SCATTER_THRESHOLD : PROXIMITY_THRESHOLD;
175
- if (minDistance <= threshold) {
176
- return closestDataElement;
177
- }
178
- }
179
-
180
- // Second: Collect all bbox matches, excluding panels and data elements with points
181
- const elementMatches = [];
182
- const panelMatches = [];
183
-
184
- for (const [name, bbox] of Object.entries(elementBboxes)) {
185
- if (x >= bbox.x0 && x <= bbox.x1 && y >= bbox.y0 && y <= bbox.y1) {
186
- const area = (bbox.x1 - bbox.x0) * (bbox.y1 - bbox.y0);
187
- const isPanel = bbox.is_panel || name.endsWith('_panel');
188
- const hasPoints = bbox.points && bbox.points.length > 0;
189
-
190
- if (hasPoints) {
191
- // Already handled above with proximity
192
- continue;
193
- } else if (isPanel) {
194
- panelMatches.push({name, area, bbox});
195
- } else {
196
- elementMatches.push({name, area, bbox});
197
- }
198
- }
199
- }
200
-
201
- // Return smallest non-panel element if any
202
- if (elementMatches.length > 0) {
203
- elementMatches.sort((a, b) => a.area - b.area);
204
- return elementMatches[0].name;
205
- }
206
-
207
- // Fallback to panel selection (useful for multi-panel figures)
208
- if (panelMatches.length > 0) {
209
- panelMatches.sort((a, b) => a.area - b.area);
210
- return panelMatches[0].name;
211
- }
212
-
213
- return null;
214
- }
215
-
216
- function distanceToNearestPoint(px, py, points) {
217
- // Find distance to nearest point in scatter
218
- let minDist = Infinity;
219
- for (const [x, y] of points) {
220
- const dist = Math.sqrt((px - x) ** 2 + (py - y) ** 2);
221
- if (dist < minDist) minDist = dist;
222
- }
223
- return minDist;
224
- }
225
-
226
- function distanceToLine(px, py, points) {
227
- let minDist = Infinity;
228
- for (let i = 0; i < points.length - 1; i++) {
229
- const [x1, y1] = points[i];
230
- const [x2, y2] = points[i + 1];
231
- const dist = distanceToSegment(px, py, x1, y1, x2, y2);
232
- if (dist < minDist) minDist = dist;
233
- }
234
- return minDist;
235
- }
236
-
237
- function distanceToSegment(px, py, x1, y1, x2, y2) {
238
- const dx = x2 - x1;
239
- const dy = y2 - y1;
240
- const lenSq = dx * dx + dy * dy;
241
-
242
- if (lenSq === 0) {
243
- return Math.sqrt((px - x1) ** 2 + (py - y1) ** 2);
244
- }
245
-
246
- let t = ((px - x1) * dx + (py - y1) * dy) / lenSq;
247
- t = Math.max(0, Math.min(1, t));
248
-
249
- const projX = x1 + t * dx;
250
- const projY = y1 + t * dy;
251
-
252
- return Math.sqrt((px - projX) ** 2 + (py - projY) ** 2);
253
- }
254
-
255
- function findAllElementsAt(x, y) {
256
- // Find all elements at cursor position (for cycle selection)
257
- // Returns array sorted by specificity (most specific first)
258
- const PROXIMITY_THRESHOLD = 15;
259
- const SCATTER_THRESHOLD = 20;
260
-
261
- const results = [];
262
-
263
- for (const [name, bbox] of Object.entries(elementBboxes)) {
264
- let match = false;
265
- let distance = Infinity;
266
- let priority = 0; // Lower = more specific
267
-
268
- const hasPoints = bbox.points && bbox.points.length > 0;
269
- const elementType = bbox.element_type || '';
270
- const isPanel = bbox.is_panel || name.endsWith('_panel');
271
-
272
- // Check data elements with points (lines, scatter)
273
- if (hasPoints) {
274
- if (x >= bbox.x0 - SCATTER_THRESHOLD && x <= bbox.x1 + SCATTER_THRESHOLD &&
275
- y >= bbox.y0 - SCATTER_THRESHOLD && y <= bbox.y1 + SCATTER_THRESHOLD) {
276
-
277
- if (elementType === 'scatter') {
278
- distance = distanceToNearestPoint(x, y, bbox.points);
279
- if (distance <= SCATTER_THRESHOLD) {
280
- match = true;
281
- priority = 1; // Scatter points = high priority
282
- }
283
- } else {
284
- distance = distanceToLine(x, y, bbox.points);
285
- if (distance <= PROXIMITY_THRESHOLD) {
286
- match = true;
287
- priority = 2; // Lines = high priority
288
- }
289
- }
290
- }
291
- }
292
-
293
- // Check bbox containment
294
- if (x >= bbox.x0 && x <= bbox.x1 && y >= bbox.y0 && y <= bbox.y1) {
295
- const area = (bbox.x1 - bbox.x0) * (bbox.y1 - bbox.y0);
296
-
297
- if (!match) {
298
- match = true;
299
- distance = 0;
300
- }
301
-
302
- if (isPanel) {
303
- priority = 100; // Panels = lowest priority
304
- } else if (!hasPoints) {
305
- // Small elements like labels, ticks - use area for priority
306
- priority = 10 + Math.min(area / 10000, 50);
307
- }
308
- }
309
-
310
- if (match) {
311
- results.push({ name, distance, priority, bbox });
312
- }
313
- }
314
-
315
- // Sort by priority (lower first), then by distance
316
- results.sort((a, b) => {
317
- if (a.priority !== b.priority) return a.priority - b.priority;
318
- return a.distance - b.distance;
319
- });
320
-
321
- return results.map(r => r.name);
322
- }
323
-
324
- function drawTracePath(bbox, scaleX, scaleY, type) {
325
- if (!bbox.points || bbox.points.length < 2) return '';
326
-
327
- const points = bbox.points;
328
- let pathD = `M ${points[0][0] * scaleX} ${points[0][1] * scaleY}`;
329
- for (let i = 1; i < points.length; i++) {
330
- pathD += ` L ${points[i][0] * scaleX} ${points[i][1] * scaleY}`;
331
- }
332
-
333
- const className = type === 'hover' ? 'hover-path' : 'selected-path';
334
- const labelX = points[0][0] * scaleX;
335
- const labelY = points[0][1] * scaleY - 8;
336
- const labelClass = type === 'hover' ? 'hover-label' : 'selected-label';
337
-
338
- return `<path class="${className}" d="${pathD}"/>` +
339
- `<text class="${labelClass}" x="${labelX}" y="${labelY}">${bbox.label}</text>`;
340
- }
341
-
342
- function drawScatterPoints(bbox, scaleX, scaleY, type) {
343
- // Draw scatter points as circles
344
- if (!bbox.points || bbox.points.length === 0) return '';
345
-
346
- const className = type === 'hover' ? 'hover-scatter' : 'selected-scatter';
347
- const labelClass = type === 'hover' ? 'hover-label' : 'selected-label';
348
- const radius = 4;
349
-
350
- let svg = '';
351
- for (const [x, y] of bbox.points) {
352
- svg += `<circle class="${className}" cx="${x * scaleX}" cy="${y * scaleY}" r="${radius}"/>`;
353
- }
354
-
355
- // Add label near first point
356
- if (bbox.points.length > 0) {
357
- const labelX = bbox.points[0][0] * scaleX;
358
- const labelY = bbox.points[0][1] * scaleY - 10;
359
- svg += `<text class="${labelClass}" x="${labelX}" y="${labelY}">${bbox.label}</text>`;
360
- }
361
-
362
- return svg;
363
- }
364
-
365
- function updateOverlay() {
366
- const overlay = document.getElementById('hover-overlay');
367
- const img = document.getElementById('preview-img');
368
- const rect = img.getBoundingClientRect();
369
-
370
- overlay.setAttribute('width', rect.width);
371
- overlay.setAttribute('height', rect.height);
372
-
373
- const scaleX = rect.width / imgSize.width;
374
- const scaleY = rect.height / imgSize.height;
375
-
376
- let svg = '';
377
-
378
- function drawElement(elementName, type) {
379
- const bbox = elementBboxes[elementName];
380
- if (!bbox) return '';
381
-
382
- const elementType = bbox.element_type || '';
383
- const hasPoints = bbox.points && bbox.points.length > 0;
384
-
385
- // Lines - draw as path
386
- if ((elementType === 'line' || elementName.includes('trace_')) && hasPoints) {
387
- return drawTracePath(bbox, scaleX, scaleY, type);
388
- }
389
- // Scatter - draw as circles
390
- else if (elementType === 'scatter' && hasPoints) {
391
- return drawScatterPoints(bbox, scaleX, scaleY, type);
392
- }
393
- // Default - draw bbox rectangle
394
- else {
395
- const rectClass = type === 'hover' ? 'hover-rect' : 'selected-rect';
396
- const labelClass = type === 'hover' ? 'hover-label' : 'selected-label';
397
- const x = bbox.x0 * scaleX - 2;
398
- const y = bbox.y0 * scaleY - 2;
399
- const w = (bbox.x1 - bbox.x0) * scaleX + 4;
400
- const h = (bbox.y1 - bbox.y0) * scaleY + 4;
401
- return `<rect class="${rectClass}" x="${x}" y="${y}" width="${w}" height="${h}" rx="2"/>` +
402
- `<text class="${labelClass}" x="${x}" y="${y - 4}">${bbox.label}</text>`;
403
- }
404
- }
405
-
406
- if (hoveredElement && hoveredElement !== selectedElement) {
407
- svg += drawElement(hoveredElement, 'hover');
408
- }
409
-
410
- if (selectedElement) {
411
- svg += drawElement(selectedElement, 'selected');
412
- }
413
-
414
- overlay.innerHTML = svg;
415
- }
416
-
417
- function expandSection(sectionId) {
418
- document.querySelectorAll('.section').forEach(section => {
419
- const header = section.querySelector('.section-header');
420
- const content = section.querySelector('.section-content');
421
- if (section.id === sectionId) {
422
- header?.classList.remove('collapsed');
423
- content?.classList.remove('collapsed');
424
- } else if (header?.classList.contains('section-toggle')) {
425
- header?.classList.add('collapsed');
426
- content?.classList.add('collapsed');
427
- }
428
- });
429
- }
430
-
431
- function scrollToSection(elementName) {
432
- const elementToSection = {
433
- 'title': 'section-labels',
434
- 'xlabel': 'section-labels',
435
- 'ylabel': 'section-labels',
436
- 'xaxis_ticks': 'section-ticks',
437
- 'yaxis_ticks': 'section-ticks',
438
- 'legend': 'section-legend'
439
- };
440
-
441
- const fieldMap = {
442
- 'title': 'title',
443
- 'xlabel': 'xlabel',
444
- 'ylabel': 'ylabel',
445
- 'xaxis_ticks': 'x_n_ticks',
446
- 'yaxis_ticks': 'y_n_ticks',
447
- 'legend': 'legend_visible'
448
- };
449
-
450
- if (elementName.startsWith('trace_')) {
451
- expandSection('section-traces');
452
- const traceIdx = elementBboxes[elementName]?.trace_idx;
453
- if (traceIdx !== undefined) {
454
- const traceColors = document.querySelectorAll('.trace-color');
455
- if (traceColors[traceIdx]) {
456
- setTimeout(() => {
457
- traceColors[traceIdx].scrollIntoView({behavior: 'smooth', block: 'center'});
458
- traceColors[traceIdx].click();
459
- }, 100);
460
- }
461
- }
462
- return;
463
- }
464
-
465
- const sectionId = elementToSection[elementName];
466
- if (sectionId) {
467
- expandSection(sectionId);
468
- }
469
-
470
- const fieldId = fieldMap[elementName];
471
- if (fieldId) {
472
- const field = document.getElementById(fieldId);
473
- if (field) {
474
- setTimeout(() => {
475
- field.scrollIntoView({behavior: 'smooth', block: 'center'});
476
- field.focus();
477
- }, 100);
478
- }
479
- }
480
-
481
- // Always show selected element panel
482
- showSelectedElementPanel(elementName);
483
- }
484
-
485
- // Selected element panel management
486
- function showSelectedElementPanel(elementName) {
487
- const section = document.getElementById('section-selected');
488
- const titleEl = document.getElementById('selected-element-title');
489
- const typeBadge = document.getElementById('element-type-badge');
490
- const axisInfo = document.getElementById('element-axis-info');
491
-
492
- // Hide all property sections first
493
- document.querySelectorAll('.element-props').forEach(el => el.style.display = 'none');
494
-
495
- if (!elementName) {
496
- section.style.display = 'none';
497
- return;
498
- }
499
-
500
- section.style.display = 'block';
501
-
502
- // Parse element name to extract type and info
503
- const elementInfo = parseElementName(elementName);
504
- const bbox = elementBboxes[elementName] || {};
505
-
506
- // Update title
507
- titleEl.textContent = `Selected: ${elementInfo.displayName}`;
508
-
509
- // Update type badge
510
- typeBadge.className = `element-type-badge ${elementInfo.type}`;
511
- typeBadge.textContent = elementInfo.type;
512
-
513
- // Update axis info
514
- if (elementInfo.axisId) {
515
- const row = elementInfo.axisId.match(/ax_(\\d)(\\d)/);
516
- if (row) {
517
- axisInfo.textContent = `Panel: Row ${parseInt(row[1])+1}, Col ${parseInt(row[2])+1}`;
518
- } else {
519
- axisInfo.textContent = `Axis: ${elementInfo.axisId}`;
520
- }
521
- } else {
522
- axisInfo.textContent = '';
523
- }
524
-
525
- // Show appropriate property panel and populate with current values
526
- showPropertiesForElement(elementInfo, bbox);
527
- }
528
-
529
- function parseElementName(name) {
530
- // Parse names like: ax_00_scatter_0, ax_11_trace_1, ax_01_xlabel, trace_0, xlabel, etc.
531
- const result = {
532
- original: name,
533
- type: 'unknown',
534
- displayName: name,
535
- axisId: null,
536
- index: null
537
- };
538
-
539
- // Check for axis prefix (ax_XX_)
540
- const axisMatch = name.match(/^(ax_\\d+)_(.+)$/);
541
- if (axisMatch) {
542
- result.axisId = axisMatch[1];
543
- name = axisMatch[2]; // Rest of the name
544
- }
545
-
546
- // Determine element type
547
- if (name.includes('scatter')) {
548
- result.type = 'scatter';
549
- const idx = name.match(/scatter_(\\d+)/);
550
- result.index = idx ? parseInt(idx[1]) : 0;
551
- result.displayName = `Scatter ${result.index + 1}`;
552
- } else if (name.includes('trace')) {
553
- result.type = 'trace';
554
- const idx = name.match(/trace_(\\d+)/);
555
- result.index = idx ? parseInt(idx[1]) : 0;
556
- result.displayName = `Line ${result.index + 1}`;
557
- } else if (name.includes('fill')) {
558
- result.type = 'fill';
559
- const idx = name.match(/fill_(\\d+)/);
560
- result.index = idx ? parseInt(idx[1]) : 0;
561
- result.displayName = `Fill Area ${result.index + 1}`;
562
- } else if (name.includes('bar')) {
563
- result.type = 'bar';
564
- const idx = name.match(/bar_(\\d+)/);
565
- result.index = idx ? parseInt(idx[1]) : 0;
566
- result.displayName = `Bar ${result.index + 1}`;
567
- } else if (name === 'xlabel' || name === 'ylabel' || name === 'title') {
568
- result.type = 'label';
569
- result.displayName = name.charAt(0).toUpperCase() + name.slice(1);
570
- } else if (name === 'legend') {
571
- result.type = 'legend';
572
- result.displayName = 'Legend';
573
- } else if (name.includes('panel')) {
574
- result.type = 'panel';
575
- result.displayName = 'Panel';
576
- }
577
-
578
- return result;
579
- }
580
-
581
- function showPropertiesForElement(elementInfo, bbox) {
582
- const type = elementInfo.type;
583
-
584
- if (type === 'trace') {
585
- const props = document.getElementById('selected-trace-props');
586
- props.style.display = 'block';
587
-
588
- // Try to get current values from overrides
589
- const traceOverrides = getTraceOverrides(elementInfo);
590
- if (traceOverrides) {
591
- document.getElementById('sel-trace-label').value = traceOverrides.label || '';
592
- document.getElementById('sel-trace-color').value = traceOverrides.color || '#1f77b4';
593
- document.getElementById('sel-trace-color-text').value = traceOverrides.color || '#1f77b4';
594
- document.getElementById('sel-trace-linewidth').value = traceOverrides.linewidth || 1.0;
595
- document.getElementById('sel-trace-linestyle').value = traceOverrides.linestyle || '-';
596
- document.getElementById('sel-trace-marker').value = traceOverrides.marker || '';
597
- document.getElementById('sel-trace-markersize').value = traceOverrides.markersize || 4;
598
- document.getElementById('sel-trace-alpha').value = traceOverrides.alpha || 1;
599
- }
600
- } else if (type === 'scatter') {
601
- const props = document.getElementById('selected-scatter-props');
602
- props.style.display = 'block';
603
-
604
- const scatterOverrides = getScatterOverrides(elementInfo);
605
- if (scatterOverrides) {
606
- document.getElementById('sel-scatter-color').value = scatterOverrides.color || '#1f77b4';
607
- document.getElementById('sel-scatter-color-text').value = scatterOverrides.color || '#1f77b4';
608
- document.getElementById('sel-scatter-size').value = scatterOverrides.size || 20;
609
- document.getElementById('sel-scatter-marker').value = scatterOverrides.marker || 'o';
610
- document.getElementById('sel-scatter-alpha').value = scatterOverrides.alpha || 0.7;
611
- document.getElementById('sel-scatter-edgecolor').value = scatterOverrides.edgecolor || '#000000';
612
- document.getElementById('sel-scatter-edgecolor-text').value = scatterOverrides.edgecolor || '#000000';
613
- }
614
- } else if (type === 'fill') {
615
- const props = document.getElementById('selected-fill-props');
616
- props.style.display = 'block';
617
-
618
- const fillOverrides = getFillOverrides(elementInfo);
619
- if (fillOverrides) {
620
- document.getElementById('sel-fill-color').value = fillOverrides.color || '#1f77b4';
621
- document.getElementById('sel-fill-color-text').value = fillOverrides.color || '#1f77b4';
622
- document.getElementById('sel-fill-alpha').value = fillOverrides.alpha || 0.3;
623
- }
624
- } else if (type === 'bar') {
625
- const props = document.getElementById('selected-bar-props');
626
- props.style.display = 'block';
627
- } else if (type === 'label') {
628
- const props = document.getElementById('selected-label-props');
629
- props.style.display = 'block';
630
-
631
- // Get label text from global overrides
632
- const labelName = elementInfo.displayName.toLowerCase();
633
- document.getElementById('sel-label-text').value = overrides[labelName] || '';
634
- document.getElementById('sel-label-fontsize').value = overrides.axis_fontsize || 7;
635
- } else if (type === 'panel') {
636
- const props = document.getElementById('selected-panel-props');
637
- props.style.display = 'block';
638
-
639
- // Load existing panel overrides
640
- const panelOverrides = getPanelOverrides(elementInfo);
641
- document.getElementById('sel-panel-title').value = panelOverrides.title || '';
642
- document.getElementById('sel-panel-xlabel').value = panelOverrides.xlabel || '';
643
- document.getElementById('sel-panel-ylabel').value = panelOverrides.ylabel || '';
644
- } else if (type === 'legend') {
645
- // For legend, expand the legend section instead
646
- expandSection('section-legend');
647
- }
648
- }
649
-
650
- function getTraceOverrides(elementInfo) {
651
- // Initialize element overrides storage if not exists
652
- if (!overrides.element_overrides) {
653
- overrides.element_overrides = {};
654
- }
655
-
656
- const key = elementInfo.original;
657
- if (!overrides.element_overrides[key]) {
658
- // Try to get from traces array
659
- if (traces[elementInfo.index]) {
660
- overrides.element_overrides[key] = { ...traces[elementInfo.index] };
661
- } else {
662
- overrides.element_overrides[key] = {};
663
- }
664
- }
665
- return overrides.element_overrides[key];
666
- }
667
-
668
- function getScatterOverrides(elementInfo) {
669
- if (!overrides.element_overrides) {
670
- overrides.element_overrides = {};
671
- }
672
- const key = elementInfo.original;
673
- if (!overrides.element_overrides[key]) {
674
- overrides.element_overrides[key] = {};
675
- }
676
- return overrides.element_overrides[key];
677
- }
678
-
679
- function getFillOverrides(elementInfo) {
680
- if (!overrides.element_overrides) {
681
- overrides.element_overrides = {};
682
- }
683
- const key = elementInfo.original;
684
- if (!overrides.element_overrides[key]) {
685
- overrides.element_overrides[key] = {};
686
- }
687
- return overrides.element_overrides[key];
688
- }
689
-
690
- function getPanelOverrides(elementInfo) {
691
- if (!overrides.element_overrides) {
692
- overrides.element_overrides = {};
693
- }
694
- const key = elementInfo.original;
695
- if (!overrides.element_overrides[key]) {
696
- overrides.element_overrides[key] = {};
697
- }
698
- return overrides.element_overrides[key];
699
- }
700
-
701
- function applySelectedElementChanges() {
702
- if (!selectedElement) return;
703
-
704
- const elementInfo = parseElementName(selectedElement);
705
- const type = elementInfo.type;
706
-
707
- if (!overrides.element_overrides) {
708
- overrides.element_overrides = {};
709
- }
710
-
711
- if (type === 'trace') {
712
- overrides.element_overrides[selectedElement] = {
713
- label: document.getElementById('sel-trace-label').value,
714
- color: document.getElementById('sel-trace-color').value,
715
- linewidth: parseFloat(document.getElementById('sel-trace-linewidth').value),
716
- linestyle: document.getElementById('sel-trace-linestyle').value,
717
- marker: document.getElementById('sel-trace-marker').value,
718
- markersize: parseFloat(document.getElementById('sel-trace-markersize').value),
719
- alpha: parseFloat(document.getElementById('sel-trace-alpha').value)
720
- };
721
- } else if (type === 'scatter') {
722
- overrides.element_overrides[selectedElement] = {
723
- color: document.getElementById('sel-scatter-color').value,
724
- size: parseFloat(document.getElementById('sel-scatter-size').value),
725
- marker: document.getElementById('sel-scatter-marker').value,
726
- alpha: parseFloat(document.getElementById('sel-scatter-alpha').value),
727
- edgecolor: document.getElementById('sel-scatter-edgecolor').value
728
- };
729
- } else if (type === 'fill') {
730
- overrides.element_overrides[selectedElement] = {
731
- color: document.getElementById('sel-fill-color').value,
732
- alpha: parseFloat(document.getElementById('sel-fill-alpha').value)
733
- };
734
- } else if (type === 'label') {
735
- const labelName = elementInfo.displayName.toLowerCase();
736
- overrides[labelName] = document.getElementById('sel-label-text').value;
737
- overrides.axis_fontsize = parseFloat(document.getElementById('sel-label-fontsize').value);
738
- } else if (type === 'bar') {
739
- overrides.element_overrides[selectedElement] = {
740
- facecolor: document.getElementById('sel-bar-facecolor').value,
741
- edgecolor: document.getElementById('sel-bar-edgecolor').value,
742
- alpha: parseFloat(document.getElementById('sel-bar-alpha').value)
743
- };
744
- } else if (type === 'panel') {
745
- // Panel-specific overrides (per-axis) including title, xlabel, ylabel
746
- overrides.element_overrides[selectedElement] = {
747
- title: document.getElementById('sel-panel-title').value,
748
- xlabel: document.getElementById('sel-panel-xlabel').value,
749
- ylabel: document.getElementById('sel-panel-ylabel').value,
750
- facecolor: document.getElementById('sel-panel-facecolor').value,
751
- transparent: document.getElementById('sel-panel-transparent').checked,
752
- grid: document.getElementById('sel-panel-grid').checked
753
- };
754
- }
755
-
756
- // Trigger update
757
- updatePreview();
758
- document.getElementById('status').textContent = `Applied changes to ${elementInfo.displayName}`;
759
- }
760
-
761
- // Sync color inputs
762
- function setupColorSync(colorId, textId) {
763
- const colorInput = document.getElementById(colorId);
764
- const textInput = document.getElementById(textId);
765
- if (colorInput && textInput) {
766
- colorInput.addEventListener('input', () => {
767
- textInput.value = colorInput.value;
768
- });
769
- textInput.addEventListener('input', () => {
770
- if (/^#[0-9A-Fa-f]{6}$/.test(textInput.value)) {
771
- colorInput.value = textInput.value;
772
- }
773
- });
774
- }
775
- }
776
-
777
- // Theme management
778
- function toggleTheme() {
779
- const html = document.documentElement;
780
- const current = html.getAttribute('data-theme');
781
- const next = current === 'dark' ? 'light' : 'dark';
782
- html.setAttribute('data-theme', next);
783
- document.getElementById('theme-icon').innerHTML = next === 'dark' ? '&#9790;' : '&#9788;';
784
- localStorage.setItem('scitex-editor-theme', next);
785
- }
786
-
787
- // Collapsible sections
788
- function toggleSection(header) {
789
- header.classList.toggle('collapsed');
790
- const content = header.nextElementSibling;
791
- content.classList.toggle('collapsed');
792
- }
793
-
794
- function toggleCustomLegendPosition() {
795
- const legendLoc = document.getElementById('legend_loc').value;
796
- const customCoordsDiv = document.getElementById('custom-legend-coords');
797
- customCoordsDiv.style.display = legendLoc === 'custom' ? 'flex' : 'none';
798
- }
799
-
800
- // Dimension unit toggle
801
- function setDimensionUnit(unit) {
802
- if (unit === dimensionUnit) return;
803
-
804
- const widthInput = document.getElementById('fig_width');
805
- const heightInput = document.getElementById('fig_height');
806
- const widthLabel = document.getElementById('fig_width_label');
807
- const heightLabel = document.getElementById('fig_height_label');
808
- const mmBtn = document.getElementById('unit-mm');
809
- const inchBtn = document.getElementById('unit-inch');
810
-
811
- // Get current values
812
- let width = parseFloat(widthInput.value) || 0;
813
- let height = parseFloat(heightInput.value) || 0;
814
-
815
- // Convert values
816
- if (unit === 'mm' && dimensionUnit === 'inch') {
817
- // inch to mm
818
- width = Math.round(width * INCH_TO_MM * 10) / 10;
819
- height = Math.round(height * INCH_TO_MM * 10) / 10;
820
- widthInput.min = 10;
821
- widthInput.max = 300;
822
- widthInput.step = 1;
823
- heightInput.min = 10;
824
- heightInput.max = 300;
825
- heightInput.step = 1;
826
- } else if (unit === 'inch' && dimensionUnit === 'mm') {
827
- // mm to inch
828
- width = Math.round(width * MM_TO_INCH * 100) / 100;
829
- height = Math.round(height * MM_TO_INCH * 100) / 100;
830
- widthInput.min = 0.5;
831
- widthInput.max = 12;
832
- widthInput.step = 0.05;
833
- heightInput.min = 0.5;
834
- heightInput.max = 12;
835
- heightInput.step = 0.05;
836
- }
837
-
838
- // Update values and labels
839
- widthInput.value = width;
840
- heightInput.value = height;
841
- widthLabel.textContent = `Width (${unit})`;
842
- heightLabel.textContent = `Height (${unit})`;
843
-
844
- // Update button states
845
- if (unit === 'mm') {
846
- mmBtn.classList.add('active');
847
- inchBtn.classList.remove('active');
848
- } else {
849
- mmBtn.classList.remove('active');
850
- inchBtn.classList.add('active');
851
- }
852
-
853
- dimensionUnit = unit;
854
- }
855
-
856
- // Background type management
857
- let backgroundType = 'transparent';
858
- let initializingBackground = true; // Flag to prevent updates during init
859
-
860
- function setBackgroundType(type) {
861
- backgroundType = type;
862
-
863
- // Update hidden inputs for collectOverrides
864
- const facecolorInput = document.getElementById('facecolor');
865
- const transparentInput = document.getElementById('transparent');
866
-
867
- if (type === 'white') {
868
- facecolorInput.value = '#ffffff';
869
- transparentInput.value = 'false';
870
- } else if (type === 'black') {
871
- facecolorInput.value = '#000000';
872
- transparentInput.value = 'false';
873
- } else {
874
- // transparent
875
- facecolorInput.value = '#ffffff';
876
- transparentInput.value = 'true';
877
- }
878
-
879
- // Update button states
880
- document.querySelectorAll('.bg-btn').forEach(btn => btn.classList.remove('active'));
881
- document.getElementById(`bg-${type}`).classList.add('active');
882
-
883
- // Trigger update only after initialization
884
- if (!initializingBackground) {
885
- scheduleUpdate();
886
- }
887
- }
888
-
889
- // Get figure dimensions in inches (for matplotlib)
890
- function getFigSizeInches() {
891
- let width = parseFloat(document.getElementById('fig_width').value) || 80;
892
- let height = parseFloat(document.getElementById('fig_height').value) || 68;
893
-
894
- if (dimensionUnit === 'mm') {
895
- width = width * MM_TO_INCH;
896
- height = height * MM_TO_INCH;
897
- }
898
-
899
- return [Math.round(width * 100) / 100, Math.round(height * 100) / 100];
900
- }
901
-
902
- // Initialize fields
903
- document.addEventListener('DOMContentLoaded', () => {
904
- // Load saved theme
905
- const savedTheme = localStorage.getItem('scitex-editor-theme');
906
- if (savedTheme) {
907
- document.documentElement.setAttribute('data-theme', savedTheme);
908
- document.getElementById('theme-icon').innerHTML = savedTheme === 'dark' ? '&#9790;' : '&#9788;';
909
- }
910
-
911
- // Labels
912
- if (overrides.title) document.getElementById('title').value = overrides.title;
913
- if (overrides.xlabel) document.getElementById('xlabel').value = overrides.xlabel;
914
- if (overrides.ylabel) document.getElementById('ylabel').value = overrides.ylabel;
915
-
916
- // Axis limits
917
- if (overrides.xlim) {
918
- document.getElementById('xmin').value = overrides.xlim[0];
919
- document.getElementById('xmax').value = overrides.xlim[1];
920
- }
921
- if (overrides.ylim) {
922
- document.getElementById('ymin').value = overrides.ylim[0];
923
- document.getElementById('ymax').value = overrides.ylim[1];
924
- }
925
-
926
- // Traces
927
- document.getElementById('linewidth').value = overrides.linewidth || 1.0;
928
- updateTracesList();
929
-
930
- // Legend
931
- document.getElementById('legend_visible').checked = overrides.legend_visible !== false;
932
- document.getElementById('legend_loc').value = overrides.legend_loc || 'best';
933
- document.getElementById('legend_frameon').checked = overrides.legend_frameon || false;
934
- document.getElementById('legend_fontsize').value = overrides.legend_fontsize || 6;
935
- document.getElementById('legend_x').value = overrides.legend_x !== undefined ? overrides.legend_x : 0.5;
936
- document.getElementById('legend_y').value = overrides.legend_y !== undefined ? overrides.legend_y : 0.5;
937
- toggleCustomLegendPosition();
938
-
939
- // Ticks
940
- document.getElementById('x_n_ticks').value = overrides.x_n_ticks || overrides.n_ticks || 4;
941
- document.getElementById('y_n_ticks').value = overrides.y_n_ticks || overrides.n_ticks || 4;
942
- document.getElementById('hide_x_ticks').checked = overrides.hide_x_ticks || false;
943
- document.getElementById('hide_y_ticks').checked = overrides.hide_y_ticks || false;
944
- document.getElementById('tick_fontsize').value = overrides.tick_fontsize || 7;
945
- document.getElementById('tick_length').value = overrides.tick_length || 0.8;
946
- document.getElementById('tick_width').value = overrides.tick_width || 0.2;
947
- document.getElementById('tick_direction').value = overrides.tick_direction || 'out';
948
-
949
- // Style
950
- document.getElementById('grid').checked = overrides.grid || false;
951
- document.getElementById('hide_top_spine').checked = overrides.hide_top_spine !== false;
952
- document.getElementById('hide_right_spine').checked = overrides.hide_right_spine !== false;
953
- document.getElementById('axis_width').value = overrides.axis_width || 0.2;
954
- document.getElementById('axis_fontsize').value = overrides.axis_fontsize || 7;
955
- // Initialize background type from overrides
956
- const isTransparent = overrides.transparent !== false;
957
- const facecolor = overrides.facecolor || '#ffffff';
958
- document.getElementById('facecolor').value = facecolor;
959
-
960
- if (isTransparent) {
961
- setBackgroundType('transparent');
962
- } else if (facecolor === '#000000') {
963
- setBackgroundType('black');
964
- } else {
965
- setBackgroundType('white');
966
- }
967
-
968
- // Dimensions (convert from inches in metadata to mm by default)
969
- if (overrides.fig_size) {
970
- // fig_size is in inches in the JSON - convert to mm for default display
971
- const widthMm = Math.round(overrides.fig_size[0] * INCH_TO_MM);
972
- const heightMm = Math.round(overrides.fig_size[1] * INCH_TO_MM);
973
- document.getElementById('fig_width').value = widthMm;
974
- document.getElementById('fig_height').value = heightMm;
975
- }
976
- document.getElementById('dpi').value = overrides.dpi || 300;
977
- // Default unit is mm, which is already set in HTML and JS state
978
-
979
- // Note: facecolor is now managed by background toggle buttons (white/transparent/black)
980
- // No text input sync needed
981
-
982
- updateAnnotationsList();
983
- updatePreview();
984
- initHoverSystem();
985
- setAutoUpdateInterval();
986
-
987
- // Setup color sync for selected element property inputs
988
- setupColorSync('sel-trace-color', 'sel-trace-color-text');
989
- setupColorSync('sel-scatter-color', 'sel-scatter-color-text');
990
- setupColorSync('sel-scatter-edgecolor', 'sel-scatter-edgecolor-text');
991
- setupColorSync('sel-fill-color', 'sel-fill-color-text');
992
- setupColorSync('sel-bar-facecolor', 'sel-bar-facecolor-text');
993
- setupColorSync('sel-bar-edgecolor', 'sel-bar-edgecolor-text');
994
- setupColorSync('sel-panel-facecolor', 'sel-panel-facecolor-text');
995
-
996
- // Mark initialization complete - now background changes will trigger updates
997
- initializingBackground = false;
998
- });
999
-
1000
- // Traces list management
1001
- function updateTracesList() {
1002
- const list = document.getElementById('traces-list');
1003
- if (!traces || traces.length === 0) {
1004
- list.innerHTML = '<div style="padding: 10px; color: var(--text-muted); font-size: 0.85em;">No traces found in metadata</div>';
1005
- return;
1006
- }
1007
-
1008
- list.innerHTML = traces.map((t, i) => `
1009
- <div class="trace-item">
1010
- <input type="color" class="trace-color" value="${t.color || '#1f77b4'}"
1011
- onchange="updateTraceColor(${i}, this.value)">
1012
- <span class="trace-label">${t.label || t.id || 'Trace ' + (i+1)}</span>
1013
- <div class="trace-style">
1014
- <select onchange="updateTraceStyle(${i}, this.value)">
1015
- <option value="-" ${t.linestyle === '-' ? 'selected' : ''}>Solid</option>
1016
- <option value="--" ${t.linestyle === '--' ? 'selected' : ''}>Dashed</option>
1017
- <option value=":" ${t.linestyle === ':' ? 'selected' : ''}>Dotted</option>
1018
- <option value="-." ${t.linestyle === '-.' ? 'selected' : ''}>Dash-dot</option>
1019
- </select>
1020
- </div>
1021
- </div>
1022
- `).join('');
1023
- }
1024
-
1025
- function updateTraceColor(idx, color) {
1026
- if (traces[idx]) {
1027
- traces[idx].color = color;
1028
- scheduleUpdate();
1029
- }
1030
- }
1031
-
1032
- function updateTraceStyle(idx, style) {
1033
- if (traces[idx]) {
1034
- traces[idx].linestyle = style;
1035
- scheduleUpdate();
1036
- }
1037
- }
1038
-
1039
- function collectOverrides() {
1040
- const o = {};
1041
-
1042
- // Labels
1043
- const title = document.getElementById('title').value;
1044
- const xlabel = document.getElementById('xlabel').value;
1045
- const ylabel = document.getElementById('ylabel').value;
1046
- if (title) o.title = title;
1047
- if (xlabel) o.xlabel = xlabel;
1048
- if (ylabel) o.ylabel = ylabel;
1049
-
1050
- // Axis limits
1051
- const xmin = document.getElementById('xmin').value;
1052
- const xmax = document.getElementById('xmax').value;
1053
- if (xmin !== '' && xmax !== '') o.xlim = [parseFloat(xmin), parseFloat(xmax)];
1054
-
1055
- const ymin = document.getElementById('ymin').value;
1056
- const ymax = document.getElementById('ymax').value;
1057
- if (ymin !== '' && ymax !== '') o.ylim = [parseFloat(ymin), parseFloat(ymax)];
1058
-
1059
- // Traces
1060
- o.linewidth = parseFloat(document.getElementById('linewidth').value) || 1.0;
1061
- o.traces = traces;
1062
-
1063
- // Legend
1064
- o.legend_visible = document.getElementById('legend_visible').checked;
1065
- o.legend_loc = document.getElementById('legend_loc').value;
1066
- o.legend_frameon = document.getElementById('legend_frameon').checked;
1067
- o.legend_fontsize = parseInt(document.getElementById('legend_fontsize').value) || 6;
1068
- o.legend_x = parseFloat(document.getElementById('legend_x').value) || 0.5;
1069
- o.legend_y = parseFloat(document.getElementById('legend_y').value) || 0.5;
1070
-
1071
- // Ticks
1072
- o.x_n_ticks = parseInt(document.getElementById('x_n_ticks').value) || 4;
1073
- o.y_n_ticks = parseInt(document.getElementById('y_n_ticks').value) || 4;
1074
- o.hide_x_ticks = document.getElementById('hide_x_ticks').checked;
1075
- o.hide_y_ticks = document.getElementById('hide_y_ticks').checked;
1076
- o.tick_fontsize = parseInt(document.getElementById('tick_fontsize').value) || 7;
1077
- o.tick_length = parseFloat(document.getElementById('tick_length').value) || 0.8;
1078
- o.tick_width = parseFloat(document.getElementById('tick_width').value) || 0.2;
1079
- o.tick_direction = document.getElementById('tick_direction').value;
1080
-
1081
- // Style
1082
- o.grid = document.getElementById('grid').checked;
1083
- o.hide_top_spine = document.getElementById('hide_top_spine').checked;
1084
- o.hide_right_spine = document.getElementById('hide_right_spine').checked;
1085
- o.axis_width = parseFloat(document.getElementById('axis_width').value) || 0.2;
1086
- o.axis_fontsize = parseInt(document.getElementById('axis_fontsize').value) || 7;
1087
- o.facecolor = document.getElementById('facecolor').value;
1088
- o.transparent = document.getElementById('transparent').value === 'true';
1089
-
1090
- // Dimensions (always in inches for matplotlib)
1091
- o.fig_size = getFigSizeInches();
1092
- o.dpi = parseInt(document.getElementById('dpi').value) || 300;
1093
-
1094
- // Annotations
1095
- o.annotations = overrides.annotations || [];
1096
-
1097
- // Element-specific overrides (per-element styles)
1098
- if (overrides.element_overrides) {
1099
- o.element_overrides = overrides.element_overrides;
1100
- }
1101
-
1102
- return o;
1103
- }
1104
-
1105
- async function updatePreview() {
1106
- setStatus('Updating...', false);
1107
- overrides = collectOverrides();
1108
- try {
1109
- const resp = await fetch('/update', {
1110
- method: 'POST',
1111
- headers: {'Content-Type': 'application/json'},
1112
- body: JSON.stringify({overrides})
1113
- });
1114
- const data = await resp.json();
1115
- document.getElementById('preview-img').src = 'data:image/png;base64,' + data.image;
1116
-
1117
- if (data.bboxes) {
1118
- elementBboxes = data.bboxes;
1119
- }
1120
- if (data.img_size) {
1121
- imgSize = data.img_size;
1122
- }
1123
-
1124
- selectedElement = null;
1125
- hoveredElement = null;
1126
- updateOverlay();
1127
-
1128
- setStatus('Preview updated', false);
1129
- } catch (e) {
1130
- setStatus('Error: ' + e.message, true);
1131
- }
1132
- }
1133
-
1134
- async function saveManual() {
1135
- setStatus('Saving...', false);
1136
- try {
1137
- const resp = await fetch('/save', {
1138
- method: 'POST',
1139
- headers: {'Content-Type': 'application/json'}
1140
- });
1141
- const data = await resp.json();
1142
- if (data.status === 'saved') {
1143
- setStatus('Saved: ' + data.path.split('/').pop(), false);
1144
- } else {
1145
- setStatus('Error: ' + data.message, true);
1146
- }
1147
- } catch (e) {
1148
- setStatus('Error: ' + e.message, true);
1149
- }
1150
- }
1151
-
1152
- function resetOverrides() {
1153
- if (confirm('Reset all changes to original values?')) {
1154
- location.reload();
1155
- }
1156
- }
1157
-
1158
- function addAnnotation() {
1159
- const text = document.getElementById('annot-text').value;
1160
- if (!text) return;
1161
- const x = parseFloat(document.getElementById('annot-x').value) || 0.5;
1162
- const y = parseFloat(document.getElementById('annot-y').value) || 0.5;
1163
- const size = parseInt(document.getElementById('annot-size').value) || 8;
1164
- if (!overrides.annotations) overrides.annotations = [];
1165
- overrides.annotations.push({type: 'text', text, x, y, fontsize: size});
1166
- document.getElementById('annot-text').value = '';
1167
- updateAnnotationsList();
1168
- updatePreview();
1169
- }
1170
-
1171
- function removeAnnotation(idx) {
1172
- overrides.annotations.splice(idx, 1);
1173
- updateAnnotationsList();
1174
- updatePreview();
1175
- }
1176
-
1177
- function updateAnnotationsList() {
1178
- const list = document.getElementById('annotations-list');
1179
- const annotations = overrides.annotations || [];
1180
- if (annotations.length === 0) {
1181
- list.innerHTML = '';
1182
- return;
1183
- }
1184
- list.innerHTML = annotations.map((a, i) =>
1185
- `<div class="annotation-item">
1186
- <span>${a.text.substring(0, 25)}${a.text.length > 25 ? '...' : ''} (${a.x.toFixed(2)}, ${a.y.toFixed(2)})</span>
1187
- <button onclick="removeAnnotation(${i})">Remove</button>
1188
- </div>`
1189
- ).join('');
1190
- }
1191
-
1192
- function setStatus(msg, isError = false) {
1193
- const el = document.getElementById('status');
1194
- el.textContent = msg;
1195
- el.classList.toggle('error', isError);
1196
- }
1197
-
1198
- // Debounced auto-update
1199
- let updateTimer = null;
1200
- const DEBOUNCE_DELAY = 500;
1201
-
1202
- function scheduleUpdate() {
1203
- if (updateTimer) clearTimeout(updateTimer);
1204
- updateTimer = setTimeout(() => {
1205
- updatePreview();
1206
- }, DEBOUNCE_DELAY);
1207
- }
1208
-
1209
- // Auto-update on input changes
1210
- document.querySelectorAll('input[type="text"], input[type="number"]').forEach(el => {
1211
- el.addEventListener('input', scheduleUpdate);
1212
- el.addEventListener('keypress', (e) => {
1213
- if (e.key === 'Enter') {
1214
- if (updateTimer) clearTimeout(updateTimer);
1215
- updatePreview();
1216
- }
1217
- });
1218
- });
1219
-
1220
- document.querySelectorAll('input[type="checkbox"], select').forEach(el => {
1221
- el.addEventListener('change', () => {
1222
- if (updateTimer) clearTimeout(updateTimer);
1223
- updatePreview();
1224
- });
1225
- });
1226
-
1227
- document.querySelectorAll('input[type="color"]').forEach(el => {
1228
- el.addEventListener('change', () => {
1229
- if (updateTimer) clearTimeout(updateTimer);
1230
- updatePreview();
1231
- });
1232
- });
1233
-
1234
- // Ctrl+S keyboard shortcut to save
1235
- document.addEventListener('keydown', (e) => {
1236
- if ((e.ctrlKey || e.metaKey) && e.key === 's') {
1237
- e.preventDefault();
1238
- saveManual();
1239
- }
1240
- });
1241
-
1242
- // Auto-update interval system
1243
- let autoUpdateIntervalId = null;
1244
-
1245
- function setAutoUpdateInterval() {
1246
- if (autoUpdateIntervalId) {
1247
- clearInterval(autoUpdateIntervalId);
1248
- autoUpdateIntervalId = null;
1249
- }
1250
-
1251
- const intervalMs = parseInt(document.getElementById('auto_update_interval').value);
1252
- if (intervalMs > 0) {
1253
- autoUpdateIntervalId = setInterval(() => {
1254
- updatePreview();
1255
- }, intervalMs);
1256
- }
1257
- }
1258
- """
1259
-
1260
-
1261
- # EOF