arrayview 0.14.0__tar.gz → 0.15.0__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 (111) hide show
  1. {arrayview-0.14.0 → arrayview-0.15.0}/.claude/skills/ui-consistency-audit/SKILL.md +2 -2
  2. {arrayview-0.14.0 → arrayview-0.15.0}/.claude/skills/viewer-ui-checklist/SKILL.md +3 -3
  3. arrayview-0.15.0/.github/copilot-instructions.md +39 -0
  4. arrayview-0.15.0/.ignore +1 -0
  5. arrayview-0.15.0/.mex/AGENTS.md +9 -0
  6. arrayview-0.15.0/.mex/ROUTER.md +86 -0
  7. {arrayview-0.14.0 → arrayview-0.15.0}/.mex/SETUP.md +1 -1
  8. arrayview-0.15.0/.mex/context/architecture.md +90 -0
  9. {arrayview-0.14.0 → arrayview-0.15.0}/.mex/context/conventions.md +12 -5
  10. arrayview-0.15.0/.mex/context/decisions.md +70 -0
  11. arrayview-0.15.0/.mex/context/frontend.md +140 -0
  12. arrayview-0.15.0/.mex/context/project-state.md +34 -0
  13. arrayview-0.15.0/.mex/context/render-pipeline.md +110 -0
  14. arrayview-0.15.0/.mex/context/setup.md +66 -0
  15. arrayview-0.15.0/.mex/context/stack.md +59 -0
  16. arrayview-0.15.0/.mex/patterns/INDEX.md +29 -0
  17. arrayview-0.15.0/.mex/patterns/add-file-format.md +96 -0
  18. arrayview-0.15.0/.mex/patterns/add-server-endpoint.md +86 -0
  19. {arrayview-0.14.0 → arrayview-0.15.0}/.mex/patterns/debug-render.md +7 -3
  20. {arrayview-0.14.0 → arrayview-0.15.0}/.mex/patterns/frontend-change.md +28 -18
  21. arrayview-0.15.0/.opencode/opencode.json +4 -0
  22. arrayview-0.15.0/.vscode/settings.json +208 -0
  23. arrayview-0.15.0/AGENTS.md +50 -0
  24. arrayview-0.15.0/IMMERSIVE_ANIMATION.md +43 -0
  25. {arrayview-0.14.0 → arrayview-0.15.0}/PKG-INFO +1 -1
  26. {arrayview-0.14.0 → arrayview-0.15.0}/docs/loading.md +5 -0
  27. arrayview-0.15.0/plans/2026-04-14-immersive-animation.md +388 -0
  28. {arrayview-0.14.0 → arrayview-0.15.0}/pyproject.toml +1 -1
  29. {arrayview-0.14.0 → arrayview-0.15.0}/src/arrayview/_app.py +2 -1
  30. {arrayview-0.14.0 → arrayview-0.15.0}/src/arrayview/_io.py +8 -0
  31. {arrayview-0.14.0 → arrayview-0.15.0}/src/arrayview/_launcher.py +272 -150
  32. {arrayview-0.14.0 → arrayview-0.15.0}/src/arrayview/_render.py +55 -40
  33. {arrayview-0.14.0 → arrayview-0.15.0}/src/arrayview/_server.py +54 -13
  34. {arrayview-0.14.0 → arrayview-0.15.0}/src/arrayview/_session.py +104 -15
  35. {arrayview-0.14.0 → arrayview-0.15.0}/src/arrayview/_shell.html +80 -27
  36. {arrayview-0.14.0 → arrayview-0.15.0}/src/arrayview/_stdio_server.py +51 -13
  37. {arrayview-0.14.0 → arrayview-0.15.0}/src/arrayview/_viewer.html +806 -668
  38. {arrayview-0.14.0 → arrayview-0.15.0}/src/arrayview/_vscode.py +17 -8
  39. arrayview-0.15.0/src/arrayview/arrayview-opener.vsix +0 -0
  40. {arrayview-0.14.0 → arrayview-0.15.0}/tests/test_api.py +86 -0
  41. {arrayview-0.14.0 → arrayview-0.15.0}/tests/test_browser.py +21 -2
  42. arrayview-0.15.0/tests/test_loading_server.py +107 -0
  43. {arrayview-0.14.0 → arrayview-0.15.0}/tests/visual_smoke.py +34 -30
  44. {arrayview-0.14.0 → arrayview-0.15.0}/uv.lock +1 -1
  45. arrayview-0.15.0/vscode-extension/AGENTS.md +26 -0
  46. {arrayview-0.14.0 → arrayview-0.15.0}/vscode-extension/extension.js +23 -4
  47. {arrayview-0.14.0 → arrayview-0.15.0}/vscode-extension/package.json +11 -1
  48. arrayview-0.14.0/.mex/AGENTS.md +0 -33
  49. arrayview-0.14.0/.mex/ROUTER.md +0 -76
  50. arrayview-0.14.0/.mex/context/architecture.md +0 -89
  51. arrayview-0.14.0/.mex/context/decisions.md +0 -71
  52. arrayview-0.14.0/.mex/context/setup.md +0 -68
  53. arrayview-0.14.0/.mex/context/stack.md +0 -60
  54. arrayview-0.14.0/.mex/patterns/INDEX.md +0 -11
  55. arrayview-0.14.0/.mex/patterns/add-file-format.md +0 -70
  56. arrayview-0.14.0/.mex/patterns/vscode-display.md +0 -113
  57. arrayview-0.14.0/AGENTS.md +0 -65
  58. arrayview-0.14.0/src/arrayview/arrayview-opener.vsix +0 -0
  59. {arrayview-0.14.0 → arrayview-0.15.0}/.claude/skills/invocation-consistency/SKILL.md +0 -0
  60. {arrayview-0.14.0 → arrayview-0.15.0}/.claude/skills/modes-consistency/SKILL.md +0 -0
  61. {arrayview-0.14.0 → arrayview-0.15.0}/.claude/skills/visual-bug-fixing/SKILL.md +0 -0
  62. {arrayview-0.14.0 → arrayview-0.15.0}/.github/workflows/docs.yml +0 -0
  63. {arrayview-0.14.0 → arrayview-0.15.0}/.github/workflows/python-publish.yml +0 -0
  64. {arrayview-0.14.0 → arrayview-0.15.0}/.gitignore +0 -0
  65. {arrayview-0.14.0 → arrayview-0.15.0}/.mex/SYNC.md +0 -0
  66. {arrayview-0.14.0 → arrayview-0.15.0}/.mex/patterns/README.md +0 -0
  67. {arrayview-0.14.0 → arrayview-0.15.0}/.python-version +0 -0
  68. {arrayview-0.14.0 → arrayview-0.15.0}/CONTRIBUTING.md +0 -0
  69. {arrayview-0.14.0 → arrayview-0.15.0}/LICENSE +0 -0
  70. {arrayview-0.14.0 → arrayview-0.15.0}/README.md +0 -0
  71. {arrayview-0.14.0 → arrayview-0.15.0}/docs/comparing.md +0 -0
  72. {arrayview-0.14.0 → arrayview-0.15.0}/docs/configuration.md +0 -0
  73. {arrayview-0.14.0 → arrayview-0.15.0}/docs/display.md +0 -0
  74. {arrayview-0.14.0 → arrayview-0.15.0}/docs/index.md +0 -0
  75. {arrayview-0.14.0 → arrayview-0.15.0}/docs/logo.png +0 -0
  76. {arrayview-0.14.0 → arrayview-0.15.0}/docs/measurement.md +0 -0
  77. {arrayview-0.14.0 → arrayview-0.15.0}/docs/remote.md +0 -0
  78. {arrayview-0.14.0 → arrayview-0.15.0}/docs/stylesheets/extra.css +0 -0
  79. {arrayview-0.14.0 → arrayview-0.15.0}/docs/viewing.md +0 -0
  80. {arrayview-0.14.0 → arrayview-0.15.0}/matlab/arrayview.m +0 -0
  81. {arrayview-0.14.0 → arrayview-0.15.0}/mkdocs.yml +0 -0
  82. {arrayview-0.14.0 → arrayview-0.15.0}/plans/webview/LOG.md +0 -0
  83. {arrayview-0.14.0 → arrayview-0.15.0}/scripts/demo.py +0 -0
  84. {arrayview-0.14.0 → arrayview-0.15.0}/scripts/release.sh +0 -0
  85. {arrayview-0.14.0 → arrayview-0.15.0}/src/arrayview/ARCHITECTURE.md +0 -0
  86. {arrayview-0.14.0 → arrayview-0.15.0}/src/arrayview/__init__.py +0 -0
  87. {arrayview-0.14.0 → arrayview-0.15.0}/src/arrayview/__main__.py +0 -0
  88. {arrayview-0.14.0 → arrayview-0.15.0}/src/arrayview/_config.py +0 -0
  89. {arrayview-0.14.0 → arrayview-0.15.0}/src/arrayview/_icon.png +0 -0
  90. {arrayview-0.14.0 → arrayview-0.15.0}/src/arrayview/_platform.py +0 -0
  91. {arrayview-0.14.0 → arrayview-0.15.0}/src/arrayview/_segmentation.py +0 -0
  92. {arrayview-0.14.0 → arrayview-0.15.0}/src/arrayview/_torch.py +0 -0
  93. {arrayview-0.14.0 → arrayview-0.15.0}/src/arrayview/gsap.min.js +0 -0
  94. {arrayview-0.14.0 → arrayview-0.15.0}/tests/conftest.py +0 -0
  95. {arrayview-0.14.0 → arrayview-0.15.0}/tests/make_vectorfield_test_arrays.py +0 -0
  96. {arrayview-0.14.0 → arrayview-0.15.0}/tests/test_cli.py +0 -0
  97. {arrayview-0.14.0 → arrayview-0.15.0}/tests/test_command_reachability.py +0 -0
  98. {arrayview-0.14.0 → arrayview-0.15.0}/tests/test_config.py +0 -0
  99. {arrayview-0.14.0 → arrayview-0.15.0}/tests/test_cross_mode_parametrized.py +0 -0
  100. {arrayview-0.14.0 → arrayview-0.15.0}/tests/test_interactions.py +0 -0
  101. {arrayview-0.14.0 → arrayview-0.15.0}/tests/test_large_arrays.py +0 -0
  102. {arrayview-0.14.0 → arrayview-0.15.0}/tests/test_mode_consistency.py +0 -0
  103. {arrayview-0.14.0 → arrayview-0.15.0}/tests/test_mode_matrix.py +0 -0
  104. {arrayview-0.14.0 → arrayview-0.15.0}/tests/test_mode_roundtrip.py +0 -0
  105. {arrayview-0.14.0 → arrayview-0.15.0}/tests/test_nifti_meta.py +0 -0
  106. {arrayview-0.14.0 → arrayview-0.15.0}/tests/test_rgb_pixel_art.py +0 -0
  107. {arrayview-0.14.0 → arrayview-0.15.0}/tests/test_torch.py +0 -0
  108. {arrayview-0.14.0 → arrayview-0.15.0}/tests/test_view_component_integration.py +0 -0
  109. {arrayview-0.14.0 → arrayview-0.15.0}/tests/test_view_component_unit.py +0 -0
  110. {arrayview-0.14.0 → arrayview-0.15.0}/tests/ui_audit.py +0 -0
  111. {arrayview-0.14.0 → arrayview-0.15.0}/vscode-extension/LICENSE +0 -0
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: ui-consistency-audit
3
- description: Use when implementing or modifying any visual feature, layout, keyboard shortcut, or mode-specific behavior in arrayview. Proactively identifies all affected mode combinations before coding, prescribes per-mode behavior, checks for UI clashes, and runs a Playwright-based visual audit after implementation. Absorbs and replaces the modes-consistency skill.
3
+ description: Use when the user explicitly requests a full visual audit, when validating UI work for a release, or when diagnosing a cross-mode visual regression in arrayview. Proactively identifies all affected mode combinations, prescribes per-mode behavior, checks for UI clashes, and runs a Playwright-based visual audit after implementation. Absorbs and replaces the modes-consistency skill.
4
4
  ---
