arrayview 0.29.0__tar.gz → 0.29.1__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 (216) hide show
  1. {arrayview-0.29.0 → arrayview-0.29.1}/PKG-INFO +1 -1
  2. arrayview-0.29.1/docs/vscode-window-routing-plan.md +37 -0
  3. {arrayview-0.29.0 → arrayview-0.29.1}/pyproject.toml +1 -1
  4. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_vscode_extension.py +1 -1
  5. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_vscode_signal.py +49 -86
  6. arrayview-0.29.1/src/arrayview/arrayview-opener.vsix +0 -0
  7. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_lifecycle_contract.py +117 -0
  8. {arrayview-0.29.0 → arrayview-0.29.1}/uv.lock +1 -1
  9. {arrayview-0.29.0 → arrayview-0.29.1}/vscode-extension/extension.js +1 -0
  10. {arrayview-0.29.0 → arrayview-0.29.1}/vscode-extension/package.json +1 -1
  11. arrayview-0.29.0/src/arrayview/arrayview-opener.vsix +0 -0
  12. {arrayview-0.29.0 → arrayview-0.29.1}/.agents/skills/frontend-designer/SKILL.md +0 -0
  13. {arrayview-0.29.0 → arrayview-0.29.1}/.agents/skills/invocation-consistency/SKILL.md +0 -0
  14. {arrayview-0.29.0 → arrayview-0.29.1}/.agents/skills/modes-consistency/SKILL.md +0 -0
  15. {arrayview-0.29.0 → arrayview-0.29.1}/.agents/skills/playwright-cli/SKILL.md +0 -0
  16. {arrayview-0.29.0 → arrayview-0.29.1}/.agents/skills/playwright-cli/references/element-attributes.md +0 -0
  17. {arrayview-0.29.0 → arrayview-0.29.1}/.agents/skills/playwright-cli/references/playwright-tests.md +0 -0
  18. {arrayview-0.29.0 → arrayview-0.29.1}/.agents/skills/playwright-cli/references/request-mocking.md +0 -0
  19. {arrayview-0.29.0 → arrayview-0.29.1}/.agents/skills/playwright-cli/references/running-code.md +0 -0
  20. {arrayview-0.29.0 → arrayview-0.29.1}/.agents/skills/playwright-cli/references/session-management.md +0 -0
  21. {arrayview-0.29.0 → arrayview-0.29.1}/.agents/skills/playwright-cli/references/storage-state.md +0 -0
  22. {arrayview-0.29.0 → arrayview-0.29.1}/.agents/skills/playwright-cli/references/test-generation.md +0 -0
  23. {arrayview-0.29.0 → arrayview-0.29.1}/.agents/skills/playwright-cli/references/tracing.md +0 -0
  24. {arrayview-0.29.0 → arrayview-0.29.1}/.agents/skills/playwright-cli/references/video-recording.md +0 -0
  25. {arrayview-0.29.0 → arrayview-0.29.1}/.agents/skills/todo-workflow/SKILL.md +0 -0
  26. {arrayview-0.29.0 → arrayview-0.29.1}/.agents/skills/ui-consistency-audit/SKILL.md +0 -0
  27. {arrayview-0.29.0 → arrayview-0.29.1}/.agents/skills/viewer-ui-checklist/SKILL.md +0 -0
  28. {arrayview-0.29.0 → arrayview-0.29.1}/.agents/skills/visual-bug-fixing/SKILL.md +0 -0
  29. {arrayview-0.29.0 → arrayview-0.29.1}/.github/copilot-instructions.md +0 -0
  30. {arrayview-0.29.0 → arrayview-0.29.1}/.github/workflows/docs.yml +0 -0
  31. {arrayview-0.29.0 → arrayview-0.29.1}/.github/workflows/python-publish.yml +0 -0
  32. {arrayview-0.29.0 → arrayview-0.29.1}/.gitignore +0 -0
  33. {arrayview-0.29.0 → arrayview-0.29.1}/.ignore +0 -0
  34. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/AGENTS.md +0 -0
  35. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/ROUTER.md +0 -0
  36. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/SETUP.md +0 -0
  37. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/SYNC.md +0 -0
  38. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/context/architecture.md +0 -0
  39. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/context/conventions.md +0 -0
  40. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/context/decisions.md +0 -0
  41. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/context/frontend.md +0 -0
  42. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/context/lifecycle.md +0 -0
  43. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/context/project-state.md +0 -0
  44. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/context/render-pipeline.md +0 -0
  45. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/context/setup.md +0 -0
  46. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/context/stack.md +0 -0
  47. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/patterns/INDEX.md +0 -0
  48. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/patterns/README.md +0 -0
  49. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/patterns/add-file-format.md +0 -0
  50. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/patterns/add-server-endpoint.md +0 -0
  51. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/patterns/animation-verify.md +0 -0
  52. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/patterns/debug-render.md +0 -0
  53. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/patterns/debug-vscode-extension-python.md +0 -0
  54. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/patterns/extend-compare-mode.md +0 -0
  55. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/patterns/extract-server-route-module.md +0 -0
  56. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/patterns/frontend-change.md +0 -0
  57. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/setup.sh +0 -0
  58. {arrayview-0.29.0 → arrayview-0.29.1}/.mex/sync.sh +0 -0
  59. {arrayview-0.29.0 → arrayview-0.29.1}/.opencode/opencode.json +0 -0
  60. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T18-46-49-737Z.yml +0 -0
  61. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T18-48-21-979Z.yml +0 -0
  62. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T18-51-35-665Z.yml +0 -0
  63. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T19-07-01-393Z.yml +0 -0
  64. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T19-14-37-969Z.yml +0 -0
  65. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T19-21-30-940Z.yml +0 -0
  66. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T19-23-08-126Z.yml +0 -0
  67. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T19-29-33-155Z.yml +0 -0
  68. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T19-31-25-336Z.yml +0 -0
  69. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T19-31-53-789Z.yml +0 -0
  70. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T19-39-12-257Z.yml +0 -0
  71. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T19-39-16-449Z.yml +0 -0
  72. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T20-15-25-513Z.yml +0 -0
  73. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T20-25-13-179Z.yml +0 -0
  74. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T20-39-01-435Z.yml +0 -0
  75. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T21-01-27-659Z.yml +0 -0
  76. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T21-01-41-283Z.yml +0 -0
  77. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T21-03-00-625Z.yml +0 -0
  78. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T21-04-12-887Z.yml +0 -0
  79. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T21-33-39-044Z.yml +0 -0
  80. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T21-38-01-530Z.yml +0 -0
  81. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T21-45-20-383Z.yml +0 -0
  82. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T21-55-11-545Z.yml +0 -0
  83. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T21-56-03-307Z.yml +0 -0
  84. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T21-56-35-733Z.yml +0 -0
  85. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T21-57-12-181Z.yml +0 -0
  86. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T21-57-37-748Z.yml +0 -0
  87. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T21-58-13-679Z.yml +0 -0
  88. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-06T22-37-23-895Z.yml +0 -0
  89. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T00-39-18-637Z.yml +0 -0
  90. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T01-41-46-243Z.yml +0 -0
  91. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T04-31-48-472Z.yml +0 -0
  92. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T12-14-15-632Z.yml +0 -0
  93. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T12-14-47-582Z.yml +0 -0
  94. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T12-16-23-471Z.yml +0 -0
  95. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T12-17-10-247Z.yml +0 -0
  96. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T12-18-24-707Z.yml +0 -0
  97. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T12-20-06-164Z.yml +0 -0
  98. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T12-20-28-342Z.yml +0 -0
  99. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T12-21-54-962Z.yml +0 -0
  100. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T12-22-34-666Z.yml +0 -0
  101. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T12-23-11-336Z.yml +0 -0
  102. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T12-23-36-260Z.yml +0 -0
  103. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T12-24-09-267Z.yml +0 -0
  104. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T12-24-35-434Z.yml +0 -0
  105. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T12-25-57-010Z.yml +0 -0
  106. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T12-34-48-823Z.yml +0 -0
  107. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T12-46-46-468Z.yml +0 -0
  108. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T12-48-17-930Z.yml +0 -0
  109. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T12-49-26-400Z.yml +0 -0
  110. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T12-50-31-563Z.yml +0 -0
  111. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/page-2026-05-07T12-56-45-568Z.yml +0 -0
  112. {arrayview-0.29.0 → arrayview-0.29.1}/.playwright-cli/theme-dark.yml +0 -0
  113. {arrayview-0.29.0 → arrayview-0.29.1}/.python-version +0 -0
  114. {arrayview-0.29.0 → arrayview-0.29.1}/.vscode/settings.json +0 -0
  115. {arrayview-0.29.0 → arrayview-0.29.1}/AGENTS.md +0 -0
  116. {arrayview-0.29.0 → arrayview-0.29.1}/CONTRIBUTING.md +0 -0
  117. {arrayview-0.29.0 → arrayview-0.29.1}/DESIGN.md +0 -0
  118. {arrayview-0.29.0 → arrayview-0.29.1}/LICENSE +0 -0
  119. {arrayview-0.29.0 → arrayview-0.29.1}/README.md +0 -0
  120. {arrayview-0.29.0 → arrayview-0.29.1}/docs/comparing.md +0 -0
  121. {arrayview-0.29.0 → arrayview-0.29.1}/docs/configuration.md +0 -0
  122. {arrayview-0.29.0 → arrayview-0.29.1}/docs/display.md +0 -0
  123. {arrayview-0.29.0 → arrayview-0.29.1}/docs/index.md +0 -0
  124. {arrayview-0.29.0 → arrayview-0.29.1}/docs/launch-routing-refactor.md +0 -0
  125. {arrayview-0.29.0 → arrayview-0.29.1}/docs/lifecycle.md +0 -0
  126. {arrayview-0.29.0 → arrayview-0.29.1}/docs/loading.md +0 -0
  127. {arrayview-0.29.0 → arrayview-0.29.1}/docs/logo.png +0 -0
  128. {arrayview-0.29.0 → arrayview-0.29.1}/docs/measurement.md +0 -0
  129. {arrayview-0.29.0 → arrayview-0.29.1}/docs/remote.md +0 -0
  130. {arrayview-0.29.0 → arrayview-0.29.1}/docs/stylesheets/extra.css +0 -0
  131. {arrayview-0.29.0 → arrayview-0.29.1}/docs/viewing.md +0 -0
  132. {arrayview-0.29.0 → arrayview-0.29.1}/experiments/ws_tunnel_diag/ws_tunnel_diag.py +0 -0
  133. {arrayview-0.29.0 → arrayview-0.29.1}/matlab/arrayview.m +0 -0
  134. {arrayview-0.29.0 → arrayview-0.29.1}/mkdocs.yml +0 -0
  135. {arrayview-0.29.0 → arrayview-0.29.1}/plans/2026-04-14-immersive-animation.md +0 -0
  136. {arrayview-0.29.0 → arrayview-0.29.1}/plans/2026-05-07-unified-colormap-picker.md +0 -0
  137. {arrayview-0.29.0 → arrayview-0.29.1}/plans/arrayview_tool_menu_fix_prompt.md +0 -0
  138. {arrayview-0.29.0 → arrayview-0.29.1}/plans/arrayview_tool_menu_implementation_plan.md +0 -0
  139. {arrayview-0.29.0 → arrayview-0.29.1}/plans/egg-placement-mockup.html +0 -0
  140. {arrayview-0.29.0 → arrayview-0.29.1}/plans/refactoring_plan.md +0 -0
  141. {arrayview-0.29.0 → arrayview-0.29.1}/plans/roi-analysis-requirements.md +0 -0
  142. {arrayview-0.29.0 → arrayview-0.29.1}/plans/webview/LOG.md +0 -0
  143. {arrayview-0.29.0 → arrayview-0.29.1}/scripts/demo.py +0 -0
  144. {arrayview-0.29.0 → arrayview-0.29.1}/scripts/release.sh +0 -0
  145. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/ARCHITECTURE.md +0 -0
  146. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/__init__.py +0 -0
  147. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/__main__.py +0 -0
  148. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_analysis.py +0 -0
  149. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_app.py +0 -0
  150. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_codex_open.py +0 -0
  151. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_config.py +0 -0
  152. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_diff.py +0 -0
  153. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_icon.png +0 -0
  154. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_imaging.py +0 -0
  155. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_io.py +0 -0
  156. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_launch_plan.py +0 -0
  157. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_launcher.py +0 -0
  158. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_lifecycle.py +0 -0
  159. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_overlays.py +0 -0
  160. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_platform.py +0 -0
  161. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_render.py +0 -0
  162. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_routes_analysis.py +0 -0
  163. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_routes_export.py +0 -0
  164. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_routes_loading.py +0 -0
  165. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_routes_persistence.py +0 -0
  166. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_routes_preload.py +0 -0
  167. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_routes_query.py +0 -0
  168. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_routes_rendering.py +0 -0
  169. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_routes_segmentation.py +0 -0
  170. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_routes_state.py +0 -0
  171. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_routes_vectorfield.py +0 -0
  172. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_routes_websocket.py +0 -0
  173. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_segmentation.py +0 -0
  174. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_server.py +0 -0
  175. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_session.py +0 -0
  176. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_shell.html +0 -0
  177. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_synthetic_mri.py +0 -0
  178. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_torch.py +0 -0
  179. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_vectorfield.py +0 -0
  180. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_viewer.html +0 -0
  181. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_vscode.py +0 -0
  182. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/_vscode_browser.py +0 -0
  183. {arrayview-0.29.0 → arrayview-0.29.1}/src/arrayview/gsap.min.js +0 -0
  184. {arrayview-0.29.0 → arrayview-0.29.1}/tests/capture_v_animation.py +0 -0
  185. {arrayview-0.29.0 → arrayview-0.29.1}/tests/conftest.py +0 -0
  186. {arrayview-0.29.0 → arrayview-0.29.1}/tests/lifecycle_matrix.py +0 -0
  187. {arrayview-0.29.0 → arrayview-0.29.1}/tests/make_vectorfield_test_arrays.py +0 -0
  188. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_api.py +0 -0
  189. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_backend_shared.py +0 -0
  190. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_browser.py +0 -0
  191. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_cli.py +0 -0
  192. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_colorbar_hover_highlight.py +0 -0
  193. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_command_reachability.py +0 -0
  194. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_config.py +0 -0
  195. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_cross_mode_parametrized.py +0 -0
  196. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_interactions.py +0 -0
  197. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_io.py +0 -0
  198. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_large_arrays.py +0 -0
  199. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_launch_plan.py +0 -0
  200. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_loading_server.py +0 -0
  201. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_mode_consistency.py +0 -0
  202. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_mode_matrix.py +0 -0
  203. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_mode_roundtrip.py +0 -0
  204. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_nifti_meta.py +0 -0
  205. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_nifti_series.py +0 -0
  206. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_rgb_pixel_art.py +0 -0
  207. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_torch.py +0 -0
  208. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_view_component_integration.py +0 -0
  209. {arrayview-0.29.0 → arrayview-0.29.1}/tests/test_view_component_unit.py +0 -0
  210. {arrayview-0.29.0 → arrayview-0.29.1}/tests/ui_audit.py +0 -0
  211. {arrayview-0.29.0 → arrayview-0.29.1}/tests/v_anim_frames/.gitkeep +0 -0
  212. {arrayview-0.29.0 → arrayview-0.29.1}/tests/visual_smoke.py +0 -0
  213. {arrayview-0.29.0 → arrayview-0.29.1}/vscode-extension/AGENTS.md +0 -0
  214. {arrayview-0.29.0 → arrayview-0.29.1}/vscode-extension/LICENSE +0 -0
  215. {arrayview-0.29.0 → arrayview-0.29.1}/vscode-extension/lifecycle_helpers.js +0 -0
  216. {arrayview-0.29.0 → arrayview-0.29.1}/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.29.0
