scitex 2.5.0__py3-none-any.whl → 2.7.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 (1091) hide show
  1. scitex/__init__.py +19 -8
  2. scitex/__main__.py +2 -1
  3. scitex/__version__.py +1 -1
  4. scitex/_optional_deps.py +13 -20
  5. scitex/ai/__init__.py +5 -0
  6. scitex/ai/_gen_ai/_Anthropic.py +3 -1
  7. scitex/ai/_gen_ai/_BaseGenAI.py +3 -2
  8. scitex/ai/_gen_ai/_DeepSeek.py +1 -1
  9. scitex/ai/_gen_ai/_Google.py +3 -2
  10. scitex/ai/_gen_ai/_Llama.py +4 -2
  11. scitex/ai/_gen_ai/_OpenAI.py +3 -1
  12. scitex/ai/_gen_ai/_PARAMS.py +1 -0
  13. scitex/ai/_gen_ai/_Perplexity.py +3 -1
  14. scitex/ai/_gen_ai/__init__.py +1 -0
  15. scitex/ai/_gen_ai/_format_output_func.py +3 -1
  16. scitex/ai/classification/CrossValidationExperiment.py +8 -14
  17. scitex/ai/classification/examples/timeseries_cv_demo.py +128 -112
  18. scitex/ai/classification/reporters/_BaseClassificationReporter.py +2 -0
  19. scitex/ai/classification/reporters/_ClassificationReporter.py +30 -45
  20. scitex/ai/classification/reporters/_MultiClassificationReporter.py +8 -11
  21. scitex/ai/classification/reporters/_SingleClassificationReporter.py +126 -182
  22. scitex/ai/classification/reporters/__init__.py +1 -1
  23. scitex/ai/classification/reporters/reporter_utils/_Plotter.py +213 -119
  24. scitex/ai/classification/reporters/reporter_utils/__init__.py +28 -36
  25. scitex/ai/classification/reporters/reporter_utils/aggregation.py +125 -143
  26. scitex/ai/classification/reporters/reporter_utils/data_models.py +128 -120
  27. scitex/ai/classification/reporters/reporter_utils/reporting.py +507 -340
  28. scitex/ai/classification/reporters/reporter_utils/storage.py +4 -1
  29. scitex/ai/classification/reporters/reporter_utils/validation.py +141 -154
  30. scitex/ai/classification/timeseries/_TimeSeriesBlockingSplit.py +204 -129
  31. scitex/ai/classification/timeseries/_TimeSeriesCalendarSplit.py +215 -171
  32. scitex/ai/classification/timeseries/_TimeSeriesMetadata.py +17 -17
  33. scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +67 -143
  34. scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit_v01-not-using-n_splits.py +67 -143
  35. scitex/ai/classification/timeseries/_TimeSeriesStrategy.py +12 -13
  36. scitex/ai/classification/timeseries/_TimeSeriesStratifiedSplit.py +231 -144
  37. scitex/ai/classification/timeseries/__init__.py +2 -4
  38. scitex/ai/classification/timeseries/_normalize_timestamp.py +3 -0
  39. scitex/ai/clustering/_pca.py +0 -1
  40. scitex/ai/clustering/_umap.py +1 -2
  41. scitex/ai/feature_extraction/__init__.py +10 -8
  42. scitex/ai/feature_extraction/vit.py +0 -1
  43. scitex/ai/feature_selection/feature_selection.py +3 -8
  44. scitex/ai/metrics/_calc_conf_mat.py +2 -0
  45. scitex/ai/metrics/_calc_feature_importance.py +3 -7
  46. scitex/ai/metrics/_calc_pre_rec_auc.py +5 -5
  47. scitex/ai/metrics/_calc_roc_auc.py +4 -2
  48. scitex/ai/metrics/_calc_seizure_prediction_metrics.py +35 -20
  49. scitex/ai/metrics/_calc_silhouette_score.py +1 -3
  50. scitex/ai/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger.py +0 -3
  51. scitex/ai/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger2020.py +0 -3
  52. scitex/ai/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger913A.py +0 -3
  53. scitex/ai/optim/_optimizers.py +1 -1
  54. scitex/ai/plt/__init__.py +6 -1
  55. scitex/ai/plt/_plot_feature_importance.py +1 -3
  56. scitex/ai/plt/_plot_learning_curve.py +9 -24
  57. scitex/ai/plt/_plot_optuna_study.py +4 -3
  58. scitex/ai/plt/_plot_pre_rec_curve.py +9 -15
  59. scitex/ai/plt/_plot_roc_curve.py +6 -8
  60. scitex/ai/plt/_stx_conf_mat.py +121 -122
  61. scitex/ai/sampling/undersample.py +3 -2
  62. scitex/ai/sklearn/__init__.py +2 -2
  63. scitex/ai/training/_LearningCurveLogger.py +23 -10
  64. scitex/ai/utils/_check_params.py +0 -1
  65. scitex/benchmark/__init__.py +15 -25
  66. scitex/benchmark/benchmark.py +124 -117
  67. scitex/benchmark/monitor.py +117 -107
  68. scitex/benchmark/profiler.py +61 -58
  69. scitex/bridge/__init__.py +110 -0
  70. scitex/bridge/_helpers.py +149 -0
  71. scitex/bridge/_plt_vis.py +529 -0
  72. scitex/bridge/_protocol.py +283 -0
  73. scitex/bridge/_stats_plt.py +261 -0
  74. scitex/bridge/_stats_vis.py +265 -0
  75. scitex/browser/__init__.py +0 -2
  76. scitex/browser/auth/__init__.py +0 -0
  77. scitex/browser/auth/google.py +16 -11
  78. scitex/browser/automation/CookieHandler.py +2 -3
  79. scitex/browser/collaboration/__init__.py +3 -0
  80. scitex/browser/collaboration/auth_helpers.py +3 -1
  81. scitex/browser/collaboration/collaborative_agent.py +2 -0
  82. scitex/browser/collaboration/interactive_panel.py +2 -2
  83. scitex/browser/collaboration/shared_session.py +20 -11
  84. scitex/browser/collaboration/standard_interactions.py +1 -0
  85. scitex/browser/core/BrowserMixin.py +12 -30
  86. scitex/browser/core/ChromeProfileManager.py +9 -24
  87. scitex/browser/debugging/_browser_logger.py +15 -25
  88. scitex/browser/debugging/_failure_capture.py +9 -2
  89. scitex/browser/debugging/_highlight_element.py +15 -6
  90. scitex/browser/debugging/_show_grid.py +5 -6
  91. scitex/browser/debugging/_sync_session.py +4 -3
  92. scitex/browser/debugging/_test_monitor.py +14 -5
  93. scitex/browser/debugging/_visual_cursor.py +46 -35
  94. scitex/browser/interaction/click_center.py +4 -3
  95. scitex/browser/interaction/click_with_fallbacks.py +7 -10
  96. scitex/browser/interaction/close_popups.py +79 -66
  97. scitex/browser/interaction/fill_with_fallbacks.py +8 -8
  98. scitex/browser/pdf/__init__.py +3 -1
  99. scitex/browser/pdf/click_download_for_chrome_pdf_viewer.py +11 -10
  100. scitex/browser/pdf/detect_chrome_pdf_viewer.py +3 -6
  101. scitex/browser/remote/CaptchaHandler.py +109 -96
  102. scitex/browser/remote/ZenRowsAPIClient.py +91 -97
  103. scitex/browser/remote/ZenRowsBrowserManager.py +138 -112
  104. scitex/browser/stealth/HumanBehavior.py +4 -9
  105. scitex/browser/stealth/StealthManager.py +11 -26
  106. scitex/capture/__init__.py +17 -17
  107. scitex/capture/__main__.py +2 -3
  108. scitex/capture/capture.py +23 -51
  109. scitex/capture/cli.py +14 -39
  110. scitex/capture/gif.py +5 -9
  111. scitex/capture/mcp_server.py +7 -20
  112. scitex/capture/session.py +4 -3
  113. scitex/capture/utils.py +18 -53
  114. scitex/cli/__init__.py +1 -1
  115. scitex/cli/cloud.py +158 -116
  116. scitex/cli/config.py +224 -0
  117. scitex/cli/main.py +41 -40
  118. scitex/cli/scholar.py +60 -27
  119. scitex/cli/security.py +14 -20
  120. scitex/cli/web.py +87 -90
  121. scitex/cli/writer.py +51 -45
  122. scitex/cloud/__init__.py +14 -11
  123. scitex/cloud/_matplotlib_hook.py +6 -6
  124. scitex/config/README.md +313 -0
  125. scitex/config/{PriorityConfig.py → _PriorityConfig.py} +114 -17
  126. scitex/config/_ScitexConfig.py +319 -0
  127. scitex/config/__init__.py +41 -9
  128. scitex/config/_paths.py +325 -0
  129. scitex/config/default.yaml +81 -0
  130. scitex/context/_suppress_output.py +2 -3
  131. scitex/db/_BaseMixins/_BaseBackupMixin.py +3 -1
  132. scitex/db/_BaseMixins/_BaseBatchMixin.py +3 -1
  133. scitex/db/_BaseMixins/_BaseBlobMixin.py +3 -1
  134. scitex/db/_BaseMixins/_BaseImportExportMixin.py +1 -3
  135. scitex/db/_BaseMixins/_BaseIndexMixin.py +3 -1
  136. scitex/db/_BaseMixins/_BaseMaintenanceMixin.py +1 -3
  137. scitex/db/_BaseMixins/_BaseQueryMixin.py +3 -1
  138. scitex/db/_BaseMixins/_BaseRowMixin.py +3 -1
  139. scitex/db/_BaseMixins/_BaseTableMixin.py +3 -1
  140. scitex/db/_BaseMixins/_BaseTransactionMixin.py +1 -3
  141. scitex/db/_BaseMixins/__init__.py +1 -1
  142. scitex/db/__init__.py +9 -1
  143. scitex/db/__main__.py +8 -21
  144. scitex/db/_check_health.py +15 -31
  145. scitex/db/_delete_duplicates.py +7 -4
  146. scitex/db/_inspect.py +22 -38
  147. scitex/db/_inspect_optimized.py +89 -85
  148. scitex/db/_postgresql/_PostgreSQL.py +0 -1
  149. scitex/db/_postgresql/_PostgreSQLMixins/_BlobMixin.py +3 -1
  150. scitex/db/_postgresql/_PostgreSQLMixins/_ConnectionMixin.py +1 -3
  151. scitex/db/_postgresql/_PostgreSQLMixins/_ImportExportMixin.py +1 -3
  152. scitex/db/_postgresql/_PostgreSQLMixins/_MaintenanceMixin.py +1 -4
  153. scitex/db/_postgresql/_PostgreSQLMixins/_QueryMixin.py +3 -3
  154. scitex/db/_postgresql/_PostgreSQLMixins/_RowMixin.py +3 -1
  155. scitex/db/_postgresql/_PostgreSQLMixins/_TransactionMixin.py +1 -3
  156. scitex/db/_postgresql/__init__.py +1 -1
  157. scitex/db/_sqlite3/_SQLite3.py +2 -4
  158. scitex/db/_sqlite3/_SQLite3Mixins/_ArrayMixin.py +11 -12
  159. scitex/db/_sqlite3/_SQLite3Mixins/_ArrayMixin_v01-need-_hash-col.py +19 -14
  160. scitex/db/_sqlite3/_SQLite3Mixins/_BatchMixin.py +3 -1
  161. scitex/db/_sqlite3/_SQLite3Mixins/_BlobMixin.py +7 -7
  162. scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin.py +118 -111
  163. scitex/db/_sqlite3/_SQLite3Mixins/_ConnectionMixin.py +8 -10
  164. scitex/db/_sqlite3/_SQLite3Mixins/_GitMixin.py +17 -45
  165. scitex/db/_sqlite3/_SQLite3Mixins/_ImportExportMixin.py +1 -3
  166. scitex/db/_sqlite3/_SQLite3Mixins/_IndexMixin.py +3 -1
  167. scitex/db/_sqlite3/_SQLite3Mixins/_QueryMixin.py +3 -4
  168. scitex/db/_sqlite3/_SQLite3Mixins/_RowMixin.py +9 -9
  169. scitex/db/_sqlite3/_SQLite3Mixins/_TableMixin.py +18 -11
  170. scitex/db/_sqlite3/_SQLite3Mixins/__init__.py +1 -0
  171. scitex/db/_sqlite3/__init__.py +1 -1
  172. scitex/db/_sqlite3/_delete_duplicates.py +13 -11
  173. scitex/decorators/__init__.py +29 -4
  174. scitex/decorators/_auto_order.py +43 -43
  175. scitex/decorators/_batch_fn.py +12 -6
  176. scitex/decorators/_cache_disk.py +8 -9
  177. scitex/decorators/_cache_disk_async.py +8 -7
  178. scitex/decorators/_combined.py +19 -13
  179. scitex/decorators/_converters.py +16 -3
  180. scitex/decorators/_deprecated.py +32 -22
  181. scitex/decorators/_numpy_fn.py +18 -4
  182. scitex/decorators/_pandas_fn.py +17 -5
  183. scitex/decorators/_signal_fn.py +17 -3
  184. scitex/decorators/_torch_fn.py +32 -15
  185. scitex/decorators/_xarray_fn.py +23 -9
  186. scitex/dev/_analyze_code_flow.py +0 -2
  187. scitex/dict/_DotDict.py +15 -19
  188. scitex/dict/_flatten.py +1 -0
  189. scitex/dict/_listed_dict.py +1 -0
  190. scitex/dict/_pop_keys.py +1 -0
  191. scitex/dict/_replace.py +1 -0
  192. scitex/dict/_safe_merge.py +1 -0
  193. scitex/dict/_to_str.py +2 -3
  194. scitex/dsp/__init__.py +13 -4
  195. scitex/dsp/_crop.py +3 -1
  196. scitex/dsp/_detect_ripples.py +3 -1
  197. scitex/dsp/_modulation_index.py +3 -1
  198. scitex/dsp/_time.py +3 -1
  199. scitex/dsp/_wavelet.py +0 -1
  200. scitex/dsp/example.py +0 -5
  201. scitex/dsp/filt.py +4 -0
  202. scitex/dsp/utils/__init__.py +4 -1
  203. scitex/dsp/utils/pac.py +3 -3
  204. scitex/dt/_normalize_timestamp.py +4 -1
  205. scitex/errors.py +3 -6
  206. scitex/etc/__init__.py +1 -1
  207. scitex/gen/_DimHandler.py +6 -6
  208. scitex/gen/__init__.py +5 -1
  209. scitex/gen/_deprecated_close.py +1 -0
  210. scitex/gen/_deprecated_start.py +5 -3
  211. scitex/gen/_detect_environment.py +44 -41
  212. scitex/gen/_detect_notebook_path.py +51 -47
  213. scitex/gen/_embed.py +1 -1
  214. scitex/gen/_get_notebook_path.py +81 -62
  215. scitex/gen/_inspect_module.py +0 -1
  216. scitex/gen/_norm.py +16 -7
  217. scitex/gen/_norm_cache.py +78 -65
  218. scitex/gen/_print_config.py +0 -3
  219. scitex/gen/_src.py +2 -3
  220. scitex/gen/_title_case.py +3 -2
  221. scitex/gen/_to_even.py +8 -8
  222. scitex/gen/_transpose.py +3 -3
  223. scitex/gen/misc.py +0 -3
  224. scitex/gists/_SigMacro_processFigure_S.py +2 -2
  225. scitex/gists/_SigMacro_toBlue.py +2 -2
  226. scitex/gists/__init__.py +4 -1
  227. scitex/git/_branch.py +19 -11
  228. scitex/git/_clone.py +23 -15
  229. scitex/git/_commit.py +10 -12
  230. scitex/git/_init.py +15 -38
  231. scitex/git/_remote.py +9 -3
  232. scitex/git/_result.py +3 -0
  233. scitex/git/_retry.py +2 -5
  234. scitex/git/_types.py +4 -0
  235. scitex/git/_validation.py +8 -8
  236. scitex/git/_workflow.py +4 -4
  237. scitex/io/__init__.py +2 -1
  238. scitex/io/_glob.py +2 -2
  239. scitex/io/_json2md.py +3 -3
  240. scitex/io/_load.py +6 -8
  241. scitex/io/_load_cache.py +71 -71
  242. scitex/io/_load_configs.py +2 -3
  243. scitex/io/_load_modules/_H5Explorer.py +6 -12
  244. scitex/io/_load_modules/_ZarrExplorer.py +3 -3
  245. scitex/io/_load_modules/_bibtex.py +62 -63
  246. scitex/io/_load_modules/_canvas.py +4 -9
  247. scitex/io/_load_modules/_catboost.py +7 -2
  248. scitex/io/_load_modules/_hdf5.py +2 -0
  249. scitex/io/_load_modules/_image.py +5 -1
  250. scitex/io/_load_modules/_matlab.py +3 -1
  251. scitex/io/_load_modules/_optuna.py +0 -1
  252. scitex/io/_load_modules/_pdf.py +38 -29
  253. scitex/io/_load_modules/_sqlite3.py +1 -0
  254. scitex/io/_load_modules/_txt.py +2 -0
  255. scitex/io/_load_modules/_xml.py +9 -9
  256. scitex/io/_load_modules/_zarr.py +12 -10
  257. scitex/io/_metadata.py +76 -37
  258. scitex/io/_qr_utils.py +18 -13
  259. scitex/io/_save.py +220 -63
  260. scitex/io/_save_modules/__init__.py +7 -2
  261. scitex/io/_save_modules/_bibtex.py +66 -61
  262. scitex/io/_save_modules/_canvas.py +5 -6
  263. scitex/io/_save_modules/_catboost.py +2 -2
  264. scitex/io/_save_modules/_csv.py +4 -4
  265. scitex/io/_save_modules/_excel.py +5 -9
  266. scitex/io/_save_modules/_hdf5.py +9 -21
  267. scitex/io/_save_modules/_html.py +5 -5
  268. scitex/io/_save_modules/_image.py +105 -8
  269. scitex/io/_save_modules/_joblib.py +2 -2
  270. scitex/io/_save_modules/_json.py +51 -6
  271. scitex/io/_save_modules/_listed_dfs_as_csv.py +2 -1
  272. scitex/io/_save_modules/_listed_scalars_as_csv.py +2 -1
  273. scitex/io/_save_modules/_matlab.py +2 -2
  274. scitex/io/_save_modules/_numpy.py +6 -8
  275. scitex/io/_save_modules/_pickle.py +4 -4
  276. scitex/io/_save_modules/_plotly.py +3 -3
  277. scitex/io/_save_modules/_tex.py +23 -25
  278. scitex/io/_save_modules/_text.py +2 -2
  279. scitex/io/_save_modules/_yaml.py +9 -9
  280. scitex/io/_save_modules/_zarr.py +15 -15
  281. scitex/io/utils/__init__.py +2 -1
  282. scitex/io/utils/h5_to_zarr.py +173 -155
  283. scitex/linalg/__init__.py +1 -1
  284. scitex/linalg/_geometric_median.py +4 -3
  285. scitex/logging/_Tee.py +5 -7
  286. scitex/logging/__init__.py +18 -19
  287. scitex/logging/_config.py +4 -1
  288. scitex/logging/_context.py +6 -5
  289. scitex/logging/_formatters.py +2 -3
  290. scitex/logging/_handlers.py +19 -20
  291. scitex/logging/_levels.py +9 -17
  292. scitex/logging/_logger.py +74 -15
  293. scitex/logging/_print_capture.py +17 -17
  294. scitex/nn/_BNet.py +1 -3
  295. scitex/nn/_Filters.py +6 -2
  296. scitex/nn/_ModulationIndex.py +3 -1
  297. scitex/nn/_PAC.py +3 -2
  298. scitex/nn/_PSD.py +0 -1
  299. scitex/nn/__init__.py +16 -3
  300. scitex/path/_clean.py +10 -8
  301. scitex/path/_find.py +1 -1
  302. scitex/path/_get_spath.py +1 -2
  303. scitex/path/_mk_spath.py +1 -1
  304. scitex/path/_symlink.py +5 -10
  305. scitex/pd/__init__.py +4 -1
  306. scitex/pd/_force_df.py +24 -24
  307. scitex/pd/_get_unique.py +1 -0
  308. scitex/pd/_merge_columns.py +1 -1
  309. scitex/pd/_round.py +11 -7
  310. scitex/pd/_to_xy.py +0 -1
  311. scitex/plt/REQUESTS.md +191 -0
  312. scitex/plt/__init__.py +185 -87
  313. scitex/plt/_subplots/_AxesWrapper.py +22 -6
  314. scitex/plt/_subplots/_AxisWrapper.py +100 -39
  315. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +74 -52
  316. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +183 -73
  317. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +61 -45
  318. scitex/plt/_subplots/_AxisWrapperMixins/_TrackingMixin.py +26 -14
  319. scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +80 -73
  320. scitex/plt/_subplots/_FigWrapper.py +93 -60
  321. scitex/plt/_subplots/_SubplotsWrapper.py +135 -68
  322. scitex/plt/_subplots/__init__.py +10 -0
  323. scitex/plt/_subplots/_export_as_csv.py +89 -47
  324. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +1 -0
  325. scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +6 -4
  326. scitex/plt/_subplots/_export_as_csv_formatters/_format_bar.py +88 -38
  327. scitex/plt/_subplots/_export_as_csv_formatters/_format_barh.py +25 -31
  328. scitex/plt/_subplots/_export_as_csv_formatters/_format_boxplot.py +53 -23
  329. scitex/plt/_subplots/_export_as_csv_formatters/_format_contour.py +38 -25
  330. scitex/plt/_subplots/_export_as_csv_formatters/_format_contourf.py +17 -9
  331. scitex/plt/_subplots/_export_as_csv_formatters/_format_errorbar.py +70 -124
  332. scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +12 -10
  333. scitex/plt/_subplots/_export_as_csv_formatters/_format_fill.py +31 -17
  334. scitex/plt/_subplots/_export_as_csv_formatters/_format_fill_between.py +33 -21
  335. scitex/plt/_subplots/_export_as_csv_formatters/_format_hexbin.py +14 -4
  336. scitex/plt/_subplots/_export_as_csv_formatters/_format_hist.py +43 -29
  337. scitex/plt/_subplots/_export_as_csv_formatters/_format_hist2d.py +14 -4
  338. scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow.py +27 -11
  339. scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +7 -5
  340. scitex/plt/_subplots/_export_as_csv_formatters/_format_matshow.py +9 -7
  341. scitex/plt/_subplots/_export_as_csv_formatters/_format_pie.py +15 -6
  342. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot.py +85 -46
  343. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_box.py +52 -27
  344. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_imshow.py +1 -0
  345. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +16 -17
  346. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +7 -5
  347. scitex/plt/_subplots/_export_as_csv_formatters/_format_quiver.py +10 -8
  348. scitex/plt/_subplots/_export_as_csv_formatters/_format_scatter.py +17 -6
  349. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_barplot.py +43 -26
  350. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_boxplot.py +68 -47
  351. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_heatmap.py +52 -64
  352. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_histplot.py +55 -50
  353. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +9 -11
  354. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_kdeplot.py +63 -29
  355. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +4 -4
  356. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +6 -4
  357. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_scatterplot.py +44 -40
  358. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_stripplot.py +46 -39
  359. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_swarmplot.py +46 -39
  360. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_violinplot.py +75 -94
  361. scitex/plt/_subplots/_export_as_csv_formatters/_format_stem.py +12 -3
  362. scitex/plt/_subplots/_export_as_csv_formatters/_format_step.py +12 -3
  363. scitex/plt/_subplots/_export_as_csv_formatters/_format_streamplot.py +10 -8
  364. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_conf_mat.py +17 -15
  365. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_ecdf.py +10 -9
  366. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_fillv.py +35 -31
  367. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_heatmap.py +18 -18
  368. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_image.py +24 -18
  369. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_joyplot.py +9 -7
  370. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_line.py +34 -23
  371. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_ci.py +15 -13
  372. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_std.py +12 -10
  373. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_median_iqr.py +15 -13
  374. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_raster.py +11 -9
  375. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_rectangle.py +84 -56
  376. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter_hist.py +35 -32
  377. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_shaded_line.py +46 -30
  378. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_violin.py +51 -51
  379. scitex/plt/_subplots/_export_as_csv_formatters/_format_text.py +32 -31
  380. scitex/plt/_subplots/_export_as_csv_formatters/_format_violin.py +34 -31
  381. scitex/plt/_subplots/_export_as_csv_formatters/_format_violinplot.py +44 -37
  382. scitex/plt/_subplots/_export_as_csv_formatters/verify_formatters.py +91 -74
  383. scitex/plt/_tpl.py +6 -5
  384. scitex/plt/ax/_plot/__init__.py +24 -0
  385. scitex/plt/ax/_plot/_add_fitted_line.py +12 -11
  386. scitex/plt/ax/_plot/_plot_circular_hist.py +3 -1
  387. scitex/plt/ax/_plot/_plot_statistical_shaded_line.py +25 -19
  388. scitex/plt/ax/_plot/_stx_conf_mat.py +6 -3
  389. scitex/plt/ax/_plot/_stx_ecdf.py +5 -3
  390. scitex/plt/ax/_plot/_stx_fillv.py +4 -2
  391. scitex/plt/ax/_plot/_stx_heatmap.py +7 -4
  392. scitex/plt/ax/_plot/_stx_image.py +7 -5
  393. scitex/plt/ax/_plot/_stx_joyplot.py +32 -10
  394. scitex/plt/ax/_plot/_stx_raster.py +26 -11
  395. scitex/plt/ax/_plot/_stx_rectangle.py +2 -2
  396. scitex/plt/ax/_plot/_stx_shaded_line.py +15 -11
  397. scitex/plt/ax/_plot/_stx_violin.py +3 -1
  398. scitex/plt/ax/_style/_add_marginal_ax.py +6 -4
  399. scitex/plt/ax/_style/_auto_scale_axis.py +14 -10
  400. scitex/plt/ax/_style/_extend.py +3 -1
  401. scitex/plt/ax/_style/_force_aspect.py +5 -3
  402. scitex/plt/ax/_style/_format_units.py +2 -2
  403. scitex/plt/ax/_style/_hide_spines.py +5 -1
  404. scitex/plt/ax/_style/_map_ticks.py +5 -3
  405. scitex/plt/ax/_style/_rotate_labels.py +5 -4
  406. scitex/plt/ax/_style/_rotate_labels_v01.py +73 -63
  407. scitex/plt/ax/_style/_set_log_scale.py +120 -85
  408. scitex/plt/ax/_style/_set_meta.py +99 -76
  409. scitex/plt/ax/_style/_set_supxyt.py +33 -16
  410. scitex/plt/ax/_style/_set_xyt.py +27 -18
  411. scitex/plt/ax/_style/_share_axes.py +15 -5
  412. scitex/plt/ax/_style/_show_spines.py +58 -57
  413. scitex/plt/ax/_style/_style_barplot.py +1 -1
  414. scitex/plt/ax/_style/_style_boxplot.py +25 -14
  415. scitex/plt/ax/_style/_style_errorbar.py +0 -0
  416. scitex/plt/ax/_style/_style_scatter.py +1 -1
  417. scitex/plt/ax/_style/_style_suptitles.py +3 -3
  418. scitex/plt/ax/_style/_style_violinplot.py +8 -2
  419. scitex/plt/color/__init__.py +34 -2
  420. scitex/plt/color/_add_hue_col.py +1 -0
  421. scitex/plt/color/_colors.py +0 -1
  422. scitex/plt/color/_get_colors_from_conf_matap.py +3 -1
  423. scitex/plt/color/_vizualize_colors.py +0 -1
  424. scitex/plt/docs/FIGURE_ARCHITECTURE.md +155 -97
  425. scitex/plt/gallery/README.md +75 -0
  426. scitex/plt/gallery/__init__.py +29 -0
  427. scitex/plt/gallery/_generate.py +153 -0
  428. scitex/plt/gallery/_plots.py +594 -0
  429. scitex/plt/gallery/_registry.py +153 -0
  430. scitex/plt/styles/__init__.py +9 -9
  431. scitex/plt/styles/_plot_defaults.py +62 -61
  432. scitex/plt/styles/_plot_postprocess.py +126 -77
  433. scitex/plt/styles/_style_loader.py +0 -0
  434. scitex/plt/styles/presets.py +43 -18
  435. scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_between.json +110 -0
  436. scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_betweenx.json +88 -0
  437. scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fill_between.json +103 -0
  438. scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fillv.json +106 -0
  439. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/bar.json +92 -0
  440. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/barh.json +92 -0
  441. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/boxplot.json +92 -0
  442. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_bar.json +84 -0
  443. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_barh.json +84 -0
  444. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_box.json +83 -0
  445. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_boxplot.json +93 -0
  446. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violin.json +91 -0
  447. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violinplot.json +91 -0
  448. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/violinplot.json +91 -0
  449. scitex/plt/templates/research-master/scitex/vis/gallery/contour/contour.json +97 -0
  450. scitex/plt/templates/research-master/scitex/vis/gallery/contour/contourf.json +98 -0
  451. scitex/plt/templates/research-master/scitex/vis/gallery/contour/stx_contour.json +84 -0
  452. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist.json +101 -0
  453. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist2d.json +96 -0
  454. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_ecdf.json +95 -0
  455. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_joyplot.json +95 -0
  456. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_kde.json +93 -0
  457. scitex/plt/templates/research-master/scitex/vis/gallery/grid/imshow.json +95 -0
  458. scitex/plt/templates/research-master/scitex/vis/gallery/grid/matshow.json +95 -0
  459. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_conf_mat.json +83 -0
  460. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_heatmap.json +92 -0
  461. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_image.json +121 -0
  462. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_imshow.json +84 -0
  463. scitex/plt/templates/research-master/scitex/vis/gallery/line/plot.json +110 -0
  464. scitex/plt/templates/research-master/scitex/vis/gallery/line/step.json +92 -0
  465. scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_line.json +95 -0
  466. scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_shaded_line.json +96 -0
  467. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/hexbin.json +95 -0
  468. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/scatter.json +95 -0
  469. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stem.json +92 -0
  470. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stx_scatter.json +84 -0
  471. scitex/plt/templates/research-master/scitex/vis/gallery/special/pie.json +94 -0
  472. scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_raster.json +109 -0
  473. scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_rectangle.json +108 -0
  474. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/errorbar.json +93 -0
  475. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_errorbar.json +84 -0
  476. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_ci.json +96 -0
  477. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_std.json +96 -0
  478. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_median_iqr.json +96 -0
  479. scitex/plt/templates/research-master/scitex/vis/gallery/vector/quiver.json +99 -0
  480. scitex/plt/templates/research-master/scitex/vis/gallery/vector/streamplot.json +100 -0
  481. scitex/plt/utils/__init__.py +29 -2
  482. scitex/plt/utils/_close.py +8 -3
  483. scitex/plt/utils/_collect_figure_metadata.py +3031 -265
  484. scitex/plt/utils/_colorbar.py +15 -17
  485. scitex/plt/utils/_configure_mpl.py +22 -14
  486. scitex/plt/utils/_crop.py +60 -27
  487. scitex/plt/utils/_csv_column_naming.py +123 -72
  488. scitex/plt/utils/_dimension_viewer.py +7 -19
  489. scitex/plt/utils/_figure_from_axes_mm.py +70 -16
  490. scitex/plt/utils/_figure_mm.py +3 -2
  491. scitex/plt/utils/_get_actual_font.py +5 -4
  492. scitex/plt/utils/_histogram_utils.py +52 -48
  493. scitex/plt/utils/_is_valid_axis.py +19 -13
  494. scitex/plt/utils/_mk_colorbar.py +3 -3
  495. scitex/plt/utils/_scientific_captions.py +202 -139
  496. scitex/plt/utils/_scitex_config.py +98 -98
  497. scitex/plt/utils/_units.py +0 -0
  498. scitex/plt/utils/metadata/__init__.py +36 -0
  499. scitex/plt/utils/metadata/_artist_extraction.py +119 -0
  500. scitex/plt/utils/metadata/_axes_metadata.py +93 -0
  501. scitex/plt/utils/metadata/_collection_artists.py +292 -0
  502. scitex/plt/utils/metadata/_core.py +208 -0
  503. scitex/plt/utils/metadata/_csv_column_extraction.py +186 -0
  504. scitex/plt/utils/metadata/_csv_hash.py +115 -0
  505. scitex/plt/utils/metadata/_csv_verification.py +95 -0
  506. scitex/plt/utils/metadata/_data_linkage.py +263 -0
  507. scitex/plt/utils/metadata/_dimensions.py +239 -0
  508. scitex/plt/utils/metadata/_figure_metadata.py +58 -0
  509. scitex/plt/utils/metadata/_image_text_artists.py +168 -0
  510. scitex/plt/utils/metadata/_label_parsing.py +82 -0
  511. scitex/plt/utils/metadata/_legend_extraction.py +120 -0
  512. scitex/plt/utils/metadata/_line_artists.py +367 -0
  513. scitex/plt/utils/metadata/_line_semantic_handling.py +173 -0
  514. scitex/plt/utils/metadata/_patch_artists.py +211 -0
  515. scitex/plt/utils/metadata/_plot_content.py +26 -0
  516. scitex/plt/utils/metadata/_plot_type_detection.py +184 -0
  517. scitex/plt/utils/metadata/_precision.py +134 -0
  518. scitex/plt/utils/metadata/_precision_config.py +68 -0
  519. scitex/plt/utils/metadata/_precision_sections.py +211 -0
  520. scitex/plt/utils/metadata/_recipe_extraction.py +267 -0
  521. scitex/plt/utils/metadata/_style_parsing.py +174 -0
  522. scitex/repro/_RandomStateManager.py +33 -38
  523. scitex/repro/__init__.py +16 -7
  524. scitex/repro/_gen_ID.py +7 -9
  525. scitex/repro/_gen_timestamp.py +7 -6
  526. scitex/repro/_hash_array.py +8 -12
  527. scitex/reproduce/__init__.py +1 -1
  528. scitex/resource/_get_processor_usages.py +3 -1
  529. scitex/resource/_log_processor_usages.py +3 -1
  530. scitex/rng/__init__.py +1 -1
  531. scitex/schema/README.md +178 -0
  532. scitex/schema/__init__.py +144 -0
  533. scitex/schema/_canvas.py +444 -0
  534. scitex/schema/_stats.py +762 -0
  535. scitex/schema/_validation.py +590 -0
  536. scitex/scholar/.legacy/Scholar.py +5 -12
  537. scitex/scholar/.legacy/_Scholar.py +66 -99
  538. scitex/scholar/.legacy/_ScholarAPI.py +75 -66
  539. scitex/scholar/.legacy/_tmp/search_engine/_BaseSearchEngine.py +3 -3
  540. scitex/scholar/.legacy/_tmp/search_engine/_UnifiedSearcher.py +4 -9
  541. scitex/scholar/.legacy/_tmp/search_engine/__init__.py +14 -21
  542. scitex/scholar/.legacy/_tmp/search_engine/local/_LocalSearchEngine.py +40 -37
  543. scitex/scholar/.legacy/_tmp/search_engine/local/_VectorSearchEngine.py +31 -28
  544. scitex/scholar/.legacy/_tmp/search_engine/web/_ArxivSearchEngine.py +74 -65
  545. scitex/scholar/.legacy/_tmp/search_engine/web/_CrossRefSearchEngine.py +122 -116
  546. scitex/scholar/.legacy/_tmp/search_engine/web/_GoogleScholarSearchEngine.py +65 -59
  547. scitex/scholar/.legacy/_tmp/search_engine/web/_PubMedSearchEngine.py +121 -107
  548. scitex/scholar/.legacy/_tmp/search_engine/web/_SemanticScholarSearchEngine.py +5 -12
  549. scitex/scholar/.legacy/database/_DatabaseEntry.py +49 -45
  550. scitex/scholar/.legacy/database/_DatabaseIndex.py +131 -94
  551. scitex/scholar/.legacy/database/_LibraryManager.py +65 -63
  552. scitex/scholar/.legacy/database/_PaperDatabase.py +138 -124
  553. scitex/scholar/.legacy/database/_ScholarDatabaseIntegration.py +14 -36
  554. scitex/scholar/.legacy/database/_StorageIntegratedDB.py +192 -156
  555. scitex/scholar/.legacy/database/_ZoteroCompatibleDB.py +300 -237
  556. scitex/scholar/.legacy/database/__init__.py +2 -1
  557. scitex/scholar/.legacy/database/manage.py +92 -84
  558. scitex/scholar/.legacy/lookup/_LookupIndex.py +157 -101
  559. scitex/scholar/.legacy/lookup/__init__.py +2 -1
  560. scitex/scholar/.legacy/metadata/doi/batch/_MetadataHandlerForBatchDOIResolution.py +4 -9
  561. scitex/scholar/.legacy/metadata/doi/batch/_ProgressManagerForBatchDOIResolution.py +10 -23
  562. scitex/scholar/.legacy/metadata/doi/batch/_SourceStatsManagerForBatchDOIResolution.py +4 -9
  563. scitex/scholar/.legacy/metadata/doi/batch/__init__.py +3 -1
  564. scitex/scholar/.legacy/metadata/doi/resolvers/_BatchDOIResolver.py +10 -25
  565. scitex/scholar/.legacy/metadata/doi/resolvers/_BibTeXDOIResolver.py +19 -49
  566. scitex/scholar/.legacy/metadata/doi/resolvers/_DOIResolver.py +1 -0
  567. scitex/scholar/.legacy/metadata/doi/resolvers/_SingleDOIResolver.py +8 -20
  568. scitex/scholar/.legacy/metadata/doi/sources/.combined-SemanticScholarSource/_SemanticScholarSource.py +37 -35
  569. scitex/scholar/.legacy/metadata/doi/sources/.combined-SemanticScholarSource/_SemanticScholarSourceEnhanced.py +49 -37
  570. scitex/scholar/.legacy/metadata/doi/sources/_ArXivSource.py +11 -30
  571. scitex/scholar/.legacy/metadata/doi/sources/_BaseDOISource.py +19 -47
  572. scitex/scholar/.legacy/metadata/doi/sources/_CrossRefLocalSource.py +1 -0
  573. scitex/scholar/.legacy/metadata/doi/sources/_CrossRefSource.py +12 -33
  574. scitex/scholar/.legacy/metadata/doi/sources/_OpenAlexSource.py +8 -20
  575. scitex/scholar/.legacy/metadata/doi/sources/_PubMedSource.py +10 -27
  576. scitex/scholar/.legacy/metadata/doi/sources/_SemanticScholarSource.py +11 -29
  577. scitex/scholar/.legacy/metadata/doi/sources/_SourceManager.py +8 -21
  578. scitex/scholar/.legacy/metadata/doi/sources/_SourceResolutionStrategy.py +24 -55
  579. scitex/scholar/.legacy/metadata/doi/sources/_SourceRotationManager.py +8 -21
  580. scitex/scholar/.legacy/metadata/doi/sources/_URLDOISource.py +9 -16
  581. scitex/scholar/.legacy/metadata/doi/sources/_UnifiedSource.py +8 -22
  582. scitex/scholar/.legacy/metadata/doi/sources/__init__.py +1 -0
  583. scitex/scholar/.legacy/metadata/doi/utils/_PubMedConverter.py +4 -8
  584. scitex/scholar/.legacy/metadata/doi/utils/_RateLimitHandler.py +17 -43
  585. scitex/scholar/.legacy/metadata/doi/utils/_TextNormalizer.py +8 -18
  586. scitex/scholar/.legacy/metadata/doi/utils/_URLDOIExtractor.py +4 -8
  587. scitex/scholar/.legacy/metadata/doi/utils/__init__.py +1 -0
  588. scitex/scholar/.legacy/metadata/doi/utils/_to_complete_metadata_structure.py +1 -0
  589. scitex/scholar/.legacy/metadata/enrichment/_LibraryEnricher.py +2 -3
  590. scitex/scholar/.legacy/metadata/enrichment/enrichers/_ImpactFactorEnricher.py +6 -12
  591. scitex/scholar/.legacy/metadata/enrichment/enrichers/_SmartEnricher.py +5 -10
  592. scitex/scholar/.legacy/metadata/enrichment/sources/_UnifiedMetadataSource.py +4 -5
  593. scitex/scholar/.legacy/metadata/query_to_full_meta_json.py +8 -12
  594. scitex/scholar/.legacy/metadata/urls/_URLMetadataHandler.py +3 -3
  595. scitex/scholar/.legacy/metadata/urls/_ZoteroTranslatorRunner.py +15 -21
  596. scitex/scholar/.legacy/metadata/urls/__init__.py +3 -3
  597. scitex/scholar/.legacy/metadata/urls/_finder.py +4 -6
  598. scitex/scholar/.legacy/metadata/urls/_handler.py +7 -15
  599. scitex/scholar/.legacy/metadata/urls/_resolver.py +6 -12
  600. scitex/scholar/.legacy/search/_Embedder.py +74 -69
  601. scitex/scholar/.legacy/search/_SemanticSearch.py +91 -90
  602. scitex/scholar/.legacy/search/_SemanticSearchEngine.py +104 -109
  603. scitex/scholar/.legacy/search/_UnifiedSearcher.py +530 -471
  604. scitex/scholar/.legacy/search/_VectorDatabase.py +111 -92
  605. scitex/scholar/.legacy/search/__init__.py +1 -0
  606. scitex/scholar/.legacy/storage/_EnhancedStorageManager.py +182 -154
  607. scitex/scholar/.legacy/storage/__init__.py +2 -1
  608. scitex/scholar/__init__.py +0 -2
  609. scitex/scholar/__main__.py +1 -3
  610. scitex/scholar/auth/ScholarAuthManager.py +13 -36
  611. scitex/scholar/auth/core/AuthenticationGateway.py +15 -29
  612. scitex/scholar/auth/core/BrowserAuthenticator.py +22 -57
  613. scitex/scholar/auth/core/StrategyResolver.py +10 -27
  614. scitex/scholar/auth/core/__init__.py +5 -1
  615. scitex/scholar/auth/gateway/_OpenURLLinkFinder.py +11 -21
  616. scitex/scholar/auth/gateway/_OpenURLResolver.py +10 -18
  617. scitex/scholar/auth/gateway/_resolve_functions.py +3 -3
  618. scitex/scholar/auth/providers/BaseAuthenticator.py +1 -0
  619. scitex/scholar/auth/providers/EZProxyAuthenticator.py +7 -14
  620. scitex/scholar/auth/providers/OpenAthensAuthenticator.py +29 -57
  621. scitex/scholar/auth/providers/ShibbolethAuthenticator.py +87 -73
  622. scitex/scholar/auth/session/AuthCacheManager.py +12 -22
  623. scitex/scholar/auth/session/SessionManager.py +4 -6
  624. scitex/scholar/auth/sso/BaseSSOAutomator.py +13 -19
  625. scitex/scholar/auth/sso/OpenAthensSSOAutomator.py +16 -45
  626. scitex/scholar/auth/sso/SSOAutomator.py +8 -15
  627. scitex/scholar/auth/sso/UniversityOfMelbourneSSOAutomator.py +13 -23
  628. scitex/scholar/browser/ScholarBrowserManager.py +31 -56
  629. scitex/scholar/browser/__init__.py +1 -0
  630. scitex/scholar/browser/utils/click_and_wait.py +3 -4
  631. scitex/scholar/browser/utils/close_unwanted_pages.py +4 -7
  632. scitex/scholar/browser/utils/wait_redirects.py +15 -40
  633. scitex/scholar/citation_graph/__init__.py +0 -0
  634. scitex/scholar/citation_graph/builder.py +3 -7
  635. scitex/scholar/citation_graph/database.py +4 -11
  636. scitex/scholar/citation_graph/example.py +5 -10
  637. scitex/scholar/citation_graph/models.py +0 -0
  638. scitex/scholar/cli/_url_utils.py +1 -1
  639. scitex/scholar/cli/chrome.py +5 -3
  640. scitex/scholar/cli/download_pdf.py +13 -14
  641. scitex/scholar/cli/handlers/bibtex_handler.py +4 -12
  642. scitex/scholar/cli/handlers/doi_handler.py +1 -3
  643. scitex/scholar/cli/handlers/project_handler.py +6 -20
  644. scitex/scholar/cli/open_browser.py +41 -39
  645. scitex/scholar/cli/open_browser_auto.py +31 -39
  646. scitex/scholar/cli/open_browser_monitored.py +27 -24
  647. scitex/scholar/config/ScholarConfig.py +5 -8
  648. scitex/scholar/config/__init__.py +1 -0
  649. scitex/scholar/config/core/_CascadeConfig.py +3 -3
  650. scitex/scholar/config/core/_PathManager.py +16 -28
  651. scitex/scholar/core/Paper.py +79 -78
  652. scitex/scholar/core/Papers.py +16 -27
  653. scitex/scholar/core/Scholar.py +98 -229
  654. scitex/scholar/core/journal_normalizer.py +52 -49
  655. scitex/scholar/core/oa_cache.py +27 -23
  656. scitex/scholar/core/open_access.py +17 -8
  657. scitex/scholar/docs/template.py +4 -3
  658. scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/clf_svm.py +0 -0
  659. scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/download.py +0 -0
  660. scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/plot_conf_mat.py +0 -0
  661. scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/plot_digits.py +0 -0
  662. scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/plot_umap_space.py +0 -0
  663. scitex/scholar/examples/00_config.py +10 -9
  664. scitex/scholar/examples/01_auth.py +3 -0
  665. scitex/scholar/examples/02_browser.py +14 -10
  666. scitex/scholar/examples/03_01-engine.py +3 -0
  667. scitex/scholar/examples/03_02-engine-for-bibtex.py +4 -3
  668. scitex/scholar/examples/04_01-url.py +9 -9
  669. scitex/scholar/examples/04_02-url-for-bibtex.py +7 -3
  670. scitex/scholar/examples/04_02-url-for-dois.py +87 -97
  671. scitex/scholar/examples/05_download_pdf.py +10 -4
  672. scitex/scholar/examples/06_find_and_download.py +6 -6
  673. scitex/scholar/examples/06_parse_bibtex.py +17 -17
  674. scitex/scholar/examples/07_storage_integration.py +6 -9
  675. scitex/scholar/examples/99_fullpipeline-for-bibtex.py +14 -15
  676. scitex/scholar/examples/99_fullpipeline-for-one-entry.py +31 -23
  677. scitex/scholar/examples/99_maintenance.py +3 -0
  678. scitex/scholar/examples/dev.py +2 -3
  679. scitex/scholar/examples/zotero_integration.py +11 -18
  680. scitex/scholar/impact_factor/ImpactFactorEngine.py +7 -9
  681. scitex/scholar/impact_factor/estimation/__init__.py +4 -4
  682. scitex/scholar/impact_factor/estimation/core/__init__.py +3 -7
  683. scitex/scholar/impact_factor/estimation/core/cache_manager.py +223 -211
  684. scitex/scholar/impact_factor/estimation/core/calculator.py +165 -131
  685. scitex/scholar/impact_factor/estimation/core/journal_matcher.py +217 -172
  686. scitex/scholar/impact_factor/jcr/ImpactFactorJCREngine.py +6 -14
  687. scitex/scholar/impact_factor/jcr/build_database.py +4 -3
  688. scitex/scholar/integration/base.py +9 -17
  689. scitex/scholar/integration/mendeley/exporter.py +2 -4
  690. scitex/scholar/integration/mendeley/importer.py +3 -3
  691. scitex/scholar/integration/mendeley/linker.py +3 -3
  692. scitex/scholar/integration/mendeley/mapper.py +9 -6
  693. scitex/scholar/integration/zotero/__main__.py +26 -43
  694. scitex/scholar/integration/zotero/exporter.py +15 -11
  695. scitex/scholar/integration/zotero/importer.py +12 -10
  696. scitex/scholar/integration/zotero/linker.py +8 -12
  697. scitex/scholar/integration/zotero/mapper.py +17 -12
  698. scitex/scholar/metadata_engines/.combined-SemanticScholarSource/_SemanticScholarSource.py +37 -35
  699. scitex/scholar/metadata_engines/.combined-SemanticScholarSource/_SemanticScholarSourceEnhanced.py +47 -35
  700. scitex/scholar/metadata_engines/ScholarEngine.py +21 -43
  701. scitex/scholar/metadata_engines/__init__.py +1 -0
  702. scitex/scholar/metadata_engines/individual/ArXivEngine.py +15 -37
  703. scitex/scholar/metadata_engines/individual/CrossRefEngine.py +15 -42
  704. scitex/scholar/metadata_engines/individual/CrossRefLocalEngine.py +24 -45
  705. scitex/scholar/metadata_engines/individual/OpenAlexEngine.py +11 -21
  706. scitex/scholar/metadata_engines/individual/PubMedEngine.py +10 -27
  707. scitex/scholar/metadata_engines/individual/SemanticScholarEngine.py +28 -35
  708. scitex/scholar/metadata_engines/individual/URLDOIEngine.py +11 -22
  709. scitex/scholar/metadata_engines/individual/_BaseDOIEngine.py +20 -49
  710. scitex/scholar/metadata_engines/utils/_PubMedConverter.py +4 -8
  711. scitex/scholar/metadata_engines/utils/_URLDOIExtractor.py +5 -10
  712. scitex/scholar/metadata_engines/utils/__init__.py +2 -0
  713. scitex/scholar/metadata_engines/utils/_metadata2bibtex.py +3 -0
  714. scitex/scholar/metadata_engines/utils/_standardize_metadata.py +2 -3
  715. scitex/scholar/pdf_download/ScholarPDFDownloader.py +25 -37
  716. scitex/scholar/pdf_download/strategies/chrome_pdf_viewer.py +11 -19
  717. scitex/scholar/pdf_download/strategies/direct_download.py +5 -9
  718. scitex/scholar/pdf_download/strategies/manual_download_fallback.py +3 -3
  719. scitex/scholar/pdf_download/strategies/manual_download_utils.py +6 -13
  720. scitex/scholar/pdf_download/strategies/open_access_download.py +49 -31
  721. scitex/scholar/pdf_download/strategies/response_body.py +8 -19
  722. scitex/scholar/pipelines/ScholarPipelineBibTeX.py +9 -18
  723. scitex/scholar/pipelines/ScholarPipelineMetadataParallel.py +25 -26
  724. scitex/scholar/pipelines/ScholarPipelineMetadataSingle.py +62 -23
  725. scitex/scholar/pipelines/ScholarPipelineParallel.py +13 -30
  726. scitex/scholar/pipelines/ScholarPipelineSearchParallel.py +299 -220
  727. scitex/scholar/pipelines/ScholarPipelineSearchSingle.py +202 -165
  728. scitex/scholar/pipelines/ScholarPipelineSingle.py +25 -51
  729. scitex/scholar/pipelines/SearchQueryParser.py +55 -55
  730. scitex/scholar/search_engines/ScholarSearchEngine.py +31 -27
  731. scitex/scholar/search_engines/_BaseSearchEngine.py +20 -23
  732. scitex/scholar/search_engines/individual/ArXivSearchEngine.py +53 -35
  733. scitex/scholar/search_engines/individual/CrossRefSearchEngine.py +47 -40
  734. scitex/scholar/search_engines/individual/OpenAlexSearchEngine.py +55 -50
  735. scitex/scholar/search_engines/individual/PubMedSearchEngine.py +8 -10
  736. scitex/scholar/search_engines/individual/SemanticScholarSearchEngine.py +55 -49
  737. scitex/scholar/storage/BibTeXHandler.py +150 -95
  738. scitex/scholar/storage/PaperIO.py +3 -6
  739. scitex/scholar/storage/ScholarLibrary.py +70 -49
  740. scitex/scholar/storage/_DeduplicationManager.py +52 -25
  741. scitex/scholar/storage/_LibraryCacheManager.py +19 -46
  742. scitex/scholar/storage/_LibraryManager.py +65 -175
  743. scitex/scholar/url_finder/ScholarURLFinder.py +9 -25
  744. scitex/scholar/url_finder/strategies/find_pdf_urls_by_direct_links.py +1 -1
  745. scitex/scholar/url_finder/strategies/find_pdf_urls_by_href.py +6 -10
  746. scitex/scholar/url_finder/strategies/find_pdf_urls_by_navigation.py +4 -6
  747. scitex/scholar/url_finder/strategies/find_pdf_urls_by_publisher_patterns.py +8 -15
  748. scitex/scholar/url_finder/strategies/find_pdf_urls_by_zotero_translators.py +3 -3
  749. scitex/scholar/url_finder/strategies/find_supplementary_urls_by_href.py +3 -3
  750. scitex/scholar/url_finder/translators/core/patterns.py +6 -4
  751. scitex/scholar/url_finder/translators/core/registry.py +6 -9
  752. scitex/scholar/url_finder/translators/individual/BOFiP_Impots.py +60 -52
  753. scitex/scholar/url_finder/translators/individual/Baidu_Scholar.py +54 -62
  754. scitex/scholar/url_finder/translators/individual/Bangkok_Post.py +38 -44
  755. scitex/scholar/url_finder/translators/individual/Baruch_Foundation.py +43 -47
  756. scitex/scholar/url_finder/translators/individual/Beobachter.py +46 -50
  757. scitex/scholar/url_finder/translators/individual/Bezneng_Gajit.py +37 -41
  758. scitex/scholar/url_finder/translators/individual/BibLaTeX.py +59 -52
  759. scitex/scholar/url_finder/translators/individual/BibTeX.py +83 -79
  760. scitex/scholar/url_finder/translators/individual/Biblio_com.py +48 -51
  761. scitex/scholar/url_finder/translators/individual/Bibliontology_RDF.py +58 -56
  762. scitex/scholar/url_finder/translators/individual/Camara_Brasileira_do_Livro_ISBN.py +102 -99
  763. scitex/scholar/url_finder/translators/individual/CanLII.py +49 -43
  764. scitex/scholar/url_finder/translators/individual/Canada_com.py +36 -40
  765. scitex/scholar/url_finder/translators/individual/Canadian_Letters_and_Images.py +43 -43
  766. scitex/scholar/url_finder/translators/individual/Canadiana_ca.py +77 -66
  767. scitex/scholar/url_finder/translators/individual/Cascadilla_Proceedings_Project.py +68 -62
  768. scitex/scholar/url_finder/translators/individual/Central_and_Eastern_European_Online_Library_Journals.py +60 -60
  769. scitex/scholar/url_finder/translators/individual/Champlain_Society_Collection.py +63 -61
  770. scitex/scholar/url_finder/translators/individual/Chicago_Journal_of_Theoretical_Computer_Science.py +74 -58
  771. scitex/scholar/url_finder/translators/individual/Christian_Science_Monitor.py +32 -38
  772. scitex/scholar/url_finder/translators/individual/Columbia_University_Press.py +51 -47
  773. scitex/scholar/url_finder/translators/individual/Common_Place.py +66 -57
  774. scitex/scholar/url_finder/translators/individual/Cornell_LII.py +66 -62
  775. scitex/scholar/url_finder/translators/individual/Cornell_University_Press.py +38 -45
  776. scitex/scholar/url_finder/translators/individual/CourtListener.py +52 -56
  777. scitex/scholar/url_finder/translators/individual/DAI_Zenon.py +53 -54
  778. scitex/scholar/url_finder/translators/individual/access_medicine.py +27 -33
  779. scitex/scholar/url_finder/translators/individual/acm.py +1 -1
  780. scitex/scholar/url_finder/translators/individual/acm_digital_library.py +93 -63
  781. scitex/scholar/url_finder/translators/individual/airiti.py +3 -1
  782. scitex/scholar/url_finder/translators/individual/aosic.py +3 -1
  783. scitex/scholar/url_finder/translators/individual/archive_ouverte_aosic.py +3 -1
  784. scitex/scholar/url_finder/translators/individual/archive_ouverte_en_sciences_de_l_information_et_de_la_communication___aosic_.py +6 -2
  785. scitex/scholar/url_finder/translators/individual/artforum.py +35 -27
  786. scitex/scholar/url_finder/translators/individual/arxiv.py +1 -1
  787. scitex/scholar/url_finder/translators/individual/arxiv_org.py +8 -4
  788. scitex/scholar/url_finder/translators/individual/atlanta_journal_constitution.py +22 -18
  789. scitex/scholar/url_finder/translators/individual/atypon_journals.py +19 -11
  790. scitex/scholar/url_finder/translators/individual/austlii_and_nzlii.py +48 -44
  791. scitex/scholar/url_finder/translators/individual/australian_dictionary_of_biography.py +21 -17
  792. scitex/scholar/url_finder/translators/individual/bailii.py +22 -19
  793. scitex/scholar/url_finder/translators/individual/bbc.py +46 -42
  794. scitex/scholar/url_finder/translators/individual/bbc_genome.py +37 -25
  795. scitex/scholar/url_finder/translators/individual/biblioteca_nacional_de_maestros.py +24 -20
  796. scitex/scholar/url_finder/translators/individual/bibliotheque_archives_nationale_quebec_pistard.py +42 -43
  797. scitex/scholar/url_finder/translators/individual/bibliotheque_archives_nationales_quebec.py +87 -81
  798. scitex/scholar/url_finder/translators/individual/bibliotheque_nationale_france.py +39 -37
  799. scitex/scholar/url_finder/translators/individual/bibsys.py +32 -28
  800. scitex/scholar/url_finder/translators/individual/bioconductor.py +58 -52
  801. scitex/scholar/url_finder/translators/individual/biomed_central.py +23 -15
  802. scitex/scholar/url_finder/translators/individual/biorxiv.py +26 -13
  803. scitex/scholar/url_finder/translators/individual/blogger.py +39 -43
  804. scitex/scholar/url_finder/translators/individual/bloomberg.py +48 -52
  805. scitex/scholar/url_finder/translators/individual/bloomsbury_food_library.py +37 -37
  806. scitex/scholar/url_finder/translators/individual/bluesky.py +30 -28
  807. scitex/scholar/url_finder/translators/individual/bnf_isbn.py +1 -1
  808. scitex/scholar/url_finder/translators/individual/bocc.py +66 -60
  809. scitex/scholar/url_finder/translators/individual/boe.py +52 -52
  810. scitex/scholar/url_finder/translators/individual/brill.py +3 -1
  811. scitex/scholar/url_finder/translators/individual/business_standard.py +36 -38
  812. scitex/scholar/url_finder/translators/individual/cabi_cab_abstracts.py +39 -41
  813. scitex/scholar/url_finder/translators/individual/cambridge.py +3 -1
  814. scitex/scholar/url_finder/translators/individual/cambridge_core.py +30 -24
  815. scitex/scholar/url_finder/translators/individual/caod.py +50 -46
  816. scitex/scholar/url_finder/translators/individual/cbc.py +91 -67
  817. scitex/scholar/url_finder/translators/individual/ccfr_bnf.py +49 -53
  818. scitex/scholar/url_finder/translators/individual/cia_world_factbook.py +43 -33
  819. scitex/scholar/url_finder/translators/individual/crossref_rest.py +208 -174
  820. scitex/scholar/url_finder/translators/individual/current_affairs.py +29 -35
  821. scitex/scholar/url_finder/translators/individual/dabi.py +70 -66
  822. scitex/scholar/url_finder/translators/individual/dagens_nyheter.py +3 -1
  823. scitex/scholar/url_finder/translators/individual/dagstuhl.py +10 -15
  824. scitex/scholar/url_finder/translators/individual/dar_almandumah.py +13 -9
  825. scitex/scholar/url_finder/translators/individual/dart_europe.py +19 -22
  826. scitex/scholar/url_finder/translators/individual/data_gov.py +2 -2
  827. scitex/scholar/url_finder/translators/individual/databrary.py +27 -28
  828. scitex/scholar/url_finder/translators/individual/datacite_json.py +152 -137
  829. scitex/scholar/url_finder/translators/individual/dataverse.py +68 -64
  830. scitex/scholar/url_finder/translators/individual/daum_news.py +38 -38
  831. scitex/scholar/url_finder/translators/individual/dblp.py +4 -8
  832. scitex/scholar/url_finder/translators/individual/dblp_computer_science_bibliography.py +8 -3
  833. scitex/scholar/url_finder/translators/individual/dbpia.py +5 -3
  834. scitex/scholar/url_finder/translators/individual/defense_technical_information_center.py +30 -28
  835. scitex/scholar/url_finder/translators/individual/delpher.py +102 -79
  836. scitex/scholar/url_finder/translators/individual/demographic_research.py +35 -31
  837. scitex/scholar/url_finder/translators/individual/denik_cz.py +58 -54
  838. scitex/scholar/url_finder/translators/individual/depatisnet.py +7 -10
  839. scitex/scholar/url_finder/translators/individual/der_freitag.py +81 -66
  840. scitex/scholar/url_finder/translators/individual/der_spiegel.py +56 -54
  841. scitex/scholar/url_finder/translators/individual/digibib_net.py +3 -1
  842. scitex/scholar/url_finder/translators/individual/digizeitschriften.py +3 -1
  843. scitex/scholar/url_finder/translators/individual/dpla.py +13 -14
  844. scitex/scholar/url_finder/translators/individual/dspace.py +2 -2
  845. scitex/scholar/url_finder/translators/individual/ebrary.py +3 -1
  846. scitex/scholar/url_finder/translators/individual/ebscohost.py +3 -1
  847. scitex/scholar/url_finder/translators/individual/electronic_colloquium_on_computational_complexity.py +3 -1
  848. scitex/scholar/url_finder/translators/individual/elife.py +3 -1
  849. scitex/scholar/url_finder/translators/individual/elsevier_health_journals.py +3 -1
  850. scitex/scholar/url_finder/translators/individual/emerald.py +3 -1
  851. scitex/scholar/url_finder/translators/individual/emerald_insight.py +3 -1
  852. scitex/scholar/url_finder/translators/individual/epicurious.py +3 -1
  853. scitex/scholar/url_finder/translators/individual/eurogamerusgamer.py +3 -1
  854. scitex/scholar/url_finder/translators/individual/fachportal_padagogik.py +3 -1
  855. scitex/scholar/url_finder/translators/individual/frontiers.py +1 -1
  856. scitex/scholar/url_finder/translators/individual/gale_databases.py +3 -1
  857. scitex/scholar/url_finder/translators/individual/gms_german_medical_science.py +6 -2
  858. scitex/scholar/url_finder/translators/individual/ieee_computer_society.py +6 -2
  859. scitex/scholar/url_finder/translators/individual/ieee_xplore.py +41 -35
  860. scitex/scholar/url_finder/translators/individual/inter_research_science_center.py +6 -2
  861. scitex/scholar/url_finder/translators/individual/jisc_historical_texts.py +3 -1
  862. scitex/scholar/url_finder/translators/individual/jstor.py +14 -12
  863. scitex/scholar/url_finder/translators/individual/korean_national_library.py +3 -1
  864. scitex/scholar/url_finder/translators/individual/la_times.py +3 -1
  865. scitex/scholar/url_finder/translators/individual/landesbibliographie_baden_wurttemberg.py +3 -1
  866. scitex/scholar/url_finder/translators/individual/legislative_insight.py +3 -1
  867. scitex/scholar/url_finder/translators/individual/libraries_tasmania.py +3 -1
  868. scitex/scholar/url_finder/translators/individual/library_catalog__koha_.py +3 -1
  869. scitex/scholar/url_finder/translators/individual/lingbuzz.py +2 -2
  870. scitex/scholar/url_finder/translators/individual/max_planck_institute_for_the_history_of_science_virtual_laboratory_library.py +3 -1
  871. scitex/scholar/url_finder/translators/individual/mdpi.py +12 -6
  872. scitex/scholar/url_finder/translators/individual/microbiology_society_journals.py +3 -1
  873. scitex/scholar/url_finder/translators/individual/midas_journals.py +3 -1
  874. scitex/scholar/url_finder/translators/individual/nagoya_university_opac.py +3 -1
  875. scitex/scholar/url_finder/translators/individual/nature_publishing_group.py +32 -19
  876. scitex/scholar/url_finder/translators/individual/ntsb_accident_reports.py +3 -1
  877. scitex/scholar/url_finder/translators/individual/openedition_journals.py +8 -4
  878. scitex/scholar/url_finder/translators/individual/orcid.py +16 -15
  879. scitex/scholar/url_finder/translators/individual/oxford.py +25 -19
  880. scitex/scholar/url_finder/translators/individual/oxford_dictionaries_premium.py +3 -1
  881. scitex/scholar/url_finder/translators/individual/ozon_ru.py +3 -1
  882. scitex/scholar/url_finder/translators/individual/plos.py +9 -12
  883. scitex/scholar/url_finder/translators/individual/polygon.py +3 -1
  884. scitex/scholar/url_finder/translators/individual/primo.py +3 -1
  885. scitex/scholar/url_finder/translators/individual/project_muse.py +3 -1
  886. scitex/scholar/url_finder/translators/individual/pubfactory_journals.py +3 -1
  887. scitex/scholar/url_finder/translators/individual/pubmed.py +71 -65
  888. scitex/scholar/url_finder/translators/individual/pubmed_central.py +8 -6
  889. scitex/scholar/url_finder/translators/individual/rechtspraak_nl.py +3 -1
  890. scitex/scholar/url_finder/translators/individual/sage_journals.py +25 -17
  891. scitex/scholar/url_finder/translators/individual/sciencedirect.py +36 -17
  892. scitex/scholar/url_finder/translators/individual/semantics_visual_library.py +3 -1
  893. scitex/scholar/url_finder/translators/individual/silverchair.py +70 -52
  894. scitex/scholar/url_finder/translators/individual/sora.py +3 -1
  895. scitex/scholar/url_finder/translators/individual/springer.py +15 -11
  896. scitex/scholar/url_finder/translators/individual/ssrn.py +3 -3
  897. scitex/scholar/url_finder/translators/individual/stanford_encyclopedia_of_philosophy.py +3 -1
  898. scitex/scholar/url_finder/translators/individual/superlib.py +3 -1
  899. scitex/scholar/url_finder/translators/individual/treesearch.py +3 -1
  900. scitex/scholar/url_finder/translators/individual/university_of_chicago_press_books.py +3 -1
  901. scitex/scholar/url_finder/translators/individual/vlex.py +3 -1
  902. scitex/scholar/url_finder/translators/individual/web_of_science.py +3 -1
  903. scitex/scholar/url_finder/translators/individual/web_of_science_nextgen.py +3 -1
  904. scitex/scholar/url_finder/translators/individual/wiley.py +31 -25
  905. scitex/scholar/url_finder/translators/individual/wilson_center_digital_archive.py +3 -1
  906. scitex/scholar/utils/bibtex/_parse_bibtex.py +3 -3
  907. scitex/scholar/utils/cleanup/_cleanup_scholar_processes.py +5 -9
  908. scitex/scholar/utils/text/_TextNormalizer.py +249 -176
  909. scitex/scholar/utils/validation/DOIValidator.py +31 -28
  910. scitex/scholar/utils/validation/__init__.py +0 -0
  911. scitex/scholar/utils/validation/validate_library_dois.py +61 -57
  912. scitex/scholar/zotero/__init__.py +1 -1
  913. scitex/security/cli.py +7 -20
  914. scitex/security/github.py +45 -32
  915. scitex/session/__init__.py +8 -9
  916. scitex/session/_decorator.py +49 -42
  917. scitex/session/_lifecycle.py +39 -39
  918. scitex/session/_manager.py +24 -20
  919. scitex/sh/__init__.py +4 -3
  920. scitex/sh/_execute.py +10 -7
  921. scitex/sh/_security.py +3 -3
  922. scitex/sh/_types.py +2 -3
  923. scitex/stats/__init__.py +57 -6
  924. scitex/stats/_schema.py +42 -569
  925. scitex/stats/auto/__init__.py +188 -0
  926. scitex/stats/auto/_context.py +331 -0
  927. scitex/stats/auto/_formatting.py +679 -0
  928. scitex/stats/auto/_rules.py +901 -0
  929. scitex/stats/auto/_selector.py +554 -0
  930. scitex/stats/auto/_styles.py +721 -0
  931. scitex/stats/correct/__init__.py +4 -4
  932. scitex/stats/correct/_correct_bonferroni.py +43 -34
  933. scitex/stats/correct/_correct_fdr.py +14 -40
  934. scitex/stats/correct/_correct_fdr_.py +39 -46
  935. scitex/stats/correct/_correct_holm.py +14 -32
  936. scitex/stats/correct/_correct_sidak.py +36 -21
  937. scitex/stats/descriptive/_circular.py +20 -21
  938. scitex/stats/descriptive/_describe.py +19 -5
  939. scitex/stats/descriptive/_nan.py +5 -7
  940. scitex/stats/descriptive/_real.py +4 -3
  941. scitex/stats/effect_sizes/__init__.py +10 -11
  942. scitex/stats/effect_sizes/_cliffs_delta.py +35 -32
  943. scitex/stats/effect_sizes/_cohens_d.py +30 -31
  944. scitex/stats/effect_sizes/_epsilon_squared.py +19 -22
  945. scitex/stats/effect_sizes/_eta_squared.py +23 -27
  946. scitex/stats/effect_sizes/_prob_superiority.py +18 -21
  947. scitex/stats/posthoc/__init__.py +3 -3
  948. scitex/stats/posthoc/_dunnett.py +75 -55
  949. scitex/stats/posthoc/_games_howell.py +61 -43
  950. scitex/stats/posthoc/_tukey_hsd.py +42 -34
  951. scitex/stats/power/__init__.py +2 -2
  952. scitex/stats/power/_power.py +56 -56
  953. scitex/stats/tests/__init__.py +1 -1
  954. scitex/stats/tests/correlation/__init__.py +1 -1
  955. scitex/stats/tests/correlation/_test_pearson.py +28 -38
  956. scitex/stats/utils/__init__.py +14 -17
  957. scitex/stats/utils/_effect_size.py +85 -78
  958. scitex/stats/utils/_formatters.py +49 -43
  959. scitex/stats/utils/_normalizers.py +7 -14
  960. scitex/stats/utils/_power.py +56 -56
  961. scitex/str/__init__.py +1 -0
  962. scitex/str/_clean_path.py +3 -3
  963. scitex/str/_factor_out_digits.py +86 -58
  964. scitex/str/_format_plot_text.py +180 -111
  965. scitex/str/_latex.py +19 -19
  966. scitex/str/_latex_fallback.py +9 -10
  967. scitex/str/_parse.py +3 -6
  968. scitex/str/_print_debug.py +13 -13
  969. scitex/str/_printc.py +2 -0
  970. scitex/str/_search.py +3 -3
  971. scitex/template/.legacy/_clone_project.py +9 -13
  972. scitex/template/__init__.py +10 -2
  973. scitex/template/_clone_project.py +7 -2
  974. scitex/template/_copy.py +1 -0
  975. scitex/template/_customize.py +3 -6
  976. scitex/template/_git_strategy.py +2 -3
  977. scitex/template/_rename.py +1 -0
  978. scitex/template/clone_pip_project.py +6 -7
  979. scitex/template/clone_research.py +7 -10
  980. scitex/template/clone_singularity.py +6 -7
  981. scitex/template/clone_writer_directory.py +6 -7
  982. scitex/tex/_preview.py +26 -11
  983. scitex/tex/_to_vec.py +10 -7
  984. scitex/torch/__init__.py +11 -1
  985. scitex/types/_ArrayLike.py +2 -0
  986. scitex/types/_is_listed_X.py +3 -3
  987. scitex/units.py +110 -77
  988. scitex/utils/_compress_hdf5.py +3 -3
  989. scitex/utils/_email.py +8 -4
  990. scitex/utils/_notify.py +14 -8
  991. scitex/utils/_search.py +6 -6
  992. scitex/utils/_verify_scitex_format.py +17 -42
  993. scitex/utils/_verify_scitex_format_v01.py +12 -34
  994. scitex/utils/template.py +4 -3
  995. scitex/vis/__init__.py +0 -0
  996. scitex/vis/backend/__init__.py +3 -3
  997. scitex/vis/backend/{export.py → _export.py} +1 -1
  998. scitex/vis/backend/{parser.py → _parser.py} +1 -3
  999. scitex/vis/backend/{render.py → _render.py} +1 -1
  1000. scitex/vis/canvas.py +15 -3
  1001. scitex/vis/editor/__init__.py +0 -0
  1002. scitex/vis/editor/_dearpygui_editor.py +450 -304
  1003. scitex/vis/editor/_defaults.py +114 -123
  1004. scitex/vis/editor/_edit.py +38 -26
  1005. scitex/vis/editor/_flask_editor.py +8 -8
  1006. scitex/vis/editor/_mpl_editor.py +63 -48
  1007. scitex/vis/editor/_qt_editor.py +210 -159
  1008. scitex/vis/editor/_tkinter_editor.py +146 -89
  1009. scitex/vis/editor/flask_editor/__init__.py +10 -10
  1010. scitex/vis/editor/flask_editor/_bbox.py +529 -0
  1011. scitex/vis/editor/flask_editor/{core.py → _core.py} +45 -29
  1012. scitex/vis/editor/flask_editor/_plotter.py +567 -0
  1013. scitex/vis/editor/flask_editor/_renderer.py +393 -0
  1014. scitex/vis/editor/flask_editor/{utils.py → _utils.py} +13 -14
  1015. scitex/vis/editor/flask_editor/templates/__init__.py +5 -5
  1016. scitex/vis/editor/flask_editor/templates/{html.py → _html.py} +234 -16
  1017. scitex/vis/editor/flask_editor/templates/_scripts.py +1261 -0
  1018. scitex/vis/editor/flask_editor/templates/{styles.py → _styles.py} +192 -2
  1019. scitex/vis/io/__init__.py +5 -5
  1020. scitex/vis/io/{canvas.py → _canvas.py} +8 -4
  1021. scitex/vis/io/{data.py → _data.py} +13 -9
  1022. scitex/vis/io/{directory.py → _directory.py} +7 -4
  1023. scitex/vis/io/{export.py → _export.py} +15 -12
  1024. scitex/vis/io/{load.py → _load.py} +1 -1
  1025. scitex/vis/io/{panel.py → _panel.py} +21 -13
  1026. scitex/vis/io/{save.py → _save.py} +0 -0
  1027. scitex/vis/model/__init__.py +7 -7
  1028. scitex/vis/model/{annotations.py → _annotations.py} +2 -4
  1029. scitex/vis/model/{axes.py → _axes.py} +1 -1
  1030. scitex/vis/model/{figure.py → _figure.py} +0 -0
  1031. scitex/vis/model/{guides.py → _guides.py} +1 -1
  1032. scitex/vis/model/{plot.py → _plot.py} +2 -4
  1033. scitex/vis/model/{plot_types.py → _plot_types.py} +0 -0
  1034. scitex/vis/model/{styles.py → _styles.py} +0 -0
  1035. scitex/vis/utils/__init__.py +2 -2
  1036. scitex/vis/utils/{defaults.py → _defaults.py} +1 -2
  1037. scitex/vis/utils/{validate.py → _validate.py} +3 -9
  1038. scitex/web/__init__.py +7 -1
  1039. scitex/web/_scraping.py +54 -38
  1040. scitex/web/_search_pubmed.py +30 -14
  1041. scitex/writer/.legacy/Writer_v01-refactored.py +4 -4
  1042. scitex/writer/.legacy/_compile.py +18 -28
  1043. scitex/writer/Writer.py +8 -21
  1044. scitex/writer/__init__.py +11 -11
  1045. scitex/writer/_clone_writer_project.py +2 -6
  1046. scitex/writer/_compile/__init__.py +1 -0
  1047. scitex/writer/_compile/_parser.py +1 -0
  1048. scitex/writer/_compile/_runner.py +35 -38
  1049. scitex/writer/_compile/_validator.py +1 -0
  1050. scitex/writer/_compile/manuscript.py +1 -0
  1051. scitex/writer/_compile/revision.py +1 -0
  1052. scitex/writer/_compile/supplementary.py +1 -0
  1053. scitex/writer/_compile_async.py +5 -12
  1054. scitex/writer/_project/__init__.py +1 -0
  1055. scitex/writer/_project/_create.py +10 -25
  1056. scitex/writer/_project/_trees.py +4 -9
  1057. scitex/writer/_project/_validate.py +2 -3
  1058. scitex/writer/_validate_tree_structures.py +7 -18
  1059. scitex/writer/dataclasses/__init__.py +8 -10
  1060. scitex/writer/dataclasses/config/_CONSTANTS.py +2 -3
  1061. scitex/writer/dataclasses/config/_WriterConfig.py +4 -9
  1062. scitex/writer/dataclasses/contents/_ManuscriptContents.py +14 -25
  1063. scitex/writer/dataclasses/contents/_RevisionContents.py +21 -16
  1064. scitex/writer/dataclasses/contents/_SupplementaryContents.py +21 -24
  1065. scitex/writer/dataclasses/core/_Document.py +2 -3
  1066. scitex/writer/dataclasses/core/_DocumentSection.py +8 -23
  1067. scitex/writer/dataclasses/results/_CompilationResult.py +2 -3
  1068. scitex/writer/dataclasses/results/_LaTeXIssue.py +3 -6
  1069. scitex/writer/dataclasses/results/_SaveSectionsResponse.py +20 -9
  1070. scitex/writer/dataclasses/results/_SectionReadResponse.py +24 -10
  1071. scitex/writer/dataclasses/tree/_ConfigTree.py +7 -4
  1072. scitex/writer/dataclasses/tree/_ManuscriptTree.py +10 -13
  1073. scitex/writer/dataclasses/tree/_RevisionTree.py +16 -17
  1074. scitex/writer/dataclasses/tree/_ScriptsTree.py +10 -5
  1075. scitex/writer/dataclasses/tree/_SharedTree.py +10 -13
  1076. scitex/writer/dataclasses/tree/_SupplementaryTree.py +15 -14
  1077. scitex/writer/utils/.legacy_git_retry.py +3 -8
  1078. scitex/writer/utils/_parse_latex_logs.py +2 -3
  1079. scitex/writer/utils/_parse_script_args.py +20 -23
  1080. scitex/writer/utils/_watch.py +5 -5
  1081. {scitex-2.5.0.dist-info → scitex-2.7.0.dist-info}/METADATA +4 -10
  1082. {scitex-2.5.0.dist-info → scitex-2.7.0.dist-info}/RECORD +1071 -975
  1083. scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin_v01-indentation-issues.py +0 -583
  1084. scitex/plt/_subplots/_export_as_csv_formatters.py +0 -112
  1085. scitex/vis/editor/flask_editor/bbox.py +0 -216
  1086. scitex/vis/editor/flask_editor/plotter.py +0 -130
  1087. scitex/vis/editor/flask_editor/renderer.py +0 -184
  1088. scitex/vis/editor/flask_editor/templates/scripts.py +0 -614
  1089. {scitex-2.5.0.dist-info → scitex-2.7.0.dist-info}/WHEEL +0 -0
  1090. {scitex-2.5.0.dist-info → scitex-2.7.0.dist-info}/entry_points.txt +0 -0
  1091. {scitex-2.5.0.dist-info → scitex-2.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,1261 @@
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