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
@@ -0,0 +1,314 @@
1
+ /**
2
+ * Axis-Based Alignment
3
+ * Scientific plot alignment using axes bounding boxes
4
+ */
5
+
6
+ // ============================================================================
7
+ // Get Axes Bbox for Panel
8
+ // ============================================================================
9
+ function getAxesBboxForPanel(panelName) {
10
+ const cache = panelBboxesCache[panelName];
11
+ if (!cache || !cache.bboxes) return null;
12
+
13
+ const bboxes = cache.bboxes;
14
+
15
+ // Method 1: Look for ax_00_panel, ax_01_panel, etc.
16
+ for (const key of Object.keys(bboxes)) {
17
+ if (key.endsWith('_panel') && key.startsWith('ax_')) {
18
+ const bbox = bboxes[key];
19
+ if (bbox && bbox.x0 !== undefined) {
20
+ return {
21
+ x0: bbox.x0,
22
+ y0: bbox.y0,
23
+ x1: bbox.x1,
24
+ y1: bbox.y1,
25
+ key: key
26
+ };
27
+ }
28
+ }
29
+ }
30
+
31
+ // Method 2: Calculate axes bbox from spine bboxes (xaxis_spine + yaxis_spine)
32
+ // This is the common case for matplotlib figures
33
+ let xSpine = null, ySpine = null;
34
+ for (const key of Object.keys(bboxes)) {
35
+ if (key.endsWith('_xaxis_spine') && key.startsWith('ax_')) {
36
+ xSpine = bboxes[key];
37
+ }
38
+ if (key.endsWith('_yaxis_spine') && key.startsWith('ax_')) {
39
+ ySpine = bboxes[key];
40
+ }
41
+ }
42
+
43
+ if (xSpine && ySpine) {
44
+ // Combine spine bboxes to get axes area
45
+ // Y-spine defines left edge, X-spine defines bottom edge
46
+ const x0 = ySpine.x0 !== undefined ? ySpine.x0 : ySpine.x;
47
+ const y0 = ySpine.y0 !== undefined ? ySpine.y0 : ySpine.y;
48
+ const x1 = xSpine.x1 !== undefined ? xSpine.x1 : (xSpine.x + xSpine.width);
49
+ const y1 = xSpine.y1 !== undefined ? xSpine.y1 : (xSpine.y + xSpine.height);
50
+
51
+ return {
52
+ x0: Math.min(x0, xSpine.x0 || xSpine.x || x0),
53
+ y0: Math.min(y0, xSpine.y0 || xSpine.y || y0),
54
+ x1: Math.max(x1, ySpine.x1 || (ySpine.x + ySpine.width) || x1),
55
+ y1: Math.max(y1, ySpine.y1 || (ySpine.y + ySpine.height) || y1),
56
+ key: 'derived_from_spines'
57
+ };
58
+ }
59
+
60
+ // Method 3: Fallback to _meta.axes_bbox_px for single-axes plots
61
+ if (bboxes._meta && bboxes._meta.axes_bbox_px) {
62
+ const axBbox = bboxes._meta.axes_bbox_px;
63
+ return {
64
+ x0: axBbox.x0 || axBbox.x,
65
+ y0: axBbox.y0 || axBbox.y,
66
+ x1: axBbox.x1 || (axBbox.x + axBbox.width),
67
+ y1: axBbox.y1 || (axBbox.y + axBbox.height),
68
+ key: '_meta.axes_bbox_px'
69
+ };
70
+ }
71
+
72
+ return null;
73
+ }
74
+
75
+ // ============================================================================
76
+ // Calculate Axis Edge Offset
77
+ // ============================================================================
78
+ function getAxisEdgeOffset(panel, axesBbox, edge, imgSize) {
79
+ if (!axesBbox || !imgSize) return 0;
80
+
81
+ // Scale factor from image pixels to displayed panel pixels
82
+ const panelEl = panel;
83
+ const displayWidth = panelEl.offsetWidth;
84
+ const displayHeight = panelEl.offsetHeight;
85
+ const scaleX = displayWidth / imgSize.width;
86
+ const scaleY = displayHeight / imgSize.height;
87
+
88
+ switch(edge) {
89
+ case 'left':
90
+ // Y-axis left edge
91
+ return axesBbox.x0 * scaleX;
92
+ case 'right':
93
+ // Right edge of axes
94
+ return axesBbox.x1 * scaleX;
95
+ case 'top':
96
+ // Top edge of axes
97
+ return axesBbox.y0 * scaleY;
98
+ case 'bottom':
99
+ // X-axis bottom edge
100
+ return axesBbox.y1 * scaleY;
101
+ case 'center-h':
102
+ // Horizontal center of axes
103
+ return ((axesBbox.x0 + axesBbox.x1) / 2) * scaleX;
104
+ case 'center-v':
105
+ // Vertical center of axes
106
+ return ((axesBbox.y0 + axesBbox.y1) / 2) * scaleY;
107
+ default:
108
+ return 0;
109
+ }
110
+ }
111
+
112
+ // ============================================================================
113
+ // Align Panels by Axis
114
+ // ============================================================================
115
+ function alignPanelsByAxis(edge) {
116
+ // Use selected panels only
117
+ const panels = Array.from(document.querySelectorAll('.panel-canvas-item.selected'));
118
+ if (panels.length < 2) {
119
+ setStatus('Select at least 2 panels for axis alignment', true);
120
+ return;
121
+ }
122
+
123
+ // Collect panel info with axes bboxes
124
+ const panelInfos = [];
125
+ for (const panel of panels) {
126
+ const panelName = panel.dataset.panelName;
127
+ const cache = panelBboxesCache[panelName];
128
+ const axesBbox = getAxesBboxForPanel(panelName);
129
+
130
+ if (!axesBbox || !cache) {
131
+ console.warn(`Panel ${panelName}: no axes bbox found`);
132
+ continue;
133
+ }
134
+
135
+ const currentPos = panelPositions[panelName];
136
+ const axisOffset = getAxisEdgeOffset(panel, axesBbox, edge, cache.imgSize);
137
+
138
+ panelInfos.push({
139
+ panel,
140
+ panelName,
141
+ axesBbox,
142
+ imgSize: cache.imgSize,
143
+ currentPos,
144
+ axisOffset
145
+ });
146
+ }
147
+
148
+ if (panelInfos.length < 2) {
149
+ setStatus('Not enough panels with axes info', true);
150
+ return;
151
+ }
152
+
153
+ // Calculate target position - use the first panel's axis position as reference
154
+ const referenceInfo = panelInfos[0];
155
+ const referenceAxisPos = referenceInfo.currentPos.x + (edge.includes('h') || edge === 'left' || edge === 'right' ? referenceInfo.axisOffset : 0);
156
+ const referenceAxisPosY = referenceInfo.currentPos.y + (edge.includes('v') || edge === 'top' || edge === 'bottom' ? referenceInfo.axisOffset : 0);
157
+
158
+ if (edge === 'left' || edge === 'right' || edge === 'center-h') {
159
+ // Align horizontally (match X positions of axis edges)
160
+ // Target = first panel's axis X position in canvas coords
161
+ for (const info of panelInfos) {
162
+ const newX = referenceAxisPos - info.axisOffset;
163
+ info.currentPos.x = newX;
164
+ info.panel.style.left = newX + 'px';
165
+ panelLayoutMm[info.panelName].x_mm = newX / canvasScale;
166
+ }
167
+ } else {
168
+ // Align vertically (match Y positions of axis edges)
169
+ // Target = first panel's axis Y position in canvas coords
170
+ for (const info of panelInfos) {
171
+ const newY = referenceAxisPosY - info.axisOffset;
172
+ info.currentPos.y = newY;
173
+ info.panel.style.top = newY + 'px';
174
+ panelLayoutMm[info.panelName].y_mm = newY / canvasScale;
175
+ }
176
+ }
177
+
178
+ // Update layout data
179
+ updatePanelLayoutFromDOM();
180
+ layoutModified = true;
181
+ }
182
+
183
+ // ============================================================================
184
+ // Stack Panels Vertically
185
+ // ============================================================================
186
+ function stackPanelsVertically() {
187
+ // Use selected panels only
188
+ const panels = Array.from(document.querySelectorAll('.panel-canvas-item.selected'));
189
+ if (panels.length < 2) {
190
+ setStatus('Select at least 2 panels for stacking', true);
191
+ return;
192
+ }
193
+
194
+ // Collect panel info with axes bboxes
195
+ const panelInfos = [];
196
+ for (const panel of panels) {
197
+ const panelName = panel.dataset.panelName;
198
+ const cache = panelBboxesCache[panelName];
199
+ const axesBbox = getAxesBboxForPanel(panelName);
200
+
201
+ if (!axesBbox || !cache) continue;
202
+
203
+ const currentPos = panelPositions[panelName];
204
+ const axisOffsetLeft = getAxisEdgeOffset(panel, axesBbox, 'left', cache.imgSize);
205
+
206
+ panelInfos.push({
207
+ panel,
208
+ panelName,
209
+ axesBbox,
210
+ imgSize: cache.imgSize,
211
+ currentPos,
212
+ axisOffsetLeft,
213
+ height: panel.offsetHeight
214
+ });
215
+ }
216
+
217
+ if (panelInfos.length < 2) return;
218
+
219
+ // Sort by current vertical position
220
+ panelInfos.sort((a, b) => a.currentPos.y - b.currentPos.y);
221
+
222
+ // Use first panel as reference for Y-axis alignment
223
+ const referenceAxisX = panelInfos[0].currentPos.x + panelInfos[0].axisOffsetLeft;
224
+
225
+ // Stack panels vertically with small gap, aligned by Y-axis
226
+ const gap = 10; // pixels
227
+ let currentY = panelInfos[0].currentPos.y;
228
+
229
+ for (const info of panelInfos) {
230
+ // Align Y-axis (left edge of axes)
231
+ const newX = referenceAxisX - info.axisOffsetLeft;
232
+ info.currentPos.x = newX;
233
+ info.panel.style.left = newX + 'px';
234
+
235
+ // Stack vertically
236
+ info.currentPos.y = currentY;
237
+ info.panel.style.top = currentY + 'px';
238
+ currentY += info.height + gap;
239
+ }
240
+
241
+ // Update layout data
242
+ updatePanelLayoutFromDOM();
243
+ layoutModified = true;
244
+ }
245
+
246
+ // ============================================================================
247
+ // Axis Alignment Shortcut Handler
248
+ // ============================================================================
249
+ function handleAlignByAxisShortcut(key) {
250
+ const panels = document.querySelectorAll('.panel-canvas-item');
251
+ if (panels.length < 2) {
252
+ setStatus('Need multiple panels for axis alignment', true);
253
+ return;
254
+ }
255
+
256
+ const dirNames = {
257
+ 'l': 'Y-axis (left edge)',
258
+ 'r': 'Right edge',
259
+ 't': 'Top edge',
260
+ 'b': 'X-axis (bottom edge)',
261
+ 'c': 'Center horizontal',
262
+ 'm': 'Center vertical',
263
+ 's': 'Stacked vertically'
264
+ };
265
+
266
+ switch(key) {
267
+ case 'l': alignPanelsByAxis('left'); break; // Y-axis left
268
+ case 'r': alignPanelsByAxis('right'); break; // Right edge
269
+ case 't': alignPanelsByAxis('top'); break; // Top edge
270
+ case 'b': alignPanelsByAxis('bottom'); break; // X-axis bottom
271
+ case 'c': alignPanelsByAxis('center-h'); break; // Horizontal center
272
+ case 'm': alignPanelsByAxis('center-v'); break; // Vertical center
273
+ case 's': stackPanelsVertically(); break; // Stack with Y-axis alignment
274
+ default:
275
+ setStatus('Unknown axis key: ' + key + '. Use L/R/T/B/C/M/S', true);
276
+ return;
277
+ }
278
+ if (dirNames[key]) {
279
+ setStatus(`Aligned by axis: ${dirNames[key]}`, false);
280
+ }
281
+ }
282
+
283
+ // ============================================================================
284
+ // Panel Movement (Arrow Keys)
285
+ // ============================================================================
286
+ function moveSelectedPanel(direction, amountMm) {
287
+ const selected = document.querySelector('.panel-canvas-item.selected');
288
+ if (!selected) return;
289
+
290
+ const panelName = selected.dataset.panelName;
291
+ const pos = panelPositions[panelName];
292
+ if (!pos) return;
293
+
294
+ switch(direction) {
295
+ case 'left': pos.x -= amountMm * canvasScale; break;
296
+ case 'right': pos.x += amountMm * canvasScale; break;
297
+ case 'up': pos.y -= amountMm * canvasScale; break;
298
+ case 'down': pos.y += amountMm * canvasScale; break;
299
+ }
300
+
301
+ // Update position in pixels (canvasScale = px/mm)
302
+ selected.style.left = pos.x + 'px';
303
+ selected.style.top = pos.y + 'px';
304
+
305
+ // Update layout data
306
+ panelLayoutMm[panelName] = {
307
+ ...panelLayoutMm[panelName],
308
+ x_mm: pos.x / canvasScale,
309
+ y_mm: pos.y / canvasScale
310
+ };
311
+
312
+ layoutModified = true;
313
+ setStatus(`Moved ${panelName} ${direction} by ${amountMm}mm`);
314
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Basic Panel Alignment
3
+ * Bounding box-based alignment (non-scientific)
4
+ */
5
+
6
+ // ============================================================================
7
+ // Basic Alignment (by bounding box)
8
+ // ============================================================================
9
+ function alignPanels(mode) {
10
+ const selectedPanels = getSelectedPanels();
11
+ if (selectedPanels.length < 2) return;
12
+
13
+ // Get bounds
14
+ let targetValue;
15
+ switch(mode) {
16
+ case 'left':
17
+ targetValue = Math.min(...selectedPanels.map(p => p.pos.x));
18
+ selectedPanels.forEach(p => {
19
+ p.pos.x = targetValue;
20
+ p.item.style.left = targetValue + 'px';
21
+ });
22
+ break;
23
+ case 'right':
24
+ targetValue = Math.max(...selectedPanels.map(p => p.pos.x + p.pos.width));
25
+ selectedPanels.forEach(p => {
26
+ p.pos.x = targetValue - p.pos.width;
27
+ p.item.style.left = p.pos.x + 'px';
28
+ });
29
+ break;
30
+ case 'top':
31
+ targetValue = Math.min(...selectedPanels.map(p => p.pos.y));
32
+ selectedPanels.forEach(p => {
33
+ p.pos.y = targetValue;
34
+ p.item.style.top = targetValue + 'px';
35
+ });
36
+ break;
37
+ case 'bottom':
38
+ targetValue = Math.max(...selectedPanels.map(p => p.pos.y + p.pos.height));
39
+ selectedPanels.forEach(p => {
40
+ p.pos.y = targetValue - p.pos.height;
41
+ p.item.style.top = p.pos.y + 'px';
42
+ });
43
+ break;
44
+ case 'center-h':
45
+ const avgX = selectedPanels.reduce((sum, p) => sum + p.pos.x + p.pos.width/2, 0) / selectedPanels.length;
46
+ selectedPanels.forEach(p => {
47
+ p.pos.x = avgX - p.pos.width/2;
48
+ p.item.style.left = p.pos.x + 'px';
49
+ });
50
+ break;
51
+ case 'center-v':
52
+ const avgY = selectedPanels.reduce((sum, p) => sum + p.pos.y + p.pos.height/2, 0) / selectedPanels.length;
53
+ selectedPanels.forEach(p => {
54
+ p.pos.y = avgY - p.pos.height/2;
55
+ p.item.style.top = p.pos.y + 'px';
56
+ });
57
+ break;
58
+ }
59
+
60
+ // Update layout data
61
+ updatePanelLayoutFromDOM();
62
+ layoutModified = true;
63
+ setStatus(`Aligned panels: ${mode}`);
64
+ }
65
+
66
+ // ============================================================================
67
+ // Alignment Shortcut Handler
68
+ // ============================================================================
69
+ function handleAlignShortcut(key, isShift) {
70
+ const panels = document.querySelectorAll('.panel-canvas-item');
71
+ if (panels.length < 2) {
72
+ setStatus('Need multiple panels for alignment', true);
73
+ return;
74
+ }
75
+
76
+ switch(key) {
77
+ case 'l': alignPanels('left'); break;
78
+ case 'r': alignPanels('right'); break;
79
+ case 't': alignPanels('top'); break;
80
+ case 'b': alignPanels('bottom'); break;
81
+ case 'c': alignPanels('center-h'); break;
82
+ case 'm': alignPanels('center-v'); break;
83
+ case 'h': distributePanels('horizontal'); break;
84
+ case 'v': distributePanels('vertical'); break;
85
+ default:
86
+ setStatus('Unknown alignment key: ' + key, true);
87
+ }
88
+ }
89
+
90
+ // ============================================================================
91
+ // Z-Order Management
92
+ // ============================================================================
93
+ function bringPanelToFront() {
94
+ const selected = document.querySelector('.panel-canvas-item.selected');
95
+ if (selected) {
96
+ selected.style.zIndex = (parseInt(selected.style.zIndex || 0) + 1).toString();
97
+ setStatus('Brought panel to front');
98
+ }
99
+ }
100
+
101
+ function sendPanelToBack() {
102
+ const selected = document.querySelector('.panel-canvas-item.selected');
103
+ if (selected) {
104
+ selected.style.zIndex = (parseInt(selected.style.zIndex || 0) - 1).toString();
105
+ setStatus('Sent panel to back');
106
+ }
107
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Panel Distribution
3
+ * Evenly distribute panels horizontally or vertically
4
+ */
5
+
6
+ // ============================================================================
7
+ // Distribute Panels
8
+ // ============================================================================
9
+ function distributePanels(direction) {
10
+ const selectedPanels = getSelectedPanels();
11
+ if (selectedPanels.length < 3) {
12
+ setStatus('Need at least 3 panels for distribution', true);
13
+ return;
14
+ }
15
+
16
+ if (direction === 'horizontal') {
17
+ // Sort by X position
18
+ selectedPanels.sort((a, b) => a.pos.x - b.pos.x);
19
+
20
+ const first = selectedPanels[0];
21
+ const last = selectedPanels[selectedPanels.length - 1];
22
+ const totalSpace = (last.pos.x + last.pos.width) - first.pos.x;
23
+ const totalPanelWidth = selectedPanels.reduce((sum, p) => sum + p.pos.width, 0);
24
+ const gap = (totalSpace - totalPanelWidth) / (selectedPanels.length - 1);
25
+
26
+ let currentX = first.pos.x;
27
+ selectedPanels.forEach(p => {
28
+ p.pos.x = currentX;
29
+ p.item.style.left = currentX + 'px';
30
+ currentX += p.pos.width + gap;
31
+ });
32
+ } else {
33
+ // Sort by Y position
34
+ selectedPanels.sort((a, b) => a.pos.y - b.pos.y);
35
+
36
+ const first = selectedPanels[0];
37
+ const last = selectedPanels[selectedPanels.length - 1];
38
+ const totalSpace = (last.pos.y + last.pos.height) - first.pos.y;
39
+ const totalPanelHeight = selectedPanels.reduce((sum, p) => sum + p.pos.height, 0);
40
+ const gap = (totalSpace - totalPanelHeight) / (selectedPanels.length - 1);
41
+
42
+ let currentY = first.pos.y;
43
+ selectedPanels.forEach(p => {
44
+ p.pos.y = currentY;
45
+ p.item.style.top = currentY + 'px';
46
+ currentY += p.pos.height + gap;
47
+ });
48
+ }
49
+
50
+ // Update layout data
51
+ updatePanelLayoutFromDOM();
52
+ layoutModified = true;
53
+ setStatus(`Distributed panels ${direction}ly`);
54
+ }
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Canvas View Management
3
+ * Handles the unified canvas view for multi-panel figures
4
+ */
5
+
6
+ // ============================================================================
7
+ // Canvas Mode Control
8
+ // ============================================================================
9
+ function setCanvasMode(mode) {
10
+ canvasMode = mode;
11
+ document.getElementById('canvas-grid').classList.toggle('canvas-mode', mode === 'canvas');
12
+ document.getElementById('canvas-grid').classList.toggle('grid-mode', mode === 'grid');
13
+ }
14
+
15
+ // ============================================================================
16
+ // Canvas Rendering
17
+ // ============================================================================
18
+ function renderCanvasView() {
19
+ const container = document.getElementById('canvas-grid');
20
+ container.innerHTML = '';
21
+
22
+ // Fetch panels if not cached
23
+ if (!panelData || !panelData.panels) {
24
+ return;
25
+ }
26
+
27
+ if (canvasMode === 'canvas') {
28
+ // Calculate canvas size based on number of panels
29
+ const panels = panelData.panels;
30
+
31
+ panels.forEach((panel, idx) => {
32
+ const item = document.createElement('div');
33
+ item.className = 'panel-canvas-item';
34
+ item.dataset.panelName = panel.name;
35
+
36
+ // Initialize position if not set
37
+ if (!panelPositions[panel.name]) {
38
+ panelPositions[panel.name] = {
39
+ x: idx * 150,
40
+ y: idx * 150,
41
+ width: panel.width_px || 400,
42
+ height: panel.height_px || 300
43
+ };
44
+ }
45
+
46
+ const pos = panelPositions[panel.name];
47
+ item.style.left = pos.x + 'px';
48
+ item.style.top = pos.y + 'px';
49
+ item.style.width = pos.width + 'px';
50
+ item.style.height = pos.height + 'px';
51
+
52
+ item.innerHTML = `
53
+ <div class="panel-drag-handle">☰</div>
54
+ <div class="panel-label">${panel.name}</div>
55
+ <img src="data:image/png;base64,${panel.image_base64}" style="width: 100%; height: 100%; object-fit: contain;">
56
+ <canvas class="panel-overlay"></canvas>
57
+ `;
58
+
59
+ container.appendChild(item);
60
+
61
+ // Double-click to edit
62
+ item.addEventListener('dblclick', () => {
63
+ loadPanelForEditing(panel.name);
64
+ });
65
+
66
+ // Drag start
67
+ initPanelDrag(item, panel.name);
68
+ });
69
+
70
+ // Update canvas height to fit all panels
71
+ updateCanvasSize();
72
+ } else {
73
+ // Grid mode - use CSS grid layout (simpler)
74
+ loadPanelGrid();
75
+ }
76
+ }
77
+
78
+ // ============================================================================
79
+ // Interactive Element Detection Helper
80
+ // ============================================================================
81
+ function isInteractiveElement(target) {
82
+ // SVG paths with hover-path class are interactive elements
83
+ if (target.classList && target.classList.contains('hover-path')) {
84
+ return true;
85
+ }
86
+ // Check parent elements for hover-path (click might be on child)
87
+ let parent = target.parentElement;
88
+ while (parent) {
89
+ if (parent.tagName === 'path' || parent.classList.contains('hover-path')) {
90
+ // Path elements in SVG overlay are interactive
91
+ return true;
92
+ }
93
+ parent = parent.parentElement;
94
+ }
95
+ return false;
96
+ }
97
+
98
+ // ============================================================================
99
+ // Canvas Size Management
100
+ // ============================================================================
101
+ function updateCanvasSize() {
102
+ // Find the maximum extent of all panels
103
+ let maxX = 0;
104
+ let maxY = 0;
105
+
106
+ Object.values(panelPositions).forEach(pos => {
107
+ maxX = Math.max(maxX, pos.x + pos.width);
108
+ maxY = Math.max(maxY, pos.y + pos.height);
109
+ });
110
+
111
+ // Add some padding
112
+ const container = document.getElementById('canvas-grid');
113
+ if (container && canvasMode === 'canvas') {
114
+ container.style.minHeight = (maxY + 100) + 'px';
115
+ container.style.minWidth = (maxX + 100) + 'px';
116
+ }
117
+ }
118
+
119
+ // ============================================================================
120
+ // Canvas Zoom Functions
121
+ // ============================================================================
122
+ function zoomCanvas(factor) {
123
+ canvasZoom = Math.max(0.1, Math.min(5.0, canvasZoom * factor));
124
+ const container = document.getElementById('canvas-grid');
125
+ if (container) {
126
+ container.style.transform = `scale(${canvasZoom})`;
127
+ container.style.transformOrigin = 'top left';
128
+ }
129
+ }
130
+
131
+ function fitCanvasToWindow() {
132
+ const container = document.getElementById('canvas-grid');
133
+ if (!container) return;
134
+
135
+ const containerWidth = container.scrollWidth;
136
+ const windowWidth = window.innerWidth - 400; // Account for side panels
137
+ canvasZoom = Math.min(1.0, windowWidth / containerWidth);
138
+ container.style.transform = `scale(${canvasZoom})`;
139
+ container.style.transformOrigin = 'top left';
140
+ }
141
+
142
+ function resizeCanvas(factor) {
143
+ const container = document.getElementById('canvas-grid');
144
+ if (!container) return;
145
+
146
+ // Scale all panel positions and sizes
147
+ Object.keys(panelPositions).forEach(name => {
148
+ const pos = panelPositions[name];
149
+ pos.x *= factor;
150
+ pos.y *= factor;
151
+ pos.width *= factor;
152
+ pos.height *= factor;
153
+ });
154
+
155
+ renderCanvasView();
156
+ }
157
+
158
+ // ============================================================================
159
+ // Panel Layout Update from DOM
160
+ // ============================================================================
161
+ function updatePanelLayoutFromDOM() {
162
+ document.querySelectorAll('.panel-canvas-item').forEach(item => {
163
+ const name = item.dataset.panelName;
164
+ const rect = item.getBoundingClientRect();
165
+ panelPositions[name] = {
166
+ x: parseFloat(item.style.left),
167
+ y: parseFloat(item.style.top),
168
+ width: rect.width,
169
+ height: rect.height
170
+ };
171
+ });
172
+ }