5
5
 
6
6
  # ArrayView UI Consistency Audit
7
7
 
8
8
  ## Rule
9
9
 
10
- Every visual change must be verified across **all applicable mode × array-count combinations** before it ships. This skill has two phases: a **proactive phase** (before coding) that plans the cross-mode behavior, and a **reactive phase** (after coding) that runs an automated visual audit.
10
+ This is the **full** visual audit path. Do not invoke it by default for every UI change. During normal feature development, do targeted verification for the specific area touched. Use this skill when the user explicitly asks for a broad visual check, when a regression spans modes/layouts, or when validating UI work for a release. This skill has two phases: a **proactive phase** (before coding) that plans the cross-mode behavior, and a **reactive phase** (after coding) that runs an automated visual audit.
11
11
 
12
12
  ---
13
13
 
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: viewer-ui-checklist
3
- description: Use when adding keyboard shortcuts, changing layout, or making any UI change to arrayview. Ensures visual_smoke.py, help overlay, and docs/ stay in sync.
3
+ description: Use when the user explicitly asks to sync UI docs/help/test coverage, or when preparing a UI change for release. Ensures visual_smoke.py, help overlay, and docs/ stay in sync.
4
4
  ---
5
5
 
6
6
  # ArrayView UI Checklist
7
7
 
