plexora 0.0.7__tar.gz → 0.0.8__tar.gz
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-0.0.7/plexora.egg-info → plexora-0.0.8}/PKG-INFO +1 -1
- {plexora-0.0.7 → plexora-0.0.8}/plexora/askpass.py +27 -1
- {plexora-0.0.7 → plexora-0.0.8}/plexora/cli.py +21 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/css/main.css +82 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/css/settings.css +66 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/connectionModal.js +87 -10
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/remoteGlobe.js +9 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/remoteState.js +13 -2
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/settingsPage.js +18 -2
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/templates/base.html +4 -4
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/templates/settings.html +26 -4
- {plexora-0.0.7 → plexora-0.0.8}/plexora/connect.py +366 -10
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/roi/__init__.py +100 -100
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/models/data_model.py +53 -5
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/models/nodes.py +53 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/models/recipes.py +51 -24
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/models/remote_sessions.py +160 -9
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/models/remotes.py +26 -1
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/node/api.py +92 -4
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/providers/__init__.py +21 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/providers/node.py +33 -4
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/routes/settings_routes.py +48 -1
- {plexora-0.0.7 → plexora-0.0.8/plexora.egg-info}/PKG-INFO +1 -1
- {plexora-0.0.7 → plexora-0.0.8}/plexora.egg-info/SOURCES.txt +1 -0
- {plexora-0.0.7 → plexora-0.0.8}/pyproject.toml +1 -1
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_cli.py +16 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_connect.py +212 -1
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_connection_modal.py +44 -0
- plexora-0.0.8/tests/test_node_reconnect.py +255 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_node_warm_and_cache.py +174 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_recipes.py +23 -7
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_remote_connect.py +284 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_settings_remotes_page.py +109 -0
- {plexora-0.0.7 → plexora-0.0.8}/LICENSE +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/MANIFEST.in +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/README.md +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/__init__.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/__main__.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/_resources.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/_url.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/api/__init__.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/api/dataset.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/api/http.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/api/plugin.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/api/store.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/dist/354_bundle.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/dist/354_bundle.js.LICENSE.txt +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/dist/418_bundle.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/dist/418_bundle.js.LICENSE.txt +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/dist/693b935cb1f907814be9c6199a68c217.svg +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/dist/713c134ff47cd328a5b711526f151082.svg +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/dist/770_bundle.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/dist/ff743f408972e0e96ca9153ed5ae4c83.svg +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/dist/vendor_bundle.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/dist/vendor_bundle.js.LICENSE.txt +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/LICENSE.txt +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/canvas-overlay-hd.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/changelog.txt +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/.gitattributes +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/LICENSE.txt +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/Toolbar.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/fullpage.psd +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/home.psd +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/next.psd +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/previous.psd +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateleft.psd +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateright.psd +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/zoom.psd +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/README.md +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_grouphover.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_hover.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_pressed.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_rest.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_grouphover.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_hover.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_pressed.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_rest.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_grouphover.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_hover.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_pressed.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_rest.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_grouphover.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_hover.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_pressed.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_rest.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_grouphover.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_hover.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_pressed.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_rest.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_grouphover.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_hover.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_pressed.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_rest.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_grouphover.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_hover.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_pressed.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_rest.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_grouphover.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_hover.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_pressed.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_rest.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/external/openseadragon-bin-2.4.0/openseadragon-scalebar.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/css/import.css +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/css/openProject.css +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/css/quickView.css +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/css/tokens.css +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/css/viewer.css +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/img/apple-touch-icon.png +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/img/favicon.ico +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/img/logo.ai +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/img/logo.svg +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/img/logo_with_text.ai +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/img/logo_with_text.svg +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/main.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/pluginRegistry.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/appRouter.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/appStatus.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/browsePicker.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/dataLayer.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/dataLocation.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/datasetContext.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/fileLocation.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/glRenderer.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/importFormValidation.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/keyboardShortcuts.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/logTerminal.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/numericData.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/pageBoot.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/passVariablesToFrontend.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/pathPicker.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/placePicker.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/resourceRouting.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/resourceStatus.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/sessionExpiry.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/services/simpleEventHandler.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/vendor.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/channelList.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/channelNamesUpload.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/colorSwatchPicker.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/columnClassifier.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/coordinateField.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/dataSourceField.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/imageViewer.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/miniMap.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/navbarControls.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/openProjectPage.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/popoverPortal.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/projectEdit.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/quickViewLanding.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/rainbow.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/requirementsModal.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/rgbImageViewer.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/searchableSelect.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/segmentationProgress.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/segmentationWait.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/toolLoader.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/viewerControls.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/viewerManager.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/views/viewerSidebar.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/js/workers/tileDecoder.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/shaders/frag.glsl +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/src/shaders/vert.glsl +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/templates/_fragment.html +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/templates/index.html +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/templates/open_project.html +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/templates/project_columns.html +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/templates/project_edit.html +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/client/templates/upload.html +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/datasource.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/jupyter.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/nodes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/notebook_env.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/paths.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/__init__.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/cell_explorer/__init__.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/cell_explorer/server/__init__.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/cell_explorer/server/routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/cell_explorer/server/state.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/cell_explorer/server/values.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/cell_explorer/server/variables.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/cell_explorer/static/cellExplorerApi.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/cell_explorer/static/cellExplorerColors.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/cell_explorer/static/cellExplorerContinuous.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/cell_explorer/static/cellExplorerFigureBridge.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/cell_explorer/static/cellExplorerLegend.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/cell_explorer/static/cellExplorerRoiBridge.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/cell_explorer/static/cellExplorerSidebarController.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/cell_explorer/static/cellExplorerState.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/cell_explorer/static/cell_explorer.css +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/cell_explorer/templates/cell_explorer/panel.html +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/__init__.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/server/__init__.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/server/compose.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/server/export.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/server/export_jobs.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/server/operations.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/server/pixels.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/server/provenance.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/server/render.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/server/repository.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/server/routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/server/schema.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/server/shapegeom.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/server/sources.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/server/strokegeom.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/server/textmetrics.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureActions.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureBuilderApi.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureCanvas.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureCaptureBoxes.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureCaptureDock.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureCaptureTool.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureChoiceField.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureColorField.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureConfirm.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureContextBar.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureContextMenu.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureDocumentState.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureExportUi.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureImagePanel.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureLibrary.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureLineDefs.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureLinePanel.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figurePanelCompositor.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figurePointEditor.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureQuickEdit.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureRichText.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureSceneSnapshot.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureSchema.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureShapeDefs.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureShapeDrawing.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureShapeGeometry.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureShapePanel.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureSidebarController.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureStrokeGeometry.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureTextEditor.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureTextPanel.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureViewOptions.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figureWorkspace.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/static/figure_builder.css +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/templates/figure_builder/library.html +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/templates/figure_builder/workspace.html +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/figure_builder/templates/figure_builder/workspace_body.html +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/gating/__init__.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/gating/server/__init__.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/gating/server/anndata_gates.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/gating/server/database.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/gating/server/model.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/gating/server/routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/gating/server/tableops.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/gating/static/csvGatingList.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/gating/static/gating.css +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/gating/static/gatingApi.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/gating/static/gatingSidebarController.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/gating/templates/gating/legacy.html +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/gating/templates/gating/panel.html +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/roi/server/__init__.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/roi/server/adapters.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/roi/server/geojson.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/roi/server/geometry.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/roi/server/mapping.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/roi/server/operations.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/roi/server/repository.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/roi/server/routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/roi/server/schema.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/roi/server/tableops.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/roi/static/roi.css +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/roi/static/roiApi.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/roi/static/roiFigureBridge.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/roi/static/roiGeometry.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/roi/static/roiRenderer.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/roi/static/roiSidebarController.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/roi/static/roiState.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/roi/static/roiTools.js +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/plugins/roi/templates/roi/panel.html +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/proxy.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/models/adapters/__init__.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/models/adapters/anndata_adapter.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/models/adapters/base.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/models/adapters/classify.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/models/adapters/csv_adapter.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/models/adapters/inspection.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/models/adapters/spatialdata_adapter.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/models/centroid_tiles.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/models/data_migration.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/models/database_model.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/models/project.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/models/secret_store.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/node/app.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/node/resources.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/plugins.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/providers/base.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/providers/http.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/providers/local.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/providers/operations.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/providers/wire.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/routes/browse_routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/routes/data_routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/routes/import_routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/routes/page_routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/routes/project_routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/routes/quick_view_routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/routes/system_routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/routes/tool_routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/routes/transfer_routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/utils/addHEColumns.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/utils/channel_file.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/utils/dir_listing.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/utils/fast_png.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/utils/file_transfer.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/utils/fullConversion.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/utils/native_dialog.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/utils/pre_normalization.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/utils/segmentation_pyramid.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/utils/smallestenclosingcircle.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server/utils/tiffsurgeon.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/server_cli.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora/test.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora.egg-info/dependency_links.txt +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora.egg-info/entry_points.txt +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora.egg-info/requires.txt +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/plexora.egg-info/top_level.txt +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/setup.cfg +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_anndata_adapter.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_api_dataset.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_api_store.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_app_router.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_app_shell.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_auth_token.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_browse_routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_cell_color_layer.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_cell_mode_control.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_centroid_tiles.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_channel_names_modal.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_channel_names_upload.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_channel_overview.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_channel_rename_state.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_classify_columns.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_column_classifier_css.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_config_store.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_csv_adapter.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_data_location.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_database_model.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_datalayer_requests.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_derive_anndata_channel_names.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_detect_data_type.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_file_location.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_file_transfer_node.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_hd_slider_domain.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_icon_names.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_jupyter_viewer.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_label_outlines.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_label_tile_lifecycle.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_marker_split.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_mask_without_table.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_metadata_columns.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_mini_map.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_nav_shortcuts.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_node_direct_routing.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_node_disconnect.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_node_dynamic.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_node_image.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_node_routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_node_table.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_notebook_env.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_notebook_mode.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_optional_segmentation.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_page_assets.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_page_routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_path_picker.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_paths.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_plugin_boundary.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_plugin_css_boundary.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_plugins.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_popover_portal.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_project_edit_routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_project_record.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_quick_view_routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_reconnect.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_refresh_dataset.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_register_anndata_datasource.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_register_image_datasource.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_remote_globe.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_remote_health.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_remote_state.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_rename_channels.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_requirements_routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_resource_status_banner.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_segmentation_mapping.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_segmentation_mode_routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_segmentation_pyramid.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_segmentation_wait.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_session_expiry.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_session_walltime.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_settings_page.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_shared_roots.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_spatialdata_adapter.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_tile_url.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_tool_assets.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_tool_cards.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_tool_coexistence.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_tool_switching.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_transfer_routes.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_unreachable_node.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_url_helpers.py +0 -0
- {plexora-0.0.7 → plexora-0.0.8}/tests/test_view_menu.py +0 -0
|
@@ -87,6 +87,31 @@ def _request(url, payload=None, timeout=10):
|
|
|
87
87
|
return json.loads(body) if body.strip() else {}
|
|
88
88
|
|
|
89
89
|
|
|
90
|
+
def asking_process():
|
|
91
|
+
"""Which ssh is asking, or None when that cannot be known.
|
|
92
|
+
|
|
93
|
+
One connection to a cluster runs three ssh processes and two of them
|
|
94
|
+
authenticate to the SAME host, so they ask the same question word for word.
|
|
95
|
+
Plexora reuses an answer across hops but must not reuse one that a hop has
|
|
96
|
+
already refused, and those two cases are only distinguishable by which
|
|
97
|
+
process is asking -- identical text otherwise.
|
|
98
|
+
|
|
99
|
+
The POSIX helper `exec`s this interpreter, so this process's parent IS the
|
|
100
|
+
ssh that wants an answer, and it stays the same pid across that ssh's own
|
|
101
|
+
retries. The Windows wrapper is a .bat and cannot exec, so the parent there
|
|
102
|
+
is a transient cmd.exe that would look like a new asker every time --
|
|
103
|
+
exactly the wrong way round. So Windows reports nothing and Plexora falls
|
|
104
|
+
back to treating any repeated question as a refusal, which costs a person
|
|
105
|
+
one extra typing and never replays a rejected secret.
|
|
106
|
+
"""
|
|
107
|
+
if os.name == "nt":
|
|
108
|
+
return None
|
|
109
|
+
try:
|
|
110
|
+
return f"pid:{os.getppid()}"
|
|
111
|
+
except OSError:
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
|
|
90
115
|
def ask(prompt, env=None):
|
|
91
116
|
"""Post `prompt` back to Plexora and block until somebody answers it.
|
|
92
117
|
|
|
@@ -107,7 +132,8 @@ def ask(prompt, env=None):
|
|
|
107
132
|
timeout = DEFAULT_TIMEOUT
|
|
108
133
|
|
|
109
134
|
opened = _request(_url(base, "prompt", token),
|
|
110
|
-
{"nonce": nonce, "prompt": str(prompt)
|
|
135
|
+
{"nonce": nonce, "prompt": str(prompt),
|
|
136
|
+
"asker": asking_process()})
|
|
111
137
|
prompt_id = opened.get("id")
|
|
112
138
|
if not prompt_id:
|
|
113
139
|
raise AskpassError(opened.get("error") or "Plexora refused the prompt")
|
|
@@ -948,6 +948,10 @@ def _build_node_parser():
|
|
|
948
948
|
help="Seconds to wait for the node to answer. Raise it when a "
|
|
949
949
|
"segmentation mask has to be converted first.",
|
|
950
950
|
)
|
|
951
|
+
node_connect.add_argument(
|
|
952
|
+
"--install", action="store_true",
|
|
953
|
+
help="Install or update Plexora on that host first: `pip install "
|
|
954
|
+
"--upgrade plexora`, in the environment --remote-command names.")
|
|
951
955
|
node_connect.add_argument("--plugins", default=None,
|
|
952
956
|
help="Plugins the node should be able to run.")
|
|
953
957
|
|
|
@@ -1000,6 +1004,7 @@ def _run_node_connect(args):
|
|
|
1000
1004
|
remote_port=args.remote_port,
|
|
1001
1005
|
timeout=args.timeout,
|
|
1002
1006
|
plugins=args.plugins,
|
|
1007
|
+
install=bool(getattr(args, "install", False)),
|
|
1003
1008
|
register=register,
|
|
1004
1009
|
)
|
|
1005
1010
|
|
|
@@ -1169,6 +1174,15 @@ def _build_connect_parser():
|
|
|
1169
1174
|
"where the images are on the cluster and the cell table never "
|
|
1170
1175
|
"left your laptop. Repeat for each.",
|
|
1171
1176
|
)
|
|
1177
|
+
connect.add_argument(
|
|
1178
|
+
"--install",
|
|
1179
|
+
action="store_true",
|
|
1180
|
+
help="Install or update Plexora on the remote host before launching "
|
|
1181
|
+
"it: `pip install --upgrade plexora`, in the environment "
|
|
1182
|
+
"--remote-command names. Off by default, because it writes to "
|
|
1183
|
+
"that machine. Saved with --save, and the same switch the "
|
|
1184
|
+
"Settings form calls \"Install or update Plexora\".",
|
|
1185
|
+
)
|
|
1172
1186
|
connect.add_argument(
|
|
1173
1187
|
"--no-local-node",
|
|
1174
1188
|
action="store_true",
|
|
@@ -1291,6 +1305,12 @@ def connect_kwargs(args, profile=None):
|
|
|
1291
1305
|
or (profile.name if profile is not None else None)),
|
|
1292
1306
|
"node_port": args.node_port,
|
|
1293
1307
|
"local_node": not args.no_local_node,
|
|
1308
|
+
# An opt-in either way round, so the flag does not have to beat a saved
|
|
1309
|
+
# value: `--install` on the command line, or a profile that was saved
|
|
1310
|
+
# with it on. Nothing here can turn it off for one run, which is the
|
|
1311
|
+
# right asymmetry -- forgetting to install is a stale Plexora, and
|
|
1312
|
+
# installing by surprise is a write to somebody's account.
|
|
1313
|
+
"install": bool(getattr(args, "install", False) or saved("install", False)),
|
|
1294
1314
|
}
|
|
1295
1315
|
|
|
1296
1316
|
|
|
@@ -1334,6 +1354,7 @@ def _save_remote(name, target, kwargs):
|
|
|
1334
1354
|
serve=tuple(kwargs["also_serve"]),
|
|
1335
1355
|
local_serve=tuple(kwargs["local_serve"]),
|
|
1336
1356
|
node_name=kwargs["node_name"],
|
|
1357
|
+
install=kwargs["install"],
|
|
1337
1358
|
))
|
|
1338
1359
|
print(f"Saved as {name!r}. Next time: plexora connect {name}")
|
|
1339
1360
|
|
|
@@ -3090,6 +3090,88 @@ nav {
|
|
|
3090
3090
|
color: var(--text-muted);
|
|
3091
3091
|
}
|
|
3092
3092
|
|
|
3093
|
+
/* A switch shaped as one of the form's own grid cells, so the grid places it
|
|
3094
|
+
beside the box above wherever the dialog is wide enough for two columns.
|
|
3095
|
+
Everything about it that is not layout — the track, the thumb, the travel,
|
|
3096
|
+
the focus ring — is the Settings page's switch at the same sizes, because
|
|
3097
|
+
these are the same control in two places and a user who has seen one should
|
|
3098
|
+
recognise the other. */
|
|
3099
|
+
.connect-switch {
|
|
3100
|
+
flex-direction: row;
|
|
3101
|
+
align-items: flex-start;
|
|
3102
|
+
justify-content: space-between;
|
|
3103
|
+
gap: var(--space-3);
|
|
3104
|
+
margin: 0;
|
|
3105
|
+
cursor: pointer;
|
|
3106
|
+
}
|
|
3107
|
+
|
|
3108
|
+
/* Left alone where the grid has already paired it with the box above -- and
|
|
3109
|
+
widened to the whole row where it would otherwise sit alone in the left
|
|
3110
|
+
column with the toggle stranded mid-dialog. `:last-child:nth-child(odd)` is
|
|
3111
|
+
exactly "the odd one out of a two-column grid"; in the one-column layout a
|
|
3112
|
+
narrow dialog falls back to, every cell spans anyway and this changes
|
|
3113
|
+
nothing. */
|
|
3114
|
+
.connect-form > .connect-switch:last-child:nth-child(odd) {
|
|
3115
|
+
grid-column: 1 / -1;
|
|
3116
|
+
}
|
|
3117
|
+
|
|
3118
|
+
.connect-switch-text {
|
|
3119
|
+
display: flex;
|
|
3120
|
+
flex-direction: column;
|
|
3121
|
+
gap: var(--space-1);
|
|
3122
|
+
min-width: 0;
|
|
3123
|
+
}
|
|
3124
|
+
|
|
3125
|
+
/* Focusable but not visible: the track is the control a mouse sees, and the
|
|
3126
|
+
input is the one the keyboard and the accessibility tree see. */
|
|
3127
|
+
.connect-switch-input {
|
|
3128
|
+
position: absolute;
|
|
3129
|
+
width: 1px;
|
|
3130
|
+
height: 1px;
|
|
3131
|
+
opacity: 0;
|
|
3132
|
+
margin: 0;
|
|
3133
|
+
pointer-events: none;
|
|
3134
|
+
}
|
|
3135
|
+
|
|
3136
|
+
.connect-switch-track {
|
|
3137
|
+
flex: none;
|
|
3138
|
+
position: relative;
|
|
3139
|
+
width: 32px;
|
|
3140
|
+
height: 19px;
|
|
3141
|
+
margin-top: 1px;
|
|
3142
|
+
background: var(--surface-2);
|
|
3143
|
+
border: 1px solid var(--border-subtle);
|
|
3144
|
+
border-radius: 999px;
|
|
3145
|
+
transition: background var(--duration-fast) var(--ease-standard),
|
|
3146
|
+
border-color var(--duration-fast) var(--ease-standard);
|
|
3147
|
+
}
|
|
3148
|
+
|
|
3149
|
+
.connect-switch-thumb {
|
|
3150
|
+
position: absolute;
|
|
3151
|
+
top: 2px;
|
|
3152
|
+
left: 2px;
|
|
3153
|
+
width: 13px;
|
|
3154
|
+
height: 13px;
|
|
3155
|
+
background: var(--text-muted);
|
|
3156
|
+
border-radius: 50%;
|
|
3157
|
+
transition: transform var(--duration-fast) var(--ease-standard),
|
|
3158
|
+
background var(--duration-fast) var(--ease-standard);
|
|
3159
|
+
}
|
|
3160
|
+
|
|
3161
|
+
.connect-switch-input:checked + .connect-switch-track {
|
|
3162
|
+
background: var(--accent-channel);
|
|
3163
|
+
border-color: var(--accent-channel);
|
|
3164
|
+
}
|
|
3165
|
+
|
|
3166
|
+
.connect-switch-input:checked + .connect-switch-track .connect-switch-thumb {
|
|
3167
|
+
background: var(--surface-0);
|
|
3168
|
+
transform: translateX(13px);
|
|
3169
|
+
}
|
|
3170
|
+
|
|
3171
|
+
.connect-switch-input:focus-visible + .connect-switch-track {
|
|
3172
|
+
box-shadow: 0 0 0 3px var(--accent-channel-soft);
|
|
3173
|
+
}
|
|
3174
|
+
|
|
3093
3175
|
/* Shut on arrival. What is inside is the escape hatch for a preset that is
|
|
3094
3176
|
almost right — a different partition, a `plexora` ssh cannot find — and
|
|
3095
3177
|
opening it by default would put a cluster's flag syntax in front of the
|
|
@@ -815,6 +815,21 @@
|
|
|
815
815
|
margin-bottom: var(--space-4);
|
|
816
816
|
}
|
|
817
817
|
|
|
818
|
+
/* A group title with a control on the end of it. The row keeps the title's own
|
|
819
|
+
bottom margin so the form below sits where it always did -- the switch is a
|
|
820
|
+
passenger on an existing row, not a row of its own. */
|
|
821
|
+
.remote-group-head {
|
|
822
|
+
display: flex;
|
|
823
|
+
align-items: center;
|
|
824
|
+
justify-content: space-between;
|
|
825
|
+
gap: var(--space-4);
|
|
826
|
+
margin-bottom: var(--space-4);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
.remote-group-head .remote-group-title {
|
|
830
|
+
margin-bottom: 0;
|
|
831
|
+
}
|
|
832
|
+
|
|
818
833
|
.remote-examples,
|
|
819
834
|
.remote-ports {
|
|
820
835
|
font-size: 0.8rem;
|
|
@@ -922,6 +937,57 @@
|
|
|
922
937
|
box-shadow: 0 0 0 3px var(--accent-channel-soft);
|
|
923
938
|
}
|
|
924
939
|
|
|
940
|
+
/* The same switch, sized to sit on a line of text rather than to own a block.
|
|
941
|
+
Everything it does not restate -- the track's colours, the thumb's travel,
|
|
942
|
+
the focus ring -- is inherited from the rules above, so the two cannot drift
|
|
943
|
+
into looking like two different controls.
|
|
944
|
+
|
|
945
|
+
`flex: none` and `align-items: center` are what make it a passenger: the
|
|
946
|
+
full-width version stretches and top-aligns because it carries a label and a
|
|
947
|
+
sentence of hint, and this one carries three words. */
|
|
948
|
+
.remote-switch-inline {
|
|
949
|
+
flex: none;
|
|
950
|
+
align-items: center;
|
|
951
|
+
justify-content: flex-start;
|
|
952
|
+
gap: var(--space-3);
|
|
953
|
+
padding: 0;
|
|
954
|
+
white-space: nowrap;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
.remote-switch-inline .remote-switch-label {
|
|
958
|
+
font-size: 0.8rem;
|
|
959
|
+
color: var(--text-secondary);
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
.remote-switch-inline .remote-switch-track {
|
|
963
|
+
width: 32px;
|
|
964
|
+
height: 19px;
|
|
965
|
+
margin-top: 0;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
.remote-switch-inline .remote-switch-thumb {
|
|
969
|
+
top: 2px;
|
|
970
|
+
left: 2px;
|
|
971
|
+
width: 13px;
|
|
972
|
+
height: 13px;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
.remote-switch-inline .remote-switch-input:checked
|
|
976
|
+
+ .remote-switch-track .remote-switch-thumb {
|
|
977
|
+
transform: translateX(13px);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
/* Narrow cards put the title and the switch on two lines rather than letting
|
|
981
|
+
either shrink: the title is uppercase and letter-spaced, so it wraps badly,
|
|
982
|
+
and the switch label is the thing being turned on. */
|
|
983
|
+
@media (max-width: 520px) {
|
|
984
|
+
.remote-group-head {
|
|
985
|
+
flex-direction: column;
|
|
986
|
+
align-items: flex-start;
|
|
987
|
+
gap: var(--space-2);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
|
|
925
991
|
/* -- what a job asks for ------------------------------------------------- */
|
|
926
992
|
|
|
927
993
|
.remote-reveal {
|
|
@@ -41,19 +41,38 @@ window.PlexoraConnectionModal = (function () {
|
|
|
41
41
|
|
|
42
42
|
const Remotes = () => window.PlexoraRemotes;
|
|
43
43
|
|
|
44
|
-
//: The
|
|
45
|
-
//:
|
|
46
|
-
//:
|
|
47
|
-
//:
|
|
44
|
+
//: The things a connection does, in order, with what each is called while
|
|
45
|
+
//: it is happening. Two of them are conditional, and each is drawn only
|
|
46
|
+
//: for a profile that actually does it: `queued` for one whose Plexora
|
|
47
|
+
//: runs inside a scheduler job, `install` for one with "install or update
|
|
48
|
+
//: Plexora" switched on. Drawing either unconditionally would promise a
|
|
49
|
+
//: wait that is never coming.
|
|
50
|
+
//:
|
|
51
|
+
//: Installing sits where it does because that is where it runs -- after
|
|
52
|
+
//: the login and before anything is launched, so that what starts
|
|
53
|
+
//: afterwards is the version it just installed.
|
|
48
54
|
const STEPS = [
|
|
49
55
|
{ state: "connecting", label: "Reaching the machine" },
|
|
50
56
|
{ state: "authenticating", label: "Signing in" },
|
|
57
|
+
{ state: "installing", label: "Installing Plexora", install: true },
|
|
51
58
|
{ state: "waiting_for_job", label: "Waiting for the scheduler",
|
|
52
59
|
queued: true },
|
|
53
60
|
{ state: "tunneling", label: "Opening the tunnel" },
|
|
54
61
|
{ state: "waiting_for_app", label: "Starting Plexora" },
|
|
55
62
|
];
|
|
56
63
|
|
|
64
|
+
//: What the install step is called when the launch command names an
|
|
65
|
+
//: environment. The name comes from the server -- one reading of one field
|
|
66
|
+
//: (`connect.environment_label`), the same one `pip` will be run through
|
|
67
|
+
//: -- so a step cannot promise an environment the install does not write
|
|
68
|
+
//: to. Without a name it stays the plain label: "in undefined" would be
|
|
69
|
+
//: worse than saying nothing, and a `module load` line genuinely has no
|
|
70
|
+
//: name to give.
|
|
71
|
+
function installLabel(environment) {
|
|
72
|
+
return environment ? "Installing Plexora in " + environment
|
|
73
|
+
: "Installing Plexora";
|
|
74
|
+
}
|
|
75
|
+
|
|
57
76
|
//: What the last step is called for a data node, which does not start a
|
|
58
77
|
//: viewer at all. The step list is otherwise identical because the login
|
|
59
78
|
//: is identical -- same profile, same ssh, same password.
|
|
@@ -89,14 +108,23 @@ window.PlexoraConnectionModal = (function () {
|
|
|
89
108
|
* was running -- and marking the whole list pending would throw away the
|
|
90
109
|
* most useful thing on screen, which is how far it got.
|
|
91
110
|
*/
|
|
92
|
-
function stepStates(state, kind, queued, lastOpening) {
|
|
93
|
-
const steps = STEPS.filter((step) => !step.queued || queued)
|
|
111
|
+
function stepStates(state, kind, queued, lastOpening, install) {
|
|
112
|
+
const steps = STEPS.filter((step) => (!step.queued || queued)
|
|
113
|
+
&& (!step.install || Boolean(install)));
|
|
94
114
|
const failed = state === "failed" || state === "exited";
|
|
95
115
|
const at = steps.findIndex(
|
|
96
116
|
(step) => step.state === (failed ? lastOpening : state));
|
|
97
117
|
return steps.map((step, index) => {
|
|
98
|
-
|
|
99
|
-
|
|
118
|
+
let label = step.label;
|
|
119
|
+
if (step.state === "waiting_for_app" && kind === "node") {
|
|
120
|
+
label = NODE_LAST_STEP;
|
|
121
|
+
} else if (step.state === "installing") {
|
|
122
|
+
// `install` doubles as the environment's name when there is
|
|
123
|
+
// one: the caller has a profile, not two separate answers, and
|
|
124
|
+
// a second parameter that could only ever be set alongside
|
|
125
|
+
// this one is a pair that can be passed inconsistently.
|
|
126
|
+
label = installLabel(typeof install === "string" ? install : "");
|
|
127
|
+
}
|
|
100
128
|
let status = "pending";
|
|
101
129
|
if (state === "connected") status = "done";
|
|
102
130
|
else if (at < 0) status = "pending";
|
|
@@ -323,7 +351,14 @@ window.PlexoraConnectionModal = (function () {
|
|
|
323
351
|
|
|
324
352
|
function paintSteps(state, entry, phase, error) {
|
|
325
353
|
stepsEl.replaceChildren();
|
|
326
|
-
|
|
354
|
+
// The environment's name when the profile has one, `true` when it
|
|
355
|
+
// installs without naming one, `false` when it does not install.
|
|
356
|
+
// See stepStates: one value, because "does it install" and "into
|
|
357
|
+
// what" are one fact about one profile.
|
|
358
|
+
const installing = !entry || !entry.install ? false
|
|
359
|
+
: (entry.installEnv || true);
|
|
360
|
+
stepStates(state, kind, entry ? entry.queued : false, lastOpening,
|
|
361
|
+
installing)
|
|
327
362
|
.forEach((step) => {
|
|
328
363
|
const item = el("li", "connect-step is-" + step.status);
|
|
329
364
|
item.append(el("span", "connect-step-mark"));
|
|
@@ -627,6 +662,36 @@ window.PlexoraConnectionModal = (function () {
|
|
|
627
662
|
return wrap;
|
|
628
663
|
}
|
|
629
664
|
|
|
665
|
+
/**
|
|
666
|
+
* A switch, shaped so the form's own grid places it.
|
|
667
|
+
*
|
|
668
|
+
* `.connect-form` is `repeat(auto-fit, minmax(14rem, 1fr))`, so
|
|
669
|
+
* this is a cell like any other: it sits beside the box above it
|
|
670
|
+
* wherever the dialog is wide enough for two columns, and wraps
|
|
671
|
+
* under it where it is not. That is the whole of "inline when
|
|
672
|
+
* there is room" -- there is no row here to be given one.
|
|
673
|
+
*/
|
|
674
|
+
function switchField(key, label, hint, initial) {
|
|
675
|
+
const wrap = el("label", "connect-field connect-switch");
|
|
676
|
+
const text = el("span", "connect-switch-text");
|
|
677
|
+
text.append(el("span", "connect-field-label", label));
|
|
678
|
+
if (hint) text.append(el("span", "connect-field-hint", hint));
|
|
679
|
+
const input = el("input", "connect-switch-input");
|
|
680
|
+
input.type = "checkbox";
|
|
681
|
+
input.checked = Boolean(initial);
|
|
682
|
+
const track = el("span", "connect-switch-track");
|
|
683
|
+
track.setAttribute("aria-hidden", "true");
|
|
684
|
+
track.append(el("span", "connect-switch-thumb"));
|
|
685
|
+
wrap.append(text, input, track);
|
|
686
|
+
// Read by the same loop that reads every text box, so the
|
|
687
|
+
// answers object stays one shape. `checked`, not `value`: the
|
|
688
|
+
// server reads this key as a boolean (recipes.compose keeps
|
|
689
|
+
// the switches out of the trim-to-string pass for exactly
|
|
690
|
+
// this reason).
|
|
691
|
+
boxes[key] = { get value() { return input.checked; } };
|
|
692
|
+
return wrap;
|
|
693
|
+
}
|
|
694
|
+
|
|
630
695
|
// The three job numbers arrive FILLED IN, not as grey placeholder
|
|
631
696
|
// text over an empty box. A default nobody can see is a default
|
|
632
697
|
// nobody can correct, and on a multiplexed image these three are
|
|
@@ -686,6 +751,16 @@ window.PlexoraConnectionModal = (function () {
|
|
|
686
751
|
"Set this if a login over SSH cannot find plexora — by a wide "
|
|
687
752
|
+ "margin the commonest reason a connection fails. An "
|
|
688
753
|
+ "environment path is enough."));
|
|
754
|
+
// Next to the field that names the environment, because that is
|
|
755
|
+
// the environment it writes to. Off on arrival for every preset:
|
|
756
|
+
// no starting point gets to decide that software should be
|
|
757
|
+
// installed into somebody's account on a machine it has only read
|
|
758
|
+
// the documentation for.
|
|
759
|
+
advancedForm.append(switchField(
|
|
760
|
+
"install", "Install or update Plexora",
|
|
761
|
+
"Runs pip install --upgrade plexora in that environment "
|
|
762
|
+
+ "before launching, and shows it in the connection log.",
|
|
763
|
+
false));
|
|
689
764
|
advanced.append(advancedForm);
|
|
690
765
|
parts.body.append(advanced);
|
|
691
766
|
|
|
@@ -698,7 +773,9 @@ window.PlexoraConnectionModal = (function () {
|
|
|
698
773
|
button("btn btn-primary", "Save and connect", async () => {
|
|
699
774
|
const answers = {};
|
|
700
775
|
Object.keys(boxes).forEach((key) => {
|
|
701
|
-
|
|
776
|
+
const value = boxes[key].value;
|
|
777
|
+
answers[key] = typeof value === "string"
|
|
778
|
+
? value.trim() : value;
|
|
702
779
|
});
|
|
703
780
|
try {
|
|
704
781
|
const saved = await saveRecipe(recipe.id, answers);
|
|
@@ -104,6 +104,15 @@ window.PlexoraRemoteGlobe = (function () {
|
|
|
104
104
|
return { word: "Healthy", glyph: "circle-check", cls: "is-healthy",
|
|
105
105
|
ms: probe.ms, detail: "" };
|
|
106
106
|
}
|
|
107
|
+
//: Its own word, because it is its own situation and the other two
|
|
108
|
+
//: would both be lies. The machine IS answering, so "Not answering" is
|
|
109
|
+
//: false; the server knows exactly what is wrong, so "Unknown" throws
|
|
110
|
+
//: that away. What is stale is the address the open project is using,
|
|
111
|
+
//: and the detail line says so and says what fixes it.
|
|
112
|
+
if (probe.state === "stale") {
|
|
113
|
+
return { word: "Reconnected", glyph: "arrows-rotate", cls: "is-degraded",
|
|
114
|
+
ms: null, detail: probe.detail || "" };
|
|
115
|
+
}
|
|
107
116
|
return {
|
|
108
117
|
word: probe.state === "unreachable" ? "Not answering" : "Unknown",
|
|
109
118
|
glyph: "triangle-exclamation", cls: "is-degraded", ms: null,
|
|
@@ -48,8 +48,8 @@ window.PlexoraRemotes = (function () {
|
|
|
48
48
|
//: remote_sessions.OPENING_STATES. Anything not here is settled, one way
|
|
49
49
|
//: or the other, and asking again would be asking a question whose answer
|
|
50
50
|
//: cannot change on its own.
|
|
51
|
-
const OPENING = ["connecting", "authenticating", "
|
|
52
|
-
"tunneling", "waiting_for_app"];
|
|
51
|
+
const OPENING = ["connecting", "authenticating", "installing",
|
|
52
|
+
"waiting_for_job", "tunneling", "waiting_for_app"];
|
|
53
53
|
|
|
54
54
|
//: What each state is called on screen. One map, because the same word has
|
|
55
55
|
//: to appear on the card, in the modal's step list and in the globe's
|
|
@@ -59,6 +59,7 @@ window.PlexoraRemotes = (function () {
|
|
|
59
59
|
idle: "Not connected",
|
|
60
60
|
connecting: "Connecting",
|
|
61
61
|
authenticating: "Needs your password",
|
|
62
|
+
installing: "Installing Plexora",
|
|
62
63
|
waiting_for_job: "Queued",
|
|
63
64
|
tunneling: "Tunnelling",
|
|
64
65
|
waiting_for_app: "Starting",
|
|
@@ -214,6 +215,16 @@ window.PlexoraRemotes = (function () {
|
|
|
214
215
|
//: setting, and worth knowing BEFORE pressing Connect: it
|
|
215
216
|
//: turns seconds into a queue.
|
|
216
217
|
queued: remote.srun !== null && remote.srun !== undefined,
|
|
218
|
+
//: Whether connecting to this machine also installs or updates
|
|
219
|
+
//: Plexora on it, and what the environment it writes to is
|
|
220
|
+
//: called. Both from the profile, so the step list can say so
|
|
221
|
+
//: BEFORE it happens rather than after -- which for the one
|
|
222
|
+
//: step that writes to somebody else's machine is the
|
|
223
|
+
//: difference between a plan and a surprise. `installEnv` is a
|
|
224
|
+
//: display name and may be null even when `install` is on: not
|
|
225
|
+
//: every launch command names an environment anything.
|
|
226
|
+
install: Boolean(remote.install),
|
|
227
|
+
installEnv: remote.install_env || null,
|
|
217
228
|
viewer: viewer,
|
|
218
229
|
node: node,
|
|
219
230
|
//: Either half up is "this machine is reachable" for the
|
|
@@ -814,8 +814,15 @@
|
|
|
814
814
|
card.chip.className = "settings-node-state is-" + state;
|
|
815
815
|
card.chip.textContent = window.PlexoraRemotes.label(state);
|
|
816
816
|
|
|
817
|
+
// On the address line rather than as a badge of its own: these are
|
|
818
|
+
// both "what pressing Connect will do to that machine before you see
|
|
819
|
+
// anything", and the install one is the half that writes.
|
|
817
820
|
card.address.textContent = entry.detail
|
|
818
|
-
+ (entry.queued ? " · runs inside a job" : "")
|
|
821
|
+
+ (entry.queued ? " · runs inside a job" : "")
|
|
822
|
+
+ (entry.install
|
|
823
|
+
? " · installs Plexora"
|
|
824
|
+
+ (entry.installEnv ? " in " + entry.installEnv : "")
|
|
825
|
+
: "");
|
|
819
826
|
|
|
820
827
|
card.phase.textContent = half.phase || "";
|
|
821
828
|
card.phase.hidden = !half.phase;
|
|
@@ -1144,6 +1151,8 @@
|
|
|
1144
1151
|
this.revealJob(scheduler);
|
|
1145
1152
|
const bind = el("settings_remote_bind_node");
|
|
1146
1153
|
if (bind) bind.checked = !!remote.bind_node;
|
|
1154
|
+
const install = el("settings_remote_install");
|
|
1155
|
+
if (install) install.checked = !!remote.install;
|
|
1147
1156
|
|
|
1148
1157
|
this.forwards = (remote.forwards || []).map(String);
|
|
1149
1158
|
this.renderForwards();
|
|
@@ -1152,7 +1161,12 @@
|
|
|
1152
1161
|
// empty disclosure on every Edit makes the form look like it has nine
|
|
1153
1162
|
// questions when this server answered three of them.
|
|
1154
1163
|
const advanced = el("settings_remote_advanced");
|
|
1164
|
+
// `install` counts here for a reason the others do not have: it is the
|
|
1165
|
+
// one setting in this form that makes connecting WRITE to the far
|
|
1166
|
+
// machine, so an Edit that hid it would be hiding the answer somebody
|
|
1167
|
+
// is most likely to have come to change.
|
|
1155
1168
|
if (advanced) advanced.open = scheduler || this.forwards.length > 0
|
|
1169
|
+
|| Boolean(remote.install)
|
|
1156
1170
|
|| Boolean(remote.remote_command
|
|
1157
1171
|
&& remote.remote_command !== "plexora");
|
|
1158
1172
|
text(el("settings_remote_form_title"), "Edit “" + remote.name + "”");
|
|
@@ -1166,7 +1180,8 @@
|
|
|
1166
1180
|
setValue(REMOTE_FIELDS[key], "");
|
|
1167
1181
|
});
|
|
1168
1182
|
setValue("settings_remote_port", "");
|
|
1169
|
-
["settings_remote_use_srun", "settings_remote_bind_node"
|
|
1183
|
+
["settings_remote_use_srun", "settings_remote_bind_node",
|
|
1184
|
+
"settings_remote_install"].forEach((id) => {
|
|
1170
1185
|
const box = el(id);
|
|
1171
1186
|
if (box) box.checked = false;
|
|
1172
1187
|
});
|
|
@@ -1188,6 +1203,7 @@
|
|
|
1188
1203
|
});
|
|
1189
1204
|
body.use_srun = !!(el("settings_remote_use_srun") || {}).checked;
|
|
1190
1205
|
body.bind_node = !!(el("settings_remote_bind_node") || {}).checked;
|
|
1206
|
+
body.install = !!(el("settings_remote_install") || {}).checked;
|
|
1191
1207
|
body.forwards = this.forwards.slice();
|
|
1192
1208
|
|
|
1193
1209
|
const button = el("settings_remote_save");
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="apple-touch-icon" href="{{ data.base_url }}/client/src/img/apple-touch-icon.png">
|
|
9
9
|
<!-- Styles -->
|
|
10
10
|
<link rel="stylesheet" href="{{ data.base_url }}/client/src/css/tokens.css?v=20260829_view_palette">
|
|
11
|
-
<link rel="stylesheet" href="{{ data.base_url }}/client/src/css/main.css?v=
|
|
11
|
+
<link rel="stylesheet" href="{{ data.base_url }}/client/src/css/main.css?v=20260830_install_plexora">
|
|
12
12
|
{% block style %}{% endblock %}
|
|
13
13
|
|
|
14
14
|
|
|
@@ -87,17 +87,17 @@
|
|
|
87
87
|
the one poll that four surfaces used to run three of. It costs nothing
|
|
88
88
|
on a page that never subscribes -- there is no timer until somebody
|
|
89
89
|
does. #}
|
|
90
|
-
<script src="{{ data.base_url }}/client/src/js/services/remoteState.js?v=
|
|
90
|
+
<script src="{{ data.base_url }}/client/src/js/services/remoteState.js?v=20260830_install_plexora" defer></script>
|
|
91
91
|
{# The connection log, shared by the modal and the Settings page's cards.
|
|
92
92
|
Before both of them: they call into it as they build. #}
|
|
93
93
|
<script src="{{ data.base_url }}/client/src/js/services/logTerminal.js?v=20260829_terminal" defer></script>
|
|
94
|
-
<script src="{{ data.base_url }}/client/src/js/services/connectionModal.js?v=
|
|
94
|
+
<script src="{{ data.base_url }}/client/src/js/services/connectionModal.js?v=20260830_install_plexora" defer></script>
|
|
95
95
|
<script src="{{ data.base_url }}/client/src/js/services/placePicker.js?v=20260828_connect_modal" defer></script>
|
|
96
96
|
{# The navbar's connection icon. Deferred like the rest, which still runs
|
|
97
97
|
it after pageBoot.js below (a normal script beats every deferred one),
|
|
98
98
|
so PlexoraPage.register exists by the time this file's last line runs.
|
|
99
99
|
It needs popoverPortal.js, loaded above with the other widgets. #}
|
|
100
|
-
<script src="{{ data.base_url }}/client/src/js/services/remoteGlobe.js?v=
|
|
100
|
+
<script src="{{ data.base_url }}/client/src/js/services/remoteGlobe.js?v=20260830_node_reconnect" defer></script>
|
|
101
101
|
{# Says so before a scheduled job ends, from whichever page the user is on.
|
|
102
102
|
After connectionModal.js, which its one button opens; passive, so it
|
|
103
103
|
costs nothing while a connection sits settled. #}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{% extends layout|default('base.html', true) %}
|
|
2
2
|
{% block style %}
|
|
3
|
-
<link rel="stylesheet" href="{{ data.base_url }}/client/src/css/settings.css?v=
|
|
3
|
+
<link rel="stylesheet" href="{{ data.base_url }}/client/src/css/settings.css?v=20260830_install_plexora">
|
|
4
4
|
{% endblock %}
|
|
5
5
|
{% block content %}
|
|
6
6
|
<title>Plexora - Settings</title>
|
|
@@ -281,7 +281,24 @@
|
|
|
281
281
|
<summary class="remote-advanced-summary">Advanced options</summary>
|
|
282
282
|
|
|
283
283
|
<div class="remote-group">
|
|
284
|
-
|
|
284
|
+
{# The switch rides the group's own title row
|
|
285
|
+
rather than taking a row of its own: it is the
|
|
286
|
+
same question as the box below it -- which
|
|
287
|
+
environment -- asked as "and keep Plexora
|
|
288
|
+
current in it". A dedicated row would put an
|
|
289
|
+
uppercase section label and a lone toggle on two
|
|
290
|
+
lines that both say "environment". #}
|
|
291
|
+
<div class="remote-group-head">
|
|
292
|
+
<div class="remote-group-title">Plexora environment</div>
|
|
293
|
+
<label class="remote-switch remote-switch-inline">
|
|
294
|
+
<span class="remote-switch-label">Install or update Plexora</span>
|
|
295
|
+
<input type="checkbox" id="settings_remote_install"
|
|
296
|
+
class="remote-switch-input">
|
|
297
|
+
<span class="remote-switch-track" aria-hidden="true">
|
|
298
|
+
<span class="remote-switch-thumb"></span>
|
|
299
|
+
</span>
|
|
300
|
+
</label>
|
|
301
|
+
</div>
|
|
285
302
|
<div class="remote-form">
|
|
286
303
|
<label class="remote-field">
|
|
287
304
|
<span class="remote-label">Plexora command or environment</span>
|
|
@@ -290,7 +307,12 @@
|
|
|
290
307
|
placeholder="plexora" autocomplete="off">
|
|
291
308
|
<span class="remote-hint">
|
|
292
309
|
If SSH cannot find Plexora, specify its
|
|
293
|
-
environment or launch command.
|
|
310
|
+
environment or launch command. With
|
|
311
|
+
<strong>Install or update Plexora</strong>
|
|
312
|
+
on, connecting runs
|
|
313
|
+
<code>pip install --upgrade plexora</code>
|
|
314
|
+
in this same environment first, and shows
|
|
315
|
+
the whole of it in the connection log.
|
|
294
316
|
</span>
|
|
295
317
|
</label>
|
|
296
318
|
{# The examples are behind a disclosure because
|
|
@@ -541,5 +563,5 @@
|
|
|
541
563
|
{# remoteState.js is loaded by base.html, above and deferred, so it is parsed
|
|
542
564
|
before this file runs -- the Remote servers section is one of its
|
|
543
565
|
subscribers rather than a poller of its own. #}
|
|
544
|
-
<script src="{{ data.base_url }}/client/src/js/views/settingsPage.js?v=
|
|
566
|
+
<script src="{{ data.base_url }}/client/src/js/views/settingsPage.js?v=20260830_install_plexora" defer></script>
|
|
545
567
|
{% endblock %}
|