Conversation
Shift+drag draws a freehand polygon selection in addition to the
existing box selection. Both selection types can be dragged after
creation and cleared by clicking outside. The value format is now
a tagged union: {"type": "box"|"lasso", "data": ...} for
extensibility. All helper methods (get_bounds, get_vertices,
contains_point, get_mask, get_indices) handle both types.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
for more information, see https://pre-commit.ci
…ents Replace useState-driven interaction state with useRef + requestAnimationFrame to eliminate React re-renders on every mouse move during drawing/dragging. Add DataPoint type to distinguish data coordinates from pixel coordinates, Escape key to cancel in-progress selections, cursor feedback (crosshair/move), touch-action:none for mobile support, and smarter onMouseLeave behavior that doesn't finalize selections unexpectedly.
for more information, see https://pre-commit.ci
Implements a version of the suggestion in [review](#8342 (comment)). Since all interaction state already lives in refs and React does no rendering work (it's all canvas), separating the DOM/canvas logic into its own class gives a cleaner boundary between React lifecycle and imperative drawing code. These chnages extract into an imperative `MatplotlibRenderer` class without React. The class uses `AbortSignal` for cleanup (all `addEventListener` calls pass `{ signal }` for automatic removal) and a generation counter for stale image load guards. The React component becomes a thin lifecycle shell that creates the renderer on mount, syncs props via `update()` each render, and aborts the controller on unmount. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Adds d3-brush-style resize behavior: hovering box edges/corners shows directional cursors, and dragging them resizes the selection using an anchor + axis-lock approach. These changes required refactoring interaction state to a nested state machine. Replaces flat InteractionMode/InteractionState with a nested discriminated union (`Interaction = idle | box | lasso`) where each selection type carries its own typed action (drawing, dragging, resizing): | Interaction | Actions | |-------------|---------| | `idle` | | | `box` | `null` · `drawing` · `dragging` · `resizing` | | `lasso` | `null` · `drawing` · `dragging` | https://github.com/user-attachments/assets/d7a41e87-00d1-41c0-8240-839e73f2035b
On HiDPI/Retina displays the selection box and lasso overlays rendered blurry because the canvas backing store matched the logical pixel dimensions rather than the physical ones. The browser stretched the 1x canvas to fill the CSS size, producing soft edges on every fillRect, strokeRect, and lineTo call. | before | after | |-------------|---------| | <img src="https://github.com/user-attachments/assets/ac089535-65b5-417b-9736-af6f6cdaf9b4" /> | <img src="https://github.com/user-attachments/assets/4d9499a6-47aa-4e61-b26f-d0159b92cc01" /> |
dmadisetti
reviewed
Feb 19, 2026
Collaborator
dmadisetti
left a comment
There was a problem hiding this comment.
Left general comment on the api. Super fun addition, I don't think my comments are a blocker- just wanted to raise some thoughts on misuse / cumbersome nature of calling get_mask
manzt
previously approved these changes
Feb 19, 2026
Collaborator
|
Failing test is relevant but lgtm otherwise |
dmadisetti
previously approved these changes
Feb 19, 2026
LiquidGunay
pushed a commit
to LiquidGunay/marimo
that referenced
this pull request
Feb 21, 2026
This PR adds a new public API, `mo.ui.matplotlib(axis: plt.Axes, *, debounce: bool=False.)`, which adds reactive selection to a matplotlib Axes. This API is designed for scatter plots and scatter plot selections, but should be extensible enough to support other plot types and interactions (such as span selections)/geometries in the future. This feature was inspired by and based on @koaning's `Wigglystuff` `ChartSelect` anywidget for matplotlib. ## Selections Two types of selections are supported: box and lasso. Box is the default selection, with <kbd>Shift</kbd>+click triggering lasso selection. ## Python usage ```python import matplotlib.pyplot as plt import marimo as mo import numpy as np x = np.arange(5) y = x**2 plt.scatter(x=x, y=y) fig = mo.ui.matplotlib(plt.gca()) fig ``` ```python # Filter data using the selection mask = fig.value.get_mask(x, y) selected_x, selected_y = x[mask], y[mask] ``` If `debounce=True` is passed to the constructor, data is only sent back on mouse up. ## `value` type The element's `value` is a frozen dataclass containing information about the interaction and selection. Each dataclass exposes a `get_mask(x: np.typing.ArrayLike, y: np.typing.ArrayLike)` method which returns a boolean mask for indexing into the scattered data. An empty selection returns an `EmptySelection` sentinel object which is `False-y`, so users can write code like ```python if fig.value: # do something with selection ... ``` `EmptySelection.get_mask(x, y)` does return an all-`False` mask, so users can index into the original data without checking if the selection is empty. The dataclass type is not returned as part of the public API. We could extend the class of interactions/geometries handled by adding new classes in the future. ## Smoke test This PR includes a smoke test that exercises linear and log scale axes ## Media https://github.com/user-attachments/assets/e085f4d0-d17b-4d44-854c-7c3f2f156b4d --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Trevor Manz <trevor.j.manz@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR adds a new public API,
mo.ui.matplotlib(axis: plt.Axes, *, debounce: bool=False.), which adds reactive selection to a matplotlib Axes. This API is designed for scatter plots and scatter plot selections, but should be extensible enough to support other plot types and interactions (such as span selections)/geometries in the future.This feature was inspired by and based on @koaning's
WigglystuffChartSelectanywidget for matplotlib.Selections
Two types of selections are supported: box and lasso. Box is the default selection, with Shift+click triggering lasso selection.
Python usage
If
debounce=Trueis passed to the constructor, data is only sent back on mouse up.valuetypeThe element's
valueis a frozen dataclass containing information about the interaction and selection. Each dataclass exposes aget_mask(x: np.typing.ArrayLike, y: np.typing.ArrayLike)method which returns a boolean mask for indexing into the scattered data.An empty selection returns an
EmptySelectionsentinel object which isFalse-y, so users can write code likeEmptySelection.get_mask(x, y)does return an all-Falsemask, so users can index into the original data without checking if the selection is empty.The dataclass type is not returned as part of the public API. We could extend the class of interactions/geometries handled by adding new classes in the future.
Smoke test
This PR includes a smoke test that exercises linear and log scale axes
Media
mo-ui-matplotlib.mp4