8
8
  ## Rule
9
9
 
10
- Every UI change to arrayview MUST be reflected in `tests/visual_smoke.py`, the help overlay, and the relevant `docs/*.md` page before the task is complete.
10
+ This is the release/explicit-sync checklist for UI work. Do not invoke it by default for every UI edit during development. Use it when the user explicitly asks for the full sync, or when preparing a UI change for release.
11
11
 
12
12
  `README.md` is intentionally minimal and stable — do **not** add per-feature shortcuts or behavior to it. User-facing docs live in `docs/` and are organized by topic (`display.md`, `viewing.md`, `comparing.md`, `loading.md`, `measurement.md`, `remote.md`, `configuration.md`).
13
13
 
@@ -19,7 +19,7 @@ Every UI change to arrayview MUST be reflected in `tests/visual_smoke.py`, the h
19
19
  - Layout changes (canvas sizing, colorbar position, overlays)
20
20
  - New overlay, dialog, or panel
21
21
 
22
- ## Steps (mandatory, in order)
22
+ ## Steps (when this checklist is invoked)
23
23
 
24
24
  1. **Update coverage table** at the top of `visual_smoke.py`
25
25
  - If shortcut is now testable: change `✗` to `✓ NN` with scenario number
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: agents
3
+ description: Always-loaded project anchor. Read this first. Contains project identity, non-negotiables, commands, and pointer to ROUTER.md for full context.
4
+ last_updated: [YYYY-MM-DD]
5
+ ---
6
+
7
+ # [Project Name]
8
+
9
+ ## What This Is
10
+ <!-- One sentence. What does this project do?
11
+ Length: 1 sentence maximum.
12
+ Not a tagline — a factual description of what the software does.
13
+ Example: "A REST API for managing inventory across multiple warehouse locations." -->
14
+
15
+ ## Non-Negotiables
16
+ <!-- Hard rules the agent must never violate. Not preferences — rules.
17
+ These are the things that, if broken, cause real damage to the codebase.
18
+ Length: 3-7 items maximum. More than 7 means the list has not been prioritised.
19
+ Example:
20
+ - Never write database queries outside of the repository layer
21
+ - Never commit secrets or API keys
22
+ - Always handle errors explicitly — no silent failures -->
23
+
24
+ ## Commands
25
+ <!-- The exact commands needed to work on this project.
26
+ Include: run dev server, run tests, run linter, build.
27
+ Use the actual commands from this codebase — not placeholders.
28
+ Example:
29
+ - Dev: `npm run dev`
30
+ - Test: `npm test`
31
+ - Lint: `npm run lint`
32
+ - Build: `npm run build` -->
33
+
34
+ ## After Every Task
35
+ After completing any task: update `.mex/ROUTER.md` project state and any `.mex/` files that are now out of date. If no pattern existed for the task you just completed, create one in `.mex/patterns/`.
36
+
37
+ ## Navigation
38
+ At the start of every session, read `.mex/ROUTER.md` before doing anything else.
39
+ For full project context, patterns, and task guidance — everything is there.
@@ -0,0 +1 @@
1
+ .worktrees/
@@ -0,0 +1,9 @@
1
+ ---
2
+ name: agents
3
+ description: Lightweight pointer to ROUTER.md for task routing.
4
+ last_updated: 2026-04-14
5
+ ---
6
+
7
+ # arrayview
8
+
9
+ Use `.mex/ROUTER.md` only when you need project state, routing, or a matching task pattern.
@@ -0,0 +1,86 @@
1
+ ---
2
+ name: router
3
+ description: Navigation hub for task routing, project state, and behavioral guidance. Start here, then load the minimum extra context needed for the task.
4
+ edges:
5
+ - target: context/project-state.md
6
+ condition: when the task depends on what is currently shipped, in progress, or recently changed
7
+ - target: context/architecture.md
8
+ condition: when working on system design, integrations, or understanding how components connect
9
+ - target: context/stack.md
10
+ condition: when working with specific technologies, libraries, or making tech decisions
11
+ - target: context/conventions.md
12
+ condition: when writing new code, reviewing code, or unsure about project patterns
13
+ - target: context/decisions.md
14
+ condition: when making architectural choices or understanding why something is built a certain way
15
+ - target: context/setup.md
16
+ condition: when setting up the dev environment or running the project for the first time
17
+ - target: context/frontend.md
18
+ condition: when working on _viewer.html — modes, reconcilers, command registry, View Component System
19
+ - target: context/render-pipeline.md
20
+ condition: when working on rendering, colormaps, LUTs, caching, or the render thread
21
+ - target: patterns/INDEX.md
22
+ condition: when starting a task — check the pattern index for a matching pattern file
23
+ last_updated: 2026-04-15
24
+ ---
25
+
26
+ # arrayview — Router
27
+
28
+ ## What This Is
29
+ Python package for interactively viewing multi-dimensional arrays (numpy, NIfTI, zarr, etc.) with a FastAPI backend, single-file HTML/JS frontend, and multi-environment display routing (Jupyter, VS Code, SSH, native window).
30
+
31
+ ## Non-Negotiables
32
+ - Never split `_viewer.html` — entire frontend is one self-contained file, no build step
33
+ - All heavy imports (numpy, matplotlib, nibabel, FastAPI, uvicorn) must be lazy — CLI fast path stays near-zero cost
34
+ - New rendering features must be consistent across all six invocation environments
35
+ - Global state lives in `_session.py` only — `SESSIONS`, `SERVER_LOOP`, `VIEWER_SOCKETS` never redefined elsewhere
36
+ - Render thread must remain a raw `threading.Thread` + `SimpleQueue`, not `concurrent.futures`
37
+
38
+ ## Commands
39
+ - Test: `uv run pytest tests/`
40
+ - Visual smoke: `uv run pytest tests/visual_smoke.py`
41
+ - CLI: `uvx arrayview <file>`
42
+ - Build: `uv build`
43
+
44
+ ## Context Budget
45
+
46
+ 1. Start with this file plus **at most one pattern and one context file**.
47
+ 2. Treat frontmatter `edges` as **optional suggestions**, not a preload list.
48
+ 3. Follow **one extra edge only when blocked**. Do not recursively fan out through second-hop edges.
49
+ 4. Load `context/project-state.md` only when the task depends on current shipped or in-progress work.
50
+ 5. For UI, animation, or behavior bugs: ask plain-English clarification questions **before** reading git history, large diffs, or broad source sweeps.
51
+
52
+ ## Current Project State
53
+
54
+ Load `context/project-state.md` only when you need active-workstream or recent-shipping detail.
55
+
56
+ ## Routing Table
57
+
58
+ | Task type | Load |
59
+ |-----------|------|
60
+ | Checking current shipped / in-progress status | `context/project-state.md` |
61
+ | Understanding system architecture | `context/architecture.md` |
62
+ | Working with a specific technology | `context/stack.md` |
63
+ | Writing or reviewing code | `context/conventions.md` |
64
+ | Making a design decision | `context/decisions.md` |
65
+ | Setting up or running the project | `context/setup.md` |
66
+ | Editing `_viewer.html` (frontend) | `context/frontend.md` + `patterns/frontend-change.md` |
67
+ | Render pipeline, colormaps, caching | `context/render-pipeline.md` |
68
+ | Adding a new file format | `patterns/add-file-format.md` |
69
+ | Adding a server route / WebSocket endpoint | `patterns/add-server-endpoint.md` |
70
+ | Visual bugs / render artifacts | `patterns/debug-render.md` |
71
+ | Any specific task | Check `patterns/INDEX.md` for a matching pattern |
72
+
73
+ ## Behavioural Contract
74
+
75
+ 1. **CONTEXT** — Load from the routing table. Start small: this file + one pattern + one context file is the default cap. Do not preload unrelated docs.
76
+ 2. **CLARIFY** — If the task is about UI behavior, animation, or UX expectations, ask plain-English questions before deep investigation.
77
+ 3. **BUILD** — Do the work. If deviating from an established pattern, say so before writing code.
78
+ 4. **VERIFY** — If a pattern file was loaded, use its Verify section. Otherwise use the shared Verify Checklist in `context/conventions.md`.
79
+ 5. **DEBUG** — If verification fails, check `patterns/INDEX.md` for one debug pattern, fix, then re-run VERIFY.
80
+ 6. **GROW** — After completing the task: create/update patterns, update stale context files, and update `context/project-state.md` if significant.
81
+
82
+ ## After Completing a Task
83
+
84
+ - [ ] Update `context/project-state.md` if shipped or in-progress status changed
85
+ - [ ] Update any stale `.mex/context/` or `.mex/patterns/` files touched by the task
86
+ - [ ] If this revealed a repeatable workflow, add or update a pattern in `.mex/patterns/`
@@ -224,5 +224,5 @@ A well-populated scaffold should give the agent enough to:
224
224
  Once the scaffold is populated, use these to keep it aligned with your codebase:
