Skip to content

Feat multipolygon brush#153

Open
Aiosa wants to merge 14 commits intorelease/v3from
feat-multipolygon-brush
Open

Feat multipolygon brush#153
Aiosa wants to merge 14 commits intorelease/v3from
feat-multipolygon-brush

Conversation

@Aiosa
Copy link
Member

@Aiosa Aiosa commented Mar 4, 2026

No description provided.

@coderabbitai
Copy link

coderabbitai bot commented Mar 4, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3120d597-ee91-4f6f-aedf-7fa0b9959565

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat-multipolygon-brush

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces significant enhancements to the annotation system, focusing on improving user interaction, data management, and developer extensibility. The changes streamline how annotations are selected, edited, and managed within the history panel, while also refining the underlying tools for creating complex shapes like multipolygons. The update also modernizes the build pipeline and expands the event system to support more dynamic and responsive annotation workflows.

Highlights

  • Refactored Annotation Selection and Deselection: The core logic for handling annotation selection and deselection has been significantly refactored to improve multi-selection capabilities, programmatic control, and UI synchronization. This includes changes to internal state management, event emission, and how selections are applied to the canvas.
  • Enhanced History Management and UI Synchronization: The annotation history manager (AnnotationHistoryManager) has been improved with better visual synchronization between the canvas and the history board. New mechanisms for pausing selection sync, handling edit modes, and updating UI controls (like delete buttons) based on selection state have been introduced.
  • Improved FreeFormTool (Magic Wand) Functionality: The FreeFormTool, often referred to as the magic wand, has received updates to its contour processing, polygon simplification algorithms, and binary mask generation. These changes aim to produce more accurate and optimized multipolygon annotations.
  • Extended Event System for Annotations and Layers: A comprehensive set of new events has been added to the annotation module, covering various lifecycle stages for annotations (create, delete, replace, edit, select, deselect, comment actions) and layers (add, remove). This provides more granular control and extensibility for plugins.
  • Refined Annotation Factories and Highlighting: Annotation object factories (e.g., Ruler, Text, Multipolygon) have been updated for consistent highlighting behavior, improved editing capabilities (especially for text annotations), and more robust serialization. A new createHighlight method replaces the previous selected method for generating highlight objects.
  • Flexible Export Options: The export functionality now supports exporting only selected annotations or layers, providing more control over the data being exported. This is integrated with the new selection management system and includes error handling for empty selections.
  • Updated Build Process with esbuild: The project's build process has been modernized by integrating esbuild for UI bundling. A new buildUI Grunt task and a postinstall script ensure that UI assets are correctly built and bundled.
