arrayview 0.26.0__tar.gz → 0.26.2__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.
- {arrayview-0.26.0 → arrayview-0.26.2}/.agents/skills/invocation-consistency/SKILL.md +21 -3
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/context/architecture.md +12 -1
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/patterns/add-file-format.md +20 -4
- {arrayview-0.26.0 → arrayview-0.26.2}/PKG-INFO +1 -1
- {arrayview-0.26.0 → arrayview-0.26.2}/pyproject.toml +1 -1
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/__init__.py +2 -1
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_io.py +17 -2
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_launcher.py +15 -9
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_platform.py +3 -1
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_routes_loading.py +7 -1
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_stdio_server.py +55 -1
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_viewer.html +50 -53
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_api.py +82 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_command_reachability.py +18 -0
- arrayview-0.26.2/tests/test_io.py +139 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/uv.lock +1 -1
- {arrayview-0.26.0 → arrayview-0.26.2}/.agents/skills/frontend-designer/SKILL.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.agents/skills/modes-consistency/SKILL.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.agents/skills/playwright-cli/SKILL.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.agents/skills/playwright-cli/references/element-attributes.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.agents/skills/playwright-cli/references/playwright-tests.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.agents/skills/playwright-cli/references/request-mocking.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.agents/skills/playwright-cli/references/running-code.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.agents/skills/playwright-cli/references/session-management.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.agents/skills/playwright-cli/references/storage-state.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.agents/skills/playwright-cli/references/test-generation.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.agents/skills/playwright-cli/references/tracing.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.agents/skills/playwright-cli/references/video-recording.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.agents/skills/todo-workflow/SKILL.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.agents/skills/ui-consistency-audit/SKILL.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.agents/skills/viewer-ui-checklist/SKILL.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.agents/skills/visual-bug-fixing/SKILL.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.github/copilot-instructions.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.github/workflows/docs.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.github/workflows/python-publish.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.gitignore +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.ignore +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/AGENTS.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/ROUTER.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/SETUP.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/SYNC.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/context/conventions.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/context/decisions.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/context/frontend.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/context/lifecycle.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/context/project-state.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/context/render-pipeline.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/context/setup.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/context/stack.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/patterns/INDEX.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/patterns/README.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/patterns/add-server-endpoint.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/patterns/animation-verify.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/patterns/debug-render.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/patterns/debug-vscode-extension-python.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/patterns/extend-compare-mode.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/patterns/extract-server-route-module.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/patterns/frontend-change.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/setup.sh +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.mex/sync.sh +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.opencode/opencode.json +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T18-46-49-737Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T18-48-21-979Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T18-51-35-665Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T19-07-01-393Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T19-14-37-969Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T19-21-30-940Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T19-23-08-126Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T19-29-33-155Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T19-31-25-336Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T19-31-53-789Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T19-39-12-257Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T19-39-16-449Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T20-15-25-513Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T20-25-13-179Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T20-39-01-435Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T21-01-27-659Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T21-01-41-283Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T21-03-00-625Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T21-04-12-887Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T21-33-39-044Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T21-38-01-530Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T21-45-20-383Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T21-55-11-545Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T21-56-03-307Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T21-56-35-733Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T21-57-12-181Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T21-57-37-748Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T21-58-13-679Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-06T22-37-23-895Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T00-39-18-637Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T01-41-46-243Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T04-31-48-472Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T12-14-15-632Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T12-14-47-582Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T12-16-23-471Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T12-17-10-247Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T12-18-24-707Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T12-20-06-164Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T12-20-28-342Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T12-21-54-962Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T12-22-34-666Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T12-23-11-336Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T12-23-36-260Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T12-24-09-267Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T12-24-35-434Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T12-25-57-010Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T12-34-48-823Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T12-46-46-468Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T12-48-17-930Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T12-49-26-400Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T12-50-31-563Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/page-2026-05-07T12-56-45-568Z.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.playwright-cli/theme-dark.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.python-version +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/.vscode/settings.json +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/AGENTS.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/CONTRIBUTING.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/DESIGN.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/LICENSE +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/README.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/docs/comparing.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/docs/configuration.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/docs/display.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/docs/index.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/docs/lifecycle.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/docs/loading.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/docs/logo.png +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/docs/measurement.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/docs/remote.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/docs/stylesheets/extra.css +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/docs/viewing.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/matlab/arrayview.m +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/mkdocs.yml +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/plans/2026-04-14-immersive-animation.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/plans/2026-05-07-unified-colormap-picker.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/plans/arrayview_tool_menu_fix_prompt.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/plans/arrayview_tool_menu_implementation_plan.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/plans/egg-placement-mockup.html +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/plans/refactoring_plan.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/plans/webview/LOG.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/scripts/demo.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/scripts/release.sh +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/ARCHITECTURE.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/__main__.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_analysis.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_app.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_codex_open.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_config.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_diff.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_icon.png +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_imaging.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_lifecycle.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_overlays.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_render.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_routes_analysis.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_routes_export.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_routes_persistence.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_routes_preload.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_routes_query.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_routes_rendering.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_routes_segmentation.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_routes_state.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_routes_vectorfield.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_routes_websocket.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_segmentation.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_server.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_session.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_shell.html +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_synthetic_mri.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_torch.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_vectorfield.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_vscode.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_vscode_browser.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_vscode_extension.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_vscode_shm.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/_vscode_signal.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/arrayview-opener.vsix +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/src/arrayview/gsap.min.js +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/capture_v_animation.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/conftest.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/lifecycle_matrix.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/make_vectorfield_test_arrays.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_backend_shared.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_browser.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_cli.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_colorbar_hover_highlight.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_config.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_cross_mode_parametrized.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_interactions.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_large_arrays.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_lifecycle_contract.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_loading_server.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_mode_consistency.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_mode_entry_batching.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_mode_matrix.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_mode_roundtrip.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_nifti_meta.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_rgb_pixel_art.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_stdio_server.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_torch.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_view_component_integration.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/test_view_component_unit.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/ui_audit.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/v_anim_frames/.gitkeep +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/tests/visual_smoke.py +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/vscode-extension/AGENTS.md +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/vscode-extension/LICENSE +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/vscode-extension/extension.js +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/vscode-extension/lifecycle_helpers.js +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/vscode-extension/package.json +0 -0
- {arrayview-0.26.0 → arrayview-0.26.2}/vscode-extension/test_lifecycle_helpers.js +0 -0
|
@@ -20,6 +20,15 @@ Every behavior that depends on *how* arrayview is started (server lifecycle, bro
|
|
|
20
20
|
| **VS Code terminal** | `arrayview()` or `view()` | VS Code Simple Browser | in-process or subprocess |
|
|
21
21
|
| **Plain SSH (no VS Code)** | `arrayview()` or `view()` | prints port-forward hint | in-process or subprocess |
|
|
22
22
|
|
|
23
|
+
## Transport Split
|
|
24
|
+
|
|
25
|
+
ArrayView has two backend transports. Invocation-sensitive fixes must account for both:
|
|
26
|
+
|
|
27
|
+
- **Network mode**: FastAPI routes and WebSocket server. File registration goes through `/load` in `_routes_loading.py`; spawned CLI sessions load in `_launcher._serve_daemon()`.
|
|
28
|
+
- **VS Code direct mode**: stdio subprocess. VS Code tunnel launches write a signal file; the extension runs `python -m arrayview --mode stdio <file>`, and file registration goes through `_stdio_server._handle_register()`.
|
|
29
|
+
|
|
30
|
+
Do not assume a `/load` or daemon fix covers VS Code tunnel. Shared behavior such as file-format key selection belongs in `_io.py` helpers and must be used by both transports.
|
|
31
|
+
|
|
23
32
|
## Key Detection Functions (all in `_platform.py`)
|
|
24
33
|
|
|
25
34
|
```python
|
|
@@ -86,17 +95,24 @@ for pid_str in client_pids:
|
|
|
86
95
|
- Jupyter: repeated calls don't fail on port-already-in-use
|
|
87
96
|
- Julia: `_view_julia()` passes new params via CLI flag or file
|
|
88
97
|
|
|
89
|
-
2. **
|
|
98
|
+
2. **File/session registration consistent?**
|
|
99
|
+
- FastAPI `/load` path handles the feature
|
|
100
|
+
- CLI spawned daemon path handles the feature
|
|
101
|
+
- VS Code direct stdio `_handle_register()` path handles the feature
|
|
102
|
+
- Direct `--mode stdio <file>` CLI path handles the feature
|
|
103
|
+
- Multi-array/default-key behavior is centralized in `_io.py`
|
|
104
|
+
|
|
105
|
+
3. **Browser/display opening?**
|
|
90
106
|
- Local native window (macOS): `pywebview` opens correctly
|
|
91
107
|
- VS Code terminal: Simple Browser opens via extension signal file
|
|
92
108
|
- VS Code tunnel: reaches client-side Simple Browser, not remote host
|
|
93
109
|
- Verify `_ensure_vscode_extension()` installs/updates if VSIX changed
|
|
94
110
|
|
|
95
|
-
|
|
111
|
+
4. **Environment detection?**
|
|
96
112
|
- Check detection order: Julia → Jupyter → VS Code remote → VS Code terminal → local
|
|
97
113
|
- `_find_vscode_ipc_hook()` walks up to 12 parent processes; new subprocess wrappers may need the same
|
|
98
114
|
|
|
99
|
-
|
|
115
|
+
5. **VS Code extension changed?**
|
|
100
116
|
- Rebuild VSIX: `cd vscode-extension && vsce package -o ../src/arrayview/arrayview-opener.vsix`
|
|
101
117
|
- Bump `_VSCODE_EXT_VERSION` in `_vscode.py` and `vscode-extension/package.json` together
|
|
102
118
|
|
|
@@ -111,6 +127,8 @@ uv run python -c "from arrayview import view; import numpy as np; view(np.zeros(
|
|
|
111
127
|
## Red Flags — STOP
|
|
112
128
|
|
|
113
129
|
- "I changed server startup but only tested CLI" → test all paths
|
|
130
|
+
- "I fixed `/load` but not `_stdio_server.py`" → VS Code tunnel can still be broken
|
|
131
|
+
- "I fixed `_serve_daemon()` but `--diagnose` says `is_vscode_remote: true`" → tunnel uses stdio direct mode, not the daemon
|
|
114
132
|
- "Works locally but not in tunnel" → check `_is_vscode_remote()` path and port exposure
|
|
115
133
|
- "I used `127.0.0.1` in the URL" → use `localhost`
|
|
116
134
|
- "I fixed tmux with `#{client_pid}`" → only works with one client; use `list-clients`
|
|
@@ -20,7 +20,7 @@ edges:
|
|
|
20
20
|
condition: when the task involves _viewer.html, modes, reconcilers, or the View Component System
|
|
21
21
|
- target: context/render-pipeline.md
|
|
22
22
|
condition: when the task involves slice extraction, colormaps, caching, or the render thread
|
|
23
|
-
last_updated: 2026-06-
|
|
23
|
+
last_updated: 2026-06-11
|
|
24
24
|
---
|
|
25
25
|
|
|
26
26
|
# Architecture
|
|
@@ -90,6 +90,17 @@ Tool launch, tool activation, and drawer visibility are separate states.
|
|
|
90
90
|
|
|
91
91
|
Detection logic: `_platform.py`. Display opening: `_launcher.py` + `_vscode.py`.
|
|
92
92
|
|
|
93
|
+
## File Registration Paths
|
|
94
|
+
|
|
95
|
+
File parsing belongs in `_io.py`, but session registration happens through different transports:
|
|
96
|
+
|
|
97
|
+
- **Network `/load`**: existing FastAPI server path in `_routes_loading.py`.
|
|
98
|
+
- **Spawned CLI daemon**: `_launcher._serve_daemon()` loads before publishing the pending session.
|
|
99
|
+
- **VS Code tunnel direct webview**: `_stdio_server._handle_register()` handles `python -m arrayview --mode stdio <file>` spawned by the extension.
|
|
100
|
+
- **Direct stdio CLI mode**: `_launcher.arrayview()` preloads positional files before starting `run_stdio_server()`.
|
|
101
|
+
|
|
102
|
+
For multi-array formats or formats with non-displayable members, keep key filtering/default selection in `_io.py` helpers and call those helpers from every registration path. A fix in `/load` alone does not cover VS Code tunnel.
|
|
103
|
+
|
|
93
104
|
## External Dependencies
|
|
94
105
|
|
|
95
106
|
- **FastAPI + uvicorn** — async HTTP/WebSocket server, lazy-imported in `_launcher.py`. Never call uvicorn directly — use the `_uvicorn()` accessor.
|
|
@@ -15,20 +15,26 @@ edges:
|
|
|
15
15
|
condition: for the lazy import pattern and Verify Checklist
|
|
16
16
|
- target: context/render-pipeline.md
|
|
17
17
|
condition: when the new format returns an unusual dtype that may affect the render pipeline
|
|
18
|
-
last_updated: 2026-
|
|
18
|
+
last_updated: 2026-06-11
|
|
19
19
|
---
|
|
20
20
|
|
|
21
21
|
# Add File Format
|
|
22
22
|
|
|
23
23
|
## Context
|
|
24
24
|
|
|
25
|
-
All file
|
|
25
|
+
All file parsing goes through `_io.load_data(filepath)` or `_io.load_data_with_meta(filepath)` in `src/arrayview/_io.py`. The function dispatches on file extension. New formats must:
|
|
26
26
|
1. Register the extension in `_SUPPORTED_EXTS`
|
|
27
27
|
2. Add a lazy import (if the library is not already imported at module level)
|
|
28
28
|
3. Add a dispatch branch in `load_data()`
|
|
29
29
|
4. Return a numpy array (or array-like with `.shape` and `.dtype`)
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
Transport/session creation is separate from parsing:
|
|
32
|
+
|
|
33
|
+
- FastAPI `/load`: `src/arrayview/_routes_loading.py`
|
|
34
|
+
- CLI spawned daemon and direct stdio CLI mode: `src/arrayview/_launcher.py`
|
|
35
|
+
- VS Code tunnel direct webview: `src/arrayview/_stdio_server.py`
|
|
36
|
+
|
|
37
|
+
The server and CLI must not import format libraries directly. They may call generic `_io` helpers such as `list_array_keys()` or `default_array_key()` when they need to choose a key before creating a session.
|
|
32
38
|
|
|
33
39
|
## Steps
|
|
34
40
|
|
|
@@ -74,18 +80,28 @@ The server (`_server.py`) and CLI (`_launcher.py`) never import format libraries
|
|
|
74
80
|
return list(_mylib().peek_shape(fpath))
|
|
75
81
|
```
|
|
76
82
|
|
|
83
|
+
7. **Keep key/default selection consistent across transports** for multi-array formats (`.npz`, `.mat`, HDF5-like containers):
|
|
84
|
+
- Put filtering and default-key logic in `_io.py`, not in a route or launcher branch.
|
|
85
|
+
- Use the same helper from FastAPI `/load`, CLI daemon/direct paths, and `_stdio_server.py`.
|
|
86
|
+
- If `/load` returns picker keys for multiple arrays, define what non-picker transports do (usually first displayable key).
|
|
87
|
+
|
|
77
88
|
## Gotchas
|
|
78
89
|
|
|
79
90
|
- **Do not import the format library at the top of `_io.py`** without checking import cost first. Nibabel is the reference: it is lazy because it costs ~200 ms on first import.
|
|
80
91
|
- **`.nii.gz` is two extensions** — the dispatch checks the full suffix, not just the last dot. New formats with compound extensions (`.foo.bar`) need special suffix handling in `load_data()`.
|
|
81
92
|
- **Zarr is not loaded via `load_data()`** in all paths — zarr arrays can also be passed directly to `view()` as array objects. Ensure zarr-specific logic lives in `_session.py` / `_render.py`, not only in the file-load path.
|
|
82
|
-
- **No format-specific code in `_server.py` or `
|
|
93
|
+
- **No format-specific parsing code in `_server.py`, `_launcher.py`, or `_stdio_server.py`** — if you find yourself adding a format import or dtype/shape filter in those modules, move it to `_io.py`.
|
|
94
|
+
- **VS Code tunnel is stdio, not HTTP** — `arrayview file.ext` in a VS Code tunnel writes a signal file; the extension then runs `python -m arrayview --mode stdio file.ext`. A `/load` fix alone does not cover this path.
|
|
83
95
|
|
|
84
96
|
## Verify
|
|
85
97
|
|
|
86
98
|
- [ ] Extension added to `_SUPPORTED_EXTS`
|
|
87
99
|
- [ ] New library import is lazy (accessor function pattern) unless it imports in <10 ms
|
|
88
100
|
- [ ] `load_data("myfile.myext")` returns a numpy array with correct shape and dtype
|
|
101
|
+
- [ ] If the format can contain multiple arrays or non-displayable members, add tests for `list_array_keys()` / `default_array_key()` and direct `load_data(..., key=...)`
|
|
102
|
+
- [ ] FastAPI `/load` works for the file, including picker/default-key behavior
|
|
103
|
+
- [ ] `_stdio_server._handle_register()` works for the file, because VS Code tunnel direct webview uses stdio instead of `/load`
|
|
104
|
+
- [ ] CLI daemon/direct paths use generic `_io` helpers for key selection instead of duplicating format rules
|
|
89
105
|
- [ ] `view(load_data("myfile.myext"))` opens the viewer without errors
|
|
90
106
|
- [ ] If full-load format, added to `FULL_LOAD_EXTS`
|
|
91
107
|
- [ ] `uv run pytest` on `tests/test_mode_consistency.py` still passes (no dtype regressions)
|
|
@@ -38,6 +38,15 @@ def _fix_mat_complex(arr):
|
|
|
38
38
|
return arr
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
def _is_viewable_mat_array(value):
|
|
42
|
+
"""True for MATLAB arrays ArrayView can display directly."""
|
|
43
|
+
return (
|
|
44
|
+
isinstance(value, np.ndarray)
|
|
45
|
+
and value.ndim >= 1
|
|
46
|
+
and value.dtype.kind in ("b", "i", "u", "f", "c")
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
41
50
|
def list_npz_keys(filepath):
|
|
42
51
|
"""Return [{key, shape, dtype}] for each ndarray in an .npz file.
|
|
43
52
|
|
|
@@ -67,7 +76,7 @@ def list_mat_keys(filepath):
|
|
|
67
76
|
for k, v in mat.items():
|
|
68
77
|
if k.startswith("_"):
|
|
69
78
|
continue
|
|
70
|
-
if
|
|
79
|
+
if _is_viewable_mat_array(v):
|
|
71
80
|
keys.append({"key": k, "shape": list(v.shape), "dtype": str(v.dtype)})
|
|
72
81
|
return keys
|
|
73
82
|
except NotImplementedError:
|
|
@@ -95,6 +104,12 @@ def list_array_keys(filepath):
|
|
|
95
104
|
return []
|
|
96
105
|
|
|
97
106
|
|
|
107
|
+
def default_array_key(filepath):
|
|
108
|
+
"""Return the first selectable array key for multi-array formats, if any."""
|
|
109
|
+
keys = list_array_keys(filepath)
|
|
110
|
+
return keys[0]["key"] if keys else None
|
|
111
|
+
|
|
112
|
+
|
|
98
113
|
def _select_npz_array(npz, filepath):
|
|
99
114
|
"""Load the first array from a multi-array .npz file.
|
|
100
115
|
|
|
@@ -233,7 +248,7 @@ def load_data(filepath, key=None):
|
|
|
233
248
|
arrays = {
|
|
234
249
|
k: v
|
|
235
250
|
for k, v in mat.items()
|
|
236
|
-
if not k.startswith("_") and
|
|
251
|
+
if not k.startswith("_") and _is_viewable_mat_array(v)
|
|
237
252
|
}
|
|
238
253
|
if key is not None:
|
|
239
254
|
return _fix_mat_complex(arrays[key])
|
|
@@ -2717,7 +2717,7 @@ def _serve_daemon(
|
|
|
2717
2717
|
_array_keys = list_array_keys(filepath)
|
|
2718
2718
|
except Exception:
|
|
2719
2719
|
pass
|
|
2720
|
-
_load_key = _array_keys[0]["key"] if _array_keys
|
|
2720
|
+
_load_key = _array_keys[0]["key"] if _array_keys else None
|
|
2721
2721
|
data, spatial_meta = load_data_with_meta(filepath, key=_load_key)
|
|
2722
2722
|
if cleanup:
|
|
2723
2723
|
try:
|
|
@@ -3187,12 +3187,12 @@ def arrayview():
|
|
|
3187
3187
|
)
|
|
3188
3188
|
print(f"SESSION:{info}", file=sys.stderr)
|
|
3189
3189
|
elif args.files:
|
|
3190
|
-
from arrayview._io import load_data
|
|
3190
|
+
from arrayview._io import default_array_key, load_data
|
|
3191
3191
|
from arrayview._session import SESSIONS, Session
|
|
3192
3192
|
|
|
3193
3193
|
# First file is the main array
|
|
3194
3194
|
base_path = args.files[0]
|
|
3195
|
-
data = load_data(base_path)
|
|
3195
|
+
data = load_data(base_path, key=default_array_key(base_path))
|
|
3196
3196
|
session = Session(
|
|
3197
3197
|
data=data,
|
|
3198
3198
|
filepath=base_path,
|
|
@@ -3205,7 +3205,10 @@ def arrayview():
|
|
|
3205
3205
|
if getattr(args, "vectorfield", None):
|
|
3206
3206
|
from arrayview._vectorfield import _configure_vectorfield
|
|
3207
3207
|
|
|
3208
|
-
vf_data = load_data(
|
|
3208
|
+
vf_data = load_data(
|
|
3209
|
+
args.vectorfield,
|
|
3210
|
+
key=default_array_key(args.vectorfield),
|
|
3211
|
+
)
|
|
3209
3212
|
_configure_vectorfield(
|
|
3210
3213
|
session, vf_data,
|
|
3211
3214
|
getattr(args, "vectorfield_components_dim", None),
|
|
@@ -3216,7 +3219,7 @@ def arrayview():
|
|
|
3216
3219
|
# Load overlay(s) as separate sessions
|
|
3217
3220
|
overlay_sids = []
|
|
3218
3221
|
for ov_path in (getattr(args, "overlay", None) or []):
|
|
3219
|
-
ov_data = load_data(ov_path)
|
|
3222
|
+
ov_data = load_data(ov_path, key=default_array_key(ov_path))
|
|
3220
3223
|
ov_session = Session(
|
|
3221
3224
|
data=ov_data,
|
|
3222
3225
|
filepath=ov_path,
|
|
@@ -3232,7 +3235,7 @@ def arrayview():
|
|
|
3232
3235
|
if getattr(args, "compare", None):
|
|
3233
3236
|
compare_paths.append(args.compare)
|
|
3234
3237
|
for cp in compare_paths:
|
|
3235
|
-
cmp_data = load_data(cp)
|
|
3238
|
+
cmp_data = load_data(cp, key=default_array_key(cp))
|
|
3236
3239
|
cmp_session = Session(
|
|
3237
3240
|
data=cmp_data,
|
|
3238
3241
|
filepath=cp,
|
|
@@ -3572,18 +3575,21 @@ def arrayview():
|
|
|
3572
3575
|
|
|
3573
3576
|
if args.vectorfield:
|
|
3574
3577
|
try:
|
|
3575
|
-
from arrayview._io import load_data
|
|
3578
|
+
from arrayview._io import default_array_key, load_data
|
|
3576
3579
|
from arrayview._render import _detect_rgb_axis
|
|
3577
3580
|
from arrayview._vectorfield import _resolve_vfield_layout
|
|
3578
3581
|
|
|
3579
|
-
base_data = load_data(base_file)
|
|
3582
|
+
base_data = load_data(base_file, key=default_array_key(base_file))
|
|
3580
3583
|
image_shape = tuple(int(s) for s in base_data.shape)
|
|
3581
3584
|
if args.rgb:
|
|
3582
3585
|
rgb_axis = _detect_rgb_axis(image_shape)
|
|
3583
3586
|
image_shape = tuple(
|
|
3584
3587
|
s for i, s in enumerate(image_shape) if i != rgb_axis
|
|
3585
3588
|
)
|
|
3586
|
-
vf_data = load_data(
|
|
3589
|
+
vf_data = load_data(
|
|
3590
|
+
args.vectorfield,
|
|
3591
|
+
key=default_array_key(args.vectorfield),
|
|
3592
|
+
)
|
|
3587
3593
|
layout = _resolve_vfield_layout(
|
|
3588
3594
|
tuple(int(s) for s in vf_data.shape),
|
|
3589
3595
|
image_shape,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import importlib.util
|
|
5
6
|
import os
|
|
6
7
|
import subprocess
|
|
7
8
|
import sys
|
|
@@ -414,12 +415,13 @@ def _can_native_window() -> bool:
|
|
|
414
415
|
# Plain SSH (no VS Code): the display is on the client machine, not here.
|
|
415
416
|
if os.environ.get("SSH_CLIENT") or os.environ.get("SSH_CONNECTION"):
|
|
416
417
|
return False
|
|
418
|
+
if importlib.util.find_spec("webview") is None:
|
|
419
|
+
return False
|
|
417
420
|
if sys.platform in ("darwin", "win32"):
|
|
418
421
|
return True
|
|
419
422
|
# Linux/BSD: need a display server AND pywebview's GUI bindings
|
|
420
423
|
if not (os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY")):
|
|
421
424
|
return False
|
|
422
|
-
import importlib.util
|
|
423
425
|
|
|
424
426
|
return (
|
|
425
427
|
importlib.util.find_spec("qtpy") is not None
|
|
@@ -123,8 +123,14 @@ def register_loading_routes(app, *, notify_shells, setup_rgb) -> None:
|
|
|
123
123
|
_array_keys = await asyncio.to_thread(list_array_keys, filepath)
|
|
124
124
|
if len(_array_keys) > 1 and not _key:
|
|
125
125
|
return {"array_keys": _array_keys, "filepath": filepath}
|
|
126
|
+
if len(_array_keys) == 1 and not _key:
|
|
127
|
+
_key = _array_keys[0]["key"]
|
|
126
128
|
|
|
127
|
-
data, spatial_meta = await asyncio.to_thread(
|
|
129
|
+
data, spatial_meta = await asyncio.to_thread(
|
|
130
|
+
load_data_with_meta,
|
|
131
|
+
filepath,
|
|
132
|
+
key=_key,
|
|
133
|
+
)
|
|
128
134
|
except Exception as e:
|
|
129
135
|
return {"error": str(e)}
|
|
130
136
|
session = await asyncio.to_thread(Session, data, filepath=filepath, name=name)
|
|
@@ -115,9 +115,19 @@ def _handle_register(msg: dict) -> None:
|
|
|
115
115
|
name = msg.get("name") or __import__("os").path.basename(file_path)
|
|
116
116
|
options = msg.get("options", {})
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
from ._io import list_array_keys
|
|
119
|
+
|
|
120
|
+
array_keys = list_array_keys(file_path)
|
|
121
|
+
key = array_keys[0]["key"] if array_keys else None
|
|
122
|
+
data, spatial_meta = load_data_with_meta(
|
|
123
|
+
file_path,
|
|
124
|
+
key=key,
|
|
125
|
+
)
|
|
119
126
|
session = Session(data=data, filepath=file_path, name=name)
|
|
120
127
|
session.spatial_meta = spatial_meta
|
|
128
|
+
if len(array_keys) > 1:
|
|
129
|
+
session.array_keys = array_keys
|
|
130
|
+
session.array_filepath = file_path
|
|
121
131
|
if spatial_meta is not None:
|
|
122
132
|
session.original_volume = data
|
|
123
133
|
|
|
@@ -149,6 +159,48 @@ def _handle_metadata(msg: dict) -> None:
|
|
|
149
159
|
_write_json(_build_metadata(session))
|
|
150
160
|
|
|
151
161
|
|
|
162
|
+
def _handle_reload_key(sid: str, msg: dict) -> None:
|
|
163
|
+
"""Reload a session's data from a multi-array file with a different key."""
|
|
164
|
+
session = SESSIONS.get(sid)
|
|
165
|
+
if not session:
|
|
166
|
+
_write_error("session not found")
|
|
167
|
+
return
|
|
168
|
+
array_filepath = getattr(session, "array_filepath", None)
|
|
169
|
+
if not array_filepath:
|
|
170
|
+
_write_error("session has no array keys")
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
try:
|
|
174
|
+
body = json.loads(msg.get("body") or "{}")
|
|
175
|
+
except Exception:
|
|
176
|
+
_write_error("invalid JSON body")
|
|
177
|
+
return
|
|
178
|
+
key = body.get("key")
|
|
179
|
+
if not key:
|
|
180
|
+
_write_error("key is required")
|
|
181
|
+
return
|
|
182
|
+
|
|
183
|
+
try:
|
|
184
|
+
from ._io import load_data
|
|
185
|
+
|
|
186
|
+
data = load_data(array_filepath, key=key)
|
|
187
|
+
except Exception as e:
|
|
188
|
+
_write_error(str(e))
|
|
189
|
+
return
|
|
190
|
+
|
|
191
|
+
session.data = data
|
|
192
|
+
session.shape = data.shape
|
|
193
|
+
session.data_version += 1
|
|
194
|
+
session.raw_cache.clear()
|
|
195
|
+
session.rgba_cache.clear()
|
|
196
|
+
session.mosaic_cache.clear()
|
|
197
|
+
session._raw_bytes = 0
|
|
198
|
+
session._rgba_bytes = 0
|
|
199
|
+
session._mosaic_bytes = 0
|
|
200
|
+
session._estimated_mem = session._estimate_memory()
|
|
201
|
+
_write_json({"ok": True})
|
|
202
|
+
|
|
203
|
+
|
|
152
204
|
def _handle_sessions() -> None:
|
|
153
205
|
"""Return list of active sessions."""
|
|
154
206
|
_write_json([_session_summary(s) for s in SESSIONS.values()])
|
|
@@ -404,6 +456,8 @@ def _handle_fetch_proxy(msg: dict) -> None:
|
|
|
404
456
|
_handle_metadata({"sid": sid})
|
|
405
457
|
elif route == "clearcache" and sid:
|
|
406
458
|
_handle_clearcache({"sid": sid})
|
|
459
|
+
elif route == "session" and len(parts) >= 3 and parts[2] == "reload-key":
|
|
460
|
+
_handle_reload_key(parts[1], msg)
|
|
407
461
|
elif route == "sessions":
|
|
408
462
|
_handle_sessions()
|
|
409
463
|
elif route == "histogram" and sid:
|
|
@@ -285,7 +285,7 @@
|
|
|
285
285
|
.dim-size { opacity: 0.28; font-size: 0.68em; font-weight: normal; }
|
|
286
286
|
.dim-hover-idx { font-size: 0.68em; font-weight: normal; color: var(--dim-hover, #777); opacity: 1; }
|
|
287
287
|
.active-dim { font-weight: bold; }
|
|
288
|
-
.playing-dim {
|
|
288
|
+
.playing-dim { font-weight: bold; }
|
|
289
289
|
.vfield-t-dim { color: var(--vfield-t-dim); }
|
|
290
290
|
.active-vfield-t { font-weight: bold; }
|
|
291
291
|
.dim-sep { color: var(--muted); opacity: 0.4; font-weight: normal; }
|
|
@@ -466,8 +466,7 @@
|
|
|
466
466
|
color: var(--text);
|
|
467
467
|
-webkit-text-fill-color: var(--text);
|
|
468
468
|
}
|
|
469
|
-
.active-dim .dim-slash:not([style*="background"])
|
|
470
|
-
.playing-dim .dim-slash:not([style*="background"]) {
|
|
469
|
+
.active-dim .dim-slash:not([style*="background"]) {
|
|
471
470
|
color: var(--active-dim) !important;
|
|
472
471
|
-webkit-text-fill-color: var(--active-dim) !important;
|
|
473
472
|
}
|
|
@@ -13784,10 +13783,9 @@
|
|
|
13784
13783
|
const subtle = style.getPropertyValue('--subtle').trim();
|
|
13785
13784
|
let fill;
|
|
13786
13785
|
if (isVfieldT) fill = style.getPropertyValue('--vfield-t-dim').trim();
|
|
13787
|
-
else if (playing) fill = style.getPropertyValue('--playing-dim').trim();
|
|
13788
13786
|
else if (active) fill = style.getPropertyValue('--active-dim').trim();
|
|
13789
13787
|
else fill = muted;
|
|
13790
|
-
const unfilled = (active ||
|
|
13788
|
+
const unfilled = (active || isVfieldT) ? `color-mix(in srgb, ${fill} 32%, ${muted})` : subtle;
|
|
13791
13789
|
return slashFill(pct, fill, unfilled);
|
|
13792
13790
|
}
|
|
13793
13791
|
/* ── JS: Info Bar and Pixel Display ───────────────────────── */
|
|
@@ -17577,24 +17575,20 @@
|
|
|
17577
17575
|
// Transpose x and y: swap the dimension indices and flips
|
|
17578
17576
|
if (shape.length < 2) return;
|
|
17579
17577
|
if (multiViewActive) {
|
|
17580
|
-
|
|
17581
|
-
|
|
17582
|
-
|
|
17583
|
-
|
|
17584
|
-
|
|
17585
|
-
mvViews.forEach(v => { mvDrawFrame(v); mvRender(v); });
|
|
17586
|
-
});
|
|
17578
|
+
mvViews.forEach(v => { [v.dimX, v.dimY] = [v.dimY, v.dimX]; v.seq++; });
|
|
17579
|
+
[dim_x, dim_y] = [dim_y, dim_x];
|
|
17580
|
+
[flip_x, flip_y] = [flip_y, flip_x];
|
|
17581
|
+
flipDims[dim_x] = flip_x; flipDims[dim_y] = flip_y;
|
|
17582
|
+
mvViews.forEach(v => { mvDrawFrame(v); mvRender(v); });
|
|
17587
17583
|
showStatus('transposed x/y (all panes)');
|
|
17588
17584
|
if (typeof _cropReconcileBindings === 'function') _cropReconcileBindings();
|
|
17589
17585
|
return;
|
|
17590
17586
|
}
|
|
17591
17587
|
const wasMosaic = dim_z >= 0;
|
|
17592
|
-
|
|
17593
|
-
|
|
17594
|
-
|
|
17595
|
-
|
|
17596
|
-
updateView(); triggerPreload();
|
|
17597
|
-
});
|
|
17588
|
+
[dim_x, dim_y] = [dim_y, dim_x];
|
|
17589
|
+
[flip_x, flip_y] = [flip_y, flip_x];
|
|
17590
|
+
if (!wasMosaic) dim_z = -1;
|
|
17591
|
+
updateView(); triggerPreload();
|
|
17598
17592
|
showStatus(wasMosaic ? 'transposed x/y (mosaic)' : 'transposed x/y');
|
|
17599
17593
|
if (typeof _cropReconcileBindings === 'function') _cropReconcileBindings();
|
|
17600
17594
|
},
|
|
@@ -17605,34 +17599,32 @@
|
|
|
17605
17599
|
run: (ctx, e) => {
|
|
17606
17600
|
if (_segMode) { _segReset(); return; }
|
|
17607
17601
|
if (multiViewActive) {
|
|
17608
|
-
|
|
17609
|
-
|
|
17610
|
-
|
|
17611
|
-
|
|
17612
|
-
|
|
17613
|
-
|
|
17614
|
-
|
|
17615
|
-
|
|
17616
|
-
|
|
17617
|
-
|
|
17618
|
-
|
|
17619
|
-
|
|
17620
|
-
|
|
17621
|
-
|
|
17622
|
-
|
|
17623
|
-
|
|
17624
|
-
|
|
17625
|
-
|
|
17626
|
-
|
|
17627
|
-
|
|
17628
|
-
|
|
17629
|
-
|
|
17630
|
-
|
|
17631
|
-
|
|
17632
|
-
|
|
17633
|
-
|
|
17634
|
-
}
|
|
17635
|
-
});
|
|
17602
|
+
const oldDimX = dim_x, oldDimY = dim_y;
|
|
17603
|
+
dim_x = oldDimY; dim_y = oldDimX;
|
|
17604
|
+
const ofx = flipDims[oldDimX] || false, ofy = flipDims[oldDimY] || false;
|
|
17605
|
+
flipDims[dim_x] = ofy; flipDims[dim_y] = !ofx;
|
|
17606
|
+
const third = mvDims.find(d => d !== oldDimX && d !== oldDimY);
|
|
17607
|
+
mvDims = [dim_y, dim_x, third];
|
|
17608
|
+
const newDefs = [
|
|
17609
|
+
{ dimX: mvDims[1], dimY: mvDims[0], sliceDir: mvDims[2] },
|
|
17610
|
+
{ dimX: mvDims[2], dimY: mvDims[0], sliceDir: mvDims[1] },
|
|
17611
|
+
{ dimX: mvDims[2], dimY: mvDims[1], sliceDir: mvDims[0] },
|
|
17612
|
+
];
|
|
17613
|
+
mvViews.forEach((v, i) => {
|
|
17614
|
+
v.dimX = newDefs[i].dimX; v.dimY = newDefs[i].dimY;
|
|
17615
|
+
v.sliceDir = newDefs[i].sliceDir;
|
|
17616
|
+
v.seq++;
|
|
17617
|
+
});
|
|
17618
|
+
mvViews.forEach(v => { mvDrawFrame(v); mvRender(v); });
|
|
17619
|
+
const _axLetters = ['x', 'y', 'z'];
|
|
17620
|
+
mvViews.forEach((v, i) => {
|
|
17621
|
+
const newX = _axLetters[mvDims.indexOf(v.dimX)] || '?';
|
|
17622
|
+
const newY = _axLetters[mvDims.indexOf(v.dimY)] || '?';
|
|
17623
|
+
v.axLblX = newX; v.axLblY = newY;
|
|
17624
|
+
if (v.axesSvg) {
|
|
17625
|
+
v.axesSvg.querySelector('.axes-lbl-x').textContent = newX;
|
|
17626
|
+
v.axesSvg.querySelector('.axes-lbl-y').textContent = newY;
|
|
17627
|
+
}
|
|
17636
17628
|
});
|
|
17637
17629
|
showStatus('rotated 90°');
|
|
17638
17630
|
renderInfo(); saveState();
|
|
@@ -17645,11 +17637,9 @@
|
|
|
17645
17637
|
if (lastImageData) ctx.putImageData(applyFlips(lastImageData, lastImgW, lastImgH), 0, 0);
|
|
17646
17638
|
renderInfo(); saveState();
|
|
17647
17639
|
} else {
|
|
17648
|
-
|
|
17649
|
-
|
|
17650
|
-
|
|
17651
|
-
updateView(); triggerPreload();
|
|
17652
|
-
});
|
|
17640
|
+
const [odx, ody, ofx, ofy] = [dim_x, dim_y, flip_x, flip_y];
|
|
17641
|
+
dim_x = ody; dim_y = odx; flip_x = ofy; flip_y = !ofx;
|
|
17642
|
+
updateView(); triggerPreload();
|
|
17653
17643
|
showStatus('rotated 90°'); saveState();
|
|
17654
17644
|
}
|
|
17655
17645
|
if (typeof _cropReconcileBindings === 'function') _cropReconcileBindings();
|
|
@@ -21058,7 +21048,7 @@
|
|
|
21058
21048
|
|
|
21059
21049
|
/* ── JS: Keyboard Shortcuts ───────────────────────────────── */
|
|
21060
21050
|
|
|
21061
|
-
|
|
21051
|
+
function _handleViewerShortcutKeydown(e) {
|
|
21062
21052
|
e.preventDefault();
|
|
21063
21053
|
e.stopImmediatePropagation();
|
|
21064
21054
|
if (_cmapPickerOpen) {
|
|
@@ -21146,7 +21136,14 @@
|
|
|
21146
21136
|
document.getElementById('info-overlay').classList.remove('visible');
|
|
21147
21137
|
return;
|
|
21148
21138
|
}
|
|
21149
|
-
}
|
|
21139
|
+
}
|
|
21140
|
+
|
|
21141
|
+
sink.addEventListener('keydown', _handleViewerShortcutKeydown);
|
|
21142
|
+
document.addEventListener('keydown', (e) => {
|
|
21143
|
+
if (e.target === sink) return;
|
|
21144
|
+
if (_isEditableActive()) return;
|
|
21145
|
+
_handleViewerShortcutKeydown(e);
|
|
21146
|
+
}, true);
|
|
21150
21147
|
|
|
21151
21148
|
// Hold-`g` finalizer. The picker handles its own commit/cancel
|
|
21152
21149
|
// through armUp(); this listener just routes the keyup to it. Listen
|