Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion matlab/pack_toolbox.m
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
fullfile('matlab', 'toolboxInfo.xml')
fullfile('matlab', 'examples', 'demo.m')
fullfile('web', 'index.html')
fullfile('web', 'derived-series.js')
fullfile('web', 'webview-bundle.js')
fullfile('web', 'styles.css')
fullfile('web', 'pulseq-bundle.js')
fullfile('web', 'logo.png')
fullfile('web', 'logo.svg')
Expand Down
3 changes: 2 additions & 1 deletion matlab/seqeyes.m
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,8 @@ function safeRemoveDir(folderPath)
mkdir(tempDir);
[tmplDir, ~, ~] = fileparts(templatePath);
copyfile(fullfile(tmplDir, 'pulseq-bundle.js'), fullfile(tempDir, 'pulseq-bundle.js'));
copyfile(fullfile(tmplDir, 'derived-series.js'), fullfile(tempDir, 'derived-series.js'));
copyfile(fullfile(tmplDir, 'webview-bundle.js'), fullfile(tempDir, 'webview-bundle.js'));
copyfile(fullfile(tmplDir, 'styles.css'), fullfile(tempDir, 'styles.css'));
% Copy logo assets if they exist (silently skip if missing)
if isfile(fullfile(tmplDir, 'logo.png'))
copyfile(fullfile(tmplDir, 'logo.png'), fullfile(tempDir, 'logo.png'));
Expand Down
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,9 @@
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./ && npm run copy-assets",
"watch": "npm run copy-assets && tsc -watch -p ./",
"copy-assets": "node -e \"var s='src/editor/webview/assets',d='out/editor/webview/assets',fs=require('fs'),p=require('path');fs.mkdirSync(d,{recursive:true});fs.readdirSync(s).forEach(function(f){fs.copyFileSync(p.join(s,f),p.join(d,f))});console.log('Assets copied to out/');\"",
"compile": "node scripts/build-extension.mjs && node scripts/build-webview.mjs",
"watch": "node scripts/build-webview.mjs && node scripts/build-extension.mjs --watch",
"typecheck": "tsc -p ./ --noEmit",
"lint": "eslint src web test --ext ts",
"test": "vitest run",
"test:browser": "playwright test",
Expand All @@ -141,12 +141,12 @@
"perf:browser": "npm run build:web && playwright test --config playwright.perf.config.ts",
"perf": "npm run perf:node && npm run perf:browser",
"test:vscode": "npm run compile && node test/vscode/run-tests.cjs",
"package:vsix": "vsce package --out seqeyes-web.vsix",
"package:vsix": "npm run compile && vsce package --out seqeyes-web.vsix",
"test:package": "npm run package:vsix && node test/vscode/package-smoke.cjs seqeyes-web.vsix",
"check": "npm run compile && npm run lint && npm test && npm run build:web",
"check": "npm run lint && npm test && npm run build:web && npm run compile",
"export:kspace": "node out/cli/exportKspace.js",
"package": "vsce package",
"build:web": "npx esbuild web/pulseq-browser.ts --bundle --format=iife --global-name=Pulseq --outfile=web/pulseq-bundle.js --target=es2020 && node -e \"const fs=require('fs');fs.copyFileSync('images/logo.png','web/logo.png');fs.copyFileSync('web/pulseq-bundle.js','python/src/seqeyes/resources/pulseq-bundle.js')\"",
"package": "npm run compile && vsce package",
"build:web": "node scripts/build-webview.mjs && npx esbuild web/pulseq-browser.ts --bundle --format=iife --global-name=Pulseq --outfile=web/pulseq-bundle.js --target=es2020 && node -e \"const fs=require('fs');fs.copyFileSync('images/logo.png','web/logo.png');fs.copyFileSync('web/pulseq-bundle.js','python/src/seqeyes/resources/pulseq-bundle.js');fs.copyFileSync('web/webview-bundle.js','python/src/seqeyes/resources/webview-bundle.js')\"",
"predeploy": "npm run build:web"
},
"devDependencies": {
Expand Down
2,667 changes: 2,667 additions & 0 deletions python/src/seqeyes/resources/webview-bundle.js

Large diffs are not rendered by default.

80 changes: 80 additions & 0 deletions scripts/build-extension.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* build-extension.mjs — Build the VS Code extension with esbuild.
*
* Replaces `tsc -p ./` for the extension build. Bundles src/extension.ts
* and its dependencies into out/extension.js (CommonJS, targeting Node 18).
* The vscode module is externalised (provided by the VS Code host).
*
* The webview assets are NOT bundled here — they are built separately by
* build-webview.mjs and copied into out/editor/webview/assets/.
*
* Usage:
* node scripts/build-extension.mjs # one-shot build
* node scripts/build-extension.mjs --watch # incremental watch mode
*/

import * as esbuild from 'esbuild';
import * as path from 'path';
import { fileURLToPath } from 'url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, '..');

const watch = process.argv.includes('--watch');

const common = {
bundle: true,
platform: 'node',
target: 'node18',
format: 'cjs',
external: ['vscode'],
sourcemap: true,
logLevel: 'info',
};

const extensionEntry = path.join(ROOT, 'src', 'extension.ts');
const extensionOutfile = path.join(ROOT, 'out', 'extension.js');

const cliEntry = path.join(ROOT, 'src', 'cli', 'exportKspace.ts');
const cliOutfile = path.join(ROOT, 'out', 'cli', 'exportKspace.js');

async function build() {
if (watch) {
// Watch mode: use esbuild context for incremental rebuilds
const extCtx = await esbuild.context({
...common,
entryPoints: [extensionEntry],
outfile: extensionOutfile,
});
await extCtx.watch();
console.log('👁 Watching src/extension.ts …');

const cliCtx = await esbuild.context({
...common,
entryPoints: [cliEntry],
outfile: cliOutfile,
});
await cliCtx.watch();
console.log('👁 Watching src/cli/exportKspace.ts …');
console.log('(Press Ctrl+C to stop watching)');
} else {
await esbuild.build({
...common,
entryPoints: [extensionEntry],
outfile: extensionOutfile,
});
console.log('✓ out/extension.js (VS Code extension)');

await esbuild.build({
...common,
entryPoints: [cliEntry],
outfile: cliOutfile,
});
console.log('✓ out/cli/exportKspace.js');
}
}

build().catch(err => {
console.error(err);
process.exit(1);
});
63 changes: 63 additions & 0 deletions scripts/build-webview.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* build-webview.mjs — Bundle the shared SeqEyes webview rendering assets.
*
* Concatenates the five webview JS files (state, derived-series, drawing,
* kspace, interaction) into a single self-contained script. The files
* communicate via shared globals so they are concatenated without IIFE
* wrapping — they run in the global scope. The VS Code extension wraps
* them in an IIFE when inlining into the webview; the standalone web app
* loads them directly.
*
* Usage: node scripts/build-webview.mjs
*/

import * as fs from 'fs';
import * as path from 'path';
import { fileURLToPath } from 'url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, '..');

const ASSETS_DIR = path.join(ROOT, 'src', 'editor', 'webview', 'assets');

const files = [
'state.js',
'derived-series.js',
'drawing.js',
'kspace.js',
'interaction.js',
];

const parts = files.map(f => {
const content = fs.readFileSync(path.join(ASSETS_DIR, f), 'utf-8');
return `/* ══ ${f} ══ */\n${content}`;
});

const bundle = `/* SeqEyes WebView Bundle — auto-generated, do not edit */
"use strict";
${parts.join('\n\n')}
`;

// Write to web/ (for standalone web app)
const webDir = path.join(ROOT, 'web');
fs.mkdirSync(webDir, { recursive: true });
fs.writeFileSync(path.join(webDir, 'webview-bundle.js'), bundle, 'utf-8');
console.log('✓ web/webview-bundle.js');

// Copy to out/ (for VS Code extension — the copy-assets step will use this)
const outDir = path.join(ROOT, 'out', 'editor', 'webview', 'assets');
fs.mkdirSync(outDir, { recursive: true });
fs.writeFileSync(path.join(outDir, 'webview-bundle.js'), bundle, 'utf-8');
console.log('✓ out/editor/webview/assets/webview-bundle.js');

// Also copy styles.css to both destinations
const cssContent = fs.readFileSync(path.join(ASSETS_DIR, 'styles.css'), 'utf-8');
fs.writeFileSync(path.join(webDir, 'styles.css'), cssContent, 'utf-8');
console.log('✓ web/styles.css');
fs.writeFileSync(path.join(outDir, 'styles.css'), cssContent, 'utf-8');
console.log('✓ out/editor/webview/assets/styles.css');

// Copy template.html to out/
const htmlContent = fs.readFileSync(path.join(ASSETS_DIR, 'template.html'), 'utf-8');
fs.writeFileSync(path.join(outDir, 'template.html'), htmlContent, 'utf-8');
console.log('✓ out/editor/webview/assets/template.html');
33 changes: 8 additions & 25 deletions src/editor/webviewContent.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,12 @@
/**
* webviewContent.ts — SeqEyes webview HTML assembler.
*
* Reads CSS, HTML, and JS from real files under webview/assets/ so you get
* full syntax highlighting, IntelliSense, and formatting in each language.
* Reads the pre-built webview bundle (produced by scripts/build-webview.mjs)
* plus CSS and HTML template from the out/ directory. The webview JS is
* wrapped in an IIFE so it runs isolated from the VS Code webview host.
*
* Asset files (copied to out/ during build):
* webview/assets/styles.css → <style> block
* webview/assets/template.html → <body> markup
* webview/assets/state.js → state, helpers, data reception
* webview/assets/derived-series.js → multiresolution derived-curve summaries
* webview/assets/drawing.js → Canvas rendering functions
* webview/assets/kspace.js → WebGL k-space viewer
* webview/assets/interaction.js → mouse, wheel, toolbar & IIFE close
*
* Files are read synchronously once when the module first loads — the
* webview content is static and this runs in Node.js, so there is no
* performance penalty.
* When bundled with esbuild, __dirname points to out/.
* Asset files live under out/editor/webview/assets/.
*/

import * as fs from 'fs';
Expand All @@ -24,19 +15,15 @@ import * as path from 'path';
// eslint-disable-next-line @typescript-eslint/naming-convention
declare const __dirname: string;

const ASSETS_DIR = path.join(__dirname, 'webview', 'assets');
const ASSETS_DIR = path.join(__dirname, 'editor', 'webview', 'assets');

function readAsset(filename: string): string {
return fs.readFileSync(path.join(ASSETS_DIR, filename), 'utf-8');
}

const CSS = readAsset('styles.css');
const HTML_BODY = readAsset('template.html');
const JS_STATE = readAsset('state.js');
const JS_DERIVED_SERIES = readAsset('derived-series.js');
const JS_DRAWING = readAsset('drawing.js');
const JS_KSPACE = readAsset('kspace.js');
const JS_INTERACTION = readAsset('interaction.js');
const WEBVIEW_BUNDLE = readAsset('webview-bundle.js');

export function getWebviewContent(_hint: number): string {
return [
Expand All @@ -54,11 +41,7 @@ export function getWebviewContent(_hint: number): string {
HTML_BODY,
'<script>',
'(function(){',
JS_STATE,
JS_DERIVED_SERIES,
JS_DRAWING,
JS_KSPACE,
JS_INTERACTION,
WEBVIEW_BUNDLE,
'})();',
'</script>',
'</body>',
Expand Down
2 changes: 1 addition & 1 deletion test/pulseq/waveform-overview.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ describe('standalone waveform overview', () => {
});

function loadOverviewApi(): OverviewApi {
const source = readFileSync(join(__dirname, '..', '..', 'web', 'derived-series.js'), 'utf8');
const source = readFileSync(join(__dirname, '..', '..', 'src', 'editor', 'webview', 'assets', 'derived-series.js'), 'utf8');
const context = createContext({ Float64Array, Int32Array, Infinity, isFinite, Math });
runInContext(source, context);
return context as unknown as OverviewApi;
Expand Down
7 changes: 1 addition & 6 deletions test/vscode/package-smoke.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,9 @@ const entrySet = new Set(entries);
const requiredEntries = [
'extension/package.json',
'extension/out/extension.js',
'extension/out/editor/seqEditorProvider.js',
'extension/out/editor/webviewContent.js',
'extension/out/editor/webview/assets/template.html',
'extension/out/editor/webview/assets/styles.css',
'extension/out/editor/webview/assets/state.js',
'extension/out/editor/webview/assets/drawing.js',
'extension/out/editor/webview/assets/kspace.js',
'extension/out/editor/webview/assets/interaction.js',
'extension/out/editor/webview/assets/webview-bundle.js',
'extension/images/logo.png',
'extension/LICENSE.txt',
'extension/README.md',
Expand Down
Loading
Loading