225
225
 
226
226
  - **`mex check`** — detect drift (zero tokens, zero AI)
227
- - **`.mex/sync.sh`** — interactive drift check + targeted or full resync
227
+ - **`.mex/SYNC.md`** — interactive drift check + targeted or full resync
228
228
  - **`mex watch`** — auto drift score after every commit
@@ -0,0 +1,90 @@
1
+ ---
2
+ name: architecture
3
+ description: How the major pieces of arrayview connect and flow. Load when working on system design, integrations, or understanding how components interact.
4
+ triggers:
5
+ - "architecture"
6
+ - "system design"
7
+ - "how does X connect to Y"
8
+ - "integration"
9
+ - "flow"
10
+ - "data flow"
11
+ - "display routing"
12
+ edges:
13
+ - target: context/stack.md
14
+ condition: when specific technology details are needed
15
+ - target: context/decisions.md
16
+ condition: when understanding why the architecture is structured this way
17
+ - target: context/frontend.md
18
+ condition: when the task involves _viewer.html, modes, reconcilers, or the View Component System
19
+ - target: context/render-pipeline.md
20
+ condition: when the task involves slice extraction, colormaps, caching, or the render thread
21
+ last_updated: 2026-04-15
22
+ ---
23
+
24
+ # Architecture
25
+
26
+ ## System Overview
27
+
28
+ ```
29
+ CLI / Python API (view() or uvx arrayview <file>)
30
+ └─ _launcher.py → FastAPI server (_server.py, uvicorn) [network mode]
31
+ → _stdio_server.py [VS Code direct webview]
32
+
33
+ Server (either mode)
34
+ ├─ _session.py Session objects, global state, render thread, caches
35
+ ├─ _render.py extract_slice → apply_complex_mode → render_rgba → PNG pipeline
36
+ └─ _io.py load_data() — npy/npz/nii/zarr/h5/mat/tif/pt routing
37
+
38
+ Browser (_viewer.html — single self-contained HTML+JS+CSS file)
39
+ ├─ WebSocket /ws/{sid} binary PNG slices from server
40
+ ├─ GET /meta/{sid} session metadata on first load
41
+ └─ Canvas + UI colorbars, eggs, dynamic islands, mode transitions
42
+ ```
43
+
44
+ A `view()` call creates a `Session`, starts the FastAPI server if not running,
45
+ registers the session via HTTP POST `/load`, then opens a display for the detected
46
+ environment (Jupyter inline, VS Code SimpleBrowser or direct webview, native
47
+ pywebview, or system browser).
48
+
49
+ ## Key Components
50
+
51
+ - **`_launcher.py`** — CLI parser, `view()` API, `ViewHandle`, server lifecycle, SSH relay, file watching. Heavy imports (`_session`, `_render`, `_io`, uvicorn) are all lazy to keep the CLI fast path near-zero cost.
52
+ - **`_server.py`** — FastAPI app with all REST and WebSocket routes (`/meta/{sid}`, `/load`, `/slice`, `/ws/{sid}`, `/seg/*`, `/reload`, etc.). Dispatches render work to the render thread via `_render()` from `_session.py`.
53
+ - **`_session.py`** — Single source of global mutable state: `SESSIONS`, `SERVER_LOOP`, `VIEWER_SOCKETS`, `VIEWER_SIDS`, `SHELL_SOCKETS`. Owns the render thread (`_RENDER_QUEUE`, `_RENDER_THREAD`), prefetch pool, and the `Session` class with its three LRU caches.
54
+ - **`_render.py`** — Stateless rendering functions: `extract_slice()`, `apply_complex_mode()`, `render_rgba()`, `render_rgb_rgba()`, `render_mosaic()`, `extract_projection()`. Owns colormap LUTs (`LUTS` dict, lazy-initialized by `_init_luts()`).
55
+ - **`_io.py`** — All file-format loading behind `load_data(filepath)`. Lazy nibabel import for NIfTI. Handles `.npy`, `.npz`, `.nii/.nii.gz`, `.zarr`, `.zarr.zip`, `.pt/.pth`, `.h5/.hdf5`, `.tif/.tiff`, `.mat`. Extensions registered in `_SUPPORTED_EXTS`.
56
+ - **`_platform.py`** — Environment detection: checks jupyter → vscode → julia → ssh → terminal in priority order. Results cached. Never short-circuit this order.
57
+ - **`_vscode.py`** — VS Code extension install/management, signal-file IPC, shared-memory IPC, SimpleBrowser and direct webview opening.
58
+ - **`_stdio_server.py`** — Alternative to FastAPI for VS Code tunnel (direct webview): JSON on stdin, length-prefixed binary on stdout.
59
+ - **`_viewer.html`** — The entire frontend (~15 600 lines). CSS + JS in one file, no build step. Canvas-based rendering, WebSocket binary protocol, all viewing modes, reconcilers, command registry. See `context/frontend.md`.
60
+
61
+ ## Display Routing
62
+
63
+ | Environment | Default display | Server mode |
64
+ |---|---|---|
65
+ | Jupyter | Inline iframe | network |
66
+ | VS Code local | Simple Browser | network |
67
+ | VS Code tunnel | Direct webview (stdio) | stdio |
68
+ | Julia | System browser | network |
69
+ | CLI / Python script | Native pywebview | network |
70
+ | SSH terminal | VS Code ext via TCP relay (prints URL on failure) | network |
71
+
72
+ Detection logic: `_platform.py`. Display opening: `_launcher.py` + `_vscode.py`.
73
+
74
+ ## External Dependencies
75
+
76
+ - **FastAPI + uvicorn** — async HTTP/WebSocket server, lazy-imported in `_launcher.py`. Never call uvicorn directly — use the `_uvicorn()` accessor.
77
+ - **nibabel** — NIfTI file loading. Lazy-imported in `_io.py` via `_nib()`. Only loaded for `.nii` / `.nii.gz`.
78
+ - **numpy** — Core array type throughout. The only non-lazy import in the render path.
79
+ - **matplotlib** — Colormap LUT generation only. Lazy, initialized once by `_init_luts()` in `_render.py`.
80
+ - **qmricolors** — Registers the `lipari` and `navia` colormaps. Git dependency (`oscarvanderheide/qmricolors`).
81
+ - **zarr** — Lazy chunk access for `.zarr` / `.zarr.zip`. Chunk presets via `zarr_chunk_preset()` in `_session.py`.
82
+ - **pywebview** — Native OS window. Lazy, only started when `_can_native_window()` is true.
83
+
84
+ ## What Does NOT Exist Here
85
+
86
+ - No persistent storage — sessions are in-memory only; nothing is written to disk by the server.
87
+ - No authentication or multi-user access control — server binds to localhost.
88
+ - No build step for the frontend — `_viewer.html` is a single static file served from package resources.
89
+ - No background job queue — heavy ops run in the render thread or prefetch pool, both owned by `_session.py`.
90
+ - No nnInteractive server — `_segmentation.py` is a pure HTTP client to a separately running nnInteractive process.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: conventions
3
- description: How code is written in arrayview — naming, structure, lazy imports, and frontend organization. Load when writing new code or reviewing existing code.
3
+ description: How code is written in arrayview — naming, structure, lazy imports, frontend organization, and the shared Verify Checklist.
4
4
  triggers:
