plexora 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plexora/__init__.py +129 -0
- plexora/__main__.py +15 -0
- plexora/_url.py +105 -0
- plexora/api/__init__.py +55 -0
- plexora/api/dataset.py +398 -0
- plexora/api/http.py +19 -0
- plexora/api/plugin.py +541 -0
- plexora/api/store.py +221 -0
- plexora/cli.py +777 -0
- plexora/client/dist/354_bundle.js +2 -0
- plexora/client/dist/354_bundle.js.LICENSE.txt +20 -0
- plexora/client/dist/418_bundle.js +2 -0
- plexora/client/dist/418_bundle.js.LICENSE.txt +1 -0
- plexora/client/dist/693b935cb1f907814be9c6199a68c217.svg +19 -0
- plexora/client/dist/713c134ff47cd328a5b711526f151082.svg +18 -0
- plexora/client/dist/770_bundle.js +1 -0
- plexora/client/dist/ff743f408972e0e96ca9153ed5ae4c83.svg +23 -0
- plexora/client/dist/vendor_bundle.js +2 -0
- plexora/client/dist/vendor_bundle.js.LICENSE.txt +308 -0
- plexora/client/external/openseadragon-bin-2.4.0/LICENSE.txt +28 -0
- plexora/client/external/openseadragon-bin-2.4.0/canvas-overlay-hd.js +140 -0
- plexora/client/external/openseadragon-bin-2.4.0/changelog.txt +501 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/.gitattributes +17 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/LICENSE.txt +116 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/Toolbar.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/fullpage.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/home.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/next.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/previous.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateleft.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateright.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/zoom.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/README.md +9 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-scalebar.js +562 -0
- plexora/client/src/css/import.css +474 -0
- plexora/client/src/css/main.css +903 -0
- plexora/client/src/css/openProject.css +363 -0
- plexora/client/src/css/quickView.css +166 -0
- plexora/client/src/css/tokens.css +51 -0
- plexora/client/src/css/viewer.css +1655 -0
- plexora/client/src/img/apple-touch-icon.png +0 -0
- plexora/client/src/img/favicon.ico +0 -0
- plexora/client/src/img/logo.ai +8892 -33
- plexora/client/src/img/logo.svg +1 -0
- plexora/client/src/img/logo_with_text.ai +9011 -34
- plexora/client/src/img/logo_with_text.svg +1 -0
- plexora/client/src/js/main.js +704 -0
- plexora/client/src/js/pluginRegistry.js +108 -0
- plexora/client/src/js/services/appStatus.js +286 -0
- plexora/client/src/js/services/browsePicker.js +73 -0
- plexora/client/src/js/services/dataLayer.js +400 -0
- plexora/client/src/js/services/datasetContext.js +143 -0
- plexora/client/src/js/services/glRenderer.js +143 -0
- plexora/client/src/js/services/importFormValidation.js +257 -0
- plexora/client/src/js/services/numericData.js +100 -0
- plexora/client/src/js/services/passVariablesToFrontend.js +20 -0
- plexora/client/src/js/services/simpleEventHandler.js +25 -0
- plexora/client/src/js/vendor.js +29 -0
- plexora/client/src/js/views/channelList.js +663 -0
- plexora/client/src/js/views/colorSwatchPicker.js +202 -0
- plexora/client/src/js/views/columnClassifier.js +146 -0
- plexora/client/src/js/views/coordinateField.js +133 -0
- plexora/client/src/js/views/dataSourceField.js +276 -0
- plexora/client/src/js/views/imageViewer.js +3306 -0
- plexora/client/src/js/views/miniMap.js +787 -0
- plexora/client/src/js/views/navbarControls.js +178 -0
- plexora/client/src/js/views/openProjectPage.js +239 -0
- plexora/client/src/js/views/projectEdit.js +335 -0
- plexora/client/src/js/views/quickViewLanding.js +136 -0
- plexora/client/src/js/views/rainbow.js +326 -0
- plexora/client/src/js/views/requirementsModal.js +572 -0
- plexora/client/src/js/views/rgbImageViewer.js +115 -0
- plexora/client/src/js/views/searchableSelect.js +411 -0
- plexora/client/src/js/views/segmentationProgress.js +122 -0
- plexora/client/src/js/views/toolLoader.js +747 -0
- plexora/client/src/js/views/viewerControls.js +689 -0
- plexora/client/src/js/views/viewerManager.js +434 -0
- plexora/client/src/js/views/viewerSidebar.js +1117 -0
- plexora/client/src/js/workers/tileDecoder.js +130 -0
- plexora/client/src/shaders/frag.glsl +528 -0
- plexora/client/src/shaders/vert.glsl +12 -0
- plexora/client/templates/base.html +212 -0
- plexora/client/templates/index.html +238 -0
- plexora/client/templates/open_project.html +96 -0
- plexora/client/templates/project_columns.html +86 -0
- plexora/client/templates/project_edit.html +196 -0
- plexora/client/templates/upload.html +118 -0
- plexora/connect.py +528 -0
- plexora/datasource.py +782 -0
- plexora/jupyter.py +406 -0
- plexora/notebook_env.py +232 -0
- plexora/paths.py +486 -0
- plexora/plugins/__init__.py +12 -0
- plexora/plugins/cell_explorer/__init__.py +100 -0
- plexora/plugins/cell_explorer/server/__init__.py +11 -0
- plexora/plugins/cell_explorer/server/routes.py +167 -0
- plexora/plugins/cell_explorer/server/state.py +281 -0
- plexora/plugins/cell_explorer/server/values.py +145 -0
- plexora/plugins/cell_explorer/server/variables.py +367 -0
- plexora/plugins/cell_explorer/static/cellExplorerApi.js +158 -0
- plexora/plugins/cell_explorer/static/cellExplorerColors.js +316 -0
- plexora/plugins/cell_explorer/static/cellExplorerContinuous.js +403 -0
- plexora/plugins/cell_explorer/static/cellExplorerFigureBridge.js +133 -0
- plexora/plugins/cell_explorer/static/cellExplorerLegend.js +188 -0
- plexora/plugins/cell_explorer/static/cellExplorerRoiBridge.js +822 -0
- plexora/plugins/cell_explorer/static/cellExplorerSidebarController.js +826 -0
- plexora/plugins/cell_explorer/static/cellExplorerState.js +372 -0
- plexora/plugins/cell_explorer/static/cell_explorer.css +752 -0
- plexora/plugins/cell_explorer/templates/cell_explorer/panel.html +137 -0
- plexora/plugins/figure_builder/__init__.py +125 -0
- plexora/plugins/figure_builder/server/__init__.py +7 -0
- plexora/plugins/figure_builder/server/compose.py +365 -0
- plexora/plugins/figure_builder/server/export.py +784 -0
- plexora/plugins/figure_builder/server/export_jobs.py +148 -0
- plexora/plugins/figure_builder/server/operations.py +556 -0
- plexora/plugins/figure_builder/server/pixels.py +142 -0
- plexora/plugins/figure_builder/server/provenance.py +174 -0
- plexora/plugins/figure_builder/server/render.py +278 -0
- plexora/plugins/figure_builder/server/repository.py +701 -0
- plexora/plugins/figure_builder/server/routes.py +594 -0
- plexora/plugins/figure_builder/server/schema.py +1023 -0
- plexora/plugins/figure_builder/server/sources.py +120 -0
- plexora/plugins/figure_builder/server/textmetrics.py +155 -0
- plexora/plugins/figure_builder/static/figureActions.js +363 -0
- plexora/plugins/figure_builder/static/figureBuilderApi.js +255 -0
- plexora/plugins/figure_builder/static/figureCanvas.js +2362 -0
- plexora/plugins/figure_builder/static/figureCaptureBoxes.js +369 -0
- plexora/plugins/figure_builder/static/figureCaptureDock.js +466 -0
- plexora/plugins/figure_builder/static/figureCaptureTool.js +1034 -0
- plexora/plugins/figure_builder/static/figureConfirm.js +136 -0
- plexora/plugins/figure_builder/static/figureContextBar.js +1193 -0
- plexora/plugins/figure_builder/static/figureContextMenu.js +253 -0
- plexora/plugins/figure_builder/static/figureDocumentState.js +275 -0
- plexora/plugins/figure_builder/static/figureExportUi.js +174 -0
- plexora/plugins/figure_builder/static/figureLibrary.js +247 -0
- plexora/plugins/figure_builder/static/figureQuickEdit.js +628 -0
- plexora/plugins/figure_builder/static/figureRichText.js +634 -0
- plexora/plugins/figure_builder/static/figureSceneSnapshot.js +361 -0
- plexora/plugins/figure_builder/static/figureSchema.js +218 -0
- plexora/plugins/figure_builder/static/figureSidebarController.js +1159 -0
- plexora/plugins/figure_builder/static/figureTextEditor.js +503 -0
- plexora/plugins/figure_builder/static/figureTextPanel.js +491 -0
- plexora/plugins/figure_builder/static/figureViewOptions.js +273 -0
- plexora/plugins/figure_builder/static/figureWorkspace.js +2030 -0
- plexora/plugins/figure_builder/static/figure_builder.css +3292 -0
- plexora/plugins/figure_builder/templates/figure_builder/library.html +74 -0
- plexora/plugins/figure_builder/templates/figure_builder/workspace.html +27 -0
- plexora/plugins/figure_builder/templates/figure_builder/workspace_body.html +458 -0
- plexora/plugins/gating/__init__.py +76 -0
- plexora/plugins/gating/server/__init__.py +0 -0
- plexora/plugins/gating/server/anndata_gates.py +367 -0
- plexora/plugins/gating/server/database.py +16 -0
- plexora/plugins/gating/server/model.py +320 -0
- plexora/plugins/gating/server/routes.py +235 -0
- plexora/plugins/gating/static/csvGatingList.js +947 -0
- plexora/plugins/gating/static/gating.css +289 -0
- plexora/plugins/gating/static/gatingApi.js +221 -0
- plexora/plugins/gating/static/gatingSidebarController.js +513 -0
- plexora/plugins/gating/templates/gating/legacy.html +26 -0
- plexora/plugins/gating/templates/gating/panel.html +69 -0
- plexora/plugins/roi/__init__.py +95 -0
- plexora/plugins/roi/server/__init__.py +8 -0
- plexora/plugins/roi/server/adapters.py +655 -0
- plexora/plugins/roi/server/geojson.py +267 -0
- plexora/plugins/roi/server/geometry.py +181 -0
- plexora/plugins/roi/server/mapping.py +169 -0
- plexora/plugins/roi/server/operations.py +293 -0
- plexora/plugins/roi/server/repository.py +215 -0
- plexora/plugins/roi/server/routes.py +409 -0
- plexora/plugins/roi/server/schema.py +323 -0
- plexora/plugins/roi/static/roi.css +583 -0
- plexora/plugins/roi/static/roiApi.js +177 -0
- plexora/plugins/roi/static/roiFigureBridge.js +107 -0
- plexora/plugins/roi/static/roiGeometry.js +350 -0
- plexora/plugins/roi/static/roiRenderer.js +278 -0
- plexora/plugins/roi/static/roiSidebarController.js +1038 -0
- plexora/plugins/roi/static/roiState.js +518 -0
- plexora/plugins/roi/static/roiTools.js +993 -0
- plexora/plugins/roi/templates/roi/panel.html +240 -0
- plexora/proxy.py +57 -0
- plexora/server/models/adapters/__init__.py +94 -0
- plexora/server/models/adapters/anndata_adapter.py +388 -0
- plexora/server/models/adapters/base.py +93 -0
- plexora/server/models/adapters/classify.py +192 -0
- plexora/server/models/adapters/csv_adapter.py +105 -0
- plexora/server/models/adapters/inspection.py +265 -0
- plexora/server/models/adapters/spatialdata_adapter.py +248 -0
- plexora/server/models/centroid_tiles.py +305 -0
- plexora/server/models/data_model.py +1659 -0
- plexora/server/models/database_model.py +169 -0
- plexora/server/models/project.py +1270 -0
- plexora/server/plugins.py +199 -0
- plexora/server/routes/browse_routes.py +41 -0
- plexora/server/routes/data_routes.py +294 -0
- plexora/server/routes/import_routes.py +603 -0
- plexora/server/routes/page_routes.py +139 -0
- plexora/server/routes/project_routes.py +438 -0
- plexora/server/routes/quick_view_routes.py +62 -0
- plexora/server/routes/system_routes.py +39 -0
- plexora/server/routes/tool_routes.py +418 -0
- plexora/server/utils/addHEColumns.py +11 -0
- plexora/server/utils/fast_png.py +97 -0
- plexora/server/utils/fullConversion.py +50 -0
- plexora/server/utils/native_dialog.py +168 -0
- plexora/server/utils/pre_normalization.py +47 -0
- plexora/server/utils/segmentation_pyramid.py +598 -0
- plexora/server/utils/smallestenclosingcircle.py +126 -0
- plexora/server/utils/tiffsurgeon.py +372 -0
- plexora/server_cli.py +48 -0
- plexora/test.py +0 -0
- plexora-0.0.1.dist-info/METADATA +341 -0
- plexora-0.0.1.dist-info/RECORD +248 -0
- plexora-0.0.1.dist-info/WHEEL +5 -0
- plexora-0.0.1.dist-info/entry_points.txt +12 -0
- plexora-0.0.1.dist-info/licenses/LICENSE +207 -0
- plexora-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<head>
|
|
3
|
+
{% block title %}{% endblock %}
|
|
4
|
+
<meta content="utf-8" http-equiv="encoding">
|
|
5
|
+
<!-- Favicon -->
|
|
6
|
+
<link rel="icon" type="image/svg+xml" href="{{ data.base_url }}/client/src/img/logo.svg">
|
|
7
|
+
<link rel="alternate icon" href="{{ data.base_url }}/client/src/img/favicon.ico">
|
|
8
|
+
<link rel="apple-touch-icon" href="{{ data.base_url }}/client/src/img/apple-touch-icon.png">
|
|
9
|
+
<!-- Styles -->
|
|
10
|
+
<link rel="stylesheet" href="{{ data.base_url }}/client/src/css/tokens.css?v=20260814_unified_navbar">
|
|
11
|
+
<link rel="stylesheet" href="{{ data.base_url }}/client/src/css/main.css?v=20260824_icon_names">
|
|
12
|
+
{% block style %}{% endblock %}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
<!-- Flask variables -->
|
|
16
|
+
<script src="{{ data.base_url }}/client/src/js/services/passVariablesToFrontend.js?v=20260811_selection_provider" type="text/javascript"></script>
|
|
17
|
+
<script type="text/javascript">
|
|
18
|
+
//<!--@formatter:off-->
|
|
19
|
+
window.flaskVariables = passVariablesToFrontend({{data|tojson}});
|
|
20
|
+
window.PLEXORA_BASE_URL = window.flaskVariables.base_url || "";
|
|
21
|
+
//<!--@formatter:on-->
|
|
22
|
+
</script>
|
|
23
|
+
<!-- App-wide status indicator. Loaded here, before everything else and not
|
|
24
|
+
deferred, because it wraps window.fetch to detect a dead/erroring
|
|
25
|
+
server and must be installed before any script issues a request. -->
|
|
26
|
+
<script src="{{ data.base_url }}/client/src/js/services/appStatus.js?v=20260816_app_status" type="text/javascript"></script>
|
|
27
|
+
|
|
28
|
+
<!-- Webpack -->
|
|
29
|
+
<script src="{{ data.base_url }}/client/dist/vendor_bundle.js?v=20260824_feature_source"></script>
|
|
30
|
+
|
|
31
|
+
<!-- OpenSeadragon imports-->
|
|
32
|
+
<script src="{{ data.base_url }}/client/external/openseadragon-bin-2.4.0/canvas-overlay-hd.js"></script>
|
|
33
|
+
<script src="{{ data.base_url }}/client/external/openseadragon-bin-2.4.0/openseadragon-scalebar.js"></script>
|
|
34
|
+
|
|
35
|
+
<!-- Scripts -->
|
|
36
|
+
<script src="{{ data.base_url }}/client/src/js/pluginRegistry.js?v=20260821_plugin_layers"></script>
|
|
37
|
+
<script src="{{ data.base_url }}/client/src/js/services/datasetContext.js?v=20260820_csv_markers_log1p"></script>
|
|
38
|
+
<script src="{{ data.base_url }}/client/src/js/views/rainbow.js?v=20260811_selection_provider"></script>
|
|
39
|
+
<script src="{{ data.base_url }}/client/src/js/services/dataLayer.js?v=20260819_feature_source"></script>
|
|
40
|
+
<script src="{{ data.base_url }}/client/src/js/services/numericData.js?v=20260819_feature_source"></script>
|
|
41
|
+
<script src="{{ data.base_url }}/client/src/js/views/channelList.js?v=20260811_selection_provider"></script>
|
|
42
|
+
<script src="{{ data.base_url }}/client/src/js/views/imageViewer.js?v=20260821_minimap2"></script>
|
|
43
|
+
<script src="{{ data.base_url }}/client/src/js/views/miniMap.js?v=20260821_minimap2"></script>
|
|
44
|
+
<script src="{{ data.base_url }}/client/src/js/views/rgbImageViewer.js?v=20260814_unified_navbar"></script>
|
|
45
|
+
<script src="{{ data.base_url }}/client/src/js/views/searchableSelect.js?v=20260821_cell_panel_visibility"></script>
|
|
46
|
+
<script src="{{ data.base_url }}/client/src/js/views/colorSwatchPicker.js?v=20260821_cell_panel_controls"></script>
|
|
47
|
+
<script src="{{ data.base_url }}/client/src/js/views/viewerControls.js?v=20260821_roi_composition_v2"></script>
|
|
48
|
+
<script src="{{ data.base_url }}/client/src/js/views/viewerSidebar.js?v=20260824_icon_names"></script>
|
|
49
|
+
<script src="{{ data.base_url }}/client/src/js/services/simpleEventHandler.js?v=20260811_selection_provider"></script>
|
|
50
|
+
<script src="{{ data.base_url }}/client/src/js/views/navbarControls.js?v=20260821_plugin_layers" defer></script>
|
|
51
|
+
<script src="{{ data.base_url }}/client/src/js/services/browsePicker.js?v=20260819_feature_source" defer></script>
|
|
52
|
+
<script src="{{ data.base_url }}/client/src/js/views/columnClassifier.js?v=20260820_csv_markers_log1p" defer></script>
|
|
53
|
+
<script src="{{ data.base_url }}/client/src/js/views/coordinateField.js?v=20260819_ask_cell_id" defer></script>
|
|
54
|
+
<script src="{{ data.base_url }}/client/src/js/views/dataSourceField.js?v=20260819_data_table_pick" defer></script>
|
|
55
|
+
<script src="{{ data.base_url }}/client/src/js/views/requirementsModal.js?v=20260820_optional_offer" defer></script>
|
|
56
|
+
<script src="{{ data.base_url }}/client/src/js/views/toolLoader.js?v=20260824_icon_names" defer></script>
|
|
57
|
+
|
|
58
|
+
{# Assets of the plugin this page opened with, if any. Declared by the
|
|
59
|
+
plugin (Plugin.scripts/styles) and cache-busted from its version, so
|
|
60
|
+
core names no plugin and the eager path here cannot drift out of sync
|
|
61
|
+
with the lazy path in tool_routes.py -- which is exactly what happened
|
|
62
|
+
when these two <script> tags were maintained by hand: they were once
|
|
63
|
+
omitted entirely, leaving the panel rendered but inert. #}
|
|
64
|
+
{% for url in data.active_tool_styles %}
|
|
65
|
+
<link rel="stylesheet" href="{{ url }}">
|
|
66
|
+
{% endfor %}
|
|
67
|
+
{% for url in data.active_tool_scripts %}
|
|
68
|
+
<script src="{{ url }}"></script>
|
|
69
|
+
{% endfor %}
|
|
70
|
+
|
|
71
|
+
</head>
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
<body>
|
|
75
|
+
<nav id="topBar" class="navbar navbar-expand-sm navbar-dark">
|
|
76
|
+
<a id="title" class="navbar-brand mb-0 h1" href="{{ data.base_url }}/">
|
|
77
|
+
<img src="{{ data.base_url }}/client/src/img/logo.svg" alt="" id="navbar-logo">
|
|
78
|
+
Plexora
|
|
79
|
+
</a>
|
|
80
|
+
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
|
81
|
+
<ul class="navbar-nav me-auto">
|
|
82
|
+
|
|
83
|
+
<li class="nav-item dropdown">
|
|
84
|
+
<span class="nav-link dropdown-toggle" id="navbarFileDropdown" role="button" data-bs-toggle="dropdown"
|
|
85
|
+
aria-haspopup="true" aria-expanded="false">
|
|
86
|
+
File
|
|
87
|
+
</span>
|
|
88
|
+
<div class="dropdown-menu" aria-labelledby="navbarFileDropdown">
|
|
89
|
+
<a class="dropdown-item" href="{{ data.base_url }}/upload_page">Create Project…</a>
|
|
90
|
+
<a class="dropdown-item" href="{{ data.base_url }}/open_project">Open Project…</a>
|
|
91
|
+
{# Ways in that an installed plugin asked for. Data, not
|
|
92
|
+
markup: the plugin supplies a label and a path inside its
|
|
93
|
+
own namespace, and this renders it with core's classes,
|
|
94
|
+
so contributing an entry cannot restyle or script the
|
|
95
|
+
menu. Renders nothing on a core-only build, which is what
|
|
96
|
+
keeps this page byte-identical without plugins. #}
|
|
97
|
+
{% for item in data.plugin_nav_items if item.menu == 'file' %}
|
|
98
|
+
<a class="dropdown-item" id="{{ item.id }}" href="{{ item.href }}">{{ item.label }}</a>
|
|
99
|
+
{% endfor %}
|
|
100
|
+
<div class="dropdown-divider"></div>
|
|
101
|
+
<a class="dropdown-item" id="nav_add_image" href="#">Add Image…</a>
|
|
102
|
+
<div class="dropdown-divider"></div>
|
|
103
|
+
<div class="dropdown-item nav-submenu-parent{% if not data.datasource %} disabled{% endif %}"
|
|
104
|
+
id="nav_export_menu" tabindex="0">
|
|
105
|
+
<span>Export Image</span>
|
|
106
|
+
<span class="fas fa-chevron-right nav-submenu-caret"></span>
|
|
107
|
+
<div class="dropdown-menu nav-submenu">
|
|
108
|
+
<button type="button" id="nav_export_image_png" class="dropdown-item"
|
|
109
|
+
{% if not data.datasource %}disabled{% endif %}>PNG…</button>
|
|
110
|
+
<button type="button" id="nav_export_image_pdf" class="dropdown-item"
|
|
111
|
+
{% if not data.datasource %}disabled{% endif %}>PDF…</button>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
{# Quit ends the server process. In notebook/hosted mode
|
|
115
|
+
that process belongs to the kernel or the hub, not to the
|
|
116
|
+
person clicking, so the button is not offered -- and
|
|
117
|
+
/shutdown refuses it there too. #}
|
|
118
|
+
{% if not data.notebook_mode %}
|
|
119
|
+
<div class="dropdown-divider"></div>
|
|
120
|
+
<button type="button" id="nav_quit" class="dropdown-item text-danger">Quit</button>
|
|
121
|
+
{% endif %}
|
|
122
|
+
</div>
|
|
123
|
+
</li>
|
|
124
|
+
|
|
125
|
+
<li class="nav-item dropdown">
|
|
126
|
+
<span class="nav-link dropdown-toggle" id="navbarEditDropdown" role="button" data-bs-toggle="dropdown"
|
|
127
|
+
data-bs-auto-close="outside" aria-haspopup="true" aria-expanded="false">
|
|
128
|
+
Edit
|
|
129
|
+
</span>
|
|
130
|
+
<div class="dropdown-menu" aria-labelledby="navbarEditDropdown">
|
|
131
|
+
<span id="nav_undo" class="dropdown-item disabled" aria-disabled="true">Undo</span>
|
|
132
|
+
<span id="nav_redo" class="dropdown-item disabled" aria-disabled="true">Redo</span>
|
|
133
|
+
</div>
|
|
134
|
+
</li>
|
|
135
|
+
|
|
136
|
+
{% if data.datasource and data.image_kind != 'rgb' and data.available_tools %}
|
|
137
|
+
<li class="nav-item dropdown">
|
|
138
|
+
<span class="nav-link dropdown-toggle" id="navbarToolsDropdown" role="button"
|
|
139
|
+
data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
140
|
+
Tools
|
|
141
|
+
</span>
|
|
142
|
+
<div class="dropdown-menu" aria-labelledby="navbarToolsDropdown">
|
|
143
|
+
{% for tool in data.available_tools %}
|
|
144
|
+
<a class="dropdown-item" data-tool="{{ tool.name }}"
|
|
145
|
+
href="{{ data.base_url }}/{{ data.datasource }}/tools/{{ tool.name }}">{{ tool.label }}</a>
|
|
146
|
+
{% endfor %}
|
|
147
|
+
</div>
|
|
148
|
+
</li>
|
|
149
|
+
{% endif %}
|
|
150
|
+
|
|
151
|
+
<li class="nav-item dropdown">
|
|
152
|
+
<span class="nav-link dropdown-toggle" id="navbarViewDropdown" role="button" data-bs-toggle="dropdown"
|
|
153
|
+
data-bs-auto-close="outside" aria-haspopup="true" aria-expanded="false">
|
|
154
|
+
View
|
|
155
|
+
</span>
|
|
156
|
+
<div class="dropdown-menu" aria-labelledby="navbarViewDropdown">
|
|
157
|
+
<label class="dropdown-item nav-check-item">
|
|
158
|
+
<input type="checkbox" id="nav_toggle_sidebar" {% if not data.datasource %}disabled{% endif %}>
|
|
159
|
+
Show Sidebar
|
|
160
|
+
</label>
|
|
161
|
+
<label class="dropdown-item nav-check-item">
|
|
162
|
+
<input type="checkbox" id="nav_toggle_scalebar" checked
|
|
163
|
+
{% if not data.datasource or data.image_kind == 'rgb' %}disabled{% endif %}>
|
|
164
|
+
Show Scalebar
|
|
165
|
+
</label>
|
|
166
|
+
<div class="dropdown-divider"></div>
|
|
167
|
+
{# Mirrors the sidebar's Cells control. Radios rather than
|
|
168
|
+
checkboxes because the four are one choice -- the pair of
|
|
169
|
+
checkboxes this replaced let the menu show a combination
|
|
170
|
+
the viewer cannot draw. Which of them are usable depends
|
|
171
|
+
on the project and is settled client-side, where the
|
|
172
|
+
config is (navbarControls.js). #}
|
|
173
|
+
<label class="dropdown-item nav-check-item">
|
|
174
|
+
<input type="radio" name="nav_cell_mode" id="nav_cell_mode_none" value="none" checked
|
|
175
|
+
{% if not data.datasource or data.image_kind == 'rgb' %}disabled{% endif %}>
|
|
176
|
+
No Cells
|
|
177
|
+
</label>
|
|
178
|
+
<label class="dropdown-item nav-check-item">
|
|
179
|
+
<input type="radio" name="nav_cell_mode" id="nav_cell_mode_centroids" value="centroids"
|
|
180
|
+
{% if not data.datasource or data.image_kind == 'rgb' %}disabled{% endif %}>
|
|
181
|
+
Show Centroids
|
|
182
|
+
</label>
|
|
183
|
+
<label class="dropdown-item nav-check-item">
|
|
184
|
+
<input type="radio" name="nav_cell_mode" id="nav_cell_mode_outlines" value="outlines"
|
|
185
|
+
{% if not data.datasource or data.image_kind == 'rgb' %}disabled{% endif %}>
|
|
186
|
+
Show Outlines
|
|
187
|
+
</label>
|
|
188
|
+
<label class="dropdown-item nav-check-item">
|
|
189
|
+
<input type="radio" name="nav_cell_mode" id="nav_cell_mode_filled" value="filled"
|
|
190
|
+
{% if not data.datasource or data.image_kind == 'rgb' %}disabled{% endif %}>
|
|
191
|
+
Fill Cells
|
|
192
|
+
</label>
|
|
193
|
+
<label class="dropdown-item nav-check-item"
|
|
194
|
+
data-tooltip="Reloads the image in high resolution -- may be slower.">
|
|
195
|
+
<input type="checkbox" id="nav_toggle_hd"
|
|
196
|
+
{% if not data.datasource or data.image_kind == 'rgb' %}disabled{% endif %}>
|
|
197
|
+
HD Mode
|
|
198
|
+
</label>
|
|
199
|
+
</div>
|
|
200
|
+
</li>
|
|
201
|
+
|
|
202
|
+
</ul>
|
|
203
|
+
</div>
|
|
204
|
+
<!-- Sits outside .navbar-collapse so it stays visible when the navbar
|
|
205
|
+
collapses. Filled in by appStatus.js. -->
|
|
206
|
+
<div id="app_status"></div>
|
|
207
|
+
</nav>
|
|
208
|
+
|
|
209
|
+
{% block content %}{% endblock %}
|
|
210
|
+
|
|
211
|
+
</body>
|
|
212
|
+
</html>
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
{% extends 'base.html' %}
|
|
2
|
+
|
|
3
|
+
{% block style %}
|
|
4
|
+
<link rel="stylesheet" href="{{ data.base_url }}/client/src/css/viewer.css?v=20260823_viewer_refactor">
|
|
5
|
+
<link rel="stylesheet" href="{{ data.base_url }}/client/src/css/quickView.css?v=20260815_gating_fixes">
|
|
6
|
+
{% endblock %}
|
|
7
|
+
|
|
8
|
+
{% block content %}
|
|
9
|
+
|
|
10
|
+
{% if data.datasource %}
|
|
11
|
+
<div id="container" class="container">
|
|
12
|
+
<div id="bodyDiv" class="viewer-shell">
|
|
13
|
+
<aside id="viewer_sidebar" class="viewer-sidebar">
|
|
14
|
+
<div class="sidebar-header">
|
|
15
|
+
<div>
|
|
16
|
+
<span class="sidebar-kicker">Plexora</span>
|
|
17
|
+
<h1>Viewer</h1>
|
|
18
|
+
</div>
|
|
19
|
+
<button id="sidebar_collapse_button" class="icon-button" title="Collapse controls" type="button">
|
|
20
|
+
<span class="fas fa-chevron-left"></span>
|
|
21
|
+
</button>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
{% if data.image_kind != 'rgb' %}
|
|
25
|
+
{# Foldable, like a plugin's card, but fixed in place: this is core's
|
|
26
|
+
own section, so it has no grip and no remove button. Only the
|
|
27
|
+
chevron the plugin cards also carry, because a long channel list
|
|
28
|
+
is the thing that pushes the plugin stack off the bottom of the
|
|
29
|
+
sidebar. The whole title row is the toggle -- a 24px chevron is a
|
|
30
|
+
small target for something the user reaches for repeatedly. #}
|
|
31
|
+
<section class="sidebar-section" id="image_channel_section">
|
|
32
|
+
<div class="section-heading">
|
|
33
|
+
<h2>
|
|
34
|
+
<button id="image_channel_collapse" class="section-collapse" type="button"
|
|
35
|
+
aria-expanded="true" aria-controls="image_channel_body"
|
|
36
|
+
title="Collapse or expand image channels">
|
|
37
|
+
<span class="fas fa-chevron-down section-collapse-chevron"></span>
|
|
38
|
+
<span class="fas fa-layer-group section-heading-icon"></span>
|
|
39
|
+
Image Channels
|
|
40
|
+
</button>
|
|
41
|
+
</h2>
|
|
42
|
+
<div class="section-actions">
|
|
43
|
+
<button id="channels_upload_icon" class="icon-button" title="Rename channels from a CSV (one name per row, in channel order)" type="button">
|
|
44
|
+
<span class="fas fa-file-arrow-up"></span>
|
|
45
|
+
</button>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
{# Everything the chevron folds away. A wrapper rather than
|
|
49
|
+
hiding the section's children one by one, so the heading --
|
|
50
|
+
and the CSV-rename button that sits in it -- stay reachable
|
|
51
|
+
while folded. #}
|
|
52
|
+
<div id="image_channel_body" class="section-body">
|
|
53
|
+
<form id="channels_arrow-upload-form">
|
|
54
|
+
<input id="channels-upload-from-arrow" class="hidden" type="file">
|
|
55
|
+
<input type="hidden" id="channels_upload-arrow-datasource" name="channels_upload-arrow-datasource">
|
|
56
|
+
</form>
|
|
57
|
+
<div class="selected-count">
|
|
58
|
+
<span id="num-selected-channels">0</span>
|
|
59
|
+
<span>active</span>
|
|
60
|
+
<span class="selected-count-max">· <span id="max-channels">15</span> max</span>
|
|
61
|
+
</div>
|
|
62
|
+
<div id="channel_slot_list" class="channel-slot-list"></div>
|
|
63
|
+
<button id="add_channel_button" class="sidebar-action secondary" type="button">
|
|
64
|
+
<span class="fas fa-plus"></span>
|
|
65
|
+
Add Channel
|
|
66
|
+
</button>
|
|
67
|
+
</div>
|
|
68
|
+
</section>
|
|
69
|
+
{% endif %}
|
|
70
|
+
|
|
71
|
+
{# Plugin sidebar mount point: rendered server-side on a direct or
|
|
72
|
+
bookmarked ?tool=<name> load; otherwise left empty and filled
|
|
73
|
+
client-side by toolLoader.js on first open, via the
|
|
74
|
+
/tools/<tool>/panel endpoint, so opening or closing mid-session
|
|
75
|
+
never requires a page navigation.
|
|
76
|
+
|
|
77
|
+
Which template lands here comes from the active plugin's own
|
|
78
|
+
Plugin.panels mapping, keyed by this slot id. Applicability is
|
|
79
|
+
already decided in page_routes (a tool is only active if this
|
|
80
|
+
datasource satisfies what the plugin declared it needs), so
|
|
81
|
+
there is nothing image-kind-specific to test here. #}
|
|
82
|
+
<div id="tool_panel_slot" data-tool-mount="{{ data.active_tool }}"{% if not data.active_tool_panels.tool_panel_slot %} class="tool-panel-hidden"{% endif %}>
|
|
83
|
+
{% if data.active_tool_panels.tool_panel_slot %}
|
|
84
|
+
{% include data.active_tool_panels.tool_panel_slot %}
|
|
85
|
+
{% endif %}
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
{% if data.image_kind != 'rgb' %}
|
|
89
|
+
<section class="sidebar-section compact">
|
|
90
|
+
{# How cells are drawn, as one choice rather than two toggles.
|
|
91
|
+
Exactly one representation is active at a time -- there is no
|
|
92
|
+
meaningful "filled AND outlined" state, and the two checkboxes
|
|
93
|
+
this replaced could express one ("Outlines" plus "Centroids")
|
|
94
|
+
that the renderer then had to arbitrate.
|
|
95
|
+
|
|
96
|
+
Geometry is core's, deliberately: a plugin that colours cells
|
|
97
|
+
supplies the colours and nothing else, so every such plugin
|
|
98
|
+
gets these three representations without shipping three
|
|
99
|
+
renderers of its own.
|
|
100
|
+
|
|
101
|
+
Every option but None starts disabled and viewerControls.init()
|
|
102
|
+
enables what this project can actually draw. Rendering them
|
|
103
|
+
live and switching them off a moment later would offer, briefly,
|
|
104
|
+
a Filled button that cannot fill.
|
|
105
|
+
|
|
106
|
+
One line, with HD. The "Cells" caption this row used to carry
|
|
107
|
+
is on the group's aria-label instead: four buttons reading
|
|
108
|
+
None/Centroids/Outlines/Filled do not need a word above them
|
|
109
|
+
to say what they are about, and the space it was taking is the
|
|
110
|
+
space HD now sits in. #}
|
|
111
|
+
<div class="cell-display-row">
|
|
112
|
+
<div class="cell-mode-control" id="cell_display_control" role="radiogroup" aria-label="Cells">
|
|
113
|
+
<div class="cell-mode-options">
|
|
114
|
+
<button type="button" id="cell_mode_none" class="cell-mode-option is-active"
|
|
115
|
+
data-cell-mode="none" role="radio" aria-checked="true">None</button>
|
|
116
|
+
<button type="button" id="cell_mode_centroids" class="cell-mode-option"
|
|
117
|
+
data-cell-mode="centroids" role="radio" aria-checked="false" disabled
|
|
118
|
+
title="Needs cell coordinates">Centroids</button>
|
|
119
|
+
<button type="button" id="cell_mode_outlines" class="cell-mode-option"
|
|
120
|
+
data-cell-mode="outlines" role="radio" aria-checked="false" disabled
|
|
121
|
+
title="Needs a segmentation mask">Outlines</button>
|
|
122
|
+
<button type="button" id="cell_mode_filled" class="cell-mode-option"
|
|
123
|
+
data-cell-mode="filled" role="radio" aria-checked="false" disabled
|
|
124
|
+
title="Needs a segmentation mask">Filled</button>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
{# HD reads as one more button in the row rather than a
|
|
128
|
+
stray checkbox beside a segmented control. The input is
|
|
129
|
+
still a real checkbox -- everything that reads
|
|
130
|
+
#viewer_controls_hd.checked is unchanged -- it is just
|
|
131
|
+
drawn as the <span> next to it, which is why the span is
|
|
132
|
+
a sibling and not a wrapper. #}
|
|
133
|
+
<div id="viewer_controls_panel">
|
|
134
|
+
<label class="cell-toggle" data-tooltip="Reloads the image in high resolution -- may be slower.">
|
|
135
|
+
<input id="viewer_controls_hd" type="checkbox">
|
|
136
|
+
<span>HD</span>
|
|
137
|
+
</label>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
{# Only while centroids are what is drawn. Size is meaningless
|
|
142
|
+
for the other three, and a control that is present but inert
|
|
143
|
+
reads as broken rather than as not applicable. #}
|
|
144
|
+
<div id="cell_point_size_row" class="cell-point-size" hidden>
|
|
145
|
+
<label class="control-label" for="cell_point_size">Point size</label>
|
|
146
|
+
<input id="cell_point_size" type="range"
|
|
147
|
+
min="0.4" max="3" step="0.1" value="1">
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
{# How strongly the ACTIVE plugin's layer sits over what is under
|
|
151
|
+
it -- the tissue, and any layer stacked below it. Core's and
|
|
152
|
+
shared, for the same reason the mode buttons are: compositing
|
|
153
|
+
belongs to the viewer, so every plugin that colours cells gets
|
|
154
|
+
this without shipping a slider of its own. Hidden until a
|
|
155
|
+
plugin registers a layer; there is nothing to fade a plain
|
|
156
|
+
white cell layer against. #}
|
|
157
|
+
<div id="cell_layer_opacity_row" class="cell-layer-opacity" hidden>
|
|
158
|
+
<label class="control-label" for="cell_layer_opacity">Opacity</label>
|
|
159
|
+
<input id="cell_layer_opacity" type="range"
|
|
160
|
+
min="0" max="100" step="1" value="70">
|
|
161
|
+
<span id="cell_layer_opacity_value" class="cell-layer-opacity-value">70%</span>
|
|
162
|
+
</div>
|
|
163
|
+
</section>
|
|
164
|
+
{% endif %}
|
|
165
|
+
</aside>
|
|
166
|
+
|
|
167
|
+
<div id="openseadragon_wrapper">
|
|
168
|
+
<button id="sidebar_expand_button" class="sidebar-expand-button" title="Show controls" type="button">
|
|
169
|
+
<span class="fas fa-chevron-right"></span>
|
|
170
|
+
</button>
|
|
171
|
+
<div id="openseadragon">
|
|
172
|
+
<div id="openseadragon_loader" class="viewer-loader">
|
|
173
|
+
<div class="lds-ellipsis">
|
|
174
|
+
<div></div>
|
|
175
|
+
<div></div>
|
|
176
|
+
<div></div>
|
|
177
|
+
<div></div>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
<div id="legacy_controls_mount" aria-hidden="true">
|
|
184
|
+
<div id="channel_list_wrapper">
|
|
185
|
+
<div id="lowRow1" class="row">
|
|
186
|
+
<div id="boxHeading1" class="col-auto">
|
|
187
|
+
<span class="box-heading">Channels</span>
|
|
188
|
+
</div>
|
|
189
|
+
<div id="download-channels-header-div"></div>
|
|
190
|
+
<div class="col-auto" id="selected-channels-header-div"></div>
|
|
191
|
+
</div>
|
|
192
|
+
<div class="list" id="channel_list">
|
|
193
|
+
<div class="row">
|
|
194
|
+
<div class="col-auto">
|
|
195
|
+
<div class="lds-ellipsis" id="channel_list_loader"></div>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
<div id="tool_panel_legacy_slot" data-tool-mount="{{ data.active_tool }}">
|
|
202
|
+
{% if data.active_tool_panels.tool_panel_legacy_slot %}
|
|
203
|
+
{% include data.active_tool_panels.tool_panel_legacy_slot %}
|
|
204
|
+
{% endif %}
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
<script src="{{ data.base_url }}/client/src/js/main.js?v=20260821_roi_composition_v2" defer></script>
|
|
210
|
+
{% else %}
|
|
211
|
+
<div id="quick_view_landing" class="quick-view-landing">
|
|
212
|
+
<div class="quick-view-card">
|
|
213
|
+
<span class="sidebar-kicker">Plexora</span>
|
|
214
|
+
<h1>Take a Quick Look</h1>
|
|
215
|
+
<p class="quick-view-subtitle">Open a local image. It loads straight into the viewer -- no setup required.</p>
|
|
216
|
+
|
|
217
|
+
<div id="quick_view_dropzone" class="quick-view-dropzone" tabindex="0">
|
|
218
|
+
<span class="fas fa-folder-open quick-view-dropzone-icon"></span>
|
|
219
|
+
<div class="quick-view-dropzone-text">Click here to open an image</div>
|
|
220
|
+
<div class="quick-view-dropzone-hint">Opens a file browser on this machine</div>
|
|
221
|
+
</div>
|
|
222
|
+
|
|
223
|
+
<div id="quick_view_path_fallback" class="quick-view-path-fallback" hidden>
|
|
224
|
+
<label for="quick_view_path_input" class="quick-view-path-label">Image file path</label>
|
|
225
|
+
<div class="quick-view-path-row">
|
|
226
|
+
<input id="quick_view_path_input" type="text" autocomplete="off" spellcheck="false" placeholder="C:\path\to\image.ome.tif">
|
|
227
|
+
<button id="quick_view_path_load" class="sidebar-action" type="button" disabled>Load</button>
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
|
|
231
|
+
<div id="quick_view_status" class="quick-view-status" hidden></div>
|
|
232
|
+
|
|
233
|
+
<p class="quick-view-footer">Need segmentation, gating, or a feature table? Use the <a href="{{ data.base_url }}/upload_page">full import wizard</a> instead.</p>
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
<script src="{{ data.base_url }}/client/src/js/views/quickViewLanding.js?v=20260811_selection_provider" defer></script>
|
|
237
|
+
{% endif %}
|
|
238
|
+
{% endblock %}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
{% extends 'base.html' %}
|
|
2
|
+
{% block style %}
|
|
3
|
+
<link rel="stylesheet" href="{{ data.base_url }}/client/src/css/openProject.css?v=20260824_shared_roots">
|
|
4
|
+
{% endblock %}
|
|
5
|
+
{% block content %}
|
|
6
|
+
<title>Plexora - Open Project</title>
|
|
7
|
+
<div class="open-project-page">
|
|
8
|
+
<div class="open-project-header">
|
|
9
|
+
<div>
|
|
10
|
+
<h1>Open Project</h1>
|
|
11
|
+
<p class="open-project-subtitle" id="project-count">Loading…</p>
|
|
12
|
+
</div>
|
|
13
|
+
<a class="btn btn-primary open-project-create" href="{{ data.base_url }}/upload_page">
|
|
14
|
+
<span class="fas fa-plus"></span> Create Project…
|
|
15
|
+
</a>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
{# [ Projects ] [ Figures ] ... -- the strip only exists once something else
|
|
19
|
+
is on it. A single tab reading "Projects" beside nothing is a control
|
|
20
|
+
that does not control anything, so a core-only build renders no strip at
|
|
21
|
+
all rather than a degenerate one. #}
|
|
22
|
+
{% set page_tabs = data.plugin_nav_items | selectattr('menu', 'equalto', 'open_project') | list %}
|
|
23
|
+
{% if page_tabs %}
|
|
24
|
+
<div class="open-project-tabs" role="tablist">
|
|
25
|
+
<span class="open-project-tab is-active" role="tab" aria-selected="true">Projects</span>
|
|
26
|
+
{% for item in page_tabs %}
|
|
27
|
+
<a class="open-project-tab" id="{{ item.id }}" role="tab" aria-selected="false"
|
|
28
|
+
href="{{ item.href }}">{{ item.label }}</a>
|
|
29
|
+
{% endfor %}
|
|
30
|
+
</div>
|
|
31
|
+
{% endif %}
|
|
32
|
+
|
|
33
|
+
<div class="open-project-toolbar">
|
|
34
|
+
<div class="open-project-search">
|
|
35
|
+
<span class="fas fa-magnifying-glass"></span>
|
|
36
|
+
<input type="text" id="project-search" placeholder="Search projects…" autocomplete="off">
|
|
37
|
+
</div>
|
|
38
|
+
<select id="project-sort" class="open-project-sort">
|
|
39
|
+
<option value="lastOpenedAt">Recently Opened</option>
|
|
40
|
+
<option value="createdAt">Date Created</option>
|
|
41
|
+
<option value="name">Name (A–Z)</option>
|
|
42
|
+
</select>
|
|
43
|
+
<div class="open-project-view-toggle" role="group" aria-label="View">
|
|
44
|
+
<button type="button" id="project-view-grid" class="view-toggle-button" title="Grid view">
|
|
45
|
+
<span class="fas fa-grip"></span>
|
|
46
|
+
</button>
|
|
47
|
+
<button type="button" id="project-view-list" class="view-toggle-button" title="List view">
|
|
48
|
+
<span class="fas fa-list"></span>
|
|
49
|
+
</button>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<div id="project-results" class="project-results project-grid"></div>
|
|
54
|
+
|
|
55
|
+
<div id="project-empty-state" class="open-project-state" hidden>
|
|
56
|
+
<span class="fas fa-folder-open open-project-state-icon"></span>
|
|
57
|
+
<p>No projects yet</p>
|
|
58
|
+
<a class="btn btn-primary" href="{{ data.base_url }}/upload_page">Create your first project…</a>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<div id="project-no-results" class="open-project-state" hidden>
|
|
62
|
+
<span class="fas fa-magnifying-glass open-project-state-icon"></span>
|
|
63
|
+
<p>No projects match your search</p>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div class="modal fade" id="deleteProjectModal" tabindex="-1" aria-hidden="true">
|
|
67
|
+
<div class="modal-dialog modal-dialog-centered">
|
|
68
|
+
<div class="modal-content">
|
|
69
|
+
<div class="modal-header">
|
|
70
|
+
<h5 class="modal-title">Delete project?</h5>
|
|
71
|
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="modal-body">
|
|
74
|
+
<p>Delete <strong id="deleteProjectModalName"></strong>? This removes its data from disk and cannot be undone.</p>
|
|
75
|
+
</div>
|
|
76
|
+
<div class="modal-footer">
|
|
77
|
+
<button type="button" class="btn btn-outline-light" data-bs-dismiss="modal">Cancel</button>
|
|
78
|
+
{# A button, not a link: deleting posts, so there is no URL
|
|
79
|
+
for a prefetcher or a stale bookmark to follow.
|
|
80
|
+
|
|
81
|
+
Dismissed by the data API rather than from the click
|
|
82
|
+
handler, which used to call window.bootstrap.Modal --
|
|
83
|
+
undefined here, because vendor.js imports bootstrap as an
|
|
84
|
+
ES module. That registers this data API but never assigns
|
|
85
|
+
the global, so the optional chain the handler used
|
|
86
|
+
evaluated to undefined and the dialog just sat there
|
|
87
|
+
after a successful delete. #}
|
|
88
|
+
<button id="deleteProjectModalConfirm" type="button" class="btn btn-danger"
|
|
89
|
+
data-bs-dismiss="modal">Delete</button>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
<script src="{{ data.base_url }}/client/src/js/views/openProjectPage.js?v=20260824_shared_roots" defer></script>
|
|
96
|
+
{% endblock %}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{% extends 'base.html' %}
|
|
2
|
+
{% block style %}
|
|
3
|
+
<link rel="stylesheet" href="{{ data.base_url }}/client/src/css/import.css?v=20260819_feature_source">
|
|
4
|
+
{% endblock %}
|
|
5
|
+
{% block content %}
|
|
6
|
+
<title>Plexora - Columns</title>
|
|
7
|
+
{#
|
|
8
|
+
The one screen that survives from the old two-step import, and the only one.
|
|
9
|
+
Which columns are markers is a fact about the dataset that every plugin then
|
|
10
|
+
reads, so it is worth one confirmation -- and it is the only thing about a
|
|
11
|
+
CSV that cannot be worked out reliably from the file.
|
|
12
|
+
#}
|
|
13
|
+
<div class="import-page">
|
|
14
|
+
<div id="upload-parent">
|
|
15
|
+
<div class="import-card import-card-wide">
|
|
16
|
+
<span class="import-kicker">{{ data.datasetName }}</span>
|
|
17
|
+
<h1>Confirm Columns</h1>
|
|
18
|
+
<p class="import-subtitle">We have guessed which columns hold marker intensities and which
|
|
19
|
+
describe the cells. Correct anything we got wrong before continuing.</p>
|
|
20
|
+
|
|
21
|
+
<div id="classifier"></div>
|
|
22
|
+
|
|
23
|
+
<div class="import-actions">
|
|
24
|
+
<button id="columns_save" class="btn btn-primary" type="button">Continue</button>
|
|
25
|
+
</div>
|
|
26
|
+
<div id="columns_error" class="import-alert" role="alert" hidden></div>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
<script src="{{ data.base_url }}/client/src/js/views/columnClassifier.js?v=20260820_csv_markers_log1p"></script>
|
|
31
|
+
<script src="{{ data.base_url }}/client/src/js/views/segmentationProgress.js?v=20260817_single_import"></script>
|
|
32
|
+
<script>
|
|
33
|
+
(function () {
|
|
34
|
+
const data = {{ data|tojson }};
|
|
35
|
+
const classifier = PlexoraColumnClassifier.mount(
|
|
36
|
+
document.getElementById("classifier"),
|
|
37
|
+
{
|
|
38
|
+
markers: data.markers,
|
|
39
|
+
metadata: data.metadata,
|
|
40
|
+
roles: data.roles,
|
|
41
|
+
roleLabels: data.roleLabels,
|
|
42
|
+
},
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const button = document.getElementById("columns_save");
|
|
46
|
+
const error = document.getElementById("columns_error");
|
|
47
|
+
|
|
48
|
+
button.addEventListener("click", async () => {
|
|
49
|
+
button.disabled = true;
|
|
50
|
+
error.hidden = true;
|
|
51
|
+
const task = window.PlexoraStatus?.begin("Saving columns");
|
|
52
|
+
try {
|
|
53
|
+
const response = await fetch(
|
|
54
|
+
plexoraUrl(`project/${encodeURIComponent(data.datasetName)}/columns`),
|
|
55
|
+
{
|
|
56
|
+
method: "POST",
|
|
57
|
+
headers: { "Content-Type": "application/json" },
|
|
58
|
+
body: JSON.stringify(classifier.value()),
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
const result = await response.json();
|
|
62
|
+
if (!response.ok || !result.success) {
|
|
63
|
+
throw new Error(result.error || "Could not save the columns");
|
|
64
|
+
}
|
|
65
|
+
task?.done();
|
|
66
|
+
// A mask supplied at import is still converting; the overlay
|
|
67
|
+
// waits for it rather than opening a viewer with no cells.
|
|
68
|
+
const viewerUrl = plexoraUrl(encodeURIComponent(data.datasetName));
|
|
69
|
+
if (result.segmentation_pending) {
|
|
70
|
+
awaitSegmentationThenOpen({
|
|
71
|
+
datasource: data.datasetName,
|
|
72
|
+
redirectUrl: viewerUrl,
|
|
73
|
+
});
|
|
74
|
+
} else {
|
|
75
|
+
window.location.href = viewerUrl;
|
|
76
|
+
}
|
|
77
|
+
} catch (e) {
|
|
78
|
+
task?.fail("Save failed");
|
|
79
|
+
error.textContent = e.message;
|
|
80
|
+
error.hidden = false;
|
|
81
|
+
button.disabled = false;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
})();
|
|
85
|
+
</script>
|
|
86
|
+
{% endblock %}
|