scitex 2.7.0__py3-none-any.whl → 2.8.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (355) hide show
  1. scitex/__init__.py +6 -2
  2. scitex/__version__.py +1 -1
  3. scitex/audio/README.md +52 -0
  4. scitex/audio/__init__.py +384 -0
  5. scitex/audio/__main__.py +129 -0
  6. scitex/audio/_tts.py +334 -0
  7. scitex/audio/engines/__init__.py +44 -0
  8. scitex/audio/engines/base.py +275 -0
  9. scitex/audio/engines/elevenlabs_engine.py +143 -0
  10. scitex/audio/engines/gtts_engine.py +162 -0
  11. scitex/audio/engines/pyttsx3_engine.py +131 -0
  12. scitex/audio/mcp_server.py +757 -0
  13. scitex/bridge/_helpers.py +1 -1
  14. scitex/bridge/_plt_vis.py +1 -1
  15. scitex/bridge/_stats_vis.py +1 -1
  16. scitex/dev/plt/__init__.py +272 -0
  17. scitex/dev/plt/plot_mpl_axhline.py +28 -0
  18. scitex/dev/plt/plot_mpl_axhspan.py +28 -0
  19. scitex/dev/plt/plot_mpl_axvline.py +28 -0
  20. scitex/dev/plt/plot_mpl_axvspan.py +28 -0
  21. scitex/dev/plt/plot_mpl_bar.py +29 -0
  22. scitex/dev/plt/plot_mpl_barh.py +29 -0
  23. scitex/dev/plt/plot_mpl_boxplot.py +28 -0
  24. scitex/dev/plt/plot_mpl_contour.py +31 -0
  25. scitex/dev/plt/plot_mpl_contourf.py +31 -0
  26. scitex/dev/plt/plot_mpl_errorbar.py +30 -0
  27. scitex/dev/plt/plot_mpl_eventplot.py +28 -0
  28. scitex/dev/plt/plot_mpl_fill.py +30 -0
  29. scitex/dev/plt/plot_mpl_fill_between.py +31 -0
  30. scitex/dev/plt/plot_mpl_hexbin.py +28 -0
  31. scitex/dev/plt/plot_mpl_hist.py +28 -0
  32. scitex/dev/plt/plot_mpl_hist2d.py +28 -0
  33. scitex/dev/plt/plot_mpl_imshow.py +29 -0
  34. scitex/dev/plt/plot_mpl_pcolormesh.py +31 -0
  35. scitex/dev/plt/plot_mpl_pie.py +29 -0
  36. scitex/dev/plt/plot_mpl_plot.py +29 -0
  37. scitex/dev/plt/plot_mpl_quiver.py +31 -0
  38. scitex/dev/plt/plot_mpl_scatter.py +28 -0
  39. scitex/dev/plt/plot_mpl_stackplot.py +31 -0
  40. scitex/dev/plt/plot_mpl_stem.py +29 -0
  41. scitex/dev/plt/plot_mpl_step.py +29 -0
  42. scitex/dev/plt/plot_mpl_violinplot.py +28 -0
  43. scitex/dev/plt/plot_sns_barplot.py +29 -0
  44. scitex/dev/plt/plot_sns_boxplot.py +29 -0
  45. scitex/dev/plt/plot_sns_heatmap.py +28 -0
  46. scitex/dev/plt/plot_sns_histplot.py +29 -0
  47. scitex/dev/plt/plot_sns_kdeplot.py +29 -0
  48. scitex/dev/plt/plot_sns_lineplot.py +31 -0
  49. scitex/dev/plt/plot_sns_scatterplot.py +29 -0
  50. scitex/dev/plt/plot_sns_stripplot.py +29 -0
  51. scitex/dev/plt/plot_sns_swarmplot.py +29 -0
  52. scitex/dev/plt/plot_sns_violinplot.py +29 -0
  53. scitex/dev/plt/plot_stx_bar.py +29 -0
  54. scitex/dev/plt/plot_stx_barh.py +29 -0
  55. scitex/dev/plt/plot_stx_box.py +28 -0
  56. scitex/dev/plt/plot_stx_boxplot.py +28 -0
  57. scitex/dev/plt/plot_stx_conf_mat.py +28 -0
  58. scitex/dev/plt/plot_stx_contour.py +31 -0
  59. scitex/dev/plt/plot_stx_ecdf.py +28 -0
  60. scitex/dev/plt/plot_stx_errorbar.py +30 -0
  61. scitex/dev/plt/plot_stx_fill_between.py +31 -0
  62. scitex/dev/plt/plot_stx_fillv.py +28 -0
  63. scitex/dev/plt/plot_stx_heatmap.py +28 -0
  64. scitex/dev/plt/plot_stx_image.py +28 -0
  65. scitex/dev/plt/plot_stx_imshow.py +28 -0
  66. scitex/dev/plt/plot_stx_joyplot.py +28 -0
  67. scitex/dev/plt/plot_stx_kde.py +28 -0
  68. scitex/dev/plt/plot_stx_line.py +28 -0
  69. scitex/dev/plt/plot_stx_mean_ci.py +28 -0
  70. scitex/dev/plt/plot_stx_mean_std.py +28 -0
  71. scitex/dev/plt/plot_stx_median_iqr.py +28 -0
  72. scitex/dev/plt/plot_stx_raster.py +28 -0
  73. scitex/dev/plt/plot_stx_rectangle.py +28 -0
  74. scitex/dev/plt/plot_stx_scatter.py +29 -0
  75. scitex/dev/plt/plot_stx_shaded_line.py +29 -0
  76. scitex/dev/plt/plot_stx_violin.py +28 -0
  77. scitex/dev/plt/plot_stx_violinplot.py +28 -0
  78. scitex/diagram/README.md +197 -0
  79. scitex/diagram/__init__.py +48 -0
  80. scitex/diagram/_compile.py +312 -0
  81. scitex/diagram/_diagram.py +355 -0
  82. scitex/diagram/_presets.py +173 -0
  83. scitex/diagram/_schema.py +182 -0
  84. scitex/diagram/_split.py +278 -0
  85. scitex/fig/__init__.py +352 -0
  86. scitex/{vis → fig}/backend/_parser.py +1 -1
  87. scitex/{vis → fig}/canvas.py +1 -1
  88. scitex/{vis → fig}/editor/__init__.py +5 -2
  89. scitex/{vis → fig}/editor/_dearpygui_editor.py +1 -1
  90. scitex/{vis → fig}/editor/_defaults.py +70 -5
  91. scitex/{vis → fig}/editor/_mpl_editor.py +1 -1
  92. scitex/{vis → fig}/editor/_qt_editor.py +182 -2
  93. scitex/{vis → fig}/editor/_tkinter_editor.py +1 -1
  94. scitex/fig/editor/edit/__init__.py +50 -0
  95. scitex/fig/editor/edit/backend_detector.py +109 -0
  96. scitex/fig/editor/edit/bundle_resolver.py +240 -0
  97. scitex/fig/editor/edit/editor_launcher.py +239 -0
  98. scitex/fig/editor/edit/manual_handler.py +53 -0
  99. scitex/fig/editor/edit/panel_loader.py +232 -0
  100. scitex/fig/editor/edit/path_resolver.py +67 -0
  101. scitex/fig/editor/flask_editor/_bbox.py +1299 -0
  102. scitex/fig/editor/flask_editor/_core.py +1429 -0
  103. scitex/{vis → fig}/editor/flask_editor/_plotter.py +38 -4
  104. scitex/fig/editor/flask_editor/_renderer.py +813 -0
  105. scitex/fig/editor/flask_editor/static/css/base/reset.css +41 -0
  106. scitex/fig/editor/flask_editor/static/css/base/typography.css +16 -0
  107. scitex/fig/editor/flask_editor/static/css/base/variables.css +85 -0
  108. scitex/fig/editor/flask_editor/static/css/components/buttons.css +217 -0
  109. scitex/fig/editor/flask_editor/static/css/components/context-menu.css +93 -0
  110. scitex/fig/editor/flask_editor/static/css/components/dropdown.css +57 -0
  111. scitex/fig/editor/flask_editor/static/css/components/forms.css +112 -0
  112. scitex/fig/editor/flask_editor/static/css/components/modal.css +59 -0
  113. scitex/fig/editor/flask_editor/static/css/components/sections.css +212 -0
  114. scitex/fig/editor/flask_editor/static/css/features/canvas.css +176 -0
  115. scitex/fig/editor/flask_editor/static/css/features/element-inspector.css +190 -0
  116. scitex/fig/editor/flask_editor/static/css/features/loading.css +59 -0
  117. scitex/fig/editor/flask_editor/static/css/features/overlay.css +45 -0
  118. scitex/fig/editor/flask_editor/static/css/features/panel-grid.css +95 -0
  119. scitex/fig/editor/flask_editor/static/css/features/selection.css +101 -0
  120. scitex/fig/editor/flask_editor/static/css/features/statistics.css +138 -0
  121. scitex/fig/editor/flask_editor/static/css/index.css +31 -0
  122. scitex/fig/editor/flask_editor/static/css/layout/container.css +7 -0
  123. scitex/fig/editor/flask_editor/static/css/layout/controls.css +56 -0
  124. scitex/fig/editor/flask_editor/static/css/layout/preview.css +78 -0
  125. scitex/fig/editor/flask_editor/static/js/alignment/axis.js +314 -0
  126. scitex/fig/editor/flask_editor/static/js/alignment/basic.js +107 -0
  127. scitex/fig/editor/flask_editor/static/js/alignment/distribute.js +54 -0
  128. scitex/fig/editor/flask_editor/static/js/canvas/canvas.js +172 -0
  129. scitex/fig/editor/flask_editor/static/js/canvas/dragging.js +258 -0
  130. scitex/fig/editor/flask_editor/static/js/canvas/resize.js +48 -0
  131. scitex/fig/editor/flask_editor/static/js/canvas/selection.js +71 -0
  132. scitex/fig/editor/flask_editor/static/js/core/api.js +288 -0
  133. scitex/fig/editor/flask_editor/static/js/core/state.js +143 -0
  134. scitex/fig/editor/flask_editor/static/js/core/utils.js +245 -0
  135. scitex/fig/editor/flask_editor/static/js/dev/element-inspector.js +992 -0
  136. scitex/fig/editor/flask_editor/static/js/editor/bbox.js +339 -0
  137. scitex/fig/editor/flask_editor/static/js/editor/element-drag.js +286 -0
  138. scitex/fig/editor/flask_editor/static/js/editor/overlay.js +371 -0
  139. scitex/fig/editor/flask_editor/static/js/editor/preview.js +293 -0
  140. scitex/fig/editor/flask_editor/static/js/main.js +426 -0
  141. scitex/fig/editor/flask_editor/static/js/shortcuts/context-menu.js +152 -0
  142. scitex/fig/editor/flask_editor/static/js/shortcuts/keyboard.js +265 -0
  143. scitex/fig/editor/flask_editor/static/js/ui/controls.js +184 -0
  144. scitex/fig/editor/flask_editor/static/js/ui/download.js +57 -0
  145. scitex/fig/editor/flask_editor/static/js/ui/help.js +100 -0
  146. scitex/fig/editor/flask_editor/static/js/ui/theme.js +34 -0
  147. scitex/fig/editor/flask_editor/templates/__init__.py +123 -0
  148. scitex/fig/editor/flask_editor/templates/_html.py +852 -0
  149. scitex/fig/editor/flask_editor/templates/_scripts.py +4933 -0
  150. scitex/fig/editor/flask_editor/templates/_styles.py +1658 -0
  151. scitex/{vis → fig}/io/__init__.py +13 -1
  152. scitex/fig/io/_bundle.py +1058 -0
  153. scitex/{vis → fig}/io/_canvas.py +1 -1
  154. scitex/{vis → fig}/io/_data.py +1 -1
  155. scitex/{vis → fig}/io/_export.py +1 -1
  156. scitex/{vis → fig}/io/_load.py +1 -1
  157. scitex/{vis → fig}/io/_panel.py +1 -1
  158. scitex/{vis → fig}/io/_save.py +1 -1
  159. scitex/{vis → fig}/model/__init__.py +1 -1
  160. scitex/{vis → fig}/model/_annotations.py +1 -1
  161. scitex/{vis → fig}/model/_axes.py +1 -1
  162. scitex/{vis → fig}/model/_figure.py +1 -1
  163. scitex/{vis → fig}/model/_guides.py +1 -1
  164. scitex/{vis → fig}/model/_plot.py +1 -1
  165. scitex/{vis → fig}/model/_styles.py +1 -1
  166. scitex/{vis → fig}/utils/__init__.py +1 -1
  167. scitex/io/__init__.py +22 -26
  168. scitex/io/_bundle.py +493 -0
  169. scitex/io/_flush.py +5 -2
  170. scitex/io/_load.py +98 -0
  171. scitex/io/_load_modules/_H5Explorer.py +5 -2
  172. scitex/io/_load_modules/_canvas.py +2 -2
  173. scitex/io/_load_modules/_image.py +3 -4
  174. scitex/io/_load_modules/_txt.py +4 -2
  175. scitex/io/_metadata.py +34 -324
  176. scitex/io/_metadata_modules/__init__.py +46 -0
  177. scitex/io/_metadata_modules/_embed.py +70 -0
  178. scitex/io/_metadata_modules/_read.py +64 -0
  179. scitex/io/_metadata_modules/_utils.py +79 -0
  180. scitex/io/_metadata_modules/embed_metadata_jpeg.py +74 -0
  181. scitex/io/_metadata_modules/embed_metadata_pdf.py +53 -0
  182. scitex/io/_metadata_modules/embed_metadata_png.py +26 -0
  183. scitex/io/_metadata_modules/embed_metadata_svg.py +62 -0
  184. scitex/io/_metadata_modules/read_metadata_jpeg.py +57 -0
  185. scitex/io/_metadata_modules/read_metadata_pdf.py +51 -0
  186. scitex/io/_metadata_modules/read_metadata_png.py +39 -0
  187. scitex/io/_metadata_modules/read_metadata_svg.py +44 -0
  188. scitex/io/_qr_utils.py +5 -3
  189. scitex/io/_save.py +548 -30
  190. scitex/io/_save_modules/_canvas.py +3 -3
  191. scitex/io/_save_modules/_image.py +5 -9
  192. scitex/io/_save_modules/_tex.py +7 -4
  193. scitex/io/_zip_bundle.py +439 -0
  194. scitex/io/utils/h5_to_zarr.py +11 -9
  195. scitex/msword/__init__.py +255 -0
  196. scitex/msword/profiles.py +357 -0
  197. scitex/msword/reader.py +753 -0
  198. scitex/msword/utils.py +289 -0
  199. scitex/msword/writer.py +362 -0
  200. scitex/plt/__init__.py +5 -2
  201. scitex/plt/_subplots/_AxesWrapper.py +6 -6
  202. scitex/plt/_subplots/_AxisWrapper.py +15 -9
  203. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/__init__.py +36 -0
  204. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_labels.py +264 -0
  205. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_metadata.py +213 -0
  206. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_visual.py +128 -0
  207. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +59 -0
  208. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_base.py +34 -0
  209. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_scientific.py +593 -0
  210. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_statistical.py +654 -0
  211. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_stx_aliases.py +527 -0
  212. scitex/plt/_subplots/_AxisWrapperMixins/_RawMatplotlibMixin.py +321 -0
  213. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/__init__.py +33 -0
  214. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_base.py +152 -0
  215. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +600 -0
  216. scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +79 -5
  217. scitex/plt/_subplots/_FigWrapper.py +6 -6
  218. scitex/plt/_subplots/_SubplotsWrapper.py +28 -18
  219. scitex/plt/_subplots/_export_as_csv.py +35 -5
  220. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +8 -0
  221. scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +10 -21
  222. scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +18 -7
  223. scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +28 -12
  224. scitex/plt/_subplots/_export_as_csv_formatters/_format_matshow.py +10 -4
  225. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_imshow.py +13 -1
  226. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +12 -2
  227. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +10 -3
  228. scitex/plt/_subplots/_export_as_csv_formatters/_format_quiver.py +10 -4
  229. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +18 -3
  230. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +44 -36
  231. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +14 -2
  232. scitex/plt/_subplots/_export_as_csv_formatters/_format_streamplot.py +11 -5
  233. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_bar.py +84 -0
  234. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_barh.py +85 -0
  235. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_conf_mat.py +14 -3
  236. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_contour.py +54 -0
  237. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_ecdf.py +14 -2
  238. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_errorbar.py +120 -0
  239. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_heatmap.py +16 -6
  240. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_image.py +29 -19
  241. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_imshow.py +63 -0
  242. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_joyplot.py +22 -5
  243. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_ci.py +18 -14
  244. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_std.py +18 -14
  245. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_median_iqr.py +18 -14
  246. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_raster.py +10 -2
  247. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter.py +51 -0
  248. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter_hist.py +18 -9
  249. scitex/plt/ax/_plot/_stx_ecdf.py +4 -2
  250. scitex/plt/gallery/_generate.py +421 -14
  251. scitex/plt/io/__init__.py +53 -0
  252. scitex/plt/io/_bundle.py +490 -0
  253. scitex/plt/io/_layered_bundle.py +1343 -0
  254. scitex/plt/styles/SCITEX_STYLE.yaml +26 -0
  255. scitex/plt/styles/__init__.py +14 -0
  256. scitex/plt/styles/presets.py +78 -0
  257. scitex/plt/utils/__init__.py +13 -1
  258. scitex/plt/utils/_collect_figure_metadata.py +10 -14
  259. scitex/plt/utils/_configure_mpl.py +6 -18
  260. scitex/plt/utils/_crop.py +32 -14
  261. scitex/plt/utils/_csv_column_naming.py +54 -0
  262. scitex/plt/utils/_figure_mm.py +116 -1
  263. scitex/plt/utils/_hitmap.py +1643 -0
  264. scitex/plt/utils/metadata/__init__.py +25 -0
  265. scitex/plt/utils/metadata/_core.py +9 -10
  266. scitex/plt/utils/metadata/_dimensions.py +6 -3
  267. scitex/plt/utils/metadata/_editable_export.py +405 -0
  268. scitex/plt/utils/metadata/_geometry_extraction.py +570 -0
  269. scitex/schema/__init__.py +109 -16
  270. scitex/schema/_canvas.py +1 -1
  271. scitex/schema/_plot.py +1015 -0
  272. scitex/schema/_stats.py +2 -2
  273. scitex/stats/__init__.py +117 -0
  274. scitex/stats/io/__init__.py +29 -0
  275. scitex/stats/io/_bundle.py +156 -0
  276. scitex/tex/__init__.py +4 -0
  277. scitex/tex/_export.py +890 -0
  278. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/METADATA +11 -1
  279. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/RECORD +294 -170
  280. scitex/io/memo.md +0 -2827
  281. scitex/plt/REQUESTS.md +0 -191
  282. scitex/plt/_subplots/TODO.md +0 -53
  283. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +0 -559
  284. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +0 -1609
  285. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +0 -447
  286. scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_between.json +0 -110
  287. scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_betweenx.json +0 -88
  288. scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fill_between.json +0 -103
  289. scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fillv.json +0 -106
  290. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/bar.json +0 -92
  291. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/barh.json +0 -92
  292. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/boxplot.json +0 -92
  293. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_bar.json +0 -84
  294. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_barh.json +0 -84
  295. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_box.json +0 -83
  296. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_boxplot.json +0 -93
  297. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violin.json +0 -91
  298. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violinplot.json +0 -91
  299. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/violinplot.json +0 -91
  300. scitex/plt/templates/research-master/scitex/vis/gallery/contour/contour.json +0 -97
  301. scitex/plt/templates/research-master/scitex/vis/gallery/contour/contourf.json +0 -98
  302. scitex/plt/templates/research-master/scitex/vis/gallery/contour/stx_contour.json +0 -84
  303. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist.json +0 -101
  304. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist2d.json +0 -96
  305. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_ecdf.json +0 -95
  306. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_joyplot.json +0 -95
  307. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_kde.json +0 -93
  308. scitex/plt/templates/research-master/scitex/vis/gallery/grid/imshow.json +0 -95
  309. scitex/plt/templates/research-master/scitex/vis/gallery/grid/matshow.json +0 -95
  310. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_conf_mat.json +0 -83
  311. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_heatmap.json +0 -92
  312. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_image.json +0 -121
  313. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_imshow.json +0 -84
  314. scitex/plt/templates/research-master/scitex/vis/gallery/line/plot.json +0 -110
  315. scitex/plt/templates/research-master/scitex/vis/gallery/line/step.json +0 -92
  316. scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_line.json +0 -95
  317. scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_shaded_line.json +0 -96
  318. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/hexbin.json +0 -95
  319. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/scatter.json +0 -95
  320. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stem.json +0 -92
  321. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stx_scatter.json +0 -84
  322. scitex/plt/templates/research-master/scitex/vis/gallery/special/pie.json +0 -94
  323. scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_raster.json +0 -109
  324. scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_rectangle.json +0 -108
  325. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/errorbar.json +0 -93
  326. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_errorbar.json +0 -84
  327. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_ci.json +0 -96
  328. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_std.json +0 -96
  329. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_median_iqr.json +0 -96
  330. scitex/plt/templates/research-master/scitex/vis/gallery/vector/quiver.json +0 -99
  331. scitex/plt/templates/research-master/scitex/vis/gallery/vector/streamplot.json +0 -100
  332. scitex/vis/__init__.py +0 -177
  333. scitex/vis/editor/_edit.py +0 -390
  334. scitex/vis/editor/flask_editor/_bbox.py +0 -529
  335. scitex/vis/editor/flask_editor/_core.py +0 -168
  336. scitex/vis/editor/flask_editor/_renderer.py +0 -393
  337. scitex/vis/editor/flask_editor/templates/__init__.py +0 -33
  338. scitex/vis/editor/flask_editor/templates/_html.py +0 -513
  339. scitex/vis/editor/flask_editor/templates/_scripts.py +0 -1261
  340. scitex/vis/editor/flask_editor/templates/_styles.py +0 -739
  341. /scitex/{vis → fig}/README.md +0 -0
  342. /scitex/{vis → fig}/backend/__init__.py +0 -0
  343. /scitex/{vis → fig}/backend/_export.py +0 -0
  344. /scitex/{vis → fig}/backend/_render.py +0 -0
  345. /scitex/{vis → fig}/docs/CANVAS_ARCHITECTURE.md +0 -0
  346. /scitex/{vis → fig}/editor/_flask_editor.py +0 -0
  347. /scitex/{vis → fig}/editor/flask_editor/__init__.py +0 -0
  348. /scitex/{vis → fig}/editor/flask_editor/_utils.py +0 -0
  349. /scitex/{vis → fig}/io/_directory.py +0 -0
  350. /scitex/{vis → fig}/model/_plot_types.py +0 -0
  351. /scitex/{vis → fig}/utils/_defaults.py +0 -0
  352. /scitex/{vis → fig}/utils/_validate.py +0 -0
  353. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/WHEEL +0 -0
  354. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/entry_points.txt +0 -0
  355. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/licenses/LICENSE +0 -0
