arrayview 0.26.2__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.
Files changed (215) hide show
  1. {arrayview-0.26.2 → arrayview-0.26.3}/PKG-INFO +1 -1
  2. arrayview-0.26.3/docs/launch-routing-refactor.md +167 -0
  3. {arrayview-0.26.2 → arrayview-0.26.3}/pyproject.toml +1 -1
  4. arrayview-0.26.3/src/arrayview/_launch_plan.py +285 -0
  5. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_launcher.py +131 -54
  6. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_platform.py +14 -9
  7. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_server.py +1 -0
  8. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_viewer.html +49 -9
  9. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_vscode_signal.py +31 -57
  10. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_api.py +207 -5
  11. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_cli.py +62 -0
  12. arrayview-0.26.3/tests/test_launch_plan.py +239 -0
  13. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_lifecycle_contract.py +48 -0
  14. {arrayview-0.26.2 → arrayview-0.26.3}/uv.lock +1 -1
  15. {arrayview-0.26.2 → arrayview-0.26.3}/.agents/skills/frontend-designer/SKILL.md +0 -0
  16. {arrayview-0.26.2 → arrayview-0.26.3}/.agents/skills/invocation-consistency/SKILL.md +0 -0
  17. {arrayview-0.26.2 → arrayview-0.26.3}/.agents/skills/modes-consistency/SKILL.md +0 -0
  18. {arrayview-0.26.2 → arrayview-0.26.3}/.agents/skills/playwright-cli/SKILL.md +0 -0
  19. {arrayview-0.26.2 → arrayview-0.26.3}/.agents/skills/playwright-cli/references/element-attributes.md +0 -0
  20. {arrayview-0.26.2 → arrayview-0.26.3}/.agents/skills/playwright-cli/references/playwright-tests.md +0 -0
  21. {arrayview-0.26.2 → arrayview-0.26.3}/.agents/skills/playwright-cli/references/request-mocking.md +0 -0
  22. {arrayview-0.26.2 → arrayview-0.26.3}/.agents/skills/playwright-cli/references/running-code.md +0 -0
  23. {arrayview-0.26.2 → arrayview-0.26.3}/.agents/skills/playwright-cli/references/session-management.md +0 -0
  24. {arrayview-0.26.2 → arrayview-0.26.3}/.agents/skills/playwright-cli/references/storage-state.md +0 -0
  25. {arrayview-0.26.2 → arrayview-0.26.3}/.agents/skills/playwright-cli/references/test-generation.md +0 -0
  26. {arrayview-0.26.2 → arrayview-0.26.3}/.agents/skills/playwright-cli/references/tracing.md +0 -0
  27. {arrayview-0.26.2 → arrayview-0.26.3}/.agents/skills/playwright-cli/references/video-recording.md +0 -0
  28. {arrayview-0.26.2 → arrayview-0.26.3}/.agents/skills/todo-workflow/SKILL.md +0 -0
  29. {arrayview-0.26.2 → arrayview-0.26.3}/.agents/skills/ui-consistency-audit/SKILL.md +0 -0
  30. {arrayview-0.26.2 → arrayview-0.26.3}/.agents/skills/viewer-ui-checklist/SKILL.md +0 -0
  31. {arrayview-0.26.2 → arrayview-0.26.3}/.agents/skills/visual-bug-fixing/SKILL.md +0 -0
  32. {arrayview-0.26.2 → arrayview-0.26.3}/.github/copilot-instructions.md +0 -0
  33. {arrayview-0.26.2 → arrayview-0.26.3}/.github/workflows/docs.yml +0 -0
  34. {arrayview-0.26.2 → arrayview-0.26.3}/.github/workflows/python-publish.yml +0 -0
  35. {arrayview-0.26.2 → arrayview-0.26.3}/.gitignore +0 -0
  36. {arrayview-0.26.2 → arrayview-0.26.3}/.ignore +0 -0
  37. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/AGENTS.md +0 -0
  38. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/ROUTER.md +0 -0
  39. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/SETUP.md +0 -0
  40. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/SYNC.md +0 -0
  41. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/context/architecture.md +0 -0
  42. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/context/conventions.md +0 -0
  43. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/context/decisions.md +0 -0
  44. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/context/frontend.md +0 -0
  45. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/context/lifecycle.md +0 -0
  46. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/context/project-state.md +0 -0
  47. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/context/render-pipeline.md +0 -0
  48. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/context/setup.md +0 -0
  49. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/context/stack.md +0 -0
  50. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/patterns/INDEX.md +0 -0
  51. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/patterns/README.md +0 -0
  52. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/patterns/add-file-format.md +0 -0
  53. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/patterns/add-server-endpoint.md +0 -0
  54. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/patterns/animation-verify.md +0 -0
  55. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/patterns/debug-render.md +0 -0
  56. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/patterns/debug-vscode-extension-python.md +0 -0
  57. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/patterns/extend-compare-mode.md +0 -0
  58. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/patterns/extract-server-route-module.md +0 -0
  59. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/patterns/frontend-change.md +0 -0
  60. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/setup.sh +0 -0
  61. {arrayview-0.26.2 → arrayview-0.26.3}/.mex/sync.sh +0 -0
  62. {arrayview-0.26.2 → arrayview-0.26.3}/.opencode/opencode.json +0 -0
  63. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T18-46-49-737Z.yml +0 -0
  64. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T18-48-21-979Z.yml +0 -0
  65. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T18-51-35-665Z.yml +0 -0
  66. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T19-07-01-393Z.yml +0 -0
  67. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T19-14-37-969Z.yml +0 -0
  68. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T19-21-30-940Z.yml +0 -0
  69. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T19-23-08-126Z.yml +0 -0
  70. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T19-29-33-155Z.yml +0 -0
  71. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T19-31-25-336Z.yml +0 -0
  72. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T19-31-53-789Z.yml +0 -0
  73. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T19-39-12-257Z.yml +0 -0
  74. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T19-39-16-449Z.yml +0 -0
  75. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T20-15-25-513Z.yml +0 -0
  76. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T20-25-13-179Z.yml +0 -0
  77. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T20-39-01-435Z.yml +0 -0
  78. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-01-27-659Z.yml +0 -0
  79. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-01-41-283Z.yml +0 -0
  80. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-03-00-625Z.yml +0 -0
  81. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-04-12-887Z.yml +0 -0
  82. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-33-39-044Z.yml +0 -0
  83. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-38-01-530Z.yml +0 -0
  84. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-45-20-383Z.yml +0 -0
  85. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-55-11-545Z.yml +0 -0
  86. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-56-03-307Z.yml +0 -0
  87. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-56-35-733Z.yml +0 -0
  88. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-57-12-181Z.yml +0 -0
  89. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-57-37-748Z.yml +0 -0
  90. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T21-58-13-679Z.yml +0 -0
  91. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-06T22-37-23-895Z.yml +0 -0
  92. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T00-39-18-637Z.yml +0 -0
  93. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T01-41-46-243Z.yml +0 -0
  94. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T04-31-48-472Z.yml +0 -0
  95. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-14-15-632Z.yml +0 -0
  96. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-14-47-582Z.yml +0 -0
  97. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-16-23-471Z.yml +0 -0
  98. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-17-10-247Z.yml +0 -0
  99. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-18-24-707Z.yml +0 -0
  100. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-20-06-164Z.yml +0 -0
  101. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-20-28-342Z.yml +0 -0
  102. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-21-54-962Z.yml +0 -0
  103. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-22-34-666Z.yml +0 -0
  104. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-23-11-336Z.yml +0 -0
  105. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-23-36-260Z.yml +0 -0
  106. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-24-09-267Z.yml +0 -0
  107. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-24-35-434Z.yml +0 -0
  108. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-25-57-010Z.yml +0 -0
  109. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-34-48-823Z.yml +0 -0
  110. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-46-46-468Z.yml +0 -0
  111. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-48-17-930Z.yml +0 -0
  112. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-49-26-400Z.yml +0 -0
  113. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-50-31-563Z.yml +0 -0
  114. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/page-2026-05-07T12-56-45-568Z.yml +0 -0
  115. {arrayview-0.26.2 → arrayview-0.26.3}/.playwright-cli/theme-dark.yml +0 -0
  116. {arrayview-0.26.2 → arrayview-0.26.3}/.python-version +0 -0
  117. {arrayview-0.26.2 → arrayview-0.26.3}/.vscode/settings.json +0 -0
  118. {arrayview-0.26.2 → arrayview-0.26.3}/AGENTS.md +0 -0
  119. {arrayview-0.26.2 → arrayview-0.26.3}/CONTRIBUTING.md +0 -0
  120. {arrayview-0.26.2 → arrayview-0.26.3}/DESIGN.md +0 -0
  121. {arrayview-0.26.2 → arrayview-0.26.3}/LICENSE +0 -0
  122. {arrayview-0.26.2 → arrayview-0.26.3}/README.md +0 -0
  123. {arrayview-0.26.2 → arrayview-0.26.3}/docs/comparing.md +0 -0
  124. {arrayview-0.26.2 → arrayview-0.26.3}/docs/configuration.md +0 -0
  125. {arrayview-0.26.2 → arrayview-0.26.3}/docs/display.md +0 -0
  126. {arrayview-0.26.2 → arrayview-0.26.3}/docs/index.md +0 -0
  127. {arrayview-0.26.2 → arrayview-0.26.3}/docs/lifecycle.md +0 -0
  128. {arrayview-0.26.2 → arrayview-0.26.3}/docs/loading.md +0 -0
  129. {arrayview-0.26.2 → arrayview-0.26.3}/docs/logo.png +0 -0
  130. {arrayview-0.26.2 → arrayview-0.26.3}/docs/measurement.md +0 -0
  131. {arrayview-0.26.2 → arrayview-0.26.3}/docs/remote.md +0 -0
  132. {arrayview-0.26.2 → arrayview-0.26.3}/docs/stylesheets/extra.css +0 -0
  133. {arrayview-0.26.2 → arrayview-0.26.3}/docs/viewing.md +0 -0
  134. {arrayview-0.26.2 → arrayview-0.26.3}/matlab/arrayview.m +0 -0
  135. {arrayview-0.26.2 → arrayview-0.26.3}/mkdocs.yml +0 -0
  136. {arrayview-0.26.2 → arrayview-0.26.3}/plans/2026-04-14-immersive-animation.md +0 -0
  137. {arrayview-0.26.2 → arrayview-0.26.3}/plans/2026-05-07-unified-colormap-picker.md +0 -0
  138. {arrayview-0.26.2 → arrayview-0.26.3}/plans/arrayview_tool_menu_fix_prompt.md +0 -0
  139. {arrayview-0.26.2 → arrayview-0.26.3}/plans/arrayview_tool_menu_implementation_plan.md +0 -0
  140. {arrayview-0.26.2 → arrayview-0.26.3}/plans/egg-placement-mockup.html +0 -0
  141. {arrayview-0.26.2 → arrayview-0.26.3}/plans/refactoring_plan.md +0 -0
  142. {arrayview-0.26.2 → arrayview-0.26.3}/plans/webview/LOG.md +0 -0
  143. {arrayview-0.26.2 → arrayview-0.26.3}/scripts/demo.py +0 -0
  144. {arrayview-0.26.2 → arrayview-0.26.3}/scripts/release.sh +0 -0
  145. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/ARCHITECTURE.md +0 -0
  146. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/__init__.py +0 -0
  147. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/__main__.py +0 -0
  148. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_analysis.py +0 -0
  149. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_app.py +0 -0
  150. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_codex_open.py +0 -0
  151. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_config.py +0 -0
  152. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_diff.py +0 -0
  153. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_icon.png +0 -0
  154. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_imaging.py +0 -0
  155. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_io.py +0 -0
  156. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_lifecycle.py +0 -0
  157. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_overlays.py +0 -0
  158. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_render.py +0 -0
  159. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_routes_analysis.py +0 -0
  160. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_routes_export.py +0 -0
  161. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_routes_loading.py +0 -0
  162. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_routes_persistence.py +0 -0
  163. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_routes_preload.py +0 -0
  164. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_routes_query.py +0 -0
  165. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_routes_rendering.py +0 -0
  166. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_routes_segmentation.py +0 -0
  167. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_routes_state.py +0 -0
  168. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_routes_vectorfield.py +0 -0
  169. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_routes_websocket.py +0 -0
  170. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_segmentation.py +0 -0
  171. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_session.py +0 -0
  172. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_shell.html +0 -0
  173. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_stdio_server.py +0 -0
  174. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_synthetic_mri.py +0 -0
  175. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_torch.py +0 -0
  176. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_vectorfield.py +0 -0
  177. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_vscode.py +0 -0
  178. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_vscode_browser.py +0 -0
  179. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_vscode_extension.py +0 -0
  180. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/_vscode_shm.py +0 -0
  181. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/arrayview-opener.vsix +0 -0
  182. {arrayview-0.26.2 → arrayview-0.26.3}/src/arrayview/gsap.min.js +0 -0
  183. {arrayview-0.26.2 → arrayview-0.26.3}/tests/capture_v_animation.py +0 -0
  184. {arrayview-0.26.2 → arrayview-0.26.3}/tests/conftest.py +0 -0
  185. {arrayview-0.26.2 → arrayview-0.26.3}/tests/lifecycle_matrix.py +0 -0
  186. {arrayview-0.26.2 → arrayview-0.26.3}/tests/make_vectorfield_test_arrays.py +0 -0
  187. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_backend_shared.py +0 -0
  188. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_browser.py +0 -0
  189. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_colorbar_hover_highlight.py +0 -0
  190. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_command_reachability.py +0 -0
  191. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_config.py +0 -0
  192. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_cross_mode_parametrized.py +0 -0
  193. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_interactions.py +0 -0
  194. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_io.py +0 -0
  195. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_large_arrays.py +0 -0
  196. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_loading_server.py +0 -0
  197. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_mode_consistency.py +0 -0
  198. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_mode_entry_batching.py +0 -0
  199. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_mode_matrix.py +0 -0
  200. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_mode_roundtrip.py +0 -0
  201. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_nifti_meta.py +0 -0
  202. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_rgb_pixel_art.py +0 -0
  203. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_stdio_server.py +0 -0
  204. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_torch.py +0 -0
  205. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_view_component_integration.py +0 -0
  206. {arrayview-0.26.2 → arrayview-0.26.3}/tests/test_view_component_unit.py +0 -0
  207. {arrayview-0.26.2 → arrayview-0.26.3}/tests/ui_audit.py +0 -0
  208. {arrayview-0.26.2 → arrayview-0.26.3}/tests/v_anim_frames/.gitkeep +0 -0
  209. {arrayview-0.26.2 → arrayview-0.26.3}/tests/visual_smoke.py +0 -0
  210. {arrayview-0.26.2 → arrayview-0.26.3}/vscode-extension/AGENTS.md +0 -0
  211. {arrayview-0.26.2 → arrayview-0.26.3}/vscode-extension/LICENSE +0 -0
  212. {arrayview-0.26.2 → arrayview-0.26.3}/vscode-extension/extension.js +0 -0
  213. {arrayview-0.26.2 → arrayview-0.26.3}/vscode-extension/lifecycle_helpers.js +0 -0
  214. {arrayview-0.26.2 → arrayview-0.26.3}/vscode-extension/package.json +0 -0
  215. {arrayview-0.26.2 → arrayview-0.26.3}/vscode-extension/test_lifecycle_helpers.js +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arrayview
3
- Version: 0.26.2
3
+ Version: 0.26.3
4
4
  Summary: Fast multi-dimensional array viewer
5
5
  Project-URL: Home, https://github.com/oscarvanderheide/arrayview
6
6
  Project-URL: Source, https://github.com/oscarvanderheide/arrayview
@@ -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.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "arrayview"
7
- version = "0.26.2"
7
+ version = "0.26.3"
8
8
  description = "Fast multi-dimensional array viewer"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.12"
@@ -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