3
+ Version: 0.29.1
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,37 @@
1
+ # VS Code Window Routing Plan
2
+
3
+ ## Problem
4
+
5
+ ArrayView can open the viewer tab in the wrong VS Code window when multiple
6
+ local windows and multiple remote/tunnel windows are active. The unsafe cases
7
+ are the ones where Python cannot prove the originating window and falls back to
8
+ writing multiple signal files or a shared signal file.
9
+
10
+ ## Contract
11
+
12
+ - An exact `ARRAYVIEW_WINDOW_ID` wins when its registration exists.
13
+ - A missing or stale `ARRAYVIEW_WINDOW_ID` with multiple active registrations
14
+ fails closed with a diagnostic.
15
+ - PID ancestry and IPC hook matching are allowed only when they identify a
16
+ single window.
17
+ - tmux client fallback is allowed only when all attached clients for the current
18
+ tmux session report the same `ARRAYVIEW_WINDOW_ID`.
19
+ - File-explorer opens launched by the extension keep using the extension's own
20
+ `ARRAYVIEW_WINDOW_ID`.
21
+
22
+ ## Implementation Slice
23
+
24
+ - Tighten `src/arrayview/_vscode_signal.py` window selection.
25
+ - Add focused lifecycle tests for local ambiguity, stale exact IDs, exact-ID
26
+ ownership, and tmux multi-client ambiguity.
27
+ - Avoid changing `_app.py` or the viewer frontend.
28
+
29
+ ## Manual Verification Targets
30
+
31
+ - Local VS Code terminal, one window: opens in that window.
32
+ - Local VS Code terminal, several windows: stale/missing exact ID prints a
33
+ diagnostic instead of opening elsewhere.
34
+ - Remote/tunnel terminal, several windows: exact ID opens the matching window;
35
+ stale/missing ID fails closed.
36
+ - File explorer custom editor: spawned Python receives the extension window ID
37
+ and opens back into the same window.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "arrayview"
7
- version = "0.29.0"
7
+ version = "0.29.1"
8
8
  description = "Fast multi-dimensional array viewer"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.12"
