Skip to content

Conversation

@igoroctaviano
Copy link
Collaborator

@igoroctaviano igoroctaviano commented Dec 18, 2025

Clustering Configuration and Memory Monitoring

Summary

This PR introduces several enhancements to annotation clustering and processing, plus adds comprehensive memory monitoring capabilities:

  1. Clustering Pixel Size Threshold - Configurable threshold for enabling/disabling clustering based on pixel size
  2. UI Toggle for Clustering - Easy-to-use switch to enable/disable clustering with conditional UI display
  3. Memory Monitoring - Real-time browser memory usage tracking with automatic warnings

Changes

1. Clustering Pixel Size Threshold (Initial Feature)

  • Added clusteringPixelSizeThreshold to component state

    • Type: number
    • Default value: 0.001 mm (1 micrometer)
    • Added to SlideViewerState interface
  • Added threshold input UI

    • InputNumber component in Annotation Groups menu
    • Allows users to set pixel size threshold in millimeters
    • Supports values from 0 to 100 mm with 0.001 precision
    • Placeholder text: "Auto (zoom-based)"
    • Help text explains threshold behavior
  • Updated viewer construction

    • Passes clusteringPixelSizeThreshold to viewer via annotationOptions
    • Integrated with constructViewers utility function
  • Added handleClusteringPixelSizeThresholdChange handler

    • Updates state when threshold value changes
    • Updates viewer annotation options when clustering is enabled

2. UI Toggle for Clustering (Latest Feature)

  • Added clustering toggle switch in the Annotation Groups menu

    • Located above the clustering pixel size threshold input
    • Uses Ant Design Switch component
    • Enabled by default (isClusteringEnabled: true)
  • Conditional display of threshold input

    • The "Clustering Pixel Size Threshold" input is only shown when clustering is enabled
    • When clustering is disabled, the threshold UI is hidden
  • State Management

    • Added isClusteringEnabled to component state (type: boolean, default: true)
    • Updated shouldComponentUpdate to include clustering state changes
    • Ensures component re-renders when clustering state changes
  • Viewer Integration

    • When clustering is disabled, clusteringPixelSizeThreshold is passed as undefined to the viewer
    • When enabled, uses the current threshold value from state
    • Added handleClusteringToggle handler with error handling

3. Memory Monitoring

  • Created MemoryMonitor service (src/services/MemoryMonitor.ts)

    • Singleton service for application-wide memory monitoring
    • Supports modern API (performance.measureUserAgentSpecificMemory()) with cross-origin isolation
    • Falls back to Chrome-specific API (performance.memory) when modern API unavailable
    • Automatic API detection and graceful degradation
    • Subscription-based updates for components
    • Configurable monitoring interval (default: 5 seconds)
    • Warning thresholds: 80% (high) and 90% (critical)
  • Created MemoryFooter component (src/components/MemoryFooter.tsx)

    • Displays real-time memory usage in footer
    • Shows used memory, heap limit, usage percentage, and remaining memory
    • Color-coded status indicators (green/orange/red)
    • Automatically issues warnings via NotificationMiddleware when memory is high or critical
    • Hides gracefully when monitoring unavailable
  • Added type declarations (src/types/performance.d.ts)

    • Extended Performance interface for experimental APIs
    • Extended Window interface for cross-origin isolation flag
    • Proper TypeScript support for modern memory APIs
  • Integrated into App layout

    • Added MemoryFooter component to all routes (worklist, study viewer, projects, logout)
    • Footer appears at bottom of all pages
    • Memory monitoring starts automatically when app loads
  • Documentation

    • Created docs/MEMORY_MONITORING.md with comprehensive technical documentation
    • Updated README.md with memory monitoring feature description
    • Added to table of contents

4. Style Updates

  • Updated styling for clustering settings
    • Improved layout and spacing in clustering menu items

Code Quality

  • All new comments use JSDoc format (/** */)
  • Fixed linting issues (explicit null/undefined checks)
  • Follows existing code patterns and conventions
  • Removed unnecessary comments per .cursorrules guidelines
  • No emojis in code or messages
  • Proper TypeScript typing throughout

