arrayview 0.40.0__tar.gz → 0.41.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {arrayview-0.40.0 → arrayview-0.41.1}/PKG-INFO +1 -1
- {arrayview-0.40.0 → arrayview-0.41.1}/pyproject.toml +2 -1
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/ARCHITECTURE.md +1 -1
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_analysis.py +1 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_io.py +39 -30
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_launch_plan.py +9 -1
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_launcher.py +413 -84
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_lifecycle.py +20 -4
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_platform.py +11 -2
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_routes_loading.py +100 -9
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_routes_query.py +75 -1
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_routes_state.py +55 -41
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_routes_vectorfield.py +41 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_server.py +107 -21
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_session.py +20 -2
- arrayview-0.41.1/src/arrayview/_source_safety.py +775 -0
- arrayview-0.41.1/src/arrayview/_tutorial.py +194 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_viewer.html +960 -173
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_vscode_browser.py +38 -23
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_vscode_extension.py +202 -26
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_vscode_signal.py +12 -3
- arrayview-0.41.1/src/arrayview/_watch.py +57 -0
- arrayview-0.41.1/src/arrayview/arrayview-opener.vsix +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/lifecycle_matrix.py +1 -1
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_api.py +47 -10
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_browser.py +50 -74
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_display_opener_results.py +40 -1
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_launch_blackbox.py +275 -3
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_launch_plan.py +36 -3
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_lifecycle_contract.py +299 -18
- arrayview-0.41.1/tests/test_nifti_meta.py +174 -0
- arrayview-0.41.1/tests/test_source_safety.py +716 -0
- arrayview-0.41.1/tests/test_tutorial_browser.py +402 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_vscode_ack_protocol.py +53 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/visual_smoke.py +23 -11
- arrayview-0.40.0/src/arrayview/_tutorial.py +0 -106
- arrayview-0.40.0/src/arrayview/arrayview-opener.vsix +0 -0
- arrayview-0.40.0/tests/test_command_reachability.py +0 -140
- arrayview-0.40.0/tests/test_cross_mode_parametrized.py +0 -214
- arrayview-0.40.0/tests/test_interactions.py +0 -2132
- arrayview-0.40.0/tests/test_nifti_meta.py +0 -59
- arrayview-0.40.0/tests/test_tutorial_browser.py +0 -197
- arrayview-0.40.0/tests/test_view_component_integration.py +0 -1675
- arrayview-0.40.0/tests/test_view_component_unit.py +0 -165
- {arrayview-0.40.0 → arrayview-0.41.1}/.gitignore +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/LICENSE +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/README.md +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/__init__.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/__main__.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_app.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_config.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_diagnostics.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_dicom.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_diff.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_icon.png +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_imaging.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_instance_registry.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_launch_trace.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_overlays.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_render.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_routes_analysis.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_routes_drop.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_routes_export.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_routes_persistence.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_routes_preferences.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_routes_preload.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_routes_rendering.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_routes_segmentation.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_routes_websocket.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_segmentation.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_session_spec.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_shell.html +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_synthetic_mri.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_torch.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_vectorfield.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/_vscode.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/gsap.min.js +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/src/arrayview/todo.md +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/capture_patient_loading.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/capture_v_animation.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/conftest.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/make_vectorfield_test_arrays.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_1d_arrays.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_backend_shared.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_cli.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_colorbar_hover_highlight.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_config.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_dicom.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_doctor_commands.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_drop_import.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_instance_registry.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_invocation_contract_matrix.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_io.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_large_arrays.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_launch_diagnostics.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_launch_execution_ownership.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_launch_trace.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_loading_server.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_mode_consistency.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_mode_matrix.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_mode_roundtrip.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_nifti_series.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_preferences_ui.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_public_invocation_regressions.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_render_failure_reporting.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_render_progress.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_rgb_pixel_art.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_safe_server_stop.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_server_identity_status.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_session_spec.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_spawn_ownership.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_torch.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_tutorial.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_value_formatting.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_view_handle_lifecycle.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_view_launch_execution.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/test_window_level_preview.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/ui_audit.py +0 -0
- {arrayview-0.40.0 → arrayview-0.41.1}/tests/v_anim_frames/.gitkeep +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "arrayview"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.41.1"
|
|
8
8
|
description = "Fast multi-dimensional array viewer"
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -63,6 +63,7 @@ test = [
|
|
|
63
63
|
"httpx>=0.27",
|
|
64
64
|
"pytest-playwright>=0.5",
|
|
65
65
|
"pillow>=12.1.1",
|
|
66
|
+
"nbclassic>=1.3,<2",
|
|
66
67
|
]
|
|
67
68
|
docs = [
|
|
68
69
|
"mkdocs>=1.6",
|
|
@@ -169,7 +169,7 @@ A refactoring in progress that introduces a View/Layer/Slicer/LayoutStrategy/Mod
|
|
|
169
169
|
**Status:** Phases 1–15 complete. Phase 12 (keybind collapse) and Phase 17 (final cleanup) pending completion of render-pipeline migration from legacy globals to `displayState`.
|
|
170
170
|
|
|
171
171
|
### Command Registry
|
|
172
|
-
All keybinds flow through a VS Code-style command registry in `_viewer.html`. Three tables: `commands` (id → `{title, when, run}`), `keybinds` (key+modifiers → command id), and `makeContext(state)` (mode/state flag bag). `dispatchCommand(e)` is wired as a prefix to the keydown handler; on a match it evaluates `when` against the context and runs the command, otherwise falls through. The help overlay is rendered at runtime from the `GUIDE_TABS` static data structure in `_viewer.html` — when adding or changing a keybind, update `GUIDE_TABS` manually. A `/`-triggered command palette fuzzy-searches all commands.
|
|
172
|
+
All keybinds flow through a VS Code-style command registry in `_viewer.html`. Three tables: `commands` (id → `{title, when, run}`), `keybinds` (key+modifiers → command id), and `makeContext(state)` (mode/state flag bag). `dispatchCommand(e)` is wired as a prefix to the keydown handler; on a match it evaluates `when` against the context and runs the command, otherwise falls through. The help overlay is rendered at runtime from the `GUIDE_TABS` static data structure in `_viewer.html` — when adding or changing a keybind, update `GUIDE_TABS` manually. A `/`-triggered command palette fuzzy-searches all commands.
|
|
173
173
|
|
|
174
174
|
### Reconcilers
|
|
175
175
|
Functions in the "UI Validation and Reconciliation" section (~line 13666) that enforce consistent UI state. When mode changes happen, reconcilers update visibility of containers, colorbars, dynamic islands, and compare sub-mode UI. There are four:
|
|
@@ -51,6 +51,7 @@ def _build_metadata(session) -> dict:
|
|
|
51
51
|
"voxel_sizes": list(sm["voxel_sizes"]),
|
|
52
52
|
"axis_labels": list(sm["axis_labels"]),
|
|
53
53
|
"is_oblique": bool(sm["is_oblique"]),
|
|
54
|
+
"is_canonical": bool(sm.get("is_canonical", True)),
|
|
54
55
|
}
|
|
55
56
|
meta["ras_resample_active"] = bool(
|
|
56
57
|
getattr(session, "ras_resample_active", False)
|
|
@@ -139,8 +139,13 @@ def _select_npz_array(npz, filepath):
|
|
|
139
139
|
return npz[keys[0]]
|
|
140
140
|
|
|
141
141
|
|
|
142
|
-
def _nifti_header_with_meta(filepath):
|
|
143
|
-
"""Return a NIfTI image and
|
|
142
|
+
def _nifti_header_with_meta(filepath, *, native=False):
|
|
143
|
+
"""Return a NIfTI image and spatial metadata without reading voxels.
|
|
144
|
+
|
|
145
|
+
Directory collections use canonical metadata because their lazy series
|
|
146
|
+
objects expose canonical axes. A single volume uses native metadata so an
|
|
147
|
+
uncompressed NIfTI can keep its proxy and storage-friendly axis order.
|
|
148
|
+
"""
|
|
144
149
|
nib = _nib()
|
|
145
150
|
img = nib.load(filepath)
|
|
146
151
|
original_affine = np.asarray(img.affine, dtype=np.float64)
|
|
@@ -151,30 +156,40 @@ def _nifti_header_with_meta(filepath):
|
|
|
151
156
|
transform, img.shape[:3]
|
|
152
157
|
)
|
|
153
158
|
|
|
154
|
-
|
|
159
|
+
data_affine = original_affine if native else affine_canonical
|
|
160
|
+
rot = data_affine[:3, :3]
|
|
155
161
|
voxel_sizes = tuple(float(np.linalg.norm(rot[:, i])) for i in range(3))
|
|
162
|
+
canonical_rot = affine_canonical[:3, :3]
|
|
163
|
+
canonical_voxel_sizes = tuple(
|
|
164
|
+
float(np.linalg.norm(canonical_rot[:, i])) for i in range(3)
|
|
165
|
+
)
|
|
156
166
|
norm_rot = np.zeros((3, 3))
|
|
157
167
|
for i in range(3):
|
|
158
|
-
if
|
|
159
|
-
norm_rot[:, i] =
|
|
160
|
-
|
|
161
|
-
neg_labels = ("L", "P", "I")
|
|
162
|
-
axis_labels = tuple(
|
|
163
|
-
pos_labels[i] if norm_rot[i, i] >= 0 else neg_labels[i] for i in range(3)
|
|
164
|
-
)
|
|
168
|
+
if canonical_voxel_sizes[i] > 0:
|
|
169
|
+
norm_rot[:, i] = canonical_rot[:, i] / canonical_voxel_sizes[i]
|
|
170
|
+
axis_labels = tuple(str(v) for v in nib.aff2axcodes(data_affine))
|
|
165
171
|
off_diag_max = max(
|
|
166
172
|
(abs(norm_rot[i, j]) for i in range(3) for j in range(3) if i != j),
|
|
167
173
|
default=0.0,
|
|
168
174
|
)
|
|
169
175
|
canonical_shape = tuple(int(img.shape[int(axis)]) for axis in transform[:, 0])
|
|
170
|
-
|
|
176
|
+
meta = {
|
|
171
177
|
"affine": original_affine,
|
|
172
178
|
"affine_canonical": affine_canonical,
|
|
179
|
+
"data_affine": data_affine,
|
|
173
180
|
"voxel_sizes": voxel_sizes,
|
|
174
181
|
"axis_labels": axis_labels,
|
|
175
182
|
"is_oblique": bool(off_diag_max > 1e-3),
|
|
176
|
-
"
|
|
183
|
+
"is_canonical": bool(
|
|
184
|
+
np.array_equal(transform, np.asarray([[0, 1], [1, 1], [2, 1]]))
|
|
185
|
+
),
|
|
186
|
+
"to_canonical": transform,
|
|
177
187
|
}
|
|
188
|
+
if native:
|
|
189
|
+
meta["spatial_shape"] = tuple(int(v) for v in img.shape[:3])
|
|
190
|
+
else:
|
|
191
|
+
meta["canonical_shape"] = canonical_shape
|
|
192
|
+
return img, meta
|
|
178
193
|
|
|
179
194
|
|
|
180
195
|
def _nifti_display_array(proxy):
|
|
@@ -373,25 +388,19 @@ def _get_progressive_nifti_pool(*, overlay_prefetch=False):
|
|
|
373
388
|
|
|
374
389
|
|
|
375
390
|
def _load_nifti_with_meta(filepath):
|
|
376
|
-
"""Load a NIfTI file
|
|
377
|
-
|
|
378
|
-
meta is a dict with keys:
|
|
379
|
-
affine : original 4x4 affine (RAS+ mm)
|
|
380
|
-
affine_canonical : 4x4 affine after as_closest_canonical
|
|
381
|
-
voxel_sizes : tuple (sx, sy, sz) in mm, post-reorient
|
|
382
|
-
axis_labels : tuple of 3 strs from {"R","L","A","P","S","I"}
|
|
383
|
-
— positive direction of each canonical axis
|
|
384
|
-
is_oblique : bool — True if rotation part has off-diagonal magnitude > 1e-3
|
|
385
|
-
after normalizing voxel sizes
|
|
386
|
-
"""
|
|
387
|
-
img, meta = _nifti_header_with_meta(filepath)
|
|
388
|
-
canon = _nib().as_closest_canonical(img)
|
|
389
|
-
|
|
390
|
-
# NOTE: reorient requires materializing axis permutes/flips. .nii.gz is
|
|
391
|
-
# already eager (gzip not seekable), so this is free; .nii loses mmap as a
|
|
392
|
-
# necessary cost to apply the reorient.
|
|
393
|
-
arr = np.asarray(canon.dataobj)
|
|
391
|
+
"""Load a NIfTI file in native voxel order and return (array, meta).
|
|
394
392
|
|
|
393
|
+
Uncompressed .nii stays proxy-backed. This preserves cheap native-plane
|
|
394
|
+
reads even when the affine describes permuted or flipped anatomical axes.
|
|
395
|
+
Compressed .nii.gz still has to be decoded eagerly because gzip is not
|
|
396
|
+
random-access, but it is no longer silently reordered.
|
|
397
|
+
"""
|
|
398
|
+
img, meta = _nifti_header_with_meta(filepath, native=True)
|
|
399
|
+
arr = (
|
|
400
|
+
img.dataobj
|
|
401
|
+
if filepath.endswith(".nii")
|
|
402
|
+
else _nifti_display_array(img.dataobj)
|
|
403
|
+
)
|
|
395
404
|
return arr, meta
|
|
396
405
|
|
|
397
406
|
|
|
@@ -267,6 +267,8 @@ def _completion_target(
|
|
|
267
267
|
if plan.display is Display.BROWSER:
|
|
268
268
|
if _placement(evidence) is Placement.SSH:
|
|
269
269
|
return CompletionTarget.GUIDANCE_PRINTED
|
|
270
|
+
if _placement(evidence) is Placement.VSCODE_REMOTE:
|
|
271
|
+
return CompletionTarget.FRAME_READY
|
|
270
272
|
return CompletionTarget.DISPATCH_ACCEPTED
|
|
271
273
|
if invocation in {Invocation.CLI, Invocation.VSCODE_EXPLORER}:
|
|
272
274
|
return CompletionTarget.FRAME_READY
|
|
@@ -412,7 +414,9 @@ def plan_launch(
|
|
|
412
414
|
window=None if remote_jupyter else window,
|
|
413
415
|
inline=False if remote_jupyter else intent.inline,
|
|
414
416
|
environment=environment,
|
|
415
|
-
native_available=facts.native_backend is not None,
|
|
417
|
+
native_available=facts.native_backend is not None,
|
|
418
|
+
in_vscode_tunnel=facts.in_vscode_tunnel,
|
|
419
|
+
reasons=reasons,
|
|
416
420
|
)
|
|
417
421
|
if display is Display.VSCODE and environment not in {
|
|
418
422
|
Environment.VSCODE_LOCAL, Environment.VSCODE_REMOTE
|
|
@@ -436,6 +440,7 @@ def _display_policy(
|
|
|
436
440
|
inline: bool | None,
|
|
437
441
|
environment: Environment,
|
|
438
442
|
native_available: bool,
|
|
443
|
+
in_vscode_tunnel: bool,
|
|
439
444
|
reasons: list[str],
|
|
440
445
|
) -> tuple[Display, Display | None, bool]:
|
|
441
446
|
if window == "none":
|
|
@@ -461,6 +466,9 @@ def _display_policy(
|
|
|
461
466
|
return Display.VSCODE, None, False
|
|
462
467
|
return Display.VSCODE, Display.BROWSER, True
|
|
463
468
|
if window == "browser":
|
|
469
|
+
if environment is Environment.VSCODE_REMOTE and in_vscode_tunnel:
|
|
470
|
+
reasons.append("remote_external_browser")
|
|
471
|
+
return Display.BROWSER, None, False
|
|
464
472
|
if environment is Environment.VSCODE_REMOTE:
|
|
465
473
|
reasons.append("remote_browser_redirected_to_vscode")
|
|
466
474
|
return Display.VSCODE, None, False
|