From e19d188fcd8aea664feb8858ed56232f79ae153c Mon Sep 17 00:00:00 2001 From: Copilot CLI Date: Tue, 25 Aug 2026 05:49:00 +0000 Subject: [PATCH] mrd-viz: consolidate release UX and D3 scaffold Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- mrd-viz/README.md | 14 +++- mrd-viz/extension/mrd-viz/CHANGELOG.md | 4 +- mrd-viz/extension/mrd-viz/README.md | 13 ++- mrd-viz/extension/mrd-viz/package.json | 10 +++ mrd-viz/extension/mrd-viz/src/extension.ts | 50 ++++++++++- .../mrd-viz/src/mrdEditorProvider.ts | 6 +- .../mrd-viz/src/test/extension.test.ts | 3 + mrd-viz/extension/mrd-viz/src/webviewHtml.ts | 9 +- .../extension/mrd-viz/src/workflowPanel.ts | 84 +++++++++++++++++++ 9 files changed, 183 insertions(+), 10 deletions(-) create mode 100644 mrd-viz/extension/mrd-viz/src/workflowPanel.ts diff --git a/mrd-viz/README.md b/mrd-viz/README.md index 27bc572a..dc6cd319 100644 --- a/mrd-viz/README.md +++ b/mrd-viz/README.md @@ -2,4 +2,16 @@ Lightweight MRD inspection and preview VS Code extension. -The current focus is direct inspection of existing `.mrd` files rather than reconstruction orchestration. See `docs/TECHNICAL_DESIGN.md` for detailed project context and implementation plan. \ No newline at end of file +The current focus is direct inspection of existing `.mrd` files rather than reconstruction orchestration. See `docs/TECHNICAL_DESIGN.md` for detailed project context and implementation plan. + +## Functionality / UX progression + +| Track | Before UX | After UX | Status | +| --- | --- | --- | --- | +| **PR78 checkpoint** | Dev-container-first onboarding (`just mrd-viz-container-setup`) | Extension/backend become usable across windows attached to the same dev container | Delivered | +| **PR79 + D3 consolidated** | Users could end up in Python/PyPI/manual interpreter flows | **Stable install path added as the primary UX** via `https://github.com/ismrmrd/mrd/releases/latest`, with in-product “Open Stable Install Link” guidance | Delivered | +| **D1 local fallback** | Backend setup was mostly manual | Command-driven managed setup + manual override remain available for development/debug paths | Delivered | +| **D3 workflow surface** | File-first custom editor only | Added workflow/session-level panel scaffold (`Open Workflow View (D3 Scaffold)`) to iterate on run-level UX | Scaffolded | +| **D2** | No dedicated multi-file comparison workflow | Planned deeper multi-file comparison UX on top of current viewer contracts | Planned | +| **Marketplace** | Side-load/release flow primary | Planned one-click marketplace install/update once publisher path is finalized | Planned | +| **F10 onboarding polish** | Onboarding still split across docs + commands | Planned final simplification from researcher feedback rounds | Planned | \ No newline at end of file diff --git a/mrd-viz/extension/mrd-viz/CHANGELOG.md b/mrd-viz/extension/mrd-viz/CHANGELOG.md index a14e5840..b1073073 100644 --- a/mrd-viz/extension/mrd-viz/CHANGELOG.md +++ b/mrd-viz/extension/mrd-viz/CHANGELOG.md @@ -6,4 +6,6 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how ## [Unreleased] -- Initial release \ No newline at end of file +- Added a stable install command (`MRD Viz: Open Stable Install Link`) that opens the latest MRD release URL. +- Updated backend-missing UX to prioritize the stable release install path before Python fallback setup. +- Added `MRD Viz: Open Workflow View (D3 Scaffold)` as the first workflow/session-level D3 panel scaffold. \ No newline at end of file diff --git a/mrd-viz/extension/mrd-viz/README.md b/mrd-viz/extension/mrd-viz/README.md index 414819ba..a718bd92 100644 --- a/mrd-viz/extension/mrd-viz/README.md +++ b/mrd-viz/extension/mrd-viz/README.md @@ -10,12 +10,13 @@ A Visual Studio Code extension for inspecting [MRD](https://github.com/ismrmrd/m ## Requirements -MRD Viz relies on a Python backend (the `mrd_viz` package) to read `.mrd` files. You need: +For researcher onboarding, use the stable release installer flow first: -- Python 3.12 -- The `mrd_viz` backend installed into an environment the extension can find. +- Open: `https://github.com/ismrmrd/mrd/releases/latest` +- Install the latest MRD Viz release artifact -Point the extension at the interpreter with the `mrdViz.backendPath` setting (see below). During local development (the F5 dev host) the extension also auto-detects a virtual environment at `mrd-viz/backend/.venv`. +That path is intended to avoid local Python/PyPI setup. +For development/debug fallback, MRD Viz can use a Python backend (the `mrd_viz` package) via `mrdViz.backendPath`. During local development (the F5 dev host) the extension also auto-detects `mrd-viz/backend/.venv`. ## Extension Settings @@ -28,6 +29,10 @@ This extension contributes the following settings: ## Commands - `MRD Viz: Open File` — open the selected or picked `.mrd` file in MRD Viz. +- `MRD Viz: Open Stable Install Link` — open the latest MRD release page for the stable installer path. +- `MRD Viz: Set Up Backend` — choose stable installer, managed local setup, or manual interpreter selection. +- `MRD Viz: Select Python Interpreter` — set `mrdViz.backendPath` manually. +- `MRD Viz: Open Workflow View (D3 Scaffold)` — open the first workflow/session-level D3 panel scaffold. ## Known Issues diff --git a/mrd-viz/extension/mrd-viz/package.json b/mrd-viz/extension/mrd-viz/package.json index 333ce839..401c06da 100644 --- a/mrd-viz/extension/mrd-viz/package.json +++ b/mrd-viz/extension/mrd-viz/package.json @@ -34,6 +34,16 @@ "command": "mrd-viz.selectInterpreter", "title": "Select Python Interpreter", "category": "MRD Viz" + }, + { + "command": "mrd-viz.openStableInstallLink", + "title": "Open Stable Install Link", + "category": "MRD Viz" + }, + { + "command": "mrd-viz.openWorkflowView", + "title": "Open Workflow View (D3 Scaffold)", + "category": "MRD Viz" } ], "customEditors": [ diff --git a/mrd-viz/extension/mrd-viz/src/extension.ts b/mrd-viz/extension/mrd-viz/src/extension.ts index edb6c693..d3442ac5 100644 --- a/mrd-viz/extension/mrd-viz/src/extension.ts +++ b/mrd-viz/extension/mrd-viz/src/extension.ts @@ -13,6 +13,9 @@ import { import { invalidateBackendCache, managedVenvDirectory, managedVenvPythonPath } from './backendResolver'; import { MrdEditorProvider, MRD_VIEW_TYPE } from './mrdEditorProvider'; import { runProcess } from './subprocess'; +import { showWorkflowScaffoldPanel } from './workflowPanel'; + +const STABLE_RELEASE_URL = 'https://github.com/ismrmrd/mrd/releases/latest'; export function activate(context: vscode.ExtensionContext) { const outputChannel = vscode.window.createOutputChannel('MRD Viz'); @@ -28,6 +31,8 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push( vscode.commands.registerCommand('mrd-viz.setUpBackend', () => setUpBackend(context, outputChannel)), vscode.commands.registerCommand('mrd-viz.selectInterpreter', () => selectInterpreter()), + vscode.commands.registerCommand('mrd-viz.openStableInstallLink', () => openStableInstallLink()), + vscode.commands.registerCommand('mrd-viz.openWorkflowView', () => showWorkflowScaffoldPanel()), vscode.workspace.onDidChangeConfiguration(event => { if (event.affectsConfiguration('mrdViz.backendPath')) { invalidateBackendCache(); @@ -106,6 +111,46 @@ async function pickTargetUri(resource?: vscode.Uri, selectedResources?: vscode.U } async function setUpBackend(context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel): Promise { + const quickPick = await vscode.window.showQuickPick( + [ + { + label: 'Use stable release installer (recommended)', + description: 'No Python/PyPI setup steps. Open the latest GitHub release install link.', + action: 'stable' as const, + }, + { + label: 'Set up backend automatically here', + description: 'Creates a managed Python environment and installs mrd-viz.', + action: 'managed' as const, + }, + { + label: 'Select backend interpreter manually', + description: 'Point mrdViz.backendPath at an existing backend environment.', + action: 'manual' as const, + }, + ], + { + placeHolder: 'Choose backend setup mode', + ignoreFocusOut: true, + }, + ); + if (!quickPick) { + return; + } + + if (quickPick.action === 'stable') { + await openStableInstallLink(); + return; + } + if (quickPick.action === 'manual') { + await selectInterpreter(); + return; + } + + await setUpManagedBackend(context, outputChannel); +} + +async function setUpManagedBackend(context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel): Promise { // If a managed backend is already provisioned, this rebuilds it from scratch, so confirm the // reinstall rather than silently clobbering a working environment. const alreadyInstalled = existsSync(managedVenvPythonPath(context)); @@ -133,6 +178,10 @@ async function setUpBackend(context: vscode.ExtensionContext, outputChannel: vsc } } +async function openStableInstallLink(): Promise { + await vscode.env.openExternal(vscode.Uri.parse(STABLE_RELEASE_URL)); +} + async function selectInterpreter(): Promise { const picked = await vscode.window.showOpenDialog({ canSelectMany: false, @@ -350,4 +399,3 @@ function appendIfPresent(outputChannel: vscode.OutputChannel, label: string, tex function isMrdFile(filePath: string): boolean { return filePath.toLowerCase().endsWith('.mrd'); } - diff --git a/mrd-viz/extension/mrd-viz/src/mrdEditorProvider.ts b/mrd-viz/extension/mrd-viz/src/mrdEditorProvider.ts index f524a416..9a551a20 100644 --- a/mrd-viz/extension/mrd-viz/src/mrdEditorProvider.ts +++ b/mrd-viz/extension/mrd-viz/src/mrdEditorProvider.ts @@ -11,7 +11,11 @@ import { appendIfPresent, bindViewerMessageHandling } from './viewerController'; export const MRD_VIEW_TYPE = 'mrd-viz.mrdFile'; -const BACKEND_SETUP_COMMANDS = new Set(['mrd-viz.setUpBackend', 'mrd-viz.selectInterpreter']); +const BACKEND_SETUP_COMMANDS = new Set([ + 'mrd-viz.setUpBackend', + 'mrd-viz.selectInterpreter', + 'mrd-viz.openStableInstallLink', +]); class MrdDocument implements vscode.CustomDocument { constructor(readonly uri: vscode.Uri) {} diff --git a/mrd-viz/extension/mrd-viz/src/test/extension.test.ts b/mrd-viz/extension/mrd-viz/src/test/extension.test.ts index cbc91c0f..8911e101 100644 --- a/mrd-viz/extension/mrd-viz/src/test/extension.test.ts +++ b/mrd-viz/extension/mrd-viz/src/test/extension.test.ts @@ -58,6 +58,8 @@ suite('MRD Viz Extension', () => { assert.ok(commands.includes('mrd-viz.openFile')); assert.ok(commands.includes('mrd-viz.setUpBackend')); assert.ok(commands.includes('mrd-viz.selectInterpreter')); + assert.ok(commands.includes('mrd-viz.openStableInstallLink')); + assert.ok(commands.includes('mrd-viz.openWorkflowView')); }); test('routes command opens through the custom editor view type', () => { @@ -97,6 +99,7 @@ suite('MRD Viz Extension', () => { assert.ok(html.includes('No module named 'mrd_viz'') || html.includes("No module named 'mrd_viz'")); assert.ok(html.includes('mrd-viz.setUpBackend')); assert.ok(html.includes('mrd-viz.selectInterpreter')); + assert.ok(html.includes('mrd-viz.openStableInstallLink')); }); test('tailors the backend-missing view when a developer override is broken', () => { diff --git a/mrd-viz/extension/mrd-viz/src/webviewHtml.ts b/mrd-viz/extension/mrd-viz/src/webviewHtml.ts index 6f04060e..d984efc7 100644 --- a/mrd-viz/extension/mrd-viz/src/webviewHtml.ts +++ b/mrd-viz/extension/mrd-viz/src/webviewHtml.ts @@ -103,12 +103,16 @@ export function getMrdBackendMissingHtml(webview: vscode.Webview, tried: Backend
    ${triedItems}