User Experience

  • Users can configure clustering pixel size threshold for fine-grained control
  • Easy toggle to enable/disable clustering without losing threshold configuration
  • When clustering is disabled, threshold input is hidden to reduce UI clutter
  • Clustering is enabled by default to maintain existing behavior
  • Toggle state is preserved and works correctly even when toggled before annotations are loaded
  • Memory monitoring provides visibility into browser memory usage
  • Automatic warnings help prevent memory-related crashes
  • Footer display is unobtrusive and informative

Testing

  • Threshold can be set and updated successfully

  • Toggle can be switched on/off successfully

  • Threshold input appears/disappears based on toggle state

  • Viewer correctly applies clustering settings when toggled

  • No unnecessary annotation loading when toggling clustering

  • Works correctly when toggled before annotations are loaded

  • Memory monitoring displays correctly in footer

  • Memory warnings appear at appropriate thresholds

  • Monitoring works with both modern and fallback APIs

  • Footer hides gracefully when APIs unavailable

  • Bulk annotations always cluster #330 (comment)

  • DMV PR: Add clusteringPixelSizeThreshold option dicom-microscopy-viewer#222

Screenshot 2025-12-18 at 19 49 31

Other issues

@deepsource-io
Copy link

deepsource-io bot commented Dec 18, 2025

Here's the code health analysis summary for commits fea13f6..d460e27. View details on DeepSource ↗.

Analysis Summary

AnalyzerStatusSummaryLink
DeepSource JavaScript LogoJavaScript❌ Failure
❗ 33 occurences introduced
🎯 208 occurences resolved
View Check ↗

💡 If you’re a repository administrator, you can configure the quality gates from the settings.

@igoroctaviano
Copy link
Collaborator Author

@fedorov I have this solution for items 1 and 2 of #330 (comment). Please let me know if its better now.

@fedorov
Copy link
Member

fedorov commented Jan 4, 2026

Now the image doesn't load at all. I deployed using the feat/cluster-threshold for both slim and dmv. Tested using the same URL as in #330 https://andrey-slim-test.web.app/studies/2.25.302737996345872783571112300080988167697/series/1.3.6.1.4.1.5962.99.1.1250863857.1162905243.1637633436401.2.0.

image

@fedorov
Copy link
Member

fedorov commented Jan 5, 2026

Then we broke the production with another regression... It was working as of Nov 21, 2025, as documented in the screenshot in #330 (comment).

@igoroctaviano
Copy link
Collaborator Author

Then we broke the production with another regression... It was working as of Nov 21, 2025, as documented in the screenshot in #330 (comment).

  "https://proxy.imaging.datacommons.cancer.gov/current/viewer-only-no-downloads-see-tinyurl-dot-com-slash-3j3d9jyp/dicomWeb/studies/2.25.106918873973188798943205935727506273925/series/1.3.6.1.4.1.5962.99.1.1088146757.1503397867.1637470719301.2.0/instances/1.3.6.1.4.1.5962.99.1.1088146757.1503397867.1637470719301.22.0/frames/39" \
  -H 'Accept: multipart/related; type="image/jls"; transfer-syntax=1.2.840.10008.1.2.4.80, multipart/related; type="image/jls"; transfer-syntax=1.2.840.10008.1.2.4.81, multipart/related; type="image/jp2"; transfer-syntax=1.2.840.10008.1.2.4.90, multipart/related; type="image/jp2"; transfer-syntax=1.2.840.10008.1.2.4.91, multipart/related; type="image/jpx"; transfer-syntax=1.2.840.10008.1.2.4.92, multipart/related; type="image/jpx"; transfer-syntax=1.2.840.10008.1.2.4.93, multipart/related; type="application/octet-stream"; transfer-syntax=*, */*;q=0.1'

If you try this curl it returns:

* Request completely sent off
< HTTP/2 200
< content-type: multipart/related; boundary=19b0c81d44214ba1e34ef05ad1c6cec5a344b04b1e085b59b4499caddbf0; transfer-syntax=1.2.840.10008.1.2.4.80; type="image/jls"
< vary: Origin
< vary: X-Origin
< vary: Referer
< x-xss-protection: 0
< x-frame-options: SAMEORIGIN
< x-content-type-options: nosniff
< alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
< alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
< strict-transport-security: max-age=31536001; includeSubDomains; preload
< x-cloud-trace-context: 0fe719e72d005ec594972f978f92aaa3;o=1
< date: Mon, 12 Jan 2026 17:14:29 GMT
< server: Google Frontend
< content-length: 378
< via: 1.1 google
<
--19b0c81d44214ba1e34ef05ad1c6cec5a344b04b1e085b59b4499caddbf0
Content-Type: image/jls; transfer-syntax=1.2.840.10008.1.2.4.80

