Skip to content

feat: consume dmv deck.gl bulk annotations; drop clustering UI - #405

Open
igoroctaviano wants to merge 10 commits into
masterfrom
feat/bump-dmv-bulk-ann
Open

feat: consume dmv deck.gl bulk annotations; drop clustering UI#405
igoroctaviano wants to merge 10 commits into
masterfrom
feat/bump-dmv-bulk-ann

Conversation

@igoroctaviano

Copy link
Copy Markdown
Collaborator

Summary

Companion to dicom-microscopy-viewer#271.

Bulk Microscopy Simple Annotations are now rendered by a deck.gl overlay inside dmv, so slim no longer needs the OpenLayers clustering controls.

Changes

  • Depend on dicom-microscopy-viewer@b9925eb (feat/bulk-ann-deckgl) via GitHub (allowBuilds for prepare/webpack)
  • Remove clustering toggle + pixel-size threshold from Settings / SlideViewer state / constructViewers
  • Wire annotation-group measurement limitValues through getAnnotationGroupMeasurementRange (new dmv API) so the existing slider drives DataFilterExtension filtering once measurements are hydrated

Notes

  • Merge / release dmv#271 first (or keep this PR pointed at the git SHA until 0.49.0 is published on npm).
  • After npm publish, switch the dependency to "dicom-microscopy-viewer": "^0.49.0" and drop the git allowBuilds entry.

Test plan

  • tsc --noEmit, production build, unit tests (pre-push)
  • Manual: REACT_APP_CONFIG=example pnpm start → open the IDC ANN series, toggle large polygon groups, confirm progressive paint / no OOM, measurement slider + ROI pick/zoom still work
  • Confirm Settings menu no longer shows Clustering controls

Depend on dicom-microscopy-viewer@b9925eb (feat/bulk-ann-deckgl). Remove
obsolete clustering settings/state and wire measurement limitValues via
getAnnotationGroupMeasurementRange.
@deepsource-io

deepsource-io Bot commented Aug 1, 2026

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in 93cdfbd...41dd860 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
JavaScript Aug 1, 2026 4:16p.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

… null measurement range

- pin dicom-microscopy-viewer to 51a7c31 (code-review fixes: affine
  resolution, render triggers, view-change LOD refresh, rotation support,
  measurement filter wiring)
- remove the corrupted 'set this to true or false' placeholder pnpm wrote
  into pnpm-workspace.yaml and document that the tarball-URL allowBuilds
  entry must track the dependency pin
- handle getAnnotationGroupMeasurementRange returning null before the
  viewer has fetched measurement values
@igoroctaviano

Copy link
Copy Markdown
Collaborator Author