@@ -44,7 +44,7 @@ def _vscode_app_bundle() -> str | None:
44
44
 
45
45
  _VSCODE_EXT_INSTALLED = False # cached so we only check once per process
46
46
  _VSCODE_EXT_FRESH_INSTALL = False # True if we just installed it this session
47
- _VSCODE_EXT_VERSION = "0.14.33" # current bundled extension version
47
+ _VSCODE_EXT_VERSION = "0.14.34" # current bundled extension version
48
48
 
49
49
  def _bundled_vscode_vsix_version(vsix_path: str) -> str | None:
50
50
  """Return the bundled opener extension version recorded inside the VSIX."""
@@ -163,7 +163,11 @@ def _find_arrayview_window_id() -> str | None:
163
163
  pass
164
164
 
165
165
  # tmux: process tree is detached from VS Code terminal; check all session clients.
166
+ # Only accept the fallback if every attached client for this tmux session
167
+ # reports the same window ID. Returning the first client is unsafe when the
168
+ # same session is attached from multiple VS Code windows.
166
169
  if os.environ.get("TERM_PROGRAM") == "tmux":
170
+ tmux_window_ids: set[str] = set()
167
171
  try:
168
172
  r_sid = subprocess.run(
169
173
  ["tmux", "display-message", "-p", "#{session_id}"],
@@ -187,7 +191,9 @@ def _find_arrayview_window_id() -> str | None:
187
191
  with open(f"/proc/{client_pid}/environ", "rb") as fh:
188
192
  for entry in fh.read().split(b"\0"):
189
193
  if entry.startswith(b"ARRAYVIEW_WINDOW_ID="):
190
- return entry[len(b"ARRAYVIEW_WINDOW_ID="):].decode()
194
+ val = entry[len(b"ARRAYVIEW_WINDOW_ID="):].decode()
195
+ if val:
196
+ tmux_window_ids.add(val)
191
197
  except Exception:
192
198
  pass
193
199
  # macOS: ps ewwww
@@ -198,11 +204,21 @@ def _find_arrayview_window_id() -> str | None:
198
204
  )