Changelog
  • Gruntfile.js
    • Added a new 'buildUI' Grunt task to bundle UI assets using esbuild.
    • Updated the 'postinstall' script to execute 'grunt css' and 'grunt buildUI'.
  • modules/annotations/EVENTS.md
    • Added numerous new events for annotation and layer lifecycle, including creation, deletion, replacement, editing, selection, deselection, and comment management.
    • Included a detailed TypeScript type definition for 'AnnotationComment'.
  • modules/annotations/annotations-canvas.js
    • Refactored annotation selection state management, replacing '__selectionSnapshot' with an array and introducing '__programmaticClear' for controlled selection clearing.
    • Modified the 'export' method to save ongoing edits before serialization.
    • Updated 'exportPartial' to filter out objects marked with 'excludeFromExport'.
    • Adjusted layer management methods ('setActiveLayer', 'unsetActiveLayer', 'selectLayer', 'deselectLayer', 'clearLayerSelection') to work with layer objects and emit richer event payloads.
    • Changed property '_position' to 'position' for layer data handling.
    • Rewrote 'deleteObject' to handle various annotation types (layers, helpers, full annotations) and integrate with the history manager.
    • Modified 'addAnnotation' and 'replaceAnnotation' to automatically select the newly created/replaced annotation.
    • Updated 'getSelectedAnnotationIds' to use 'internalID' instead of 'incrementId'.
    • Refactored 'selectAnnotation' and 'deselectAnnotation' for improved multi-selection and programmatic control, including a 'clearPrevious' option.
    • Replaced '_applySelectionFromIds' with '_applySelection' for consistent selection application and highlighting.
    • Updated '_emitAnnotationSelectionChanged' to pass 'selected' and 'deselected' annotation arrays.
    • Enhanced 'clearAnnotationSelection' to remove highlights and use programmatic clearing.
    • Adjusted 'isAnnotationSelected' to support array-based references and 'internalID'.
    • Simplified 'deleteSelectedAnnotations' and 'deleteAllAnnotations' to leverage the refactored 'deleteObject'.
    • Modified 'updateSingleAnnotationVisuals' to accept a selection array and render preset text.
    • Updated event listeners for 'annotation-selection-changed' to process both selected and deselected annotations.
    • Refined 'selection:cleared' event handling to prevent unintended deselection.
    • Removed complex active selection logic from canvas event listeners.
    • Added 'altKey' support for cycling through overlapping objects during selection.
    • Updated 'highlightAnnotation' to use 'factory.createHighlight' for generating highlight objects.
    • Improved 'import' logic to check for factory existence and use the module's history manager for ID assignment.
    • Modified 'clearAll' to use 'clearAnnotationSelection(true)'.
  • modules/annotations/annotations.js
    • Updated 'setAnnotationCommonVisualProperty' to correctly apply common visual properties via the preset manager.
    • Adjusted 'fabric.Object.prototype.zooming' for highlight objects to use 'this' context and refined stroke styling.
    • Changed 'StateFreeFormToolAdd.handleClickUp' to trigger a canvas rerender after finishing the free-form tool operation.
  • modules/annotations/convert/asapXml.js
    • Updated factory and polygon factory references to use 'this.context.module' for consistency.
  • modules/annotations/convert/convertor.js
    • Expanded JSDoc for 'encodePartial' to include new options like 'scopeSelected'.
    • Implemented 'scopeSelected' option in 'encodePartial' to allow exporting only selected annotations/layers, with error handling for empty selections.
    • Updated preset and annotation getters to use 'context.module.presets.toObject()' for consistency.
  • modules/annotations/convert/geoJSON.js
    • Updated factory references to use 'this.context.module' for consistency.
  • modules/annotations/convert/qupath.js
    • Updated factory and preset references to use 'this.context.module' for consistency.
    • Modified 'OSDAnnotations.Preset.fromJSONFriendlyObject' to use 'this.context.module'.
  • modules/annotations/freeFormTool.js
    • Adjusted canvas dimensions for internal drawing buffers.
    • Changed fill rule from 'evenodd' to 'nonzero' for canvas context operations.
    • Increased the distance threshold for processing contours in '_processContours'.
    • Integrated 'OSDAnnotations.PolygonUtilities.simplify' and 'simplifyQuality' for aggressive polygon simplification in '_getContours'.
    • Updated '_getBinaryMask' to use luminance and alpha channel for mask generation, improving accuracy, and simplified bounds assignment.
  • modules/annotations/history.js
    • Introduced '_selectionSyncPaused' flag to control selection synchronization.
    • Updated event handlers for 'layer-selection-changed', 'active-layer-changed', and 'annotation-selection-changed' to use new object-based payloads and '_updateSelectionVisuals'.
    • Added 'getHistoryHeaderElementId' for consistent header ID retrieval.
    • Modified 'destroyHistoryWindow' to save ongoing edits before closing.
    • Updated history window header HTML with a new ID for better control.
    • Refined CSS for selected layers and annotation indentation in the history board.
    • Improved 'refresh' method to filter board items and update selection visuals using new methods.
    • Added '_withSelectionSyncPaused' utility function to temporarily disable selection synchronization.
    • Updated '_syncSortableSelection' to work with annotation/layer objects instead of just IDs and respect the sync pause.
    • Ensured 'setHistoryState' is called on '_syncLoad'.
    • Added warning dialogs for invalid drag-and-drop operations in the history board.
    • Modified 'Sortable.utils.select' to be called on shift-click in the history board.
    • Added checks for 'isOngoingEdit' in '_boardItemClick' and 'moveAnnotationInBoard'.
    • Updated '_setControlsVisuallyEnabled' to use 'pointerEvents' and 'opacity' for header control.
    • Renamed '_updateDeleteLayerHeaderButton' to '_updateDeleteSelectionHeaderButton' and enhanced its functionality to handle disabled states and check for any selected items.
    • Refactored '_syncSelectionFromCanvas' to process 'selected' and 'deselected' arrays for more precise UI updates.
    • Updated '_clearAllSelectionVisuals' to call the new delete selection header button update.
    • Replaced '_updateActiveLayerVisual', '_updateSelectedLayersVisual', and '_updateSelectedAnnotationsVisual' with a unified '_updateSelectionVisuals' method.
    • Added '_stripLabelSuffix' helper for text processing.
    • Enhanced '_boardItemEdit' to select the annotation, remove highlight, set 'hoverCursor', and update input fields for category editing.
    • Modified '_boardItemSave' to strip label suffix from category, reset 'hoverCursor', and update button styling.
    • Refactored '_enableAfterEdit' and '_disableForEdit' to manage toolbar interaction state and emit 'enabled-edit-mode' events.
    • Changed '_emitLayerObjectsChanged' to raise the event on 'this._context.fabric'.
  • modules/annotations/magic-wand.js
    • Replaced 'promoteHelperAnnotation' with 'deleteHelperAnnotation' followed by 'addAnnotation' for magic wand results.
  • modules/annotations/objectAdvancedFactories.js
    • Ensured 'Ruler.create' returns the created instance.
    • Renamed 'Ruler.selected' to 'Ruler.createHighlight' and updated it to call 'super.createHighlight'.
    • Corrected 'Ruler.toPointArray' to accurately calculate coordinates relative to the object's position.
  • modules/annotations/objectGenericFactories.js
    • Updated 'Rect' constructor to remove the 'autoCreationStrategy' parameter.
    • Removed redundant 'parameters.controls = {}' assignment in 'Rect.create'.
    • Modified 'Rect.edit' to set 'hasControls: true' during editing.
    • Updated 'Rect.recalculate' to set 'hasControls: false' after editing.
    • Adjusted 'Ellipse.recalculate' to set 'hasControls: false' after editing.
    • Changed 'Text' factory to use 'i-text' as the fabric type and set 'options.editable = false' in 'create'.
    • Set 'selectable: true' for text annotations in 'Text.configure'.
    • Added 'Text.renderPresetText' to dynamically update text based on preset metadata.
    • Updated 'Text.copy' to use 'fabric.IText'.
    • Enhanced 'Text.edit' to set 'editable: true' and store original text for comparison.
    • Refactored 'Text.recalculate' to handle custom text input, update 'meta.category', and reset 'editable: false'.
    • Renamed 'Text.selected' to 'Text.createHighlight' and made it return 'undefined'.
    • Set 'hoverCursor: 'default'' in 'ExplicitPointsObjectFactory.edit'.
    • Added a todo for optional copy in 'ExplicitPointsObjectFactory.recalculate'.
    • Ensured 'Multipolygon.configure' returns the configured object.
    • Renamed 'Multipolygon.selected' to 'Multipolygon.createHighlight' and updated it to call 'super.createHighlight'.
    • Set 'hoverCursor: 'default'' in 'Multipolygon.edit'.
    • Corrected 'Multipolygon._transformPoint' to use 'fabricObject.canvas.viewportTransform'.
  • modules/annotations/objects.js
    • Added 'AnnotationObjectFactory.geometryProps' static property for consistent geometry cloning.
    • Commented out 'renderAllControls' for future integration.
    • Replaced 'cloneFabricObject' with '_cloneFabricObject' for more robust deep cloning using 'geometryProps'.
    • Renamed 'selected' to 'createHighlight' and refactored its implementation to use '_cloneFabricObject', adjust stroke styling, and set 'evented: false' and 'excludeFromExport: true' for highlight objects.
    • Added an empty 'selected' method as a placeholder.
    • Added an empty 'renderPresetText' method as a placeholder.
    • Updated 'PolygonUtilities.simplify' to use screen pixel tolerance, clamp tolerance values, and remove the 'imagePixelOnScreen' parameter for more adaptive simplification.
  • modules/annotations/presets.js
    • Adjusted 'commonAnnotationVisuals.cornerColor' value.
    • Modified 'getAnnotationOptions' to set 'layerID: undefined' by default.
  • modules/annotations/viewport-segmentation.js
    • Replaced 'promoteHelperAnnotation' with 'deleteHelperAnnotation' followed by 'addAnnotation' for viewport segmentation results.
    • Updated '_getBinaryMask' to use the alpha channel for mask generation and simplified bounds assignment.
  • package.json
    • Added a 'postinstall' script to run 'grunt css && grunt buildUI'.
  • plugins/annotations/annotationsGUI.js
    • Updated 'AnnotationComment' typedef to match new event definitions.
    • Added a new 'annotations-outline-control' div to the UI.
    • Added a handler for the 'annotation-updated-comment' event.
    • Modified '_addComment' to request canvas rerender before adding the comment.
    • Changed sorting order for replies in '_renderComments'.
    • Added 'data-reply-to' attribute to comment elements and used 'mapAuthorCallback' in '_renderSingleComment'.
    • Updated '_deleteComment' to accept the full comment object and handle parent placeholder removal more robustly.
    • Modified '_addReplyComment' to request canvas rerender and add the comment.
    • Introduced 'annotationsEnabledEditModeHandler' and '_toggleEnabledStyle' to manage UI element states during annotation editing.
    • Updated '_toggleEnabled' to use '_toggleEnabledStyle' for consistent UI state management.
    • Refactored 'getExportData' and 'exportToFile' to utilize the new 'ioArgs.scopeSelected' option for exporting selected items.
    • Ensured 'delete copy.internalID' is called for copied annotations to prevent ID conflicts.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new buildUI Grunt task using esbuild and updates the package.json to run it post-installation. A significant portion of the changes involves enhancing annotation and layer management within annotations-canvas.js, including refactoring selection logic, improving event emission for layer and annotation selection, and updating the deleteObject method to handle various annotation types and integrate with the history manager. New annotation-related events are documented in EVENTS.md. The AnnotationHistoryManager in history.js is updated to better synchronize with canvas selections, improve UI rendering for selected items, and introduce a _withSelectionSyncPaused mechanism to prevent selection feedback loops. The FreeFormTool and ViewportSegmentation modules receive updates to their canvas sizing, contour processing, and mask generation logic, including polygon simplification and improved pixel detection. Annotation factories in objectAdvancedFactories.js and objectGenericFactories.js are adjusted for highlight creation, text rendering, and editing behavior, with Text annotations now using IText and having improved edit/recalculate logic. Export functionalities in convertor.js and related files are updated to support exporting only selected annotations/layers. Minor fixes include updating comment handling in annotationsGUI.js to use Date.now() and improve reply rendering, and adjusting styling and event handling for UI elements. A review comment highlights a potential Stored XSS vulnerability in history.js due to unescaped user input in an input tag's value attribute. Another comment points out that the selection:cleared event handler in annotations-canvas.js prevents users from deselecting all annotations by clicking on an empty area, suggesting a more intuitive approach for selection clearing.