+
-

Point MRD Viz at a Python environment

-

Create an environment that has the mrd_viz package, then set mrdViz.backendPath to its interpreter (or to a prebuilt mrd-viz binary):

+

Recommended: stable release installer (no Python/PyPI steps)

+

Use Open Stable Install Link to go to the latest MRD release page and install the researcher build artifact. That path is intended to avoid local Python/PyPI setup.

+ +

Fallback: point MRD Viz at a Python environment

+

If you are developing or debugging, create an environment that has the mrd_viz package, then set mrdViz.backendPath to its interpreter (or to a prebuilt mrd-viz binary):

python3 -m venv ~/.mrd-viz-venv
 # install the backend from your mrd checkout (mrd-viz is not published to PyPI):
 ~/.mrd-viz-venv/bin/pip install -e path/to/mrd/mrd-viz/backend
@@ -122,6 +126,7 @@ export function getMrdBackendMissingHtml(webview: vscode.Webview, tried: Backend
 		(function () {
 			const vscode = acquireVsCodeApi();
 			function send(command) { vscode.postMessage({ type: 'command', command: command }); }
+			document.getElementById('stable').addEventListener('click', function () { send('mrd-viz.openStableInstallLink'); });
 			document.getElementById('setup').addEventListener('click', function () { send('mrd-viz.setUpBackend'); });
 			document.getElementById('select').addEventListener('click', function () { send('mrd-viz.selectInterpreter'); });
 		})();
diff --git a/mrd-viz/extension/mrd-viz/src/workflowPanel.ts b/mrd-viz/extension/mrd-viz/src/workflowPanel.ts
new file mode 100644
index 00000000..d8f078a0
--- /dev/null
+++ b/mrd-viz/extension/mrd-viz/src/workflowPanel.ts
@@ -0,0 +1,84 @@
+import * as vscode from 'vscode';
+
+const WORKFLOW_VIEW_TYPE = 'mrd-viz.workflowScaffold';
+
+export function showWorkflowScaffoldPanel(): void {
+	const panel = vscode.window.createWebviewPanel(
+		WORKFLOW_VIEW_TYPE,
+		'MRD Viz: Workflow View (D3 Scaffold)',
+		vscode.ViewColumn.Beside,
+		{ enableScripts: false, retainContextWhenHidden: true },
+	);
+	panel.webview.html = workflowScaffoldHtml(panel.webview);
+}
+
+function workflowScaffoldHtml(webview: vscode.Webview): string {
+	const nonce = getNonce();
+	return `
+
+
+	
+	
+	
+	MRD Viz Workflow Scaffold
+	
+
+
+	

D3 workflow-level view scaffold

+

This panel is the initial D3 surface for workflow/session-level MRD navigation while file-first viewing remains stable.

+
+
+

Run timeline (placeholder)

+
    +
  • Session/run sequence with stage markers
  • +
  • Filter by acquisition, image, and waveform events
  • +
+
+
+

Artifact list (placeholder)

+
    +
  • Grouped outputs per run
  • +
  • Open output artifacts directly in MRD Viz
  • +
+
+
+

Comparison hooks (placeholder)

+
    +
  • Metadata snapshots across runs
  • +
  • Bridge into D2 multi-file comparison workflows
  • +
+
+
+ +`; +} + +function getNonce(): string { + const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let nonce = ''; + for (let i = 0; i < 32; i += 1) { + nonce += alphabet.charAt(Math.floor(Math.random() * alphabet.length)); + } + return nonce; +}