scitex 2.8.1__py3-none-any.whl → 2.10.2__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.
- scitex/__init__.py +15 -7
- scitex/__version__.py +1 -2
- scitex/_install_guide.py +250 -0
- scitex/_optional_deps.py +206 -39
- scitex/ai/_gen_ai/_Groq.py +2 -4
- scitex/ai/_gen_ai/_OpenAI.py +5 -2
- scitex/ai/_gen_ai/_Perplexity.py +20 -6
- scitex/audio/__init__.py +24 -15
- scitex/audio/_cross_process_lock.py +139 -0
- scitex/audio/_mcp_handlers.py +256 -0
- scitex/audio/_mcp_tool_schemas.py +203 -0
- scitex/audio/engines/elevenlabs_engine.py +5 -2
- scitex/audio/mcp_server.py +98 -457
- scitex/bridge/__init__.py +30 -19
- scitex/bridge/_figrecipe.py +245 -0
- scitex/bridge/_helpers.py +2 -1
- scitex/bridge/_plt_vis.py +23 -10
- scitex/bridge/_stats_plt.py +18 -5
- scitex/bridge/_stats_vis.py +16 -2
- scitex/browser/__init__.py +84 -44
- scitex/browser/automation/__init__.py +5 -1
- scitex/browser/core/BrowserMixin.py +17 -4
- scitex/browser/core/__init__.py +11 -2
- scitex/browser/remote/CaptchaHandler.py +1 -1
- scitex/browser/remote/ZenRowsAPIClient.py +1 -1
- scitex/capture/grid.py +487 -0
- scitex/capture/mcp_handlers.py +401 -0
- scitex/capture/mcp_tool_defs.py +192 -0
- scitex/capture/mcp_tools.py +241 -0
- scitex/capture/mcp_utils.py +30 -0
- scitex/cli/convert.py +421 -0
- scitex/cli/main.py +6 -4
- scitex/datetime/__init__.py +46 -0
- scitex/datetime/_linspace.py +100 -0
- scitex/datetime/_normalize_timestamp.py +306 -0
- scitex/db/_delete_duplicates.py +4 -4
- scitex/db/_sqlite3/_delete_duplicates.py +11 -2
- scitex/dev/plt/__init__.py +61 -62
- scitex/dev/plt/demo_plotters/__init__.py +0 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axhline.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axhspan.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axvline.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axvspan.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_bar.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_barh.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_boxplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_contour.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_contourf.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_errorbar.py +30 -0
- scitex/dev/plt/demo_plotters/plot_mpl_eventplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_fill.py +30 -0
- scitex/dev/plt/demo_plotters/plot_mpl_fill_between.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hexbin.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hist.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hist2d.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_imshow.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_pcolormesh.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_pie.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_plot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_quiver.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_scatter.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_stackplot.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_stem.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_step.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_violinplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_sns_barplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_boxplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_heatmap.py +28 -0
- scitex/dev/plt/demo_plotters/plot_sns_histplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_kdeplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_lineplot.py +31 -0
- scitex/dev/plt/demo_plotters/plot_sns_scatterplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_stripplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_swarmplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_violinplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_bar.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_barh.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_box.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_boxplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_conf_mat.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_contour.py +31 -0
- scitex/dev/plt/demo_plotters/plot_stx_ecdf.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_errorbar.py +30 -0
- scitex/dev/plt/demo_plotters/plot_stx_fill_between.py +31 -0
- scitex/dev/plt/demo_plotters/plot_stx_fillv.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_heatmap.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_image.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_imshow.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_joyplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_kde.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_line.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_mean_ci.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_mean_std.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_median_iqr.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_raster.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_rectangle.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_scatter.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_shaded_line.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_violin.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_violinplot.py +28 -0
- scitex/dev/plt/mpl/get_dir_ax.py +46 -0
- scitex/dev/plt/mpl/get_signatures.py +176 -0
- scitex/dev/plt/mpl/get_signatures_details.py +522 -0
- scitex/dict/_pop_keys.py +1 -7
- scitex/dsp/__init__.py +15 -10
- scitex/dsp/add_noise.py +5 -2
- scitex/dsp/example.py +35 -22
- scitex/dsp/filt.py +8 -3
- scitex/dsp/reference.py +3 -2
- scitex/dsp/utils/__init__.py +2 -1
- scitex/dsp/utils/_differential_bandpass_filters.py +14 -4
- scitex/dt/__init__.py +39 -2
- scitex/errors.py +82 -521
- scitex/fig/__init__.py +4 -4
- scitex/fig/editor/edit/panel_loader.py +1 -1
- scitex/fig/io/_bundle.py +7 -7
- scitex/fts/README.md +262 -0
- scitex/fts/TODO.md +66 -0
- scitex/fts/__init__.py +90 -0
- scitex/fts/_bundle/README_IN_BUNDLE.md +102 -0
- scitex/fts/_bundle/_FTS.py +657 -0
- scitex/fts/_bundle/__init__.py +38 -0
- scitex/fts/_bundle/_children.py +216 -0
- scitex/fts/_bundle/_conversion/__init__.py +15 -0
- scitex/fts/_bundle/_conversion/_bundle2dict.py +44 -0
- scitex/fts/_bundle/_conversion/_dict2bundle.py +50 -0
- scitex/fts/_bundle/_dataclasses/_Axes.py +57 -0
- scitex/fts/_bundle/_dataclasses/_BBox.py +54 -0
- scitex/fts/_bundle/_dataclasses/_ColumnDef.py +72 -0
- scitex/fts/_bundle/_dataclasses/_DataFormat.py +40 -0
- scitex/fts/_bundle/_dataclasses/_DataInfo.py +135 -0
- scitex/fts/_bundle/_dataclasses/_DataSource.py +44 -0
- scitex/fts/_bundle/_dataclasses/_Node.py +319 -0
- scitex/fts/_bundle/_dataclasses/_NodeRefs.py +45 -0
- scitex/fts/_bundle/_dataclasses/_SizeMM.py +38 -0
- scitex/fts/_bundle/_dataclasses/__init__.py +35 -0
- scitex/fts/_bundle/_extractors/__init__.py +32 -0
- scitex/fts/_bundle/_extractors/_extract_bar.py +131 -0
- scitex/fts/_bundle/_extractors/_extract_line.py +71 -0
- scitex/fts/_bundle/_extractors/_extract_scatter.py +79 -0
- scitex/fts/_bundle/_loader.py +134 -0
- scitex/fts/_bundle/_mpl_helpers.py +389 -0
- scitex/fts/_bundle/_saver.py +269 -0
- scitex/fts/_bundle/_storage.py +200 -0
- scitex/fts/_bundle/_utils/__init__.py +55 -0
- scitex/fts/_bundle/_utils/_const.py +26 -0
- scitex/fts/_bundle/_utils/_errors.py +73 -0
- scitex/fts/_bundle/_utils/_generate.py +21 -0
- scitex/fts/_bundle/_utils/_types.py +76 -0
- scitex/fts/_bundle/_validation.py +434 -0
- scitex/fts/_bundle/_zipbundle.py +165 -0
- scitex/fts/_fig/__init__.py +22 -0
- scitex/fts/_fig/_backend/__init__.py +53 -0
- scitex/fts/_fig/_backend/_export.py +165 -0
- scitex/fts/_fig/_backend/_parser.py +188 -0
- scitex/fts/_fig/_backend/_render.py +538 -0
- scitex/fts/_fig/_composite.py +345 -0
- scitex/fts/_fig/_dataclasses/_ChannelEncoding.py +46 -0
- scitex/fts/_fig/_dataclasses/_Encoding.py +82 -0
- scitex/fts/_fig/_dataclasses/_Theme.py +441 -0
- scitex/fts/_fig/_dataclasses/_TraceEncoding.py +52 -0
- scitex/fts/_fig/_dataclasses/__init__.py +47 -0
- scitex/fts/_fig/_editor/__init__.py +14 -0
- scitex/fts/_fig/_editor/_cui/__init__.py +33 -0
- scitex/fts/_fig/_editor/_cui/_backend_detector.py +39 -0
- scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +366 -0
- scitex/fts/_fig/_editor/_cui/_editor_launcher.py +175 -0
- scitex/fts/_fig/_editor/_cui/_manual_handler.py +52 -0
- scitex/fts/_fig/_editor/_cui/_panel_loader.py +246 -0
- scitex/fts/_fig/_editor/_cui/_path_resolver.py +66 -0
- scitex/fts/_fig/_editor/_defaults.py +300 -0
- scitex/fts/_fig/_editor/_gui/__init__.py +11 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +20 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_bbox.py +1339 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_core.py +1688 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +664 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_renderer.py +853 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +79 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +41 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +16 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +85 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +217 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +93 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +57 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +112 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +59 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +212 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/canvas.css +176 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +190 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +59 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +45 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +95 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +101 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +138 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +31 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +7 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +56 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +78 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +314 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +107 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +54 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +172 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +258 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +48 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +71 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +288 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +143 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +245 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +992 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +339 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +286 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +371 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +293 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +426 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +152 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +265 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +184 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +57 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +100 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +34 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +124 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +851 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +4932 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +1657 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor.py +36 -0
- scitex/fts/_fig/_models/_Annotations.py +115 -0
- scitex/fts/_fig/_models/_Axes.py +152 -0
- scitex/fts/_fig/_models/_Figure.py +138 -0
- scitex/fts/_fig/_models/_Guides.py +104 -0
- scitex/fts/_fig/_models/_Plot.py +123 -0
- scitex/fts/_fig/_models/_Styles.py +245 -0
- scitex/fts/_fig/_models/__init__.py +80 -0
- scitex/fts/_fig/_models/_plot_types/__init__.py +156 -0
- scitex/fts/_fig/_models/_plot_types/_bar.py +43 -0
- scitex/fts/_fig/_models/_plot_types/_box.py +38 -0
- scitex/fts/_fig/_models/_plot_types/_distribution.py +36 -0
- scitex/fts/_fig/_models/_plot_types/_errorbar.py +60 -0
- scitex/fts/_fig/_models/_plot_types/_histogram.py +30 -0
- scitex/fts/_fig/_models/_plot_types/_image.py +61 -0
- scitex/fts/_fig/_models/_plot_types/_line.py +57 -0
- scitex/fts/_fig/_models/_plot_types/_scatter.py +30 -0
- scitex/fts/_fig/_models/_plot_types/_seaborn.py +121 -0
- scitex/fts/_fig/_models/_plot_types/_violin.py +36 -0
- scitex/fts/_fig/_utils/__init__.py +129 -0
- scitex/fts/_fig/_utils/_auto_layout.py +127 -0
- scitex/fts/_fig/_utils/_calc_bounds.py +111 -0
- scitex/fts/_fig/_utils/_const_sizes.py +48 -0
- scitex/fts/_fig/_utils/_convert_coords.py +77 -0
- scitex/fts/_fig/_utils/_get_template.py +178 -0
- scitex/fts/_fig/_utils/_normalize.py +73 -0
- scitex/fts/_fig/_utils/_plot_layout.py +397 -0
- scitex/fts/_fig/_utils/_validate.py +197 -0
- scitex/fts/_kinds/__init__.py +45 -0
- scitex/fts/_kinds/_figure/__init__.py +19 -0
- scitex/fts/_kinds/_figure/_composite.py +345 -0
- scitex/fts/_kinds/_plot/__init__.py +25 -0
- scitex/fts/_kinds/_plot/_backend/__init__.py +53 -0
- scitex/fts/_kinds/_plot/_backend/_export.py +165 -0
- scitex/fts/_kinds/_plot/_backend/_parser.py +188 -0
- scitex/fts/_kinds/_plot/_backend/_render.py +538 -0
- scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +46 -0
- scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +82 -0
- scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +441 -0
- scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +52 -0
- scitex/fts/_kinds/_plot/_dataclasses/__init__.py +47 -0
- scitex/fts/_kinds/_plot/_models/_Annotations.py +115 -0
- scitex/fts/_kinds/_plot/_models/_Axes.py +152 -0
- scitex/fts/_kinds/_plot/_models/_Figure.py +138 -0
- scitex/fts/_kinds/_plot/_models/_Guides.py +104 -0
- scitex/fts/_kinds/_plot/_models/_Plot.py +123 -0
- scitex/fts/_kinds/_plot/_models/_Styles.py +245 -0
- scitex/fts/_kinds/_plot/_models/__init__.py +80 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +156 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +43 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +38 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +36 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +60 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +30 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +61 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +57 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +30 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +121 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +36 -0
- scitex/fts/_kinds/_plot/_utils/__init__.py +129 -0
- scitex/fts/_kinds/_plot/_utils/_auto_layout.py +127 -0
- scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +111 -0
- scitex/fts/_kinds/_plot/_utils/_const_sizes.py +48 -0
- scitex/fts/_kinds/_plot/_utils/_convert_coords.py +77 -0
- scitex/fts/_kinds/_plot/_utils/_get_template.py +178 -0
- scitex/fts/_kinds/_plot/_utils/_normalize.py +73 -0
- scitex/fts/_kinds/_plot/_utils/_plot_layout.py +397 -0
- scitex/fts/_kinds/_plot/_utils/_validate.py +197 -0
- scitex/fts/_kinds/_shape/__init__.py +141 -0
- scitex/fts/_kinds/_stats/__init__.py +56 -0
- scitex/fts/_kinds/_stats/_dataclasses/_Stats.py +423 -0
- scitex/fts/_kinds/_stats/_dataclasses/__init__.py +48 -0
- scitex/fts/_kinds/_table/__init__.py +72 -0
- scitex/fts/_kinds/_table/_latex/__init__.py +93 -0
- scitex/fts/_kinds/_table/_latex/_editor/__init__.py +11 -0
- scitex/fts/_kinds/_table/_latex/_editor/_app.py +725 -0
- scitex/fts/_kinds/_table/_latex/_export.py +279 -0
- scitex/fts/_kinds/_table/_latex/_figure_exporter.py +153 -0
- scitex/fts/_kinds/_table/_latex/_stats_formatter.py +274 -0
- scitex/fts/_kinds/_table/_latex/_table_exporter.py +362 -0
- scitex/fts/_kinds/_table/_latex/_utils.py +369 -0
- scitex/fts/_kinds/_table/_latex/_validator.py +445 -0
- scitex/fts/_kinds/_text/__init__.py +77 -0
- scitex/fts/_schemas/data_info.schema.json +75 -0
- scitex/fts/_schemas/encoding.schema.json +90 -0
- scitex/fts/_schemas/node.schema.json +145 -0
- scitex/fts/_schemas/render_manifest.schema.json +62 -0
- scitex/fts/_schemas/stats.schema.json +132 -0
- scitex/fts/_schemas/theme.schema.json +141 -0
- scitex/fts/_stats/__init__.py +48 -0
- scitex/fts/_stats/_dataclasses/_Stats.py +423 -0
- scitex/fts/_stats/_dataclasses/__init__.py +48 -0
- scitex/fts/_tables/__init__.py +65 -0
- scitex/fts/_tables/_latex/__init__.py +93 -0
- scitex/fts/_tables/_latex/_editor/__init__.py +11 -0
- scitex/fts/_tables/_latex/_editor/_app.py +725 -0
- scitex/fts/_tables/_latex/_export.py +279 -0
- scitex/fts/_tables/_latex/_figure_exporter.py +153 -0
- scitex/fts/_tables/_latex/_stats_formatter.py +274 -0
- scitex/fts/_tables/_latex/_table_exporter.py +362 -0
- scitex/fts/_tables/_latex/_utils.py +369 -0
- scitex/fts/_tables/_latex/_validator.py +445 -0
- scitex/gen/__init__.py +66 -25
- scitex/gen/misc.py +28 -0
- scitex/io/__init__.py +47 -32
- scitex/io/_load.py +87 -36
- scitex/io/_load_modules/__init__.py +10 -7
- scitex/io/_load_modules/_pandas.py +6 -1
- scitex/io/_save.py +299 -1556
- scitex/io/_save_modules/__init__.py +76 -19
- scitex/io/_save_modules/_figure_utils.py +90 -0
- scitex/io/_save_modules/_image_csv.py +497 -0
- scitex/io/_save_modules/_legends.py +91 -0
- scitex/io/_save_modules/_pltz_bundle.py +356 -0
- scitex/io/_save_modules/_pltz_stx.py +536 -0
- scitex/io/_save_modules/_stx_bundle.py +104 -0
- scitex/io/_save_modules/_symlink.py +96 -0
- scitex/io/_save_modules/_yaml.py +1 -1
- scitex/io/_save_modules/_zarr.py +64 -18
- scitex/io/bundle/README.md +212 -0
- scitex/io/bundle/__init__.py +110 -0
- scitex/io/{_bundle.py → bundle/_core.py} +168 -97
- scitex/io/bundle/_nested.py +713 -0
- scitex/io/bundle/_types.py +74 -0
- scitex/io/{_zip_bundle.py → bundle/_zip.py} +93 -45
- scitex/io/utils/h5_to_zarr.py +1 -1
- scitex/logging/__init__.py +108 -13
- scitex/logging/_errors.py +508 -0
- scitex/logging/_formatters.py +30 -6
- scitex/logging/_warnings.py +261 -0
- scitex/plt/__init__.py +4 -1
- scitex/plt/_figrecipe.py +236 -0
- scitex/plt/_subplots/_AxisWrapper.py +6 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +112 -1
- scitex/plt/_subplots/_FigWrapper.py +15 -0
- scitex/plt/_subplots/_SubplotsWrapper.py +125 -489
- scitex/plt/_subplots/_export_as_csv.py +11 -0
- scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +2 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_pcolormesh.py +66 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stackplot.py +62 -0
- scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py +208 -0
- scitex/plt/_subplots/_fonts.py +71 -0
- scitex/plt/_subplots/_mm_layout.py +282 -0
- scitex/plt/gallery/__init__.py +99 -2
- scitex/plt/styles/_plot_postprocess.py +3 -1
- scitex/plt/utils/_configure_mpl.py +16 -19
- scitex/repro/_RandomStateManager.py +13 -8
- scitex/resource/__init__.py +19 -1
- scitex/resource/_utils/_get_env_info.py +13 -25
- scitex/schema/__init__.py +149 -160
- scitex/schema/_encoding.py +273 -0
- scitex/schema/_figure_elements.py +406 -0
- scitex/schema/_theme.py +360 -0
- scitex/schema/_validation.py +0 -98
- scitex/scholar/__init__.py +56 -14
- scitex/scholar/auth/ScholarAuthManager.py +1 -1
- scitex/scholar/auth/__init__.py +11 -2
- scitex/scholar/auth/providers/BaseAuthenticator.py +1 -1
- scitex/scholar/auth/providers/EZProxyAuthenticator.py +1 -1
- scitex/scholar/auth/providers/OpenAthensAuthenticator.py +1 -1
- scitex/scholar/auth/providers/ShibbolethAuthenticator.py +1 -1
- scitex/scholar/config/ScholarConfig.py +1 -1
- scitex/scholar/core/Scholar.py +1 -1
- scitex/session/_decorator.py +18 -16
- scitex/session/_lifecycle.py +9 -11
- scitex/session/template.py +9 -8
- scitex/sh/test_sh.py +72 -0
- scitex/sh/test_sh_simple.py +61 -0
- scitex/stats/__init__.py +221 -97
- scitex/stats/_schema.py +21 -22
- scitex/stats/descriptive/_circular.py +212 -351
- scitex/stats/descriptive/_describe.py +81 -132
- scitex/stats/descriptive/_nan.py +205 -433
- scitex/stats/descriptive/_real.py +127 -141
- scitex/str/_format_plot_text.py +5 -5
- scitex/str/_latex.py +26 -84
- scitex/str/_latex_fallback.py +53 -47
- scitex/web/_search_pubmed.py +5 -4
- scitex/writer/tests/test_diff_between.py +451 -0
- scitex/writer/tests/test_document_section.py +311 -0
- scitex/writer/tests/test_document_workflow.py +393 -0
- scitex/writer/tests/test_writer.py +361 -0
- scitex/writer/tests/test_writer_integration.py +303 -0
- {scitex-2.8.1.dist-info → scitex-2.10.2.dist-info}/METADATA +368 -183
- {scitex-2.8.1.dist-info → scitex-2.10.2.dist-info}/RECORD +412 -97
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/ARCHITECTURE_EXAMPLE.md +0 -905
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/BULLETIN_BOARD_EXAMPLE.md +0 -99
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/PROJECT_DESCRIPTION_EXAMPLE.md +0 -96
- {scitex-2.8.1.dist-info → scitex-2.10.2.dist-info}/WHEEL +0 -0
- {scitex-2.8.1.dist-info → scitex-2.10.2.dist-info}/entry_points.txt +0 -0
- {scitex-2.8.1.dist-info → scitex-2.10.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Overlay Rendering
|
|
3
|
+
* Handles SVG overlay for element highlighting and debug visualization
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Main Overlay Update
|
|
8
|
+
// ============================================================================
|
|
9
|
+
function updateOverlay() {
|
|
10
|
+
const overlay = document.getElementById('hover-overlay');
|
|
11
|
+
// Find the visible preview element (SVG wrapper or img)
|
|
12
|
+
const svgWrapper = document.getElementById('preview-svg-wrapper');
|
|
13
|
+
const imgEl = document.getElementById('preview');
|
|
14
|
+
|
|
15
|
+
let targetEl = null;
|
|
16
|
+
if (svgWrapper) {
|
|
17
|
+
targetEl = svgWrapper.querySelector('svg') || svgWrapper;
|
|
18
|
+
} else if (imgEl && imgEl.offsetWidth > 0) {
|
|
19
|
+
targetEl = imgEl;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!targetEl) return;
|
|
23
|
+
|
|
24
|
+
const rect = targetEl.getBoundingClientRect();
|
|
25
|
+
if (rect.width === 0 || rect.height === 0) return;
|
|
26
|
+
|
|
27
|
+
// Guard against zero imgSize (can cause Infinity scale)
|
|
28
|
+
if (!imgSize || !imgSize.width || !imgSize.height || imgSize.width === 0 || imgSize.height === 0) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
overlay.setAttribute('width', rect.width);
|
|
33
|
+
overlay.setAttribute('height', rect.height);
|
|
34
|
+
overlay.style.width = rect.width + 'px';
|
|
35
|
+
overlay.style.height = rect.height + 'px';
|
|
36
|
+
|
|
37
|
+
// Position overlay over the target element
|
|
38
|
+
const containerRect = document.getElementById('preview-container').getBoundingClientRect();
|
|
39
|
+
overlay.style.left = (rect.left - containerRect.left) + 'px';
|
|
40
|
+
overlay.style.top = (rect.top - containerRect.top) + 'px';
|
|
41
|
+
|
|
42
|
+
const scaleX = rect.width / imgSize.width;
|
|
43
|
+
const scaleY = rect.height / imgSize.height;
|
|
44
|
+
|
|
45
|
+
let svg = '';
|
|
46
|
+
|
|
47
|
+
// Debug mode: draw ALL bboxes
|
|
48
|
+
if (debugMode) {
|
|
49
|
+
svg += drawDebugBboxes(scaleX, scaleY);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function drawElement(elementName, type) {
|
|
53
|
+
const bbox = elementBboxes[elementName];
|
|
54
|
+
if (!bbox) return '';
|
|
55
|
+
|
|
56
|
+
const elementType = bbox.element_type || '';
|
|
57
|
+
const hasPoints = bbox.points && bbox.points.length > 0;
|
|
58
|
+
|
|
59
|
+
// Lines - draw as path
|
|
60
|
+
if ((elementType === 'line' || elementName.includes('trace_')) && hasPoints) {
|
|
61
|
+
return drawTracePath(bbox, scaleX, scaleY, type);
|
|
62
|
+
}
|
|
63
|
+
// Scatter - draw as circles
|
|
64
|
+
else if (elementType === 'scatter' && hasPoints) {
|
|
65
|
+
return drawScatterPoints(bbox, scaleX, scaleY, type);
|
|
66
|
+
}
|
|
67
|
+
// Default - draw bbox rectangle
|
|
68
|
+
else {
|
|
69
|
+
const rectClass = type === 'hover' ? 'hover-rect' : 'selected-rect';
|
|
70
|
+
const labelClass = type === 'hover' ? 'hover-label' : 'selected-label';
|
|
71
|
+
const x = bbox.x0 * scaleX - 2;
|
|
72
|
+
const y = bbox.y0 * scaleY - 2;
|
|
73
|
+
const w = (bbox.x1 - bbox.x0) * scaleX + 4;
|
|
74
|
+
const h = (bbox.y1 - bbox.y0) * scaleY + 4;
|
|
75
|
+
return `<rect class="${rectClass}" x="${x}" y="${y}" width="${w}" height="${h}" rx="2"/>` +
|
|
76
|
+
`<text class="${labelClass}" x="${x}" y="${y - 4}">${bbox.label || elementName}</text>`;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (hoveredElement && hoveredElement !== selectedElement) {
|
|
81
|
+
svg += drawElement(hoveredElement, 'hover');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (selectedElement) {
|
|
85
|
+
svg += drawElement(selectedElement, 'selected');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
overlay.innerHTML = svg;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ============================================================================
|
|
92
|
+
// Draw Path/Line Elements
|
|
93
|
+
// ============================================================================
|
|
94
|
+
function drawTracePath(bbox, scaleX, scaleY, type) {
|
|
95
|
+
if (!Array.isArray(bbox.points) || bbox.points.length < 2) return '';
|
|
96
|
+
|
|
97
|
+
const points = bbox.points.filter(pt => Array.isArray(pt) && pt.length >= 2);
|
|
98
|
+
if (points.length < 2) return '';
|
|
99
|
+
|
|
100
|
+
let pathD = `M ${points[0][0] * scaleX} ${points[0][1] * scaleY}`;
|
|
101
|
+
for (let i = 1; i < points.length; i++) {
|
|
102
|
+
pathD += ` L ${points[i][0] * scaleX} ${points[i][1] * scaleY}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const className = type === 'hover' ? 'hover-path' : 'selected-path';
|
|
106
|
+
const labelX = points[0][0] * scaleX;
|
|
107
|
+
const labelY = points[0][1] * scaleY - 8;
|
|
108
|
+
const labelClass = type === 'hover' ? 'hover-label' : 'selected-label';
|
|
109
|
+
|
|
110
|
+
return `<path class="${className}" d="${pathD}"/>` +
|
|
111
|
+
`<text class="${labelClass}" x="${labelX}" y="${labelY}">${bbox.label || ''}</text>`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ============================================================================
|
|
115
|
+
// Draw Scatter Points
|
|
116
|
+
// ============================================================================
|
|
117
|
+
function drawScatterPoints(bbox, scaleX, scaleY, type) {
|
|
118
|
+
// Draw scatter points as circles
|
|
119
|
+
if (!Array.isArray(bbox.points) || bbox.points.length === 0) return '';
|
|
120
|
+
|
|
121
|
+
const className = type === 'hover' ? 'hover-scatter' : 'selected-scatter';
|
|
122
|
+
const labelClass = type === 'hover' ? 'hover-label' : 'selected-label';
|
|
123
|
+
const radius = 4;
|
|
124
|
+
|
|
125
|
+
let svg = '';
|
|
126
|
+
for (const pt of bbox.points) {
|
|
127
|
+
if (!Array.isArray(pt) || pt.length < 2) continue;
|
|
128
|
+
const [x, y] = pt;
|
|
129
|
+
svg += `<circle class="${className}" cx="${x * scaleX}" cy="${y * scaleY}" r="${radius}"/>`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Add label near first point
|
|
133
|
+
const validPoints = bbox.points.filter(pt => Array.isArray(pt) && pt.length >= 2);
|
|
134
|
+
if (validPoints.length > 0) {
|
|
135
|
+
const labelX = validPoints[0][0] * scaleX;
|
|
136
|
+
const labelY = validPoints[0][1] * scaleY - 10;
|
|
137
|
+
svg += `<text class="${labelClass}" x="${labelX}" y="${labelY}">${bbox.label || ''}</text>`;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return svg;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ============================================================================
|
|
144
|
+
// Debug Visualization
|
|
145
|
+
// ============================================================================
|
|
146
|
+
function drawDebugBboxes(scaleX, scaleY) {
|
|
147
|
+
let svg = '';
|
|
148
|
+
let count = 0;
|
|
149
|
+
|
|
150
|
+
for (const [name, bbox] of Object.entries(elementBboxes)) {
|
|
151
|
+
if (name === '_meta') continue;
|
|
152
|
+
|
|
153
|
+
count++;
|
|
154
|
+
const hasPoints = bbox.points && bbox.points.length > 0;
|
|
155
|
+
const elementType = bbox.element_type || '';
|
|
156
|
+
|
|
157
|
+
// Choose color based on element type
|
|
158
|
+
let rectClass = 'debug-rect';
|
|
159
|
+
if (name.includes('trace_') || elementType === 'line') {
|
|
160
|
+
rectClass = 'debug-rect-trace';
|
|
161
|
+
} else if (name.includes('legend')) {
|
|
162
|
+
rectClass = 'debug-rect-legend';
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Draw bbox rectangle
|
|
166
|
+
const x = bbox.x0 * scaleX;
|
|
167
|
+
const y = bbox.y0 * scaleY;
|
|
168
|
+
const w = (bbox.x1 - bbox.x0) * scaleX;
|
|
169
|
+
const h = (bbox.y1 - bbox.y0) * scaleY;
|
|
170
|
+
|
|
171
|
+
svg += `<rect class="${rectClass}" x="${x}" y="${y}" width="${w}" height="${h}"/>`;
|
|
172
|
+
svg += `<text class="debug-label" x="${x + 2}" y="${y + 10}">${name}</text>`;
|
|
173
|
+
|
|
174
|
+
// Draw path points if available
|
|
175
|
+
if (hasPoints && bbox.points.length > 1) {
|
|
176
|
+
let pathD = `M ${bbox.points[0][0] * scaleX} ${bbox.points[0][1] * scaleY}`;
|
|
177
|
+
for (let i = 1; i < bbox.points.length; i++) {
|
|
178
|
+
const pt = bbox.points[i];
|
|
179
|
+
if (pt && pt.length >= 2) {
|
|
180
|
+
pathD += ` L ${pt[0] * scaleX} ${pt[1] * scaleY}`;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
svg += `<path class="debug-path" d="${pathD}"/>`;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return svg;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function toggleDebugMode() {
|
|
191
|
+
debugMode = !debugMode;
|
|
192
|
+
const btn = document.getElementById('debug-toggle-btn');
|
|
193
|
+
if (btn) {
|
|
194
|
+
btn.classList.toggle('active', debugMode);
|
|
195
|
+
btn.textContent = debugMode ? 'Hide Hit Areas' : 'Show Hit Areas';
|
|
196
|
+
}
|
|
197
|
+
updateOverlay();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ============================================================================
|
|
201
|
+
// Panel Overlay Update (for multi-panel canvas)
|
|
202
|
+
// ============================================================================
|
|
203
|
+
function updatePanelOverlay(overlay, bboxes, imgSizePanel, displayWidth, displayHeight, hovered, selected, img) {
|
|
204
|
+
if (!overlay) return;
|
|
205
|
+
|
|
206
|
+
overlay.innerHTML = '';
|
|
207
|
+
|
|
208
|
+
// Calculate actual rendered dimensions accounting for object-fit: contain
|
|
209
|
+
let scaleX, scaleY, offsetX = 0, offsetY = 0;
|
|
210
|
+
|
|
211
|
+
if (img && img.naturalWidth && img.naturalHeight) {
|
|
212
|
+
const dims = getObjectFitContainDimensions(img);
|
|
213
|
+
scaleX = dims.displayWidth / imgSizePanel.width;
|
|
214
|
+
scaleY = dims.displayHeight / imgSizePanel.height;
|
|
215
|
+
offsetX = dims.offsetX;
|
|
216
|
+
offsetY = dims.offsetY;
|
|
217
|
+
|
|
218
|
+
// Use container dimensions for the overlay size
|
|
219
|
+
overlay.setAttribute('width', img.clientWidth);
|
|
220
|
+
overlay.setAttribute('height', img.clientHeight);
|
|
221
|
+
} else {
|
|
222
|
+
// Fallback for backward compatibility
|
|
223
|
+
scaleX = displayWidth / imgSizePanel.width;
|
|
224
|
+
scaleY = displayHeight / imgSizePanel.height;
|
|
225
|
+
overlay.setAttribute('width', displayWidth);
|
|
226
|
+
overlay.setAttribute('height', displayHeight);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let svg = '';
|
|
230
|
+
|
|
231
|
+
// Debug mode: draw all bboxes (with offset for object-fit:contain letterboxing)
|
|
232
|
+
if (panelDebugMode) {
|
|
233
|
+
svg += drawPanelDebugBboxes(bboxes, scaleX, scaleY, offsetX, offsetY);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function drawPanelElement(elementName, type) {
|
|
237
|
+
const bbox = bboxes[elementName];
|
|
238
|
+
if (!bbox) return '';
|
|
239
|
+
|
|
240
|
+
const elementType = bbox.element_type || '';
|
|
241
|
+
const hasPoints = bbox.points && bbox.points.length > 0;
|
|
242
|
+
|
|
243
|
+
// Lines - draw as path (with offset)
|
|
244
|
+
if ((elementType === 'line' || elementName.includes('trace_')) && hasPoints) {
|
|
245
|
+
const points = bbox.points.filter(pt => Array.isArray(pt) && pt.length >= 2);
|
|
246
|
+
if (points.length < 2) return '';
|
|
247
|
+
|
|
248
|
+
let pathD = `M ${points[0][0] * scaleX + offsetX} ${points[0][1] * scaleY + offsetY}`;
|
|
249
|
+
for (let i = 1; i < points.length; i++) {
|
|
250
|
+
pathD += ` L ${points[i][0] * scaleX + offsetX} ${points[i][1] * scaleY + offsetY}`;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const className = type === 'hover' ? 'hover-path' : 'selected-path';
|
|
254
|
+
return `<path class="${className}" d="${pathD}"/>`;
|
|
255
|
+
}
|
|
256
|
+
// Scatter - draw as circles (with offset)
|
|
257
|
+
else if (elementType === 'scatter' && hasPoints) {
|
|
258
|
+
const className = type === 'hover' ? 'hover-scatter' : 'selected-scatter';
|
|
259
|
+
let circles = '';
|
|
260
|
+
for (const pt of bbox.points) {
|
|
261
|
+
if (!Array.isArray(pt) || pt.length < 2) continue;
|
|
262
|
+
const [x, y] = pt;
|
|
263
|
+
circles += `<circle class="${className}" cx="${x * scaleX + offsetX}" cy="${y * scaleY + offsetY}" r="3"/>`;
|
|
264
|
+
}
|
|
265
|
+
return circles;
|
|
266
|
+
}
|
|
267
|
+
// Default - draw bbox rectangle (with offset)
|
|
268
|
+
else {
|
|
269
|
+
const rectClass = type === 'hover' ? 'hover-rect' : 'selected-rect';
|
|
270
|
+
const x = bbox.x0 * scaleX + offsetX;
|
|
271
|
+
const y = bbox.y0 * scaleY + offsetY;
|
|
272
|
+
const w = (bbox.x1 - bbox.x0) * scaleX;
|
|
273
|
+
const h = (bbox.y1 - bbox.y0) * scaleY;
|
|
274
|
+
return `<rect class="${rectClass}" x="${x}" y="${y}" width="${w}" height="${h}" rx="1"/>`;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (hovered && hovered !== selected) {
|
|
279
|
+
svg += drawPanelElement(hovered, 'hover');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (selected) {
|
|
283
|
+
svg += drawPanelElement(selected, 'selected');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
overlay.innerHTML = svg;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function drawPanelDebugBboxes(bboxes, scaleX, scaleY, offsetX, offsetY) {
|
|
290
|
+
let svg = '';
|
|
291
|
+
|
|
292
|
+
// Default offset to 0 if not provided
|
|
293
|
+
offsetX = offsetX || 0;
|
|
294
|
+
offsetY = offsetY || 0;
|
|
295
|
+
|
|
296
|
+
for (const [name, bbox] of Object.entries(bboxes)) {
|
|
297
|
+
if (name === '_meta') continue;
|
|
298
|
+
|
|
299
|
+
const hasPoints = bbox.points && bbox.points.length > 0;
|
|
300
|
+
const elementType = bbox.element_type || '';
|
|
301
|
+
|
|
302
|
+
// Choose color based on element type
|
|
303
|
+
let rectClass = 'debug-rect';
|
|
304
|
+
if (name.includes('trace_') || elementType === 'line') {
|
|
305
|
+
rectClass = 'debug-rect-trace';
|
|
306
|
+
} else if (name.includes('legend')) {
|
|
307
|
+
rectClass = 'debug-rect-legend';
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Draw bbox rectangle (with offset for object-fit:contain letterboxing)
|
|
311
|
+
const x = bbox.x0 * scaleX + offsetX;
|
|
312
|
+
const y = bbox.y0 * scaleY + offsetY;
|
|
313
|
+
const w = (bbox.x1 - bbox.x0) * scaleX;
|
|
314
|
+
const h = (bbox.y1 - bbox.y0) * scaleY;
|
|
315
|
+
|
|
316
|
+
svg += `<rect class="${rectClass}" x="${x}" y="${y}" width="${w}" height="${h}"/>`;
|
|
317
|
+
|
|
318
|
+
// Draw short label (truncated for small panels)
|
|
319
|
+
const shortName = name.length > 15 ? name.substring(0, 12) + '...' : name;
|
|
320
|
+
svg += `<text class="debug-label" x="${x + 2}" y="${y + 8}" font-size="8">${shortName}</text>`;
|
|
321
|
+
|
|
322
|
+
// Draw path points if available (with offset)
|
|
323
|
+
if (hasPoints && bbox.points.length > 1) {
|
|
324
|
+
let pathD = `M ${bbox.points[0][0] * scaleX + offsetX} ${bbox.points[0][1] * scaleY + offsetY}`;
|
|
325
|
+
for (let i = 1; i < bbox.points.length; i++) {
|
|
326
|
+
const pt = bbox.points[i];
|
|
327
|
+
if (pt && pt.length >= 2) {
|
|
328
|
+
pathD += ` L ${pt[0] * scaleX + offsetX} ${pt[1] * scaleY + offsetY}`;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
svg += `<path class="debug-path" d="${pathD}" stroke-width="0.5"/>`;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return svg;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function togglePanelDebugMode() {
|
|
339
|
+
panelDebugMode = !panelDebugMode;
|
|
340
|
+
const btn = document.getElementById('panel-debug-toggle-btn');
|
|
341
|
+
if (btn) {
|
|
342
|
+
btn.classList.toggle('active', panelDebugMode);
|
|
343
|
+
btn.textContent = panelDebugMode ? 'Hide Panel Hit Areas' : 'Show Panel Hit Areas';
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Redraw all panel overlays
|
|
347
|
+
redrawAllPanelOverlays();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function redrawAllPanelOverlays() {
|
|
351
|
+
document.querySelectorAll('.panel-canvas-item').forEach((item, idx) => {
|
|
352
|
+
const panelName = item.dataset.panelName;
|
|
353
|
+
const overlay = item.querySelector('.panel-card-overlay');
|
|
354
|
+
const img = item.querySelector('img');
|
|
355
|
+
const panelCache = panelBboxesCache[panelName];
|
|
356
|
+
|
|
357
|
+
if (overlay && img && panelCache) {
|
|
358
|
+
const rect = img.getBoundingClientRect();
|
|
359
|
+
updatePanelOverlay(
|
|
360
|
+
overlay,
|
|
361
|
+
panelCache.bboxes,
|
|
362
|
+
panelCache.imgSize,
|
|
363
|
+
rect.width,
|
|
364
|
+
rect.height,
|
|
365
|
+
panelHoveredElement,
|
|
366
|
+
selectedElement,
|
|
367
|
+
img
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preview Management
|
|
3
|
+
* Handles preview loading, panel navigation, and grid view
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Initial Preview Loading
|
|
8
|
+
// ============================================================================
|
|
9
|
+
async function loadInitialPreview() {
|
|
10
|
+
setStatus('Loading preview...', false);
|
|
11
|
+
try {
|
|
12
|
+
const resp = await fetch('/preview');
|
|
13
|
+
const data = await resp.json();
|
|
14
|
+
|
|
15
|
+
console.log('=== PREVIEW DATA RECEIVED ===');
|
|
16
|
+
console.log('format:', data.format);
|
|
17
|
+
console.log('img_size:', data.img_size);
|
|
18
|
+
console.log('bboxes keys:', Object.keys(data.bboxes || {}));
|
|
19
|
+
|
|
20
|
+
const previewContainer = document.getElementById('preview-container');
|
|
21
|
+
const img = document.getElementById('preview');
|
|
22
|
+
|
|
23
|
+
if (data.format === 'svg' && data.svg) {
|
|
24
|
+
// Handle SVG: replace img with inline SVG
|
|
25
|
+
const svgWrapper = document.createElement('div');
|
|
26
|
+
svgWrapper.id = 'preview-svg-wrapper';
|
|
27
|
+
svgWrapper.innerHTML = data.svg;
|
|
28
|
+
svgWrapper.style.width = '100%';
|
|
29
|
+
svgWrapper.style.maxHeight = '70vh';
|
|
30
|
+
|
|
31
|
+
// Find the SVG element and set styles
|
|
32
|
+
const svgEl = svgWrapper.querySelector('svg');
|
|
33
|
+
if (svgEl) {
|
|
34
|
+
svgEl.style.width = '100%';
|
|
35
|
+
svgEl.style.height = 'auto';
|
|
36
|
+
svgEl.style.maxHeight = '70vh';
|
|
37
|
+
svgEl.id = 'preview'; // Keep same ID for event handlers
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
img.style.display = 'none';
|
|
41
|
+
const existingWrapper = document.getElementById('preview-svg-wrapper');
|
|
42
|
+
if (existingWrapper) existingWrapper.remove();
|
|
43
|
+
previewContainer.appendChild(svgWrapper);
|
|
44
|
+
} else if (data.image) {
|
|
45
|
+
// Handle PNG: show as base64 image
|
|
46
|
+
img.src = 'data:image/png;base64,' + data.image;
|
|
47
|
+
img.style.display = 'block';
|
|
48
|
+
const existingWrapper = document.getElementById('preview-svg-wrapper');
|
|
49
|
+
if (existingWrapper) existingWrapper.remove();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (data.bboxes) {
|
|
53
|
+
elementBboxes = data.bboxes;
|
|
54
|
+
originalBboxes = JSON.parse(JSON.stringify(data.bboxes)); // Deep copy
|
|
55
|
+
if (data.bboxes._meta) {
|
|
56
|
+
schemaMeta = data.bboxes._meta;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (data.img_size) {
|
|
60
|
+
imgSize = data.img_size;
|
|
61
|
+
originalImgSize = {...data.img_size}; // Copy
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
isShowingOriginalPreview = true;
|
|
65
|
+
updateOverlay();
|
|
66
|
+
setStatus('Preview loaded', false);
|
|
67
|
+
|
|
68
|
+
// Initialize hover system for the SVG if needed
|
|
69
|
+
if (data.format === 'svg') {
|
|
70
|
+
const svgWrapper = document.getElementById('preview-svg-wrapper');
|
|
71
|
+
if (svgWrapper) {
|
|
72
|
+
initHoverSystemForElement(svgWrapper.querySelector('svg'));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Draw debug bboxes if debug mode is on
|
|
77
|
+
if (debugMode) {
|
|
78
|
+
updateOverlay();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Handle multi-panel figz bundles
|
|
82
|
+
if (data.panel_info && data.panel_info.panels) {
|
|
83
|
+
panelData = data.panel_info;
|
|
84
|
+
currentPanelIndex = data.panel_info.current_index || 0;
|
|
85
|
+
console.log('Multi-panel figz detected:', panelData.panels.length, 'panels');
|
|
86
|
+
loadPanelGrid();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Start auto-update AFTER initial preview is loaded
|
|
90
|
+
setAutoUpdateInterval();
|
|
91
|
+
} catch (e) {
|
|
92
|
+
setStatus('Error loading preview: ' + e.message, true);
|
|
93
|
+
console.error('Preview load error:', e);
|
|
94
|
+
// Start auto-update even on error so the editor works
|
|
95
|
+
setAutoUpdateInterval();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ============================================================================
|
|
100
|
+
// Multi-Panel Grid Loading
|
|
101
|
+
// ============================================================================
|
|
102
|
+
async function loadPanelGrid() {
|
|
103
|
+
if (!panelData || panelData.panels.length <= 1) {
|
|
104
|
+
// Not a multi-panel bundle or only one panel
|
|
105
|
+
document.getElementById('panel-grid-section').style.display = 'none';
|
|
106
|
+
document.getElementById('preview-header').style.display = 'none';
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
console.log('Loading panel canvas for', panelData.panels.length, 'panels');
|
|
111
|
+
|
|
112
|
+
// Hide single-panel preview completely for multi-panel bundles (unified canvas only)
|
|
113
|
+
document.getElementById('preview-header').style.display = 'none';
|
|
114
|
+
const previewWrapper = document.querySelector('.preview-wrapper');
|
|
115
|
+
if (previewWrapper) {
|
|
116
|
+
previewWrapper.style.display = 'none';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Fetch all panel images with bboxes
|
|
120
|
+
try {
|
|
121
|
+
const resp = await fetch('/panels');
|
|
122
|
+
const data = await resp.json();
|
|
123
|
+
|
|
124
|
+
if (data.error) {
|
|
125
|
+
console.error('Panel canvas error:', data.error);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const canvasEl = document.getElementById('panel-canvas');
|
|
130
|
+
canvasEl.innerHTML = '';
|
|
131
|
+
|
|
132
|
+
// Use figz layout to position panels as unified canvas (matching export)
|
|
133
|
+
const hasLayout = data.layout && Object.keys(data.layout).length > 0;
|
|
134
|
+
|
|
135
|
+
// Calculate scale factor: convert mm to pixels
|
|
136
|
+
// Find total figure dimensions from layout
|
|
137
|
+
let maxX = 0, maxY = 0;
|
|
138
|
+
if (hasLayout) {
|
|
139
|
+
Object.values(data.layout).forEach(l => {
|
|
140
|
+
const right = (l.position?.x_mm || 0) + (l.size?.width_mm || 80);
|
|
141
|
+
const bottom = (l.position?.y_mm || 0) + (l.size?.height_mm || 50);
|
|
142
|
+
maxX = Math.max(maxX, right);
|
|
143
|
+
maxY = Math.max(maxY, bottom);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Scale to fit canvas (max width ~700px for good display)
|
|
148
|
+
const canvasMaxWidth = 700;
|
|
149
|
+
const scale = hasLayout && maxX > 0 ? canvasMaxWidth / maxX : 3; // ~3px per mm fallback
|
|
150
|
+
canvasScale = scale; // Store globally for drag conversions
|
|
151
|
+
|
|
152
|
+
// Reset layout tracking
|
|
153
|
+
panelLayoutMm = {};
|
|
154
|
+
layoutModified = false;
|
|
155
|
+
|
|
156
|
+
data.panels.forEach((panel, idx) => {
|
|
157
|
+
// Store bboxes and imgSize in cache for interactive hover/click
|
|
158
|
+
if (panel.bboxes && panel.img_size) {
|
|
159
|
+
panelBboxesCache[panel.name] = {
|
|
160
|
+
bboxes: panel.bboxes,
|
|
161
|
+
imgSize: panel.img_size
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Use figz layout for positioning (unified canvas like export)
|
|
166
|
+
let pos, posMm;
|
|
167
|
+
if (panel.layout && panel.layout.position && panel.layout.size) {
|
|
168
|
+
const x_mm = panel.layout.position.x_mm || 0;
|
|
169
|
+
const y_mm = panel.layout.position.y_mm || 0;
|
|
170
|
+
const width_mm = panel.layout.size.width_mm || 80;
|
|
171
|
+
const height_mm = panel.layout.size.height_mm || 50;
|
|
172
|
+
pos = {
|
|
173
|
+
x: x_mm * scale,
|
|
174
|
+
y: y_mm * scale,
|
|
175
|
+
width: width_mm * scale,
|
|
176
|
+
height: height_mm * scale,
|
|
177
|
+
};
|
|
178
|
+
posMm = { x_mm, y_mm, width_mm, height_mm };
|
|
179
|
+
} else {
|
|
180
|
+
// Fallback grid layout if no figz layout
|
|
181
|
+
const cols = Math.ceil(Math.sqrt(data.panels.length));
|
|
182
|
+
const baseWidth = 220, baseHeight = 180, padding = 15;
|
|
183
|
+
const col = idx % cols;
|
|
184
|
+
const row = Math.floor(idx / cols);
|
|
185
|
+
pos = {
|
|
186
|
+
x: padding + col * (baseWidth + padding),
|
|
187
|
+
y: padding + row * (baseHeight + padding),
|
|
188
|
+
width: baseWidth,
|
|
189
|
+
height: baseHeight,
|
|
190
|
+
};
|
|
191
|
+
// Convert to mm for fallback
|
|
192
|
+
posMm = {
|
|
193
|
+
x_mm: pos.x / scale,
|
|
194
|
+
y_mm: pos.y / scale,
|
|
195
|
+
width_mm: pos.width / scale,
|
|
196
|
+
height_mm: pos.height / scale,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
panelPositions[panel.name] = pos;
|
|
200
|
+
panelLayoutMm[panel.name] = posMm;
|
|
201
|
+
|
|
202
|
+
const item = document.createElement('div');
|
|
203
|
+
item.className = 'panel-canvas-item' + (idx === currentPanelIndex ? ' active' : '');
|
|
204
|
+
item.dataset.panelIndex = idx;
|
|
205
|
+
item.dataset.panelName = panel.name;
|
|
206
|
+
item.style.left = pos.x + 'px';
|
|
207
|
+
item.style.top = pos.y + 'px';
|
|
208
|
+
item.style.width = pos.width + 'px';
|
|
209
|
+
item.style.height = pos.height + 'px';
|
|
210
|
+
|
|
211
|
+
if (panel.image) {
|
|
212
|
+
item.innerHTML = `
|
|
213
|
+
<span class="panel-canvas-label">${panel.name}</span>
|
|
214
|
+
<span class="panel-position-indicator" id="pos-${panel.name}"></span>
|
|
215
|
+
<div class="panel-drag-handle" title="Drag to move panel">⋮⋮</div>
|
|
216
|
+
<div class="panel-card-container">
|
|
217
|
+
<img src="data:image/png;base64,${panel.image}" alt="Panel ${panel.name}">
|
|
218
|
+
<svg class="panel-card-overlay" id="panel-overlay-${idx}"></svg>
|
|
219
|
+
</div>
|
|
220
|
+
`;
|
|
221
|
+
} else {
|
|
222
|
+
item.innerHTML = `
|
|
223
|
+
<span class="panel-canvas-label">${panel.name}</span>
|
|
224
|
+
<span class="panel-position-indicator" id="pos-${panel.name}"></span>
|
|
225
|
+
<div class="panel-drag-handle" title="Drag to move panel">⋮⋮</div>
|
|
226
|
+
<div style="padding: 20px; color: var(--text-muted);">No preview</div>
|
|
227
|
+
`;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Add interactive event handlers (hover, click for element selection)
|
|
231
|
+
// Note: initCanvasItemInteraction already calls initPanelDrag internally
|
|
232
|
+
initCanvasItemInteraction(item, idx, panel.name);
|
|
233
|
+
|
|
234
|
+
canvasEl.appendChild(item);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Update canvas size to fit all panels (unified canvas)
|
|
238
|
+
const canvasHeight = Math.max(...Object.values(panelPositions).map(p => p.y + p.height)) + 10;
|
|
239
|
+
const canvasWidth = Math.max(...Object.values(panelPositions).map(p => p.x + p.width)) + 10;
|
|
240
|
+
canvasEl.style.minHeight = Math.max(400, canvasHeight) + 'px';
|
|
241
|
+
canvasEl.style.minWidth = canvasWidth + 'px';
|
|
242
|
+
|
|
243
|
+
// Update panel indicator
|
|
244
|
+
updatePanelIndicator();
|
|
245
|
+
|
|
246
|
+
// Show unified canvas for multi-panel figures
|
|
247
|
+
showingPanelGrid = true;
|
|
248
|
+
document.getElementById('panel-grid-section').style.display = 'block';
|
|
249
|
+
} catch (e) {
|
|
250
|
+
console.error('Error loading panels:', e);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ============================================================================
|
|
255
|
+
// Panel Navigation
|
|
256
|
+
// ============================================================================
|
|
257
|
+
function togglePanelGrid() {
|
|
258
|
+
showingPanelGrid = !showingPanelGrid;
|
|
259
|
+
if (showingPanelGrid) {
|
|
260
|
+
loadPanelGrid();
|
|
261
|
+
} else {
|
|
262
|
+
document.getElementById('panel-grid-section').style.display = 'none';
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function prevPanel() {
|
|
267
|
+
if (currentPanelIndex > 0) {
|
|
268
|
+
loadPanelForEditing(panelData.panels[currentPanelIndex - 1].name);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function nextPanel() {
|
|
273
|
+
if (currentPanelIndex < panelData.panels.length - 1) {
|
|
274
|
+
loadPanelForEditing(panelData.panels[currentPanelIndex + 1].name);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function updatePanelIndicator() {
|
|
279
|
+
if (!panelData) return;
|
|
280
|
+
|
|
281
|
+
const indicator = document.getElementById('panel-indicator');
|
|
282
|
+
const prevBtn = document.getElementById('btn-prev-panel');
|
|
283
|
+
const nextBtn = document.getElementById('btn-next-panel');
|
|
284
|
+
|
|
285
|
+
// Update indicator text (if elements exist)
|
|
286
|
+
if (indicator) {
|
|
287
|
+
indicator.textContent = `${currentPanelIndex + 1} / ${panelData.panels.length}`;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Update button states
|
|
291
|
+
if (prevBtn) prevBtn.disabled = currentPanelIndex === 0;
|
|
292
|
+
if (nextBtn) nextBtn.disabled = currentPanelIndex === panelData.panels.length - 1;
|
|
293
|
+
}
|