arrayview 0.26.1__tar.gz → 0.26.3__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.1 → arrayview-0.26.3}/PKG-INFO +1 -1
- arrayview-0.26.3/docs/launch-routing-refactor.md +167 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/pyproject.toml +1 -1
- arrayview-0.26.3/src/arrayview/_launch_plan.py +285 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_launcher.py +131 -54
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_platform.py +14 -9
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_server.py +1 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_stdio_server.py +51 -2
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_viewer.html +49 -9
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_vscode_signal.py +31 -57
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_api.py +207 -5
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_cli.py +62 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_io.py +58 -1
- arrayview-0.26.3/tests/test_launch_plan.py +239 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_lifecycle_contract.py +48 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/uv.lock +1 -1
- {arrayview-0.26.1 → arrayview-0.26.3}/.agents/skills/frontend-designer/SKILL.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.agents/skills/invocation-consistency/SKILL.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.agents/skills/modes-consistency/SKILL.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.agents/skills/playwright-cli/SKILL.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.agents/skills/playwright-cli/references/element-attributes.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.agents/skills/playwright-cli/references/playwright-tests.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.agents/skills/playwright-cli/references/request-mocking.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.agents/skills/playwright-cli/references/running-code.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.agents/skills/playwright-cli/references/session-management.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.agents/skills/playwright-cli/references/storage-state.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.agents/skills/playwright-cli/references/test-generation.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.agents/skills/playwright-cli/references/tracing.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.agents/skills/playwright-cli/references/video-recording.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.agents/skills/todo-workflow/SKILL.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.agents/skills/ui-consistency-audit/SKILL.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.agents/skills/viewer-ui-checklist/SKILL.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.agents/skills/visual-bug-fixing/SKILL.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.github/copilot-instructions.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.github/workflows/docs.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.github/workflows/python-publish.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.gitignore +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.ignore +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/AGENTS.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/ROUTER.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/SETUP.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/SYNC.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/context/architecture.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/context/conventions.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/context/decisions.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/context/frontend.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/context/lifecycle.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/context/project-state.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/context/render-pipeline.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/context/setup.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/context/stack.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/patterns/INDEX.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/patterns/README.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/patterns/add-file-format.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/patterns/add-server-endpoint.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/patterns/animation-verify.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/patterns/debug-render.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/patterns/debug-vscode-extension-python.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/patterns/extend-compare-mode.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/patterns/extract-server-route-module.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/patterns/frontend-change.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/setup.sh +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.mex/sync.sh +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.opencode/opencode.json +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T18-46-49-737Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T18-48-21-979Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T18-51-35-665Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T19-07-01-393Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T19-14-37-969Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T19-21-30-940Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T19-23-08-126Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T19-29-33-155Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T19-31-25-336Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T19-31-53-789Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T19-39-12-257Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T19-39-16-449Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T20-15-25-513Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T20-25-13-179Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T20-39-01-435Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-01-27-659Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-01-41-283Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-03-00-625Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-04-12-887Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-33-39-044Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-38-01-530Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-45-20-383Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-55-11-545Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-56-03-307Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-56-35-733Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-57-12-181Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-57-37-748Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-58-13-679Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T22-37-23-895Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T00-39-18-637Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T01-41-46-243Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T04-31-48-472Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-14-15-632Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-14-47-582Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-16-23-471Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-17-10-247Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-18-24-707Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-20-06-164Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-20-28-342Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-21-54-962Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-22-34-666Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-23-11-336Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-23-36-260Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-24-09-267Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-24-35-434Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-25-57-010Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-34-48-823Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-46-46-468Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-48-17-930Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-49-26-400Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-50-31-563Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-56-45-568Z.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.playwright-cli/theme-dark.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.python-version +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/.vscode/settings.json +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/AGENTS.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/CONTRIBUTING.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/DESIGN.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/LICENSE +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/README.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/docs/comparing.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/docs/configuration.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/docs/display.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/docs/index.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/docs/lifecycle.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/docs/loading.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/docs/logo.png +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/docs/measurement.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/docs/remote.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/docs/stylesheets/extra.css +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/docs/viewing.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/matlab/arrayview.m +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/mkdocs.yml +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/plans/2026-04-14-immersive-animation.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/plans/2026-05-07-unified-colormap-picker.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/plans/arrayview_tool_menu_fix_prompt.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/plans/arrayview_tool_menu_implementation_plan.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/plans/egg-placement-mockup.html +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/plans/refactoring_plan.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/plans/webview/LOG.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/scripts/demo.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/scripts/release.sh +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/ARCHITECTURE.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/__init__.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/__main__.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_analysis.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_app.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_codex_open.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_config.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_diff.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_icon.png +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_imaging.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_io.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_lifecycle.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_overlays.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_render.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_routes_analysis.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_routes_export.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_routes_loading.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_routes_persistence.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_routes_preload.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_routes_query.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_routes_rendering.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_routes_segmentation.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_routes_state.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_routes_vectorfield.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_routes_websocket.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_segmentation.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_session.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_shell.html +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_synthetic_mri.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_torch.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_vectorfield.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_vscode.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_vscode_browser.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_vscode_extension.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/_vscode_shm.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/arrayview-opener.vsix +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/src/arrayview/gsap.min.js +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/capture_v_animation.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/conftest.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/lifecycle_matrix.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/make_vectorfield_test_arrays.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_backend_shared.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_browser.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_colorbar_hover_highlight.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_command_reachability.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_config.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_cross_mode_parametrized.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_interactions.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_large_arrays.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_loading_server.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_mode_consistency.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_mode_entry_batching.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_mode_matrix.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_mode_roundtrip.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_nifti_meta.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_rgb_pixel_art.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_stdio_server.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_torch.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_view_component_integration.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/test_view_component_unit.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/ui_audit.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/v_anim_frames/.gitkeep +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/tests/visual_smoke.py +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/vscode-extension/AGENTS.md +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/vscode-extension/LICENSE +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/vscode-extension/extension.js +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/vscode-extension/lifecycle_helpers.js +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/vscode-extension/package.json +0 -0
- {arrayview-0.26.1 → arrayview-0.26.3}/vscode-extension/test_lifecycle_helpers.js +0 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Launch Routing Refactor
|
|
2
|
+
|
|
3
|
+
ArrayView currently opens displays through several partially overlapping paths:
|
|
4
|
+
CLI, `view()`, Jupyter, Julia, VS Code local, VS Code tunnel, SSH, native
|
|
5
|
+
pywebview, browser fallback, and stdio/direct webview mode. The same policy is
|
|
6
|
+
encoded in multiple places, so fixes in one path can leave another path broken.
|
|
7
|
+
|
|
8
|
+
This refactor makes launch routing explicit, shared, and inspectable.
|
|
9
|
+
|
|
10
|
+
## Problem
|
|
11
|
+
|
|
12
|
+
The brittle behavior comes from policy being split across:
|
|
13
|
+
|
|
14
|
+
- environment detection in `_platform.py`
|
|
15
|
+
- CLI planning in `_launcher.py`
|
|
16
|
+
- Python `view()` planning in `_launcher.py`
|
|
17
|
+
- VS Code signal/direct-mode fallback in `_vscode_browser.py` and `_vscode_signal.py`
|
|
18
|
+
- separate HTTP and stdio session registration paths
|
|
19
|
+
|
|
20
|
+
Symptoms seen recently:
|
|
21
|
+
|
|
22
|
+
- CLI diagnose says native pywebview should open, but runtime falls back to a browser.
|
|
23
|
+
- Linux native detection allows GTK systems while native launch forced Qt.
|
|
24
|
+
- `view()` and CLI had different native readiness checks.
|
|
25
|
+
- Existing-server remote behavior can accidentally switch from registered URL mode to direct stdio mode.
|
|
26
|
+
|
|
27
|
+
## Target Model
|
|
28
|
+
|
|
29
|
+
Create a single launch plan used by both CLI and `view()`.
|
|
30
|
+
|
|
31
|
+
The plan should be data-only:
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
Invocation = "cli" | "python" | "jupyter" | "julia" | "stdio" | "codex"
|
|
35
|
+
Environment = "terminal" | "vscode_local" | "vscode_remote" | "ssh" | "jupyter" | "julia"
|
|
36
|
+
Transport = "http" | "stdio_file" | "stdio_shm" | "none"
|
|
37
|
+
ServerOwner = "existing" | "spawned_daemon" | "in_process" | "persistent" | "external"
|
|
38
|
+
Display = "native" | "browser" | "vscode" | "inline" | "none"
|
|
39
|
+
Registration = "http_load" | "daemon_startup" | "in_process_session" | "stdio_register" | "relay"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Executors may perform work, but should not invent policy. Browser fallback,
|
|
43
|
+
VS Code direct mode, native readiness, and remote behavior must be selected by
|
|
44
|
+
the plan.
|
|
45
|
+
|
|
46
|
+
## Phase 1: Snapshot And Diagnose
|
|
47
|
+
|
|
48
|
+
Add a launch environment snapshot:
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
snapshot_launch_environment(port, invocation, requested_window)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
It should capture:
|
|
55
|
+
|
|
56
|
+
- platform
|
|
57
|
+
- relevant environment variables
|
|
58
|
+
- config default
|
|
59
|
+
- native backend: `qt`, `gtk`, `""`, or `None`
|
|
60
|
+
- port busy / ArrayView server alive / server PID / hostname
|
|
61
|
+
- VS Code local vs remote/tunnel
|
|
62
|
+
- SSH state
|
|
63
|
+
- Jupyter and Julia state
|
|
64
|
+
|
|
65
|
+
`arrayview --diagnose` must print the same snapshot and plan used by real launch.
|
|
66
|
+
|
|
67
|
+
## Phase 2: Shared Planner
|
|
68
|
+
|
|
69
|
+
Add:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
plan_launch(invocation, request, env) -> LaunchPlan
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The planner decides:
|
|
76
|
+
|
|
77
|
+
- transport: HTTP, stdio file, stdio SHM, or none
|
|
78
|
+
- display: native, browser, vscode, inline, none
|
|
79
|
+
- server owner: existing, spawned daemon, in-process, persistent, external
|
|
80
|
+
- registration: HTTP load, daemon startup, in-process session, stdio register, relay
|
|
81
|
+
- fallback policy: whether fallback is allowed and where it goes
|
|
82
|
+
|
|
83
|
+
CLI and `view()` should both call this planner.
|
|
84
|
+
|
|
85
|
+
## Phase 3: Native Readiness
|
|
86
|
+
|
|
87
|
+
Use one native readiness primitive everywhere:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
open_native_shell(...)
|
|
91
|
+
wait_native_ready(shell_socket=True, viewer_socket=True, process_alive=True)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Rules:
|
|
95
|
+
|
|
96
|
+
- process crash is native launch failure
|
|
97
|
+
- shell WebSocket means the native window is usable
|
|
98
|
+
- viewer WebSocket means the content loaded
|
|
99
|
+
- missing viewer WebSocket alone is not native failure
|
|
100
|
+
- browser fallback happens only after native process/shell failure
|
|
101
|
+
|
|
102
|
+
## Phase 4: Remote Existing-Server Fix
|
|
103
|
+
|
|
104
|
+
Separate these cases explicitly:
|
|
105
|
+
|
|
106
|
+
- `Display=vscode`, `Transport=http`: open the registered HTTP URL in VS Code
|
|
107
|
+
- `Display=vscode`, `Transport=stdio_file`: open direct stdio file session
|
|
108
|
+
|
|
109
|
+
Do not let `_open_browser(..., filepath=...)` implicitly switch an existing
|
|
110
|
+
registered URL into direct stdio mode.
|
|
111
|
+
|
|
112
|
+
## Phase 5: VS Code Direct Mode
|
|
113
|
+
|
|
114
|
+
Consolidate SHM/direct mode:
|
|
115
|
+
|
|
116
|
+
- one `_open_direct_via_shm()` implementation
|
|
117
|
+
- one SHM wait loop
|
|
118
|
+
- Julia remote, Python remote, and tunnel notebook paths call the same helper
|
|
119
|
+
|
|
120
|
+
Remove or clearly deprecate duplicate direct-mode helpers.
|
|
121
|
+
|
|
122
|
+
## Phase 6: Registration Semantics
|
|
123
|
+
|
|
124
|
+
Align HTTP `/load` and stdio registration:
|
|
125
|
+
|
|
126
|
+
- shared file/key selection behavior
|
|
127
|
+
- shared RAM guard behavior where possible
|
|
128
|
+
- shared RGB/vectorfield/overlay/compare semantics where possible
|
|
129
|
+
- documented differences where transports genuinely diverge
|
|
130
|
+
|
|
131
|
+
## Contract Tests
|
|
132
|
+
|
|
133
|
+
Prefer contract tests over broad visual smoke for launch routing:
|
|
134
|
+
|
|
135
|
+
- macOS terminal CLI native
|
|
136
|
+
- Linux Qt native
|
|
137
|
+
- Linux GTK native
|
|
138
|
+
- Linux no display browser fallback
|
|
139
|
+
- plain SSH guidance
|
|
140
|
+
- VS Code local tab
|
|
141
|
+
- VS Code remote direct stdio
|
|
142
|
+
- VS Code remote existing HTTP URL
|
|
143
|
+
- Jupyter inline
|
|
144
|
+
- VS Code remote notebook direct
|
|
145
|
+
- Julia subprocess/direct
|
|
146
|
+
|
|
147
|
+
Execution contracts:
|
|
148
|
+
|
|
149
|
+
- existing server native shell does not fall back only because viewer socket is late
|
|
150
|
+
- spawned daemon native shell does not fall back only because viewer socket is late
|
|
151
|
+
- `view()` native shell does not fall back only because viewer socket is late
|
|
152
|
+
- remote existing server opens registered URL, not direct stdio
|
|
153
|
+
- GTK-only Linux passes `gui="gtk"` to pywebview
|
|
154
|
+
|
|
155
|
+
## Migration Order
|
|
156
|
+
|
|
157
|
+
1. Add launch data model and environment snapshot without behavior changes.
|
|
158
|
+
2. Make `--diagnose` use the snapshot and shared planner.
|
|
159
|
+
3. Move CLI planning into `plan_launch()`.
|
|
160
|
+
4. Move `view()` planning into `plan_launch()`.
|
|
161
|
+
5. Unify native readiness for CLI and `view()`.
|
|
162
|
+
6. Fix remote existing-server URL/direct ambiguity.
|
|
163
|
+
7. Consolidate SHM direct mode.
|
|
164
|
+
8. Align HTTP and stdio registration semantics.
|
|
165
|
+
|
|
166
|
+
Each step should have focused invocation tests and should avoid unrelated UI or
|
|
167
|
+
rendering changes.
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"""Data-only launch routing primitives and environment snapshots."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import asdict, dataclass
|
|
6
|
+
from enum import Enum
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import socket
|
|
10
|
+
import sys
|
|
11
|
+
import urllib.request
|
|
12
|
+
|
|
13
|
+
_LOOPBACK_HOST = "localhost"
|
|
14
|
+
_PING_TIMEOUT_SECONDS = 0.2
|
|
15
|
+
|
|
16
|
+
_SNAPSHOT_ENV_KEYS = (
|
|
17
|
+
"ARRAYVIEW_WINDOW",
|
|
18
|
+
"ARRAYVIEW_WINDOW_ID",
|
|
19
|
+
"DISPLAY",
|
|
20
|
+
"TERM_PROGRAM",
|
|
21
|
+
"VSCODE_AGENT_FOLDER",
|
|
22
|
+
"VSCODE_INJECTION",
|
|
23
|
+
"VSCODE_IPC_HOOK_CLI",
|
|
24
|
+
"WAYLAND_DISPLAY",
|
|
25
|
+
"SSH_CLIENT",
|
|
26
|
+
"SSH_CONNECTION",
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class _StrEnum(str, Enum):
|
|
31
|
+
def __str__(self) -> str:
|
|
32
|
+
return self.value
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class Invocation(_StrEnum):
|
|
36
|
+
CLI = "cli"
|
|
37
|
+
PYTHON = "python"
|
|
38
|
+
JUPYTER = "jupyter"
|
|
39
|
+
JULIA = "julia"
|
|
40
|
+
STDIO = "stdio"
|
|
41
|
+
CODEX = "codex"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class Environment(_StrEnum):
|
|
45
|
+
TERMINAL = "terminal"
|
|
46
|
+
VSCODE_LOCAL = "vscode_local"
|
|
47
|
+
VSCODE_REMOTE = "vscode_remote"
|
|
48
|
+
SSH = "ssh"
|
|
49
|
+
JUPYTER = "jupyter"
|
|
50
|
+
JULIA = "julia"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class Transport(_StrEnum):
|
|
54
|
+
HTTP = "http"
|
|
55
|
+
STDIO_FILE = "stdio_file"
|
|
56
|
+
STDIO_SHM = "stdio_shm"
|
|
57
|
+
NONE = "none"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ServerOwner(_StrEnum):
|
|
61
|
+
EXISTING = "existing"
|
|
62
|
+
SPAWNED_DAEMON = "spawned_daemon"
|
|
63
|
+
IN_PROCESS = "in_process"
|
|
64
|
+
PERSISTENT = "persistent"
|
|
65
|
+
EXTERNAL = "external"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class Display(_StrEnum):
|
|
69
|
+
NATIVE = "native"
|
|
70
|
+
BROWSER = "browser"
|
|
71
|
+
VSCODE = "vscode"
|
|
72
|
+
INLINE = "inline"
|
|
73
|
+
NONE = "none"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class Registration(_StrEnum):
|
|
77
|
+
HTTP_LOAD = "http_load"
|
|
78
|
+
DAEMON_STARTUP = "daemon_startup"
|
|
79
|
+
IN_PROCESS_SESSION = "in_process_session"
|
|
80
|
+
STDIO_REGISTER = "stdio_register"
|
|
81
|
+
RELAY = "relay"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass(frozen=True)
|
|
85
|
+
class ServerSnapshot:
|
|
86
|
+
port: int
|
|
87
|
+
port_busy: bool
|
|
88
|
+
arrayview_server_alive: bool
|
|
89
|
+
server_pid: int | None = None
|
|
90
|
+
server_hostname: str | None = None
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@dataclass(frozen=True)
|
|
94
|
+
class LaunchEnvironmentSnapshot:
|
|
95
|
+
invocation: Invocation
|
|
96
|
+
requested_window: str | None
|
|
97
|
+
environment: Environment
|
|
98
|
+
platform: str
|
|
99
|
+
env_vars: dict[str, str]
|
|
100
|
+
config_default: str | None
|
|
101
|
+
native_backend: str | None
|
|
102
|
+
server: ServerSnapshot
|
|
103
|
+
in_jupyter: bool
|
|
104
|
+
in_julia: bool
|
|
105
|
+
in_vscode_terminal: bool
|
|
106
|
+
is_vscode_remote: bool
|
|
107
|
+
in_vscode_tunnel: bool
|
|
108
|
+
ssh_connection: bool
|
|
109
|
+
ssh_client: bool
|
|
110
|
+
hostname: str
|
|
111
|
+
|
|
112
|
+
def to_dict(self) -> dict:
|
|
113
|
+
return asdict(self)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@dataclass(frozen=True)
|
|
117
|
+
class LaunchRequest:
|
|
118
|
+
port: int
|
|
119
|
+
requested_window: str | None = None
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@dataclass(frozen=True)
|
|
123
|
+
class LaunchPlan:
|
|
124
|
+
invocation: Invocation
|
|
125
|
+
environment: Environment
|
|
126
|
+
transport: Transport
|
|
127
|
+
server_owner: ServerOwner
|
|
128
|
+
display: Display
|
|
129
|
+
registration: Registration
|
|
130
|
+
fallback_display: Display | None = None
|
|
131
|
+
fallback_allowed: bool = False
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def snapshot_launch_environment(
|
|
135
|
+
port: int,
|
|
136
|
+
invocation: Invocation | str,
|
|
137
|
+
requested_window: str | None = None,
|
|
138
|
+
) -> LaunchEnvironmentSnapshot:
|
|
139
|
+
"""Capture launch-relevant facts without importing the server stack."""
|
|
140
|
+
inv = _coerce_invocation(invocation)
|
|
141
|
+
env_vars = _snapshot_env_vars()
|
|
142
|
+
in_jupyter = _platform_bool("_in_jupyter")
|
|
143
|
+
in_julia = _platform_bool("_is_julia_env")
|
|
144
|
+
in_vscode_terminal = _platform_bool("_in_vscode_terminal")
|
|
145
|
+
is_vscode_remote = _platform_bool("_is_vscode_remote")
|
|
146
|
+
in_vscode_tunnel = _platform_bool("_in_vscode_tunnel")
|
|
147
|
+
ssh_connection = bool(os.environ.get("SSH_CONNECTION"))
|
|
148
|
+
ssh_client = bool(os.environ.get("SSH_CLIENT"))
|
|
149
|
+
environment = _classify_environment(
|
|
150
|
+
in_jupyter=in_jupyter,
|
|
151
|
+
in_julia=in_julia,
|
|
152
|
+
in_vscode_terminal=in_vscode_terminal,
|
|
153
|
+
is_vscode_remote=is_vscode_remote,
|
|
154
|
+
ssh_connection=ssh_connection,
|
|
155
|
+
ssh_client=ssh_client,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
return LaunchEnvironmentSnapshot(
|
|
159
|
+
invocation=inv,
|
|
160
|
+
requested_window=requested_window,
|
|
161
|
+
environment=environment,
|
|
162
|
+
platform=sys.platform,
|
|
163
|
+
env_vars=env_vars,
|
|
164
|
+
config_default=_config_window_default(environment.value),
|
|
165
|
+
native_backend=_native_window_gui(),
|
|
166
|
+
server=_server_snapshot(port),
|
|
167
|
+
in_jupyter=in_jupyter,
|
|
168
|
+
in_julia=in_julia,
|
|
169
|
+
in_vscode_terminal=in_vscode_terminal,
|
|
170
|
+
is_vscode_remote=is_vscode_remote,
|
|
171
|
+
in_vscode_tunnel=in_vscode_tunnel,
|
|
172
|
+
ssh_connection=ssh_connection,
|
|
173
|
+
ssh_client=ssh_client,
|
|
174
|
+
hostname=socket.gethostname(),
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _coerce_invocation(value: Invocation | str) -> Invocation:
|
|
179
|
+
if isinstance(value, Invocation):
|
|
180
|
+
return value
|
|
181
|
+
return Invocation(value)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _snapshot_env_vars() -> dict[str, str]:
|
|
185
|
+
return {key: os.environ[key] for key in _SNAPSHOT_ENV_KEYS if key in os.environ}
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _platform_bool(name: str) -> bool:
|
|
189
|
+
try:
|
|
190
|
+
from arrayview import _platform
|
|
191
|
+
|
|
192
|
+
return bool(getattr(_platform, name)())
|
|
193
|
+
except Exception:
|
|
194
|
+
return False
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _native_window_gui() -> str | None:
|
|
198
|
+
try:
|
|
199
|
+
from arrayview._platform import _native_window_gui as native_window_gui
|
|
200
|
+
|
|
201
|
+
return native_window_gui()
|
|
202
|
+
except Exception:
|
|
203
|
+
return None
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _config_window_default(environment: str) -> str | None:
|
|
207
|
+
try:
|
|
208
|
+
from arrayview._config import load_config
|
|
209
|
+
|
|
210
|
+
cfg = load_config()
|
|
211
|
+
except Exception:
|
|
212
|
+
return None
|
|
213
|
+
window_cfg = cfg.get("window", {})
|
|
214
|
+
if not isinstance(window_cfg, dict):
|
|
215
|
+
return None
|
|
216
|
+
env_key = "vscode" if environment in {"vscode_local", "vscode_remote"} else environment
|
|
217
|
+
value = window_cfg.get(env_key) or window_cfg.get("default")
|
|
218
|
+
if isinstance(value, str):
|
|
219
|
+
return value.strip().lower() or None
|
|
220
|
+
return None
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _classify_environment(
|
|
224
|
+
*,
|
|
225
|
+
in_jupyter: bool,
|
|
226
|
+
in_julia: bool,
|
|
227
|
+
in_vscode_terminal: bool,
|
|
228
|
+
is_vscode_remote: bool,
|
|
229
|
+
ssh_connection: bool,
|
|
230
|
+
ssh_client: bool,
|
|
231
|
+
) -> Environment:
|
|
232
|
+
if in_jupyter:
|
|
233
|
+
return Environment.JUPYTER
|
|
234
|
+
if in_julia:
|
|
235
|
+
return Environment.JULIA
|
|
236
|
+
if is_vscode_remote:
|
|
237
|
+
return Environment.VSCODE_REMOTE
|
|
238
|
+
if in_vscode_terminal:
|
|
239
|
+
return Environment.VSCODE_LOCAL
|
|
240
|
+
if ssh_connection or ssh_client:
|
|
241
|
+
return Environment.SSH
|
|
242
|
+
return Environment.TERMINAL
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _server_snapshot(port: int) -> ServerSnapshot:
|
|
246
|
+
payload = _ping_arrayview_server(port)
|
|
247
|
+
return ServerSnapshot(
|
|
248
|
+
port=port,
|
|
249
|
+
port_busy=_port_busy(port),
|
|
250
|
+
arrayview_server_alive=payload is not None,
|
|
251
|
+
server_pid=_int_or_none(payload.get("pid")) if payload else None,
|
|
252
|
+
server_hostname=_str_or_none(payload.get("hostname")) if payload else None,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _port_busy(port: int) -> bool:
|
|
257
|
+
try:
|
|
258
|
+
with socket.create_connection(
|
|
259
|
+
(_LOOPBACK_HOST, port), timeout=_PING_TIMEOUT_SECONDS
|
|
260
|
+
):
|
|
261
|
+
return True
|
|
262
|
+
except OSError:
|
|
263
|
+
return False
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def _ping_arrayview_server(port: int) -> dict | None:
|
|
267
|
+
url = f"http://{_LOOPBACK_HOST}:{port}/ping"
|
|
268
|
+
try:
|
|
269
|
+
with urllib.request.urlopen(url, timeout=_PING_TIMEOUT_SECONDS) as resp:
|
|
270
|
+
if resp.status != 200:
|
|
271
|
+
return None
|
|
272
|
+
payload = json.loads(resp.read().decode("utf-8"))
|
|
273
|
+
except Exception:
|
|
274
|
+
return None
|
|
275
|
+
if payload.get("ok") is True and payload.get("service") == "arrayview":
|
|
276
|
+
return payload
|
|
277
|
+
return None
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _int_or_none(value: object) -> int | None:
|
|
281
|
+
return value if isinstance(value, int) else None
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def _str_or_none(value: object) -> str | None:
|
|
285
|
+
return value if isinstance(value, str) else None
|