scitex 2.7.3__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 (563) 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/dev/plt/plot_mpl_axhline.py +0 -0
  105. scitex/dev/plt/plot_mpl_axhspan.py +0 -0
  106. scitex/dev/plt/plot_mpl_axvline.py +0 -0
  107. scitex/dev/plt/plot_mpl_axvspan.py +0 -0
  108. scitex/dev/plt/plot_mpl_bar.py +0 -0
  109. scitex/dev/plt/plot_mpl_barh.py +0 -0
  110. scitex/dev/plt/plot_mpl_boxplot.py +0 -0
  111. scitex/dev/plt/plot_mpl_contour.py +0 -0
  112. scitex/dev/plt/plot_mpl_contourf.py +0 -0
  113. scitex/dev/plt/plot_mpl_errorbar.py +0 -0
  114. scitex/dev/plt/plot_mpl_eventplot.py +0 -0
  115. scitex/dev/plt/plot_mpl_fill.py +0 -0
  116. scitex/dev/plt/plot_mpl_fill_between.py +0 -0
  117. scitex/dev/plt/plot_mpl_hexbin.py +0 -0
  118. scitex/dev/plt/plot_mpl_hist.py +0 -0
  119. scitex/dev/plt/plot_mpl_hist2d.py +0 -0
  120. scitex/dev/plt/plot_mpl_imshow.py +0 -0
  121. scitex/dev/plt/plot_mpl_pcolormesh.py +0 -0
  122. scitex/dev/plt/plot_mpl_pie.py +0 -0
  123. scitex/dev/plt/plot_mpl_plot.py +0 -0
  124. scitex/dev/plt/plot_mpl_quiver.py +0 -0
  125. scitex/dev/plt/plot_mpl_scatter.py +0 -0
  126. scitex/dev/plt/plot_mpl_stackplot.py +0 -0
  127. scitex/dev/plt/plot_mpl_stem.py +0 -0
  128. scitex/dev/plt/plot_mpl_step.py +0 -0
  129. scitex/dev/plt/plot_mpl_violinplot.py +0 -0
  130. scitex/dev/plt/plot_sns_barplot.py +0 -0
  131. scitex/dev/plt/plot_sns_boxplot.py +0 -0
  132. scitex/dev/plt/plot_sns_heatmap.py +0 -0
  133. scitex/dev/plt/plot_sns_histplot.py +0 -0
  134. scitex/dev/plt/plot_sns_kdeplot.py +0 -0
  135. scitex/dev/plt/plot_sns_lineplot.py +0 -0
  136. scitex/dev/plt/plot_sns_scatterplot.py +0 -0
  137. scitex/dev/plt/plot_sns_stripplot.py +0 -0
  138. scitex/dev/plt/plot_sns_swarmplot.py +0 -0
  139. scitex/dev/plt/plot_sns_violinplot.py +0 -0
  140. scitex/dev/plt/plot_stx_bar.py +0 -0
  141. scitex/dev/plt/plot_stx_barh.py +0 -0
  142. scitex/dev/plt/plot_stx_box.py +0 -0
  143. scitex/dev/plt/plot_stx_boxplot.py +0 -0
  144. scitex/dev/plt/plot_stx_conf_mat.py +0 -0
  145. scitex/dev/plt/plot_stx_contour.py +0 -0
  146. scitex/dev/plt/plot_stx_ecdf.py +0 -0
  147. scitex/dev/plt/plot_stx_errorbar.py +0 -0
  148. scitex/dev/plt/plot_stx_fill_between.py +0 -0
  149. scitex/dev/plt/plot_stx_fillv.py +0 -0
  150. scitex/dev/plt/plot_stx_heatmap.py +0 -0
  151. scitex/dev/plt/plot_stx_image.py +0 -0
  152. scitex/dev/plt/plot_stx_imshow.py +0 -0
  153. scitex/dev/plt/plot_stx_joyplot.py +0 -0
  154. scitex/dev/plt/plot_stx_kde.py +0 -0
  155. scitex/dev/plt/plot_stx_line.py +0 -0
  156. scitex/dev/plt/plot_stx_mean_ci.py +0 -0
  157. scitex/dev/plt/plot_stx_mean_std.py +0 -0
  158. scitex/dev/plt/plot_stx_median_iqr.py +0 -0
  159. scitex/dev/plt/plot_stx_raster.py +0 -0
  160. scitex/dev/plt/plot_stx_rectangle.py +0 -0
  161. scitex/dev/plt/plot_stx_scatter.py +0 -0
  162. scitex/dev/plt/plot_stx_shaded_line.py +0 -0
  163. scitex/dev/plt/plot_stx_violin.py +0 -0
  164. scitex/dev/plt/plot_stx_violinplot.py +0 -0
  165. scitex/diagram/README.md +197 -0
  166. scitex/diagram/__init__.py +48 -0
  167. scitex/diagram/_compile.py +312 -0
  168. scitex/diagram/_diagram.py +355 -0
  169. scitex/diagram/_presets.py +173 -0
  170. scitex/diagram/_schema.py +182 -0
  171. scitex/diagram/_split.py +278 -0
  172. scitex/dict/_pop_keys.py +1 -7
  173. scitex/dsp/__init__.py +15 -10
  174. scitex/dsp/add_noise.py +5 -2
  175. scitex/dsp/example.py +35 -22
  176. scitex/dsp/filt.py +8 -3
  177. scitex/dsp/reference.py +3 -2
  178. scitex/dsp/utils/__init__.py +2 -1
  179. scitex/dsp/utils/_differential_bandpass_filters.py +14 -4
  180. scitex/dt/__init__.py +39 -2
  181. scitex/errors.py +82 -521
  182. scitex/fig/__init__.py +4 -4
  183. scitex/fig/editor/__init__.py +5 -2
  184. scitex/fig/editor/_dearpygui_editor.py +1 -1
  185. scitex/fig/editor/_mpl_editor.py +1 -1
  186. scitex/fig/editor/_qt_editor.py +1 -1
  187. scitex/fig/editor/_tkinter_editor.py +1 -1
  188. scitex/fig/editor/edit/__init__.py +50 -0
  189. scitex/fig/editor/edit/backend_detector.py +109 -0
  190. scitex/fig/editor/edit/bundle_resolver.py +240 -0
  191. scitex/fig/editor/edit/editor_launcher.py +239 -0
  192. scitex/fig/editor/edit/manual_handler.py +53 -0
  193. scitex/fig/editor/edit/panel_loader.py +232 -0
  194. scitex/fig/editor/edit/path_resolver.py +67 -0
  195. scitex/fig/editor/flask_editor/_bbox.py +23 -0
  196. scitex/fig/editor/flask_editor/_core.py +908 -103
  197. scitex/fig/editor/flask_editor/_renderer.py +74 -0
  198. scitex/fig/editor/flask_editor/static/css/base/reset.css +41 -0
  199. scitex/fig/editor/flask_editor/static/css/base/typography.css +16 -0
  200. scitex/fig/editor/flask_editor/static/css/base/variables.css +85 -0
  201. scitex/fig/editor/flask_editor/static/css/components/buttons.css +217 -0
  202. scitex/fig/editor/flask_editor/static/css/components/context-menu.css +93 -0
  203. scitex/fig/editor/flask_editor/static/css/components/dropdown.css +57 -0
  204. scitex/fig/editor/flask_editor/static/css/components/forms.css +112 -0
  205. scitex/fig/editor/flask_editor/static/css/components/modal.css +59 -0
  206. scitex/fig/editor/flask_editor/static/css/components/sections.css +212 -0
  207. scitex/fig/editor/flask_editor/static/css/features/canvas.css +176 -0
  208. scitex/fig/editor/flask_editor/static/css/features/element-inspector.css +190 -0
  209. scitex/fig/editor/flask_editor/static/css/features/loading.css +59 -0
  210. scitex/fig/editor/flask_editor/static/css/features/overlay.css +45 -0
  211. scitex/fig/editor/flask_editor/static/css/features/panel-grid.css +95 -0
  212. scitex/fig/editor/flask_editor/static/css/features/selection.css +101 -0
  213. scitex/fig/editor/flask_editor/static/css/features/statistics.css +138 -0
  214. scitex/fig/editor/flask_editor/static/css/index.css +31 -0
  215. scitex/fig/editor/flask_editor/static/css/layout/container.css +7 -0
  216. scitex/fig/editor/flask_editor/static/css/layout/controls.css +56 -0
  217. scitex/fig/editor/flask_editor/static/css/layout/preview.css +78 -0
  218. scitex/fig/editor/flask_editor/static/js/alignment/axis.js +314 -0
  219. scitex/fig/editor/flask_editor/static/js/alignment/basic.js +107 -0
  220. scitex/fig/editor/flask_editor/static/js/alignment/distribute.js +54 -0
  221. scitex/fig/editor/flask_editor/static/js/canvas/canvas.js +172 -0
  222. scitex/fig/editor/flask_editor/static/js/canvas/dragging.js +258 -0
  223. scitex/fig/editor/flask_editor/static/js/canvas/resize.js +48 -0
  224. scitex/fig/editor/flask_editor/static/js/canvas/selection.js +71 -0
  225. scitex/fig/editor/flask_editor/static/js/core/api.js +288 -0
  226. scitex/fig/editor/flask_editor/static/js/core/state.js +143 -0
  227. scitex/fig/editor/flask_editor/static/js/core/utils.js +245 -0
  228. scitex/fig/editor/flask_editor/static/js/dev/element-inspector.js +992 -0
  229. scitex/fig/editor/flask_editor/static/js/editor/bbox.js +339 -0
  230. scitex/fig/editor/flask_editor/static/js/editor/element-drag.js +286 -0
  231. scitex/fig/editor/flask_editor/static/js/editor/overlay.js +371 -0
  232. scitex/fig/editor/flask_editor/static/js/editor/preview.js +293 -0
  233. scitex/fig/editor/flask_editor/static/js/main.js +426 -0
  234. scitex/fig/editor/flask_editor/static/js/shortcuts/context-menu.js +152 -0
  235. scitex/fig/editor/flask_editor/static/js/shortcuts/keyboard.js +265 -0
  236. scitex/fig/editor/flask_editor/static/js/ui/controls.js +184 -0
  237. scitex/fig/editor/flask_editor/static/js/ui/download.js +57 -0
  238. scitex/fig/editor/flask_editor/static/js/ui/help.js +100 -0
  239. scitex/fig/editor/flask_editor/static/js/ui/theme.js +34 -0
  240. scitex/fig/editor/flask_editor/templates/__init__.py +95 -5
  241. scitex/fig/editor/flask_editor/templates/_html.py +27 -9
  242. scitex/fig/editor/flask_editor/templates/_scripts.py +1928 -131
  243. scitex/fig/editor/flask_editor/templates/_styles.py +363 -51
  244. scitex/fig/io/_bundle.py +104 -19
  245. scitex/fts/README.md +262 -0
  246. scitex/fts/TODO.md +66 -0
  247. scitex/fts/__init__.py +90 -0
  248. scitex/fts/_bundle/README_IN_BUNDLE.md +102 -0
  249. scitex/fts/_bundle/_FTS.py +657 -0
  250. scitex/fts/_bundle/__init__.py +38 -0
  251. scitex/fts/_bundle/_children.py +216 -0
  252. scitex/fts/_bundle/_conversion/__init__.py +15 -0
  253. scitex/fts/_bundle/_conversion/_bundle2dict.py +44 -0
  254. scitex/fts/_bundle/_conversion/_dict2bundle.py +50 -0
  255. scitex/fts/_bundle/_dataclasses/_Axes.py +57 -0
  256. scitex/fts/_bundle/_dataclasses/_BBox.py +54 -0
  257. scitex/fts/_bundle/_dataclasses/_ColumnDef.py +72 -0
  258. scitex/fts/_bundle/_dataclasses/_DataFormat.py +40 -0
  259. scitex/fts/_bundle/_dataclasses/_DataInfo.py +135 -0
  260. scitex/fts/_bundle/_dataclasses/_DataSource.py +44 -0
  261. scitex/fts/_bundle/_dataclasses/_Node.py +319 -0
  262. scitex/fts/_bundle/_dataclasses/_NodeRefs.py +45 -0
  263. scitex/fts/_bundle/_dataclasses/_SizeMM.py +38 -0
  264. scitex/fts/_bundle/_dataclasses/__init__.py +35 -0
  265. scitex/fts/_bundle/_extractors/__init__.py +32 -0
  266. scitex/fts/_bundle/_extractors/_extract_bar.py +131 -0
  267. scitex/fts/_bundle/_extractors/_extract_line.py +71 -0
  268. scitex/fts/_bundle/_extractors/_extract_scatter.py +79 -0
  269. scitex/fts/_bundle/_loader.py +134 -0
  270. scitex/fts/_bundle/_mpl_helpers.py +389 -0
  271. scitex/fts/_bundle/_saver.py +269 -0
  272. scitex/fts/_bundle/_storage.py +200 -0
  273. scitex/fts/_bundle/_utils/__init__.py +55 -0
  274. scitex/fts/_bundle/_utils/_const.py +26 -0
  275. scitex/fts/_bundle/_utils/_errors.py +73 -0
  276. scitex/fts/_bundle/_utils/_generate.py +21 -0
  277. scitex/fts/_bundle/_utils/_types.py +76 -0
  278. scitex/fts/_bundle/_validation.py +434 -0
  279. scitex/fts/_bundle/_zipbundle.py +165 -0
  280. scitex/fts/_fig/__init__.py +22 -0
  281. scitex/fts/_fig/_backend/__init__.py +53 -0
  282. scitex/fts/_fig/_backend/_export.py +165 -0
  283. scitex/fts/_fig/_backend/_parser.py +188 -0
  284. scitex/fts/_fig/_backend/_render.py +538 -0
  285. scitex/fts/_fig/_composite.py +345 -0
  286. scitex/fts/_fig/_dataclasses/_ChannelEncoding.py +46 -0
  287. scitex/fts/_fig/_dataclasses/_Encoding.py +82 -0
  288. scitex/fts/_fig/_dataclasses/_Theme.py +441 -0
  289. scitex/fts/_fig/_dataclasses/_TraceEncoding.py +52 -0
  290. scitex/fts/_fig/_dataclasses/__init__.py +47 -0
  291. scitex/fts/_fig/_editor/__init__.py +14 -0
  292. scitex/fts/_fig/_editor/_cui/__init__.py +33 -0
  293. scitex/fts/_fig/_editor/_cui/_backend_detector.py +39 -0
  294. scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +366 -0
  295. scitex/fts/_fig/_editor/_cui/_editor_launcher.py +175 -0
  296. scitex/fts/_fig/_editor/_cui/_manual_handler.py +52 -0
  297. scitex/fts/_fig/_editor/_cui/_panel_loader.py +246 -0
  298. scitex/fts/_fig/_editor/_cui/_path_resolver.py +66 -0
  299. scitex/fts/_fig/_editor/_defaults.py +300 -0
  300. scitex/fts/_fig/_editor/_gui/__init__.py +11 -0
  301. scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +20 -0
  302. scitex/fts/_fig/_editor/_gui/_flask_editor/_bbox.py +1339 -0
  303. scitex/fts/_fig/_editor/_gui/_flask_editor/_core.py +1688 -0
  304. scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +664 -0
  305. scitex/fts/_fig/_editor/_gui/_flask_editor/_renderer.py +853 -0
  306. scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +79 -0
  307. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +41 -0
  308. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +16 -0
  309. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +85 -0
  310. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +217 -0
  311. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +93 -0
  312. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +57 -0
  313. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +112 -0
  314. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +59 -0
  315. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +212 -0
  316. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/canvas.css +176 -0
  317. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +190 -0
  318. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +59 -0
  319. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +45 -0
  320. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +95 -0
  321. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +101 -0
  322. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +138 -0
  323. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +31 -0
  324. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +7 -0
  325. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +56 -0
  326. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +78 -0
  327. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +314 -0
  328. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +107 -0
  329. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +54 -0
  330. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +172 -0
  331. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +258 -0
  332. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +48 -0
  333. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +71 -0
  334. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +288 -0
  335. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +143 -0
  336. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +245 -0
  337. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +992 -0
  338. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +339 -0
  339. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +286 -0
  340. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +371 -0
  341. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +293 -0
  342. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +426 -0
  343. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +152 -0
  344. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +265 -0
  345. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +184 -0
  346. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +57 -0
  347. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +100 -0
  348. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +34 -0
  349. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +124 -0
  350. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +851 -0
  351. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +4932 -0
  352. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +1657 -0
  353. scitex/fts/_fig/_editor/_gui/_flask_editor.py +36 -0
  354. scitex/fts/_fig/_models/_Annotations.py +115 -0
  355. scitex/fts/_fig/_models/_Axes.py +152 -0
  356. scitex/fts/_fig/_models/_Figure.py +138 -0
  357. scitex/fts/_fig/_models/_Guides.py +104 -0
  358. scitex/fts/_fig/_models/_Plot.py +123 -0
  359. scitex/fts/_fig/_models/_Styles.py +245 -0
  360. scitex/fts/_fig/_models/__init__.py +80 -0
  361. scitex/fts/_fig/_models/_plot_types/__init__.py +156 -0
  362. scitex/fts/_fig/_models/_plot_types/_bar.py +43 -0
  363. scitex/fts/_fig/_models/_plot_types/_box.py +38 -0
  364. scitex/fts/_fig/_models/_plot_types/_distribution.py +36 -0
  365. scitex/fts/_fig/_models/_plot_types/_errorbar.py +60 -0
  366. scitex/fts/_fig/_models/_plot_types/_histogram.py +30 -0
  367. scitex/fts/_fig/_models/_plot_types/_image.py +61 -0
  368. scitex/fts/_fig/_models/_plot_types/_line.py +57 -0
  369. scitex/fts/_fig/_models/_plot_types/_scatter.py +30 -0
  370. scitex/fts/_fig/_models/_plot_types/_seaborn.py +121 -0
  371. scitex/fts/_fig/_models/_plot_types/_violin.py +36 -0
  372. scitex/fts/_fig/_utils/__init__.py +129 -0
  373. scitex/fts/_fig/_utils/_auto_layout.py +127 -0
  374. scitex/fts/_fig/_utils/_calc_bounds.py +111 -0
  375. scitex/fts/_fig/_utils/_const_sizes.py +48 -0
  376. scitex/fts/_fig/_utils/_convert_coords.py +77 -0
  377. scitex/fts/_fig/_utils/_get_template.py +178 -0
  378. scitex/fts/_fig/_utils/_normalize.py +73 -0
  379. scitex/fts/_fig/_utils/_plot_layout.py +397 -0
  380. scitex/fts/_fig/_utils/_validate.py +197 -0
  381. scitex/fts/_kinds/__init__.py +45 -0
  382. scitex/fts/_kinds/_figure/__init__.py +19 -0
  383. scitex/fts/_kinds/_figure/_composite.py +345 -0
  384. scitex/fts/_kinds/_plot/__init__.py +25 -0
  385. scitex/fts/_kinds/_plot/_backend/__init__.py +53 -0
  386. scitex/fts/_kinds/_plot/_backend/_export.py +165 -0
  387. scitex/fts/_kinds/_plot/_backend/_parser.py +188 -0
  388. scitex/fts/_kinds/_plot/_backend/_render.py +538 -0
  389. scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +46 -0
  390. scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +82 -0
  391. scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +441 -0
  392. scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +52 -0
  393. scitex/fts/_kinds/_plot/_dataclasses/__init__.py +47 -0
  394. scitex/fts/_kinds/_plot/_models/_Annotations.py +115 -0
  395. scitex/fts/_kinds/_plot/_models/_Axes.py +152 -0
  396. scitex/fts/_kinds/_plot/_models/_Figure.py +138 -0
  397. scitex/fts/_kinds/_plot/_models/_Guides.py +104 -0
  398. scitex/fts/_kinds/_plot/_models/_Plot.py +123 -0
  399. scitex/fts/_kinds/_plot/_models/_Styles.py +245 -0
  400. scitex/fts/_kinds/_plot/_models/__init__.py +80 -0
  401. scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +156 -0
  402. scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +43 -0
  403. scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +38 -0
  404. scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +36 -0
  405. scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +60 -0
  406. scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +30 -0
  407. scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +61 -0
  408. scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +57 -0
  409. scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +30 -0
  410. scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +121 -0
  411. scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +36 -0
  412. scitex/fts/_kinds/_plot/_utils/__init__.py +129 -0
  413. scitex/fts/_kinds/_plot/_utils/_auto_layout.py +127 -0
  414. scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +111 -0
  415. scitex/fts/_kinds/_plot/_utils/_const_sizes.py +48 -0
  416. scitex/fts/_kinds/_plot/_utils/_convert_coords.py +77 -0
  417. scitex/fts/_kinds/_plot/_utils/_get_template.py +178 -0
  418. scitex/fts/_kinds/_plot/_utils/_normalize.py +73 -0
  419. scitex/fts/_kinds/_plot/_utils/_plot_layout.py +397 -0
  420. scitex/fts/_kinds/_plot/_utils/_validate.py +197 -0
  421. scitex/fts/_kinds/_shape/__init__.py +141 -0
  422. scitex/fts/_kinds/_stats/__init__.py +56 -0
  423. scitex/fts/_kinds/_stats/_dataclasses/_Stats.py +423 -0
  424. scitex/fts/_kinds/_stats/_dataclasses/__init__.py +48 -0
  425. scitex/fts/_kinds/_table/__init__.py +72 -0
  426. scitex/fts/_kinds/_table/_latex/__init__.py +93 -0
  427. scitex/fts/_kinds/_table/_latex/_editor/__init__.py +11 -0
  428. scitex/fts/_kinds/_table/_latex/_editor/_app.py +725 -0
  429. scitex/fts/_kinds/_table/_latex/_export.py +279 -0
  430. scitex/fts/_kinds/_table/_latex/_figure_exporter.py +153 -0
  431. scitex/fts/_kinds/_table/_latex/_stats_formatter.py +274 -0
  432. scitex/fts/_kinds/_table/_latex/_table_exporter.py +362 -0
  433. scitex/fts/_kinds/_table/_latex/_utils.py +369 -0
  434. scitex/fts/_kinds/_table/_latex/_validator.py +445 -0
  435. scitex/fts/_kinds/_text/__init__.py +77 -0
  436. scitex/fts/_schemas/data_info.schema.json +75 -0
  437. scitex/fts/_schemas/encoding.schema.json +90 -0
  438. scitex/fts/_schemas/node.schema.json +145 -0
  439. scitex/fts/_schemas/render_manifest.schema.json +62 -0
  440. scitex/fts/_schemas/stats.schema.json +132 -0
  441. scitex/fts/_schemas/theme.schema.json +141 -0
  442. scitex/fts/_stats/__init__.py +48 -0
  443. scitex/fts/_stats/_dataclasses/_Stats.py +423 -0
  444. scitex/fts/_stats/_dataclasses/__init__.py +48 -0
  445. scitex/fts/_tables/__init__.py +65 -0
  446. scitex/fts/_tables/_latex/__init__.py +93 -0
  447. scitex/fts/_tables/_latex/_editor/__init__.py +11 -0
  448. scitex/fts/_tables/_latex/_editor/_app.py +725 -0
  449. scitex/fts/_tables/_latex/_export.py +279 -0
  450. scitex/fts/_tables/_latex/_figure_exporter.py +153 -0
  451. scitex/fts/_tables/_latex/_stats_formatter.py +274 -0
  452. scitex/fts/_tables/_latex/_table_exporter.py +362 -0
  453. scitex/fts/_tables/_latex/_utils.py +369 -0
  454. scitex/fts/_tables/_latex/_validator.py +445 -0
  455. scitex/gen/__init__.py +66 -25
  456. scitex/gen/misc.py +28 -0
  457. scitex/io/__init__.py +47 -20
  458. scitex/io/_load.py +87 -36
  459. scitex/io/_load_modules/__init__.py +10 -7
  460. scitex/io/_load_modules/_pandas.py +6 -1
  461. scitex/io/_save.py +299 -1556
  462. scitex/io/_save_modules/__init__.py +76 -19
  463. scitex/io/_save_modules/_figure_utils.py +90 -0
  464. scitex/io/_save_modules/_image_csv.py +497 -0
  465. scitex/io/_save_modules/_legends.py +91 -0
  466. scitex/io/_save_modules/_pltz_bundle.py +356 -0
  467. scitex/io/_save_modules/_pltz_stx.py +536 -0
  468. scitex/io/_save_modules/_stx_bundle.py +104 -0
  469. scitex/io/_save_modules/_symlink.py +96 -0
  470. scitex/io/_save_modules/_yaml.py +1 -1
  471. scitex/io/_save_modules/_zarr.py +64 -18
  472. scitex/io/bundle/README.md +212 -0
  473. scitex/io/bundle/__init__.py +110 -0
  474. scitex/io/{_bundle.py → bundle/_core.py} +219 -89
  475. scitex/io/bundle/_nested.py +713 -0
  476. scitex/io/bundle/_types.py +74 -0
  477. scitex/io/bundle/_zip.py +487 -0
  478. scitex/io/utils/h5_to_zarr.py +1 -1
  479. scitex/logging/__init__.py +108 -13
  480. scitex/logging/_errors.py +508 -0
  481. scitex/logging/_formatters.py +30 -6
  482. scitex/logging/_warnings.py +261 -0
  483. scitex/plt/__init__.py +4 -1
  484. scitex/plt/_figrecipe.py +236 -0
  485. scitex/plt/_subplots/_AxisWrapper.py +6 -0
  486. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/__init__.py +0 -0
  487. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_labels.py +0 -0
  488. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_metadata.py +0 -0
  489. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_visual.py +0 -0
  490. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +0 -0
  491. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_base.py +0 -0
  492. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_scientific.py +0 -0
  493. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_statistical.py +0 -0
  494. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_stx_aliases.py +0 -0
  495. scitex/plt/_subplots/_AxisWrapperMixins/_RawMatplotlibMixin.py +0 -0
  496. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/__init__.py +0 -0
  497. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_base.py +0 -0
  498. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +0 -0
  499. scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +112 -1
  500. scitex/plt/_subplots/_FigWrapper.py +15 -0
  501. scitex/plt/_subplots/_SubplotsWrapper.py +125 -489
  502. scitex/plt/_subplots/_export_as_csv.py +11 -0
  503. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +2 -0
  504. scitex/plt/_subplots/_export_as_csv_formatters/_format_pcolormesh.py +66 -0
  505. scitex/plt/_subplots/_export_as_csv_formatters/_format_stackplot.py +62 -0
  506. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_bar.py +0 -0
  507. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_barh.py +0 -0
  508. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_errorbar.py +0 -0
  509. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter.py +0 -0
  510. scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py +208 -0
  511. scitex/plt/_subplots/_fonts.py +71 -0
  512. scitex/plt/_subplots/_mm_layout.py +282 -0
  513. scitex/plt/gallery/__init__.py +99 -2
  514. scitex/plt/io/_layered_bundle.py +0 -0
  515. scitex/plt/styles/_plot_postprocess.py +3 -1
  516. scitex/plt/utils/_configure_mpl.py +16 -19
  517. scitex/repro/_RandomStateManager.py +13 -8
  518. scitex/resource/__init__.py +19 -1
  519. scitex/resource/_utils/_get_env_info.py +13 -25
  520. scitex/schema/__init__.py +149 -160
  521. scitex/schema/_encoding.py +273 -0
  522. scitex/schema/_figure_elements.py +406 -0
  523. scitex/schema/_plot.py +0 -0
  524. scitex/schema/_theme.py +360 -0
  525. scitex/schema/_validation.py +0 -98
  526. scitex/scholar/__init__.py +56 -14
  527. scitex/scholar/auth/ScholarAuthManager.py +1 -1
  528. scitex/scholar/auth/__init__.py +11 -2
  529. scitex/scholar/auth/providers/BaseAuthenticator.py +1 -1
  530. scitex/scholar/auth/providers/EZProxyAuthenticator.py +1 -1
  531. scitex/scholar/auth/providers/OpenAthensAuthenticator.py +1 -1
  532. scitex/scholar/auth/providers/ShibbolethAuthenticator.py +1 -1
  533. scitex/scholar/config/ScholarConfig.py +1 -1
  534. scitex/scholar/core/Scholar.py +1 -1
  535. scitex/session/_decorator.py +18 -16
  536. scitex/session/_lifecycle.py +9 -11
  537. scitex/session/template.py +9 -8
  538. scitex/sh/test_sh.py +72 -0
  539. scitex/sh/test_sh_simple.py +61 -0
  540. scitex/stats/__init__.py +221 -97
  541. scitex/stats/_schema.py +21 -22
  542. scitex/stats/descriptive/_circular.py +212 -351
  543. scitex/stats/descriptive/_describe.py +81 -132
  544. scitex/stats/descriptive/_nan.py +205 -433
  545. scitex/stats/descriptive/_real.py +127 -141
  546. scitex/str/_format_plot_text.py +5 -5
  547. scitex/str/_latex.py +26 -84
  548. scitex/str/_latex_fallback.py +53 -47
  549. scitex/web/_search_pubmed.py +5 -4
  550. scitex/writer/tests/test_diff_between.py +451 -0
  551. scitex/writer/tests/test_document_section.py +311 -0
  552. scitex/writer/tests/test_document_workflow.py +393 -0
  553. scitex/writer/tests/test_writer.py +361 -0
  554. scitex/writer/tests/test_writer_integration.py +303 -0
  555. {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/METADATA +364 -181
  556. {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/RECORD +479 -108
  557. scitex/fig/editor/_edit.py +0 -751
  558. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/ARCHITECTURE_EXAMPLE.md +0 -905
  559. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/BULLETIN_BOARD_EXAMPLE.md +0 -99
  560. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/PROJECT_DESCRIPTION_EXAMPLE.md +0 -96
  561. {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/WHEEL +0 -0
  562. {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/entry_points.txt +0 -0
  563. {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,182 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: 2025-12-15
4
+ # Author: ywatanabe / Claude
5
+ # File: scitex/diagram/_schema.py
6
+
7
+ """
8
+ Schema definitions for SciTeX Diagram.
9
+
10
+ The schema defines paper-specific constraints that Mermaid/Graphviz don't know:
11
+ - Paper layout (single/double column, max width)
12
+ - Reading direction preferences
13
+ - Node emphasis for scientific communication
14
+ - Semantic layer grouping
15
+ """
16
+
17
+ from dataclasses import dataclass, field
18
+ from enum import Enum
19
+ from typing import List, Dict, Optional, Literal
20
+
21
+
22
+ class DiagramType(Enum):
23
+ """Semantic type of diagram - affects layout strategy."""
24
+ WORKFLOW = "workflow" # Sequential process, prefer LR/TB flow
25
+ DECISION = "decision" # Decision tree, prefer TB with branches
26
+ PIPELINE = "pipeline" # Data pipeline, strict LR with stages
27
+ HIERARCHY = "hierarchy" # Tree structure, TB with levels
28
+ COMPARISON = "comparison" # Side-by-side, two columns
29
+
30
+
31
+ class ColumnLayout(Enum):
32
+ """Paper column layout."""
33
+ SINGLE = "single" # Full width (~170mm)
34
+ DOUBLE = "double" # Half width (~85mm)
35
+
36
+
37
+ class SpacingLevel(Enum):
38
+ """Abstract spacing levels - mapped to backend-specific values."""
39
+ TIGHT = "tight" # Publication: minimal whitespace
40
+ COMPACT = "compact"
41
+ MEDIUM = "medium"
42
+ LARGE = "large"
43
+
44
+
45
+ class PaperMode(Enum):
46
+ """Paper mode affects layout density and edge visibility."""
47
+ DRAFT = "draft" # Full arrows, visible bidirectional, medium spacing
48
+ PUBLICATION = "publication" # Compact, return edges hidden/dotted
49
+
50
+
51
+ @dataclass
52
+ class PaperConstraints:
53
+ """Paper-specific constraints that affect layout."""
54
+ column: ColumnLayout = ColumnLayout.SINGLE
55
+ max_width_mm: int = 170
56
+ reading_direction: Literal["left_to_right", "top_to_bottom"] = "left_to_right"
57
+ mode: PaperMode = PaperMode.DRAFT # draft: full details, publication: compact
58
+ emphasize: List[str] = field(default_factory=list) # Node IDs to highlight
59
+
60
+ # Scientific communication hints
61
+ main_flow: List[str] = field(default_factory=list) # Critical path nodes
62
+ secondary_flow: List[str] = field(default_factory=list) # Supporting elements
63
+ return_edges: List[tuple] = field(default_factory=list) # Edges to hide in publication
64
+
65
+
66
+ @dataclass
67
+ class LayoutHints:
68
+ """Abstract layout hints - compiled to backend directives."""
69
+ layers: List[List[str]] = field(default_factory=list) # Nodes grouped by rank
70
+ alignment: Dict[str, str] = field(default_factory=dict) # Node alignment hints
71
+ layer_gap: SpacingLevel = SpacingLevel.MEDIUM
72
+ node_gap: SpacingLevel = SpacingLevel.MEDIUM
73
+
74
+ # Subgraph organization
75
+ groups: Dict[str, List[str]] = field(default_factory=dict) # Named groups
76
+
77
+
78
+ @dataclass
79
+ class NodeSpec:
80
+ """Specification for a single node."""
81
+ id: str
82
+ label: str
83
+ shape: Literal["box", "rounded", "diamond", "circle", "stadium"] = "box"
84
+ emphasis: Literal["normal", "primary", "success", "warning", "muted"] = "normal"
85
+
86
+ def short_label(self, max_chars: int = 20) -> str:
87
+ """Return truncated label for compact layouts."""
88
+ if len(self.label) <= max_chars:
89
+ return self.label
90
+ return self.label[:max_chars-3] + "..."
91
+
92
+
93
+ @dataclass
94
+ class EdgeSpec:
95
+ """Specification for an edge between nodes."""
96
+ source: str
97
+ target: str
98
+ label: Optional[str] = None
99
+ style: Literal["solid", "dashed", "dotted"] = "solid"
100
+ arrow: Literal["normal", "none", "open"] = "normal"
101
+
102
+
103
+ @dataclass
104
+ class DiagramSpec:
105
+ """Complete diagram specification - the semantic layer."""
106
+
107
+ # Metadata
108
+ type: DiagramType = DiagramType.WORKFLOW
109
+ title: str = ""
110
+
111
+ # Paper constraints
112
+ paper: PaperConstraints = field(default_factory=PaperConstraints)
113
+
114
+ # Layout hints
115
+ layout: LayoutHints = field(default_factory=LayoutHints)
116
+
117
+ # Content
118
+ nodes: List[NodeSpec] = field(default_factory=list)
119
+ edges: List[EdgeSpec] = field(default_factory=list)
120
+
121
+ # Theme
122
+ theme: Dict[str, str] = field(default_factory=dict)
123
+
124
+ @classmethod
125
+ def from_dict(cls, data: dict) -> "DiagramSpec":
126
+ """Create DiagramSpec from dictionary (parsed YAML)."""
127
+ spec = cls()
128
+
129
+ # Parse type
130
+ if "type" in data:
131
+ spec.type = DiagramType(data["type"])
132
+
133
+ spec.title = data.get("title", "")
134
+
135
+ # Parse paper constraints
136
+ if "paper" in data:
137
+ p = data["paper"]
138
+ spec.paper = PaperConstraints(
139
+ column=ColumnLayout(p.get("column", "single")),
140
+ max_width_mm=p.get("max_width_mm", 170),
141
+ reading_direction=p.get("reading_direction", "left_to_right"),
142
+ mode=PaperMode(p.get("mode", "draft")),
143
+ emphasize=p.get("emphasize", []),
144
+ main_flow=p.get("main_flow", []),
145
+ secondary_flow=p.get("secondary_flow", []),
146
+ return_edges=[tuple(e) for e in p.get("return_edges", [])],
147
+ )
148
+
149
+ # Parse layout hints
150
+ if "layout" in data:
151
+ lt = data["layout"]
152
+ spec.layout = LayoutHints(
153
+ layers=lt.get("layers", []),
154
+ alignment=lt.get("alignment", {}),
155
+ layer_gap=SpacingLevel(lt.get("layer_gap", "medium")),
156
+ node_gap=SpacingLevel(lt.get("node_gap", "medium")),
157
+ groups=lt.get("groups", {}),
158
+ )
159
+
160
+ # Parse nodes
161
+ for n in data.get("nodes", []):
162
+ spec.nodes.append(NodeSpec(
163
+ id=n["id"],
164
+ label=n.get("label", n["id"]),
165
+ shape=n.get("shape", "box"),
166
+ emphasis=n.get("emphasis", "normal"),
167
+ ))
168
+
169
+ # Parse edges
170
+ for e in data.get("edges", []):
171
+ spec.edges.append(EdgeSpec(
172
+ source=e["from"] if "from" in e else e["source"],
173
+ target=e["to"] if "to" in e else e["target"],
174
+ label=e.get("label"),
175
+ style=e.get("style", "solid"),
176
+ arrow=e.get("arrow", "normal"),
177
+ ))
178
+
179
+ # Theme
180
+ spec.theme = data.get("theme", {})
181
+
182
+ return spec
@@ -0,0 +1,278 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: 2025-12-15
4
+ # Author: ywatanabe / Claude
5
+ # File: scitex/diagram/_split.py
6
+
7
+ """
8
+ Auto-split large diagrams into multiple figures.
9
+
10
+ Strategies:
11
+ - by_groups: Split by existing layout.groups (deterministic, paper-friendly)
12
+ - by_articulation: Split at hub nodes (graph-theoretic)
13
+
14
+ The split preserves "ghost nodes" at boundaries for visual continuity.
15
+ """
16
+
17
+ from dataclasses import dataclass, field
18
+ from typing import List, Dict, Set, Optional, Tuple
19
+ from copy import deepcopy
20
+ from enum import Enum
21
+
22
+ from scitex.diagram._schema import DiagramSpec, NodeSpec, EdgeSpec
23
+
24
+
25
+ class SplitStrategy(Enum):
26
+ BY_GROUPS = "by_groups" # Split by layout.groups
27
+ BY_ARTICULATION = "by_articulation" # Split at hub nodes
28
+
29
+
30
+ @dataclass
31
+ class SplitConfig:
32
+ """Configuration for auto-splitting."""
33
+ enabled: bool = False
34
+ max_nodes: int = 12 # Split if more nodes than this
35
+ strategy: SplitStrategy = SplitStrategy.BY_GROUPS
36
+ keep_hubs: bool = True # Show hub nodes in both parts
37
+ ghost_style: str = "muted" # Style for ghost nodes
38
+
39
+
40
+ @dataclass
41
+ class SplitResult:
42
+ """Result of splitting a diagram."""
43
+ figures: List[DiagramSpec]
44
+ labels: List[str] # Figure labels (A, B, C, ...)
45
+ cut_nodes: Set[str] # Nodes that appear in multiple figures
46
+
47
+
48
+ def split_diagram(
49
+ spec: DiagramSpec,
50
+ config: Optional[SplitConfig] = None,
51
+ group_assignments: Optional[List[List[str]]] = None,
52
+ ) -> SplitResult:
53
+ """
54
+ Split a diagram into multiple figures.
55
+
56
+ Parameters
57
+ ----------
58
+ spec : DiagramSpec
59
+ Original diagram specification.
60
+ config : SplitConfig, optional
61
+ Split configuration.
62
+ group_assignments : List[List[str]], optional
63
+ Manual group assignments for splitting.
64
+ If provided, overrides automatic detection.
65
+
66
+ Returns
67
+ -------
68
+ SplitResult
69
+ List of split diagram specifications.
70
+ """
71
+ if config is None:
72
+ config = SplitConfig(enabled=True)
73
+
74
+ # Check if split is needed
75
+ if not config.enabled or len(spec.nodes) <= config.max_nodes:
76
+ return SplitResult(figures=[spec], labels=[""], cut_nodes=set())
77
+
78
+ # Determine groups to split by
79
+ if group_assignments:
80
+ groups = group_assignments
81
+ elif config.strategy == SplitStrategy.BY_GROUPS:
82
+ groups = _split_by_groups(spec, max_nodes=config.max_nodes)
83
+ else: # BY_ARTICULATION
84
+ groups = _split_by_articulation(spec)
85
+
86
+ # Create split figures
87
+ figures = []
88
+ labels = []
89
+ cut_nodes = set()
90
+
91
+ for i, group_nodes in enumerate(groups):
92
+ fig, cuts = _create_split_figure(spec, group_nodes, config)
93
+ figures.append(fig)
94
+ labels.append(chr(ord('A') + i))
95
+ cut_nodes.update(cuts)
96
+
97
+ return SplitResult(figures=figures, labels=labels, cut_nodes=cut_nodes)
98
+
99
+
100
+ def _split_by_groups(spec: DiagramSpec, max_nodes: int = 12) -> List[List[str]]:
101
+ """
102
+ Split by existing layout.groups using greedy packing.
103
+
104
+ Packs groups into figures until max_nodes is exceeded,
105
+ then starts a new figure.
106
+
107
+ Returns list of node ID lists, one per split figure.
108
+ """
109
+ if not spec.layout.groups:
110
+ # No groups defined - try to split in half
111
+ node_ids = [n.id for n in spec.nodes]
112
+ mid = len(node_ids) // 2
113
+ return [node_ids[:mid], node_ids[mid:]]
114
+
115
+ # Group keys in order
116
+ group_names = list(spec.layout.groups.keys())
117
+
118
+ # Greedy packing: add groups until max_nodes exceeded
119
+ figures = []
120
+ current_figure = []
121
+ current_count = 0
122
+
123
+ for group_name in group_names:
124
+ group_nodes = spec.layout.groups[group_name]
125
+ group_size = len(group_nodes)
126
+
127
+ # If adding this group exceeds max and we have something, start new figure
128
+ if current_count + group_size > max_nodes and current_figure:
129
+ figures.append(current_figure)
130
+ current_figure = []
131
+ current_count = 0
132
+
133
+ # Add group to current figure
134
+ current_figure.extend(group_nodes)
135
+ current_count += group_size
136
+
137
+ # Don't forget the last figure
138
+ if current_figure:
139
+ figures.append(current_figure)
140
+
141
+ # Add ungrouped nodes to first figure
142
+ grouped = set()
143
+ for fig in figures:
144
+ grouped.update(fig)
145
+ for n in spec.nodes:
146
+ if n.id not in grouped:
147
+ if figures:
148
+ figures[0].append(n.id)
149
+ else:
150
+ figures.append([n.id])
151
+
152
+ # Ensure at least 2 figures if we have enough nodes
153
+ if len(figures) == 1 and len(figures[0]) > max_nodes:
154
+ # Force split in half
155
+ nodes = figures[0]
156
+ mid = len(nodes) // 2
157
+ figures = [nodes[:mid], nodes[mid:]]
158
+
159
+ return figures
160
+
161
+
162
+ def _split_by_articulation(spec: DiagramSpec) -> List[List[str]]:
163
+ """
164
+ Split at articulation points (hub nodes).
165
+
166
+ This finds nodes that, if removed, would disconnect the graph.
167
+ These are natural split points for large diagrams.
168
+ """
169
+ # Build adjacency
170
+ adj: Dict[str, Set[str]] = {n.id: set() for n in spec.nodes}
171
+ for e in spec.edges:
172
+ adj[e.source].add(e.target)
173
+ adj[e.target].add(e.source)
174
+
175
+ # Find node with most connections (hub)
176
+ hub = max(adj.keys(), key=lambda x: len(adj[x]))
177
+
178
+ # BFS from first node, stopping at hub
179
+ visited = {hub} # Block the hub
180
+ node_ids = [n.id for n in spec.nodes if n.id != hub]
181
+
182
+ if not node_ids:
183
+ return [[hub]]
184
+
185
+ # Find components when hub is removed
186
+ components = []
187
+ for start in node_ids:
188
+ if start in visited:
189
+ continue
190
+ component = []
191
+ queue = [start]
192
+ while queue:
193
+ curr = queue.pop(0)
194
+ if curr in visited:
195
+ continue
196
+ visited.add(curr)
197
+ component.append(curr)
198
+ for neighbor in adj[curr]:
199
+ if neighbor not in visited:
200
+ queue.append(neighbor)
201
+ if component:
202
+ components.append(component)
203
+
204
+ # Add hub to each component (as ghost)
205
+ for comp in components:
206
+ comp.append(hub)
207
+
208
+ return components if components else [[n.id for n in spec.nodes]]
209
+
210
+
211
+ def _create_split_figure(
212
+ spec: DiagramSpec,
213
+ node_ids: List[str],
214
+ config: SplitConfig,
215
+ ) -> Tuple[DiagramSpec, Set[str]]:
216
+ """
217
+ Create a split figure containing specified nodes.
218
+
219
+ Returns (figure_spec, ghost_node_ids).
220
+ """
221
+ node_id_set = set(node_ids)
222
+
223
+ # Find edges that cross the boundary
224
+ boundary_nodes = set()
225
+ for edge in spec.edges:
226
+ src_in = edge.source in node_id_set
227
+ tgt_in = edge.target in node_id_set
228
+ if src_in and not tgt_in:
229
+ if config.keep_hubs:
230
+ boundary_nodes.add(edge.target)
231
+ elif tgt_in and not src_in:
232
+ if config.keep_hubs:
233
+ boundary_nodes.add(edge.source)
234
+
235
+ # Create new spec
236
+ new_spec = DiagramSpec(
237
+ type=spec.type,
238
+ title=spec.title,
239
+ paper=deepcopy(spec.paper),
240
+ layout=deepcopy(spec.layout),
241
+ theme=dict(spec.theme),
242
+ )
243
+
244
+ # Filter nodes
245
+ node_map = {n.id: n for n in spec.nodes}
246
+ for node_id in node_ids:
247
+ if node_id in node_map:
248
+ new_spec.nodes.append(deepcopy(node_map[node_id]))
249
+
250
+ # Add ghost nodes (boundary nodes not in this split)
251
+ for ghost_id in boundary_nodes:
252
+ if ghost_id in node_map and ghost_id not in node_id_set:
253
+ ghost = deepcopy(node_map[ghost_id])
254
+ ghost.emphasis = config.ghost_style
255
+ ghost.label = f"→ {ghost.label}" # Mark as continuation
256
+ new_spec.nodes.append(ghost)
257
+
258
+ # Filter edges
259
+ all_ids = node_id_set | boundary_nodes
260
+ for edge in spec.edges:
261
+ if edge.source in all_ids and edge.target in all_ids:
262
+ new_spec.edges.append(deepcopy(edge))
263
+
264
+ # Filter groups
265
+ new_spec.layout.groups = {}
266
+ for group_name, group_nodes in spec.layout.groups.items():
267
+ filtered = [n for n in group_nodes if n in all_ids]
268
+ if filtered:
269
+ new_spec.layout.groups[group_name] = filtered
270
+
271
+ # Filter layers
272
+ new_spec.layout.layers = []
273
+ for layer in spec.layout.layers:
274
+ filtered = [n for n in layer if n in all_ids]
275
+ if filtered:
276
+ new_spec.layout.layers.append(filtered)
277
+
278
+ return new_spec, boundary_nodes
scitex/dict/_pop_keys.py CHANGED
@@ -1,11 +1,7 @@
1
1
  #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
2
  # Timestamp: "2025-11-10 22:40:16 (ywatanabe)"
4
3
 
5
4
 
6
- import numpy as np
7
-
8
-
9
5
  def pop_keys(keys_list, keys_to_pop):
10
6
  """Remove specified keys from a list of keys.
11
7
 
@@ -28,9 +24,7 @@ def pop_keys(keys_list, keys_to_pop):
28
24
  >>> pop_keys(keys_list, keys_to_pop)
29
25
  ['a', 'c', 'e', 'bde']
30
26
  """
31
- indi_to_remain = [k not in keys_to_pop for k in keys_list]
32
- keys_remainded_list = list(np.array(keys_list)[list(indi_to_remain)])
33
- return keys_remainded_list
27
+ return [k for k in keys_list if k not in keys_to_pop]
34
28
 
35
29
 
36
30
  # EOF
scitex/dsp/__init__.py CHANGED
@@ -3,20 +3,23 @@
3
3
 
4
4
  import warnings
5
5
 
6
+ # Import example, params, norm, reference, filt, and add_noise modules as submodules
7
+ from . import add_noise, example, filt, norm, params, reference
8
+
6
9
  # Core imports that should always work
7
10
  from ._crop import crop
8
11
  from ._demo_sig import demo_sig
9
12
  from ._detect_ripples import (
10
- detect_ripples,
11
- _preprocess,
12
- _find_events,
13
- _drop_ripples_at_edges,
14
13
  _calc_relative_peak_position,
14
+ _drop_ripples_at_edges,
15
+ _find_events,
16
+ _preprocess,
15
17
  _sort_columns,
18
+ detect_ripples,
16
19
  )
17
20
  from ._ensure_3d import ensure_3d
18
21
  from ._hilbert import hilbert
19
- from ._modulation_index import modulation_index, _reshape
22
+ from ._modulation_index import _reshape, modulation_index
20
23
  from ._pac import pac
21
24
  from ._psd import band_powers, psd
22
25
  from ._resample import resample
@@ -24,16 +27,12 @@ from ._time import time
24
27
  from ._transform import to_segments, to_sktime_df
25
28
  from ._wavelet import wavelet
26
29
 
27
- # Import example and params modules as submodules
28
- from . import example
29
- from . import params
30
-
31
30
  # Try to import audio-related functions that require PortAudio
32
31
  try:
33
32
  from ._listen import list_and_select_device
34
33
 
35
34
  _audio_available = True
36
- except (ImportError, OSError) as e:
35
+ except (ImportError, OSError):
37
36
  warnings.warn(
38
37
  "Audio functionality unavailable: PortAudio library not found. "
39
38
  "Install PortAudio to use audio features (e.g., sudo apt-get install portaudio19-dev)",
@@ -62,17 +61,23 @@ __all__ = [
62
61
  "_preprocess",
63
62
  "_reshape",
64
63
  "_sort_columns",
64
+ "add_noise",
65
65
  "band_powers",
66
66
  "crop",
67
67
  "demo_sig",
68
68
  "detect_ripples",
69
69
  "ensure_3d",
70
+ "example",
71
+ "filt",
70
72
  "get_eeg_pos",
71
73
  "hilbert",
72
74
  "list_and_select_device",
73
75
  "modulation_index",
76
+ "norm",
74
77
  "pac",
78
+ "params",
75
79
  "psd",
80
+ "reference",
76
81
  "resample",
77
82
  "time",
78
83
  "to_segments",
scitex/dsp/add_noise.py CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
2
  # Time-stamp: "ywatanabe (2024-11-02 23:09:49)"
4
3
  # File: ./scitex_repo/src/scitex/dsp/add_noise.py
5
4
 
6
5
  import torch
6
+
7
7
  from scitex.decorators import signal_fn
8
8
 
9
9
 
@@ -50,9 +50,11 @@ def pink(x, amp=1.0, dim=-1):
50
50
 
51
51
  @signal_fn
52
52
  def brown(x, amp=1.0, dim=-1):
53
+ from scitex.dsp import norm
54
+
53
55
  noise = _uniform(x.shape, amp=amp)
54
56
  noise = torch.cumsum(noise, dim=dim)
55
- noise = scitex.dsp.norm.minmax(noise, amp=amp, dim=dim)
57
+ noise = norm.minmax(noise, amp=amp, dim=dim)
56
58
  return x + noise.to(x.device)
57
59
 
58
60
 
@@ -60,6 +62,7 @@ if __name__ == "__main__":
60
62
  import sys
61
63
 
62
64
  import matplotlib.pyplot as plt
65
+
63
66
  import scitex
64
67
 
65
68
  # Start
scitex/dsp/example.py CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
2
  # Time-stamp: "2024-04-06 01:36:18 (ywatanabe)"
4
3
 
5
4
  import matplotlib
@@ -9,6 +8,17 @@ import matplotlib.pyplot as plt
9
8
  import pandas as pd
10
9
  import scitex
11
10
 
11
+ import scitex
12
+
13
+ # Module-level constants (defaults for example functions)
14
+ TGT_FS = 512
15
+ LOW_HZ = 20
16
+ HIGH_HZ = 50
17
+ SIGMA = 10
18
+
19
+ # Default color cycle
20
+ CC = {"blue": "#1f77b4", "red": "#d62728", "green": "#2ca02c"}
21
+
12
22
 
13
23
  # Functions
14
24
  def calc_norm_resample_filt_hilbert(xx, tt, fs, sig_type, verbose=True):
@@ -17,18 +27,19 @@ def calc_norm_resample_filt_hilbert(xx, tt, fs, sig_type, verbose=True):
17
27
  if sig_type == "tensorpac":
18
28
  xx = xx[:, :, 0]
19
29
 
20
- sigs[f"orig"] = (xx, tt, fs)
30
+ sigs["orig"] = (xx, tt, fs)
21
31
 
22
32
  # Normalization
23
33
  sigs["z_normed"] = (scitex.dsp.norm.z(xx), tt, fs)
24
34
  sigs["minmax_normed"] = (scitex.dsp.norm.minmax(xx), tt, fs)
25
35
 
26
36
  # Resampling
27
- sigs["resampled"] = (
28
- scitex.dsp.resample(xx, fs, TGT_FS),
29
- tt[:: int(fs / TGT_FS)],
30
- TGT_FS,
31
- )
37
+ resampled_xx = scitex.dsp.resample(xx, fs, TGT_FS)
38
+ # Create proper time vector for resampled signal
39
+ import numpy as np
40
+
41
+ resampled_tt = np.linspace(tt[0], tt[-1], resampled_xx.shape[-1])
42
+ sigs["resampled"] = (resampled_xx, resampled_tt, TGT_FS)
32
43
 
33
44
  # Noise injection
34
45
  sigs["gaussian_noise_added"] = (scitex.dsp.add_noise.gauss(xx), tt, fs)
@@ -36,15 +47,16 @@ def calc_norm_resample_filt_hilbert(xx, tt, fs, sig_type, verbose=True):
36
47
  sigs["pink_noise_added"] = (scitex.dsp.add_noise.pink(xx), tt, fs)
37
48
  sigs["brown_noise_added"] = (scitex.dsp.add_noise.brown(xx), tt, fs)
38
49
 
39
- # Filtering
50
+ # Filtering (bands format is [[low_hz, high_hz]])
51
+ bands = [[LOW_HZ, HIGH_HZ]]
40
52
  sigs[f"bandpass_filted ({LOW_HZ} - {HIGH_HZ} Hz)"] = (
41
- scitex.dsp.filt.bandpass(xx, fs, low_hz=LOW_HZ, high_hz=HIGH_HZ),
53
+ scitex.dsp.filt.bandpass(xx, fs, bands),
42
54
  tt,
43
55
  fs,
44
56
  )
45
57
 
46
58
  sigs[f"bandstop_filted ({LOW_HZ} - {HIGH_HZ} Hz)"] = (
47
- scitex.dsp.filt.bandstop(xx, fs, low_hz=LOW_HZ, high_hz=HIGH_HZ),
59
+ scitex.dsp.filt.bandstop(xx, fs, bands),
48
60
  tt,
49
61
  fs,
50
62
  )
@@ -88,18 +100,19 @@ def plot_signals(plt, sigs, sig_type):
88
100
  # if sig_type == "tensorpac":
89
101
  # xx = xx[:, :, 0]
90
102
 
91
- try:
92
- ax.plot(
93
- tt,
94
- xx[i_batch, i_ch],
95
- label=col,
96
- c=CC["red"] if col == "hilbert_amp" else CC["blue"],
97
- )
98
- except Exception as e:
99
- print(e)
100
- import ipdb
101
-
102
- ipdb.set_trace()
103
+ # Handle potential shape mismatches from filter operations
104
+ signal = xx[i_batch, i_ch]
105
+ if hasattr(signal, "squeeze"):
106
+ signal = signal.squeeze()
107
+ if hasattr(signal, "numpy"):
108
+ signal = signal.numpy()
109
+
110
+ ax.plot(
111
+ tt,
112
+ signal,
113
+ label=col,
114
+ c=CC["red"] if col == "hilbert_amp" else CC["blue"],
115
+ )
103
116
 
104
117
  # Adjustments
105
118
  ax.legend(loc="upper left")