@niivue/nv-ext-save-html 1.0.0-rc.4 → 1.0.0-rc.40
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.
- package/README.md +2 -2
- package/dist/gzip.d.ts +21 -0
- package/dist/gzip.test.d.ts +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/nv-ext-save-html.js +128 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -13,10 +13,10 @@ Requires `@niivue/niivue` as a peer dependency.
|
|
|
13
13
|
## Usage
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
|
-
import
|
|
16
|
+
import NiiVue from '@niivue/niivue'
|
|
17
17
|
import { saveHTML } from '@niivue/nv-ext-save-html'
|
|
18
18
|
|
|
19
|
-
const nv = new
|
|
19
|
+
const nv = new NiiVue()
|
|
20
20
|
await nv.attachTo('gl1')
|
|
21
21
|
await nv.loadVolumes([{ url: 'brain.nii.gz' }])
|
|
22
22
|
|
package/dist/gzip.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gzip helpers built on the Web Streams API (available in all modern browsers).
|
|
3
|
+
*
|
|
4
|
+
* Split out of `index.ts` so it can be unit tested: `index.ts` imports
|
|
5
|
+
* `@niivue/niivue`, whose module graph uses Vite's `import.meta.glob` and cannot
|
|
6
|
+
* be loaded by the Bun test runner.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Gzip-compress a Uint8Array.
|
|
10
|
+
*
|
|
11
|
+
* Start consuming `readable` BEFORE awaiting the write/close. A CompressionStream
|
|
12
|
+
* is a TransformStream whose readable side applies backpressure once its queue
|
|
13
|
+
* fills; if nothing is reading, `writer.write()` / `writer.close()` never settle
|
|
14
|
+
* and this hangs forever. In Chromium that happens for inputs as small as ~1 MB —
|
|
15
|
+
* i.e. every real document — so the read must already be in flight. (This is the
|
|
16
|
+
* same shape niivue's own `codecs/NVGz.ts` compress() uses.)
|
|
17
|
+
*
|
|
18
|
+
* `chunk` is a copy: a view backed by a SharedArrayBuffer (see the extension
|
|
19
|
+
* context's `acquireSharedBuffer`) cannot be enqueued directly.
|
|
20
|
+
*/
|
|
21
|
+
export declare function gzipCompress(data: Uint8Array): Promise<Uint8Array>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { default as NiiVue } from '@niivue/niivue';
|
|
2
|
+
export interface SaveHTMLOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Fully self-contained niivue ESM bundle as a source code string.
|
|
5
|
+
*
|
|
6
|
+
* The bundle must export `NiiVue` as its **default export** and must
|
|
7
|
+
* have all dependencies (cbor-x, gl-matrix, nifti-reader-js, etc.) inlined.
|
|
8
|
+
*
|
|
9
|
+
* Build one with e.g. `vite build` using `rollupOptions.external: []`.
|
|
10
|
+
*/
|
|
11
|
+
niivueBundleSource: string;
|
|
12
|
+
/** Canvas element ID in the generated HTML (default: `"gl1"`). */
|
|
13
|
+
canvasId?: string;
|
|
14
|
+
/** HTML `<title>` (default: `"NiiVue Scene"`). */
|
|
15
|
+
title?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Generate a self-contained HTML string that embeds the current NiiVue scene.
|
|
19
|
+
*
|
|
20
|
+
* The returned HTML includes the niivue library and all loaded data (volumes,
|
|
21
|
+
* meshes, drawings, settings). Opening the HTML file in a browser will
|
|
22
|
+
* recreate the scene — no server or external files needed.
|
|
23
|
+
*
|
|
24
|
+
* @param nv - The NiiVue instance whose scene to export.
|
|
25
|
+
* @param options - See {@link SaveHTMLOptions}.
|
|
26
|
+
* @returns A complete HTML document as a string.
|
|
27
|
+
*/
|
|
28
|
+
export declare function generateHTML(nv: NiiVue, options: SaveHTMLOptions): Promise<string>;
|
|
29
|
+
/**
|
|
30
|
+
* Download the current NiiVue scene as a self-contained HTML file.
|
|
31
|
+
*
|
|
32
|
+
* Convenience wrapper around {@link generateHTML} that triggers a browser
|
|
33
|
+
* download.
|
|
34
|
+
*
|
|
35
|
+
* @param nv - The NiiVue instance whose scene to export.
|
|
36
|
+
* @param filename - Name of the downloaded file (default: `"scene.html"`).
|
|
37
|
+
* @param options - See {@link SaveHTMLOptions}.
|
|
38
|
+
*/
|
|
39
|
+
export declare function saveHTML(nv: NiiVue, filename: string | undefined, options: SaveHTMLOptions): Promise<void>;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
async function l(e) {
|
|
2
|
+
const a = new CompressionStream("gzip"), t = new Uint8Array(e.byteLength);
|
|
3
|
+
t.set(e);
|
|
4
|
+
const n = new Response(a.readable).arrayBuffer(), o = a.writable.getWriter();
|
|
5
|
+
return await o.write(t), await o.close(), new Uint8Array(await n);
|
|
6
|
+
}
|
|
7
|
+
function d(e) {
|
|
8
|
+
const t = [];
|
|
9
|
+
for (let n = 0; n < e.length; n += 32768)
|
|
10
|
+
t.push(String.fromCharCode(...e.subarray(n, n + 32768)));
|
|
11
|
+
return btoa(t.join(""));
|
|
12
|
+
}
|
|
13
|
+
function u(e) {
|
|
14
|
+
return e.replace(/<\/script/gi, "<\\/script");
|
|
15
|
+
}
|
|
16
|
+
function p(e) {
|
|
17
|
+
return e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
18
|
+
}
|
|
19
|
+
function m(e, a, t, n) {
|
|
20
|
+
return `<!DOCTYPE html>
|
|
21
|
+
<html lang="en">
|
|
22
|
+
<head>
|
|
23
|
+
<meta charset="utf-8" />
|
|
24
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
25
|
+
<title>${p(n)}</title>
|
|
26
|
+
<style>
|
|
27
|
+
html, body {
|
|
28
|
+
margin: 0;
|
|
29
|
+
padding: 0;
|
|
30
|
+
height: 100%;
|
|
31
|
+
width: 100%;
|
|
32
|
+
overflow: hidden;
|
|
33
|
+
background: #000;
|
|
34
|
+
font-family: system-ui, Arial, Helvetica, sans-serif;
|
|
35
|
+
user-select: none;
|
|
36
|
+
}
|
|
37
|
+
main {
|
|
38
|
+
position: relative;
|
|
39
|
+
width: 100%;
|
|
40
|
+
height: 100%;
|
|
41
|
+
}
|
|
42
|
+
canvas {
|
|
43
|
+
position: absolute;
|
|
44
|
+
width: 100%;
|
|
45
|
+
height: 100%;
|
|
46
|
+
cursor: crosshair;
|
|
47
|
+
}
|
|
48
|
+
canvas:focus {
|
|
49
|
+
outline: none;
|
|
50
|
+
}
|
|
51
|
+
</style>
|
|
52
|
+
</head>
|
|
53
|
+
<body>
|
|
54
|
+
<main>
|
|
55
|
+
<canvas id="${t}"></canvas>
|
|
56
|
+
</main>
|
|
57
|
+
|
|
58
|
+
<!-- NiiVue library bundle (inert — not executed by the browser) -->
|
|
59
|
+
<script id="__niivue_bundle__" type="text/plain">
|
|
60
|
+
${u(e)}
|
|
61
|
+
<\/script>
|
|
62
|
+
|
|
63
|
+
<!-- NVD document data as gzip-compressed, base-64-encoded CBOR (inert) -->
|
|
64
|
+
<script id="__nvd_data__" type="text/plain">
|
|
65
|
+
${a}
|
|
66
|
+
<\/script>
|
|
67
|
+
|
|
68
|
+
<!-- Loader: import the bundle, decode the document, restore the scene -->
|
|
69
|
+
<script type="module">
|
|
70
|
+
// 1. Dynamic-import the embedded niivue bundle
|
|
71
|
+
const bundleSrc = document.getElementById("__niivue_bundle__").textContent;
|
|
72
|
+
const blob = new Blob([bundleSrc], { type: "text/javascript" });
|
|
73
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
74
|
+
const niivueModule = await import(blobUrl);
|
|
75
|
+
URL.revokeObjectURL(blobUrl);
|
|
76
|
+
const NiiVue = niivueModule.default ?? niivueModule.NiiVue;
|
|
77
|
+
|
|
78
|
+
// 2. Decode base-64, gzip-decompress, and wrap as a File
|
|
79
|
+
const base64 = document.getElementById("__nvd_data__").textContent.trim();
|
|
80
|
+
const raw = atob(base64);
|
|
81
|
+
const compressed = new Uint8Array(raw.length);
|
|
82
|
+
for (let i = 0; i < raw.length; i++) compressed[i] = raw.charCodeAt(i);
|
|
83
|
+
const ds = new DecompressionStream("gzip");
|
|
84
|
+
const writer = ds.writable.getWriter();
|
|
85
|
+
writer.write(compressed);
|
|
86
|
+
writer.close();
|
|
87
|
+
const bytes = new Uint8Array(await new Response(ds.readable).arrayBuffer());
|
|
88
|
+
const file = new File([bytes], "scene.nvd");
|
|
89
|
+
|
|
90
|
+
// 3. Create NiiVue, attach to canvas, and load the document
|
|
91
|
+
const nv = new NiiVue();
|
|
92
|
+
await nv.attachTo("${t}");
|
|
93
|
+
await nv.loadDocument(file);
|
|
94
|
+
// The serialized document has an empty matcap to avoid baking in
|
|
95
|
+
// environment-specific URLs. Restore the default matcap from this bundle.
|
|
96
|
+
if (nv.opts?.matcaps) {
|
|
97
|
+
const defaultMatcap = Object.values(nv.opts.matcaps)[0];
|
|
98
|
+
if (defaultMatcap) await nv.loadMatcap(defaultMatcap);
|
|
99
|
+
}
|
|
100
|
+
<\/script>
|
|
101
|
+
</body>
|
|
102
|
+
</html>`;
|
|
103
|
+
}
|
|
104
|
+
async function b(e, a) {
|
|
105
|
+
const {
|
|
106
|
+
niivueBundleSource: t,
|
|
107
|
+
canvasId: n = "gl1",
|
|
108
|
+
title: o = "NiiVue Scene"
|
|
109
|
+
} = a;
|
|
110
|
+
if (!t)
|
|
111
|
+
throw new Error(
|
|
112
|
+
"nv-ext-save-html: niivueBundleSource is required. Provide the full source of a self-contained niivue ESM bundle."
|
|
113
|
+
);
|
|
114
|
+
const r = e.volumeMatcap;
|
|
115
|
+
e.model.volume.matcap = "";
|
|
116
|
+
const i = e.serializeDocument();
|
|
117
|
+
e.model.volume.matcap = r;
|
|
118
|
+
const c = await l(i), s = d(c);
|
|
119
|
+
return m(t, s, n, o);
|
|
120
|
+
}
|
|
121
|
+
async function h(e, a = "scene.html", t) {
|
|
122
|
+
const n = await b(e, t), o = new Blob([n], { type: "text/html" }), r = URL.createObjectURL(o), i = document.createElement("a");
|
|
123
|
+
i.href = r, i.download = a, document.body.appendChild(i), i.click(), document.body.removeChild(i), URL.revokeObjectURL(r);
|
|
124
|
+
}
|
|
125
|
+
export {
|
|
126
|
+
b as generateHTML,
|
|
127
|
+
h as saveHTML
|
|
128
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@niivue/nv-ext-save-html",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.40",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Export a NiiVue scene as a self-contained HTML file",
|
|
6
6
|
"main": "./dist/nv-ext-save-html.js",
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
"dist"
|
|
17
17
|
],
|
|
18
18
|
"peerDependencies": {
|
|
19
|
-
"@niivue/niivue": "1.0.0-rc.
|
|
19
|
+
"@niivue/niivue": "1.0.0-rc.40"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
|
-
"@niivue/niivue": "1.0.0-rc.
|
|
22
|
+
"@niivue/niivue": "1.0.0-rc.40",
|
|
23
23
|
"typescript": "~5.8.3",
|
|
24
24
|
"vite": "^6.3.2",
|
|
25
25
|
"vite-plugin-dts": "^4.5.4"
|