scitex 2.8.1__py3-none-any.whl → 2.10.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 (415) hide show
  1. scitex/__init__.py +15 -7
  2. scitex/__version__.py +1 -2
  3. scitex/_install_guide.py +250 -0
  4. scitex/_optional_deps.py +206 -39
  5. scitex/ai/_gen_ai/_Groq.py +2 -4
  6. scitex/ai/_gen_ai/_OpenAI.py +5 -2
  7. scitex/ai/_gen_ai/_Perplexity.py +20 -6
  8. scitex/audio/__init__.py +24 -15
  9. scitex/audio/_cross_process_lock.py +139 -0
  10. scitex/audio/_mcp_handlers.py +256 -0
  11. scitex/audio/_mcp_tool_schemas.py +203 -0
  12. scitex/audio/engines/elevenlabs_engine.py +5 -2
  13. scitex/audio/mcp_server.py +98 -457
  14. scitex/bridge/__init__.py +30 -19
  15. scitex/bridge/_figrecipe.py +245 -0
  16. scitex/bridge/_helpers.py +2 -1
  17. scitex/bridge/_plt_vis.py +23 -10
  18. scitex/bridge/_stats_plt.py +18 -5
  19. scitex/bridge/_stats_vis.py +16 -2
  20. scitex/browser/__init__.py +84 -44
  21. scitex/browser/automation/__init__.py +5 -1
  22. scitex/browser/core/BrowserMixin.py +17 -4
  23. scitex/browser/core/__init__.py +11 -2
  24. scitex/browser/remote/CaptchaHandler.py +1 -1
  25. scitex/browser/remote/ZenRowsAPIClient.py +1 -1
  26. scitex/capture/grid.py +487 -0
  27. scitex/capture/mcp_handlers.py +401 -0
  28. scitex/capture/mcp_tool_defs.py +192 -0
  29. scitex/capture/mcp_tools.py +241 -0
  30. scitex/capture/mcp_utils.py +30 -0
  31. scitex/cli/convert.py +421 -0
  32. scitex/cli/main.py +6 -4
  33. scitex/datetime/__init__.py +46 -0
  34. scitex/datetime/_linspace.py +100 -0
  35. scitex/datetime/_normalize_timestamp.py +306 -0
  36. scitex/db/_delete_duplicates.py +4 -4
  37. scitex/db/_sqlite3/_delete_duplicates.py +11 -2
  38. scitex/dev/plt/__init__.py +61 -62
  39. scitex/dev/plt/demo_plotters/__init__.py +0 -0
  40. scitex/dev/plt/demo_plotters/plot_mpl_axhline.py +28 -0
  41. scitex/dev/plt/demo_plotters/plot_mpl_axhspan.py +28 -0
  42. scitex/dev/plt/demo_plotters/plot_mpl_axvline.py +28 -0
  43. scitex/dev/plt/demo_plotters/plot_mpl_axvspan.py +28 -0
  44. scitex/dev/plt/demo_plotters/plot_mpl_bar.py +29 -0
  45. scitex/dev/plt/demo_plotters/plot_mpl_barh.py +29 -0
  46. scitex/dev/plt/demo_plotters/plot_mpl_boxplot.py +28 -0
  47. scitex/dev/plt/demo_plotters/plot_mpl_contour.py +31 -0
  48. scitex/dev/plt/demo_plotters/plot_mpl_contourf.py +31 -0
  49. scitex/dev/plt/demo_plotters/plot_mpl_errorbar.py +30 -0
  50. scitex/dev/plt/demo_plotters/plot_mpl_eventplot.py +28 -0
  51. scitex/dev/plt/demo_plotters/plot_mpl_fill.py +30 -0
  52. scitex/dev/plt/demo_plotters/plot_mpl_fill_between.py +31 -0
  53. scitex/dev/plt/demo_plotters/plot_mpl_hexbin.py +28 -0
  54. scitex/dev/plt/demo_plotters/plot_mpl_hist.py +28 -0
  55. scitex/dev/plt/demo_plotters/plot_mpl_hist2d.py +28 -0
  56. scitex/dev/plt/demo_plotters/plot_mpl_imshow.py +29 -0
  57. scitex/dev/plt/demo_plotters/plot_mpl_pcolormesh.py +31 -0
  58. scitex/dev/plt/demo_plotters/plot_mpl_pie.py +29 -0
  59. scitex/dev/plt/demo_plotters/plot_mpl_plot.py +29 -0
  60. scitex/dev/plt/demo_plotters/plot_mpl_quiver.py +31 -0
  61. scitex/dev/plt/demo_plotters/plot_mpl_scatter.py +28 -0
  62. scitex/dev/plt/demo_plotters/plot_mpl_stackplot.py +31 -0
  63. scitex/dev/plt/demo_plotters/plot_mpl_stem.py +29 -0
  64. scitex/dev/plt/demo_plotters/plot_mpl_step.py +29 -0
  65. scitex/dev/plt/demo_plotters/plot_mpl_violinplot.py +28 -0
  66. scitex/dev/plt/demo_plotters/plot_sns_barplot.py +29 -0
  67. scitex/dev/plt/demo_plotters/plot_sns_boxplot.py +29 -0
  68. scitex/dev/plt/demo_plotters/plot_sns_heatmap.py +28 -0
  69. scitex/dev/plt/demo_plotters/plot_sns_histplot.py +29 -0
  70. scitex/dev/plt/demo_plotters/plot_sns_kdeplot.py +29 -0
  71. scitex/dev/plt/demo_plotters/plot_sns_lineplot.py +31 -0
  72. scitex/dev/plt/demo_plotters/plot_sns_scatterplot.py +29 -0
  73. scitex/dev/plt/demo_plotters/plot_sns_stripplot.py +29 -0
  74. scitex/dev/plt/demo_plotters/plot_sns_swarmplot.py +29 -0
  75. scitex/dev/plt/demo_plotters/plot_sns_violinplot.py +29 -0
  76. scitex/dev/plt/demo_plotters/plot_stx_bar.py +29 -0
  77. scitex/dev/plt/demo_plotters/plot_stx_barh.py +29 -0
  78. scitex/dev/plt/demo_plotters/plot_stx_box.py +28 -0
  79. scitex/dev/plt/demo_plotters/plot_stx_boxplot.py +28 -0
  80. scitex/dev/plt/demo_plotters/plot_stx_conf_mat.py +28 -0
  81. scitex/dev/plt/demo_plotters/plot_stx_contour.py +31 -0
  82. scitex/dev/plt/demo_plotters/plot_stx_ecdf.py +28 -0
  83. scitex/dev/plt/demo_plotters/plot_stx_errorbar.py +30 -0
  84. scitex/dev/plt/demo_plotters/plot_stx_fill_between.py +31 -0
  85. scitex/dev/plt/demo_plotters/plot_stx_fillv.py +28 -0
  86. scitex/dev/plt/demo_plotters/plot_stx_heatmap.py +28 -0
  87. scitex/dev/plt/demo_plotters/plot_stx_image.py +28 -0
  88. scitex/dev/plt/demo_plotters/plot_stx_imshow.py +28 -0
  89. scitex/dev/plt/demo_plotters/plot_stx_joyplot.py +28 -0
  90. scitex/dev/plt/demo_plotters/plot_stx_kde.py +28 -0
  91. scitex/dev/plt/demo_plotters/plot_stx_line.py +28 -0
  92. scitex/dev/plt/demo_plotters/plot_stx_mean_ci.py +28 -0
  93. scitex/dev/plt/demo_plotters/plot_stx_mean_std.py +28 -0
  94. scitex/dev/plt/demo_plotters/plot_stx_median_iqr.py +28 -0
  95. scitex/dev/plt/demo_plotters/plot_stx_raster.py +28 -0
  96. scitex/dev/plt/demo_plotters/plot_stx_rectangle.py +28 -0
  97. scitex/dev/plt/demo_plotters/plot_stx_scatter.py +29 -0
  98. scitex/dev/plt/demo_plotters/plot_stx_shaded_line.py +29 -0
  99. scitex/dev/plt/demo_plotters/plot_stx_violin.py +28 -0
  100. scitex/dev/plt/demo_plotters/plot_stx_violinplot.py +28 -0
  101. scitex/dev/plt/mpl/get_dir_ax.py +46 -0
  102. scitex/dev/plt/mpl/get_signatures.py +176 -0
  103. scitex/dev/plt/mpl/get_signatures_details.py +522 -0
  104. scitex/dict/_pop_keys.py +1 -7
  105. scitex/dsp/__init__.py +15 -10
  106. scitex/dsp/add_noise.py +5 -2
  107. scitex/dsp/example.py +35 -22
  108. scitex/dsp/filt.py +8 -3
  109. scitex/dsp/reference.py +3 -2
  110. scitex/dsp/utils/__init__.py +2 -1
  111. scitex/dsp/utils/_differential_bandpass_filters.py +14 -4
  112. scitex/dt/__init__.py +39 -2
  113. scitex/errors.py +82 -521
  114. scitex/fig/__init__.py +4 -4
  115. scitex/fig/editor/edit/panel_loader.py +1 -1
  116. scitex/fig/io/_bundle.py +7 -7
  117. scitex/fts/README.md +262 -0
  118. scitex/fts/TODO.md +66 -0
  119. scitex/fts/__init__.py +90 -0
  120. scitex/fts/_bundle/README_IN_BUNDLE.md +102 -0
  121. scitex/fts/_bundle/_FTS.py +657 -0
  122. scitex/fts/_bundle/__init__.py +38 -0
  123. scitex/fts/_bundle/_children.py +216 -0
  124. scitex/fts/_bundle/_conversion/__init__.py +15 -0
  125. scitex/fts/_bundle/_conversion/_bundle2dict.py +44 -0
  126. scitex/fts/_bundle/_conversion/_dict2bundle.py +50 -0
  127. scitex/fts/_bundle/_dataclasses/_Axes.py +57 -0
  128. scitex/fts/_bundle/_dataclasses/_BBox.py +54 -0
  129. scitex/fts/_bundle/_dataclasses/_ColumnDef.py +72 -0
  130. scitex/fts/_bundle/_dataclasses/_DataFormat.py +40 -0
  131. scitex/fts/_bundle/_dataclasses/_DataInfo.py +135 -0
  132. scitex/fts/_bundle/_dataclasses/_DataSource.py +44 -0
  133. scitex/fts/_bundle/_dataclasses/_Node.py +319 -0
  134. scitex/fts/_bundle/_dataclasses/_NodeRefs.py +45 -0
  135. scitex/fts/_bundle/_dataclasses/_SizeMM.py +38 -0
  136. scitex/fts/_bundle/_dataclasses/__init__.py +35 -0
  137. scitex/fts/_bundle/_extractors/__init__.py +32 -0
  138. scitex/fts/_bundle/_extractors/_extract_bar.py +131 -0
  139. scitex/fts/_bundle/_extractors/_extract_line.py +71 -0
  140. scitex/fts/_bundle/_extractors/_extract_scatter.py +79 -0
  141. scitex/fts/_bundle/_loader.py +134 -0
  142. scitex/fts/_bundle/_mpl_helpers.py +389 -0
  143. scitex/fts/_bundle/_saver.py +269 -0
  144. scitex/fts/_bundle/_storage.py +200 -0
  145. scitex/fts/_bundle/_utils/__init__.py +55 -0
  146. scitex/fts/_bundle/_utils/_const.py +26 -0
  147. scitex/fts/_bundle/_utils/_errors.py +73 -0
  148. scitex/fts/_bundle/_utils/_generate.py +21 -0
  149. scitex/fts/_bundle/_utils/_types.py +76 -0
  150. scitex/fts/_bundle/_validation.py +434 -0
  151. scitex/fts/_bundle/_zipbundle.py +165 -0
  152. scitex/fts/_fig/__init__.py +22 -0
  153. scitex/fts/_fig/_backend/__init__.py +53 -0
  154. scitex/fts/_fig/_backend/_export.py +165 -0
  155. scitex/fts/_fig/_backend/_parser.py +188 -0
  156. scitex/fts/_fig/_backend/_render.py +538 -0
  157. scitex/fts/_fig/_composite.py +345 -0
  158. scitex/fts/_fig/_dataclasses/_ChannelEncoding.py +46 -0
  159. scitex/fts/_fig/_dataclasses/_Encoding.py +82 -0
  160. scitex/fts/_fig/_dataclasses/_Theme.py +441 -0
  161. scitex/fts/_fig/_dataclasses/_TraceEncoding.py +52 -0
  162. scitex/fts/_fig/_dataclasses/__init__.py +47 -0
  163. scitex/fts/_fig/_editor/__init__.py +14 -0
  164. scitex/fts/_fig/_editor/_cui/__init__.py +33 -0
  165. scitex/fts/_fig/_editor/_cui/_backend_detector.py +39 -0
  166. scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +366 -0
  167. scitex/fts/_fig/_editor/_cui/_editor_launcher.py +175 -0
  168. scitex/fts/_fig/_editor/_cui/_manual_handler.py +52 -0
  169. scitex/fts/_fig/_editor/_cui/_panel_loader.py +246 -0
  170. scitex/fts/_fig/_editor/_cui/_path_resolver.py +66 -0
  171. scitex/fts/_fig/_editor/_defaults.py +300 -0
  172. scitex/fts/_fig/_editor/_gui/__init__.py +11 -0
  173. scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +20 -0
  174. scitex/fts/_fig/_editor/_gui/_flask_editor/_bbox.py +1339 -0
  175. scitex/fts/_fig/_editor/_gui/_flask_editor/_core.py +1688 -0
  176. scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +664 -0
  177. scitex/fts/_fig/_editor/_gui/_flask_editor/_renderer.py +853 -0
  178. scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +79 -0
  179. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +41 -0
  180. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +16 -0
  181. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +85 -0
  182. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +217 -0
  183. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +93 -0
  184. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +57 -0
  185. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +112 -0
  186. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +59 -0
  187. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +212 -0
  188. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/canvas.css +176 -0
  189. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +190 -0
  190. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +59 -0
  191. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +45 -0
  192. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +95 -0
  193. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +101 -0
  194. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +138 -0
  195. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +31 -0
  196. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +7 -0
  197. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +56 -0
  198. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +78 -0
  199. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +314 -0
  200. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +107 -0
  201. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +54 -0
  202. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +172 -0
  203. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +258 -0
  204. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +48 -0
  205. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +71 -0
  206. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +288 -0
  207. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +143 -0
  208. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +245 -0
  209. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +992 -0
  210. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +339 -0
  211. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +286 -0
  212. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +371 -0
  213. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +293 -0
  214. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +426 -0
  215. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +152 -0
  216. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +265 -0
  217. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +184 -0
  218. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +57 -0
  219. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +100 -0
  220. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +34 -0
  221. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +124 -0
  222. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +851 -0
  223. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +4932 -0
  224. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +1657 -0
  225. scitex/fts/_fig/_editor/_gui/_flask_editor.py +36 -0
  226. scitex/fts/_fig/_models/_Annotations.py +115 -0
  227. scitex/fts/_fig/_models/_Axes.py +152 -0
  228. scitex/fts/_fig/_models/_Figure.py +138 -0
  229. scitex/fts/_fig/_models/_Guides.py +104 -0
  230. scitex/fts/_fig/_models/_Plot.py +123 -0
  231. scitex/fts/_fig/_models/_Styles.py +245 -0
  232. scitex/fts/_fig/_models/__init__.py +80 -0
  233. scitex/fts/_fig/_models/_plot_types/__init__.py +156 -0
  234. scitex/fts/_fig/_models/_plot_types/_bar.py +43 -0
  235. scitex/fts/_fig/_models/_plot_types/_box.py +38 -0
  236. scitex/fts/_fig/_models/_plot_types/_distribution.py +36 -0
  237. scitex/fts/_fig/_models/_plot_types/_errorbar.py +60 -0
  238. scitex/fts/_fig/_models/_plot_types/_histogram.py +30 -0
  239. scitex/fts/_fig/_models/_plot_types/_image.py +61 -0
  240. scitex/fts/_fig/_models/_plot_types/_line.py +57 -0
  241. scitex/fts/_fig/_models/_plot_types/_scatter.py +30 -0
  242. scitex/fts/_fig/_models/_plot_types/_seaborn.py +121 -0
  243. scitex/fts/_fig/_models/_plot_types/_violin.py +36 -0
  244. scitex/fts/_fig/_utils/__init__.py +129 -0
  245. scitex/fts/_fig/_utils/_auto_layout.py +127 -0
  246. scitex/fts/_fig/_utils/_calc_bounds.py +111 -0
  247. scitex/fts/_fig/_utils/_const_sizes.py +48 -0
  248. scitex/fts/_fig/_utils/_convert_coords.py +77 -0
  249. scitex/fts/_fig/_utils/_get_template.py +178 -0
  250. scitex/fts/_fig/_utils/_normalize.py +73 -0
  251. scitex/fts/_fig/_utils/_plot_layout.py +397 -0
  252. scitex/fts/_fig/_utils/_validate.py +197 -0
  253. scitex/fts/_kinds/__init__.py +45 -0
  254. scitex/fts/_kinds/_figure/__init__.py +19 -0
  255. scitex/fts/_kinds/_figure/_composite.py +345 -0
  256. scitex/fts/_kinds/_plot/__init__.py +25 -0
  257. scitex/fts/_kinds/_plot/_backend/__init__.py +53 -0
  258. scitex/fts/_kinds/_plot/_backend/_export.py +165 -0
  259. scitex/fts/_kinds/_plot/_backend/_parser.py +188 -0
  260. scitex/fts/_kinds/_plot/_backend/_render.py +538 -0
  261. scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +46 -0
  262. scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +82 -0
  263. scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +441 -0
  264. scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +52 -0
  265. scitex/fts/_kinds/_plot/_dataclasses/__init__.py +47 -0
  266. scitex/fts/_kinds/_plot/_models/_Annotations.py +115 -0
  267. scitex/fts/_kinds/_plot/_models/_Axes.py +152 -0
  268. scitex/fts/_kinds/_plot/_models/_Figure.py +138 -0
  269. scitex/fts/_kinds/_plot/_models/_Guides.py +104 -0
  270. scitex/fts/_kinds/_plot/_models/_Plot.py +123 -0
  271. scitex/fts/_kinds/_plot/_models/_Styles.py +245 -0
  272. scitex/fts/_kinds/_plot/_models/__init__.py +80 -0
  273. scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +156 -0
  274. scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +43 -0
  275. scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +38 -0
  276. scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +36 -0
  277. scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +60 -0
  278. scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +30 -0
  279. scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +61 -0
  280. scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +57 -0
  281. scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +30 -0
  282. scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +121 -0
  283. scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +36 -0
  284. scitex/fts/_kinds/_plot/_utils/__init__.py +129 -0
  285. scitex/fts/_kinds/_plot/_utils/_auto_layout.py +127 -0
  286. scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +111 -0
  287. scitex/fts/_kinds/_plot/_utils/_const_sizes.py +48 -0
  288. scitex/fts/_kinds/_plot/_utils/_convert_coords.py +77 -0
  289. scitex/fts/_kinds/_plot/_utils/_get_template.py +178 -0
  290. scitex/fts/_kinds/_plot/_utils/_normalize.py +73 -0
  291. scitex/fts/_kinds/_plot/_utils/_plot_layout.py +397 -0
  292. scitex/fts/_kinds/_plot/_utils/_validate.py +197 -0
  293. scitex/fts/_kinds/_shape/__init__.py +141 -0
  294. scitex/fts/_kinds/_stats/__init__.py +56 -0
  295. scitex/fts/_kinds/_stats/_dataclasses/_Stats.py +423 -0
  296. scitex/fts/_kinds/_stats/_dataclasses/__init__.py +48 -0
  297. scitex/fts/_kinds/_table/__init__.py +72 -0
  298. scitex/fts/_kinds/_table/_latex/__init__.py +93 -0
  299. scitex/fts/_kinds/_table/_latex/_editor/__init__.py +11 -0
  300. scitex/fts/_kinds/_table/_latex/_editor/_app.py +725 -0
  301. scitex/fts/_kinds/_table/_latex/_export.py +279 -0
  302. scitex/fts/_kinds/_table/_latex/_figure_exporter.py +153 -0
  303. scitex/fts/_kinds/_table/_latex/_stats_formatter.py +274 -0
  304. scitex/fts/_kinds/_table/_latex/_table_exporter.py +362 -0
  305. scitex/fts/_kinds/_table/_latex/_utils.py +369 -0
  306. scitex/fts/_kinds/_table/_latex/_validator.py +445 -0
  307. scitex/fts/_kinds/_text/__init__.py +77 -0
  308. scitex/fts/_schemas/data_info.schema.json +75 -0
  309. scitex/fts/_schemas/encoding.schema.json +90 -0
  310. scitex/fts/_schemas/node.schema.json +145 -0
  311. scitex/fts/_schemas/render_manifest.schema.json +62 -0
  312. scitex/fts/_schemas/stats.schema.json +132 -0
  313. scitex/fts/_schemas/theme.schema.json +141 -0
  314. scitex/fts/_stats/__init__.py +48 -0
  315. scitex/fts/_stats/_dataclasses/_Stats.py +423 -0
  316. scitex/fts/_stats/_dataclasses/__init__.py +48 -0
  317. scitex/fts/_tables/__init__.py +65 -0
  318. scitex/fts/_tables/_latex/__init__.py +93 -0
  319. scitex/fts/_tables/_latex/_editor/__init__.py +11 -0
  320. scitex/fts/_tables/_latex/_editor/_app.py +725 -0
  321. scitex/fts/_tables/_latex/_export.py +279 -0
  322. scitex/fts/_tables/_latex/_figure_exporter.py +153 -0
  323. scitex/fts/_tables/_latex/_stats_formatter.py +274 -0
  324. scitex/fts/_tables/_latex/_table_exporter.py +362 -0
  325. scitex/fts/_tables/_latex/_utils.py +369 -0
  326. scitex/fts/_tables/_latex/_validator.py +445 -0
  327. scitex/gen/__init__.py +66 -25
  328. scitex/gen/misc.py +28 -0
  329. scitex/io/__init__.py +47 -32
  330. scitex/io/_load.py +87 -36
  331. scitex/io/_load_modules/__init__.py +10 -7
  332. scitex/io/_load_modules/_pandas.py +6 -1
  333. scitex/io/_save.py +299 -1556
  334. scitex/io/_save_modules/__init__.py +76 -19
  335. scitex/io/_save_modules/_figure_utils.py +90 -0
  336. scitex/io/_save_modules/_image_csv.py +497 -0
  337. scitex/io/_save_modules/_legends.py +91 -0
  338. scitex/io/_save_modules/_pltz_bundle.py +356 -0
  339. scitex/io/_save_modules/_pltz_stx.py +536 -0
  340. scitex/io/_save_modules/_stx_bundle.py +104 -0
  341. scitex/io/_save_modules/_symlink.py +96 -0
  342. scitex/io/_save_modules/_yaml.py +1 -1
  343. scitex/io/_save_modules/_zarr.py +64 -18
  344. scitex/io/bundle/README.md +212 -0
  345. scitex/io/bundle/__init__.py +110 -0
  346. scitex/io/{_bundle.py → bundle/_core.py} +168 -97
  347. scitex/io/bundle/_nested.py +713 -0
  348. scitex/io/bundle/_types.py +74 -0
  349. scitex/io/{_zip_bundle.py → bundle/_zip.py} +93 -45
  350. scitex/io/utils/h5_to_zarr.py +1 -1
  351. scitex/logging/__init__.py +108 -13
  352. scitex/logging/_errors.py +508 -0
  353. scitex/logging/_formatters.py +30 -6
  354. scitex/logging/_warnings.py +261 -0
  355. scitex/plt/__init__.py +4 -1
  356. scitex/plt/_figrecipe.py +236 -0
  357. scitex/plt/_subplots/_AxisWrapper.py +6 -0
  358. scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +112 -1
  359. scitex/plt/_subplots/_FigWrapper.py +15 -0
  360. scitex/plt/_subplots/_SubplotsWrapper.py +125 -489
  361. scitex/plt/_subplots/_export_as_csv.py +11 -0
  362. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +2 -0
  363. scitex/plt/_subplots/_export_as_csv_formatters/_format_pcolormesh.py +66 -0
  364. scitex/plt/_subplots/_export_as_csv_formatters/_format_stackplot.py +62 -0
  365. scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py +208 -0
  366. scitex/plt/_subplots/_fonts.py +71 -0
  367. scitex/plt/_subplots/_mm_layout.py +282 -0
  368. scitex/plt/gallery/__init__.py +99 -2
  369. scitex/plt/styles/_plot_postprocess.py +3 -1
  370. scitex/plt/utils/_configure_mpl.py +16 -19
  371. scitex/repro/_RandomStateManager.py +13 -8
  372. scitex/resource/__init__.py +19 -1
  373. scitex/resource/_utils/_get_env_info.py +13 -25
  374. scitex/schema/__init__.py +149 -160
  375. scitex/schema/_encoding.py +273 -0
  376. scitex/schema/_figure_elements.py +406 -0
  377. scitex/schema/_theme.py +360 -0
  378. scitex/schema/_validation.py +0 -98
  379. scitex/scholar/__init__.py +56 -14
  380. scitex/scholar/auth/ScholarAuthManager.py +1 -1
  381. scitex/scholar/auth/__init__.py +11 -2
  382. scitex/scholar/auth/providers/BaseAuthenticator.py +1 -1
  383. scitex/scholar/auth/providers/EZProxyAuthenticator.py +1 -1
  384. scitex/scholar/auth/providers/OpenAthensAuthenticator.py +1 -1
  385. scitex/scholar/auth/providers/ShibbolethAuthenticator.py +1 -1
  386. scitex/scholar/config/ScholarConfig.py +1 -1
  387. scitex/scholar/core/Scholar.py +1 -1
  388. scitex/session/_decorator.py +18 -16
  389. scitex/session/_lifecycle.py +9 -11
  390. scitex/session/template.py +9 -8
  391. scitex/sh/test_sh.py +72 -0
  392. scitex/sh/test_sh_simple.py +61 -0
  393. scitex/stats/__init__.py +221 -97
  394. scitex/stats/_schema.py +21 -22
  395. scitex/stats/descriptive/_circular.py +212 -351
  396. scitex/stats/descriptive/_describe.py +81 -132
  397. scitex/stats/descriptive/_nan.py +205 -433
  398. scitex/stats/descriptive/_real.py +127 -141
  399. scitex/str/_format_plot_text.py +5 -5
  400. scitex/str/_latex.py +26 -84
  401. scitex/str/_latex_fallback.py +53 -47
  402. scitex/web/_search_pubmed.py +5 -4
  403. scitex/writer/tests/test_diff_between.py +451 -0
  404. scitex/writer/tests/test_document_section.py +311 -0
  405. scitex/writer/tests/test_document_workflow.py +393 -0
  406. scitex/writer/tests/test_writer.py +361 -0
  407. scitex/writer/tests/test_writer_integration.py +303 -0
  408. {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/METADATA +364 -181
  409. {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/RECORD +412 -97
  410. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/ARCHITECTURE_EXAMPLE.md +0 -905
  411. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/BULLETIN_BOARD_EXAMPLE.md +0 -99
  412. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/PROJECT_DESCRIPTION_EXAMPLE.md +0 -96
  413. {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/WHEEL +0 -0
  414. {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/entry_points.txt +0 -0
  415. {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,441 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-12-21
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fts/_fig/_dataclasses/_Theme.py
4
+
5
+ """Theme - Visual aesthetics for FTS bundles."""
6
+
7
+ import json
8
+ from dataclasses import dataclass, field
9
+ from typing import Any, Dict, List, Optional
10
+
11
+ THEME_VERSION = "1.0.0"
12
+
13
+
14
+ @dataclass
15
+ class Colors:
16
+ """Color palette configuration."""
17
+
18
+ palette: List[str] = field(
19
+ default_factory=lambda: ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd"]
20
+ )
21
+ primary: Optional[str] = None
22
+ secondary: Optional[str] = None
23
+ background: str = "#ffffff"
24
+ text: str = "#000000"
25
+ axis: str = "#333333"
26
+ grid: str = "#e0e0e0"
27
+
28
+ def to_dict(self) -> Dict[str, Any]:
29
+ result = {
30
+ "palette": self.palette,
31
+ "background": self.background,
32
+ "text": self.text,
33
+ "axis": self.axis,
34
+ "grid": self.grid,
35
+ }
36
+ if self.primary:
37
+ result["primary"] = self.primary
38
+ if self.secondary:
39
+ result["secondary"] = self.secondary
40
+ return result
41
+
42
+ @classmethod
43
+ def from_dict(cls, data: Any) -> "Colors":
44
+ # Return if already a Colors instance
45
+ if isinstance(data, cls):
46
+ return data
47
+ if not isinstance(data, dict):
48
+ return cls()
49
+ return cls(
50
+ palette=data.get("palette", ["#1f77b4", "#ff7f0e", "#2ca02c"]),
51
+ primary=data.get("primary"),
52
+ secondary=data.get("secondary"),
53
+ background=data.get("background", "#ffffff"),
54
+ text=data.get("text", "#000000"),
55
+ axis=data.get("axis", "#333333"),
56
+ grid=data.get("grid", "#e0e0e0"),
57
+ )
58
+
59
+
60
+ @dataclass
61
+ class Typography:
62
+ """Typography configuration."""
63
+
64
+ family: str = "sans-serif"
65
+ size_pt: float = 8.0
66
+ title_size_pt: float = 10.0
67
+ label_size_pt: float = 8.0
68
+ tick_size_pt: float = 7.0
69
+
70
+ def to_dict(self) -> Dict[str, Any]:
71
+ return {
72
+ "family": self.family,
73
+ "size_pt": self.size_pt,
74
+ "title_size_pt": self.title_size_pt,
75
+ "label_size_pt": self.label_size_pt,
76
+ "tick_size_pt": self.tick_size_pt,
77
+ }
78
+
79
+ @classmethod
80
+ def from_dict(cls, data: Any) -> "Typography":
81
+ # Return if already a Typography instance
82
+ if isinstance(data, cls):
83
+ return data
84
+ if not isinstance(data, dict):
85
+ return cls()
86
+ return cls(
87
+ family=data.get("family", "sans-serif"),
88
+ size_pt=data.get("size_pt", 8.0),
89
+ title_size_pt=data.get("title_size_pt", data.get("title_size", 10.0)),
90
+ label_size_pt=data.get("label_size_pt", data.get("label_size", 8.0)),
91
+ tick_size_pt=data.get("tick_size_pt", data.get("tick_size", 7.0)),
92
+ )
93
+
94
+
95
+ @dataclass
96
+ class Lines:
97
+ """Line style configuration."""
98
+
99
+ width_pt: float = 1.0
100
+ style: str = "solid" # solid, dashed, dotted, dashdot
101
+
102
+ def to_dict(self) -> Dict[str, Any]:
103
+ return {"width_pt": self.width_pt, "style": self.style}
104
+
105
+ @classmethod
106
+ def from_dict(cls, data: Any) -> "Lines":
107
+ if isinstance(data, cls):
108
+ return data
109
+ if not isinstance(data, dict):
110
+ return cls()
111
+ return cls(
112
+ width_pt=data.get("width_pt", 1.0),
113
+ style=data.get("style", "solid"),
114
+ )
115
+
116
+
117
+ @dataclass
118
+ class Markers:
119
+ """Marker style configuration."""
120
+
121
+ size_pt: float = 4.0
122
+ symbol: str = "circle" # circle, square, triangle, cross, diamond
123
+
124
+ def to_dict(self) -> Dict[str, Any]:
125
+ return {"size_pt": self.size_pt, "symbol": self.symbol}
126
+
127
+ @classmethod
128
+ def from_dict(cls, data: Any) -> "Markers":
129
+ if isinstance(data, cls):
130
+ return data
131
+ if not isinstance(data, dict):
132
+ return cls()
133
+ return cls(
134
+ size_pt=data.get("size_pt", 4.0),
135
+ symbol=data.get("symbol", "circle"),
136
+ )
137
+
138
+
139
+ @dataclass
140
+ class Grid:
141
+ """Grid line configuration."""
142
+
143
+ show: bool = True
144
+ major_width_pt: float = 0.5
145
+ minor_width_pt: float = 0.25
146
+ major_alpha: float = 0.3
147
+ minor_alpha: float = 0.1
148
+
149
+ def to_dict(self) -> Dict[str, Any]:
150
+ return {
151
+ "show": self.show,
152
+ "major_width_pt": self.major_width_pt,
153
+ "minor_width_pt": self.minor_width_pt,
154
+ "major_alpha": self.major_alpha,
155
+ "minor_alpha": self.minor_alpha,
156
+ }
157
+
158
+ @classmethod
159
+ def from_dict(cls, data: Any) -> "Grid":
160
+ if isinstance(data, cls):
161
+ return data
162
+ if isinstance(data, bool):
163
+ return cls(show=data)
164
+ if not isinstance(data, dict):
165
+ return cls()
166
+ return cls(
167
+ show=data.get("show", True),
168
+ major_width_pt=data.get("major_width_pt", 0.5),
169
+ minor_width_pt=data.get("minor_width_pt", 0.25),
170
+ major_alpha=data.get("major_alpha", 0.3),
171
+ minor_alpha=data.get("minor_alpha", 0.1),
172
+ )
173
+
174
+
175
+ @dataclass
176
+ class TraceTheme:
177
+ """Per-trace theme overrides."""
178
+
179
+ trace_id: str
180
+ color: Optional[str] = None
181
+ line_width_pt: Optional[float] = None
182
+ marker_size_pt: Optional[float] = None
183
+ alpha: Optional[float] = None
184
+
185
+ def to_dict(self) -> Dict[str, Any]:
186
+ result = {"trace_id": self.trace_id}
187
+ if self.color:
188
+ result["color"] = self.color
189
+ if self.line_width_pt is not None:
190
+ result["line_width_pt"] = self.line_width_pt
191
+ if self.marker_size_pt is not None:
192
+ result["marker_size_pt"] = self.marker_size_pt
193
+ if self.alpha is not None:
194
+ result["alpha"] = self.alpha
195
+ return result
196
+
197
+ @classmethod
198
+ def from_dict(cls, data: Any) -> "TraceTheme":
199
+ if isinstance(data, cls):
200
+ return data
201
+ if not isinstance(data, dict):
202
+ return cls(trace_id="trace_0")
203
+ return cls(
204
+ trace_id=data.get("trace_id", "trace_0"),
205
+ color=data.get("color"),
206
+ line_width_pt=data.get("line_width_pt"),
207
+ marker_size_pt=data.get("marker_size_pt"),
208
+ alpha=data.get("alpha"),
209
+ )
210
+
211
+
212
+ @dataclass
213
+ class FigureTitle:
214
+ """Figure title configuration for publications."""
215
+
216
+ text: str = ""
217
+ prefix: str = "Figure"
218
+ number: Optional[int] = None
219
+ fontsize: Optional[float] = None
220
+ fontweight: str = "bold"
221
+
222
+ def to_dict(self) -> Dict[str, Any]:
223
+ result = {"text": self.text, "prefix": self.prefix, "fontweight": self.fontweight}
224
+ if self.number is not None:
225
+ result["number"] = self.number
226
+ if self.fontsize is not None:
227
+ result["fontsize"] = self.fontsize
228
+ return result
229
+
230
+ @classmethod
231
+ def from_dict(cls, data: Any) -> "FigureTitle":
232
+ if isinstance(data, cls):
233
+ return data
234
+ if not isinstance(data, dict):
235
+ return cls()
236
+ return cls(
237
+ text=data.get("text", ""),
238
+ prefix=data.get("prefix", "Figure"),
239
+ number=data.get("number"),
240
+ fontsize=data.get("fontsize"),
241
+ fontweight=data.get("fontweight", "bold"),
242
+ )
243
+
244
+
245
+ @dataclass
246
+ class PanelDescription:
247
+ """Single panel description for captions."""
248
+
249
+ label: str
250
+ description: str
251
+
252
+ def to_dict(self) -> Dict[str, Any]:
253
+ return {"label": self.label, "description": self.description}
254
+
255
+ @classmethod
256
+ def from_dict(cls, data: Any) -> "PanelDescription":
257
+ if isinstance(data, cls):
258
+ return data
259
+ if not isinstance(data, dict):
260
+ return cls(label="", description="")
261
+ return cls(
262
+ label=data.get("label", ""),
263
+ description=data.get("description", ""),
264
+ )
265
+
266
+
267
+ @dataclass
268
+ class Caption:
269
+ """Figure caption for publications."""
270
+
271
+ text: str = ""
272
+ panels: List[PanelDescription] = field(default_factory=list)
273
+
274
+ def to_dict(self) -> Dict[str, Any]:
275
+ result = {"text": self.text}
276
+ if self.panels:
277
+ result["panels"] = [p.to_dict() for p in self.panels]
278
+ return result
279
+
280
+ @classmethod
281
+ def from_dict(cls, data: Any) -> "Caption":
282
+ if isinstance(data, cls):
283
+ return data
284
+ if not isinstance(data, dict):
285
+ return cls()
286
+ panels = [PanelDescription.from_dict(p) for p in data.get("panels", [])]
287
+ return cls(text=data.get("text", ""), panels=panels)
288
+
289
+
290
+ @dataclass
291
+ class PanelLabels:
292
+ """Panel label styling (A, B, C, etc.)."""
293
+
294
+ style: str = "uppercase" # uppercase, lowercase, numeric
295
+ fontsize: float = 12.0
296
+ fontweight: str = "bold"
297
+ position: str = "top-left" # top-left, top-right, bottom-left, bottom-right
298
+ offset_x: float = 0.02
299
+ offset_y: float = 0.98
300
+
301
+ def to_dict(self) -> Dict[str, Any]:
302
+ return {
303
+ "style": self.style,
304
+ "fontsize": self.fontsize,
305
+ "fontweight": self.fontweight,
306
+ "position": self.position,
307
+ "offset_x": self.offset_x,
308
+ "offset_y": self.offset_y,
309
+ }
310
+
311
+ @classmethod
312
+ def from_dict(cls, data: Any) -> "PanelLabels":
313
+ if isinstance(data, cls):
314
+ return data
315
+ if not isinstance(data, dict):
316
+ return cls()
317
+ return cls(
318
+ style=data.get("style", "uppercase"),
319
+ fontsize=data.get("fontsize", 12.0),
320
+ fontweight=data.get("fontweight", "bold"),
321
+ position=data.get("position", "top-left"),
322
+ offset_x=data.get("offset_x", 0.02),
323
+ offset_y=data.get("offset_y", 0.98),
324
+ )
325
+
326
+
327
+ @dataclass
328
+ class Theme:
329
+ """Complete theme specification for an FTS bundle.
330
+
331
+ Stored in canonical/theme.json.
332
+ """
333
+
334
+ # Core mode
335
+ mode: str = "light" # "light" | "dark"
336
+
337
+ # Styling components
338
+ colors: Colors = field(default_factory=Colors)
339
+ typography: Typography = field(default_factory=Typography)
340
+ lines: Lines = field(default_factory=Lines)
341
+ markers: Markers = field(default_factory=Markers)
342
+ grid: Grid = field(default_factory=Grid)
343
+
344
+ # Per-trace overrides
345
+ traces: List[TraceTheme] = field(default_factory=list)
346
+
347
+ # Publication metadata
348
+ preset: Optional[str] = None # nature, science, cell, ieee, acs, minimal, presentation
349
+ figure_title: Optional[FigureTitle] = None
350
+ caption: Optional[Caption] = None
351
+ panel_labels: Optional[PanelLabels] = None
352
+
353
+ # Alias for typography (for convenience)
354
+ fonts: Optional[Typography] = None
355
+
356
+ # Schema metadata
357
+ schema_name: str = "fts.theme"
358
+ schema_version: str = THEME_VERSION
359
+
360
+ def to_dict(self) -> Dict[str, Any]:
361
+ result = {
362
+ "mode": self.mode,
363
+ "colors": self.colors.to_dict(),
364
+ "typography": self.typography.to_dict(),
365
+ "lines": self.lines.to_dict(),
366
+ "markers": self.markers.to_dict(),
367
+ "grid": self.grid.to_dict(),
368
+ }
369
+ if self.traces:
370
+ result["traces"] = [t.to_dict() for t in self.traces]
371
+ if self.preset:
372
+ result["preset"] = self.preset
373
+ if self.figure_title:
374
+ result["figure_title"] = self.figure_title.to_dict()
375
+ if self.caption:
376
+ result["caption"] = self.caption.to_dict()
377
+ if self.panel_labels:
378
+ result["panel_labels"] = self.panel_labels.to_dict()
379
+ return result
380
+
381
+ def to_json(self, indent: int = 2) -> str:
382
+ return json.dumps(self.to_dict(), indent=indent)
383
+
384
+ @classmethod
385
+ def from_dict(cls, data: Dict[str, Any]) -> "Theme":
386
+ # Handle fonts as alias for typography
387
+ typography_data = data.get("typography", {})
388
+ fonts_data = data.get("fonts")
389
+ if fonts_data and not typography_data:
390
+ typography_data = fonts_data
391
+
392
+ # Parse figure_title
393
+ figure_title = None
394
+ if "figure_title" in data and data["figure_title"]:
395
+ figure_title = FigureTitle.from_dict(data["figure_title"])
396
+
397
+ # Parse caption
398
+ caption = None
399
+ if "caption" in data and data["caption"]:
400
+ caption = Caption.from_dict(data["caption"])
401
+
402
+ # Parse panel_labels
403
+ panel_labels = None
404
+ if "panel_labels" in data and data["panel_labels"]:
405
+ panel_labels = PanelLabels.from_dict(data["panel_labels"])
406
+
407
+ return cls(
408
+ mode=data.get("mode", "light"),
409
+ colors=Colors.from_dict(data.get("colors", {})),
410
+ typography=Typography.from_dict(typography_data),
411
+ lines=Lines.from_dict(data.get("lines", {})),
412
+ markers=Markers.from_dict(data.get("markers", {})),
413
+ grid=Grid.from_dict(data.get("grid", {})),
414
+ traces=[TraceTheme.from_dict(t) for t in data.get("traces", [])],
415
+ preset=data.get("preset"),
416
+ figure_title=figure_title,
417
+ caption=caption,
418
+ panel_labels=panel_labels,
419
+ )
420
+
421
+ @classmethod
422
+ def from_json(cls, json_str: str) -> "Theme":
423
+ return cls.from_dict(json.loads(json_str))
424
+
425
+
426
+ __all__ = [
427
+ "THEME_VERSION",
428
+ "Colors",
429
+ "Typography",
430
+ "Lines",
431
+ "Markers",
432
+ "Grid",
433
+ "TraceTheme",
434
+ "FigureTitle",
435
+ "PanelDescription",
436
+ "Caption",
437
+ "PanelLabels",
438
+ "Theme",
439
+ ]
440
+
441
+ # EOF
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-12-20
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_dataclasses/TraceEncoding.py
4
+
5
+ """TraceEncoding - Encoding for a single trace/data series."""
6
+
7
+ from dataclasses import dataclass
8
+ from typing import Any, Dict, Optional
9
+
10
+ from ._ChannelEncoding import ChannelEncoding
11
+
12
+
13
+ @dataclass
14
+ class TraceEncoding:
15
+ """Encoding specification for a single trace/data series."""
16
+
17
+ trace_id: str
18
+ data_ref: Optional[str] = None # Path to data file within bundle
19
+ x: Optional[ChannelEncoding] = None
20
+ y: Optional[ChannelEncoding] = None
21
+ color: Optional[ChannelEncoding] = None
22
+ size: Optional[ChannelEncoding] = None
23
+ group: Optional[ChannelEncoding] = None
24
+ label: Optional[ChannelEncoding] = None
25
+
26
+ def to_dict(self) -> Dict[str, Any]:
27
+ result = {"trace_id": self.trace_id}
28
+ if self.data_ref:
29
+ result["data_ref"] = self.data_ref
30
+ for channel in ["x", "y", "color", "size", "group", "label"]:
31
+ enc = getattr(self, channel)
32
+ if enc:
33
+ result[channel] = enc.to_dict()
34
+ return result
35
+
36
+ @classmethod
37
+ def from_dict(cls, data: Dict[str, Any]) -> "TraceEncoding":
38
+ return cls(
39
+ trace_id=data.get("trace_id", "trace_0"),
40
+ data_ref=data.get("data_ref"),
41
+ x=ChannelEncoding.from_dict(data["x"]) if "x" in data else None,
42
+ y=ChannelEncoding.from_dict(data["y"]) if "y" in data else None,
43
+ color=ChannelEncoding.from_dict(data["color"]) if "color" in data else None,
44
+ size=ChannelEncoding.from_dict(data["size"]) if "size" in data else None,
45
+ group=ChannelEncoding.from_dict(data["group"]) if "group" in data else None,
46
+ label=ChannelEncoding.from_dict(data["label"]) if "label" in data else None,
47
+ )
48
+
49
+
50
+ __all__ = ["TraceEncoding"]
51
+
52
+ # EOF
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-12-21
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fts/_fig/_dataclasses/__init__.py
4
+
5
+ """Figure-specific dataclasses for FTS."""
6
+
7
+ from ._ChannelEncoding import ChannelEncoding
8
+ from ._Encoding import ENCODING_VERSION, AxesConfig, Encoding
9
+ from ._Theme import (
10
+ THEME_VERSION,
11
+ Caption,
12
+ Colors,
13
+ FigureTitle,
14
+ Grid,
15
+ Lines,
16
+ Markers,
17
+ PanelDescription,
18
+ PanelLabels,
19
+ Theme,
20
+ TraceTheme,
21
+ Typography,
22
+ )
23
+ from ._TraceEncoding import TraceEncoding
24
+
25
+ __all__ = [
26
+ # Encoding
27
+ "ENCODING_VERSION",
28
+ "AxesConfig",
29
+ "ChannelEncoding",
30
+ "TraceEncoding",
31
+ "Encoding",
32
+ # Theme
33
+ "THEME_VERSION",
34
+ "Colors",
35
+ "Typography",
36
+ "Lines",
37
+ "Markers",
38
+ "Grid",
39
+ "TraceTheme",
40
+ "FigureTitle",
41
+ "PanelDescription",
42
+ "Caption",
43
+ "PanelLabels",
44
+ "Theme",
45
+ ]
46
+
47
+ # EOF
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-12-20
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_fig/editor/__init__.py
4
+
5
+ """FTS Figure Editor - Interactive GUI for editing figure styles."""
6
+
7
+ from ._cui import edit, save_manual_overrides
8
+
9
+ __all__ = [
10
+ "edit",
11
+ "save_manual_overrides",
12
+ ]
13
+
14
+ # EOF
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-12-20
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_fig/editor/cui/__init__.py
4
+
5
+ """CUI module for launching visual figure editors."""
6
+
7
+ from ._backend_detector import detect_best_backend, print_available_backends
8
+ from ._bundle_resolver import (
9
+ resolve_figz_bundle,
10
+ resolve_layered_pltz_bundle,
11
+ resolve_pltz_bundle,
12
+ resolve_stx_bundle,
13
+ )
14
+ from ._editor_launcher import edit
15
+ from ._manual_handler import compute_file_hash, save_manual_overrides
16
+ from ._panel_loader import load_panel_data
17
+ from ._path_resolver import resolve_figure_paths
18
+
19
+ __all__ = [
20
+ "edit",
21
+ "detect_best_backend",
22
+ "print_available_backends",
23
+ "resolve_figure_paths",
24
+ "resolve_pltz_bundle",
25
+ "resolve_figz_bundle",
26
+ "resolve_layered_pltz_bundle",
27
+ "resolve_stx_bundle",
28
+ "load_panel_data",
29
+ "compute_file_hash",
30
+ "save_manual_overrides",
31
+ ]
32
+
33
+ # EOF
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-12-20
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_fig/_editor/_cui/_backend_detector.py
4
+
5
+ """Backend detection for figure editor (Flask only)."""
6
+
7
+ __all__ = ["print_available_backends", "detect_best_backend"]
8
+
9
+
10
+ def print_available_backends() -> None:
11
+ """Print Flask backend availability."""
12
+ print("\n" + "=" * 50)
13
+ print("SciTeX Visual Editor")
14
+ print("=" * 50)
15
+
16
+ try:
17
+ import flask
18
+
19
+ print(f" Flask: [OK] {flask.__version__}")
20
+ except ImportError:
21
+ print(" Flask: [NOT INSTALLED]")
22
+ print("\nInstall: pip install flask")
23
+
24
+ print("=" * 50 + "\n")
25
+
26
+
27
+ def detect_best_backend() -> str:
28
+ """Return Flask as the only supported backend."""
29
+ try:
30
+ import flask
31
+
32
+ return "flask"
33
+ except ImportError:
34
+ raise ImportError(
35
+ "Flask is required for the editor. Install with: pip install flask"
36
+ )
37
+
38
+
39
+ # EOF