seqeyes-python 0.2.12__tar.gz → 0.2.13__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.
Files changed (20) hide show
  1. {seqeyes_python-0.2.12 → seqeyes_python-0.2.13}/PKG-INFO +2 -2
  2. {seqeyes_python-0.2.12 → seqeyes_python-0.2.13}/pyproject.toml +1 -1
  3. {seqeyes_python-0.2.12 → seqeyes_python-0.2.13}/src/seqeyes/__init__.py +1 -1
  4. {seqeyes_python-0.2.12 → seqeyes_python-0.2.13}/src/seqeyes/resources/pulseq-bundle.js +11 -2
  5. {seqeyes_python-0.2.12 → seqeyes_python-0.2.13}/src/seqeyes/resources/webview-bundle.js +83 -1
  6. {seqeyes_python-0.2.12 → seqeyes_python-0.2.13}/.gitignore +0 -0
  7. {seqeyes_python-0.2.12 → seqeyes_python-0.2.13}/README.md +0 -0
  8. {seqeyes_python-0.2.12 → seqeyes_python-0.2.13}/examples/demo.ipynb +0 -0
  9. {seqeyes_python-0.2.12 → seqeyes_python-0.2.13}/examples/demo.py +0 -0
  10. {seqeyes_python-0.2.12 → seqeyes_python-0.2.13}/src/seqeyes/_plot.py +0 -0
  11. {seqeyes_python-0.2.12 → seqeyes_python-0.2.13}/src/seqeyes/_renderer.py +0 -0
  12. {seqeyes_python-0.2.12 → seqeyes_python-0.2.13}/src/seqeyes/resources/viewer.html +0 -0
  13. {seqeyes_python-0.2.12 → seqeyes_python-0.2.13}/tests/conftest.py +0 -0
  14. {seqeyes_python-0.2.12 → seqeyes_python-0.2.13}/tests/debug_full.html +0 -0
  15. {seqeyes_python-0.2.12 → seqeyes_python-0.2.13}/tests/debug_minimal.html +0 -0
  16. {seqeyes_python-0.2.12 → seqeyes_python-0.2.13}/tests/debug_viewer.html +0 -0
  17. {seqeyes_python-0.2.12 → seqeyes_python-0.2.13}/tests/debug_viewer.py +0 -0
  18. {seqeyes_python-0.2.12 → seqeyes_python-0.2.13}/tests/integration_test.py +0 -0
  19. {seqeyes_python-0.2.12 → seqeyes_python-0.2.13}/tests/test_plot.py +0 -0
  20. {seqeyes_python-0.2.12 → seqeyes_python-0.2.13}/tests/test_renderer.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: seqeyes-python
3
- Version: 0.2.12
3
+ Version: 0.2.13
4
4
  Summary: Interactive Pulseq MRI sequence viewer for Jupyter — lightweight, beautiful, replaces seq.plot() in pypulseq.
5
5
  Project-URL: Homepage, https://github.com/bughht/seqeyes_plugin
6
6
  Project-URL: Repository, https://github.com/bughht/seqeyes_plugin
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "seqeyes-python"
7
- version = "0.2.12"
7
+ version = "0.2.13"
8
8
  description = "Interactive Pulseq MRI sequence viewer for Jupyter — lightweight, beautiful, replaces seq.plot() in pypulseq."
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -29,4 +29,4 @@ from seqeyes._plot import set, reset
29
29
  from seqeyes._renderer import SeqEyesViewer
30
30
 
31
31
  __all__ = ["set", "reset", "SeqEyesViewer"]
32
- __version__ = "0.2.12"
32
+ __version__ = "0.2.13"
@@ -54,7 +54,7 @@ var Pulseq = (() => {
54
54
  });
55
55
 
56
56
  // package.json
57
- var version = "0.2.12";
57
+ var version = "0.2.13";
58
58
 
59
59
  // src/pulseq/decompressor.ts