[{
  "error": {
    "code": 400,
    "message": "transcoding frame: generic::invalid_argument: Component 1 out of 3 components does not have the expected width. Component Width: 128, Expected Columns: 256",
    "status": "INVALID_ARGUMENT"
  }
}
* Connection #0 to host proxy.imaging.datacommons.cancer.gov left intact

No problems using dcm4chee... Let me know if you get the same results!

@fedorov
Copy link
Member

fedorov commented Jan 16, 2026

@igoroctaviano I deployed with the feat/cluster-threshold branch both for slim and dmv, and now it is failing with the following error.

https://andrey-slim-test.web.app/studies/2.25.302737996345872783571112300080988167697/series/1.3.6.1.4.1.5962.99.1.1250863857.1162905243.1637633436401.2.0

image

@igoroctaviano igoroctaviano changed the title Add clusteringPixelSizeThreshold Add clusteringPixelSizeThreshold + memory monitoring Jan 20, 2026
@fedorov
Copy link
Member

fedorov commented Jan 23, 2026

Igor, the update of accept headers seems to work great!

But I don't see the memory monitoring footer you mentioned.

@fedorov
Copy link
Member

fedorov commented Jan 23, 2026

As clarified by @igoroctaviano, this requires new config flag: enableMemoryMonitoring: true (it is interesting that it was not flagged by the agent in the PR notes - or I missed it).

Looks nice and helpful!

image

@igoroctaviano let's get those merged!

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds configurable annotation clustering controls and introduces an optional, app-wide memory monitoring footer (plus a small enhancement to the DICOM Tag Browser to better align the selected series with the active viewport).

Changes:

  • Add clustering enable/disable toggle and clusteringPixelSizeThreshold plumbing into viewer construction and SlideViewer UI/state.
  • Add memory monitoring service + footer UI, gated behind enableMemoryMonitoring app config.
  • Improve DICOM Tag Browser series selection behavior (pass series UID from current route; avoid in-place sorting).

Reviewed changes

Copilot reviewed 13 out of 17 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
yarn.lock Updates lock entry for pinned dicomweb-client version resolution.
package.json Pins dicomweb-client to 0.10.3 (no range).
src/types/performance.d.ts Adds TS declarations for experimental memory APIs.
src/services/MemoryMonitor.ts Introduces singleton memory monitoring with modern + Chrome fallback APIs.
src/components/MemoryFooter.tsx Adds footer UI to display memory usage and emit warnings.
src/AppConfig.d.ts Adds enableMemoryMonitoring configuration flag.
src/App.tsx Conditionally renders MemoryFooter across routes when enabled.
src/components/SlideViewer/utils/viewerUtils.ts Passes annotationOptions.clusteringPixelSizeThreshold into DMV viewer construction.
src/components/SlideViewer/types.ts Extends SlideViewerState with clustering threshold + enabled flag.
src/components/SlideViewer.tsx Adds clustering toggle/threshold UI + handlers; adds shouldComponentUpdate.
src/components/Header.tsx Extracts seriesInstanceUID from route and passes it to Tag Browser.
src/components/DicomTagBrowser/DicomTagBrowser.tsx Accepts seriesInstanceUID, avoids in-place sort, and auto-selects matching series.
src/App.light.less Adds multiline menu item CSS helper class.
src/App.dark.less Adds multiline menu item CSS helper class.
docs/MEMORY_MONITORING.md Adds detailed documentation for memory monitoring feature/configuration.
README.md Documents memory monitoring feature and config flag.
.gitignore Ignores .cursorrules.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@igoroctaviano
Copy link
Collaborator Author

@fedorov updated, do you need to run copilot again?

@fedorov fedorov requested a review from Copilot January 26, 2026 15:43
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 17 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@fedorov
Copy link
Member

fedorov commented Jan 26, 2026

@igoroctaviano I hope the new comments are helpful, but if they are not - I definitely trust you more than Copilot! Please use your judgement.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 17 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 17 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@igoroctaviano
Copy link
Collaborator Author

@fedorov addressed the CR issues.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 17 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
4 Security Hotspots
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@igoroctaviano igoroctaviano merged commit bcf57c9 into master Jan 29, 2026
6 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants