@biongff/vizarr 0.3.0-dev.2025-11-12.283baee9
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/.gitattributes +1 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +32 -0
- package/.github/ISSUE_TEMPLATE/documentation_update.md +16 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- package/.github/ISSUE_TEMPLATE/maintenance.md +19 -0
- package/.github/ISSUE_TEMPLATE/optimisation_request.md +23 -0
- package/.github/ISSUE_TEMPLATE/support_request.md +25 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +21 -0
- package/.github/dependabot.yml +13 -0
- package/.github/workflows/build_and_deploy.yml +29 -0
- package/.github/workflows/npm-publish-dev.yml +49 -0
- package/.github/workflows/npm-publish.yml +20 -0
- package/.github/workflows/release.yml +37 -0
- package/.github/workflows/test.yml +21 -0
- package/.releaserc.json +29 -0
- package/CITATION.cff +56 -0
- package/CONTRIBUTING.md +82 -0
- package/LICENSE +21 -0
- package/README.md +85 -0
- package/assets/favicon.ico +0 -0
- package/assets/favicon.png +0 -0
- package/assets/logo-wide.png +0 -0
- package/assets/logo-wide.svg +60 -0
- package/assets/logo.png +0 -0
- package/assets/logo.svg +36 -0
- package/assets/robots.txt +3 -0
- package/assets/screenshot.png +0 -0
- package/biome.json +34 -0
- package/index.html +24 -0
- package/main.ts +52 -0
- package/package.json +55 -0
- package/patches/@vivjs__loaders@0.17.2.patch +13 -0
- package/python/.python-version +1 -0
- package/python/README.md +14 -0
- package/python/Untitled.ipynb +40 -0
- package/python/deno.json +22 -0
- package/python/notebooks/IDR_example.ipynb +86 -0
- package/python/notebooks/README.md +35 -0
- package/python/notebooks/create_fixture.py +72 -0
- package/python/notebooks/getting_started.ipynb +217 -0
- package/python/notebooks/mandelbrot.ipynb +211 -0
- package/python/notebooks/spatial_transformations.ipynb +139 -0
- package/python/pyproject.toml +18 -0
- package/python/src/vizarr/__init__.py +10 -0
- package/python/src/vizarr/_widget.js +113 -0
- package/python/src/vizarr/_widget.py +67 -0
- package/python/uv.lock +2495 -0
- package/src/ZarrPixelSource.ts +182 -0
- package/src/codecs/jpeg2k.ts +22 -0
- package/src/codecs/register.ts +2 -0
- package/src/components/LayerController/AcquisitionController.tsx +46 -0
- package/src/components/LayerController/AddChannelButton.tsx +122 -0
- package/src/components/LayerController/AxisOptions.tsx +93 -0
- package/src/components/LayerController/AxisSlider.tsx +86 -0
- package/src/components/LayerController/AxisSliders.tsx +29 -0
- package/src/components/LayerController/ChannelController.tsx +84 -0
- package/src/components/LayerController/ChannelOptions.tsx +183 -0
- package/src/components/LayerController/ColorPalette.tsx +40 -0
- package/src/components/LayerController/Content.tsx +71 -0
- package/src/components/LayerController/Header.tsx +48 -0
- package/src/components/LayerController/Labels.tsx +73 -0
- package/src/components/LayerController/LayerFitToViewportButton.tsx +43 -0
- package/src/components/LayerController/LayerVisibilityButton.tsx +32 -0
- package/src/components/LayerController/OpacitySlider.tsx +25 -0
- package/src/components/LayerController/index.tsx +42 -0
- package/src/components/Menu.tsx +54 -0
- package/src/components/Viewer.tsx +136 -0
- package/src/hooks.ts +29 -0
- package/src/index.tsx +138 -0
- package/src/io.ts +276 -0
- package/src/layers/grid-layer.ts +214 -0
- package/src/layers/label-layer.ts +446 -0
- package/src/layers/viv-layers.ts +55 -0
- package/src/lru-store.ts +57 -0
- package/src/ome.ts +388 -0
- package/src/state.ts +202 -0
- package/src/theme.ts +80 -0
- package/src/types.ts +112 -0
- package/src/utils.ts +655 -0
- package/tsconfig.json +14 -0
- package/vite.config.js +47 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import * as vizarr from "https://hms-dbmi.github.io/vizarr/index.js";
|
|
2
|
+
import debounce from "https://esm.sh/just-debounce-it@3";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @template T
|
|
6
|
+
* @param {import("npm:@anywidget/types").AnyModel} model
|
|
7
|
+
* @param {any} payload
|
|
8
|
+
* @param {{ timeout?: number }} [options]
|
|
9
|
+
* @returns {Promise<{ data: T, buffers: DataView[] }>}
|
|
10
|
+
*/
|
|
11
|
+
function send(model, payload, { timeout = 3000 } = {}) {
|
|
12
|
+
let uuid = globalThis.crypto.randomUUID();
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
let timer = setTimeout(() => {
|
|
15
|
+
reject(new Error(`Promise timed out after ${timeout} ms`));
|
|
16
|
+
model.off("msg:custom", handler);
|
|
17
|
+
}, timeout);
|
|
18
|
+
/**
|
|
19
|
+
* @param {{ uuid: string, payload: T }} msg
|
|
20
|
+
* @param {DataView[]} buffers
|
|
21
|
+
*/
|
|
22
|
+
function handler(msg, buffers) {
|
|
23
|
+
if (!(msg.uuid === uuid)) return;
|
|
24
|
+
clearTimeout(timer);
|
|
25
|
+
resolve({ data: msg.payload, buffers });
|
|
26
|
+
model.off("msg:custom", handler);
|
|
27
|
+
}
|
|
28
|
+
model.on("msg:custom", handler);
|
|
29
|
+
model.send({ payload, uuid });
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {import("npm:@anywidget/types").AnyModel} model
|
|
35
|
+
* @param {string | { id: string }} source
|
|
36
|
+
*/
|
|
37
|
+
function get_source(model, source) {
|
|
38
|
+
if (typeof source === "string") {
|
|
39
|
+
return source;
|
|
40
|
+
}
|
|
41
|
+
// create a python
|
|
42
|
+
return {
|
|
43
|
+
/**
|
|
44
|
+
* @param {string} key
|
|
45
|
+
* @return {Promise<Uint8Array | undefined>}
|
|
46
|
+
*/
|
|
47
|
+
async get(key) {
|
|
48
|
+
const { data, buffers } = await send(model, {
|
|
49
|
+
type: "get",
|
|
50
|
+
source_id: source.id,
|
|
51
|
+
key,
|
|
52
|
+
});
|
|
53
|
+
if (!data.success) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
return new Uint8Array(buffers[0].buffer);
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @typedef Model
|
|
63
|
+
* @property {string} height
|
|
64
|
+
* @property {ViewState=} view_state
|
|
65
|
+
* @property {{ source: string | { id: string }}[]} _configs
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @typedef ViewState
|
|
70
|
+
* @property {number} zoom
|
|
71
|
+
* @property {[x: number, y: number]} target
|
|
72
|
+
*/
|
|
73
|
+
|
|
74
|
+
export default {
|
|
75
|
+
/** @type {import("npm:@anywidget/types").Render<Model>} */
|
|
76
|
+
async render({ model, el }) {
|
|
77
|
+
let div = document.createElement("div");
|
|
78
|
+
{
|
|
79
|
+
div.style.height = model.get("height");
|
|
80
|
+
div.style.backgroundColor = "black";
|
|
81
|
+
model.on("change:height", () => {
|
|
82
|
+
div.style.height = model.get("height");
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
let viewer = await vizarr.createViewer(div);
|
|
86
|
+
{
|
|
87
|
+
model.on("change:view_state", () => {
|
|
88
|
+
viewer.setViewState(model.get("view_state"));
|
|
89
|
+
});
|
|
90
|
+
viewer.on(
|
|
91
|
+
"viewStateChange",
|
|
92
|
+
debounce((/** @type {ViewState} */ update) => {
|
|
93
|
+
model.set("view_state", update);
|
|
94
|
+
model.save_changes();
|
|
95
|
+
}, 200),
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
{
|
|
99
|
+
// sources are append-only now
|
|
100
|
+
for (const config of model.get("_configs")) {
|
|
101
|
+
const source = get_source(model, config.source);
|
|
102
|
+
viewer.addImage({ ...config, source });
|
|
103
|
+
}
|
|
104
|
+
model.on("change:_configs", () => {
|
|
105
|
+
const last = model.get("_configs").at(-1);
|
|
106
|
+
if (!last) return;
|
|
107
|
+
const source = get_source(model, last.source);
|
|
108
|
+
viewer.addImage({ ...last, source });
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
el.appendChild(div);
|
|
112
|
+
},
|
|
113
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import anywidget
|
|
2
|
+
import traitlets
|
|
3
|
+
import pathlib
|
|
4
|
+
|
|
5
|
+
import zarr
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
__all__ = ["Viewer"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _store_keyprefix(obj):
|
|
12
|
+
# Just grab the store and key_prefix from zarr.Array and zarr.Group objects
|
|
13
|
+
if isinstance(obj, (zarr.Array, zarr.Group)):
|
|
14
|
+
return obj.store, obj._key_prefix
|
|
15
|
+
|
|
16
|
+
if isinstance(obj, np.ndarray):
|
|
17
|
+
# Create an in-memory store, and write array as as single chunk
|
|
18
|
+
store = {}
|
|
19
|
+
arr = zarr.create(
|
|
20
|
+
store=store, shape=obj.shape, chunks=obj.shape, dtype=obj.dtype
|
|
21
|
+
)
|
|
22
|
+
arr[:] = obj
|
|
23
|
+
return store, ""
|
|
24
|
+
|
|
25
|
+
if hasattr(obj, "__getitem__") and hasattr(obj, "__contains__"):
|
|
26
|
+
return obj, ""
|
|
27
|
+
|
|
28
|
+
raise TypeError("Cannot normalize store path")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Viewer(anywidget.AnyWidget):
|
|
32
|
+
_esm = pathlib.Path(__file__).parent / "_widget.js"
|
|
33
|
+
_configs = traitlets.List().tag(sync=True)
|
|
34
|
+
view_state = traitlets.Dict().tag(sync=True)
|
|
35
|
+
height = traitlets.Unicode("500px").tag(sync=True)
|
|
36
|
+
|
|
37
|
+
def __init__(self, **kwargs):
|
|
38
|
+
super().__init__(**kwargs)
|
|
39
|
+
self._store_paths = []
|
|
40
|
+
self.on_msg(self._handle_custom_msg)
|
|
41
|
+
|
|
42
|
+
def _handle_custom_msg(self, msg, buffers):
|
|
43
|
+
store, key_prefix = self._store_paths[msg["payload"]["source_id"]]
|
|
44
|
+
key = key_prefix + msg["payload"]["key"].lstrip("/")
|
|
45
|
+
|
|
46
|
+
if msg["payload"]["type"] == "has":
|
|
47
|
+
self.send({"uuid": msg["uuid"], "payload": key in store})
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
if msg["payload"]["type"] == "get":
|
|
51
|
+
try:
|
|
52
|
+
buffers = [store[key]]
|
|
53
|
+
except KeyError:
|
|
54
|
+
buffers = []
|
|
55
|
+
self.send(
|
|
56
|
+
{"uuid": msg["uuid"], "payload": {"success": len(buffers) == 1}},
|
|
57
|
+
buffers,
|
|
58
|
+
)
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
def add_image(self, source, **config):
|
|
62
|
+
if not isinstance(source, str):
|
|
63
|
+
store, key_prefix = _store_keyprefix(source)
|
|
64
|
+
source = {"id": len(self._store_paths)}
|
|
65
|
+
self._store_paths.append((store, key_prefix))
|
|
66
|
+
config["source"] = source
|
|
67
|
+
self._configs = self._configs + [config]
|