5
5
  - "convention"
6
6
  - "pattern"
@@ -14,9 +14,11 @@ edges:
14
14
  condition: when a convention depends on understanding the system structure
15
15
  - target: context/stack.md
16
16
  condition: when the convention is library-specific
17
- - target: patterns/frontend-change.md
18
- condition: when writing frontend (HTML/CSS/JS) code
19
- last_updated: 2026-04-13
17
+ - target: context/frontend.md
18
+ condition: when applying frontend conventions (section separators, reconcilers, command registry)
19
+ - target: context/render-pipeline.md
20
+ condition: when applying render pipeline conventions (pipeline order, cache patterns, LUT usage)
21
+ last_updated: 2026-04-15
20
22
  ---
21
23
 
22
24
  # Conventions
@@ -77,6 +79,11 @@ Always check in this priority order. Never short-circuit.
77
79
 
78
80
  **Float rendering convention** — slices are always converted to `np.float32` before applying colormap. RGB uint8 arrays skip colormap and go directly to RGBA. Complex arrays are transformed by `apply_complex_mode()` before any colormap step.
79
81
 
82
+ ## Context Discipline
83
+
84
+ - Load this as the shared reference alongside a task pattern; do not use it to fan out into more task-specific files.
85
+ - If you need a different task workflow, go back to `ROUTER.md` or `patterns/INDEX.md` and pick one pattern deliberately.
86
+
80
87
  ## Verify Checklist
81
88
 
82
89
  Before presenting any code change:
@@ -86,4 +93,4 @@ Before presenting any code change:
86
93
  - [ ] Frontend changes are in `_viewer.html` only — no new JS/CSS files
87
94
  - [ ] New rendering functions follow the `extract_slice → apply_complex_mode → apply_colormap_rgba` pipeline order
88
95
  - [ ] Environment detection changes go in `_platform.py`, not inline in `_launcher.py` or `_vscode.py`
