Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
f2926c5
Add viv loader
igoroctaviano Apr 7, 2026
de332d5
Improve viewport displaying
igoroctaviano Apr 7, 2026
f7ebea7
Fixing 8-bit: Rendering images correctly (wrong position)
igoroctaviano Apr 7, 2026
42dbd68
Fixing 8-bit: Rendering images correctly (correct position)
igoroctaviano Apr 7, 2026
b2dad70
Add request abort
igoroctaviano Apr 7, 2026
29d36d1
Working: Bulk ann (polygons)
igoroctaviano Apr 8, 2026
457f48e
Working: Bulk ann (polygons)
igoroctaviano Apr 8, 2026
bd5bd55
Linting
igoroctaviano Apr 8, 2026
3fd745d
Linting and padding
igoroctaviano Apr 8, 2026
4fd23eb
Linting and padding and styling
igoroctaviano Apr 8, 2026
c03494a
Merge branch 'master' of github.com:ImagingDataCommons/slim into feat…
igoroctaviano Apr 14, 2026
d4decc8
Fix icc profile for viv mode
igoroctaviano Apr 14, 2026
5f0ea90
Merge branch 'master' into feat/viv-loader
igoroctaviano Apr 14, 2026
c231a79
Merge branch 'master' of github.com:ImagingDataCommons/slim into feat…
igoroctaviano Apr 17, 2026
3277448
Merge branch 'feat/viv-loader' of github.com:ImagingDataCommons/slim …
igoroctaviano Apr 17, 2026
d136f1d
Merge branch 'master' of github.com:ImagingDataCommons/slim into feat…
igoroctaviano May 7, 2026
13bdd9a
Improve performance
igoroctaviano May 7, 2026
a39ec69
Improve performance
igoroctaviano May 7, 2026
d76573d
Add logging and chunk annotations processing
igoroctaviano May 19, 2026
a3147c4
Load images from center outwards
igoroctaviano May 19, 2026
74973e5
Load annotations from center
igoroctaviano May 19, 2026
93987de
Use points to improve memory usage
igoroctaviano May 19, 2026
0614d09
Scale points
igoroctaviano May 19, 2026
6b131ba
Hardening
igoroctaviano May 20, 2026
a312e9e
Add loading indicator for bulk annotation retrieve and processing.
igoroctaviano May 20, 2026
8e803a1
Add tooltip
igoroctaviano May 20, 2026
5def528
Improving image load and caching
igoroctaviano May 20, 2026
7e86241
Update comments
igoroctaviano May 20, 2026
772dbd5
Clean up ci
igoroctaviano May 20, 2026
0dcd397
Add tooltip back
igoroctaviano May 20, 2026
5e21695
Update point size
igoroctaviano May 20, 2026
44a3699
Load ann during retrieval
igoroctaviano Jun 4, 2026
44eef6f
Fix final rendering
igoroctaviano Jun 4, 2026
148a143
Merge branch 'master' into feat/viv-loader
igoroctaviano Jun 5, 2026
61f353c
Merge branch 'master' of github.com:ImagingDataCommons/slim into feat…
igoroctaviano Jul 2, 2026
c86d8d2
Merge branch 'master' of githubt push origin feat/viv-`:w
igoroctaviano Jul 2, 2026
5698f5d
Update lock
igoroctaviano Jul 2, 2026
3552687
Merge branch 'master' into feat/viv-loader
igoroctaviano Jul 6, 2026
d7c322f
Merge branch 'master' into feat/viv-loader
igoroctaviano Jul 9, 2026
e0afffc
Address stream / load and pan
igoroctaviano Jul 13, 2026
983240d
Adjust stroke
igoroctaviano Jul 13, 2026
1751074
Adjust stroke
igoroctaviano Jul 13, 2026
498139d
Merge branch 'master' of github.com:ImagingDataCommons/slim into feat…
igoroctaviano Jul 13, 2026
bc00df8
Address errors
igoroctaviano Jul 13, 2026
08c16c1
Merge branch 'master' of github.com:ImagingDataCommons/slim into feat…
igoroctaviano Jul 13, 2026
e1abe00
Address errors
igoroctaviano Jul 13, 2026
f79338d
Update point vs polygon defaults
igoroctaviano Jul 14, 2026
818febd
fix: address code-review findings for viv loader path
igoroctaviano Jul 31, 2026
9fe26aa
fix: align XHR retry defaults with master polish
igoroctaviano Jul 31, 2026
ca226ac
fix: prevent stale hydrate settle from wiping a re-dispatch
igoroctaviano Jul 31, 2026
1f333be
fix: tighten bulk-stream edge cases from pipeline review
igoroctaviano Jul 31, 2026
baf4f5e
docs: prefer JSDoc block comments for explanations
igoroctaviano Jul 31, 2026
5fb22d5
docs: clarify responseType capture comment in retry hook
igoroctaviano Jul 31, 2026
19a8723
fix(viv): skip useless bulk Range retries and duplicate layer fallback
igoroctaviano Jul 31, 2026
1c9b654
fix(viv): drop hydrate ownership on catalog invalidation
igoroctaviano Jul 31, 2026
8c70b89
Merge branch 'master' into feat/viv-loader
igoroctaviano Aug 6, 2026
76c7cbd
ci: retrigger checks after master merge
igoroctaviano Aug 6, 2026
435b7e7
fix: silence react-hooks exhaustive-deps CI failures in viv bulk hook
igoroctaviano Aug 6, 2026
bfb6df9
Merge remote-tracking branch 'origin/master' into feat/viv-loader
igoroctaviano Aug 11, 2026
7c8f819
Merge branch 'master' into feat/viv-loader
igoroctaviano Aug 13, 2026
2c3b6f5
Merge branch 'master' into feat/viv-loader
igoroctaviano Aug 13, 2026
311a08b
Merge branch 'master' into feat/viv-loader
igoroctaviano Aug 14, 2026
5c8920a
Merge branch 'master' into feat/viv-loader
igoroctaviano Aug 14, 2026
33cd2c5
Merge branch 'master' into feat/viv-loader
igoroctaviano Aug 14, 2026
90a43e5
Merge master into feat/viv-loader
igoroctaviano Aug 14, 2026
41e7e83
Merge branch 'master' into feat/viv-loader
igoroctaviano Aug 14, 2026
3fbc195
fix: resolve DeepSource code quality issues
igoroctaviano Aug 14, 2026
65c853c
fix(viv): address DeepSource code quality issues
igoroctaviano Aug 14, 2026
5279008
fix(viv): address remaining DeepSource code quality issues
igoroctaviano Aug 14, 2026
f4ea80d
fix(viv): address SonarQube and DeepSource quality issues
igoroctaviano Aug 14, 2026
b2c9e34
fix(viv): add eslint-disable for intentional useMemo dependencies
igoroctaviano Aug 14, 2026
48c5e57
fix(viv): place eslint-disable before dependency array
igoroctaviano Aug 14, 2026
8a5302f
chore(ci): remove e2e-visual workflow from viv-loader branch
igoroctaviano Aug 14, 2026
8767da7
fix(viv): address DeepSource code quality issues and remove e2e workflow
igoroctaviano Aug 14, 2026
e6f27d9
fix(viv): address additional DeepSource code quality issues
igoroctaviano Aug 14, 2026
0bf252a
fix(viv): suppress remaining DeepSource issues
igoroctaviano Aug 14, 2026
7d11f80
fix(viv): address all remaining DeepSource issues
igoroctaviano Aug 14, 2026
a94b807
fix(viv): fix DeepSource issues in VivSlideViewport
igoroctaviano Aug 14, 2026
94ca784
Merge branch 'master' into feat/viv-loader
igoroctaviano Aug 14, 2026
d0f9fef
fix(deps): add missing dicomweb-client@0.10.3 entry in lockfile
igoroctaviano Aug 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions craco.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,19 @@ module.exports = {
asyncWebAssembly: true
}

/* Viv / Deck.gl / Luma ship modern JS; transpile for CRA 5 / webpack 5. */
config.module.rules.push({
test: /\.m?js$/,
include: /node_modules[\\/](@deck\.gl|@luma\.gl|@math\.gl|@probe\.gl|@vivjs)[\\/]/,
use: {
loader: require.resolve('babel-loader'),
options: {
presets: [require.resolve('babel-preset-react-app/dependencies')],
cacheDirectory: true
}
}
})

if (linkedDmv) {
config.watchOptions = {
...config.watchOptions,
Expand Down
14 changes: 14 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,20 @@
]
},
"dependencies": {
"@deck.gl/core": "9.1.15",
"@deck.gl/extensions": "9.1.15",
"@deck.gl/geo-layers": "9.1.15",
"@deck.gl/layers": "9.1.15",
"@deck.gl/mesh-layers": "9.1.15",
"@deck.gl/react": "9.1.15",
"@deck.gl/widgets": "9.1.15",
"@vivjs/layers": "^0.20.0",
"@vivjs/loaders": "^0.20.0",
"@luma.gl/constants": "~9.1.10",
"@luma.gl/core": "~9.1.10",
"@luma.gl/engine": "~9.1.10",
"@luma.gl/shadertools": "~9.1.10",
"@luma.gl/webgl": "~9.1.10",
"antd": "^4.22.8",
"classnames": "^2.2.6",
"dcmjs": "^0.35.0",
Expand Down
1,119 changes: 1,119 additions & 0 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

158 changes: 103 additions & 55 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
Navigate,
Route,
Routes,
useLocation,
useParams,
} from 'react-router-dom'

Expand Down Expand Up @@ -47,6 +48,8 @@
config: AppConfig
}): JSX.Element {
const { studyInstanceUID } = useParams()
const location = useLocation()
const isVivRoute = location.pathname.startsWith('/viv/')

if (studyInstanceUID === undefined) {
return <Navigate to="/" replace />
Expand All @@ -64,6 +67,8 @@
app={app}
enableAnnotationTools={enableAnnotationTools}
studyInstanceUID={studyInstanceUID}
isVivRoute={isVivRoute}
vivSettings={config.vivSettings}
/>
</ValidationProvider>
)
Expand Down Expand Up @@ -464,6 +469,51 @@
this.signIn()
}

/** Shared layout for study viewer routes (classic and `/viv/...`). */
private renderCaseViewerShell(

Check warning on line 473 in src/App.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Method 'renderCaseViewerShell' has too many parameters (8). Maximum allowed is 7.

See more on https://sonarcloud.io/project/issues?id=ImagingDataCommons_slim&issues=AZ8j04DXQNFxXeYuiD2q&open=AZ8j04DXQNFxXeYuiD2q&pullRequest=381
appInfo: {
name: string
version: string
homepage: string
uid: string
organization?: string
},
enableWorklist: boolean,
enableServerSelection: boolean,
enableMemoryMonitoring: boolean,
isLogoutPossible: boolean,
onLogout: () => void,
layoutStyle: React.CSSProperties,
layoutContentStyle: React.CSSProperties,
): JSX.Element {
return (
<SettingsProvider>
<AppShell enableMemoryMonitoring={enableMemoryMonitoring}>
<Layout style={layoutStyle}>
<Header
app={appInfo}
user={this.state.user}
showWorklistButton={enableWorklist}
onServerSelection={this.handleServerSelection}
onUserLogout={isLogoutPossible ? onLogout : undefined}
showServerSelectionButton={enableServerSelection}
clients={this.state.clients}
defaultClients={this.state.defaultClients}
/>
<Layout.Content style={layoutContentStyle}>
<ParametrizedCaseViewer
clients={this.state.clients}
user={this.state.user}
config={this.props.config}
app={appInfo}
/>
</Layout.Content>
</Layout>
</AppShell>
</SettingsProvider>
)
}

render(): React.ReactNode {
const appInfo = {
name: this.props.name,
Expand Down Expand Up @@ -521,16 +571,20 @@
display: 'flex',
flexDirection: 'column',
}
const routerFuture = {
v7_startTransition: true,
v7_relativeSplatPath: true,
} as const

if (this.state.redirectTo !== undefined) {
return (
<BrowserRouter basename={this.props.config.path}>
<BrowserRouter basename={this.props.config.path} future={routerFuture}>
<Navigate to={this.state.redirectTo} replace />
</BrowserRouter>
)
} else if (this.state.isLoading) {
return (
<BrowserRouter basename={this.props.config.path}>
<BrowserRouter basename={this.props.config.path} future={routerFuture}>
<AppShell enableMemoryMonitoring={false}>
<Layout style={layoutStyle}>
<Header
Expand Down Expand Up @@ -561,7 +615,7 @@
return <InfoPage type="error" message={this.state.error.message} />
} else {
return (
<BrowserRouter basename={this.props.config.path}>
<BrowserRouter basename={this.props.config.path} future={routerFuture}>
<Routes>
<Route
path="/"
Expand All @@ -587,61 +641,55 @@
/>
<Route
path="/studies/:studyInstanceUID/*"
element={
<SettingsProvider>
<AppShell enableMemoryMonitoring={enableMemoryMonitoring}>
<Layout style={layoutStyle}>
<Header
app={appInfo}
user={this.state.user}
showWorklistButton={enableWorklist}
onServerSelection={this.handleServerSelection}
onUserLogout={isLogoutPossible ? onLogout : undefined}
showServerSelectionButton={enableServerSelection}
clients={this.state.clients}
defaultClients={this.state.defaultClients}
/>
<Layout.Content style={layoutContentStyle}>
<ParametrizedCaseViewer
clients={this.state.clients}
user={this.state.user}
config={this.props.config}
app={appInfo}
/>
</Layout.Content>
</Layout>
</AppShell>
</SettingsProvider>
}
element={this.renderCaseViewerShell(
appInfo,
enableWorklist,
enableServerSelection,
enableMemoryMonitoring,
isLogoutPossible,
onLogout,
layoutStyle,
layoutContentStyle,
)}
/>
<Route
path="/projects/:project/locations/:location/datasets/:dataset/dicomStores/:dicomStore/study/:studyInstanceUID/*"
element={
<SettingsProvider>
<AppShell enableMemoryMonitoring={enableMemoryMonitoring}>
<Layout style={layoutStyle}>
<Header
app={appInfo}
user={this.state.user}
showWorklistButton={enableWorklist}
onServerSelection={this.handleServerSelection}
onUserLogout={isLogoutPossible ? onLogout : undefined}
showServerSelectionButton={enableServerSelection}
clients={this.state.clients}
defaultClients={this.state.defaultClients}
/>
<Layout.Content style={layoutContentStyle}>
<ParametrizedCaseViewer
clients={this.state.clients}
user={this.state.user}
config={this.props.config}
app={appInfo}
/>
</Layout.Content>
</Layout>
</AppShell>
</SettingsProvider>
}
element={this.renderCaseViewerShell(
appInfo,
enableWorklist,
enableServerSelection,
enableMemoryMonitoring,
isLogoutPossible,
onLogout,
layoutStyle,
layoutContentStyle,
)}
/>
<Route
path="/viv/studies/:studyInstanceUID/*"
element={this.renderCaseViewerShell(
appInfo,
enableWorklist,
enableServerSelection,
enableMemoryMonitoring,
isLogoutPossible,
onLogout,
layoutStyle,
layoutContentStyle,
)}
/>
<Route
path="/viv/projects/:project/locations/:location/datasets/:dataset/dicomStores/:dicomStore/study/:studyInstanceUID/*"
element={this.renderCaseViewerShell(
appInfo,
enableWorklist,
enableServerSelection,
enableMemoryMonitoring,
isLogoutPossible,
onLogout,
layoutStyle,
layoutContentStyle,
)}
/>
<Route
path="/logout"
Expand Down
19 changes: 19 additions & 0 deletions src/AppConfig.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,23 @@ export interface OidcSettings {
endSessionEndpoint?: string
}

export interface VivChannelSelection {
c: number
t?: number
z?: number
}

export interface VivSettings {
selections?: VivChannelSelection[]
channelsVisible?: boolean[]
contrastLimits?: Array<[number, number]>
colors?: Array<[number, number, number]>
initialViewState?: {
target: [number, number, number]
zoom?: number
}
}

export default interface AppConfig {
/**
* Currently, only one server is supported. However, support for multiple
Expand Down Expand Up @@ -109,4 +126,6 @@ export default interface AppConfig {
enableInDevelopment?: boolean
}
enableMemoryMonitoring?: boolean
/** Optional display overrides for the Viv slide viewer at `/viv/...` routes (channels, contrast, etc.). */
vivSettings?: VivSettings
}
14 changes: 14 additions & 0 deletions src/DicomWebManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,20 @@ export default class DicomWebManager implements dwc.api.DICOMwebClient {
return this.stores[0].client.headers
}

/**
* Run a callback on the primary {@link dwc.api.DICOMwebClient} (the store used for reads).
* Used e.g. to append Viv tile {@link dwc.api.DICOMwebClientOptions.requestHooks} that must
* see the same XHR pipeline as {@link dmv.viewer.VolumeImageViewer}.
*/
applyToPrimaryDicomwebClient(
fn: (client: dwc.api.DICOMwebClient) => void,
): void {
if (this.stores.length === 0) {
return
}
fn(this.stores[0].client)
}

/**
* Store new instances in the first writable configured store. Picking the
* first writable (rather than always store[0]) keeps backwards compatibility
Expand Down
Loading