arrayview 0.47.5__tar.gz → 0.48.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.47.5 → arrayview-0.48.0}/PKG-INFO +1 -1
- {arrayview-0.47.5 → arrayview-0.48.0}/pyproject.toml +1 -1
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_io.py +25 -7
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_render.py +3 -1
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_routes_websocket.py +24 -6
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_viewer.html +98 -53
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_vscode_extension.py +1 -1
- arrayview-0.48.0/src/arrayview/arrayview-opener.vsix +0 -0
- arrayview-0.47.5/src/arrayview/arrayview-opener.vsix +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/.gitignore +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/LICENSE +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/README.md +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/ARCHITECTURE.md +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/__init__.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/__main__.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_analysis.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_app.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_bootstrap_app.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_config.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_diagnostics.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_dicom.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_diff.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_extra_ports.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_icon.png +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_imaging.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_instance_registry.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_launch_plan.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_launch_trace.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_launcher.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_lifecycle.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_overlays.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_platform.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_routes_analysis.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_routes_drop.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_routes_export.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_routes_loading.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_routes_persistence.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_routes_preferences.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_routes_preload.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_routes_query.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_routes_rendering.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_routes_segmentation.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_routes_state.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_routes_vectorfield.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_segmentation.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_server.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_session.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_session_spec.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_shell.html +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_source_safety.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_synthetic_mri.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_torch.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_tutorial.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_vectorfield.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_vscode.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_vscode_browser.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_vscode_signal.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/_watch.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/gsap.min.js +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/src/arrayview/todo.md +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/capture_patient_loading.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/capture_v_animation.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/conftest.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/make_vectorfield_test_arrays.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_1d_arrays.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_api.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_backend_shared.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_browser.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_cli.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_config.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_dicom.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_drop_import.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_instance_registry.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_io.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_large_arrays.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_launch_blackbox.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_mode_roundtrip.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_nifti_meta.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_nifti_series.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_rgb_pixel_art.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_safe_server_stop.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_source_safety.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_torch.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_tutorial.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/test_vsix_bundle.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/ui_audit.py +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/v_anim_frames/.gitkeep +0 -0
- {arrayview-0.47.5 → arrayview-0.48.0}/tests/visual_smoke.py +0 -0
|
@@ -1748,7 +1748,7 @@ def _viewer_start_index(shape, dims):
|
|
|
1748
1748
|
)
|
|
1749
1749
|
|
|
1750
1750
|
|
|
1751
|
-
def prefetch_first_frame_pages(mapped, filepath) -> bool:
|
|
1751
|
+
def prefetch_first_frame_pages(mapped, filepath, progress=None) -> bool:
|
|
1752
1752
|
"""Pull just the pages of the viewer's first frame into memory, in parallel.
|
|
1753
1753
|
|
|
1754
1754
|
When the first frame is one element per page across the whole file (a
|
|
@@ -1768,7 +1768,9 @@ def prefetch_first_frame_pages(mapped, filepath) -> bool:
|
|
|
1768
1768
|
dims = _viewer_start_dims_for_shape(shape)
|
|
1769
1769
|
if dims is None:
|
|
1770
1770
|
return False
|
|
1771
|
-
return prefetch_slice_pages(
|
|
1771
|
+
return prefetch_slice_pages(
|
|
1772
|
+
mapped, filepath, dims, _viewer_start_index(shape, dims), progress=progress
|
|
1773
|
+
)
|
|
1772
1774
|
|
|
1773
1775
|
|
|
1774
1776
|
@functools.lru_cache(maxsize=64)
|
|
@@ -1776,7 +1778,7 @@ def _on_known_local_disk_cached(filepath) -> bool:
|
|
|
1776
1778
|
return _on_known_local_disk(filepath)
|
|
1777
1779
|
|
|
1778
1780
|
|
|
1779
|
-
def prefetch_slice_pages(mapped, filepath, dims, index) -> bool:
|
|
1781
|
+
def prefetch_slice_pages(mapped, filepath, dims, index, progress=None) -> bool:
|
|
1780
1782
|
"""Fetch the pages of one 2-D slice of a memmap with parallel reads.
|
|
1781
1783
|
|
|
1782
1784
|
Faulting such a slice in through the map is the slow way: readahead drags
|
|
@@ -1784,7 +1786,9 @@ def prefetch_slice_pages(mapped, filepath, dims, index) -> bool:
|
|
|
1784
1786
|
C-order file), and without readahead each page is its own wait (7.7 s).
|
|
1785
1787
|
Many small reads in flight take ~0.7 s on a local disk. Only worth it when
|
|
1786
1788
|
the slice is scattered one element per page; returns False otherwise, or
|
|
1787
|
-
off a known local disk, having read nothing.
|
|
1789
|
+
off a known local disk, having read nothing. *progress*, if given, is
|
|
1790
|
+
called with ``(pages_done, pages_total)`` as batches of pages land — this
|
|
1791
|
+
fetch is the whole wait, so it is what a progress line should follow.
|
|
1788
1792
|
"""
|
|
1789
1793
|
shape = tuple(int(s) for s in mapped.shape)
|
|
1790
1794
|
strides = tuple(int(s) for s in mapped.strides)
|
|
@@ -1815,14 +1819,28 @@ def prefetch_slice_pages(mapped, filepath, dims, index) -> bool:
|
|
|
1815
1819
|
except (AttributeError, OSError):
|
|
1816
1820
|
pass
|
|
1817
1821
|
offsets = (pages * _MMAP_PAGE_BYTES).tolist()
|
|
1822
|
+
total = len(offsets)
|
|
1818
1823
|
n = _FIRST_FRAME_PREFETCH_THREADS
|
|
1824
|
+
# Interleaved batches: each one spans the file, so batches finish at
|
|
1825
|
+
# a steady rate and the progress they report moves evenly.
|
|
1826
|
+
batches = max(n, 64)
|
|
1827
|
+
lock = threading.Lock()
|
|
1828
|
+
done = [0]
|
|
1819
1829
|
|
|
1820
1830
|
def _warm(part):
|
|
1821
1831
|
for offset in part:
|
|
1822
1832
|
os.pread(fd, _MMAP_PAGE_BYTES, offset)
|
|
1833
|
+
if progress is not None:
|
|
1834
|
+
with lock:
|
|
1835
|
+
done[0] += len(part)
|
|
1836
|
+
finished = done[0]
|
|
1837
|
+
try:
|
|
1838
|
+
progress(finished, total)
|
|
1839
|
+
except Exception:
|
|
1840
|
+
pass
|
|
1823
1841
|
|
|
1824
1842
|
with ThreadPoolExecutor(max_workers=n, thread_name_prefix="arrayview-slice-prefetch") as pool:
|
|
1825
|
-
for _ in pool.map(_warm, [offsets[k::
|
|
1843
|
+
for _ in pool.map(_warm, [offsets[k::batches] for k in range(batches)]):
|
|
1826
1844
|
pass
|
|
1827
1845
|
except OSError:
|
|
1828
1846
|
return False
|
|
@@ -1988,12 +2006,12 @@ def load_data(filepath, key=None, *, progress=None, fast_first_frame=False):
|
|
|
1988
2006
|
mapped = np.load(filepath, mmap_mode="r")
|
|
1989
2007
|
if size >= npy_eager_limit():
|
|
1990
2008
|
if fast_first_frame:
|
|
1991
|
-
prefetch_first_frame_pages(mapped, filepath)
|
|
2009
|
+
prefetch_first_frame_pages(mapped, filepath, progress)
|
|
1992
2010
|
return mapped
|
|
1993
2011
|
if mmap_first_frame_is_cheap(mapped, size):
|
|
1994
2012
|
# Session creation upgrades this to an in-RAM array in the background.
|
|
1995
2013
|
return mapped
|
|
1996
|
-
if fast_first_frame and prefetch_first_frame_pages(mapped, filepath):
|
|
2014
|
+
if fast_first_frame and prefetch_first_frame_pages(mapped, filepath, progress):
|
|
1997
2015
|
# Same as above: the first frame's pages are already in memory, and
|
|
1998
2016
|
# the full read runs behind the viewer.
|
|
1999
2017
|
return mapped
|
|
@@ -214,7 +214,9 @@ def extract_slice(session, dim_x, dim_y, idx_list, progress=None):
|
|
|
214
214
|
from ._io import prefetch_slice_pages
|
|
215
215
|
|
|
216
216
|
try:
|
|
217
|
-
prefetch_slice_pages(
|
|
217
|
+
prefetch_slice_pages(
|
|
218
|
+
data, data.filename, (dim_x, dim_y), idx_list, progress=progress
|
|
219
|
+
)
|
|
218
220
|
except Exception:
|
|
219
221
|
pass
|
|
220
222
|
extracted = _extract_with_progress(data, slicer, progress)
|
|
@@ -81,9 +81,11 @@ def _render_progress_reporter(
|
|
|
81
81
|
Anything finishing inside `quiet_for_s` therefore shows nothing at all,
|
|
82
82
|
which is what keeps a fast load from flashing a bar on its way past.
|
|
83
83
|
"""
|
|
84
|
-
state = {"started": time.perf_counter(), "last": 0.0}
|
|
84
|
+
state = {"started": time.perf_counter(), "last": 0.0, "closed": False}
|
|
85
85
|
|
|
86
86
|
def report(done, total):
|
|
87
|
+
if state["closed"]:
|
|
88
|
+
return
|
|
87
89
|
now = time.perf_counter()
|
|
88
90
|
if now - state["started"] < quiet_for_s:
|
|
89
91
|
return
|
|
@@ -98,15 +100,24 @@ def _render_progress_reporter(
|
|
|
98
100
|
"total": int(total),
|
|
99
101
|
"label": label,
|
|
100
102
|
}
|
|
103
|
+
def _send():
|
|
104
|
+
# Checked again on the loop: an update queued just before the
|
|
105
|
+
# frame was sent would otherwise arrive after it, and the viewer
|
|
106
|
+
# (which clears its bar when a frame lands) kept the bar up.
|
|
107
|
+
if not state["closed"]:
|
|
108
|
+
asyncio.ensure_future(_send_json_quietly(ws, payload))
|
|
109
|
+
|
|
101
110
|
try:
|
|
102
|
-
loop.call_soon_threadsafe(
|
|
103
|
-
lambda: asyncio.ensure_future(_send_json_quietly(ws, payload))
|
|
104
|
-
)
|
|
111
|
+
loop.call_soon_threadsafe(_send)
|
|
105
112
|
except RuntimeError:
|
|
106
113
|
# Loop already closed (viewer went away mid-render); progress is
|
|
107
114
|
# never worth raising over.
|
|
108
115
|
pass
|
|
109
116
|
|
|
117
|
+
def close():
|
|
118
|
+
state["closed"] = True
|
|
119
|
+
|
|
120
|
+
report.close = close
|
|
110
121
|
return report
|
|
111
122
|
|
|
112
123
|
|
|
@@ -118,9 +129,11 @@ async def _await_session_reporting_read_progress(ws, sid):
|
|
|
118
129
|
holding a line to the viewer while the wait happens.
|
|
119
130
|
|
|
120
131
|
Silent for the first stretch, so an ordinary fast open never flashes a
|
|
121
|
-
bar on its way past
|
|
132
|
+
bar on its way past. Shorter than the viewer's own 0.6 s before it shows
|
|
133
|
+
its loading screen (counted from page load, which starts later), so the
|
|
134
|
+
line is ready when the screen appears rather than coming after it.
|
|
122
135
|
"""
|
|
123
|
-
quiet_for_s = 0.
|
|
136
|
+
quiet_for_s = 0.3
|
|
124
137
|
started = time.perf_counter()
|
|
125
138
|
waiter = asyncio.ensure_future(wait_for_session_ready(sid))
|
|
126
139
|
last = None
|
|
@@ -694,7 +707,12 @@ def register_websocket_routes(app) -> None:
|
|
|
694
707
|
|
|
695
708
|
# The pixels exist; nothing after this point reads the array,
|
|
696
709
|
# and the viewer clears its indicator when the frame lands.
|
|
710
|
+
# Closing the reporter also drops updates still queued for
|
|
711
|
+
# the socket, which would otherwise land after the frame.
|
|
712
|
+
sink = session.progress_sink
|
|
697
713
|
session.progress_sink = None
|
|
714
|
+
if hasattr(sink, "close"):
|
|
715
|
+
sink.close()
|
|
698
716
|
render_ms = (time.perf_counter() - render_t0) * 1000.0
|
|
699
717
|
post_t0 = time.perf_counter()
|
|
700
718
|
header = np.array([seq, w, h], dtype=np.uint32).tobytes()
|
|
@@ -4482,9 +4482,17 @@
|
|
|
4482
4482
|
box-shadow: none;
|
|
4483
4483
|
}
|
|
4484
4484
|
.qmri-synth-param-value { color: var(--text); text-align: right; font-variant-numeric: tabular-nums; }
|
|
4485
|
+
/* Hidden for the first 600 ms of the page: most opens draw their
|
|
4486
|
+
first frame within that, and a logo that flashes past reads as
|
|
4487
|
+
noise. Only a slower open gets the loading screen (see the script
|
|
4488
|
+
after the overlay markup). */
|
|
4485
4489
|
#loading-overlay {
|
|
4486
4490
|
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
4487
4491
|
position: fixed; inset: 0; z-index: 5; pointer-events: none;
|
|
4492
|
+
visibility: hidden; opacity: 0;
|
|
4493
|
+
}
|
|
4494
|
+
#loading-overlay.av-load-shown {
|
|
4495
|
+
visibility: visible; opacity: 1; transition: opacity 0.15s ease;
|
|
4488
4496
|
}
|
|
4489
4497
|
#loading-overlay .av-load-logo {
|
|
4490
4498
|
width: 64px; height: 64px;
|
|
@@ -4492,11 +4500,28 @@
|
|
|
4492
4500
|
#loading-overlay .av-load-name {
|
|
4493
4501
|
margin-top: 14px; font-size: 11px; color: var(--muted);
|
|
4494
4502
|
letter-spacing: 0.1em; text-transform: uppercase;
|
|
4495
|
-
|
|
4503
|
+
min-height: 1.2em; opacity: 0;
|
|
4496
4504
|
}
|
|
4497
4505
|
#loading-overlay .av-load-name.visible { opacity: 1; }
|
|
4506
|
+
/* The startup screen is the only loading signal until the first frame:
|
|
4507
|
+
logo, name, and — whenever there is progress to report — this line
|
|
4508
|
+
under the name. The top-edge hairline and the busy panel stay hidden
|
|
4509
|
+
meanwhile (see _reconcileUI), so a slow open never stacks three
|
|
4510
|
+
different indicators. */
|
|
4511
|
+
#loading-overlay .av-load-progress {
|
|
4512
|
+
margin-top: 12px; width: 120px; height: 2px; border-radius: 1px;
|
|
4513
|
+
background: color-mix(in srgb, var(--muted) 35%, transparent);
|
|
4514
|
+
overflow: hidden; opacity: 0; transition: opacity 0.25s ease;
|
|
4515
|
+
}
|
|
4516
|
+
#loading-overlay .av-load-progress.visible { opacity: 1; }
|
|
4517
|
+
#loading-overlay .av-load-progress .bar {
|
|
4518
|
+
height: 100%; width: 0%;
|
|
4519
|
+
background: var(--accent, #4cc9f0);
|
|
4520
|
+
transition: width 0.25s linear;
|
|
4521
|
+
}
|
|
4498
4522
|
#loading-overlay.av-array-switch .av-load-logo,
|
|
4499
|
-
#loading-overlay.av-array-switch .av-load-name
|
|
4523
|
+
#loading-overlay.av-array-switch .av-load-name,
|
|
4524
|
+
#loading-overlay.av-array-switch .av-load-progress { display: none; }
|
|
4500
4525
|
/* Deliberately outside #loading-overlay. The overlay is centred over the
|
|
4501
4526
|
canvas, so anything drawn in it sits on top of the slice — wrong for
|
|
4502
4527
|
progress that continues after the viewer is live and showing data.
|
|
@@ -5717,12 +5742,29 @@
|
|
|
5717
5742
|
<div id="multi-view-wrap"></div>
|
|
5718
5743
|
<div id="qmri-view-wrap"></div>
|
|
5719
5744
|
<div id="stack-mosaic-wrap"></div>
|
|
5720
|
-
<div id="loading-overlay"><svg class="av-load-logo av-logo-loading" width="64" height="64" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><rect width="16" height="16" rx="2.5" fill="#0c0c0c"/><rect id="load-b0" x="2" y="2" width="3" height="3" fill="#3a0ca3"/><rect id="load-b1" x="6.5" y="2" width="3" height="3" fill="#560bad"/><rect id="load-b2" x="11" y="2" width="3" height="3" fill="#c77dff"/><rect id="load-b3" x="2" y="6.5" width="3" height="3" fill="#4361ee"/><rect id="load-b4" x="6.5" y="6.5" width="3" height="3" fill="#4cc9f0"/><rect id="load-b5" x="11" y="6.5" width="3" height="3" fill="#f5c842"/><rect id="load-b6" x="2" y="11" width="3" height="3" fill="#4895ef"/><rect id="load-b7" x="6.5" y="11" width="3" height="3" fill="#80ed99"/><rect id="load-b8" x="11" y="11" width="3" height="3" fill="#f8961e"/></svg><div class="av-load-name"></div></div>
|
|
5745
|
+
<div id="loading-overlay"><svg class="av-load-logo av-logo-loading" width="64" height="64" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><rect width="16" height="16" rx="2.5" fill="#0c0c0c"/><rect id="load-b0" x="2" y="2" width="3" height="3" fill="#3a0ca3"/><rect id="load-b1" x="6.5" y="2" width="3" height="3" fill="#560bad"/><rect id="load-b2" x="11" y="2" width="3" height="3" fill="#c77dff"/><rect id="load-b3" x="2" y="6.5" width="3" height="3" fill="#4361ee"/><rect id="load-b4" x="6.5" y="6.5" width="3" height="3" fill="#4cc9f0"/><rect id="load-b5" x="11" y="6.5" width="3" height="3" fill="#f5c842"/><rect id="load-b6" x="2" y="11" width="3" height="3" fill="#4895ef"/><rect id="load-b7" x="6.5" y="11" width="3" height="3" fill="#80ed99"/><rect id="load-b8" x="11" y="11" width="3" height="3" fill="#f8961e"/></svg><div class="av-load-name"></div><div class="av-load-progress"><div class="bar"></div></div></div>
|
|
5721
5746
|
<div id="render-progress"><div class="bar"></div></div>
|
|
5722
5747
|
<div id="render-progress-text"></div>
|
|
5723
5748
|
<div id="fft-spinner-overlay"><div class="av-loading-panel"><svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><rect width="16" height="16" rx="2.5" fill="#0c0c0c"/><rect x="2" y="2" width="3" height="3" fill="#3a0ca3"/><rect x="6.5" y="2" width="3" height="3" fill="#560bad"/><rect x="11" y="2" width="3" height="3" fill="#c77dff"/><rect x="2" y="6.5" width="3" height="3" fill="#4361ee"/><rect x="6.5" y="6.5" width="3" height="3" fill="#4cc9f0"/><rect x="11" y="6.5" width="3" height="3" fill="#f5c842"/><rect x="2" y="11" width="3" height="3" fill="#4895ef"/><rect x="6.5" y="11" width="3" height="3" fill="#80ed99"/><rect x="11" y="11" width="3" height="3" fill="#f8961e"/></svg><span>Computing FFT…</span></div></div>
|
|
5724
5749
|
<div id="busy-overlay" role="status" aria-live="polite" aria-hidden="true"><div class="av-loading-panel"><svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><rect width="16" height="16" rx="2.5" fill="#0c0c0c"/><rect x="2" y="2" width="3" height="3" fill="#3a0ca3"/><rect x="6.5" y="2" width="3" height="3" fill="#560bad"/><rect x="11" y="2" width="3" height="3" fill="#c77dff"/><rect x="2" y="6.5" width="3" height="3" fill="#4361ee"/><rect x="6.5" y="6.5" width="3" height="3" fill="#4cc9f0"/><rect x="11" y="6.5" width="3" height="3" fill="#f5c842"/><rect x="2" y="11" width="3" height="3" fill="#4895ef"/><rect x="6.5" y="11" width="3" height="3" fill="#80ed99"/><rect x="11" y="11" width="3" height="3" fill="#f8961e"/></svg><span>Loading…</span></div></div>
|
|
5725
5750
|
<script>
|
|
5751
|
+
// Show the loading screen only if the first frame has not arrived
|
|
5752
|
+
// 600 ms into the page (a switch between arrays shows its panel
|
|
5753
|
+
// at once). Timed from navigation start, here rather than in the
|
|
5754
|
+
// main script, which may itself take longer than that to arrive.
|
|
5755
|
+
try {
|
|
5756
|
+
const ol = document.getElementById('loading-overlay');
|
|
5757
|
+
setTimeout(() => {
|
|
5758
|
+
if (document.body.classList.contains('av-loading')) ol.classList.add('av-load-shown');
|
|
5759
|
+
}, Math.max(0, 600 - performance.now()));
|
|
5760
|
+
} catch (_) {}
|
|
5761
|
+
// The array's name under the startup logo, so the loading screen
|
|
5762
|
+
// does not change while it is up.
|
|
5763
|
+
try {
|
|
5764
|
+
const name = new URLSearchParams(__ARRAYVIEW_QUERY__ || location.search).get('av_name');
|
|
5765
|
+
const el = document.querySelector('#loading-overlay .av-load-name');
|
|
5766
|
+
if (el && name) { el.textContent = name; el.classList.add('visible'); }
|
|
5767
|
+
} catch (_) {}
|
|
5726
5768
|
// After a switch between arrays of a multi-array file the page
|
|
5727
5769
|
// reloads. Show the same small loading panel the switch used, not
|
|
5728
5770
|
// the startup splash, so no second, larger logo flashes by.
|
|
@@ -5734,7 +5776,7 @@
|
|
|
5734
5776
|
const panel = document.querySelector('#busy-overlay .av-loading-panel').cloneNode(true);
|
|
5735
5777
|
panel.querySelector('span').textContent = `Loading ${key}…`;
|
|
5736
5778
|
panel.classList.add('av-array-switch-panel');
|
|
5737
|
-
ol.classList.add('av-array-switch');
|
|
5779
|
+
ol.classList.add('av-array-switch', 'av-load-shown');
|
|
5738
5780
|
ol.appendChild(panel);
|
|
5739
5781
|
}
|
|
5740
5782
|
} catch (_) {}
|
|
@@ -5920,6 +5962,17 @@
|
|
|
5920
5962
|
// server could not answer immediately (still loading), in which case
|
|
5921
5963
|
// the viewer asks over the socket exactly as it always has.
|
|
5922
5964
|
const BOOT_METADATA = __ARRAYVIEW_BOOT_METADATA__;
|
|
5965
|
+
// Launches without ?av_name= still have the name in the page itself.
|
|
5966
|
+
(function () {
|
|
5967
|
+
try {
|
|
5968
|
+
const el = document.querySelector('#loading-overlay .av-load-name');
|
|
5969
|
+
const name = BOOT_METADATA && BOOT_METADATA.name;
|
|
5970
|
+
if (el && !el.textContent && name && name !== 'welcome') {
|
|
5971
|
+
el.textContent = name;
|
|
5972
|
+
el.classList.add('visible');
|
|
5973
|
+
}
|
|
5974
|
+
} catch (_) {}
|
|
5975
|
+
})();
|
|
5923
5976
|
/* __AV_STATIC_SCRIPT_BELOW__ — nothing after this line is substituted at serve time. */
|
|
5924
5977
|
const urlParams = new URLSearchParams(_arrayViewLaunchSearch);
|
|
5925
5978
|
const _isIntegratedBrowserViewer = (
|
|
@@ -7028,6 +7081,7 @@
|
|
|
7028
7081
|
[arrayName, info, cbWrap, helpHint].forEach(el => {
|
|
7029
7082
|
if (!el) return;
|
|
7030
7083
|
el.style.removeProperty('opacity');
|
|
7084
|
+
el.style.removeProperty('transition');
|
|
7031
7085
|
el.style.removeProperty('--fly-y');
|
|
7032
7086
|
el.style.removeProperty('scale');
|
|
7033
7087
|
el.style.removeProperty('transformOrigin');
|
|
@@ -7086,15 +7140,11 @@
|
|
|
7086
7140
|
const helpHint = document.getElementById('help-hint');
|
|
7087
7141
|
const cbVisible = cbWrap && getComputedStyle(cbWrap).display !== 'none';
|
|
7088
7142
|
|
|
7089
|
-
// Seize opacity immediately
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
if (arrayName) gsap.set(arrayName, { '--fly-y': '-8px' });
|
|
7095
|
-
if (info) gsap.set(info, { '--fly-y': '-8px' });
|
|
7096
|
-
if (cbVisible) gsap.set(cbWrap, { '--fly-y': '12px' });
|
|
7097
|
-
if (helpHint) gsap.set(helpHint, { scale: 0.55, transformOrigin: 'center center' });
|
|
7143
|
+
// Seize opacity immediately, and switch off their CSS opacity
|
|
7144
|
+
// transition: left on, it re-eases every step of the fade below
|
|
7145
|
+
// and stretches it to twice its length.
|
|
7146
|
+
gsap.set([canvasWrap, arrayName, info, helpHint], { opacity: 0, transition: 'none' });
|
|
7147
|
+
if (cbVisible) gsap.set(cbWrap, { opacity: 0, transition: 'none' });
|
|
7098
7148
|
|
|
7099
7149
|
_startupAnimTl = gsap.timeline({ onComplete: () => {
|
|
7100
7150
|
clearTimeout(_safety);
|
|
@@ -7103,47 +7153,28 @@
|
|
|
7103
7153
|
if (ol) ol.style.display = 'none';
|
|
7104
7154
|
}});
|
|
7105
7155
|
|
|
7106
|
-
//
|
|
7156
|
+
// The logo stops pulsing, and the loading screen hands over to the
|
|
7157
|
+
// viewer in one short cross-fade: picture and its labels arrive
|
|
7158
|
+
// together. (They used to arrive in two waves, the labels flying in
|
|
7159
|
+
// after the picture, which read as yet another loading step.)
|
|
7107
7160
|
_startupAnimTl.call(() => {
|
|
7108
7161
|
if (loadLogo) loadLogo.classList.remove('av-logo-loading');
|
|
7109
7162
|
}, null, 0);
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
if (
|
|
7113
|
-
_startupAnimTl.to(
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
_startupAnimTl.to(loadName, {
|
|
7119
|
-
opacity: 0, duration: 0.14, ease: 'power2.in'
|
|
7120
|
-
}, 0.05);
|
|
7121
|
-
}
|
|
7122
|
-
_startupAnimTl.to(canvasWrap, {
|
|
7123
|
-
opacity: 1, duration: 0.22, ease: 'power2.out'
|
|
7124
|
-
}, 0.08);
|
|
7163
|
+
const loadProgress = ol ? ol.querySelector('.av-load-progress') : null;
|
|
7164
|
+
const leaving = [loadLogo, loadName, loadProgress].filter(Boolean);
|
|
7165
|
+
if (leaving.length) {
|
|
7166
|
+
_startupAnimTl.to(leaving, { opacity: 0, duration: 0.16, ease: 'power2.in' }, 0);
|
|
7167
|
+
}
|
|
7168
|
+
const arriving = [canvasWrap, arrayName, info, helpHint].filter(Boolean);
|
|
7169
|
+
if (cbVisible) arriving.push(cbWrap);
|
|
7170
|
+
_startupAnimTl.to(arriving, { opacity: 1, duration: 0.2, ease: 'power2.out' }, 0.06);
|
|
7125
7171
|
_startupAnimTl.call(() => {
|
|
7126
7172
|
document.body.classList.remove('av-loading');
|
|
7127
7173
|
_reconcileUI();
|
|
7128
|
-
}, null, 0.
|
|
7174
|
+
}, null, 0.06);
|
|
7129
7175
|
_startupAnimTl.call(() => {
|
|
7130
7176
|
if (ol) ol.style.display = 'none';
|
|
7131
|
-
}, null, 0.
|
|
7132
|
-
|
|
7133
|
-
// ── Act 3: Chrome assembles around canvas ──────────────────────
|
|
7134
|
-
_startupAnimTl
|
|
7135
|
-
.to(arrayName, { opacity: 1, '--fly-y': '0px', duration: 0.26, ease: 'power3.out' }, 0.32)
|
|
7136
|
-
.to(info, { opacity: 1, '--fly-y': '0px', duration: 0.26, ease: 'power3.out' }, 0.38);
|
|
7137
|
-
if (cbVisible) {
|
|
7138
|
-
_startupAnimTl.to(cbWrap, {
|
|
7139
|
-
opacity: 1, '--fly-y': '0px', duration: 0.24, ease: 'power3.out'
|
|
7140
|
-
}, 0.48);
|
|
7141
|
-
}
|
|
7142
|
-
if (helpHint) {
|
|
7143
|
-
_startupAnimTl.to(helpHint, {
|
|
7144
|
-
opacity: 1, scale: 1, duration: 0.22, ease: 'back.out(1.7)'
|
|
7145
|
-
}, 0.58);
|
|
7146
|
-
}
|
|
7177
|
+
}, null, 0.26);
|
|
7147
7178
|
}
|
|
7148
7179
|
let _immersiveDriveTween = null; // tween that scrubs immersiveTl.progress()
|
|
7149
7180
|
let _immTargetZoom = 1.0; // cached zoom target at start of transition
|
|
@@ -15925,7 +15956,12 @@
|
|
|
15925
15956
|
} else if (msg.type === 'render_timing' && _PERF_ENABLED) {
|
|
15926
15957
|
_perfHandleTimingMessage(event.data, 'main');
|
|
15927
15958
|
} else if (msg.type === 'render_progress') {
|
|
15928
|
-
|
|
15959
|
+
// Only while a frame is actually awaited: a late
|
|
15960
|
+
// report for a frame already drawn would show a
|
|
15961
|
+
// bar that nothing then clears.
|
|
15962
|
+
if (isRendering || !_reportedFirstFrame) {
|
|
15963
|
+
_showRenderProgress(msg.done, msg.total, msg.label);
|
|
15964
|
+
}
|
|
15929
15965
|
} else if (msg.type === 'warm_path') {
|
|
15930
15966
|
_warmClientPath(msg.connections);
|
|
15931
15967
|
} else if (msg.type === 'render_error') {
|
|
@@ -17037,6 +17073,8 @@
|
|
|
17037
17073
|
}
|
|
17038
17074
|
function _showRenderProgress(done, total, label) {
|
|
17039
17075
|
if (!total || done > total) return;
|
|
17076
|
+
const startupBar = document.querySelector('#loading-overlay .av-load-progress .bar');
|
|
17077
|
+
if (startupBar) startupBar.style.width = ((done / total) * 100).toFixed(1) + '%';
|
|
17040
17078
|
const bar = document.querySelector('#render-progress .bar');
|
|
17041
17079
|
const text = document.getElementById('render-progress-text');
|
|
17042
17080
|
if (!bar || !text) return;
|
|
@@ -41906,22 +41944,29 @@
|
|
|
41906
41944
|
_reconcileMainLoupeState();
|
|
41907
41945
|
_reconcileInfoCursor();
|
|
41908
41946
|
_reconcileModePicker();
|
|
41947
|
+
// Until the first frame the startup screen owns every loading
|
|
41948
|
+
// signal; its own line stands in for the hairline.
|
|
41949
|
+
const startupScreen = document.body.classList.contains('av-loading');
|
|
41950
|
+
const startupProgress = document.querySelector('#loading-overlay .av-load-progress');
|
|
41951
|
+
if (startupProgress) {
|
|
41952
|
+
startupProgress.classList.toggle('visible', startupScreen && _renderProgressVisible);
|
|
41953
|
+
}
|
|
41909
41954
|
const busyOverlay = document.getElementById('busy-overlay');
|
|
41910
41955
|
if (busyOverlay) {
|
|
41911
|
-
const busy =
|
|
41956
|
+
const busy = !startupScreen
|
|
41957
|
+
&& (_patientLoadingVisible || _slowRenderBusyVisible || _smPageBusyVisible);
|
|
41912
41958
|
busyOverlay.classList.toggle('visible', busy);
|
|
41913
41959
|
busyOverlay.setAttribute('aria-hidden', busy ? 'false' : 'true');
|
|
41914
41960
|
}
|
|
41961
|
+
const hairline = _renderProgressVisible && !startupScreen;
|
|
41915
41962
|
const renderProgress = document.getElementById('render-progress');
|
|
41916
41963
|
if (renderProgress) {
|
|
41917
|
-
renderProgress.classList.toggle('visible',
|
|
41918
|
-
renderProgress.setAttribute(
|
|
41919
|
-
'aria-hidden', _renderProgressVisible ? 'false' : 'true'
|
|
41920
|
-
);
|
|
41964
|
+
renderProgress.classList.toggle('visible', hairline);
|
|
41965
|
+
renderProgress.setAttribute('aria-hidden', hairline ? 'false' : 'true');
|
|
41921
41966
|
}
|
|
41922
41967
|
const renderProgressText = document.getElementById('render-progress-text');
|
|
41923
41968
|
if (renderProgressText) {
|
|
41924
|
-
renderProgressText.classList.toggle('visible',
|
|
41969
|
+
renderProgressText.classList.toggle('visible', hairline);
|
|
41925
41970
|
}
|
|
41926
41971
|
const dropMenu = document.getElementById('drop-action-menu');
|
|
41927
41972
|
if (dropMenu) {
|
|
@@ -58,7 +58,7 @@ _VSCODE_EXT_FRESH_INSTALL = False # True if we just installed it this session
|
|
|
58
58
|
_VSCODE_EXT_RELOAD_REQUIRED = False # installed files are newer than the live host
|
|
59
59
|
_VSCODE_EXT_INSTALL_FAILED = False # automatic install could not complete safely
|
|
60
60
|
_VSCODE_EXT_NO_LIVE_WINDOW = False # no live host claims this terminal's window id
|
|
61
|
-
_VSCODE_EXT_VERSION = "0.15.
|
|
61
|
+
_VSCODE_EXT_VERSION = "0.15.70" # current bundled extension version
|
|
62
62
|
_VSCODE_CONFIGURED_PORTS: set[int] = set()
|
|
63
63
|
# Version-skew notices already printed, so a long-lived process (Jupyter, a
|
|
64
64
|
# script opening several arrays) states the mismatch once instead of per call.
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|