89
- - [ ] Cross-mode consistency: any new visual feature verified in all six invocation environments (see `invocation-consistency` skill)
96
+ - [ ] Cross-mode consistency: any new visual feature is checked in all six invocation environments when the task or verification scope requires it
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: decisions
3
+ description: Key architectural and technical decisions with reasoning. Load when making design choices or understanding why something is built a certain way.
4
+ triggers:
5
+ - "why do we"
6
+ - "why is it"
7
+ - "decision"
8
+ - "alternative"
9
+ - "we chose"
10
+ edges:
11
+ - target: context/architecture.md
12
+ condition: when a decision relates to system structure
13
+ - target: context/stack.md
14
+ condition: when a decision relates to technology choice
15
+ - target: context/frontend.md
16
+ condition: when a decision relates to the frontend architecture or viewer modes
17
+ last_updated: 2026-04-15
18
+ ---
19
+
20
+ # Decisions
21
+
22
+ ## Decision Log
23
+
24
+ ### Single self-contained _viewer.html — no build step
25
+ **Date:** pre-2024 (initial design)
26
+ **Status:** Active
27
+ **Decision:** The entire frontend (CSS + JS) lives in one `_viewer.html` file with no bundler or build step.
28
+ **Reasoning:** The package is installed as a Python wheel. Shipping a single file that works as a package resource eliminates build infrastructure (npm, webpack, etc.) and makes the project trivially installable. The file is served directly from package resources via `importlib.resources`.
29
+ **Alternatives considered:** Separate JS/CSS files with a bundler (rejected — adds npm as a required toolchain and a build step to every contribution); Jinja2 templating (rejected — adds complexity without benefit when one file is sufficient).
30
+ **Consequences:** Frontend is ~15 600 lines in one file. All edits happen in `_viewer.html` only — never create companion JS/CSS files.
31
+
32
+ ### Lazy imports everywhere in _launcher.py
33
+ **Date:** 2024 (extracted from _app.py)
34
+ **Status:** Active
35
+ **Decision:** `_launcher.py` does not import numpy, _session, _render, _io, or uvicorn at module level. All are deferred to first use via accessor functions (`_server_mod()`, `_uvicorn()`, etc.) or `_LazyMod`.
36
+ **Reasoning:** The CLI fast path — when the server is already running — only needs to send a `/load` HTTP request. Eager imports add ~300–350 ms per invocation, which is unacceptable for a tool used interactively dozens of times per session.
37
+ **Alternatives considered:** Importing everything eagerly (rejected — too slow); using `importlib.import_module` everywhere (equivalent, but the `_mod_cache = None` + accessor pattern is more readable and explicit).
38
+ **Consequences:** Any new heavy import added to `_launcher.py` must follow the accessor pattern. Violating this breaks the fast path.
39
+
40
+ ### Global state lives exclusively in _session.py
41
+ **Date:** 2024 (modular refactor from _app.py)
42
+ **Status:** Active
43
+ **Decision:** `SESSIONS`, `SERVER_LOOP`, `VIEWER_SOCKETS`, `VIEWER_SIDS`, `SHELL_SOCKETS`, `_RENDER_QUEUE`, `_RENDER_THREAD` are all defined in `_session.py` and imported by name elsewhere. No other module defines or shadows these.
44
+ **Reasoning:** Before the refactor, global state was scattered across `_app.py`. Centralizing it in one module makes concurrent access patterns explicit and prevents accidental redefinition.
45
+ **Alternatives considered:** Thread-local storage (rejected — server loop and sessions need to be shared across threads); a dedicated `State` singleton class (rejected — no benefit over module-level globals for this use case).
46
+ **Consequences:** Any new global mutable state must be added to `_session.py`. Never redefine these names in `_server.py`, `_launcher.py`, or anywhere else.
47
+
48
+ ### Render thread is threading.Thread + SimpleQueue, not concurrent.futures
49
+ **Date:** 2024
50
+ **Status:** Active
51
+ **Decision:** The CPU-bound render work runs in a raw `threading.Thread` driven by `_queue.SimpleQueue`, not a `ThreadPoolExecutor`.
52
+ **Reasoning:** During Python interpreter shutdown, `concurrent.futures` sets a `_global_shutdown` flag that prevents submitting new work. Because the server can still receive requests during shutdown, this caused render failures. Raw `threading.Thread` with `daemon=True` is unaffected by the executor lifecycle.
53
+ **Alternatives considered:** `ThreadPoolExecutor` (rejected — shutdown race); `asyncio.run_in_executor` with default executor (same issue).
54
+ **Consequences:** The prefetch pool *does* use `concurrent.futures.ThreadPoolExecutor` (it submits non-critical background work and gracefully handles `RuntimeError` on shutdown). Only the critical render path must stay on `SimpleQueue`.
55
+
56
+ ### stdio transport for VS Code tunnel (direct webview)
57
+ **Date:** 2024
58
+ **Status:** Active
59
+ **Decision:** When a VS Code tunnel is detected, `_stdio_server.py` replaces FastAPI+WebSocket. Messages are JSON on stdin, length-prefixed binary on stdout. The VS Code extension bridges `postMessage` ↔ subprocess stdio.
60
+ **Reasoning:** VS Code tunnel environments block arbitrary TCP ports. The Direct WebView API bypasses the network entirely by running the viewer as a subprocess of the extension. This gives reliable display without requiring port forwarding.
61
+ **Alternatives considered:** Port forwarding via VS Code tunnel (rejected — unreliable, depends on tunnel configuration); WebSocket over stdio tunnel (rejected — more complex than length-prefixed binary).
62
+ **Consequences:** `_stdio_server.py` must mirror every route and feature of `_server.py`. New server features must be implemented in both. The `_vscode.py` signal-file and shared-memory IPC are part of this same display path.
63
+
64
+ ### UI visibility changes go through reconcilers, not ad hoc style/classList calls
65
+ **Date:** 2025 (View Component System refactor)
66
+ **Status:** Active
67
+ **Decision:** All visibility and layout state changes in `_viewer.html` must go through the four reconciler functions (unified UI reconciler, layout container visibility, compare sub-mode state, CB/island visibility).
68
+ **Reasoning:** Before reconcilers, mode-switch functions each managed their own ad hoc `style.display` toggles. This caused regressions where fixing one mode's layout broke another. Reconcilers enforce a single source of truth for UI state.
69
+ **Alternatives considered:** Each mode function manages its own visibility (previous approach, rejected due to regression rate); CSS class toggling with no reconciler (rejected — same problem in a different form).
70
+ **Consequences:** When adding a new UI element, wire it into the appropriate reconciler. Never set `style.display` or toggle classes in mode-entry/exit functions — that work belongs in the reconcilers.
@@ -0,0 +1,140 @@
1
+ ---
2
+ name: frontend
3
+ description: _viewer.html internals — mode matrix, reconcilers, command registry, View Component System, CSS architecture. Load for any task touching _viewer.html.
4
+ triggers:
5
+ - "_viewer.html"
6
+ - "frontend"
7
+ - "mode"
8
+ - "reconciler"
9
+ - "keybind"
10
+ - "colorbar"
11
+ - "canvas"
12
+ - "dynamic island"
13
+ - "egg"
14
+ - "command registry"
15
+ - "View Component"
16
+ - "modeManager"
17
+ edges:
18
+ - target: context/architecture.md
19
+ condition: when understanding how the frontend connects to the server
20
+ - target: context/decisions.md
21
+ condition: when understanding why the frontend is a single file or why reconcilers exist
22
+ - target: context/conventions.md
23
+ condition: when writing new frontend code and need section separator conventions
24
+ - target: patterns/frontend-change.md
25
+ condition: when making a concrete change to _viewer.html
26
+ last_updated: 2026-04-15
27
+ ---
28
+
29
+ # Frontend (_viewer.html)
30
+
31
+ ~15 600 lines. Single file, no build step. All CSS and JS inline.
32
+
33
+ ## Section Organization
34
+
35
+ Section separators: `/* ── Section Name ── */` in CSS, `// ── Section Name ──` in JS.
36
+
37
+ **CSS (lines ~7–1500)**
38
+ | Section | What it covers |
39
+ |---------|----------------|
40
+ | Theme Variables and Base Layout | CSS custom properties, dark theme palette, root layout grid |
41
+ | ColorBar and Dynamic Islands | Colorbar positioning, egg badges, info bar, dimension sliders |
42
+ | Generic ColorBar class styles | Shared `.av-colorbar` styles used by the `ColorBar` JS class |
43
+ | Compare, Overlay, and Prompt Styles | Side-by-side panes, overlay blend, prompt dialogs |
44
+ | Help and Info Overlays | Help shortcut overlay, array-info panel |
45
+ | Immersive, Fullscreen, and Compact Mode | Zen mode hide rules, fullscreen layout, compact overrides |
46
+
47
+ **JavaScript (lines ~1500–14750)**
48
+ | Section | What it covers |
49
+ |---------|----------------|
50
+ | Constants and Transport Setup | WS URL construction, stdio/postMessage transport abstraction |
51
+ | Viewer State Variables | All mutable state: slice indices, zoom, mode flags |
52
+ | Mode Registry | Mode name → enter/exit function mapping |
53
+ | PanManager | Canvas panning state machine |
54
+ | Canvas Scaling and Layout | `scaleCanvas()`, `mvScaleAllCanvases()`, `compareScaleCanvases()`, `qvScaleAllCanvases()` |
55
+ | Compare Mode | Multi-pane compare infrastructure, drag-to-reorder |
56
+ | Colorbar Rendering and Histogram | Colorbar draw routines, histogram morph |
57
+ | ColorBar class | Reusable `ColorBar` class (~900 lines) |
58
+ | WebSocket and Data Transport | Binary slice receive, request queueing, reconnect |
59
+ | Initialization and Metadata Fetch | `/meta` fetch, loading screen |
60
+ | Info Bar and Pixel Display | Bottom info bar, hover pixel readout |
61
+ | State Persistence and Restore | URL hash state, `sessionStorage` save/restore |
62
+ | Rendering Pipeline | `updateView()`, play/animate, screenshot |
63
+ | ROI and Selection Modes | Rectangle/ellipse ROI, statistics computation |
64
+ | nnInteractive Segmentation | Click-to-segment UI, mask overlay, undo stack |
65
+ | Keyboard Shortcuts | Command registry + command palette |
66
+ | Mode Transitions | Compare/multiview/qMRI enter/exit, crosshair animation |
67
+ | Scroll, Zoom, and Pan | Mouse wheel, pinch zoom |
68
+ | Immersive Mode, Cross-Fade, and Visual Effects | Zen mode, fullscreen, animated transitions |
69
+ | UI Validation and Reconciliation | Reconcilers (~line 13666) |
70
+ | Colormap Strip and Wipe/Flicker Compare Tools | A/B wipe, flicker, checkerboard |
71
+ | Ruler, Line Profile, and Mini-Map | Distance measurement, intensity profile |
72
+ | Compact Mode and Touch Input | Touch gesture handling |
73
+ | File Picker and Session Management | File browser, drag-and-drop |
74
+
75
+ ## Mode Matrix
76
+
77
+ | Mode | Scale function | `modeManager.modeName` |
78
+ |------|---------------|------------------------|
79
+ | Normal | `scaleCanvas()` | `'normal'` |
80
+ | Immersive (Zen) | `scaleCanvas()` | `'normal'` |
81
+ | Compact | `scaleCanvas()` | `'normal'` |
82
+ | Multiview (3-pane oblique) | `mvScaleAllCanvases()` | `'multiview'` |
83
+ | Compare | `compareScaleCanvases()` | `'compare'` |
84
+ | Diff / Registration / Wipe/Flicker/Checker | `compareScaleCanvases()` | `'compare'` |
85
+ | Compare + MV | `compareMvScaleAllCanvases()` | `'compare-mv'` |
86
+ | Compare + qMRI | `compareQmriScaleAllCanvases()` | `'compare-qmri'` |
87
+ | qMRI | `qvScaleAllCanvases()` | `'qmri'` |
88
+ | qMRI Mosaic | `qvScaleAllCanvases()` | `'qmri-mosaic'` |
89
+ | MIP (3-D volume) | N/A (WebGL) | `'mip'` |
90
+
91
+ ## Key Concepts
92
+
93
+ ### Reconcilers (~line 13666)
94
+ Four functions enforcing consistent UI state across mode changes:
95
+ 1. **Unified UI reconciler** — master state enforcer
96
+ 2. **Layout container visibility reconciler** — show/hide mode-specific containers
97
+ 3. **Compare sub-mode state reconciler** — diff/overlay/wipe/flicker/checkerboard
98
+ 4. **CB / island visibility reconciler** — colorbar and dynamic island show/hide
99
+
100
+ **Rule:** All visibility changes go through reconcilers. Never set `style.display` or toggle classes in mode-entry/exit functions — that belongs in the reconcilers.
101
+
102
+ ### Command Registry
103
+ Three tables: `commands` (id → `{title, when, run}`), `keybinds` (key+modifiers → command id), `makeContext(state)` (mode/state flag bag). `dispatchCommand(e)` is the keydown prefix handler.
104
+
105
+ **Rule:** When adding or changing a keybind, update both `commands`/`keybinds` tables AND `GUIDE_TABS` (the static data structure that renders the help overlay). Do not edit overlay HTML directly.
106
+
107
+ ### View Component System (Phases 1–15 complete)
108
+ Introduces `View`, `Slicer`, `Layer`, `LayoutStrategy`, `modeManager` alongside the legacy render pipeline.
109
+
110
+ | Primitive | What it owns |
111
+ |-----------|-------------|
112
+ | `makeDisplayState(overrides)` | Plain-object factory: `{vmin, vmax, cmapIdx, logScale, complexMode, renderMode, projectionMode, …}` |
113
+ | `class View` | Canvas + ColorBar + DisplayState + Slicer + Layers[]. Has `init()`, `render()`, `requestRender()` |
114
+ | `class Slicer` | `FreeSliceSlicer`, `OrthogonalSlicer(axis)` |
115
+ | `class Layer` | `CrosshairLayer`, `VectorFieldLayer`, `OverlayLayer` — duck-typed, composable |
116
+ | `class LayoutStrategy` | `NormalLayout`, `MultiViewLayout`, `CompareLayout`, `QmriLayout`, etc. |
117
+ | `const modeManager` | Singleton: `{currentViews[], currentLayout, modeName, enterMode(), …}` |
118
+
119
+ **Sync-block pattern:** Every `enterXxx()` function ends with a block that creates thin `View` wrappers over existing legacy canvases/colorbars and populates `modeManager.currentViews` + `modeName`. The legacy render pipeline is unchanged.
120
+
121
+ **Dual-write pattern:** Where commands update legacy globals (`logScale`, `colormap_idx`, etc.), they also write to `view.displayState.xField` so both systems stay in sync.
122
+
123
+ **Shim layer (Section 7):** `Object.defineProperty(window, 'manualVmin', …)` intercepts all bare writes to `manualVmin`/`manualVmax` (27 sites) and routes them to `modeManager.currentViews[0].displayState.vmin/vmax`.
124
+
125
+ ### Eggs
126
+ Pill badges below the canvas showing active transforms: `FFT` `LOG` `MAGNITUDE` `PHASE` `REAL` `IMAG` `RGB` `ALPHA` `PROJECTION`. **ROI** and **SEGMENT** are NOT eggs — they are interaction modes with their own dynamic island UI.
127
+
128
+ ### Dynamic Islands
129
+ Floating UI panels: ROI statistics, segmentation controls, colorbar hover, dimension sliders. Use absolute/fixed positioning — must be tested across normal, immersive, multiview, and compare modes.
130
+
131
+ ## CSS Architecture
132
+
133
+ - **Dark theme only.** `#0c0c0c` background, `#d8d8d8` text, `#f5c842` yellow accents.
134
+ - **No CSS framework.** All custom properties in `:root`.
135
+ - **Mode-specific overrides** use `.immersive-mode`, `.compact-mode`, `.multiview-mode` classes on `body`.
136
+ - **`av-loading` class** on `body` during initial load — hides UI until data arrives.
137
+ - **Canvas size management:** `scaleCanvas()` (and variants) are the only legitimate way to set canvas dimensions. Never set canvas `width`/`height` directly.
138
+
139
+ ## WebSocket Binary Protocol
140
+ Slice data arrives as raw RGBA bytes. Header format and byte offsets are tightly coupled between `_server.py` (Python) and the WS handler in `_viewer.html` (JS). Changes to one must match the other exactly.
@@ -0,0 +1,34 @@
1
+ ---
2
+ name: project-state
3
+ description: Current working, in-progress, and shelved status. Load only when the task depends on active workstreams or recent shipped behavior.
4
+ triggers:
5
+ - "current state"
6
+ - "what's in progress"
7
+ - "recent work"
8
+ - "active feature"
9
+ - "shipped recently"
10
+ last_updated: 2026-04-15
11
+ ---
12
+
13
+ # Project State
14
+
15
+ ## Working
16
+
17
+ - CLI (`uvx arrayview file.npy`) and Python API (`view(arr)`) — both stable
18
+ - All six display environments: Jupyter inline, VS Code local, VS Code tunnel (stdio), Julia, native pywebview, SSH URL print
19
+ - File formats: `.npy`, `.npz`, `.nii`/`.nii.gz`, `.zarr`, `.h5`/`.hdf5`, `.mat`, `.tif`/`.tiff`, `.pt`/`.pth`
20
+ - Rendering pipeline: colormaps, complex modes, mosaic, RGB/RGBA, projections, overlays
21
+ - NIfTI spatial metadata, RAS resampling
22
+ - VS Code extension v0.14.5 — stable window ID via `EnvironmentVariableCollection`; `arrayview.openInFloatingWindow` setting moves new tabs to a floating window; `view(arr, floating=True)` and `arrayview file.npy --floating` open in a floating window per-call regardless of global setting; `!vscode.env.remoteName` guard removed (remote VS Code supports floating windows); floating mode now uses a single persistent shell hub panel (`_shell.html`) so all arrays share one floating window as tabs instead of opening separate windows; fixed: second CLI call now injects tab via `new_tab` postMessage relay (extension -> hub wrapper -> shell iframe) instead of relying on WebSocket notify which wasn't sent in VS Code mode
23
+ - Colorbar refactor: `ColorBar` JS class partially migrated (in progress)
24
+ - Colorbar island flip: `c` and `d` keys trigger 3D `rotateX` card flip (front=colorbar, back=cmap thumbnails/histogram)
25
+ - Cold-start loading spinner in VS Code and native shell
26
+
27
+ ## In Progress
28
+
29
+ - Smooth immersive transition — stale scrub geometry handoff is fixed, immersive overlay fade-in is held until after the class switch, shared slim colorbar returns through `drawSlimColorbar()` on reverse, and active scrub suppresses minimap/overflow/drag side effects. Single-pane scrub now targets the actual centered immersive viewport rect instead of a hardcoded corner box, the dimbar stays above the pane during scrub, the shared colorbar sits behind the growing pane, and the phantom extra `av-view-wrap` footprint in normal mode was removed by rebinding `NormalLayout` to the real `#viewer` canvas. Cross-mode parity and deeper reverse-pinch validation still need manual verification.
30
+
31
+ ## Not Yet Built
32
+
33
+ - Independent split view for mismatched-shape arrays (designed, shelved)
34
+ - Admin/config UI (file-based `~/.arrayview/config.toml` only)