Skip to content

Add Split Segments Toolkit and Draw Mode for Skeleton Tool #8434

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 93 commits into from
Apr 29, 2025

Conversation

philippotto
Copy link
Member

@philippotto philippotto commented Mar 7, 2025

This PR:

  • adds a new "split segment" workflow. Users can create skeletons that define a 3D surface to split a segment. Then, a part of that segment can be relabeled using the floodfill tool (which will not cross the 3D surface). This behavior can be enabled by selecting the "Split Segments" toolkit.
  • adds a new "draw mode" for the skeleton tool. When enabled, one can rapidly create notes by keeping the left mouse button pressed and moving the cursor.
  • Different sets of tools can be selected from a new toolkits dropdown (next to the view mode dropdown). By default, all tools will be visible. However, one can also choose read-only, volume or split-segments toolkits.
  • Dev-related:
    • updates @types/three
    • refactoring of AnnotationTool enum (class descriptors with properties instead of enum)
    • refactoring of annotation tool buttons in toolbar (necessary because of the toolkits)
    • refactoring dis/re-enabling of tools into saga (is a better place in my opinion)
    • refactoring of disabled tool accessors into own module (also avoided circular dependencies)
    • fix cycling of tools in view mode
    • add "Toggle Action Logging" to command palette for super users

image
image
image

URL of deployed dev instance (used for testing):

  • https://___.webknossos.xyz

