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
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,197 @@
1
+ # SciTeX Diagram
2
+
3
+ Paper-optimized diagram generation with semantic constraints.
4
+
5
+ ## Overview
6
+
7
+ SciTeX Diagram provides a **semantic layer** above Mermaid/Graphviz that understands paper constraints:
8
+ - Column width (single/double)
9
+ - Reading direction
10
+ - Node emphasis for scientific communication
11
+ - Automatic splitting of large diagrams
12
+
13
+ **Key insight**: LLMs are good at generating *constraints*, not pixel layouts. SciTeX Diagram defines "what this diagram means for a paper" and compiles that to backend-specific layout directives.
14
+
15
+ ## Architecture
16
+
17
+ ```
18
+ scitex-diagram.yaml ← Semantic layer (human/LLM readable)
19
+
20
+ Compiler (applies paper constraints)
21
+
22
+ workflow.mmd / workflow.dot ← Backend output
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```python
28
+ from scitex.diagram import Diagram
29
+
30
+ # Create programmatically
31
+ d = Diagram(type="workflow", title="Data Pipeline")
32
+ d.add_node("input", "Raw Data", shape="stadium")
33
+ d.add_node("process", "Transform", emphasis="primary")
34
+ d.add_node("output", "Results", shape="stadium")
35
+ d.add_edge("input", "process")
36
+ d.add_edge("process", "output")
37
+
38
+ # Export
39
+ d.to_mermaid("pipeline.mmd")
40
+ d.to_graphviz("pipeline.dot")
41
+ ```
42
+
43
+ ## From YAML Specification
44
+
45
+ ```yaml
46
+ # workflow.diagram.yaml
47
+ type: workflow
48
+ title: SciTeX Figure Lifecycle
49
+
50
+ paper:
51
+ column: single
52
+ mode: publication # draft | publication
53
+ emphasize: [figz_bundle, editor]
54
+ return_edges: # Hide in publication mode
55
+ - [editor, figz_bundle]
56
+
57
+ layout:
58
+ layer_gap: tight
59
+ layers: # rank=same constraints
60
+ - [python, savefig]
61
+ - [figz_bundle]
62
+ - [editor, ai_review]
63
+
64
+ nodes:
65
+ - id: python
66
+ label: Python
67
+ shape: rounded
68
+ - id: figz_bundle
69
+ label: .figz Bundle
70
+ shape: stadium
71
+ emphasis: primary
72
+
73
+ edges:
74
+ - from: python
75
+ to: savefig
76
+ - from: savefig
77
+ to: figz_bundle
78
+ ```
79
+
80
+ ```python
81
+ d = Diagram.from_yaml("workflow.diagram.yaml")
82
+ d.to_mermaid("workflow.mmd")
83
+ ```
84
+
85
+ ## Paper Modes
86
+
87
+ ### Draft Mode (default)
88
+ - Full arrows and labels
89
+ - Medium spacing
90
+ - All edges visible
91
+
92
+ ### Publication Mode
93
+ - Tight spacing (`ranksep=0.3, nodesep=0.2`)
94
+ - Return edges hidden (invisible but constrain layout)
95
+ - No clusters in Graphviz (uses `rank=same` only)
96
+
97
+ ```yaml
98
+ paper:
99
+ mode: publication
100
+ return_edges:
101
+ - [editor, figz_bundle] # Will be invisible
102
+ ```
103
+
104
+ ## Auto-Split Large Diagrams
105
+
106
+ ```python
107
+ d = Diagram.from_yaml("large_workflow.yaml")
108
+
109
+ # Split if > 8 nodes per figure
110
+ parts = d.split(max_nodes=8, strategy="by_groups")
111
+
112
+ for i, part in enumerate(parts):
113
+ part.to_mermaid(f"fig_{chr(65+i)}.mmd") # fig_A.mmd, fig_B.mmd
114
+ ```
115
+
116
+ ### Split Strategies
117
+
118
+ | Strategy | Description |
119
+ |----------|-------------|
120
+ | `by_groups` | Split by layout.groups (deterministic, paper-friendly) |
121
+ | `by_articulation` | Split at hub nodes (graph-theoretic) |
122
+
123
+ Ghost nodes are automatically added at boundaries with `→` prefix.
124
+
125
+ ## Diagram Types
126
+
127
+ | Type | Direction | Use Case |
128
+ |------|-----------|----------|
129
+ | `workflow` | LR | Sequential processes |
130
+ | `decision` | TB | Decision trees |
131
+ | `pipeline` | LR | Data pipelines with stages |
132
+ | `hierarchy` | TB | Tree structures |
133
+ | `comparison` | LR | Side-by-side comparison |
134
+
135
+ ## Node Shapes
136
+
137
+ | Shape | Mermaid | Use Case |
138
+ |-------|---------|----------|
139
+ | `box` | `["label"]` | Default |
140
+ | `rounded` | `("label")` | Processes |
141
+ | `stadium` | `(["label"])` | Start/End |
142
+ | `diamond` | `{"label"}` | Decisions |
143
+ | `circle` | `(("label"))` | Events |
144
+
145
+ ## Emphasis Levels
146
+
147
+ | Level | Color | Use Case |
148
+ |-------|-------|----------|
149
+ | `normal` | Dark | Default |
150
+ | `primary` | Blue | Key nodes |
151
+ | `success` | Green | Positive outcomes |
152
+ | `warning` | Red | Negative outcomes |
153
+ | `muted` | Gray | Secondary/derived |
154
+
155
+ ## Graphviz Output
156
+
157
+ For tightest layouts, use Graphviz:
158
+
159
+ ```bash
160
+ # Render DOT to PNG
161
+ dot -Tpng workflow.dot -o workflow.png
162
+
163
+ # Render DOT to SVG (vector)
164
+ dot -Tsvg workflow.dot -o workflow.svg
165
+ ```
166
+
167
+ Note: Mermaid doesn't support `rank=same` constraints, so Graphviz produces more compact output.
168
+
169
+ ## API Reference
170
+
171
+ ### Diagram Class
172
+
173
+ ```python
174
+ Diagram(type="workflow", title="", column="single")
175
+ Diagram.from_yaml(path)
176
+ Diagram.from_mermaid(path, diagram_type="workflow")
177
+
178
+ diagram.add_node(id, label, shape="box", emphasis="normal")
179
+ diagram.add_edge(source, target, label=None, style="solid")
180
+ diagram.set_group(group_name, node_ids)
181
+ diagram.emphasize(*node_ids)
182
+
183
+ diagram.to_mermaid(path=None) -> str
184
+ diagram.to_graphviz(path=None) -> str
185
+ diagram.to_yaml(path=None) -> str
186
+ diagram.split(max_nodes=12, strategy="by_groups") -> List[Diagram]
187
+ ```
188
+
189
+ ### Schema Classes
190
+
191
+ ```python
192
+ DiagramSpec # Complete specification
193
+ PaperConstraints # column, mode, emphasize, return_edges
194
+ LayoutHints # groups, layers, layer_gap, node_gap
195
+ NodeSpec # id, label, shape, emphasis
196
+ EdgeSpec # source, target, label, style
197
+ ```
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: 2025-12-15
4
+ # Author: ywatanabe / Claude
5
+ # File: scitex/diagram/__init__.py
6
+
7
+ """
8
+ SciTeX Diagram - Paper-optimized diagram generation.
9
+
10
+ This module provides a semantic layer above Mermaid/Graphviz/D2 that
11
+ understands paper constraints (column width, reading direction, emphasis)
12
+ and compiles to backend formats with appropriate layout hints.
13
+
14
+ Key insight: LLMs are good at generating CONSTRAINTS, not pixel layouts.
15
+ SciTeX Diagram defines "what this diagram means for a paper" and compiles
16
+ that to backend-specific layout directives.
17
+
18
+ Example
19
+ -------
20
+ >>> from scitex.diagram import Diagram
21
+ >>>
22
+ >>> diagram = Diagram.from_yaml("workflow.diagram.yaml")
23
+ >>> diagram.to_mermaid("workflow.mmd")
24
+ >>> diagram.to_graphviz("workflow.dot")
25
+ """
26
+
27
+ from scitex.diagram._schema import DiagramSpec, PaperConstraints, LayoutHints, PaperMode
28
+ from scitex.diagram._diagram import Diagram
29
+ from scitex.diagram._compile import compile_to_mermaid, compile_to_graphviz
30
+ from scitex.diagram._presets import WORKFLOW_PRESET, DECISION_PRESET, PIPELINE_PRESET
31
+ from scitex.diagram._split import split_diagram, SplitConfig, SplitStrategy, SplitResult
32
+
33
+ __all__ = [
34
+ "Diagram",
35
+ "DiagramSpec",
36
+ "PaperConstraints",
37
+ "LayoutHints",
38
+ "PaperMode",
39
+ "compile_to_mermaid",
40
+ "compile_to_graphviz",
41
+ "WORKFLOW_PRESET",
42
+ "DECISION_PRESET",
43
+ "PIPELINE_PRESET",
44
+ "split_diagram",
45
+ "SplitConfig",
46
+ "SplitStrategy",
47
+ "SplitResult",
48
+ ]
@@ -0,0 +1,312 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: 2025-12-15
4
+ # Author: ywatanabe / Claude
5
+ # File: scitex/diagram/_compile.py
6
+
7
+ """
8
+ Compilers from DiagramSpec to backend formats (Mermaid, Graphviz).
9
+
10
+ The compiler applies paper constraints to generate backend-specific
11
+ layout directives. This is where domain knowledge about "good paper figures"
12
+ gets encoded.
13
+ """
14
+
15
+ import json
16
+ from typing import Optional
17
+ from scitex.diagram._schema import (
18
+ DiagramSpec, DiagramType, ColumnLayout, SpacingLevel, PaperMode
19
+ )
20
+ from scitex.diagram._presets import get_preset, DiagramPreset
21
+
22
+
23
+ def compile_to_mermaid(
24
+ spec: DiagramSpec,
25
+ preset: Optional[DiagramPreset] = None
26
+ ) -> str:
27
+ """
28
+ Compile DiagramSpec to Mermaid format with paper-optimized settings.
29
+
30
+ Parameters
31
+ ----------
32
+ spec : DiagramSpec
33
+ The semantic diagram specification.
34
+ preset : DiagramPreset, optional
35
+ Override preset (default: inferred from spec.type).
36
+
37
+ Returns
38
+ -------
39
+ str
40
+ Mermaid diagram source code.
41
+ """
42
+ if preset is None:
43
+ preset = get_preset(spec.type.value)
44
+
45
+ lines = []
46
+
47
+ # Theme initialization
48
+ theme_vars = {**preset.mermaid_theme, **spec.theme}
49
+ theme_json = json.dumps({"theme": "base", "themeVariables": theme_vars})
50
+ lines.append(f"%%{{init: {theme_json}}}%%")
51
+
52
+ # Determine direction based on paper constraints
53
+ direction = preset.mermaid_direction
54
+ if spec.paper.reading_direction == "top_to_bottom":
55
+ direction = "TB"
56
+ elif spec.paper.column == ColumnLayout.DOUBLE:
57
+ # Double column prefers vertical to save horizontal space
58
+ direction = "TB"
59
+
60
+ lines.append(f"graph {direction}")
61
+
62
+ # Build node ID to spec mapping
63
+ node_map = {n.id: n for n in spec.nodes}
64
+
65
+ # Generate subgraphs for groups
66
+ indent = " "
67
+ for group_name, group_nodes in spec.layout.groups.items():
68
+ lines.append(f'{indent}subgraph {_sanitize_id(group_name)}["{group_name}"]')
69
+ for node_id in group_nodes:
70
+ if node_id in node_map:
71
+ node = node_map[node_id]
72
+ lines.append(f"{indent}{indent}{_mermaid_node(node, preset)}")
73
+ lines.append(f"{indent}end")
74
+
75
+ # Generate standalone nodes (not in any group)
76
+ grouped_nodes = set()
77
+ for group_nodes in spec.layout.groups.values():
78
+ grouped_nodes.update(group_nodes)
79
+
80
+ for node in spec.nodes:
81
+ if node.id not in grouped_nodes:
82
+ lines.append(f"{indent}{_mermaid_node(node, preset)}")
83
+
84
+ # Generate edges
85
+ for edge in spec.edges:
86
+ edge_str = _mermaid_edge(edge)
87
+ lines.append(f"{indent}{edge_str}")
88
+
89
+ # Generate styles for emphasized nodes
90
+ for node in spec.nodes:
91
+ if node.emphasis != "normal" or node.id in spec.paper.emphasize:
92
+ emphasis = "primary" if node.id in spec.paper.emphasize else node.emphasis
93
+ style = preset.emphasis_styles.get(emphasis, {})
94
+ if style:
95
+ style_parts = [f"{k}:{v}" for k, v in style.items()]
96
+ lines.append(f"{indent}style {_sanitize_id(node.id)} {','.join(style_parts)}")
97
+
98
+ return "\n".join(lines)
99
+
100
+
101
+ def compile_to_graphviz(
102
+ spec: DiagramSpec,
103
+ preset: Optional[DiagramPreset] = None
104
+ ) -> str:
105
+ """
106
+ Compile DiagramSpec to Graphviz DOT format.
107
+
108
+ Parameters
109
+ ----------
110
+ spec : DiagramSpec
111
+ The semantic diagram specification.
112
+ preset : DiagramPreset, optional
113
+ Override preset.
114
+
115
+ Returns
116
+ -------
117
+ str
118
+ Graphviz DOT source code.
119
+ """
120
+ if preset is None:
121
+ preset = get_preset(spec.type.value)
122
+
123
+ is_publication = spec.paper.mode == PaperMode.PUBLICATION
124
+ lines = []
125
+
126
+ # Determine direction
127
+ rankdir = preset.graphviz_rankdir
128
+ if spec.paper.reading_direction == "top_to_bottom":
129
+ rankdir = "TB"
130
+ elif spec.paper.column == ColumnLayout.DOUBLE:
131
+ rankdir = "TB"
132
+
133
+ # Get spacing - publication mode uses tight spacing
134
+ if is_publication:
135
+ spacing = preset.spacing_map.get("tight", {})
136
+ else:
137
+ spacing = preset.spacing_map.get(spec.layout.layer_gap.value, {})
138
+ ranksep = spacing.get("ranksep", preset.graphviz_ranksep)
139
+ nodesep = spacing.get("nodesep", preset.graphviz_nodesep)
140
+
141
+ lines.append("digraph G {")
142
+ lines.append(f" rankdir={rankdir};")
143
+ lines.append(f" ranksep={ranksep};")
144
+ lines.append(f" nodesep={nodesep};")
145
+ lines.append(" splines=ortho;") # Orthogonal edges for cleaner look
146
+ lines.append(' node [fontname="Helvetica", fontsize=10];')
147
+ lines.append(' edge [fontname="Helvetica", fontsize=9];')
148
+ lines.append("")
149
+
150
+ # Node map
151
+ node_map = {n.id: n for n in spec.nodes}
152
+
153
+ # Build return edges set for publication mode
154
+ return_edge_set = set()
155
+ for e in spec.paper.return_edges:
156
+ if len(e) >= 2:
157
+ return_edge_set.add((e[0], e[1]))
158
+
159
+ # Generate subgraphs (without clusters for tighter layout in publication)
160
+ if is_publication and spec.layout.layers:
161
+ # In publication mode with layers, skip clusters - use rank=same instead
162
+ for node in spec.nodes:
163
+ lines.append(f" {_graphviz_node(node, preset, spec.paper.emphasize)}")
164
+ else:
165
+ # Draft mode: use clusters for visual grouping
166
+ cluster_idx = 0
167
+ for group_name, group_nodes in spec.layout.groups.items():
168
+ lines.append(f' subgraph cluster_{cluster_idx} {{')
169
+ lines.append(f' label="{group_name}";')
170
+ for node_id in group_nodes:
171
+ if node_id in node_map:
172
+ node = node_map[node_id]
173
+ lines.append(f" {_graphviz_node(node, preset, spec.paper.emphasize)}")
174
+ lines.append(" }")
175
+ cluster_idx += 1
176
+
177
+ # Standalone nodes
178
+ grouped_nodes = set()
179
+ for group_nodes in spec.layout.groups.values():
180
+ grouped_nodes.update(group_nodes)
181
+
182
+ for node in spec.nodes:
183
+ if node.id not in grouped_nodes:
184
+ lines.append(f" {_graphviz_node(node, preset, spec.paper.emphasize)}")
185
+
186
+ lines.append("")
187
+
188
+ # Rank constraints from layers (CRITICAL for minimizing whitespace)
189
+ for layer in spec.layout.layers:
190
+ if layer:
191
+ node_ids = "; ".join(_sanitize_id(n) for n in layer)
192
+ lines.append(f" {{ rank=same; {node_ids}; }}")
193
+
194
+ lines.append("")
195
+
196
+ # Edges - handle return edges in publication mode
197
+ for edge in spec.edges:
198
+ edge_key = (edge.source, edge.target)
199
+ if is_publication and edge_key in return_edge_set:
200
+ # Make return edges invisible in publication mode
201
+ lines.append(f" {_graphviz_edge_with_style(edge, invisible=True)}")
202
+ else:
203
+ lines.append(f" {_graphviz_edge(edge)}")
204
+
205
+ lines.append("}")
206
+
207
+ return "\n".join(lines)
208
+
209
+
210
+ def _sanitize_id(s: str) -> str:
211
+ """Make string safe for use as node ID."""
212
+ import re
213
+ # Remove or replace problematic characters for Mermaid/Graphviz
214
+ s = re.sub(r'[^\w]', '_', s) # Replace non-word chars with _
215
+ s = re.sub(r'_+', '_', s) # Collapse multiple underscores
216
+ s = s.strip('_') # Remove leading/trailing underscores
217
+ return s or "node"
218
+
219
+
220
+ def _mermaid_node(node, preset: DiagramPreset) -> str:
221
+ """Generate Mermaid node definition."""
222
+ shape_template = preset.mermaid_shapes.get(node.shape, '["__LABEL__"]')
223
+ shape_str = shape_template.replace("__LABEL__", node.label)
224
+ return f"{_sanitize_id(node.id)}{shape_str}"
225
+
226
+
227
+ def _mermaid_edge(edge) -> str:
228
+ """Generate Mermaid edge definition."""
229
+ arrow = "-->" if edge.arrow == "normal" else "---"
230
+ if edge.style == "dashed":
231
+ arrow = "-.->" if edge.arrow == "normal" else "-.-"
232
+ elif edge.style == "dotted":
233
+ arrow = "..>" if edge.arrow == "normal" else "..."
234
+
235
+ src = _sanitize_id(edge.source)
236
+ tgt = _sanitize_id(edge.target)
237
+
238
+ if edge.label:
239
+ return f'{src} {arrow}|"{edge.label}"| {tgt}'
240
+ return f"{src} {arrow} {tgt}"
241
+
242
+
243
+ def _graphviz_node(node, preset: DiagramPreset, emphasize: list) -> str:
244
+ """Generate Graphviz node definition."""
245
+ shape = preset.graphviz_shapes.get(node.shape, "box")
246
+
247
+ # Get emphasis style
248
+ emphasis_key = "primary" if node.id in emphasize else node.emphasis
249
+ style = preset.emphasis_styles.get(emphasis_key, {})
250
+
251
+ attrs = [f'label="{node.label}"', f'shape={shape}']
252
+
253
+ # Collect style values (filled, rounded, etc.) - combine with comma
254
+ styles = []
255
+ if style.get("fill"):
256
+ attrs.append(f'fillcolor="{style["fill"]}"')
257
+ styles.append("filled")
258
+ if style.get("stroke"):
259
+ attrs.append(f'color="{style["stroke"]}"')
260
+ if node.shape == "rounded":
261
+ styles.append("rounded")
262
+
263
+ # Output style once with comma-separated values
264
+ if styles:
265
+ attrs.append(f'style="{",".join(styles)}"')
266
+
267
+ return f'{_sanitize_id(node.id)} [{", ".join(attrs)}];'
268
+
269
+
270
+ def _graphviz_edge(edge) -> str:
271
+ """Generate Graphviz edge definition."""
272
+ src = _sanitize_id(edge.source)
273
+ tgt = _sanitize_id(edge.target)
274
+
275
+ attrs = []
276
+ if edge.label:
277
+ attrs.append(f'label="{edge.label}"')
278
+ if edge.style == "dashed":
279
+ attrs.append("style=dashed")
280
+ elif edge.style == "dotted":
281
+ attrs.append("style=dotted")
282
+ if edge.arrow == "none":
283
+ attrs.append("arrowhead=none")
284
+
285
+ if attrs:
286
+ return f'{src} -> {tgt} [{", ".join(attrs)}];'
287
+ return f"{src} -> {tgt};"
288
+
289
+
290
+ def _graphviz_edge_with_style(edge, invisible: bool = False) -> str:
291
+ """Generate Graphviz edge with optional invisible style."""
292
+ src = _sanitize_id(edge.source)
293
+ tgt = _sanitize_id(edge.target)
294
+
295
+ attrs = []
296
+ if invisible:
297
+ attrs.append("style=invis")
298
+ # Invisible edges still constrain layout
299
+ attrs.append("constraint=true")
300
+ else:
301
+ if edge.label:
302
+ attrs.append(f'label="{edge.label}"')
303
+ if edge.style == "dashed":
304
+ attrs.append("style=dashed")
305
+ elif edge.style == "dotted":
306
+ attrs.append("style=dotted")
307
+ if edge.arrow == "none":
308
+ attrs.append("arrowhead=none")
309
+
310
+ if attrs:
311
+ return f'{src} -> {tgt} [{", ".join(attrs)}];'
312
+ return f"{src} -> {tgt};"