199
205
  for token in r.stdout.split():
200
206
  if token.startswith("ARRAYVIEW_WINDOW_ID="):
201
- return token[len("ARRAYVIEW_WINDOW_ID="):]
207
+ val = token[len("ARRAYVIEW_WINDOW_ID="):]
208
+ if val:
209
+ tmux_window_ids.add(val)
202
210
  except Exception:
203
211
  pass
204
212
  except Exception:
205
213
  pass
214
+ if len(tmux_window_ids) == 1:
215
+ return next(iter(tmux_window_ids))
216
+ if len(tmux_window_ids) > 1:
217
+ _vprint(
218
+ "[ArrayView] signal: tmux clients report multiple "
219
+ f"ARRAYVIEW_WINDOW_ID values: {sorted(tmux_window_ids)}",
220
+ flush=True,
221
+ )
206
222
 
207
223
  return None
208
224
 
@@ -323,53 +339,8 @@ def _write_vscode_signal(payload: dict, delay: float = 0.0, skip_compat: bool =
323
339
  except Exception:
324
340
  uses_pid = env_wid.isdigit()
325
341
 
326
- # Guard against stale ARRAYVIEW_WINDOW_ID: when the extension
327
- # host restarts (e.g. tunnel reconnection), the old process may
328
- # still be alive but is no longer connected to a VS Code client.
329
- # Detect this by looking for a newer registration from the same
330
- # server (same first ppid — the tunnel/server parent process).
331
- # Only needed for PID-based IDs (fallbackId=True); hookTag IDs
332
- # are unique per IPC socket and never go stale across restarts.
333
- #
334
- # Skip on macOS: all extension hosts are direct children of the
335
- # same main Electron process (ppids[0] == Electron PID for every
336
- # window), so the "same ppids[0]" heuristic matches ALL concurrent
337
- # windows and incorrectly redirects signals to the wrong window.
338
- # The stable window ID feature (v0.14.0+) already handles restart
339
- # continuity on macOS via EnvironmentVariableCollection, making
340
- # this stale-redirect unnecessary there.
341
- if uses_pid and sys.platform != "darwin" and not _is_vscode_remote():
342
- _env_ts = _reg_data.get("ts", 0)
343
- _env_ppids = _reg_data.get("ppids", [])
344
- try:
345
- for _fname in os.listdir(signal_dir):
346
- if not (_fname.startswith("window-") and _fname.endswith(".json")):
347
- continue
348
- _other_wid = _fname[7:-5]
349
- if _other_wid == env_wid:
350
- continue
351
- with open(os.path.join(signal_dir, _fname)) as _f:
352
- _other = json.load(_f)
353
- _other_ts = _other.get("ts", 0)
354
- _other_ppids = _other.get("ppids", [])
355
- if (
356
- _other_ts > _env_ts
357
- and len(_env_ppids) >= 1
358
- and len(_other_ppids) >= 1
359
- and _env_ppids[0] == _other_ppids[0]
360
- ):
361
- _vprint(
362
- f"[ArrayView] signal: ARRAYVIEW_WINDOW_ID={env_wid} is stale "
363
- f"(newer registration {_other_wid} found), redirecting",
364
- flush=True,
365
- )
366
- env_wid = _other_wid
367
- _reg_data = _other
368
- uses_pid = _other.get("fallbackId", False)
369
- _env_ts = _other_ts
370
- except Exception:
371
- pass
372
-
342
+ # Trust an exact window ID. Redirecting to a newer same-parent
343
+ # registration can silently open the tab in a live sibling window.
373
344
  _prefix = "pid" if uses_pid else "ipc"
374
345
  filenames = (f"open-request-{_prefix}-{env_wid}.json",)
375
346
  targeted_via_env = True
@@ -410,33 +381,15 @@ def _write_vscode_signal(payload: dict, delay: float = 0.0, skip_compat: bool =
410
381
  flush=True,
411
382
  )
412
383
  elif len(_all_windows) > 1:
413
- if _is_vscode_remote():
414
- print(
415
- "[ArrayView] VS Code tunnel window is ambiguous: "
416
- f"ARRAYVIEW_WINDOW_ID={env_wid!r} is not registered "
417
- f"and {len(_all_windows)} VS Code windows are active. "
418
- "Open a fresh terminal in the target VS Code window "
419
- "and run ArrayView again.",
420
- flush=True,
421
- )
422
- return False
423
- else:
424
- _wfiles = []
425
- for _fn in _all_windows:
426
- _wid = _fn[7:-5]
427
- _wfiles.append(
428
- f"open-request-pid-{_wid}.json"
429
- if _wid.isdigit()
430
- else f"open-request-ipc-{_wid}.json"
431
- )
432
- data["broadcast"] = True
433
- filenames = tuple(_wfiles)
434
- targeted_via_env = True
435
- _vprint(
436
- f"[ArrayView] signal: {len(_all_windows)} windows registered, "
437
- f"broadcasting with focus guard",
438
- flush=True,
439
- )
384
+ print(
385
+ "[ArrayView] VS Code window is ambiguous: "
386
+ f"ARRAYVIEW_WINDOW_ID={env_wid!r} is not registered "
387
+ f"and {len(_all_windows)} VS Code windows are active. "
388
+ "Open a fresh terminal in the target VS Code window "
389
+ "and run ArrayView again.",
390
+ flush=True,
391
+ )
392
+ return False
440
393
  # else: 0 windows — fall through to subsequent targeting
441
394
 
442
395
  if targeted_via_env:
@@ -491,10 +444,16 @@ def _write_vscode_signal(payload: dict, delay: float = 0.0, skip_compat: bool =
491
444
  except Exception:
492
445
  pass
493
446
  if len(_wfiles) > 1:
494
- data["broadcast"] = True
495
- filenames = (
496
- tuple(_wfiles) if _wfiles else (_VSCODE_SIGNAL_FILENAME,)
497
- )
447
+ print(
448
+ "[ArrayView] VS Code window is ambiguous: "
449
+ "no exact ARRAYVIEW_WINDOW_ID or PID match is available "
450
+ f"and {len(_wfiles)} VS Code windows are active. "
451
+ "Open a fresh terminal in the target VS Code window "
452
+ "and run ArrayView again.",
453
+ flush=True,
454
+ )
455
+ return False
456
+ filenames = tuple(_wfiles) if _wfiles else (_VSCODE_SIGNAL_FILENAME,)
498
457
  else:
499
458
  filenames = (
500
459
  _VSCODE_SIGNAL_FILENAME,
@@ -535,12 +494,17 @@ def _write_vscode_signal(payload: dict, delay: float = 0.0, skip_compat: bool =
535
494
  except Exception:
536
495
  pass
537
496
  if len(window_files) > 1:
538
- # Mark as broadcast so extensions check focus before opening
539
- data["broadcast"] = True
497
+ print(
498
+ "[ArrayView] VS Code window is ambiguous: "
499
+ "no exact ARRAYVIEW_WINDOW_ID or PID match is available "
500
+ f"and {len(window_files)} VS Code windows are active. "
501
+ "Open a fresh terminal in the target VS Code window "
502
+ "and run ArrayView again.",
503
+ flush=True,
504
+ )
505
+ return False
540
506
  filenames = (
541
- tuple(window_files)
542
- if window_files
543
- else (_VSCODE_SIGNAL_FILENAME,)
507
+ tuple(window_files) if window_files else (_VSCODE_SIGNAL_FILENAME,)
544
508
  )
545
509
  else:
546
510
  # Not in VS Code terminal: use shared file
@@ -800,4 +764,3 @@ def _cleanup_zombie_registrations(verbose: bool = False) -> int:
800
764
  pass
801
765
 
802
766
  return removed
803
-
@@ -186,6 +186,123 @@ def test_vscode_tunnel_exact_window_id_is_not_redirected_to_newer_sibling(
186
186
  assert not (signal_dir / "open-request-pid-200.json").exists()
187
187
 
188
188
 
189
+ def test_vscode_local_exact_window_id_is_not_redirected_to_newer_sibling(
190
+ monkeypatch, tmp_path
191
+ ):
192
+ import json
193
+ import arrayview._vscode_signal as signal
194
+
195
+ home = tmp_path / "home"
196
+ signal_dir = home / ".arrayview"
197
+ signal_dir.mkdir(parents=True)
198
+ monkeypatch.setenv("HOME", str(home))
199
+ monkeypatch.setattr(signal, "_is_vscode_remote", lambda: False)
200
+ monkeypatch.setattr(signal, "_find_arrayview_window_id", lambda: "100")
201
+
202
+ (signal_dir / "window-100.json").write_text(
203
+ json.dumps({"pid": 100, "ppids": [10], "fallbackId": True, "ts": 1})
204
+ )
205
+ (signal_dir / "window-200.json").write_text(
206
+ json.dumps({"pid": 200, "ppids": [10], "fallbackId": True, "ts": 2})
207
+ )
208
+
209
+ opened = signal._write_vscode_signal(
210
+ {"url": "http://localhost:8000/?sid=abc"},
211
+ skip_compat=True,
212
+ )
213
+
214
+ assert opened is True
215
+ assert (signal_dir / "open-request-pid-100.json").exists()
216
+ assert not (signal_dir / "open-request-pid-200.json").exists()
217
+
218
+
219
+ def test_vscode_local_stale_window_id_with_multiple_windows_fails_closed(
220
+ monkeypatch, tmp_path, capsys
221
+ ):
222
+ import json
223
+ import arrayview._vscode_signal as signal
224
+
225
+ home = tmp_path / "home"
226
+ signal_dir = home / ".arrayview"
227
+ signal_dir.mkdir(parents=True)
228
+ monkeypatch.setenv("HOME", str(home))
229
+ monkeypatch.setattr(signal, "_is_vscode_remote", lambda: False)
230
+ monkeypatch.setattr(signal, "_find_arrayview_window_id", lambda: "stale")
231
+ monkeypatch.setattr(signal, "_find_current_vscode_window_id", lambda: None)
232
+
233
+ for wid in ("100", "200"):
234
+ (signal_dir / f"window-{wid}.json").write_text(
235
+ json.dumps({"pid": int(wid), "ppids": [10], "fallbackId": True})
236
+ )
237
+
238
+ opened = signal._write_vscode_signal(
239
+ {"url": "http://localhost:8000/?sid=abc"},
240
+ skip_compat=True,
241
+ )
242
+
243
+ assert opened is False
244
+ assert "VS Code window is ambiguous" in capsys.readouterr().out
245
+ assert not list(signal_dir.glob("open-request-*"))
246
+
247
+
248
+ def test_vscode_local_missing_window_match_with_multiple_windows_fails_closed(
249
+ monkeypatch, tmp_path, capsys
250
+ ):
251
+ import json
252
+ import arrayview._vscode_signal as signal
253
+
254
+ home = tmp_path / "home"
255
+ signal_dir = home / ".arrayview"
256
+ signal_dir.mkdir(parents=True)
257
+ monkeypatch.setenv("HOME", str(home))
258
+ monkeypatch.setattr(signal, "_is_vscode_remote", lambda: False)
259
+ monkeypatch.setattr(signal, "_find_arrayview_window_id", lambda: None)
260
+ monkeypatch.setattr(signal, "_find_current_vscode_window_id", lambda: None)
261
+ monkeypatch.setattr("arrayview._platform._find_vscode_ipc_hook", lambda: None)
262
+ monkeypatch.setattr("arrayview._platform._in_vscode_terminal", lambda: True)
263
+
264
+ for wid in ("100", "200"):
265
+ (signal_dir / f"window-{wid}.json").write_text(
266
+ json.dumps({"pid": int(wid), "ppids": [10], "fallbackId": True})
267
+ )
268
+
269
+ opened = signal._write_vscode_signal(
270
+ {"url": "http://localhost:8000/?sid=abc"},
271
+ skip_compat=True,
272
+ )
273
+
274
+ assert opened is False
275
+ assert "VS Code window is ambiguous" in capsys.readouterr().out
276
+ assert not list(signal_dir.glob("open-request-*"))
277
+
278
+
279
+ def test_tmux_multiple_window_ids_are_ambiguous(monkeypatch):
280
+ import arrayview._vscode_signal as signal
281
+
282
+ monkeypatch.delenv("ARRAYVIEW_WINDOW_ID", raising=False)
283
+ monkeypatch.setenv("TERM_PROGRAM", "tmux")
284
+ monkeypatch.setattr(signal, "get_ppid", lambda _pid: -1)
285
+
286
+ class _Result:
287
+ def __init__(self, stdout):
288
+ self.stdout = stdout
289
+
290
+ def _run(cmd, *args, **kwargs):
291
+ if cmd[:3] == ["tmux", "display-message", "-p"]:
292
+ return _Result("$1\n")
293
+ if cmd[:3] == ["tmux", "list-clients", "-t"]:
294
+ return _Result("111\n222\n")
295
+ if cmd[:3] == ["ps", "ewwww", "-p"]:
296
+ pid = cmd[-1]
297
+ wid = "win-a" if pid == "111" else "win-b"
298
+ return _Result(f"COMMAND ARRAYVIEW_WINDOW_ID={wid}\n")
299
+ return _Result("")
300
+
301
+ monkeypatch.setattr(signal.subprocess, "run", _run)
302
+
303
+ assert signal._find_arrayview_window_id() is None
304
+
305
+
189
306
  def test_transient_waiter_notices_quick_viewer_connect_close(monkeypatch):
190
307
  import arrayview._launcher as launcher
191
308
  import arrayview._session as session_mod
@@ -198,7 +198,7 @@ wheels = [
198
198
 
199
199
  [[package]]
200
200
  name = "arrayview"
201
- version = "0.29.0"
201
+ version = "0.29.1"
202
202
  source = { editable = "." }
203
203
  dependencies = [
204
204
  { name = "fastapi" },
@@ -702,6 +702,7 @@ async function openInWebviewPanel(url, title, floating = false) {
702
702
  panel.webview.html = _viewerPanelHtml(url);
703
703
 
704
704
  _openPanels.set(url, panel);
705
+ const pingUrl = pingUrlFromViewerUrl(url);
705
706
  let panelDisposed = false;
706
707
  panel.onDidDispose(() => {
707
708
  panelDisposed = true;
@@ -2,7 +2,7 @@
2
2
  "name": "arrayview-opener",
3
3
  "displayName": "ArrayView Opener",
4
4
  "description": "Opens ArrayView in VS Code and can promote tunnel ports to public preview",
5
- "version": "0.14.33",
5
+ "version": "0.14.34",
6
6
  "publisher": "arrayview",
7
7
  "license": "MIT",
8
8
  "icon": "icon.png",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes