This commit is contained in:
mario
2025-03-07 13:47:44 +07:00
commit c4efec5a14
3358 changed files with 303774 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
{
"label": "Development",
"position": 5
}

View File

@@ -0,0 +1,205 @@
---
sidebar_position: 2
sidebar_label: Architecture
---
# Architecture
In order to achieve a platform that can support various workflows and be
extensible for the foreseeable future we went through extensive planning of
possible use cases and decided to significantly change and improve the
architecture.
Below, we aim to demystify that complexity by providing insight into how
`OHIF Platform` is architected, and the role each of its dependent libraries
plays.
## Overview
The [OHIF Medical Image Viewing Platform][viewers-project] is maintained as a
[`monorepo`][monorepo]. This means that this repository, instead of containing a
single project, contains many projects. If you explore our project structure,
you'll see the following:
```bash
├── extensions
│ ├── default # default functionalities
│ ├── cornerstone # 2D/3D images w/ Cornerstonejs
│ ├── cornerstone-dicom-sr # Structured reports
│ ├── measurement-tracking # measurement tracking
│ └── dicom-pdf # View DICOM wrapped PDFs in viewport
| # and many more ...
├── modes
│ └── longitudinal # longitudinal measurement tracking mode
| └── basic-dev-mode # basic viewer with Cornerstone (a developer focused mode)
| # and many more
├── platform
│ ├── core # Business Logic
│ ├── i18n # Internationalization Support
│ ├── ui # React component library
│ └── app # Connects platform and extension projects
├── ... # misc. shared configuration
├── lerna.json # MonoRepo (Lerna) settings
├── package.json # Shared devDependencies and commands
└── README.md
```
OHIF v3 is composed of the following components, described in detail in further
sections:
- `@ohif/app`: The core framework that controls extension registration, mode
composition and routing.
- `@ohif/core`: A library of useful and reusable medical imaging functionality
for the web.
- `@ohif/ui`: A library of reusable components to build OHIF-styled applications
with.
- `Extensions`: A set of building blocks for building applications. The OHIF org
maintains a few core libraries.
- `Modes`: Configuration objects that tell @ohif/app how to compose
extensions to build applications on different routes of the platform.
## Extensions
The `extensions` directory contains many packages that provide essential
functionalities such as rendering, study/series browsers, measurement tracking
that modes can consume to enable a certain workflow. Extensions have had their
behavior changed in `OHIF-v3` and their api is expanded. In summary:
> In `OHIF-v3`, extensions no longer automatically hook themselves to the app.
> Now, registering an extension makes its component available to `modes` that
> wish to use them. Basically, extensions in `OHIF-v3` are **building blocks**
> for building applications.
OHIF team maintains several high value and commonly used functionalities in its
own extensions. For a list of extensions maintained by OHIF,
[check out this helpful table](../platform/extensions/index.md#maintained-extensions).
As an example `default` extension provides a default viewer layout, a
study/series browser and a datasource that maps to a DICOMWeb compliant backend.
[Click here to read more about extensions!](../platform/extensions/index.md)
## Modes
The `modes` directory contains workflows that can be registered with OHIF within
certain `routes`. The mode will get used once the user opens the viewer on the
registered route.
OHIF extensions were designed to provide certain core functionalities for
building your viewer. However, often in medical imaging we face a specific use
case in which we are using some core functionalities, adding our specific UI,
and use it in our workflows. Previously, to achieve this you had to create an
extension to add have such feature. `OHIF-v3` introduces `Modes` to enable
building such workflows by re-using the core functionalities from the
extensions.
Some common workflows may include:
- Measurement tracking for lesions
- Segmentation of brain abnormalities
- AI probe mode for detecting prostate cancer
In the mentioned modes above, they will share the same core rendering module
that the `default` extension provides. However, segmentation mode will require
segmentation tools which is not needed for the other two. As you can see, modes
are a layer on top of extensions, that you can configure in order to achieve
certain workflows.
To summarize the difference between extensions and modes in `OHIF-v3` and
extensions in `OHIF-v2`
> - `Modes` are configuration objects that tell _@ohif/app_ how to compose
> extensions to build applications on different routes of the platform.
> - In v2 extensions are “plugins” that add functionality to a core viewer.
> - In v3 extensions are building blocks that a mode uses to build an entire
> viewer layout.
[Click here to read more about modes!](../platform/modes/index.md)
## Platform
### `@ohif/app`
This library is the core library which consumes modes and extensions and builds
an application. Extensions can be passed in as app configuration and will be
consumed and initialized at the appropriate time by the application. Upon
initialization the viewer will consume extensions and modes and build up the
route desired, these can then be accessed via the study list, or directly via
url parameters.
Upon release modes will also be plugged into the app via configuration, but this
is still an area which is under development/discussion, and they are currently
pulled from the window in beta.
Future ideas for this framework involve only adding modes and fetching the
required extension versions at either runtime or build time, but this decision
is still up for discussion.
### `@ohif/core`
OHIF core is a carefully maintained and tested set of web-based medical imaging
functions and classes. This library includes managers and services used from
within the viewer app.
OHIF core is largely similar to the @ohif/core library in v2, however a lot of
logic has been moved to extensions: however all logic about DICOMWeb and other
data fetching mechanisms have been pulled out, as these now live in extensions,
described later.
### `@ohif/ui`
Firstly, a large time-consumer/barrier for entry we discovered was building new
UI in a timely manner that fit OHIFs theme. For this reason we have built a new
UI component library which contains all the components one needs to build their
own viewer.
These components are presentational only, so you can reuse them with whatever
logic you desire. As the components are presentational, you may swap out
@ohif/ui for a custom UI library with conforming API if you wish to white label
the viewer. The UI library is here to make development easier and quicker, but
it is not mandatory for extension components to use.
[Check out our component library!](https://ui.ohif.org/)
## Overview of the architecture
OHIF-v3 architecture can be seen in the following figure. We will explore each
piece in more detail.
![mode-archs](../assets/img/mode-archs.png)
## Common Questions
> Can I create my own Viewer using Vue.js or Angular.js?
You can, but you will not be able to leverage as much of the existing code and
components. `@ohif/core` could still be used for business logic, and to provide
a model for extensions. `@ohif/ui` would then become a guide for the components
you would need to recreate.
> When I want to implement a functionality, should it be in the mode or in an
> extension?
This is a great question. Modes are designed to consume extensions, so you
should implement your functionality in one of the modules of your new extension,
and let the mode consume it. This way, in the future, if you needed another mode
that utilizes the same functionality, you can easily hook the extension to the
new mode as well.
<!--
Links
-->
<!-- prettier-ignore-start -->
[monorepo]: https://github.com/OHIF/Viewers/issues/768
[viewers-project]: https://github.com/OHIF/Viewers
[viewer-npm]: https://www.npmjs.com/package/@ohif/app
[pwa]: https://developers.google.com/web/progressive-web-apps/
[configuration]: ../configuration/index.md
[extensions]: ../platform/extensions/index.md
[core-github]: https://github.com/OHIF/viewers/platform/core
[ui-github]: https://github.com/OHIF/Viewers/tree/master/platform/ui
<!-- prettier-ignore-end -->

View File

@@ -0,0 +1,90 @@
---
sidebar_position: 8
sidebar_label: Continuous Integration
---
# Continuous Integration (CI)
This repository uses `CircleCI` and `Netlify` for Continuous integration.
## Deploy Previews
[Netlify Deploy previews][deploy-previews] are generated for every pull request.
They allow pull request authors and reviewers to "Preview" the OHIF Viewer as if
the changes had been merged.
Deploy previews can be configured by modifying the `netlify.toml` file in the
root of the repository. Some additional scripts/assets for netlify are included
in the root `.netlify` directory.
## Workflows
[CircleCI Workflows][circleci-workflows] are a set of rules for defining a
collection of jobs and their run order. They are self-documenting and their
configuration can be found in our CircleCI configuration file:
`.circleci/config.yml`.
### Workflow: PR_CHECKS
The PR_CHECKS workflow (Pull Request Checks) runs our automated unit and
end-to-end tests for every code check-in. These tests must all pass before code
can be merged to our `master` branch.
![PR_CHECKS](../assets/img/WORKFLOW_PR_CHECKS.png)
### Workflow: PR_OPTIONAL_DOCKER_PUBLISH
The PR_OPTIONAL_DOCKER_PUBLISH workflow allows for "manual approval" to publish
the pull request as a tagged docker image. This is helpful when changes need to
be tested with the Google Adapter before merging to `master`.
![PR_Workflow](../assets/img/WORKFLOW_PR_OPTIONAL_DOCKER_PUBLISH.png)
> NOTE: This workflow will fail unless it's for a branch on our `upstream`
> repository. If you need this functionality, but the branch is from a fork,
> merge the changes to a short-lived `feature/` branch on `upstream`
### Workflow: DEPLOY
The DEPLOY workflow deploys the OHIF Viewer when changes are merged to master.
It uses the Netlify CLI to deploy assets created as part of the repository's PWA
Build process (`yarn run build`). The workflow allows for "Manual Approval" to
promote the build to `STAGING` and `PRODUCTION` environments.
![WORKFLOW_DEPLOY](../assets/img/WORKFLOW_DEPLOY.png)
| Environment | Description | URL |
| ----------- | ---------------------------------------------------------------------------------- | --------------------------------------------- |
| Development | Always reflects latest changes on `master` branch. | [Netlify][netlify-dev] / [OHIF][ohif-dev] |
| Staging | For manual testing before promotion to prod. Keeps development workflow unblocked. | [Netlify][netlify-stage] / [OHIF][ohif-stage] |
| Production | Stable, tested, updated less frequently. | [Netlify][netlify-prod] / [OHIF][ohif-prod] |
### Workflow: RELEASE
The RELEASE workflow publishes our `npm` packages, updated documentation, and
`docker` image when changes are merged to master. `Lerna` and "Semantic Commit
Syntax" are used to independently version and publish the many packages in our
monorepository. If a new version is cut/released, a Docker image is created.
Documentation is generated with `gitbook` and pushed to our `gh-pages` branch.
GitHub hosts the `gh-pages` branch with GitHub Pages.
- Platform Packages: https://github.com/ohif/viewers/#platform
- Extension Packages: https://github.com/ohif/viewers/#extensions
- Documentation: https://docs.ohif.org/
![WORKFLOW_RELEASE](../assets/img/WORKFLOW_RELEASE.png)
<!--
LINKS
-->
<!-- prettier-ignore-start -->
[deploy-previews]: https://www.netlify.com/blog/2016/07/20/introducing-deploy-previews-in-netlify/
[circleci-workflows]: https://circleci.com/docs/2.0/workflows/
[netlify-dev]: https://ohif-dev.netlify.com
[netlify-stage]: https://ohif-stage.netlify.com
[netlify-prod]: https://ohif-prod.netlify.com
[ohif-dev]: https://viewer-dev.ohif.org
[ohif-stage]: https://viewer-stage.ohif.org
[ohif-prod]: https://viewer-prod.ohif.org
<!-- prettier-ignore-end -->

View File

@@ -0,0 +1,152 @@
---
sidebar_position: 5
sidebar_label: Contributing
---
# Contributing
## How can I help?
Fork the repository, make your change and submit a pull request. If you would
like to discuss the changes you intend to make to clarify where or how they
should be implemented, please don't hesitate to create a new issue. At a
minimum, you may want to read the following documentation:
- [Getting Started](/development/getting-started.md)
- [Architecture](./architecture.md)
Pull requests that are:
- Small
- [Well tested](./testing.md)
- Decoupled
Are much more likely to get reviewed and merged in a timely manner.
## When changes impact multiple repositories
While this can be tricky, we've tried to reduce how often this situation crops
up this with our [recent switch to a monorepo][monorepo]. Our maintained
extensions, ui components, internationalization library, and business logic can
all be developed by simply running `yarn run dev` from the repository root.
Testing the viewer with locally developed, unpublished package changes from a
package outside of the monorepo is most common with extension development. Let's
demonstrate how to accomplish this with two commonly forked extension
dependencies:
### `cornerstone-tools`
On your local file system:
```bash title="/my-projects/"
├── cornerstonejs/cornerstone-tools
└── ohif/viewers
```
- Open a terminal/shell
- Navigate to `cornerstonejs/cornerstone-tools`
- `yarn install`
- [`yarn link`](https://yarnpkg.com/en/docs/cli/link)
- `yarn run dev`
* Open a new terminal/shell
* Navigate to `ohif/viewers` (the root of ohif project)
- `yarn install`
- [`yarn link cornerstone-tools`](https://yarnpkg.com/en/docs/cli/link)
- `yarn run dev`
As you make changed to `cornerstone-tools`, and it's output is rebuilt, you
should see the following behavior:
![tools](..//assets/img/cornerstone-tools-link.gif)
If you wish to stop using your local package, run the following commands in the
`ohif/viewers` repository root:
- `yarn unlink cornerstone-tools`
- `yarn install --force`
<!--
### `react-vtkjs-viewport`
On your local file system:
```bash
# code/my-projects/
.
├── ohif/react-vtkjs-viewport
└── ohif/viewers
```
- Open a terminal/shell
- Navigate to `ohif/react-vtkjs-viewport`
- `yarn install`
- [`yarn link`](https://yarnpkg.com/en/docs/cli/link)
- `yarn run start`
- Open a new terminal/shell
- Navigate to `ohif/viewers`.
- `yarn install`
- [`yarn link react-vtkjs-viewport`](https://yarnpkg.com/en/docs/cli/link)
- `yarn run dev` -->
#### Other linkage notes
We're still working out some of the kinks with local package development as
there are a lot of factors that can influence the behavior of our development
server and bundler. If you encounter issues not addressed here, please don't
hesitate to reach out on GitHub.
Sometimes you might encounter a situation where the linking doesn't work as
expected. This might happen when there are multiple linked packages with the
same name. You can [remove][unlink] the linked packages inside yarn and try
again.
## Any guidance on submitting changes?
While we do appreciate code contributions, triaging and integrating contributed
code changes can be very time consuming. Please consider the following tips when
working on your pull requests:
- Functionality is appropriate for the repository. Consider creating a GitHub
issue to discuss your suggested changes.
- The scope of the pull request is not too large. Please consider separate pull
requests for each feature as big pull requests are very time consuming to
understand.
We will provide feedback on your pull requests as soon as possible. Following
the tips above will help ensure your changes are reviewed.
<!-- ## Testing contribution pull requests
OHIF uses [netlify](https://www.netlify.com/) so that pull requests are
autogenerated and available for testing.
For example, [this url][example-url] allows you to test [pull request 237, the
request that created this FAQ entry,][pr-237] using data pulled from Amazon S3.
Replacing the number 237 in the link below with your pull request number should
let you test it as well and you can use this link for discussions on github
without requiring reviewers to download and build your branch.
```bash
https://deploy-preview-237--ohif.netlify.com/viewer/?url=https://s3.eu-central-1.amazonaws.com/ohif-viewer/sampleDICOM.json
```
If you have made a documentation change, a link like this will let you preview
the gitbook generated by the pull request:
```bash
https://deploy-preview-237--ohif.netlify.com/contributing.html
``` -->
<!--
Links
-->
<!-- prettier-ignore-start -->
[example-url]: https://deploy-preview-237--ohif.netlify.com/viewer/?url=https://s3.eu-central-1.amazonaws.com/ohif-viewer/sampleDICOM.json
[pr-237]: https://github.com/OHIF/Viewers/pull/237
[monorepo]: https://github.com/OHIF/Viewers/issues/768
[unlink]: https://stackoverflow.com/questions/58459698/is-there-a-command-to-unlink-all-yarn-packages-yarn-unlink-all
<!-- prettier-ignore-end -->

View File

@@ -0,0 +1,132 @@
---
sidebar_position: 1
sidebar_label: Getting Started
---
# Getting Started
## Setup
### Fork & Clone
If you intend to contribute back changes, or if you would like to pull updates
we make to the OHIF Viewer, then follow these steps:
- [Fork][fork-a-repo] the [OHIF/Viewers][ohif-viewers-repo] repository
- [Create a local clone][clone-a-repo] of your fork
- `git clone https://github.com/YOUR-USERNAME/Viewers`
- Add OHIF/Viewers as a [remote repository][add-remote-repo] labeled `upstream`
- Navigate to the cloned project's directory
- `git remote add upstream https://github.com/OHIF/Viewers.git`
With this setup, you can now [sync your fork][sync-changes] to keep it
up-to-date with the upstream (original) repository. This is called a "Triangular
Workflow" and is common for Open Source projects. The GitHub blog has a [good
graphic that illustrates this setup][triangular-workflow].
### Private
Alternatively, if you intend to use the OHIF Viewer as a starting point, and you
aren't as concerned with syncing updates, then follow these steps:
1. Navigate to the [OHIF/Viewers][ohif-viewers] repository
2. Click `Clone or download`, and then `Download ZIP`
3. Use the contents of the `.zip` file as a starting point for your viewer
> NOTE: It is still possible to sync changes using this approach. However,
> submitting pull requests for fixes and features are best done with the
> separate, forked repository setup described in "Fork & Clone"
## Developing
### Branches
#### `master` branch - The latest dev (beta) release
- `master` - The latest dev release
This is typically where the latest development happens. Code that is in the master branch has passed code reviews and automated tests, but it may not be deemed ready for production. This branch usually contains the most recent changes and features being worked on by the development team. It's often the starting point for creating feature branches (where new features are developed) and hotfix branches (for urgent fixes).
Each package is tagged with beta version numbers, and published to npm such as `@ohif/ui@3.6.0-beta.1`
### `release/*` branches - The latest stable releases
Once the `master` branch code reaches a stable, release-ready state, we conduct a comprehensive code review and QA testing. Upon approval, we create a new release branch from `master`. These branches represent the latest stable version considered ready for production.
For example, `release/3.5` is the branch for version 3.5.0, and `release/3.6` is for version 3.6.0. After each release, we wait a few days to ensure no critical bugs. If any are found, we fix them in the release branch and create a new release with a minor version bump, e.g., 3.5.1 in the `release/3.5` branch.
Each package is tagged with version numbers and published to npm, such as `@ohif/ui@3.5.0`. Note that `master` is always ahead of the `release` branch. We publish docker builds for both beta and stable releases.
Here is a schematic representation of our development workflow:
### Requirements
- [Node.js & NPM](https://nodejs.org/en/)
- [Yarn](https://yarnpkg.com/en/)
- Yarn workspaces should be enabled:
- `yarn config set workspaces-experimental true`
### Kick the tires
Navigate to the root of the project's directory in your terminal and run the
following commands:
```bash
# Restore dependencies
yarn install
# Start local development server
yarn run dev
```
You should see the following output:
```bash
@ohif/app: i 「wds」: Project is running at http://localhost:3000/
@ohif/app: i 「wds」: webpack output is served from /
@ohif/app: i 「wds」: Content not from webpack is served from D:\code\ohif\Viewers\platform\viewer
@ohif/app: i 「wds」: 404s will fallback to /index.html
# And a list of all generated files
```
### 🎉 Celebrate 🎉
<div style={{padding:"56.25% 0 0 0", position:"relative"}}>
<iframe src="https://player.vimeo.com/video/843233770?badge=0&amp;autopause=0&amp;player_id=0&amp;app_id=58479" frameBorder="0" allow="autoplay; fullscreen; picture-in-picture" allowFullScreen style= {{ position:"absolute",top:0,left:0,width:"100%",height:"100%"}} title="measurement-report"></iframe>
</div>
### Building for Production
> More comprehensive guides for building and publishing can be found in our
> [deployment docs](./../deployment/index.md)
```bash
# Build static assets to host a PWA
yarn run build
```
## Troubleshooting
- If you receive a _"No Studies Found"_ message and do not see your studies, try
changing the Study Date filters to a wider range.
- If you see a 'Loading' message which never resolves, check your browser's
JavaScript console inside the Developer Tools to identify any errors.
<!--
Links
-->
<!-- prettier-ignore-start -->
[fork-a-repo]: https://help.github.com/en/articles/fork-a-repo
[clone-a-repo]: https://help.github.com/en/articles/fork-a-repo#step-2-create-a-local-clone-of-your-fork
[add-remote-repo]: https://help.github.com/en/articles/fork-a-repo#step-3-configure-git-to-sync-your-fork-with-the-original-spoon-knife-repository
[sync-changes]: https://help.github.com/en/articles/syncing-a-fork
[triangular-workflow]: https://github.blog/2015-07-29-git-2-5-including-multiple-worktrees-and-triangular-workflows/#improved-support-for-triangular-workflows
[ohif-viewers-repo]: https://github.com/OHIF/Viewers/
[ohif-viewers]: https://github.com/OHIF/Viewers
<!-- prettier-ignore-end -->

View File

@@ -0,0 +1,62 @@
---
sidebar_position: 9
sidebar_label: Local Linking
---
# Introduction
Local linking allows you to develop and test a library in the context of an application before it's published or when you encounter
a bug that you suspect is related to a library. With Yarn, this can be achieved through the yarn link command.
The general procedure is as follows:
Link the Library:
```sh
cd /path/to/library
yarn link
```
This command will create a symlink in a global directory for the library.
Link to the Application:
```sh
cd /path/to/application
yarn link "library-name"
```
Creates a symlink from the global directory to the application's node_modules.
# Tutorial for linking Cornerstone3D to OHIF
Below we demonstrate how to link Cornerstone3D to OHIF Viewer. This is useful for testing and debugging Cornerstone3D in the context of OHIF Viewer.
<div style={{padding:"56.25% 0 0 0", position:"relative"}}>
<iframe src="https://player.vimeo.com/video/849096279?badge=0&amp;autopause=0&amp;player_id=0&amp;app_id=58479" frameBorder="0" allow="autoplay; fullscreen; picture-in-picture" allowFullScreen style= {{ position:"absolute",top:0,left:0,width:"100%",height:"100%"}} title="measurement-report"></iframe>
</div>
::tip
Since `@cornerstonejs/tools` depends on `@cornerstonejs/core`, if you need the changes
you made in `@cornerstonejs/core` to be reflected in `@cornerstonejs/tools`, you need to
also link `@cornerstonejs/core` to `@cornerstonejs/tools`.
```sh
cd /path/to/cornerstonejs-core
# for the core
yarn link
cd /path/to/cornerstonejs-tools
yarn link "@cornerstonejs/core"
# for the tools
yarn link
# inside OHIF
cd /path/to/OHIFViewer
yarn link "@cornerstonejs/core"
yarn link "@cornerstonejs/tools"
```

View File

@@ -0,0 +1,312 @@
---
sidebar_position: 3
sidebar_label: OHIF CLI
---
# OHIF Command Line Interface
OHIF-v3 architecture has been re-designed to enable building applications that
are easily extensible to various use cases (Modes) that behind the scene would
utilize desired functionalities (Extensions) to reach the goal of the use case.
Now, the question is _how to create/remove/install/uninstall an extension and/or
mode?_
You can use the `cli` script that comes with the OHIF monorepo to achieve these
goals.
:::note Info
In the long-term, we envision our `cli` tool to be a separate installable
package that you can invoke anywhere on your local system to achieve the same
goals. In the meantime, `cli` will remain as part of the OHIF monorepo and needs
to be invoked using the `yarn` command.
:::
## CLI Installation
You don't need to install the `cli` currently. You can use `yarn` to invoke its
commands.
## Commands
:::note Important
All commands should run from the root of the monorepo.
:::
There are various commands that can be used to interact with the OHIF-v3 CLI. If
you run the following command, you will see a list of available commands.
```
yarn run cli --help
```
which will output
```
OHIF CLI
Options:
-V, --version output the version number
-h, --help display help for command
Commands:
create-extension Create a new template extension
create-mode Create a new template Mode
add-extension <packageName> [version] Adds an ohif extension
remove-extension <packageName> removes an ohif extension
add-mode <packageName> [version] Removes an ohif mode
remove-mode <packageName> Removes an ohif mode
link-extension <packageDir> Links a local OHIF extension to the Viewer to be used for development
unlink-extension <extensionName> Unlinks a local OHIF extension from the Viewer
link-mode <packageDir> Links a local OHIF mode to the Viewer to be used for development
unlink-mode <extensionName> Unlinks a local OHIF mode from the Viewer
list List Added Extensions and Modes
search [options] Search NPM for the list of Modes and Extensions
help [command] display help for command
```
As seen there are commands for you such as: `create-extension`, `create-mode`,
`add-extension`, `remove-extension`, `add-mode`, `remove-mode`,
`link-extension`, `unlink-extension`, `link-mode`, `unlink-mode`, `list`,
`search`, and `help`. Here we will go through each of the commands and describe
them.
### create-mode
If you need to create a new mode, you can use the `create-mode` command. This
command will create a new mode template in the directory that you specify.
The command will ask you couple of information/questions in order
to properly create the mode metadata in the `package.json` file.
```bash
yarn run cli create-mode
```
<div style={{textAlign: 'center',}}>
![image](../assets/img/create-mode.png)
</div>
Note 1: Some questions have a default answer, which is indicated inside the
parenthesis. If you don't want to answer the question, just hit enter. It will
use the default answer.
Note 2: As you see in the questions, you can initiate a git repository for the
new mode right away by answering `Y` (default) to the question.
Note 3: Finally, as indicated by the green lines at the end, `create-mode` command only
create the mode template. You will need to link the mode to the Viewer in order
to use it. See the [`link-mode`](#link-mode) command.
If we take a look at the directory that we created, we will see the following
files:
<div style={{maxWidth:"500px"}}>
![image](../assets/img/mode-template.png)
</div>
### create-extension
Similar to the `create-mode` command, you can use the `create-extension`
command to create a new extension template. This command will create a new
extension template in the directory that you specify the path.
```bash
yarn run cli create-extension
```
Note: again similar to the `create-extension` command, you need to manually link
the extension to the Viewer in order to use it. See the
[`link-mode`](#link-mode) command.
### link-extension
`link-extension` command will link a local OHIF extension to the Viewer. This
command will utilize `yarn link` to achieve so.
```bash
yarn run cli link-extension <extensionDir>
```
### unlink-extension
There might be situations where you want to unlink an extension from the Viewer
after some developments. `unlink-extension` command will do so.
```bash
ohif-cli unlink-extension <extensionName>
```
### link-mode
Similar to the `link-extension` command, `link-mode` command will link a local
OHIF mode to the Viewer.
```bash
yarn run cli link-mode <modeDir>
```
### unlink-mode
Similar to the `unlink-extension` command, `unlink-mode` command will unlink a
local OHIF mode from the Viewer.
```bash
ohif-cli unlink-mode <modeName>
```
### add-mode
OHIF is a modular viewer. This means that you can install (add) different modes
to the viewer if they are published online . `add-mode` command will add a new mode to
the viewer. It will look for the mode in the NPM registry and installs it. This
command will also add the extension dependencies that the mode relies on to the
Viewer (if specified in the peerDependencies section of the package.json).
:::note Important
`cli` will validate the npm package before adding it to the Viewer. An OHIF mode
should have `ohif-mode` as one of its keywords.
:::
Note: If you don't specify the version, the latest version will be used.
```bash
yarn run cli add-mode <packageName> [version]
```
For instance `@ohif-test/mode-clock` is an example OHIF mode that we have
published to NPM. This mode basically has a panel that shows the clock :)
We can add this mode to the Viewer by running the following command:
```bash
yarn run cli add-mode @ohif-test/mode-clock
```
After installation, the Viewer has a new mode!
![image](../assets/img/add-mode.png)
Note: If the mode has an extension peerDependency (in this case @ohif-test/extension-clock),
`cli` will automatically add the extension to the Viewer too.
The result
![image](../assets/img/clock-mode.png)
![image](../assets/img/clock-mode1.png)
### add-extension
This command will add an OHIF extension to the Viewer. It will look for the
extension in the NPM registry and install it.
```bash
yarn run cli add-extension <packageName> [version]
```
### remove-mode
This command will remove the mode from the Viewer and also remove the extension
dependencies that the mode relies on from the Viewer.
```bash
yarn run cli remove-mode <packageName>
```
### remove-extension
Similar to the `remove-mode` command, this command will remove the extension
from the Viewer.
```bash
yarn run cli remove-extension <packageName>
```
### list
`list` command will list all the installed extensions and modes in
the Viewer. It uses the `PluginConfig.json` file to list the installed
extensions and modes.
```bash
yarn run cli list
```
an output would look like this:
<div style={{maxWidth:"500px"}}>
![image](../assets/img/ohif-cli-list.png)
</div>
### search
Using `search` command, you can search for OHIF extensions and modes
in the NPM registry. This tool can accept a `--verbose` flag to show more
information about the results.
```bash
yarn run cli search [--verbose]
```
<div style={{maxWidth:"600px"}}>
![image](../assets/img/cli-search-no-verbose.png)
</div>
with the verbose flag `ohif-cli search --verbose` you will achieve the following
output:
<div style={{maxWidth:"600px"}}>
![image](../assets/img/cli-search-with-verbose.png)
</div>
## PluginConfig.json
To make all the above commands work, we have created a new file called `PluginConfig.json` which contains the
information needed to run the commands. You **don't need to (and should not)**
edit/update/modify this file as it is automatically generated by the CLI. You
can take a look at what this file contains by going to
`platform/app/PluginConfig.json` in your project's root directory. In short,
this file tracks and stores all the extensions/modes and the their version that
are currently being used by the viewer.
## Private NPM Repos
For the `yarn cli` to view private NPM repos, create a read-only token with the
following steps and export it as an environmental variable. You may also export
an existing npm token.
```
npm login
npm token create --read-only
export NPM_TOKEN=<your readonly token>
```
## External dependencies
The ohif-cli will add the path to the external dependencies to the webpack config,
so that you can install them in your project and use them in your custom
extensions and modes. To achieve this ohif-cli will update the webpack.pwa.js
file in the platform/app directory.
## Video tutorials
See the [Video Tutorials](./video-tutorials.md) for videos of some the above
commands in action.

View File

@@ -0,0 +1,156 @@
---
sidebar_position: 6
sidebar_label: Issue & PR Triage Process
---
# Our Process
Our process is a living, breathing thing. We strive to have regular
[retrospectives][retrospective] that help us shape and adapt our process to our
team's current needs. This document attempts to capture the broad strokes of
that process in an effort to:
- Strengthen community member involvement and understanding
- Welcome feedback and helpful suggestions
## Issue Triage
[GitHub issues][gh-issues] are the best way to provide feedback, ask questions,
and suggest changes to the OHIF Viewer's core team. Community issues generally
fall into one of three categories, and are marked with a `triage` label when
created.
| Issue Template Name | Description |
| ---------------------- | ---------------------------------------------------------------------------------------- |
| Community: Report 🐛 | Describe a new issue; Provide steps to reproduce; Expected versus actual result? |
| Community: Request ✋ | Describe a proposed new feature. Why should it be implemented? What is the impact/value? |
| Community: Question ❓ | Seek clarification or assistance relevant to the repository. |
_table 1. issue template names and descriptions_
Issues that require `triage` are akin to support tickets. As this is often our
first contact with would-be adopters and contributors, it's important that we
strive for timely responses and satisfactory resolutions. We attempt to
accomplish this by:
1. Responding to issue requiring `triage` at least once a week
2. Create new "official issues" from "community issues"
3. Provide clear guidance and next steps (when applicable)
4. Regularly clean up old (stale) issues
> 🖋 Less obviously, patterns in the issues being reported can highlight areas
> that need improvement. For example, users often have difficulty navigating
> CORS issues when deploying the OHIF Viewer -- how do we best reduce our ticket
> volume for this issue?
### Backlogged Issues
Community issues serve as vehicles of discussion that lead us to "backlogged
issues". Backlogged issues are the distilled and actionable information
extracted from community issues. They contain the scope and requirements
necessary for hand-off to a core-team (or community) contributor ^\_^
| Category | Description | Labels |
| -------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| Bugs | An issue with steps that produce a bug (an unexpected result). | [Bug: Verified 🐛][label-bug] |
| Stories | A feature/enhancement with a clear benefit, boundaries, and requirements. | [Story 🙌][label-story] |
| Tasks | Changes that improve [UX], [DX], or test coverage; but don't impact application behavior | [Task: CI/Tooling 🤖][label-tooling], [Task: Docs 📖][label-docs], [Task: Refactor 🛠][label-refactor], [Task: Tests 🔬][label-tests] |
_table 2. backlogged issue types ([full list of labels][gh-labels])_
## Issue Curation (["backlog grooming"][groom-backlog])
If a [GitHub issue][gh-issues] has a `bug`, `story`, or `task` label; it's on
our backlog. If an issue is on our backlog, it means we are, at the very least,
committed to reviewing any community drafted Pull Requests to complete the
issue. If you're interested in seeing an issue completed but don't know where to
start, please don't hesitate to leave a comment!
While we don't yet have a long-term or quarterly road map, we do regularly add
items to our ["Active Development" GitHub Project Board][gh-board]. Items on
this project board are either in active development by Core Team members, or
queued up for development as in-progress items are completed.
> 🖋 Want to contribute but not sure where to start? Check out [Up for
> grabs][label-grabs] issues and our [Contributing
> documentation][contributing-docs]
## Contributions (Pull Requests)
Incoming Pull Requests (PRs) are triaged using the following labels. Code review
is performed on all PRs where the bug fix or added functionality is deemed
appropriate:
| Labels | Description |
| ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| **Classification** | |
| [PR: Bug Fix][label-bug] | Filed to address a Bug. |
| [PR: Draft][draft] | Filed to gather early feedback from the core team, but which is not intended for merging in the short term. |
| **Review Workflow** | |
| [PR: Awaiting Response 💬][awaiting-response] | The core team is waiting for additional information from the author. |
| [PR: Awaiting Review 👀][awaiting-review] | The core team has not yet performed a code review. |
| [PR: Awaiting Revisions 🖊][awaiting-revisions] | Following code review, this label is applied until the author has made sufficient changes. |
| **QA** | |
| [PR: Awaiting User Cases 💃][awaiting-stories] | The PR code changes need common language descriptions of impact to end users before the review can start |
| [PR: No UX Impact 🙃][no-ux-impact] | The PR code changes do not impact the user's experience |
We rely on GitHub Checks and integrations with third party services to evaluate
changes in code quality and test coverage. Tests must pass and User cases must
be present (when applicable) before a PR can be merged to master, and code
quality and test coverage must not be changed by a significant margin. For some
repositories, visual screenshot-based tests are also included, and video
recordings of end-to-end tests are stored for later review.
[You can read more about our continuous integration efforts here](/development/continuous-integration.md)
## Releases
Releases are made automatically based on the type of commits which have been
merged (major.minor.patch). Releases are automatically pushed to NPM. Release
notes are automatically generated. Users can subscribe to GitHub and NPM
releases.
We host development, staging, and production environments for the Progressive
Web Application version of the OHIF Viewer. [Development][ohif-dev] always
reflects the latest changes on our master branch. [Staging][ohif-stage] is used
to regression test a release before a bi-weekly deploy to our [Production
environment][ohif-prod].
Important announcements are made on GitHub, tagged as Announcement, and pinned
so that they remain at the top of the Issue page.
The Core team occasionally performs full manual testing to begin the process of
releasing a Stable version. Once testing is complete, the known issues are
addressed and a Stable version is released.
<!--
LINKS
-->
<!-- prettier-ignore-start -->
[groom-backlog]: https://www.agilealliance.org/glossary/backlog-grooming
[retrospective]: https://www.atlassian.com/team-playbook/plays/retrospective
[gh-issues]: https://github.com/OHIF/Viewers/issues/new/choose
[gh-labels]: https://github.com/OHIF/Viewers/labels
<!-- Issue Labels -->
[label-story]: https://github.com/OHIF/Viewers/labels/Story%20%3Araised_hands%3A
[label-tooling]: https://github.com/OHIF/Viewers/labels/Task%3A%20CI%2FTooling%20%3Arobot%3A
[label-docs]: https://github.com/OHIF/Viewers/labels/Task%3A%20Docs%20%3Abook%3A
[label-refactor]: https://github.com/OHIF/Viewers/labels/Task%3A%20Refactor%20%3Ahammer_and_wrench%3A
[label-tests]: https://github.com/OHIF/Viewers/labels/Task%3A%20Tests%20%3Amicroscope%3A
[label-bug]: https://github.com/OHIF/Viewers/labels/Bug%3A%20Verified%20%3Abug%3A
<!-- PR Labels -->
[draft]: https://github.com/OHIF/Viewers/labels/PR%3A%20Draft
[awaiting-response]: https://github.com/OHIF/Viewers/labels/PR%3A%20Awaiting%20Response%20%3Aspeech_balloon%3A
[awaiting-review]: https://github.com/OHIF/Viewers/labels/PR%3A%20Awaiting%20Review%20%3Aeyes%3A
[awaiting-stories]: https://github.com/OHIF/Viewers/labels/PR%3A%20Awaiting%20UX%20Stories%20%3Adancer%3A
[awaiting-revisions]: https://github.com/OHIF/Viewers/labels/PR%3A%20Awaiting%20Revisions%20%3Apen%3A
[no-ux-impact]: https://github.com/OHIF/Viewers/labels/PR%3A%20No%20UX%20Impact%20%3Aupside_down_face%3A
<!-- -->
[ohif-dev]: https://viewer-dev.ohif.org
[ohif-stage]: https://viewer-stage.ohif.org
[ohif-prod]: https://viewer.ohif.org
[gh-board]: https://github.com/OHIF/Viewers/projects/4
[label-grabs]: https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3A%22Up+For+Grabs+%3Araising_hand_woman%3A%22
[contributing-docs]: ./development/contributing.md
<!-- prettier-ignore-end -->

View File

@@ -0,0 +1,217 @@
---
sidebar_position: 7
sidebar_label: Testing
---
# Running Tests for OHIF
We introduce here various test types that is available for OHIF, and how to run
each test in order to make sure your contribution hasn't broken any existing
functionalities. Idea and philosophy of each testing category is discussed in
the second part of this page.
## Unit test
To run the unit test:
```bash
yarn run test:unit:ci
```
Note: You should have already installed all the packages with `yarn install`.
Running unit test will generate a report at the end showing the successful and
unsuccessful tests with detailed explanations.
## End-to-end test
For running the OHIF e2e test you need to run the following steps:
- Open a new terminal, and from the root of the OHIF mono repo, run the following command:
```bash
yarn test:data
```
This will download the required data to run the e2e tests (it might take a while).
The `test:data` only needs to be run once and checks the data out. Read more about
test data [below](#test-data).
- Run the viewer with e2e config
```bash
APP_CONFIG=config/e2e.js yarn start
```
You should be able to see test studies in the study list
![OHIF-e2e-test-studies](../assets/img/OHIF-e2e-test-studies.png)
- Open a new terminal inside the OHIF project, and run the e2e cypress test
```bash
yarn test:e2e
```
You should be able to see the cypress window open
![e2e-cypress](../assets/img/e2e-cypress.png)
Run the tests by clicking on the `Run #number integration tests` .
A new window will open, and you will see e2e tests being executed one after
each other.
![e2e-cypress-final](../assets/img/e2e-cypress-final.png)
## Test Data
The testing data is stored in two OHIF repositories. The first contains the
binary DICOM data, at [viewer-testdata](https://github.com/OHIF/viewer-testdata.git)
while the second module contains data in the DICOMweb format, installed as a submodule
into OHIF in the `testdata` directory. This is retrieved via the command
```bash
yarn test:data
```
or the equivalent command `git submodule update --init`
When adding new data, run:
```
npm install -g dicomp10-to-dicomweb
mkdicomweb -d dicomweb dcm
```
to update the local dicomweb submodule in viewer-testdata. Then, commit
that data and update the submodules used in OHIF and in the viewer-testdata
parent modules.
All data MUST be fully anonymized and allowed to be used for open access.
Any attributions should be included in the DCM directory.
## Testing Philosophy
> Testing is an opinionated topic. Here is a rough overview of our testing
> philosophy. See something you want to discuss or think should be changed? Open
> a PR and let's discuss.
You're an engineer. You know how to write code, and writing tests isn't all that
different. But do you know why we write tests? Do you know when to write one, or
what kind of test to write? How do you know if a test is a _"good"_ test? This
document's goal is to give you the tools you need to make those determinations.
Okay. So why do we write tests? To increase our... **CONFIDENCE**
- If I do a large refactor, does everything still work?
- If I changed some critical piece of code, is it safe to push to production?
Gaining the confidence we need to answer these questions after every change is
costly. Good tests allow us to answer them without manual regression testing.
What and how we choose to test to increase that confidence is nuanced.
## Further Reading: Kinds of Tests
Test's buy us confidence, but not all tests are created equal. Each kind of test
has a different cost to write and maintain. An expensive test is worth it if it
gives us confidence that a payment is processed, but it may not be the best
choice for asserting an element's border color.
| Test Type | Example | Speed | Cost |
| ----------- | ------------------------------------------------------------------------ | ---------------- | ------------------------------------------------------------------------ |
| Static | `addNums(1, '2')` called with `string`, expected `int`. | :rocket: Instant | :money_with_wings: |
| Unit | `addNums(1, 2)` returns expected result `3` | :airplane: Fast | :money_with_wings::money_with_wings: |
| Integration | Clicking "Sign In", navigates to the dashboard (mocked network requests) | :running: Okay | :money_with_wings::money_with_wings::money_with_wings: |
| End-to-end | Clicking "Sign In", navigates to the dashboard (no mocks) | :turtle: Slow | :money_with_wings::money_with_wings::money_with_wings::money_with_wings: |
- :rocket: Speed: How quickly tests run
- :money_with_wings: Cost: Time to write, and to debug when broken (more points
of failure)
### Static Code Analysis
Modern tooling gives us this "for free". It can catch invalid regular
expressions, unused variables, and guarantee we're calling methods/functions
with the expected parameter types.
Example Tooling:
- [ESLint][eslint-rules]
- [TypeScript][typescript-docs] or [Flow][flow-org]
### Unit Tests
The building blocks of our libraries and applications. For these, you'll often
be testing a single function or method. Conceptually, this equates to:
_Pure Function Test:_
- If I call `sum(2, 2)`, I expect the output to be `4`
_Side Effect Test:_
- If I call `resetViewport(viewport)`, I expect `cornerstone.reset` to be called
with `viewport`
#### When to use
Anything that is exposed as public API should have unit tests.
#### When to avoid
You're actually testing implementation details. You're testing implementation
details if:
- Your test does something that the consumer of your code would never do.
- IE. Using a private function
- A refactor can break your tests
### Integration Tests
We write integration tests to gain confidence that several units work together.
Generally, we want to mock as little as possible for these tests. In practice,
this means only mocking network requests.
### End-to-End Tests
These are the most expensive tests to write and maintain. Largely because, when
they fail, they have the largest number of potential points of failure. So why
do we write them? Because they also buy us the most confidence.
#### When to use
Mission critical features and functionality, or to cover a large breadth of
functionality until unit tests catch up. Unsure if we should have a test for
feature `X` or scenario `Y`? Open an issue and let's discuss.
### General
- [Assert(js) Conf 2018 Talks][assert-js-talks]
- [Write tests. Not too many. Mostly integration.][kent-talk] - Kent C. Dodds
- [I see your point, but…][gleb-talk] - Gleb Bahmutov
- [Static vs Unit vs Integration vs E2E Testing][kent-blog] - Kent C. Dodds
(Blog)
### End-to-end Testing w/ Cypress
- [Getting Started](https://docs.cypress.io/guides/overview/why-cypress.html)
- Be sure to check out `Getting Started` and `Core Concepts`
- [Best Practices](https://docs.cypress.io/guides/references/best-practices.html)
- [Example Recipes](https://docs.cypress.io/examples/examples/recipes.html)
<!--
Links
-->
<!-- prettier-ignore-start -->
[eslint-rules]: https://eslint.org/docs/rules/
[mini-pacs]: https://github.com/OHIF/viewer-testdata
[typescript-docs]: https://www.typescriptlang.org/docs/home.html
[flow-org]: https://flow.org/
<!-- Talks -->
[assert-js-talks]: https://www.youtube.com/playlist?list=PLZ66c9_z3umNSrKSb5cmpxdXZcIPNvKGw
[kent-talk]: https://www.youtube.com/watch?v=Fha2bVoC8SE
[gleb-talk]: https://www.youtube.com/watch?v=5FnalKRjpZk
[kent-blog]: https://kentcdodds.com/blog/unit-vs-integration-vs-e2e-tests
<!-- Images -->
[testing-trophy]: https://twitter.com/kentcdodds/status/960723172591992832?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E960723172591992832&ref_url=https%3A%2F%2Fkentcdodds.com%2Fblog%2Fwrite-tests
[aaron-square]: https://twitter.com/Carofine247/status/966727489274961920
[gleb-pyramid]: https://twitter.com/Carofine247/status/966764532046684160/photo/3
[testing-pyramid]: https://dojo.ministryoftesting.com/dojo/lessons/the-mobile-test-pyramid
[testing-dorito]: https://twitter.com/denvercoder/status/960752578198843392
[testing-dorito-img]: https://pbs.twimg.com/media/DVVHXycUMAAcN-F?format=jpg&name=4096x4096
<!-- prettier-ignore-end -->

View File

@@ -0,0 +1,85 @@
---
sidebar_position: 10
sidebar_label: Global Types
---
# Extending App Types and Services in Your Application
This documentation provides an overview and examples on how to use and extend `withAppTypes`, integrate custom properties, and add services in the global namespace of the application. This helps in enhancing the application's modularity and extensibility.
## Overview of `withAppTypes`
The `withAppTypes` function is a TypeScript utility that extends the base properties of components or modules with the application's core service and manager types. It allows for a more flexible and type-safe way to pass around core functionality and custom properties.
### Using `withAppTypes`
`withAppTypes` can be enhanced using generics to include custom properties. This is particularly useful for passing additional data or configurations specific to your component or service.
### Extending with Custom Properties
You can extend `withAppTypes` to include custom properties by defining an interface for the props you need. For example:
```typescript
interface ColorbarProps {
viewportId: string;
displaySets: Array<any>;
colorbarProperties: ColorbarProperties;
}
export function Colorbar({
viewportId,
displaySets,
commandsManager, // injected type
servicesManager, // injected type
colorbarProperties,
}: withAppTypes<ColorbarProps>): ReactElement {
// Component logic here
}
```
In this example, `ColorbarProps` is a custom interface that extends the application types through `withAppTypes`.
## Typing the custom extensions's new services
Extensions can define additional services that integrate seamlessly into the application's global service architecture, and will be available on the ServicesManager for use across the application.
### Adding the extension's services Types
Declare your service in the global namespace and use it across your application as demonstrated below:
`extensions/my-extension/src/types/whatever.ts`
```typescript
declare global {
namespace AppTypes {
// only add if you need direct access to the service ex. AppTypes.MicroscopyService
export type MicroscopyService = MicroscopyServiceType;
// add to the global Services interface, and to withAppTypes
export interface Services {
microscopyService?: MicroscopyServiceType;
}
}
}
```
Doing the above adds the `microscopyService` to the global Services interface, which ServicesManager uses by default `public services: AppTypes.Services = {};` to type services, and is also used by withAppTypes to inject services into components.
You will also get access to the seperate services via `AppTypes.YourServiceName` in your application.
```typescript
export function CustomComponent({
servicesManager,
}: withAppTypes<CustomComponentProps>): ReactElement {
const { microscopyService } = servicesManager.services;
microscopyService.someMethod(); // auto completation available
}
```
```typescript
export function CustomComponent2(
microscopyService: AppTypes.MicroscopyService,
): ReactElement {
microscopyService.someMethod(); // auto completation available
}
```

View File

@@ -0,0 +1,68 @@
---
sidebar_position: 4
sidebar_label: Video Tutorials
---
# Video Tutorials
## Creating, Linking and Publishing OHIF Modes and Extensions
The [OHIF CLI](./ohif-cli.md) facilitates the creation, linkage and publication
of OHIF modes and extensions. The videos below walk through how to use the CLI for
- creating modes and extensions
- linking local modes and extensions
- publishing modes and extensions to NPM
- adding published modes and extensions to OHIF
- submitting a mode to OHIF
The videos build on top of one another whereby the mode and extension created
in each of the first two videos are published to NPM and then the published
entities are added to OHIF.
### Creating and Linking a Mode
The first video demonstrates the creation and linkage of a mode.
<div style={{padding:"56.25% 0 0 0", position:"relative"}}>
<iframe src="https://player.vimeo.com/video/884891676?badge=0&amp;autopause=0&amp;player_id=0&amp;app_id=58479" frameBorder="0" allow="autoplay; fullscreen; picture-in-picture" allowFullScreen style= {{ position:"absolute",top:0,left:0,width:"100%",height:"100%"}} title="Create and link a mode"></iframe>
</div>
### Creating and Linking an Extension
The second video creates and links an extension. The mode from the first
video is modified to reference the extension.
<div style={{padding:"56.25% 0 0 0", position:"relative"}}>
<iframe src="https://player.vimeo.com/video/884891718?badge=0&amp;autopause=0&amp;player_id=0&amp;app_id=58479" frameBorder="0" allow="autoplay; fullscreen; picture-in-picture" allowFullScreen style= {{ position:"absolute",top:0,left:0,width:"100%",height:"100%"}} title="Create and link an extension"></iframe>
</div>
### Publishing an Extension to NPM
The third video shows how the extension created in the second video can
be published to NPM.
<div style={{padding:"56.25% 0 0 0", position:"relative"}}>
<iframe src="https://player.vimeo.com/video/884891701?badge=0&amp;autopause=0&amp;player_id=0&amp;app_id=58479" frameBorder="0" allow="autoplay; fullscreen; picture-in-picture" allowFullScreen style= {{ position:"absolute",top:0,left:0,width:"100%",height:"100%"}} title="Publish an extension to NPM"></iframe>
</div>
### Publishing a Mode to NPM
The fourth video shows how the mode created in the first video can be
published to NPM.
<div style={{padding:"56.25% 0 0 0", position:"relative"}}>
<iframe src="https://player.vimeo.com/video/884893445?badge=0&amp;autopause=0&amp;player_id=0&amp;app_id=58479" frameBorder="0" allow="autoplay; fullscreen; picture-in-picture" allowFullScreen style= {{ position:"absolute",top:0,left:0,width:"100%",height:"100%"}} title="Publish a mode to NPM"></iframe>
</div>
### Adding a Mode from NPM
The fifth video adds the mode and extension published in NPM to OHIF. Note
that since the mode references the extension both are added with one CLI
command.
<div style={{padding:"56.25% 0 0 0", position:"relative"}}>
<iframe src="https://player.vimeo.com/video/884893388?badge=0&amp;autopause=0&amp;player_id=0&amp;app_id=58479" frameBorder="0" allow="autoplay; fullscreen; picture-in-picture" allowFullScreen style= {{ position:"absolute",top:0,left:0,width:"100%",height:"100%"}} title="Add a mode from NPM"></iframe>
</div>
### Submitting a Mode to OHIF
The sixth video demonstrates how a mode can be submitted to OHIF to have it
appear in OHIF's mode gallery.
<div style={{padding:"56.25% 0 0 0", position:"relative"}}>
<iframe src="https://player.vimeo.com/video/884893372?badge=0&amp;autopause=0&amp;player_id=0&amp;app_id=58479" frameBorder="0" allow="autoplay; fullscreen; picture-in-picture" allowFullScreen style= {{ position:"absolute",top:0,left:0,width:"100%",height:"100%"}} title="Submit a mode to OHIF"></iframe>
</div>