60
60
  function decompressShape(compressed, numSamples) {
@@ -3342,7 +3342,16 @@ var Pulseq = (() => {
3342
3342
  kspaceRasterSamples: 12e6,
3343
3343
  kspaceAdcSamples: 8e6,
3344
3344
  kspaceGridCandidates: 18e6,
3345
- derivedRasterSamples: 2e6
3345
+ derivedRasterSamples: 2e6,
3346
+ /**
3347
+ * Ceiling on the display samples one sequence load may carry to the VS Code
3348
+ * webview, counted as (time, amplitude) pairs across every RF and gradient
3349
+ * waveform. At 12 bytes per pair this bounds the shared binary buffers at
3350
+ * roughly 137 MiB in the extension host and again in the renderer.
3351
+ * Per-waveform detail is reduced uniformly once a sequence would exceed it,
3352
+ * because the alternative is an allocation neither process can satisfy.
3353
+ */
3354
+ displayTransportSamples: 12e6
3346
3355
  });
3347
3356
  function derivedDetailViewLimitSec(gradientRaster, trTimeSec, maxRasterSamples = INTERACTIVE_COMPUTE_LIMITS.derivedRasterSamples) {
3348
3357
  if (!(gradientRaster > 0) || !(maxRasterSamples > 0)) return 0;
@@ -1,5 +1,69 @@
1
1
  /* SeqEyes WebView Bundle — auto-generated, do not edit */
2
2
  "use strict";
3
+ /* ══ block-transport.js ══ */
4
+ /* ══════════════════════════════════════════════════════════════════════════
5
+ block-transport.js — rehydrate the packed block payload from the extension.
6
+
7
+ The standalone web app builds BL in this same heap, so its waveform arrays
8
+ cost one copy and nothing more. The VS Code extension has to cross a process
9
+ boundary, and VS Code serialises webview messages with JSON.stringify: every
10
+ sample becomes ~19 characters of text, the extension host and the renderer
11
+ each hold that string alongside the object graph, and V8 refuses to build a
12
+ string longer than 512 MiB at all. Dense arbitrary waveforms reach ~18 KB of
13
+ JSON per block, so a few tens of thousands of them cannot be delivered as
14
+ JSON at any amount of memory.
15
+
16
+ src/editor/blockTransport.ts therefore sends the samples as two shared binary
17
+ buffers — Float64 times, Float32 amplitudes — plus an envelope carrying each
18
+ waveform's offset and count. Restoring the arrays as typed-array views over
19
+ those buffers reproduces the exact block shape the renderer reads, without
20
+ copying a single sample: the renderer only ever indexes these arrays and asks
21
+ for their length, which a typed array answers identically.
22
+ ══════════════════════════════════════════════════════════════════════════ */
23
+
24
+ /** Interpret a transferred buffer as `Ctor`, or null if it did not arrive. */
25
+ function asTypedView(buffer,Ctor){
26
+ if(buffer instanceof Ctor)return buffer;
27
+ if(buffer instanceof ArrayBuffer)return new Ctor(buffer);
28
+ // Some hosts hand the transfer back as a byte view rather than the buffer.
29
+ if(buffer&&buffer.buffer instanceof ArrayBuffer&&typeof buffer.byteLength==='number')
30
+ return new Ctor(buffer.buffer,buffer.byteOffset||0,Math.floor(buffer.byteLength/Ctor.BYTES_PER_ELEMENT));
31
+ return null;
32
+ }
33
+
34
+ /**
35
+ * Attach typed-array views to the envelope in place and return it as BL.
36
+ * Throws rather than returning partial blocks: a viewer that silently draws
37
+ * half a sequence is worse than one that says why it cannot.
38
+ */
39
+ function unpackSequenceBlocks(envelope,timeBuffer,valueBuffer,sampleCount){
40
+ if(!envelope||!envelope.length)return [];
41
+ var times=asTypedView(timeBuffer,Float64Array),values=asTypedView(valueBuffer,Float32Array);
42
+ if(!times||!values)throw new Error('the waveform sample buffers did not arrive as binary data');
43
+ var available=Math.min(times.length,values.length);
44
+ if(sampleCount>available)
45
+ throw new Error('the waveform sample buffers are short by '+(sampleCount-available)+' samples');
46
+ for(var i=0;i<envelope.length;i++){
47
+ var b=envelope[i];
48
+ if(b.rf){
49
+ b.rf.t=times.subarray(b.rf.o,b.rf.o+b.rf.n);
50
+ b.rf.m=values.subarray(b.rf.o,b.rf.o+b.rf.n);
51
+ b.rf.pt=times.subarray(b.rf.qo,b.rf.qo+b.rf.qn);
52
+ b.rf.p=values.subarray(b.rf.qo,b.rf.qo+b.rf.qn);
53
+ }
54
+ if(b.gx)attachGradientSamples(b.gx,times,values);
55
+ if(b.gy)attachGradientSamples(b.gy,times,values);
56
+ if(b.gz)attachGradientSamples(b.gz,times,values);
57
+ }
58
+ return envelope;
59
+ }
60
+
61
+ function attachGradientSamples(gradient,times,values){
62
+ gradient.t=times.subarray(gradient.o,gradient.o+gradient.n);
63
+ gradient.w=values.subarray(gradient.o,gradient.o+gradient.n);
64
+ }
65
+
66
+
3
67
  /* ══ state.js ══ */
4
68
  /* ═══════════════════════════════════════════════════════════════════════
5
69
  Application state & configuration
@@ -248,6 +312,16 @@ function applySerializedKspace(payload){
248
312
  }
249
313
  }
250
314
 
315
+ /* Report a load that did not arrive. Clearing the previous sequence matters as
316
+ much as the message: the progress overlay reaches "Ready" either way, so
317
+ leaving stale blocks on screen would read as a successful load. */
318
+ function showSequenceLoadFailure(message){
319
+ BL=[];waveformOverview=null;blockPos=[];mmCache=null;
320
+ setViewerNotice('sequence',message);
321
+ setExportButtonEnabled(false);
322
+ draw();drawMinimap();
323
+ }
324
+
251
325
  /* ── Data reception ───────────────────────────────────────────────────── */
252
326
  window.addEventListener('message',function(e){
253
327
  var m=e.data;
@@ -270,7 +344,13 @@ window.addEventListener('message',function(e){
270
344
  return;
271
345
  }
272
346
  if(m.type==='sequenceData'){
273
- BL=m.blocks||[];TD=m.totalDuration||0;GR=m.gradRaster||1e-5;
347
+ try{
348
+ BL=unpackSequenceBlocks(m.blocks,m.sampleTimes,m.sampleValues,m.sampleCount||0);
349
+ }catch(err){
350
+ showSequenceLoadFailure('Failed to load the sequence waveforms: '+(err&&err.message||err)+'.');
351
+ return;
352
+ }
353
+ TD=m.totalDuration||0;GR=m.gradRaster||1e-5;
274
354
  setViewerNotice('sequence',m.notices&&m.notices.length?m.notices.join(' '):null);
275
355
  setViewerNotice('kspaceOverrideFailure',null);setKspaceSafetyWarning(m.kspaceSafety||null);
276
356
  setViewerNotice('m1',null);setViewerNotice('pns',null);setViewerNotice('pnsThreshold',null);
@@ -288,6 +368,8 @@ window.addEventListener('message',function(e){
288
368
  if(seqTiming&&seqTiming.trTimeSec>0){fitToFirstTR();}else{fit();}
289
369
  draw();drawKs();drawMinimap();
290
370
  setExportButtonEnabled(true);
371
+ }else if(m.type==='loadError'){
372
+ showSequenceLoadFailure(m.message||'The sequence could not be loaded.');
291
373
  }else if(m.type==='kspaceData'){
292
374
  applySerializedKspace(m.kspace);finishDangerousKspaceCalculation(null);drawKs();
293
375
  if(typeof kOpen!=='undefined'&&!kOpen)document.getElementById('kbtn').click();