arrayview 0.29.0__py3-none-any.whl → 0.29.2__py3-none-any.whl
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/_launcher.py +6 -4
- arrayview/_viewer.html +24 -88
- arrayview/_vscode_extension.py +1 -1
- arrayview/_vscode_signal.py +124 -93
- arrayview/arrayview-opener.vsix +0 -0
- {arrayview-0.29.0.dist-info → arrayview-0.29.2.dist-info}/METADATA +1 -1
- {arrayview-0.29.0.dist-info → arrayview-0.29.2.dist-info}/RECORD +10 -10
- {arrayview-0.29.0.dist-info → arrayview-0.29.2.dist-info}/WHEEL +0 -0
- {arrayview-0.29.0.dist-info → arrayview-0.29.2.dist-info}/entry_points.txt +0 -0
- {arrayview-0.29.0.dist-info → arrayview-0.29.2.dist-info}/licenses/LICENSE +0 -0
arrayview/_launcher.py
CHANGED
|
@@ -677,9 +677,7 @@ def _activate_early_cli_native_shell(
|
|
|
677
677
|
notified = bool(notify_result.get("notified"))
|
|
678
678
|
except Exception:
|
|
679
679
|
notified = False
|
|
680
|
-
if notified and
|
|
681
|
-
port, viewer_before=viewer_before
|
|
682
|
-
):
|
|
680
|
+
if notified and _wait_for_viewer_connection(port, before=viewer_before):
|
|
683
681
|
return True
|
|
684
682
|
_vprint(
|
|
685
683
|
"[ArrayView] Native window did not connect to the backend; falling back to browser",
|
|
@@ -1216,8 +1214,12 @@ def _handle_cli_spawned_daemon(
|
|
|
1216
1214
|
and not is_remote
|
|
1217
1215
|
and not overlay_files
|
|
1218
1216
|
and not compare_files
|
|
1217
|
+
and not sys.platform.startswith("linux")
|
|
1219
1218
|
):
|
|
1220
|
-
|
|
1219
|
+
# Linux QtWebEngine denies sessionStorage to the viewer iframe when its
|
|
1220
|
+
# parent shell was created from inline data. Let Linux open the normal
|
|
1221
|
+
# HTTP shell after the daemon is listening instead.
|
|
1222
|
+
url_shell_early = f"http://{_LOOPBACK_HOST}:{port}/shell"
|
|
1221
1223
|
early_native_shell_opened, early_native_shell_proc = _open_webview_cli_tracked(
|
|
1222
1224
|
url_shell_early, 1400, 900, shell_port=port
|
|
1223
1225
|
)
|
arrayview/_viewer.html
CHANGED
|
@@ -439,7 +439,6 @@
|
|
|
439
439
|
#mv-cb { border: none; }
|
|
440
440
|
.highlight { color: var(--highlight); font-weight: bold; }
|
|
441
441
|
.dim-size { opacity: 0.28; font-size: 0.68em; font-weight: normal; }
|
|
442
|
-
.dim-hover-idx { font-size: 0.68em; font-weight: normal; color: var(--dim-hover, #777); opacity: 1; }
|
|
443
442
|
.active-dim { font-weight: bold; }
|
|
444
443
|
.playing-dim { font-weight: bold; }
|
|
445
444
|
.vfield-t-dim { color: var(--vfield-t-dim); }
|
|
@@ -3338,6 +3337,7 @@
|
|
|
3338
3337
|
#mv-panes.mv-crosshair-active .mv-pane::after {
|
|
3339
3338
|
content: ''; position: absolute; inset: 0; z-index: 4;
|
|
3340
3339
|
box-shadow: inset 0 0 0 var(--mv-plane-border-width, 1px) var(--mv-plane-border, var(--active-dim));
|
|
3340
|
+
opacity: 0.45;
|
|
3341
3341
|
border-radius: inherit; box-sizing: border-box;
|
|
3342
3342
|
pointer-events: none;
|
|
3343
3343
|
}
|
|
@@ -13885,7 +13885,6 @@
|
|
|
13885
13885
|
if (!['circle', 'rect', 'freehand', 'floodfill'].includes(_roiShape)) _roiShape = 'circle';
|
|
13886
13886
|
_roiVisible = true;
|
|
13887
13887
|
hideAllPixelInfo();
|
|
13888
|
-
_resetDimbarHover();
|
|
13889
13888
|
canvas.style.cursor = 'crosshair';
|
|
13890
13889
|
if (qmriActive) qmriViews.forEach(v => { v.canvas.style.cursor = 'crosshair'; });
|
|
13891
13890
|
if (multiViewActive) mvViews.forEach(v => { v.canvas.style.cursor = 'crosshair'; });
|
|
@@ -16481,38 +16480,6 @@
|
|
|
16481
16480
|
_updateMvPaneFill();
|
|
16482
16481
|
}
|
|
16483
16482
|
|
|
16484
|
-
function _dimHoverPadded(val, maxVal) {
|
|
16485
|
-
const maxStr = String(maxVal);
|
|
16486
|
-
const valStr = String(val);
|
|
16487
|
-
const padLen = maxStr.length - valStr.length;
|
|
16488
|
-
if (padLen <= 0) return '/' + valStr;
|
|
16489
|
-
return '/<span style="opacity:0.28">' + '0'.repeat(padLen) + '</span>' + valStr;
|
|
16490
|
-
}
|
|
16491
|
-
function _updateDimbarHover(px, py, xLabel, yLabel, pz) {
|
|
16492
|
-
// xLabel/yLabel: 'x','y','z' — which dimbar label to update (default x,y)
|
|
16493
|
-
// pz: optional z index (for mosaic mode)
|
|
16494
|
-
const xl = xLabel || 'x', yl = yLabel || 'y';
|
|
16495
|
-
const xEl = document.getElementById('dim-' + xl + '-size');
|
|
16496
|
-
const yEl = document.getElementById('dim-' + yl + '-size');
|
|
16497
|
-
const zEl = document.getElementById('dim-z-size');
|
|
16498
|
-
// Resolve label to shape dim index for padding width
|
|
16499
|
-
const labelToDim = (multiViewActive || compareMvActive)
|
|
16500
|
-
? { x: (multiViewActive ? mvDims : compareMvDims)[0], y: (multiViewActive ? mvDims : compareMvDims)[1], z: (multiViewActive ? mvDims : compareMvDims)[2] }
|
|
16501
|
-
: { x: dim_x, y: dim_y, z: dim_z };
|
|
16502
|
-
const xMax = labelToDim[xl] >= 0 ? shape[labelToDim[xl]] : 1;
|
|
16503
|
-
const yMax = labelToDim[yl] >= 0 ? shape[labelToDim[yl]] : 1;
|
|
16504
|
-
if (xEl) { xEl.innerHTML = _dimHoverPadded(px, xMax); xEl.className = 'dim-hover-idx'; }
|
|
16505
|
-
if (yEl) { yEl.innerHTML = _dimHoverPadded(py, yMax); yEl.className = 'dim-hover-idx'; }
|
|
16506
|
-
if (zEl && pz != null) {
|
|
16507
|
-
const zMax = dim_z >= 0 ? shape[dim_z] : 1;
|
|
16508
|
-
zEl.innerHTML = _dimHoverPadded(pz + 1, zMax); zEl.className = 'dim-hover-idx';
|
|
16509
|
-
}
|
|
16510
|
-
}
|
|
16511
|
-
function _resetDimbarHover() {
|
|
16512
|
-
// Re-render dimbar to restore correct max sizes for all modes
|
|
16513
|
-
renderInfo();
|
|
16514
|
-
}
|
|
16515
|
-
|
|
16516
16483
|
// Gnomon visibility: shown while a dim-bar token is hovered, or flashed
|
|
16517
16484
|
// briefly when activeDim changes via h/l/←/→. Hidden otherwise.
|
|
16518
16485
|
// In oblique mode the x/y labels no longer name axis-aligned directions,
|
|
@@ -17711,13 +17678,18 @@
|
|
|
17711
17678
|
|
|
17712
17679
|
function saveState() {
|
|
17713
17680
|
if (!sid) return;
|
|
17714
|
-
|
|
17681
|
+
try {
|
|
17682
|
+
sessionStorage.setItem('av_' + sid, JSON.stringify(collectStateSnapshot()));
|
|
17683
|
+
} catch (e) {
|
|
17684
|
+
// Some embedded webviews deny storage access. Viewing must keep
|
|
17685
|
+
// working even when this optional convenience is unavailable.
|
|
17686
|
+
}
|
|
17715
17687
|
}
|
|
17716
17688
|
|
|
17717
17689
|
function restoreStateFromSessionStorage() {
|
|
17718
|
-
const raw = sessionStorage.getItem('av_' + sid);
|
|
17719
|
-
if (!raw) return false;
|
|
17720
17690
|
try {
|
|
17691
|
+
const raw = sessionStorage.getItem('av_' + sid);
|
|
17692
|
+
if (!raw) return false;
|
|
17721
17693
|
return applyStateSnapshot(JSON.parse(raw));
|
|
17722
17694
|
} catch (e) {
|
|
17723
17695
|
return false;
|
|
@@ -20117,7 +20089,6 @@
|
|
|
20117
20089
|
showPixelInfo(document.getElementById('main-pixel-info'), { valueText: _pixelFmt(d.value, _activeNumericContext()), rawX, rawY }, e.clientX, e.clientY);
|
|
20118
20090
|
}).catch(() => {});
|
|
20119
20091
|
}
|
|
20120
|
-
_updateDimbarHover(localX, localY, null, null, zIdx);
|
|
20121
20092
|
return;
|
|
20122
20093
|
}
|
|
20123
20094
|
|
|
@@ -20131,11 +20102,6 @@
|
|
|
20131
20102
|
imgW: canvas.width,
|
|
20132
20103
|
imgH: canvas.height,
|
|
20133
20104
|
displayEl: document.getElementById('main-pixel-info'),
|
|
20134
|
-
onResult: (d, px, py) => {
|
|
20135
|
-
if (_pixelInfoActive() && !multiViewActive && !compareActive) {
|
|
20136
|
-
_updateDimbarHover(px, py);
|
|
20137
|
-
}
|
|
20138
|
-
},
|
|
20139
20105
|
});
|
|
20140
20106
|
});
|
|
20141
20107
|
canvas.addEventListener('mouseleave', () => {
|
|
@@ -20154,8 +20120,7 @@
|
|
|
20154
20120
|
if (_pixelInfoHeld) {
|
|
20155
20121
|
_pixelInfoHeld = false;
|
|
20156
20122
|
if (!_pixelInfoVisible) {
|
|
20157
|
-
|
|
20158
|
-
if (piEl) piEl.style.display = 'none';
|
|
20123
|
+
hideAllPixelInfo();
|
|
20159
20124
|
}
|
|
20160
20125
|
}
|
|
20161
20126
|
});
|
|
@@ -20924,7 +20889,6 @@
|
|
|
20924
20889
|
if (!_pixelInfoVisible) {
|
|
20925
20890
|
_clearHoverPins();
|
|
20926
20891
|
hideAllPixelInfo();
|
|
20927
|
-
_resetDimbarHover();
|
|
20928
20892
|
const axEl = document.getElementById('main-axes-svg');
|
|
20929
20893
|
if (axEl) {
|
|
20930
20894
|
axEl.querySelector('.axes-lbl-x').textContent = 'x';
|
|
@@ -26047,21 +26011,6 @@
|
|
|
26047
26011
|
imgH: view.lastH,
|
|
26048
26012
|
view,
|
|
26049
26013
|
displayEl: view.pixelInfoEl,
|
|
26050
|
-
onResult: (d, px, py) => {
|
|
26051
|
-
if (_pixelInfoActive()) {
|
|
26052
|
-
// Map pane dims to dimbar x/y/z labels
|
|
26053
|
-
const labels = ['x','y','z'];
|
|
26054
|
-
const activeDims = multiViewActive ? mvDims : (compareMvActive ? compareMvDims : []);
|
|
26055
|
-
const posX = activeDims.indexOf(view.dimX);
|
|
26056
|
-
const posY = activeDims.indexOf(view.dimY);
|
|
26057
|
-
const lblX = labels[posX] || 'x';
|
|
26058
|
-
const lblY = labels[posY] || 'y';
|
|
26059
|
-
const elX = document.getElementById('dim-' + lblX + '-size');
|
|
26060
|
-
const elY = document.getElementById('dim-' + lblY + '-size');
|
|
26061
|
-
if (elX) { elX.textContent = '/' + px; elX.className = 'dim-hover-idx'; }
|
|
26062
|
-
if (elY) { elY.textContent = '/' + py; elY.className = 'dim-hover-idx'; }
|
|
26063
|
-
}
|
|
26064
|
-
},
|
|
26065
26014
|
});
|
|
26066
26015
|
});
|
|
26067
26016
|
cv.addEventListener('mouseleave', () => {
|
|
@@ -26072,7 +26021,6 @@
|
|
|
26072
26021
|
if (tq) tq.classList.remove('visible');
|
|
26073
26022
|
_roiSuppressPixelInfo = false;
|
|
26074
26023
|
if (!mvDraggingView && view.pixelInfoEl) view.pixelInfoEl.style.display = 'none';
|
|
26075
|
-
if (_pixelInfoActive()) _resetDimbarHover();
|
|
26076
26024
|
});
|
|
26077
26025
|
// Click-to-copy: multiview canvases
|
|
26078
26026
|
let _mvClickDown = null;
|
|
@@ -26935,7 +26883,6 @@
|
|
|
26935
26883
|
view,
|
|
26936
26884
|
onResult: (d, px, py) => {
|
|
26937
26885
|
if (_pixelInfoActive()) {
|
|
26938
|
-
_updateDimbarHover(px, py);
|
|
26939
26886
|
return; // hover mode: only show hovered pane's value
|
|
26940
26887
|
}
|
|
26941
26888
|
// Non-hover: fetch for all other qmri views at same pixel
|
|
@@ -26962,10 +26909,9 @@
|
|
|
26962
26909
|
});
|
|
26963
26910
|
});
|
|
26964
26911
|
cv.addEventListener('mouseleave', () => {
|
|
26965
|
-
|
|
26966
|
-
|
|
26967
|
-
|
|
26968
|
-
});
|
|
26912
|
+
if (_qmriLoupeDrag && _qmriLoupeDrag.canvas === cv) _endQmriLoupe();
|
|
26913
|
+
qmriViews.forEach(v => { if (v.pixelInfoEl) v.pixelInfoEl.style.display = 'none'; });
|
|
26914
|
+
});
|
|
26969
26915
|
// ROI drawing on qMRI canvases
|
|
26970
26916
|
if (rectRoiMode) cv.style.cursor = 'crosshair';
|
|
26971
26917
|
cv.addEventListener('mousemove', (e) => {
|
|
@@ -27029,6 +26975,11 @@
|
|
|
27029
26975
|
}
|
|
27030
26976
|
});
|
|
27031
26977
|
cv.addEventListener('mousedown', (e) => {
|
|
26978
|
+
if (!rectRoiMode && e.button === 0 && !e.shiftKey && !e.metaKey && !e.ctrlKey && !e.altKey
|
|
26979
|
+
&& !_cropActive && !_segMode && !_rulerMode) {
|
|
26980
|
+
_pixelInfoHeld = true;
|
|
26981
|
+
cv.dispatchEvent(new MouseEvent('mousemove', { clientX: e.clientX, clientY: e.clientY, bubbles: true }));
|
|
26982
|
+
}
|
|
27032
26983
|
if (!rectRoiMode || e.button !== 0 || e.shiftKey || e.metaKey) return;
|
|
27033
26984
|
e.preventDefault(); sink.focus();
|
|
27034
26985
|
const rect = cv.getBoundingClientRect();
|
|
@@ -27449,15 +27400,17 @@
|
|
|
27449
27400
|
view,
|
|
27450
27401
|
displayEl: view.pixelInfoEl,
|
|
27451
27402
|
qmriRole: view.qmriRole,
|
|
27452
|
-
onResult: (d, px, py) => {
|
|
27453
|
-
if (_pixelInfoActive()) _updateDimbarHover(px, py);
|
|
27454
|
-
},
|
|
27455
27403
|
});
|
|
27456
27404
|
});
|
|
27405
|
+
cv.addEventListener('mousedown', (e) => {
|
|
27406
|
+
if (e.button !== 0 || e.shiftKey || e.metaKey || e.ctrlKey || e.altKey
|
|
27407
|
+
|| _cropActive || _segMode || _rulerMode) return;
|
|
27408
|
+
_pixelInfoHeld = true;
|
|
27409
|
+
cv.dispatchEvent(new MouseEvent('mousemove', { clientX: e.clientX, clientY: e.clientY, bubbles: true }));
|
|
27410
|
+
});
|
|
27457
27411
|
cv.addEventListener('mouseleave', () => {
|
|
27458
27412
|
if (_qmriLoupeDrag && _qmriLoupeDrag.canvas === cv) _endQmriLoupe();
|
|
27459
27413
|
compareQmriViews.forEach(v => { if (v.pixelInfoEl) v.pixelInfoEl.style.display = 'none'; });
|
|
27460
|
-
if (_pixelInfoActive()) _resetDimbarHover();
|
|
27461
27414
|
});
|
|
27462
27415
|
if (!httpTransport) {
|
|
27463
27416
|
view.ws = createTransport(sidVal,
|
|
@@ -27936,20 +27889,6 @@
|
|
|
27936
27889
|
imgH: view.lastH,
|
|
27937
27890
|
view,
|
|
27938
27891
|
displayEl: view.pixelInfoEl,
|
|
27939
|
-
onResult: (d, px, py) => {
|
|
27940
|
-
if (_pixelInfoActive()) {
|
|
27941
|
-
const labels = ['x','y','z'];
|
|
27942
|
-
const activeDims = compareMvDims || [];
|
|
27943
|
-
const posX = activeDims.indexOf(view.dimX);
|
|
27944
|
-
const posY = activeDims.indexOf(view.dimY);
|
|
27945
|
-
const lblX = labels[posX] || 'x';
|
|
27946
|
-
const lblY = labels[posY] || 'y';
|
|
27947
|
-
const elX = document.getElementById('dim-' + lblX + '-size');
|
|
27948
|
-
const elY = document.getElementById('dim-' + lblY + '-size');
|
|
27949
|
-
if (elX) { elX.textContent = '/' + px; elX.className = 'dim-hover-idx'; }
|
|
27950
|
-
if (elY) { elY.textContent = '/' + py; elY.className = 'dim-hover-idx'; }
|
|
27951
|
-
}
|
|
27952
|
-
},
|
|
27953
27892
|
});
|
|
27954
27893
|
});
|
|
27955
27894
|
cv.addEventListener('mouseleave', () => {
|
|
@@ -27957,7 +27896,6 @@
|
|
|
27957
27896
|
if (!cmvDraggingView) {
|
|
27958
27897
|
compareMvViews.forEach(v => { if (v.pixelInfoEl) v.pixelInfoEl.style.display = 'none'; });
|
|
27959
27898
|
}
|
|
27960
|
-
if (_pixelInfoActive()) _resetDimbarHover();
|
|
27961
27899
|
});
|
|
27962
27900
|
// Click-to-copy: compare-multiview canvases
|
|
27963
27901
|
let _cmvClickDown = null;
|
|
@@ -30116,7 +30054,6 @@
|
|
|
30116
30054
|
displayEl: piEl,
|
|
30117
30055
|
onResult: (d, px, py) => {
|
|
30118
30056
|
if (!_pixelInfoActive()) return;
|
|
30119
|
-
_updateDimbarHover(px, py);
|
|
30120
30057
|
// Show value on other compare panes at the same pixel
|
|
30121
30058
|
const ver = indices.join(',') + ':' + dim_x + ':' + dim_y + ':' + complexMode + ':' + logScale;
|
|
30122
30059
|
compareCanvases.forEach((ocv, j) => {
|
|
@@ -30145,7 +30082,6 @@
|
|
|
30145
30082
|
cv.addEventListener('mouseleave', () => {
|
|
30146
30083
|
_comparePiEls.forEach(p => { if (p) p.style.display = 'none'; });
|
|
30147
30084
|
hideCompareCrosshairs();
|
|
30148
|
-
if (_pixelInfoActive()) _resetDimbarHover();
|
|
30149
30085
|
});
|
|
30150
30086
|
});
|
|
30151
30087
|
// Click-to-copy: compare canvases
|
arrayview/_vscode_extension.py
CHANGED
|
@@ -44,7 +44,7 @@ def _vscode_app_bundle() -> str | None:
|
|
|
44
44
|
|
|
45
45
|
_VSCODE_EXT_INSTALLED = False # cached so we only check once per process
|
|
46
46
|
_VSCODE_EXT_FRESH_INSTALL = False # True if we just installed it this session
|
|
47
|
-
_VSCODE_EXT_VERSION = "0.14.
|
|
47
|
+
_VSCODE_EXT_VERSION = "0.14.35" # current bundled extension version
|
|
48
48
|
|
|
49
49
|
def _bundled_vscode_vsix_version(vsix_path: str) -> str | None:
|
|
50
50
|
"""Return the bundled opener extension version recorded inside the VSIX."""
|
arrayview/_vscode_signal.py
CHANGED
|
@@ -163,7 +163,11 @@ def _find_arrayview_window_id() -> str | None:
|
|
|
163
163
|
pass
|
|
164
164
|
|
|
165
165
|
# tmux: process tree is detached from VS Code terminal; check all session clients.
|
|
166
|
+
# Only accept the fallback if every attached client for this tmux session
|
|
167
|
+
# reports the same window ID. Returning the first client is unsafe when the
|
|
168
|
+
# same session is attached from multiple VS Code windows.
|
|
166
169
|
if os.environ.get("TERM_PROGRAM") == "tmux":
|
|
170
|
+
tmux_window_ids: set[str] = set()
|
|
167
171
|
try:
|
|
168
172
|
r_sid = subprocess.run(
|
|
169
173
|
["tmux", "display-message", "-p", "#{session_id}"],
|
|
@@ -187,7 +191,9 @@ def _find_arrayview_window_id() -> str | None:
|
|
|
187
191
|
with open(f"/proc/{client_pid}/environ", "rb") as fh:
|
|
188
192
|
for entry in fh.read().split(b"\0"):
|
|
189
193
|
if entry.startswith(b"ARRAYVIEW_WINDOW_ID="):
|
|
190
|
-
|
|
194
|
+
val = entry[len(b"ARRAYVIEW_WINDOW_ID="):].decode()
|
|
195
|
+
if val:
|
|
196
|
+
tmux_window_ids.add(val)
|
|
191
197
|
except Exception:
|
|
192
198
|
pass
|
|
193
199
|
# macOS: ps ewwww
|
|
@@ -198,11 +204,21 @@ def _find_arrayview_window_id() -> str | None:
|
|
|
198
204
|
)
|
|
199
205
|
for token in r.stdout.split():
|
|
200
206
|
if token.startswith("ARRAYVIEW_WINDOW_ID="):
|
|
201
|
-
|
|
207
|
+
val = token[len("ARRAYVIEW_WINDOW_ID="):]
|
|
208
|
+
if val:
|
|
209
|
+
tmux_window_ids.add(val)
|
|
202
210
|
except Exception:
|
|
203
211
|
pass
|
|
204
212
|
except Exception:
|
|
205
213
|
pass
|
|
214
|
+
if len(tmux_window_ids) == 1:
|
|
215
|
+
return next(iter(tmux_window_ids))
|
|
216
|
+
if len(tmux_window_ids) > 1:
|
|
217
|
+
_vprint(
|
|
218
|
+
"[ArrayView] signal: tmux clients report multiple "
|
|
219
|
+
f"ARRAYVIEW_WINDOW_ID values: {sorted(tmux_window_ids)}",
|
|
220
|
+
flush=True,
|
|
221
|
+
)
|
|
206
222
|
|
|
207
223
|
return None
|
|
208
224
|
|
|
@@ -307,6 +323,38 @@ def _write_vscode_signal(payload: dict, delay: float = 0.0, skip_compat: bool =
|
|
|
307
323
|
data.setdefault("maxAgeMs", _VSCODE_SIGNAL_MAX_AGE_MS)
|
|
308
324
|
data.setdefault("requestId", uuid.uuid4().hex)
|
|
309
325
|
|
|
326
|
+
def _focused_window_fallback() -> tuple[str, ...]:
|
|
327
|
+
"""Let the focused extension window claim an untargeted request."""
|
|
328
|
+
data["broadcast"] = True
|
|
329
|
+
return (
|
|
330
|
+
(_VSCODE_SIGNAL_FILENAME,)
|
|
331
|
+
if skip_compat
|
|
332
|
+
else (_VSCODE_SIGNAL_FILENAME, *_VSCODE_COMPAT_SIGNAL_FILENAMES)
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
def _registrations_are_remote(window_files: list[str]) -> bool:
|
|
336
|
+
"""Return whether every registration belongs to a remote extension host."""
|
|
337
|
+
if not window_files:
|
|
338
|
+
return False
|
|
339
|
+
for window_file in window_files:
|
|
340
|
+
try:
|
|
341
|
+
with open(os.path.join(signal_dir, window_file)) as registration:
|
|
342
|
+
registration_data = json.load(registration)
|
|
343
|
+
remote_name = registration_data.get("remoteName")
|
|
344
|
+
if remote_name:
|
|
345
|
+
continue
|
|
346
|
+
pid = int(registration_data["pid"])
|
|
347
|
+
with open(f"/proc/{pid}/cmdline", "rb") as command_file:
|
|
348
|
+
command = command_file.read().replace(b"\0", b" ").decode()
|
|
349
|
+
if (
|
|
350
|
+
"/.vscode/cli/servers/" not in command
|
|
351
|
+
and "/.vscode-server/" not in command
|
|
352
|
+
):
|
|
353
|
+
return False
|
|
354
|
+
except Exception:
|
|
355
|
+
return False
|
|
356
|
+
return True
|
|
357
|
+
|
|
310
358
|
# --- Primary: ARRAYVIEW_WINDOW_ID (all platforms) ---
|
|
311
359
|
# The extension injects this env var into every terminal via
|
|
312
360
|
# EnvironmentVariableCollection. It uniquely identifies the VS Code
|
|
@@ -323,53 +371,8 @@ def _write_vscode_signal(payload: dict, delay: float = 0.0, skip_compat: bool =
|
|
|
323
371
|
except Exception:
|
|
324
372
|
uses_pid = env_wid.isdigit()
|
|
325
373
|
|
|
326
|
-
#
|
|
327
|
-
#
|
|
328
|
-
# still be alive but is no longer connected to a VS Code client.
|
|
329
|
-
# Detect this by looking for a newer registration from the same
|
|
330
|
-
# server (same first ppid — the tunnel/server parent process).
|
|
331
|
-
# Only needed for PID-based IDs (fallbackId=True); hookTag IDs
|
|
332
|
-
# are unique per IPC socket and never go stale across restarts.
|
|
333
|
-
#
|
|
334
|
-
# Skip on macOS: all extension hosts are direct children of the
|
|
335
|
-
# same main Electron process (ppids[0] == Electron PID for every
|
|
336
|
-
# window), so the "same ppids[0]" heuristic matches ALL concurrent
|
|
337
|
-
# windows and incorrectly redirects signals to the wrong window.
|
|
338
|
-
# The stable window ID feature (v0.14.0+) already handles restart
|
|
339
|
-
# continuity on macOS via EnvironmentVariableCollection, making
|
|
340
|
-
# this stale-redirect unnecessary there.
|
|
341
|
-
if uses_pid and sys.platform != "darwin" and not _is_vscode_remote():
|
|
342
|
-
_env_ts = _reg_data.get("ts", 0)
|
|
343
|
-
_env_ppids = _reg_data.get("ppids", [])
|
|
344
|
-
try:
|
|
345
|
-
for _fname in os.listdir(signal_dir):
|
|
346
|
-
if not (_fname.startswith("window-") and _fname.endswith(".json")):
|
|
347
|
-
continue
|
|
348
|
-
_other_wid = _fname[7:-5]
|
|
349
|
-
if _other_wid == env_wid:
|
|
350
|
-
continue
|
|
351
|
-
with open(os.path.join(signal_dir, _fname)) as _f:
|
|
352
|
-
_other = json.load(_f)
|
|
353
|
-
_other_ts = _other.get("ts", 0)
|
|
354
|
-
_other_ppids = _other.get("ppids", [])
|
|
355
|
-
if (
|
|
356
|
-
_other_ts > _env_ts
|
|
357
|
-
and len(_env_ppids) >= 1
|
|
358
|
-
and len(_other_ppids) >= 1
|
|
359
|
-
and _env_ppids[0] == _other_ppids[0]
|
|
360
|
-
):
|
|
361
|
-
_vprint(
|
|
362
|
-
f"[ArrayView] signal: ARRAYVIEW_WINDOW_ID={env_wid} is stale "
|
|
363
|
-
f"(newer registration {_other_wid} found), redirecting",
|
|
364
|
-
flush=True,
|
|
365
|
-
)
|
|
366
|
-
env_wid = _other_wid
|
|
367
|
-
_reg_data = _other
|
|
368
|
-
uses_pid = _other.get("fallbackId", False)
|
|
369
|
-
_env_ts = _other_ts
|
|
370
|
-
except Exception:
|
|
371
|
-
pass
|
|
372
|
-
|
|
374
|
+
# Trust an exact window ID. Redirecting to a newer same-parent
|
|
375
|
+
# registration can silently open the tab in a live sibling window.
|
|
373
376
|
_prefix = "pid" if uses_pid else "ipc"
|
|
374
377
|
filenames = (f"open-request-{_prefix}-{env_wid}.json",)
|
|
375
378
|
targeted_via_env = True
|
|
@@ -410,9 +413,18 @@ def _write_vscode_signal(payload: dict, delay: float = 0.0, skip_compat: bool =
|
|
|
410
413
|
flush=True,
|
|
411
414
|
)
|
|
412
415
|
elif len(_all_windows) > 1:
|
|
413
|
-
if
|
|
416
|
+
if _registrations_are_remote(_all_windows):
|
|
417
|
+
filenames = _focused_window_fallback()
|
|
418
|
+
targeted_via_env = True
|
|
419
|
+
_vprint(
|
|
420
|
+
"[ArrayView] signal: stale ARRAYVIEW_WINDOW_ID with "
|
|
421
|
+
f"{len(_all_windows)} remote windows; "
|
|
422
|
+
"using focused-window fallback",
|
|
423
|
+
flush=True,
|
|
424
|
+
)
|
|
425
|
+
else:
|
|
414
426
|
print(
|
|
415
|
-
"[ArrayView] VS Code
|
|
427
|
+
"[ArrayView] VS Code window is ambiguous: "
|
|
416
428
|
f"ARRAYVIEW_WINDOW_ID={env_wid!r} is not registered "
|
|
417
429
|
f"and {len(_all_windows)} VS Code windows are active. "
|
|
418
430
|
"Open a fresh terminal in the target VS Code window "
|
|
@@ -420,23 +432,6 @@ def _write_vscode_signal(payload: dict, delay: float = 0.0, skip_compat: bool =
|
|
|
420
432
|
flush=True,
|
|
421
433
|
)
|
|
422
434
|
return False
|
|
423
|
-
else:
|
|
424
|
-
_wfiles = []
|
|
425
|
-
for _fn in _all_windows:
|
|
426
|
-
_wid = _fn[7:-5]
|
|
427
|
-
_wfiles.append(
|
|
428
|
-
f"open-request-pid-{_wid}.json"
|
|
429
|
-
if _wid.isdigit()
|
|
430
|
-
else f"open-request-ipc-{_wid}.json"
|
|
431
|
-
)
|
|
432
|
-
data["broadcast"] = True
|
|
433
|
-
filenames = tuple(_wfiles)
|
|
434
|
-
targeted_via_env = True
|
|
435
|
-
_vprint(
|
|
436
|
-
f"[ArrayView] signal: {len(_all_windows)} windows registered, "
|
|
437
|
-
f"broadcasting with focus guard",
|
|
438
|
-
flush=True,
|
|
439
|
-
)
|
|
440
435
|
# else: 0 windows — fall through to subsequent targeting
|
|
441
436
|
|
|
442
437
|
if targeted_via_env:
|
|
@@ -491,10 +486,30 @@ def _write_vscode_signal(payload: dict, delay: float = 0.0, skip_compat: bool =
|
|
|
491
486
|
except Exception:
|
|
492
487
|
pass
|
|
493
488
|
if len(_wfiles) > 1:
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
489
|
+
_window_files = [
|
|
490
|
+
f"window-{name.split('-', 3)[-1][:-5]}.json"
|
|
491
|
+
for name in _wfiles
|
|
492
|
+
]
|
|
493
|
+
if _registrations_are_remote(_window_files):
|
|
494
|
+
filenames = _focused_window_fallback()
|
|
495
|
+
_vprint(
|
|
496
|
+
"[ArrayView] signal: no exact match for local "
|
|
497
|
+
f"terminal with {len(_wfiles)} remote windows; "
|
|
498
|
+
"using focused-window fallback",
|
|
499
|
+
flush=True,
|
|
500
|
+
)
|
|
501
|
+
else:
|
|
502
|
+
print(
|
|
503
|
+
"[ArrayView] VS Code window is ambiguous: "
|
|
504
|
+
"no exact ARRAYVIEW_WINDOW_ID or PID match is available "
|
|
505
|
+
f"and {len(_wfiles)} VS Code windows are active. "
|
|
506
|
+
"Open a fresh terminal in the target VS Code window "
|
|
507
|
+
"and run ArrayView again.",
|
|
508
|
+
flush=True,
|
|
509
|
+
)
|
|
510
|
+
return False
|
|
511
|
+
else:
|
|
512
|
+
filenames = tuple(_wfiles) if _wfiles else (_VSCODE_SIGNAL_FILENAME,)
|
|
498
513
|
else:
|
|
499
514
|
filenames = (
|
|
500
515
|
_VSCODE_SIGNAL_FILENAME,
|
|
@@ -535,13 +550,33 @@ def _write_vscode_signal(payload: dict, delay: float = 0.0, skip_compat: bool =
|
|
|
535
550
|
except Exception:
|
|
536
551
|
pass
|
|
537
552
|
if len(window_files) > 1:
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
553
|
+
_registration_files = [
|
|
554
|
+
fname
|
|
555
|
+
for fname in os.listdir(signal_dir_temp)
|
|
556
|
+
if fname.startswith("window-") and fname.endswith(".json")
|
|
557
|
+
]
|
|
558
|
+
if _registrations_are_remote(_registration_files):
|
|
559
|
+
filenames = _focused_window_fallback()
|
|
560
|
+
_vprint(
|
|
561
|
+
"[ArrayView] signal: local terminal has no exact "
|
|
562
|
+
f"match among {len(window_files)} remote windows; "
|
|
563
|
+
"using focused-window fallback",
|
|
564
|
+
flush=True,
|
|
565
|
+
)
|
|
566
|
+
else:
|
|
567
|
+
print(
|
|
568
|
+
"[ArrayView] VS Code window is ambiguous: "
|
|
569
|
+
"no exact ARRAYVIEW_WINDOW_ID or PID match is available "
|
|
570
|
+
f"and {len(window_files)} VS Code windows are active. "
|
|
571
|
+
"Open a fresh terminal in the target VS Code window "
|
|
572
|
+
"and run ArrayView again.",
|
|
573
|
+
flush=True,
|
|
574
|
+
)
|
|
575
|
+
return False
|
|
576
|
+
else:
|
|
577
|
+
filenames = (
|
|
578
|
+
tuple(window_files) if window_files else (_VSCODE_SIGNAL_FILENAME,)
|
|
579
|
+
)
|
|
545
580
|
else:
|
|
546
581
|
# Not in VS Code terminal: use shared file
|
|
547
582
|
filenames = (
|
|
@@ -604,15 +639,13 @@ def _write_vscode_signal(payload: dict, delay: float = 0.0, skip_compat: bool =
|
|
|
604
639
|
flush=True,
|
|
605
640
|
)
|
|
606
641
|
elif len(_all_windows_r) > 1:
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
f"
|
|
611
|
-
"
|
|
612
|
-
"and run ArrayView again.",
|
|
642
|
+
filenames = _focused_window_fallback()
|
|
643
|
+
_vprint(
|
|
644
|
+
"[ArrayView] signal: stale remote ARRAYVIEW_WINDOW_ID with "
|
|
645
|
+
f"{len(_all_windows_r)} registered windows; "
|
|
646
|
+
"using focused-window fallback",
|
|
613
647
|
flush=True,
|
|
614
648
|
)
|
|
615
|
-
return False
|
|
616
649
|
else:
|
|
617
650
|
env_wid = None # 0 windows: keep existing fallback
|
|
618
651
|
|
|
@@ -639,14 +672,13 @@ def _write_vscode_signal(payload: dict, delay: float = 0.0, skip_compat: bool =
|
|
|
639
672
|
flush=True,
|
|
640
673
|
)
|
|
641
674
|
elif len(_all_windows_r) > 1:
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
"
|
|
645
|
-
"
|
|
646
|
-
"
|
|
675
|
+
filenames = _focused_window_fallback()
|
|
676
|
+
_vprint(
|
|
677
|
+
"[ArrayView] signal: remote window ID unavailable with "
|
|
678
|
+
f"{len(_all_windows_r)} registered windows; "
|
|
679
|
+
"using focused-window fallback",
|
|
647
680
|
flush=True,
|
|
648
681
|
)
|
|
649
|
-
return False
|
|
650
682
|
else:
|
|
651
683
|
_vprint(f"[ArrayView] signal: no registered remote window, using shared fallback", flush=True)
|
|
652
684
|
filenames = (
|
|
@@ -800,4 +832,3 @@ def _cleanup_zombie_registrations(verbose: bool = False) -> int:
|
|
|
800
832
|
pass
|
|
801
833
|
|
|
802
834
|
return removed
|
|
803
|
-
|
arrayview/arrayview-opener.vsix
CHANGED
|
Binary file
|
|
@@ -10,7 +10,7 @@ arrayview/_icon.png,sha256=a8RTAbvweLhh3hzD5eGg76Peq-KSDcS6YrCphRb_HEs,2748
|
|
|
10
10
|
arrayview/_imaging.py,sha256=MQX1uy4m1lofOeoLDhCp6cHO5p_V5fXm4qlSu9Ykn-c,534
|
|
11
11
|
arrayview/_io.py,sha256=JX3_vn0C9JLhc_NhC5QwamsjQWDhbk7080E9JrSfWJk,28531
|
|
12
12
|
arrayview/_launch_plan.py,sha256=hC9SoijWlYRvabwodIPePOnysVOVsnYcvlUYo4farIs,7277
|
|
13
|
-
arrayview/_launcher.py,sha256=
|
|
13
|
+
arrayview/_launcher.py,sha256=NNQshRVqJQNC5we1fU-1uVzCC1oWm4QeR7cndg--HXM,134555
|
|
14
14
|
arrayview/_lifecycle.py,sha256=oFD05gNYAu6OBIUFcKmPfT-_Hp03KaI2oK8komUXtJY,937
|
|
15
15
|
arrayview/_overlays.py,sha256=C-qDZFBOgwJQL84XxFoMAX3qyfNa2EI5N-AlOvXeoTI,1634
|
|
16
16
|
arrayview/_platform.py,sha256=U1XPqEjPeqy3l_xJh9Rmn_3d_6Fh_clJUcA0uR6QBGA,19043
|
|
@@ -33,15 +33,15 @@ arrayview/_shell.html,sha256=xzHIB-kRX12okubZoqdrgsWDY8CCLteg5jtXxDQru8U,11624
|
|
|
33
33
|
arrayview/_synthetic_mri.py,sha256=CNL-2WC835i6WCqkvmWBCO6WQ5w7HE5trNb8OyEfe64,7365
|
|
34
34
|
arrayview/_torch.py,sha256=13sqIMCUu_-b9SQ9KfDCExDz2UMStL87EDVjnbJxAEo,7536
|
|
35
35
|
arrayview/_vectorfield.py,sha256=eDqMCMEGjO8Jdt-6Auh01cz3WD9dbF7xYUWD9xHAf1E,8115
|
|
36
|
-
arrayview/_viewer.html,sha256=
|
|
36
|
+
arrayview/_viewer.html,sha256=6QgxrHju1uv7rk5StEjXUxoX5TCSXEA76OIyx7PM5iI,1757428
|
|
37
37
|
arrayview/_vscode.py,sha256=uY3SYJYjgzuYCbdycmm53ZaPNJIPPtGiIQZKBUobMz4,871
|
|
38
38
|
arrayview/_vscode_browser.py,sha256=InU9EPDALA5ZZsAph6wNi7EwrZgbfn66uZy1iy4yQy8,7932
|
|
39
|
-
arrayview/_vscode_extension.py,sha256=
|
|
40
|
-
arrayview/_vscode_signal.py,sha256=
|
|
41
|
-
arrayview/arrayview-opener.vsix,sha256=
|
|
39
|
+
arrayview/_vscode_extension.py,sha256=B7rTmFqzlxOB6rN0U_gntoIlqvOl7VRQA9SW--PXR_o,13423
|
|
40
|
+
arrayview/_vscode_signal.py,sha256=rJqe5EjtekKA06d4fc1GEoE0kSq_--TsxFMfj-SAW0w,36678
|
|
41
|
+
arrayview/arrayview-opener.vsix,sha256=1KWu52Mdk7TexvcnS5OpjyGubqUxNG5uPjkcYt1SWAY,19874
|
|
42
42
|
arrayview/gsap.min.js,sha256=VGihm4idI0E1uqrWezWsiScB9F_jMosn7qs6pPSkeac,72304
|
|
43
|
-
arrayview-0.29.
|
|
44
|
-
arrayview-0.29.
|
|
45
|
-
arrayview-0.29.
|
|
46
|
-
arrayview-0.29.
|
|
47
|
-
arrayview-0.29.
|
|
43
|
+
arrayview-0.29.2.dist-info/METADATA,sha256=YUYJgmRoLFLN-n4QPLRwWmiSIbevuWoGrhQ5VAZiLFQ,2054
|
|
44
|
+
arrayview-0.29.2.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
45
|
+
arrayview-0.29.2.dist-info/entry_points.txt,sha256=FQSz3M11B-zaVLDyXofKUDvMhHASz7OjmoLgI4nn7ko,105
|
|
46
|
+
arrayview-0.29.2.dist-info/licenses/LICENSE,sha256=34OivjM9faB2QaPjZpMRTIiSx31V3U9cEyQ6F2k-bH0,1062
|
|
47
|
+
arrayview-0.29.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|