Skip to content

fix: Restore viewport interactivity when deleting in-progress Spline or Livewire measurement#5905

Open
GhadeerAlbattarni wants to merge 3 commits intoOHIF:masterfrom
GhadeerAlbattarni:fix/spline-livewire-tools-interactivity
Open

fix: Restore viewport interactivity when deleting in-progress Spline or Livewire measurement#5905
GhadeerAlbattarni wants to merge 3 commits intoOHIF:masterfrom
GhadeerAlbattarni:fix/spline-livewire-tools-interactivity

Conversation

@GhadeerAlbattarni
Copy link
Collaborator

@GhadeerAlbattarni GhadeerAlbattarni commented Mar 17, 2026

Context

Fixes #5673

This PR fixes loss of viewport interactivity when a user deletes an in-progress Spline or Livewire measurement from the context menu.

When the user starts drawing with Spline or Livewire tool, then attempt to delete the measurement before it's completed (whether through backspace or right click), the viewport becomes non-interactive, the user cannot draw again, switch tools, until they double-click on the image or refresh the viewer.

Root cause

Deleting the measurement only removed the annotation from annotation state. The Spline/Livewire tool was never told to cancel, so it kept its in-progress state (isDrawing). The tool stayed in “active manipulation” mode and continued to block other interaction with the viewport.

Changes & Results

  • initMeasurementService.ts
    In the MEASUREMENT_REMOVED subscription, get the active viewport element (same pattern as MEASUREMENT_UPDATED) and call cancelActiveManipulations(element) before removeAnnotation.

Before
After deleting a non completed measurement, the viewport becomes interactive and can't use any tools.

After
The viewport stay active after deleting a measurement

spline-tool-delete-fix.mov

Testing

  1. Open a study in the viewer mode.
  2. Select Spline or Livewire tool.
  3. Start drawing (place one or more points).
  4. Right-click on the viewport and choose 'Delete measurement' or backspace
  5. Confirm the measurement is removed and the viewport stays interactive: you can immediately draw a new measurement, switch tools, etc.

Checklist

PR

  • My Pull Request title is descriptive, accurate and follows the
    semantic-release format and guidelines.

Code

  • My code has been well-documented (function documentation, inline comments,
    etc.)

Public Documentation Updates

  • [] The documentation page has been updated as necessary for any public API
    additions or removals.

Tested Environment

  • OS: macOS 10.15.4
  • Node version: v22.13.0
  • Browser: Chrome 83.0.4103.116

Greptile Summary

This PR fixes a viewport interactivity bug where deleting an in-progress Spline or Livewire annotation (via the context menu or backspace) left the tool in its isDrawing state, blocking all further interaction until the user double-clicked or refreshed. The root cause was that MEASUREMENT_REMOVED only removed the annotation from state without notifying the active tool to cancel its manipulation.

Key changes:

  • initMeasurementService.ts: Imports cancelActiveManipulations from @cornerstonejs/tools and calls it on the active viewport element at the start of the MEASUREMENT_REMOVED handler — following the same getActiveViewportEnabledElement pattern already used in the MEASUREMENT_UPDATED handler. The fix also uses ?.viewport?.element (with extra optional chaining on viewport) which is slightly safer than the existing ?.viewport.element usage on line 408.
  • tests/Livewire.spec.ts & tests/Spline.spec.ts: Two new E2E tests each verify that after deleting an in-progress annotation via the context menu, the viewport remains interactive and a new annotation can be successfully drawn and confirmed.

Minor concerns:

  • cancelActiveManipulations is called for every MEASUREMENT_REMOVED event, not only when the annotation is in-progress. This could interrupt an active drawing session if a separate completed annotation is deleted concurrently (e.g., from the measurements side-panel).
  • The Livewire.spec.ts test includes an unused rightPanelPageObject parameter that should be removed.

Confidence Score: 4/5

  • Safe to merge with minor cleanup; the core fix is correct and well-targeted.
  • The fix correctly resolves the reported bug using an established pattern already present in the file, and is backed by two new E2E tests. A small logic concern exists around calling cancelActiveManipulations unconditionally for all removals (not just in-progress ones), and there is an unused parameter in the Livewire test. Neither issue is blocking, but the unconditional cancellation could introduce subtle regressions in edge cases.
  • extensions/cornerstone/src/initMeasurementService.ts — review the unconditional cancelActiveManipulations call to ensure it doesn't affect scenarios where a completed annotation is deleted while drawing is active.

Important Files Changed

