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,74 @@
|
|
|
1
|
+
{% extends 'base.html' %}
|
|
2
|
+
{% block content %}
|
|
3
|
+
<title>Plexora - Figures</title>
|
|
4
|
+
{# Deliberately shaped like the Open Project page, down to the class names it
|
|
5
|
+
reuses from openProject.css: a figure and a project are the same KIND of
|
|
6
|
+
thing to a user -- something saved, found again and opened -- and giving the
|
|
7
|
+
two lists different furniture would be inventing a distinction that does not
|
|
8
|
+
exist. Everything specific to figures is prefixed .fb-, which is what keeps
|
|
9
|
+
this plugin's stylesheet off core's elements. #}
|
|
10
|
+
<div class="open-project-page fb-library">
|
|
11
|
+
<div class="open-project-header">
|
|
12
|
+
<div>
|
|
13
|
+
<h1>Figures</h1>
|
|
14
|
+
<p class="open-project-subtitle" id="fb_library_count">Loading…</p>
|
|
15
|
+
</div>
|
|
16
|
+
<button type="button" class="btn btn-primary open-project-create" id="fb_library_create">
|
|
17
|
+
<span class="fas fa-plus"></span> New Figure…
|
|
18
|
+
</button>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<div class="open-project-tabs" role="tablist">
|
|
22
|
+
<a class="open-project-tab" role="tab" aria-selected="false"
|
|
23
|
+
href="{{ data.base_url }}/open_project">Projects</a>
|
|
24
|
+
<span class="open-project-tab is-active" role="tab" aria-selected="true">Figures</span>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div class="open-project-toolbar">
|
|
28
|
+
<div class="open-project-search">
|
|
29
|
+
<span class="fas fa-magnifying-glass"></span>
|
|
30
|
+
<input type="text" id="fb_library_search" placeholder="Search figures…" autocomplete="off">
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<div id="fb_library_results" class="project-results project-grid"></div>
|
|
35
|
+
|
|
36
|
+
<div id="fb_library_empty" class="open-project-state" hidden>
|
|
37
|
+
<span class="fas fa-images open-project-state-icon"></span>
|
|
38
|
+
<p>No figures yet</p>
|
|
39
|
+
<p class="fb-muted">Open a project, find a view worth keeping, and capture it —
|
|
40
|
+
or start an empty figure here.</p>
|
|
41
|
+
<button type="button" class="btn btn-primary" id="fb_library_create_empty">Create your first figure…</button>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div id="fb_library_no_results" class="open-project-state" hidden>
|
|
45
|
+
<span class="fas fa-magnifying-glass open-project-state-icon"></span>
|
|
46
|
+
<p>No figures match your search</p>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
{# A button, not a link: deleting posts, so there is no URL for a
|
|
50
|
+
prefetcher or a stale bookmark to follow. Dismissed through bootstrap's
|
|
51
|
+
data API for the reason core's delete dialog documents -- the ES-module
|
|
52
|
+
build never assigns window.bootstrap. #}
|
|
53
|
+
<div class="modal fade" id="fb_delete_modal" tabindex="-1" aria-hidden="true">
|
|
54
|
+
<div class="modal-dialog modal-dialog-centered">
|
|
55
|
+
<div class="modal-content">
|
|
56
|
+
<div class="modal-header">
|
|
57
|
+
<h5 class="modal-title">Delete figure?</h5>
|
|
58
|
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="modal-body">
|
|
61
|
+
<p>Delete <strong id="fb_delete_modal_name"></strong>? Its layout, captured
|
|
62
|
+
scenes and previews are removed from disk and cannot be recovered.</p>
|
|
63
|
+
<p class="fb-muted">The images it referenced are untouched.</p>
|
|
64
|
+
</div>
|
|
65
|
+
<div class="modal-footer">
|
|
66
|
+
<button type="button" class="btn btn-outline-light" data-bs-dismiss="modal">Cancel</button>
|
|
67
|
+
<button id="fb_delete_modal_confirm" type="button" class="btn btn-danger"
|
|
68
|
+
data-bs-dismiss="modal">Delete</button>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
{% endblock %}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{% extends 'base.html' %}
|
|
2
|
+
{% block content %}
|
|
3
|
+
<title>Plexora - Figure</title>
|
|
4
|
+
{# The viewer's stylesheet, on a page with no viewer.
|
|
5
|
+
|
|
6
|
+
Quick Edit mounts a SECOND ViewerSidebar beside a mini preview of a figure
|
|
7
|
+
panel -- core's own channel widget, driving core's own markup, so a user
|
|
8
|
+
never has to learn a second set of channel controls and a fix to the real
|
|
9
|
+
one lands here too. The look of that markup lives in viewer.css, which
|
|
10
|
+
index.html normally owns.
|
|
11
|
+
|
|
12
|
+
Safe to link here because every id rule in it (#bodyDiv, #openseadragon,
|
|
13
|
+
#channel_list, ...) names an element index.html renders and this page does
|
|
14
|
+
not, so they are inert; what applies is the .channel-slot / .searchable-select
|
|
15
|
+
/ .color-swatch class set, which is exactly what is being borrowed. #}
|
|
16
|
+
<link rel="stylesheet" href="{{ data.base_url }}/client/src/css/viewer.css?v=20260823_viewer_refactor">
|
|
17
|
+
{# One figure, opened without a project.
|
|
18
|
+
|
|
19
|
+
Canvas-only by construction: there is no viewer here because there is no
|
|
20
|
+
datasource here, and a figure legitimately references several. Editing a
|
|
21
|
+
panel's SCENE therefore navigates to that panel's own datasource with the
|
|
22
|
+
tool open (see figureWorkspace.js) -- which is a page load, and is the honest
|
|
23
|
+
one: main.js boots per page and the server holds one loaded datasource, so
|
|
24
|
+
"swap the image under the live viewer" is not a thing this app can do without
|
|
25
|
+
pretending. #}
|
|
26
|
+
{% include 'figure_builder/workspace_body.html' %}
|
|
27
|
+
{% endblock %}
|
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
{# The figure canvas.
|
|
2
|
+
|
|
3
|
+
ONE workspace, and the canvas is nearly all of it. This markup used to be
|
|
4
|
+
rendered in two places -- a figure's own page and a split pane beside the
|
|
5
|
+
live viewer -- and the split pane is gone: composing a figure and looking
|
|
6
|
+
down a microscope are different activities, and half a window was not enough
|
|
7
|
+
room for either. What is left is a page whose dominant element is the figure.
|
|
8
|
+
|
|
9
|
+
The permanent chrome is deliberately thin: a narrow tool rail, a tray of
|
|
10
|
+
panels, and a topbar. Everything that ACTS on a selection is built in
|
|
11
|
+
JavaScript into #fb_overlay_layer and floats beside what it acts on
|
|
12
|
+
(figureContextBar.js) -- a fixed properties column would take three hundred
|
|
13
|
+
pixels away from the thing the user is looking at, permanently, to show
|
|
14
|
+
controls that are relevant some of the time.
|
|
15
|
+
|
|
16
|
+
#fb_text_panel is not a return of that column, and the distinction is the
|
|
17
|
+
word "permanently": it is CONTEXTUAL, present only while a text object is
|
|
18
|
+
selected or being typed into, and gone the rest of the time. Text carries
|
|
19
|
+
far more formatting than any other object here -- family, size, colour,
|
|
20
|
+
weight, leading, two alignments -- and a popover deep enough to hold all of
|
|
21
|
+
it is a popover nobody can work in. It holds formatting only; anything that
|
|
22
|
+
can be done to any OBJECT stays on the floating bar.
|
|
23
|
+
|
|
24
|
+
That bar renders from figureActions.js, one declaration of what each action
|
|
25
|
+
is and when it applies, which the right-click menu renders from as well. It
|
|
26
|
+
used to be four hand-built lists and they had drifted -- the menu disabled
|
|
27
|
+
"Bring to front" where the bar offered it live on a text box and it did
|
|
28
|
+
nothing at all.
|
|
29
|
+
|
|
30
|
+
#fb_overlay_layer is the one part of this the canvas renderer never touches.
|
|
31
|
+
FigureCanvas.render() replaces the whole of #fb_page_surface on every change,
|
|
32
|
+
so anything that has to survive a render -- the context bar, an open text
|
|
33
|
+
editor -- lives out here instead. #}
|
|
34
|
+
<div class="fb-workspace" id="fb_workspace"
|
|
35
|
+
data-figure-id="{{ data.figure_id | default('') }}">
|
|
36
|
+
{# Clusters on the desk, not a strip across it. The bar itself paints
|
|
37
|
+
nothing: what floats is the controls, in the same card as the rail and
|
|
38
|
+
the tray. The gap between two clusters is what says which controls belong
|
|
39
|
+
together, which is the job the grey pill inside a solid bar was doing --
|
|
40
|
+
without a box inside a box. #}
|
|
41
|
+
<header class="fb-topbar">
|
|
42
|
+
<div class="fb-bar-cluster fb-bar-cluster-lead">
|
|
43
|
+
{# The library is where this goes by default and where it goes if
|
|
44
|
+
anything is uncertain. It is retargeted at the VIEWER, in
|
|
45
|
+
JavaScript, when the note left by figureSidebarController says
|
|
46
|
+
that is where the user came from -- because the commonest trip
|
|
47
|
+
there is is capture a few fields, look at the figure, go back to
|
|
48
|
+
the slide for one more, and an arrow that always went to the
|
|
49
|
+
library made the way back a search. #}
|
|
50
|
+
<a class="fb-back" id="fb_back" href="{{ data.figure_builder_url }}/figures"
|
|
51
|
+
title="Back to figures">
|
|
52
|
+
<span class="fas fa-chevron-left"></span>
|
|
53
|
+
</a>
|
|
54
|
+
{# contenteditable would be fewer elements and the wrong control: it
|
|
55
|
+
accepts pasted markup, and this value is rendered back into a
|
|
56
|
+
list of cards. A plain input cannot carry anything but text. #}
|
|
57
|
+
<input type="text" id="fb_title" class="fb-title-input" autocomplete="off"
|
|
58
|
+
spellcheck="false" maxlength="200" aria-label="Figure title">
|
|
59
|
+
<span id="fb_save_status" class="fb-save-status" role="status" aria-live="polite"></span>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div class="fb-topbar-spacer"></div>
|
|
63
|
+
|
|
64
|
+
{# History, pages and View in one card: three ways of moving around what
|
|
65
|
+
already exists. Export is not one of those, and does not share it. #}
|
|
66
|
+
<div class="fb-bar-cluster">
|
|
67
|
+
<div class="fb-topbar-group" role="group" aria-label="History">
|
|
68
|
+
<button type="button" id="fb_undo" class="fb-icon-button" title="Undo (⌘Z)" disabled>
|
|
69
|
+
<span class="fas fa-rotate-left"></span>
|
|
70
|
+
</button>
|
|
71
|
+
<button type="button" id="fb_redo" class="fb-icon-button" title="Redo (⇧⌘Z)" disabled>
|
|
72
|
+
<span class="fas fa-rotate-right"></span>
|
|
73
|
+
</button>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<span class="fb-bar-divider" aria-hidden="true"></span>
|
|
77
|
+
|
|
78
|
+
<div class="fb-topbar-group" role="group" aria-label="Pages">
|
|
79
|
+
<select id="fb_page_select" class="fb-select fb-select-quiet" aria-label="Page"></select>
|
|
80
|
+
<button type="button" id="fb_page_menu" class="fb-icon-button" title="Page actions"
|
|
81
|
+
aria-haspopup="true" aria-expanded="false">
|
|
82
|
+
<span class="fas fa-ellipsis"></span>
|
|
83
|
+
</button>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<span class="fb-bar-divider" aria-hidden="true"></span>
|
|
87
|
+
|
|
88
|
+
<button type="button" id="fb_view_menu" class="fb-button"
|
|
89
|
+
aria-haspopup="true" aria-expanded="false">
|
|
90
|
+
<span class="fas fa-eye"></span> View
|
|
91
|
+
</button>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
{# Zoom is not up here. It changes where the eye is, not what the figure
|
|
95
|
+
is, and sitting in a row with Undo and Export it looked like the same
|
|
96
|
+
kind of control. It lives in the status bar at the foot of the
|
|
97
|
+
canvas, beside the readout it moves. #}
|
|
98
|
+
|
|
99
|
+
<button type="button" id="fb_export_open" class="fb-button fb-button-primary fb-bar-solo">
|
|
100
|
+
<span class="fas fa-file-export"></span> Export
|
|
101
|
+
</button>
|
|
102
|
+
</header>
|
|
103
|
+
|
|
104
|
+
{# A native <dialog>: modal, focus-trapped and Esc-dismissible without a
|
|
105
|
+
line of script, and it cannot end up behind the canvas the way a
|
|
106
|
+
positioned div can. #}
|
|
107
|
+
<dialog id="fb_export_dialog" class="fb-dialog">
|
|
108
|
+
<h2>Export figure</h2>
|
|
109
|
+
<p class="fb-muted">Panels are re-rendered from the source images at the resolution
|
|
110
|
+
you ask for — not from what is on screen.</p>
|
|
111
|
+
|
|
112
|
+
<label class="fb-field-label" for="fb_export_format">Format</label>
|
|
113
|
+
<select id="fb_export_format" class="fb-select">
|
|
114
|
+
<option value="pdf">PDF — text and legends stay editable</option>
|
|
115
|
+
<option value="tiff">TIFF — flattened, for submission</option>
|
|
116
|
+
<option value="png">PNG — flattened, for circulating</option>
|
|
117
|
+
</select>
|
|
118
|
+
|
|
119
|
+
<label class="fb-field-label" for="fb_export_dpi">Resolution</label>
|
|
120
|
+
<select id="fb_export_dpi" class="fb-select">
|
|
121
|
+
<option value="150">150 DPI</option>
|
|
122
|
+
<option value="300" selected>300 DPI</option>
|
|
123
|
+
<option value="600">600 DPI</option>
|
|
124
|
+
<option value="1200">1200 DPI</option>
|
|
125
|
+
</select>
|
|
126
|
+
|
|
127
|
+
<label class="fb-check">
|
|
128
|
+
<input type="checkbox" id="fb_export_provenance" checked>
|
|
129
|
+
Append a provenance page
|
|
130
|
+
</label>
|
|
131
|
+
|
|
132
|
+
{# Stated up front rather than discovered in the file. Only PNG has an
|
|
133
|
+
alpha channel; a transparent page rendered to PDF or TIFF is drawn on
|
|
134
|
+
white, which is a decision the author should make knowingly. #}
|
|
135
|
+
<p class="fb-muted">A transparent page keeps its transparency in PNG only.
|
|
136
|
+
PDF and TIFF render it white.</p>
|
|
137
|
+
|
|
138
|
+
{# Filled by the preflight, before anything is rendered: a warning
|
|
139
|
+
attached to a file that is already written is a warning nobody acts
|
|
140
|
+
on. #}
|
|
141
|
+
<div id="fb_export_warnings" class="fb-export-warnings"></div>
|
|
142
|
+
|
|
143
|
+
<div class="fb-banner-actions">
|
|
144
|
+
<span id="fb_export_status" class="fb-muted" role="status" aria-live="polite"></span>
|
|
145
|
+
{# This plugin's own buttons, not core's .sidebar-action. The
|
|
146
|
+
figure page links viewer.css for the borrowed channel widget
|
|
147
|
+
alone; making the export dialog depend on it too would tie the
|
|
148
|
+
look of a Figure Builder control to a stylesheet that is here
|
|
149
|
+
for something else entirely. #}
|
|
150
|
+
<a id="fb_export_download" class="fb-button" href="#" download hidden>
|
|
151
|
+
<span class="fas fa-download"></span> Download
|
|
152
|
+
</a>
|
|
153
|
+
<button type="button" id="fb_export_stop" class="fb-button" hidden>Stop</button>
|
|
154
|
+
<button type="button" id="fb_export_start" class="fb-button fb-button-primary">Export</button>
|
|
155
|
+
<button type="button" id="fb_export_cancel" class="fb-button">Close</button>
|
|
156
|
+
</div>
|
|
157
|
+
</dialog>
|
|
158
|
+
|
|
159
|
+
<div id="fb_conflict_banner" class="fb-banner fb-banner-warning" hidden>
|
|
160
|
+
<span class="fas fa-code-branch"></span>
|
|
161
|
+
<div>
|
|
162
|
+
<strong>Changed in another session.</strong>
|
|
163
|
+
<div class="fb-banner-detail">Someone — probably another tab — saved this
|
|
164
|
+
figure since this one loaded. Reloading discards the edits made here.</div>
|
|
165
|
+
<div class="fb-banner-actions">
|
|
166
|
+
<button id="fb_conflict_reload" class="fb-button" type="button">Reload theirs</button>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
<div id="fb_unreadable_banner" class="fb-banner fb-banner-danger" hidden>
|
|
172
|
+
<span class="fas fa-triangle-exclamation"></span>
|
|
173
|
+
<div>
|
|
174
|
+
<strong>This figure could not be opened.</strong>
|
|
175
|
+
<div id="fb_unreadable_detail" class="fb-banner-detail"></div>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
{# Nothing in here is a column. The canvas runs the full width of the body
|
|
180
|
+
and the rail and the tray are cards lying on top of it -- which is why
|
|
181
|
+
.fb-main comes FIRST: it is the only flex child that takes space, and the
|
|
182
|
+
two cards are positioned against the body and out of the flow. Quick Edit
|
|
183
|
+
is the exception, and takes its 420px from the canvas, because reframing
|
|
184
|
+
a panel means watching the figure change as you do it. #}
|
|
185
|
+
<div class="fb-body">
|
|
186
|
+
<main class="fb-main" id="fb_main">
|
|
187
|
+
{# Optional, off by default and remembered per browser. Two
|
|
188
|
+
<canvas> strips rather than a wall of positioned ticks: a ruler
|
|
189
|
+
at 1 mm resolution across an A4 page is 210 elements that have to
|
|
190
|
+
be rebuilt on every zoom and every scroll. #}
|
|
191
|
+
<canvas class="fb-ruler fb-ruler-h" id="fb_ruler_h" aria-hidden="true"></canvas>
|
|
192
|
+
<canvas class="fb-ruler fb-ruler-v" id="fb_ruler_v" aria-hidden="true"></canvas>
|
|
193
|
+
<div class="fb-ruler-corner" id="fb_ruler_corner" aria-hidden="true"></div>
|
|
194
|
+
|
|
195
|
+
<div class="fb-canvas-scroll" id="fb_canvas_scroll">
|
|
196
|
+
{# The sheet and the two things that belong to it, shrink-wrapped
|
|
197
|
+
and centred as one block: centred independently, the caption
|
|
198
|
+
and the button would drift off the page edges they are
|
|
199
|
+
supposed to line up with. #}
|
|
200
|
+
<div class="fb-sheet">
|
|
201
|
+
<div class="fb-sheet-caption" id="fb_sheet_caption"></div>
|
|
202
|
+
<div class="fb-page" id="fb_page">
|
|
203
|
+
{# Under the panels, and children of the page so they
|
|
204
|
+
scale with it: a grid drawn in screen pixels drifts
|
|
205
|
+
off the millimetres it is supposed to represent the
|
|
206
|
+
moment the zoom changes. #}
|
|
207
|
+
<div class="fb-page-grid" id="fb_page_grid" aria-hidden="true"></div>
|
|
208
|
+
<div class="fb-page-margins" id="fb_page_margins" aria-hidden="true"></div>
|
|
209
|
+
<div class="fb-page-surface" id="fb_page_surface"></div>
|
|
210
|
+
{# Guides and the marquee live above the panels and take
|
|
211
|
+
no pointer events, so a snap line can never be what a
|
|
212
|
+
drag grabs. #}
|
|
213
|
+
<div class="fb-page-guides" id="fb_page_guides" aria-hidden="true"></div>
|
|
214
|
+
</div>
|
|
215
|
+
|
|
216
|
+
{# Under the page and as wide as it. Inside the scroll
|
|
217
|
+
surface rather than in the status bar, so it scrolls away
|
|
218
|
+
with the sheet it follows: it is part of the document,
|
|
219
|
+
not part of the window. #}
|
|
220
|
+
<button type="button" id="fb_page_add" class="fb-add-page">
|
|
221
|
+
<span class="fas fa-plus" aria-hidden="true"></span> Add page
|
|
222
|
+
</button>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
{# Never re-rendered by the canvas. See the file comment. #}
|
|
227
|
+
<div class="fb-overlay-layer" id="fb_overlay_layer"></div>
|
|
228
|
+
</main>
|
|
229
|
+
|
|
230
|
+
{# Level 1: the only controls that are always visible, and there are
|
|
231
|
+
four of them. Everything else is contextual, in a menu, or on the
|
|
232
|
+
right-click.
|
|
233
|
+
|
|
234
|
+
There is no Select tool. Selecting is what the pointer does when it
|
|
235
|
+
is not doing anything else: the drawing tools are one-shot and hand
|
|
236
|
+
the pointer back the moment they have placed something, and clicking
|
|
237
|
+
the desk drops one armed by mistake. A button for "no tool" would be
|
|
238
|
+
a light that is on almost always, and a click nobody needs to make.
|
|
239
|
+
|
|
240
|
+
Scale bar and Legend are not here either. They act on a SELECTION,
|
|
241
|
+
they have real settings behind them, and both already exist in the
|
|
242
|
+
floating bar next to the panels they change -- up here they were a
|
|
243
|
+
second, blunter copy of the same switch, dimmed most of the time.
|
|
244
|
+
|
|
245
|
+
Labelled, at the cost of twenty pixels of width: four icons in a
|
|
246
|
+
stack with nothing under them is four things to hover before
|
|
247
|
+
starting, every session, because "a square" is not a word -- and
|
|
248
|
+
this rail is the first thing a new user has to read. #}
|
|
249
|
+
<nav class="fb-tool-rail fb-card" id="fb_tool_rail" aria-label="Figure tools">
|
|
250
|
+
{# The one rail item that opens a panel rather than arming a tool.
|
|
251
|
+
The badge is what stops a capture arriving into a closed drawer
|
|
252
|
+
from looking like a capture that was lost. #}
|
|
253
|
+
<div class="fb-tool-holder">
|
|
254
|
+
<button type="button" class="fb-tool is-active" id="fb_tool_panels"
|
|
255
|
+
title="Panels captured for this figure"
|
|
256
|
+
aria-haspopup="true" aria-expanded="true"
|
|
257
|
+
aria-controls="fb_tray_panel">
|
|
258
|
+
<span class="fas fa-images" aria-hidden="true"></span>
|
|
259
|
+
<span class="fb-tool-label">Panels</span>
|
|
260
|
+
</button>
|
|
261
|
+
<span class="fb-tool-badge" id="fb_tray_badge" aria-hidden="true"></span>
|
|
262
|
+
</div>
|
|
263
|
+
|
|
264
|
+
<div class="fb-tool-rail-gap"></div>
|
|
265
|
+
|
|
266
|
+
<button type="button" class="fb-tool" data-tool="text"
|
|
267
|
+
title="Text" aria-pressed="false">
|
|
268
|
+
<span class="fas fa-font" aria-hidden="true"></span>
|
|
269
|
+
<span class="fb-tool-label">Text</span>
|
|
270
|
+
</button>
|
|
271
|
+
{# Two menus rather than four buttons, and split where the CONTROLS
|
|
272
|
+
differ: a rectangle and an ellipse have a fill and are dragged
|
|
273
|
+
out as a box, a line and an arrow have two ends and are dragged
|
|
274
|
+
out along one. Four permanent icons would double the rail to
|
|
275
|
+
save one click on the rarer three. #}
|
|
276
|
+
<button type="button" class="fb-tool" id="fb_tool_shapes"
|
|
277
|
+
title="Shapes" aria-haspopup="true" aria-expanded="false"
|
|
278
|
+
aria-pressed="false">
|
|
279
|
+
<span class="fas fa-shapes" aria-hidden="true"></span>
|
|
280
|
+
<span class="fb-tool-label">Shapes</span>
|
|
281
|
+
</button>
|
|
282
|
+
<button type="button" class="fb-tool" id="fb_tool_lines"
|
|
283
|
+
title="Lines and arrows" aria-haspopup="true" aria-expanded="false"
|
|
284
|
+
aria-pressed="false">
|
|
285
|
+
<span class="fas fa-arrow-right-long" aria-hidden="true"></span>
|
|
286
|
+
<span class="fb-tool-label">Lines</span>
|
|
287
|
+
</button>
|
|
288
|
+
</nav>
|
|
289
|
+
|
|
290
|
+
{# The text sidebar. CONTEXTUAL, and that is the whole distinction from
|
|
291
|
+
the fixed properties column this workspace deleted: it is present
|
|
292
|
+
only while a text object is selected or being typed into, so the
|
|
293
|
+
canvas keeps the full width for everything else anyone does here.
|
|
294
|
+
|
|
295
|
+
It carries text FORMATTING and nothing else. Actions that apply to
|
|
296
|
+
any object -- arrange, align, group, duplicate, delete -- belong to
|
|
297
|
+
the bar that floats over the selection, which is one shared component
|
|
298
|
+
for every object kind rather than a list rebuilt per type.
|
|
299
|
+
|
|
300
|
+
`.fb-side-panel` is what it shares with the tray below: ONE strip
|
|
301
|
+
beside the rail, holding one panel at a time, at one size and in one
|
|
302
|
+
place. Which of them is in it is FigureWorkspace.showSidebar's to
|
|
303
|
+
say, and neither hides or shows itself -- two panels each answering
|
|
304
|
+
that for themselves is what drew this one underneath the tray, where
|
|
305
|
+
it was invisible and still took every click on the overlap. #}
|
|
306
|
+
<aside class="fb-side-panel fb-text-panel fb-card" id="fb_text_panel"
|
|
307
|
+
aria-label="Text properties" hidden></aside>
|
|
308
|
+
|
|
309
|
+
{# What this figure is made of. Panels arrive here from the viewer and
|
|
310
|
+
are dragged onto the page; nothing is ever placed automatically at
|
|
311
|
+
capture time, because composition is a different sitting from
|
|
312
|
+
exploration.
|
|
313
|
+
|
|
314
|
+
A card the rail opens, with its own title and its own way out.
|
|
315
|
+
Open on arrival: a figure page whose panels were behind a closed
|
|
316
|
+
drawer would look, on the day it is reopened, like a figure that had
|
|
317
|
+
lost them. #}
|
|
318
|
+
<aside class="fb-side-panel fb-tray-panel fb-card" id="fb_tray_panel"
|
|
319
|
+
aria-label="Panels">
|
|
320
|
+
<div class="fb-side-heading">
|
|
321
|
+
<span class="fb-side-title">Panels</span>
|
|
322
|
+
<span id="fb_tray_count" class="fb-tray-count"></span>
|
|
323
|
+
<button type="button" id="fb_tray_close" class="fb-icon-button"
|
|
324
|
+
title="Close">
|
|
325
|
+
<span class="fas fa-xmark" aria-hidden="true"></span>
|
|
326
|
+
</button>
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
{# Where the next panels come from, at the top of the thing they
|
|
330
|
+
land in.
|
|
331
|
+
|
|
332
|
+
Two routes, because a figure is made of two kinds of thing. A
|
|
333
|
+
project opens that project's viewer with THIS figure already
|
|
334
|
+
chosen as the capture destination, so the fields taken over
|
|
335
|
+
there arrive in this tray and the dock's "Figure Canvas" is the
|
|
336
|
+
way back. An image file is for the schematic or the plot that
|
|
337
|
+
was never in Plexora, and it lands in the figure alone.
|
|
338
|
+
|
|
339
|
+
A menu rather than two buttons: one is a page load and the other
|
|
340
|
+
is a file dialog, and neither is used often enough to spend a
|
|
341
|
+
permanent row of a narrow card on. The file input lives here now
|
|
342
|
+
rather than in the rail -- it is a way of adding a panel, and
|
|
343
|
+
this is where adding panels is. #}
|
|
344
|
+
<div class="fb-tray-add">
|
|
345
|
+
<button type="button" id="fb_tray_add" class="fb-button fb-tray-add-button"
|
|
346
|
+
aria-haspopup="true" aria-expanded="false">
|
|
347
|
+
<span class="fas fa-plus" aria-hidden="true"></span> Add panels
|
|
348
|
+
</button>
|
|
349
|
+
<input type="file" id="fb_add_image_input" accept="image/*" multiple hidden>
|
|
350
|
+
</div>
|
|
351
|
+
{# Shown only once the tray is long enough for finding something in
|
|
352
|
+
it to be work -- which is sooner now that the names are behind a
|
|
353
|
+
hover.
|
|
354
|
+
|
|
355
|
+
"Filter", not "Search": it narrows the grid under it as the
|
|
356
|
+
characters arrive and there is nothing to submit. The × and Esc
|
|
357
|
+
are the way back out, because a filter you have forgotten is on
|
|
358
|
+
looks exactly like a tray that has lost panels. #}
|
|
359
|
+
<div class="fb-tray-search" id="fb_tray_search_wrap" hidden>
|
|
360
|
+
<span class="fas fa-magnifying-glass" aria-hidden="true"></span>
|
|
361
|
+
<input type="search" id="fb_tray_search" placeholder="Filter panels"
|
|
362
|
+
autocomplete="off" spellcheck="false" aria-label="Filter panels">
|
|
363
|
+
<button type="button" id="fb_tray_search_clear" class="fb-tray-search-clear"
|
|
364
|
+
title="Clear filter (Esc)" hidden>
|
|
365
|
+
<span class="fas fa-xmark" aria-hidden="true"></span>
|
|
366
|
+
</button>
|
|
367
|
+
</div>
|
|
368
|
+
<div class="fb-tray-strip" id="fb_tray_strip" role="listbox" aria-multiselectable="true"></div>
|
|
369
|
+
<div class="fb-tray-empty" id="fb_tray_empty" hidden>
|
|
370
|
+
<p class="fb-muted">Captured panels land here.</p>
|
|
371
|
+
<p class="fb-muted">Use <strong>Add panels</strong> to open a project and
|
|
372
|
+
capture a field, or drop an image file onto the page.</p>
|
|
373
|
+
</div>
|
|
374
|
+
</aside>
|
|
375
|
+
|
|
376
|
+
{# Quick Edit: the microscopy view behind one panel, adjusted without
|
|
377
|
+
leaving the canvas.
|
|
378
|
+
|
|
379
|
+
The channel controls below the preview are CORE's -- a second
|
|
380
|
+
ViewerSidebar instance driving the markup below, which carries core's
|
|
381
|
+
own classes and `fbqe_`-prefixed ids so the two instances can never
|
|
382
|
+
find each other's elements. Writing a second channel widget here
|
|
383
|
+
would be two implementations of colour, contrast and ordering that
|
|
384
|
+
agree until the day one of them is fixed, and a user who has to learn
|
|
385
|
+
the second one.
|
|
386
|
+
|
|
387
|
+
Deliberately NOT a second scientific viewer: there is no OSD, no tile
|
|
388
|
+
stack, no overlays and no plugins in here. Anything past reframing
|
|
389
|
+
and channels is what "Open in Main Viewer" is for. #}
|
|
390
|
+
<aside class="fb-quickedit" id="fb_quickedit" hidden aria-label="Quick Edit">
|
|
391
|
+
<header class="fb-quickedit-head">
|
|
392
|
+
<span class="fb-quickedit-title" id="fb_quickedit_title"></span>
|
|
393
|
+
<button type="button" id="fb_quickedit_close" class="fb-icon-button"
|
|
394
|
+
title="Cancel">
|
|
395
|
+
<span class="fas fa-xmark"></span>
|
|
396
|
+
</button>
|
|
397
|
+
</header>
|
|
398
|
+
|
|
399
|
+
<div class="fb-quickedit-view">
|
|
400
|
+
<canvas id="fb_quickedit_canvas"></canvas>
|
|
401
|
+
</div>
|
|
402
|
+
<p class="fb-muted fb-quickedit-note" id="fb_quickedit_note">
|
|
403
|
+
Drag to reposition, scroll to zoom. The bright rectangle is what the
|
|
404
|
+
panel will show.
|
|
405
|
+
</p>
|
|
406
|
+
|
|
407
|
+
<div class="fb-quickedit-channels" id="fb_quickedit_channels">
|
|
408
|
+
<div class="selected-count">
|
|
409
|
+
<span id="fbqe_num-selected-channels">0</span>
|
|
410
|
+
<span>active</span>
|
|
411
|
+
<span class="selected-count-max">· <span id="fbqe_max-channels">15</span> max</span>
|
|
412
|
+
</div>
|
|
413
|
+
<div id="fbqe_channel_slot_list" class="channel-slot-list"></div>
|
|
414
|
+
<button id="fbqe_add_channel_button" class="sidebar-action secondary" type="button">
|
|
415
|
+
<span class="fas fa-plus"></span>
|
|
416
|
+
Add Channel
|
|
417
|
+
</button>
|
|
418
|
+
</div>
|
|
419
|
+
|
|
420
|
+
<footer class="fb-quickedit-actions">
|
|
421
|
+
<button type="button" id="fb_quickedit_main" class="fb-button">
|
|
422
|
+
Open in Main Viewer
|
|
423
|
+
</button>
|
|
424
|
+
<div class="fb-topbar-spacer"></div>
|
|
425
|
+
<button type="button" id="fb_quickedit_cancel" class="fb-button">
|
|
426
|
+
Cancel
|
|
427
|
+
</button>
|
|
428
|
+
<button type="button" id="fb_quickedit_done" class="fb-button fb-button-primary">Done</button>
|
|
429
|
+
</footer>
|
|
430
|
+
</aside>
|
|
431
|
+
</div>
|
|
432
|
+
|
|
433
|
+
{# What is on the page, and what the pointer can do to it. The
|
|
434
|
+
topbar acts on the figure; this acts on the view of it. #}
|
|
435
|
+
<div class="fb-statusbar">
|
|
436
|
+
<span id="fb_object_count" class="fb-statusbar-count"></span>
|
|
437
|
+
<span class="fb-statusbar-hint">
|
|
438
|
+
<kbd>⌘</kbd>-scroll to zoom · <kbd>Space</kbd>-drag to pan
|
|
439
|
+
</span>
|
|
440
|
+
<div class="fb-topbar-spacer"></div>
|
|
441
|
+
{# The only thing down here anyone clicks, so it is the only thing down
|
|
442
|
+
here in a card. The count and the hint stay on the desk. #}
|
|
443
|
+
<div class="fb-bar-cluster" role="group" aria-label="Zoom">
|
|
444
|
+
<button type="button" id="fb_zoom_out" class="fb-icon-button" title="Zoom out (⌘−)">
|
|
445
|
+
<span class="fas fa-minus" aria-hidden="true"></span>
|
|
446
|
+
</button>
|
|
447
|
+
<button type="button" id="fb_zoom_readout" class="fb-zoom-readout"
|
|
448
|
+
title="Zoom" aria-haspopup="true" aria-expanded="false">100%</button>
|
|
449
|
+
<button type="button" id="fb_zoom_in" class="fb-icon-button" title="Zoom in (⌘=)">
|
|
450
|
+
<span class="fas fa-plus" aria-hidden="true"></span>
|
|
451
|
+
</button>
|
|
452
|
+
<span class="fb-bar-divider" aria-hidden="true"></span>
|
|
453
|
+
<button type="button" id="fb_zoom_fit" class="fb-icon-button" title="Fit page (⌘0)">
|
|
454
|
+
<span class="fas fa-expand" aria-hidden="true"></span>
|
|
455
|
+
</button>
|
|
456
|
+
</div>
|
|
457
|
+
</div>
|
|
458
|
+
</div>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Marker-threshold gating: Plexora's first plugin.
|
|
2
|
+
|
|
3
|
+
Everything gating owns lives under this directory -- server code, client
|
|
4
|
+
assets, templates and tests -- so the whole feature can be read, moved or
|
|
5
|
+
removed as one unit.
|
|
6
|
+
|
|
7
|
+
Kept deliberately light: this module is imported whenever gating is activated,
|
|
8
|
+
and its only job is to describe the plugin. The Blueprint is built by a factory
|
|
9
|
+
at install time so that reading the descriptor does not drag in
|
|
10
|
+
scipy/sklearn/anndata/h5py.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from plexora.api.plugin import Plugin, Requires
|
|
14
|
+
|
|
15
|
+
VERSION = "20260824_icon_names"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _blueprint():
|
|
19
|
+
from plexora.plugins.gating.server.routes import gating_bp
|
|
20
|
+
|
|
21
|
+
return gating_bp
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
PLUGIN = Plugin(
|
|
25
|
+
name="gating",
|
|
26
|
+
label="Thresholding",
|
|
27
|
+
version=VERSION,
|
|
28
|
+
blueprint_factory=_blueprint,
|
|
29
|
+
# Templates are namespaced by plugin name, so two plugins can both ship a
|
|
30
|
+
# "panel.html" without colliding in Flask's shared template lookup.
|
|
31
|
+
panels={
|
|
32
|
+
"tool_panel_slot": "gating/panel.html",
|
|
33
|
+
"tool_panel_legacy_slot": "gating/legacy.html",
|
|
34
|
+
},
|
|
35
|
+
# Filenames within this plugin's own static/ directory. Core turns them
|
|
36
|
+
# into base-URL-safe, version-stamped URLs -- a plugin never writes a path
|
|
37
|
+
# that assumes where the app is mounted.
|
|
38
|
+
# gatingApi.js first: the other two construct GatingApi at init.
|
|
39
|
+
scripts=("gatingApi.js", "csvGatingList.js", "gatingSidebarController.js"),
|
|
40
|
+
styles=("gating.css",),
|
|
41
|
+
# Gating thresholds feature-table columns, so a project without one has
|
|
42
|
+
# nothing to gate; it needs the marker/metadata split to know WHICH columns
|
|
43
|
+
# to offer, and the three roles it resolves through dataset.schema (cell id
|
|
44
|
+
# to report selections, x/y to keep coordinates out of the marker list).
|
|
45
|
+
#
|
|
46
|
+
# How cells are drawn is deliberately NOT declared here. Gating paints
|
|
47
|
+
# per-cell results over the image, but the project answers that itself --
|
|
48
|
+
# the mask if there is one, centroids otherwise (see project.cell_layer).
|
|
49
|
+
#
|
|
50
|
+
# Which matrix holds those values IS declared, because a threshold is a
|
|
51
|
+
# number on an axis: set against raw counts it means nothing on a
|
|
52
|
+
# log-transformed copy of the same data, and nothing about the values says
|
|
53
|
+
# which of the two the file handed over.
|
|
54
|
+
#
|
|
55
|
+
# Segmentation is offered, not demanded: gating still works with centroids
|
|
56
|
+
# alone. Listing it as optional is what lets core ask at the right moment
|
|
57
|
+
# instead of this plugin growing its own file-path box.
|
|
58
|
+
#
|
|
59
|
+
# image_id is demanded, and used to be optional. It is only safely optional
|
|
60
|
+
# when the table covers a single image, and nothing establishes that:
|
|
61
|
+
# deciding it needs to know which column is the image id, which is the
|
|
62
|
+
# question itself. The import-time guard only fires when a column-name
|
|
63
|
+
# heuristic recognises the name, so a table keyed on "roi" or "core" gets
|
|
64
|
+
# imported whole and draws several images' cells over one image. Asking is
|
|
65
|
+
# the only honest form -- and "this data has only one image" is one of the
|
|
66
|
+
# answers, so a genuinely single-image project is not made to invent a
|
|
67
|
+
# column it does not have.
|
|
68
|
+
requires=Requires(
|
|
69
|
+
table=True,
|
|
70
|
+
markers=True,
|
|
71
|
+
features=True,
|
|
72
|
+
roles=("cell_id", "x", "y", "image_id"),
|
|
73
|
+
optional=("segmentation",),
|
|
74
|
+
),
|
|
75
|
+
owns_cell_layer=True,
|
|
76
|
+
)
|
|
File without changes
|