arrayview 0.15.0__tar.gz → 0.17.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.
- {arrayview-0.15.0 → arrayview-0.17.0}/.gitignore +2 -1
- {arrayview-0.15.0 → arrayview-0.17.0}/.mex/ROUTER.md +1 -1
- {arrayview-0.15.0 → arrayview-0.17.0}/.mex/context/architecture.md +5 -5
- {arrayview-0.15.0 → arrayview-0.17.0}/.mex/context/decisions.md +9 -1
- {arrayview-0.15.0 → arrayview-0.17.0}/.mex/context/frontend.md +9 -3
- {arrayview-0.15.0 → arrayview-0.17.0}/.mex/context/project-state.md +13 -2
- {arrayview-0.15.0 → arrayview-0.17.0}/IMMERSIVE_ANIMATION.md +1 -1
- {arrayview-0.15.0 → arrayview-0.17.0}/PKG-INFO +2 -1
- {arrayview-0.15.0 → arrayview-0.17.0}/docs/loading.md +1 -1
- {arrayview-0.15.0 → arrayview-0.17.0}/docs/remote.md +3 -3
- {arrayview-0.15.0 → arrayview-0.17.0}/pyproject.toml +2 -1
- {arrayview-0.15.0 → arrayview-0.17.0}/src/arrayview/ARCHITECTURE.md +3 -3
- {arrayview-0.15.0 → arrayview-0.17.0}/src/arrayview/_launcher.py +180 -152
- {arrayview-0.15.0 → arrayview-0.17.0}/src/arrayview/_platform.py +23 -1
- {arrayview-0.15.0 → arrayview-0.17.0}/src/arrayview/_server.py +721 -7
- {arrayview-0.15.0 → arrayview-0.17.0}/src/arrayview/_stdio_server.py +1 -1
- {arrayview-0.15.0 → arrayview-0.17.0}/src/arrayview/_viewer.html +4349 -590
- {arrayview-0.15.0 → arrayview-0.17.0}/src/arrayview/_vscode.py +8 -8
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/test_api.py +90 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/test_browser.py +20 -9
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/test_interactions.py +18 -34
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/test_mode_roundtrip.py +6 -31
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/visual_smoke.py +19 -23
- {arrayview-0.15.0 → arrayview-0.17.0}/uv.lock +1103 -1
- {arrayview-0.15.0 → arrayview-0.17.0}/vscode-extension/extension.js +2 -123
- {arrayview-0.15.0 → arrayview-0.17.0}/.claude/skills/invocation-consistency/SKILL.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.claude/skills/modes-consistency/SKILL.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.claude/skills/ui-consistency-audit/SKILL.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.claude/skills/viewer-ui-checklist/SKILL.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.claude/skills/visual-bug-fixing/SKILL.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.github/copilot-instructions.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.github/workflows/docs.yml +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.github/workflows/python-publish.yml +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.ignore +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.mex/AGENTS.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.mex/SETUP.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.mex/SYNC.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.mex/context/conventions.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.mex/context/render-pipeline.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.mex/context/setup.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.mex/context/stack.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.mex/patterns/INDEX.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.mex/patterns/README.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.mex/patterns/add-file-format.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.mex/patterns/add-server-endpoint.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.mex/patterns/debug-render.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.mex/patterns/frontend-change.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.opencode/opencode.json +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.python-version +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/.vscode/settings.json +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/AGENTS.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/CONTRIBUTING.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/LICENSE +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/README.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/docs/comparing.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/docs/configuration.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/docs/display.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/docs/index.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/docs/logo.png +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/docs/measurement.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/docs/stylesheets/extra.css +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/docs/viewing.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/matlab/arrayview.m +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/mkdocs.yml +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/plans/2026-04-14-immersive-animation.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/plans/webview/LOG.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/scripts/demo.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/scripts/release.sh +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/src/arrayview/__init__.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/src/arrayview/__main__.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/src/arrayview/_app.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/src/arrayview/_config.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/src/arrayview/_icon.png +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/src/arrayview/_io.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/src/arrayview/_render.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/src/arrayview/_segmentation.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/src/arrayview/_session.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/src/arrayview/_shell.html +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/src/arrayview/_torch.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/src/arrayview/arrayview-opener.vsix +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/src/arrayview/gsap.min.js +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/conftest.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/make_vectorfield_test_arrays.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/test_cli.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/test_command_reachability.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/test_config.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/test_cross_mode_parametrized.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/test_large_arrays.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/test_loading_server.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/test_mode_consistency.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/test_mode_matrix.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/test_nifti_meta.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/test_rgb_pixel_art.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/test_torch.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/test_view_component_integration.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/test_view_component_unit.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/tests/ui_audit.py +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/vscode-extension/AGENTS.md +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/vscode-extension/LICENSE +0 -0
- {arrayview-0.15.0 → arrayview-0.17.0}/vscode-extension/package.json +0 -0
|
@@ -20,7 +20,7 @@ edges:
|
|
|
20
20
|
condition: when working on rendering, colormaps, LUTs, caching, or the render thread
|
|
21
21
|
- target: patterns/INDEX.md
|
|
22
22
|
condition: when starting a task — check the pattern index for a matching pattern file
|
|
23
|
-
last_updated: 2026-04-
|
|
23
|
+
last_updated: 2026-04-17
|
|
24
24
|
---
|
|
25
25
|
|
|
26
26
|
# arrayview — Router
|
|
@@ -43,18 +43,18 @@ Browser (_viewer.html — single self-contained HTML+JS+CSS file)
|
|
|
43
43
|
|
|
44
44
|
A `view()` call creates a `Session`, starts the FastAPI server if not running,
|
|
45
45
|
registers the session via HTTP POST `/load`, then opens a display for the detected
|
|
46
|
-
environment (Jupyter inline, VS Code
|
|
46
|
+
environment (Jupyter inline, VS Code webview panel or direct webview, native
|
|
47
47
|
pywebview, or system browser).
|
|
48
48
|
|
|
49
49
|
## Key Components
|
|
50
50
|
|
|
51
|
-
- **`_launcher.py`** — CLI parser, `view()` API, `ViewHandle`, server lifecycle,
|
|
51
|
+
- **`_launcher.py`** — CLI parser, `view()` API, `ViewHandle`, server lifecycle, reverse-tunnel relay (`--relay`), file watching. Heavy imports (`_session`, `_render`, `_io`, uvicorn) are all lazy to keep the CLI fast path near-zero cost.
|
|
52
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
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
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
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
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,
|
|
57
|
+
- **`_vscode.py`** — VS Code extension install/management, signal-file IPC, shared-memory IPC, webview panel and direct webview opening.
|
|
58
58
|
- **`_stdio_server.py`** — Alternative to FastAPI for VS Code tunnel (direct webview): JSON on stdin, length-prefixed binary on stdout.
|
|
59
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
60
|
|
|
@@ -63,11 +63,11 @@ pywebview, or system browser).
|
|
|
63
63
|
| Environment | Default display | Server mode |
|
|
64
64
|
|---|---|---|
|
|
65
65
|
| Jupyter | Inline iframe | network |
|
|
66
|
-
| VS Code local |
|
|
66
|
+
| VS Code local | Webview panel | network |
|
|
67
67
|
| VS Code tunnel | Direct webview (stdio) | stdio |
|
|
68
68
|
| Julia | System browser | network |
|
|
69
69
|
| CLI / Python script | Native pywebview | network |
|
|
70
|
-
| SSH terminal |
|
|
70
|
+
| SSH terminal | Prints URL — user forwards port with `ssh -L` | network |
|
|
71
71
|
|
|
72
72
|
Detection logic: `_platform.py`. Display opening: `_launcher.py` + `_vscode.py`.
|
|
73
73
|
|
|
@@ -14,7 +14,7 @@ edges:
|
|
|
14
14
|
condition: when a decision relates to technology choice
|
|
15
15
|
- target: context/frontend.md
|
|
16
16
|
condition: when a decision relates to the frontend architecture or viewer modes
|
|
17
|
-
last_updated: 2026-04-
|
|
17
|
+
last_updated: 2026-04-16
|
|
18
18
|
---
|
|
19
19
|
|
|
20
20
|
# Decisions
|
|
@@ -61,6 +61,14 @@ last_updated: 2026-04-15
|
|
|
61
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
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
63
|
|
|
64
|
+
### ROI in qMRI uses per-pane overlay canvases, not a shared overlay
|
|
65
|
+
**Date:** 2026-04-16
|
|
66
|
+
**Status:** Active
|
|
67
|
+
**Decision:** Each qMRI pane gets its own `.qv-roi-overlay` canvas element. ROI shapes are drawn on all overlays simultaneously during drag, and stats are fetched per parameter map.
|
|
68
|
+
**Reasoning:** In qMRI mode, the single main canvas is hidden and replaced by N independent pane canvases, each with its own coordinate system and scale. A shared overlay (like the main `#roi-overlay`) cannot span multiple independently-positioned canvases. Per-pane overlays also allow the ROI mirroring UX where drawing on one pane instantly shows the same shape on all others.
|
|
69
|
+
**Alternatives considered:** A single overlay canvas spanning the entire `#qmri-view-wrap` (rejected — would need to track pane positions and clip per-pane, fragile across resize/mosaic layout changes); reusing the main `#roi-overlay` (rejected — it's inside `#canvas-viewport` which is hidden in qMRI mode).
|
|
70
|
+
**Consequences:** Overlay canvases must set `background: transparent` to override the global `canvas { background: var(--bg) }` rule. qMRI view objects carry `roiOverlay` and `roiCtx` properties. `_drawAllQvRois()` replaces `_drawAllRois()` in qMRI mode; `_redrawRoiOverlays()` dispatches to the correct function based on mode.
|
|
71
|
+
|
|
64
72
|
### UI visibility changes go through reconcilers, not ad hoc style/classList calls
|
|
65
73
|
**Date:** 2025 (View Component System refactor)
|
|
66
74
|
**Status:** Active
|
|
@@ -23,7 +23,7 @@ edges:
|
|
|
23
23
|
condition: when writing new frontend code and need section separator conventions
|
|
24
24
|
- target: patterns/frontend-change.md
|
|
25
25
|
condition: when making a concrete change to _viewer.html
|
|
26
|
-
last_updated: 2026-04-
|
|
26
|
+
last_updated: 2026-04-17
|
|
27
27
|
---
|
|
28
28
|
|
|
29
29
|
# Frontend (_viewer.html)
|
|
@@ -60,7 +60,7 @@ Section separators: `/* ── Section Name ── */` in CSS, `// ── Sectio
|
|
|
60
60
|
| Info Bar and Pixel Display | Bottom info bar, hover pixel readout |
|
|
61
61
|
| State Persistence and Restore | URL hash state, `sessionStorage` save/restore |
|
|
62
62
|
| Rendering Pipeline | `updateView()`, play/animate, screenshot |
|
|
63
|
-
| ROI and Selection Modes | Rectangle/
|
|
63
|
+
| ROI and Selection Modes | Rectangle/circle/freehand ROI, statistics computation, qMRI cross-pane mirroring |
|
|
64
64
|
| nnInteractive Segmentation | Click-to-segment UI, mask overlay, undo stack |
|
|
65
65
|
| Keyboard Shortcuts | Command registry + command palette |
|
|
66
66
|
| Mode Transitions | Compare/multiview/qMRI enter/exit, crosshair animation |
|
|
@@ -122,11 +122,17 @@ Introduces `View`, `Slicer`, `Layer`, `LayoutStrategy`, `modeManager` alongside
|
|
|
122
122
|
|
|
123
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
124
|
|
|
125
|
+
### Plugin Shelf (`/` menu)
|
|
126
|
+
`SPECIAL_MODE_TILES` array defines five plugins: qMRI, Segmentation, ROI, Overlay, Vector field. The shelf supports multi-select (spacebar toggles, Enter applies). Mutual exclusion is enforced via `tile.excludes` arrays — ROI ↔ Segmentation, and Overlay/Vector field are reciprocally exclusive with all other plugins. `_applyShelfSelection()` diffs current state against selection, exits removed plugins, then enters new ones wrapped in `crossfade()`. Overlay and Vector field auto-seed the shelf at init when arrays are present (`_overlaySids.length > 0`, `hasVectorfield`); their per-tile state lives in `_shelfSelection.has(id)` since there is no separate mode flag.
|
|
127
|
+
|
|
125
128
|
### Eggs
|
|
126
129
|
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
130
|
|
|
128
131
|
### Dynamic Islands
|
|
129
|
-
Floating UI panels
|
|
132
|
+
Floating UI panels. `renderIsland()` collects all active plugins (`qmriActive`, `rectRoiMode`, `_segMode`/`_segActivating`, `_shelfSelection.has('overlay')`, `_shelfSelection.has('vectorfield')`) and renders sections for each with dividers between them. ROI/Segmentation share the same shape-toolbar + rows + magnifier-action layout; segmentation also renders a pulsing "connecting" loading row during `_segActivating`. Overlay section renders per-overlay rows (swatch + editable label + eye + `×`) with a shared opacity slider and a `+ add overlay` button that opens the filesystem picker. Vector field section renders a visibility row plus density and length sliders wired to `vfDensityLevel` / `vfLengthLevel` (both directions — keyboard `[ ] { }` also re-renders the island). An inline `~` collapse button at the island's top-right triggers `_collapseIslandToHint()`. `renderIsland()` early-returns while `_islandSliderDragging` is true so a mid-drag DOM rebuild doesn't kill the grab. Use absolute/fixed positioning — must be tested across normal, immersive, multiview, and compare modes.
|
|
133
|
+
|
|
134
|
+
### ROI in qMRI Mode
|
|
135
|
+
Each qMRI pane has a `.qv-roi-overlay` canvas (transparent, `position: absolute`, `pointer-events: none`). Drawing a ROI on any pane mirrors it to all panes in real-time via `_drawAllQvRois()`. On finalize, `_fetchQvRoiStats()` fetches per-parameter-map statistics in parallel. Results stored as `roi.qmriStats[]` and displayed as sub-rows in the island. Key functions: `_drawAllQvRois`, `_clearQvRoiOverlays`, `_redrawRoiOverlays`, `_finalizeQvRoi`, `_fetchQvRoiStats`. **Important:** the global `canvas { background: var(--bg) }` rule makes all canvas elements opaque — overlay canvases must set `background: transparent` to avoid covering the underlying content.
|
|
130
136
|
|
|
131
137
|
## CSS Architecture
|
|
132
138
|
|
|
@@ -7,7 +7,7 @@ triggers:
|
|
|
7
7
|
- "recent work"
|
|
8
8
|
- "active feature"
|
|
9
9
|
- "shipped recently"
|
|
10
|
-
last_updated: 2026-04-
|
|
10
|
+
last_updated: 2026-04-17
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
# Project State
|
|
@@ -15,7 +15,7 @@ last_updated: 2026-04-15
|
|
|
15
15
|
## Working
|
|
16
16
|
|
|
17
17
|
- CLI (`uvx arrayview file.npy`) and Python API (`view(arr)`) — both stable
|
|
18
|
-
-
|
|
18
|
+
- Display environments: Jupyter inline, VS Code local, VS Code tunnel (stdio), Julia, native pywebview, SSH URL print (user forwards port with `ssh -L`)
|
|
19
19
|
- File formats: `.npy`, `.npz`, `.nii`/`.nii.gz`, `.zarr`, `.h5`/`.hdf5`, `.mat`, `.tif`/`.tiff`, `.pt`/`.pth`
|
|
20
20
|
- Rendering pipeline: colormaps, complex modes, mosaic, RGB/RGBA, projections, overlays
|
|
21
21
|
- NIfTI spatial metadata, RAS resampling
|
|
@@ -23,10 +23,21 @@ last_updated: 2026-04-15
|
|
|
23
23
|
- Colorbar refactor: `ColorBar` JS class partially migrated (in progress)
|
|
24
24
|
- Colorbar island flip: `c` and `d` keys trigger 3D `rotateX` card flip (front=colorbar, back=cmap thumbnails/histogram)
|
|
25
25
|
- Cold-start loading spinner in VS Code and native shell
|
|
26
|
+
- Plugin shelf (`/` menu) supports multi-select: spacebar toggles plugins, Enter applies selection. Mutual exclusion enforced (ROI ↔ Segmentation, and overlay/vectorfield ↔ everything else). Cursor indicator shows focused tile via yellow background + left accent bar
|
|
27
|
+
- Dynamic island renders sections for all active plugins simultaneously (qMRI pills + ROI shapes/stats separated by divider), replacing the old single-plugin priority chain
|
|
28
|
+
- ROI mode works alongside qMRI: drawing on any pane mirrors the ROI to all panes in real-time via per-pane overlay canvases; stats fetched per parameter map and shown as sub-rows in the island
|
|
29
|
+
- qMRI map toggle (`_islandToggleQmriMap`) fade animation now covers dimbar and array-name in addition to panes and colorbars
|
|
30
|
+
- Segmentation menu shares the ROI layout (yellow accent, magnifier action icon, common `#export-overlay` modal). Pre-activation shows a pulsing "nnInteractive · connecting" row that morphs into the normal shape toolbar once `/seg/activate` resolves
|
|
31
|
+
- Overlays are a plugin tile (`OV`): per-overlay row with colour swatch, editable label, eye toggle, × delete; shared opacity slider; `+ add overlay` opens a filesystem picker rooted at the launched file's directory
|
|
32
|
+
- Vector field is a plugin tile (`VF`): row with visibility toggle + density/length sliders bi-directionally wired to the `[ ] { }` keyboard commands
|
|
33
|
+
- CLI `--overlay FILE` can be repeated to load multiple overlays at launch
|
|
34
|
+
- Filesystem picker endpoint (`GET /fs/list`) clamped to `$HOME`. Accepts `base_sid` + `mode` (`overlay` | `vectorfield`) to filter entries by a cheap header-shape peek (`.npy`, `.nii`/`.nii.gz`, `.h5`, `.zarr`); overlay mode requires identical shape, vectorfield mode requires base shape plus one axis of size 3
|
|
35
|
+
- Island collapse affordance: inline `~` at the island's top-right animates the panel into the bottom-left `~` hint circle; external `~` hint only visible while the island is actually collapsed. New `/` hint circle at bottom-right opens the plugin shelf
|
|
26
36
|
|
|
27
37
|
## In Progress
|
|
28
38
|
|
|
29
39
|
- 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.
|
|
40
|
+
- ROI + qMRI integration refinements: floodfill not yet supported on qMRI panes; ROI hover tooltip not yet wired for qMRI canvases; per-pane stats are re-fetched on each ROI draw but not updated on slice scroll
|
|
30
41
|
|
|
31
42
|
## Not Yet Built
|
|
32
43
|
|
|
@@ -16,7 +16,7 @@ The canvas grows smoothly, tied 1-to-1 with how much you've pinched. No spring,
|
|
|
16
16
|
|
|
17
17
|
### When the canvas reaches its final immersive size (progress = 1)
|
|
18
18
|
|
|
19
|
-
The immersive layout class is applied.
|
|
19
|
+
The immersive layout class is applied. A0t this point the dimbar and colorbar are completely invisible, so they are silently repositioned to their overlay locations — dimbar near the top of the canvas, colorbar near the bottom. They teleport while invisible, not animate. Then they fade from invisible to visible at those positions. They do not move during this fade-in.
|
|
20
20
|
|
|
21
21
|
### Pinching back out (progress 1 → 0)
|
|
22
22
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arrayview
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.17.0
|
|
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
|
|
@@ -16,6 +16,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
16
16
|
Requires-Python: >=3.12
|
|
17
17
|
Requires-Dist: fastapi>=0.129.0
|
|
18
18
|
Requires-Dist: h5py>=3.0
|
|
19
|
+
Requires-Dist: jupyter-server-proxy>=4.0
|
|
19
20
|
Requires-Dist: matplotlib>=3.9.0
|
|
20
21
|
Requires-Dist: nibabel>=5.3.3
|
|
21
22
|
Requires-Dist: numpy>=2.4.2
|
|
@@ -61,7 +61,7 @@ Key parameters:
|
|
|
61
61
|
| `None` | Auto: native outside Jupyter, inline inside |
|
|
62
62
|
| `"native"` | Native desktop window |
|
|
63
63
|
| `"browser"` | System browser |
|
|
64
|
-
| `"vscode"` | VS Code
|
|
64
|
+
| `"vscode"` | VS Code tab |
|
|
65
65
|
| `"inline"` | Inline IFrame (Jupyter / VS Code notebook) |
|
|
66
66
|
|
|
67
67
|
## File Picker
|
|
@@ -12,7 +12,7 @@ Then open `http://localhost:8000` in your local browser.
|
|
|
12
12
|
|
|
13
13
|
## VS Code
|
|
14
14
|
|
|
15
|
-
Auto-detects VS Code terminals and opens in
|
|
15
|
+
Auto-detects VS Code terminals and opens in a VS Code tab. Works automatically.
|
|
16
16
|
|
|
17
17
|
## VS Code tunnel
|
|
18
18
|
|
|
@@ -73,7 +73,7 @@ ssh -R 8000:localhost:8000 user@gpu-server
|
|
|
73
73
|
arrayview array.npy
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
The array is sent back to the remote machine and the viewer opens in
|
|
76
|
+
The array is sent back to the remote machine and the viewer opens in a VS Code tab locally.
|
|
77
77
|
|
|
78
78
|
If port 8000 is already taken on the GPU server:
|
|
79
79
|
|
|
@@ -88,5 +88,5 @@ arrayview array.npy --relay 8765
|
|
|
88
88
|
|-------|----------|
|
|
89
89
|
| `native` | Desktop window (default outside Jupyter) |
|
|
90
90
|
| `browser` | System browser |
|
|
91
|
-
| `vscode` | VS Code
|
|
91
|
+
| `vscode` | VS Code tab |
|
|
92
92
|
| `inline` | Inline IFrame (default in Jupyter) |
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "arrayview"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.17.0"
|
|
8
8
|
description = "Fast multi-dimensional array viewer"
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -22,6 +22,7 @@ urls = { "Home" = "https://github.com/oscarvanderheide/arrayview", "Source" = "h
|
|
|
22
22
|
dependencies = [
|
|
23
23
|
"fastapi>=0.129.0",
|
|
24
24
|
"h5py>=3.0",
|
|
25
|
+
"jupyter-server-proxy>=4.0",
|
|
25
26
|
"matplotlib>=3.9.0",
|
|
26
27
|
"nibabel>=5.3.3",
|
|
27
28
|
"numpy>=2.4.2",
|
|
@@ -26,11 +26,11 @@ Frontend (_viewer.html — single self-contained HTML file)
|
|
|
26
26
|
| Environment | Default display | Server mode |
|
|
27
27
|
|--------------------|------------------------------------|-------------|
|
|
28
28
|
| Jupyter | Inline iframe | network |
|
|
29
|
-
| VS Code local |
|
|
29
|
+
| VS Code local | Webview panel (network) | network |
|
|
30
30
|
| VS Code tunnel | Direct webview (stdio) | stdio |
|
|
31
31
|
| Julia | System browser | network |
|
|
32
32
|
| CLI / Python script | Native pywebview | network |
|
|
33
|
-
| SSH terminal
|
|
33
|
+
| SSH terminal | Prints URL — user forwards port with `ssh -L` | network |
|
|
34
34
|
|
|
35
35
|
Detection logic lives in `_platform.py`. Display opening logic lives in `_launcher.py` (section: ViewHandle and view() API) and `_vscode.py`.
|
|
36
36
|
|
|
@@ -43,7 +43,7 @@ Detection logic lives in `_platform.py`. Display opening logic lives in `_launch
|
|
|
43
43
|
| `_app.py` | 179 | Backward-compat shim — re-exports everything from the split modules |
|
|
44
44
|
| `_config.py` | 121 | `~/.arrayview/config.toml` read/write, valid window modes/env keys |
|
|
45
45
|
| `_io.py` | 253 | Data loading: numpy, NIfTI (lazy nibabel), zarr, DICOM, raw files |
|
|
46
|
-
| `_launcher.py` | 2817 | **Main entry.** CLI parser, `view()` API, `ViewHandle`, server lifecycle,
|
|
46
|
+
| `_launcher.py` | 2817 | **Main entry.** CLI parser, `view()` API, `ViewHandle`, server lifecycle, reverse-tunnel relay, demo arrays, file watching |
|
|
47
47
|
| `_platform.py` | 396 | Environment detection: Jupyter, VS Code, SSH, tunnel, Julia, native-window capability |
|
|
48
48
|
| `_render.py` | 834 | Rendering pipeline: colormap LUTs, slice extraction, RGBA/RGB/mosaic rendering, overlay compositing, preload |
|
|
49
49
|
| `_segmentation.py` | 227 | nnInteractive segmentation client (pure HTTP, no nnInteractive dependency) |
|