Filename Overview
extensions/cornerstone/src/initMeasurementService.ts Adds cancelActiveManipulations call in the MEASUREMENT_REMOVED handler to restore viewport interactivity after deleting an in-progress annotation. The fix is correct for the reported bug but is applied unconditionally to all removals, which could inadvertently cancel an active drawing if a different annotation is removed concurrently.
tests/Livewire.spec.ts Adds a new E2E test covering the delete-in-progress-annotation scenario for Livewire. The test logic is correct and mirrors the Spline test, but the function signature includes an unused rightPanelPageObject parameter that should be removed.
tests/Spline.spec.ts Adds a new E2E test covering the delete-in-progress-annotation scenario for Spline. Clean implementation with no issues found.

Sequence Diagram

sequenceDiagram
    participant User
    participant Viewport
    participant ContextMenu
    participant MeasurementService
    participant initMeasurementService
    participant CornerstoneTools

    User->>Viewport: Start drawing (Spline/Livewire)
    Viewport->>CornerstoneTools: Tool enters isDrawing=true state
    User->>ContextMenu: Right-click → Delete measurement
    ContextMenu->>MeasurementService: remove(measurementId)
    MeasurementService->>initMeasurementService: MEASUREMENT_REMOVED event
    Note over initMeasurementService: NEW: cancelActiveManipulations(element)
    initMeasurementService->>CornerstoneTools: cancelActiveManipulations(element)
    CornerstoneTools->>Viewport: Reset isDrawing=false, restore interactivity
    initMeasurementService->>CornerstoneTools: removeAnnotation(measurementId)
    Viewport-->>User: Viewport is interactive again
Loading

Last reviewed commit: c076066

Greptile also left 2 inline comments on this PR.

@netlify
Copy link

netlify bot commented Mar 17, 2026

Deploy Preview for ohif-dev canceled.

Name Link
🔨 Latest commit 914c22e
🔍 Latest deploy log https://app.netlify.com/projects/ohif-dev/deploys/69b9c9c78ef977000848036f

Comment on lines +28 to +33
test('should restore viewport interactivity after deleting an in-progress Livewire annotation via context menu', async ({
page,
DOMOverlayPageObject,
mainToolbarPageObject,
viewportPageObject,
rightPanelPageObject,
Copy link
Contributor

Choose a reason for hiding this comment

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

P2 Unused rightPanelPageObject parameter

rightPanelPageObject is destructured in the test function signature but is never referenced anywhere inside the test body. This will typically trigger a lint/TypeScript "unused variable" warning. It should be removed from the parameter list to keep the signature consistent with what is actually used (matching the pattern in Spline.spec.ts where this parameter is correctly omitted).

Suggested change
test('should restore viewport interactivity after deleting an in-progress Livewire annotation via context menu', async ({
page,
DOMOverlayPageObject,
mainToolbarPageObject,
viewportPageObject,
rightPanelPageObject,
test('should restore viewport interactivity after deleting an in-progress Livewire annotation via context menu', async ({
page,
DOMOverlayPageObject,
mainToolbarPageObject,
viewportPageObject,
}) => {

Comment on lines +507 to +512
// Cancel any active tool manipulation (e.g., Spline/Livewire) to avoid leaving the tool
// in a drawing state after deleting a not completed measurement, which can block viewport interactivity.
const element = getActiveViewportEnabledElement(viewportGridService)?.viewport?.element;
if (element) {
cancelActiveManipulations(element);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

P2 cancelActiveManipulations called unconditionally on all removals

cancelActiveManipulations is invoked for every MEASUREMENT_REMOVED event regardless of whether the removed annotation was actually in-progress. Consider a scenario where a user is actively drawing a Spline/Livewire annotation while a different (completed) annotation is removed — e.g., via the measurements side-panel, an undo operation, or a programmatic delete from another source. In that case, the call would cancel the user's active drawing unexpectedly.

A safer approach would be to only cancel when the removed annotation is the one currently being drawn. You could check the annotation's isDrawing flag or whether it has no closed contour before calling cancelActiveManipulations:

const removedAnnotation = annotation.state.getAnnotation(removedMeasurementId);
// Only cancel if the annotation was still in-progress (i.e. not yet completed/closed)
if (element && removedAnnotation?.data?.contour?.closed === false) {
  cancelActiveManipulations(element);
}

While this edge case may be difficult to hit in practice (since the viewport focus is typically held by the drawing interaction), it is worth being explicit about the intent.

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.

[Bug] Viewport interactivity is lost after deleting Spline or Livewire tool with right click menu while drawing it

1 participant