scitex 2.10.3__py3-none-any.whl → 2.11.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 (363) hide show
  1. scitex/__init__.py +1 -4
  2. scitex/__version__.py +1 -1
  3. scitex/_install_guide.py +14 -2
  4. scitex/bridge/_figrecipe.py +1 -1
  5. scitex/bridge/_helpers.py +1 -1
  6. scitex/bridge/_plt_vis.py +1 -1
  7. scitex/bridge/_stats_plt.py +1 -1
  8. scitex/bridge/_stats_vis.py +2 -2
  9. scitex/{fig → canvas}/__init__.py +84 -96
  10. scitex/{fig → canvas}/backend/_parser.py +1 -1
  11. scitex/{fig → canvas}/canvas.py +13 -14
  12. scitex/{fts/_fig/_editor → canvas/editor}/_defaults.py +2 -2
  13. scitex/{fig → canvas}/editor/edit/__init__.py +11 -14
  14. scitex/{fig → canvas}/editor/edit/bundle_resolver.py +56 -48
  15. scitex/{fig → canvas}/editor/edit/editor_launcher.py +79 -26
  16. scitex/{fts/_fig/_editor/_cui/_panel_loader.py → canvas/editor/edit/panel_loader.py} +8 -8
  17. scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/_bbox.py +2 -1
  18. scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/_core.py +84 -84
  19. scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/_renderer.py +7 -6
  20. scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/static/css/features/canvas.css +2 -2
  21. scitex/{fig → canvas}/editor/flask_editor/static/css/features/panel-grid.css +1 -1
  22. scitex/{fig → canvas}/editor/flask_editor/static/js/core/api.js +3 -4
  23. scitex/{fig → canvas}/editor/flask_editor/static/js/editor/preview.js +5 -5
  24. scitex/{fig → canvas}/editor/flask_editor/templates/_html.py +3 -3
  25. scitex/{fig → canvas}/editor/flask_editor/templates/_scripts.py +10 -10
  26. scitex/{fig → canvas}/editor/flask_editor/templates/_styles.py +3 -3
  27. scitex/{fig → canvas}/io/__init__.py +32 -38
  28. scitex/{fig → canvas}/io/_bundle.py +217 -154
  29. scitex/{fig → canvas}/io/_canvas.py +1 -1
  30. scitex/{fig → canvas}/io/_data.py +1 -1
  31. scitex/{fig → canvas}/io/_export.py +1 -1
  32. scitex/{fig → canvas}/io/_load.py +1 -1
  33. scitex/{fig → canvas}/io/_panel.py +1 -1
  34. scitex/{fig → canvas}/io/_save.py +1 -1
  35. scitex/{fig → canvas}/model/__init__.py +1 -1
  36. scitex/{fig → canvas}/model/_annotations.py +1 -1
  37. scitex/{fig → canvas}/model/_axes.py +1 -1
  38. scitex/{fig → canvas}/model/_figure.py +1 -1
  39. scitex/{fig → canvas}/model/_guides.py +1 -1
  40. scitex/{fig → canvas}/model/_plot.py +1 -1
  41. scitex/{fig → canvas}/model/_styles.py +1 -1
  42. scitex/{fig → canvas}/utils/__init__.py +1 -1
  43. scitex/cli/convert.py +10 -6
  44. scitex/diagram/README.md +7 -7
  45. scitex/io/__init__.py +7 -19
  46. scitex/io/_load.py +15 -19
  47. scitex/io/_load_modules/_canvas.py +2 -2
  48. scitex/io/_load_modules/_con.py +5 -5
  49. scitex/io/_load_modules/_eeg.py +16 -12
  50. scitex/io/_save.py +11 -16
  51. scitex/io/_save_modules/__init__.py +6 -10
  52. scitex/io/_save_modules/_canvas.py +3 -3
  53. scitex/io/_save_modules/_plot_bundle.py +112 -0
  54. scitex/io/_save_modules/{_pltz_stx.py → _plot_scitex.py} +7 -7
  55. scitex/io/_save_modules/_stx_bundle.py +16 -16
  56. scitex/io/bundle/README.md +89 -80
  57. scitex/{fts/_bundle/_FTS.py → io/bundle/_Bundle.py} +197 -95
  58. scitex/io/bundle/__init__.py +67 -35
  59. scitex/{fts/_bundle → io/bundle}/_children.py +32 -40
  60. scitex/io/bundle/_core.py +184 -97
  61. scitex/{fts/_bundle/_dataclasses/_Node.py → io/bundle/_dataclasses/_Spec.py} +29 -23
  62. scitex/{fts/_bundle/_dataclasses/_NodeRefs.py → io/bundle/_dataclasses/_SpecRefs.py} +6 -6
  63. scitex/{fts/_bundle → io/bundle}/_dataclasses/__init__.py +4 -4
  64. scitex/{fts/_bundle → io/bundle}/_loader.py +19 -19
  65. scitex/io/bundle/_manifest.py +99 -0
  66. scitex/{fts/_bundle → io/bundle}/_mpl_helpers.py +119 -28
  67. scitex/io/bundle/_nested.py +113 -100
  68. scitex/{fts/_bundle → io/bundle}/_saver.py +13 -14
  69. scitex/{fts/_bundle → io/bundle}/_storage.py +3 -3
  70. scitex/io/bundle/_types.py +41 -16
  71. scitex/{fts/_bundle → io/bundle}/_validation.py +20 -18
  72. scitex/io/bundle/_zip.py +21 -31
  73. scitex/{fts/_kinds → io/bundle/kinds}/_plot/_backend/_parser.py +1 -1
  74. scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Annotations.py +1 -1
  75. scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Axes.py +1 -1
  76. scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Figure.py +1 -1
  77. scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_Guides.py +1 -1
  78. scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Plot.py +1 -1
  79. scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_Styles.py +1 -1
  80. scitex/{fts/_kinds → io/bundle/kinds}/_plot/_utils/_plot_layout.py +1 -1
  81. scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/__init__.py +1 -1
  82. scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_editor/_app.py +1 -1
  83. scitex/{fts/_tables → io/bundle/kinds/_table}/_latex/_export.py +1 -1
  84. scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_figure_exporter.py +1 -1
  85. scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_table_exporter.py +1 -1
  86. scitex/io/bundle/schemas/__init__.py +30 -0
  87. scitex/parallel/_run.py +5 -4
  88. scitex/path/_find.py +60 -83
  89. scitex/path/_get_module_path.py +23 -21
  90. scitex/path/_get_spath.py +6 -27
  91. scitex/path/_getsize.py +23 -9
  92. scitex/path/_increment_version.py +31 -38
  93. scitex/path/_mk_spath.py +26 -29
  94. scitex/path/_path.py +5 -12
  95. scitex/path/_split.py +27 -15
  96. scitex/path/_this_path.py +23 -9
  97. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +2 -1
  98. scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +2 -2
  99. scitex/plt/gallery/_generate.py +76 -50
  100. scitex/plt/io/__init__.py +17 -19
  101. scitex/plt/io/_bundle.py +99 -52
  102. scitex/plt/io/_layered_bundle.py +303 -168
  103. scitex/plt/utils/_csv_column_naming.py +250 -118
  104. scitex/schema/__init__.py +69 -73
  105. scitex/schema/_canvas.py +1 -1
  106. scitex/schema/_stats.py +2 -2
  107. scitex/stats/__init__.py +30 -33
  108. scitex/stats/_schema.py +1 -1
  109. scitex/stats/io/__init__.py +10 -11
  110. scitex/stats/io/_bundle.py +16 -16
  111. {scitex-2.10.3.dist-info → scitex-2.11.0.dist-info}/METADATA +190 -73
  112. {scitex-2.10.3.dist-info → scitex-2.11.0.dist-info}/RECORD +237 -360
  113. scitex/fig/editor/_defaults.py +0 -300
  114. scitex/fig/editor/edit/panel_loader.py +0 -232
  115. scitex/fig/editor/flask_editor/_bbox.py +0 -1299
  116. scitex/fig/editor/flask_editor/_core.py +0 -1429
  117. scitex/fig/editor/flask_editor/_renderer.py +0 -813
  118. scitex/fig/editor/flask_editor/static/css/features/canvas.css +0 -176
  119. scitex/fts/README.md +0 -262
  120. scitex/fts/TODO.md +0 -66
  121. scitex/fts/__init__.py +0 -90
  122. scitex/fts/_bundle/README_IN_BUNDLE.md +0 -102
  123. scitex/fts/_bundle/__init__.py +0 -38
  124. scitex/fts/_bundle/_utils/__init__.py +0 -55
  125. scitex/fts/_bundle/_utils/_const.py +0 -26
  126. scitex/fts/_bundle/_utils/_errors.py +0 -73
  127. scitex/fts/_bundle/_utils/_generate.py +0 -21
  128. scitex/fts/_bundle/_utils/_types.py +0 -76
  129. scitex/fts/_bundle/_zipbundle.py +0 -165
  130. scitex/fts/_fig/__init__.py +0 -22
  131. scitex/fts/_fig/_backend/_parser.py +0 -188
  132. scitex/fts/_fig/_editor/__init__.py +0 -14
  133. scitex/fts/_fig/_editor/_cui/__init__.py +0 -33
  134. scitex/fts/_fig/_editor/_cui/_backend_detector.py +0 -39
  135. scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +0 -366
  136. scitex/fts/_fig/_editor/_cui/_editor_launcher.py +0 -175
  137. scitex/fts/_fig/_editor/_cui/_manual_handler.py +0 -52
  138. scitex/fts/_fig/_editor/_cui/_path_resolver.py +0 -66
  139. scitex/fts/_fig/_editor/_gui/__init__.py +0 -11
  140. scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +0 -20
  141. scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +0 -664
  142. scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +0 -79
  143. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +0 -41
  144. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +0 -16
  145. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +0 -85
  146. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +0 -217
  147. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +0 -93
  148. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +0 -57
  149. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +0 -112
  150. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +0 -59
  151. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +0 -212
  152. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +0 -190
  153. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +0 -59
  154. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +0 -45
  155. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +0 -95
  156. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +0 -101
  157. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +0 -138
  158. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +0 -31
  159. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +0 -7
  160. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +0 -56
  161. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +0 -78
  162. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +0 -314
  163. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +0 -107
  164. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +0 -54
  165. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +0 -172
  166. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +0 -258
  167. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +0 -48
  168. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +0 -71
  169. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +0 -288
  170. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +0 -143
  171. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +0 -245
  172. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +0 -992
  173. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +0 -339
  174. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +0 -286
  175. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +0 -371
  176. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +0 -293
  177. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +0 -426
  178. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +0 -152
  179. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +0 -265
  180. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +0 -184
  181. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +0 -57
  182. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +0 -100
  183. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +0 -34
  184. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +0 -124
  185. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +0 -851
  186. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +0 -4932
  187. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +0 -1657
  188. scitex/fts/_fig/_editor/_gui/_flask_editor.py +0 -36
  189. scitex/fts/_fig/_models/_Annotations.py +0 -115
  190. scitex/fts/_fig/_models/_Axes.py +0 -152
  191. scitex/fts/_fig/_models/_Figure.py +0 -138
  192. scitex/fts/_fig/_models/_Plot.py +0 -123
  193. scitex/fts/_fig/_utils/_plot_layout.py +0 -397
  194. scitex/fts/_kinds/_figure/_composite.py +0 -345
  195. scitex/fts/_kinds/_plot/_backend/__init__.py +0 -53
  196. scitex/fts/_kinds/_plot/_backend/_export.py +0 -165
  197. scitex/fts/_kinds/_plot/_backend/_render.py +0 -538
  198. scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +0 -46
  199. scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +0 -82
  200. scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +0 -441
  201. scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +0 -52
  202. scitex/fts/_kinds/_plot/_dataclasses/__init__.py +0 -47
  203. scitex/fts/_kinds/_plot/_models/_Guides.py +0 -104
  204. scitex/fts/_kinds/_plot/_models/_Styles.py +0 -245
  205. scitex/fts/_kinds/_plot/_models/__init__.py +0 -80
  206. scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +0 -156
  207. scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +0 -43
  208. scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +0 -38
  209. scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +0 -36
  210. scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +0 -60
  211. scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +0 -30
  212. scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +0 -61
  213. scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +0 -57
  214. scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +0 -30
  215. scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +0 -121
  216. scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +0 -36
  217. scitex/fts/_kinds/_plot/_utils/__init__.py +0 -129
  218. scitex/fts/_kinds/_plot/_utils/_auto_layout.py +0 -127
  219. scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +0 -111
  220. scitex/fts/_kinds/_plot/_utils/_const_sizes.py +0 -48
  221. scitex/fts/_kinds/_plot/_utils/_convert_coords.py +0 -77
  222. scitex/fts/_kinds/_plot/_utils/_get_template.py +0 -178
  223. scitex/fts/_kinds/_plot/_utils/_normalize.py +0 -73
  224. scitex/fts/_kinds/_plot/_utils/_validate.py +0 -197
  225. scitex/fts/_kinds/_table/_latex/_export.py +0 -279
  226. scitex/fts/_stats/__init__.py +0 -48
  227. scitex/fts/_stats/_dataclasses/_Stats.py +0 -423
  228. scitex/fts/_stats/_dataclasses/__init__.py +0 -48
  229. scitex/fts/_tables/__init__.py +0 -65
  230. scitex/fts/_tables/_latex/__init__.py +0 -93
  231. scitex/fts/_tables/_latex/_editor/__init__.py +0 -11
  232. scitex/fts/_tables/_latex/_editor/_app.py +0 -725
  233. scitex/fts/_tables/_latex/_figure_exporter.py +0 -153
  234. scitex/fts/_tables/_latex/_stats_formatter.py +0 -274
  235. scitex/fts/_tables/_latex/_table_exporter.py +0 -362
  236. scitex/fts/_tables/_latex/_utils.py +0 -369
  237. scitex/fts/_tables/_latex/_validator.py +0 -445
  238. scitex/io/_save_modules/_pltz_bundle.py +0 -356
  239. /scitex/{fig → canvas}/README.md +0 -0
  240. /scitex/{fig → canvas}/backend/__init__.py +0 -0
  241. /scitex/{fig → canvas}/backend/_export.py +0 -0
  242. /scitex/{fig → canvas}/backend/_render.py +0 -0
  243. /scitex/{fig → canvas}/docs/CANVAS_ARCHITECTURE.md +0 -0
  244. /scitex/{fig → canvas}/editor/__init__.py +0 -0
  245. /scitex/{fig → canvas}/editor/_dearpygui_editor.py +0 -0
  246. /scitex/{fig → canvas}/editor/_flask_editor.py +0 -0
  247. /scitex/{fig → canvas}/editor/_mpl_editor.py +0 -0
  248. /scitex/{fig → canvas}/editor/_qt_editor.py +0 -0
  249. /scitex/{fig → canvas}/editor/_tkinter_editor.py +0 -0
  250. /scitex/{fig → canvas}/editor/edit/backend_detector.py +0 -0
  251. /scitex/{fig → canvas}/editor/edit/manual_handler.py +0 -0
  252. /scitex/{fig → canvas}/editor/edit/path_resolver.py +0 -0
  253. /scitex/{fig → canvas}/editor/flask_editor/__init__.py +0 -0
  254. /scitex/{fig → canvas}/editor/flask_editor/_plotter.py +0 -0
  255. /scitex/{fig → canvas}/editor/flask_editor/_utils.py +0 -0
  256. /scitex/{fig → canvas}/editor/flask_editor/static/css/base/reset.css +0 -0
  257. /scitex/{fig → canvas}/editor/flask_editor/static/css/base/typography.css +0 -0
  258. /scitex/{fig → canvas}/editor/flask_editor/static/css/base/variables.css +0 -0
  259. /scitex/{fig → canvas}/editor/flask_editor/static/css/components/buttons.css +0 -0
  260. /scitex/{fig → canvas}/editor/flask_editor/static/css/components/context-menu.css +0 -0
  261. /scitex/{fig → canvas}/editor/flask_editor/static/css/components/dropdown.css +0 -0
  262. /scitex/{fig → canvas}/editor/flask_editor/static/css/components/forms.css +0 -0
  263. /scitex/{fig → canvas}/editor/flask_editor/static/css/components/modal.css +0 -0
  264. /scitex/{fig → canvas}/editor/flask_editor/static/css/components/sections.css +0 -0
  265. /scitex/{fig → canvas}/editor/flask_editor/static/css/features/element-inspector.css +0 -0
  266. /scitex/{fig → canvas}/editor/flask_editor/static/css/features/loading.css +0 -0
  267. /scitex/{fig → canvas}/editor/flask_editor/static/css/features/overlay.css +0 -0
  268. /scitex/{fig → canvas}/editor/flask_editor/static/css/features/selection.css +0 -0
  269. /scitex/{fig → canvas}/editor/flask_editor/static/css/features/statistics.css +0 -0
  270. /scitex/{fig → canvas}/editor/flask_editor/static/css/index.css +0 -0
  271. /scitex/{fig → canvas}/editor/flask_editor/static/css/layout/container.css +0 -0
  272. /scitex/{fig → canvas}/editor/flask_editor/static/css/layout/controls.css +0 -0
  273. /scitex/{fig → canvas}/editor/flask_editor/static/css/layout/preview.css +0 -0
  274. /scitex/{fig → canvas}/editor/flask_editor/static/js/alignment/axis.js +0 -0
  275. /scitex/{fig → canvas}/editor/flask_editor/static/js/alignment/basic.js +0 -0
  276. /scitex/{fig → canvas}/editor/flask_editor/static/js/alignment/distribute.js +0 -0
  277. /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/canvas.js +0 -0
  278. /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/dragging.js +0 -0
  279. /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/resize.js +0 -0
  280. /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/selection.js +0 -0
  281. /scitex/{fig → canvas}/editor/flask_editor/static/js/core/state.js +0 -0
  282. /scitex/{fig → canvas}/editor/flask_editor/static/js/core/utils.js +0 -0
  283. /scitex/{fig → canvas}/editor/flask_editor/static/js/dev/element-inspector.js +0 -0
  284. /scitex/{fig → canvas}/editor/flask_editor/static/js/editor/bbox.js +0 -0
  285. /scitex/{fig → canvas}/editor/flask_editor/static/js/editor/element-drag.js +0 -0
  286. /scitex/{fig → canvas}/editor/flask_editor/static/js/editor/overlay.js +0 -0
  287. /scitex/{fig → canvas}/editor/flask_editor/static/js/main.js +0 -0
  288. /scitex/{fig → canvas}/editor/flask_editor/static/js/shortcuts/context-menu.js +0 -0
  289. /scitex/{fig → canvas}/editor/flask_editor/static/js/shortcuts/keyboard.js +0 -0
  290. /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/controls.js +0 -0
  291. /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/download.js +0 -0
  292. /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/help.js +0 -0
  293. /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/theme.js +0 -0
  294. /scitex/{fig → canvas}/editor/flask_editor/templates/__init__.py +0 -0
  295. /scitex/{fig → canvas}/io/_directory.py +0 -0
  296. /scitex/{fig → canvas}/model/_plot_types.py +0 -0
  297. /scitex/{fig → canvas}/utils/_defaults.py +0 -0
  298. /scitex/{fig → canvas}/utils/_validate.py +0 -0
  299. /scitex/{fts/_bundle → io/bundle}/_conversion/__init__.py +0 -0
  300. /scitex/{fts/_bundle → io/bundle}/_conversion/_bundle2dict.py +0 -0
  301. /scitex/{fts/_bundle → io/bundle}/_conversion/_dict2bundle.py +0 -0
  302. /scitex/{fts/_bundle → io/bundle}/_dataclasses/_Axes.py +0 -0
  303. /scitex/{fts/_bundle → io/bundle}/_dataclasses/_BBox.py +0 -0
  304. /scitex/{fts/_bundle → io/bundle}/_dataclasses/_ColumnDef.py +0 -0
  305. /scitex/{fts/_bundle → io/bundle}/_dataclasses/_DataFormat.py +0 -0
  306. /scitex/{fts/_bundle → io/bundle}/_dataclasses/_DataInfo.py +0 -0
  307. /scitex/{fts/_bundle → io/bundle}/_dataclasses/_DataSource.py +0 -0
  308. /scitex/{fts/_bundle → io/bundle}/_dataclasses/_SizeMM.py +0 -0
  309. /scitex/{fts/_bundle → io/bundle}/_extractors/__init__.py +0 -0
  310. /scitex/{fts/_bundle → io/bundle}/_extractors/_extract_bar.py +0 -0
  311. /scitex/{fts/_bundle → io/bundle}/_extractors/_extract_line.py +0 -0
  312. /scitex/{fts/_bundle → io/bundle}/_extractors/_extract_scatter.py +0 -0
  313. /scitex/{fts/_kinds → io/bundle/kinds}/__init__.py +0 -0
  314. /scitex/{fts/_kinds → io/bundle/kinds}/_figure/__init__.py +0 -0
  315. /scitex/{fts/_fig → io/bundle/kinds/_figure}/_composite.py +0 -0
  316. /scitex/{fts/_kinds → io/bundle/kinds}/_plot/__init__.py +0 -0
  317. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_backend/__init__.py +0 -0
  318. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_backend/_export.py +0 -0
  319. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_backend/_render.py +0 -0
  320. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_ChannelEncoding.py +0 -0
  321. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_Encoding.py +0 -0
  322. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_Theme.py +0 -0
  323. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_TraceEncoding.py +0 -0
  324. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/__init__.py +0 -0
  325. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/__init__.py +0 -0
  326. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/__init__.py +0 -0
  327. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_bar.py +0 -0
  328. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_box.py +0 -0
  329. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_distribution.py +0 -0
  330. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_errorbar.py +0 -0
  331. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_histogram.py +0 -0
  332. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_image.py +0 -0
  333. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_line.py +0 -0
  334. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_scatter.py +0 -0
  335. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_seaborn.py +0 -0
  336. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_violin.py +0 -0
  337. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/__init__.py +0 -0
  338. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_auto_layout.py +0 -0
  339. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_calc_bounds.py +0 -0
  340. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_const_sizes.py +0 -0
  341. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_convert_coords.py +0 -0
  342. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_get_template.py +0 -0
  343. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_normalize.py +0 -0
  344. /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_validate.py +0 -0
  345. /scitex/{fts/_kinds → io/bundle/kinds}/_shape/__init__.py +0 -0
  346. /scitex/{fts/_kinds → io/bundle/kinds}/_stats/__init__.py +0 -0
  347. /scitex/{fts/_kinds → io/bundle/kinds}/_stats/_dataclasses/_Stats.py +0 -0
  348. /scitex/{fts/_kinds → io/bundle/kinds}/_stats/_dataclasses/__init__.py +0 -0
  349. /scitex/{fts/_kinds → io/bundle/kinds}/_table/__init__.py +0 -0
  350. /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_editor/__init__.py +0 -0
  351. /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_stats_formatter.py +0 -0
  352. /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_utils.py +0 -0
  353. /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_validator.py +0 -0
  354. /scitex/{fts/_kinds → io/bundle/kinds}/_text/__init__.py +0 -0
  355. /scitex/{fts/_schemas → io/bundle/schemas}/data_info.schema.json +0 -0
  356. /scitex/{fts/_schemas → io/bundle/schemas}/encoding.schema.json +0 -0
  357. /scitex/{fts/_schemas → io/bundle/schemas}/node.schema.json +0 -0
  358. /scitex/{fts/_schemas → io/bundle/schemas}/render_manifest.schema.json +0 -0
  359. /scitex/{fts/_schemas → io/bundle/schemas}/stats.schema.json +0 -0
  360. /scitex/{fts/_schemas → io/bundle/schemas}/theme.schema.json +0 -0
  361. {scitex-2.10.3.dist-info → scitex-2.11.0.dist-info}/WHEEL +0 -0
  362. {scitex-2.10.3.dist-info → scitex-2.11.0.dist-info}/entry_points.txt +0 -0
  363. {scitex-2.10.3.dist-info → scitex-2.11.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,725 +0,0 @@
1
- #!/usr/bin/env python3
2
- # Timestamp: 2025-12-20
3
- # File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_tables/_latex/_editor/_app.py
4
-
5
- """Flask application for LaTeX error editor."""
6
-
7
- import json
8
- import socket
9
- import subprocess
10
- import tempfile
11
- import webbrowser
12
- from pathlib import Path
13
- from typing import TYPE_CHECKING, Optional
14
-
15
- from flask import Flask, jsonify, render_template_string, request
16
-
17
- from .._validator import ValidationResult, validate_latex
18
-
19
- if TYPE_CHECKING:
20
- from scitex.fts import FTS
21
-
22
-
23
- # HTML template with CodeMirror editor
24
- EDITOR_TEMPLATE = """
25
- <!DOCTYPE html>
26
- <html lang="en">
27
- <head>
28
- <meta charset="UTF-8">
29
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
30
- <title>LaTeX Editor - FTS</title>
31
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.css">
32
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/theme/monokai.min.css">
33
- <style>
34
- :root {
35
- --bg-primary: #1e1e1e;
36
- --bg-secondary: #252526;
37
- --bg-tertiary: #2d2d30;
38
- --text-primary: #d4d4d4;
39
- --text-secondary: #808080;
40
- --accent: #569cd6;
41
- --error: #f44747;
42
- --warning: #ffcc00;
43
- --success: #4ec9b0;
44
- --border: #3c3c3c;
45
- }
46
-
47
- * {
48
- box-sizing: border-box;
49
- margin: 0;
50
- padding: 0;
51
- }
52
-
53
- body {
54
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
55
- background: var(--bg-primary);
56
- color: var(--text-primary);
57
- height: 100vh;
58
- display: flex;
59
- flex-direction: column;
60
- }
61
-
62
- header {
63
- background: var(--bg-secondary);
64
- padding: 12px 20px;
65
- display: flex;
66
- justify-content: space-between;
67
- align-items: center;
68
- border-bottom: 1px solid var(--border);
69
- }
70
-
71
- header h1 {
72
- font-size: 16px;
73
- font-weight: 500;
74
- }
75
-
76
- .controls {
77
- display: flex;
78
- gap: 10px;
79
- }
80
-
81
- button {
82
- background: var(--bg-tertiary);
83
- color: var(--text-primary);
84
- border: 1px solid var(--border);
85
- padding: 8px 16px;
86
- border-radius: 4px;
87
- cursor: pointer;
88
- font-size: 13px;
89
- transition: background 0.2s;
90
- }
91
-
92
- button:hover {
93
- background: var(--accent);
94
- color: #fff;
95
- }
96
-
97
- button.primary {
98
- background: var(--accent);
99
- color: #fff;
100
- border-color: var(--accent);
101
- }
102
-
103
- button.primary:hover {
104
- background: #4a8bc9;
105
- }
106
-
107
- .main-container {
108
- flex: 1;
109
- display: flex;
110
- overflow: hidden;
111
- }
112
-
113
- .editor-panel {
114
- flex: 1;
115
- display: flex;
116
- flex-direction: column;
117
- border-right: 1px solid var(--border);
118
- }
119
-
120
- .CodeMirror {
121
- flex: 1;
122
- font-family: 'Fira Code', 'Consolas', monospace;
123
- font-size: 14px;
124
- line-height: 1.6;
125
- }
126
-
127
- .error-panel {
128
- width: 350px;
129
- background: var(--bg-secondary);
130
- display: flex;
131
- flex-direction: column;
132
- }
133
-
134
- .error-panel h2 {
135
- padding: 12px 16px;
136
- font-size: 14px;
137
- font-weight: 500;
138
- background: var(--bg-tertiary);
139
- border-bottom: 1px solid var(--border);
140
- }
141
-
142
- .error-list {
143
- flex: 1;
144
- overflow-y: auto;
145
- padding: 8px;
146
- }
147
-
148
- .error-item {
149
- padding: 10px 12px;
150
- margin-bottom: 8px;
151
- border-radius: 4px;
152
- cursor: pointer;
153
- transition: background 0.2s;
154
- }
155
-
156
- .error-item:hover {
157
- background: var(--bg-tertiary);
158
- }
159
-
160
- .error-item.error {
161
- border-left: 3px solid var(--error);
162
- }
163
-
164
- .error-item.warning {
165
- border-left: 3px solid var(--warning);
166
- }
167
-
168
- .error-item.info {
169
- border-left: 3px solid var(--accent);
170
- }
171
-
172
- .error-code {
173
- display: inline-block;
174
- background: var(--bg-tertiary);
175
- padding: 2px 6px;
176
- border-radius: 3px;
177
- font-size: 11px;
178
- margin-right: 8px;
179
- }
180
-
181
- .error-location {
182
- color: var(--text-secondary);
183
- font-size: 12px;
184
- margin-top: 4px;
185
- }
186
-
187
- .error-message {
188
- font-size: 13px;
189
- margin-top: 4px;
190
- }
191
-
192
- .error-suggestion {
193
- color: var(--success);
194
- font-size: 12px;
195
- margin-top: 6px;
196
- }
197
-
198
- .no-errors {
199
- padding: 20px;
200
- text-align: center;
201
- color: var(--success);
202
- }
203
-
204
- .status-bar {
205
- background: var(--bg-secondary);
206
- padding: 6px 16px;
207
- font-size: 12px;
208
- display: flex;
209
- justify-content: space-between;
210
- border-top: 1px solid var(--border);
211
- }
212
-
213
- .status-item {
214
- display: flex;
215
- align-items: center;
216
- gap: 6px;
217
- }
218
-
219
- .status-dot {
220
- width: 8px;
221
- height: 8px;
222
- border-radius: 50%;
223
- }
224
-
225
- .status-dot.valid {
226
- background: var(--success);
227
- }
228
-
229
- .status-dot.invalid {
230
- background: var(--error);
231
- }
232
-
233
- .cm-error-line {
234
- background: rgba(244, 71, 71, 0.15) !important;
235
- }
236
-
237
- .cm-warning-line {
238
- background: rgba(255, 204, 0, 0.1) !important;
239
- }
240
-
241
- /* Loading spinner */
242
- .loading {
243
- display: none;
244
- position: fixed;
245
- top: 50%;
246
- left: 50%;
247
- transform: translate(-50%, -50%);
248
- background: var(--bg-secondary);
249
- padding: 20px 40px;
250
- border-radius: 8px;
251
- box-shadow: 0 4px 20px rgba(0,0,0,0.5);
252
- }
253
-
254
- .loading.active {
255
- display: block;
256
- }
257
- </style>
258
- </head>
259
- <body>
260
- <header>
261
- <h1>LaTeX Editor - {{ bundle_name }}</h1>
262
- <div class="controls">
263
- <button onclick="validateCode()">Validate</button>
264
- <button onclick="compilePreview()">Compile Preview</button>
265
- <button class="primary" onclick="saveCode()">Save</button>
266
- </div>
267
- </header>
268
-
269
- <div class="main-container">
270
- <div class="editor-panel">
271
- <textarea id="latex-editor">{{ latex_code }}</textarea>
272
- </div>
273
-
274
- <div class="error-panel">
275
- <h2>Issues <span id="issue-count">({{ error_count }})</span></h2>
276
- <div class="error-list" id="error-list">
277
- {% if errors %}
278
- {% for error in errors %}
279
- <div class="error-item {{ error.severity.value }}"
280
- onclick="goToLine({{ error.line }}, {{ error.column }})">
281
- <span class="error-code">{{ error.code }}</span>
282
- <span class="error-severity">{{ error.severity.value }}</span>
283
- <div class="error-location">Line {{ error.line }}, Col {{ error.column }}</div>
284
- <div class="error-message">{{ error.message }}</div>
285
- {% if error.suggestion %}
286
- <div class="error-suggestion">Fix: {{ error.suggestion }}</div>
287
- {% endif %}
288
- </div>
289
- {% endfor %}
290
- {% else %}
291
- <div class="no-errors">No issues found</div>
292
- {% endif %}
293
- </div>
294
- </div>
295
- </div>
296
-
297
- <div class="status-bar">
298
- <div class="status-item">
299
- <span class="status-dot {{ 'valid' if is_valid else 'invalid' }}"></span>
300
- <span id="status-text">{{ 'Valid' if is_valid else 'Has issues' }}</span>
301
- </div>
302
- <div class="status-item">
303
- <span id="cursor-pos">Line 1, Col 1</span>
304
- </div>
305
- </div>
306
-
307
- <div class="loading" id="loading">Processing...</div>
308
-
309
- <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.js"></script>
310
- <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/mode/stex/stex.min.js"></script>
311
- <script>
312
- let editor;
313
- let lineWidgets = [];
314
-
315
- document.addEventListener('DOMContentLoaded', function() {
316
- editor = CodeMirror.fromTextArea(document.getElementById('latex-editor'), {
317
- mode: 'stex',
318
- theme: 'monokai',
319
- lineNumbers: true,
320
- lineWrapping: true,
321
- indentUnit: 4,
322
- tabSize: 4,
323
- indentWithTabs: false,
324
- matchBrackets: true,
325
- autoCloseBrackets: true
326
- });
327
-
328
- editor.on('cursorActivity', function() {
329
- const pos = editor.getCursor();
330
- document.getElementById('cursor-pos').textContent =
331
- `Line ${pos.line + 1}, Col ${pos.ch + 1}`;
332
- });
333
-
334
- // Highlight initial errors
335
- highlightErrors({{ errors_json|safe }});
336
- });
337
-
338
- function goToLine(line, col) {
339
- editor.setCursor(line - 1, col - 1);
340
- editor.focus();
341
- }
342
-
343
- function highlightErrors(errors) {
344
- // Clear previous highlights
345
- lineWidgets.forEach(w => w.clear());
346
- lineWidgets = [];
347
-
348
- errors.forEach(error => {
349
- if (error.line > 0) {
350
- const className = error.severity === 'error' ? 'cm-error-line' : 'cm-warning-line';
351
- const handle = editor.addLineClass(error.line - 1, 'background', className);
352
- lineWidgets.push({
353
- clear: () => editor.removeLineClass(handle, 'background', className)
354
- });
355
- }
356
- });
357
- }
358
-
359
- function showLoading(show) {
360
- document.getElementById('loading').classList.toggle('active', show);
361
- }
362
-
363
- async function validateCode() {
364
- showLoading(true);
365
- try {
366
- const response = await fetch('/validate', {
367
- method: 'POST',
368
- headers: {'Content-Type': 'application/json'},
369
- body: JSON.stringify({
370
- code: editor.getValue(),
371
- level: 'semantic'
372
- })
373
- });
374
- const data = await response.json();
375
- updateErrors(data.errors);
376
- updateStatus(data.is_valid);
377
- } catch (e) {
378
- console.error('Validation failed:', e);
379
- }
380
- showLoading(false);
381
- }
382
-
383
- async function saveCode() {
384
- showLoading(true);
385
- try {
386
- const response = await fetch('/save', {
387
- method: 'POST',
388
- headers: {'Content-Type': 'application/json'},
389
- body: JSON.stringify({
390
- code: editor.getValue()
391
- })
392
- });
393
- const data = await response.json();
394
- if (data.success) {
395
- alert('Saved successfully!');
396
- } else {
397
- alert('Save failed: ' + data.error);
398
- }
399
- } catch (e) {
400
- alert('Save failed: ' + e.message);
401
- }
402
- showLoading(false);
403
- }
404
-
405
- async function compilePreview() {
406
- showLoading(true);
407
- try {
408
- const response = await fetch('/compile', {
409
- method: 'POST',
410
- headers: {'Content-Type': 'application/json'},
411
- body: JSON.stringify({
412
- code: editor.getValue()
413
- })
414
- });
415
- const data = await response.json();
416
- if (data.success) {
417
- window.open('/preview', '_blank');
418
- } else {
419
- updateErrors(data.errors || []);
420
- alert('Compilation failed. See errors panel.');
421
- }
422
- } catch (e) {
423
- alert('Compilation failed: ' + e.message);
424
- }
425
- showLoading(false);
426
- }
427
-
428
- function updateErrors(errors) {
429
- const list = document.getElementById('error-list');
430
- const count = document.getElementById('issue-count');
431
-
432
- count.textContent = `(${errors.length})`;
433
-
434
- if (errors.length === 0) {
435
- list.innerHTML = '<div class="no-errors">No issues found</div>';
436
- } else {
437
- list.innerHTML = errors.map(e => `
438
- <div class="error-item ${e.severity}" onclick="goToLine(${e.line}, ${e.column})">
439
- <span class="error-code">${e.code}</span>
440
- <span class="error-severity">${e.severity}</span>
441
- <div class="error-location">Line ${e.line}, Col ${e.column}</div>
442
- <div class="error-message">${e.message}</div>
443
- ${e.suggestion ? `<div class="error-suggestion">Fix: ${e.suggestion}</div>` : ''}
444
- </div>
445
- `).join('');
446
- }
447
-
448
- highlightErrors(errors);
449
- }
450
-
451
- function updateStatus(isValid) {
452
- const dot = document.querySelector('.status-dot');
453
- const text = document.getElementById('status-text');
454
-
455
- dot.className = 'status-dot ' + (isValid ? 'valid' : 'invalid');
456
- text.textContent = isValid ? 'Valid' : 'Has issues';
457
- }
458
- </script>
459
- </body>
460
- </html>
461
- """
462
-
463
-
464
- class LaTeXEditor:
465
- """Flask-based LaTeX editor with error highlighting."""
466
-
467
- def __init__(
468
- self,
469
- latex_code: str,
470
- bundle: Optional["FTS"] = None,
471
- output_path: Optional[Path] = None,
472
- ):
473
- """Initialize editor.
474
-
475
- Args:
476
- latex_code: Initial LaTeX code
477
- bundle: Associated FTS bundle
478
- output_path: Where to save edits
479
- """
480
- self.latex_code = latex_code
481
- self.bundle = bundle
482
- self.output_path = output_path
483
- self.app = Flask(__name__)
484
- self._setup_routes()
485
- self._validation_result: Optional[ValidationResult] = None
486
- self._compiled_pdf: Optional[Path] = None
487
-
488
- def _setup_routes(self) -> None:
489
- """Setup Flask routes."""
490
-
491
- @self.app.route("/")
492
- def index():
493
- # Validate code
494
- self._validation_result = validate_latex(self.latex_code, level="semantic")
495
-
496
- errors = self._validation_result.all_issues
497
- errors_json = json.dumps(
498
- [
499
- {
500
- "line": e.line,
501
- "column": e.column,
502
- "message": e.message,
503
- "severity": e.severity.value,
504
- "code": e.code,
505
- "suggestion": e.suggestion,
506
- }
507
- for e in errors
508
- ]
509
- )
510
-
511
- bundle_name = "Untitled"
512
- if self.bundle:
513
- bundle_name = self.bundle.node.name or self.bundle.node.id
514
-
515
- return render_template_string(
516
- EDITOR_TEMPLATE,
517
- latex_code=self.latex_code,
518
- errors=errors,
519
- errors_json=errors_json,
520
- error_count=len(errors),
521
- is_valid=self._validation_result.is_valid,
522
- bundle_name=bundle_name,
523
- )
524
-
525
- @self.app.route("/validate", methods=["POST"])
526
- def validate():
527
- data = request.get_json()
528
- code = data.get("code", "")
529
- level = data.get("level", "semantic")
530
-
531
- result = validate_latex(code, level=level)
532
- self.latex_code = code
533
-
534
- return jsonify(
535
- {
536
- "is_valid": result.is_valid,
537
- "errors": [
538
- {
539
- "line": e.line,
540
- "column": e.column,
541
- "message": e.message,
542
- "severity": e.severity.value,
543
- "code": e.code,
544
- "suggestion": e.suggestion,
545
- }
546
- for e in result.all_issues
547
- ],
548
- }
549
- )
550
-
551
- @self.app.route("/save", methods=["POST"])
552
- def save():
553
- data = request.get_json()
554
- code = data.get("code", "")
555
-
556
- try:
557
- if self.output_path:
558
- self.output_path.write_text(code, encoding="utf-8")
559
- self.latex_code = code
560
- return jsonify({"success": True})
561
- elif self.bundle:
562
- # Save to bundle exports
563
- exports_dir = self.bundle.path / "exports"
564
- exports_dir.mkdir(exist_ok=True)
565
- tex_path = exports_dir / f"{self.bundle.node.id}.tex"
566
- tex_path.write_text(code, encoding="utf-8")
567
- self.latex_code = code
568
- return jsonify({"success": True, "path": str(tex_path)})
569
- else:
570
- return jsonify({"success": False, "error": "No output path configured"})
571
- except Exception as e:
572
- return jsonify({"success": False, "error": str(e)})
573
-
574
- @self.app.route("/compile", methods=["POST"])
575
- def compile():
576
- data = request.get_json()
577
- code = data.get("code", "")
578
-
579
- # Wrap in document if needed
580
- if "\\documentclass" not in code:
581
- code = _wrap_in_document(code)
582
-
583
- try:
584
- with tempfile.TemporaryDirectory() as tmpdir:
585
- tex_path = Path(tmpdir) / "document.tex"
586
- tex_path.write_text(code, encoding="utf-8")
587
-
588
- proc = subprocess.run(
589
- ["pdflatex", "-interaction=nonstopmode", str(tex_path)],
590
- cwd=tmpdir,
591
- capture_output=True,
592
- text=True,
593
- timeout=30,
594
- )
595
-
596
- pdf_path = Path(tmpdir) / "document.pdf"
597
- if pdf_path.exists():
598
- # Copy to temp location accessible by preview route
599
- import shutil
600
-
601
- self._compiled_pdf = Path(tempfile.gettempdir()) / "fsb_preview.pdf"
602
- shutil.copy(pdf_path, self._compiled_pdf)
603
- return jsonify({"success": True})
604
- else:
605
- # Parse errors
606
- result = validate_latex(code, level="compile")
607
- return jsonify(
608
- {
609
- "success": False,
610
- "errors": [
611
- {
612
- "line": e.line,
613
- "column": e.column,
614
- "message": e.message,
615
- "severity": e.severity.value,
616
- "code": e.code,
617
- "suggestion": e.suggestion,
618
- }
619
- for e in result.all_issues
620
- ],
621
- }
622
- )
623
-
624
- except FileNotFoundError:
625
- return jsonify({"success": False, "error": "pdflatex not found"})
626
- except subprocess.TimeoutExpired:
627
- return jsonify({"success": False, "error": "Compilation timed out"})
628
- except Exception as e:
629
- return jsonify({"success": False, "error": str(e)})
630
-
631
- @self.app.route("/preview")
632
- def preview():
633
- if self._compiled_pdf and self._compiled_pdf.exists():
634
- from flask import send_file
635
-
636
- return send_file(self._compiled_pdf, mimetype="application/pdf")
637
- return "No preview available", 404
638
-
639
- @self.app.route("/shutdown", methods=["POST"])
640
- def shutdown():
641
- func = request.environ.get("werkzeug.server.shutdown")
642
- if func:
643
- func()
644
- return "Shutting down..."
645
-
646
- def run(self, port: int = 5051, open_browser: bool = True) -> None:
647
- """Run the editor server.
648
-
649
- Args:
650
- port: Port to run on
651
- open_browser: Whether to open browser automatically
652
- """
653
- # Find available port
654
- port = _find_available_port(port)
655
-
656
- url = f"http://127.0.0.1:{port}"
657
- print(f"LaTeX Editor running at {url}")
658
-
659
- if open_browser:
660
- webbrowser.open(url)
661
-
662
- self.app.run(host="127.0.0.1", port=port, debug=False, use_reloader=False)
663
-
664
-
665
- def _find_available_port(start_port: int = 5051) -> int:
666
- """Find an available port.
667
-
668
- Args:
669
- start_port: Port to start searching from
670
-
671
- Returns:
672
- Available port number
673
- """
674
- for port in range(start_port, start_port + 100):
675
- try:
676
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
677
- s.bind(("127.0.0.1", port))
678
- return port
679
- except OSError:
680
- continue
681
- raise RuntimeError("No available ports found")
682
-
683
-
684
- def _wrap_in_document(content: str) -> str:
685
- """Wrap content in a minimal LaTeX document.
686
-
687
- Args:
688
- content: LaTeX content
689
-
690
- Returns:
691
- Complete document string
692
- """
693
- return f"""\\documentclass{{article}}
694
- \\usepackage{{graphicx}}
695
- \\usepackage{{booktabs}}
696
- \\usepackage{{amsmath}}
697
- \\begin{{document}}
698
- {content}
699
- \\end{{document}}
700
- """
701
-
702
-
703
- def launch_editor(
704
- latex_code: str,
705
- bundle: Optional["FTS"] = None,
706
- output_path: Optional[Path] = None,
707
- port: int = 5051,
708
- open_browser: bool = True,
709
- ) -> None:
710
- """Launch the LaTeX editor.
711
-
712
- Args:
713
- latex_code: LaTeX code to edit
714
- bundle: Associated FTS bundle
715
- output_path: Where to save edits
716
- port: Port to run on
717
- open_browser: Whether to open browser
718
- """
719
- editor = LaTeXEditor(latex_code, bundle, output_path)
720
- editor.run(port=port, open_browser=open_browser)
721
-
722
-
723
- __all__ = ["LaTeXEditor", "launch_editor"]
724
-
725
- # EOF