Comment on lines 1744 to 1752
mainRowContent = `
<label class="show-hint d-block py-1" style="white-space: nowrap; padding-left:0;">
<input type="text"
class="form-control border-0"
readonly
style="background:transparent;color: inherit; display:inline-block; padding-left:0"
value="${objCategory} ${object.label}"
value="${displayValue}"
name="category">
</label>`;

Choose a reason for hiding this comment

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

security-high high

The displayValue variable, which can contain user-controlled metadata (category), is embedded into the value attribute of an input tag without proper escaping. This allows for attribute injection and Stored XSS if the value contains double quotes and a script tag (e.g., "><script>alert(1)</script>).

Comment on lines +1692 to +1695
this.canvas.on('selection:cleared', function(e) {
if (!_this.__programmaticClear && e.deselected && e.deselected.length > 0) _this.canvas.setActiveObject(e.deselected[0]);
_this.__programmaticClear = false;
});

Choose a reason for hiding this comment

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

medium

The selection:cleared event handler prevents users from deselecting all annotations by clicking on an empty area of the canvas. This overrides standard UI behavior and could be frustrating. It appears to be a workaround for accidental deselection.

A more intuitive approach would be to manage selection clearing within the mouse:down event handler. There, you can check if the click is on an empty area and no modifier keys are pressed, and then explicitly clear the selection. This would provide more predictable behavior for the user.

@Aiosa
Copy link
Member Author

Aiosa commented Mar 4, 2026

Please address also the chat comments.

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