scitex/__init__.py CHANGED
@@ -140,7 +140,7 @@ reproduce = _LazyModule("reproduce")
140
140
  rng = _LazyModule("rng")
141
141
  scholar = _LazyModule("scholar")
142
142
  writer = _LazyModule("writer")
143
- vis = _LazyModule("vis")
143
+ fig = _LazyModule("fig")
144
144
  resource = _LazyModule("resource")
145
145
  tex = _LazyModule("tex")
146
146
  linalg = _LazyModule("linalg")
@@ -161,6 +161,8 @@ capture = _LazyModule("capture")
161
161
  template = _LazyModule("template")
162
162
  cloud = _LazyModule("cloud")
163
163
  config = _LazyModule("config")
164
+ audio = _LazyModule("audio")
165
+ msword = _LazyModule("msword")
164
166
 
165
167
  # Centralized path configuration - eager loaded for convenience
166
168
  # Usage: scitex.PATHS.logs, scitex.PATHS.cache, etc.
@@ -207,7 +209,7 @@ __all__ = [
207
209
  "reproduce",
208
210
  "scholar",
209
211
  "writer",
210
- "vis",
212
+ "fig",
211
213
  "resource",
212
214
  "tex",
213
215
  "linalg",
@@ -221,6 +223,8 @@ __all__ = [
221
223
  "gists",
222
224
  "cloud",
223
225
  "config",
226
+ "audio",
227
+ "msword",
224
228
  "PATHS",
225
229
  "INJECTED",
226
230
  ]
scitex/__version__.py CHANGED
@@ -9,6 +9,6 @@ __FILE__ = "./src/scitex/__version__.py"
9
9
  __DIR__ = os.path.dirname(__FILE__)
10
10
  # ----------------------------------------
11
11
 
12
- __version__ = "2.7.0"
12
+ __version__ = "2.8.1"
13
13
 
14
14
  # EOF
scitex/audio/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # SciTeX Audio
2
+
3
+ Text-to-Speech with automatic fallback: pyttsx3 -> gtts -> elevenlabs
4
+
5
+ ## Usage
6
+
7
+ ```python
8
+ import scitex
9
+
10
+ # Basic
11
+ scitex.audio.speak("Hello!")
12
+
13
+ # Faster speech (rate in words per minute)
14
+ scitex.audio.speak("Hello!", rate=200)
15
+
16
+ # Specific backend
17
+ scitex.audio.speak("Hello", backend="pyttsx3")
18
+
19
+ # Stop speech
20
+ scitex.audio.stop_speech()
21
+ ```
22
+
23
+ ## MCP Server
24
+
25
+ Add to `~/.claude.json`:
26
+
27
+ ```json
28
+ {
29
+ "mcpServers": {
30
+ "scitex-audio": {
31
+ "command": "/path/to/python",
32
+ "args": ["-m", "scitex.audio", "--mcp"]
33
+ }
34
+ }
35
+ }
36
+ ```
37
+
38
+ ### Tools
39
+
40
+ | Tool | Description |
41
+ |------|-------------|
42
+ | `speak` | Text to speech (supports `rate` param for speed) |
43
+ | `generate_audio` | Save audio to file |
44
+ | `list_backends` | Show available backends |
45
+
46
+ ## Backends
47
+
48
+ | Backend | Cost | Internet | Install |
49
+ |---------|------|----------|---------|
50
+ | pyttsx3 | Free | No | `pip install pyttsx3` + `apt install espeak-ng` |
51
+ | gtts | Free | Yes | `pip install gTTS` |
52
+ | elevenlabs | Paid | Yes | `pip install elevenlabs` + API key |
@@ -0,0 +1,384 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-12-11 (ywatanabe)"
4
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/audio/__init__.py
5
+ # ----------------------------------------
6
+
7
+ """
8
+ SciTeX Audio Module - Text-to-Speech with Multiple Backends
9
+
10
+ Fallback order: pyttsx3 -> gtts -> elevenlabs
11
+
12
+ Backends:
13
+ - pyttsx3: System TTS (offline, free, uses espeak/SAPI5)
14
+ - gtts: Google TTS (free, requires internet)
15
+ - elevenlabs: ElevenLabs (paid, high quality)
16
+
17
+ Usage:
18
+ import scitex
19
+
20
+ # Auto-select with fallback (pyttsx3 -> gtts -> elevenlabs)
21
+ scitex.audio.speak("Hello, world!")
22
+
23
+ # Specify backend
24
+ scitex.audio.speak("Hello", backend="gtts")
25
+
26
+ # Use TTS class directly
27
+ from scitex.audio import GoogleTTS, ElevenLabsTTS, SystemTTS
28
+ tts = SystemTTS()
29
+ tts.speak("Hello!")
30
+ """
31
+
32
+ import subprocess
33
+ from typing import List, Optional
34
+
35
+ # Import from engines subpackage
36
+ from .engines import (
37
+ BaseTTS,
38
+ TTSBackend,
39
+ SystemTTS,
40
+ GoogleTTS,
41
+ ElevenLabsTTS,
42
+ )
43
+
44
+
45
+ def stop_speech() -> None:
46
+ """Stop any currently playing speech by killing espeak processes."""
47
+ try:
48
+ subprocess.run(["pkill", "-9", "espeak"], capture_output=True)
49
+ except Exception:
50
+ pass
51
+
52
+
53
+ def check_wsl_audio() -> dict:
54
+ """Check WSL audio status and connectivity.
55
+
56
+ Returns:
57
+ dict with keys:
58
+ - is_wsl: bool - whether running in WSL
59
+ - wslg_available: bool - whether WSLg is available
60
+ - pulse_server_exists: bool - whether PulseServer socket exists
61
+ - pulse_connected: bool - whether PulseAudio connection works
62
+ - windows_fallback_available: bool - whether Windows fallback is available
63
+ - recommended: str - recommended playback method
64
+ """
65
+ import os
66
+ import shutil
67
+
68
+ result = {
69
+ "is_wsl": False,
70
+ "wslg_available": False,
71
+ "pulse_server_exists": False,
72
+ "pulse_connected": False,
73
+ "windows_fallback_available": False,
74
+ "recommended": "linux",
75
+ }
76
+
77
+ # Check if in WSL
78
+ if os.path.exists("/mnt/c/Windows"):
79
+ result["is_wsl"] = True
80
+
81
+ # Check WSLg
82
+ if os.path.exists("/mnt/wslg"):
83
+ result["wslg_available"] = True
84
+
85
+ # Check PulseServer socket
86
+ if os.path.exists("/mnt/wslg/PulseServer"):
87
+ result["pulse_server_exists"] = True
88
+
89
+ # Try to connect to PulseAudio
90
+ try:
91
+ env = os.environ.copy()
92
+ env["PULSE_SERVER"] = "unix:/mnt/wslg/PulseServer"
93
+ proc = subprocess.run(
94
+ ["pactl", "info"],
95
+ capture_output=True,
96
+ timeout=5,
97
+ env=env,
98
+ )
99
+ if proc.returncode == 0:
100
+ result["pulse_connected"] = True
101
+ except Exception:
102
+ pass
103
+
104
+ # Check Windows fallback
105
+ if shutil.which("powershell.exe"):
106
+ result["windows_fallback_available"] = True
107
+
108
+ # Determine recommendation
109
+ if result["pulse_connected"]:
110
+ result["recommended"] = "linux"
111
+ elif result["windows_fallback_available"]:
112
+ result["recommended"] = "windows"
113
+ else:
114
+ result["recommended"] = "none"
115
+ else:
116
+ # Native Linux
117
+ result["recommended"] = "linux"
118
+
119
+ return result
120
+
121
+ # Keep legacy TTS import for backwards compatibility
122
+ from ._tts import TTS
123
+
124
+ __all__ = [
125
+ "speak",
126
+ "stop_speech",
127
+ "check_wsl_audio",
128
+ "TTS",
129
+ "GoogleTTS",
130
+ "ElevenLabsTTS",
131
+ "SystemTTS",
132
+ "BaseTTS",
133
+ "TTSBackend",
134
+ "get_tts",
135
+ "available_backends",
136
+ "start_mcp_server",
137
+ "FALLBACK_ORDER",
138
+ ]
139
+
140
+ # Fallback order: pyttsx3 (offline, free) -> gtts (free) -> elevenlabs (paid)
141
+ # FALLBACK_ORDER = ["pyttsx3", "gtts", "elevenlabs"]
142
+ FALLBACK_ORDER = ["gtts", "pyttsx3", "elevenlabs"]
143
+
144
+
145
+ def available_backends() -> List[str]:
146
+ """Return list of available TTS backends in fallback order."""
147
+ backends = []
148
+
149
+ # Check pyttsx3 (offline)
150
+ if SystemTTS:
151
+ try:
152
+ import pyttsx3
153
+ # Try to init to check if espeak is available
154
+ engine = pyttsx3.init()
155
+ engine.stop()
156
+ backends.append("pyttsx3")
157
+ except Exception:
158
+ pass
159
+
160
+ # Check gTTS (requires internet, but no API key)
161
+ if GoogleTTS:
162
+ backends.append("gtts")
163
+
164
+ # Check ElevenLabs (requires API key)
165
+ if ElevenLabsTTS:
166
+ import os
167
+ if os.environ.get("ELEVENLABS_API_KEY"):
168
+ backends.append("elevenlabs")
169
+
170
+ return backends
171
+
172
+
173
+ def get_tts(backend: Optional[str] = None, **kwargs) -> BaseTTS:
174
+ """Get a TTS instance for the specified backend.
175
+
176
+ Args:
177
+ backend: Backend name ('pyttsx3', 'gtts', 'elevenlabs').
178
+ Auto-selects with fallback if None.
179
+ **kwargs: Backend-specific options.
180
+
181
+ Returns:
182
+ TTS instance.
183
+
184
+ Raises:
185
+ ValueError: If no backends available or backend not found.
186
+ """
187
+ backends = available_backends()
188
+
189
+ if not backends:
190
+ raise ValueError(
191
+ "No TTS backends available. Install one of:\n"
192
+ " pip install pyttsx3 # System TTS (offline, free)\n"
193
+ " + Linux: sudo apt install espeak-ng libespeak1\n"
194
+ " pip install gTTS # Google TTS (free, needs internet)\n"
195
+ " pip install elevenlabs # ElevenLabs (paid, best quality)"
196
+ )
197
+
198
+ if backend is None:
199
+ # Use fallback order: pyttsx3 -> gtts -> elevenlabs
200
+ for b in FALLBACK_ORDER:
201
+ if b in backends:
202
+ backend = b
203
+ break
204
+
205
+ if backend == "pyttsx3" and SystemTTS and "pyttsx3" in backends:
206
+ return SystemTTS(**kwargs)
207
+ elif backend == "gtts" and GoogleTTS:
208
+ return GoogleTTS(**kwargs)
209
+ elif backend == "elevenlabs" and ElevenLabsTTS:
210
+ return ElevenLabsTTS(**kwargs)
211
+ else:
212
+ raise ValueError(
213
+ f"Backend '{backend}' not available. "
214
+ f"Available: {backends}"
215
+ )
216
+
217
+
218
+ def _try_speak_with_fallback(
219
+ text: str,
220
+ voice: Optional[str] = None,
221
+ play: bool = True,
222
+ output_path: Optional[str] = None,
223
+ **kwargs,
224
+ ) -> tuple:
225
+ """Try to speak with fallback through backends.
226
+
227
+ Returns:
228
+ (result, backend_used, error_log)
229
+ """
230
+ backends = available_backends()
231
+ errors = []
232
+
233
+ for backend in FALLBACK_ORDER:
234
+ if backend not in backends:
235
+ continue
236
+
237
+ try:
238
+ tts = get_tts(backend, **kwargs)
239
+ result = tts.speak(
240
+ text=text,
241
+ voice=voice,
242
+ play=play,
243
+ output_path=output_path,
244
+ )
245
+ return (result, backend, errors)
246
+ except Exception as e:
247
+ errors.append(f"{backend}: {str(e)}")
248
+ continue
249
+
250
+ return (None, None, errors)
251
+
252
+
253
+ # Cache for default TTS instance
254
+ _default_tts: Optional[BaseTTS] = None
255
+ _default_backend: Optional[str] = None
256
+
257
+
258
+ def speak(
259
+ text: str,
260
+ backend: Optional[str] = None,
261
+ voice: Optional[str] = None,
262
+ play: bool = True,
263
+ output_path: Optional[str] = None,
264
+ fallback: bool = True,
265
+ rate: Optional[int] = None,
266
+ speed: Optional[float] = None,
267
+ **kwargs,
268
+ ) -> Optional[str]:
269
+ """Convert text to speech with automatic fallback.
270
+
271
+ Fallback order: pyttsx3 -> gtts -> elevenlabs
272
+
273
+ Args:
274
+ text: Text to speak.
275
+ backend: TTS backend ('pyttsx3', 'gtts', 'elevenlabs').
276
+ Auto-selects with fallback if None.
277
+ voice: Voice name, ID, or language code.
278
+ play: Whether to play the audio.
279
+ output_path: Path to save audio file.
280
+ fallback: If True, try next backend on failure.
281
+ rate: Speech rate in words per minute (pyttsx3 only, default 150).
282
+ speed: Speed multiplier for gtts (1.0=normal, >1.0=faster, <1.0=slower).
283
+ **kwargs: Additional backend options.
284
+
285
+ Returns:
286
+ Path to audio file if output_path specified, else None.
287
+
288
+ Examples:
289
+ import scitex
290
+
291
+ # Simple (auto-selects with fallback)
292
+ scitex.audio.speak("Hello!")
293
+
294
+ # Faster speech (pyttsx3)
295
+ scitex.audio.speak("Hello", rate=200)
296
+
297
+ # Faster speech (gtts with pydub)
298
+ scitex.audio.speak("Hello", backend="gtts", speed=1.5)
299
+
300
+ # Specific backend (no fallback)
301
+ scitex.audio.speak("Hello", backend="pyttsx3", fallback=False)
302
+
303
+ # Different language (gTTS)
304
+ scitex.audio.speak("Bonjour", backend="gtts", voice="fr")
305
+
306
+ # Save to file
307
+ scitex.audio.speak("Test", output_path="/tmp/test.mp3")
308
+ """
309
+ global _default_tts, _default_backend
310
+
311
+ # Stop any previously running speech first
312
+ stop_speech()
313
+
314
+ # Pass rate to kwargs for pyttsx3
315
+ if rate is not None:
316
+ kwargs["rate"] = rate
317
+
318
+ # Pass speed to kwargs for gtts
319
+ if speed is not None:
320
+ kwargs["speed"] = speed
321
+
322
+ # If specific backend requested without fallback
323
+ if backend and not fallback:
324
+ tts = get_tts(backend, **kwargs)
325
+ result = tts.speak(
326
+ text=text,
327
+ voice=voice,
328
+ play=play,
329
+ output_path=output_path,
330
+ )
331
+ return str(result) if result else None
332
+
333
+ # Use fallback logic
334
+ if fallback and backend is None:
335
+ result, used_backend, errors = _try_speak_with_fallback(
336
+ text=text,
337
+ voice=voice,
338
+ play=play,
339
+ output_path=output_path,
340
+ **kwargs,
341
+ )
342
+ if result is None and errors:
343
+ raise RuntimeError(
344
+ f"All TTS backends failed:\n" + "\n".join(errors)
345
+ )
346
+ return str(result) if result else None
347
+
348
+ # Specific backend with fallback enabled
349
+ try:
350
+ tts = get_tts(backend, **kwargs)
351
+ result = tts.speak(
352
+ text=text,
353
+ voice=voice,
354
+ play=play,
355
+ output_path=output_path,
356
+ )
357
+ return str(result) if result else None
358
+ except Exception as e:
359
+ if fallback:
360
+ # Try other backends
361
+ result, used_backend, errors = _try_speak_with_fallback(
362
+ text=text,
363
+ voice=voice,
364
+ play=play,
365
+ output_path=output_path,
366
+ **kwargs,
367
+ )
368
+ if result is None:
369
+ raise RuntimeError(
370
+ f"Primary backend '{backend}' failed: {e}\n"
371
+ f"Fallback errors:\n" + "\n".join(errors)
372
+ )
373
+ return str(result) if result else None
374
+ raise
375
+
376
+
377
+ def start_mcp_server():
378
+ """Start the MCP server for audio."""
379
+ import asyncio
380
+ from .mcp_server import main
381
+ asyncio.run(main())
382
+
383
+
384
+ # EOF
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-12-11 (ywatanabe)"
4
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/audio/__main__.py
5
+ # ----------------------------------------
6
+
7
+ """
8
+ CLI entry point for SciTeX Audio.
9
+
10
+ Usage:
11
+ python -m scitex.audio --mcp # Start MCP server
12
+ python -m scitex.audio speak "Hello" # Quick TTS with fallback
13
+ python -m scitex.audio --help # Show help
14
+ """
15
+
16
+ import argparse
17
+ import asyncio
18
+ import sys
19
+
20
+
21
+ def main():
22
+ parser = argparse.ArgumentParser(
23
+ description="SciTeX Audio - Text-to-Speech with fallback (pyttsx3 -> gtts -> elevenlabs)"
24
+ )
25
+
26
+ # Global options
27
+ parser.add_argument(
28
+ "--mcp", action="store_true",
29
+ help="Start MCP server (for Claude Code integration)"
30
+ )
31
+
32
+ subparsers = parser.add_subparsers(dest="command", help="Commands")
33
+
34
+ # Speak command
35
+ speak_parser = subparsers.add_parser("speak", help="Quick text-to-speech")
36
+ speak_parser.add_argument("text", help="Text to speak")
37
+ speak_parser.add_argument(
38
+ "-b", "--backend",
39
+ choices=["pyttsx3", "gtts", "elevenlabs"],
40
+ help="TTS backend (auto-selects with fallback if not specified)"
41
+ )
42
+ speak_parser.add_argument(
43
+ "-v", "--voice", help="Voice name or language code"
44
+ )
45
+ speak_parser.add_argument(
46
+ "-o", "--output", help="Save to file"
47
+ )
48
+ speak_parser.add_argument(
49
+ "--no-play", action="store_true", help="Don't play audio"
50
+ )
51
+ speak_parser.add_argument(
52
+ "--no-fallback", action="store_true", help="Disable fallback"
53
+ )
54
+
55
+ # List backends
56
+ backends_parser = subparsers.add_parser("backends", help="List available backends")
57
+
58
+ # List voices
59
+ voices_parser = subparsers.add_parser("voices", help="List available voices")
60
+ voices_parser.add_argument(
61
+ "-b", "--backend",
62
+ choices=["pyttsx3", "gtts", "elevenlabs"],
63
+ help="Backend to list voices for"
64
+ )
65
+
66
+ args = parser.parse_args()
67
+
68
+ # MCP server mode
69
+ if args.mcp:
70
+ from .mcp_server import main as server_main
71
+ asyncio.run(server_main())
72
+ return
73
+
74
+ if args.command == "speak":
75
+ from . import speak
76
+
77
+ speak(
78
+ text=args.text,
79
+ backend=args.backend,
80
+ voice=args.voice,
81
+ play=not args.no_play,
82
+ output_path=args.output,
83
+ fallback=not args.no_fallback,
84
+ )
85
+
86
+ elif args.command == "backends":
87
+ from . import available_backends, FALLBACK_ORDER
88
+
89
+ backends = available_backends()
90
+ print("Available TTS backends (in fallback order):")
91
+ for b in FALLBACK_ORDER:
92
+ status = "available" if b in backends else "not available"
93
+ desc = {
94
+ "pyttsx3": "System TTS (offline, free)",
95
+ "gtts": "Google TTS (free, needs internet)",
96
+ "elevenlabs": "ElevenLabs (paid, high quality)",
97
+ }
98
+ marker = "[*]" if b in backends else "[ ]"
99
+ print(f" {marker} {b}: {desc.get(b, '')} - {status}")
100
+
101
+ elif args.command == "voices":
102
+ from . import get_tts, available_backends
103
+
104
+ backend = args.backend
105
+ if not backend:
106
+ backends = available_backends()
107
+ backend = backends[0] if backends else None
108
+
109
+ if backend:
110
+ try:
111
+ tts = get_tts(backend)
112
+ voices = tts.get_voices()
113
+ print(f"Voices for {backend}:")
114
+ for v in voices:
115
+ print(f" {v.get('name', 'unknown')}: {v.get('id', '')}")
116
+ except Exception as e:
117
+ print(f"Error: {e}")
118
+ else:
119
+ print("No backends available")
120
+
121
+ else:
122
+ # Default: show help
123
+ parser.print_help()
124
+
125
+
126
+ if __name__ == "__main__":
127
+ main()
128
+
129
+ # EOF