Steps to test:

  • use all tools intensively
    • cycle with w (or shift w)
    • select different toolkits (switching from one to another should automatically switch the tool if it's not available anymore)
    • test that a temporarily disabled tool becomes enabled automatically unless the user manually switched in the meantime
  • test the new split workflow
    • select toolkit
    • create boundingbox
    • create nodes
    • use the 3d filltool (restricted to bbox) and relabel a part
  • test the new draw mode of the skeleton
    • also check that the status bar reads the correct descriptions

Issues:


(Please delete unneeded items, merge only when none are left open)

@philippotto philippotto self-assigned this Mar 7, 2025
Copy link
Contributor

coderabbitai bot commented Mar 7, 2025

📝 Walkthrough

Walkthrough

This update introduces a comprehensive "Split Segments" workflow and toolkit for voxel-wise segment splitting in WEBKNOSSOS. Key changes include new React UI components for toolkits, tool buttons, and split segment controls; a new saga and geometry module to render and manage 3D split boundary surfaces; and integration of these features into the flood fill algorithm to prevent propagation across user-defined surfaces. The codebase refactors tool management to use class-based abstractions, adds toolkit selection to the UI, and updates reducers, sagas, and selectors to support toolkit-driven workflows and tool disabling logic. Extensive documentation and test updates accompany these changes.

Changes

Files / Areas Change Summary
frontend/javascripts/oxalis/model/accessors/tool_accessor.ts, constants.ts, related reducers, sagas, selectors Refactored tool management: replaced enum-based tool definitions with class-based abstractions, removed old constants, updated all references, and introduced toolkit groupings.
frontend/javascripts/oxalis/model/accessors/disabled_tool_accessor.ts Added new logic to compute disabled state and explanations for tools based on application state, with memoization and detailed condition checks.
frontend/javascripts/oxalis/model/bucket_data_handling/data_cube.ts Modified flood fill to accept a split boundary mesh and prevent propagation across user-defined 3D surfaces via intersection tests.
frontend/javascripts/oxalis/geometries/compute_split_boundary_mesh_with_splines.ts Introduced algorithm to compute a 3D surface from user-placed skeleton nodes, interpolating splines and generating a mesh for segment splitting.
frontend/javascripts/oxalis/controller/scene_controller.ts, split_boundary_mesh_saga.ts Added management of split boundary mesh rendering, lifecycle, and visibility; integrated with application state and toolkits via a new saga.
frontend/javascripts/oxalis/view/action-bar/tools/toolbar_view.tsx, tool_buttons.tsx, tool_helpers.tsx, toolkit_switcher_view.tsx, skeleton_specific_ui.tsx, volume_specific_ui.tsx, brush_presets.tsx Added new modular React components for toolbar, tool buttons, toolkit selection, skeleton and volume-specific UI, and brush preset management.
frontend/javascripts/oxalis/view/action_bar_view.tsx, view/action-bar/view_modes_view.tsx Updated action bar to use new toolbar and toolkit components, removed prop-driven state in favor of Redux selectors.
frontend/javascripts/oxalis/model/sagas/annotation_tool_saga.ts, root_saga.ts, split_boundary_mesh_saga.ts Updated sagas to support toolkit switching, automatic tool switching based on toolkit and disabled state, and split boundary mesh lifecycle.
frontend/javascripts/oxalis/model/reducers/*, api/api_latest.ts, default_state.ts, store.ts Updated reducers, API, and default state to support toolkit and tool abstractions, continuous node creation, and new user configuration properties.
frontend/javascripts/oxalis/view/action-bar/toolbar_view.tsx Removed: Old monolithic toolbar component and related helpers, replaced by modular components.
frontend/javascripts/oxalis/view/action-bar/tools/toolbar_view.tsx, tool_buttons.tsx, etc. Added: New modular toolbar and tool button components with improved structure and maintainability.
frontend/javascripts/oxalis/model/volumetracing/volumelayer.ts Updated neighbor queue return values to include origin voxel, supporting new flood fill logic.
frontend/javascripts/oxalis/model/sagas/volume/floodfill_saga.tsx Integrated split boundary mesh into flood fill saga, with UI warnings for unsupported cases.
Documentation (docs/proofreading/*, ui/toolbar.md, etc.) Added and updated documentation for split segments toolkit, skeleton pen mode, toolkits, and proofreading workflows.
Tests (test/sagas/*, test/api/*, etc.) Updated and extended tests to use new tool abstractions and validate split segment logic.
Miscellaneous (e.g., CHANGELOG.unreleased.md, package.json, minor spelling/doc fixes) Updated changelog, dependencies, and fixed minor typos in code and documentation.

Assessment against linked issues

Objective (Issue #8078) Addressed Explanation
Add novel split tools to enable voxel-wise segment splitting with user-defined 3D interfaces and bounding boxes
Interpolate split interfaces across multiple sections and validate completeness before applying split
Integrate split surface into flood fill so propagation is restricted by user-defined boundary
Provide UI and workflow for split segment creation, including bounding box and node placement
Document the split segment workflow and update relevant user guides

Possibly related PRs

  • scalableminds/webknossos#8325: Also modifies UpdatableTexture.ts, but focuses on method parameter types rather than constructor or toolkit logic.
  • scalableminds/webknossos#8267: Adds bounding box restriction to flood fill, which is conceptually and code-wise related to the new split boundary mesh restriction.

Suggested labels

new feature, enhancement, refactoring

Suggested reviewers

  • MichaelBuessemeyer

Poem

🐇✨
In the land of voxel dreams,
Where segments twist and split in streams,
A rabbit hops with toolkit pride—
"Split Segments" now, no need to hide!
With bounding box and surface blue,
Your flood fills stop where you want them to.
Annotate and leap ahead—
The code is fresh, the docs are read!
🥕

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Base automatically changed from more-dtypes to master March 20, 2025 08:08
@philippotto philippotto changed the title [WIP] Split segment with spline surface [WIP] Tool for split segments in bounding boxes (using spline surfaces) Apr 10, 2025

![A visualization of a floodfill operation "bleeding" across the boundary because the boundary is not precise enough](../images/splitting-floodfill-visualization.png)

## Impact of "Split Segments" Toolkit
Copy link
Member

Choose a reason for hiding this comment

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

The nodes/skeletons that are being placed in this mode, are the persisted? Or will they disappear after the split operation?

Copy link
Member Author

Choose a reason for hiding this comment

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

The nodes/skeletons that are being placed in this mode, are the persisted?

yes, it's a normal tree. it is only rendered differently when the split toolkit is active. if the toolkit is deactivated, the tree will look like any other tree.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
docs/proofreading/split_segments_toolkit.md (1)

3-4: Duplicate suggestion: clear split interface description
This sentence effectively conveys how to draw the splitting surface, as previously recommended.

🧹 Nitpick comments (5)
docs/proofreading/split_segments_toolkit.md (5)

6-9: Consider using a numbered list for sequential steps
Since these bullets describe an ordered workflow, switching to a numbered list (1., 2., …) could improve readability.


10-10: Improve readability and correct spelling
This bullet is very long—consider splitting into two sentences. Also replace “proof-reading” with “proofreading” and rephrase one of the “If you want to” clauses to avoid repetition.

🧰 Tools
🪛 LanguageTool

[style] ~10-~10: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...ml) for large-scale corrections. If you want to correct data for the purpose of ground-...

(REP_WANT_TO_VB)


[misspelling] ~10-~10: This word is normally spelled as one.
Context: ...o only do a local correction instead of proof-reading the whole dataset. - Go to the first z ...

(EN_COMPOUNDS_PROOF_READING)


12-12: Format tool mode with code ticks
For consistency, wrap UI elements like Pen mode in backticks (e.g., activate the Pen mode) so they stand out.


14-14: Rephrase to avoid repetition
You’ve already used “If you want to” above. Consider “To adjust the interpolation, add new nodes on a slice…” to vary the phrasing.


22-22: Improve alt text for accessibility
The current alt text is verbose. Consider simplifying to something like:
“Flood-fill bleeding across an incomplete split surface.”

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3b40430 and 6cad801.

📒 Files selected for processing (2)
  • docs/proofreading/split_segments_toolkit.md (1 hunks)
  • docs/skeleton_annotation/tools.md (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • docs/skeleton_annotation/tools.md
🧰 Additional context used
🪛 LanguageTool
docs/proofreading/split_segments_toolkit.md

[style] ~10-~10: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...ml) for large-scale corrections. If you want to correct data for the purpose of ground-...

(REP_WANT_TO_VB)


[misspelling] ~10-~10: This word is normally spelled as one.
Context: ...o only do a local correction instead of proof-reading the whole dataset. - Go to the first z ...

(EN_COMPOUNDS_PROOF_READING)

🔇 Additional comments (6)
docs/proofreading/split_segments_toolkit.md (6)

1-2: Good heading clarity
The title clearly indicates the feature being documented.


11-11: Verify UI terminology
Ensure that “Go to the first z slice of the bounding box” matches the exact wording and capitalization used in the application’s UI.


15-15: Verify tool mode names
Confirm that “3D mode” and “Restrict to Bounding Box” match the exact labels in the UI settings to avoid confusion.


17-19: Approve troubleshooting advice
The troubleshooting steps are clear and provide helpful guidance for common pitfalls.


20-21: Approve configuration troubleshooting
The note about spline-curve configuration and clipping distance is concise and accurate.


29-31: Approve impact section details
The bullet points accurately describe how the toolkit alters rendering and flood-fill behavior.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
docs/proofreading/split_segments_toolkit.md (1)

24-31: ⚠️ Potential issue

Fix product name casing
The application’s name is consistently branded as “WebKnossos,” not “WEBKNOSSOS.” Please update to avoid confusion.

Proposed diff:

- By activating that toolkit, WEBKNOSSOS behaves differently in the following way:
+ By activating that toolkit, WebKnossos behaves differently in the following way:
🧹 Nitpick comments (2)
docs/proofreading/split_segments_toolkit.md (2)

8-16: Refine long sentence and correct spelling
The third sentence in step 1 is very long and uses “proof-reading” (hyphenated), which is non-standard. Consider splitting it for readability and changing “proof-reading” to “proofreading”.

Proposed diff:

- 1. Use the bounding box tool to create a box around the merge error that you want to correct. Theoretically, this step is optional, but keep in mind that relabeling large parts of a segment requires a lot of computing resources and is limited by your computer performance. Consider using the [proofreading tool](../proofreading/tools.html) for large-scale corrections. If you want to correct data for the purpose of ground-truth, it is often enough to only do a local correction instead of proof-reading the whole dataset.
+ 1. Use the bounding box tool to create a box around the merge error you want to correct. This step is optional, but relabeling large regions can be resource-intensive. For large-scale corrections, consider using the [proofreading tool](../proofreading/tools.html). When generating ground-truth data, a local correction within the bounding box is often sufficient instead of proofreading the entire dataset.
🧰 Tools
🪛 LanguageTool

[style] ~10-~10: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...ml) for large-scale corrections. If you want to correct data for the purpose of ground-...

(REP_WANT_TO_VB)


[misspelling] ~10-~10: This word is normally spelled as one.
Context: ...o only do a local correction instead of proof-reading the whole dataset. 2. Go to the first z...

(EN_COMPOUNDS_PROOF_READING)


17-23: Enhance formatting for UI elements and consistency
In the Troubleshooting section, GUI labels like “Layers” → “Skeleton” should use backticks for consistency with other docs.

Proposed diff:

- - If multiple spline curves appear per slice, this is often due to a configuration issue. Please adapt the clipping distance in the left sidebar ("Layers" -> "Skeleton").
+ - If multiple spline curves appear per slice, this is often due to a configuration issue. Please adapt the clipping distance in the left sidebar (`Layers` → `Skeleton`).
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6cad801 and 87ef13d.

📒 Files selected for processing (1)
  • docs/proofreading/split_segments_toolkit.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
docs/proofreading/split_segments_toolkit.md

[style] ~10-~10: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...ml) for large-scale corrections. If you want to correct data for the purpose of ground-...

(REP_WANT_TO_VB)


[misspelling] ~10-~10: This word is normally spelled as one.
Context: ...o only do a local correction instead of proof-reading the whole dataset. 2. Go to the first z...

(EN_COMPOUNDS_PROOF_READING)

🔇 Additional comments (1)
docs/proofreading/split_segments_toolkit.md (1)

1-5: Introduction is clear and well-structured
The title and opening paragraph succinctly introduce the Split Segments Toolkit and link to the Toolkits overview. No changes needed here.

@MichaelBuessemeyer MichaelBuessemeyer self-requested a review April 25, 2025 12:25
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (4)
docs/proofreading/split_segments_toolkit.md (4)

4-4: Consolidated phrasing matches past suggestion
This sentence aligns exactly with a previous suggestion to introduce the split interface. No additional changes needed here.


15-15: Highlighting past suggestion for bounding box restriction
This instruction reflects a previous recommendation to remind users to enable both 3D and bounding box modes during the fill step. It’s been applied correctly.


19-19: Reminder: Draw the splitting surface larger
This advice repeats an earlier note to enlarge the surface to prevent floodfill bleed. It’s already included and no further action is needed.


27-27: Product name casing is inconsistent
This line still uses WEBKNOSSOS in all caps, which conflicts with the application’s branding. Please replace with WebKnossos.

🧹 Nitpick comments (4)
docs/proofreading/split_segments_toolkit.md (4)

12-12: Use code formatting for UI labels
UI labels like Pen mode are more clearly denoted with backticks than quotes. For consistency:

- Consider activating the "Pen" mode within the skeleton tool
+ Consider activating the `Pen` mode within the skeleton tool

21-21: Link to configuration documentation
When advising users to adapt the clipping distance, consider linking to the relevant settings page for clarity:

- If multiple spline curves appear per slice, adapt the [Skeleton clipping distance configuration](../ui/toolbar.md#skeleton-settings).

22-22: Consider adding a figure caption for accessibility
Wrapping the image in a <figure> with a <figcaption> provides additional context and improves accessibility:

<figure>
  <img src="../images/splitting-floodfill-visualization.png" alt="Floodfill bleed visualization">
  <figcaption>Figure: Floodfill operation bleeding across an incomplete boundary.</figcaption>
</figure>

29-31: Refine phrasing for parallel structure
For a more consistent tone and parallelism, you could adjust the bullets:

- Using the spline curves from before, a 3D surface is generated...
+ A 3D surface is generated from these spline curves...

- The flood-fill tool will respect the splitting surface by not crossing it.
+ The flood-fill tool will not cross the splitting surface.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 87ef13d and de74025.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (6)
  • CHANGELOG.unreleased.md (1 hunks)
  • README.md (2 hunks)
  • docs/getting_started.md (1 hunks)
  • docs/proofreading/split_segments_toolkit.md (1 hunks)
  • frontend/javascripts/oxalis/model_initialization.ts (3 hunks)
  • package.json (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • README.md
  • package.json
  • docs/getting_started.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • CHANGELOG.unreleased.md
  • frontend/javascripts/oxalis/model_initialization.ts
🧰 Additional context used
🪛 LanguageTool
docs/proofreading/split_segments_toolkit.md

[style] ~10-~10: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...ml) for large-scale corrections. If you want to correct data for the purpose of ground-...

(REP_WANT_TO_VB)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: backend-tests
  • GitHub Check: build-smoketest-push
  • GitHub Check: frontend-tests
🔇 Additional comments (1)
docs/proofreading/split_segments_toolkit.md (1)

8-8: Great use of an ordered list for workflow steps
Switching from bullet points to a numbered list improves clarity and signals the required sequence.

@@ -0,0 +1,31 @@
# Split Segments Toolkit

As explained in the [Toolkits](../ui/toolbar.md#Toolkits) section, there is a dedicated toolkit for splitting segments in your volume annotation.
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Broken markdown link anchor
The link to the Toolkits section uses #Toolkits, but Markdown heading anchors are typically all lowercase (#toolkits). This may break the link. Please update accordingly:

- As explained in the [Toolkits](../ui/toolbar.md#Toolkits) section...
+ As explained in the [Toolkits](../ui/toolbar.md#toolkits) section...
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
As explained in the [Toolkits](../ui/toolbar.md#Toolkits) section, there is a dedicated toolkit for splitting segments in your volume annotation.
As explained in the [Toolkits](../ui/toolbar.md#toolkits) section, there is a dedicated toolkit for splitting segments in your volume annotation.

Copy link
Contributor

@MichaelBuessemeyer MichaelBuessemeyer left a comment

Choose a reason for hiding this comment

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

Thanks for going through all my feedback 🙏 I looked at everything relevant again.

  1. I think this is my problem but during the first spliting attempt I think I missed to strech the plane through all slices that need to be splitted leading to the flood fill "bleeding into the other side" of the plane. Not sure how to avoid this except for undo and refine. But I think this fine. Still watned to mention it.

yeah, this is not ideal, but for the first iteration okay imo.

Ok 👍

  1. The 3d floodfill restrictions are a little too hard. For each split I tried, the limit was reached and a bbox was created. Maybe increasing the limit a little more would help here 🤔

the limits should be orthogonal to this PR. when using the "restrict to bbox" feature, the limits are already higher and I think that should be enough for now.

But in that case the user would need to explicitly create a new bounding box before flood filling :/. But maybe that's also ok for the first iteration.

  1. In case the user wants to split of a merged cell and wants to create a new cell it might be convinient to have this as part of the split toolkit. The brush makes it easy via pressing c and direclty indicating that a new cell was created via the color of the brush. But the split toolkit there is no real visual indication for this. The floodfill tool also supports the c shortcut but except for the number of the active segment in the toolbar changing there is no visual indication. Maybe some kind of visual ui change in case of creating a new cell in the flood fill tool would be helpful 🤔. Bbut maybe that's not something for this PR. And maybe creating a new cell is very unsual as usually only a segment of the cell needs to be corrected to be part of a different cell. 🤔 So please just treat this a a thought and not as a must fix / implement or so :)

when the fill tool is active, there is the normal "create new segment id" button in the toolbar with the colored dot-indcator. isn't this enough?

Oh 💡, I was looking for such a button while the flood fill tool wasn't the active tool. So please nvm this.

Comment on lines +114 to +125
const getSuperUserItems = (): CommandWithoutId[] => {
if (!activeUser?.isSuperUser) {
return [];
}
return [
{
name: "Toggle Action Logging",
command: () => (WkDevFlags.logActions = !WkDevFlags.logActions),
color: commandEntryColor,
},
];
};
Copy link
Contributor

Choose a reason for hiding this comment

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

For me, it is also fine to only have this available via webknossos.DEV, but my argument is that these cool helping tools should somehow be discoverable or documented for us developers. E.g.: I'd never know about the shader editor if you wouldn't have shown it to me. This would go the same for this feature. So the usage of webknossos.DEV seems to me to be dependent on the DEVs knowledge of such an implementation and what I am requesting is a way we can share this knowledge. Or do you think that the devs should look into wk_dev.ts in case they are debugging and maybe find something there? Because that is at least not my current workflow.

const minZ = Math.min(...zValues);
const maxZ = Math.max(...zValues);

const curvesByZ: Record<number, THREE.CatmullRomCurve3> = {};
Copy link
Contributor

Choose a reason for hiding this comment

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

Neat, thanks a lot. That really helps 🙏

As everything is sorted by z axis. Did you try how well the tool works if trying to split in another viewport besides the xy viewport? Maybe it is possible to generate plausible skeletons in these other viewports without meeting the "at least 3 points per z coordinate requirement" leading to no mesh being generated 🤔?

@@ -100,6 +100,7 @@ export default function computeSplitBoundaryMeshWithSplines(points: Vector3[]):

// Number of points per curve
const numPoints = 50;
const numDivisions = numPoints - 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

why is this now 49?

Copy link
Member Author

Choose a reason for hiding this comment

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

there should be 50 points on the spline. this means that the line will be divided into 49 "sections". if threejs' getPoints function would require the point count, this would not be necessary. however, it requests a division count.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, ok a little unexpected. Thanks for the clarification 👍 (was unable to find the docs for getPoints in the catmull curve -> it is inherited as I just noticed 🙈)

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah, I only saw it because my code editor showed the functions signature (thanks to TS).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (1)
frontend/javascripts/oxalis/model/sagas/annotation_tool_saga.ts (1)

24-32: ⚠️ Potential issue

Guard against empty toolkit arrays.

If Toolkits[activeToolkit] is ever empty, indexing [0] will return undefined. It's safer to add a check before attempting to access the first element.

 function* ensureActiveToolIsInToolkit() {
   const activeToolkit = yield* select((state) => state.userConfiguration.activeToolkit);
   const activeTool = yield* select((state) => state.uiInformation.activeTool);
   const availableTools = Toolkits[activeToolkit];

+ if (!availableTools?.length) {
+   console.warn("No tools available for the active toolkit. Please verify configuration.");
+   return;
+ }

   if (!availableTools.includes(activeTool)) {
     yield put(setToolAction(availableTools[0]));
   }
 }
🧹 Nitpick comments (10)
frontend/javascripts/oxalis/view/action-bar/tools/toolkit_switcher_view.tsx (1)

53-67: Consider extracting badge styling to constants

The badge styling contains hardcoded values including a high z-index (1000). For better maintainability, consider extracting these styles to a constants file.

<Badge
  dot={activeToolkit !== Toolkit.ALL_TOOLS}
-  style={{
-    boxShadow: "none",
-    background: "red",
-    zIndex: 1000,
-  }}
+  style={TOOLKIT_INDICATOR_BADGE_STYLE}
>

Then define the constant in tool_helpers.tsx:

export const TOOLKIT_INDICATOR_BADGE_STYLE = {
  boxShadow: "none",
  background: "red",
  zIndex: 1000,
};
frontend/javascripts/oxalis/view/action-bar/tools/brush_presets.tsx (2)

78-87: Simplify conditional assignment of brush sizes

The current code unnecessarily uses optional chaining after already checking if presetBrushSizes is null. This can be simplified.

let smallBrushSize: number, mediumBrushSize: number, largeBrushSize: number;
if (presetBrushSizes == null) {
  smallBrushSize = defaultBrushSizes.small;
  mediumBrushSize = defaultBrushSizes.medium;
  largeBrushSize = defaultBrushSizes.large;
} else {
-  smallBrushSize = presetBrushSizes?.small;
-  mediumBrushSize = presetBrushSizes?.medium;
-  largeBrushSize = presetBrushSizes?.large;
+  smallBrushSize = presetBrushSizes.small;
+  mediumBrushSize = presetBrushSizes.medium;
+  largeBrushSize = presetBrushSizes.large;
}

254-256: Document calculateMediumBrushSize function

The calculateMediumBrushSize function uses a specific formula that would benefit from a comment explaining the rationale behind the calculation.

function calculateMediumBrushSize(maximumBrushSize: number) {
+  // Calculate a medium brush size that scales with the maximum size
+  // but remains reasonable for practical use (roughly 1/10 of the range)
  return Math.ceil((maximumBrushSize - userSettings.brushSize.minimum) / 10) * 5;
}
frontend/javascripts/oxalis/view/action-bar/tools/toolbar_view.tsx (3)

115-124: Consider using a more informative icon for the split workflow tag

The current implementation uses an InfoCircleOutlined icon with an orange Tag, which might signal a warning. Since this is informational rather than a warning, consider using a different icon or color.

{isSplitToolkit ? (
  <FastTooltip
    title={`Some tools behave differently because the "Split Segments" toolkit is active. Read more in the documentation.`}
  >
-    <Tag style={{ marginLeft: 12 }} color="orange">
-      <InfoCircleOutlined style={{ marginRight: 4 }} />
+    <Tag style={{ marginLeft: 12 }} color="blue">
+      <InfoCircleOutlined style={{ marginRight: 4 }} />
      Split Workflow
    </Tag>
  </FastTooltip>
) : null}

129-141: Consider breaking down ToolSpecificSettings into smaller components

The ToolSpecificSettings component has many props and conditional rendering logic. Consider breaking it down into smaller, more focused components for better maintainability.

For example, you could create separate components like BrushToolSettings, MeasurementToolSettings, etc., and conditionally render them based on the active tool.


202-219: Extract QuickSelect AI button to a separate component

The Quick Select AI toggle button implementation is quite complex and would be more maintainable as a separate component.

Consider extracting this to a new component like QuickSelectAIToggle in the volume_specific_ui.tsx file, similar to other tool-specific components.

frontend/javascripts/oxalis/view/action-bar/tools/skeleton_specific_ui.tsx (1)

164-180: Consider using a more maintainable approach for icon positioning

The absolute positioning of the plus icon might break if the base styles change. Consider using a more robust approach, such as wrapping both icons in a container with relative positioning.

<ButtonComponent
  onClick={handleCreateTree}
  style={{ ...NARROW_BUTTON_STYLE, paddingRight: 5 }}
  title={`Create a new Tree (C) – ${activeTreeHint}`}
>
+  <div style={{ position: 'relative', display: 'inline-block' }}>
    <i
      style={{
        opacity: 0.9,
        transform: "scale(0.9) translate(-2px, -1px)",
        marginRight: 3,
      }}
      className="fas fa-project-diagram"
    />
    <i
      className="fas fa-plus"
      style={{
-       position: "absolute",
-       top: 13,
-       left: 21,
+       position: "absolute",
+       bottom: -5,
+       right: -5,
        fontSize: 11,
      }}
    />
+  </div>
</ButtonComponent>
frontend/javascripts/oxalis/view/action-bar/tools/tool_buttons.tsx (2)

104-108: Consider adding a JSDoc comment to document this helper function.

The getIsVolumeModificationAllowed function is used by multiple tool components. Since it encapsulates an important permission check, adding a JSDoc comment would help explain the conditions under which volume modification is allowed.

+/**
+ * Determines if volume modification is allowed based on annotation state.
+ * Volume modification requires: volume data is present, annotation is not read-only,
+ * and there is no editable mapping active.
+ * @param state The current application state
+ * @returns boolean indicating if volume modification is allowed
+ */
 function getIsVolumeModificationAllowed(state: OxalisState) {
   const isReadOnly = !state.annotation.restrictions.allowUpdate;
   const hasVolume = state.annotation?.volumes.length > 0;
   return hasVolume && !isReadOnly && !hasEditableMapping(state);
 }

92-100: Consider standardizing icon opacity pattern across all tools.

Most tools set icon opacity based on the disabled state, but the pattern varies slightly between components. For instance, the Skeleton tool sets opacity inline, while the Trace tool combines it with other styles using the spread operator.

For consistency, consider standardizing how opacity is applied to icons across all tool components.

 // Example of a standardized approach for one of the components
 <img
   src="/assets/images/quick-select-tool.svg"
   alt="Quick Select Icon"
   style={{
-    opacity: disabledInfosForTools[AnnotationTool.QUICK_SELECT.id].isDisabled ? 0.5 : 1,
-    ...IMG_STYLE_FOR_SPACEY_ICONS,
+    ...IMG_STYLE_FOR_SPACEY_ICONS,
+    opacity: disabledInfosForTools[AnnotationTool.QUICK_SELECT.id].isDisabled ? 0.5 : 1,
   }}
 />

Also applies to: 185-198, 303-312

frontend/javascripts/oxalis/view/action-bar/tools/volume_specific_ui.tsx (1)

313-337: Consider moving NUX state update to a Redux action.

The NuxPopConfirm component directly calls the API and dispatches an action with the result. This mixes API call and state management logic in a UI component. Consider moving this logic to a saga or action creator for better separation of concerns.

// In a new file like frontend/javascripts/oxalis/model/actions/nux_actions.ts
export const updateNuxStateAction = (nuxKey: keyof APIUser["novelUserExperienceInfos"], value: boolean) => ({
  type: "UPDATE_NUX_STATE",
  nuxKey,
  value,
});

// In a saga file
function* handleUpdateNuxState(action) {
  const { nuxKey, value } = action;
  const activeUser = yield select(state => state.activeUser);
  if (!activeUser) return;
  
  try {
    const [newUserSync] = yield call(updateNovelUserExperienceInfos, activeUser, {
      [nuxKey]: value,
    });
    yield put(setActiveUserAction(newUserSync));
  } catch (error) {
    console.error("Failed to update NUX state:", error);
  }
}

// Then in the component
<Popconfirm
  open
  title="Did you know?"
  showCancel={false}
  onConfirm={() => {
    dispatch(updateNuxStateAction("hasSeenSegmentAnythingWithDepth", true));
  }}
  // ...rest of props
>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de74025 and 4e304ca.

📒 Files selected for processing (14)
  • frontend/javascripts/libs/order_points_with_mst.ts (1 hunks)
  • frontend/javascripts/oxalis/controller/viewmodes/plane_controller.tsx (13 hunks)
  • frontend/javascripts/oxalis/model/sagas/annotation_tool_saga.ts (5 hunks)
  • frontend/javascripts/oxalis/model/sagas/split_boundary_mesh_saga.ts (1 hunks)
  • frontend/javascripts/oxalis/view/action-bar/toolbar_view.tsx (0 hunks)
  • frontend/javascripts/oxalis/view/action-bar/tools/brush_presets.tsx (1 hunks)
  • frontend/javascripts/oxalis/view/action-bar/tools/skeleton_specific_ui.tsx (1 hunks)
  • frontend/javascripts/oxalis/view/action-bar/tools/tool_buttons.tsx (1 hunks)
  • frontend/javascripts/oxalis/view/action-bar/tools/tool_helpers.tsx (1 hunks)
  • frontend/javascripts/oxalis/view/action-bar/tools/toolbar_view.tsx (1 hunks)
  • frontend/javascripts/oxalis/view/action-bar/tools/toolkit_switcher_view.tsx (1 hunks)
  • frontend/javascripts/oxalis/view/action-bar/tools/volume_specific_ui.tsx (1 hunks)
  • frontend/javascripts/oxalis/view/action-bar/view_modes_view.tsx (4 hunks)
  • frontend/javascripts/oxalis/view/action_bar_view.tsx (6 hunks)
💤 Files with no reviewable changes (1)
  • frontend/javascripts/oxalis/view/action-bar/toolbar_view.tsx
✅ Files skipped from review due to trivial changes (1)
  • frontend/javascripts/oxalis/controller/viewmodes/plane_controller.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • frontend/javascripts/oxalis/view/action-bar/view_modes_view.tsx
  • frontend/javascripts/libs/order_points_with_mst.ts
  • frontend/javascripts/oxalis/model/sagas/split_boundary_mesh_saga.ts
  • frontend/javascripts/oxalis/view/action_bar_view.tsx
🧰 Additional context used
🧬 Code Graph Analysis (5)
frontend/javascripts/oxalis/view/action-bar/tools/toolkit_switcher_view.tsx (3)
frontend/javascripts/oxalis/store.ts (1)
  • OxalisState (608-644)
frontend/javascripts/oxalis/model/actions/settings_actions.ts (1)
  • updateUserSettingAction (55-63)
frontend/javascripts/oxalis/view/action-bar/tools/tool_helpers.tsx (1)
  • NARROW_BUTTON_STYLE (8-11)
frontend/javascripts/oxalis/view/action-bar/tools/brush_presets.tsx (9)
frontend/javascripts/oxalis/model/actions/settings_actions.ts (1)
  • updateUserSettingAction (55-63)
frontend/javascripts/oxalis/store.ts (2)
  • BrushPresets (325-329)
  • OxalisState (608-644)
frontend/javascripts/oxalis/constants.ts (1)
  • Unicode (235-239)
frontend/javascripts/oxalis/model/accessors/volumetracing_accessor.ts (1)
  • getMaximumBrushSize (262-273)
frontend/javascripts/types/schemas/user_settings.schema.ts (1)
  • userSettings (10-152)
frontend/javascripts/oxalis/model/accessors/view_mode_accessor.ts (1)
  • getViewportExtents (58-61)
frontend/javascripts/oxalis/model/actions/volumetracing_actions.ts (1)
  • setMousePositionAction (333-337)
frontend/javascripts/components/fast_tooltip.tsx (1)
  • FastTooltip (54-123)
frontend/javascripts/oxalis/view/components/setting_input_views.tsx (1)
  • LogSliderSetting (147-241)
frontend/javascripts/oxalis/view/action-bar/tools/skeleton_specific_ui.tsx (10)
frontend/javascripts/oxalis/store.ts (1)
  • OxalisState (608-644)
frontend/javascripts/oxalis/model/actions/settings_actions.ts (1)
  • updateUserSettingAction (55-63)
frontend/javascripts/libs/utils.ts (1)
  • isUserAdminOrManager (496-498)
frontend/javascripts/libs/react_helpers.tsx (1)
  • useIsActiveUserAdminOrManager (132-135)
frontend/javascripts/oxalis/model/accessors/volumetracing_accessor.ts (1)
  • getActiveSegmentationTracing (333-333)
frontend/javascripts/oxalis/model/actions/skeletontracing_actions.tsx (2)
  • setMergerModeEnabledAction (546-550)
  • createTreeAction (301-311)
frontend/javascripts/oxalis/view/components/button_component.tsx (1)
  • ToggleButton (64-67)
frontend/javascripts/oxalis/view/action-bar/tools/tool_helpers.tsx (2)
  • NARROW_BUTTON_STYLE (8-11)
  • IMG_STYLE_FOR_SPACEY_ICONS (12-18)
frontend/javascripts/oxalis/view/action-bar/starting_job_modals.tsx (1)
  • MaterializeVolumeAnnotationModal (1157-1275)
frontend/javascripts/oxalis/model/accessors/skeletontracing_accessor.ts (1)
  • getActiveTree (82-93)
frontend/javascripts/oxalis/view/action-bar/tools/tool_helpers.tsx (2)
frontend/javascripts/libs/window.ts (1)
  • document (19-34)
frontend/javascripts/components/fast_tooltip.tsx (1)
  • FastTooltip (54-123)
frontend/javascripts/oxalis/view/action-bar/tools/tool_buttons.tsx (7)
frontend/javascripts/oxalis/model/accessors/tool_accessor.ts (1)
  • AnnotationToolId (11-11)
frontend/javascripts/oxalis/model/accessors/volumetracing_accessor.ts (3)
  • getRenderableMagForActiveSegmentationTracing (561-563)
  • hasEditableMapping (591-600)
  • hasAgglomerateMapping (797-826)
frontend/javascripts/components/fast_tooltip.tsx (1)
  • FastTooltip (54-123)
frontend/javascripts/oxalis/view/action-bar/tools/tool_helpers.tsx (2)
  • ToolRadioButton (67-92)
  • IMG_STYLE_FOR_SPACEY_ICONS (12-18)
frontend/javascripts/oxalis/store.ts (1)
  • OxalisState (608-644)
frontend/javascripts/oxalis/model/accessors/disabled_tool_accessor.ts (1)
  • getDisabledInfoForTools (366-368)
frontend/javascripts/oxalis/model/actions/dataset_actions.ts (1)
  • ensureLayerMappingsAreLoadedAction (47-51)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build-smoketest-push
  • GitHub Check: backend-tests
🔇 Additional comments (21)
frontend/javascripts/oxalis/view/action-bar/tools/toolkit_switcher_view.tsx (1)

9-26: Well-structured toolkit options definition

The toolkit options are clearly defined with descriptive labels and corresponding keys from the Toolkit enum. This makes it easy to understand and maintain the available toolkits.

frontend/javascripts/oxalis/view/action-bar/tools/brush_presets.tsx (1)

89-95: Well-implemented brush centering

The centering of the brush in the viewport when opening the popover is well implemented, calculating the center position based on the active viewport.

frontend/javascripts/oxalis/view/action-bar/tools/toolbar_view.tsx (2)

62-74: Good documentation for tool radio group logic

The toolToRadioGroupValue function is well-documented, clearly explaining the special handling for measurement tools in the radio group.


99-105: Efficient tool button rendering

The code efficiently uses the toolkit configuration to determine which tools to render, and uses a component lookup pattern to render the appropriate button component for each tool.

frontend/javascripts/oxalis/view/action-bar/tools/skeleton_specific_ui.tsx (4)

22-69: Well-implemented conditional rendering based on state and permissions

The component properly checks multiple conditions (merger mode, editable mappings, user permissions) to determine which buttons to show and which to disable. The tooltips are informative and context-sensitive.


77-90: Good conditional rendering based on active toolkit

The component conditionally renders the skeleton-specific buttons based on whether the Split Segments toolkit is active, which makes sense as some buttons may not be applicable in that mode.


109-116: Well-implemented continuous node creation button

The continuous node creation toggle button is straightforward and has a clear tooltip explaining its functionality. This new "draw mode" feature mentioned in the PR objectives is well implemented.


136-147: Nice active tree visualization with color badge

The CreateTreeButton component provides helpful visual feedback by displaying the active tree's color in a badge. The tooltip also provides useful information about the active tree.

frontend/javascripts/oxalis/view/action-bar/tools/tool_helpers.tsx (3)

8-18: Good exportable style constants!

Having these centralized styles for buttons and icons promotes consistency across the UI.


20-65: Well-designed RadioButtonWithTooltip component with appropriate focus management.

This component effectively wraps Ant Design's Radio.Button with a tooltip and handles edge cases well. The blur on click improves the user experience by removing focus indicators after selection, and the tooltip integration is nicely commented.

Note the clever pattern of removing padding from the button itself and adding it to the inner span, ensuring the tooltip appears correctly when hovering anywhere on the button.


67-92: Clean implementation of ToolRadioButton.

This component builds nicely on the RadioButtonWithTooltip to provide a consistent format for tool tooltips, combining the name and description (or disabled explanation) in a standard format.

frontend/javascripts/oxalis/view/action-bar/tools/tool_buttons.tsx (4)

38-53: Helpful multi-slice annotation info icon with clear user feedback.

This component provides useful visual feedback when users are annotating at low magnification where actions might affect multiple slices. The conditional display based on magnification is a good UX choice.


132-133: Consistent pattern for showing multi-slice annotation info.

The pattern of conditionally showing the multi-slice annotation icon when the tool is active is applied consistently across all relevant tools. This provides a clean and consistent UX for the user.

Also applies to: 168-169, 197-198, 230-232, 259-261


344-391: Good preloading pattern in ProofreadTool component.

The onMouseEnter handler dispatches an action to ensure layer mappings are loaded, which is a nice way to preload data before the user actually clicks the button. This reduces perceived latency when the tool is activated.


19-36:

❓ Verification inconclusive

Well-organized mapping of tool IDs to components.

The ToolIdToComponent record provides a clean way to map from tool IDs to their respective React components. This makes the code more maintainable and allows for easy tool registration.

One small note: The Area Measurement tool maps to a null component function. Is this intentional because the UI for this tool is handled elsewhere, or is this a placeholder for future implementation?


🏁 Script executed:

#!/bin/bash
# Check where AREA_MEASUREMENT tool might be used
rg -A 5 "AREA_MEASUREMENT" --type ts --type tsx

Length of output: 75


It looks like ripgrep didn’t recognize the tsx type. Let’s search without restricting file types:


🏁 Script executed:

#!/bin/bash
# Search for any references to AREA_MEASUREMENT across .ts and .tsx files
rg -n "AREA_MEASUREMENT" -g '*.{ts,tsx}'

Length of output: 2268


Toolbar Mapping: AREA_MEASUREMENT Returns Null
The ToolIdToComponent entry for AnnotationTool.AREA_MEASUREMENT.id is currently a no-op (() => null), which means no button will appear in the action bar for the area-measurement tool. If you intentionally handle the area-measurement UI via the floating controls in plane_controller/tool_controls, this is fine. Otherwise, you may want to supply and register a dedicated button component here to keep toolbar behavior consistent.

• File: frontend/javascripts/oxalis/view/action-bar/tools/tool_buttons.tsx
Lines 34–35

[AnnotationTool.AREA_MEASUREMENT.id]: () => null,

Please confirm whether this placeholder is intentional or if we should add an actual toolbar component for the area-measurement tool.

frontend/javascripts/oxalis/model/sagas/annotation_tool_saga.ts (2)

34-87: Well-designed tool switching mechanism with smart restoration logic.

The switchAwayFromDisabledTool saga intelligently handles the case when a tool becomes disabled:

  1. It automatically switches to the MOVE tool as a fallback
  2. It remembers the previously disabled tool
  3. It automatically restores the previous tool if it becomes available again
  4. It clears memory if the user explicitly selects another tool

The debouncing approach with yield delay(50) is a good compromise to reduce the performance impact of listening to all actions without needing to maintain an exhaustive list of relevant actions.


131-149: Clean orchestration in the main toolSaga.

The main saga neatly orchestrates:

  1. Setting the right initial state with ensureWkReady and toolkit setup for view mode
  2. Forking the worker sagas to handle specific responsibilities
  3. Setting up watchers for relevant state changes

The code follows good saga patterns by separating concerns and making each saga responsible for a specific aspect of tool management.

frontend/javascripts/oxalis/view/action-bar/tools/volume_specific_ui.tsx (4)

97-147: Excellent modifier key handling in OverwriteModeSwitch.

The OverwriteModeSwitch component implements a sophisticated system for toggling between overwrite modes using keyboard modifiers. The code includes detailed comments explaining the various states and toggle logic, which is very helpful for understanding the complex interactions.

Note that the component continues to process key events even when not visible, which is essential for consistent behavior with keyboard shortcuts.


221-231: Good useMemo optimization for buttonsRender.

The useCallback hook with appropriate dependencies ensures that the buttonsRender function is only recreated when tooltipTitle or isDisabled changes. This is a good optimization for a function passed to a child component.


382-416: Good toggle button with clear visual and textual feedback.

The FloodFillSettings component provides clear feedback on the current state through both visual styling (opacity and button type) and a descriptive tooltip explaining how the restriction works. This is excellent for usability.


456-491: Nicely organized compact button group for proofreading.

The ProofreadingComponents uses Space.Compact to create a visually cohesive set of related buttons. Each button has clear tooltips explaining its function and provides immediate visual feedback on the current state.

Comment on lines +39 to +44
const handleMenuClick: MenuProps["onClick"] = (args) => {
const toolkit = args.key;
Store.dispatch(updateUserSettingAction("activeToolkit", toolkit as Toolkit));
// @ts-expect-error ts-migrate(2339) FIXME: Property 'blur' does not exist on type 'Element'.
args.domEvent.target.blur();
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix TypeScript error in event handling

There's a TypeScript error in the handleMenuClick function that needs to be addressed.

const handleMenuClick: MenuProps["onClick"] = (args) => {
  const toolkit = args.key;
  Store.dispatch(updateUserSettingAction("activeToolkit", toolkit as Toolkit));
-  // @ts-expect-error ts-migrate(2339) FIXME: Property 'blur' does not exist on type 'Element'.
-  args.domEvent.target.blur();
+  (args.domEvent.target as HTMLElement).blur();
};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleMenuClick: MenuProps["onClick"] = (args) => {
const toolkit = args.key;
Store.dispatch(updateUserSettingAction("activeToolkit", toolkit as Toolkit));
// @ts-expect-error ts-migrate(2339) FIXME: Property 'blur' does not exist on type 'Element'.
args.domEvent.target.blur();
};
const handleMenuClick: MenuProps["onClick"] = (args) => {
const toolkit = args.key;
Store.dispatch(updateUserSettingAction("activeToolkit", toolkit as Toolkit));
(args.domEvent.target as HTMLElement).blur();
};

Comment on lines +228 to +232
onOpenChange={(open: boolean) => {
setIsBrushSizePopoverOpen(open);
if (open) centerBrushInViewport();
else dispatch(setMousePositionAction(null));
}}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Clean up brush position when component unmounts

The component properly cleans up the mouse position when the popover is closed, but you should also ensure it's cleaned up if the component unmounts while the popover is open.

Add a cleanup function to the component:

useEffect(() => {
  if (presetBrushSizes == null) {
    handleUpdatePresetBrushSizes(defaultBrushSizes);
  }
}, [presetBrushSizes]);

+ useEffect(() => {
+   return () => {
+     // Clean up mouse position on unmount
+     dispatch(setMousePositionAction(null));
+   };
+ }, [dispatch]);

Comment on lines +71 to +76
// biome-ignore lint/correctness/useExhaustiveDependencies: Needs investigation whether defaultBrushSizes is needed as dependency.
useEffect(() => {
if (presetBrushSizes == null) {
handleUpdatePresetBrushSizes(defaultBrushSizes);
}
}, [presetBrushSizes]);
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Fix useEffect dependency warning

The linter is warning about missing dependencies in the useEffect. This should be investigated and fixed to prevent potential bugs.

The comment indicates defaultBrushSizes might need to be added as a dependency. Let's analyze if it's necessary:


🏁 Script executed:

#!/bin/bash
# Check where defaultBrushSizes is used in the useEffect
rg -A 2 -B 2 "defaultBrushSizes" frontend/javascripts/oxalis/view/action-bar/tools/brush_presets.tsx

Length of output: 1135


🏁 Script executed:

#!/bin/bash
# Locate handleUpdatePresetBrushSizes definition and usage context
rg -A 3 -B 3 "handleUpdatePresetBrushSizes" -n frontend/javascripts/oxalis/view/action-bar/tools/brush_presets.tsx

Length of output: 1621


Fix useEffect dependency warning

Add defaultBrushSizes to the useEffect dependency array so the hook will re-run whenever its value changes. To fully satisfy the exhaustive-deps rule, you can also memoize defaultBrushSizes (and/or wrap handleUpdatePresetBrushSizes in useCallback) and then remove the biome-ignore comment.

• File: frontend/javascripts/oxalis/view/action-bar/tools/brush_presets.tsx

  1. Memoize defaultBrushSizes to stabilize its identity:
    const defaultBrushSizes = useMemo(
      () => getDefaultBrushSizes(maximumBrushSize, userSettings.brushSize.minimum),
      [maximumBrushSize, userSettings.brushSize.minimum]
    );
  2. (Optional) Wrap dispatcher in useCallback to silence linter on the updater function:
    const handleUpdatePresetBrushSizes = useCallback(
      (brushSizes: BrushPresets) =>
        Store.dispatch(updateUserSettingAction("presetBrushSizes", brushSizes)),
      []
    );
  3. Update the useEffect to include defaultBrushSizes in deps and remove the ignore comment:
    -  // biome-ignore lint/correctness/useExhaustiveDependencies: Needs investigation whether defaultBrushSizes is needed as dependency.
    -  useEffect(() => {
    +   useEffect(() => {
        if (presetBrushSizes == null) {
          handleUpdatePresetBrushSizes(defaultBrushSizes);
        }
    -  }, [presetBrushSizes]);
    +  }, [presetBrushSizes, defaultBrushSizes]);

After making these changes, you can delete the // biome-ignore… line.

Comment on lines +151 to +157
value={overwriteMode}
// @ts-expect-error ts-migrate(2322) FIXME: Type '(event: { target: { value: Overwri... Remove this comment to see the full error message
onChange={handleSetOverwriteMode}
style={{
marginLeft: 10,
}}
>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix TypeScript error in event handler type.

There's a TypeScript error that's being suppressed with a @ts-expect-error comment. Since this is a new file, it would be better to fix the underlying type issue rather than suppressing it.

 <Radio.Group
   value={overwriteMode}
-  // @ts-expect-error ts-migrate(2322) FIXME: Type '(event: {    target: {        value: Overwri... Remove this comment to see the full error message
-  onChange={handleSetOverwriteMode}
+  onChange={handleSetOverwriteMode as unknown as ((e: RadioChangeEvent) => void)}
   style={{
     marginLeft: 10,
   }}
 >

Alternatively, if you have time to properly fix the type issue, update the handleSetOverwriteMode function signature to match what Radio.Group expects:

-const handleSetOverwriteMode = (event: {
-  target: {
-    value: OverwriteMode;
-  };
-}) => {
+const handleSetOverwriteMode = (event: RadioChangeEvent) => {
   Store.dispatch(updateUserSettingAction("overwriteMode", event.target.value as OverwriteMode));
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
value={overwriteMode}
// @ts-expect-error ts-migrate(2322) FIXME: Type '(event: { target: { value: Overwri... Remove this comment to see the full error message
onChange={handleSetOverwriteMode}
style={{
marginLeft: 10,
}}
>
<Radio.Group
value={overwriteMode}
onChange={handleSetOverwriteMode as unknown as ((e: RadioChangeEvent) => void)}
style={{
marginLeft: 10,
}}
>

Copy link
Contributor

@MichaelBuessemeyer MichaelBuessemeyer left a comment

Choose a reason for hiding this comment

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

Just did another testing round and everything still works splendid. Thanks for bearing with my feedback 🐈

@philippotto philippotto enabled auto-merge (squash) April 29, 2025 09:29
@philippotto philippotto changed the title Add Split Segments Toolkit Add Split Segments Toolkit and Draw Mode for Skeleton Tool Apr 29, 2025
@philippotto philippotto merged commit 0486446 into master Apr 29, 2025
5 checks passed
@philippotto philippotto deleted the tps-split branch April 29, 2025 09:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add "Split Segment" Tools
3 participants