reflex-ohif-viewer 0.1.0__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.
- reflex_ohif_viewer-0.1.0/CHANGELOG.md +99 -0
- reflex_ohif_viewer-0.1.0/CONTRIBUTING.md +138 -0
- reflex_ohif_viewer-0.1.0/LICENSE +21 -0
- reflex_ohif_viewer-0.1.0/MANIFEST.in +13 -0
- reflex_ohif_viewer-0.1.0/PKG-INFO +493 -0
- reflex_ohif_viewer-0.1.0/README.md +455 -0
- reflex_ohif_viewer-0.1.0/SECURITY.md +66 -0
- reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/RxDicomViewer.jsx +899 -0
- reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/RxOhifViewer.jsx +126 -0
- reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/__init__.py +140 -0
- reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/_assets.py +54 -0
- reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/constants.py +406 -0
- reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/dicom_viewer.py +572 -0
- reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/dicom_viewer.pyi +217 -0
- reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/dicomweb.py +490 -0
- reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/ohif_config.py +361 -0
- reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/ohif_viewer.py +336 -0
- reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/ohif_viewer.pyi +156 -0
- reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/plugin.py +283 -0
- reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/py.typed +0 -0
- reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/rxCornerstoneRuntime.js +364 -0
- reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/rxOhifUrl.js +132 -0
- reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer.egg-info/PKG-INFO +493 -0
- reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer.egg-info/SOURCES.txt +33 -0
- reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer.egg-info/dependency_links.txt +1 -0
- reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer.egg-info/requires.txt +11 -0
- reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer.egg-info/top_level.txt +1 -0
- reflex_ohif_viewer-0.1.0/pyproject.toml +108 -0
- reflex_ohif_viewer-0.1.0/setup.cfg +4 -0
- reflex_ohif_viewer-0.1.0/tests/test_components.py +168 -0
- reflex_ohif_viewer-0.1.0/tests/test_dicomweb.py +71 -0
- reflex_ohif_viewer-0.1.0/tests/test_ohif_config.py +82 -0
- reflex_ohif_viewer-0.1.0/tests/test_plugin.py +117 -0
- reflex_ohif_viewer-0.1.0/tests/test_url_builder.py +163 -0
- reflex_ohif_viewer-0.1.0/tests/test_url_builder_parity.py +129 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project uses
|
|
5
|
+
[semantic versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [0.1.0] — 2026-09-17
|
|
8
|
+
|
|
9
|
+
First release.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **`ohif_viewer`** — embeds a self-hosted OHIF Viewer v3 build in an iframe,
|
|
14
|
+
with every viewer and study-list query parameter exposed as a prop:
|
|
15
|
+
studies and priors, series filters, initial series and instance, hanging
|
|
16
|
+
protocol and stage, token, `configUrl`, customization, theme, debug and the
|
|
17
|
+
`multimonitor` / `screenNumber` pair.
|
|
18
|
+
Events: `on_viewer_load`, `on_url_change`, `on_viewer_message`.
|
|
19
|
+
- **`build_ohif_url`** — the same URL builder as a plain function, so links can
|
|
20
|
+
be computed, logged and unit-tested server-side. The Python and JavaScript
|
|
21
|
+
implementations are pinned to each other by a parity test that runs the
|
|
22
|
+
JavaScript one under Node.
|
|
23
|
+
- **`dicom_viewer`** — a native Cornerstone3D 5.10.6 viewport: stack, volume
|
|
24
|
+
(MPR) and 3D volume rendering; 37 tools; window/level, colormap, inversion and
|
|
25
|
+
cine driven from props; a four-corner DICOM overlay; and a DICOMweb loader
|
|
26
|
+
that builds `wadors:` imageIds and caches series metadata. Events report the
|
|
27
|
+
viewer's readiness, slice, window/level, annotations and errors back to Reflex
|
|
28
|
+
state.
|
|
29
|
+
- **Imperative API** — `set_tool`, `jump_to_slice`, `scroll`, `set_window`,
|
|
30
|
+
`set_window_preset`, `set_colormap`, `set_zoom`, `rotate`, `flip`,
|
|
31
|
+
`reset_camera`, `reset_properties`, `play_cine`, `stop_cine`,
|
|
32
|
+
`clear_measurements`, `remove_measurement` and the general `viewer_call`.
|
|
33
|
+
- **`DicomWebClient`** — QIDO-RS study, series and instance search plus WADO-RS
|
|
34
|
+
metadata, so a study browser can be ordinary Reflex state.
|
|
35
|
+
- **`OhifAppConfig` / `DicomWebDataSource`** — build OHIF's `window.config` as
|
|
36
|
+
JavaScript (for `APP_CONFIG` / `app-config.js`), as JSON (for `?configUrl=`)
|
|
37
|
+
or as a plain mapping, plus `docker_run_command` for a ready-to-run container.
|
|
38
|
+
- **`CornerstonePlugin`** — configures Vite for Cornerstone: keeps the
|
|
39
|
+
Cornerstone packages out of dependency pre-bundling, forces the CommonJS
|
|
40
|
+
leaves through it, declares a browser `events` implementation, treats `.wasm`
|
|
41
|
+
as an asset, emits ES-module workers, and copies the codec `.wasm` files into
|
|
42
|
+
`public/cs-wasm/`.
|
|
43
|
+
- **Constants** — grouped tool lists, window presets, colormaps, volume-
|
|
44
|
+
rendering presets, OHIF modes and hanging protocol ids, and four public demo
|
|
45
|
+
studies whose series UIDs were verified against the live server.
|
|
46
|
+
- **A five-page demo app** — overview, native viewport with a live measurement
|
|
47
|
+
table, MPR, the OHIF iframe with a URL builder, and a configuration generator.
|
|
48
|
+
|
|
49
|
+
### Project
|
|
50
|
+
|
|
51
|
+
- **Continuous integration** — `ci.yml` runs ruff, checks that the generated
|
|
52
|
+
`.pyi` stubs still match the component source, runs the suite on Python 3.10
|
|
53
|
+
to 3.13 with Node present so the URL-builder parity test does not skip, and
|
|
54
|
+
builds the distribution, installs the wheel into a clean environment and
|
|
55
|
+
imports it to prove the frontend assets are really packaged.
|
|
56
|
+
- **Security validation** — `security.yml` runs Bandit over the published
|
|
57
|
+
package, `pip-audit` over the installed dependency tree, Gitleaks over the
|
|
58
|
+
full history, and a dependency review on every pull request; `codeql.yml`
|
|
59
|
+
analyses both the Python and the JavaScript halves. The two scheduled runs
|
|
60
|
+
catch advisories published against code that has not changed.
|
|
61
|
+
- **Release automation** — `release.yml` is triggered by a `vX.Y.Z` tag. It
|
|
62
|
+
refuses to run unless the tag, `pyproject.toml` and this changelog agree,
|
|
63
|
+
re-runs the checks against the tagged commit, then publishes to PyPI through
|
|
64
|
+
Trusted Publishing (OpenID Connect — the repository holds no PyPI token) with
|
|
65
|
+
build attestations, and opens a GitHub Release carrying that version's
|
|
66
|
+
changelog section.
|
|
67
|
+
- **Packaging** — the licence is declared as a PEP 639 SPDX expression rather
|
|
68
|
+
than the deprecated classifier, and `MANIFEST.in` puts the changelog, the
|
|
69
|
+
security policy and the contributing guide into the sdist.
|
|
70
|
+
|
|
71
|
+
### Security notes
|
|
72
|
+
|
|
73
|
+
- `viewer_call` validates its `method` argument as a JavaScript identifier. The
|
|
74
|
+
viewport id and the arguments are JSON-encoded on their way into the generated
|
|
75
|
+
script, but a method name is a property access and cannot be, so it was the
|
|
76
|
+
one place a caller could have spliced script into the page.
|
|
77
|
+
|
|
78
|
+
### Notes on the upstream projects
|
|
79
|
+
|
|
80
|
+
Checked against the OHIF 3.13.8 source and the published npm tarballs rather
|
|
81
|
+
than the documentation:
|
|
82
|
+
|
|
83
|
+
- `@ohif/app` on npm is a prebuilt static site. Its `main` points at
|
|
84
|
+
`dist/index.umd.js`, a file the tarball does not contain, so
|
|
85
|
+
`import App from '@ohif/app'` has never worked. There is no web component
|
|
86
|
+
either.
|
|
87
|
+
- OHIF defines no postMessage protocol. `ohif_viewer` therefore builds a URL and
|
|
88
|
+
nothing more; `on_viewer_message` exists for deployments that add their own
|
|
89
|
+
OHIF extension.
|
|
90
|
+
- The `:latest` Docker tag and the `latest` npm dist-tag both lag the real
|
|
91
|
+
stable release — pin `ohif/app:v3.13.8`.
|
|
92
|
+
- `@cornerstonejs/streaming-image-volume-loader` was merged into
|
|
93
|
+
`@cornerstonejs/core` in 2.x and is not installed.
|
|
94
|
+
- No existing React wrapper for Cornerstone3D was usable:
|
|
95
|
+
`@cornerstonejs/react` and `react-cornerstone3d` do not exist on npm,
|
|
96
|
+
`react-cornerstone-viewport` targets the abandoned legacy cornerstone, and
|
|
97
|
+
`@ohif/ui-next` contains no viewport code.
|
|
98
|
+
|
|
99
|
+
[0.1.0]: https://github.com/ecrespo/reflex-ohif-viewer/releases/tag/v0.1.0
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Getting set up
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
git clone git@github.com:ecrespo/reflex-ohif-viewer.git
|
|
7
|
+
cd reflex-ohif-viewer
|
|
8
|
+
uv venv && source .venv/bin/activate
|
|
9
|
+
uv pip install -e ".[dev]"
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Node is optional but recommended: one test runs the JavaScript URL builder under
|
|
13
|
+
Node to check it against the Python one, and skips itself when Node is missing.
|
|
14
|
+
|
|
15
|
+
## The checks CI runs
|
|
16
|
+
|
|
17
|
+
Run these before opening a pull request; they are exactly what
|
|
18
|
+
`.github/workflows/ci.yml` and `security.yml` run.
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
ruff check . # lint
|
|
22
|
+
ruff format --check . # formatting
|
|
23
|
+
pytest -q # tests
|
|
24
|
+
bandit -c pyproject.toml -r custom_components # Python SAST
|
|
25
|
+
pip-audit # dependency advisories
|
|
26
|
+
python -m build && twine check dist/* # the artifacts PyPI will get
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Branching and releases
|
|
30
|
+
|
|
31
|
+
- `main` is the released branch and is protected: no direct pushes, no force
|
|
32
|
+
pushes, no deletion, and every one of the CI, security and CodeQL checks has
|
|
33
|
+
to be green before a pull request can merge. A pull request is required but an
|
|
34
|
+
approving review is not, so a solo maintainer is not locked out; the branch
|
|
35
|
+
also has to be up to date with `main` before it merges.
|
|
36
|
+
- `develop` is the integration branch. Open your pull request against it.
|
|
37
|
+
- A release is a pull request from `develop` to `main`, then a `vX.Y.Z` tag on
|
|
38
|
+
`main`. The tag is what triggers `.github/workflows/release.yml`, which builds
|
|
39
|
+
the artifacts, publishes them to PyPI through Trusted Publishing and opens a
|
|
40
|
+
GitHub Release.
|
|
41
|
+
|
|
42
|
+
Every release needs a new version number in `pyproject.toml` — PyPI refuses to
|
|
43
|
+
replace a version that already exists — and a matching `CHANGELOG.md` section.
|
|
44
|
+
The release workflow refuses to run if the tag and `pyproject.toml` disagree.
|
|
45
|
+
|
|
46
|
+
## Publishing
|
|
47
|
+
|
|
48
|
+
Nothing in this repository holds a PyPI credential. The release workflow
|
|
49
|
+
authenticates to PyPI over OpenID Connect through
|
|
50
|
+
[Trusted Publishing](https://docs.pypi.org/trusted-publishers/), so the
|
|
51
|
+
`pypi` job mints a short-lived token for one upload and there is nothing to
|
|
52
|
+
rotate or leak.
|
|
53
|
+
|
|
54
|
+
That needs one entry on PyPI, created once by the project owner at
|
|
55
|
+
<https://pypi.org/manage/account/publishing/> (for the very first release, use
|
|
56
|
+
the *pending publisher* form — the project does not exist on PyPI yet):
|
|
57
|
+
|
|
58
|
+
| Field | Value |
|
|
59
|
+
|---|---|
|
|
60
|
+
| PyPI Project Name | `reflex-ohif-viewer` |
|
|
61
|
+
| Owner | `ecrespo` |
|
|
62
|
+
| Repository name | `reflex-ohif-viewer` |
|
|
63
|
+
| Workflow name | `release.yml` |
|
|
64
|
+
| Environment name | `pypi` |
|
|
65
|
+
|
|
66
|
+
The `pypi` environment also has to exist in this repository, under
|
|
67
|
+
*Settings → Environments*. It is restricted to a `v*.*.*` tag deployment branch
|
|
68
|
+
policy, so nothing but a version tag can reach it — a run from a branch cannot
|
|
69
|
+
publish even if the workflow were changed to try. Add a required reviewer to the
|
|
70
|
+
environment if you want a human to approve every upload; the workflow will wait
|
|
71
|
+
on it.
|
|
72
|
+
|
|
73
|
+
To cut a release:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# On develop: bump the version and write the changelog section.
|
|
77
|
+
$EDITOR pyproject.toml CHANGELOG.md
|
|
78
|
+
git commit -am "chore: release 0.2.0"
|
|
79
|
+
# Open and merge the pull request into main, then:
|
|
80
|
+
git checkout main && git pull
|
|
81
|
+
git tag -a v0.2.0 -m "v0.2.0"
|
|
82
|
+
git push origin v0.2.0
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The tag is the trigger. The workflow re-runs the lint and tests against the
|
|
86
|
+
tagged commit, builds the sdist and wheel, publishes them to PyPI with build
|
|
87
|
+
attestations, and opens a GitHub Release whose notes are that version's
|
|
88
|
+
changelog section.
|
|
89
|
+
|
|
90
|
+
### Gallery listing
|
|
91
|
+
|
|
92
|
+
The [Reflex component gallery](https://reflex.dev/docs/custom-components/) is a
|
|
93
|
+
separate registry from PyPI, and registering is manual and done once:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
reflex login
|
|
97
|
+
reflex component share
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
It asks for the published package name (`reflex-ohif-viewer`) and, optionally, a
|
|
101
|
+
preview image and a demo URL. Publish to PyPI first — `share` does not check
|
|
102
|
+
that the package exists.
|
|
103
|
+
|
|
104
|
+
## Working on the component
|
|
105
|
+
|
|
106
|
+
The two Python component classes have generated `.pyi` stubs next to them,
|
|
107
|
+
marked `DO NOT EDIT`. After adding or changing a prop, regenerate them:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
python -c "from reflex.utils.pyi_generator import PyiGenerator; \
|
|
111
|
+
PyiGenerator().scan_all(['custom_components/reflex_ohif_viewer'])"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
CI checks that the committed stubs match the source, so a forgotten regeneration
|
|
115
|
+
fails the build rather than shipping stale autocomplete.
|
|
116
|
+
|
|
117
|
+
If you touch `rxOhifUrl.js` or `build_ohif_url`, keep them in step —
|
|
118
|
+
`tests/test_url_builder_parity.py` runs both and compares the result.
|
|
119
|
+
|
|
120
|
+
## Trying it out
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
cd ohif_viewer_demo
|
|
124
|
+
reflex run
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The demo defaults to the public OHIF DICOMweb server. The `/ohif` page needs a
|
|
128
|
+
running OHIF build:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
docker run -d --name ohif -p 3001:80 ohif/app:v3.13.8
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Scope
|
|
135
|
+
|
|
136
|
+
This package wraps OHIF and Cornerstone3D; it does not fork them. Bugs in the
|
|
137
|
+
rendering itself belong upstream. What belongs here is the Reflex surface: props,
|
|
138
|
+
events, the URL and config builders, the DICOMweb client and the Vite plugin.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ernesto Crespo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# The wheel takes its extra files from [tool.setuptools.package-data]; the sdist
|
|
2
|
+
# needs them named here as well, along with the documentation a source release
|
|
3
|
+
# should carry.
|
|
4
|
+
include README.md
|
|
5
|
+
include CHANGELOG.md
|
|
6
|
+
include LICENSE
|
|
7
|
+
include SECURITY.md
|
|
8
|
+
include CONTRIBUTING.md
|
|
9
|
+
recursive-include custom_components/reflex_ohif_viewer *.js *.jsx *.pyi
|
|
10
|
+
include custom_components/reflex_ohif_viewer/py.typed
|
|
11
|
+
recursive-exclude custom_components *.egg-info
|
|
12
|
+
prune ohif_viewer_demo/.web
|
|
13
|
+
prune ohif_viewer_demo/.states
|