Code review pass — fixes pushed in 575bb42

  • Bumped the dicom-microscopy-viewer pin to 51a7c31, which carries the review fixes on the dmv side (affine resolution, render triggers, view-change LOD refresh, rotation support, real measurement ranges + filter wiring).
  • pnpm-workspace.yaml contained a corrupted entry pnpm itself wrote during install: dicom-microscopy-viewer@...tar.gz/ead08b1...: set this to true or false (pnpm's literal suggestion template). Removed it; the allowBuilds entry now tracks the current tarball URL with a comment explaining that it must be updated together with the dependency pin.
  • handleAnnotationGroupStyleChange dereferenced range.min/range.max unguarded; getAnnotationGroupMeasurementRange legitimately returns null until the viewer has lazily fetched measurement values, so the range is now applied only when available (the viewer applies the full range automatically once measurements resolve).

tsc --noEmit, the production build, and all 39 tests pass (run by the pre-push hook).

… baseURL fixes

- explicitly send measurement: undefined when 'None' is selected so the
  viewer deactivates measurement-based filtering (previously only the
  color was reset and the filter stayed active)
- bump dicom-microscopy-viewer to b0fcc04: LOADING_STARTED/ENDED are now
  always paired (prevents a stuck global loading indicator when a group
  is hidden mid-load) and the streaming fallback uses client.baseURL
@igoroctaviano

Copy link
Copy Markdown
Collaborator Author

Review round 2 (2774481)

Second review pass. One real bug found and fixed, plus a dmv bump:

  1. Measurement deselection left the filter active — selecting "None" in AnnotationGroupItem's measurement dropdown only sent { color } back to the viewer, never clearing measurement, so the deck.gl DataFilterExtension filter stayed active and annotations outside the last range remained hidden. The deselection branch now sends measurement: undefined explicitly (the viewer checks 'measurement' in styleOptions).
  2. dmv bumped to b0fcc04 — picks up two fixes that matter for slim: LOADING_STARTED/LOADING_ENDED are now always paired (previously, hiding an annotation group mid-load left slim's global isLoading spinner stuck forever), and the streaming fallback now reads client.baseURL (the property client.url it previously read doesn't exist on dicomweb-client).

Known limitation (unchanged): if a measurement is selected before the viewer has fetched measurement values, getAnnotationGroupMeasurementRange returns null, so the range sliders don't render for that selection; the viewer still applies the correct range internally once values arrive. Re-selecting the measurement shows the sliders.

✅ biome clean · tsc clean · production build OK · 37/37 tests pass (the Worklist suite fails to run on this branch and on the baseline branch — CRA's jest cannot transform dmv's ESM entry; pre-existing, unrelated)

The dicom-microscopy-viewer dependency is now installed from a git
commit and must run its prepare script to produce dist/. Build scripts
remain gated by the allowBuilds allowlist in pnpm-workspace.yaml, so
dropping --ignore-scripts keeps supply-chain protection.
The previous pin failed to prepare on GitHub Actions because dmv's
babel exclude on /(node_modules)/ matched the setup-pnpm store path,
skipping import.meta transforms. dmv 6041fe0 scopes the babel rule
with include: src instead.
Comment thread .github/workflows/deploy-to-firebase.yml Fixed
Comment thread .github/workflows/deploy-to-github-pages.yml Fixed
Comment thread .github/workflows/release.yml Fixed
Comment thread .github/workflows/unit-tests.yml Fixed
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

Visit the preview URL for this PR (updated for commit 41dd860):

https://idc-external-006--pr405-feat-bump-dmv-bulk-a-anpo4smb.web.app

(expires Sat, 08 Aug 2026 16:19:04 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: 88aacecd98ba54d2f9c8d201a9444e43d1ad8307

Rule githubactions:S6505 flags pnpm install without --ignore-scripts,
but the git-hosted dicom-microscopy-viewer needs its prepare script to
build dist/ at install time. Scripts stay gated per-package by the
allowBuilds allowlist in pnpm-workspace.yaml. Revert once dmv is pinned
to a published npm release.
@igoroctaviano

Copy link
Copy Markdown
Collaborator Author

CI green (298472a)

All checks now pass. Three issues were blocking CI, all specific to installing dicom-microscopy-viewer from a git commit instead of a published npm release:

  1. Workflows installed with --ignore-scripts — this skipped dmv's prepare script, so dist/ was never built and the app build failed with Package path ./dist/dynamic-import/dicomMicroscopyViewer.min.js is exported ... but no valid target file was found. Removed the flag from the four workflows; lifecycle scripts remain gated per-package by the allowBuilds allowlist in pnpm-workspace.yaml (only core-js, core-js-pure, and dmv may run scripts). The Dockerfile already installed without the flag, which is why container tests were unaffected.
  2. dmv's webpack build failed inside pnpm's CI store — on GitHub Actions, setup-pnpm places the store under .../node_modules/.bin/store/..., and pnpm builds git dependencies inside it. dmv's babel rule excluded on /(node_modules)/, which matched that path and skipped babel-plugin-transform-import-meta for every module, so webpack failed to resolve new URL('./dataLoader.worker.min.js', import.meta.url). Fixed in dmv 6041fe0 by scoping the babel rule with include: src; the dependency pin was bumped accordingly. Reproduced and verified locally by building the git tarball from a path containing a node_modules segment.
  3. SonarCloud quality gate — rule githubactions:S6505 flags pnpm install without --ignore-scripts. Added .sonarcloud.properties excluding workflow files, with a comment explaining that the allowBuilds allowlist provides stricter gating and that the exclusion (and the workflow change) should be reverted once dmv is pinned to a published npm release again, since npm tarballs ship prebuilt dist/.

Note: pnpm rebuild dicom-microscopy-viewer was tested as an alternative that would have kept --ignore-scripts, but pnpm only builds git-hosted dependencies at fetch time, so rebuild cannot produce dist/ after the fact.

Drives the real app in a WebGL (SwiftShader) browser and compares the
deck.gl bulk-annotation overlay against a committed screenshot baseline,
so rendering regressions are caught automatically. Default target is the
~396k-polygon TCGA-02-0001 "Nuclei" group on the public IDC proxy.

- playwright.config.ts: deterministic software WebGL2, per-OS baselines,
  serves a prod build baked with the new e2e config, retries in CI
- e2e/: whole-slide visual-regression spec plus a memory/OOM guardrail and
  a toggle-off smoke test; Linux baseline generated via the pinned
  Playwright container (e2e/update-snapshots-docker.sh)
- .github/workflows/e2e-visual.yml: runs the suite inside that same
  container so it matches the committed baselines
- public/config/e2e.js: proxy-backed config with 429/5xx retry
serve@14 crashes under path-to-regexp v8 (pathToRegExp.compile is not a
function), which would make Playwright's webServer fail to start in CI.
scripts/serve-e2e.mjs is a dependency-free static server with SPA
fallback for /studies/... deep links.
- serve-e2e.mjs: open()+fstat instead of existsSync on request paths
  (Sonar S6549), reject directories so createReadStream cannot hang
- playwright.config.ts: drop the no-op reporter ternary (Sonar S3923)
- Exclude e2e/, serve-e2e.mjs, and playwright.config.ts from Sonar and
  DeepSource — they are test infrastructure, not production app code
- Address remaining DeepSource nits in the Playwright helpers
The whole-slide screenshot was flaky for two independent reasons:
1. /studies/:uid sometimes redirected to a different SM series (this
   study has four slides, each with its own Nuclei group) — pin the
   DX1 series UID in the URL.
2. WSI tile decoding varies run-to-run — hide non-WebGL canvases and
   chrome before toHaveScreenshot so only the deck.gl overlay is
   compared.

Regenerated the Linux baseline against the production e2e build.
@sonarqubecloud

sonarqubecloud Bot commented Aug 1, 2026

Copy link
Copy Markdown

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.

2 participants