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..8f9a81f. View details on DeepSource ↗.

Analysis Summary

AnalyzerStatusSummaryLink
DeepSource JavaScript LogoJavaScript❌ Failure
❗ 18 occurences introduced
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
@sonarqubecloud
Copy link

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