fix(multiframe): metadata handling of NM studies and loading order (#4540)

This commit is contained in:
Alireza 2024-11-27 13:08:18 -05:00 committed by GitHub
parent 3b66867508
commit 71f06dcd3e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
60 changed files with 765 additions and 924 deletions

View File

@ -5,8 +5,6 @@ server {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
add_header Cross-Origin-Opener-Policy same-origin;
add_header Cross-Origin-Embedder-Policy require-corp;
add_header Cross-Origin-Resource-Policy same-origin;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;

View File

@ -7,8 +7,6 @@ server {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
add_header Cross-Origin-Opener-Policy same-origin;
add_header Cross-Origin-Embedder-Policy require-corp;
add_header Cross-Origin-Resource-Policy same-origin;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;

View File

@ -57,10 +57,6 @@ if (!process.env.APP_CONFIG) {
}
module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
if (!process.env.NODE_ENV) {
throw new Error('process.env.NODE_ENV not set');
}
const mode = NODE_ENV === 'production' ? 'production' : 'development';
const isProdBuild = NODE_ENV === 'production';
const isQuickBuild = QUICK_BUILD === 'true';
@ -108,7 +104,7 @@ module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
exclude: /node_modules/,
loader: 'babel-loader',
options: {
plugins: ['react-refresh/babel'],
plugins: isProdBuild ? [] : ['react-refresh/babel'],
},
},
]),

View File

@ -46,8 +46,8 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^2.2.4",
"@cornerstonejs/core": "^2.2.4",
"@cornerstonejs/adapters": "^2.2.20",
"@cornerstonejs/core": "^2.2.20",
"@kitware/vtk.js": "32.1.0",
"react-color": "^2.19.3"
}

View File

@ -46,8 +46,8 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^2.2.4",
"@cornerstonejs/core": "^2.2.4",
"@cornerstonejs/adapters": "^2.2.20",
"@cornerstonejs/core": "^2.2.20",
"@kitware/vtk.js": "32.1.0",
"react-color": "^2.19.3"
}

View File

@ -46,9 +46,9 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^2.2.4",
"@cornerstonejs/core": "^2.2.4",
"@cornerstonejs/tools": "^2.2.4",
"@cornerstonejs/adapters": "^2.2.20",
"@cornerstonejs/core": "^2.2.20",
"@cornerstonejs/tools": "^2.2.20",
"classnames": "^2.3.2"
}
}

View File

@ -42,8 +42,8 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/core": "^2.2.4",
"@cornerstonejs/tools": "^2.2.4",
"@cornerstonejs/core": "^2.2.20",
"@cornerstonejs/tools": "^2.2.20",
"classnames": "^2.3.2"
}
}

View File

@ -38,7 +38,7 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.4",
"@cornerstonejs/codec-openjph": "^2.4.5",
"@cornerstonejs/dicom-image-loader": "^2.2.4",
"@cornerstonejs/dicom-image-loader": "^2.2.20",
"@icr/polyseg-wasm": "^0.4.0",
"@ohif/core": "3.9.1",
"@ohif/ui": "3.9.1",
@ -55,9 +55,9 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^2.2.4",
"@cornerstonejs/core": "^2.2.4",
"@cornerstonejs/tools": "^2.2.4",
"@cornerstonejs/adapters": "^2.2.20",
"@cornerstonejs/core": "^2.2.20",
"@cornerstonejs/tools": "^2.2.20",
"@icr/polyseg-wasm": "^0.4.0",
"@kitware/vtk.js": "32.1.0",
"html2canvas": "^1.4.1",

View File

@ -45,7 +45,7 @@ import {
useSegmentationPresentationStore,
useSynchronizersStore,
} from './stores';
import { useToggleOneUpViewportGridStore } from '../../default/src/stores/useToggleOneUpViewportGridStore';
import { useToggleOneUpViewportGridStore } from '@ohif/extension-default';
import { useActiveViewportSegmentationRepresentations } from './hooks/useActiveViewportSegmentationRepresentations';
import { useMeasurements } from './hooks/useMeasurements';
import getPanelModule from './getPanelModule';

View File

@ -38,6 +38,7 @@ import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
import { useLutPresentationStore } from './stores/useLutPresentationStore';
import { usePositionPresentationStore } from './stores/usePositionPresentationStore';
import { useSegmentationPresentationStore } from './stores/useSegmentationPresentationStore';
import { imageRetrieveMetadataProvider } from '@cornerstonejs/core/utilities';
const { registerColormap } = csUtilities.colormap;
@ -135,9 +136,21 @@ export default async function init({
cornerstoneStreamingDynamicImageVolumeLoader
);
hangingProtocolService.registerImageLoadStrategy('interleaveCenter', interleaveCenterLoader);
hangingProtocolService.registerImageLoadStrategy('interleaveTopToBottom', interleaveTopToBottom);
hangingProtocolService.registerImageLoadStrategy('nth', nthLoader);
// Register strategies using the wrapper
const imageLoadStrategies = {
interleaveCenter: interleaveCenterLoader,
interleaveTopToBottom: interleaveTopToBottom,
nth: nthLoader,
};
Object.entries(imageLoadStrategies).forEach(([name, strategyFn]) => {
hangingProtocolService.registerImageLoadStrategy(
name,
createMetadataWrappedStrategy(strategyFn)
);
});
// ... existing code ...
// add metadata providers
metaData.addProvider(
@ -296,6 +309,28 @@ function initializeWebWorkerProgressHandler(uiNotificationService) {
});
}
/**
* Creates a wrapped image load strategy with metadata handling
* @param strategyFn - The image loading strategy function to wrap
* @returns A wrapped strategy function that handles metadata configuration
*/
const createMetadataWrappedStrategy = (strategyFn: (args: any) => any) => {
return (args: any) => {
const clonedConfig = imageRetrieveMetadataProvider.clone();
imageRetrieveMetadataProvider.clear();
try {
const result = strategyFn(args);
return result;
} finally {
// Ensure metadata is always restored, even if there's an error
setTimeout(() => {
imageRetrieveMetadataProvider.restore(clonedConfig);
}, 10);
}
};
};
function CPUModal() {
return (
<div>

View File

@ -59,10 +59,10 @@ const initMeasurementService = (
'Crosshairs',
Length.matchingCriteria,
() => {
console.warn('Crosshairs mapping not implemented.');
return null;
},
() => {
console.warn('Crosshairs mapping not implemented.');
return null;
}
);

View File

@ -1043,6 +1043,11 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
// Store the current position presentations for each viewport.
viewports.forEach(({ id: viewportId }) => {
const presentation = this._getPositionPresentation(viewportId);
// During a resize, the slice index should remain unchanged. This is a temporary fix for
// a larger issue regarding the definition of slice index with slab thickness.
// We need to revisit this to make it more robust and understandable.
delete presentation.viewReference.sliceIndex;
this.beforeResizePositionPresentations.set(viewportId, presentation);
});
@ -1051,10 +1056,12 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
renderingEngine.resize(isImmediate);
renderingEngine.render();
// Reset the camera for viewports that should reset their camera on resize,
// Reset the camera for all viewports using position presentation to maintain relative size/position
// which means only those viewports that have a zoom level of 1.
this.beforeResizePositionPresentations.forEach((positionPresentation, viewportId) => {
this.setPresentations(viewportId, { positionPresentation });
this.setPresentations(viewportId, {
positionPresentation,
});
});
// Resize and render the rendering engine again.

View File

@ -108,7 +108,7 @@ export default function interleaveTopToBottom({
volumes.forEach(volume => {
const requests = volume.getImageLoadRequests();
if (!requests.length || !requests[0] || !requests[0].imageId) {
if (!requests?.[0]?.imageId) {
return;
}

View File

@ -93,7 +93,7 @@ function createDicomJSONApi(dicomJsonConfig) {
const { query } = qs.parseUrl(instance.url);
// Add imageId specific mapping to this data as the URL isn't necessarliy WADO-URI.
// Add imageId specific mapping to this data as the URL isn't necessarily WADO-URI.
metadataProvider.addImageIdToUIDs(imageId, {
StudyInstanceUID,
SeriesInstanceUID,

View File

@ -142,9 +142,7 @@ function createDicomLocalApi(dicomLocalConfig) {
study.series.forEach(aSeries => {
const { SeriesInstanceUID } = aSeries;
const isMultiframe = aSeries.instances[0].NumberOfFrames > 1;
aSeries.instances.forEach((instance, index) => {
aSeries.instances.forEach(instance => {
const {
url: imageId,
StudyInstanceUID,
@ -153,14 +151,22 @@ function createDicomLocalApi(dicomLocalConfig) {
} = instance;
instance.imageId = imageId;
// Add imageId specific mapping to this data as the URL isn't necessarily WADO-URI.
metadataProvider.addImageIdToUIDs(imageId, {
StudyInstanceUID,
SeriesInstanceUID,
SOPInstanceUID,
frameIndex: isMultiframe ? index : 1,
});
const numberOfFrames = instance.NumberOfFrames || 1;
// Process all frames consistently, whether single or multiframe
for (let i = 0; i < numberOfFrames; i++) {
const frameNumber = i + 1;
const frameImageId = implementation.getImageIdsForInstance({
instance,
frame: frameNumber,
});
// Add imageId specific mapping to this data as the URL isn't necessarily WADO-URI.
metadataProvider.addImageIdToUIDs(frameImageId, {
StudyInstanceUID,
SeriesInstanceUID,
SOPInstanceUID,
frameNumber: numberOfFrames > 1 ? frameNumber : undefined,
});
}
});
DicomMetadataStore._broadcastEvent(EVENTS.INSTANCES_ADDED, {
@ -209,9 +215,11 @@ function createDicomLocalApi(dicomLocalConfig) {
return imageIds;
},
getImageIdsForInstance({ instance, frame }) {
if (instance.imageId) {
return instance.imageId;
}
// Important: Never use instance.imageId because it might be multiframe,
// which would make it an invalid imageId.
// if (instance.imageId) {
// return instance.imageId;
// }
const { StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID } = instance;
const storedInstance = DicomMetadataStore.getInstance(

View File

@ -434,23 +434,32 @@ function createDicomWebApi(dicomWebConfig, servicesManager) {
instance.wadoRoot = dicomWebConfig.wadoRoot;
instance.wadoUri = dicomWebConfig.wadoUri;
const imageId = implementation.getImageIdsForInstance({
instance,
});
const { StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID } = instance;
const numberOfFrames = instance.NumberOfFrames || 1;
// Process all frames consistently, whether single or multiframe
for (let i = 0; i < numberOfFrames; i++) {
const frameNumber = i + 1;
const frameImageId = implementation.getImageIdsForInstance({
instance,
frame: frameNumber,
});
// Add imageId specific mapping to this data as the URL isn't necessarily WADO-URI.
metadataProvider.addImageIdToUIDs(frameImageId, {
StudyInstanceUID,
SeriesInstanceUID,
SOPInstanceUID,
frameNumber: numberOfFrames > 1 ? frameNumber : undefined,
});
}
// Adding imageId to each instance
// Todo: This is not the best way I can think of to let external
// metadata handlers know about the imageId that is stored in the store
instance.imageId = imageId;
// Adding UIDs to metadataProvider
// Note: storing imageURI in metadataProvider since stack viewports
// will use the same imageURI
metadataProvider.addImageIdToUIDs(imageId, {
StudyInstanceUID,
SeriesInstanceUID: instance.SeriesInstanceUID,
SOPInstanceUID: instance.SOPInstanceUID,
const imageId = implementation.getImageIdsForInstance({
instance,
});
instance.imageId = imageId;
});
DicomMetadataStore.addInstances(naturalizedInstances, madeInClient);

View File

@ -32,8 +32,8 @@
"start": "yarn run dev"
},
"peerDependencies": {
"@cornerstonejs/core": "^2.2.4",
"@cornerstonejs/tools": "^2.2.4",
"@cornerstonejs/core": "^2.2.20",
"@cornerstonejs/tools": "^2.2.20",
"@ohif/core": "3.9.1",
"@ohif/extension-cornerstone-dicom-sr": "3.9.1",
"@ohif/extension-default": "3.9.1",

View File

@ -9,6 +9,7 @@ import { StudyBrowser } from '@ohif/ui-next';
import { useTrackedMeasurements } from '../../getContextModule';
import { Separator } from '@ohif/ui-next';
import { PanelStudyBrowserHeader } from '@ohif/extension-default';
import { useAppConfig } from '@state';
import { defaultActionIcons, defaultViewPresets } from './constants';
const { formatDate, createStudyBrowserTabs } = utils;
@ -27,7 +28,7 @@ const thumbnailNoImageModalities = [
*
* @param {*} param0
*/
function PanelStudyBrowserTracking({
export default function PanelStudyBrowserTracking({
servicesManager,
getImageSrc,
getStudiesForPatientByMRN,
@ -45,6 +46,10 @@ function PanelStudyBrowserTracking({
customizationService,
} = servicesManager.services;
const navigate = useNavigate();
const { mode: studyMode } = customizationService.getCustomization('PanelStudyBrowser.studyMode', {
id: 'default',
mode: 'all',
});
const { t } = useTranslation('Common');
@ -55,7 +60,8 @@ function PanelStudyBrowserTracking({
const [{ activeViewportId, viewports, isHangingProtocolLayout }, viewportGridService] =
useViewportGrid();
const [trackedMeasurements, sendTrackedMeasurementsEvent] = useTrackedMeasurements();
const [activeTabName, setActiveTabName] = useState('all');
const [activeTabName, setActiveTabName] = useState(studyMode);
const [expandedStudyInstanceUIDs, setExpandedStudyInstanceUIDs] = useState([
...StudyInstanceUIDs,
]);
@ -117,6 +123,10 @@ function PanelStudyBrowserTracking({
const { trackedSeries } = trackedMeasurements.context;
useEffect(() => {
setActiveTabName(studyMode);
}, [studyMode]);
// ~~ studyDisplayList
useEffect(() => {
// Fetch all studies for the patient in each primary study
@ -528,8 +538,6 @@ PanelStudyBrowserTracking.propTypes = {
requestDisplaySetCreationForStudy: PropTypes.func.isRequired,
};
export default PanelStudyBrowserTracking;
function getImageIdForThumbnail(displaySet: any, imageIds: any) {
let imageId;
if (displaySet.isDynamicVolume) {

View File

@ -6,6 +6,11 @@ export const performCustomizations = customizationService => {
id: 'PanelSegmentation.disableEditing',
disableEditing: true,
},
// to only show current study in the panel study browser
// {
// id: 'PanelStudyBrowser.studyMode',
// mode: 'primary',
// },
// To disable editing in the MeasurementTable
// {
// id: 'PanelMeasurement.disableEditing',

View File

@ -84,7 +84,7 @@ function modeFactory({ modeConfiguration }) {
lesionStats: 'Lesion Statistics',
minValue: 'Minimum Value',
maxValue: 'Maximum Value',
meanValue: 'Mean Value',
meanValue: 'Mean Value (ml)',
volume: 'Volume',
suvPeak: 'SUV Peak',
suvMax: 'Maximum SUV',

View File

@ -120,7 +120,7 @@ function modeFactory({ modeConfiguration }) {
minValue: 'Minimum Value',
maxValue: 'Maximum Value',
meanValue: 'Mean Value',
volume: 'Volume',
volume: 'Volume (ml)',
suvPeak: 'SUV Peak',
suvMax: 'Maximum SUV',
suvMaxIJK: 'SUV Max IJK',

View File

@ -221,8 +221,6 @@ http {
location / {
add_header Cache-Control "no-store, no-cache, must-revalidate";
add_header 'Cross-Origin-Opener-Policy' 'same-origin' always;
add_header 'Cross-Origin-Embedder-Policy' 'require-corp' always;
}
location /keycloak/ {

View File

@ -79,8 +79,6 @@ http {
index index.html;
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-store, no-cache, must-revalidate";
add_header Cross-Origin-Opener-Policy 'same-origin' always;
add_header Cross-Origin-Embedder-Policy 'require-corp' always;
}
}
}

View File

@ -191,8 +191,6 @@ http {
location / {
add_header Cache-Control "no-store, no-cache, must-revalidate";
add_header 'Cross-Origin-Opener-Policy' 'same-origin' always;
add_header 'Cross-Origin-Embedder-Policy' 'require-corp' always;
}
location /keycloak/ {

View File

@ -78,8 +78,6 @@ http {
index index.html;
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-store, no-cache, must-revalidate";
add_header 'Cross-Origin-Opener-Policy' 'same-origin' always;
add_header 'Cross-Origin-Embedder-Policy' 'require-corp' always;
}
}

View File

@ -182,8 +182,6 @@ http {
index index.html;
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-store, no-cache, must-revalidate";
add_header 'Cross-Origin-Opener-Policy' 'same-origin' always;
add_header 'Cross-Origin-Embedder-Policy' 'require-corp' always;
}
# EXAMPLE: Reverse Proxy, no auth

View File

@ -118,8 +118,6 @@ http {
index index.html;
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-store, no-cache, must-revalidate";
add_header 'Cross-Origin-Opener-Policy' 'same-origin' always;
add_header 'Cross-Origin-Embedder-Policy' 'require-corp' always;
}
# EXAMPLE: Redirect server error pages to the static page /40x.html

View File

@ -133,8 +133,6 @@ module.exports = (env, argv) => {
new InjectManifest({
swDest: 'sw.js',
swSrc: path.join(SRC_DIR, 'service-worker.js'),
// Increase the limit to 4mb:
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
// Need to exclude the theme as it is updated independently
exclude: [/theme/],
// Cache large files for the manifests to avoid warning messages
@ -174,10 +172,6 @@ module.exports = (env, argv) => {
disableDotRule: true,
index: PUBLIC_URL + 'index.html',
},
headers: {
'Cross-Origin-Embedder-Policy': 'require-corp',
'Cross-Origin-Opener-Policy': 'same-origin',
},
devMiddleware: {
writeToDisk: true,
},
@ -206,5 +200,9 @@ module.exports = (env, argv) => {
);
}
mergedConfig.watchOptions = {
ignored: /node_modules\/@cornerstonejs/,
};
return mergedConfig;
};

View File

@ -73,7 +73,10 @@ function getRuntimeLoadModesExtensions(modules) {
dynamicLoad.push(
` if( module==="${packageName}") {`,
` const imported = await window.browserImportFunction('${module.importPath}');`,
' return ' + (module.globalName ? `window["${module.globalName}"];` : `imported["${module.importName || 'default'}"];`),
' return ' +
(module.globalName
? `window["${module.globalName}"];`
: `imported["${module.importName || 'default'}"];`),
' }'
);
return;
@ -201,12 +204,6 @@ function writePluginImportsFile(SRC_DIR, DIST_DIR) {
'dist'
);
console.warn('copy plugins', [
...copyPluginPublicToDistBuild,
...copyPluginPublicToDistLink,
...copyPluginDistToDistBuild,
...copyPluginDistToDistLink,
]);
return [
...copyPluginPublicToDistBuild,
...copyPluginPublicToDistLink,

View File

@ -53,7 +53,7 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.4",
"@cornerstonejs/codec-openjph": "^2.4.5",
"@cornerstonejs/dicom-image-loader": "^2.2.4",
"@cornerstonejs/dicom-image-loader": "^2.2.20",
"@emotion/serialize": "^1.1.3",
"@ohif/core": "3.9.1",
"@ohif/extension-cornerstone": "3.9.1",

View File

@ -1,12 +1,3 @@
{
"rewrites": [{ "source": "*", "destination": "index.html" }],
"headers": [
{
"source": "**/*",
"headers": [
{ "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" },
{ "key": "Cross-Origin-Opener-Policy", "value": "same-origin" }
]
}
]
"rewrites": [{ "source": "*", "destination": "index.html" }]
}

View File

@ -2,7 +2,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
* {
min-height: 0 !important;
}

View File

@ -37,7 +37,7 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.4",
"@cornerstonejs/codec-openjph": "^2.4.5",
"@cornerstonejs/dicom-image-loader": "^2.2.4",
"@cornerstonejs/dicom-image-loader": "^2.2.20",
"@ohif/ui": "3.9.1",
"cornerstone-math": "0.1.9",
"dicom-parser": "^1.8.21"

View File

@ -8,7 +8,6 @@ import toNumber from '../utils/toNumber';
import combineFrameInstance from '../utils/combineFrameInstance';
class MetadataProvider {
private readonly studies: Map<string, any> = new Map();
private readonly imageURIToUIDs: Map<string, any> = new Map();
private readonly imageUIDsByImageId: Map<string, any> = new Map();
// Can be used to store custom metadata for a specific type.
@ -17,6 +16,10 @@ class MetadataProvider {
private readonly customMetadata: Map<string, any> = new Map();
addImageIdToUIDs(imageId, uids) {
if (!imageId) {
throw new Error('MetadataProvider::Empty imageId');
}
// This method is a fallback for when you don't have WADO-URI or WADO-RS.
// You can add instances fetched by any method by calling addInstance, and hook an imageId to point at it here.
// An example would be dicom hosted at some random site.
@ -35,6 +38,10 @@ class MetadataProvider {
}
_getInstance(imageId) {
if (!imageId) {
throw new Error('MetadataProvider::Empty imageId');
}
const uids = this.getUIDsFromImageID(imageId);
if (!uids) {
@ -455,17 +462,6 @@ class MetadataProvider {
}
getUIDsFromImageID(imageId) {
if (!imageId) {
throw new Error('MetadataProvider::Empty imageId');
}
// TODO: adding csiv here is not really correct. Probably need to use
// metadataProvider.addImageIdToUIDs(imageId, {
// StudyInstanceUID,
// SeriesInstanceUID,
// SOPInstanceUID,
// })
// somewhere else
const cachedUIDs = this.imageUIDsByImageId.get(imageId);
if (cachedUIDs) {
return cachedUIDs;

View File

@ -280,7 +280,7 @@ export default class CustomizationService extends PubSubService {
return customization;
}
const parent = this.getCustomization(customizationType);
const result = parent ? Object.assign(Object.create(parent), customization) : customization;
const result = parent ? Object.assign({}, parent, customization) : customization;
// Execute an nested type information
return result.transform?.(this) || result;
}

View File

@ -679,7 +679,7 @@ class StudyPrefetcherService extends PubSubService {
this._displaySetLoadingStates.clear();
this._imageIdsToDisplaySetsMap.clear();
this._inflightRequests.clear();
this.imageLoadPoolManager.clearRequestStack(IMAGE_REQUEST_TYPE);
this.imageLoadPoolManager.clearRequestStack(this.requestType);
this._broadcastEvent(this.EVENTS.SERVICE_STOPPED, {});
}

View File

@ -81,6 +81,7 @@ declare global {
}
export interface Config {
studyBrowserMode?: 'all' | 'primary';
routerBasename?: string;
customizationService?: CustomizationServiceType;
extensions?: string[];

View File

@ -1,3 +1,5 @@
import { vec3 } from 'gl-matrix';
/**
* Combine the Per instance frame data, the shared frame data
* and the root data objects.
@ -9,25 +11,28 @@
* single frame data. (eg frame is undefined is the same as frame===1).
*/
const combineFrameInstance = (frame, instance) => {
const { PerFrameFunctionalGroupsSequence, SharedFunctionalGroupsSequence, NumberOfFrames } =
instance;
const {
PerFrameFunctionalGroupsSequence,
SharedFunctionalGroupsSequence,
NumberOfFrames,
SpacingBetweenSlices,
} = instance;
if (PerFrameFunctionalGroupsSequence || NumberOfFrames > 1) {
const frameNumber = Number.parseInt(frame || 1);
const shared = (
SharedFunctionalGroupsSequence ? Object.values(SharedFunctionalGroupsSequence[0]) : []
)
.filter(it => !!it)
.map(it => it[0])
.filter(it => it !== undefined && typeof it === 'object');
const perFrame = (
PerFrameFunctionalGroupsSequence
? Object.values(PerFrameFunctionalGroupsSequence[frameNumber - 1])
: []
)
.filter(it => !!it)
.map(it => it[0])
.filter(it => it !== undefined && typeof it === 'object');
const shared = SharedFunctionalGroupsSequence
? Object.values(SharedFunctionalGroupsSequence[0])
.filter(Boolean)
.map(it => it[0])
.filter(it => typeof it === 'object')
: [];
const perFrame = PerFrameFunctionalGroupsSequence
? Object.values(PerFrameFunctionalGroupsSequence[frameNumber - 1])
.filter(Boolean)
.map(it => it[0])
.filter(it => typeof it === 'object')
: [];
// this is to fix NM multiframe datasets with position and orientation
// information inside DetectorInformationSequence
@ -35,9 +40,40 @@ const combineFrameInstance = (frame, instance) => {
instance.ImageOrientationPatient =
instance.DetectorInformationSequence[0].ImageOrientationPatient;
}
let ImagePositionPatientToUse = instance.ImagePositionPatient;
if (!instance.ImagePositionPatient && instance.DetectorInformationSequence) {
instance.ImagePositionPatient = instance.DetectorInformationSequence[0].ImagePositionPatient;
const imagePositionPatient = instance.DetectorInformationSequence[0].ImagePositionPatient;
const imageOrientationPatient = instance.ImageOrientationPatient;
// Calculate the position for the current frame
if (imageOrientationPatient && SpacingBetweenSlices) {
const rowOrientation = vec3.fromValues(
imageOrientationPatient[0],
imageOrientationPatient[1],
imageOrientationPatient[2]
);
const colOrientation = vec3.fromValues(
imageOrientationPatient[3],
imageOrientationPatient[4],
imageOrientationPatient[5]
);
const normalVector = vec3.cross(vec3.create(), rowOrientation, colOrientation);
const position = vec3.scaleAndAdd(
vec3.create(),
imagePositionPatient,
normalVector,
SpacingBetweenSlices * (frameNumber - 1)
);
ImagePositionPatientToUse = [position[0], position[1], position[2]];
}
}
console.debug('🚀 ~ ImagePositionPatientToUse:', ImagePositionPatientToUse);
const newInstance = Object.assign(instance, { frameNumber: frameNumber });
@ -52,7 +88,8 @@ const combineFrameInstance = (frame, instance) => {
// back to the dicomMetaStore so we don't have to do this again.
return {
...newInstance,
ImagePositionPatient: newInstance.ImagePositionPatient ?? [0, 0, frameNumber],
ImagePositionPatient: ImagePositionPatientToUse ??
newInstance.ImagePositionPatient ?? [0, 0, frameNumber],
};
} else {
return instance;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 307 KiB

View File

@ -102,18 +102,12 @@ directory:
yarn global add http-server
# Change the directory to the platform/app
cd platform/app
# Serve the files in our current directory
npx serve ./dist -c ../public/serve.json
```
:::caution
In the video below notice that there is `platform/viewer` which has been renamed to `platform/app` in the latest version
:::
<div style={{padding:"56.25% 0 0 0", position:"relative"}}>
<iframe src="https://player.vimeo.com/video/551957266?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>
### Automating Builds and Deployments

View File

@ -1,5 +1,5 @@
---
sidebar_position: 7
sidebar_position: 8
---
# Cross-Origin Information for OHIF
@ -7,42 +7,10 @@ sidebar_position: 7
This document describes various security configurations, settings and environments/contexts needed to fully leverage OHIFs capabilities. One may need some configurations while others might need ALL of them - it all depends on the environment OHIF is expected to run in.
In particular, three of OHIFs features depend on these configurations:
- [OHIFs use of SharedArrayBuffer](#sharedarraybuffer)
- [Embedding OHIF in an iframe](#embedding-ohif-in-an-iframe)
- [XMLHttpRequests to fetch data from data sources](#cors-in-ohif)
## SharedArrayBuffer
A `SharedArrayBuffer` is a JavaScript object that is similar to an `ArrayBuffer` but can be shared between web workers and the window that spawned them via the `postMessage` API. See [SharedArrayBuffer in MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer) for more information.
:::tip
To turn off Shared Array Buffer completely, just set `useSharedArrayBuffer` to `false` in the [OHIF configuration](../configuration/configurationFiles.md). But keep in mind that you will not get the performance boost that Shared Array Buffer offers for decoding and rendering big volumes where web workers write to the same memory space.
:::
### Security Requirements
In order to use `SharedArrayBuffer` objects in the browser, the following security conditions must be met:
- The page must be served in a [secure context](#secure-context).
- The page must have [cross-origin isolation](#cross-origin-isolation) enabled.
### `SharedArrayBuffer` in OHIF
OHIF uses `SharedArrayBuffer` in its volume loader (from Cornerstone3D). It comes with the benefit of improved performance and optimization at the cost of some configuration to use it.
As such, if the following popup is shown when launching OHIF then the OHIF server will have to be configured to permit the loading of volumetric images and data. Note that stack viewports are still available and functional even when this error is present.
![OHIF in non-secure context](../assets/img/ohif-non-secure-context.png)
To better determine which (if not all) of the [security requirements](#security-requirements) are lacking, have a look at the browser console.
Output in the console similar to the following indicates that OHIF is not running in a [secure context](#secure-context).
![browser console for non-secure context](../assets/img/browser-console-non-secure-context.png)
Absence of the above error in the console together with the presence of the Cross Origin Isolation popup warning, likely indicates that either or both of the [COOP](#coop---cross-origin-opener-policy) and/or [COEP](#coep---cross-origin-embedder-policy) headers are not set for OHIF.
## Embedding OHIF in an iframe
As described [here](./iframe.md), there are cases where OHIF will be embedded in an iframe. The following links provide more information for setting up and configuring OHIF to work in an iframe:
@ -64,9 +32,6 @@ URLs that are NOT local must be delivered over `https://` or `wss://` (i.e. TLS)
A page embedded in an iframe is considered secure if it itself and every one of its embedding ancestors are delivered securely. Otherwise it is deemed insecure.
### Why does OHIF require a secure context?
Beyond all of the inherent benefits of a secure connection, OHIF requires a secure context so that it can utilize [SharedArrayBuffer](#sharedarraybuffer) objects for volume rendering.
### Configuring/setting up a secure context
@ -143,91 +108,8 @@ add_header 'Access-Control-Allow-Origin' '*' always;
```
:::
## COOP - Cross-Origin Opener Policy
The COOP HTTP response header restricts the global, root document of the page from being referenced and accessed by another cross-origin document that might open the page in a window. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy) for more information.
### Header Values Pertinent to OHIF (see [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy#syntax) for more information)
|Value|Description|
|-----|-----------|
|same-origin|Restricts the document to be referenced by openers of the same origin only.|
### COOP in OHIF
COOP is required for [SharedArrayBuffer](#sharedarraybuffer) usage in OHIF. See also [Troubleshooting Cross-origin Isolation in OHIF](#troubleshooting-cross-origin-isolation-in-ohif).
## COEP - Cross-Origin Embedder Policy
The COEP HTTP response header restricts cross-origin documents from being embedded into a document (e.g. in an iframe, video, image, etc). See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy) for more information.
### Header Values Pertinent to OHIF (see [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy#syntax) for more information)
|Value|Description|
|-----|-----------|
|require-corp|Permits the document to load either of the following embedded resources: <ul><li>Those from the same origin</li><li>Cross-origin resources embedded by a DOM element that has the appropriate [crossorigin attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) set</li><li>Cross-origin resources with the appropriate [CORP response header](#corp---cross-origin-resource-policy)</li></ul>
|credentialless|See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy#syntax) for more information|
### COEP in OHIF
COEP is required for [SharedArrayBuffer](#sharedarraybuffer) usage in OHIF. See also [Troubleshooting Cross-origin Isolation in OHIF](#troubleshooting-cross-origin-isolation-in-ohif).
## Cross-origin Isolation
Cross-origin isolation is [enabled](https://web.dev/cross-origin-isolation-guide/#enable-cross-origin-isolation) for a web page when the following COOP and COEP headers are set.
- [COOP](#coop---cross-origin-opener-policy) with `same-origin`
- [COEP](#coep---cross-origin-embedder-policy) with `require-corp` or `credentialless`
### iframe
An iframe is considered to have cross-origin isolation enabled if it itself has the appropriate COOP and COEP headers set as well as every one of its embedding ancestors.
### Troubleshooting Cross-origin Isolation in OHIF
The [SharedArrayBuffer in OHIF](#sharedarraybuffer-in-ohif) section describes how to determine if there are problems with cross-origin isolation in OHIF. If it is determined that COOP and/or COEP is indeed an issue, then the COOP and COEP headers must be set for OHIF. How to accomplish this varies per server or service that is hosting OHIF. The following are just a few examples.
:::tip
In the default dev environment, the following can be set in the webpack.pwa.js file…
```javascript
devServer: {
headers: {
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "require-corp"
}
}
```
:::
:::tip
If deploying OHIF using Netlify, the Netlify configuration [file](https://docs.netlify.com/configure-builds/file-based-configuration/) can be used to configure the headers as such…
```
[[headers]]
# Define which paths this specific [[headers]] block will cover.
for = "/*"
[headers.values]
Cross-Origin-Opener-Policy = "same-origin"
Cross-Origin-Embedder-Policy = "require-corp"
```
:::
:::tip
If OHIF is served behind nginx, then the headers can be set in the nginx.conf file as follows. The [template nginx configuration file](https://github.com/OHIF/Viewers/blob/master/.docker/Viewer-v3.x/default.conf.template) for creating a [OHIF Docker image](./docker.md#building-the-docker-image) has an example of this too.
```nginx
server {
location / {
add_header Cross-Origin-Opener-Policy same-origin;
add_header Cross-Origin-Embedder-Policy require-corp;
}
}
```
:::
## CORP - Cross-Origin Resource Policy
The CORP HTTP response header indicates which origins can read and use a resource. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin_Resource_Policy) for more information.
### Header Values (see [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin_Resource_Policy#usage) for more information)
@ -239,12 +121,6 @@ The CORP HTTP response header indicates which origins can read and use a resourc
### OHIF and CORP
There are two scenarios where the CORP header is relevant to OHIF:
- [PDF from a Cross Origin DICOMweb Data Source](#pdf-from-a-cross-origin-dicomweb-data-source)
- [OHIF as a Cross-origin Resource in an iframe](#ohif-as-a-cross-origin-resource-in-an-iframe)
Both these scenarios stem from the fact that OHIF has to be served with the [COEP](#coep---cross-origin-embedder-policy) header to support [SharedArrayBuffer](#sharedarraybuffer).
#### PDF from a Cross Origin DICOMweb Data Source
@ -266,48 +142,3 @@ If the dcm4chee server and the OHIF server are hosted on the same site, then the
add_header 'Cross-Origin-Resource-Policy' 'same-site' always;
```
:::
#### OHIF as a Cross-origin Resource in an iframe
There are cases where [OHIF is embedded in an iframe](./iframe.md) and the embedding page is from a different origin. Again due to the [security requirements for SharedArrayBuffer](#security-requirements), [both OHIF and the embedding page](#iframe) must have the appropriate COEP header. In this scenario, OHIF is the cross-origin resource and since the `<iframe>` tag does not support the `crossorigin` attribute, OHIF must be served with the appropriate CORP header.
:::info
Setting the CORP header such that OHIF can be embedded in an iframe varies per server or service hosting OHIF. What follows are just a few examples. Note that whenever the embedding page is hosted on the same site as OHIF, consider using the `same-site` value instead of `cross-origin`.
:::
:::tip
In the default dev environment, the following can be set in the webpack.pwa.js file…
```javascript
devServer: {
headers: {
"Cross-Origin-Resource-Policy": "cross-origin",
}
}
```
:::
:::tip
If deploying OHIF using Netlify, the Netlify configuration [file](https://docs.netlify.com/configure-builds/file-based-configuration/) can be used to configure the header as such…
```
[[headers]]
# Define which paths this specific [[headers]] block will cover.
for = "/*"
[headers.values]
Cross-Origin-Resource-Policy = "cross-origin"
```
:::
:::tip
If OHIF is served behind nginx, then the header can be set in the nginx.conf file as follows.
```nginx
server {
location / {
add_header Cross-Origin-Resource-Policy cross-origin;
}
}
```
:::

View File

@ -35,22 +35,13 @@ PUBLIC_URL=/my-awesome-viewer/ APP_CONFIG=config/default.js yarn build
```
We can use the `npx serve` to serve the build folder. There are two things you need to consider however,
1. You need to change the public/serve.json file to reflect the new routerBasename in the destination (see the example below)
1. You need to change the `public/serve.json` file to reflect the new routerBasename in the destination (see the example below)
```json
// final serve.json
{
"rewrites": [{ "source": "*", "destination": "my-awesome-viewer/index.html" }],
"headers": [
{
"source": "**/*",
"headers": [
{ "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" },
{ "key": "Cross-Origin-Opener-Policy", "value": "same-origin" }
]
}
]
}
```

View File

@ -18,6 +18,14 @@ The machine on which to build and run the Docker container must have:
3. [Docker](https://docs.docker.com/get-docker/) installed.
## Building the Docker Image
:::info
In this tutorial, we will build the Docker image for the OHIF Viewer and OHIF server as defined in the `default.js` config. If you need a custom build, you can modify the configuration file to include your data sources and then build the Docker image. For more information on data sources, see [here](../platform/extensions/modules/data-source.md).
Below we show how to point the Docker image to a custom configuration file.
:::
The docker image can be built from a terminal window as such:
1. Switch to the OHIF Viewer code root directory.
2. Issue the following Docker command. Note that what follows `-t` flag is the `{name}:{tag}` for the Docker image and is arbitrary when creating a local Docker image.
@ -121,20 +129,8 @@ For SSL Docker deployments, the CORP header value is set [here](https://github.c
We make no claims or guarantees regarding this section concerning security. If in doubt, enlist the help of an expert and conduct proper audits.
:::
### Why SSL?
As described [here](./cors.md), OHIF must be used in a [secure context](./cors.md#secure-context) in order to fully leverage all of OHIF's capabilities. Whenever OHIF is not running in a secure context and is navigated to using the OHIF's server IP address (e.g. `http://192.168.1.162:3000`) or domain name (e.g. `http://my.ohif.server`) then the following popup message will be displayed in OHIF
![OHIF in non-secure context](../assets/img/ohif-non-secure-context.png)
and the following message will appear in the browser console.
![browser console for non-secure context](../assets/img/browser-console-non-secure-context.png)
:::info
The above indicate that OHIF is not running in a secure context. Among other things, this means information transferred to/from OHIF is not encrypted and certain capabilities such as 3D volume loading will NOT work. However, basic stack viewport functionality will continue to function.
Consideration must be given as to whether OHIF should be deployed in a secure context over SSL.
:::
If OHIF is not deployed over SSL, this means information transferred to/from OHIF is not encrypted. Consideration must be given as to whether OHIF should be deployed in a secure context over SSL.
### Specifying the SSL Port, Certificate and Private Key

View File

@ -1,5 +1,5 @@
---
sidebar_position: 8
sidebar_position: 7
sidebar_label: iframe
---
@ -40,7 +40,7 @@ The PUBLIC_URL tells the application where to find the static assets and the rou
### Try it locally
Download the index.html and the build (against the /ohif/ path) from [here](https://ohif-assets.s3.us-east-2.amazonaws.com/iframe-basic/Archive.zip)
Download the index.html and the build (against the /ohif/ path) from [here](https://ohif-assets-new.s3.us-east-1.amazonaws.com/iframe-basic/Archive.zip)
Then run the
@ -53,119 +53,3 @@ npx http-server unzipped-folder
You should be able to see
![Alt text](../assets/img/iframe-basic.png)
:::info
Notice the Cross Origin Isolation Warning. It is present to indicate that OHIF cannot render volumes because the volume viewports
use SharedArrayBuffer which is not allowed for non cross origin isolated apps. You can read more about Cross Origin Isolation here
https://web.dev/coop-coep/ or follow the steps below to enable it.
:::
### Fixing the Cross Origin Isolation Warning to enable volume rendering
For that we need a more sophisticated setup, since we need to add the Cross Origin Embedder Policy and Cross Origin Opener Policy headers
to make the parent app cross origin isolated. For that we can use an Express server. (Note: you can use any other method
to add the headers, this is just one of the methods)
Download files from [here](https://ohif-assets.s3.us-east-2.amazonaws.com/iframe-express/Archive.zip)
```js
const express = require("express")
const app = express()
app.use((req, res, next) => {
res.setHeader("Cross-Origin-Opener-Policy", "same-origin")
res.setHeader("Cross-Origin-Embedder-Policy", "require-corp")
next()
})
app.use(express.static("public")) // 'public' should be the folder with the static OHIF build files
app.listen(8080, () => console.log("Listening on port 8080!"))
```
![Alt text](../assets/img/iframe-headers.png)
:::tip
if you are using webpack with react you can set
```js
devServer: {
headers: {
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "require-corp"
}
}
```
:::
:::tip
If you are using Angular, you should modify the `angular.json` file to add the headers
```js
"serve": {
//
"configurations": {
//
"development": {
//
"headers": {
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "require-corp"
}
}
},
//
},
```
:::
## Development Server
If you are not using the static build, you can use the iframe to load the viewer from the local development server. For example, if you are running the viewer locally on port 3000, you can use the following iframe element to load the viewer:
```html
// e.g., app running on 3001 and iframe loading the viewer from 3000
<iframe src="http://localhost:3000" style="width: 100%; height: 500px; border: none"/>
```
Notice that not including the static build removes the need for
the PUBLIC_URL and the routerBasename. However, the Cross Origin Resource Policy (CORP)
headers must be set because the viewer will be loaded from a different port. You can read
more about CORP [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin_Resource_Policy).
Basically in the development server that is serving the viewer, add the following headers:
```js
// use this if the embedding app is running on the same site as OHIF
// (e.g. parent/embedding app is http://localhost:3001 and OHIF is http://localhost:3000)
"Cross-Origin-Resource-Policy": "same-site"
or
// use this if the embedding app is a completely different origin than OHIF (e.g.
// parent/embedding app is http://192.168.1.2 and OHIF is http://localhost:3000)
"Cross-Origin-Resource-Policy": "cross-origin"
```
:::info
You can't set the `Cross-Origin-Resource-Policy` to `same-origin` since the viewer is loaded from a different port.
:::
:::tip
If you are using webpack to serve the viewer it would be
```js
devServer: {
headers: {
"Cross-Origin-Resource-Policy": "same-site" // cross-origin is also valid
}
}
```
:::tip
Take a look at how other people have integrated OHIF in their react app
example1: https://github.com/OHIF/Viewers/issues/3371#issuecomment-1630405255
:::

View File

@ -35,61 +35,6 @@ 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

View File

@ -8,55 +8,4 @@ sidebar_label: Local Linking
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"
```
You can take a look at the Cornerstonejs tutorial for linking https://www.cornerstonejs.org/docs/contribute/linking

View File

@ -2,14 +2,6 @@
## Why do I keep seeing a Cross Origin Isolation warning
If you encounter a warning while running OHIF indicating that your application is not cross-origin isolated, it implies that volume rendering, such as MPR, will not function properly since they depend on Shared Array Buffers. To resolve this issue, we recommend referring to our comprehensive guide on Cross Origin Isolation available at [our dedicated cors page](../deployment/cors.md).
## What if my setup does not support the Shared Array Buffers API?
You can simply disable that by adding the `useSharedArrayBuffer: 'FALSE'` (notice the string FALSE), and the volumes will only use a regular
array buffer which is a bit slower but will work on all browsers.
## Viewer opens but does not show any thumbnails
Thumbnails may not appear in your DICOMWeb application for various reasons. This guide focuses on one primary scenario, which is you are using
@ -274,6 +266,10 @@ which then it will look like
![alt text](faq-measure-5.png)
:::info
There is also dedicated example for this in the [cornerstone3D examples](https://www.cornerstonejs.org/live-examples/dynamicallyaddannotations).
:::
## How do I sort the series in the study panel by a specific value

View File

@ -97,6 +97,35 @@ import { ReactComponent as arrowDown } from './../../assets/icons/arrow-down.svg
We have removed the Polyfill.io script from the Viewer. If you require polyfills, you can add them to your project manually. This change primarily affects Internet Explorer, which Microsoft has already [ended support for](https://learn.microsoft.com/en-us/lifecycle/faq/internet-explorer-microsoft-edge#is-internet-explorer-11-the-last-version-of-internet-explorer-).
---
## Webpack changes
We previously were copying dicom-image-loader wasm files to the public folder via
```js
// platform/app/.webpack/webpack.pwa.js
{
from: '../../../node_modules/@cornerstonejs/dicom-image-loader/dist/dynamic-import',
to: DIST_DIR,
},
```
but now after our upgrade to Cornerstone 3D 2.0, we don't need to do this anymore.
---
## Scroll utility
The `jumpToSlice` utility has been relocated from `@cornerstonejs/tools` utilities to `@cornerstonejs/core/utilities`.
migration
```js
import { jumpToSlice } from '@cornerstonejs/core/utilities';
```
---
@ -178,9 +207,19 @@ Now:
---
## CustomizationService
The `CustomizationService` uses `contentF` instead of `content`.
So make sure your customizations are updated accordingly.
---
## SidePanel auto switch if open
In `basic viewer` mode, if the side panel is open and the segmentation panel is active, adding a measurement will automatically switch to the measurement panel. This switch won't occur if the side panel is closed. To enable or disable this feature, adjust your mode configuration accordingly.
In `basic viewer` mode, when the side panel is open and the segmentation panel is active, adding a measurement will automatically switch to the measurement panel. This switch won't happen if the side panel is closed. To enable or disable this feature, adjust your mode configuration accordingly.
To prevent this behavior, remove the following code from your mode:
```js
panelService.addActivatePanelTriggers('your.panel.id', [

View File

@ -2,9 +2,6 @@
title: UI
---
## New Components
You can explore our new playground at `docs.ohif.org/ui` to see the latest components and their properties. We haven't provided a migration guide yet because the old components are still available. Feel free to update your codebase, including custom extensions and UI, to use the new Button, Dropdown, Icons, and other new components from `@ohif/ui-next`. The old methods (importing from `@ohif/ui`) will continue to work for now. However, the new components have a slightly different API, and we plan to deprecate the old components in a future release, as we see the new ones as the future of OHIF.
@ -147,3 +144,13 @@ All the DropDown and Icons are now in the @ohif/ui-next package.
---
## ui, ui-next configs
We currently have two component libraries that we plan to merge in the future, so we need to maintain both configurations. If your styles aren't applying correctly, ensure you update both `platform/ui-next/tailwind.config.js` and `platform/ui/tailwind.config.js`.
### addIcon from ui-next
if you add custom icons, you may need to add them using a new `addIcon` utility which adds the icon to both `ui` and `ui-next`.

View File

@ -84,7 +84,7 @@ important api endpoints that you need to implement:
- `initialize`: This method is called when the data source is first created in the mode.tsx, it is used to initialize the data source and set the configuration. For instance, `dicomwebDatasource` uses this method to grab the StudyInstanceUID from the URL and set it as the active study, as opposed to `dicomJSONDatasource` which uses url in the browser to fetch the data and store it in a cache
- `query.studies.search`: This is used in the study panel on the left to fetch the prior studies for the same MRN which is then used to display on the `All` tab. it is also used in the Worklist to show all the studies from the server.
- `query.series.search`: This is used to fetch the series information for a given study that is expanded in the Worklist.
- `retrieve.bulkDataURI`: used to render RTSTUCTURESET in the viewport.
- `retrieve.bulkDataURI`: used to render RTSTUCTURESET in the viewport. It is an object that contains `enabled` as property and other options that are specific to the data source.
- `retrieve.series.metadata`: It is a crucial end point that is used to fetch series level metadata which for hanging displaySets and displaySet creation.
- `store.dicom`: If you don't need store functionality, you can skip this method. This is used to store the data in the backend.

View File

@ -137,7 +137,52 @@ this pattern, where multiple toolbar buttons are using the same evaluator but wi
},
```
#### Viewport and Modality Support Evaluation
The toolbar system now uses a more robust approach for evaluating button states based on viewport types and modalities:
**Viewport Type Support**:
Use `evaluate.viewport.supported` to disable buttons for specific viewport types:
```js
{
name: 'evaluate.viewport.supported',
unsupportedViewportTypes: ['volume3d', 'video', 'sm']
}
```
**Modality Support**:
Use `evaluate.modality.supported` to control button state based on modalities:
```js
{
name: 'evaluate.modality.supported',
supportedModalities: ['CT', 'MR'], // Enable only for these modalities
// OR
unsupportedModalities: ['US'] // Disable for these modalities
}
```
#### Composing evaluators
Multiple evaluators can be combined to create complex conditions:
```js
evaluate: [
'evaluate.cine',
{
name: 'evaluate.viewport.supported',
unsupportedViewportTypes: ['volume3d']
},
{
name: 'evaluate.modality.supported',
supportedModalities: ['CT']
}
]
```
This evaluation system provides more precise control over when toolbar buttons are enabled or disabled based on the active viewport's characteristics.
You can choose to set up multiple evaluators for a single button. This comes in handy when you need to assess the button according to various conditions. For example, we aim to prevent the Cine player from showing up on the 3D viewport, so we have:

View File

@ -5,50 +5,141 @@ sidebar_label: Segmentation Service
# Segmentation Service
## Overview
Using Segmentation Service you can create, edit and delete segmentation data, and
change appearance of the segmentation including color, opacity and visibility.
Segmentations in OHIF are based on the Segmentations in Cornerstone3D. You can
read more about it in the [Cornerstone Segmentation](https://www.cornerstonejs.org/docs/concepts/cornerstone-tools/segmentation/). OHIF currently only supports
one representation of the segmentation data.
## Events
There are seven events that get publish in `MeasurementService`:
```typescript
SEGMENTATION_MODIFIED // When a segmentation is updated
SEGMENTATION_DATA_MODIFIED // When segmentation data changes
SEGMENTATION_ADDED // When new segmentation is added
SEGMENTATION_REMOVED // When segmentation is removed
SEGMENT_LOADING_COMPLETE // When segment group adds pixel data to volume
SEGMENTATION_LOADING_COMPLETE // When full segmentation volume is filled
```
| Event | Description |
| --------------------- | ------------------------------------------------------ |
| SEGMENTATION_MODIFIED | Fires when a segmentation is updated e.g., segment added, removed etc.|
| SEGMENTATION_DATA_MODIFIED | Fires when the segmentation data changes |
| SEGMENTATION_ADDED | Fires when a new segmentation is added to OHIF |
| SEGMENTATION_REMOVED | Fires when a segmentation is removed from OHIF |
| SEGMENT_LOADING_COMPLETE | Fires when a segment group adds its pixel data to the volume |
| SEGMENTATION_LOADING_COMPLETE | Fires when the full segmentation volume is filled with its segments |
## Core APIs
### Creation Methods
## API
```typescript
createLabelmapForDisplaySet(
displaySet,
{
segmentationId?: string,
label: string,
segments?: {
[segmentIndex: number]: Partial<Segment>
}
}
)
```
### Segmentation Creation
### Segmentation Management
- `createEmptyLabelmapForDisplaySetUID`: based on a reference displaySet, create a new segmentation. E.g., create a new segmentation based on a CT series
- `createSegmentationForSEGDisplaySet`: given a segDisplaySet loaded by a sopClassHandler, create a new segmentation
- `addSegmentationRepresentationToToolGroup`: given the toolGroupId, add the given segmentationId to the toolGroup.
```typescript
setActiveSegmentation(viewportId, segmentationId)
getSegmentations()
getSegmentation(segmentationId)
jumpToSegmentCenter(segmentationId, segmentIndex, viewportId)
highlightSegment(segmentationId, segmentIndex, viewportId)
```
### Segment Operations
### Segmentation Behavior
```typescript
addSegment(segmentationId, {
segmentIndex?: number,
label?: string,
color?: [number, number, number, number], // RGBA
visibility?: boolean,
isLocked?: boolean,
active?: boolean
})
- setActiveSegmentationForToolGroup, getSegmentations, getSegmentation, jumpToSegmentCenter, highlightSegment
setSegmentColor(viewportId, segmentationId, segmentIndex, color)
setSegmentVisibility(viewportId, segmentationId, segmentIndex, visibility)
```
## Data Structures
### Segment Behavior
### Segmentation Object
- setSegmentLocked, removeSegment, addSegment, setSegmentLocked, setSegmentLabel, setActiveSegment,
setSegmentRGBAColor
```typescript
interface Segmentation {
segmentationId: string;
label: string;
segments: {
[segmentIndex: number]: {
segmentIndex: number;
label: string;
locked: boolean;
cachedStats: { [key: string]: unknown };
active: boolean;
}
};
representationData: RepresentationsData;
}
```
### Segmentation Configuration
## Code Examples
Setters
### Creating a Segmentation
- setSegmentVisibility, setSegmentColor, setSegmentRGBA, setSegmentOpacity, toggleSegmentationVisibility,
```typescript
const displaySet = displaySetService.getDisplaySetByUID(displaySetUID);
const segmentationId = await segmentationService.createLabelmapForDisplaySet(
displaySet,
{
label: 'New Segmentation',
segments: {
1: {
label: 'First Segment',
active: true
}
}
}
);
```
### Managing Active Segmentations
```typescript
segmentationService.setActiveSegmentation('viewport-1', segmentationId);
```
### Adding Segments
```typescript
segmentationService.addSegment(segmentationId, {
label: 'Tumor',
color: [255, 0, 0, 255], // RGBA format
active: true
});
```
### Visibility Management
```typescript
// Set segment visibility
segmentationService.setSegmentVisibility(
'viewport-1',
segmentationId,
1, // segmentIndex
true // visible
);
// Get viewport IDs with segmentation
const viewportIds = segmentationService.getViewportIdsWithSegmentation(segmentationId);
```
### Segment Styling
```typescript
// Set segment color
segmentationService.setSegmentColor(
'viewport-1',
segmentationId,
1, // segmentIndex
[255, 0, 0, 255] // RGBA
);
```

View File

@ -41,16 +41,16 @@
]
},
"dependencies": {
"@docusaurus/core": "3.6.0",
"@docusaurus/faster": "3.6.0",
"@docusaurus/plugin-client-redirects": "3.6.0",
"@docusaurus/plugin-google-gtag": "3.6.0",
"@docusaurus/plugin-ideal-image": "3.6.0",
"@docusaurus/plugin-pwa": "3.6.0",
"@docusaurus/preset-classic": "3.6.0",
"@docusaurus/remark-plugin-npm2yarn": "3.6.0",
"@docusaurus/theme-classic": "3.6.0",
"@docusaurus/theme-live-codeblock": "3.6.0",
"@docusaurus/core": "3.6.1",
"@docusaurus/faster": "3.6.1",
"@docusaurus/plugin-client-redirects": "3.6.1",
"@docusaurus/plugin-google-gtag": "3.6.1",
"@docusaurus/plugin-ideal-image": "3.6.1",
"@docusaurus/plugin-pwa": "3.6.1",
"@docusaurus/preset-classic": "3.6.1",
"@docusaurus/remark-plugin-npm2yarn": "3.6.1",
"@docusaurus/theme-classic": "3.6.1",
"@docusaurus/theme-live-codeblock": "3.6.1",
"@mdx-js/react": "3.0.1",
"@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-checkbox": "^1.1.1",

View File

@ -139,14 +139,7 @@ const DataRow: React.FC<DataRowProps> = ({
className="whitespace-pre-wrap"
>
{indentation}
{cleanText.includes(':') ? (
<>
<span className="font-medium">{cleanText.split(':')[0]}:</span>
{cleanText.split(':')[1]}
</>
) : (
<span className="font-medium">{cleanText}</span>
)}
<span className="font-medium">{cleanText}</span>
</div>
);
};
@ -199,14 +192,14 @@ const DataRow: React.FC<DataRowProps> = ({
{/* Hover Overlay */}
<div className="bg-primary/20 pointer-events-none absolute inset-0 opacity-0 transition-opacity group-hover:opacity-100"></div>
{/* Number Box */}
<div
className={`flex h-7 max-h-7 w-7 flex-shrink-0 items-center justify-center rounded-l border-r border-black text-base ${
isSelected ? 'bg-highlight text-black' : 'bg-muted text-muted-foreground'
} overflow-hidden`}
>
{number}
</div>
{/* Number Box */}
<div
className={`flex h-7 max-h-7 w-7 flex-shrink-0 items-center justify-center rounded-l border-r border-black text-base ${
isSelected ? 'bg-highlight text-black' : 'bg-muted text-muted-foreground'
} overflow-hidden`}
>
{number}
</div>
{/* Color Circle (Optional) */}
{colorHex && (
@ -313,10 +306,16 @@ const DataRow: React.FC<DataRowProps> = ({
</div>
</div>
{details && details.primary?.length > 0 && (
{/* Details Section */}
{details && (details.primary?.length > 0 || details.secondary?.length > 0) && (
<div className="ml-7 px-2 py-2">
<div className="text-secondary-foreground text-base leading-normal">
{renderDetails(details.primary)}
<div className="text-secondary-foreground flex items-center gap-1 text-base leading-normal">
{details.primary?.length > 0 && renderDetails(details.primary)}
{details.secondary?.length > 0 && (
<div className="text-muted-foreground ml-auto text-sm">
{renderDetails(details.secondary)}
</div>
)}
</div>
</div>
)}

View File

@ -34,7 +34,7 @@ export const SegmentationSegments: React.FC<{
}
const segmentCount = Object.keys(representationToUse.segments).length;
const height = mode === 'collapsed' ? 'h-[600px]' : `h-[${segmentCount * 200}px]`;
const height = mode === 'collapsed' ? 'h-[900px]' : `h-[${segmentCount * 200}px]`;
return (
<ScrollArea

View File

@ -398,17 +398,6 @@ SidePanel.propTypes = {
side: PropTypes.oneOf(['left', 'right']).isRequired,
className: PropTypes.string,
activeTabIndex: PropTypes.number,
tabs: PropTypes.oneOfType([
PropTypes.arrayOf(
PropTypes.shape({
iconName: PropTypes.string.isRequired,
iconLabel: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
content: PropTypes.func, // TODO: Should be node, but it keeps complaining?
})
),
]),
onOpen: PropTypes.func,
onClose: PropTypes.func,
onActiveTabIndexChange: PropTypes.func,

View File

@ -4,10 +4,6 @@
@tailwind components;
@tailwind utilities;
* {
min-height: 0 !important;
}
/* OHIF Theme */
@layer base {

View File

@ -237,7 +237,7 @@ const SidePanel = ({
content={getToolTipContent(childComponent.label, childComponent.disabled)}
className={classnames(
'flex items-center',
side === 'left' ? 'justify-end ' : 'justify-start '
side === 'left' ? 'justify-end' : 'justify-start'
)}
>
<div
@ -292,7 +292,7 @@ const SidePanel = ({
const numCols = getNumGridColumns(tabs.length, gridWidth);
return (
<div className={classnames('flex grow ', side === 'right' ? 'justify-start' : 'justify-end')}>
<div className={classnames('flex grow', side === 'right' ? 'justify-start' : 'justify-end')}>
<div
className={classnames('bg-primary-dark text-primary-active flex flex-wrap')}
style={getGridStyle(side, tabs.length, gridWidth, expandedWidth)}
@ -354,7 +354,7 @@ const SidePanel = ({
return (
<div
className={classnames(
'text-primary-active flex grow cursor-pointer select-none justify-center self-center text-[13px]'
'text-primary-active flex grow cursor-pointer select-none justify-center self-center text-[13px]'
)}
style={{
...(side === 'left'
@ -371,7 +371,7 @@ const SidePanel = ({
const getOpenStateComponent = () => {
return (
<div className="bg-primary-dark flex select-none rounded-t pt-1.5 pb-[2px] ">
<div className="bg-primary-dark flex select-none rounded-t pt-1.5 pb-[2px]">
{getCloseIcon()}
{tabs.length === 1 ? getOneTabComponent() : getTabGridComponent()}
</div>

View File

@ -5,10 +5,6 @@
@tailwind components;
@tailwind utilities;
* {
min-height: 0 !important;
}
@layer base {
html {
font-family: 'Inter', sans-serif;

553
yarn.lock
View File

@ -2580,10 +2580,10 @@
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
"@cornerstonejs/adapters@^2.2.4":
version "2.2.4"
resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-2.2.4.tgz#6f5edf17dc5f76cb6c80d67a4b70c38c1ca05700"
integrity sha512-sWatnhu93NjaT8MsrJIsexpxzFrUmtxrS6lSBca9CWNDMLR1t8boff6fmvGKE8TYbQkPQZrytRJBSw5O6pWdMg==
"@cornerstonejs/adapters@^2.2.20":
version "2.2.20"
resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-2.2.20.tgz#82fd0775a6ac4e138bda4f3dad412593133b16a9"
integrity sha512-o9c3eTJa7D0wgdkAdg33/BET5+BgM5qjt6Xmd/QSXsghPyJcmvnxIzKS+upt2///u+f8q4aaiL3dgsVzNc6L9g==
dependencies:
"@babel/runtime-corejs2" "^7.17.8"
buffer "^6.0.3"
@ -2616,19 +2616,19 @@
resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-openjph/-/codec-openjph-2.4.5.tgz#8690b61a86fa53ef38a70eee9d665a79229517c0"
integrity sha512-MZCUy8VG0VG5Nl1l58+g+kH3LujAzLYTfJqkwpWI2gjSrGXnP6lgwyy4GmPRZWVoS40/B1LDNALK905cNWm+sg==
"@cornerstonejs/core@^2.2.4":
version "2.2.4"
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-2.2.4.tgz#eeb4e383a332e43186299cb19ddd46dad1013437"
integrity sha512-nw14sTuz02o2W8a0WT0dH4J1B7iZ4KZU5r0KDnYkpDHjL1Y30lmSt098Pxb4Z9Xj+GJYunlWv962RtVhnWbSZA==
"@cornerstonejs/core@^2.2.20":
version "2.2.20"
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-2.2.20.tgz#a39176c0f832ce2d91f414a2f06010c54c3b99b0"
integrity sha512-1gfARz1tqoAnic1rTGILF9MbwC0YICCNWS+k5vCkf+1ZX3XYepMt3bV3GPPAcTDJ2t6t20Ii1xdKueDn3BSIXw==
dependencies:
"@kitware/vtk.js" "32.1.0"
"@kitware/vtk.js" "32.1.1"
comlink "^4.4.1"
gl-matrix "^3.4.3"
"@cornerstonejs/dicom-image-loader@^2.2.4":
version "2.2.4"
resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-2.2.4.tgz#117635b11a0230381fe31a5119271b472f7048d5"
integrity sha512-DwRZrTaVrrH7aGqFibcL+Ijnygtd3sxovxREzHWqGrmmxpwsm+aA9Z5nXeg58hk5Kvd0Zt8iwlrtcnTeD2DPiw==
"@cornerstonejs/dicom-image-loader@^2.2.20":
version "2.2.20"
resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-2.2.20.tgz#3d1d448ac2c7010ae29359209f5a6b1790777930"
integrity sha512-pYyYzPbbwOU9tH84WAhBK/+XdJ/Gc3hYfsrsuGk3ufVsdjRywvjGviSOYN4My0w++ezanaSFdPNcghtqdL8etQ==
dependencies:
"@cornerstonejs/codec-charls" "^1.2.3"
"@cornerstonejs/codec-libjpeg-turbo-8bit" "^1.2.2"
@ -2639,10 +2639,10 @@
pako "^2.0.4"
uuid "^9.0.0"
"@cornerstonejs/tools@^2.2.4":
version "2.2.4"
resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-2.2.4.tgz#3fa5e99daffc73434d7385885dda09e17820756c"
integrity sha512-pEqJzAp4/ZtVmWqPG7C/hH/09dt6OBoRq5fiEJE/mxJXbe77FeDq5zQy8Irv7IkDpXpnc8kL+6L9Zb5awc3uRA==
"@cornerstonejs/tools@^2.2.20":
version "2.2.20"
resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-2.2.20.tgz#d2fcbc1b6f585f4094bb5d03d3ae87862e1df092"
integrity sha512-rS7KNfLjwtZ0MzP9eT5EDKBDoEh/Nt5htJxj9b/BvL2W6X/vyF361tWYf/L2dIo0NdR6LBYds3Gdm31y+b7drA==
dependencies:
"@types/offscreencanvas" "2019.7.3"
comlink "^4.4.1"
@ -2806,10 +2806,10 @@
"@docsearch/css" "3.6.2"
algoliasearch "^4.19.1"
"@docusaurus/babel@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/babel/-/babel-3.6.0.tgz#735a003207925bd782dd08ffa5d8b3503c1f8d72"
integrity sha512-7CsoQFiadoq7AHSUIQNkI/lGfg9AQ2ZBzsf9BqfZGXkHwWDy6twuohEaG0PgQv1npSRSAB2dioVxhRSErnqKNA==
"@docusaurus/babel@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/babel/-/babel-3.6.1.tgz#5f48a275934b8164ccac3a6fd1fca3741374c884"
integrity sha512-JcKaunW8Ml2nTnfnvFc55T00Y+aCpNWnf1KY/gG+wWxHYDH0IdXOOz+k6NAlEAerW8+VYLfUqRIqHZ7N/DVXvQ==
dependencies:
"@babel/core" "^7.25.9"
"@babel/generator" "^7.25.9"
@ -2821,23 +2821,23 @@
"@babel/runtime" "^7.25.9"
"@babel/runtime-corejs3" "^7.25.9"
"@babel/traverse" "^7.25.9"
"@docusaurus/logger" "3.6.0"
"@docusaurus/utils" "3.6.0"
"@docusaurus/logger" "3.6.1"
"@docusaurus/utils" "3.6.1"
babel-plugin-dynamic-import-node "^2.3.3"
fs-extra "^11.1.1"
tslib "^2.6.0"
"@docusaurus/bundler@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/bundler/-/bundler-3.6.0.tgz#bdd060ba4d009211348e4e973a3bf4861cf0996b"
integrity sha512-o5T9HXkPKH0OQAifTxEXaebcO8kaz3tU1+wlIShZ2DKJHlsyWX3N4rToWBHroWnV/ZCT2XN3kLRzXASqrnb9Tw==
"@docusaurus/bundler@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/bundler/-/bundler-3.6.1.tgz#240343d31f39638f987caf54793c09820270ecd5"
integrity sha512-vHSEx8Ku9x/gfIC6k4xb8J2nTxagLia0KvZkPZhxfkD1+n8i+Dj4BZPWTmv+kCA17RbgAvECG0XRZ0/ZEspQBQ==
dependencies:
"@babel/core" "^7.25.9"
"@docusaurus/babel" "3.6.0"
"@docusaurus/cssnano-preset" "3.6.0"
"@docusaurus/logger" "3.6.0"
"@docusaurus/types" "3.6.0"
"@docusaurus/utils" "3.6.0"
"@docusaurus/babel" "3.6.1"
"@docusaurus/cssnano-preset" "3.6.1"
"@docusaurus/logger" "3.6.1"
"@docusaurus/types" "3.6.1"
"@docusaurus/utils" "3.6.1"
autoprefixer "^10.4.14"
babel-loader "^9.2.1"
clean-css "^5.3.2"
@ -2858,18 +2858,18 @@
webpack "^5.95.0"
webpackbar "^6.0.1"
"@docusaurus/core@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-3.6.0.tgz#b23fc7e253a49cc3e5ac9e091354f497cc0b101b"
integrity sha512-lvRgMoKJJSRDt9+HhAqFcICV4kp/mw1cJJrLxIw4Q2XZnFGM1XUuwcbuaqWmGog+NcOLZaPCcCtZbn60EMCtjQ==
"@docusaurus/core@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-3.6.1.tgz#260d78e1eb7129ccb441fa944b5f7e6f492ac6cb"
integrity sha512-cDKxPihiM2z7G+4QtpTczS7uxNfNG6naSqM65OmAJET0CFRHbc9mDlLFtQF0lsVES91SHqfcGaaLZmi2FjdwWA==
dependencies:
"@docusaurus/babel" "3.6.0"
"@docusaurus/bundler" "3.6.0"
"@docusaurus/logger" "3.6.0"
"@docusaurus/mdx-loader" "3.6.0"
"@docusaurus/utils" "3.6.0"
"@docusaurus/utils-common" "3.6.0"
"@docusaurus/utils-validation" "3.6.0"
"@docusaurus/babel" "3.6.1"
"@docusaurus/bundler" "3.6.1"
"@docusaurus/logger" "3.6.1"
"@docusaurus/mdx-loader" "3.6.1"
"@docusaurus/utils" "3.6.1"
"@docusaurus/utils-common" "3.6.1"
"@docusaurus/utils-validation" "3.6.1"
boxen "^6.2.1"
chalk "^4.1.2"
chokidar "^3.5.3"
@ -2907,21 +2907,22 @@
webpack-dev-server "^4.15.2"
webpack-merge "^6.0.1"
"@docusaurus/cssnano-preset@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-3.6.0.tgz#02378e53e9568ed5fc8871d4fc158ea96fd7421c"
integrity sha512-h3jlOXqqzNSoU+C4CZLNpFtD+v2xr1UBf4idZpwMgqid9r6lb5GS7tWKnQnauio6OipacbHbDXEX3JyT1PlDkg==
"@docusaurus/cssnano-preset@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-3.6.1.tgz#dc07b15f37d5c7bc1e59255ce0fa8825dde2dfb7"
integrity sha512-ZxYUmNeyQHW2w4/PJ7d07jQDuxzmKr9uPAQ6IVe5dTkeIeV0mDBB3jOLeJkNoI42Ru9JKEqQ9aVDtM9ct6QHnw==
dependencies:
cssnano-preset-advanced "^6.1.2"
postcss "^8.4.38"
postcss-sort-media-queries "^5.2.0"
tslib "^2.6.0"
"@docusaurus/faster@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/faster/-/faster-3.6.0.tgz#70540e689fd16e26e6105fe689a1347b8ac93834"
integrity sha512-9X06/KHD3f4CKm0SjZQIK3UDE/XvouAMiAlYMPV5LprubFlxVQecZG9QnfOe7VIfUh9IuPiDPuGg17yy0efwww==
"@docusaurus/faster@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/faster/-/faster-3.6.1.tgz#f564abe9904c4132ca32164012f8427270c257c8"
integrity sha512-W3a9m7Q/fEeOpOw9/XktLCHRtp1sV2AdZWMCjH3kP1jY1TDyLFFiHJ0+1uwVpOw4/oPJqZSTRKP+IdW4+65NgQ==
dependencies:
"@docusaurus/types" "3.6.1"
"@rspack/core" "^1.0.14"
"@swc/core" "^1.7.39"
"@swc/html" "^1.7.39"
@ -2931,33 +2932,33 @@
tslib "^2.6.0"
webpack "^5.95.0"
"@docusaurus/logger@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-3.6.0.tgz#c7349c2636087f55f573a60a3c7f69b87d59974d"
integrity sha512-BcQhoXilXW0607cH/kO6P5Gt5KxCGfoJ+QDKNf3yO2S09/RsITlW+0QljXPbI3DklTrHrhRDmgGk1yX4nUhWTA==
"@docusaurus/logger@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-3.6.1.tgz#724b7f9d8c435c9933d52792458659471ec90919"
integrity sha512-OvetI/nnOMBSqCkUzKAQhnIjhxduECK4qTu3tq/8/h/qqvLsvKURojm04WPE54L+Uy+UXMas0hnbBJd8zDlEOw==
dependencies:
chalk "^4.1.2"
tslib "^2.6.0"
"@docusaurus/lqip-loader@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/lqip-loader/-/lqip-loader-3.6.0.tgz#c40cbe74690455e5c65749d4a9a2bc00806a91a9"
integrity sha512-jEQjnKAjhgyZ/aQf0Tbl3eDpp1j0C/GojE/cv2Bad89o+luV1OAOs9UHDRyKwWQfN+se0D9kFxkfsfwYxMpI8A==
"@docusaurus/lqip-loader@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/lqip-loader/-/lqip-loader-3.6.1.tgz#b5a4956b8906b2087e2f9429c63ddaff184f8f77"
integrity sha512-H/VVvnvFupFhQ81FuTyA/XHxEZPKh99T6Wg6KgN+/yvcn7869RdgrlDhKDnXZ7j2u80eFsVNjAcPfW1cSAtK6A==
dependencies:
"@docusaurus/logger" "3.6.0"
"@docusaurus/logger" "3.6.1"
file-loader "^6.2.0"
lodash "^4.17.21"
sharp "^0.32.3"
tslib "^2.6.0"
"@docusaurus/mdx-loader@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-3.6.0.tgz#f8ba7af9d59473a7182f6a9307e0432f8dce905b"
integrity sha512-GhRzL1Af/AdSSrGesSPOU/iP/aXadTGmVKuysCxZDrQR2RtBtubQZ9aw+KvdFVV7R4K/CsbgD6J5oqrXlEPk3Q==
"@docusaurus/mdx-loader@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-3.6.1.tgz#6482e6f2f32ccab4a74d8b64d7eeec4fdf9be475"
integrity sha512-KPIsYi0S3X3/rNrW3V1fgOu5t6ahYWc31zTHHod8pacFxdmk9Uf6uuw+Jd6Cly1ilgal+41Ku+s0gmMuqKqiqg==
dependencies:
"@docusaurus/logger" "3.6.0"
"@docusaurus/utils" "3.6.0"
"@docusaurus/utils-validation" "3.6.0"
"@docusaurus/logger" "3.6.1"
"@docusaurus/utils" "3.6.1"
"@docusaurus/utils-validation" "3.6.1"
"@mdx-js/mdx" "^3.0.0"
"@slorber/remark-comment" "^1.0.0"
escape-html "^1.0.3"
@ -2994,12 +2995,12 @@
react-helmet-async "*"
react-loadable "npm:@docusaurus/react-loadable@5.5.2"
"@docusaurus/module-type-aliases@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-3.6.0.tgz#44083c34a53db1dde06364b4e7f2d144fa2d5394"
integrity sha512-szTrIN/6/fuk0xkf3XbRfdTFJzRQ8d1s3sQj5++58wltrT7v3yn1149oc9ryYjMpRcbsarGloQwMu7ofPe4XPg==
"@docusaurus/module-type-aliases@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-3.6.1.tgz#2780e19411d0c8b16d46a032eae9e60e742ae681"
integrity sha512-J+q1jgm7TnEfVIUZImSFeLA1rghb6nwtoB9siHdcgKpDqFJ9/S7xhQL2aEKE7iZMZYzpu+2F390E9A7GkdEJNA==
dependencies:
"@docusaurus/types" "3.6.0"
"@docusaurus/types" "3.6.1"
"@types/history" "^4.7.11"
"@types/react" "*"
"@types/react-router-config" "*"
@ -3007,34 +3008,34 @@
react-helmet-async "*"
react-loadable "npm:@docusaurus/react-loadable@6.0.0"
"@docusaurus/plugin-client-redirects@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-3.6.0.tgz#54155826e9e5da625e296eb93e346c11c1541a78"
integrity sha512-LIqRT6dtgxdENQH8XYwOOyxfKuzFD1ayJmIDCp9Yi/rbdcPE4vvTcESLGXOKvcyWZSfyCu+JA8Tyk4qpp+2J0w==
"@docusaurus/plugin-client-redirects@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-3.6.1.tgz#a035708ff13234a30dbaa84526ab12a102e00d07"
integrity sha512-gY1LEQw4zSLSPNVxZk9mDj5ct+sJNux0SiY1PbMiI9sK8xXqkTC7QuRyrxRPelSZX9K4nyhK8PjYbVXAN+GUWQ==
dependencies:
"@docusaurus/core" "3.6.0"
"@docusaurus/logger" "3.6.0"
"@docusaurus/utils" "3.6.0"
"@docusaurus/utils-common" "3.6.0"
"@docusaurus/utils-validation" "3.6.0"
"@docusaurus/core" "3.6.1"
"@docusaurus/logger" "3.6.1"
"@docusaurus/utils" "3.6.1"
"@docusaurus/utils-common" "3.6.1"
"@docusaurus/utils-validation" "3.6.1"
eta "^2.2.0"
fs-extra "^11.1.1"
lodash "^4.17.21"
tslib "^2.6.0"
"@docusaurus/plugin-content-blog@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.6.0.tgz#9128175b4c3ce885d9090183d74c60813844ea8d"
integrity sha512-o4aT1/E0Ldpzs/hQff5uyoSriAhS/yqBhqSn+fvSw465AaqRsva6O7CZSYleuBq6x2bewyE3QJq2PcTiHhAd8g==
"@docusaurus/plugin-content-blog@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.6.1.tgz#1127d35e1a443e87f9674f955acf7864bc62bfed"
integrity sha512-FUmsn3xg/XD/K/4FQd8XHrs92aQdZO5LUtpHnRvO1/6DY87SMz6B6ERAN9IGQQld//M2/LVTHkZy8oVhQZQHIQ==
dependencies:
"@docusaurus/core" "3.6.0"
"@docusaurus/logger" "3.6.0"
"@docusaurus/mdx-loader" "3.6.0"
"@docusaurus/theme-common" "3.6.0"
"@docusaurus/types" "3.6.0"
"@docusaurus/utils" "3.6.0"
"@docusaurus/utils-common" "3.6.0"
"@docusaurus/utils-validation" "3.6.0"
"@docusaurus/core" "3.6.1"
"@docusaurus/logger" "3.6.1"
"@docusaurus/mdx-loader" "3.6.1"
"@docusaurus/theme-common" "3.6.1"
"@docusaurus/types" "3.6.1"
"@docusaurus/utils" "3.6.1"
"@docusaurus/utils-common" "3.6.1"
"@docusaurus/utils-validation" "3.6.1"
cheerio "1.0.0-rc.12"
feed "^4.2.2"
fs-extra "^11.1.1"
@ -3046,20 +3047,20 @@
utility-types "^3.10.0"
webpack "^5.88.1"
"@docusaurus/plugin-content-docs@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.6.0.tgz#15cae4bf81da0b0ddce09d53b10b7209116ea9c2"
integrity sha512-c5gZOxocJKO/Zev2MEZInli+b+VNswDGuKHE6QtFgidhAJonwjh2kwj967RvWFaMMk62HlLJLZ+IGK2XsVy4Aw==
"@docusaurus/plugin-content-docs@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.6.1.tgz#7c461b33ecc41e27fc02830bcde54378f68b2512"
integrity sha512-Uq8kyn5DYCDmkUlB9sWChhWghS4lUFNiQU+RXcAXJ3qCVXsBpPsh6RF+npQG1N+j4wAbjydM1iLLJJzp+x3eMQ==
dependencies:
"@docusaurus/core" "3.6.0"
"@docusaurus/logger" "3.6.0"
"@docusaurus/mdx-loader" "3.6.0"
"@docusaurus/module-type-aliases" "3.6.0"
"@docusaurus/theme-common" "3.6.0"
"@docusaurus/types" "3.6.0"
"@docusaurus/utils" "3.6.0"
"@docusaurus/utils-common" "3.6.0"
"@docusaurus/utils-validation" "3.6.0"
"@docusaurus/core" "3.6.1"
"@docusaurus/logger" "3.6.1"
"@docusaurus/mdx-loader" "3.6.1"
"@docusaurus/module-type-aliases" "3.6.1"
"@docusaurus/theme-common" "3.6.1"
"@docusaurus/types" "3.6.1"
"@docusaurus/utils" "3.6.1"
"@docusaurus/utils-common" "3.6.1"
"@docusaurus/utils-validation" "3.6.1"
"@types/react-router-config" "^5.0.7"
combine-promises "^1.1.0"
fs-extra "^11.1.1"
@ -3069,95 +3070,95 @@
utility-types "^3.10.0"
webpack "^5.88.1"
"@docusaurus/plugin-content-pages@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.6.0.tgz#5dd284bf063baaba1e0305c90b1dd0d5acc7e466"
integrity sha512-RKHhJrfkadHc7+tt1cP48NWifOrhkSRMPdXNYytzhoQrXlP6Ph+3tfQ4/n+nT0S3Y9+wwRxYqRqA380ZLt+QtQ==
"@docusaurus/plugin-content-pages@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.6.1.tgz#7d3dcdcc49e3c31ed13dceab830ee7fd9a1c4658"
integrity sha512-TZtL+2zq20gqGalzoIT2rEF1T4YCZ26jTvlCJXs78+incIajfdHtmdOq7rQW0oV7oqTjpGllbp788nY/vY9jgw==
dependencies:
"@docusaurus/core" "3.6.0"
"@docusaurus/mdx-loader" "3.6.0"
"@docusaurus/types" "3.6.0"
"@docusaurus/utils" "3.6.0"
"@docusaurus/utils-validation" "3.6.0"
"@docusaurus/core" "3.6.1"
"@docusaurus/mdx-loader" "3.6.1"
"@docusaurus/types" "3.6.1"
"@docusaurus/utils" "3.6.1"
"@docusaurus/utils-validation" "3.6.1"
fs-extra "^11.1.1"
tslib "^2.6.0"
webpack "^5.88.1"
"@docusaurus/plugin-debug@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-3.6.0.tgz#0a6da9ba31a0acb176ae2762b4d6b96b1906c826"
integrity sha512-o8T1Rl94COLdSlKvjYLQpRJQRU8WWZ8EX1B0yV0dQLNN8reyH7MQW+6z1ig4sQFfH3pnjPWVGHfuEjcib5m7Eg==
"@docusaurus/plugin-debug@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-3.6.1.tgz#e73fca0307b864b000c98b7110009c6b4a3efc2b"
integrity sha512-DeKPZtoVExDSYCbzoz7y5Dhc6+YPqRWfVGwEEUyKopSyQYefp0OV8hvASmbJCn2WyThRgspOUhog3FSEhz+agw==
dependencies:
"@docusaurus/core" "3.6.0"
"@docusaurus/types" "3.6.0"
"@docusaurus/utils" "3.6.0"
"@docusaurus/core" "3.6.1"
"@docusaurus/types" "3.6.1"
"@docusaurus/utils" "3.6.1"
fs-extra "^11.1.1"
react-json-view-lite "^1.2.0"
tslib "^2.6.0"
"@docusaurus/plugin-google-analytics@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.6.0.tgz#9e8245eef1bee95e44ef2af92ce3e844a8e93e64"
integrity sha512-kgRFbfpi6Hshj75YUztKyEMtI/kw0trPRwoTN4g+W1NK99R/vh8phTvhBTIMnDbetU79795LkwfG0rZ/ce6zWQ==
"@docusaurus/plugin-google-analytics@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.6.1.tgz#f20027f75cd45830eefcd7a172ded8b15de0b516"
integrity sha512-ZEoERiDHxSfhaEeT35ukQ892NzGHWiUvfxUsnPiRuGEhMoQlxMSp60shBuSZ1sUKuZlndoEl5qAXJg09Wls/Sg==
dependencies:
"@docusaurus/core" "3.6.0"
"@docusaurus/types" "3.6.0"
"@docusaurus/utils-validation" "3.6.0"
"@docusaurus/core" "3.6.1"
"@docusaurus/types" "3.6.1"
"@docusaurus/utils-validation" "3.6.1"
tslib "^2.6.0"
"@docusaurus/plugin-google-gtag@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.6.0.tgz#bed8381fe3ab357d56a565f657e38d8ea6272703"
integrity sha512-nqu4IfjaO4UX+dojHL2BxHRS+sKj31CIMWYo49huQ3wTET0Oc3u/WGTaKd3ShTPDhkgiRhTOSTPUwJWrU55nHg==
"@docusaurus/plugin-google-gtag@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.6.1.tgz#db08bfcef319c494e5969c0d96d62bd8f43469db"
integrity sha512-u/E9vXUsZxYaV6Brvfee8NiH/iR0cMml9P/ifz4EpH/Jfxdbw8rbCT0Nm/h7EFgEY48Uqkl5huSbIvFB9n8aTQ==
dependencies:
"@docusaurus/core" "3.6.0"
"@docusaurus/types" "3.6.0"
"@docusaurus/utils-validation" "3.6.0"
"@docusaurus/core" "3.6.1"
"@docusaurus/types" "3.6.1"
"@docusaurus/utils-validation" "3.6.1"
"@types/gtag.js" "^0.0.12"
tslib "^2.6.0"
"@docusaurus/plugin-google-tag-manager@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.6.0.tgz#326382de05888ea4317837be736eabd635adbc71"
integrity sha512-OU6c5xI0nOVbEc9eImGvvsgNWe4vGm97t/W3aLHjWsHyNk3uwFNBQMHRvBUwAi9k/K3kyC5E7DWnc67REhdLOw==
"@docusaurus/plugin-google-tag-manager@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.6.1.tgz#f3aa3cd0e7e6be793e5af3fe048a6ad12c3f0211"
integrity sha512-By+NKkGYV8tSo8/RyS1OXikOtqsko5jJZ/uioJfBjsBGgSbiMJ+Y/HogFBke0mgSvf7NPGKZTbYm5+FJ8YUtPQ==
dependencies:
"@docusaurus/core" "3.6.0"
"@docusaurus/types" "3.6.0"
"@docusaurus/utils-validation" "3.6.0"
"@docusaurus/core" "3.6.1"
"@docusaurus/types" "3.6.1"
"@docusaurus/utils-validation" "3.6.1"
tslib "^2.6.0"
"@docusaurus/plugin-ideal-image@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-ideal-image/-/plugin-ideal-image-3.6.0.tgz#41ffed294bd18ef7f9d4bf0d2d248a4bb890d62a"
integrity sha512-6BaZU7mX+RH+lRFPm5nj3GGfAsjsJ1JHjp0Xjb4ilfte7L2IZkTl6MPpvAzyPC0qIUxfXRcJs4qd/HUE0xcTlw==
"@docusaurus/plugin-ideal-image@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-ideal-image/-/plugin-ideal-image-3.6.1.tgz#3c0bb8e398ef0b7ea1ee313ee1e774de93f2b941"
integrity sha512-hiGRPPlsM02aEOPlQc9rVnrckbVR6HswG7yDpZOtBEhw+ysXFsl/8gzAxFBL4ogKjN28WrlMCn/6IIkxY/EyOQ==
dependencies:
"@docusaurus/core" "3.6.0"
"@docusaurus/lqip-loader" "3.6.0"
"@docusaurus/core" "3.6.1"
"@docusaurus/lqip-loader" "3.6.1"
"@docusaurus/responsive-loader" "^1.7.0"
"@docusaurus/theme-translations" "3.6.0"
"@docusaurus/types" "3.6.0"
"@docusaurus/utils-validation" "3.6.0"
"@docusaurus/theme-translations" "3.6.1"
"@docusaurus/types" "3.6.1"
"@docusaurus/utils-validation" "3.6.1"
"@slorber/react-ideal-image" "^0.0.12"
react-waypoint "^10.3.0"
sharp "^0.32.3"
tslib "^2.6.0"
webpack "^5.88.1"
"@docusaurus/plugin-pwa@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-pwa/-/plugin-pwa-3.6.0.tgz#9851d1dd52bf8bf75f7d97e9f42cb6e29cacb68d"
integrity sha512-IVUJIBeGwbIkrI8HgVkw3eonw23wq5EjPOioXzht4Dql1Bb13xF/SmO6Bj8NE2KNmlubOKRKwLy8gYpB3i8cmQ==
"@docusaurus/plugin-pwa@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-pwa/-/plugin-pwa-3.6.1.tgz#541501c814d1b5d9681f8a55224e24b121ab7c75"
integrity sha512-GsbpsgpAwdBpDpeVzk3UPNUQAc0oGU+0GL5s1LmFgqfpql5C7ZnkwD9FmW6lvieabQjC+Ga0d0zDqu20coqIRQ==
dependencies:
"@babel/core" "^7.25.9"
"@babel/preset-env" "^7.25.9"
"@docusaurus/bundler" "3.6.0"
"@docusaurus/core" "3.6.0"
"@docusaurus/logger" "3.6.0"
"@docusaurus/theme-common" "3.6.0"
"@docusaurus/theme-translations" "3.6.0"
"@docusaurus/types" "3.6.0"
"@docusaurus/utils" "3.6.0"
"@docusaurus/utils-validation" "3.6.0"
"@docusaurus/bundler" "3.6.1"
"@docusaurus/core" "3.6.1"
"@docusaurus/logger" "3.6.1"
"@docusaurus/theme-common" "3.6.1"
"@docusaurus/theme-translations" "3.6.1"
"@docusaurus/types" "3.6.1"
"@docusaurus/utils" "3.6.1"
"@docusaurus/utils-validation" "3.6.1"
babel-loader "^9.2.1"
clsx "^2.0.0"
core-js "^3.31.1"
@ -3168,39 +3169,39 @@
workbox-precaching "^7.0.0"
workbox-window "^7.0.0"
"@docusaurus/plugin-sitemap@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.6.0.tgz#c7c93f75f03391ca9071da48563fc4faa84966bc"
integrity sha512-YB5XMdf9FjLhgbHY/cDbYhVxsgcpPIjxY9769HUgFOB7GVzItTLOR71W035R1BiR2CA5QAn3XOSg36WLRxlhQQ==
"@docusaurus/plugin-sitemap@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.6.1.tgz#c842019a64d8dd12b64145115e60771d482db997"
integrity sha512-i8R/GTKew4Cufb+7YQTwfPcNOhKTJzZ1VZ5OqQwI9c3pZK2TltQyhqKDVN94KCTbSSKvOYYytYfRAB2uPnH1/A==
dependencies:
"@docusaurus/core" "3.6.0"
"@docusaurus/logger" "3.6.0"
"@docusaurus/types" "3.6.0"
"@docusaurus/utils" "3.6.0"
"@docusaurus/utils-common" "3.6.0"
"@docusaurus/utils-validation" "3.6.0"
"@docusaurus/core" "3.6.1"
"@docusaurus/logger" "3.6.1"
"@docusaurus/types" "3.6.1"
"@docusaurus/utils" "3.6.1"
"@docusaurus/utils-common" "3.6.1"
"@docusaurus/utils-validation" "3.6.1"
fs-extra "^11.1.1"
sitemap "^7.1.1"
tslib "^2.6.0"
"@docusaurus/preset-classic@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-3.6.0.tgz#71561f366a266be571022764eb8b9e5618f573eb"
integrity sha512-kpGNdQzr/Dpm7o3b1iaQrz4DMDx3WIeBbl4V4P4maa2zAQkTdlaP4CMgA5oKrRrpqPLnQFsUM/b+qf2glhl2Tw==
"@docusaurus/preset-classic@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-3.6.1.tgz#196540ca8495075d24eb724af3bf4c75d2412754"
integrity sha512-b90Y1XRH9e+oa/E3NmiFEFOwgYUd+knFcZUy81nM3FJs038WbEA0T55NQsuPW0s7nOsCShQ7dVFyKxV+Wp31Nw==
dependencies:
"@docusaurus/core" "3.6.0"
"@docusaurus/plugin-content-blog" "3.6.0"
"@docusaurus/plugin-content-docs" "3.6.0"
"@docusaurus/plugin-content-pages" "3.6.0"
"@docusaurus/plugin-debug" "3.6.0"
"@docusaurus/plugin-google-analytics" "3.6.0"
"@docusaurus/plugin-google-gtag" "3.6.0"
"@docusaurus/plugin-google-tag-manager" "3.6.0"
"@docusaurus/plugin-sitemap" "3.6.0"
"@docusaurus/theme-classic" "3.6.0"
"@docusaurus/theme-common" "3.6.0"
"@docusaurus/theme-search-algolia" "3.6.0"
"@docusaurus/types" "3.6.0"
"@docusaurus/core" "3.6.1"
"@docusaurus/plugin-content-blog" "3.6.1"
"@docusaurus/plugin-content-docs" "3.6.1"
"@docusaurus/plugin-content-pages" "3.6.1"
"@docusaurus/plugin-debug" "3.6.1"
"@docusaurus/plugin-google-analytics" "3.6.1"
"@docusaurus/plugin-google-gtag" "3.6.1"
"@docusaurus/plugin-google-tag-manager" "3.6.1"
"@docusaurus/plugin-sitemap" "3.6.1"
"@docusaurus/theme-classic" "3.6.1"
"@docusaurus/theme-common" "3.6.1"
"@docusaurus/theme-search-algolia" "3.6.1"
"@docusaurus/types" "3.6.1"
"@docusaurus/react-loadable@5.5.2":
version "5.5.2"
@ -3210,10 +3211,10 @@
"@types/react" "*"
prop-types "^15.6.2"
"@docusaurus/remark-plugin-npm2yarn@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/remark-plugin-npm2yarn/-/remark-plugin-npm2yarn-3.6.0.tgz#b0e287c88ded88de576f1e2eb74d466062d73c35"
integrity sha512-Imh0v68hFWSWlcE7qLlnZD9r4bQOzM6rcWTh6mPpfx2zHAai+so+SJmrMwZ1AJGfC08Y7Ch2Q/G4AThalXRVqQ==
"@docusaurus/remark-plugin-npm2yarn@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/remark-plugin-npm2yarn/-/remark-plugin-npm2yarn-3.6.1.tgz#9098b41101fe99214ac837954ef6365c046f909f"
integrity sha512-ve8xXfyB4oQwtYMgZoFxWWyp/aa3mobt4jLRjNHwRLQ6Jy7bMmFQ7uX99ExDttyGme2ABX4/vDNYfXDRdKm91Q==
dependencies:
mdast-util-mdx "^3.0.0"
npm-to-yarn "^3.0.0"
@ -3228,24 +3229,24 @@
dependencies:
loader-utils "^2.0.0"
"@docusaurus/theme-classic@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-3.6.0.tgz#8f34b65c85f5082deb3633a893974d2eee309121"
integrity sha512-sAXNfwPL6uRD+BuHuKXZfAXud7SS7IK/JdrPuzyQxdO1gJKzI5GFfe1ED1QoJDNWJWJ01JHE5rSnwYLEADc2rQ==
"@docusaurus/theme-classic@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-3.6.1.tgz#dff4c7732b590e231dfb764e1d9a8acb5cf28954"
integrity sha512-5lVUmIXk7zp+n9Ki2lYWrmhbd6mssOlKCnnDJvY4QDi3EgjRisIu5g4yKXoWTIbiqE7m7q/dS9cbeShEtfkKng==
dependencies:
"@docusaurus/core" "3.6.0"
"@docusaurus/logger" "3.6.0"
"@docusaurus/mdx-loader" "3.6.0"
"@docusaurus/module-type-aliases" "3.6.0"
"@docusaurus/plugin-content-blog" "3.6.0"
"@docusaurus/plugin-content-docs" "3.6.0"
"@docusaurus/plugin-content-pages" "3.6.0"
"@docusaurus/theme-common" "3.6.0"
"@docusaurus/theme-translations" "3.6.0"
"@docusaurus/types" "3.6.0"
"@docusaurus/utils" "3.6.0"
"@docusaurus/utils-common" "3.6.0"
"@docusaurus/utils-validation" "3.6.0"
"@docusaurus/core" "3.6.1"
"@docusaurus/logger" "3.6.1"
"@docusaurus/mdx-loader" "3.6.1"
"@docusaurus/module-type-aliases" "3.6.1"
"@docusaurus/plugin-content-blog" "3.6.1"
"@docusaurus/plugin-content-docs" "3.6.1"
"@docusaurus/plugin-content-pages" "3.6.1"
"@docusaurus/theme-common" "3.6.1"
"@docusaurus/theme-translations" "3.6.1"
"@docusaurus/types" "3.6.1"
"@docusaurus/utils" "3.6.1"
"@docusaurus/utils-common" "3.6.1"
"@docusaurus/utils-validation" "3.6.1"
"@mdx-js/react" "^3.0.0"
clsx "^2.0.0"
copy-text-to-clipboard "^3.2.0"
@ -3260,15 +3261,15 @@
tslib "^2.6.0"
utility-types "^3.10.0"
"@docusaurus/theme-common@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-3.6.0.tgz#9a061d278df76da0f70a9465cd0b7299c14d03d3"
integrity sha512-frjlYE5sRs+GuPs4XXlp9aMLI2O4H5FPpznDAXBrCm+8EpWRiIb443ePMxM3IyMCQ5bwFlki0PI9C+r4apstnw==
"@docusaurus/theme-common@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-3.6.1.tgz#d160516db9482ab19f7921d8a75093885d04d3de"
integrity sha512-18iEYNpMvarGfq9gVRpGowSZD24vZ39Iz4acqaj64180i54V9el8tVnhNr/wRvrUm1FY30A1NHLqnMnDz4rYEQ==
dependencies:
"@docusaurus/mdx-loader" "3.6.0"
"@docusaurus/module-type-aliases" "3.6.0"
"@docusaurus/utils" "3.6.0"
"@docusaurus/utils-common" "3.6.0"
"@docusaurus/mdx-loader" "3.6.1"
"@docusaurus/module-type-aliases" "3.6.1"
"@docusaurus/utils" "3.6.1"
"@docusaurus/utils-common" "3.6.1"
"@types/history" "^4.7.11"
"@types/react" "*"
"@types/react-router-config" "*"
@ -3278,34 +3279,34 @@
tslib "^2.6.0"
utility-types "^3.10.0"
"@docusaurus/theme-live-codeblock@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/theme-live-codeblock/-/theme-live-codeblock-3.6.0.tgz#de670b60c9acfdf03feb79e32a3be0d90fe11647"
integrity sha512-EphCTxJZgnEvgA1kdbr+R/cBu3fdawA2ooQji8EKPKg9i4Uex0xfFGhWja/0SaMKGqmfHRz9SY8I7j75O3cluw==
"@docusaurus/theme-live-codeblock@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/theme-live-codeblock/-/theme-live-codeblock-3.6.1.tgz#748f2864df9ad2cdbb4f370b816ad1dc2d2bc175"
integrity sha512-8GdnkwaNxb0dmRHGDlO+5QXdz9mrLni/wqai06LSGOTRv+GY3JC2Zov/KKkHZxx5MNFagqq01sGdn1TBBrS9jg==
dependencies:
"@docusaurus/core" "3.6.0"
"@docusaurus/theme-common" "3.6.0"
"@docusaurus/theme-translations" "3.6.0"
"@docusaurus/utils-validation" "3.6.0"
"@docusaurus/core" "3.6.1"
"@docusaurus/theme-common" "3.6.1"
"@docusaurus/theme-translations" "3.6.1"
"@docusaurus/utils-validation" "3.6.1"
"@philpl/buble" "^0.19.7"
clsx "^2.0.0"
fs-extra "^11.1.1"
react-live "^4.1.6"
tslib "^2.6.0"
"@docusaurus/theme-search-algolia@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.6.0.tgz#47dcfca68f50163abce411dd9b181855a9ec9c83"
integrity sha512-4IwRUkxjrisR8LXBHeE4d2btraWdMficbgiVL3UHvJURmyvgzMBZQP8KrK8rjdXeu8SuRxSmeV6NSVomRvdbEg==
"@docusaurus/theme-search-algolia@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.6.1.tgz#a9cc9c9517a22459354703cf33d469e7263d3854"
integrity sha512-BjmuiFRpQP1WEm8Mzu1Bb0Wdas6G65VHXDDNr7XTKgbstxalE6vuxt0ioXTDFS2YVep5748aVhKvnxR9gm2Liw==
dependencies:
"@docsearch/react" "^3.5.2"
"@docusaurus/core" "3.6.0"
"@docusaurus/logger" "3.6.0"
"@docusaurus/plugin-content-docs" "3.6.0"
"@docusaurus/theme-common" "3.6.0"
"@docusaurus/theme-translations" "3.6.0"
"@docusaurus/utils" "3.6.0"
"@docusaurus/utils-validation" "3.6.0"
"@docusaurus/core" "3.6.1"
"@docusaurus/logger" "3.6.1"
"@docusaurus/plugin-content-docs" "3.6.1"
"@docusaurus/theme-common" "3.6.1"
"@docusaurus/theme-translations" "3.6.1"
"@docusaurus/utils" "3.6.1"
"@docusaurus/utils-validation" "3.6.1"
algoliasearch "^4.18.0"
algoliasearch-helper "^3.13.3"
clsx "^2.0.0"
@ -3315,10 +3316,10 @@
tslib "^2.6.0"
utility-types "^3.10.0"
"@docusaurus/theme-translations@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-3.6.0.tgz#93994e931f340c1712c81ac80dbab5750c24634f"
integrity sha512-L555X8lWE3fv8VaF0Bc1VnAgi10UvRKFcvADHiYR7Gj37ItaWP5i7xLHsSw7fi/SHTXe5wfIeCFNqUYHyCOHAQ==
"@docusaurus/theme-translations@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-3.6.1.tgz#d6bbb20141ca70f352201f2f412b6b8d988d86b4"
integrity sha512-bNm5G6sueUezvyhsBegA1wwM38yW0BnqpZTE9KHO2yKnkERNMaV5x/yPJ/DNCOHjJtCcJ5Uz55g2AS75Go31xA==
dependencies:
fs-extra "^11.1.1"
tslib "^2.6.0"
@ -3342,10 +3343,10 @@
webpack "^5.88.1"
webpack-merge "^5.9.0"
"@docusaurus/types@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-3.6.0.tgz#8fa82332a7c7b8093b5c55e1115f5854ce484978"
integrity sha512-jADLgoZGWhAzThr+mRiyuFD4OUzt6jHnb7NRArRKorgxckqUBaPyFOau9hhbcSTHtU6ceyeWjN7FDt7uG2Hplw==
"@docusaurus/types@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-3.6.1.tgz#0e55a0a51a3e55658b0845af83d5fe17c495978e"
integrity sha512-hCB1hj9DYutVYBisnPNobz9SzEmCcf1EetJv09O49Cov3BqOkm+vnnjB3d957YJMtpLGQoKBeN/FF1DZ830JwQ==
dependencies:
"@mdx-js/mdx" "^3.0.0"
"@types/history" "^4.7.11"
@ -3357,34 +3358,36 @@
webpack "^5.95.0"
webpack-merge "^5.9.0"
"@docusaurus/utils-common@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-3.6.0.tgz#11855ea503132bbcaba6ca4d351293ff10a75d34"
integrity sha512-diUDNfbw33GaZMmKwdTckT2IBfVouXLXRD+zphH9ywswuaEIKqixvuf5g41H7MBBrlMsxhna3uTMoB4B/OPDcA==
"@docusaurus/utils-common@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-3.6.1.tgz#994160470e6bd2c0eb771f2132883d21b3b6830f"
integrity sha512-LX1qiTiC0aS8c92uZ+Wj2iNCNJyYZJIKY8/nZDKNMBfo759VYVS3RX3fKP3DznB+16sYp7++MyCz/T6fOGaRfw==
dependencies:
"@docusaurus/types" "3.6.1"
tslib "^2.6.0"
"@docusaurus/utils-validation@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-3.6.0.tgz#5557ca14fa64ac29e6f70e61006be721395ecde5"
integrity sha512-CRHiKKJEKA0GFlfOf71JWHl7PtwOyX0+Zg9ep9NFEZv6Lcx3RJ9nhl7p8HRjPL6deyYceavM//BsfW4pCI4BtA==
"@docusaurus/utils-validation@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-3.6.1.tgz#8e4b5bd8b71f55228543e3fda1301e9fb83df1c6"
integrity sha512-+iMd6zRl5cJQm7nUP+7pSO/oAXsN79eHO34ME7l2YJt4GEAr70l5kkD58u2jEPpp+wSXT70c7x2A2lzJI1E8jw==
dependencies:
"@docusaurus/logger" "3.6.0"
"@docusaurus/utils" "3.6.0"
"@docusaurus/utils-common" "3.6.0"
"@docusaurus/logger" "3.6.1"
"@docusaurus/utils" "3.6.1"
"@docusaurus/utils-common" "3.6.1"
fs-extra "^11.2.0"
joi "^17.9.2"
js-yaml "^4.1.0"
lodash "^4.17.21"
tslib "^2.6.0"
"@docusaurus/utils@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-3.6.0.tgz#192785da6fd62dfd83d6f1879c3aa45547f5df23"
integrity sha512-VKczAutI4mptiAw/WcYEu5WeVhQ6Q1zdIUl64SGw9K++9lziH+Kt10Ee8l2dMpRkiUk6zzK20kMNlX2WCUwXYQ==
"@docusaurus/utils@3.6.1":
version "3.6.1"
resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-3.6.1.tgz#4e4f80be294671bfb83910352d3625878134bf48"
integrity sha512-nS3WCvepwrnBEgSG5vQu40XG95lC9Jeh/odV5u5IhU1eQFEGDst9xBi6IK5yZdsGvbuaXBZLZtOqWYtuuFa/rQ==
dependencies:
"@docusaurus/logger" "3.6.0"
"@docusaurus/utils-common" "3.6.0"
"@docusaurus/logger" "3.6.1"
"@docusaurus/types" "3.6.1"
"@docusaurus/utils-common" "3.6.1"
"@svgr/webpack" "^8.1.0"
escape-string-regexp "^4.0.0"
file-loader "^6.2.0"
@ -4025,6 +4028,28 @@
worker-loader "3.0.8"
xmlbuilder2 "3.0.2"
"@kitware/vtk.js@32.1.1":
version "32.1.1"
resolved "https://registry.yarnpkg.com/@kitware/vtk.js/-/vtk.js-32.1.1.tgz#20179cb5f65f33dea4626a948afe24eb16364f8a"
integrity sha512-rrh+PvmjpMDCOsgr7B8ZVxnF75TXjUJOVleMO5QJFh7DP+31927l9DJA4WRiJU7mbpYuZGt8/vAojO5fCizUkA==
dependencies:
"@babel/runtime" "7.22.11"
"@types/webxr" "^0.5.5"
commander "9.2.0"
d3-scale "4.0.2"
fast-deep-equal "^3.1.3"
fflate "0.7.3"
gl-matrix "3.4.3"
globalthis "1.0.3"
seedrandom "3.0.5"
shader-loader "1.3.1"
shelljs "0.8.5"
spark-md5 "3.0.2"
stream-browserify "3.0.0"
webworker-promise "0.5.0"
worker-loader "3.0.8"
xmlbuilder2 "3.0.2"
"@leichtgewicht/ip-codec@^2.0.1":
version "2.0.5"
resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz#4fc56c15c580b9adb7dc3c333a134e540b44bfb1"