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.
Files changed (248) hide show
  1. plexora/__init__.py +129 -0
  2. plexora/__main__.py +15 -0
  3. plexora/_url.py +105 -0
  4. plexora/api/__init__.py +55 -0
  5. plexora/api/dataset.py +398 -0
  6. plexora/api/http.py +19 -0
  7. plexora/api/plugin.py +541 -0
  8. plexora/api/store.py +221 -0
  9. plexora/cli.py +777 -0
  10. plexora/client/dist/354_bundle.js +2 -0
  11. plexora/client/dist/354_bundle.js.LICENSE.txt +20 -0
  12. plexora/client/dist/418_bundle.js +2 -0
  13. plexora/client/dist/418_bundle.js.LICENSE.txt +1 -0
  14. plexora/client/dist/693b935cb1f907814be9c6199a68c217.svg +19 -0
  15. plexora/client/dist/713c134ff47cd328a5b711526f151082.svg +18 -0
  16. plexora/client/dist/770_bundle.js +1 -0
  17. plexora/client/dist/ff743f408972e0e96ca9153ed5ae4c83.svg +23 -0
  18. plexora/client/dist/vendor_bundle.js +2 -0
  19. plexora/client/dist/vendor_bundle.js.LICENSE.txt +308 -0
  20. plexora/client/external/openseadragon-bin-2.4.0/LICENSE.txt +28 -0
  21. plexora/client/external/openseadragon-bin-2.4.0/canvas-overlay-hd.js +140 -0
  22. plexora/client/external/openseadragon-bin-2.4.0/changelog.txt +501 -0
  23. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/.gitattributes +17 -0
  24. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/LICENSE.txt +116 -0
  25. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/Toolbar.png +0 -0
  26. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/fullpage.psd +0 -0
  27. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/home.psd +0 -0
  28. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/next.psd +0 -0
  29. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/previous.psd +0 -0
  30. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateleft.psd +0 -0
  31. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateright.psd +0 -0
  32. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/zoom.psd +0 -0
  33. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/README.md +9 -0
  34. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_grouphover.png +0 -0
  35. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_hover.png +0 -0
  36. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_pressed.png +0 -0
  37. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_rest.png +0 -0
  38. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_grouphover.png +0 -0
  39. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_hover.png +0 -0
  40. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_pressed.png +0 -0
  41. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_rest.png +0 -0
  42. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_grouphover.png +0 -0
  43. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_hover.png +0 -0
  44. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_pressed.png +0 -0
  45. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_rest.png +0 -0
  46. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_grouphover.png +0 -0
  47. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_hover.png +0 -0
  48. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_pressed.png +0 -0
  49. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_rest.png +0 -0
  50. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_grouphover.png +0 -0
  51. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_hover.png +0 -0
  52. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_pressed.png +0 -0
  53. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_rest.png +0 -0
  54. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_grouphover.png +0 -0
  55. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_hover.png +0 -0
  56. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_pressed.png +0 -0
  57. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_rest.png +0 -0
  58. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_grouphover.png +0 -0
  59. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_hover.png +0 -0
  60. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_pressed.png +0 -0
  61. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_rest.png +0 -0
  62. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_grouphover.png +0 -0
  63. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_hover.png +0 -0
  64. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_pressed.png +0 -0
  65. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_rest.png +0 -0
  66. plexora/client/external/openseadragon-bin-2.4.0/openseadragon-scalebar.js +562 -0
  67. plexora/client/src/css/import.css +474 -0
  68. plexora/client/src/css/main.css +903 -0
  69. plexora/client/src/css/openProject.css +363 -0
  70. plexora/client/src/css/quickView.css +166 -0
  71. plexora/client/src/css/tokens.css +51 -0
  72. plexora/client/src/css/viewer.css +1655 -0
  73. plexora/client/src/img/apple-touch-icon.png +0 -0
  74. plexora/client/src/img/favicon.ico +0 -0
  75. plexora/client/src/img/logo.ai +8892 -33
  76. plexora/client/src/img/logo.svg +1 -0
  77. plexora/client/src/img/logo_with_text.ai +9011 -34
  78. plexora/client/src/img/logo_with_text.svg +1 -0
  79. plexora/client/src/js/main.js +704 -0
  80. plexora/client/src/js/pluginRegistry.js +108 -0
  81. plexora/client/src/js/services/appStatus.js +286 -0
  82. plexora/client/src/js/services/browsePicker.js +73 -0
  83. plexora/client/src/js/services/dataLayer.js +400 -0
  84. plexora/client/src/js/services/datasetContext.js +143 -0
  85. plexora/client/src/js/services/glRenderer.js +143 -0
  86. plexora/client/src/js/services/importFormValidation.js +257 -0
  87. plexora/client/src/js/services/numericData.js +100 -0
  88. plexora/client/src/js/services/passVariablesToFrontend.js +20 -0
  89. plexora/client/src/js/services/simpleEventHandler.js +25 -0
  90. plexora/client/src/js/vendor.js +29 -0
  91. plexora/client/src/js/views/channelList.js +663 -0
  92. plexora/client/src/js/views/colorSwatchPicker.js +202 -0
  93. plexora/client/src/js/views/columnClassifier.js +146 -0
  94. plexora/client/src/js/views/coordinateField.js +133 -0
  95. plexora/client/src/js/views/dataSourceField.js +276 -0
  96. plexora/client/src/js/views/imageViewer.js +3306 -0
  97. plexora/client/src/js/views/miniMap.js +787 -0
  98. plexora/client/src/js/views/navbarControls.js +178 -0
  99. plexora/client/src/js/views/openProjectPage.js +239 -0
  100. plexora/client/src/js/views/projectEdit.js +335 -0
  101. plexora/client/src/js/views/quickViewLanding.js +136 -0
  102. plexora/client/src/js/views/rainbow.js +326 -0
  103. plexora/client/src/js/views/requirementsModal.js +572 -0
  104. plexora/client/src/js/views/rgbImageViewer.js +115 -0
  105. plexora/client/src/js/views/searchableSelect.js +411 -0
  106. plexora/client/src/js/views/segmentationProgress.js +122 -0
  107. plexora/client/src/js/views/toolLoader.js +747 -0
  108. plexora/client/src/js/views/viewerControls.js +689 -0
  109. plexora/client/src/js/views/viewerManager.js +434 -0
  110. plexora/client/src/js/views/viewerSidebar.js +1117 -0
  111. plexora/client/src/js/workers/tileDecoder.js +130 -0
  112. plexora/client/src/shaders/frag.glsl +528 -0
  113. plexora/client/src/shaders/vert.glsl +12 -0
  114. plexora/client/templates/base.html +212 -0
  115. plexora/client/templates/index.html +238 -0
  116. plexora/client/templates/open_project.html +96 -0
  117. plexora/client/templates/project_columns.html +86 -0
  118. plexora/client/templates/project_edit.html +196 -0
  119. plexora/client/templates/upload.html +118 -0
  120. plexora/connect.py +528 -0
  121. plexora/datasource.py +782 -0
  122. plexora/jupyter.py +406 -0
  123. plexora/notebook_env.py +232 -0
  124. plexora/paths.py +486 -0
  125. plexora/plugins/__init__.py +12 -0
  126. plexora/plugins/cell_explorer/__init__.py +100 -0
  127. plexora/plugins/cell_explorer/server/__init__.py +11 -0
  128. plexora/plugins/cell_explorer/server/routes.py +167 -0
  129. plexora/plugins/cell_explorer/server/state.py +281 -0
  130. plexora/plugins/cell_explorer/server/values.py +145 -0
  131. plexora/plugins/cell_explorer/server/variables.py +367 -0
  132. plexora/plugins/cell_explorer/static/cellExplorerApi.js +158 -0
  133. plexora/plugins/cell_explorer/static/cellExplorerColors.js +316 -0
  134. plexora/plugins/cell_explorer/static/cellExplorerContinuous.js +403 -0
  135. plexora/plugins/cell_explorer/static/cellExplorerFigureBridge.js +133 -0
  136. plexora/plugins/cell_explorer/static/cellExplorerLegend.js +188 -0
  137. plexora/plugins/cell_explorer/static/cellExplorerRoiBridge.js +822 -0
  138. plexora/plugins/cell_explorer/static/cellExplorerSidebarController.js +826 -0
  139. plexora/plugins/cell_explorer/static/cellExplorerState.js +372 -0
  140. plexora/plugins/cell_explorer/static/cell_explorer.css +752 -0
  141. plexora/plugins/cell_explorer/templates/cell_explorer/panel.html +137 -0
  142. plexora/plugins/figure_builder/__init__.py +125 -0
  143. plexora/plugins/figure_builder/server/__init__.py +7 -0
  144. plexora/plugins/figure_builder/server/compose.py +365 -0
  145. plexora/plugins/figure_builder/server/export.py +784 -0
  146. plexora/plugins/figure_builder/server/export_jobs.py +148 -0
  147. plexora/plugins/figure_builder/server/operations.py +556 -0
  148. plexora/plugins/figure_builder/server/pixels.py +142 -0
  149. plexora/plugins/figure_builder/server/provenance.py +174 -0
  150. plexora/plugins/figure_builder/server/render.py +278 -0
  151. plexora/plugins/figure_builder/server/repository.py +701 -0
  152. plexora/plugins/figure_builder/server/routes.py +594 -0
  153. plexora/plugins/figure_builder/server/schema.py +1023 -0
  154. plexora/plugins/figure_builder/server/sources.py +120 -0
  155. plexora/plugins/figure_builder/server/textmetrics.py +155 -0
  156. plexora/plugins/figure_builder/static/figureActions.js +363 -0
  157. plexora/plugins/figure_builder/static/figureBuilderApi.js +255 -0
  158. plexora/plugins/figure_builder/static/figureCanvas.js +2362 -0
  159. plexora/plugins/figure_builder/static/figureCaptureBoxes.js +369 -0
  160. plexora/plugins/figure_builder/static/figureCaptureDock.js +466 -0
  161. plexora/plugins/figure_builder/static/figureCaptureTool.js +1034 -0
  162. plexora/plugins/figure_builder/static/figureConfirm.js +136 -0
  163. plexora/plugins/figure_builder/static/figureContextBar.js +1193 -0
  164. plexora/plugins/figure_builder/static/figureContextMenu.js +253 -0
  165. plexora/plugins/figure_builder/static/figureDocumentState.js +275 -0
  166. plexora/plugins/figure_builder/static/figureExportUi.js +174 -0
  167. plexora/plugins/figure_builder/static/figureLibrary.js +247 -0
  168. plexora/plugins/figure_builder/static/figureQuickEdit.js +628 -0
  169. plexora/plugins/figure_builder/static/figureRichText.js +634 -0
  170. plexora/plugins/figure_builder/static/figureSceneSnapshot.js +361 -0
  171. plexora/plugins/figure_builder/static/figureSchema.js +218 -0
  172. plexora/plugins/figure_builder/static/figureSidebarController.js +1159 -0
  173. plexora/plugins/figure_builder/static/figureTextEditor.js +503 -0
  174. plexora/plugins/figure_builder/static/figureTextPanel.js +491 -0
  175. plexora/plugins/figure_builder/static/figureViewOptions.js +273 -0
  176. plexora/plugins/figure_builder/static/figureWorkspace.js +2030 -0
  177. plexora/plugins/figure_builder/static/figure_builder.css +3292 -0
  178. plexora/plugins/figure_builder/templates/figure_builder/library.html +74 -0
  179. plexora/plugins/figure_builder/templates/figure_builder/workspace.html +27 -0
  180. plexora/plugins/figure_builder/templates/figure_builder/workspace_body.html +458 -0
  181. plexora/plugins/gating/__init__.py +76 -0
  182. plexora/plugins/gating/server/__init__.py +0 -0
  183. plexora/plugins/gating/server/anndata_gates.py +367 -0
  184. plexora/plugins/gating/server/database.py +16 -0
  185. plexora/plugins/gating/server/model.py +320 -0
  186. plexora/plugins/gating/server/routes.py +235 -0
  187. plexora/plugins/gating/static/csvGatingList.js +947 -0
  188. plexora/plugins/gating/static/gating.css +289 -0
  189. plexora/plugins/gating/static/gatingApi.js +221 -0
  190. plexora/plugins/gating/static/gatingSidebarController.js +513 -0
  191. plexora/plugins/gating/templates/gating/legacy.html +26 -0
  192. plexora/plugins/gating/templates/gating/panel.html +69 -0
  193. plexora/plugins/roi/__init__.py +95 -0
  194. plexora/plugins/roi/server/__init__.py +8 -0
  195. plexora/plugins/roi/server/adapters.py +655 -0
  196. plexora/plugins/roi/server/geojson.py +267 -0
  197. plexora/plugins/roi/server/geometry.py +181 -0
  198. plexora/plugins/roi/server/mapping.py +169 -0
  199. plexora/plugins/roi/server/operations.py +293 -0
  200. plexora/plugins/roi/server/repository.py +215 -0
  201. plexora/plugins/roi/server/routes.py +409 -0
  202. plexora/plugins/roi/server/schema.py +323 -0
  203. plexora/plugins/roi/static/roi.css +583 -0
  204. plexora/plugins/roi/static/roiApi.js +177 -0
  205. plexora/plugins/roi/static/roiFigureBridge.js +107 -0
  206. plexora/plugins/roi/static/roiGeometry.js +350 -0
  207. plexora/plugins/roi/static/roiRenderer.js +278 -0
  208. plexora/plugins/roi/static/roiSidebarController.js +1038 -0
  209. plexora/plugins/roi/static/roiState.js +518 -0
  210. plexora/plugins/roi/static/roiTools.js +993 -0
  211. plexora/plugins/roi/templates/roi/panel.html +240 -0
  212. plexora/proxy.py +57 -0
  213. plexora/server/models/adapters/__init__.py +94 -0
  214. plexora/server/models/adapters/anndata_adapter.py +388 -0
  215. plexora/server/models/adapters/base.py +93 -0
  216. plexora/server/models/adapters/classify.py +192 -0
  217. plexora/server/models/adapters/csv_adapter.py +105 -0
  218. plexora/server/models/adapters/inspection.py +265 -0
  219. plexora/server/models/adapters/spatialdata_adapter.py +248 -0
  220. plexora/server/models/centroid_tiles.py +305 -0
  221. plexora/server/models/data_model.py +1659 -0
  222. plexora/server/models/database_model.py +169 -0
  223. plexora/server/models/project.py +1270 -0
  224. plexora/server/plugins.py +199 -0
  225. plexora/server/routes/browse_routes.py +41 -0
  226. plexora/server/routes/data_routes.py +294 -0
  227. plexora/server/routes/import_routes.py +603 -0
  228. plexora/server/routes/page_routes.py +139 -0
  229. plexora/server/routes/project_routes.py +438 -0
  230. plexora/server/routes/quick_view_routes.py +62 -0
  231. plexora/server/routes/system_routes.py +39 -0
  232. plexora/server/routes/tool_routes.py +418 -0
  233. plexora/server/utils/addHEColumns.py +11 -0
  234. plexora/server/utils/fast_png.py +97 -0
  235. plexora/server/utils/fullConversion.py +50 -0
  236. plexora/server/utils/native_dialog.py +168 -0
  237. plexora/server/utils/pre_normalization.py +47 -0
  238. plexora/server/utils/segmentation_pyramid.py +598 -0
  239. plexora/server/utils/smallestenclosingcircle.py +126 -0
  240. plexora/server/utils/tiffsurgeon.py +372 -0
  241. plexora/server_cli.py +48 -0
  242. plexora/test.py +0 -0
  243. plexora-0.0.1.dist-info/METADATA +341 -0
  244. plexora-0.0.1.dist-info/RECORD +248 -0
  245. plexora-0.0.1.dist-info/WHEEL +5 -0
  246. plexora-0.0.1.dist-info/entry_points.txt +12 -0
  247. plexora-0.0.1.dist-info/licenses/LICENSE +207 -0
  248. 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&hellip;</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&hellip;
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&hellip;" 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 &mdash;
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&hellip;</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 &mdash; 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 &mdash; text and legends stay editable</option>
115
+ <option value="tiff">TIFF &mdash; flattened, for submission</option>
116
+ <option value="png">PNG &mdash; 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 &mdash; probably another tab &mdash; 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">&middot; <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 &middot; <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