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.
Files changed (35) hide show
  1. reflex_ohif_viewer-0.1.0/CHANGELOG.md +99 -0
  2. reflex_ohif_viewer-0.1.0/CONTRIBUTING.md +138 -0
  3. reflex_ohif_viewer-0.1.0/LICENSE +21 -0
  4. reflex_ohif_viewer-0.1.0/MANIFEST.in +13 -0
  5. reflex_ohif_viewer-0.1.0/PKG-INFO +493 -0
  6. reflex_ohif_viewer-0.1.0/README.md +455 -0
  7. reflex_ohif_viewer-0.1.0/SECURITY.md +66 -0
  8. reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/RxDicomViewer.jsx +899 -0
  9. reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/RxOhifViewer.jsx +126 -0
  10. reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/__init__.py +140 -0
  11. reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/_assets.py +54 -0
  12. reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/constants.py +406 -0
  13. reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/dicom_viewer.py +572 -0
  14. reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/dicom_viewer.pyi +217 -0
  15. reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/dicomweb.py +490 -0
  16. reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/ohif_config.py +361 -0
  17. reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/ohif_viewer.py +336 -0
  18. reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/ohif_viewer.pyi +156 -0
  19. reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/plugin.py +283 -0
  20. reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/py.typed +0 -0
  21. reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/rxCornerstoneRuntime.js +364 -0
  22. reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer/rxOhifUrl.js +132 -0
  23. reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer.egg-info/PKG-INFO +493 -0
  24. reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer.egg-info/SOURCES.txt +33 -0
  25. reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer.egg-info/dependency_links.txt +1 -0
  26. reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer.egg-info/requires.txt +11 -0
  27. reflex_ohif_viewer-0.1.0/custom_components/reflex_ohif_viewer.egg-info/top_level.txt +1 -0
  28. reflex_ohif_viewer-0.1.0/pyproject.toml +108 -0
  29. reflex_ohif_viewer-0.1.0/setup.cfg +4 -0
  30. reflex_ohif_viewer-0.1.0/tests/test_components.py +168 -0
  31. reflex_ohif_viewer-0.1.0/tests/test_dicomweb.py +71 -0
  32. reflex_ohif_viewer-0.1.0/tests/test_ohif_config.py +82 -0
  33. reflex_ohif_viewer-0.1.0/tests/test_plugin.py +117 -0
  34. reflex_ohif_viewer-0.1.0/tests/test_url_builder.py +163 -0
  35. 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